diff --git a/.cargo/config b/.cargo/config index 3f07816e3c7..a160c8eab72 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,3 +1,3 @@ [target.x86_64-pc-windows-msvc] -# Link the C runtime statically ; https://github.com/paritytech/parity-ethereum/issues/6643 +# Link the C runtime statically ; https://github.com/openethereum/openethereum/issues/6643 rustflags = ["-Ctarget-feature=+crt-static"] diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000000..4c712f8324b --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Reformat the source code +610d9baba4af83b5767c659ca2ccfed337af1056 diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index a737b1a880f..58361cef883 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -2,11 +2,11 @@ ## 1. Purpose -A primary goal of Parity is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof). +A primary goal of OpenEthereum is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof). This code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior. -We invite all those who participate in Parity to help us create safe and positive experiences for everyone. +We invite all those who participate in OpenEthereum to help us create safe and positive experiences for everyone. ## 2. Open Source Citizenship @@ -63,7 +63,7 @@ Additionally, community organizers are available to help community members engag ## 7. Addressing Grievances -If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify Parity Technologies with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies. +If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify OpenEthereum Technologies with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies. ## 8. Scope @@ -73,7 +73,7 @@ This code of conduct and its related procedures also applies to unacceptable beh ## 9. Contact info -You can contact Parity via Email: community@parity.io +You can contact OpenEthereum via Email: community@parity.io ## 10. License and attribution diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index b51cca8821e..eb48b425fce 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -2,7 +2,7 @@ ## Do you have a question? -Check out our [Basic Usage](https://wiki.parity.io/Basic-Usage), [Configuration](https://wiki.parity.io/Configuring-Parity-Ethereum), and [FAQ](https://wiki.parity.io/FAQ) articles on our [wiki](https://wiki.parity.io/)! +Check out our [Beginner Introduction](https://openethereum.github.io/Beginner-Introduction), [Configuration](https://openethereum.github.io//Configuring-OpenEthereum), and [FAQ](https://openethereum.github.io/FAQ) articles on our [wiki](https://openethereum.github.io/)! See also frequently asked questions [tagged with `parity`](https://ethereum.stackexchange.com/questions/tagged/parity?sort=votes&pageSize=50) on Stack Exchange. @@ -10,11 +10,11 @@ See also frequently asked questions [tagged with `parity`](https://ethereum.stac Do **not** open an issue on Github if you think your discovered bug could be a **security-relevant vulnerability**. Please, read our [security policy](../SECURITY.md) instead. -Otherwise, just create a [new issue](https://github.com/paritytech/parity-ethereum/issues/new) in our repository and state: +Otherwise, just create a [new issue](https://github.com/openethereum/openethereum/issues/new) in our repository and state: -- What's your Parity Ethereum version? +- What's your OpenEthereum version? - What's your operating system and version? -- How did you install Parity Ethereum? +- How did you install OpenEthereum? - Is your node fully synchronized? - Did you try turning it off and on again? @@ -22,9 +22,44 @@ Also, try to include **steps to reproduce** the issue and expand on the **actual ## Contribute! -If you would like to contribute to Parity Ethereum, please **fork it**, fix bugs or implement features, and [propose a pull request](https://github.com/paritytech/parity-ethereum/compare). +If you would like to contribute to OpenEthereum, please **fork it**, fix bugs or implement features, and [propose a pull request](https://github.com/openethereum/openethereum/compare). -Please, refer to the [Coding Guide](https://wiki.parity.io/Coding-guide) in our wiki for more details about hacking on Parity. +### Labels & Milestones + +We use [labels](https://github.com/openethereum/openethereum/labels) to manage PRs and issues and communicate the state of a PR. Please familiarize yourself with them. Furthermore we are organizing issues in [milestones](https://github.com/openethereum/openethereum/milestones). Best way to get started is to a pick a ticket from the current milestone tagged [`easy`](https://github.com/openethereum/openethereum/labels/Q2-easy%20%F0%9F%92%83) and get going, or [`mentor`](https://github.com/openethereum/openethereum/labels/Q1-mentor%20%F0%9F%95%BA) and get in contact with the mentor offering their support on that larger task. + +### Rules + +There are a few basic ground-rules for contributors (including the maintainer(s) of the project): + +* **No pushing directly to the master branch**. +* **All modifications** must be made in a **pull-request** to solicit feedback from other contributors. +* Pull-requests cannot be merged before CI runs green and two reviewers have given their approval. +* All code changed should be formated by running `cargo fmt -- --config=merge_imports=true` + +### Recommendations + +* **Non-master branch names** *should* be prefixed with a short name moniker, followed by the associated Github Issue ID (if any), and a brief description of the task using the format `--` (e.g. `gavin-123-readme`). The name moniker helps people to inquiry about their unfinished work, and the GitHub Issue ID helps your future self and other developers (particularly those who are onboarding) find out about and understand the original scope of the task, and where it fits into Parity Ethereum [Projects](https://github.com/openethereum/openethereum/projects). +* **Remove stale branches periodically** + +### Preparing Pull Requests + +* If your PR does not alter any logic (e.g. comments, dependencies, docs), then it may be tagged [`insubstantial`](https://github.com/openethereum/openethereum/pulls?q=is%3Aopen+is%3Apr+label%3A%22A2-insubstantial+%F0%9F%91%B6%22). + +* Once a PR is ready for review please add the [`pleasereview`](https://github.com/openethereum/openethereum/pulls?utf8=%E2%9C%93&q=is%3Aopen+is%3Apr+label%3A%22A0-pleasereview+%F0%9F%A4%93%22+) label. + +### Reviewing Pull Requests*: + +* At least two reviewers are required to review PRs (even for PRs tagged [`insubstantial`](https://github.com/openethereum/openethereum/pulls?q=is%3Aopen+is%3Apr+label%3A%22A2-insubstantial+%F0%9F%91%B6%22)). + +When doing a review, make sure to look for any: + +* Buggy behavior. +* Undue maintenance burden. +* Breaking with house coding style. +* Pessimization (i.e. reduction of speed as measured in the projects benchmarks). +* Breaking changes should be carefuly reviewed and tagged as such so they end up in the [changelog](../CHANGELOG.md). +* Uselessness (i.e. it does not strictly add a feature or fix a known issue). ## License. diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index fb059d428d6..9308be5bca7 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,6 +1,6 @@ _Before filing a new issue, please **provide the following information**._ -- **Parity Ethereum version**: 0.0.0 +- **OpenEthereum version**: 0.0.0 - **Operating system**: Windows / MacOS / Linux - **Installation**: homebrew / one-line installer / built from source - **Fully synchronized**: no / yes diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml new file mode 100644 index 00000000000..a8357e5e967 --- /dev/null +++ b/.github/workflows/build-test.yml @@ -0,0 +1,99 @@ +name: Build and Test Suite + +on: + pull_request: + push: + branches: + - master + - stable +jobs: + build-tests: + name: Test and Build + env: + SCCACHE_CACHE_SIZE: "1G" + SCCACHE_IDLE_TIMEOUT: 0 + strategy: + matrix: + platform: + - ubuntu-20.04 + - macos-latest + - windows-latest + toolchain: + - stable + runs-on: ${{ matrix.platform }} + steps: + - name: Checkout sources + uses: actions/checkout@master + with: + submodules: true + - name: Install toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.toolchain }} + profile: minimal + override: true + - name: Cache cargo registry + uses: actions/cache@v2 + with: + path: ~/.cargo/registry + key: ${{ runner.os }}-cargo-registry-build-tests-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo index + uses: actions/cache@v2 + with: + path: ~/.cargo/git + key: ${{ runner.os }}-cargo-git-build-tests-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo build + uses: actions/cache@v2 + with: + path: target + key: ${{ runner.os }}-cargo-build-target-build-tests-${{ hashFiles('**/Cargo.lock') }} + - name: Cache sccache linux + if: matrix.platform == 'ubuntu-20.04' + uses: actions/cache@v2 + with: + path: "/home/runner/.cache/sccache" + key: ${{ runner.os }}-sccache-build-tests-${{ hashFiles('**/Cargo.lock') }} + - name: Cache sccache MacOS + if: matrix.platform == 'macos-latest' + uses: actions/cache@v2 + with: + path: "/Users/runner/Library/Caches/Mozilla.sccache" + key: ${{ runner.os }}-sccache-build-tests-${{ hashFiles('**/Cargo.lock') }} + - name: Cache sccache Windows + if: matrix.platform == 'windows-latest' + uses: actions/cache@v2 + with: + path: "C:\\Users\\runneradmin\\AppData\\Local\\Mozilla\\sccache\\cache" + key: ${{ runner.os }}-sccache-build-tests-${{ hashFiles('**/Cargo.lock') }} + - name: Install sccache for ${{ matrix.platform }} + shell: pwsh + run: pwsh scripts/actions/install-sccache.ps1 ${{ runner.os}} + - name: Install LLVM for Windows + if: matrix.platform == 'windows-latest' + run: choco install llvm + - name: Sccache statistics + run: sccache --show-stats + - name: Build tests + uses: actions-rs/cargo@v1 + with: + command: test + args: --locked --all --release --features "json-tests" --verbose --no-run + - name: Run tests for ${{ matrix.platform }} + if: matrix.platform == 'windows-latest' + continue-on-error: true #Skip step if Windows tests failure + uses: actions-rs/cargo@v1 + with: + command: test + args: --locked --all --release --features "json-tests" --verbose + - name: Run tests for ${{ matrix.platform }} + if: matrix.platform != 'windows-latest' + uses: actions-rs/cargo@v1 + with: + command: test + args: --locked --all --release --features "json-tests" --verbose + - name: Stop sccache + if: always() + run: sccache --stop-server + - name: Prepare build directory for cache + shell: bash + run: bash scripts/actions/clean-target.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000000..5c2fa643296 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,341 @@ +name: Build Release Suite + +on: + push: + branches: + - stable + tags: + - v* + +# Global vars +env: + AWS_REGION: "us-east-1" + AWS_S3_ARTIFACTS_BUCKET: "openethereum-releases" + +jobs: + build: + name: Build Release + env: + SCCACHE_CACHE_SIZE: "1G" + SCCACHE_IDLE_TIMEOUT: 0 + strategy: + matrix: + platform: + - ubuntu-20.04 + - macos-latest + - windows-latest + toolchain: + - stable + runs-on: ${{ matrix.platform }} + steps: + - name: Checkout sources + uses: actions/checkout@master + - name: Install toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.toolchain }} + profile: minimal + override: true + - name: Cache cargo registry + uses: actions/cache@v2 + with: + path: ~/.cargo/registry + key: ${{ runner.os }}-cargo-registry-build-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo index + uses: actions/cache@v2 + with: + path: ~/.cargo/git + key: ${{ runner.os }}-cargo-git-build-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo build + uses: actions/cache@v2 + with: + path: target + key: ${{ runner.os }}-cargo-build-target-build-${{ hashFiles('**/Cargo.lock') }} + - name: Cache sccache linux + if: matrix.platform == 'ubuntu-20.04' + uses: actions/cache@v2 + with: + path: "/home/runner/.cache/sccache" + key: ${{ runner.os }}-sccache-build-${{ hashFiles('**/Cargo.lock') }} + - name: Cache sccache MacOS + if: matrix.platform == 'macos-latest' + uses: actions/cache@v2 + with: + path: "/Users/runner/Library/Caches/Mozilla.sccache" + key: ${{ runner.os }}-sccache-build-${{ hashFiles('**/Cargo.lock') }} + - name: Cache sccache Windows + if: matrix.platform == 'windows-latest' + uses: actions/cache@v2 + with: + path: "C:\\Users\\runneradmin\\AppData\\Local\\Mozilla\\sccache\\cache" + key: ${{ runner.os }}-sccache-build-${{ hashFiles('**/Cargo.lock') }} + - name: Install sccache for ${{ matrix.platform }} + shell: pwsh + run: pwsh scripts/actions/install-sccache.ps1 ${{ runner.os}} + + # ============================== + # Windows Build + # ============================== + + - name: Install LLVM for Windows + if: matrix.platform == 'windows-latest' + run: choco install llvm + + - name: Sccache statistics + run: sccache --show-stats + + - name: Build OpenEthereum for Windows + if: matrix.platform == 'windows-latest' + run: sh scripts/actions/build-windows.sh ${{matrix.platform}} + + - name: Upload Windows build + uses: actions/upload-artifact@v2 + if: matrix.platform == 'windows-latest' + with: + name: windows-artifacts + path: artifacts + + # ============================== + # Linux/Macos Build + # ============================== + + - name: Build OpenEthereum for ${{matrix.platform}} + if: matrix.platform != 'windows-latest' + run: sh scripts/actions/build-linux.sh ${{matrix.platform}} + + - name: Upload Linux build + uses: actions/upload-artifact@v2 + if: matrix.platform == 'ubuntu-20.04' + with: + name: linux-artifacts + path: artifacts + + - name: Upload MacOS build + uses: actions/upload-artifact@v2 + if: matrix.platform == 'macos-latest' + with: + name: macos-artifacts + path: artifacts + + # ============================== + # End builds + # ============================== + + - name: Stop sccache + if: always() + run: sccache --stop-server + + - name: Prepare build directory for cache + shell: bash + run: bash scripts/actions/clean-target.sh + + zip-artifacts-creator: + name: Create zip artifacts + needs: build + runs-on: ubuntu-20.04 + steps: + - name: Set env + run: echo ::set-env name=RELEASE_VERSION::${GITHUB_REF#refs/*/} + + # ============================== + # Create ZIP files + # ============================== + + - name: Download Windows artifacts + uses: actions/download-artifact@v2 + with: + name: windows-artifacts + path: windows-artifacts + + - name: Download Linux artifacts + uses: actions/download-artifact@v2 + with: + name: linux-artifacts + path: linux-artifacts + + - name: Download MacOS artifacts + uses: actions/download-artifact@v2 + with: + name: macos-artifacts + path: macos-artifacts + + - name: Display structure of downloaded files + run: ls + + - name: Create zip Linux + id: create_zip_linux + run: | + cd linux-artifacts/ + zip -rT openethereum-linux-${{ env.RELEASE_VERSION }}.zip * + ls openethereum-linux-${{ env.RELEASE_VERSION }}.zip + cd .. + mv linux-artifacts/openethereum-linux-${{ env.RELEASE_VERSION }}.zip . + + echo "Setting outputs..." + echo ::set-output name=LINUX_ARTIFACT::openethereum-linux-${{ env.RELEASE_VERSION }}.zip + echo ::set-output name=LINUX_SHASUM::$(shasum -a 256 openethereum-linux-${{ env.RELEASE_VERSION }}.zip | awk '{print $1}') + + - name: Create zip MacOS + id: create_zip_macos + run: | + cd macos-artifacts/ + zip -rT openethereum-macos-${{ env.RELEASE_VERSION }}.zip * + ls openethereum-macos-${{ env.RELEASE_VERSION }}.zip + cd .. + mv macos-artifacts/openethereum-macos-${{ env.RELEASE_VERSION }}.zip . + + echo "Setting outputs..." + echo ::set-output name=MACOS_ARTIFACT::openethereum-macos-${{ env.RELEASE_VERSION }}.zip + echo ::set-output name=MACOS_SHASUM::$(shasum -a 256 openethereum-macos-${{ env.RELEASE_VERSION }}.zip | awk '{print $1}') + + - name: Create zip Windows + id: create_zip_windows + run: | + cd windows-artifacts/ + zip -rT openethereum-windows-${{ env.RELEASE_VERSION }}.zip * + ls openethereum-windows-${{ env.RELEASE_VERSION }}.zip + cd .. + mv windows-artifacts/openethereum-windows-${{ env.RELEASE_VERSION }}.zip . + + echo "Setting outputs..." + echo ::set-output name=WINDOWS_ARTIFACT::openethereum-windows-${{ env.RELEASE_VERSION }}.zip + echo ::set-output name=WINDOWS_SHASUM::$(shasum -a 256 openethereum-windows-${{ env.RELEASE_VERSION }}.zip | awk '{print $1}') + + # ======================================================================= + # Upload artifacts + # This is required to share artifacts between different jobs + # ======================================================================= + + - name: Upload artifacts + uses: actions/upload-artifact@v2 + with: + name: openethereum-linux-${{ env.RELEASE_VERSION }}.zip + path: openethereum-linux-${{ env.RELEASE_VERSION }}.zip + + - name: Upload artifacts + uses: actions/upload-artifact@v2 + with: + name: openethereum-macos-${{ env.RELEASE_VERSION }}.zip + path: openethereum-macos-${{ env.RELEASE_VERSION }}.zip + + - name: Upload artifacts + uses: actions/upload-artifact@v2 + with: + name: openethereum-windows-${{ env.RELEASE_VERSION }}.zip + path: openethereum-windows-${{ env.RELEASE_VERSION }}.zip + + # ======================================================================= + # Upload artifacts to S3 + # This is required by some software distribution systems which require + # artifacts to be downloadable, like Brew on MacOS. + # ======================================================================= + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Copy files to S3 with the AWS CLI + run: | + # Deploy zip artifacts to S3 bucket to a directory whose name is the tagged release version. + # Deploy macos binary artifact (if required, add more `aws s3 cp` commands to deploy specific OS versions) + aws s3 cp macos-artifacts/openethereum s3://${{ env.AWS_S3_ARTIFACTS_BUCKET }}/${{ env.RELEASE_VERSION }}/macos/ --region ${{ env.AWS_REGION }} + + outputs: + linux-artifact: ${{ steps.create_zip_linux.outputs.LINUX_ARTIFACT }} + linux-shasum: ${{ steps.create_zip_linux.outputs.LINUX_SHASUM }} + macos-artifact: ${{ steps.create_zip_macos.outputs.MACOS_ARTIFACT }} + macos-shasum: ${{ steps.create_zip_macos.outputs.MACOS_SHASUM }} + windows-artifact: ${{ steps.create_zip_windows.outputs.WINDOWS_ARTIFACT }} + windows-shasum: ${{ steps.create_zip_windows.outputs.WINDOWS_SHASUM }} + + draft-release: + name: Draft Release + needs: zip-artifacts-creator + runs-on: ubuntu-20.04 + steps: + - name: Set env + run: echo ::set-env name=RELEASE_VERSION::${GITHUB_REF#refs/*/} + + # ============================== + # Download artifacts + # ============================== + + - name: Download artifacts + uses: actions/download-artifact@v2 + with: + name: openethereum-linux-${{ env.RELEASE_VERSION }}.zip + + - name: Download artifacts + uses: actions/download-artifact@v2 + with: + name: openethereum-macos-${{ env.RELEASE_VERSION }}.zip + + - name: Download artifacts + uses: actions/download-artifact@v2 + with: + name: openethereum-windows-${{ env.RELEASE_VERSION }}.zip + + - name: Display structure of downloaded files + run: ls + + # ============================== + # Create release draft + # ============================== + + - name: Create Release Draft + id: create_release_draft + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token + with: + tag_name: ${{ github.ref }} + release_name: OpenEthereum ${{ github.ref }} + body: | + This release contains + + | System | Architecture | Binary | Sha256 Checksum | + |:---:|:---:|:---:|:---| + | Apple Icon by Pixel Perfect from https://www.flaticon.com/authors/pixel-perfect | x64 | [${{ needs.zip-artifacts-creator.outputs.macos-artifact }}](https://github.com/openethereum/openethereum/releases/download/${{ env.RELEASE_VERSION }}/${{ needs.zip-artifacts-creator.outputs.macos-artifact }}) | `${{ needs.zip-artifacts-creator.outputs.macos-shasum }}` | + | Linux Icon by Pixel Perfect from https://www.flaticon.com/authors/pixel-perfect | x64 | [${{ needs.zip-artifacts-creator.outputs.linux-artifact }}](https://github.com/openethereum/openethereum/releases/download/${{ env.RELEASE_VERSION }}/${{ needs.zip-artifacts-creator.outputs.linux-artifact }}) | `${{ needs.zip-artifacts-creator.outputs.linux-shasum }}` | + | Windows Icon by Pixel Perfect from https://www.flaticon.com/authors/pixel-perfect | x64 | [${{ needs.zip-artifacts-creator.outputs.windows-artifact }}](https://github.com/openethereum/openethereum/releases/download/${{ env.RELEASE_VERSION }}/${{ needs.zip-artifacts-creator.outputs.windows-artifact }}) | `${{ needs.zip-artifacts-creator.outputs.windows-shasum }}` | + | | | | | + | **System** | **Option** | - | **Resource** | + | Settings Icon by Pixel Perfect from https://www.flaticon.com/authors/pixel-perfect | Docker | - | [hub.docker.com/r/openethereum/openethereum](https://hub.docker.com/r/openethereum/openethereum) | + + draft: true + prerelease: true + + - name: Upload Release Asset - Linux + id: upload_release_asset_linux + uses: actions/upload-release-asset@v1.0.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release_draft.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps + asset_path: ./openethereum-linux-${{ env.RELEASE_VERSION }}.zip + asset_name: openethereum-linux-${{ env.RELEASE_VERSION }}.zip + asset_content_type: application/zip + + - name: Upload Release Asset - MacOS + id: upload_release_asset_macos + uses: actions/upload-release-asset@v1.0.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release_draft.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps + asset_path: ./openethereum-macos-${{ env.RELEASE_VERSION }}.zip + asset_name: openethereum-macos-${{ env.RELEASE_VERSION }}.zip + asset_content_type: application/zip + + - name: Upload Release Asset - Windows + id: upload_release_asset_windows + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release_draft.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps + asset_path: ./openethereum-windows-${{ env.RELEASE_VERSION }}.zip + asset_name: openethereum-windows-${{ env.RELEASE_VERSION }}.zip + asset_content_type: application/zip diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 00000000000..03edc614fda --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,86 @@ +name: Check + +on: + pull_request: + push: + branches: + - master + - stable +jobs: + check: + name: Check + runs-on: ubuntu-20.04 + env: + SCCACHE_CACHE_SIZE: "1G" + SCCACHE_IDLE_TIMEOUT: 0 + steps: + - name: Checkout sources + uses: actions/checkout@master + with: + submodules: true + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + override: true + - name: Cache cargo registry + uses: actions/cache@v2 + with: + path: ~/.cargo/registry + key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo index + uses: actions/cache@v2 + with: + path: ~/.cargo/git + key: ${{ runner.os }}-cargo-git-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo build + uses: actions/cache@v2 + with: + path: target + key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} + # Install sccache based on https://github.com/denoland/rusty_v8/blob/master/.github/workflows/ci.yml#L69 + - name: Cache sccache + uses: actions/cache@v2 + with: + path: "/home/runner/.cache/sccache" + key: ${{ runner.os }}-sccache-check-${{ hashFiles('**/Cargo.lock') }} + - name: Install sccache for Linux + shell: pwsh + run: pwsh scripts/actions/install-sccache.ps1 ${{ runner.os}} + - name: Sccache statistics + run: sccache --show-stats + - name: Run cargo check 1/3 + uses: actions-rs/cargo@v1 + with: + command: check + args: --locked --no-default-features --verbose + - name: Run cargo check 2/3 + uses: actions-rs/cargo@v1 + with: + command: check + args: --locked --manifest-path util/io/Cargo.toml --no-default-features --verbose + - name: Run cargo check 3/3 + uses: actions-rs/cargo@v1 + with: + command: check + args: --locked --manifest-path util/io/Cargo.toml --features "mio" --verbose + - name: Run cargo check evmbin + uses: actions-rs/cargo@v1 + with: + command: check + args: --locked -p evmbin --verbose + - name: Run cargo check benches + uses: actions-rs/cargo@v1 + with: + command: check + args: --locked --all --benches --verbose + - name: Run validate chainspecs + run: ./scripts/actions/validate-chainspecs.sh + - name: Stop sccache + if: always() + run: sccache --stop-server + continue-on-error: true + - name: Prepare build directory for cache + shell: bash + run: bash scripts/actions/clean-target.sh diff --git a/.github/workflows/fmt.yml b/.github/workflows/fmt.yml new file mode 100644 index 00000000000..a22d8fce72d --- /dev/null +++ b/.github/workflows/fmt.yml @@ -0,0 +1,20 @@ +on: [push, pull_request] + +name: rustfmt + +jobs: + fmt: + name: Rustfmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - run: rustup component add rustfmt + - uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check --config merge_imports=true diff --git a/.gitignore b/.gitignore index 53c0e8ac65f..a3d3dc5e0e6 100644 --- a/.gitignore +++ b/.gitignore @@ -38,8 +38,10 @@ node_modules # Build artifacts out/ -parity-clib-examples/cpp/build/ .vscode rls/ /parity.* + +# cargo remote artifacts +remote-target diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 436b8a156f5..00000000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,261 +0,0 @@ -stages: - - test - - build - - publish - - optional - -image: parity/rust-parity-ethereum-build:xenial -variables: - GIT_STRATEGY: fetch - GIT_SUBMODULE_STRATEGY: recursive - CI_SERVER_NAME: "GitLab CI" - CARGO_TARGET: x86_64-unknown-linux-gnu - -.no_git: &no_git # disable git strategy - variables: - GIT_STRATEGY: none - GIT_SUBMODULE_STRATEGY: none - -.releaseable_branches: # list of git refs for building GitLab artifacts (think "pre-release binaries") - only: &releaseable_branches - - stable - - beta - - tags - - schedules - -.collect_artifacts: &collect_artifacts - artifacts: - name: "${CI_JOB_NAME}_${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}" - when: on_success - expire_in: 1 mos - paths: - - artifacts/ - -.docker-cache-status: &docker-cache-status - variables: - CARGO_HOME: "/ci-cache/parity-ethereum/cargo/${CI_JOB_NAME}" - before_script: - - SCCACHE_ERROR_LOG=/builds/parity/parity-ethereum/sccache_error.log RUST_LOG=sccache::server=debug sccache --start-server - - sccache -s - after_script: - - echo "All crate-types:" - - grep 'parse_arguments.*--crate-type' sccache_error.log | sed -re 's/.*"--crate-type", "([^"]+)".*/\1/' | sort | uniq -c - - echo "Non-cacheable reasons:" - - grep CannotCache sccache_error.log | sed -re 's/.*CannotCache\((.+)\).*/\1/' | sort | uniq -c - tags: - - linux-docker - - -cargo-check 0 3: - stage: test - <<: *docker-cache-status - script: - - time cargo check --target $CARGO_TARGET --locked --no-default-features - - sccache -s - -cargo-check 1 3: - stage: test - <<: *docker-cache-status - script: - - time cargo check --target $CARGO_TARGET --locked --manifest-path util/io/Cargo.toml --no-default-features - - sccache -s - -cargo-check 2 3: - stage: test - <<: *docker-cache-status - script: - - time cargo check --target $CARGO_TARGET --locked --manifest-path util/io/Cargo.toml --features "mio" - - sccache -s - -cargo-audit: - stage: test - <<: *docker-cache-status - script: - - cargo audit - - sccache -s - -validate-chainspecs: - stage: test - <<: *docker-cache-status - script: - - ./scripts/gitlab/validate-chainspecs.sh - - sccache -s - -test-cpp: - stage: build - <<: *docker-cache-status - script: - - ./scripts/gitlab/test-cpp.sh - - sccache -s - -test-linux: - stage: build - <<: *docker-cache-status - script: - - ./scripts/gitlab/test-linux.sh - - sccache -s - -build-android: - stage: build - image: parity/rust-parity-ethereum-android-build:stretch - variables: - CARGO_TARGET: armv7-linux-androideabi - <<: *docker-cache-status - <<: *collect_artifacts - script: - - scripts/gitlab/build-linux.sh - tags: - - linux-docker - -build-linux: &build-linux - stage: build - only: *releaseable_branches - <<: *docker-cache-status - <<: *collect_artifacts - script: - - scripts/gitlab/build-linux.sh - - sccache -s - -build-linux-i386: - <<: *build-linux - image: parity/rust-parity-ethereum-build:i386 - variables: - CARGO_TARGET: i686-unknown-linux-gnu - -build-linux-arm64: - <<: *build-linux - image: parity/rust-parity-ethereum-build:arm64 - variables: - CARGO_TARGET: aarch64-unknown-linux-gnu - -build-linux-armhf: - <<: *build-linux - image: parity/rust-parity-ethereum-build:armhf - variables: - CARGO_TARGET: armv7-unknown-linux-gnueabihf - -build-darwin: - stage: build - only: *releaseable_branches - <<: *collect_artifacts - variables: - CARGO_TARGET: x86_64-apple-darwin - CARGO_HOME: "${CI_PROJECT_DIR}/.cargo" - CC: gcc - CXX: g++ - script: - - scripts/gitlab/build-linux.sh - tags: - - rust-osx - -build-windows: - stage: build - <<: *collect_artifacts - only: *releaseable_branches - variables: - CARGO_TARGET: x86_64-pc-windows-msvc - CARGO_HOME: "C:/ci-cache/parity-ethereum/cargo/$CI_JOB_NAME" - GIT_SUBMODULE_STRATEGY: none - script: - - sh scripts/gitlab/build-windows.sh - tags: - - rust-windows - -publish-docker: - stage: publish - only: *releaseable_branches - cache: {} - dependencies: - - build-linux - tags: - - shell - script: - - scripts/gitlab/publish-docker.sh parity - -publish-snap: &publish-snap - stage: publish - only: *releaseable_branches - <<: *collect_artifacts - image: snapcore/snapcraft - variables: - BUILD_ARCH: amd64 - cache: {} - dependencies: - - build-linux - tags: - - linux-docker - script: - - scripts/gitlab/publish-snap.sh - -publish-snap-i386: - <<: *publish-snap - variables: - BUILD_ARCH: i386 - dependencies: - - build-linux-i386 - -publish-snap-arm64: - <<: *publish-snap - variables: - BUILD_ARCH: arm64 - dependencies: - - build-linux-arm64 - -publish-snap-armhf: - <<: *publish-snap - variables: - BUILD_ARCH: armhf - dependencies: - - build-linux-armhf - -publish-onchain: - stage: publish - only: *releaseable_branches - cache: {} - dependencies: - - build-linux - - build-darwin - - build-windows - script: - - scripts/gitlab/publish-onchain.sh - tags: - - linux-docker - -publish-awss3-release: - image: parity/awscli:latest - stage: publish - only: *releaseable_branches - <<: *no_git - cache: {} - dependencies: - - build-linux - - build-darwin - - build-windows - script: - - echo "__________Push binaries to AWS S3____________" - - case "${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}" in - (beta|stable|nightly) - export BUCKET=releases.parity.io/ethereum; - ;; - (*) - export BUCKET=builds-parity; - ;; - esac - - aws s3 sync ./artifacts s3://${BUCKET}/${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}/ - - echo "__________Read from S3____________" - - aws s3 ls s3://${BUCKET}/${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}} --recursive --human-readable --summarize - tags: - - linux-docker - -publish-docs: - stage: publish - image: parity/rust-parity-ethereum-docs:xenial - only: - - tags - except: - - nightly - cache: {} - script: - - scripts/gitlab/publish-docs.sh - tags: - - linux-docker diff --git a/CHANGELOG.md b/CHANGELOG.md index b986da688cc..6fdf1b30aee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,164 +1,34 @@ -## Parity-Ethereum [v2.4.3](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.3) (2019-03-22) +## OpenEthereum v3.1RC1 + +OpenEthereum 3.1rc1 is a candidate release based on v2.5.13 which is the last stable version known of the client that does not include any of the issues introduced in v2.7. +It removes non core features like Ethereum Classic, Private Transactions, Light Client, Updater, IPFS and Swarm support, currently deprecated flags such as expanse, kotti, mordor testnets. + +Database migration utility currently in beta: https://github.com/openethereum/3.1-db-upgrade-tool + +The full list of included changes from v2.5.13 to v3.1: + +- Remove classic, kotti, mordor, expanse (#52) +- Added bad block header hash for ropsten (#49) +- Remove accounts bloom (#33) +- Bump jsonrpc-- to v15 +- Implement eth/64, remove eth/62 (#46) +- No snapshotting by default (#11814) +- Update Ellaism chainspec +- Prometheus, heavy memory calls removed (#27) +- Update ethereum/tests +- Implement JSON test suite (#11801) +- Fix issues during block sync (#11265) +- Fix race same block (#11400) +- EIP-2537: Precompile for BLS12-381 curve operations (#11707) +- Remove private transactions +- Remove GetNodeData +- Remove IPFS integration (#11532) +- Remove updater +- Remove light client +- Remove C and Java bindings (#11346) +- Remove whisper (#10855) +- EIP-2315: Simple Subroutines for the EVM (#11629) +- Remove deprecated flags (removal of --geth flag) +- Remove support for hardware wallets (#10678) +- Update bootnodes -Parity-Ethereum 2.4.3-beta is a bugfix release that improves performance and stability. This patch release contains a critical bug fix where serving light clients previously led to client crashes. Upgrading is highly recommended. - -The full list of included changes: -- 2.4.3 beta backports ([#10508](https://github.com/paritytech/parity-ethereum/pull/10508)) - - Version: bump beta - - Add additional request tests ([#10503](https://github.com/paritytech/parity-ethereum/pull/10503)) - -## Parity-Ethereum [v2.4.2](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.2) (2019-03-20) - -Parity-Ethereum 2.4.2-beta is a bugfix release that improves performance and stability. - -The full list of included changes: -- 2.4.2 beta backports ([#10488](https://github.com/paritytech/parity-ethereum/pull/10488)) - - Version: bump beta - - Сaching through docker volume ([#10477](https://github.com/paritytech/parity-ethereum/pull/10477)) - - fix win&mac build ([#10486](https://github.com/paritytech/parity-ethereum/pull/10486)) - - fix(extract `timestamp_checked_add` as lib) ([#10383](https://github.com/paritytech/parity-ethereum/pull/10383)) - -## Parity-Ethereum [v2.4.1](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.1) (2019-03-19) - -Parity-Ethereum 2.4.1-beta is a bugfix release that improves performance and stability. - -The full list of included changes: -- 2.4.1 beta backports ([#10471](https://github.com/paritytech/parity-ethereum/pull/10471)) - - Version: bump beta - - Implement parity_versionInfo & parity_setChain on LC; fix parity_setChain ([#10312](https://github.com/paritytech/parity-ethereum/pull/10312)) - - CI publish to aws ([#10446](https://github.com/paritytech/parity-ethereum/pull/10446)) - - CI aws git checkout ([#10451](https://github.com/paritytech/parity-ethereum/pull/10451)) - - Revert "CI aws git checkout ([#10451](https://github.com/paritytech/parity-ethereum/pull/10451))" ([#10456](https://github.com/paritytech/parity-ethereum/pull/10456)) - - Tests parallelized ([#10452](https://github.com/paritytech/parity-ethereum/pull/10452)) - - Ensure static validator set changes are recognized ([#10467](https://github.com/paritytech/parity-ethereum/pull/10467)) - -## Parity-Ethereum [v2.4.0](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.0) (2019-02-25) - -Parity-Ethereum 2.4.0-beta is our trifortnightly minor version release coming with a lot of new features as well as bugfixes and performance improvements. - -Notable changes: -- Account management is now deprecated ([#10213](https://github.com/paritytech/parity-ethereum/pull/10213)) -- Local accounts can now be specified via CLI ([#9960](https://github.com/paritytech/parity-ethereum/pull/9960)) -- Chains can now be reset to a particular block via CLI ([#9782](https://github.com/paritytech/parity-ethereum/pull/9782)) -- Ethash now additionally implements ProgPoW ([#9762](https://github.com/paritytech/parity-ethereum/pull/9762)) -- The `eip1283DisableTransition` flag was added to revert EIP-1283 ([#10214](https://github.com/paritytech/parity-ethereum/pull/10214)) - -The full list of included changes: -- More Backports for Beta 2.4.0 ([#10431](https://github.com/paritytech/parity-ethereum/pull/10431)) - - Revert some changes, could be buggy ([#10399](https://github.com/paritytech/parity-ethereum/pull/10399)) - - Ci: clean up gitlab-ci.yml leftovers from previous merge ([#10429](https://github.com/paritytech/parity-ethereum/pull/10429)) - - 10000 > 5000 ([#10422](https://github.com/paritytech/parity-ethereum/pull/10422)) - - Fix underflow in pip, closes [#10419](https://github.com/paritytech/parity-ethereum/pull/10419) ([#10423](https://github.com/paritytech/parity-ethereum/pull/10423)) - - Fix panic when logging directory does not exist, closes [#10420](https://github.com/paritytech/parity-ethereum/pull/10420) ([#10424](https://github.com/paritytech/parity-ethereum/pull/10424)) - - Update hardcoded headers for Foundation, Ropsten, Kovan and Classic ([#10417](https://github.com/paritytech/parity-ethereum/pull/10417)) -- Backports for Beta 2.4.0 ([#10416](https://github.com/paritytech/parity-ethereum/pull/10416)) - - No-git for publish jobs, empty artifacts dir ([#10393](https://github.com/paritytech/parity-ethereum/pull/10393)) - - Snap: reenable i386, arm64, armhf architecture publishing ([#10386](https://github.com/paritytech/parity-ethereum/pull/10386)) - - Tx pool: always accept local transactions ([#10375](https://github.com/paritytech/parity-ethereum/pull/10375)) - - Fix to_pod storage trie value decoding ([#10368](https://github.com/paritytech/parity-ethereum/pull/10368)) -- Version: mark 2.4.0 beta -- Update to latest mem-db, hash-db and trie-db. ([#10314](https://github.com/paritytech/parity-ethereum/pull/10314)) -- Tx pool: always accept local transactions ([#10375](https://github.com/paritytech/parity-ethereum/pull/10375)) -- Fix(trace_main! macro): don't re-export ([#10384](https://github.com/paritytech/parity-ethereum/pull/10384)) -- Exchanged old(azure) bootnodes with new(ovh) ones ([#10309](https://github.com/paritytech/parity-ethereum/pull/10309)) -- Ethash: implement Progpow ([#9762](https://github.com/paritytech/parity-ethereum/pull/9762)) -- Snap: add the removable-media plug ([#10377](https://github.com/paritytech/parity-ethereum/pull/10377)) -- Add message to IO errors ([#10324](https://github.com/paritytech/parity-ethereum/pull/10324)) -- Chore(bump parity-daemonize): require rust >= 1.31 ([#10359](https://github.com/paritytech/parity-ethereum/pull/10359)) -- Secretstore: use in-memory transport in cluster tests ([#9850](https://github.com/paritytech/parity-ethereum/pull/9850)) -- Add fields to `memzero`'s Cargo.toml ([#10362](https://github.com/paritytech/parity-ethereum/pull/10362)) -- Snap: release untagged versions from branches to the candidate snap channel ([#10357](https://github.com/paritytech/parity-ethereum/pull/10357)) -- Fix(compilation warns): `no-default-features` ([#10346](https://github.com/paritytech/parity-ethereum/pull/10346)) -- No volumes are needed, just run -v volume:/path/in/the/container ([#10345](https://github.com/paritytech/parity-ethereum/pull/10345)) -- Fixed misstype ([#10351](https://github.com/paritytech/parity-ethereum/pull/10351)) -- Snap: prefix version and populate candidate channel ([#10343](https://github.com/paritytech/parity-ethereum/pull/10343)) -- Bundle protocol and packet_id together in chain sync ([#10315](https://github.com/paritytech/parity-ethereum/pull/10315)) -- Role back docker build image and docker deploy image to ubuntu:xenial… ([#10338](https://github.com/paritytech/parity-ethereum/pull/10338)) -- Change docker image based on debian instead of ubuntu due to the chan… ([#10336](https://github.com/paritytech/parity-ethereum/pull/10336)) -- Don't add discovery initiators to the node table ([#10305](https://github.com/paritytech/parity-ethereum/pull/10305)) -- Fix(docker): fix not receives SIGINT ([#10059](https://github.com/paritytech/parity-ethereum/pull/10059)) -- Snap: official image / test ([#10168](https://github.com/paritytech/parity-ethereum/pull/10168)) -- Fix(add helper for timestamp overflows) ([#10330](https://github.com/paritytech/parity-ethereum/pull/10330)) -- Additional error for invalid gas ([#10327](https://github.com/paritytech/parity-ethereum/pull/10327)) -- Revive parity_setMinGasPrice RPC call ([#10294](https://github.com/paritytech/parity-ethereum/pull/10294)) -- Add Statetest support for Constantinople Fix ([#10323](https://github.com/paritytech/parity-ethereum/pull/10323)) -- Fix(parity-clib): grumbles that were not addressed in [#9920](https://github.com/paritytech/parity-ethereum/pull/9920) ([#10154](https://github.com/paritytech/parity-ethereum/pull/10154)) -- Fix(light-rpc): Make `light_sync` generic ([#10238](https://github.com/paritytech/parity-ethereum/pull/10238)) -- Fix publish job ([#10317](https://github.com/paritytech/parity-ethereum/pull/10317)) -- Secure WS-RPC: grant access to all apis ([#10246](https://github.com/paritytech/parity-ethereum/pull/10246)) -- Make specification of protocol in SyncRequester::send_request explicit ([#10295](https://github.com/paritytech/parity-ethereum/pull/10295)) -- Fix: parity-clib/examples/cpp/CMakeLists.txt ([#10313](https://github.com/paritytech/parity-ethereum/pull/10313)) -- Ci optimizations ([#10297](https://github.com/paritytech/parity-ethereum/pull/10297)) -- Increase number of requested block bodies in chain sync ([#10247](https://github.com/paritytech/parity-ethereum/pull/10247)) -- Deprecate account management ([#10213](https://github.com/paritytech/parity-ethereum/pull/10213)) -- Properly handle check_epoch_end_signal errors ([#10015](https://github.com/paritytech/parity-ethereum/pull/10015)) -- Fix(osx and windows builds): bump parity-daemonize ([#10291](https://github.com/paritytech/parity-ethereum/pull/10291)) -- Add missing step for Using `systemd` service file ([#10175](https://github.com/paritytech/parity-ethereum/pull/10175)) -- Call private contract methods from another private contract (read-onl… ([#10086](https://github.com/paritytech/parity-ethereum/pull/10086)) -- Update ring to 0.14 ([#10262](https://github.com/paritytech/parity-ethereum/pull/10262)) -- Fix(secret-store): deprecation warning ([#10301](https://github.com/paritytech/parity-ethereum/pull/10301)) -- Update to jsonrpc-derive 10.0.2, fixes aliases bug ([#10300](https://github.com/paritytech/parity-ethereum/pull/10300)) -- Convert to jsonrpc-derive, use jsonrpc-* from crates.io ([#10298](https://github.com/paritytech/parity-ethereum/pull/10298)) -- Fix Windows build ([#10284](https://github.com/paritytech/parity-ethereum/pull/10284)) -- Don't run the CPP example on CI ([#10285](https://github.com/paritytech/parity-ethereum/pull/10285)) -- Additional tests for uint deserialization. ([#10279](https://github.com/paritytech/parity-ethereum/pull/10279)) -- Prevent silent errors in daemon mode ([#10007](https://github.com/paritytech/parity-ethereum/pull/10007)) -- Fix join-set test to be deterministic. ([#10263](https://github.com/paritytech/parity-ethereum/pull/10263)) -- Update CHANGELOG-2.2.md ([#10254](https://github.com/paritytech/parity-ethereum/pull/10254)) -- Macos heapsize force jemalloc ([#10234](https://github.com/paritytech/parity-ethereum/pull/10234)) -- Allow specifying local accounts via CLI ([#9960](https://github.com/paritytech/parity-ethereum/pull/9960)) -- Take in account zero gas price certification when doing transact_cont… ([#10232](https://github.com/paritytech/parity-ethereum/pull/10232)) -- Update CHANGELOG.md ([#10249](https://github.com/paritytech/parity-ethereum/pull/10249)) -- Fix typo: CHANGELOG-2.1 -> CHANGELOG-2.2 ([#10233](https://github.com/paritytech/parity-ethereum/pull/10233)) -- Update copyright year to 2019. ([#10181](https://github.com/paritytech/parity-ethereum/pull/10181)) -- Fixed: types::transaction::SignedTransaction; ([#10229](https://github.com/paritytech/parity-ethereum/pull/10229)) -- Fix(ManageNetwork): replace Range with RangeInclusive ([#10209](https://github.com/paritytech/parity-ethereum/pull/10209)) -- Import rpc transactions sequentially ([#10051](https://github.com/paritytech/parity-ethereum/pull/10051)) -- Enable St-Peters-Fork ("Constantinople Fix") ([#10223](https://github.com/paritytech/parity-ethereum/pull/10223)) -- Add EIP-1283 disable transition ([#10214](https://github.com/paritytech/parity-ethereum/pull/10214)) -- Echo CORS request headers by default ([#10221](https://github.com/paritytech/parity-ethereum/pull/10221)) -- Happy New Year! ([#10211](https://github.com/paritytech/parity-ethereum/pull/10211)) -- Perform stripping during build ([#10208](https://github.com/paritytech/parity-ethereum/pull/10208)) -- Remove CallContract and RegistryInfo re-exports from `ethcore/client` ([#10205](https://github.com/paritytech/parity-ethereum/pull/10205)) -- Extract CallContract and RegistryInfo traits into their own crate ([#10178](https://github.com/paritytech/parity-ethereum/pull/10178)) -- Update the changelogs for 2.1.11, 2.2.6, 2.2.7, and 2.3.0 ([#10197](https://github.com/paritytech/parity-ethereum/pull/10197)) -- Cancel Constantinople HF on POA Core ([#10198](https://github.com/paritytech/parity-ethereum/pull/10198)) -- Adds cli interface to allow reseting chain to a particular block ([#9782](https://github.com/paritytech/parity-ethereum/pull/9782)) -- Run all `igd` methods in its own thread ([#10195](https://github.com/paritytech/parity-ethereum/pull/10195)) -- Pull constantinople on ethereum network ([#10189](https://github.com/paritytech/parity-ethereum/pull/10189)) -- Update for Android cross-compilation. ([#10180](https://github.com/paritytech/parity-ethereum/pull/10180)) -- Version: bump fork blocks for kovan and foundation ([#10186](https://github.com/paritytech/parity-ethereum/pull/10186)) -- Handle the case for contract creation on an empty but exist account w… ([#10065](https://github.com/paritytech/parity-ethereum/pull/10065)) -- Align personal_unlockAccount behaviour when permanent unlock is disab… ([#10060](https://github.com/paritytech/parity-ethereum/pull/10060)) -- Drop `runtime` after others (especially `ws_server`) ([#10179](https://github.com/paritytech/parity-ethereum/pull/10179)) -- Version: bump nightly to 2.4 ([#10165](https://github.com/paritytech/parity-ethereum/pull/10165)) -- Skip locking in statedb for non-canon blocks ([#10141](https://github.com/paritytech/parity-ethereum/pull/10141)) -- Remove reference to ui-interface command-line option ([#10170](https://github.com/paritytech/parity-ethereum/pull/10170)) -- Fix [#9822](https://github.com/paritytech/parity-ethereum/pull/9822): trace_filter does not return failed contract creation ([#10140](https://github.com/paritytech/parity-ethereum/pull/10140)) -- Fix _cannot recursively call into `Core`_ issue ([#10144](https://github.com/paritytech/parity-ethereum/pull/10144)) -- Fix(whisper): correct PoW calculation ([#10166](https://github.com/paritytech/parity-ethereum/pull/10166)) -- Bump JSON-RPC ([#10151](https://github.com/paritytech/parity-ethereum/pull/10151)) -- Ping nodes from discovery ([#10167](https://github.com/paritytech/parity-ethereum/pull/10167)) -- Fix(android): remove dependency to libusb ([#10161](https://github.com/paritytech/parity-ethereum/pull/10161)) -- Refactor(trim_right_matches -> trim_end_matches) ([#10159](https://github.com/paritytech/parity-ethereum/pull/10159)) -- Merge Machine and WithRewards ([#10071](https://github.com/paritytech/parity-ethereum/pull/10071)) - -## Previous releases - -- [CHANGELOG-2.3](docs/CHANGELOG-2.3.md) (_stable_) -- [CHANGELOG-2.2](docs/CHANGELOG-2.2.md) (EOL: 2019-02-25) -- [CHANGELOG-2.1](docs/CHANGELOG-2.1.md) (EOL: 2019-01-16) -- [CHANGELOG-2.0](docs/CHANGELOG-2.0.md) (EOL: 2018-11-15) -- [CHANGELOG-1.11](docs/CHANGELOG-1.11.md) (EOL: 2018-09-19) -- [CHANGELOG-1.10](docs/CHANGELOG-1.10.md) (EOL: 2018-07-18) -- [CHANGELOG-1.9](docs/CHANGELOG-1.9.md) (EOL: 2018-05-09) -- [CHANGELOG-1.8](docs/CHANGELOG-1.8.md) (EOL: 2018-03-22) -- [CHANGELOG-1.7](docs/CHANGELOG-1.7.md) (EOL: 2018-01-25) -- [CHANGELOG-1.6](docs/CHANGELOG-1.6.md) (EOL: 2017-10-15) -- [CHANGELOG-1.5](docs/CHANGELOG-1.5.md) (EOL: 2017-07-28) -- [CHANGELOG-1.4](docs/CHANGELOG-1.4.md) (EOL: 2017-03-13) -- [CHANGELOG-1.3](docs/CHANGELOG-1.3.md) (EOL: 2017-01-19) -- [CHANGELOG-1.2](docs/CHANGELOG-1.2.md) (EOL: 2016-11-07) -- [CHANGELOG-1.1](docs/CHANGELOG-1.1.md) (EOL: 2016-08-12) -- [CHANGELOG-1.0](docs/CHANGELOG-1.0.md) (EOL: 2016-06-24) -- [CHANGELOG-0.9](docs/CHANGELOG-0.9.md) (EOL: 2016-05-02) diff --git a/Cargo.lock b/Cargo.lock index c1b7f247968..e9fe52db71d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,506 +1,689 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "aes" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9" +dependencies = [ + "aes-soft", + "aesni", + "block-cipher-trait", +] + +[[package]] +name = "aes-ctr" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e5b0458ea3beae0d1d8c0f3946564f8e10f90646cf78c06b4351052058d1ee" +dependencies = [ + "aes-soft", + "aesni", + "ctr", + "stream-cipher", +] + +[[package]] +name = "aes-soft" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" +dependencies = [ + "block-cipher-trait", + "byteorder", + "opaque-debug", +] + +[[package]] +name = "aesni" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" +dependencies = [ + "block-cipher-trait", + "opaque-debug", + "stream-cipher", +] + +[[package]] +name = "aho-corasick" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5" +dependencies = [ + "memchr", +] + [[package]] name = "aho-corasick" -version = "0.6.8" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" dependencies = [ - "memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr", ] [[package]] name = "ansi_term" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455" [[package]] name = "ansi_term" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" dependencies = [ - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8", ] [[package]] name = "app_dirs" version = "1.2.1" -source = "git+https://github.com/paritytech/app-dirs-rs#0b37f9481ce29e9d5174ad185bca695b206368eb" +source = "git+https://github.com/openethereum/app-dirs-rs#0b37f9481ce29e9d5174ad185bca695b206368eb" dependencies = [ - "ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "shell32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "xdg 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ole32-sys", + "shell32-sys", + "winapi 0.2.8", + "xdg", ] [[package]] name = "arrayref" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" [[package]] name = "arrayvec" -version = "0.4.7" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" dependencies = [ - "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "nodrop", ] [[package]] -name = "ascii" -version = "0.7.1" +name = "arrayvec" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" [[package]] name = "assert_matches" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7deb0a829ca7bcfaf5da70b073a8d128619259a7be8216a355e23f00763059e5" [[package]] name = "atty" -version = "0.2.11" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "winapi 0.3.8", ] +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + [[package]] name = "backtrace" -version = "0.3.9" +version = "0.3.40" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" dependencies = [ - "backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys", + "cfg-if", + "libc", + "rustc-demangle", ] [[package]] name = "backtrace-sys" -version = "0.1.24" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" dependencies = [ - "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "libc", ] -[[package]] -name = "base-x" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "base64" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "safemem", ] [[package]] name = "base64" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bincode" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", ] [[package]] name = "bit-set" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c" dependencies = [ - "bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bit-vec", ] [[package]] name = "bit-vec" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f" [[package]] name = "bitflags" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" [[package]] name = "bitflags" -version = "0.9.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] -name = "bitflags" -version = "1.0.4" +name = "bitvec" +version = "0.17.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c" +dependencies = [ + "either", + "radium", +] + +[[package]] +name = "block-buffer" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" +dependencies = [ + "arrayref", + "byte-tools 0.2.0", +] [[package]] name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools 0.3.1", + "byteorder", + "generic-array 0.12.3", +] + +[[package]] +name = "block-cipher-trait" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" +dependencies = [ + "generic-array 0.12.3", +] + +[[package]] +name = "block-modes" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31aa8410095e39fdb732909fb5730a48d5bd7c2e3cd76bd1b07b3dbea130c529" +dependencies = [ + "block-cipher-trait", + "block-padding", +] + +[[package]] +name = "block-padding" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1", ] [[package]] name = "blooms-db" version = "0.1.0" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "ethbloom 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "criterion 0.3.0", + "ethbloom 0.5.0", + "parking_lot 0.7.1", + "tempdir", + "tiny-keccak 1.5.0", ] [[package]] name = "bn" version = "0.4.4" -source = "git+https://github.com/paritytech/bn#2a71dbde5ca93451c8da2135767896a64483759e" +source = "git+https://github.com/paritytech/bn#6079255e65793038b9a6e5292203eab482737cc2" +dependencies = [ + "byteorder", + "crunchy 0.2.2", + "lazy_static", + "rand 0.5.6", + "rustc-hex 2.0.1", +] + +[[package]] +name = "bstr" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d6c2c5b58ab920a4f5aeaaca34b4488074e8cc7596af94e6f8c6ff247c60245" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "memchr", + "regex-automata", + "serde", ] +[[package]] +name = "build_const" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" + +[[package]] +name = "byte-slice-cast" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0a5e3906bcbf133e33c1d4d95afc664ad37fbdb9f6568d8043e7ea8c27d93d3" + [[package]] name = "byte-tools" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "byteorder" -version = "1.2.6" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" [[package]] name = "bytes" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "either", + "iovec", ] [[package]] -name = "cast" -version = "0.2.2" +name = "c2-chacha" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" +dependencies = [ + "ppv-lite86", +] [[package]] -name = "cc" -version = "1.0.28" +name = "cast" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" [[package]] -name = "cesu8" -version = "1.1.0" +name = "cc" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa87058dce70a3ff5621797f1506cb837edd02ac4c0ae642b4542dce802908b8" [[package]] name = "cfg-if" -version = "0.1.5" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "chainspec" version = "0.1.0" dependencies = [ - "ethjson 0.1.0", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "ethjson", + "serde_json", ] [[package]] name = "chrono" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cid" -version = "0.3.0" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68" dependencies = [ - "integer-encoding 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "multibase 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "multihash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "num-integer", + "num-traits 0.2.8", + "time", ] [[package]] name = "clap" -version = "2.32.0" +version = "2.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" dependencies = [ - "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term 0.11.0", + "atty", + "bitflags 1.2.1", + "strsim 0.8.0", + "textwrap 0.11.0", + "unicode-width", + "vec_map", ] [[package]] name = "cli-signer" version = "1.4.0" dependencies = [ - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-rpc 1.12.0", - "parity-rpc-client 1.4.0", - "rpassword 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.4.2", + "futures", + "parity-rpc", + "parity-rpc-client", + "rpassword", ] [[package]] name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", ] [[package]] name = "cmake" -version = "0.1.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "combine" -version = "3.6.1" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fb25b677f8bf1eb325017cb6bb8452f87969db0fedb4f757b297bee78a7c62" dependencies = [ - "ascii 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", ] [[package]] name = "common-types" version = "0.1.0" dependencies = [ - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethjson 0.1.0", - "ethkey 0.3.0", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp_derive 0.1.0", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unexpected 0.1.0", + "ethereum-types 0.4.2", + "ethjson", + "ethkey", + "heapsize", + "keccak-hash", + "parity-bytes", + "rlp 0.3.0", + "rlp_derive", + "rustc-hex 1.0.0", + "unexpected", ] [[package]] -name = "criterion" -version = "0.2.5" +name = "constant_time_eq" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", - "criterion-plot 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "criterion-stats 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "csv 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "handlebars 0.32.4 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools-num 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120" [[package]] -name = "criterion-plot" -version = "0.2.5" +name = "crc" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", + "build_const", ] [[package]] -name = "criterion-stats" -version = "0.2.5" +name = "criterion" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "thread-scoped 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +checksum = "0363053954f3e679645fc443321ca128b7b950a6fe288cf5f9335cc22ee58394" +dependencies = [ + "atty", + "cast", + "clap", + "criterion-plot 0.3.1", + "csv", + "itertools 0.8.1", + "lazy_static", + "libc", + "num-traits 0.2.8", + "rand_core 0.3.1", + "rand_os 0.1.3", + "rand_xoshiro 0.1.0", + "rayon", + "rayon-core", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", ] [[package]] -name = "crossbeam" -version = "0.4.1" +name = "criterion" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938703e165481c8d612ea3479ac8342e5615185db37765162e762ec3523e2fc6" dependencies = [ - "crossbeam-channel 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-deque 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty", + "cast", + "clap", + "criterion-plot 0.4.0", + "csv", + "itertools 0.8.1", + "lazy_static", + "num-traits 0.2.8", + "rand_core 0.5.1", + "rand_os 0.2.2", + "rand_xoshiro 0.3.1", + "rayon", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", ] [[package]] -name = "crossbeam-channel" -version = "0.2.6" +name = "criterion-plot" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f9212ddf2f4a9eb2d401635190600656a1f88a932ef53d06e7fa4c7e02fb8e" dependencies = [ - "crossbeam-epoch 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "cast", + "itertools 0.8.1", ] [[package]] -name = "crossbeam-deque" -version = "0.2.0" +name = "criterion-plot" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eccdc6ce8bbe352ca89025bee672aa6d24f4eb8c53e3a8b5d1bc58011da072a2" dependencies = [ - "crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cast", + "itertools 0.8.1", ] [[package]] name = "crossbeam-deque" -version = "0.5.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" dependencies = [ - "crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch", + "crossbeam-utils 0.6.6", ] [[package]] name = "crossbeam-deque" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" dependencies = [ - "crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch", + "crossbeam-utils 0.6.6", ] [[package]] name = "crossbeam-epoch" -version = "0.3.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" dependencies = [ - "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.12", + "cfg-if", + "crossbeam-utils 0.6.6", + "lazy_static", + "memoffset", + "scopeguard 1.0.0", ] [[package]] -name = "crossbeam-epoch" -version = "0.5.2" +name = "crossbeam-queue" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" dependencies = [ - "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6", ] [[package]] -name = "crossbeam-epoch" -version = "0.6.1" +name = "crossbeam-queue" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" dependencies = [ - "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "crossbeam-utils 0.7.2", + "maybe-uninit", ] [[package]] name = "crossbeam-utils" -version = "0.2.2" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" dependencies = [ - "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "lazy_static", ] [[package]] name = "crossbeam-utils" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "crossbeam-utils" -version = "0.6.2" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ - "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0", + "cfg-if", + "lazy_static", ] [[package]] name = "crunchy" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" [[package]] name = "crunchy" -version = "0.2.1" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-mac" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7afa06d05a046c7a47c3a849907ec303504608c927f4e85f7bfff22b7180d971" +dependencies = [ + "constant_time_eq", + "generic-array 0.9.0", +] + +[[package]] +name = "crypto-mac" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" +dependencies = [ + "generic-array 0.12.3", + "subtle 1.0.0", +] [[package]] name = "csv" -version = "1.0.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d" dependencies = [ - "csv-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr", + "csv-core", + "itoa", + "ryu", + "serde", ] [[package]] name = "csv-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c" dependencies = [ - "memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr", ] [[package]] name = "ct-logs" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b4660f8b07a560a88c02d76286edb9f0d5d64e495d2b0f233186155aa51be1f" +dependencies = [ + "sct", +] + +[[package]] +name = "ctr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736" dependencies = [ - "sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait", + "stream-cipher", ] [[package]] @@ -508,1660 +691,1920 @@ name = "ctrlc" version = "1.1.1" source = "git+https://github.com/paritytech/rust-ctrlc.git#b523017108bb2d571a7a69bd97bc406e63bc7a9d" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys", + "libc", + "winapi 0.2.8", ] [[package]] name = "derive_more" -version = "0.14.0" +version = "0.99.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298998b1cf6b5b2c8a7b023dfd45821825ce3ba8a8af55c921a0e734e4653f76" dependencies = [ - "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.20", + "quote 1.0.7", + "syn 1.0.40", ] [[package]] name = "difference" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3304d19798a8e067e48d8e69b2c37f0b5e9b4e462504ad9e27e9f3fce02bba8" [[package]] name = "digest" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" +dependencies = [ + "generic-array 0.9.0", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" dependencies = [ - "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3", ] [[package]] name = "dir" version = "0.1.2" dependencies = [ - "app_dirs 1.2.1 (git+https://github.com/paritytech/app-dirs-rs)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "home 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "journaldb 0.2.0", + "app_dirs", + "ethereum-types 0.4.2", + "home 0.3.4", + "journaldb", ] [[package]] name = "docopt" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f525a586d310c87df72ebcd98009e57f1cc030c8c268305287a476beb653969" dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "regex 1.3.9", + "serde", + "strsim 0.9.2", ] [[package]] name = "edit-distance" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbaaaf38131deb9ca518a274a45bfdb8771f139517b073b16c2d3d32ae5037b" + +[[package]] +name = "eip-152" +version = "0.1.0" +dependencies = [ + "rustc-hex 2.0.1", +] [[package]] name = "eip-712" version = "0.1.0" dependencies = [ - "ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lunarity-lexer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "toolshed 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "validator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "validator_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi", + "ethereum-types 0.4.2", + "failure", + "indexmap", + "itertools 0.7.11", + "keccak-hash", + "lazy_static", + "lunarity-lexer", + "regex 1.3.9", + "rustc-hex 2.0.1", + "serde", + "serde_derive", + "serde_json", + "validator", + "validator_derive", ] [[package]] name = "either" -version = "1.5.0" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" [[package]] name = "elastic-array" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "073be79b6538296faf81c631872676600616073817dd9a440c477ad09b408983" dependencies = [ - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", + "heapsize", ] [[package]] name = "enum_primitive" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180" dependencies = [ - "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.43", ] [[package]] name = "env_logger" version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" dependencies = [ - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "atty", + "humantime", + "log", + "regex 1.3.9", + "termcolor", ] [[package]] name = "error-chain" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab49e9dcb602294bc42f9a7dfc9bc6e936fca4418ea300dbfb84fe16de0b7d9" dependencies = [ - "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace", + "version_check", ] [[package]] name = "eth-secp256k1" version = "0.5.7" -source = "git+https://github.com/paritytech/rust-secp256k1#ccc06e7480148b723eb44ac56cf4d20eec380b6f" +source = "git+https://github.com/paritytech/rust-secp256k1?rev=ccc06e7480148b723eb44ac56cf4d20eec380b6f#ccc06e7480148b723eb44ac56cf4d20eec380b6f" +dependencies = [ + "arrayvec 0.4.12", + "cc", + "cfg-if", + "rand 0.4.6", +] + +[[package]] +name = "eth_pairings" +version = "0.6.0" +source = "git+https://github.com/matter-labs/eip1962.git?rev=ece6cbabc41948db4200e41f0bfdab7ab94c7af8#ece6cbabc41948db4200e41f0bfdab7ab94c7af8" +dependencies = [ + "byteorder", + "eth_pairings_repr_derive", + "fixed_width_field", + "fixed_width_group_and_loop", + "num-bigint 0.2.3", + "num-traits 0.2.8", + "once_cell", + "static_assertions", +] + +[[package]] +name = "eth_pairings_repr_derive" +version = "0.2.0" +source = "git+https://github.com/matter-labs/eip1962.git?rev=ece6cbabc41948db4200e41f0bfdab7ab94c7af8#ece6cbabc41948db4200e41f0bfdab7ab94c7af8" dependencies = [ - "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "proc-macro2 1.0.20", + "quote 1.0.7", + "syn 1.0.40", ] [[package]] name = "ethabi" version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb362fde43ed0b50b258bb0c72b72b3dccfd29f8de9506295eaf9251c49ca31" dependencies = [ - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain", + "ethereum-types 0.4.2", + "rustc-hex 2.0.1", + "serde", + "serde_derive", + "serde_json", + "tiny-keccak 1.5.0", ] [[package]] name = "ethabi-contract" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "795e25fd868e12a59ca235dbe1f6cc8f1eba8f67d6a39438b29535e0126e0c27" [[package]] name = "ethabi-derive" version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66a587250c8190be9d6ae28d67b8957ed97cb9eee2e272173a20593ab054a075" dependencies = [ - "ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi", + "heck", + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.26", ] [[package]] name = "ethash" version = "1.12.0" dependencies = [ - "criterion 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "primal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "criterion 0.2.11", + "crunchy 0.1.6", + "either", + "ethereum-types 0.4.2", + "keccak-hash", + "log", + "memmap", + "parking_lot 0.7.1", + "primal", + "rustc-hex 1.0.0", + "serde_json", + "tempdir", ] [[package]] name = "ethbloom" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a93a43ce2e9f09071449da36bfa7a1b20b950ee344b6904ff23de493b03b386" +dependencies = [ + "crunchy 0.1.6", + "ethereum-types-serialize", + "fixed-hash 0.2.2", + "serde", + "tiny-keccak 1.5.0", +] + +[[package]] +name = "ethbloom" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71a6567e6fd35589fea0c63b94b4cf2e55573e413901bdbe60ab15cf0e25e5df" dependencies = [ - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types-serialize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy 0.2.2", + "fixed-hash 0.6.1", + "impl-rlp", + "impl-serde", + "tiny-keccak 2.0.2", ] [[package]] name = "ethcore" version = "1.12.0" dependencies = [ - "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "blooms-db 0.1.0", - "bn 0.4.4 (git+https://github.com/paritytech/bn)", - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "common-types 0.1.0", - "criterion 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 6.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethash 1.12.0", - "ethcore-accounts 0.1.0", - "ethcore-blockchain 0.1.0", - "ethcore-bloom-journal 0.1.0", - "ethcore-call-contract 0.1.0", - "ethcore-db 0.1.0", - "ethcore-io 1.12.0", - "ethcore-miner 1.12.0", - "ethcore-stratum 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethjson 0.1.0", - "ethkey 0.3.0", - "evm 0.1.0", - "fetch 0.1.0", - "hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "journaldb 0.2.0", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term 0.10.2", + "blooms-db", + "common-types", + "criterion 0.2.11", + "crossbeam-utils 0.6.6", + "eip-152", + "env_logger", + "error-chain", + "ethabi", + "ethabi-contract", + "ethabi-derive", + "ethash", + "ethcore-accounts", + "ethcore-blockchain", + "ethcore-bloom-journal", + "ethcore-builtin", + "ethcore-call-contract", + "ethcore-db", + "ethcore-io", + "ethcore-miner", + "ethcore-stratum", + "ethereum-types 0.4.2", + "ethjson", + "ethkey", + "evm", + "fetch", + "globset", + "hash-db", + "heapsize", + "hex-literal", + "itertools 0.5.10", + "journaldb", + "keccak-hash", "keccak-hasher 0.1.1", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-memorydb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-rocksdb 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "len-caching-lock 0.1.1", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "macros 0.1.0", - "memory-cache 0.1.0", - "memory-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-crypto 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-runtime 0.1.0", - "parity-snappy 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "patricia-trie-ethereum 0.1.0", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp_compress 0.1.0", - "rlp_derive 0.1.0", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "stats 0.1.0", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "time-utils 0.1.0", - "trace-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-standardmap 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "triehash-ethereum 0.2.0", - "unexpected 0.1.0", - "using_queue 0.1.0", - "vm 0.1.0", - "wasm 0.1.0", + "kvdb", + "kvdb-memorydb", + "kvdb-rocksdb", + "lazy_static", + "len-caching-lock", + "log", + "lru-cache", + "macros", + "maplit", + "memory-cache", + "memory-db 0.11.0", + "num_cpus", + "parity-bytes", + "parity-runtime", + "parity-snappy", + "parking_lot 0.7.1", + "patricia-trie-ethereum", + "rand 0.4.6", + "rayon", + "regex 1.3.9", + "rlp 0.3.0", + "rlp_compress", + "rlp_derive", + "rustc-hex 1.0.0", + "serde", + "serde_derive", + "serde_json", + "stats", + "tempdir", + "tempfile", + "time-utils", + "trace-time", + "trie-db", + "trie-standardmap", + "triehash-ethereum", + "unexpected", + "using_queue", + "vm", + "walkdir", + "wasm", ] [[package]] name = "ethcore-accounts" version = "0.1.0" dependencies = [ - "common-types 0.1.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "ethstore 0.2.1", - "fake-hardware-wallet 0.0.1", - "hardware-wallet 1.12.0", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "common-types", + "ethereum-types 0.4.2", + "ethkey", + "ethstore", + "log", + "parking_lot 0.7.1", + "serde", + "serde_derive", + "serde_json", + "tempdir", ] [[package]] name = "ethcore-blockchain" version = "0.1.0" dependencies = [ - "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "blooms-db 0.1.0", - "common-types 0.1.0", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-db 0.1.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-memorydb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp_compress 0.1.0", - "rlp_derive 0.1.0", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term 0.11.0", + "blooms-db", + "common-types", + "env_logger", + "ethcore-db", + "ethereum-types 0.4.2", + "ethkey", + "heapsize", + "itertools 0.5.10", + "keccak-hash", + "kvdb", + "kvdb-memorydb", + "log", + "parity-bytes", + "parking_lot 0.7.1", + "rand 0.6.5", + "rayon", + "rlp 0.3.0", + "rlp_compress", + "rlp_derive", + "rustc-hex 1.0.0", + "tempdir", + "triehash-ethereum", ] [[package]] name = "ethcore-bloom-journal" version = "0.1.0" dependencies = [ - "siphasher 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "siphasher", ] [[package]] -name = "ethcore-call-contract" +name = "ethcore-builtin" version = "0.1.0" dependencies = [ - "common-types 0.1.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bn", + "byteorder", + "eip-152", + "eth_pairings", + "ethereum-types 0.4.2", + "ethjson", + "ethkey", + "hex-literal", + "keccak-hash", + "log", + "macros", + "maplit", + "num", + "parity-bytes", + "parity-crypto 0.4.2", ] [[package]] -name = "ethcore-db" +name = "ethcore-call-contract" version = "0.1.0" dependencies = [ - "common-types 0.1.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp_derive 0.1.0", + "common-types", + "ethereum-types 0.4.2", + "parity-bytes", ] [[package]] -name = "ethcore-io" -version = "1.12.0" +name = "ethcore-db" +version = "0.1.0" dependencies = [ - "crossbeam-deque 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "timer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "common-types", + "ethereum-types 0.4.2", + "heapsize", + "kvdb", + "parking_lot 0.7.1", + "rlp 0.3.0", + "rlp_derive", ] [[package]] -name = "ethcore-light" +name = "ethcore-io" version = "1.12.0" dependencies = [ - "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "common-types 0.1.0", - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.0", - "ethcore-blockchain 0.1.0", - "ethcore-db 0.1.0", - "ethcore-io 1.12.0", - "ethcore-network 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "failsafe 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fastmap 0.1.0", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "journaldb 0.2.0", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hasher 0.1.1", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-memorydb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memory-cache 0.1.0", - "memory-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "patricia-trie-ethereum 0.1.0", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp_derive 0.1.0", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "stats 0.1.0", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "triehash-ethereum 0.2.0", - "vm 0.1.0", + "crossbeam-deque 0.6.3", + "fnv", + "futures", + "log", + "mio", + "num_cpus", + "parking_lot 0.7.1", + "slab 0.4.2", + "time", + "timer", + "tokio", ] [[package]] name = "ethcore-logger" version = "1.12.0" dependencies = [ - "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term 0.10.2", + "arrayvec 0.4.12", + "atty", + "env_logger", + "lazy_static", + "log", + "parking_lot 0.7.1", + "regex 1.3.9", + "time", ] [[package]] name = "ethcore-miner" version = "1.12.0" dependencies = [ - "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "common-types 0.1.0", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 6.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethash 1.12.0", - "ethcore-call-contract 0.1.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "fetch 0.1.0", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-runtime 0.1.0", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "price-info 1.12.0", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "trace-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "transaction-pool 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term 0.10.2", + "common-types", + "env_logger", + "error-chain", + "ethabi", + "ethabi-contract", + "ethabi-derive", + "ethash", + "ethcore-call-contract", + "ethereum-types 0.4.2", + "ethkey", + "fetch", + "futures", + "heapsize", + "hyper 0.12.35", + "keccak-hash", + "linked-hash-map", + "log", + "parity-runtime", + "parking_lot 0.7.1", + "price-info", + "rlp 0.3.0", + "rustc-hex 1.0.0", + "trace-time", + "transaction-pool", + "url 2.1.0", ] [[package]] name = "ethcore-network" version = "1.12.0" dependencies = [ - "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-io 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "ipnetwork 0.12.8 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-crypto 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-snappy 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "assert_matches", + "error-chain", + "ethcore-io", + "ethereum-types 0.4.2", + "ethkey", + "ipnetwork", + "lazy_static", + "libc", + "parity-crypto 0.3.1", + "parity-snappy", + "rlp 0.3.0", + "semver", + "serde", + "serde_derive", ] [[package]] name = "ethcore-network-devp2p" version = "1.12.0" dependencies = [ - "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-io 1.12.0", - "ethcore-network 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "igd 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ipnetwork 0.12.8 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-crypto 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-path 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-snappy 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ethcore-private-tx" -version = "1.0.0" -dependencies = [ - "common-types 0.1.0", - "derive_more 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 6.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.0", - "ethcore-call-contract 0.1.0", - "ethcore-io 1.12.0", - "ethcore-miner 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethjson 0.1.0", - "ethkey 0.3.0", - "fetch 0.1.0", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-crypto 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "patricia-trie-ethereum 0.1.0", - "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp_derive 0.1.0", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "transaction-pool 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term 0.10.2", + "assert_matches", + "bytes", + "env_logger", + "error-chain", + "ethcore-io", + "ethcore-network", + "ethereum-types 0.4.2", + "ethkey", + "igd", + "ipnetwork", + "keccak-hash", + "libc", + "log", + "lru-cache", + "mio", + "parity-bytes", + "parity-crypto 0.3.1", + "parity-path", + "parity-snappy", + "parking_lot 0.7.1", + "rand 0.4.6", + "rlp 0.3.0", + "rust-crypto", + "rustc-hex 1.0.0", + "serde", + "serde_derive", + "serde_json", + "slab 0.2.0", + "tempdir", + "tiny-keccak 1.5.0", ] [[package]] name = "ethcore-secretstore" version = "1.0.0" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "common-types 0.1.0", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 6.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.0", - "ethcore-accounts 0.1.0", - "ethcore-call-contract 0.1.0", - "ethcore-sync 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-rocksdb 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-crypto 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-runtime 0.1.0", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "common-types", + "env_logger", + "ethabi", + "ethabi-contract", + "ethabi-derive", + "ethcore", + "ethcore-accounts", + "ethcore-call-contract", + "ethcore-sync", + "ethereum-types 0.4.2", + "ethkey", + "futures", + "hyper 0.12.35", + "jsonrpc-server-utils", + "keccak-hash", + "kvdb", + "kvdb-rocksdb", + "lazy_static", + "log", + "parity-bytes", + "parity-crypto 0.3.1", + "parity-runtime", + "parking_lot 0.7.1", + "percent-encoding 2.1.0", + "rustc-hex 1.0.0", + "serde", + "serde_derive", + "serde_json", + "tempdir", + "tiny-keccak 1.5.0", + "tokio", + "tokio-io", + "tokio-service", + "url 2.1.0", ] [[package]] name = "ethcore-service" version = "0.1.0" dependencies = [ - "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.0", - "ethcore-blockchain 0.1.0", - "ethcore-db 0.1.0", - "ethcore-io 1.12.0", - "ethcore-private-tx 1.0.0", - "ethcore-sync 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-rocksdb 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "trace-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term 0.10.2", + "error-chain", + "ethcore", + "ethcore-blockchain", + "ethcore-db", + "ethcore-io", + "ethcore-sync", + "ethereum-types 0.4.2", + "kvdb", + "kvdb-rocksdb", + "log", + "tempdir", + "trace-time", ] [[package]] name = "ethcore-stratum" version = "1.12.0" dependencies = [ - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-tcp-server 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger", + "ethereum-types 0.4.2", + "jsonrpc-core", + "jsonrpc-tcp-server", + "keccak-hash", + "log", + "parking_lot 0.7.1", + "tokio", + "tokio-io", ] [[package]] name = "ethcore-sync" version = "1.12.0" dependencies = [ - "common-types 0.1.0", - "enum_primitive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.0", - "ethcore-io 1.12.0", - "ethcore-light 1.12.0", - "ethcore-network 1.12.0", - "ethcore-network-devp2p 1.12.0", - "ethcore-private-tx 1.0.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "ethstore 0.2.1", - "fastmap 0.1.0", - "hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "common-types", + "derive_more", + "enum_primitive", + "env_logger", + "ethcore", + "ethcore-io", + "ethcore-network", + "ethcore-network-devp2p", + "ethereum-forkid", + "ethereum-types 0.4.2", + "ethkey", + "ethstore", + "fastmap", + "hash-db", + "heapsize", + "keccak-hash", "keccak-hasher 0.1.1", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-memorydb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "macros 0.1.0", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "trace-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "triehash-ethereum 0.2.0", + "kvdb", + "kvdb-memorydb", + "log", + "macros", + "parity-bytes", + "parking_lot 0.7.1", + "primitive-types", + "rand 0.4.6", + "rlp 0.3.0", + "rlp 0.4.5", + "rustc-hex 1.0.0", + "stats", + "trace-time", + "triehash-ethereum", +] + +[[package]] +name = "ethereum-forkid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3010d8372e3a76d4e2c44de0a080257ab62b6d108857ee7bd70fe8dfb2815f13" +dependencies = [ + "crc", + "ethereum-types 0.9.2", + "maplit", + "parity-util-mem", + "rlp 0.4.5", + "rlp-derive", ] [[package]] name = "ethereum-types" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e742184dc63a01c8ea0637369f8faa27c40f537949908a237f95c05e68d2c96" +dependencies = [ + "crunchy 0.1.6", + "ethbloom 0.5.0", + "ethereum-types-serialize", + "fixed-hash 0.2.2", + "serde", + "uint 0.4.1", +] + +[[package]] +name = "ethereum-types" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "473aecff686bd8e7b9db0165cbbb53562376b39bf35b427f0c60446a9e1634b0" dependencies = [ - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "ethbloom 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types-serialize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ethbloom 0.9.2", + "fixed-hash 0.6.1", + "impl-rlp", + "impl-serde", + "primitive-types", + "uint 0.8.5", ] [[package]] name = "ethereum-types-serialize" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1873d77b32bc1891a79dad925f2acbc318ee942b38b9110f9dbc5fbeffcea350" dependencies = [ - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", ] [[package]] name = "ethjson" version = "0.1.0" dependencies = [ - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.4.2", + "macros", + "maplit", + "rustc-hex 1.0.0", + "serde", + "serde_derive", + "serde_json", ] [[package]] name = "ethkey" version = "0.3.0" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "edit-distance 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memzero 0.1.0", - "parity-crypto 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wordlist 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "edit-distance", + "eth-secp256k1", + "ethereum-types 0.4.2", + "lazy_static", + "log", + "memzero", + "parity-crypto 0.3.1", + "parity-wordlist", + "quick-error", + "rand 0.4.6", + "rustc-hex 1.0.0", + "serde", + "serde_derive", + "tiny-keccak 1.5.0", ] [[package]] name = "ethkey-cli" version = "0.1.0" dependencies = [ - "docopt 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "panic_hook 0.1.0", - "parity-wordlist 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "docopt", + "env_logger", + "ethkey", + "panic_hook", + "parity-wordlist", + "rustc-hex 1.0.0", + "serde", + "serde_derive", + "threadpool", ] [[package]] name = "ethstore" version = "0.2.1" dependencies = [ - "dir 0.1.2", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-crypto 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wordlist 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "dir", + "ethereum-types 0.4.2", + "ethkey", + "itertools 0.5.10", + "lazy_static", + "libc", + "log", + "matches", + "parity-crypto 0.3.1", + "parity-wordlist", + "parking_lot 0.7.1", + "rand 0.4.6", + "rustc-hex 1.0.0", + "serde", + "serde_derive", + "serde_json", + "smallvec 0.6.13", + "tempdir", + "time", + "tiny-keccak 1.5.0", ] [[package]] name = "ethstore-cli" version = "0.1.1" dependencies = [ - "dir 0.1.2", - "docopt 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethstore 0.2.1", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "panic_hook 0.1.0", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dir", + "docopt", + "env_logger", + "ethstore", + "num_cpus", + "panic_hook", + "parking_lot 0.7.1", + "rustc-hex 1.0.0", + "serde", + "serde_derive", + "tempdir", ] [[package]] name = "evm" version = "0.1.0" dependencies = [ - "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "criterion 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memory-cache 0.1.0", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "vm 0.1.0", + "bit-set", + "criterion 0.2.11", + "ethereum-types 0.4.2", + "heapsize", + "hex-literal", + "keccak-hash", + "lazy_static", + "log", + "memory-cache", + "num-bigint 0.2.3", + "parity-bytes", + "parking_lot 0.7.1", + "rustc-hex 1.0.0", + "vm", ] [[package]] name = "evmbin" version = "0.1.0" dependencies = [ - "common-types 0.1.0", - "docopt 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethjson 0.1.0", - "evm 0.1.0", - "panic_hook 0.1.0", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "vm 0.1.0", -] - -[[package]] -name = "failsafe" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "common-types", + "criterion 0.3.0", + "docopt", + "env_logger", + "ethcore", + "ethereum-types 0.4.2", + "ethjson", + "evm", + "panic_hook", + "parity-bytes", + "pretty_assertions", + "rustc-hex 1.0.0", + "serde", + "serde_derive", + "serde_json", + "tempdir", + "vm", ] [[package]] name = "failure" -version = "0.1.3" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" dependencies = [ - "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace", + "failure_derive", ] [[package]] name = "failure_derive" -version = "0.1.3" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ - "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.20", + "quote 1.0.7", + "syn 1.0.40", + "synstructure 0.12.2", ] [[package]] name = "fake-fetch" version = "0.0.1" dependencies = [ - "fetch 0.1.0", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fake-hardware-wallet" -version = "0.0.1" -dependencies = [ - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", + "fetch", + "futures", + "hyper 0.12.35", ] [[package]] name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "fastmap" version = "0.1.0" dependencies = [ - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.4.2", + "plain_hasher", ] [[package]] name = "fdlimit" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "fetch" version = "0.1.0" dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper-rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "futures", + "http", + "hyper 0.12.35", + "hyper-rustls", + "log", + "tokio", + "url 2.1.0", ] [[package]] name = "fixed-hash" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5ec8112f00ea8a483e04748a85522184418fd1cf02890b626d8fc28683f7de" dependencies = [ - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "heapsize", + "libc", + "rand 0.4.6", + "rustc-hex 1.0.0", ] [[package]] -name = "fixedbitset" +name = "fixed-hash" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11498d382790b7a8f2fd211780bec78619bba81cdad3a283997c0c41f836759c" +dependencies = [ + "byteorder", + "rand 0.7.2", + "rustc-hex 2.0.1", + "static_assertions", +] + +[[package]] +name = "fixed_width_field" +version = "0.1.0" +source = "git+https://github.com/matter-labs/eip1962.git?rev=ece6cbabc41948db4200e41f0bfdab7ab94c7af8#ece6cbabc41948db4200e41f0bfdab7ab94c7af8" +dependencies = [ + "simple_uint", +] + +[[package]] +name = "fixed_width_group_and_loop" +version = "0.1.0" +source = "git+https://github.com/matter-labs/eip1962.git?rev=ece6cbabc41948db4200e41f0bfdab7ab94c7af8#ece6cbabc41948db4200e41f0bfdab7ab94c7af8" +dependencies = [ + "simple_uint", +] + +[[package]] +name = "fixedbitset" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" [[package]] name = "fnv" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" [[package]] name = "fs-swap" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921d332c89b3b61a826de38c61ee5b6e02c56806cade1b0e5d81bd71f57a71bb" dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "libc", + "libloading", + "winapi 0.3.8", ] [[package]] name = "fs_extra" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674" + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "fuchsia-zircon-sys", ] [[package]] name = "fuchsia-zircon-sys" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" -version = "0.1.25" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" [[package]] name = "futures-cpupool" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures", + "num_cpus", ] [[package]] name = "fxhash" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", ] [[package]] name = "gcc" version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" [[package]] name = "generic-array" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" dependencies = [ - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", ] [[package]] name = "getopts" -version = "0.2.18" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "getrandom" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" dependencies = [ - "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "libc", + "wasi", ] [[package]] name = "globset" -version = "0.4.2" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ad1da430bd7281dde2576f44c84cc3f0f7b475e7202cd503042dff01a8c8120" dependencies = [ - "aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.7.6", + "bstr", + "fnv", + "log", + "regex 1.3.9", ] [[package]] name = "h2" -version = "0.1.12" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "string 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "bytes", + "fnv", + "futures", + "http", + "indexmap", + "log", + "slab 0.4.2", + "string", + "tokio-io", ] [[package]] name = "hamming" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65043da274378d68241eb9a8f8f8aa54e349136f7b8e12f63e3ef44043cc30e1" + +[[package]] +name = "hash-db" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b03501f6e1a2a97f1618879aba3156f14ca2847faa530c4e28859638bd11483" [[package]] -name = "handlebars" -version = "0.32.4" +name = "hash256-std-hasher" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5c13dbac3cc50684760f54af18545c9e80fb75e93a3e586d71ebdc13138f6a4" dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "pest 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "pest_derive 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy 0.2.2", ] [[package]] -name = "hardware-wallet" -version = "1.12.0" +name = "heapsize" +version = "0.4.2" +source = "git+https://github.com/cheme/heapsize.git?branch=ec-macfix#c07ffe843acb9da570682e290a48540741afdce1" dependencies = [ - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "hidapi 0.3.1 (git+https://github.com/paritytech/hidapi-rs)", - "libusb 0.3.0 (git+https://github.com/paritytech/libusb-rs)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 1.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "trezor-sys 1.0.0 (git+https://github.com/paritytech/trezor-sys)", + "jemallocator", + "winapi 0.3.8", ] [[package]] -name = "hash-db" -version = "0.11.0" +name = "heck" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +dependencies = [ + "unicode-segmentation", +] [[package]] -name = "heapsize" -version = "0.4.2" -source = "git+https://github.com/cheme/heapsize.git?branch=ec-macfix#421df390a930cb523a09e5528e6fe57b534b3b26" +name = "hermit-abi" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "307c3c9f937f38e3534b1d6447ecf090cafcc9744e4a6360e8b037b2cf5af120" dependencies = [ - "jemallocator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] -name = "heck" -version = "0.3.0" +name = "hex-literal" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "961de220ec9a91af2e1e5bd80d02109155695e516771762381ef8581317066e0" dependencies = [ - "unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hex-literal-impl", + "proc-macro-hack", ] [[package]] -name = "hex" -version = "0.2.0" +name = "hex-literal-impl" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d4c5c844e2fee0bf673d54c2c177f1713b3d2af2ff6e666b49cb7572e6cf42d" +dependencies = [ + "proc-macro-hack", +] [[package]] -name = "hidapi" -version = "0.3.1" -source = "git+https://github.com/paritytech/hidapi-rs#d4d323767d6f27cf5a3d73fbae0b0f2134d579bf" +name = "hmac" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "733e1b3ac906631ca01ebb577e9bb0f5e37a454032b9036b5eaea4013ed6f99a" +dependencies = [ + "crypto-mac 0.6.2", + "digest 0.7.6", +] + +[[package]] +name = "hmac" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" dependencies = [ - "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac 0.7.0", + "digest 0.8.1", ] [[package]] name = "home" -version = "0.3.3" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29302b90cfa76231a757a887d1e3153331a63c7f80b6c75f86366334cbe70708" +dependencies = [ + "scopeguard 0.3.3", + "winapi 0.3.8", +] + +[[package]] +name = "home" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3753954f7bd71f0e671afb8b5a992d1724cf43b7f95a563cd4a0bde94659ca8" dependencies = [ - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.0.0", + "winapi 0.3.8", ] [[package]] name = "http" -version = "0.1.15" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" +dependencies = [ + "bytes", + "futures", + "http", + "tokio-buf", ] [[package]] name = "httparse" -version = "1.3.3" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" [[package]] name = "humantime" -version = "1.1.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" dependencies = [ - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-error", ] [[package]] name = "hyper" version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "want 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +checksum = "34a590ca09d341e94cddf8e5af0bbccde205d5fbc2fa3c09dd67c7f85cea59d7" +dependencies = [ + "base64 0.9.3", + "bytes", + "futures", + "futures-cpupool", + "httparse", + "iovec", + "language-tags", + "log", + "mime", + "net2", + "percent-encoding 1.0.1", + "relay", + "time", + "tokio-core", + "tokio-io", + "tokio-service", + "unicase", + "want 0.0.4", ] [[package]] name = "hyper" -version = "0.12.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "h2 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", +version = "0.12.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6" +dependencies = [ + "bytes", + "futures", + "futures-cpupool", + "h2", + "http", + "http-body", + "httparse", + "iovec", + "itoa", + "log", + "net2", + "rustc_version", + "time", + "tokio", + "tokio-buf", + "tokio-executor", + "tokio-io", + "tokio-reactor", + "tokio-tcp", + "tokio-threadpool", + "tokio-timer 0.2.13", + "want 0.2.0", ] [[package]] name = "hyper-rustls" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b66d1bd4864ef036adf2363409caa3acd63ebb4725957b66e621c8a36631a3" dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "ct-logs 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)", - "rustls 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-rustls 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki-roots 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "ct-logs", + "futures", + "hyper 0.12.35", + "rustls", + "tokio-io", + "tokio-rustls", + "webpki", + "webpki-roots", ] [[package]] name = "idna" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", ] [[package]] name = "if_chain" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bac95d9aa0624e7b78187d6fb8ab012b41d9f6f54b1bcb61e61c4845f8357ec" [[package]] name = "igd" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8aef7814a769f156ef3a86169a8b04c066e3aebc324f522c159978466e32a1c" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-retry 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "xmltree 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures", + "hyper 0.11.27", + "rand 0.4.6", + "regex 0.2.11", + "tokio-core", + "tokio-retry", + "tokio-timer 0.1.2", + "xml-rs", + "xmltree", ] [[package]] -name = "indexmap" -version = "1.0.2" +name = "impl-codec" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53" +dependencies = [ + "parity-scale-codec", +] [[package]] -name = "integer-encoding" -version = "1.0.5" +name = "impl-rlp" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f7a72f11830b52333f36e3b09a288333888bf54380fd0ac0790a3c31ab0f3c5" +dependencies = [ + "rlp 0.4.5", +] + +[[package]] +name = "impl-serde" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b47ca4d2b6931707a55fce5cf66aff80e2178c8b63bbb4ecb5695cbc870ddf6f" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef5550a42e3740a0e71f909d4c861056a284060af885ae7aa6242820f920d9d" +dependencies = [ + "proc-macro2 1.0.20", + "quote 1.0.7", + "syn 1.0.40", +] + +[[package]] +name = "indexmap" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712d7b3ea5827fcb9d4fda14bf4da5f136f0db2ae9c8f4bd4e2d1c6fde4e6db2" +dependencies = [ + "autocfg 0.1.7", +] [[package]] name = "interleaved-ordered" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "141340095b15ed7491bd3d4ced9d20cebfb826174b6bb03386381f62b01e3d77" [[package]] name = "iovec" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "ipnetwork" version = "0.12.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70783119ac90828aaba91eae39db32c6c1b8838deea3637e5238efa0130801ab" [[package]] name = "itertools" version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4833d6978da405305126af4ac88569b5d71ff758581ce5a987dbfa3755f694fc" dependencies = [ - "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "either", ] [[package]] name = "itertools" -version = "0.7.8" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d" dependencies = [ - "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "either", ] [[package]] -name = "itertools-num" -version = "0.1.2" +name = "itertools" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa75c9dea7b07be3138c49abbb83fd4bea199b5cdc76f9804458edc5da0d6e" dependencies = [ - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "either", ] [[package]] name = "itoa" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" [[package]] name = "jemalloc-sys" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfc62c8e50e381768ce8ee0428ee53741929f7ebd73e4d83f669bcf7693e00ae" dependencies = [ - "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", - "fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "fs_extra", + "libc", ] [[package]] name = "jemallocator" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f0cd42ac65f758063fea55126b0148b1ce0a6354ff78e07a4d6806bc65c4ab3" dependencies = [ - "jemalloc-sys 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "jemalloc-sys", + "libc", ] -[[package]] -name = "jni" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cesu8 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "combine 3.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "journaldb" version = "0.2.0" dependencies = [ - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fastmap 0.1.0", - "hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger", + "ethereum-types 0.4.2", + "fastmap", + "hash-db", + "heapsize", + "keccak-hash", "keccak-hasher 0.1.1", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-memorydb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memory-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "kvdb", + "kvdb-memorydb", + "log", + "memory-db 0.11.0", + "parity-bytes", + "parking_lot 0.7.1", + "rlp 0.3.0", ] [[package]] name = "jsonrpc-core" -version = "10.0.1" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b12567a31d48588a65b6cf870081e6ba1d7b2ae353977cb9820d512e69c70" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "futures", + "log", + "serde", + "serde_derive", + "serde_json", ] [[package]] name = "jsonrpc-derive" -version = "10.0.2" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2cc6ea7f785232d9ca8786a44e9fa698f92149dcdc1acc4aa1fc69c4993d79e" dependencies = [ - "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-crate", + "proc-macro2 1.0.20", + "quote 1.0.7", + "syn 1.0.40", ] [[package]] name = "jsonrpc-http-server" -version = "10.0.1" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9996b26c0c7a59626d0ed6c5ec8bf06218e62ce1474bd2849f9b9fd38a0158c0" dependencies = [ - "hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-server-utils 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.35", + "jsonrpc-core", + "jsonrpc-server-utils", + "log", + "net2", + "parking_lot 0.10.2", + "unicase", ] [[package]] name = "jsonrpc-ipc-server" -version = "10.0.1" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e8f2278fb2b277175b6e21b23e7ecf30e78daff5ee301d0a2a411d9a821a0a" dependencies = [ - "jsonrpc-core 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-server-utils 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-tokio-ipc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core", + "jsonrpc-server-utils", + "log", + "parity-tokio-ipc", + "parking_lot 0.10.2", + "tokio-service", ] [[package]] name = "jsonrpc-pubsub" -version = "10.0.1" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f389c5cd1f3db258a99296892c21047e21ae73ff4c0e2d39650ea86fe994b4c7" dependencies = [ - "jsonrpc-core 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core", + "log", + "parking_lot 0.10.2", + "rand 0.7.2", + "serde", ] [[package]] name = "jsonrpc-server-utils" -version = "10.0.1" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c623e1895d0d9110cb0ea7736cfff13191ff52335ad33b21bd5c775ea98b27af" dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "globset", + "jsonrpc-core", + "lazy_static", + "log", + "tokio", + "tokio-codec", + "unicase", ] [[package]] name = "jsonrpc-tcp-server" -version = "10.0.1" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b7d4a377ea231ca0d14fe1fb515ca89d6a46a33169efa6bdd5d0e56b9f359fc" dependencies = [ - "jsonrpc-core 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-server-utils 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core", + "jsonrpc-server-utils", + "log", + "parking_lot 0.10.2", + "tokio-service", ] [[package]] name = "jsonrpc-ws-server" -version = "10.0.1" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436a92034d0137ab3e3c64a7a6350b428f31cb4d7d1a89f284bcdbcd98a7bc56" dependencies = [ - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-server-utils 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-ws 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core", + "jsonrpc-server-utils", + "log", + "parity-ws", + "parking_lot 0.10.2", + "slab 0.4.2", ] [[package]] name = "keccak-hash" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "253bbe643c32c816bf58fa5a88248fafedeebb139705ad17a62add3517854a86" dependencies = [ - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.4.2", + "tiny-keccak 1.5.0", ] [[package]] name = "keccak-hasher" version = "0.1.1" dependencies = [ - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.4.2", + "hash-db", + "plain_hasher", + "tiny-keccak 1.5.0", +] + +[[package]] +name = "keccak-hasher" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb9d3670023f4c04153d90b8a557a822d1b27ed702bb015a87cf7bffead5b611" +dependencies = [ + "hash-db", + "hash256-std-hasher", + "tiny-keccak 1.5.0", ] [[package]] name = "kernel32-sys" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8", + "winapi-build", ] [[package]] name = "kvdb" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b2f251f01a7224426abdb2563707d856f7de995d821744fd8fa8e2874f69e3" dependencies = [ - "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "elastic-array", + "parity-bytes", ] [[package]] name = "kvdb-memorydb" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bcdf5eb083602cff61a6f8438dce2a7900d714e893fc48781c39fb119d37aa" dependencies = [ - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kvdb", + "parking_lot 0.6.4", ] [[package]] name = "kvdb-rocksdb" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5c5f57ad492ecfb9e2a91614ff0204bda82e41f832bebd64cd03ffecb74e02b" dependencies = [ - "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fs-swap 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "interleaved-ordered 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-rocksdb 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "elastic-array", + "fs-swap", + "interleaved-ordered", + "kvdb", + "log", + "num_cpus", + "parity-rocksdb", + "parking_lot 0.9.0", + "regex 1.3.9", ] [[package]] name = "language-tags" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" [[package]] name = "lazy_static" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lazycell" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" [[package]] name = "len-caching-lock" version = "0.1.1" dependencies = [ - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.7.1", ] [[package]] name = "libc" -version = "0.2.48" +version = "0.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" [[package]] name = "libloading" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" dependencies = [ - "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "winapi 0.3.8", ] [[package]] -name = "libusb" -version = "0.3.0" -source = "git+https://github.com/paritytech/libusb-rs#442708954a720bc89a9cf41e7be021a778bdbc27" -dependencies = [ - "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "libusb-sys 0.2.4 (git+https://github.com/paritytech/libusb-sys)", -] +name = "linked-hash-map" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" [[package]] -name = "libusb-sys" -version = "0.2.4" -source = "git+https://github.com/paritytech/libusb-sys#1d33d9840a82adaf4d6a1a0f5141f022e5676802" +name = "local-encoding" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1ceb20f39ff7ae42f3ff9795f3986b1daad821caaa1e1732a0944103a5a1a66" dependencies = [ - "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys", + "skeptic", + "winapi 0.2.8", ] [[package]] -name = "linked-hash-map" -version = "0.5.1" +name = "lock_api" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" +dependencies = [ + "owning_ref", + "scopeguard 0.3.3", +] [[package]] -name = "local-encoding" -version = "0.2.0" +name = "lock_api" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "skeptic 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.0.0", ] [[package]] -name = "lock_api" -version = "0.1.4" +name = "log" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" dependencies = [ - "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", ] [[package]] -name = "log" -version = "0.3.9" +name = "logos" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60ca690691528b32832c7e8aaae8ae1edcdee4e9ffde55b2d31a4795bc7a12d0" dependencies = [ - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "logos-derive", + "toolshed", ] [[package]] -name = "log" -version = "0.4.6" +name = "logos-derive" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917dccdd529d5681f3d28b26bcfdafd2ed67fe4f26d15b5ac679f67b55279f3d" dependencies = [ - "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30", + "quote 0.6.13", + "regex-syntax 0.6.18", + "syn 0.15.26", + "utf8-ranges", ] [[package]] name = "lru-cache" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" dependencies = [ - "linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "linked-hash-map", ] [[package]] name = "lunarity-lexer" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a5446c03ed5bd4ae2cca322c4c84d9bd9741b6788f75c404719474cb63d3b7" dependencies = [ - "toolshed 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "logos", ] [[package]] name = "macros" version = "0.1.0" +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + [[package]] name = "matches" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + +[[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.1.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" dependencies = [ - "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "memmap" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "winapi 0.3.8", ] [[package]] name = "memoffset" -version = "0.2.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a85c1a8c329f11437034d7313dca647c79096523533a1c79e86f1d0f657c7cc" +dependencies = [ + "rustc_version", +] [[package]] name = "memory-cache" version = "0.1.0" dependencies = [ - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "heapsize", + "lru-cache", +] + +[[package]] +name = "memory-db" +version = "0.11.0" +dependencies = [ + "criterion 0.2.11", + "hash-db", + "heapsize", + "keccak-hasher 0.11.0", ] [[package]] name = "memory-db" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94da53143d45f6bad3753f532e56ad57a6a26c0ca6881794583310c7cb4c885f" dependencies = [ - "hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", + "hash-db", + "heapsize", ] [[package]] name = "memory_units" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" [[package]] name = "memzero" @@ -2171,1613 +2614,2033 @@ version = "0.1.0" name = "migration-rocksdb" version = "0.1.0" dependencies = [ - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-rocksdb 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "macros 0.1.0", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "kvdb", + "kvdb-rocksdb", + "log", + "macros", + "tempdir", ] [[package]] name = "mime" -version = "0.3.12" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "mime_guess" -version = "2.0.0-alpha.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "mime 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", - "phf 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", - "phf_codegen 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "dd1d63acd1b78403cc0c325605908475dd9b9a3acbf65ed8bcab97e27014afcf" [[package]] name = "mio" -version = "0.6.16" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" dependencies = [ - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazycell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow 0.2.1", + "net2", + "slab 0.4.2", + "winapi 0.2.8", ] [[package]] name = "mio-extras" version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40" dependencies = [ - "lazycell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazycell", + "log", + "mio", + "slab 0.4.2", ] [[package]] name = "mio-named-pipes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" dependencies = [ - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log", + "mio", + "miow 0.3.3", + "winapi 0.3.8", ] [[package]] name = "mio-uds" version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" dependencies = [ - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec", + "libc", + "mio", ] [[package]] name = "miow" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", ] [[package]] name = "miow" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226" dependencies = [ - "socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "multibase" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "base-x 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "multihash" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "sha1 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2", + "winapi 0.3.8", ] [[package]] name = "nan-preserving-float" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34d4f00fcc2f4c9efa8cc971db0da9e28290e28e97af47585e48691ef10ff31f" [[package]] name = "net2" version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" dependencies = [ - "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "libc", + "winapi 0.3.8", ] [[package]] name = "node-filter" version = "1.12.0" dependencies = [ - "ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 6.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.0", - "ethcore-io 1.12.0", - "ethcore-network 1.12.0", - "ethcore-network-devp2p 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-memorydb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi", + "ethabi-contract", + "ethabi-derive", + "ethcore", + "ethcore-io", + "ethcore-network", + "ethcore-network-devp2p", + "ethereum-types 0.4.2", + "kvdb-memorydb", + "log", + "lru-cache", + "parking_lot 0.7.1", + "tempdir", ] [[package]] name = "nodrop" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] name = "num" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" dependencies = [ - "num-bigint 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint 0.1.44", + "num-integer", + "num-iter", + "num-traits 0.2.8", ] [[package]] name = "num-bigint" version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1" +dependencies = [ + "num-integer", + "num-traits 0.2.8", + "rand 0.4.6", + "rustc-serialize", +] + +[[package]] +name = "num-bigint" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9c3f34cdd24f334cb265d9bf8bfa8a241920d026916785747a92f0e55541a1a" dependencies = [ - "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7", + "num-integer", + "num-traits 0.2.8", ] [[package]] name = "num-integer" -version = "0.1.39" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" dependencies = [ - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7", + "num-traits 0.2.8", ] [[package]] name = "num-iter" -version = "0.1.37" +version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bd5272412d173d6bf9afdf98db8612bbabc9a7a830b7bfc9c188911716132e" dependencies = [ - "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7", + "num-integer", + "num-traits 0.2.8", ] [[package]] name = "num-traits" version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" dependencies = [ - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8", ] [[package]] name = "num-traits" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" +dependencies = [ + "autocfg 0.1.7", +] [[package]] name = "num_cpus" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "155394f924cdddf08149da25bfb932d226b4a593ca7468b08191ff6335941af5" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi", + "libc", ] [[package]] name = "number_prefix" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf9993e59c894e3c08aa1c2712914e9e6bf1fcbfc6bef283e2183df345a4fee" dependencies = [ - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8", ] [[package]] name = "ole32-sys" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8", + "winapi-build", ] [[package]] -name = "order-stat" -version = "0.1.3" +name = "once_cell" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" [[package]] -name = "ordered-float" -version = "0.5.2" +name = "opaque-debug" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "openethereum" +version = "3.1.0-rc1" +dependencies = [ + "ansi_term 0.10.2", + "atty", + "blooms-db", + "clap", + "cli-signer", + "common-types", + "ctrlc", + "dir", + "docopt", + "ethcore", + "ethcore-accounts", + "ethcore-blockchain", + "ethcore-call-contract", + "ethcore-db", + "ethcore-io", + "ethcore-logger", + "ethcore-miner", + "ethcore-network", + "ethcore-secretstore", + "ethcore-service", + "ethcore-sync", + "ethereum-types 0.4.2", + "ethkey", + "ethstore", + "fake-fetch", + "fdlimit", + "fetch", + "futures", + "hyper 0.12.35", + "ipnetwork", + "journaldb", + "jsonrpc-core", + "keccak-hash", + "kvdb", + "kvdb-rocksdb", + "lazy_static", + "log", + "migration-rocksdb", + "node-filter", + "num_cpus", + "number_prefix", + "panic_hook", + "parity-bytes", + "parity-daemonize", + "parity-local-store", + "parity-path", + "parity-rpc", + "parity-runtime", + "parity-version", + "parking_lot 0.7.1", + "pretty_assertions", + "prometheus", + "regex 1.3.9", + "registrar", + "rlp 0.3.0", + "rpassword", + "rustc-hex 1.0.0", + "rustc_version", + "semver", + "serde", + "serde_derive", + "serde_json", + "stats", + "tempdir", + "term_size", + "textwrap 0.9.0", + "toml 0.4.10", + "winapi 0.3.8", ] +[[package]] +name = "order-stat" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efa535d5117d3661134dbf1719b6f0ffe06f2375843b13935db186cd094105eb" + [[package]] name = "ordermap" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" [[package]] name = "owning_ref" -version = "0.3.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" dependencies = [ - "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "stable_deref_trait", ] [[package]] name = "panic_hook" version = "0.1.0" dependencies = [ - "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace", ] [[package]] name = "parity-bytes" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c276d76c5333b8c2579e02d49a06733a55b8282d2d9b13e8d53b6406bd7e30a" [[package]] -name = "parity-clib" -version = "1.12.0" +name = "parity-crypto" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1b9c063d87e1507cb3807493c8d21859ef23b5414b39f81c53f0ba267d64c1" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "jni 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "panic_hook 0.1.0", - "parity-ethereum 2.5.0", - "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-current-thread 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "aes", + "aes-ctr", + "block-modes", + "digest 0.8.1", + "quick-error", + "ring", + "ripemd160", + "scrypt 0.1.2", + "sha2 0.8.0", + "tiny-keccak 1.5.0", ] [[package]] name = "parity-crypto" -version = "0.3.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27a9c2b525c93d717a234eb220c26474f8d97b08ac50d79faeac4cb6c74bf0b9" dependencies = [ - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "aes", + "aes-ctr", + "block-modes", + "digest 0.8.1", + "hmac 0.7.1", + "pbkdf2 0.3.0", + "rand 0.7.2", + "ripemd160", + "rustc-hex 2.0.1", + "scrypt 0.2.0", + "sha2 0.8.0", + "subtle 2.1.0", + "tiny-keccak 1.5.0", + "zeroize", ] [[package]] name = "parity-daemonize" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b1910b2793ff52713fca0a4ee92544ebec59ccd218ea74560be6f947b4ca77" dependencies = [ - "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-ethereum" -version = "2.5.0" -dependencies = [ - "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "blooms-db 0.1.0", - "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cli-signer 1.4.0", - "common-types 0.1.0", - "ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)", - "dir 0.1.2", - "docopt 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.0", - "ethcore-accounts 0.1.0", - "ethcore-blockchain 0.1.0", - "ethcore-call-contract 0.1.0", - "ethcore-db 0.1.0", - "ethcore-io 1.12.0", - "ethcore-light 1.12.0", - "ethcore-logger 1.12.0", - "ethcore-miner 1.12.0", - "ethcore-network 1.12.0", - "ethcore-private-tx 1.0.0", - "ethcore-secretstore 1.0.0", - "ethcore-service 0.1.0", - "ethcore-sync 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "ethstore 0.2.1", - "fake-fetch 0.0.1", - "fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "ipnetwork 0.12.8 (registry+https://github.com/rust-lang/crates.io-index)", - "journaldb 0.2.0", - "jsonrpc-core 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-rocksdb 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "migration-rocksdb 0.1.0", - "node-filter 1.12.0", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "number_prefix 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "panic_hook 0.1.0", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-daemonize 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-hash-fetch 1.12.0", - "parity-ipfs-api 1.12.0", - "parity-local-store 0.1.0", - "parity-path 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-rpc 1.12.0", - "parity-runtime 0.1.0", - "parity-updater 1.12.0", - "parity-version 2.5.0", - "parity-whisper 0.1.0", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "registrar 0.0.1", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rpassword 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-hash-fetch" -version = "1.12.0" -dependencies = [ - "ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 6.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-fetch 0.0.1", - "fetch 0.1.0", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", - "mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-runtime 0.1.0", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "registrar 0.0.1", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-ipfs-api" -version = "1.12.0" -dependencies = [ - "cid 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-http-server 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "multihash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term 0.11.0", + "failure", + "libc", + "log", + "mio", ] [[package]] name = "parity-local-store" version = "0.1.0" dependencies = [ - "common-types 0.1.0", - "ethcore-io 1.12.0", - "ethkey 0.3.0", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-memorydb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "common-types", + "ethcore-io", + "ethkey", + "kvdb", + "kvdb-memorydb", + "log", + "rlp 0.3.0", + "serde", + "serde_derive", + "serde_json", ] [[package]] name = "parity-path" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b027aab22527061b7005cecf7805e8f42eed94ce89e76bac3a6035394b56627" +dependencies = [ + "home 0.5.1", +] [[package]] name = "parity-rocksdb" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d17caf6640e24b70242f3f48615e3f0764f98871e8c7aea25584e29833eb5a8" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "local-encoding 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-rocksdb-sys 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "local-encoding", + "parity-rocksdb-sys", ] [[package]] name = "parity-rocksdb-sys" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9581e6b8c63f3808500638372ee56faaaffb57c4d349974bff591606b94d5f57" dependencies = [ - "cmake 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "local-encoding 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-snappy-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cmake", + "libc", + "local-encoding", + "parity-snappy-sys", ] [[package]] name = "parity-rpc" version = "1.12.0" dependencies = [ - "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cid 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "common-types 0.1.0", - "eip-712 0.1.0", - "ethash 1.12.0", - "ethcore 1.12.0", - "ethcore-accounts 0.1.0", - "ethcore-io 1.12.0", - "ethcore-light 1.12.0", - "ethcore-logger 1.12.0", - "ethcore-miner 1.12.0", - "ethcore-network 1.12.0", - "ethcore-private-tx 1.0.0", - "ethcore-sync 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethjson 0.1.0", - "ethkey 0.3.0", - "ethstore 0.2.1", - "fake-fetch 0.0.1", - "fastmap 0.1.0", - "fetch 0.1.0", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-derive 10.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-http-server 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-ipc-server 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-pubsub 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-ws-server 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "macros 0.1.0", - "multihash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-crypto 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-runtime 0.1.0", - "parity-updater 1.12.0", - "parity-version 2.5.0", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "stats 0.1.0", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "transaction-pool 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "transient-hashmap 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "vm 0.1.0", + "ansi_term 0.10.2", + "common-types", + "eip-712", + "ethash", + "ethcore", + "ethcore-accounts", + "ethcore-io", + "ethcore-logger", + "ethcore-miner", + "ethcore-network", + "ethcore-sync", + "ethereum-types 0.4.2", + "ethjson", + "ethkey", + "ethstore", + "fake-fetch", + "fetch", + "futures", + "itertools 0.5.10", + "jsonrpc-core", + "jsonrpc-derive", + "jsonrpc-http-server", + "jsonrpc-ipc-server", + "jsonrpc-pubsub", + "jsonrpc-ws-server", + "keccak-hash", + "log", + "macros", + "order-stat", + "parity-bytes", + "parity-crypto 0.3.1", + "parity-runtime", + "parity-version", + "parking_lot 0.7.1", + "pretty_assertions", + "rand 0.4.6", + "rlp 0.3.0", + "rustc-hex 1.0.0", + "serde", + "serde_derive", + "serde_json", + "stats", + "tempdir", + "tiny-keccak 1.5.0", + "tokio-timer 0.1.2", + "transaction-pool", + "transient-hashmap", + "vm", ] [[package]] name = "parity-rpc-client" version = "1.4.0" dependencies = [ - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-ws-server 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-rpc 1.12.0", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.4.2", + "futures", + "jsonrpc-core", + "jsonrpc-ws-server", + "keccak-hash", + "log", + "matches", + "parity-rpc", + "parking_lot 0.9.0", + "serde", + "serde_json", + "url 2.1.0", ] [[package]] name = "parity-runtime" version = "0.1.0" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures", + "tokio", +] + +[[package]] +name = "parity-scale-codec" +version = "1.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c740e5fbcb6847058b40ac7e5574766c6388f585e184d769910fe0d3a2ca861" +dependencies = [ + "arrayvec 0.5.1", + "bitvec", + "byte-slice-cast", + "serde", ] [[package]] name = "parity-snappy" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2c5f9d149b13134b8b354d93a92830efcbee6fe5b73a2e6e540fe70d4dd8a63" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-snappy-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "parity-snappy-sys", ] [[package]] name = "parity-snappy-sys" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a413d51e5e1927320c9de992998e4a279dffb8c8a7363570198bd8383e66f1b" dependencies = [ - "cmake 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "cmake", + "libc", ] [[package]] name = "parity-tokio-ipc" -version = "0.1.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e57fea504fea33f9fbb5f49f378359030e7e026a6ab849bb9e8f0787376f1bf" dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-named-pipes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-uds 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "futures", + "libc", + "log", + "mio-named-pipes", + "miow 0.3.3", + "rand 0.7.2", + "tokio", + "tokio-named-pipes", + "tokio-uds", + "winapi 0.3.8", ] [[package]] -name = "parity-updater" -version = "1.12.0" +name = "parity-util-mem" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297ff91fa36aec49ce183484b102f6b75b46776822bd81525bfc4cc9b0dd0f5c" dependencies = [ - "common-types 0.1.0", - "ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 6.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.0", - "ethcore-sync 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-hash-fetch 1.12.0", - "parity-path 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-version 2.5.0", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "ethereum-types 0.9.2", + "impl-trait-for-tuples", + "parity-util-mem-derive", + "primitive-types", + "winapi 0.3.8", ] [[package]] -name = "parity-version" -version = "2.5.0" +name = "parity-util-mem-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "vergen 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.20", + "syn 1.0.40", + "synstructure 0.12.2", ] [[package]] -name = "parity-wasm" -version = "0.31.3" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "parity-version" +version = "3.1.0-rc1" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-bytes", + "rlp 0.3.0", + "rustc_version", + "target_info", + "toml 0.4.10", + "vergen", ] [[package]] -name = "parity-whisper" -version = "0.1.0" +name = "parity-wasm" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511379a8194230c2395d2f5fa627a5a7e108a9f976656ce723ae68fca4097bfc" dependencies = [ - "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-network 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-derive 10.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-pubsub 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memzero 0.1.0", - "ordered-float 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-crypto 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "time-utils 0.1.0", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", ] [[package]] name = "parity-wordlist" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573d08f0d3bc8a6ffcdac1de2725b5daeed8db26345a9c12d91648e2d6457f3e" dependencies = [ - "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "rand 0.6.5", ] [[package]] name = "parity-ws" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e02a625dd75084c2a7024f07c575b61b782f729d18702dabb3cdbf31911dc61" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "bytes", + "httparse", + "log", + "mio", + "mio-extras", + "rand 0.7.2", + "sha-1", + "slab 0.4.2", + "url 2.1.0", ] [[package]] name = "parking_lot" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" dependencies = [ - "lock_api 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api 0.1.5", + "parking_lot_core 0.3.1", ] [[package]] name = "parking_lot" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" dependencies = [ - "lock_api 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api 0.1.5", + "parking_lot_core 0.4.0", ] [[package]] -name = "parking_lot_core" -version = "0.3.1" +name = "parking_lot" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api 0.3.4", + "parking_lot_core 0.6.2", + "rustc_version", ] [[package]] -name = "parking_lot_core" -version = "0.4.0" +name = "parking_lot" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" dependencies = [ - "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "thread-id 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api 0.3.4", + "parking_lot_core 0.7.2", ] [[package]] -name = "patricia-trie-ethereum" -version = "0.1.0" +name = "parking_lot_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" dependencies = [ - "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "journaldb 0.2.0", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hasher 0.1.1", - "memory-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "rand 0.5.6", + "rustc_version", + "smallvec 0.6.13", + "winapi 0.3.8", ] [[package]] -name = "percent-encoding" -version = "1.0.1" +name = "parking_lot_core" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" +dependencies = [ + "backtrace", + "libc", + "petgraph", + "rand 0.6.5", + "rustc_version", + "smallvec 0.6.13", + "thread-id", + "winapi 0.3.8", +] [[package]] -name = "pest" -version = "1.0.6" +name = "parking_lot_core" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" +dependencies = [ + "cfg-if", + "cloudabi", + "libc", + "redox_syscall", + "rustc_version", + "smallvec 0.6.13", + "winapi 0.3.8", +] [[package]] -name = "pest_derive" -version = "1.0.8" +name = "parking_lot_core" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" dependencies = [ - "pest 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "cloudabi", + "libc", + "redox_syscall", + "smallvec 1.4.2", + "winapi 0.3.8", ] [[package]] -name = "petgraph" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "patricia-trie-ethereum" +version = "0.1.0" dependencies = [ - "fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "elastic-array", + "ethereum-types 0.4.2", + "hash-db", + "journaldb", + "keccak-hash", + "keccak-hasher 0.1.1", + "memory-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-bytes", + "rlp 0.3.0", + "trie-db", ] [[package]] -name = "phf" -version = "0.7.23" +name = "pbkdf2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09cddfbfc98de7f76931acf44460972edb4023eb14d0c6d4018800e552d8e0" dependencies = [ - "phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "crypto-mac 0.6.2", + "generic-array 0.9.0", ] [[package]] -name = "phf_codegen" -version = "0.7.23" +name = "pbkdf2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" dependencies = [ - "phf_generator 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", - "phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.9.3", + "byteorder", + "crypto-mac 0.7.0", + "hmac 0.7.1", + "rand 0.5.6", + "sha2 0.8.0", + "subtle 1.0.0", ] [[package]] -name = "phf_generator" -version = "0.7.23" +name = "percent-encoding" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" [[package]] -name = "phf_shared" -version = "0.7.23" +name = "percent-encoding" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "petgraph" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" dependencies = [ - "siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "fixedbitset", + "ordermap", ] [[package]] name = "plain_hasher" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1c24f5061a6a53aaa21b0aaaa2e1beb5271a9ecc8c5bd7ae9ac92969070a2a" dependencies = [ - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy 0.2.2", ] +[[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" + [[package]] name = "pretty_assertions" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2412f3332a07c7a2a50168988dcc184f32180a9758ad470390e5f55e089f6b6e" dependencies = [ - "difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "difference", ] [[package]] name = "price-info" version = "1.12.0" dependencies = [ - "fake-fetch 0.0.1", - "fetch 0.1.0", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-runtime 0.1.0", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-fetch", + "fetch", + "futures", + "log", + "parity-runtime", + "parking_lot 0.7.1", + "serde_json", ] [[package]] name = "primal" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e31b86efadeaeb1235452171a66689682783149a6249ff334a2c5d8218d00a4" dependencies = [ - "primal-check 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "primal-estimate 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "primal-sieve 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "primal-check", + "primal-estimate", + "primal-sieve", ] [[package]] name = "primal-bit" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686a64e2f50194c64942992af5799e6b6e8775b8f88c607d72ed0a2fd58b9b21" dependencies = [ - "hamming 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "hamming", ] [[package]] name = "primal-check" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e65f96c0a171f887198c274392c99a116ef65aa7f53f3b6d4902f493965c2d1" dependencies = [ - "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer", ] [[package]] name = "primal-estimate" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ea4531dde757b56906493c8604641da14607bf9cdaa80fb9c9cabd2429f8d5" [[package]] name = "primal-sieve" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da2d6ed369bb4b0273aeeb43f07c105c0117717cbae827b20719438eb2eb798c" +dependencies = [ + "hamming", + "primal-bit", + "primal-estimate", + "smallvec 0.6.13", +] + +[[package]] +name = "primitive-types" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c55c21c64d0eaa4d7ed885d959ef2d62d9e488c27c0e02d9aa5ce6c877b7d5f8" +dependencies = [ + "fixed-hash 0.6.1", + "impl-codec", + "impl-rlp", + "impl-serde", + "uint 0.8.5", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" dependencies = [ - "hamming 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "primal-bit 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "primal-estimate 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.5.5", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" +dependencies = [ + "proc-macro2 1.0.20", + "quote 1.0.7", + "syn 1.0.40", ] [[package]] name = "proc-macro2" -version = "0.4.20" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0", +] + +[[package]] +name = "proc-macro2" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175c513d55719db99da20232b06cda8bab6b83ec2d04e3283edf0213c37c1a29" +dependencies = [ + "unicode-xid 0.2.0", +] + +[[package]] +name = "prometheus" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0ced56dee39a6e960c15c74dc48849d614586db2eaada6497477af7c7811cd" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static", + "protobuf", + "spin", + "thiserror", ] [[package]] name = "protobuf" -version = "1.7.4" +version = "2.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d883f78645c21b7281d21305181aa1f4dd9e9363e7cf2566c93121552cff003e" [[package]] name = "pulldown-cmark" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8361e81576d2e02643b04950e487ec172b687180da65c731c03cf336784e6c07" dependencies = [ - "getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "getopts", ] [[package]] name = "pwasm-run-test" version = "0.1.0" dependencies = [ - "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethjson 0.1.0", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "vm 0.1.0", - "wasm 0.1.0", + "clap", + "env_logger", + "ethereum-types 0.4.2", + "ethjson", + "rustc-hex 1.0.0", + "serde", + "serde_derive", + "serde_json", + "vm", + "wasm", ] [[package]] name = "pwasm-utils" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efb0dcbddbb600f47a7098d33762a00552c671992171637f5bb310b37fe1f0e4" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "log", + "parity-wasm", ] [[package]] name = "quick-error" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" [[package]] name = "quote" -version = "0.3.15" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", +] [[package]] name = "quote" -version = "0.6.8" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ - "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.20", ] +[[package]] +name = "radium" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" + [[package]] name = "rand" -version = "0.3.22" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" dependencies = [ - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "rand 0.4.6", ] [[package]] name = "rand" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" dependencies = [ - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi 0.3.8", ] [[package]] name = "rand" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "winapi 0.3.8", ] [[package]] name = "rand" -version = "0.6.1" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7", + "libc", + "rand_chacha 0.1.1", + "rand_core 0.4.2", + "rand_hc 0.1.0", + "rand_isaac", + "rand_jitter", + "rand_os 0.1.3", + "rand_pcg", + "rand_xorshift", + "winapi 0.3.8", +] + +[[package]] +name = "rand" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" +dependencies = [ + "getrandom", + "libc", + "rand_chacha 0.2.1", + "rand_core 0.5.1", + "rand_hc 0.2.0", ] [[package]] name = "rand_chacha" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" dependencies = [ - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7", + "rand_core 0.3.1", +] + +[[package]] +name = "rand_chacha" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +dependencies = [ + "c2-chacha", + "rand_core 0.5.1", ] [[package]] name = "rand_core" -version = "0.2.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" dependencies = [ - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2", ] [[package]] name = "rand_core" -version = "0.3.0" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] [[package]] name = "rand_hc" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" dependencies = [ - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", ] [[package]] name = "rand_isaac" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" dependencies = [ - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "rand_core 0.4.2", + "winapi 0.3.8", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi 0.3.8", +] + +[[package]] +name = "rand_os" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a788ae3edb696cfcba1c19bfd388cc4b8c21f8a408432b199c072825084da58a" +dependencies = [ + "getrandom", + "rand_core 0.5.1", ] [[package]] name = "rand_pcg" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" dependencies = [ - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7", + "rand_core 0.4.2", ] [[package]] name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_xoshiro" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b418169fb9c46533f326efd6eed2576699c44ca92d3052a066214a8d828929" dependencies = [ - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "rand_core 0.3.1", +] + +[[package]] +name = "rand_xoshiro" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e18c91676f670f6f0312764c759405f13afb98d5d73819840cf72a518487bff" +dependencies = [ + "rand_core 0.5.1", ] [[package]] name = "rayon" -version = "1.0.2" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a27732a533a1be0a0035a111fe76db89ad312f6f0347004c220c57f209a123" dependencies = [ - "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.7.1", + "either", + "rayon-core", ] [[package]] name = "rayon-core" -version = "1.4.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98dcf634205083b17d0861252431eb2acbfb698ab7478a2d20de07954f47ec7b" dependencies = [ - "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.7.1", + "crossbeam-queue 0.1.2", + "crossbeam-utils 0.6.6", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", ] [[package]] name = "redox_syscall" -version = "0.1.40" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" [[package]] -name = "redox_termios" -version = "0.1.1" +name = "regex" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384" dependencies = [ - "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.6.10", + "memchr", + "regex-syntax 0.5.6", + "thread_local 0.3.6", + "utf8-ranges", ] [[package]] name = "regex" -version = "0.2.11" +version = "1.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" dependencies = [ - "aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.7.6", + "memchr", + "regex-syntax 0.6.18", + "thread_local 1.0.1", ] [[package]] -name = "regex" -version = "1.1.2" +name = "regex-automata" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92b73c2a1770c255c240eaa4ee600df1704a38dc3feaa6e949e7fcd4f8dc09f9" dependencies = [ - "aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", ] [[package]] name = "regex-syntax" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" dependencies = [ - "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ucd-util", ] [[package]] name = "regex-syntax" -version = "0.6.2" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" [[package]] name = "registrar" version = "0.0.1" dependencies = [ - "ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 6.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi", + "ethabi-contract", + "ethabi-derive", + "futures", + "keccak-hash", +] + +[[package]] +name = "relay" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" +dependencies = [ + "futures", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" +dependencies = [ + "winapi 0.3.8", +] + +[[package]] +name = "ring" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "426bc186e3e95cac1e4a4be125a4aca7e84c2d616ffc02244eef36e2a60a093c" +dependencies = [ + "cc", + "lazy_static", + "libc", + "spin", + "untrusted", + "winapi 0.3.8", ] [[package]] -name = "relay" -version = "0.1.1" +name = "ripemd160" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad5112e0dbbb87577bfbc56c42450235e3012ce336e29c5befd7807bd626da4a" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer 0.7.3", + "digest 0.8.1", + "opaque-debug", ] [[package]] -name = "remove_dir_all" -version = "0.5.1" +name = "rlp" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524c5ad554859785dfc8469df3ed5e0b5784d4d335877ed47c8d90fc0eb238fe" dependencies = [ - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "elastic-array", + "ethereum-types 0.4.2", + "rustc-hex 2.0.1", ] [[package]] -name = "ring" -version = "0.14.3" +name = "rlp" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d1effe9845d54f90e7be8420ee49e5c94623140b97ee4bc6fb5bfddb745720" dependencies = [ - "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "ethereum-types 0.4.2", + "rustc-hex 2.0.1", ] [[package]] name = "rlp" -version = "0.2.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a7d3f9bed94764eac15b8f14af59fac420c236adaff743b7bcc88e265cb4345" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.0.1", ] [[package]] -name = "rlp" -version = "0.3.0" +name = "rlp-derive" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.20", + "quote 1.0.7", + "syn 1.0.40", ] [[package]] name = "rlp_compress" version = "0.1.0" dependencies = [ - "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "elastic-array", + "lazy_static", + "rlp 0.3.0", ] [[package]] name = "rlp_derive" version = "0.1.0" dependencies = [ - "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30", + "quote 0.6.13", + "rlp 0.3.0", + "syn 0.15.26", ] [[package]] name = "rpassword" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b273c91bd242ca03ad6d71c143b6f17a48790e61f21a6c78568fa2b6774a24a4" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "rprompt 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys", + "libc", + "rprompt", + "winapi 0.2.8", ] [[package]] name = "rprompt" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1601f32bc5858aae3cbfa1c645c96c4d820cc5c16be0194f089560c00b6eb625" [[package]] name = "rust-crypto" version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" dependencies = [ - "gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc", + "libc", + "rand 0.3.23", + "rustc-serialize", + "time", ] [[package]] name = "rustc-demangle" -version = "0.1.9" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" [[package]] name = "rustc-hex" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceb8ce7a5e520de349e1fa172baeba4a9e8d5ef06c47471863530bc4972ee1e" [[package]] name = "rustc-hex" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403bb3a286107a04825a5f82e1270acc1e14028d3d554d7a1e08914549575ab8" [[package]] name = "rustc-serialize" version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" [[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver", ] [[package]] name = "rustls" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f271e3552cd835fa28c541c34a7e8fdd8cdff09d77fe4eb8f6c42e87a11b096e" dependencies = [ - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.10.1", + "log", + "ring", + "sct", + "untrusted", + "webpki", ] [[package]] name = "ryu" -version = "0.2.6" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" [[package]] name = "safemem" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" [[package]] name = "same-file" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" dependencies = [ - "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util", ] [[package]] name = "scoped-tls" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" [[package]] name = "scopeguard" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" + +[[package]] +name = "scopeguard" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" + +[[package]] +name = "scrypt" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8570c5e2fa69cb29d492fd4e9974b6b5facb5a888e1c6da630d4a3cd7ebfef4a" +dependencies = [ + "byte-tools 0.3.1", + "byteorder", + "hmac 0.6.3", + "pbkdf2 0.2.3", + "sha2 0.7.1", +] + +[[package]] +name = "scrypt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "656c79d0e90d0ab28ac86bf3c3d10bfbbac91450d3f190113b4e76d9fec3cfdd" +dependencies = [ + "byte-tools 0.3.1", + "byteorder", + "hmac 0.7.1", + "pbkdf2 0.3.0", + "sha2 0.8.0", +] [[package]] name = "sct" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f5adf8fbd58e1b1b52699dc8bed2630faecb6d8c7bee77d009d6bbe4af569b9" dependencies = [ - "ring 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ring", + "untrusted", ] [[package]] name = "semver" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "semver-parser", + "serde", ] [[package]] name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.89" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b39bd9b0b087684013a792c59e3e07a46a01d2322518d8a1104641a0b1be0" +dependencies = [ + "serde_derive", +] [[package]] name = "serde_derive" -version = "1.0.89" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca13fc1a832f793322228923fbb3aba9f3f44444898f835d31ad1b74fa0a2bf8" dependencies = [ - "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.20", + "quote 1.0.7", + "syn 1.0.40", ] [[package]] name = "serde_json" -version = "1.0.39" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f72eb2a68a7dc3f9a691bfda9305a1c017a6215e5a4545c258500d2099a37c2" dependencies = [ - "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa", + "ryu", + "serde", ] [[package]] -name = "sha1" -version = "0.5.0" +name = "sha-1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug", +] [[package]] -name = "sha1" -version = "0.6.0" +name = "sha2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" +dependencies = [ + "block-buffer 0.3.3", + "byte-tools 0.2.0", + "digest 0.7.6", + "fake-simd", +] [[package]] name = "sha2" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" dependencies = [ - "block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug", ] [[package]] name = "shell32-sys" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ee04b46101f57121c9da2b151988283b6beb79b34f5bb29a58ee48cb695122c" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8", + "winapi-build", ] [[package]] -name = "simplelog" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "simple_uint" +version = "0.1.0" +source = "git+https://github.com/matter-labs/eip1962.git?rev=ece6cbabc41948db4200e41f0bfdab7ab94c7af8#ece6cbabc41948db4200e41f0bfdab7ab94c7af8" dependencies = [ - "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "crunchy 0.2.2", + "rustc-hex 2.0.1", + "static_assertions", ] [[package]] name = "siphasher" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "siphasher" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833011ca526bd88f16778d32c699d325a9ad302fa06381cd66f7be63351d3f6d" [[package]] name = "skeptic" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ebf8a06f5f8bae61ae5bbc7af7aac4ef6907ae975130faba1199e5fe82256a" dependencies = [ - "pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "pulldown-cmark", + "tempdir", ] [[package]] name = "slab" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbdd334bd28d328dad1c41b0ea662517883d8880d8533895ef96c8003dec9c4" [[package]] name = "slab" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" [[package]] name = "slab" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "smallvec" -version = "0.6.5" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" dependencies = [ - "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "maybe-uninit", ] +[[package]] +name = "smallvec" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" + [[package]] name = "socket2" -version = "0.3.8" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85" dependencies = [ - "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "libc", + "redox_syscall", + "winapi 0.3.8", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "stable_deref_trait" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stats" version = "0.1.0" dependencies = [ - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log", + "prometheus", +] + +[[package]] +name = "stream-cipher" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c" +dependencies = [ + "generic-array 0.12.3", ] [[package]] name = "string" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" +dependencies = [ + "bytes", +] [[package]] name = "strsim" -version = "0.7.0" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "strsim" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "032c03039aae92b350aad2e3779c352e104d919cb192ba2fabbd7b831ce4f0f6" + +[[package]] +name = "subtle" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" + +[[package]] +name = "subtle" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01dca13cf6c3b179864ab3292bd794e757618d35a7766b7c46050c614ba00829" [[package]] name = "syn" -version = "0.11.11" +version = "0.15.26" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9" dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid 0.1.0", ] [[package]] name = "syn" -version = "0.15.26" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "963f7d3cc59b59b9325165add223142bbf1df27655d07789f109896d353d8350" dependencies = [ - "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.20", + "quote 1.0.7", + "unicode-xid 0.2.0", ] [[package]] -name = "synom" -version = "0.11.3" +name = "synstructure" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" dependencies = [ - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.26", + "unicode-xid 0.1.0", ] [[package]] name = "synstructure" -version = "0.10.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "575be94ccb86e8da37efb894a87e2b660be299b41d8ef347f9d6d79fbe61b1ba" dependencies = [ - "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.20", + "quote 1.0.7", + "syn 1.0.40", + "unicode-xid 0.2.0", ] [[package]] name = "target_info" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe" [[package]] name = "tempdir" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" dependencies = [ - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.6", + "remove_dir_all", ] [[package]] -name = "term" -version = "0.5.1" +name = "tempfile" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "libc", + "rand 0.7.2", + "redox_syscall", + "remove_dir_all", + "winapi 0.3.8", ] [[package]] name = "term_size" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys", + "libc", + "winapi 0.2.8", ] [[package]] name = "termcolor" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" dependencies = [ - "wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wincolor", ] [[package]] -name = "termion" -version = "1.5.1" +name = "textwrap" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width", ] [[package]] name = "textwrap" -version = "0.9.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ - "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width", ] [[package]] -name = "textwrap" -version = "0.10.0" +name = "thiserror" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" dependencies = [ - "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.20", + "quote 1.0.7", + "syn 1.0.40", ] [[package]] name = "thread-id" version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "redox_syscall", + "winapi 0.3.8", ] [[package]] -name = "thread-scoped" -version = "1.0.2" +name = "thread_local" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +dependencies = [ + "lazy_static", +] [[package]] name = "thread_local" -version = "0.3.6" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", ] [[package]] name = "threadpool" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865" dependencies = [ - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus", ] [[package]] name = "time" -version = "0.1.40" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "redox_syscall", + "winapi 0.3.8", ] [[package]] @@ -3788,432 +4651,532 @@ version = "0.1.0" name = "timer" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d42176308937165701f50638db1c31586f183f1aab416268216577aec7306b" dependencies = [ - "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono", ] [[package]] name = "tiny-keccak" -version = "1.4.2" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d8a021c69bb74a44ccedb824a046447e2c84a01df9e5c20779750acb38e11b2" +dependencies = [ + "crunchy 0.2.2", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy 0.2.2", +] + +[[package]] +name = "tinytemplate" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4574b75faccaacddb9b284faecdf0b544b80b6b294f3d062d325c5726a209c20" dependencies = [ - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", + "serde_json", ] [[package]] name = "tokio" -version = "0.1.11" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" +dependencies = [ + "bytes", + "futures", + "mio", + "num_cpus", + "tokio-codec", + "tokio-current-thread", + "tokio-executor", + "tokio-fs", + "tokio-io", + "tokio-reactor", + "tokio-sync", + "tokio-tcp", + "tokio-threadpool", + "tokio-timer 0.2.13", + "tokio-udp", + "tokio-uds", +] + +[[package]] +name = "tokio-buf" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46" dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-current-thread 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-fs 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-udp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-uds 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "either", + "futures", ] [[package]] name = "tokio-codec" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "futures", + "tokio-io", ] [[package]] name = "tokio-core" version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeeffbbb94209023feaef3c196a41cbcdafa06b4a6f893f68779bb5e53796f71" dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "futures", + "iovec", + "log", + "mio", + "scoped-tls", + "tokio", + "tokio-executor", + "tokio-io", + "tokio-reactor", + "tokio-timer 0.2.13", ] [[package]] name = "tokio-current-thread" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures", + "tokio-executor", ] [[package]] name = "tokio-executor" -version = "0.1.5" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2", + "futures", ] [[package]] name = "tokio-fs" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures", + "tokio-io", + "tokio-threadpool", ] [[package]] name = "tokio-io" -version = "0.1.9" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "futures", + "log", ] [[package]] name = "tokio-named-pipes" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d282d483052288b2308ba5ee795f5673b159c9bdf63c385a05609da782a5eae" dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "futures", + "mio", + "mio-named-pipes", + "tokio", ] [[package]] name = "tokio-reactor" -version = "0.1.6" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" dependencies = [ - "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2", + "futures", + "lazy_static", + "log", + "mio", + "num_cpus", + "parking_lot 0.9.0", + "slab 0.4.2", + "tokio-executor", + "tokio-io", + "tokio-sync", ] [[package]] name = "tokio-retry" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05746ae87dca83a2016b4f5dba5b237b897dd12fd324f60afe282112f16969a" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures", + "rand 0.3.23", + "tokio-core", + "tokio-service", ] [[package]] name = "tokio-rustls" -version = "0.9.0" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a199832a67452c60bed18ed951d28d5755ff57b02b3d2d535d9f13a81ea6c9" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "rustls 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures", + "rustls", + "tokio-io", + "webpki", ] [[package]] name = "tokio-service" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "futures", +] + +[[package]] +name = "tokio-sync" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d06554cce1ae4a50f42fba8023918afa931413aded705b560e29600ccf7c6d76" +dependencies = [ + "fnv", + "futures", ] [[package]] name = "tokio-tcp" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "futures", + "iovec", + "mio", + "tokio-io", + "tokio-reactor", ] [[package]] name = "tokio-threadpool" -version = "0.1.7" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" dependencies = [ - "crossbeam-deque 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.7.1", + "crossbeam-queue 0.2.3", + "crossbeam-utils 0.7.2", + "futures", + "lazy_static", + "log", + "num_cpus", + "slab 0.4.2", + "tokio-executor", ] [[package]] name = "tokio-timer" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6131e780037787ff1b3f8aad9da83bca02438b72277850dd6ad0d455e0e20efc" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures", + "slab 0.3.0", ] [[package]] name = "tokio-timer" -version = "0.2.7" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" dependencies = [ - "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2", + "futures", + "slab 0.4.2", + "tokio-executor", ] [[package]] name = "tokio-udp" -version = "0.1.2" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02298505547f73e60f568359ef0d016d5acd6e830ab9bc7c4a5b3403440121b" dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "futures", + "log", + "mio", + "tokio-codec", + "tokio-io", + "tokio-reactor", ] [[package]] name = "tokio-uds" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445" dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "futures", + "iovec", + "libc", + "log", + "mio", + "mio-uds", + "tokio-codec", + "tokio-io", + "tokio-reactor", ] [[package]] name = "toml" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" +dependencies = [ + "serde", +] + +[[package]] +name = "toml" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d1404644c8b12b16bfcffa4322403a91a451584daaaa7c28d3152e6cbc98cf" dependencies = [ - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", ] [[package]] name = "toolshed" -version = "0.4.0" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a272adbf14cfbb486774d09ee3e00c38d488cd390084a528f70e10e3a184a8" dependencies = [ - "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fxhash", ] [[package]] name = "trace-time" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9adf04084eeb9a1ea91be6c3f8ef3df392391c91fc7d8f696d4875f6754e715" dependencies = [ - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log", ] [[package]] name = "transaction-pool" -version = "2.0.0" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8bbee24c711a878e7d8f89460569034cacf2d8c58dde785b5ffa06ed6b59663" dependencies = [ - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "trace-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log", + "smallvec 0.6.13", + "trace-time", ] [[package]] name = "transient-hashmap" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "trezor-sys" -version = "1.0.0" -source = "git+https://github.com/paritytech/trezor-sys#8a401705e58c83db6c29c199d9577b78fde40709" -dependencies = [ - "protobuf 1.7.4 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "aeb4b191d033a35edfce392a38cdcf9790b6cebcb30fa690c312c29da4dc433e" [[package]] name = "trie-db" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c7319e28ca295f27359d944a682f7f65b419158bf1590c92cadc0000258d788" dependencies = [ - "elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "elastic-array", + "hash-db", + "log", + "rand 0.6.5", ] [[package]] name = "trie-standardmap" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e26f52976a57a0859616d6fcec87092ac35d08eabbd78dc3dabee93b480ea5f" dependencies = [ - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.4.2", + "keccak-hash", + "parity-bytes", + "rlp 0.2.4", ] [[package]] name = "triehash" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d26efb4ddf87870fc08dc9a6580dc3061be350d7b9d0eb30aef1c8b4227aa46" dependencies = [ - "hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db", + "rlp 0.3.0", ] [[package]] name = "triehash-ethereum" version = "0.2.0" dependencies = [ - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.4.2", "keccak-hasher 0.1.1", - "triehash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "triehash", ] [[package]] name = "try-lock" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2aa4715743892880f70885373966c83d73ef1b0838a664ef0c76fffd35e7c2" [[package]] name = "try-lock" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" [[package]] name = "typenum" -version = "1.10.0" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" [[package]] name = "ucd-util" -version = "0.1.1" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85f514e095d348c279b1e5cd76795082cf15bd59b93207832abe0b1d8fed236" [[package]] name = "uint" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ba11732b9161b94c41798e5197e5e75388d012f760c42adb5000353e98646" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "crunchy 0.1.6", + "heapsize", + "rustc-hex 2.0.1", ] [[package]] -name = "unexpected" -version = "0.1.0" - -[[package]] -name = "unicase" -version = "1.4.2" +name = "uint" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9db035e67dfaf7edd9aebfe8676afcd63eed53c8a4044fed514c8cccf1835177" dependencies = [ - "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "crunchy 0.2.2", + "rustc-hex 2.0.1", + "static_assertions", ] +[[package]] +name = "unexpected" +version = "0.1.0" + [[package]] name = "unicase" -version = "2.2.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e2e6bd1e59e56598518beb94fd6db628ded570326f0a98c679a304bd9f00150" dependencies = [ - "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check", ] [[package]] name = "unicode-bidi" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "matches", ] [[package]] name = "unicode-normalization" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" +dependencies = [ + "smallvec 0.6.13", +] [[package]] name = "unicode-segmentation" -version = "1.2.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f5526225fd8b77342d5986ab5f6055552e9c0776193b5b63fd53b46debfad7" [[package]] name = "unicode-width" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" [[package]] name = "unicode-xid" -version = "0.0.4" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" [[package]] name = "unicode-xid" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] -name = "unreachable" -version = "1.0.0" +name = "untrusted" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f" [[package]] -name = "untrusted" -version = "0.6.2" +name = "url" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" +dependencies = [ + "idna 0.1.5", + "matches", + "percent-encoding 1.0.1", +] [[package]] name = "url" -version = "1.7.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61" dependencies = [ - "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.2.0", + "matches", + "percent-encoding 2.1.0", ] [[package]] @@ -4222,609 +5185,264 @@ version = "0.1.0" [[package]] name = "utf8-ranges" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba" [[package]] name = "validator" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "236a5eda3df2c877872e98dbc55d497d943792e6405d8fc65bd4f8a5e3b53c99" dependencies = [ - "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.1.5", + "lazy_static", + "regex 1.3.9", + "serde", + "serde_derive", + "serde_json", + "url 1.7.2", ] [[package]] name = "validator_derive" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d360d6f5754972c0c1da14fb3d5580daa31aee566e1e45e2f8d3bf5950ecd3e9" dependencies = [ - "if_chain 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", - "validator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "if_chain", + "lazy_static", + "proc-macro2 0.4.30", + "quote 0.6.13", + "regex 1.3.9", + "syn 0.15.26", + "validator", ] [[package]] name = "vec_map" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" [[package]] name = "vergen" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3365f36c57e5df714a34be40902b27a992eeddb9996eca52d0584611cf885d" dependencies = [ - "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 0.7.0", + "time", ] [[package]] name = "version_check" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" [[package]] name = "vm" version = "0.1.0" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethjson 0.1.0", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "patricia-trie-ethereum 0.1.0", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.4.2", + "ethjson", + "keccak-hash", + "parity-bytes", + "patricia-trie-ethereum", + "rlp 0.3.0", ] -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "walkdir" -version = "2.2.5" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" dependencies = [ - "same-file 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file", + "winapi 0.3.8", + "winapi-util", ] [[package]] name = "want" version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a05d9d966753fa4b5c8db73fcab5eed4549cfe0e1e4e66911e5564a0085c35d1" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures", + "log", + "try-lock 0.1.0", ] [[package]] name = "want" -version = "0.0.6" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures", + "log", + "try-lock 0.2.2", ] +[[package]] +name = "wasi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" + [[package]] name = "wasm" version = "0.1.0" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)", - "pwasm-utils 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "vm 0.1.0", - "wasmi 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "env_logger", + "ethereum-types 0.4.2", + "libc", + "log", + "parity-wasm", + "pwasm-utils", + "vm", + "wasmi", ] [[package]] name = "wasmi" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4a6d379e9332b1b1f52c5a87f2481c85c7c931d8ec411963dfb8f26b1ec1e3" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "nan-preserving-float 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "memory_units", + "nan-preserving-float", + "parity-wasm", ] [[package]] name = "webpki" version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f7e1cd7900a3a6b65a3e8780c51a3e6b59c0e2c55c6dc69578c288d69f7d082" dependencies = [ - "ring 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ring", + "untrusted", ] [[package]] name = "webpki-roots" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c10fa4212003ba19a564f25cd8ab572c6791f99a03cc219c13ed35ccab00de0e" dependencies = [ - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "whisper-cli" -version = "0.1.0" -dependencies = [ - "docopt 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-network 1.12.0", - "ethcore-network-devp2p 1.12.0", - "ethkey 0.3.0", - "jsonrpc-core 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-http-server 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-pubsub 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "panic_hook 0.1.0", - "parity-whisper 0.1.0", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "untrusted", + "webpki", ] [[package]] name = "winapi" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" [[package]] name = "winapi" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" [[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.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" dependencies = [ - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8", ] [[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 = "wincolor" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96f5016b18804d24db43cebf3c77269e7569b8954a8464501c216cc5e070eaa9" dependencies = [ - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8", + "winapi-util", ] [[package]] name = "ws2_32-sys" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8", + "winapi-build", ] [[package]] name = "xdg" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" [[package]] name = "xml-rs" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c1cb601d29fe2c2ac60a2b2e5e293994d87a1f6fa9687a31a15270f909be9c2" dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", ] [[package]] name = "xmltree" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9cfb54ca6b8f17d2377219ce485b134d53561b77e1393c7ea416f543a527431" +dependencies = [ + "xml-rs", +] + +[[package]] +name = "zeroize" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45af6a010d13e4cf5b54c94ba5a2b2eba5596b9e46bf5875612d332a1f2b3f86" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "080616bd0e31f36095288bb0acdf1f78ef02c2fa15527d7e993f2a6c7591643e" dependencies = [ - "xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "68f56c7353e5a9547cbd76ed90f7bb5ffc3ba09d4ea9bd1d8c06c8b1142eeb5a" -"checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455" -"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -"checksum app_dirs 1.2.1 (git+https://github.com/paritytech/app-dirs-rs)" = "" -"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" -"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" -"checksum ascii 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae7d751998c189c1d4468cf0a39bb2eae052a9c58d50ebb3b9591ee3813ad50" -"checksum assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7deb0a829ca7bcfaf5da70b073a8d128619259a7be8216a355e23f00763059e5" -"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" -"checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" -"checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" -"checksum base-x 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5cda5d0f5584d129112ad8bf4775b9fd2b9f1e30738c7b1a25314ba2244d6a51" -"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" -"checksum bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e103c8b299b28a9c6990458b7013dc4a8356a9b854c51b9883241f5866fac36e" -"checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c" -"checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f" -"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" -"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" -"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" -"checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" -"checksum bn 0.4.4 (git+https://github.com/paritytech/bn)" = "" -"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" -"checksum byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "90492c5858dd7d2e78691cfb89f90d273a2800fc11d98f60786e5d87e2f83781" -"checksum bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0ce55bd354b095246fc34caf4e9e242f5297a7fd938b090cadfea6eee614aa62" -"checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" -"checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749" -"checksum cesu8 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" -"checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" -"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" -"checksum cid 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0e37fba0087d9f3f4e269827a55dc511abf3e440cc097a0c154ff4e6584f988" -"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" -"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum cmake 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "6ec65ee4f9c9d16f335091d23693457ed4928657ba4982289d7fafee03bc614a" -"checksum combine 3.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fc1d011beeed29187b8db2ac3925c8dd4d3e87db463dc9d2d2833985388fc5bc" -"checksum criterion 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c47d2b548c5647e1a436dc0cb78d4ebf51b6bf7ab101ed76662828bdd4d3a24a" -"checksum criterion-plot 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6e649d6aacdbbdb94ec659561a309a71336fc5655ed408f3afd28df2fc0c4f4f" -"checksum criterion-stats 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ff43cac80562f91ead0b617c1be74edf350adfaa195809d355de98dfc8f9237d" -"checksum crossbeam 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d7408247b1b87f480890f28b670c5f8d9a8a4274833433fe74dc0dfd46d33650" -"checksum crossbeam-channel 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7b85741761b7f160bc5e7e0c14986ef685b7f8bf9b7ad081c60c604bb4649827" -"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" -"checksum crossbeam-deque 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7792c4a9b5a4222f654e3728a3dd945aacc24d2c3a1a096ed265d80e4929cb9a" -"checksum crossbeam-deque 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3486aefc4c0487b9cb52372c97df0a48b8c249514af1ee99703bf70d2f2ceda1" -"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" -"checksum crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30fecfcac6abfef8771151f8be4abc9e4edc112c2bcb233314cafde2680536e9" -"checksum crossbeam-epoch 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2449aaa4ec7ef96e5fb24db16024b935df718e9ae1cec0a1e68feeca2efca7b8" -"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" -"checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015" -"checksum crossbeam-utils 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e07fc155212827475223f0bcfae57e945e694fc90950ddf3f6695bbfd5555c72" -"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" -"checksum crunchy 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c240f247c278fa08a6d4820a6a222bfc6e0d999e51ba67be94f44c905b2161f2" -"checksum csv 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d54f6b0fd69128a2894b1a3e57af5849a0963c1cc77b165d30b896e40296452" -"checksum csv-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4dd8e6d86f7ba48b4276ef1317edc8cc36167546d8972feb4a2b5fec0b374105" -"checksum ct-logs 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b4660f8b07a560a88c02d76286edb9f0d5d64e495d2b0f233186155aa51be1f" -"checksum ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)" = "" -"checksum derive_more 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fbe9f11be34f800b3ecaaed0ec9ec2e015d1d0ba0c8644c1310f73d6e8994615" -"checksum difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3304d19798a8e067e48d8e69b2c37f0b5e9b4e462504ad9e27e9f3fce02bba8" -"checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" -"checksum docopt 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db2906c2579b5b7207fc1e328796a9a8835dc44e22dbe8e460b1d636f9a7b225" -"checksum edit-distance 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3bd26878c3d921f89797a4e1a1711919f999a9f6946bb6f5a4ffda126d297b7e" -"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" -"checksum elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "88d4851b005ef16de812ea9acdb7bece2f0a40dd86c07b85631d7dafa54537bb" -"checksum enum_primitive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180" -"checksum env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)" = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" -"checksum error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02" -"checksum eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)" = "" -"checksum ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8eb362fde43ed0b50b258bb0c72b72b3dccfd29f8de9506295eaf9251c49ca31" -"checksum ethabi-contract 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "795e25fd868e12a59ca235dbe1f6cc8f1eba8f67d6a39438b29535e0126e0c27" -"checksum ethabi-derive 6.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "66a587250c8190be9d6ae28d67b8957ed97cb9eee2e272173a20593ab054a075" -"checksum ethbloom 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a93a43ce2e9f09071449da36bfa7a1b20b950ee344b6904ff23de493b03b386" -"checksum ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e742184dc63a01c8ea0637369f8faa27c40f537949908a237f95c05e68d2c96" -"checksum ethereum-types-serialize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1873d77b32bc1891a79dad925f2acbc318ee942b38b9110f9dbc5fbeffcea350" -"checksum failsafe 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad3bf1642583ea2f1fa38a1e8546613a7488816941b33e5f0fccceac61879118" -"checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7" -"checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596" -"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -"checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa" -"checksum fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d5ec8112f00ea8a483e04748a85522184418fd1cf02890b626d8fc28683f7de" -"checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" -"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" -"checksum fs-swap 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "921d332c89b3b61a826de38c61ee5b6e02c56806cade1b0e5d81bd71f57a71bb" -"checksum fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674" -"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "49e7653e374fe0d0c12de4250f0bdb60680b8c80eed558c5c7538eec9c89e21b" -"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" -"checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -"checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" -"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" -"checksum getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0a7292d30132fb5424b354f5dc02512a86e4c516fe544bb7a25e7f266951b797" -"checksum globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4743617a7464bbda3c8aec8558ff2f9429047e025771037df561d383337ff865" -"checksum h2 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "a27e7ed946e8335bdf9a191bc1b9b14a03ba822d013d2f58437f4fabcbd7fc2c" -"checksum hamming 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65043da274378d68241eb9a8f8f8aa54e349136f7b8e12f63e3ef44043cc30e1" -"checksum handlebars 0.32.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d89ec99d1594f285d4590fc32bac5f75cdab383f1123d504d27862c644a807dd" -"checksum hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1b03501f6e1a2a97f1618879aba3156f14ca2847faa530c4e28859638bd11483" -"checksum heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)" = "" -"checksum heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea04fa3ead4e05e51a7c806fc07271fdbde4e246a6c6d1efd52e72230b771b82" -"checksum hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa" -"checksum hidapi 0.3.1 (git+https://github.com/paritytech/hidapi-rs)" = "" -"checksum home 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "80dff82fb58cfbbc617fb9a9184b010be0529201553cda50ad04372bc2333aff" -"checksum http 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "1a10e5b573b9a0146545010f50772b9e8b1dd0a256564cc4307694c68832a2f5" -"checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83" -"checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e" -"checksum hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)" = "34a590ca09d341e94cddf8e5af0bbccde205d5fbc2fa3c09dd67c7f85cea59d7" -"checksum hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)" = "f1ebec079129e43af5e234ef36ee3d7e6085687d145b7ea653b262d16c6b65f1" -"checksum hyper-rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff2c61fbda2bc72e793e329190a3e8f0ae74cb896905c8b301304c4c93f2755" -"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -"checksum if_chain 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4bac95d9aa0624e7b78187d6fb8ab012b41d9f6f54b1bcb61e61c4845f8357ec" -"checksum igd 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a254e265e8810deb357a9de757f784787ec415d056ededf410c0aa460afee9e" -"checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" -"checksum integer-encoding 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "26746cbc2e680af687e88d717f20ff90079bd10fc984ad57d277cd0e37309fa5" -"checksum interleaved-ordered 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "141340095b15ed7491bd3d4ced9d20cebfb826174b6bb03386381f62b01e3d77" -"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" -"checksum ipnetwork 0.12.8 (registry+https://github.com/rust-lang/crates.io-index)" = "70783119ac90828aaba91eae39db32c6c1b8838deea3637e5238efa0130801ab" -"checksum itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4833d6978da405305126af4ac88569b5d71ff758581ce5a987dbfa3755f694fc" -"checksum itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f58856976b776fedd95533137617a02fb25719f40e7d9b01c7043cd65474f450" -"checksum itertools-num 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "83ca7b70b838f2e34bc6c2f367a1ed1cfe34fb82464adecadd31cdcc7da882fc" -"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" -"checksum jemalloc-sys 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "bfc62c8e50e381768ce8ee0428ee53741929f7ebd73e4d83f669bcf7693e00ae" -"checksum jemallocator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9f0cd42ac65f758063fea55126b0148b1ce0a6354ff78e07a4d6806bc65c4ab3" -"checksum jni 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "294eca097d1dc0bf59de5ab9f7eafa5f77129e9f6464c957ed3ddeb705fb4292" -"checksum jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" -"checksum jsonrpc-core 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a5152c3fda235dfd68341b3edf4121bc4428642c93acbd6de88c26bf95fc5d7" -"checksum jsonrpc-derive 10.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c14be84e86c75935be83a34c6765bf31f97ed6c9163bb0b83007190e9703940a" -"checksum jsonrpc-http-server 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "99e1ce36c7cc9dcab398024d76849ab2cb917ee812653bce6f74fc9eb7c82d16" -"checksum jsonrpc-ipc-server 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fac6b8682243740a32bfb288880c71cc06eca29616cdf551e4136a190b11b96d" -"checksum jsonrpc-pubsub 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "56608ed54b1b2a69f4357cb8bdfbcbd99fe1179383c03a09bb428931bd35f592" -"checksum jsonrpc-server-utils 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5521613b31ea22d36d9f95ad642058dccec846a94ed8690957652d479f620707" -"checksum jsonrpc-tcp-server 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c873dac37a601fb88d40ba49eeac3f1aa60953c06b2e99ddbf0569b6f8028478" -"checksum jsonrpc-ws-server 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20b8333a5a6e6ccbcf5c90f90919de557cba4929efa164e9bd0e8e497eb20e46" -"checksum keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "253bbe643c32c816bf58fa5a88248fafedeebb139705ad17a62add3517854a86" -"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "72ae89206cea31c32014b39d5a454b96135894221610dbfd19cf4d2d044fa546" -"checksum kvdb-memorydb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45bcdf5eb083602cff61a6f8438dce2a7900d714e893fc48781c39fb119d37aa" -"checksum kvdb-rocksdb 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "06cf755dc587839ba34d3cbe3f12b6ad55850fbcdfe67336157a021a1a5c43ae" -"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" -"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" -"checksum lazycell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddba4c30a78328befecec92fc94970e53b3ae385827d28620f0f5bb2493081e0" -"checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047" -"checksum libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3ad660d7cb8c5822cd83d10897b0f1f1526792737a179e73896152f85b88c2" -"checksum libusb 0.3.0 (git+https://github.com/paritytech/libusb-rs)" = "" -"checksum libusb-sys 0.2.4 (git+https://github.com/paritytech/libusb-sys)" = "" -"checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e" -"checksum local-encoding 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1ceb20f39ff7ae42f3ff9795f3986b1daad821caaa1e1732a0944103a5a1a66" -"checksum lock_api 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775751a3e69bde4df9b38dd00a1b5d6ac13791e4223d4a0506577f0dd27cfb7a" -"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" -"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" -"checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -"checksum lunarity-lexer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a1670671f305792567116d4660e6e5bd785d6fa973e817c3445c0a7a54cecb6" -"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -"checksum memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4b3629fe9fdbff6daa6c33b90f7c08355c1aca05a3d01fa8063b822fcf185f3b" -"checksum memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff" -"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" -"checksum memory-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94da53143d45f6bad3753f532e56ad57a6a26c0ca6881794583310c7cb4c885f" -"checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" -"checksum mime 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "0a907b83e7b9e987032439a387e187119cddafc92d5c2aaeb1d92580a793f630" -"checksum mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "30de2e4613efcba1ec63d8133f344076952090c122992a903359be5a4f99c3ed" -"checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432" -"checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40" -"checksum mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" -"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" -"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226" -"checksum multibase 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b9c35dac080fd6e16a99924c8dfdef0af89d797dd851adab25feaffacf7850d6" -"checksum multihash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c62469025f45dee2464ef9fc845f4683c543993792c1993e7d903c17a4546b74" -"checksum nan-preserving-float 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34d4f00fcc2f4c9efa8cc971db0da9e28290e28e97af47585e48691ef10ff31f" -"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" -"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" -"checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" -"checksum num-bigint 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1" -"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" -"checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" -"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" -"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" -"checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" -"checksum number_prefix 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dbf9993e59c894e3c08aa1c2712914e9e6bf1fcbfc6bef283e2183df345a4fee" -"checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c" -"checksum order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "efa535d5117d3661134dbf1719b6f0ffe06f2375843b13935db186cd094105eb" -"checksum ordered-float 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7eb5259643245d3f292c7a146b2df53bba24d7eab159410e648eb73dc164669d" -"checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" -"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" -"checksum parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa5168b4cf41f3835e4bc6ffb32f51bc9365dc50cb351904595b3931d917fd0c" -"checksum parity-crypto 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b9db194dfbcfe3b398d63d765437a5c7232d59906e203055f0e993f6458ff1" -"checksum parity-daemonize 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "69b1910b2793ff52713fca0a4ee92544ebec59ccd218ea74560be6f947b4ca77" -"checksum parity-path 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5962540f99d3895d9addf535f37ab1397886bc2c68e59efd040ef458e5f8c3f7" -"checksum parity-rocksdb 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cd55d2d6d6000ec99f021cf52c9acc7d2a402e14f95ced4c5de230696fabe00b" -"checksum parity-rocksdb-sys 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5bbb241262c768522f6460f0e2672dee185c8504d4d0a5a5bab45c1147981c4f" -"checksum parity-snappy 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2c5f9d149b13134b8b354d93a92830efcbee6fe5b73a2e6e540fe70d4dd8a63" -"checksum parity-snappy-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1a413d51e5e1927320c9de992998e4a279dffb8c8a7363570198bd8383e66f1b" -"checksum parity-tokio-ipc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eb002c2d3539ccd3b82bd915ec060028d4ab350ad203dbffa20028c1e483af5b" -"checksum parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)" = "511379a8194230c2395d2f5fa627a5a7e108a9f976656ce723ae68fca4097bfc" -"checksum parity-wordlist 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf13102febd98f4ad416a526b42deb82daf482626ba6ab10d0ebf8f45327514c" -"checksum parity-ws 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2fec5048fba72a2e01baeb0d08089db79aead4b57e2443df172fb1840075a233" -"checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" -"checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" -"checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" -"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" -"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" -"checksum pest 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0fce5d8b5cc33983fc74f78ad552b5522ab41442c4ca91606e4236eb4b5ceefc" -"checksum pest_derive 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3294f437119209b084c797604295f40227cffa35c57220b1e99a6ff3bf8ee4" -"checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" -"checksum phf 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "cec29da322b242f4c3098852c77a0ca261c9c01b806cae85a5572a1eb94db9a6" -"checksum phf_codegen 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "7d187f00cd98d5afbcd8898f6cf181743a449162aeb329dcd2f3849009e605ad" -"checksum phf_generator 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "03dc191feb9b08b0dc1330d6549b795b9d81aec19efe6b4a45aec8d4caee0c4b" -"checksum phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "b539898d22d4273ded07f64a05737649dc69095d92cb87c7097ec68e3f150b93" -"checksum plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "95fa6386b1d34aaf0adb9b7dd2885dbe7c34190e6263785e5a7ec2b19044a90f" -"checksum pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2412f3332a07c7a2a50168988dcc184f32180a9758ad470390e5f55e089f6b6e" -"checksum primal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0e31b86efadeaeb1235452171a66689682783149a6249ff334a2c5d8218d00a4" -"checksum primal-bit 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "686a64e2f50194c64942992af5799e6b6e8775b8f88c607d72ed0a2fd58b9b21" -"checksum primal-check 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8e65f96c0a171f887198c274392c99a116ef65aa7f53f3b6d4902f493965c2d1" -"checksum primal-estimate 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "56ea4531dde757b56906493c8604641da14607bf9cdaa80fb9c9cabd2429f8d5" -"checksum primal-sieve 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "da2d6ed369bb4b0273aeeb43f07c105c0117717cbae827b20719438eb2eb798c" -"checksum proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "3d7b7eaaa90b4a90a932a9ea6666c95a389e424eff347f0f793979289429feee" -"checksum protobuf 1.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "52fbc45bf6709565e44ef31847eb7407b3c3c80af811ee884a04da071dcca12b" -"checksum pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8361e81576d2e02643b04950e487ec172b687180da65c731c03cf336784e6c07" -"checksum pwasm-utils 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e9135bed7b452e20dbb395a2d519abaf0c46d60e7ecc02daeeab447d29bada1" -"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" -"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" -"checksum quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dd636425967c33af890042c483632d33fa7a18f19ad1d7ea72e8998c6ef8dea5" -"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" -"checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" -"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" -"checksum rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae9d223d52ae411a33cf7e54ec6034ec165df296ccd23533d671a28252b6f66a" -"checksum rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "771b009e3a508cb67e8823dda454aaa5368c7bc1c16829fb77d3e980440dd34a" -"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372" -"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db" -"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -"checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05" -"checksum rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effa3fcaa47e18db002bdde6060944b6d2f9cfd8db471c30e873448ad9187be3" -"checksum rayon 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "df7a791f788cb4c516f0e091301a29c2b71ef680db5e644a7d68835c8ae6dbfa" -"checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356" -"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" -"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" -"checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384" -"checksum regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53ee8cfdddb2e0291adfb9f13d31d3bbe0a03c9a402c01b1e24188d86c35b24f" -"checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" -"checksum regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "747ba3b235651f6e2f67dfa8bcdcd073ddb7c243cb21c442fc12395dfcac212d" -"checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" -"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" -"checksum ring 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)" = "be5386a5f59e5f5bcaea38b50ad26c09e3918a0abc0610640b3be5cfd85d6894" -"checksum rlp 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "524c5ad554859785dfc8469df3ed5e0b5784d4d335877ed47c8d90fc0eb238fe" -"checksum rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "16d1effe9845d54f90e7be8420ee49e5c94623140b97ee4bc6fb5bfddb745720" -"checksum rpassword 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b273c91bd242ca03ad6d71c143b6f17a48790e61f21a6c78568fa2b6774a24a4" -"checksum rprompt 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1601f32bc5858aae3cbfa1c645c96c4d820cc5c16be0194f089560c00b6eb625" -"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" -"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" -"checksum rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0ceb8ce7a5e520de349e1fa172baeba4a9e8d5ef06c47471863530bc4972ee1e" -"checksum rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "403bb3a286107a04825a5f82e1270acc1e14028d3d554d7a1e08914549575ab8" -"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" -"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum rustls 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)" = "38af00e78b66109e7184a0ee16940f41583161b7ec0518af258e4bcaed15db25" -"checksum ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7153dd96dade874ab973e098cb62fcdbb89a03682e46b144fd09550998d4a4a7" -"checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9" -"checksum same-file 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10f7794e2fda7f594866840e95f5c5962e886e228e68b6505885811a94dd728c" -"checksum scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" -"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" -"checksum sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f5adf8fbd58e1b1b52699dc8bed2630faecb6d8c7bee77d009d6bbe4af569b9" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "92514fb95f900c9b5126e32d020f5c6d40564c27a5ea6d1d7d9f157a96623560" -"checksum serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6eabf4b5914e88e24eea240bb7c9f9a2cbc1bbbe8d961d381975ec3c6b806c" -"checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d" -"checksum sha1 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "171698ce4ec7cbb93babeb3190021b4d72e96ccb98e33d277ae4ea959d6f2d9e" -"checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" -"checksum sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" -"checksum shell32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9ee04b46101f57121c9da2b151988283b6beb79b34f5bb29a58ee48cb695122c" -"checksum simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e95345f185d5adeb8ec93459d2dc99654e294cc6ccf5b75414d8ea262de9a13" -"checksum siphasher 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "833011ca526bd88f16778d32c699d325a9ad302fa06381cd66f7be63351d3f6d" -"checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" -"checksum skeptic 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24ebf8a06f5f8bae61ae5bbc7af7aac4ef6907ae975130faba1199e5fe82256a" -"checksum slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6dbdd334bd28d328dad1c41b0ea662517883d8880d8533895ef96c8003dec9c4" -"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" -"checksum slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f9776d6b986f77b35c6cf846c11ad986ff128fe0b2b63a3628e3755e8d3102d" -"checksum smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "153ffa32fd170e9944f7e0838edf824a754ec4c1fc64746fcc9fe1f8fa602e5d" -"checksum socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c4d11a52082057d87cb5caa31ad812f4504b97ab44732cd8359df2e9ff9f48e7" -"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" -"checksum string 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00caf261d6f90f588f8450b8e1230fa0d5be49ee6140fdfbcb55335aff350970" -"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" -"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" -"checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9" -"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" -"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" -"checksum target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe" -"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" -"checksum term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e6b677dd1e8214ea1ef4297f85dbcbed8e8cdddb561040cc998ca2551c37561" -"checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327" -"checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f" -"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" -"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" -"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" -"checksum thread-id 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" -"checksum thread-scoped 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bcbb6aa301e5d3b0b5ef639c9a9c7e2f1c944f177b460c04dc24c69b1fa2bd99" -"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" -"checksum threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865" -"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" -"checksum timer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "31d42176308937165701f50638db1c31586f183f1aab416268216577aec7306b" -"checksum tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e9175261fbdb60781fcd388a4d6cc7e14764a2b629a7ad94abb439aed223a44f" -"checksum tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "6e93c78d23cc61aa245a8acd2c4a79c4d7fa7fb5c3ca90d5737029f043a84895" -"checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" -"checksum tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "aeeffbbb94209023feaef3c196a41cbcdafa06b4a6f893f68779bb5e53796f71" -"checksum tokio-current-thread 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f90fcd90952f0a496d438a976afba8e5c205fb12123f813d8ab3aa1c8436638c" -"checksum tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c117b6cf86bb730aab4834f10df96e4dd586eff2c3c27d3781348da49e255bde" -"checksum tokio-fs 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b5cbe4ca6e71cb0b62a66e4e6f53a8c06a6eefe46cc5f665ad6f274c9906f135" -"checksum tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "8b8a85fffbec3c5ab1ab62324570230dcd37ee5996a7859da5caf7b9d45e3e8c" -"checksum tokio-named-pipes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d282d483052288b2308ba5ee795f5673b159c9bdf63c385a05609da782a5eae" -"checksum tokio-reactor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4b26fd37f1125738b2170c80b551f69ff6fecb277e6e5ca885e53eec2b005018" -"checksum tokio-retry 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f05746ae87dca83a2016b4f5dba5b237b897dd12fd324f60afe282112f16969a" -"checksum tokio-rustls 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7223fa02f4b2d9f3736f13cc3dea3723aaec57ca4b3dded922126ebbb2cb8ce9" -"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" -"checksum tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7ad235e9dadd126b2d47f6736f65aa1fdcd6420e66ca63f44177bc78df89f912" -"checksum tokio-threadpool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "bbd8a8b911301c60cbfaa2a6588fb210e5c1038375b8bdecc47aa09a94c3c05f" -"checksum tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6131e780037787ff1b3f8aad9da83bca02438b72277850dd6ad0d455e0e20efc" -"checksum tokio-timer 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3a52f00c97fedb6d535d27f65cccb7181c8dd4c6edc3eda9ea93f6d45d05168e" -"checksum tokio-udp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "da941144b816d0dcda4db3a1ba87596e4df5e860a72b70783fe435891f80601c" -"checksum tokio-uds 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "22e3aa6d1fcc19e635418dc0a30ab5bd65d347973d6f43f1a37bf8d9d1335fc9" -"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" -"checksum toolshed 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "450441e131c7663af72e63a33c02a6a1fbaaa8601dc652ed6757813bb55aeec7" -"checksum trace-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe82f2f0bf1991e163e757baf044282823155dd326e70f44ce2186c3c320cc9" -"checksum transaction-pool 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8d8bd3123931aa6e49dd03bc8a2400490e14701d779458d1f1fff1f04c6f666" -"checksum transient-hashmap 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aeb4b191d033a35edfce392a38cdcf9790b6cebcb30fa690c312c29da4dc433e" -"checksum trezor-sys 1.0.0 (git+https://github.com/paritytech/trezor-sys)" = "" -"checksum trie-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c7319e28ca295f27359d944a682f7f65b419158bf1590c92cadc0000258d788" -"checksum trie-standardmap 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e26f52976a57a0859616d6fcec87092ac35d08eabbd78dc3dabee93b480ea5f" -"checksum triehash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d26efb4ddf87870fc08dc9a6580dc3061be350d7b9d0eb30aef1c8b4227aa46" -"checksum try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee2aa4715743892880f70885373966c83d73ef1b0838a664ef0c76fffd35e7c2" -"checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" -"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" -"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" -"checksum uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "754ba11732b9161b94c41798e5197e5e75388d012f760c42adb5000353e98646" -"checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" -"checksum unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d3218ea14b4edcaccfa0df0a64a3792a2c32cc706f1b336e48867f9d3147f90" -"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" -"checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1" -"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" -"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -"checksum untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f" -"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6" -"checksum utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd70f467df6810094968e2fce0ee1bd0e87157aceb026a8c083bcf5e25b9efe4" -"checksum validator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "236a5eda3df2c877872e98dbc55d497d943792e6405d8fc65bd4f8a5e3b53c99" -"checksum validator_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d360d6f5754972c0c1da14fb3d5580daa31aee566e1e45e2f8d3bf5950ecd3e9" -"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" -"checksum vergen 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c3365f36c57e5df714a34be40902b27a992eeddb9996eca52d0584611cf885d" -"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum walkdir 2.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "af464bc7be7b785c7ac72e266a6b67c4c9070155606f51655a650a6686204e35" -"checksum want 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a05d9d966753fa4b5c8db73fcab5eed4549cfe0e1e4e66911e5564a0085c35d1" -"checksum want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3" -"checksum wasmi 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b4a6d379e9332b1b1f52c5a87f2481c85c7c931d8ec411963dfb8f26b1ec1e3" -"checksum webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4f7e1cd7900a3a6b65a3e8780c51a3e6b59c0e2c55c6dc69578c288d69f7d082" -"checksum webpki-roots 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c10fa4212003ba19a564f25cd8ab572c6791f99a03cc219c13ed35ccab00de0e" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" -"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "afc5508759c5bf4285e61feb862b6083c8480aec864fa17a81fdec6f69b461ab" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" -"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -"checksum xdg 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a66b7c2281ebde13cf4391d70d4c7e5946c3c25e72a7b859ca8f677dcd0b0c61" -"checksum xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c1cb601d29fe2c2ac60a2b2e5e293994d87a1f6fa9687a31a15270f909be9c2" -"checksum xmltree 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9cfb54ca6b8f17d2377219ce485b134d53561b77e1393c7ea416f543a527431" + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.26", + "synstructure 0.10.1", +] diff --git a/Cargo.toml b/Cargo.toml index a783175607f..c2eccb97920 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,13 @@ [package] -description = "Parity Ethereum client" -name = "parity-ethereum" +description = "OpenEthereum" +name = "openethereum" # NOTE Make sure to update util/version/Cargo.toml as well -version = "2.5.0" +version = "3.1.0-rc1" license = "GPL-3.0" -authors = ["Parity Technologies "] +authors = [ + "OpenEthereum developers", + "Parity Technologies " +] [dependencies] blooms-db = { path = "util/blooms-db" } @@ -27,9 +30,10 @@ serde = "1.0" serde_json = "1.0" serde_derive = "1.0" futures = "0.1" +hyper = { version = "0.12" } fdlimit = "0.1" ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" } -jsonrpc-core = "10.0.1" +jsonrpc-core = "15.0.0" parity-bytes = "0.1" common-types = { path = "ethcore/types" } ethcore = { path = "ethcore", features = ["parity"] } @@ -38,28 +42,23 @@ ethcore-blockchain = { path = "ethcore/blockchain" } ethcore-call-contract = { path = "ethcore/call-contract"} ethcore-db = { path = "ethcore/db" } ethcore-io = { path = "util/io" } -ethcore-light = { path = "ethcore/light" } ethcore-logger = { path = "parity/logger" } ethcore-miner = { path = "miner" } ethcore-network = { path = "util/network" } -ethcore-private-tx = { path = "ethcore/private-tx" } ethcore-service = { path = "ethcore/service" } ethcore-sync = { path = "ethcore/sync" } ethereum-types = "0.4" ethkey = { path = "accounts/ethkey" } ethstore = { path = "accounts/ethstore" } +fetch = { path = "util/fetch" } node-filter = { path = "ethcore/node-filter" } rlp = { version = "0.3.0", features = ["ethereum"] } cli-signer= { path = "cli-signer" } parity-daemonize = "0.3" -parity-hash-fetch = { path = "updater/hash-fetch" } -parity-ipfs-api = { path = "ipfs" } parity-local-store = { path = "miner/local-store" } parity-runtime = { path = "util/runtime" } parity-rpc = { path = "rpc" } -parity-updater = { path = "updater" } parity-version = { path = "util/version" } -parity-whisper = { path = "whisper" } parity-path = "0.1" dir = { path = "util/dir" } panic_hook = { path = "util/panic-hook" } @@ -68,6 +67,8 @@ migration-rocksdb = { path = "util/migration-rocksdb" } kvdb = "0.1" kvdb-rocksdb = "0.1.3" journaldb = { path = "util/journaldb" } +stats = { path = "util/stats" } +prometheus = "0.9.0" ethcore-secretstore = { path = "secret-store", optional = true } @@ -106,22 +107,21 @@ deadlock_detection = ["parking_lot/deadlock_detection"] # `valgrind --tool=massif /path/to/parity ` # and `massif-visualizer` for visualization memory_profiling = [] -# hardcode version number 1.3.7 of parity to force an update -# in order to manually test that parity fall-over to the local version -# in case of invalid or deprecated command line arguments are entered -test-updater = ["parity-updater/test-updater"] [lib] path = "parity/lib.rs" [[bin]] path = "parity/main.rs" -name = "parity" +name = "openethereum" -[profile.dev] +[profile.test] +lto = false +opt-level = 3 # makes tests slower to compile, but faster to run [profile.release] debug = false +lto = true [workspace] # This should only list projects that are not @@ -133,8 +133,6 @@ members = [ "chainspec", "ethcore/wasm/run", "evmbin", - "parity-clib", - "whisper/cli", "util/triehash-ethereum", "util/keccak-hasher", "util/patricia-trie-ethereum", diff --git a/README.md b/README.md index 1c0302535f2..86ccc0e16d3 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,38 @@ -![Parity Ethereum](docs/logo-parity-ethereum.svg) +# OpenEthereum -

The Fastest and most Advanced Ethereum Client.

+Fast and feature-rich multi-network Ethereum client. -

» Download the latest release «

+[» Download the latest release «](https://github.com/openethereum/openethereum/releases/latest) -

-

+[![GPL licensed][license-badge]][license-url] +[![Build Status][ci-badge]][ci-url] +[![Discord chat][chat-badge]][chat-url] -**Built for mission-critical use**: Miners, service providers, and exchanges need fast synchronisation and maximum uptime. Parity Ethereum provides the core infrastructure essential for speedy and reliable services. +[license-badge]: https://img.shields.io/badge/license-GPL_v3-green.svg +[license-url]: LICENSE +[ci-badge]: https://github.com/openethereum/openethereum/workflows/Build%20and%20Test%20Suite/badge.svg +[ci-url]: https://github.com/openethereum/openethereum/actions +[chat-badge]: https://img.shields.io/discord/669192218728202270.svg?logo=discord +[chat-url]: https://discord.io/openethereum + +## Table of Contents + +1. [Description](#chapter-001) +2. [Technical Overview](#chapter-002) +3. [Building](#chapter-003)
+ 3.1 [Building Dependencies](#chapter-0031)
+ 3.2 [Building from Source Code](#chapter-0032)
+ 3.3 [Starting OpenEthereum](#chapter-0034) +4. [Testing](#chapter-004) +5. [Documentation](#chapter-005) +6. [Toolchain](#chapter-006) +7. [Contributing](#chapter-008) +8. [License](#chapter-009) + + +## 1. Description + +**Built for mission-critical use**: Miners, service providers, and exchanges need fast synchronisation and maximum uptime. OpenEthereum provides the core infrastructure essential for speedy and reliable services. - Clean, modular codebase for easy customisation - Advanced CLI-based client @@ -15,19 +40,21 @@ - Synchronise in hours, not days with Warp Sync - Modular for light integration into your service or product -## Technical Overview +## 2. Technical Overview + +OpenEthereum's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing OpenEthereum using the **Rust programming language**. OpenEthereum is licensed under the GPLv3 and can be used for all your Ethereum needs. -Parity Ethereum's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing Parity Ethereum using the sophisticated and cutting-edge **Rust programming language**. Parity Ethereum is licensed under the GPLv3 and can be used for all your Ethereum needs. +By default, OpenEthereum runs a JSON-RPC HTTP server on port `:8545` and a Web-Sockets server on port `:8546`. This is fully configurable and supports a number of APIs. -By default, Parity Ethereum runs a JSON-RPC HTTP server on port `:8545` and a Web-Sockets server on port `:8546`. This is fully configurable and supports a number of APIs. +If you run into problems while using OpenEthereum, check out the [old wiki for documentation](https://openethereum.github.io/), feel free to [file an issue in this repository](https://github.com/openethereum/openethereum/issues/new), or hop on our [Discord](https://discord.io/openethereum) chat room to ask a question. We are glad to help! -If you run into problems while using Parity Ethereum, check out the [wiki for documentation](https://wiki.parity.io/), feel free to [file an issue in this repository](https://github.com/paritytech/parity-ethereum/issues/new), or hop on our [Gitter](https://gitter.im/paritytech/parity) or [Riot](https://riot.im/app/#/group/+parity:matrix.parity.io) chat room to ask a question. We are glad to help! **For security-critical issues**, please refer to the security policy outlined in [SECURITY.md](SECURITY.md). +You can download OpenEthereum's latest release at [the releases page](https://github.com/openethereum/openethereum/releases) or follow the instructions below to build from source. Read the [CHANGELOG.md](CHANGELOG.md) for a list of all changes between different versions. -Parity Ethereum's current beta-release is 2.1. You can download it at [the releases page](https://github.com/paritytech/parity-ethereum/releases) or follow the instructions below to build from source. Please, mind the [CHANGELOG.md](CHANGELOG.md) for a list of all changes between different versions. +## 3. Building -## Build Dependencies +### 3.1 Build Dependencies -Parity Ethereum requires **latest stable Rust version** to build. +OpenEthereum requires **latest stable Rust version** to build. We recommend installing Rust through [rustup](https://www.rustup.rs/). If you don't already have `rustup`, you can install it like this: @@ -36,7 +63,7 @@ We recommend installing Rust through [rustup](https://www.rustup.rs/). If you do $ curl https://sh.rustup.rs -sSf | sh ``` - Parity Ethereum also requires `gcc`, `g++`, `libudev-dev`, `pkg-config`, `file`, `make`, and `cmake` packages to be installed. + OpenEthereum also requires `clang` (>= 9.0), `clang++`, `pkg-config`, `file`, `make`, and `cmake` packages to be installed. - OSX: ```bash @@ -45,7 +72,7 @@ We recommend installing Rust through [rustup](https://www.rustup.rs/). If you do `clang` is required. It comes with Xcode command line tools or can be installed with homebrew. -- Windows +- Windows: Make sure you have Visual Studio 2015 with C++ support installed. Next, download and run the `rustup` installer from https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe, start "VS2015 x64 Native Tools Command Prompt", and use the following command to install and set up the `msvc` toolchain: ```bash @@ -56,14 +83,14 @@ Once you have `rustup` installed, then you need to install: * [Perl](https://www.perl.org) * [Yasm](https://yasm.tortall.net) -Make sure that these binaries are in your `PATH`. After that, you should be able to build Parity Ethereum from source. +Make sure that these binaries are in your `PATH`. After that, you should be able to build OpenEthereum from source. -## Build from Source Code +### 3.2 Build from Source Code ```bash -# download Parity Ethereum code -$ git clone https://github.com/paritytech/parity-ethereum -$ cd parity-ethereum +# download OpenEthereum code +$ git clone https://github.com/openethereum/openethereum +$ cd openethereum # build in release mode $ cargo build --release --features final @@ -83,74 +110,209 @@ Note, when compiling a crate and you receive errors, it's in most cases your out $ cargo clean ``` -This always compiles the latest nightly builds. If you want to build stable or beta, do a +This always compiles the latest nightly builds. If you want to build stable, do a ```bash $ git checkout stable ``` -or +### 3.3 Starting OpenEthereum -```bash -$ git checkout beta -``` +#### Manually -## Simple One-Line Installer for Mac and Linux +To start OpenEthereum manually, just run ```bash -bash <(curl https://get.parity.io -L) +$ ./target/release/openethereum ``` -The one-line installer always defaults to the latest beta release. To install a stable release, run: +so OpenEthereum begins syncing the Ethereum blockchain. -```bash -bash <(curl https://get.parity.io -L) -r stable -``` +#### Using `systemd` service file -## Start Parity Ethereum +To start OpenEthereum as a regular user using `systemd` init: -### Manually +1. Copy `./scripts/openethereum.service` to your +`systemd` user directory (usually `~/.config/systemd/user`). +2. Copy release to bin folder, write `sudo install ./target/release/openethereum /usr/bin/openethereum` +3. To configure OpenEthereum, see [our wiki](https://openethereum.github.io/Configuring-OpenEthereum) for details. -To start Parity Ethereum manually, just run +## 4. Testing -```bash -$ ./target/release/parity -``` +Download the required test files: `git submodule update --init --recursive`. You can run tests with the following commands: + +* **All** packages + ``` + cargo test --all + ``` -so Parity Ethereum begins syncing the Ethereum blockchain. +* Specific package + ``` + cargo test --package + ``` -### Using `systemd` service file +Replace `` with one of the packages from the [package list](#package-list) (e.g. `cargo test --package evmbin`). -To start Parity Ethereum as a regular user using `systemd` init: +You can show your logs in the test output by passing `--nocapture` (i.e. `cargo test --package evmbin -- --nocapture`) -1. Copy `./scripts/parity.service` to your -`systemd` user directory (usually `~/.config/systemd/user`). -2. Copy release to bin folder, write `sudo install ./target/release/parity /usr/bin/parity` -3. To configure Parity Ethereum, write a `/etc/parity/config.toml` config file, see [Configuring Parity Ethereum](https://paritytech.github.io/wiki/Configuring-Parity) for details. +## 5. Documentation + +Be sure to [check out our wiki](https://openethereum.github.io/) for more information. + +### Viewing documentation for OpenEthereum packages + +You can generate documentation for OpenEthereum Rust packages that automatically opens in your web browser using [rustdoc with Cargo](https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html#using-rustdoc-with-cargo) (of the The Rustdoc Book), by running the the following commands: + +* **All** packages + ``` + cargo doc --document-private-items --open + ``` + +* Specific package + ``` + cargo doc --package -- --document-private-items --open + ``` + +Use`--document-private-items` to also view private documentation and `--no-deps` to exclude building documentation for dependencies. + +Replacing `` with one of the following from the details section below (i.e. `cargo doc --package openethereum --open`): + + +**Package List** +

+ +* OpenEthereum Client Application + ```bash + openethereum + ``` +* OpenEthereum Account Management, Key Management Tool, and Keys Generator + ```bash + ethcore-accounts, ethkey-cli, ethstore, ethstore-cli + ``` +* OpenEthereum Chain Specification + ```bash + chainspec + ``` +* OpenEthereum CLI Signer Tool & RPC Client + ```bash + cli-signer parity-rpc-client + ``` +* OpenEthereum Ethash & ProgPoW Implementations + ```bash + ethash + ``` +* EthCore Library + ```bash + ethcore + ``` + * OpenEthereum Blockchain Database, Test Generator, Configuration, +Caching, Importing Blocks, and Block Information + ```bash + ethcore-blockchain + ``` + * OpenEthereum Contract Calls and Blockchain Service & Registry Information + ```bash + ethcore-call-contract + ``` + * OpenEthereum Database Access & Utilities, Database Cache Manager + ```bash + ethcore-db + ``` + * OpenEthereum Virtual Machine (EVM) Rust Implementation + ```bash + evm + ``` + * OpenEthereum Light Client Implementation + ```bash + ethcore-light + ``` + * Smart Contract based Node Filter, Manage Permissions of Network Connections + ```bash + node-filter + ``` + * OpenEthereum Client & Network Service Creation & Registration with the I/O Subsystem + ```bash + ethcore-service + ``` + * OpenEthereum Blockchain Synchronization + ```bash + ethcore-sync + ``` + * OpenEthereum Common Types + ```bash + common-types + ``` + * OpenEthereum Virtual Machines (VM) Support Library + ```bash + vm + ``` + * OpenEthereum WASM Interpreter + ```bash + wasm + ``` + * OpenEthereum WASM Test Runner + ```bash + pwasm-run-test + ``` + * OpenEthereum EVM Implementation + ```bash + evmbin + ``` + * OpenEthereum JSON Deserialization + ```bash + ethjson + ``` + * OpenEthereum State Machine Generalization for Consensus Engines + ```bash + parity-machine + ``` +* OpenEthereum Miner Interface + ```bash + ethcore-miner parity-local-store price-info ethcore-stratum using_queue + ``` +* OpenEthereum Logger Implementation + ```bash + ethcore-logger + ``` +* OpenEthereum JSON-RPC Servers + ```bash + parity-rpc + ``` +* OpenEthereum Updater Service + ```bash + parity-updater parity-hash-fetch + ``` +* OpenEthereum Core Libraries (`util`) + ```bash + accounts-bloom blooms-db dir eip-712 fake-fetch fastmap fetch ethcore-io + journaldb keccak-hasher len-caching-lock memory-cache memzero + migration-rocksdb ethcore-network ethcore-network-devp2p panic_hook + patricia-trie-ethereum registrar rlp_compress stats + time-utils triehash-ethereum unexpected parity-version + ``` + +

+ +## 6. Toolchain -## Parity Ethereum toolchain +In addition to the OpenEthereum client, there are additional tools in this repository available: -In addition to the Parity Ethereum client, there are additional tools in this repository available: +- [evmbin](./evmbin) - OpenEthereum EVM Implementation. +- [ethstore](./accounts/ethstore) - OpenEthereum Key Management. +- [ethkey](./accounts/ethkey) - OpenEthereum Keys Generator. -- [evmbin](https://github.com/paritytech/parity-ethereum/blob/master/evmbin/) - EVM implementation for Parity Ethereum. -- [ethabi](https://github.com/paritytech/ethabi) - Parity Ethereum function calls encoding. -- [ethstore](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethstore) - Parity Ethereum key management. -- [ethkey](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethkey) - Parity Ethereum keys generator. -- [whisper](https://github.com/paritytech/parity-ethereum/blob/master/whisper/) - Implementation of Whisper-v2 PoC. +The following tools are available in a separate repository: +- [ethabi](https://github.com/openethereum/ethabi) - OpenEthereum Encoding of Function Calls. [Docs here](https://crates.io/crates/ethabi) +- [whisper](https://github.com/openethereum/whisper) - OpenEthereum Whisper-v2 PoC Implementation. -## Join the chat! +## 7. Contributing -Questions? Get in touch with us on Gitter: -[![Gitter: Parity](https://img.shields.io/badge/gitter-parity-4AB495.svg)](https://gitter.im/paritytech/parity) -[![Gitter: Parity.js](https://img.shields.io/badge/gitter-parity.js-4AB495.svg)](https://gitter.im/paritytech/parity.js) -[![Gitter: Parity/Miners](https://img.shields.io/badge/gitter-parity/miners-4AB495.svg)](https://gitter.im/paritytech/parity/miners) -[![Gitter: Parity-PoA](https://img.shields.io/badge/gitter-parity--poa-4AB495.svg)](https://gitter.im/paritytech/parity-poa) +An introduction has been provided in the ["So You Want to be a Core Developer" presentation slides by Hernando Castano](http://tiny.cc/contrib-to-parity-eth). Additional guidelines are provided in [CONTRIBUTING](./.github/CONTRIBUTING.md). -Alternatively, join our community on Matrix: -[![Riot: +Parity](https://img.shields.io/badge/riot-%2Bparity%3Amatrix.parity.io-orange.svg)](https://riot.im/app/#/group/+parity:matrix.parity.io) +### Contributor Code of Conduct -## Documentation +[CODE_OF_CONDUCT](./.github/CODE_OF_CONDUCT.md) -Official website: https://parity.io +## 8. License -Be sure to [check out our wiki](https://wiki.parity.io) for more information. +[LICENSE](./LICENSE) diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 3f977bd1b0a..00000000000 --- a/SECURITY.md +++ /dev/null @@ -1,80 +0,0 @@ -# Security Policy - -Parity Technologies is committed to resolving security vulnerabilities in our software quickly and carefully. We take the necessary steps to minimize risk, provide timely information, and deliver vulnerability fixes and mitigations required to address security issues. - -## Reporting a Vulnerability - -Security vulnerabilities in Parity software should be reported by email to security@parity.io. If you think your report might be eligible for the Parity Bug Bounty Program, your email should be send to bugbounty@parity.io. - -Your report should include the following: - -- your name -- description of the vulnerability -- attack scenario (if any) -- components -- reproduction -- other details - -Try to include as much information in your report as you can, including a description of the vulnerability, its potential impact, and steps for reproducing it. Be sure to use a descriptive subject line. - -You'll receive a response to your email within two business days indicating the next steps in handling your report. We encourage finders to use encrypted communication channels to protect the confidentiality of vulnerability reports. You can encrypt your report using our public key. This key is [on MIT's key server](https://pgp.mit.edu/pks/lookup?op=get&search=0x5D0F03018D07DE73) server and reproduced below. - -After the initial reply to your report, our team will endeavor to keep you informed of the progress being made towards a fix. These updates will be sent at least every five business days. - -Thank you for taking the time to responsibly disclose any vulnerabilities you find. - -## Responsible Investigation and Reporting - -Responsible investigation and reporting includes, but isn't limited to, the following: - -- Don't violate the privacy of other users, destroy data, etc. -- Don’t defraud or harm Parity Technologies Ltd or its users during your research; you should make a good faith effort to not interrupt or degrade our services. -- Don't target our physical security measures, or attempt to use social engineering, spam, distributed denial of service (DDOS) attacks, etc. -- Initially report the bug only to us and not to anyone else. -- Give us a reasonable amount of time to fix the bug before disclosing it to anyone else, and give us adequate written warning before disclosing it to anyone else. -- In general, please investigate and report bugs in a way that makes a reasonable, good faith effort not to be disruptive or harmful to us or our users. Otherwise your actions might be interpreted as an attack rather than an effort to be helpful. - -## Bug Bounty Program - -Our Bug Bounty Program allows us to recognise and reward members of the Parity community for helping us find and address significant bugs, in accordance with the terms of the Parity Bug Bounty Program. A detailed description on eligibility, rewards, legal information and terms & conditions for contributors can be found on [our website](https://paritytech.io/bug-bounty.html). - - - - - - -## Plaintext PGP Key - -``` ------BEGIN PGP PUBLIC KEY BLOCK----- - -mQENBFlyIAwBCACe0keNPjgYzZ1Oy/8t3zj/Qw9bHHqrzx7FWy8NbXnYBM19NqOZ -DIP7Oe0DvCaf/uruBskCS0iVstHlEFQ2AYe0Ei0REt9lQdy61GylU/DEB3879IG+ -6FO0SnFeYeerv1/hFI2K6uv8v7PyyVDiiJSW0I1KIs2OBwJicTKmWxLAeQsRgx9G -yRGalrVk4KP+6pWTA7k3DxmDZKZyfYV/Ej10NtuzmsemwDbv98HKeomp/kgFOfSy -3AZjeCpctlsNqpjUuXa0/HudmH2WLxZ0fz8XeoRh8XM9UudNIecjrDqmAFrt/btQ -/3guvlzhFCdhYPVGsUusKMECk/JG+Xx1/1ZjABEBAAG0LFBhcml0eSBTZWN1cml0 -eSBDb250YWN0IDxzZWN1cml0eUBwYXJpdHkuaW8+iQFUBBMBCAA+FiEE2uUVYCjP -N6B8aTiDXQ8DAY0H3nMFAllyIAwCGwMFCQPCZwAFCwkIBwIGFQgJCgsCBBYCAwEC -HgECF4AACgkQXQ8DAY0H3nM60wgAkS3A36Zc+upiaxU7tumcGv+an17j7gin0sif -+0ELSjVfrXInM6ovai+NhUdcLkJ7tCrKS90fvlaELK5Sg9CXBWCTFccKN4A/B7ey -rOg2NPXUecnyBB/XqQgKYH7ujYlOlqBDXMfz6z8Hj6WToxg9PPMGGomyMGh8AWxM -3yRPFs5RKt0VKgN++5N00oly5Y8ri5pgCidDvCLYMGTVDHFKwkuc9w6BlWlu1R1e -/hXFWUFAP1ffTAul3QwyKhjPn2iotCdxXjvt48KaU8DN4iL7aMBN/ZBKqGS7yRdF -D/JbJyaaJ0ZRvFSTSXy/sWY3z1B5mtCPBxco8hqqNfRkCwuZ6LkBDQRZciAMAQgA -8BP8xrwe12TOUTqL/Vrbxv/FLdhKh53J6TrPKvC2TEEKOrTNo5ahRq+XOS5E7G2N -x3b+fq8gR9BzFcldAx0XWUtGs/Wv++ulaSNqTBxj13J3G3WGsUfMKxRgj//piCUD -bCFLQfGZdKk0M1o9QkPVARwwmvCNiNB/l++xGqPtfc44H5jWj3GoGvL2MkShPzrN -yN/bJ+m+R5gtFGdInqa5KXBuxxuW25eDKJ+LzjbgUgeC76wNcfOiQHTdMkcupjdO -bbGFwo10hcbRAOcZEv6//Zrlmk/6nPxEd2hN20St2bSN0+FqfZ267mWEu3ejsgF8 -ArdCpv5h4fBvJyNwiTZwIQARAQABiQE8BBgBCAAmFiEE2uUVYCjPN6B8aTiDXQ8D -AY0H3nMFAllyIAwCGwwFCQPCZwAACgkQXQ8DAY0H3nNisggAl4fqhRlA34wIb190 -sqXHVxiCuzPaqS6krE9xAa1+gncX485OtcJNqnjugHm2rFE48lv7oasviuPXuInE -/OgVFnXYv9d/Xx2JUeDs+bFTLouCDRY2Unh7KJZasfqnMcCHWcxHx5FvRNZRssaB -WTZVo6sizPurGUtbpYe4/OLFhadBqAE0EUmVRFEUMc1YTnu4eLaRBzoWN4d2UWwi -LN25RSrVSke7LTSFbgn9ntQrQ2smXSR+cdNkkfRCjFcpUaecvFl9HwIqoyVbT4Ym -0hbpbbX/cJdc91tKa+psa29uMeGL/cgL9fAu19yNFRyOTMxjZnvql1X/WE1pLmoP -ETBD1Q== -=K9Qw ------END PGP PUBLIC KEY BLOCK----- -``` diff --git a/accounts/Cargo.toml b/accounts/Cargo.toml index 072593cd104..e88d77683a3 100644 --- a/accounts/Cargo.toml +++ b/accounts/Cargo.toml @@ -1,6 +1,6 @@ [package] -description = "Account management for Parity Ethereum" -homepage = "http://parity.io" +description = "OpenEthereum Account Management" +homepage = "https://github.com/openethereum/openethereum" license = "GPL-3.0" name = "ethcore-accounts" version = "0.1.0" @@ -17,12 +17,6 @@ serde = "1.0" serde_derive = "1.0" serde_json = "1.0" -[target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))'.dependencies] -hardware-wallet = { path = "hw" } - -[target.'cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))'.dependencies] -fake-hardware-wallet = { path = "fake-hardware-wallet" } - [dev-dependencies] ethereum-types = "0.4" tempdir = "0.3" diff --git a/accounts/ethkey/Cargo.toml b/accounts/ethkey/Cargo.toml index ec784bd11d7..e432a118203 100644 --- a/accounts/ethkey/Cargo.toml +++ b/accounts/ethkey/Cargo.toml @@ -1,18 +1,18 @@ [package] +description = "Parity Ethereum Keys Generator" name = "ethkey" version = "0.3.0" authors = ["Parity Technologies "] [dependencies] -byteorder = "1.0" edit-distance = "2.0" parity-crypto = "0.3.0" -eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" } +eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1", rev = "ccc06e7480148b723eb44ac56cf4d20eec380b6f" } ethereum-types = "0.4" lazy_static = "1.0" log = "0.4" memzero = { path = "../../util/memzero" } -parity-wordlist = "1.2" +parity-wordlist = "1.3" quick-error = "1.2.2" rand = "0.4" rustc-hex = "1.0" diff --git a/accounts/ethkey/README.md b/accounts/ethkey/README.md index 6fc98b020d6..f7abe92861d 100644 --- a/accounts/ethkey/README.md +++ b/accounts/ethkey/README.md @@ -5,7 +5,7 @@ Parity Ethereum keys generator. ### Usage ``` -Parity Ethereum keys generator. +Parity Ethereum Keys Generator. Copyright 2015-2019 Parity Technologies (UK) Ltd. Usage: @@ -218,4 +218,3 @@ _This project is a part of the Parity Ethereum toolchain._ - [ethabi](https://github.com/paritytech/ethabi) - Parity Ethereum function calls encoding. - [ethstore](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethstore) - Parity Ethereum key management. - [ethkey](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethkey) - Parity Ethereum keys generator. -- [whisper](https://github.com/paritytech/parity-ethereum/blob/master/whisper/) - Implementation of Whisper-v2 PoC. diff --git a/accounts/ethkey/cli/Cargo.toml b/accounts/ethkey/cli/Cargo.toml index d43aa2c5c0a..c1d44897cc6 100644 --- a/accounts/ethkey/cli/Cargo.toml +++ b/accounts/ethkey/cli/Cargo.toml @@ -1,4 +1,5 @@ [package] +description = "Parity Ethereum Keys Generator CLI" name = "ethkey-cli" version = "0.1.0" authors = ["Parity Technologies "] @@ -8,7 +9,7 @@ docopt = "1.0" env_logger = "0.5" ethkey = { path = "../" } panic_hook = { path = "../../../util/panic-hook" } -parity-wordlist="1.2" +parity-wordlist="1.3" rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" diff --git a/accounts/ethkey/cli/src/main.rs b/accounts/ethkey/cli/src/main.rs index 759f5f484c1..7327553e194 100644 --- a/accounts/ethkey/cli/src/main.rs +++ b/accounts/ethkey/cli/src/main.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . extern crate docopt; extern crate env_logger; @@ -26,11 +26,13 @@ extern crate threadpool; #[macro_use] extern crate serde_derive; -use std::num::ParseIntError; -use std::{env, fmt, process, io, sync}; +use std::{env, fmt, io, num::ParseIntError, process, sync}; use docopt::Docopt; -use ethkey::{KeyPair, Random, Brain, BrainPrefix, Prefix, Error as EthkeyError, Generator, sign, verify_public, verify_address, brain_recover}; +use ethkey::{ + brain_recover, sign, verify_address, verify_public, Brain, BrainPrefix, Error as EthkeyError, + Generator, KeyPair, Prefix, Random, +}; use rustc_hex::{FromHex, FromHexError}; const USAGE: &'static str = r#" @@ -65,387 +67,428 @@ Commands: #[derive(Debug, Deserialize)] struct Args { - cmd_info: bool, - cmd_generate: bool, - cmd_random: bool, - cmd_prefix: bool, - cmd_sign: bool, - cmd_verify: bool, - cmd_public: bool, - cmd_address: bool, - cmd_recover: bool, - arg_prefix: String, - arg_secret: String, - arg_secret_or_phrase: String, - arg_known_phrase: String, - arg_message: String, - arg_public: String, - arg_address: String, - arg_signature: String, - flag_secret: bool, - flag_public: bool, - flag_address: bool, - flag_brain: bool, + cmd_info: bool, + cmd_generate: bool, + cmd_random: bool, + cmd_prefix: bool, + cmd_sign: bool, + cmd_verify: bool, + cmd_public: bool, + cmd_address: bool, + cmd_recover: bool, + arg_prefix: String, + arg_secret: String, + arg_secret_or_phrase: String, + arg_known_phrase: String, + arg_message: String, + arg_public: String, + arg_address: String, + arg_signature: String, + flag_secret: bool, + flag_public: bool, + flag_address: bool, + flag_brain: bool, } #[derive(Debug)] enum Error { - Ethkey(EthkeyError), - FromHex(FromHexError), - ParseInt(ParseIntError), - Docopt(docopt::Error), - Io(io::Error), + Ethkey(EthkeyError), + FromHex(FromHexError), + ParseInt(ParseIntError), + Docopt(docopt::Error), + Io(io::Error), } impl From for Error { - fn from(err: EthkeyError) -> Self { - Error::Ethkey(err) - } + fn from(err: EthkeyError) -> Self { + Error::Ethkey(err) + } } impl From for Error { - fn from(err: FromHexError) -> Self { - Error::FromHex(err) - } + fn from(err: FromHexError) -> Self { + Error::FromHex(err) + } } impl From for Error { - fn from(err: ParseIntError) -> Self { - Error::ParseInt(err) - } + fn from(err: ParseIntError) -> Self { + Error::ParseInt(err) + } } impl From for Error { - fn from(err: docopt::Error) -> Self { - Error::Docopt(err) - } + fn from(err: docopt::Error) -> Self { + Error::Docopt(err) + } } impl From for Error { - fn from(err: io::Error) -> Self { - Error::Io(err) - } + fn from(err: io::Error) -> Self { + Error::Io(err) + } } impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match *self { - Error::Ethkey(ref e) => write!(f, "{}", e), - Error::FromHex(ref e) => write!(f, "{}", e), - Error::ParseInt(ref e) => write!(f, "{}", e), - Error::Docopt(ref e) => write!(f, "{}", e), - Error::Io(ref e) => write!(f, "{}", e), - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + Error::Ethkey(ref e) => write!(f, "{}", e), + Error::FromHex(ref e) => write!(f, "{}", e), + Error::ParseInt(ref e) => write!(f, "{}", e), + Error::Docopt(ref e) => write!(f, "{}", e), + Error::Io(ref e) => write!(f, "{}", e), + } + } } enum DisplayMode { - KeyPair, - Secret, - Public, - Address, + KeyPair, + Secret, + Public, + Address, } impl DisplayMode { - fn new(args: &Args) -> Self { - if args.flag_secret { - DisplayMode::Secret - } else if args.flag_public { - DisplayMode::Public - } else if args.flag_address { - DisplayMode::Address - } else { - DisplayMode::KeyPair - } - } + fn new(args: &Args) -> Self { + if args.flag_secret { + DisplayMode::Secret + } else if args.flag_public { + DisplayMode::Public + } else if args.flag_address { + DisplayMode::Address + } else { + DisplayMode::KeyPair + } + } } fn main() { - panic_hook::set_abort(); - env_logger::try_init().expect("Logger initialized only once."); - - match execute(env::args()) { - Ok(ok) => println!("{}", ok), - Err(Error::Docopt(ref e)) => e.exit(), - Err(err) => { - eprintln!("{}", err); - process::exit(1); - } - } + panic_hook::set_abort(); + env_logger::try_init().expect("Logger initialized only once."); + + match execute(env::args()) { + Ok(ok) => println!("{}", ok), + Err(Error::Docopt(ref e)) => e.exit(), + Err(err) => { + eprintln!("{}", err); + process::exit(1); + } + } } fn display(result: (KeyPair, Option), mode: DisplayMode) -> String { - let keypair = result.0; - match mode { - DisplayMode::KeyPair => match result.1 { - Some(extra_data) => format!("{}\n{}", extra_data, keypair), - None => format!("{}", keypair) - }, - DisplayMode::Secret => format!("{:x}", keypair.secret()), - DisplayMode::Public => format!("{:x}", keypair.public()), - DisplayMode::Address => format!("{:x}", keypair.address()), - } + let keypair = result.0; + match mode { + DisplayMode::KeyPair => match result.1 { + Some(extra_data) => format!("{}\n{}", extra_data, keypair), + None => format!("{}", keypair), + }, + DisplayMode::Secret => format!("{:x}", keypair.secret()), + DisplayMode::Public => format!("{:x}", keypair.public()), + DisplayMode::Address => format!("{:x}", keypair.address()), + } } -fn execute(command: I) -> Result where I: IntoIterator, S: AsRef { - let args: Args = Docopt::new(USAGE) - .and_then(|d| d.argv(command).deserialize())?; - - return if args.cmd_info { - let display_mode = DisplayMode::new(&args); - - let result = if args.flag_brain { - let phrase = args.arg_secret_or_phrase; - let phrase_info = validate_phrase(&phrase); - let keypair = Brain::new(phrase).generate().expect("Brain wallet generator is infallible; qed"); - (keypair, Some(phrase_info)) - } else { - let secret = args.arg_secret_or_phrase.parse().map_err(|_| EthkeyError::InvalidSecret)?; - (KeyPair::from_secret(secret)?, None) - }; - Ok(display(result, display_mode)) - } else if args.cmd_generate { - let display_mode = DisplayMode::new(&args); - let result = if args.cmd_random { - if args.flag_brain { - let mut brain = BrainPrefix::new(vec![0], usize::max_value(), BRAIN_WORDS); - let keypair = brain.generate()?; - let phrase = format!("recovery phrase: {}", brain.phrase()); - (keypair, Some(phrase)) - } else { - (Random.generate()?, None) - } - } else if args.cmd_prefix { - let prefix = args.arg_prefix.from_hex()?; - let brain = args.flag_brain; - in_threads(move || { - let iterations = 1024; - let prefix = prefix.clone(); - move || { - let prefix = prefix.clone(); - let res = if brain { - let mut brain = BrainPrefix::new(prefix, iterations, BRAIN_WORDS); - let result = brain.generate(); - let phrase = format!("recovery phrase: {}", brain.phrase()); - result.map(|keypair| (keypair, Some(phrase))) - } else { - let result = Prefix::new(prefix, iterations).generate(); - result.map(|res| (res, None)) - }; - - Ok(res.map(Some).unwrap_or(None)) - } - })? - } else { - return Ok(format!("{}", USAGE)) - }; - Ok(display(result, display_mode)) - } else if args.cmd_sign { - let secret = args.arg_secret.parse().map_err(|_| EthkeyError::InvalidSecret)?; - let message = args.arg_message.parse().map_err(|_| EthkeyError::InvalidMessage)?; - let signature = sign(&secret, &message)?; - Ok(format!("{}", signature)) - } else if args.cmd_verify { - let signature = args.arg_signature.parse().map_err(|_| EthkeyError::InvalidSignature)?; - let message = args.arg_message.parse().map_err(|_| EthkeyError::InvalidMessage)?; - let ok = if args.cmd_public { - let public = args.arg_public.parse().map_err(|_| EthkeyError::InvalidPublic)?; - verify_public(&public, &signature, &message)? - } else if args.cmd_address { - let address = args.arg_address.parse().map_err(|_| EthkeyError::InvalidAddress)?; - verify_address(&address, &signature, &message)? - } else { - return Ok(format!("{}", USAGE)) - }; - Ok(format!("{}", ok)) - } else if args.cmd_recover { - let display_mode = DisplayMode::new(&args); - let known_phrase = args.arg_known_phrase; - let address = args.arg_address.parse().map_err(|_| EthkeyError::InvalidAddress)?; - let (phrase, keypair) = in_threads(move || { - let mut it = brain_recover::PhrasesIterator::from_known_phrase(&known_phrase, BRAIN_WORDS); - move || { - let mut i = 0; - while let Some(phrase) = it.next() { - i += 1; - - let keypair = Brain::new(phrase.clone()).generate().unwrap(); - if keypair.address() == address { - return Ok(Some((phrase, keypair))) - } - - if i >= 1024 { - return Ok(None) - } - } - - Err(EthkeyError::Custom("Couldn't find any results.".into())) - } - })?; - Ok(display((keypair, Some(phrase)), display_mode)) - } else { - Ok(format!("{}", USAGE)) - } +fn execute(command: I) -> Result +where + I: IntoIterator, + S: AsRef, +{ + let args: Args = Docopt::new(USAGE).and_then(|d| d.argv(command).deserialize())?; + + return if args.cmd_info { + let display_mode = DisplayMode::new(&args); + + let result = if args.flag_brain { + let phrase = args.arg_secret_or_phrase; + let phrase_info = validate_phrase(&phrase); + let keypair = Brain::new(phrase) + .generate() + .expect("Brain wallet generator is infallible; qed"); + (keypair, Some(phrase_info)) + } else { + let secret = args + .arg_secret_or_phrase + .parse() + .map_err(|_| EthkeyError::InvalidSecret)?; + (KeyPair::from_secret(secret)?, None) + }; + Ok(display(result, display_mode)) + } else if args.cmd_generate { + let display_mode = DisplayMode::new(&args); + let result = if args.cmd_random { + if args.flag_brain { + let mut brain = BrainPrefix::new(vec![0], usize::max_value(), BRAIN_WORDS); + let keypair = brain.generate()?; + let phrase = format!("recovery phrase: {}", brain.phrase()); + (keypair, Some(phrase)) + } else { + (Random.generate()?, None) + } + } else if args.cmd_prefix { + let prefix = args.arg_prefix.from_hex()?; + let brain = args.flag_brain; + in_threads(move || { + let iterations = 1024; + let prefix = prefix.clone(); + move || { + let prefix = prefix.clone(); + let res = if brain { + let mut brain = BrainPrefix::new(prefix, iterations, BRAIN_WORDS); + let result = brain.generate(); + let phrase = format!("recovery phrase: {}", brain.phrase()); + result.map(|keypair| (keypair, Some(phrase))) + } else { + let result = Prefix::new(prefix, iterations).generate(); + result.map(|res| (res, None)) + }; + + Ok(res.map(Some).unwrap_or(None)) + } + })? + } else { + return Ok(format!("{}", USAGE)); + }; + Ok(display(result, display_mode)) + } else if args.cmd_sign { + let secret = args + .arg_secret + .parse() + .map_err(|_| EthkeyError::InvalidSecret)?; + let message = args + .arg_message + .parse() + .map_err(|_| EthkeyError::InvalidMessage)?; + let signature = sign(&secret, &message)?; + Ok(format!("{}", signature)) + } else if args.cmd_verify { + let signature = args + .arg_signature + .parse() + .map_err(|_| EthkeyError::InvalidSignature)?; + let message = args + .arg_message + .parse() + .map_err(|_| EthkeyError::InvalidMessage)?; + let ok = if args.cmd_public { + let public = args + .arg_public + .parse() + .map_err(|_| EthkeyError::InvalidPublic)?; + verify_public(&public, &signature, &message)? + } else if args.cmd_address { + let address = args + .arg_address + .parse() + .map_err(|_| EthkeyError::InvalidAddress)?; + verify_address(&address, &signature, &message)? + } else { + return Ok(format!("{}", USAGE)); + }; + Ok(format!("{}", ok)) + } else if args.cmd_recover { + let display_mode = DisplayMode::new(&args); + let known_phrase = args.arg_known_phrase; + let address = args + .arg_address + .parse() + .map_err(|_| EthkeyError::InvalidAddress)?; + let (phrase, keypair) = in_threads(move || { + let mut it = + brain_recover::PhrasesIterator::from_known_phrase(&known_phrase, BRAIN_WORDS); + move || { + let mut i = 0; + while let Some(phrase) = it.next() { + i += 1; + + let keypair = Brain::new(phrase.clone()).generate().unwrap(); + if keypair.address() == address { + return Ok(Some((phrase, keypair))); + } + + if i >= 1024 { + return Ok(None); + } + } + + Err(EthkeyError::Custom("Couldn't find any results.".into())) + } + })?; + Ok(display((keypair, Some(phrase)), display_mode)) + } else { + Ok(format!("{}", USAGE)) + }; } const BRAIN_WORDS: usize = 12; fn validate_phrase(phrase: &str) -> String { - match Brain::validate_phrase(phrase, BRAIN_WORDS) { - Ok(()) => format!("The recovery phrase looks correct.\n"), - Err(err) => format!("The recover phrase was not generated by Parity: {}", err) - } + match Brain::validate_phrase(phrase, BRAIN_WORDS) { + Ok(()) => format!("The recovery phrase looks correct.\n"), + Err(err) => format!("The recover phrase was not generated by Parity: {}", err), + } } -fn in_threads(prepare: F) -> Result where - O: Send + 'static, - X: Send + 'static, - F: Fn() -> X, - X: FnMut() -> Result, EthkeyError>, +fn in_threads(prepare: F) -> Result +where + O: Send + 'static, + X: Send + 'static, + F: Fn() -> X, + X: FnMut() -> Result, EthkeyError>, { - let pool = threadpool::Builder::new().build(); - - let (tx, rx) = sync::mpsc::sync_channel(1); - let is_done = sync::Arc::new(sync::atomic::AtomicBool::default()); - - for _ in 0..pool.max_count() { - let is_done = is_done.clone(); - let tx = tx.clone(); - let mut task = prepare(); - pool.execute(move || { - loop { - if is_done.load(sync::atomic::Ordering::SeqCst) { - return; - } - - let res = match task() { - Ok(None) => continue, - Ok(Some(v)) => Ok(v), - Err(err) => Err(err), - }; - - // We are interested only in the first response. - let _ = tx.send(res); - } - }); - } - - if let Ok(solution) = rx.recv() { - is_done.store(true, sync::atomic::Ordering::SeqCst); - return solution; - } - - Err(EthkeyError::Custom("No results found.".into())) + let pool = threadpool::Builder::new().build(); + + let (tx, rx) = sync::mpsc::sync_channel(1); + let is_done = sync::Arc::new(sync::atomic::AtomicBool::default()); + + for _ in 0..pool.max_count() { + let is_done = is_done.clone(); + let tx = tx.clone(); + let mut task = prepare(); + pool.execute(move || { + loop { + if is_done.load(sync::atomic::Ordering::SeqCst) { + return; + } + + let res = match task() { + Ok(None) => continue, + Ok(Some(v)) => Ok(v), + Err(err) => Err(err), + }; + + // We are interested only in the first response. + let _ = tx.send(res); + } + }); + } + + if let Ok(solution) = rx.recv() { + is_done.store(true, sync::atomic::Ordering::SeqCst); + return solution; + } + + Err(EthkeyError::Custom("No results found.".into())) } #[cfg(test)] mod tests { - use super::execute; - - #[test] - fn info() { - let command = vec!["ethkey", "info", "17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55"] - .into_iter() - .map(Into::into) - .collect::>(); - - let expected = + use super::execute; + + #[test] + fn info() { + let command = vec![ + "ethkey", + "info", + "17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55", + ] + .into_iter() + .map(Into::into) + .collect::>(); + + let expected = "secret: 17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55 public: 689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124 address: 26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5".to_owned(); - assert_eq!(execute(command).unwrap(), expected); - } + assert_eq!(execute(command).unwrap(), expected); + } - #[test] - fn brain() { - let command = vec!["ethkey", "info", "--brain", "this is sparta"] - .into_iter() - .map(Into::into) - .collect::>(); + #[test] + fn brain() { + let command = vec!["ethkey", "info", "--brain", "this is sparta"] + .into_iter() + .map(Into::into) + .collect::>(); - let expected = + let expected = "The recover phrase was not generated by Parity: The word 'this' does not come from the dictionary. secret: aa22b54c0cb43ee30a014afe5ef3664b1cde299feabca46cd3167a85a57c39f2 public: c4c5398da6843632c123f543d714d2d2277716c11ff612b2a2f23c6bda4d6f0327c31cd58c55a9572c3cc141dade0c32747a13b7ef34c241b26c84adbb28fcf4 address: 006e27b6a72e1f34c626762f3c4761547aff1421".to_owned(); - assert_eq!(execute(command).unwrap(), expected); - } - - #[test] - fn secret() { - let command = vec!["ethkey", "info", "--brain", "this is sparta", "--secret"] - .into_iter() - .map(Into::into) - .collect::>(); - - let expected = "aa22b54c0cb43ee30a014afe5ef3664b1cde299feabca46cd3167a85a57c39f2".to_owned(); - assert_eq!(execute(command).unwrap(), expected); - } - - #[test] - fn public() { - let command = vec!["ethkey", "info", "--brain", "this is sparta", "--public"] - .into_iter() - .map(Into::into) - .collect::>(); - - let expected = "c4c5398da6843632c123f543d714d2d2277716c11ff612b2a2f23c6bda4d6f0327c31cd58c55a9572c3cc141dade0c32747a13b7ef34c241b26c84adbb28fcf4".to_owned(); - assert_eq!(execute(command).unwrap(), expected); - } - - #[test] - fn address() { - let command = vec!["ethkey", "info", "-b", "this is sparta", "--address"] - .into_iter() - .map(Into::into) - .collect::>(); - - let expected = "006e27b6a72e1f34c626762f3c4761547aff1421".to_owned(); - assert_eq!(execute(command).unwrap(), expected); - } - - #[test] - fn sign() { - let command = vec!["ethkey", "sign", "17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987"] - .into_iter() - .map(Into::into) - .collect::>(); - - let expected = "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200".to_owned(); - assert_eq!(execute(command).unwrap(), expected); - } - - #[test] - fn verify_valid_public() { - let command = vec!["ethkey", "verify", "public", "689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987"] + assert_eq!(execute(command).unwrap(), expected); + } + + #[test] + fn secret() { + let command = vec!["ethkey", "info", "--brain", "this is sparta", "--secret"] + .into_iter() + .map(Into::into) + .collect::>(); + + let expected = + "aa22b54c0cb43ee30a014afe5ef3664b1cde299feabca46cd3167a85a57c39f2".to_owned(); + assert_eq!(execute(command).unwrap(), expected); + } + + #[test] + fn public() { + let command = vec!["ethkey", "info", "--brain", "this is sparta", "--public"] + .into_iter() + .map(Into::into) + .collect::>(); + + let expected = "c4c5398da6843632c123f543d714d2d2277716c11ff612b2a2f23c6bda4d6f0327c31cd58c55a9572c3cc141dade0c32747a13b7ef34c241b26c84adbb28fcf4".to_owned(); + assert_eq!(execute(command).unwrap(), expected); + } + + #[test] + fn address() { + let command = vec!["ethkey", "info", "-b", "this is sparta", "--address"] + .into_iter() + .map(Into::into) + .collect::>(); + + let expected = "006e27b6a72e1f34c626762f3c4761547aff1421".to_owned(); + assert_eq!(execute(command).unwrap(), expected); + } + + #[test] + fn sign() { + let command = vec![ + "ethkey", + "sign", + "17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55", + "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987", + ] + .into_iter() + .map(Into::into) + .collect::>(); + + let expected = "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200".to_owned(); + assert_eq!(execute(command).unwrap(), expected); + } + + #[test] + fn verify_valid_public() { + let command = vec!["ethkey", "verify", "public", "689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987"] .into_iter() .map(Into::into) .collect::>(); - let expected = "true".to_owned(); - assert_eq!(execute(command).unwrap(), expected); - } + let expected = "true".to_owned(); + assert_eq!(execute(command).unwrap(), expected); + } - #[test] - fn verify_valid_address() { - let command = vec!["ethkey", "verify", "address", "26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987"] + #[test] + fn verify_valid_address() { + let command = vec!["ethkey", "verify", "address", "26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987"] .into_iter() .map(Into::into) .collect::>(); - let expected = "true".to_owned(); - assert_eq!(execute(command).unwrap(), expected); - } + let expected = "true".to_owned(); + assert_eq!(execute(command).unwrap(), expected); + } - #[test] - fn verify_invalid() { - let command = vec!["ethkey", "verify", "public", "689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec986"] + #[test] + fn verify_invalid() { + let command = vec!["ethkey", "verify", "public", "689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec986"] .into_iter() .map(Into::into) .collect::>(); - let expected = "false".to_owned(); - assert_eq!(execute(command).unwrap(), expected); - } + let expected = "false".to_owned(); + assert_eq!(execute(command).unwrap(), expected); + } } diff --git a/accounts/ethkey/src/brain.rs b/accounts/ethkey/src/brain.rs index 3a970e17c8d..9d835f1180a 100644 --- a/accounts/ethkey/src/brain.rs +++ b/accounts/ethkey/src/brain.rs @@ -1,73 +1,74 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . +use super::{Generator, KeyPair, Secret}; use keccak::Keccak256; -use super::{KeyPair, Generator, Secret}; use parity_wordlist; /// Simple brainwallet. pub struct Brain(String); impl Brain { - pub fn new(s: String) -> Self { - Brain(s) - } + pub fn new(s: String) -> Self { + Brain(s) + } - pub fn validate_phrase(phrase: &str, expected_words: usize) -> Result<(), ::WordlistError> { - parity_wordlist::validate_phrase(phrase, expected_words) - } + pub fn validate_phrase(phrase: &str, expected_words: usize) -> Result<(), ::WordlistError> { + parity_wordlist::validate_phrase(phrase, expected_words) + } } impl Generator for Brain { type Error = ::Void; - fn generate(&mut self) -> Result { - let seed = self.0.clone(); - let mut secret = seed.into_bytes().keccak256(); + fn generate(&mut self) -> Result { + let seed = self.0.clone(); + let mut secret = seed.into_bytes().keccak256(); - let mut i = 0; - loop { - secret = secret.keccak256(); + let mut i = 0; + loop { + secret = secret.keccak256(); - match i > 16384 { - false => i += 1, - true => { - if let Ok(pair) = Secret::from_unsafe_slice(&secret) - .and_then(KeyPair::from_secret) - { - if pair.address()[0] == 0 { - trace!("Testing: {}, got: {:?}", self.0, pair.address()); - return Ok(pair) - } - } - }, - } - } - } + match i > 16384 { + false => i += 1, + true => { + if let Ok(pair) = + Secret::from_unsafe_slice(&secret).and_then(KeyPair::from_secret) + { + if pair.address()[0] == 0 { + trace!("Testing: {}, got: {:?}", self.0, pair.address()); + return Ok(pair); + } + } + } + } + } + } } #[cfg(test)] mod tests { - use {Brain, Generator}; + use Brain; + use Generator; - #[test] - fn test_brain() { - let words = "this is sparta!".to_owned(); - let first_keypair = Brain::new(words.clone()).generate().unwrap(); - let second_keypair = Brain::new(words.clone()).generate().unwrap(); - assert_eq!(first_keypair.secret(), second_keypair.secret()); - } + #[test] + fn test_brain() { + let words = "this is sparta!".to_owned(); + let first_keypair = Brain::new(words.clone()).generate().unwrap(); + let second_keypair = Brain::new(words.clone()).generate().unwrap(); + assert_eq!(first_keypair.secret(), second_keypair.secret()); + } } diff --git a/accounts/ethkey/src/brain_prefix.rs b/accounts/ethkey/src/brain_prefix.rs index ba0d8129615..cc25a03f21c 100644 --- a/accounts/ethkey/src/brain_prefix.rs +++ b/accounts/ethkey/src/brain_prefix.rs @@ -1,70 +1,73 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use super::{Generator, KeyPair, Error, Brain}; +use super::{Brain, Error, Generator, KeyPair}; use parity_wordlist as wordlist; /// Tries to find brain-seed keypair with address starting with given prefix. pub struct BrainPrefix { - prefix: Vec, - iterations: usize, - no_of_words: usize, - last_phrase: String, + prefix: Vec, + iterations: usize, + no_of_words: usize, + last_phrase: String, } impl BrainPrefix { - pub fn new(prefix: Vec, iterations: usize, no_of_words: usize) -> Self { - BrainPrefix { - prefix, - iterations, - no_of_words, - last_phrase: String::new(), - } - } + pub fn new(prefix: Vec, iterations: usize, no_of_words: usize) -> Self { + BrainPrefix { + prefix, + iterations, + no_of_words, + last_phrase: String::new(), + } + } - pub fn phrase(&self) -> &str { - &self.last_phrase - } + pub fn phrase(&self) -> &str { + &self.last_phrase + } } impl Generator for BrainPrefix { - type Error = Error; + type Error = Error; - fn generate(&mut self) -> Result { - for _ in 0..self.iterations { - let phrase = wordlist::random_phrase(self.no_of_words); - let keypair = Brain::new(phrase.clone()).generate().unwrap(); - if keypair.address().starts_with(&self.prefix) { - self.last_phrase = phrase; - return Ok(keypair) - } - } + fn generate(&mut self) -> Result { + for _ in 0..self.iterations { + let phrase = wordlist::random_phrase(self.no_of_words); + let keypair = Brain::new(phrase.clone()).generate().unwrap(); + if keypair.address().starts_with(&self.prefix) { + self.last_phrase = phrase; + return Ok(keypair); + } + } - Err(Error::Custom("Could not find keypair".into())) - } + Err(Error::Custom("Could not find keypair".into())) + } } #[cfg(test)] mod tests { - use {Generator, BrainPrefix}; + use BrainPrefix; + use Generator; - #[test] - fn prefix_generator() { - let prefix = vec![0x00u8]; - let keypair = BrainPrefix::new(prefix.clone(), usize::max_value(), 12).generate().unwrap(); - assert!(keypair.address().starts_with(&prefix)); - } + #[test] + fn prefix_generator() { + let prefix = vec![0x00u8]; + let keypair = BrainPrefix::new(prefix.clone(), usize::max_value(), 12) + .generate() + .unwrap(); + assert!(keypair.address().starts_with(&prefix)); + } } diff --git a/accounts/ethkey/src/brain_recover.rs b/accounts/ethkey/src/brain_recover.rs index f9922fae97f..92cd7df1e37 100644 --- a/accounts/ethkey/src/brain_recover.rs +++ b/accounts/ethkey/src/brain_recover.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use std::collections::HashSet; @@ -26,148 +26,153 @@ use super::{Address, Brain, Generator}; /// /// Returns `None` if phrase couldn't be found. pub fn brain_recover( - address: &Address, - known_phrase: &str, - expected_words: usize, + address: &Address, + known_phrase: &str, + expected_words: usize, ) -> Option { - let it = PhrasesIterator::from_known_phrase(known_phrase, expected_words); - for phrase in it { - let keypair = Brain::new(phrase.clone()).generate().expect("Brain wallets are infallible; qed"); - trace!("Testing: {}, got: {:?}", phrase, keypair.address()); - if &keypair.address() == address { - return Some(phrase); - } - } - - None + let it = PhrasesIterator::from_known_phrase(known_phrase, expected_words); + for phrase in it { + let keypair = Brain::new(phrase.clone()) + .generate() + .expect("Brain wallets are infallible; qed"); + trace!("Testing: {}, got: {:?}", phrase, keypair.address()); + if &keypair.address() == address { + return Some(phrase); + } + } + + None } fn generate_substitutions(word: &str) -> Vec<&'static str> { - let mut words = parity_wordlist::WORDS.iter().cloned() - .map(|w| (edit_distance(w, word), w)) - .collect::>(); - words.sort_by(|a, b| a.0.cmp(&b.0)); - - words.into_iter() - .map(|pair| pair.1) - .collect() + let mut words = parity_wordlist::WORDS + .iter() + .cloned() + .map(|w| (edit_distance(w, word), w)) + .collect::>(); + words.sort_by(|a, b| a.0.cmp(&b.0)); + + words.into_iter().map(|pair| pair.1).collect() } /// Iterator over possible pub struct PhrasesIterator { - words: Vec>, - combinations: u64, - indexes: Vec, - has_next: bool, + words: Vec>, + combinations: u64, + indexes: Vec, + has_next: bool, } impl PhrasesIterator { - pub fn from_known_phrase(known_phrase: &str, expected_words: usize) -> Self { - let known_words = parity_wordlist::WORDS.iter().cloned().collect::>(); - let mut words = known_phrase.split(' ') - .map(|word| match known_words.get(word) { - None => { - info!("Invalid word '{}', looking for potential substitutions.", word); - let substitutions = generate_substitutions(word); - info!("Closest words: {:?}", &substitutions[..10]); - substitutions - }, - Some(word) => vec![*word], - }) - .collect::>(); - - // add missing words - if words.len() < expected_words { - let to_add = expected_words - words.len(); - info!("Number of words is insuficcient adding {} more.", to_add); - for _ in 0..to_add { - words.push(parity_wordlist::WORDS.iter().cloned().collect()); - } - } - - // start searching - PhrasesIterator::new(words) - } - - pub fn new(words: Vec>) -> Self { - let combinations = words.iter().fold(1u64, |acc, x| acc * x.len() as u64); - let indexes = words.iter().map(|_| 0).collect(); - info!("Starting to test {} possible combinations.", combinations); - - PhrasesIterator { - words, - combinations, - indexes, - has_next: combinations > 0, - } - } - - pub fn combinations(&self) -> u64 { - self.combinations - } - - fn current(&self) -> String { - let mut s = self.words[0][self.indexes[0]].to_owned(); - for i in 1..self.indexes.len() { - s.push(' '); - s.push_str(self.words[i][self.indexes[i]]); - } - s - } - - fn next_index(&mut self) -> bool { - let mut pos = self.indexes.len(); - while pos > 0 { - pos -= 1; - self.indexes[pos] += 1; - if self.indexes[pos] >= self.words[pos].len() { - self.indexes[pos] = 0; - } else { - return true; - } - } - - false - } + pub fn from_known_phrase(known_phrase: &str, expected_words: usize) -> Self { + let known_words = parity_wordlist::WORDS + .iter() + .cloned() + .collect::>(); + let mut words = known_phrase + .split(' ') + .map(|word| match known_words.get(word) { + None => { + info!( + "Invalid word '{}', looking for potential substitutions.", + word + ); + let substitutions = generate_substitutions(word); + info!("Closest words: {:?}", &substitutions[..10]); + substitutions + } + Some(word) => vec![*word], + }) + .collect::>(); + + // add missing words + if words.len() < expected_words { + let to_add = expected_words - words.len(); + info!("Number of words is insuficcient adding {} more.", to_add); + for _ in 0..to_add { + words.push(parity_wordlist::WORDS.iter().cloned().collect()); + } + } + + // start searching + PhrasesIterator::new(words) + } + + pub fn new(words: Vec>) -> Self { + let combinations = words.iter().fold(1u64, |acc, x| acc * x.len() as u64); + let indexes = words.iter().map(|_| 0).collect(); + info!("Starting to test {} possible combinations.", combinations); + + PhrasesIterator { + words, + combinations, + indexes, + has_next: combinations > 0, + } + } + + pub fn combinations(&self) -> u64 { + self.combinations + } + + fn current(&self) -> String { + let mut s = self.words[0][self.indexes[0]].to_owned(); + for i in 1..self.indexes.len() { + s.push(' '); + s.push_str(self.words[i][self.indexes[i]]); + } + s + } + + fn next_index(&mut self) -> bool { + let mut pos = self.indexes.len(); + while pos > 0 { + pos -= 1; + self.indexes[pos] += 1; + if self.indexes[pos] >= self.words[pos].len() { + self.indexes[pos] = 0; + } else { + return true; + } + } + + false + } } impl Iterator for PhrasesIterator { - type Item = String; + type Item = String; - fn next(&mut self) -> Option { - if !self.has_next { - return None; - } + fn next(&mut self) -> Option { + if !self.has_next { + return None; + } - let phrase = self.current(); - self.has_next = self.next_index(); - Some(phrase) - } + let phrase = self.current(); + self.has_next = self.next_index(); + Some(phrase) + } } #[cfg(test)] mod tests { - use super::PhrasesIterator; - - #[test] - fn should_generate_possible_combinations() { - let mut it = PhrasesIterator::new(vec![ - vec!["1", "2", "3"], - vec!["test"], - vec!["a", "b", "c"], - ]); - - assert_eq!(it.combinations(), 9); - assert_eq!(it.next(), Some("1 test a".to_owned())); - assert_eq!(it.next(), Some("1 test b".to_owned())); - assert_eq!(it.next(), Some("1 test c".to_owned())); - assert_eq!(it.next(), Some("2 test a".to_owned())); - assert_eq!(it.next(), Some("2 test b".to_owned())); - assert_eq!(it.next(), Some("2 test c".to_owned())); - assert_eq!(it.next(), Some("3 test a".to_owned())); - assert_eq!(it.next(), Some("3 test b".to_owned())); - assert_eq!(it.next(), Some("3 test c".to_owned())); - assert_eq!(it.next(), None); - } - + use super::PhrasesIterator; + + #[test] + fn should_generate_possible_combinations() { + let mut it = + PhrasesIterator::new(vec![vec!["1", "2", "3"], vec!["test"], vec!["a", "b", "c"]]); + + assert_eq!(it.combinations(), 9); + assert_eq!(it.next(), Some("1 test a".to_owned())); + assert_eq!(it.next(), Some("1 test b".to_owned())); + assert_eq!(it.next(), Some("1 test c".to_owned())); + assert_eq!(it.next(), Some("2 test a".to_owned())); + assert_eq!(it.next(), Some("2 test b".to_owned())); + assert_eq!(it.next(), Some("2 test c".to_owned())); + assert_eq!(it.next(), Some("3 test a".to_owned())); + assert_eq!(it.next(), Some("3 test b".to_owned())); + assert_eq!(it.next(), Some("3 test c".to_owned())); + assert_eq!(it.next(), None); + } } diff --git a/accounts/ethkey/src/crypto.rs b/accounts/ethkey/src/crypto.rs index ec883dcb6d9..4fa1ee6900b 100644 --- a/accounts/ethkey/src/crypto.rs +++ b/accounts/ethkey/src/crypto.rs @@ -1,189 +1,202 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . +#![allow(deprecated)] + +use parity_crypto::error::SymmError; use secp256k1; use std::io; -use parity_crypto::error::SymmError; quick_error! { - #[derive(Debug)] - pub enum Error { - Secp(e: secp256k1::Error) { - display("secp256k1 error: {}", e) - cause(e) - from() - } - Io(e: io::Error) { - display("i/o error: {}", e) - cause(e) - from() - } - InvalidMessage { - display("invalid message") - } - Symm(e: SymmError) { - cause(e) - from() - } - } + #[derive(Debug)] + pub enum Error { + Secp(e: secp256k1::Error) { + display("secp256k1 error: {}", e) + cause(e) + from() + } + Io(e: io::Error) { + display("i/o error: {}", e) + cause(e) + from() + } + InvalidMessage { + display("invalid message") + } + Symm(e: SymmError) { + cause(e) + from() + } + } } /// ECDH functions pub mod ecdh { - use secp256k1::{self, ecdh, key}; - use super::Error; - use {Secret, Public, SECP256K1}; - - /// Agree on a shared secret - pub fn agree(secret: &Secret, public: &Public) -> Result { - let context = &SECP256K1; - let pdata = { - let mut temp = [4u8; 65]; - (&mut temp[1..65]).copy_from_slice(&public[0..64]); - temp - }; - - let publ = key::PublicKey::from_slice(context, &pdata)?; - let sec = key::SecretKey::from_slice(context, &secret)?; - let shared = ecdh::SharedSecret::new_raw(context, &publ, &sec); - - Secret::from_unsafe_slice(&shared[0..32]) - .map_err(|_| Error::Secp(secp256k1::Error::InvalidSecretKey)) - } + use super::Error; + use secp256k1::{self, ecdh, key}; + use Public; + use Secret; + use SECP256K1; + + /// Agree on a shared secret + pub fn agree(secret: &Secret, public: &Public) -> Result { + let context = &SECP256K1; + let pdata = { + let mut temp = [4u8; 65]; + (&mut temp[1..65]).copy_from_slice(&public[0..64]); + temp + }; + + let publ = key::PublicKey::from_slice(context, &pdata)?; + let sec = key::SecretKey::from_slice(context, &secret)?; + let shared = ecdh::SharedSecret::new_raw(context, &publ, &sec); + + Secret::from_unsafe_slice(&shared[0..32]) + .map_err(|_| Error::Secp(secp256k1::Error::InvalidSecretKey)) + } } /// ECIES function pub mod ecies { - use parity_crypto::{aes, digest, hmac, is_equal}; - use ethereum_types::H128; - use super::{ecdh, Error}; - use {Random, Generator, Public, Secret}; - - /// Encrypt a message with a public key, writing an HMAC covering both - /// the plaintext and authenticated data. - /// - /// Authenticated data may be empty. - pub fn encrypt(public: &Public, auth_data: &[u8], plain: &[u8]) -> Result, Error> { - let r = Random.generate()?; - let z = ecdh::agree(r.secret(), public)?; - let mut key = [0u8; 32]; - kdf(&z, &[0u8; 0], &mut key); - - let ekey = &key[0..16]; - let mkey = hmac::SigKey::sha256(&digest::sha256(&key[16..32])); - - let mut msg = vec![0u8; 1 + 64 + 16 + plain.len() + 32]; - msg[0] = 0x04u8; - { - let msgd = &mut msg[1..]; - msgd[0..64].copy_from_slice(r.public()); - let iv = H128::random(); - msgd[64..80].copy_from_slice(&iv); - { - let cipher = &mut msgd[(64 + 16)..(64 + 16 + plain.len())]; - aes::encrypt_128_ctr(ekey, &iv, plain, cipher)?; - } - let mut hmac = hmac::Signer::with(&mkey); - { - let cipher_iv = &msgd[64..(64 + 16 + plain.len())]; - hmac.update(cipher_iv); - } - hmac.update(auth_data); - let sig = hmac.sign(); - msgd[(64 + 16 + plain.len())..].copy_from_slice(&sig); - } - Ok(msg) - } - - /// Decrypt a message with a secret key, checking HMAC for ciphertext - /// and authenticated data validity. - pub fn decrypt(secret: &Secret, auth_data: &[u8], encrypted: &[u8]) -> Result, Error> { - let meta_len = 1 + 64 + 16 + 32; - if encrypted.len() < meta_len || encrypted[0] < 2 || encrypted[0] > 4 { - return Err(Error::InvalidMessage); //invalid message: publickey - } - - let e = &encrypted[1..]; - let p = Public::from_slice(&e[0..64]); - let z = ecdh::agree(secret, &p)?; - let mut key = [0u8; 32]; - kdf(&z, &[0u8; 0], &mut key); - - let ekey = &key[0..16]; - let mkey = hmac::SigKey::sha256(&digest::sha256(&key[16..32])); - - let clen = encrypted.len() - meta_len; - let cipher_with_iv = &e[64..(64+16+clen)]; - let cipher_iv = &cipher_with_iv[0..16]; - let cipher_no_iv = &cipher_with_iv[16..]; - let msg_mac = &e[(64+16+clen)..]; - - // Verify tag - let mut hmac = hmac::Signer::with(&mkey); - hmac.update(cipher_with_iv); - hmac.update(auth_data); - let mac = hmac.sign(); - - if !is_equal(&mac.as_ref()[..], msg_mac) { - return Err(Error::InvalidMessage); - } - - let mut msg = vec![0u8; clen]; - aes::decrypt_128_ctr(ekey, cipher_iv, cipher_no_iv, &mut msg[..])?; - Ok(msg) - } - - fn kdf(secret: &Secret, s1: &[u8], dest: &mut [u8]) { - // SEC/ISO/Shoup specify counter size SHOULD be equivalent - // to size of hash output, however, it also notes that - // the 4 bytes is okay. NIST specifies 4 bytes. - let mut ctr = 1u32; - let mut written = 0usize; - while written < dest.len() { - let mut hasher = digest::Hasher::sha256(); - let ctrs = [(ctr >> 24) as u8, (ctr >> 16) as u8, (ctr >> 8) as u8, ctr as u8]; - hasher.update(&ctrs); - hasher.update(secret); - hasher.update(s1); - let d = hasher.finish(); - &mut dest[written..(written + 32)].copy_from_slice(&d); - written += 32; - ctr += 1; - } - } + use super::{ecdh, Error}; + use ethereum_types::H128; + use parity_crypto::{aes, digest, hmac, is_equal}; + use Generator; + use Public; + use Random; + use Secret; + + /// Encrypt a message with a public key, writing an HMAC covering both + /// the plaintext and authenticated data. + /// + /// Authenticated data may be empty. + pub fn encrypt(public: &Public, auth_data: &[u8], plain: &[u8]) -> Result, Error> { + let r = Random.generate()?; + let z = ecdh::agree(r.secret(), public)?; + let mut key = [0u8; 32]; + kdf(&z, &[0u8; 0], &mut key); + + let ekey = &key[0..16]; + let mkey = hmac::SigKey::sha256(&digest::sha256(&key[16..32])); + + let mut msg = vec![0u8; 1 + 64 + 16 + plain.len() + 32]; + msg[0] = 0x04u8; + { + let msgd = &mut msg[1..]; + msgd[0..64].copy_from_slice(r.public()); + let iv = H128::random(); + msgd[64..80].copy_from_slice(&iv); + { + let cipher = &mut msgd[(64 + 16)..(64 + 16 + plain.len())]; + aes::encrypt_128_ctr(ekey, &iv, plain, cipher)?; + } + let mut hmac = hmac::Signer::with(&mkey); + { + let cipher_iv = &msgd[64..(64 + 16 + plain.len())]; + hmac.update(cipher_iv); + } + hmac.update(auth_data); + let sig = hmac.sign(); + msgd[(64 + 16 + plain.len())..].copy_from_slice(&sig); + } + Ok(msg) + } + + /// Decrypt a message with a secret key, checking HMAC for ciphertext + /// and authenticated data validity. + pub fn decrypt(secret: &Secret, auth_data: &[u8], encrypted: &[u8]) -> Result, Error> { + let meta_len = 1 + 64 + 16 + 32; + if encrypted.len() < meta_len || encrypted[0] < 2 || encrypted[0] > 4 { + return Err(Error::InvalidMessage); //invalid message: publickey + } + + let e = &encrypted[1..]; + let p = Public::from_slice(&e[0..64]); + let z = ecdh::agree(secret, &p)?; + let mut key = [0u8; 32]; + kdf(&z, &[0u8; 0], &mut key); + + let ekey = &key[0..16]; + let mkey = hmac::SigKey::sha256(&digest::sha256(&key[16..32])); + + let clen = encrypted.len() - meta_len; + let cipher_with_iv = &e[64..(64 + 16 + clen)]; + let cipher_iv = &cipher_with_iv[0..16]; + let cipher_no_iv = &cipher_with_iv[16..]; + let msg_mac = &e[(64 + 16 + clen)..]; + + // Verify tag + let mut hmac = hmac::Signer::with(&mkey); + hmac.update(cipher_with_iv); + hmac.update(auth_data); + let mac = hmac.sign(); + + if !is_equal(&mac.as_ref()[..], msg_mac) { + return Err(Error::InvalidMessage); + } + + let mut msg = vec![0u8; clen]; + aes::decrypt_128_ctr(ekey, cipher_iv, cipher_no_iv, &mut msg[..])?; + Ok(msg) + } + + fn kdf(secret: &Secret, s1: &[u8], dest: &mut [u8]) { + // SEC/ISO/Shoup specify counter size SHOULD be equivalent + // to size of hash output, however, it also notes that + // the 4 bytes is okay. NIST specifies 4 bytes. + let mut ctr = 1u32; + let mut written = 0usize; + while written < dest.len() { + let mut hasher = digest::Hasher::sha256(); + let ctrs = [ + (ctr >> 24) as u8, + (ctr >> 16) as u8, + (ctr >> 8) as u8, + ctr as u8, + ]; + hasher.update(&ctrs); + hasher.update(secret); + hasher.update(s1); + let d = hasher.finish(); + &mut dest[written..(written + 32)].copy_from_slice(&d); + written += 32; + ctr += 1; + } + } } #[cfg(test)] mod tests { - use super::ecies; - use {Random, Generator}; - - #[test] - fn ecies_shared() { - let kp = Random.generate().unwrap(); - let message = b"So many books, so little time"; - - let shared = b"shared"; - let wrong_shared = b"incorrect"; - let encrypted = ecies::encrypt(kp.public(), shared, message).unwrap(); - assert!(encrypted[..] != message[..]); - assert_eq!(encrypted[0], 0x04); - - assert!(ecies::decrypt(kp.secret(), wrong_shared, &encrypted).is_err()); - let decrypted = ecies::decrypt(kp.secret(), shared, &encrypted).unwrap(); - assert_eq!(decrypted[..message.len()], message[..]); - } + use super::ecies; + use Generator; + use Random; + + #[test] + fn ecies_shared() { + let kp = Random.generate().unwrap(); + let message = b"So many books, so little time"; + + let shared = b"shared"; + let wrong_shared = b"incorrect"; + let encrypted = ecies::encrypt(kp.public(), shared, message).unwrap(); + assert!(encrypted[..] != message[..]); + assert_eq!(encrypted[0], 0x04); + + assert!(ecies::decrypt(kp.secret(), wrong_shared, &encrypted).is_err()); + let decrypted = ecies::decrypt(kp.secret(), shared, &encrypted).unwrap(); + assert_eq!(decrypted[..message.len()], message[..]); + } } diff --git a/accounts/ethkey/src/error.rs b/accounts/ethkey/src/error.rs index ee191157453..3e66a905d2a 100644 --- a/accounts/ethkey/src/error.rs +++ b/accounts/ethkey/src/error.rs @@ -1,81 +1,81 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::{fmt, error}; +use std::{error, fmt}; #[derive(Debug)] /// Crypto error pub enum Error { - /// Invalid secret key - InvalidSecret, - /// Invalid public key - InvalidPublic, - /// Invalid address - InvalidAddress, - /// Invalid EC signature - InvalidSignature, - /// Invalid AES message - InvalidMessage, - /// IO Error - Io(::std::io::Error), - /// Custom - Custom(String), + /// Invalid secret key + InvalidSecret, + /// Invalid public key + InvalidPublic, + /// Invalid address + InvalidAddress, + /// Invalid EC signature + InvalidSignature, + /// Invalid AES message + InvalidMessage, + /// IO Error + Io(::std::io::Error), + /// Custom + Custom(String), } impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let msg = match *self { - Error::InvalidSecret => "Invalid secret".into(), - Error::InvalidPublic => "Invalid public".into(), - Error::InvalidAddress => "Invalid address".into(), - Error::InvalidSignature => "Invalid EC signature".into(), - Error::InvalidMessage => "Invalid AES message".into(), - Error::Io(ref err) => format!("I/O error: {}", err), - Error::Custom(ref s) => s.clone(), - }; + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let msg = match *self { + Error::InvalidSecret => "Invalid secret".into(), + Error::InvalidPublic => "Invalid public".into(), + Error::InvalidAddress => "Invalid address".into(), + Error::InvalidSignature => "Invalid EC signature".into(), + Error::InvalidMessage => "Invalid AES message".into(), + Error::Io(ref err) => format!("I/O error: {}", err), + Error::Custom(ref s) => s.clone(), + }; - f.write_fmt(format_args!("Crypto error ({})", msg)) - } + f.write_fmt(format_args!("Crypto error ({})", msg)) + } } impl error::Error for Error { - fn description(&self) -> &str { - "Crypto error" - } + fn description(&self) -> &str { + "Crypto error" + } } impl Into for Error { - fn into(self) -> String { - format!("{}", self) - } + fn into(self) -> String { + format!("{}", self) + } } impl From<::secp256k1::Error> for Error { - fn from(e: ::secp256k1::Error) -> Error { - match e { - ::secp256k1::Error::InvalidMessage => Error::InvalidMessage, - ::secp256k1::Error::InvalidPublicKey => Error::InvalidPublic, - ::secp256k1::Error::InvalidSecretKey => Error::InvalidSecret, - _ => Error::InvalidSignature, - } - } + fn from(e: ::secp256k1::Error) -> Error { + match e { + ::secp256k1::Error::InvalidMessage => Error::InvalidMessage, + ::secp256k1::Error::InvalidPublicKey => Error::InvalidPublic, + ::secp256k1::Error::InvalidSecretKey => Error::InvalidSecret, + _ => Error::InvalidSignature, + } + } } impl From<::std::io::Error> for Error { - fn from(err: ::std::io::Error) -> Error { - Error::Io(err) - } + fn from(err: ::std::io::Error) -> Error { + Error::Io(err) + } } diff --git a/accounts/ethkey/src/extended.rs b/accounts/ethkey/src/extended.rs index 7d02271ebbd..663f555e7a4 100644 --- a/accounts/ethkey/src/extended.rs +++ b/accounts/ethkey/src/extended.rs @@ -1,501 +1,589 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Extended keys +pub use self::derivation::Error as DerivationError; +use ethereum_types::H256; use secret::Secret; use Public; -use ethereum_types::H256; -pub use self::derivation::Error as DerivationError; /// Represents label that can be stored as a part of key derivation pub trait Label { - /// Length of the data that label occupies - fn len() -> usize; + /// Length of the data that label occupies + fn len() -> usize; - /// Store label data to the key derivation sequence - /// Must not use more than `len()` bytes from slice - fn store(&self, target: &mut [u8]); + /// Store label data to the key derivation sequence + /// Must not use more than `len()` bytes from slice + fn store(&self, target: &mut [u8]); } impl Label for u32 { - fn len() -> usize { 4 } - - fn store(&self, target: &mut [u8]) { - use byteorder::{BigEndian, ByteOrder}; - - BigEndian::write_u32(&mut target[0..4], *self); - } + fn len() -> usize { + 4 + } + + fn store(&self, target: &mut [u8]) { + let bytes = self.to_be_bytes(); + target[0..4].copy_from_slice(&bytes); + } } /// Key derivation over generic label `T` pub enum Derivation { - /// Soft key derivation (allow proof of parent) - Soft(T), - /// Hard key derivation (does not allow proof of parent) - Hard(T), + /// Soft key derivation (allow proof of parent) + Soft(T), + /// Hard key derivation (does not allow proof of parent) + Hard(T), } impl From for Derivation { - fn from(index: u32) -> Self { - if index < (2 << 30) { - Derivation::Soft(index) - } - else { - Derivation::Hard(index) - } - } + fn from(index: u32) -> Self { + if index < (2 << 30) { + Derivation::Soft(index) + } else { + Derivation::Hard(index) + } + } } impl Label for H256 { - fn len() -> usize { 32 } + fn len() -> usize { + 32 + } - fn store(&self, target: &mut [u8]) { - self.copy_to(&mut target[0..32]); - } + fn store(&self, target: &mut [u8]) { + self.copy_to(&mut target[0..32]); + } } /// Extended secret key, allows deterministic derivation of subsequent keys. pub struct ExtendedSecret { - secret: Secret, - chain_code: H256, + secret: Secret, + chain_code: H256, } impl ExtendedSecret { - /// New extended key from given secret and chain code. - pub fn with_code(secret: Secret, chain_code: H256) -> ExtendedSecret { - ExtendedSecret { - secret: secret, - chain_code: chain_code, - } - } - - /// New extended key from given secret with the random chain code. - pub fn new_random(secret: Secret) -> ExtendedSecret { - ExtendedSecret::with_code(secret, H256::random()) - } - - /// New extended key from given secret. - /// Chain code will be derived from the secret itself (in a deterministic way). - pub fn new(secret: Secret) -> ExtendedSecret { - let chain_code = derivation::chain_code(*secret); - ExtendedSecret::with_code(secret, chain_code) - } - - /// Derive new private key - pub fn derive(&self, index: Derivation) -> ExtendedSecret where T: Label { - let (derived_key, next_chain_code) = derivation::private(*self.secret, self.chain_code, index); - - let derived_secret = Secret::from(derived_key.0); - - ExtendedSecret::with_code(derived_secret, next_chain_code) - } - - /// Private key component of the extended key. - pub fn as_raw(&self) -> &Secret { - &self.secret - } + /// New extended key from given secret and chain code. + pub fn with_code(secret: Secret, chain_code: H256) -> ExtendedSecret { + ExtendedSecret { + secret: secret, + chain_code: chain_code, + } + } + + /// New extended key from given secret with the random chain code. + pub fn new_random(secret: Secret) -> ExtendedSecret { + ExtendedSecret::with_code(secret, H256::random()) + } + + /// New extended key from given secret. + /// Chain code will be derived from the secret itself (in a deterministic way). + pub fn new(secret: Secret) -> ExtendedSecret { + let chain_code = derivation::chain_code(*secret); + ExtendedSecret::with_code(secret, chain_code) + } + + /// Derive new private key + pub fn derive(&self, index: Derivation) -> ExtendedSecret + where + T: Label, + { + let (derived_key, next_chain_code) = + derivation::private(*self.secret, self.chain_code, index); + + let derived_secret = Secret::from(derived_key.0); + + ExtendedSecret::with_code(derived_secret, next_chain_code) + } + + /// Private key component of the extended key. + pub fn as_raw(&self) -> &Secret { + &self.secret + } } /// Extended public key, allows deterministic derivation of subsequent keys. pub struct ExtendedPublic { - public: Public, - chain_code: H256, + public: Public, + chain_code: H256, } impl ExtendedPublic { - /// New extended public key from known parent and chain code - pub fn new(public: Public, chain_code: H256) -> Self { - ExtendedPublic { public: public, chain_code: chain_code } - } - - /// Create new extended public key from known secret - pub fn from_secret(secret: &ExtendedSecret) -> Result { - Ok( - ExtendedPublic::new( - derivation::point(**secret.as_raw())?, - secret.chain_code.clone(), - ) - ) - } - - /// Derive new public key - /// Operation is defined only for index belongs [0..2^31) - pub fn derive(&self, index: Derivation) -> Result where T: Label { - let (derived_key, next_chain_code) = derivation::public(self.public, self.chain_code, index)?; - Ok(ExtendedPublic::new(derived_key, next_chain_code)) - } - - pub fn public(&self) -> &Public { - &self.public - } + /// New extended public key from known parent and chain code + pub fn new(public: Public, chain_code: H256) -> Self { + ExtendedPublic { + public: public, + chain_code: chain_code, + } + } + + /// Create new extended public key from known secret + pub fn from_secret(secret: &ExtendedSecret) -> Result { + Ok(ExtendedPublic::new( + derivation::point(**secret.as_raw())?, + secret.chain_code.clone(), + )) + } + + /// Derive new public key + /// Operation is defined only for index belongs [0..2^31) + pub fn derive(&self, index: Derivation) -> Result + where + T: Label, + { + let (derived_key, next_chain_code) = + derivation::public(self.public, self.chain_code, index)?; + Ok(ExtendedPublic::new(derived_key, next_chain_code)) + } + + pub fn public(&self) -> &Public { + &self.public + } } pub struct ExtendedKeyPair { - secret: ExtendedSecret, - public: ExtendedPublic, + secret: ExtendedSecret, + public: ExtendedPublic, } impl ExtendedKeyPair { - pub fn new(secret: Secret) -> Self { - let extended_secret = ExtendedSecret::new(secret); - let extended_public = ExtendedPublic::from_secret(&extended_secret) - .expect("Valid `Secret` always produces valid public; qed"); - ExtendedKeyPair { - secret: extended_secret, - public: extended_public, - } - } - - pub fn with_code(secret: Secret, public: Public, chain_code: H256) -> Self { - ExtendedKeyPair { - secret: ExtendedSecret::with_code(secret, chain_code.clone()), - public: ExtendedPublic::new(public, chain_code), - } - } - - pub fn with_secret(secret: Secret, chain_code: H256) -> Self { - let extended_secret = ExtendedSecret::with_code(secret, chain_code); - let extended_public = ExtendedPublic::from_secret(&extended_secret) - .expect("Valid `Secret` always produces valid public; qed"); - ExtendedKeyPair { - secret: extended_secret, - public: extended_public, - } - } - - pub fn with_seed(seed: &[u8]) -> Result { - let (master_key, chain_code) = derivation::seed_pair(seed); - Ok(ExtendedKeyPair::with_secret( - Secret::from_unsafe_slice(&*master_key).map_err(|_| DerivationError::InvalidSeed)?, - chain_code, - )) - } - - pub fn secret(&self) -> &ExtendedSecret { - &self.secret - } - - pub fn public(&self) -> &ExtendedPublic { - &self.public - } - - pub fn derive(&self, index: Derivation) -> Result where T: Label { - let derived = self.secret.derive(index); - - Ok(ExtendedKeyPair { - public: ExtendedPublic::from_secret(&derived)?, - secret: derived, - }) - } + pub fn new(secret: Secret) -> Self { + let extended_secret = ExtendedSecret::new(secret); + let extended_public = ExtendedPublic::from_secret(&extended_secret) + .expect("Valid `Secret` always produces valid public; qed"); + ExtendedKeyPair { + secret: extended_secret, + public: extended_public, + } + } + + pub fn with_code(secret: Secret, public: Public, chain_code: H256) -> Self { + ExtendedKeyPair { + secret: ExtendedSecret::with_code(secret, chain_code.clone()), + public: ExtendedPublic::new(public, chain_code), + } + } + + pub fn with_secret(secret: Secret, chain_code: H256) -> Self { + let extended_secret = ExtendedSecret::with_code(secret, chain_code); + let extended_public = ExtendedPublic::from_secret(&extended_secret) + .expect("Valid `Secret` always produces valid public; qed"); + ExtendedKeyPair { + secret: extended_secret, + public: extended_public, + } + } + + pub fn with_seed(seed: &[u8]) -> Result { + let (master_key, chain_code) = derivation::seed_pair(seed); + Ok(ExtendedKeyPair::with_secret( + Secret::from_unsafe_slice(&*master_key).map_err(|_| DerivationError::InvalidSeed)?, + chain_code, + )) + } + + pub fn secret(&self) -> &ExtendedSecret { + &self.secret + } + + pub fn public(&self) -> &ExtendedPublic { + &self.public + } + + pub fn derive(&self, index: Derivation) -> Result + where + T: Label, + { + let derived = self.secret.derive(index); + + Ok(ExtendedKeyPair { + public: ExtendedPublic::from_secret(&derived)?, + secret: derived, + }) + } } // Derivation functions for private and public keys // Work is based on BIP0032 // https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki mod derivation { - use parity_crypto::hmac; - use ethereum_types::{U256, U512, H512, H256}; - use secp256k1::key::{SecretKey, PublicKey}; - use SECP256K1; - use keccak; - use math::curve_order; - use super::{Label, Derivation}; - - #[derive(Debug)] - pub enum Error { - InvalidHardenedUse, - InvalidPoint, - MissingIndex, - InvalidSeed, - } - - // Deterministic derivation of the key using secp256k1 elliptic curve. - // Derivation can be either hardened or not. - // For hardened derivation, pass u32 index at least 2^31 or custom Derivation::Hard(T) enum - // - // Can panic if passed `private_key` is not a valid secp256k1 private key - // (outside of (0..curve_order()]) field - pub fn private(private_key: H256, chain_code: H256, index: Derivation) -> (H256, H256) where T: Label { - match index { - Derivation::Soft(index) => private_soft(private_key, chain_code, index), - Derivation::Hard(index) => private_hard(private_key, chain_code, index), - } - } - - fn hmac_pair(data: &[u8], private_key: H256, chain_code: H256) -> (H256, H256) { - let private: U256 = private_key.into(); - - // produces 512-bit derived hmac (I) - let skey = hmac::SigKey::sha512(&*chain_code); - let i_512 = hmac::sign(&skey, &data[..]); - - // left most 256 bits are later added to original private key - let hmac_key: U256 = H256::from_slice(&i_512[0..32]).into(); - // right most 256 bits are new chain code for later derivations - let next_chain_code = H256::from(&i_512[32..64]); - - let child_key = private_add(hmac_key, private).into(); - (child_key, next_chain_code) - } - - // Can panic if passed `private_key` is not a valid secp256k1 private key - // (outside of (0..curve_order()]) field - fn private_soft(private_key: H256, chain_code: H256, index: T) -> (H256, H256) where T: Label { - let mut data = vec![0u8; 33 + T::len()]; - - let sec_private = SecretKey::from_slice(&SECP256K1, &*private_key) - .expect("Caller should provide valid private key"); - let sec_public = PublicKey::from_secret_key(&SECP256K1, &sec_private) - .expect("Caller should provide valid private key"); - let public_serialized = sec_public.serialize_vec(&SECP256K1, true); - - // curve point (compressed public key) -- index - // 0.33 -- 33..end - data[0..33].copy_from_slice(&public_serialized); - index.store(&mut data[33..]); - - hmac_pair(&data, private_key, chain_code) - } - - // Deterministic derivation of the key using secp256k1 elliptic curve - // This is hardened derivation and does not allow to associate - // corresponding public keys of the original and derived private keys - fn private_hard(private_key: H256, chain_code: H256, index: T) -> (H256, H256) where T: Label { - let mut data: Vec = vec![0u8; 33 + T::len()]; - let private: U256 = private_key.into(); - - // 0x00 (padding) -- private_key -- index - // 0 -- 1..33 -- 33..end - private.to_big_endian(&mut data[1..33]); - index.store(&mut data[33..(33 + T::len())]); - - hmac_pair(&data, private_key, chain_code) - } - - fn private_add(k1: U256, k2: U256) -> U256 { - let sum = U512::from(k1) + U512::from(k2); - modulo(sum, curve_order()) - } - - // todo: surely can be optimized - fn modulo(u1: U512, u2: U256) -> U256 { - let dv = u1 / U512::from(u2); - let md = u1 - (dv * U512::from(u2)); - md.into() - } - - pub fn public(public_key: H512, chain_code: H256, derivation: Derivation) -> Result<(H512, H256), Error> where T: Label { - let index = match derivation { - Derivation::Soft(index) => index, - Derivation::Hard(_) => { return Err(Error::InvalidHardenedUse); } - }; - - let mut public_sec_raw = [0u8; 65]; - public_sec_raw[0] = 4; - public_sec_raw[1..65].copy_from_slice(&*public_key); - let public_sec = PublicKey::from_slice(&SECP256K1, &public_sec_raw).map_err(|_| Error::InvalidPoint)?; - let public_serialized = public_sec.serialize_vec(&SECP256K1, true); - - let mut data = vec![0u8; 33 + T::len()]; - // curve point (compressed public key) -- index - // 0.33 -- 33..end - data[0..33].copy_from_slice(&public_serialized); - index.store(&mut data[33..(33 + T::len())]); - - // HMAC512SHA produces [derived private(256); new chain code(256)] - let skey = hmac::SigKey::sha512(&*chain_code); - let i_512 = hmac::sign(&skey, &data[..]); - - let new_private = H256::from(&i_512[0..32]); - let new_chain_code = H256::from(&i_512[32..64]); - - // Generated private key can (extremely rarely) be out of secp256k1 key field - if curve_order() <= new_private.clone().into() { return Err(Error::MissingIndex); } - let new_private_sec = SecretKey::from_slice(&SECP256K1, &*new_private) + use super::{Derivation, Label}; + use ethereum_types::{H256, H512, U256, U512}; + use keccak; + use math::curve_order; + use parity_crypto::hmac; + use secp256k1::key::{PublicKey, SecretKey}; + use SECP256K1; + + #[derive(Debug)] + pub enum Error { + InvalidHardenedUse, + InvalidPoint, + MissingIndex, + InvalidSeed, + } + + // Deterministic derivation of the key using secp256k1 elliptic curve. + // Derivation can be either hardened or not. + // For hardened derivation, pass u32 index at least 2^31 or custom Derivation::Hard(T) enum + // + // Can panic if passed `private_key` is not a valid secp256k1 private key + // (outside of (0..curve_order()]) field + pub fn private(private_key: H256, chain_code: H256, index: Derivation) -> (H256, H256) + where + T: Label, + { + match index { + Derivation::Soft(index) => private_soft(private_key, chain_code, index), + Derivation::Hard(index) => private_hard(private_key, chain_code, index), + } + } + + fn hmac_pair(data: &[u8], private_key: H256, chain_code: H256) -> (H256, H256) { + let private: U256 = private_key.into(); + + // produces 512-bit derived hmac (I) + let skey = hmac::SigKey::sha512(&*chain_code); + let i_512 = hmac::sign(&skey, &data[..]); + + // left most 256 bits are later added to original private key + let hmac_key: U256 = H256::from_slice(&i_512[0..32]).into(); + // right most 256 bits are new chain code for later derivations + let next_chain_code = H256::from(&i_512[32..64]); + + let child_key = private_add(hmac_key, private).into(); + (child_key, next_chain_code) + } + + // Can panic if passed `private_key` is not a valid secp256k1 private key + // (outside of (0..curve_order()]) field + fn private_soft(private_key: H256, chain_code: H256, index: T) -> (H256, H256) + where + T: Label, + { + let mut data = vec![0u8; 33 + T::len()]; + + let sec_private = SecretKey::from_slice(&SECP256K1, &*private_key) + .expect("Caller should provide valid private key"); + let sec_public = PublicKey::from_secret_key(&SECP256K1, &sec_private) + .expect("Caller should provide valid private key"); + let public_serialized = sec_public.serialize_vec(&SECP256K1, true); + + // curve point (compressed public key) -- index + // 0.33 -- 33..end + data[0..33].copy_from_slice(&public_serialized); + index.store(&mut data[33..]); + + hmac_pair(&data, private_key, chain_code) + } + + // Deterministic derivation of the key using secp256k1 elliptic curve + // This is hardened derivation and does not allow to associate + // corresponding public keys of the original and derived private keys + fn private_hard(private_key: H256, chain_code: H256, index: T) -> (H256, H256) + where + T: Label, + { + let mut data: Vec = vec![0u8; 33 + T::len()]; + let private: U256 = private_key.into(); + + // 0x00 (padding) -- private_key -- index + // 0 -- 1..33 -- 33..end + private.to_big_endian(&mut data[1..33]); + index.store(&mut data[33..(33 + T::len())]); + + hmac_pair(&data, private_key, chain_code) + } + + fn private_add(k1: U256, k2: U256) -> U256 { + let sum = U512::from(k1) + U512::from(k2); + modulo(sum, curve_order()) + } + + // todo: surely can be optimized + fn modulo(u1: U512, u2: U256) -> U256 { + let dv = u1 / U512::from(u2); + let md = u1 - (dv * U512::from(u2)); + md.into() + } + + pub fn public( + public_key: H512, + chain_code: H256, + derivation: Derivation, + ) -> Result<(H512, H256), Error> + where + T: Label, + { + let index = match derivation { + Derivation::Soft(index) => index, + Derivation::Hard(_) => { + return Err(Error::InvalidHardenedUse); + } + }; + + let mut public_sec_raw = [0u8; 65]; + public_sec_raw[0] = 4; + public_sec_raw[1..65].copy_from_slice(&*public_key); + let public_sec = + PublicKey::from_slice(&SECP256K1, &public_sec_raw).map_err(|_| Error::InvalidPoint)?; + let public_serialized = public_sec.serialize_vec(&SECP256K1, true); + + let mut data = vec![0u8; 33 + T::len()]; + // curve point (compressed public key) -- index + // 0.33 -- 33..end + data[0..33].copy_from_slice(&public_serialized); + index.store(&mut data[33..(33 + T::len())]); + + // HMAC512SHA produces [derived private(256); new chain code(256)] + let skey = hmac::SigKey::sha512(&*chain_code); + let i_512 = hmac::sign(&skey, &data[..]); + + let new_private = H256::from(&i_512[0..32]); + let new_chain_code = H256::from(&i_512[32..64]); + + // Generated private key can (extremely rarely) be out of secp256k1 key field + if curve_order() <= new_private.clone().into() { + return Err(Error::MissingIndex); + } + let new_private_sec = SecretKey::from_slice(&SECP256K1, &*new_private) .expect("Private key belongs to the field [0..CURVE_ORDER) (checked above); So initializing can never fail; qed"); - let mut new_public = PublicKey::from_secret_key(&SECP256K1, &new_private_sec) - .expect("Valid private key produces valid public key"); - - // Adding two points on the elliptic curves (combining two public keys) - new_public.add_assign(&SECP256K1, &public_sec) - .expect("Addition of two valid points produce valid point"); - - let serialized = new_public.serialize_vec(&SECP256K1, false); - - Ok(( - H512::from(&serialized[1..65]), - new_chain_code, - )) - } - - fn sha3(slc: &[u8]) -> H256 { - keccak::Keccak256::keccak256(slc).into() - } - - pub fn chain_code(secret: H256) -> H256 { - // 10,000 rounds of sha3 - let mut running_sha3 = sha3(&*secret); - for _ in 0..99999 { running_sha3 = sha3(&*running_sha3); } - running_sha3 - } - - pub fn point(secret: H256) -> Result { - let sec = SecretKey::from_slice(&SECP256K1, &*secret) - .map_err(|_| Error::InvalidPoint)?; - let public_sec = PublicKey::from_secret_key(&SECP256K1, &sec) - .map_err(|_| Error::InvalidPoint)?; - let serialized = public_sec.serialize_vec(&SECP256K1, false); - Ok(H512::from(&serialized[1..65])) - } - - pub fn seed_pair(seed: &[u8]) -> (H256, H256) { - let skey = hmac::SigKey::sha512(b"Bitcoin seed"); - let i_512 = hmac::sign(&skey, seed); - - let master_key = H256::from_slice(&i_512[0..32]); - let chain_code = H256::from_slice(&i_512[32..64]); - - (master_key, chain_code) - } + let mut new_public = PublicKey::from_secret_key(&SECP256K1, &new_private_sec) + .expect("Valid private key produces valid public key"); + + // Adding two points on the elliptic curves (combining two public keys) + new_public + .add_assign(&SECP256K1, &public_sec) + .expect("Addition of two valid points produce valid point"); + + let serialized = new_public.serialize_vec(&SECP256K1, false); + + Ok((H512::from(&serialized[1..65]), new_chain_code)) + } + + fn sha3(slc: &[u8]) -> H256 { + keccak::Keccak256::keccak256(slc).into() + } + + pub fn chain_code(secret: H256) -> H256 { + // 10,000 rounds of sha3 + let mut running_sha3 = sha3(&*secret); + for _ in 0..99999 { + running_sha3 = sha3(&*running_sha3); + } + running_sha3 + } + + pub fn point(secret: H256) -> Result { + let sec = SecretKey::from_slice(&SECP256K1, &*secret).map_err(|_| Error::InvalidPoint)?; + let public_sec = + PublicKey::from_secret_key(&SECP256K1, &sec).map_err(|_| Error::InvalidPoint)?; + let serialized = public_sec.serialize_vec(&SECP256K1, false); + Ok(H512::from(&serialized[1..65])) + } + + pub fn seed_pair(seed: &[u8]) -> (H256, H256) { + let skey = hmac::SigKey::sha512(b"Bitcoin seed"); + let i_512 = hmac::sign(&skey, seed); + + let master_key = H256::from_slice(&i_512[0..32]); + let chain_code = H256::from_slice(&i_512[32..64]); + + (master_key, chain_code) + } } #[cfg(test)] mod tests { - use super::{ExtendedSecret, ExtendedPublic, ExtendedKeyPair}; - use secret::Secret; - use std::str::FromStr; - use ethereum_types::{H128, H256}; - use super::{derivation, Derivation}; - - fn master_chain_basic() -> (H256, H256) { - let seed = H128::from_str("000102030405060708090a0b0c0d0e0f") - .expect("Seed should be valid H128") - .to_vec(); - - derivation::seed_pair(&*seed) - } - - fn test_extended(f: F, test_private: H256) where F: Fn(ExtendedSecret) -> ExtendedSecret { - let (private_seed, chain_code) = master_chain_basic(); - let extended_secret = ExtendedSecret::with_code(Secret::from(private_seed.0), chain_code); - let derived = f(extended_secret); - assert_eq!(**derived.as_raw(), test_private); - } - - #[test] - fn smoky() { - let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); - let extended_secret = ExtendedSecret::with_code(secret.clone(), 0u64.into()); - - // hardened - assert_eq!(&**extended_secret.as_raw(), &*secret); - assert_eq!(&**extended_secret.derive(2147483648.into()).as_raw(), &"0927453daed47839608e414a3738dfad10aed17c459bbd9ab53f89b026c834b6".into()); - assert_eq!(&**extended_secret.derive(2147483649.into()).as_raw(), &"44238b6a29c6dcbe9b401364141ba11e2198c289a5fed243a1c11af35c19dc0f".into()); - - // normal - assert_eq!(&**extended_secret.derive(0.into()).as_raw(), &"bf6a74e3f7b36fc4c96a1e12f31abc817f9f5904f5a8fc27713163d1f0b713f6".into()); - assert_eq!(&**extended_secret.derive(1.into()).as_raw(), &"bd4fca9eb1f9c201e9448c1eecd66e302d68d4d313ce895b8c134f512205c1bc".into()); - assert_eq!(&**extended_secret.derive(2.into()).as_raw(), &"86932b542d6cab4d9c65490c7ef502d89ecc0e2a5f4852157649e3251e2a3268".into()); - - let extended_public = ExtendedPublic::from_secret(&extended_secret).expect("Extended public should be created"); - let derived_public = extended_public.derive(0.into()).expect("First derivation of public should succeed"); - assert_eq!(&*derived_public.public(), &"f7b3244c96688f92372bfd4def26dc4151529747bab9f188a4ad34e141d47bd66522ff048bc6f19a0a4429b04318b1a8796c000265b4fa200dae5f6dda92dd94".into()); - - let keypair = ExtendedKeyPair::with_secret( - Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(), - 064.into(), - ); - assert_eq!(&**keypair.derive(2147483648u32.into()).expect("Derivation of keypair should succeed").secret().as_raw(), &"edef54414c03196557cf73774bc97a645c9a1df2164ed34f0c2a78d1375a930c".into()); - } - - #[test] - fn h256_soft_match() { - let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); - let derivation_secret = H256::from_str("51eaf04f9dbbc1417dc97e789edd0c37ecda88bac490434e367ea81b71b7b015").unwrap(); - - let extended_secret = ExtendedSecret::with_code(secret.clone(), 0u64.into()); - let extended_public = ExtendedPublic::from_secret(&extended_secret).expect("Extended public should be created"); - - let derived_secret0 = extended_secret.derive(Derivation::Soft(derivation_secret)); - let derived_public0 = extended_public.derive(Derivation::Soft(derivation_secret)).expect("First derivation of public should succeed"); - - let public_from_secret0 = ExtendedPublic::from_secret(&derived_secret0).expect("Extended public should be created"); - - assert_eq!(public_from_secret0.public(), derived_public0.public()); - } - - #[test] - fn h256_hard() { - let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); - let derivation_secret = H256::from_str("51eaf04f9dbbc1417dc97e789edd0c37ecda88bac490434e367ea81b71b7b015").unwrap(); - let extended_secret = ExtendedSecret::with_code(secret.clone(), 1u64.into()); - - assert_eq!(&**extended_secret.derive(Derivation::Hard(derivation_secret)).as_raw(), &"2bc2d696fb744d77ff813b4a1ef0ad64e1e5188b622c54ba917acc5ebc7c5486".into()); - } - - #[test] - fn match_() { - let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); - let extended_secret = ExtendedSecret::with_code(secret.clone(), 1.into()); - let extended_public = ExtendedPublic::from_secret(&extended_secret).expect("Extended public should be created"); - - let derived_secret0 = extended_secret.derive(0.into()); - let derived_public0 = extended_public.derive(0.into()).expect("First derivation of public should succeed"); - - let public_from_secret0 = ExtendedPublic::from_secret(&derived_secret0).expect("Extended public should be created"); - - assert_eq!(public_from_secret0.public(), derived_public0.public()); - } - - #[test] - fn test_seeds() { - let seed = H128::from_str("000102030405060708090a0b0c0d0e0f") - .expect("Seed should be valid H128") - .to_vec(); - - // private key from bitcoin test vector - // xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs - let test_private = H256::from_str("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35") - .expect("Private should be decoded ok"); - - let (private_seed, _) = derivation::seed_pair(&*seed); - - assert_eq!(private_seed, test_private); - } - - #[test] - fn test_vector_1() { - // xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7 - // H(0) - test_extended( - |secret| secret.derive(2147483648.into()), - H256::from_str("edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea") - .expect("Private should be decoded ok") - ); - } - - #[test] - fn test_vector_2() { - // xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs - // H(0)/1 - test_extended( - |secret| secret.derive(2147483648.into()).derive(1.into()), - H256::from_str("3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368") - .expect("Private should be decoded ok") - ); - } + use super::{derivation, Derivation, ExtendedKeyPair, ExtendedPublic, ExtendedSecret}; + use ethereum_types::{H128, H256}; + use secret::Secret; + use std::str::FromStr; + + fn master_chain_basic() -> (H256, H256) { + let seed = H128::from_str("000102030405060708090a0b0c0d0e0f") + .expect("Seed should be valid H128") + .to_vec(); + + derivation::seed_pair(&*seed) + } + + fn test_extended(f: F, test_private: H256) + where + F: Fn(ExtendedSecret) -> ExtendedSecret, + { + let (private_seed, chain_code) = master_chain_basic(); + let extended_secret = ExtendedSecret::with_code(Secret::from(private_seed.0), chain_code); + let derived = f(extended_secret); + assert_eq!(**derived.as_raw(), test_private); + } + + #[test] + fn smoky() { + let secret = + Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65") + .unwrap(); + let extended_secret = ExtendedSecret::with_code(secret.clone(), 0u64.into()); + + // hardened + assert_eq!(&**extended_secret.as_raw(), &*secret); + assert_eq!( + &**extended_secret.derive(2147483648.into()).as_raw(), + &"0927453daed47839608e414a3738dfad10aed17c459bbd9ab53f89b026c834b6".into() + ); + assert_eq!( + &**extended_secret.derive(2147483649.into()).as_raw(), + &"44238b6a29c6dcbe9b401364141ba11e2198c289a5fed243a1c11af35c19dc0f".into() + ); + + // normal + assert_eq!( + &**extended_secret.derive(0.into()).as_raw(), + &"bf6a74e3f7b36fc4c96a1e12f31abc817f9f5904f5a8fc27713163d1f0b713f6".into() + ); + assert_eq!( + &**extended_secret.derive(1.into()).as_raw(), + &"bd4fca9eb1f9c201e9448c1eecd66e302d68d4d313ce895b8c134f512205c1bc".into() + ); + assert_eq!( + &**extended_secret.derive(2.into()).as_raw(), + &"86932b542d6cab4d9c65490c7ef502d89ecc0e2a5f4852157649e3251e2a3268".into() + ); + + let extended_public = ExtendedPublic::from_secret(&extended_secret) + .expect("Extended public should be created"); + let derived_public = extended_public + .derive(0.into()) + .expect("First derivation of public should succeed"); + assert_eq!(&*derived_public.public(), &"f7b3244c96688f92372bfd4def26dc4151529747bab9f188a4ad34e141d47bd66522ff048bc6f19a0a4429b04318b1a8796c000265b4fa200dae5f6dda92dd94".into()); + + let keypair = ExtendedKeyPair::with_secret( + Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65") + .unwrap(), + 064.into(), + ); + assert_eq!( + &**keypair + .derive(2147483648u32.into()) + .expect("Derivation of keypair should succeed") + .secret() + .as_raw(), + &"edef54414c03196557cf73774bc97a645c9a1df2164ed34f0c2a78d1375a930c".into() + ); + } + + #[test] + fn h256_soft_match() { + let secret = + Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65") + .unwrap(); + let derivation_secret = + H256::from_str("51eaf04f9dbbc1417dc97e789edd0c37ecda88bac490434e367ea81b71b7b015") + .unwrap(); + + let extended_secret = ExtendedSecret::with_code(secret.clone(), 0u64.into()); + let extended_public = ExtendedPublic::from_secret(&extended_secret) + .expect("Extended public should be created"); + + let derived_secret0 = extended_secret.derive(Derivation::Soft(derivation_secret)); + let derived_public0 = extended_public + .derive(Derivation::Soft(derivation_secret)) + .expect("First derivation of public should succeed"); + + let public_from_secret0 = ExtendedPublic::from_secret(&derived_secret0) + .expect("Extended public should be created"); + + assert_eq!(public_from_secret0.public(), derived_public0.public()); + } + + #[test] + fn h256_hard() { + let secret = + Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65") + .unwrap(); + let derivation_secret = + H256::from_str("51eaf04f9dbbc1417dc97e789edd0c37ecda88bac490434e367ea81b71b7b015") + .unwrap(); + let extended_secret = ExtendedSecret::with_code(secret.clone(), 1u64.into()); + + assert_eq!( + &**extended_secret + .derive(Derivation::Hard(derivation_secret)) + .as_raw(), + &"2bc2d696fb744d77ff813b4a1ef0ad64e1e5188b622c54ba917acc5ebc7c5486".into() + ); + } + + #[test] + fn match_() { + let secret = + Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65") + .unwrap(); + let extended_secret = ExtendedSecret::with_code(secret.clone(), 1.into()); + let extended_public = ExtendedPublic::from_secret(&extended_secret) + .expect("Extended public should be created"); + + let derived_secret0 = extended_secret.derive(0.into()); + let derived_public0 = extended_public + .derive(0.into()) + .expect("First derivation of public should succeed"); + + let public_from_secret0 = ExtendedPublic::from_secret(&derived_secret0) + .expect("Extended public should be created"); + + assert_eq!(public_from_secret0.public(), derived_public0.public()); + } + + #[test] + fn test_seeds() { + let seed = H128::from_str("000102030405060708090a0b0c0d0e0f") + .expect("Seed should be valid H128") + .to_vec(); + + // private key from bitcoin test vector + // xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs + let test_private = + H256::from_str("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35") + .expect("Private should be decoded ok"); + + let (private_seed, _) = derivation::seed_pair(&*seed); + + assert_eq!(private_seed, test_private); + } + + #[test] + fn test_vector_1() { + // xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7 + // H(0) + test_extended( + |secret| secret.derive(2147483648.into()), + H256::from_str("edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea") + .expect("Private should be decoded ok"), + ); + } + + #[test] + fn test_vector_2() { + // xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs + // H(0)/1 + test_extended( + |secret| secret.derive(2147483648.into()).derive(1.into()), + H256::from_str("3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368") + .expect("Private should be decoded ok"), + ); + } } diff --git a/accounts/ethkey/src/keccak.rs b/accounts/ethkey/src/keccak.rs index 202c211933c..e3c67b34d13 100644 --- a/accounts/ethkey/src/keccak.rs +++ b/accounts/ethkey/src/keccak.rs @@ -1,31 +1,33 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use tiny_keccak::Keccak; pub trait Keccak256 { - fn keccak256(&self) -> T where T: Sized; + fn keccak256(&self) -> T + where + T: Sized; } impl Keccak256<[u8; 32]> for [u8] { - fn keccak256(&self) -> [u8; 32] { - let mut keccak = Keccak::new_keccak256(); - let mut result = [0u8; 32]; - keccak.update(self); - keccak.finalize(&mut result); - result - } + fn keccak256(&self) -> [u8; 32] { + let mut keccak = Keccak::new_keccak256(); + let mut result = [0u8; 32]; + keccak.update(self); + keccak.finalize(&mut result); + result + } } diff --git a/accounts/ethkey/src/keypair.rs b/accounts/ethkey/src/keypair.rs index 2919f0cfb79..7f97e03fb8d 100644 --- a/accounts/ethkey/src/keypair.rs +++ b/accounts/ethkey/src/keypair.rs @@ -1,115 +1,120 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::fmt; -use secp256k1::key; -use rustc_hex::ToHex; +use super::{Address, Error, Public, Secret, SECP256K1}; use keccak::Keccak256; -use super::{Secret, Public, Address, SECP256K1, Error}; +use rustc_hex::ToHex; +use secp256k1::key; +use std::fmt; pub fn public_to_address(public: &Public) -> Address { - let hash = public.keccak256(); - let mut result = Address::default(); - result.copy_from_slice(&hash[12..]); - result + let hash = public.keccak256(); + let mut result = Address::default(); + result.copy_from_slice(&hash[12..]); + result } #[derive(Debug, Clone, PartialEq)] /// secp256k1 key pair pub struct KeyPair { - secret: Secret, - public: Public, + secret: Secret, + public: Public, } impl fmt::Display for KeyPair { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - writeln!(f, "secret: {}", self.secret.to_hex())?; - writeln!(f, "public: {}", self.public.to_hex())?; - write!(f, "address: {}", self.address().to_hex()) - } + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + writeln!(f, "secret: {}", self.secret.to_hex())?; + writeln!(f, "public: {}", self.public.to_hex())?; + write!(f, "address: {}", self.address().to_hex()) + } } impl KeyPair { - /// Create a pair from secret key - pub fn from_secret(secret: Secret) -> Result { - let context = &SECP256K1; - let s: key::SecretKey = key::SecretKey::from_slice(context, &secret[..])?; - let pub_key = key::PublicKey::from_secret_key(context, &s)?; - let serialized = pub_key.serialize_vec(context, false); - - let mut public = Public::default(); - public.copy_from_slice(&serialized[1..65]); - - let keypair = KeyPair { - secret: secret, - public: public, - }; - - Ok(keypair) - } - - pub fn from_secret_slice(slice: &[u8]) -> Result { - Self::from_secret(Secret::from_unsafe_slice(slice)?) - } - - pub fn from_keypair(sec: key::SecretKey, publ: key::PublicKey) -> Self { - let context = &SECP256K1; - let serialized = publ.serialize_vec(context, false); - let secret = Secret::from(sec); - let mut public = Public::default(); - public.copy_from_slice(&serialized[1..65]); - - KeyPair { - secret: secret, - public: public, - } - } - - pub fn secret(&self) -> &Secret { - &self.secret - } - - pub fn public(&self) -> &Public { - &self.public - } - - pub fn address(&self) -> Address { - public_to_address(&self.public) - } + /// Create a pair from secret key + pub fn from_secret(secret: Secret) -> Result { + let context = &SECP256K1; + let s: key::SecretKey = key::SecretKey::from_slice(context, &secret[..])?; + let pub_key = key::PublicKey::from_secret_key(context, &s)?; + let serialized = pub_key.serialize_vec(context, false); + + let mut public = Public::default(); + public.copy_from_slice(&serialized[1..65]); + + let keypair = KeyPair { + secret: secret, + public: public, + }; + + Ok(keypair) + } + + pub fn from_secret_slice(slice: &[u8]) -> Result { + Self::from_secret(Secret::from_unsafe_slice(slice)?) + } + + pub fn from_keypair(sec: key::SecretKey, publ: key::PublicKey) -> Self { + let context = &SECP256K1; + let serialized = publ.serialize_vec(context, false); + let secret = Secret::from(sec); + let mut public = Public::default(); + public.copy_from_slice(&serialized[1..65]); + + KeyPair { + secret: secret, + public: public, + } + } + + pub fn secret(&self) -> &Secret { + &self.secret + } + + pub fn public(&self) -> &Public { + &self.public + } + + pub fn address(&self) -> Address { + public_to_address(&self.public) + } } #[cfg(test)] mod tests { - use std::str::FromStr; - use {KeyPair, Secret}; - - #[test] - fn from_secret() { - let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); - let _ = KeyPair::from_secret(secret).unwrap(); - } - - #[test] - fn keypair_display() { - let expected = + use std::str::FromStr; + use KeyPair; + use Secret; + + #[test] + fn from_secret() { + let secret = + Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65") + .unwrap(); + let _ = KeyPair::from_secret(secret).unwrap(); + } + + #[test] + fn keypair_display() { + let expected = "secret: a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65 public: 8ce0db0b0359ffc5866ba61903cc2518c3675ef2cf380a7e54bde7ea20e6fa1ab45b7617346cd11b7610001ee6ae5b0155c41cad9527cbcdff44ec67848943a4 address: 5b073e9233944b5e729e46d618f0d8edf3d9c34a".to_owned(); - let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); - let kp = KeyPair::from_secret(secret).unwrap(); - assert_eq!(format!("{}", kp), expected); - } + let secret = + Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65") + .unwrap(); + let kp = KeyPair::from_secret(secret).unwrap(); + assert_eq!(format!("{}", kp), expected); + } } diff --git a/accounts/ethkey/src/lib.rs b/accounts/ethkey/src/lib.rs index 5c58333c711..4aa436dc25d 100644 --- a/accounts/ethkey/src/lib.rs +++ b/accounts/ethkey/src/lib.rs @@ -1,26 +1,25 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . // #![warn(missing_docs)] -extern crate byteorder; extern crate edit_distance; -extern crate parity_crypto; extern crate ethereum_types; extern crate memzero; +extern crate parity_crypto; extern crate parity_wordlist; #[macro_use] extern crate quick_error; @@ -40,31 +39,33 @@ extern crate serde_derive; mod brain; mod brain_prefix; mod error; -mod keypair; +mod extended; mod keccak; +mod keypair; mod password; mod prefix; mod random; -mod signature; mod secret; -mod extended; +mod signature; pub mod brain_recover; pub mod crypto; pub mod math; -pub use self::parity_wordlist::Error as WordlistError; -pub use self::brain::Brain; -pub use self::brain_prefix::BrainPrefix; -pub use self::error::Error; -pub use self::keypair::{KeyPair, public_to_address}; -pub use self::math::public_is_valid; -pub use self::password::Password; -pub use self::prefix::Prefix; -pub use self::random::Random; -pub use self::signature::{sign, verify_public, verify_address, recover, Signature}; -pub use self::secret::Secret; -pub use self::extended::{ExtendedPublic, ExtendedSecret, ExtendedKeyPair, DerivationError, Derivation}; +pub use self::{ + brain::Brain, + brain_prefix::BrainPrefix, + error::Error, + extended::{Derivation, DerivationError, ExtendedKeyPair, ExtendedPublic, ExtendedSecret}, + keypair::{public_to_address, KeyPair}, + math::public_is_valid, + parity_wordlist::Error as WordlistError, + password::Password, + prefix::Prefix, + random::Random, + secret::Secret, + signature::{recover, sign, verify_address, verify_public, Signature}, +}; use ethereum_types::H256; @@ -72,7 +73,7 @@ pub use ethereum_types::{Address, Public}; pub type Message = H256; lazy_static! { - pub static ref SECP256K1: secp256k1::Secp256k1 = secp256k1::Secp256k1::new(); + pub static ref SECP256K1: secp256k1::Secp256k1 = secp256k1::Secp256k1::new(); } /// Uninstantiatable error type for infallible generators. @@ -81,8 +82,8 @@ pub enum Void {} /// Generates new keypair. pub trait Generator { - type Error; + type Error; - /// Should be called to generate new keypair. - fn generate(&mut self) -> Result; + /// Should be called to generate new keypair. + fn generate(&mut self) -> Result; } diff --git a/accounts/ethkey/src/math.rs b/accounts/ethkey/src/math.rs index 8c3fe650df6..5b61cd05dde 100644 --- a/accounts/ethkey/src/math.rs +++ b/accounts/ethkey/src/math.rs @@ -1,129 +1,134 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use super::{SECP256K1, Public, Secret, Error}; -use secp256k1::key; -use secp256k1::constants::{GENERATOR_X, GENERATOR_Y, CURVE_ORDER}; -use ethereum_types::{U256, H256}; +use super::{Error, Public, Secret, SECP256K1}; +use ethereum_types::{H256, U256}; +use secp256k1::{ + constants::{CURVE_ORDER, GENERATOR_X, GENERATOR_Y}, + key, +}; /// Whether the public key is valid. pub fn public_is_valid(public: &Public) -> bool { - to_secp256k1_public(public).ok() - .map_or(false, |p| p.is_valid()) + to_secp256k1_public(public) + .ok() + .map_or(false, |p| p.is_valid()) } /// Inplace multiply public key by secret key (EC point * scalar) pub fn public_mul_secret(public: &mut Public, secret: &Secret) -> Result<(), Error> { - let key_secret = secret.to_secp256k1_secret()?; - let mut key_public = to_secp256k1_public(public)?; - key_public.mul_assign(&SECP256K1, &key_secret)?; - set_public(public, &key_public); - Ok(()) + let key_secret = secret.to_secp256k1_secret()?; + let mut key_public = to_secp256k1_public(public)?; + key_public.mul_assign(&SECP256K1, &key_secret)?; + set_public(public, &key_public); + Ok(()) } /// Inplace add one public key to another (EC point + EC point) pub fn public_add(public: &mut Public, other: &Public) -> Result<(), Error> { - let mut key_public = to_secp256k1_public(public)?; - let other_public = to_secp256k1_public(other)?; - key_public.add_assign(&SECP256K1, &other_public)?; - set_public(public, &key_public); - Ok(()) + let mut key_public = to_secp256k1_public(public)?; + let other_public = to_secp256k1_public(other)?; + key_public.add_assign(&SECP256K1, &other_public)?; + set_public(public, &key_public); + Ok(()) } /// Inplace sub one public key from another (EC point - EC point) pub fn public_sub(public: &mut Public, other: &Public) -> Result<(), Error> { - let mut key_neg_other = to_secp256k1_public(other)?; - key_neg_other.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; + let mut key_neg_other = to_secp256k1_public(other)?; + key_neg_other.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; - let mut key_public = to_secp256k1_public(public)?; - key_public.add_assign(&SECP256K1, &key_neg_other)?; - set_public(public, &key_public); - Ok(()) + let mut key_public = to_secp256k1_public(public)?; + key_public.add_assign(&SECP256K1, &key_neg_other)?; + set_public(public, &key_public); + Ok(()) } /// Replace public key with its negation (EC point = - EC point) pub fn public_negate(public: &mut Public) -> Result<(), Error> { - let mut key_public = to_secp256k1_public(public)?; - key_public.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; - set_public(public, &key_public); - Ok(()) + let mut key_public = to_secp256k1_public(public)?; + key_public.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; + set_public(public, &key_public); + Ok(()) } /// Return base point of secp256k1 pub fn generation_point() -> Public { - let mut public_sec_raw = [0u8; 65]; - public_sec_raw[0] = 4; - public_sec_raw[1..33].copy_from_slice(&GENERATOR_X); - public_sec_raw[33..65].copy_from_slice(&GENERATOR_Y); - - let public_key = key::PublicKey::from_slice(&SECP256K1, &public_sec_raw) - .expect("constructing using predefined constants; qed"); - let mut public = Public::default(); - set_public(&mut public, &public_key); - public + let mut public_sec_raw = [0u8; 65]; + public_sec_raw[0] = 4; + public_sec_raw[1..33].copy_from_slice(&GENERATOR_X); + public_sec_raw[33..65].copy_from_slice(&GENERATOR_Y); + + let public_key = key::PublicKey::from_slice(&SECP256K1, &public_sec_raw) + .expect("constructing using predefined constants; qed"); + let mut public = Public::default(); + set_public(&mut public, &public_key); + public } /// Return secp256k1 elliptic curve order pub fn curve_order() -> U256 { - H256::from_slice(&CURVE_ORDER).into() + H256::from_slice(&CURVE_ORDER).into() } fn to_secp256k1_public(public: &Public) -> Result { - let public_data = { - let mut temp = [4u8; 65]; - (&mut temp[1..65]).copy_from_slice(&public[0..64]); - temp - }; + let public_data = { + let mut temp = [4u8; 65]; + (&mut temp[1..65]).copy_from_slice(&public[0..64]); + temp + }; - Ok(key::PublicKey::from_slice(&SECP256K1, &public_data)?) + Ok(key::PublicKey::from_slice(&SECP256K1, &public_data)?) } fn set_public(public: &mut Public, key_public: &key::PublicKey) { - let key_public_serialized = key_public.serialize_vec(&SECP256K1, false); - public.copy_from_slice(&key_public_serialized[1..65]); + let key_public_serialized = key_public.serialize_vec(&SECP256K1, false); + public.copy_from_slice(&key_public_serialized[1..65]); } #[cfg(test)] mod tests { - use super::super::{Random, Generator}; - use super::{public_add, public_sub}; + use super::{ + super::{Generator, Random}, + public_add, public_sub, + }; - #[test] - fn public_addition_is_commutative() { - let public1 = Random.generate().unwrap().public().clone(); - let public2 = Random.generate().unwrap().public().clone(); + #[test] + fn public_addition_is_commutative() { + let public1 = Random.generate().unwrap().public().clone(); + let public2 = Random.generate().unwrap().public().clone(); - let mut left = public1.clone(); - public_add(&mut left, &public2).unwrap(); + let mut left = public1.clone(); + public_add(&mut left, &public2).unwrap(); - let mut right = public2.clone(); - public_add(&mut right, &public1).unwrap(); + let mut right = public2.clone(); + public_add(&mut right, &public1).unwrap(); - assert_eq!(left, right); - } + assert_eq!(left, right); + } - #[test] - fn public_addition_is_reversible_with_subtraction() { - let public1 = Random.generate().unwrap().public().clone(); - let public2 = Random.generate().unwrap().public().clone(); + #[test] + fn public_addition_is_reversible_with_subtraction() { + let public1 = Random.generate().unwrap().public().clone(); + let public2 = Random.generate().unwrap().public().clone(); - let mut sum = public1.clone(); - public_add(&mut sum, &public2).unwrap(); - public_sub(&mut sum, &public2).unwrap(); + let mut sum = public1.clone(); + public_add(&mut sum, &public2).unwrap(); + public_sub(&mut sum, &public2).unwrap(); - assert_eq!(sum, public1); - } + assert_eq!(sum, public1); + } } diff --git a/accounts/ethkey/src/password.rs b/accounts/ethkey/src/password.rs index 6ad665e396c..9b3aa07d741 100644 --- a/accounts/ethkey/src/password.rs +++ b/accounts/ethkey/src/password.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use std::{fmt, ptr}; @@ -20,40 +20,40 @@ use std::{fmt, ptr}; pub struct Password(String); impl fmt::Debug for Password { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Password(******)") } } impl Password { - pub fn as_bytes(&self) -> &[u8] { - self.0.as_bytes() - } + pub fn as_bytes(&self) -> &[u8] { + self.0.as_bytes() + } - pub fn as_str(&self) -> &str { - self.0.as_str() - } + pub fn as_str(&self) -> &str { + self.0.as_str() + } } // Custom drop impl to zero out memory. impl Drop for Password { - fn drop(&mut self) { - unsafe { - for byte_ref in self.0.as_mut_vec() { - ptr::write_volatile(byte_ref, 0) - } - } - } + fn drop(&mut self) { + unsafe { + for byte_ref in self.0.as_mut_vec() { + ptr::write_volatile(byte_ref, 0) + } + } + } } impl From for Password { - fn from(s: String) -> Password { - Password(s) - } + fn from(s: String) -> Password { + Password(s) + } } impl<'a> From<&'a str> for Password { - fn from(s: &'a str) -> Password { - Password::from(String::from(s)) - } + fn from(s: &'a str) -> Password { + Password::from(String::from(s)) + } } diff --git a/accounts/ethkey/src/prefix.rs b/accounts/ethkey/src/prefix.rs index 6695e93c50e..61382e721f3 100644 --- a/accounts/ethkey/src/prefix.rs +++ b/accounts/ethkey/src/prefix.rs @@ -1,59 +1,62 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use super::{Random, Generator, KeyPair, Error}; +use super::{Error, Generator, KeyPair, Random}; /// Tries to find keypair with address starting with given prefix. pub struct Prefix { - prefix: Vec, - iterations: usize, + prefix: Vec, + iterations: usize, } impl Prefix { - pub fn new(prefix: Vec, iterations: usize) -> Self { - Prefix { - prefix: prefix, - iterations: iterations, - } - } + pub fn new(prefix: Vec, iterations: usize) -> Self { + Prefix { + prefix: prefix, + iterations: iterations, + } + } } impl Generator for Prefix { - type Error = Error; - - fn generate(&mut self) -> Result { - for _ in 0..self.iterations { - let keypair = Random.generate()?; - if keypair.address().starts_with(&self.prefix) { - return Ok(keypair) - } - } - - Err(Error::Custom("Could not find keypair".into())) - } + type Error = Error; + + fn generate(&mut self) -> Result { + for _ in 0..self.iterations { + let keypair = Random.generate()?; + if keypair.address().starts_with(&self.prefix) { + return Ok(keypair); + } + } + + Err(Error::Custom("Could not find keypair".into())) + } } #[cfg(test)] mod tests { - use {Generator, Prefix}; - - #[test] - fn prefix_generator() { - let prefix = vec![0xffu8]; - let keypair = Prefix::new(prefix.clone(), usize::max_value()).generate().unwrap(); - assert!(keypair.address().starts_with(&prefix)); - } + use Generator; + use Prefix; + + #[test] + fn prefix_generator() { + let prefix = vec![0xffu8]; + let keypair = Prefix::new(prefix.clone(), usize::max_value()) + .generate() + .unwrap(); + assert!(keypair.address().starts_with(&prefix)); + } } diff --git a/accounts/ethkey/src/random.rs b/accounts/ethkey/src/random.rs index 1966cb361b6..6bf833d276f 100644 --- a/accounts/ethkey/src/random.rs +++ b/accounts/ethkey/src/random.rs @@ -1,44 +1,45 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use rand::os::OsRng; use super::{Generator, KeyPair, SECP256K1}; +use rand::os::OsRng; /// Randomly generates new keypair, instantiating the RNG each time. pub struct Random; impl Generator for Random { - type Error = ::std::io::Error; - - fn generate(&mut self) -> Result { - let mut rng = OsRng::new()?; - match rng.generate() { - Ok(pair) => Ok(pair), - Err(void) => match void {}, // LLVM unreachable - } - } + type Error = ::std::io::Error; + + fn generate(&mut self) -> Result { + let mut rng = OsRng::new()?; + match rng.generate() { + Ok(pair) => Ok(pair), + Err(void) => match void {}, // LLVM unreachable + } + } } impl Generator for OsRng { - type Error = ::Void; + type Error = ::Void; - fn generate(&mut self) -> Result { - let (sec, publ) = SECP256K1.generate_keypair(self) - .expect("context always created with full capabilities; qed"); + fn generate(&mut self) -> Result { + let (sec, publ) = SECP256K1 + .generate_keypair(self) + .expect("context always created with full capabilities; qed"); - Ok(KeyPair::from_keypair(sec, publ)) - } + Ok(KeyPair::from_keypair(sec, publ)) + } } diff --git a/accounts/ethkey/src/secret.rs b/accounts/ethkey/src/secret.rs index 84e849cabc5..ab78e494d6e 100644 --- a/accounts/ethkey/src/secret.rs +++ b/accounts/ethkey/src/secret.rs @@ -1,298 +1,322 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::fmt; -use std::ops::Deref; -use std::str::FromStr; -use rustc_hex::ToHex; -use secp256k1::constants::{SECRET_KEY_SIZE as SECP256K1_SECRET_KEY_SIZE}; -use secp256k1::key; use ethereum_types::H256; use memzero::Memzero; -use {Error, SECP256K1}; +use rustc_hex::ToHex; +use secp256k1::{constants::SECRET_KEY_SIZE as SECP256K1_SECRET_KEY_SIZE, key}; +use std::{fmt, ops::Deref, str::FromStr}; +use Error; +use SECP256K1; #[derive(Clone, PartialEq, Eq)] pub struct Secret { - inner: Memzero, + inner: Memzero, } impl ToHex for Secret { - fn to_hex(&self) -> String { - format!("{:x}", *self.inner) - } + fn to_hex(&self) -> String { + format!("{:x}", *self.inner) + } } impl fmt::LowerHex for Secret { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - self.inner.fmt(fmt) - } + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + self.inner.fmt(fmt) + } } impl fmt::Debug for Secret { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - self.inner.fmt(fmt) - } + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + self.inner.fmt(fmt) + } } impl fmt::Display for Secret { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "Secret: 0x{:x}{:x}..{:x}{:x}", self.inner[0], self.inner[1], self.inner[30], self.inner[31]) - } + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!( + fmt, + "Secret: 0x{:x}{:x}..{:x}{:x}", + self.inner[0], self.inner[1], self.inner[30], self.inner[31] + ) + } } impl Secret { - /// Creates a `Secret` from the given slice, returning `None` if the slice length != 32. - pub fn from_slice(key: &[u8]) -> Option { - if key.len() != 32 { - return None - } - let mut h = H256::default(); - h.copy_from_slice(&key[0..32]); - Some(Secret { inner: Memzero::from(h) }) - } - - /// Creates zero key, which is invalid for crypto operations, but valid for math operation. - pub fn zero() -> Self { - Secret { inner: Memzero::from(H256::default()) } - } - - /// Imports and validates the key. - pub fn from_unsafe_slice(key: &[u8]) -> Result { - let secret = key::SecretKey::from_slice(&super::SECP256K1, key)?; - Ok(secret.into()) - } - - /// Checks validity of this key. - pub fn check_validity(&self) -> Result<(), Error> { - self.to_secp256k1_secret().map(|_| ()) - } - - /// Inplace add one secret key to another (scalar + scalar) - pub fn add(&mut self, other: &Secret) -> Result<(), Error> { - match (self.is_zero(), other.is_zero()) { - (true, true) | (false, true) => Ok(()), - (true, false) => { - *self = other.clone(); - Ok(()) - }, - (false, false) => { - let mut key_secret = self.to_secp256k1_secret()?; - let other_secret = other.to_secp256k1_secret()?; - key_secret.add_assign(&SECP256K1, &other_secret)?; - - *self = key_secret.into(); - Ok(()) - }, - } - } - - /// Inplace subtract one secret key from another (scalar - scalar) - pub fn sub(&mut self, other: &Secret) -> Result<(), Error> { - match (self.is_zero(), other.is_zero()) { - (true, true) | (false, true) => Ok(()), - (true, false) => { - *self = other.clone(); - self.neg() - }, - (false, false) => { - let mut key_secret = self.to_secp256k1_secret()?; - let mut other_secret = other.to_secp256k1_secret()?; - other_secret.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; - key_secret.add_assign(&SECP256K1, &other_secret)?; - - *self = key_secret.into(); - Ok(()) - }, - } - } - - /// Inplace decrease secret key (scalar - 1) - pub fn dec(&mut self) -> Result<(), Error> { - match self.is_zero() { - true => { - *self = key::MINUS_ONE_KEY.into(); - Ok(()) - }, - false => { - let mut key_secret = self.to_secp256k1_secret()?; - key_secret.add_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; - - *self = key_secret.into(); - Ok(()) - }, - } - } - - /// Inplace multiply one secret key to another (scalar * scalar) - pub fn mul(&mut self, other: &Secret) -> Result<(), Error> { - match (self.is_zero(), other.is_zero()) { - (true, true) | (true, false) => Ok(()), - (false, true) => { - *self = Self::zero(); - Ok(()) - }, - (false, false) => { - let mut key_secret = self.to_secp256k1_secret()?; - let other_secret = other.to_secp256k1_secret()?; - key_secret.mul_assign(&SECP256K1, &other_secret)?; - - *self = key_secret.into(); - Ok(()) - }, - } - } - - /// Inplace negate secret key (-scalar) - pub fn neg(&mut self) -> Result<(), Error> { - match self.is_zero() { - true => Ok(()), - false => { - let mut key_secret = self.to_secp256k1_secret()?; - key_secret.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; - - *self = key_secret.into(); - Ok(()) - }, - } - } - - /// Inplace inverse secret key (1 / scalar) - pub fn inv(&mut self) -> Result<(), Error> { - let mut key_secret = self.to_secp256k1_secret()?; - key_secret.inv_assign(&SECP256K1)?; - - *self = key_secret.into(); - Ok(()) - } - - /// Compute power of secret key inplace (secret ^ pow). - /// This function is not intended to be used with large powers. - pub fn pow(&mut self, pow: usize) -> Result<(), Error> { - if self.is_zero() { - return Ok(()); - } - - match pow { - 0 => *self = key::ONE_KEY.into(), - 1 => (), - _ => { - let c = self.clone(); - for _ in 1..pow { - self.mul(&c)?; - } - }, - } - - Ok(()) - } - - /// Create `secp256k1::key::SecretKey` based on this secret - pub fn to_secp256k1_secret(&self) -> Result { - Ok(key::SecretKey::from_slice(&SECP256K1, &self[..])?) - } + /// Creates a `Secret` from the given slice, returning `None` if the slice length != 32. + pub fn from_slice(key: &[u8]) -> Option { + if key.len() != 32 { + return None; + } + let mut h = H256::default(); + h.copy_from_slice(&key[0..32]); + Some(Secret { + inner: Memzero::from(h), + }) + } + + /// Creates zero key, which is invalid for crypto operations, but valid for math operation. + pub fn zero() -> Self { + Secret { + inner: Memzero::from(H256::default()), + } + } + + /// Imports and validates the key. + pub fn from_unsafe_slice(key: &[u8]) -> Result { + let secret = key::SecretKey::from_slice(&super::SECP256K1, key)?; + Ok(secret.into()) + } + + /// Checks validity of this key. + pub fn check_validity(&self) -> Result<(), Error> { + self.to_secp256k1_secret().map(|_| ()) + } + + /// Inplace add one secret key to another (scalar + scalar) + pub fn add(&mut self, other: &Secret) -> Result<(), Error> { + match (self.is_zero(), other.is_zero()) { + (true, true) | (false, true) => Ok(()), + (true, false) => { + *self = other.clone(); + Ok(()) + } + (false, false) => { + let mut key_secret = self.to_secp256k1_secret()?; + let other_secret = other.to_secp256k1_secret()?; + key_secret.add_assign(&SECP256K1, &other_secret)?; + + *self = key_secret.into(); + Ok(()) + } + } + } + + /// Inplace subtract one secret key from another (scalar - scalar) + pub fn sub(&mut self, other: &Secret) -> Result<(), Error> { + match (self.is_zero(), other.is_zero()) { + (true, true) | (false, true) => Ok(()), + (true, false) => { + *self = other.clone(); + self.neg() + } + (false, false) => { + let mut key_secret = self.to_secp256k1_secret()?; + let mut other_secret = other.to_secp256k1_secret()?; + other_secret.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; + key_secret.add_assign(&SECP256K1, &other_secret)?; + + *self = key_secret.into(); + Ok(()) + } + } + } + + /// Inplace decrease secret key (scalar - 1) + pub fn dec(&mut self) -> Result<(), Error> { + match self.is_zero() { + true => { + *self = key::MINUS_ONE_KEY.into(); + Ok(()) + } + false => { + let mut key_secret = self.to_secp256k1_secret()?; + key_secret.add_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; + + *self = key_secret.into(); + Ok(()) + } + } + } + + /// Inplace multiply one secret key to another (scalar * scalar) + pub fn mul(&mut self, other: &Secret) -> Result<(), Error> { + match (self.is_zero(), other.is_zero()) { + (true, true) | (true, false) => Ok(()), + (false, true) => { + *self = Self::zero(); + Ok(()) + } + (false, false) => { + let mut key_secret = self.to_secp256k1_secret()?; + let other_secret = other.to_secp256k1_secret()?; + key_secret.mul_assign(&SECP256K1, &other_secret)?; + + *self = key_secret.into(); + Ok(()) + } + } + } + + /// Inplace negate secret key (-scalar) + pub fn neg(&mut self) -> Result<(), Error> { + match self.is_zero() { + true => Ok(()), + false => { + let mut key_secret = self.to_secp256k1_secret()?; + key_secret.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; + + *self = key_secret.into(); + Ok(()) + } + } + } + + /// Inplace inverse secret key (1 / scalar) + pub fn inv(&mut self) -> Result<(), Error> { + let mut key_secret = self.to_secp256k1_secret()?; + key_secret.inv_assign(&SECP256K1)?; + + *self = key_secret.into(); + Ok(()) + } + + /// Compute power of secret key inplace (secret ^ pow). + /// This function is not intended to be used with large powers. + pub fn pow(&mut self, pow: usize) -> Result<(), Error> { + if self.is_zero() { + return Ok(()); + } + + match pow { + 0 => *self = key::ONE_KEY.into(), + 1 => (), + _ => { + let c = self.clone(); + for _ in 1..pow { + self.mul(&c)?; + } + } + } + + Ok(()) + } + + /// Create `secp256k1::key::SecretKey` based on this secret + pub fn to_secp256k1_secret(&self) -> Result { + Ok(key::SecretKey::from_slice(&SECP256K1, &self[..])?) + } } impl FromStr for Secret { - type Err = Error; - fn from_str(s: &str) -> Result { - Ok(H256::from_str(s).map_err(|e| Error::Custom(format!("{:?}", e)))?.into()) - } + type Err = Error; + fn from_str(s: &str) -> Result { + Ok(H256::from_str(s) + .map_err(|e| Error::Custom(format!("{:?}", e)))? + .into()) + } } impl From<[u8; 32]> for Secret { - fn from(k: [u8; 32]) -> Self { - Secret { inner: Memzero::from(H256(k)) } - } + fn from(k: [u8; 32]) -> Self { + Secret { + inner: Memzero::from(H256(k)), + } + } } impl From for Secret { - fn from(s: H256) -> Self { - s.0.into() - } + fn from(s: H256) -> Self { + s.0.into() + } } impl From<&'static str> for Secret { - fn from(s: &'static str) -> Self { - s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!(Self), s)) - } + fn from(s: &'static str) -> Self { + s.parse().expect(&format!( + "invalid string literal for {}: '{}'", + stringify!(Self), + s + )) + } } impl From for Secret { - fn from(key: key::SecretKey) -> Self { - let mut a = [0; SECP256K1_SECRET_KEY_SIZE]; - a.copy_from_slice(&key[0 .. SECP256K1_SECRET_KEY_SIZE]); - a.into() - } + fn from(key: key::SecretKey) -> Self { + let mut a = [0; SECP256K1_SECRET_KEY_SIZE]; + a.copy_from_slice(&key[0..SECP256K1_SECRET_KEY_SIZE]); + a.into() + } } impl Deref for Secret { - type Target = H256; + type Target = H256; - fn deref(&self) -> &Self::Target { - &self.inner - } + fn deref(&self) -> &Self::Target { + &self.inner + } } #[cfg(test)] mod tests { - use std::str::FromStr; - use super::super::{Random, Generator}; - use super::Secret; - - #[test] - fn multiplicating_secret_inversion_with_secret_gives_one() { - let secret = Random.generate().unwrap().secret().clone(); - let mut inversion = secret.clone(); - inversion.inv().unwrap(); - inversion.mul(&secret).unwrap(); - assert_eq!(inversion, Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap()); - } - - #[test] - fn secret_inversion_is_reversible_with_inversion() { - let secret = Random.generate().unwrap().secret().clone(); - let mut inversion = secret.clone(); - inversion.inv().unwrap(); - inversion.inv().unwrap(); - assert_eq!(inversion, secret); - } - - #[test] - fn secret_pow() { - let secret = Random.generate().unwrap().secret().clone(); - - let mut pow0 = secret.clone(); - pow0.pow(0).unwrap(); - assert_eq!(pow0, Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap()); - - let mut pow1 = secret.clone(); - pow1.pow(1).unwrap(); - assert_eq!(pow1, secret); - - let mut pow2 = secret.clone(); - pow2.pow(2).unwrap(); - let mut pow2_expected = secret.clone(); - pow2_expected.mul(&secret).unwrap(); - assert_eq!(pow2, pow2_expected); - - let mut pow3 = secret.clone(); - pow3.pow(3).unwrap(); - let mut pow3_expected = secret.clone(); - pow3_expected.mul(&secret).unwrap(); - pow3_expected.mul(&secret).unwrap(); - assert_eq!(pow3, pow3_expected); - } + use super::{ + super::{Generator, Random}, + Secret, + }; + use std::str::FromStr; + + #[test] + fn multiplicating_secret_inversion_with_secret_gives_one() { + let secret = Random.generate().unwrap().secret().clone(); + let mut inversion = secret.clone(); + inversion.inv().unwrap(); + inversion.mul(&secret).unwrap(); + assert_eq!( + inversion, + Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001") + .unwrap() + ); + } + + #[test] + fn secret_inversion_is_reversible_with_inversion() { + let secret = Random.generate().unwrap().secret().clone(); + let mut inversion = secret.clone(); + inversion.inv().unwrap(); + inversion.inv().unwrap(); + assert_eq!(inversion, secret); + } + + #[test] + fn secret_pow() { + let secret = Random.generate().unwrap().secret().clone(); + + let mut pow0 = secret.clone(); + pow0.pow(0).unwrap(); + assert_eq!( + pow0, + Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001") + .unwrap() + ); + + let mut pow1 = secret.clone(); + pow1.pow(1).unwrap(); + assert_eq!(pow1, secret); + + let mut pow2 = secret.clone(); + pow2.pow(2).unwrap(); + let mut pow2_expected = secret.clone(); + pow2_expected.mul(&secret).unwrap(); + assert_eq!(pow2, pow2_expected); + + let mut pow3 = secret.clone(); + pow3.pow(3).unwrap(); + let mut pow3_expected = secret.clone(); + pow3_expected.mul(&secret).unwrap(); + pow3_expected.mul(&secret).unwrap(); + assert_eq!(pow3, pow3_expected); + } } diff --git a/accounts/ethkey/src/signature.rs b/accounts/ethkey/src/signature.rs index cc712df6904..fdb7b4c106b 100644 --- a/accounts/ethkey/src/signature.rs +++ b/accounts/ethkey/src/signature.rs @@ -1,294 +1,325 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::ops::{Deref, DerefMut}; -use std::cmp::PartialEq; -use std::fmt; -use std::str::FromStr; -use std::hash::{Hash, Hasher}; -use secp256k1::{Message as SecpMessage, RecoverableSignature, RecoveryId, Error as SecpError}; -use secp256k1::key::{SecretKey, PublicKey}; -use rustc_hex::{ToHex, FromHex}; -use ethereum_types::{H520, H256}; -use {Secret, Public, SECP256K1, Error, Message, public_to_address, Address}; +// along with OpenEthereum. If not, see . + +use ethereum_types::{H256, H520}; +use public_to_address; +use rustc_hex::{FromHex, ToHex}; +use secp256k1::{ + key::{PublicKey, SecretKey}, + Error as SecpError, Message as SecpMessage, RecoverableSignature, RecoveryId, +}; +use std::{ + cmp::PartialEq, + fmt, + hash::{Hash, Hasher}, + ops::{Deref, DerefMut}, + str::FromStr, +}; +use Address; +use Error; +use Message; +use Public; +use Secret; +use SECP256K1; /// Signature encoded as RSV components #[repr(C)] pub struct Signature([u8; 65]); impl Signature { - /// Get a slice into the 'r' portion of the data. - pub fn r(&self) -> &[u8] { - &self.0[0..32] - } - - /// Get a slice into the 's' portion of the data. - pub fn s(&self) -> &[u8] { - &self.0[32..64] - } - - /// Get the recovery byte. - pub fn v(&self) -> u8 { - self.0[64] - } - - /// Encode the signature into RSV array (V altered to be in "Electrum" notation). - pub fn into_electrum(mut self) -> [u8; 65] { - self.0[64] += 27; - self.0 - } - - /// Parse bytes as a signature encoded as RSV (V in "Electrum" notation). - /// May return empty (invalid) signature if given data has invalid length. - pub fn from_electrum(data: &[u8]) -> Self { - if data.len() != 65 || data[64] < 27 { - // fallback to empty (invalid) signature - return Signature::default(); - } - - let mut sig = [0u8; 65]; - sig.copy_from_slice(data); - sig[64] -= 27; - Signature(sig) - } - - /// Create a signature object from the sig. - pub fn from_rsv(r: &H256, s: &H256, v: u8) -> Self { - let mut sig = [0u8; 65]; - sig[0..32].copy_from_slice(&r); - sig[32..64].copy_from_slice(&s); - sig[64] = v; - Signature(sig) - } - - /// Check if this is a "low" signature. - pub fn is_low_s(&self) -> bool { - H256::from_slice(self.s()) <= "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0".into() - } - - /// Check if each component of the signature is in range. - pub fn is_valid(&self) -> bool { - self.v() <= 1 && - H256::from_slice(self.r()) < "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into() && - H256::from_slice(self.r()) >= 1.into() && - H256::from_slice(self.s()) < "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into() && - H256::from_slice(self.s()) >= 1.into() - } + /// Get a slice into the 'r' portion of the data. + pub fn r(&self) -> &[u8] { + &self.0[0..32] + } + + /// Get a slice into the 's' portion of the data. + pub fn s(&self) -> &[u8] { + &self.0[32..64] + } + + /// Get the recovery byte. + pub fn v(&self) -> u8 { + self.0[64] + } + + /// Encode the signature into RSV array (V altered to be in "Electrum" notation). + pub fn into_electrum(mut self) -> [u8; 65] { + self.0[64] += 27; + self.0 + } + + /// Parse bytes as a signature encoded as RSV (V in "Electrum" notation). + /// May return empty (invalid) signature if given data has invalid length. + pub fn from_electrum(data: &[u8]) -> Self { + if data.len() != 65 || data[64] < 27 { + // fallback to empty (invalid) signature + return Signature::default(); + } + + let mut sig = [0u8; 65]; + sig.copy_from_slice(data); + sig[64] -= 27; + Signature(sig) + } + + /// Create a signature object from the sig. + pub fn from_rsv(r: &H256, s: &H256, v: u8) -> Self { + let mut sig = [0u8; 65]; + sig[0..32].copy_from_slice(&r); + sig[32..64].copy_from_slice(&s); + sig[64] = v; + Signature(sig) + } + + /// Check if this is a "low" signature. + pub fn is_low_s(&self) -> bool { + H256::from_slice(self.s()) + <= "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0".into() + } + + /// Check if each component of the signature is in range. + pub fn is_valid(&self) -> bool { + self.v() <= 1 + && H256::from_slice(self.r()) + < "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into() + && H256::from_slice(self.r()) >= 1.into() + && H256::from_slice(self.s()) + < "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into() + && H256::from_slice(self.s()) >= 1.into() + } } // manual implementation large arrays don't have trait impls by default. // remove when integer generics exist impl PartialEq for Signature { - fn eq(&self, other: &Self) -> bool { - &self.0[..] == &other.0[..] - } + fn eq(&self, other: &Self) -> bool { + &self.0[..] == &other.0[..] + } } // manual implementation required in Rust 1.13+, see `std::cmp::AssertParamIsEq`. -impl Eq for Signature { } +impl Eq for Signature {} // also manual for the same reason, but the pretty printing might be useful. impl fmt::Debug for Signature { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - f.debug_struct("Signature") - .field("r", &self.0[0..32].to_hex()) - .field("s", &self.0[32..64].to_hex()) - .field("v", &self.0[64..65].to_hex()) - .finish() - } + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + f.debug_struct("Signature") + .field("r", &self.0[0..32].to_hex()) + .field("s", &self.0[32..64].to_hex()) + .field("v", &self.0[64..65].to_hex()) + .finish() + } } impl fmt::Display for Signature { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "{}", self.to_hex()) - } + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}", self.to_hex()) + } } impl FromStr for Signature { - type Err = Error; - - fn from_str(s: &str) -> Result { - match s.from_hex() { - Ok(ref hex) if hex.len() == 65 => { - let mut data = [0; 65]; - data.copy_from_slice(&hex[0..65]); - Ok(Signature(data)) - }, - _ => Err(Error::InvalidSignature) - } - } + type Err = Error; + + fn from_str(s: &str) -> Result { + match s.from_hex() { + Ok(ref hex) if hex.len() == 65 => { + let mut data = [0; 65]; + data.copy_from_slice(&hex[0..65]); + Ok(Signature(data)) + } + _ => Err(Error::InvalidSignature), + } + } } impl Default for Signature { - fn default() -> Self { - Signature([0; 65]) - } + fn default() -> Self { + Signature([0; 65]) + } } impl Hash for Signature { fn hash(&self, state: &mut H) { - H520::from(self.0).hash(state); + H520::from(self.0).hash(state); } } impl Clone for Signature { fn clone(&self) -> Self { - Signature(self.0) + Signature(self.0) } } impl From<[u8; 65]> for Signature { - fn from(s: [u8; 65]) -> Self { - Signature(s) - } + fn from(s: [u8; 65]) -> Self { + Signature(s) + } } impl Into<[u8; 65]> for Signature { - fn into(self) -> [u8; 65] { - self.0 - } + fn into(self) -> [u8; 65] { + self.0 + } } impl From for H520 { - fn from(s: Signature) -> Self { - H520::from(s.0) - } + fn from(s: Signature) -> Self { + H520::from(s.0) + } } impl From for Signature { - fn from(bytes: H520) -> Self { - Signature(bytes.into()) - } + fn from(bytes: H520) -> Self { + Signature(bytes.into()) + } } impl Deref for Signature { - type Target = [u8; 65]; + type Target = [u8; 65]; - fn deref(&self) -> &Self::Target { - &self.0 - } + fn deref(&self) -> &Self::Target { + &self.0 + } } impl DerefMut for Signature { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } } pub fn sign(secret: &Secret, message: &Message) -> Result { - let context = &SECP256K1; - let sec = SecretKey::from_slice(context, &secret)?; - let s = context.sign_recoverable(&SecpMessage::from_slice(&message[..])?, &sec)?; - let (rec_id, data) = s.serialize_compact(context); - let mut data_arr = [0; 65]; - - // no need to check if s is low, it always is - data_arr[0..64].copy_from_slice(&data[0..64]); - data_arr[64] = rec_id.to_i32() as u8; - Ok(Signature(data_arr)) + let context = &SECP256K1; + let sec = SecretKey::from_slice(context, &secret)?; + let s = context.sign_recoverable(&SecpMessage::from_slice(&message[..])?, &sec)?; + let (rec_id, data) = s.serialize_compact(context); + let mut data_arr = [0; 65]; + + // no need to check if s is low, it always is + data_arr[0..64].copy_from_slice(&data[0..64]); + data_arr[64] = rec_id.to_i32() as u8; + Ok(Signature(data_arr)) } -pub fn verify_public(public: &Public, signature: &Signature, message: &Message) -> Result { - let context = &SECP256K1; - let rsig = RecoverableSignature::from_compact(context, &signature[0..64], RecoveryId::from_i32(signature[64] as i32)?)?; - let sig = rsig.to_standard(context); - - let pdata: [u8; 65] = { - let mut temp = [4u8; 65]; - temp[1..65].copy_from_slice(&**public); - temp - }; - - let publ = PublicKey::from_slice(context, &pdata)?; - match context.verify(&SecpMessage::from_slice(&message[..])?, &sig, &publ) { - Ok(_) => Ok(true), - Err(SecpError::IncorrectSignature) => Ok(false), - Err(x) => Err(Error::from(x)) - } +pub fn verify_public( + public: &Public, + signature: &Signature, + message: &Message, +) -> Result { + let context = &SECP256K1; + let rsig = RecoverableSignature::from_compact( + context, + &signature[0..64], + RecoveryId::from_i32(signature[64] as i32)?, + )?; + let sig = rsig.to_standard(context); + + let pdata: [u8; 65] = { + let mut temp = [4u8; 65]; + temp[1..65].copy_from_slice(&**public); + temp + }; + + let publ = PublicKey::from_slice(context, &pdata)?; + match context.verify(&SecpMessage::from_slice(&message[..])?, &sig, &publ) { + Ok(_) => Ok(true), + Err(SecpError::IncorrectSignature) => Ok(false), + Err(x) => Err(Error::from(x)), + } } -pub fn verify_address(address: &Address, signature: &Signature, message: &Message) -> Result { - let public = recover(signature, message)?; - let recovered_address = public_to_address(&public); - Ok(address == &recovered_address) +pub fn verify_address( + address: &Address, + signature: &Signature, + message: &Message, +) -> Result { + let public = recover(signature, message)?; + let recovered_address = public_to_address(&public); + Ok(address == &recovered_address) } pub fn recover(signature: &Signature, message: &Message) -> Result { - let context = &SECP256K1; - let rsig = RecoverableSignature::from_compact(context, &signature[0..64], RecoveryId::from_i32(signature[64] as i32)?)?; - let pubkey = context.recover(&SecpMessage::from_slice(&message[..])?, &rsig)?; - let serialized = pubkey.serialize_vec(context, false); - - let mut public = Public::default(); - public.copy_from_slice(&serialized[1..65]); - Ok(public) + let context = &SECP256K1; + let rsig = RecoverableSignature::from_compact( + context, + &signature[0..64], + RecoveryId::from_i32(signature[64] as i32)?, + )?; + let pubkey = context.recover(&SecpMessage::from_slice(&message[..])?, &rsig)?; + let serialized = pubkey.serialize_vec(context, false); + + let mut public = Public::default(); + public.copy_from_slice(&serialized[1..65]); + Ok(public) } #[cfg(test)] mod tests { - use std::str::FromStr; - use {Generator, Random, Message}; - use super::{sign, verify_public, verify_address, recover, Signature}; - - #[test] - fn vrs_conversion() { - // given - let keypair = Random.generate().unwrap(); - let message = Message::default(); - let signature = sign(keypair.secret(), &message).unwrap(); - - // when - let vrs = signature.clone().into_electrum(); - let from_vrs = Signature::from_electrum(&vrs); - - // then - assert_eq!(signature, from_vrs); - } - - #[test] - fn signature_to_and_from_str() { - let keypair = Random.generate().unwrap(); - let message = Message::default(); - let signature = sign(keypair.secret(), &message).unwrap(); - let string = format!("{}", signature); - let deserialized = Signature::from_str(&string).unwrap(); - assert_eq!(signature, deserialized); - } - - #[test] - fn sign_and_recover_public() { - let keypair = Random.generate().unwrap(); - let message = Message::default(); - let signature = sign(keypair.secret(), &message).unwrap(); - assert_eq!(keypair.public(), &recover(&signature, &message).unwrap()); - } - - #[test] - fn sign_and_verify_public() { - let keypair = Random.generate().unwrap(); - let message = Message::default(); - let signature = sign(keypair.secret(), &message).unwrap(); - assert!(verify_public(keypair.public(), &signature, &message).unwrap()); - } - - #[test] - fn sign_and_verify_address() { - let keypair = Random.generate().unwrap(); - let message = Message::default(); - let signature = sign(keypair.secret(), &message).unwrap(); - assert!(verify_address(&keypair.address(), &signature, &message).unwrap()); - } + use super::{recover, sign, verify_address, verify_public, Signature}; + use std::str::FromStr; + use Generator; + use Message; + use Random; + + #[test] + fn vrs_conversion() { + // given + let keypair = Random.generate().unwrap(); + let message = Message::default(); + let signature = sign(keypair.secret(), &message).unwrap(); + + // when + let vrs = signature.clone().into_electrum(); + let from_vrs = Signature::from_electrum(&vrs); + + // then + assert_eq!(signature, from_vrs); + } + + #[test] + fn signature_to_and_from_str() { + let keypair = Random.generate().unwrap(); + let message = Message::default(); + let signature = sign(keypair.secret(), &message).unwrap(); + let string = format!("{}", signature); + let deserialized = Signature::from_str(&string).unwrap(); + assert_eq!(signature, deserialized); + } + + #[test] + fn sign_and_recover_public() { + let keypair = Random.generate().unwrap(); + let message = Message::default(); + let signature = sign(keypair.secret(), &message).unwrap(); + assert_eq!(keypair.public(), &recover(&signature, &message).unwrap()); + } + + #[test] + fn sign_and_verify_public() { + let keypair = Random.generate().unwrap(); + let message = Message::default(); + let signature = sign(keypair.secret(), &message).unwrap(); + assert!(verify_public(keypair.public(), &signature, &message).unwrap()); + } + + #[test] + fn sign_and_verify_address() { + let keypair = Random.generate().unwrap(); + let message = Message::default(); + let signature = sign(keypair.secret(), &message).unwrap(); + assert!(verify_address(&keypair.address(), &signature, &message).unwrap()); + } } diff --git a/accounts/ethstore/Cargo.toml b/accounts/ethstore/Cargo.toml index 4563a803276..af497bcff34 100644 --- a/accounts/ethstore/Cargo.toml +++ b/accounts/ethstore/Cargo.toml @@ -1,4 +1,5 @@ [package] +description = "Parity Ethereum Key Management" name = "ethstore" version = "0.2.1" authors = ["Parity Technologies "] @@ -20,7 +21,7 @@ parity-crypto = "0.3.0" ethereum-types = "0.4" dir = { path = "../../util/dir" } smallvec = "0.6" -parity-wordlist = "1.0" +parity-wordlist = "1.3" tempdir = "0.3" lazy_static = "1.2.0" diff --git a/accounts/ethstore/README.md b/accounts/ethstore/README.md index 77c37bd2462..ceebe8100a6 100644 --- a/accounts/ethstore/README.md +++ b/accounts/ethstore/README.md @@ -337,4 +337,3 @@ _This project is a part of the Parity Ethereum toolchain._ - [ethabi](https://github.com/paritytech/ethabi) - Parity Ethereum function calls encoding. - [ethstore](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethstore) - Parity Ethereum key management. - [ethkey](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethkey) - Parity Ethereum keys generator. -- [whisper](https://github.com/paritytech/parity-ethereum/blob/master/whisper/) - Implementation of Whisper-v2 PoC. diff --git a/accounts/ethstore/cli/Cargo.toml b/accounts/ethstore/cli/Cargo.toml index 82858eaa762..9578a753778 100644 --- a/accounts/ethstore/cli/Cargo.toml +++ b/accounts/ethstore/cli/Cargo.toml @@ -1,4 +1,5 @@ [package] +description = "Parity Ethereum Key Management CLI" name = "ethstore-cli" version = "0.1.1" authors = ["Parity Technologies "] diff --git a/accounts/ethstore/cli/src/crack.rs b/accounts/ethstore/cli/src/crack.rs index abe171c3560..04f6086a52a 100644 --- a/accounts/ethstore/cli/src/crack.rs +++ b/accounts/ethstore/cli/src/crack.rs @@ -1,66 +1,66 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::{cmp, thread}; -use std::sync::Arc; -use std::collections::VecDeque; use parking_lot::Mutex; +use std::{cmp, collections::VecDeque, sync::Arc, thread}; -use ethstore::{ethkey::Password, PresaleWallet, Error}; +use ethstore::{ethkey::Password, Error, PresaleWallet}; use num_cpus; pub fn run(passwords: VecDeque, wallet_path: &str) -> Result<(), Error> { - let passwords = Arc::new(Mutex::new(passwords)); + let passwords = Arc::new(Mutex::new(passwords)); - let mut handles = Vec::new(); + let mut handles = Vec::new(); - for _ in 0..num_cpus::get() { - let passwords = passwords.clone(); - let wallet = PresaleWallet::open(&wallet_path)?; - handles.push(thread::spawn(move || { - look_for_password(passwords, wallet); - })); - } + for _ in 0..num_cpus::get() { + let passwords = passwords.clone(); + let wallet = PresaleWallet::open(&wallet_path)?; + handles.push(thread::spawn(move || { + look_for_password(passwords, wallet); + })); + } - for handle in handles { - handle.join().map_err(|err| Error::Custom(format!("Error finishing thread: {:?}", err)))?; - } + for handle in handles { + handle + .join() + .map_err(|err| Error::Custom(format!("Error finishing thread: {:?}", err)))?; + } - Ok(()) + Ok(()) } fn look_for_password(passwords: Arc>>, wallet: PresaleWallet) { - let mut counter = 0; - while !passwords.lock().is_empty() { - let package = { - let mut passwords = passwords.lock(); - let len = passwords.len(); - passwords.split_off(cmp::min(len, 32)) - }; - for pass in package { - counter += 1; - match wallet.decrypt(&pass) { - Ok(_) => { - println!("Found password: {}", pass.as_str()); - passwords.lock().clear(); - return; - }, - _ if counter % 100 == 0 => print!("."), - _ => {}, - } - } - } + let mut counter = 0; + while !passwords.lock().is_empty() { + let package = { + let mut passwords = passwords.lock(); + let len = passwords.len(); + passwords.split_off(cmp::min(len, 32)) + }; + for pass in package { + counter += 1; + match wallet.decrypt(&pass) { + Ok(_) => { + println!("Found password: {}", pass.as_str()); + passwords.lock().clear(); + return; + } + _ if counter % 100 == 0 => print!("."), + _ => {} + } + } + } } diff --git a/accounts/ethstore/cli/src/main.rs b/accounts/ethstore/cli/src/main.rs index 0f564406395..6d275194e80 100644 --- a/accounts/ethstore/cli/src/main.rs +++ b/accounts/ethstore/cli/src/main.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . extern crate dir; extern crate docopt; @@ -28,14 +28,15 @@ extern crate env_logger; #[macro_use] extern crate serde_derive; -use std::collections::VecDeque; -use std::io::Read; -use std::{env, process, fs, fmt}; +use std::{collections::VecDeque, env, fmt, fs, io::Read, process}; use docopt::Docopt; -use ethstore::accounts_dir::{KeyDirectory, RootDiskDirectory}; -use ethstore::ethkey::{Address, Password}; -use ethstore::{EthStore, SimpleSecretStore, SecretStore, import_accounts, PresaleWallet, SecretVaultRef, StoreAccountRef}; +use ethstore::{ + accounts_dir::{KeyDirectory, RootDiskDirectory}, + ethkey::{Address, Password}, + import_accounts, EthStore, PresaleWallet, SecretStore, SecretVaultRef, SimpleSecretStore, + StoreAccountRef, +}; mod crack; @@ -92,226 +93,271 @@ Commands: #[derive(Debug, Deserialize)] struct Args { - cmd_insert: bool, - cmd_change_pwd: bool, - cmd_list: bool, - cmd_import: bool, - cmd_import_wallet: bool, - cmd_find_wallet_pass: bool, - cmd_remove: bool, - cmd_sign: bool, - cmd_public: bool, - cmd_list_vaults: bool, - cmd_create_vault: bool, - cmd_change_vault_pwd: bool, - cmd_move_to_vault: bool, - cmd_move_from_vault: bool, - arg_secret: String, - arg_password: String, - arg_old_pwd: String, - arg_new_pwd: String, - arg_address: String, - arg_message: String, - arg_path: String, - arg_vault: String, - flag_src: String, - flag_dir: String, - flag_vault: String, - flag_vault_pwd: String, + cmd_insert: bool, + cmd_change_pwd: bool, + cmd_list: bool, + cmd_import: bool, + cmd_import_wallet: bool, + cmd_find_wallet_pass: bool, + cmd_remove: bool, + cmd_sign: bool, + cmd_public: bool, + cmd_list_vaults: bool, + cmd_create_vault: bool, + cmd_change_vault_pwd: bool, + cmd_move_to_vault: bool, + cmd_move_from_vault: bool, + arg_secret: String, + arg_password: String, + arg_old_pwd: String, + arg_new_pwd: String, + arg_address: String, + arg_message: String, + arg_path: String, + arg_vault: String, + flag_src: String, + flag_dir: String, + flag_vault: String, + flag_vault_pwd: String, } enum Error { - Ethstore(ethstore::Error), - Docopt(docopt::Error), + Ethstore(ethstore::Error), + Docopt(docopt::Error), } impl From for Error { - fn from(err: ethstore::Error) -> Self { - Error::Ethstore(err) - } + fn from(err: ethstore::Error) -> Self { + Error::Ethstore(err) + } } impl From for Error { - fn from(err: docopt::Error) -> Self { - Error::Docopt(err) - } + fn from(err: docopt::Error) -> Self { + Error::Docopt(err) + } } impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::Ethstore(ref err) => fmt::Display::fmt(err, f), - Error::Docopt(ref err) => fmt::Display::fmt(err, f), - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::Ethstore(ref err) => fmt::Display::fmt(err, f), + Error::Docopt(ref err) => fmt::Display::fmt(err, f), + } + } } fn main() { - panic_hook::set_abort(); - if env::var("RUST_LOG").is_err() { - env::set_var("RUST_LOG", "warn") - } - env_logger::try_init().expect("Logger initialized only once."); - - match execute(env::args()) { - Ok(result) => println!("{}", result), - Err(Error::Docopt(ref e)) => e.exit(), - Err(err) => { - eprintln!("{}", err); - process::exit(1); - } - } + panic_hook::set_abort(); + if env::var("RUST_LOG").is_err() { + env::set_var("RUST_LOG", "warn") + } + env_logger::try_init().expect("Logger initialized only once."); + + match execute(env::args()) { + Ok(result) => println!("{}", result), + Err(Error::Docopt(ref e)) => e.exit(), + Err(err) => { + eprintln!("{}", err); + process::exit(1); + } + } } -fn key_dir(location: &str, password: Option) -> Result, Error> { - let dir: RootDiskDirectory = match location { - "geth" => RootDiskDirectory::create(dir::geth(false))?, - "geth-test" => RootDiskDirectory::create(dir::geth(true))?, - path if path.starts_with("parity") => { - let chain = path.split('-').nth(1).unwrap_or("ethereum"); - let path = dir::parity(chain); - RootDiskDirectory::create(path)? - }, - path => RootDiskDirectory::create(path)?, - }; - - Ok(Box::new(dir.with_password(password))) +fn key_dir(location: &str, password: Option) -> Result, Error> { + let dir: RootDiskDirectory = match location { + path if path.starts_with("parity") => { + let chain = path.split('-').nth(1).unwrap_or("ethereum"); + let mut path = dir::default_data_pathbuf(); + path.push("keys"); + path.push(chain); + RootDiskDirectory::create(path)? + } + path => RootDiskDirectory::create(path)?, + }; + + Ok(Box::new(dir.with_password(password))) } fn open_args_vault(store: &EthStore, args: &Args) -> Result { - if args.flag_vault.is_empty() { - return Ok(SecretVaultRef::Root); - } + if args.flag_vault.is_empty() { + return Ok(SecretVaultRef::Root); + } - let vault_pwd = load_password(&args.flag_vault_pwd)?; - store.open_vault(&args.flag_vault, &vault_pwd)?; - Ok(SecretVaultRef::Vault(args.flag_vault.clone())) + let vault_pwd = load_password(&args.flag_vault_pwd)?; + store.open_vault(&args.flag_vault, &vault_pwd)?; + Ok(SecretVaultRef::Vault(args.flag_vault.clone())) } -fn open_args_vault_account(store: &EthStore, address: Address, args: &Args) -> Result { - match open_args_vault(store, args)? { - SecretVaultRef::Root => Ok(StoreAccountRef::root(address)), - SecretVaultRef::Vault(name) => Ok(StoreAccountRef::vault(&name, address)), - } +fn open_args_vault_account( + store: &EthStore, + address: Address, + args: &Args, +) -> Result { + match open_args_vault(store, args)? { + SecretVaultRef::Root => Ok(StoreAccountRef::root(address)), + SecretVaultRef::Vault(name) => Ok(StoreAccountRef::vault(&name, address)), + } } fn format_accounts(accounts: &[Address]) -> String { - accounts.iter() - .enumerate() - .map(|(i, a)| format!("{:2}: 0x{:x}", i, a)) - .collect::>() - .join("\n") + accounts + .iter() + .enumerate() + .map(|(i, a)| format!("{:2}: 0x{:x}", i, a)) + .collect::>() + .join("\n") } fn format_vaults(vaults: &[String]) -> String { - vaults.join("\n") + vaults.join("\n") } fn load_password(path: &str) -> Result { - let mut file = fs::File::open(path).map_err(|e| ethstore::Error::Custom(format!("Error opening password file '{}': {}", path, e)))?; - let mut password = String::new(); - file.read_to_string(&mut password).map_err(|e| ethstore::Error::Custom(format!("Error reading password file '{}': {}", path, e)))?; - // drop EOF - let _ = password.pop(); - Ok(password.into()) + let mut file = fs::File::open(path).map_err(|e| { + ethstore::Error::Custom(format!("Error opening password file '{}': {}", path, e)) + })?; + let mut password = String::new(); + file.read_to_string(&mut password).map_err(|e| { + ethstore::Error::Custom(format!("Error reading password file '{}': {}", path, e)) + })?; + // drop EOF + let _ = password.pop(); + Ok(password.into()) } -fn execute(command: I) -> Result where I: IntoIterator, S: AsRef { - let args: Args = Docopt::new(USAGE) - .and_then(|d| d.argv(command).deserialize())?; - - let store = EthStore::open(key_dir(&args.flag_dir, None)?)?; - - return if args.cmd_insert { - let secret = args.arg_secret.parse().map_err(|_| ethstore::Error::InvalidSecret)?; - let password = load_password(&args.arg_password)?; - let vault_ref = open_args_vault(&store, &args)?; - let account_ref = store.insert_account(vault_ref, secret, &password)?; - Ok(format!("0x{:x}", account_ref.address)) - } else if args.cmd_change_pwd { - let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?; - let old_pwd = load_password(&args.arg_old_pwd)?; - let new_pwd = load_password(&args.arg_new_pwd)?; - let account_ref = open_args_vault_account(&store, address, &args)?; - let ok = store.change_password(&account_ref, &old_pwd, &new_pwd).is_ok(); - Ok(format!("{}", ok)) - } else if args.cmd_list { - let vault_ref = open_args_vault(&store, &args)?; - let accounts = store.accounts()?; - let accounts: Vec<_> = accounts - .into_iter() - .filter(|a| &a.vault == &vault_ref) - .map(|a| a.address) - .collect(); - Ok(format_accounts(&accounts)) - } else if args.cmd_import { - let password = match args.arg_password.as_ref() { - "" => None, - _ => Some(load_password(&args.arg_password)?) - }; - let src = key_dir(&args.flag_src, password)?; - let dst = key_dir(&args.flag_dir, None)?; - - let accounts = import_accounts(&*src, &*dst)?; - Ok(format_accounts(&accounts)) - } else if args.cmd_import_wallet { - let wallet = PresaleWallet::open(&args.arg_path)?; - let password = load_password(&args.arg_password)?; - let kp = wallet.decrypt(&password)?; - let vault_ref = open_args_vault(&store, &args)?; - let account_ref = store.insert_account(vault_ref, kp.secret().clone(), &password)?; - Ok(format!("0x{:x}", account_ref.address)) - } else if args.cmd_find_wallet_pass { - let passwords = load_password(&args.arg_password)?; - let passwords = passwords.as_str().lines().map(|line| str::to_owned(line).into()).collect::>(); - crack::run(passwords, &args.arg_path)?; - Ok(format!("Password not found.")) - } else if args.cmd_remove { - let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?; - let password = load_password(&args.arg_password)?; - let account_ref = open_args_vault_account(&store, address, &args)?; - let ok = store.remove_account(&account_ref, &password).is_ok(); - Ok(format!("{}", ok)) - } else if args.cmd_sign { - let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?; - let message = args.arg_message.parse().map_err(|_| ethstore::Error::InvalidMessage)?; - let password = load_password(&args.arg_password)?; - let account_ref = open_args_vault_account(&store, address, &args)?; - let signature = store.sign(&account_ref, &password, &message)?; - Ok(format!("0x{}", signature)) - } else if args.cmd_public { - let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?; - let password = load_password(&args.arg_password)?; - let account_ref = open_args_vault_account(&store, address, &args)?; - let public = store.public(&account_ref, &password)?; - Ok(format!("0x{:x}", public)) - } else if args.cmd_list_vaults { - let vaults = store.list_vaults()?; - Ok(format_vaults(&vaults)) - } else if args.cmd_create_vault { - let password = load_password(&args.arg_password)?; - store.create_vault(&args.arg_vault, &password)?; - Ok("OK".to_owned()) - } else if args.cmd_change_vault_pwd { - let old_pwd = load_password(&args.arg_old_pwd)?; - let new_pwd = load_password(&args.arg_new_pwd)?; - store.open_vault(&args.arg_vault, &old_pwd)?; - store.change_vault_password(&args.arg_vault, &new_pwd)?; - Ok("OK".to_owned()) - } else if args.cmd_move_to_vault { - let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?; - let password = load_password(&args.arg_password)?; - let account_ref = open_args_vault_account(&store, address, &args)?; - store.open_vault(&args.arg_vault, &password)?; - store.change_account_vault(SecretVaultRef::Vault(args.arg_vault), account_ref)?; - Ok("OK".to_owned()) - } else if args.cmd_move_from_vault { - let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?; - let password = load_password(&args.arg_password)?; - store.open_vault(&args.arg_vault, &password)?; - store.change_account_vault(SecretVaultRef::Root, StoreAccountRef::vault(&args.arg_vault, address))?; - Ok("OK".to_owned()) - } else { - Ok(format!("{}", USAGE)) - } +fn execute(command: I) -> Result +where + I: IntoIterator, + S: AsRef, +{ + let args: Args = Docopt::new(USAGE).and_then(|d| d.argv(command).deserialize())?; + + let store = EthStore::open(key_dir(&args.flag_dir, None)?)?; + + return if args.cmd_insert { + let secret = args + .arg_secret + .parse() + .map_err(|_| ethstore::Error::InvalidSecret)?; + let password = load_password(&args.arg_password)?; + let vault_ref = open_args_vault(&store, &args)?; + let account_ref = store.insert_account(vault_ref, secret, &password)?; + Ok(format!("0x{:x}", account_ref.address)) + } else if args.cmd_change_pwd { + let address = args + .arg_address + .parse() + .map_err(|_| ethstore::Error::InvalidAccount)?; + let old_pwd = load_password(&args.arg_old_pwd)?; + let new_pwd = load_password(&args.arg_new_pwd)?; + let account_ref = open_args_vault_account(&store, address, &args)?; + let ok = store + .change_password(&account_ref, &old_pwd, &new_pwd) + .is_ok(); + Ok(format!("{}", ok)) + } else if args.cmd_list { + let vault_ref = open_args_vault(&store, &args)?; + let accounts = store.accounts()?; + let accounts: Vec<_> = accounts + .into_iter() + .filter(|a| &a.vault == &vault_ref) + .map(|a| a.address) + .collect(); + Ok(format_accounts(&accounts)) + } else if args.cmd_import { + let password = match args.arg_password.as_ref() { + "" => None, + _ => Some(load_password(&args.arg_password)?), + }; + let src = key_dir(&args.flag_src, password)?; + let dst = key_dir(&args.flag_dir, None)?; + + let accounts = import_accounts(&*src, &*dst)?; + Ok(format_accounts(&accounts)) + } else if args.cmd_import_wallet { + let wallet = PresaleWallet::open(&args.arg_path)?; + let password = load_password(&args.arg_password)?; + let kp = wallet.decrypt(&password)?; + let vault_ref = open_args_vault(&store, &args)?; + let account_ref = store.insert_account(vault_ref, kp.secret().clone(), &password)?; + Ok(format!("0x{:x}", account_ref.address)) + } else if args.cmd_find_wallet_pass { + let passwords = load_password(&args.arg_password)?; + let passwords = passwords + .as_str() + .lines() + .map(|line| str::to_owned(line).into()) + .collect::>(); + crack::run(passwords, &args.arg_path)?; + Ok(format!("Password not found.")) + } else if args.cmd_remove { + let address = args + .arg_address + .parse() + .map_err(|_| ethstore::Error::InvalidAccount)?; + let password = load_password(&args.arg_password)?; + let account_ref = open_args_vault_account(&store, address, &args)?; + let ok = store.remove_account(&account_ref, &password).is_ok(); + Ok(format!("{}", ok)) + } else if args.cmd_sign { + let address = args + .arg_address + .parse() + .map_err(|_| ethstore::Error::InvalidAccount)?; + let message = args + .arg_message + .parse() + .map_err(|_| ethstore::Error::InvalidMessage)?; + let password = load_password(&args.arg_password)?; + let account_ref = open_args_vault_account(&store, address, &args)?; + let signature = store.sign(&account_ref, &password, &message)?; + Ok(format!("0x{}", signature)) + } else if args.cmd_public { + let address = args + .arg_address + .parse() + .map_err(|_| ethstore::Error::InvalidAccount)?; + let password = load_password(&args.arg_password)?; + let account_ref = open_args_vault_account(&store, address, &args)?; + let public = store.public(&account_ref, &password)?; + Ok(format!("0x{:x}", public)) + } else if args.cmd_list_vaults { + let vaults = store.list_vaults()?; + Ok(format_vaults(&vaults)) + } else if args.cmd_create_vault { + let password = load_password(&args.arg_password)?; + store.create_vault(&args.arg_vault, &password)?; + Ok("OK".to_owned()) + } else if args.cmd_change_vault_pwd { + let old_pwd = load_password(&args.arg_old_pwd)?; + let new_pwd = load_password(&args.arg_new_pwd)?; + store.open_vault(&args.arg_vault, &old_pwd)?; + store.change_vault_password(&args.arg_vault, &new_pwd)?; + Ok("OK".to_owned()) + } else if args.cmd_move_to_vault { + let address = args + .arg_address + .parse() + .map_err(|_| ethstore::Error::InvalidAccount)?; + let password = load_password(&args.arg_password)?; + let account_ref = open_args_vault_account(&store, address, &args)?; + store.open_vault(&args.arg_vault, &password)?; + store.change_account_vault(SecretVaultRef::Vault(args.arg_vault), account_ref)?; + Ok("OK".to_owned()) + } else if args.cmd_move_from_vault { + let address = args + .arg_address + .parse() + .map_err(|_| ethstore::Error::InvalidAccount)?; + let password = load_password(&args.arg_password)?; + store.open_vault(&args.arg_vault, &password)?; + store.change_account_vault( + SecretVaultRef::Root, + StoreAccountRef::vault(&args.arg_vault, address), + )?; + Ok("OK".to_owned()) + } else { + Ok(format!("{}", USAGE)) + }; } diff --git a/accounts/ethstore/cli/tests/cli.rs b/accounts/ethstore/cli/tests/cli.rs deleted file mode 100644 index 39e40864f2a..00000000000 --- a/accounts/ethstore/cli/tests/cli.rs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -extern crate tempdir; -use std::process::Command; -use tempdir::TempDir; -use std::fs::File; -use std::io::Write; - -fn run(args: &[&str]) -> String { - let output = Command::new("cargo") - .args(&["run", "--"]) - .args(args) - .output() - .unwrap(); - assert!(output.status.success()); - String::from_utf8(output.stdout).unwrap() -} - -#[test] -fn cli_cmd() { - Command::new("cargo") - .arg("build") - .output() - .unwrap(); - - let dir = TempDir::new("test-vault").unwrap(); - - let mut passwd = File::create(dir.path().join("test-password")).unwrap(); - writeln!(passwd, "password").unwrap(); - - let mut passwd2 = File::create(dir.path().join("test-vault-addr")).unwrap(); - writeln!(passwd2, "password2").unwrap(); - - let test_password_buf = dir.path().join("test-password"); - let test_password: &str = test_password_buf.to_str().unwrap(); - let dir_str: &str = dir.path().to_str().unwrap(); - let test_vault_addr_buf = dir.path().join("test-vault-addr"); - let test_vault_addr = test_vault_addr_buf.to_str().unwrap(); - - run(&["create-vault", "test-vault", test_password, "--dir", dir_str]); - - let output = run(&["insert", "7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5", - test_vault_addr, - "--dir", dir_str, - "--vault", "test-vault", - "--vault-pwd", test_password]); - let address = output.trim(); - - let output = run(&["list", - "--dir", dir_str, - "--vault", "test-vault", - "--vault-pwd", test_password]); - assert_eq!(output, " 0: 0xa8fa5dd30a87bb9e3288d604eb74949c515ab66e\n"); - - let output = run(&["sign", &address[2..], - test_vault_addr, - "7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5", - "--dir", dir_str, - "--vault", "test-vault", - "--vault-pwd", test_password]); - assert_eq!(output, "0x54ab6e5cf0c5cb40043fdca5d15d611a3a94285414a076dafecc8dc9c04183f413296a3defff61092c0bb478dc9887ec01070e1275234211208fb8f4be4a9b0101\n"); - - let output = run(&["public", &address[2..], test_vault_addr, - "--dir", dir_str, - "--vault", "test-vault", - "--vault-pwd", test_password]); - assert_eq!(output, "0x35f222d88b80151857a2877826d940104887376a94c1cbd2c8c7c192eb701df88a18a4ecb8b05b1466c5b3706042027b5e079fe3a3683e66d822b0e047aa3418\n"); -} diff --git a/accounts/ethstore/src/account/cipher.rs b/accounts/ethstore/src/account/cipher.rs index 1d97b69e8fe..ba3c1e70566 100644 --- a/accounts/ethstore/src/account/cipher.rs +++ b/accounts/ethstore/src/account/cipher.rs @@ -1,59 +1,57 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use json; #[derive(Debug, PartialEq, Clone)] pub struct Aes128Ctr { - pub iv: [u8; 16], + pub iv: [u8; 16], } #[derive(Debug, PartialEq, Clone)] pub enum Cipher { - Aes128Ctr(Aes128Ctr), + Aes128Ctr(Aes128Ctr), } impl From for Aes128Ctr { - fn from(json: json::Aes128Ctr) -> Self { - Aes128Ctr { - iv: json.iv.into() - } - } + fn from(json: json::Aes128Ctr) -> Self { + Aes128Ctr { iv: json.iv.into() } + } } impl Into for Aes128Ctr { - fn into(self) -> json::Aes128Ctr { - json::Aes128Ctr { - iv: From::from(self.iv) - } - } + fn into(self) -> json::Aes128Ctr { + json::Aes128Ctr { + iv: From::from(self.iv), + } + } } impl From for Cipher { - fn from(json: json::Cipher) -> Self { - match json { - json::Cipher::Aes128Ctr(params) => Cipher::Aes128Ctr(From::from(params)), - } - } + fn from(json: json::Cipher) -> Self { + match json { + json::Cipher::Aes128Ctr(params) => Cipher::Aes128Ctr(From::from(params)), + } + } } impl Into for Cipher { - fn into(self) -> json::Cipher { - match self { - Cipher::Aes128Ctr(params) => json::Cipher::Aes128Ctr(params.into()), - } - } + fn into(self) -> json::Cipher { + match self { + Cipher::Aes128Ctr(params) => json::Cipher::Aes128Ctr(params.into()), + } + } } diff --git a/accounts/ethstore/src/account/crypto.rs b/accounts/ethstore/src/account/crypto.rs index a3f6f9e9a30..5b30dbbba0c 100644 --- a/accounts/ethstore/src/account/crypto.rs +++ b/accounts/ethstore/src/account/crypto.rs @@ -1,211 +1,234 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::str; -use std::num::NonZeroU32; +use account::{Aes128Ctr, Cipher, Kdf, Pbkdf2, Prf}; +use crypto::{self, Keccak256}; use ethkey::{Password, Secret}; -use {json, Error, crypto}; -use crypto::Keccak256; +use json; use random::Random; use smallvec::SmallVec; -use account::{Cipher, Kdf, Aes128Ctr, Pbkdf2, Prf}; +use std::{num::NonZeroU32, str}; +use Error; /// Encrypted data #[derive(Debug, PartialEq, Clone)] pub struct Crypto { - /// Encryption parameters - pub cipher: Cipher, - /// Encrypted data buffer - pub ciphertext: Vec, - /// Key derivation function parameters - pub kdf: Kdf, - /// Message authentication code - pub mac: [u8; 32], + /// Encryption parameters + pub cipher: Cipher, + /// Encrypted data buffer + pub ciphertext: Vec, + /// Key derivation function parameters + pub kdf: Kdf, + /// Message authentication code + pub mac: [u8; 32], } impl From for Crypto { - fn from(json: json::Crypto) -> Self { - Crypto { - cipher: json.cipher.into(), - ciphertext: json.ciphertext.into(), - kdf: json.kdf.into(), - mac: json.mac.into(), - } - } + fn from(json: json::Crypto) -> Self { + Crypto { + cipher: json.cipher.into(), + ciphertext: json.ciphertext.into(), + kdf: json.kdf.into(), + mac: json.mac.into(), + } + } } impl From for json::Crypto { - fn from(c: Crypto) -> Self { - json::Crypto { - cipher: c.cipher.into(), - ciphertext: c.ciphertext.into(), - kdf: c.kdf.into(), - mac: c.mac.into(), - } - } + fn from(c: Crypto) -> Self { + json::Crypto { + cipher: c.cipher.into(), + ciphertext: c.ciphertext.into(), + kdf: c.kdf.into(), + mac: c.mac.into(), + } + } } impl str::FromStr for Crypto { - type Err = ::Err; + type Err = ::Err; - fn from_str(s: &str) -> Result { - s.parse::().map(Into::into) - } + fn from_str(s: &str) -> Result { + s.parse::().map(Into::into) + } } impl From for String { - fn from(c: Crypto) -> Self { - json::Crypto::from(c).into() - } + fn from(c: Crypto) -> Self { + json::Crypto::from(c).into() + } } impl Crypto { - /// Encrypt account secret - pub fn with_secret(secret: &Secret, password: &Password, iterations: NonZeroU32) -> Result { - Crypto::with_plain(&*secret, password, iterations) - } - - /// Encrypt custom plain data - pub fn with_plain(plain: &[u8], password: &Password, iterations: NonZeroU32) -> Result { - let salt: [u8; 32] = Random::random(); - let iv: [u8; 16] = Random::random(); - - // two parts of derived key - // DK = [ DK[0..15] DK[16..31] ] = [derived_left_bits, derived_right_bits] - let (derived_left_bits, derived_right_bits) = - crypto::derive_key_iterations(password.as_bytes(), &salt, iterations); - - // preallocated (on-stack in case of `Secret`) buffer to hold cipher - // length = length(plain) as we are using CTR-approach - let plain_len = plain.len(); - let mut ciphertext: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; plain_len]); - - // aes-128-ctr with initial vector of iv - crypto::aes::encrypt_128_ctr(&derived_left_bits, &iv, plain, &mut *ciphertext)?; - - // KECCAK(DK[16..31] ++ ), where DK[16..31] - derived_right_bits - let mac = crypto::derive_mac(&derived_right_bits, &*ciphertext).keccak256(); - - Ok(Crypto { - cipher: Cipher::Aes128Ctr(Aes128Ctr { - iv: iv, - }), - ciphertext: ciphertext.into_vec(), - kdf: Kdf::Pbkdf2(Pbkdf2 { - dklen: crypto::KEY_LENGTH as u32, - salt: salt.to_vec(), - c: iterations, - prf: Prf::HmacSha256, - }), - mac: mac, - }) - } - - /// Try to decrypt and convert result to account secret - pub fn secret(&self, password: &Password) -> Result { - if self.ciphertext.len() > 32 { - return Err(Error::InvalidSecret); - } - - let secret = self.do_decrypt(password, 32)?; - Ok(Secret::from_unsafe_slice(&secret)?) - } - - /// Try to decrypt and return result as is - pub fn decrypt(&self, password: &Password) -> Result, Error> { - let expected_len = self.ciphertext.len(); - self.do_decrypt(password, expected_len) - } - - fn do_decrypt(&self, password: &Password, expected_len: usize) -> Result, Error> { - let (derived_left_bits, derived_right_bits) = match self.kdf { - Kdf::Pbkdf2(ref params) => crypto::derive_key_iterations(password.as_bytes(), ¶ms.salt, params.c), - Kdf::Scrypt(ref params) => crypto::scrypt::derive_key(password.as_bytes(), ¶ms.salt, params.n, params.p, params.r)?, - }; - - let mac = crypto::derive_mac(&derived_right_bits, &self.ciphertext).keccak256(); - - if !crypto::is_equal(&mac, &self.mac) { - return Err(Error::InvalidPassword) - } - - let mut plain: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; expected_len]); - - match self.cipher { - Cipher::Aes128Ctr(ref params) => { - // checker by callers - debug_assert!(expected_len >= self.ciphertext.len()); - - let from = expected_len - self.ciphertext.len(); - crypto::aes::decrypt_128_ctr(&derived_left_bits, ¶ms.iv, &self.ciphertext, &mut plain[from..])?; - Ok(plain.into_iter().collect()) - }, - } - } + /// Encrypt account secret + pub fn with_secret( + secret: &Secret, + password: &Password, + iterations: NonZeroU32, + ) -> Result { + Crypto::with_plain(&*secret, password, iterations) + } + + /// Encrypt custom plain data + pub fn with_plain( + plain: &[u8], + password: &Password, + iterations: NonZeroU32, + ) -> Result { + let salt: [u8; 32] = Random::random(); + let iv: [u8; 16] = Random::random(); + + // two parts of derived key + // DK = [ DK[0..15] DK[16..31] ] = [derived_left_bits, derived_right_bits] + let (derived_left_bits, derived_right_bits) = + crypto::derive_key_iterations(password.as_bytes(), &salt, iterations); + + // preallocated (on-stack in case of `Secret`) buffer to hold cipher + // length = length(plain) as we are using CTR-approach + let plain_len = plain.len(); + let mut ciphertext: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; plain_len]); + + // aes-128-ctr with initial vector of iv + crypto::aes::encrypt_128_ctr(&derived_left_bits, &iv, plain, &mut *ciphertext)?; + + // KECCAK(DK[16..31] ++ ), where DK[16..31] - derived_right_bits + let mac = crypto::derive_mac(&derived_right_bits, &*ciphertext).keccak256(); + + Ok(Crypto { + cipher: Cipher::Aes128Ctr(Aes128Ctr { iv: iv }), + ciphertext: ciphertext.into_vec(), + kdf: Kdf::Pbkdf2(Pbkdf2 { + dklen: crypto::KEY_LENGTH as u32, + salt: salt.to_vec(), + c: iterations, + prf: Prf::HmacSha256, + }), + mac: mac, + }) + } + + /// Try to decrypt and convert result to account secret + pub fn secret(&self, password: &Password) -> Result { + if self.ciphertext.len() > 32 { + return Err(Error::InvalidSecret); + } + + let secret = self.do_decrypt(password, 32)?; + Ok(Secret::from_unsafe_slice(&secret)?) + } + + /// Try to decrypt and return result as is + pub fn decrypt(&self, password: &Password) -> Result, Error> { + let expected_len = self.ciphertext.len(); + self.do_decrypt(password, expected_len) + } + + fn do_decrypt(&self, password: &Password, expected_len: usize) -> Result, Error> { + let (derived_left_bits, derived_right_bits) = match self.kdf { + Kdf::Pbkdf2(ref params) => { + crypto::derive_key_iterations(password.as_bytes(), ¶ms.salt, params.c) + } + Kdf::Scrypt(ref params) => crypto::scrypt::derive_key( + password.as_bytes(), + ¶ms.salt, + params.n, + params.p, + params.r, + )?, + }; + + let mac = crypto::derive_mac(&derived_right_bits, &self.ciphertext).keccak256(); + + if !crypto::is_equal(&mac, &self.mac) { + return Err(Error::InvalidPassword); + } + + let mut plain: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; expected_len]); + + match self.cipher { + Cipher::Aes128Ctr(ref params) => { + // checker by callers + debug_assert!(expected_len >= self.ciphertext.len()); + + let from = expected_len - self.ciphertext.len(); + crypto::aes::decrypt_128_ctr( + &derived_left_bits, + ¶ms.iv, + &self.ciphertext, + &mut plain[from..], + )?; + Ok(plain.into_iter().collect()) + } + } + } } #[cfg(test)] mod tests { - use ethkey::{Generator, Random}; - use super::{Crypto, Error, NonZeroU32}; - - lazy_static! { - static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed"); - } - - #[test] - fn crypto_with_secret_create() { - let keypair = Random.generate().unwrap(); - let passwd = "this is sparta".into(); - let crypto = Crypto::with_secret(keypair.secret(), &passwd, *ITERATIONS).unwrap(); - let secret = crypto.secret(&passwd).unwrap(); - assert_eq!(keypair.secret(), &secret); - } - - #[test] - fn crypto_with_secret_invalid_password() { - let keypair = Random.generate().unwrap(); - let crypto = Crypto::with_secret(keypair.secret(), &"this is sparta".into(), *ITERATIONS).unwrap(); - assert_matches!(crypto.secret(&"this is sparta!".into()), Err(Error::InvalidPassword)) - } - - #[test] - fn crypto_with_null_plain_data() { - let original_data = b""; - let passwd = "this is sparta".into(); - let crypto = Crypto::with_plain(&original_data[..], &passwd, *ITERATIONS).unwrap(); - let decrypted_data = crypto.decrypt(&passwd).unwrap(); - assert_eq!(original_data[..], *decrypted_data); - } - - #[test] - fn crypto_with_tiny_plain_data() { - let original_data = b"{}"; - let passwd = "this is sparta".into(); - let crypto = Crypto::with_plain(&original_data[..], &passwd, *ITERATIONS).unwrap(); - let decrypted_data = crypto.decrypt(&passwd).unwrap(); - assert_eq!(original_data[..], *decrypted_data); - } - - #[test] - fn crypto_with_huge_plain_data() { - let original_data: Vec<_> = (1..65536).map(|i| (i % 256) as u8).collect(); - let passwd = "this is sparta".into(); - let crypto = Crypto::with_plain(&original_data, &passwd, *ITERATIONS).unwrap(); - let decrypted_data = crypto.decrypt(&passwd).unwrap(); - assert_eq!(&original_data, &decrypted_data); - } + use super::{Crypto, Error, NonZeroU32}; + use ethkey::{Generator, Random}; + + lazy_static! { + static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed"); + } + + #[test] + fn crypto_with_secret_create() { + let keypair = Random.generate().unwrap(); + let passwd = "this is sparta".into(); + let crypto = Crypto::with_secret(keypair.secret(), &passwd, *ITERATIONS).unwrap(); + let secret = crypto.secret(&passwd).unwrap(); + assert_eq!(keypair.secret(), &secret); + } + + #[test] + fn crypto_with_secret_invalid_password() { + let keypair = Random.generate().unwrap(); + let crypto = + Crypto::with_secret(keypair.secret(), &"this is sparta".into(), *ITERATIONS).unwrap(); + assert_matches!( + crypto.secret(&"this is sparta!".into()), + Err(Error::InvalidPassword) + ) + } + + #[test] + fn crypto_with_null_plain_data() { + let original_data = b""; + let passwd = "this is sparta".into(); + let crypto = Crypto::with_plain(&original_data[..], &passwd, *ITERATIONS).unwrap(); + let decrypted_data = crypto.decrypt(&passwd).unwrap(); + assert_eq!(original_data[..], *decrypted_data); + } + + #[test] + fn crypto_with_tiny_plain_data() { + let original_data = b"{}"; + let passwd = "this is sparta".into(); + let crypto = Crypto::with_plain(&original_data[..], &passwd, *ITERATIONS).unwrap(); + let decrypted_data = crypto.decrypt(&passwd).unwrap(); + assert_eq!(original_data[..], *decrypted_data); + } + + #[test] + fn crypto_with_huge_plain_data() { + let original_data: Vec<_> = (1..65536).map(|i| (i % 256) as u8).collect(); + let passwd = "this is sparta".into(); + let crypto = Crypto::with_plain(&original_data, &passwd, *ITERATIONS).unwrap(); + let decrypted_data = crypto.decrypt(&passwd).unwrap(); + assert_eq!(&original_data, &decrypted_data); + } } diff --git a/accounts/ethstore/src/account/kdf.rs b/accounts/ethstore/src/account/kdf.rs index 06b361cdca3..2841f177672 100644 --- a/accounts/ethstore/src/account/kdf.rs +++ b/accounts/ethstore/src/account/kdf.rs @@ -1,126 +1,126 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use json; use std::num::NonZeroU32; #[derive(Debug, PartialEq, Clone)] pub enum Prf { - HmacSha256, + HmacSha256, } #[derive(Debug, PartialEq, Clone)] pub struct Pbkdf2 { - pub c: NonZeroU32, - pub dklen: u32, - pub prf: Prf, - pub salt: Vec, + pub c: NonZeroU32, + pub dklen: u32, + pub prf: Prf, + pub salt: Vec, } #[derive(Debug, PartialEq, Clone)] pub struct Scrypt { - pub dklen: u32, - pub p: u32, - pub n: u32, - pub r: u32, - pub salt: Vec, + pub dklen: u32, + pub p: u32, + pub n: u32, + pub r: u32, + pub salt: Vec, } #[derive(Debug, PartialEq, Clone)] pub enum Kdf { - Pbkdf2(Pbkdf2), - Scrypt(Scrypt), + Pbkdf2(Pbkdf2), + Scrypt(Scrypt), } impl From for Prf { - fn from(json: json::Prf) -> Self { - match json { - json::Prf::HmacSha256 => Prf::HmacSha256, - } - } + fn from(json: json::Prf) -> Self { + match json { + json::Prf::HmacSha256 => Prf::HmacSha256, + } + } } impl Into for Prf { - fn into(self) -> json::Prf { - match self { - Prf::HmacSha256 => json::Prf::HmacSha256, - } - } + fn into(self) -> json::Prf { + match self { + Prf::HmacSha256 => json::Prf::HmacSha256, + } + } } impl From for Pbkdf2 { - fn from(json: json::Pbkdf2) -> Self { - Pbkdf2 { - c: json.c, - dklen: json.dklen, - prf: From::from(json.prf), - salt: json.salt.into(), - } - } + fn from(json: json::Pbkdf2) -> Self { + Pbkdf2 { + c: json.c, + dklen: json.dklen, + prf: From::from(json.prf), + salt: json.salt.into(), + } + } } impl Into for Pbkdf2 { - fn into(self) -> json::Pbkdf2 { - json::Pbkdf2 { - c: self.c, - dklen: self.dklen, - prf: self.prf.into(), - salt: From::from(self.salt), - } - } + fn into(self) -> json::Pbkdf2 { + json::Pbkdf2 { + c: self.c, + dklen: self.dklen, + prf: self.prf.into(), + salt: From::from(self.salt), + } + } } impl From for Scrypt { - fn from(json: json::Scrypt) -> Self { - Scrypt { - dklen: json.dklen, - p: json.p, - n: json.n, - r: json.r, - salt: json.salt.into(), - } - } + fn from(json: json::Scrypt) -> Self { + Scrypt { + dklen: json.dklen, + p: json.p, + n: json.n, + r: json.r, + salt: json.salt.into(), + } + } } impl Into for Scrypt { - fn into(self) -> json::Scrypt { - json::Scrypt { - dklen: self.dklen, - p: self.p, - n: self.n, - r: self.r, - salt: From::from(self.salt), - } - } + fn into(self) -> json::Scrypt { + json::Scrypt { + dklen: self.dklen, + p: self.p, + n: self.n, + r: self.r, + salt: From::from(self.salt), + } + } } impl From for Kdf { - fn from(json: json::Kdf) -> Self { - match json { - json::Kdf::Pbkdf2(params) => Kdf::Pbkdf2(From::from(params)), - json::Kdf::Scrypt(params) => Kdf::Scrypt(From::from(params)), - } - } + fn from(json: json::Kdf) -> Self { + match json { + json::Kdf::Pbkdf2(params) => Kdf::Pbkdf2(From::from(params)), + json::Kdf::Scrypt(params) => Kdf::Scrypt(From::from(params)), + } + } } impl Into for Kdf { - fn into(self) -> json::Kdf { - match self { - Kdf::Pbkdf2(params) => json::Kdf::Pbkdf2(params.into()), - Kdf::Scrypt(params) => json::Kdf::Scrypt(params.into()), - } - } + fn into(self) -> json::Kdf { + match self { + Kdf::Pbkdf2(params) => json::Kdf::Pbkdf2(params.into()), + Kdf::Scrypt(params) => json::Kdf::Scrypt(params.into()), + } + } } diff --git a/accounts/ethstore/src/account/mod.rs b/accounts/ethstore/src/account/mod.rs index b979d34a5ac..b1757474f54 100644 --- a/accounts/ethstore/src/account/mod.rs +++ b/accounts/ethstore/src/account/mod.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . mod cipher; mod crypto; @@ -20,8 +20,10 @@ mod kdf; mod safe_account; mod version; -pub use self::cipher::{Cipher, Aes128Ctr}; -pub use self::crypto::Crypto; -pub use self::kdf::{Kdf, Pbkdf2, Scrypt, Prf}; -pub use self::safe_account::SafeAccount; -pub use self::version::Version; +pub use self::{ + cipher::{Aes128Ctr, Cipher}, + crypto::Crypto, + kdf::{Kdf, Pbkdf2, Prf, Scrypt}, + safe_account::SafeAccount, + version::Version, +}; diff --git a/accounts/ethstore/src/account/safe_account.rs b/accounts/ethstore/src/account/safe_account.rs index 63971ef6a27..a6f3d007d79 100644 --- a/accounts/ethstore/src/account/safe_account.rs +++ b/accounts/ethstore/src/account/safe_account.rs @@ -1,234 +1,286 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use ethkey::{self, KeyPair, sign, Address, Password, Signature, Message, Public, Secret}; -use ethkey::crypto::ecdh::agree; -use {json, Error}; +use super::crypto::Crypto; use account::Version; use crypto; -use super::crypto::Crypto; +use ethkey::{ + self, crypto::ecdh::agree, sign, Address, KeyPair, Message, Password, Public, Secret, Signature, +}; +use json; use std::num::NonZeroU32; +use Error; /// Account representation. #[derive(Debug, PartialEq, Clone)] pub struct SafeAccount { - /// Account ID - pub id: [u8; 16], - /// Account version - pub version: Version, - /// Account address - pub address: Address, - /// Account private key derivation definition. - pub crypto: Crypto, - /// Account filename - pub filename: Option, - /// Account name - pub name: String, - /// Account metadata - pub meta: String, + /// Account ID + pub id: [u8; 16], + /// Account version + pub version: Version, + /// Account address + pub address: Address, + /// Account private key derivation definition. + pub crypto: Crypto, + /// Account filename + pub filename: Option, + /// Account name + pub name: String, + /// Account metadata + pub meta: String, } impl Into for SafeAccount { - fn into(self) -> json::KeyFile { - json::KeyFile { - id: From::from(self.id), - version: self.version.into(), - address: Some(self.address.into()), - crypto: self.crypto.into(), - name: Some(self.name.into()), - meta: Some(self.meta.into()), - } - } + fn into(self) -> json::KeyFile { + json::KeyFile { + id: From::from(self.id), + version: self.version.into(), + address: Some(self.address.into()), + crypto: self.crypto.into(), + name: Some(self.name.into()), + meta: Some(self.meta.into()), + } + } } impl SafeAccount { - /// Create a new account - pub fn create( - keypair: &KeyPair, - id: [u8; 16], - password: &Password, - iterations: NonZeroU32, - name: String, - meta: String - ) -> Result { - Ok(SafeAccount { - id: id, - version: Version::V3, - crypto: Crypto::with_secret(keypair.secret(), password, iterations)?, - address: keypair.address(), - filename: None, - name: name, - meta: meta, - }) - } - - /// Create a new `SafeAccount` from the given `json`; if it was read from a - /// file, the `filename` should be `Some` name. If it is as yet anonymous, then it - /// can be left `None`. - /// In case `password` is provided, we will attempt to read the secret from the keyfile - /// and derive the address from it instead of reading it directly. - /// Providing password is required for `json::KeyFile`s with no address. - pub fn from_file(json: json::KeyFile, filename: Option, password: &Option) -> Result { - let crypto = Crypto::from(json.crypto); - let address = match (password, &json.address) { - (None, Some(json_address)) => json_address.into(), - (None, None) => Err(Error::Custom( - "This keystore does not contain address. You need to provide password to import it".into()))?, - (Some(password), json_address) => { - let derived_address = KeyPair::from_secret( - crypto.secret(&password).map_err(|_| Error::InvalidPassword)? - )?.address(); - - match json_address { - Some(json_address) => { - let json_address = json_address.into(); - if derived_address != json_address { - warn!("Detected address mismatch when opening an account. Derived: {:?}, in json got: {:?}", + /// Create a new account + pub fn create( + keypair: &KeyPair, + id: [u8; 16], + password: &Password, + iterations: NonZeroU32, + name: String, + meta: String, + ) -> Result { + Ok(SafeAccount { + id: id, + version: Version::V3, + crypto: Crypto::with_secret(keypair.secret(), password, iterations)?, + address: keypair.address(), + filename: None, + name: name, + meta: meta, + }) + } + + /// Create a new `SafeAccount` from the given `json`; if it was read from a + /// file, the `filename` should be `Some` name. If it is as yet anonymous, then it + /// can be left `None`. + /// In case `password` is provided, we will attempt to read the secret from the keyfile + /// and derive the address from it instead of reading it directly. + /// Providing password is required for `json::KeyFile`s with no address. + pub fn from_file( + json: json::KeyFile, + filename: Option, + password: &Option, + ) -> Result { + let crypto = Crypto::from(json.crypto); + let address = match (password, &json.address) { + (None, Some(json_address)) => json_address.into(), + (None, None) => Err(Error::Custom( + "This keystore does not contain address. You need to provide password to import it" + .into(), + ))?, + (Some(password), json_address) => { + let derived_address = KeyPair::from_secret( + crypto + .secret(&password) + .map_err(|_| Error::InvalidPassword)?, + )? + .address(); + + match json_address { + Some(json_address) => { + let json_address = json_address.into(); + if derived_address != json_address { + warn!("Detected address mismatch when opening an account. Derived: {:?}, in json got: {:?}", derived_address, json_address); - } - }, - _ => {}, - } - derived_address - } - }; - - Ok(SafeAccount { - id: json.id.into(), - version: json.version.into(), - address, - crypto, - filename, - name: json.name.unwrap_or(String::new()), - meta: json.meta.unwrap_or("{}".to_owned()), - }) - } - - /// Create a new `SafeAccount` from the given vault `json`; if it was read from a - /// file, the `filename` should be `Some` name. If it is as yet anonymous, then it - /// can be left `None`. - pub fn from_vault_file(password: &Password, json: json::VaultKeyFile, filename: Option) -> Result { - let meta_crypto: Crypto = json.metacrypto.into(); - let meta_plain = meta_crypto.decrypt(password)?; - let meta_plain = json::VaultKeyMeta::load(&meta_plain).map_err(|e| Error::Custom(format!("{:?}", e)))?; - - SafeAccount::from_file(json::KeyFile { - id: json.id, - version: json.version, - crypto: json.crypto, - address: Some(meta_plain.address), - name: meta_plain.name, - meta: meta_plain.meta, - }, filename, &None) - } - - /// Create a new `VaultKeyFile` from the given `self` - pub fn into_vault_file(self, iterations: NonZeroU32, password: &Password) -> Result { - let meta_plain = json::VaultKeyMeta { - address: self.address.into(), - name: Some(self.name), - meta: Some(self.meta), - }; - let meta_plain = meta_plain.write().map_err(|e| Error::Custom(format!("{:?}", e)))?; - let meta_crypto = Crypto::with_plain(&meta_plain, password, iterations)?; - - Ok(json::VaultKeyFile { - id: self.id.into(), - version: self.version.into(), - crypto: self.crypto.into(), - metacrypto: meta_crypto.into(), - }) - } - - /// Sign a message. - pub fn sign(&self, password: &Password, message: &Message) -> Result { - let secret = self.crypto.secret(password)?; - sign(&secret, message).map_err(From::from) - } - - /// Decrypt a message. - pub fn decrypt(&self, password: &Password, shared_mac: &[u8], message: &[u8]) -> Result, Error> { - let secret = self.crypto.secret(password)?; - ethkey::crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from) - } - - /// Agree on shared key. - pub fn agree(&self, password: &Password, other: &Public) -> Result { - let secret = self.crypto.secret(password)?; - agree(&secret, other).map_err(From::from) - } - - /// Derive public key. - pub fn public(&self, password: &Password) -> Result { - let secret = self.crypto.secret(password)?; - Ok(KeyPair::from_secret(secret)?.public().clone()) - } - - /// Change account's password. - pub fn change_password(&self, old_password: &Password, new_password: &Password, iterations: NonZeroU32) -> Result { - let secret = self.crypto.secret(old_password)?; - let result = SafeAccount { - id: self.id.clone(), - version: self.version.clone(), - crypto: Crypto::with_secret(&secret, new_password, iterations)?, - address: self.address.clone(), - filename: self.filename.clone(), - name: self.name.clone(), - meta: self.meta.clone(), - }; - Ok(result) - } - - /// Check if password matches the account. - pub fn check_password(&self, password: &Password) -> bool { - self.crypto.secret(password).is_ok() - } + } + } + _ => {} + } + derived_address + } + }; + + Ok(SafeAccount { + id: json.id.into(), + version: json.version.into(), + address, + crypto, + filename, + name: json.name.unwrap_or(String::new()), + meta: json.meta.unwrap_or("{}".to_owned()), + }) + } + + /// Create a new `SafeAccount` from the given vault `json`; if it was read from a + /// file, the `filename` should be `Some` name. If it is as yet anonymous, then it + /// can be left `None`. + pub fn from_vault_file( + password: &Password, + json: json::VaultKeyFile, + filename: Option, + ) -> Result { + let meta_crypto: Crypto = json.metacrypto.into(); + let meta_plain = meta_crypto.decrypt(password)?; + let meta_plain = + json::VaultKeyMeta::load(&meta_plain).map_err(|e| Error::Custom(format!("{:?}", e)))?; + + SafeAccount::from_file( + json::KeyFile { + id: json.id, + version: json.version, + crypto: json.crypto, + address: Some(meta_plain.address), + name: meta_plain.name, + meta: meta_plain.meta, + }, + filename, + &None, + ) + } + + /// Create a new `VaultKeyFile` from the given `self` + pub fn into_vault_file( + self, + iterations: NonZeroU32, + password: &Password, + ) -> Result { + let meta_plain = json::VaultKeyMeta { + address: self.address.into(), + name: Some(self.name), + meta: Some(self.meta), + }; + let meta_plain = meta_plain + .write() + .map_err(|e| Error::Custom(format!("{:?}", e)))?; + let meta_crypto = Crypto::with_plain(&meta_plain, password, iterations)?; + + Ok(json::VaultKeyFile { + id: self.id.into(), + version: self.version.into(), + crypto: self.crypto.into(), + metacrypto: meta_crypto.into(), + }) + } + + /// Sign a message. + pub fn sign(&self, password: &Password, message: &Message) -> Result { + let secret = self.crypto.secret(password)?; + sign(&secret, message).map_err(From::from) + } + + /// Decrypt a message. + pub fn decrypt( + &self, + password: &Password, + shared_mac: &[u8], + message: &[u8], + ) -> Result, Error> { + let secret = self.crypto.secret(password)?; + ethkey::crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from) + } + + /// Agree on shared key. + pub fn agree(&self, password: &Password, other: &Public) -> Result { + let secret = self.crypto.secret(password)?; + agree(&secret, other).map_err(From::from) + } + + /// Derive public key. + pub fn public(&self, password: &Password) -> Result { + let secret = self.crypto.secret(password)?; + Ok(KeyPair::from_secret(secret)?.public().clone()) + } + + /// Change account's password. + pub fn change_password( + &self, + old_password: &Password, + new_password: &Password, + iterations: NonZeroU32, + ) -> Result { + let secret = self.crypto.secret(old_password)?; + let result = SafeAccount { + id: self.id.clone(), + version: self.version.clone(), + crypto: Crypto::with_secret(&secret, new_password, iterations)?, + address: self.address.clone(), + filename: self.filename.clone(), + name: self.name.clone(), + meta: self.meta.clone(), + }; + Ok(result) + } + + /// Check if password matches the account. + pub fn check_password(&self, password: &Password) -> bool { + self.crypto.secret(password).is_ok() + } } #[cfg(test)] mod tests { - use ethkey::{Generator, Random, verify_public, Message}; - use super::{SafeAccount, NonZeroU32}; - - lazy_static! { - static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed"); - } - - - #[test] - fn sign_and_verify_public() { - let keypair = Random.generate().unwrap(); - let password = "hello world".into(); - let message = Message::default(); - let account = SafeAccount::create(&keypair, [0u8; 16], &password, *ITERATIONS, "Test".to_owned(), "{}".to_owned()); - let signature = account.unwrap().sign(&password, &message).unwrap(); - assert!(verify_public(keypair.public(), &signature, &message).unwrap()); - } - - #[test] - fn change_password() { - let keypair = Random.generate().unwrap(); - let first_password = "hello world".into(); - let sec_password = "this is sparta".into(); - let message = Message::default(); - let account = SafeAccount::create(&keypair, [0u8; 16], &first_password, *ITERATIONS, "Test".to_owned(), "{}".to_owned()).unwrap(); - let new_account = account.change_password(&first_password, &sec_password, *ITERATIONS).unwrap(); - assert!(account.sign(&first_password, &message).is_ok()); - assert!(account.sign(&sec_password, &message).is_err()); - assert!(new_account.sign(&first_password, &message).is_err()); - assert!(new_account.sign(&sec_password, &message).is_ok()); - } + use super::{NonZeroU32, SafeAccount}; + use ethkey::{verify_public, Generator, Message, Random}; + + lazy_static! { + static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed"); + } + + #[test] + fn sign_and_verify_public() { + let keypair = Random.generate().unwrap(); + let password = "hello world".into(); + let message = Message::default(); + let account = SafeAccount::create( + &keypair, + [0u8; 16], + &password, + *ITERATIONS, + "Test".to_owned(), + "{}".to_owned(), + ); + let signature = account.unwrap().sign(&password, &message).unwrap(); + assert!(verify_public(keypair.public(), &signature, &message).unwrap()); + } + + #[test] + fn change_password() { + let keypair = Random.generate().unwrap(); + let first_password = "hello world".into(); + let sec_password = "this is sparta".into(); + let message = Message::default(); + let account = SafeAccount::create( + &keypair, + [0u8; 16], + &first_password, + *ITERATIONS, + "Test".to_owned(), + "{}".to_owned(), + ) + .unwrap(); + let new_account = account + .change_password(&first_password, &sec_password, *ITERATIONS) + .unwrap(); + assert!(account.sign(&first_password, &message).is_ok()); + assert!(account.sign(&sec_password, &message).is_err()); + assert!(new_account.sign(&first_password, &message).is_err()); + assert!(new_account.sign(&sec_password, &message).is_ok()); + } } diff --git a/accounts/ethstore/src/account/version.rs b/accounts/ethstore/src/account/version.rs index 3048b95b0a1..514873868f4 100644 --- a/accounts/ethstore/src/account/version.rs +++ b/accounts/ethstore/src/account/version.rs @@ -1,38 +1,38 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use json; #[derive(Debug, PartialEq, Clone)] pub enum Version { - V3, + V3, } impl From for Version { - fn from(json: json::Version) -> Self { - match json { - json::Version::V3 => Version::V3, - } - } + fn from(json: json::Version) -> Self { + match json { + json::Version::V3 => Version::V3, + } + } } impl Into for Version { - fn into(self) -> json::Version { - match self { - Version::V3 => json::Version::V3, - } - } + fn into(self) -> json::Version { + match self { + Version::V3 => json::Version::V3, + } + } } diff --git a/accounts/ethstore/src/accounts_dir/disk.rs b/accounts/ethstore/src/accounts_dir/disk.rs index 00c59b254d2..c981f6949c1 100644 --- a/accounts/ethstore/src/accounts_dir/disk.rs +++ b/accounts/ethstore/src/accounts_dir/disk.rs @@ -1,103 +1,112 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::{fs, io}; -use std::io::Write; -use std::path::{PathBuf, Path}; -use std::collections::HashMap; -use time; -use {json, SafeAccount, Error}; -use json::Uuid; -use super::{KeyDirectory, VaultKeyDirectory, VaultKeyDirectoryProvider, VaultKey}; -use super::vault::{VAULT_FILE_NAME, VaultDiskDirectory}; +use super::{ + vault::{VaultDiskDirectory, VAULT_FILE_NAME}, + KeyDirectory, VaultKey, VaultKeyDirectory, VaultKeyDirectoryProvider, +}; use ethkey::Password; +use json::{self, Uuid}; +use std::{ + collections::HashMap, + fs, io, + io::Write, + path::{Path, PathBuf}, +}; +use time; +use Error; +use SafeAccount; const IGNORED_FILES: &'static [&'static str] = &[ - "thumbs.db", - "address_book.json", - "dapps_policy.json", - "dapps_accounts.json", - "dapps_history.json", - "vault.json", + "thumbs.db", + "address_book.json", + "dapps_policy.json", + "dapps_accounts.json", + "dapps_history.json", + "vault.json", ]; /// Find a unique filename that does not exist using four-letter random suffix. -pub fn find_unique_filename_using_random_suffix(parent_path: &Path, original_filename: &str) -> io::Result { - let mut path = parent_path.join(original_filename); - let mut deduped_filename = original_filename.to_string(); - - if path.exists() { - const MAX_RETRIES: usize = 500; - let mut retries = 0; - - while path.exists() { - if retries >= MAX_RETRIES { - return Err(io::Error::new(io::ErrorKind::Other, "Exceeded maximum retries when deduplicating filename.")); - } - - let suffix = ::random::random_string(4); - deduped_filename = format!("{}-{}", original_filename, suffix); - path.set_file_name(&deduped_filename); - retries += 1; - } - } - - Ok(deduped_filename) +pub fn find_unique_filename_using_random_suffix( + parent_path: &Path, + original_filename: &str, +) -> io::Result { + let mut path = parent_path.join(original_filename); + let mut deduped_filename = original_filename.to_string(); + + if path.exists() { + const MAX_RETRIES: usize = 500; + let mut retries = 0; + + while path.exists() { + if retries >= MAX_RETRIES { + return Err(io::Error::new( + io::ErrorKind::Other, + "Exceeded maximum retries when deduplicating filename.", + )); + } + + let suffix = ::random::random_string(4); + deduped_filename = format!("{}-{}", original_filename, suffix); + path.set_file_name(&deduped_filename); + retries += 1; + } + } + + Ok(deduped_filename) } /// Create a new file and restrict permissions to owner only. It errors if the file already exists. #[cfg(unix)] pub fn create_new_file_with_permissions_to_owner(file_path: &Path) -> io::Result { - use libc; - use std::os::unix::fs::OpenOptionsExt; - - fs::OpenOptions::new() - .write(true) - .create_new(true) - .mode((libc::S_IWUSR | libc::S_IRUSR) as u32) - .open(file_path) + use std::os::unix::fs::OpenOptionsExt; + + fs::OpenOptions::new() + .write(true) + .create_new(true) + .mode((libc::S_IWUSR | libc::S_IRUSR) as u32) + .open(file_path) } /// Create a new file and restrict permissions to owner only. It errors if the file already exists. #[cfg(not(unix))] pub fn create_new_file_with_permissions_to_owner(file_path: &Path) -> io::Result { - fs::OpenOptions::new() - .write(true) - .create_new(true) - .open(file_path) + fs::OpenOptions::new() + .write(true) + .create_new(true) + .open(file_path) } /// Create a new file and restrict permissions to owner only. It replaces the existing file if it already exists. #[cfg(unix)] pub fn replace_file_with_permissions_to_owner(file_path: &Path) -> io::Result { - use libc; - use std::os::unix::fs::PermissionsExt; + use std::os::unix::fs::PermissionsExt; - let file = fs::File::create(file_path)?; - let mut permissions = file.metadata()?.permissions(); - permissions.set_mode((libc::S_IWUSR | libc::S_IRUSR) as u32); - file.set_permissions(permissions)?; + let file = fs::File::create(file_path)?; + let mut permissions = file.metadata()?.permissions(); + permissions.set_mode((libc::S_IWUSR | libc::S_IRUSR) as u32); + file.set_permissions(permissions)?; - Ok(file) + Ok(file) } /// Create a new file and restrict permissions to owner only. It replaces the existing file if it already exists. #[cfg(not(unix))] pub fn replace_file_with_permissions_to_owner(file_path: &Path) -> io::Result { - fs::File::create(file_path) + fs::File::create(file_path) } /// Root keys directory implementation @@ -105,388 +114,495 @@ pub type RootDiskDirectory = DiskDirectory; /// Disk directory key file manager pub trait KeyFileManager: Send + Sync { - /// Read `SafeAccount` from given key file stream - fn read(&self, filename: Option, reader: T) -> Result where T: io::Read; - - /// Write `SafeAccount` to given key file stream - fn write(&self, account: SafeAccount, writer: &mut T) -> Result<(), Error> where T: io::Write; + /// Read `SafeAccount` from given key file stream + fn read(&self, filename: Option, reader: T) -> Result + where + T: io::Read; + + /// Write `SafeAccount` to given key file stream + fn write(&self, account: SafeAccount, writer: &mut T) -> Result<(), Error> + where + T: io::Write; } /// Disk-based keys directory implementation -pub struct DiskDirectory where T: KeyFileManager { - path: PathBuf, - key_manager: T, +pub struct DiskDirectory +where + T: KeyFileManager, +{ + path: PathBuf, + key_manager: T, } /// Keys file manager for root keys directory #[derive(Default)] pub struct DiskKeyFileManager { - password: Option, + password: Option, } impl RootDiskDirectory { - pub fn create

(path: P) -> Result where P: AsRef { - fs::create_dir_all(&path)?; - Ok(Self::at(path)) - } - - /// allows to read keyfiles with given password (needed for keyfiles w/o address) - pub fn with_password(&self, password: Option) -> Self { - DiskDirectory::new(&self.path, DiskKeyFileManager { password }) - } - - pub fn at

(path: P) -> Self where P: AsRef { - DiskDirectory::new(path, DiskKeyFileManager::default()) - } + pub fn create

(path: P) -> Result + where + P: AsRef, + { + fs::create_dir_all(&path)?; + Ok(Self::at(path)) + } + + /// allows to read keyfiles with given password (needed for keyfiles w/o address) + pub fn with_password(&self, password: Option) -> Self { + DiskDirectory::new(&self.path, DiskKeyFileManager { password }) + } + + pub fn at

(path: P) -> Self + where + P: AsRef, + { + DiskDirectory::new(path, DiskKeyFileManager::default()) + } } -impl DiskDirectory where T: KeyFileManager { - /// Create new disk directory instance - pub fn new

(path: P, key_manager: T) -> Self where P: AsRef { - DiskDirectory { - path: path.as_ref().to_path_buf(), - key_manager: key_manager, - } - } - - fn files(&self) -> Result, Error> { - Ok(fs::read_dir(&self.path)? - .flat_map(Result::ok) - .filter(|entry| { - let metadata = entry.metadata().ok(); - let file_name = entry.file_name(); - let name = file_name.to_string_lossy(); - // filter directories - metadata.map_or(false, |m| !m.is_dir()) && +impl DiskDirectory +where + T: KeyFileManager, +{ + /// Create new disk directory instance + pub fn new

(path: P, key_manager: T) -> Self + where + P: AsRef, + { + DiskDirectory { + path: path.as_ref().to_path_buf(), + key_manager: key_manager, + } + } + + fn files(&self) -> Result, Error> { + Ok(fs::read_dir(&self.path)? + .flat_map(Result::ok) + .filter(|entry| { + let metadata = entry.metadata().ok(); + let file_name = entry.file_name(); + let name = file_name.to_string_lossy(); + // filter directories + metadata.map_or(false, |m| !m.is_dir()) && // hidden files !name.starts_with(".") && // other ignored files !IGNORED_FILES.contains(&&*name) - }) - .map(|entry| entry.path()) - .collect::>() - ) - } - - pub fn files_hash(&self) -> Result { - use std::collections::hash_map::DefaultHasher; - use std::hash::Hasher; - - let mut hasher = DefaultHasher::new(); - let files = self.files()?; - for file in files { - hasher.write(file.to_str().unwrap_or("").as_bytes()) - } - - Ok(hasher.finish()) - } - - fn last_modification_date(&self) -> Result { - use std::time::{Duration, UNIX_EPOCH}; - let duration = fs::metadata(&self.path)?.modified()?.duration_since(UNIX_EPOCH).unwrap_or(Duration::default()); - let timestamp = duration.as_secs() ^ (duration.subsec_nanos() as u64); - Ok(timestamp) - } - - /// all accounts found in keys directory - fn files_content(&self) -> Result, Error> { - // it's not done using one iterator cause - // there is an issue with rustc and it takes tooo much time to compile - let paths = self.files()?; - Ok(paths - .into_iter() - .filter_map(|path| { - let filename = Some(path.file_name().and_then(|n| n.to_str()).expect("Keys have valid UTF8 names only.").to_owned()); - fs::File::open(path.clone()) - .map_err(Into::into) - .and_then(|file| self.key_manager.read(filename, file)) - .map_err(|err| { - warn!("Invalid key file: {:?} ({})", path, err); - err - }) - .map(|account| (path, account)) - .ok() - }) - .collect() - ) - } - - /// insert account with given filename. if the filename is a duplicate of any stored account and dedup is set to - /// true, a random suffix is appended to the filename. - pub fn insert_with_filename(&self, account: SafeAccount, mut filename: String, dedup: bool) -> Result { - if dedup { - filename = find_unique_filename_using_random_suffix(&self.path, &filename)?; - } - - // path to keyfile - let keyfile_path = self.path.join(filename.as_str()); - - // update account filename - let original_account = account.clone(); - let mut account = account; - account.filename = Some(filename); - - { - // save the file - let mut file = if dedup { - create_new_file_with_permissions_to_owner(&keyfile_path)? - } else { - replace_file_with_permissions_to_owner(&keyfile_path)? - }; - - // write key content - self.key_manager.write(original_account, &mut file).map_err(|e| Error::Custom(format!("{:?}", e)))?; - - file.flush()?; - file.sync_all()?; - } - - Ok(account) - } - - /// Get key file manager referece - pub fn key_manager(&self) -> &T { - &self.key_manager - } + }) + .map(|entry| entry.path()) + .collect::>()) + } + + pub fn files_hash(&self) -> Result { + use std::{collections::hash_map::DefaultHasher, hash::Hasher}; + + let mut hasher = DefaultHasher::new(); + let files = self.files()?; + for file in files { + hasher.write(file.to_str().unwrap_or("").as_bytes()) + } + + Ok(hasher.finish()) + } + + fn last_modification_date(&self) -> Result { + use std::time::{Duration, UNIX_EPOCH}; + let duration = fs::metadata(&self.path)? + .modified()? + .duration_since(UNIX_EPOCH) + .unwrap_or(Duration::default()); + let timestamp = duration.as_secs() ^ (duration.subsec_nanos() as u64); + Ok(timestamp) + } + + /// all accounts found in keys directory + fn files_content(&self) -> Result, Error> { + // it's not done using one iterator cause + // there is an issue with rustc and it takes tooo much time to compile + let paths = self.files()?; + Ok(paths + .into_iter() + .filter_map(|path| { + let filename = Some( + path.file_name() + .and_then(|n| n.to_str()) + .expect("Keys have valid UTF8 names only.") + .to_owned(), + ); + fs::File::open(path.clone()) + .map_err(Into::into) + .and_then(|file| self.key_manager.read(filename, file)) + .map_err(|err| { + warn!("Invalid key file: {:?} ({})", path, err); + err + }) + .map(|account| (path, account)) + .ok() + }) + .collect()) + } + + /// insert account with given filename. if the filename is a duplicate of any stored account and dedup is set to + /// true, a random suffix is appended to the filename. + pub fn insert_with_filename( + &self, + account: SafeAccount, + mut filename: String, + dedup: bool, + ) -> Result { + if dedup { + filename = find_unique_filename_using_random_suffix(&self.path, &filename)?; + } + + // path to keyfile + let keyfile_path = self.path.join(filename.as_str()); + + // update account filename + let original_account = account.clone(); + let mut account = account; + account.filename = Some(filename); + + { + // save the file + let mut file = if dedup { + create_new_file_with_permissions_to_owner(&keyfile_path)? + } else { + replace_file_with_permissions_to_owner(&keyfile_path)? + }; + + // write key content + self.key_manager + .write(original_account, &mut file) + .map_err(|e| Error::Custom(format!("{:?}", e)))?; + + file.flush()?; + file.sync_all()?; + } + + Ok(account) + } + + /// Get key file manager referece + pub fn key_manager(&self) -> &T { + &self.key_manager + } } -impl KeyDirectory for DiskDirectory where T: KeyFileManager { - fn load(&self) -> Result, Error> { - let accounts = self.files_content()? - .into_iter() - .map(|(_, account)| account) - .collect(); - Ok(accounts) - } - - fn update(&self, account: SafeAccount) -> Result { - // Disk store handles updates correctly iff filename is the same - let filename = account_filename(&account); - self.insert_with_filename(account, filename, false) - } - - fn insert(&self, account: SafeAccount) -> Result { - let filename = account_filename(&account); - self.insert_with_filename(account, filename, true) - } - - fn remove(&self, account: &SafeAccount) -> Result<(), Error> { - // enumerate all entries in keystore - // and find entry with given address - let to_remove = self.files_content()? - .into_iter() - .find(|&(_, ref acc)| acc.id == account.id && acc.address == account.address); - - // remove it - match to_remove { - None => Err(Error::InvalidAccount), - Some((path, _)) => fs::remove_file(path).map_err(From::from) - } - } - - fn path(&self) -> Option<&PathBuf> { Some(&self.path) } - - fn as_vault_provider(&self) -> Option<&VaultKeyDirectoryProvider> { - Some(self) - } - - fn unique_repr(&self) -> Result { - self.last_modification_date() - } +impl KeyDirectory for DiskDirectory +where + T: KeyFileManager, +{ + fn load(&self) -> Result, Error> { + let accounts = self + .files_content()? + .into_iter() + .map(|(_, account)| account) + .collect(); + Ok(accounts) + } + + fn update(&self, account: SafeAccount) -> Result { + // Disk store handles updates correctly iff filename is the same + let filename = account_filename(&account); + self.insert_with_filename(account, filename, false) + } + + fn insert(&self, account: SafeAccount) -> Result { + let filename = account_filename(&account); + self.insert_with_filename(account, filename, true) + } + + fn remove(&self, account: &SafeAccount) -> Result<(), Error> { + // enumerate all entries in keystore + // and find entry with given address + let to_remove = self + .files_content()? + .into_iter() + .find(|&(_, ref acc)| acc.id == account.id && acc.address == account.address); + + // remove it + match to_remove { + None => Err(Error::InvalidAccount), + Some((path, _)) => fs::remove_file(path).map_err(From::from), + } + } + + fn path(&self) -> Option<&PathBuf> { + Some(&self.path) + } + + fn as_vault_provider(&self) -> Option<&dyn VaultKeyDirectoryProvider> { + Some(self) + } + + fn unique_repr(&self) -> Result { + self.last_modification_date() + } } -impl VaultKeyDirectoryProvider for DiskDirectory where T: KeyFileManager { - fn create(&self, name: &str, key: VaultKey) -> Result, Error> { - let vault_dir = VaultDiskDirectory::create(&self.path, name, key)?; - Ok(Box::new(vault_dir)) - } - - fn open(&self, name: &str, key: VaultKey) -> Result, Error> { - let vault_dir = VaultDiskDirectory::at(&self.path, name, key)?; - Ok(Box::new(vault_dir)) - } - - fn list_vaults(&self) -> Result, Error> { - Ok(fs::read_dir(&self.path)? - .filter_map(|e| e.ok().map(|e| e.path())) - .filter_map(|path| { - let mut vault_file_path = path.clone(); - vault_file_path.push(VAULT_FILE_NAME); - if vault_file_path.is_file() { - path.file_name().and_then(|f| f.to_str()).map(|f| f.to_owned()) - } else { - None - } - }) - .collect()) - } - - fn vault_meta(&self, name: &str) -> Result { - VaultDiskDirectory::meta_at(&self.path, name) - } +impl VaultKeyDirectoryProvider for DiskDirectory +where + T: KeyFileManager, +{ + fn create(&self, name: &str, key: VaultKey) -> Result, Error> { + let vault_dir = VaultDiskDirectory::create(&self.path, name, key)?; + Ok(Box::new(vault_dir)) + } + + fn open(&self, name: &str, key: VaultKey) -> Result, Error> { + let vault_dir = VaultDiskDirectory::at(&self.path, name, key)?; + Ok(Box::new(vault_dir)) + } + + fn list_vaults(&self) -> Result, Error> { + Ok(fs::read_dir(&self.path)? + .filter_map(|e| e.ok().map(|e| e.path())) + .filter_map(|path| { + let mut vault_file_path = path.clone(); + vault_file_path.push(VAULT_FILE_NAME); + if vault_file_path.is_file() { + path.file_name() + .and_then(|f| f.to_str()) + .map(|f| f.to_owned()) + } else { + None + } + }) + .collect()) + } + + fn vault_meta(&self, name: &str) -> Result { + VaultDiskDirectory::meta_at(&self.path, name) + } } impl KeyFileManager for DiskKeyFileManager { - fn read(&self, filename: Option, reader: T) -> Result where T: io::Read { - let key_file = json::KeyFile::load(reader).map_err(|e| Error::Custom(format!("{:?}", e)))?; - SafeAccount::from_file(key_file, filename, &self.password) - } - - fn write(&self, mut account: SafeAccount, writer: &mut T) -> Result<(), Error> where T: io::Write { - // when account is moved back to root directory from vault - // => remove vault field from meta - account.meta = json::remove_vault_name_from_json_meta(&account.meta) - .map_err(|err| Error::Custom(format!("{:?}", err)))?; - - let key_file: json::KeyFile = account.into(); - key_file.write(writer).map_err(|e| Error::Custom(format!("{:?}", e))) - } + fn read(&self, filename: Option, reader: T) -> Result + where + T: io::Read, + { + let key_file = + json::KeyFile::load(reader).map_err(|e| Error::Custom(format!("{:?}", e)))?; + SafeAccount::from_file(key_file, filename, &self.password) + } + + fn write(&self, mut account: SafeAccount, writer: &mut T) -> Result<(), Error> + where + T: io::Write, + { + // when account is moved back to root directory from vault + // => remove vault field from meta + account.meta = json::remove_vault_name_from_json_meta(&account.meta) + .map_err(|err| Error::Custom(format!("{:?}", err)))?; + + let key_file: json::KeyFile = account.into(); + key_file + .write(writer) + .map_err(|e| Error::Custom(format!("{:?}", e))) + } } fn account_filename(account: &SafeAccount) -> String { - // build file path - account.filename.clone().unwrap_or_else(|| { - let timestamp = time::strftime("%Y-%m-%dT%H-%M-%S", &time::now_utc()).expect("Time-format string is valid."); - format!("UTC--{}Z--{}", timestamp, Uuid::from(account.id)) - }) + // build file path + account.filename.clone().unwrap_or_else(|| { + let timestamp = time::strftime("%Y-%m-%dT%H-%M-%S", &time::now_utc()) + .expect("Time-format string is valid."); + format!("UTC--{}Z--{}", timestamp, Uuid::from(account.id)) + }) } #[cfg(test)] mod test { - extern crate tempdir; - - use std::{env, fs}; - use std::num::NonZeroU32; - use super::{KeyDirectory, RootDiskDirectory, VaultKey}; - use account::SafeAccount; - use ethkey::{Random, Generator}; - use self::tempdir::TempDir; - - lazy_static! { - static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(1024).expect("1024 > 0; qed"); - } - - #[test] - fn should_create_new_account() { - // given - let mut dir = env::temp_dir(); - dir.push("ethstore_should_create_new_account"); - let keypair = Random.generate().unwrap(); - let password = "hello world".into(); - let directory = RootDiskDirectory::create(dir.clone()).unwrap(); - - // when - let account = SafeAccount::create(&keypair, [0u8; 16], &password, *ITERATIONS, "Test".to_owned(), "{}".to_owned()); - let res = directory.insert(account.unwrap()); - - // then - assert!(res.is_ok(), "Should save account succesfuly."); - assert!(res.unwrap().filename.is_some(), "Filename has been assigned."); - - // cleanup - let _ = fs::remove_dir_all(dir); - } - - #[test] - fn should_handle_duplicate_filenames() { - // given - let mut dir = env::temp_dir(); - dir.push("ethstore_should_handle_duplicate_filenames"); - let keypair = Random.generate().unwrap(); - let password = "hello world".into(); - let directory = RootDiskDirectory::create(dir.clone()).unwrap(); - - // when - let account = SafeAccount::create(&keypair, [0u8; 16], &password, *ITERATIONS, "Test".to_owned(), "{}".to_owned()).unwrap(); - let filename = "test".to_string(); - let dedup = true; - - directory.insert_with_filename(account.clone(), "foo".to_string(), dedup).unwrap(); - let file1 = directory.insert_with_filename(account.clone(), filename.clone(), dedup).unwrap().filename.unwrap(); - let file2 = directory.insert_with_filename(account.clone(), filename.clone(), dedup).unwrap().filename.unwrap(); - let file3 = directory.insert_with_filename(account.clone(), filename.clone(), dedup).unwrap().filename.unwrap(); - - // then - // the first file should have the original names - assert_eq!(file1, filename); - - // the following duplicate files should have a suffix appended - assert!(file2 != file3); - assert_eq!(file2.len(), filename.len() + 5); - assert_eq!(file3.len(), filename.len() + 5); - - // cleanup - let _ = fs::remove_dir_all(dir); - } - - #[test] - fn should_manage_vaults() { - // given - let mut dir = env::temp_dir(); - dir.push("should_create_new_vault"); - let directory = RootDiskDirectory::create(dir.clone()).unwrap(); - let vault_name = "vault"; - let password = "password".into(); - - // then - assert!(directory.as_vault_provider().is_some()); - - // and when - let before_root_items_count = fs::read_dir(&dir).unwrap().count(); - let vault = directory.as_vault_provider().unwrap().create(vault_name, VaultKey::new(&password, *ITERATIONS)); - - // then - assert!(vault.is_ok()); - let after_root_items_count = fs::read_dir(&dir).unwrap().count(); - assert!(after_root_items_count > before_root_items_count); - - // and when - let vault = directory.as_vault_provider().unwrap().open(vault_name, VaultKey::new(&password, *ITERATIONS)); - - // then - assert!(vault.is_ok()); - let after_root_items_count2 = fs::read_dir(&dir).unwrap().count(); - assert!(after_root_items_count == after_root_items_count2); - - // cleanup - let _ = fs::remove_dir_all(dir); - } - - #[test] - fn should_list_vaults() { - // given - let temp_path = TempDir::new("").unwrap(); - let directory = RootDiskDirectory::create(&temp_path).unwrap(); - let vault_provider = directory.as_vault_provider().unwrap(); - let iter = NonZeroU32::new(1).expect("1 > 0; qed"); - vault_provider.create("vault1", VaultKey::new(&"password1".into(), iter)).unwrap(); - vault_provider.create("vault2", VaultKey::new(&"password2".into(), iter)).unwrap(); - - // then - let vaults = vault_provider.list_vaults().unwrap(); - assert_eq!(vaults.len(), 2); - assert!(vaults.iter().any(|v| &*v == "vault1")); - assert!(vaults.iter().any(|v| &*v == "vault2")); - } - - #[test] - fn hash_of_files() { - let temp_path = TempDir::new("").unwrap(); - let directory = RootDiskDirectory::create(&temp_path).unwrap(); - - let hash = directory.files_hash().expect("Files hash should be calculated ok"); - assert_eq!( - hash, - 15130871412783076140 - ); - - let keypair = Random.generate().unwrap(); - let password = "test pass".into(); - let account = SafeAccount::create(&keypair, [0u8; 16], &password, *ITERATIONS, "Test".to_owned(), "{}".to_owned()); - directory.insert(account.unwrap()).expect("Account should be inserted ok"); - - let new_hash = directory.files_hash().expect("New files hash should be calculated ok"); - - assert!(new_hash != hash, "hash of the file list should change once directory content changed"); - } + extern crate tempdir; + + use self::tempdir::TempDir; + use super::{KeyDirectory, RootDiskDirectory, VaultKey}; + use account::SafeAccount; + use ethkey::{Generator, Random}; + use std::{env, fs, num::NonZeroU32}; + + lazy_static! { + static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(1024).expect("1024 > 0; qed"); + } + + #[test] + fn should_create_new_account() { + // given + let mut dir = env::temp_dir(); + dir.push("ethstore_should_create_new_account"); + let keypair = Random.generate().unwrap(); + let password = "hello world".into(); + let directory = RootDiskDirectory::create(dir.clone()).unwrap(); + + // when + let account = SafeAccount::create( + &keypair, + [0u8; 16], + &password, + *ITERATIONS, + "Test".to_owned(), + "{}".to_owned(), + ); + let res = directory.insert(account.unwrap()); + + // then + assert!(res.is_ok(), "Should save account succesfuly."); + assert!( + res.unwrap().filename.is_some(), + "Filename has been assigned." + ); + + // cleanup + let _ = fs::remove_dir_all(dir); + } + + #[test] + fn should_handle_duplicate_filenames() { + // given + let mut dir = env::temp_dir(); + dir.push("ethstore_should_handle_duplicate_filenames"); + let keypair = Random.generate().unwrap(); + let password = "hello world".into(); + let directory = RootDiskDirectory::create(dir.clone()).unwrap(); + + // when + let account = SafeAccount::create( + &keypair, + [0u8; 16], + &password, + *ITERATIONS, + "Test".to_owned(), + "{}".to_owned(), + ) + .unwrap(); + let filename = "test".to_string(); + let dedup = true; + + directory + .insert_with_filename(account.clone(), "foo".to_string(), dedup) + .unwrap(); + let file1 = directory + .insert_with_filename(account.clone(), filename.clone(), dedup) + .unwrap() + .filename + .unwrap(); + let file2 = directory + .insert_with_filename(account.clone(), filename.clone(), dedup) + .unwrap() + .filename + .unwrap(); + let file3 = directory + .insert_with_filename(account.clone(), filename.clone(), dedup) + .unwrap() + .filename + .unwrap(); + + // then + // the first file should have the original names + assert_eq!(file1, filename); + + // the following duplicate files should have a suffix appended + assert!(file2 != file3); + assert_eq!(file2.len(), filename.len() + 5); + assert_eq!(file3.len(), filename.len() + 5); + + // cleanup + let _ = fs::remove_dir_all(dir); + } + + #[test] + fn should_manage_vaults() { + // given + let mut dir = env::temp_dir(); + dir.push("should_create_new_vault"); + let directory = RootDiskDirectory::create(dir.clone()).unwrap(); + let vault_name = "vault"; + let password = "password".into(); + + // then + assert!(directory.as_vault_provider().is_some()); + + // and when + let before_root_items_count = fs::read_dir(&dir).unwrap().count(); + let vault = directory + .as_vault_provider() + .unwrap() + .create(vault_name, VaultKey::new(&password, *ITERATIONS)); + + // then + assert!(vault.is_ok()); + let after_root_items_count = fs::read_dir(&dir).unwrap().count(); + assert!(after_root_items_count > before_root_items_count); + + // and when + let vault = directory + .as_vault_provider() + .unwrap() + .open(vault_name, VaultKey::new(&password, *ITERATIONS)); + + // then + assert!(vault.is_ok()); + let after_root_items_count2 = fs::read_dir(&dir).unwrap().count(); + assert!(after_root_items_count == after_root_items_count2); + + // cleanup + let _ = fs::remove_dir_all(dir); + } + + #[test] + fn should_list_vaults() { + // given + let temp_path = TempDir::new("").unwrap(); + let directory = RootDiskDirectory::create(&temp_path).unwrap(); + let vault_provider = directory.as_vault_provider().unwrap(); + let iter = NonZeroU32::new(1).expect("1 > 0; qed"); + vault_provider + .create("vault1", VaultKey::new(&"password1".into(), iter)) + .unwrap(); + vault_provider + .create("vault2", VaultKey::new(&"password2".into(), iter)) + .unwrap(); + + // then + let vaults = vault_provider.list_vaults().unwrap(); + assert_eq!(vaults.len(), 2); + assert!(vaults.iter().any(|v| &*v == "vault1")); + assert!(vaults.iter().any(|v| &*v == "vault2")); + } + + #[test] + fn hash_of_files() { + let temp_path = TempDir::new("").unwrap(); + let directory = RootDiskDirectory::create(&temp_path).unwrap(); + + let hash = directory + .files_hash() + .expect("Files hash should be calculated ok"); + assert_eq!(hash, 15130871412783076140); + + let keypair = Random.generate().unwrap(); + let password = "test pass".into(); + let account = SafeAccount::create( + &keypair, + [0u8; 16], + &password, + *ITERATIONS, + "Test".to_owned(), + "{}".to_owned(), + ); + directory + .insert(account.unwrap()) + .expect("Account should be inserted ok"); + + let new_hash = directory + .files_hash() + .expect("New files hash should be calculated ok"); + + assert!( + new_hash != hash, + "hash of the file list should change once directory content changed" + ); + } } diff --git a/accounts/ethstore/src/accounts_dir/memory.rs b/accounts/ethstore/src/accounts_dir/memory.rs index 617e7bcb024..80ecb667640 100644 --- a/accounts/ethstore/src/accounts_dir/memory.rs +++ b/accounts/ethstore/src/accounts_dir/memory.rs @@ -1,74 +1,77 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::collections::HashMap; -use parking_lot::RwLock; -use itertools; use ethkey::Address; +use itertools; +use parking_lot::RwLock; +use std::collections::HashMap; -use {SafeAccount, Error}; use super::KeyDirectory; +use Error; +use SafeAccount; /// Accounts in-memory storage. #[derive(Default)] pub struct MemoryDirectory { - accounts: RwLock>>, + accounts: RwLock>>, } impl KeyDirectory for MemoryDirectory { - fn load(&self) -> Result, Error> { - Ok(itertools::Itertools::flatten(self.accounts.read().values().cloned()).collect()) - } + fn load(&self) -> Result, Error> { + Ok(itertools::Itertools::flatten(self.accounts.read().values().cloned()).collect()) + } - fn update(&self, account: SafeAccount) -> Result { - let mut lock = self.accounts.write(); - let accounts = lock.entry(account.address.clone()).or_insert_with(Vec::new); - // If the filename is the same we just need to replace the entry - accounts.retain(|acc| acc.filename != account.filename); - accounts.push(account.clone()); - Ok(account) - } + fn update(&self, account: SafeAccount) -> Result { + let mut lock = self.accounts.write(); + let accounts = lock.entry(account.address.clone()).or_insert_with(Vec::new); + // If the filename is the same we just need to replace the entry + accounts.retain(|acc| acc.filename != account.filename); + accounts.push(account.clone()); + Ok(account) + } - fn insert(&self, account: SafeAccount) -> Result { - let mut lock = self.accounts.write(); - let accounts = lock.entry(account.address.clone()).or_insert_with(Vec::new); - accounts.push(account.clone()); - Ok(account) - } + fn insert(&self, account: SafeAccount) -> Result { + let mut lock = self.accounts.write(); + let accounts = lock.entry(account.address.clone()).or_insert_with(Vec::new); + accounts.push(account.clone()); + Ok(account) + } - fn remove(&self, account: &SafeAccount) -> Result<(), Error> { - let mut accounts = self.accounts.write(); - let is_empty = if let Some(accounts) = accounts.get_mut(&account.address) { - if let Some(position) = accounts.iter().position(|acc| acc == account) { - accounts.remove(position); - } - accounts.is_empty() - } else { - false - }; - if is_empty { - accounts.remove(&account.address); - } - Ok(()) - } + fn remove(&self, account: &SafeAccount) -> Result<(), Error> { + let mut accounts = self.accounts.write(); + let is_empty = if let Some(accounts) = accounts.get_mut(&account.address) { + if let Some(position) = accounts.iter().position(|acc| acc == account) { + accounts.remove(position); + } + accounts.is_empty() + } else { + false + }; + if is_empty { + accounts.remove(&account.address); + } + Ok(()) + } - fn unique_repr(&self) -> Result { - let mut val = 0u64; - let accounts = self.accounts.read(); - for acc in accounts.keys() { val = val ^ acc.low_u64() } - Ok(val) - } + fn unique_repr(&self) -> Result { + let mut val = 0u64; + let accounts = self.accounts.read(); + for acc in accounts.keys() { + val = val ^ acc.low_u64() + } + Ok(val) + } } diff --git a/accounts/ethstore/src/accounts_dir/mod.rs b/accounts/ethstore/src/accounts_dir/mod.rs index 9b1328e115a..29fc50f2b0b 100644 --- a/accounts/ethstore/src/accounts_dir/mod.rs +++ b/accounts/ethstore/src/accounts_dir/mod.rs @@ -1,25 +1,25 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Accounts Directory use ethkey::Password; -use std::num::NonZeroU32; -use std::path::{PathBuf}; -use {SafeAccount, Error}; +use std::{num::NonZeroU32, path::PathBuf}; +use Error; +use SafeAccount; mod disk; mod memory; @@ -28,79 +28,85 @@ mod vault; /// `VaultKeyDirectory::set_key` error #[derive(Debug)] pub enum SetKeyError { - /// Error is fatal and directory is probably in inconsistent state - Fatal(Error), - /// Error is non fatal, directory is reverted to pre-operation state - NonFatalOld(Error), - /// Error is non fatal, directory is consistent with new key - NonFatalNew(Error), + /// Error is fatal and directory is probably in inconsistent state + Fatal(Error), + /// Error is non fatal, directory is reverted to pre-operation state + NonFatalOld(Error), + /// Error is non fatal, directory is consistent with new key + NonFatalNew(Error), } /// Vault key #[derive(Clone, PartialEq, Eq)] pub struct VaultKey { - /// Vault password - pub password: Password, - /// Number of iterations to produce a derived key from password - pub iterations: NonZeroU32, + /// Vault password + pub password: Password, + /// Number of iterations to produce a derived key from password + pub iterations: NonZeroU32, } /// Keys directory pub trait KeyDirectory: Send + Sync { - /// Read keys from directory - fn load(&self) -> Result, Error>; - /// Insert new key to directory - fn insert(&self, account: SafeAccount) -> Result; - /// Update key in the directory - fn update(&self, account: SafeAccount) -> Result; - /// Remove key from directory - fn remove(&self, account: &SafeAccount) -> Result<(), Error>; - /// Get directory filesystem path, if available - fn path(&self) -> Option<&PathBuf> { None } - /// Return vault provider, if available - fn as_vault_provider(&self) -> Option<&VaultKeyDirectoryProvider> { None } - /// Unique representation of directory account collection - fn unique_repr(&self) -> Result; + /// Read keys from directory + fn load(&self) -> Result, Error>; + /// Insert new key to directory + fn insert(&self, account: SafeAccount) -> Result; + /// Update key in the directory + fn update(&self, account: SafeAccount) -> Result; + /// Remove key from directory + fn remove(&self, account: &SafeAccount) -> Result<(), Error>; + /// Get directory filesystem path, if available + fn path(&self) -> Option<&PathBuf> { + None + } + /// Return vault provider, if available + fn as_vault_provider(&self) -> Option<&dyn VaultKeyDirectoryProvider> { + None + } + /// Unique representation of directory account collection + fn unique_repr(&self) -> Result; } /// Vaults provider pub trait VaultKeyDirectoryProvider { - /// Create new vault with given key - fn create(&self, name: &str, key: VaultKey) -> Result, Error>; - /// Open existing vault with given key - fn open(&self, name: &str, key: VaultKey) -> Result, Error>; - /// List all vaults - fn list_vaults(&self) -> Result, Error>; - /// Get vault meta - fn vault_meta(&self, name: &str) -> Result; + /// Create new vault with given key + fn create(&self, name: &str, key: VaultKey) -> Result, Error>; + /// Open existing vault with given key + fn open(&self, name: &str, key: VaultKey) -> Result, Error>; + /// List all vaults + fn list_vaults(&self) -> Result, Error>; + /// Get vault meta + fn vault_meta(&self, name: &str) -> Result; } /// Vault directory pub trait VaultKeyDirectory: KeyDirectory { - /// Cast to `KeyDirectory` - fn as_key_directory(&self) -> &KeyDirectory; - /// Vault name - fn name(&self) -> &str; - /// Get vault key - fn key(&self) -> VaultKey; - /// Set new key for vault - fn set_key(&self, key: VaultKey) -> Result<(), SetKeyError>; - /// Get vault meta - fn meta(&self) -> String; - /// Set vault meta - fn set_meta(&self, meta: &str) -> Result<(), Error>; + /// Cast to `KeyDirectory` + fn as_key_directory(&self) -> &dyn KeyDirectory; + /// Vault name + fn name(&self) -> &str; + /// Get vault key + fn key(&self) -> VaultKey; + /// Set new key for vault + fn set_key(&self, key: VaultKey) -> Result<(), SetKeyError>; + /// Get vault meta + fn meta(&self) -> String; + /// Set vault meta + fn set_meta(&self, meta: &str) -> Result<(), Error>; } -pub use self::disk::{RootDiskDirectory, DiskKeyFileManager, KeyFileManager}; -pub use self::memory::MemoryDirectory; -pub use self::vault::VaultDiskDirectory; +pub use self::{ + disk::{DiskKeyFileManager, KeyFileManager, RootDiskDirectory}, + memory::MemoryDirectory, + vault::VaultDiskDirectory, +}; impl VaultKey { - /// Create new vault key - pub fn new(password: &Password, iterations: NonZeroU32) -> Self { - VaultKey { - password: password.clone(), - iterations: iterations, - } - } + /// Create new vault key + pub fn new(password: &Password, iterations: NonZeroU32) -> Self { + VaultKey { + password: password.clone(), + iterations: iterations, + } + } } diff --git a/accounts/ethstore/src/accounts_dir/vault.rs b/accounts/ethstore/src/accounts_dir/vault.rs index c54de7c12c1..3c4cf3f5ad1 100644 --- a/accounts/ethstore/src/accounts_dir/vault.rs +++ b/accounts/ethstore/src/accounts_dir/vault.rs @@ -1,27 +1,33 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::{fs, io}; -use std::path::{PathBuf, Path}; -use parking_lot::Mutex; -use {json, SafeAccount, Error}; +use super::{ + super::account::Crypto, + disk::{self, DiskDirectory, KeyFileManager}, + KeyDirectory, SetKeyError, VaultKey, VaultKeyDirectory, +}; use crypto::Keccak256; -use super::super::account::Crypto; -use super::{KeyDirectory, VaultKeyDirectory, VaultKey, SetKeyError}; -use super::disk::{self, DiskDirectory, KeyFileManager}; +use json; +use parking_lot::Mutex; +use std::{ + fs, io, + path::{Path, PathBuf}, +}; +use Error; +use SafeAccount; /// Name of vault metadata file pub const VAULT_FILE_NAME: &'static str = "vault.json"; @@ -33,417 +39,489 @@ pub type VaultDiskDirectory = DiskDirectory; /// Vault key file manager pub struct VaultKeyFileManager { - name: String, - key: VaultKey, - meta: Mutex, + name: String, + key: VaultKey, + meta: Mutex, } impl VaultDiskDirectory { - /// Create new vault directory with given key - pub fn create

(root: P, name: &str, key: VaultKey) -> Result where P: AsRef { - // check that vault directory does not exists - let vault_dir_path = make_vault_dir_path(root, name, true)?; - if vault_dir_path.exists() { - return Err(Error::CreationFailed); - } - - // create vault && vault file - let vault_meta = "{}"; - fs::create_dir_all(&vault_dir_path)?; - if let Err(err) = create_vault_file(&vault_dir_path, &key, vault_meta) { - let _ = fs::remove_dir_all(&vault_dir_path); // can't do anything with this - return Err(err); - } - - Ok(DiskDirectory::new(vault_dir_path, VaultKeyFileManager::new(name, key, vault_meta))) - } - - /// Open existing vault directory with given key - pub fn at

(root: P, name: &str, key: VaultKey) -> Result where P: AsRef { - // check that vault directory exists - let vault_dir_path = make_vault_dir_path(root, name, true)?; - if !vault_dir_path.is_dir() { - return Err(Error::CreationFailed); - } - - // check that passed key matches vault file - let meta = read_vault_file(&vault_dir_path, Some(&key))?; - - Ok(DiskDirectory::new(vault_dir_path, VaultKeyFileManager::new(name, key, &meta))) - } - - /// Read vault meta without actually opening the vault - pub fn meta_at

(root: P, name: &str) -> Result where P: AsRef { - // check that vault directory exists - let vault_dir_path = make_vault_dir_path(root, name, true)?; - if !vault_dir_path.is_dir() { - return Err(Error::VaultNotFound); - } - - // check that passed key matches vault file - read_vault_file(&vault_dir_path, None) - } - - fn create_temp_vault(&self, key: VaultKey) -> Result { - let original_path = self.path().expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed"); - let mut path: PathBuf = original_path.clone(); - let name = self.name(); - - path.push(name); // to jump to the next level - - let mut index = 0; - loop { - let name = format!("{}_temp_{}", name, index); - path.set_file_name(&name); - if !path.exists() { - return VaultDiskDirectory::create(original_path, &name, key); - } - - index += 1; - } - } - - fn copy_to_vault(&self, vault: &VaultDiskDirectory) -> Result<(), Error> { - for account in self.load()? { - let filename = account.filename.clone().expect("self is instance of DiskDirectory; DiskDirectory fills filename in load; qed"); - vault.insert_with_filename(account, filename, true)?; - } - - Ok(()) - } - - fn delete(&self) -> Result<(), Error> { - let path = self.path().expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed"); - fs::remove_dir_all(path).map_err(Into::into) - } + /// Create new vault directory with given key + pub fn create

(root: P, name: &str, key: VaultKey) -> Result + where + P: AsRef, + { + // check that vault directory does not exists + let vault_dir_path = make_vault_dir_path(root, name, true)?; + if vault_dir_path.exists() { + return Err(Error::CreationFailed); + } + + // create vault && vault file + let vault_meta = "{}"; + fs::create_dir_all(&vault_dir_path)?; + if let Err(err) = create_vault_file(&vault_dir_path, &key, vault_meta) { + let _ = fs::remove_dir_all(&vault_dir_path); // can't do anything with this + return Err(err); + } + + Ok(DiskDirectory::new( + vault_dir_path, + VaultKeyFileManager::new(name, key, vault_meta), + )) + } + + /// Open existing vault directory with given key + pub fn at

(root: P, name: &str, key: VaultKey) -> Result + where + P: AsRef, + { + // check that vault directory exists + let vault_dir_path = make_vault_dir_path(root, name, true)?; + if !vault_dir_path.is_dir() { + return Err(Error::CreationFailed); + } + + // check that passed key matches vault file + let meta = read_vault_file(&vault_dir_path, Some(&key))?; + + Ok(DiskDirectory::new( + vault_dir_path, + VaultKeyFileManager::new(name, key, &meta), + )) + } + + /// Read vault meta without actually opening the vault + pub fn meta_at

(root: P, name: &str) -> Result + where + P: AsRef, + { + // check that vault directory exists + let vault_dir_path = make_vault_dir_path(root, name, true)?; + if !vault_dir_path.is_dir() { + return Err(Error::VaultNotFound); + } + + // check that passed key matches vault file + read_vault_file(&vault_dir_path, None) + } + + fn create_temp_vault(&self, key: VaultKey) -> Result { + let original_path = self + .path() + .expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed"); + let mut path: PathBuf = original_path.clone(); + let name = self.name(); + + path.push(name); // to jump to the next level + + let mut index = 0; + loop { + let name = format!("{}_temp_{}", name, index); + path.set_file_name(&name); + if !path.exists() { + return VaultDiskDirectory::create(original_path, &name, key); + } + + index += 1; + } + } + + fn copy_to_vault(&self, vault: &VaultDiskDirectory) -> Result<(), Error> { + for account in self.load()? { + let filename = account.filename.clone().expect( + "self is instance of DiskDirectory; DiskDirectory fills filename in load; qed", + ); + vault.insert_with_filename(account, filename, true)?; + } + + Ok(()) + } + + fn delete(&self) -> Result<(), Error> { + let path = self + .path() + .expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed"); + fs::remove_dir_all(path).map_err(Into::into) + } } impl VaultKeyDirectory for VaultDiskDirectory { - fn as_key_directory(&self) -> &KeyDirectory { - self - } - - fn name(&self) -> &str { - &self.key_manager().name - } - - fn key(&self) -> VaultKey { - self.key_manager().key.clone() - } - - fn set_key(&self, new_key: VaultKey) -> Result<(), SetKeyError> { - let temp_vault = VaultDiskDirectory::create_temp_vault(self, new_key.clone()).map_err(|err| SetKeyError::NonFatalOld(err))?; - let mut source_path = temp_vault.path().expect("temp_vault is instance of DiskDirectory; DiskDirectory always returns path; qed").clone(); - let mut target_path = self.path().expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed").clone(); - - // preserve meta - temp_vault.set_meta(&self.meta()).map_err(SetKeyError::NonFatalOld)?; - - // jump to next fs level - source_path.push("next"); - target_path.push("next"); - - let temp_accounts = self.copy_to_vault(&temp_vault) - .and_then(|_| temp_vault.load()) - .map_err(|err| { - // ignore error, as we already processing error - let _ = temp_vault.delete(); - SetKeyError::NonFatalOld(err) - })?; - - // we can't just delete temp vault until all files moved, because - // original vault content has already been partially replaced - // => when error or crash happens here, we can't do anything - for temp_account in temp_accounts { - let filename = temp_account.filename.expect("self is instance of DiskDirectory; DiskDirectory fills filename in load; qed"); - source_path.set_file_name(&filename); - target_path.set_file_name(&filename); - fs::rename(&source_path, &target_path).map_err(|err| SetKeyError::Fatal(err.into()))?; - } - source_path.set_file_name(VAULT_FILE_NAME); - target_path.set_file_name(VAULT_FILE_NAME); - fs::rename(source_path, target_path).map_err(|err| SetKeyError::Fatal(err.into()))?; - - temp_vault.delete().map_err(|err| SetKeyError::NonFatalNew(err)) - } - - fn meta(&self) -> String { - self.key_manager().meta.lock().clone() - } - - fn set_meta(&self, meta: &str) -> Result<(), Error> { - let key_manager = self.key_manager(); - let vault_path = self.path().expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed"); - create_vault_file(vault_path, &key_manager.key, meta)?; - *key_manager.meta.lock() = meta.to_owned(); - Ok(()) - } + fn as_key_directory(&self) -> &dyn KeyDirectory { + self + } + + fn name(&self) -> &str { + &self.key_manager().name + } + + fn key(&self) -> VaultKey { + self.key_manager().key.clone() + } + + fn set_key(&self, new_key: VaultKey) -> Result<(), SetKeyError> { + let temp_vault = VaultDiskDirectory::create_temp_vault(self, new_key.clone()) + .map_err(|err| SetKeyError::NonFatalOld(err))?; + let mut source_path = temp_vault + .path() + .expect( + "temp_vault is instance of DiskDirectory; DiskDirectory always returns path; qed", + ) + .clone(); + let mut target_path = self + .path() + .expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed") + .clone(); + + // preserve meta + temp_vault + .set_meta(&self.meta()) + .map_err(SetKeyError::NonFatalOld)?; + + // jump to next fs level + source_path.push("next"); + target_path.push("next"); + + let temp_accounts = self + .copy_to_vault(&temp_vault) + .and_then(|_| temp_vault.load()) + .map_err(|err| { + // ignore error, as we already processing error + let _ = temp_vault.delete(); + SetKeyError::NonFatalOld(err) + })?; + + // we can't just delete temp vault until all files moved, because + // original vault content has already been partially replaced + // => when error or crash happens here, we can't do anything + for temp_account in temp_accounts { + let filename = temp_account.filename.expect( + "self is instance of DiskDirectory; DiskDirectory fills filename in load; qed", + ); + source_path.set_file_name(&filename); + target_path.set_file_name(&filename); + fs::rename(&source_path, &target_path).map_err(|err| SetKeyError::Fatal(err.into()))?; + } + source_path.set_file_name(VAULT_FILE_NAME); + target_path.set_file_name(VAULT_FILE_NAME); + fs::rename(source_path, target_path).map_err(|err| SetKeyError::Fatal(err.into()))?; + + temp_vault + .delete() + .map_err(|err| SetKeyError::NonFatalNew(err)) + } + + fn meta(&self) -> String { + self.key_manager().meta.lock().clone() + } + + fn set_meta(&self, meta: &str) -> Result<(), Error> { + let key_manager = self.key_manager(); + let vault_path = self + .path() + .expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed"); + create_vault_file(vault_path, &key_manager.key, meta)?; + *key_manager.meta.lock() = meta.to_owned(); + Ok(()) + } } impl VaultKeyFileManager { - pub fn new(name: &str, key: VaultKey, meta: &str) -> Self { - VaultKeyFileManager { - name: name.into(), - key: key, - meta: Mutex::new(meta.to_owned()), - } - } + pub fn new(name: &str, key: VaultKey, meta: &str) -> Self { + VaultKeyFileManager { + name: name.into(), + key: key, + meta: Mutex::new(meta.to_owned()), + } + } } impl KeyFileManager for VaultKeyFileManager { - fn read(&self, filename: Option, reader: T) -> Result where T: io::Read { - let vault_file = json::VaultKeyFile::load(reader).map_err(|e| Error::Custom(format!("{:?}", e)))?; - let mut safe_account = SafeAccount::from_vault_file(&self.key.password, vault_file, filename.clone())?; - - safe_account.meta = json::insert_vault_name_to_json_meta(&safe_account.meta, &self.name) - .map_err(|err| Error::Custom(format!("{:?}", err)))?; - Ok(safe_account) - } - - fn write(&self, mut account: SafeAccount, writer: &mut T) -> Result<(), Error> where T: io::Write { - account.meta = json::remove_vault_name_from_json_meta(&account.meta) - .map_err(|err| Error::Custom(format!("{:?}", err)))?; - - let vault_file: json::VaultKeyFile = account.into_vault_file(self.key.iterations, &self.key.password)?; - vault_file.write(writer).map_err(|e| Error::Custom(format!("{:?}", e))) - } + fn read(&self, filename: Option, reader: T) -> Result + where + T: io::Read, + { + let vault_file = + json::VaultKeyFile::load(reader).map_err(|e| Error::Custom(format!("{:?}", e)))?; + let mut safe_account = + SafeAccount::from_vault_file(&self.key.password, vault_file, filename.clone())?; + + safe_account.meta = json::insert_vault_name_to_json_meta(&safe_account.meta, &self.name) + .map_err(|err| Error::Custom(format!("{:?}", err)))?; + Ok(safe_account) + } + + fn write(&self, mut account: SafeAccount, writer: &mut T) -> Result<(), Error> + where + T: io::Write, + { + account.meta = json::remove_vault_name_from_json_meta(&account.meta) + .map_err(|err| Error::Custom(format!("{:?}", err)))?; + + let vault_file: json::VaultKeyFile = + account.into_vault_file(self.key.iterations, &self.key.password)?; + vault_file + .write(writer) + .map_err(|e| Error::Custom(format!("{:?}", e))) + } } /// Makes path to vault directory, checking that vault name is appropriate -fn make_vault_dir_path

(root: P, name: &str, check_name: bool) -> Result where P: AsRef { - // check vault name - if check_name && !check_vault_name(name) { - return Err(Error::InvalidVaultName); - } - - let mut vault_dir_path: PathBuf = root.as_ref().into(); - vault_dir_path.push(name); - Ok(vault_dir_path) +fn make_vault_dir_path

(root: P, name: &str, check_name: bool) -> Result +where + P: AsRef, +{ + // check vault name + if check_name && !check_vault_name(name) { + return Err(Error::InvalidVaultName); + } + + let mut vault_dir_path: PathBuf = root.as_ref().into(); + vault_dir_path.push(name); + Ok(vault_dir_path) } /// Every vault must have unique name => we rely on filesystem to check this /// => vault name must not contain any fs-special characters to avoid directory traversal /// => we only allow alphanumeric + separator characters in vault name. fn check_vault_name(name: &str) -> bool { - !name.is_empty() - && name.chars() - .all(|c| c.is_alphanumeric() - || c.is_whitespace() - || c == '-' || c == '_') + !name.is_empty() + && name + .chars() + .all(|c| c.is_alphanumeric() || c.is_whitespace() || c == '-' || c == '_') } /// Vault can be empty, but still must be pluggable => we store vault password in separate file -fn create_vault_file

(vault_dir_path: P, key: &VaultKey, meta: &str) -> Result<(), Error> where P: AsRef { - let password_hash = key.password.as_bytes().keccak256(); - let crypto = Crypto::with_plain(&password_hash, &key.password, key.iterations)?; - - let vault_file_path = vault_dir_path.as_ref().join(VAULT_FILE_NAME); - let temp_vault_file_name = disk::find_unique_filename_using_random_suffix(vault_dir_path.as_ref(), &VAULT_TEMP_FILE_NAME)?; - let temp_vault_file_path = vault_dir_path.as_ref().join(&temp_vault_file_name); - - // this method is used to rewrite existing vault file - // => write to temporary file first, then rename temporary file to vault file - let mut vault_file = disk::create_new_file_with_permissions_to_owner(&temp_vault_file_path)?; - let vault_file_contents = json::VaultFile { - crypto: crypto.into(), - meta: Some(meta.to_owned()), - }; - vault_file_contents.write(&mut vault_file).map_err(|e| Error::Custom(format!("{:?}", e)))?; - drop(vault_file); - fs::rename(&temp_vault_file_path, &vault_file_path)?; - - Ok(()) +fn create_vault_file

(vault_dir_path: P, key: &VaultKey, meta: &str) -> Result<(), Error> +where + P: AsRef, +{ + let password_hash = key.password.as_bytes().keccak256(); + let crypto = Crypto::with_plain(&password_hash, &key.password, key.iterations)?; + + let vault_file_path = vault_dir_path.as_ref().join(VAULT_FILE_NAME); + let temp_vault_file_name = disk::find_unique_filename_using_random_suffix( + vault_dir_path.as_ref(), + &VAULT_TEMP_FILE_NAME, + )?; + let temp_vault_file_path = vault_dir_path.as_ref().join(&temp_vault_file_name); + + // this method is used to rewrite existing vault file + // => write to temporary file first, then rename temporary file to vault file + let mut vault_file = disk::create_new_file_with_permissions_to_owner(&temp_vault_file_path)?; + let vault_file_contents = json::VaultFile { + crypto: crypto.into(), + meta: Some(meta.to_owned()), + }; + vault_file_contents + .write(&mut vault_file) + .map_err(|e| Error::Custom(format!("{:?}", e)))?; + drop(vault_file); + fs::rename(&temp_vault_file_path, &vault_file_path)?; + + Ok(()) } /// When vault is opened => we must check that password matches && read metadata -fn read_vault_file

(vault_dir_path: P, key: Option<&VaultKey>) -> Result where P: AsRef { - let mut vault_file_path: PathBuf = vault_dir_path.as_ref().into(); - vault_file_path.push(VAULT_FILE_NAME); - - let vault_file = fs::File::open(vault_file_path)?; - let vault_file_contents = json::VaultFile::load(vault_file).map_err(|e| Error::Custom(format!("{:?}", e)))?; - let vault_file_meta = vault_file_contents.meta.unwrap_or("{}".to_owned()); - let vault_file_crypto: Crypto = vault_file_contents.crypto.into(); - - if let Some(key) = key { - let password_bytes = vault_file_crypto.decrypt(&key.password)?; - let password_hash = key.password.as_bytes().keccak256(); - if password_hash != password_bytes.as_slice() { - return Err(Error::InvalidPassword); - } - } - - Ok(vault_file_meta) +fn read_vault_file

(vault_dir_path: P, key: Option<&VaultKey>) -> Result +where + P: AsRef, +{ + let mut vault_file_path: PathBuf = vault_dir_path.as_ref().into(); + vault_file_path.push(VAULT_FILE_NAME); + + let vault_file = fs::File::open(vault_file_path)?; + let vault_file_contents = + json::VaultFile::load(vault_file).map_err(|e| Error::Custom(format!("{:?}", e)))?; + let vault_file_meta = vault_file_contents.meta.unwrap_or("{}".to_owned()); + let vault_file_crypto: Crypto = vault_file_contents.crypto.into(); + + if let Some(key) = key { + let password_bytes = vault_file_crypto.decrypt(&key.password)?; + let password_hash = key.password.as_bytes().keccak256(); + if password_hash != password_bytes.as_slice() { + return Err(Error::InvalidPassword); + } + } + + Ok(vault_file_meta) } #[cfg(test)] mod test { - extern crate tempdir; - - use std::fs; - use std::io::Write; - use std::num::NonZeroU32; - use std::path::PathBuf; - use super::VaultKey; - use super::{VAULT_FILE_NAME, check_vault_name, make_vault_dir_path, create_vault_file, read_vault_file, VaultDiskDirectory}; - use self::tempdir::TempDir; - - - lazy_static! { - static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(1024).expect("1024 > 0; qed"); - } - - #[test] - fn check_vault_name_succeeds() { - assert!(check_vault_name("vault")); - assert!(check_vault_name("vault with spaces")); - assert!(check_vault_name("vault with tabs")); - assert!(check_vault_name("vault_with_underscores")); - assert!(check_vault_name("vault-with-dashes")); - assert!(check_vault_name("vault-with-digits-123")); - assert!(check_vault_name("vault中文名字")); - } - - #[test] - fn check_vault_name_fails() { - assert!(!check_vault_name("")); - assert!(!check_vault_name(".")); - assert!(!check_vault_name("*")); - assert!(!check_vault_name("../.bash_history")); - assert!(!check_vault_name("/etc/passwd")); - assert!(!check_vault_name("c:\\windows")); - } - - #[test] - fn make_vault_dir_path_succeeds() { - use std::path::Path; - - assert_eq!(&make_vault_dir_path("/home/user/parity", "vault", true).unwrap(), &Path::new("/home/user/parity/vault")); - assert_eq!(&make_vault_dir_path("/home/user/parity", "*bad-name*", false).unwrap(), &Path::new("/home/user/parity/*bad-name*")); - } - - #[test] - fn make_vault_dir_path_fails() { - assert!(make_vault_dir_path("/home/user/parity", "*bad-name*", true).is_err()); - } - - #[test] - fn create_vault_file_succeeds() { - // given - let temp_path = TempDir::new("").unwrap(); - let key = VaultKey::new(&"password".into(), *ITERATIONS); - let mut vault_dir: PathBuf = temp_path.path().into(); - vault_dir.push("vault"); - fs::create_dir_all(&vault_dir).unwrap(); - - // when - let result = create_vault_file(&vault_dir, &key, "{}"); - - // then - assert!(result.is_ok()); - let mut vault_file_path = vault_dir.clone(); - vault_file_path.push(VAULT_FILE_NAME); - assert!(vault_file_path.exists() && vault_file_path.is_file()); - } - - #[test] - fn read_vault_file_succeeds() { - // given - let temp_path = TempDir::new("").unwrap(); - let key = VaultKey::new(&"password".into(), *ITERATIONS); - let vault_file_contents = r#"{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"758696c8dc6378ab9b25bb42790da2f5"},"ciphertext":"54eb50683717d41caaeb12ea969f2c159daada5907383f26f327606a37dc7168","kdf":"pbkdf2","kdfparams":{"c":1024,"dklen":32,"prf":"hmac-sha256","salt":"3c320fa566a1a7963ac8df68a19548d27c8f40bf92ef87c84594dcd5bbc402b6"},"mac":"9e5c2314c2a0781962db85611417c614bd6756666b6b1e93840f5b6ed895f003"}}"#; - let dir: PathBuf = temp_path.path().into(); - let mut vault_file_path: PathBuf = dir.clone(); - vault_file_path.push(VAULT_FILE_NAME); - { - let mut vault_file = fs::File::create(vault_file_path).unwrap(); - vault_file.write_all(vault_file_contents.as_bytes()).unwrap(); - } - - // when - let result = read_vault_file(&dir, Some(&key)); - - // then - assert!(result.is_ok()); - } - - #[test] - fn read_vault_file_fails() { - // given - let temp_path = TempDir::new("").unwrap(); - let key = VaultKey::new(&"password1".into(), *ITERATIONS); - let dir: PathBuf = temp_path.path().into(); - let mut vault_file_path: PathBuf = dir.clone(); - vault_file_path.push(VAULT_FILE_NAME); - - // when - let result = read_vault_file(&dir, Some(&key)); - - // then - assert!(result.is_err()); - - // and when given - let vault_file_contents = r#"{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"0155e3690be19fbfbecabcd440aa284b"},"ciphertext":"4d6938a1f49b7782","kdf":"pbkdf2","kdfparams":{"c":1024,"dklen":32,"prf":"hmac-sha256","salt":"b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5"},"mac":"16381463ea11c6eb2239a9f339c2e780516d29d234ce30ac5f166f9080b5a262"}}"#; - { - let mut vault_file = fs::File::create(vault_file_path).unwrap(); - vault_file.write_all(vault_file_contents.as_bytes()).unwrap(); - } - - // when - let result = read_vault_file(&dir, Some(&key)); - - // then - assert!(result.is_err()); - } - - #[test] - fn vault_directory_can_be_created() { - // given - let temp_path = TempDir::new("").unwrap(); - let key = VaultKey::new(&"password".into(), *ITERATIONS); - let dir: PathBuf = temp_path.path().into(); - - // when - let vault = VaultDiskDirectory::create(&dir, "vault", key.clone()); - - // then - assert!(vault.is_ok()); - - // and when - let vault = VaultDiskDirectory::at(&dir, "vault", key); - - // then - assert!(vault.is_ok()); - } - - #[test] - fn vault_directory_cannot_be_created_if_already_exists() { - // given - let temp_path = TempDir::new("").unwrap(); - let key = VaultKey::new(&"password".into(), *ITERATIONS); - let dir: PathBuf = temp_path.path().into(); - let mut vault_dir = dir.clone(); - vault_dir.push("vault"); - fs::create_dir_all(&vault_dir).unwrap(); - - // when - let vault = VaultDiskDirectory::create(&dir, "vault", key); - - // then - assert!(vault.is_err()); - } - - #[test] - fn vault_directory_cannot_be_opened_if_not_exists() { - // given - let temp_path = TempDir::new("").unwrap(); - let key = VaultKey::new(&"password".into(), *ITERATIONS); - let dir: PathBuf = temp_path.path().into(); - - // when - let vault = VaultDiskDirectory::at(&dir, "vault", key); - - // then - assert!(vault.is_err()); - } + extern crate tempdir; + + use self::tempdir::TempDir; + use super::{ + check_vault_name, create_vault_file, make_vault_dir_path, read_vault_file, + VaultDiskDirectory, VaultKey, VAULT_FILE_NAME, + }; + use std::{fs, io::Write, num::NonZeroU32, path::PathBuf}; + + lazy_static! { + static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(1024).expect("1024 > 0; qed"); + } + + #[test] + fn check_vault_name_succeeds() { + assert!(check_vault_name("vault")); + assert!(check_vault_name("vault with spaces")); + assert!(check_vault_name("vault with tabs")); + assert!(check_vault_name("vault_with_underscores")); + assert!(check_vault_name("vault-with-dashes")); + assert!(check_vault_name("vault-with-digits-123")); + assert!(check_vault_name("vault中文名字")); + } + + #[test] + fn check_vault_name_fails() { + assert!(!check_vault_name("")); + assert!(!check_vault_name(".")); + assert!(!check_vault_name("*")); + assert!(!check_vault_name("../.bash_history")); + assert!(!check_vault_name("/etc/passwd")); + assert!(!check_vault_name("c:\\windows")); + } + + #[test] + fn make_vault_dir_path_succeeds() { + use std::path::Path; + + assert_eq!( + &make_vault_dir_path("/home/user/parity", "vault", true).unwrap(), + &Path::new("/home/user/parity/vault") + ); + assert_eq!( + &make_vault_dir_path("/home/user/parity", "*bad-name*", false).unwrap(), + &Path::new("/home/user/parity/*bad-name*") + ); + } + + #[test] + fn make_vault_dir_path_fails() { + assert!(make_vault_dir_path("/home/user/parity", "*bad-name*", true).is_err()); + } + + #[test] + fn create_vault_file_succeeds() { + // given + let temp_path = TempDir::new("").unwrap(); + let key = VaultKey::new(&"password".into(), *ITERATIONS); + let mut vault_dir: PathBuf = temp_path.path().into(); + vault_dir.push("vault"); + fs::create_dir_all(&vault_dir).unwrap(); + + // when + let result = create_vault_file(&vault_dir, &key, "{}"); + + // then + assert!(result.is_ok()); + let mut vault_file_path = vault_dir.clone(); + vault_file_path.push(VAULT_FILE_NAME); + assert!(vault_file_path.exists() && vault_file_path.is_file()); + } + + #[test] + fn read_vault_file_succeeds() { + // given + let temp_path = TempDir::new("").unwrap(); + let key = VaultKey::new(&"password".into(), *ITERATIONS); + let vault_file_contents = r#"{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"758696c8dc6378ab9b25bb42790da2f5"},"ciphertext":"54eb50683717d41caaeb12ea969f2c159daada5907383f26f327606a37dc7168","kdf":"pbkdf2","kdfparams":{"c":1024,"dklen":32,"prf":"hmac-sha256","salt":"3c320fa566a1a7963ac8df68a19548d27c8f40bf92ef87c84594dcd5bbc402b6"},"mac":"9e5c2314c2a0781962db85611417c614bd6756666b6b1e93840f5b6ed895f003"}}"#; + let dir: PathBuf = temp_path.path().into(); + let mut vault_file_path: PathBuf = dir.clone(); + vault_file_path.push(VAULT_FILE_NAME); + { + let mut vault_file = fs::File::create(vault_file_path).unwrap(); + vault_file + .write_all(vault_file_contents.as_bytes()) + .unwrap(); + } + + // when + let result = read_vault_file(&dir, Some(&key)); + + // then + assert!(result.is_ok()); + } + + #[test] + fn read_vault_file_fails() { + // given + let temp_path = TempDir::new("").unwrap(); + let key = VaultKey::new(&"password1".into(), *ITERATIONS); + let dir: PathBuf = temp_path.path().into(); + let mut vault_file_path: PathBuf = dir.clone(); + vault_file_path.push(VAULT_FILE_NAME); + + // when + let result = read_vault_file(&dir, Some(&key)); + + // then + assert!(result.is_err()); + + // and when given + let vault_file_contents = r#"{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"0155e3690be19fbfbecabcd440aa284b"},"ciphertext":"4d6938a1f49b7782","kdf":"pbkdf2","kdfparams":{"c":1024,"dklen":32,"prf":"hmac-sha256","salt":"b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5"},"mac":"16381463ea11c6eb2239a9f339c2e780516d29d234ce30ac5f166f9080b5a262"}}"#; + { + let mut vault_file = fs::File::create(vault_file_path).unwrap(); + vault_file + .write_all(vault_file_contents.as_bytes()) + .unwrap(); + } + + // when + let result = read_vault_file(&dir, Some(&key)); + + // then + assert!(result.is_err()); + } + + #[test] + fn vault_directory_can_be_created() { + // given + let temp_path = TempDir::new("").unwrap(); + let key = VaultKey::new(&"password".into(), *ITERATIONS); + let dir: PathBuf = temp_path.path().into(); + + // when + let vault = VaultDiskDirectory::create(&dir, "vault", key.clone()); + + // then + assert!(vault.is_ok()); + + // and when + let vault = VaultDiskDirectory::at(&dir, "vault", key); + + // then + assert!(vault.is_ok()); + } + + #[test] + fn vault_directory_cannot_be_created_if_already_exists() { + // given + let temp_path = TempDir::new("").unwrap(); + let key = VaultKey::new(&"password".into(), *ITERATIONS); + let dir: PathBuf = temp_path.path().into(); + let mut vault_dir = dir.clone(); + vault_dir.push("vault"); + fs::create_dir_all(&vault_dir).unwrap(); + + // when + let vault = VaultDiskDirectory::create(&dir, "vault", key); + + // then + assert!(vault.is_err()); + } + + #[test] + fn vault_directory_cannot_be_opened_if_not_exists() { + // given + let temp_path = TempDir::new("").unwrap(); + let key = VaultKey::new(&"password".into(), *ITERATIONS); + let dir: PathBuf = temp_path.path().into(); + + // when + let vault = VaultDiskDirectory::at(&dir, "vault", key); + + // then + assert!(vault.is_err()); + } } diff --git a/accounts/ethstore/src/error.rs b/accounts/ethstore/src/error.rs index fceaf16768b..72372db87dc 100644 --- a/accounts/ethstore/src/error.rs +++ b/accounts/ethstore/src/error.rs @@ -1,128 +1,126 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::fmt; -use std::io::Error as IoError; -use ethkey::{self, Error as EthKeyError}; use crypto::{self, Error as EthCryptoError}; -use ethkey::DerivationError; +use ethkey::{self, DerivationError, Error as EthKeyError}; +use std::{fmt, io::Error as IoError}; /// Account-related errors. #[derive(Debug)] pub enum Error { - /// IO error - Io(IoError), - /// Invalid Password - InvalidPassword, - /// Account's secret is invalid. - InvalidSecret, - /// Invalid Vault Crypto meta. - InvalidCryptoMeta, - /// Invalid Account. - InvalidAccount, - /// Invalid Message. - InvalidMessage, - /// Invalid Key File - InvalidKeyFile(String), - /// Vaults are not supported. - VaultsAreNotSupported, - /// Unsupported vault - UnsupportedVault, - /// Invalid vault name - InvalidVaultName, - /// Vault not found - VaultNotFound, - /// Account creation failed. - CreationFailed, - /// `EthKey` error - EthKey(EthKeyError), - /// `ethkey::crypto::Error` - EthKeyCrypto(ethkey::crypto::Error), - /// `EthCrypto` error - EthCrypto(EthCryptoError), - /// Derivation error - Derivation(DerivationError), - /// Custom error - Custom(String), + /// IO error + Io(IoError), + /// Invalid Password + InvalidPassword, + /// Account's secret is invalid. + InvalidSecret, + /// Invalid Vault Crypto meta. + InvalidCryptoMeta, + /// Invalid Account. + InvalidAccount, + /// Invalid Message. + InvalidMessage, + /// Invalid Key File + InvalidKeyFile(String), + /// Vaults are not supported. + VaultsAreNotSupported, + /// Unsupported vault + UnsupportedVault, + /// Invalid vault name + InvalidVaultName, + /// Vault not found + VaultNotFound, + /// Account creation failed. + CreationFailed, + /// `EthKey` error + EthKey(EthKeyError), + /// `ethkey::crypto::Error` + EthKeyCrypto(ethkey::crypto::Error), + /// `EthCrypto` error + EthCrypto(EthCryptoError), + /// Derivation error + Derivation(DerivationError), + /// Custom error + Custom(String), } impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - let s = match *self { - Error::Io(ref err) => err.to_string(), - Error::InvalidPassword => "Invalid password".into(), - Error::InvalidSecret => "Invalid secret".into(), - Error::InvalidCryptoMeta => "Invalid crypted metadata".into(), - Error::InvalidAccount => "Invalid account".into(), - Error::InvalidMessage => "Invalid message".into(), - Error::InvalidKeyFile(ref reason) => format!("Invalid key file: {}", reason), - Error::VaultsAreNotSupported => "Vaults are not supported".into(), - Error::UnsupportedVault => "Vault is not supported for this operation".into(), - Error::InvalidVaultName => "Invalid vault name".into(), - Error::VaultNotFound => "Vault not found".into(), - Error::CreationFailed => "Account creation failed".into(), - Error::EthKey(ref err) => err.to_string(), - Error::EthKeyCrypto(ref err) => err.to_string(), - Error::EthCrypto(ref err) => err.to_string(), - Error::Derivation(ref err) => format!("Derivation error: {:?}", err), - Error::Custom(ref s) => s.clone(), - }; + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + let s = match *self { + Error::Io(ref err) => err.to_string(), + Error::InvalidPassword => "Invalid password".into(), + Error::InvalidSecret => "Invalid secret".into(), + Error::InvalidCryptoMeta => "Invalid crypted metadata".into(), + Error::InvalidAccount => "Invalid account".into(), + Error::InvalidMessage => "Invalid message".into(), + Error::InvalidKeyFile(ref reason) => format!("Invalid key file: {}", reason), + Error::VaultsAreNotSupported => "Vaults are not supported".into(), + Error::UnsupportedVault => "Vault is not supported for this operation".into(), + Error::InvalidVaultName => "Invalid vault name".into(), + Error::VaultNotFound => "Vault not found".into(), + Error::CreationFailed => "Account creation failed".into(), + Error::EthKey(ref err) => err.to_string(), + Error::EthKeyCrypto(ref err) => err.to_string(), + Error::EthCrypto(ref err) => err.to_string(), + Error::Derivation(ref err) => format!("Derivation error: {:?}", err), + Error::Custom(ref s) => s.clone(), + }; - write!(f, "{}", s) - } + write!(f, "{}", s) + } } impl From for Error { - fn from(err: IoError) -> Self { - Error::Io(err) - } + fn from(err: IoError) -> Self { + Error::Io(err) + } } impl From for Error { - fn from(err: EthKeyError) -> Self { - Error::EthKey(err) - } + fn from(err: EthKeyError) -> Self { + Error::EthKey(err) + } } impl From for Error { - fn from(err: ethkey::crypto::Error) -> Self { - Error::EthKeyCrypto(err) - } + fn from(err: ethkey::crypto::Error) -> Self { + Error::EthKeyCrypto(err) + } } impl From for Error { - fn from(err: EthCryptoError) -> Self { - Error::EthCrypto(err) - } + fn from(err: EthCryptoError) -> Self { + Error::EthCrypto(err) + } } impl From for Error { - fn from(err: crypto::error::ScryptError) -> Self { - Error::EthCrypto(err.into()) - } + fn from(err: crypto::error::ScryptError) -> Self { + Error::EthCrypto(err.into()) + } } impl From for Error { - fn from(err: crypto::error::SymmError) -> Self { - Error::EthCrypto(err.into()) - } + fn from(err: crypto::error::SymmError) -> Self { + Error::EthCrypto(err.into()) + } } impl From for Error { - fn from(err: DerivationError) -> Self { - Error::Derivation(err) - } + fn from(err: DerivationError) -> Self { + Error::Derivation(err) + } } diff --git a/accounts/ethstore/src/ethkey.rs b/accounts/ethstore/src/ethkey.rs index 8cd2c533adc..dc08af41911 100644 --- a/accounts/ethstore/src/ethkey.rs +++ b/accounts/ethstore/src/ethkey.rs @@ -1,41 +1,41 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! ethkey reexport to make documentation look pretty. pub use _ethkey::*; use json; impl Into for Address { - fn into(self) -> json::H160 { - let a: [u8; 20] = self.into(); - From::from(a) - } + fn into(self) -> json::H160 { + let a: [u8; 20] = self.into(); + From::from(a) + } } impl From for Address { - fn from(json: json::H160) -> Self { - let a: [u8; 20] = json.into(); - From::from(a) - } + fn from(json: json::H160) -> Self { + let a: [u8; 20] = json.into(); + From::from(a) + } } impl<'a> From<&'a json::H160> for Address { - fn from(json: &'a json::H160) -> Self { - let mut a = [0u8; 20]; - a.copy_from_slice(json); - From::from(a) - } + fn from(json: &'a json::H160) -> Self { + let mut a = [0u8; 20]; + a.copy_from_slice(json); + From::from(a) + } } diff --git a/accounts/ethstore/src/ethstore.rs b/accounts/ethstore/src/ethstore.rs index 92eb949673f..3de0d13802a 100644 --- a/accounts/ethstore/src/ethstore.rs +++ b/accounts/ethstore/src/ethstore.rs @@ -1,1134 +1,1523 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::collections::{BTreeMap, HashMap}; -use std::num::NonZeroU32; -use std::mem; -use std::path::PathBuf; use parking_lot::{Mutex, RwLock}; -use std::time::{Instant, Duration}; +use std::{ + collections::{BTreeMap, HashMap}, + num::NonZeroU32, + path::PathBuf, + time::{Duration, Instant}, +}; -use random::Random; -use ethkey::{self, Signature, Password, Address, Message, Secret, Public, KeyPair, ExtendedKeyPair}; -use accounts_dir::{KeyDirectory, VaultKeyDirectory, VaultKey, SetKeyError}; use account::SafeAccount; +use accounts_dir::{KeyDirectory, SetKeyError, VaultKey, VaultKeyDirectory}; +use ethkey::{ + self, Address, ExtendedKeyPair, KeyPair, Message, Password, Public, Secret, Signature, +}; +use json::{self, OpaqueKeyFile, Uuid}; use presale::PresaleWallet; -use json::{self, Uuid, OpaqueKeyFile}; -use {import, Error, SimpleSecretStore, SecretStore, SecretVaultRef, StoreAccountRef, Derivation, OpaqueSecret}; - +use random::Random; +use Derivation; +use Error; +use OpaqueSecret; +use SecretStore; +use SecretVaultRef; +use SimpleSecretStore; +use StoreAccountRef; lazy_static! { - static ref KEY_ITERATIONS: NonZeroU32 = - NonZeroU32::new(crypto::KEY_ITERATIONS as u32).expect("KEY_ITERATIONS > 0; qed"); + static ref KEY_ITERATIONS: NonZeroU32 = + NonZeroU32::new(crypto::KEY_ITERATIONS as u32).expect("KEY_ITERATIONS > 0; qed"); } /// Accounts store. pub struct EthStore { - store: EthMultiStore, + store: EthMultiStore, } impl EthStore { - /// Open a new accounts store with given key directory backend. - pub fn open(directory: Box) -> Result { - Self::open_with_iterations(directory, *KEY_ITERATIONS) - } - - /// Open a new account store with given key directory backend and custom number of iterations. - pub fn open_with_iterations(directory: Box, iterations: NonZeroU32) -> Result { - Ok(EthStore { - store: EthMultiStore::open_with_iterations(directory, iterations)?, - }) - } - - /// Modify account refresh timeout - how often they are re-read from `KeyDirectory`. - /// - /// Setting this to low values (or 0) will cause new accounts to be picked up quickly, - /// although it may induce heavy disk reads and is not recommended if you manage many keys (say over 10k). - /// - /// By default refreshing is disabled, so only accounts created using this instance of `EthStore` are taken into account. - pub fn set_refresh_time(&self, time: Duration) { - self.store.set_refresh_time(time) - } - - fn get(&self, account: &StoreAccountRef) -> Result { - let mut accounts = self.store.get_accounts(account)?.into_iter(); - accounts.next().ok_or(Error::InvalidAccount) - } + /// Open a new accounts store with given key directory backend. + pub fn open(directory: Box) -> Result { + Self::open_with_iterations(directory, *KEY_ITERATIONS) + } + + /// Open a new account store with given key directory backend and custom number of iterations. + pub fn open_with_iterations( + directory: Box, + iterations: NonZeroU32, + ) -> Result { + Ok(EthStore { + store: EthMultiStore::open_with_iterations(directory, iterations)?, + }) + } + + /// Modify account refresh timeout - how often they are re-read from `KeyDirectory`. + /// + /// Setting this to low values (or 0) will cause new accounts to be picked up quickly, + /// although it may induce heavy disk reads and is not recommended if you manage many keys (say over 10k). + /// + /// By default refreshing is disabled, so only accounts created using this instance of `EthStore` are taken into account. + pub fn set_refresh_time(&self, time: Duration) { + self.store.set_refresh_time(time) + } + + fn get(&self, account: &StoreAccountRef) -> Result { + let mut accounts = self.store.get_accounts(account)?.into_iter(); + accounts.next().ok_or(Error::InvalidAccount) + } } impl SimpleSecretStore for EthStore { - fn insert_account(&self, vault: SecretVaultRef, secret: Secret, password: &Password) -> Result { - self.store.insert_account(vault, secret, password) - } - - fn insert_derived(&self, vault: SecretVaultRef, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation) - -> Result - { - self.store.insert_derived(vault, account_ref, password, derivation) - } - - fn generate_derived(&self, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation) -> Result { - self.store.generate_derived(account_ref, password, derivation) - } - - fn account_ref(&self, address: &Address) -> Result { - self.store.account_ref(address) - } - - fn accounts(&self) -> Result, Error> { - self.store.accounts() - } - - fn change_password(&self, account: &StoreAccountRef, old_password: &Password, new_password: &Password) -> Result<(), Error> { - self.store.change_password(account, old_password, new_password) - } - - fn export_account(&self, account: &StoreAccountRef, password: &Password) -> Result { - self.store.export_account(account, password) - } - - fn remove_account(&self, account: &StoreAccountRef, password: &Password) -> Result<(), Error> { - self.store.remove_account(account, password) - } - - fn sign(&self, account: &StoreAccountRef, password: &Password, message: &Message) -> Result { - self.get(account)?.sign(password, message) - } - - fn sign_derived(&self, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation, message: &Message) - -> Result - { - self.store.sign_derived(account_ref, password, derivation, message) - } - - fn agree(&self, account: &StoreAccountRef, password: &Password, other: &Public) -> Result { - self.store.agree(account, password, other) - } - - fn decrypt(&self, account: &StoreAccountRef, password: &Password, shared_mac: &[u8], message: &[u8]) -> Result, Error> { - let account = self.get(account)?; - account.decrypt(password, shared_mac, message) - } - - fn create_vault(&self, name: &str, password: &Password) -> Result<(), Error> { - self.store.create_vault(name, password) - } - - fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error> { - self.store.open_vault(name, password) - } - - fn close_vault(&self, name: &str) -> Result<(), Error> { - self.store.close_vault(name) - } - - fn list_vaults(&self) -> Result, Error> { - self.store.list_vaults() - } - - fn list_opened_vaults(&self) -> Result, Error> { - self.store.list_opened_vaults() - } - - fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error> { - self.store.change_vault_password(name, new_password) - } - - fn change_account_vault(&self, vault: SecretVaultRef, account: StoreAccountRef) -> Result { - self.store.change_account_vault(vault, account) - } - - fn get_vault_meta(&self, name: &str) -> Result { - self.store.get_vault_meta(name) - } - - fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error> { - self.store.set_vault_meta(name, meta) - } + fn insert_account( + &self, + vault: SecretVaultRef, + secret: Secret, + password: &Password, + ) -> Result { + self.store.insert_account(vault, secret, password) + } + + fn insert_derived( + &self, + vault: SecretVaultRef, + account_ref: &StoreAccountRef, + password: &Password, + derivation: Derivation, + ) -> Result { + self.store + .insert_derived(vault, account_ref, password, derivation) + } + + fn generate_derived( + &self, + account_ref: &StoreAccountRef, + password: &Password, + derivation: Derivation, + ) -> Result { + self.store + .generate_derived(account_ref, password, derivation) + } + + fn account_ref(&self, address: &Address) -> Result { + self.store.account_ref(address) + } + + fn accounts(&self) -> Result, Error> { + self.store.accounts() + } + + fn change_password( + &self, + account: &StoreAccountRef, + old_password: &Password, + new_password: &Password, + ) -> Result<(), Error> { + self.store + .change_password(account, old_password, new_password) + } + + fn export_account( + &self, + account: &StoreAccountRef, + password: &Password, + ) -> Result { + self.store.export_account(account, password) + } + + fn remove_account(&self, account: &StoreAccountRef, password: &Password) -> Result<(), Error> { + self.store.remove_account(account, password) + } + + fn sign( + &self, + account: &StoreAccountRef, + password: &Password, + message: &Message, + ) -> Result { + self.get(account)?.sign(password, message) + } + + fn sign_derived( + &self, + account_ref: &StoreAccountRef, + password: &Password, + derivation: Derivation, + message: &Message, + ) -> Result { + self.store + .sign_derived(account_ref, password, derivation, message) + } + + fn agree( + &self, + account: &StoreAccountRef, + password: &Password, + other: &Public, + ) -> Result { + self.store.agree(account, password, other) + } + + fn decrypt( + &self, + account: &StoreAccountRef, + password: &Password, + shared_mac: &[u8], + message: &[u8], + ) -> Result, Error> { + let account = self.get(account)?; + account.decrypt(password, shared_mac, message) + } + + fn create_vault(&self, name: &str, password: &Password) -> Result<(), Error> { + self.store.create_vault(name, password) + } + + fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error> { + self.store.open_vault(name, password) + } + + fn close_vault(&self, name: &str) -> Result<(), Error> { + self.store.close_vault(name) + } + + fn list_vaults(&self) -> Result, Error> { + self.store.list_vaults() + } + + fn list_opened_vaults(&self) -> Result, Error> { + self.store.list_opened_vaults() + } + + fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error> { + self.store.change_vault_password(name, new_password) + } + + fn change_account_vault( + &self, + vault: SecretVaultRef, + account: StoreAccountRef, + ) -> Result { + self.store.change_account_vault(vault, account) + } + + fn get_vault_meta(&self, name: &str) -> Result { + self.store.get_vault_meta(name) + } + + fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error> { + self.store.set_vault_meta(name, meta) + } } impl SecretStore for EthStore { - fn raw_secret(&self, account: &StoreAccountRef, password: &Password) -> Result { - Ok(OpaqueSecret(self.get(account)?.crypto.secret(password)?)) - } - - fn import_presale(&self, vault: SecretVaultRef, json: &[u8], password: &Password) -> Result { - let json_wallet = json::PresaleWallet::load(json).map_err(|_| Error::InvalidKeyFile("Invalid JSON format".to_owned()))?; - let wallet = PresaleWallet::from(json_wallet); - let keypair = wallet.decrypt(password).map_err(|_| Error::InvalidPassword)?; - self.insert_account(vault, keypair.secret().clone(), password) - } - - fn import_wallet(&self, vault: SecretVaultRef, json: &[u8], password: &Password, gen_id: bool) -> Result { - let json_keyfile = json::KeyFile::load(json).map_err(|_| Error::InvalidKeyFile("Invalid JSON format".to_owned()))?; - let mut safe_account = SafeAccount::from_file(json_keyfile, None, &None)?; - - if gen_id { - safe_account.id = Random::random(); - } - - let secret = safe_account.crypto.secret(password).map_err(|_| Error::InvalidPassword)?; - safe_account.address = KeyPair::from_secret(secret)?.address(); - self.store.import(vault, safe_account) - } - - fn test_password(&self, account: &StoreAccountRef, password: &Password) -> Result { - let account = self.get(account)?; - Ok(account.check_password(password)) - } - - fn copy_account(&self, new_store: &SimpleSecretStore, new_vault: SecretVaultRef, account: &StoreAccountRef, password: &Password, new_password: &Password) -> Result<(), Error> { - let account = self.get(account)?; - let secret = account.crypto.secret(password)?; - new_store.insert_account(new_vault, secret, new_password)?; - Ok(()) - } - - fn public(&self, account: &StoreAccountRef, password: &Password) -> Result { - let account = self.get(account)?; - account.public(password) - } - - fn uuid(&self, account: &StoreAccountRef) -> Result { - let account = self.get(account)?; - Ok(account.id.into()) - } - - fn name(&self, account: &StoreAccountRef) -> Result { - let account = self.get(account)?; - Ok(account.name.clone()) - } - - fn meta(&self, account: &StoreAccountRef) -> Result { - let account = self.get(account)?; - Ok(account.meta.clone()) - } - - fn set_name(&self, account_ref: &StoreAccountRef, name: String) -> Result<(), Error> { - let old = self.get(account_ref)?; - let mut safe_account = old.clone(); - safe_account.name = name; - - // save to file - self.store.update(account_ref, old, safe_account) - } - - fn set_meta(&self, account_ref: &StoreAccountRef, meta: String) -> Result<(), Error> { - let old = self.get(account_ref)?; - let mut safe_account = old.clone(); - safe_account.meta = meta; - - // save to file - self.store.update(account_ref, old, safe_account) - } - - fn local_path(&self) -> PathBuf { - self.store.dir.path().cloned().unwrap_or_else(PathBuf::new) - } - - fn list_geth_accounts(&self, testnet: bool) -> Vec

{ - import::read_geth_accounts(testnet) - } - - fn import_geth_accounts(&self, vault: SecretVaultRef, desired: Vec
, testnet: bool) -> Result, Error> { - let imported_addresses = match vault { - SecretVaultRef::Root => import::import_geth_accounts(&*self.store.dir, desired.into_iter().collect(), testnet), - SecretVaultRef::Vault(vault_name) => { - if let Some(vault) = self.store.vaults.lock().get(&vault_name) { - import::import_geth_accounts(vault.as_key_directory(), desired.into_iter().collect(), testnet) - } else { - Err(Error::VaultNotFound) - } - }, - }; - - imported_addresses - .map(|a| a.into_iter().map(|a| StoreAccountRef::root(a)).collect()) - } + fn raw_secret( + &self, + account: &StoreAccountRef, + password: &Password, + ) -> Result { + Ok(OpaqueSecret(self.get(account)?.crypto.secret(password)?)) + } + + fn import_presale( + &self, + vault: SecretVaultRef, + json: &[u8], + password: &Password, + ) -> Result { + let json_wallet = json::PresaleWallet::load(json) + .map_err(|_| Error::InvalidKeyFile("Invalid JSON format".to_owned()))?; + let wallet = PresaleWallet::from(json_wallet); + let keypair = wallet + .decrypt(password) + .map_err(|_| Error::InvalidPassword)?; + self.insert_account(vault, keypair.secret().clone(), password) + } + + fn import_wallet( + &self, + vault: SecretVaultRef, + json: &[u8], + password: &Password, + gen_id: bool, + ) -> Result { + let json_keyfile = json::KeyFile::load(json) + .map_err(|_| Error::InvalidKeyFile("Invalid JSON format".to_owned()))?; + let mut safe_account = SafeAccount::from_file(json_keyfile, None, &None)?; + + if gen_id { + safe_account.id = Random::random(); + } + + let secret = safe_account + .crypto + .secret(password) + .map_err(|_| Error::InvalidPassword)?; + safe_account.address = KeyPair::from_secret(secret)?.address(); + self.store.import(vault, safe_account) + } + + fn test_password(&self, account: &StoreAccountRef, password: &Password) -> Result { + let account = self.get(account)?; + Ok(account.check_password(password)) + } + + fn copy_account( + &self, + new_store: &dyn SimpleSecretStore, + new_vault: SecretVaultRef, + account: &StoreAccountRef, + password: &Password, + new_password: &Password, + ) -> Result<(), Error> { + let account = self.get(account)?; + let secret = account.crypto.secret(password)?; + new_store.insert_account(new_vault, secret, new_password)?; + Ok(()) + } + + fn public(&self, account: &StoreAccountRef, password: &Password) -> Result { + let account = self.get(account)?; + account.public(password) + } + + fn uuid(&self, account: &StoreAccountRef) -> Result { + let account = self.get(account)?; + Ok(account.id.into()) + } + + fn name(&self, account: &StoreAccountRef) -> Result { + let account = self.get(account)?; + Ok(account.name.clone()) + } + + fn meta(&self, account: &StoreAccountRef) -> Result { + let account = self.get(account)?; + Ok(account.meta.clone()) + } + + fn set_name(&self, account_ref: &StoreAccountRef, name: String) -> Result<(), Error> { + let old = self.get(account_ref)?; + let mut safe_account = old.clone(); + safe_account.name = name; + + // save to file + self.store.update(account_ref, old, safe_account) + } + + fn set_meta(&self, account_ref: &StoreAccountRef, meta: String) -> Result<(), Error> { + let old = self.get(account_ref)?; + let mut safe_account = old.clone(); + safe_account.meta = meta; + + // save to file + self.store.update(account_ref, old, safe_account) + } + + fn local_path(&self) -> PathBuf { + self.store.dir.path().cloned().unwrap_or_else(PathBuf::new) + } } /// Similar to `EthStore` but may store many accounts (with different passwords) for the same `Address` pub struct EthMultiStore { - dir: Box, - iterations: NonZeroU32, - // order lock: cache, then vaults - cache: RwLock>>, - vaults: Mutex>>, - timestamp: Mutex, + dir: Box, + iterations: NonZeroU32, + // order lock: cache, then vaults + cache: RwLock>>, + vaults: Mutex>>, + timestamp: Mutex, } struct Timestamp { - dir_hash: Option, - last_checked: Instant, - refresh_time: Duration, + dir_hash: Option, + last_checked: Instant, + refresh_time: Duration, } impl EthMultiStore { - /// Open new multi-accounts store with given key directory backend. - pub fn open(directory: Box) -> Result { - Self::open_with_iterations(directory, *KEY_ITERATIONS) - } - - /// Open new multi-accounts store with given key directory backend and custom number of iterations for new keys. - pub fn open_with_iterations(directory: Box, iterations: NonZeroU32) -> Result { - let store = EthMultiStore { - dir: directory, - vaults: Mutex::new(HashMap::new()), - iterations: iterations, - cache: Default::default(), - timestamp: Mutex::new(Timestamp { - dir_hash: None, - last_checked: Instant::now(), - // by default we never refresh accounts - refresh_time: Duration::from_secs(u64::max_value()), - }), - }; - store.reload_accounts()?; - Ok(store) - } - - /// Modify account refresh timeout - how often they are re-read from `KeyDirectory`. - /// - /// Setting this to low values (or 0) will cause new accounts to be picked up quickly, - /// although it may induce heavy disk reads and is not recommended if you manage many keys (say over 10k). - /// - /// By default refreshing is disabled, so only accounts created using this instance of `EthStore` are taken into account. - pub fn set_refresh_time(&self, time: Duration) { - self.timestamp.lock().refresh_time = time; - } - - fn reload_if_changed(&self) -> Result<(), Error> { - let mut last_timestamp = self.timestamp.lock(); - let now = Instant::now(); - if now - last_timestamp.last_checked > last_timestamp.refresh_time { - let dir_hash = Some(self.dir.unique_repr()?); - last_timestamp.last_checked = now; - if last_timestamp.dir_hash == dir_hash { - return Ok(()) - } - self.reload_accounts()?; - last_timestamp.dir_hash = dir_hash; - } - Ok(()) - } - - fn reload_accounts(&self) -> Result<(), Error> { - let mut cache = self.cache.write(); - - let mut new_accounts = BTreeMap::new(); - for account in self.dir.load()? { - let account_ref = StoreAccountRef::root(account.address); - new_accounts - .entry(account_ref) - .or_insert_with(Vec::new) - .push(account); - } - for (vault_name, vault) in &*self.vaults.lock() { - for account in vault.load()? { - let account_ref = StoreAccountRef::vault(vault_name, account.address); - new_accounts - .entry(account_ref) - .or_insert_with(Vec::new) - .push(account); - } - } - - mem::replace(&mut *cache, new_accounts); - Ok(()) - } - - fn get_accounts(&self, account: &StoreAccountRef) -> Result, Error> { - let from_cache = |account| { - let cache = self.cache.read(); - if let Some(accounts) = cache.get(account) { - if !accounts.is_empty() { - return Some(accounts.clone()) - } - } - - None - }; - - match from_cache(account) { - Some(accounts) => Ok(accounts), - None => { - self.reload_if_changed()?; - from_cache(account).ok_or(Error::InvalidAccount) - } - } - } - - fn get_matching(&self, account: &StoreAccountRef, password: &Password) -> Result, Error> { - let accounts = self.get_accounts(account)?; - - Ok(accounts.into_iter() - .filter(|acc| acc.check_password(password)) - .collect() - ) - } - - fn import(&self, vault: SecretVaultRef, account: SafeAccount) -> Result { - // save to file - let account = match vault { - SecretVaultRef::Root => self.dir.insert(account)?, - SecretVaultRef::Vault(ref vault_name) => self.vaults.lock().get_mut(vault_name).ok_or(Error::VaultNotFound)?.insert(account)?, - }; - - // update cache - let account_ref = StoreAccountRef::new(vault, account.address.clone()); - let mut cache = self.cache.write(); - cache.entry(account_ref.clone()) - .or_insert_with(Vec::new) - .push(account); - - Ok(account_ref) - } - - fn update(&self, account_ref: &StoreAccountRef, old: SafeAccount, new: SafeAccount) -> Result<(), Error> { - // save to file - let account = match account_ref.vault { - SecretVaultRef::Root => self.dir.update(new)?, - SecretVaultRef::Vault(ref vault_name) => self.vaults.lock().get_mut(vault_name).ok_or(Error::VaultNotFound)?.update(new)?, - }; - - // update cache - let mut cache = self.cache.write(); - let accounts = cache.entry(account_ref.clone()).or_insert_with(Vec::new); - // Remove old account - accounts.retain(|acc| acc != &old); - // And push updated to the end - accounts.push(account); - Ok(()) - - } - - fn remove_safe_account(&self, account_ref: &StoreAccountRef, account: &SafeAccount) -> Result<(), Error> { - // Remove from dir - match account_ref.vault { - SecretVaultRef::Root => self.dir.remove(&account)?, - SecretVaultRef::Vault(ref vault_name) => self.vaults.lock().get(vault_name).ok_or(Error::VaultNotFound)?.remove(&account)?, - }; - - // Remove from cache - let mut cache = self.cache.write(); - let is_empty = { - if let Some(accounts) = cache.get_mut(account_ref) { - if let Some(position) = accounts.iter().position(|acc| acc == account) { - accounts.remove(position); - } - accounts.is_empty() - } else { - false - } - }; - - if is_empty { - cache.remove(account_ref); - } - - return Ok(()); - } - - fn generate(&self, secret: Secret, derivation: Derivation) -> Result { - let mut extended = ExtendedKeyPair::new(secret); - match derivation { - Derivation::Hierarchical(path) => { - for path_item in path { - extended = extended.derive( - if path_item.soft { ethkey::Derivation::Soft(path_item.index) } - else { ethkey::Derivation::Hard(path_item.index) } - )?; - } - }, - Derivation::SoftHash(h256) => { extended = extended.derive(ethkey::Derivation::Soft(h256))?; } - Derivation::HardHash(h256) => { extended = extended.derive(ethkey::Derivation::Hard(h256))?; } - } - Ok(extended) - } + /// Open new multi-accounts store with given key directory backend. + pub fn open(directory: Box) -> Result { + Self::open_with_iterations(directory, *KEY_ITERATIONS) + } + + /// Open new multi-accounts store with given key directory backend and custom number of iterations for new keys. + pub fn open_with_iterations( + directory: Box, + iterations: NonZeroU32, + ) -> Result { + let store = EthMultiStore { + dir: directory, + vaults: Mutex::new(HashMap::new()), + iterations: iterations, + cache: Default::default(), + timestamp: Mutex::new(Timestamp { + dir_hash: None, + last_checked: Instant::now(), + // by default we never refresh accounts + refresh_time: Duration::from_secs(u64::max_value()), + }), + }; + store.reload_accounts()?; + Ok(store) + } + + /// Modify account refresh timeout - how often they are re-read from `KeyDirectory`. + /// + /// Setting this to low values (or 0) will cause new accounts to be picked up quickly, + /// although it may induce heavy disk reads and is not recommended if you manage many keys (say over 10k). + /// + /// By default refreshing is disabled, so only accounts created using this instance of `EthStore` are taken into account. + pub fn set_refresh_time(&self, time: Duration) { + self.timestamp.lock().refresh_time = time; + } + + fn reload_if_changed(&self) -> Result<(), Error> { + let mut last_timestamp = self.timestamp.lock(); + let now = Instant::now(); + if now - last_timestamp.last_checked > last_timestamp.refresh_time { + let dir_hash = Some(self.dir.unique_repr()?); + last_timestamp.last_checked = now; + if last_timestamp.dir_hash == dir_hash { + return Ok(()); + } + self.reload_accounts()?; + last_timestamp.dir_hash = dir_hash; + } + Ok(()) + } + + fn reload_accounts(&self) -> Result<(), Error> { + let mut cache = self.cache.write(); + + let mut new_accounts = BTreeMap::new(); + for account in self.dir.load()? { + let account_ref = StoreAccountRef::root(account.address); + new_accounts + .entry(account_ref) + .or_insert_with(Vec::new) + .push(account); + } + for (vault_name, vault) in &*self.vaults.lock() { + for account in vault.load()? { + let account_ref = StoreAccountRef::vault(vault_name, account.address); + new_accounts + .entry(account_ref) + .or_insert_with(Vec::new) + .push(account); + } + } + + *cache = new_accounts; + Ok(()) + } + + fn get_accounts(&self, account: &StoreAccountRef) -> Result, Error> { + let from_cache = |account| { + let cache = self.cache.read(); + if let Some(accounts) = cache.get(account) { + if !accounts.is_empty() { + return Some(accounts.clone()); + } + } + + None + }; + + match from_cache(account) { + Some(accounts) => Ok(accounts), + None => { + self.reload_if_changed()?; + from_cache(account).ok_or(Error::InvalidAccount) + } + } + } + + fn get_matching( + &self, + account: &StoreAccountRef, + password: &Password, + ) -> Result, Error> { + let accounts = self.get_accounts(account)?; + + Ok(accounts + .into_iter() + .filter(|acc| acc.check_password(password)) + .collect()) + } + + fn import( + &self, + vault: SecretVaultRef, + account: SafeAccount, + ) -> Result { + // save to file + let account = match vault { + SecretVaultRef::Root => self.dir.insert(account)?, + SecretVaultRef::Vault(ref vault_name) => self + .vaults + .lock() + .get_mut(vault_name) + .ok_or(Error::VaultNotFound)? + .insert(account)?, + }; + + // update cache + let account_ref = StoreAccountRef::new(vault, account.address.clone()); + let mut cache = self.cache.write(); + cache + .entry(account_ref.clone()) + .or_insert_with(Vec::new) + .push(account); + + Ok(account_ref) + } + + fn update( + &self, + account_ref: &StoreAccountRef, + old: SafeAccount, + new: SafeAccount, + ) -> Result<(), Error> { + // save to file + let account = match account_ref.vault { + SecretVaultRef::Root => self.dir.update(new)?, + SecretVaultRef::Vault(ref vault_name) => self + .vaults + .lock() + .get_mut(vault_name) + .ok_or(Error::VaultNotFound)? + .update(new)?, + }; + + // update cache + let mut cache = self.cache.write(); + let accounts = cache.entry(account_ref.clone()).or_insert_with(Vec::new); + // Remove old account + accounts.retain(|acc| acc != &old); + // And push updated to the end + accounts.push(account); + Ok(()) + } + + fn remove_safe_account( + &self, + account_ref: &StoreAccountRef, + account: &SafeAccount, + ) -> Result<(), Error> { + // Remove from dir + match account_ref.vault { + SecretVaultRef::Root => self.dir.remove(&account)?, + SecretVaultRef::Vault(ref vault_name) => self + .vaults + .lock() + .get(vault_name) + .ok_or(Error::VaultNotFound)? + .remove(&account)?, + }; + + // Remove from cache + let mut cache = self.cache.write(); + let is_empty = { + if let Some(accounts) = cache.get_mut(account_ref) { + if let Some(position) = accounts.iter().position(|acc| acc == account) { + accounts.remove(position); + } + accounts.is_empty() + } else { + false + } + }; + + if is_empty { + cache.remove(account_ref); + } + + return Ok(()); + } + + fn generate(&self, secret: Secret, derivation: Derivation) -> Result { + let mut extended = ExtendedKeyPair::new(secret); + match derivation { + Derivation::Hierarchical(path) => { + for path_item in path { + extended = extended.derive(if path_item.soft { + ethkey::Derivation::Soft(path_item.index) + } else { + ethkey::Derivation::Hard(path_item.index) + })?; + } + } + Derivation::SoftHash(h256) => { + extended = extended.derive(ethkey::Derivation::Soft(h256))?; + } + Derivation::HardHash(h256) => { + extended = extended.derive(ethkey::Derivation::Hard(h256))?; + } + } + Ok(extended) + } } impl SimpleSecretStore for EthMultiStore { - fn insert_account(&self, vault: SecretVaultRef, secret: Secret, password: &Password) -> Result { - let keypair = KeyPair::from_secret(secret).map_err(|_| Error::CreationFailed)?; - let id: [u8; 16] = Random::random(); - let account = SafeAccount::create(&keypair, id, password, self.iterations, "".to_owned(), "{}".to_owned())?; - self.import(vault, account) - } - - fn insert_derived(&self, vault: SecretVaultRef, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation) - -> Result - { - let accounts = self.get_matching(account_ref, password)?; - for account in accounts { - let extended = self.generate(account.crypto.secret(password)?, derivation)?; - return self.insert_account(vault, extended.secret().as_raw().clone(), password); - } - Err(Error::InvalidPassword) - } - - fn generate_derived(&self, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation) - -> Result - { - let accounts = self.get_matching(&account_ref, password)?; - for account in accounts { - let extended = self.generate(account.crypto.secret(password)?, derivation)?; - return Ok(ethkey::public_to_address(extended.public().public())); - } - Err(Error::InvalidPassword) - } - - fn sign_derived(&self, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation, message: &Message) - -> Result - { - let accounts = self.get_matching(&account_ref, password)?; - for account in accounts { - let extended = self.generate(account.crypto.secret(password)?, derivation)?; - let secret = extended.secret().as_raw(); - return Ok(ethkey::sign(&secret, message)?) - } - Err(Error::InvalidPassword) - } - - fn account_ref(&self, address: &Address) -> Result { - let read_from_cache = |address: &Address| { - use std::collections::Bound; - let cache = self.cache.read(); - let mut r = cache.range((Bound::Included(*address), Bound::Included(*address))); - r.next().map(|(k, _)| k.clone()) - }; - - match read_from_cache(address) { - Some(account) => Ok(account), - None => { - self.reload_if_changed()?; - read_from_cache(address).ok_or(Error::InvalidAccount) - } - } - } - - fn accounts(&self) -> Result, Error> { - self.reload_if_changed()?; - Ok(self.cache.read().keys().cloned().collect()) - } - - fn remove_account(&self, account_ref: &StoreAccountRef, password: &Password) -> Result<(), Error> { - let accounts = self.get_matching(account_ref, password)?; - - for account in accounts { - return self.remove_safe_account(account_ref, &account); - } - - Err(Error::InvalidPassword) - } - - fn change_password(&self, account_ref: &StoreAccountRef, old_password: &Password, new_password: &Password) -> Result<(), Error> { - let accounts = self.get_matching(account_ref, old_password)?; - - if accounts.is_empty() { - return Err(Error::InvalidPassword); - } - - for account in accounts { - // Change password - let new_account = account.change_password(old_password, new_password, self.iterations)?; - self.update(account_ref, account, new_account)?; - } - - Ok(()) - } - - fn export_account(&self, account_ref: &StoreAccountRef, password: &Password) -> Result { - self.get_matching(account_ref, password)?.into_iter().nth(0).map(Into::into).ok_or(Error::InvalidPassword) - } - - fn sign(&self, account: &StoreAccountRef, password: &Password, message: &Message) -> Result { - let accounts = self.get_matching(account, password)?; - match accounts.first() { - Some(ref account) => account.sign(password, message), - None => Err(Error::InvalidPassword), - } - } - - fn decrypt(&self, account: &StoreAccountRef, password: &Password, shared_mac: &[u8], message: &[u8]) -> Result, Error> { - let accounts = self.get_matching(account, password)?; - match accounts.first() { - Some(ref account) => account.decrypt(password, shared_mac, message), - None => Err(Error::InvalidPassword), - } - } - - fn agree(&self, account: &StoreAccountRef, password: &Password, other: &Public) -> Result { - let accounts = self.get_matching(account, password)?; - match accounts.first() { - Some(ref account) => account.agree(password, other), - None => Err(Error::InvalidPassword), - } - } - - fn create_vault(&self, name: &str, password: &Password) -> Result<(), Error> { - let is_vault_created = { // lock border - let mut vaults = self.vaults.lock(); - if !vaults.contains_key(&name.to_owned()) { - let vault_provider = self.dir.as_vault_provider().ok_or(Error::VaultsAreNotSupported)?; - let vault = vault_provider.create(name, VaultKey::new(password, self.iterations))?; - vaults.insert(name.to_owned(), vault); - true - } else { - false - } - }; - - if is_vault_created { - self.reload_accounts()?; - } - - Ok(()) - } - - fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error> { - let is_vault_opened = { // lock border - let mut vaults = self.vaults.lock(); - if !vaults.contains_key(&name.to_owned()) { - let vault_provider = self.dir.as_vault_provider().ok_or(Error::VaultsAreNotSupported)?; - let vault = vault_provider.open(name, VaultKey::new(password, self.iterations))?; - vaults.insert(name.to_owned(), vault); - true - } else { - false - } - }; - - if is_vault_opened { - self.reload_accounts()?; - } - - Ok(()) - } - - fn close_vault(&self, name: &str) -> Result<(), Error> { - let is_vault_removed = self.vaults.lock().remove(&name.to_owned()).is_some(); - if is_vault_removed { - self.reload_accounts()?; - } - Ok(()) - } - - fn list_vaults(&self) -> Result, Error> { - let vault_provider = self.dir.as_vault_provider().ok_or(Error::VaultsAreNotSupported)?; - vault_provider.list_vaults() - } - - fn list_opened_vaults(&self) -> Result, Error> { - Ok(self.vaults.lock().keys().cloned().collect()) - } - - fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error> { - let old_key = self.vaults.lock().get(name).map(|v| v.key()).ok_or(Error::VaultNotFound)?; - let vault_provider = self.dir.as_vault_provider().ok_or(Error::VaultsAreNotSupported)?; - let vault = vault_provider.open(name, old_key)?; - match vault.set_key(VaultKey::new(new_password, self.iterations)) { - Ok(_) => { - self.close_vault(name) - .and_then(|_| self.open_vault(name, new_password)) - }, - Err(SetKeyError::Fatal(err)) => { - let _ = self.close_vault(name); - Err(err) - }, - Err(SetKeyError::NonFatalNew(err)) => { - let _ = self.close_vault(name) - .and_then(|_| self.open_vault(name, new_password)); - Err(err) - }, - Err(SetKeyError::NonFatalOld(err)) => Err(err), - } - } - - fn change_account_vault(&self, vault: SecretVaultRef, account_ref: StoreAccountRef) -> Result { - if account_ref.vault == vault { - return Ok(account_ref); - } - - let account = self.get_accounts(&account_ref)?.into_iter().nth(0).ok_or(Error::InvalidAccount)?; - let new_account_ref = self.import(vault, account.clone())?; - self.remove_safe_account(&account_ref, &account)?; - self.reload_accounts()?; - Ok(new_account_ref) - } - - fn get_vault_meta(&self, name: &str) -> Result { - // vault meta contains password hint - // => allow reading meta even if vault is not yet opened - self.vaults.lock() - .get(name) - .and_then(|v| Some(v.meta())) - .ok_or(Error::VaultNotFound) - .or_else(|_| { - let vault_provider = self.dir.as_vault_provider().ok_or(Error::VaultsAreNotSupported)?; - vault_provider.vault_meta(name) - }) - - } - - fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error> { - self.vaults.lock() - .get(name) - .ok_or(Error::VaultNotFound) - .and_then(|v| v.set_meta(meta)) - } + fn insert_account( + &self, + vault: SecretVaultRef, + secret: Secret, + password: &Password, + ) -> Result { + let keypair = KeyPair::from_secret(secret).map_err(|_| Error::CreationFailed)?; + let id: [u8; 16] = Random::random(); + let account = SafeAccount::create( + &keypair, + id, + password, + self.iterations, + "".to_owned(), + "{}".to_owned(), + )?; + self.import(vault, account) + } + + fn insert_derived( + &self, + vault: SecretVaultRef, + account_ref: &StoreAccountRef, + password: &Password, + derivation: Derivation, + ) -> Result { + let accounts = self.get_matching(account_ref, password)?; + for account in accounts { + let extended = self.generate(account.crypto.secret(password)?, derivation)?; + return self.insert_account(vault, extended.secret().as_raw().clone(), password); + } + Err(Error::InvalidPassword) + } + + fn generate_derived( + &self, + account_ref: &StoreAccountRef, + password: &Password, + derivation: Derivation, + ) -> Result { + let accounts = self.get_matching(&account_ref, password)?; + for account in accounts { + let extended = self.generate(account.crypto.secret(password)?, derivation)?; + return Ok(ethkey::public_to_address(extended.public().public())); + } + Err(Error::InvalidPassword) + } + + fn sign_derived( + &self, + account_ref: &StoreAccountRef, + password: &Password, + derivation: Derivation, + message: &Message, + ) -> Result { + let accounts = self.get_matching(&account_ref, password)?; + for account in accounts { + let extended = self.generate(account.crypto.secret(password)?, derivation)?; + let secret = extended.secret().as_raw(); + return Ok(ethkey::sign(&secret, message)?); + } + Err(Error::InvalidPassword) + } + + fn account_ref(&self, address: &Address) -> Result { + let read_from_cache = |address: &Address| { + use std::collections::Bound; + let cache = self.cache.read(); + let mut r = cache.range((Bound::Included(*address), Bound::Included(*address))); + r.next().map(|(k, _)| k.clone()) + }; + + match read_from_cache(address) { + Some(account) => Ok(account), + None => { + self.reload_if_changed()?; + read_from_cache(address).ok_or(Error::InvalidAccount) + } + } + } + + fn accounts(&self) -> Result, Error> { + self.reload_if_changed()?; + Ok(self.cache.read().keys().cloned().collect()) + } + + fn remove_account( + &self, + account_ref: &StoreAccountRef, + password: &Password, + ) -> Result<(), Error> { + let accounts = self.get_matching(account_ref, password)?; + + for account in accounts { + return self.remove_safe_account(account_ref, &account); + } + + Err(Error::InvalidPassword) + } + + fn change_password( + &self, + account_ref: &StoreAccountRef, + old_password: &Password, + new_password: &Password, + ) -> Result<(), Error> { + let accounts = self.get_matching(account_ref, old_password)?; + + if accounts.is_empty() { + return Err(Error::InvalidPassword); + } + + for account in accounts { + // Change password + let new_account = + account.change_password(old_password, new_password, self.iterations)?; + self.update(account_ref, account, new_account)?; + } + + Ok(()) + } + + fn export_account( + &self, + account_ref: &StoreAccountRef, + password: &Password, + ) -> Result { + self.get_matching(account_ref, password)? + .into_iter() + .nth(0) + .map(Into::into) + .ok_or(Error::InvalidPassword) + } + + fn sign( + &self, + account: &StoreAccountRef, + password: &Password, + message: &Message, + ) -> Result { + let accounts = self.get_matching(account, password)?; + match accounts.first() { + Some(ref account) => account.sign(password, message), + None => Err(Error::InvalidPassword), + } + } + + fn decrypt( + &self, + account: &StoreAccountRef, + password: &Password, + shared_mac: &[u8], + message: &[u8], + ) -> Result, Error> { + let accounts = self.get_matching(account, password)?; + match accounts.first() { + Some(ref account) => account.decrypt(password, shared_mac, message), + None => Err(Error::InvalidPassword), + } + } + + fn agree( + &self, + account: &StoreAccountRef, + password: &Password, + other: &Public, + ) -> Result { + let accounts = self.get_matching(account, password)?; + match accounts.first() { + Some(ref account) => account.agree(password, other), + None => Err(Error::InvalidPassword), + } + } + + fn create_vault(&self, name: &str, password: &Password) -> Result<(), Error> { + let is_vault_created = { + // lock border + let mut vaults = self.vaults.lock(); + if !vaults.contains_key(&name.to_owned()) { + let vault_provider = self + .dir + .as_vault_provider() + .ok_or(Error::VaultsAreNotSupported)?; + let vault = + vault_provider.create(name, VaultKey::new(password, self.iterations))?; + vaults.insert(name.to_owned(), vault); + true + } else { + false + } + }; + + if is_vault_created { + self.reload_accounts()?; + } + + Ok(()) + } + + fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error> { + let is_vault_opened = { + // lock border + let mut vaults = self.vaults.lock(); + if !vaults.contains_key(&name.to_owned()) { + let vault_provider = self + .dir + .as_vault_provider() + .ok_or(Error::VaultsAreNotSupported)?; + let vault = vault_provider.open(name, VaultKey::new(password, self.iterations))?; + vaults.insert(name.to_owned(), vault); + true + } else { + false + } + }; + + if is_vault_opened { + self.reload_accounts()?; + } + + Ok(()) + } + + fn close_vault(&self, name: &str) -> Result<(), Error> { + let is_vault_removed = self.vaults.lock().remove(&name.to_owned()).is_some(); + if is_vault_removed { + self.reload_accounts()?; + } + Ok(()) + } + + fn list_vaults(&self) -> Result, Error> { + let vault_provider = self + .dir + .as_vault_provider() + .ok_or(Error::VaultsAreNotSupported)?; + vault_provider.list_vaults() + } + + fn list_opened_vaults(&self) -> Result, Error> { + Ok(self.vaults.lock().keys().cloned().collect()) + } + + fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error> { + let old_key = self + .vaults + .lock() + .get(name) + .map(|v| v.key()) + .ok_or(Error::VaultNotFound)?; + let vault_provider = self + .dir + .as_vault_provider() + .ok_or(Error::VaultsAreNotSupported)?; + let vault = vault_provider.open(name, old_key)?; + match vault.set_key(VaultKey::new(new_password, self.iterations)) { + Ok(_) => self + .close_vault(name) + .and_then(|_| self.open_vault(name, new_password)), + Err(SetKeyError::Fatal(err)) => { + let _ = self.close_vault(name); + Err(err) + } + Err(SetKeyError::NonFatalNew(err)) => { + let _ = self + .close_vault(name) + .and_then(|_| self.open_vault(name, new_password)); + Err(err) + } + Err(SetKeyError::NonFatalOld(err)) => Err(err), + } + } + + fn change_account_vault( + &self, + vault: SecretVaultRef, + account_ref: StoreAccountRef, + ) -> Result { + if account_ref.vault == vault { + return Ok(account_ref); + } + + let account = self + .get_accounts(&account_ref)? + .into_iter() + .nth(0) + .ok_or(Error::InvalidAccount)?; + let new_account_ref = self.import(vault, account.clone())?; + self.remove_safe_account(&account_ref, &account)?; + self.reload_accounts()?; + Ok(new_account_ref) + } + + fn get_vault_meta(&self, name: &str) -> Result { + // vault meta contains password hint + // => allow reading meta even if vault is not yet opened + self.vaults + .lock() + .get(name) + .and_then(|v| Some(v.meta())) + .ok_or(Error::VaultNotFound) + .or_else(|_| { + let vault_provider = self + .dir + .as_vault_provider() + .ok_or(Error::VaultsAreNotSupported)?; + vault_provider.vault_meta(name) + }) + } + + fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error> { + self.vaults + .lock() + .get(name) + .ok_or(Error::VaultNotFound) + .and_then(|v| v.set_meta(meta)) + } } #[cfg(test)] mod tests { - extern crate tempdir; - - use accounts_dir::{KeyDirectory, MemoryDirectory, RootDiskDirectory}; - use ethkey::{Random, Generator, KeyPair}; - use secret_store::{SimpleSecretStore, SecretStore, SecretVaultRef, StoreAccountRef, Derivation}; - use super::{EthStore, EthMultiStore}; - use self::tempdir::TempDir; - use ethereum_types::H256; - - fn keypair() -> KeyPair { - Random.generate().unwrap() - } - - fn store() -> EthStore { - EthStore::open(Box::new(MemoryDirectory::default())).expect("MemoryDirectory always load successfuly; qed") - } - - fn multi_store() -> EthMultiStore { - EthMultiStore::open(Box::new(MemoryDirectory::default())).expect("MemoryDirectory always load successfuly; qed") - } - - struct RootDiskDirectoryGuard { - pub key_dir: Option>, - _path: TempDir, - } - - impl RootDiskDirectoryGuard { - pub fn new() -> Self { - let temp_path = TempDir::new("").unwrap(); - let disk_dir = Box::new(RootDiskDirectory::create(temp_path.path()).unwrap()); - - RootDiskDirectoryGuard { - key_dir: Some(disk_dir), - _path: temp_path, - } - } - } - - #[test] - fn should_insert_account_successfully() { - // given - let store = store(); - let keypair = keypair(); - - // when - let passwd = "test".into(); - let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd).unwrap(); - - // then - assert_eq!(address, StoreAccountRef::root(keypair.address())); - assert!(store.get(&address).is_ok(), "Should contain account."); - assert_eq!(store.accounts().unwrap().len(), 1, "Should have one account."); - } - - #[test] - fn should_update_meta_and_name() { - // given - let store = store(); - let keypair = keypair(); - let passwd = "test".into(); - let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd).unwrap(); - assert_eq!(&store.meta(&address).unwrap(), "{}"); - assert_eq!(&store.name(&address).unwrap(), ""); - - // when - store.set_meta(&address, "meta".into()).unwrap(); - store.set_name(&address, "name".into()).unwrap(); - - // then - assert_eq!(&store.meta(&address).unwrap(), "meta"); - assert_eq!(&store.name(&address).unwrap(), "name"); - assert_eq!(store.accounts().unwrap().len(), 1); - } - - #[test] - fn should_remove_account() { - // given - let store = store(); - let passwd = "test".into(); - let keypair = keypair(); - let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd).unwrap(); - - // when - store.remove_account(&address, &passwd).unwrap(); - - // then - assert_eq!(store.accounts().unwrap().len(), 0, "Should remove account."); - } - - #[test] - fn should_return_true_if_password_is_correct() { - // given - let store = store(); - let passwd = "test".into(); - let keypair = keypair(); - let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd).unwrap(); - - // when - let res1 = store.test_password(&address, &"x".into()).unwrap(); - let res2 = store.test_password(&address, &passwd).unwrap(); - - assert!(!res1, "First password should be invalid."); - assert!(res2, "Second password should be correct."); - } - - #[test] - fn multistore_should_be_able_to_have_the_same_account_twice() { - // given - let store = multi_store(); - let passwd1 = "test".into(); - let passwd2 = "xyz".into(); - let keypair = keypair(); - let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd1).unwrap(); - let address2 = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd2).unwrap(); - assert_eq!(address, address2); - - // when - assert!(store.remove_account(&address, &passwd1).is_ok(), "First password should work."); - assert_eq!(store.accounts().unwrap().len(), 1); - - assert!(store.remove_account(&address, &passwd2).is_ok(), "Second password should work too."); - assert_eq!(store.accounts().unwrap().len(), 0); - } - - #[test] - fn should_copy_account() { - // given - let store = store(); - let passwd1 = "test".into(); - let passwd2 = "xzy".into(); - let multi_store = multi_store(); - let keypair = keypair(); - let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd1).unwrap(); - assert_eq!(multi_store.accounts().unwrap().len(), 0); - - // when - store.copy_account(&multi_store, SecretVaultRef::Root, &address, &passwd1, &passwd2).unwrap(); - - // then - assert!(store.test_password(&address, &passwd1).unwrap(), "First password should work for store."); - assert!(multi_store.sign(&address, &passwd2, &Default::default()).is_ok(), "Second password should work for second store."); - assert_eq!(multi_store.accounts().unwrap().len(), 1); - } - - #[test] - fn should_create_and_open_vaults() { - // given - let mut dir = RootDiskDirectoryGuard::new(); - let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap(); - let name1 = "vault1"; let password1 = "password1".into(); - let name2 = "vault2"; let password2 = "password2".into(); - let keypair1 = keypair(); - let keypair2 = keypair(); - let keypair3 = keypair(); let password3 = "password3".into(); - - // when - store.create_vault(name1, &password1).unwrap(); - store.create_vault(name2, &password2).unwrap(); - - // then [can create vaults] ^^^ - - // and when - store.insert_account(SecretVaultRef::Vault(name1.to_owned()), keypair1.secret().clone(), &password1).unwrap(); - store.insert_account(SecretVaultRef::Vault(name2.to_owned()), keypair2.secret().clone(), &password2).unwrap(); - store.insert_account(SecretVaultRef::Root, keypair3.secret().clone(), &password3).unwrap(); - store.insert_account(SecretVaultRef::Vault("vault3".to_owned()), keypair1.secret().clone(), &password3).unwrap_err(); - let accounts = store.accounts().unwrap(); - - // then [can create accounts in vaults] - assert_eq!(accounts.len(), 3); - assert!(accounts.iter().any(|a| a.vault == SecretVaultRef::Root)); - assert!(accounts.iter().any(|a| a.vault == SecretVaultRef::Vault(name1.to_owned()))); - assert!(accounts.iter().any(|a| a.vault == SecretVaultRef::Vault(name2.to_owned()))); - - // and when - store.close_vault(name1).unwrap(); - store.close_vault(name2).unwrap(); - store.close_vault("vault3").unwrap(); - let accounts = store.accounts().unwrap(); - - // then [can close vaults + accounts from vaults disappear] - assert_eq!(accounts.len(), 1); - assert!(accounts.iter().any(|a| a.vault == SecretVaultRef::Root)); - - // and when - store.open_vault(name1, &password2).unwrap_err(); - store.open_vault(name2, &password1).unwrap_err(); - store.open_vault(name1, &password1).unwrap(); - store.open_vault(name2, &password2).unwrap(); - let accounts = store.accounts().unwrap(); - - // then [can check vaults on open + can reopen vaults + accounts from vaults appear] - assert_eq!(accounts.len(), 3); - assert!(accounts.iter().any(|a| a.vault == SecretVaultRef::Root)); - assert!(accounts.iter().any(|a| a.vault == SecretVaultRef::Vault(name1.to_owned()))); - assert!(accounts.iter().any(|a| a.vault == SecretVaultRef::Vault(name2.to_owned()))); - } - - #[test] - fn should_move_vault_acounts() { - // given - let mut dir = RootDiskDirectoryGuard::new(); - let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap(); - let name1 = "vault1"; let password1 = "password1".into(); - let name2 = "vault2"; let password2 = "password2".into(); - let password3 = "password3".into(); - let keypair1 = keypair(); - let keypair2 = keypair(); - let keypair3 = keypair(); - - // when - store.create_vault(name1, &password1).unwrap(); - store.create_vault(name2, &password2).unwrap(); - let account1 = store.insert_account(SecretVaultRef::Vault(name1.to_owned()), keypair1.secret().clone(), &password1).unwrap(); - let account2 = store.insert_account(SecretVaultRef::Vault(name1.to_owned()), keypair2.secret().clone(), &password1).unwrap(); - let account3 = store.insert_account(SecretVaultRef::Root, keypair3.secret().clone(), &password3).unwrap(); - - // then - let account1 = store.change_account_vault(SecretVaultRef::Root, account1.clone()).unwrap(); - let account2 = store.change_account_vault(SecretVaultRef::Vault(name2.to_owned()), account2.clone()).unwrap(); - let account3 = store.change_account_vault(SecretVaultRef::Vault(name2.to_owned()), account3).unwrap(); - let accounts = store.accounts().unwrap(); - assert_eq!(accounts.len(), 3); - assert!(accounts.iter().any(|a| a == &StoreAccountRef::root(account1.address.clone()))); - assert!(accounts.iter().any(|a| a == &StoreAccountRef::vault(name2, account2.address.clone()))); - assert!(accounts.iter().any(|a| a == &StoreAccountRef::vault(name2, account3.address.clone()))); - - // and then - assert_eq!(store.meta(&StoreAccountRef::root(account1.address)).unwrap(), r#"{}"#); - assert_eq!(store.meta(&StoreAccountRef::vault("vault2", account2.address)).unwrap(), r#"{"vault":"vault2"}"#); - assert_eq!(store.meta(&StoreAccountRef::vault("vault2", account3.address)).unwrap(), r#"{"vault":"vault2"}"#); - } - - #[test] - fn should_not_remove_account_when_moving_to_self() { - // given - let mut dir = RootDiskDirectoryGuard::new(); - let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap(); - let password1 = "password1".into(); - let keypair1 = keypair(); - - // when - let account1 = store.insert_account(SecretVaultRef::Root, keypair1.secret().clone(), &password1).unwrap(); - store.change_account_vault(SecretVaultRef::Root, account1).unwrap(); - - // then - let accounts = store.accounts().unwrap(); - assert_eq!(accounts.len(), 1); - } - - #[test] - fn should_remove_account_from_vault() { - // given - let mut dir = RootDiskDirectoryGuard::new(); - let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap(); - let name1 = "vault1"; let password1 = "password1".into(); - let keypair1 = keypair(); - - // when - store.create_vault(name1, &password1).unwrap(); - let account1 = store.insert_account(SecretVaultRef::Vault(name1.to_owned()), keypair1.secret().clone(), &password1).unwrap(); - assert_eq!(store.accounts().unwrap().len(), 1); - - // then - store.remove_account(&account1, &password1).unwrap(); - assert_eq!(store.accounts().unwrap().len(), 0); - } - - #[test] - fn should_not_remove_account_from_vault_when_password_is_incorrect() { - // given - let mut dir = RootDiskDirectoryGuard::new(); - let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap(); - let name1 = "vault1"; let password1 = "password1".into(); - let password2 = "password2".into(); - let keypair1 = keypair(); - - // when - store.create_vault(name1, &password1).unwrap(); - let account1 = store.insert_account(SecretVaultRef::Vault(name1.to_owned()), keypair1.secret().clone(), &password1).unwrap(); - assert_eq!(store.accounts().unwrap().len(), 1); - - // then - store.remove_account(&account1, &password2).unwrap_err(); - assert_eq!(store.accounts().unwrap().len(), 1); - } - - #[test] - fn should_change_vault_password() { - // given - let mut dir = RootDiskDirectoryGuard::new(); - let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap(); - let name = "vault"; let password = "password".into(); - let keypair = keypair(); - - // when - store.create_vault(name, &password).unwrap(); - store.insert_account(SecretVaultRef::Vault(name.to_owned()), keypair.secret().clone(), &password).unwrap(); - - // then - assert_eq!(store.accounts().unwrap().len(), 1); - let new_password = "new_password".into(); - store.change_vault_password(name, &new_password).unwrap(); - assert_eq!(store.accounts().unwrap().len(), 1); - - // and when - store.close_vault(name).unwrap(); - - // then - store.open_vault(name, &new_password).unwrap(); - assert_eq!(store.accounts().unwrap().len(), 1); - } - - #[test] - fn should_have_different_passwords_for_vault_secret_and_meta() { - // given - let mut dir = RootDiskDirectoryGuard::new(); - let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap(); - let name = "vault"; let password = "password".into(); - let secret_password = "sec_password".into(); - let keypair = keypair(); - - // when - store.create_vault(name, &password).unwrap(); - let account_ref = store.insert_account(SecretVaultRef::Vault(name.to_owned()), keypair.secret().clone(), &secret_password).unwrap(); - - // then - assert_eq!(store.accounts().unwrap().len(), 1); - let new_secret_password = "new_sec_password".into(); - store.change_password(&account_ref, &secret_password, &new_secret_password).unwrap(); - assert_eq!(store.accounts().unwrap().len(), 1); - } - - #[test] - fn should_list_opened_vaults() { - // given - let mut dir = RootDiskDirectoryGuard::new(); - let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap(); - let name1 = "vault1"; let password1 = "password1".into(); - let name2 = "vault2"; let password2 = "password2".into(); - let name3 = "vault3"; let password3 = "password3".into(); - - // when - store.create_vault(name1, &password1).unwrap(); - store.create_vault(name2, &password2).unwrap(); - store.create_vault(name3, &password3).unwrap(); - store.close_vault(name2).unwrap(); - - // then - let opened_vaults = store.list_opened_vaults().unwrap(); - assert_eq!(opened_vaults.len(), 2); - assert!(opened_vaults.iter().any(|v| &*v == name1)); - assert!(opened_vaults.iter().any(|v| &*v == name3)); - } - - #[test] - fn should_manage_vaults_meta() { - // given - let mut dir = RootDiskDirectoryGuard::new(); - let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap(); - let name1 = "vault1"; let password1 = "password1".into(); - - // when - store.create_vault(name1, &password1).unwrap(); - - // then - assert_eq!(store.get_vault_meta(name1).unwrap(), "{}".to_owned()); - assert!(store.set_vault_meta(name1, "Hello, world!!!").is_ok()); - assert_eq!(store.get_vault_meta(name1).unwrap(), "Hello, world!!!".to_owned()); - - // and when - store.close_vault(name1).unwrap(); - store.open_vault(name1, &password1).unwrap(); - - // then - assert_eq!(store.get_vault_meta(name1).unwrap(), "Hello, world!!!".to_owned()); - - // and when - store.close_vault(name1).unwrap(); - - // then - assert_eq!(store.get_vault_meta(name1).unwrap(), "Hello, world!!!".to_owned()); - assert!(store.get_vault_meta("vault2").is_err()); - } - - #[test] - fn should_store_derived_keys() { - // given we have one account in the store - let store = store(); - let keypair = keypair(); - let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), &"test".into()).unwrap(); - - // when we deriving from that account - let derived = store.insert_derived( - SecretVaultRef::Root, - &address, - &"test".into(), - Derivation::HardHash(H256::from(0)), - ).unwrap(); - - // there should be 2 accounts in the store - let accounts = store.accounts().unwrap(); - assert_eq!(accounts.len(), 2); - - // and we can sign with the derived contract - assert!(store.sign(&derived, &"test".into(), &Default::default()).is_ok(), "Second password should work for second store."); - } - - #[test] - fn should_save_meta_when_setting_before_password() { - // given - let mut dir = RootDiskDirectoryGuard::new(); - let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap(); - let name = "vault"; let password = "password1".into(); - let new_password = "password2".into(); - - // when - store.create_vault(name, &password).unwrap(); - store.set_vault_meta(name, "OldMeta").unwrap(); - store.change_vault_password(name, &new_password).unwrap(); - - // then - assert_eq!(store.get_vault_meta(name).unwrap(), "OldMeta".to_owned()); - } - - #[test] - fn should_export_account() { - // given - let store = store(); - let keypair = keypair(); - let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), &"test".into()).unwrap(); - - // when - let exported = store.export_account(&address, &"test".into()); - - // then - assert!(exported.is_ok(), "Should export single account: {:?}", exported); - } + extern crate tempdir; + + use self::tempdir::TempDir; + use super::{EthMultiStore, EthStore}; + use accounts_dir::{KeyDirectory, MemoryDirectory, RootDiskDirectory}; + use ethereum_types::H256; + use ethkey::{Generator, KeyPair, Random}; + use secret_store::{ + Derivation, SecretStore, SecretVaultRef, SimpleSecretStore, StoreAccountRef, + }; + + fn keypair() -> KeyPair { + Random.generate().unwrap() + } + + fn store() -> EthStore { + EthStore::open(Box::new(MemoryDirectory::default())) + .expect("MemoryDirectory always load successfuly; qed") + } + + fn multi_store() -> EthMultiStore { + EthMultiStore::open(Box::new(MemoryDirectory::default())) + .expect("MemoryDirectory always load successfuly; qed") + } + + struct RootDiskDirectoryGuard { + pub key_dir: Option>, + _path: TempDir, + } + + impl RootDiskDirectoryGuard { + pub fn new() -> Self { + let temp_path = TempDir::new("").unwrap(); + let disk_dir = Box::new(RootDiskDirectory::create(temp_path.path()).unwrap()); + + RootDiskDirectoryGuard { + key_dir: Some(disk_dir), + _path: temp_path, + } + } + } + + #[test] + fn should_insert_account_successfully() { + // given + let store = store(); + let keypair = keypair(); + + // when + let passwd = "test".into(); + let address = store + .insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd) + .unwrap(); + + // then + assert_eq!(address, StoreAccountRef::root(keypair.address())); + assert!(store.get(&address).is_ok(), "Should contain account."); + assert_eq!( + store.accounts().unwrap().len(), + 1, + "Should have one account." + ); + } + + #[test] + fn should_update_meta_and_name() { + // given + let store = store(); + let keypair = keypair(); + let passwd = "test".into(); + let address = store + .insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd) + .unwrap(); + assert_eq!(&store.meta(&address).unwrap(), "{}"); + assert_eq!(&store.name(&address).unwrap(), ""); + + // when + store.set_meta(&address, "meta".into()).unwrap(); + store.set_name(&address, "name".into()).unwrap(); + + // then + assert_eq!(&store.meta(&address).unwrap(), "meta"); + assert_eq!(&store.name(&address).unwrap(), "name"); + assert_eq!(store.accounts().unwrap().len(), 1); + } + + #[test] + fn should_remove_account() { + // given + let store = store(); + let passwd = "test".into(); + let keypair = keypair(); + let address = store + .insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd) + .unwrap(); + + // when + store.remove_account(&address, &passwd).unwrap(); + + // then + assert_eq!(store.accounts().unwrap().len(), 0, "Should remove account."); + } + + #[test] + fn should_return_true_if_password_is_correct() { + // given + let store = store(); + let passwd = "test".into(); + let keypair = keypair(); + let address = store + .insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd) + .unwrap(); + + // when + let res1 = store.test_password(&address, &"x".into()).unwrap(); + let res2 = store.test_password(&address, &passwd).unwrap(); + + assert!(!res1, "First password should be invalid."); + assert!(res2, "Second password should be correct."); + } + + #[test] + fn multistore_should_be_able_to_have_the_same_account_twice() { + // given + let store = multi_store(); + let passwd1 = "test".into(); + let passwd2 = "xyz".into(); + let keypair = keypair(); + let address = store + .insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd1) + .unwrap(); + let address2 = store + .insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd2) + .unwrap(); + assert_eq!(address, address2); + + // when + assert!( + store.remove_account(&address, &passwd1).is_ok(), + "First password should work." + ); + assert_eq!(store.accounts().unwrap().len(), 1); + + assert!( + store.remove_account(&address, &passwd2).is_ok(), + "Second password should work too." + ); + assert_eq!(store.accounts().unwrap().len(), 0); + } + + #[test] + fn should_copy_account() { + // given + let store = store(); + let passwd1 = "test".into(); + let passwd2 = "xzy".into(); + let multi_store = multi_store(); + let keypair = keypair(); + let address = store + .insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd1) + .unwrap(); + assert_eq!(multi_store.accounts().unwrap().len(), 0); + + // when + store + .copy_account( + &multi_store, + SecretVaultRef::Root, + &address, + &passwd1, + &passwd2, + ) + .unwrap(); + + // then + assert!( + store.test_password(&address, &passwd1).unwrap(), + "First password should work for store." + ); + assert!( + multi_store + .sign(&address, &passwd2, &Default::default()) + .is_ok(), + "Second password should work for second store." + ); + assert_eq!(multi_store.accounts().unwrap().len(), 1); + } + + #[test] + fn should_create_and_open_vaults() { + // given + let mut dir = RootDiskDirectoryGuard::new(); + let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap(); + let name1 = "vault1"; + let password1 = "password1".into(); + let name2 = "vault2"; + let password2 = "password2".into(); + let keypair1 = keypair(); + let keypair2 = keypair(); + let keypair3 = keypair(); + let password3 = "password3".into(); + + // when + store.create_vault(name1, &password1).unwrap(); + store.create_vault(name2, &password2).unwrap(); + + // then [can create vaults] ^^^ + + // and when + store + .insert_account( + SecretVaultRef::Vault(name1.to_owned()), + keypair1.secret().clone(), + &password1, + ) + .unwrap(); + store + .insert_account( + SecretVaultRef::Vault(name2.to_owned()), + keypair2.secret().clone(), + &password2, + ) + .unwrap(); + store + .insert_account(SecretVaultRef::Root, keypair3.secret().clone(), &password3) + .unwrap(); + store + .insert_account( + SecretVaultRef::Vault("vault3".to_owned()), + keypair1.secret().clone(), + &password3, + ) + .unwrap_err(); + let accounts = store.accounts().unwrap(); + + // then [can create accounts in vaults] + assert_eq!(accounts.len(), 3); + assert!(accounts.iter().any(|a| a.vault == SecretVaultRef::Root)); + assert!(accounts + .iter() + .any(|a| a.vault == SecretVaultRef::Vault(name1.to_owned()))); + assert!(accounts + .iter() + .any(|a| a.vault == SecretVaultRef::Vault(name2.to_owned()))); + + // and when + store.close_vault(name1).unwrap(); + store.close_vault(name2).unwrap(); + store.close_vault("vault3").unwrap(); + let accounts = store.accounts().unwrap(); + + // then [can close vaults + accounts from vaults disappear] + assert_eq!(accounts.len(), 1); + assert!(accounts.iter().any(|a| a.vault == SecretVaultRef::Root)); + + // and when + store.open_vault(name1, &password2).unwrap_err(); + store.open_vault(name2, &password1).unwrap_err(); + store.open_vault(name1, &password1).unwrap(); + store.open_vault(name2, &password2).unwrap(); + let accounts = store.accounts().unwrap(); + + // then [can check vaults on open + can reopen vaults + accounts from vaults appear] + assert_eq!(accounts.len(), 3); + assert!(accounts.iter().any(|a| a.vault == SecretVaultRef::Root)); + assert!(accounts + .iter() + .any(|a| a.vault == SecretVaultRef::Vault(name1.to_owned()))); + assert!(accounts + .iter() + .any(|a| a.vault == SecretVaultRef::Vault(name2.to_owned()))); + } + + #[test] + fn should_move_vault_acounts() { + // given + let mut dir = RootDiskDirectoryGuard::new(); + let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap(); + let name1 = "vault1"; + let password1 = "password1".into(); + let name2 = "vault2"; + let password2 = "password2".into(); + let password3 = "password3".into(); + let keypair1 = keypair(); + let keypair2 = keypair(); + let keypair3 = keypair(); + + // when + store.create_vault(name1, &password1).unwrap(); + store.create_vault(name2, &password2).unwrap(); + let account1 = store + .insert_account( + SecretVaultRef::Vault(name1.to_owned()), + keypair1.secret().clone(), + &password1, + ) + .unwrap(); + let account2 = store + .insert_account( + SecretVaultRef::Vault(name1.to_owned()), + keypair2.secret().clone(), + &password1, + ) + .unwrap(); + let account3 = store + .insert_account(SecretVaultRef::Root, keypair3.secret().clone(), &password3) + .unwrap(); + + // then + let account1 = store + .change_account_vault(SecretVaultRef::Root, account1.clone()) + .unwrap(); + let account2 = store + .change_account_vault(SecretVaultRef::Vault(name2.to_owned()), account2.clone()) + .unwrap(); + let account3 = store + .change_account_vault(SecretVaultRef::Vault(name2.to_owned()), account3) + .unwrap(); + let accounts = store.accounts().unwrap(); + assert_eq!(accounts.len(), 3); + assert!(accounts + .iter() + .any(|a| a == &StoreAccountRef::root(account1.address.clone()))); + assert!(accounts + .iter() + .any(|a| a == &StoreAccountRef::vault(name2, account2.address.clone()))); + assert!(accounts + .iter() + .any(|a| a == &StoreAccountRef::vault(name2, account3.address.clone()))); + + // and then + assert_eq!( + store + .meta(&StoreAccountRef::root(account1.address)) + .unwrap(), + r#"{}"# + ); + assert_eq!( + store + .meta(&StoreAccountRef::vault("vault2", account2.address)) + .unwrap(), + r#"{"vault":"vault2"}"# + ); + assert_eq!( + store + .meta(&StoreAccountRef::vault("vault2", account3.address)) + .unwrap(), + r#"{"vault":"vault2"}"# + ); + } + + #[test] + fn should_not_remove_account_when_moving_to_self() { + // given + let mut dir = RootDiskDirectoryGuard::new(); + let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap(); + let password1 = "password1".into(); + let keypair1 = keypair(); + + // when + let account1 = store + .insert_account(SecretVaultRef::Root, keypair1.secret().clone(), &password1) + .unwrap(); + store + .change_account_vault(SecretVaultRef::Root, account1) + .unwrap(); + + // then + let accounts = store.accounts().unwrap(); + assert_eq!(accounts.len(), 1); + } + + #[test] + fn should_remove_account_from_vault() { + // given + let mut dir = RootDiskDirectoryGuard::new(); + let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap(); + let name1 = "vault1"; + let password1 = "password1".into(); + let keypair1 = keypair(); + + // when + store.create_vault(name1, &password1).unwrap(); + let account1 = store + .insert_account( + SecretVaultRef::Vault(name1.to_owned()), + keypair1.secret().clone(), + &password1, + ) + .unwrap(); + assert_eq!(store.accounts().unwrap().len(), 1); + + // then + store.remove_account(&account1, &password1).unwrap(); + assert_eq!(store.accounts().unwrap().len(), 0); + } + + #[test] + fn should_not_remove_account_from_vault_when_password_is_incorrect() { + // given + let mut dir = RootDiskDirectoryGuard::new(); + let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap(); + let name1 = "vault1"; + let password1 = "password1".into(); + let password2 = "password2".into(); + let keypair1 = keypair(); + + // when + store.create_vault(name1, &password1).unwrap(); + let account1 = store + .insert_account( + SecretVaultRef::Vault(name1.to_owned()), + keypair1.secret().clone(), + &password1, + ) + .unwrap(); + assert_eq!(store.accounts().unwrap().len(), 1); + + // then + store.remove_account(&account1, &password2).unwrap_err(); + assert_eq!(store.accounts().unwrap().len(), 1); + } + + #[test] + fn should_change_vault_password() { + // given + let mut dir = RootDiskDirectoryGuard::new(); + let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap(); + let name = "vault"; + let password = "password".into(); + let keypair = keypair(); + + // when + store.create_vault(name, &password).unwrap(); + store + .insert_account( + SecretVaultRef::Vault(name.to_owned()), + keypair.secret().clone(), + &password, + ) + .unwrap(); + + // then + assert_eq!(store.accounts().unwrap().len(), 1); + let new_password = "new_password".into(); + store.change_vault_password(name, &new_password).unwrap(); + assert_eq!(store.accounts().unwrap().len(), 1); + + // and when + store.close_vault(name).unwrap(); + + // then + store.open_vault(name, &new_password).unwrap(); + assert_eq!(store.accounts().unwrap().len(), 1); + } + + #[test] + fn should_have_different_passwords_for_vault_secret_and_meta() { + // given + let mut dir = RootDiskDirectoryGuard::new(); + let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap(); + let name = "vault"; + let password = "password".into(); + let secret_password = "sec_password".into(); + let keypair = keypair(); + + // when + store.create_vault(name, &password).unwrap(); + let account_ref = store + .insert_account( + SecretVaultRef::Vault(name.to_owned()), + keypair.secret().clone(), + &secret_password, + ) + .unwrap(); + + // then + assert_eq!(store.accounts().unwrap().len(), 1); + let new_secret_password = "new_sec_password".into(); + store + .change_password(&account_ref, &secret_password, &new_secret_password) + .unwrap(); + assert_eq!(store.accounts().unwrap().len(), 1); + } + + #[test] + fn should_list_opened_vaults() { + // given + let mut dir = RootDiskDirectoryGuard::new(); + let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap(); + let name1 = "vault1"; + let password1 = "password1".into(); + let name2 = "vault2"; + let password2 = "password2".into(); + let name3 = "vault3"; + let password3 = "password3".into(); + + // when + store.create_vault(name1, &password1).unwrap(); + store.create_vault(name2, &password2).unwrap(); + store.create_vault(name3, &password3).unwrap(); + store.close_vault(name2).unwrap(); + + // then + let opened_vaults = store.list_opened_vaults().unwrap(); + assert_eq!(opened_vaults.len(), 2); + assert!(opened_vaults.iter().any(|v| &*v == name1)); + assert!(opened_vaults.iter().any(|v| &*v == name3)); + } + + #[test] + fn should_manage_vaults_meta() { + // given + let mut dir = RootDiskDirectoryGuard::new(); + let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap(); + let name1 = "vault1"; + let password1 = "password1".into(); + + // when + store.create_vault(name1, &password1).unwrap(); + + // then + assert_eq!(store.get_vault_meta(name1).unwrap(), "{}".to_owned()); + assert!(store.set_vault_meta(name1, "Hello, world!!!").is_ok()); + assert_eq!( + store.get_vault_meta(name1).unwrap(), + "Hello, world!!!".to_owned() + ); + + // and when + store.close_vault(name1).unwrap(); + store.open_vault(name1, &password1).unwrap(); + + // then + assert_eq!( + store.get_vault_meta(name1).unwrap(), + "Hello, world!!!".to_owned() + ); + + // and when + store.close_vault(name1).unwrap(); + + // then + assert_eq!( + store.get_vault_meta(name1).unwrap(), + "Hello, world!!!".to_owned() + ); + assert!(store.get_vault_meta("vault2").is_err()); + } + + #[test] + fn should_store_derived_keys() { + // given we have one account in the store + let store = store(); + let keypair = keypair(); + let address = store + .insert_account( + SecretVaultRef::Root, + keypair.secret().clone(), + &"test".into(), + ) + .unwrap(); + + // when we deriving from that account + let derived = store + .insert_derived( + SecretVaultRef::Root, + &address, + &"test".into(), + Derivation::HardHash(H256::from(0)), + ) + .unwrap(); + + // there should be 2 accounts in the store + let accounts = store.accounts().unwrap(); + assert_eq!(accounts.len(), 2); + + // and we can sign with the derived contract + assert!( + store + .sign(&derived, &"test".into(), &Default::default()) + .is_ok(), + "Second password should work for second store." + ); + } + + #[test] + fn should_save_meta_when_setting_before_password() { + // given + let mut dir = RootDiskDirectoryGuard::new(); + let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap(); + let name = "vault"; + let password = "password1".into(); + let new_password = "password2".into(); + + // when + store.create_vault(name, &password).unwrap(); + store.set_vault_meta(name, "OldMeta").unwrap(); + store.change_vault_password(name, &new_password).unwrap(); + + // then + assert_eq!(store.get_vault_meta(name).unwrap(), "OldMeta".to_owned()); + } + + #[test] + fn should_export_account() { + // given + let store = store(); + let keypair = keypair(); + let address = store + .insert_account( + SecretVaultRef::Root, + keypair.secret().clone(), + &"test".into(), + ) + .unwrap(); + + // when + let exported = store.export_account(&address, &"test".into()); + + // then + assert!( + exported.is_ok(), + "Should export single account: {:?}", + exported + ); + } } diff --git a/accounts/ethstore/src/import.rs b/accounts/ethstore/src/import.rs index 87e9783eae7..949d37db7b3 100644 --- a/accounts/ethstore/src/import.rs +++ b/accounts/ethstore/src/import.rs @@ -1,80 +1,67 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::collections::HashSet; -use std::path::Path; -use std::fs; +use std::{collections::HashSet, fs, path::Path}; +use accounts_dir::{DiskKeyFileManager, KeyDirectory, KeyFileManager}; use ethkey::Address; -use accounts_dir::{KeyDirectory, RootDiskDirectory, DiskKeyFileManager, KeyFileManager}; -use dir; use Error; /// Import an account from a file. -pub fn import_account(path: &Path, dst: &KeyDirectory) -> Result { - let key_manager = DiskKeyFileManager::default(); - let existing_accounts = dst.load()?.into_iter().map(|a| a.address).collect::>(); - let filename = path.file_name().and_then(|n| n.to_str()).map(|f| f.to_owned()); - let account = fs::File::open(&path) - .map_err(Into::into) - .and_then(|file| key_manager.read(filename, file))?; +pub fn import_account(path: &Path, dst: &dyn KeyDirectory) -> Result { + let key_manager = DiskKeyFileManager::default(); + let existing_accounts = dst + .load()? + .into_iter() + .map(|a| a.address) + .collect::>(); + let filename = path + .file_name() + .and_then(|n| n.to_str()) + .map(|f| f.to_owned()); + let account = fs::File::open(&path) + .map_err(Into::into) + .and_then(|file| key_manager.read(filename, file))?; - let address = account.address.clone(); - if !existing_accounts.contains(&address) { - dst.insert(account)?; - } - Ok(address) + let address = account.address.clone(); + if !existing_accounts.contains(&address) { + dst.insert(account)?; + } + Ok(address) } /// Import all accounts from one directory to the other. -pub fn import_accounts(src: &KeyDirectory, dst: &KeyDirectory) -> Result, Error> { - let accounts = src.load()?; - let existing_accounts = dst.load()?.into_iter() - .map(|a| a.address) - .collect::>(); +pub fn import_accounts( + src: &dyn KeyDirectory, + dst: &dyn KeyDirectory, +) -> Result, Error> { + let accounts = src.load()?; + let existing_accounts = dst + .load()? + .into_iter() + .map(|a| a.address) + .collect::>(); - accounts.into_iter() - .filter(|a| !existing_accounts.contains(&a.address)) - .map(|a| { - let address = a.address.clone(); - dst.insert(a)?; - Ok(address) - }).collect() -} - -/// Provide a `HashSet` of all accounts available for import from the Geth keystore. -pub fn read_geth_accounts(testnet: bool) -> Vec
{ - RootDiskDirectory::at(dir::geth(testnet)) - .load() - .map(|d| d.into_iter().map(|a| a.address).collect()) - .unwrap_or_else(|_| Vec::new()) -} - -/// Import specific `desired` accounts from the Geth keystore into `dst`. -pub fn import_geth_accounts(dst: &KeyDirectory, desired: HashSet
, testnet: bool) -> Result, Error> { - let src = RootDiskDirectory::at(dir::geth(testnet)); - let accounts = src.load()?; - let existing_accounts = dst.load()?.into_iter().map(|a| a.address).collect::>(); - - accounts.into_iter() - .filter(|a| !existing_accounts.contains(&a.address)) - .filter(|a| desired.contains(&a.address)) - .map(|a| { - let address = a.address.clone(); - dst.insert(a)?; - Ok(address) - }).collect() + accounts + .into_iter() + .filter(|a| !existing_accounts.contains(&a.address)) + .map(|a| { + let address = a.address.clone(); + dst.insert(a)?; + Ok(address) + }) + .collect() } diff --git a/accounts/ethstore/src/json/bytes.rs b/accounts/ethstore/src/json/bytes.rs index 71391d8d185..b1cdcf04feb 100644 --- a/accounts/ethstore/src/json/bytes.rs +++ b/accounts/ethstore/src/json/bytes.rs @@ -1,74 +1,82 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . +use rustc_hex::{FromHex, FromHexError, ToHex}; +use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; use std::{ops, str}; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use serde::de::Error; -use rustc_hex::{ToHex, FromHex, FromHexError}; #[derive(Debug, PartialEq)] pub struct Bytes(Vec); impl ops::Deref for Bytes { - type Target = [u8]; + type Target = [u8]; - fn deref(&self) -> &Self::Target { - &self.0 - } + fn deref(&self) -> &Self::Target { + &self.0 + } } impl<'a> Deserialize<'a> for Bytes { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> - { - let s = String::deserialize(deserializer)?; - let data = s.from_hex().map_err(|e| Error::custom(format!("Invalid hex value {}", e)))?; - Ok(Bytes(data)) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + let s = String::deserialize(deserializer)?; + let data = s + .from_hex() + .map_err(|e| Error::custom(format!("Invalid hex value {}", e)))?; + Ok(Bytes(data)) + } } impl Serialize for Bytes { - fn serialize(&self, serializer: S) -> Result - where S: Serializer { - serializer.serialize_str(&self.0.to_hex()) - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.0.to_hex()) + } } impl str::FromStr for Bytes { - type Err = FromHexError; + type Err = FromHexError; - fn from_str(s: &str) -> Result { - s.from_hex().map(Bytes) - } + fn from_str(s: &str) -> Result { + s.from_hex().map(Bytes) + } } impl From<&'static str> for Bytes { - fn from(s: &'static str) -> Self { - s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!(Self), s)) - } + fn from(s: &'static str) -> Self { + s.parse().expect(&format!( + "invalid string literal for {}: '{}'", + stringify!(Self), + s + )) + } } impl From> for Bytes { - fn from(v: Vec) -> Self { - Bytes(v) - } + fn from(v: Vec) -> Self { + Bytes(v) + } } impl From for Vec { - fn from(b: Bytes) -> Self { - b.0 - } + fn from(b: Bytes) -> Self { + b.0 + } } diff --git a/accounts/ethstore/src/json/cipher.rs b/accounts/ethstore/src/json/cipher.rs index 38d897b64b0..e2848c86e93 100644 --- a/accounts/ethstore/src/json/cipher.rs +++ b/accounts/ethstore/src/json/cipher.rs @@ -1,96 +1,112 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::fmt; -use serde::{Serialize, Serializer, Deserialize, Deserializer}; -use serde::de::{Visitor, Error as SerdeError}; use super::{Error, H128}; +use serde::{ + de::{Error as SerdeError, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; +use std::fmt; #[derive(Debug, PartialEq)] pub enum CipherSer { - Aes128Ctr, + Aes128Ctr, } impl Serialize for CipherSer { - fn serialize(&self, serializer: S) -> Result - where S: Serializer { - match *self { - CipherSer::Aes128Ctr => serializer.serialize_str("aes-128-ctr"), - } - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + CipherSer::Aes128Ctr => serializer.serialize_str("aes-128-ctr"), + } + } } impl<'a> Deserialize<'a> for CipherSer { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> { - deserializer.deserialize_any(CipherSerVisitor) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + deserializer.deserialize_any(CipherSerVisitor) + } } struct CipherSerVisitor; impl<'a> Visitor<'a> for CipherSerVisitor { - type Value = CipherSer; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a valid cipher identifier") - } - - fn visit_str(self, value: &str) -> Result where E: SerdeError { - match value { - "aes-128-ctr" => Ok(CipherSer::Aes128Ctr), - _ => Err(SerdeError::custom(Error::UnsupportedCipher)) - } - } - - fn visit_string(self, value: String) -> Result where E: SerdeError { - self.visit_str(value.as_ref()) - } + type Value = CipherSer; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a valid cipher identifier") + } + + fn visit_str(self, value: &str) -> Result + where + E: SerdeError, + { + match value { + "aes-128-ctr" => Ok(CipherSer::Aes128Ctr), + _ => Err(SerdeError::custom(Error::UnsupportedCipher)), + } + } + + fn visit_string(self, value: String) -> Result + where + E: SerdeError, + { + self.visit_str(value.as_ref()) + } } #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct Aes128Ctr { - pub iv: H128, + pub iv: H128, } #[derive(Debug, PartialEq)] pub enum CipherSerParams { - Aes128Ctr(Aes128Ctr), + Aes128Ctr(Aes128Ctr), } impl Serialize for CipherSerParams { - fn serialize(&self, serializer: S) -> Result - where S: Serializer { - match *self { - CipherSerParams::Aes128Ctr(ref params) => params.serialize(serializer), - } - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + CipherSerParams::Aes128Ctr(ref params) => params.serialize(serializer), + } + } } impl<'a> Deserialize<'a> for CipherSerParams { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> { - Aes128Ctr::deserialize(deserializer) - .map(CipherSerParams::Aes128Ctr) - .map_err(|_| Error::InvalidCipherParams) - .map_err(SerdeError::custom) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + Aes128Ctr::deserialize(deserializer) + .map(CipherSerParams::Aes128Ctr) + .map_err(|_| Error::InvalidCipherParams) + .map_err(SerdeError::custom) + } } #[derive(Debug, PartialEq)] pub enum Cipher { - Aes128Ctr(Aes128Ctr), + Aes128Ctr(Aes128Ctr), } diff --git a/accounts/ethstore/src/json/crypto.rs b/accounts/ethstore/src/json/crypto.rs index 34664f98b0e..2afe5bbd156 100644 --- a/accounts/ethstore/src/json/crypto.rs +++ b/accounts/ethstore/src/json/crypto.rs @@ -1,194 +1,218 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::{fmt, str}; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use serde::ser::SerializeStruct; -use serde::de::{Visitor, MapAccess, Error}; +// along with OpenEthereum. If not, see . + +use super::{Bytes, Cipher, CipherSer, CipherSerParams, Kdf, KdfSer, KdfSerParams, H256}; +use serde::{ + de::{Error, MapAccess, Visitor}, + ser::SerializeStruct, + Deserialize, Deserializer, Serialize, Serializer, +}; use serde_json; -use super::{Cipher, CipherSer, CipherSerParams, Kdf, KdfSer, KdfSerParams, H256, Bytes}; +use std::{fmt, str}; pub type CipherText = Bytes; #[derive(Debug, PartialEq)] pub struct Crypto { - pub cipher: Cipher, - pub ciphertext: CipherText, - pub kdf: Kdf, - pub mac: H256, + pub cipher: Cipher, + pub ciphertext: CipherText, + pub kdf: Kdf, + pub mac: H256, } impl str::FromStr for Crypto { - type Err = serde_json::error::Error; + type Err = serde_json::error::Error; - fn from_str(s: &str) -> Result { - serde_json::from_str(s) - } + fn from_str(s: &str) -> Result { + serde_json::from_str(s) + } } impl From for String { - fn from(c: Crypto) -> Self { - serde_json::to_string(&c).expect("serialization cannot fail, cause all crypto keys are strings") - } + fn from(c: Crypto) -> Self { + serde_json::to_string(&c) + .expect("serialization cannot fail, cause all crypto keys are strings") + } } enum CryptoField { - Cipher, - CipherParams, - CipherText, - Kdf, - KdfParams, - Mac, - Version, + Cipher, + CipherParams, + CipherText, + Kdf, + KdfParams, + Mac, + Version, } impl<'a> Deserialize<'a> for CryptoField { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> - { - deserializer.deserialize_any(CryptoFieldVisitor) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + deserializer.deserialize_any(CryptoFieldVisitor) + } } struct CryptoFieldVisitor; impl<'a> Visitor<'a> for CryptoFieldVisitor { - type Value = CryptoField; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a valid crypto struct description") - } - - fn visit_str(self, value: &str) -> Result - where E: Error - { - match value { - "cipher" => Ok(CryptoField::Cipher), - "cipherparams" => Ok(CryptoField::CipherParams), - "ciphertext" => Ok(CryptoField::CipherText), - "kdf" => Ok(CryptoField::Kdf), - "kdfparams" => Ok(CryptoField::KdfParams), - "mac" => Ok(CryptoField::Mac), - "version" => Ok(CryptoField::Version), - _ => Err(Error::custom(format!("Unknown field: '{}'", value))), - } - } + type Value = CryptoField; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a valid crypto struct description") + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + match value { + "cipher" => Ok(CryptoField::Cipher), + "cipherparams" => Ok(CryptoField::CipherParams), + "ciphertext" => Ok(CryptoField::CipherText), + "kdf" => Ok(CryptoField::Kdf), + "kdfparams" => Ok(CryptoField::KdfParams), + "mac" => Ok(CryptoField::Mac), + "version" => Ok(CryptoField::Version), + _ => Err(Error::custom(format!("Unknown field: '{}'", value))), + } + } } impl<'a> Deserialize<'a> for Crypto { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> - { - static FIELDS: &'static [&'static str] = &["id", "version", "crypto", "Crypto", "address"]; - deserializer.deserialize_struct("Crypto", FIELDS, CryptoVisitor) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + static FIELDS: &'static [&'static str] = &["id", "version", "crypto", "Crypto", "address"]; + deserializer.deserialize_struct("Crypto", FIELDS, CryptoVisitor) + } } struct CryptoVisitor; impl<'a> Visitor<'a> for CryptoVisitor { - type Value = Crypto; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a valid vault crypto object") - } - - fn visit_map(self, mut visitor: V) -> Result - where V: MapAccess<'a> - { - let mut cipher = None; - let mut cipherparams = None; - let mut ciphertext = None; - let mut kdf = None; - let mut kdfparams = None; - let mut mac = None; - - loop { - match visitor.next_key()? { - Some(CryptoField::Cipher) => { cipher = Some(visitor.next_value()?); } - Some(CryptoField::CipherParams) => { cipherparams = Some(visitor.next_value()?); } - Some(CryptoField::CipherText) => { ciphertext = Some(visitor.next_value()?); } - Some(CryptoField::Kdf) => { kdf = Some(visitor.next_value()?); } - Some(CryptoField::KdfParams) => { kdfparams = Some(visitor.next_value()?); } - Some(CryptoField::Mac) => { mac = Some(visitor.next_value()?); } - // skip not required version field (it appears in pyethereum generated keystores) - Some(CryptoField::Version) => { visitor.next_value().unwrap_or(()) } - None => { break; } - } - } - - let cipher = match (cipher, cipherparams) { - (Some(CipherSer::Aes128Ctr), Some(CipherSerParams::Aes128Ctr(params))) => Cipher::Aes128Ctr(params), - (None, _) => return Err(V::Error::missing_field("cipher")), - (Some(_), None) => return Err(V::Error::missing_field("cipherparams")), - }; - - let ciphertext = match ciphertext { - Some(ciphertext) => ciphertext, - None => return Err(V::Error::missing_field("ciphertext")), - }; - - let kdf = match (kdf, kdfparams) { - (Some(KdfSer::Pbkdf2), Some(KdfSerParams::Pbkdf2(params))) => Kdf::Pbkdf2(params), - (Some(KdfSer::Scrypt), Some(KdfSerParams::Scrypt(params))) => Kdf::Scrypt(params), - (Some(_), Some(_)) => return Err(V::Error::custom("Invalid cipherparams")), - (None, _) => return Err(V::Error::missing_field("kdf")), - (Some(_), None) => return Err(V::Error::missing_field("kdfparams")), - }; - - let mac = match mac { - Some(mac) => mac, - None => return Err(V::Error::missing_field("mac")), - }; - - let result = Crypto { - cipher: cipher, - ciphertext: ciphertext, - kdf: kdf, - mac: mac, - }; - - Ok(result) - } + type Value = Crypto; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a valid vault crypto object") + } + + fn visit_map(self, mut visitor: V) -> Result + where + V: MapAccess<'a>, + { + let mut cipher = None; + let mut cipherparams = None; + let mut ciphertext = None; + let mut kdf = None; + let mut kdfparams = None; + let mut mac = None; + + loop { + match visitor.next_key()? { + Some(CryptoField::Cipher) => { + cipher = Some(visitor.next_value()?); + } + Some(CryptoField::CipherParams) => { + cipherparams = Some(visitor.next_value()?); + } + Some(CryptoField::CipherText) => { + ciphertext = Some(visitor.next_value()?); + } + Some(CryptoField::Kdf) => { + kdf = Some(visitor.next_value()?); + } + Some(CryptoField::KdfParams) => { + kdfparams = Some(visitor.next_value()?); + } + Some(CryptoField::Mac) => { + mac = Some(visitor.next_value()?); + } + // skip not required version field (it appears in pyethereum generated keystores) + Some(CryptoField::Version) => visitor.next_value().unwrap_or(()), + None => { + break; + } + } + } + + let cipher = match (cipher, cipherparams) { + (Some(CipherSer::Aes128Ctr), Some(CipherSerParams::Aes128Ctr(params))) => { + Cipher::Aes128Ctr(params) + } + (None, _) => return Err(V::Error::missing_field("cipher")), + (Some(_), None) => return Err(V::Error::missing_field("cipherparams")), + }; + + let ciphertext = match ciphertext { + Some(ciphertext) => ciphertext, + None => return Err(V::Error::missing_field("ciphertext")), + }; + + let kdf = match (kdf, kdfparams) { + (Some(KdfSer::Pbkdf2), Some(KdfSerParams::Pbkdf2(params))) => Kdf::Pbkdf2(params), + (Some(KdfSer::Scrypt), Some(KdfSerParams::Scrypt(params))) => Kdf::Scrypt(params), + (Some(_), Some(_)) => return Err(V::Error::custom("Invalid cipherparams")), + (None, _) => return Err(V::Error::missing_field("kdf")), + (Some(_), None) => return Err(V::Error::missing_field("kdfparams")), + }; + + let mac = match mac { + Some(mac) => mac, + None => return Err(V::Error::missing_field("mac")), + }; + + let result = Crypto { + cipher: cipher, + ciphertext: ciphertext, + kdf: kdf, + mac: mac, + }; + + Ok(result) + } } impl Serialize for Crypto { - fn serialize(&self, serializer: S) -> Result - where S: Serializer - { - let mut crypto = serializer.serialize_struct("Crypto", 6)?; - match self.cipher { - Cipher::Aes128Ctr(ref params) => { - crypto.serialize_field("cipher", &CipherSer::Aes128Ctr)?; - crypto.serialize_field("cipherparams", params)?; - }, - } - crypto.serialize_field("ciphertext", &self.ciphertext)?; - match self.kdf { - Kdf::Pbkdf2(ref params) => { - crypto.serialize_field("kdf", &KdfSer::Pbkdf2)?; - crypto.serialize_field("kdfparams", params)?; - }, - Kdf::Scrypt(ref params) => { - crypto.serialize_field("kdf", &KdfSer::Scrypt)?; - crypto.serialize_field("kdfparams", params)?; - }, - } - - crypto.serialize_field("mac", &self.mac)?; - crypto.end() - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut crypto = serializer.serialize_struct("Crypto", 6)?; + match self.cipher { + Cipher::Aes128Ctr(ref params) => { + crypto.serialize_field("cipher", &CipherSer::Aes128Ctr)?; + crypto.serialize_field("cipherparams", params)?; + } + } + crypto.serialize_field("ciphertext", &self.ciphertext)?; + match self.kdf { + Kdf::Pbkdf2(ref params) => { + crypto.serialize_field("kdf", &KdfSer::Pbkdf2)?; + crypto.serialize_field("kdfparams", params)?; + } + Kdf::Scrypt(ref params) => { + crypto.serialize_field("kdf", &KdfSer::Scrypt)?; + crypto.serialize_field("kdfparams", params)?; + } + } + + crypto.serialize_field("mac", &self.mac)?; + crypto.end() + } } diff --git a/accounts/ethstore/src/json/error.rs b/accounts/ethstore/src/json/error.rs index e02ecb9633a..6a4d8021bcd 100644 --- a/accounts/ethstore/src/json/error.rs +++ b/accounts/ethstore/src/json/error.rs @@ -1,50 +1,50 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use std::fmt; #[derive(Debug, PartialEq)] pub enum Error { - UnsupportedCipher, - InvalidCipherParams, - UnsupportedKdf, - InvalidUuid, - UnsupportedVersion, - InvalidCiphertext, - InvalidH256, - InvalidPrf, + UnsupportedCipher, + InvalidCipherParams, + UnsupportedKdf, + InvalidUuid, + UnsupportedVersion, + InvalidCiphertext, + InvalidH256, + InvalidPrf, } impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match *self { - Error::InvalidUuid => write!(f, "Invalid Uuid"), - Error::UnsupportedVersion => write!(f, "Unsupported version"), - Error::UnsupportedKdf => write!(f, "Unsupported kdf"), - Error::InvalidCiphertext => write!(f, "Invalid ciphertext"), - Error::UnsupportedCipher => write!(f, "Unsupported cipher"), - Error::InvalidCipherParams => write!(f, "Invalid cipher params"), - Error::InvalidH256 => write!(f, "Invalid hash"), - Error::InvalidPrf => write!(f, "Invalid prf"), - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + Error::InvalidUuid => write!(f, "Invalid Uuid"), + Error::UnsupportedVersion => write!(f, "Unsupported version"), + Error::UnsupportedKdf => write!(f, "Unsupported kdf"), + Error::InvalidCiphertext => write!(f, "Invalid ciphertext"), + Error::UnsupportedCipher => write!(f, "Unsupported cipher"), + Error::InvalidCipherParams => write!(f, "Invalid cipher params"), + Error::InvalidH256 => write!(f, "Invalid hash"), + Error::InvalidPrf => write!(f, "Invalid prf"), + } + } } impl Into for Error { - fn into(self) -> String { - format!("{}", self) - } + fn into(self) -> String { + format!("{}", self) + } } diff --git a/accounts/ethstore/src/json/hash.rs b/accounts/ethstore/src/json/hash.rs index 6678abb73a4..1a5b22b2ea7 100644 --- a/accounts/ethstore/src/json/hash.rs +++ b/accounts/ethstore/src/json/hash.rs @@ -1,117 +1,133 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::{ops, fmt, str}; -use rustc_hex::{FromHex, ToHex}; -use serde::{Serialize, Serializer, Deserialize, Deserializer}; -use serde::de::{Visitor, Error as SerdeError}; use super::Error; +use rustc_hex::{FromHex, ToHex}; +use serde::{ + de::{Error as SerdeError, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; +use std::{fmt, ops, str}; macro_rules! impl_hash { - ($name: ident, $size: expr) => { - pub struct $name([u8; $size]); - - impl fmt::Debug for $name { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - let self_ref: &[u8] = &self.0; - write!(f, "{:?}", self_ref) - } - } - - impl PartialEq for $name { - fn eq(&self, other: &Self) -> bool { - let self_ref: &[u8] = &self.0; - let other_ref: &[u8] = &other.0; - self_ref == other_ref - } - } - - impl ops::Deref for $name { - type Target = [u8]; - - fn deref(&self) -> &Self::Target { - &self.0 - } - } - - impl Serialize for $name { - fn serialize(&self, serializer: S) -> Result - where S: Serializer { - serializer.serialize_str(&self.0.to_hex()) - } - } - - impl<'a> Deserialize<'a> for $name { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> { - struct HashVisitor; - - impl<'b> Visitor<'b> for HashVisitor { - type Value = $name; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a hex-encoded {}", stringify!($name)) - } - - fn visit_str(self, value: &str) -> Result where E: SerdeError { - value.parse().map_err(SerdeError::custom) - } - - fn visit_string(self, value: String) -> Result where E: SerdeError { - self.visit_str(value.as_ref()) - } - } - - deserializer.deserialize_any(HashVisitor) - } - } - - impl str::FromStr for $name { - type Err = Error; - - fn from_str(value: &str) -> Result { - match value.from_hex() { - Ok(ref hex) if hex.len() == $size => { - let mut hash = [0u8; $size]; - hash.clone_from_slice(hex); - Ok($name(hash)) - } - _ => Err(Error::InvalidH256), - } - } - } - - impl From<&'static str> for $name { - fn from(s: &'static str) -> Self { - s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!($name), s)) - } - } - - impl From<[u8; $size]> for $name { - fn from(bytes: [u8; $size]) -> Self { - $name(bytes) - } - } - - impl Into<[u8; $size]> for $name { - fn into(self) -> [u8; $size] { - self.0 - } - } - } + ($name: ident, $size: expr) => { + pub struct $name([u8; $size]); + + impl fmt::Debug for $name { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + let self_ref: &[u8] = &self.0; + write!(f, "{:?}", self_ref) + } + } + + impl PartialEq for $name { + fn eq(&self, other: &Self) -> bool { + let self_ref: &[u8] = &self.0; + let other_ref: &[u8] = &other.0; + self_ref == other_ref + } + } + + impl ops::Deref for $name { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl Serialize for $name { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.0.to_hex()) + } + } + + impl<'a> Deserialize<'a> for $name { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + struct HashVisitor; + + impl<'b> Visitor<'b> for HashVisitor { + type Value = $name; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a hex-encoded {}", stringify!($name)) + } + + fn visit_str(self, value: &str) -> Result + where + E: SerdeError, + { + value.parse().map_err(SerdeError::custom) + } + + fn visit_string(self, value: String) -> Result + where + E: SerdeError, + { + self.visit_str(value.as_ref()) + } + } + + deserializer.deserialize_any(HashVisitor) + } + } + + impl str::FromStr for $name { + type Err = Error; + + fn from_str(value: &str) -> Result { + match value.from_hex() { + Ok(ref hex) if hex.len() == $size => { + let mut hash = [0u8; $size]; + hash.clone_from_slice(hex); + Ok($name(hash)) + } + _ => Err(Error::InvalidH256), + } + } + } + + impl From<&'static str> for $name { + fn from(s: &'static str) -> Self { + s.parse().expect(&format!( + "invalid string literal for {}: '{}'", + stringify!($name), + s + )) + } + } + + impl From<[u8; $size]> for $name { + fn from(bytes: [u8; $size]) -> Self { + $name(bytes) + } + } + + impl Into<[u8; $size]> for $name { + fn into(self) -> [u8; $size] { + self.0 + } + } + }; } impl_hash!(H128, 16); diff --git a/accounts/ethstore/src/json/id.rs b/accounts/ethstore/src/json/id.rs index 27550428fa1..bbf1282a6a9 100644 --- a/accounts/ethstore/src/json/id.rs +++ b/accounts/ethstore/src/json/id.rs @@ -1,153 +1,179 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Universaly unique identifier. -use std::{fmt, str}; -use rustc_hex::{ToHex, FromHex}; -use serde::{Deserialize, Serialize, Deserializer, Serializer}; -use serde::de::{Visitor, Error as SerdeError}; use super::Error; +use rustc_hex::{FromHex, ToHex}; +use serde::{ + de::{Error as SerdeError, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; +use std::{fmt, str}; /// Universaly unique identifier. #[derive(Debug, PartialEq)] pub struct Uuid([u8; 16]); impl From<[u8; 16]> for Uuid { - fn from(uuid: [u8; 16]) -> Self { - Uuid(uuid) - } + fn from(uuid: [u8; 16]) -> Self { + Uuid(uuid) + } } impl<'a> Into for &'a Uuid { - fn into(self) -> String { - let d1 = &self.0[0..4]; - let d2 = &self.0[4..6]; - let d3 = &self.0[6..8]; - let d4 = &self.0[8..10]; - let d5 = &self.0[10..16]; - [d1, d2, d3, d4, d5].into_iter().map(|d| d.to_hex()).collect::>().join("-") - } + fn into(self) -> String { + let d1 = &self.0[0..4]; + let d2 = &self.0[4..6]; + let d3 = &self.0[6..8]; + let d4 = &self.0[8..10]; + let d5 = &self.0[10..16]; + [d1, d2, d3, d4, d5] + .iter() + .map(|d| d.to_hex()) + .collect::>() + .join("-") + } } impl Into for Uuid { - fn into(self) -> String { - Into::into(&self) - } + fn into(self) -> String { + Into::into(&self) + } } impl Into<[u8; 16]> for Uuid { - fn into(self) -> [u8; 16] { - self.0 - } + fn into(self) -> [u8; 16] { + self.0 + } } impl fmt::Display for Uuid { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - let s: String = (self as &Uuid).into(); - write!(f, "{}", s) - } + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + let s: String = (self as &Uuid).into(); + write!(f, "{}", s) + } } fn copy_into(from: &str, into: &mut [u8]) -> Result<(), Error> { - let from = from.from_hex().map_err(|_| Error::InvalidUuid)?; + let from = from.from_hex().map_err(|_| Error::InvalidUuid)?; - if from.len() != into.len() { - return Err(Error::InvalidUuid); - } + if from.len() != into.len() { + return Err(Error::InvalidUuid); + } - into.copy_from_slice(&from); - Ok(()) + into.copy_from_slice(&from); + Ok(()) } impl str::FromStr for Uuid { - type Err = Error; + type Err = Error; - fn from_str(s: &str) -> Result { - let parts: Vec<&str> = s.split("-").collect(); + fn from_str(s: &str) -> Result { + let parts: Vec<&str> = s.split("-").collect(); - if parts.len() != 5 { - return Err(Error::InvalidUuid); - } + if parts.len() != 5 { + return Err(Error::InvalidUuid); + } - let mut uuid = [0u8; 16]; + let mut uuid = [0u8; 16]; - copy_into(parts[0], &mut uuid[0..4])?; - copy_into(parts[1], &mut uuid[4..6])?; - copy_into(parts[2], &mut uuid[6..8])?; - copy_into(parts[3], &mut uuid[8..10])?; - copy_into(parts[4], &mut uuid[10..16])?; + copy_into(parts[0], &mut uuid[0..4])?; + copy_into(parts[1], &mut uuid[4..6])?; + copy_into(parts[2], &mut uuid[6..8])?; + copy_into(parts[3], &mut uuid[8..10])?; + copy_into(parts[4], &mut uuid[10..16])?; - Ok(Uuid(uuid)) - } + Ok(Uuid(uuid)) + } } impl From<&'static str> for Uuid { - fn from(s: &'static str) -> Self { - s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!(Self), s)) - } + fn from(s: &'static str) -> Self { + s.parse().expect(&format!( + "invalid string literal for {}: '{}'", + stringify!(Self), + s + )) + } } impl Serialize for Uuid { - fn serialize(&self, serializer: S) -> Result - where S: Serializer { - let s: String = self.into(); - serializer.serialize_str(&s) - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let s: String = self.into(); + serializer.serialize_str(&s) + } } impl<'a> Deserialize<'a> for Uuid { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> { - deserializer.deserialize_any(UuidVisitor) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + deserializer.deserialize_any(UuidVisitor) + } } struct UuidVisitor; impl<'a> Visitor<'a> for UuidVisitor { - type Value = Uuid; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a valid hex-encoded UUID") - } - - fn visit_str(self, value: &str) -> Result where E: SerdeError { - value.parse().map_err(SerdeError::custom) - } - - fn visit_string(self, value: String) -> Result where E: SerdeError { - self.visit_str(value.as_ref()) - } + type Value = Uuid; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a valid hex-encoded UUID") + } + + fn visit_str(self, value: &str) -> Result + where + E: SerdeError, + { + value.parse().map_err(SerdeError::custom) + } + + fn visit_string(self, value: String) -> Result + where + E: SerdeError, + { + self.visit_str(value.as_ref()) + } } #[cfg(test)] mod tests { - use super::Uuid; - - #[test] - fn uuid_from_str() { - let uuid: Uuid = "3198bc9c-6672-5ab3-d995-4942343ae5b6".into(); - assert_eq!(uuid, Uuid::from([0x31, 0x98, 0xbc, 0x9c, 0x66, 0x72, 0x5a, 0xb3, 0xd9, 0x95, 0x49, 0x42, 0x34, 0x3a, 0xe5, 0xb6])); - } - - #[test] - fn uuid_from_and_to_str() { - let from = "3198bc9c-6672-5ab3-d995-4942343ae5b6"; - let uuid: Uuid = from.into(); - let to: String = uuid.into(); - assert_eq!(from, &to); - } + use super::Uuid; + + #[test] + fn uuid_from_str() { + let uuid: Uuid = "3198bc9c-6672-5ab3-d995-4942343ae5b6".into(); + assert_eq!( + uuid, + Uuid::from([ + 0x31, 0x98, 0xbc, 0x9c, 0x66, 0x72, 0x5a, 0xb3, 0xd9, 0x95, 0x49, 0x42, 0x34, 0x3a, + 0xe5, 0xb6 + ]) + ); + } + + #[test] + fn uuid_from_and_to_str() { + let from = "3198bc9c-6672-5ab3-d995-4942343ae5b6"; + let uuid: Uuid = from.into(); + let to: String = uuid.into(); + assert_eq!(from, &to); + } } diff --git a/accounts/ethstore/src/json/kdf.rs b/accounts/ethstore/src/json/kdf.rs index a8bb8b261ce..4955836f6d6 100644 --- a/accounts/ethstore/src/json/kdf.rs +++ b/accounts/ethstore/src/json/kdf.rs @@ -1,160 +1,186 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::fmt; -use std::num::NonZeroU32; -use serde::{Serialize, Serializer, Deserialize, Deserializer}; -use serde::de::{Visitor, Error as SerdeError}; -use super::{Error, Bytes}; +use super::{Bytes, Error}; +use serde::{ + de::{Error as SerdeError, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; +use std::{fmt, num::NonZeroU32}; #[derive(Debug, PartialEq)] pub enum KdfSer { - Pbkdf2, - Scrypt, + Pbkdf2, + Scrypt, } impl Serialize for KdfSer { - fn serialize(&self, serializer: S) -> Result - where S: Serializer { - match *self { - KdfSer::Pbkdf2 => serializer.serialize_str("pbkdf2"), - KdfSer::Scrypt => serializer.serialize_str("scrypt"), - } - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + KdfSer::Pbkdf2 => serializer.serialize_str("pbkdf2"), + KdfSer::Scrypt => serializer.serialize_str("scrypt"), + } + } } impl<'a> Deserialize<'a> for KdfSer { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> { - deserializer.deserialize_any(KdfSerVisitor) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + deserializer.deserialize_any(KdfSerVisitor) + } } struct KdfSerVisitor; impl<'a> Visitor<'a> for KdfSerVisitor { - type Value = KdfSer; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a kdf algorithm identifier") - } - - fn visit_str(self, value: &str) -> Result where E: SerdeError { - match value { - "pbkdf2" => Ok(KdfSer::Pbkdf2), - "scrypt" => Ok(KdfSer::Scrypt), - _ => Err(SerdeError::custom(Error::UnsupportedKdf)) - } - } - - fn visit_string(self, value: String) -> Result where E: SerdeError { - self.visit_str(value.as_ref()) - } + type Value = KdfSer; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a kdf algorithm identifier") + } + + fn visit_str(self, value: &str) -> Result + where + E: SerdeError, + { + match value { + "pbkdf2" => Ok(KdfSer::Pbkdf2), + "scrypt" => Ok(KdfSer::Scrypt), + _ => Err(SerdeError::custom(Error::UnsupportedKdf)), + } + } + + fn visit_string(self, value: String) -> Result + where + E: SerdeError, + { + self.visit_str(value.as_ref()) + } } #[derive(Debug, PartialEq)] pub enum Prf { - HmacSha256, + HmacSha256, } impl Serialize for Prf { - fn serialize(&self, serializer: S) -> Result - where S: Serializer { - match *self { - Prf::HmacSha256 => serializer.serialize_str("hmac-sha256"), - } - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + Prf::HmacSha256 => serializer.serialize_str("hmac-sha256"), + } + } } impl<'a> Deserialize<'a> for Prf { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> { - deserializer.deserialize_any(PrfVisitor) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + deserializer.deserialize_any(PrfVisitor) + } } struct PrfVisitor; impl<'a> Visitor<'a> for PrfVisitor { - type Value = Prf; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a prf algorithm identifier") - } - - fn visit_str(self, value: &str) -> Result where E: SerdeError { - match value { - "hmac-sha256" => Ok(Prf::HmacSha256), - _ => Err(SerdeError::custom(Error::InvalidPrf)), - } - } - - fn visit_string(self, value: String) -> Result where E: SerdeError { - self.visit_str(value.as_ref()) - } + type Value = Prf; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a prf algorithm identifier") + } + + fn visit_str(self, value: &str) -> Result + where + E: SerdeError, + { + match value { + "hmac-sha256" => Ok(Prf::HmacSha256), + _ => Err(SerdeError::custom(Error::InvalidPrf)), + } + } + + fn visit_string(self, value: String) -> Result + where + E: SerdeError, + { + self.visit_str(value.as_ref()) + } } #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct Pbkdf2 { - pub c: NonZeroU32, - pub dklen: u32, - pub prf: Prf, - pub salt: Bytes, + pub c: NonZeroU32, + pub dklen: u32, + pub prf: Prf, + pub salt: Bytes, } #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct Scrypt { - pub dklen: u32, - pub p: u32, - pub n: u32, - pub r: u32, - pub salt: Bytes, + pub dklen: u32, + pub p: u32, + pub n: u32, + pub r: u32, + pub salt: Bytes, } #[derive(Debug, PartialEq)] pub enum KdfSerParams { - Pbkdf2(Pbkdf2), - Scrypt(Scrypt), + Pbkdf2(Pbkdf2), + Scrypt(Scrypt), } impl Serialize for KdfSerParams { - fn serialize(&self, serializer: S) -> Result - where S: Serializer { - match *self { - KdfSerParams::Pbkdf2(ref params) => params.serialize(serializer), - KdfSerParams::Scrypt(ref params) => params.serialize(serializer), - } - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + KdfSerParams::Pbkdf2(ref params) => params.serialize(serializer), + KdfSerParams::Scrypt(ref params) => params.serialize(serializer), + } + } } impl<'a> Deserialize<'a> for KdfSerParams { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> { - use serde_json::{Value, from_value}; - - let v: Value = Deserialize::deserialize(deserializer)?; - - from_value(v.clone()).map(KdfSerParams::Pbkdf2) - .or_else(|_| from_value(v).map(KdfSerParams::Scrypt)) - .map_err(|_| D::Error::custom("Invalid KDF algorithm")) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + use serde_json::{from_value, Value}; + + let v: Value = Deserialize::deserialize(deserializer)?; + + from_value(v.clone()) + .map(KdfSerParams::Pbkdf2) + .or_else(|_| from_value(v).map(KdfSerParams::Scrypt)) + .map_err(|_| D::Error::custom("Invalid KDF algorithm")) + } } #[derive(Debug, PartialEq)] pub enum Kdf { - Pbkdf2(Pbkdf2), - Scrypt(Scrypt), + Pbkdf2(Pbkdf2), + Scrypt(Scrypt), } diff --git a/accounts/ethstore/src/json/key_file.rs b/accounts/ethstore/src/json/key_file.rs index 60c3ae859f3..480493a9cd7 100644 --- a/accounts/ethstore/src/json/key_file.rs +++ b/accounts/ethstore/src/json/key_file.rs @@ -1,195 +1,227 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::fmt; -use std::io::{Read, Write}; -use serde::{Serialize, Serializer, Deserialize, Deserializer}; -use serde::de::{Error, Visitor, MapAccess, DeserializeOwned}; +use super::{Crypto, Uuid, Version, H160}; +use serde::{ + de::{DeserializeOwned, Error, MapAccess, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; use serde_json; -use super::{Uuid, Version, Crypto, H160}; +use std::{ + fmt, + io::{Read, Write}, +}; /// Public opaque type representing serializable `KeyFile`. #[derive(Debug, PartialEq)] pub struct OpaqueKeyFile { - key_file: KeyFile + key_file: KeyFile, } impl Serialize for OpaqueKeyFile { - fn serialize(&self, serializer: S) -> Result where - S: Serializer, - { - self.key_file.serialize(serializer) - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.key_file.serialize(serializer) + } } -impl From for OpaqueKeyFile where T: Into { - fn from(val: T) -> Self { - OpaqueKeyFile { key_file: val.into() } - } +impl From for OpaqueKeyFile +where + T: Into, +{ + fn from(val: T) -> Self { + OpaqueKeyFile { + key_file: val.into(), + } + } } #[derive(Debug, PartialEq, Serialize)] pub struct KeyFile { - pub id: Uuid, - pub version: Version, - pub crypto: Crypto, - pub address: Option, - pub name: Option, - pub meta: Option, + pub id: Uuid, + pub version: Version, + pub crypto: Crypto, + pub address: Option, + pub name: Option, + pub meta: Option, } enum KeyFileField { - Id, - Version, - Crypto, - Address, - Name, - Meta, + Id, + Version, + Crypto, + Address, + Name, + Meta, } impl<'a> Deserialize<'a> for KeyFileField { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> - { - deserializer.deserialize_any(KeyFileFieldVisitor) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + deserializer.deserialize_any(KeyFileFieldVisitor) + } } struct KeyFileFieldVisitor; impl<'a> Visitor<'a> for KeyFileFieldVisitor { - type Value = KeyFileField; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a valid key file field") - } - - fn visit_str(self, value: &str) -> Result - where E: Error - { - match value { - "id" => Ok(KeyFileField::Id), - "version" => Ok(KeyFileField::Version), - "crypto" => Ok(KeyFileField::Crypto), - "Crypto" => Ok(KeyFileField::Crypto), - "address" => Ok(KeyFileField::Address), - "name" => Ok(KeyFileField::Name), - "meta" => Ok(KeyFileField::Meta), - _ => Err(Error::custom(format!("Unknown field: '{}'", value))), - } - } + type Value = KeyFileField; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a valid key file field") + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + match value { + "id" => Ok(KeyFileField::Id), + "version" => Ok(KeyFileField::Version), + "crypto" => Ok(KeyFileField::Crypto), + "Crypto" => Ok(KeyFileField::Crypto), + "address" => Ok(KeyFileField::Address), + "name" => Ok(KeyFileField::Name), + "meta" => Ok(KeyFileField::Meta), + _ => Err(Error::custom(format!("Unknown field: '{}'", value))), + } + } } impl<'a> Deserialize<'a> for KeyFile { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> - { - static FIELDS: &'static [&'static str] = &["id", "version", "crypto", "Crypto", "address"]; - deserializer.deserialize_struct("KeyFile", FIELDS, KeyFileVisitor) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + static FIELDS: &'static [&'static str] = &["id", "version", "crypto", "Crypto", "address"]; + deserializer.deserialize_struct("KeyFile", FIELDS, KeyFileVisitor) + } } -fn none_if_empty<'a, T>(v: Option) -> Option where - T: DeserializeOwned +fn none_if_empty<'a, T>(v: Option) -> Option +where + T: DeserializeOwned, { - v.and_then(|v| if v.is_null() { - None - } else { - serde_json::from_value(v).ok() - }) - + v.and_then(|v| { + if v.is_null() { + None + } else { + serde_json::from_value(v).ok() + } + }) } struct KeyFileVisitor; impl<'a> Visitor<'a> for KeyFileVisitor { - type Value = KeyFile; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a valid key object") - } - - fn visit_map(self, mut visitor: V) -> Result - where V: MapAccess<'a> - { - let mut id = None; - let mut version = None; - let mut crypto = None; - let mut address = None; - let mut name = None; - let mut meta = None; - - loop { - match visitor.next_key()? { - Some(KeyFileField::Id) => { id = Some(visitor.next_value()?); } - Some(KeyFileField::Version) => { version = Some(visitor.next_value()?); } - Some(KeyFileField::Crypto) => { crypto = Some(visitor.next_value()?); } - Some(KeyFileField::Address) => { address = Some(visitor.next_value()?); } - Some(KeyFileField::Name) => { name = none_if_empty(visitor.next_value().ok()) } - Some(KeyFileField::Meta) => { meta = none_if_empty(visitor.next_value().ok()) } - None => { break; } - } - } - - let id = match id { - Some(id) => id, - None => return Err(V::Error::missing_field("id")), - }; - - let version = match version { - Some(version) => version, - None => return Err(V::Error::missing_field("version")), - }; - - let crypto = match crypto { - Some(crypto) => crypto, - None => return Err(V::Error::missing_field("crypto")), - }; - - let result = KeyFile { - id: id, - version: version, - crypto: crypto, - address: address, - name: name, - meta: meta, - }; - - Ok(result) - } + type Value = KeyFile; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a valid key object") + } + + fn visit_map(self, mut visitor: V) -> Result + where + V: MapAccess<'a>, + { + let mut id = None; + let mut version = None; + let mut crypto = None; + let mut address = None; + let mut name = None; + let mut meta = None; + + loop { + match visitor.next_key()? { + Some(KeyFileField::Id) => { + id = Some(visitor.next_value()?); + } + Some(KeyFileField::Version) => { + version = Some(visitor.next_value()?); + } + Some(KeyFileField::Crypto) => { + crypto = Some(visitor.next_value()?); + } + Some(KeyFileField::Address) => { + address = Some(visitor.next_value()?); + } + Some(KeyFileField::Name) => name = none_if_empty(visitor.next_value().ok()), + Some(KeyFileField::Meta) => meta = none_if_empty(visitor.next_value().ok()), + None => { + break; + } + } + } + + let id = match id { + Some(id) => id, + None => return Err(V::Error::missing_field("id")), + }; + + let version = match version { + Some(version) => version, + None => return Err(V::Error::missing_field("version")), + }; + + let crypto = match crypto { + Some(crypto) => crypto, + None => return Err(V::Error::missing_field("crypto")), + }; + + let result = KeyFile { + id: id, + version: version, + crypto: crypto, + address: address, + name: name, + meta: meta, + }; + + Ok(result) + } } impl KeyFile { - pub fn load(reader: R) -> Result where R: Read { - serde_json::from_reader(reader) - } - - pub fn write(&self, writer: &mut W) -> Result<(), serde_json::Error> where W: Write { - serde_json::to_writer(writer, self) - } + pub fn load(reader: R) -> Result + where + R: Read, + { + serde_json::from_reader(reader) + } + + pub fn write(&self, writer: &mut W) -> Result<(), serde_json::Error> + where + W: Write, + { + serde_json::to_writer(writer, self) + } } #[cfg(test)] mod tests { - use std::str::FromStr; - use serde_json; - use json::{KeyFile, Uuid, Version, Crypto, Cipher, Aes128Ctr, Kdf, Scrypt}; + use json::{Aes128Ctr, Cipher, Crypto, Kdf, KeyFile, Scrypt, Uuid, Version}; + use serde_json; + use std::str::FromStr; - #[test] - fn basic_keyfile() { - let json = r#" + #[test] + fn basic_keyfile() { + let json = r#" { "address": "6edddfc6349aff20bc6467ccf276c5b52487f7a8", "crypto": { @@ -214,35 +246,36 @@ mod tests { "meta": "{}" }"#; - let expected = KeyFile { - id: Uuid::from_str("8777d9f6-7860-4b9b-88b7-0b57ee6b3a73").unwrap(), - version: Version::V3, - address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()), - crypto: Crypto { - cipher: Cipher::Aes128Ctr(Aes128Ctr { - iv: "b5a7ec855ec9e2c405371356855fec83".into(), - }), - ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(), - kdf: Kdf::Scrypt(Scrypt { - n: 262144, - dklen: 32, - p: 1, - r: 8, - salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(), - }), - mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(), - }, - name: Some("Test".to_owned()), - meta: Some("{}".to_owned()), - }; - - let keyfile: KeyFile = serde_json::from_str(json).unwrap(); - assert_eq!(keyfile, expected); - } - - #[test] - fn capital_crypto_keyfile() { - let json = r#" + let expected = KeyFile { + id: Uuid::from_str("8777d9f6-7860-4b9b-88b7-0b57ee6b3a73").unwrap(), + version: Version::V3, + address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()), + crypto: Crypto { + cipher: Cipher::Aes128Ctr(Aes128Ctr { + iv: "b5a7ec855ec9e2c405371356855fec83".into(), + }), + ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc" + .into(), + kdf: Kdf::Scrypt(Scrypt { + n: 262144, + dklen: 32, + p: 1, + r: 8, + salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(), + }), + mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(), + }, + name: Some("Test".to_owned()), + meta: Some("{}".to_owned()), + }; + + let keyfile: KeyFile = serde_json::from_str(json).unwrap(); + assert_eq!(keyfile, expected); + } + + #[test] + fn capital_crypto_keyfile() { + let json = r#" { "address": "6edddfc6349aff20bc6467ccf276c5b52487f7a8", "Crypto": { @@ -265,60 +298,62 @@ mod tests { "version": 3 }"#; - let expected = KeyFile { - id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(), - version: Version::V3, - address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()), - crypto: Crypto { - cipher: Cipher::Aes128Ctr(Aes128Ctr { - iv: "b5a7ec855ec9e2c405371356855fec83".into(), - }), - ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(), - kdf: Kdf::Scrypt(Scrypt { - n: 262144, - dklen: 32, - p: 1, - r: 8, - salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(), - }), - mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(), - }, - name: None, - meta: None, - }; - - let keyfile: KeyFile = serde_json::from_str(json).unwrap(); - assert_eq!(keyfile, expected); - } - - #[test] - fn to_and_from_json() { - let file = KeyFile { - id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(), - version: Version::V3, - address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()), - crypto: Crypto { - cipher: Cipher::Aes128Ctr(Aes128Ctr { - iv: "b5a7ec855ec9e2c405371356855fec83".into(), - }), - ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(), - kdf: Kdf::Scrypt(Scrypt { - n: 262144, - dklen: 32, - p: 1, - r: 8, - salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(), - }), - mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(), - }, - name: Some("Test".to_owned()), - meta: None, - }; - - let serialized = serde_json::to_string(&file).unwrap(); - println!("{}", serialized); - let deserialized = serde_json::from_str(&serialized).unwrap(); - - assert_eq!(file, deserialized); - } + let expected = KeyFile { + id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(), + version: Version::V3, + address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()), + crypto: Crypto { + cipher: Cipher::Aes128Ctr(Aes128Ctr { + iv: "b5a7ec855ec9e2c405371356855fec83".into(), + }), + ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc" + .into(), + kdf: Kdf::Scrypt(Scrypt { + n: 262144, + dklen: 32, + p: 1, + r: 8, + salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(), + }), + mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(), + }, + name: None, + meta: None, + }; + + let keyfile: KeyFile = serde_json::from_str(json).unwrap(); + assert_eq!(keyfile, expected); + } + + #[test] + fn to_and_from_json() { + let file = KeyFile { + id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(), + version: Version::V3, + address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()), + crypto: Crypto { + cipher: Cipher::Aes128Ctr(Aes128Ctr { + iv: "b5a7ec855ec9e2c405371356855fec83".into(), + }), + ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc" + .into(), + kdf: Kdf::Scrypt(Scrypt { + n: 262144, + dklen: 32, + p: 1, + r: 8, + salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(), + }), + mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(), + }, + name: Some("Test".to_owned()), + meta: None, + }; + + let serialized = serde_json::to_string(&file).unwrap(); + println!("{}", serialized); + let deserialized = serde_json::from_str(&serialized).unwrap(); + + assert_eq!(file, deserialized); + } } diff --git a/accounts/ethstore/src/json/mod.rs b/accounts/ethstore/src/json/mod.rs index 2b6348aae2e..02614eaea83 100644 --- a/accounts/ethstore/src/json/mod.rs +++ b/accounts/ethstore/src/json/mod.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Contract interface specification. @@ -29,15 +29,20 @@ mod vault_file; mod vault_key_file; mod version; -pub use self::bytes::Bytes; -pub use self::cipher::{Cipher, CipherSer, CipherSerParams, Aes128Ctr}; -pub use self::crypto::{Crypto, CipherText}; -pub use self::error::Error; -pub use self::hash::{H128, H160, H256}; -pub use self::id::Uuid; -pub use self::kdf::{Kdf, KdfSer, Prf, Pbkdf2, Scrypt, KdfSerParams}; -pub use self::key_file::{KeyFile, OpaqueKeyFile}; -pub use self::presale::{PresaleWallet, Encseed}; -pub use self::vault_file::VaultFile; -pub use self::vault_key_file::{VaultKeyFile, VaultKeyMeta, insert_vault_name_to_json_meta, remove_vault_name_from_json_meta}; -pub use self::version::Version; +pub use self::{ + bytes::Bytes, + cipher::{Aes128Ctr, Cipher, CipherSer, CipherSerParams}, + crypto::{CipherText, Crypto}, + error::Error, + hash::{H128, H160, H256}, + id::Uuid, + kdf::{Kdf, KdfSer, KdfSerParams, Pbkdf2, Prf, Scrypt}, + key_file::{KeyFile, OpaqueKeyFile}, + presale::{Encseed, PresaleWallet}, + vault_file::VaultFile, + vault_key_file::{ + insert_vault_name_to_json_meta, remove_vault_name_from_json_meta, VaultKeyFile, + VaultKeyMeta, + }, + version::Version, +}; diff --git a/accounts/ethstore/src/json/presale.rs b/accounts/ethstore/src/json/presale.rs index 70568d510f3..a2c9d6ce2d7 100644 --- a/accounts/ethstore/src/json/presale.rs +++ b/accounts/ethstore/src/json/presale.rs @@ -1,47 +1,50 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::io::Read; +use super::{Bytes, H160}; use serde_json; -use super::{H160, Bytes}; +use std::io::Read; pub type Encseed = Bytes; #[derive(Debug, PartialEq, Deserialize)] pub struct PresaleWallet { - pub encseed: Encseed, - #[serde(rename = "ethaddr")] - pub address: H160, + pub encseed: Encseed, + #[serde(rename = "ethaddr")] + pub address: H160, } impl PresaleWallet { - pub fn load(reader: R) -> Result where R: Read { - serde_json::from_reader(reader) - } + pub fn load(reader: R) -> Result + where + R: Read, + { + serde_json::from_reader(reader) + } } #[cfg(test)] mod tests { - use std::str::FromStr; - use serde_json; - use json::{PresaleWallet, H160}; + use json::{PresaleWallet, H160}; + use serde_json; + use std::str::FromStr; - #[test] - fn presale_wallet() { - let json = r#" + #[test] + fn presale_wallet() { + let json = r#" { "encseed": "137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066", "ethaddr": "ede84640d1a1d3e06902048e67aa7db8d52c2ce1", @@ -49,18 +52,18 @@ mod tests { "btcaddr": "1JvqEc6WLhg6GnyrLBe2ztPAU28KRfuseH" } "#; - let expected = PresaleWallet { + let expected = PresaleWallet { encseed: "137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066".into(), address: H160::from_str("ede84640d1a1d3e06902048e67aa7db8d52c2ce1").unwrap(), }; - let wallet: PresaleWallet = serde_json::from_str(json).unwrap(); - assert_eq!(expected, wallet); - } + let wallet: PresaleWallet = serde_json::from_str(json).unwrap(); + assert_eq!(expected, wallet); + } - #[test] - fn long_presale_wallet() { - let json = r#" + #[test] + fn long_presale_wallet() { + let json = r#" { "encseed": "137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0d", @@ -69,12 +72,12 @@ mod tests { "btcaddr": "1JvqEc6WLhg6GnyrLBe2ztPAU28KRfuseH" } "#; - let expected = PresaleWallet { + let expected = PresaleWallet { encseed: "137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0d".into(), address: H160::from_str("ede84640d1a1d3e06902048e67aa7db8d52c2ce1").unwrap(), }; - let wallet: PresaleWallet = serde_json::from_str(json).unwrap(); - assert_eq!(expected, wallet); - } + let wallet: PresaleWallet = serde_json::from_str(json).unwrap(); + assert_eq!(expected, wallet); + } } diff --git a/accounts/ethstore/src/json/vault_file.rs b/accounts/ethstore/src/json/vault_file.rs index 0da870931d6..cd3de8e1563 100644 --- a/accounts/ethstore/src/json/vault_file.rs +++ b/accounts/ethstore/src/json/vault_file.rs @@ -1,99 +1,105 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::io::{Read, Write}; -use serde_json; use super::Crypto; +use serde_json; +use std::io::{Read, Write}; /// Vault meta file #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct VaultFile { - /// Vault password, encrypted with vault password - pub crypto: Crypto, - /// Vault metadata string - pub meta: Option, + /// Vault password, encrypted with vault password + pub crypto: Crypto, + /// Vault metadata string + pub meta: Option, } impl VaultFile { - pub fn load(reader: R) -> Result where R: Read { - serde_json::from_reader(reader) - } + pub fn load(reader: R) -> Result + where + R: Read, + { + serde_json::from_reader(reader) + } - pub fn write(&self, writer: &mut W) -> Result<(), serde_json::Error> where W: Write { - serde_json::to_writer(writer, self) - } + pub fn write(&self, writer: &mut W) -> Result<(), serde_json::Error> + where + W: Write, + { + serde_json::to_writer(writer, self) + } } #[cfg(test)] mod test { - use serde_json; - use json::{VaultFile, Crypto, Cipher, Aes128Ctr, Kdf, Pbkdf2, Prf}; - use std::num::NonZeroU32; + use json::{Aes128Ctr, Cipher, Crypto, Kdf, Pbkdf2, Prf, VaultFile}; + use serde_json; + use std::num::NonZeroU32; - lazy_static! { - static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(1024).expect("1024 > 0; qed"); - } + lazy_static! { + static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(1024).expect("1024 > 0; qed"); + } - #[test] - fn to_and_from_json() { - let file = VaultFile { - crypto: Crypto { - cipher: Cipher::Aes128Ctr(Aes128Ctr { - iv: "0155e3690be19fbfbecabcd440aa284b".into(), - }), - ciphertext: "4d6938a1f49b7782".into(), - kdf: Kdf::Pbkdf2(Pbkdf2 { - c: *ITERATIONS, - dklen: 32, - prf: Prf::HmacSha256, - salt: "b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5".into(), - }), - mac: "16381463ea11c6eb2239a9f339c2e780516d29d234ce30ac5f166f9080b5a262".into(), - }, - meta: Some("{}".into()), - }; + #[test] + fn to_and_from_json() { + let file = VaultFile { + crypto: Crypto { + cipher: Cipher::Aes128Ctr(Aes128Ctr { + iv: "0155e3690be19fbfbecabcd440aa284b".into(), + }), + ciphertext: "4d6938a1f49b7782".into(), + kdf: Kdf::Pbkdf2(Pbkdf2 { + c: *ITERATIONS, + dklen: 32, + prf: Prf::HmacSha256, + salt: "b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5".into(), + }), + mac: "16381463ea11c6eb2239a9f339c2e780516d29d234ce30ac5f166f9080b5a262".into(), + }, + meta: Some("{}".into()), + }; - let serialized = serde_json::to_string(&file).unwrap(); - let deserialized = serde_json::from_str(&serialized).unwrap(); + let serialized = serde_json::to_string(&file).unwrap(); + let deserialized = serde_json::from_str(&serialized).unwrap(); - assert_eq!(file, deserialized); - } + assert_eq!(file, deserialized); + } - #[test] - fn to_and_from_json_no_meta() { - let file = VaultFile { - crypto: Crypto { - cipher: Cipher::Aes128Ctr(Aes128Ctr { - iv: "0155e3690be19fbfbecabcd440aa284b".into(), - }), - ciphertext: "4d6938a1f49b7782".into(), - kdf: Kdf::Pbkdf2(Pbkdf2 { - c: *ITERATIONS, - dklen: 32, - prf: Prf::HmacSha256, - salt: "b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5".into(), - }), - mac: "16381463ea11c6eb2239a9f339c2e780516d29d234ce30ac5f166f9080b5a262".into(), - }, - meta: None, - }; + #[test] + fn to_and_from_json_no_meta() { + let file = VaultFile { + crypto: Crypto { + cipher: Cipher::Aes128Ctr(Aes128Ctr { + iv: "0155e3690be19fbfbecabcd440aa284b".into(), + }), + ciphertext: "4d6938a1f49b7782".into(), + kdf: Kdf::Pbkdf2(Pbkdf2 { + c: *ITERATIONS, + dklen: 32, + prf: Prf::HmacSha256, + salt: "b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5".into(), + }), + mac: "16381463ea11c6eb2239a9f339c2e780516d29d234ce30ac5f166f9080b5a262".into(), + }, + meta: None, + }; - let serialized = serde_json::to_string(&file).unwrap(); - let deserialized = serde_json::from_str(&serialized).unwrap(); + let serialized = serde_json::to_string(&file).unwrap(); + let deserialized = serde_json::from_str(&serialized).unwrap(); - assert_eq!(file, deserialized); - } + assert_eq!(file, deserialized); + } } diff --git a/accounts/ethstore/src/json/vault_key_file.rs b/accounts/ethstore/src/json/vault_key_file.rs index dd4ba497988..47b53a2908c 100644 --- a/accounts/ethstore/src/json/vault_key_file.rs +++ b/accounts/ethstore/src/json/vault_key_file.rs @@ -1,25 +1,23 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::io::{Read, Write}; +use super::{Crypto, Uuid, Version, H160}; use serde::de::Error; -use serde_json; -use serde_json::value::Value; -use serde_json::error; -use super::{Uuid, Version, Crypto, H160}; +use serde_json::{self, error, value::Value}; +use std::io::{Read, Write}; /// Meta key name for vault field const VAULT_NAME_META_KEY: &'static str = "vault"; @@ -27,94 +25,112 @@ const VAULT_NAME_META_KEY: &'static str = "vault"; /// Key file as stored in vaults #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct VaultKeyFile { - /// Key id - pub id: Uuid, - /// Key version - pub version: Version, - /// Secret, encrypted with account password - pub crypto: Crypto, - /// Serialized `VaultKeyMeta`, encrypted with vault password - pub metacrypto: Crypto, + /// Key id + pub id: Uuid, + /// Key version + pub version: Version, + /// Secret, encrypted with account password + pub crypto: Crypto, + /// Serialized `VaultKeyMeta`, encrypted with vault password + pub metacrypto: Crypto, } /// Data, stored in `VaultKeyFile::metacrypto` #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct VaultKeyMeta { - /// Key address - pub address: H160, - /// Key name - pub name: Option, - /// Key metadata - pub meta: Option, + /// Key address + pub address: H160, + /// Key name + pub name: Option, + /// Key metadata + pub meta: Option, } /// Insert vault name to the JSON meta field -pub fn insert_vault_name_to_json_meta(meta: &str, vault_name: &str) -> Result { - let mut meta = if meta.is_empty() { - Value::Object(serde_json::Map::new()) - } else { - serde_json::from_str(meta)? - }; - - if let Some(meta_obj) = meta.as_object_mut() { - meta_obj.insert(VAULT_NAME_META_KEY.to_owned(), Value::String(vault_name.to_owned())); - serde_json::to_string(meta_obj) - } else { - Err(error::Error::custom("Meta is expected to be a serialized JSON object")) - } +pub fn insert_vault_name_to_json_meta( + meta: &str, + vault_name: &str, +) -> Result { + let mut meta = if meta.is_empty() { + Value::Object(serde_json::Map::new()) + } else { + serde_json::from_str(meta)? + }; + + if let Some(meta_obj) = meta.as_object_mut() { + meta_obj.insert( + VAULT_NAME_META_KEY.to_owned(), + Value::String(vault_name.to_owned()), + ); + serde_json::to_string(meta_obj) + } else { + Err(error::Error::custom( + "Meta is expected to be a serialized JSON object", + )) + } } /// Remove vault name from the JSON meta field pub fn remove_vault_name_from_json_meta(meta: &str) -> Result { - let mut meta = if meta.is_empty() { - Value::Object(serde_json::Map::new()) - } else { - serde_json::from_str(meta)? - }; - - if let Some(meta_obj) = meta.as_object_mut() { - meta_obj.remove(VAULT_NAME_META_KEY); - serde_json::to_string(meta_obj) - } else { - Err(error::Error::custom("Meta is expected to be a serialized JSON object")) - } + let mut meta = if meta.is_empty() { + Value::Object(serde_json::Map::new()) + } else { + serde_json::from_str(meta)? + }; + + if let Some(meta_obj) = meta.as_object_mut() { + meta_obj.remove(VAULT_NAME_META_KEY); + serde_json::to_string(meta_obj) + } else { + Err(error::Error::custom( + "Meta is expected to be a serialized JSON object", + )) + } } impl VaultKeyFile { - pub fn load(reader: R) -> Result where R: Read { - serde_json::from_reader(reader) - } - - pub fn write(&self, writer: &mut W) -> Result<(), serde_json::Error> where W: Write { - serde_json::to_writer(writer, self) - } + pub fn load(reader: R) -> Result + where + R: Read, + { + serde_json::from_reader(reader) + } + + pub fn write(&self, writer: &mut W) -> Result<(), serde_json::Error> + where + W: Write, + { + serde_json::to_writer(writer, self) + } } impl VaultKeyMeta { - pub fn load(bytes: &[u8]) -> Result { - serde_json::from_slice(&bytes) - } - - pub fn write(&self) -> Result, serde_json::Error> { - let s = serde_json::to_string(self)?; - Ok(s.as_bytes().into()) - } + pub fn load(bytes: &[u8]) -> Result { + serde_json::from_slice(&bytes) + } + + pub fn write(&self) -> Result, serde_json::Error> { + let s = serde_json::to_string(self)?; + Ok(s.as_bytes().into()) + } } #[cfg(test)] mod test { - use serde_json; - use json::{VaultKeyFile, Version, Crypto, Cipher, Aes128Ctr, Kdf, Pbkdf2, Prf, - insert_vault_name_to_json_meta, remove_vault_name_from_json_meta}; - use std::num::NonZeroU32; - - lazy_static! { - static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed"); - } - - #[test] - fn to_and_from_json() { - let file = VaultKeyFile { + use json::{ + insert_vault_name_to_json_meta, remove_vault_name_from_json_meta, Aes128Ctr, Cipher, + Crypto, Kdf, Pbkdf2, Prf, VaultKeyFile, Version, + }; + use serde_json; + use std::num::NonZeroU32; + + lazy_static! { + static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed"); + } + + #[test] + fn to_and_from_json() { + let file = VaultKeyFile { id: "08d82c39-88e3-7a71-6abb-89c8f36c3ceb".into(), version: Version::V3, crypto: Crypto { @@ -145,33 +161,45 @@ mod test { } }; - let serialized = serde_json::to_string(&file).unwrap(); - let deserialized = serde_json::from_str(&serialized).unwrap(); - - assert_eq!(file, deserialized); - } - - #[test] - fn vault_name_inserted_to_json_meta() { - assert_eq!(insert_vault_name_to_json_meta(r#""#, "MyVault").unwrap(), r#"{"vault":"MyVault"}"#); - assert_eq!(insert_vault_name_to_json_meta(r#"{"tags":["kalabala"]}"#, "MyVault").unwrap(), r#"{"tags":["kalabala"],"vault":"MyVault"}"#); - } - - #[test] - fn vault_name_not_inserted_to_json_meta() { - assert!(insert_vault_name_to_json_meta(r#"///3533"#, "MyVault").is_err()); - assert!(insert_vault_name_to_json_meta(r#""string""#, "MyVault").is_err()); - } - - #[test] - fn vault_name_removed_from_json_meta() { - assert_eq!(remove_vault_name_from_json_meta(r#"{"vault":"MyVault"}"#).unwrap(), r#"{}"#); - assert_eq!(remove_vault_name_from_json_meta(r#"{"tags":["kalabala"],"vault":"MyVault"}"#).unwrap(), r#"{"tags":["kalabala"]}"#); - } - - #[test] - fn vault_name_not_removed_from_json_meta() { - assert!(remove_vault_name_from_json_meta(r#"///3533"#).is_err()); - assert!(remove_vault_name_from_json_meta(r#""string""#).is_err()); - } + let serialized = serde_json::to_string(&file).unwrap(); + let deserialized = serde_json::from_str(&serialized).unwrap(); + + assert_eq!(file, deserialized); + } + + #[test] + fn vault_name_inserted_to_json_meta() { + assert_eq!( + insert_vault_name_to_json_meta(r#""#, "MyVault").unwrap(), + r#"{"vault":"MyVault"}"# + ); + assert_eq!( + insert_vault_name_to_json_meta(r#"{"tags":["kalabala"]}"#, "MyVault").unwrap(), + r#"{"tags":["kalabala"],"vault":"MyVault"}"# + ); + } + + #[test] + fn vault_name_not_inserted_to_json_meta() { + assert!(insert_vault_name_to_json_meta(r#"///3533"#, "MyVault").is_err()); + assert!(insert_vault_name_to_json_meta(r#""string""#, "MyVault").is_err()); + } + + #[test] + fn vault_name_removed_from_json_meta() { + assert_eq!( + remove_vault_name_from_json_meta(r#"{"vault":"MyVault"}"#).unwrap(), + r#"{}"# + ); + assert_eq!( + remove_vault_name_from_json_meta(r#"{"tags":["kalabala"],"vault":"MyVault"}"#).unwrap(), + r#"{"tags":["kalabala"]}"# + ); + } + + #[test] + fn vault_name_not_removed_from_json_meta() { + assert!(remove_vault_name_from_json_meta(r#"///3533"#).is_err()); + assert!(remove_vault_name_from_json_meta(r#""string""#).is_err()); + } } diff --git a/accounts/ethstore/src/json/version.rs b/accounts/ethstore/src/json/version.rs index cd8439c59f2..127654c1fef 100644 --- a/accounts/ethstore/src/json/version.rs +++ b/accounts/ethstore/src/json/version.rs @@ -1,58 +1,67 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::fmt; -use serde::{Serialize, Serializer, Deserialize, Deserializer}; -use serde::de::{Error as SerdeError, Visitor}; use super::Error; +use serde::{ + de::{Error as SerdeError, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; +use std::fmt; #[derive(Debug, PartialEq)] pub enum Version { - V3, + V3, } impl Serialize for Version { - fn serialize(&self, serializer: S) -> Result - where S: Serializer { - match *self { - Version::V3 => serializer.serialize_u64(3) - } - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + Version::V3 => serializer.serialize_u64(3), + } + } } impl<'a> Deserialize<'a> for Version { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> { - deserializer.deserialize_any(VersionVisitor) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + deserializer.deserialize_any(VersionVisitor) + } } struct VersionVisitor; impl<'a> Visitor<'a> for VersionVisitor { - type Value = Version; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a valid key version identifier") - } - - fn visit_u64(self, value: u64) -> Result where E: SerdeError { - match value { - 3 => Ok(Version::V3), - _ => Err(SerdeError::custom(Error::UnsupportedVersion)) - } - } + type Value = Version; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a valid key version identifier") + } + + fn visit_u64(self, value: u64) -> Result + where + E: SerdeError, + { + match value { + 3 => Ok(Version::V3), + _ => Err(SerdeError::custom(Error::UnsupportedVersion)), + } + } } diff --git a/accounts/ethstore/src/lib.rs b/accounts/ethstore/src/lib.rs index c0955caeb0d..0197229ca20 100644 --- a/accounts/ethstore/src/lib.rs +++ b/accounts/ethstore/src/lib.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Ethereum key-management. @@ -27,13 +27,13 @@ extern crate rustc_hex; extern crate serde; extern crate serde_json; extern crate smallvec; +extern crate tempdir; extern crate time; extern crate tiny_keccak; -extern crate tempdir; -extern crate parity_crypto as crypto; extern crate ethereum_types; extern crate ethkey as _ethkey; +extern crate parity_crypto as crypto; extern crate parity_wordlist; #[macro_use] @@ -60,18 +60,20 @@ mod presale; mod random; mod secret_store; -pub use self::account::{SafeAccount, Crypto}; -pub use self::error::Error; -pub use self::ethstore::{EthStore, EthMultiStore}; -pub use self::import::{import_account, import_accounts, read_geth_accounts}; -pub use self::json::OpaqueKeyFile as KeyFile; -pub use self::presale::PresaleWallet; -pub use self::secret_store::{ - SecretVaultRef, StoreAccountRef, SimpleSecretStore, SecretStore, - Derivation, IndexDerivation, +pub use self::{ + account::{Crypto, SafeAccount}, + error::Error, + ethstore::{EthMultiStore, EthStore}, + import::{import_account, import_accounts}, + json::OpaqueKeyFile as KeyFile, + parity_wordlist::random_phrase, + presale::PresaleWallet, + random::random_string, + secret_store::{ + Derivation, IndexDerivation, SecretStore, SecretVaultRef, SimpleSecretStore, + StoreAccountRef, + }, }; -pub use self::random::random_string; -pub use self::parity_wordlist::random_phrase; /// An opaque wrapper for secret. pub struct OpaqueSecret(::ethkey::Secret); diff --git a/accounts/ethstore/src/presale.rs b/accounts/ethstore/src/presale.rs index 8ca5d0b98bb..0bf5905a903 100644 --- a/accounts/ethstore/src/presale.rs +++ b/accounts/ethstore/src/presale.rs @@ -1,91 +1,93 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::fs; -use std::num::NonZeroU32; -use std::path::Path; +use crypto::{self, pbkdf2, Keccak256}; +use ethkey::{Address, KeyPair, Password, Secret}; use json; -use ethkey::{Address, Secret, KeyPair, Password}; -use crypto::{Keccak256, pbkdf2}; -use {crypto, Error}; +use std::{fs, num::NonZeroU32, path::Path}; +use Error; /// Pre-sale wallet. pub struct PresaleWallet { - iv: [u8; 16], - ciphertext: Vec, - address: Address, + iv: [u8; 16], + ciphertext: Vec, + address: Address, } impl From for PresaleWallet { - fn from(wallet: json::PresaleWallet) -> Self { - let mut iv = [0u8; 16]; - iv.copy_from_slice(&wallet.encseed[..16]); - - let mut ciphertext = vec![]; - ciphertext.extend_from_slice(&wallet.encseed[16..]); - - PresaleWallet { - iv: iv, - ciphertext: ciphertext, - address: Address::from(wallet.address), - } - } + fn from(wallet: json::PresaleWallet) -> Self { + let mut iv = [0u8; 16]; + iv.copy_from_slice(&wallet.encseed[..16]); + + let mut ciphertext = vec![]; + ciphertext.extend_from_slice(&wallet.encseed[16..]); + + PresaleWallet { + iv: iv, + ciphertext: ciphertext, + address: Address::from(wallet.address), + } + } } impl PresaleWallet { - /// Open a pre-sale wallet. - pub fn open

(path: P) -> Result where P: AsRef { - let file = fs::File::open(path)?; - let presale = json::PresaleWallet::load(file) - .map_err(|e| Error::InvalidKeyFile(format!("{}", e)))?; - Ok(PresaleWallet::from(presale)) - } - - /// Decrypt the wallet. - pub fn decrypt(&self, password: &Password) -> Result { - let mut derived_key = [0u8; 32]; - let salt = pbkdf2::Salt(password.as_bytes()); - let sec = pbkdf2::Secret(password.as_bytes()); - let iter = NonZeroU32::new(2000).expect("2000 > 0; qed"); - pbkdf2::sha256(iter, salt, sec, &mut derived_key); - - let mut key = vec![0; self.ciphertext.len()]; - let len = crypto::aes::decrypt_128_cbc(&derived_key[0..16], &self.iv, &self.ciphertext, &mut key) - .map_err(|_| Error::InvalidPassword)?; - let unpadded = &key[..len]; - - let secret = Secret::from_unsafe_slice(&unpadded.keccak256())?; - if let Ok(kp) = KeyPair::from_secret(secret) { - if kp.address() == self.address { - return Ok(kp) - } - } - - Err(Error::InvalidPassword) - } + /// Open a pre-sale wallet. + pub fn open

(path: P) -> Result + where + P: AsRef, + { + let file = fs::File::open(path)?; + let presale = + json::PresaleWallet::load(file).map_err(|e| Error::InvalidKeyFile(format!("{}", e)))?; + Ok(PresaleWallet::from(presale)) + } + + /// Decrypt the wallet. + pub fn decrypt(&self, password: &Password) -> Result { + let mut derived_key = [0u8; 32]; + let salt = pbkdf2::Salt(password.as_bytes()); + let sec = pbkdf2::Secret(password.as_bytes()); + let iter = NonZeroU32::new(2000).expect("2000 > 0; qed"); + pbkdf2::sha256(iter, salt, sec, &mut derived_key); + + let mut key = vec![0; self.ciphertext.len()]; + let len = + crypto::aes::decrypt_128_cbc(&derived_key[0..16], &self.iv, &self.ciphertext, &mut key) + .map_err(|_| Error::InvalidPassword)?; + let unpadded = &key[..len]; + + let secret = Secret::from_unsafe_slice(&unpadded.keccak256())?; + if let Ok(kp) = KeyPair::from_secret(secret) { + if kp.address() == self.address { + return Ok(kp); + } + } + + Err(Error::InvalidPassword) + } } #[cfg(test)] mod tests { - use super::PresaleWallet; - use json; + use super::PresaleWallet; + use json; - #[test] - fn test() { - let json = r#" + #[test] + fn test() { + let json = r#" { "encseed": "137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066", "ethaddr": "ede84640d1a1d3e06902048e67aa7db8d52c2ce1", @@ -93,9 +95,9 @@ mod tests { "btcaddr": "1JvqEc6WLhg6GnyrLBe2ztPAU28KRfuseH" } "#; - let wallet = json::PresaleWallet::load(json.as_bytes()).unwrap(); - let wallet = PresaleWallet::from(wallet); - assert!(wallet.decrypt(&"123".into()).is_ok()); - assert!(wallet.decrypt(&"124".into()).is_err()); - } + let wallet = json::PresaleWallet::load(json.as_bytes()).unwrap(); + let wallet = PresaleWallet::from(wallet); + assert!(wallet.decrypt(&"123".into()).is_ok()); + assert!(wallet.decrypt(&"124".into()).is_err()); + } } diff --git a/accounts/ethstore/src/random.rs b/accounts/ethstore/src/random.rs index 969c8a366d5..c50abf85fd4 100644 --- a/accounts/ethstore/src/random.rs +++ b/accounts/ethstore/src/random.rs @@ -1,45 +1,47 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use rand::{Rng, OsRng}; +use rand::{OsRng, Rng}; pub trait Random { - fn random() -> Self where Self: Sized; + fn random() -> Self + where + Self: Sized; } impl Random for [u8; 16] { - fn random() -> Self { - let mut result = [0u8; 16]; - let mut rng = OsRng::new().unwrap(); - rng.fill_bytes(&mut result); - result - } + fn random() -> Self { + let mut result = [0u8; 16]; + let mut rng = OsRng::new().unwrap(); + rng.fill_bytes(&mut result); + result + } } impl Random for [u8; 32] { - fn random() -> Self { - let mut result = [0u8; 32]; - let mut rng = OsRng::new().unwrap(); - rng.fill_bytes(&mut result); - result - } + fn random() -> Self { + let mut result = [0u8; 32]; + let mut rng = OsRng::new().unwrap(); + rng.fill_bytes(&mut result); + result + } } /// Generate a random string of given length. pub fn random_string(length: usize) -> String { - let mut rng = OsRng::new().expect("Not able to operate without random source."); - rng.gen_ascii_chars().take(length).collect() + let mut rng = OsRng::new().expect("Not able to operate without random source."); + rng.gen_ascii_chars().take(length).collect() } diff --git a/accounts/ethstore/src/secret_store.rs b/accounts/ethstore/src/secret_store.rs index 5571f83c0cd..b9c098c46f7 100644 --- a/accounts/ethstore/src/secret_store.rs +++ b/accounts/ethstore/src/secret_store.rs @@ -1,190 +1,268 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::hash::{Hash, Hasher}; -use std::path::PathBuf; -use std::cmp::Ordering; -use ethkey::{Address, Message, Signature, Secret, Password, Public}; -use Error; -use json::{Uuid, OpaqueKeyFile}; use ethereum_types::H256; +use ethkey::{Address, Message, Password, Public, Secret, Signature}; +use json::{OpaqueKeyFile, Uuid}; +use std::{ + cmp::Ordering, + hash::{Hash, Hasher}, + path::PathBuf, +}; +use Error; use OpaqueSecret; /// Key directory reference #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum SecretVaultRef { - /// Reference to key in root directory - Root, - /// Referenc to key in specific vault - Vault(String), + /// Reference to key in root directory + Root, + /// Referenc to key in specific vault + Vault(String), } /// Stored account reference #[derive(Debug, Clone, PartialEq, Eq, Ord)] pub struct StoreAccountRef { - /// Account address - pub address: Address, - /// Vault reference - pub vault: SecretVaultRef, + /// Account address + pub address: Address, + /// Vault reference + pub vault: SecretVaultRef, } impl PartialOrd for StoreAccountRef { - fn partial_cmp(&self, other: &StoreAccountRef) -> Option { - Some(self.address.cmp(&other.address).then_with(|| self.vault.cmp(&other.vault))) - } + fn partial_cmp(&self, other: &StoreAccountRef) -> Option { + Some( + self.address + .cmp(&other.address) + .then_with(|| self.vault.cmp(&other.vault)), + ) + } } impl ::std::borrow::Borrow

for StoreAccountRef { - fn borrow(&self) -> &Address { - &self.address - } + fn borrow(&self) -> &Address { + &self.address + } } /// Simple Secret Store API pub trait SimpleSecretStore: Send + Sync { - /// Inserts new accounts to the store (or vault) with given password. - fn insert_account(&self, vault: SecretVaultRef, secret: Secret, password: &Password) -> Result; - /// Inserts new derived account to the store (or vault) with given password. - fn insert_derived(&self, vault: SecretVaultRef, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation) -> Result; - /// Changes accounts password. - fn change_password(&self, account: &StoreAccountRef, old_password: &Password, new_password: &Password) -> Result<(), Error>; - /// Exports key details for account. - fn export_account(&self, account: &StoreAccountRef, password: &Password) -> Result; - /// Entirely removes account from the store and underlying storage. - fn remove_account(&self, account: &StoreAccountRef, password: &Password) -> Result<(), Error>; - /// Generates new derived account. - fn generate_derived(&self, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation) -> Result; - /// Sign a message with given account. - fn sign(&self, account: &StoreAccountRef, password: &Password, message: &Message) -> Result; - /// Sign a message with derived account. - fn sign_derived(&self, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation, message: &Message) -> Result; - /// Decrypt a messages with given account. - fn decrypt(&self, account: &StoreAccountRef, password: &Password, shared_mac: &[u8], message: &[u8]) -> Result, Error>; - /// Agree on shared key. - fn agree(&self, account: &StoreAccountRef, password: &Password, other: &Public) -> Result; - - /// Returns all accounts in this secret store. - fn accounts(&self) -> Result, Error>; - /// Get reference to some account with given address. - /// This method could be removed if we will guarantee that there is max(1) account for given address. - fn account_ref(&self, address: &Address) -> Result; - - /// Create new vault with given password - fn create_vault(&self, name: &str, password: &Password) -> Result<(), Error>; - /// Open vault with given password - fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error>; - /// Close vault - fn close_vault(&self, name: &str) -> Result<(), Error>; - /// List all vaults - fn list_vaults(&self) -> Result, Error>; - /// List all currently opened vaults - fn list_opened_vaults(&self) -> Result, Error>; - /// Change vault password - fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error>; - /// Cnage account' vault - fn change_account_vault(&self, vault: SecretVaultRef, account: StoreAccountRef) -> Result; - /// Get vault metadata string. - fn get_vault_meta(&self, name: &str) -> Result; - /// Set vault metadata string. - fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error>; + /// Inserts new accounts to the store (or vault) with given password. + fn insert_account( + &self, + vault: SecretVaultRef, + secret: Secret, + password: &Password, + ) -> Result; + /// Inserts new derived account to the store (or vault) with given password. + fn insert_derived( + &self, + vault: SecretVaultRef, + account_ref: &StoreAccountRef, + password: &Password, + derivation: Derivation, + ) -> Result; + /// Changes accounts password. + fn change_password( + &self, + account: &StoreAccountRef, + old_password: &Password, + new_password: &Password, + ) -> Result<(), Error>; + /// Exports key details for account. + fn export_account( + &self, + account: &StoreAccountRef, + password: &Password, + ) -> Result; + /// Entirely removes account from the store and underlying storage. + fn remove_account(&self, account: &StoreAccountRef, password: &Password) -> Result<(), Error>; + /// Generates new derived account. + fn generate_derived( + &self, + account_ref: &StoreAccountRef, + password: &Password, + derivation: Derivation, + ) -> Result; + /// Sign a message with given account. + fn sign( + &self, + account: &StoreAccountRef, + password: &Password, + message: &Message, + ) -> Result; + /// Sign a message with derived account. + fn sign_derived( + &self, + account_ref: &StoreAccountRef, + password: &Password, + derivation: Derivation, + message: &Message, + ) -> Result; + /// Decrypt a messages with given account. + fn decrypt( + &self, + account: &StoreAccountRef, + password: &Password, + shared_mac: &[u8], + message: &[u8], + ) -> Result, Error>; + /// Agree on shared key. + fn agree( + &self, + account: &StoreAccountRef, + password: &Password, + other: &Public, + ) -> Result; + + /// Returns all accounts in this secret store. + fn accounts(&self) -> Result, Error>; + /// Get reference to some account with given address. + /// This method could be removed if we will guarantee that there is max(1) account for given address. + fn account_ref(&self, address: &Address) -> Result; + + /// Create new vault with given password + fn create_vault(&self, name: &str, password: &Password) -> Result<(), Error>; + /// Open vault with given password + fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error>; + /// Close vault + fn close_vault(&self, name: &str) -> Result<(), Error>; + /// List all vaults + fn list_vaults(&self) -> Result, Error>; + /// List all currently opened vaults + fn list_opened_vaults(&self) -> Result, Error>; + /// Change vault password + fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error>; + /// Cnage account' vault + fn change_account_vault( + &self, + vault: SecretVaultRef, + account: StoreAccountRef, + ) -> Result; + /// Get vault metadata string. + fn get_vault_meta(&self, name: &str) -> Result; + /// Set vault metadata string. + fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error>; } /// Secret Store API pub trait SecretStore: SimpleSecretStore { + /// Returns a raw opaque Secret that can be later used to sign a message. + fn raw_secret( + &self, + account: &StoreAccountRef, + password: &Password, + ) -> Result; + + /// Signs a message with raw secret. + fn sign_with_secret( + &self, + secret: &OpaqueSecret, + message: &Message, + ) -> Result { + Ok(::ethkey::sign(&secret.0, message)?) + } + + /// Imports presale wallet + fn import_presale( + &self, + vault: SecretVaultRef, + json: &[u8], + password: &Password, + ) -> Result; + /// Imports existing JSON wallet + fn import_wallet( + &self, + vault: SecretVaultRef, + json: &[u8], + password: &Password, + gen_id: bool, + ) -> Result; + /// Copies account between stores and vaults. + fn copy_account( + &self, + new_store: &dyn SimpleSecretStore, + new_vault: SecretVaultRef, + account: &StoreAccountRef, + password: &Password, + new_password: &Password, + ) -> Result<(), Error>; + /// Checks if password matches given account. + fn test_password(&self, account: &StoreAccountRef, password: &Password) -> Result; - /// Returns a raw opaque Secret that can be later used to sign a message. - fn raw_secret(&self, account: &StoreAccountRef, password: &Password) -> Result; - - /// Signs a message with raw secret. - fn sign_with_secret(&self, secret: &OpaqueSecret, message: &Message) -> Result { - Ok(::ethkey::sign(&secret.0, message)?) - } - - /// Imports presale wallet - fn import_presale(&self, vault: SecretVaultRef, json: &[u8], password: &Password) -> Result; - /// Imports existing JSON wallet - fn import_wallet(&self, vault: SecretVaultRef, json: &[u8], password: &Password, gen_id: bool) -> Result; - /// Copies account between stores and vaults. - fn copy_account(&self, new_store: &SimpleSecretStore, new_vault: SecretVaultRef, account: &StoreAccountRef, password: &Password, new_password: &Password) -> Result<(), Error>; - /// Checks if password matches given account. - fn test_password(&self, account: &StoreAccountRef, password: &Password) -> Result; - - /// Returns a public key for given account. - fn public(&self, account: &StoreAccountRef, password: &Password) -> Result; - - /// Returns uuid of an account. - fn uuid(&self, account: &StoreAccountRef) -> Result; - /// Returns account's name. - fn name(&self, account: &StoreAccountRef) -> Result; - /// Returns account's metadata. - fn meta(&self, account: &StoreAccountRef) -> Result; - - /// Modifies account metadata. - fn set_name(&self, account: &StoreAccountRef, name: String) -> Result<(), Error>; - /// Modifies account name. - fn set_meta(&self, account: &StoreAccountRef, meta: String) -> Result<(), Error>; - - /// Returns local path of the store. - fn local_path(&self) -> PathBuf; - /// Lists all found geth accounts. - fn list_geth_accounts(&self, testnet: bool) -> Vec
; - /// Imports geth accounts to the store/vault. - fn import_geth_accounts(&self, vault: SecretVaultRef, desired: Vec
, testnet: bool) -> Result, Error>; + /// Returns a public key for given account. + fn public(&self, account: &StoreAccountRef, password: &Password) -> Result; + + /// Returns uuid of an account. + fn uuid(&self, account: &StoreAccountRef) -> Result; + /// Returns account's name. + fn name(&self, account: &StoreAccountRef) -> Result; + /// Returns account's metadata. + fn meta(&self, account: &StoreAccountRef) -> Result; + + /// Modifies account metadata. + fn set_name(&self, account: &StoreAccountRef, name: String) -> Result<(), Error>; + /// Modifies account name. + fn set_meta(&self, account: &StoreAccountRef, meta: String) -> Result<(), Error>; + + /// Returns local path of the store. + fn local_path(&self) -> PathBuf; } impl StoreAccountRef { - /// Create reference to root account with given address - pub fn root(address: Address) -> Self { - StoreAccountRef::new(SecretVaultRef::Root, address) - } - - /// Create reference to vault account with given address - pub fn vault(vault_name: &str, address: Address) -> Self { - StoreAccountRef::new(SecretVaultRef::Vault(vault_name.to_owned()), address) - } - - /// Create new account reference - pub fn new(vault_ref: SecretVaultRef, address: Address) -> Self { - StoreAccountRef { - vault: vault_ref, - address: address, - } - } + /// Create reference to root account with given address + pub fn root(address: Address) -> Self { + StoreAccountRef::new(SecretVaultRef::Root, address) + } + + /// Create reference to vault account with given address + pub fn vault(vault_name: &str, address: Address) -> Self { + StoreAccountRef::new(SecretVaultRef::Vault(vault_name.to_owned()), address) + } + + /// Create new account reference + pub fn new(vault_ref: SecretVaultRef, address: Address) -> Self { + StoreAccountRef { + vault: vault_ref, + address: address, + } + } } impl Hash for StoreAccountRef { - fn hash(&self, state: &mut H) { - self.address.hash(state); - } + fn hash(&self, state: &mut H) { + self.address.hash(state); + } } /// Node in hierarchical derivation. pub struct IndexDerivation { - /// Node is soft (allows proof of parent from parent node). - pub soft: bool, - /// Index sequence of the node. - pub index: u32, + /// Node is soft (allows proof of parent from parent node). + pub soft: bool, + /// Index sequence of the node. + pub index: u32, } /// Derivation scheme for keys pub enum Derivation { - /// Hierarchical derivation - Hierarchical(Vec), - /// Hash derivation, soft. - SoftHash(H256), - /// Hash derivation, hard. - HardHash(H256), + /// Hierarchical derivation + Hierarchical(Vec), + /// Hash derivation, soft. + SoftHash(H256), + /// Hash derivation, hard. + HardHash(H256), } diff --git a/accounts/ethstore/tests/api.rs b/accounts/ethstore/tests/api.rs index c274737522b..c37100c223c 100644 --- a/accounts/ethstore/tests/api.rs +++ b/accounts/ethstore/tests/api.rs @@ -1,154 +1,197 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -extern crate rand; extern crate ethstore; +extern crate rand; mod util; -use ethstore::{EthStore, SimpleSecretStore, SecretVaultRef, StoreAccountRef}; -use ethstore::ethkey::{Random, Generator, Secret, KeyPair, verify_address}; -use ethstore::accounts_dir::RootDiskDirectory; +use ethstore::{ + accounts_dir::RootDiskDirectory, + ethkey::{verify_address, Generator, KeyPair, Random, Secret}, + EthStore, SecretVaultRef, SimpleSecretStore, StoreAccountRef, +}; use util::TransientDir; #[test] fn secret_store_create() { - let dir = TransientDir::create().unwrap(); - let _ = EthStore::open(Box::new(dir)).unwrap(); + let dir = TransientDir::create().unwrap(); + let _ = EthStore::open(Box::new(dir)).unwrap(); } #[test] #[should_panic] fn secret_store_open_not_existing() { - let dir = TransientDir::open(); - let _ = EthStore::open(Box::new(dir)).unwrap(); + let dir = TransientDir::open(); + let _ = EthStore::open(Box::new(dir)).unwrap(); } fn random_secret() -> Secret { - Random.generate().unwrap().secret().clone() + Random.generate().unwrap().secret().clone() } #[test] fn secret_store_create_account() { - let dir = TransientDir::create().unwrap(); - let store = EthStore::open(Box::new(dir)).unwrap(); - assert_eq!(store.accounts().unwrap().len(), 0); - assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok()); - assert_eq!(store.accounts().unwrap().len(), 1); - assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok()); - assert_eq!(store.accounts().unwrap().len(), 2); + let dir = TransientDir::create().unwrap(); + let store = EthStore::open(Box::new(dir)).unwrap(); + assert_eq!(store.accounts().unwrap().len(), 0); + assert!(store + .insert_account(SecretVaultRef::Root, random_secret(), &"".into()) + .is_ok()); + assert_eq!(store.accounts().unwrap().len(), 1); + assert!(store + .insert_account(SecretVaultRef::Root, random_secret(), &"".into()) + .is_ok()); + assert_eq!(store.accounts().unwrap().len(), 2); } #[test] fn secret_store_sign() { - let dir = TransientDir::create().unwrap(); - let store = EthStore::open(Box::new(dir)).unwrap(); - assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok()); - let accounts = store.accounts().unwrap(); - assert_eq!(accounts.len(), 1); - assert!(store.sign(&accounts[0], &"".into(), &Default::default()).is_ok()); - assert!(store.sign(&accounts[0], &"1".into(), &Default::default()).is_err()); + let dir = TransientDir::create().unwrap(); + let store = EthStore::open(Box::new(dir)).unwrap(); + assert!(store + .insert_account(SecretVaultRef::Root, random_secret(), &"".into()) + .is_ok()); + let accounts = store.accounts().unwrap(); + assert_eq!(accounts.len(), 1); + assert!(store + .sign(&accounts[0], &"".into(), &Default::default()) + .is_ok()); + assert!(store + .sign(&accounts[0], &"1".into(), &Default::default()) + .is_err()); } #[test] fn secret_store_change_password() { - let dir = TransientDir::create().unwrap(); - let store = EthStore::open(Box::new(dir)).unwrap(); - assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok()); - let accounts = store.accounts().unwrap(); - assert_eq!(accounts.len(), 1); - assert!(store.sign(&accounts[0], &"".into(), &Default::default()).is_ok()); - assert!(store.change_password(&accounts[0], &"".into(), &"1".into()).is_ok()); - assert!(store.sign(&accounts[0], &"".into(), &Default::default()).is_err()); - assert!(store.sign(&accounts[0], &"1".into(), &Default::default()).is_ok()); + let dir = TransientDir::create().unwrap(); + let store = EthStore::open(Box::new(dir)).unwrap(); + assert!(store + .insert_account(SecretVaultRef::Root, random_secret(), &"".into()) + .is_ok()); + let accounts = store.accounts().unwrap(); + assert_eq!(accounts.len(), 1); + assert!(store + .sign(&accounts[0], &"".into(), &Default::default()) + .is_ok()); + assert!(store + .change_password(&accounts[0], &"".into(), &"1".into()) + .is_ok()); + assert!(store + .sign(&accounts[0], &"".into(), &Default::default()) + .is_err()); + assert!(store + .sign(&accounts[0], &"1".into(), &Default::default()) + .is_ok()); } #[test] fn secret_store_remove_account() { - let dir = TransientDir::create().unwrap(); - let store = EthStore::open(Box::new(dir)).unwrap(); - assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok()); - let accounts = store.accounts().unwrap(); - assert_eq!(accounts.len(), 1); - assert!(store.remove_account(&accounts[0], &"".into()).is_ok()); - assert_eq!(store.accounts().unwrap().len(), 0); - assert!(store.remove_account(&accounts[0], &"".into()).is_err()); + let dir = TransientDir::create().unwrap(); + let store = EthStore::open(Box::new(dir)).unwrap(); + assert!(store + .insert_account(SecretVaultRef::Root, random_secret(), &"".into()) + .is_ok()); + let accounts = store.accounts().unwrap(); + assert_eq!(accounts.len(), 1); + assert!(store.remove_account(&accounts[0], &"".into()).is_ok()); + assert_eq!(store.accounts().unwrap().len(), 0); + assert!(store.remove_account(&accounts[0], &"".into()).is_err()); } fn test_path() -> &'static str { - match ::std::fs::metadata("ethstore") { - Ok(_) => "ethstore/tests/res/geth_keystore", - Err(_) => "tests/res/geth_keystore", - } + match ::std::fs::metadata("ethstore") { + Ok(_) => "ethstore/tests/res/geth_keystore", + Err(_) => "tests/res/geth_keystore", + } } fn pat_path() -> &'static str { - match ::std::fs::metadata("ethstore") { - Ok(_) => "ethstore/tests/res/pat", - Err(_) => "tests/res/pat", - } + match ::std::fs::metadata("ethstore") { + Ok(_) => "ethstore/tests/res/pat", + Err(_) => "tests/res/pat", + } } fn ciphertext_path() -> &'static str { - match ::std::fs::metadata("ethstore") { - Ok(_) => "ethstore/tests/res/ciphertext", - Err(_) => "tests/res/ciphertext", - } + match ::std::fs::metadata("ethstore") { + Ok(_) => "ethstore/tests/res/ciphertext", + Err(_) => "tests/res/ciphertext", + } } #[test] fn secret_store_laod_geth_files() { - let dir = RootDiskDirectory::at(test_path()); - let store = EthStore::open(Box::new(dir)).unwrap(); - assert_eq!(store.accounts().unwrap(), vec![ - StoreAccountRef::root("3f49624084b67849c7b4e805c5988c21a430f9d9".into()), - StoreAccountRef::root("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf".into()), - StoreAccountRef::root("63121b431a52f8043c16fcf0d1df9cb7b5f66649".into()), - ]); + let dir = RootDiskDirectory::at(test_path()); + let store = EthStore::open(Box::new(dir)).unwrap(); + assert_eq!( + store.accounts().unwrap(), + vec![ + StoreAccountRef::root("3f49624084b67849c7b4e805c5988c21a430f9d9".into()), + StoreAccountRef::root("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf".into()), + StoreAccountRef::root("63121b431a52f8043c16fcf0d1df9cb7b5f66649".into()), + ] + ); } #[test] fn secret_store_load_pat_files() { - let dir = RootDiskDirectory::at(pat_path()); - let store = EthStore::open(Box::new(dir)).unwrap(); - assert_eq!(store.accounts().unwrap(), vec![ - StoreAccountRef::root("3f49624084b67849c7b4e805c5988c21a430f9d9".into()), - StoreAccountRef::root("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf".into()), - ]); + let dir = RootDiskDirectory::at(pat_path()); + let store = EthStore::open(Box::new(dir)).unwrap(); + assert_eq!( + store.accounts().unwrap(), + vec![ + StoreAccountRef::root("3f49624084b67849c7b4e805c5988c21a430f9d9".into()), + StoreAccountRef::root("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf".into()), + ] + ); } #[test] fn test_decrypting_files_with_short_ciphertext() { - // 31e9d1e6d844bd3a536800ef8d8be6a9975db509, 30 - let kp1 = KeyPair::from_secret("000081c29e8142bb6a81bef5a92bda7a8328a5c85bb2f9542e76f9b0f94fc018".parse().unwrap()).unwrap(); - // d1e64e5480bfaf733ba7d48712decb8227797a4e , 31 - let kp2 = KeyPair::from_secret("00fa7b3db73dc7dfdf8c5fbdb796d741e4488628c41fc4febd9160a866ba0f35".parse().unwrap()).unwrap(); - let dir = RootDiskDirectory::at(ciphertext_path()); - let store = EthStore::open(Box::new(dir)).unwrap(); - let accounts = store.accounts().unwrap(); - assert_eq!(accounts, vec![ - StoreAccountRef::root("31e9d1e6d844bd3a536800ef8d8be6a9975db509".into()), - StoreAccountRef::root("d1e64e5480bfaf733ba7d48712decb8227797a4e".into()), - ]); - - let message = Default::default(); - - let s1 = store.sign(&accounts[0], &"foo".into(), &message).unwrap(); - let s2 = store.sign(&accounts[1], &"foo".into(), &message).unwrap(); - assert!(verify_address(&accounts[0].address, &s1, &message).unwrap()); - assert!(verify_address(&kp1.address(), &s1, &message).unwrap()); - assert!(verify_address(&kp2.address(), &s2, &message).unwrap()); + // 31e9d1e6d844bd3a536800ef8d8be6a9975db509, 30 + let kp1 = KeyPair::from_secret( + "000081c29e8142bb6a81bef5a92bda7a8328a5c85bb2f9542e76f9b0f94fc018" + .parse() + .unwrap(), + ) + .unwrap(); + // d1e64e5480bfaf733ba7d48712decb8227797a4e , 31 + let kp2 = KeyPair::from_secret( + "00fa7b3db73dc7dfdf8c5fbdb796d741e4488628c41fc4febd9160a866ba0f35" + .parse() + .unwrap(), + ) + .unwrap(); + let dir = RootDiskDirectory::at(ciphertext_path()); + let store = EthStore::open(Box::new(dir)).unwrap(); + let accounts = store.accounts().unwrap(); + assert_eq!( + accounts, + vec![ + StoreAccountRef::root("31e9d1e6d844bd3a536800ef8d8be6a9975db509".into()), + StoreAccountRef::root("d1e64e5480bfaf733ba7d48712decb8227797a4e".into()), + ] + ); + + let message = Default::default(); + + let s1 = store.sign(&accounts[0], &"foo".into(), &message).unwrap(); + let s2 = store.sign(&accounts[1], &"foo".into(), &message).unwrap(); + assert!(verify_address(&accounts[0].address, &s1, &message).unwrap()); + assert!(verify_address(&kp1.address(), &s1, &message).unwrap()); + assert!(verify_address(&kp2.address(), &s2, &message).unwrap()); } diff --git a/accounts/ethstore/tests/util/mod.rs b/accounts/ethstore/tests/util/mod.rs index 76500796275..ea2af9839ac 100644 --- a/accounts/ethstore/tests/util/mod.rs +++ b/accounts/ethstore/tests/util/mod.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . mod transient_dir; diff --git a/accounts/ethstore/tests/util/transient_dir.rs b/accounts/ethstore/tests/util/transient_dir.rs index 67511a9b99b..88fb755c1b1 100644 --- a/accounts/ethstore/tests/util/transient_dir.rs +++ b/accounts/ethstore/tests/util/transient_dir.rs @@ -1,81 +1,82 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::path::PathBuf; -use std::{env, fs}; -use rand::{Rng, OsRng}; -use ethstore::accounts_dir::{KeyDirectory, RootDiskDirectory}; -use ethstore::{Error, SafeAccount}; +use ethstore::{ + accounts_dir::{KeyDirectory, RootDiskDirectory}, + Error, SafeAccount, +}; +use rand::{OsRng, Rng}; +use std::{env, fs, path::PathBuf}; pub fn random_dir() -> PathBuf { - let mut rng = OsRng::new().unwrap(); - let mut dir = env::temp_dir(); - dir.push(format!("{:x}-{:x}", rng.next_u64(), rng.next_u64())); - dir + let mut rng = OsRng::new().unwrap(); + let mut dir = env::temp_dir(); + dir.push(format!("{:x}-{:x}", rng.next_u64(), rng.next_u64())); + dir } pub struct TransientDir { - dir: RootDiskDirectory, - path: PathBuf, + dir: RootDiskDirectory, + path: PathBuf, } impl TransientDir { - pub fn create() -> Result { - let path = random_dir(); - let result = TransientDir { - dir: RootDiskDirectory::create(&path)?, - path: path, - }; + pub fn create() -> Result { + let path = random_dir(); + let result = TransientDir { + dir: RootDiskDirectory::create(&path)?, + path: path, + }; - Ok(result) - } + Ok(result) + } - pub fn open() -> Self { - let path = random_dir(); - TransientDir { - dir: RootDiskDirectory::at(&path), - path: path, - } - } + pub fn open() -> Self { + let path = random_dir(); + TransientDir { + dir: RootDiskDirectory::at(&path), + path: path, + } + } } impl Drop for TransientDir { - fn drop(&mut self) { - fs::remove_dir_all(&self.path).expect("Expected to remove temp dir"); - } + fn drop(&mut self) { + fs::remove_dir_all(&self.path).expect("Expected to remove temp dir"); + } } impl KeyDirectory for TransientDir { - fn load(&self) -> Result, Error> { - self.dir.load() - } + fn load(&self) -> Result, Error> { + self.dir.load() + } - fn update(&self, account: SafeAccount) -> Result { - self.dir.update(account) - } + fn update(&self, account: SafeAccount) -> Result { + self.dir.update(account) + } - fn insert(&self, account: SafeAccount) -> Result { - self.dir.insert(account) - } + fn insert(&self, account: SafeAccount) -> Result { + self.dir.insert(account) + } - fn remove(&self, account: &SafeAccount) -> Result<(), Error> { - self.dir.remove(account) - } + fn remove(&self, account: &SafeAccount) -> Result<(), Error> { + self.dir.remove(account) + } - fn unique_repr(&self) -> Result { - self.dir.unique_repr() - } + fn unique_repr(&self) -> Result { + self.dir.unique_repr() + } } diff --git a/accounts/fake-hardware-wallet/Cargo.toml b/accounts/fake-hardware-wallet/Cargo.toml deleted file mode 100644 index 5e20d8a6d31..00000000000 --- a/accounts/fake-hardware-wallet/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -description = "Fake hardware-wallet, for OS' that don't support libusb" -name = "fake-hardware-wallet" -version = "0.0.1" -license = "GPL-3.0" -authors = ["Parity Technologies "] - -[dependencies] -ethereum-types = "0.4" -ethkey = { path = "../../accounts/ethkey" } diff --git a/accounts/fake-hardware-wallet/src/lib.rs b/accounts/fake-hardware-wallet/src/lib.rs deleted file mode 100644 index d04590865cd..00000000000 --- a/accounts/fake-hardware-wallet/src/lib.rs +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Dummy module for platforms that does not provide support for hardware wallets (libusb) - -extern crate ethereum_types; -extern crate ethkey; - -use std::fmt; -use ethereum_types::U256; -use ethkey::{Address, Signature}; - -pub struct WalletInfo { - pub address: Address, - pub name: String, - pub manufacturer: String, -} - -#[derive(Debug)] -/// `ErrorType` for devices with no `hardware wallet` -pub enum Error { - NoWallet, - KeyNotFound, -} - -pub struct TransactionInfo { - /// Nonce - pub nonce: U256, - /// Gas price - pub gas_price: U256, - /// Gas limit - pub gas_limit: U256, - /// Receiver - pub to: Option
, - /// Value - pub value: U256, - /// Data - pub data: Vec, - /// Chain ID - pub chain_id: Option, -} - -pub enum KeyPath { - /// Ethereum. - Ethereum, - /// Ethereum classic. - EthereumClassic, -} - -/// `HardwareWalletManager` for devices with no `hardware wallet` -pub struct HardwareWalletManager; - -impl HardwareWalletManager { - pub fn new() -> Result { - Err(Error::NoWallet) - } - - pub fn set_key_path(&self, _key_path: KeyPath) {} - - pub fn wallet_info(&self, _: &Address) -> Option { - None - } - - pub fn list_wallets(&self) -> Vec { - Vec::with_capacity(0) - } - - pub fn list_locked_wallets(&self) -> Result, Error> { - Err(Error::NoWallet) - } - - pub fn pin_matrix_ack(&self, _: &str, _: &str) -> Result { - Err(Error::NoWallet) - } - - pub fn sign_transaction(&self, _address: &Address, _transaction: &TransactionInfo, _rlp_transaction: &[u8]) -> Result { - Err(Error::NoWallet) } - - pub fn sign_message(&self, _address: &Address, _msg: &[u8]) -> Result { - Err(Error::NoWallet) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "No hardware wallet!!") - } -} diff --git a/accounts/hw/Cargo.toml b/accounts/hw/Cargo.toml deleted file mode 100644 index ae7f0a655c5..00000000000 --- a/accounts/hw/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -description = "Hardware wallet support." -homepage = "http://parity.io" -license = "GPL-3.0" -name = "hardware-wallet" -version = "1.12.0" -authors = ["Parity Technologies "] - -[dependencies] -log = "0.4" -parking_lot = "0.7" -protobuf = "1.4" -hidapi = { git = "https://github.com/paritytech/hidapi-rs" } -libusb = { git = "https://github.com/paritytech/libusb-rs" } -trezor-sys = { git = "https://github.com/paritytech/trezor-sys" } -ethkey = { path = "../ethkey" } -ethereum-types = "0.4" -semver = "0.9" - -[dev-dependencies] -rustc-hex = "1.0" diff --git a/accounts/hw/src/ledger.rs b/accounts/hw/src/ledger.rs deleted file mode 100644 index 9bad48ec545..00000000000 --- a/accounts/hw/src/ledger.rs +++ /dev/null @@ -1,534 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Ledger hardware wallet module. Supports Ledger Blue and Nano S. -//! See for protocol details. - -use std::cmp::min; -use std::str::FromStr; -use std::sync::Arc; -use std::time::{Duration, Instant}; -use std::fmt; - -use ethereum_types::{H256, Address}; -use ethkey::Signature; -use hidapi; -use libusb; -use parking_lot::{Mutex, RwLock}; -use semver::Version as FirmwareVersion; -use super::{WalletInfo, KeyPath, Device, DeviceDirection, Wallet, is_valid_hid_device}; - -const APDU_TAG: u8 = 0x05; -const APDU_CLA: u8 = 0xe0; -const APDU_PAYLOAD_HEADER_LEN: usize = 7; - -const ETH_DERIVATION_PATH_BE: [u8; 17] = [4, 0x80, 0, 0, 44, 0x80, 0, 0, 60, 0x80, 0, 0, 0, 0, 0, 0, 0]; // 44'/60'/0'/0 -const ETC_DERIVATION_PATH_BE: [u8; 21] = [5, 0x80, 0, 0, 44, 0x80, 0, 0, 60, 0x80, 0x02, 0x73, 0xd0, 0x80, 0, 0, 0, 0, 0, 0, 0]; // 44'/60'/160720'/0'/0 - -/// Ledger vendor ID -const LEDGER_VID: u16 = 0x2c97; -/// Ledger product IDs: [Nano S and Blue] -const LEDGER_PIDS: [u16; 2] = [0x0000, 0x0001]; -const LEDGER_TRANSPORT_HEADER_LEN: usize = 5; - -const MAX_CHUNK_SIZE: usize = 255; - -const HID_PACKET_SIZE: usize = 64 + HID_PREFIX_ZERO; - -#[cfg(windows)] const HID_PREFIX_ZERO: usize = 1; -#[cfg(not(windows))] const HID_PREFIX_ZERO: usize = 0; - -mod commands { - pub const GET_APP_CONFIGURATION: u8 = 0x06; - pub const GET_ETH_PUBLIC_ADDRESS: u8 = 0x02; - pub const SIGN_ETH_TRANSACTION: u8 = 0x04; - pub const SIGN_ETH_PERSONAL_MESSAGE: u8 = 0x08; -} - -/// Hardware wallet error. -#[derive(Debug)] -pub enum Error { - /// Ethereum wallet protocol error. - Protocol(&'static str), - /// Hidapi error. - Usb(hidapi::HidError), - /// Libusb error - LibUsb(libusb::Error), - /// Device with request key is not available. - KeyNotFound, - /// Signing has been cancelled by user. - UserCancel, - /// Impossible error - Impossible, - /// No device arrived - NoDeviceArrived, - /// No device left - NoDeviceLeft, - /// Invalid PID or VID - InvalidDevice, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match *self { - Error::Protocol(ref s) => write!(f, "Ledger protocol error: {}", s), - Error::Usb(ref e) => write!(f, "USB communication error: {}", e), - Error::LibUsb(ref e) => write!(f, "LibUSB communication error: {}", e), - Error::KeyNotFound => write!(f, "Key not found"), - Error::UserCancel => write!(f, "Operation has been cancelled"), - Error::Impossible => write!(f, "Placeholder error"), - Error::NoDeviceArrived => write!(f, "No device arrived"), - Error::NoDeviceLeft=> write!(f, "No device left"), - Error::InvalidDevice => write!(f, "Device with non-supported product ID or vendor ID was detected"), - } - } -} - -impl From for Error { - fn from(err: hidapi::HidError) -> Self { - Error::Usb(err) - } -} - -impl From for Error { - fn from(err: libusb::Error) -> Self { - Error::LibUsb(err) - } -} - -/// Ledger device manager. -pub struct Manager { - usb: Arc>, - devices: RwLock>, - key_path: RwLock, -} - -impl Manager { - /// Create a new instance. - pub fn new(usb: Arc>) -> Arc { - Arc::new(Self { - usb, - devices: RwLock::new(Vec::new()), - key_path: RwLock::new(KeyPath::Ethereum), - }) - } - - // Transport Protocol: - // * Communication Channel Id (2 bytes big endian ) - // * Command Tag (1 byte) - // * Packet Sequence ID (2 bytes big endian) - // * Payload (Optional) - // - // Payload - // * APDU Total Length (2 bytes big endian) - // * APDU_CLA (1 byte) - // * APDU_INS (1 byte) - // * APDU_P1 (1 byte) - // * APDU_P2 (1 byte) - // * APDU_LENGTH (1 byte) - // * APDU_Payload (Variable) - // - fn write(handle: &hidapi::HidDevice, command: u8, p1: u8, p2: u8, data: &[u8]) -> Result<(), Error> { - let data_len = data.len(); - let mut offset = 0; - let mut sequence_number = 0; - let mut hid_chunk = [0_u8; HID_PACKET_SIZE]; - - while sequence_number == 0 || offset < data_len { - let header = if sequence_number == 0 { LEDGER_TRANSPORT_HEADER_LEN + APDU_PAYLOAD_HEADER_LEN } else { LEDGER_TRANSPORT_HEADER_LEN }; - let size = min(64 - header, data_len - offset); - { - let chunk = &mut hid_chunk[HID_PREFIX_ZERO..]; - chunk[0..5].copy_from_slice(&[0x01, 0x01, APDU_TAG, (sequence_number >> 8) as u8, (sequence_number & 0xff) as u8 ]); - - if sequence_number == 0 { - let data_len = data.len() + 5; - chunk[5..12].copy_from_slice(&[(data_len >> 8) as u8, (data_len & 0xff) as u8, APDU_CLA, command, p1, p2, data.len() as u8]); - } - - chunk[header..header + size].copy_from_slice(&data[offset..offset + size]); - } - trace!(target: "hw", "Ledger write {:?}", &hid_chunk[..]); - let n = handle.write(&hid_chunk[..])?; - if n < size + header { - return Err(Error::Protocol("Write data size mismatch")); - } - offset += size; - sequence_number += 1; - if sequence_number >= 0xffff { - return Err(Error::Protocol("Maximum sequence number reached")); - } - } - Ok(()) - } - - // Transport Protocol: - // * Communication Channel Id (2 bytes big endian ) - // * Command Tag (1 byte) - // * Packet Sequence ID (2 bytes big endian) - // * Payload (Optional) - // - // Payload - // * APDU Total Length (2 bytes big endian) - // * APDU_CLA (1 byte) - // * APDU_INS (1 byte) - // * APDU_P1 (1 byte) - // * APDU_P2 (1 byte) - // * APDU_LENGTH (1 byte) - // * APDU_Payload (Variable) - // - fn read(handle: &hidapi::HidDevice) -> Result, Error> { - let mut message_size = 0; - let mut message = Vec::new(); - - // terminate the loop if `sequence_number` reaches its max_value and report error - for chunk_index in 0..=0xffff { - let mut chunk: [u8; HID_PACKET_SIZE] = [0; HID_PACKET_SIZE]; - let chunk_size = handle.read(&mut chunk)?; - trace!(target: "hw", "Ledger read {:?}", &chunk[..]); - if chunk_size < LEDGER_TRANSPORT_HEADER_LEN || chunk[0] != 0x01 || chunk[1] != 0x01 || chunk[2] != APDU_TAG { - return Err(Error::Protocol("Unexpected chunk header")); - } - let seq = (chunk[3] as usize) << 8 | (chunk[4] as usize); - if seq != chunk_index { - return Err(Error::Protocol("Unexpected chunk header")); - } - - let mut offset = 5; - if seq == 0 { - // Read message size and status word. - if chunk_size < 7 { - return Err(Error::Protocol("Unexpected chunk header")); - } - message_size = (chunk[5] as usize) << 8 | (chunk[6] as usize); - offset += 2; - } - message.extend_from_slice(&chunk[offset..chunk_size]); - message.truncate(message_size); - if message.len() == message_size { - break; - } - } - if message.len() < 2 { - return Err(Error::Protocol("No status word")); - } - let status = (message[message.len() - 2] as usize) << 8 | (message[message.len() - 1] as usize); - debug!(target: "hw", "Read status {:x}", status); - match status { - 0x6700 => Err(Error::Protocol("Incorrect length")), - 0x6982 => Err(Error::Protocol("Security status not satisfied (Canceled by user)")), - 0x6a80 => Err(Error::Protocol("Invalid data")), - 0x6a82 => Err(Error::Protocol("File not found")), - 0x6a85 => Err(Error::UserCancel), - 0x6b00 => Err(Error::Protocol("Incorrect parameters")), - 0x6d00 => Err(Error::Protocol("Not implemented. Make sure the Ledger Ethereum Wallet app is running.")), - 0x6faa => Err(Error::Protocol("Your Ledger need to be unplugged")), - 0x6f00...0x6fff => Err(Error::Protocol("Internal error")), - 0x9000 => Ok(()), - _ => Err(Error::Protocol("Unknown error")), - - }?; - let new_len = message.len() - 2; - message.truncate(new_len); - Ok(message) - } - - fn send_apdu(handle: &hidapi::HidDevice, command: u8, p1: u8, p2: u8, data: &[u8]) -> Result, Error> { - Self::write(&handle, command, p1, p2, data)?; - Self::read(&handle) - } - - fn get_firmware_version(handle: &hidapi::HidDevice) -> Result { - let ver = Self::send_apdu(&handle, commands::GET_APP_CONFIGURATION, 0, 0, &[])?; - if ver.len() != 4 { - return Err(Error::Protocol("Version packet size mismatch")); - } - Ok(FirmwareVersion::new(ver[1].into(), ver[2].into(), ver[3].into())) - } - - fn get_derivation_path(&self) -> &[u8] { - match *self.key_path.read() { - KeyPath::Ethereum => Ð_DERIVATION_PATH_BE, - KeyPath::EthereumClassic => &ETC_DERIVATION_PATH_BE, - } - } - - fn signer_helper(&self, address: &Address, data: &[u8], command: u8) -> Result { - let usb = self.usb.lock(); - let devices = self.devices.read(); - let device = devices.iter().find(|d| &d.info.address == address).ok_or(Error::KeyNotFound)?; - let handle = self.open_path(|| usb.open_path(&device.path))?; - - // Signing personal messages are only support by Ledger firmware version 1.0.8 or newer - if command == commands::SIGN_ETH_PERSONAL_MESSAGE { - let version = Self::get_firmware_version(&handle)?; - if version < FirmwareVersion::new(1, 0, 8) { - return Err(Error::Protocol("Signing personal messages with Ledger requires version 1.0.8")); - } - } - - let mut chunk= [0_u8; MAX_CHUNK_SIZE]; - let derivation_path = self.get_derivation_path(); - - // Copy the address of the key (only done once) - chunk[0..derivation_path.len()].copy_from_slice(derivation_path); - - let key_length = derivation_path.len(); - let max_payload_size = MAX_CHUNK_SIZE - key_length; - let data_len = data.len(); - - let mut result = Vec::new(); - let mut offset = 0; - - while offset < data_len { - let p1 = if offset == 0 { 0 } else { 0x80 }; - let take = min(max_payload_size, data_len - offset); - - // Fetch piece of data and copy it! - { - let (_key, d) = &mut chunk.split_at_mut(key_length); - let (dst, _rem) = &mut d.split_at_mut(take); - dst.copy_from_slice(&data[offset..(offset + take)]); - } - - result = Self::send_apdu(&handle, command, p1, 0, &chunk[0..(key_length + take)])?; - offset += take; - } - - if result.len() != 65 { - return Err(Error::Protocol("Signature packet size mismatch")); - } - let v = (result[0] + 1) % 2; - let r = H256::from_slice(&result[1..33]); - let s = H256::from_slice(&result[33..65]); - Ok(Signature::from_rsv(&r, &s, v)) - } - - pub fn sign_message(&self, address: &Address, msg: &[u8]) -> Result { - self.signer_helper(address, msg, commands::SIGN_ETH_PERSONAL_MESSAGE) - } -} - -impl<'a> Wallet<'a> for Manager { - type Error = Error; - type Transaction = &'a [u8]; - - fn sign_transaction(&self, address: &Address, transaction: Self::Transaction) -> Result { - self.signer_helper(address, transaction, commands::SIGN_ETH_TRANSACTION) - } - - fn set_key_path(&self, key_path: KeyPath) { - *self.key_path.write() = key_path; - } - - fn update_devices(&self, device_direction: DeviceDirection) -> Result { - let mut usb = self.usb.lock(); - usb.refresh_devices(); - let devices = usb.devices(); - let num_prev_devices = self.devices.read().len(); - - // Sometimes when a ledger is connected at run-time with no other devices connected it will case a `disconnected` event. - // To work around this, ignore such spurious events and poll a couple of extra times in order to get the correct state. - if DeviceDirection::Left == device_direction && num_prev_devices == 0 { - return Err(Error::NoDeviceArrived); - } - - let detected_devices = devices.iter() - .filter(|&d| is_valid_ledger(d.vendor_id, d.product_id) && - is_valid_hid_device(d.usage_page, d.interface_number) - ) - .fold(Vec::new(), |mut v, d| { - match self.read_device(&usb, &d) { - Ok(info) => { - trace!(target: "hw", "Found device: {:?}", info); - v.push(info); - } - Err(e) => trace!(target: "hw", "Error reading device info: {}", e), - }; - v - }); - - let num_curr_devices = detected_devices.len(); - *self.devices.write() = detected_devices; - - match device_direction { - DeviceDirection::Arrived => { - if num_curr_devices > num_prev_devices { - Ok(num_curr_devices - num_prev_devices) - } else { - Err(Error::NoDeviceArrived) - } - } - DeviceDirection::Left => { - if num_prev_devices > num_curr_devices { - Ok(num_prev_devices - num_curr_devices) - } else { - Err(Error::NoDeviceLeft) - } - } - } - } - - fn read_device(&self, usb: &hidapi::HidApi, dev_info: &hidapi::HidDeviceInfo) -> Result { - let handle = self.open_path(|| usb.open_path(&dev_info.path))?; - let manufacturer = dev_info.manufacturer_string.clone().unwrap_or_else(|| "Unknown".to_owned()); - let name = dev_info.product_string.clone().unwrap_or_else(|| "Unknown".to_owned()); - let serial = dev_info.serial_number.clone().unwrap_or_else(|| "Unknown".to_owned()); - match self.get_address(&handle) { - Ok(Some(addr)) => { - Ok(Device { - path: dev_info.path.clone(), - info: WalletInfo { - name, - manufacturer, - serial, - address: addr, - }, - }) - } - // This variant is not possible, but the trait forces this return type - Ok(None) => Err(Error::Impossible), - Err(e) => Err(e), - } - } - - fn list_devices(&self) -> Vec { - self.devices.read().iter().map(|d| d.info.clone()).collect() - } - - // Not used because it is not supported by Ledger - fn list_locked_devices(&self) -> Vec { - vec![] - } - - fn get_wallet(&self, address: &Address) -> Option { - self.devices.read().iter().find(|d| &d.info.address == address).map(|d| d.info.clone()) - } - - fn get_address(&self, device: &hidapi::HidDevice) -> Result, Self::Error> { - let ledger_version = Self::get_firmware_version(&device)?; - if ledger_version < FirmwareVersion::new(1, 0, 3) { - return Err(Error::Protocol("Ledger version 1.0.3 is required")); - } - - let derivation_path = self.get_derivation_path(); - - let key_and_address = Self::send_apdu(device, commands::GET_ETH_PUBLIC_ADDRESS, 0, 0, derivation_path)?; - if key_and_address.len() != 107 { // 1 + 65 PK + 1 + 40 Addr (ascii-hex) - return Err(Error::Protocol("Key packet size mismatch")); - } - let address_string = ::std::str::from_utf8(&key_and_address[67..107]) - .map_err(|_| Error::Protocol("Invalid address string"))?; - - let address = Address::from_str(&address_string) - .map_err(|_| Error::Protocol("Invalid address string"))?; - - Ok(Some(address)) - } - - fn open_path(&self, f: F) -> Result - where F: Fn() -> Result - { - f().map_err(Into::into) - } -} - -/// Check if the detected device is a valid `Ledger device` by checking both the product ID and the vendor ID -pub fn is_valid_ledger(vendor_id: u16, product_id: u16) -> bool { - vendor_id == LEDGER_VID && LEDGER_PIDS.contains(&product_id) -} - -/// Poll the device in maximum `max_polling_duration` if it doesn't succeed -pub fn try_connect_polling(ledger: &Manager, max_polling_duration: &Duration, device_direction: DeviceDirection) -> bool { - let start_time = Instant::now(); - while start_time.elapsed() <= *max_polling_duration { - if let Ok(num_devices) = ledger.update_devices(device_direction) { - trace!(target: "hw", "{} number of Ledger(s) {}", num_devices, device_direction); - return true; - } - } - false -} - -#[cfg(test)] -mod tests { - use rustc_hex::FromHex; - use super::*; - use ::HardwareWalletManager; - - /// This test can't be run without an actual ledger device connected with the `Ledger Wallet Ethereum application` running - #[test] - #[ignore] - fn sign_personal_message() { - let manager = HardwareWalletManager::new().unwrap(); - - let ledger = &manager.ledger; - - // Update device list - ledger.update_devices(DeviceDirection::Arrived).expect("No Ledger found, make sure you have a unlocked Ledger connected with the Ledger Wallet Ethereum running"); - - // Fetch the ethereum address of a connected ledger device - let address = ledger.list_devices() - .iter() - .filter(|d| d.manufacturer == "Ledger".to_string()) - .nth(0) - .map(|d| d.address.clone()) - .expect("No ledger device detected"); - - // 44 bytes transaction - let tx = FromHex::from_hex("eb018504a817c80082520894a6ca2e6707f2cc189794a9dd459d5b05ed1bcd1c8703f26fcfb7a22480018080").unwrap(); - let signature = ledger.sign_transaction(&address, &tx); - assert!(signature.is_ok()); - } - - /// This test can't be run without an actual ledger device connected with the `Ledger Wallet Ethereum application` running - #[test] - #[ignore] - fn smoke() { - let manager = HardwareWalletManager::new().unwrap(); - let ledger = &manager.ledger; - - // Update device list - ledger.update_devices(DeviceDirection::Arrived).expect("No Ledger found, make sure you have a unlocked Ledger connected with the Ledger Wallet Ethereum running"); - - // Fetch the ethereum address of a connected ledger device - let address = ledger.list_devices() - .iter() - .filter(|d| d.manufacturer == "Ledger".to_string()) - .nth(0) - .map(|d| d.address) - .expect("No ledger device detected"); - - // 44 bytes transaction - let tx = FromHex::from_hex("eb018504a817c80082520894a6ca2e6707f2cc189794a9dd459d5b05ed1bcd1c8703f26fcfb7a22480018080").unwrap(); - let signature = ledger.sign_transaction(&address, &tx); - println!("Got {:?}", signature); - assert!(signature.is_ok()); - - // 218 bytes transaction - let large_tx = FromHex::from_hex("f86b028511cfc15d00825208940975ca9f986eee35f5cbba2d672ad9bc8d2a08448766c92c5cf830008026a0d2b0d401b543872d2a6a50de92455decbb868440321bf63a13b310c069e2ba5ba03c6d51bcb2e1653be86546b87f8a12ddb45b6d4e568420299b96f64c19701040f86b028511cfc15d00825208940975ca9f986eee35f5cbba2d672ad9bc8d2a08448766c92c5cf830008026a0d2b0d401b543872d2a6a50de92455decbb868440321bf63a13b310c069e2ba5ba03c6d51bcb2e1653be86546b87f8a12ddb45b6d4e568420299b96f64c19701040").unwrap(); - let signature = ledger.sign_transaction(&address, &large_tx); - println!("Got {:?}", signature); - assert!(signature.is_ok()); - - // 36206 bytes transaction (You need to confirm many transaction on your `Ledger` for this) - let huge_tx = FromHex::from_hex("f86b028511cfc15d00825208940975ca9f986eee35f5cbba2d672ad9bc8d2a08448766c92c5cf830008026a0d2b0d401b543872d2a6a50de92455decbb868440321bf63a13b310c069e2ba5ba03c6d51bcb2e1653be86546b87f8a12ddb45b6d4e568420299b96f64c19701040f86b028511cfc15d00825208940975ca9f986eee35f5cbba2d672ad9bc8d2a08448766c92c5cf830008026a0d2b0d401b543872d2a6a50de92455decbb868440321bf63a13b310c069e2ba5ba03c6d51bcb2e1653be86546b87f8a12ddb45b6d4e568420299b96f64c1970104000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7cd58ab9190c2792714ab06df5b67e66d9e3873eed251d7beb4fa252d6fed6a0ab1e5fabd284f40878d38f6e63d72eec55c6e1aa8d79c06adf714e3523a1f83da763f4bcc9d34424aba82981534066379c1cba244352042de13168556be761f8b1000807b6a6cd340b97a93cd850ee54335b1043bac153c1b0736a88919bb1a21d6befba34d9af51a9b3eb39164c64fe88efe62f136d0bc83cad1f963aec6344b9e406f7381ad2462dcf1434c90c426ee907e6a05abe39c2b36d1dfb966bcf5a4de5af9f07819256357489365c96b21d92a103a776b656fc10ad1083cf679d240bf09bf2eb7635d7bfa969ce7fbb4e0cd5835f79ca9f5583e3a9eca219fab2f773d9c7e838a7a9ef8755dc22e4880367c2b5e40795fe526fc5d1461e50d5cb053e001206460fc6617a38499db525112a7edde38b9547853ad6e5ab359233611148f196501deafae414acde9df81efd7c4144b8fd27f63ac252ecede9609b3f9e634ae95c13058ad2b4529bbb07b5d7ac567c2da994084c3c73ef7c453fc139fcdb3939461da5bf0fa3f2a83517463d02b903af5d845929cf12c9a1479f6801f20085887a94d72814671dac994e14b2faa3251d465ce16d855f33259d94fcc9553b25b488d5c45fe74de60c303bc75bcdde9374ca268767f5767638d1aec5f6f95cab8e9e27b9a80ddf3dbbe24f790debd9e3baa30145d499dd1afb5662a11788b1bb3dedc1ebc5eff9641fa6918d958e4738bae3854e4cd43f9173cd4c9c821190ec287c18035a530c2dc63077d292b3a35b3756ba9e08295a02e37d332552f9f4fdbb945df004aa5b072f9f0e9fc2e4ed6fe455d95b003e5e593dcbfad0b3b47aa855b34008e0e9a2e1cc23b975a3e6808be59dcaa8a87145c1d5183c799d06100d500227e6a758757b4f7d042b3485aa0ce5e91b2b2e67d3cfdf1c226b7ab90e40f0a0d30cbbf425f495bd5a80202909ad419745a59210e2c42a1846e656f67a764ee307abbd76fbb0c99a702253b7a753c3b93e974881f3c97987856b57449e92ffa759da041a2acac59ea2d53836098196355ae0aa2a185dbb002a67c1a278a6032f156bc1e6d7f4ff6c674126af272fdfd1dcd6a810f42878164f1c7ae346b0dd91b678b363d0e33f4b81f2d7cc14da555dcbe4b9f80ac0fed6265a6ecce278888c9794373dcb0d20aa811a9fe9864fab25eaf12764bb2f1a68cd8756cd0b3583f6e5ec74ca5c327b3f6599fa9ec32ccd1831ae323689ef4a1b1a587cbbd2120e0bb8e59f9fc87d93e0365eb36557be6c45c30c1baeba33cdaa877a87e51fd70f2b5521078607d012d65f1fcca8051a01004a6d10f662dfa6445b2ac015cb3ce8fde56bbff93f5d620171e638c6e05504c2aeeeb74c7667aee1709846cb84d345a011c21c1b4e3fd09774ab4dcc63bda04bb0f4fc49d6145d202d807cc2d8eab29b3babe15e53a3656daf0b022ac37513f77660d43d60bdd3e882eef239bfe13dba2e12707733d56e49f638005e06019a7335d8184f1039ab18084de896a946c23045e5c164dc9d32f2f227c89f717a87d1243516b922e5f270c751f1bdb2b1d3a38a15b18a7b8b7e0818573f31320d496e14a348f979b7606c5124e007493f2f40c931f68e3483a46ab2b853a90bd38ae85e6252fece6fd36f7dad0d07b6763d8001a0d6abee62452904f979cc52fa15001b06eef08f17d6e16d493d227ce9277392337a1c71713603e03803d38d1c24184b52049bc029f4f00b22d2acdef91c776a74aa184cc84b0e764f463ed05c2e16a7a0dcb6c27dd4aeca8aeac1545b48896775ba3fe9de4ea36e946d8f4ec16ca7ae58165e8ddc9189d5cc569888a59733529add4b213ea5c00ad3ed3709c0175b542513c90e18f2d4fa2301389102839d969e9f0d614943fe489750f27382f7ab273f51fcb995f449fa5fba108ad0955ed0819a0a62308021ac4ab0c97f04de9fb8533489b2685447ad71c7f9a9bc89975f9cdde87a3af89ae5bff37d1f192a31b7c5aad50486931bc07820d7dae398960965baba6cfc05c56df18b8ef0f5db488eb87be803fc94e3ad3bd6e4f358fe7ce15ca21c9a4752ddfa98337177a7c096d829886e8d71340a01644c64090c84e88235b11bd1fefe506d59733cdd82286fb466ee215914b06a138356e82c0ae6d5fd8e5fb310eb375540308d95b5d53832a5dae9652f91c1e8c14402991e38836813604dcaf272fc552e7682a6eaa7aacfd4ed1c7107b0232cdee00aef865c5577f2391937b76e34810f9d49fe31e54425b6f5e1d0e436e1366e9762d8295877e27ae495ace18fccfaafd850544c9be949d15d421cf6f4bb180225f7f86ca64480975c486df0eeb4fa80a4632cff28d36585cb5dc534553454ea810260983d02060caf6b1eb2b9443b1552ff73d243fecc9779635ed137a3bc8c04ef13f0329a7a5a54b2af0738218cc91be0ee63512f009435d8623ff4e8cdaf743818510b22e42b586a7e5e75525bb61dd2deb96adc95e07998a265d58fe4df4b9ead5b5f15b9daee510558fbdfae7a56931a6f4c729c18e0d29c467fed504810b7d9dfa0613d1657d9bfa5887e3f327cf46d7059a8a0fd654c60cb9c683c55439cd5186d1615f45f7108f261aff77791cf24c975120acf2b357dfbd2defafac0016525cff9400e0feeddff27910fbf2fa84c35fcaaec90863b605db5adbad0593601447605d68b943249861f8cd33c6419c7611403376a6bb438ee857ced2e6842f99ed1b4a9dc79f835813a4f8d07c14f1ef98773286e79cec1c9ce8c26e00418f1b27c7ef104fc96ea2b2ddefb46e2fec4feef2771a1d7e2643586b6fb97094a8d298de12a6f8f78d88e5d67442ed3310fb40aa6439b89c834e43ecd4a80c0a1d74ce6a90a67bcc996a7e93b6f397fe7ab2fa43711a72b84f8c94bd1e4ac62657b98a4b814d8ef2bb469165464a90d5353aa95d09b6ef4ffef081cab5e9dc12d743364f06d4118a585f7d455fd6e3b01434a728a768987c181409eb939e9396666560d394fb151fc67cb9cddea0a94d3e33382bd0617c95304da97994f110eafaaaff6eecb54421e01dc850dc73d77df18bbf68ecc8b37ee2fff7b6f88c139f7d88d763248deb8b4e16a8fab216c0ce88faea030f3a5c994c6e4ef6a9a68cbc9310787232198b020a7c014a1fa32c1736885603dd4921cd360bfb7dca7aafcbe81d7621dbeb4e5c094c2584c339ce70176d7fd2a6cfc4bbea6b433377eff7320d412947ac774688010369b197ec4d0471b9cc73cf9a3e71bd10901beefb10ca1c53428b89ea63427aae9ede5ba104d3fb54d0447458dd9780cd4e925f1edad33f6f0884cc47da562a3c6e2f5a958a8d8723919c4b88d067343a246c6722b6f9f82018d5213648792f38fa8ea1e635b3983dc1f941630fb3762ef1814ee3f41691b24583ddca585289568b4e64f82448b54797d382916e562b3f4795e2d726facea988249e2c3f72d44ec7197b6f783c6c7a133004d5e131b7b4d6a9557c56942ca4bd1f070a2b46c3a6b81bb9a4d570ac6afea75de65ecd331dff1e0252e0f9095f974f47b2d340d67704343b2e8832232210d2f79665bebccab528745c1dc3b28a78aafa3785c29ce2eb6a8403e4d8eded1cc2554ece0a542aa2febd711164f7d7e3a492a87b01d6b4206e593b3aa6d431e908282fcfee0d14dae4b99176a16fa32f730c2d336dcfe7eff84a7aaab1fc32ac8c2e9ab6ebb72c0306bc6998ec22d6cf20c2b6660cfbbeb064b3047c1cf650df12bd153cd7eec5dc181e46575f07c8e292cc191117cd28302d1f9c72d79b1f4062dd683ca95c3a744ac310764e56b2f02a0c2850a2f24c1b298e712374e9adfe68e5414386d7671bd52f6f472eebfdf51677ce379afe7b8085459fb1e6966f5cef45b256489b7ec8a8939cd931009c8a26642f1ff78cab06a5d25522a922cd5e4541dcdbde4848177a42476b141ce9ea035d28742cee0e5e85eb78ceb2b720e112aeb76cd0eb3fc34574c7476110b3b9dff5c19fceae816715b31fc289c0e7149e8488a59e075ac6683f237886a63a25ad23bf903480b9acf3f724d5ace0ca3a842939d4828910cc735e6513dfc4055624d68a048a626fab6b910eaf558c1b43daf1cf26338bca68b5e308b734b61624c97bf70a82430d586a6c3cf59e1bab2532fd9fa1f6fe4f757c7ede0cabea52f2cbf00cc88ca7db4ccc0ff92c0836e7405ebef2ad2e4b7d3b455d8e4d9ae575d884347bdadb67f5e24058a44ae1335280b671ec3bb9d8247e28fecedf5c151fe892bb0f6e67351752e4b1bf75dcd5af3e62ab4aedc5aa32a1606b4a0de3156b356b0fe74e898065d1e720b81663453fc97f935da3b5755a0629f38d6ae5f8e5e77eb64bbef5fc70d4081ebee7a9f7169df4f0e11796f7a79e9128ec996b6fbd8f6fa56e11f17db4925c27f4cd3ddbdee8a50e0b0d4d8f6e527302cbc4dbeef4b0338e6ac7515c1e796b39c8e83f457b50925c39d405f4cd3c1aaf3188c5ac62bf1dd362bc8c9d4e49d3d2b7c2dd2291fa4bb22d7cbe7963b654d92643b789366d1dce842f47919a1cf5073da8916701f907c4d2f8a710c58e85b59f590123d3f8e57cdc14df41a1481a893b9f9505dc0637ba9b27657b0ceab87b0e4bc742924e6d8bf895b407c54df8622018417f9e543fe49f5b10a7a5fc66e5589304af33a20ea108ddf63facebcb20d22eac2fdf4a97285ae6d3f87865fae1331d00e631dfe5366345e0d78bb39a8077484a941176bc63f469f001cfd230347580b6226d6adff5ab112dcd53e7118925296b1a05978a703e383e6ffa5158fc36781f74501564992ab244d3475e1ee8e7146033da2dc116489b84c378e4a750947eb9ccb982a197f13976bb105c81624618c697f32a5b9e03f3675b2315fe773e4922c2e3da7f68ac225107405ece58dc6bbe2bd8947f3e4269ce245589497cd892c750f9ace0440f48057090c8a6cbd5046d3d982d634b4ad6ba41c7a38b7b8b0f91cb6898e769479fc3c7e7d2010b7fb38ef13c17db705a36455a34969803323806009a4e141a5c42da0f7a5e4760d07250d7e483ca6274e57cc2885e5728c24c8b5102845e8bb74b1c394fa7a206ec052c953967380d64c148ca480ab0edbc5da1a7a1e649c2ebfd19fefc52d81aeed7cd83f3c1d2128bd66feb99d5d8fbced01383d2abbf9be47f3390dd336c22b533a731d1c59c3bc5361d781ca15430d84f3c67d6981ab99100f53b6b5623df9d8eecc99d24e02d9301d636c2d5988e98a54339d5b516379a67d50dd9994a28fae5b806c56b353a84cb31729487a6d9851960b83ebc5178be689720a80c5c412e67f8ed55724534c92ab15c3bbc5bf13dfbff02d41ce4c9bc112746b62dea2b21d034e9a31e276eacfeeafc672b95e701ec0fc7ebd4b020a73fc37361b3f136246a0e3a8378442eb5e60abd7da2032dca9b5556aa22e5007c901f438c5e1baeb5d3ec6128a84d310363c6ec17d4ffece27f502b5c63d20cb1d11d0cfc316074faa820a03e6c577389e5e82ebe5f0976b6f5266618f5eb56986714d5cc75fe87176e92dcf01c58029d2b838022c0812c933db17dc4566d233720075065fda26f44b0ed3a46b6143fe180b7a1e6c1558f87b875aedf8c2fa968e2c925f0c08c7e0f23a9cf1b46f7955d9f1db300dab801f5672e2a7231bb2b622b0dc0dd9f2ec64a5f10c239e613247f8685369ed60b2d262c038fcc43924c5aca318385c12412b10d89753f9dfca43eff5f2be7d7d7b2788b877efa8b46ec5c9e99f922839bef71c613cd44cba597cf68de366eaa8874032c14d8012b41e72fd66422f7031d26be0dc4fef8f36a3c124e4ae767a665a94233812984c4466f5bd698b5fc22153c9c2f4110d9defb23c00e722692983b32ee0e84514169910bb21b14066d048960b29b3ff4c090dd5723ca4dcdebd207d4f88da831f0ee7de4aa302a06589a4aba3ca696e7d3c3e9a93af79db91f7a06b0ad825a8652f74bdb72f580e9afb31aae58807e24067f08dd719abb4e6e458bc8aa272d7a5bbd00710c43a1fea220b9022a26b574997517d04573786a4c3e09d30f3ec32f328462e26d4f7ff015121758ce1a2fd51e7f419eb6d8ac04497ab812aa6ba2e981a312ca16c38ed887b2342b0a91348198797919671a23e2b0634b523f931e48ce0d8eb840c54045d9193afec069803901e5ec1108782503cabd0f43373a85acacfa8af44ef2b1d09e4589d2dd4fdcefbf435cb61254f189ad433fa6a4e190627732ae4ef2b0c85cfcbbbaa0137033034e70a3906112dc76ec101f3198e25fb38aad46261d6019690dbf059d66c44e7ada244589c55edfc2e7d18c0ddfcd2d3841bd54d8502763cd0f4696d44686ae3be29ba3063ff6e7aee14de126dc43302f7c0b57d59eb4fdfc4903ccbd3f7309225dd90b5f25c5ade49c14334c0e00fd18b1dc611b10fbbb98c560ad4908842e765c661b9bce005aeede6461254338b8dad3203ee1b58bac1062c7e02e2aa6d420283ed81525839f2c8ff54ac71cc105042c594fb7fd7b55c14cd1247347a197ea8f93c1bbeada1dbf3e59b798c9b15765ab23f856fcf4eeaa5892c3857646bcfd8ad2bf0a15607e0d6696a8548da32955f1f8476f8a20fe4f59b3e9bf4468730b8d46c824a370d37695d1bdcac521032804c5cc66505637701e653ccbddb052f4ecf185b3605d0ba3a4fd99161973e36a35bf79571841ef7506db822dd2a5c959f36418a8dd8acb5b3ecbf3e7918a73695501ef8f440aba43c6e4575880ba3bb83e0a839254fd8d8c6b979d79337a68d218565a5dcb1518c6c82aa73ce7f54a9434ceb5f5fd503137164d74a230e46ce298b98576fea88806bc51e393acdb2abac1da23219b4dbcfba366d834d40dd8e616d214c3478136050555539eba776bf506870c3d20c4a4645b9a7c4ffa976534068009840aadae71f578ef1a325717f64dff840b9dda81b123086a47a172e6793e68af6140b1492058fecd68c4c23db1cc13d2b57f52d0cba89cd4c26d1bd580dd2a054a1d934a80b9eda8ffb503b7e3e62d00a3d075235410149e976529d8029595e4daaae1aa685f3cbdac9b26916320e75b0846d2de8673600212bb648b26e3f1709df425136f33f46129afc90839d24de1e9fee51c685db8a280a5dd4c3ac1539664cc36ffd4537af480d4082146e7395cd6de1f8b652bca8853ec742366702afd6ed79a5920e4ad1317545266f6dbb796ace0fdc731997cd94e1bd8e6689c856adcf153909cfe882b9b02650f4f9eb8620983f0c6b95b3558682d8134a9ec8fa97e174173041115b2eae21fa0b72d0a3c7c2bf9b022fa141a8b495de8321c152b0a9a942c5baf290a234ade4e8b579238a627196fa5621b196ecbe31583517ec4ed82e8d3fb21a892dfd65ccfccd2d36c5d32afa4d4bf201d684c4b1c8c1207db455dede5b908ac63d5fc0bd2b36e11df53bd52e5ce27a9af9444a8cc4391ccc82914b79ba2971ef4ea5d5c30372e7cdbe9bedfcea9ccc8140f8c3ad1bcda29d11fe51affc74f17c9832798e10222701e0d6e93fd109cc9a12df4ee5d38c531574d39a9f4357a60f8150ee509c68e469b4eb0e9be2e6ef9099f1bb949f738fa801d223316fbb1e179b74445228c8b3c40440306e4821077860c37d6b8c17230fcf7ea48d0bb0d98fd3f1f00655e11a8b2e0a7d5da8427784a8fc6d1a2d4d1d3adcc02030b50a700788ce4078c199fc733e2ad469dd9c775d7a8025b4db9b960619f0263b7f09d038cdf85045ac2a1cc5a18364048bf242af713ac4db889489d781ff16b1dcdf66acd89bd6c7651f25a17ce751b67697739dc4d1a125fdd5a8ecbb0cfaf31cd4179249e91171ef3e628dda697afed9d09b53260ae475d59ccb45a6ffd85a2c4241fd134462cf2ec21b51422439aac77954d1b2396761f16e1c6e3242b538f23f584b95cd4b811e35a526748050a7eaa02cebdf8887d94287c99500bf9c2afb7f36ff47e17906534097b02f10620958e889d2392d30660e513c22f580a505314eea4a865d97adb9136c495403e321f425348b56ce8f8e8e91ccd702ade0bbd1efdebef8344bb9defd471ef4b214976556f59f679e0fa39a2007bb9902f5a60ba044c4316c27f6b634241acdc3ce437c4fad599aabba291bfd71c05eca6d9df49abc33ae7709f6622e516c22418e7ab86144f6baf3697bfeeee65294175e5dc9ce5ec82da64537f5f5b83f5a938e41fa8f6f97f9102fda8bcbfb6a5c58f79648b97e948a074e459b9b75a1793cf7d9ca5d7ab27cf7035ece0612d348a23c0fed509c5e18d19b1e659af237c3b9aba4fa8477de805c5f8ccd0cbf3846b6ee1bc9ef76a190952115bd08a5108c8bba76d8d762184c122d081c6dc8b4c49a7f0e16ad4cbed86c6818d4f22c03a100c9afe3675a2f354bf1c2cde1f5e5a63b95761e10d27c9482539387e3aeeaadaeab59faaa20cf595d4d8c57509c751446282581ed28cc55736211e6fabb63d0f299e39ac1cd2af1431bfb03f86e5e59691dffad4e275d4611cb2d7d3be3defcb77907c94db86d989a2ca7e19729e3454eef23b0d58bff8203b08f41b40913f2d2dd2e8c98af09e5aaee76030d8201640d78e7bcfc6c1171e04cb39a6bd060ca41ebbfd090883d8b3569c39fc19cb5d87c15062c9f09138d4e3d3f3421227fb2ac48b224438b12702cb67e2db161a3c771d866c3cc55d15a094f72fe314092e846256e44a1dc513b02bbdd976321f470f81f36e719b9acf22179855d36ad0c50dab79da662e9ea7f9685ec0b44817271ffe2b7254ab7f3ddc389847e17edbd33fbf789bcd604ccca0c01c60deca286858b16dfa17c5875916e0159dfd4f0495c08bf6de51365e2175e47325d5ee71c96ea8ce24c4541886e0854bf7dd8a980aea1aba9add0316f3d052a2eea95c02c241523f3274ee62c883c4ac440d7626cdb4f0aba7a4ea686b2778cd7d7be220357de63cce55a3928aab4c200a2cd65b04d831ba0b54dc91cd6ea410359512130d2a0122f3c9752ba6210ea3b115caf891f0a0a7ef210d1988324a9af926cea8487640a473aefb2e3b4b9259ca4da66089d7f7800f87cb2bd068b8c268dfac897b9a2dd1ff4ac2b19a48b7e95a39ebc6afa2dceca7928ed8e43630d673e5c7ba1fb4afbbd40243ed411b6519420e738c24ab183f900872f10248190358636c789b842f156987d0593fa7cb813f5c688652f871aada7cb5a9c2e15ddedac147151b4d5a7bc4b33cecac961a3487984918868515ca73ebc647945fd9044f3c085b184b3f9d333a7b74927fbbe4a0d846744e0fd6bc36f9381f76422633946fe79e64c3fd63e30096ef400df8cd8c884bad1955b82c013c1a190db92699d39217e46d3db284f35b18b782e791d722d12b85c8a26ac98e9dea8356f9d3ca58833aef4ffd883953f24c96f5351438dccf33693230db5d72389905b49d7308cc30b805fa968532a976009a527bfce9ea921ff4ea9723be5b5972ace8553441a4dac7f0b2114edd3a25666d70c4f94131a63f4521dbd004309157bb32f9fc649058ffbe747bc3addc523f805f1b34787b0f446c9ed1d1966550c7d0c10e342316c6b34899064d0d2dcbb09087ac20572103ee01193a3eab06c06e3206cd60bdbe367af81dee5ab3e5dde9836c558e54c9bb6aa306a609225cf25a65b575fa97d9c962b72b798e9a7fd8192ba879964cedf623d544c8929af5c8dea56721d25578434e2b234289895c697c9c1bc4556e4f6df479a837d1e9132c011e47f9e23fd27b70e7601fdd24f28937efb9e46673b9f56914638c793f5c3b625664f2b221afb3fce5aee92a84d45bab5cda58c49777f82b2b1c8293d727fec90dd73581b087367add474dc7b4cad75cea1e43619ef3fa1b35175f5f0889c031c2083e764b0f4389fffeb307831b73763e73d2c3112adff579d4dcfa1c09d3f2c5927568a70027242e6bec83c5e2cf7e125d8b5e4ad2ec339fb79bb15b8b9a6db0ea9408fc6fb8ca6efe9ae0c8c25900d859b17fc44c4a262c7a5e06ae9e2083fc6dc36bd08d648e9a1a3d8fcbedf12777d690ff15dc7096e7c8b33e71b19005c9e1b20d2c2b6f5c7c1204edc691b389b6ad04f896ed297922bb92b9e6d10a2df2a83dc71c15d2010b595c72d5677017d6d7938ca3538d671e13b8496583b4f9fa59fd481f1f438f92b01a6c5f7169d44b93c0b6863c1a183e871e7f50e26e6d41243a1c509d423309dc886dbb9ac245263ae9d6024456e72b57e17cb08ef00f4fa4dd9fd27de0685c4c6c680ad654e3d81dbb450f0a5e7821412d442c2034093e3fb10234e6a51b98fd388eafd0eec66b42c275a3547f72c7f3d16ed81395e9a2664faacdf99bb22327280e518e4ff047451e6f7420b562c68877c96e129d0cbe18896aff48d49da028dc97aa0108da9b29c540c5238d676dccafdf463694aea34ad4f513b6c7a58d071c335ff1313d41b7cdd902904b8c9fbea2ed34878b407ebb8144f603683ce4ce61eb0690a00d492978aac3a0f3010b7479667811c3332c06553c14809c723316c84d084530e93a63bf0b7658f7bf367d29577236e23ab658a685f2612f0216a932a24aa4f70b8d0609aa9ca14e4d91b8ed9fb62864ded646012ef675ea359117c07f528d7dfb742aab9ac892851e97c94f72d5c34d4feebc7f67e09fdc6f633f050833192f15a7acf4f8c8beb3adf3860fb26fee39a416ec362e4b6d9ced09fa57b3d5b7fb7de018e4fd93eb65634c08f6d4f1e2f490c2a8b1be2794a27de0dbecc9949fd1d5eefa0fc6f0033a2bdecfcaa267280b445e92385d2edd4c2b31bdc5d54ddd6cb30b3c370a893c217945d346d1c5b8b98ac754a01afeba6f5526939ccfe9f2432461a99c7b9b44a3983eb65fb064c32f8c72e18b8f6e42e72a1bac21b3cf94526f81089b235794412d1aed20f48324d742d4079e9546f495248cf7f42839852d604598ca2079fe44b125ae9970973b57c156e83fabe6d64c9aaab5c243d1dc71520d45317b913205979fe5bc075b0068d8a5ceb7c8ff9149c763c22b08d35a09feb8156bf7d8eda212a102906e251efcef1ebed894556f18444a0938b4c050f2b873505bdce97cd4fe539a944b94e281292f38850dec9e9f108d3b2d5a83837d114bcb3d6e6511629f310d194328eb05a7b88e7a053e97dd92881c89a1169e7d23a4fa1ebf532eed2579fc4482b9c93da2b5e9619f289f346160996cc61a3f380ea71b25e777af37dce79039cf90a2bf16ddd46733fe9c1cddbe7a42fc5faa7869c96ec463e9817495bc24a23cd9968213927522ddb0d6ba5db92f5736a5723135305a6c083a9bb54da7e43da3ebb07066ad94e597706062118fef17e9e65363f71d8859d30527a495f06bb025c1d26c6fc80e9b140c7108c57ee5583063bd8d2a7efe6a3026a79f2294e09ce980be8ce1a017132ccf48a63eb32454b12506a6099d4e310f07612e77da46aa0caed8fb0446fd6091140db2cb1432bb93cbf681cefae9d849fee6b0d87898d52d31a209ca6f168b6305011e2c9a55fc5ad2237d7c2d06b98e0703ff2a89fc7af8471aecd2a6cc0a4745082db863bc8d46209d51135333a03b328345b86d6cfc23d6d7384fae5d8546f05725ab139e2c25b0dd9b2113b2774391aa058cf90915bc97a94e74ca0ff6785243122f12decdc48aaa8ff27200007f35e928e62269f7f07407802c9a10648a91180d559c5c37cf3f425c9949b9e38ce4c99b71810babe45344d929906776a66fab175e20bc5930f1dc4b5b888301028b6e0f92293e468d0c6b191f0840ed822c036e6257bbd4f0db8e931463826c0be855add67bff5fdc6d4de7347fa07e63d68f4b6876774a39dff1ae927614f8a879f128713e24b263850f1ab3176ed0e9ca9369af947bb8e862e927cf803ea7b53b68eb8c5f87f1cde2399122b7892ccd4071610f0873981ece2ed719bebb0d508037e46b95610d14e9a826549cfedecea1d32074aa439592929873b49d9434f35646adeabc8b52e323ec2dd6d0d6e27b530361fd8bf9e4e3a0a58e3079dc63156a684bd5cde53ba8c9c51da274bd61cdab187a3fc0a84d5005319f05fc7ddbda575f73f3178336413f8ba0b99cbfdd5c350a3a925260284d75fe06371716f951d76078df7cbe6f25beab46b8f4222c74f68822d6747314b688839540d3bb9bd0f45a028e780fe2b5c78e28dbce66680f1e57b68d6088101146aa9f976bad10933e4f5481444a46d40413ae5d00044a29dd3760c712c04771976280f793ac5bf8cc1187976096e4620d646358f207a9166b9d27030721fc00688a0df926e6f4944ba6e78dc862a8e55e3d1a20d2993d8c8410548e9bf1b6efa181daf8bc060bd1af3dbd8853d6d3f54bdd1f6270b20fcf7f90310109b98f6b366a4ebc6f717962e408bf865d0128fc9ed607f848d376ab1c50e66152f74916a28539a762c75387d144bdaf4a0b8b0e7baec532e8d531501674a8727547916fbcb2e45f9c7d41063bcfec3de1b0adee000e555397ab16fb0977a8c3ac1385dfc89eb7db5cceb9109077d36ca9ff5fcf9feed6b985693746a95ba34f7d2875f61ee8606302b6470f8ad17b781daab036e288e5ee083a3a36eb116a34f5ad97e1675181818289f514efe868feeec3b48b1a574b9405668aa536e572f0e2b46fdfccaea5b2f65285f6a9a05c020bf440f5db912c8ac289c67b9d724225eff88366992f08711f35112e66b765872d39b54cdb5c4c0719b2c17dfade7e2f19281e6ae7885708ee8a8f6f90ce79387e6e47b33f15f212c5b386a5aa5f93cb597698dae4b5999ccb4d652a08c41ed27c45d2ecbd112a679374ddd6606ca76ceca9ab08f7f648d248622ddd633dfc121f9470930ae058cfa9455ddbd25a38aaf48f242ab6e0dc895c5b2af0d9ab0c996df526f144cce6297af5f3ac5fa1d159f52e072b827dbd273afcc6e3b8fa1151acaaca5965a4b6cf5b0ea6275da3208159c6bd6d716eb61309eb4ddfe1bbc4ef8d013d477668cb3506ebb4724ccc72affdab79dcdfaaee55a5946b4a3f768dae9fedddedc6c5712296f26c025ed2ee299cd15b1e692c616094f500fc53fcd9838401c0ea6b6ccb883c149a52d875501ec2e647b1d6720a8227e33cbc1f429ef60103f3334e3de2e40ed4a59d811b8cc51a695de25ebc66eca519222dafa22dbca634220097b1d3f9aeddc91d11019d7215629122b4dc6e3211ad842288b581c31e44fa79e1f7855d8fa77e7a224cf571aa3c16b5f4fe5feb16d7d1bdecc543b0e8ff01c677ec6801e87241ddaa02a5c83bbfd1d84c62e269f6ce8a708e693b86d8e5439f129431a4c1c0bc6ad47784c38e1cacf6c523da23f65a76c264b96aabb50aa9e299be6abd1c9d078ac3b2c5f2c3986b5707f143513b4ea91a2052731ef5b48780dd0cc6626a0f0c358454f6eb36df7caee6f8dfb3ea19a0ae79c0d1587140147be3efb2a0da1305d5fe056010c518e3471572d889304c4ce00acc78fed04a4b888d5e7e57d6cb5cf4e5cf1f8782e1b25ad948eb3e443db75af9233aaaf6659adbe0ef33d4b3ba5214b85e656719df2eba42235b2e268f80e3c5971d28957f8e93f5b04a3d5eaa607fd4bb838ae48661bd093342762cfd1ed60b21f04f5b95c3e5426ca6127b04810e2ee25bb56ae81d7840328d8d4f7d1bd341ed58b102d9860806f4a4d117c044f472c85ba422eab084faf8994cfe0a880bc46dc9c1a8c11995610756e2ac50c5fea8ebbcb53dcc76b1944ce364f8878f42310fe0f8cc211c62f627d12b20527dfd84b78c98b1122050cbcbdb70e08010f68294a6a805d3fab97e76cd695f918e73763ac2c3dfe4a8d75db87dc37e2399fd854f3284d29c7bae3d3e31c4375ad9e047f03a5204c2ba93b6025c112ea2c9fcd731e380a8aaa42860c859c2e2cfd333f0bee741e21f78776defea86e862711f0d0bbf64003ee848a8d1a12dd00c024cbee343d1093e653555c033c198401caeb951860392b5b1eed6200828aa310ed466e41d855dc4231464adc2b6b6fd66e03fd42736fb791387efec28b37d0686272a6bb181a621aae7be06866bdc1c4be69e94642c8d3782f5ab7cc8c890699008b52a11b149a517771b93bc2ae597dedaf0237ea8d9674e26fc75c3b468e04e2fc317d03484a75fb274f7ba1617bbb72ec16da1fd4109952d052e9de7c00761736dd17e70db0976692626ccf8bc9e88ad6c25ed88a2f7c2750add4ceb95744f690ee5f2fa423a2b62ae57c1105958bd8e81025c9412fa71f5d1e81bd6cffa01f489fab7e90ab8a3c8aaffc8e3d594beb254c460347196473117ec2a416dea464eaff95da6cec26b5535954901298f11932ebeca52aded139f2d5aa2c24174e2f6c701ce1f4564c60861ce3b9cdac1cfecf071295c5ec581f0f075096fa457373c124b6c8cae3aaf915e4701ad94ec9c01e5ca0552019bd7f107a7d5afab9e4a5e7cc7b4c5416656ad064f4a0f89afbf7c5b884b69a12fbce8aa73a49b2e5c5728c67a7396bb8341afdf52213b2f7f8e84962cccbeaea63a3c7b24881ecdde39cc57b4f211cd57c6f982217758042f61b648496e62b612b7b8bbe1b9f15d237aeac42b54d15166b5c71eb27ccca1fc9e050adc62a267eb82ca2144ba323a73aa11e2fdaa87695c70316754faf7aec44a49b668362b0b35e884019227e7b9a35e8841e64e0009c713d7f3e4a74cc3feaecf4c99b8d0ecd85c8ff89771b63a38e3af990641f28fa7e4ea560577d600f43ccd467d6a347fef04d392d42f8e97659348c68b41299f94db4b713d61868adbd20a4db74f61bd0d1e7846bfc8b8f8bb50bf50c2fbfdaa87328933741aa2b1ca50cb759c1276f1a7930952ed656921f5ce5569ed16b31b2a1b6009c784199ae60ce2e35d573808a195974536f220cd14dd634bd06800435cf1219047f6246c2d9bdea5e489ab4862f0cb0f01439ad2ad1e2042b3f63b8611a87efbe842613c21761de4c79291a8491092c20134252b8e900e5d3cc70e75d32cc41452c5c33b66087213c34f67ae73fd56a183be858f1c3bcd73d814bb9e3f78cd18992b0ea401d8f25c3b60c055df8e6430b62899bc86167d0b5e2bbf16d75bf3f2b94c26542202bbfa0abe99be1a07c78140f42c12f51576007bb5439966a47cadf5c4ea624a75e7a4f01d8733aee57e3497c013de4a33cf54a94acad9b1aad837865a6881db9a725310eed49581d2223f2b0984757bf3fc5122c5dd572ecc781b48fc508122775779d2b2849e11684a585ce844d21352f8d35ea53f0f34d772bd9ca76cc4dc33aa3f2e72418c097614fa5260eaf3c2d724d3599dfa0991a9c0eec9c4d550886c85e1ab2541e9868a36afbe0d9c07c93e44c4c73c66f88e770e5d4e4ac331fafc6870c928fca85756c444c6e8f6cf75865859abf0cfecc8e89b8c806a2e6af7cb752215bec6201eeb41759b27d599931dc2ae75d605b3e387bf263ebfd09ce2154b81479675555ec74ad85150f8eb8c1b3c4f31f6409648f9c1b4678c82e8e2afa9c887f3210afffed160d1634ab0259e1bf5565d8598605a435bd289afbbc12034f67199b67bb0fddb4b9180908c483ae5a8eed16221687e1f524d010ce5db78d1b999069f225479fd6bf0681c7ee95d4665925bc96399989b85284087e67d5a070f2713feb78bcb91bc019f3f19bf3abb7cf36ebb98f09fd64b61e2bddc9ae6335da48ba85b62562726e142bb9d9e5c8f278dbaa0657dfe3e410f03211a072555624d98790aefe8e7b0281ff6af3de79dd5a414632f9d4913a480e9cd6990f94350304f853ba5679a4cb3a647b98bf1eee6cf70f77581a1ff82a9ffd7296e8fd172d37b1b0d1621692cbfeff8de18658f04af5d5be08bce66e5dfec5989b674219f9ceb6a1037c80a8febdfac63d482debd34c3057a677420f0bdd66e2c2b25a9c1d34b76b4a998ad3ee21d1e49f812422c83016c12c201ac2b0f07ddc00638846f215bfa6c575cbfd577178eb0282ade2c459a13386f5dee8a7502321292a7de077f4fd12967b8c8055596e7a43287639843b6ebee58d463fa044562ec2da7f9c2a7f28cce685178eddd3b9fe7b10202997b6b170555a71555cfebd06cba6bb019f8cfac2ec5db3b1d1ca88acef9accf76b6a74600e590a0eba1c839d6a577d3877e7d6d010b04fc58e160ec9733bf200a9e0b24fe8ef32613cf2c7b1515008b8833e34d3967ccbc8bbe30fd1810f23bb153b814392eb37d8917e96260b3cb16895ef13b96d72c81a14b908224571680dd56d04a59a6583a232ec58e8cff16f6428b5e3dd19f362992608aba912b642aac9950777627ffa4eadfe9f31b73c3fbca11d2abb623b732f3d7c296806151257c9f2306dee1c84eb05d586e7a82a8750905716b3e51600250a1e3b4bf274130a1bfa47117cc8b6db3741ba04d977015b8ee250c3ffaf859fdf0372b88fec188830b5870f251889584333547f3436a548801fd3236da2ccb2b504f85ef1d259bc3e00f0ced934a4b297ecce0d668fb3ecb524d3ff4380a7856c7060006de31931d0b26ec1d084e0dce3b9a123741cdc326b441131d777799623c6340410c331c7e8a4a8175d7d250274cc4ffebd5d46d855bf90842888893c348f0a447998e3aaffc81c9b65e3a772eca5c2f0907ee13ab6a2babe99f388755fa3ac9dc79a2ba4ad7a869a876448ed1d4dd6a8c678065cfc90df8470b29c83719bfcbec7c5e3244a665a28593ad42ab84663bccf570a8e8b783565f909b5e6e8cd69ed6f79fc945ce5d845c998f25b9dc118c96dd2c0f592a73497dbd9e050632c8d82656a71460d0ae7f5f38636692a78083b2fffaa517dc2dfe18ae020e6a5562be54ed9046c7129b3a57dcbd1917efb0579fa9a3978690fded8e52e4860db75b2a93c77316a6e84df4965291a7531e2abc0fcc0d0016acc29680baa575cb7be1a03206236310eb5120ab4069e0f8f0cc3f6bd188ca91963eafc2bc66b1a42f8c49359cf3171a72eef94eddd8aab03f770cb2f489aece4e09a85fe6b9790ced5feced19e4cfe6bcafd1a5d99fe56b78f7a14fdea11fd5e331e23191a3f74b32d8ff2740409f346aedf469eb8aca16b43dcc44c400ae3e6d1c4717ae1f18a2f70830aa0c4d5734922374dad8c006ab97e02a4263999ecad0b1e9f24ed0b599467c962932ec610e63c0b3ac845f5d4d10979c92bd884669908696172609e0da039728baa1f0dca8885d5439ca420e87f5c449908b2a5f69b65b60adbf5d74b21eb1f4e0d79558c59b4499c245a9952de8d3a51021f2e77c44e06a489df3b72d28e5d03ddd358ced4f5a1fe057e58b86f9e717cb9001cec6d6665cc0f5b9cf89873e6e7d10355746e99494766c937683684312b630337d1c411f3f2eddc52a8267e19d38ee12c810cc4e33193e26790b13d1847c56282ac86697996daa386b06ec2ceaa97fac9c018baf644622c74546177267b053a82292c1a1cf194909beba3f2670acf1d095b0caed4b8da2fe48c9da3dc61969d938707a62ce9cf55b89ceaa04a9069d38f4e89db794a335933c5b45fe215976e76dc71b7719c2ef29d06d2dbcfce0470007331a221dbce6baa3f418f989d7dd927d343152ee310d084799300e8d3801f9d464d9bbd5687e3203cfb8e589fbab39ad4851b07bd13b29d7f4b767858d13c5937a482207470f673593aa9abe339b3d63b7ea4ad60e51e7f9080381eb07213ad1996ba7bd28f8b44b7ea037e0bf9716f56820f908fd4027249df11aea06df25b3860cb18b68a7df5ed0d14730035291346049e1e5cbdefb30719548fde4f986bd9871a71b5bc7f6e03ea4fcf1c6ddfecb06413832ac27b08d203070acdaf432bafdb288908dfd673caddbfe41af8255ff7106d39db8d003ec1abcc3000bd7fe1daec2624bbe8417f81150f20a8a48324100ef1570a6de7c0a21e16f6991b23016671bc96ee55e99a97a5a0120af8ecb816137d5f40b9e71d56cbecf61569dcd2f850ede77437be06fd85b54d7220b9bcd13e682a8227c7a05a4efc8d258b0331b0f47cf45ec370b491d6b2e4e601e50483480d9437fdf570b6be69b28b964972fac047f8aaecbe567c8ee3d583a46d5b58fa3c361dd3ad73c91727e4d0594f428acfa977206c20995612834497928d507eb62aca1752a8f3048c932b9f0f80f7c627a87f2b50d581961b8739bddfe2afabb1c757f366acd1e639de808409f598755dad254c60b5aefbbdcbad52f72c756e5e4b286a6866af769593f66256fadc939d3d23d1db9096038b40ed224ace023f2e3ea84fb4092c974cb44ffbe489f0ddbdd79e66281ef9c44e81781b849b0d3101c17e54ebf8bd69393b9220c75c7d3c564862ef35d7dfedc855e2ea15a6159c6c2bd01d2c4f3c316ddc43f937cc295fe35365a69ffe68a2a3bfa7eff90c2fe8563f6438117c31ab48cbd5a3ef1c7a03a03a048be4a9fe0de1d6a86feb144731f4e84f1b509db65d35b1b8ec3d0f462392da10694b207ef1d9fa2581b572f9c45012151f039ebed848b3fc211b2b4d6d48266e8bf800e68cb1165cfb17cb14af4fff107e57bc90b9e32006dd090ae12ff39b000c474f77da32549f51d07bb23d233485be9143c55849b5fa241337c050d48d88e4723f7f1032120cb609c584cb10cd777404556df84cd095c4a9668d392cb9a6197ce04e4234d48b47f8deaad83ee95292c9a9e9d42838c12e34046483ebd821284ac349fddb3d89c0e9a85716ca5f2c60569686d3580c6c7bce0a0ec4183fea724ad02763f66f85992fedf49c67a54c8ecc5b47d6e00cfeaf23b2425b795be93d65d92fe0ac761cca8b2feb4fd7a4bd21bc98a7328f178a61aabc2edf843e23ee94c757a457d448f3588b4e39cb14d855c35372c2060966df0e3382afe2d18988ee7676511e43afae09d6e16b50bfd290c1202c5c82520bfadb7b9eff22c2e9d202e7606f23182c08f0d405cfda6e8bf4b222a14a96015602cd77b2e0af5027938348075115b146166990bdccdaefa94626e140f8ea6fe6b51fb38fbf7ec39b89e68174db08d243a5da08a573545993db451bcd7462ba2c308849e6f54fd68eac003dff1971d19a00ae1d326d9db706197ce15397066ca114645ee39bb1a950c068908be503b2cf3ee74048dd92808e07172ba1362b3ad4103953c990e19b4581c54b5a240d90ec56150fdd5d9d1e497090941b541a9fa202d09f2790bd29f53fcf2adeddd4b4ecbff252921feca36cbe51e5185234641c8df314dec556280e408ad6605cb82f9fa5cbec32b2d478e876b4c3bc5019c344ee2f0bc33d26ae3b69e349771a8069f38f879d82e1c68f84d44516db921ca606b6e310e9ef0729b9fc76eaff94d3e44f865a6943eecc5ea1dc097e69e91344f7b287223fdf25ed3512e1fa34b0879ade1a2786571435e71d3fab19a6ba93b5d83e20f05afba10ab48ddee2c6feee813635318ac35bece3a339fb5c2278df5b9a6b7859343ff5530a2dbeda669a47a5eb0efc46c148ab00165563023536cf71f189c6b855ca6aaa056233ba82edf29e82d96c6118a0e6bf37d2ab2945ed1904f1dfd19ede3dfcf257aea6d560e3776159ffc384b3540deb1cc38d1022e530c2d46557a21eeb744ed5c00843f7b6d5953f1ff4770d26dde34c4cfbd308074e0df53264afc5a3a7ab8a57dae296c39bd72b88ad988319ba9e13ea529783d5c926d2f48599720695fd174f8873d0f660f002d8d0ee134271450c12e9dddb641b240795c2c09b958778e16081bc9180442c45fa916de16c83f16c50092eef58a56191bbcd906eb475b97d37b7f5cb00a79a9ad66a636e1052f9dd1e75d02a5af4840dfda7eac68c749bb857675e67b450a484d3e7b13a77fdabff0e97dfb705e5f4f6cf1e95a5f6cc38e099634a020087f868580ce2ec0837525b8c58f08444d7fd4333a589c0356de22568b4fad8766ee3325cbd65843f2c713ecdb44c96411ea871c039915b546ed6fbafbd51805ac48d06c6924d3f7036e1814250f50f27342c8c4ded3e68b6b3f161d46379c1088a7a123f48f0e7cb5a348f472eb155956fe232fd301e64f341041683ce3b25bba7f290a10282a8dba3a2a3da24461a5be148c2241d627889adca5acad981583fac81d0ee4ef77038c1f80db9dfe740720904512691a9c8545a9d173c08c2e8599010c972c2c34287d91ac7803a5700a0d6e29b7774f8f487b70cf8d0ec9474443e2c0c051116b16aef491c3945a65e6ddcd7931a7259e56902a2866b95d3c0bb7a3ea61b1f3b54ae56e6a7366ea895056ea0d1c251cd74f7b82b0d47464826f4aca77434df3d909271a825b57890cd830011981d95229cc0427cdc97758ddbc76d6cc77ba06c92d19daac8bbecbf55535e98bd4754ec06a6e632225c43bc46068baa688636eaba53926ca093a7addcd6a696a902ac35631aa43d9d66f77270cc7bf66140dac239034ba304e1aa0a265131e9fb2b7f079861b0e4cb9c911ce82ef0b685002476baf26401dc8cc444543129f82ac6b103881c596b19d9eba8ed6b230c17914d5c34a0040c18dc54d8c4b637ee683637fc5a82ac1cf12691bb28fc0bbb307fc032ec3d2b06eaec56ed769b5e892816c7350dce89551e87918f67a117c39f256a368586c78c2e9614e9658161511a8dad53afe8cb9eebe67c6596a90eeea1d3d2466a4d77a1129c0a4409b98d8ac0b925c4b2b3500665a3cf4ceb82cb0b6732eea8a796f9b79d2ea49be97066bc1f606d9f1f59f41d2acbb878a0783093fc4ab0ef866ff60a6a1a58d3cee90307f09247b5212f8709856251ff5d8fb77657110bbb3f3aeff07898f049c821a82c11e27b0c176a9feb12de5d08498018f7607156c5065cb56bf9d6867a4495f26a07e0f01312c2ee897b82d8eba0cbc473da402814dba727521cfec6afac2cc59cdd6a75e1f8f40585e5cda51a7434a81ccf4b7de33c663dc174ba973cebc5a56831005d231c719ea34ce42999c471fccfdbdbaf1acd2f9c16f258e32c70511c475ab264173246ebf31459a05ecb4df443066b61a243903e80ff907af17a96d7afd9763df8f8c4fc49775bc805e2dc165bd6f1c4e06688521557ed9ddb6860fbed1e32957bea1174b3a9aa809d7fa6301fbbb6b3774cd856095f14c6378cfe98f05d4f06fae91769165dd0adfc51bf8f57d701ef14a99d608db0a104ea78fe5b13794cb8529afa5352d1dbc8235d96148c8f9c2e29d6e2359a8dbeba56c9376b26f8384c66548979f4d982fe0652cd86bb60e6f2463ec63dcdd5f93d4bfaefe48f8012c63b32ad3c02ec9088896f6a0c8b1097c1ad911ada7a2d6f0d201a28b70752182885464dd688535bdfc045e8dafbe34b20eca00848e757b4a37de219be5a5fe7a4bc5cfaad29ed92e9eda2bed08407e0d0f53caf6b3590210067d8b9ef16f9a8f5612315dfa415f1efc8d7349394143a149480ce3ccd60ccaff0d9a8a797820f41b431ce3afc4adb2e07cde16015087e09e08bd13471dee960db35cbc3b53c187a5bca7ed50017e09b2ae2c837b1f6557753c7f5b004332ffa2b52d8a2269e7cf9cc397c6079aa5add61d7a560a894e71510e104f52a93622e34037b1db70a05bcfc546ea2ec7153e69a8df18fa9eadaae2c1438710477a9a23e0f7092c310c5288e2d39d362a0a33f9e3d8d9792b51a71d9014abcff66ee509baa3dad341b1e4b6c601a2966f77172a4df0f32170f3386a6600b0b63699fe21e26eeb475507e99f666e0ac349b9e23463450f4fa4498356887d9e1c5f7d18ade51e526d27ccae799d6775336ca9ca8e54d707639ecb0618a3c675533494e2435c0b3780a66defddd217d2cc464014bef8a051d8f292abf9e5cafa78c600c21ed3d40ede937b1e162a1e14757d39d77d4fad8711b6b46ae707b82ced0739f9fb6bcd9b557982e89bb3af5f3fb5448ea960f454f4475ee78970acda37501a8825a04cecf3e544651eea8933379da3c3e7de0a875d689003c00d276470fda3b6ed6473cf8094ab91784d1c0f9468379e8e9729dc1032a5ca14378f8147409f13cd6994de961e2245b35c814596087625d3d3267fc0c1e5614a4af94993091ead40bc9e1d3093228b70c188855ae9e914b15aacfd4f83fde83072af92b2cc968c93cac74e15322eaff32a7bbbb982fb725aeb71f34bf16323d9c0a11dbaf3ab676a9cd1dcfc3f8a0c66d1f082f23806133002c50b59d4513dbe3419d5002263287ff47abdba0862341effe669f26b375337170c8e0742113e1063e8141c4aa9eb4970471f3187f581b71e6f7fe2f8043d065620da8a066d112fedeb33525eb1061c0d0fe9fb415bddae8ed2eb5c3ae6aa0549230e436afacaddc389b2c66499d7fdec2090e7e13560ca0a64803554c7cd9cfcc1cb48427cf9ccd954bb7446c887e2756db2882ff12eaa64efae3a24b35d1d0402922efe90319510495420301d3360f4486d3f87e3dc4f9337bf3fb4e3c6a82850a840153a1936e7cf74086757b72a8db19d33a62a29f3dd4fdef454d9222031aa0958af21851b66aebc09a5c08efd204f3ff18cb1055e8181d6630309fcc91c0d6daef19e618a3ee23e817a586d02364710cfab0b9f2cf18502a34e67d112f1730d44ccae54dc221d7f3877bb828e7109878109f8e95e2e1407df4e588801d25d9c2a1c501e74890631e9a92d823ebbe6b5635488f7d48788ef77658e3bbaf287536b37d3a7ab1ec1749656f2ebfe562765e71dd3e1b895d9b5c315fcf2b3a063c57e74ad1e7586b293ede4c77732f38d316c14210a121153fc50007f78ed64a8e207e9d04b312ae7f97a946c74d2a1181b67e845c3ac6e340b2428c8a5546679707fded3406fc221900b118a3279e13b74926c793e27fc4cc32ae478b4421d6eef75d3a273ff61d0e95b4981e8dd57e16bb00e09bfbbc2ce60cd844a9abb839b8b671fabddfd6e86a30c0a24e73c3c17770f34641951e5dc73ca11d8f8419a7407d483e0f5f1714df0a1775574b5500e8a5a28c655dbc28d7a1ca4b83fd4ebcc7ef2e4994c97c87659681acebe7417328c8612e8570e7ade7ead7f4fc711c9c539362779e6be525bdf5ec037f670b5235c06a1acd89b4ffc21668a7269cc73bf6d1399852eebb8b1dde8ef072e8d80832ba32c8e9480da2c4f5c3209c557f31beef41c00d22ee7c7e2c1bf9952ba8a03c1afae9b4aa63135d2b131f2b2804afcdcc762e1bcec8c8151f471572888933ce97dd787121ced446aa9718bf3766bb6d8a752692c59489d5b565e1693aa0f67b352f915808e415cba13a9864bbd33ebc97dfdc0d357d6769f2f545cc6529c0f634da901ae63bfcbab0a3896bc43faed6a6c23bb4e92f3d669d2e0ff485287cce322b98d02866f026cc556ec8aba6608ac2b5dbc29e104ef2e28d7b51ce63110025bdbfc5d44e8aa7a04ecece07b9860618a162e7289e8d672bb9b15b6ffc87f738b0c7a2b733c5794afe58b1beee4b6780ed453bf2ef2b584dcf32bf732c98fe359abced05fc115e531b088c61b0d5d5058af10120581d7db192e13a5b7b17874f000343aecc8d5005b91b13720bc831de5f1de5e3ddce27ba05213cd126a7cda0afa9745f498200269a5736f63b0faec36bbd646a868100c17cb7f6639f2f14b6c52198fab04c1645bed8763799acf8fef62b82fda1825a3379c000255002788d686695b4c17be3931e69db8980d0216024e9b7b0588cdf8c8102d11f55f971b3163c392cfaa796e0b85dd0bbacd6ca50b3ab80c2e90fa0c18d3526e05b2a46c2eab823c0511b43c71122d533e27ee6d6e34706fc411c67a3b87440a3429df3009996743ed3e4dc244fac98a789f17818a926a0aae81ecde260982b80acc299f57a570a86ee28d0414edc91fb6d5f9a88aeb31bf22270bf3517aefe1140b05be97123cc43df6e8e8e4df96803fdd59715c87afcf0189fb5448663eb35d2c4e5b13dd0233a95f8d6187bf0d5d3ba35adba59e162e877d5a0397d9495ebfc771ae68283be15d883e91b81b1bb0cd8da6c300df7e2bc8a21094cadc974c8270d8ee37fc7e7501a57eaecbc244ed61cfc8d556e38c0611a5269c3b930ee5f37a9771f0c152a5e28df07a104360c973b9a83d3ec5c0aa012bff141842e9b68222647c7d022753dbaae024877f421ff36b3721c26a39b3009683c8c510ba0ba8b5dc1033f9b56e9a43b3141a92599378622a2ca8136f5f1f51cf7b7dce7d043f65f8562b33c4864adc30e7d4c808b10abbbd92f94272b68b063f7d7baf7fd6eb31cc76690042233bc8dee7253f89ce23de7a535af022dae95ac321694d6ce311744d9c152e4424a0a502d221b2e602ada71c60a2f15b7086d75867476b0633063297681fbb0a3e154efe552cdbd9d3203f2e447b60b643b823ea12f504f33f6b6c3bd20e54cf38e3c45c5d472814db60741687894e6cc3c78196d5e722499d202334fb742f14dc2ccb7d114ae0c4cd61ce2ed0cc7fe25a395d6b73c1dfee9174e59d129e7f3c42f93a246d918028d4e2dc804438799 -").unwrap(); - let signature = ledger.sign_transaction(&address, &huge_tx); - println!("Got {:?}", signature); - assert!(signature.is_ok()); - } -} diff --git a/accounts/hw/src/lib.rs b/accounts/hw/src/lib.rs deleted file mode 100644 index a7da8da45e2..00000000000 --- a/accounts/hw/src/lib.rs +++ /dev/null @@ -1,402 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Hardware wallet management. - -#![warn(missing_docs)] -#![warn(warnings)] - -extern crate ethereum_types; -extern crate ethkey; -extern crate hidapi; -extern crate libusb; -extern crate parking_lot; -extern crate protobuf; -extern crate semver; -extern crate trezor_sys; - -#[macro_use] extern crate log; -#[cfg(test)] extern crate rustc_hex; - -mod ledger; -mod trezor; - -use std::sync::{Arc, atomic, atomic::AtomicBool, Weak}; -use std::{fmt, time::Duration}; -use std::thread; - -use ethereum_types::U256; -use ethkey::{Address, Signature}; -use parking_lot::Mutex; - -const HID_GLOBAL_USAGE_PAGE: u16 = 0xFF00; -const HID_USB_DEVICE_CLASS: u8 = 0; -const MAX_POLLING_DURATION: Duration = Duration::from_millis(500); -const USB_EVENT_POLLING_INTERVAL: Duration = Duration::from_millis(500); - -/// `HardwareWallet` device -#[derive(Debug)] -pub struct Device { - path: String, - info: WalletInfo, -} - -/// `Wallet` trait -pub trait Wallet<'a> { - /// Error - type Error; - /// Transaction data format - type Transaction; - - /// Sign transaction data with wallet managing `address`. - fn sign_transaction(&self, address: &Address, transaction: Self::Transaction) -> Result; - - /// Set key derivation path for a chain. - fn set_key_path(&self, key_path: KeyPath); - - /// Re-populate device list - /// Note, this assumes all devices are iterated over and updated - fn update_devices(&self, device_direction: DeviceDirection) -> Result; - - /// Read device info - fn read_device(&self, usb: &hidapi::HidApi, dev_info: &hidapi::HidDeviceInfo) -> Result; - - /// List connected and acknowledged wallets - fn list_devices(&self) -> Vec; - - /// List locked wallets - /// This may be moved if it is the wrong assumption, for example this is not supported by Ledger - /// Then this method return a empty vector - fn list_locked_devices(&self) -> Vec; - - /// Get wallet info. - fn get_wallet(&self, address: &Address) -> Option; - - /// Generate ethereum address for a Wallet - fn get_address(&self, device: &hidapi::HidDevice) -> Result, Self::Error>; - - /// Open a device using `device path` - /// Note, f - is a closure that borrows HidResult - /// HidDevice is in turn a type alias for a `c_void function pointer` - /// For further information see: - /// * - /// * - fn open_path(&self, f: F) -> Result - where F: Fn() -> Result; -} - -/// Hardware wallet error. -#[derive(Debug)] -pub enum Error { - /// Ledger device error. - LedgerDevice(ledger::Error), - /// Trezor device error - TrezorDevice(trezor::Error), - /// USB error. - Usb(libusb::Error), - /// HID error - Hid(String), - /// Hardware wallet not found for specified key. - KeyNotFound, -} - -/// This is the transaction info we need to supply to Trezor message. It's more -/// or less a duplicate of `ethcore::transaction::Transaction`, but we can't -/// import ethcore here as that would be a circular dependency. -pub struct TransactionInfo { - /// Nonce - pub nonce: U256, - /// Gas price - pub gas_price: U256, - /// Gas limit - pub gas_limit: U256, - /// Receiver - pub to: Option
, - /// Value - pub value: U256, - /// Data - pub data: Vec, - /// Chain ID - pub chain_id: Option, -} - -/// Hardware wallet information. -#[derive(Debug, Clone)] -pub struct WalletInfo { - /// Wallet device name. - pub name: String, - /// Wallet device manufacturer. - pub manufacturer: String, - /// Wallet device serial number. - pub serial: String, - /// Ethereum address. - pub address: Address, -} - -/// Key derivation paths used on hardware wallets. -#[derive(Debug, Clone, Copy)] -pub enum KeyPath { - /// Ethereum. - Ethereum, - /// Ethereum classic. - EthereumClassic, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match *self { - Error::KeyNotFound => write!(f, "Key not found for given address."), - Error::LedgerDevice(ref e) => write!(f, "{}", e), - Error::TrezorDevice(ref e) => write!(f, "{}", e), - Error::Usb(ref e) => write!(f, "{}", e), - Error::Hid(ref e) => write!(f, "{}", e), - } - } -} - -impl From for Error { - fn from(err: ledger::Error) -> Self { - match err { - ledger::Error::KeyNotFound => Error::KeyNotFound, - _ => Error::LedgerDevice(err), - } - } -} - -impl From for Error { - fn from(err: trezor::Error) -> Self { - match err { - trezor::Error::KeyNotFound => Error::KeyNotFound, - _ => Error::TrezorDevice(err), - } - } -} - -impl From for Error { - fn from(err: libusb::Error) -> Self { - Error::Usb(err) - } -} - -/// Specifies the direction of the `HardwareWallet` i.e, whether it arrived or left -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum DeviceDirection { - /// Device arrived - Arrived, - /// Device left - Left, -} - -impl fmt::Display for DeviceDirection { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - DeviceDirection::Arrived => write!(f, "arrived"), - DeviceDirection::Left => write!(f, "left"), - } - } -} - -/// Hardware wallet management interface. -pub struct HardwareWalletManager { - exiting: Arc, - ledger: Arc, - trezor: Arc, -} - -impl HardwareWalletManager { - /// Hardware wallet constructor - pub fn new() -> Result { - let exiting = Arc::new(AtomicBool::new(false)); - let hidapi = Arc::new(Mutex::new(hidapi::HidApi::new().map_err(|e| Error::Hid(e.to_string().clone()))?)); - let ledger = ledger::Manager::new(hidapi.clone()); - let trezor = trezor::Manager::new(hidapi.clone()); - let usb_context = Arc::new(libusb::Context::new()?); - - let l = ledger.clone(); - let t = trezor.clone(); - let exit = exiting.clone(); - - // Subscribe to all vendor IDs (VIDs) and product IDs (PIDs) - // This means that the `HardwareWalletManager` is responsible to validate the detected device - usb_context.register_callback( - None, None, Some(HID_USB_DEVICE_CLASS), - Box::new(EventHandler::new( - Arc::downgrade(&ledger), - Arc::downgrade(&trezor) - )) - )?; - - // Hardware event subscriber thread - thread::Builder::new() - .name("hw_wallet_manager".to_string()) - .spawn(move || { - if let Err(e) = l.update_devices(DeviceDirection::Arrived) { - debug!(target: "hw", "Ledger couldn't connect at startup, error: {}", e); - } - if let Err(e) = t.update_devices(DeviceDirection::Arrived) { - debug!(target: "hw", "Trezor couldn't connect at startup, error: {}", e); - } - - while !exit.load(atomic::Ordering::Acquire) { - if let Err(e) = usb_context.handle_events(Some(USB_EVENT_POLLING_INTERVAL)) { - debug!(target: "hw", "HardwareWalletManager event handler error: {}", e); - } - } - }) - .ok(); - - Ok(Self { - exiting, - trezor, - ledger, - }) - } - - /// Select key derivation path for a chain. - /// Currently, only one hard-coded keypath is supported - /// It is managed by `ethcore/account_provider` - pub fn set_key_path(&self, key_path: KeyPath) { - self.ledger.set_key_path(key_path); - self.trezor.set_key_path(key_path); - } - - /// List connected wallets. This only returns wallets that are ready to be used. - pub fn list_wallets(&self) -> Vec { - let mut wallets = Vec::new(); - wallets.extend(self.ledger.list_devices()); - wallets.extend(self.trezor.list_devices()); - wallets - } - - /// Return a list of paths to locked hardware wallets - /// This is only applicable to Trezor because Ledger only appears as - /// a device when it is unlocked - pub fn list_locked_wallets(&self) -> Result, Error> { - Ok(self.trezor.list_locked_devices()) - } - - /// Get connected wallet info. - pub fn wallet_info(&self, address: &Address) -> Option { - if let Some(info) = self.ledger.get_wallet(address) { - Some(info) - } else { - self.trezor.get_wallet(address) - } - } - - /// Sign a message with the wallet (only supported by Ledger) - pub fn sign_message(&self, address: &Address, msg: &[u8]) -> Result { - if self.ledger.get_wallet(address).is_some() { - Ok(self.ledger.sign_message(address, msg)?) - } else if self.trezor.get_wallet(address).is_some() { - Err(Error::TrezorDevice(trezor::Error::NoSigningMessage)) - } else { - Err(Error::KeyNotFound) - } - } - - /// Sign transaction data with wallet managing `address`. - pub fn sign_transaction(&self, address: &Address, t_info: &TransactionInfo, encoded_transaction: &[u8]) -> Result { - if self.ledger.get_wallet(address).is_some() { - Ok(self.ledger.sign_transaction(address, encoded_transaction)?) - } else if self.trezor.get_wallet(address).is_some() { - Ok(self.trezor.sign_transaction(address, t_info)?) - } else { - Err(Error::KeyNotFound) - } - } - - /// Send a pin to a device at a certain path to unlock it - /// This is only applicable to Trezor because Ledger only appears as - /// a device when it is unlocked - pub fn pin_matrix_ack(&self, path: &str, pin: &str) -> Result { - self.trezor.pin_matrix_ack(path, pin).map_err(Error::TrezorDevice) - } -} - -impl Drop for HardwareWalletManager { - fn drop(&mut self) { - // Indicate to the USB Hotplug handler that it - // shall terminate but don't wait for it to terminate. - // If it doesn't terminate for some reason USB Hotplug events will be handled - // even if the HardwareWalletManger has been dropped - self.exiting.store(true, atomic::Ordering::Release); - } -} - -/// Hardware wallet event handler -/// -/// Note, that this runs to completion and race-conditions can't occur but it can -/// stop other events for being processed with an infinite loop or similar -struct EventHandler { - ledger: Weak, - trezor: Weak, -} - -impl EventHandler { - /// Trezor event handler constructor - pub fn new(ledger: Weak, trezor: Weak) -> Self { - Self { ledger, trezor } - } - - fn extract_device_info(device: &libusb::Device) -> Result<(u16, u16), Error> { - let desc = device.device_descriptor()?; - Ok((desc.vendor_id(), desc.product_id())) - } -} - -impl libusb::Hotplug for EventHandler { - fn device_arrived(&mut self, device: libusb::Device) { - // Upgrade reference to an Arc - if let (Some(ledger), Some(trezor)) = (self.ledger.upgrade(), self.trezor.upgrade()) { - // Version ID and Product ID are available - if let Ok((vid, pid)) = Self::extract_device_info(&device) { - if trezor::is_valid_trezor(vid, pid) { - if !trezor::try_connect_polling(&trezor, &MAX_POLLING_DURATION, DeviceDirection::Arrived) { - trace!(target: "hw", "Trezor device was detected but connection failed"); - } - } else if ledger::is_valid_ledger(vid, pid) { - if !ledger::try_connect_polling(&ledger, &MAX_POLLING_DURATION, DeviceDirection::Arrived) { - trace!(target: "hw", "Ledger device was detected but connection failed"); - } - } - } - } - } - - fn device_left(&mut self, device: libusb::Device) { - // Upgrade reference to an Arc - if let (Some(ledger), Some(trezor)) = (self.ledger.upgrade(), self.trezor.upgrade()) { - // Version ID and Product ID are available - if let Ok((vid, pid)) = Self::extract_device_info(&device) { - if trezor::is_valid_trezor(vid, pid) { - if !trezor::try_connect_polling(&trezor, &MAX_POLLING_DURATION, DeviceDirection::Left) { - trace!(target: "hw", "Trezor device was detected but disconnection failed"); - } - } else if ledger::is_valid_ledger(vid, pid) { - if !ledger::try_connect_polling(&ledger, &MAX_POLLING_DURATION, DeviceDirection::Left) { - trace!(target: "hw", "Ledger device was detected but disconnection failed"); - } - } - } - } - } -} - -/// Helper to determine if a device is a valid HID -pub fn is_valid_hid_device(usage_page: u16, interface_number: i32) -> bool { - usage_page == HID_GLOBAL_USAGE_PAGE || interface_number == HID_USB_DEVICE_CLASS as i32 -} diff --git a/accounts/hw/src/trezor.rs b/accounts/hw/src/trezor.rs deleted file mode 100644 index b20123ed8b3..00000000000 --- a/accounts/hw/src/trezor.rs +++ /dev/null @@ -1,463 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Trezor hardware wallet module. Supports Trezor v1. -//! See -//! and -//! for protocol details. - -use std::cmp::{min, max}; -use std::sync::Arc; -use std::time::{Duration, Instant}; -use std::fmt; - -use ethereum_types::{U256, H256, Address}; -use ethkey::Signature; -use hidapi; -use libusb; -use parking_lot::{Mutex, RwLock}; -use protobuf::{self, Message, ProtobufEnum}; -use super::{DeviceDirection, WalletInfo, TransactionInfo, KeyPath, Wallet, Device, is_valid_hid_device}; -use trezor_sys::messages::{EthereumAddress, PinMatrixAck, MessageType, EthereumTxRequest, EthereumSignTx, EthereumGetAddress, EthereumTxAck, ButtonAck}; - -/// Trezor v1 vendor ID -const TREZOR_VID: u16 = 0x534c; -/// Trezor product IDs -const TREZOR_PIDS: [u16; 1] = [0x0001]; - -const ETH_DERIVATION_PATH: [u32; 5] = [0x8000_002C, 0x8000_003C, 0x8000_0000, 0, 0]; // m/44'/60'/0'/0/0 -const ETC_DERIVATION_PATH: [u32; 5] = [0x8000_002C, 0x8000_003D, 0x8000_0000, 0, 0]; // m/44'/61'/0'/0/0 - -/// Hardware wallet error. -#[derive(Debug)] -pub enum Error { - /// Ethereum wallet protocol error. - Protocol(&'static str), - /// Hidapi error. - Usb(hidapi::HidError), - /// Libusb error - LibUsb(libusb::Error), - /// Device with request key is not available. - KeyNotFound, - /// Signing has been cancelled by user. - UserCancel, - /// The Message Type given in the trezor RPC call is not something we recognize - BadMessageType, - /// Trying to read from a closed device at the given path - LockedDevice(String), - /// Signing messages are not supported by Trezor - NoSigningMessage, - /// No device arrived - NoDeviceArrived, - /// No device left - NoDeviceLeft, - /// Invalid PID or VID - InvalidDevice, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match *self { - Error::Protocol(ref s) => write!(f, "Trezor protocol error: {}", s), - Error::Usb(ref e) => write!(f, "USB communication error: {}", e), - Error::LibUsb(ref e) => write!(f, "LibUSB communication error: {}", e), - Error::KeyNotFound => write!(f, "Key not found"), - Error::UserCancel => write!(f, "Operation has been cancelled"), - Error::BadMessageType => write!(f, "Bad Message Type in RPC call"), - Error::LockedDevice(ref s) => write!(f, "Device is locked, needs PIN to perform operations: {}", s), - Error::NoSigningMessage=> write!(f, "Signing messages are not supported by Trezor"), - Error::NoDeviceArrived => write!(f, "No device arrived"), - Error::NoDeviceLeft => write!(f, "No device left"), - Error::InvalidDevice => write!(f, "Device with non-supported product ID or vendor ID was detected"), - } - } -} - -impl From for Error { - fn from(err: hidapi::HidError) -> Self { - Error::Usb(err) - } -} - -impl From for Error { - fn from(err: libusb::Error) -> Self { - Error::LibUsb(err) - } -} - -impl From for Error { - fn from(_: protobuf::ProtobufError) -> Self { - Error::Protocol(&"Could not read response from Trezor Device") - } -} - -/// Trezor device manager -pub struct Manager { - usb: Arc>, - devices: RwLock>, - locked_devices: RwLock>, - key_path: RwLock, -} - -/// HID Version used for the Trezor device -enum HidVersion { - V1, - V2, -} - -impl Manager { - /// Create a new instance. - pub fn new(usb: Arc>) -> Arc { - Arc::new(Self { - usb, - devices: RwLock::new(Vec::new()), - locked_devices: RwLock::new(Vec::new()), - key_path: RwLock::new(KeyPath::Ethereum), - }) - } - - pub fn pin_matrix_ack(&self, device_path: &str, pin: &str) -> Result { - let unlocked = { - let usb = self.usb.lock(); - let device = self.open_path(|| usb.open_path(&device_path))?; - let t = MessageType::MessageType_PinMatrixAck; - let mut m = PinMatrixAck::new(); - m.set_pin(pin.to_string()); - self.send_device_message(&device, t, &m)?; - let (resp_type, _) = self.read_device_response(&device)?; - match resp_type { - // Getting an Address back means it's unlocked, this is undocumented behavior - MessageType::MessageType_EthereumAddress => Ok(true), - // Getting anything else means we didn't unlock it - _ => Ok(false), - - } - }; - self.update_devices(DeviceDirection::Arrived)?; - unlocked - } - - fn u256_to_be_vec(&self, val: &U256) -> Vec { - let mut buf = [0_u8; 32]; - val.to_big_endian(&mut buf); - buf.iter().skip_while(|x| **x == 0).cloned().collect() - } - - fn signing_loop(&self, handle: &hidapi::HidDevice, chain_id: &Option, data: &[u8]) -> Result { - let (resp_type, bytes) = self.read_device_response(&handle)?; - match resp_type { - MessageType::MessageType_Cancel => Err(Error::UserCancel), - MessageType::MessageType_ButtonRequest => { - self.send_device_message(handle, MessageType::MessageType_ButtonAck, &ButtonAck::new())?; - // Signing loop goes back to the top and reading blocks - // for up to 5 minutes waiting for response from the device - // if the user doesn't click any button within 5 minutes you - // get a signing error and the device sort of locks up on the signing screen - self.signing_loop(handle, chain_id, data) - } - MessageType::MessageType_EthereumTxRequest => { - let resp: EthereumTxRequest = protobuf::core::parse_from_bytes(&bytes)?; - if resp.has_data_length() { - let mut msg = EthereumTxAck::new(); - let len = resp.get_data_length() as usize; - msg.set_data_chunk(data[..len].to_vec()); - self.send_device_message(handle, MessageType::MessageType_EthereumTxAck, &msg)?; - self.signing_loop(handle, chain_id, &data[len..]) - } else { - let v = resp.get_signature_v(); - let r = H256::from_slice(resp.get_signature_r()); - let s = H256::from_slice(resp.get_signature_s()); - if let Some(c_id) = *chain_id { - // If there is a chain_id supplied, Trezor will return a v - // part of the signature that is already adjusted for EIP-155, - // so v' = v + 2 * chain_id + 35, but code further down the - // pipeline will already do this transformation, so remove it here - let adjustment = 35 + 2 * c_id as u32; - Ok(Signature::from_rsv(&r, &s, (max(v, adjustment) - adjustment) as u8)) - } else { - // If there isn't a chain_id, v will be returned as v + 27 - let adjusted_v = if v < 27 { v } else { v - 27 }; - Ok(Signature::from_rsv(&r, &s, adjusted_v as u8)) - } - } - } - MessageType::MessageType_Failure => Err(Error::Protocol("Last message sent to Trezor failed")), - _ => Err(Error::Protocol("Unexpected response from Trezor device.")), - } - } - - fn send_device_message(&self, device: &hidapi::HidDevice, msg_type: MessageType, msg: &Message) -> Result { - let msg_id = msg_type as u16; - let mut message = msg.write_to_bytes()?; - let msg_size = message.len(); - let mut data = Vec::new(); - let hid_version = self.probe_hid_version(device)?; - // Magic constants - data.push(b'#'); - data.push(b'#'); - // Convert msg_id to BE and split into bytes - data.push(((msg_id >> 8) & 0xFF) as u8); - data.push((msg_id & 0xFF) as u8); - // Convert msg_size to BE and split into bytes - data.push(((msg_size >> 24) & 0xFF) as u8); - data.push(((msg_size >> 16) & 0xFF) as u8); - data.push(((msg_size >> 8) & 0xFF) as u8); - data.push((msg_size & 0xFF) as u8); - data.append(&mut message); - while data.len() % 63 > 0 { - data.push(0); - } - let mut total_written = 0; - for chunk in data.chunks(63) { - let mut padded_chunk = match hid_version { - HidVersion::V1 => vec![b'?'], - HidVersion::V2 => vec![0, b'?'], - }; - padded_chunk.extend_from_slice(&chunk); - total_written += device.write(&padded_chunk)?; - } - Ok(total_written) - } - - fn probe_hid_version(&self, device: &hidapi::HidDevice) -> Result { - let mut buf2 = [0xFF_u8; 65]; - buf2[0] = 0; - buf2[1] = 63; - let mut buf1 = [0xFF_u8; 64]; - buf1[0] = 63; - if device.write(&buf2)? == 65 { - Ok(HidVersion::V2) - } else if device.write(&buf1)? == 64 { - Ok(HidVersion::V1) - } else { - Err(Error::Usb("Unable to determine HID Version")) - } - } - - fn read_device_response(&self, device: &hidapi::HidDevice) -> Result<(MessageType, Vec), Error> { - let protocol_err = Error::Protocol(&"Unexpected wire response from Trezor Device"); - let mut buf = vec![0; 64]; - - let first_chunk = device.read_timeout(&mut buf, 300_000)?; - if first_chunk < 9 || buf[0] != b'?' || buf[1] != b'#' || buf[2] != b'#' { - return Err(protocol_err); - } - let msg_type = MessageType::from_i32(((buf[3] as i32 & 0xFF) << 8) + (buf[4] as i32 & 0xFF)).ok_or(protocol_err)?; - let msg_size = ((buf[5] as u32 & 0xFF) << 24) + ((buf[6] as u32 & 0xFF) << 16) + ((buf[7] as u32 & 0xFF) << 8) + (buf[8] as u32 & 0xFF); - let mut data = Vec::new(); - data.extend_from_slice(&buf[9..]); - while data.len() < (msg_size as usize) { - device.read_timeout(&mut buf, 10_000)?; - data.extend_from_slice(&buf[1..]); - } - Ok((msg_type, data[..msg_size as usize].to_vec())) - } -} - -impl<'a> Wallet<'a> for Manager { - type Error = Error; - type Transaction = &'a TransactionInfo; - - fn sign_transaction(&self, address: &Address, t_info: Self::Transaction) -> - Result { - let usb = self.usb.lock(); - let devices = self.devices.read(); - let device = devices.iter().find(|d| &d.info.address == address).ok_or(Error::KeyNotFound)?; - let handle = self.open_path(|| usb.open_path(&device.path))?; - let msg_type = MessageType::MessageType_EthereumSignTx; - let mut message = EthereumSignTx::new(); - match *self.key_path.read() { - KeyPath::Ethereum => message.set_address_n(ETH_DERIVATION_PATH.to_vec()), - KeyPath::EthereumClassic => message.set_address_n(ETC_DERIVATION_PATH.to_vec()), - } - message.set_nonce(self.u256_to_be_vec(&t_info.nonce)); - message.set_gas_limit(self.u256_to_be_vec(&t_info.gas_limit)); - message.set_gas_price(self.u256_to_be_vec(&t_info.gas_price)); - message.set_value(self.u256_to_be_vec(&t_info.value)); - - if let Some(addr) = t_info.to { - message.set_to(addr.to_vec()) - } - let first_chunk_length = min(t_info.data.len(), 1024); - let chunk = &t_info.data[0..first_chunk_length]; - message.set_data_initial_chunk(chunk.to_vec()); - message.set_data_length(t_info.data.len() as u32); - if let Some(c_id) = t_info.chain_id { - message.set_chain_id(c_id as u32); - } - - self.send_device_message(&handle, msg_type, &message)?; - - self.signing_loop(&handle, &t_info.chain_id, &t_info.data[first_chunk_length..]) - } - - fn set_key_path(&self, key_path: KeyPath) { - *self.key_path.write() = key_path; - } - - fn update_devices(&self, device_direction: DeviceDirection) -> Result { - let mut usb = self.usb.lock(); - usb.refresh_devices(); - let devices = usb.devices(); - let num_prev_devices = self.devices.read().len(); - - let detected_devices = devices.iter() - .filter(|&d| is_valid_trezor(d.vendor_id, d.product_id) && - is_valid_hid_device(d.usage_page, d.interface_number) - ) - .fold(Vec::new(), |mut v, d| { - match self.read_device(&usb, &d) { - Ok(info) => { - trace!(target: "hw", "Found device: {:?}", info); - v.push(info); - } - Err(e) => trace!(target: "hw", "Error reading device info: {}", e), - }; - v - }); - - let num_curr_devices = detected_devices.len(); - *self.devices.write() = detected_devices; - - match device_direction { - DeviceDirection::Arrived => { - if num_curr_devices > num_prev_devices { - Ok(num_curr_devices - num_prev_devices) - } else { - Err(Error::NoDeviceArrived) - } - } - DeviceDirection::Left => { - if num_prev_devices > num_curr_devices { - Ok(num_prev_devices - num_curr_devices) - } else { - Err(Error::NoDeviceLeft) - } - } - } - } - - fn read_device(&self, usb: &hidapi::HidApi, dev_info: &hidapi::HidDeviceInfo) -> Result { - let handle = self.open_path(|| usb.open_path(&dev_info.path))?; - let manufacturer = dev_info.manufacturer_string.clone().unwrap_or_else(|| "Unknown".to_owned()); - let name = dev_info.product_string.clone().unwrap_or_else(|| "Unknown".to_owned()); - let serial = dev_info.serial_number.clone().unwrap_or_else(|| "Unknown".to_owned()); - match self.get_address(&handle) { - Ok(Some(addr)) => { - Ok(Device { - path: dev_info.path.clone(), - info: WalletInfo { - name, - manufacturer, - serial, - address: addr, - }, - }) - } - Ok(None) => Err(Error::LockedDevice(dev_info.path.clone())), - Err(e) => Err(e), - } - } - - fn list_devices(&self) -> Vec { - self.devices.read().iter().map(|d| d.info.clone()).collect() - } - - fn list_locked_devices(&self) -> Vec { - (*self.locked_devices.read()).clone() - } - - fn get_wallet(&self, address: &Address) -> Option { - self.devices.read().iter().find(|d| &d.info.address == address).map(|d| d.info.clone()) - } - - fn get_address(&self, device: &hidapi::HidDevice) -> Result, Error> { - let typ = MessageType::MessageType_EthereumGetAddress; - let mut message = EthereumGetAddress::new(); - match *self.key_path.read() { - KeyPath::Ethereum => message.set_address_n(ETH_DERIVATION_PATH.to_vec()), - KeyPath::EthereumClassic => message.set_address_n(ETC_DERIVATION_PATH.to_vec()), - } - message.set_show_display(false); - self.send_device_message(&device, typ, &message)?; - - let (resp_type, bytes) = self.read_device_response(&device)?; - match resp_type { - MessageType::MessageType_EthereumAddress => { - let response: EthereumAddress = protobuf::core::parse_from_bytes(&bytes)?; - Ok(Some(From::from(response.get_address()))) - } - _ => Ok(None), - } - } - - fn open_path(&self, f: F) -> Result - where F: Fn() -> Result - { - f().map_err(Into::into) - } -} - -/// Poll the device in maximum `max_polling_duration` if it doesn't succeed -pub fn try_connect_polling(trezor: &Manager, duration: &Duration, dir: DeviceDirection) -> bool { - let start_time = Instant::now(); - while start_time.elapsed() <= *duration { - if let Ok(num_devices) = trezor.update_devices(dir) { - trace!(target: "hw", "{} Trezor devices {}", num_devices, dir); - return true - } - } - false -} - -/// Check if the detected device is a Trezor device by checking both the product ID and the vendor ID -pub fn is_valid_trezor(vid: u16, pid: u16) -> bool { - vid == TREZOR_VID && TREZOR_PIDS.contains(&pid) -} - -#[test] -#[ignore] -/// This test can't be run without an actual trezor device connected -/// (and unlocked) attached to the machine that's running the test -fn test_signature() { - use ethereum_types::Address; - use MAX_POLLING_DURATION; - use super::HardwareWalletManager; - - let manager = HardwareWalletManager::new().unwrap(); - - assert_eq!(try_connect_polling(&manager.trezor, &MAX_POLLING_DURATION, DeviceDirection::Arrived), true); - - let addr: Address = manager.list_wallets() - .iter() - .filter(|d| d.name == "TREZOR".to_string() && d.manufacturer == "SatoshiLabs".to_string()) - .nth(0) - .map(|d| d.address) - .unwrap(); - - let t_info = TransactionInfo { - nonce: U256::from(1), - gas_price: U256::from(100), - gas_limit: U256::from(21_000), - to: Some(Address::from(1337)), - chain_id: Some(1), - value: U256::from(1_000_000), - data: (&[1u8; 3000]).to_vec(), - }; - - let signature = manager.trezor.sign_transaction(&addr, &t_info); - assert!(signature.is_ok()); -} diff --git a/accounts/src/account_data.rs b/accounts/src/account_data.rs index a36d38740e4..5bb41daf268 100644 --- a/accounts/src/account_data.rs +++ b/accounts/src/account_data.rs @@ -1,73 +1,71 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Account Metadata -use std::{ - collections::HashMap, - time::Instant, -}; +use std::{collections::HashMap, time::Instant}; use ethkey::{Address, Password}; -use serde_derive::{Serialize, Deserialize}; +use serde_derive::{Deserialize, Serialize}; use serde_json; /// Type of unlock. #[derive(Clone, PartialEq)] pub enum Unlock { - /// If account is unlocked temporarily, it should be locked after first usage. - OneTime, - /// Account unlocked permanently can always sign message. - /// Use with caution. - Perm, - /// Account unlocked with a timeout - Timed(Instant), + /// If account is unlocked temporarily, it should be locked after first usage. + OneTime, + /// Account unlocked permanently can always sign message. + /// Use with caution. + Perm, + /// Account unlocked with a timeout + Timed(Instant), } /// Data associated with account. #[derive(Clone)] pub struct AccountData { - pub unlock: Unlock, - pub password: Password, + pub unlock: Unlock, + pub password: Password, } /// Collected account metadata #[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct AccountMeta { - /// The name of the account. - pub name: String, - /// The rest of the metadata of the account. - pub meta: String, - /// The 128-bit Uuid of the account, if it has one (brain-wallets don't). - pub uuid: Option, + /// The name of the account. + pub name: String, + /// The rest of the metadata of the account. + pub meta: String, + /// The 128-bit Uuid of the account, if it has one (brain-wallets don't). + pub uuid: Option, } impl AccountMeta { - /// Read a hash map of Address -> AccountMeta - pub fn read(reader: R) -> Result, serde_json::Error> where - R: ::std::io::Read, - { - serde_json::from_reader(reader) - } + /// Read a hash map of Address -> AccountMeta + pub fn read(reader: R) -> Result, serde_json::Error> + where + R: ::std::io::Read, + { + serde_json::from_reader(reader) + } - /// Write a hash map of Address -> AccountMeta - pub fn write(m: &HashMap, writer: &mut W) -> Result<(), serde_json::Error> where - W: ::std::io::Write, - { - serde_json::to_writer(writer, m) - } + /// Write a hash map of Address -> AccountMeta + pub fn write(m: &HashMap, writer: &mut W) -> Result<(), serde_json::Error> + where + W: ::std::io::Write, + { + serde_json::to_writer(writer, m) + } } - diff --git a/accounts/src/error.rs b/accounts/src/error.rs index 2aa3564efd5..a826e0d167b 100644 --- a/accounts/src/error.rs +++ b/accounts/src/error.rs @@ -1,56 +1,46 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. -// This file is part of Parity. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity. If not, see . +// along with OpenEthereum. If not, see . use std::fmt; -use ethstore::{Error as SSError}; -use hardware_wallet::{Error as HardwareError}; +use ethstore::Error as SSError; /// Signing error #[derive(Debug)] pub enum SignError { - /// Account is not unlocked - NotUnlocked, - /// Account does not exist. - NotFound, - /// Low-level hardware device error. - Hardware(HardwareError), - /// Low-level error from store - SStore(SSError), + /// Account is not unlocked + NotUnlocked, + /// Account does not exist. + NotFound, + /// Low-level error from store + SStore(SSError), } impl fmt::Display for SignError { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match *self { - SignError::NotUnlocked => write!(f, "Account is locked"), - SignError::NotFound => write!(f, "Account does not exist"), - SignError::Hardware(ref e) => write!(f, "{}", e), - SignError::SStore(ref e) => write!(f, "{}", e), - } - } -} - -impl From for SignError { - fn from(e: HardwareError) -> Self { - SignError::Hardware(e) - } + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + SignError::NotUnlocked => write!(f, "Account is locked"), + SignError::NotFound => write!(f, "Account does not exist"), + SignError::SStore(ref e) => write!(f, "{}", e), + } + } } impl From for SignError { - fn from(e: SSError) -> Self { - SignError::SStore(e) - } + fn from(e: SSError) -> Self { + SignError::SStore(e) + } } diff --git a/accounts/src/lib.rs b/accounts/src/lib.rs index 0107eadad0d..d7003d56c9f 100644 --- a/accounts/src/lib.rs +++ b/accounts/src/lib.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . #![warn(missing_docs)] @@ -22,730 +22,820 @@ mod account_data; mod error; mod stores; -#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))] -extern crate fake_hardware_wallet as hardware_wallet; - -use self::account_data::{Unlock, AccountData}; -use self::stores::AddressBook; +use self::{ + account_data::{AccountData, Unlock}, + stores::AddressBook, +}; -use std::collections::HashMap; -use std::time::{Instant, Duration}; +use std::{ + collections::HashMap, + time::{Duration, Instant}, +}; -use common_types::transaction::{Action, Transaction}; -use ethkey::{Address, Message, Public, Secret, Password, Random, Generator}; -use ethstore::accounts_dir::MemoryDirectory; +use ethkey::{Address, Generator, Message, Password, Public, Random, Secret}; use ethstore::{ - SimpleSecretStore, SecretStore, EthStore, EthMultiStore, - random_string, SecretVaultRef, StoreAccountRef, OpaqueSecret, + accounts_dir::MemoryDirectory, random_string, EthMultiStore, EthStore, OpaqueSecret, + SecretStore, SecretVaultRef, SimpleSecretStore, StoreAccountRef, }; -use log::{warn, debug}; +use log::*; use parking_lot::RwLock; pub use ethkey::Signature; -pub use ethstore::{Derivation, IndexDerivation, KeyFile, Error}; -pub use hardware_wallet::{Error as HardwareError, HardwareWalletManager, KeyPath, TransactionInfo}; +pub use ethstore::{Derivation, Error, IndexDerivation, KeyFile}; -pub use self::account_data::AccountMeta; -pub use self::error::SignError; +pub use self::{account_data::AccountMeta, error::SignError}; type AccountToken = Password; /// Account management settings. #[derive(Debug, Default)] pub struct AccountProviderSettings { - /// Enable hardware wallet support. - pub enable_hardware_wallets: bool, - /// Use the classic chain key on the hardware wallet. - pub hardware_wallet_classic_key: bool, - /// Store raw account secret when unlocking the account permanently. - pub unlock_keep_secret: bool, - /// Disallowed accounts. - pub blacklisted_accounts: Vec
, + /// Store raw account secret when unlocking the account permanently. + pub unlock_keep_secret: bool, + /// Disallowed accounts. + pub blacklisted_accounts: Vec
, } /// Account management. /// Responsible for unlocking accounts. pub struct AccountProvider { - /// For performance reasons some methods can re-use unlocked secrets. - unlocked_secrets: RwLock>, - /// Unlocked account data. - unlocked: RwLock>, - /// Address book. - address_book: RwLock, - /// Accounts on disk - sstore: Box, - /// Accounts unlocked with rolling tokens - transient_sstore: EthMultiStore, - /// Accounts in hardware wallets. - hardware_store: Option, - /// When unlocking account permanently we additionally keep a raw secret in memory - /// to increase the performance of transaction signing. - unlock_keep_secret: bool, - /// Disallowed accounts. - blacklisted_accounts: Vec
, + /// For performance reasons some methods can re-use unlocked secrets. + unlocked_secrets: RwLock>, + /// Unlocked account data. + unlocked: RwLock>, + /// Address book. + address_book: RwLock, + /// Accounts on disk + sstore: Box, + /// Accounts unlocked with rolling tokens + transient_sstore: EthMultiStore, + /// When unlocking account permanently we additionally keep a raw secret in memory + /// to increase the performance of transaction signing. + unlock_keep_secret: bool, + /// Disallowed accounts. + blacklisted_accounts: Vec
, } fn transient_sstore() -> EthMultiStore { - EthMultiStore::open(Box::new(MemoryDirectory::default())).expect("MemoryDirectory load always succeeds; qed") + EthMultiStore::open(Box::new(MemoryDirectory::default())) + .expect("MemoryDirectory load always succeeds; qed") } impl AccountProvider { - /// Creates new account provider. - pub fn new(sstore: Box, settings: AccountProviderSettings) -> Self { - let mut hardware_store = None; - - if settings.enable_hardware_wallets { - match HardwareWalletManager::new() { - Ok(manager) => { - manager.set_key_path(if settings.hardware_wallet_classic_key { KeyPath::EthereumClassic } else { KeyPath::Ethereum }); - hardware_store = Some(manager) - }, - Err(e) => debug!("Error initializing hardware wallets: {}", e), - } - } - - if let Ok(accounts) = sstore.accounts() { - for account in accounts.into_iter().filter(|a| settings.blacklisted_accounts.contains(&a.address)) { - warn!("Local Account {} has a blacklisted (known to be weak) address and will be ignored", + /// Creates new account provider. + pub fn new(sstore: Box, settings: AccountProviderSettings) -> Self { + if let Ok(accounts) = sstore.accounts() { + for account in accounts + .into_iter() + .filter(|a| settings.blacklisted_accounts.contains(&a.address)) + { + warn!("Local Account {} has a blacklisted (known to be weak) address and will be ignored", account.address); - } - } - - // Remove blacklisted accounts from address book. - let mut address_book = AddressBook::new(&sstore.local_path()); - for addr in &settings.blacklisted_accounts { - address_book.remove(*addr); - } - - AccountProvider { - unlocked_secrets: RwLock::new(HashMap::new()), - unlocked: RwLock::new(HashMap::new()), - address_book: RwLock::new(address_book), - sstore: sstore, - transient_sstore: transient_sstore(), - hardware_store: hardware_store, - unlock_keep_secret: settings.unlock_keep_secret, - blacklisted_accounts: settings.blacklisted_accounts, - } - } - - /// Creates not disk backed provider. - pub fn transient_provider() -> Self { - AccountProvider { - unlocked_secrets: RwLock::new(HashMap::new()), - unlocked: RwLock::new(HashMap::new()), - address_book: RwLock::new(AddressBook::transient()), - sstore: Box::new(EthStore::open(Box::new(MemoryDirectory::default())).expect("MemoryDirectory load always succeeds; qed")), - transient_sstore: transient_sstore(), - hardware_store: None, - unlock_keep_secret: false, - blacklisted_accounts: vec![], - } - } - - /// Creates new random account. - pub fn new_account(&self, password: &Password) -> Result { - self.new_account_and_public(password).map(|d| d.0) - } - - /// Creates new random account and returns address and public key - pub fn new_account_and_public(&self, password: &Password) -> Result<(Address, Public), Error> { - let acc = Random.generate().expect("secp context has generation capabilities; qed"); - let public = acc.public().clone(); - let secret = acc.secret().clone(); - let account = self.sstore.insert_account(SecretVaultRef::Root, secret, password)?; - Ok((account.address, public)) - } - - /// Inserts new account into underlying store. - /// Does not unlock account! - pub fn insert_account(&self, secret: Secret, password: &Password) -> Result { - let account = self.sstore.insert_account(SecretVaultRef::Root, secret, password)?; - if self.blacklisted_accounts.contains(&account.address) { - self.sstore.remove_account(&account, password)?; - return Err(Error::InvalidAccount.into()); - } - Ok(account.address) - } - - /// Generates new derived account based on the existing one - /// If password is not provided, account must be unlocked - /// New account will be created with the same password (if save: true) - pub fn derive_account(&self, address: &Address, password: Option, derivation: Derivation, save: bool) - -> Result - { - let account = self.sstore.account_ref(&address)?; - let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?; - Ok( - if save { self.sstore.insert_derived(SecretVaultRef::Root, &account, &password, derivation)?.address } - else { self.sstore.generate_derived(&account, &password, derivation)? } - ) - } - - /// Import a new presale wallet. - pub fn import_presale(&self, presale_json: &[u8], password: &Password) -> Result { - let account = self.sstore.import_presale(SecretVaultRef::Root, presale_json, password)?; - Ok(Address::from(account.address).into()) - } - - /// Import a new wallet. - pub fn import_wallet(&self, json: &[u8], password: &Password, gen_id: bool) -> Result { - let account = self.sstore.import_wallet(SecretVaultRef::Root, json, password, gen_id)?; - if self.blacklisted_accounts.contains(&account.address) { - self.sstore.remove_account(&account, password)?; - return Err(Error::InvalidAccount.into()); - } - Ok(Address::from(account.address).into()) - } - - /// Checks whether an account with a given address is present. - pub fn has_account(&self, address: Address) -> bool { - self.sstore.account_ref(&address).is_ok() && !self.blacklisted_accounts.contains(&address) - } - - /// Returns addresses of all accounts. - pub fn accounts(&self) -> Result, Error> { - let accounts = self.sstore.accounts()?; - Ok(accounts - .into_iter() - .map(|a| a.address) - .filter(|address| !self.blacklisted_accounts.contains(address)) - .collect() - ) - } - - /// Returns the address of default account. - pub fn default_account(&self) -> Result { - Ok(self.accounts()?.first().cloned().unwrap_or_default()) - } - - /// Returns addresses of hardware accounts. - pub fn hardware_accounts(&self) -> Result, Error> { - if let Some(accounts) = self.hardware_store.as_ref().map(|h| h.list_wallets()) { - if !accounts.is_empty() { - return Ok(accounts.into_iter().map(|a| a.address).collect()); - } - } - Err(Error::Custom("No hardware wallet accounts were found".into())) - } - - /// Get a list of paths to locked hardware wallets - pub fn locked_hardware_accounts(&self) -> Result, SignError> { - match self.hardware_store.as_ref().map(|h| h.list_locked_wallets()) { - None => Err(SignError::NotFound), - Some(Err(e)) => Err(SignError::Hardware(e)), - Some(Ok(s)) => Ok(s), - } - } - - /// Provide a pin to a locked hardware wallet on USB path to unlock it - pub fn hardware_pin_matrix_ack(&self, path: &str, pin: &str) -> Result { - match self.hardware_store.as_ref().map(|h| h.pin_matrix_ack(path, pin)) { - None => Err(SignError::NotFound), - Some(Err(e)) => Err(SignError::Hardware(e)), - Some(Ok(s)) => Ok(s), - } - } - - /// Returns each address along with metadata. - pub fn addresses_info(&self) -> HashMap { - self.address_book.read().get() - } - - /// Returns each address along with metadata. - pub fn set_address_name(&self, account: Address, name: String) { - self.address_book.write().set_name(account, name) - } - - /// Returns each address along with metadata. - pub fn set_address_meta(&self, account: Address, meta: String) { - self.address_book.write().set_meta(account, meta) - } - - /// Removes and address from the address book - pub fn remove_address(&self, addr: Address) { - self.address_book.write().remove(addr) - } - - /// Returns each account along with name and meta. - pub fn accounts_info(&self) -> Result, Error> { - let r = self.sstore.accounts()? - .into_iter() - .filter(|a| !self.blacklisted_accounts.contains(&a.address)) - .map(|a| (a.address.clone(), self.account_meta(a.address).ok().unwrap_or_default())) - .collect(); - Ok(r) - } - - /// Returns each hardware account along with name and meta. - pub fn hardware_accounts_info(&self) -> Result, Error> { - let r = self.hardware_accounts()? - .into_iter() - .map(|address| (address.clone(), self.account_meta(address).ok().unwrap_or_default())) - .collect(); - Ok(r) - } - - /// Returns each hardware account along with name and meta. - pub fn is_hardware_address(&self, address: &Address) -> bool { - self.hardware_store.as_ref().and_then(|s| s.wallet_info(address)).is_some() - } - - /// Returns each account along with name and meta. - pub fn account_meta(&self, address: Address) -> Result { - if let Some(info) = self.hardware_store.as_ref().and_then(|s| s.wallet_info(&address)) { - Ok(AccountMeta { - name: info.name, - meta: info.manufacturer, - uuid: None, - }) - } else { - let account = self.sstore.account_ref(&address)?; - Ok(AccountMeta { - name: self.sstore.name(&account)?, - meta: self.sstore.meta(&account)?, - uuid: self.sstore.uuid(&account).ok().map(Into::into), // allowed to not have a Uuid - }) - } - } - - /// Returns account public key. - pub fn account_public(&self, address: Address, password: &Password) -> Result { - self.sstore.public(&self.sstore.account_ref(&address)?, password) - } - - /// Returns each account along with name and meta. - pub fn set_account_name(&self, address: Address, name: String) -> Result<(), Error> { - self.sstore.set_name(&self.sstore.account_ref(&address)?, name)?; - Ok(()) - } - - /// Returns each account along with name and meta. - pub fn set_account_meta(&self, address: Address, meta: String) -> Result<(), Error> { - self.sstore.set_meta(&self.sstore.account_ref(&address)?, meta)?; - Ok(()) - } - - /// Returns `true` if the password for `account` is `password`. `false` if not. - pub fn test_password(&self, address: &Address, password: &Password) -> Result { - self.sstore.test_password(&self.sstore.account_ref(&address)?, password) - .map_err(Into::into) - } - - /// Permanently removes an account. - pub fn kill_account(&self, address: &Address, password: &Password) -> Result<(), Error> { - self.sstore.remove_account(&self.sstore.account_ref(&address)?, &password)?; - Ok(()) - } - - /// Changes the password of `account` from `password` to `new_password`. Fails if incorrect `password` given. - pub fn change_password(&self, address: &Address, password: Password, new_password: Password) -> Result<(), Error> { - self.sstore.change_password(&self.sstore.account_ref(address)?, &password, &new_password) - } - - /// Exports an account for given address. - pub fn export_account(&self, address: &Address, password: Password) -> Result { - self.sstore.export_account(&self.sstore.account_ref(address)?, &password) - } - - /// Helper method used for unlocking accounts. - fn unlock_account(&self, address: Address, password: Password, unlock: Unlock) -> Result<(), Error> { - let account = self.sstore.account_ref(&address)?; - - // check if account is already unlocked permanently, if it is, do nothing - let mut unlocked = self.unlocked.write(); - if let Some(data) = unlocked.get(&account) { - if let Unlock::Perm = data.unlock { - return Ok(()) - } - } - - if self.unlock_keep_secret && unlock == Unlock::Perm { - // verify password and get the secret - let secret = self.sstore.raw_secret(&account, &password)?; - self.unlocked_secrets.write().insert(account.clone(), secret); - } else { - // verify password by signing dump message - // result may be discarded - let _ = self.sstore.sign(&account, &password, &Default::default())?; - } - - let data = AccountData { - unlock: unlock, - password: password, - }; - - unlocked.insert(account, data); - Ok(()) - } - - fn password(&self, account: &StoreAccountRef) -> Result { - let mut unlocked = self.unlocked.write(); - let data = unlocked.get(account).ok_or(SignError::NotUnlocked)?.clone(); - if let Unlock::OneTime = data.unlock { - unlocked.remove(account).expect("data exists: so key must exist: qed"); - } - if let Unlock::Timed(ref end) = data.unlock { - if Instant::now() > *end { - unlocked.remove(account).expect("data exists: so key must exist: qed"); - return Err(SignError::NotUnlocked); - } - } - Ok(data.password) - } - - /// Unlocks account permanently. - pub fn unlock_account_permanently(&self, account: Address, password: Password) -> Result<(), Error> { - self.unlock_account(account, password, Unlock::Perm) - } - - /// Unlocks account temporarily (for one signing). - pub fn unlock_account_temporarily(&self, account: Address, password: Password) -> Result<(), Error> { - self.unlock_account(account, password, Unlock::OneTime) - } - - /// Unlocks account temporarily with a timeout. - pub fn unlock_account_timed(&self, account: Address, password: Password, duration: Duration) -> Result<(), Error> { - self.unlock_account(account, password, Unlock::Timed(Instant::now() + duration)) - } - - /// Checks if given account is unlocked - pub fn is_unlocked(&self, address: &Address) -> bool { - let unlocked = self.unlocked.read(); - let unlocked_secrets = self.unlocked_secrets.read(); - self.sstore.account_ref(address) - .map(|r| unlocked.get(&r).is_some() || unlocked_secrets.get(&r).is_some()) - .unwrap_or(false) - } - - /// Checks if given account is unlocked permanently - pub fn is_unlocked_permanently(&self, address: &Address) -> bool { - let unlocked = self.unlocked.read(); - self.sstore.account_ref(address) - .map(|r| unlocked.get(&r).map_or(false, |account| account.unlock == Unlock::Perm)) - .unwrap_or(false) - } - - /// Signs the message. If password is not provided the account must be unlocked. - pub fn sign(&self, address: Address, password: Option, message: Message) -> Result { - let account = self.sstore.account_ref(&address)?; - match self.unlocked_secrets.read().get(&account) { - Some(secret) => { - Ok(self.sstore.sign_with_secret(&secret, &message)?) - }, - None => { - let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?; - Ok(self.sstore.sign(&account, &password, &message)?) - } - } - } - - /// Signs message using the derived secret. If password is not provided the account must be unlocked. - pub fn sign_derived(&self, address: &Address, password: Option, derivation: Derivation, message: Message) - -> Result - { - let account = self.sstore.account_ref(address)?; - let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?; - Ok(self.sstore.sign_derived(&account, &password, derivation, &message)?) - } - - /// Signs given message with supplied token. Returns a token to use in next signing within this session. - pub fn sign_with_token(&self, address: Address, token: AccountToken, message: Message) -> Result<(Signature, AccountToken), SignError> { - let account = self.sstore.account_ref(&address)?; - let is_std_password = self.sstore.test_password(&account, &token)?; - - let new_token = Password::from(random_string(16)); - let signature = if is_std_password { - // Insert to transient store - self.sstore.copy_account(&self.transient_sstore, SecretVaultRef::Root, &account, &token, &new_token)?; - // sign - self.sstore.sign(&account, &token, &message)? - } else { - // check transient store - self.transient_sstore.change_password(&account, &token, &new_token)?; - // and sign - self.transient_sstore.sign(&account, &new_token, &message)? - }; - - Ok((signature, new_token)) - } - - /// Decrypts a message with given token. Returns a token to use in next operation for this account. - pub fn decrypt_with_token(&self, address: Address, token: AccountToken, shared_mac: &[u8], message: &[u8]) - -> Result<(Vec, AccountToken), SignError> - { - let account = self.sstore.account_ref(&address)?; - let is_std_password = self.sstore.test_password(&account, &token)?; - - let new_token = Password::from(random_string(16)); - let message = if is_std_password { - // Insert to transient store - self.sstore.copy_account(&self.transient_sstore, SecretVaultRef::Root, &account, &token, &new_token)?; - // decrypt - self.sstore.decrypt(&account, &token, shared_mac, message)? - } else { - // check transient store - self.transient_sstore.change_password(&account, &token, &new_token)?; - // and decrypt - self.transient_sstore.decrypt(&account, &token, shared_mac, message)? - }; - - Ok((message, new_token)) - } - - /// Decrypts a message. If password is not provided the account must be unlocked. - pub fn decrypt(&self, address: Address, password: Option, shared_mac: &[u8], message: &[u8]) -> Result, SignError> { - let account = self.sstore.account_ref(&address)?; - let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?; - Ok(self.sstore.decrypt(&account, &password, shared_mac, message)?) - } - - /// Agree on shared key. - pub fn agree(&self, address: Address, password: Option, other_public: &Public) -> Result { - let account = self.sstore.account_ref(&address)?; - let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?; - Ok(self.sstore.agree(&account, &password, other_public)?) - } - - /// Returns the underlying `SecretStore` reference if one exists. - pub fn list_geth_accounts(&self, testnet: bool) -> Vec
{ - self.sstore.list_geth_accounts(testnet).into_iter().map(|a| Address::from(a).into()).collect() - } - - /// Returns the underlying `SecretStore` reference if one exists. - pub fn import_geth_accounts(&self, desired: Vec
, testnet: bool) -> Result, Error> { - self.sstore.import_geth_accounts(SecretVaultRef::Root, desired, testnet) - .map(|a| a.into_iter().map(|a| a.address).collect()) - .map_err(Into::into) - } - - /// Create new vault. - pub fn create_vault(&self, name: &str, password: &Password) -> Result<(), Error> { - self.sstore.create_vault(name, password) - .map_err(Into::into) - } - - /// Open existing vault. - pub fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error> { - self.sstore.open_vault(name, password) - .map_err(Into::into) - } - - /// Close previously opened vault. - pub fn close_vault(&self, name: &str) -> Result<(), Error> { - self.sstore.close_vault(name) - .map_err(Into::into) - } - - /// List all vaults - pub fn list_vaults(&self) -> Result, Error> { - self.sstore.list_vaults() - .map_err(Into::into) - } - - /// List all currently opened vaults - pub fn list_opened_vaults(&self) -> Result, Error> { - self.sstore.list_opened_vaults() - .map_err(Into::into) - } - - /// Change vault password. - pub fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error> { - self.sstore.change_vault_password(name, new_password) - .map_err(Into::into) - } - - /// Change vault of the given address. - pub fn change_vault(&self, address: Address, new_vault: &str) -> Result<(), Error> { - let new_vault_ref = if new_vault.is_empty() { SecretVaultRef::Root } else { SecretVaultRef::Vault(new_vault.to_owned()) }; - let old_account_ref = self.sstore.account_ref(&address)?; - self.sstore.change_account_vault(new_vault_ref, old_account_ref) - .map_err(Into::into) - .map(|_| ()) - } - - /// Get vault metadata string. - pub fn get_vault_meta(&self, name: &str) -> Result { - self.sstore.get_vault_meta(name) - .map_err(Into::into) - } - - /// Set vault metadata string. - pub fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error> { - self.sstore.set_vault_meta(name, meta) - .map_err(Into::into) - } - - /// Sign message with hardware wallet. - pub fn sign_message_with_hardware(&self, address: &Address, message: &[u8]) -> Result { - match self.hardware_store.as_ref().map(|s| s.sign_message(address, message)) { - None | Some(Err(HardwareError::KeyNotFound)) => Err(SignError::NotFound), - Some(Err(e)) => Err(From::from(e)), - Some(Ok(s)) => Ok(s), - } - } - - /// Sign transaction with hardware wallet. - pub fn sign_transaction_with_hardware(&self, address: &Address, transaction: &Transaction, chain_id: Option, rlp_encoded_transaction: &[u8]) -> Result { - let t_info = TransactionInfo { - nonce: transaction.nonce, - gas_price: transaction.gas_price, - gas_limit: transaction.gas, - to: match transaction.action { - Action::Create => None, - Action::Call(ref to) => Some(to.clone()), - }, - value: transaction.value, - data: transaction.data.to_vec(), - chain_id: chain_id, - }; - match self.hardware_store.as_ref().map(|s| s.sign_transaction(&address, &t_info, rlp_encoded_transaction)) { - None | Some(Err(HardwareError::KeyNotFound)) => Err(SignError::NotFound), - Some(Err(e)) => Err(From::from(e)), - Some(Ok(s)) => Ok(s), - } - } + } + } + + // Remove blacklisted accounts from address book. + let mut address_book = AddressBook::new(&sstore.local_path()); + for addr in &settings.blacklisted_accounts { + address_book.remove(*addr); + } + + AccountProvider { + unlocked_secrets: RwLock::new(HashMap::new()), + unlocked: RwLock::new(HashMap::new()), + address_book: RwLock::new(address_book), + sstore: sstore, + transient_sstore: transient_sstore(), + unlock_keep_secret: settings.unlock_keep_secret, + blacklisted_accounts: settings.blacklisted_accounts, + } + } + + /// Creates not disk backed provider. + pub fn transient_provider() -> Self { + AccountProvider { + unlocked_secrets: RwLock::new(HashMap::new()), + unlocked: RwLock::new(HashMap::new()), + address_book: RwLock::new(AddressBook::transient()), + sstore: Box::new( + EthStore::open(Box::new(MemoryDirectory::default())) + .expect("MemoryDirectory load always succeeds; qed"), + ), + transient_sstore: transient_sstore(), + unlock_keep_secret: false, + blacklisted_accounts: vec![], + } + } + + /// Creates new random account. + pub fn new_account(&self, password: &Password) -> Result { + self.new_account_and_public(password).map(|d| d.0) + } + + /// Creates new random account and returns address and public key + pub fn new_account_and_public(&self, password: &Password) -> Result<(Address, Public), Error> { + let acc = Random + .generate() + .expect("secp context has generation capabilities; qed"); + let public = acc.public().clone(); + let secret = acc.secret().clone(); + let account = self + .sstore + .insert_account(SecretVaultRef::Root, secret, password)?; + Ok((account.address, public)) + } + + /// Inserts new account into underlying store. + /// Does not unlock account! + pub fn insert_account(&self, secret: Secret, password: &Password) -> Result { + let account = self + .sstore + .insert_account(SecretVaultRef::Root, secret, password)?; + if self.blacklisted_accounts.contains(&account.address) { + self.sstore.remove_account(&account, password)?; + return Err(Error::InvalidAccount.into()); + } + Ok(account.address) + } + + /// Generates new derived account based on the existing one + /// If password is not provided, account must be unlocked + /// New account will be created with the same password (if save: true) + pub fn derive_account( + &self, + address: &Address, + password: Option, + derivation: Derivation, + save: bool, + ) -> Result { + let account = self.sstore.account_ref(&address)?; + let password = password + .map(Ok) + .unwrap_or_else(|| self.password(&account))?; + Ok(if save { + self.sstore + .insert_derived(SecretVaultRef::Root, &account, &password, derivation)? + .address + } else { + self.sstore + .generate_derived(&account, &password, derivation)? + }) + } + + /// Import a new presale wallet. + pub fn import_presale( + &self, + presale_json: &[u8], + password: &Password, + ) -> Result { + let account = self + .sstore + .import_presale(SecretVaultRef::Root, presale_json, password)?; + Ok(Address::from(account.address).into()) + } + + /// Import a new wallet. + pub fn import_wallet( + &self, + json: &[u8], + password: &Password, + gen_id: bool, + ) -> Result { + let account = self + .sstore + .import_wallet(SecretVaultRef::Root, json, password, gen_id)?; + if self.blacklisted_accounts.contains(&account.address) { + self.sstore.remove_account(&account, password)?; + return Err(Error::InvalidAccount.into()); + } + Ok(Address::from(account.address).into()) + } + + /// Checks whether an account with a given address is present. + pub fn has_account(&self, address: Address) -> bool { + self.sstore.account_ref(&address).is_ok() && !self.blacklisted_accounts.contains(&address) + } + + /// Returns addresses of all accounts. + pub fn accounts(&self) -> Result, Error> { + let accounts = self.sstore.accounts()?; + Ok(accounts + .into_iter() + .map(|a| a.address) + .filter(|address| !self.blacklisted_accounts.contains(address)) + .collect()) + } + + /// Returns the address of default account. + pub fn default_account(&self) -> Result { + Ok(self.accounts()?.first().cloned().unwrap_or_default()) + } + + /// Returns each address along with metadata. + pub fn addresses_info(&self) -> HashMap { + self.address_book.read().get() + } + + /// Returns each address along with metadata. + pub fn set_address_name(&self, account: Address, name: String) { + self.address_book.write().set_name(account, name) + } + + /// Returns each address along with metadata. + pub fn set_address_meta(&self, account: Address, meta: String) { + self.address_book.write().set_meta(account, meta) + } + + /// Removes and address from the address book + pub fn remove_address(&self, addr: Address) { + self.address_book.write().remove(addr) + } + + /// Returns each account along with name and meta. + pub fn accounts_info(&self) -> Result, Error> { + let r = self + .sstore + .accounts()? + .into_iter() + .filter(|a| !self.blacklisted_accounts.contains(&a.address)) + .map(|a| { + ( + a.address.clone(), + self.account_meta(a.address).ok().unwrap_or_default(), + ) + }) + .collect(); + Ok(r) + } + + /// Returns each account along with name and meta. + pub fn account_meta(&self, address: Address) -> Result { + let account = self.sstore.account_ref(&address)?; + Ok(AccountMeta { + name: self.sstore.name(&account)?, + meta: self.sstore.meta(&account)?, + uuid: self.sstore.uuid(&account).ok().map(Into::into), // allowed to not have a Uuid + }) + } + + /// Returns account public key. + pub fn account_public(&self, address: Address, password: &Password) -> Result { + self.sstore + .public(&self.sstore.account_ref(&address)?, password) + } + + /// Returns each account along with name and meta. + pub fn set_account_name(&self, address: Address, name: String) -> Result<(), Error> { + self.sstore + .set_name(&self.sstore.account_ref(&address)?, name)?; + Ok(()) + } + + /// Returns each account along with name and meta. + pub fn set_account_meta(&self, address: Address, meta: String) -> Result<(), Error> { + self.sstore + .set_meta(&self.sstore.account_ref(&address)?, meta)?; + Ok(()) + } + + /// Returns `true` if the password for `account` is `password`. `false` if not. + pub fn test_password(&self, address: &Address, password: &Password) -> Result { + self.sstore + .test_password(&self.sstore.account_ref(&address)?, password) + .map_err(Into::into) + } + + /// Permanently removes an account. + pub fn kill_account(&self, address: &Address, password: &Password) -> Result<(), Error> { + self.sstore + .remove_account(&self.sstore.account_ref(&address)?, &password)?; + Ok(()) + } + + /// Changes the password of `account` from `password` to `new_password`. Fails if incorrect `password` given. + pub fn change_password( + &self, + address: &Address, + password: Password, + new_password: Password, + ) -> Result<(), Error> { + self.sstore + .change_password(&self.sstore.account_ref(address)?, &password, &new_password) + } + + /// Exports an account for given address. + pub fn export_account(&self, address: &Address, password: Password) -> Result { + self.sstore + .export_account(&self.sstore.account_ref(address)?, &password) + } + + /// Helper method used for unlocking accounts. + fn unlock_account( + &self, + address: Address, + password: Password, + unlock: Unlock, + ) -> Result<(), Error> { + let account = self.sstore.account_ref(&address)?; + + // check if account is already unlocked permanently, if it is, do nothing + let mut unlocked = self.unlocked.write(); + if let Some(data) = unlocked.get(&account) { + if let Unlock::Perm = data.unlock { + return Ok(()); + } + } + + if self.unlock_keep_secret && unlock == Unlock::Perm { + // verify password and get the secret + let secret = self.sstore.raw_secret(&account, &password)?; + self.unlocked_secrets + .write() + .insert(account.clone(), secret); + } else { + // verify password by signing dump message + // result may be discarded + let _ = self.sstore.sign(&account, &password, &Default::default())?; + } + + let data = AccountData { + unlock: unlock, + password: password, + }; + + unlocked.insert(account, data); + Ok(()) + } + + fn password(&self, account: &StoreAccountRef) -> Result { + let mut unlocked = self.unlocked.write(); + let data = unlocked.get(account).ok_or(SignError::NotUnlocked)?.clone(); + if let Unlock::OneTime = data.unlock { + unlocked + .remove(account) + .expect("data exists: so key must exist: qed"); + } + if let Unlock::Timed(ref end) = data.unlock { + if Instant::now() > *end { + unlocked + .remove(account) + .expect("data exists: so key must exist: qed"); + return Err(SignError::NotUnlocked); + } + } + Ok(data.password) + } + + /// Unlocks account permanently. + pub fn unlock_account_permanently( + &self, + account: Address, + password: Password, + ) -> Result<(), Error> { + self.unlock_account(account, password, Unlock::Perm) + } + + /// Unlocks account temporarily (for one signing). + pub fn unlock_account_temporarily( + &self, + account: Address, + password: Password, + ) -> Result<(), Error> { + self.unlock_account(account, password, Unlock::OneTime) + } + + /// Unlocks account temporarily with a timeout. + pub fn unlock_account_timed( + &self, + account: Address, + password: Password, + duration: Duration, + ) -> Result<(), Error> { + self.unlock_account(account, password, Unlock::Timed(Instant::now() + duration)) + } + + /// Checks if given account is unlocked + pub fn is_unlocked(&self, address: &Address) -> bool { + let unlocked = self.unlocked.read(); + let unlocked_secrets = self.unlocked_secrets.read(); + self.sstore + .account_ref(address) + .map(|r| unlocked.get(&r).is_some() || unlocked_secrets.get(&r).is_some()) + .unwrap_or(false) + } + + /// Checks if given account is unlocked permanently + pub fn is_unlocked_permanently(&self, address: &Address) -> bool { + let unlocked = self.unlocked.read(); + self.sstore + .account_ref(address) + .map(|r| { + unlocked + .get(&r) + .map_or(false, |account| account.unlock == Unlock::Perm) + }) + .unwrap_or(false) + } + + /// Signs the message. If password is not provided the account must be unlocked. + pub fn sign( + &self, + address: Address, + password: Option, + message: Message, + ) -> Result { + let account = self.sstore.account_ref(&address)?; + match self.unlocked_secrets.read().get(&account) { + Some(secret) => Ok(self.sstore.sign_with_secret(&secret, &message)?), + None => { + let password = password + .map(Ok) + .unwrap_or_else(|| self.password(&account))?; + Ok(self.sstore.sign(&account, &password, &message)?) + } + } + } + + /// Signs message using the derived secret. If password is not provided the account must be unlocked. + pub fn sign_derived( + &self, + address: &Address, + password: Option, + derivation: Derivation, + message: Message, + ) -> Result { + let account = self.sstore.account_ref(address)?; + let password = password + .map(Ok) + .unwrap_or_else(|| self.password(&account))?; + Ok(self + .sstore + .sign_derived(&account, &password, derivation, &message)?) + } + + /// Signs given message with supplied token. Returns a token to use in next signing within this session. + pub fn sign_with_token( + &self, + address: Address, + token: AccountToken, + message: Message, + ) -> Result<(Signature, AccountToken), SignError> { + let account = self.sstore.account_ref(&address)?; + let is_std_password = self.sstore.test_password(&account, &token)?; + + let new_token = Password::from(random_string(16)); + let signature = if is_std_password { + // Insert to transient store + self.sstore.copy_account( + &self.transient_sstore, + SecretVaultRef::Root, + &account, + &token, + &new_token, + )?; + // sign + self.sstore.sign(&account, &token, &message)? + } else { + // check transient store + self.transient_sstore + .change_password(&account, &token, &new_token)?; + // and sign + self.transient_sstore.sign(&account, &new_token, &message)? + }; + + Ok((signature, new_token)) + } + + /// Decrypts a message with given token. Returns a token to use in next operation for this account. + pub fn decrypt_with_token( + &self, + address: Address, + token: AccountToken, + shared_mac: &[u8], + message: &[u8], + ) -> Result<(Vec, AccountToken), SignError> { + let account = self.sstore.account_ref(&address)?; + let is_std_password = self.sstore.test_password(&account, &token)?; + + let new_token = Password::from(random_string(16)); + let message = if is_std_password { + // Insert to transient store + self.sstore.copy_account( + &self.transient_sstore, + SecretVaultRef::Root, + &account, + &token, + &new_token, + )?; + // decrypt + self.sstore.decrypt(&account, &token, shared_mac, message)? + } else { + // check transient store + self.transient_sstore + .change_password(&account, &token, &new_token)?; + // and decrypt + self.transient_sstore + .decrypt(&account, &token, shared_mac, message)? + }; + + Ok((message, new_token)) + } + + /// Decrypts a message. If password is not provided the account must be unlocked. + pub fn decrypt( + &self, + address: Address, + password: Option, + shared_mac: &[u8], + message: &[u8], + ) -> Result, SignError> { + let account = self.sstore.account_ref(&address)?; + let password = password + .map(Ok) + .unwrap_or_else(|| self.password(&account))?; + Ok(self + .sstore + .decrypt(&account, &password, shared_mac, message)?) + } + + /// Agree on shared key. + pub fn agree( + &self, + address: Address, + password: Option, + other_public: &Public, + ) -> Result { + let account = self.sstore.account_ref(&address)?; + let password = password + .map(Ok) + .unwrap_or_else(|| self.password(&account))?; + Ok(self.sstore.agree(&account, &password, other_public)?) + } + + /// Create new vault. + pub fn create_vault(&self, name: &str, password: &Password) -> Result<(), Error> { + self.sstore.create_vault(name, password).map_err(Into::into) + } + + /// Open existing vault. + pub fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error> { + self.sstore.open_vault(name, password).map_err(Into::into) + } + + /// Close previously opened vault. + pub fn close_vault(&self, name: &str) -> Result<(), Error> { + self.sstore.close_vault(name).map_err(Into::into) + } + + /// List all vaults + pub fn list_vaults(&self) -> Result, Error> { + self.sstore.list_vaults().map_err(Into::into) + } + + /// List all currently opened vaults + pub fn list_opened_vaults(&self) -> Result, Error> { + self.sstore.list_opened_vaults().map_err(Into::into) + } + + /// Change vault password. + pub fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error> { + self.sstore + .change_vault_password(name, new_password) + .map_err(Into::into) + } + + /// Change vault of the given address. + pub fn change_vault(&self, address: Address, new_vault: &str) -> Result<(), Error> { + let new_vault_ref = if new_vault.is_empty() { + SecretVaultRef::Root + } else { + SecretVaultRef::Vault(new_vault.to_owned()) + }; + let old_account_ref = self.sstore.account_ref(&address)?; + self.sstore + .change_account_vault(new_vault_ref, old_account_ref) + .map_err(Into::into) + .map(|_| ()) + } + + /// Get vault metadata string. + pub fn get_vault_meta(&self, name: &str) -> Result { + self.sstore.get_vault_meta(name).map_err(Into::into) + } + + /// Set vault metadata string. + pub fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error> { + self.sstore.set_vault_meta(name, meta).map_err(Into::into) + } } #[cfg(test)] mod tests { - use super::{AccountProvider, Unlock}; - use std::time::{Duration, Instant}; - use ethkey::{Generator, Random, Address}; - use ethstore::{StoreAccountRef, Derivation}; - use ethereum_types::H256; - - #[test] - fn unlock_account_temp() { - let kp = Random.generate().unwrap(); - let ap = AccountProvider::transient_provider(); - assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok()); - assert!(ap.unlock_account_temporarily(kp.address(), "test1".into()).is_err()); - assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok()); - assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); - assert!(ap.sign(kp.address(), None, Default::default()).is_err()); - } - - #[test] - fn derived_account_nosave() { - let kp = Random.generate().unwrap(); - let ap = AccountProvider::transient_provider(); - assert!(ap.insert_account(kp.secret().clone(), &"base".into()).is_ok()); - assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok()); - - let derived_addr = ap.derive_account( - &kp.address(), - None, - Derivation::SoftHash(H256::from(999)), - false, - ).expect("Derivation should not fail"); - - assert!(ap.unlock_account_permanently(derived_addr, "base".into()).is_err(), - "There should be an error because account is not supposed to be saved"); - } - - #[test] - fn derived_account_save() { - let kp = Random.generate().unwrap(); - let ap = AccountProvider::transient_provider(); - assert!(ap.insert_account(kp.secret().clone(), &"base".into()).is_ok()); - assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok()); - - let derived_addr = ap.derive_account( - &kp.address(), - None, - Derivation::SoftHash(H256::from(999)), - true, - ).expect("Derivation should not fail"); - - assert!(ap.unlock_account_permanently(derived_addr, "base_wrong".into()).is_err(), - "There should be an error because password is invalid"); - - assert!(ap.unlock_account_permanently(derived_addr, "base".into()).is_ok(), - "Should be ok because account is saved and password is valid"); - } - - #[test] - fn derived_account_sign() { - let kp = Random.generate().unwrap(); - let ap = AccountProvider::transient_provider(); - assert!(ap.insert_account(kp.secret().clone(), &"base".into()).is_ok()); - assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok()); - - let derived_addr = ap.derive_account( - &kp.address(), - None, - Derivation::SoftHash(H256::from(1999)), - true, - ).expect("Derivation should not fail"); - ap.unlock_account_permanently(derived_addr, "base".into()) - .expect("Should be ok because account is saved and password is valid"); - - let msg = Default::default(); - let signed_msg1 = ap.sign(derived_addr, None, msg) - .expect("Signing with existing unlocked account should not fail"); - let signed_msg2 = ap.sign_derived( - &kp.address(), - None, - Derivation::SoftHash(H256::from(1999)), - msg, - ).expect("Derived signing with existing unlocked account should not fail"); - - assert_eq!(signed_msg1, signed_msg2, - "Signed messages should match"); - } - - #[test] - fn unlock_account_perm() { - let kp = Random.generate().unwrap(); - let ap = AccountProvider::transient_provider(); - assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok()); - assert!(ap.unlock_account_permanently(kp.address(), "test1".into()).is_err()); - assert!(ap.unlock_account_permanently(kp.address(), "test".into()).is_ok()); - assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); - assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); - assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok()); - assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); - assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); - } - - #[test] - fn unlock_account_timer() { - let kp = Random.generate().unwrap(); - let ap = AccountProvider::transient_provider(); - assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok()); - assert!(ap.unlock_account_timed(kp.address(), "test1".into(), Duration::from_secs(60)).is_err()); - assert!(ap.unlock_account_timed(kp.address(), "test".into(), Duration::from_secs(60)).is_ok()); - assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); - ap.unlocked.write().get_mut(&StoreAccountRef::root(kp.address())).unwrap().unlock = Unlock::Timed(Instant::now()); - assert!(ap.sign(kp.address(), None, Default::default()).is_err()); - } - - #[test] - fn should_sign_and_return_token() { - // given - let kp = Random.generate().unwrap(); - let ap = AccountProvider::transient_provider(); - assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok()); - - // when - let (_signature, token) = ap.sign_with_token(kp.address(), "test".into(), Default::default()).unwrap(); - - // then - ap.sign_with_token(kp.address(), token.clone(), Default::default()) - .expect("First usage of token should be correct."); - assert!(ap.sign_with_token(kp.address(), token, Default::default()).is_err(), "Second usage of the same token should fail."); - } - - #[test] - fn should_not_return_blacklisted_account() { - // given - let mut ap = AccountProvider::transient_provider(); - let acc = ap.new_account(&"test".into()).unwrap(); - ap.blacklisted_accounts = vec![acc]; - - // then - assert_eq!(ap.accounts_info().unwrap().keys().cloned().collect::>(), vec![]); - assert_eq!(ap.accounts().unwrap(), vec![]); - } + use super::{AccountProvider, Unlock}; + use ethereum_types::H256; + use ethkey::{Address, Generator, Random}; + use ethstore::{Derivation, StoreAccountRef}; + use std::time::{Duration, Instant}; + + #[test] + fn unlock_account_temp() { + let kp = Random.generate().unwrap(); + let ap = AccountProvider::transient_provider(); + assert!(ap + .insert_account(kp.secret().clone(), &"test".into()) + .is_ok()); + assert!(ap + .unlock_account_temporarily(kp.address(), "test1".into()) + .is_err()); + assert!(ap + .unlock_account_temporarily(kp.address(), "test".into()) + .is_ok()); + assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); + assert!(ap.sign(kp.address(), None, Default::default()).is_err()); + } + + #[test] + fn derived_account_nosave() { + let kp = Random.generate().unwrap(); + let ap = AccountProvider::transient_provider(); + assert!(ap + .insert_account(kp.secret().clone(), &"base".into()) + .is_ok()); + assert!(ap + .unlock_account_permanently(kp.address(), "base".into()) + .is_ok()); + + let derived_addr = ap + .derive_account( + &kp.address(), + None, + Derivation::SoftHash(H256::from(999)), + false, + ) + .expect("Derivation should not fail"); + + assert!( + ap.unlock_account_permanently(derived_addr, "base".into()) + .is_err(), + "There should be an error because account is not supposed to be saved" + ); + } + + #[test] + fn derived_account_save() { + let kp = Random.generate().unwrap(); + let ap = AccountProvider::transient_provider(); + assert!(ap + .insert_account(kp.secret().clone(), &"base".into()) + .is_ok()); + assert!(ap + .unlock_account_permanently(kp.address(), "base".into()) + .is_ok()); + + let derived_addr = ap + .derive_account( + &kp.address(), + None, + Derivation::SoftHash(H256::from(999)), + true, + ) + .expect("Derivation should not fail"); + + assert!( + ap.unlock_account_permanently(derived_addr, "base_wrong".into()) + .is_err(), + "There should be an error because password is invalid" + ); + + assert!( + ap.unlock_account_permanently(derived_addr, "base".into()) + .is_ok(), + "Should be ok because account is saved and password is valid" + ); + } + + #[test] + fn derived_account_sign() { + let kp = Random.generate().unwrap(); + let ap = AccountProvider::transient_provider(); + assert!(ap + .insert_account(kp.secret().clone(), &"base".into()) + .is_ok()); + assert!(ap + .unlock_account_permanently(kp.address(), "base".into()) + .is_ok()); + + let derived_addr = ap + .derive_account( + &kp.address(), + None, + Derivation::SoftHash(H256::from(1999)), + true, + ) + .expect("Derivation should not fail"); + ap.unlock_account_permanently(derived_addr, "base".into()) + .expect("Should be ok because account is saved and password is valid"); + + let msg = Default::default(); + let signed_msg1 = ap + .sign(derived_addr, None, msg) + .expect("Signing with existing unlocked account should not fail"); + let signed_msg2 = ap + .sign_derived( + &kp.address(), + None, + Derivation::SoftHash(H256::from(1999)), + msg, + ) + .expect("Derived signing with existing unlocked account should not fail"); + + assert_eq!(signed_msg1, signed_msg2, "Signed messages should match"); + } + + #[test] + fn unlock_account_perm() { + let kp = Random.generate().unwrap(); + let ap = AccountProvider::transient_provider(); + assert!(ap + .insert_account(kp.secret().clone(), &"test".into()) + .is_ok()); + assert!(ap + .unlock_account_permanently(kp.address(), "test1".into()) + .is_err()); + assert!(ap + .unlock_account_permanently(kp.address(), "test".into()) + .is_ok()); + assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); + assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); + assert!(ap + .unlock_account_temporarily(kp.address(), "test".into()) + .is_ok()); + assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); + assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); + } + + #[test] + fn unlock_account_timer() { + let kp = Random.generate().unwrap(); + let ap = AccountProvider::transient_provider(); + assert!(ap + .insert_account(kp.secret().clone(), &"test".into()) + .is_ok()); + assert!(ap + .unlock_account_timed(kp.address(), "test1".into(), Duration::from_secs(60)) + .is_err()); + assert!(ap + .unlock_account_timed(kp.address(), "test".into(), Duration::from_secs(60)) + .is_ok()); + assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); + ap.unlocked + .write() + .get_mut(&StoreAccountRef::root(kp.address())) + .unwrap() + .unlock = Unlock::Timed(Instant::now()); + assert!(ap.sign(kp.address(), None, Default::default()).is_err()); + } + + #[test] + fn should_sign_and_return_token() { + // given + let kp = Random.generate().unwrap(); + let ap = AccountProvider::transient_provider(); + assert!(ap + .insert_account(kp.secret().clone(), &"test".into()) + .is_ok()); + + // when + let (_signature, token) = ap + .sign_with_token(kp.address(), "test".into(), Default::default()) + .unwrap(); + + // then + ap.sign_with_token(kp.address(), token.clone(), Default::default()) + .expect("First usage of token should be correct."); + assert!( + ap.sign_with_token(kp.address(), token, Default::default()) + .is_err(), + "Second usage of the same token should fail." + ); + } + + #[test] + fn should_not_return_blacklisted_account() { + // given + let mut ap = AccountProvider::transient_provider(); + let acc = ap.new_account(&"test".into()).unwrap(); + ap.blacklisted_accounts = vec![acc]; + + // then + assert_eq!( + ap.accounts_info() + .unwrap() + .keys() + .cloned() + .collect::>(), + vec![] + ); + assert_eq!(ap.accounts().unwrap(), vec![]); + } } diff --git a/accounts/src/stores.rs b/accounts/src/stores.rs index baa26cc48bb..63a551be1f3 100644 --- a/accounts/src/stores.rs +++ b/accounts/src/stores.rs @@ -1,24 +1,26 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Address Book Store -use std::{fs, fmt, hash, ops}; -use std::collections::HashMap; -use std::path::{Path, PathBuf}; +use std::{ + collections::HashMap, + fmt, fs, hash, ops, + path::{Path, PathBuf}, +}; use ethkey::Address; use log::{trace, warn}; @@ -27,163 +29,209 @@ use crate::AccountMeta; /// Disk-backed map from Address to String. Uses JSON. pub struct AddressBook { - cache: DiskMap, + cache: DiskMap, } impl AddressBook { - /// Creates new address book at given directory. - pub fn new(path: &Path) -> Self { - let mut r = AddressBook { - cache: DiskMap::new(path, "address_book.json") - }; - r.cache.revert(AccountMeta::read); - r - } - - /// Creates transient address book (no changes are saved to disk). - pub fn transient() -> Self { - AddressBook { - cache: DiskMap::transient() - } - } - - /// Get the address book. - pub fn get(&self) -> HashMap { - self.cache.clone() - } - - fn save(&self) { - self.cache.save(AccountMeta::write) - } - - /// Sets new name for given address. - pub fn set_name(&mut self, a: Address, name: String) { - { - let x = self.cache.entry(a) - .or_insert_with(|| AccountMeta {name: Default::default(), meta: "{}".to_owned(), uuid: None}); - x.name = name; - } - self.save(); - } - - /// Sets new meta for given address. - pub fn set_meta(&mut self, a: Address, meta: String) { - { - let x = self.cache.entry(a) - .or_insert_with(|| AccountMeta {name: "Anonymous".to_owned(), meta: Default::default(), uuid: None}); - x.meta = meta; - } - self.save(); - } - - /// Removes an entry - pub fn remove(&mut self, a: Address) { - self.cache.remove(&a); - self.save(); - } + /// Creates new address book at given directory. + pub fn new(path: &Path) -> Self { + let mut r = AddressBook { + cache: DiskMap::new(path, "address_book.json"), + }; + r.cache.revert(AccountMeta::read); + r + } + + /// Creates transient address book (no changes are saved to disk). + pub fn transient() -> Self { + AddressBook { + cache: DiskMap::transient(), + } + } + + /// Get the address book. + pub fn get(&self) -> HashMap { + self.cache.clone() + } + + fn save(&self) { + self.cache.save(AccountMeta::write) + } + + /// Sets new name for given address. + pub fn set_name(&mut self, a: Address, name: String) { + { + let x = self.cache.entry(a).or_insert_with(|| AccountMeta { + name: Default::default(), + meta: "{}".to_owned(), + uuid: None, + }); + x.name = name; + } + self.save(); + } + + /// Sets new meta for given address. + pub fn set_meta(&mut self, a: Address, meta: String) { + { + let x = self.cache.entry(a).or_insert_with(|| AccountMeta { + name: "Anonymous".to_owned(), + meta: Default::default(), + uuid: None, + }); + x.meta = meta; + } + self.save(); + } + + /// Removes an entry + pub fn remove(&mut self, a: Address) { + self.cache.remove(&a); + self.save(); + } } /// Disk-serializable HashMap #[derive(Debug)] struct DiskMap { - path: PathBuf, - cache: HashMap, - transient: bool, + path: PathBuf, + cache: HashMap, + transient: bool, } impl ops::Deref for DiskMap { - type Target = HashMap; - fn deref(&self) -> &Self::Target { - &self.cache - } + type Target = HashMap; + fn deref(&self) -> &Self::Target { + &self.cache + } } impl ops::DerefMut for DiskMap { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.cache - } + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.cache + } } impl DiskMap { - pub fn new(path: &Path, file_name: &str) -> Self { - let mut path = path.to_owned(); - path.push(file_name); - trace!(target: "diskmap", "path={:?}", path); - DiskMap { - path: path, - cache: HashMap::new(), - transient: false, - } - } - - pub fn transient() -> Self { - let mut map = DiskMap::new(&PathBuf::new(), "diskmap.json".into()); - map.transient = true; - map - } - - fn revert(&mut self, read: F) where - F: Fn(fs::File) -> Result, E>, - E: fmt::Display, - { - if self.transient { return; } - trace!(target: "diskmap", "revert {:?}", self.path); - let _ = fs::File::open(self.path.clone()) - .map_err(|e| trace!(target: "diskmap", "Couldn't open disk map: {}", e)) - .and_then(|f| read(f).map_err(|e| warn!(target: "diskmap", "Couldn't read disk map: {}", e))) - .and_then(|m| { - self.cache = m; - Ok(()) - }); - } - - fn save(&self, write: F) where - F: Fn(&HashMap, &mut fs::File) -> Result<(), E>, - E: fmt::Display, - { - if self.transient { return; } - trace!(target: "diskmap", "save {:?}", self.path); - let _ = fs::File::create(self.path.clone()) - .map_err(|e| warn!(target: "diskmap", "Couldn't open disk map for writing: {}", e)) - .and_then(|mut f| { - write(&self.cache, &mut f).map_err(|e| warn!(target: "diskmap", "Couldn't write to disk map: {}", e)) - }); - } + pub fn new(path: &Path, file_name: &str) -> Self { + let mut path = path.to_owned(); + path.push(file_name); + trace!(target: "diskmap", "path={:?}", path); + DiskMap { + path: path, + cache: HashMap::new(), + transient: false, + } + } + + pub fn transient() -> Self { + let mut map = DiskMap::new(&PathBuf::new(), "diskmap.json".into()); + map.transient = true; + map + } + + fn revert(&mut self, read: F) + where + F: Fn(fs::File) -> Result, E>, + E: fmt::Display, + { + if self.transient { + return; + } + trace!(target: "diskmap", "revert {:?}", self.path); + let _ = fs::File::open(self.path.clone()) + .map_err(|e| trace!(target: "diskmap", "Couldn't open disk map: {}", e)) + .and_then(|f| { + read(f).map_err(|e| warn!(target: "diskmap", "Couldn't read disk map: {}", e)) + }) + .and_then(|m| { + self.cache = m; + Ok(()) + }); + } + + fn save(&self, write: F) + where + F: Fn(&HashMap, &mut fs::File) -> Result<(), E>, + E: fmt::Display, + { + if self.transient { + return; + } + trace!(target: "diskmap", "save {:?}", self.path); + let _ = fs::File::create(self.path.clone()) + .map_err(|e| warn!(target: "diskmap", "Couldn't open disk map for writing: {}", e)) + .and_then(|mut f| { + write(&self.cache, &mut f) + .map_err(|e| warn!(target: "diskmap", "Couldn't write to disk map: {}", e)) + }); + } } #[cfg(test)] mod tests { - use super::AddressBook; - use std::collections::HashMap; - use tempdir::TempDir; - use crate::account_data::AccountMeta; - - #[test] - fn should_save_and_reload_address_book() { - let tempdir = TempDir::new("").unwrap(); - let mut b = AddressBook::new(tempdir.path()); - b.set_name(1.into(), "One".to_owned()); - b.set_meta(1.into(), "{1:1}".to_owned()); - let b = AddressBook::new(tempdir.path()); - assert_eq!(b.get(), vec![ - (1, AccountMeta {name: "One".to_owned(), meta: "{1:1}".to_owned(), uuid: None}) - ].into_iter().map(|(a, b)| (a.into(), b)).collect::>()); - } - - #[test] - fn should_remove_address() { - let tempdir = TempDir::new("").unwrap(); - let mut b = AddressBook::new(tempdir.path()); - - b.set_name(1.into(), "One".to_owned()); - b.set_name(2.into(), "Two".to_owned()); - b.set_name(3.into(), "Three".to_owned()); - b.remove(2.into()); - - let b = AddressBook::new(tempdir.path()); - assert_eq!(b.get(), vec![ - (1, AccountMeta{name: "One".to_owned(), meta: "{}".to_owned(), uuid: None}), - (3, AccountMeta{name: "Three".to_owned(), meta: "{}".to_owned(), uuid: None}), - ].into_iter().map(|(a, b)| (a.into(), b)).collect::>()); - } + use super::AddressBook; + use crate::account_data::AccountMeta; + use std::collections::HashMap; + use tempdir::TempDir; + + #[test] + fn should_save_and_reload_address_book() { + let tempdir = TempDir::new("").unwrap(); + let mut b = AddressBook::new(tempdir.path()); + b.set_name(1.into(), "One".to_owned()); + b.set_meta(1.into(), "{1:1}".to_owned()); + let b = AddressBook::new(tempdir.path()); + assert_eq!( + b.get(), + vec![( + 1, + AccountMeta { + name: "One".to_owned(), + meta: "{1:1}".to_owned(), + uuid: None + } + )] + .into_iter() + .map(|(a, b)| (a.into(), b)) + .collect::>() + ); + } + + #[test] + fn should_remove_address() { + let tempdir = TempDir::new("").unwrap(); + let mut b = AddressBook::new(tempdir.path()); + + b.set_name(1.into(), "One".to_owned()); + b.set_name(2.into(), "Two".to_owned()); + b.set_name(3.into(), "Three".to_owned()); + b.remove(2.into()); + + let b = AddressBook::new(tempdir.path()); + assert_eq!( + b.get(), + vec![ + ( + 1, + AccountMeta { + name: "One".to_owned(), + meta: "{}".to_owned(), + uuid: None + } + ), + ( + 3, + AccountMeta { + name: "Three".to_owned(), + meta: "{}".to_owned(), + uuid: None + } + ), + ] + .into_iter() + .map(|(a, b)| (a.into(), b)) + .collect::>() + ); + } } diff --git a/chainspec/Cargo.toml b/chainspec/Cargo.toml index 8739ca7dcba..c0308edd320 100644 --- a/chainspec/Cargo.toml +++ b/chainspec/Cargo.toml @@ -1,4 +1,5 @@ [package] +description = "Parity Ethereum Chain Specification" name = "chainspec" version = "0.1.0" authors = ["Marek Kotewicz "] diff --git a/chainspec/src/main.rs b/chainspec/src/main.rs index 45490fe7f6a..4928a99b5af 100644 --- a/chainspec/src/main.rs +++ b/chainspec/src/main.rs @@ -1,49 +1,51 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -extern crate serde_json; extern crate ethjson; +extern crate serde_json; -use std::{fs, env, process}; use ethjson::spec::Spec; +use std::{env, fs, process}; fn quit(s: &str) -> ! { - println!("{}", s); - process::exit(1); + println!("{}", s); + process::exit(1); } fn main() { - let mut args = env::args(); - if args.len() != 2 { - quit("You need to specify chainspec.json\n\ + let mut args = env::args(); + if args.len() != 2 { + quit( + "You need to specify chainspec.json\n\ \n\ - ./chainspec "); - } + ./chainspec ", + ); + } - let path = args.nth(1).expect("args.len() == 2; qed"); - let file = match fs::File::open(&path) { - Ok(file) => file, - Err(_) => quit(&format!("{} could not be opened", path)), - }; + let path = args.nth(1).expect("args.len() == 2; qed"); + let file = match fs::File::open(&path) { + Ok(file) => file, + Err(_) => quit(&format!("{} could not be opened", path)), + }; - let spec: Result = serde_json::from_reader(file); + let spec: Result = serde_json::from_reader(file); - if let Err(err) = spec { - quit(&format!("{} {}", path, err.to_string())); - } + if let Err(err) = spec { + quit(&format!("{} {}", path, err.to_string())); + } - println!("{} is valid", path); + println!("{} is valid", path); } diff --git a/cli-signer/Cargo.toml b/cli-signer/Cargo.toml index 11dd06107e2..bc833fca407 100644 --- a/cli-signer/Cargo.toml +++ b/cli-signer/Cargo.toml @@ -1,10 +1,10 @@ [package] -authors = ["Parity "] -description = "Parity Cli Tool" -homepage = "http://parity.io" +description = "OpenEthereum CLI Signer Tool" +homepage = "https://github.com/openethereum/openethereum" license = "GPL-3.0" name = "cli-signer" version = "1.4.0" +authors = ["Parity "] [dependencies] ethereum-types = "0.4" diff --git a/cli-signer/rpc-client/Cargo.toml b/cli-signer/rpc-client/Cargo.toml index 53ec983391e..90b8d73b697 100644 --- a/cli-signer/rpc-client/Cargo.toml +++ b/cli-signer/rpc-client/Cargo.toml @@ -1,10 +1,10 @@ [package] -authors = ["Parity "] -description = "Parity Rpc Client" -homepage = "http://parity.io" +description = "OpenEthereum RPC Client" +homepage = "https://github.com/openethereum/openethereum" license = "GPL-3.0" name = "parity-rpc-client" version = "1.4.0" +authors = ["Parity "] [dependencies] ethereum-types = "0.4" @@ -12,10 +12,10 @@ futures = "0.1" log = "0.4" serde = "1.0" serde_json = "1.0" -url = "1.2.0" +url = "2" matches = "0.1" -parking_lot = "0.7" -jsonrpc-core = "10.0.1" -jsonrpc-ws-server = "10.0.1" +parking_lot = "0.9" +jsonrpc-core = "15.0.0" +jsonrpc-ws-server = "15.0.0" parity-rpc = { path = "../../rpc" } keccak-hash = "0.1" diff --git a/cli-signer/rpc-client/src/client.rs b/cli-signer/rpc-client/src/client.rs index a9ca5e68a0a..30c227bf9ee 100644 --- a/cli-signer/rpc-client/src/client.rs +++ b/cli-signer/rpc-client/src/client.rs @@ -1,347 +1,327 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::fmt::{Debug, Formatter, Error as FmtError}; -use std::io::{BufReader, BufRead}; -use std::sync::Arc; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::collections::BTreeMap; -use std::thread; -use std::time; +// along with OpenEthereum. If not, see . + +use std::{ + collections::BTreeMap, + fmt::{Debug, Error as FmtError, Formatter}, + io::{BufRead, BufReader}, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }, + thread, time, +}; -use std::path::PathBuf; use hash::keccak; use parking_lot::Mutex; +use std::{fs::File, path::PathBuf}; use url::Url; -use std::fs::File; use ws::ws::{ - self, - Request, - Handler, - Sender, - Handshake, - Error as WsError, - ErrorKind as WsErrorKind, - Message, - Result as WsResult, + self, Error as WsError, ErrorKind as WsErrorKind, Handler, Handshake, Message, Request, + Result as WsResult, Sender, }; use serde::de::DeserializeOwned; -use serde_json::{ - self as json, - Value as JsonValue, - Error as JsonError, -}; +use serde_json::{self as json, Error as JsonError, Value as JsonValue}; -use futures::{Canceled, Complete, Future, oneshot, done}; +use futures::{done, oneshot, Canceled, Complete, Future}; -use jsonrpc_core::{Id, Version, Params, Error as JsonRpcError}; -use jsonrpc_core::request::MethodCall; -use jsonrpc_core::response::{Output, Success, Failure}; +use jsonrpc_core::{ + request::MethodCall, + response::{Failure, Output, Success}, + Error as JsonRpcError, Id, Params, Version, +}; use BoxFuture; /// The actual websocket connection handler, passed into the /// event loop of ws-rs struct RpcHandler { - pending: Pending, - // Option is used here as temporary storage until connection - // is setup and the values are moved into the new `Rpc` - complete: Option>>, - auth_code: String, - out: Option, + pending: Pending, + // Option is used here as temporary storage until connection + // is setup and the values are moved into the new `Rpc` + complete: Option>>, + auth_code: String, + out: Option, } impl RpcHandler { - fn new( - out: Sender, - auth_code: String, - complete: Complete> - ) -> Self { - RpcHandler { - out: Some(out), - auth_code: auth_code, - pending: Pending::new(), - complete: Some(complete), - } - } + fn new(out: Sender, auth_code: String, complete: Complete>) -> Self { + RpcHandler { + out: Some(out), + auth_code: auth_code, + pending: Pending::new(), + complete: Some(complete), + } + } } impl Handler for RpcHandler { - fn build_request(&mut self, url: &Url) -> WsResult { - match Request::from_url(url) { - Ok(mut r) => { - let timestamp = time::UNIX_EPOCH.elapsed().map_err(|err| { - WsError::new(WsErrorKind::Internal, format!("{}", err)) - })?; - let secs = timestamp.as_secs(); - let hashed = keccak(format!("{}:{}", self.auth_code, secs)); - let proto = format!("{:x}_{}", hashed, secs); - r.add_protocol(&proto); - Ok(r) - }, - Err(e) => - Err(WsError::new(WsErrorKind::Internal, format!("{}", e))), - } - } - fn on_error(&mut self, err: WsError) { - match self.complete.take() { - Some(c) => match c.send(Err(RpcError::WsError(err))) { - Ok(_) => {}, - Err(_) => warn!(target: "rpc-client", "Unable to notify about error."), - }, - None => warn!(target: "rpc-client", "unexpected error: {}", err), - } - } - fn on_open(&mut self, _: Handshake) -> WsResult<()> { - match (self.complete.take(), self.out.take()) { - (Some(c), Some(out)) => { - let res = c.send(Ok(Rpc { - out: out, - counter: AtomicUsize::new(0), - pending: self.pending.clone(), - })); - if let Err(_) = res { - warn!(target: "rpc-client", "Unable to open a connection.") - } - Ok(()) - }, - _ => { - let msg = format!("on_open called twice"); - Err(WsError::new(WsErrorKind::Internal, msg)) - } - } - } - fn on_message(&mut self, msg: Message) -> WsResult<()> { - let ret: Result; - let response_id; - let string = &msg.to_string(); - match json::from_str::(&string) { - Ok(Output::Success(Success { result, id: Id::Num(id), .. })) => - { - ret = Ok(result); - response_id = id as usize; - } - Ok(Output::Failure(Failure { error, id: Id::Num(id), .. })) => { - ret = Err(error); - response_id = id as usize; - } - Err(e) => { - warn!( - target: "rpc-client", - "recieved invalid message: {}\n {:?}", - string, - e - ); - return Ok(()) - }, - _ => { - warn!( - target: "rpc-client", - "recieved invalid message: {}", - string - ); - return Ok(()) - } - } - - match self.pending.remove(response_id) { - Some(c) => if let Err(_) = c.send(ret.map_err(|err| RpcError::JsonRpc(err))) { - warn!(target: "rpc-client", "Unable to send response.") - }, - None => warn!( - target: "rpc-client", - "warning: unexpected id: {}", - response_id - ), - } - Ok(()) - } + fn build_request(&mut self, url: &Url) -> WsResult { + match Request::from_url(url) { + Ok(mut r) => { + let timestamp = time::UNIX_EPOCH + .elapsed() + .map_err(|err| WsError::new(WsErrorKind::Internal, format!("{}", err)))?; + let secs = timestamp.as_secs(); + let hashed = keccak(format!("{}:{}", self.auth_code, secs)); + let proto = format!("{:x}_{}", hashed, secs); + r.add_protocol(&proto); + Ok(r) + } + Err(e) => Err(WsError::new(WsErrorKind::Internal, format!("{}", e))), + } + } + fn on_error(&mut self, err: WsError) { + match self.complete.take() { + Some(c) => match c.send(Err(RpcError::WsError(err))) { + Ok(_) => {} + Err(_) => warn!(target: "rpc-client", "Unable to notify about error."), + }, + None => warn!(target: "rpc-client", "unexpected error: {}", err), + } + } + fn on_open(&mut self, _: Handshake) -> WsResult<()> { + match (self.complete.take(), self.out.take()) { + (Some(c), Some(out)) => { + let res = c.send(Ok(Rpc { + out: out, + counter: AtomicUsize::new(0), + pending: self.pending.clone(), + })); + if let Err(_) = res { + warn!(target: "rpc-client", "Unable to open a connection.") + } + Ok(()) + } + _ => { + let msg = format!("on_open called twice"); + Err(WsError::new(WsErrorKind::Internal, msg)) + } + } + } + fn on_message(&mut self, msg: Message) -> WsResult<()> { + let ret: Result; + let response_id; + let string = &msg.to_string(); + match json::from_str::(&string) { + Ok(Output::Success(Success { + result, + id: Id::Num(id), + .. + })) => { + ret = Ok(result); + response_id = id as usize; + } + Ok(Output::Failure(Failure { + error, + id: Id::Num(id), + .. + })) => { + ret = Err(error); + response_id = id as usize; + } + Err(e) => { + warn!( + target: "rpc-client", + "recieved invalid message: {}\n {:?}", + string, + e + ); + return Ok(()); + } + _ => { + warn!( + target: "rpc-client", + "recieved invalid message: {}", + string + ); + return Ok(()); + } + } + + match self.pending.remove(response_id) { + Some(c) => { + if let Err(_) = c.send(ret.map_err(|err| RpcError::JsonRpc(err))) { + warn!(target: "rpc-client", "Unable to send response.") + } + } + None => warn!( + target: "rpc-client", + "warning: unexpected id: {}", + response_id + ), + } + Ok(()) + } } /// Keeping track of issued requests to be matched up with responses #[derive(Clone)] -struct Pending( - Arc>>>> -); +struct Pending(Arc>>>>); impl Pending { - fn new() -> Self { - Pending(Arc::new(Mutex::new(BTreeMap::new()))) - } - fn insert(&mut self, k: usize, v: Complete>) { - self.0.lock().insert(k, v); - } - fn remove( - &mut self, - k: usize - ) -> Option>> { - self.0.lock().remove(&k) - } + fn new() -> Self { + Pending(Arc::new(Mutex::new(BTreeMap::new()))) + } + fn insert(&mut self, k: usize, v: Complete>) { + self.0.lock().insert(k, v); + } + fn remove(&mut self, k: usize) -> Option>> { + self.0.lock().remove(&k) + } } fn get_authcode(path: &PathBuf) -> Result { - if let Ok(fd) = File::open(path) { - if let Some(Ok(line)) = BufReader::new(fd).lines().next() { - let mut parts = line.split(';'); - let token = parts.next(); - - if let Some(code) = token { - return Ok(code.into()); - } - } - } - Err(RpcError::NoAuthCode) + if let Ok(fd) = File::open(path) { + if let Some(Ok(line)) = BufReader::new(fd).lines().next() { + let mut parts = line.split(';'); + let token = parts.next(); + + if let Some(code) = token { + return Ok(code.into()); + } + } + } + Err(RpcError::NoAuthCode) } /// The handle to the connection pub struct Rpc { - out: Sender, - counter: AtomicUsize, - pending: Pending, + out: Sender, + counter: AtomicUsize, + pending: Pending, } impl Rpc { - /// Blocking, returns a new initialized connection or RpcError - pub fn new(url: &str, authpath: &PathBuf) -> Result { - let rpc = Self::connect(url, authpath).map(|rpc| rpc).wait()?; - rpc - } - - /// Non-blocking, returns a future - pub fn connect( - url: &str, authpath: &PathBuf - ) -> BoxFuture, Canceled> { - let (c, p) = oneshot::>(); - match get_authcode(authpath) { - Err(e) => return Box::new(done(Ok(Err(e)))), - Ok(code) => { - let url = String::from(url); - // The ws::connect takes a FnMut closure, which means c cannot - // be moved into it, since it's consumed on complete. - // Therefore we wrap it in an option and pick it out once. - let mut once = Some(c); - thread::spawn(move || { - let conn = ws::connect(url, |out| { - // this will panic if the closure is called twice, - // which it should never be. - let c = once.take() - .expect("connection closure called only once"); - RpcHandler::new(out, code.clone(), c) - }); - match conn { - Err(err) => { - // since ws::connect is only called once, it cannot - // both fail and succeed. - let c = once.take() - .expect("connection closure called only once"); - let _ = c.send(Err(RpcError::WsError(err))); - }, - // c will complete on the `on_open` event in the Handler - _ => () - } - }); - Box::new(p) - } - } - } - - /// Non-blocking, returns a future of the request response - pub fn request( - &mut self, method: &'static str, params: Vec - ) -> BoxFuture, Canceled> - where T: DeserializeOwned + Send + Sized { - - let (c, p) = oneshot::>(); - - let id = self.counter.fetch_add(1, Ordering::Relaxed); - self.pending.insert(id, c); - - let request = MethodCall { - jsonrpc: Some(Version::V2), - method: method.to_owned(), - params: Params::Array(params), - id: Id::Num(id as u64), - }; - - let serialized = json::to_string(&request) - .expect("request is serializable"); - let _ = self.out.send(serialized); - - Box::new(p.map(|result| { - match result { - Ok(json) => { - let t: T = json::from_value(json)?; - Ok(t) - }, - Err(err) => Err(err) - } - })) - } + /// Blocking, returns a new initialized connection or RpcError + pub fn new(url: &str, authpath: &PathBuf) -> Result { + let rpc = Self::connect(url, authpath).map(|rpc| rpc).wait()?; + rpc + } + + /// Non-blocking, returns a future + pub fn connect(url: &str, authpath: &PathBuf) -> BoxFuture, Canceled> { + let (c, p) = oneshot::>(); + match get_authcode(authpath) { + Err(e) => return Box::new(done(Ok(Err(e)))), + Ok(code) => { + let url = String::from(url); + // The ws::connect takes a FnMut closure, which means c cannot + // be moved into it, since it's consumed on complete. + // Therefore we wrap it in an option and pick it out once. + let mut once = Some(c); + thread::spawn(move || { + let conn = ws::connect(url, |out| { + // this will panic if the closure is called twice, + // which it should never be. + let c = once.take().expect("connection closure called only once"); + RpcHandler::new(out, code.clone(), c) + }); + match conn { + Err(err) => { + // since ws::connect is only called once, it cannot + // both fail and succeed. + let c = once.take().expect("connection closure called only once"); + let _ = c.send(Err(RpcError::WsError(err))); + } + // c will complete on the `on_open` event in the Handler + _ => (), + } + }); + Box::new(p) + } + } + } + + /// Non-blocking, returns a future of the request response + pub fn request( + &mut self, + method: &'static str, + params: Vec, + ) -> BoxFuture, Canceled> + where + T: DeserializeOwned + Send + Sized, + { + let (c, p) = oneshot::>(); + + let id = self.counter.fetch_add(1, Ordering::Relaxed); + self.pending.insert(id, c); + + let request = MethodCall { + jsonrpc: Some(Version::V2), + method: method.to_owned(), + params: Params::Array(params), + id: Id::Num(id as u64), + }; + + let serialized = json::to_string(&request).expect("request is serializable"); + let _ = self.out.send(serialized); + + Box::new(p.map(|result| match result { + Ok(json) => { + let t: T = json::from_value(json)?; + Ok(t) + } + Err(err) => Err(err), + })) + } } pub enum RpcError { - WrongVersion(String), - ParseError(JsonError), - MalformedResponse(String), - JsonRpc(JsonRpcError), - WsError(WsError), - Canceled(Canceled), - UnexpectedId, - NoAuthCode, + WrongVersion(String), + ParseError(JsonError), + MalformedResponse(String), + JsonRpc(JsonRpcError), + WsError(WsError), + Canceled(Canceled), + UnexpectedId, + NoAuthCode, } impl Debug for RpcError { - fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { - match *self { - RpcError::WrongVersion(ref s) - => write!(f, "Expected version 2.0, got {}", s), - RpcError::ParseError(ref err) - => write!(f, "ParseError: {}", err), - RpcError::MalformedResponse(ref s) - => write!(f, "Malformed response: {}", s), - RpcError::JsonRpc(ref json) - => write!(f, "JsonRpc error: {:?}", json), - RpcError::WsError(ref s) - => write!(f, "Websocket error: {}", s), - RpcError::Canceled(ref s) - => write!(f, "Futures error: {:?}", s), - RpcError::UnexpectedId - => write!(f, "Unexpected response id"), - RpcError::NoAuthCode - => write!(f, "No authcodes available"), - } - } + fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { + match *self { + RpcError::WrongVersion(ref s) => write!(f, "Expected version 2.0, got {}", s), + RpcError::ParseError(ref err) => write!(f, "ParseError: {}", err), + RpcError::MalformedResponse(ref s) => write!(f, "Malformed response: {}", s), + RpcError::JsonRpc(ref json) => write!(f, "JsonRpc error: {:?}", json), + RpcError::WsError(ref s) => write!(f, "Websocket error: {}", s), + RpcError::Canceled(ref s) => write!(f, "Futures error: {:?}", s), + RpcError::UnexpectedId => write!(f, "Unexpected response id"), + RpcError::NoAuthCode => write!(f, "No authcodes available"), + } + } } impl From for RpcError { - fn from(err: JsonError) -> RpcError { - RpcError::ParseError(err) - } + fn from(err: JsonError) -> RpcError { + RpcError::ParseError(err) + } } impl From for RpcError { - fn from(err: WsError) -> RpcError { - RpcError::WsError(err) - } + fn from(err: WsError) -> RpcError { + RpcError::WsError(err) + } } impl From for RpcError { - fn from(err: Canceled) -> RpcError { - RpcError::Canceled(err) - } + fn from(err: Canceled) -> RpcError { + RpcError::Canceled(err) + } } diff --git a/cli-signer/rpc-client/src/lib.rs b/cli-signer/rpc-client/src/lib.rs index d0e087e59d9..3f4b4d2d6db 100644 --- a/cli-signer/rpc-client/src/lib.rs +++ b/cli-signer/rpc-client/src/lib.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . pub mod client; pub mod signer_client; @@ -21,12 +21,12 @@ extern crate ethereum_types; extern crate futures; extern crate jsonrpc_core; extern crate jsonrpc_ws_server as ws; +extern crate keccak_hash as hash; extern crate parity_rpc as rpc; extern crate parking_lot; extern crate serde; extern crate serde_json; extern crate url; -extern crate keccak_hash as hash; #[macro_use] extern crate log; @@ -36,56 +36,55 @@ extern crate log; extern crate matches; /// Boxed future response. -pub type BoxFuture = Box + Send>; +pub type BoxFuture = Box + Send>; #[cfg(test)] mod tests { - use futures::Future; - use std::path::PathBuf; - use client::{Rpc, RpcError}; - use rpc; - - #[test] - fn test_connection_refused() { - let (_srv, port, mut authcodes) = rpc::tests::ws::serve(); + use client::{Rpc, RpcError}; + use futures::Future; + use rpc; + use std::path::PathBuf; - let _ = authcodes.generate_new(); - authcodes.to_file(&authcodes.path).unwrap(); + #[test] + fn test_connection_refused() { + let (_srv, port, mut authcodes) = rpc::tests::ws::serve(); - let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port - 1), - &authcodes.path); + let _ = authcodes.generate_new(); + authcodes.to_file(&authcodes.path).unwrap(); - let _ = connect.map(|conn| { - assert!(matches!(&conn, &Err(RpcError::WsError(_)))); - }).wait(); - } + let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port - 1), &authcodes.path); - #[test] - fn test_authcode_fail() { - let (_srv, port, _) = rpc::tests::ws::serve(); - let path = PathBuf::from("nonexist"); + let _ = connect + .map(|conn| { + assert!(matches!(&conn, &Err(RpcError::WsError(_)))); + }) + .wait(); + } - let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), &path); + #[test] + fn test_authcode_fail() { + let (_srv, port, _) = rpc::tests::ws::serve(); + let path = PathBuf::from("nonexist"); - let _ = connect.map(|conn| { - assert!(matches!(&conn, &Err(RpcError::NoAuthCode))); - }).wait(); - } + let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), &path); - #[test] - fn test_authcode_correct() { - let (_srv, port, mut authcodes) = rpc::tests::ws::serve(); + let _ = connect + .map(|conn| { + assert!(matches!(&conn, &Err(RpcError::NoAuthCode))); + }) + .wait(); + } - let _ = authcodes.generate_new(); - authcodes.to_file(&authcodes.path).unwrap(); + #[test] + fn test_authcode_correct() { + let (_srv, port, mut authcodes) = rpc::tests::ws::serve(); - let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), - &authcodes.path); + let _ = authcodes.generate_new(); + authcodes.to_file(&authcodes.path).unwrap(); - let _ = connect.map(|conn| { - assert!(conn.is_ok()) - }).wait(); - } + let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), &authcodes.path); + let _ = connect.map(|conn| assert!(conn.is_ok())).wait(); + } } diff --git a/cli-signer/rpc-client/src/signer_client.rs b/cli-signer/rpc-client/src/signer_client.rs index 997841936d1..62839486962 100644 --- a/cli-signer/rpc-client/src/signer_client.rs +++ b/cli-signer/rpc-client/src/signer_client.rs @@ -1,63 +1,76 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use client::{Rpc, RpcError}; use ethereum_types::U256; -use rpc::signer::{ConfirmationRequest, TransactionModification, TransactionCondition}; +use futures::Canceled; +use rpc::signer::{ConfirmationRequest, TransactionCondition, TransactionModification}; use serde; -use serde_json::{Value as JsonValue, to_value}; +use serde_json::{to_value, Value as JsonValue}; use std::path::PathBuf; -use futures::{Canceled}; -use {BoxFuture}; +use BoxFuture; pub struct SignerRpc { - rpc: Rpc, + rpc: Rpc, } impl SignerRpc { - pub fn new(url: &str, authfile: &PathBuf) -> Result { - Ok(SignerRpc { rpc: Rpc::new(&url, authfile)? }) - } - - pub fn requests_to_confirm(&mut self) -> BoxFuture, RpcError>, Canceled> { - self.rpc.request("signer_requestsToConfirm", vec![]) - } - - pub fn confirm_request( - &mut self, - id: U256, - new_gas: Option, - new_gas_price: Option, - new_condition: Option>, - pwd: &str - ) -> BoxFuture, Canceled> { - self.rpc.request("signer_confirmRequest", vec![ - Self::to_value(&format!("{:#x}", id)), - Self::to_value(&TransactionModification { sender: None, gas_price: new_gas_price, gas: new_gas, condition: new_condition }), - Self::to_value(&pwd), - ]) - } - - pub fn reject_request(&mut self, id: U256) -> BoxFuture, Canceled> { - self.rpc.request("signer_rejectRequest", vec![ - JsonValue::String(format!("{:#x}", id)) - ]) - } - - fn to_value(v: &T) -> JsonValue { - to_value(v).expect("Our types are always serializable; qed") - } + pub fn new(url: &str, authfile: &PathBuf) -> Result { + Ok(SignerRpc { + rpc: Rpc::new(&url, authfile)?, + }) + } + + pub fn requests_to_confirm( + &mut self, + ) -> BoxFuture, RpcError>, Canceled> { + self.rpc.request("signer_requestsToConfirm", vec![]) + } + + pub fn confirm_request( + &mut self, + id: U256, + new_gas: Option, + new_gas_price: Option, + new_condition: Option>, + pwd: &str, + ) -> BoxFuture, Canceled> { + self.rpc.request( + "signer_confirmRequest", + vec![ + Self::to_value(&format!("{:#x}", id)), + Self::to_value(&TransactionModification { + sender: None, + gas_price: new_gas_price, + gas: new_gas, + condition: new_condition, + }), + Self::to_value(&pwd), + ], + ) + } + + pub fn reject_request(&mut self, id: U256) -> BoxFuture, Canceled> { + self.rpc.request( + "signer_rejectRequest", + vec![JsonValue::String(format!("{:#x}", id))], + ) + } + + fn to_value(v: &T) -> JsonValue { + to_value(v).expect("Our types are always serializable; qed") + } } diff --git a/cli-signer/src/lib.rs b/cli-signer/src/lib.rs index 3ef6e70549b..fbd1b62cbbc 100644 --- a/cli-signer/src/lib.rs +++ b/cli-signer/src/lib.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . extern crate ethereum_types; extern crate futures; @@ -21,177 +21,142 @@ extern crate rpassword; extern crate parity_rpc as rpc; extern crate parity_rpc_client as client; +use client::signer_client::SignerRpc; use ethereum_types::U256; use rpc::signer::ConfirmationRequest; -use client::signer_client::SignerRpc; -use std::io::{Write, BufRead, BufReader, stdout, stdin}; -use std::path::PathBuf; -use std::fs::File; +use std::{ + fs::File, + io::{stdin, stdout, BufRead, BufReader, Write}, + path::PathBuf, +}; use futures::Future; -fn sign_interactive( - signer: &mut SignerRpc, - password: &str, - request: ConfirmationRequest -) { - print!("\n{}\nSign this transaction? (y)es/(N)o/(r)eject: ", request); - let _ = stdout().flush(); - match BufReader::new(stdin()).lines().next() { - Some(Ok(line)) => { - match line.to_lowercase().chars().nth(0) { - Some('y') => { - match sign_transaction(signer, request.id, password) { - Ok(s) | Err(s) => println!("{}", s), - } - } - Some('r') => { - match reject_transaction(signer, request.id) { - Ok(s) | Err(s) => println!("{}", s), - } - } - _ => () - } - } - _ => println!("Could not read from stdin") - } +fn sign_interactive(signer: &mut SignerRpc, password: &str, request: ConfirmationRequest) { + print!( + "\n{}\nSign this transaction? (y)es/(N)o/(r)eject: ", + request + ); + let _ = stdout().flush(); + match BufReader::new(stdin()).lines().next() { + Some(Ok(line)) => match line.to_lowercase().chars().nth(0) { + Some('y') => match sign_transaction(signer, request.id, password) { + Ok(s) | Err(s) => println!("{}", s), + }, + Some('r') => match reject_transaction(signer, request.id) { + Ok(s) | Err(s) => println!("{}", s), + }, + _ => (), + }, + _ => println!("Could not read from stdin"), + } } -fn sign_transactions( - signer: &mut SignerRpc, - password: String -) -> Result { - signer.requests_to_confirm().map(|reqs| { - match reqs { - Ok(ref reqs) if reqs.is_empty() => { - Ok("No transactions in signing queue".to_owned()) - } - Ok(reqs) => { - for r in reqs { - sign_interactive(signer, &password, r) - } - Ok("".to_owned()) - } - Err(err) => { - Err(format!("error: {:?}", err)) - } - } - }).map_err(|err| { - format!("{:?}", err) - }).wait()? +fn sign_transactions(signer: &mut SignerRpc, password: String) -> Result { + signer + .requests_to_confirm() + .map(|reqs| match reqs { + Ok(ref reqs) if reqs.is_empty() => Ok("No transactions in signing queue".to_owned()), + Ok(reqs) => { + for r in reqs { + sign_interactive(signer, &password, r) + } + Ok("".to_owned()) + } + Err(err) => Err(format!("error: {:?}", err)), + }) + .map_err(|err| format!("{:?}", err)) + .wait()? } fn list_transactions(signer: &mut SignerRpc) -> Result { - signer.requests_to_confirm().map(|reqs| { - match reqs { - Ok(ref reqs) if reqs.is_empty() => { - Ok("No transactions in signing queue".to_owned()) - } - Ok(ref reqs) => { - Ok(format!("Transaction queue:\n{}", reqs - .iter() - .map(|r| format!("{}", r)) - .collect::>() - .join("\n"))) - } - Err(err) => { - Err(format!("error: {:?}", err)) - } - } - }).map_err(|err| { - format!("{:?}", err) - }).wait()? + signer + .requests_to_confirm() + .map(|reqs| match reqs { + Ok(ref reqs) if reqs.is_empty() => Ok("No transactions in signing queue".to_owned()), + Ok(ref reqs) => Ok(format!( + "Transaction queue:\n{}", + reqs.iter() + .map(|r| format!("{}", r)) + .collect::>() + .join("\n") + )), + Err(err) => Err(format!("error: {:?}", err)), + }) + .map_err(|err| format!("{:?}", err)) + .wait()? } -fn sign_transaction( - signer: &mut SignerRpc, id: U256, password: &str -) -> Result { - signer.confirm_request(id, None, None, None, password).map(|res| { - match res { - Ok(u) => Ok(format!("Signed transaction id: {:#x}", u)), - Err(e) => Err(format!("{:?}", e)), - } - }).map_err(|err| { - format!("{:?}", err) - }).wait()? +fn sign_transaction(signer: &mut SignerRpc, id: U256, password: &str) -> Result { + signer + .confirm_request(id, None, None, None, password) + .map(|res| match res { + Ok(u) => Ok(format!("Signed transaction id: {:#x}", u)), + Err(e) => Err(format!("{:?}", e)), + }) + .map_err(|err| format!("{:?}", err)) + .wait()? } -fn reject_transaction( - signer: &mut SignerRpc, id: U256) -> Result -{ - signer.reject_request(id).map(|res| { - match res { - Ok(true) => Ok(format!("Rejected transaction id {:#x}", id)), - Ok(false) => Err(format!("No such request")), - Err(e) => Err(format!("{:?}", e)), - } - }).map_err(|err| { - format!("{:?}", err) - }).wait()? +fn reject_transaction(signer: &mut SignerRpc, id: U256) -> Result { + signer + .reject_request(id) + .map(|res| match res { + Ok(true) => Ok(format!("Rejected transaction id {:#x}", id)), + Ok(false) => Err(format!("No such request")), + Err(e) => Err(format!("{:?}", e)), + }) + .map_err(|err| format!("{:?}", err)) + .wait()? } // cmds -pub fn signer_list( - signerport: u16, authfile: PathBuf -) -> Result { - let addr = &format!("ws://127.0.0.1:{}", signerport); - let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| { - format!("{:?}", err) - })?; - list_transactions(&mut signer) +pub fn signer_list(signerport: u16, authfile: PathBuf) -> Result { + let addr = &format!("ws://127.0.0.1:{}", signerport); + let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| format!("{:?}", err))?; + list_transactions(&mut signer) } pub fn signer_reject( - id: Option, signerport: u16, authfile: PathBuf + id: Option, + signerport: u16, + authfile: PathBuf, ) -> Result { - let id = id.ok_or(format!("id required for signer reject"))?; - let addr = &format!("ws://127.0.0.1:{}", signerport); - let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| { - format!("{:?}", err) - })?; - reject_transaction(&mut signer, U256::from(id)) + let id = id.ok_or(format!("id required for signer reject"))?; + let addr = &format!("ws://127.0.0.1:{}", signerport); + let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| format!("{:?}", err))?; + reject_transaction(&mut signer, U256::from(id)) } pub fn signer_sign( - id: Option, - pwfile: Option, - signerport: u16, - authfile: PathBuf + id: Option, + pwfile: Option, + signerport: u16, + authfile: PathBuf, ) -> Result { - let password; - match pwfile { - Some(pwfile) => { - match File::open(pwfile) { - Ok(fd) => { - match BufReader::new(fd).lines().next() { - Some(Ok(line)) => password = line, - _ => return Err(format!("No password in file")) - } - }, - Err(e) => - return Err(format!("Could not open password file: {}", e)) - } - } - None => { - password = match rpassword::prompt_password_stdout("Password: ") { - Ok(p) => p, - Err(e) => return Err(format!("{}", e)), - } - } - } - - let addr = &format!("ws://127.0.0.1:{}", signerport); - let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| { - format!("{:?}", err) - })?; - - match id { - Some(id) => { - sign_transaction(&mut signer, U256::from(id), &password) - }, - None => { - sign_transactions(&mut signer, password) - } - } + let password; + match pwfile { + Some(pwfile) => match File::open(pwfile) { + Ok(fd) => match BufReader::new(fd).lines().next() { + Some(Ok(line)) => password = line, + _ => return Err(format!("No password in file")), + }, + Err(e) => return Err(format!("Could not open password file: {}", e)), + }, + None => { + password = match rpassword::prompt_password_stdout("Password: ") { + Ok(p) => p, + Err(e) => return Err(format!("{}", e)), + } + } + } + + let addr = &format!("ws://127.0.0.1:{}", signerport); + let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| format!("{:?}", err))?; + + match id { + Some(id) => sign_transaction(&mut signer, U256::from(id), &password), + None => sign_transactions(&mut signer, password), + } } diff --git a/docs/CHANGELOG-1.4.md b/docs/CHANGELOG-1.4.md index 7595c10a7ef..71bdd3a0b4f 100644 --- a/docs/CHANGELOG-1.4.md +++ b/docs/CHANGELOG-1.4.md @@ -27,7 +27,7 @@ Parity 1.4.10 is a first stable release of 1.4.x series. It includes a few minor - Gas_limit for blocks, mined by Parity will be divisible by 37 (#4154) [#4179](https://github.com/paritytech/parity/pull/4179) - gas_limit for new blocks will divide evenly by 13 - - increased PARITY_GAS_LIMIT_DETERMINANT to 37 + - increased GAS_LIMIT_DETERMINANT to 37 - separate method for marking mined block - debug_asserts(gas_limit within protocol range) - round_block_gas_limit method is now static diff --git a/docs/CHANGELOG-1.5.md b/docs/CHANGELOG-1.5.md index fe9f72375ee..aeaed99c888 100644 --- a/docs/CHANGELOG-1.5.md +++ b/docs/CHANGELOG-1.5.md @@ -421,7 +421,7 @@ Full changes: - Prevent duplicate incoming connections ([#4180](https://github.com/paritytech/parity/pull/4180)) - Gas_limit for blocks, mined by Parity will be divisible by 37 ([#4154](https://github.com/paritytech/parity/pull/4154)) [#4176](https://github.com/paritytech/parity/pull/4176) - gas_limit for new blocks will divide evenly by 13 - - increased PARITY_GAS_LIMIT_DETERMINANT to 37 + - increased GAS_LIMIT_DETERMINANT to 37 - separate method for marking mined block - debug_asserts(gas_limit within protocol range) - round_block_gas_limit method is now static diff --git a/docs/CHANGELOG-2.4.md b/docs/CHANGELOG-2.4.md new file mode 100644 index 00000000000..ad7fc373d9d --- /dev/null +++ b/docs/CHANGELOG-2.4.md @@ -0,0 +1,128 @@ +## Parity-Ethereum [v2.4.9](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.9) + +Parity Ethereum v2.4.9-stable is a security update which addresses servo/rust-smallvec#148 + +The full list of included changes: + +* cargo update -p smallvec ([#10822](https://github.com/paritytech/parity-ethereum/pull/10822)) + +## Parity-Ethereum [v2.4.8](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.8) + +Parity-Ethereum 2.4.8-stable is a bugfix release that improves performance and stability. + +* Blockchain: fix reset chain +* State tests: treat empty accounts the same as non-existant accounts (EIP 1052) +* Aura: fix Timestamp Overflow +* Networking: support discovery-only peers (geth bootnodes) +* Snapshotting: fix unclean shutdown while snappshotting is under way + +The full list of included changes: + +* ethcore/res: activate atlantis classic hf on block 8772000 ([#10766](https://github.com/paritytech/parity-ethereum/pull/10766)) +* fix docker tags for publishing ([#10741](https://github.com/paritytech/parity-ethereum/pull/10741)) +* Reset blockchain properly ([#10669](https://github.com/paritytech/parity-ethereum/pull/10669)) +* adds rpc error message for --no-ancient-blocks ([#10608](https://github.com/paritytech/parity-ethereum/pull/10608)) +* Treat empty account the same as non-exist accounts in EIP-1052 ([#10775](https://github.com/paritytech/parity-ethereum/pull/10775)) +* fix: aura don't add `SystemTime::now()` ([#10720](https://github.com/paritytech/parity-ethereum/pull/10720)) +* DevP2p: Get node IP address and udp port from Socket, if not included in PING packet ([#10705](https://github.com/paritytech/parity-ethereum/pull/10705)) +* Revert "fix: aura don't add `SystemTime::now()` ([#10720](https://github.com/paritytech/parity-ethereum/pull/10720))" +* Add a way to signal shutdown to snapshotting threads ([#10744](https://github.com/paritytech/parity-ethereum/pull/10744)) + +## Parity-Ethereum [v2.4.7](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.7) + +Parity-Ethereum 2.4.7-stable is a bugfix release that improves performance and stability. + +Among others, it enables the _Atlantis_ hardfork on **Morden** and **Kotti** Classic networks. + +The full list of included changes: + +* [CI] allow cargo audit to fail ([#10676](https://github.com/paritytech/parity-ethereum/pull/10676)) +* new image ([#10673](https://github.com/paritytech/parity-ethereum/pull/10673)) +* Update publishing ([#10644](https://github.com/paritytech/parity-ethereum/pull/10644)) +* enable lto for release builds ([#10717](https://github.com/paritytech/parity-ethereum/pull/10717)) +* Use RUSTFLAGS to set the optimization level ([#10719](https://github.com/paritytech/parity-ethereum/pull/10719)) +* ethcore: enable ECIP-1054 for classic ([#10731](https://github.com/paritytech/parity-ethereum/pull/10731)) + +## Parity-Ethereum [v2.4.6](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.6) + +Parity-Ethereum 2.4.6-stable is a bugfix release that improves performance and stability. + +Among others, it enables the Petersburg hardfork on **Rinkeby** and **POA-Core** Network, as well as the **Kovan** Network community hardfork. + +The full list of included changes: + +* ci: publish docs debug ([#10638](https://github.com/paritytech/parity-ethereum/pull/10638)) + +## Parity-Ethereum [v2.4.5](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.5) + +Parity-Ethereum 2.4.5-stable is a bugfix release that improves performance and stability. This release improves memory optimizations around timestamp handling and stabilizes the 2.4 release branch. + +As of today, Parity-Ethereum 2.3 reaches end of life and everyone is encouraged to upgrade. + +## Parity-Ethereum [v2.4.4](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.4) + +Parity-Ethereum 2.4.4-beta is a bugfix release that improves performance and stability. This patch release removes the dead chain configs for Easthub and Ethereum Social. + +The full list of included changes: + +* fix(rpc-types): replace uint and hash with `ethereum_types v0.4` ([#10217](https://github.com/paritytech/parity-ethereum/pull/10217)) +* chore(bump ethereum-types) ([#10396](https://github.com/paritytech/parity-ethereum/pull/10396)) +* fix(light eth_gasPrice): ask network if not in cache ([#10535](https://github.com/paritytech/parity-ethereum/pull/10535)) +* fix(light account response): update `tx_queue` ([#10545](https://github.com/paritytech/parity-ethereum/pull/10545)) +* fix(bump dependencies) ([#10540](https://github.com/paritytech/parity-ethereum/pull/10540)) +* tx-pool: check transaction readiness before replacing ([#10526](https://github.com/paritytech/parity-ethereum/pull/10526)) +* fix #10390 ([#10391](https://github.com/paritytech/parity-ethereum/pull/10391)) +* private-tx: replace error_chain ([#10510](https://github.com/paritytech/parity-ethereum/pull/10510)) + +## Parity-Ethereum [v2.4.3](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.3) + +Parity-Ethereum 2.4.3-beta is a bugfix release that improves performance and stability. This patch release contains a critical bug fix where serving light clients previously led to client crashes. Upgrading is highly recommended. + +The full list of included changes: + +* Add additional request tests ([#10503](https://github.com/paritytech/parity-ethereum/pull/10503)) + +## Parity-Ethereum [v2.4.2](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.2) + +Parity-Ethereum 2.4.2-beta is a bugfix release that improves performance and stability. + +The full list of included changes: + +* Сaching through docker volume ([#10477](https://github.com/paritytech/parity-ethereum/pull/10477)) +* fix win&mac build ([#10486](https://github.com/paritytech/parity-ethereum/pull/10486)) +* fix(extract `timestamp_checked_add` as lib) ([#10383](https://github.com/paritytech/parity-ethereum/pull/10383)) + +## Parity-Ethereum [v2.4.1](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.1) + +Parity-Ethereum 2.4.1-beta is a bugfix release that improves performance and stability. + +The full list of included changes: + +* Implement parity_versionInfo & parity_setChain on LC; fix parity_setChain ([#10312](https://github.com/paritytech/parity-ethereum/pull/10312)) +* CI publish to aws ([#10446](https://github.com/paritytech/parity-ethereum/pull/10446)) +* CI aws git checkout ([#10451](https://github.com/paritytech/parity-ethereum/pull/10451)) +* Revert "CI aws git checkout ([#10451](https://github.com/paritytech/parity-ethereum/pull/10451))" (#10456) +* Revert "CI aws git checkout ([#10451](https://github.com/paritytech/parity-ethereum/pull/10451))" +* Tests parallelized ([#10452](https://github.com/paritytech/parity-ethereum/pull/10452)) +* Ensure static validator set changes are recognized ([#10467](https://github.com/paritytech/parity-ethereum/pull/10467)) + +## Parity-Ethereum [v2.4.0](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.0) + +Parity-Ethereum 2.4.0-beta is our trifortnightly minor version release coming with a lot of new features as well as bugfixes and performance improvements. + +Notable changes: +- Account management is now deprecated ([#10213](https://github.com/paritytech/parity-ethereum/pull/10213)) +- Local accounts can now be specified via CLI ([#9960](https://github.com/paritytech/parity-ethereum/pull/9960)) +- Chains can now be reset to a particular block via CLI ([#9782](https://github.com/paritytech/parity-ethereum/pull/9782)) +- Ethash now additionally implements ProgPoW ([#9762](https://github.com/paritytech/parity-ethereum/pull/9762)) +- The `eip1283DisableTransition` flag was added to revert EIP-1283 ([#10214](https://github.com/paritytech/parity-ethereum/pull/10214)) + +The full list of included changes: + +* revert some changes, could be buggy ([#10399](https://github.com/paritytech/parity-ethereum/pull/10399)) +* 10000 > 5000 ([#10422](https://github.com/paritytech/parity-ethereum/pull/10422)) +* fix panic when logging directory does not exist, closes #10420 ([#10424](https://github.com/paritytech/parity-ethereum/pull/10424)) +* fix underflow in pip, closes #10419 ([#10423](https://github.com/paritytech/parity-ethereum/pull/10423)) +* ci: clean up gitlab-ci.yml leftovers from previous merge ([#10429](https://github.com/paritytech/parity-ethereum/pull/10429)) +* Update hardcoded headers for Foundation, Ropsten, Kovan and Classic ([#10417](https://github.com/paritytech/parity-ethereum/pull/10417)) + diff --git a/docs/CHANGELOG-2.5.md b/docs/CHANGELOG-2.5.md new file mode 100644 index 00000000000..bdd036977db --- /dev/null +++ b/docs/CHANGELOG-2.5.md @@ -0,0 +1,220 @@ +## Parity-Ethereum [v2.5.13](https://github.com/openethereum/openethereum/releases/tag/v2.5.13) + +Parity Ethereum v2.5.13-stable is a security release. Valid blocks with manipulated transactions (added/replaced) cause the client to stall. + +The full list of included changes: +* Make sure to not mark block header hash as invalid if only the body is wrong (#11356) + +## Parity-Ethereum [v2.5.12](https://github.com/openethereum/openethereum/releases/tag/v2.5.12) + +Parity Ethereum v2.5.12-stable is a patch release that adds Istanbul hardfork +block numbers for POA and xDai networks, implements ECIP-1056 and implements +EIP-2384/2387 - Muir Glacier. + +The full list of included changes: +* Enable EIP-2384 for ice age hard fork (#11281) +* ethcore/res: activate agharta on classic 9573000 (#11331) +* Istanbul HF in xDai (2019-12-12) (#11299) +* Istanbul HF in POA Core (2019-12-19) (#11298) +* Istanbul HF in POA Sokol (2019-12-05) (#11282) +* Activate ecip-1061 on kotti and mordor (#11338) +* Enable basic verification of local transactions (#11332) +* Disallow EIP-86 style null signatures for transactions outside tests (#11335) + + +## Parity-Ethereum [v2.5.11](https://github.com/openethereum/openethereum/releases/tag/v2.5.11) + +Parity Ethereum v2.5.11-stable is an emergency patch release that adds the missing +eip1344_transition for mainnet - Users are advised to update as soon as possible +to prevent any issues with the imminent Istanbul hardfork + +The full list of included changes: +- [chainspec]: add `eip1344_transition` for istanbul (#11301) + +## Parity-Ethereum [v2.5.10](https://github.com/openethereum/openethereum/releases/tag/2.5.10) + +Parity Ethereum v2.5.10-stable is a patch release that adds block numbers for +activating the Istanbul hardfork on mainnet, as well as a large number of +various bugfixes, QoL changes, some code cleanup/refactoring and other +miscellaneous changes. + +This release removes legacy aliases for the mainnet. If you specify `--chain homestead`, `--chain frontier` or `--chain byzantium`, this will need to be changed to one of: `--chain eth`, `--chain ethereum`, `--chain foundation` or `--chain mainnet`. + +The full list of included changes: + +* ropsten #6631425 foundation #8798209 (#11201) +* [stable] builtin, istanbul and mordor testnet backports (#11234) + * ethcore-builtin (#10850) + * [builtin]: support `multiple prices and activations` in chain spec (#11039) + * [chain specs]: activate `Istanbul` on mainnet (#11228) + * ethcore/res: add mordor testnet configuration (#11200) +* Update list of bootnodes for xDai chain (#11236) +* ethcore: remove `test-helper feat` from build (#11047) +* Secret store: fix Instant::now() related race in net_keep_alive (#11155) (#11159) +* [stable]: backport #10691 and #10683 (#11143) + * Fix compiler warning (that will become an error) (#10683) + * Refactor Clique stepping (#10691) +* Add Constantinople eips to the dev (instant_seal) config (#10809) +* Add cargo-remote dir to .gitignore (?) +* Insert explicit warning into the panic hook (#11225) +* Fix docker centos build (#11226) +* Update MIX bootnodes. (#11203) +* Use provided usd-per-eth value if an endpoint is specified (#11209) +* Add new line after writing block to hex file. (#10984) +* Type annotation for next_key() matching of json filter options (#11192) (but no `FilterOption` in 2.5 so…) +* Upgrade jsonrpc to latest (#11206) +* [CI] check evmbin build (#11096) +* Correct EIP-712 encoding (#11092) +* [client]: Fix for incorrectly dropped consensus messages (#11086) +* Fix block detail updating (#11015) +* Switching sccache from local to Redis (#10971) +* Made ecrecover implementation trait public (#11188) +* [dependencies]: jsonrpc `14.0.1` (#11183) +* [receipt]: add `sender` & `receiver` to `RichReceipts` (#11179) +* [ethcore/builtin]: do not panic in blake2pricer on short input (#11180) +* util Host: fix a double Read Lock bug in fn Host::session_readable() (#11175) +* ethcore client: fix a double Read Lock bug in fn Client::logs() (#11172) +* Change how RPCs eth_call and eth_estimateGas handle "Pending" (#11127) +* Cleanup stratum a bit (#11161) +* Upgrade to jsonrpc v14 (#11151) +* SecretStore: expose restore_key_public in HTTP API (#10241) + +## Parity-Ethereum [v2.5.9](https://github.com/openethereum/openethereum/releases/tag/v2.5.9) + +Parity Ethereum v2.5.9-stable is a patch release that adds the block numbers for activating the Istanbul hardfork on test networks: Ropsten, Görli, Rinkeby and Kovan. + +The full list of included changes: + +* ethcore/res: activate Istanbul on Ropsten, Görli, Rinkeby, Kovan (#11068) +* [json-spec] make blake2 pricing spec more readable (#11034) + +## Parity-Ethereum [v2.5.8](https://github.com/openethereum/openethereum/releases/tag/v2.5.8) + +Parity Ethereum v2.5.8-stable is a patch release that improves security, stability and performance. + +* The most noteworthy improvement in this release is incorporating all the EIPs required for the Istanbul hard fork. +* This release also fixes certain security and performance issues, one of which was suspected to be consensus-threatening but turned out to be benign. Thanks to Martin Holst Swende and Felix Lange from the Ethereum Foundation for bringing the suspicious issue to our attention. + +The full list of included changes: + +* add more tx tests (#11038) +* Fix parallel transactions race-condition (#10995) +* Add blake2_f precompile (#11017) +* [trace] introduce trace failed to Ext (#11019) +* Edit publish-onchain.sh to use https (#11016) +* Fix deadlock in network-devp2p (#11013) +* EIP 1108: Reduce alt_bn128 precompile gas costs (#11008) +* xDai chain support and nodes list update (#10989) +* EIP 2028: transaction gas lowered from 68 to 16 (#10987) +* EIP-1344 Add CHAINID op-code (#10983) +* manual publish jobs for releases, no changes for nightlies (#10977) +* [blooms-db] Fix benchmarks (#10974) +* Verify transaction against its block during import (#10954) +* Better error message for rpc gas price errors (#10931) +* tx-pool: accept local tx with higher gas price when pool full (#10901) +* Fix fork choice (#10837) +* Cleanup unused vm dependencies (#10787) +* Fix compilation on recent nightlies (#10991) +* Don't build rpc with ethcore test-helpers (#11048) +* EIP 1884 Re-pricing of trie-size dependent operations (#10992) +* Implement EIP-1283 reenable transition, EIP-1706 and EIP-2200 (#10191) + +## Parity-Ethereum [v2.5.7](https://github.com/openethereum/openethereum/releases/tag/v2.5.7) + +Parity Ethereum v2.5.7-stable is a bugfix release that fixes a potential DoS attack in the trace_call RPC method. This is a critical upgrade for anyone running Parity nodes with RPC exposed to the public internet (and highly recommended for anyone else). For details see this blog post. + +## Parity-Ethereum [v2.5.6](https://github.com/openethereum/openethereum/releases/tag/v2.5.6) + +Parity-Ethereum v2.5.6-stable is a bugfix release that improves stability. + +* Allow specifying hostnames for node URLs +* Fix a bug where archive nodes were losing peers + +The full list of included changes: + +* Kaspersky AV whitelisting (#10919) +* Avast whitelist script (#10900) +* Docker images renaming (#10863) +* Remove excessive warning (#10831) +* Allow --nat extip:your.host.here.org (#10830) +* When updating the client or when called from RPC, sleep should mean sleep (#10814) +* added new ropsten-bootnode and removed old one (#10794) +* ethkey no longer uses byteorder (#10786) +* Do not drop the peer with None difficulty (#10772) +* docs: Update Readme with TOC, Contributor Guideline. Update Cargo package descriptions (#10652) + +## Parity-Ethereum [v2.5.5](https://github.com/openethereum/openethereum/releases/tag/v2.5.5) + +Parity-Ethereum v2.5.5-stable is a minor release that improves performance and stability. +This release stabilises the 2.5 branch. + +As of today, Parity-Ethereum 2.4 reaches end of life and everyone is +encouraged to upgrade. + +## Parity-Ethereum [v2.5.4](https://github.com/openethereum/openethereum/releases/tag/v2.5.4) + +Parity Ethereum v2.5.4-beta is a security update that addresses servo/rust-smallvec#148 + +The full list of included changes: + +* cargo update -p smallvec ([#10822](https://github.com/openethereum/openethereum/pull/10822)) + +## Parity-Ethereum [v2.5.3](https://github.com/openethereum/openethereum/releases/tag/v2.5.3) + +Parity-Ethereum 2.5.3-beta is a bugfix release that improves performance and stability. + +* EthereumClassic: activate the Atlantis Hardfork +* Clique: fix time overflow +* State tests: treat empty accounts the same as non-existant accounts (EIP 1052) +* Networking: support discovery-only peers (geth bootnodes) +* Snapshotting: fix unclean shutdown while snappshotting is under way + +The full list of included changes: + +* ethcore/res: activate atlantis classic hf on block 8772000 ([#10766](https://github.com/openethereum/openethereum/pull/10766)) +* fix docker tags for publishing ([#10741](https://github.com/openethereum/openethereum/pull/10741)) +* fix: aura don't add `SystemTime::now()` ([#10720](https://github.com/openethereum/openethereum/pull/10720)) +* Treat empty account the same as non-exist accounts in EIP-1052 ([#10775](https://github.com/openethereum/openethereum/pull/10775)) +* DevP2p: Get node IP address and udp port from Socket, if not included in PING packet ([#10705](https://github.com/openethereum/openethereum/pull/10705)) +* Add a way to signal shutdown to snapshotting threads ([#10744](https://github.com/openethereum/openethereum/pull/10744)) + +## Parity-Ethereum [v2.5.2](https://github.com/openethereum/openethereum/releases/tag/v2.5.2) + +Parity-Ethereum 2.5.2-beta is a bugfix release that improves performance and stability. + +Among others, it enables the _Atlantis_ hardfork on **Morden** and **Kotti** Classic networks. + +The full list of included changes: + +* [CI] allow cargo audit to fail ([#10676](https://github.com/openethereum/openethereum/pull/10676)) +* Reset blockchain properly ([#10669](https://github.com/openethereum/openethereum/pull/10669)) +* new image ([#10673](https://github.com/openethereum/openethereum/pull/10673)) +* Update publishing ([#10644](https://github.com/openethereum/openethereum/pull/10644)) +* enable lto for release builds ([#10717](https://github.com/openethereum/openethereum/pull/10717)) +* Use RUSTFLAGS to set the optimization level ([#10719](https://github.com/openethereum/openethereum/pull/10719)) +* ethcore: enable ECIP-1054 for classic ([#10731](https://github.com/openethereum/openethereum/pull/10731)) + +## Parity-Ethereum [v2.5.1](https://github.com/openethereum/openethereum/releases/tag/v2.5.1) + +Parity-Ethereum 2.5.1-beta is a bugfix release that improves performance and stability. + +Among others, it enables the Petersburg hardfork on **Rinkeby** and **POA-Core** Network, as well as the **Kovan** Network community hardfork. + +The full list of included changes: + +* ci: publish docs debug ([#10638](https://github.com/openethereum/openethereum/pull/10638)) + +## Parity-Ethereum [v2.5.0](https://github.com/openethereum/openethereum/releases/tag/v2.5.0) + +Parity-Ethereum 2.5.0-beta is a minor release that improves performance and stabilizes the 2.5 branch by marking it as beta release. + +- This release adds support for the Clique consensus engine ([#9981](https://github.com/openethereum/openethereum/pull/9981)) + - This enables Parity-Ethereum users to use the Görli, the Kotti Classic, and the legacy Rinkeby testnet. To get started try `parity --chain goerli`; note that light client support is currently not yet fully functional. +- This release removes the dead chain configs for Easthub and Ethereum Social ([#10531](https://github.com/openethereum/openethereum/pull/10531)) + +As of today, Parity-Ethereum 2.3 reaches end of life and everyone is encouraged to upgrade. + +The full list of included changes: + +* fix(light cull): poll light cull instead of timer ([#10559](https://github.com/openethereum/openethereum/pull/10559)) + diff --git a/docs/CHANGELOG-2.6.md b/docs/CHANGELOG-2.6.md new file mode 100644 index 00000000000..f09474e67de --- /dev/null +++ b/docs/CHANGELOG-2.6.md @@ -0,0 +1,307 @@ +## Parity-Ethereum [v2.6.8](https://github.com/openethereum/openethereum/releases/tag/v2.6.8) + +Parity Ethereum v2.6.8-beta is a security release. Valid blocks with manipulated transactions (added/replaced) cause the client to stall. + +The full list of included changes: +* Make sure to not mark block header hash as invalid if only the body is wrong (#11356) + +## Parity-Ethereum [v2.6.7](https://github.com/openethereum/openethereum/releases/tag/v2.6.7) + +Parity Ethereum v2.6.7-beta is a patch release that adds Istanbul hardfork +block numbers for POA and xDai networks, implements ECIP-1056 and implements +EIP-2384/2387 - Muir Glacier. + +The full list of included changes: +* Enable EIP-2384 for ice age hard fork (#11281) +* ethcore/res: activate agharta on classic 9573000 (#11331) +* Istanbul HF in xDai (2019-12-12) (#11299) +* Istanbul HF in POA Core (2019-12-19) (#11298) +* Istanbul HF in POA Sokol (2019-12-05) (#11282) +* Activate ecip-1061 on kotti and mordor (#11338) +* Enable basic verification of local transactions (#11332) +* Disallow EIP-86 style null signatures for transactions outside tests (#11335) +* SecretStore database migration to v4 (#11322) + +## Parity-Ethereum [v2.6.6](https://github.com/openethereum/openethereum/releases/tag/v2.6.6) + +Parity Ethereum v2.6.6-beta is an emergency patch release that adds the missing +eip1344_transition for mainnet - Users are advised to update as soon as possible +to prevent any issues with the imminent Istanbul hardfork + +The full list of included changes: +* [chainspec]: add `eip1344_transition` for istanbul (#11301) + +## Parity-Ethereum [v2.6.5](https://github.com/openethereum/openethereum/releases/tag/v2.6.5) + +Parity Ethereum v2.6.5-beta is a patch release that adds block numbers for activating the Istanbul hardfork on mainnet, as well as a large number of various bugfixes, QoL changes, some code cleanup/refactoring and other miscellaneous changes. + +This release removes legacy aliases for the mainnet. If you specify `--chain homestead`, `--chain frontier` or `--chain byzantium`, this will need to be changed to one of: `--chain eth`, `--chain ethereum`, `--chain foundation` or `--chain mainnet`. + +This release includes important changes to how snapshots are produced. The size of the Ethereum account state means that producing a snapshot takes a long while; most nodes today are not able to finish before the relevant state is pruned. Starting with v2.6.5, pruning is paused while a snapshot is underway, hopefully fixing the current dearth of recent snapshots. The downside to this is that memory usage goes up while a snapshot is produced. + +The full list of included changes: + +* [CI] check evmbin build (#11096) +* Correct EIP-712 encoding (#11092) +* [client]: Fix for incorrectly dropped consensus messages (#11082) (#11086) +* Update hardcoded headers (foundation, classic, kovan, xdai, ewc, ...) (#11053) +* Add cargo-remote dir to .gitignore (?) +* Update light client headers: ropsten 6631425 foundation 8798209 (#11201) +* Update list of bootnodes for xDai chain (#11236) +* ethcore/res: add mordor testnet configuration (#11200) +* [chain specs]: activate Istanbul on mainnet (#11228) +* [builtin]: support multiple prices and activations in chain spec (#11039) +* [receipt]: add sender & receiver to RichReceipts (#11179) +* [ethcore/builtin]: do not panic in blake2pricer on short input (#11180) +* Made ecrecover implementation trait public (#11188) +* Fix docker centos build (#11226) +* Update MIX bootnodes. (#11203) +* Insert explicit warning into the panic hook (#11225) +* Use provided usd-per-eth value if an endpoint is specified (#11209) +* Cleanup stratum a bit (#11161) +* Add Constantinople EIPs to the dev (instant_seal) config (#10809) (already backported) +* util Host: fix a double Read Lock bug in fn Host::session_readable() (#11175) +* ethcore client: fix a double Read Lock bug in fn Client::logs() (#11172) +* Type annotation for next_key() matching of json filter options (#11192) +* Upgrade jsonrpc to latest (#11206) +* [dependencies]: jsonrpc 14.0.1 (#11183) +* Upgrade to jsonrpc v14 (#11151) +* Switching sccache from local to Redis (#10971) +* Snapshot restoration overhaul (#11219) +* Add new line after writing block to hex file. (#10984) +* Pause pruning while snapshotting (#11178) +* Change how RPCs eth_call and eth_estimateGas handle "Pending" (#11127) +* Fix block detail updating (#11015) +* Make InstantSeal Instant again #11186 +* Filter out some bad ropsten warp snapshots (#11247) +* Allow default block parameter to be blockHash (#10932) + +## Parity-Ethereum [v2.6.4](https://github.com/openethereum/openethereum/releases/tag/v2.6.4) + +Parity Ethereum v2.6.4-stable is a patch release that adds the block numbers for activating the Istanbul hardfork on test networks: Ropsten, Görli, Rinkeby and Kovan. + +A full list of included changes: + +* ethcore/res: activate Istanbul on Ropsten, Görli, Rinkeby, Kovan (#11068) +* cleanup json crate (#11027) +* [json-spec] make blake2 pricing spec more readable (#11034) +* Update JSON tests to d4f86ecf4aa7c (#11054) + +## Parity-Ethereum [v2.6.3](https://github.com/openethereum/openethereum/releases/tag/v2.6.3) + +Parity Ethereum v2.6.3-stable is a patch release that improves security, stability and performance. + +* The most noteworthy improvement in this release is incorporating all the EIPs required for the Istanbul hard fork. +* This release also fixes certain security and performance issues, one of which was suspected to be consensus-threatening but turned out to be benign. Thanks to Martin Holst Swende and Felix Lange from the Ethereum Foundation for bringing the suspicious issue to our attention. + +The full list of included changes: + +* add more tx tests (#11038) +* Fix parallel transactions race-condition (#10995) +* Add blake2_f precompile (#11017) +* [trace] introduce trace failed to Ext (#11019) +* Edit publish-onchain.sh to use https (#11016) +* Fix deadlock in network-devp2p (#11013) +* EIP 1108: Reduce alt_bn128 precompile gas costs (#11008) +* xDai chain support and nodes list update (#10989) +* EIP 2028: transaction gas lowered from 68 to 16 (#10987) +* EIP-1344 Add CHAINID op-code (#10983) +* manual publish jobs for releases, no changes for nightlies (#10977) +* [blooms-db] Fix benchmarks (#10974) +* Verify transaction against its block during import (#10954) +* Better error message for rpc gas price errors (#10931) +* Fix fork choice (#10837) +* Fix compilation on recent nightlies (#10991) +* Don't build rpc with ethcore test-helpers (#11048) +* EIP 1884 Re-pricing of trie-size dependent operations (#10992) +* Implement EIP-1283 reenable transition, EIP-1706 and EIP-2200 (#10191) + +## Parity-Ethereum [v2.6.2](https://github.com/openethereum/openethereum/releases/tag/v2.6.2) + +Parity Ethereum v2.6.2-stable is a bugfix release that fixes a potential DoS attack in the trace_call RPC method. This is a critical upgrade for anyone running Parity nodes with RPC exposed to the public internet (and highly recommended for anyone else). For details see this blog post. + +## Parity-Ethereum [v2.6.1](https://github.com/openethereum/openethereum/releases/tag/v2.6.1) + +Parity-Ethereum 2.6.1-beta is a patch release that improves stability. + +This release includes: + * Allow specifying hostnames for node URLs + * Fix a bug where archive nodes were losing peers + * Add support for Energy Web Foundations new chains 'Volta' and 'EWC', and remove their deprecated 'Tobalaba' chain. + +The full list of included changes: + * Add support for Energy Web Foundation's new chains (#10957) + * Kaspersky AV whitelisting (#10919) + * Avast whitelist script (#10900) + * Docker images renaming (#10863) + * Remove excessive warning (#10831) + * Allow --nat extip:your.host.here.org (#10830) + * When updating the client or when called from RPC, sleep should mean sleep (#10814) + * added new ropsten-bootnode and removed old one (#10794) + * ethkey no longer uses byteorder (#10786) + * docs: Update Readme with TOC, Contributor Guideline. Update Cargo package descriptions (#10652) + +## Parity-Ethereum [v2.6.0](https://github.com/openethereum/openethereum/releases/tag/v2.6.0) + +Parity-Ethereum 2.6.0-beta is a minor release that stabilizes the 2.6 branch by +marking it as a beta release. + +This release includes: + * Major refactoring of the codebase + * Many bugfixes + * Significant improvements to logging, error and warning message clarity. + * SecretStore: remove support of old database formats (#10757) + * This is a potentially breaking change if you have not upgraded for + quite some time. + + As of today, Parity-Ethereum 2.4 reaches end of life, and everyone is + encouraged to upgrade. + +The full list of included changes: +* update jsonrpc to 12.0 ([#10841](https://github.com/openethereum/openethereum/pull/10841)) +* Move more code into state-account ([#10840](https://github.com/openethereum/openethereum/pull/10840)) +* Extract AccountDB to account-db ([#10839](https://github.com/openethereum/openethereum/pull/10839)) +* Extricate PodAccount and state Account to own crates ([#10838](https://github.com/openethereum/openethereum/pull/10838)) +* Fix fork choice ([#10837](https://github.com/openethereum/openethereum/pull/10837)) +* tests: Relates to #10655: Test instructions for Readme ([#10835](https://github.com/openethereum/openethereum/pull/10835)) +* idiomatic changes to PodState ([#10834](https://github.com/openethereum/openethereum/pull/10834)) +* Break circular dependency between Client and Engine (part 1) ([#10833](https://github.com/openethereum/openethereum/pull/10833)) +* Remove excessive warning ([#10831](https://github.com/openethereum/openethereum/pull/10831)) +* Allow --nat extip:your.host.here.org ([#10830](https://github.com/openethereum/openethereum/pull/10830)) +* ethcore does not use byteorder ([#10829](https://github.com/openethereum/openethereum/pull/10829)) +* Fix typo in README.md ([#10828](https://github.com/openethereum/openethereum/pull/10828)) +* Update wordlist to v1.3 ([#10823](https://github.com/openethereum/openethereum/pull/10823)) +* bump `smallvec 0.6.10` to fix vulnerability ([#10822](https://github.com/openethereum/openethereum/pull/10822)) +* removed additional_params method ([#10818](https://github.com/openethereum/openethereum/pull/10818)) +* Improve logging when remote peer is unknown ([#10817](https://github.com/openethereum/openethereum/pull/10817)) +* replace memzero with zeroize crate ([#10816](https://github.com/openethereum/openethereum/pull/10816)) +* When updating the client or when called from RPC, sleep should mean sleep ([#10814](https://github.com/openethereum/openethereum/pull/10814)) +* Don't reimplement the logic from the Default impl ([#10813](https://github.com/openethereum/openethereum/pull/10813)) +* refactor: whisper: Add type aliases and update rustdocs in message.rs ([#10812](https://github.com/openethereum/openethereum/pull/10812)) +* test: whisper/cli `add invalid pool size test depending on processor` ([#10811](https://github.com/openethereum/openethereum/pull/10811)) +* Add Constantinople EIPs to the dev (instant_seal) config ([#10809](https://github.com/openethereum/openethereum/pull/10809)) +* fix spurious test failure ([#10808](https://github.com/openethereum/openethereum/pull/10808)) +* revert temp changes to .gitlab-ci.yml ([#10807](https://github.com/openethereum/openethereum/pull/10807)) +* removed redundant fmt::Display implementations ([#10806](https://github.com/openethereum/openethereum/pull/10806)) +* removed EthEngine alias ([#10805](https://github.com/openethereum/openethereum/pull/10805)) +* ethcore-bloom-journal updated to 2018 ([#10804](https://github.com/openethereum/openethereum/pull/10804)) +* Fix a few typos and unused warnings. ([#10803](https://github.com/openethereum/openethereum/pull/10803)) +* updated price-info to edition 2018 ([#10801](https://github.com/openethereum/openethereum/pull/10801)) +* updated parity-local-store to edition 2018 ([#10800](https://github.com/openethereum/openethereum/pull/10800)) +* updated project to ansi_term 0.11 ([#10799](https://github.com/openethereum/openethereum/pull/10799)) +* ethcore-light uses bincode 1.1 ([#10798](https://github.com/openethereum/openethereum/pull/10798)) +* ethcore-network-devp2p uses igd 0.9 ([#10797](https://github.com/openethereum/openethereum/pull/10797)) +* Better logging when backfilling ancient blocks fail ([#10796](https://github.com/openethereum/openethereum/pull/10796)) +* added new ropsten-bootnode and removed old one ([#10794](https://github.com/openethereum/openethereum/pull/10794)) +* Removed machine abstraction from ethcore ([#10791](https://github.com/openethereum/openethereum/pull/10791)) +* Removed redundant ethcore-service error type ([#10788](https://github.com/openethereum/openethereum/pull/10788)) +* Cleanup unused vm dependencies ([#10787](https://github.com/openethereum/openethereum/pull/10787)) +* ethkey no longer uses byteorder ([#10786](https://github.com/openethereum/openethereum/pull/10786)) +* Updated blooms-db to rust 2018 and removed redundant deps ([#10785](https://github.com/openethereum/openethereum/pull/10785)) +* Treat empty account the same as non-exist accounts in EIP-1052 ([#10775](https://github.com/openethereum/openethereum/pull/10775)) +* Do not drop the peer with None difficulty ([#10772](https://github.com/openethereum/openethereum/pull/10772)) +* EIP-1702: Generalized Account Versioning Scheme ([#10771](https://github.com/openethereum/openethereum/pull/10771)) +* Move Engine::register_client to be before other I/O handler registration ([#10767](https://github.com/openethereum/openethereum/pull/10767)) +* ethcore/res: activate atlantis classic hf on block 8772000 ([#10766](https://github.com/openethereum/openethereum/pull/10766)) +* Updated Bn128PairingImpl to use optimized batch pairing ([#10765](https://github.com/openethereum/openethereum/pull/10765)) +* Remove unused code ([#10762](https://github.com/openethereum/openethereum/pull/10762)) +* Initialize private tx logger only if private tx functionality is enabled ([#10758](https://github.com/openethereum/openethereum/pull/10758)) +* SecretStore: remove support of old database formats ([#10757](https://github.com/openethereum/openethereum/pull/10757)) +* Enable aesni ([#10756](https://github.com/openethereum/openethereum/pull/10756)) +* updater: fix static id hashes initialization ([#10755](https://github.com/openethereum/openethereum/pull/10755)) +* Use fewer threads for snapshotting ([#10752](https://github.com/openethereum/openethereum/pull/10752)) +* Die error_chain, die ([#10747](https://github.com/openethereum/openethereum/pull/10747)) +* Fix deprectation warnings on nightly ([#10746](https://github.com/openethereum/openethereum/pull/10746)) +* Improve logging and cleanup in miner around block sealing ([#10745](https://github.com/openethereum/openethereum/pull/10745)) +* Add a way to signal shutdown to snapshotting threads ([#10744](https://github.com/openethereum/openethereum/pull/10744)) +* fix docker tags for publishing ([#10741](https://github.com/openethereum/openethereum/pull/10741)) +* refactor: Fix indentation in ethjson ([#10740](https://github.com/openethereum/openethereum/pull/10740)) +* Log validator set changes in EpochManager ([#10734](https://github.com/openethereum/openethereum/pull/10734)) +* Print warnings when using dangerous settings for ValidatorSet ([#10733](https://github.com/openethereum/openethereum/pull/10733)) +* ethcore: enable ECIP-1054 for classic ([#10731](https://github.com/openethereum/openethereum/pull/10731)) +* Stop breaking out of loop if a non-canonical hash is found ([#10729](https://github.com/openethereum/openethereum/pull/10729)) +* Removed secret_store folder ([#10722](https://github.com/openethereum/openethereum/pull/10722)) +* Revert "enable lto for release builds (#10717)" ([#10721](https://github.com/openethereum/openethereum/pull/10721)) +* fix: aura don't add `SystemTime::now()` ([#10720](https://github.com/openethereum/openethereum/pull/10720)) +* Use RUSTFLAGS to set the optimization level ([#10719](https://github.com/openethereum/openethereum/pull/10719)) +* enable lto for release builds ([#10717](https://github.com/openethereum/openethereum/pull/10717)) +* [devp2p] Update to 2018 edition ([#10716](https://github.com/openethereum/openethereum/pull/10716)) +* [devp2p] Don't use `rust-crypto` ([#10714](https://github.com/openethereum/openethereum/pull/10714)) +* [devp2p] Fix warnings and re-org imports ([#10710](https://github.com/openethereum/openethereum/pull/10710)) +* DevP2p: Get node IP address and udp port from Socket, if not included in PING packet ([#10705](https://github.com/openethereum/openethereum/pull/10705)) +* introduce MissingParent Error, fixes #10699 ([#10700](https://github.com/openethereum/openethereum/pull/10700)) +* Refactor Clique stepping ([#10691](https://github.com/openethereum/openethereum/pull/10691)) +* add_sync_notifier in EthPubSubClient holds on to a Client for too long ([#10689](https://github.com/openethereum/openethereum/pull/10689)) +* Fix compiler warning (that will become an error) ([#10683](https://github.com/openethereum/openethereum/pull/10683)) +* Don't panic if extra_data is longer than VANITY_LENGTH ([#10682](https://github.com/openethereum/openethereum/pull/10682)) +* Remove annoying compiler warnings ([#10679](https://github.com/openethereum/openethereum/pull/10679)) +* Remove support for hardware wallets ([#10678](https://github.com/openethereum/openethereum/pull/10678)) +* [CI] allow cargo audit to fail ([#10676](https://github.com/openethereum/openethereum/pull/10676)) +* new image ([#10673](https://github.com/openethereum/openethereum/pull/10673)) +* Upgrade ethereum types ([#10670](https://github.com/openethereum/openethereum/pull/10670)) +* Reset blockchain properly ([#10669](https://github.com/openethereum/openethereum/pull/10669)) +* fix: Move PR template into .github/ folder ([#10663](https://github.com/openethereum/openethereum/pull/10663)) +* docs: evmbin - Update Rust docs ([#10658](https://github.com/openethereum/openethereum/pull/10658)) +* refactor: Related #9459 - evmbin: replace untyped json! macro with fully typed serde serialization using Rust structs ([#10657](https://github.com/openethereum/openethereum/pull/10657)) +* docs: Add PR template ([#10654](https://github.com/openethereum/openethereum/pull/10654)) +* docs: Add ProgPoW Rust docs to ethash module ([#10653](https://github.com/openethereum/openethereum/pull/10653)) +* docs: Update Readme with TOC, Contributor Guideline. Update Cargo package descriptions ([#10652](https://github.com/openethereum/openethereum/pull/10652)) +* Upgrade to parity-crypto 0.4 ([#10650](https://github.com/openethereum/openethereum/pull/10650)) +* fix(compilation warnings) ([#10649](https://github.com/openethereum/openethereum/pull/10649)) +* [whisper] Move needed aes_gcm crypto in-crate ([#10647](https://github.com/openethereum/openethereum/pull/10647)) +* Update publishing ([#10644](https://github.com/openethereum/openethereum/pull/10644)) +* ci: publish docs debug ([#10638](https://github.com/openethereum/openethereum/pull/10638)) +* Fix publish docs ([#10635](https://github.com/openethereum/openethereum/pull/10635)) +* Fix rinkeby petersburg fork ([#10632](https://github.com/openethereum/openethereum/pull/10632)) +* Update kovan.json to switch Kovan validator set to POA Consensus Contracts ([#10628](https://github.com/openethereum/openethereum/pull/10628)) +* [ethcore] remove error_chain ([#10616](https://github.com/openethereum/openethereum/pull/10616)) +* Remove unused import ([#10615](https://github.com/openethereum/openethereum/pull/10615)) +* Adds parity_getRawBlockByNumber, parity_submitRawBlock ([#10609](https://github.com/openethereum/openethereum/pull/10609)) +* adds rpc error message for --no-ancient-blocks ([#10608](https://github.com/openethereum/openethereum/pull/10608)) +* Constantinople HF on POA Core ([#10606](https://github.com/openethereum/openethereum/pull/10606)) +* Clique: zero-fill extradata when the supplied value is less than 32 bytes in length ([#10605](https://github.com/openethereum/openethereum/pull/10605)) +* evm: add some mulmod benches ([#10600](https://github.com/openethereum/openethereum/pull/10600)) +* sccache logs to stdout ([#10596](https://github.com/openethereum/openethereum/pull/10596)) +* update bootnodes ([#10595](https://github.com/openethereum/openethereum/pull/10595)) +* Merge `Notifier` and `TransactionsPoolNotifier` ([#10591](https://github.com/openethereum/openethereum/pull/10591)) +* fix(whisper): change expiry `unix_time + ttl + work` ([#10587](https://github.com/openethereum/openethereum/pull/10587)) +* fix(evmbin): make benches compile again ([#10586](https://github.com/openethereum/openethereum/pull/10586)) +* fix issue with compilation when 'slow-blocks' feature enabled ([#10585](https://github.com/openethereum/openethereum/pull/10585)) +* Allow CORS requests in Secret Store API ([#10584](https://github.com/openethereum/openethereum/pull/10584)) +* CI improvements ([#10579](https://github.com/openethereum/openethereum/pull/10579)) +* ethcore: improve timestamp handling ([#10574](https://github.com/openethereum/openethereum/pull/10574)) +* Update Issue Template to direct security issue to email ([#10562](https://github.com/openethereum/openethereum/pull/10562)) +* version: bump master to 2.6 ([#10560](https://github.com/openethereum/openethereum/pull/10560)) +* fix(light cull): poll light cull instead of timer ([#10559](https://github.com/openethereum/openethereum/pull/10559)) +* Watch transactions pool ([#10558](https://github.com/openethereum/openethereum/pull/10558)) +* Add SealingState; don't prepare block when not ready. ([#10529](https://github.com/openethereum/openethereum/pull/10529)) +* Explicitly enable or disable Stratum in config file (Issue 9785) ([#10521](https://github.com/openethereum/openethereum/pull/10521)) +* Add filtering capability to `parity_pendingTransactions` (issue 8269) ([#10506](https://github.com/openethereum/openethereum/pull/10506)) +* Remove calls to heapsize ([#10432](https://github.com/openethereum/openethereum/pull/10432)) +* RPC: Implements eth_subscribe("syncing") ([#10311](https://github.com/openethereum/openethereum/pull/10311)) +* SecretStore: non-blocking wait of session completion ([#10303](https://github.com/openethereum/openethereum/pull/10303)) +* Node table limiting and cache for node filter ([#10288](https://github.com/openethereum/openethereum/pull/10288)) +* SecretStore: expose restore_key_public in HTTP API ([#10241](https://github.com/openethereum/openethereum/pull/10241)) +* Trivial journal for private transactions ([#10056](https://github.com/openethereum/openethereum/pull/10056)) + +## Previous releases + +- [CHANGELOG-2.5](docs/CHANGELOG-2.5.md) (_stable_) +- [CHANGELOG-2.4](docs/CHANGELOG-2.4.md) (EOL: 2019-07-08) +- [CHANGELOG-2.3](docs/CHANGELOG-2.3.md) (EOL: 2019-04-09) +- [CHANGELOG-2.2](docs/CHANGELOG-2.2.md) (EOL: 2019-02-25) +- [CHANGELOG-2.1](docs/CHANGELOG-2.1.md) (EOL: 2019-01-16) +- [CHANGELOG-2.0](docs/CHANGELOG-2.0.md) (EOL: 2018-11-15) +- [CHANGELOG-1.11](docs/CHANGELOG-1.11.md) (EOL: 2018-09-19) +- [CHANGELOG-1.10](docs/CHANGELOG-1.10.md) (EOL: 2018-07-18) +- [CHANGELOG-1.9](docs/CHANGELOG-1.9.md) (EOL: 2018-05-09) +- [CHANGELOG-1.8](docs/CHANGELOG-1.8.md) (EOL: 2018-03-22) +- [CHANGELOG-1.7](docs/CHANGELOG-1.7.md) (EOL: 2018-01-25) +- [CHANGELOG-1.6](docs/CHANGELOG-1.6.md) (EOL: 2017-10-15) +- [CHANGELOG-1.5](docs/CHANGELOG-1.5.md) (EOL: 2017-07-28) +- [CHANGELOG-1.4](docs/CHANGELOG-1.4.md) (EOL: 2017-03-13) +- [CHANGELOG-1.3](docs/CHANGELOG-1.3.md) (EOL: 2017-01-19) +- [CHANGELOG-1.2](docs/CHANGELOG-1.2.md) (EOL: 2016-11-07) +- [CHANGELOG-1.1](docs/CHANGELOG-1.1.md) (EOL: 2016-08-12) +- [CHANGELOG-1.0](docs/CHANGELOG-1.0.md) (EOL: 2016-06-24) +- [CHANGELOG-0.9](docs/CHANGELOG-0.9.md) (EOL: 2016-05-02) diff --git a/docs/CHANGELOG-2.7.md b/docs/CHANGELOG-2.7.md new file mode 100644 index 00000000000..99a7b03d68e --- /dev/null +++ b/docs/CHANGELOG-2.7.md @@ -0,0 +1,383 @@ +## Parity-Ethereum [v2.7.2](https://github.com/openethereum/openethereum/releases/tag/v2.7.2) + +Parity Ethereum v2.7.2-stable is a patch version release of parity-ethereum. +Starting in the 2.7.x series of releases, parity-ethereum is switching to a single stable release +track. As a result, any clients that currently receive updates from the beta +track should switch to the stable track. +Due to database format changes, upgrading from 2.5.x or 2.6.x is one-way only. + +The full list of included changes: +* [eth classic chainspec]: remove balance = 1 (#11458) +* backwards compatible call_type creation_method (#11450 + #11455) +* chore: remove unused dependencies (#11432) +* Cargo.lock: new lockfile format (#11448) +* rlp_derive: cleanup (#11446) +* Avoid long state queries when serving GetNodeData requests (#11444) +* update kvdb-rocksdb to 0.4 (#11442) +* Remove dead bootnodes, add new geth bootnodes (#11441) +* goerli: replace foundation bootnode (#11433) +* fix: export hardcoded sync format (#11416) +* verification: fix race same block + misc (#11400) +* update classic testnet bootnodes (#11398) +* gcc to clang (#11453) + +## Parity-Ethereum [v2.7.1](https://github.com/openethereum/openethereum/releases/tag/v2.7.1) +* Revert #11311 (#11427) + +## Parity-Ethereum [v2.7.0](https://github.com/openethereum/openethereum/releases/tag/v2.7.0) + +Parity Ethereum v2.7.0-stable is a minor version release of parity-ethereum. As +of this release, parity-ethereum is switching to a single `stable` release +track. As a result, any clients that currently receive updates from the `beta` +track should switch to the `stable` track. + +The full list of included changes from `v2.5-stable` to `v2.7-stable` (the +`v2.6-beta` branch will already include some of these changes): + +* miner: fix deprecation warning Error::description (#11380) +* Fix Aztlan hard fork issues (#11347) +* authority_round: Fix next_step_time_duration. (#11379) +* Set the block gas limit to the value returned by a contract call (#10928) +* [Trace] Distinguish between `create` and `create2` (#11311) +* fix cargo audit (#11378) +* Fix esoteric test config variable (#11292) +* Rip out the C and Java bindings (#11346) +* Encapsulate access to the client for secret store (#11232) +* Forward-port #11356 (#11359) +* Fix error message typo (#11363) +* [util/migration]: remove needless `static` bounds (#11348) +* Replace stale boot nodes with latest list (#11351) +* Update to latest `kvdb-*`: no default column, DBValue is Vec (#11312) +* we do not profit from incremental now (#11302) +* update autoupdate fork blocks for nightly (#11308) +* Add Nat PMP method to P2P module (#11210) +* Add randomness contract support to AuthorityRound. (#10946) +* ethcore/res: activate ecip-1061 on kotti and mordor (#11338) +* tx-q: enable basic verification of local transactions (#11332) +* remove null signatures (#11335) +* ethcore/res: activate agharta on classic 9573000 (#11331) +* [secretstore] migrate to version 4 (#11322) +* Enable EIP-2384 for ice age hard fork (#11281) +* Fix atomicity violation in network-devp2p (#11277) +* Istanbul activation on xDai (#11299) +* Istanbul activation on POA Core (#11298) +* Adds support for ipc socket permissions (#11273) +* Add check for deserialising hex values over U256 limit (#11309) +* validate-chainspecs: check istanbul eips are in the foundation spec (#11305) +* [chainspec]: add `eip1344_transition` for istanbul (#11301) +* only add transactions to signing-queue if it is enabled (#11272) +* Use upstream rocksdb (#11248) +* Treat only blocks in queue as synced (#11264) +* add support for evan.network chains (#11289) +* Add benchmarks and tests for RlpNodeCodec decoding (#11287) +* upgrade vergen to 3.0 (#11293) +* interruptible test and build jobs (#11294) +* Istanbul HF on POA Sokol (#11282) +* [ethcore]: apply filter when `PendingSet::AlwaysQueue` in `ready_transactions_filtered` (#11227) +* Update lib.rs (#11286) +* Don't prune ancient state when instantiating a Client (#11270) +* fixed verify_uncles error type (#11276) +* ethcore: fix rlp deprecation warnings (#11280) +* Upgrade trie-db to 0.16.0. (#11274) +* Clarify what first_block `None` means (#11269) +* removed redundant VMType enum with one variant (#11266) +* Ensure jsonrpc threading settings are sane (#11267) +* Return Ok(None) when the registrar contract returns empty slice (#11257) +* Add a benchmark for snapshot::account::to_fat_rlps() (#11185) +* Fix misc compile warnings (#11258) +* simplify verification (#11249) +* update ropsten forkCanonHash, forkBlock (#11247) +* Make InstantSeal Instant again (#11186) +* ropsten #6631425 foundation #8798209 (#11201) +* Update list of bootnodes for xDai chain (#11236) +* ethcore/res: add mordor testnet configuration (#11200) +* [chain specs]: activate `Istanbul` on mainnet (#11228) +* [builtin]: support `multiple prices and activations` in chain spec (#11039) +* Insert explicit warning into the panic hook (#11225) +* Snapshot restoration overhaul (#11219) +* Fix docker centos build (#11226) +* retry on gitlab system failures (#11222) +* Update bootnodes. (#11203) +* Use provided usd-per-eth value if an endpoint is specified (#11209) +* Use a lock instead of atomics for snapshot Progress (#11197) +* [informant]: `MillisecondDuration` -> `as_millis()` (#11211) +* Step duration map configuration parameter ported from the POA Network fork (#10902) +* Upgrade jsonrpc to latest (#11206) +* [export hardcoded sync]: use debug for `H256` (#11204) +* Pause pruning while snapshotting (#11178) +* Type annotation for next_key() matching of json filter options (#11192) +* Crypto primitives removed from ethkey (#11174) +* Made ecrecover implementation trait public (#11188) +* Remove unused macro_use. (#11191) +* [dependencies]: jsonrpc `14.0.1` (#11183) +* [receipt]: add `sender` & `receiver` to `RichReceipts` (#11179) +* [dependencies] bump rand 0.7 (#11022) +* [ethcore/builtin]: do not panic in blake2pricer on short input (#11180) +* TxPermissions ver 3: gas price & data (#11170) +* [ethash] chainspec validate `ecip1017EraRounds` non-zero (#11123) +* util Host: fix a double Read Lock bug in fn Host::session_readable() (#11175) +* ethcore client: fix a double Read Lock bug in fn Client::logs() (#11172) +* Aura: Report malice on sibling blocks from the same validator (#11160) +* Change how RPCs eth_call and eth_estimateGas handle "Pending" (#11127) +* Cleanup stratum a bit (#11161) +* [keccak-hasher]: rust2018 (#11163) +* Upgrade to jsonrpc v14 (#11151) +* Secret store: fix Instant::now() related race in net_keep_alive (#11155) +* RPC method for clearing the engine signer (#10920) +* Use TryFrom instead of From+panic for Builtin (#11140) +* Fix sccache statistics (#11145) +* Update ethereum types to 0.8.0 version (#11139) +* [json]: add docs to `hardfork specification` (#11138) +* ServiceTransactionChecker::refresh_cache: allow registrar unavailable (#11126) +* Fix some random typos, formatting/whitespace (#11128) +* Refactor parity_listStorageKeys with count parameter optional (#11124) +* Make EIP712Domain Fields Optional (#11103) +* EIP-712: bump version in prep for publishing (#11106) +* move StateResult to `common-types` (#11121) +* Deduplicate registrar contract & calling logic (#11110) +* Refactor return type of `BlockChainClient::code` #7098 (#11102) +* Switching sccache from local to Redis (#10971) +* SIMD Implementation for EIP-152 (#11056) +* Fix deprecated trait objects without an explicit `dyn` (#11112) +* [spec] fix rinkeby spec (#11108) +* Update to latest jsonrpc (#11111) +* use images from our registry (#11105) +* Correct EIP-712 encoding (#11092) +* [CI] check evmbin build (#11096) +* Update `kvdb`, `kvdb-rocksdb` and `h2` (#11091) +* [client]: Fix for incorrectly dropped consensus messages (#11082) (#11086) +* Update JSON tests to d4f86ecf4aa7c (#11054) +* fix(network): typo (#11088) +* [ethash] remove manual unrolling (#11069) +* ethcore/res: activate Istanbul on Ropsten, Görli, Rinkeby, Kovan (#11068) +* [sync]: rust 2018 (#11067) +* [ethcore]: move client test types to test-helpers (#11062) +* [sync]: remove unused dependencies or make dev (#11061) +* [ethcore]: reduce re-exports (#11059) +* [evmbin] fix time formatting (#11060) +* Update hardcoded headers (foundation, classic, kovan, xdai, ewc, ...) (#11053) +* cargo update -p eth-secp256k1 (#11052) +* ethcore: remove `test-helper feat` from build (#11047) +* Include test-helpers from ethjson (#11045) +* [ethcore]: cleanup dependencies (#11043) +* add more tx tests (#11038) +* Fix parallel transactions race-condition (#10995) +* [ethcore]: make it compile without `test-helpers` feature (#11036) +* Benchmarks for block verification (#11035) +* Move snapshot related traits to their proper place (#11012) +* cleanup json crate (#11027) +* [spec] add istanbul test spec (#11033) +* [json-spec] make blake2 pricing spec more readable (#11034) +* Add blake2_f precompile (#11017) +* Add new line after writing block to hex file. (#10984) +* fix: remove unused error-chain (#11028) +* fix: remove needless use of itertools (#11029) +* Convert `std::test` benchmarks to use Criterion (#10999) +* Fix block detail updating (#11015) +* [trace] introduce trace failed to Ext (#11019) +* cli: update usage and version headers (#10924) +* [private-tx] remove unused rand (#11024) +* Extract snapshot to own crate (#11010) +* Edit publish-onchain.sh to use https (#11016) +* EIP 1108: Reduce alt_bn128 precompile gas costs (#11008) +* Fix deadlock in `network-devp2p` (#11013) +* Implement EIP-1283 reenable transition, EIP-1706 and EIP-2200 (#10191) +* EIP 1884 Re-pricing of trie-size dependent operations (#10992) +* xDai chain support and nodes list update (#10989) +* [trace] check mem diff within range (#11002) +* EIP-1344 Add CHAINID op-code (#10983) +* Make ClientIoMessage generic over the Client (#10981) +* bump spin to 0.5.2 (#10996) +* fix compile warnings (#10993) +* Fix compilation on recent nightlies (#10991) +* [ipfs] Convert to edition 2018 (#10979) +* Extract spec to own crate (#10978) +* EIP 2028: transaction gas lowered from 68 to 16 (#10987) +* Extract engines to own crates (#10966) +* Configuration map of block reward contract addresses (#10875) +* Add a 2/3 quorum option to Authority Round. (#10909) +* Fix rlp decode for inline trie nodes. (#10980) +* Private contract migration and offchain state sync (#10748) +* manual publish jobs for releases, no changes for nightlies (#10977) +* Extract the Engine trait (#10958) +* Better error message for rpc gas price errors (#10931) +* [.gitlab.yml] cargo check ethcore benches (#10965) +* Verify transaction against its block during import (#10954) +* [evmbin] fix compilation (#10976) +* Update to latest trie version. (#10972) +* [blooms-db] Fix benchmarks (#10974) +* Fix ethcore/benches build. (#10964) +* tx-pool: accept local tx with higher gas price when pool full (#10901) +* Disable unsyncable expanse chain (#10926) +* Extract Machine from ethcore (#10949) +* removed redundant state_root function from spec, improve spec error types (#10955) +* Add support for Energy Web Foundation's new chains (#10957) +* [evmbin] add more tests to main.rs (#10956) +* Fix compiler warnings in util/io and upgrade to edition 2018 Upgrade mio to latest (#10953) +* unify loading spec && further spec cleanups (#10948) +* refactor: Refactor evmbin CLI (#10742) +* journaldb changes (#10929) +* Allow default block parameter to be blockHash (#10932) +* Enable sealing when engine is ready (#10938) +* Fix some warnings and typos. (#10941) +* Updated security@parity.io key (#10939) +* Change the return type of step_inner function. (#10940) +* get rid of hidden mutability of Spec (#10904) +* simplify BlockReward::reward implementation (#10906) +* Kaspersky AV whitelisting (#10919) +* additional arithmetic EVM opcode benchmarks (#10916) +* [Cargo.lock] cargo update -p crossbeam-epoch (#10921) +* Fixes incorrect comment. (#10913) +* Add file path to disk map write/read warnings (#10911) +* remove verify_transaction_unordered from engine (#10891) +* Avast whitelist script (#10900) +* cleanup ethcore ethereum module (#10899) +* Move more types out of ethcore (#10880) +* return block nonce when engine is clique (#10892) +* TransactionQueue::import accepts iterator (#10889) +* rename is_pruned to is_prunable (#10888) +* simplify create_address_scheme (#10890) +* Move DatabaseExtras back to trace (#10868) +* Update README.md and Changelogs (#10866) +* whisper is no longer a part of parity-ethereum repo (#10855) +* [ethash] remove mem::uninitialized (#10861) +* Docker images renaming (#10863) +* Move the substate module into ethcore/executive (#10867) +* Run cargo fix on a few of the worst offenders (#10854) +* removed redundant fork choice abstraction (#10849) +* Extract state-db from ethcore (#10858) +* Fix fork choice (#10837) +* Move more code into state-account (#10840) +* Remove compiler warning (#10865) +* [ethash] use static_assertions crate (#10860) +* EIP-1702: Generalized Account Versioning Scheme (#10771) +* ethcore-builtin (#10850) +* removed QueueError type (#10852) +* removed unused macros (#10851) +* bump crossbeam (#10848) +* removed unused trait PrivateNotify and unused Error types (#10847) +* make fn submit_seal more idiomatic (#10843) +* update parking-lot to 0.8 (#10845) +* Update version to 2.7.0 (#10846) +* update jsonrpc to 12.0 (#10841) +* Improve logging and cleanup in miner around block sealing (#10745) +* Extract AccountDB to account-db (#10839) +* test: Update Whisper test for invalid pool size (#10811) +* Extricate PodAccount and state Account to own crates (#10838) +* logs (#10817) +* refactor: whisper: Add type aliases and update rustdocs in message.rs (#10812) +* Break circular dependency between Client and Engine (part 1) (#10833) +* tests: Relates to #10655: Test instructions for Readme (#10835) +* refactor: Related #9459 - evmbin: replace untyped json! macro with fully typed serde serialization using Rust structs (#10657) +* idiomatic changes to PodState (#10834) +* Allow --nat extip:your.host.here.org (#10830) +* When updating the client or when called from RPC, sleep should mean sleep (#10814) +* Remove excessive warning (#10831) +* Fix typo in README.md (#10828) +* ethcore does not use byteorder (#10829) +* Better logging when backfilling ancient blocks fail (#10796) +* depends: Update wordlist to v1.3 (#10823) +* cargo update -p smallvec (#10822) +* replace memzero with zeroize crate (#10816) +* Don't repeat the logic from Default impl (#10813) +* removed additional_params method (#10818) +* Add Constantinople eips to the dev (instant_seal) config (#10809) +* removed redundant fmt::Display implementations (#10806) +* revert changes to .gitlab-ci.yml (#10807) +* Add filtering capability to `parity_pendingTransactions` (issue 8269) (#10506) +* removed EthEngine alias (#10805) +* wait a bit longer in should_check_status_of_request_when_its_resolved (#10808) +* Do not drop the peer with None difficulty (#10772) +* ethcore-bloom-journal updated to 2018 (#10804) +* ethcore-light uses bincode 1.1 (#10798) +* Fix a few typos and unused warnings. (#10803) +* updated project to ansi_term 0.11 (#10799) +* added new ropsten-bootnode and removed old one (#10794) +* updated price-info to edition 2018 (#10801) +* ethcore-network-devp2p uses igd 0.9 (#10797) +* updated parity-local-store to edition 2018 and removed redundant Error type (#10800) +* Cleanup unused vm dependencies (#10787) +* Removed redundant ethcore-service error type (#10788) +* Removed machine abstraction from ethcore (#10791) +* Updated blooms-db to rust 2018 and removed redundant deps (#10785) +* ethkey no longer uses byteorder (#10786) +* Log validator set changes in EpochManager (#10734) +* Treat empty account the same as non-exist accounts in EIP-1052 (#10775) +* docs: Update Readme with TOC, Contributor Guideline. Update Cargo package descriptions (#10652) +* Move Engine::register_client to be before other I/O handler registration (#10767) +* Print warnings when using dangerous settings for ValidatorSet (#10733) +* ethcore/res: activate atlantis classic hf on block 8772000 (#10766) +* refactor: Fix indentation (#10740) +* Updated Bn128PairingImpl to use optimized batch pairing (#10765) +* fix: aura don't add `SystemTime::now()` (#10720) +* Initialize private tx logger only if private tx functionality is enabled (#10758) +* Remove unused code (#10762) +* Remove calls to heapsize (#10432) +* [devp2p] Update to 2018 edition (#10716) +* Add a way to signal shutdown to snapshotting threads (#10744) +* Enable aesni (#10756) +* remove support of old SS db formats (#10757) +* [devp2p] Don't use `rust-crypto` (#10714) +* updater: fix static id hashes initialization (#10755) +* Use fewer threads for snapshotting (#10752) +* Die error_chain, die (#10747) +* Fix deprectation warnings on nightly (#10746) +* fix docker tags for publishing (#10741) +* DevP2p: Get node IP address and udp port from Socket, if not included in PING packet (#10705) +* ethcore: enable ECIP-1054 for classic (#10731) +* Stop breaking out of loop if a non-canonical hash is found (#10729) +* Refactor Clique stepping (#10691) +* Use RUSTFLAGS to set the optimization level (#10719) +* SecretStore: non-blocking wait of session completion (#10303) +* removed secret_store folder (#10722) +* SecretStore: expose restore_key_public in HTTP API (#10241) +* Revert "enable lto for release builds (#10717)" (#10721) +* enable lto for release builds (#10717) +* Merge `Notifier` and `TransactionsPoolNotifier` (#10591) +* [devp2p] Fix warnings and re-org imports (#10710) +* Upgrade ethereum types (#10670) +* introduce MissingParent Error, fixes #10699 (#10700) +* Update publishing (#10644) +* Upgrade to parity-crypto 0.4 (#10650) +* new image (#10673) +* Add SealingState; don't prepare block when not ready. (#10529) +* Fix compiler warning (that will become an error) (#10683) +* add_sync_notifier in EthPubSubClient holds on to a Client for too long (#10689) +* Don't panic if extra_data is longer than VANITY_LENGTH (#10682) +* docs: evmbin - Update Rust docs (#10658) +* Remove annoying compiler warnings (#10679) +* Reset blockchain properly (#10669) +* Remove support for hardware wallets (#10678) +* [CI] allow cargo audit to fail (#10676) +* docs: Add ProgPoW Rust docs to ethash module (#10653) +* fix: Move PR template into .github/ folder (#10663) +* docs: Add PR template (#10654) +* Trivial journal for private transactions (#10056) +* fix(compilation warnings) (#10649) +* [whisper] Move needed aes_gcm crypto in-crate (#10647) +* Adds parity_getRawBlockByNumber, parity_submitRawBlock (#10609) +* Fix rinkeby petersburg fork (#10632) +* ci: publish docs debug (#10638) +* Fix publish docs (#10635) +* Update kovan.json to switch validator set to POA Consensus Contracts (#10628) +* [ethcore] remove error_chain (#10616) +* Remove unused import (#10615) +* evm: add some mulmod benches (#10600) +* Clique: zero-fill extradata when the supplied value is less than 32 bytes in length (#10605) +* Constantinople HF on POA Core (#10606) +* adds rpc error message for --no-ancient-blocks (#10608) +* Allow CORS requests in Secret Store API (#10584) +* update bootnodes (#10595) +* sccache logs to stdout (#10596) +* fix(whisper expiry): current time + work + ttl (#10587) +* CI improvements (#10579) +* Watch transactions pool (#10558) +* fix(evmbin): make benches compile again (#10586) +* fix issue with compilation when 'slow-blocks' feature enabled (#10585) +* Reject crazy timestamps instead of truncating. (#10574) +* Node table limiting and cache for node filter (#10288) +* fix(light cull): poll light cull instead of timer (#10559) +* Update Issue Template to direct security issue to email (#10562) +* RPC: Implements eth_subscribe("syncing") (#10311) +* Explicitly enable or disable Stratum in config file (Issue 9785) (#10521) +* version: bump master to 2.6 (#10560) diff --git a/docs/CHANGELOG-3.0.md b/docs/CHANGELOG-3.0.md new file mode 100644 index 00000000000..c7f1a768064 --- /dev/null +++ b/docs/CHANGELOG-3.0.md @@ -0,0 +1,122 @@ +## OpenEthereum [v3.0.1](https://github.com/openethereum/openethereum/releases/tag/v3.0.1) +- Add missing forks to fork ID (#11747) + +## OpenEthereum [v3.0.0](https://github.com/openethereum/openethereum/releases/tag/v3.0.0) + +OpenEthereum v3.0.0 is the first release of OpenEthereum client as part of OpenEthereum project, divested from Parity Technologies. + +This release marks the transition from Parity Technologies infrastructure and bootnodes to the one managed by OpenEthereum project. + +Parity-Ethereum v2.7 users can continue using their existing data folders. Command-line interface has also stayed identical. +Unless specified otherwise, OpenEthereum v3.0.0 will detect if the user's database in the old Parity-Ethereum default path, +and only if it's not found will write to the new default location. + +This release includes several major improvements to network and database stack: +- Support for `eth/64` protocol and Node Discovery v4 `Ethereum Node Records` extension. +- Accounts bloom is removed which should decrease the database size. + +**Due to database changes this is a one-way upgrade. Please back up your database if you plan to continue using Parity-Ethereum v2.7.2.** + +Note that this release drops support for IPFS and `eth/62` protocol. Additionally, it marks light client, private transactions and updater as deprecated features which may be removed in a future release. + +The full list of included changes: +- Add deprecation warnings (#11682) +- Add Curl to Docker image (#11687) +- v3 release version strings and track stable (#11680) +- Fix sccache server errors (#11675) +- Don't delete old db after migration (#11662) +- rename inject to drain_transaction_overlay (#11657) +- Drain the transaction overlay (#11654) +- vergen library seems to depend not only on the .git folder content but also on the git binary (#11651) +- New default paths (#11641) +- Update EWF's chains with Istanbul transition block numbers (#11482) +- add openethereum supplementary bootnodes, those are not active right now, but will be in case the network needs more power (#11650) +- Remove Parity bootnodes (#11644) +- Remove accounts bloom (#11589) +- Deploy docker images on master (#11640) +- Fix some compiler warnings (#11632) +- Add support for non-fork side of Phoenix (#11627) +- validate mainnet specs against all forks (#11625) +- Fix ecrecover builtin (#11623) +- Update .gitmodules (#11628) +- ethcore/res: activate ecip-1088 phoenix on classic (#11598) +- Upgrade parity-common deps to latest (#11620) +- Fix Goerli syncing (#11604) +- deps: switch to upstream ctrlc (#11617) +- Deduplicating crate dependencies (part 3 of n) (#11614) +- Deduplicating crate dependencies (part 2 of n, `slab`) (#11613) +- Actually save ENR on creation and modification (#11602) +- Activate POSDAO on xDai chain and update bootnodes (#11610) +- Activate on-chain randomness in POA Core (#11609) +- Deduplicating crate dependencies (part 1 of n) (#11606) +- Update enodes for POA Sokol (#11611) +- Remove .git folder from dogerignore file so vergen library can get build date and commit hash in the binary generatio vergen library can get build date and commit hash in the binary generation (#11608) +- Reduced gas cost for static calls made to precompiles EIP2046/1352 (#11583) +- `ethcore-bloom-journal` was renamed to `accounts-bloom` (#11605) +- Use serde_json to export hardcoded sync (#11601) +- Node Discovery v4 ENR Extension (EIP-868) (#11540) +- Fix compile warnings (#11595) +- Update version to 3.0.0-alpha.1 (#11592) +- ethcore/res: bump canon fork hash for mordor and kotti testnets (#11584) +- Update on push tags (#11590) +- Replace deprecated tempdir dependency with tempfile (#11588) +- Fix project name, links, rename the binaries (#11580) +- Update Cargo.lock (#11573) +- ci: workaround for the cache bug on Linux (#11568) +- Increase the default pruning parameters (#11558) +- Add Docker build and push to github actions (#11555) +- Update README (#11578) +- informant: display I/O stats (#11523) +- [devp2p discovery]: remove `deprecated_echo_hash` (#11564) +- [secretstore] create db_version file when database doesn't exist (#11570) +- Remove Parity's Security Policy (#11565) +- ethcore/res: enable ecip-1088 phoenix upgrade for kotti and mordor testnets (#11529) +- Misc docs and renames …and one less clone (#11556) +- [secretstore]: don't sign message with only zeroes (#11561) +- [devp2p discovery]: cleanup (#11547) +- Code cleanup in the sync module (#11552) +- initial cleanup (#11542) +- Warn if genesis constructor revert (#11550) +- ethcore: cleanup after #11531 (#11546) +- license update (#11543) +- Less cloning when importing blocks (#11531) +- Github Actions (#11528) +- Fix Alpine Dockerfile (#11538) +- Remove AuxiliaryData/AuxiliaryRequest (#11533) +- [journaldb]: cleanup (#11534) +- Remove references to parity-ethereum (#11525) +- Drop IPFS support (#11532) +- chain-supplier: fix warning reporting for GetNodeData request (#11530) +- Faster kill_garbage (#11514) +- [EngineSigner]: don't sign message with only zeroes (#11524) +- fix compilation warnings (#11522) +- [ethcore cleanup]: various unrelated fixes from `#11493` (#11507) +- Add benchmark for transaction execution (#11509) +- Add Smart Contract License v1.0 +- Misc fixes (#11510) +- [dependencies]: unify `rustc-hex` (#11506) +- Activate on-chain randomness in POA Sokol (#11505) +- Grab bag of cleanup (#11504) +- Implement eth/64 (EIP-2364) and drop support for eth/62 (#11472) +- [dependencies]: remove `util/macros` (#11501) +- OpenEthereum bootnodes are added (#11499) +- [ci benches]: use `all-features` (#11496) +- [verification]: make test-build compile standalone (#11495) +- complete null-signatures removal (#11491) +- Include the seal when populating the header for a new block (#11475) +- fix compilation warnings (#11492) +- cargo update -p cmake (#11490) +- update to published rlp-derive (#11489) +- Switch usage of Secret Store to the external lib (#11487) +- Switch from the internal runtime lib to external one from crates.io (#11480) +- Update params.rs (#11474) +- weak_counts has been stabilized (#11476) +- sync: remove broken eth_protocol_version method (#11473) +- Use parity-crypto updated to use upstream rust-secp256k1 (#11406) +- Cleanup some code in Aura (#11466) +- upgrade some of the dependencies (#11467) +- Some more release track changes to README.md (#11465) +- Update simple one-line installer due to switching to a single stable release track (#11463) +- update Dockerfile (#11461) +- Implement EIP-2124 (#11456) +- [eth classic chainspec]: remove `balance = 1` (#11459) \ No newline at end of file diff --git a/ethash/Cargo.toml b/ethash/Cargo.toml index 929895aca78..06ee93f8892 100644 --- a/ethash/Cargo.toml +++ b/ethash/Cargo.toml @@ -1,4 +1,5 @@ [package] +description = "Parity Ethereum Ethash & ProgPoW Implementations" name = "ethash" version = "1.12.0" authors = ["Parity Technologies "] @@ -26,7 +27,9 @@ bench = [] [[bench]] name = "basic" harness = false +required-features = ['bench'] [[bench]] name = "progpow" harness = false +required-features = ['bench'] diff --git a/ethash/benches/basic.rs b/ethash/benches/basic.rs index 5bc10e948e0..8e60a698b08 100644 --- a/ethash/benches/basic.rs +++ b/ethash/benches/basic.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . #[macro_use] extern crate criterion; @@ -21,93 +21,106 @@ extern crate ethash; use criterion::Criterion; use ethash::{NodeCacheBuilder, OptimizeFor}; -const HASH: [u8; 32] = [0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, - 0xe4, 0x0a, 0xb3, 0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, - 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94, 0x05, 0x52, 0x7d, 0x72]; +const HASH: [u8; 32] = [ + 0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, 0xe4, 0x0a, 0xb3, 0x35, 0x8a, + 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94, 0x05, 0x52, 0x7d, 0x72, +]; const NONCE: u64 = 0xd7b3ac70a301a249; criterion_group!( - basic, - bench_light_compute_memmap, - bench_light_compute_memory, - bench_light_new_round_trip_memmap, - bench_light_new_round_trip_memory, - bench_light_from_file_round_trip_memory, - bench_light_from_file_round_trip_memmap + basic, + bench_light_compute_memmap, + bench_light_compute_memory, + bench_light_new_round_trip_memmap, + bench_light_new_round_trip_memory, + bench_light_from_file_round_trip_memory, + bench_light_from_file_round_trip_memmap ); criterion_main!(basic); fn bench_light_compute_memmap(b: &mut Criterion) { - use std::env; + use std::env; - let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); - let light = builder.light(&env::temp_dir(), 486382); + let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); + let light = builder.light(&env::temp_dir(), 486382); - b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| light.compute(&HASH, NONCE, u64::max_value()))); + b.bench_function("bench_light_compute_memmap", move |b| { + b.iter(|| light.compute(&HASH, NONCE, u64::max_value())) + }); } fn bench_light_compute_memory(b: &mut Criterion) { - use std::env; + use std::env; - let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value()); - let light = builder.light(&env::temp_dir(), 486382); + let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value()); + let light = builder.light(&env::temp_dir(), 486382); - b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| light.compute(&HASH, NONCE, u64::max_value()))); + b.bench_function("bench_light_compute_memmap", move |b| { + b.iter(|| light.compute(&HASH, NONCE, u64::max_value())) + }); } fn bench_light_new_round_trip_memmap(b: &mut Criterion) { - use std::env; - - b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| { - let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); - let light = builder.light(&env::temp_dir(), 486382); - light.compute(&HASH, NONCE, u64::max_value()); - })); + use std::env; + + b.bench_function("bench_light_compute_memmap", move |b| { + b.iter(|| { + let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); + let light = builder.light(&env::temp_dir(), 486382); + light.compute(&HASH, NONCE, u64::max_value()); + }) + }); } fn bench_light_new_round_trip_memory(b: &mut Criterion) { - use std::env; - - b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| { - let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value()); - let light = builder.light(&env::temp_dir(), 486382); - light.compute(&HASH, NONCE, u64::max_value()); - })); + use std::env; + + b.bench_function("bench_light_compute_memmap", move |b| { + b.iter(|| { + let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value()); + let light = builder.light(&env::temp_dir(), 486382); + light.compute(&HASH, NONCE, u64::max_value()); + }) + }); } fn bench_light_from_file_round_trip_memory(b: &mut Criterion) { - use std::env; - - let dir = env::temp_dir(); - let height = 486382; - { - let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value()); - let mut dummy = builder.light(&dir, height); - dummy.to_file().unwrap(); - } - - b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| { - let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value()); - let light = builder.light_from_file(&dir, 486382).unwrap(); - light.compute(&HASH, NONCE, u64::max_value()); - })); + use std::env; + + let dir = env::temp_dir(); + let height = 486382; + { + let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value()); + let mut dummy = builder.light(&dir, height); + dummy.to_file().unwrap(); + } + + b.bench_function("bench_light_compute_memmap", move |b| { + b.iter(|| { + let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value()); + let light = builder.light_from_file(&dir, 486382).unwrap(); + light.compute(&HASH, NONCE, u64::max_value()); + }) + }); } fn bench_light_from_file_round_trip_memmap(b: &mut Criterion) { - use std::env; - - let dir = env::temp_dir(); - let height = 486382; - - { - let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); - let mut dummy = builder.light(&dir, height); - dummy.to_file().unwrap(); - } - - b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| { - let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); - let light = builder.light_from_file(&dir, 486382).unwrap(); - light.compute(&HASH, NONCE, u64::max_value()); - })); + use std::env; + + let dir = env::temp_dir(); + let height = 486382; + + { + let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); + let mut dummy = builder.light(&dir, height); + dummy.to_file().unwrap(); + } + + b.bench_function("bench_light_compute_memmap", move |b| { + b.iter(|| { + let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); + let light = builder.light_from_file(&dir, 486382).unwrap(); + light.compute(&HASH, NONCE, u64::max_value()); + }) + }); } diff --git a/ethash/benches/progpow.rs b/ethash/benches/progpow.rs index e086a14b42f..939818e7b0c 100644 --- a/ethash/benches/progpow.rs +++ b/ethash/benches/progpow.rs @@ -7,80 +7,71 @@ extern crate tempdir; use criterion::Criterion; use ethash::progpow; -use tempdir::TempDir; +use ethash::{compute::light_compute, NodeCacheBuilder, OptimizeFor}; use rustc_hex::FromHex; -use ethash::{NodeCacheBuilder, OptimizeFor}; -use ethash::compute::light_compute; +use tempdir::TempDir; fn bench_hashimoto_light(c: &mut Criterion) { - let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); - let tempdir = TempDir::new("").unwrap(); - let light = builder.light(&tempdir.path(), 1); - let h = FromHex::from_hex("c9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f").unwrap(); - let mut hash = [0; 32]; - hash.copy_from_slice(&h); + let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); + let tempdir = TempDir::new("").unwrap(); + let light = builder.light(&tempdir.path(), 1); + let h = FromHex::from_hex("c9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f") + .unwrap(); + let mut hash = [0; 32]; + hash.copy_from_slice(&h); - c.bench_function("hashimoto_light", move |b| { - b.iter(|| light_compute(&light, &hash, 0)) - }); + c.bench_function("hashimoto_light", move |b| { + b.iter(|| light_compute(&light, &hash, 0)) + }); } fn bench_progpow_light(c: &mut Criterion) { - let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); - let tempdir = TempDir::new("").unwrap(); - let cache = builder.new_cache(tempdir.into_path(), 0); + let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); + let tempdir = TempDir::new("").unwrap(); + let cache = builder.new_cache(tempdir.into_path(), 0); - let h = FromHex::from_hex("c9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f").unwrap(); - let mut hash = [0; 32]; - hash.copy_from_slice(&h); + let h = FromHex::from_hex("c9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f") + .unwrap(); + let mut hash = [0; 32]; + hash.copy_from_slice(&h); - c.bench_function("progpow_light", move |b| { - b.iter(|| { - let c_dag = progpow::generate_cdag(cache.as_ref()); - progpow::progpow( - hash, - 0, - 0, - cache.as_ref(), - &c_dag, - ); - }) - }); + c.bench_function("progpow_light", move |b| { + b.iter(|| { + let c_dag = progpow::generate_cdag(cache.as_ref()); + progpow::progpow(hash, 0, 0, cache.as_ref(), &c_dag); + }) + }); } fn bench_progpow_optimal_light(c: &mut Criterion) { - let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); - let tempdir = TempDir::new("").unwrap(); - let cache = builder.new_cache(tempdir.into_path(), 0); - let c_dag = progpow::generate_cdag(cache.as_ref()); + let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); + let tempdir = TempDir::new("").unwrap(); + let cache = builder.new_cache(tempdir.into_path(), 0); + let c_dag = progpow::generate_cdag(cache.as_ref()); - let h = FromHex::from_hex("c9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f").unwrap(); - let mut hash = [0; 32]; - hash.copy_from_slice(&h); + let h = FromHex::from_hex("c9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f") + .unwrap(); + let mut hash = [0; 32]; + hash.copy_from_slice(&h); - c.bench_function("progpow_optimal_light", move |b| { - b.iter(|| { - progpow::progpow( - hash, - 0, - 0, - cache.as_ref(), - &c_dag, - ); - }) - }); + c.bench_function("progpow_optimal_light", move |b| { + b.iter(|| { + progpow::progpow(hash, 0, 0, cache.as_ref(), &c_dag); + }) + }); } fn bench_keccak_f800_long(c: &mut Criterion) { - c.bench_function("keccak_f800_long(0, 0, 0)", |b| { - b.iter(|| progpow::keccak_f800_long([0; 32], 0, [0; 8])) - }); + c.bench_function("keccak_f800_long(0, 0, 0)", |b| { + b.iter(|| progpow::keccak_f800_long([0; 32], 0, [0; 8])) + }); } -criterion_group!(benches, - bench_hashimoto_light, - bench_progpow_light, - bench_progpow_optimal_light, - bench_keccak_f800_long, +criterion_group!( + benches, + bench_hashimoto_light, + bench_progpow_light, + bench_progpow_optimal_light, + bench_keccak_f800_long, ); criterion_main!(benches); diff --git a/ethash/src/cache.rs b/ethash/src/cache.rs index b16d2731457..ab3407a07ef 100644 --- a/ethash/src/cache.rs +++ b/ethash/src/cache.rs @@ -1,306 +1,313 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use compute::Light; use either::Either; -use keccak::{H256, keccak_512}; +use keccak::{keccak_512, H256}; use memmap::MmapMut; use parking_lot::Mutex; use seed_compute::SeedHashCompute; -use shared::{ETHASH_CACHE_ROUNDS, NODE_BYTES, NODE_DWORDS, Node, epoch, get_cache_size, to_hex}; +use shared::{epoch, get_cache_size, to_hex, Node, ETHASH_CACHE_ROUNDS, NODE_BYTES, NODE_DWORDS}; -use std::borrow::Cow; -use std::fs; -use std::io::{self, Read, Write}; -use std::path::{Path, PathBuf}; -use std::slice; -use std::sync::Arc; +use std::{ + borrow::Cow, + fs, + io::{self, Read, Write}, + path::{Path, PathBuf}, + slice, + sync::Arc, +}; type Cache = Either, MmapMut>; #[derive(PartialEq, Eq, Debug, Clone, Copy)] pub enum OptimizeFor { - Cpu, - Memory, + Cpu, + Memory, } impl Default for OptimizeFor { - fn default() -> Self { - OptimizeFor::Cpu - } + fn default() -> Self { + OptimizeFor::Cpu + } } fn byte_size(cache: &Cache) -> usize { - use self::Either::{Left, Right}; + use self::Either::{Left, Right}; - match *cache { - Left(ref vec) => vec.len() * NODE_BYTES, - Right(ref mmap) => mmap.len(), - } + match *cache { + Left(ref vec) => vec.len() * NODE_BYTES, + Right(ref mmap) => mmap.len(), + } } fn new_buffer(path: &Path, num_nodes: usize, ident: &H256, optimize_for: OptimizeFor) -> Cache { - let memmap = match optimize_for { - OptimizeFor::Cpu => None, - OptimizeFor::Memory => make_memmapped_cache(path, num_nodes, ident).ok(), - }; - - memmap.map(Either::Right).unwrap_or_else(|| { - Either::Left(make_memory_cache(num_nodes, ident)) - }) + let memmap = match optimize_for { + OptimizeFor::Cpu => None, + OptimizeFor::Memory => make_memmapped_cache(path, num_nodes, ident).ok(), + }; + + memmap + .map(Either::Right) + .unwrap_or_else(|| Either::Left(make_memory_cache(num_nodes, ident))) } #[derive(Clone)] pub struct NodeCacheBuilder { - // TODO: Remove this locking and just use an `Rc`? - seedhash: Arc>, - optimize_for: OptimizeFor, - progpow_transition: u64, + // TODO: Remove this locking and just use an `Rc`? + seedhash: Arc>, + optimize_for: OptimizeFor, + progpow_transition: u64, } // TODO: Abstract the "optimize for" logic pub struct NodeCache { - builder: NodeCacheBuilder, - cache_dir: Cow<'static, Path>, - cache_path: PathBuf, - epoch: u64, - cache: Cache, + builder: NodeCacheBuilder, + cache_dir: Cow<'static, Path>, + cache_path: PathBuf, + epoch: u64, + cache: Cache, } impl NodeCacheBuilder { - pub fn light(&self, cache_dir: &Path, block_number: u64) -> Light { - Light::new_with_builder(self, cache_dir, block_number, self.progpow_transition) - } - - pub fn light_from_file(&self, cache_dir: &Path, block_number: u64) -> io::Result { - Light::from_file_with_builder(self, cache_dir, block_number, self.progpow_transition) - } - - pub fn new>>(optimize_for: T, progpow_transition: u64) -> Self { - NodeCacheBuilder { - seedhash: Arc::new(Mutex::new(SeedHashCompute::default())), - optimize_for: optimize_for.into().unwrap_or_default(), - progpow_transition - } - } - - fn block_number_to_ident(&self, block_number: u64) -> H256 { - self.seedhash.lock().hash_block_number(block_number) - } - - fn epoch_to_ident(&self, epoch: u64) -> H256 { - self.seedhash.lock().hash_epoch(epoch) - } - - pub fn from_file>>( - &self, - cache_dir: P, - block_number: u64, - ) -> io::Result { - let cache_dir = cache_dir.into(); - let ident = self.block_number_to_ident(block_number); - - let path = cache_path(cache_dir.as_ref(), &ident); - - let cache = cache_from_path(&path, self.optimize_for)?; - let expected_cache_size = get_cache_size(block_number); - - if byte_size(&cache) == expected_cache_size { - Ok(NodeCache { - builder: self.clone(), - epoch: epoch(block_number), - cache_dir: cache_dir, - cache_path: path, - cache: cache, - }) - } else { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "Node cache is of incorrect size", - )) - } - } - - pub fn new_cache>>( - &self, - cache_dir: P, - block_number: u64, - ) -> NodeCache { - let cache_dir = cache_dir.into(); - let ident = self.block_number_to_ident(block_number); - - let cache_size = get_cache_size(block_number); - - // We use `debug_assert` since it is impossible for `get_cache_size` to return an unaligned - // value with the current implementation. If the implementation changes, CI will catch it. - debug_assert!(cache_size % NODE_BYTES == 0, "Unaligned cache size"); - let num_nodes = cache_size / NODE_BYTES; - - let path = cache_path(cache_dir.as_ref(), &ident); - let nodes = new_buffer(&path, num_nodes, &ident, self.optimize_for); - - NodeCache { - builder: self.clone(), - epoch: epoch(block_number), - cache_dir: cache_dir.into(), - cache_path: path, - cache: nodes, - } - } + pub fn light(&self, cache_dir: &Path, block_number: u64) -> Light { + Light::new_with_builder(self, cache_dir, block_number, self.progpow_transition) + } + + pub fn light_from_file(&self, cache_dir: &Path, block_number: u64) -> io::Result { + Light::from_file_with_builder(self, cache_dir, block_number, self.progpow_transition) + } + + pub fn new>>(optimize_for: T, progpow_transition: u64) -> Self { + NodeCacheBuilder { + seedhash: Arc::new(Mutex::new(SeedHashCompute::default())), + optimize_for: optimize_for.into().unwrap_or_default(), + progpow_transition, + } + } + + fn block_number_to_ident(&self, block_number: u64) -> H256 { + self.seedhash.lock().hash_block_number(block_number) + } + + fn epoch_to_ident(&self, epoch: u64) -> H256 { + self.seedhash.lock().hash_epoch(epoch) + } + + pub fn from_file>>( + &self, + cache_dir: P, + block_number: u64, + ) -> io::Result { + let cache_dir = cache_dir.into(); + let ident = self.block_number_to_ident(block_number); + + let path = cache_path(cache_dir.as_ref(), &ident); + + let cache = cache_from_path(&path, self.optimize_for)?; + let expected_cache_size = get_cache_size(block_number); + + if byte_size(&cache) == expected_cache_size { + Ok(NodeCache { + builder: self.clone(), + epoch: epoch(block_number), + cache_dir: cache_dir, + cache_path: path, + cache: cache, + }) + } else { + Err(io::Error::new( + io::ErrorKind::InvalidData, + "Node cache is of incorrect size", + )) + } + } + + pub fn new_cache>>( + &self, + cache_dir: P, + block_number: u64, + ) -> NodeCache { + let cache_dir = cache_dir.into(); + let ident = self.block_number_to_ident(block_number); + + let cache_size = get_cache_size(block_number); + + // We use `debug_assert` since it is impossible for `get_cache_size` to return an unaligned + // value with the current implementation. If the implementation changes, CI will catch it. + debug_assert!(cache_size % NODE_BYTES == 0, "Unaligned cache size"); + let num_nodes = cache_size / NODE_BYTES; + + let path = cache_path(cache_dir.as_ref(), &ident); + let nodes = new_buffer(&path, num_nodes, &ident, self.optimize_for); + + NodeCache { + builder: self.clone(), + epoch: epoch(block_number), + cache_dir: cache_dir.into(), + cache_path: path, + cache: nodes, + } + } } impl NodeCache { - pub fn cache_path(&self) -> &Path { - &self.cache_path - } - - pub fn flush(&mut self) -> io::Result<()> { - if let Some(last) = self.epoch.checked_sub(2).map(|ep| { - cache_path(self.cache_dir.as_ref(), &self.builder.epoch_to_ident(ep)) - }) - { - fs::remove_file(last).unwrap_or_else(|error| match error.kind() { - io::ErrorKind::NotFound => (), - _ => warn!("Error removing stale DAG cache: {:?}", error), - }); - } - - consume_cache(&mut self.cache, &self.cache_path) - } + pub fn cache_path(&self) -> &Path { + &self.cache_path + } + + pub fn flush(&mut self) -> io::Result<()> { + if let Some(last) = self + .epoch + .checked_sub(2) + .map(|ep| cache_path(self.cache_dir.as_ref(), &self.builder.epoch_to_ident(ep))) + { + fs::remove_file(last).unwrap_or_else(|error| match error.kind() { + io::ErrorKind::NotFound => (), + _ => warn!("Error removing stale DAG cache: {:?}", error), + }); + } + + consume_cache(&mut self.cache, &self.cache_path) + } } fn make_memmapped_cache(path: &Path, num_nodes: usize, ident: &H256) -> io::Result { - use std::fs::OpenOptions; + use std::fs::OpenOptions; - let file = OpenOptions::new() - .read(true) - .write(true) - .create(true) - .open(&path)?; - file.set_len((num_nodes * NODE_BYTES) as _)?; + let file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(&path)?; + file.set_len((num_nodes * NODE_BYTES) as _)?; - let mut memmap = unsafe { MmapMut::map_mut(&file)? }; + let mut memmap = unsafe { MmapMut::map_mut(&file)? }; - unsafe { initialize_memory(memmap.as_mut_ptr() as *mut Node, num_nodes, ident) }; + unsafe { initialize_memory(memmap.as_mut_ptr() as *mut Node, num_nodes, ident) }; - Ok(memmap) + Ok(memmap) } fn make_memory_cache(num_nodes: usize, ident: &H256) -> Vec { - let mut nodes: Vec = Vec::with_capacity(num_nodes); - // Use uninit instead of unnecessarily writing `size_of::() * num_nodes` 0s - unsafe { - initialize_memory(nodes.as_mut_ptr(), num_nodes, ident); - nodes.set_len(num_nodes); - } - - nodes + let mut nodes: Vec = Vec::with_capacity(num_nodes); + // Use uninit instead of unnecessarily writing `size_of::() * num_nodes` 0s + unsafe { + initialize_memory(nodes.as_mut_ptr(), num_nodes, ident); + nodes.set_len(num_nodes); + } + + nodes } fn cache_path<'a, P: Into>>(path: P, ident: &H256) -> PathBuf { - let mut buf = path.into().into_owned(); - buf.push(to_hex(ident)); - buf + let mut buf = path.into().into_owned(); + buf.push(to_hex(ident)); + buf } fn consume_cache(cache: &mut Cache, path: &Path) -> io::Result<()> { - use std::fs::OpenOptions; - - match *cache { - Either::Left(ref mut vec) => { - let mut file = OpenOptions::new() - .read(true) - .write(true) - .create(true) - .open(&path)?; - - let buf = unsafe { - slice::from_raw_parts_mut(vec.as_mut_ptr() as *mut u8, vec.len() * NODE_BYTES) - }; - - file.write_all(buf).map(|_| ()) - } - Either::Right(ref mmap) => { - mmap.flush() - } - } + use std::fs::OpenOptions; + + match *cache { + Either::Left(ref mut vec) => { + let mut file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(&path)?; + + let buf = unsafe { + slice::from_raw_parts_mut(vec.as_mut_ptr() as *mut u8, vec.len() * NODE_BYTES) + }; + + file.write_all(buf).map(|_| ()) + } + Either::Right(ref mmap) => mmap.flush(), + } } fn cache_from_path(path: &Path, optimize_for: OptimizeFor) -> io::Result { - let memmap = match optimize_for { - OptimizeFor::Cpu => None, - OptimizeFor::Memory => { - let file = fs::OpenOptions::new().read(true).write(true).create(true).open(path)?; - unsafe { MmapMut::map_mut(&file).ok() } - }, - }; - - memmap.map(Either::Right).ok_or(()).or_else(|_| { - read_from_path(path).map(Either::Left) - }) + let memmap = match optimize_for { + OptimizeFor::Cpu => None, + OptimizeFor::Memory => { + let file = fs::OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(path)?; + unsafe { MmapMut::map_mut(&file).ok() } + } + }; + + memmap + .map(Either::Right) + .ok_or(()) + .or_else(|_| read_from_path(path).map(Either::Left)) } fn read_from_path(path: &Path) -> io::Result> { - use std::fs::File; - use std::mem; + use std::{fs::File, mem}; - let mut file = File::open(path)?; + let mut file = File::open(path)?; - let mut nodes: Vec = Vec::with_capacity(file.metadata().map(|m| m.len() as _).unwrap_or( - NODE_BYTES * 1_000_000, - )); - file.read_to_end(&mut nodes)?; + let mut nodes: Vec = Vec::with_capacity( + file.metadata() + .map(|m| m.len() as _) + .unwrap_or(NODE_BYTES * 1_000_000), + ); + file.read_to_end(&mut nodes)?; - nodes.shrink_to_fit(); + nodes.shrink_to_fit(); - if nodes.len() % NODE_BYTES != 0 || nodes.capacity() % NODE_BYTES != 0 { - return Err(io::Error::new( - io::ErrorKind::Other, - "Node cache is not a multiple of node size", - )); - } + if nodes.len() % NODE_BYTES != 0 || nodes.capacity() % NODE_BYTES != 0 { + return Err(io::Error::new( + io::ErrorKind::Other, + "Node cache is not a multiple of node size", + )); + } - let out: Vec = unsafe { - Vec::from_raw_parts( - nodes.as_mut_ptr() as *mut _, - nodes.len() / NODE_BYTES, - nodes.capacity() / NODE_BYTES, - ) - }; + let out: Vec = unsafe { + Vec::from_raw_parts( + nodes.as_mut_ptr() as *mut _, + nodes.len() / NODE_BYTES, + nodes.capacity() / NODE_BYTES, + ) + }; - mem::forget(nodes); + mem::forget(nodes); - Ok(out) + Ok(out) } impl AsRef<[Node]> for NodeCache { - fn as_ref(&self) -> &[Node] { - match self.cache { - Either::Left(ref vec) => vec, - Either::Right(ref mmap) => unsafe { - let bytes = mmap.as_ptr(); - // This isn't a safety issue, so we can keep this a debug lint. We don't care about - // people manually messing with the files unless it can cause unsafety, but if we're - // generating incorrect files then we want to catch that in CI. - debug_assert_eq!(mmap.len() % NODE_BYTES, 0); - slice::from_raw_parts(bytes as _, mmap.len() / NODE_BYTES) - }, - } - } + fn as_ref(&self) -> &[Node] { + match self.cache { + Either::Left(ref vec) => vec, + Either::Right(ref mmap) => unsafe { + let bytes = mmap.as_ptr(); + // This isn't a safety issue, so we can keep this a debug lint. We don't care about + // people manually messing with the files unless it can cause unsafety, but if we're + // generating incorrect files then we want to catch that in CI. + debug_assert_eq!(mmap.len() % NODE_BYTES, 0); + slice::from_raw_parts(bytes as _, mmap.len() / NODE_BYTES) + }, + } + } } // This takes a raw pointer and a counter because `memory` may be uninitialized. `memory` _must_ be @@ -311,47 +318,47 @@ impl AsRef<[Node]> for NodeCache { // out. It counts as a read and causes all writes afterwards to be elided. Yes, really. I know, I // want to refactor this to use less `unsafe` as much as the next rustacean. unsafe fn initialize_memory(memory: *mut Node, num_nodes: usize, ident: &H256) { - let dst = memory as *mut u8; + let dst = memory as *mut u8; - debug_assert_eq!(ident.len(), 32); - keccak_512::unchecked(dst, NODE_BYTES, ident.as_ptr(), ident.len()); + debug_assert_eq!(ident.len(), 32); + keccak_512::unchecked(dst, NODE_BYTES, ident.as_ptr(), ident.len()); - for i in 1..num_nodes { - // We use raw pointers here, see above - let dst = memory.offset(i as _) as *mut u8; - let src = memory.offset(i as isize - 1) as *mut u8; + for i in 1..num_nodes { + // We use raw pointers here, see above + let dst = memory.offset(i as _) as *mut u8; + let src = memory.offset(i as isize - 1) as *mut u8; - keccak_512::unchecked(dst, NODE_BYTES, src, NODE_BYTES); - } + keccak_512::unchecked(dst, NODE_BYTES, src, NODE_BYTES); + } - // Now this is initialized, we can treat it as a slice. - let nodes: &mut [Node] = slice::from_raw_parts_mut(memory, num_nodes); + // Now this is initialized, we can treat it as a slice. + let nodes: &mut [Node] = slice::from_raw_parts_mut(memory, num_nodes); - // For `unroll!`, see below. If the literal in `unroll!` is not the same as the RHS here then - // these have got out of sync! Don't let this happen! - debug_assert_eq!(NODE_DWORDS, 8); + // For `unroll!`, see below. If the literal in `unroll!` is not the same as the RHS here then + // these have got out of sync! Don't let this happen! + debug_assert_eq!(NODE_DWORDS, 8); - // This _should_ get unrolled by the compiler, since it's not using the loop variable. - for _ in 0..ETHASH_CACHE_ROUNDS { - for i in 0..num_nodes { - let data_idx = (num_nodes - 1 + i) % num_nodes; - let idx = nodes.get_unchecked_mut(i).as_words()[0] as usize % num_nodes; + // This _should_ get unrolled by the compiler, since it's not using the loop variable. + for _ in 0..ETHASH_CACHE_ROUNDS { + for i in 0..num_nodes { + let data_idx = (num_nodes - 1 + i) % num_nodes; + let idx = nodes.get_unchecked_mut(i).as_words()[0] as usize % num_nodes; - let data = { - let mut data: Node = nodes.get_unchecked(data_idx).clone(); - let rhs: &Node = nodes.get_unchecked(idx); + let data = { + let mut data: Node = nodes.get_unchecked(data_idx).clone(); + let rhs: &Node = nodes.get_unchecked(idx); - unroll! { - for w in 0..8 { - *data.as_dwords_mut().get_unchecked_mut(w) ^= - *rhs.as_dwords().get_unchecked(w); - } - } + unroll! { + for w in 0..8 { + *data.as_dwords_mut().get_unchecked_mut(w) ^= + *rhs.as_dwords().get_unchecked(w); + } + } - data - }; + data + }; - keccak_512::write(&data.bytes, &mut nodes.get_unchecked_mut(i).bytes); - } - } + keccak_512::write(&data.bytes, &mut nodes.get_unchecked_mut(i).bytes); + } + } } diff --git a/ethash/src/compute.rs b/ethash/src/compute.rs index 36826121db9..c378c754f7c 100644 --- a/ethash/src/compute.rs +++ b/ethash/src/compute.rs @@ -1,33 +1,32 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Ethash implementation //! See https://github.com/ethereum/wiki/wiki/Ethash // TODO: fix endianess for big endian -use keccak::{keccak_512, keccak_256, H256}; use cache::{NodeCache, NodeCacheBuilder}; -use progpow::{CDag, generate_cdag, progpow, keccak_f800_short, keccak_f800_long}; +use keccak::{keccak_256, keccak_512, H256}; +use progpow::{generate_cdag, keccak_f800_long, keccak_f800_short, progpow, CDag}; use seed_compute::SeedHashCompute; use shared::*; use std::io; -use std::{mem, ptr}; -use std::path::Path; +use std::{mem, path::Path, ptr}; const MIX_WORDS: usize = ETHASH_MIX_BYTES / 4; const MIX_NODES: usize = MIX_WORDS / NODE_WORDS; @@ -35,92 +34,99 @@ pub const FNV_PRIME: u32 = 0x01000193; /// Computation result pub struct ProofOfWork { - /// Difficulty boundary - pub value: H256, - /// Mix - pub mix_hash: H256, + /// Difficulty boundary + pub value: H256, + /// Mix + pub mix_hash: H256, } enum Algorithm { - Hashimoto, - Progpow(Box), + Hashimoto, + Progpow(Box), } pub struct Light { - block_number: u64, - cache: NodeCache, - algorithm: Algorithm, + block_number: u64, + cache: NodeCache, + algorithm: Algorithm, } /// Light cache structure impl Light { - pub fn new_with_builder( - builder: &NodeCacheBuilder, - cache_dir: &Path, - block_number: u64, - progpow_transition: u64, - ) -> Self { - let cache = builder.new_cache(cache_dir.to_path_buf(), block_number); - - let algorithm = if block_number >= progpow_transition { - Algorithm::Progpow(Box::new(generate_cdag(cache.as_ref()))) - } else { - Algorithm::Hashimoto - }; - - Light { block_number, cache, algorithm } - } - - /// Calculate the light boundary data - /// `header_hash` - The header hash to pack into the mix - /// `nonce` - The nonce to pack into the mix - pub fn compute(&self, header_hash: &H256, nonce: u64, block_number: u64) -> ProofOfWork { - match self.algorithm { - Algorithm::Progpow(ref c_dag) => { - let (value, mix_hash) = progpow( - *header_hash, - nonce, - block_number, - self.cache.as_ref(), - c_dag, - ); - - ProofOfWork { value, mix_hash } - }, - Algorithm::Hashimoto => light_compute(self, header_hash, nonce), - } - - } - - pub fn from_file_with_builder( - builder: &NodeCacheBuilder, - cache_dir: &Path, - block_number: u64, - progpow_transition: u64, - ) -> io::Result { - let cache = builder.from_file(cache_dir.to_path_buf(), block_number)?; - - let algorithm = if block_number >= progpow_transition { - Algorithm::Progpow(Box::new(generate_cdag(cache.as_ref()))) - } else { - Algorithm::Hashimoto - }; - - Ok(Light { block_number, cache, algorithm }) - } - - pub fn to_file(&mut self) -> io::Result<&Path> { - self.cache.flush()?; - Ok(self.cache.cache_path()) - } + pub fn new_with_builder( + builder: &NodeCacheBuilder, + cache_dir: &Path, + block_number: u64, + progpow_transition: u64, + ) -> Self { + let cache = builder.new_cache(cache_dir.to_path_buf(), block_number); + + let algorithm = if block_number >= progpow_transition { + Algorithm::Progpow(Box::new(generate_cdag(cache.as_ref()))) + } else { + Algorithm::Hashimoto + }; + + Light { + block_number, + cache, + algorithm, + } + } + + /// Calculate the light boundary data + /// `header_hash` - The header hash to pack into the mix + /// `nonce` - The nonce to pack into the mix + pub fn compute(&self, header_hash: &H256, nonce: u64, block_number: u64) -> ProofOfWork { + match self.algorithm { + Algorithm::Progpow(ref c_dag) => { + let (value, mix_hash) = progpow( + *header_hash, + nonce, + block_number, + self.cache.as_ref(), + c_dag, + ); + + ProofOfWork { value, mix_hash } + } + Algorithm::Hashimoto => light_compute(self, header_hash, nonce), + } + } + + pub fn from_file_with_builder( + builder: &NodeCacheBuilder, + cache_dir: &Path, + block_number: u64, + progpow_transition: u64, + ) -> io::Result { + let cache = builder.from_file(cache_dir.to_path_buf(), block_number)?; + + let algorithm = if block_number >= progpow_transition { + Algorithm::Progpow(Box::new(generate_cdag(cache.as_ref()))) + } else { + Algorithm::Hashimoto + }; + + Ok(Light { + block_number, + cache, + algorithm, + }) + } + + pub fn to_file(&mut self) -> io::Result<&Path> { + self.cache.flush()?; + Ok(self.cache.cache_path()) + } } pub fn slow_hash_block_number(block_number: u64) -> H256 { - SeedHashCompute::resume_compute_seedhash([0u8; 32], 0, block_number / ETHASH_EPOCH_LENGTH) + SeedHashCompute::resume_compute_seedhash([0u8; 32], 0, block_number / ETHASH_EPOCH_LENGTH) } fn fnv_hash(x: u32, y: u32) -> u32 { - return x.wrapping_mul(FNV_PRIME) ^ y; + return x.wrapping_mul(FNV_PRIME) ^ y; } /// Difficulty quick check for POW preverification @@ -129,33 +135,38 @@ fn fnv_hash(x: u32, y: u32) -> u32 { /// `nonce` The block's nonce /// `mix_hash` The mix digest hash /// Boundary recovered from mix hash -pub fn quick_get_difficulty(header_hash: &H256, nonce: u64, mix_hash: &H256, progpow: bool) -> H256 { - unsafe { - if progpow { - let seed = keccak_f800_short(*header_hash, nonce, [0u32; 8]); - keccak_f800_long(*header_hash, seed, mem::transmute(*mix_hash)) - } else { - // This is safe - the `keccak_512` call below reads the first 40 bytes (which we explicitly set - // with two `copy_nonoverlapping` calls) but writes the first 64, and then we explicitly write - // the next 32 bytes before we read the whole thing with `keccak_256`. - // - // This cannot be elided by the compiler as it doesn't know the implementation of - // `keccak_512`. - let mut buf: [u8; 64 + 32] = mem::uninitialized(); - - ptr::copy_nonoverlapping(header_hash.as_ptr(), buf.as_mut_ptr(), 32); - ptr::copy_nonoverlapping(&nonce as *const u64 as *const u8, buf[32..].as_mut_ptr(), 8); - - keccak_512::unchecked(buf.as_mut_ptr(), 64, buf.as_ptr(), 40); - ptr::copy_nonoverlapping(mix_hash.as_ptr(), buf[64..].as_mut_ptr(), 32); - - // This is initialized in `keccak_256` - let mut hash: [u8; 32] = mem::uninitialized(); - keccak_256::unchecked(hash.as_mut_ptr(), hash.len(), buf.as_ptr(), buf.len()); - - hash - } - } +pub fn quick_get_difficulty( + header_hash: &H256, + nonce: u64, + mix_hash: &H256, + progpow: bool, +) -> H256 { + unsafe { + if progpow { + let seed = keccak_f800_short(*header_hash, nonce, [0u32; 8]); + keccak_f800_long(*header_hash, seed, mem::transmute(*mix_hash)) + } else { + // This is safe - the `keccak_512` call below reads the first 40 bytes (which we explicitly set + // with two `copy_nonoverlapping` calls) but writes the first 64, and then we explicitly write + // the next 32 bytes before we read the whole thing with `keccak_256`. + // + // This cannot be elided by the compiler as it doesn't know the implementation of + // `keccak_512`. + let mut buf: [u8; 64 + 32] = ::mem::MaybeUninit::uninit().assume_init(); + + ptr::copy_nonoverlapping(header_hash.as_ptr(), buf.as_mut_ptr(), 32); + ptr::copy_nonoverlapping(&nonce as *const u64 as *const u8, buf[32..].as_mut_ptr(), 8); + + keccak_512::unchecked(buf.as_mut_ptr(), 64, buf.as_ptr(), 40); + ptr::copy_nonoverlapping(mix_hash.as_ptr(), buf[64..].as_mut_ptr(), 32); + + // This is initialized in `keccak_256` + let mut hash: [u8; 32] = ::mem::MaybeUninit::uninit().assume_init(); + keccak_256::unchecked(hash.as_mut_ptr(), hash.len(), buf.as_ptr(), buf.len()); + + hash + } + } } /// Calculate the light client data @@ -163,289 +174,310 @@ pub fn quick_get_difficulty(header_hash: &H256, nonce: u64, mix_hash: &H256, pro /// `header_hash` - The header hash to pack into the mix /// `nonce` - The nonce to pack into the mix pub fn light_compute(light: &Light, header_hash: &H256, nonce: u64) -> ProofOfWork { - let full_size = get_data_size(light.block_number); - hash_compute(light, full_size, header_hash, nonce) + let full_size = get_data_size(light.block_number); + hash_compute(light, full_size, header_hash, nonce) } fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64) -> ProofOfWork { - macro_rules! make_const_array { - ($n:expr, $value:expr) => {{ - // We use explicit lifetimes to ensure that val's borrow is invalidated until the - // transmuted val dies. - unsafe fn make_const_array(val: &mut [T]) -> &mut [U; $n] { - use ::std::mem; - - debug_assert_eq!(val.len() * mem::size_of::(), $n * mem::size_of::()); - &mut *(val.as_mut_ptr() as *mut [U; $n]) - } - - make_const_array($value) - }} - } - - #[repr(C)] - struct MixBuf { - half_mix: Node, - compress_bytes: [u8; MIX_WORDS], - }; - - if full_size % MIX_WORDS != 0 { - panic!("Unaligned full size"); - } - - // You may be asking yourself: what in the name of Crypto Jesus is going on here? So: we need - // `half_mix` and `compress_bytes` in a single array later down in the code (we hash them - // together to create `value`) so that we can hash the full array. However, we do a bunch of - // reading and writing to these variables first. We originally allocated two arrays and then - // stuck them together with `ptr::copy_nonoverlapping` at the end, but this method is - // _significantly_ faster - by my benchmarks, a consistent 3-5%. This is the most ridiculous - // optimization I have ever done and I am so sorry. I can only chalk it up to cache locality - // improvements, since I can't imagine that 3-5% of our runtime is taken up by catting two - // arrays together. - let mut buf: MixBuf = MixBuf { - half_mix: unsafe { - // Pack `header_hash` and `nonce` together - // We explicitly write the first 40 bytes, leaving the last 24 as uninitialized. Then - // `keccak_512` reads the first 40 bytes (4th parameter) and overwrites the entire array, - // leaving it fully initialized. - let mut out: [u8; NODE_BYTES] = mem::uninitialized(); - - ptr::copy_nonoverlapping(header_hash.as_ptr(), out.as_mut_ptr(), header_hash.len()); - ptr::copy_nonoverlapping( - &nonce as *const u64 as *const u8, - out[header_hash.len()..].as_mut_ptr(), - mem::size_of::(), - ); - - // compute keccak-512 hash and replicate across mix - keccak_512::unchecked( - out.as_mut_ptr(), - NODE_BYTES, - out.as_ptr(), - header_hash.len() + mem::size_of::(), - ); - - Node { bytes: out } - }, - // This is fully initialized before being read, see `let mut compress = ...` below - compress_bytes: unsafe { mem::uninitialized() }, - }; - - let mut mix: [_; MIX_NODES] = [buf.half_mix.clone(), buf.half_mix.clone()]; - - let page_size = 4 * MIX_WORDS; - let num_full_pages = (full_size / page_size) as u32; - // deref once for better performance - let cache: &[Node] = light.cache.as_ref(); - let first_val = buf.half_mix.as_words()[0]; - - debug_assert_eq!(MIX_NODES, 2); - debug_assert_eq!(NODE_WORDS, 16); - - for i in 0..ETHASH_ACCESSES as u32 { - let index = { - // This is trivially safe, but does not work on big-endian. The safety of this is - // asserted in debug builds (see the definition of `make_const_array!`). - let mix_words: &mut [u32; MIX_WORDS] = - unsafe { make_const_array!(MIX_WORDS, &mut mix) }; - - fnv_hash(first_val ^ i, mix_words[i as usize % MIX_WORDS]) % num_full_pages - }; - - unroll! { - // MIX_NODES - for n in 0..2 { - let tmp_node = calculate_dag_item( - index * MIX_NODES as u32 + n as u32, - cache, - ); - - unroll! { - // NODE_WORDS - for w in 0..16 { - mix[n].as_words_mut()[w] = - fnv_hash( - mix[n].as_words()[w], - tmp_node.as_words()[w], - ); - } - } - } - } - } - - let mix_words: [u32; MIX_WORDS] = unsafe { mem::transmute(mix) }; - - { - // This is an uninitialized buffer to begin with, but we iterate precisely `compress.len()` - // times and set each index, leaving the array fully initialized. THIS ONLY WORKS ON LITTLE- - // ENDIAN MACHINES. See a future PR to make this and the rest of the code work correctly on - // big-endian arches like mips. - let compress: &mut [u32; MIX_WORDS / 4] = - unsafe { make_const_array!(MIX_WORDS / 4, &mut buf.compress_bytes) }; - - // Compress mix - debug_assert_eq!(MIX_WORDS / 4, 8); - unroll! { - for i in 0..8 { - let w = i * 4; - - let mut reduction = mix_words[w + 0]; - reduction = reduction.wrapping_mul(FNV_PRIME) ^ mix_words[w + 1]; - reduction = reduction.wrapping_mul(FNV_PRIME) ^ mix_words[w + 2]; - reduction = reduction.wrapping_mul(FNV_PRIME) ^ mix_words[w + 3]; - compress[i] = reduction; - } - } - } - - let mix_hash = buf.compress_bytes; - - let value: H256 = { - // We can interpret the buffer as an array of `u8`s, since it's `repr(C)`. - let read_ptr: *const u8 = &buf as *const MixBuf as *const u8; - // We overwrite the second half since `keccak_256` has an internal buffer and so allows - // overlapping arrays as input. - let write_ptr: *mut u8 = &mut buf.compress_bytes as *mut [u8; 32] as *mut u8; - unsafe { - keccak_256::unchecked( - write_ptr, - buf.compress_bytes.len(), - read_ptr, - buf.half_mix.bytes.len() + buf.compress_bytes.len(), - ); - } - buf.compress_bytes - }; - - ProofOfWork { mix_hash: mix_hash, value: value } + macro_rules! make_const_array { + ($n:expr, $value:expr) => {{ + // We use explicit lifetimes to ensure that val's borrow is invalidated until the + // transmuted val dies. + unsafe fn make_const_array(val: &mut [T]) -> &mut [U; $n] { + use std::mem; + + debug_assert_eq!(val.len() * mem::size_of::(), $n * mem::size_of::()); + &mut *(val.as_mut_ptr() as *mut [U; $n]) + } + + make_const_array($value) + }}; + } + + #[repr(C)] + struct MixBuf { + half_mix: Node, + compress_bytes: [u8; MIX_WORDS], + }; + + if full_size % MIX_WORDS != 0 { + panic!("Unaligned full size"); + } + + // You may be asking yourself: what in the name of Crypto Jesus is going on here? So: we need + // `half_mix` and `compress_bytes` in a single array later down in the code (we hash them + // together to create `value`) so that we can hash the full array. However, we do a bunch of + // reading and writing to these variables first. We originally allocated two arrays and then + // stuck them together with `ptr::copy_nonoverlapping` at the end, but this method is + // _significantly_ faster - by my benchmarks, a consistent 3-5%. This is the most ridiculous + // optimization I have ever done and I am so sorry. I can only chalk it up to cache locality + // improvements, since I can't imagine that 3-5% of our runtime is taken up by catting two + // arrays together. + let mut buf: MixBuf = MixBuf { + half_mix: unsafe { + // Pack `header_hash` and `nonce` together + // We explicitly write the first 40 bytes, leaving the last 24 as uninitialized. Then + // `keccak_512` reads the first 40 bytes (4th parameter) and overwrites the entire array, + // leaving it fully initialized. + let mut out: [u8; NODE_BYTES] = ::mem::MaybeUninit::uninit().assume_init(); + + ptr::copy_nonoverlapping(header_hash.as_ptr(), out.as_mut_ptr(), header_hash.len()); + ptr::copy_nonoverlapping( + &nonce as *const u64 as *const u8, + out[header_hash.len()..].as_mut_ptr(), + mem::size_of::(), + ); + + // compute keccak-512 hash and replicate across mix + keccak_512::unchecked( + out.as_mut_ptr(), + NODE_BYTES, + out.as_ptr(), + header_hash.len() + mem::size_of::(), + ); + + Node { bytes: out } + }, + // This is fully initialized before being read, see `let mut compress = ...` below + compress_bytes: unsafe { ::mem::MaybeUninit::uninit().assume_init() }, + }; + + let mut mix: [_; MIX_NODES] = [buf.half_mix.clone(), buf.half_mix.clone()]; + + let page_size = 4 * MIX_WORDS; + let num_full_pages = (full_size / page_size) as u32; + // deref once for better performance + let cache: &[Node] = light.cache.as_ref(); + let first_val = buf.half_mix.as_words()[0]; + + debug_assert_eq!(MIX_NODES, 2); + debug_assert_eq!(NODE_WORDS, 16); + + for i in 0..ETHASH_ACCESSES as u32 { + let index = { + // This is trivially safe, but does not work on big-endian. The safety of this is + // asserted in debug builds (see the definition of `make_const_array!`). + let mix_words: &mut [u32; MIX_WORDS] = + unsafe { make_const_array!(MIX_WORDS, &mut mix) }; + + fnv_hash(first_val ^ i, mix_words[i as usize % MIX_WORDS]) % num_full_pages + }; + + unroll! { + // MIX_NODES + for n in 0..2 { + let tmp_node = calculate_dag_item( + index * MIX_NODES as u32 + n as u32, + cache, + ); + + unroll! { + // NODE_WORDS + for w in 0..16 { + mix[n].as_words_mut()[w] = + fnv_hash( + mix[n].as_words()[w], + tmp_node.as_words()[w], + ); + } + } + } + } + } + + let mix_words: [u32; MIX_WORDS] = unsafe { mem::transmute(mix) }; + + { + // This is an uninitialized buffer to begin with, but we iterate precisely `compress.len()` + // times and set each index, leaving the array fully initialized. THIS ONLY WORKS ON LITTLE- + // ENDIAN MACHINES. See a future PR to make this and the rest of the code work correctly on + // big-endian arches like mips. + let compress: &mut [u32; MIX_WORDS / 4] = + unsafe { make_const_array!(MIX_WORDS / 4, &mut buf.compress_bytes) }; + + // Compress mix + debug_assert_eq!(MIX_WORDS / 4, 8); + unroll! { + for i in 0..8 { + let w = i * 4; + + let mut reduction = mix_words[w + 0]; + reduction = reduction.wrapping_mul(FNV_PRIME) ^ mix_words[w + 1]; + reduction = reduction.wrapping_mul(FNV_PRIME) ^ mix_words[w + 2]; + reduction = reduction.wrapping_mul(FNV_PRIME) ^ mix_words[w + 3]; + compress[i] = reduction; + } + } + } + + let mix_hash = buf.compress_bytes; + + let value: H256 = { + // We can interpret the buffer as an array of `u8`s, since it's `repr(C)`. + let read_ptr: *const u8 = &buf as *const MixBuf as *const u8; + // We overwrite the second half since `keccak_256` has an internal buffer and so allows + // overlapping arrays as input. + let write_ptr: *mut u8 = &mut buf.compress_bytes as *mut [u8; 32] as *mut u8; + unsafe { + keccak_256::unchecked( + write_ptr, + buf.compress_bytes.len(), + read_ptr, + buf.half_mix.bytes.len() + buf.compress_bytes.len(), + ); + } + buf.compress_bytes + }; + + ProofOfWork { + mix_hash: mix_hash, + value: value, + } } // TODO: Use the `simd` crate pub fn calculate_dag_item(node_index: u32, cache: &[Node]) -> Node { - let num_parent_nodes = cache.len(); - let mut ret = cache[node_index as usize % num_parent_nodes].clone(); - ret.as_words_mut()[0] ^= node_index; + let num_parent_nodes = cache.len(); + let mut ret = cache[node_index as usize % num_parent_nodes].clone(); + ret.as_words_mut()[0] ^= node_index; - keccak_512::inplace(ret.as_bytes_mut()); + keccak_512::inplace(ret.as_bytes_mut()); - debug_assert_eq!(NODE_WORDS, 16); - for i in 0..ETHASH_DATASET_PARENTS as u32 { - let parent_index = fnv_hash(node_index ^ i, ret.as_words()[i as usize % NODE_WORDS]) % - num_parent_nodes as u32; - let parent = &cache[parent_index as usize]; + debug_assert_eq!(NODE_WORDS, 16); + for i in 0..ETHASH_DATASET_PARENTS as u32 { + let parent_index = fnv_hash(node_index ^ i, ret.as_words()[i as usize % NODE_WORDS]) + % num_parent_nodes as u32; + let parent = &cache[parent_index as usize]; - unroll! { - for w in 0..16 { - ret.as_words_mut()[w] = fnv_hash(ret.as_words()[w], parent.as_words()[w]); - } - } - } + unroll! { + for w in 0..16 { + ret.as_words_mut()[w] = fnv_hash(ret.as_words()[w], parent.as_words()[w]); + } + } + } - keccak_512::inplace(ret.as_bytes_mut()); + keccak_512::inplace(ret.as_bytes_mut()); - ret + ret } #[cfg(test)] mod test { - use super::*; - use std::fs; - use tempdir::TempDir; - - #[test] - fn test_get_cache_size() { - // https://github.com/ethereum/wiki/wiki/Ethash/ef6b93f9596746a088ea95d01ca2778be43ae68f#data-sizes - assert_eq!(16776896usize, get_cache_size(0)); - assert_eq!(16776896usize, get_cache_size(1)); - assert_eq!(16776896usize, get_cache_size(ETHASH_EPOCH_LENGTH - 1)); - assert_eq!(16907456usize, get_cache_size(ETHASH_EPOCH_LENGTH)); - assert_eq!(16907456usize, get_cache_size(ETHASH_EPOCH_LENGTH + 1)); - assert_eq!(284950208usize, get_cache_size(2046 * ETHASH_EPOCH_LENGTH)); - assert_eq!(285081536usize, get_cache_size(2047 * ETHASH_EPOCH_LENGTH)); - assert_eq!(285081536usize, get_cache_size(2048 * ETHASH_EPOCH_LENGTH - 1)); - } - - #[test] - fn test_get_data_size() { - // https://github.com/ethereum/wiki/wiki/Ethash/ef6b93f9596746a088ea95d01ca2778be43ae68f#data-sizes - assert_eq!(1073739904usize, get_data_size(0)); - assert_eq!(1073739904usize, get_data_size(1)); - assert_eq!(1073739904usize, get_data_size(ETHASH_EPOCH_LENGTH - 1)); - assert_eq!(1082130304usize, get_data_size(ETHASH_EPOCH_LENGTH)); - assert_eq!(1082130304usize, get_data_size(ETHASH_EPOCH_LENGTH + 1)); - assert_eq!(18236833408usize, get_data_size(2046 * ETHASH_EPOCH_LENGTH)); - assert_eq!(18245220736usize, get_data_size(2047 * ETHASH_EPOCH_LENGTH)); - } - - #[test] - fn test_difficulty_test() { - let hash = [ - 0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, 0xe4, 0x0a, 0xb3, - 0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94, - 0x05, 0x52, 0x7d, 0x72, - ]; - let mix_hash = [ - 0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce, - 0x6b, 0xdf, 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, 0x7b, 0xb2, 0xd3, 0x2a, - 0x64, 0x31, 0xab, 0x6d, - ]; - let nonce = 0xd7b3ac70a301a249; - let boundary_good = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2, - 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a, - 0xe9, 0x7e, 0x53, 0x84, - ]; - assert_eq!(quick_get_difficulty(&hash, nonce, &mix_hash, false)[..], boundary_good[..]); - let boundary_bad = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3a, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2, - 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a, - 0xe9, 0x7e, 0x53, 0x84, - ]; - assert!(quick_get_difficulty(&hash, nonce, &mix_hash, false)[..] != boundary_bad[..]); - } - - #[test] - fn test_light_compute() { - let hash = [ - 0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, 0xe4, 0x0a, 0xb3, - 0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94, - 0x05, 0x52, 0x7d, 0x72, - ]; - let mix_hash = [ - 0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce, - 0x6b, 0xdf, 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, 0x7b, 0xb2, 0xd3, 0x2a, - 0x64, 0x31, 0xab, 0x6d, - ]; - let boundary = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2, - 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a, - 0xe9, 0x7e, 0x53, 0x84, - ]; - let nonce = 0xd7b3ac70a301a249; - - let tempdir = TempDir::new("").unwrap(); - // difficulty = 0x085657254bd9u64; - let light = NodeCacheBuilder::new(None, u64::max_value()).light(tempdir.path(), 486382); - let result = light_compute(&light, &hash, nonce); - assert_eq!(result.mix_hash[..], mix_hash[..]); - assert_eq!(result.value[..], boundary[..]); - } - - #[test] - fn test_drop_old_data() { - let tempdir = TempDir::new("").unwrap(); - let builder = NodeCacheBuilder::new(None, u64::max_value()); - let first = builder.light(tempdir.path(), 0).to_file().unwrap().to_owned(); - - let second = builder.light(tempdir.path(), ETHASH_EPOCH_LENGTH).to_file().unwrap().to_owned(); - assert!(fs::metadata(&first).is_ok()); - - let _ = builder.light(tempdir.path(), ETHASH_EPOCH_LENGTH * 2).to_file(); - assert!(fs::metadata(&first).is_err()); - assert!(fs::metadata(&second).is_ok()); - - let _ = builder.light(tempdir.path(), ETHASH_EPOCH_LENGTH * 3).to_file(); - assert!(fs::metadata(&second).is_err()); - } + use super::*; + use std::fs; + use tempdir::TempDir; + + #[test] + fn test_get_cache_size() { + // https://github.com/ethereum/wiki/wiki/Ethash/ef6b93f9596746a088ea95d01ca2778be43ae68f#data-sizes + assert_eq!(16776896usize, get_cache_size(0)); + assert_eq!(16776896usize, get_cache_size(1)); + assert_eq!(16776896usize, get_cache_size(ETHASH_EPOCH_LENGTH - 1)); + assert_eq!(16907456usize, get_cache_size(ETHASH_EPOCH_LENGTH)); + assert_eq!(16907456usize, get_cache_size(ETHASH_EPOCH_LENGTH + 1)); + assert_eq!(284950208usize, get_cache_size(2046 * ETHASH_EPOCH_LENGTH)); + assert_eq!(285081536usize, get_cache_size(2047 * ETHASH_EPOCH_LENGTH)); + assert_eq!( + 285081536usize, + get_cache_size(2048 * ETHASH_EPOCH_LENGTH - 1) + ); + } + + #[test] + fn test_get_data_size() { + // https://github.com/ethereum/wiki/wiki/Ethash/ef6b93f9596746a088ea95d01ca2778be43ae68f#data-sizes + assert_eq!(1073739904usize, get_data_size(0)); + assert_eq!(1073739904usize, get_data_size(1)); + assert_eq!(1073739904usize, get_data_size(ETHASH_EPOCH_LENGTH - 1)); + assert_eq!(1082130304usize, get_data_size(ETHASH_EPOCH_LENGTH)); + assert_eq!(1082130304usize, get_data_size(ETHASH_EPOCH_LENGTH + 1)); + assert_eq!(18236833408usize, get_data_size(2046 * ETHASH_EPOCH_LENGTH)); + assert_eq!(18245220736usize, get_data_size(2047 * ETHASH_EPOCH_LENGTH)); + } + + #[test] + fn test_difficulty_test() { + let hash = [ + 0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, 0xe4, 0x0a, 0xb3, + 0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94, + 0x05, 0x52, 0x7d, 0x72, + ]; + let mix_hash = [ + 0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce, + 0x6b, 0xdf, 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, 0x7b, 0xb2, 0xd3, 0x2a, + 0x64, 0x31, 0xab, 0x6d, + ]; + let nonce = 0xd7b3ac70a301a249; + let boundary_good = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2, + 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a, + 0xe9, 0x7e, 0x53, 0x84, + ]; + assert_eq!( + quick_get_difficulty(&hash, nonce, &mix_hash, false)[..], + boundary_good[..] + ); + let boundary_bad = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3a, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2, + 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a, + 0xe9, 0x7e, 0x53, 0x84, + ]; + assert!(quick_get_difficulty(&hash, nonce, &mix_hash, false)[..] != boundary_bad[..]); + } + + #[test] + fn test_light_compute() { + let hash = [ + 0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, 0xe4, 0x0a, 0xb3, + 0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94, + 0x05, 0x52, 0x7d, 0x72, + ]; + let mix_hash = [ + 0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce, + 0x6b, 0xdf, 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, 0x7b, 0xb2, 0xd3, 0x2a, + 0x64, 0x31, 0xab, 0x6d, + ]; + let boundary = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2, + 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a, + 0xe9, 0x7e, 0x53, 0x84, + ]; + let nonce = 0xd7b3ac70a301a249; + + let tempdir = TempDir::new("").unwrap(); + // difficulty = 0x085657254bd9u64; + let light = NodeCacheBuilder::new(None, u64::max_value()).light(tempdir.path(), 486382); + let result = light_compute(&light, &hash, nonce); + assert_eq!(result.mix_hash[..], mix_hash[..]); + assert_eq!(result.value[..], boundary[..]); + } + + #[test] + fn test_drop_old_data() { + let tempdir = TempDir::new("").unwrap(); + let builder = NodeCacheBuilder::new(None, u64::max_value()); + let first = builder + .light(tempdir.path(), 0) + .to_file() + .unwrap() + .to_owned(); + + let second = builder + .light(tempdir.path(), ETHASH_EPOCH_LENGTH) + .to_file() + .unwrap() + .to_owned(); + assert!(fs::metadata(&first).is_ok()); + + let _ = builder + .light(tempdir.path(), ETHASH_EPOCH_LENGTH * 2) + .to_file(); + assert!(fs::metadata(&first).is_err()); + assert!(fs::metadata(&second).is_ok()); + + let _ = builder + .light(tempdir.path(), ETHASH_EPOCH_LENGTH * 3) + .to_file(); + assert!(fs::metadata(&second).is_err()); + } } diff --git a/ethash/src/keccak.rs b/ethash/src/keccak.rs index 3f7576c7bb8..f09ce56f874 100644 --- a/ethash/src/keccak.rs +++ b/ethash/src/keccak.rs @@ -1,56 +1,66 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . extern crate keccak_hash as hash; pub type H256 = [u8; 32]; pub mod keccak_512 { - use super::hash; + use super::hash; - pub use self::hash::keccak_512_unchecked as unchecked; + pub use self::hash::keccak_512_unchecked as unchecked; - pub fn write(input: &[u8], output: &mut [u8]) { - hash::keccak_512(input, output); - } + pub fn write(input: &[u8], output: &mut [u8]) { + hash::keccak_512(input, output); + } - pub fn inplace(input: &mut [u8]) { - // This is safe since `keccak_*` uses an internal buffer and copies the result to the output. This - // means that we can reuse the input buffer for both input and output. - unsafe { - hash::keccak_512_unchecked(input.as_mut_ptr(), input.len(), input.as_ptr(), input.len()); - } - } + pub fn inplace(input: &mut [u8]) { + // This is safe since `keccak_*` uses an internal buffer and copies the result to the output. This + // means that we can reuse the input buffer for both input and output. + unsafe { + hash::keccak_512_unchecked( + input.as_mut_ptr(), + input.len(), + input.as_ptr(), + input.len(), + ); + } + } } pub mod keccak_256 { - use super::hash; - - pub use self::hash::keccak_256_unchecked as unchecked; - - #[allow(dead_code)] - pub fn write(input: &[u8], output: &mut [u8]) { - hash::keccak_256(input, output); - } - - pub fn inplace(input: &mut [u8]) { - // This is safe since `keccak_*` uses an internal buffer and copies the result to the output. This - // means that we can reuse the input buffer for both input and output. - unsafe { - hash::keccak_256_unchecked(input.as_mut_ptr(), input.len(), input.as_ptr(), input.len()); - } - } + use super::hash; + + pub use self::hash::keccak_256_unchecked as unchecked; + + #[allow(dead_code)] + pub fn write(input: &[u8], output: &mut [u8]) { + hash::keccak_256(input, output); + } + + pub fn inplace(input: &mut [u8]) { + // This is safe since `keccak_*` uses an internal buffer and copies the result to the output. This + // means that we can reuse the input buffer for both input and output. + unsafe { + hash::keccak_256_unchecked( + input.as_mut_ptr(), + input.len(), + input.as_ptr(), + input.len(), + ); + } + } } diff --git a/ethash/src/lib.rs b/ethash/src/lib.rs index e40c08920cf..e56b09358ea 100644 --- a/ethash/src/lib.rs +++ b/ethash/src/lib.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . extern crate either; extern crate ethereum_types; @@ -39,9 +39,9 @@ pub mod compute; #[cfg(not(feature = "bench"))] mod compute; -mod seed_compute; mod cache; mod keccak; +mod seed_compute; mod shared; #[cfg(feature = "bench")] @@ -50,190 +50,221 @@ pub mod progpow; mod progpow; pub use cache::{NodeCacheBuilder, OptimizeFor}; -pub use compute::{ProofOfWork, quick_get_difficulty, slow_hash_block_number}; use compute::Light; +pub use compute::{quick_get_difficulty, slow_hash_block_number, ProofOfWork}; use ethereum_types::{U256, U512}; use keccak::H256; use parking_lot::Mutex; pub use seed_compute::SeedHashCompute; pub use shared::ETHASH_EPOCH_LENGTH; -use std::mem; -use std::path::{Path, PathBuf}; +use std::{ + mem, + path::{Path, PathBuf}, +}; use std::sync::Arc; struct LightCache { - recent_epoch: Option, - recent: Option>, - prev_epoch: Option, - prev: Option>, + recent_epoch: Option, + recent: Option>, + prev_epoch: Option, + prev: Option>, } /// Light/Full cache manager. pub struct EthashManager { - nodecache_builder: NodeCacheBuilder, - cache: Mutex, - cache_dir: PathBuf, - progpow_transition: u64, + nodecache_builder: NodeCacheBuilder, + cache: Mutex, + cache_dir: PathBuf, + progpow_transition: u64, } impl EthashManager { - /// Create a new new instance of ethash manager - pub fn new>>(cache_dir: &Path, optimize_for: T, progpow_transition: u64) -> EthashManager { - EthashManager { - cache_dir: cache_dir.to_path_buf(), - nodecache_builder: NodeCacheBuilder::new(optimize_for.into().unwrap_or_default(), progpow_transition), - progpow_transition: progpow_transition, - cache: Mutex::new(LightCache { - recent_epoch: None, - recent: None, - prev_epoch: None, - prev: None, - }), - } - } - - /// Calculate the light client data - /// `block_number` - Block number to check - /// `light` - The light client handler - /// `header_hash` - The header hash to pack into the mix - /// `nonce` - The nonce to pack into the mix - pub fn compute_light(&self, block_number: u64, header_hash: &H256, nonce: u64) -> ProofOfWork { - let epoch = block_number / ETHASH_EPOCH_LENGTH; - let light = { - let mut lights = self.cache.lock(); - let light = if block_number == self.progpow_transition { - // we need to regenerate the cache to trigger algorithm change to progpow inside `Light` - None - } else { - match lights.recent_epoch.clone() { - Some(ref e) if *e == epoch => lights.recent.clone(), - _ => match lights.prev_epoch.clone() { - Some(e) if e == epoch => { - // don't swap if recent is newer. - if lights.recent_epoch > lights.prev_epoch { - None - } else { - // swap - let t = lights.prev_epoch; - lights.prev_epoch = lights.recent_epoch; - lights.recent_epoch = t; - let t = lights.prev.clone(); - lights.prev = lights.recent.clone(); - lights.recent = t; - lights.recent.clone() - } - } - _ => None, - }, - } - }; - - match light { - None => { - let light = match self.nodecache_builder.light_from_file( - &self.cache_dir, - block_number, - ) { - Ok(light) => Arc::new(light), - Err(e) => { - debug!("Light cache file not found for {}:{}", block_number, e); - let mut light = self.nodecache_builder.light( - &self.cache_dir, - block_number, - ); - if let Err(e) = light.to_file() { - warn!("Light cache file write error: {}", e); - } - Arc::new(light) - } - }; - lights.prev_epoch = mem::replace(&mut lights.recent_epoch, Some(epoch)); - lights.prev = mem::replace(&mut lights.recent, Some(light.clone())); - light - } - Some(light) => light, - } - }; - light.compute(header_hash, nonce, block_number) - } + /// Create a new new instance of ethash manager + pub fn new>>( + cache_dir: &Path, + optimize_for: T, + progpow_transition: u64, + ) -> EthashManager { + EthashManager { + cache_dir: cache_dir.to_path_buf(), + nodecache_builder: NodeCacheBuilder::new( + optimize_for.into().unwrap_or_default(), + progpow_transition, + ), + progpow_transition: progpow_transition, + cache: Mutex::new(LightCache { + recent_epoch: None, + recent: None, + prev_epoch: None, + prev: None, + }), + } + } + + /// Calculate the light client data + /// `block_number` - Block number to check + /// `light` - The light client handler + /// `header_hash` - The header hash to pack into the mix + /// `nonce` - The nonce to pack into the mix + pub fn compute_light(&self, block_number: u64, header_hash: &H256, nonce: u64) -> ProofOfWork { + let epoch = block_number / ETHASH_EPOCH_LENGTH; + let light = { + let mut lights = self.cache.lock(); + let light = if block_number == self.progpow_transition { + // we need to regenerate the cache to trigger algorithm change to progpow inside `Light` + None + } else { + match lights.recent_epoch.clone() { + Some(ref e) if *e == epoch => lights.recent.clone(), + _ => match lights.prev_epoch.clone() { + Some(e) if e == epoch => { + // don't swap if recent is newer. + if lights.recent_epoch > lights.prev_epoch { + None + } else { + // swap + let t = lights.prev_epoch; + lights.prev_epoch = lights.recent_epoch; + lights.recent_epoch = t; + let t = lights.prev.clone(); + lights.prev = lights.recent.clone(); + lights.recent = t; + lights.recent.clone() + } + } + _ => None, + }, + } + }; + + match light { + None => { + let light = match self + .nodecache_builder + .light_from_file(&self.cache_dir, block_number) + { + Ok(light) => Arc::new(light), + Err(e) => { + debug!("Light cache file not found for {}:{}", block_number, e); + let mut light = + self.nodecache_builder.light(&self.cache_dir, block_number); + if let Err(e) = light.to_file() { + warn!("Light cache file write error: {}", e); + } + Arc::new(light) + } + }; + lights.prev_epoch = mem::replace(&mut lights.recent_epoch, Some(epoch)); + lights.prev = mem::replace(&mut lights.recent, Some(light.clone())); + light + } + Some(light) => light, + } + }; + light.compute(header_hash, nonce, block_number) + } } /// Convert an Ethash boundary to its original difficulty. Basically just `f(x) = 2^256 / x`. pub fn boundary_to_difficulty(boundary: ðereum_types::H256) -> U256 { - difficulty_to_boundary_aux(&**boundary) + difficulty_to_boundary_aux(&**boundary) } /// Convert an Ethash difficulty to the target boundary. Basically just `f(x) = 2^256 / x`. pub fn difficulty_to_boundary(difficulty: &U256) -> ethereum_types::H256 { - difficulty_to_boundary_aux(difficulty).into() + difficulty_to_boundary_aux(difficulty).into() } fn difficulty_to_boundary_aux>(difficulty: T) -> ethereum_types::U256 { - let difficulty = difficulty.into(); + let difficulty = difficulty.into(); - assert!(!difficulty.is_zero()); + assert!(!difficulty.is_zero()); - if difficulty == U512::one() { - U256::max_value() - } else { - // difficulty > 1, so result should never overflow 256 bits - U256::from((U512::one() << 256) / difficulty) - } + if difficulty == U512::one() { + U256::max_value() + } else { + // difficulty > 1, so result should never overflow 256 bits + U256::from((U512::one() << 256) / difficulty) + } } #[test] fn test_lru() { - use tempdir::TempDir; - - let tempdir = TempDir::new("").unwrap(); - let ethash = EthashManager::new(tempdir.path(), None, u64::max_value()); - let hash = [0u8; 32]; - ethash.compute_light(1, &hash, 1); - ethash.compute_light(50000, &hash, 1); - assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 1); - assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 0); - ethash.compute_light(1, &hash, 1); - assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 0); - assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 1); - ethash.compute_light(70000, &hash, 1); - assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 2); - assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 0); + use tempdir::TempDir; + + let tempdir = TempDir::new("").unwrap(); + let ethash = EthashManager::new(tempdir.path(), None, u64::max_value()); + let hash = [0u8; 32]; + ethash.compute_light(1, &hash, 1); + ethash.compute_light(50000, &hash, 1); + assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 1); + assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 0); + ethash.compute_light(1, &hash, 1); + assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 0); + assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 1); + ethash.compute_light(70000, &hash, 1); + assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 2); + assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 0); } #[test] fn test_difficulty_to_boundary() { - use ethereum_types::H256; - use std::str::FromStr; + use ethereum_types::H256; + use std::str::FromStr; - assert_eq!(difficulty_to_boundary(&U256::from(1)), H256::from(U256::max_value())); - assert_eq!(difficulty_to_boundary(&U256::from(2)), H256::from_str("8000000000000000000000000000000000000000000000000000000000000000").unwrap()); - assert_eq!(difficulty_to_boundary(&U256::from(4)), H256::from_str("4000000000000000000000000000000000000000000000000000000000000000").unwrap()); - assert_eq!(difficulty_to_boundary(&U256::from(32)), H256::from_str("0800000000000000000000000000000000000000000000000000000000000000").unwrap()); + assert_eq!( + difficulty_to_boundary(&U256::from(1)), + H256::from(U256::max_value()) + ); + assert_eq!( + difficulty_to_boundary(&U256::from(2)), + H256::from_str("8000000000000000000000000000000000000000000000000000000000000000").unwrap() + ); + assert_eq!( + difficulty_to_boundary(&U256::from(4)), + H256::from_str("4000000000000000000000000000000000000000000000000000000000000000").unwrap() + ); + assert_eq!( + difficulty_to_boundary(&U256::from(32)), + H256::from_str("0800000000000000000000000000000000000000000000000000000000000000").unwrap() + ); } #[test] fn test_difficulty_to_boundary_regression() { - use ethereum_types::H256; - - // the last bit was originally being truncated when performing the conversion - // https://github.com/paritytech/parity-ethereum/issues/8397 - for difficulty in 1..9 { - assert_eq!(U256::from(difficulty), boundary_to_difficulty(&difficulty_to_boundary(&difficulty.into()))); - assert_eq!(H256::from(difficulty), difficulty_to_boundary(&boundary_to_difficulty(&difficulty.into()))); - assert_eq!(U256::from(difficulty), boundary_to_difficulty(&boundary_to_difficulty(&difficulty.into()).into())); - assert_eq!(H256::from(difficulty), difficulty_to_boundary(&difficulty_to_boundary(&difficulty.into()).into())); - } + use ethereum_types::H256; + + // the last bit was originally being truncated when performing the conversion + // https://github.com/openethereum/openethereum/issues/8397 + for difficulty in 1..9 { + assert_eq!( + U256::from(difficulty), + boundary_to_difficulty(&difficulty_to_boundary(&difficulty.into())) + ); + assert_eq!( + H256::from(difficulty), + difficulty_to_boundary(&boundary_to_difficulty(&difficulty.into())) + ); + assert_eq!( + U256::from(difficulty), + boundary_to_difficulty(&boundary_to_difficulty(&difficulty.into()).into()) + ); + assert_eq!( + H256::from(difficulty), + difficulty_to_boundary(&difficulty_to_boundary(&difficulty.into()).into()) + ); + } } #[test] #[should_panic] fn test_difficulty_to_boundary_panics_on_zero() { - difficulty_to_boundary(&U256::from(0)); + difficulty_to_boundary(&U256::from(0)); } #[test] #[should_panic] fn test_boundary_to_difficulty_panics_on_zero() { - boundary_to_difficulty(ðereum_types::H256::from(0)); + boundary_to_difficulty(ðereum_types::H256::from(0)); } diff --git a/ethash/src/progpow.rs b/ethash/src/progpow.rs index 038f38c2259..131f90c2c0d 100644 --- a/ethash/src/progpow.rs +++ b/ethash/src/progpow.rs @@ -1,22 +1,22 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity. If not, see . +// along with OpenEthereum. If not, see . -use compute::{FNV_PRIME, calculate_dag_item}; +use compute::{calculate_dag_item, FNV_PRIME}; use keccak::H256; -use shared::{ETHASH_ACCESSES, ETHASH_MIX_BYTES, Node, get_data_size}; +use shared::{get_data_size, Node, ETHASH_ACCESSES, ETHASH_MIX_BYTES}; const PROGPOW_CACHE_BYTES: usize = 16 * 1024; const PROGPOW_CACHE_WORDS: usize = PROGPOW_CACHE_BYTES / 4; @@ -32,564 +32,551 @@ const PROGPOW_REGS: usize = 32; const FNV_HASH: u32 = 0x811c9dc5; const KECCAKF_RNDC: [u32; 24] = [ - 0x00000001, 0x00008082, 0x0000808a, 0x80008000, 0x0000808b, 0x80000001, - 0x80008081, 0x00008009, 0x0000008a, 0x00000088, 0x80008009, 0x8000000a, - 0x8000808b, 0x0000008b, 0x00008089, 0x00008003, 0x00008002, 0x00000080, - 0x0000800a, 0x8000000a, 0x80008081, 0x00008080, 0x80000001, 0x80008008 + 0x00000001, 0x00008082, 0x0000808a, 0x80008000, 0x0000808b, 0x80000001, 0x80008081, 0x00008009, + 0x0000008a, 0x00000088, 0x80008009, 0x8000000a, 0x8000808b, 0x0000008b, 0x00008089, 0x00008003, + 0x00008002, 0x00000080, 0x0000800a, 0x8000000a, 0x80008081, 0x00008080, 0x80000001, 0x80008008, ]; const KECCAKF_ROTC: [u32; 24] = [ - 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, - 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44 + 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44, ]; const KECCAKF_PILN: [usize; 24] = [ - 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, - 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1 + 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1, ]; fn keccak_f800_round(st: &mut [u32; 25], r: usize) { - // Theta - let mut bc = [0u32; 5]; - for i in 0..bc.len() { - bc[i] = st[i] ^ st[i + 5] ^ st[i + 10] ^ st[i + 15] ^ st[i + 20]; - } - - for i in 0..bc.len() { - let t = bc[(i + 4) % 5] ^ bc[(i + 1) % 5].rotate_left(1); - for j in (0..st.len()).step_by(5) { - st[j + i] ^= t; - } - } - - // Rho Pi - let mut t = st[1]; - - debug_assert_eq!(KECCAKF_ROTC.len(), 24); - for i in 0..24 { - let j = KECCAKF_PILN[i]; - bc[0] = st[j]; - st[j] = t.rotate_left(KECCAKF_ROTC[i]); - t = bc[0]; - } - - // Chi - for j in (0..st.len()).step_by(5) { - for i in 0..bc.len() { - bc[i] = st[j + i]; - } - for i in 0..bc.len() { - st[j + i] ^= (!bc[(i + 1) % 5]) & bc[(i + 2) % 5]; - } - } - - // Iota - debug_assert!(r < KECCAKF_RNDC.len()); - st[0] ^= KECCAKF_RNDC[r]; + // Theta + let mut bc = [0u32; 5]; + for i in 0..bc.len() { + bc[i] = st[i] ^ st[i + 5] ^ st[i + 10] ^ st[i + 15] ^ st[i + 20]; + } + + for i in 0..bc.len() { + let t = bc[(i + 4) % 5] ^ bc[(i + 1) % 5].rotate_left(1); + for j in (0..st.len()).step_by(5) { + st[j + i] ^= t; + } + } + + // Rho Pi + let mut t = st[1]; + + debug_assert_eq!(KECCAKF_ROTC.len(), 24); + for i in 0..24 { + let j = KECCAKF_PILN[i]; + bc[0] = st[j]; + st[j] = t.rotate_left(KECCAKF_ROTC[i]); + t = bc[0]; + } + + // Chi + for j in (0..st.len()).step_by(5) { + for i in 0..bc.len() { + bc[i] = st[j + i]; + } + for i in 0..bc.len() { + st[j + i] ^= (!bc[(i + 1) % 5]) & bc[(i + 2) % 5]; + } + } + + // Iota + debug_assert!(r < KECCAKF_RNDC.len()); + st[0] ^= KECCAKF_RNDC[r]; } fn keccak_f800(header_hash: H256, nonce: u64, result: [u32; 8], st: &mut [u32; 25]) { - for i in 0..8 { - st[i] = (header_hash[4 * i] as u32) + - ((header_hash[4 * i + 1] as u32) << 8) + - ((header_hash[4 * i + 2] as u32) << 16) + - ((header_hash[4 * i + 3] as u32) << 24); - } - - st[8] = nonce as u32; - st[9] = (nonce >> 32) as u32; - - for i in 0..8 { - st[10 + i] = result[i]; - } - - for r in 0..22 { - keccak_f800_round(st, r); - } + for i in 0..8 { + st[i] = (header_hash[4 * i] as u32) + + ((header_hash[4 * i + 1] as u32) << 8) + + ((header_hash[4 * i + 2] as u32) << 16) + + ((header_hash[4 * i + 3] as u32) << 24); + } + + st[8] = nonce as u32; + st[9] = (nonce >> 32) as u32; + + for i in 0..8 { + st[10 + i] = result[i]; + } + + for r in 0..22 { + keccak_f800_round(st, r); + } } pub fn keccak_f800_short(header_hash: H256, nonce: u64, result: [u32; 8]) -> u64 { - let mut st = [0u32; 25]; - keccak_f800(header_hash, nonce, result, &mut st); - (st[0].swap_bytes() as u64) << 32 | st[1].swap_bytes() as u64 + let mut st = [0u32; 25]; + keccak_f800(header_hash, nonce, result, &mut st); + (st[0].swap_bytes() as u64) << 32 | st[1].swap_bytes() as u64 } pub fn keccak_f800_long(header_hash: H256, nonce: u64, result: [u32; 8]) -> H256 { - let mut st = [0u32; 25]; - keccak_f800(header_hash, nonce, result, &mut st); - - // NOTE: transmute from `[u32; 8]` to `[u8; 32]` - unsafe { - std::mem::transmute( - [st[0], st[1], st[2], st[3], st[4], st[5], st[6], st[7]] - ) - } + let mut st = [0u32; 25]; + keccak_f800(header_hash, nonce, result, &mut st); + + // NOTE: transmute from `[u32; 8]` to `[u8; 32]` + unsafe { std::mem::transmute([st[0], st[1], st[2], st[3], st[4], st[5], st[6], st[7]]) } } #[inline] fn fnv1a_hash(h: u32, d: u32) -> u32 { - (h ^ d).wrapping_mul(FNV_PRIME) + (h ^ d).wrapping_mul(FNV_PRIME) } #[derive(Clone)] struct Kiss99 { - z: u32, - w: u32, - jsr: u32, - jcong: u32, + z: u32, + w: u32, + jsr: u32, + jcong: u32, } impl Kiss99 { - fn new(z: u32, w: u32, jsr: u32, jcong: u32) -> Kiss99 { - Kiss99 { z, w, jsr, jcong } - } - - #[inline] - fn next_u32(&mut self) -> u32 { - self.z = 36969u32.wrapping_mul(self.z & 65535).wrapping_add(self.z >> 16); - self.w = 18000u32.wrapping_mul(self.w & 65535).wrapping_add(self.w >> 16); - let mwc = (self.z << 16).wrapping_add(self.w); - self.jsr ^= self.jsr << 17; - self.jsr ^= self.jsr >> 13; - self.jsr ^= self.jsr << 5; - self.jcong = 69069u32.wrapping_mul(self.jcong).wrapping_add(1234567); - - (mwc ^ self.jcong).wrapping_add(self.jsr) - } + fn new(z: u32, w: u32, jsr: u32, jcong: u32) -> Kiss99 { + Kiss99 { z, w, jsr, jcong } + } + + #[inline] + fn next_u32(&mut self) -> u32 { + self.z = 36969u32 + .wrapping_mul(self.z & 65535) + .wrapping_add(self.z >> 16); + self.w = 18000u32 + .wrapping_mul(self.w & 65535) + .wrapping_add(self.w >> 16); + let mwc = (self.z << 16).wrapping_add(self.w); + self.jsr ^= self.jsr << 17; + self.jsr ^= self.jsr >> 13; + self.jsr ^= self.jsr << 5; + self.jcong = 69069u32.wrapping_mul(self.jcong).wrapping_add(1234567); + + (mwc ^ self.jcong).wrapping_add(self.jsr) + } } fn fill_mix(seed: u64, lane_id: u32) -> [u32; PROGPOW_REGS] { - // Use FNV to expand the per-warp seed to per-lane - // Use KISS to expand the per-lane seed to fill mix - let z = fnv1a_hash(FNV_HASH, seed as u32); - let w = fnv1a_hash(z, (seed >> 32) as u32); - let jsr = fnv1a_hash(w, lane_id); - let jcong = fnv1a_hash(jsr, lane_id); + // Use FNV to expand the per-warp seed to per-lane + // Use KISS to expand the per-lane seed to fill mix + let z = fnv1a_hash(FNV_HASH, seed as u32); + let w = fnv1a_hash(z, (seed >> 32) as u32); + let jsr = fnv1a_hash(w, lane_id); + let jcong = fnv1a_hash(jsr, lane_id); - let mut rnd = Kiss99::new(z, w, jsr, jcong); + let mut rnd = Kiss99::new(z, w, jsr, jcong); - let mut mix = [0; PROGPOW_REGS]; + let mut mix = [0; PROGPOW_REGS]; - debug_assert_eq!(PROGPOW_REGS, 32); - for i in 0..32 { - mix[i] = rnd.next_u32(); - } + debug_assert_eq!(PROGPOW_REGS, 32); + for i in 0..32 { + mix[i] = rnd.next_u32(); + } - mix + mix } // Merge new data from b into the value in a. Assuming A has high entropy only // do ops that retain entropy even if B is low entropy (IE don't do A&B) fn merge(a: u32, b: u32, r: u32) -> u32 { - match r % 4 { - 0 => a.wrapping_mul(33).wrapping_add(b), - 1 => (a ^ b).wrapping_mul(33), - 2 => a.rotate_left(((r >> 16) % 31) + 1) ^ b, - _ => a.rotate_right(((r >> 16) % 31) + 1) ^ b, - } + match r % 4 { + 0 => a.wrapping_mul(33).wrapping_add(b), + 1 => (a ^ b).wrapping_mul(33), + 2 => a.rotate_left(((r >> 16) % 31) + 1) ^ b, + _ => a.rotate_right(((r >> 16) % 31) + 1) ^ b, + } } fn math(a: u32, b: u32, r: u32) -> u32 { - match r % 11 { - 0 => a.wrapping_add(b), - 1 => a.wrapping_mul(b), - 2 => ((a as u64).wrapping_mul(b as u64) >> 32) as u32, - 3 => a.min(b), - 4 => a.rotate_left(b), - 5 => a.rotate_right(b), - 6 => a & b, - 7 => a | b, - 8 => a ^ b, - 9 => a.leading_zeros() + b.leading_zeros(), - _ => a.count_ones() + b.count_ones(), - } + match r % 11 { + 0 => a.wrapping_add(b), + 1 => a.wrapping_mul(b), + 2 => ((a as u64).wrapping_mul(b as u64) >> 32) as u32, + 3 => a.min(b), + 4 => a.rotate_left(b), + 5 => a.rotate_right(b), + 6 => a & b, + 7 => a | b, + 8 => a ^ b, + 9 => a.leading_zeros() + b.leading_zeros(), + _ => a.count_ones() + b.count_ones(), + } } fn progpow_init(seed: u64) -> (Kiss99, [u32; PROGPOW_REGS], [u32; PROGPOW_REGS]) { - let z = fnv1a_hash(FNV_HASH, seed as u32); - let w = fnv1a_hash(z, (seed >> 32) as u32); - let jsr = fnv1a_hash(w, seed as u32); - let jcong = fnv1a_hash(jsr, (seed >> 32) as u32); - - let mut rnd = Kiss99::new(z, w, jsr, jcong); - - // Create a random sequence of mix destinations for merge() and mix sources - // for cache reads guarantees every destination merged once and guarantees - // no duplicate cache reads, which could be optimized away. Uses - // Fisher-Yates shuffle. - let mut mix_seq_dst = [0u32; PROGPOW_REGS]; - let mut mix_seq_cache = [0u32; PROGPOW_REGS]; - for i in 0..mix_seq_dst.len() { - mix_seq_dst[i] = i as u32; - mix_seq_cache[i] = i as u32; - } - - for i in (1..mix_seq_dst.len()).rev() { - let j = rnd.next_u32() as usize % (i + 1); - mix_seq_dst.swap(i, j); - - let j = rnd.next_u32() as usize % (i + 1); - mix_seq_cache.swap(i, j); - } - - (rnd, mix_seq_dst, mix_seq_cache) + let z = fnv1a_hash(FNV_HASH, seed as u32); + let w = fnv1a_hash(z, (seed >> 32) as u32); + let jsr = fnv1a_hash(w, seed as u32); + let jcong = fnv1a_hash(jsr, (seed >> 32) as u32); + + let mut rnd = Kiss99::new(z, w, jsr, jcong); + + // Create a random sequence of mix destinations for merge() and mix sources + // for cache reads guarantees every destination merged once and guarantees + // no duplicate cache reads, which could be optimized away. Uses + // Fisher-Yates shuffle. + let mut mix_seq_dst = [0u32; PROGPOW_REGS]; + let mut mix_seq_cache = [0u32; PROGPOW_REGS]; + for i in 0..mix_seq_dst.len() { + mix_seq_dst[i] = i as u32; + mix_seq_cache[i] = i as u32; + } + + for i in (1..mix_seq_dst.len()).rev() { + let j = rnd.next_u32() as usize % (i + 1); + mix_seq_dst.swap(i, j); + + let j = rnd.next_u32() as usize % (i + 1); + mix_seq_cache.swap(i, j); + } + + (rnd, mix_seq_dst, mix_seq_cache) } pub type CDag = [u32; PROGPOW_CACHE_WORDS]; fn progpow_loop( - seed: u64, - loop_: usize, - mix: &mut [[u32; PROGPOW_REGS]; PROGPOW_LANES], - cache: &[Node], - c_dag: &CDag, - data_size: usize, + seed: u64, + loop_: usize, + mix: &mut [[u32; PROGPOW_REGS]; PROGPOW_LANES], + cache: &[Node], + c_dag: &CDag, + data_size: usize, ) { - // All lanes share a base address for the global load. Global offset uses - // mix[0] to guarantee it depends on the load result. - let g_offset = mix[loop_ % PROGPOW_LANES][0] as usize % - (64 * data_size / (PROGPOW_LANES * PROGPOW_DAG_LOADS)); - - // 256 bytes of dag data - let mut dag_item = [0u32; 64]; - - // Fetch DAG nodes (64 bytes each) - for l in 0..PROGPOW_DAG_LOADS { - let index = g_offset * PROGPOW_LANES * PROGPOW_DAG_LOADS + l * 16; - let node = calculate_dag_item(index as u32 / 16, cache); - dag_item[l * 16..(l + 1) * 16].clone_from_slice(node.as_words()); - } - - let (rnd, mix_seq_dst, mix_seq_cache) = progpow_init(seed); - - // Lanes can execute in parallel and will be convergent - for l in 0..mix.len() { - let mut rnd = rnd.clone(); - - // Initialize the seed and mix destination sequence - let mut mix_seq_dst_cnt = 0; - let mut mix_seq_cache_cnt = 0; - - let mut mix_dst = || { - let res = mix_seq_dst[mix_seq_dst_cnt % PROGPOW_REGS] as usize; - mix_seq_dst_cnt += 1; - res - }; - let mut mix_cache = || { - let res = mix_seq_cache[mix_seq_cache_cnt % PROGPOW_REGS] as usize; - mix_seq_cache_cnt += 1; - res - }; - - for i in 0..PROGPOW_CNT_CACHE.max(PROGPOW_CNT_MATH) { - if i < PROGPOW_CNT_CACHE { - // Cached memory access, lanes access random 32-bit locations - // within the first portion of the DAG - let offset = mix[l][mix_cache()] as usize % PROGPOW_CACHE_WORDS; - let data = c_dag[offset]; - let dst = mix_dst(); - - mix[l][dst] = merge(mix[l][dst], data, rnd.next_u32()); - } - - if i < PROGPOW_CNT_MATH { - // Random math - // Generate 2 unique sources - let src_rnd = rnd.next_u32() % (PROGPOW_REGS * (PROGPOW_REGS - 1)) as u32; - let src1 = src_rnd % PROGPOW_REGS as u32; // 0 <= src1 < PROGPOW_REGS - let mut src2 = src_rnd / PROGPOW_REGS as u32; // 0 <= src2 < PROGPOW_REGS - 1 - if src2 >= src1 { - src2 += 1; // src2 is now any reg other than src1 - } - - let data = math(mix[l][src1 as usize], mix[l][src2 as usize], rnd.next_u32()); - let dst = mix_dst(); - - mix[l][dst] = merge(mix[l][dst], data, rnd.next_u32()); - } - } - - // Global load to sequential locations - let mut data_g = [0u32; PROGPOW_DAG_LOADS]; - let index = ((l ^ loop_) % PROGPOW_LANES) * PROGPOW_DAG_LOADS; - for i in 0..PROGPOW_DAG_LOADS { - data_g[i] = dag_item[index + i]; - } - - // Consume the global load data at the very end of the loop to allow - // full latency hiding. Always merge into `mix[0]` to feed the offset - // calculation. - mix[l][0] = merge(mix[l][0], data_g[0], rnd.next_u32()); - for i in 1..PROGPOW_DAG_LOADS { - let dst = mix_dst(); - mix[l][dst] = merge(mix[l][dst], data_g[i], rnd.next_u32()); - } - } + // All lanes share a base address for the global load. Global offset uses + // mix[0] to guarantee it depends on the load result. + let g_offset = mix[loop_ % PROGPOW_LANES][0] as usize + % (64 * data_size / (PROGPOW_LANES * PROGPOW_DAG_LOADS)); + + // 256 bytes of dag data + let mut dag_item = [0u32; 64]; + + // Fetch DAG nodes (64 bytes each) + for l in 0..PROGPOW_DAG_LOADS { + let index = g_offset * PROGPOW_LANES * PROGPOW_DAG_LOADS + l * 16; + let node = calculate_dag_item(index as u32 / 16, cache); + dag_item[l * 16..(l + 1) * 16].clone_from_slice(node.as_words()); + } + + let (rnd, mix_seq_dst, mix_seq_cache) = progpow_init(seed); + + // Lanes can execute in parallel and will be convergent + for l in 0..mix.len() { + let mut rnd = rnd.clone(); + + // Initialize the seed and mix destination sequence + let mut mix_seq_dst_cnt = 0; + let mut mix_seq_cache_cnt = 0; + + let mut mix_dst = || { + let res = mix_seq_dst[mix_seq_dst_cnt % PROGPOW_REGS] as usize; + mix_seq_dst_cnt += 1; + res + }; + let mut mix_cache = || { + let res = mix_seq_cache[mix_seq_cache_cnt % PROGPOW_REGS] as usize; + mix_seq_cache_cnt += 1; + res + }; + + for i in 0..PROGPOW_CNT_CACHE.max(PROGPOW_CNT_MATH) { + if i < PROGPOW_CNT_CACHE { + // Cached memory access, lanes access random 32-bit locations + // within the first portion of the DAG + let offset = mix[l][mix_cache()] as usize % PROGPOW_CACHE_WORDS; + let data = c_dag[offset]; + let dst = mix_dst(); + + mix[l][dst] = merge(mix[l][dst], data, rnd.next_u32()); + } + + if i < PROGPOW_CNT_MATH { + // Random math + // Generate 2 unique sources + let src_rnd = rnd.next_u32() % (PROGPOW_REGS * (PROGPOW_REGS - 1)) as u32; + let src1 = src_rnd % PROGPOW_REGS as u32; // 0 <= src1 < PROGPOW_REGS + let mut src2 = src_rnd / PROGPOW_REGS as u32; // 0 <= src2 < PROGPOW_REGS - 1 + if src2 >= src1 { + src2 += 1; // src2 is now any reg other than src1 + } + + let data = math(mix[l][src1 as usize], mix[l][src2 as usize], rnd.next_u32()); + let dst = mix_dst(); + + mix[l][dst] = merge(mix[l][dst], data, rnd.next_u32()); + } + } + + // Global load to sequential locations + let mut data_g = [0u32; PROGPOW_DAG_LOADS]; + let index = ((l ^ loop_) % PROGPOW_LANES) * PROGPOW_DAG_LOADS; + for i in 0..PROGPOW_DAG_LOADS { + data_g[i] = dag_item[index + i]; + } + + // Consume the global load data at the very end of the loop to allow + // full latency hiding. Always merge into `mix[0]` to feed the offset + // calculation. + mix[l][0] = merge(mix[l][0], data_g[0], rnd.next_u32()); + for i in 1..PROGPOW_DAG_LOADS { + let dst = mix_dst(); + mix[l][dst] = merge(mix[l][dst], data_g[i], rnd.next_u32()); + } + } } pub fn progpow( - header_hash: H256, - nonce: u64, - block_number: u64, - cache: &[Node], - c_dag: &CDag, + header_hash: H256, + nonce: u64, + block_number: u64, + cache: &[Node], + c_dag: &CDag, ) -> (H256, H256) { - let mut mix = [[0u32; PROGPOW_REGS]; PROGPOW_LANES]; - let mut lane_results = [0u32; PROGPOW_LANES]; - let mut result = [0u32; 8]; - - let data_size = get_data_size(block_number) / PROGPOW_MIX_BYTES; - - // NOTE: This assert is required to aid the optimizer elide the non-zero - // remainder check in `progpow_loop`. - assert!(data_size > 0); - - // Initialize mix for all lanes - let seed = keccak_f800_short(header_hash, nonce, result); - - for l in 0..mix.len() { - mix[l] = fill_mix(seed, l as u32); - } - - // Execute the randomly generated inner loop - let period = block_number / PROGPOW_PERIOD_LENGTH as u64; - for i in 0..PROGPOW_CNT_DAG { - progpow_loop( - period, - i, - &mut mix, - cache, - c_dag, - data_size, - ); - } - - // Reduce mix data to a single per-lane result - for l in 0..lane_results.len() { - lane_results[l] = FNV_HASH; - for i in 0..PROGPOW_REGS { - lane_results[l] = fnv1a_hash(lane_results[l], mix[l][i]); - } - } - - // Reduce all lanes to a single 128-bit result - result = [FNV_HASH; 8]; - for l in 0..PROGPOW_LANES { - result[l % 8] = fnv1a_hash(result[l % 8], lane_results[l]); - } - - let digest = keccak_f800_long(header_hash, seed, result); - - // NOTE: transmute from `[u32; 8]` to `[u8; 32]` - let result = unsafe { ::std::mem::transmute(result) }; - - (digest, result) + let mut mix = [[0u32; PROGPOW_REGS]; PROGPOW_LANES]; + let mut lane_results = [0u32; PROGPOW_LANES]; + let mut result = [0u32; 8]; + + let data_size = get_data_size(block_number) / PROGPOW_MIX_BYTES; + + // NOTE: This assert is required to aid the optimizer elide the non-zero + // remainder check in `progpow_loop`. + assert!(data_size > 0); + + // Initialize mix for all lanes + let seed = keccak_f800_short(header_hash, nonce, result); + + for l in 0..mix.len() { + mix[l] = fill_mix(seed, l as u32); + } + + // Execute the randomly generated inner loop + let period = block_number / PROGPOW_PERIOD_LENGTH as u64; + for i in 0..PROGPOW_CNT_DAG { + progpow_loop(period, i, &mut mix, cache, c_dag, data_size); + } + + // Reduce mix data to a single per-lane result + for l in 0..lane_results.len() { + lane_results[l] = FNV_HASH; + for i in 0..PROGPOW_REGS { + lane_results[l] = fnv1a_hash(lane_results[l], mix[l][i]); + } + } + + // Reduce all lanes to a single 128-bit result + result = [FNV_HASH; 8]; + for l in 0..PROGPOW_LANES { + result[l % 8] = fnv1a_hash(result[l % 8], lane_results[l]); + } + + let digest = keccak_f800_long(header_hash, seed, result); + + // NOTE: transmute from `[u32; 8]` to `[u8; 32]` + let result = unsafe { ::std::mem::transmute(result) }; + + (digest, result) } pub fn generate_cdag(cache: &[Node]) -> CDag { - let mut c_dag = [0u32; PROGPOW_CACHE_WORDS]; + let mut c_dag = [0u32; PROGPOW_CACHE_WORDS]; - for i in 0..PROGPOW_CACHE_WORDS / 16 { - let node = calculate_dag_item(i as u32, cache); - for j in 0..16 { - c_dag[i * 16 + j] = node.as_words()[j]; - } - } + for i in 0..PROGPOW_CACHE_WORDS / 16 { + let node = calculate_dag_item(i as u32, cache); + for j in 0..16 { + c_dag[i * 16 + j] = node.as_words()[j]; + } + } - c_dag + c_dag } #[cfg(test)] mod test { - use tempdir::TempDir; - - use cache::{NodeCacheBuilder, OptimizeFor}; - use keccak::H256; - use rustc_hex::FromHex; - use serde_json::{self, Value}; - use std::collections::VecDeque; - use super::*; - - fn h256(hex: &str) -> H256 { - let bytes = FromHex::from_hex(hex).unwrap(); - let mut res = [0; 32]; - res.copy_from_slice(&bytes); - res - } - - #[test] - fn test_cdag() { - let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); - let tempdir = TempDir::new("").unwrap(); - let cache = builder.new_cache(tempdir.into_path(), 0); - - let c_dag = generate_cdag(cache.as_ref()); - - let expected = vec![ - 690150178u32, 1181503948, 2248155602, 2118233073, 2193871115, - 1791778428, 1067701239, 724807309, 530799275, 3480325829, 3899029234, - 1998124059, 2541974622, 1100859971, 1297211151, 3268320000, 2217813733, - 2690422980, 3172863319, 2651064309 - ]; - - assert_eq!( - c_dag.iter().take(20).cloned().collect::>(), - expected, - ); - } - - #[test] - fn test_random_merge() { - let tests = [ - (1000000u32, 101u32, 33000101u32), - (2000000, 102, 66003366), - (3000000, 103, 6000103), - (4000000, 104, 2000104), - (1000000, 0, 33000000), - (2000000, 0, 66000000), - (3000000, 0, 6000000), - (4000000, 0, 2000000), - ]; - - for (i, &(a, b, expected)) in tests.iter().enumerate() { - assert_eq!( - merge(a, b, i as u32), - expected, - ); - } - } - - #[test] - fn test_random_math() { - let tests = [ - (20u32, 22u32, 42u32), - (70000, 80000, 1305032704), - (70000, 80000, 1), - (1, 2, 1), - (3, 10000, 196608), - (3, 0, 3), - (3, 6, 2), - (3, 6, 7), - (3, 6, 5), - (0, 0xffffffff, 32), - (3 << 13, 1 << 5, 3), - (22, 20, 42), - (80000, 70000, 1305032704), - (80000, 70000, 1), - (2, 1, 1), - (10000, 3, 80000), - (0, 3, 0), - (6, 3, 2), - (6, 3, 7), - (6, 3, 5), - (0, 0xffffffff, 32), - (3 << 13, 1 << 5, 3), - ]; - - for (i, &(a, b, expected)) in tests.iter().enumerate() { - assert_eq!( - math(a, b, i as u32), - expected, - ); - } - } - - #[test] - fn test_keccak_256() { - let expected = "5dd431e5fbc604f499bfa0232f45f8f142d0ff5178f539e5a7800bf0643697af"; - assert_eq!( - keccak_f800_long([0; 32], 0, [0; 8]), - h256(expected), - ); - } - - #[test] - fn test_keccak_64() { - let expected: u64 = 0x5dd431e5fbc604f4; - assert_eq!( - keccak_f800_short([0; 32], 0, [0; 8]), - expected, - ); - } - - #[test] - fn test_progpow_hash() { - let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); - let tempdir = TempDir::new("").unwrap(); - let cache = builder.new_cache(tempdir.into_path(), 0); - let c_dag = generate_cdag(cache.as_ref()); - - let header_hash = [0; 32]; - - let (digest, result) = progpow( - header_hash, - 0, - 0, - cache.as_ref(), - &c_dag, - ); - - let expected_digest = FromHex::from_hex("63155f732f2bf556967f906155b510c917e48e99685ead76ea83f4eca03ab12b").unwrap(); - let expected_result = FromHex::from_hex("faeb1be51075b03a4ff44b335067951ead07a3b078539ace76fd56fc410557a3").unwrap(); - - assert_eq!( - digest.to_vec(), - expected_digest, - ); - - assert_eq!( - result.to_vec(), - expected_result, - ); - } - - #[test] - fn test_progpow_testvectors() { - struct ProgpowTest { - block_number: u64, - header_hash: H256, - nonce: u64, - mix_hash: H256, - final_hash: H256, - } - - let tests: Vec> = - serde_json::from_slice(include_bytes!("../res/progpow_testvectors.json")).unwrap(); - - let tests: Vec = tests.into_iter().map(|mut test: VecDeque| { - assert!(test.len() == 5); - - let block_number: u64 = serde_json::from_value(test.pop_front().unwrap()).unwrap(); - let header_hash: String = serde_json::from_value(test.pop_front().unwrap()).unwrap(); - let nonce: String = serde_json::from_value(test.pop_front().unwrap()).unwrap(); - let mix_hash: String = serde_json::from_value(test.pop_front().unwrap()).unwrap(); - let final_hash: String = serde_json::from_value(test.pop_front().unwrap()).unwrap(); - - ProgpowTest { - block_number, - header_hash: h256(&header_hash), - nonce: u64::from_str_radix(&nonce, 16).unwrap(), - mix_hash: h256(&mix_hash), - final_hash: h256(&final_hash), - } - }).collect(); - - for test in tests { - let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); - let tempdir = TempDir::new("").unwrap(); - let cache = builder.new_cache(tempdir.path().to_owned(), test.block_number); - let c_dag = generate_cdag(cache.as_ref()); - - let (digest, result) = progpow( - test.header_hash, - test.nonce, - test.block_number, - cache.as_ref(), - &c_dag, - ); - - assert_eq!(digest, test.final_hash); - assert_eq!(result, test.mix_hash); - } - } + use tempdir::TempDir; + + use super::*; + use cache::{NodeCacheBuilder, OptimizeFor}; + use keccak::H256; + use rustc_hex::FromHex; + use serde_json::{self, Value}; + use std::collections::VecDeque; + + fn h256(hex: &str) -> H256 { + let bytes = FromHex::from_hex(hex).unwrap(); + let mut res = [0; 32]; + res.copy_from_slice(&bytes); + res + } + + #[test] + fn test_cdag() { + let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); + let tempdir = TempDir::new("").unwrap(); + let cache = builder.new_cache(tempdir.into_path(), 0); + + let c_dag = generate_cdag(cache.as_ref()); + + let expected = vec![ + 690150178u32, + 1181503948, + 2248155602, + 2118233073, + 2193871115, + 1791778428, + 1067701239, + 724807309, + 530799275, + 3480325829, + 3899029234, + 1998124059, + 2541974622, + 1100859971, + 1297211151, + 3268320000, + 2217813733, + 2690422980, + 3172863319, + 2651064309, + ]; + + assert_eq!(c_dag.iter().take(20).cloned().collect::>(), expected,); + } + + #[test] + fn test_random_merge() { + let tests = [ + (1000000u32, 101u32, 33000101u32), + (2000000, 102, 66003366), + (3000000, 103, 6000103), + (4000000, 104, 2000104), + (1000000, 0, 33000000), + (2000000, 0, 66000000), + (3000000, 0, 6000000), + (4000000, 0, 2000000), + ]; + + for (i, &(a, b, expected)) in tests.iter().enumerate() { + assert_eq!(merge(a, b, i as u32), expected,); + } + } + + #[test] + fn test_random_math() { + let tests = [ + (20u32, 22u32, 42u32), + (70000, 80000, 1305032704), + (70000, 80000, 1), + (1, 2, 1), + (3, 10000, 196608), + (3, 0, 3), + (3, 6, 2), + (3, 6, 7), + (3, 6, 5), + (0, 0xffffffff, 32), + (3 << 13, 1 << 5, 3), + (22, 20, 42), + (80000, 70000, 1305032704), + (80000, 70000, 1), + (2, 1, 1), + (10000, 3, 80000), + (0, 3, 0), + (6, 3, 2), + (6, 3, 7), + (6, 3, 5), + (0, 0xffffffff, 32), + (3 << 13, 1 << 5, 3), + ]; + + for (i, &(a, b, expected)) in tests.iter().enumerate() { + assert_eq!(math(a, b, i as u32), expected,); + } + } + + #[test] + fn test_keccak_256() { + let expected = "5dd431e5fbc604f499bfa0232f45f8f142d0ff5178f539e5a7800bf0643697af"; + assert_eq!(keccak_f800_long([0; 32], 0, [0; 8]), h256(expected),); + } + + #[test] + fn test_keccak_64() { + let expected: u64 = 0x5dd431e5fbc604f4; + assert_eq!(keccak_f800_short([0; 32], 0, [0; 8]), expected,); + } + + #[test] + fn test_progpow_hash() { + let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); + let tempdir = TempDir::new("").unwrap(); + let cache = builder.new_cache(tempdir.into_path(), 0); + let c_dag = generate_cdag(cache.as_ref()); + + let header_hash = [0; 32]; + + let (digest, result) = progpow(header_hash, 0, 0, cache.as_ref(), &c_dag); + + let expected_digest = + FromHex::from_hex("63155f732f2bf556967f906155b510c917e48e99685ead76ea83f4eca03ab12b") + .unwrap(); + let expected_result = + FromHex::from_hex("faeb1be51075b03a4ff44b335067951ead07a3b078539ace76fd56fc410557a3") + .unwrap(); + + assert_eq!(digest.to_vec(), expected_digest,); + + assert_eq!(result.to_vec(), expected_result,); + } + + #[test] + fn test_progpow_testvectors() { + struct ProgpowTest { + block_number: u64, + header_hash: H256, + nonce: u64, + mix_hash: H256, + final_hash: H256, + } + + let tests: Vec> = + serde_json::from_slice(include_bytes!("../res/progpow_testvectors.json")).unwrap(); + + let tests: Vec = tests + .into_iter() + .map(|mut test: VecDeque| { + assert!(test.len() == 5); + + let block_number: u64 = serde_json::from_value(test.pop_front().unwrap()).unwrap(); + let header_hash: String = + serde_json::from_value(test.pop_front().unwrap()).unwrap(); + let nonce: String = serde_json::from_value(test.pop_front().unwrap()).unwrap(); + let mix_hash: String = serde_json::from_value(test.pop_front().unwrap()).unwrap(); + let final_hash: String = serde_json::from_value(test.pop_front().unwrap()).unwrap(); + + ProgpowTest { + block_number, + header_hash: h256(&header_hash), + nonce: u64::from_str_radix(&nonce, 16).unwrap(), + mix_hash: h256(&mix_hash), + final_hash: h256(&final_hash), + } + }) + .collect(); + + for test in tests { + let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); + let tempdir = TempDir::new("").unwrap(); + let cache = builder.new_cache(tempdir.path().to_owned(), test.block_number); + let c_dag = generate_cdag(cache.as_ref()); + + let (digest, result) = progpow( + test.header_hash, + test.nonce, + test.block_number, + cache.as_ref(), + &c_dag, + ); + + assert_eq!(digest, test.final_hash); + assert_eq!(result, test.mix_hash); + } + } } diff --git a/ethash/src/seed_compute.rs b/ethash/src/seed_compute.rs index 51782803285..6ca9aba7e3b 100644 --- a/ethash/src/seed_compute.rs +++ b/ethash/src/seed_compute.rs @@ -1,102 +1,110 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use shared; use keccak::{keccak_256, H256}; +use shared; use std::cell::Cell; #[derive(Default)] pub struct SeedHashCompute { - prev_epoch: Cell, - prev_seedhash: Cell, + prev_epoch: Cell, + prev_seedhash: Cell, } impl SeedHashCompute { - #[inline] - fn reset_cache(&self) { - self.prev_epoch.set(0); - self.prev_seedhash.set([0u8; 32]); - } - - #[inline] - pub fn hash_block_number(&self, block_number: u64) -> H256 { - self.hash_epoch(shared::epoch(block_number)) - } - - #[inline] - pub fn hash_epoch(&self, epoch: u64) -> H256 { - if epoch < self.prev_epoch.get() { - // can't build on previous hash if requesting an older block - self.reset_cache(); - } - if epoch > self.prev_epoch.get() { - let seed_hash = SeedHashCompute::resume_compute_seedhash( - self.prev_seedhash.get(), - self.prev_epoch.get(), - epoch, - ); - self.prev_seedhash.set(seed_hash); - self.prev_epoch.set(epoch); - } - self.prev_seedhash.get() - } - - #[inline] - pub fn resume_compute_seedhash(mut hash: H256, start_epoch: u64, end_epoch: u64) -> H256 { - for _ in start_epoch..end_epoch { - keccak_256::inplace(&mut hash); - } - hash - } + #[inline] + fn reset_cache(&self) { + self.prev_epoch.set(0); + self.prev_seedhash.set([0u8; 32]); + } + + #[inline] + pub fn hash_block_number(&self, block_number: u64) -> H256 { + self.hash_epoch(shared::epoch(block_number)) + } + + #[inline] + pub fn hash_epoch(&self, epoch: u64) -> H256 { + if epoch < self.prev_epoch.get() { + // can't build on previous hash if requesting an older block + self.reset_cache(); + } + if epoch > self.prev_epoch.get() { + let seed_hash = SeedHashCompute::resume_compute_seedhash( + self.prev_seedhash.get(), + self.prev_epoch.get(), + epoch, + ); + self.prev_seedhash.set(seed_hash); + self.prev_epoch.set(epoch); + } + self.prev_seedhash.get() + } + + #[inline] + pub fn resume_compute_seedhash(mut hash: H256, start_epoch: u64, end_epoch: u64) -> H256 { + for _ in start_epoch..end_epoch { + keccak_256::inplace(&mut hash); + } + hash + } } #[cfg(test)] mod tests { - use super::SeedHashCompute; - - #[test] - fn test_seed_compute_once() { - let seed_compute = SeedHashCompute::default(); - let hash = [241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253, 147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162]; - assert_eq!(seed_compute.hash_block_number(486382), hash); - } - - #[test] - fn test_seed_compute_zero() { - let seed_compute = SeedHashCompute::default(); - assert_eq!(seed_compute.hash_block_number(0), [0u8; 32]); - } - - #[test] - fn test_seed_compute_after_older() { - let seed_compute = SeedHashCompute::default(); - // calculating an older value first shouldn't affect the result - let _ = seed_compute.hash_block_number(50000); - let hash = [241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253, 147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162]; - assert_eq!(seed_compute.hash_block_number(486382), hash); - } - - #[test] - fn test_seed_compute_after_newer() { - let seed_compute = SeedHashCompute::default(); - // calculating an newer value first shouldn't affect the result - let _ = seed_compute.hash_block_number(972764); - let hash = [241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253, 147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162]; - assert_eq!(seed_compute.hash_block_number(486382), hash); - } - + use super::SeedHashCompute; + + #[test] + fn test_seed_compute_once() { + let seed_compute = SeedHashCompute::default(); + let hash = [ + 241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253, + 147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162, + ]; + assert_eq!(seed_compute.hash_block_number(486382), hash); + } + + #[test] + fn test_seed_compute_zero() { + let seed_compute = SeedHashCompute::default(); + assert_eq!(seed_compute.hash_block_number(0), [0u8; 32]); + } + + #[test] + fn test_seed_compute_after_older() { + let seed_compute = SeedHashCompute::default(); + // calculating an older value first shouldn't affect the result + let _ = seed_compute.hash_block_number(50000); + let hash = [ + 241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253, + 147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162, + ]; + assert_eq!(seed_compute.hash_block_number(486382), hash); + } + + #[test] + fn test_seed_compute_after_newer() { + let seed_compute = SeedHashCompute::default(); + // calculating an newer value first shouldn't affect the result + let _ = seed_compute.hash_block_number(972764); + let hash = [ + 241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253, + 147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162, + ]; + assert_eq!(seed_compute.hash_block_number(486382), hash); + } } diff --git a/ethash/src/shared.rs b/ethash/src/shared.rs index 2c9a9fa9d0e..4f72b61a8ad 100644 --- a/ethash/src/shared.rs +++ b/ethash/src/shared.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use primal::is_prime; @@ -31,38 +31,39 @@ pub const NODE_WORDS: usize = NODE_BYTES / 4; pub const NODE_BYTES: usize = 64; pub fn epoch(block_number: u64) -> u64 { - block_number / ETHASH_EPOCH_LENGTH + block_number / ETHASH_EPOCH_LENGTH } static CHARS: &'static [u8] = b"0123456789abcdef"; pub fn to_hex(bytes: &[u8]) -> String { - let mut v = Vec::with_capacity(bytes.len() * 2); - for &byte in bytes.iter() { - v.push(CHARS[(byte >> 4) as usize]); - v.push(CHARS[(byte & 0xf) as usize]); - } + let mut v = Vec::with_capacity(bytes.len() * 2); + for &byte in bytes.iter() { + v.push(CHARS[(byte >> 4) as usize]); + v.push(CHARS[(byte & 0xf) as usize]); + } - unsafe { String::from_utf8_unchecked(v) } + unsafe { String::from_utf8_unchecked(v) } } pub fn get_cache_size(block_number: u64) -> usize { - // TODO: Memoise - let mut sz: u64 = CACHE_BYTES_INIT + CACHE_BYTES_GROWTH * (block_number / ETHASH_EPOCH_LENGTH); - sz = sz - NODE_BYTES as u64; - while !is_prime(sz / NODE_BYTES as u64) { - sz = sz - 2 * NODE_BYTES as u64; - } - sz as usize + // TODO: Memoise + let mut sz: u64 = CACHE_BYTES_INIT + CACHE_BYTES_GROWTH * (block_number / ETHASH_EPOCH_LENGTH); + sz = sz - NODE_BYTES as u64; + while !is_prime(sz / NODE_BYTES as u64) { + sz = sz - 2 * NODE_BYTES as u64; + } + sz as usize } pub fn get_data_size(block_number: u64) -> usize { - // TODO: Memoise - let mut sz: u64 = DATASET_BYTES_INIT + DATASET_BYTES_GROWTH * (block_number / ETHASH_EPOCH_LENGTH); - sz = sz - ETHASH_MIX_BYTES as u64; - while !is_prime(sz / ETHASH_MIX_BYTES as u64) { - sz = sz - 2 * ETHASH_MIX_BYTES as u64; - } - sz as usize + // TODO: Memoise + let mut sz: u64 = + DATASET_BYTES_INIT + DATASET_BYTES_GROWTH * (block_number / ETHASH_EPOCH_LENGTH); + sz = sz - ETHASH_MIX_BYTES as u64; + while !is_prime(sz / ETHASH_MIX_BYTES as u64) { + sz = sz - 2 * ETHASH_MIX_BYTES as u64; + } + sz as usize } pub type NodeBytes = [u8; NODE_BYTES]; @@ -81,7 +82,7 @@ macro_rules! static_assert_size_eq { }; (@inner $a:ty, $b:ty) => { unsafe { - let val: $b = ::std::mem::uninitialized(); + let val: $b = ::mem::MaybeUninit::uninit().assume_init(); let _: $a = ::std::mem::transmute(val); } }; @@ -100,15 +101,19 @@ static_assert_size_eq!(Node, NodeBytes, NodeWords, NodeDwords); #[repr(C)] pub union Node { - pub dwords: NodeDwords, - pub words: NodeWords, - pub bytes: NodeBytes, + pub dwords: NodeDwords, + pub words: NodeWords, + pub bytes: NodeBytes, } impl Clone for Node { - fn clone(&self) -> Self { - unsafe { Node { bytes: *&self.bytes } } - } + fn clone(&self) -> Self { + unsafe { + Node { + bytes: *&self.bytes, + } + } + } } // We use `inline(always)` because I was experiencing an 100% slowdown and `perf` showed that these @@ -117,33 +122,33 @@ impl Clone for Node { // performance regression. It's not caused by the `debug_assert_eq!` either, your guess is as good // as mine. impl Node { - #[inline(always)] - pub fn as_bytes(&self) -> &NodeBytes { - unsafe { &self.bytes } - } - - #[inline(always)] - pub fn as_bytes_mut(&mut self) -> &mut NodeBytes { - unsafe { &mut self.bytes } - } - - #[inline(always)] - pub fn as_words(&self) -> &NodeWords { - unsafe { &self.words } - } - - #[inline(always)] - pub fn as_words_mut(&mut self) -> &mut NodeWords { - unsafe { &mut self.words } - } - - #[inline(always)] - pub fn as_dwords(&self) -> &NodeDwords { - unsafe { &self.dwords } - } - - #[inline(always)] - pub fn as_dwords_mut(&mut self) -> &mut NodeDwords { - unsafe { &mut self.dwords } - } + #[inline(always)] + pub fn as_bytes(&self) -> &NodeBytes { + unsafe { &self.bytes } + } + + #[inline(always)] + pub fn as_bytes_mut(&mut self) -> &mut NodeBytes { + unsafe { &mut self.bytes } + } + + #[inline(always)] + pub fn as_words(&self) -> &NodeWords { + unsafe { &self.words } + } + + #[inline(always)] + pub fn as_words_mut(&mut self) -> &mut NodeWords { + unsafe { &mut self.words } + } + + #[inline(always)] + pub fn as_dwords(&self) -> &NodeDwords { + unsafe { &self.dwords } + } + + #[inline(always)] + pub fn as_dwords_mut(&mut self) -> &mut NodeDwords { + unsafe { &mut self.dwords } + } } diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 336d6a0fcc8..62e82d3ff46 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -1,6 +1,6 @@ [package] -description = "Ethcore library" -homepage = "http://parity.io" +description = "OpenEthereum (EthCore) Library" +homepage = "https://github.com/openethereum/openethereum" license = "GPL-3.0" name = "ethcore" version = "1.12.0" @@ -9,10 +9,9 @@ authors = ["Parity Technologies "] [dependencies] ansi_term = "0.10" blooms-db = { path = "../util/blooms-db", optional = true } -bn = { git = "https://github.com/paritytech/bn", default-features = false } -byteorder = "1.0" common-types = { path = "types" } -crossbeam = "0.4" +crossbeam-utils = "0.6" +eip-152 = { version = "0.1", path = "../util/EIP-152" } env_logger = { version = "0.5", optional = true } error-chain = { version = "0.12", default-features = false } ethabi = "6.0" @@ -21,6 +20,7 @@ ethabi-derive = "6.0" ethash = { path = "../ethash" } ethcore-blockchain = { path = "./blockchain" } ethcore-bloom-journal = { path = "../util/bloom" } +ethcore-builtin = { path = "./builtin" } ethcore-call-contract = { path = "./call-contract" } ethcore-db = { path = "./db" } ethcore-io = { path = "../util/io" } @@ -30,6 +30,7 @@ ethereum-types = "0.4" ethjson = { path = "../json" } ethkey = { path = "../accounts/ethkey" } evm = { path = "evm" } +globset = "0.4" hash-db = "0.11.0" heapsize = "0.4" itertools = "0.5" @@ -44,31 +45,34 @@ len-caching-lock = { path = "../util/len-caching-lock" } log = "0.4" lru-cache = "0.1" macros = { path = "../util/macros" } +maplit = "1" memory-cache = { path = "../util/memory-cache" } -memory-db = "0.11.0" -num = { version = "0.1", default-features = false, features = ["bigint"] } +memory-db = { path = "../util/memory-db" } num_cpus = "1.2" parity-bytes = "0.1" -parity-crypto = "0.3.0" parity-snappy = "0.1" parking_lot = "0.7" trie-db = "0.11.0" patricia-trie-ethereum = { path = "../util/patricia-trie-ethereum" } rand = "0.4" -rayon = "1.0" +rayon = "1.1" +regex = "1.3.9" rlp = { version = "0.3.0", features = ["ethereum"] } rlp_derive = { path = "../util/rlp-derive" } rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" +serde_json = "1.0" stats = { path = "../util/stats" } tempdir = {version="0.3", optional = true} +tempfile = "3.1.0" time-utils = { path = "../util/time-utils" } trace-time = "0.1" triehash-ethereum = { version = "0.2", path = "../util/triehash-ethereum" } unexpected = { path = "../util/unexpected" } using_queue = { path = "../miner/using-queue" } vm = { path = "vm" } +walkdir = "2.3" wasm = { path = "wasm" } [dev-dependencies] @@ -77,6 +81,7 @@ criterion = "0.2" env_logger = "0.5" ethcore-accounts = { path = "../accounts" } fetch = { path = "../util/fetch" } +hex-literal = "0.2.1" kvdb-rocksdb = "0.1.3" parity-runtime = { path = "../util/runtime" } rlp_compress = { path = "../util/rlp-compress" } @@ -89,7 +94,8 @@ parity = ["work-notify", "price-info", "stratum"] # but might be omitted for other dependent crates. work-notify = ["ethcore-miner/work-notify"] price-info = ["ethcore-miner/price-info"] -stratum = ["ethcore-stratum"] +stratum = [ "ethcore-stratum" ] + # Disables seal verification for mined blocks. # This allows you to submit any seal via RPC to test and benchmark @@ -111,7 +117,7 @@ ci-skip-tests = [] # Run memory/cpu heavy tests. test-heavy = [] # Compile test helpers -test-helpers = ["tempdir", "kvdb-rocksdb", "blooms-db"] +test-helpers = ["tempdir", "kvdb-rocksdb", "blooms-db", "common-types/test-helpers"] # Enables slow 'to-pod-full' method for use in tests and evmbin. to-pod-full = [] diff --git a/ethcore/benches/builtin.rs b/ethcore/benches/builtin.rs index d7ed483dd03..6eaf50562f0 100644 --- a/ethcore/benches/builtin.rs +++ b/ethcore/benches/builtin.rs @@ -1,151 +1,143 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . #[macro_use] extern crate criterion; #[macro_use] extern crate lazy_static; - extern crate ethcore; +extern crate ethcore_builtin; extern crate ethereum_types; extern crate parity_bytes as bytes; extern crate rustc_hex; -use criterion::{Criterion, Bencher}; use bytes::BytesRef; -use ethcore::builtin::Builtin; -use ethcore::machine::EthereumMachine; -use ethereum_types::U256; -use ethcore::ethereum::new_byzantium_test_machine; +use criterion::{Bencher, Criterion}; +use ethcore::{ethereum::new_byzantium_test_machine, machine::EthereumMachine}; +use ethcore_builtin::Builtin; use rustc_hex::FromHex; lazy_static! { - static ref BYZANTIUM_MACHINE: EthereumMachine = new_byzantium_test_machine(); + static ref BYZANTIUM_MACHINE: EthereumMachine = new_byzantium_test_machine(); } struct BuiltinBenchmark<'a> { - builtin: &'a Builtin, - input: Vec, - expected: Vec, + builtin: &'a Builtin, + input: Vec, + expected: Vec, } impl<'a> BuiltinBenchmark<'a> { - fn new(builtin_address: &'static str, input: &str, expected: &str) -> BuiltinBenchmark<'a> { - let builtins = BYZANTIUM_MACHINE.builtins(); - - let builtin = builtins.get(&builtin_address.into()).unwrap().clone(); - let input = FromHex::from_hex(input).unwrap(); - let expected = FromHex::from_hex(expected).unwrap(); + fn new(builtin_address: &'static str, input: &str, expected: &str) -> BuiltinBenchmark<'a> { + let builtins = BYZANTIUM_MACHINE.builtins(); - BuiltinBenchmark { - builtin, input, expected - } - } + let builtin = builtins.get(&builtin_address.into()).unwrap().clone(); + let input = FromHex::from_hex(input).unwrap(); + let expected = FromHex::from_hex(expected).unwrap(); - fn gas_cost(&self) -> U256 { - self.builtin.cost(&self.input) - } + BuiltinBenchmark { + builtin, + input, + expected, + } + } - fn run(&self, b: &mut Bencher) { - let mut output = vec![0; self.expected.len()]; + fn run(&self, b: &mut Bencher) { + let mut output = vec![0; self.expected.len()]; - b.iter(|| { - self.builtin.execute(&self.input, &mut BytesRef::Fixed(&mut output)).unwrap(); - }); + b.iter(|| { + self.builtin + .execute(&self.input, &mut BytesRef::Fixed(&mut output)) + .unwrap(); + }); - assert_eq!(self.expected[..], output[..]); - } + assert_eq!(self.expected[..], output[..]); + } } -fn bench( - id: &str, - builtin_address: &'static str, - input: &str, - expected: &str, - b: &mut Criterion, -) { - let bench = BuiltinBenchmark::new(builtin_address, input, expected); - b.bench_function(id, move |b| bench.run(b)); +fn bench(id: &str, builtin_address: &'static str, input: &str, expected: &str, b: &mut Criterion) { + let bench = BuiltinBenchmark::new(builtin_address, input, expected); + b.bench_function(id, move |b| bench.run(b)); } criterion_group!( - builtin, - ecrecover, - sha256, - ripemd, - identity, - modexp_eip_example1, - modexp_eip_example2, - modexp_nagydani_1_square, - modexp_nagydani_1_qube, - modexp_nagydani_1_pow0x10001, - modexp_nagydani_2_square, - modexp_nagydani_2_qube, - modexp_nagydani_2_pow0x10001, - modexp_nagydani_3_square, - modexp_nagydani_3_qube, - modexp_nagydani_3_pow0x10001, - modexp_nagydani_4_square, - modexp_nagydani_4_qube, - modexp_nagydani_4_pow0x10001, - modexp_nagydani_5_square, - modexp_nagydani_5_qube, - modexp_nagydani_5_pow0x10001, - alt_bn128_add_chfast1, - alt_bn128_add_chfast2, - alt_bn128_add_cdetrio1, - alt_bn128_add_cdetrio2, - alt_bn128_add_cdetrio3, - alt_bn128_add_cdetrio4, - alt_bn128_add_cdetrio5, - alt_bn128_add_cdetrio6, - alt_bn128_add_cdetrio7, - alt_bn128_add_cdetrio8, - alt_bn128_add_cdetrio9, - alt_bn128_add_cdetrio10, - alt_bn128_add_cdetrio11, - alt_bn128_add_cdetrio12, - alt_bn128_add_cdetrio13, - alt_bn128_add_cdetrio14, - alt_bn128_mul_chfast1, - alt_bn128_mul_chfast2, - alt_bn128_mul_chfast3, - alt_bn128_mul_cdetrio1, - alt_bn128_mul_cdetrio6, - alt_bn128_mul_cdetrio11, - alt_bn128_pairing_jeff1, - alt_bn128_pairing_jeff2, - alt_bn128_pairing_jeff3, - alt_bn128_pairing_jeff4, - alt_bn128_pairing_jeff5, - alt_bn128_pairing_jeff6, - alt_bn128_pairing_empty_data, - alt_bn128_pairing_one_point, - alt_bn128_pairing_two_point_match_2, - alt_bn128_pairing_two_point_match_3, - alt_bn128_pairing_two_point_match_4, - alt_bn128_pairing_ten_point_match_1, - alt_bn128_pairing_ten_point_match_2, - alt_bn128_pairing_ten_point_match_3 + builtin, + ecrecover, + sha256, + ripemd, + identity, + modexp_eip_example1, + modexp_eip_example2, + modexp_nagydani_1_square, + modexp_nagydani_1_qube, + modexp_nagydani_1_pow0x10001, + modexp_nagydani_2_square, + modexp_nagydani_2_qube, + modexp_nagydani_2_pow0x10001, + modexp_nagydani_3_square, + modexp_nagydani_3_qube, + modexp_nagydani_3_pow0x10001, + modexp_nagydani_4_square, + modexp_nagydani_4_qube, + modexp_nagydani_4_pow0x10001, + modexp_nagydani_5_square, + modexp_nagydani_5_qube, + modexp_nagydani_5_pow0x10001, + alt_bn128_add_chfast1, + alt_bn128_add_chfast2, + alt_bn128_add_cdetrio1, + alt_bn128_add_cdetrio2, + alt_bn128_add_cdetrio3, + alt_bn128_add_cdetrio4, + alt_bn128_add_cdetrio5, + alt_bn128_add_cdetrio6, + alt_bn128_add_cdetrio7, + alt_bn128_add_cdetrio8, + alt_bn128_add_cdetrio9, + alt_bn128_add_cdetrio10, + alt_bn128_add_cdetrio11, + alt_bn128_add_cdetrio12, + alt_bn128_add_cdetrio13, + alt_bn128_add_cdetrio14, + alt_bn128_mul_chfast1, + alt_bn128_mul_chfast2, + alt_bn128_mul_chfast3, + alt_bn128_mul_cdetrio1, + alt_bn128_mul_cdetrio6, + alt_bn128_mul_cdetrio11, + alt_bn128_pairing_jeff1, + alt_bn128_pairing_jeff2, + alt_bn128_pairing_jeff3, + alt_bn128_pairing_jeff4, + alt_bn128_pairing_jeff5, + alt_bn128_pairing_jeff6, + alt_bn128_pairing_empty_data, + alt_bn128_pairing_one_point, + alt_bn128_pairing_two_point_match_2, + alt_bn128_pairing_two_point_match_3, + alt_bn128_pairing_two_point_match_4, + alt_bn128_pairing_ten_point_match_1, + alt_bn128_pairing_ten_point_match_2, + alt_bn128_pairing_ten_point_match_3 ); criterion_main!(builtin); fn ecrecover(b: &mut Criterion) { - bench( + bench( "ecrecover", "0000000000000000000000000000000000000001", // ecrecover "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02", @@ -155,7 +147,7 @@ fn ecrecover(b: &mut Criterion) { } fn sha256(b: &mut Criterion) { - bench( + bench( "sha256", "0000000000000000000000000000000000000002", // sha256 "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02", @@ -165,7 +157,7 @@ fn sha256(b: &mut Criterion) { } fn ripemd(b: &mut Criterion) { - bench( + bench( "ripemd", "0000000000000000000000000000000000000003", // ripemd "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02", @@ -175,7 +167,7 @@ fn ripemd(b: &mut Criterion) { } fn identity(b: &mut Criterion) { - bench( + bench( "identity", "0000000000000000000000000000000000000004", // identity "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02", @@ -185,7 +177,7 @@ fn identity(b: &mut Criterion) { } fn modexp_eip_example1(b: &mut Criterion) { - bench( + bench( "modexp_eip_example1", "0000000000000000000000000000000000000005", // modexp "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002003fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", @@ -195,7 +187,7 @@ fn modexp_eip_example1(b: &mut Criterion) { } fn modexp_eip_example2(b: &mut Criterion) { - bench( + bench( "modexp_eip_example2", "0000000000000000000000000000000000000005", // modexp "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", @@ -205,7 +197,7 @@ fn modexp_eip_example2(b: &mut Criterion) { } fn modexp_nagydani_1_square(b: &mut Criterion) { - bench( + bench( "modexp_nagydani_1_square", "0000000000000000000000000000000000000005", // modexp "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb502fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", @@ -215,7 +207,7 @@ fn modexp_nagydani_1_square(b: &mut Criterion) { } fn modexp_nagydani_1_qube(b: &mut Criterion) { - bench( + bench( "modexp_nagydani_1_qube", "0000000000000000000000000000000000000005", // modexp "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb503fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", @@ -225,7 +217,7 @@ fn modexp_nagydani_1_qube(b: &mut Criterion) { } fn modexp_nagydani_1_pow0x10001(b: &mut Criterion) { - bench( + bench( "modexp_nagydani_1_pow0x10001", "0000000000000000000000000000000000000005", // modexp "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb5010001fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", @@ -235,7 +227,7 @@ fn modexp_nagydani_1_pow0x10001(b: &mut Criterion) { } fn modexp_nagydani_2_square(b: &mut Criterion) { - bench( + bench( "modexp_nagydani_2_square", "0000000000000000000000000000000000000005", // modexp "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5102e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", @@ -245,7 +237,7 @@ fn modexp_nagydani_2_square(b: &mut Criterion) { } fn modexp_nagydani_2_qube(b: &mut Criterion) { - bench( + bench( "modexp_nagydani_2_qube", "0000000000000000000000000000000000000005", // modexp "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5103e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", @@ -255,7 +247,7 @@ fn modexp_nagydani_2_qube(b: &mut Criterion) { } fn modexp_nagydani_2_pow0x10001(b: &mut Criterion) { - bench( + bench( "modexp_nagydani_2_pow0x10001", "0000000000000000000000000000000000000005", // modexp "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf51010001e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", @@ -265,7 +257,7 @@ fn modexp_nagydani_2_pow0x10001(b: &mut Criterion) { } fn modexp_nagydani_3_square(b: &mut Criterion) { - bench( + bench( "modexp_nagydani_3_square", "0000000000000000000000000000000000000005", // modexp "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb02d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", @@ -275,7 +267,7 @@ fn modexp_nagydani_3_square(b: &mut Criterion) { } fn modexp_nagydani_3_qube(b: &mut Criterion) { - bench( + bench( "modexp_nagydani_3_qube", "0000000000000000000000000000000000000005", // modexp "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb03d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", @@ -285,7 +277,7 @@ fn modexp_nagydani_3_qube(b: &mut Criterion) { } fn modexp_nagydani_3_pow0x10001(b: &mut Criterion) { - bench( + bench( "modexp_nagydani_3_pow0x10001", "0000000000000000000000000000000000000005", // modexp "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb010001d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", @@ -295,7 +287,7 @@ fn modexp_nagydani_3_pow0x10001(b: &mut Criterion) { } fn modexp_nagydani_4_square(b: &mut Criterion) { - bench( + bench( "modexp_nagydani_4_square", "0000000000000000000000000000000000000005", // modexp "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8102df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", @@ -305,7 +297,7 @@ fn modexp_nagydani_4_square(b: &mut Criterion) { } fn modexp_nagydani_4_qube(b: &mut Criterion) { - bench( + bench( "modexp_nagydani_4_qube", "0000000000000000000000000000000000000005", // modexp "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8103df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", @@ -315,7 +307,7 @@ fn modexp_nagydani_4_qube(b: &mut Criterion) { } fn modexp_nagydani_4_pow0x10001(b: &mut Criterion) { - bench( + bench( "modexp_nagydani_4_pow0x10001", "0000000000000000000000000000000000000005", // modexp "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b81010001df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", @@ -325,7 +317,7 @@ fn modexp_nagydani_4_pow0x10001(b: &mut Criterion) { } fn modexp_nagydani_5_square(b: &mut Criterion) { - bench( + bench( "modexp_nagydani_5_square", "0000000000000000000000000000000000000005", // modexp "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf02e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", @@ -335,7 +327,7 @@ fn modexp_nagydani_5_square(b: &mut Criterion) { } fn modexp_nagydani_5_qube(b: &mut Criterion) { - bench( + bench( "modexp_nagydani_5_qube", "0000000000000000000000000000000000000005", // modexp "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf03e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", @@ -345,7 +337,7 @@ fn modexp_nagydani_5_qube(b: &mut Criterion) { } fn modexp_nagydani_5_pow0x10001(b: &mut Criterion) { - bench( + bench( "modexp_nagydani_5_pow0x10001", "0000000000000000000000000000000000000005", // modexp "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf010001e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", @@ -355,7 +347,7 @@ fn modexp_nagydani_5_pow0x10001(b: &mut Criterion) { } fn alt_bn128_add_chfast1(b: &mut Criterion) { - bench( + bench( "alt_bn128_add_chfast1", "0000000000000000000000000000000000000006", // alt_bn128_add "18b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f3726607c2b7f58a84bd6145f00c9c2bc0bb1a187f20ff2c92963a88019e7c6a014eed06614e20c147e940f2d70da3f74c9a17df361706a4485c742bd6788478fa17d7", @@ -365,7 +357,7 @@ fn alt_bn128_add_chfast1(b: &mut Criterion) { } fn alt_bn128_add_chfast2(b: &mut Criterion) { - bench( + bench( "alt_bn128_add_chfast2", "0000000000000000000000000000000000000006", // alt_bn128_add "2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c91518b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f37266", @@ -375,7 +367,7 @@ fn alt_bn128_add_chfast2(b: &mut Criterion) { } fn alt_bn128_add_cdetrio1(b: &mut Criterion) { - bench( + bench( "alt_bn128_add_cdetrio1", "0000000000000000000000000000000000000006", // alt_bn128_add "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", @@ -385,7 +377,7 @@ fn alt_bn128_add_cdetrio1(b: &mut Criterion) { } fn alt_bn128_add_cdetrio2(b: &mut Criterion) { - bench( + bench( "alt_bn128_add_cdetrio2", "0000000000000000000000000000000000000006", // alt_bn128_add "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", @@ -395,7 +387,7 @@ fn alt_bn128_add_cdetrio2(b: &mut Criterion) { } fn alt_bn128_add_cdetrio3(b: &mut Criterion) { - bench( + bench( "alt_bn128_add_cdetrio3", "0000000000000000000000000000000000000006", // alt_bn128_add "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", @@ -405,7 +397,7 @@ fn alt_bn128_add_cdetrio3(b: &mut Criterion) { } fn alt_bn128_add_cdetrio4(b: &mut Criterion) { - bench( + bench( "alt_bn128_add_cdetrio4", "0000000000000000000000000000000000000006", // alt_bn128_add "", @@ -415,7 +407,7 @@ fn alt_bn128_add_cdetrio4(b: &mut Criterion) { } fn alt_bn128_add_cdetrio5(b: &mut Criterion) { - bench( + bench( "alt_bn128_add_cdetrio5", "0000000000000000000000000000000000000006", // alt_bn128_add "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", @@ -425,7 +417,7 @@ fn alt_bn128_add_cdetrio5(b: &mut Criterion) { } fn alt_bn128_add_cdetrio6(b: &mut Criterion) { - bench( + bench( "alt_bn128_add_cdetrio6", "0000000000000000000000000000000000000006", // alt_bn128_add "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", @@ -435,7 +427,7 @@ fn alt_bn128_add_cdetrio6(b: &mut Criterion) { } fn alt_bn128_add_cdetrio7(b: &mut Criterion) { - bench( + bench( "alt_bn128_add_cdetrio7", "0000000000000000000000000000000000000006", // alt_bn128_add "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", @@ -445,7 +437,7 @@ fn alt_bn128_add_cdetrio7(b: &mut Criterion) { } fn alt_bn128_add_cdetrio8(b: &mut Criterion) { - bench( + bench( "alt_bn128_add_cdetrio8", "0000000000000000000000000000000000000006", // alt_bn128_add "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", @@ -455,7 +447,7 @@ fn alt_bn128_add_cdetrio8(b: &mut Criterion) { } fn alt_bn128_add_cdetrio9(b: &mut Criterion) { - bench( + bench( "alt_bn128_add_cdetrio9", "0000000000000000000000000000000000000006", // alt_bn128_add "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", @@ -465,7 +457,7 @@ fn alt_bn128_add_cdetrio9(b: &mut Criterion) { } fn alt_bn128_add_cdetrio10(b: &mut Criterion) { - bench( + bench( "alt_bn128_add_cdetrio10", "0000000000000000000000000000000000000006", // alt_bn128_add "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", @@ -475,7 +467,7 @@ fn alt_bn128_add_cdetrio10(b: &mut Criterion) { } fn alt_bn128_add_cdetrio11(b: &mut Criterion) { - bench( + bench( "alt_bn128_add_cdetrio11", "0000000000000000000000000000000000000006", // alt_bn128_add "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", @@ -485,7 +477,7 @@ fn alt_bn128_add_cdetrio11(b: &mut Criterion) { } fn alt_bn128_add_cdetrio12(b: &mut Criterion) { - bench( + bench( "alt_bn128_add_cdetrio12", "0000000000000000000000000000000000000006", // alt_bn128_add "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", @@ -495,7 +487,7 @@ fn alt_bn128_add_cdetrio12(b: &mut Criterion) { } fn alt_bn128_add_cdetrio13(b: &mut Criterion) { - bench( + bench( "alt_bn128_add_cdetrio13", "0000000000000000000000000000000000000006", // alt_bn128_add "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d98", @@ -505,7 +497,7 @@ fn alt_bn128_add_cdetrio13(b: &mut Criterion) { } fn alt_bn128_add_cdetrio14(b: &mut Criterion) { - bench( + bench( "alt_bn128_add_cdetrio14", "0000000000000000000000000000000000000006", // alt_bn128_add "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa92e83f8d734803fc370eba25ed1f6b8768bd6d83887b87165fc2434fe11a830cb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", @@ -515,7 +507,7 @@ fn alt_bn128_add_cdetrio14(b: &mut Criterion) { } fn alt_bn128_mul_chfast1(b: &mut Criterion) { - bench( + bench( "alt_bn128_mul_chfast1", "0000000000000000000000000000000000000007", // alt_bn128_mul "2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb721611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb20400000000000000000000000000000000000000000000000011138ce750fa15c2", @@ -525,7 +517,7 @@ fn alt_bn128_mul_chfast1(b: &mut Criterion) { } fn alt_bn128_mul_chfast2(b: &mut Criterion) { - bench( + bench( "alt_bn128_mul_chfast2", "0000000000000000000000000000000000000007", // alt_bn128_mul "070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46", @@ -535,7 +527,7 @@ fn alt_bn128_mul_chfast2(b: &mut Criterion) { } fn alt_bn128_mul_chfast3(b: &mut Criterion) { - bench( + bench( "alt_bn128_mul_chfast3", "0000000000000000000000000000000000000007", // alt_bn128_mul "025a6f4181d2b4ea8b724290ffb40156eb0adb514c688556eb79cdea0752c2bb2eff3f31dea215f1eb86023a133a996eb6300b44da664d64251d05381bb8a02e183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3", @@ -545,7 +537,7 @@ fn alt_bn128_mul_chfast3(b: &mut Criterion) { } fn alt_bn128_mul_cdetrio1(b: &mut Criterion) { - bench( + bench( "alt_bn128_mul_cdetrio1", "0000000000000000000000000000000000000007", // alt_bn128_mul "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -555,7 +547,7 @@ fn alt_bn128_mul_cdetrio1(b: &mut Criterion) { } fn alt_bn128_mul_cdetrio6(b: &mut Criterion) { - bench( + bench( "alt_bn128_mul_cdetrio6", "0000000000000000000000000000000000000007", // alt_bn128_mul "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -565,7 +557,7 @@ fn alt_bn128_mul_cdetrio6(b: &mut Criterion) { } fn alt_bn128_mul_cdetrio11(b: &mut Criterion) { - bench( + bench( "alt_bn128_mul_cdetrio11", "0000000000000000000000000000000000000007", // alt_bn128_mul "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d98ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -575,7 +567,7 @@ fn alt_bn128_mul_cdetrio11(b: &mut Criterion) { } fn alt_bn128_pairing_jeff1(b: &mut Criterion) { - bench( + bench( "alt_bn128_pairing_jeff1", "0000000000000000000000000000000000000008", // alt_bn128_pairing "1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f593034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf704bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a416782bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", @@ -585,7 +577,7 @@ fn alt_bn128_pairing_jeff1(b: &mut Criterion) { } fn alt_bn128_pairing_jeff2(b: &mut Criterion) { - bench( + bench( "alt_bn128_pairing_jeff2", "0000000000000000000000000000000000000008", // alt_bn128_pairing "2eca0c7238bf16e83e7a1e6c5d49540685ff51380f309842a98561558019fc0203d3260361bb8451de5ff5ecd17f010ff22f5c31cdf184e9020b06fa5997db841213d2149b006137fcfb23036606f848d638d576a120ca981b5b1a5f9300b3ee2276cf730cf493cd95d64677bbb75fc42db72513a4c1e387b476d056f80aa75f21ee6226d31426322afcda621464d0611d226783262e21bb3bc86b537e986237096df1f82dff337dd5972e32a8ad43e28a78a96a823ef1cd4debe12b6552ea5f06967a1237ebfeca9aaae0d6d0bab8e28c198c5a339ef8a2407e31cdac516db922160fa257a5fd5b280642ff47b65eca77e626cb685c84fa6d3b6882a283ddd1198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", @@ -595,7 +587,7 @@ fn alt_bn128_pairing_jeff2(b: &mut Criterion) { } fn alt_bn128_pairing_jeff3(b: &mut Criterion) { - bench( + bench( "alt_bn128_pairing_jeff3", "0000000000000000000000000000000000000008", // alt_bn128_pairing "0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba2e89718ad33c8bed92e210e81d1853435399a271913a6520736a4729cf0d51eb01a9e2ffa2e92599b68e44de5bcf354fa2642bd4f26b259daa6f7ce3ed57aeb314a9a87b789a58af499b314e13c3d65bede56c07ea2d418d6874857b70763713178fb49a2d6cd347dc58973ff49613a20757d0fcc22079f9abd10c3baee245901b9e027bd5cfc2cb5db82d4dc9677ac795ec500ecd47deee3b5da006d6d049b811d7511c78158de484232fc68daf8a45cf217d1c2fae693ff5871e8752d73b21198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", @@ -605,7 +597,7 @@ fn alt_bn128_pairing_jeff3(b: &mut Criterion) { } fn alt_bn128_pairing_jeff4(b: &mut Criterion) { - bench( + bench( "alt_bn128_pairing_jeff4", "0000000000000000000000000000000000000008", // alt_bn128_pairing "2f2ea0b3da1e8ef11914acf8b2e1b32d99df51f5f4f206fc6b947eae860eddb6068134ddb33dc888ef446b648d72338684d678d2eb2371c61a50734d78da4b7225f83c8b6ab9de74e7da488ef02645c5a16a6652c3c71a15dc37fe3a5dcb7cb122acdedd6308e3bb230d226d16a105295f523a8a02bfc5e8bd2da135ac4c245d065bbad92e7c4e31bf3757f1fe7362a63fbfee50e7dc68da116e67d600d9bf6806d302580dc0661002994e7cd3a7f224e7ddc27802777486bf80f40e4ca3cfdb186bac5188a98c45e6016873d107f5cd131f3a3e339d0375e58bd6219347b008122ae2b09e539e152ec5364e7e2204b03d11d3caa038bfc7cd499f8176aacbee1f39e4e4afc4bc74790a4a028aff2c3d2538731fb755edefd8cb48d6ea589b5e283f150794b6736f670d6a1033f9b46c6f5204f50813eb85c8dc4b59db1c5d39140d97ee4d2b36d99bc49974d18ecca3e7ad51011956051b464d9e27d46cc25e0764bb98575bd466d32db7b15f582b2d5c452b36aa394b789366e5e3ca5aabd415794ab061441e51d01e94640b7e3084a07e02c78cf3103c542bc5b298669f211b88da1679b0b64a63b7e0e7bfe52aae524f73a55be7fe70c7e9bfc94b4cf0da1213d2149b006137fcfb23036606f848d638d576a120ca981b5b1a5f9300b3ee2276cf730cf493cd95d64677bbb75fc42db72513a4c1e387b476d056f80aa75f21ee6226d31426322afcda621464d0611d226783262e21bb3bc86b537e986237096df1f82dff337dd5972e32a8ad43e28a78a96a823ef1cd4debe12b6552ea5f", @@ -615,7 +607,7 @@ fn alt_bn128_pairing_jeff4(b: &mut Criterion) { } fn alt_bn128_pairing_jeff5(b: &mut Criterion) { - bench( + bench( "alt_bn128_pairing_jeff5", "0000000000000000000000000000000000000008", // alt_bn128_pairing "20a754d2071d4d53903e3b31a7e98ad6882d58aec240ef981fdf0a9d22c5926a29c853fcea789887315916bbeb89ca37edb355b4f980c9a12a94f30deeed30211213d2149b006137fcfb23036606f848d638d576a120ca981b5b1a5f9300b3ee2276cf730cf493cd95d64677bbb75fc42db72513a4c1e387b476d056f80aa75f21ee6226d31426322afcda621464d0611d226783262e21bb3bc86b537e986237096df1f82dff337dd5972e32a8ad43e28a78a96a823ef1cd4debe12b6552ea5f1abb4a25eb9379ae96c84fff9f0540abcfc0a0d11aeda02d4f37e4baf74cb0c11073b3ff2cdbb38755f8691ea59e9606696b3ff278acfc098fa8226470d03869217cee0a9ad79a4493b5253e2e4e3a39fc2df38419f230d341f60cb064a0ac290a3d76f140db8418ba512272381446eb73958670f00cf46f1d9e64cba057b53c26f64a8ec70387a13e41430ed3ee4a7db2059cc5fc13c067194bcc0cb49a98552fd72bd9edb657346127da132e5b82ab908f5816c826acb499e22f2412d1a2d70f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd2198a1f162a73261f112401aa2db79c7dab1533c9935c77290a6ce3b191f2318d198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", @@ -625,7 +617,7 @@ fn alt_bn128_pairing_jeff5(b: &mut Criterion) { } fn alt_bn128_pairing_jeff6(b: &mut Criterion) { - bench( + bench( "alt_bn128_pairing_jeff6", "0000000000000000000000000000000000000008", // alt_bn128_pairing "1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f593034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf704bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a416782bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c103188585e2364128fe25c70558f1560f4f9350baf3959e603cc91486e110936198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", @@ -635,17 +627,17 @@ fn alt_bn128_pairing_jeff6(b: &mut Criterion) { } fn alt_bn128_pairing_empty_data(b: &mut Criterion) { - bench( - "alt_bn128_pairing_empty_data", - "0000000000000000000000000000000000000008", // alt_bn128_pairing - "", - "0000000000000000000000000000000000000000000000000000000000000001", - b, - ); + bench( + "alt_bn128_pairing_empty_data", + "0000000000000000000000000000000000000008", // alt_bn128_pairing + "", + "0000000000000000000000000000000000000000000000000000000000000001", + b, + ); } fn alt_bn128_pairing_one_point(b: &mut Criterion) { - bench( + bench( "alt_bn128_pairing_one_point", "0000000000000000000000000000000000000008", // alt_bn128_pairing "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", @@ -655,7 +647,7 @@ fn alt_bn128_pairing_one_point(b: &mut Criterion) { } fn alt_bn128_pairing_two_point_match_2(b: &mut Criterion) { - bench( + bench( "alt_bn128_pairing_two_point_match_2", "0000000000000000000000000000000000000008", // alt_bn128_pairing "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d", @@ -665,7 +657,7 @@ fn alt_bn128_pairing_two_point_match_2(b: &mut Criterion) { } fn alt_bn128_pairing_two_point_match_3(b: &mut Criterion) { - bench( + bench( "alt_bn128_pairing_two_point_match_3", "0000000000000000000000000000000000000008", // alt_bn128_pairing "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", @@ -675,7 +667,7 @@ fn alt_bn128_pairing_two_point_match_3(b: &mut Criterion) { } fn alt_bn128_pairing_two_point_match_4(b: &mut Criterion) { - bench( + bench( "alt_bn128_pairing_two_point_match_4", "0000000000000000000000000000000000000008", // alt_bn128_pairing "105456a333e6d636854f987ea7bb713dfd0ae8371a72aea313ae0c32c0bf10160cf031d41b41557f3e7e3ba0c51bebe5da8e6ecd855ec50fc87efcdeac168bcc0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa114c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a2101b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000021a2c3013d2ea92e13c800cde68ef56a294b883f6ac35d25f587c09b1b3c635f7290158a80cd3d66530f74dc94c94adb88f5cdb481acca997b6e60071f08a115f2f997f3dbd66a7afe07fe7862ce239edba9e05c5afff7f8a1259c9733b2dfbb929d1691530ca701b4a106054688728c9972c8512e9789e9567aae23e302ccd75", @@ -685,7 +677,7 @@ fn alt_bn128_pairing_two_point_match_4(b: &mut Criterion) { } fn alt_bn128_pairing_ten_point_match_1(b: &mut Criterion) { - bench( + bench( "alt_bn128_pairing_ten_point_match_1", "0000000000000000000000000000000000000008", // alt_bn128_pairing "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d", @@ -695,7 +687,7 @@ fn alt_bn128_pairing_ten_point_match_1(b: &mut Criterion) { } fn alt_bn128_pairing_ten_point_match_2(b: &mut Criterion) { - bench( + bench( "alt_bn128_pairing_ten_point_match_2", "0000000000000000000000000000000000000008", // alt_bn128_pairing "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", @@ -705,7 +697,7 @@ fn alt_bn128_pairing_ten_point_match_2(b: &mut Criterion) { } fn alt_bn128_pairing_ten_point_match_3(b: &mut Criterion) { - bench( + bench( "alt_bn128_pairing_ten_point_match_3", "0000000000000000000000000000000000000008", // alt_bn128_pairing "105456a333e6d636854f987ea7bb713dfd0ae8371a72aea313ae0c32c0bf10160cf031d41b41557f3e7e3ba0c51bebe5da8e6ecd855ec50fc87efcdeac168bcc0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa114c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a2101b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000021a2c3013d2ea92e13c800cde68ef56a294b883f6ac35d25f587c09b1b3c635f7290158a80cd3d66530f74dc94c94adb88f5cdb481acca997b6e60071f08a115f2f997f3dbd66a7afe07fe7862ce239edba9e05c5afff7f8a1259c9733b2dfbb929d1691530ca701b4a106054688728c9972c8512e9789e9567aae23e302ccd75", diff --git a/ethcore/blockchain/Cargo.toml b/ethcore/blockchain/Cargo.toml index a2636975705..d627341de85 100644 --- a/ethcore/blockchain/Cargo.toml +++ b/ethcore/blockchain/Cargo.toml @@ -1,6 +1,6 @@ [package] -description = "Ethcore blockchain database" -homepage = "http://parity.io" +description = "OpenEthereum Blockchain Database, Test Generator, Configuration, Caching, Importing Blocks, and Block Information" +homepage = "https://github.com/openethereum/openethereum" license = "GPL-3.0" name = "ethcore-blockchain" version = "0.1.0" @@ -8,26 +8,28 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -ansi_term = "0.10" +ansi_term = "0.11" blooms-db = { path = "../../util/blooms-db" } common-types = { path = "../types" } ethcore-db = { path = "../db" } ethereum-types = "0.4" heapsize = "0.4" itertools = "0.5" +keccak-hash = "0.1" kvdb = "0.1" log = "0.4" parity-bytes = "0.1" parking_lot = "0.7" -rayon = "1.0" +rand = "0.6" +rayon = "1.1" rlp = { version = "0.3.0", features = ["ethereum"] } rlp_compress = { path = "../../util/rlp-compress" } rlp_derive = { path = "../../util/rlp-derive" } +triehash-ethereum = { version = "0.2", path = "../../util/triehash-ethereum" } [dev-dependencies] env_logger = "0.5" ethkey = { path = "../../accounts/ethkey" } -keccak-hash = "0.1" rustc-hex = "1.0" tempdir = "0.3" kvdb-memorydb = "0.1" diff --git a/ethcore/blockchain/src/best_block.rs b/ethcore/blockchain/src/best_block.rs index 20f247391dc..7ee83954215 100644 --- a/ethcore/blockchain/src/best_block.rs +++ b/ethcore/blockchain/src/best_block.rs @@ -1,23 +1,22 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use ethereum_types::{H256, U256}; -use common_types::{encoded, BlockNumber}; -use common_types::header::Header; +use common_types::{encoded, header::Header, BlockNumber}; /// Contains information on a best block that is specific to the consensus engine. /// @@ -26,19 +25,19 @@ use common_types::header::Header; /// /// Sometimes refered as 'latest block'. pub struct BestBlock { - /// Best block decoded header. - pub header: Header, - /// Best block uncompressed bytes. - pub block: encoded::Block, - /// Best block total difficulty. - pub total_difficulty: U256, + /// Best block decoded header. + pub header: Header, + /// Best block uncompressed bytes. + pub block: encoded::Block, + /// Best block total difficulty. + pub total_difficulty: U256, } /// Best ancient block info. If the blockchain has a gap this keeps track of where it starts. #[derive(Default)] pub struct BestAncientBlock { - /// Best block hash. - pub hash: H256, - /// Best block number. - pub number: BlockNumber, + /// Best block hash. + pub hash: H256, + /// Best block number. + pub number: BlockNumber, } diff --git a/ethcore/blockchain/src/block_info.rs b/ethcore/blockchain/src/block_info.rs index 15f71ecb855..5390d7be6c5 100644 --- a/ethcore/blockchain/src/block_info.rs +++ b/ethcore/blockchain/src/block_info.rs @@ -1,54 +1,54 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use ethereum_types::{H256, U256}; use common_types::BlockNumber; +use ethereum_types::{H256, U256}; /// Brief info about inserted block. #[derive(Clone)] pub struct BlockInfo { - /// Block hash. - pub hash: H256, - /// Block number. - pub number: BlockNumber, - /// Total block difficulty. - pub total_difficulty: U256, - /// Block location in blockchain. - pub location: BlockLocation + /// Block hash. + pub hash: H256, + /// Block number. + pub number: BlockNumber, + /// Total block difficulty. + pub total_difficulty: U256, + /// Block location in blockchain. + pub location: BlockLocation, } /// Describes location of newly inserted block. #[derive(Debug, Clone, PartialEq)] pub enum BlockLocation { - /// It's part of the canon chain. - CanonChain, - /// It's not a part of the canon chain. - Branch, - /// It's part of the fork which should become canon chain, - /// because its total difficulty is higher than current - /// canon chain difficulty. - BranchBecomingCanonChain(BranchBecomingCanonChainData), + /// It's part of the canon chain. + CanonChain, + /// It's not a part of the canon chain. + Branch, + /// It's part of the fork which should become canon chain, + /// because its total difficulty is higher than current + /// canon chain difficulty. + BranchBecomingCanonChain(BranchBecomingCanonChainData), } #[derive(Debug, Clone, PartialEq)] pub struct BranchBecomingCanonChainData { - /// Hash of the newest common ancestor with old canon chain. - pub ancestor: H256, - /// Hashes of the blocks between ancestor and this block. - pub enacted: Vec, - /// Hashes of the blocks which were invalidated. - pub retracted: Vec, + /// Hash of the newest common ancestor with old canon chain. + pub ancestor: H256, + /// Hashes of the blocks between ancestor and this block. + pub enacted: Vec, + /// Hashes of the blocks which were invalidated. + pub retracted: Vec, } diff --git a/ethcore/blockchain/src/blockchain.rs b/ethcore/blockchain/src/blockchain.rs index de6e8c134a6..389b95c492d 100644 --- a/ethcore/blockchain/src/blockchain.rs +++ b/ethcore/blockchain/src/blockchain.rs @@ -1,377 +1,454 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Blockchain database. -use std::collections::{HashMap, HashSet}; -use std::{mem, io}; -use std::path::Path; -use std::sync::Arc; +use std::{ + collections::{HashMap, HashSet}, + io, mem, + path::Path, + sync::Arc, +}; use ansi_term::Colour; use blooms_db; -use common_types::BlockNumber; -use common_types::blockchain_info::BlockChainInfo; -use common_types::encoded; -use common_types::engines::ForkChoice; -use common_types::engines::epoch::{Transition as EpochTransition, PendingTransition as PendingEpochTransition}; -use common_types::header::{Header, ExtendedHeader}; -use common_types::log_entry::{LogEntry, LocalizedLogEntry}; -use common_types::receipt::Receipt; -use common_types::transaction::LocalizedTransaction; -use common_types::tree_route::TreeRoute; -use common_types::view; -use common_types::views::{BlockView, HeaderView}; -use ethcore_db::cache_manager::CacheManager; -use ethcore_db::keys::{BlockReceipts, BlockDetails, TransactionAddress, EPOCH_KEY_PREFIX, EpochTransitions}; -use ethcore_db::{self as db, Writable, Readable, CacheUpdatePolicy}; -use ethereum_types::{H256, Bloom, BloomRef, U256}; +use common_types::{ + blockchain_info::BlockChainInfo, + encoded, + engines::{ + epoch::{PendingTransition as PendingEpochTransition, Transition as EpochTransition}, + ForkChoice, + }, + header::{ExtendedHeader, Header}, + log_entry::{LocalizedLogEntry, LogEntry}, + receipt::Receipt, + transaction::LocalizedTransaction, + tree_route::TreeRoute, + view, + views::{BlockView, HeaderView}, + BlockNumber, +}; +use ethcore_db::{ + self as db, + cache_manager::CacheManager, + keys::{BlockDetails, BlockReceipts, EpochTransitions, TransactionAddress, EPOCH_KEY_PREFIX}, + CacheUpdatePolicy, Readable, Writable, +}; +use ethereum_types::{Bloom, BloomRef, H256, U256}; use heapsize::HeapSizeOf; use itertools::Itertools; use kvdb::{DBTransaction, KeyValueDB}; -use log::{trace, warn, info}; +use log::{info, trace, warn}; use parity_bytes::Bytes; use parking_lot::{Mutex, RwLock}; use rayon::prelude::*; use rlp::RlpStream; -use rlp_compress::{compress, decompress, blocks_swapper}; +use rlp_compress::{blocks_swapper, compress, decompress}; -use crate::best_block::{BestBlock, BestAncientBlock}; -use crate::block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainData}; -use crate::update::{ExtrasUpdate, ExtrasInsert}; -use crate::{CacheSize, ImportRoute, Config}; +use crate::{ + best_block::{BestAncientBlock, BestBlock}, + block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainData}, + update::{ExtrasInsert, ExtrasUpdate}, + CacheSize, Config, ImportRoute, +}; /// Database backing `BlockChain`. pub trait BlockChainDB: Send + Sync { - /// Generic key value store. - fn key_value(&self) -> &Arc; + /// Generic key value store. + fn key_value(&self) -> &Arc; - /// Header blooms database. - fn blooms(&self) -> &blooms_db::Database; + /// Header blooms database. + fn blooms(&self) -> &blooms_db::Database; - /// Trace blooms database. - fn trace_blooms(&self) -> &blooms_db::Database; + /// Trace blooms database. + fn trace_blooms(&self) -> &blooms_db::Database; - /// Restore the DB from the given path - fn restore(&self, new_db: &str) -> Result<(), io::Error> { - // First, close the Blooms databases - self.blooms().close()?; - self.trace_blooms().close()?; + /// Restore the DB from the given path + fn restore(&self, new_db: &str) -> Result<(), io::Error> { + // First, close the Blooms databases + self.blooms().close()?; + self.trace_blooms().close()?; - // Restore the key_value DB - self.key_value().restore(new_db)?; + // Restore the key_value DB + self.key_value().restore(new_db)?; - // Re-open the Blooms databases - self.blooms().reopen()?; - self.trace_blooms().reopen()?; - Ok(()) - } + // Re-open the Blooms databases + self.blooms().reopen()?; + self.trace_blooms().reopen()?; + Ok(()) + } } /// Generic database handler. This trait contains one function `open`. When called, it opens database with a /// predefined config. pub trait BlockChainDBHandler: Send + Sync { - /// Open the predefined key-value database. - fn open(&self, path: &Path) -> io::Result>; + /// Open the predefined key-value database. + fn open(&self, path: &Path) -> io::Result>; } /// Interface for querying blocks by hash and by number. pub trait BlockProvider { - /// Returns true if the given block is known - /// (though not necessarily a part of the canon chain). - fn is_known(&self, hash: &H256) -> bool; - - /// Returns true if the given block is known and in the canon chain. - fn is_canon(&self, hash: &H256) -> bool { - let is_canon = || Some(hash == &self.block_hash(self.block_number(hash)?)?); - is_canon().unwrap_or(false) - } - - /// Get the first block of the best part of the chain. - /// Return `None` if there is no gap and the first block is the genesis. - /// Any queries of blocks which precede this one are not guaranteed to - /// succeed. - fn first_block(&self) -> Option; - - /// Get the number of the first block. - fn first_block_number(&self) -> Option { - self.first_block().map(|b| self.block_number(&b).expect("First block is always set to an existing block or `None`. Existing block always has a number; qed")) - } - - /// Get the best block of an first block sequence if there is a gap. - fn best_ancient_block(&self) -> Option; - - /// Get the number of the first block. - fn best_ancient_number(&self) -> Option { - self.best_ancient_block().map(|h| self.block_number(&h).expect("Ancient block is always set to an existing block or `None`. Existing block always has a number; qed")) - } - /// Get raw block data - fn block(&self, hash: &H256) -> Option; - - /// Get the familial details concerning a block. - fn block_details(&self, hash: &H256) -> Option; - - /// Get the hash of given block's number. - fn block_hash(&self, index: BlockNumber) -> Option; - - /// Get the address of transaction with given hash. - fn transaction_address(&self, hash: &H256) -> Option; - - /// Get receipts of block with given hash. - fn block_receipts(&self, hash: &H256) -> Option; - - /// Get the header RLP of a block. - fn block_header_data(&self, hash: &H256) -> Option; - - /// Get the block body (uncles and transactions). - fn block_body(&self, hash: &H256) -> Option; - - /// Get a list of uncles for a given block. - /// Returns None if block does not exist. - fn uncles(&self, hash: &H256) -> Option> { - self.block_body(hash).map(|body| body.uncles()) - } - - /// Get a list of uncle hashes for a given block. - /// Returns None if block does not exist. - fn uncle_hashes(&self, hash: &H256) -> Option> { - self.block_body(hash).map(|body| body.uncle_hashes()) - } - - /// Get the number of given block's hash. - fn block_number(&self, hash: &H256) -> Option { - self.block_header_data(hash).map(|header| header.number()) - } - - /// Get transaction with given transaction hash. - fn transaction(&self, address: &TransactionAddress) -> Option { - self.block_body(&address.block_hash) - .and_then(|body| self.block_number(&address.block_hash) - .and_then(|n| body.view().localized_transaction_at(&address.block_hash, n, address.index))) - } - - /// Get a list of transactions for a given block. - /// Returns None if block does not exist. - fn transactions(&self, hash: &H256) -> Option> { - self.block_body(hash) - .and_then(|body| self.block_number(hash) - .map(|n| body.view().localized_transactions(hash, n))) - } - - /// Returns reference to genesis hash. - fn genesis_hash(&self) -> H256 { - self.block_hash(0).expect("Genesis hash should always exist") - } - - /// Returns the header of the genesis block. - fn genesis_header(&self) -> encoded::Header { - self.block_header_data(&self.genesis_hash()) - .expect("Genesis header always stored; qed") - } - - /// Returns numbers of blocks containing given bloom. - fn blocks_with_bloom<'a, B, I, II>(&self, blooms: II, from_block: BlockNumber, to_block: BlockNumber) -> Vec - where - BloomRef<'a>: From, - II: IntoIterator + Copy, - I: Iterator, - Self: Sized; - - /// Returns logs matching given filter. - fn logs(&self, blocks: Vec, matches: F, limit: Option) -> Vec - where F: Fn(&LogEntry) -> bool + Send + Sync, Self: Sized; + /// Returns true if the given block is known + /// (though not necessarily a part of the canon chain). + fn is_known(&self, hash: &H256) -> bool; + + /// Returns true if the given block is known and in the canon chain. + fn is_canon(&self, hash: &H256) -> bool { + let is_canon = || Some(hash == &self.block_hash(self.block_number(hash)?)?); + is_canon().unwrap_or(false) + } + + /// Get the first block of the best part of the chain. + /// Return `None` if there is no gap and the first block is the genesis. + /// Any queries of blocks which precede this one are not guaranteed to + /// succeed. + fn first_block(&self) -> Option; + + /// Get the number of the first block. + fn first_block_number(&self) -> Option { + self.first_block().map(|b| self.block_number(&b).expect("First block is always set to an existing block or `None`. Existing block always has a number; qed")) + } + + /// Get the best block of an first block sequence if there is a gap. + fn best_ancient_block(&self) -> Option; + + /// Get the number of the first block. + fn best_ancient_number(&self) -> Option { + self.best_ancient_block().map(|h| self.block_number(&h).expect("Ancient block is always set to an existing block or `None`. Existing block always has a number; qed")) + } + /// Get raw block data + fn block(&self, hash: &H256) -> Option; + + /// Get the familial details concerning a block. + fn block_details(&self, hash: &H256) -> Option; + + /// Get the hash of given block's number. + fn block_hash(&self, index: BlockNumber) -> Option; + + /// Get the address of transaction with given hash. + fn transaction_address(&self, hash: &H256) -> Option; + + /// Get receipts of block with given hash. + fn block_receipts(&self, hash: &H256) -> Option; + + /// Get the header RLP of a block. + fn block_header_data(&self, hash: &H256) -> Option; + + /// Get the block body (uncles and transactions). + fn block_body(&self, hash: &H256) -> Option; + + /// Get a list of uncles for a given block. + /// Returns None if block does not exist. + fn uncles(&self, hash: &H256) -> Option> { + self.block_body(hash).map(|body| body.uncles()) + } + + /// Get a list of uncle hashes for a given block. + /// Returns None if block does not exist. + fn uncle_hashes(&self, hash: &H256) -> Option> { + self.block_body(hash).map(|body| body.uncle_hashes()) + } + + /// Get the number of given block's hash. + fn block_number(&self, hash: &H256) -> Option { + self.block_header_data(hash).map(|header| header.number()) + } + + /// Get transaction with given transaction hash. + fn transaction(&self, address: &TransactionAddress) -> Option { + self.block_body(&address.block_hash).and_then(|body| { + self.block_number(&address.block_hash).and_then(|n| { + body.view() + .localized_transaction_at(&address.block_hash, n, address.index) + }) + }) + } + + /// Get a list of transactions for a given block. + /// Returns None if block does not exist. + fn transactions(&self, hash: &H256) -> Option> { + self.block_body(hash).and_then(|body| { + self.block_number(hash) + .map(|n| body.view().localized_transactions(hash, n)) + }) + } + + /// Returns reference to genesis hash. + fn genesis_hash(&self) -> H256 { + self.block_hash(0) + .expect("Genesis hash should always exist") + } + + /// Returns the header of the genesis block. + fn genesis_header(&self) -> encoded::Header { + self.block_header_data(&self.genesis_hash()) + .expect("Genesis header always stored; qed") + } + + /// Returns numbers of blocks containing given bloom. + fn blocks_with_bloom<'a, B, I, II>( + &self, + blooms: II, + from_block: BlockNumber, + to_block: BlockNumber, + ) -> Vec + where + BloomRef<'a>: From, + II: IntoIterator + Copy, + I: Iterator, + Self: Sized; + + /// Returns logs matching given filter. + fn logs( + &self, + blocks: Vec, + matches: F, + limit: Option, + ) -> Vec + where + F: Fn(&LogEntry) -> bool + Send + Sync, + Self: Sized; +} + +/// Interface for querying blocks with pending db transaction by hash and by number. +trait InTransactionBlockProvider { + /// Get the familial details concerning a block. + fn uncommitted_block_details(&self, hash: &H256) -> Option; } #[derive(Debug, Hash, Eq, PartialEq, Clone)] enum CacheId { - BlockHeader(H256), - BlockBody(H256), - BlockDetails(H256), - BlockHashes(BlockNumber), - TransactionAddresses(H256), - BlockReceipts(H256), + BlockHeader(H256), + BlockBody(H256), + BlockDetails(H256), + BlockHashes(BlockNumber), + TransactionAddresses(H256), + BlockReceipts(H256), } /// Structure providing fast access to blockchain data. /// /// **Does not do input data verification.** pub struct BlockChain { - // All locks must be captured in the order declared here. - best_block: RwLock, - // Stores best block of the first uninterrupted sequence of blocks. `None` if there are no gaps. - // Only updated with `insert_unordered_block`. - best_ancient_block: RwLock>, - // Stores the last block of the last sequence of blocks. `None` if there are no gaps. - // This is calculated on start and does not get updated. - first_block: Option, - - // block cache - block_headers: RwLock>, - block_bodies: RwLock>, - - // extra caches - block_details: RwLock>, - block_hashes: RwLock>, - transaction_addresses: RwLock>, - block_receipts: RwLock>, - - db: Arc, - - cache_man: Mutex>, - - pending_best_ancient_block: RwLock>>, - pending_best_block: RwLock>, - pending_block_hashes: RwLock>, - pending_block_details: RwLock>, - pending_transaction_addresses: RwLock>>, + // All locks must be captured in the order declared here. + best_block: RwLock, + // Stores best block of the first uninterrupted sequence of blocks. `None` if there are no gaps. + // Only updated with `insert_unordered_block`. + best_ancient_block: RwLock>, + // Stores the last block of the last sequence of blocks. `None` if there are no gaps. + // This is calculated on start and does not get updated. + first_block: Option, + + // block cache + block_headers: RwLock>, + block_bodies: RwLock>, + + // extra caches + block_details: RwLock>, + block_hashes: RwLock>, + transaction_addresses: RwLock>, + block_receipts: RwLock>, + + db: Arc, + + cache_man: Mutex>, + + pending_best_ancient_block: RwLock>>, + pending_best_block: RwLock>, + pending_block_hashes: RwLock>, + pending_block_details: RwLock>, + pending_transaction_addresses: RwLock>>, } impl BlockProvider for BlockChain { - /// Returns true if the given block is known - /// (though not necessarily a part of the canon chain). - fn is_known(&self, hash: &H256) -> bool { - self.db.key_value().exists_with_cache(db::COL_EXTRA, &self.block_details, hash) - } - - fn first_block(&self) -> Option { - self.first_block.clone() - } - - fn best_ancient_block(&self) -> Option { - self.best_ancient_block.read().as_ref().map(|b| b.hash) - } - - fn best_ancient_number(&self) -> Option { - self.best_ancient_block.read().as_ref().map(|b| b.number) - } - - /// Get raw block data - fn block(&self, hash: &H256) -> Option { - let header = self.block_header_data(hash)?; - let body = self.block_body(hash)?; - Some(encoded::Block::new_from_header_and_body(&header.view(), &body.view())) - } - - /// Get block header data - fn block_header_data(&self, hash: &H256) -> Option { - // Check cache first - { - let read = self.block_headers.read(); - if let Some(v) = read.get(hash) { - return Some(v.clone()); - } - } - - // Check if it's the best block - { - let best_block = self.best_block.read(); - if &best_block.header.hash() == hash { - return Some(best_block.header.encoded()) - } - } - - // Read from DB and populate cache - let b = self.db.key_value().get(db::COL_HEADERS, hash) - .expect("Low level database error when fetching block header data. Some issue with disk?")?; - - let header = encoded::Header::new(decompress(&b, blocks_swapper()).into_vec()); - let mut write = self.block_headers.write(); - write.insert(*hash, header.clone()); - - self.cache_man.lock().note_used(CacheId::BlockHeader(*hash)); - Some(header) - } - - /// Get block body data - fn block_body(&self, hash: &H256) -> Option { - // Check cache first - { - let read = self.block_bodies.read(); - if let Some(v) = read.get(hash) { - return Some(v.clone()); - } - } - - // Check if it's the best block - { - let best_block = self.best_block.read(); - if &best_block.header.hash() == hash { - return Some(encoded::Body::new(Self::block_to_body(best_block.block.rlp().as_raw()))); - } - } - - // Read from DB and populate cache - let b = self.db.key_value().get(db::COL_BODIES, hash) - .expect("Low level database error when fetching block body data. Some issue with disk?")?; - - let body = encoded::Body::new(decompress(&b, blocks_swapper()).into_vec()); - let mut write = self.block_bodies.write(); - write.insert(*hash, body.clone()); - - self.cache_man.lock().note_used(CacheId::BlockBody(*hash)); - Some(body) - } - - /// Get the familial details concerning a block. - fn block_details(&self, hash: &H256) -> Option { - let result = self.db.key_value().read_with_cache(db::COL_EXTRA, &self.block_details, hash)?; - self.cache_man.lock().note_used(CacheId::BlockDetails(*hash)); - Some(result) - } - - /// Get the hash of given block's number. - fn block_hash(&self, index: BlockNumber) -> Option { - let result = self.db.key_value().read_with_cache(db::COL_EXTRA, &self.block_hashes, &index)?; - self.cache_man.lock().note_used(CacheId::BlockHashes(index)); - Some(result) - } - - /// Get the address of transaction with given hash. - fn transaction_address(&self, hash: &H256) -> Option { - let result = self.db.key_value().read_with_cache(db::COL_EXTRA, &self.transaction_addresses, hash)?; - self.cache_man.lock().note_used(CacheId::TransactionAddresses(*hash)); - Some(result) - } - - /// Get receipts of block with given hash. - fn block_receipts(&self, hash: &H256) -> Option { - let result = self.db.key_value().read_with_cache(db::COL_EXTRA, &self.block_receipts, hash)?; - self.cache_man.lock().note_used(CacheId::BlockReceipts(*hash)); - Some(result) - } - - /// Returns numbers of blocks containing given bloom. - fn blocks_with_bloom<'a, B, I, II>(&self, blooms: II, from_block: BlockNumber, to_block: BlockNumber) -> Vec - where - BloomRef<'a>: From, - II: IntoIterator + Copy, - I: Iterator { - self.db.blooms() - .filter(from_block, to_block, blooms) - .expect("Low level database error when searching blooms. Some issue with disk?") - } - - /// Returns logs matching given filter. The order of logs returned will be the same as the order of the blocks - /// provided. And it's the callers responsibility to sort blocks provided in advance. - fn logs(&self, mut blocks: Vec, matches: F, limit: Option) -> Vec - where F: Fn(&LogEntry) -> bool + Send + Sync, Self: Sized { - // sort in reverse order - blocks.reverse(); - - let mut logs = blocks + /// Returns true if the given block is known + /// (though not necessarily a part of the canon chain). + fn is_known(&self, hash: &H256) -> bool { + self.db + .key_value() + .exists_with_cache(db::COL_EXTRA, &self.block_details, hash) + } + + fn first_block(&self) -> Option { + self.first_block.clone() + } + + fn best_ancient_block(&self) -> Option { + self.best_ancient_block.read().as_ref().map(|b| b.hash) + } + + fn best_ancient_number(&self) -> Option { + self.best_ancient_block.read().as_ref().map(|b| b.number) + } + + /// Get raw block data + fn block(&self, hash: &H256) -> Option { + let header = self.block_header_data(hash)?; + let body = self.block_body(hash)?; + Some(encoded::Block::new_from_header_and_body( + &header.view(), + &body.view(), + )) + } + + /// Get block header data + fn block_header_data(&self, hash: &H256) -> Option { + // Check cache first + { + let read = self.block_headers.read(); + if let Some(v) = read.get(hash) { + return Some(v.clone()); + } + } + + // Check if it's the best block + { + let best_block = self.best_block.read(); + if &best_block.header.hash() == hash { + return Some(best_block.header.encoded()); + } + } + + // Read from DB and populate cache + let b = self.db.key_value().get(db::COL_HEADERS, hash).expect( + "Low level database error when fetching block header data. Some issue with disk?", + )?; + + let header = encoded::Header::new(decompress(&b, blocks_swapper()).into_vec()); + let mut write = self.block_headers.write(); + write.insert(*hash, header.clone()); + + self.cache_man.lock().note_used(CacheId::BlockHeader(*hash)); + Some(header) + } + + /// Get block body data + fn block_body(&self, hash: &H256) -> Option { + // Check cache first + { + let read = self.block_bodies.read(); + if let Some(v) = read.get(hash) { + return Some(v.clone()); + } + } + + // Check if it's the best block + { + let best_block = self.best_block.read(); + if &best_block.header.hash() == hash { + return Some(encoded::Body::new(Self::block_to_body( + best_block.block.rlp().as_raw(), + ))); + } + } + + // Read from DB and populate cache + let b = self.db.key_value().get(db::COL_BODIES, hash).expect( + "Low level database error when fetching block body data. Some issue with disk?", + )?; + + let body = encoded::Body::new(decompress(&b, blocks_swapper()).into_vec()); + let mut write = self.block_bodies.write(); + write.insert(*hash, body.clone()); + + self.cache_man.lock().note_used(CacheId::BlockBody(*hash)); + Some(body) + } + + /// Get the familial details concerning a block. + fn block_details(&self, hash: &H256) -> Option { + let result = + self.db + .key_value() + .read_with_cache(db::COL_EXTRA, &self.block_details, hash)?; + self.cache_man + .lock() + .note_used(CacheId::BlockDetails(*hash)); + Some(result) + } + + /// Get the hash of given block's number. + fn block_hash(&self, index: BlockNumber) -> Option { + let result = + self.db + .key_value() + .read_with_cache(db::COL_EXTRA, &self.block_hashes, &index)?; + self.cache_man.lock().note_used(CacheId::BlockHashes(index)); + Some(result) + } + + /// Get the address of transaction with given hash. + fn transaction_address(&self, hash: &H256) -> Option { + let result = self.db.key_value().read_with_cache( + db::COL_EXTRA, + &self.transaction_addresses, + hash, + )?; + self.cache_man + .lock() + .note_used(CacheId::TransactionAddresses(*hash)); + Some(result) + } + + /// Get receipts of block with given hash. + fn block_receipts(&self, hash: &H256) -> Option { + let result = + self.db + .key_value() + .read_with_cache(db::COL_EXTRA, &self.block_receipts, hash)?; + self.cache_man + .lock() + .note_used(CacheId::BlockReceipts(*hash)); + Some(result) + } + + /// Returns numbers of blocks containing given bloom. + fn blocks_with_bloom<'a, B, I, II>( + &self, + blooms: II, + from_block: BlockNumber, + to_block: BlockNumber, + ) -> Vec + where + BloomRef<'a>: From, + II: IntoIterator + Copy, + I: Iterator, + { + self.db + .blooms() + .filter(from_block, to_block, blooms) + .expect("Low level database error when searching blooms. Some issue with disk?") + } + + /// Returns logs matching given filter. The order of logs returned will be the same as the order of the blocks + /// provided. And it's the callers responsibility to sort blocks provided in advance. + fn logs( + &self, + mut blocks: Vec, + matches: F, + limit: Option, + ) -> Vec + where + F: Fn(&LogEntry) -> bool + Send + Sync, + Self: Sized, + { + // sort in reverse order + blocks.reverse(); + + let mut logs = blocks .chunks(128) .flat_map(move |blocks_chunk| { blocks_chunk.into_par_iter() @@ -419,2083 +496,2623 @@ impl BlockProvider for BlockChain { }) .take(limit.unwrap_or(::std::usize::MAX)) .collect::>(); - logs.reverse(); - logs - } + logs.reverse(); + logs + } +} + +impl InTransactionBlockProvider for BlockChain { + fn uncommitted_block_details(&self, hash: &H256) -> Option { + let result = self.db.key_value().read_with_two_layer_cache( + db::COL_EXTRA, + &self.pending_block_details, + &self.block_details, + hash, + )?; + self.cache_man + .lock() + .note_used(CacheId::BlockDetails(*hash)); + Some(result) + } } /// An iterator which walks the blockchain towards the genesis. #[derive(Clone)] pub struct AncestryIter<'a> { - current: H256, - chain: &'a BlockChain, + current: H256, + chain: &'a BlockChain, } impl<'a> Iterator for AncestryIter<'a> { - type Item = H256; - fn next(&mut self) -> Option { - if self.current.is_zero() { - None - } else { - self.chain.block_details(&self.current) - .map(|details| mem::replace(&mut self.current, details.parent)) - } - } + type Item = H256; + fn next(&mut self) -> Option { + if self.current.is_zero() { + None + } else { + self.chain + .block_details(&self.current) + .map(|details| mem::replace(&mut self.current, details.parent)) + } + } } /// An iterator which walks the blockchain towards the genesis, with metadata information. pub struct AncestryWithMetadataIter<'a> { - current: H256, - chain: &'a BlockChain, + current: H256, + chain: &'a BlockChain, } impl<'a> Iterator for AncestryWithMetadataIter<'a> { - type Item = ExtendedHeader; - fn next(&mut self) -> Option { - if self.current.is_zero() { - None - } else { - let details = self.chain.block_details(&self.current); - let header = self.chain.block_header_data(&self.current) - .map(|h| h.decode().expect("Stored block header data is valid RLP; qed")); - - match (details, header) { - (Some(details), Some(header)) => { - self.current = details.parent; - Some(ExtendedHeader { - parent_total_difficulty: details.total_difficulty - *header.difficulty(), - is_finalized: details.is_finalized, - header, - }) - }, - _ => { - self.current = H256::default(); - None - }, - } - } - } + type Item = ExtendedHeader; + fn next(&mut self) -> Option { + if self.current.is_zero() { + None + } else { + let details = self.chain.block_details(&self.current); + let header = self.chain.block_header_data(&self.current).map(|h| { + h.decode() + .expect("Stored block header data is valid RLP; qed") + }); + + match (details, header) { + (Some(details), Some(header)) => { + self.current = details.parent; + Some(ExtendedHeader { + parent_total_difficulty: details.total_difficulty - *header.difficulty(), + is_finalized: details.is_finalized, + header, + }) + } + _ => { + self.current = H256::default(); + None + } + } + } + } } /// An iterator which walks all epoch transitions. /// Returns epoch transitions. pub struct EpochTransitionIter<'a> { - chain: &'a BlockChain, - prefix_iter: Box, Box<[u8]>)> + 'a>, + chain: &'a BlockChain, + prefix_iter: Box, Box<[u8]>)> + 'a>, } impl<'a> Iterator for EpochTransitionIter<'a> { - type Item = (u64, EpochTransition); - - fn next(&mut self) -> Option { - loop { - // some epochs never occurred on the main chain. - let (key, val) = self.prefix_iter.next()?; - - // iterator may continue beyond values beginning with this - // prefix. - if !key.starts_with(&EPOCH_KEY_PREFIX[..]) { - return None - } - - let transitions: EpochTransitions = ::rlp::decode(&val[..]).expect("decode error: the db is corrupted or the data structure has changed"); - - // if there are multiple candidates, at most one will be on the - // canon chain. - for transition in transitions.candidates.into_iter() { - let is_in_canon_chain = self.chain.block_hash(transition.block_number) - .map_or(false, |hash| hash == transition.block_hash); - - // if the transition is within the block gap, there will only be - // one candidate, and it will be from a snapshot restored from. - let is_ancient = self.chain.first_block_number() - .map_or(false, |first| first > transition.block_number); - - if is_ancient || is_in_canon_chain { - return Some((transitions.number, transition)) - } - } - } - } + type Item = (u64, EpochTransition); + + fn next(&mut self) -> Option { + loop { + // some epochs never occurred on the main chain. + let (key, val) = self.prefix_iter.next()?; + + // iterator may continue beyond values beginning with this + // prefix. + if !key.starts_with(&EPOCH_KEY_PREFIX[..]) { + return None; + } + + let transitions: EpochTransitions = ::rlp::decode(&val[..]) + .expect("decode error: the db is corrupted or the data structure has changed"); + + // if there are multiple candidates, at most one will be on the + // canon chain. + for transition in transitions.candidates.into_iter() { + let is_in_canon_chain = self + .chain + .block_hash(transition.block_number) + .map_or(false, |hash| hash == transition.block_hash); + + // if the transition is within the block gap, there will only be + // one candidate, and it will be from a snapshot restored from. + let is_ancient = self + .chain + .first_block_number() + .map_or(false, |first| first > transition.block_number); + + if is_ancient || is_in_canon_chain { + return Some((transitions.number, transition)); + } + } + } + } } impl BlockChain { - /// Create new instance of blockchain from given Genesis. - pub fn new(config: Config, genesis: &[u8], db: Arc) -> BlockChain { - // 400 is the average size of the key - let cache_man = CacheManager::new(config.pref_cache_size, config.max_cache_size, 400); - - let mut bc = BlockChain { - first_block: None, - best_block: RwLock::new(BestBlock { - // BestBlock will be overwritten anyway. - header: Default::default(), - total_difficulty: Default::default(), - block: encoded::Block::new(genesis.into()), - }), - best_ancient_block: RwLock::new(None), - block_headers: RwLock::new(HashMap::new()), - block_bodies: RwLock::new(HashMap::new()), - block_details: RwLock::new(HashMap::new()), - block_hashes: RwLock::new(HashMap::new()), - transaction_addresses: RwLock::new(HashMap::new()), - block_receipts: RwLock::new(HashMap::new()), - db: db.clone(), - cache_man: Mutex::new(cache_man), - pending_best_ancient_block: RwLock::new(None), - pending_best_block: RwLock::new(None), - pending_block_hashes: RwLock::new(HashMap::new()), - pending_block_details: RwLock::new(HashMap::new()), - pending_transaction_addresses: RwLock::new(HashMap::new()), - }; - - // load best block - let best_block_hash = match bc.db.key_value().get(db::COL_EXTRA, b"best") - .expect("Low-level database error when fetching 'best' block. Some issue with disk?") - { - Some(best) => { - H256::from_slice(&best) - } - None => { - // best block does not exist - // we need to insert genesis into the cache - let block = view!(BlockView, genesis); - let header = block.header_view(); - let hash = block.hash(); - - let details = BlockDetails { - number: header.number(), - total_difficulty: header.difficulty(), - parent: header.parent_hash(), - children: vec![], - is_finalized: false, - }; - - let mut batch = DBTransaction::new(); - batch.put(db::COL_HEADERS, &hash, block.header_rlp().as_raw()); - batch.put(db::COL_BODIES, &hash, &Self::block_to_body(genesis)); - - batch.write(db::COL_EXTRA, &hash, &details); - batch.write(db::COL_EXTRA, &header.number(), &hash); - - batch.put(db::COL_EXTRA, b"best", &hash); - bc.db.key_value().write(batch).expect("Low level database error when fetching 'best' block. Some issue with disk?"); - hash - } - }; - - { - // Fetch best block details - let best_block_total_difficulty = bc.block_details(&best_block_hash) + /// Create new instance of blockchain from given Genesis. + pub fn new(config: Config, genesis: &[u8], db: Arc) -> BlockChain { + // 400 is the average size of the key + let cache_man = CacheManager::new(config.pref_cache_size, config.max_cache_size, 400); + + let mut bc = BlockChain { + first_block: None, + best_block: RwLock::new(BestBlock { + // BestBlock will be overwritten anyway. + header: Default::default(), + total_difficulty: Default::default(), + block: encoded::Block::new(genesis.into()), + }), + best_ancient_block: RwLock::new(None), + block_headers: RwLock::new(HashMap::new()), + block_bodies: RwLock::new(HashMap::new()), + block_details: RwLock::new(HashMap::new()), + block_hashes: RwLock::new(HashMap::new()), + transaction_addresses: RwLock::new(HashMap::new()), + block_receipts: RwLock::new(HashMap::new()), + db: db.clone(), + cache_man: Mutex::new(cache_man), + pending_best_ancient_block: RwLock::new(None), + pending_best_block: RwLock::new(None), + pending_block_hashes: RwLock::new(HashMap::new()), + pending_block_details: RwLock::new(HashMap::new()), + pending_transaction_addresses: RwLock::new(HashMap::new()), + }; + + // load best block + let best_block_hash = + match bc.db.key_value().get(db::COL_EXTRA, b"best").expect( + "Low-level database error when fetching 'best' block. Some issue with disk?", + ) { + Some(best) => H256::from_slice(&best), + None => { + // best block does not exist + // we need to insert genesis into the cache + let block = view!(BlockView, genesis); + let header = block.header_view(); + let hash = block.hash(); + + let details = BlockDetails { + number: header.number(), + total_difficulty: header.difficulty(), + parent: header.parent_hash(), + children: vec![], + is_finalized: false, + }; + + let mut batch = DBTransaction::new(); + batch.put(db::COL_HEADERS, &hash, block.header_rlp().as_raw()); + batch.put(db::COL_BODIES, &hash, &Self::block_to_body(genesis)); + + batch.write(db::COL_EXTRA, &hash, &details); + batch.write(db::COL_EXTRA, &header.number(), &hash); + + batch.put(db::COL_EXTRA, b"best", &hash); + bc.db.key_value().write(batch).expect( + "Low level database error when fetching 'best' block. Some issue with disk?", + ); + hash + } + }; + + { + // Fetch best block details + let best_block_total_difficulty = bc.block_details(&best_block_hash) .expect("Best block is from a known block hash; a known block hash always comes with a known block detail; qed") .total_difficulty; - let best_block_rlp = bc.block(&best_block_hash) - .expect("Best block is from a known block hash; qed"); - - // and write them - let mut best_block = bc.best_block.write(); - *best_block = BestBlock { - total_difficulty: best_block_total_difficulty, - header: best_block_rlp.decode_header(), - block: best_block_rlp, - }; - } - - { - let best_block_number = bc.best_block.read().header.number(); - // Fetch first and best ancient block details - let raw_first = bc.db.key_value().get(db::COL_EXTRA, b"first") - .expect("Low level database error when fetching 'first' block. Some issue with disk?") - .map(|v| v.into_vec()); - let mut best_ancient = bc.db.key_value().get(db::COL_EXTRA, b"ancient") + let best_block_rlp = bc + .block(&best_block_hash) + .expect("Best block is from a known block hash; qed"); + + // and write them to the cache. + let mut best_block = bc.best_block.write(); + *best_block = BestBlock { + total_difficulty: best_block_total_difficulty, + header: best_block_rlp.decode_header(), + block: best_block_rlp, + }; + } + + { + let best_block_number = bc.best_block.read().header.number(); + // Fetch first and best ancient block details + let raw_first = bc + .db + .key_value() + .get(db::COL_EXTRA, b"first") + .expect( + "Low level database error when fetching 'first' block. Some issue with disk?", + ) + .map(|v| v.into_vec()); + let mut best_ancient = bc.db.key_value().get(db::COL_EXTRA, b"ancient") .expect("Low level database error when fetching 'best ancient' block. Some issue with disk?") .map(|h| H256::from_slice(&h)); - let best_ancient_number; - if best_ancient.is_none() && best_block_number > 1 && bc.block_hash(1).is_none() { - best_ancient = Some(bc.genesis_hash()); - best_ancient_number = Some(0); - } else { - best_ancient_number = best_ancient.as_ref().and_then(|h| bc.block_number(h)); - } - - // binary search for the first block. - match raw_first { - None => { - let (mut f, mut hash) = (best_block_number, best_block_hash); - let mut l = best_ancient_number.unwrap_or(0); - - loop { - if l >= f { break; } - - let step = (f - l) >> 1; - let m = l + step; - - match bc.block_hash(m) { - Some(h) => { f = m; hash = h }, - None => { l = m + 1 }, - } - } - - if hash != bc.genesis_hash() { - trace!("First block calculated: {:?}", hash); - let mut batch = db.key_value().transaction(); - batch.put(db::COL_EXTRA, b"first", &hash); - db.key_value().write(batch).expect("Low level database error when writing 'first' block. Some issue with disk?"); - bc.first_block = Some(hash); - } - }, - Some(raw_first) => { - bc.first_block = Some(H256::from_slice(&raw_first)); - }, - } - - // and write them - if let (Some(hash), Some(number)) = (best_ancient, best_ancient_number) { - let mut best_ancient_block = bc.best_ancient_block.write(); - *best_ancient_block = Some(BestAncientBlock { - hash: hash, - number: number, - }); - } - } - - bc - } - - /// Returns true if the given parent block has given child - /// (though not necessarily a part of the canon chain). - fn is_known_child(&self, parent: &H256, hash: &H256) -> bool { - self.db.key_value().read_with_cache(db::COL_EXTRA, &self.block_details, parent).map_or(false, |d| d.children.contains(hash)) - } - - /// fetches the list of blocks from best block to n, and n's parent hash - /// where n > 0 - pub fn block_headers_from_best_block(&self, n: u32) -> Option<(Vec, H256)> { - let mut blocks = Vec::with_capacity(n as usize); - let mut hash = self.best_block_hash(); - - for _ in 0..n { - let current_hash = self.block_header_data(&hash)?; - hash = current_hash.parent_hash(); - blocks.push(current_hash); - } - - Some((blocks, hash)) - } - - /// Returns a tree route between `from` and `to`, which is a tuple of: - /// - /// - a vector of hashes of all blocks, ordered from `from` to `to`. - /// - /// - common ancestor of these blocks. - /// - /// - an index where best common ancestor would be - /// - /// 1.) from newer to older - /// - /// - bc: `A1 -> A2 -> A3 -> A4 -> A5` - /// - from: A5, to: A4 - /// - route: - /// - /// ```json - /// { blocks: [A5], ancestor: A4, index: 1 } - /// ``` - /// - /// 2.) from older to newer - /// - /// - bc: `A1 -> A2 -> A3 -> A4 -> A5` - /// - from: A3, to: A4 - /// - route: - /// - /// ```json - /// { blocks: [A4], ancestor: A3, index: 0 } - /// ``` - /// - /// 3.) fork: - /// - /// - bc: - /// - /// ```text - /// A1 -> A2 -> A3 -> A4 - /// -> B3 -> B4 - /// ``` - /// - from: B4, to: A4 - /// - route: - /// - /// ```json - /// { blocks: [B4, B3, A3, A4], ancestor: A2, index: 2 } - /// ``` - /// - /// If the tree route verges into pruned or unknown blocks, - /// `None` is returned. - pub fn tree_route(&self, from: H256, to: H256) -> Option { - let mut from_branch = vec![]; - let mut is_from_route_finalized = false; - let mut to_branch = vec![]; - - let mut from_details = self.block_details(&from)?; - let mut to_details = self.block_details(&to)?; - let mut current_from = from; - let mut current_to = to; - - // reset from && to to the same level - while from_details.number > to_details.number { - from_branch.push(current_from); - current_from = from_details.parent.clone(); - from_details = self.block_details(&from_details.parent)?; - is_from_route_finalized = is_from_route_finalized || from_details.is_finalized; - } - - while to_details.number > from_details.number { - to_branch.push(current_to); - current_to = to_details.parent.clone(); - to_details = self.block_details(&to_details.parent)?; - } - - assert_eq!(from_details.number, to_details.number); - - // move to shared parent - while current_from != current_to { - from_branch.push(current_from); - current_from = from_details.parent.clone(); - from_details = self.block_details(&from_details.parent)?; - is_from_route_finalized = is_from_route_finalized || from_details.is_finalized; - - to_branch.push(current_to); - current_to = to_details.parent.clone(); - to_details = self.block_details(&to_details.parent)?; - } - - let index = from_branch.len(); - - from_branch.extend(to_branch.into_iter().rev()); - - Some(TreeRoute { - blocks: from_branch, - ancestor: current_from, - index: index, - is_from_route_finalized: is_from_route_finalized, - }) - } - - /// Inserts a verified, known block from the canonical chain. - /// - /// Can be performed out-of-order, but care must be taken that the final chain is in a correct state. - /// This is used by snapshot restoration and when downloading missing blocks for the chain gap. - /// `is_best` forces the best block to be updated to this block. - /// `is_ancient` forces the best block of the first block sequence to be updated to this block. - /// `parent_td` is a parent total diffuculty - /// Supply a dummy parent total difficulty when the parent block may not be in the chain. - /// Returns true if the block is disconnected. - pub fn insert_unordered_block(&self, batch: &mut DBTransaction, block: encoded::Block, receipts: Vec, parent_td: Option, is_best: bool, is_ancient: bool) -> bool { - let block_number = block.header_view().number(); - let block_parent_hash = block.header_view().parent_hash(); - let block_difficulty = block.header_view().difficulty(); - let hash = block.header_view().hash(); - - if self.is_known(&hash) { - return false; - } - - assert!(self.pending_best_block.read().is_none()); - - let compressed_header = compress(block.header_view().rlp().as_raw(), blocks_swapper()); - let compressed_body = compress(&Self::block_to_body(block.raw()), blocks_swapper()); - - // store block in db - batch.put(db::COL_HEADERS, &hash, &compressed_header); - batch.put(db::COL_BODIES, &hash, &compressed_body); - - let maybe_parent = self.block_details(&block_parent_hash); - - if let Some(parent_details) = maybe_parent { - // parent known to be in chain. - let info = BlockInfo { - hash: hash, - number: block_number, - total_difficulty: parent_details.total_difficulty + block_difficulty, - location: BlockLocation::CanonChain, - }; - - self.prepare_update(batch, ExtrasUpdate { - block_hashes: self.prepare_block_hashes_update(&info), - block_details: self.prepare_block_details_update(block_parent_hash, &info, false), - block_receipts: self.prepare_block_receipts_update(receipts, &info), - blocks_blooms: self.prepare_block_blooms_update(block.header_view().log_bloom(), &info), - transactions_addresses: self.prepare_transaction_addresses_update(block.view().transaction_hashes(), &info), - info: info, - block, - }, is_best); - - if is_ancient { - self.set_best_ancient_block(block_number, &hash, batch); - } - - false - } else { - // parent not in the chain yet. we need the parent difficulty to proceed. - let d = parent_td + let best_ancient_number; + if best_ancient.is_none() && best_block_number > 1 && bc.block_hash(1).is_none() { + best_ancient = Some(bc.genesis_hash()); + best_ancient_number = Some(0); + } else { + best_ancient_number = best_ancient.as_ref().and_then(|h| bc.block_number(h)); + } + + // binary search for the first block. + match raw_first { + None => { + let (mut f, mut hash) = (best_block_number, best_block_hash); + let mut l = best_ancient_number.unwrap_or(0); + + loop { + if l >= f { + break; + } + + let step = (f - l) >> 1; + let m = l + step; + + match bc.block_hash(m) { + Some(h) => { + f = m; + hash = h + } + None => l = m + 1, + } + } + + if hash != bc.genesis_hash() { + trace!("First block calculated: {:?}", hash); + let mut batch = db.key_value().transaction(); + batch.put(db::COL_EXTRA, b"first", &hash); + db.key_value().write(batch).expect("Low level database error when writing 'first' block. Some issue with disk?"); + bc.first_block = Some(hash); + } + } + Some(raw_first) => { + bc.first_block = Some(H256::from_slice(&raw_first)); + } + } + + // and write them + if let (Some(hash), Some(number)) = (best_ancient, best_ancient_number) { + let mut best_ancient_block = bc.best_ancient_block.write(); + *best_ancient_block = Some(BestAncientBlock { + hash: hash, + number: number, + }); + } + } + + bc + } + + /// Returns true if the given parent block has given child + /// (though not necessarily a part of the canon chain). + fn is_known_child(&self, parent: &H256, hash: &H256) -> bool { + self.db + .key_value() + .read_with_cache(db::COL_EXTRA, &self.block_details, parent) + .map_or(false, |d| d.children.contains(hash)) + } + + /// Returns a tree route between `from` and `to`, which is a tuple of: + /// + /// - a vector of hashes of all blocks, ordered from `from` to `to`. + /// + /// - common ancestor of these blocks. + /// + /// - an index where best common ancestor would be + /// + /// 1.) from newer to older + /// + /// - bc: `A1 -> A2 -> A3 -> A4 -> A5` + /// - from: A5, to: A4 + /// - route: + /// + /// ```json + /// { blocks: [A5], ancestor: A4, index: 1 } + /// ``` + /// + /// 2.) from older to newer + /// + /// - bc: `A1 -> A2 -> A3 -> A4 -> A5` + /// - from: A3, to: A4 + /// - route: + /// + /// ```json + /// { blocks: [A4], ancestor: A3, index: 0 } + /// ``` + /// + /// 3.) fork: + /// + /// - bc: + /// + /// ```text + /// A1 -> A2 -> A3 -> A4 + /// -> B3 -> B4 + /// ``` + /// - from: B4, to: A4 + /// - route: + /// + /// ```json + /// { blocks: [B4, B3, A3, A4], ancestor: A2, index: 2 } + /// ``` + /// + /// If the tree route verges into pruned or unknown blocks, + /// `None` is returned. + /// + /// `is_from_route_finalized` returns whether the `from` part of the + /// route contains a finalized block. This only holds if the two parts (from + /// and to) are on different branches, ie. on 2 different forks. + pub fn tree_route(&self, from: H256, to: H256) -> Option { + let mut from_branch = vec![]; + let mut is_from_route_finalized = false; + let mut to_branch = vec![]; + + let mut from_details = self.block_details(&from)?; + let mut to_details = self.block_details(&to)?; + let mut current_from = from; + let mut current_to = to; + + // reset from && to to the same level + while from_details.number > to_details.number { + from_branch.push(current_from); + is_from_route_finalized = is_from_route_finalized || from_details.is_finalized; + current_from = from_details.parent.clone(); + from_details = self.block_details(&from_details.parent)?; + } + + while to_details.number > from_details.number { + to_branch.push(current_to); + current_to = to_details.parent.clone(); + to_details = self.block_details(&to_details.parent)?; + } + + assert_eq!(from_details.number, to_details.number); + + // move to shared parent + while current_from != current_to { + from_branch.push(current_from); + is_from_route_finalized = is_from_route_finalized || from_details.is_finalized; + current_from = from_details.parent.clone(); + from_details = self.block_details(&from_details.parent)?; + + to_branch.push(current_to); + current_to = to_details.parent.clone(); + to_details = self.block_details(&to_details.parent)?; + } + + let index = from_branch.len(); + + from_branch.extend(to_branch.into_iter().rev()); + + Some(TreeRoute { + blocks: from_branch, + ancestor: current_from, + index: index, + is_from_route_finalized: is_from_route_finalized, + }) + } + + /// Inserts a verified, known block from the canonical chain. + /// + /// Can be performed out-of-order, but care must be taken that the final chain is in a correct state. + /// This is used by snapshot restoration and when downloading missing blocks for the chain gap. + /// `is_best` forces the best block to be updated to this block. + /// `is_ancient` forces the best block of the first block sequence to be updated to this block. + /// `parent_td` is a parent total diffuculty + /// Supply a dummy parent total difficulty when the parent block may not be in the chain. + /// Returns true if the block is disconnected. + pub fn insert_unordered_block( + &self, + batch: &mut DBTransaction, + block: encoded::Block, + receipts: Vec, + parent_td: Option, + is_best: bool, + is_ancient: bool, + ) -> bool { + let block_number = block.header_view().number(); + let block_parent_hash = block.header_view().parent_hash(); + let block_difficulty = block.header_view().difficulty(); + let hash = block.header_view().hash(); + + if self.is_known(&hash) { + return false; + } + + assert!(self.pending_best_block.read().is_none()); + + let compressed_header = compress(block.header_view().rlp().as_raw(), blocks_swapper()); + let compressed_body = compress(&Self::block_to_body(block.raw()), blocks_swapper()); + + // store block in db + batch.put(db::COL_HEADERS, &hash, &compressed_header); + batch.put(db::COL_BODIES, &hash, &compressed_body); + + let maybe_parent = self.uncommitted_block_details(&block_parent_hash); + + if let Some(parent_details) = maybe_parent { + // parent known to be in chain. + let info = BlockInfo { + hash: hash, + number: block_number, + total_difficulty: parent_details.total_difficulty + block_difficulty, + location: BlockLocation::CanonChain, + }; + + self.prepare_update( + batch, + ExtrasUpdate { + block_hashes: self.prepare_block_hashes_update(&info), + block_details: self.prepare_block_details_update( + block_parent_hash, + &info, + false, + ), + block_receipts: self.prepare_block_receipts_update(receipts, &info), + blocks_blooms: self + .prepare_block_blooms_update(block.header_view().log_bloom(), &info), + transactions_addresses: self.prepare_transaction_addresses_update( + block.view().transaction_hashes(), + &info, + ), + info: info, + block, + }, + is_best, + ); + + if is_ancient { + self.set_best_ancient_block(block_number, &hash, batch); + } + + false + } else { + // parent not in the chain yet. we need the parent difficulty to proceed. + let d = parent_td .expect("parent total difficulty always supplied for first block in chunk. only first block can have missing parent; qed"); - let info = BlockInfo { - hash: hash, - number: block_number, - total_difficulty: d + block_difficulty, - location: BlockLocation::CanonChain, - }; - - // TODO [sorpaas] support warp sync insertion of finalization and metadata. - let block_details = BlockDetails { - number: block_number, - total_difficulty: info.total_difficulty, - parent: block_parent_hash, - children: Vec::new(), - is_finalized: false, - }; - - let mut update = HashMap::new(); - update.insert(hash, block_details); - - self.prepare_update(batch, ExtrasUpdate { - block_hashes: self.prepare_block_hashes_update(&info), - block_details: update, - block_receipts: self.prepare_block_receipts_update(receipts, &info), - blocks_blooms: self.prepare_block_blooms_update(block.header_view().log_bloom(), &info), - transactions_addresses: self.prepare_transaction_addresses_update(block.view().transaction_hashes(), &info), - info: info, - block, - }, is_best); - true - } - } - - /// Update the best ancient block to the given hash, after checking that - /// it's directly linked to the currently known best ancient block - pub fn update_best_ancient_block(&self, hash: &H256) { - // Get the block view of the next ancient block (it must - // be in DB at this point) - let block_view = match self.block(hash) { - Some(v) => v, - None => return, - }; - - // So that `best_ancient_block` gets unlocked before calling - // `set_best_ancient_block` - { - // Get the target hash ; if there are no ancient block, - // it means that the chain is already fully linked - // Release the `best_ancient_block` RwLock - let target_hash = { - let best_ancient_block = self.best_ancient_block.read(); - let cur_ancient_block = match *best_ancient_block { - Some(ref b) => b, - None => return, - }; - - // Ensure that the new best ancient block is after the current one - if block_view.number() <= cur_ancient_block.number { - return; - } - - cur_ancient_block.hash.clone() - }; - - let mut block_hash = *hash; - let mut is_linked = false; - - loop { - if block_hash == target_hash { - is_linked = true; - break; - } - - match self.block_details(&block_hash) { - Some(block_details) => { - block_hash = block_details.parent; - }, - None => break, - } - } - - if !is_linked { - trace!(target: "blockchain", "The given block {:x} is not linked to the known ancient block {:x}", hash, target_hash); - return; - } - } - - let mut batch = self.db.key_value().transaction(); - self.set_best_ancient_block(block_view.number(), hash, &mut batch); - self.db.key_value().write(batch).expect("Low level database error."); - } - - /// Set the best ancient block with the given value: private method - /// `best_ancient_block` must not be locked, otherwise a DeadLock would occur - fn set_best_ancient_block(&self, block_number: BlockNumber, block_hash: &H256, batch: &mut DBTransaction) { - let mut pending_best_ancient_block = self.pending_best_ancient_block.write(); - let ancient_number = self.best_ancient_block.read().as_ref().map_or(0, |b| b.number); - if self.block_hash(block_number + 1).is_some() { - trace!(target: "blockchain", "The two ends of the chain have met."); - batch.delete(db::COL_EXTRA, b"ancient"); - *pending_best_ancient_block = Some(None); - } else if block_number > ancient_number { - trace!(target: "blockchain", "Updating the best ancient block to {}.", block_number); - batch.put(db::COL_EXTRA, b"ancient", &block_hash); - *pending_best_ancient_block = Some(Some(BestAncientBlock { - hash: *block_hash, - number: block_number, - })); - } - } - - /// Insert an epoch transition. Provide an epoch number being transitioned to - /// and epoch transition object. - /// - /// The block the transition occurred at should have already been inserted into the chain. - pub fn insert_epoch_transition(&self, batch: &mut DBTransaction, epoch_num: u64, transition: EpochTransition) { - let mut transitions = match self.db.key_value().read(db::COL_EXTRA, &epoch_num) { - Some(existing) => existing, - None => EpochTransitions { - number: epoch_num, - candidates: Vec::with_capacity(1), - } - }; - - // ensure we don't write any duplicates. - if transitions.candidates.iter().find(|c| c.block_hash == transition.block_hash).is_none() { - transitions.candidates.push(transition); - batch.write(db::COL_EXTRA, &epoch_num, &transitions); - } - } - - /// Iterate over all epoch transitions. - /// This will only return transitions within the canonical chain. - pub fn epoch_transitions(&self) -> EpochTransitionIter { - let iter = self.db.key_value().iter_from_prefix(db::COL_EXTRA, &EPOCH_KEY_PREFIX[..]); - EpochTransitionIter { - chain: self, - prefix_iter: iter, - } - } - - /// Get a specific epoch transition by block number and provided block hash. - pub fn epoch_transition(&self, block_num: u64, block_hash: H256) -> Option { - trace!(target: "blockchain", "Loading epoch transition at block {}, {}", + let info = BlockInfo { + hash: hash, + number: block_number, + total_difficulty: d + block_difficulty, + location: BlockLocation::CanonChain, + }; + + // TODO [sorpaas] support warp sync insertion of finalization and metadata. + let block_details = BlockDetails { + number: block_number, + total_difficulty: info.total_difficulty, + parent: block_parent_hash, + children: Vec::new(), + is_finalized: false, + }; + + let mut update = HashMap::new(); + update.insert(hash, block_details); + + self.prepare_update( + batch, + ExtrasUpdate { + block_hashes: self.prepare_block_hashes_update(&info), + block_details: update, + block_receipts: self.prepare_block_receipts_update(receipts, &info), + blocks_blooms: self + .prepare_block_blooms_update(block.header_view().log_bloom(), &info), + transactions_addresses: self.prepare_transaction_addresses_update( + block.view().transaction_hashes(), + &info, + ), + info: info, + block, + }, + is_best, + ); + true + } + } + + /// clears all caches, re-loads best block from disk for testing purposes + pub fn clear_cache(&self) { + self.block_bodies.write().clear(); + self.block_details.write().clear(); + self.block_hashes.write().clear(); + self.block_headers.write().clear(); + // Fetch best block details from disk + let best_block_hash = self + .db + .key_value() + .get(db::COL_EXTRA, b"best") + .expect("Low-level database error when fetching 'best' block. Some issue with disk?") + .as_ref() + .map(|r| H256::from_slice(r)) + .unwrap(); + let best_block_total_difficulty = self.block_details(&best_block_hash) + .expect("Best block is from a known block hash; a known block hash always comes with a known block detail; qed") + .total_difficulty; + let best_block_rlp = self + .block(&best_block_hash) + .expect("Best block is from a known block hash; qed"); + + // and write them to the cache + let mut best_block = self.best_block.write(); + *best_block = BestBlock { + total_difficulty: best_block_total_difficulty, + header: best_block_rlp.decode_header(), + block: best_block_rlp, + }; + } + + /// Update the best ancient block to the given hash, after checking that + /// it's directly linked to the currently known best ancient block + pub fn update_best_ancient_block(&self, hash: &H256) { + // Get the block view of the next ancient block (it must + // be in DB at this point) + let block_view = match self.block(hash) { + Some(v) => v, + None => return, + }; + + // So that `best_ancient_block` gets unlocked before calling + // `set_best_ancient_block` + { + // Get the target hash ; if there are no ancient block, + // it means that the chain is already fully linked + // Release the `best_ancient_block` RwLock + let target_hash = { + let best_ancient_block = self.best_ancient_block.read(); + let cur_ancient_block = match *best_ancient_block { + Some(ref b) => b, + None => return, + }; + + // Ensure that the new best ancient block is after the current one + if block_view.number() <= cur_ancient_block.number { + return; + } + + cur_ancient_block.hash.clone() + }; + + let mut block_hash = *hash; + let mut is_linked = false; + + loop { + if block_hash == target_hash { + is_linked = true; + break; + } + + match self.block_details(&block_hash) { + Some(block_details) => { + block_hash = block_details.parent; + } + None => break, + } + } + + if !is_linked { + trace!(target: "blockchain", "The given block {:x} is not linked to the known ancient block {:x}", hash, target_hash); + return; + } + } + + let mut batch = self.db.key_value().transaction(); + self.set_best_ancient_block(block_view.number(), hash, &mut batch); + self.db + .key_value() + .write(batch) + .expect("Low level database error."); + } + + /// Set the best ancient block with the given value: private method + /// `best_ancient_block` must not be locked, otherwise a DeadLock would occur + fn set_best_ancient_block( + &self, + block_number: BlockNumber, + block_hash: &H256, + batch: &mut DBTransaction, + ) { + let mut pending_best_ancient_block = self.pending_best_ancient_block.write(); + let ancient_number = self + .best_ancient_block + .read() + .as_ref() + .map_or(0, |b| b.number); + if self.block_hash(block_number + 1).is_some() { + trace!(target: "blockchain", "The two ends of the chain have met."); + batch.delete(db::COL_EXTRA, b"ancient"); + *pending_best_ancient_block = Some(None); + } else if block_number > ancient_number { + trace!(target: "blockchain", "Updating the best ancient block to {}.", block_number); + batch.put(db::COL_EXTRA, b"ancient", &block_hash); + *pending_best_ancient_block = Some(Some(BestAncientBlock { + hash: *block_hash, + number: block_number, + })); + } + } + + /// Insert an epoch transition. Provide an epoch number being transitioned to + /// and epoch transition object. + /// + /// The block the transition occurred at should have already been inserted into the chain. + pub fn insert_epoch_transition( + &self, + batch: &mut DBTransaction, + epoch_num: u64, + transition: EpochTransition, + ) { + let mut transitions = match self.db.key_value().read(db::COL_EXTRA, &epoch_num) { + Some(existing) => existing, + None => EpochTransitions { + number: epoch_num, + candidates: Vec::with_capacity(1), + }, + }; + + // ensure we don't write any duplicates. + if transitions + .candidates + .iter() + .find(|c| c.block_hash == transition.block_hash) + .is_none() + { + transitions.candidates.push(transition); + batch.write(db::COL_EXTRA, &epoch_num, &transitions); + } + } + + /// Iterate over all epoch transitions. + /// This will only return transitions within the canonical chain. + pub fn epoch_transitions(&self) -> EpochTransitionIter { + let iter = self + .db + .key_value() + .iter_from_prefix(db::COL_EXTRA, &EPOCH_KEY_PREFIX[..]); + EpochTransitionIter { + chain: self, + prefix_iter: iter, + } + } + + /// Get a specific epoch transition by block number and provided block hash. + pub fn epoch_transition(&self, block_num: u64, block_hash: H256) -> Option { + trace!(target: "blockchain", "Loading epoch transition at block {}, {}", block_num, block_hash); - self.db.key_value().read(db::COL_EXTRA, &block_num).and_then(|transitions: EpochTransitions| { - transitions.candidates.into_iter().find(|c| c.block_hash == block_hash) - }) - } - - /// Get the transition to the epoch the given parent hash is part of - /// or transitions to. - /// This will give the epoch that any children of this parent belong to. - /// - /// The block corresponding the the parent hash must be stored already. - pub fn epoch_transition_for(&self, parent_hash: H256) -> Option { - // slow path: loop back block by block - for hash in self.ancestry_iter(parent_hash)? { - let details = self.block_details(&hash)?; - - // look for transition in database. - if let Some(transition) = self.epoch_transition(details.number, hash) { - return Some(transition) - } - - // canonical hash -> fast breakout: - // get the last epoch transition up to this block. - // - // if `block_hash` is canonical it will only return transitions up to - // the parent. - if self.block_hash(details.number)? == hash { - return self.epoch_transitions() - .map(|(_, t)| t) - .take_while(|t| t.block_number <= details.number) - .last() - } - } - - // should never happen as the loop will encounter genesis before concluding. - None - } - - /// Write a pending epoch transition by block hash. - pub fn insert_pending_transition(&self, batch: &mut DBTransaction, hash: H256, t: PendingEpochTransition) { - batch.write(db::COL_EXTRA, &hash, &t); - } - - /// Get a pending epoch transition by block hash. - // TODO: implement removal safely: this can only be done upon finality of a block - // that _uses_ the pending transition. - pub fn get_pending_transition(&self, hash: H256) -> Option { - self.db.key_value().read(db::COL_EXTRA, &hash) - } - - /// Add a child to a given block. Assumes that the block hash is in - /// the chain and the child's parent is this block. - /// - /// Used in snapshots to glue the chunks together at the end. - pub fn add_child(&self, batch: &mut DBTransaction, block_hash: H256, child_hash: H256) { - let mut parent_details = self.block_details(&block_hash) - .unwrap_or_else(|| panic!("Invalid block hash: {:?}", block_hash)); - - parent_details.children.push(child_hash); - - let mut update = HashMap::new(); - update.insert(block_hash, parent_details); - - let mut write_details = self.block_details.write(); - batch.extend_with_cache(db::COL_EXTRA, &mut *write_details, update, CacheUpdatePolicy::Overwrite); - - self.cache_man.lock().note_used(CacheId::BlockDetails(block_hash)); - } - - /// Inserts the block into backing cache database. - /// Expects the block to be valid and already verified. - /// If the block is already known, does nothing. - pub fn insert_block(&self, batch: &mut DBTransaction, block: encoded::Block, receipts: Vec, extras: ExtrasInsert) -> ImportRoute { - let parent_hash = block.header_view().parent_hash(); - let best_hash = self.best_block_hash(); - - let route = self.tree_route(best_hash, parent_hash).expect("forks are only kept when it has common ancestors; tree route from best to prospective's parent always exists; qed"); - - self.insert_block_with_route(batch, block, receipts, route, extras) - } - - /// Inserts the block into backing cache database with already generated route information. - /// Expects the block to be valid and already verified and route is tree route information from current best block to new block's parent. - /// If the block is already known, does nothing. - pub fn insert_block_with_route(&self, batch: &mut DBTransaction, block: encoded::Block, receipts: Vec, route: TreeRoute, extras: ExtrasInsert) -> ImportRoute { - let hash = block.header_view().hash(); - let parent_hash = block.header_view().parent_hash(); - - if self.is_known_child(&parent_hash, &hash) { - return ImportRoute::none(); - } - - assert!(self.pending_best_block.read().is_none()); - - let compressed_header = compress(block.header_view().rlp().as_raw(), blocks_swapper()); - let compressed_body = compress(&Self::block_to_body(block.raw()), blocks_swapper()); - - // store block in db - batch.put(db::COL_HEADERS, &hash, &compressed_header); - batch.put(db::COL_BODIES, &hash, &compressed_body); - - let info = self.block_info(&block.header_view(), route, &extras); - - if let BlockLocation::BranchBecomingCanonChain(ref d) = info.location { - info!(target: "reorg", "Reorg to {} ({} {} {})", - Colour::Yellow.bold().paint(format!("#{} {}", info.number, info.hash)), - Colour::Red.paint(d.retracted.iter().join(" ")), - Colour::White.paint(format!("#{} {}", self.block_details(&d.ancestor).expect("`ancestor` is in the route; qed").number, d.ancestor)), - Colour::Green.paint(d.enacted.iter().join(" ")) - ); - } - - self.prepare_update(batch, ExtrasUpdate { - block_hashes: self.prepare_block_hashes_update(&info), - block_details: self.prepare_block_details_update(parent_hash, &info, extras.is_finalized), - block_receipts: self.prepare_block_receipts_update(receipts, &info), - blocks_blooms: self.prepare_block_blooms_update(block.header_view().log_bloom(), &info), - transactions_addresses: self.prepare_transaction_addresses_update(block.view().transaction_hashes(), &info), - info: info.clone(), - block, - }, true); - - ImportRoute::from(info) - } - - /// Get inserted block info which is critical to prepare extras updates. - fn block_info(&self, header: &HeaderView, route: TreeRoute, extras: &ExtrasInsert) -> BlockInfo { - let hash = header.hash(); - let number = header.number(); - let parent_hash = header.parent_hash(); - let parent_details = self.block_details(&parent_hash).unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash)); - - BlockInfo { - hash: hash, - number: number, - total_difficulty: parent_details.total_difficulty + header.difficulty(), - location: match extras.fork_choice { - ForkChoice::New => { - // On new best block we need to make sure that all ancestors - // are moved to "canon chain" - // find the route between old best block and the new one - match route.blocks.len() { - 0 => BlockLocation::CanonChain, - _ => { - let retracted = route.blocks.iter().take(route.index).cloned().collect::>().into_iter().collect::>(); - let enacted = route.blocks.into_iter().skip(route.index).collect::>(); - BlockLocation::BranchBecomingCanonChain(BranchBecomingCanonChainData { - ancestor: route.ancestor, - enacted: enacted, - retracted: retracted, - }) - } - } - }, - ForkChoice::Old => BlockLocation::Branch, - }, - } - } - - /// Mark a block to be considered finalized. Returns `Some(())` if the operation succeeds, and `None` if the block - /// hash is not found. - pub fn mark_finalized(&self, batch: &mut DBTransaction, block_hash: H256) -> Option<()> { - let mut block_details = self.block_details(&block_hash)?; - block_details.is_finalized = true; - - self.update_block_details(batch, block_hash, block_details); - Some(()) - } - - /// Prepares extras block detail update. - fn update_block_details(&self, batch: &mut DBTransaction, block_hash: H256, block_details: BlockDetails) { - let mut details_map = HashMap::new(); - details_map.insert(block_hash, block_details); - - // We're only updating one existing value. So it shouldn't suffer from cache decoherence problem. - let mut write_details = self.pending_block_details.write(); - batch.extend_with_cache(db::COL_EXTRA, &mut *write_details, details_map, CacheUpdatePolicy::Overwrite); - } - - /// Prepares extras update. - fn prepare_update(&self, batch: &mut DBTransaction, update: ExtrasUpdate, is_best: bool) { - - { - let mut write_receipts = self.block_receipts.write(); - batch.extend_with_cache(db::COL_EXTRA, &mut *write_receipts, update.block_receipts, CacheUpdatePolicy::Remove); - } - - if let Some((block, blooms)) = update.blocks_blooms { - self.db.blooms() - .insert_blooms(block, blooms.iter()) - .expect("Low level database error when updating blooms. Some issue with disk?"); - } - - // These cached values must be updated last with all four locks taken to avoid - // cache decoherence - { - let mut best_block = self.pending_best_block.write(); - if is_best && update.info.location != BlockLocation::Branch { - batch.put(db::COL_EXTRA, b"best", &update.info.hash); - *best_block = Some(BestBlock { - total_difficulty: update.info.total_difficulty, - header: update.block.decode_header(), - block: update.block, - }); - } - - let mut write_hashes = self.pending_block_hashes.write(); - let mut write_details = self.pending_block_details.write(); - let mut write_txs = self.pending_transaction_addresses.write(); - - batch.extend_with_cache(db::COL_EXTRA, &mut *write_details, update.block_details, CacheUpdatePolicy::Overwrite); - batch.extend_with_cache(db::COL_EXTRA, &mut *write_hashes, update.block_hashes, CacheUpdatePolicy::Overwrite); - batch.extend_with_option_cache(db::COL_EXTRA, &mut *write_txs, update.transactions_addresses, CacheUpdatePolicy::Overwrite); - } - } - - /// Apply pending insertion updates - pub fn commit(&self) { - let mut pending_best_ancient_block = self.pending_best_ancient_block.write(); - let mut pending_best_block = self.pending_best_block.write(); - let mut pending_write_hashes = self.pending_block_hashes.write(); - let mut pending_block_details = self.pending_block_details.write(); - let mut pending_write_txs = self.pending_transaction_addresses.write(); - - let mut best_block = self.best_block.write(); - let mut best_ancient_block = self.best_ancient_block.write(); - let mut write_block_details = self.block_details.write(); - let mut write_hashes = self.block_hashes.write(); - let mut write_txs = self.transaction_addresses.write(); - // update best ancient block - if let Some(block_option) = pending_best_ancient_block.take() { - *best_ancient_block = block_option; - } - // update best block - if let Some(block) = pending_best_block.take() { - *best_block = block; - } - - let pending_txs = mem::replace(&mut *pending_write_txs, HashMap::new()); - let (retracted_txs, enacted_txs) = pending_txs.into_iter().partition::, _>(|&(_, ref value)| value.is_none()); - - let pending_hashes_keys: Vec<_> = pending_write_hashes.keys().cloned().collect(); - let enacted_txs_keys: Vec<_> = enacted_txs.keys().cloned().collect(); - let pending_block_hashes: Vec<_> = pending_block_details.keys().cloned().collect(); - - write_hashes.extend(mem::replace(&mut *pending_write_hashes, HashMap::new())); - write_txs.extend(enacted_txs.into_iter().map(|(k, v)| (k, v.expect("Transactions were partitioned; qed")))); - write_block_details.extend(mem::replace(&mut *pending_block_details, HashMap::new())); - - for hash in retracted_txs.keys() { - write_txs.remove(hash); - } - - let mut cache_man = self.cache_man.lock(); - for n in pending_hashes_keys { - cache_man.note_used(CacheId::BlockHashes(n)); - } - - for hash in enacted_txs_keys { - cache_man.note_used(CacheId::TransactionAddresses(hash)); - } - - for hash in pending_block_hashes { - cache_man.note_used(CacheId::BlockDetails(hash)); - } - } - - /// Iterator that lists `first` and then all of `first`'s ancestors, by hash. - pub fn ancestry_iter(&self, first: H256) -> Option { - if self.is_known(&first) { - Some(AncestryIter { - current: first, - chain: self, - }) - } else { - None - } - } - - /// Iterator that lists `first` and then all of `first`'s ancestors, by extended header. - pub fn ancestry_with_metadata_iter<'a>(&'a self, first: H256) -> AncestryWithMetadataIter { - AncestryWithMetadataIter { - current: if self.is_known(&first) { - first - } else { - H256::default() // zero hash - }, - chain: self - } - } - - /// Given a block's `parent`, find every block header which represents a valid possible uncle. - pub fn find_uncle_headers(&self, parent: &H256, uncle_generations: usize) -> Option> { - self.find_uncle_hashes(parent, uncle_generations) - .map(|v| v.into_iter().filter_map(|h| self.block_header_data(&h)).collect()) - } - - /// Given a block's `parent`, find every block hash which represents a valid possible uncle. - pub fn find_uncle_hashes(&self, parent: &H256, uncle_generations: usize) -> Option> { - if !self.is_known(parent) { - return None; - } - - let mut excluded = HashSet::new(); - let ancestry = self.ancestry_iter(parent.clone())?; - - for a in ancestry.clone().take(uncle_generations) { - if let Some(uncles) = self.uncle_hashes(&a) { - excluded.extend(uncles); - excluded.insert(a); - } else { - break - } - } - - let mut ret = Vec::new(); - for a in ancestry.skip(1).take(uncle_generations) { - if let Some(details) = self.block_details(&a) { - ret.extend(details.children.iter().filter(|h| !excluded.contains(h))) - } else { - break - } - } - - Some(ret) - } - - /// This function returns modified block hashes. - fn prepare_block_hashes_update(&self, info: &BlockInfo) -> HashMap { - let mut block_hashes = HashMap::new(); - - match info.location { - BlockLocation::Branch => (), - BlockLocation::CanonChain => { - block_hashes.insert(info.number, info.hash); - }, - BlockLocation::BranchBecomingCanonChain(ref data) => { - let ancestor_number = self.block_number(&data.ancestor).expect("Block number of ancestor is always in DB"); - let start_number = ancestor_number + 1; - - for (index, hash) in data.enacted.iter().cloned().enumerate() { - block_hashes.insert(start_number + index as BlockNumber, hash); - } - - block_hashes.insert(info.number, info.hash); - } - } - - block_hashes - } - - /// This function returns modified block details. - /// Uses the given parent details or attempts to load them from the database. - fn prepare_block_details_update(&self, parent_hash: H256, info: &BlockInfo, is_finalized: bool) -> HashMap { - // update parent - let mut parent_details = self.block_details(&parent_hash).unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash)); - parent_details.children.push(info.hash); - - // create current block details. - let details = BlockDetails { - number: info.number, - total_difficulty: info.total_difficulty, - parent: parent_hash, - children: vec![], - is_finalized: is_finalized, - }; - - // write to batch - let mut block_details = HashMap::new(); - block_details.insert(parent_hash, parent_details); - block_details.insert(info.hash, details); - block_details - } - - /// This function returns modified block receipts. - fn prepare_block_receipts_update(&self, receipts: Vec, info: &BlockInfo) -> HashMap { - let mut block_receipts = HashMap::new(); - block_receipts.insert(info.hash, BlockReceipts::new(receipts)); - block_receipts - } - - /// This function returns modified transaction addresses. - fn prepare_transaction_addresses_update(&self, transaction_hashes: Vec, info: &BlockInfo) -> HashMap> { - match info.location { - BlockLocation::CanonChain => { - transaction_hashes.into_iter() - .enumerate() - .map(|(i ,tx_hash)| { - (tx_hash, Some(TransactionAddress { - block_hash: info.hash, - index: i - })) - }) - .collect() - }, - BlockLocation::BranchBecomingCanonChain(ref data) => { - let addresses = data.enacted.iter() - .flat_map(|hash| { - let body = self.block_body(hash).expect("Enacted block must be in database."); - let hashes = body.transaction_hashes(); - hashes.into_iter() - .enumerate() - .map(|(i, tx_hash)| (tx_hash, Some(TransactionAddress { - block_hash: *hash, - index: i, - }))) - .collect::>>() - }); - - let current_addresses = transaction_hashes.into_iter() - .enumerate() - .map(|(i ,tx_hash)| { - (tx_hash, Some(TransactionAddress { - block_hash: info.hash, - index: i - })) - }); - - let retracted = data.retracted.iter().flat_map(|hash| { - let body = self.block_body(hash).expect("Retracted block must be in database."); - let hashes = body.transaction_hashes(); - hashes.into_iter().map(|hash| (hash, None)).collect::>>() - }); - - // The order here is important! Don't remove transaction if it was part of enacted blocks as well. - retracted.chain(addresses).chain(current_addresses).collect() - }, - BlockLocation::Branch => HashMap::new(), - } - } - - /// This functions returns modified blocks blooms. - /// - /// To accelerate blooms lookups, blomms are stored in multiple - /// layers (BLOOM_LEVELS, currently 3). - /// ChainFilter is responsible for building and rebuilding these layers. - /// It returns them in HashMap, where values are Blooms and - /// keys are BloomIndexes. BloomIndex represents bloom location on one - /// of these layers. - /// - /// To reduce number of queries to databse, block blooms are stored - /// in BlocksBlooms structure which contains info about several - /// (BLOOM_INDEX_SIZE, currently 16) consecutive blocks blooms. - /// - /// Later, BloomIndexer is used to map bloom location on filter layer (BloomIndex) - /// to bloom location in database (BlocksBloomLocation). - /// - fn prepare_block_blooms_update(&self, log_bloom: Bloom, info: &BlockInfo) -> Option<(u64, Vec)> { - match info.location { - BlockLocation::Branch => None, - BlockLocation::CanonChain => { - if log_bloom.is_zero() { - None - } else { - Some((info.number, vec![log_bloom])) - } - }, - BlockLocation::BranchBecomingCanonChain(ref data) => { - let ancestor_number = self.block_number(&data.ancestor) + self.db + .key_value() + .read(db::COL_EXTRA, &block_num) + .and_then(|transitions: EpochTransitions| { + transitions + .candidates + .into_iter() + .find(|c| c.block_hash == block_hash) + }) + } + + /// Get the transition to the epoch the given parent hash is part of + /// or transitions to. + /// This will give the epoch that any children of this parent belong to. + /// + /// The block corresponding the the parent hash must be stored already. + pub fn epoch_transition_for(&self, parent_hash: H256) -> Option { + // slow path: loop back block by block + for hash in self.ancestry_iter(parent_hash)? { + let details = self.block_details(&hash)?; + + // look for transition in database. + if let Some(transition) = self.epoch_transition(details.number, hash) { + return Some(transition); + } + + // canonical hash -> fast breakout: + // get the last epoch transition up to this block. + // + // if `block_hash` is canonical it will only return transitions up to + // the parent. + if self.block_hash(details.number)? == hash { + return self + .epoch_transitions() + .map(|(_, t)| t) + .take_while(|t| t.block_number <= details.number) + .last(); + } + } + + // should never happen as the loop will encounter genesis before concluding. + None + } + + /// Write a pending epoch transition by block hash. + pub fn insert_pending_transition( + &self, + batch: &mut DBTransaction, + hash: H256, + t: PendingEpochTransition, + ) { + batch.write(db::COL_EXTRA, &hash, &t); + } + + /// Get a pending epoch transition by block hash. + // TODO: implement removal safely: this can only be done upon finality of a block + // that _uses_ the pending transition. + pub fn get_pending_transition(&self, hash: H256) -> Option { + self.db.key_value().read(db::COL_EXTRA, &hash) + } + + /// Add a child to a given block. Assumes that the block hash is in + /// the chain and the child's parent is this block. + /// + /// Used in snapshots to glue the chunks together at the end. + pub fn add_child(&self, batch: &mut DBTransaction, block_hash: H256, child_hash: H256) { + let mut parent_details = self + .uncommitted_block_details(&block_hash) + .unwrap_or_else(|| panic!("Invalid block hash: {:?}", block_hash)); + + parent_details.children.push(child_hash); + + let mut update = HashMap::new(); + update.insert(block_hash, parent_details); + + let mut write_details = self.block_details.write(); + batch.extend_with_cache( + db::COL_EXTRA, + &mut *write_details, + update, + CacheUpdatePolicy::Overwrite, + ); + + self.cache_man + .lock() + .note_used(CacheId::BlockDetails(block_hash)); + } + + /// Inserts the block into backing cache database. + /// Expects the block to be valid and already verified. + /// If the block is already known, does nothing. + pub fn insert_block( + &self, + batch: &mut DBTransaction, + block: encoded::Block, + receipts: Vec, + extras: ExtrasInsert, + ) -> ImportRoute { + let parent_hash = block.header_view().parent_hash(); + let best_hash = self.best_block_hash(); + + let route = self.tree_route(best_hash, parent_hash).expect("forks are only kept when it has common ancestors; tree route from best to prospective's parent always exists; qed"); + + self.insert_block_with_route(batch, block, receipts, route, extras) + } + + /// Inserts the block into backing cache database with already generated route information. + /// Expects the block to be valid and already verified and route is tree route information from current best block to new block's parent. + /// If the block is already known, does nothing. + pub fn insert_block_with_route( + &self, + batch: &mut DBTransaction, + block: encoded::Block, + receipts: Vec, + route: TreeRoute, + extras: ExtrasInsert, + ) -> ImportRoute { + let hash = block.header_view().hash(); + let parent_hash = block.header_view().parent_hash(); + + if self.is_known_child(&parent_hash, &hash) { + return ImportRoute::none(); + } + + assert!(self.pending_best_block.read().is_none()); + + let compressed_header = compress(block.header_view().rlp().as_raw(), blocks_swapper()); + let compressed_body = compress(&Self::block_to_body(block.raw()), blocks_swapper()); + + // store block in db + batch.put(db::COL_HEADERS, &hash, &compressed_header); + batch.put(db::COL_BODIES, &hash, &compressed_body); + + let info = self.block_info(&block.header_view(), route, &extras); + + if let BlockLocation::BranchBecomingCanonChain(ref d) = info.location { + info!(target: "reorg", "Reorg to {} ({} {} {})", + Colour::Yellow.bold().paint(format!("#{} {}", info.number, info.hash)), + Colour::Red.paint(d.retracted.iter().join(" ")), + Colour::White.paint(format!("#{} {}", self.block_details(&d.ancestor).expect("`ancestor` is in the route; qed").number, d.ancestor)), + Colour::Green.paint(d.enacted.iter().join(" ")) + ); + } + + self.prepare_update( + batch, + ExtrasUpdate { + block_hashes: self.prepare_block_hashes_update(&info), + block_details: self.prepare_block_details_update( + parent_hash, + &info, + extras.is_finalized, + ), + block_receipts: self.prepare_block_receipts_update(receipts, &info), + blocks_blooms: self + .prepare_block_blooms_update(block.header_view().log_bloom(), &info), + transactions_addresses: self + .prepare_transaction_addresses_update(block.view().transaction_hashes(), &info), + info: info.clone(), + block, + }, + true, + ); + + ImportRoute::from(info) + } + + /// Get inserted block info which is critical to prepare extras updates. + fn block_info( + &self, + header: &HeaderView, + route: TreeRoute, + extras: &ExtrasInsert, + ) -> BlockInfo { + let hash = header.hash(); + let number = header.number(); + let parent_hash = header.parent_hash(); + let parent_details = self + .block_details(&parent_hash) + .unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash)); + + BlockInfo { + hash: hash, + number: number, + total_difficulty: parent_details.total_difficulty + header.difficulty(), + location: match extras.fork_choice { + ForkChoice::New => { + // On new best block we need to make sure that all ancestors + // are moved to "canon chain" + // find the route between old best block and the new one + match route.blocks.len() { + 0 => BlockLocation::CanonChain, + _ => { + let retracted = route + .blocks + .iter() + .take(route.index) + .cloned() + .collect::>() + .into_iter() + .collect::>(); + let enacted = route + .blocks + .into_iter() + .skip(route.index) + .collect::>(); + BlockLocation::BranchBecomingCanonChain(BranchBecomingCanonChainData { + ancestor: route.ancestor, + enacted: enacted, + retracted: retracted, + }) + } + } + } + ForkChoice::Old => BlockLocation::Branch, + }, + } + } + + /// Mark a block to be considered finalized. Returns `Some(())` if the operation succeeds, and `None` if the block + /// hash is not found. + pub fn mark_finalized(&self, batch: &mut DBTransaction, block_hash: H256) -> Option<()> { + let mut block_details = self.uncommitted_block_details(&block_hash)?; + block_details.is_finalized = true; + + self.update_block_details(batch, block_hash, block_details); + Some(()) + } + + /// Prepares extras block detail update. + fn update_block_details( + &self, + batch: &mut DBTransaction, + block_hash: H256, + block_details: BlockDetails, + ) { + let mut details_map = HashMap::new(); + details_map.insert(block_hash, block_details); + + // We're only updating one existing value. So it shouldn't suffer from cache decoherence problem. + let mut write_details = self.pending_block_details.write(); + batch.extend_with_cache( + db::COL_EXTRA, + &mut *write_details, + details_map, + CacheUpdatePolicy::Overwrite, + ); + } + + /// Prepares extras update. + fn prepare_update(&self, batch: &mut DBTransaction, update: ExtrasUpdate, is_best: bool) { + { + let mut write_receipts = self.block_receipts.write(); + batch.extend_with_cache( + db::COL_EXTRA, + &mut *write_receipts, + update.block_receipts, + CacheUpdatePolicy::Remove, + ); + } + + if let Some((block, blooms)) = update.blocks_blooms { + self.db + .blooms() + .insert_blooms(block, blooms.iter()) + .expect("Low level database error when updating blooms. Some issue with disk?"); + } + + // These cached values must be updated last with all four locks taken to avoid + // cache decoherence + { + let mut best_block = self.pending_best_block.write(); + if is_best && update.info.location != BlockLocation::Branch { + batch.put(db::COL_EXTRA, b"best", &update.info.hash); + *best_block = Some(BestBlock { + total_difficulty: update.info.total_difficulty, + header: update.block.decode_header(), + block: update.block, + }); + } + + let mut write_hashes = self.pending_block_hashes.write(); + let mut write_details = self.pending_block_details.write(); + let mut write_txs = self.pending_transaction_addresses.write(); + + batch.extend_with_cache( + db::COL_EXTRA, + &mut *write_details, + update.block_details, + CacheUpdatePolicy::Overwrite, + ); + batch.extend_with_cache( + db::COL_EXTRA, + &mut *write_hashes, + update.block_hashes, + CacheUpdatePolicy::Overwrite, + ); + batch.extend_with_option_cache( + db::COL_EXTRA, + &mut *write_txs, + update.transactions_addresses, + CacheUpdatePolicy::Overwrite, + ); + } + } + + /// Apply pending insertion updates + pub fn commit(&self) { + let mut pending_best_ancient_block = self.pending_best_ancient_block.write(); + let mut pending_best_block = self.pending_best_block.write(); + let mut pending_write_hashes = self.pending_block_hashes.write(); + let mut pending_block_details = self.pending_block_details.write(); + let mut pending_write_txs = self.pending_transaction_addresses.write(); + + let mut best_block = self.best_block.write(); + let mut best_ancient_block = self.best_ancient_block.write(); + let mut write_block_details = self.block_details.write(); + let mut write_hashes = self.block_hashes.write(); + let mut write_txs = self.transaction_addresses.write(); + // update best ancient block + if let Some(block_option) = pending_best_ancient_block.take() { + *best_ancient_block = block_option; + } + // update best block + if let Some(block) = pending_best_block.take() { + *best_block = block; + } + + let pending_txs = mem::replace(&mut *pending_write_txs, HashMap::new()); + let (retracted_txs, enacted_txs) = pending_txs + .into_iter() + .partition::, _>(|&(_, ref value)| value.is_none()); + + let pending_hashes_keys: Vec<_> = pending_write_hashes.keys().cloned().collect(); + let enacted_txs_keys: Vec<_> = enacted_txs.keys().cloned().collect(); + let pending_block_hashes: Vec<_> = pending_block_details.keys().cloned().collect(); + + write_hashes.extend(mem::replace(&mut *pending_write_hashes, HashMap::new())); + write_txs.extend( + enacted_txs + .into_iter() + .map(|(k, v)| (k, v.expect("Transactions were partitioned; qed"))), + ); + write_block_details.extend(mem::replace(&mut *pending_block_details, HashMap::new())); + + for hash in retracted_txs.keys() { + write_txs.remove(hash); + } + + let mut cache_man = self.cache_man.lock(); + for n in pending_hashes_keys { + cache_man.note_used(CacheId::BlockHashes(n)); + } + + for hash in enacted_txs_keys { + cache_man.note_used(CacheId::TransactionAddresses(hash)); + } + + for hash in pending_block_hashes { + cache_man.note_used(CacheId::BlockDetails(hash)); + } + } + + /// Iterator that lists `first` and then all of `first`'s ancestors, by hash. + pub fn ancestry_iter(&self, first: H256) -> Option { + if self.is_known(&first) { + Some(AncestryIter { + current: first, + chain: self, + }) + } else { + None + } + } + + /// Iterator that lists `first` and then all of `first`'s ancestors, by extended header. + pub fn ancestry_with_metadata_iter<'a>(&'a self, first: H256) -> AncestryWithMetadataIter { + AncestryWithMetadataIter { + current: if self.is_known(&first) { + first + } else { + H256::default() // zero hash + }, + chain: self, + } + } + + /// Given a block's `parent`, find every block header which represents a valid possible uncle. + pub fn find_uncle_headers( + &self, + parent: &H256, + uncle_generations: usize, + ) -> Option> { + self.find_uncle_hashes(parent, uncle_generations).map(|v| { + v.into_iter() + .filter_map(|h| self.block_header_data(&h)) + .collect() + }) + } + + /// Given a block's `parent`, find every block hash which represents a valid possible uncle. + pub fn find_uncle_hashes(&self, parent: &H256, uncle_generations: usize) -> Option> { + if !self.is_known(parent) { + return None; + } + + let mut excluded = HashSet::new(); + let ancestry = self.ancestry_iter(parent.clone())?; + + for a in ancestry.clone().take(uncle_generations) { + if let Some(uncles) = self.uncle_hashes(&a) { + excluded.extend(uncles); + excluded.insert(a); + } else { + break; + } + } + + let mut ret = Vec::new(); + for a in ancestry.skip(1).take(uncle_generations) { + if let Some(details) = self.block_details(&a) { + ret.extend(details.children.iter().filter(|h| !excluded.contains(h))) + } else { + break; + } + } + + Some(ret) + } + + /// This function returns modified block hashes. + fn prepare_block_hashes_update(&self, info: &BlockInfo) -> HashMap { + let mut block_hashes = HashMap::new(); + + match info.location { + BlockLocation::Branch => (), + BlockLocation::CanonChain => { + block_hashes.insert(info.number, info.hash); + } + BlockLocation::BranchBecomingCanonChain(ref data) => { + let ancestor_number = self + .block_number(&data.ancestor) + .expect("Block number of ancestor is always in DB"); + let start_number = ancestor_number + 1; + + for (index, hash) in data.enacted.iter().cloned().enumerate() { + block_hashes.insert(start_number + index as BlockNumber, hash); + } + + block_hashes.insert(info.number, info.hash); + } + } + + block_hashes + } + + /// This function returns modified block details. + /// Uses the given parent details or attempts to load them from the database. + fn prepare_block_details_update( + &self, + parent_hash: H256, + info: &BlockInfo, + is_finalized: bool, + ) -> HashMap { + // update parent + let mut parent_details = self + .uncommitted_block_details(&parent_hash) + .unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash)); + parent_details.children.push(info.hash); + + // create current block details. + let details = BlockDetails { + number: info.number, + total_difficulty: info.total_difficulty, + parent: parent_hash, + children: vec![], + is_finalized: is_finalized, + }; + + // write to batch + let mut block_details = HashMap::new(); + block_details.insert(parent_hash, parent_details); + block_details.insert(info.hash, details); + block_details + } + + /// This function returns modified block receipts. + fn prepare_block_receipts_update( + &self, + receipts: Vec, + info: &BlockInfo, + ) -> HashMap { + let mut block_receipts = HashMap::new(); + block_receipts.insert(info.hash, BlockReceipts::new(receipts)); + block_receipts + } + + /// This function returns modified transaction addresses. + fn prepare_transaction_addresses_update( + &self, + transaction_hashes: Vec, + info: &BlockInfo, + ) -> HashMap> { + match info.location { + BlockLocation::CanonChain => transaction_hashes + .into_iter() + .enumerate() + .map(|(i, tx_hash)| { + ( + tx_hash, + Some(TransactionAddress { + block_hash: info.hash, + index: i, + }), + ) + }) + .collect(), + BlockLocation::BranchBecomingCanonChain(ref data) => { + let addresses = data.enacted.iter().flat_map(|hash| { + let body = self + .block_body(hash) + .expect("Enacted block must be in database."); + let hashes = body.transaction_hashes(); + hashes + .into_iter() + .enumerate() + .map(|(i, tx_hash)| { + ( + tx_hash, + Some(TransactionAddress { + block_hash: *hash, + index: i, + }), + ) + }) + .collect::>>() + }); + + let current_addresses = + transaction_hashes + .into_iter() + .enumerate() + .map(|(i, tx_hash)| { + ( + tx_hash, + Some(TransactionAddress { + block_hash: info.hash, + index: i, + }), + ) + }); + + let retracted = data.retracted.iter().flat_map(|hash| { + let body = self + .block_body(hash) + .expect("Retracted block must be in database."); + let hashes = body.transaction_hashes(); + hashes + .into_iter() + .map(|hash| (hash, None)) + .collect::>>() + }); + + // The order here is important! Don't remove transaction if it was part of enacted blocks as well. + retracted + .chain(addresses) + .chain(current_addresses) + .collect() + } + BlockLocation::Branch => HashMap::new(), + } + } + + /// This functions returns modified blocks blooms. + /// + /// To accelerate blooms lookups, blomms are stored in multiple + /// layers (BLOOM_LEVELS, currently 3). + /// ChainFilter is responsible for building and rebuilding these layers. + /// It returns them in HashMap, where values are Blooms and + /// keys are BloomIndexes. BloomIndex represents bloom location on one + /// of these layers. + /// + /// To reduce number of queries to databse, block blooms are stored + /// in BlocksBlooms structure which contains info about several + /// (BLOOM_INDEX_SIZE, currently 16) consecutive blocks blooms. + /// + /// Later, BloomIndexer is used to map bloom location on filter layer (BloomIndex) + /// to bloom location in database (BlocksBloomLocation). + /// + fn prepare_block_blooms_update( + &self, + log_bloom: Bloom, + info: &BlockInfo, + ) -> Option<(u64, Vec)> { + match info.location { + BlockLocation::Branch => None, + BlockLocation::CanonChain => { + if log_bloom.is_zero() { + None + } else { + Some((info.number, vec![log_bloom])) + } + } + BlockLocation::BranchBecomingCanonChain(ref data) => { + let ancestor_number = self.block_number(&data.ancestor) .expect("hash belongs to an ancestor of an inserted block; this branch is only reachable for normal block insertion (non-ancient); ancestors of an inserted block are always available for normal block insertion; block number of an inserted block is always available; qed"); - let start_number = ancestor_number + 1; + let start_number = ancestor_number + 1; - let mut blooms: Vec = data.enacted.iter() + let mut blooms: Vec = data.enacted.iter() .map(|hash| self.block_header_data(hash) .expect("hash belongs to an inserted block; block header data of an inserted block is always available; qed")) .map(|h| h.log_bloom()) .collect(); - blooms.push(log_bloom); - Some((start_number, blooms)) - } - } - } - - /// Get best block hash. - pub fn best_block_hash(&self) -> H256 { - self.best_block.read().header.hash() - } - - /// Get best block number. - pub fn best_block_number(&self) -> BlockNumber { - self.best_block.read().header.number() - } - - /// Get best block timestamp. - pub fn best_block_timestamp(&self) -> u64 { - self.best_block.read().header.timestamp() - } - - /// Get best block total difficulty. - pub fn best_block_total_difficulty(&self) -> U256 { - self.best_block.read().total_difficulty - } - - /// Get best block header - pub fn best_block_header(&self) -> Header { - self.best_block.read().header.clone() - } - - /// Get current cache size. - pub fn cache_size(&self) -> CacheSize { - CacheSize { - blocks: self.block_headers.read().heap_size_of_children() + self.block_bodies.read().heap_size_of_children(), - block_details: self.block_details.read().heap_size_of_children(), - transaction_addresses: self.transaction_addresses.read().heap_size_of_children(), - block_receipts: self.block_receipts.read().heap_size_of_children(), - } - } - - /// Ticks our cache system and throws out any old data. - pub fn collect_garbage(&self) { - let current_size = self.cache_size().total(); - - let mut block_headers = self.block_headers.write(); - let mut block_bodies = self.block_bodies.write(); - let mut block_details = self.block_details.write(); - let mut block_hashes = self.block_hashes.write(); - let mut transaction_addresses = self.transaction_addresses.write(); - let mut block_receipts = self.block_receipts.write(); - - let mut cache_man = self.cache_man.lock(); - cache_man.collect_garbage(current_size, | ids | { - for id in &ids { - match *id { - CacheId::BlockHeader(ref h) => { block_headers.remove(h); }, - CacheId::BlockBody(ref h) => { block_bodies.remove(h); }, - CacheId::BlockDetails(ref h) => { block_details.remove(h); } - CacheId::BlockHashes(ref h) => { block_hashes.remove(h); } - CacheId::TransactionAddresses(ref h) => { transaction_addresses.remove(h); } - CacheId::BlockReceipts(ref h) => { block_receipts.remove(h); } - } - } - - block_headers.shrink_to_fit(); - block_bodies.shrink_to_fit(); - block_details.shrink_to_fit(); - block_hashes.shrink_to_fit(); - transaction_addresses.shrink_to_fit(); - block_receipts.shrink_to_fit(); - - block_headers.heap_size_of_children() + - block_bodies.heap_size_of_children() + - block_details.heap_size_of_children() + - block_hashes.heap_size_of_children() + - transaction_addresses.heap_size_of_children() + - block_receipts.heap_size_of_children() - }); - } - - /// Create a block body from a block. - pub fn block_to_body(block: &[u8]) -> Bytes { - let mut body = RlpStream::new_list(2); - let block_view = view!(BlockView, block); - body.append_raw(block_view.transactions_rlp().as_raw(), 1); - body.append_raw(block_view.uncles_rlp().as_raw(), 1); - body.out() - } - - /// Returns general blockchain information - pub fn chain_info(&self) -> BlockChainInfo { - // Make sure to call internal methods first to avoid - // recursive locking of `best_block`. - let first_block_hash = self.first_block(); - let first_block_number = self.first_block_number().into(); - let genesis_hash = self.genesis_hash(); - - // ensure data consistencly by locking everything first - let best_block = self.best_block.read(); - let best_ancient_block = self.best_ancient_block.read(); - BlockChainInfo { - total_difficulty: best_block.total_difficulty, - pending_total_difficulty: best_block.total_difficulty, - genesis_hash, - best_block_hash: best_block.header.hash(), - best_block_number: best_block.header.number(), - best_block_timestamp: best_block.header.timestamp(), - first_block_hash, - first_block_number, - ancient_block_hash: best_ancient_block.as_ref().map(|b| b.hash), - ancient_block_number: best_ancient_block.as_ref().map(|b| b.number), - } - } + blooms.push(log_bloom); + Some((start_number, blooms)) + } + } + } + + /// Get best block hash. + pub fn best_block_hash(&self) -> H256 { + self.best_block.read().header.hash() + } + + /// Get best block number. + pub fn best_block_number(&self) -> BlockNumber { + self.best_block.read().header.number() + } + + /// Get best block timestamp. + pub fn best_block_timestamp(&self) -> u64 { + self.best_block.read().header.timestamp() + } + + /// Get best block total difficulty. + pub fn best_block_total_difficulty(&self) -> U256 { + self.best_block.read().total_difficulty + } + + /// Get best block header + pub fn best_block_header(&self) -> Header { + self.best_block.read().header.clone() + } + + /// Get current cache size. + pub fn cache_size(&self) -> CacheSize { + CacheSize { + blocks: self.block_headers.read().heap_size_of_children() + + self.block_bodies.read().heap_size_of_children(), + block_details: self.block_details.read().heap_size_of_children(), + transaction_addresses: self.transaction_addresses.read().heap_size_of_children(), + block_receipts: self.block_receipts.read().heap_size_of_children(), + } + } + + /// Ticks our cache system and throws out any old data. + pub fn collect_garbage(&self) { + let current_size = self.cache_size().total(); + + let mut block_headers = self.block_headers.write(); + let mut block_bodies = self.block_bodies.write(); + let mut block_details = self.block_details.write(); + let mut block_hashes = self.block_hashes.write(); + let mut transaction_addresses = self.transaction_addresses.write(); + let mut block_receipts = self.block_receipts.write(); + + let mut cache_man = self.cache_man.lock(); + cache_man.collect_garbage(current_size, |ids| { + for id in &ids { + match *id { + CacheId::BlockHeader(ref h) => { + block_headers.remove(h); + } + CacheId::BlockBody(ref h) => { + block_bodies.remove(h); + } + CacheId::BlockDetails(ref h) => { + block_details.remove(h); + } + CacheId::BlockHashes(ref h) => { + block_hashes.remove(h); + } + CacheId::TransactionAddresses(ref h) => { + transaction_addresses.remove(h); + } + CacheId::BlockReceipts(ref h) => { + block_receipts.remove(h); + } + } + } + + block_headers.shrink_to_fit(); + block_bodies.shrink_to_fit(); + block_details.shrink_to_fit(); + block_hashes.shrink_to_fit(); + transaction_addresses.shrink_to_fit(); + block_receipts.shrink_to_fit(); + + block_headers.heap_size_of_children() + + block_bodies.heap_size_of_children() + + block_details.heap_size_of_children() + + block_hashes.heap_size_of_children() + + transaction_addresses.heap_size_of_children() + + block_receipts.heap_size_of_children() + }); + } + + /// Create a block body from a block. + pub fn block_to_body(block: &[u8]) -> Bytes { + let mut body = RlpStream::new_list(2); + let block_view = view!(BlockView, block); + body.append_raw(block_view.transactions_rlp().as_raw(), 1); + body.append_raw(block_view.uncles_rlp().as_raw(), 1); + body.out() + } + + /// Returns general blockchain information + pub fn chain_info(&self) -> BlockChainInfo { + // Make sure to call internal methods first to avoid + // recursive locking of `best_block`. + let first_block_hash = self.first_block(); + let first_block_number = self.first_block_number().into(); + let genesis_hash = self.genesis_hash(); + + // ensure data consistencly by locking everything first + let best_block = self.best_block.read(); + let best_ancient_block = self.best_ancient_block.read(); + BlockChainInfo { + total_difficulty: best_block.total_difficulty, + pending_total_difficulty: best_block.total_difficulty, + genesis_hash, + best_block_hash: best_block.header.hash(), + best_block_number: best_block.header.number(), + best_block_timestamp: best_block.header.timestamp(), + first_block_hash, + first_block_number, + ancient_block_hash: best_ancient_block.as_ref().map(|b| b.hash), + ancient_block_number: best_ancient_block.as_ref().map(|b| b.number), + } + } } #[cfg(test)] mod tests { - use super::*; - - use std::iter; - - use common_types::receipt::{Receipt, TransactionOutcome}; - use common_types::transaction::{Transaction, Action}; - use crate::generator::{BlockGenerator, BlockBuilder, BlockOptions}; - use ethkey::Secret; - use keccak_hash::keccak; - use rustc_hex::FromHex; - use tempdir::TempDir; - - struct TestBlockChainDB { - _blooms_dir: TempDir, - _trace_blooms_dir: TempDir, - blooms: blooms_db::Database, - trace_blooms: blooms_db::Database, - key_value: Arc, - } - - impl BlockChainDB for TestBlockChainDB { - fn key_value(&self) -> &Arc { - &self.key_value - } - - fn blooms(&self) -> &blooms_db::Database { - &self.blooms - } - - fn trace_blooms(&self) -> &blooms_db::Database { - &self.trace_blooms - } - } - - /// Creates new test instance of `BlockChainDB` - pub fn new_db() -> Arc { - let blooms_dir = TempDir::new("").unwrap(); - let trace_blooms_dir = TempDir::new("").unwrap(); - - let db = TestBlockChainDB { - blooms: blooms_db::Database::open(blooms_dir.path()).unwrap(), - trace_blooms: blooms_db::Database::open(trace_blooms_dir.path()).unwrap(), - _blooms_dir: blooms_dir, - _trace_blooms_dir: trace_blooms_dir, - key_value: Arc::new(kvdb_memorydb::create(ethcore_db::NUM_COLUMNS.unwrap())) - }; - - Arc::new(db) - } - - fn new_chain(genesis: encoded::Block, db: Arc) -> BlockChain { - BlockChain::new(Config::default(), genesis.raw(), db) - } - - fn insert_block(db: &Arc, bc: &BlockChain, block: encoded::Block, receipts: Vec) -> ImportRoute { - insert_block_commit(db, bc, block, receipts, true) - } - - fn insert_block_commit(db: &Arc, bc: &BlockChain, block: encoded::Block, receipts: Vec, commit: bool) -> ImportRoute { - let mut batch = db.key_value().transaction(); - let res = insert_block_batch(&mut batch, bc, block, receipts); - db.key_value().write(batch).unwrap(); - if commit { - bc.commit(); - } - res - } - - fn insert_block_batch(batch: &mut DBTransaction, bc: &BlockChain, block: encoded::Block, receipts: Vec) -> ImportRoute { - use crate::ExtrasInsert; - - let fork_choice = { - let header = block.header_view(); - let parent_hash = header.parent_hash(); - let parent_details = bc.block_details(&parent_hash).unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash)); - let block_total_difficulty = parent_details.total_difficulty + header.difficulty(); - if block_total_difficulty > bc.best_block_total_difficulty() { - common_types::engines::ForkChoice::New - } else { - common_types::engines::ForkChoice::Old - } - }; - - bc.insert_block(batch, block, receipts, ExtrasInsert { - fork_choice: fork_choice, - is_finalized: false, - }) - } - - #[test] - fn should_cache_best_block() { - // given - let genesis = BlockBuilder::genesis(); - let first = genesis.add_block(); - - let db = new_db(); - let bc = new_chain(genesis.last().encoded(), db.clone()); - assert_eq!(bc.best_block_number(), 0); - - // when - insert_block_commit(&db, &bc, first.last().encoded(), vec![], false); - assert_eq!(bc.best_block_number(), 0); - bc.commit(); - // NOTE no db.write here (we want to check if best block is cached) - - // then - assert_eq!(bc.best_block_number(), 1); - assert!(bc.block(&bc.best_block_hash()).is_some(), "Best block should be queryable even without DB write."); - } - - #[test] - fn basic_blockchain_insert() { - let genesis = BlockBuilder::genesis(); - let first = genesis.add_block(); - - let genesis = genesis.last(); - let first = first.last(); - let genesis_hash = genesis.hash(); - let first_hash = first.hash(); - - let db = new_db(); - let bc = new_chain(genesis.encoded(), db.clone()); - - assert_eq!(bc.genesis_hash(), genesis_hash); - assert_eq!(bc.best_block_hash(), genesis_hash); - assert_eq!(bc.block_hash(0), Some(genesis_hash)); - assert_eq!(bc.block_hash(1), None); - assert_eq!(bc.block_details(&genesis_hash).unwrap().children, vec![]); - - let mut batch = db.key_value().transaction(); - insert_block_batch(&mut batch, &bc, first.encoded(), vec![]); - db.key_value().write(batch).unwrap(); - bc.commit(); - - assert_eq!(bc.block_hash(0), Some(genesis_hash)); - assert_eq!(bc.best_block_number(), 1); - assert_eq!(bc.best_block_hash(), first_hash); - assert_eq!(bc.block_hash(1), Some(first_hash)); - assert_eq!(bc.block_details(&first_hash).unwrap().parent, genesis_hash); - assert_eq!(bc.block_details(&genesis_hash).unwrap().children, vec![first_hash]); - assert_eq!(bc.block_hash(2), None); - } - - #[test] - fn check_ancestry_iter() { - let genesis = BlockBuilder::genesis(); - let first_10 = genesis.add_blocks(10); - let generator = BlockGenerator::new(vec![first_10]); - - let db = new_db(); - let bc = new_chain(genesis.last().encoded(), db.clone()); - - let mut block_hashes = vec![genesis.last().hash()]; - let mut batch = db.key_value().transaction(); - for block in generator { - block_hashes.push(block.hash()); - insert_block_batch(&mut batch, &bc, block.encoded(), vec![]); - bc.commit(); - } - db.key_value().write(batch).unwrap(); - - block_hashes.reverse(); - - assert_eq!(bc.ancestry_iter(block_hashes[0].clone()).unwrap().collect::>(), block_hashes); - assert_eq!(block_hashes.len(), 11); - } - - #[test] - fn test_find_uncles() { - let genesis = BlockBuilder::genesis(); - let b1a = genesis.add_block(); - let b2a = b1a.add_block(); - let b3a = b2a.add_block(); - let b4a = b3a.add_block(); - let b5a = b4a.add_block(); - - let b1b = genesis.add_block_with_difficulty(9); - let b2b = b1a.add_block_with_difficulty(9); - let b3b = b2a.add_block_with_difficulty(9); - let b4b = b3a.add_block_with_difficulty(9); - let b5b = b4a.add_block_with_difficulty(9); - - let uncle_headers = vec![ - b4b.last().header().encoded(), - b3b.last().header().encoded(), - b2b.last().header().encoded(), - ]; - let b4a_hash = b4a.last().hash(); - - let generator = BlockGenerator::new( - vec![b1a, b1b, b2a, b2b, b3a, b3b, b4a, b4b, b5a, b5b] - ); - - let db = new_db(); - let bc = new_chain(genesis.last().encoded(), db.clone()); - - for b in generator { - insert_block(&db, &bc, b.encoded(), vec![]); - } - - assert_eq!(uncle_headers, bc.find_uncle_headers(&b4a_hash, 3).unwrap()); - // TODO: insert block that already includes one of them as an uncle to check it's not allowed. - } - - fn secret() -> Secret { - keccak("").into() - } - - #[test] - fn test_fork_transaction_addresses() { - let t1 = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Create, - value: 100.into(), - data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&secret(), None); - - let t1_hash = t1.hash(); - - let genesis = BlockBuilder::genesis(); - let b1a = genesis.add_block_with_transactions(iter::once(t1)); - let b1b = genesis.add_block_with_difficulty(9); - let b2 = b1b.add_block(); - - let b1a_hash = b1a.last().hash(); - let b2_hash = b2.last().hash(); - - let db = new_db(); - let bc = new_chain(genesis.last().encoded(), db.clone()); - - let mut batch = db.key_value().transaction(); - let _ = insert_block_batch(&mut batch, &bc, b1a.last().encoded(), vec![]); - bc.commit(); - let _ = insert_block_batch(&mut batch, &bc, b1b.last().encoded(), vec![]); - bc.commit(); - db.key_value().write(batch).unwrap(); - - assert_eq!(bc.best_block_hash(), b1a_hash); - assert_eq!(bc.transaction_address(&t1_hash), Some(TransactionAddress { - block_hash: b1a_hash, - index: 0, - })); - - // now let's make forked chain the canon chain - let mut batch = db.key_value().transaction(); - let _ = insert_block_batch(&mut batch, &bc, b2.last().encoded(), vec![]); - bc.commit(); - db.key_value().write(batch).unwrap(); - - // Transaction should be retracted - assert_eq!(bc.best_block_hash(), b2_hash); - assert_eq!(bc.transaction_address(&t1_hash), None); - } - - #[test] - fn test_overwriting_transaction_addresses() { - let t1 = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Create, - value: 100.into(), - data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&secret(), None); - - let t2 = Transaction { - nonce: 1.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Create, - value: 100.into(), - data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&secret(), None); - - let t3 = Transaction { - nonce: 2.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Create, - value: 100.into(), - data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&secret(), None); - - let genesis = BlockBuilder::genesis(); - let b1a = genesis.add_block_with_transactions(vec![t1.clone(), t2.clone()]); - // insert transactions in different order, - // the block has lower difficulty, so the hash is also different - let b1b = genesis.add_block_with(|| BlockOptions { - difficulty: 9.into(), - transactions: vec![t2.clone(), t1.clone()], - ..Default::default() - }); - let b2 = b1b.add_block_with_transactions(iter::once(t3.clone())); - - let b1a_hash = b1a.last().hash(); - let b1b_hash = b1b.last().hash(); - let b2_hash = b2.last().hash(); - - let t1_hash = t1.hash(); - let t2_hash = t2.hash(); - let t3_hash = t3.hash(); - - let db = new_db(); - let bc = new_chain(genesis.last().encoded(), db.clone()); - - let mut batch = db.key_value().transaction(); - let _ = insert_block_batch(&mut batch, &bc, b1a.last().encoded(), vec![]); - bc.commit(); - let _ = insert_block_batch(&mut batch, &bc, b1b.last().encoded(), vec![]); - bc.commit(); - db.key_value().write(batch).unwrap(); - - assert_eq!(bc.best_block_hash(), b1a_hash); - assert_eq!(bc.transaction_address(&t1_hash), Some(TransactionAddress { - block_hash: b1a_hash, - index: 0, - })); - assert_eq!(bc.transaction_address(&t2_hash), Some(TransactionAddress { - block_hash: b1a_hash, - index: 1, - })); - - // now let's make forked chain the canon chain - let mut batch = db.key_value().transaction(); - let _ = insert_block_batch(&mut batch, &bc, b2.last().encoded(), vec![]); - bc.commit(); - db.key_value().write(batch).unwrap(); - - assert_eq!(bc.best_block_hash(), b2_hash); - assert_eq!(bc.transaction_address(&t1_hash), Some(TransactionAddress { - block_hash: b1b_hash, - index: 1, - })); - assert_eq!(bc.transaction_address(&t2_hash), Some(TransactionAddress { - block_hash: b1b_hash, - index: 0, - })); - assert_eq!(bc.transaction_address(&t3_hash), Some(TransactionAddress { - block_hash: b2_hash, - index: 0, - })); - } - - #[test] - fn test_small_fork() { - let genesis = BlockBuilder::genesis(); - let b1 = genesis.add_block(); - let b2 = b1.add_block(); - let b3a = b2.add_block(); - let b3b = b2.add_block_with_difficulty(9); - - let genesis_hash = genesis.last().hash(); - let b1_hash = b1.last().hash(); - let b2_hash = b2.last().hash(); - let b3a_hash = b3a.last().hash(); - let b3b_hash = b3b.last().hash(); - - // b3a is a part of canon chain, whereas b3b is part of sidechain - let best_block_hash = b3a_hash; - - let db = new_db(); - let bc = new_chain(genesis.last().encoded(), db.clone()); - - let mut batch = db.key_value().transaction(); - let ir1 = insert_block_batch(&mut batch, &bc, b1.last().encoded(), vec![]); - bc.commit(); - let ir2 = insert_block_batch(&mut batch, &bc, b2.last().encoded(), vec![]); - bc.commit(); - let ir3b = insert_block_batch(&mut batch, &bc, b3b.last().encoded(), vec![]); - bc.commit(); - db.key_value().write(batch).unwrap(); - assert_eq!(bc.block_hash(3).unwrap(), b3b_hash); - let mut batch = db.key_value().transaction(); - let ir3a = insert_block_batch(&mut batch, &bc, b3a.last().encoded(), vec![]); - bc.commit(); - db.key_value().write(batch).unwrap(); - - assert_eq!(ir1, ImportRoute { - enacted: vec![b1_hash], - retracted: vec![], - omitted: vec![], - }); - - assert_eq!(ir2, ImportRoute { - enacted: vec![b2_hash], - retracted: vec![], - omitted: vec![], - }); - - assert_eq!(ir3b, ImportRoute { - enacted: vec![b3b_hash], - retracted: vec![], - omitted: vec![], - }); - - assert_eq!(ir3a, ImportRoute { - enacted: vec![b3a_hash], - retracted: vec![b3b_hash], - omitted: vec![], - }); - - assert_eq!(bc.best_block_hash(), best_block_hash); - assert_eq!(bc.block_number(&genesis_hash).unwrap(), 0); - assert_eq!(bc.block_number(&b1_hash).unwrap(), 1); - assert_eq!(bc.block_number(&b2_hash).unwrap(), 2); - assert_eq!(bc.block_number(&b3a_hash).unwrap(), 3); - assert_eq!(bc.block_number(&b3b_hash).unwrap(), 3); - - assert_eq!(bc.block_hash(0).unwrap(), genesis_hash); - assert_eq!(bc.block_hash(1).unwrap(), b1_hash); - assert_eq!(bc.block_hash(2).unwrap(), b2_hash); - assert_eq!(bc.block_hash(3).unwrap(), b3a_hash); - - // test trie route - let r0_1 = bc.tree_route(genesis_hash, b1_hash).unwrap(); - assert_eq!(r0_1.ancestor, genesis_hash); - assert_eq!(r0_1.blocks, [b1_hash]); - assert_eq!(r0_1.index, 0); - - let r0_2 = bc.tree_route(genesis_hash, b2_hash).unwrap(); - assert_eq!(r0_2.ancestor, genesis_hash); - assert_eq!(r0_2.blocks, [b1_hash, b2_hash]); - assert_eq!(r0_2.index, 0); - - let r1_3a = bc.tree_route(b1_hash, b3a_hash).unwrap(); - assert_eq!(r1_3a.ancestor, b1_hash); - assert_eq!(r1_3a.blocks, [b2_hash, b3a_hash]); - assert_eq!(r1_3a.index, 0); - - let r1_3b = bc.tree_route(b1_hash, b3b_hash).unwrap(); - assert_eq!(r1_3b.ancestor, b1_hash); - assert_eq!(r1_3b.blocks, [b2_hash, b3b_hash]); - assert_eq!(r1_3b.index, 0); - - let r3a_3b = bc.tree_route(b3a_hash, b3b_hash).unwrap(); - assert_eq!(r3a_3b.ancestor, b2_hash); - assert_eq!(r3a_3b.blocks, [b3a_hash, b3b_hash]); - assert_eq!(r3a_3b.index, 1); - - let r1_0 = bc.tree_route(b1_hash, genesis_hash).unwrap(); - assert_eq!(r1_0.ancestor, genesis_hash); - assert_eq!(r1_0.blocks, [b1_hash]); - assert_eq!(r1_0.index, 1); - - let r2_0 = bc.tree_route(b2_hash, genesis_hash).unwrap(); - assert_eq!(r2_0.ancestor, genesis_hash); - assert_eq!(r2_0.blocks, [b2_hash, b1_hash]); - assert_eq!(r2_0.index, 2); - - let r3a_1 = bc.tree_route(b3a_hash, b1_hash).unwrap(); - assert_eq!(r3a_1.ancestor, b1_hash); - assert_eq!(r3a_1.blocks, [b3a_hash, b2_hash]); - assert_eq!(r3a_1.index, 2); - - let r3b_1 = bc.tree_route(b3b_hash, b1_hash).unwrap(); - assert_eq!(r3b_1.ancestor, b1_hash); - assert_eq!(r3b_1.blocks, [b3b_hash, b2_hash]); - assert_eq!(r3b_1.index, 2); - - let r3b_3a = bc.tree_route(b3b_hash, b3a_hash).unwrap(); - assert_eq!(r3b_3a.ancestor, b2_hash); - assert_eq!(r3b_3a.blocks, [b3b_hash, b3a_hash]); - assert_eq!(r3b_3a.index, 1); - } - - #[test] - fn test_reopen_blockchain_db() { - let genesis = BlockBuilder::genesis(); - let first = genesis.add_block(); - let genesis_hash = genesis.last().hash(); - let first_hash = first.last().hash(); - - let db = new_db(); - - { - let bc = new_chain(genesis.last().encoded(), db.clone()); - assert_eq!(bc.best_block_hash(), genesis_hash); - let mut batch = db.key_value().transaction(); - insert_block_batch(&mut batch, &bc, first.last().encoded(), vec![]); - db.key_value().write(batch).unwrap(); - bc.commit(); - assert_eq!(bc.best_block_hash(), first_hash); - } - - { - let bc = new_chain(genesis.last().encoded(), db.clone()); - - assert_eq!(bc.best_block_hash(), first_hash); - } - } - - #[test] - fn find_transaction_by_hash() { - let genesis = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0af81e09f8c46ca322193edfda764fa7e88e81923f802f1d325ec0b0308ac2cd0a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200008083023e38808454c98c8142a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421880102030405060708c0c0".from_hex().unwrap(); - let b1 = "f904a8f901faa0ce1f26f798dd03c8782d63b3e42e79a64eaea5694ea686ac5d7ce3df5171d1aea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0a65c2364cd0f1542d761823dc0109c6b072f14c20459598c5455c274601438f4a070616ebd7ad2ed6fb7860cf7e9df00163842351c38a87cac2c1cb193895035a2a05c5b4fc43c2d45787f54e1ae7d27afdb4ad16dfc567c5692070d5c4556e0b1d7b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200000183023ec683021536845685109780a029f07836e4e59229b3a065913afc27702642c683bba689910b2b2fd45db310d3888957e6d004a31802f902a7f85f800a8255f094aaaf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca0575da4e21b66fa764be5f74da9389e67693d066fb0d1312e19e17e501da00ecda06baf5a5327595f6619dfc2fcb3f2e6fb410b5810af3cb52d0e7508038e91a188f85f010a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba04fa966bf34b93abc1bcd665554b7f316b50f928477b50be0f3285ead29d18c5ba017bba0eeec1625ab433746955e125d46d80b7fdc97386c51266f842d8e02192ef85f020a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca004377418ae981cc32b1312b4a427a1d69a821b28db8584f5f2bd8c6d42458adaa053a1dba1af177fac92f3b6af0a9fa46a22adf56e686c93794b6a012bf254abf5f85f030a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca04fe13febd28a05f4fcb2f451d7ddc2dda56486d9f8c79a62b0ba4da775122615a0651b2382dd402df9ebc27f8cb4b2e0f3cea68dda2dca0ee9603608f0b6f51668f85f040a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba078e6a0ba086a08f8450e208a399bb2f2d2a0d984acd2517c7c7df66ccfab567da013254002cd45a97fac049ae00afbc43ed0d9961d0c56a3b2382c80ce41c198ddf85f050a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba0a7174d8f43ea71c8e3ca9477691add8d80ac8e0ed89d8d8b572041eef81f4a54a0534ea2e28ec4da3b5b944b18c51ec84a5cf35f5b3343c5fb86521fd2d388f506f85f060a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba034bd04065833536a10c77ee2a43a5371bc6d34837088b861dd9d4b7f44074b59a078807715786a13876d3455716a6b9cb2186b7a4887a5c31160fc877454958616c0".from_hex().unwrap(); - let b1_hash: H256 = "f53f268d23a71e85c7d6d83a9504298712b84c1a2ba220441c86eeda0bf0b6e3".into(); - - let db = new_db(); - let bc = new_chain(encoded::Block::new(genesis), db.clone()); - let mut batch = db.key_value().transaction(); - insert_block_batch(&mut batch, &bc, encoded::Block::new(b1), vec![]); - db.key_value().write(batch).unwrap(); - bc.commit(); - - let transactions = bc.transactions(&b1_hash).unwrap(); - assert_eq!(transactions.len(), 7); - for t in transactions { - assert_eq!(bc.transaction(&bc.transaction_address(&t.hash()).unwrap()).unwrap(), t); - } - } - - #[test] - fn test_logs() { - let t1 = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Create, - value: 101.into(), - data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&secret(), None); - let t2 = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Create, - value: 102.into(), - data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&secret(), None); - let t3 = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Create, - value: 103.into(), - data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&secret(), None); - let t4 = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Create, - value: 104.into(), - data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&secret(), None); - let tx_hash1 = t1.hash(); - let tx_hash2 = t2.hash(); - let tx_hash3 = t3.hash(); - let tx_hash4 = t4.hash(); - - let genesis = BlockBuilder::genesis(); - let b1 = genesis.add_block_with_transactions(vec![t1, t2]); - let b2 = b1.add_block_with_transactions(iter::once(t3)); - let b3 = genesis.add_block_with(|| BlockOptions { - transactions: vec![t4.clone()], - difficulty: U256::from(9), - ..Default::default() - }); // Branch block - let b1_hash = b1.last().hash(); - let b1_number = b1.last().number(); - let b2_hash = b2.last().hash(); - let b2_number = b2.last().number(); - let b3_hash = b3.last().hash(); - let b3_number = b3.last().number(); - - let db = new_db(); - let bc = new_chain(genesis.last().encoded(), db.clone()); - insert_block(&db, &bc, b1.last().encoded(), vec![Receipt { - outcome: TransactionOutcome::StateRoot(H256::default()), - gas_used: 10_000.into(), - log_bloom: Default::default(), - logs: vec![ - LogEntry { address: Default::default(), topics: vec![], data: vec![1], }, - LogEntry { address: Default::default(), topics: vec![], data: vec![2], }, - ], - }, - Receipt { - outcome: TransactionOutcome::StateRoot(H256::default()), - gas_used: 10_000.into(), - log_bloom: Default::default(), - logs: vec![ - LogEntry { address: Default::default(), topics: vec![], data: vec![3], }, - ], - }]); - insert_block(&db, &bc, b2.last().encoded(), vec![ - Receipt { - outcome: TransactionOutcome::StateRoot(H256::default()), - gas_used: 10_000.into(), - log_bloom: Default::default(), - logs: vec![ - LogEntry { address: Default::default(), topics: vec![], data: vec![4], }, - ], - } - ]); - insert_block(&db, &bc, b3.last().encoded(), vec![ - Receipt { - outcome: TransactionOutcome::StateRoot(H256::default()), - gas_used: 10_000.into(), - log_bloom: Default::default(), - logs: vec![ - LogEntry { address: Default::default(), topics: vec![], data: vec![5], }, - ], - } - ]); - - // when - let logs1 = bc.logs(vec![b1_hash, b2_hash], |_| true, None); - let logs2 = bc.logs(vec![b1_hash, b2_hash], |_| true, Some(1)); - let logs3 = bc.logs(vec![b3_hash], |_| true, None); - - // then - assert_eq!(logs1, vec![ - LocalizedLogEntry { - entry: LogEntry { address: Default::default(), topics: vec![], data: vec![1] }, - block_hash: b1_hash, - block_number: b1_number, - transaction_hash: tx_hash1, - transaction_index: 0, - transaction_log_index: 0, - log_index: 0, - }, - LocalizedLogEntry { - entry: LogEntry { address: Default::default(), topics: vec![], data: vec![2] }, - block_hash: b1_hash, - block_number: b1_number, - transaction_hash: tx_hash1, - transaction_index: 0, - transaction_log_index: 1, - log_index: 1, - }, - LocalizedLogEntry { - entry: LogEntry { address: Default::default(), topics: vec![], data: vec![3] }, - block_hash: b1_hash, - block_number: b1_number, - transaction_hash: tx_hash2, - transaction_index: 1, - transaction_log_index: 0, - log_index: 2, - }, - LocalizedLogEntry { - entry: LogEntry { address: Default::default(), topics: vec![], data: vec![4] }, - block_hash: b2_hash, - block_number: b2_number, - transaction_hash: tx_hash3, - transaction_index: 0, - transaction_log_index: 0, - log_index: 0, - } - ]); - assert_eq!(logs2, vec![ - LocalizedLogEntry { - entry: LogEntry { address: Default::default(), topics: vec![], data: vec![4] }, - block_hash: b2_hash, - block_number: b2_number, - transaction_hash: tx_hash3, - transaction_index: 0, - transaction_log_index: 0, - log_index: 0, - } - ]); - assert_eq!(logs3, vec![ - LocalizedLogEntry { - entry: LogEntry { address: Default::default(), topics: vec![], data: vec![5] }, - block_hash: b3_hash, - block_number: b3_number, - transaction_hash: tx_hash4, - transaction_index: 0, - transaction_log_index: 0, - log_index: 0, - } - ]); - } - - #[test] - fn test_bloom_filter_simple() { - let bloom_b1: Bloom = "00000020000000000000000000000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000400000000000000000000002000".into(); - - let bloom_b2: Bloom = "00000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(); - - let bloom_ba: Bloom = "00000000000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(); - - let genesis = BlockBuilder::genesis(); - let b1 = genesis.add_block_with(|| BlockOptions { - bloom: bloom_b1.clone(), - difficulty: 9.into(), - ..Default::default() - }); - let b2 = b1.add_block_with_bloom(bloom_b2); - let b3 = b2.add_block_with_bloom(bloom_ba); - - let b1a = genesis.add_block_with_bloom(bloom_ba); - let b2a = b1a.add_block_with_bloom(bloom_ba); - - let db = new_db(); - let bc = new_chain(genesis.last().encoded(), db.clone()); - - let blocks_b1 = bc.blocks_with_bloom(Some(&bloom_b1), 0, 5); - let blocks_b2 = bc.blocks_with_bloom(Some(&bloom_b2), 0, 5); - assert!(blocks_b1.is_empty()); - assert!(blocks_b2.is_empty()); - - insert_block(&db, &bc, b1.last().encoded(), vec![]); - let blocks_b1 = bc.blocks_with_bloom(Some(&bloom_b1), 0, 5); - let blocks_b2 = bc.blocks_with_bloom(Some(&bloom_b2), 0, 5); - assert_eq!(blocks_b1, vec![1]); - assert!(blocks_b2.is_empty()); - - insert_block(&db, &bc, b2.last().encoded(), vec![]); - let blocks_b1 = bc.blocks_with_bloom(Some(&bloom_b1), 0, 5); - let blocks_b2 = bc.blocks_with_bloom(Some(&bloom_b2), 0, 5); - assert_eq!(blocks_b1, vec![1]); - assert_eq!(blocks_b2, vec![2]); - - // hasn't been forked yet - insert_block(&db, &bc, b1a.last().encoded(), vec![]); - let blocks_b1 = bc.blocks_with_bloom(Some(&bloom_b1), 0, 5); - let blocks_b2 = bc.blocks_with_bloom(Some(&bloom_b2), 0, 5); - let blocks_ba = bc.blocks_with_bloom(Some(&bloom_ba), 0, 5); - assert_eq!(blocks_b1, vec![1]); - assert_eq!(blocks_b2, vec![2]); - assert!(blocks_ba.is_empty()); - - // fork has happend - insert_block(&db, &bc, b2a.last().encoded(), vec![]); - let blocks_b1 = bc.blocks_with_bloom(Some(&bloom_b1), 0, 5); - let blocks_b2 = bc.blocks_with_bloom(Some(&bloom_b2), 0, 5); - let blocks_ba = bc.blocks_with_bloom(Some(&bloom_ba), 0, 5); - assert!(blocks_b1.is_empty()); - assert!(blocks_b2.is_empty()); - assert_eq!(blocks_ba, vec![1, 2]); - - // fork back - insert_block(&db, &bc, b3.last().encoded(), vec![]); - let blocks_b1 = bc.blocks_with_bloom(Some(&bloom_b1), 0, 5); - let blocks_b2 = bc.blocks_with_bloom(Some(&bloom_b2), 0, 5); - let blocks_ba = bc.blocks_with_bloom(Some(&bloom_ba), 0, 5); - assert_eq!(blocks_b1, vec![1]); - assert_eq!(blocks_b2, vec![2]); - assert_eq!(blocks_ba, vec![3]); - } - - #[test] - fn test_insert_unordered() { - let bloom_b1: Bloom = "00000020000000000000000000000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000400000000000000000000002000".into(); - - let bloom_b2: Bloom = "00000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(); - - let bloom_b3: Bloom = "00000000000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(); - - let genesis = BlockBuilder::genesis(); - let b1 = genesis.add_block_with_bloom(bloom_b1); - let b2 = b1.add_block_with_bloom(bloom_b2); - let b3 = b2.add_block_with_bloom(bloom_b3); - let b1_total_difficulty = genesis.last().difficulty() + b1.last().difficulty(); - - let db = new_db(); - let bc = new_chain(genesis.last().encoded(), db.clone()); - let mut batch = db.key_value().transaction(); - bc.insert_unordered_block(&mut batch, b2.last().encoded(), vec![], Some(b1_total_difficulty), false, false); - bc.commit(); - bc.insert_unordered_block(&mut batch, b3.last().encoded(), vec![], None, true, false); - bc.commit(); - bc.insert_unordered_block(&mut batch, b1.last().encoded(), vec![], None, false, false); - bc.commit(); - db.key_value().write(batch).unwrap(); - - assert_eq!(bc.best_block_hash(), b3.last().hash()); - assert_eq!(bc.block_hash(1).unwrap(), b1.last().hash()); - assert_eq!(bc.block_hash(2).unwrap(), b2.last().hash()); - assert_eq!(bc.block_hash(3).unwrap(), b3.last().hash()); - - let blocks_b1 = bc.blocks_with_bloom(Some(&bloom_b1), 0, 3); - let blocks_b2 = bc.blocks_with_bloom(Some(&bloom_b2), 0, 3); - let blocks_b3 = bc.blocks_with_bloom(Some(&bloom_b3), 0, 3); - - assert_eq!(blocks_b1, vec![1]); - assert_eq!(blocks_b2, vec![2]); - assert_eq!(blocks_b3, vec![3]); - } - - #[test] - fn test_best_block_update() { - let genesis = BlockBuilder::genesis(); - let next_5 = genesis.add_blocks(5); - let uncle = genesis.add_block_with_difficulty(9); - let generator = BlockGenerator::new(iter::once(next_5)); - - let db = new_db(); - { - let bc = new_chain(genesis.last().encoded(), db.clone()); - - let mut batch = db.key_value().transaction(); - // create a longer fork - for block in generator { - insert_block_batch(&mut batch, &bc, block.encoded(), vec![]); - bc.commit(); - } - - assert_eq!(bc.best_block_number(), 5); - insert_block_batch(&mut batch, &bc, uncle.last().encoded(), vec![]); - db.key_value().write(batch).unwrap(); - bc.commit(); - } - - // re-loading the blockchain should load the correct best block. - let bc = new_chain(genesis.last().encoded(), db); - assert_eq!(bc.best_block_number(), 5); - } - - #[test] - fn epoch_transitions_iter() { - use common_types::engines::epoch::Transition as EpochTransition; - - let genesis = BlockBuilder::genesis(); - let next_5 = genesis.add_blocks(5); - let uncle = genesis.add_block_with_difficulty(9); - let generator = BlockGenerator::new(iter::once(next_5)); - - let db = new_db(); - { - let bc = new_chain(genesis.last().encoded(), db.clone()); - - let mut batch = db.key_value().transaction(); - // create a longer fork - for (i, block) in generator.into_iter().enumerate() { - - insert_block_batch(&mut batch, &bc, block.encoded(), vec![]); - bc.insert_epoch_transition(&mut batch, i as u64, EpochTransition { - block_hash: block.hash(), - block_number: i as u64 + 1, - proof: vec![], - }); - bc.commit(); - } - - assert_eq!(bc.best_block_number(), 5); - - insert_block_batch(&mut batch, &bc, uncle.last().encoded(), vec![]); - bc.insert_epoch_transition(&mut batch, 999, EpochTransition { - block_hash: uncle.last().hash(), - block_number: 1, - proof: vec![], - }); - - db.key_value().write(batch).unwrap(); - bc.commit(); - - // epoch 999 not in canonical chain. - assert_eq!(bc.epoch_transitions().map(|(i, _)| i).collect::>(), vec![0, 1, 2, 3, 4]); - } - - // re-loading the blockchain should load the correct best block. - let bc = new_chain(genesis.last().encoded(), db); - - assert_eq!(bc.best_block_number(), 5); - assert_eq!(bc.epoch_transitions().map(|(i, _)| i).collect::>(), vec![0, 1, 2, 3, 4]); - } - - #[test] - fn epoch_transition_for() { - use common_types::engines::epoch::Transition as EpochTransition; - - let genesis = BlockBuilder::genesis(); - let fork_7 = genesis.add_blocks_with(7, || BlockOptions { - difficulty: 9.into(), - ..Default::default() - }); - let next_10 = genesis.add_blocks(10); - let fork_generator = BlockGenerator::new(iter::once(fork_7)); - let next_generator = BlockGenerator::new(iter::once(next_10)); - - let db = new_db(); - - let bc = new_chain(genesis.last().encoded(), db.clone()); - - let mut batch = db.key_value().transaction(); - bc.insert_epoch_transition(&mut batch, 0, EpochTransition { - block_hash: bc.genesis_hash(), - block_number: 0, - proof: vec![], - }); - db.key_value().write(batch).unwrap(); - - // set up a chain where we have a canonical chain of 10 blocks - // and a non-canonical fork of 8 from genesis. - let fork_hash = { - for block in fork_generator { - insert_block(&db, &bc, block.encoded(), vec![]); - } - - assert_eq!(bc.best_block_number(), 7); - bc.chain_info().best_block_hash - }; - - for block in next_generator { - insert_block(&db, &bc, block.encoded(), vec![]); - } - - assert_eq!(bc.best_block_number(), 10); - - let mut batch = db.key_value().transaction(); - bc.insert_epoch_transition(&mut batch, 4, EpochTransition { - block_hash: bc.block_hash(4).unwrap(), - block_number: 4, - proof: vec![], - }); - db.key_value().write(batch).unwrap(); - - // blocks where the parent is one of the first 4 will be part of genesis epoch. - for i in 0..4 { - let hash = bc.block_hash(i).unwrap(); - assert_eq!(bc.epoch_transition_for(hash).unwrap().block_number, 0); - } - - // blocks where the parent is the transition at 4 or after will be - // part of that epoch. - for i in 4..11 { - let hash = bc.block_hash(i).unwrap(); - assert_eq!(bc.epoch_transition_for(hash).unwrap().block_number, 4); - } - - let fork_hashes = bc.ancestry_iter(fork_hash).unwrap().collect::>(); - assert_eq!(fork_hashes.len(), 8); - - // non-canonical fork blocks should all have genesis transition - for fork_hash in fork_hashes { - assert_eq!(bc.epoch_transition_for(fork_hash).unwrap().block_number, 0); - } - } + use super::*; + + use std::iter; + + use crate::generator::{BlockBuilder, BlockGenerator, BlockOptions}; + use common_types::{ + receipt::{Receipt, TransactionOutcome}, + transaction::{Action, Transaction}, + }; + use ethkey::Secret; + use keccak_hash::keccak; + use rustc_hex::FromHex; + use tempdir::TempDir; + + struct TestBlockChainDB { + _blooms_dir: TempDir, + _trace_blooms_dir: TempDir, + blooms: blooms_db::Database, + trace_blooms: blooms_db::Database, + key_value: Arc, + } + + impl BlockChainDB for TestBlockChainDB { + fn key_value(&self) -> &Arc { + &self.key_value + } + + fn blooms(&self) -> &blooms_db::Database { + &self.blooms + } + + fn trace_blooms(&self) -> &blooms_db::Database { + &self.trace_blooms + } + } + + /// Creates new test instance of `BlockChainDB` + pub fn new_db() -> Arc { + let blooms_dir = TempDir::new("").unwrap(); + let trace_blooms_dir = TempDir::new("").unwrap(); + + let db = TestBlockChainDB { + blooms: blooms_db::Database::open(blooms_dir.path()).unwrap(), + trace_blooms: blooms_db::Database::open(trace_blooms_dir.path()).unwrap(), + _blooms_dir: blooms_dir, + _trace_blooms_dir: trace_blooms_dir, + key_value: Arc::new(kvdb_memorydb::create(ethcore_db::NUM_COLUMNS.unwrap())), + }; + + Arc::new(db) + } + + fn new_chain(genesis: encoded::Block, db: Arc) -> BlockChain { + BlockChain::new(Config::default(), genesis.raw(), db) + } + + fn insert_block( + db: &Arc, + bc: &BlockChain, + block: encoded::Block, + receipts: Vec, + ) -> ImportRoute { + insert_block_commit(db, bc, block, receipts, true) + } + + fn insert_block_commit( + db: &Arc, + bc: &BlockChain, + block: encoded::Block, + receipts: Vec, + commit: bool, + ) -> ImportRoute { + let mut batch = db.key_value().transaction(); + let res = insert_block_batch(&mut batch, bc, block, receipts); + db.key_value().write(batch).unwrap(); + if commit { + bc.commit(); + } + res + } + + fn insert_block_batch( + batch: &mut DBTransaction, + bc: &BlockChain, + block: encoded::Block, + receipts: Vec, + ) -> ImportRoute { + let fork_choice = { + let header = block.header_view(); + let parent_hash = header.parent_hash(); + let parent_details = bc + .uncommitted_block_details(&parent_hash) + .unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash)); + let block_total_difficulty = parent_details.total_difficulty + header.difficulty(); + if block_total_difficulty > bc.best_block_total_difficulty() { + common_types::engines::ForkChoice::New + } else { + common_types::engines::ForkChoice::Old + } + }; + + bc.insert_block( + batch, + block, + receipts, + ExtrasInsert { + fork_choice: fork_choice, + is_finalized: false, + }, + ) + } + + #[test] + fn should_cache_best_block() { + // given + let genesis = BlockBuilder::genesis(); + let first = genesis.add_block(); + + let db = new_db(); + let bc = new_chain(genesis.last().encoded(), db.clone()); + assert_eq!(bc.best_block_number(), 0); + + // when + insert_block_commit(&db, &bc, first.last().encoded(), vec![], false); + assert_eq!(bc.best_block_number(), 0); + bc.commit(); + // NOTE no db.write here (we want to check if best block is cached) + + // then + assert_eq!(bc.best_block_number(), 1); + assert!( + bc.block(&bc.best_block_hash()).is_some(), + "Best block should be queryable even without DB write." + ); + } + + #[test] + fn basic_blockchain_insert() { + let genesis = BlockBuilder::genesis(); + let first = genesis.add_block(); + + let genesis = genesis.last(); + let first = first.last(); + let genesis_hash = genesis.hash(); + let first_hash = first.hash(); + + let db = new_db(); + let bc = new_chain(genesis.encoded(), db.clone()); + + assert_eq!(bc.genesis_hash(), genesis_hash); + assert_eq!(bc.best_block_hash(), genesis_hash); + assert_eq!(bc.block_hash(0), Some(genesis_hash)); + assert_eq!(bc.block_hash(1), None); + assert_eq!(bc.block_details(&genesis_hash).unwrap().children, vec![]); + + let mut batch = db.key_value().transaction(); + insert_block_batch(&mut batch, &bc, first.encoded(), vec![]); + db.key_value().write(batch).unwrap(); + bc.commit(); + + assert_eq!(bc.block_hash(0), Some(genesis_hash)); + assert_eq!(bc.best_block_number(), 1); + assert_eq!(bc.best_block_hash(), first_hash); + assert_eq!(bc.block_hash(1), Some(first_hash)); + assert_eq!(bc.block_details(&first_hash).unwrap().parent, genesis_hash); + assert_eq!( + bc.block_details(&genesis_hash).unwrap().children, + vec![first_hash] + ); + assert_eq!(bc.block_hash(2), None); + } + + #[test] + fn check_ancestry_iter() { + let genesis = BlockBuilder::genesis(); + let first_10 = genesis.add_blocks(10); + let generator = BlockGenerator::new(vec![first_10]); + + let db = new_db(); + let bc = new_chain(genesis.last().encoded(), db.clone()); + + let mut block_hashes = vec![genesis.last().hash()]; + let mut batch = db.key_value().transaction(); + for block in generator { + block_hashes.push(block.hash()); + insert_block_batch(&mut batch, &bc, block.encoded(), vec![]); + bc.commit(); + } + db.key_value().write(batch).unwrap(); + + block_hashes.reverse(); + + assert_eq!( + bc.ancestry_iter(block_hashes[0].clone()) + .unwrap() + .collect::>(), + block_hashes + ); + assert_eq!(block_hashes.len(), 11); + } + + #[test] + fn test_find_uncles() { + let genesis = BlockBuilder::genesis(); + let b1a = genesis.add_block(); + let b2a = b1a.add_block(); + let b3a = b2a.add_block(); + let b4a = b3a.add_block(); + let b5a = b4a.add_block(); + + let b1b = genesis.add_block_with_difficulty(9); + let b2b = b1a.add_block_with_difficulty(9); + let b3b = b2a.add_block_with_difficulty(9); + let b4b = b3a.add_block_with_difficulty(9); + let b5b = b4a.add_block_with_difficulty(9); + + let uncle_headers = vec![ + b4b.last().header().encoded(), + b3b.last().header().encoded(), + b2b.last().header().encoded(), + ]; + let b4a_hash = b4a.last().hash(); + + let generator = BlockGenerator::new(vec![b1a, b1b, b2a, b2b, b3a, b3b, b4a, b4b, b5a, b5b]); + + let db = new_db(); + let bc = new_chain(genesis.last().encoded(), db.clone()); + + for b in generator { + insert_block(&db, &bc, b.encoded(), vec![]); + } + + assert_eq!(uncle_headers, bc.find_uncle_headers(&b4a_hash, 3).unwrap()); + // TODO: insert block that already includes one of them as an uncle to check it's not allowed. + } + + fn secret() -> Secret { + keccak("").into() + } + + #[test] + fn test_fork_transaction_addresses() { + let t1 = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 100.into(), + data: "601080600c6000396000f3006000355415600957005b60203560003555" + .from_hex() + .unwrap(), + } + .sign(&secret(), None); + + let t1_hash = t1.hash(); + + let genesis = BlockBuilder::genesis(); + let b1a = genesis.add_block_with_transactions(iter::once(t1)); + let b1b = genesis.add_block_with_difficulty(9); + let b2 = b1b.add_block(); + + let b1a_hash = b1a.last().hash(); + let b2_hash = b2.last().hash(); + + let db = new_db(); + let bc = new_chain(genesis.last().encoded(), db.clone()); + + let mut batch = db.key_value().transaction(); + let _ = insert_block_batch(&mut batch, &bc, b1a.last().encoded(), vec![]); + bc.commit(); + let _ = insert_block_batch(&mut batch, &bc, b1b.last().encoded(), vec![]); + bc.commit(); + db.key_value().write(batch).unwrap(); + + assert_eq!(bc.best_block_hash(), b1a_hash); + assert_eq!( + bc.transaction_address(&t1_hash), + Some(TransactionAddress { + block_hash: b1a_hash, + index: 0, + }) + ); + + // now let's make forked chain the canon chain + let mut batch = db.key_value().transaction(); + let _ = insert_block_batch(&mut batch, &bc, b2.last().encoded(), vec![]); + bc.commit(); + db.key_value().write(batch).unwrap(); + + // Transaction should be retracted + assert_eq!(bc.best_block_hash(), b2_hash); + assert_eq!(bc.transaction_address(&t1_hash), None); + } + + #[test] + fn test_overwriting_transaction_addresses() { + let t1 = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 100.into(), + data: "601080600c6000396000f3006000355415600957005b60203560003555" + .from_hex() + .unwrap(), + } + .sign(&secret(), None); + + let t2 = Transaction { + nonce: 1.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 100.into(), + data: "601080600c6000396000f3006000355415600957005b60203560003555" + .from_hex() + .unwrap(), + } + .sign(&secret(), None); + + let t3 = Transaction { + nonce: 2.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 100.into(), + data: "601080600c6000396000f3006000355415600957005b60203560003555" + .from_hex() + .unwrap(), + } + .sign(&secret(), None); + + let genesis = BlockBuilder::genesis(); + let b1a = genesis.add_block_with_transactions(vec![t1.clone(), t2.clone()]); + // insert transactions in different order, + // the block has lower difficulty, so the hash is also different + let b1b = genesis.add_block_with(|| BlockOptions { + difficulty: 9.into(), + transactions: vec![t2.clone(), t1.clone()], + ..Default::default() + }); + let b2 = b1b.add_block_with_transactions(iter::once(t3.clone())); + + let b1a_hash = b1a.last().hash(); + let b1b_hash = b1b.last().hash(); + let b2_hash = b2.last().hash(); + + let t1_hash = t1.hash(); + let t2_hash = t2.hash(); + let t3_hash = t3.hash(); + + let db = new_db(); + let bc = new_chain(genesis.last().encoded(), db.clone()); + + let mut batch = db.key_value().transaction(); + let _ = insert_block_batch(&mut batch, &bc, b1a.last().encoded(), vec![]); + bc.commit(); + let _ = insert_block_batch(&mut batch, &bc, b1b.last().encoded(), vec![]); + bc.commit(); + db.key_value().write(batch).unwrap(); + + assert_eq!(bc.best_block_hash(), b1a_hash); + assert_eq!( + bc.transaction_address(&t1_hash), + Some(TransactionAddress { + block_hash: b1a_hash, + index: 0, + }) + ); + assert_eq!( + bc.transaction_address(&t2_hash), + Some(TransactionAddress { + block_hash: b1a_hash, + index: 1, + }) + ); + + // now let's make forked chain the canon chain + let mut batch = db.key_value().transaction(); + let _ = insert_block_batch(&mut batch, &bc, b2.last().encoded(), vec![]); + bc.commit(); + db.key_value().write(batch).unwrap(); + + assert_eq!(bc.best_block_hash(), b2_hash); + assert_eq!( + bc.transaction_address(&t1_hash), + Some(TransactionAddress { + block_hash: b1b_hash, + index: 1, + }) + ); + assert_eq!( + bc.transaction_address(&t2_hash), + Some(TransactionAddress { + block_hash: b1b_hash, + index: 0, + }) + ); + assert_eq!( + bc.transaction_address(&t3_hash), + Some(TransactionAddress { + block_hash: b2_hash, + index: 0, + }) + ); + } + + #[test] + fn test_small_fork() { + let genesis = BlockBuilder::genesis(); + let b1 = genesis.add_block(); + let b2 = b1.add_block(); + let b3a = b2.add_block(); + let b3b = b2.add_block_with_difficulty(9); + + let genesis_hash = genesis.last().hash(); + let b1_hash = b1.last().hash(); + let b2_hash = b2.last().hash(); + let b3a_hash = b3a.last().hash(); + let b3b_hash = b3b.last().hash(); + + // b3a is a part of canon chain, whereas b3b is part of sidechain + let best_block_hash = b3a_hash; + + let db = new_db(); + let bc = new_chain(genesis.last().encoded(), db.clone()); + + let mut batch = db.key_value().transaction(); + let ir1 = insert_block_batch(&mut batch, &bc, b1.last().encoded(), vec![]); + bc.commit(); + let ir2 = insert_block_batch(&mut batch, &bc, b2.last().encoded(), vec![]); + bc.commit(); + let ir3b = insert_block_batch(&mut batch, &bc, b3b.last().encoded(), vec![]); + bc.commit(); + db.key_value().write(batch).unwrap(); + assert_eq!(bc.block_hash(3).unwrap(), b3b_hash); + let mut batch = db.key_value().transaction(); + let ir3a = insert_block_batch(&mut batch, &bc, b3a.last().encoded(), vec![]); + bc.commit(); + db.key_value().write(batch).unwrap(); + + assert_eq!( + ir1, + ImportRoute { + enacted: vec![b1_hash], + retracted: vec![], + omitted: vec![], + } + ); + + assert_eq!( + ir2, + ImportRoute { + enacted: vec![b2_hash], + retracted: vec![], + omitted: vec![], + } + ); + + assert_eq!( + ir3b, + ImportRoute { + enacted: vec![b3b_hash], + retracted: vec![], + omitted: vec![], + } + ); + + assert_eq!( + ir3a, + ImportRoute { + enacted: vec![b3a_hash], + retracted: vec![b3b_hash], + omitted: vec![], + } + ); + + assert_eq!(bc.best_block_hash(), best_block_hash); + assert_eq!(bc.block_number(&genesis_hash).unwrap(), 0); + assert_eq!(bc.block_number(&b1_hash).unwrap(), 1); + assert_eq!(bc.block_number(&b2_hash).unwrap(), 2); + assert_eq!(bc.block_number(&b3a_hash).unwrap(), 3); + assert_eq!(bc.block_number(&b3b_hash).unwrap(), 3); + + assert_eq!(bc.block_hash(0).unwrap(), genesis_hash); + assert_eq!(bc.block_hash(1).unwrap(), b1_hash); + assert_eq!(bc.block_hash(2).unwrap(), b2_hash); + assert_eq!(bc.block_hash(3).unwrap(), b3a_hash); + + // test trie route + let r0_1 = bc.tree_route(genesis_hash, b1_hash).unwrap(); + assert_eq!(r0_1.ancestor, genesis_hash); + assert_eq!(r0_1.blocks, [b1_hash]); + assert_eq!(r0_1.index, 0); + + let r0_2 = bc.tree_route(genesis_hash, b2_hash).unwrap(); + assert_eq!(r0_2.ancestor, genesis_hash); + assert_eq!(r0_2.blocks, [b1_hash, b2_hash]); + assert_eq!(r0_2.index, 0); + + let r1_3a = bc.tree_route(b1_hash, b3a_hash).unwrap(); + assert_eq!(r1_3a.ancestor, b1_hash); + assert_eq!(r1_3a.blocks, [b2_hash, b3a_hash]); + assert_eq!(r1_3a.index, 0); + + let r1_3b = bc.tree_route(b1_hash, b3b_hash).unwrap(); + assert_eq!(r1_3b.ancestor, b1_hash); + assert_eq!(r1_3b.blocks, [b2_hash, b3b_hash]); + assert_eq!(r1_3b.index, 0); + + let r3a_3b = bc.tree_route(b3a_hash, b3b_hash).unwrap(); + assert_eq!(r3a_3b.ancestor, b2_hash); + assert_eq!(r3a_3b.blocks, [b3a_hash, b3b_hash]); + assert_eq!(r3a_3b.index, 1); + + let r1_0 = bc.tree_route(b1_hash, genesis_hash).unwrap(); + assert_eq!(r1_0.ancestor, genesis_hash); + assert_eq!(r1_0.blocks, [b1_hash]); + assert_eq!(r1_0.index, 1); + + let r2_0 = bc.tree_route(b2_hash, genesis_hash).unwrap(); + assert_eq!(r2_0.ancestor, genesis_hash); + assert_eq!(r2_0.blocks, [b2_hash, b1_hash]); + assert_eq!(r2_0.index, 2); + + let r3a_1 = bc.tree_route(b3a_hash, b1_hash).unwrap(); + assert_eq!(r3a_1.ancestor, b1_hash); + assert_eq!(r3a_1.blocks, [b3a_hash, b2_hash]); + assert_eq!(r3a_1.index, 2); + + let r3b_1 = bc.tree_route(b3b_hash, b1_hash).unwrap(); + assert_eq!(r3b_1.ancestor, b1_hash); + assert_eq!(r3b_1.blocks, [b3b_hash, b2_hash]); + assert_eq!(r3b_1.index, 2); + + let r3b_3a = bc.tree_route(b3b_hash, b3a_hash).unwrap(); + assert_eq!(r3b_3a.ancestor, b2_hash); + assert_eq!(r3b_3a.blocks, [b3b_hash, b3a_hash]); + assert_eq!(r3b_3a.index, 1); + } + + #[test] + fn test_reopen_blockchain_db() { + let genesis = BlockBuilder::genesis(); + let first = genesis.add_block(); + let genesis_hash = genesis.last().hash(); + let first_hash = first.last().hash(); + + let db = new_db(); + + { + let bc = new_chain(genesis.last().encoded(), db.clone()); + assert_eq!(bc.best_block_hash(), genesis_hash); + let mut batch = db.key_value().transaction(); + insert_block_batch(&mut batch, &bc, first.last().encoded(), vec![]); + db.key_value().write(batch).unwrap(); + bc.commit(); + assert_eq!(bc.best_block_hash(), first_hash); + } + + { + let bc = new_chain(genesis.last().encoded(), db.clone()); + + assert_eq!(bc.best_block_hash(), first_hash); + } + } + + #[test] + fn find_transaction_by_hash() { + let genesis = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0af81e09f8c46ca322193edfda764fa7e88e81923f802f1d325ec0b0308ac2cd0a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200008083023e38808454c98c8142a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421880102030405060708c0c0".from_hex().unwrap(); + let b1 = "f904a8f901faa0ce1f26f798dd03c8782d63b3e42e79a64eaea5694ea686ac5d7ce3df5171d1aea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0a65c2364cd0f1542d761823dc0109c6b072f14c20459598c5455c274601438f4a070616ebd7ad2ed6fb7860cf7e9df00163842351c38a87cac2c1cb193895035a2a05c5b4fc43c2d45787f54e1ae7d27afdb4ad16dfc567c5692070d5c4556e0b1d7b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200000183023ec683021536845685109780a029f07836e4e59229b3a065913afc27702642c683bba689910b2b2fd45db310d3888957e6d004a31802f902a7f85f800a8255f094aaaf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca0575da4e21b66fa764be5f74da9389e67693d066fb0d1312e19e17e501da00ecda06baf5a5327595f6619dfc2fcb3f2e6fb410b5810af3cb52d0e7508038e91a188f85f010a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba04fa966bf34b93abc1bcd665554b7f316b50f928477b50be0f3285ead29d18c5ba017bba0eeec1625ab433746955e125d46d80b7fdc97386c51266f842d8e02192ef85f020a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca004377418ae981cc32b1312b4a427a1d69a821b28db8584f5f2bd8c6d42458adaa053a1dba1af177fac92f3b6af0a9fa46a22adf56e686c93794b6a012bf254abf5f85f030a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca04fe13febd28a05f4fcb2f451d7ddc2dda56486d9f8c79a62b0ba4da775122615a0651b2382dd402df9ebc27f8cb4b2e0f3cea68dda2dca0ee9603608f0b6f51668f85f040a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba078e6a0ba086a08f8450e208a399bb2f2d2a0d984acd2517c7c7df66ccfab567da013254002cd45a97fac049ae00afbc43ed0d9961d0c56a3b2382c80ce41c198ddf85f050a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba0a7174d8f43ea71c8e3ca9477691add8d80ac8e0ed89d8d8b572041eef81f4a54a0534ea2e28ec4da3b5b944b18c51ec84a5cf35f5b3343c5fb86521fd2d388f506f85f060a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba034bd04065833536a10c77ee2a43a5371bc6d34837088b861dd9d4b7f44074b59a078807715786a13876d3455716a6b9cb2186b7a4887a5c31160fc877454958616c0".from_hex().unwrap(); + let b1_hash: H256 = + "f53f268d23a71e85c7d6d83a9504298712b84c1a2ba220441c86eeda0bf0b6e3".into(); + + let db = new_db(); + let bc = new_chain(encoded::Block::new(genesis), db.clone()); + let mut batch = db.key_value().transaction(); + insert_block_batch(&mut batch, &bc, encoded::Block::new(b1), vec![]); + db.key_value().write(batch).unwrap(); + bc.commit(); + + let transactions = bc.transactions(&b1_hash).unwrap(); + assert_eq!(transactions.len(), 7); + for t in transactions { + assert_eq!( + bc.transaction(&bc.transaction_address(&t.hash()).unwrap()) + .unwrap(), + t + ); + } + } + + #[test] + fn test_logs() { + let t1 = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 101.into(), + data: "601080600c6000396000f3006000355415600957005b60203560003555" + .from_hex() + .unwrap(), + } + .sign(&secret(), None); + let t2 = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 102.into(), + data: "601080600c6000396000f3006000355415600957005b60203560003555" + .from_hex() + .unwrap(), + } + .sign(&secret(), None); + let t3 = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 103.into(), + data: "601080600c6000396000f3006000355415600957005b60203560003555" + .from_hex() + .unwrap(), + } + .sign(&secret(), None); + let t4 = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 104.into(), + data: "601080600c6000396000f3006000355415600957005b60203560003555" + .from_hex() + .unwrap(), + } + .sign(&secret(), None); + let tx_hash1 = t1.hash(); + let tx_hash2 = t2.hash(); + let tx_hash3 = t3.hash(); + let tx_hash4 = t4.hash(); + + let genesis = BlockBuilder::genesis(); + let b1 = genesis.add_block_with_transactions(vec![t1, t2]); + let b2 = b1.add_block_with_transactions(iter::once(t3)); + let b3 = genesis.add_block_with(|| BlockOptions { + transactions: vec![t4.clone()], + difficulty: U256::from(9), + ..Default::default() + }); // Branch block + let b1_hash = b1.last().hash(); + let b1_number = b1.last().number(); + let b2_hash = b2.last().hash(); + let b2_number = b2.last().number(); + let b3_hash = b3.last().hash(); + let b3_number = b3.last().number(); + + let db = new_db(); + let bc = new_chain(genesis.last().encoded(), db.clone()); + insert_block( + &db, + &bc, + b1.last().encoded(), + vec![ + Receipt { + outcome: TransactionOutcome::StateRoot(H256::default()), + gas_used: 10_000.into(), + log_bloom: Default::default(), + logs: vec![ + LogEntry { + address: Default::default(), + topics: vec![], + data: vec![1], + }, + LogEntry { + address: Default::default(), + topics: vec![], + data: vec![2], + }, + ], + }, + Receipt { + outcome: TransactionOutcome::StateRoot(H256::default()), + gas_used: 10_000.into(), + log_bloom: Default::default(), + logs: vec![LogEntry { + address: Default::default(), + topics: vec![], + data: vec![3], + }], + }, + ], + ); + insert_block( + &db, + &bc, + b2.last().encoded(), + vec![Receipt { + outcome: TransactionOutcome::StateRoot(H256::default()), + gas_used: 10_000.into(), + log_bloom: Default::default(), + logs: vec![LogEntry { + address: Default::default(), + topics: vec![], + data: vec![4], + }], + }], + ); + insert_block( + &db, + &bc, + b3.last().encoded(), + vec![Receipt { + outcome: TransactionOutcome::StateRoot(H256::default()), + gas_used: 10_000.into(), + log_bloom: Default::default(), + logs: vec![LogEntry { + address: Default::default(), + topics: vec![], + data: vec![5], + }], + }], + ); + + // when + let logs1 = bc.logs(vec![b1_hash, b2_hash], |_| true, None); + let logs2 = bc.logs(vec![b1_hash, b2_hash], |_| true, Some(1)); + let logs3 = bc.logs(vec![b3_hash], |_| true, None); + + // then + assert_eq!( + logs1, + vec![ + LocalizedLogEntry { + entry: LogEntry { + address: Default::default(), + topics: vec![], + data: vec![1] + }, + block_hash: b1_hash, + block_number: b1_number, + transaction_hash: tx_hash1, + transaction_index: 0, + transaction_log_index: 0, + log_index: 0, + }, + LocalizedLogEntry { + entry: LogEntry { + address: Default::default(), + topics: vec![], + data: vec![2] + }, + block_hash: b1_hash, + block_number: b1_number, + transaction_hash: tx_hash1, + transaction_index: 0, + transaction_log_index: 1, + log_index: 1, + }, + LocalizedLogEntry { + entry: LogEntry { + address: Default::default(), + topics: vec![], + data: vec![3] + }, + block_hash: b1_hash, + block_number: b1_number, + transaction_hash: tx_hash2, + transaction_index: 1, + transaction_log_index: 0, + log_index: 2, + }, + LocalizedLogEntry { + entry: LogEntry { + address: Default::default(), + topics: vec![], + data: vec![4] + }, + block_hash: b2_hash, + block_number: b2_number, + transaction_hash: tx_hash3, + transaction_index: 0, + transaction_log_index: 0, + log_index: 0, + } + ] + ); + assert_eq!( + logs2, + vec![LocalizedLogEntry { + entry: LogEntry { + address: Default::default(), + topics: vec![], + data: vec![4] + }, + block_hash: b2_hash, + block_number: b2_number, + transaction_hash: tx_hash3, + transaction_index: 0, + transaction_log_index: 0, + log_index: 0, + }] + ); + assert_eq!( + logs3, + vec![LocalizedLogEntry { + entry: LogEntry { + address: Default::default(), + topics: vec![], + data: vec![5] + }, + block_hash: b3_hash, + block_number: b3_number, + transaction_hash: tx_hash4, + transaction_index: 0, + transaction_log_index: 0, + log_index: 0, + }] + ); + } + + #[test] + fn test_bloom_filter_simple() { + let bloom_b1: Bloom = "00000020000000000000000000000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000400000000000000000000002000".into(); + + let bloom_b2: Bloom = "00000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(); + + let bloom_ba: Bloom = "00000000000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(); + + let genesis = BlockBuilder::genesis(); + let b1 = genesis.add_block_with(|| BlockOptions { + bloom: bloom_b1.clone(), + difficulty: 9.into(), + ..Default::default() + }); + let b2 = b1.add_block_with_bloom(bloom_b2); + let b3 = b2.add_block_with_bloom(bloom_ba); + + let b1a = genesis.add_block_with_bloom(bloom_ba); + let b2a = b1a.add_block_with_bloom(bloom_ba); + + let db = new_db(); + let bc = new_chain(genesis.last().encoded(), db.clone()); + + let blocks_b1 = bc.blocks_with_bloom(Some(&bloom_b1), 0, 5); + let blocks_b2 = bc.blocks_with_bloom(Some(&bloom_b2), 0, 5); + assert!(blocks_b1.is_empty()); + assert!(blocks_b2.is_empty()); + + insert_block(&db, &bc, b1.last().encoded(), vec![]); + let blocks_b1 = bc.blocks_with_bloom(Some(&bloom_b1), 0, 5); + let blocks_b2 = bc.blocks_with_bloom(Some(&bloom_b2), 0, 5); + assert_eq!(blocks_b1, vec![1]); + assert!(blocks_b2.is_empty()); + + insert_block(&db, &bc, b2.last().encoded(), vec![]); + let blocks_b1 = bc.blocks_with_bloom(Some(&bloom_b1), 0, 5); + let blocks_b2 = bc.blocks_with_bloom(Some(&bloom_b2), 0, 5); + assert_eq!(blocks_b1, vec![1]); + assert_eq!(blocks_b2, vec![2]); + + // hasn't been forked yet + insert_block(&db, &bc, b1a.last().encoded(), vec![]); + let blocks_b1 = bc.blocks_with_bloom(Some(&bloom_b1), 0, 5); + let blocks_b2 = bc.blocks_with_bloom(Some(&bloom_b2), 0, 5); + let blocks_ba = bc.blocks_with_bloom(Some(&bloom_ba), 0, 5); + assert_eq!(blocks_b1, vec![1]); + assert_eq!(blocks_b2, vec![2]); + assert!(blocks_ba.is_empty()); + + // fork has happend + insert_block(&db, &bc, b2a.last().encoded(), vec![]); + let blocks_b1 = bc.blocks_with_bloom(Some(&bloom_b1), 0, 5); + let blocks_b2 = bc.blocks_with_bloom(Some(&bloom_b2), 0, 5); + let blocks_ba = bc.blocks_with_bloom(Some(&bloom_ba), 0, 5); + assert!(blocks_b1.is_empty()); + assert!(blocks_b2.is_empty()); + assert_eq!(blocks_ba, vec![1, 2]); + + // fork back + insert_block(&db, &bc, b3.last().encoded(), vec![]); + let blocks_b1 = bc.blocks_with_bloom(Some(&bloom_b1), 0, 5); + let blocks_b2 = bc.blocks_with_bloom(Some(&bloom_b2), 0, 5); + let blocks_ba = bc.blocks_with_bloom(Some(&bloom_ba), 0, 5); + assert_eq!(blocks_b1, vec![1]); + assert_eq!(blocks_b2, vec![2]); + assert_eq!(blocks_ba, vec![3]); + } + + #[test] + fn test_insert_unordered() { + let bloom_b1: Bloom = "00000020000000000000000000000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000400000000000000000000002000".into(); + + let bloom_b2: Bloom = "00000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(); + + let bloom_b3: Bloom = "00000000000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(); + + let genesis = BlockBuilder::genesis(); + let b1 = genesis.add_block_with_bloom(bloom_b1); + let b2 = b1.add_block_with_bloom(bloom_b2); + let b3 = b2.add_block_with_bloom(bloom_b3); + let b1_total_difficulty = genesis.last().difficulty() + b1.last().difficulty(); + + let db = new_db(); + let bc = new_chain(genesis.last().encoded(), db.clone()); + let mut batch = db.key_value().transaction(); + bc.insert_unordered_block( + &mut batch, + b2.last().encoded(), + vec![], + Some(b1_total_difficulty), + false, + false, + ); + bc.commit(); + bc.insert_unordered_block(&mut batch, b3.last().encoded(), vec![], None, true, false); + bc.commit(); + bc.insert_unordered_block(&mut batch, b1.last().encoded(), vec![], None, false, false); + bc.commit(); + db.key_value().write(batch).unwrap(); + + assert_eq!(bc.best_block_hash(), b3.last().hash()); + assert_eq!(bc.block_hash(1).unwrap(), b1.last().hash()); + assert_eq!(bc.block_hash(2).unwrap(), b2.last().hash()); + assert_eq!(bc.block_hash(3).unwrap(), b3.last().hash()); + + let blocks_b1 = bc.blocks_with_bloom(Some(&bloom_b1), 0, 3); + let blocks_b2 = bc.blocks_with_bloom(Some(&bloom_b2), 0, 3); + let blocks_b3 = bc.blocks_with_bloom(Some(&bloom_b3), 0, 3); + + assert_eq!(blocks_b1, vec![1]); + assert_eq!(blocks_b2, vec![2]); + assert_eq!(blocks_b3, vec![3]); + } + + #[test] + fn test_best_block_update() { + let genesis = BlockBuilder::genesis(); + let next_5 = genesis.add_blocks(5); + let uncle = genesis.add_block_with_difficulty(9); + let generator = BlockGenerator::new(iter::once(next_5)); + + let db = new_db(); + { + let bc = new_chain(genesis.last().encoded(), db.clone()); + + let mut batch = db.key_value().transaction(); + // create a longer fork + for block in generator { + insert_block_batch(&mut batch, &bc, block.encoded(), vec![]); + bc.commit(); + } + + assert_eq!(bc.best_block_number(), 5); + insert_block_batch(&mut batch, &bc, uncle.last().encoded(), vec![]); + db.key_value().write(batch).unwrap(); + bc.commit(); + } + + // re-loading the blockchain should load the correct best block. + let bc = new_chain(genesis.last().encoded(), db); + assert_eq!(bc.best_block_number(), 5); + } + + #[test] + fn epoch_transitions_iter() { + use common_types::engines::epoch::Transition as EpochTransition; + + let genesis = BlockBuilder::genesis(); + let next_5 = genesis.add_blocks(5); + let uncle = genesis.add_block_with_difficulty(9); + let generator = BlockGenerator::new(iter::once(next_5)); + + let db = new_db(); + { + let bc = new_chain(genesis.last().encoded(), db.clone()); + + let mut batch = db.key_value().transaction(); + // create a longer fork + for (i, block) in generator.into_iter().enumerate() { + insert_block_batch(&mut batch, &bc, block.encoded(), vec![]); + bc.insert_epoch_transition( + &mut batch, + i as u64, + EpochTransition { + block_hash: block.hash(), + block_number: i as u64 + 1, + proof: vec![], + }, + ); + bc.commit(); + } + + assert_eq!(bc.best_block_number(), 5); + + insert_block_batch(&mut batch, &bc, uncle.last().encoded(), vec![]); + bc.insert_epoch_transition( + &mut batch, + 999, + EpochTransition { + block_hash: uncle.last().hash(), + block_number: 1, + proof: vec![], + }, + ); + + db.key_value().write(batch).unwrap(); + bc.commit(); + + // epoch 999 not in canonical chain. + assert_eq!( + bc.epoch_transitions().map(|(i, _)| i).collect::>(), + vec![0, 1, 2, 3, 4] + ); + } + + // re-loading the blockchain should load the correct best block. + let bc = new_chain(genesis.last().encoded(), db); + + assert_eq!(bc.best_block_number(), 5); + assert_eq!( + bc.epoch_transitions().map(|(i, _)| i).collect::>(), + vec![0, 1, 2, 3, 4] + ); + } + + #[test] + fn epoch_transition_for() { + use common_types::engines::epoch::Transition as EpochTransition; + + let genesis = BlockBuilder::genesis(); + let fork_7 = genesis.add_blocks_with(7, || BlockOptions { + difficulty: 9.into(), + ..Default::default() + }); + let next_10 = genesis.add_blocks(10); + let fork_generator = BlockGenerator::new(iter::once(fork_7)); + let next_generator = BlockGenerator::new(iter::once(next_10)); + + let db = new_db(); + + let bc = new_chain(genesis.last().encoded(), db.clone()); + + let mut batch = db.key_value().transaction(); + bc.insert_epoch_transition( + &mut batch, + 0, + EpochTransition { + block_hash: bc.genesis_hash(), + block_number: 0, + proof: vec![], + }, + ); + db.key_value().write(batch).unwrap(); + + // set up a chain where we have a canonical chain of 10 blocks + // and a non-canonical fork of 8 from genesis. + let fork_hash = { + for block in fork_generator { + insert_block(&db, &bc, block.encoded(), vec![]); + } + + assert_eq!(bc.best_block_number(), 7); + bc.chain_info().best_block_hash + }; + + for block in next_generator { + insert_block(&db, &bc, block.encoded(), vec![]); + } + + assert_eq!(bc.best_block_number(), 10); + + let mut batch = db.key_value().transaction(); + bc.insert_epoch_transition( + &mut batch, + 4, + EpochTransition { + block_hash: bc.block_hash(4).unwrap(), + block_number: 4, + proof: vec![], + }, + ); + db.key_value().write(batch).unwrap(); + + // blocks where the parent is one of the first 4 will be part of genesis epoch. + for i in 0..4 { + let hash = bc.block_hash(i).unwrap(); + assert_eq!(bc.epoch_transition_for(hash).unwrap().block_number, 0); + } + + // blocks where the parent is the transition at 4 or after will be + // part of that epoch. + for i in 4..11 { + let hash = bc.block_hash(i).unwrap(); + assert_eq!(bc.epoch_transition_for(hash).unwrap().block_number, 4); + } + + let fork_hashes = bc.ancestry_iter(fork_hash).unwrap().collect::>(); + assert_eq!(fork_hashes.len(), 8); + + // non-canonical fork blocks should all have genesis transition + for fork_hash in fork_hashes { + assert_eq!(bc.epoch_transition_for(fork_hash).unwrap().block_number, 0); + } + } + + #[test] + fn tree_rout_with_finalization() { + let genesis = BlockBuilder::genesis(); + let a = genesis.add_block(); + // First branch + let a1 = a.add_block_with_random_transactions(); + let a2 = a1.add_block_with_random_transactions(); + let a3 = a2.add_block_with_random_transactions(); + // Second branch + let b1 = a.add_block_with_random_transactions(); + let b2 = b1.add_block_with_random_transactions(); + + let a_hash = a.last().hash(); + let a1_hash = a1.last().hash(); + let a2_hash = a2.last().hash(); + let a3_hash = a3.last().hash(); + let b2_hash = b2.last().hash(); + + let bootstrap_chain = |blocks: Vec<&BlockBuilder>| { + let db = new_db(); + let bc = new_chain(genesis.last().encoded(), db.clone()); + let mut batch = db.key_value().transaction(); + for block in blocks { + insert_block_batch(&mut batch, &bc, block.last().encoded(), vec![]); + bc.commit(); + } + db.key_value().write(batch).unwrap(); + (db, bc) + }; + + let mark_finalized = |block_hash: H256, db: &Arc, bc: &BlockChain| { + let mut batch = db.key_value().transaction(); + bc.mark_finalized(&mut batch, block_hash).unwrap(); + bc.commit(); + db.key_value().write(batch).unwrap(); + }; + + // Case 1: fork, with finalized common ancestor + { + let (db, bc) = bootstrap_chain(vec![&a, &a1, &a2, &a3, &b1, &b2]); + assert_eq!(bc.best_block_hash(), a3_hash); + assert_eq!(bc.block_hash(2).unwrap(), a1_hash); + + mark_finalized(a_hash, &db, &bc); + assert!( + !bc.tree_route(a3_hash, b2_hash) + .unwrap() + .is_from_route_finalized + ); + assert!( + !bc.tree_route(b2_hash, a3_hash) + .unwrap() + .is_from_route_finalized + ); + } + + // Case 2: fork with a finalized block on a branch + { + let (db, bc) = bootstrap_chain(vec![&a, &a1, &a2, &a3, &b1, &b2]); + assert_eq!(bc.best_block_hash(), a3_hash); + assert_eq!(bc.block_hash(2).unwrap(), a1_hash); + + mark_finalized(a2_hash, &db, &bc); + assert!( + bc.tree_route(a3_hash, b2_hash) + .unwrap() + .is_from_route_finalized + ); + assert!( + !bc.tree_route(b2_hash, a3_hash) + .unwrap() + .is_from_route_finalized + ); + } + + // Case 3: no-fork, with a finalized block + { + let (db, bc) = bootstrap_chain(vec![&a, &a1, &a2]); + assert_eq!(bc.best_block_hash(), a2_hash); + + mark_finalized(a1_hash, &db, &bc); + assert!( + !bc.tree_route(a1_hash, a2_hash) + .unwrap() + .is_from_route_finalized + ); + assert!( + !bc.tree_route(a2_hash, a1_hash) + .unwrap() + .is_from_route_finalized + ); + } + } } diff --git a/ethcore/blockchain/src/cache.rs b/ethcore/blockchain/src/cache.rs index f17afbb2789..6edfcd566cd 100644 --- a/ethcore/blockchain/src/cache.rs +++ b/ethcore/blockchain/src/cache.rs @@ -1,35 +1,35 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . /// Represents blockchain's in-memory cache size in bytes. #[derive(Debug)] pub struct CacheSize { - /// Blocks cache size. - pub blocks: usize, - /// BlockDetails cache size. - pub block_details: usize, - /// Transaction addresses cache size. - pub transaction_addresses: usize, - /// Block receipts size. - pub block_receipts: usize, + /// Blocks cache size. + pub blocks: usize, + /// BlockDetails cache size. + pub block_details: usize, + /// Transaction addresses cache size. + pub transaction_addresses: usize, + /// Block receipts size. + pub block_receipts: usize, } impl CacheSize { - /// Total amount used by the cache. - pub fn total(&self) -> usize { - self.blocks + self.block_details + self.transaction_addresses + self.block_receipts - } + /// Total amount used by the cache. + pub fn total(&self) -> usize { + self.blocks + self.block_details + self.transaction_addresses + self.block_receipts + } } diff --git a/ethcore/blockchain/src/config.rs b/ethcore/blockchain/src/config.rs index 8cd84b59376..39c37d8461b 100644 --- a/ethcore/blockchain/src/config.rs +++ b/ethcore/blockchain/src/config.rs @@ -1,35 +1,35 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Blockchain configuration. /// Blockchain configuration. #[derive(Debug, PartialEq, Clone)] pub struct Config { - /// Preferred cache size in bytes. - pub pref_cache_size: usize, - /// Maximum cache size in bytes. - pub max_cache_size: usize, + /// Preferred cache size in bytes. + pub pref_cache_size: usize, + /// Maximum cache size in bytes. + pub max_cache_size: usize, } impl Default for Config { - fn default() -> Self { - Config { - pref_cache_size: 1 << 14, - max_cache_size: 1 << 20, - } - } + fn default() -> Self { + Config { + pref_cache_size: 1 << 14, + max_cache_size: 1 << 20, + } + } } diff --git a/ethcore/blockchain/src/generator.rs b/ethcore/blockchain/src/generator.rs index 32ec2802dfb..fc3fbd20d53 100644 --- a/ethcore/blockchain/src/generator.rs +++ b/ethcore/blockchain/src/generator.rs @@ -1,249 +1,293 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Blockchain generator for tests. +use ethereum_types::{Bloom, H256, U256}; use std::collections::VecDeque; -use ethereum_types::{U256, H256, Bloom}; -use common_types::encoded; -use common_types::header::Header; -use common_types::transaction::SignedTransaction; -use common_types::view; -use common_types::views::BlockView; +use common_types::{ + encoded, + header::Header, + transaction::{Action, SignedTransaction, Transaction}, + view, + views::BlockView, +}; +use keccak_hash::keccak; use rlp::encode; use rlp_derive::RlpEncodable; +use triehash_ethereum::ordered_trie_root; /// Helper structure, used for encoding blocks. #[derive(Default, Clone, RlpEncodable)] pub struct Block { - /// Block header - pub header: Header, - /// Block transactions - pub transactions: Vec, - /// Block uncles - pub uncles: Vec
+ /// Block header + pub header: Header, + /// Block transactions + pub transactions: Vec, + /// Block uncles + pub uncles: Vec
, } impl Block { - /// Get a copy of the header - #[inline] - pub fn header(&self) -> Header { - self.header.clone() - } - - /// Get block hash - #[inline] - pub fn hash(&self) -> H256 { - view!(BlockView, &self.encoded().raw()).header_view().hash() - } - - /// Get block number - #[inline] - pub fn number(&self) -> u64 { - self.header.number() - } - - /// Get RLP encoding of this block - #[inline] - pub fn encoded(&self) -> encoded::Block { - encoded::Block::new(encode(self)) - } - - /// Get block difficulty - #[inline] - pub fn difficulty(&self) -> U256 { - *self.header.difficulty() - } + /// Get a copy of the header + #[inline] + pub fn header(&self) -> Header { + self.header.clone() + } + + /// Get block hash + #[inline] + pub fn hash(&self) -> H256 { + view!(BlockView, &self.encoded().raw()).header_view().hash() + } + + /// Get block number + #[inline] + pub fn number(&self) -> u64 { + self.header.number() + } + + /// Get RLP encoding of this block + #[inline] + pub fn encoded(&self) -> encoded::Block { + encoded::Block::new(encode(self)) + } + + /// Get block difficulty + #[inline] + pub fn difficulty(&self) -> U256 { + *self.header.difficulty() + } } /// Specify block options for generator #[derive(Debug)] pub struct BlockOptions { - /// Difficulty - pub difficulty: U256, - /// Set bloom filter - pub bloom: Bloom, - /// Transactions included in blocks - pub transactions: Vec, + /// Difficulty + pub difficulty: U256, + /// Set bloom filter + pub bloom: Bloom, + /// Transactions included in blocks + pub transactions: Vec, } impl Default for BlockOptions { - fn default() -> Self { - BlockOptions { - difficulty: 10.into(), - bloom: Bloom::default(), - transactions: Vec::new(), - } - } + fn default() -> Self { + BlockOptions { + difficulty: 10.into(), + bloom: Bloom::default(), + transactions: Vec::new(), + } + } } /// Utility to create blocks #[derive(Clone)] pub struct BlockBuilder { - blocks: VecDeque, + blocks: VecDeque, } impl BlockBuilder { - /// Create new BlockBuilder starting at genesis. - pub fn genesis() -> Self { - let mut blocks = VecDeque::with_capacity(1); - blocks.push_back(Block::default()); - - BlockBuilder { - blocks, - } - } - - /// Add new block with default options. - #[inline] - pub fn add_block(&self) -> Self { - self.add_block_with(|| BlockOptions::default()) - } - - /// Add `count` number of blocks with default options. - #[inline] - pub fn add_blocks(&self, count: usize) -> Self { - self.add_blocks_with(count, || BlockOptions::default()) - } - - /// Add block with specified options. - #[inline] - pub fn add_block_with(&self, get_metadata: T) -> Self where T: Fn() -> BlockOptions { - self.add_blocks_with(1, get_metadata) - } - - /// Add a block with given difficulty - #[inline] - pub fn add_block_with_difficulty(&self, difficulty: T) -> Self where T: Into { - let difficulty = difficulty.into(); - self.add_blocks_with(1, move || BlockOptions { - difficulty, - ..Default::default() - }) - } - - /// Add a block with given transactions. - #[inline] - pub fn add_block_with_transactions(&self, transactions: T) -> Self - where T: IntoIterator { - let transactions = transactions.into_iter().collect::>(); - self.add_blocks_with(1, || BlockOptions { - transactions: transactions.clone(), - ..Default::default() - }) - } - - /// Add a block with given bloom filter. - #[inline] - pub fn add_block_with_bloom(&self, bloom: Bloom) -> Self { - self.add_blocks_with(1, move || BlockOptions { - bloom, - ..Default::default() - }) - } - - /// Add a bunch of blocks with given metadata. - pub fn add_blocks_with(&self, count: usize, get_metadata: T) -> Self where T: Fn() -> BlockOptions { - assert!(count > 0, "There must be at least 1 block"); - let mut parent_hash = self.last().hash(); - let mut parent_number = self.last().number(); - let mut blocks = VecDeque::with_capacity(count); - for _ in 0..count { - let mut block = Block::default(); - let metadata = get_metadata(); - let block_number = parent_number + 1; - block.header.set_parent_hash(parent_hash); - block.header.set_number(block_number); - block.header.set_log_bloom(metadata.bloom); - block.header.set_difficulty(metadata.difficulty); - block.transactions = metadata.transactions; - - parent_hash = block.hash(); - parent_number = block_number; - - blocks.push_back(block); - } - - BlockBuilder { - blocks, - } - } - - /// Get a reference to the last generated block. - #[inline] - pub fn last(&self) -> &Block { - self.blocks.back().expect("There is always at least 1 block") - } + /// Create new BlockBuilder starting at genesis. + pub fn genesis() -> Self { + let mut blocks = VecDeque::with_capacity(1); + blocks.push_back(Block::default()); + + BlockBuilder { blocks } + } + + /// Add new block with default options. + #[inline] + pub fn add_block(&self) -> Self { + self.add_block_with(|| BlockOptions::default()) + } + + /// Add `count` number of blocks with default options. + #[inline] + pub fn add_blocks(&self, count: usize) -> Self { + self.add_blocks_with(count, || BlockOptions::default()) + } + + /// Add block with specified options. + #[inline] + pub fn add_block_with(&self, get_metadata: T) -> Self + where + T: Fn() -> BlockOptions, + { + self.add_blocks_with(1, get_metadata) + } + + /// Add a block with given difficulty + #[inline] + pub fn add_block_with_difficulty(&self, difficulty: T) -> Self + where + T: Into, + { + let difficulty = difficulty.into(); + self.add_blocks_with(1, move || BlockOptions { + difficulty, + ..Default::default() + }) + } + + /// Add a block with randomly generated transactions. + #[inline] + pub fn add_block_with_random_transactions(&self) -> Self { + // Maximum of ~50 transactions + let count = rand::random::() as usize / 5; + let transactions = std::iter::repeat_with(|| { + let data_len = rand::random::(); + let data = std::iter::repeat_with(|| rand::random::()) + .take(data_len as usize) + .collect::>(); + Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 100.into(), + data, + } + .sign(&keccak("").into(), None) + }) + .take(count); + + self.add_block_with_transactions(transactions) + } + + /// Add a block with given transactions. + #[inline] + pub fn add_block_with_transactions(&self, transactions: T) -> Self + where + T: IntoIterator, + { + let transactions = transactions.into_iter().collect::>(); + self.add_blocks_with(1, || BlockOptions { + transactions: transactions.clone(), + ..Default::default() + }) + } + + /// Add a block with given bloom filter. + #[inline] + pub fn add_block_with_bloom(&self, bloom: Bloom) -> Self { + self.add_blocks_with(1, move || BlockOptions { + bloom, + ..Default::default() + }) + } + + /// Add a bunch of blocks with given metadata. + pub fn add_blocks_with(&self, count: usize, get_metadata: T) -> Self + where + T: Fn() -> BlockOptions, + { + assert!(count > 0, "There must be at least 1 block"); + let mut parent_hash = self.last().hash(); + let mut parent_number = self.last().number(); + let mut blocks = VecDeque::with_capacity(count); + for _ in 0..count { + let mut block = Block::default(); + let metadata = get_metadata(); + let block_number = parent_number + 1; + let transactions = metadata.transactions; + let transactions_root = ordered_trie_root(transactions.iter().map(rlp::encode)); + + block.header.set_parent_hash(parent_hash); + block.header.set_number(block_number); + block.header.set_log_bloom(metadata.bloom); + block.header.set_difficulty(metadata.difficulty); + block.header.set_transactions_root(transactions_root); + block.transactions = transactions; + + parent_hash = block.hash(); + parent_number = block_number; + + blocks.push_back(block); + } + + BlockBuilder { blocks } + } + + /// Get a reference to the last generated block. + #[inline] + pub fn last(&self) -> &Block { + self.blocks + .back() + .expect("There is always at least 1 block") + } } /// Generates a blockchain from given block builders (blocks will be concatenated). #[derive(Clone)] pub struct BlockGenerator { - builders: VecDeque, + builders: VecDeque, } impl BlockGenerator { - /// Create new block generator. - pub fn new(builders: T) -> Self where T: IntoIterator { - BlockGenerator { - builders: builders.into_iter().collect(), - } - } + /// Create new block generator. + pub fn new(builders: T) -> Self + where + T: IntoIterator, + { + BlockGenerator { + builders: builders.into_iter().collect(), + } + } } impl Iterator for BlockGenerator { - type Item = Block; - - fn next(&mut self) -> Option { - loop { - match self.builders.front_mut() { - Some(ref mut builder) => { - if let Some(block) = builder.blocks.pop_front() { - return Some(block); - } - }, - None => return None, - } - self.builders.pop_front(); - } - - } + type Item = Block; + + fn next(&mut self) -> Option { + loop { + match self.builders.front_mut() { + Some(ref mut builder) => { + if let Some(block) = builder.blocks.pop_front() { + return Some(block); + } + } + None => return None, + } + self.builders.pop_front(); + } + } } #[cfg(test)] mod tests { - use super::{BlockBuilder, BlockOptions, BlockGenerator}; - - #[test] - fn test_block_builder() { - let genesis = BlockBuilder::genesis(); - let block_1 = genesis.add_block(); - let block_1001 = block_1.add_blocks(1000); - let block_1002 = block_1001.add_block_with(|| BlockOptions::default()); - let generator = BlockGenerator::new(vec![genesis, block_1, block_1001, block_1002]); - assert_eq!(generator.count(), 1003); - } - - #[test] - fn test_block_builder_fork() { - let genesis = BlockBuilder::genesis(); - let block_10a = genesis.add_blocks(10); - let block_11b = genesis.add_blocks(11); - assert_eq!(block_10a.last().number(), 10); - assert_eq!(block_11b.last().number(), 11); - } + use super::{BlockBuilder, BlockGenerator, BlockOptions}; + + #[test] + fn test_block_builder() { + let genesis = BlockBuilder::genesis(); + let block_1 = genesis.add_block(); + let block_1001 = block_1.add_blocks(1000); + let block_1002 = block_1001.add_block_with(|| BlockOptions::default()); + let generator = BlockGenerator::new(vec![genesis, block_1, block_1001, block_1002]); + assert_eq!(generator.count(), 1003); + } + + #[test] + fn test_block_builder_fork() { + let genesis = BlockBuilder::genesis(); + let block_10a = genesis.add_blocks(10); + let block_11b = genesis.add_blocks(11); + assert_eq!(block_10a.last().number(), 10); + assert_eq!(block_11b.last().number(), 11); + } } diff --git a/ethcore/blockchain/src/import_route.rs b/ethcore/blockchain/src/import_route.rs index 8c635b4e5ab..126b5068a4d 100644 --- a/ethcore/blockchain/src/import_route.rs +++ b/ethcore/blockchain/src/import_route.rs @@ -1,135 +1,147 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Import route. -use ethereum_types::H256; use crate::block_info::{BlockInfo, BlockLocation}; +use ethereum_types::H256; /// Import route for newly inserted block. #[derive(Debug, PartialEq, Clone)] pub struct ImportRoute { - /// Blocks that were invalidated by new block. - pub retracted: Vec, - /// Blocks that were validated by new block. - pub enacted: Vec, - /// Blocks which are neither retracted nor enacted. - pub omitted: Vec, + /// Blocks that were invalidated by new block. + pub retracted: Vec, + /// Blocks that were validated by new block. + pub enacted: Vec, + /// Blocks which are neither retracted nor enacted. + pub omitted: Vec, } impl ImportRoute { - /// Empty import route. - pub fn none() -> Self { - ImportRoute { - retracted: vec![], - enacted: vec![], - omitted: vec![], - } - } + /// Empty import route. + pub fn none() -> Self { + ImportRoute { + retracted: vec![], + enacted: vec![], + omitted: vec![], + } + } } impl From for ImportRoute { - fn from(info: BlockInfo) -> ImportRoute { - match info.location { - BlockLocation::CanonChain => ImportRoute { - retracted: vec![], - enacted: vec![info.hash], - omitted: vec![], - }, - BlockLocation::Branch => ImportRoute { - retracted: vec![], - enacted: vec![], - omitted: vec![info.hash], - }, - BlockLocation::BranchBecomingCanonChain(mut data) => { - data.enacted.push(info.hash); - ImportRoute { - retracted: data.retracted, - enacted: data.enacted, - omitted: vec![], - } - } - } - } + fn from(info: BlockInfo) -> ImportRoute { + match info.location { + BlockLocation::CanonChain => ImportRoute { + retracted: vec![], + enacted: vec![info.hash], + omitted: vec![], + }, + BlockLocation::Branch => ImportRoute { + retracted: vec![], + enacted: vec![], + omitted: vec![info.hash], + }, + BlockLocation::BranchBecomingCanonChain(mut data) => { + data.enacted.push(info.hash); + ImportRoute { + retracted: data.retracted, + enacted: data.enacted, + omitted: vec![], + } + } + } + } } #[cfg(test)] mod tests { - use ethereum_types::{H256, U256}; - use crate::block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainData}; - use super::ImportRoute; - - #[test] - fn import_route_none() { - assert_eq!(ImportRoute::none(), ImportRoute { - enacted: vec![], - retracted: vec![], - omitted: vec![], - }); - } - - #[test] - fn import_route_branch() { - let info = BlockInfo { - hash: H256::from(U256::from(1)), - number: 0, - total_difficulty: U256::from(0), - location: BlockLocation::Branch, - }; - - assert_eq!(ImportRoute::from(info), ImportRoute { - retracted: vec![], - enacted: vec![], - omitted: vec![H256::from(U256::from(1))], - }); - } - - #[test] - fn import_route_canon_chain() { - let info = BlockInfo { - hash: H256::from(U256::from(1)), - number: 0, - total_difficulty: U256::from(0), - location: BlockLocation::CanonChain, - }; - - assert_eq!(ImportRoute::from(info), ImportRoute { - retracted: vec![], - enacted: vec![H256::from(U256::from(1))], - omitted: vec![], - }); - } - - #[test] - fn import_route_branch_becoming_canon_chain() { - let info = BlockInfo { - hash: H256::from(U256::from(2)), - number: 0, - total_difficulty: U256::from(0), - location: BlockLocation::BranchBecomingCanonChain(BranchBecomingCanonChainData { - ancestor: H256::from(U256::from(0)), - enacted: vec![H256::from(U256::from(1))], - retracted: vec![H256::from(U256::from(3)), H256::from(U256::from(4))], - }) - }; - - assert_eq!(ImportRoute::from(info), ImportRoute { - retracted: vec![H256::from(U256::from(3)), H256::from(U256::from(4))], - enacted: vec![H256::from(U256::from(1)), H256::from(U256::from(2))], - omitted: vec![], - }); - } + use super::ImportRoute; + use crate::block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainData}; + use ethereum_types::{H256, U256}; + + #[test] + fn import_route_none() { + assert_eq!( + ImportRoute::none(), + ImportRoute { + enacted: vec![], + retracted: vec![], + omitted: vec![], + } + ); + } + + #[test] + fn import_route_branch() { + let info = BlockInfo { + hash: H256::from(U256::from(1)), + number: 0, + total_difficulty: U256::from(0), + location: BlockLocation::Branch, + }; + + assert_eq!( + ImportRoute::from(info), + ImportRoute { + retracted: vec![], + enacted: vec![], + omitted: vec![H256::from(U256::from(1))], + } + ); + } + + #[test] + fn import_route_canon_chain() { + let info = BlockInfo { + hash: H256::from(U256::from(1)), + number: 0, + total_difficulty: U256::from(0), + location: BlockLocation::CanonChain, + }; + + assert_eq!( + ImportRoute::from(info), + ImportRoute { + retracted: vec![], + enacted: vec![H256::from(U256::from(1))], + omitted: vec![], + } + ); + } + + #[test] + fn import_route_branch_becoming_canon_chain() { + let info = BlockInfo { + hash: H256::from(U256::from(2)), + number: 0, + total_difficulty: U256::from(0), + location: BlockLocation::BranchBecomingCanonChain(BranchBecomingCanonChainData { + ancestor: H256::from(U256::from(0)), + enacted: vec![H256::from(U256::from(1))], + retracted: vec![H256::from(U256::from(3)), H256::from(U256::from(4))], + }), + }; + + assert_eq!( + ImportRoute::from(info), + ImportRoute { + retracted: vec![H256::from(U256::from(3)), H256::from(U256::from(4))], + enacted: vec![H256::from(U256::from(1)), H256::from(U256::from(2))], + omitted: vec![], + } + ); + } } diff --git a/ethcore/blockchain/src/lib.rs b/ethcore/blockchain/src/lib.rs index 3f07a6d8070..943bc3c89ac 100644 --- a/ethcore/blockchain/src/lib.rs +++ b/ethcore/blockchain/src/lib.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Blockchain database. @@ -28,10 +28,12 @@ mod update; pub mod generator; -pub use self::blockchain::{BlockProvider, BlockChain, BlockChainDB, BlockChainDBHandler}; -pub use self::cache::CacheSize; -pub use self::config::Config; -pub use self::import_route::ImportRoute; -pub use self::update::ExtrasInsert; -pub use ethcore_db::keys::{BlockReceipts, BlockDetails, TransactionAddress, BlockNumberKey}; +pub use self::{ + blockchain::{BlockChain, BlockChainDB, BlockChainDBHandler, BlockProvider}, + cache::CacheSize, + config::Config, + import_route::ImportRoute, + update::ExtrasInsert, +}; pub use common_types::tree_route::TreeRoute; +pub use ethcore_db::keys::{BlockDetails, BlockNumberKey, BlockReceipts, TransactionAddress}; diff --git a/ethcore/blockchain/src/update.rs b/ethcore/blockchain/src/update.rs index 959f55fdff5..9ba4673549d 100644 --- a/ethcore/blockchain/src/update.rs +++ b/ethcore/blockchain/src/update.rs @@ -1,51 +1,49 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use std::collections::HashMap; -use common_types::BlockNumber; -use common_types::encoded::Block; -use common_types::engines::ForkChoice; +use common_types::{encoded::Block, engines::ForkChoice, BlockNumber}; use ethcore_db::keys::{BlockDetails, BlockReceipts, TransactionAddress}; -use ethereum_types::{H256, Bloom}; +use ethereum_types::{Bloom, H256}; use crate::block_info::BlockInfo; /// Block extras update info. pub struct ExtrasUpdate { - /// Block info. - pub info: BlockInfo, - /// Current block uncompressed rlp bytes - pub block: Block, - /// Modified block hashes. - pub block_hashes: HashMap, - /// Modified block details. - pub block_details: HashMap, - /// Modified block receipts. - pub block_receipts: HashMap, - /// Modified blocks blooms. - pub blocks_blooms: Option<(u64, Vec)>, - /// Modified transaction addresses (None signifies removed transactions). - pub transactions_addresses: HashMap>, + /// Block info. + pub info: BlockInfo, + /// Current block uncompressed rlp bytes + pub block: Block, + /// Modified block hashes. + pub block_hashes: HashMap, + /// Modified block details. + pub block_details: HashMap, + /// Modified block receipts. + pub block_receipts: HashMap, + /// Modified blocks blooms. + pub blocks_blooms: Option<(u64, Vec)>, + /// Modified transaction addresses (None signifies removed transactions). + pub transactions_addresses: HashMap>, } /// Extra information in block insertion. pub struct ExtrasInsert { - /// The primitive fork choice before applying finalization rules. - pub fork_choice: ForkChoice, - /// Is the inserted block considered finalized. - pub is_finalized: bool, + /// The primitive fork choice before applying finalization rules. + pub fork_choice: ForkChoice, + /// Is the inserted block considered finalized. + pub is_finalized: bool, } diff --git a/ethcore/builtin/Cargo.toml b/ethcore/builtin/Cargo.toml new file mode 100644 index 00000000000..83bc87128fb --- /dev/null +++ b/ethcore/builtin/Cargo.toml @@ -0,0 +1,25 @@ +[package] +description = "ethereum vm builtin" +name = "ethcore-builtin" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +bn = { git = "https://github.com/paritytech/bn", default-features = false } +byteorder = "1.3.2" +eip-152 = { path = "../../util/EIP-152" } +ethereum-types = "0.4" +ethjson = { path = "../../json" } +ethkey = { path = "../../accounts/ethkey" } +keccak-hash = "0.1.0" +log = "0.4" +macros = { path = "../../util/macros" } +num = { version = "0.1", default-features = false, features = ["bigint"] } +parity-bytes = "0.1" +parity-crypto = "0.4.0" +eth_pairings = { git = "https://github.com/matter-labs/eip1962.git", default-features = false, features = ["eip_2537"], rev = "ece6cbabc41948db4200e41f0bfdab7ab94c7af8" } + +[dev-dependencies] +hex-literal = "0.2.1" +maplit = "1.0.2" diff --git a/ethcore/builtin/src/lib.rs b/ethcore/builtin/src/lib.rs new file mode 100644 index 00000000000..36d8c990f06 --- /dev/null +++ b/ethcore/builtin/src/lib.rs @@ -0,0 +1,2590 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. + +// OpenEthereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// OpenEthereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with OpenEthereum. If not, see . + +//! Standard built-in contracts. + +#![allow(missing_docs)] + +use std::{ + cmp::{max, min}, + collections::BTreeMap, + convert::{TryFrom, TryInto}, + io::{self, Cursor, Read}, + mem::size_of, + str::FromStr, +}; + +use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; +use eip_152::compress; +use eth_pairings::public_interface::eip2537::{ + EIP2537Executor, SCALAR_BYTE_LENGTH, SERIALIZED_G1_POINT_BYTE_LENGTH, + SERIALIZED_G2_POINT_BYTE_LENGTH, +}; +use ethereum_types::{H256, U256}; +use ethjson; +use ethkey::{recover as ec_recover, Signature}; +use keccak_hash::keccak; +use log::{trace, warn}; +use num::{BigUint, One, Zero}; +use parity_bytes::BytesRef; +use parity_crypto::digest; + +/// Native implementation of a built-in contract. +pub trait Implementation: Send + Sync { + /// execute this built-in on the given input, writing to the given output. + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str>; +} + +/// A gas pricing scheme for built-in contracts. +trait Pricer: Send + Sync { + /// The gas cost of running this built-in for the given input data at block number `at` + fn cost(&self, input: &[u8]) -> U256; +} + +/// Pricing for the Blake2 compression function (aka "F"). +/// Computes the price as a fixed cost per round where the number of rounds is part of the input +/// byte slice. +pub type Blake2FPricer = u64; + +impl Pricer for Blake2FPricer { + fn cost(&self, input: &[u8]) -> U256 { + const FOUR: usize = std::mem::size_of::(); + // Returning zero if the conversion fails is fine because `execute()` will check the length + // and bail with the appropriate error. + if input.len() < FOUR { + return U256::zero(); + } + let (rounds_bytes, _) = input.split_at(FOUR); + let rounds = u32::from_be_bytes(rounds_bytes.try_into().unwrap_or([0u8; 4])); + U256::from(*self as u64 * rounds as u64) + } +} + +/// Pricing model +#[derive(Debug)] +enum Pricing { + AltBn128Pairing(AltBn128PairingPricer), + AltBn128ConstOperations(AltBn128ConstOperations), + Blake2F(Blake2FPricer), + Linear(Linear), + Modexp(ModexpPricer), + Bls12Pairing(Bls12PairingPricer), + Bls12ConstOperations(Bls12ConstOperations), + Bls12MultiexpG1(Bls12MultiexpPricerG1), + Bls12MultiexpG2(Bls12MultiexpPricerG2), +} + +impl Pricer for Pricing { + fn cost(&self, input: &[u8]) -> U256 { + match self { + Pricing::AltBn128Pairing(inner) => inner.cost(input), + Pricing::AltBn128ConstOperations(inner) => inner.cost(input), + Pricing::Blake2F(inner) => inner.cost(input), + Pricing::Linear(inner) => inner.cost(input), + Pricing::Modexp(inner) => inner.cost(input), + Pricing::Bls12Pairing(inner) => inner.cost(input), + Pricing::Bls12ConstOperations(inner) => inner.cost(input), + Pricing::Bls12MultiexpG1(inner) => inner.cost(input), + Pricing::Bls12MultiexpG2(inner) => inner.cost(input), + } + } +} + +/// A linear pricing model. This computes a price using a base cost and a cost per-word. +#[derive(Debug)] +struct Linear { + base: u64, + word: u64, +} + +/// A special pricing model for modular exponentiation. +#[derive(Debug)] +struct ModexpPricer { + divisor: u64, +} + +impl Pricer for Linear { + fn cost(&self, input: &[u8]) -> U256 { + U256::from(self.base) + U256::from(self.word) * U256::from((input.len() + 31) / 32) + } +} + +/// alt_bn128 pairing price +#[derive(Debug, Copy, Clone)] +struct AltBn128PairingPrice { + base: u64, + pair: u64, +} + +/// alt_bn128_pairing pricing model. This computes a price using a base cost and a cost per pair. +#[derive(Debug)] +struct AltBn128PairingPricer { + price: AltBn128PairingPrice, +} + +/// Pricing for constant alt_bn128 operations (ECADD and ECMUL) +#[derive(Debug, Copy, Clone)] +pub struct AltBn128ConstOperations { + /// Fixed price. + pub price: u64, +} + +impl Pricer for AltBn128ConstOperations { + fn cost(&self, _input: &[u8]) -> U256 { + self.price.into() + } +} + +impl Pricer for AltBn128PairingPricer { + fn cost(&self, input: &[u8]) -> U256 { + U256::from(self.price.base) + U256::from(self.price.pair) * U256::from(input.len() / 192) + } +} + +impl Pricer for ModexpPricer { + fn cost(&self, input: &[u8]) -> U256 { + let mut reader = input.chain(io::repeat(0)); + let mut buf = [0; 32]; + + // read lengths as U256 here for accurate gas calculation. + let mut read_len = || { + reader + .read_exact(&mut buf[..]) + .expect("reading from zero-extended memory cannot fail; qed"); + U256::from(H256::from_slice(&buf[..])) + }; + let base_len = read_len(); + let exp_len = read_len(); + let mod_len = read_len(); + + if mod_len.is_zero() && base_len.is_zero() { + return U256::zero(); + } + + let max_len = U256::from(u32::max_value() / 2); + if base_len > max_len || mod_len > max_len || exp_len > max_len { + return U256::max_value(); + } + let (base_len, exp_len, mod_len) = + (base_len.low_u64(), exp_len.low_u64(), mod_len.low_u64()); + + let m = max(mod_len, base_len); + // read fist 32-byte word of the exponent. + let exp_low = if base_len + 96 >= input.len() as u64 { + U256::zero() + } else { + buf.iter_mut().for_each(|b| *b = 0); + let mut reader = input[(96 + base_len as usize)..].chain(io::repeat(0)); + let len = min(exp_len, 32) as usize; + reader + .read_exact(&mut buf[(32 - len)..]) + .expect("reading from zero-extended memory cannot fail; qed"); + U256::from(H256::from_slice(&buf[..])) + }; + + let adjusted_exp_len = Self::adjusted_exp_len(exp_len, exp_low); + + let (gas, overflow) = Self::mult_complexity(m).overflowing_mul(max(adjusted_exp_len, 1)); + if overflow { + return U256::max_value(); + } + (gas / self.divisor as u64).into() + } +} + +impl ModexpPricer { + fn adjusted_exp_len(len: u64, exp_low: U256) -> u64 { + let bit_index = if exp_low.is_zero() { + 0 + } else { + (255 - exp_low.leading_zeros()) as u64 + }; + if len <= 32 { + bit_index + } else { + 8 * (len - 32) + bit_index + } + } + + fn mult_complexity(x: u64) -> u64 { + match x { + x if x <= 64 => x * x, + x if x <= 1024 => (x * x) / 4 + 96 * x - 3072, + x => (x * x) / 16 + 480 * x - 199_680, + } + } +} + +/// Bls12 pairing price +#[derive(Debug, Copy, Clone)] +struct Bls12PairingPrice { + base: u64, + pair: u64, +} + +/// bls12_pairing pricing model. This computes a price using a base cost and a cost per pair. +#[derive(Debug)] +struct Bls12PairingPricer { + price: Bls12PairingPrice, +} + +/// Pricing for constant Bls12 operations (ADD and MUL in G1 and G2, as well as mappings) +#[derive(Debug, Copy, Clone)] +pub struct Bls12ConstOperations { + /// Fixed price. + pub price: u64, +} + +/// Discount table for multiexponentiation (Pippenger's Algorithm) +/// Later on is normalized using the divisor +pub const BLS12_MULTIEXP_DISCOUNTS_TABLE: [[u64; 2]; BLS12_MULTIEXP_PAIRS_FOR_MAX_DISCOUNT] = [ + [1, 1200], + [2, 888], + [3, 764], + [4, 641], + [5, 594], + [6, 547], + [7, 500], + [8, 453], + [9, 438], + [10, 423], + [11, 408], + [12, 394], + [13, 379], + [14, 364], + [15, 349], + [16, 334], + [17, 330], + [18, 326], + [19, 322], + [20, 318], + [21, 314], + [22, 310], + [23, 306], + [24, 302], + [25, 298], + [26, 294], + [27, 289], + [28, 285], + [29, 281], + [30, 277], + [31, 273], + [32, 269], + [33, 268], + [34, 266], + [35, 265], + [36, 263], + [37, 262], + [38, 260], + [39, 259], + [40, 257], + [41, 256], + [42, 254], + [43, 253], + [44, 251], + [45, 250], + [46, 248], + [47, 247], + [48, 245], + [49, 244], + [50, 242], + [51, 241], + [52, 239], + [53, 238], + [54, 236], + [55, 235], + [56, 233], + [57, 232], + [58, 231], + [59, 229], + [60, 228], + [61, 226], + [62, 225], + [63, 223], + [64, 222], + [65, 221], + [66, 220], + [67, 219], + [68, 219], + [69, 218], + [70, 217], + [71, 216], + [72, 216], + [73, 215], + [74, 214], + [75, 213], + [76, 213], + [77, 212], + [78, 211], + [79, 211], + [80, 210], + [81, 209], + [82, 208], + [83, 208], + [84, 207], + [85, 206], + [86, 205], + [87, 205], + [88, 204], + [89, 203], + [90, 202], + [91, 202], + [92, 201], + [93, 200], + [94, 199], + [95, 199], + [96, 198], + [97, 197], + [98, 196], + [99, 196], + [100, 195], + [101, 194], + [102, 193], + [103, 193], + [104, 192], + [105, 191], + [106, 191], + [107, 190], + [108, 189], + [109, 188], + [110, 188], + [111, 187], + [112, 186], + [113, 185], + [114, 185], + [115, 184], + [116, 183], + [117, 182], + [118, 182], + [119, 181], + [120, 180], + [121, 179], + [122, 179], + [123, 178], + [124, 177], + [125, 176], + [126, 176], + [127, 175], + [128, 174], +]; + +/// Max discount allowed +pub const BLS12_MULTIEXP_MAX_DISCOUNT: u64 = 174; +/// Max discount is reached at this number of pairs +pub const BLS12_MULTIEXP_PAIRS_FOR_MAX_DISCOUNT: usize = 128; +/// Divisor for discounts table +pub const BLS12_MULTIEXP_DISCOUNT_DIVISOR: u64 = 1000; +/// Length of single G1 + G2 points pair for pairing operation +pub const BLS12_G1_AND_G2_PAIR_LEN: usize = + SERIALIZED_G1_POINT_BYTE_LENGTH + SERIALIZED_G2_POINT_BYTE_LENGTH; + +/// Marter trait for length of input per one pair (point + scalar) +pub trait PointScalarLength: Copy + Clone + std::fmt::Debug + Send + Sync { + /// Length itself + const LENGTH: usize; +} +/// Marker trait that indicated that we perform operations in G1 +#[derive(Clone, Copy, Debug)] +pub struct G1Marker; +impl PointScalarLength for G1Marker { + const LENGTH: usize = SERIALIZED_G1_POINT_BYTE_LENGTH + SCALAR_BYTE_LENGTH; +} +/// Marker trait that indicated that we perform operations in G2 +#[derive(Clone, Copy, Debug)] +pub struct G2Marker; +impl PointScalarLength for G2Marker { + const LENGTH: usize = SERIALIZED_G2_POINT_BYTE_LENGTH + SCALAR_BYTE_LENGTH; +} + +/// Pricing for constant Bls12 operations (ADD and MUL in G1 and G2, as well as mappings) +#[derive(Debug, Copy, Clone)] +pub struct Bls12MultiexpPricer { + /// Base const of the operation (G1 or G2 multiplication) + pub base_price: Bls12ConstOperations, + + _marker: std::marker::PhantomData

, +} + +impl Pricer for Bls12ConstOperations { + fn cost(&self, _input: &[u8]) -> U256 { + self.price.into() + } +} + +impl Pricer for Bls12PairingPricer { + fn cost(&self, input: &[u8]) -> U256 { + U256::from(self.price.base) + + U256::from(self.price.pair) * U256::from(input.len() / BLS12_G1_AND_G2_PAIR_LEN) + } +} + +impl Pricer for Bls12MultiexpPricer

{ + fn cost(&self, input: &[u8]) -> U256 { + let num_pairs = input.len() / P::LENGTH; + if num_pairs == 0 { + return U256::zero(); + } + let discount = if num_pairs > BLS12_MULTIEXP_PAIRS_FOR_MAX_DISCOUNT { + BLS12_MULTIEXP_MAX_DISCOUNT + } else { + let table_entry = BLS12_MULTIEXP_DISCOUNTS_TABLE[num_pairs - 1]; + table_entry[1] + }; + U256::from(self.base_price.price) * U256::from(num_pairs) * U256::from(discount) + / U256::from(BLS12_MULTIEXP_DISCOUNT_DIVISOR) + } +} + +/// Multiexp pricer in G1 +pub type Bls12MultiexpPricerG1 = Bls12MultiexpPricer; + +/// Multiexp pricer in G2 +pub type Bls12MultiexpPricerG2 = Bls12MultiexpPricer; + +/// Pricing scheme, execution definition, and activation block for a built-in contract. +/// +/// Call `cost` to compute cost for the given input, `execute` to execute the contract +/// on the given input, and `is_active` to determine whether the contract is active. +pub struct Builtin { + pricer: BTreeMap, + native: EthereumBuiltin, +} + +impl Builtin { + /// Simple forwarder for cost. + /// + /// Return the cost of the most recently activated pricer at the current block number. + /// + /// If no pricer is actived `zero` is returned + /// + /// If multiple `activation_at` has the same block number the last one is used + /// (follows `BTreeMap` semantics). + #[inline] + pub fn cost(&self, input: &[u8], at: u64) -> U256 { + if let Some((_, pricer)) = self.pricer.range(0..=at).last() { + pricer.cost(input) + } else { + U256::zero() + } + } + + /// Simple forwarder for execute. + #[inline] + pub fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + self.native.execute(input, output) + } + + /// Whether the builtin is activated at the given block number. + #[inline] + pub fn is_active(&self, at: u64) -> bool { + self.pricer.range(0..=at).last().is_some() + } +} + +impl TryFrom for Builtin { + type Error = String; + + fn try_from(b: ethjson::spec::builtin::Builtin) -> Result { + let native = EthereumBuiltin::from_str(&b.name)?; + let mut pricer = BTreeMap::new(); + + for (activate_at, p) in b.pricing { + pricer.insert(activate_at, p.price.into()); + } + + Ok(Self { pricer, native }) + } +} + +impl From for Pricing { + fn from(pricing: ethjson::spec::builtin::Pricing) -> Self { + match pricing { + ethjson::spec::builtin::Pricing::Blake2F { gas_per_round } => { + Pricing::Blake2F(gas_per_round) + } + ethjson::spec::builtin::Pricing::Linear(linear) => Pricing::Linear(Linear { + base: linear.base, + word: linear.word, + }), + ethjson::spec::builtin::Pricing::Modexp(exp) => Pricing::Modexp(ModexpPricer { + divisor: if exp.divisor == 0 { + warn!(target: "builtin", "Zero modexp divisor specified. Falling back to default: 10."); + 10 + } else { + exp.divisor + }, + }), + ethjson::spec::builtin::Pricing::AltBn128Pairing(pricer) => { + Pricing::AltBn128Pairing(AltBn128PairingPricer { + price: AltBn128PairingPrice { + base: pricer.base, + pair: pricer.pair, + }, + }) + } + ethjson::spec::builtin::Pricing::AltBn128ConstOperations(pricer) => { + Pricing::AltBn128ConstOperations(AltBn128ConstOperations { + price: pricer.price, + }) + } + ethjson::spec::builtin::Pricing::Bls12ConstOperations(pricer) => { + Pricing::Bls12ConstOperations(Bls12ConstOperations { + price: pricer.price, + }) + } + ethjson::spec::builtin::Pricing::Bls12Pairing(pricer) => { + Pricing::Bls12Pairing(Bls12PairingPricer { + price: Bls12PairingPrice { + base: pricer.base, + pair: pricer.pair, + }, + }) + } + ethjson::spec::builtin::Pricing::Bls12G1Multiexp(pricer) => { + Pricing::Bls12MultiexpG1(Bls12MultiexpPricerG1 { + base_price: Bls12ConstOperations { price: pricer.base }, + _marker: std::marker::PhantomData, + }) + } + ethjson::spec::builtin::Pricing::Bls12G2Multiexp(pricer) => { + Pricing::Bls12MultiexpG2(Bls12MultiexpPricerG2 { + base_price: Bls12ConstOperations { price: pricer.base }, + _marker: std::marker::PhantomData, + }) + } + } + } +} + +/// Ethereum builtins: +enum EthereumBuiltin { + /// The identity function + Identity(Identity), + /// ec recovery + EcRecover(EcRecover), + /// sha256 + Sha256(Sha256), + /// ripemd160 + Ripemd160(Ripemd160), + /// modexp (EIP 198) + Modexp(Modexp), + /// alt_bn128_add + Bn128Add(Bn128Add), + /// alt_bn128_mul + Bn128Mul(Bn128Mul), + /// alt_bn128_pairing + Bn128Pairing(Bn128Pairing), + /// blake2_f (The Blake2 compression function F, EIP-152) + Blake2F(Blake2F), + /// bls12_381 addition in g1 + Bls12G1Add(Bls12G1Add), + /// bls12_381 multiplication in g1 + Bls12G1Mul(Bls12G1Mul), + /// bls12_381 multiexponentiation in g1 + Bls12G1MultiExp(Bls12G1MultiExp), + /// bls12_381 addition in g2 + Bls12G2Add(Bls12G2Add), + /// bls12_381 multiplication in g2 + Bls12G2Mul(Bls12G2Mul), + /// bls12_381 multiexponentiation in g2 + Bls12G2MultiExp(Bls12G2MultiExp), + /// bls12_381 pairing + Bls12Pairing(Bls12Pairing), + /// bls12_381 fp to g1 mapping + Bls12MapFpToG1(Bls12MapFpToG1), + /// bls12_381 fp2 to g2 mapping + Bls12MapFp2ToG2(Bls12MapFp2ToG2), +} + +impl FromStr for EthereumBuiltin { + type Err = String; + + fn from_str(name: &str) -> Result { + match name { + "identity" => Ok(EthereumBuiltin::Identity(Identity)), + "ecrecover" => Ok(EthereumBuiltin::EcRecover(EcRecover)), + "sha256" => Ok(EthereumBuiltin::Sha256(Sha256)), + "ripemd160" => Ok(EthereumBuiltin::Ripemd160(Ripemd160)), + "modexp" => Ok(EthereumBuiltin::Modexp(Modexp)), + "alt_bn128_add" => Ok(EthereumBuiltin::Bn128Add(Bn128Add)), + "alt_bn128_mul" => Ok(EthereumBuiltin::Bn128Mul(Bn128Mul)), + "alt_bn128_pairing" => Ok(EthereumBuiltin::Bn128Pairing(Bn128Pairing)), + "blake2_f" => Ok(EthereumBuiltin::Blake2F(Blake2F)), + "bls12_381_g1_add" => Ok(EthereumBuiltin::Bls12G1Add(Bls12G1Add)), + "bls12_381_g1_mul" => Ok(EthereumBuiltin::Bls12G1Mul(Bls12G1Mul)), + "bls12_381_g1_multiexp" => Ok(EthereumBuiltin::Bls12G1MultiExp(Bls12G1MultiExp)), + "bls12_381_g2_add" => Ok(EthereumBuiltin::Bls12G2Add(Bls12G2Add)), + "bls12_381_g2_mul" => Ok(EthereumBuiltin::Bls12G2Mul(Bls12G2Mul)), + "bls12_381_g2_multiexp" => Ok(EthereumBuiltin::Bls12G2MultiExp(Bls12G2MultiExp)), + "bls12_381_pairing" => Ok(EthereumBuiltin::Bls12Pairing(Bls12Pairing)), + "bls12_381_fp_to_g1" => Ok(EthereumBuiltin::Bls12MapFpToG1(Bls12MapFpToG1)), + "bls12_381_fp2_to_g2" => Ok(EthereumBuiltin::Bls12MapFp2ToG2(Bls12MapFp2ToG2)), + _ => return Err(format!("invalid builtin name: {}", name)), + } + } +} + +impl Implementation for EthereumBuiltin { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + match self { + EthereumBuiltin::Identity(inner) => inner.execute(input, output), + EthereumBuiltin::EcRecover(inner) => inner.execute(input, output), + EthereumBuiltin::Sha256(inner) => inner.execute(input, output), + EthereumBuiltin::Ripemd160(inner) => inner.execute(input, output), + EthereumBuiltin::Modexp(inner) => inner.execute(input, output), + EthereumBuiltin::Bn128Add(inner) => inner.execute(input, output), + EthereumBuiltin::Bn128Mul(inner) => inner.execute(input, output), + EthereumBuiltin::Bn128Pairing(inner) => inner.execute(input, output), + EthereumBuiltin::Blake2F(inner) => inner.execute(input, output), + EthereumBuiltin::Bls12G1Add(inner) => inner.execute(input, output), + EthereumBuiltin::Bls12G1Mul(inner) => inner.execute(input, output), + EthereumBuiltin::Bls12G1MultiExp(inner) => inner.execute(input, output), + EthereumBuiltin::Bls12G2Add(inner) => inner.execute(input, output), + EthereumBuiltin::Bls12G2Mul(inner) => inner.execute(input, output), + EthereumBuiltin::Bls12G2MultiExp(inner) => inner.execute(input, output), + EthereumBuiltin::Bls12Pairing(inner) => inner.execute(input, output), + EthereumBuiltin::Bls12MapFpToG1(inner) => inner.execute(input, output), + EthereumBuiltin::Bls12MapFp2ToG2(inner) => inner.execute(input, output), + } + } +} + +#[derive(Debug)] +pub struct Identity; + +#[derive(Debug)] +pub struct EcRecover; + +#[derive(Debug)] +pub struct Sha256; + +#[derive(Debug)] +pub struct Ripemd160; + +#[derive(Debug)] +pub struct Modexp; + +#[derive(Debug)] +pub struct Bn128Add; + +#[derive(Debug)] +pub struct Bn128Mul; + +#[derive(Debug)] +pub struct Bn128Pairing; + +#[derive(Debug)] +pub struct Blake2F; + +#[derive(Debug)] +/// The Bls12G1Add builtin. +pub struct Bls12G1Add; + +#[derive(Debug)] +/// The Bls12G1Mul builtin. +pub struct Bls12G1Mul; + +#[derive(Debug)] +/// The Bls12G1MultiExp builtin. +pub struct Bls12G1MultiExp; + +#[derive(Debug)] +/// The Bls12G2Add builtin. +pub struct Bls12G2Add; + +#[derive(Debug)] +/// The Bls12G2Mul builtin. +pub struct Bls12G2Mul; + +#[derive(Debug)] +/// The Bls12G2MultiExp builtin. +pub struct Bls12G2MultiExp; + +#[derive(Debug)] +/// The Bls12Pairing builtin. +pub struct Bls12Pairing; + +#[derive(Debug)] +/// The Bls12MapFpToG1 builtin. +pub struct Bls12MapFpToG1; + +#[derive(Debug)] +/// The Bls12MapFp2ToG2 builtin. +pub struct Bls12MapFp2ToG2; + +impl Implementation for Identity { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + output.write(0, input); + Ok(()) + } +} + +impl Implementation for EcRecover { + fn execute(&self, i: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + let len = min(i.len(), 128); + + let mut input = [0; 128]; + input[..len].copy_from_slice(&i[..len]); + + let hash = H256::from_slice(&input[0..32]); + let v = H256::from_slice(&input[32..64]); + let r = H256::from_slice(&input[64..96]); + let s = H256::from_slice(&input[96..128]); + + let bit = match v[31] { + 27 | 28 if v.0[..31] == [0; 31] => v[31] - 27, + _ => { + return Ok(()); + } + }; + + let s = Signature::from_rsv(&r, &s, bit); + if s.is_valid() { + if let Ok(p) = ec_recover(&s, &hash) { + let r = keccak(p); + output.write(0, &[0; 12]); + output.write(12, &r[12..r.len()]); + } + } + + Ok(()) + } +} + +impl Implementation for Sha256 { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + let d = digest::sha256(input); + output.write(0, &*d); + Ok(()) + } +} + +impl Implementation for Blake2F { + /// Format of `input`: + /// [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 execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + const BLAKE2_F_ARG_LEN: usize = 213; + const PROOF: &str = "Checked the length of the input above; qed"; + + if input.len() != BLAKE2_F_ARG_LEN { + trace!(target: "builtin", "input length for Blake2 F precompile should be exactly 213 bytes, was {}", input.len()); + return Err("input length for Blake2 F precompile should be exactly 213 bytes".into()); + } + + let mut cursor = Cursor::new(input); + let rounds = cursor.read_u32::().expect(PROOF); + + // state vector, h + let mut h = [0u64; 8]; + for state_word in &mut h { + *state_word = cursor.read_u64::().expect(PROOF); + } + + // message block vector, m + let mut m = [0u64; 16]; + for msg_word in &mut m { + *msg_word = cursor.read_u64::().expect(PROOF); + } + + // 2w-bit offset counter, t + let t = [ + cursor.read_u64::().expect(PROOF), + cursor.read_u64::().expect(PROOF), + ]; + + // final block indicator flag, "f" + let f = match input.last() { + Some(1) => true, + Some(0) => false, + _ => { + trace!(target: "builtin", "incorrect final block indicator flag, was: {:?}", input.last()); + return Err("incorrect final block indicator flag".into()); + } + }; + + compress(&mut h, m, t, f, rounds as usize); + + let mut output_buf = [0u8; 8 * size_of::()]; + for (i, state_word) in h.iter().enumerate() { + output_buf[i * 8..(i + 1) * 8].copy_from_slice(&state_word.to_le_bytes()); + } + output.write(0, &output_buf[..]); + Ok(()) + } +} + +impl Implementation for Ripemd160 { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + let hash = digest::ripemd160(input); + output.write(0, &[0; 12][..]); + output.write(12, &hash); + Ok(()) + } +} + +// calculate modexp: left-to-right binary exponentiation to keep multiplicands lower +fn modexp(mut base: BigUint, exp: Vec, modulus: BigUint) -> BigUint { + const BITS_PER_DIGIT: usize = 8; + + // n^m % 0 || n^m % 1 + if modulus <= BigUint::one() { + return BigUint::zero(); + } + + // normalize exponent + let mut exp = exp.into_iter().skip_while(|d| *d == 0).peekable(); + + // n^0 % m + if exp.peek().is_none() { + return BigUint::one(); + } + + // 0^n % m, n > 0 + if base.is_zero() { + return BigUint::zero(); + } + + base %= &modulus; + + // Fast path for base divisible by modulus. + if base.is_zero() { + return BigUint::zero(); + } + + // Left-to-right binary exponentiation (Handbook of Applied Cryptography - Algorithm 14.79). + // http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf + let mut result = BigUint::one(); + + for digit in exp { + let mut mask = 1 << (BITS_PER_DIGIT - 1); + + for _ in 0..BITS_PER_DIGIT { + result = &result * &result % &modulus; + + if digit & mask > 0 { + result = result * &base % &modulus; + } + + mask >>= 1; + } + } + + result +} + +impl Implementation for Modexp { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + let mut reader = input.chain(io::repeat(0)); + let mut buf = [0; 32]; + + // read lengths as usize. + // ignoring the first 24 bytes might technically lead us to fall out of consensus, + // but so would running out of addressable memory! + let mut read_len = |reader: &mut io::Chain<&[u8], io::Repeat>| { + reader + .read_exact(&mut buf[..]) + .expect("reading from zero-extended memory cannot fail; qed"); + let mut len_bytes = [0u8; 8]; + len_bytes.copy_from_slice(&buf[24..]); + u64::from_be_bytes(len_bytes) as usize + }; + + let base_len = read_len(&mut reader); + let exp_len = read_len(&mut reader); + let mod_len = read_len(&mut reader); + + // Gas formula allows arbitrary large exp_len when base and modulus are empty, so we need to handle empty base first. + let r = if base_len == 0 && mod_len == 0 { + BigUint::zero() + } else { + // read the numbers themselves. + let mut buf = vec![0; max(mod_len, max(base_len, exp_len))]; + let mut read_num = |reader: &mut io::Chain<&[u8], io::Repeat>, len: usize| { + reader + .read_exact(&mut buf[..len]) + .expect("reading from zero-extended memory cannot fail; qed"); + BigUint::from_bytes_be(&buf[..len]) + }; + + let base = read_num(&mut reader, base_len); + + let mut exp_buf = vec![0; exp_len]; + reader + .read_exact(&mut exp_buf[..exp_len]) + .expect("reading from zero-extended memory cannot fail; qed"); + + let modulus = read_num(&mut reader, mod_len); + + modexp(base, exp_buf, modulus) + }; + + // 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. + if bytes.len() <= mod_len { + let res_start = mod_len - bytes.len(); + output.write(res_start, &bytes); + } + + Ok(()) + } +} + +impl Implementation for Bls12G1Add { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + let result = EIP2537Executor::g1_add(input); + + match result { + Ok(result_bytes) => { + output.write(0, &result_bytes[..]); + + Ok(()) + } + Err(e) => { + trace!(target: "builtin", "Bls12G1Add error: {:?}", e); + + Err("Bls12G1Add error") + } + } + } +} + +impl Implementation for Bls12G1Mul { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + let result = EIP2537Executor::g1_mul(input); + + match result { + Ok(result_bytes) => { + output.write(0, &result_bytes[..]); + + Ok(()) + } + Err(e) => { + trace!(target: "builtin", "Bls12G1Mul error: {:?}", e); + + Err("Bls12G1Mul error") + } + } + } +} + +impl Implementation for Bls12G1MultiExp { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + let result = EIP2537Executor::g1_multiexp(input); + + match result { + Ok(result_bytes) => { + output.write(0, &result_bytes[..]); + + Ok(()) + } + Err(e) => { + trace!(target: "builtin", "Bls12G1MultiExp error: {:?}", e); + + Err("Bls12G1MultiExp error") + } + } + } +} + +impl Implementation for Bls12G2Add { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + let result = EIP2537Executor::g2_add(input); + + match result { + Ok(result_bytes) => { + output.write(0, &result_bytes[..]); + + Ok(()) + } + Err(e) => { + trace!(target: "builtin", "Bls12G2Add error: {:?}", e); + + Err("Bls12G2Add error") + } + } + } +} + +impl Implementation for Bls12G2Mul { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + let result = EIP2537Executor::g2_mul(input); + + match result { + Ok(result_bytes) => { + output.write(0, &result_bytes[..]); + + Ok(()) + } + Err(e) => { + trace!(target: "builtin", "Bls12G2Mul error: {:?}", e); + + Err("Bls12G2Mul error") + } + } + } +} + +impl Implementation for Bls12G2MultiExp { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + let result = EIP2537Executor::g2_multiexp(input); + + match result { + Ok(result_bytes) => { + output.write(0, &result_bytes[..]); + + Ok(()) + } + Err(e) => { + trace!(target: "builtin", "Bls12G2MultiExp error: {:?}", e); + + Err("Bls12G2MultiExp error") + } + } + } +} + +impl Implementation for Bls12Pairing { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + let result = EIP2537Executor::pair(input); + + match result { + Ok(result_bytes) => { + output.write(0, &result_bytes[..]); + + Ok(()) + } + Err(e) => { + trace!(target: "builtin", "Bls12Pairing error: {:?}", e); + + Err("Bls12Pairing error") + } + } + } +} + +impl Implementation for Bls12MapFpToG1 { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + let result = EIP2537Executor::map_fp_to_g1(input); + + match result { + Ok(result_bytes) => { + output.write(0, &result_bytes[..]); + + Ok(()) + } + Err(e) => { + trace!(target: "builtin", "Bls12MapFpToG1 error: {:?}", e); + + Err("Bls12MapFpToG1 error") + } + } + } +} + +impl Implementation for Bls12MapFp2ToG2 { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + let result = EIP2537Executor::map_fp2_to_g2(input); + + match result { + Ok(result_bytes) => { + output.write(0, &result_bytes[..]); + + Ok(()) + } + Err(e) => { + trace!(target: "builtin", "Bls12MapFp2ToG2 error: {:?}", e); + + Err("Bls12MapFp2ToG2 error") + } + } + } +} + +fn read_fr(reader: &mut io::Chain<&[u8], io::Repeat>) -> Result { + let mut buf = [0u8; 32]; + + reader + .read_exact(&mut buf[..]) + .expect("reading from zero-extended memory cannot fail; qed"); + bn::Fr::from_slice(&buf[0..32]).map_err(|_| "Invalid field element") +} + +fn read_point(reader: &mut io::Chain<&[u8], io::Repeat>) -> Result { + use bn::{AffineG1, Fq, Group, G1}; + + let mut buf = [0u8; 32]; + + reader + .read_exact(&mut buf[..]) + .expect("reading from zero-extended memory cannot fail; qed"); + let px = Fq::from_slice(&buf[0..32]).map_err(|_| "Invalid point x coordinate")?; + + reader + .read_exact(&mut buf[..]) + .expect("reading from zero-extended memory cannot fail; qed"); + let py = Fq::from_slice(&buf[0..32]).map_err(|_| "Invalid point y coordinate")?; + Ok(if px == Fq::zero() && py == Fq::zero() { + G1::zero() + } else { + AffineG1::new(px, py) + .map_err(|_| "Invalid curve point")? + .into() + }) +} + +impl Implementation for Bn128Add { + // Can fail if any of the 2 points does not belong the bn128 curve + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + use bn::AffineG1; + + let mut padded_input = input.chain(io::repeat(0)); + let p1 = read_point(&mut padded_input)?; + let p2 = read_point(&mut padded_input)?; + + let mut write_buf = [0u8; 64]; + if let Some(sum) = AffineG1::from_jacobian(p1 + p2) { + // point not at infinity + sum.x() + .to_big_endian(&mut write_buf[0..32]) + .expect("Cannot fail since 0..32 is 32-byte length"); + sum.y() + .to_big_endian(&mut write_buf[32..64]) + .expect("Cannot fail since 32..64 is 32-byte length"); + } + output.write(0, &write_buf); + + Ok(()) + } +} + +impl Implementation for Bn128Mul { + // Can fail if first paramter (bn128 curve point) does not actually belong to the curve + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + use bn::AffineG1; + + let mut padded_input = input.chain(io::repeat(0)); + let p = read_point(&mut padded_input)?; + let fr = read_fr(&mut padded_input)?; + + let mut write_buf = [0u8; 64]; + if let Some(sum) = AffineG1::from_jacobian(p * fr) { + // point not at infinity + sum.x() + .to_big_endian(&mut write_buf[0..32]) + .expect("Cannot fail since 0..32 is 32-byte length"); + sum.y() + .to_big_endian(&mut write_buf[32..64]) + .expect("Cannot fail since 32..64 is 32-byte length"); + } + output.write(0, &write_buf); + Ok(()) + } +} + +impl Implementation for Bn128Pairing { + /// Can fail if: + /// - input length is not a multiple of 192 + /// - any of odd points does not belong to bn128 curve + /// - any of even points does not belong to the twisted bn128 curve over the field F_p^2 = F_p[i] / (i^2 + 1) + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + if input.len() % 192 != 0 { + return Err("Invalid input length, must be multiple of 192 (3 * (32*2))".into()); + } + + if let Err(err) = self.execute_with_error(input, output) { + trace!(target: "builtin", "Pairing error: {:?}", err); + return Err(err); + } + Ok(()) + } +} + +impl Bn128Pairing { + fn execute_with_error(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + use bn::{pairing, AffineG1, AffineG2, Fq, Fq2, Group, Gt, G1, G2}; + + let ret_val = if input.is_empty() { + U256::one() + } else { + // (a, b_a, b_b - each 64-byte affine coordinates) + let elements = input.len() / 192; + let mut vals = Vec::new(); + for idx in 0..elements { + let a_x = Fq::from_slice(&input[idx * 192..idx * 192 + 32]) + .map_err(|_| "Invalid a argument x coordinate")?; + + let a_y = Fq::from_slice(&input[idx * 192 + 32..idx * 192 + 64]) + .map_err(|_| "Invalid a argument y coordinate")?; + + let b_a_y = Fq::from_slice(&input[idx * 192 + 64..idx * 192 + 96]) + .map_err(|_| "Invalid b argument imaginary coeff x coordinate")?; + + let b_a_x = Fq::from_slice(&input[idx * 192 + 96..idx * 192 + 128]) + .map_err(|_| "Invalid b argument imaginary coeff y coordinate")?; + + let b_b_y = Fq::from_slice(&input[idx * 192 + 128..idx * 192 + 160]) + .map_err(|_| "Invalid b argument real coeff x coordinate")?; + + let b_b_x = Fq::from_slice(&input[idx * 192 + 160..idx * 192 + 192]) + .map_err(|_| "Invalid b argument real coeff y coordinate")?; + + let b_a = Fq2::new(b_a_x, b_a_y); + let b_b = Fq2::new(b_b_x, b_b_y); + let b = if b_a.is_zero() && b_b.is_zero() { + G2::zero() + } else { + G2::from( + AffineG2::new(b_a, b_b).map_err(|_| "Invalid b argument - not on curve")?, + ) + }; + let a = if a_x.is_zero() && a_y.is_zero() { + G1::zero() + } else { + G1::from( + AffineG1::new(a_x, a_y).map_err(|_| "Invalid a argument - not on curve")?, + ) + }; + vals.push((a, b)); + } + + let mul = vals + .into_iter() + .fold(Gt::one(), |s, (a, b)| s * pairing(a, b)); + + if mul == Gt::one() { + U256::one() + } else { + U256::zero() + } + }; + + let mut buf = [0u8; 32]; + ret_val.to_big_endian(&mut buf); + output.write(0, &buf); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::{ + modexp as me, BTreeMap, Bls12ConstOperations, Bls12PairingPrice, Bls12PairingPricer, + Builtin, EthereumBuiltin, FromStr, Implementation, Linear, ModexpPricer, Pricing, + }; + use ethereum_types::U256; + use ethjson::spec::builtin::{ + AltBn128Pairing as JsonAltBn128PairingPricing, Builtin as JsonBuiltin, + Linear as JsonLinearPricing, Pricing as JsonPricing, PricingAt, + }; + use hex_literal::hex; + use macros::map; + use maplit::btreemap; + use num::{BigUint, One, Zero}; + use parity_bytes::BytesRef; + use std::convert::TryFrom; + + #[test] + fn blake2f_cost() { + let f = Builtin { + pricer: map![0 => Pricing::Blake2F(123)], + native: EthereumBuiltin::from_str("blake2_f").unwrap(), + }; + // 5 rounds + let input = hex!("0000000548c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); + let mut output = [0u8; 64]; + f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) + .unwrap(); + + assert_eq!(f.cost(&input[..], 0), U256::from(123 * 5)); + } + + #[test] + fn blake2f_cost_on_invalid_length() { + let f = Builtin { + pricer: map![0 => Pricing::Blake2F(123)], + native: EthereumBuiltin::from_str("blake2_f").expect("known builtin"), + }; + // invalid input (too short) + let input = hex!("00"); + + assert_eq!(f.cost(&input[..], 0), U256::from(0)); + } + + #[test] + fn blake2_f_is_err_on_invalid_length() { + let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); + // Test vector 1 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-1 + let input = hex!("00000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); + let mut out = [0u8; 64]; + + let result = blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err(), + "input length for Blake2 F precompile should be exactly 213 bytes" + ); + } + + #[test] + fn blake2_f_is_err_on_invalid_length_2() { + let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); + // Test vector 2 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-2 + let input = hex!("000000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); + let mut out = [0u8; 64]; + + let result = blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err(), + "input length for Blake2 F precompile should be exactly 213 bytes" + ); + } + + #[test] + fn blake2_f_is_err_on_bad_finalization_flag() { + let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); + // Test vector 3 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-3 + let input = hex!("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000002"); + let mut out = [0u8; 64]; + + let result = blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "incorrect final block indicator flag"); + } + + #[test] + fn blake2_f_zero_rounds_is_ok_test_vector_4() { + let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); + // Test vector 4 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-4 + let input = hex!("0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); + let expected = hex!("08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b"); + let mut output = [0u8; 64]; + blake2 + .execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) + .unwrap(); + assert_eq!(&output[..], &expected[..]); + } + + #[test] + fn blake2_f_test_vector_5() { + let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); + // Test vector 5 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-5 + let input = hex!("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); + let expected = hex!("ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"); + let mut out = [0u8; 64]; + blake2 + .execute(&input[..], &mut BytesRef::Fixed(&mut out[..])) + .unwrap(); + assert_eq!(&out[..], &expected[..]); + } + + #[test] + fn blake2_f_test_vector_6() { + let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); + // Test vector 6 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-6 + let input = hex!("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000"); + let expected = hex!("75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735"); + let mut out = [0u8; 64]; + blake2 + .execute(&input[..], &mut BytesRef::Fixed(&mut out[..])) + .unwrap(); + assert_eq!(&out[..], &expected[..]); + } + + #[test] + fn blake2_f_test_vector_7() { + let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); + // Test vector 7 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-7 + let input = hex!("0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); + let expected = hex!("b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421"); + let mut out = [0u8; 64]; + blake2 + .execute(&input[..], &mut BytesRef::Fixed(&mut out[..])) + .unwrap(); + assert_eq!(&out[..], &expected[..]); + } + + #[ignore] + #[test] + fn blake2_f_test_vector_8() { + let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); + // Test vector 8 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-8 + // Note this test is slow, 4294967295/0xffffffff rounds take a while. + let input = hex!("ffffffff48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); + let expected = hex!("fc59093aafa9ab43daae0e914c57635c5402d8e3d2130eb9b3cc181de7f0ecf9b22bf99a7815ce16419e200e01846e6b5df8cc7703041bbceb571de6631d2615"); + let mut out = [0u8; 64]; + blake2 + .execute(&input[..], &mut BytesRef::Fixed(&mut out[..])) + .unwrap(); + assert_eq!(&out[..], &expected[..]); + } + + #[test] + fn modexp_func() { + // n^0 % m == 1 + let mut base = BigUint::parse_bytes(b"12345", 10).unwrap(); + let mut exp = BigUint::zero(); + let mut modulus = BigUint::parse_bytes(b"789", 10).unwrap(); + assert_eq!(me(base, exp.to_bytes_be(), modulus), BigUint::one()); + + // 0^n % m == 0 + base = BigUint::zero(); + exp = BigUint::parse_bytes(b"12345", 10).unwrap(); + modulus = BigUint::parse_bytes(b"789", 10).unwrap(); + assert_eq!(me(base, exp.to_bytes_be(), modulus), BigUint::zero()); + + // n^m % 1 == 0 + base = BigUint::parse_bytes(b"12345", 10).unwrap(); + exp = BigUint::parse_bytes(b"789", 10).unwrap(); + modulus = BigUint::one(); + assert_eq!(me(base, exp.to_bytes_be(), modulus), BigUint::zero()); + + // if n % d == 0, then n^m % d == 0 + base = BigUint::parse_bytes(b"12345", 10).unwrap(); + exp = BigUint::parse_bytes(b"789", 10).unwrap(); + modulus = BigUint::parse_bytes(b"15", 10).unwrap(); + assert_eq!(me(base, exp.to_bytes_be(), modulus), BigUint::zero()); + + // others + base = BigUint::parse_bytes(b"12345", 10).unwrap(); + exp = BigUint::parse_bytes(b"789", 10).unwrap(); + modulus = BigUint::parse_bytes(b"97", 10).unwrap(); + assert_eq!( + me(base, exp.to_bytes_be(), modulus), + BigUint::parse_bytes(b"55", 10).unwrap() + ); + } + + #[test] + fn identity() { + let f = EthereumBuiltin::from_str("identity").unwrap(); + let i = [0u8, 1, 2, 3]; + + let mut o2 = [255u8; 2]; + f.execute(&i[..], &mut BytesRef::Fixed(&mut o2[..])) + .expect("Builtin should not fail"); + assert_eq!(i[0..2], o2); + + let mut o4 = [255u8; 4]; + f.execute(&i[..], &mut BytesRef::Fixed(&mut o4[..])) + .expect("Builtin should not fail"); + assert_eq!(i, o4); + + let mut o8 = [255u8; 8]; + f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])) + .expect("Builtin should not fail"); + assert_eq!(i, o8[..4]); + assert_eq!([255u8; 4], o8[4..]); + } + + #[test] + fn sha256() { + let f = EthereumBuiltin::from_str("sha256").unwrap(); + let i = [0u8; 0]; + + let mut o = [255u8; 32]; + f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])) + .expect("Builtin should not fail"); + assert_eq!( + &o[..], + hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") + ); + + let mut o8 = [255u8; 8]; + f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])) + .expect("Builtin should not fail"); + assert_eq!(&o8[..], hex!("e3b0c44298fc1c14")); + + let mut o34 = [255u8; 34]; + f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..])) + .expect("Builtin should not fail"); + assert_eq!( + &o34[..], + &hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855ffff")[..] + ); + + let mut ov = vec![]; + f.execute(&i[..], &mut BytesRef::Flexible(&mut ov)) + .expect("Builtin should not fail"); + assert_eq!( + &ov[..], + &hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")[..] + ); + } + + #[test] + fn ripemd160() { + let f = EthereumBuiltin::from_str("ripemd160").unwrap(); + let i = [0u8; 0]; + + let mut o = [255u8; 32]; + f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])) + .expect("Builtin should not fail"); + assert_eq!( + &o[..], + &hex!("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31")[..] + ); + + let mut o8 = [255u8; 8]; + f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])) + .expect("Builtin should not fail"); + assert_eq!(&o8[..], &hex!("0000000000000000")[..]); + + let mut o34 = [255u8; 34]; + f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..])) + .expect("Builtin should not fail"); + assert_eq!( + &o34[..], + &hex!("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31ffff")[..] + ); + } + + #[test] + fn ecrecover() { + let f = EthereumBuiltin::from_str("ecrecover").unwrap(); + + let i = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03"); + + let mut o = [255u8; 32]; + f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])) + .expect("Builtin should not fail"); + assert_eq!( + &o[..], + &hex!("000000000000000000000000c08b5542d177ac6686946920409741463a15dddb")[..] + ); + + let mut o8 = [255u8; 8]; + f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])) + .expect("Builtin should not fail"); + assert_eq!(&o8[..], &hex!("0000000000000000")[..]); + + let mut o34 = [255u8; 34]; + f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..])) + .expect("Builtin should not fail"); + assert_eq!( + &o34[..], + &hex!("000000000000000000000000c08b5542d177ac6686946920409741463a15dddbffff")[..] + ); + + let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001a650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03"); + let mut o = [255u8; 32]; + f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])) + .expect("Builtin should not fail"); + assert_eq!( + &o[..], + &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..] + ); + + let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000"); + let mut o = [255u8; 32]; + f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])) + .expect("Builtin should not fail"); + assert_eq!( + &o[..], + &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..] + ); + + let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b"); + let mut o = [255u8; 32]; + f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])) + .expect("Builtin should not fail"); + assert_eq!( + &o[..], + &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..] + ); + + let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000001b"); + let mut o = [255u8; 32]; + f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])) + .expect("Builtin should not fail"); + assert_eq!( + &o[..], + &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..] + ); + + let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + let mut o = [255u8; 32]; + f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])) + .expect("Builtin should not fail"); + assert_eq!( + &o[..], + &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..] + ); + + // TODO: Should this (corrupted version of the above) fail rather than returning some address? + /* let i_bad = FromHex::from_hex("48173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap(); + let mut o = [255u8; 32]; + f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])); + assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);*/ + } + + #[test] + fn modexp() { + let f = Builtin { + pricer: map![0 => Pricing::Modexp(ModexpPricer { divisor: 20 })], + native: EthereumBuiltin::from_str("modexp").unwrap(), + }; + + // test for potential gas cost multiplication overflow + { + let input = hex!("0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000003b27bafd00000000000000000000000000000000000000000000000000000000503c8ac3"); + let expected_cost = U256::max_value(); + assert_eq!(f.cost(&input[..], 0), expected_cost); + } + + // test for potential exp len overflow + { + let input = hex!( + " + 00000000000000000000000000000000000000000000000000000000000000ff + 2a1e530000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000" + ); + + let mut output = vec![0u8; 32]; + let expected = hex!("0000000000000000000000000000000000000000000000000000000000000000"); + let expected_cost = U256::max_value(); + + f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) + .expect("Builtin should fail"); + assert_eq!(output, expected); + assert_eq!(f.cost(&input[..], 0), expected_cost); + } + + // fermat's little theorem example. + { + let input = hex!( + " + 0000000000000000000000000000000000000000000000000000000000000001 + 0000000000000000000000000000000000000000000000000000000000000020 + 0000000000000000000000000000000000000000000000000000000000000020 + 03 + fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e + fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" + ); + + let mut output = vec![0u8; 32]; + let expected = hex!("0000000000000000000000000000000000000000000000000000000000000001"); + let expected_cost = 13056; + + f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) + .expect("Builtin should not fail"); + assert_eq!(output, expected); + assert_eq!(f.cost(&input[..], 0), expected_cost.into()); + } + + // second example from EIP: zero base. + { + let input = hex!( + " + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000020 + 0000000000000000000000000000000000000000000000000000000000000020 + fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e + fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" + ); + + let mut output = vec![0u8; 32]; + let expected = hex!("0000000000000000000000000000000000000000000000000000000000000000"); + let expected_cost = 13056; + + f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) + .expect("Builtin should not fail"); + assert_eq!(output, expected); + assert_eq!(f.cost(&input[..], 0), expected_cost.into()); + } + + // another example from EIP: zero-padding + { + let input = hex!( + " + 0000000000000000000000000000000000000000000000000000000000000001 + 0000000000000000000000000000000000000000000000000000000000000002 + 0000000000000000000000000000000000000000000000000000000000000020 + 03 + ffff + 80" + ); + + let mut output = vec![0u8; 32]; + let expected = hex!("3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab"); + let expected_cost = 768; + + f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) + .expect("Builtin should not fail"); + assert_eq!(output, expected); + assert_eq!(f.cost(&input[..], 0), expected_cost.into()); + } + + // zero-length modulus. + { + let input = hex!( + " + 0000000000000000000000000000000000000000000000000000000000000001 + 0000000000000000000000000000000000000000000000000000000000000002 + 0000000000000000000000000000000000000000000000000000000000000000 + 03 + ffff" + ); + + let mut output = vec![]; + let expected_cost = 0; + + f.execute(&input[..], &mut BytesRef::Flexible(&mut output)) + .expect("Builtin should not fail"); + assert_eq!(output.len(), 0); // shouldn't have written any output. + assert_eq!(f.cost(&input[..], 0), expected_cost.into()); + } + } + + #[test] + fn bn128_add() { + let f = Builtin { + pricer: map![0 => Pricing::Linear(Linear { base: 0, word: 0 })], + native: EthereumBuiltin::from_str("alt_bn128_add").unwrap(), + }; + + // zero-points additions + { + let input = hex!( + " + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000" + ); + + let mut output = vec![0u8; 64]; + let expected = hex!( + " + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000" + ); + + f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) + .expect("Builtin should not fail"); + assert_eq!(output, &expected[..]); + } + + // no input, should not fail + { + let mut empty = [0u8; 0]; + let input = BytesRef::Fixed(&mut empty); + + let mut output = vec![0u8; 64]; + let expected = hex!( + " + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000" + ); + + f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) + .expect("Builtin should not fail"); + assert_eq!(output, &expected[..]); + } + + // should fail - point not on curve + { + let input = hex!( + " + 1111111111111111111111111111111111111111111111111111111111111111 + 1111111111111111111111111111111111111111111111111111111111111111 + 1111111111111111111111111111111111111111111111111111111111111111 + 1111111111111111111111111111111111111111111111111111111111111111" + ); + + let mut output = vec![0u8; 64]; + + let res = f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])); + assert!(res.is_err(), "There should be built-in error here"); + } + } + + #[test] + fn bn128_mul() { + let f = Builtin { + pricer: map![0 => Pricing::Linear(Linear { base: 0, word: 0 })], + native: EthereumBuiltin::from_str("alt_bn128_mul").unwrap(), + }; + + // zero-point multiplication + { + let input = hex!( + " + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0200000000000000000000000000000000000000000000000000000000000000" + ); + + let mut output = vec![0u8; 64]; + let expected = hex!( + " + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000" + ); + + f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) + .expect("Builtin should not fail"); + assert_eq!(output, &expected[..]); + } + + // should fail - point not on curve + { + let input = hex!( + " + 1111111111111111111111111111111111111111111111111111111111111111 + 1111111111111111111111111111111111111111111111111111111111111111 + 0f00000000000000000000000000000000000000000000000000000000000000" + ); + + let mut output = vec![0u8; 64]; + + let res = f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])); + assert!(res.is_err(), "There should be built-in error here"); + } + } + + fn builtin_pairing() -> Builtin { + Builtin { + pricer: map![0 => Pricing::Linear(Linear { base: 0, word: 0 })], + native: EthereumBuiltin::from_str("alt_bn128_pairing").unwrap(), + } + } + + fn empty_test(f: Builtin, expected: Vec) { + let mut empty = [0u8; 0]; + let input = BytesRef::Fixed(&mut empty); + + let mut output = vec![0u8; expected.len()]; + + f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) + .expect("Builtin should not fail"); + assert_eq!(output, expected); + } + + fn error_test(f: Builtin, input: &[u8], msg_contains: Option<&str>) { + let mut output = vec![0u8; 64]; + let res = f.execute(input, &mut BytesRef::Fixed(&mut output[..])); + if let Some(msg) = msg_contains { + if let Err(e) = res { + if !e.contains(msg) { + panic!( + "There should be error containing '{}' here, but got: '{}'", + msg, e + ); + } + } + } else { + assert!(res.is_err(), "There should be built-in error here"); + } + } + + #[test] + fn bn128_pairing_empty() { + // should not fail, because empty input is a valid input of 0 elements + empty_test( + builtin_pairing(), + hex!("0000000000000000000000000000000000000000000000000000000000000001").to_vec(), + ); + } + + #[test] + fn bn128_pairing_notcurve() { + // should fail - point not on curve + error_test( + builtin_pairing(), + &hex!( + " + 1111111111111111111111111111111111111111111111111111111111111111 + 1111111111111111111111111111111111111111111111111111111111111111 + 1111111111111111111111111111111111111111111111111111111111111111 + 1111111111111111111111111111111111111111111111111111111111111111 + 1111111111111111111111111111111111111111111111111111111111111111 + 1111111111111111111111111111111111111111111111111111111111111111" + ), + Some("not on curve"), + ); + } + + #[test] + fn bn128_pairing_fragmented() { + // should fail - input length is invalid + error_test( + builtin_pairing(), + &hex!( + " + 1111111111111111111111111111111111111111111111111111111111111111 + 1111111111111111111111111111111111111111111111111111111111111111 + 111111111111111111111111111111" + ), + Some("Invalid input length"), + ); + } + + #[test] + #[should_panic] + fn from_unknown_linear() { + let _ = EthereumBuiltin::from_str("foo").unwrap(); + } + + #[test] + fn is_active() { + let pricer = Pricing::Linear(Linear { base: 10, word: 20 }); + let b = Builtin { + pricer: map![100_000 => pricer], + native: EthereumBuiltin::from_str("identity").unwrap(), + }; + + assert!(!b.is_active(99_999)); + assert!(b.is_active(100_000)); + assert!(b.is_active(100_001)); + } + + #[test] + fn from_named_linear() { + let pricer = Pricing::Linear(Linear { base: 10, word: 20 }); + let b = Builtin { + pricer: map![0 => pricer], + native: EthereumBuiltin::from_str("identity").unwrap(), + }; + + assert_eq!(b.cost(&[0; 0], 0), U256::from(10)); + assert_eq!(b.cost(&[0; 1], 0), U256::from(30)); + assert_eq!(b.cost(&[0; 32], 0), U256::from(30)); + assert_eq!(b.cost(&[0; 33], 0), U256::from(50)); + + let i = [0u8, 1, 2, 3]; + let mut o = [255u8; 4]; + b.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])) + .expect("Builtin should not fail"); + assert_eq!(i, o); + } + + #[test] + fn from_json() { + let b = Builtin::try_from(ethjson::spec::Builtin { + name: "identity".to_owned(), + pricing: map![ + 0 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { base: 10, word: 20 }) + } + ], + }) + .expect("known builtin"); + + assert_eq!(b.cost(&[0; 0], 0), U256::from(10)); + assert_eq!(b.cost(&[0; 1], 0), U256::from(30)); + assert_eq!(b.cost(&[0; 32], 0), U256::from(30)); + assert_eq!(b.cost(&[0; 33], 0), U256::from(50)); + + let i = [0u8, 1, 2, 3]; + let mut o = [255u8; 4]; + b.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])) + .expect("Builtin should not fail"); + assert_eq!(i, o); + } + + #[test] + fn bn128_pairing_eip1108_transition() { + let b = Builtin::try_from(JsonBuiltin { + name: "alt_bn128_pairing".to_owned(), + pricing: map![ + 10 => PricingAt { + info: None, + price: JsonPricing::AltBn128Pairing(JsonAltBn128PairingPricing { + base: 100_000, + pair: 80_000, + }), + }, + 20 => PricingAt { + info: None, + price: JsonPricing::AltBn128Pairing(JsonAltBn128PairingPricing { + base: 45_000, + pair: 34_000, + }), + } + ], + }) + .unwrap(); + + assert_eq!( + b.cost(&[0; 192 * 3], 10), + U256::from(340_000), + "80 000 * 3 + 100 000 == 340 000" + ); + assert_eq!( + b.cost(&[0; 192 * 7], 20), + U256::from(283_000), + "34 000 * 7 + 45 000 == 283 000" + ); + } + + #[test] + fn bn128_add_eip1108_transition() { + let b = Builtin::try_from(JsonBuiltin { + name: "alt_bn128_add".to_owned(), + pricing: map![ + 10 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { + base: 500, + word: 0, + }), + }, + 20 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { + base: 150, + word: 0, + }), + } + ], + }) + .unwrap(); + + assert_eq!(b.cost(&[0; 192], 10), U256::from(500)); + assert_eq!( + b.cost(&[0; 10], 20), + U256::from(150), + "after istanbul hardfork gas cost for add should be 150" + ); + } + + #[test] + fn bn128_mul_eip1108_transition() { + let b = Builtin::try_from(JsonBuiltin { + name: "alt_bn128_mul".to_owned(), + pricing: map![ + 10 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { + base: 40_000, + word: 0, + }), + }, + 20 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { + base: 6_000, + word: 0, + }), + } + ], + }) + .unwrap(); + + assert_eq!(b.cost(&[0; 192], 10), U256::from(40_000)); + assert_eq!( + b.cost(&[0; 10], 20), + U256::from(6_000), + "after istanbul hardfork gas cost for mul should be 6 000" + ); + } + + #[test] + fn multimap_use_most_recent_on_activate() { + let b = Builtin::try_from(JsonBuiltin { + name: "alt_bn128_mul".to_owned(), + pricing: map![ + 10 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { + base: 40_000, + word: 0, + }), + }, + 20 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { + base: 6_000, + word: 0, + }) + }, + 100 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { + base: 1_337, + word: 0, + }) + } + ], + }) + .unwrap(); + + assert_eq!( + b.cost(&[0; 2], 0), + U256::zero(), + "not activated yet; should be zero" + ); + assert_eq!(b.cost(&[0; 3], 10), U256::from(40_000), "use price #1"); + assert_eq!(b.cost(&[0; 4], 20), U256::from(6_000), "use price #2"); + assert_eq!(b.cost(&[0; 1], 99), U256::from(6_000), "use price #2"); + assert_eq!(b.cost(&[0; 1], 100), U256::from(1_337), "use price #3"); + assert_eq!( + b.cost(&[0; 1], u64::max_value()), + U256::from(1_337), + "use price #3 indefinitely" + ); + } + + #[test] + fn multimap_use_last_with_same_activate_at() { + let b = Builtin::try_from(JsonBuiltin { + name: "alt_bn128_mul".to_owned(), + pricing: map![ + 1 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { + base: 40_000, + word: 0, + }), + }, + 1 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { + base: 6_000, + word: 0, + }), + }, + 1 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { + base: 1_337, + word: 0, + }), + } + ], + }) + .unwrap(); + + assert_eq!(b.cost(&[0; 1], 0), U256::from(0), "not activated yet"); + assert_eq!(b.cost(&[0; 1], 1), U256::from(1_337)); + } + + #[test] + fn bls12_381_g1_add() { + let f = Builtin { + pricer: btreemap![0 => Pricing::Bls12ConstOperations(Bls12ConstOperations{price: 1})], + native: EthereumBuiltin::from_str("bls12_381_g1_add").unwrap(), + }; + + let input = hex!(" + 00000000000000000000000000000000117dbe419018f67844f6a5e1b78a1e597283ad7b8ee7ac5e58846f5a5fd68d0da99ce235a91db3ec1cf340fe6b7afcdb + 0000000000000000000000000000000013316f23de032d25e912ae8dc9b54c8dba1be7cecdbb9d2228d7e8f652011d46be79089dd0a6080a73c82256ce5e4ed2 + 000000000000000000000000000000000441e7f7f96198e4c23bd5eb16f1a7f045dbc8c53219ab2bcea91d3a027e2dfe659feac64905f8b9add7e4bfc91bec2b + 0000000000000000000000000000000005fc51bb1b40c87cd4292d4b66f8ca5ce4ef9abd2b69d4464b4879064203bda7c9fc3f896a3844ebc713f7bb20951d95 + "); + let expected = hex!(" + 0000000000000000000000000000000016b8ab56b45a9294466809b8e858c1ad15ad0d52cfcb62f8f5753dc94cee1de6efaaebce10701e3ec2ecaa9551024ea + 600000000000000000000000000000000124571eec37c0b1361023188d66ec17c1ec230d31b515e0e81e599ec19e40c8a7c8cdea9735bc3d8b4e37ca7e5dd71f6 + "); + + let mut output = [0u8; 128]; + + f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) + .expect("Builtin should not fail"); + assert_eq!(&output[..], &expected[..]); + } + + #[test] + fn bls12_381_g1_mul() { + let f = Builtin { + pricer: btreemap![0 => Pricing::Bls12ConstOperations(Bls12ConstOperations{price: 1})], + native: EthereumBuiltin::from_str("bls12_381_g1_mul").unwrap(), + }; + + let input = hex!(" + 000000000000000000000000000000000b3a1dfe2d1b62538ed49648cb2a8a1d66bdc4f7a492eee59942ab810a306876a7d49e5ac4c6bb1613866c158ded993e + 000000000000000000000000000000001300956110f47ca8e2aacb30c948dfd046bf33f69bf54007d76373c5a66019454da45e3cf14ce2b9d53a50c9b4366aa3 + ac23d04ee3acc757aae6795532ce4c9f34534e506a4d843a26b052a040c79659 + "); + let expected = hex!(" + 000000000000000000000000000000001227b7021e9d3dc8bcbf5b346fc503f7f8576965769c5e22bb70056eef03c84b8c80290ae9ce20345770290c55549bce + 00000000000000000000000000000000188ddbbfb4ad2d34a8d3dc0ec92b70b63caa73ad7dea0cc9740bac2309b4bb11107912bd086379746e9a9bcd26d4db58 + "); + + let mut output = [0u8; 128]; + + f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) + .expect("Builtin should not fail"); + assert_eq!(&output[..], &expected[..]); + } + + #[test] + fn bls12_381_g1_multiexp() { + let f = Builtin { + pricer: btreemap![0 => Pricing::Bls12ConstOperations(Bls12ConstOperations{price: 1})], + native: EthereumBuiltin::from_str("bls12_381_g1_multiexp").unwrap(), + }; + let input = hex!(" + 0000000000000000000000000000000012196c5a43d69224d8713389285f26b98f86ee910ab3dd668e413738282003cc5b7357af9a7af54bb713d62255e80f56 + 0000000000000000000000000000000006ba8102bfbeea4416b710c73e8cce3032c31c6269c44906f8ac4f7874ce99fb17559992486528963884ce429a992fee + b3c940fe79b6966489b527955de7599194a9ac69a6ff58b8d99e7b1084f0464e + 00000000000000000000000000000000117dbe419018f67844f6a5e1b78a1e597283ad7b8ee7ac5e58846f5a5fd68d0da99ce235a91db3ec1cf340fe6b7afcdb + 0000000000000000000000000000000013316f23de032d25e912ae8dc9b54c8dba1be7cecdbb9d2228d7e8f652011d46be79089dd0a6080a73c82256ce5e4ed2 + 4d0e25bf3f6fc9f4da25d21fdc71773f1947b7a8a775b8177f7eca990b05b71d + 0000000000000000000000000000000008ab7b556c672db7883ec47efa6d98bb08cec7902ebb421aac1c31506b177ac444ffa2d9b400a6f1cbdc6240c607ee11 + 0000000000000000000000000000000016b7fa9adf4addc2192271ce7ad3c8d8f902d061c43b7d2e8e26922009b777855bffabe7ed1a09155819eabfa87f276f + 973f40c12c92b703d7b7848ef8b4466d40823aad3943a312b57432b91ff68be1 + 0000000000000000000000000000000015ff9a232d9b5a8020a85d5fe08a1dcfb73ece434258fe0e2fddf10ddef0906c42dcb5f5d62fc97f934ba900f17beb33 + 0000000000000000000000000000000009cfe4ee2241d9413c616462d7bac035a6766aeaab69c81e094d75b840df45d7e0dfac0265608b93efefb9a8728b98e4 + 4c51f97bcdda93904ae26991b471e9ea942e2b5b8ed26055da11c58bc7b5002a + 0000000000000000000000000000000017a17b82e3bfadf3250210d8ef572c02c3610d65ab4d7366e0b748768a28ee6a1b51f77ed686a64f087f36f641e7dca9 + 00000000000000000000000000000000077ea73d233ccea51dc4d5acecf6d9332bf17ae51598f4b394a5f62fb387e9c9aa1d6823b64a074f5873422ca57545d3 + 8964d5867927bc3e35a0b4c457482373969bff5edff8a781d65573e07fd87b89 + 000000000000000000000000000000000c1243478f4fbdc21ea9b241655947a28accd058d0cdb4f9f0576d32f09dddaf0850464550ff07cab5927b3e4c863ce9 + 0000000000000000000000000000000015fb54db10ffac0b6cd374eb7168a8cb3df0a7d5f872d8e98c1f623deb66df5dd08ff4c3658f2905ec8bd02598bd4f90 + 787c38b944eadbd03fd3187f450571740f6cd00e5b2e560165846eb800e5c944 + 000000000000000000000000000000000328f09584b6d6c98a709fc22e184123994613aca95a28ac53df8523b92273eb6f4e2d9b2a7dcebb474604d54a210719 + 000000000000000000000000000000001220ebde579911fe2e707446aaad8d3789fae96ae2e23670a4fd856ed82daaab704779eb4224027c1ed9460f39951a1b + aaee7ae2a237e8e53560c79e7baa9adf9c00a0ea4d6f514e7a6832eb15cef1e1 + 0000000000000000000000000000000002ebfa98aa92c32a29ebe17fcb1819ba82e686abd9371fcee8ea793b4c72b6464085044f818f1f5902396df0122830cb + 00000000000000000000000000000000001184715b8432ed190b459113977289a890f68f6085ea111466af15103c9c02467da33e01d6bff87fd57db6ccba442a + dac6ed3ef45c1d7d3028f0f89e5458797996d3294b95bebe049b76c7d0db317c + 0000000000000000000000000000000009d6424e002439998e91cd509f85751ad25e574830c564e7568347d19e3f38add0cab067c0b4b0801785a78bcbeaf246 + 000000000000000000000000000000000ef6d7db03ee654503b46ff0dbc3297536a422e963bda9871a8da8f4eeb98dedebd6071c4880b4636198f4c2375dc795 + bb30985756c3ca075114c92f231575d6befafe4084517f1166a47376867bd108 + 0000000000000000000000000000000002d1cdb93191d1f9f0308c2c55d0208a071f5520faca7c52ab0311dbc9ba563bd33b5dd6baa77bf45ac2c3269e945f48 + 00000000000000000000000000000000072a52106e6d7b92c594c4dacd20ef5fab7141e45c231457cd7e71463b2254ee6e72689e516fa6a8f29f2a173ce0a190 + fb730105809f64ea522983d6bbb62f7e2e8cbf702685e9be10e2ef71f8187672 + 0000000000000000000000000000000000641642f6801d39a09a536f506056f72a619c50d043673d6d39aa4af11d8e3ded38b9c3bbc970dbc1bd55d68f94b50d + 0000000000000000000000000000000009ab050de356a24aea90007c6b319614ba2f2ed67223b972767117769e3c8e31ee4056494628fb2892d3d37afb6ac943 + b6a9408625b0ca8fcbfb21d34eec2d8e24e9a30d2d3b32d7a37d110b13afbfea + 000000000000000000000000000000000fd4893addbd58fb1bf30b8e62bef068da386edbab9541d198e8719b2de5beb9223d87387af82e8b55bd521ff3e47e2d + 000000000000000000000000000000000f3a923b76473d5b5a53501790cb02597bb778bdacb3805a9002b152d22241ad131d0f0d6a260739cbab2c2fe602870e + 3b77283d0a7bb9e17a27e66851792fdd605cc0a339028b8985390fd024374c76 + 0000000000000000000000000000000002cb4b24c8aa799fd7cb1e4ab1aab1372113200343d8526ea7bc64dfaf926baf5d90756a40e35617854a2079cd07fba4 + 0000000000000000000000000000000003327ca22bd64ebd673cc6d5b02b2a8804d5353c9d251637c4273ad08d581cc0d58da9bea27c37a0b3f4961dbafd276b + dd994eae929aee7428fdda2e44f8cb12b10b91c83b22abc8bbb561310b62257c + 00000000000000000000000000000000024ad70f2b2105ca37112858e84c6f5e3ffd4a8b064522faae1ecba38fabd52a6274cb46b00075deb87472f11f2e67d9 + 0000000000000000000000000000000010a502c8b2a68aa30d2cb719273550b9a3c283c35b2e18a01b0b765344ffaaa5cb30a1e3e6ecd3a53ab67658a5787681 + 7010b134989c8368c7f831f9dd9f9a890e2c1435681107414f2e8637153bbf6a + 0000000000000000000000000000000000704cc57c8e0944326ddc7c747d9e7347a7f6918977132eea269f161461eb64066f773352f293a3ac458dc3ccd5026a + 000000000000000000000000000000001099d3c2bb2d082f2fdcbed013f7ac69e8624f4fcf6dfab3ee9dcf7fbbdb8c49ee79de40e887c0b6828d2496e3a6f768 + 94c68bc8d91ac8c489ee87dbfc4b94c93c8bbd5fc04c27db8b02303f3a659054 + 00000000000000000000000000000000130535a29392c77f045ac90e47f2e7b3cffff94494fe605aad345b41043f6663ada8e2e7ecd3d06f3b8854ef92212f42 + 000000000000000000000000000000001699a3cc1f10cd2ed0dc68eb916b4402e4f12bf4746893bf70e26e209e605ea89e3d53e7ac52bd07713d3c8fc671931d + b3682accc3939283b870357cf83683350baf73aa0d3d68bda82a0f6ae7e51746 + "); + let expected = hex!(" + 000000000000000000000000000000000b370fc4ca67fb0c3c270b1b4c4816ef953cd9f7cf6ad20e88099c40aace9c4bb3f4cd215e5796f65080c69c9f4d2a0f + 0000000000000000000000000000000007203220935ddc0190e2d7a99ec3f9231da550768373f9a5933dffd366f48146f8ea5fe5dee6539d925288083bb5a8f1 + "); + + let mut output = [0u8; 128]; + + f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) + .expect("Builtin should not fail"); + assert_eq!(&output[..], &expected[..]); + } + + #[test] + fn bls12_381_g2_add() { + let f = Builtin { + pricer: btreemap![0 => Pricing::Bls12ConstOperations(Bls12ConstOperations{price: 1})], + native: EthereumBuiltin::from_str("bls12_381_g2_add").unwrap(), + }; + let input = hex!(" + 00000000000000000000000000000000161c595d151a765c7dee03c9210414cdffab84b9078b4b98f9df09be5ec299b8f6322c692214f00ede97958f235c352b + 00000000000000000000000000000000106883e0937cb869e579b513bde8f61020fcf26be38f8b98eae3885cedec2e028970415fc653cf10e64727b7f6232e06 + 000000000000000000000000000000000f351a82b733af31af453904874b7ca6252957a1ab51ec7f7b6fff85bbf3331f870a7e72a81594a9930859237e7a154d + 0000000000000000000000000000000012fcf20d1750901f2cfed64fd362f010ee64fafe9ddab406cc352b65829b929881a50514d53247d1cca7d6995d0bc9b2 + 00000000000000000000000000000000148b7dfc21521d79ff817c7a0305f1048851e283be13c07d5c04d28b571d48172838399ba539529e8d037ffd1f729558 + 0000000000000000000000000000000003015abea326c15098f5205a8b2d3cd74d72dac59d60671ca6ef8c9c714ea61ffdacd46d1024b5b4f7e6b3b569fabaf2 + 0000000000000000000000000000000011f0c512fe7dc2dd8abdc1d22c2ecd2e7d1b84f8950ab90fc93bf54badf7bb9a9bad8c355d52a5efb110dca891e4cc3d + 0000000000000000000000000000000019774010814d1d94caf3ecda3ef4f5c5986e966eaf187c32a8a5a4a59452af0849690cf71338193f2d8435819160bcfb + "); + let expected = hex!(" + 000000000000000000000000000000000383ab7a17cc57e239e874af3f1aaabba0e64625b848676712f05f56132dbbd1cadfabeb3fe1f461daba3f1720057ddd + 00000000000000000000000000000000096967e9b3747f1b8e344535eaa0c51e70bc77412bfaa2a7ce76f11f570c9febb8f4227316866a416a50436d098e6f9a + 000000000000000000000000000000001079452b7519a7b090d668d54c266335b1cdd1080ed867dd17a2476b11c2617da829bf740e51cb7dfd60d73ed02c0c67 + 00000000000000000000000000000000015fc3a972e05cbd9014882cfe6f2f16d0291c403bf28b05056ac625e4f71dfb1295c85d73145ef554614e6eb2d5bf02 + "); + + let mut output = [0u8; 256]; + + f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) + .expect("Builtin should not fail"); + assert_eq!(&output[..], &expected[..]); + } + + #[test] + fn bls12_381_g2_mul() { + let f = Builtin { + pricer: btreemap![0 => Pricing::Bls12ConstOperations(Bls12ConstOperations{price: 1})], + native: EthereumBuiltin::from_str("bls12_381_g2_mul").unwrap(), + }; + + let input = hex!(" + 00000000000000000000000000000000159da74f15e4c614b418997f81a1b8a3d9eb8dd80d94b5bad664bff271bb0f2d8f3c4ceb947dc6300d5003a2f7d7a829 + 000000000000000000000000000000000cdd4d1d4666f385dd54052cf5c1966328403251bebb29f0d553a9a96b5ade350c8493270e9b5282d8a06f9fa8d7b1d9 + 00000000000000000000000000000000189f8d3c94fdaa72cc67a7f93d35f91e22206ff9e97eed9601196c28d45b69c802ae92bcbf582754717b0355e08d37c0 + 00000000000000000000000000000000054b0a282610f108fc7f6736b8c22c8778d082bf4b0d0abca5a228198eba6a868910dd5c5c440036968e977955054196 + b6a9408625b0ca8fcbfb21d34eec2d8e24e9a30d2d3b32d7a37d110b13afbfea + "); + let expected = hex!(" + 000000000000000000000000000000000b24adeb2ca184c9646cb39f45e0cf8711e10bf308ddae06519562b0af3b43be44c2fcb90622726f7446ed690551d30e + 00000000000000000000000000000000069467c3edc19416067f572c51740ba8e0e7380121ade98e38ce26d907a2bf3a4e82af2bd195b6c3b7c9b29218880531 + 000000000000000000000000000000000eb8c90d0727511be53ffcb6f3b144c07983ed4b76d31ab003e45b37c7bc1066910f5e29f5adad5757af979dd0d8351d + 0000000000000000000000000000000004760f8d814189dcd893949797a3c4f56f2b60964bba3a4fc741e7ead05eb886787b2502fc64b20363eeba44e65d0ca0 + "); + + let mut output = [0u8; 256]; + + f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) + .expect("Builtin should not fail"); + assert_eq!(&output[..], &expected[..]); + } + + #[test] + fn bls12_381_g2_multiexp() { + let f = Builtin { + pricer: btreemap![0 => Pricing::Bls12ConstOperations(Bls12ConstOperations{price: 1})], + native: EthereumBuiltin::from_str("bls12_381_g2_multiexp").unwrap(), + }; + + let input = hex!(" + 00000000000000000000000000000000039b10ccd664da6f273ea134bb55ee48f09ba585a7e2bb95b5aec610631ac49810d5d616f67ba0147e6d1be476ea220e + 0000000000000000000000000000000000fbcdff4e48e07d1f73ec42fe7eb026f5c30407cfd2f22bbbfe5b2a09e8a7bb4884178cb6afd1c95f80e646929d3004 + 0000000000000000000000000000000001ed3b0e71acb0adbf44643374edbf4405af87cfc0507db7e8978889c6c3afbe9754d1182e98ac3060d64994d31ef576 + 000000000000000000000000000000001681a2bf65b83be5a2ca50430949b6e2a099977482e9405b593f34d2ed877a3f0d1bddc37d0cec4d59d7df74b2b8f2df + b3c940fe79b6966489b527955de7599194a9ac69a6ff58b8d99e7b1084f0464e + 0000000000000000000000000000000018c0ada6351b70661f053365deae56910798bd2ace6e2bf6ba4192d1a229967f6af6ca1c9a8a11ebc0a232344ee0f6d6 + 000000000000000000000000000000000cc70a587f4652039d8117b6103858adcd9728f6aebe230578389a62da0042b7623b1c0436734f463cfdd187d2090324 + 0000000000000000000000000000000009f50bd7beedb23328818f9ffdafdb6da6a4dd80c5a9048ab8b154df3cad938ccede829f1156f769d9e149791e8e0cd9 + 00000000000000000000000000000000079ba50d2511631b20b6d6f3841e616e9d11b68ec3368cd60129d9d4787ab56c4e9145a38927e51c9cd6271d493d9388 + 4d0e25bf3f6fc9f4da25d21fdc71773f1947b7a8a775b8177f7eca990b05b71d + 0000000000000000000000000000000003632695b09dbf86163909d2bb25995b36ad1d137cf252860fd4bb6c95749e19eb0c1383e9d2f93f2791cb0cf6c8ed9d + 000000000000000000000000000000001688a855609b0bbff4452d146396558ff18777f329fd4f76a96859dabfc6a6f6977c2496280dbe3b1f8923990c1d6407 + 000000000000000000000000000000000c8567fee05d05af279adc67179468a29d7520b067dbb348ee315a99504f70a206538b81a457cce855f4851ad48b7e80 + 000000000000000000000000000000001238dcdfa80ea46e1500026ea5feadb421de4409f4992ffbf5ae59fa67fd82f38452642a50261b849e74b4a33eed70cc + 973f40c12c92b703d7b7848ef8b4466d40823aad3943a312b57432b91ff68be1 + 000000000000000000000000000000000149704960cccf9d5ea414c73871e896b1d4cf0a946b0db72f5f2c5df98d2ec4f3adbbc14c78047961bc9620cb6cfb59 + 00000000000000000000000000000000140c5d25e534fb1bfdc19ba4cecaabe619f6e0cd3d60b0f17dafd7bcd27b286d4f4477d00c5e1af22ee1a0c67fbf177c + 00000000000000000000000000000000029a1727041590b8459890de736df15c00d80ab007c3aee692ddcdf75790c9806d198e9f4502bec2f0a623491c3f877d + 0000000000000000000000000000000008a94c98baa9409151030d4fae2bd4a64c6f11ea3c99b9661fdaed226b9a7c2a7d609be34afda5d18b8911b6e015bf49 + 4c51f97bcdda93904ae26991b471e9ea942e2b5b8ed26055da11c58bc7b5002a + 000000000000000000000000000000001156d478661337478ab0cbc877a99d9e4d9824a2b3f605d41404d6b557b3ffabbf42635b0bbcb854cf9ed8b8637561a8 + 000000000000000000000000000000001147ed317d5642e699787a7b47e6795c9a8943a34a694007e44f8654ba96390cf19f010dcf695e22c21874022c6ce291 + 000000000000000000000000000000000c6dccdf920fd5e7fae284115511952633744c6ad94120d9cae6acda8a7c23c48bd912cba6c38de5159587e1e6cad519 + 000000000000000000000000000000001944227d462bc2e5dcc6f6db0f83dad411ba8895262836f975b2b91e06fd0e2138862162acc04e9e65050b34ccbd1a4e + 8964d5867927bc3e35a0b4c457482373969bff5edff8a781d65573e07fd87b89 + 0000000000000000000000000000000019c31e3ab8cc9c920aa8f56371f133b6cb8d7b0b74b23c0c7201aca79e5ae69dc01f1f74d2492dcb081895b17d106b4e + 000000000000000000000000000000001789b0d371bd63077ccde3dbbebf3531368feb775bced187fb31cc6821481664600978e323ff21085b8c08e0f21daf72 + 000000000000000000000000000000000009eacfe8f4a2a9bae6573424d07f42bd6af8a9d55f71476a7e3c7a4b2b898550c1e72ec13afd4eff22421a03af1d31 + 000000000000000000000000000000000410bd4ea74dcfa33f2976aa1b571c67cbb596ab10f76a8aaf4548f1097e55b3373bff02683f806cb84e1e0e877819e2 + 787c38b944eadbd03fd3187f450571740f6cd00e5b2e560165846eb800e5c944 + 00000000000000000000000000000000147f09986691f2e57073378e8bfd58804241eed7934f6adfe6d0a6bac4da0b738495778a303e52113e1c80e698476d50 + 000000000000000000000000000000000762348b84c92a8ca6de319cf1f8f11db296a71b90fe13e1e4bcd25903829c00a5d2ad4b1c8d98c37eaad7e042ab023d + 0000000000000000000000000000000011d1d94530d4a2daf0e902a5c3382cd135938557f94b04bccea5e16ea089c5e020e13524c854a316662bd68784fe31f3 + 00000000000000000000000000000000070828522bec75b6a492fd9bca7b54dac6fbbf4f0bc3179d312bb65c647439e3868e4d5b21af5a64c93aeee8a9b7e46e + aaee7ae2a237e8e53560c79e7baa9adf9c00a0ea4d6f514e7a6832eb15cef1e1 + 000000000000000000000000000000000690a0869204c8dced5ba0ce13554b2703a3f18afb8fa8fa1c457d79c58fdc25471ae85bafad52e506fc1917fc3becff + 0000000000000000000000000000000010f7dbb16f8571ede1cec79e3f9ea03ae6468d7285984713f19607f5cab902b9a6b7cbcfd900be5c2e407cc093ea0e67 + 00000000000000000000000000000000151caf87968433cb1f85fc1854c57049be22c26497a86bfbd66a2b3af121d894dba8004a17c6ff96a5843c2719fa32d1 + 0000000000000000000000000000000011f0270f2b039409f70392879bcc2c67c836c100cf9883d3dc48d7adbcd52037d270539e863a951acd47ecaa1ca4db12 + dac6ed3ef45c1d7d3028f0f89e5458797996d3294b95bebe049b76c7d0db317c + 0000000000000000000000000000000017fae043c8fd4c520a90d4a6bd95f5b0484acc279b899e7b1d8f7f7831cc6ba37cd5965c4dc674768f5805842d433af3 + 0000000000000000000000000000000008ddd7b41b8fa4d29fb931830f29b46f4015ec202d51cb969d7c832aafc0995c875cd45eff4a083e2d5ecb5ad185b64f + 0000000000000000000000000000000015d384ab7e52420b83a69827257cb52b00f0199ed2240a142812b46cf67e92b99942ac59fb9f9efd7dd822f5a36c799f + 00000000000000000000000000000000074b3a16a9cc4be9da0ac8e2e7003d9c1ec89244d2c33441b31af76716cce439f805843a9a44701203231efdca551d5b + bb30985756c3ca075114c92f231575d6befafe4084517f1166a47376867bd108 + 000000000000000000000000000000000e25365988664e8b6ade2e5a40da49c11ff1e084cc0f8dca51f0d0578555d39e3617c8cadb2abc2633b28c5895ab0a9e + 00000000000000000000000000000000169f5fd768152169c403475dee475576fd2cc3788179453b0039ff3cb1b7a5a0fff8f82d03f56e65cad579218486c3b6 + 00000000000000000000000000000000087ccd7f92032febc1f75c7115111ede4acbb2e429cbccf3959524d0b79c449d431ff65485e1aecb442b53fec80ecb40 + 00000000000000000000000000000000135d63f264360003b2eb28f126c6621a40088c6eb15acc4aea89d6068e9d5a47f842aa4b4300f5cda5cc5831edb81596 + fb730105809f64ea522983d6bbb62f7e2e8cbf702685e9be10e2ef71f8187672 + 00000000000000000000000000000000159da74f15e4c614b418997f81a1b8a3d9eb8dd80d94b5bad664bff271bb0f2d8f3c4ceb947dc6300d5003a2f7d7a829 + 000000000000000000000000000000000cdd4d1d4666f385dd54052cf5c1966328403251bebb29f0d553a9a96b5ade350c8493270e9b5282d8a06f9fa8d7b1d9 + 00000000000000000000000000000000189f8d3c94fdaa72cc67a7f93d35f91e22206ff9e97eed9601196c28d45b69c802ae92bcbf582754717b0355e08d37c0 + 00000000000000000000000000000000054b0a282610f108fc7f6736b8c22c8778d082bf4b0d0abca5a228198eba6a868910dd5c5c440036968e977955054196 + b6a9408625b0ca8fcbfb21d34eec2d8e24e9a30d2d3b32d7a37d110b13afbfea + 000000000000000000000000000000000f29b0d2b6e3466668e1328048e8dbc782c1111ab8cbe718c85d58ded992d97ca8ba20b9d048feb6ed0aa1b4139d02d3 + 000000000000000000000000000000000d1f0dae940b99fbfc6e4a58480cac8c4e6b2fe33ce6f39c7ac1671046ce94d9e16cba2bb62c6749ef73d45bea21501a + 000000000000000000000000000000001902ccece1c0c763fd06934a76d1f2f056563ae6d8592bafd589cfebd6f057726fd908614ccd6518a21c66ecc2f78b66 + 0000000000000000000000000000000017f6b113f8872c3187d20b0c765d73b850b54244a719cf461fb318796c0b8f310b5490959f9d9187f99c8ed3e25e42a9 + 3b77283d0a7bb9e17a27e66851792fdd605cc0a339028b8985390fd024374c76 + 000000000000000000000000000000000576b8cf1e69efdc277465c344cadf7f8cceffacbeca83821f3ff81717308b97f4ac046f1926e7c2eb42677d7afc257c + 000000000000000000000000000000000cc1524531e96f3c00e4250dd351aedb5a4c3184aff52ec8c13d470068f5967f3674fe173ee239933e67501a9decc668 + 0000000000000000000000000000000001610cfcaea414c241b44cf6f3cc319dcb51d6b8de29c8a6869ff7c1ebb7b747d881e922b42e8fab96bde7cf23e8e4cd + 0000000000000000000000000000000017d4444dc8b6893b681cf10dac8169054f9d2f61d3dd5fd785ae7afa49d18ebbde9ce8dde5641adc6b38173173459836 + dd994eae929aee7428fdda2e44f8cb12b10b91c83b22abc8bbb561310b62257c + 000000000000000000000000000000000ca8f961f86ee6c46fc88fbbf721ba760186f13cd4cce743f19dc60a89fd985cb3feee34dcc4656735a326f515a729e4 + 00000000000000000000000000000000174baf466b809b1155d524050f7ee58c7c5cf728c674e0ce549f5551047a4479ca15bdf69b403b03fa74eb1b26bbff6c + 0000000000000000000000000000000000e8c8b587c171b1b292779abfef57202ed29e7fe94ade9634ec5a2b3b4692a4f3c15468e3f6418b144674be70780d5b + 000000000000000000000000000000001865e99cf97d88bdf56dae32314eb32295c39a1e755cd7d1478bea8520b9ff21c39b683b92ae15568420c390c42b123b + 7010b134989c8368c7f831f9dd9f9a890e2c1435681107414f2e8637153bbf6a + 0000000000000000000000000000000017eccd446f10018219a1bd111b8786cf9febd49f9e7e754e82dd155ead59b819f0f20e42f4635d5044ec5d550d847623 + 000000000000000000000000000000000403969d2b8f914ff2ea3bf902782642e2c6157bd2a343acf60ff9125b48b558d990a74c6d4d6398e7a3cc2a16037346 + 000000000000000000000000000000000bd45f61f142bd78619fb520715320eb5e6ebafa8b078ce796ba62fe1a549d5fb9df57e92d8d2795988eb6ae18cf9d93 + 00000000000000000000000000000000097db1314e064b8e670ec286958f17065bce644cf240ab1b1b220504560d36a0b43fc18453ff3a2bb315e219965f5bd3 + 94c68bc8d91ac8c489ee87dbfc4b94c93c8bbd5fc04c27db8b02303f3a659054 + 00000000000000000000000000000000018244ab39a716e252cbfb986c7958b371e29ea9190010d1f5e1cfdb6ce4822d4055c37cd411fc9a0c46d728f2c13ecf + 0000000000000000000000000000000001985d3c667c8d68c9adb92bdc7a8af959c17146544997d97116120a0f55366bd7ad7ffa28d93ee51222ff9222779675 + 000000000000000000000000000000000c70fd4e3c8f2a451f83fb6c046431b38251b7bae44cf8d36df69a03e2d3ce6137498523fcf0bcf29b5d69e8f265e24d + 00000000000000000000000000000000047b9163a218f7654a72e0d7c651a2cf7fd95e9784a59e0bf119d081de6c0465d374a55fbc1eff9828c9fd29abf4c4bd + b3682accc3939283b870357cf83683350baf73aa0d3d68bda82a0f6ae7e51746 + "); + let expected = hex!(" + 00000000000000000000000000000000083ad744b34f6393bc983222b004657494232c5d9fbc978d76e2377a28a34c4528da5d91cbc0977dc953397a6d21eca2 + 0000000000000000000000000000000015aec6526e151cf5b8403353517dfb9a162087a698b71f32b266d3c5c936a83975d5567c25b3a5994042ec1379c8e526 + 000000000000000000000000000000000e3647185d1a20efad19f975729908840dc33909a583600f7915025f906aef9c022fd34e618170b11178aaa824ae36b3 + 00000000000000000000000000000000159576d1d53f6cd12c39d651697e11798321f17cd287118d7ebeabf68281bc03109ee103ee8ef2ef93c71dd1dcbaf1e0 + "); + + let mut output = [0u8; 256]; + + f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) + .expect("Builtin should not fail"); + assert_eq!(&output[..], &expected[..]); + } + + #[test] + fn bls12_381_pairing() { + let f = Builtin { + pricer: btreemap![0 => Pricing::Bls12Pairing(Bls12PairingPricer{price: Bls12PairingPrice{base: 1, pair: 1}})], + native: EthereumBuiltin::from_str("bls12_381_pairing").unwrap(), + }; + + let input = hex!(" + 000000000000000000000000000000001830f52d9bff64a623c6f5259e2cd2c2a08ea17a8797aaf83174ea1e8c3bd3955c2af1d39bfa474815bfe60714b7cd80 + 000000000000000000000000000000000874389c02d4cf1c61bc54c4c24def11dfbe7880bc998a95e70063009451ee8226fec4b278aade3a7cea55659459f1d5 + 00000000000000000000000000000000197737f831d4dc7e708475f4ca7ca15284db2f3751fcaac0c17f517f1ddab35e1a37907d7b99b39d6c8d9001cd50e79e + 000000000000000000000000000000000af1a3f6396f0c983e7c2d42d489a3ae5a3ff0a553d93154f73ac770cd0af7467aa0cef79f10bbd34621b3ec9583a834 + 000000000000000000000000000000001918cb6e448ed69fb906145de3f11455ee0359d030e90d673ce050a360d796de33ccd6a941c49a1414aca1c26f9e699e + 0000000000000000000000000000000019a915154a13249d784093facc44520e7f3a18410ab2a3093e0b12657788e9419eec25729944f7945e732104939e7a9e + 000000000000000000000000000000001830f52d9bff64a623c6f5259e2cd2c2a08ea17a8797aaf83174ea1e8c3bd3955c2af1d39bfa474815bfe60714b7cd80 + 00000000000000000000000000000000118cd94e36ab177de95f52f180fdbdc584b8d30436eb882980306fa0625f07a1f7ad3b4c38a921c53d14aa9a6ba5b8d6 + 00000000000000000000000000000000197737f831d4dc7e708475f4ca7ca15284db2f3751fcaac0c17f517f1ddab35e1a37907d7b99b39d6c8d9001cd50e79e + 000000000000000000000000000000000af1a3f6396f0c983e7c2d42d489a3ae5a3ff0a553d93154f73ac770cd0af7467aa0cef79f10bbd34621b3ec9583a834 + 000000000000000000000000000000001918cb6e448ed69fb906145de3f11455ee0359d030e90d673ce050a360d796de33ccd6a941c49a1414aca1c26f9e699e + 0000000000000000000000000000000019a915154a13249d784093facc44520e7f3a18410ab2a3093e0b12657788e9419eec25729944f7945e732104939e7a9e + "); + let expected = hex!( + " + 0000000000000000000000000000000000000000000000000000000000000001 + " + ); + + let mut output = [0u8; 32]; + + f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) + .expect("Builtin should not fail"); + assert_eq!(&output[..], &expected[..]); + } + + #[test] + fn bls12_381_fp_to_g1() { + let f = Builtin { + pricer: btreemap![0 => Pricing::Bls12Pairing(Bls12PairingPricer{price: Bls12PairingPrice{base: 1, pair: 1}})], + native: EthereumBuiltin::from_str("bls12_381_fp_to_g1").unwrap(), + }; + + let input = hex!(" + 0000000000000000000000000000000017f66b472b36717ee0902d685c808bb5f190bbcb2c51d067f1cbec64669f10199a5868d7181dcec0498fcc71f5acaf79 + "); + let expected = hex!(" + 00000000000000000000000000000000188dc9e5ddf48977f33aeb6e505518269bf67fb624fa86b79741d842e75a6fa1be0911c2caa9e55571b6e55a3c0c0b9e + 00000000000000000000000000000000193e8b7c7e78daf104a59d7b39401a65355fa874bd34e91688580941e99a863367efc68fe871e38e07423090e93919c9 + "); + + let mut output = [0u8; 128]; + + f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) + .expect("Builtin should not fail"); + assert_eq!(&output[..], &expected[..]); + } + + #[test] + fn bls12_381_fp2_to_g2() { + let f = Builtin { + pricer: btreemap![0 => Pricing::Bls12Pairing(Bls12PairingPricer{price: Bls12PairingPrice{base: 1, pair: 1}})], + native: EthereumBuiltin::from_str("bls12_381_fp2_to_g2").unwrap(), + }; + + let input = hex!(" + 000000000000000000000000000000000f470603a402bc134db1b389fd187460f9eb2dd001a2e99f730af386508c62f0e911d831a2562da84bce11d39f2ff13f + 000000000000000000000000000000000d8c45f4ab20642d0cba9764126e0818b7d731a6ba29ed234d9d6309a5e8ddfbd85193f1fa8b7cfeed3d31b23b904ee9 + "); + let expected = hex!(" + 0000000000000000000000000000000012e74d5a0c005a86ca148e9eff8e34a00bfa8b6e6aadf633d65cd09bb29917e0ceb0d5c9d9650c162d7fe4aa27452685 + 0000000000000000000000000000000005f09101a2088712619f9c096403b66855a12f9016c55aef6047372fba933f02d9d59db1a86df7be57978021e2457821 + 00000000000000000000000000000000136975b37fe400d1d217a2b496c1552b39be4e9e71dd7ad482f5f0836d271d02959fdb698dda3d0530587fb86e0db1dd + 0000000000000000000000000000000000bad0aabd9309e92e2dd752f4dd73be07c0de2c5ddd57916b9ffa065d7440d03d44e7c042075cda694414a9fb639bb7 + "); + + let mut output = [0u8; 256]; + + f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) + .expect("Builtin should not fail"); + assert_eq!(&output[..], &expected[..]); + } + + #[test] + fn bls12_381_g1_multiexp_init_from_spec() { + use ethjson::spec::builtin::{Bls12G1Multiexp, Pricing}; + + let b = Builtin::try_from(JsonBuiltin { + name: "bls12_381_g1_multiexp".to_owned(), + pricing: btreemap![ + 10000000 => PricingAt { + info: None, + price: Pricing::Bls12G1Multiexp(Bls12G1Multiexp{ + base: 12000, + }), + } + ], + }) + .unwrap(); + + match b.native { + EthereumBuiltin::Bls12G1MultiExp(..) => {} + _ => { + panic!("invalid precompile type"); + } + } + } + + #[test] + fn bls12_381_g2_multiexp_init_from_spec() { + use ethjson::spec::builtin::{Bls12G2Multiexp, Pricing}; + + let b = Builtin::try_from(JsonBuiltin { + name: "bls12_381_g2_multiexp".to_owned(), + pricing: btreemap![ + 10000000 => PricingAt { + info: None, + price: Pricing::Bls12G2Multiexp(Bls12G2Multiexp{ + base: 55000, + }), + } + ], + }) + .unwrap(); + + match b.native { + EthereumBuiltin::Bls12G2MultiExp(..) => {} + _ => { + panic!("invalid precompile type"); + } + } + } +} diff --git a/ethcore/call-contract/Cargo.toml b/ethcore/call-contract/Cargo.toml index 068434a1dec..7ee9bb7e652 100644 --- a/ethcore/call-contract/Cargo.toml +++ b/ethcore/call-contract/Cargo.toml @@ -1,4 +1,5 @@ [package] +description = "Parity Ethereum (EthCore) Contract Calls and Blockchain Service & Registry Information" name = "ethcore-call-contract" version = "0.1.0" license = "GPL-3.0" diff --git a/ethcore/call-contract/src/call_contract.rs b/ethcore/call-contract/src/call_contract.rs index 8b042f0833c..fac70a6f78f 100644 --- a/ethcore/call-contract/src/call_contract.rs +++ b/ethcore/call-contract/src/call_contract.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Provides CallContract and RegistryInfo traits @@ -22,12 +22,12 @@ use types::ids::BlockId; /// Provides `call_contract` method pub trait CallContract { - /// Like `call`, but with various defaults. Designed to be used for calling contracts. - fn call_contract(&self, id: BlockId, address: Address, data: Bytes) -> Result; + /// Like `call`, but with various defaults. Designed to be used for calling contracts. + fn call_contract(&self, id: BlockId, address: Address, data: Bytes) -> Result; } /// Provides information on a blockchain service and it's registry pub trait RegistryInfo { - /// Get the address of a particular blockchain service, if available. - fn registry_address(&self, name: String, block: BlockId) -> Option

; + /// Get the address of a particular blockchain service, if available. + fn registry_address(&self, name: String, block: BlockId) -> Option
; } diff --git a/ethcore/call-contract/src/lib.rs b/ethcore/call-contract/src/lib.rs index 1cbfb11378e..f629c35e928 100644 --- a/ethcore/call-contract/src/lib.rs +++ b/ethcore/call-contract/src/lib.rs @@ -1,23 +1,23 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . #![warn(missing_docs)] //! Call Contract module -//! +//! //! This crate exposes traits required to call contracts at particular block. //! All utilities that depend on on-chain data should use those traits to access it. diff --git a/ethcore/db/Cargo.toml b/ethcore/db/Cargo.toml index 53ec9f7b881..2c730a758ba 100644 --- a/ethcore/db/Cargo.toml +++ b/ethcore/db/Cargo.toml @@ -1,6 +1,6 @@ [package] -description = "Ethcore DB access utilities" -homepage = "http://parity.io" +description = "OpenEthereum DB access utilities" +homepage = "https://github.com/openethereum/openethereum" license = "GPL-3.0" name = "ethcore-db" version = "0.1.0" diff --git a/ethcore/db/src/cache_manager.rs b/ethcore/db/src/cache_manager.rs index 34a02d72137..3ea81793190 100644 --- a/ethcore/db/src/cache_manager.rs +++ b/ethcore/db/src/cache_manager.rs @@ -1,82 +1,106 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Database cache manager -use std::collections::{VecDeque, HashSet}; -use std::hash::Hash; +use std::{ + collections::{HashSet, VecDeque}, + hash::Hash, +}; const COLLECTION_QUEUE_SIZE: usize = 8; /// DB cache manager pub struct CacheManager { - pref_cache_size: usize, - max_cache_size: usize, - bytes_per_cache_entry: usize, - cache_usage: VecDeque> + pref_cache_size: usize, + max_cache_size: usize, + bytes_per_cache_entry: usize, + cache_usage: VecDeque>, } -impl CacheManager where T: Eq + Hash { - /// Create new cache manager with preferred (heap) sizes. - pub fn new(pref_cache_size: usize, max_cache_size: usize, bytes_per_cache_entry: usize) -> Self { - CacheManager { - pref_cache_size: pref_cache_size, - max_cache_size: max_cache_size, - bytes_per_cache_entry: bytes_per_cache_entry, - cache_usage: (0..COLLECTION_QUEUE_SIZE).into_iter().map(|_| Default::default()).collect(), - } - } +impl CacheManager +where + T: Eq + Hash, +{ + /// Create new cache manager with preferred (heap) sizes. + pub fn new( + pref_cache_size: usize, + max_cache_size: usize, + bytes_per_cache_entry: usize, + ) -> Self { + CacheManager { + pref_cache_size: pref_cache_size, + max_cache_size: max_cache_size, + bytes_per_cache_entry: bytes_per_cache_entry, + cache_usage: (0..COLLECTION_QUEUE_SIZE) + .into_iter() + .map(|_| Default::default()) + .collect(), + } + } - /// Mark element as used. - pub fn note_used(&mut self, id: T) { - if !self.cache_usage[0].contains(&id) { - if let Some(c) = self.cache_usage.iter_mut().skip(1).find(|e| e.contains(&id)) { - c.remove(&id); - } - self.cache_usage[0].insert(id); - } - } + /// Mark element as used. + pub fn note_used(&mut self, id: T) { + if !self.cache_usage[0].contains(&id) { + if let Some(c) = self + .cache_usage + .iter_mut() + .skip(1) + .find(|e| e.contains(&id)) + { + c.remove(&id); + } + self.cache_usage[0].insert(id); + } + } - /// Collects unused objects from cache. - /// First params is the current size of the cache. - /// Second one is an with objects to remove. It should also return new size of the cache. - pub fn collect_garbage(&mut self, current_size: usize, mut notify_unused: F) where F: FnMut(HashSet) -> usize { - if current_size < self.pref_cache_size { - self.rotate_cache_if_needed(); - return; - } + /// Collects unused objects from cache. + /// First params is the current size of the cache. + /// Second one is an with objects to remove. It should also return new size of the cache. + pub fn collect_garbage(&mut self, current_size: usize, mut notify_unused: F) + where + F: FnMut(HashSet) -> usize, + { + if current_size < self.pref_cache_size { + self.rotate_cache_if_needed(); + return; + } - for _ in 0..COLLECTION_QUEUE_SIZE { - if let Some(back) = self.cache_usage.pop_back() { - let current_size = notify_unused(back); - self.cache_usage.push_front(Default::default()); - if current_size < self.max_cache_size { - break - } - } - } - } + for _ in 0..COLLECTION_QUEUE_SIZE { + if let Some(back) = self.cache_usage.pop_back() { + let current_size = notify_unused(back); + self.cache_usage.push_front(Default::default()); + if current_size < self.max_cache_size { + break; + } + } + } + } - fn rotate_cache_if_needed(&mut self) { - if self.cache_usage.is_empty() { return } + fn rotate_cache_if_needed(&mut self) { + if self.cache_usage.is_empty() { + return; + } - if self.cache_usage[0].len() * self.bytes_per_cache_entry > self.pref_cache_size / COLLECTION_QUEUE_SIZE { - if let Some(cache) = self.cache_usage.pop_back() { - self.cache_usage.push_front(cache); - } - } - } + if self.cache_usage[0].len() * self.bytes_per_cache_entry + > self.pref_cache_size / COLLECTION_QUEUE_SIZE + { + if let Some(cache) = self.cache_usage.pop_back() { + self.cache_usage.push_front(cache); + } + } + } } diff --git a/ethcore/db/src/db.rs b/ethcore/db/src/db.rs index c00f63eac23..67cba2fbdfb 100644 --- a/ethcore/db/src/db.rs +++ b/ethcore/db/src/db.rs @@ -1,26 +1,24 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Database utilities and definitions. -use std::ops::Deref; -use std::hash::Hash; -use std::collections::HashMap; -use parking_lot::RwLock; use kvdb::{DBTransaction, KeyValueDB}; +use parking_lot::RwLock; +use std::{collections::HashMap, hash::Hash, ops::Deref}; use rlp; @@ -35,205 +33,281 @@ pub const COL_BODIES: Option = Some(2); pub const COL_EXTRA: Option = Some(3); /// Column for Traces pub const COL_TRACE: Option = Some(4); -/// Column for the empty accounts bloom filter. +/// Column for the accounts existence bloom filter. +#[deprecated(since = "3.0.0", note = "Accounts bloom column is deprecated")] pub const COL_ACCOUNT_BLOOM: Option = Some(5); /// Column for general information from the local node which can persist. pub const COL_NODE_INFO: Option = Some(6); -/// Column for the light client chain. -pub const COL_LIGHT_CHAIN: Option = Some(7); /// Number of columns in DB -pub const NUM_COLUMNS: Option = Some(8); +pub const NUM_COLUMNS: Option = Some(7); /// Modes for updating caches. #[derive(Clone, Copy)] pub enum CacheUpdatePolicy { - /// Overwrite entries. - Overwrite, - /// Remove entries. - Remove, + /// Overwrite entries. + Overwrite, + /// Remove entries. + Remove, } /// A cache for arbitrary key-value pairs. pub trait Cache { - /// Insert an entry into the cache and get the old value. - fn insert(&mut self, k: K, v: V) -> Option; + /// Insert an entry into the cache and get the old value. + fn insert(&mut self, k: K, v: V) -> Option; - /// Remove an entry from the cache, getting the old value if it existed. - fn remove(&mut self, k: &K) -> Option; + /// Remove an entry from the cache, getting the old value if it existed. + fn remove(&mut self, k: &K) -> Option; - /// Query the cache for a key's associated value. - fn get(&self, k: &K) -> Option<&V>; + /// Query the cache for a key's associated value. + fn get(&self, k: &K) -> Option<&V>; } -impl Cache for HashMap where K: Hash + Eq { - fn insert(&mut self, k: K, v: V) -> Option { - HashMap::insert(self, k, v) - } - - fn remove(&mut self, k: &K) -> Option { - HashMap::remove(self, k) - } - - fn get(&self, k: &K) -> Option<&V> { - HashMap::get(self, k) - } +impl Cache for HashMap +where + K: Hash + Eq, +{ + fn insert(&mut self, k: K, v: V) -> Option { + HashMap::insert(self, k, v) + } + + fn remove(&mut self, k: &K) -> Option { + HashMap::remove(self, k) + } + + fn get(&self, k: &K) -> Option<&V> { + HashMap::get(self, k) + } } /// Should be used to get database key associated with given value. pub trait Key { - /// The db key associated with this value. - type Target: Deref; + /// The db key associated with this value. + type Target: Deref; - /// Returns db key. - fn key(&self) -> Self::Target; + /// Returns db key. + fn key(&self) -> Self::Target; } /// Should be used to write value into database. pub trait Writable { - /// Writes the value into the database. - fn write(&mut self, col: Option, key: &Key, value: &T) where T: rlp::Encodable, R: Deref; - - /// Deletes key from the databse. - fn delete(&mut self, col: Option, key: &Key) where T: rlp::Encodable, R: Deref; - - /// Writes the value into the database and updates the cache. - fn write_with_cache(&mut self, col: Option, cache: &mut Cache, key: K, value: T, policy: CacheUpdatePolicy) where - K: Key + Hash + Eq, - T: rlp::Encodable, - R: Deref { - self.write(col, &key, &value); - match policy { - CacheUpdatePolicy::Overwrite => { - cache.insert(key, value); - }, - CacheUpdatePolicy::Remove => { - cache.remove(&key); - } - } - } - - /// Writes the values into the database and updates the cache. - fn extend_with_cache(&mut self, col: Option, cache: &mut Cache, values: HashMap, policy: CacheUpdatePolicy) where - K: Key + Hash + Eq, - T: rlp::Encodable, - R: Deref { - match policy { - CacheUpdatePolicy::Overwrite => { - for (key, value) in values { - self.write(col, &key, &value); - cache.insert(key, value); - } - }, - CacheUpdatePolicy::Remove => { - for (key, value) in &values { - self.write(col, key, value); - cache.remove(key); - } - }, - } - } - - /// Writes and removes the values into the database and updates the cache. - fn extend_with_option_cache(&mut self, col: Option, cache: &mut Cache>, values: HashMap>, policy: CacheUpdatePolicy) where - K: Key + Hash + Eq, - T: rlp::Encodable, - R: Deref { - match policy { - CacheUpdatePolicy::Overwrite => { - for (key, value) in values { - match value { - Some(ref v) => self.write(col, &key, v), - None => self.delete(col, &key), - } - cache.insert(key, value); - } - }, - CacheUpdatePolicy::Remove => { - for (key, value) in values { - match value { - Some(v) => self.write(col, &key, &v), - None => self.delete(col, &key), - } - cache.remove(&key); - } - }, - } - } - + /// Writes the value into the database. + fn write(&mut self, col: Option, key: &dyn Key, value: &T) + where + T: rlp::Encodable, + R: Deref; + + /// Deletes key from the databse. + fn delete(&mut self, col: Option, key: &dyn Key) + where + T: rlp::Encodable, + R: Deref; + + /// Writes the value into the database and updates the cache. + fn write_with_cache( + &mut self, + col: Option, + cache: &mut dyn Cache, + key: K, + value: T, + policy: CacheUpdatePolicy, + ) where + K: Key + Hash + Eq, + T: rlp::Encodable, + R: Deref, + { + self.write(col, &key, &value); + match policy { + CacheUpdatePolicy::Overwrite => { + cache.insert(key, value); + } + CacheUpdatePolicy::Remove => { + cache.remove(&key); + } + } + } + + /// Writes the values into the database and updates the cache. + fn extend_with_cache( + &mut self, + col: Option, + cache: &mut dyn Cache, + values: HashMap, + policy: CacheUpdatePolicy, + ) where + K: Key + Hash + Eq, + T: rlp::Encodable, + R: Deref, + { + match policy { + CacheUpdatePolicy::Overwrite => { + for (key, value) in values { + self.write(col, &key, &value); + cache.insert(key, value); + } + } + CacheUpdatePolicy::Remove => { + for (key, value) in &values { + self.write(col, key, value); + cache.remove(key); + } + } + } + } + + /// Writes and removes the values into the database and updates the cache. + fn extend_with_option_cache( + &mut self, + col: Option, + cache: &mut dyn Cache>, + values: HashMap>, + policy: CacheUpdatePolicy, + ) where + K: Key + Hash + Eq, + T: rlp::Encodable, + R: Deref, + { + match policy { + CacheUpdatePolicy::Overwrite => { + for (key, value) in values { + match value { + Some(ref v) => self.write(col, &key, v), + None => self.delete(col, &key), + } + cache.insert(key, value); + } + } + CacheUpdatePolicy::Remove => { + for (key, value) in values { + match value { + Some(v) => self.write(col, &key, &v), + None => self.delete(col, &key), + } + cache.remove(&key); + } + } + } + } } /// Should be used to read values from database. pub trait Readable { - /// Returns value for given key. - fn read(&self, col: Option, key: &Key) -> Option where - T: rlp::Decodable, - R: Deref; - - /// Returns value for given key either in cache or in database. - fn read_with_cache(&self, col: Option, cache: &RwLock, key: &K) -> Option where - K: Key + Eq + Hash + Clone, - T: Clone + rlp::Decodable, - C: Cache { - { - let read = cache.read(); - if let Some(v) = read.get(key) { - return Some(v.clone()); - } - } - - self.read(col, key).map(|value: T|{ - let mut write = cache.write(); - write.insert(key.clone(), value.clone()); - value - }) - } - - /// Returns true if given value exists. - fn exists(&self, col: Option, key: &Key) -> bool where R: Deref; - - /// Returns true if given value exists either in cache or in database. - fn exists_with_cache(&self, col: Option, cache: &RwLock, key: &K) -> bool where - K: Eq + Hash + Key, - R: Deref, - C: Cache { - { - let read = cache.read(); - if read.get(key).is_some() { - return true; - } - } - - self.exists::(col, key) - } + /// Returns value for given key. + fn read(&self, col: Option, key: &dyn Key) -> Option + where + T: rlp::Decodable, + R: Deref; + + /// Returns value for given key either in cache or in database. + fn read_with_cache(&self, col: Option, cache: &RwLock, key: &K) -> Option + where + K: Key + Eq + Hash + Clone, + T: Clone + rlp::Decodable, + C: Cache, + { + { + let read = cache.read(); + if let Some(v) = read.get(key) { + return Some(v.clone()); + } + } + + self.read(col, key).map(|value: T| { + let mut write = cache.write(); + write.insert(key.clone(), value.clone()); + value + }) + } + + /// Returns value for given key either in two-layered cache or in database. + fn read_with_two_layer_cache( + &self, + col: Option, + l1_cache: &RwLock, + l2_cache: &RwLock, + key: &K, + ) -> Option + where + K: Key + Eq + Hash + Clone, + T: Clone + rlp::Decodable, + C: Cache, + { + { + let read = l1_cache.read(); + if let Some(v) = read.get(key) { + return Some(v.clone()); + } + } + + self.read_with_cache(col, l2_cache, key) + } + + /// Returns true if given value exists. + fn exists(&self, col: Option, key: &dyn Key) -> bool + where + R: Deref; + + /// Returns true if given value exists either in cache or in database. + fn exists_with_cache(&self, col: Option, cache: &RwLock, key: &K) -> bool + where + K: Eq + Hash + Key, + R: Deref, + C: Cache, + { + { + let read = cache.read(); + if read.get(key).is_some() { + return true; + } + } + + self.exists::(col, key) + } } impl Writable for DBTransaction { - fn write(&mut self, col: Option, key: &Key, value: &T) where T: rlp::Encodable, R: Deref { - self.put(col, &key.key(), &rlp::encode(value)); - } - - fn delete(&mut self, col: Option, key: &Key) where T: rlp::Encodable, R: Deref { - self.delete(col, &key.key()); - } + fn write(&mut self, col: Option, key: &dyn Key, value: &T) + where + T: rlp::Encodable, + R: Deref, + { + self.put(col, &key.key(), &rlp::encode(value)); + } + + fn delete(&mut self, col: Option, key: &dyn Key) + where + T: rlp::Encodable, + R: Deref, + { + self.delete(col, &key.key()); + } } impl Readable for KVDB { - fn read(&self, col: Option, key: &Key) -> Option - where T: rlp::Decodable, R: Deref { - self.get(col, &key.key()) - .expect(&format!("db get failed, key: {:?}", &key.key() as &[u8])) - .map(|v| rlp::decode(&v).expect("decode db value failed") ) - - } - - fn exists(&self, col: Option, key: &Key) -> bool where R: Deref { - let result = self.get(col, &key.key()); - - match result { - Ok(v) => v.is_some(), - Err(err) => { - panic!("db get failed, key: {:?}, err: {:?}", &key.key() as &[u8], err); - } - } - } + fn read(&self, col: Option, key: &dyn Key) -> Option + where + T: rlp::Decodable, + R: Deref, + { + self.get(col, &key.key()) + .expect(&format!("db get failed, key: {:?}", &key.key() as &[u8])) + .map(|v| rlp::decode(&v).expect("decode db value failed")) + } + + fn exists(&self, col: Option, key: &dyn Key) -> bool + where + R: Deref, + { + let result = self.get(col, &key.key()); + + match result { + Ok(v) => v.is_some(), + Err(err) => { + panic!( + "db get failed, key: {:?}, err: {:?}", + &key.key() as &[u8], + err + ); + } + } + } } diff --git a/ethcore/db/src/keys.rs b/ethcore/db/src/keys.rs index 96ecde85fb5..bd765599056 100644 --- a/ethcore/db/src/keys.rs +++ b/ethcore/db/src/keys.rs @@ -1,114 +1,111 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Blockchain DB extras. -use std::io::Write; -use std::ops; +use std::{io::Write, ops}; -use common_types::BlockNumber; -use common_types::engines::epoch::Transition as EpochTransition; -use common_types::receipt::Receipt; +use common_types::{engines::epoch::Transition as EpochTransition, receipt::Receipt, BlockNumber}; use ethereum_types::{H256, H264, U256}; use heapsize::HeapSizeOf; use kvdb::PREFIX_LEN as DB_PREFIX_LEN; use rlp; -use rlp_derive::{RlpEncodableWrapper, RlpDecodableWrapper, RlpEncodable, RlpDecodable}; +use rlp_derive::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper}; use crate::db::Key; /// Represents index of extra data in database #[derive(Copy, Debug, Hash, Eq, PartialEq, Clone)] pub enum ExtrasIndex { - /// Block details index - BlockDetails = 0, - /// Block hash index - BlockHash = 1, - /// Transaction address index - TransactionAddress = 2, - /// Block receipts index - BlockReceipts = 4, - /// Epoch transition data index. - EpochTransitions = 5, - /// Pending epoch transition data index. - PendingEpochTransition = 6, + /// Block details index + BlockDetails = 0, + /// Block hash index + BlockHash = 1, + /// Transaction address index + TransactionAddress = 2, + /// Block receipts index + BlockReceipts = 4, + /// Epoch transition data index. + EpochTransitions = 5, + /// Pending epoch transition data index. + PendingEpochTransition = 6, } fn with_index(hash: &H256, i: ExtrasIndex) -> H264 { - let mut result = H264::default(); - result[0] = i as u8; - (*result)[1..].clone_from_slice(hash); - result + let mut result = H264::default(); + result[0] = i as u8; + (*result)[1..].clone_from_slice(hash); + result } /// Wrapper for block number used as a DB key. pub struct BlockNumberKey([u8; 5]); impl ops::Deref for BlockNumberKey { - type Target = [u8]; + type Target = [u8]; - fn deref(&self) -> &Self::Target { - &self.0 - } + fn deref(&self) -> &Self::Target { + &self.0 + } } impl Key for BlockNumber { - type Target = BlockNumberKey; - - fn key(&self) -> Self::Target { - let mut result = [0u8; 5]; - result[0] = ExtrasIndex::BlockHash as u8; - result[1] = (self >> 24) as u8; - result[2] = (self >> 16) as u8; - result[3] = (self >> 8) as u8; - result[4] = *self as u8; - BlockNumberKey(result) - } + type Target = BlockNumberKey; + + fn key(&self) -> Self::Target { + let mut result = [0u8; 5]; + result[0] = ExtrasIndex::BlockHash as u8; + result[1] = (self >> 24) as u8; + result[2] = (self >> 16) as u8; + result[3] = (self >> 8) as u8; + result[4] = *self as u8; + BlockNumberKey(result) + } } impl Key for H256 { - type Target = H264; + type Target = H264; - fn key(&self) -> H264 { - with_index(self, ExtrasIndex::BlockDetails) - } + fn key(&self) -> H264 { + with_index(self, ExtrasIndex::BlockDetails) + } } impl Key for H256 { - type Target = H264; + type Target = H264; - fn key(&self) -> H264 { - with_index(self, ExtrasIndex::TransactionAddress) - } + fn key(&self) -> H264 { + with_index(self, ExtrasIndex::TransactionAddress) + } } impl Key for H256 { - type Target = H264; + type Target = H264; - fn key(&self) -> H264 { - with_index(self, ExtrasIndex::BlockReceipts) - } + fn key(&self) -> H264 { + with_index(self, ExtrasIndex::BlockReceipts) + } } impl Key for H256 { - type Target = H264; + type Target = H264; - fn key(&self) -> H264 { - with_index(self, ExtrasIndex::PendingEpochTransition) - } + fn key(&self) -> H264 { + with_index(self, ExtrasIndex::PendingEpochTransition) + } } /// length of epoch keys. @@ -117,153 +114,170 @@ pub const EPOCH_KEY_LEN: usize = DB_PREFIX_LEN + 16; /// epoch key prefix. /// used to iterate over all epoch transitions in order from genesis. pub const EPOCH_KEY_PREFIX: &'static [u8; DB_PREFIX_LEN] = &[ - ExtrasIndex::EpochTransitions as u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ExtrasIndex::EpochTransitions as u8, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, ]; /// Epoch transitions key pub struct EpochTransitionsKey([u8; EPOCH_KEY_LEN]); impl ops::Deref for EpochTransitionsKey { - type Target = [u8]; + type Target = [u8]; - fn deref(&self) -> &[u8] { &self.0[..] } + fn deref(&self) -> &[u8] { + &self.0[..] + } } impl Key for u64 { - type Target = EpochTransitionsKey; + type Target = EpochTransitionsKey; - fn key(&self) -> Self::Target { - let mut arr = [0u8; EPOCH_KEY_LEN]; - arr[..DB_PREFIX_LEN].copy_from_slice(&EPOCH_KEY_PREFIX[..]); + fn key(&self) -> Self::Target { + let mut arr = [0u8; EPOCH_KEY_LEN]; + arr[..DB_PREFIX_LEN].copy_from_slice(&EPOCH_KEY_PREFIX[..]); - write!(&mut arr[DB_PREFIX_LEN..], "{:016x}", self) - .expect("format arg is valid; no more than 16 chars will be written; qed"); + write!(&mut arr[DB_PREFIX_LEN..], "{:016x}", self) + .expect("format arg is valid; no more than 16 chars will be written; qed"); - EpochTransitionsKey(arr) - } + EpochTransitionsKey(arr) + } } /// Familial details concerning a block #[derive(Debug, Clone)] pub struct BlockDetails { - /// Block number - pub number: BlockNumber, - /// Total difficulty of the block and all its parents - pub total_difficulty: U256, - /// Parent block hash - pub parent: H256, - /// List of children block hashes - pub children: Vec, - /// Whether the block is considered finalized - pub is_finalized: bool, + /// Block number + pub number: BlockNumber, + /// Total difficulty of the block and all its parents + pub total_difficulty: U256, + /// Parent block hash + pub parent: H256, + /// List of children block hashes + pub children: Vec, + /// Whether the block is considered finalized + pub is_finalized: bool, } impl rlp::Encodable for BlockDetails { - fn rlp_append(&self, stream: &mut rlp::RlpStream) { - let use_short_version = !self.is_finalized; - - match use_short_version { - true => { stream.begin_list(4); }, - false => { stream.begin_list(5); }, - } - - stream.append(&self.number); - stream.append(&self.total_difficulty); - stream.append(&self.parent); - stream.append_list(&self.children); - if !use_short_version { - stream.append(&self.is_finalized); - } - } + fn rlp_append(&self, stream: &mut rlp::RlpStream) { + let use_short_version = !self.is_finalized; + + match use_short_version { + true => { + stream.begin_list(4); + } + false => { + stream.begin_list(5); + } + } + + stream.append(&self.number); + stream.append(&self.total_difficulty); + stream.append(&self.parent); + stream.append_list(&self.children); + if !use_short_version { + stream.append(&self.is_finalized); + } + } } impl rlp::Decodable for BlockDetails { - fn decode(rlp: &rlp::Rlp) -> Result { - let use_short_version = match rlp.item_count()? { - 4 => true, - 5 => false, - _ => return Err(rlp::DecoderError::RlpIncorrectListLen), - }; - - Ok(BlockDetails { - number: rlp.val_at(0)?, - total_difficulty: rlp.val_at(1)?, - parent: rlp.val_at(2)?, - children: rlp.list_at(3)?, - is_finalized: if use_short_version { - false - } else { - rlp.val_at(4)? - }, - }) - } + fn decode(rlp: &rlp::Rlp) -> Result { + let use_short_version = match rlp.item_count()? { + 4 => true, + 5 => false, + _ => return Err(rlp::DecoderError::RlpIncorrectListLen), + }; + + Ok(BlockDetails { + number: rlp.val_at(0)?, + total_difficulty: rlp.val_at(1)?, + parent: rlp.val_at(2)?, + children: rlp.list_at(3)?, + is_finalized: if use_short_version { + false + } else { + rlp.val_at(4)? + }, + }) + } } impl HeapSizeOf for BlockDetails { - fn heap_size_of_children(&self) -> usize { - self.children.heap_size_of_children() - } + fn heap_size_of_children(&self) -> usize { + self.children.heap_size_of_children() + } } /// Represents address of certain transaction within block #[derive(Debug, PartialEq, Clone, RlpEncodable, RlpDecodable)] pub struct TransactionAddress { - /// Block hash - pub block_hash: H256, - /// Transaction index within the block - pub index: usize + /// Block hash + pub block_hash: H256, + /// Transaction index within the block + pub index: usize, } impl HeapSizeOf for TransactionAddress { - fn heap_size_of_children(&self) -> usize { 0 } + fn heap_size_of_children(&self) -> usize { + 0 + } } /// Contains all block receipts. #[derive(Clone, RlpEncodableWrapper, RlpDecodableWrapper)] pub struct BlockReceipts { - /// Block receipts - pub receipts: Vec, + /// Block receipts + pub receipts: Vec, } impl BlockReceipts { - /// Create new block receipts wrapper. - pub fn new(receipts: Vec) -> Self { - BlockReceipts { - receipts: receipts - } - } + /// Create new block receipts wrapper. + pub fn new(receipts: Vec) -> Self { + BlockReceipts { receipts: receipts } + } } impl HeapSizeOf for BlockReceipts { - fn heap_size_of_children(&self) -> usize { - self.receipts.heap_size_of_children() - } + fn heap_size_of_children(&self) -> usize { + self.receipts.heap_size_of_children() + } } /// Candidate transitions to an epoch with specific number. #[derive(Clone, RlpEncodable, RlpDecodable)] pub struct EpochTransitions { - /// Epoch number - pub number: u64, - /// List of candidate transitions - pub candidates: Vec, + /// Epoch number + pub number: u64, + /// List of candidate transitions + pub candidates: Vec, } #[cfg(test)] mod tests { - use rlp::*; + use rlp::*; - use super::BlockReceipts; + use super::BlockReceipts; - #[test] - fn encode_block_receipts() { - let br = BlockReceipts::new(Vec::new()); + #[test] + fn encode_block_receipts() { + let br = BlockReceipts::new(Vec::new()); - let mut s = RlpStream::new_list(2); - s.append(&br); - assert!(!s.is_finished(), "List shouldn't finished yet"); - s.append(&br); - assert!(s.is_finished(), "List should be finished now"); - s.out(); - } + let mut s = RlpStream::new_list(2); + s.append(&br); + assert!(!s.is_finished(), "List shouldn't finished yet"); + s.append(&br); + assert!(s.is_finished(), "List should be finished now"); + s.out(); + } } diff --git a/ethcore/db/src/lib.rs b/ethcore/db/src/lib.rs index 3fdb368a1aa..9181b640291 100644 --- a/ethcore/db/src/lib.rs +++ b/ethcore/db/src/lib.rs @@ -1,26 +1,26 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -//! Parity Ethereum database access utilities. +//! OpenEthereum database access utilities. #![warn(missing_docs)] mod db; -pub mod keys; pub mod cache_manager; +pub mod keys; pub use self::db::*; diff --git a/ethcore/evm/Cargo.toml b/ethcore/evm/Cargo.toml index cb70c42e425..b5f4d0685fa 100644 --- a/ethcore/evm/Cargo.toml +++ b/ethcore/evm/Cargo.toml @@ -1,4 +1,5 @@ [package] +description = "Parity Ethereum Virtual Machine (EVM) Rust Implementation" name = "evm" version = "0.1.0" authors = ["Parity Technologies "] @@ -14,10 +15,12 @@ vm = { path = "../vm" } keccak-hash = "0.1" parking_lot = "0.7" memory-cache = { path = "../../util/memory-cache" } +num-bigint = "0.2" [dev-dependencies] rustc-hex = "1.0" criterion = "0.2" +hex-literal = "0.2.0" [features] evm-debug = [] diff --git a/ethcore/evm/benches/basic.rs b/ethcore/evm/benches/basic.rs index f17ba363a58..9d0b1575b32 100644 --- a/ethcore/evm/benches/basic.rs +++ b/ethcore/evm/benches/basic.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! benchmarking for EVM @@ -20,140 +20,190 @@ extern crate criterion; extern crate bit_set; extern crate ethereum_types; -extern crate parking_lot; -extern crate heapsize; -extern crate vm; extern crate evm; +extern crate heapsize; extern crate keccak_hash as hash; extern crate memory_cache; extern crate parity_bytes as bytes; +extern crate parking_lot; extern crate rustc_hex; +extern crate vm; -use criterion::{Criterion, Bencher, black_box}; -use std::str::FromStr; -use std::sync::Arc; -use ethereum_types::{U256, Address}; -use vm::{ActionParams, Result, GasLeft, Ext}; -use vm::tests::FakeExt; +use criterion::{black_box, Bencher, Criterion}; +use ethereum_types::{Address, U256}; use evm::Factory; use rustc_hex::FromHex; +use std::{str::FromStr, sync::Arc}; +use vm::{tests::FakeExt, ActionParams, Ext, GasLeft, Result}; criterion_group!( - basic, - simple_loop_log0_usize, - simple_loop_log0_u256, - mem_gas_calculation_same_usize, - mem_gas_calculation_same_u256, - mem_gas_calculation_increasing_usize, - mem_gas_calculation_increasing_u256 + basic, + simple_loop_log0_usize, + simple_loop_log0_u256, + mem_gas_calculation_same_usize, + mem_gas_calculation_same_u256, + mem_gas_calculation_increasing_usize, + mem_gas_calculation_increasing_u256, + blockhash_mulmod_small, + blockhash_mulmod_large, ); criterion_main!(basic); fn simple_loop_log0_usize(b: &mut Criterion) { - b.bench_function("simple_loop_log0_usize", |b| { - simple_loop_log0(U256::from(::std::usize::MAX), b); - }); + b.bench_function("simple_loop_log0_usize", |b| { + simple_loop_log0(U256::from(::std::usize::MAX), b); + }); } fn simple_loop_log0_u256(b: &mut Criterion) { - b.bench_function("simple_loop_log0_u256", |b| { - simple_loop_log0(!U256::zero(), b); - }); + b.bench_function("simple_loop_log0_u256", |b| { + simple_loop_log0(!U256::zero(), b); + }); } fn simple_loop_log0(gas: U256, b: &mut Bencher) { - let factory = Factory::default(); - let mut ext = FakeExt::new(); + let factory = Factory::default(); + let mut ext = FakeExt::new(); - let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let code = black_box( - "62ffffff5b600190036000600fa0600357".from_hex().unwrap() - ); + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let code = black_box("62ffffff5b600190036000600fa0600357".from_hex().unwrap()); - b.iter(|| { - let mut params = ActionParams::default(); - params.address = address.clone(); - params.gas = gas; - params.code = Some(Arc::new(code.clone())); + b.iter(|| { + let mut params = ActionParams::default(); + params.address = address.clone(); + params.gas = gas; + params.code = Some(Arc::new(code.clone())); - let vm = factory.create(params, ext.schedule(), 0); + let vm = factory.create(params, ext.schedule(), 0); - result(vm.exec(&mut ext).ok().unwrap()) - }); + result(vm.exec(&mut ext).ok().unwrap()) + }); } fn mem_gas_calculation_same_usize(b: &mut Criterion) { - b.bench_function("mem_gas_calculation_same_usize", |b| { - mem_gas_calculation_same(U256::from(::std::usize::MAX), b); - }); + b.bench_function("mem_gas_calculation_same_usize", |b| { + mem_gas_calculation_same(U256::from(::std::usize::MAX), b); + }); } fn mem_gas_calculation_same_u256(b: &mut Criterion) { - b.bench_function("mem_gas_calculation_same_u256", |b| { - mem_gas_calculation_same(!U256::zero(), b); - }); + b.bench_function("mem_gas_calculation_same_u256", |b| { + mem_gas_calculation_same(!U256::zero(), b); + }); } fn mem_gas_calculation_same(gas: U256, b: &mut Bencher) { - let factory = Factory::default(); - let mut ext = FakeExt::new(); + let factory = Factory::default(); + let mut ext = FakeExt::new(); - let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - b.iter(|| { - let code = black_box( - "6110006001556001546000555b610fff805560016000540380600055600c57".from_hex().unwrap() - ); + b.iter(|| { + let code = black_box( + "6110006001556001546000555b610fff805560016000540380600055600c57" + .from_hex() + .unwrap(), + ); - let mut params = ActionParams::default(); - params.address = address.clone(); - params.gas = gas; - params.code = Some(Arc::new(code.clone())); + let mut params = ActionParams::default(); + params.address = address.clone(); + params.gas = gas; + params.code = Some(Arc::new(code.clone())); - let vm = factory.create(params, ext.schedule(), 0); + let vm = factory.create(params, ext.schedule(), 0); - result(vm.exec(&mut ext).ok().unwrap()) - }); + result(vm.exec(&mut ext).ok().unwrap()) + }); } fn mem_gas_calculation_increasing_usize(b: &mut Criterion) { - b.bench_function("mem_gas_calculation_increasing_usize", |b| { - mem_gas_calculation_increasing(U256::from(::std::usize::MAX), b); - }); + b.bench_function("mem_gas_calculation_increasing_usize", |b| { + mem_gas_calculation_increasing(U256::from(::std::usize::MAX), b); + }); } fn mem_gas_calculation_increasing_u256(b: &mut Criterion) { - b.bench_function("mem_gas_calculation_increasing_u256", |b| { - mem_gas_calculation_increasing(!U256::zero(), b); - }); + b.bench_function("mem_gas_calculation_increasing_u256", |b| { + mem_gas_calculation_increasing(!U256::zero(), b); + }); } fn mem_gas_calculation_increasing(gas: U256, b: &mut Bencher) { - let factory = Factory::default(); - let mut ext = FakeExt::new(); + let factory = Factory::default(); + let mut ext = FakeExt::new(); + + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + + b.iter(|| { + let code = black_box( + "6110006001556001546000555b610fff60005401805560016000540380600055600c57" + .from_hex() + .unwrap(), + ); + + let mut params = ActionParams::default(); + params.address = address.clone(); + params.gas = gas; + params.code = Some(Arc::new(code.clone())); + + let vm = factory.create(params, ext.schedule(), 0); + + result(vm.exec(&mut ext).ok().unwrap()) + }); +} + +fn blockhash_mulmod_small(b: &mut Criterion) { + b.bench_function("blockhash_mulmod_small", |b| { + let factory = Factory::default(); + let mut ext = FakeExt::new(); + + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + + b.iter(|| { + let code = black_box( + "6080604052348015600f57600080fd5b5060005a90505b60c881111560de5760017effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095060017effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095060017effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095060017effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095060017effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8009505a90506016565b506035806100ed6000396000f3fe6080604052600080fdfea165627a7a72305820bde4a0ac6d0fac28fc879244baf8a6a0eda514bc95fb7ecbcaaebf2556e2687c0029".from_hex().unwrap() + ); + + let mut params = ActionParams::default(); + params.address = address.clone(); + params.gas = U256::from(4_000u64); + params.code = Some(Arc::new(code.clone())); + + let vm = factory.create(params, ext.schedule(), 0); + + result(vm.exec(&mut ext).ok().unwrap()) + }); + }); +} + +fn blockhash_mulmod_large(b: &mut Criterion) { + b.bench_function("blockhash_mulmod_large", |b| { + let factory = Factory::default(); + let mut ext = FakeExt::new(); - let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - b.iter(|| { - let code = black_box( - "6110006001556001546000555b610fff60005401805560016000540380600055600c57".from_hex().unwrap() - ); + b.iter(|| { + let code = black_box( + "608060405234801561001057600080fd5b5060005a90505b60c8811115610177577efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff17efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08009507efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff17efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08009507efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff17efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08009507efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff17efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08009507efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff17efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08009505a9050610017565b506035806101866000396000f3fe6080604052600080fdfea165627a7a72305820dcaec306f67bb96f3044fff25c9af2ec66f01d0954d0656964f046f42f2780670029".from_hex().unwrap() + ); - let mut params = ActionParams::default(); - params.address = address.clone(); - params.gas = gas; - params.code = Some(Arc::new(code.clone())); + let mut params = ActionParams::default(); + params.address = address.clone(); + params.gas = U256::from(4_000u64); + params.code = Some(Arc::new(code.clone())); - let vm = factory.create(params, ext.schedule(), 0); + let vm = factory.create(params, ext.schedule(), 0); - result(vm.exec(&mut ext).ok().unwrap()) + result(vm.exec(&mut ext).ok().unwrap()) + }); }); } fn result(r: Result) -> U256 { - match r { - Ok(GasLeft::Known(gas_left)) => gas_left, - Ok(GasLeft::NeedsReturn { gas_left, .. }) => gas_left, - _ => U256::zero(), - } + match r { + Ok(GasLeft::Known(gas_left)) => gas_left, + Ok(GasLeft::NeedsReturn { gas_left, .. }) => gas_left, + _ => U256::zero(), + } } diff --git a/ethcore/evm/src/evm.rs b/ethcore/evm/src/evm.rs index f8a08b2b2e3..8599f6d5706 100644 --- a/ethcore/evm/src/evm.rs +++ b/ethcore/evm/src/evm.rs @@ -1,35 +1,35 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Evm interface. -use std::{ops, cmp, fmt}; use ethereum_types::{U128, U256, U512}; -use vm::{Ext, Result, ReturnData, GasLeft, Error}; +use std::{cmp, fmt, ops}; +use vm::{Error, Ext, GasLeft, Result, ReturnData}; /// Finalization result. Gas Left: either it is a known value, or it needs to be computed by processing /// a return instruction. #[derive(Debug)] pub struct FinalizationResult { - /// Final amount of gas left. - pub gas_left: U256, - /// Apply execution state changes or revert them. - pub apply_state: bool, - /// Return data buffer. - pub return_data: ReturnData, + /// Final amount of gas left. + pub gas_left: U256, + /// Apply execution state changes or revert them. + pub apply_state: bool, + /// Return data buffer. + pub return_data: ReturnData, } /// Types that can be "finalized" using an EVM. @@ -37,171 +37,188 @@ pub struct FinalizationResult { /// In practice, this is just used to define an inherent impl on /// `Reult>`. pub trait Finalize { - /// Consume the externalities, call return if necessary, and produce call result. - fn finalize(self, ext: E) -> Result; + /// Consume the externalities, call return if necessary, and produce call result. + fn finalize(self, ext: E) -> Result; } impl Finalize for Result { - fn finalize(self, ext: E) -> Result { - match self { - Ok(GasLeft::Known(gas_left)) => Ok(FinalizationResult { gas_left: gas_left, apply_state: true, return_data: ReturnData::empty() }), - Ok(GasLeft::NeedsReturn { gas_left, data, apply_state }) => ext.ret(&gas_left, &data, apply_state).map(|gas_left| FinalizationResult { - gas_left: gas_left, - apply_state: apply_state, - return_data: data, - }), - Err(err) => Err(err), - } - } + fn finalize(self, ext: E) -> Result { + match self { + Ok(GasLeft::Known(gas_left)) => Ok(FinalizationResult { + gas_left, + apply_state: true, + return_data: ReturnData::empty(), + }), + Ok(GasLeft::NeedsReturn { + gas_left, + data, + apply_state, + }) => ext + .ret(&gas_left, &data, apply_state) + .map(|gas_left| FinalizationResult { + gas_left, + apply_state, + return_data: data, + }), + Err(err) => Err(err), + } + } } impl Finalize for Error { - fn finalize(self, _ext: E) -> Result { - Err(self) - } + fn finalize(self, _ext: E) -> Result { + Err(self) + } } /// Cost calculation type. For low-gas usage we calculate costs using usize instead of U256 -pub trait CostType: Sized + From + Copy + Send - + ops::Mul + ops::Div + ops::Add + ops::Sub - + ops::Shr + ops::Shl - + cmp::Ord + fmt::Debug { - /// Converts this cost into `U256` - fn as_u256(&self) -> U256; - /// Tries to fit `U256` into this `Cost` type - fn from_u256(val: U256) -> Result; - /// Convert to usize (may panic) - fn as_usize(&self) -> usize; - /// Add with overflow - fn overflow_add(self, other: Self) -> (Self, bool); - /// Multiple with overflow - fn overflow_mul(self, other: Self) -> (Self, bool); - /// Single-step full multiplication and shift: `(self*other) >> shr` - /// Should not overflow on intermediate steps - fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool); +pub trait CostType: + Sized + + From + + Copy + + Send + + ops::Mul + + ops::Div + + ops::Add + + ops::Sub + + ops::Shr + + ops::Shl + + cmp::Ord + + fmt::Debug +{ + /// Converts this cost into `U256` + fn as_u256(&self) -> U256; + /// Tries to fit `U256` into this `Cost` type + fn from_u256(val: U256) -> Result; + /// Convert to usize (may panic) + fn as_usize(&self) -> usize; + /// Add with overflow + fn overflow_add(self, other: Self) -> (Self, bool); + /// Multiple with overflow + fn overflow_mul(self, other: Self) -> (Self, bool); + /// Single-step full multiplication and shift: `(self*other) >> shr` + /// Should not overflow on intermediate steps + fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool); } impl CostType for U256 { - fn as_u256(&self) -> U256 { - *self - } - - fn from_u256(val: U256) -> Result { - Ok(val) - } - - fn as_usize(&self) -> usize { - self.as_u64() as usize - } - - fn overflow_add(self, other: Self) -> (Self, bool) { - self.overflowing_add(other) - } - - fn overflow_mul(self, other: Self) -> (Self, bool) { - self.overflowing_mul(other) - } - - fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool) { - let x = self.full_mul(other); - let U512(parts) = x; - let overflow = (parts[4] | parts[5] | parts[6] | parts[7]) > 0; - let U512(parts) = x >> shr; - ( - U256([parts[0], parts[1], parts[2], parts[3]]), - overflow - ) - } + fn as_u256(&self) -> U256 { + *self + } + + fn from_u256(val: U256) -> Result { + Ok(val) + } + + fn as_usize(&self) -> usize { + self.as_u64() as usize + } + + fn overflow_add(self, other: Self) -> (Self, bool) { + self.overflowing_add(other) + } + + fn overflow_mul(self, other: Self) -> (Self, bool) { + self.overflowing_mul(other) + } + + fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool) { + let x = self.full_mul(other); + let U512(parts) = x; + let overflow = (parts[4] | parts[5] | parts[6] | parts[7]) > 0; + let U512(parts) = x >> shr; + (U256([parts[0], parts[1], parts[2], parts[3]]), overflow) + } } impl CostType for usize { - fn as_u256(&self) -> U256 { - U256::from(*self) - } - - fn from_u256(val: U256) -> Result { - let res = val.low_u64() as usize; - - // validate if value fits into usize - if U256::from(res) != val { - return Err(Error::OutOfGas); - } - - Ok(res) - } - - fn as_usize(&self) -> usize { - *self - } - - fn overflow_add(self, other: Self) -> (Self, bool) { - self.overflowing_add(other) - } - - fn overflow_mul(self, other: Self) -> (Self, bool) { - self.overflowing_mul(other) - } - - fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool) { - let (c, o) = U128::from(self).overflowing_mul(U128::from(other)); - let U128(parts) = c; - let overflow = o | (parts[1] > 0); - let U128(parts) = c >> shr; - let result = parts[0] as usize; - let overflow = overflow | (parts[0] > result as u64); - (result, overflow) - } + fn as_u256(&self) -> U256 { + U256::from(*self) + } + + fn from_u256(val: U256) -> Result { + let res = val.low_u64() as usize; + + // validate if value fits into usize + if U256::from(res) != val { + return Err(Error::OutOfGas); + } + + Ok(res) + } + + fn as_usize(&self) -> usize { + *self + } + + fn overflow_add(self, other: Self) -> (Self, bool) { + self.overflowing_add(other) + } + + fn overflow_mul(self, other: Self) -> (Self, bool) { + self.overflowing_mul(other) + } + + fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool) { + let (c, o) = U128::from(self).overflowing_mul(U128::from(other)); + let U128(parts) = c; + let overflow = o | (parts[1] > 0); + let U128(parts) = c >> shr; + let result = parts[0] as usize; + let overflow = overflow | (parts[0] > result as u64); + (result, overflow) + } } #[cfg(test)] mod tests { - use ethereum_types::U256; - use super::CostType; - - #[test] - fn should_calculate_overflow_mul_shr_without_overflow() { - // given - let num = 1048576; - - // when - let (res1, o1) = U256::from(num).overflow_mul_shr(U256::from(num), 20); - let (res2, o2) = num.overflow_mul_shr(num, 20); - - // then - assert_eq!(res1, U256::from(num)); - assert!(!o1); - assert_eq!(res2, num); - assert!(!o2); - } - - #[test] - fn should_calculate_overflow_mul_shr_with_overflow() { - // given - let max = u64::max_value(); - let num1 = U256([max, max, max, max]); - let num2 = usize::max_value(); - - // when - let (res1, o1) = num1.overflow_mul_shr(num1, 256); - let (res2, o2) = num2.overflow_mul_shr(num2, 64); - - // then - assert_eq!(res2, num2 - 1); - assert!(o2); - - assert_eq!(res1, !U256::zero() - U256::one()); - assert!(o1); - } - - #[test] - fn should_validate_u256_to_usize_conversion() { - // given - let v = U256::from(usize::max_value()) + U256::from(1); - - // when - let res = usize::from_u256(v); - - // then - assert!(res.is_err()); - } + use super::CostType; + use ethereum_types::U256; + + #[test] + fn should_calculate_overflow_mul_shr_without_overflow() { + // given + let num = 1048576; + + // when + let (res1, o1) = U256::from(num).overflow_mul_shr(U256::from(num), 20); + let (res2, o2) = num.overflow_mul_shr(num, 20); + + // then + assert_eq!(res1, U256::from(num)); + assert!(!o1); + assert_eq!(res2, num); + assert!(!o2); + } + + #[test] + fn should_calculate_overflow_mul_shr_with_overflow() { + // given + let max = u64::max_value(); + let num1 = U256([max, max, max, max]); + let num2 = usize::max_value(); + + // when + let (res1, o1) = num1.overflow_mul_shr(num1, 256); + let (res2, o2) = num2.overflow_mul_shr(num2, 64); + + // then + assert_eq!(res2, num2 - 1); + assert!(o2); + + assert_eq!(res1, !U256::zero() - U256::one()); + assert!(o1); + } + + #[test] + fn should_validate_u256_to_usize_conversion() { + // given + let v = U256::from(usize::max_value()) + U256::from(1); + + // when + let res = usize::from_u256(v); + + // then + assert!(res.is_err()); + } } diff --git a/ethcore/evm/src/factory.rs b/ethcore/evm/src/factory.rs index 484b2852fec..9fa1e708251 100644 --- a/ethcore/evm/src/factory.rs +++ b/ethcore/evm/src/factory.rs @@ -1,82 +1,91 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Evm factory. //! +use super::{interpreter::SharedCache, vm::ActionParams, vmtype::VMType}; +use ethereum_types::U256; use std::sync::Arc; use vm::{Exec, Schedule}; -use ethereum_types::U256; -use super::vm::ActionParams; -use super::interpreter::SharedCache; -use super::vmtype::VMType; /// Evm factory. Creates appropriate Evm. #[derive(Clone)] pub struct Factory { - evm: VMType, - evm_cache: Arc, + evm: VMType, + evm_cache: Arc, } impl Factory { - /// Create fresh instance of VM - /// Might choose implementation depending on supplied gas. - pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box { - match self.evm { - VMType::Interpreter => if Self::can_fit_in_usize(¶ms.gas) { - Box::new(super::interpreter::Interpreter::::new(params, self.evm_cache.clone(), schedule, depth)) - } else { - Box::new(super::interpreter::Interpreter::::new(params, self.evm_cache.clone(), schedule, depth)) - } - } - } + /// Create fresh instance of VM + /// Might choose implementation depending on supplied gas. + pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box { + match self.evm { + VMType::Interpreter => { + if Self::can_fit_in_usize(¶ms.gas) { + Box::new(super::interpreter::Interpreter::::new( + params, + self.evm_cache.clone(), + schedule, + depth, + )) + } else { + Box::new(super::interpreter::Interpreter::::new( + params, + self.evm_cache.clone(), + schedule, + depth, + )) + } + } + } + } - /// Create new instance of specific `VMType` factory, with a size in bytes - /// for caching jump destinations. - pub fn new(evm: VMType, cache_size: usize) -> Self { - Factory { - evm: evm, - evm_cache: Arc::new(SharedCache::new(cache_size)), - } - } + /// Create new instance of specific `VMType` factory, with a size in bytes + /// for caching jump destinations. + pub fn new(evm: VMType, cache_size: usize) -> Self { + Factory { + evm, + evm_cache: Arc::new(SharedCache::new(cache_size)), + } + } - fn can_fit_in_usize(gas: &U256) -> bool { - gas == &U256::from(gas.low_u64() as usize) - } + fn can_fit_in_usize(gas: &U256) -> bool { + gas == &U256::from(gas.low_u64() as usize) + } } impl Default for Factory { - /// Returns native rust evm factory - fn default() -> Factory { - Factory { - evm: VMType::Interpreter, - evm_cache: Arc::new(SharedCache::default()), - } - } + /// Returns native rust evm factory + fn default() -> Factory { + Factory { + evm: VMType::Interpreter, + evm_cache: Arc::new(SharedCache::default()), + } + } } #[test] fn test_create_vm() { - use vm::Ext; - use vm::tests::FakeExt; - use bytes::Bytes; + use bytes::Bytes; + use vm::{tests::FakeExt, Ext}; - let mut params = ActionParams::default(); - params.code = Some(Arc::new(Bytes::default())); - let ext = FakeExt::new(); - let _vm = Factory::default().create(params, ext.schedule(), ext.depth()); + let mut params = ActionParams::default(); + params.code = Some(Arc::new(Bytes::default())); + let ext = FakeExt::new(); + let _vm = Factory::default().create(params, ext.schedule(), ext.depth()); } /// Create tests by injecting different VM factories diff --git a/ethcore/evm/src/instructions.rs b/ethcore/evm/src/instructions.rs index 0cdbb5687da..e120f97df0d 100644 --- a/ethcore/evm/src/instructions.rs +++ b/ethcore/evm/src/instructions.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! VM Instructions list and utility functions @@ -43,559 +43,575 @@ macro_rules! enum_with_from_u8 { } enum_with_from_u8! { - #[doc = "Virtual machine bytecode instruction."] - #[repr(u8)] - #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug, Hash)] - pub enum Instruction { - #[doc = "halts execution"] - STOP = 0x00, - #[doc = "addition operation"] - ADD = 0x01, - #[doc = "mulitplication operation"] - MUL = 0x02, - #[doc = "subtraction operation"] - SUB = 0x03, - #[doc = "integer division operation"] - DIV = 0x04, - #[doc = "signed integer division operation"] - SDIV = 0x05, - #[doc = "modulo remainder operation"] - MOD = 0x06, - #[doc = "signed modulo remainder operation"] - SMOD = 0x07, - #[doc = "unsigned modular addition"] - ADDMOD = 0x08, - #[doc = "unsigned modular multiplication"] - MULMOD = 0x09, - #[doc = "exponential operation"] - EXP = 0x0a, - #[doc = "extend length of signed integer"] - SIGNEXTEND = 0x0b, - - #[doc = "less-than comparision"] - LT = 0x10, - #[doc = "greater-than comparision"] - GT = 0x11, - #[doc = "signed less-than comparision"] - SLT = 0x12, - #[doc = "signed greater-than comparision"] - SGT = 0x13, - #[doc = "equality comparision"] - EQ = 0x14, - #[doc = "simple not operator"] - ISZERO = 0x15, - #[doc = "bitwise AND operation"] - AND = 0x16, - #[doc = "bitwise OR operation"] - OR = 0x17, - #[doc = "bitwise XOR operation"] - XOR = 0x18, - #[doc = "bitwise NOT opertation"] - NOT = 0x19, - #[doc = "retrieve single byte from word"] - BYTE = 0x1a, - #[doc = "shift left operation"] - SHL = 0x1b, - #[doc = "logical shift right operation"] - SHR = 0x1c, - #[doc = "arithmetic shift right operation"] - SAR = 0x1d, - - #[doc = "compute SHA3-256 hash"] - SHA3 = 0x20, - - #[doc = "get address of currently executing account"] - ADDRESS = 0x30, - #[doc = "get balance of the given account"] - BALANCE = 0x31, - #[doc = "get execution origination address"] - ORIGIN = 0x32, - #[doc = "get caller address"] - CALLER = 0x33, - #[doc = "get deposited value by the instruction/transaction responsible for this execution"] - CALLVALUE = 0x34, - #[doc = "get input data of current environment"] - CALLDATALOAD = 0x35, - #[doc = "get size of input data in current environment"] - CALLDATASIZE = 0x36, - #[doc = "copy input data in current environment to memory"] - CALLDATACOPY = 0x37, - #[doc = "get size of code running in current environment"] - CODESIZE = 0x38, - #[doc = "copy code running in current environment to memory"] - CODECOPY = 0x39, - #[doc = "get price of gas in current environment"] - GASPRICE = 0x3a, - #[doc = "get external code size (from another contract)"] - EXTCODESIZE = 0x3b, - #[doc = "copy external code (from another contract)"] - EXTCODECOPY = 0x3c, - #[doc = "get the size of the return data buffer for the last call"] - RETURNDATASIZE = 0x3d, - #[doc = "copy return data buffer to memory"] - RETURNDATACOPY = 0x3e, - #[doc = "return the keccak256 hash of contract code"] - EXTCODEHASH = 0x3f, - - #[doc = "get hash of most recent complete block"] - BLOCKHASH = 0x40, - #[doc = "get the block's coinbase address"] - COINBASE = 0x41, - #[doc = "get the block's timestamp"] - TIMESTAMP = 0x42, - #[doc = "get the block's number"] - NUMBER = 0x43, - #[doc = "get the block's difficulty"] - DIFFICULTY = 0x44, - #[doc = "get the block's gas limit"] - GASLIMIT = 0x45, - - #[doc = "remove item from stack"] - POP = 0x50, - #[doc = "load word from memory"] - MLOAD = 0x51, - #[doc = "save word to memory"] - MSTORE = 0x52, - #[doc = "save byte to memory"] - MSTORE8 = 0x53, - #[doc = "load word from storage"] - SLOAD = 0x54, - #[doc = "save word to storage"] - SSTORE = 0x55, - #[doc = "alter the program counter"] - JUMP = 0x56, - #[doc = "conditionally alter the program counter"] - JUMPI = 0x57, - #[doc = "get the program counter"] - PC = 0x58, - #[doc = "get the size of active memory"] - MSIZE = 0x59, - #[doc = "get the amount of available gas"] - GAS = 0x5a, - #[doc = "set a potential jump destination"] - JUMPDEST = 0x5b, - - #[doc = "place 1 byte item on stack"] - PUSH1 = 0x60, - #[doc = "place 2 byte item on stack"] - PUSH2 = 0x61, - #[doc = "place 3 byte item on stack"] - PUSH3 = 0x62, - #[doc = "place 4 byte item on stack"] - PUSH4 = 0x63, - #[doc = "place 5 byte item on stack"] - PUSH5 = 0x64, - #[doc = "place 6 byte item on stack"] - PUSH6 = 0x65, - #[doc = "place 7 byte item on stack"] - PUSH7 = 0x66, - #[doc = "place 8 byte item on stack"] - PUSH8 = 0x67, - #[doc = "place 9 byte item on stack"] - PUSH9 = 0x68, - #[doc = "place 10 byte item on stack"] - PUSH10 = 0x69, - #[doc = "place 11 byte item on stack"] - PUSH11 = 0x6a, - #[doc = "place 12 byte item on stack"] - PUSH12 = 0x6b, - #[doc = "place 13 byte item on stack"] - PUSH13 = 0x6c, - #[doc = "place 14 byte item on stack"] - PUSH14 = 0x6d, - #[doc = "place 15 byte item on stack"] - PUSH15 = 0x6e, - #[doc = "place 16 byte item on stack"] - PUSH16 = 0x6f, - #[doc = "place 17 byte item on stack"] - PUSH17 = 0x70, - #[doc = "place 18 byte item on stack"] - PUSH18 = 0x71, - #[doc = "place 19 byte item on stack"] - PUSH19 = 0x72, - #[doc = "place 20 byte item on stack"] - PUSH20 = 0x73, - #[doc = "place 21 byte item on stack"] - PUSH21 = 0x74, - #[doc = "place 22 byte item on stack"] - PUSH22 = 0x75, - #[doc = "place 23 byte item on stack"] - PUSH23 = 0x76, - #[doc = "place 24 byte item on stack"] - PUSH24 = 0x77, - #[doc = "place 25 byte item on stack"] - PUSH25 = 0x78, - #[doc = "place 26 byte item on stack"] - PUSH26 = 0x79, - #[doc = "place 27 byte item on stack"] - PUSH27 = 0x7a, - #[doc = "place 28 byte item on stack"] - PUSH28 = 0x7b, - #[doc = "place 29 byte item on stack"] - PUSH29 = 0x7c, - #[doc = "place 30 byte item on stack"] - PUSH30 = 0x7d, - #[doc = "place 31 byte item on stack"] - PUSH31 = 0x7e, - #[doc = "place 32 byte item on stack"] - PUSH32 = 0x7f, - - #[doc = "copies the highest item in the stack to the top of the stack"] - DUP1 = 0x80, - #[doc = "copies the second highest item in the stack to the top of the stack"] - DUP2 = 0x81, - #[doc = "copies the third highest item in the stack to the top of the stack"] - DUP3 = 0x82, - #[doc = "copies the 4th highest item in the stack to the top of the stack"] - DUP4 = 0x83, - #[doc = "copies the 5th highest item in the stack to the top of the stack"] - DUP5 = 0x84, - #[doc = "copies the 6th highest item in the stack to the top of the stack"] - DUP6 = 0x85, - #[doc = "copies the 7th highest item in the stack to the top of the stack"] - DUP7 = 0x86, - #[doc = "copies the 8th highest item in the stack to the top of the stack"] - DUP8 = 0x87, - #[doc = "copies the 9th highest item in the stack to the top of the stack"] - DUP9 = 0x88, - #[doc = "copies the 10th highest item in the stack to the top of the stack"] - DUP10 = 0x89, - #[doc = "copies the 11th highest item in the stack to the top of the stack"] - DUP11 = 0x8a, - #[doc = "copies the 12th highest item in the stack to the top of the stack"] - DUP12 = 0x8b, - #[doc = "copies the 13th highest item in the stack to the top of the stack"] - DUP13 = 0x8c, - #[doc = "copies the 14th highest item in the stack to the top of the stack"] - DUP14 = 0x8d, - #[doc = "copies the 15th highest item in the stack to the top of the stack"] - DUP15 = 0x8e, - #[doc = "copies the 16th highest item in the stack to the top of the stack"] - DUP16 = 0x8f, - - #[doc = "swaps the highest and second highest value on the stack"] - SWAP1 = 0x90, - #[doc = "swaps the highest and third highest value on the stack"] - SWAP2 = 0x91, - #[doc = "swaps the highest and 4th highest value on the stack"] - SWAP3 = 0x92, - #[doc = "swaps the highest and 5th highest value on the stack"] - SWAP4 = 0x93, - #[doc = "swaps the highest and 6th highest value on the stack"] - SWAP5 = 0x94, - #[doc = "swaps the highest and 7th highest value on the stack"] - SWAP6 = 0x95, - #[doc = "swaps the highest and 8th highest value on the stack"] - SWAP7 = 0x96, - #[doc = "swaps the highest and 9th highest value on the stack"] - SWAP8 = 0x97, - #[doc = "swaps the highest and 10th highest value on the stack"] - SWAP9 = 0x98, - #[doc = "swaps the highest and 11th highest value on the stack"] - SWAP10 = 0x99, - #[doc = "swaps the highest and 12th highest value on the stack"] - SWAP11 = 0x9a, - #[doc = "swaps the highest and 13th highest value on the stack"] - SWAP12 = 0x9b, - #[doc = "swaps the highest and 14th highest value on the stack"] - SWAP13 = 0x9c, - #[doc = "swaps the highest and 15th highest value on the stack"] - SWAP14 = 0x9d, - #[doc = "swaps the highest and 16th highest value on the stack"] - SWAP15 = 0x9e, - #[doc = "swaps the highest and 17th highest value on the stack"] - SWAP16 = 0x9f, - - #[doc = "Makes a log entry, no topics."] - LOG0 = 0xa0, - #[doc = "Makes a log entry, 1 topic."] - LOG1 = 0xa1, - #[doc = "Makes a log entry, 2 topics."] - LOG2 = 0xa2, - #[doc = "Makes a log entry, 3 topics."] - LOG3 = 0xa3, - #[doc = "Makes a log entry, 4 topics."] - LOG4 = 0xa4, - - #[doc = "create a new account with associated code"] - CREATE = 0xf0, - #[doc = "message-call into an account"] - CALL = 0xf1, - #[doc = "message-call with another account's code only"] - CALLCODE = 0xf2, - #[doc = "halt execution returning output data"] - RETURN = 0xf3, - #[doc = "like CALLCODE but keeps caller's value and sender"] - DELEGATECALL = 0xf4, - #[doc = "create a new account and set creation address to sha3(sender + sha3(init code)) % 2**160"] - CREATE2 = 0xf5, - #[doc = "stop execution and revert state changes. Return output data."] - REVERT = 0xfd, - #[doc = "like CALL but it does not take value, nor modify the state"] - STATICCALL = 0xfa, - #[doc = "halt execution and register account for later deletion"] - SUICIDE = 0xff, - } + #[doc = "Virtual machine bytecode instruction."] + #[repr(u8)] + #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug, Hash)] + pub enum Instruction { + #[doc = "halts execution"] + STOP = 0x00, + #[doc = "addition operation"] + ADD = 0x01, + #[doc = "mulitplication operation"] + MUL = 0x02, + #[doc = "subtraction operation"] + SUB = 0x03, + #[doc = "integer division operation"] + DIV = 0x04, + #[doc = "signed integer division operation"] + SDIV = 0x05, + #[doc = "modulo remainder operation"] + MOD = 0x06, + #[doc = "signed modulo remainder operation"] + SMOD = 0x07, + #[doc = "unsigned modular addition"] + ADDMOD = 0x08, + #[doc = "unsigned modular multiplication"] + MULMOD = 0x09, + #[doc = "exponential operation"] + EXP = 0x0a, + #[doc = "extend length of signed integer"] + SIGNEXTEND = 0x0b, + + #[doc = "less-than comparision"] + LT = 0x10, + #[doc = "greater-than comparision"] + GT = 0x11, + #[doc = "signed less-than comparision"] + SLT = 0x12, + #[doc = "signed greater-than comparision"] + SGT = 0x13, + #[doc = "equality comparision"] + EQ = 0x14, + #[doc = "simple not operator"] + ISZERO = 0x15, + #[doc = "bitwise AND operation"] + AND = 0x16, + #[doc = "bitwise OR operation"] + OR = 0x17, + #[doc = "bitwise XOR operation"] + XOR = 0x18, + #[doc = "bitwise NOT opertation"] + NOT = 0x19, + #[doc = "retrieve single byte from word"] + BYTE = 0x1a, + #[doc = "shift left operation"] + SHL = 0x1b, + #[doc = "logical shift right operation"] + SHR = 0x1c, + #[doc = "arithmetic shift right operation"] + SAR = 0x1d, + + #[doc = "compute SHA3-256 hash"] + SHA3 = 0x20, + + #[doc = "get address of currently executing account"] + ADDRESS = 0x30, + #[doc = "get balance of the given account"] + BALANCE = 0x31, + #[doc = "get execution origination address"] + ORIGIN = 0x32, + #[doc = "get caller address"] + CALLER = 0x33, + #[doc = "get deposited value by the instruction/transaction responsible for this execution"] + CALLVALUE = 0x34, + #[doc = "get input data of current environment"] + CALLDATALOAD = 0x35, + #[doc = "get size of input data in current environment"] + CALLDATASIZE = 0x36, + #[doc = "copy input data in current environment to memory"] + CALLDATACOPY = 0x37, + #[doc = "get size of code running in current environment"] + CODESIZE = 0x38, + #[doc = "copy code running in current environment to memory"] + CODECOPY = 0x39, + #[doc = "get price of gas in current environment"] + GASPRICE = 0x3a, + #[doc = "get external code size (from another contract)"] + EXTCODESIZE = 0x3b, + #[doc = "copy external code (from another contract)"] + EXTCODECOPY = 0x3c, + #[doc = "get the size of the return data buffer for the last call"] + RETURNDATASIZE = 0x3d, + #[doc = "copy return data buffer to memory"] + RETURNDATACOPY = 0x3e, + #[doc = "return the keccak256 hash of contract code"] + EXTCODEHASH = 0x3f, + + #[doc = "get hash of most recent complete block"] + BLOCKHASH = 0x40, + #[doc = "get the block's coinbase address"] + COINBASE = 0x41, + #[doc = "get the block's timestamp"] + TIMESTAMP = 0x42, + #[doc = "get the block's number"] + NUMBER = 0x43, + #[doc = "get the block's difficulty"] + DIFFICULTY = 0x44, + #[doc = "get the block's gas limit"] + GASLIMIT = 0x45, + #[doc = "get chain ID"] + CHAINID = 0x46, + #[doc = "get balance of own account"] + SELFBALANCE = 0x47, + + #[doc = "remove item from stack"] + POP = 0x50, + #[doc = "load word from memory"] + MLOAD = 0x51, + #[doc = "save word to memory"] + MSTORE = 0x52, + #[doc = "save byte to memory"] + MSTORE8 = 0x53, + #[doc = "load word from storage"] + SLOAD = 0x54, + #[doc = "save word to storage"] + SSTORE = 0x55, + #[doc = "alter the program counter"] + JUMP = 0x56, + #[doc = "conditionally alter the program counter"] + JUMPI = 0x57, + #[doc = "get the program counter"] + PC = 0x58, + #[doc = "get the size of active memory"] + MSIZE = 0x59, + #[doc = "get the amount of available gas"] + GAS = 0x5a, + #[doc = "set a potential jump destination"] + JUMPDEST = 0x5b, + + #[doc = "place 1 byte item on stack"] + PUSH1 = 0x60, + #[doc = "place 2 byte item on stack"] + PUSH2 = 0x61, + #[doc = "place 3 byte item on stack"] + PUSH3 = 0x62, + #[doc = "place 4 byte item on stack"] + PUSH4 = 0x63, + #[doc = "place 5 byte item on stack"] + PUSH5 = 0x64, + #[doc = "place 6 byte item on stack"] + PUSH6 = 0x65, + #[doc = "place 7 byte item on stack"] + PUSH7 = 0x66, + #[doc = "place 8 byte item on stack"] + PUSH8 = 0x67, + #[doc = "place 9 byte item on stack"] + PUSH9 = 0x68, + #[doc = "place 10 byte item on stack"] + PUSH10 = 0x69, + #[doc = "place 11 byte item on stack"] + PUSH11 = 0x6a, + #[doc = "place 12 byte item on stack"] + PUSH12 = 0x6b, + #[doc = "place 13 byte item on stack"] + PUSH13 = 0x6c, + #[doc = "place 14 byte item on stack"] + PUSH14 = 0x6d, + #[doc = "place 15 byte item on stack"] + PUSH15 = 0x6e, + #[doc = "place 16 byte item on stack"] + PUSH16 = 0x6f, + #[doc = "place 17 byte item on stack"] + PUSH17 = 0x70, + #[doc = "place 18 byte item on stack"] + PUSH18 = 0x71, + #[doc = "place 19 byte item on stack"] + PUSH19 = 0x72, + #[doc = "place 20 byte item on stack"] + PUSH20 = 0x73, + #[doc = "place 21 byte item on stack"] + PUSH21 = 0x74, + #[doc = "place 22 byte item on stack"] + PUSH22 = 0x75, + #[doc = "place 23 byte item on stack"] + PUSH23 = 0x76, + #[doc = "place 24 byte item on stack"] + PUSH24 = 0x77, + #[doc = "place 25 byte item on stack"] + PUSH25 = 0x78, + #[doc = "place 26 byte item on stack"] + PUSH26 = 0x79, + #[doc = "place 27 byte item on stack"] + PUSH27 = 0x7a, + #[doc = "place 28 byte item on stack"] + PUSH28 = 0x7b, + #[doc = "place 29 byte item on stack"] + PUSH29 = 0x7c, + #[doc = "place 30 byte item on stack"] + PUSH30 = 0x7d, + #[doc = "place 31 byte item on stack"] + PUSH31 = 0x7e, + #[doc = "place 32 byte item on stack"] + PUSH32 = 0x7f, + + #[doc = "copies the highest item in the stack to the top of the stack"] + DUP1 = 0x80, + #[doc = "copies the second highest item in the stack to the top of the stack"] + DUP2 = 0x81, + #[doc = "copies the third highest item in the stack to the top of the stack"] + DUP3 = 0x82, + #[doc = "copies the 4th highest item in the stack to the top of the stack"] + DUP4 = 0x83, + #[doc = "copies the 5th highest item in the stack to the top of the stack"] + DUP5 = 0x84, + #[doc = "copies the 6th highest item in the stack to the top of the stack"] + DUP6 = 0x85, + #[doc = "copies the 7th highest item in the stack to the top of the stack"] + DUP7 = 0x86, + #[doc = "copies the 8th highest item in the stack to the top of the stack"] + DUP8 = 0x87, + #[doc = "copies the 9th highest item in the stack to the top of the stack"] + DUP9 = 0x88, + #[doc = "copies the 10th highest item in the stack to the top of the stack"] + DUP10 = 0x89, + #[doc = "copies the 11th highest item in the stack to the top of the stack"] + DUP11 = 0x8a, + #[doc = "copies the 12th highest item in the stack to the top of the stack"] + DUP12 = 0x8b, + #[doc = "copies the 13th highest item in the stack to the top of the stack"] + DUP13 = 0x8c, + #[doc = "copies the 14th highest item in the stack to the top of the stack"] + DUP14 = 0x8d, + #[doc = "copies the 15th highest item in the stack to the top of the stack"] + DUP15 = 0x8e, + #[doc = "copies the 16th highest item in the stack to the top of the stack"] + DUP16 = 0x8f, + + #[doc = "swaps the highest and second highest value on the stack"] + SWAP1 = 0x90, + #[doc = "swaps the highest and third highest value on the stack"] + SWAP2 = 0x91, + #[doc = "swaps the highest and 4th highest value on the stack"] + SWAP3 = 0x92, + #[doc = "swaps the highest and 5th highest value on the stack"] + SWAP4 = 0x93, + #[doc = "swaps the highest and 6th highest value on the stack"] + SWAP5 = 0x94, + #[doc = "swaps the highest and 7th highest value on the stack"] + SWAP6 = 0x95, + #[doc = "swaps the highest and 8th highest value on the stack"] + SWAP7 = 0x96, + #[doc = "swaps the highest and 9th highest value on the stack"] + SWAP8 = 0x97, + #[doc = "swaps the highest and 10th highest value on the stack"] + SWAP9 = 0x98, + #[doc = "swaps the highest and 11th highest value on the stack"] + SWAP10 = 0x99, + #[doc = "swaps the highest and 12th highest value on the stack"] + SWAP11 = 0x9a, + #[doc = "swaps the highest and 13th highest value on the stack"] + SWAP12 = 0x9b, + #[doc = "swaps the highest and 14th highest value on the stack"] + SWAP13 = 0x9c, + #[doc = "swaps the highest and 15th highest value on the stack"] + SWAP14 = 0x9d, + #[doc = "swaps the highest and 16th highest value on the stack"] + SWAP15 = 0x9e, + #[doc = "swaps the highest and 17th highest value on the stack"] + SWAP16 = 0x9f, + + #[doc = "Makes a log entry, no topics."] + LOG0 = 0xa0, + #[doc = "Makes a log entry, 1 topic."] + LOG1 = 0xa1, + #[doc = "Makes a log entry, 2 topics."] + LOG2 = 0xa2, + #[doc = "Makes a log entry, 3 topics."] + LOG3 = 0xa3, + #[doc = "Makes a log entry, 4 topics."] + LOG4 = 0xa4, + + #[doc = "Marks the entry point to a subroutine."] + BEGINSUB = 0x5c, + #[doc = "Returns from a subroutine."] + RETURNSUB = 0x5d, + #[doc = "Jumps to a defined BEGINSUB subroutine."] + JUMPSUB = 0x5e, + + #[doc = "create a new account with associated code"] + CREATE = 0xf0, + #[doc = "message-call into an account"] + CALL = 0xf1, + #[doc = "message-call with another account's code only"] + CALLCODE = 0xf2, + #[doc = "halt execution returning output data"] + RETURN = 0xf3, + #[doc = "like CALLCODE but keeps caller's value and sender"] + DELEGATECALL = 0xf4, + #[doc = "create a new account and set creation address to sha3(sender + sha3(init code)) % 2**160"] + CREATE2 = 0xf5, + #[doc = "stop execution and revert state changes. Return output data."] + REVERT = 0xfd, + #[doc = "like CALL but it does not take value, nor modify the state"] + STATICCALL = 0xfa, + #[doc = "halt execution and register account for later deletion"] + SUICIDE = 0xff, + } } impl Instruction { - /// Returns true if given instruction is `PUSHN` instruction. - pub fn is_push(&self) -> bool { - *self >= PUSH1 && *self <= PUSH32 - } - - /// Returns number of bytes to read for `PUSHN` instruction - /// PUSH1 -> 1 - pub fn push_bytes(&self) -> Option { - if self.is_push() { - Some(((*self as u8) - (PUSH1 as u8) + 1) as usize) - } else { - None - } - } - - /// Returns stack position of item to duplicate - /// DUP1 -> 0 - pub fn dup_position(&self) -> Option { - if *self >= DUP1 && *self <= DUP16 { - Some(((*self as u8) - (DUP1 as u8)) as usize) - } else { - None - } - } - - /// Returns stack position of item to SWAP top with - /// SWAP1 -> 1 - pub fn swap_position(&self) -> Option { - if *self >= SWAP1 && *self <= SWAP16 { - Some(((*self as u8) - (SWAP1 as u8) + 1) as usize) - } else { - None - } - } - - /// Returns number of topics to take from stack - /// LOG0 -> 0 - pub fn log_topics(&self) -> Option { - if *self >= LOG0 && *self <= LOG4 { - Some(((*self as u8) - (LOG0 as u8)) as usize) - } else { - None - } - } - - /// Returns the instruction info. - pub fn info(&self) -> &'static InstructionInfo { - INSTRUCTIONS[*self as usize].as_ref().expect("A instruction is defined in Instruction enum, but it is not found in InstructionInfo struct; this indicates a logic failure in the code.") - } + /// Returns true if given instruction is `PUSHN` instruction. + pub fn is_push(&self) -> bool { + *self >= PUSH1 && *self <= PUSH32 + } + + /// Returns number of bytes to read for `PUSHN` instruction + /// PUSH1 -> 1 + pub fn push_bytes(&self) -> Option { + if self.is_push() { + Some(((*self as u8) - (PUSH1 as u8) + 1) as usize) + } else { + None + } + } + + /// Returns stack position of item to duplicate + /// DUP1 -> 0 + pub fn dup_position(&self) -> Option { + if *self >= DUP1 && *self <= DUP16 { + Some(((*self as u8) - (DUP1 as u8)) as usize) + } else { + None + } + } + + /// Returns stack position of item to SWAP top with + /// SWAP1 -> 1 + pub fn swap_position(&self) -> Option { + if *self >= SWAP1 && *self <= SWAP16 { + Some(((*self as u8) - (SWAP1 as u8) + 1) as usize) + } else { + None + } + } + + /// Returns number of topics to take from stack + /// LOG0 -> 0 + pub fn log_topics(&self) -> Option { + if *self >= LOG0 && *self <= LOG4 { + Some(((*self as u8) - (LOG0 as u8)) as usize) + } else { + None + } + } + + /// Returns the instruction info. + pub fn info(&self) -> &'static InstructionInfo { + INSTRUCTIONS[*self as usize].as_ref().expect("A instruction is defined in Instruction enum, but it is not found in InstructionInfo struct; this indicates a logic failure in the code.") + } } #[derive(PartialEq, Clone, Copy)] pub enum GasPriceTier { - /// 0 Zero - Zero, - /// 2 Quick - Base, - /// 3 Fastest - VeryLow, - /// 5 Fast - Low, - /// 8 Mid - Mid, - /// 10 Slow - High, - /// 20 Ext - Ext, - /// Multiparam or otherwise special - Special, + /// 0 Zero + Zero, + /// 2 Quick + Base, + /// 3 Fastest + VeryLow, + /// 5 Fast + Low, + /// 8 Mid + Mid, + /// 10 Slow + High, + /// 20 Ext + Ext, + /// Multiparam or otherwise special + Special, } impl GasPriceTier { - /// Returns the index in schedule for specific `GasPriceTier` - pub fn idx(&self) -> usize { - match self { - &GasPriceTier::Zero => 0, - &GasPriceTier::Base => 1, - &GasPriceTier::VeryLow => 2, - &GasPriceTier::Low => 3, - &GasPriceTier::Mid => 4, - &GasPriceTier::High => 5, - &GasPriceTier::Ext => 6, - &GasPriceTier::Special => 7, - } - } + /// Returns the index in schedule for specific `GasPriceTier` + pub fn idx(&self) -> usize { + match self { + &GasPriceTier::Zero => 0, + &GasPriceTier::Base => 1, + &GasPriceTier::VeryLow => 2, + &GasPriceTier::Low => 3, + &GasPriceTier::Mid => 4, + &GasPriceTier::High => 5, + &GasPriceTier::Ext => 6, + &GasPriceTier::Special => 7, + } + } } /// EVM instruction information. #[derive(Copy, Clone)] pub struct InstructionInfo { - /// Mnemonic name. - pub name: &'static str, - /// Number of stack arguments. - pub args: usize, - /// Number of returned stack items. - pub ret: usize, - /// Gas price tier. - pub tier: GasPriceTier + /// Mnemonic name. + pub name: &'static str, + /// Number of stack arguments. + pub args: usize, + /// Number of returned stack items. + pub ret: usize, + /// Gas price tier. + pub tier: GasPriceTier, } impl InstructionInfo { - /// Create new instruction info. - pub fn new(name: &'static str, args: usize, ret: usize, tier: GasPriceTier) -> Self { - InstructionInfo { - name: name, - args: args, - ret: ret, - tier: tier - } - } + /// Create new instruction info. + pub fn new(name: &'static str, args: usize, ret: usize, tier: GasPriceTier) -> Self { + InstructionInfo { + name, + args, + ret, + tier, + } + } } lazy_static! { - /// Static instruction table. - static ref INSTRUCTIONS: [Option; 0x100] = { - let mut arr = [None; 0x100]; - arr[STOP as usize] = Some(InstructionInfo::new("STOP", 0, 0, GasPriceTier::Zero)); - arr[ADD as usize] = Some(InstructionInfo::new("ADD", 2, 1, GasPriceTier::VeryLow)); - arr[SUB as usize] = Some(InstructionInfo::new("SUB", 2, 1, GasPriceTier::VeryLow)); - arr[MUL as usize] = Some(InstructionInfo::new("MUL", 2, 1, GasPriceTier::Low)); - arr[DIV as usize] = Some(InstructionInfo::new("DIV", 2, 1, GasPriceTier::Low)); - arr[SDIV as usize] = Some(InstructionInfo::new("SDIV", 2, 1, GasPriceTier::Low)); - arr[MOD as usize] = Some(InstructionInfo::new("MOD", 2, 1, GasPriceTier::Low)); - arr[SMOD as usize] = Some(InstructionInfo::new("SMOD", 2, 1, GasPriceTier::Low)); - arr[EXP as usize] = Some(InstructionInfo::new("EXP", 2, 1, GasPriceTier::Special)); - arr[NOT as usize] = Some(InstructionInfo::new("NOT", 1, 1, GasPriceTier::VeryLow)); - arr[LT as usize] = Some(InstructionInfo::new("LT", 2, 1, GasPriceTier::VeryLow)); - arr[GT as usize] = Some(InstructionInfo::new("GT", 2, 1, GasPriceTier::VeryLow)); - arr[SLT as usize] = Some(InstructionInfo::new("SLT", 2, 1, GasPriceTier::VeryLow)); - arr[SGT as usize] = Some(InstructionInfo::new("SGT", 2, 1, GasPriceTier::VeryLow)); - arr[EQ as usize] = Some(InstructionInfo::new("EQ", 2, 1, GasPriceTier::VeryLow)); - arr[ISZERO as usize] = Some(InstructionInfo::new("ISZERO", 1, 1, GasPriceTier::VeryLow)); - arr[AND as usize] = Some(InstructionInfo::new("AND", 2, 1, GasPriceTier::VeryLow)); - arr[OR as usize] = Some(InstructionInfo::new("OR", 2, 1, GasPriceTier::VeryLow)); - arr[XOR as usize] = Some(InstructionInfo::new("XOR", 2, 1, GasPriceTier::VeryLow)); - arr[BYTE as usize] = Some(InstructionInfo::new("BYTE", 2, 1, GasPriceTier::VeryLow)); - arr[SHL as usize] = Some(InstructionInfo::new("SHL", 2, 1, GasPriceTier::VeryLow)); - arr[SHR as usize] = Some(InstructionInfo::new("SHR", 2, 1, GasPriceTier::VeryLow)); - arr[SAR as usize] = Some(InstructionInfo::new("SAR", 2, 1, GasPriceTier::VeryLow)); - arr[ADDMOD as usize] = Some(InstructionInfo::new("ADDMOD", 3, 1, GasPriceTier::Mid)); - arr[MULMOD as usize] = Some(InstructionInfo::new("MULMOD", 3, 1, GasPriceTier::Mid)); - arr[SIGNEXTEND as usize] = Some(InstructionInfo::new("SIGNEXTEND", 2, 1, GasPriceTier::Low)); - arr[RETURNDATASIZE as usize] = Some(InstructionInfo::new("RETURNDATASIZE", 0, 1, GasPriceTier::Base)); - arr[RETURNDATACOPY as usize] = Some(InstructionInfo::new("RETURNDATACOPY", 3, 0, GasPriceTier::VeryLow)); - arr[SHA3 as usize] = Some(InstructionInfo::new("SHA3", 2, 1, GasPriceTier::Special)); - arr[ADDRESS as usize] = Some(InstructionInfo::new("ADDRESS", 0, 1, GasPriceTier::Base)); - arr[BALANCE as usize] = Some(InstructionInfo::new("BALANCE", 1, 1, GasPriceTier::Special)); - arr[ORIGIN as usize] = Some(InstructionInfo::new("ORIGIN", 0, 1, GasPriceTier::Base)); - arr[CALLER as usize] = Some(InstructionInfo::new("CALLER", 0, 1, GasPriceTier::Base)); - arr[CALLVALUE as usize] = Some(InstructionInfo::new("CALLVALUE", 0, 1, GasPriceTier::Base)); - arr[CALLDATALOAD as usize] = Some(InstructionInfo::new("CALLDATALOAD", 1, 1, GasPriceTier::VeryLow)); - arr[CALLDATASIZE as usize] = Some(InstructionInfo::new("CALLDATASIZE", 0, 1, GasPriceTier::Base)); - arr[CALLDATACOPY as usize] = Some(InstructionInfo::new("CALLDATACOPY", 3, 0, GasPriceTier::VeryLow)); - arr[EXTCODEHASH as usize] = Some(InstructionInfo::new("EXTCODEHASH", 1, 1, GasPriceTier::Special)); - arr[CODESIZE as usize] = Some(InstructionInfo::new("CODESIZE", 0, 1, GasPriceTier::Base)); - arr[CODECOPY as usize] = Some(InstructionInfo::new("CODECOPY", 3, 0, GasPriceTier::VeryLow)); - arr[GASPRICE as usize] = Some(InstructionInfo::new("GASPRICE", 0, 1, GasPriceTier::Base)); - arr[EXTCODESIZE as usize] = Some(InstructionInfo::new("EXTCODESIZE", 1, 1, GasPriceTier::Special)); - arr[EXTCODECOPY as usize] = Some(InstructionInfo::new("EXTCODECOPY", 4, 0, GasPriceTier::Special)); - arr[BLOCKHASH as usize] = Some(InstructionInfo::new("BLOCKHASH", 1, 1, GasPriceTier::Ext)); - arr[COINBASE as usize] = Some(InstructionInfo::new("COINBASE", 0, 1, GasPriceTier::Base)); - arr[TIMESTAMP as usize] = Some(InstructionInfo::new("TIMESTAMP", 0, 1, GasPriceTier::Base)); - arr[NUMBER as usize] = Some(InstructionInfo::new("NUMBER", 0, 1, GasPriceTier::Base)); - arr[DIFFICULTY as usize] = Some(InstructionInfo::new("DIFFICULTY", 0, 1, GasPriceTier::Base)); - arr[GASLIMIT as usize] = Some(InstructionInfo::new("GASLIMIT", 0, 1, GasPriceTier::Base)); - arr[POP as usize] = Some(InstructionInfo::new("POP", 1, 0, GasPriceTier::Base)); - arr[MLOAD as usize] = Some(InstructionInfo::new("MLOAD", 1, 1, GasPriceTier::VeryLow)); - arr[MSTORE as usize] = Some(InstructionInfo::new("MSTORE", 2, 0, GasPriceTier::VeryLow)); - arr[MSTORE8 as usize] = Some(InstructionInfo::new("MSTORE8", 2, 0, GasPriceTier::VeryLow)); - arr[SLOAD as usize] = Some(InstructionInfo::new("SLOAD", 1, 1, GasPriceTier::Special)); - arr[SSTORE as usize] = Some(InstructionInfo::new("SSTORE", 2, 0, GasPriceTier::Special)); - arr[JUMP as usize] = Some(InstructionInfo::new("JUMP", 1, 0, GasPriceTier::Mid)); - arr[JUMPI as usize] = Some(InstructionInfo::new("JUMPI", 2, 0, GasPriceTier::High)); - arr[PC as usize] = Some(InstructionInfo::new("PC", 0, 1, GasPriceTier::Base)); - arr[MSIZE as usize] = Some(InstructionInfo::new("MSIZE", 0, 1, GasPriceTier::Base)); - arr[GAS as usize] = Some(InstructionInfo::new("GAS", 0, 1, GasPriceTier::Base)); - arr[JUMPDEST as usize] = Some(InstructionInfo::new("JUMPDEST", 0, 0, GasPriceTier::Special)); - arr[PUSH1 as usize] = Some(InstructionInfo::new("PUSH1", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH2 as usize] = Some(InstructionInfo::new("PUSH2", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH3 as usize] = Some(InstructionInfo::new("PUSH3", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH4 as usize] = Some(InstructionInfo::new("PUSH4", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH5 as usize] = Some(InstructionInfo::new("PUSH5", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH6 as usize] = Some(InstructionInfo::new("PUSH6", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH7 as usize] = Some(InstructionInfo::new("PUSH7", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH8 as usize] = Some(InstructionInfo::new("PUSH8", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH9 as usize] = Some(InstructionInfo::new("PUSH9", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH10 as usize] = Some(InstructionInfo::new("PUSH10", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH11 as usize] = Some(InstructionInfo::new("PUSH11", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH12 as usize] = Some(InstructionInfo::new("PUSH12", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH13 as usize] = Some(InstructionInfo::new("PUSH13", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH14 as usize] = Some(InstructionInfo::new("PUSH14", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH15 as usize] = Some(InstructionInfo::new("PUSH15", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH16 as usize] = Some(InstructionInfo::new("PUSH16", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH17 as usize] = Some(InstructionInfo::new("PUSH17", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH18 as usize] = Some(InstructionInfo::new("PUSH18", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH19 as usize] = Some(InstructionInfo::new("PUSH19", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH20 as usize] = Some(InstructionInfo::new("PUSH20", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH21 as usize] = Some(InstructionInfo::new("PUSH21", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH22 as usize] = Some(InstructionInfo::new("PUSH22", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH23 as usize] = Some(InstructionInfo::new("PUSH23", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH24 as usize] = Some(InstructionInfo::new("PUSH24", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH25 as usize] = Some(InstructionInfo::new("PUSH25", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH26 as usize] = Some(InstructionInfo::new("PUSH26", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH27 as usize] = Some(InstructionInfo::new("PUSH27", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH28 as usize] = Some(InstructionInfo::new("PUSH28", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH29 as usize] = Some(InstructionInfo::new("PUSH29", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH30 as usize] = Some(InstructionInfo::new("PUSH30", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH31 as usize] = Some(InstructionInfo::new("PUSH31", 0, 1, GasPriceTier::VeryLow)); - arr[PUSH32 as usize] = Some(InstructionInfo::new("PUSH32", 0, 1, GasPriceTier::VeryLow)); - arr[DUP1 as usize] = Some(InstructionInfo::new("DUP1", 1, 2, GasPriceTier::VeryLow)); - arr[DUP2 as usize] = Some(InstructionInfo::new("DUP2", 2, 3, GasPriceTier::VeryLow)); - arr[DUP3 as usize] = Some(InstructionInfo::new("DUP3", 3, 4, GasPriceTier::VeryLow)); - arr[DUP4 as usize] = Some(InstructionInfo::new("DUP4", 4, 5, GasPriceTier::VeryLow)); - arr[DUP5 as usize] = Some(InstructionInfo::new("DUP5", 5, 6, GasPriceTier::VeryLow)); - arr[DUP6 as usize] = Some(InstructionInfo::new("DUP6", 6, 7, GasPriceTier::VeryLow)); - arr[DUP7 as usize] = Some(InstructionInfo::new("DUP7", 7, 8, GasPriceTier::VeryLow)); - arr[DUP8 as usize] = Some(InstructionInfo::new("DUP8", 8, 9, GasPriceTier::VeryLow)); - arr[DUP9 as usize] = Some(InstructionInfo::new("DUP9", 9, 10, GasPriceTier::VeryLow)); - arr[DUP10 as usize] = Some(InstructionInfo::new("DUP10", 10, 11, GasPriceTier::VeryLow)); - arr[DUP11 as usize] = Some(InstructionInfo::new("DUP11", 11, 12, GasPriceTier::VeryLow)); - arr[DUP12 as usize] = Some(InstructionInfo::new("DUP12", 12, 13, GasPriceTier::VeryLow)); - arr[DUP13 as usize] = Some(InstructionInfo::new("DUP13", 13, 14, GasPriceTier::VeryLow)); - arr[DUP14 as usize] = Some(InstructionInfo::new("DUP14", 14, 15, GasPriceTier::VeryLow)); - arr[DUP15 as usize] = Some(InstructionInfo::new("DUP15", 15, 16, GasPriceTier::VeryLow)); - arr[DUP16 as usize] = Some(InstructionInfo::new("DUP16", 16, 17, GasPriceTier::VeryLow)); - arr[SWAP1 as usize] = Some(InstructionInfo::new("SWAP1", 2, 2, GasPriceTier::VeryLow)); - arr[SWAP2 as usize] = Some(InstructionInfo::new("SWAP2", 3, 3, GasPriceTier::VeryLow)); - arr[SWAP3 as usize] = Some(InstructionInfo::new("SWAP3", 4, 4, GasPriceTier::VeryLow)); - arr[SWAP4 as usize] = Some(InstructionInfo::new("SWAP4", 5, 5, GasPriceTier::VeryLow)); - arr[SWAP5 as usize] = Some(InstructionInfo::new("SWAP5", 6, 6, GasPriceTier::VeryLow)); - arr[SWAP6 as usize] = Some(InstructionInfo::new("SWAP6", 7, 7, GasPriceTier::VeryLow)); - arr[SWAP7 as usize] = Some(InstructionInfo::new("SWAP7", 8, 8, GasPriceTier::VeryLow)); - arr[SWAP8 as usize] = Some(InstructionInfo::new("SWAP8", 9, 9, GasPriceTier::VeryLow)); - arr[SWAP9 as usize] = Some(InstructionInfo::new("SWAP9", 10, 10, GasPriceTier::VeryLow)); - arr[SWAP10 as usize] = Some(InstructionInfo::new("SWAP10", 11, 11, GasPriceTier::VeryLow)); - arr[SWAP11 as usize] = Some(InstructionInfo::new("SWAP11", 12, 12, GasPriceTier::VeryLow)); - arr[SWAP12 as usize] = Some(InstructionInfo::new("SWAP12", 13, 13, GasPriceTier::VeryLow)); - arr[SWAP13 as usize] = Some(InstructionInfo::new("SWAP13", 14, 14, GasPriceTier::VeryLow)); - arr[SWAP14 as usize] = Some(InstructionInfo::new("SWAP14", 15, 15, GasPriceTier::VeryLow)); - arr[SWAP15 as usize] = Some(InstructionInfo::new("SWAP15", 16, 16, GasPriceTier::VeryLow)); - arr[SWAP16 as usize] = Some(InstructionInfo::new("SWAP16", 17, 17, GasPriceTier::VeryLow)); - arr[LOG0 as usize] = Some(InstructionInfo::new("LOG0", 2, 0, GasPriceTier::Special)); - arr[LOG1 as usize] = Some(InstructionInfo::new("LOG1", 3, 0, GasPriceTier::Special)); - arr[LOG2 as usize] = Some(InstructionInfo::new("LOG2", 4, 0, GasPriceTier::Special)); - arr[LOG3 as usize] = Some(InstructionInfo::new("LOG3", 5, 0, GasPriceTier::Special)); - arr[LOG4 as usize] = Some(InstructionInfo::new("LOG4", 6, 0, GasPriceTier::Special)); - arr[CREATE as usize] = Some(InstructionInfo::new("CREATE", 3, 1, GasPriceTier::Special)); - arr[CALL as usize] = Some(InstructionInfo::new("CALL", 7, 1, GasPriceTier::Special)); - arr[CALLCODE as usize] = Some(InstructionInfo::new("CALLCODE", 7, 1, GasPriceTier::Special)); - arr[RETURN as usize] = Some(InstructionInfo::new("RETURN", 2, 0, GasPriceTier::Zero)); - arr[DELEGATECALL as usize] = Some(InstructionInfo::new("DELEGATECALL", 6, 1, GasPriceTier::Special)); - arr[STATICCALL as usize] = Some(InstructionInfo::new("STATICCALL", 6, 1, GasPriceTier::Special)); - arr[SUICIDE as usize] = Some(InstructionInfo::new("SUICIDE", 1, 0, GasPriceTier::Special)); - arr[CREATE2 as usize] = Some(InstructionInfo::new("CREATE2", 4, 1, GasPriceTier::Special)); - arr[REVERT as usize] = Some(InstructionInfo::new("REVERT", 2, 0, GasPriceTier::Zero)); - arr - }; + /// Static instruction table. + static ref INSTRUCTIONS: [Option; 0x100] = { + let mut arr = [None; 0x100]; + arr[STOP as usize] = Some(InstructionInfo::new("STOP", 0, 0, GasPriceTier::Zero)); + arr[ADD as usize] = Some(InstructionInfo::new("ADD", 2, 1, GasPriceTier::VeryLow)); + arr[SUB as usize] = Some(InstructionInfo::new("SUB", 2, 1, GasPriceTier::VeryLow)); + arr[MUL as usize] = Some(InstructionInfo::new("MUL", 2, 1, GasPriceTier::Low)); + arr[DIV as usize] = Some(InstructionInfo::new("DIV", 2, 1, GasPriceTier::Low)); + arr[SDIV as usize] = Some(InstructionInfo::new("SDIV", 2, 1, GasPriceTier::Low)); + arr[MOD as usize] = Some(InstructionInfo::new("MOD", 2, 1, GasPriceTier::Low)); + arr[SMOD as usize] = Some(InstructionInfo::new("SMOD", 2, 1, GasPriceTier::Low)); + arr[EXP as usize] = Some(InstructionInfo::new("EXP", 2, 1, GasPriceTier::Special)); + arr[NOT as usize] = Some(InstructionInfo::new("NOT", 1, 1, GasPriceTier::VeryLow)); + arr[LT as usize] = Some(InstructionInfo::new("LT", 2, 1, GasPriceTier::VeryLow)); + arr[GT as usize] = Some(InstructionInfo::new("GT", 2, 1, GasPriceTier::VeryLow)); + arr[SLT as usize] = Some(InstructionInfo::new("SLT", 2, 1, GasPriceTier::VeryLow)); + arr[SGT as usize] = Some(InstructionInfo::new("SGT", 2, 1, GasPriceTier::VeryLow)); + arr[EQ as usize] = Some(InstructionInfo::new("EQ", 2, 1, GasPriceTier::VeryLow)); + arr[ISZERO as usize] = Some(InstructionInfo::new("ISZERO", 1, 1, GasPriceTier::VeryLow)); + arr[AND as usize] = Some(InstructionInfo::new("AND", 2, 1, GasPriceTier::VeryLow)); + arr[OR as usize] = Some(InstructionInfo::new("OR", 2, 1, GasPriceTier::VeryLow)); + arr[XOR as usize] = Some(InstructionInfo::new("XOR", 2, 1, GasPriceTier::VeryLow)); + arr[BYTE as usize] = Some(InstructionInfo::new("BYTE", 2, 1, GasPriceTier::VeryLow)); + arr[SHL as usize] = Some(InstructionInfo::new("SHL", 2, 1, GasPriceTier::VeryLow)); + arr[SHR as usize] = Some(InstructionInfo::new("SHR", 2, 1, GasPriceTier::VeryLow)); + arr[SAR as usize] = Some(InstructionInfo::new("SAR", 2, 1, GasPriceTier::VeryLow)); + arr[ADDMOD as usize] = Some(InstructionInfo::new("ADDMOD", 3, 1, GasPriceTier::Mid)); + arr[MULMOD as usize] = Some(InstructionInfo::new("MULMOD", 3, 1, GasPriceTier::Mid)); + arr[SIGNEXTEND as usize] = Some(InstructionInfo::new("SIGNEXTEND", 2, 1, GasPriceTier::Low)); + arr[RETURNDATASIZE as usize] = Some(InstructionInfo::new("RETURNDATASIZE", 0, 1, GasPriceTier::Base)); + arr[RETURNDATACOPY as usize] = Some(InstructionInfo::new("RETURNDATACOPY", 3, 0, GasPriceTier::VeryLow)); + arr[SHA3 as usize] = Some(InstructionInfo::new("SHA3", 2, 1, GasPriceTier::Special)); + arr[ADDRESS as usize] = Some(InstructionInfo::new("ADDRESS", 0, 1, GasPriceTier::Base)); + arr[BALANCE as usize] = Some(InstructionInfo::new("BALANCE", 1, 1, GasPriceTier::Special)); + arr[ORIGIN as usize] = Some(InstructionInfo::new("ORIGIN", 0, 1, GasPriceTier::Base)); + arr[CALLER as usize] = Some(InstructionInfo::new("CALLER", 0, 1, GasPriceTier::Base)); + arr[CALLVALUE as usize] = Some(InstructionInfo::new("CALLVALUE", 0, 1, GasPriceTier::Base)); + arr[CALLDATALOAD as usize] = Some(InstructionInfo::new("CALLDATALOAD", 1, 1, GasPriceTier::VeryLow)); + arr[CALLDATASIZE as usize] = Some(InstructionInfo::new("CALLDATASIZE", 0, 1, GasPriceTier::Base)); + arr[CALLDATACOPY as usize] = Some(InstructionInfo::new("CALLDATACOPY", 3, 0, GasPriceTier::VeryLow)); + arr[EXTCODEHASH as usize] = Some(InstructionInfo::new("EXTCODEHASH", 1, 1, GasPriceTier::Special)); + arr[CODESIZE as usize] = Some(InstructionInfo::new("CODESIZE", 0, 1, GasPriceTier::Base)); + arr[CODECOPY as usize] = Some(InstructionInfo::new("CODECOPY", 3, 0, GasPriceTier::VeryLow)); + arr[GASPRICE as usize] = Some(InstructionInfo::new("GASPRICE", 0, 1, GasPriceTier::Base)); + arr[EXTCODESIZE as usize] = Some(InstructionInfo::new("EXTCODESIZE", 1, 1, GasPriceTier::Special)); + arr[EXTCODECOPY as usize] = Some(InstructionInfo::new("EXTCODECOPY", 4, 0, GasPriceTier::Special)); + arr[BLOCKHASH as usize] = Some(InstructionInfo::new("BLOCKHASH", 1, 1, GasPriceTier::Ext)); + arr[COINBASE as usize] = Some(InstructionInfo::new("COINBASE", 0, 1, GasPriceTier::Base)); + arr[TIMESTAMP as usize] = Some(InstructionInfo::new("TIMESTAMP", 0, 1, GasPriceTier::Base)); + arr[NUMBER as usize] = Some(InstructionInfo::new("NUMBER", 0, 1, GasPriceTier::Base)); + arr[DIFFICULTY as usize] = Some(InstructionInfo::new("DIFFICULTY", 0, 1, GasPriceTier::Base)); + arr[GASLIMIT as usize] = Some(InstructionInfo::new("GASLIMIT", 0, 1, GasPriceTier::Base)); + arr[CHAINID as usize] = Some(InstructionInfo::new("CHAINID", 0, 1, GasPriceTier::Base)); + arr[SELFBALANCE as usize] = Some(InstructionInfo::new("SELFBALANCE", 0, 1, GasPriceTier::Low)); + arr[POP as usize] = Some(InstructionInfo::new("POP", 1, 0, GasPriceTier::Base)); + arr[MLOAD as usize] = Some(InstructionInfo::new("MLOAD", 1, 1, GasPriceTier::VeryLow)); + arr[MSTORE as usize] = Some(InstructionInfo::new("MSTORE", 2, 0, GasPriceTier::VeryLow)); + arr[MSTORE8 as usize] = Some(InstructionInfo::new("MSTORE8", 2, 0, GasPriceTier::VeryLow)); + arr[SLOAD as usize] = Some(InstructionInfo::new("SLOAD", 1, 1, GasPriceTier::Special)); + arr[SSTORE as usize] = Some(InstructionInfo::new("SSTORE", 2, 0, GasPriceTier::Special)); + arr[JUMP as usize] = Some(InstructionInfo::new("JUMP", 1, 0, GasPriceTier::Mid)); + arr[JUMPI as usize] = Some(InstructionInfo::new("JUMPI", 2, 0, GasPriceTier::High)); + arr[PC as usize] = Some(InstructionInfo::new("PC", 0, 1, GasPriceTier::Base)); + arr[MSIZE as usize] = Some(InstructionInfo::new("MSIZE", 0, 1, GasPriceTier::Base)); + arr[GAS as usize] = Some(InstructionInfo::new("GAS", 0, 1, GasPriceTier::Base)); + arr[JUMPDEST as usize] = Some(InstructionInfo::new("JUMPDEST", 0, 0, GasPriceTier::Special)); + arr[PUSH1 as usize] = Some(InstructionInfo::new("PUSH1", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH2 as usize] = Some(InstructionInfo::new("PUSH2", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH3 as usize] = Some(InstructionInfo::new("PUSH3", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH4 as usize] = Some(InstructionInfo::new("PUSH4", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH5 as usize] = Some(InstructionInfo::new("PUSH5", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH6 as usize] = Some(InstructionInfo::new("PUSH6", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH7 as usize] = Some(InstructionInfo::new("PUSH7", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH8 as usize] = Some(InstructionInfo::new("PUSH8", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH9 as usize] = Some(InstructionInfo::new("PUSH9", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH10 as usize] = Some(InstructionInfo::new("PUSH10", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH11 as usize] = Some(InstructionInfo::new("PUSH11", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH12 as usize] = Some(InstructionInfo::new("PUSH12", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH13 as usize] = Some(InstructionInfo::new("PUSH13", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH14 as usize] = Some(InstructionInfo::new("PUSH14", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH15 as usize] = Some(InstructionInfo::new("PUSH15", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH16 as usize] = Some(InstructionInfo::new("PUSH16", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH17 as usize] = Some(InstructionInfo::new("PUSH17", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH18 as usize] = Some(InstructionInfo::new("PUSH18", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH19 as usize] = Some(InstructionInfo::new("PUSH19", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH20 as usize] = Some(InstructionInfo::new("PUSH20", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH21 as usize] = Some(InstructionInfo::new("PUSH21", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH22 as usize] = Some(InstructionInfo::new("PUSH22", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH23 as usize] = Some(InstructionInfo::new("PUSH23", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH24 as usize] = Some(InstructionInfo::new("PUSH24", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH25 as usize] = Some(InstructionInfo::new("PUSH25", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH26 as usize] = Some(InstructionInfo::new("PUSH26", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH27 as usize] = Some(InstructionInfo::new("PUSH27", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH28 as usize] = Some(InstructionInfo::new("PUSH28", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH29 as usize] = Some(InstructionInfo::new("PUSH29", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH30 as usize] = Some(InstructionInfo::new("PUSH30", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH31 as usize] = Some(InstructionInfo::new("PUSH31", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH32 as usize] = Some(InstructionInfo::new("PUSH32", 0, 1, GasPriceTier::VeryLow)); + arr[DUP1 as usize] = Some(InstructionInfo::new("DUP1", 1, 2, GasPriceTier::VeryLow)); + arr[DUP2 as usize] = Some(InstructionInfo::new("DUP2", 2, 3, GasPriceTier::VeryLow)); + arr[DUP3 as usize] = Some(InstructionInfo::new("DUP3", 3, 4, GasPriceTier::VeryLow)); + arr[DUP4 as usize] = Some(InstructionInfo::new("DUP4", 4, 5, GasPriceTier::VeryLow)); + arr[DUP5 as usize] = Some(InstructionInfo::new("DUP5", 5, 6, GasPriceTier::VeryLow)); + arr[DUP6 as usize] = Some(InstructionInfo::new("DUP6", 6, 7, GasPriceTier::VeryLow)); + arr[DUP7 as usize] = Some(InstructionInfo::new("DUP7", 7, 8, GasPriceTier::VeryLow)); + arr[DUP8 as usize] = Some(InstructionInfo::new("DUP8", 8, 9, GasPriceTier::VeryLow)); + arr[DUP9 as usize] = Some(InstructionInfo::new("DUP9", 9, 10, GasPriceTier::VeryLow)); + arr[DUP10 as usize] = Some(InstructionInfo::new("DUP10", 10, 11, GasPriceTier::VeryLow)); + arr[DUP11 as usize] = Some(InstructionInfo::new("DUP11", 11, 12, GasPriceTier::VeryLow)); + arr[DUP12 as usize] = Some(InstructionInfo::new("DUP12", 12, 13, GasPriceTier::VeryLow)); + arr[DUP13 as usize] = Some(InstructionInfo::new("DUP13", 13, 14, GasPriceTier::VeryLow)); + arr[DUP14 as usize] = Some(InstructionInfo::new("DUP14", 14, 15, GasPriceTier::VeryLow)); + arr[DUP15 as usize] = Some(InstructionInfo::new("DUP15", 15, 16, GasPriceTier::VeryLow)); + arr[DUP16 as usize] = Some(InstructionInfo::new("DUP16", 16, 17, GasPriceTier::VeryLow)); + arr[SWAP1 as usize] = Some(InstructionInfo::new("SWAP1", 2, 2, GasPriceTier::VeryLow)); + arr[SWAP2 as usize] = Some(InstructionInfo::new("SWAP2", 3, 3, GasPriceTier::VeryLow)); + arr[SWAP3 as usize] = Some(InstructionInfo::new("SWAP3", 4, 4, GasPriceTier::VeryLow)); + arr[SWAP4 as usize] = Some(InstructionInfo::new("SWAP4", 5, 5, GasPriceTier::VeryLow)); + arr[SWAP5 as usize] = Some(InstructionInfo::new("SWAP5", 6, 6, GasPriceTier::VeryLow)); + arr[SWAP6 as usize] = Some(InstructionInfo::new("SWAP6", 7, 7, GasPriceTier::VeryLow)); + arr[SWAP7 as usize] = Some(InstructionInfo::new("SWAP7", 8, 8, GasPriceTier::VeryLow)); + arr[SWAP8 as usize] = Some(InstructionInfo::new("SWAP8", 9, 9, GasPriceTier::VeryLow)); + arr[SWAP9 as usize] = Some(InstructionInfo::new("SWAP9", 10, 10, GasPriceTier::VeryLow)); + arr[SWAP10 as usize] = Some(InstructionInfo::new("SWAP10", 11, 11, GasPriceTier::VeryLow)); + arr[SWAP11 as usize] = Some(InstructionInfo::new("SWAP11", 12, 12, GasPriceTier::VeryLow)); + arr[SWAP12 as usize] = Some(InstructionInfo::new("SWAP12", 13, 13, GasPriceTier::VeryLow)); + arr[SWAP13 as usize] = Some(InstructionInfo::new("SWAP13", 14, 14, GasPriceTier::VeryLow)); + arr[SWAP14 as usize] = Some(InstructionInfo::new("SWAP14", 15, 15, GasPriceTier::VeryLow)); + arr[SWAP15 as usize] = Some(InstructionInfo::new("SWAP15", 16, 16, GasPriceTier::VeryLow)); + arr[SWAP16 as usize] = Some(InstructionInfo::new("SWAP16", 17, 17, GasPriceTier::VeryLow)); + arr[LOG0 as usize] = Some(InstructionInfo::new("LOG0", 2, 0, GasPriceTier::Special)); + arr[LOG1 as usize] = Some(InstructionInfo::new("LOG1", 3, 0, GasPriceTier::Special)); + arr[LOG2 as usize] = Some(InstructionInfo::new("LOG2", 4, 0, GasPriceTier::Special)); + arr[LOG3 as usize] = Some(InstructionInfo::new("LOG3", 5, 0, GasPriceTier::Special)); + arr[LOG4 as usize] = Some(InstructionInfo::new("LOG4", 6, 0, GasPriceTier::Special)); + arr[BEGINSUB as usize] = Some(InstructionInfo::new("BEGINSUB", 0, 0, GasPriceTier::Base)); + arr[JUMPSUB as usize] = Some(InstructionInfo::new("JUMPSUB", 1, 0, GasPriceTier::High)); + arr[RETURNSUB as usize] = Some(InstructionInfo::new("RETURNSUB", 0, 0, GasPriceTier::Low)); + arr[CREATE as usize] = Some(InstructionInfo::new("CREATE", 3, 1, GasPriceTier::Special)); + arr[CALL as usize] = Some(InstructionInfo::new("CALL", 7, 1, GasPriceTier::Special)); + arr[CALLCODE as usize] = Some(InstructionInfo::new("CALLCODE", 7, 1, GasPriceTier::Special)); + arr[RETURN as usize] = Some(InstructionInfo::new("RETURN", 2, 0, GasPriceTier::Zero)); + arr[DELEGATECALL as usize] = Some(InstructionInfo::new("DELEGATECALL", 6, 1, GasPriceTier::Special)); + arr[STATICCALL as usize] = Some(InstructionInfo::new("STATICCALL", 6, 1, GasPriceTier::Special)); + arr[SUICIDE as usize] = Some(InstructionInfo::new("SUICIDE", 1, 0, GasPriceTier::Special)); + arr[CREATE2 as usize] = Some(InstructionInfo::new("CREATE2", 4, 1, GasPriceTier::Special)); + arr[REVERT as usize] = Some(InstructionInfo::new("REVERT", 2, 0, GasPriceTier::Zero)); + arr + }; } /// Maximal number of topics for log instructions @@ -603,40 +619,40 @@ pub const MAX_NO_OF_TOPICS: usize = 4; #[cfg(test)] mod tests { - use super::*; - - #[test] - fn test_is_push() { - assert!(PUSH1.is_push()); - assert!(PUSH32.is_push()); - assert!(!DUP1.is_push()); - } - - #[test] - fn test_get_push_bytes() { - assert_eq!(PUSH1.push_bytes(), Some(1)); - assert_eq!(PUSH3.push_bytes(), Some(3)); - assert_eq!(PUSH32.push_bytes(), Some(32)); - } - - #[test] - fn test_get_dup_position() { - assert_eq!(DUP1.dup_position(), Some(0)); - assert_eq!(DUP5.dup_position(), Some(4)); - assert_eq!(DUP10.dup_position(), Some(9)); - } - - #[test] - fn test_get_swap_position() { - assert_eq!(SWAP1.swap_position(), Some(1)); - assert_eq!(SWAP5.swap_position(), Some(5)); - assert_eq!(SWAP10.swap_position(), Some(10)); - } - - #[test] - fn test_get_log_topics() { - assert_eq!(LOG0.log_topics(), Some(0)); - assert_eq!(LOG2.log_topics(), Some(2)); - assert_eq!(LOG4.log_topics(), Some(4)); - } + use super::*; + + #[test] + fn test_is_push() { + assert!(PUSH1.is_push()); + assert!(PUSH32.is_push()); + assert!(!DUP1.is_push()); + } + + #[test] + fn test_get_push_bytes() { + assert_eq!(PUSH1.push_bytes(), Some(1)); + assert_eq!(PUSH3.push_bytes(), Some(3)); + assert_eq!(PUSH32.push_bytes(), Some(32)); + } + + #[test] + fn test_get_dup_position() { + assert_eq!(DUP1.dup_position(), Some(0)); + assert_eq!(DUP5.dup_position(), Some(4)); + assert_eq!(DUP10.dup_position(), Some(9)); + } + + #[test] + fn test_get_swap_position() { + assert_eq!(SWAP1.swap_position(), Some(1)); + assert_eq!(SWAP5.swap_position(), Some(5)); + assert_eq!(SWAP10.swap_position(), Some(10)); + } + + #[test] + fn test_get_log_topics() { + assert_eq!(LOG0.log_topics(), Some(0)); + assert_eq!(LOG2.log_topics(), Some(2)); + assert_eq!(LOG4.log_topics(), Some(4)); + } } diff --git a/ethcore/evm/src/interpreter/gasometer.rs b/ethcore/evm/src/interpreter/gasometer.rs index 26fec2d9376..8bb514aac55 100644 --- a/ethcore/evm/src/interpreter/gasometer.rs +++ b/ethcore/evm/src/interpreter/gasometer.rs @@ -1,485 +1,516 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::cmp; -use ethereum_types::{U256, H256}; use super::u256_to_address; +use ethereum_types::{H256, U256}; +use std::cmp; -use {evm, vm}; +use evm; use instructions::{self, Instruction, InstructionInfo}; use interpreter::stack::Stack; -use vm::Schedule; +use vm::{self, Schedule}; macro_rules! overflowing { - ($x: expr) => {{ - let (v, overflow) = $x; - if overflow { return Err(vm::Error::OutOfGas); } - v - }} + ($x: expr) => {{ + let (v, overflow) = $x; + if overflow { + return Err(vm::Error::OutOfGas); + } + v + }}; } enum Request { - Gas(Cost), - GasMem(Cost, Cost), - GasMemProvide(Cost, Cost, Option), - GasMemCopy(Cost, Cost, Cost) + Gas(Cost), + GasMem(Cost, Cost), + GasMemProvide(Cost, Cost, Option), + GasMemCopy(Cost, Cost, Cost), } pub struct InstructionRequirements { - pub gas_cost: Cost, - pub provide_gas: Option, - pub memory_total_gas: Cost, - pub memory_required_size: usize, + pub gas_cost: Cost, + pub provide_gas: Option, + pub memory_total_gas: Cost, + pub memory_required_size: usize, } pub struct Gasometer { - pub current_gas: Gas, - pub current_mem_gas: Gas, + pub current_gas: Gas, + pub current_mem_gas: Gas, } impl Gasometer { - - pub fn new(current_gas: Gas) -> Self { - Gasometer { - current_gas: current_gas, - current_mem_gas: Gas::from(0), - } - } - - pub fn verify_gas(&self, gas_cost: &Gas) -> vm::Result<()> { - match &self.current_gas < gas_cost { - true => Err(vm::Error::OutOfGas), - false => Ok(()) - } - } - - /// How much gas is provided to a CALL/CREATE, given that we need to deduct `needed` for this operation - /// and that we `requested` some. - pub fn gas_provided(&self, schedule: &Schedule, needed: Gas, requested: Option) -> vm::Result { - // Try converting requested gas to `Gas` (`U256/u64`) - // but in EIP150 even if we request more we should never fail from OOG - let requested = requested.map(Gas::from_u256); - - match schedule.sub_gas_cap_divisor { - Some(cap_divisor) if self.current_gas >= needed => { - let gas_remaining = self.current_gas - needed; - let max_gas_provided = match cap_divisor { - 64 => gas_remaining - (gas_remaining >> 6), - cap_divisor => gas_remaining - gas_remaining / Gas::from(cap_divisor), - }; - - if let Some(Ok(r)) = requested { - Ok(cmp::min(r, max_gas_provided)) - } else { - Ok(max_gas_provided) - } - }, - _ => { - if let Some(r) = requested { - r - } else if self.current_gas >= needed { - Ok(self.current_gas - needed) - } else { - Ok(0.into()) - } - }, - } - } - - /// Determine how much gas is used by the given instruction, given the machine's state. - /// - /// We guarantee that the final element of the returned tuple (`provided`) will be `Some` - /// iff the `instruction` is one of `CREATE`, or any of the `CALL` variants. In this case, - /// it will be the amount of gas that the current context provides to the child context. - pub fn requirements( - &mut self, - ext: &vm::Ext, - instruction: Instruction, - info: &InstructionInfo, - stack: &Stack, - current_mem_size: usize, - ) -> vm::Result> { - let schedule = ext.schedule(); - let tier = info.tier.idx(); - let default_gas = Gas::from(schedule.tier_step_gas[tier]); - - let cost = match instruction { - instructions::JUMPDEST => { - Request::Gas(Gas::from(1)) - }, - instructions::SSTORE => { - let address = H256::from(stack.peek(0)); - let newval = stack.peek(1); - let val = U256::from(&*ext.storage_at(&address)?); - - let gas = if schedule.eip1283 { - let orig = U256::from(&*ext.initial_storage_at(&address)?); - calculate_eip1283_sstore_gas(schedule, &orig, &val, &newval) - } else { - if val.is_zero() && !newval.is_zero() { - schedule.sstore_set_gas - } else { - // Refund for below case is added when actually executing sstore - // !is_zero(&val) && is_zero(newval) - schedule.sstore_reset_gas - } - }; - Request::Gas(Gas::from(gas)) - }, - instructions::SLOAD => { - Request::Gas(Gas::from(schedule.sload_gas)) - }, - instructions::BALANCE => { - Request::Gas(Gas::from(schedule.balance_gas)) - }, - instructions::EXTCODESIZE => { - Request::Gas(Gas::from(schedule.extcodesize_gas)) - }, - instructions::EXTCODEHASH => { - Request::Gas(Gas::from(schedule.extcodehash_gas)) - }, - instructions::SUICIDE => { - let mut gas = Gas::from(schedule.suicide_gas); - - let is_value_transfer = !ext.origin_balance()?.is_zero(); - let address = u256_to_address(stack.peek(0)); - if ( - !schedule.no_empty && !ext.exists(&address)? - ) || ( - schedule.no_empty && is_value_transfer && !ext.exists_and_not_null(&address)? - ) { - gas = overflowing!(gas.overflow_add(schedule.suicide_to_new_account_cost.into())); - } - - Request::Gas(gas) - }, - instructions::MSTORE | instructions::MLOAD => { - Request::GasMem(default_gas, mem_needed_const(stack.peek(0), 32)?) - }, - instructions::MSTORE8 => { - Request::GasMem(default_gas, mem_needed_const(stack.peek(0), 1)?) - }, - instructions::RETURN | instructions::REVERT => { - Request::GasMem(default_gas, mem_needed(stack.peek(0), stack.peek(1))?) - }, - instructions::SHA3 => { - let words = overflowing!(to_word_size(Gas::from_u256(*stack.peek(1))?)); - let gas = overflowing!(Gas::from(schedule.sha3_gas).overflow_add(overflowing!(Gas::from(schedule.sha3_word_gas).overflow_mul(words)))); - Request::GasMem(gas, mem_needed(stack.peek(0), stack.peek(1))?) - }, - instructions::CALLDATACOPY | instructions::CODECOPY | instructions::RETURNDATACOPY => { - Request::GasMemCopy(default_gas, mem_needed(stack.peek(0), stack.peek(2))?, Gas::from_u256(*stack.peek(2))?) - }, - instructions::EXTCODECOPY => { - Request::GasMemCopy(schedule.extcodecopy_base_gas.into(), mem_needed(stack.peek(1), stack.peek(3))?, Gas::from_u256(*stack.peek(3))?) - }, - instructions::LOG0 | instructions::LOG1 | instructions::LOG2 | instructions::LOG3 | instructions::LOG4 => { - let no_of_topics = instruction.log_topics().expect("log_topics always return some for LOG* instructions; qed"); - let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics; - - let data_gas = overflowing!(Gas::from_u256(*stack.peek(1))?.overflow_mul(Gas::from(schedule.log_data_gas))); - let gas = overflowing!(data_gas.overflow_add(Gas::from(log_gas))); - Request::GasMem(gas, mem_needed(stack.peek(0), stack.peek(1))?) - }, - instructions::CALL | instructions::CALLCODE => { - let mut gas = Gas::from(schedule.call_gas); - let mem = cmp::max( - mem_needed(stack.peek(5), stack.peek(6))?, - mem_needed(stack.peek(3), stack.peek(4))? - ); - - let address = u256_to_address(stack.peek(1)); - let is_value_transfer = !stack.peek(2).is_zero(); - - if instruction == instructions::CALL && ( - (!schedule.no_empty && !ext.exists(&address)?) - || - (schedule.no_empty && is_value_transfer && !ext.exists_and_not_null(&address)?) - ) { - gas = overflowing!(gas.overflow_add(schedule.call_new_account_gas.into())); - } - - if is_value_transfer { - gas = overflowing!(gas.overflow_add(schedule.call_value_transfer_gas.into())); - } - - let requested = *stack.peek(0); - - Request::GasMemProvide(gas, mem, Some(requested)) - }, - instructions::DELEGATECALL | instructions::STATICCALL => { - let gas = Gas::from(schedule.call_gas); - let mem = cmp::max( - mem_needed(stack.peek(4), stack.peek(5))?, - mem_needed(stack.peek(2), stack.peek(3))? - ); - let requested = *stack.peek(0); - - Request::GasMemProvide(gas, mem, Some(requested)) - }, - instructions::CREATE => { - let start = stack.peek(1); - let len = stack.peek(2); - - let gas = Gas::from(schedule.create_gas); - let mem = mem_needed(start, len)?; - - Request::GasMemProvide(gas, mem, None) - }, - instructions::CREATE2 => { - let start = stack.peek(1); - let len = stack.peek(2); - - let base = Gas::from(schedule.create_gas); - let word = overflowing!(to_word_size(Gas::from_u256(*len)?)); - let word_gas = overflowing!(Gas::from(schedule.sha3_word_gas).overflow_mul(word)); - let gas = overflowing!(base.overflow_add(word_gas)); - let mem = mem_needed(start, len)?; - - Request::GasMemProvide(gas, mem, None) - }, - instructions::EXP => { - let expon = stack.peek(1); - let bytes = ((expon.bits() + 7) / 8) as usize; - let gas = Gas::from(schedule.exp_gas + schedule.exp_byte_gas * bytes); - Request::Gas(gas) - }, - instructions::BLOCKHASH => { - Request::Gas(Gas::from(schedule.blockhash_gas)) - }, - _ => Request::Gas(default_gas), - }; - - Ok(match cost { - Request::Gas(gas) => { - InstructionRequirements { - gas_cost: gas, - provide_gas: None, - memory_required_size: 0, - memory_total_gas: self.current_mem_gas, - } - }, - Request::GasMem(gas, mem_size) => { - let (mem_gas_cost, new_mem_gas, new_mem_size) = self.mem_gas_cost(schedule, current_mem_size, &mem_size)?; - let gas = overflowing!(gas.overflow_add(mem_gas_cost)); - InstructionRequirements { - gas_cost: gas, - provide_gas: None, - memory_required_size: new_mem_size, - memory_total_gas: new_mem_gas, - } - }, - Request::GasMemProvide(gas, mem_size, requested) => { - let (mem_gas_cost, new_mem_gas, new_mem_size) = self.mem_gas_cost(schedule, current_mem_size, &mem_size)?; - let gas = overflowing!(gas.overflow_add(mem_gas_cost)); - let provided = self.gas_provided(schedule, gas, requested)?; - let total_gas = overflowing!(gas.overflow_add(provided)); - - InstructionRequirements { - gas_cost: total_gas, - provide_gas: Some(provided), - memory_required_size: new_mem_size, - memory_total_gas: new_mem_gas, - } - }, - Request::GasMemCopy(gas, mem_size, copy) => { - let (mem_gas_cost, new_mem_gas, new_mem_size) = self.mem_gas_cost(schedule, current_mem_size, &mem_size)?; - let copy = overflowing!(to_word_size(copy)); - let copy_gas = overflowing!(Gas::from(schedule.copy_gas).overflow_mul(copy)); - let gas = overflowing!(gas.overflow_add(copy_gas)); - let gas = overflowing!(gas.overflow_add(mem_gas_cost)); - - InstructionRequirements { - gas_cost: gas, - provide_gas: None, - memory_required_size: new_mem_size, - memory_total_gas: new_mem_gas, - } - }, - }) - } - - fn mem_gas_cost(&self, schedule: &Schedule, current_mem_size: usize, mem_size: &Gas) -> vm::Result<(Gas, Gas, usize)> { - let gas_for_mem = |mem_size: Gas| { - let s = mem_size >> 5; - // s * memory_gas + s * s / quad_coeff_div - let a = overflowing!(s.overflow_mul(Gas::from(schedule.memory_gas))); - - // Calculate s*s/quad_coeff_div - assert_eq!(schedule.quad_coeff_div, 512); - let b = overflowing!(s.overflow_mul_shr(s, 9)); - Ok(overflowing!(a.overflow_add(b))) - }; - - let current_mem_size = Gas::from(current_mem_size); - let req_mem_size_rounded = overflowing!(to_word_size(*mem_size)) << 5; - - let (mem_gas_cost, new_mem_gas) = if req_mem_size_rounded > current_mem_size { - let new_mem_gas = gas_for_mem(req_mem_size_rounded)?; - (new_mem_gas - self.current_mem_gas, new_mem_gas) - } else { - (Gas::from(0), self.current_mem_gas) - }; - - Ok((mem_gas_cost, new_mem_gas, req_mem_size_rounded.as_usize())) - } + pub fn new(current_gas: Gas) -> Self { + Gasometer { + current_gas: current_gas, + current_mem_gas: Gas::from(0), + } + } + + pub fn verify_gas(&self, gas_cost: &Gas) -> vm::Result<()> { + match &self.current_gas < gas_cost { + true => Err(vm::Error::OutOfGas), + false => Ok(()), + } + } + + /// How much gas is provided to a CALL/CREATE, given that we need to deduct `needed` for this operation + /// and that we `requested` some. + pub fn gas_provided( + &self, + schedule: &Schedule, + needed: Gas, + requested: Option, + ) -> vm::Result { + // Try converting requested gas to `Gas` (`U256/u64`) + // but in EIP150 even if we request more we should never fail from OOG + let requested = requested.map(Gas::from_u256); + + match schedule.sub_gas_cap_divisor { + Some(cap_divisor) if self.current_gas >= needed => { + let gas_remaining = self.current_gas - needed; + let max_gas_provided = match cap_divisor { + 64 => gas_remaining - (gas_remaining >> 6), + cap_divisor => gas_remaining - gas_remaining / Gas::from(cap_divisor), + }; + + if let Some(Ok(r)) = requested { + Ok(cmp::min(r, max_gas_provided)) + } else { + Ok(max_gas_provided) + } + } + _ => { + if let Some(r) = requested { + r + } else if self.current_gas >= needed { + Ok(self.current_gas - needed) + } else { + Ok(0.into()) + } + } + } + } + + /// Determine how much gas is used by the given instruction, given the machine's state. + /// + /// We guarantee that the final element of the returned tuple (`provided`) will be `Some` + /// iff the `instruction` is one of `CREATE`, or any of the `CALL` variants. In this case, + /// it will be the amount of gas that the current context provides to the child context. + pub fn requirements( + &mut self, + ext: &dyn vm::Ext, + instruction: Instruction, + info: &InstructionInfo, + stack: &dyn Stack, + current_mem_size: usize, + ) -> vm::Result> { + let schedule = ext.schedule(); + let tier = info.tier.idx(); + let default_gas = Gas::from(schedule.tier_step_gas[tier]); + + let cost = match instruction { + instructions::JUMPDEST => Request::Gas(Gas::from(1)), + instructions::SSTORE => { + if schedule.eip1706 && self.current_gas <= Gas::from(schedule.call_stipend) { + return Err(vm::Error::OutOfGas); + } + let address = H256::from(stack.peek(0)); + let newval = stack.peek(1); + let val = U256::from(&*ext.storage_at(&address)?); + + let gas = if schedule.eip1283 { + let orig = U256::from(&*ext.initial_storage_at(&address)?); + calculate_eip1283_sstore_gas(schedule, &orig, &val, &newval) + } else { + if val.is_zero() && !newval.is_zero() { + schedule.sstore_set_gas + } else { + // Refund for below case is added when actually executing sstore + // !is_zero(&val) && is_zero(newval) + schedule.sstore_reset_gas + } + }; + Request::Gas(Gas::from(gas)) + } + instructions::SLOAD => Request::Gas(Gas::from(schedule.sload_gas)), + instructions::BALANCE => Request::Gas(Gas::from(schedule.balance_gas)), + instructions::EXTCODESIZE => Request::Gas(Gas::from(schedule.extcodesize_gas)), + instructions::EXTCODEHASH => Request::Gas(Gas::from(schedule.extcodehash_gas)), + instructions::SUICIDE => { + let mut gas = Gas::from(schedule.suicide_gas); + + let is_value_transfer = !ext.origin_balance()?.is_zero(); + let address = u256_to_address(stack.peek(0)); + if (!schedule.no_empty && !ext.exists(&address)?) + || (schedule.no_empty + && is_value_transfer + && !ext.exists_and_not_null(&address)?) + { + gas = + overflowing!(gas.overflow_add(schedule.suicide_to_new_account_cost.into())); + } + + Request::Gas(gas) + } + instructions::MSTORE | instructions::MLOAD => { + Request::GasMem(default_gas, mem_needed_const(stack.peek(0), 32)?) + } + instructions::MSTORE8 => { + Request::GasMem(default_gas, mem_needed_const(stack.peek(0), 1)?) + } + instructions::RETURN | instructions::REVERT => { + Request::GasMem(default_gas, mem_needed(stack.peek(0), stack.peek(1))?) + } + instructions::SHA3 => { + let words = overflowing!(to_word_size(Gas::from_u256(*stack.peek(1))?)); + let gas = overflowing!(Gas::from(schedule.sha3_gas).overflow_add(overflowing!( + Gas::from(schedule.sha3_word_gas).overflow_mul(words) + ))); + Request::GasMem(gas, mem_needed(stack.peek(0), stack.peek(1))?) + } + instructions::CALLDATACOPY | instructions::CODECOPY | instructions::RETURNDATACOPY => { + Request::GasMemCopy( + default_gas, + mem_needed(stack.peek(0), stack.peek(2))?, + Gas::from_u256(*stack.peek(2))?, + ) + } + instructions::EXTCODECOPY => Request::GasMemCopy( + schedule.extcodecopy_base_gas.into(), + mem_needed(stack.peek(1), stack.peek(3))?, + Gas::from_u256(*stack.peek(3))?, + ), + instructions::LOG0 + | instructions::LOG1 + | instructions::LOG2 + | instructions::LOG3 + | instructions::LOG4 => { + let no_of_topics = instruction + .log_topics() + .expect("log_topics always return some for LOG* instructions; qed"); + let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics; + + let data_gas = + overflowing!(Gas::from_u256(*stack.peek(1))? + .overflow_mul(Gas::from(schedule.log_data_gas))); + let gas = overflowing!(data_gas.overflow_add(Gas::from(log_gas))); + Request::GasMem(gas, mem_needed(stack.peek(0), stack.peek(1))?) + } + instructions::CALL | instructions::CALLCODE => { + let mut gas = Gas::from(schedule.call_gas); + let mem = cmp::max( + mem_needed(stack.peek(5), stack.peek(6))?, + mem_needed(stack.peek(3), stack.peek(4))?, + ); + + let address = u256_to_address(stack.peek(1)); + let is_value_transfer = !stack.peek(2).is_zero(); + + if instruction == instructions::CALL + && ((!schedule.no_empty && !ext.exists(&address)?) + || (schedule.no_empty + && is_value_transfer + && !ext.exists_and_not_null(&address)?)) + { + gas = overflowing!(gas.overflow_add(schedule.call_new_account_gas.into())); + } + + if is_value_transfer { + gas = overflowing!(gas.overflow_add(schedule.call_value_transfer_gas.into())); + } + + let requested = *stack.peek(0); + + Request::GasMemProvide(gas, mem, Some(requested)) + } + instructions::DELEGATECALL | instructions::STATICCALL => { + let gas = Gas::from(schedule.call_gas); + let mem = cmp::max( + mem_needed(stack.peek(4), stack.peek(5))?, + mem_needed(stack.peek(2), stack.peek(3))?, + ); + let requested = *stack.peek(0); + + Request::GasMemProvide(gas, mem, Some(requested)) + } + instructions::CREATE => { + let start = stack.peek(1); + let len = stack.peek(2); + + let gas = Gas::from(schedule.create_gas); + let mem = mem_needed(start, len)?; + + Request::GasMemProvide(gas, mem, None) + } + instructions::CREATE2 => { + let start = stack.peek(1); + let len = stack.peek(2); + + let base = Gas::from(schedule.create_gas); + let word = overflowing!(to_word_size(Gas::from_u256(*len)?)); + let word_gas = overflowing!(Gas::from(schedule.sha3_word_gas).overflow_mul(word)); + let gas = overflowing!(base.overflow_add(word_gas)); + let mem = mem_needed(start, len)?; + + Request::GasMemProvide(gas, mem, None) + } + instructions::EXP => { + let expon = stack.peek(1); + let bytes = ((expon.bits() + 7) / 8) as usize; + let gas = Gas::from(schedule.exp_gas + schedule.exp_byte_gas * bytes); + Request::Gas(gas) + } + instructions::BLOCKHASH => Request::Gas(Gas::from(schedule.blockhash_gas)), + _ => Request::Gas(default_gas), + }; + + Ok(match cost { + Request::Gas(gas) => InstructionRequirements { + gas_cost: gas, + provide_gas: None, + memory_required_size: 0, + memory_total_gas: self.current_mem_gas, + }, + Request::GasMem(gas, mem_size) => { + let (mem_gas_cost, new_mem_gas, new_mem_size) = + self.mem_gas_cost(schedule, current_mem_size, &mem_size)?; + let gas = overflowing!(gas.overflow_add(mem_gas_cost)); + InstructionRequirements { + gas_cost: gas, + provide_gas: None, + memory_required_size: new_mem_size, + memory_total_gas: new_mem_gas, + } + } + Request::GasMemProvide(gas, mem_size, requested) => { + let (mem_gas_cost, new_mem_gas, new_mem_size) = + self.mem_gas_cost(schedule, current_mem_size, &mem_size)?; + let gas = overflowing!(gas.overflow_add(mem_gas_cost)); + let provided = self.gas_provided(schedule, gas, requested)?; + let total_gas = overflowing!(gas.overflow_add(provided)); + + InstructionRequirements { + gas_cost: total_gas, + provide_gas: Some(provided), + memory_required_size: new_mem_size, + memory_total_gas: new_mem_gas, + } + } + Request::GasMemCopy(gas, mem_size, copy) => { + let (mem_gas_cost, new_mem_gas, new_mem_size) = + self.mem_gas_cost(schedule, current_mem_size, &mem_size)?; + let copy = overflowing!(to_word_size(copy)); + let copy_gas = overflowing!(Gas::from(schedule.copy_gas).overflow_mul(copy)); + let gas = overflowing!(gas.overflow_add(copy_gas)); + let gas = overflowing!(gas.overflow_add(mem_gas_cost)); + + InstructionRequirements { + gas_cost: gas, + provide_gas: None, + memory_required_size: new_mem_size, + memory_total_gas: new_mem_gas, + } + } + }) + } + + fn mem_gas_cost( + &self, + schedule: &Schedule, + current_mem_size: usize, + mem_size: &Gas, + ) -> vm::Result<(Gas, Gas, usize)> { + let gas_for_mem = |mem_size: Gas| { + let s = mem_size >> 5; + // s * memory_gas + s * s / quad_coeff_div + let a = overflowing!(s.overflow_mul(Gas::from(schedule.memory_gas))); + + // Calculate s*s/quad_coeff_div + assert_eq!(schedule.quad_coeff_div, 512); + let b = overflowing!(s.overflow_mul_shr(s, 9)); + Ok(overflowing!(a.overflow_add(b))) + }; + + let current_mem_size = Gas::from(current_mem_size); + let req_mem_size_rounded = overflowing!(to_word_size(*mem_size)) << 5; + + let (mem_gas_cost, new_mem_gas) = if req_mem_size_rounded > current_mem_size { + let new_mem_gas = gas_for_mem(req_mem_size_rounded)?; + (new_mem_gas - self.current_mem_gas, new_mem_gas) + } else { + (Gas::from(0), self.current_mem_gas) + }; + + Ok((mem_gas_cost, new_mem_gas, req_mem_size_rounded.as_usize())) + } } #[inline] fn mem_needed_const(mem: &U256, add: usize) -> vm::Result { - Gas::from_u256(overflowing!(mem.overflowing_add(U256::from(add)))) + Gas::from_u256(overflowing!(mem.overflowing_add(U256::from(add)))) } #[inline] fn mem_needed(offset: &U256, size: &U256) -> vm::Result { - if size.is_zero() { - return Ok(Gas::from(0)); - } + if size.is_zero() { + return Ok(Gas::from(0)); + } - Gas::from_u256(overflowing!(offset.overflowing_add(*size))) + Gas::from_u256(overflowing!(offset.overflowing_add(*size))) } #[inline] fn add_gas_usize(value: Gas, num: usize) -> (Gas, bool) { - value.overflow_add(Gas::from(num)) + value.overflow_add(Gas::from(num)) } #[inline] fn to_word_size(value: Gas) -> (Gas, bool) { - let (gas, overflow) = add_gas_usize(value, 31); - if overflow { - return (gas, overflow); - } + let (gas, overflow) = add_gas_usize(value, 31); + if overflow { + return (gas, overflow); + } - (gas >> 5, false) + (gas >> 5, false) } #[inline] -fn calculate_eip1283_sstore_gas(schedule: &Schedule, original: &U256, current: &U256, new: &U256) -> Gas { - Gas::from( - if current == new { - // 1. If current value equals new value (this is a no-op), 200 gas is deducted. - schedule.sload_gas - } else { - // 2. If current value does not equal new value - if original == current { - // 2.1. If original value equals current value (this storage slot has not been changed by the current execution context) - if original.is_zero() { - // 2.1.1. If original value is 0, 20000 gas is deducted. - schedule.sstore_set_gas - } else { - // 2.1.2. Otherwise, 5000 gas is deducted. - schedule.sstore_reset_gas - - // 2.1.2.1. If new value is 0, add 15000 gas to refund counter. - } - } else { - // 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses. - schedule.sload_gas - - // 2.2.1. If original value is not 0 - // 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0. - // 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter. - - // 2.2.2. If original value equals new value (this storage slot is reset) - // 2.2.2.1. If original value is 0, add 19800 gas to refund counter. - // 2.2.2.2. Otherwise, add 4800 gas to refund counter. - } - } - ) +fn calculate_eip1283_sstore_gas( + schedule: &Schedule, + original: &U256, + current: &U256, + new: &U256, +) -> Gas { + Gas::from(if current == new { + // 1. If current value equals new value (this is a no-op), 200 gas is deducted. + schedule.sload_gas + } else { + // 2. If current value does not equal new value + if original == current { + // 2.1. If original value equals current value (this storage slot has not been changed by the current execution context) + if original.is_zero() { + // 2.1.1. If original value is 0, 20000 gas is deducted. + schedule.sstore_set_gas + } else { + // 2.1.2. Otherwise, 5000 gas is deducted. + schedule.sstore_reset_gas + + // 2.1.2.1. If new value is 0, add 15000 gas to refund counter. + } + } else { + // 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses. + schedule.sload_gas + + // 2.2.1. If original value is not 0 + // 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0. + // 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter. + + // 2.2.2. If original value equals new value (this storage slot is reset) + // 2.2.2.1. If original value is 0, add 19800 gas to refund counter. + // 2.2.2.2. Otherwise, add 4800 gas to refund counter. + } + }) } -pub fn handle_eip1283_sstore_clears_refund(ext: &mut vm::Ext, original: &U256, current: &U256, new: &U256) { - let sstore_clears_schedule = ext.schedule().sstore_refund_gas; - - if current == new { - // 1. If current value equals new value (this is a no-op), 200 gas is deducted. - } else { - // 2. If current value does not equal new value - if original == current { - // 2.1. If original value equals current value (this storage slot has not been changed by the current execution context) - if original.is_zero() { - // 2.1.1. If original value is 0, 20000 gas is deducted. - } else { - // 2.1.2. Otherwise, 5000 gas is deducted. - if new.is_zero() { - // 2.1.2.1. If new value is 0, add 15000 gas to refund counter. - ext.add_sstore_refund(sstore_clears_schedule); - } - } - } else { - // 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses. - - if !original.is_zero() { - // 2.2.1. If original value is not 0 - if current.is_zero() { - // 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0. - ext.sub_sstore_refund(sstore_clears_schedule); - } else if new.is_zero() { - // 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter. - ext.add_sstore_refund(sstore_clears_schedule); - } - } - - if original == new { - // 2.2.2. If original value equals new value (this storage slot is reset) - if original.is_zero() { - // 2.2.2.1. If original value is 0, add 19800 gas to refund counter. - let refund = ext.schedule().sstore_set_gas - ext.schedule().sload_gas; - ext.add_sstore_refund(refund); - } else { - // 2.2.2.2. Otherwise, add 4800 gas to refund counter. - let refund = ext.schedule().sstore_reset_gas - ext.schedule().sload_gas; - ext.add_sstore_refund(refund); - } - } - } - } +pub fn handle_eip1283_sstore_clears_refund( + ext: &mut dyn vm::Ext, + original: &U256, + current: &U256, + new: &U256, +) { + let sstore_clears_schedule = ext.schedule().sstore_refund_gas; + + if current == new { + // 1. If current value equals new value (this is a no-op), 200 gas is deducted. + } else { + // 2. If current value does not equal new value + if original == current { + // 2.1. If original value equals current value (this storage slot has not been changed by the current execution context) + if original.is_zero() { + // 2.1.1. If original value is 0, 20000 gas is deducted. + } else { + // 2.1.2. Otherwise, 5000 gas is deducted. + if new.is_zero() { + // 2.1.2.1. If new value is 0, add 15000 gas to refund counter. + ext.add_sstore_refund(sstore_clears_schedule); + } + } + } else { + // 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses. + + if !original.is_zero() { + // 2.2.1. If original value is not 0 + if current.is_zero() { + // 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0. + ext.sub_sstore_refund(sstore_clears_schedule); + } else if new.is_zero() { + // 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter. + ext.add_sstore_refund(sstore_clears_schedule); + } + } + + if original == new { + // 2.2.2. If original value equals new value (this storage slot is reset) + if original.is_zero() { + // 2.2.2.1. If original value is 0, add 19800 gas to refund counter. + let refund = ext.schedule().sstore_set_gas - ext.schedule().sload_gas; + ext.add_sstore_refund(refund); + } else { + // 2.2.2.2. Otherwise, add 4800 gas to refund counter. + let refund = ext.schedule().sstore_reset_gas - ext.schedule().sload_gas; + ext.add_sstore_refund(refund); + } + } + } + } } #[test] fn test_mem_gas_cost() { - // given - let gasometer = Gasometer::::new(U256::zero()); - let schedule = Schedule::default(); - let current_mem_size = 5; - let mem_size = !U256::zero(); - - // when - let result = gasometer.mem_gas_cost(&schedule, current_mem_size, &mem_size); - - // then - if result.is_ok() { - assert!(false, "Should fail with OutOfGas"); - } + // given + let gasometer = Gasometer::::new(U256::zero()); + let schedule = Schedule::default(); + let current_mem_size = 5; + let mem_size = !U256::zero(); + + // when + let result = gasometer.mem_gas_cost(&schedule, current_mem_size, &mem_size); + + // then + if result.is_ok() { + assert!(false, "Should fail with OutOfGas"); + } } #[test] fn test_calculate_mem_cost() { - // given - let gasometer = Gasometer::::new(0); - let schedule = Schedule::default(); - let current_mem_size = 0; - let mem_size = 5; - - // when - let (mem_cost, new_mem_gas, mem_size) = gasometer.mem_gas_cost(&schedule, current_mem_size, &mem_size).unwrap(); - - // then - assert_eq!(mem_cost, 3); - assert_eq!(new_mem_gas, 3); - assert_eq!(mem_size, 32); + // given + let gasometer = Gasometer::::new(0); + let schedule = Schedule::default(); + let current_mem_size = 0; + let mem_size = 5; + + // when + let (mem_cost, new_mem_gas, mem_size) = gasometer + .mem_gas_cost(&schedule, current_mem_size, &mem_size) + .unwrap(); + + // then + assert_eq!(mem_cost, 3); + assert_eq!(new_mem_gas, 3); + assert_eq!(mem_size, 32); } diff --git a/ethcore/evm/src/interpreter/informant.rs b/ethcore/evm/src/interpreter/informant.rs index 93d459f4177..03ae3695152 100644 --- a/ethcore/evm/src/interpreter/informant.rs +++ b/ethcore/evm/src/interpreter/informant.rs @@ -1,162 +1,174 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . pub use self::inner::*; #[macro_use] #[cfg(not(feature = "evm-debug"))] mod inner { - macro_rules! evm_debug { - ($x: expr) => {} - } - - pub struct EvmInformant; - impl EvmInformant { - pub fn new(_depth: usize) -> Self { - EvmInformant {} - } - pub fn done(&mut self) {} - } + macro_rules! evm_debug { + ($x: expr) => {}; + } + + pub struct EvmInformant; + impl EvmInformant { + pub fn new(_depth: usize) -> Self { + EvmInformant {} + } + pub fn done(&mut self) {} + } } #[macro_use] #[cfg(feature = "evm-debug")] mod inner { - use std::iter; - use std::collections::HashMap; - use std::time::{Instant, Duration}; - - use ethereum_types::U256; - - use interpreter::stack::Stack; - use instructions::{Instruction, InstructionInfo}; - use CostType; - - macro_rules! evm_debug { - ($x: expr) => { - $x - } - } - - fn print(data: String) { - if cfg!(feature = "evm-debug-tests") { - println!("{}", data); - } else { - debug!(target: "evm", "{}", data); - } - } - - pub struct EvmInformant { - spacing: String, - last_instruction: Instant, - stats: HashMap, - } - - impl EvmInformant { - - fn color(instruction: Instruction, name: &str) -> String { - let c = instruction as usize % 6; - let colors = [31, 34, 33, 32, 35, 36]; - format!("\x1B[1;{}m{}\x1B[0m", colors[c], name) - } - - fn as_micro(duration: &Duration) -> u64 { - let mut sec = duration.as_secs(); - let subsec = duration.subsec_nanos() as u64; - sec = sec.saturating_mul(1_000_000u64); - sec += subsec / 1_000; - sec - } - - pub fn new(depth: usize) -> Self { - EvmInformant { - spacing: iter::repeat(".").take(depth).collect(), - last_instruction: Instant::now(), - stats: HashMap::new(), - } - } - - pub fn before_instruction(&mut self, pc: usize, instruction: Instruction, info: &InstructionInfo, current_gas: &Cost, stack: &Stack) { - let time = self.last_instruction.elapsed(); - self.last_instruction = Instant::now(); - - print(format!("{}[0x{:<3x}][{:>19}(0x{:<2x}) Gas Left: {:6?} (Previous took: {:10}μs)", - &self.spacing, - pc, - Self::color(instruction, info.name), - instruction as u8, - current_gas, - Self::as_micro(&time), - )); - - if info.args > 0 { - for (idx, item) in stack.peek_top(info.args).iter().enumerate() { - print(format!("{} |{:2}: {:?}", self.spacing, idx, item)); - } - } - } - - pub fn after_instruction(&mut self, instruction: Instruction) { - let stats = self.stats.entry(instruction).or_insert_with(|| Stats::default()); - let took = self.last_instruction.elapsed(); - stats.note(took); - } - - pub fn done(&mut self) { - // Print out stats - let mut stats: Vec<(_,_)> = self.stats.drain().collect(); - stats.sort_by(|ref a, ref b| b.1.avg().cmp(&a.1.avg())); - - print(format!("\n{}-------OPCODE STATS:", self.spacing)); - for (instruction, stats) in stats.into_iter() { - let info = instruction.info(); - print(format!("{}-------{:>19}(0x{:<2x}) count: {:4}, avg: {:10}μs", - self.spacing, - Self::color(instruction, info.name), - instruction as u8, - stats.count, - stats.avg(), - )); - } - } - - } - - struct Stats { - count: u64, - total_duration: Duration, - } - - impl Default for Stats { - fn default() -> Self { - Stats { - count: 0, - total_duration: Duration::from_secs(0), - } - } - } - - impl Stats { - fn note(&mut self, took: Duration) { - self.count += 1; - self.total_duration += took; - } - - fn avg(&self) -> u64 { - EvmInformant::as_micro(&self.total_duration) / self.count - } - } + use std::{ + collections::HashMap, + iter, + time::{Duration, Instant}, + }; + + use ethereum_types::U256; + + use instructions::{Instruction, InstructionInfo}; + use interpreter::stack::Stack; + use CostType; + + macro_rules! evm_debug { + ($x: expr) => { + $x + }; + } + + fn print(data: String) { + if cfg!(feature = "evm-debug-tests") { + println!("{}", data); + } else { + debug!(target: "evm", "{}", data); + } + } + + pub struct EvmInformant { + spacing: String, + last_instruction: Instant, + stats: HashMap, + } + + impl EvmInformant { + fn color(instruction: Instruction, name: &str) -> String { + let c = instruction as usize % 6; + let colors = [31, 34, 33, 32, 35, 36]; + format!("\x1B[1;{}m{}\x1B[0m", colors[c], name) + } + + fn as_micro(duration: &Duration) -> u64 { + let mut sec = duration.as_secs(); + let subsec = duration.subsec_nanos() as u64; + sec = sec.saturating_mul(1_000_000u64); + sec += subsec / 1_000; + sec + } + + pub fn new(depth: usize) -> Self { + EvmInformant { + spacing: iter::repeat(".").take(depth).collect(), + last_instruction: Instant::now(), + stats: HashMap::new(), + } + } + + pub fn before_instruction( + &mut self, + pc: usize, + instruction: Instruction, + info: &InstructionInfo, + current_gas: &Cost, + stack: &dyn Stack, + ) { + let time = self.last_instruction.elapsed(); + self.last_instruction = Instant::now(); + + print(format!( + "{}[0x{:<3x}][{:>19}(0x{:<2x}) Gas Left: {:6?} (Previous took: {:10}μs)", + &self.spacing, + pc, + Self::color(instruction, info.name), + instruction as u8, + current_gas, + Self::as_micro(&time), + )); + + if info.args > 0 { + for (idx, item) in stack.peek_top(info.args).iter().enumerate() { + print(format!("{} |{:2}: {:?}", self.spacing, idx, item)); + } + } + } + + pub fn after_instruction(&mut self, instruction: Instruction) { + let stats = self + .stats + .entry(instruction) + .or_insert_with(|| Stats::default()); + let took = self.last_instruction.elapsed(); + stats.note(took); + } + + pub fn done(&mut self) { + // Print out stats + let mut stats: Vec<(_, _)> = self.stats.drain().collect(); + stats.sort_by(|ref a, ref b| b.1.avg().cmp(&a.1.avg())); + + print(format!("\n{}-------OPCODE STATS:", self.spacing)); + for (instruction, stats) in stats.into_iter() { + let info = instruction.info(); + print(format!( + "{}-------{:>19}(0x{:<2x}) count: {:4}, avg: {:10}μs", + self.spacing, + Self::color(instruction, info.name), + instruction as u8, + stats.count, + stats.avg(), + )); + } + } + } + + struct Stats { + count: u64, + total_duration: Duration, + } + + impl Default for Stats { + fn default() -> Self { + Stats { + count: 0, + total_duration: Duration::from_secs(0), + } + } + } + + impl Stats { + fn note(&mut self, took: Duration) { + self.count += 1; + self.total_duration += took; + } + + fn avg(&self) -> u64 { + EvmInformant::as_micro(&self.total_duration) / self.count + } + } } diff --git a/ethcore/evm/src/interpreter/memory.rs b/ethcore/evm/src/interpreter/memory.rs index 16c575d5e5c..22d5a4df5c4 100644 --- a/ethcore/evm/src/interpreter/memory.rs +++ b/ethcore/evm/src/interpreter/memory.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use ethereum_types::U256; use vm::ReturnData; @@ -20,172 +20,175 @@ use vm::ReturnData; const MAX_RETURN_WASTE_BYTES: usize = 16384; pub trait Memory { - /// Retrieve current size of the memory - fn size(&self) -> usize; - /// Resize (shrink or expand) the memory to specified size (fills 0) - fn resize(&mut self, new_size: usize); - /// Resize the memory only if its smaller - fn expand(&mut self, new_size: usize); - /// Write single byte to memory - fn write_byte(&mut self, offset: U256, value: U256); - /// Write a word to memory. Does not resize memory! - fn write(&mut self, offset: U256, value: U256); - /// Read a word from memory - fn read(&self, offset: U256) -> U256; - /// Write slice of bytes to memory. Does not resize memory! - fn write_slice(&mut self, offset: U256, &[u8]); - /// Retrieve part of the memory between offset and offset + size - fn read_slice(&self, offset: U256, size: U256) -> &[u8]; - /// Retrieve writeable part of memory - fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut[u8]; - /// Convert memory into return data. - fn into_return_data(self, offset: U256, size: U256) -> ReturnData; + /// Retrieve current size of the memory + fn size(&self) -> usize; + /// Resize (shrink or expand) the memory to specified size (fills 0) + fn resize(&mut self, new_size: usize); + /// Resize the memory only if its smaller + fn expand(&mut self, new_size: usize); + /// Write single byte to memory + fn write_byte(&mut self, offset: U256, value: U256); + /// Write a word to memory. Does not resize memory! + fn write(&mut self, offset: U256, value: U256); + /// Read a word from memory + fn read(&self, offset: U256) -> U256; + /// Write slice of bytes to memory. Does not resize memory! + fn write_slice(&mut self, offset: U256, &[u8]); + /// Retrieve part of the memory between offset and offset + size + fn read_slice(&self, offset: U256, size: U256) -> &[u8]; + /// Retrieve writeable part of memory + fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut [u8]; + /// Convert memory into return data. + fn into_return_data(self, offset: U256, size: U256) -> ReturnData; } /// Checks whether offset and size is valid memory range -pub fn is_valid_range(off: usize, size: usize) -> bool { - // When size is zero we haven't actually expanded the memory - let overflow = off.overflowing_add(size).1; - size > 0 && !overflow +pub fn is_valid_range(off: usize, size: usize) -> bool { + // When size is zero we haven't actually expanded the memory + let overflow = off.overflowing_add(size).1; + size > 0 && !overflow } impl Memory for Vec { - fn size(&self) -> usize { - self.len() - } - - fn read_slice(&self, init_off_u: U256, init_size_u: U256) -> &[u8] { - let off = init_off_u.low_u64() as usize; - let size = init_size_u.low_u64() as usize; - if !is_valid_range(off, size) { - &self[0..0] - } else { - &self[off..off+size] - } - } - - fn read(&self, offset: U256) -> U256 { - let off = offset.low_u64() as usize; - U256::from(&self[off..off+32]) - } - - fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut [u8] { - let off = offset.low_u64() as usize; - let s = size.low_u64() as usize; - if !is_valid_range(off, s) { - &mut self[0..0] - } else { - &mut self[off..off+s] - } - } - - fn write_slice(&mut self, offset: U256, slice: &[u8]) { - if !slice.is_empty() { - let off = offset.low_u64() as usize; - self[off..off+slice.len()].copy_from_slice(slice); - } - } - - fn write(&mut self, offset: U256, value: U256) { - let off = offset.low_u64() as usize; - value.to_big_endian(&mut self[off..off+32]); - } - - fn write_byte(&mut self, offset: U256, value: U256) { - let off = offset.low_u64() as usize; - let val = value.low_u64() as u64; - self[off] = val as u8; - } - - fn resize(&mut self, new_size: usize) { - self.resize(new_size, 0); - } - - fn expand(&mut self, size: usize) { - if size > self.len() { - Memory::resize(self, size) - } - } - - fn into_return_data(mut self, offset: U256, size: U256) -> ReturnData { - let mut offset = offset.low_u64() as usize; - let size = size.low_u64() as usize; - - if !is_valid_range(offset, size) { - return ReturnData::empty(); - } - - if self.len() - size > MAX_RETURN_WASTE_BYTES { - if offset == 0 { - self.truncate(size); - self.shrink_to_fit(); - } else { - self = self[offset..(offset + size)].to_vec(); - offset = 0; - } - } - ReturnData::new(self, offset, size) - } + fn size(&self) -> usize { + self.len() + } + + fn read_slice(&self, init_off_u: U256, init_size_u: U256) -> &[u8] { + let off = init_off_u.low_u64() as usize; + let size = init_size_u.low_u64() as usize; + if !is_valid_range(off, size) { + &self[0..0] + } else { + &self[off..off + size] + } + } + + fn read(&self, offset: U256) -> U256 { + let off = offset.low_u64() as usize; + U256::from(&self[off..off + 32]) + } + + fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut [u8] { + let off = offset.low_u64() as usize; + let s = size.low_u64() as usize; + if !is_valid_range(off, s) { + &mut self[0..0] + } else { + &mut self[off..off + s] + } + } + + fn write_slice(&mut self, offset: U256, slice: &[u8]) { + if !slice.is_empty() { + let off = offset.low_u64() as usize; + self[off..off + slice.len()].copy_from_slice(slice); + } + } + + fn write(&mut self, offset: U256, value: U256) { + let off = offset.low_u64() as usize; + value.to_big_endian(&mut self[off..off + 32]); + } + + fn write_byte(&mut self, offset: U256, value: U256) { + let off = offset.low_u64() as usize; + let val = value.low_u64() as u64; + self[off] = val as u8; + } + + fn resize(&mut self, new_size: usize) { + self.resize(new_size, 0); + } + + fn expand(&mut self, size: usize) { + if size > self.len() { + Memory::resize(self, size) + } + } + + fn into_return_data(mut self, offset: U256, size: U256) -> ReturnData { + let mut offset = offset.low_u64() as usize; + let size = size.low_u64() as usize; + + if !is_valid_range(offset, size) { + return ReturnData::empty(); + } + + if self.len() - size > MAX_RETURN_WASTE_BYTES { + if offset == 0 { + self.truncate(size); + self.shrink_to_fit(); + } else { + self = self[offset..(offset + size)].to_vec(); + offset = 0; + } + } + ReturnData::new(self, offset, size) + } } #[cfg(test)] mod tests { - use ethereum_types::U256; - use super::Memory; - - #[test] - fn test_memory_read_and_write() { - // given - let mem: &mut Memory = &mut vec![]; - mem.resize(0x80 + 32); - - // when - mem.write(U256::from(0x80), U256::from(0xabcdef)); - - // then - assert_eq!(mem.read(U256::from(0x80)), U256::from(0xabcdef)); - } - - #[test] - fn test_memory_read_and_write_byte() { - // given - let mem: &mut Memory = &mut vec![]; - mem.resize(32); - - // when - mem.write_byte(U256::from(0x1d), U256::from(0xab)); - mem.write_byte(U256::from(0x1e), U256::from(0xcd)); - mem.write_byte(U256::from(0x1f), U256::from(0xef)); - - // then - assert_eq!(mem.read(U256::from(0x00)), U256::from(0xabcdef)); - } - - #[test] - fn test_memory_read_slice_and_write_slice() { - let mem: &mut Memory = &mut vec![]; - mem.resize(32); - - { - let slice = "abcdefghijklmnopqrstuvwxyz012345".as_bytes(); - mem.write_slice(U256::from(0), slice); - - assert_eq!(mem.read_slice(U256::from(0), U256::from(32)), slice); - } - - // write again - { - let slice = "67890".as_bytes(); - mem.write_slice(U256::from(0x1), slice); - - assert_eq!(mem.read_slice(U256::from(0), U256::from(7)), "a67890g".as_bytes()); - } - - // write empty slice out of bounds - { - let slice = []; - mem.write_slice(U256::from(0x1000), &slice); - assert_eq!(mem.size(), 32); - } - } + use super::Memory; + use ethereum_types::U256; + + #[test] + fn test_memory_read_and_write() { + // given + let mem: &mut dyn Memory = &mut vec![]; + mem.resize(0x80 + 32); + + // when + mem.write(U256::from(0x80), U256::from(0xabcdef)); + + // then + assert_eq!(mem.read(U256::from(0x80)), U256::from(0xabcdef)); + } + + #[test] + fn test_memory_read_and_write_byte() { + // given + let mem: &mut dyn Memory = &mut vec![]; + mem.resize(32); + + // when + mem.write_byte(U256::from(0x1d), U256::from(0xab)); + mem.write_byte(U256::from(0x1e), U256::from(0xcd)); + mem.write_byte(U256::from(0x1f), U256::from(0xef)); + + // then + assert_eq!(mem.read(U256::from(0x00)), U256::from(0xabcdef)); + } + + #[test] + fn test_memory_read_slice_and_write_slice() { + let mem: &mut dyn Memory = &mut vec![]; + mem.resize(32); + + { + let slice = "abcdefghijklmnopqrstuvwxyz012345".as_bytes(); + mem.write_slice(U256::from(0), slice); + + assert_eq!(mem.read_slice(U256::from(0), U256::from(32)), slice); + } + + // write again + { + let slice = "67890".as_bytes(); + mem.write_slice(U256::from(0x1), slice); + + assert_eq!( + mem.read_slice(U256::from(0), U256::from(7)), + "a67890g".as_bytes() + ); + } + + // write empty slice out of bounds + { + let slice = []; + mem.write_slice(U256::from(0x1000), &slice); + assert_eq!(mem.size(), 32); + } + } } diff --git a/ethcore/evm/src/interpreter/mod.rs b/ethcore/evm/src/interpreter/mod.rs index d699e61cbec..332b268fb38 100644 --- a/ethcore/evm/src/interpreter/mod.rs +++ b/ethcore/evm/src/interpreter/mod.rs @@ -1,48 +1,48 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Rust VM implementation #[macro_use] mod informant; mod gasometer; -mod stack; mod memory; mod shared_cache; +mod stack; -use std::marker::PhantomData; -use std::{cmp, mem}; -use std::sync::Arc; -use hash::keccak; use bytes::Bytes; -use ethereum_types::{U256, U512, H256, Address}; +use ethereum_types::{Address, H256, U256}; +use hash::keccak; +use num_bigint::BigUint; +use std::{cmp, marker::PhantomData, mem, sync::Arc}; use vm::{ - self, ActionParams, ParamsType, ActionValue, CallType, MessageCallResult, - ContractCreateResult, CreateContractAddress, ReturnData, GasLeft, Schedule, - TrapKind, TrapError + self, ActionParams, ActionValue, CallType, ContractCreateResult, CreateContractAddress, + GasLeft, MessageCallResult, ParamsType, ReturnData, Schedule, TrapError, TrapKind, }; use evm::CostType; use instructions::{self, Instruction, InstructionInfo}; -use self::gasometer::Gasometer; -use self::stack::{Stack, VecStack}; -use self::memory::Memory; pub use self::shared_cache::SharedCache; +use self::{ + gasometer::Gasometer, + memory::Memory, + stack::{Stack, VecStack}, +}; use bit_set::BitSet; @@ -61,1174 +61,1513 @@ const TWO_POW_96: U256 = U256([0, 0x100000000, 0, 0]); //0x1 00000000 00000000 0 const TWO_POW_224: U256 = U256([0, 0, 0, 0x100000000]); //0x1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 const TWO_POW_248: U256 = U256([0, 0, 0, 0x100000000000000]); //0x1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000000 +/// Maximum subroutine stack size as specified in +/// https://eips.ethereum.org/EIPS/eip-2315. +pub const MAX_SUB_STACK_SIZE: usize = 1023; + +fn to_biguint(x: U256) -> BigUint { + let mut bytes = [0u8; 32]; + x.to_little_endian(&mut bytes); + BigUint::from_bytes_le(&bytes) +} + +fn from_biguint(x: BigUint) -> U256 { + let bytes = x.to_bytes_le(); + U256::from_little_endian(&bytes) +} + /// Abstraction over raw vector of Bytes. Easier state management of PC. struct CodeReader { - position: ProgramCounter, - code: Arc, + position: ProgramCounter, + code: Arc, } impl CodeReader { - /// Create new code reader - starting at position 0. - fn new(code: Arc) -> Self { - CodeReader { - code, - position: 0, - } - } - - /// Get `no_of_bytes` from code and convert to U256. Move PC - fn read(&mut self, no_of_bytes: usize) -> U256 { - let pos = self.position; - self.position += no_of_bytes; - let max = cmp::min(pos + no_of_bytes, self.code.len()); - U256::from(&self.code[pos..max]) - } - - fn len(&self) -> usize { - self.code.len() - } + /// Create new code reader - starting at position 0. + fn new(code: Arc) -> Self { + CodeReader { code, position: 0 } + } + + /// Get `no_of_bytes` from code and convert to U256. Move PC + fn read(&mut self, no_of_bytes: usize) -> U256 { + let pos = self.position; + self.position += no_of_bytes; + let max = cmp::min(pos + no_of_bytes, self.code.len()); + U256::from(&self.code[pos..max]) + } + + fn len(&self) -> usize { + self.code.len() + } } enum InstructionResult { - Ok, - UnusedGas(Gas), - JumpToPosition(U256), - StopExecutionNeedsReturn { - /// Gas left. - gas: Gas, - /// Return data offset. - init_off: U256, - /// Return data size. - init_size: U256, - /// Apply or revert state changes. - apply: bool, - }, - StopExecution, - Trap(TrapKind), + Ok, + UnusedGas(Gas), + JumpToPosition(U256), + JumpToSubroutine(U256), + ReturnFromSubroutine(usize), + StopExecutionNeedsReturn { + /// Gas left. + gas: Gas, + /// Return data offset. + init_off: U256, + /// Return data size. + init_size: U256, + /// Apply or revert state changes. + apply: bool, + }, + StopExecution, + Trap(TrapKind), } -enum Never {} - /// ActionParams without code, so that it can be feed into CodeReader. #[derive(Debug)] struct InterpreterParams { - /// Address of currently executed code. - pub code_address: Address, - /// Hash of currently executed code. - pub code_hash: Option, - /// Receive address. Usually equal to code_address, - /// except when called using CALLCODE. - pub address: Address, - /// Sender of current part of the transaction. - pub sender: Address, - /// Transaction initiator. - pub origin: Address, - /// Gas paid up front for transaction execution - pub gas: U256, - /// Gas price. - pub gas_price: U256, - /// Transaction value. - pub value: ActionValue, - /// Input data. - pub data: Option, - /// Type of call - pub call_type: CallType, - /// Param types encoding - pub params_type: ParamsType, + /// Address of currently executed code. + pub code_address: Address, + /// Hash of currently executed code. + pub code_hash: Option, + /// Receive address. Usually equal to code_address, + /// except when called using CALLCODE. + pub address: Address, + /// Sender of current part of the transaction. + pub sender: Address, + /// Transaction initiator. + pub origin: Address, + /// Gas paid up front for transaction execution + pub gas: U256, + /// Gas price. + pub gas_price: U256, + /// Transaction value. + pub value: ActionValue, + /// Input data. + pub data: Option, + /// Type of call + pub call_type: CallType, + /// Param types encoding + pub params_type: ParamsType, } impl From for InterpreterParams { - fn from(params: ActionParams) -> Self { - InterpreterParams { - code_address: params.code_address, - code_hash: params.code_hash, - address: params.address, - sender: params.sender, - origin: params.origin, - gas: params.gas, - gas_price: params.gas_price, - value: params.value, - data: params.data, - call_type: params.call_type, - params_type: params.params_type, - } - } + fn from(params: ActionParams) -> Self { + InterpreterParams { + code_address: params.code_address, + code_hash: params.code_hash, + address: params.address, + sender: params.sender, + origin: params.origin, + gas: params.gas, + gas_price: params.gas_price, + value: params.value, + data: params.data, + call_type: params.call_type, + params_type: params.params_type, + } + } } /// Stepping result returned by interpreter. pub enum InterpreterResult { - /// The VM has already stopped. - Stopped, - /// The VM has just finished execution in the current step. - Done(vm::Result), - /// The VM can continue to run. - Continue, - Trap(TrapKind), -} - -impl From for InterpreterResult { - fn from(error: vm::Error) -> InterpreterResult { - InterpreterResult::Done(Err(error)) - } + /// The VM has already stopped. + Stopped, + /// The VM has just finished execution in the current step. + Done(vm::Result), + /// The VM can continue to run. + Continue, + Trap(TrapKind), } /// Intepreter EVM implementation pub struct Interpreter { - mem: Vec, - cache: Arc, - params: InterpreterParams, - reader: CodeReader, - return_data: ReturnData, - informant: informant::EvmInformant, - do_trace: bool, - done: bool, - valid_jump_destinations: Option>, - gasometer: Option>, - stack: VecStack, - resume_output_range: Option<(U256, U256)>, - resume_result: Option>, - last_stack_ret_len: usize, - _type: PhantomData, + mem: Vec, + cache: Arc, + params: InterpreterParams, + reader: CodeReader, + return_data: ReturnData, + informant: informant::EvmInformant, + do_trace: bool, + done: bool, + valid_jump_destinations: Option>, + valid_subroutine_destinations: Option>, + gasometer: Option>, + stack: VecStack, + return_stack: Vec, + resume_output_range: Option<(U256, U256)>, + resume_result: Option>, + last_stack_ret_len: usize, + _type: PhantomData, } impl vm::Exec for Interpreter { - fn exec(mut self: Box, ext: &mut vm::Ext) -> vm::ExecTrapResult { - loop { - let result = self.step(ext); - match result { - InterpreterResult::Continue => {}, - InterpreterResult::Done(value) => return Ok(value), - InterpreterResult::Trap(trap) => match trap { - TrapKind::Call(params) => { - return Err(TrapError::Call(params, self)); - }, - TrapKind::Create(params, address) => { - return Err(TrapError::Create(params, address, self)); - }, - }, - InterpreterResult::Stopped => panic!("Attempted to execute an already stopped VM.") - } - } - } + fn exec(mut self: Box, ext: &mut dyn vm::Ext) -> vm::ExecTrapResult { + loop { + let result = self.step(ext); + match result { + InterpreterResult::Continue => {} + InterpreterResult::Done(value) => return Ok(value), + InterpreterResult::Trap(trap) => match trap { + TrapKind::Call(params) => { + return Err(TrapError::Call(params, self)); + } + TrapKind::Create(params, address) => { + return Err(TrapError::Create(params, address, self)); + } + }, + InterpreterResult::Stopped => panic!("Attempted to execute an already stopped VM."), + } + } + } } impl vm::ResumeCall for Interpreter { - fn resume_call(mut self: Box, result: MessageCallResult) -> Box { - { - let this = &mut *self; - let (out_off, out_size) = this.resume_output_range.take().expect("Box is obtained from a call opcode; resume_output_range is always set after those opcodes are executed; qed"); - - match result { - MessageCallResult::Success(gas_left, data) => { - let output = this.mem.writeable_slice(out_off, out_size); - let len = cmp::min(output.len(), data.len()); - (&mut output[..len]).copy_from_slice(&data[..len]); - - this.return_data = data; - this.stack.push(U256::one()); - this.resume_result = Some(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater than current one"))); - }, - MessageCallResult::Reverted(gas_left, data) => { - let output = this.mem.writeable_slice(out_off, out_size); - let len = cmp::min(output.len(), data.len()); - (&mut output[..len]).copy_from_slice(&data[..len]); - - this.return_data = data; - this.stack.push(U256::zero()); - this.resume_result = Some(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater than current one"))); - }, - MessageCallResult::Failed => { - this.stack.push(U256::zero()); - this.resume_result = Some(InstructionResult::Ok); - }, - } - } - self - } + fn resume_call(mut self: Box, result: MessageCallResult) -> Box { + { + let this = &mut *self; + let (out_off, out_size) = this.resume_output_range.take().expect("Box is obtained from a call opcode; resume_output_range is always set after those opcodes are executed; qed"); + + match result { + MessageCallResult::Success(gas_left, data) => { + let output = this.mem.writeable_slice(out_off, out_size); + let len = cmp::min(output.len(), data.len()); + (&mut output[..len]).copy_from_slice(&data[..len]); + + this.return_data = data; + this.stack.push(U256::one()); + this.resume_result = Some(InstructionResult::UnusedGas( + Cost::from_u256(gas_left) + .expect("Gas left cannot be greater than current one"), + )); + } + MessageCallResult::Reverted(gas_left, data) => { + let output = this.mem.writeable_slice(out_off, out_size); + let len = cmp::min(output.len(), data.len()); + (&mut output[..len]).copy_from_slice(&data[..len]); + + this.return_data = data; + this.stack.push(U256::zero()); + this.resume_result = Some(InstructionResult::UnusedGas( + Cost::from_u256(gas_left) + .expect("Gas left cannot be greater than current one"), + )); + } + MessageCallResult::Failed => { + this.stack.push(U256::zero()); + this.resume_result = Some(InstructionResult::Ok); + } + } + } + self + } } impl vm::ResumeCreate for Interpreter { - fn resume_create(mut self: Box, result: ContractCreateResult) -> Box { - match result { - ContractCreateResult::Created(address, gas_left) => { - self.stack.push(address_to_u256(address)); - self.resume_result = Some(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater."))); - }, - ContractCreateResult::Reverted(gas_left, return_data) => { - self.stack.push(U256::zero()); - self.return_data = return_data; - self.resume_result = Some(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater."))); - }, - ContractCreateResult::Failed => { - self.stack.push(U256::zero()); - self.resume_result = Some(InstructionResult::Ok); - }, - } - self - } + fn resume_create(mut self: Box, result: ContractCreateResult) -> Box { + match result { + ContractCreateResult::Created(address, gas_left) => { + self.stack.push(address_to_u256(address)); + self.resume_result = Some(InstructionResult::UnusedGas( + Cost::from_u256(gas_left).expect("Gas left cannot be greater."), + )); + } + ContractCreateResult::Reverted(gas_left, return_data) => { + self.stack.push(U256::zero()); + self.return_data = return_data; + self.resume_result = Some(InstructionResult::UnusedGas( + Cost::from_u256(gas_left).expect("Gas left cannot be greater."), + )); + } + ContractCreateResult::Failed => { + self.stack.push(U256::zero()); + self.resume_result = Some(InstructionResult::Ok); + } + } + self + } } impl Interpreter { - /// Create a new `Interpreter` instance with shared cache. - pub fn new(mut params: ActionParams, cache: Arc, schedule: &Schedule, depth: usize) -> Interpreter { - let reader = CodeReader::new(params.code.take().expect("VM always called with code; qed")); - let params = InterpreterParams::from(params); - let informant = informant::EvmInformant::new(depth); - let valid_jump_destinations = None; - let gasometer = Cost::from_u256(params.gas).ok().map(|gas| Gasometer::::new(gas)); - let stack = VecStack::with_capacity(schedule.stack_limit, U256::zero()); - - Interpreter { - cache, params, reader, informant, - valid_jump_destinations, gasometer, stack, - done: false, - do_trace: true, - mem: Vec::new(), - return_data: ReturnData::empty(), - last_stack_ret_len: 0, - resume_output_range: None, - resume_result: None, - _type: PhantomData, - } - } - - /// Execute a single step on the VM. - #[inline(always)] - pub fn step(&mut self, ext: &mut vm::Ext) -> InterpreterResult { - if self.done { - return InterpreterResult::Stopped; - } - - let result = if self.gasometer.is_none() { - InterpreterResult::Done(Err(vm::Error::OutOfGas)) - } else if self.reader.len() == 0 { - InterpreterResult::Done(Ok(GasLeft::Known(self.gasometer.as_ref().expect("Gasometer None case is checked above; qed").current_gas.as_u256()))) - } else { - self.step_inner(ext).err().expect("step_inner never returns Ok(()); qed") - }; - - if let &InterpreterResult::Done(_) = &result { - self.done = true; - self.informant.done(); - } - return result; - } - - /// Inner helper function for step. - #[inline(always)] - fn step_inner(&mut self, ext: &mut vm::Ext) -> Result { - let result = match self.resume_result.take() { - Some(result) => result, - None => { - let opcode = self.reader.code[self.reader.position]; - let instruction = Instruction::from_u8(opcode); - self.reader.position += 1; - - // TODO: make compile-time removable if too much of a performance hit. - self.do_trace = self.do_trace && ext.trace_next_instruction( - self.reader.position - 1, opcode, self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas.as_u256(), - ); - - let instruction = match instruction { - Some(i) => i, - None => return Err(InterpreterResult::Done(Err(vm::Error::BadInstruction { - instruction: opcode - }))), - }; - - let info = instruction.info(); - self.last_stack_ret_len = info.ret; - self.verify_instruction(ext, instruction, info)?; - - // Calculate gas cost - let requirements = self.gasometer.as_mut().expect(GASOMETER_PROOF).requirements(ext, instruction, info, &self.stack, self.mem.size())?; - if self.do_trace { - ext.trace_prepare_execute(self.reader.position - 1, opcode, requirements.gas_cost.as_u256(), Self::mem_written(instruction, &self.stack), Self::store_written(instruction, &self.stack)); - } - - self.gasometer.as_mut().expect(GASOMETER_PROOF).verify_gas(&requirements.gas_cost)?; - self.mem.expand(requirements.memory_required_size); - self.gasometer.as_mut().expect(GASOMETER_PROOF).current_mem_gas = requirements.memory_total_gas; - self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas = self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas - requirements.gas_cost; - - evm_debug!({ self.informant.before_instruction(self.reader.position, instruction, info, &self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas, &self.stack) }); - - // Execute instruction - let current_gas = self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas; - let result = self.exec_instruction( - current_gas, ext, instruction, requirements.provide_gas - )?; - - evm_debug!({ self.informant.after_instruction(instruction) }); - - result - }, - }; - - if let InstructionResult::Trap(trap) = result { - return Err(InterpreterResult::Trap(trap)); - } - - if let InstructionResult::UnusedGas(ref gas) = result { - self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas = self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas + *gas; - } - - if self.do_trace { - ext.trace_executed( - self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas.as_u256(), - self.stack.peek_top(self.last_stack_ret_len), - &self.mem, - ); - } - - // Advance - match result { - InstructionResult::JumpToPosition(position) => { - if self.valid_jump_destinations.is_none() { - self.valid_jump_destinations = Some(self.cache.jump_destinations(&self.params.code_hash, &self.reader.code)); - } - let jump_destinations = self.valid_jump_destinations.as_ref().expect("jump_destinations are initialized on first jump; qed"); - let pos = self.verify_jump(position, jump_destinations)?; - self.reader.position = pos; - }, - InstructionResult::StopExecutionNeedsReturn {gas, init_off, init_size, apply} => { - let mem = mem::replace(&mut self.mem, Vec::new()); - return Err(InterpreterResult::Done(Ok(GasLeft::NeedsReturn { - gas_left: gas.as_u256(), - data: mem.into_return_data(init_off, init_size), - apply_state: apply - }))); - }, - InstructionResult::StopExecution => { - return Err(InterpreterResult::Done(Ok(GasLeft::Known(self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas.as_u256())))); - }, - _ => {}, - } - - if self.reader.position >= self.reader.len() { - return Err(InterpreterResult::Done(Ok(GasLeft::Known(self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas.as_u256())))); - } - - Err(InterpreterResult::Continue) - } - - fn verify_instruction(&self, ext: &vm::Ext, instruction: Instruction, info: &InstructionInfo) -> vm::Result<()> { - let schedule = ext.schedule(); - - if (instruction == instructions::DELEGATECALL && !schedule.have_delegate_call) || - (instruction == instructions::CREATE2 && !schedule.have_create2) || - (instruction == instructions::STATICCALL && !schedule.have_static_call) || - ((instruction == instructions::RETURNDATACOPY || instruction == instructions::RETURNDATASIZE) && !schedule.have_return_data) || - (instruction == instructions::REVERT && !schedule.have_revert) || - ((instruction == instructions::SHL || instruction == instructions::SHR || instruction == instructions::SAR) && !schedule.have_bitwise_shifting) || - (instruction == instructions::EXTCODEHASH && !schedule.have_extcodehash) - { - return Err(vm::Error::BadInstruction { - instruction: instruction as u8 - }); - } - - if !self.stack.has(info.args) { - Err(vm::Error::StackUnderflow { - instruction: info.name, - wanted: info.args, - on_stack: self.stack.size() - }) - } else if self.stack.size() - info.args + info.ret > schedule.stack_limit { - Err(vm::Error::OutOfStack { - instruction: info.name, - wanted: info.ret - info.args, - limit: schedule.stack_limit - }) - } else { - Ok(()) - } - } - - fn mem_written( - instruction: Instruction, - stack: &Stack - ) -> Option<(usize, usize)> { - let read = |pos| stack.peek(pos).low_u64() as usize; - let written = match instruction { - instructions::MSTORE | instructions::MLOAD => Some((read(0), 32)), - instructions::MSTORE8 => Some((read(0), 1)), - instructions::CALLDATACOPY | instructions::CODECOPY | instructions::RETURNDATACOPY => Some((read(0), read(2))), - instructions::EXTCODECOPY => Some((read(1), read(3))), - instructions::CALL | instructions::CALLCODE => Some((read(5), read(6))), - instructions::DELEGATECALL | instructions::STATICCALL => Some((read(4), read(5))), - _ => None, - }; - - match written { - Some((offset, size)) if !memory::is_valid_range(offset, size) => None, - written => written, - } - } - - fn store_written( - instruction: Instruction, - stack: &Stack - ) -> Option<(U256, U256)> { - match instruction { - instructions::SSTORE => Some((stack.peek(0).clone(), stack.peek(1).clone())), - _ => None, - } - } - - fn exec_instruction( - &mut self, - gas: Cost, - ext: &mut vm::Ext, - instruction: Instruction, - provided: Option - ) -> vm::Result> { - match instruction { - instructions::JUMP => { - let jump = self.stack.pop_back(); - return Ok(InstructionResult::JumpToPosition( - jump - )); - }, - instructions::JUMPI => { - let jump = self.stack.pop_back(); - let condition = self.stack.pop_back(); - if !condition.is_zero() { - return Ok(InstructionResult::JumpToPosition( - jump - )); - } - }, - instructions::JUMPDEST => { - // ignore - }, - instructions::CREATE | instructions::CREATE2 => { - let endowment = self.stack.pop_back(); - let init_off = self.stack.pop_back(); - let init_size = self.stack.pop_back(); - let address_scheme = match instruction { - instructions::CREATE => CreateContractAddress::FromSenderAndNonce, - instructions::CREATE2 => CreateContractAddress::FromSenderSaltAndCodeHash(self.stack.pop_back().into()), - _ => unreachable!("instruction can only be CREATE/CREATE2 checked above; qed"), - }; - - let create_gas = provided.expect("`provided` comes through Self::exec from `Gasometer::get_gas_cost_mem`; `gas_gas_mem_cost` guarantees `Some` when instruction is `CALL`/`CALLCODE`/`DELEGATECALL`/`CREATE`; this is `CREATE`; qed"); - - if ext.is_static() { - return Err(vm::Error::MutableCallInStaticContext); - } - - // clear return data buffer before creating new call frame. - self.return_data = ReturnData::empty(); - - let can_create = ext.balance(&self.params.address)? >= endowment && ext.depth() < ext.schedule().max_depth; - if !can_create { - self.stack.push(U256::zero()); - return Ok(InstructionResult::UnusedGas(create_gas)); - } - - let contract_code = self.mem.read_slice(init_off, init_size); - - let create_result = ext.create(&create_gas.as_u256(), &endowment, contract_code, address_scheme, true); - return match create_result { - Ok(ContractCreateResult::Created(address, gas_left)) => { - self.stack.push(address_to_u256(address)); - Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater."))) - }, - Ok(ContractCreateResult::Reverted(gas_left, return_data)) => { - self.stack.push(U256::zero()); - self.return_data = return_data; - Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater."))) - }, - Ok(ContractCreateResult::Failed) => { - self.stack.push(U256::zero()); - Ok(InstructionResult::Ok) - }, - Err(trap) => { - Ok(InstructionResult::Trap(trap)) - }, - }; - }, - instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL | instructions::STATICCALL => { - assert!(ext.schedule().call_value_transfer_gas > ext.schedule().call_stipend, "overflow possible"); - - self.stack.pop_back(); - let call_gas = provided.expect("`provided` comes through Self::exec from `Gasometer::get_gas_cost_mem`; `gas_gas_mem_cost` guarantees `Some` when instruction is `CALL`/`CALLCODE`/`DELEGATECALL`/`CREATE`; this is one of `CALL`/`CALLCODE`/`DELEGATECALL`; qed"); - let code_address = self.stack.pop_back(); - let code_address = u256_to_address(&code_address); - - let value = if instruction == instructions::DELEGATECALL { - None - } else if instruction == instructions::STATICCALL { - Some(U256::zero()) - } else { - Some(self.stack.pop_back()) - }; - - let in_off = self.stack.pop_back(); - let in_size = self.stack.pop_back(); - let out_off = self.stack.pop_back(); - let out_size = self.stack.pop_back(); - - // Add stipend (only CALL|CALLCODE when value > 0) - let call_gas = call_gas.overflow_add(value.map_or_else(|| Cost::from(0), |val| match val.is_zero() { - false => Cost::from(ext.schedule().call_stipend), - true => Cost::from(0), - })).0; - - // Get sender & receive addresses, check if we have balance - let (sender_address, receive_address, has_balance, call_type) = match instruction { - instructions::CALL => { - if ext.is_static() && value.map_or(false, |v| !v.is_zero()) { - return Err(vm::Error::MutableCallInStaticContext); - } - let has_balance = ext.balance(&self.params.address)? >= value.expect("value set for all but delegate call; qed"); - (&self.params.address, &code_address, has_balance, CallType::Call) - }, - instructions::CALLCODE => { - let has_balance = ext.balance(&self.params.address)? >= value.expect("value set for all but delegate call; qed"); - (&self.params.address, &self.params.address, has_balance, CallType::CallCode) - }, - instructions::DELEGATECALL => (&self.params.sender, &self.params.address, true, CallType::DelegateCall), - instructions::STATICCALL => (&self.params.address, &code_address, true, CallType::StaticCall), - _ => panic!(format!("Unexpected instruction {:?} in CALL branch.", instruction)) - }; - - // clear return data buffer before creating new call frame. - self.return_data = ReturnData::empty(); - - let can_call = has_balance && ext.depth() < ext.schedule().max_depth; - if !can_call { - self.stack.push(U256::zero()); - return Ok(InstructionResult::UnusedGas(call_gas)); - } - - let call_result = { - let input = self.mem.read_slice(in_off, in_size); - ext.call(&call_gas.as_u256(), sender_address, receive_address, value, input, &code_address, call_type, true) - }; - - self.resume_output_range = Some((out_off, out_size)); - - return match call_result { - Ok(MessageCallResult::Success(gas_left, data)) => { - let output = self.mem.writeable_slice(out_off, out_size); - let len = cmp::min(output.len(), data.len()); - (&mut output[..len]).copy_from_slice(&data[..len]); - - self.stack.push(U256::one()); - self.return_data = data; - Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater than current one"))) - }, - Ok(MessageCallResult::Reverted(gas_left, data)) => { - let output = self.mem.writeable_slice(out_off, out_size); - let len = cmp::min(output.len(), data.len()); - (&mut output[..len]).copy_from_slice(&data[..len]); - - self.stack.push(U256::zero()); - self.return_data = data; - Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater than current one"))) - }, - Ok(MessageCallResult::Failed) => { - self.stack.push(U256::zero()); - Ok(InstructionResult::Ok) - }, - Err(trap) => { - Ok(InstructionResult::Trap(trap)) - }, - }; - }, - instructions::RETURN => { - let init_off = self.stack.pop_back(); - let init_size = self.stack.pop_back(); - - return Ok(InstructionResult::StopExecutionNeedsReturn {gas: gas, init_off: init_off, init_size: init_size, apply: true}) - }, - instructions::REVERT => { - let init_off = self.stack.pop_back(); - let init_size = self.stack.pop_back(); - - return Ok(InstructionResult::StopExecutionNeedsReturn {gas: gas, init_off: init_off, init_size: init_size, apply: false}) - }, - instructions::STOP => { - return Ok(InstructionResult::StopExecution); - }, - instructions::SUICIDE => { - let address = self.stack.pop_back(); - ext.suicide(&u256_to_address(&address))?; - return Ok(InstructionResult::StopExecution); - }, - instructions::LOG0 | instructions::LOG1 | instructions::LOG2 | instructions::LOG3 | instructions::LOG4 => { - let no_of_topics = instruction.log_topics().expect("log_topics always return some for LOG* instructions; qed"); - - let offset = self.stack.pop_back(); - let size = self.stack.pop_back(); - let topics = self.stack.pop_n(no_of_topics) - .iter() - .map(H256::from) - .collect(); - ext.log(topics, self.mem.read_slice(offset, size))?; - }, - instructions::PUSH1 | instructions::PUSH2 | instructions::PUSH3 | instructions::PUSH4 | - instructions::PUSH5 | instructions::PUSH6 | instructions::PUSH7 | instructions::PUSH8 | - instructions::PUSH9 | instructions::PUSH10 | instructions::PUSH11 | instructions::PUSH12 | - instructions::PUSH13 | instructions::PUSH14 | instructions::PUSH15 | instructions::PUSH16 | - instructions::PUSH17 | instructions::PUSH18 | instructions::PUSH19 | instructions::PUSH20 | - instructions::PUSH21 | instructions::PUSH22 | instructions::PUSH23 | instructions::PUSH24 | - instructions::PUSH25 | instructions::PUSH26 | instructions::PUSH27 | instructions::PUSH28 | - instructions::PUSH29 | instructions::PUSH30 | instructions::PUSH31 | instructions::PUSH32 => { - let bytes = instruction.push_bytes().expect("push_bytes always return some for PUSH* instructions"); - let val = self.reader.read(bytes); - self.stack.push(val); - }, - instructions::MLOAD => { - let word = self.mem.read(self.stack.pop_back()); - self.stack.push(U256::from(word)); - }, - instructions::MSTORE => { - let offset = self.stack.pop_back(); - let word = self.stack.pop_back(); - Memory::write(&mut self.mem, offset, word); - }, - instructions::MSTORE8 => { - let offset = self.stack.pop_back(); - let byte = self.stack.pop_back(); - self.mem.write_byte(offset, byte); - }, - instructions::MSIZE => { - self.stack.push(U256::from(self.mem.size())); - }, - instructions::SHA3 => { - let offset = self.stack.pop_back(); - let size = self.stack.pop_back(); - let k = keccak(self.mem.read_slice(offset, size)); - self.stack.push(U256::from(&*k)); - }, - instructions::SLOAD => { - let key = H256::from(&self.stack.pop_back()); - let word = U256::from(&*ext.storage_at(&key)?); - self.stack.push(word); - }, - instructions::SSTORE => { - let address = H256::from(&self.stack.pop_back()); - let val = self.stack.pop_back(); - - let current_val = U256::from(&*ext.storage_at(&address)?); - // Increase refund for clear - if ext.schedule().eip1283 { - let original_val = U256::from(&*ext.initial_storage_at(&address)?); - gasometer::handle_eip1283_sstore_clears_refund(ext, &original_val, ¤t_val, &val); - } else { - if !current_val.is_zero() && val.is_zero() { - let sstore_clears_schedule = ext.schedule().sstore_refund_gas; - ext.add_sstore_refund(sstore_clears_schedule); - } - } - ext.set_storage(address, H256::from(&val))?; - }, - instructions::PC => { - self.stack.push(U256::from(self.reader.position - 1)); - }, - instructions::GAS => { - self.stack.push(gas.as_u256()); - }, - instructions::ADDRESS => { - self.stack.push(address_to_u256(self.params.address.clone())); - }, - instructions::ORIGIN => { - self.stack.push(address_to_u256(self.params.origin.clone())); - }, - instructions::BALANCE => { - let address = u256_to_address(&self.stack.pop_back()); - let balance = ext.balance(&address)?; - self.stack.push(balance); - }, - instructions::CALLER => { - self.stack.push(address_to_u256(self.params.sender.clone())); - }, - instructions::CALLVALUE => { - self.stack.push(match self.params.value { - ActionValue::Transfer(val) | ActionValue::Apparent(val) => val - }); - }, - instructions::CALLDATALOAD => { - let big_id = self.stack.pop_back(); - let id = big_id.low_u64() as usize; - let max = id.wrapping_add(32); - if let Some(data) = self.params.data.as_ref() { - let bound = cmp::min(data.len(), max); - if id < bound && big_id < U256::from(data.len()) { - let mut v = [0u8; 32]; - v[0..bound-id].clone_from_slice(&data[id..bound]); - self.stack.push(U256::from(&v[..])) - } else { - self.stack.push(U256::zero()) - } - } else { - self.stack.push(U256::zero()) - } - }, - instructions::CALLDATASIZE => { - self.stack.push(U256::from(self.params.data.as_ref().map_or(0, |l| l.len()))); - }, - instructions::CODESIZE => { - self.stack.push(U256::from(self.reader.len())); - }, - instructions::RETURNDATASIZE => { - self.stack.push(U256::from(self.return_data.len())) - }, - instructions::EXTCODESIZE => { - let address = u256_to_address(&self.stack.pop_back()); - let len = ext.extcodesize(&address)?.unwrap_or(0); - self.stack.push(U256::from(len)); - }, - instructions::EXTCODEHASH => { - let address = u256_to_address(&self.stack.pop_back()); - let hash = ext.extcodehash(&address)?.unwrap_or_else(H256::zero); - self.stack.push(U256::from(hash)); - }, - instructions::CALLDATACOPY => { - Self::copy_data_to_memory(&mut self.mem, &mut self.stack, &self.params.data.as_ref().map_or_else(|| &[] as &[u8], |d| &*d as &[u8])); - }, - instructions::RETURNDATACOPY => { - { - let source_offset = self.stack.peek(1); - let size = self.stack.peek(2); - let return_data_len = U256::from(self.return_data.len()); - if source_offset.saturating_add(*size) > return_data_len { - return Err(vm::Error::OutOfBounds); - } - } - Self::copy_data_to_memory(&mut self.mem, &mut self.stack, &*self.return_data); - }, - instructions::CODECOPY => { - Self::copy_data_to_memory(&mut self.mem, &mut self.stack, &self.reader.code); - }, - instructions::EXTCODECOPY => { - let address = u256_to_address(&self.stack.pop_back()); - let code = ext.extcode(&address)?; - Self::copy_data_to_memory( - &mut self.mem, - &mut self.stack, - code.as_ref().map(|c| &(*c)[..]).unwrap_or(&[]) - ); - }, - instructions::GASPRICE => { - self.stack.push(self.params.gas_price.clone()); - }, - instructions::BLOCKHASH => { - let block_number = self.stack.pop_back(); - let block_hash = ext.blockhash(&block_number); - self.stack.push(U256::from(&*block_hash)); - }, - instructions::COINBASE => { - self.stack.push(address_to_u256(ext.env_info().author.clone())); - }, - instructions::TIMESTAMP => { - self.stack.push(U256::from(ext.env_info().timestamp)); - }, - instructions::NUMBER => { - self.stack.push(U256::from(ext.env_info().number)); - }, - instructions::DIFFICULTY => { - self.stack.push(ext.env_info().difficulty.clone()); - }, - instructions::GASLIMIT => { - self.stack.push(ext.env_info().gas_limit.clone()); - }, - - // Stack instructions - - instructions::DUP1 | instructions::DUP2 | instructions::DUP3 | instructions::DUP4 | - instructions::DUP5 | instructions::DUP6 | instructions::DUP7 | instructions::DUP8 | - instructions::DUP9 | instructions::DUP10 | instructions::DUP11 | instructions::DUP12 | - instructions::DUP13 | instructions::DUP14 | instructions::DUP15 | instructions::DUP16 => { - let position = instruction.dup_position().expect("dup_position always return some for DUP* instructions"); - let val = self.stack.peek(position).clone(); - self.stack.push(val); - }, - instructions::SWAP1 | instructions::SWAP2 | instructions::SWAP3 | instructions::SWAP4 | - instructions::SWAP5 | instructions::SWAP6 | instructions::SWAP7 | instructions::SWAP8 | - instructions::SWAP9 | instructions::SWAP10 | instructions::SWAP11 | instructions::SWAP12 | - instructions::SWAP13 | instructions::SWAP14 | instructions::SWAP15 | instructions::SWAP16 => { - let position = instruction.swap_position().expect("swap_position always return some for SWAP* instructions"); - self.stack.swap_with_top(position) - }, - instructions::POP => { - self.stack.pop_back(); - }, - instructions::ADD => { - let a = self.stack.pop_back(); - let b = self.stack.pop_back(); - self.stack.push(a.overflowing_add(b).0); - }, - instructions::MUL => { - let a = self.stack.pop_back(); - let b = self.stack.pop_back(); - self.stack.push(a.overflowing_mul(b).0); - }, - instructions::SUB => { - let a = self.stack.pop_back(); - let b = self.stack.pop_back(); - self.stack.push(a.overflowing_sub(b).0); - }, - instructions::DIV => { - let a = self.stack.pop_back(); - let b = self.stack.pop_back(); - self.stack.push(if !b.is_zero() { - match b { - ONE => a, - TWO => a >> 1, - TWO_POW_5 => a >> 5, - TWO_POW_8 => a >> 8, - TWO_POW_16 => a >> 16, - TWO_POW_24 => a >> 24, - TWO_POW_64 => a >> 64, - TWO_POW_96 => a >> 96, - TWO_POW_224 => a >> 224, - TWO_POW_248 => a >> 248, - _ => a / b, - } - } else { - U256::zero() - }); - }, - instructions::MOD => { - let a = self.stack.pop_back(); - let b = self.stack.pop_back(); - self.stack.push(if !b.is_zero() { - a % b - } else { - U256::zero() - }); - }, - instructions::SDIV => { - let (a, sign_a) = get_and_reset_sign(self.stack.pop_back()); - let (b, sign_b) = get_and_reset_sign(self.stack.pop_back()); - - // -2^255 - let min = (U256::one() << 255) - U256::one(); - self.stack.push(if b.is_zero() { - U256::zero() - } else if a == min && b == !U256::zero() { - min - } else { - let c = a / b; - set_sign(c, sign_a ^ sign_b) - }); - }, - instructions::SMOD => { - let ua = self.stack.pop_back(); - let ub = self.stack.pop_back(); - let (a, sign_a) = get_and_reset_sign(ua); - let b = get_and_reset_sign(ub).0; - - self.stack.push(if !b.is_zero() { - let c = a % b; - set_sign(c, sign_a) - } else { - U256::zero() - }); - }, - instructions::EXP => { - let base = self.stack.pop_back(); - let expon = self.stack.pop_back(); - let res = base.overflowing_pow(expon).0; - self.stack.push(res); - }, - instructions::NOT => { - let a = self.stack.pop_back(); - self.stack.push(!a); - }, - instructions::LT => { - let a = self.stack.pop_back(); - let b = self.stack.pop_back(); - self.stack.push(Self::bool_to_u256(a < b)); - }, - instructions::SLT => { - let (a, neg_a) = get_and_reset_sign(self.stack.pop_back()); - let (b, neg_b) = get_and_reset_sign(self.stack.pop_back()); - - let is_positive_lt = a < b && !(neg_a | neg_b); - let is_negative_lt = a > b && (neg_a & neg_b); - let has_different_signs = neg_a && !neg_b; - - self.stack.push(Self::bool_to_u256(is_positive_lt | is_negative_lt | has_different_signs)); - }, - instructions::GT => { - let a = self.stack.pop_back(); - let b = self.stack.pop_back(); - self.stack.push(Self::bool_to_u256(a > b)); - }, - instructions::SGT => { - let (a, neg_a) = get_and_reset_sign(self.stack.pop_back()); - let (b, neg_b) = get_and_reset_sign(self.stack.pop_back()); - - let is_positive_gt = a > b && !(neg_a | neg_b); - let is_negative_gt = a < b && (neg_a & neg_b); - let has_different_signs = !neg_a && neg_b; - - self.stack.push(Self::bool_to_u256(is_positive_gt | is_negative_gt | has_different_signs)); - }, - instructions::EQ => { - let a = self.stack.pop_back(); - let b = self.stack.pop_back(); - self.stack.push(Self::bool_to_u256(a == b)); - }, - instructions::ISZERO => { - let a = self.stack.pop_back(); - self.stack.push(Self::bool_to_u256(a.is_zero())); - }, - instructions::AND => { - let a = self.stack.pop_back(); - let b = self.stack.pop_back(); - self.stack.push(a & b); - }, - instructions::OR => { - let a = self.stack.pop_back(); - let b = self.stack.pop_back(); - self.stack.push(a | b); - }, - instructions::XOR => { - let a = self.stack.pop_back(); - let b = self.stack.pop_back(); - self.stack.push(a ^ b); - }, - instructions::BYTE => { - let word = self.stack.pop_back(); - let val = self.stack.pop_back(); - let byte = match word < U256::from(32) { - true => (val >> (8 * (31 - word.low_u64() as usize))) & U256::from(0xff), - false => U256::zero() - }; - self.stack.push(byte); - }, - instructions::ADDMOD => { - let a = self.stack.pop_back(); - let b = self.stack.pop_back(); - let c = self.stack.pop_back(); - - self.stack.push(if !c.is_zero() { - // upcast to 512 - let a5 = U512::from(a); - let res = a5.overflowing_add(U512::from(b)).0; - let x = res % U512::from(c); - U256::from(x) - } else { - U256::zero() - }); - }, - instructions::MULMOD => { - let a = self.stack.pop_back(); - let b = self.stack.pop_back(); - let c = self.stack.pop_back(); - - self.stack.push(if !c.is_zero() { - let a5 = U512::from(a); - let res = a5.overflowing_mul(U512::from(b)).0; - let x = res % U512::from(c); - U256::from(x) - } else { - U256::zero() - }); - }, - instructions::SIGNEXTEND => { - let bit = self.stack.pop_back(); - if bit < U256::from(32) { - let number = self.stack.pop_back(); - let bit_position = (bit.low_u64() * 8 + 7) as usize; - - let bit = number.bit(bit_position); - let mask = (U256::one() << bit_position) - U256::one(); - self.stack.push(if bit { - number | !mask - } else { - number & mask - }); - } - }, - instructions::SHL => { - const CONST_256: U256 = U256([256, 0, 0, 0]); - - let shift = self.stack.pop_back(); - let value = self.stack.pop_back(); - - let result = if shift >= CONST_256 { - U256::zero() - } else { - value << (shift.as_u32() as usize) - }; - self.stack.push(result); - }, - instructions::SHR => { - const CONST_256: U256 = U256([256, 0, 0, 0]); - - let shift = self.stack.pop_back(); - let value = self.stack.pop_back(); - - let result = if shift >= CONST_256 { - U256::zero() - } else { - value >> (shift.as_u32() as usize) - }; - self.stack.push(result); - }, - instructions::SAR => { - // We cannot use get_and_reset_sign/set_sign here, because the rounding looks different. - - const CONST_256: U256 = U256([256, 0, 0, 0]); - const CONST_HIBIT: U256 = U256([0, 0, 0, 0x8000000000000000]); - - let shift = self.stack.pop_back(); - let value = self.stack.pop_back(); - let sign = value & CONST_HIBIT != U256::zero(); - - let result = if shift >= CONST_256 { - if sign { - U256::max_value() - } else { - U256::zero() - } - } else { - let shift = shift.as_u32() as usize; - let mut shifted = value >> shift; - if sign { - shifted = shifted | (U256::max_value() << (256 - shift)); - } - shifted - }; - self.stack.push(result); - }, - }; - Ok(InstructionResult::Ok) - } - - fn copy_data_to_memory(mem: &mut Vec, stack: &mut Stack, source: &[u8]) { - let dest_offset = stack.pop_back(); - let source_offset = stack.pop_back(); - let size = stack.pop_back(); - let source_size = U256::from(source.len()); - - let output_end = match source_offset > source_size || size > source_size || source_offset + size > source_size { - true => { - let zero_slice = if source_offset > source_size { - mem.writeable_slice(dest_offset, size) - } else { - mem.writeable_slice(dest_offset + source_size - source_offset, source_offset + size - source_size) - }; - for i in zero_slice.iter_mut() { - *i = 0; - } - source.len() - }, - false => (size.low_u64() + source_offset.low_u64()) as usize - }; - - if source_offset < source_size { - let output_begin = source_offset.low_u64() as usize; - mem.write_slice(dest_offset, &source[output_begin..output_end]); - } - } - - fn verify_jump(&self, jump_u: U256, valid_jump_destinations: &BitSet) -> vm::Result { - let jump = jump_u.low_u64() as usize; - - if valid_jump_destinations.contains(jump) && U256::from(jump) == jump_u { - Ok(jump) - } else { - Err(vm::Error::BadJumpDestination { - destination: jump - }) - } - } - - fn bool_to_u256(val: bool) -> U256 { - if val { - U256::one() - } else { - U256::zero() - } - } + /// Create a new `Interpreter` instance with shared cache. + pub fn new( + mut params: ActionParams, + cache: Arc, + schedule: &Schedule, + depth: usize, + ) -> Interpreter { + let reader = CodeReader::new(params.code.take().expect("VM always called with code; qed")); + let params = InterpreterParams::from(params); + let informant = informant::EvmInformant::new(depth); + let valid_jump_destinations = None; + let valid_subroutine_destinations = None; + let gasometer = Cost::from_u256(params.gas) + .ok() + .map(|gas| Gasometer::::new(gas)); + let stack = VecStack::with_capacity(schedule.stack_limit, U256::zero()); + let return_stack = Vec::with_capacity(MAX_SUB_STACK_SIZE); + + Interpreter { + cache, + params, + reader, + informant, + valid_jump_destinations, + valid_subroutine_destinations, + gasometer, + stack, + return_stack, + done: false, + // Overridden in `step_inner` based on + // the result of `ext.trace_next_instruction`. + do_trace: true, + mem: Vec::new(), + return_data: ReturnData::empty(), + last_stack_ret_len: 0, + resume_output_range: None, + resume_result: None, + _type: PhantomData, + } + } + + /// Execute a single step on the VM. + #[inline(always)] + pub fn step(&mut self, ext: &mut dyn vm::Ext) -> InterpreterResult { + if self.done { + return InterpreterResult::Stopped; + } + + let result = if self.gasometer.is_none() { + InterpreterResult::Done(Err(vm::Error::OutOfGas)) + } else if self.reader.len() == 0 { + let current_gas = self + .gasometer + .as_ref() + .expect("Gasometer None case is checked above; qed") + .current_gas + .as_u256(); + InterpreterResult::Done(Ok(GasLeft::Known(current_gas))) + } else { + self.step_inner(ext) + }; + + if let &InterpreterResult::Done(_) = &result { + self.done = true; + self.informant.done(); + } + result + } + + /// Inner helper function for step. + #[inline(always)] + fn step_inner(&mut self, ext: &mut dyn vm::Ext) -> InterpreterResult { + let result = match self.resume_result.take() { + Some(result) => result, + None => { + let opcode = self.reader.code[self.reader.position]; + let instruction = Instruction::from_u8(opcode); + self.reader.position += 1; + + // TODO: make compile-time removable if too much of a performance hit. + self.do_trace = self.do_trace + && ext.trace_next_instruction( + self.reader.position - 1, + opcode, + self.gasometer + .as_mut() + .expect(GASOMETER_PROOF) + .current_gas + .as_u256(), + ); + + let instruction = match instruction { + Some(i) => i, + None => { + return InterpreterResult::Done(Err(vm::Error::BadInstruction { + instruction: opcode, + })) + } + }; + + let info = instruction.info(); + self.last_stack_ret_len = info.ret; + if let Err(e) = self.verify_instruction(ext, instruction, info) { + return InterpreterResult::Done(Err(e)); + }; + + // Calculate gas cost + let requirements = match self + .gasometer + .as_mut() + .expect(GASOMETER_PROOF) + .requirements(ext, instruction, info, &self.stack, self.mem.size()) + { + Ok(t) => t, + Err(e) => return InterpreterResult::Done(Err(e)), + }; + if self.do_trace { + ext.trace_prepare_execute( + self.reader.position - 1, + opcode, + requirements.gas_cost.as_u256(), + Self::mem_written(instruction, &self.stack), + Self::store_written(instruction, &self.stack), + ); + } + if let Err(e) = self + .gasometer + .as_mut() + .expect(GASOMETER_PROOF) + .verify_gas(&requirements.gas_cost) + { + if self.do_trace { + ext.trace_failed(); + } + return InterpreterResult::Done(Err(e)); + } + self.mem.expand(requirements.memory_required_size); + self.gasometer + .as_mut() + .expect(GASOMETER_PROOF) + .current_mem_gas = requirements.memory_total_gas; + self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas = + self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas + - requirements.gas_cost; + + evm_debug!({ + self.informant.before_instruction( + self.reader.position, + instruction, + info, + &self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas, + &self.stack, + ) + }); + + // Execute instruction + let current_gas = self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas; + let result = match self.exec_instruction( + current_gas, + ext, + instruction, + requirements.provide_gas, + ) { + Err(x) => { + if self.do_trace { + ext.trace_failed(); + } + return InterpreterResult::Done(Err(x)); + } + Ok(x) => x, + }; + evm_debug!({ self.informant.after_instruction(instruction) }); + result + } + }; + + if let InstructionResult::Trap(trap) = result { + return InterpreterResult::Trap(trap); + } + + if let InstructionResult::UnusedGas(ref gas) = result { + self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas = + self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas + *gas; + } + + if self.do_trace { + ext.trace_executed( + self.gasometer + .as_mut() + .expect(GASOMETER_PROOF) + .current_gas + .as_u256(), + self.stack.peek_top(self.last_stack_ret_len), + &self.mem, + ); + } + + // Advance + match result { + InstructionResult::JumpToPosition(position) => { + if self.valid_jump_destinations.is_none() { + self.valid_jump_destinations = Some( + self.cache + .jump_and_sub_destinations(&self.params.code_hash, &self.reader.code) + .0, + ); + } + let jump_destinations = self + .valid_jump_destinations + .as_ref() + .expect("jump_destinations are initialized on first jump; qed"); + let pos = match self.verify_jump(position, jump_destinations) { + Ok(x) => x, + Err(e) => return InterpreterResult::Done(Err(e)), + }; + self.reader.position = pos; + } + InstructionResult::JumpToSubroutine(position) => { + if self.valid_subroutine_destinations.is_none() { + self.valid_subroutine_destinations = Some( + self.cache + .jump_and_sub_destinations(&self.params.code_hash, &self.reader.code) + .1, + ); + } + let subroutine_destinations = self + .valid_subroutine_destinations + .as_ref() + .expect("subroutine_destinations are initialized on first jump; qed"); + let pos = match self.verify_jump(position, subroutine_destinations) { + Ok(x) => x, + Err(e) => return InterpreterResult::Done(Err(e)), + }; + self.return_stack.push(self.reader.position); + self.reader.position = pos + 1; + } + InstructionResult::ReturnFromSubroutine(pos) => { + self.reader.position = pos; + } + InstructionResult::StopExecutionNeedsReturn { + gas, + init_off, + init_size, + apply, + } => { + let mem = mem::replace(&mut self.mem, Vec::new()); + return InterpreterResult::Done(Ok(GasLeft::NeedsReturn { + gas_left: gas.as_u256(), + data: mem.into_return_data(init_off, init_size), + apply_state: apply, + })); + } + InstructionResult::StopExecution => { + return InterpreterResult::Done(Ok(GasLeft::Known( + self.gasometer + .as_mut() + .expect(GASOMETER_PROOF) + .current_gas + .as_u256(), + ))); + } + _ => {} + } + + if self.reader.position >= self.reader.len() { + return InterpreterResult::Done(Ok(GasLeft::Known( + self.gasometer + .as_mut() + .expect(GASOMETER_PROOF) + .current_gas + .as_u256(), + ))); + } + + InterpreterResult::Continue + } + + fn verify_instruction( + &self, + ext: &dyn vm::Ext, + instruction: Instruction, + info: &InstructionInfo, + ) -> vm::Result<()> { + let schedule = ext.schedule(); + + use instructions::*; + if (instruction == DELEGATECALL && !schedule.have_delegate_call) + || (instruction == CREATE2 && !schedule.have_create2) + || (instruction == STATICCALL && !schedule.have_static_call) + || ((instruction == RETURNDATACOPY || instruction == RETURNDATASIZE) + && !schedule.have_return_data) + || (instruction == REVERT && !schedule.have_revert) + || ((instruction == SHL || instruction == SHR || instruction == SAR) + && !schedule.have_bitwise_shifting) + || (instruction == EXTCODEHASH && !schedule.have_extcodehash) + || (instruction == CHAINID && !schedule.have_chain_id) + || (instruction == SELFBALANCE && !schedule.have_selfbalance) + || ((instruction == BEGINSUB || instruction == JUMPSUB || instruction == RETURNSUB) + && !schedule.have_subs) + { + return Err(vm::Error::BadInstruction { + instruction: instruction as u8, + }); + } + + if !self.stack.has(info.args) { + Err(vm::Error::StackUnderflow { + instruction: info.name, + wanted: info.args, + on_stack: self.stack.size(), + }) + } else if self.stack.size() - info.args + info.ret > schedule.stack_limit { + Err(vm::Error::OutOfStack { + instruction: info.name, + wanted: info.ret - info.args, + limit: schedule.stack_limit, + }) + } else { + Ok(()) + } + } + + fn mem_written(instruction: Instruction, stack: &dyn Stack) -> Option<(usize, usize)> { + let read = |pos| stack.peek(pos).low_u64() as usize; + let written = match instruction { + instructions::MSTORE | instructions::MLOAD => Some((read(0), 32)), + instructions::MSTORE8 => Some((read(0), 1)), + instructions::CALLDATACOPY | instructions::CODECOPY | instructions::RETURNDATACOPY => { + Some((read(0), read(2))) + } + instructions::EXTCODECOPY => Some((read(1), read(3))), + instructions::CALL | instructions::CALLCODE => Some((read(5), read(6))), + instructions::DELEGATECALL | instructions::STATICCALL => Some((read(4), read(5))), + _ => None, + }; + + match written { + Some((offset, size)) if !memory::is_valid_range(offset, size) => None, + written => written, + } + } + + fn store_written(instruction: Instruction, stack: &dyn Stack) -> Option<(U256, U256)> { + match instruction { + instructions::SSTORE => Some((stack.peek(0).clone(), stack.peek(1).clone())), + _ => None, + } + } + + fn exec_instruction( + &mut self, + gas: Cost, + ext: &mut dyn vm::Ext, + instruction: Instruction, + provided: Option, + ) -> vm::Result> { + match instruction { + instructions::JUMP => { + let jump = self.stack.pop_back(); + return Ok(InstructionResult::JumpToPosition(jump)); + } + instructions::JUMPI => { + let jump = self.stack.pop_back(); + let condition = self.stack.pop_back(); + if !condition.is_zero() { + return Ok(InstructionResult::JumpToPosition(jump)); + } + } + instructions::JUMPDEST => { + // ignore + } + instructions::BEGINSUB => { + return Err(vm::Error::InvalidSubEntry); + } + instructions::JUMPSUB => { + if self.return_stack.len() >= MAX_SUB_STACK_SIZE { + return Err(vm::Error::OutOfSubStack { + wanted: 1, + limit: MAX_SUB_STACK_SIZE, + }); + } + let sub_destination = self.stack.pop_back(); + return Ok(InstructionResult::JumpToSubroutine(sub_destination)); + } + instructions::RETURNSUB => { + if let Some(pos) = self.return_stack.pop() { + return Ok(InstructionResult::ReturnFromSubroutine(pos)); + } else { + return Err(vm::Error::SubStackUnderflow { + wanted: 1, + on_stack: 0, + }); + } + } + instructions::CREATE | instructions::CREATE2 => { + let endowment = self.stack.pop_back(); + let init_off = self.stack.pop_back(); + let init_size = self.stack.pop_back(); + let address_scheme = match instruction { + instructions::CREATE => CreateContractAddress::FromSenderAndNonce, + instructions::CREATE2 => CreateContractAddress::FromSenderSaltAndCodeHash( + self.stack.pop_back().into(), + ), + _ => unreachable!("instruction can only be CREATE/CREATE2 checked above; qed"), + }; + + let create_gas = provided.expect("`provided` comes through Self::exec from `Gasometer::get_gas_cost_mem`; `gas_gas_mem_cost` guarantees `Some` when instruction is `CALL`/`CALLCODE`/`DELEGATECALL`/`CREATE`; this is `CREATE`; qed"); + + if ext.is_static() { + return Err(vm::Error::MutableCallInStaticContext); + } + + // clear return data buffer before creating new call frame. + self.return_data = ReturnData::empty(); + + let can_create = ext.balance(&self.params.address)? >= endowment + && ext.depth() < ext.schedule().max_depth; + if !can_create { + self.stack.push(U256::zero()); + return Ok(InstructionResult::UnusedGas(create_gas)); + } + + let contract_code = self.mem.read_slice(init_off, init_size); + + let create_result = ext.create( + &create_gas.as_u256(), + &endowment, + contract_code, + address_scheme, + true, + ); + return match create_result { + Ok(ContractCreateResult::Created(address, gas_left)) => { + self.stack.push(address_to_u256(address)); + Ok(InstructionResult::UnusedGas( + Cost::from_u256(gas_left).expect("Gas left cannot be greater."), + )) + } + Ok(ContractCreateResult::Reverted(gas_left, return_data)) => { + self.stack.push(U256::zero()); + self.return_data = return_data; + Ok(InstructionResult::UnusedGas( + Cost::from_u256(gas_left).expect("Gas left cannot be greater."), + )) + } + Ok(ContractCreateResult::Failed) => { + self.stack.push(U256::zero()); + Ok(InstructionResult::Ok) + } + Err(trap) => Ok(InstructionResult::Trap(trap)), + }; + } + instructions::CALL + | instructions::CALLCODE + | instructions::DELEGATECALL + | instructions::STATICCALL => { + assert!( + ext.schedule().call_value_transfer_gas > ext.schedule().call_stipend, + "overflow possible" + ); + + self.stack.pop_back(); + let call_gas = provided.expect("`provided` comes through Self::exec from `Gasometer::get_gas_cost_mem`; `gas_gas_mem_cost` guarantees `Some` when instruction is `CALL`/`CALLCODE`/`DELEGATECALL`/`CREATE`; this is one of `CALL`/`CALLCODE`/`DELEGATECALL`; qed"); + let code_address = self.stack.pop_back(); + let code_address = u256_to_address(&code_address); + + let value = if instruction == instructions::DELEGATECALL { + None + } else if instruction == instructions::STATICCALL { + Some(U256::zero()) + } else { + Some(self.stack.pop_back()) + }; + + let in_off = self.stack.pop_back(); + let in_size = self.stack.pop_back(); + let out_off = self.stack.pop_back(); + let out_size = self.stack.pop_back(); + + // Add stipend (only CALL|CALLCODE when value > 0) + let call_gas = call_gas + .overflow_add(value.map_or_else( + || Cost::from(0), + |val| match val.is_zero() { + false => Cost::from(ext.schedule().call_stipend), + true => Cost::from(0), + }, + )) + .0; + + // Get sender & receive addresses, check if we have balance + let (sender_address, receive_address, has_balance, call_type) = match instruction { + instructions::CALL => { + if ext.is_static() && value.map_or(false, |v| !v.is_zero()) { + return Err(vm::Error::MutableCallInStaticContext); + } + let has_balance = ext.balance(&self.params.address)? + >= value.expect("value set for all but delegate call; qed"); + ( + &self.params.address, + &code_address, + has_balance, + CallType::Call, + ) + } + instructions::CALLCODE => { + let has_balance = ext.balance(&self.params.address)? + >= value.expect("value set for all but delegate call; qed"); + ( + &self.params.address, + &self.params.address, + has_balance, + CallType::CallCode, + ) + } + instructions::DELEGATECALL => ( + &self.params.sender, + &self.params.address, + true, + CallType::DelegateCall, + ), + instructions::STATICCALL => ( + &self.params.address, + &code_address, + true, + CallType::StaticCall, + ), + _ => panic!(format!( + "Unexpected instruction {:?} in CALL branch.", + instruction + )), + }; + + // clear return data buffer before creating new call frame. + self.return_data = ReturnData::empty(); + + let can_call = has_balance && ext.depth() < ext.schedule().max_depth; + if !can_call { + self.stack.push(U256::zero()); + return Ok(InstructionResult::UnusedGas(call_gas)); + } + + let call_result = { + let input = self.mem.read_slice(in_off, in_size); + ext.call( + &call_gas.as_u256(), + sender_address, + receive_address, + value, + input, + &code_address, + call_type, + true, + ) + }; + + self.resume_output_range = Some((out_off, out_size)); + + return match call_result { + Ok(MessageCallResult::Success(gas_left, data)) => { + let output = self.mem.writeable_slice(out_off, out_size); + let len = cmp::min(output.len(), data.len()); + (&mut output[..len]).copy_from_slice(&data[..len]); + + self.stack.push(U256::one()); + self.return_data = data; + Ok(InstructionResult::UnusedGas( + Cost::from_u256(gas_left) + .expect("Gas left cannot be greater than current one"), + )) + } + Ok(MessageCallResult::Reverted(gas_left, data)) => { + let output = self.mem.writeable_slice(out_off, out_size); + let len = cmp::min(output.len(), data.len()); + (&mut output[..len]).copy_from_slice(&data[..len]); + + self.stack.push(U256::zero()); + self.return_data = data; + Ok(InstructionResult::UnusedGas( + Cost::from_u256(gas_left) + .expect("Gas left cannot be greater than current one"), + )) + } + Ok(MessageCallResult::Failed) => { + self.stack.push(U256::zero()); + Ok(InstructionResult::Ok) + } + Err(trap) => Ok(InstructionResult::Trap(trap)), + }; + } + instructions::RETURN => { + let init_off = self.stack.pop_back(); + let init_size = self.stack.pop_back(); + + return Ok(InstructionResult::StopExecutionNeedsReturn { + gas: gas, + init_off: init_off, + init_size: init_size, + apply: true, + }); + } + instructions::REVERT => { + let init_off = self.stack.pop_back(); + let init_size = self.stack.pop_back(); + + return Ok(InstructionResult::StopExecutionNeedsReturn { + gas: gas, + init_off: init_off, + init_size: init_size, + apply: false, + }); + } + instructions::STOP => { + return Ok(InstructionResult::StopExecution); + } + instructions::SUICIDE => { + let address = self.stack.pop_back(); + ext.suicide(&u256_to_address(&address))?; + return Ok(InstructionResult::StopExecution); + } + instructions::LOG0 + | instructions::LOG1 + | instructions::LOG2 + | instructions::LOG3 + | instructions::LOG4 => { + let no_of_topics = instruction + .log_topics() + .expect("log_topics always return some for LOG* instructions; qed"); + + let offset = self.stack.pop_back(); + let size = self.stack.pop_back(); + let topics = self + .stack + .pop_n(no_of_topics) + .iter() + .map(H256::from) + .collect(); + ext.log(topics, self.mem.read_slice(offset, size))?; + } + instructions::PUSH1 + | instructions::PUSH2 + | instructions::PUSH3 + | instructions::PUSH4 + | instructions::PUSH5 + | instructions::PUSH6 + | instructions::PUSH7 + | instructions::PUSH8 + | instructions::PUSH9 + | instructions::PUSH10 + | instructions::PUSH11 + | instructions::PUSH12 + | instructions::PUSH13 + | instructions::PUSH14 + | instructions::PUSH15 + | instructions::PUSH16 + | instructions::PUSH17 + | instructions::PUSH18 + | instructions::PUSH19 + | instructions::PUSH20 + | instructions::PUSH21 + | instructions::PUSH22 + | instructions::PUSH23 + | instructions::PUSH24 + | instructions::PUSH25 + | instructions::PUSH26 + | instructions::PUSH27 + | instructions::PUSH28 + | instructions::PUSH29 + | instructions::PUSH30 + | instructions::PUSH31 + | instructions::PUSH32 => { + let bytes = instruction + .push_bytes() + .expect("push_bytes always return some for PUSH* instructions"); + let val = self.reader.read(bytes); + self.stack.push(val); + } + instructions::MLOAD => { + let word = self.mem.read(self.stack.pop_back()); + self.stack.push(U256::from(word)); + } + instructions::MSTORE => { + let offset = self.stack.pop_back(); + let word = self.stack.pop_back(); + Memory::write(&mut self.mem, offset, word); + } + instructions::MSTORE8 => { + let offset = self.stack.pop_back(); + let byte = self.stack.pop_back(); + self.mem.write_byte(offset, byte); + } + instructions::MSIZE => { + self.stack.push(U256::from(self.mem.size())); + } + instructions::SHA3 => { + let offset = self.stack.pop_back(); + let size = self.stack.pop_back(); + let k = keccak(self.mem.read_slice(offset, size)); + self.stack.push(U256::from(&*k)); + } + instructions::SLOAD => { + let key = H256::from(&self.stack.pop_back()); + let word = U256::from(&*ext.storage_at(&key)?); + self.stack.push(word); + } + instructions::SSTORE => { + let address = H256::from(&self.stack.pop_back()); + let val = self.stack.pop_back(); + + let current_val = U256::from(&*ext.storage_at(&address)?); + // Increase refund for clear + if ext.schedule().eip1283 { + let original_val = U256::from(&*ext.initial_storage_at(&address)?); + gasometer::handle_eip1283_sstore_clears_refund( + ext, + &original_val, + ¤t_val, + &val, + ); + } else { + if !current_val.is_zero() && val.is_zero() { + let sstore_clears_schedule = ext.schedule().sstore_refund_gas; + ext.add_sstore_refund(sstore_clears_schedule); + } + } + ext.set_storage(address, H256::from(&val))?; + } + instructions::PC => { + self.stack.push(U256::from(self.reader.position - 1)); + } + instructions::GAS => { + self.stack.push(gas.as_u256()); + } + instructions::ADDRESS => { + self.stack + .push(address_to_u256(self.params.address.clone())); + } + instructions::ORIGIN => { + self.stack.push(address_to_u256(self.params.origin.clone())); + } + instructions::BALANCE => { + let address = u256_to_address(&self.stack.pop_back()); + let balance = ext.balance(&address)?; + self.stack.push(balance); + } + instructions::CALLER => { + self.stack.push(address_to_u256(self.params.sender.clone())); + } + instructions::CALLVALUE => { + self.stack.push(match self.params.value { + ActionValue::Transfer(val) | ActionValue::Apparent(val) => val, + }); + } + instructions::CALLDATALOAD => { + let big_id = self.stack.pop_back(); + let id = big_id.low_u64() as usize; + let max = id.wrapping_add(32); + if let Some(data) = self.params.data.as_ref() { + let bound = cmp::min(data.len(), max); + if id < bound && big_id < U256::from(data.len()) { + let mut v = [0u8; 32]; + v[0..bound - id].clone_from_slice(&data[id..bound]); + self.stack.push(U256::from(&v[..])) + } else { + self.stack.push(U256::zero()) + } + } else { + self.stack.push(U256::zero()) + } + } + instructions::CALLDATASIZE => { + self.stack + .push(U256::from(self.params.data.as_ref().map_or(0, |l| l.len()))); + } + instructions::CODESIZE => { + self.stack.push(U256::from(self.reader.len())); + } + instructions::RETURNDATASIZE => self.stack.push(U256::from(self.return_data.len())), + instructions::EXTCODESIZE => { + let address = u256_to_address(&self.stack.pop_back()); + let len = ext.extcodesize(&address)?.unwrap_or(0); + self.stack.push(U256::from(len)); + } + instructions::EXTCODEHASH => { + let address = u256_to_address(&self.stack.pop_back()); + let hash = ext.extcodehash(&address)?.unwrap_or_else(H256::zero); + self.stack.push(U256::from(hash)); + } + instructions::CALLDATACOPY => { + Self::copy_data_to_memory( + &mut self.mem, + &mut self.stack, + &self + .params + .data + .as_ref() + .map_or_else(|| &[] as &[u8], |d| &*d as &[u8]), + ); + } + instructions::RETURNDATACOPY => { + { + let source_offset = self.stack.peek(1); + let size = self.stack.peek(2); + let return_data_len = U256::from(self.return_data.len()); + if source_offset.saturating_add(*size) > return_data_len { + return Err(vm::Error::OutOfBounds); + } + } + Self::copy_data_to_memory(&mut self.mem, &mut self.stack, &*self.return_data); + } + instructions::CODECOPY => { + Self::copy_data_to_memory(&mut self.mem, &mut self.stack, &self.reader.code); + } + instructions::EXTCODECOPY => { + let address = u256_to_address(&self.stack.pop_back()); + let code = ext.extcode(&address)?; + Self::copy_data_to_memory( + &mut self.mem, + &mut self.stack, + code.as_ref().map(|c| &(*c)[..]).unwrap_or(&[]), + ); + } + instructions::GASPRICE => { + self.stack.push(self.params.gas_price.clone()); + } + instructions::BLOCKHASH => { + let block_number = self.stack.pop_back(); + let block_hash = ext.blockhash(&block_number); + self.stack.push(U256::from(&*block_hash)); + } + instructions::COINBASE => { + self.stack + .push(address_to_u256(ext.env_info().author.clone())); + } + instructions::TIMESTAMP => { + self.stack.push(U256::from(ext.env_info().timestamp)); + } + instructions::NUMBER => { + self.stack.push(U256::from(ext.env_info().number)); + } + instructions::DIFFICULTY => { + self.stack.push(ext.env_info().difficulty.clone()); + } + instructions::GASLIMIT => { + self.stack.push(ext.env_info().gas_limit.clone()); + } + instructions::CHAINID => self.stack.push(ext.chain_id().into()), + instructions::SELFBALANCE => { + self.stack.push(ext.balance(&self.params.address)?); + } + + // Stack instructions + instructions::DUP1 + | instructions::DUP2 + | instructions::DUP3 + | instructions::DUP4 + | instructions::DUP5 + | instructions::DUP6 + | instructions::DUP7 + | instructions::DUP8 + | instructions::DUP9 + | instructions::DUP10 + | instructions::DUP11 + | instructions::DUP12 + | instructions::DUP13 + | instructions::DUP14 + | instructions::DUP15 + | instructions::DUP16 => { + let position = instruction + .dup_position() + .expect("dup_position always return some for DUP* instructions"); + let val = self.stack.peek(position).clone(); + self.stack.push(val); + } + instructions::SWAP1 + | instructions::SWAP2 + | instructions::SWAP3 + | instructions::SWAP4 + | instructions::SWAP5 + | instructions::SWAP6 + | instructions::SWAP7 + | instructions::SWAP8 + | instructions::SWAP9 + | instructions::SWAP10 + | instructions::SWAP11 + | instructions::SWAP12 + | instructions::SWAP13 + | instructions::SWAP14 + | instructions::SWAP15 + | instructions::SWAP16 => { + let position = instruction + .swap_position() + .expect("swap_position always return some for SWAP* instructions"); + self.stack.swap_with_top(position) + } + instructions::POP => { + self.stack.pop_back(); + } + instructions::ADD => { + let a = self.stack.pop_back(); + let b = self.stack.pop_back(); + self.stack.push(a.overflowing_add(b).0); + } + instructions::MUL => { + let a = self.stack.pop_back(); + let b = self.stack.pop_back(); + self.stack.push(a.overflowing_mul(b).0); + } + instructions::SUB => { + let a = self.stack.pop_back(); + let b = self.stack.pop_back(); + self.stack.push(a.overflowing_sub(b).0); + } + instructions::DIV => { + let a = self.stack.pop_back(); + let b = self.stack.pop_back(); + self.stack.push(if !b.is_zero() { + match b { + ONE => a, + TWO => a >> 1, + TWO_POW_5 => a >> 5, + TWO_POW_8 => a >> 8, + TWO_POW_16 => a >> 16, + TWO_POW_24 => a >> 24, + TWO_POW_64 => a >> 64, + TWO_POW_96 => a >> 96, + TWO_POW_224 => a >> 224, + TWO_POW_248 => a >> 248, + _ => a / b, + } + } else { + U256::zero() + }); + } + instructions::MOD => { + let a = self.stack.pop_back(); + let b = self.stack.pop_back(); + self.stack + .push(if !b.is_zero() { a % b } else { U256::zero() }); + } + instructions::SDIV => { + let (a, sign_a) = get_and_reset_sign(self.stack.pop_back()); + let (b, sign_b) = get_and_reset_sign(self.stack.pop_back()); + + // -2^255 + let min = (U256::one() << 255) - U256::one(); + self.stack.push(if b.is_zero() { + U256::zero() + } else if a == min && b == !U256::zero() { + min + } else { + let c = a / b; + set_sign(c, sign_a ^ sign_b) + }); + } + instructions::SMOD => { + let ua = self.stack.pop_back(); + let ub = self.stack.pop_back(); + let (a, sign_a) = get_and_reset_sign(ua); + let b = get_and_reset_sign(ub).0; + + self.stack.push(if !b.is_zero() { + let c = a % b; + set_sign(c, sign_a) + } else { + U256::zero() + }); + } + instructions::EXP => { + let base = self.stack.pop_back(); + let expon = self.stack.pop_back(); + let res = base.overflowing_pow(expon).0; + self.stack.push(res); + } + instructions::NOT => { + let a = self.stack.pop_back(); + self.stack.push(!a); + } + instructions::LT => { + let a = self.stack.pop_back(); + let b = self.stack.pop_back(); + self.stack.push(Self::bool_to_u256(a < b)); + } + instructions::SLT => { + let (a, neg_a) = get_and_reset_sign(self.stack.pop_back()); + let (b, neg_b) = get_and_reset_sign(self.stack.pop_back()); + + let is_positive_lt = a < b && !(neg_a | neg_b); + let is_negative_lt = a > b && (neg_a & neg_b); + let has_different_signs = neg_a && !neg_b; + + self.stack.push(Self::bool_to_u256( + is_positive_lt | is_negative_lt | has_different_signs, + )); + } + instructions::GT => { + let a = self.stack.pop_back(); + let b = self.stack.pop_back(); + self.stack.push(Self::bool_to_u256(a > b)); + } + instructions::SGT => { + let (a, neg_a) = get_and_reset_sign(self.stack.pop_back()); + let (b, neg_b) = get_and_reset_sign(self.stack.pop_back()); + + let is_positive_gt = a > b && !(neg_a | neg_b); + let is_negative_gt = a < b && (neg_a & neg_b); + let has_different_signs = !neg_a && neg_b; + + self.stack.push(Self::bool_to_u256( + is_positive_gt | is_negative_gt | has_different_signs, + )); + } + instructions::EQ => { + let a = self.stack.pop_back(); + let b = self.stack.pop_back(); + self.stack.push(Self::bool_to_u256(a == b)); + } + instructions::ISZERO => { + let a = self.stack.pop_back(); + self.stack.push(Self::bool_to_u256(a.is_zero())); + } + instructions::AND => { + let a = self.stack.pop_back(); + let b = self.stack.pop_back(); + self.stack.push(a & b); + } + instructions::OR => { + let a = self.stack.pop_back(); + let b = self.stack.pop_back(); + self.stack.push(a | b); + } + instructions::XOR => { + let a = self.stack.pop_back(); + let b = self.stack.pop_back(); + self.stack.push(a ^ b); + } + instructions::BYTE => { + let word = self.stack.pop_back(); + let val = self.stack.pop_back(); + let byte = match word < U256::from(32) { + true => (val >> (8 * (31 - word.low_u64() as usize))) & U256::from(0xff), + false => U256::zero(), + }; + self.stack.push(byte); + } + instructions::ADDMOD => { + let a = self.stack.pop_back(); + let b = self.stack.pop_back(); + let c = self.stack.pop_back(); + + self.stack.push(if !c.is_zero() { + let a_num = to_biguint(a); + let b_num = to_biguint(b); + let c_num = to_biguint(c); + let res = a_num + b_num; + let x = res % c_num; + from_biguint(x) + } else { + U256::zero() + }); + } + instructions::MULMOD => { + let a = self.stack.pop_back(); + let b = self.stack.pop_back(); + let c = self.stack.pop_back(); + + self.stack.push(if !c.is_zero() { + let a_num = to_biguint(a); + let b_num = to_biguint(b); + let c_num = to_biguint(c); + let res = a_num * b_num; + let x = res % c_num; + from_biguint(x) + } else { + U256::zero() + }); + } + instructions::SIGNEXTEND => { + let bit = self.stack.pop_back(); + if bit < U256::from(32) { + let number = self.stack.pop_back(); + let bit_position = (bit.low_u64() * 8 + 7) as usize; + + let bit = number.bit(bit_position); + let mask = (U256::one() << bit_position) - U256::one(); + self.stack + .push(if bit { number | !mask } else { number & mask }); + } + } + instructions::SHL => { + const CONST_256: U256 = U256([256, 0, 0, 0]); + + let shift = self.stack.pop_back(); + let value = self.stack.pop_back(); + + let result = if shift >= CONST_256 { + U256::zero() + } else { + value << (shift.as_u32() as usize) + }; + self.stack.push(result); + } + instructions::SHR => { + const CONST_256: U256 = U256([256, 0, 0, 0]); + + let shift = self.stack.pop_back(); + let value = self.stack.pop_back(); + + let result = if shift >= CONST_256 { + U256::zero() + } else { + value >> (shift.as_u32() as usize) + }; + self.stack.push(result); + } + instructions::SAR => { + // We cannot use get_and_reset_sign/set_sign here, because the rounding looks different. + + const CONST_256: U256 = U256([256, 0, 0, 0]); + const CONST_HIBIT: U256 = U256([0, 0, 0, 0x8000000000000000]); + + let shift = self.stack.pop_back(); + let value = self.stack.pop_back(); + let sign = value & CONST_HIBIT != U256::zero(); + + let result = if shift >= CONST_256 { + if sign { + U256::max_value() + } else { + U256::zero() + } + } else { + let shift = shift.as_u32() as usize; + let mut shifted = value >> shift; + if sign { + shifted = shifted | (U256::max_value() << (256 - shift)); + } + shifted + }; + self.stack.push(result); + } + }; + Ok(InstructionResult::Ok) + } + + fn copy_data_to_memory(mem: &mut Vec, stack: &mut dyn Stack, source: &[u8]) { + let dest_offset = stack.pop_back(); + let source_offset = stack.pop_back(); + let size = stack.pop_back(); + let source_size = U256::from(source.len()); + + let output_end = match source_offset > source_size + || size > source_size + || source_offset + size > source_size + { + true => { + let zero_slice = if source_offset > source_size { + mem.writeable_slice(dest_offset, size) + } else { + mem.writeable_slice( + dest_offset + source_size - source_offset, + source_offset + size - source_size, + ) + }; + for i in zero_slice.iter_mut() { + *i = 0; + } + source.len() + } + false => (size.low_u64() + source_offset.low_u64()) as usize, + }; + + if source_offset < source_size { + let output_begin = source_offset.low_u64() as usize; + mem.write_slice(dest_offset, &source[output_begin..output_end]); + } + } + + fn verify_jump(&self, jump_u: U256, valid_jump_destinations: &BitSet) -> vm::Result { + let jump = jump_u.low_u64() as usize; + + if valid_jump_destinations.contains(jump) && U256::from(jump) == jump_u { + Ok(jump) + } else { + // Note: if jump > usize, BadJumpDestination value is trimmed + Err(vm::Error::BadJumpDestination { destination: jump }) + } + } + + fn bool_to_u256(val: bool) -> U256 { + if val { + U256::one() + } else { + U256::zero() + } + } } fn get_and_reset_sign(value: U256) -> (U256, bool) { - let U256(arr) = value; - let sign = arr[3].leading_zeros() == 0; - (set_sign(value, sign), sign) + let U256(arr) = value; + let sign = arr[3].leading_zeros() == 0; + (set_sign(value, sign), sign) } fn set_sign(value: U256, sign: bool) -> U256 { - if sign { - (!U256::zero() ^ value).overflowing_add(U256::one()).0 - } else { - value - } + if sign { + (!U256::zero() ^ value).overflowing_add(U256::one()).0 + } else { + value + } } #[inline] fn u256_to_address(value: &U256) -> Address { - Address::from(H256::from(value)) + Address::from(H256::from(value)) } #[inline] fn address_to_u256(value: Address) -> U256 { - U256::from(&*H256::from(value)) + U256::from(&*H256::from(value)) } #[cfg(test)] mod tests { - use std::sync::Arc; - use rustc_hex::FromHex; - use vmtype::VMType; - use factory::Factory; - use vm::{self, Exec, ActionParams, ActionValue}; - use vm::tests::{FakeExt, test_finalize}; - - fn interpreter(params: ActionParams, ext: &vm::Ext) -> Box { - Factory::new(VMType::Interpreter, 1).create(params, ext.schedule(), ext.depth()) - } - - #[test] - fn should_not_fail_on_tracing_mem() { - let code = "7feeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006000527faaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa6020526000620f120660406000601773945304eb96065b2a98b57a48a06ae28d285a71b56101f4f1600055".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.address = 5.into(); - params.gas = 300_000.into(); - params.gas_price = 1.into(); - params.value = ActionValue::Transfer(100_000.into()); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - ext.balances.insert(5.into(), 1_000_000_000.into()); - ext.tracing = true; - - let gas_left = { - let mut vm = interpreter(params, &ext); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_eq!(ext.calls.len(), 1); - assert_eq!(gas_left, 248_212.into()); - } - - #[test] - fn should_not_overflow_returndata() { - let code = "6001600160000360003e00".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.address = 5.into(); - params.gas = 300_000.into(); - params.gas_price = 1.into(); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new_byzantium(); - ext.balances.insert(5.into(), 1_000_000_000.into()); - ext.tracing = true; - - let err = { - let mut vm = interpreter(params, &ext); - test_finalize(vm.exec(&mut ext).ok().unwrap()).err().unwrap() - }; - - assert_eq!(err, ::vm::Error::OutOfBounds); - } + use factory::Factory; + use rustc_hex::FromHex; + use std::sync::Arc; + use vm::{ + self, + tests::{test_finalize, FakeExt}, + ActionParams, ActionValue, Exec, + }; + use vmtype::VMType; + + fn interpreter(params: ActionParams, ext: &dyn vm::Ext) -> Box { + Factory::new(VMType::Interpreter, 1).create(params, ext.schedule(), ext.depth()) + } + + #[test] + fn should_not_fail_on_tracing_mem() { + let code = "7feeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006000527faaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa6020526000620f120660406000601773945304eb96065b2a98b57a48a06ae28d285a71b56101f4f1600055".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.address = 5.into(); + params.gas = 300_000.into(); + params.gas_price = 1.into(); + params.value = ActionValue::Transfer(100_000.into()); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + ext.balances.insert(5.into(), 1_000_000_000.into()); + ext.tracing = true; + + let gas_left = { + let vm = interpreter(params, &ext); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_eq!(ext.calls.len(), 1); + assert_eq!(gas_left, 248_212.into()); + } + + #[test] + fn should_not_overflow_returndata() { + let code = "6001600160000360003e00".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.address = 5.into(); + params.gas = 300_000.into(); + params.gas_price = 1.into(); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new_byzantium(); + ext.balances.insert(5.into(), 1_000_000_000.into()); + ext.tracing = true; + + let err = { + let vm = interpreter(params, &ext); + test_finalize(vm.exec(&mut ext).ok().unwrap()) + .err() + .unwrap() + }; + + assert_eq!(err, ::vm::Error::OutOfBounds); + } } diff --git a/ethcore/evm/src/interpreter/shared_cache.rs b/ethcore/evm/src/interpreter/shared_cache.rs index da7c03efa01..459bb1a90d3 100644 --- a/ethcore/evm/src/interpreter/shared_cache.rs +++ b/ethcore/evm/src/interpreter/shared_cache.rs @@ -1,112 +1,219 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::sync::Arc; +use super::super::instructions::{self, Instruction}; +use bit_set::BitSet; +use ethereum_types::H256; use hash::KECCAK_EMPTY; use heapsize::HeapSizeOf; -use ethereum_types::H256; -use parking_lot::Mutex; use memory_cache::MemoryLruCache; -use bit_set::BitSet; -use super::super::instructions::{self, Instruction}; +use parking_lot::Mutex; +use std::sync::Arc; const DEFAULT_CACHE_SIZE: usize = 4 * 1024 * 1024; // stub for a HeapSizeOf implementation. +#[derive(Clone)] struct Bits(Arc); impl HeapSizeOf for Bits { - fn heap_size_of_children(&self) -> usize { - // dealing in bits here - self.0.capacity() * 8 - } + fn heap_size_of_children(&self) -> usize { + // dealing in bits here + self.0.capacity() * 8 + } +} + +#[derive(Clone)] +struct CacheItem { + jump_destination: Bits, + sub_entrypoint: Bits, +} + +impl HeapSizeOf for CacheItem { + fn heap_size_of_children(&self) -> usize { + self.jump_destination.heap_size_of_children() + self.sub_entrypoint.heap_size_of_children() + } } /// Global cache for EVM interpreter pub struct SharedCache { - jump_destinations: Mutex>, + jump_destinations: Mutex>, } impl SharedCache { - /// Create a jump destinations cache with a maximum size in bytes - /// to cache. - pub fn new(max_size: usize) -> Self { - SharedCache { - jump_destinations: Mutex::new(MemoryLruCache::new(max_size)), - } - } - - /// Get jump destinations bitmap for a contract. - pub fn jump_destinations(&self, code_hash: &Option, code: &[u8]) -> Arc { - if let Some(ref code_hash) = code_hash { - if code_hash == &KECCAK_EMPTY { - return Self::find_jump_destinations(code); - } - - if let Some(d) = self.jump_destinations.lock().get_mut(code_hash) { - return d.0.clone(); - } - } - - let d = Self::find_jump_destinations(code); - - if let Some(ref code_hash) = code_hash { - self.jump_destinations.lock().insert(*code_hash, Bits(d.clone())); - } - - d - } - - fn find_jump_destinations(code: &[u8]) -> Arc { - let mut jump_dests = BitSet::with_capacity(code.len()); - let mut position = 0; - - while position < code.len() { - let instruction = Instruction::from_u8(code[position]); - - if let Some(instruction) = instruction { - if instruction == instructions::JUMPDEST { - jump_dests.insert(position); - } else if let Some(push_bytes) = instruction.push_bytes() { - position += push_bytes; - } - } - position += 1; - } - - jump_dests.shrink_to_fit(); - Arc::new(jump_dests) - } + /// Create a jump destinations cache with a maximum size in bytes + /// to cache. + pub fn new(max_size: usize) -> Self { + SharedCache { + jump_destinations: Mutex::new(MemoryLruCache::new(max_size)), + } + } + + /// Get jump destinations bitmap for a contract. + pub fn jump_and_sub_destinations( + &self, + code_hash: &Option, + code: &[u8], + ) -> (Arc, Arc) { + if let Some(ref code_hash) = code_hash { + if code_hash == &KECCAK_EMPTY { + let cache_item = Self::find_jump_and_sub_destinations(code); + return (cache_item.jump_destination.0, cache_item.sub_entrypoint.0); + } + + if let Some(d) = self.jump_destinations.lock().get_mut(code_hash) { + return (d.jump_destination.0.clone(), d.sub_entrypoint.0.clone()); + } + } + + let d = Self::find_jump_and_sub_destinations(code); + + if let Some(ref code_hash) = code_hash { + self.jump_destinations.lock().insert(*code_hash, d.clone()); + } + + (d.jump_destination.0, d.sub_entrypoint.0) + } + + fn find_jump_and_sub_destinations(code: &[u8]) -> CacheItem { + let mut jump_dests = BitSet::with_capacity(code.len()); + let mut sub_entrypoints = BitSet::with_capacity(code.len()); + let mut position = 0; + + while position < code.len() { + let instruction = Instruction::from_u8(code[position]); + + if let Some(instruction) = instruction { + match instruction { + instructions::JUMPDEST => { + jump_dests.insert(position); + } + instructions::BEGINSUB => { + sub_entrypoints.insert(position); + } + _ => { + if let Some(push_bytes) = instruction.push_bytes() { + position += push_bytes; + } + } + } + } + position += 1; + } + + jump_dests.shrink_to_fit(); + CacheItem { + jump_destination: Bits(Arc::new(jump_dests)), + sub_entrypoint: Bits(Arc::new(sub_entrypoints)), + } + } } impl Default for SharedCache { - fn default() -> Self { - SharedCache::new(DEFAULT_CACHE_SIZE) - } + fn default() -> Self { + SharedCache::new(DEFAULT_CACHE_SIZE) + } } -#[test] -fn test_find_jump_destinations() { - use rustc_hex::FromHex; - // given - let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap(); - - // when - let valid_jump_destinations = SharedCache::find_jump_destinations(&code); - - // then - assert!(valid_jump_destinations.contains(66)); +#[cfg(test)] +mod test { + use super::*; + use hex_literal::hex; + + #[test] + fn test_find_jump_destinations() { + // given + + // 0000 7F PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + // 0021 7F PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + // 0042 5B JUMPDEST + // 0043 01 ADD + // 0044 60 PUSH1 0x00 + // 0046 55 SSTORE + let code = hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055"); + + // when + let cache_item = SharedCache::find_jump_and_sub_destinations(&code); + + // then + assert!(cache_item + .jump_destination + .0 + .iter() + .eq(vec![66].into_iter())); + assert!(cache_item.sub_entrypoint.0.is_empty()); + } + + #[test] + fn test_find_jump_destinations_not_in_data_segments() { + // given + + // 0000 60 06 PUSH1 06 + // 0002 56 JUMP + // 0003 50 5B PUSH1 0x5B + // 0005 56 STOP + // 0006 5B JUMPDEST + // 0007 60 04 PUSH1 04 + // 0009 56 JUMP + let code = hex!("600656605B565B6004"); + + // when + let cache_item = SharedCache::find_jump_and_sub_destinations(&code); + + // then + assert!(cache_item.jump_destination.0.iter().eq(vec![6].into_iter())); + assert!(cache_item.sub_entrypoint.0.is_empty()); + } + + #[test] + fn test_find_sub_entrypoints() { + // given + + // see https://eips.ethereum.org/EIPS/eip-2315 for disassembly + let code = hex!("6800000000000000000c5e005c60115e5d5c5d"); + + // when + let cache_item = SharedCache::find_jump_and_sub_destinations(&code); + + // then + assert!(cache_item.jump_destination.0.is_empty()); + assert!(cache_item + .sub_entrypoint + .0 + .iter() + .eq(vec![12, 17].into_iter())); + } + + #[test] + fn test_find_jump_and_sub_allowing_unknown_opcodes() { + // precondition + assert!(Instruction::from_u8(0xcc) == None); + + // given + + // 0000 5B JUMPDEST + // 0001 CC ??? + // 0002 5C BEGINSUB + let code = hex!("5BCC5C"); + + // when + let cache_item = SharedCache::find_jump_and_sub_destinations(&code); + + // then + assert!(cache_item.jump_destination.0.iter().eq(vec![0].into_iter())); + assert!(cache_item.sub_entrypoint.0.iter().eq(vec![2].into_iter())); + } } diff --git a/ethcore/evm/src/interpreter/stack.rs b/ethcore/evm/src/interpreter/stack.rs index 87e14bdadbd..86c89086352 100644 --- a/ethcore/evm/src/interpreter/stack.rs +++ b/ethcore/evm/src/interpreter/stack.rs @@ -1,93 +1,98 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::fmt; use instructions; +use std::fmt; /// Stack trait with VM-friendly API pub trait Stack { - /// Returns `Stack[len(Stack) - no_from_top]` - fn peek(&self, no_from_top: usize) -> &T; - /// Swaps Stack[len(Stack)] and Stack[len(Stack) - no_from_top] - fn swap_with_top(&mut self, no_from_top: usize); - /// Returns true if Stack has at least `no_of_elems` elements - fn has(&self, no_of_elems: usize) -> bool; - /// Get element from top and remove it from Stack. Panics if stack is empty. - fn pop_back(&mut self) -> T; - /// Get (up to `instructions::MAX_NO_OF_TOPICS`) elements from top and remove them from Stack. Panics if stack is empty. - fn pop_n(&mut self, no_of_elems: usize) -> &[T]; - /// Add element on top of the Stack - fn push(&mut self, elem: T); - /// Get number of elements on Stack - fn size(&self) -> usize; - /// Returns all data on stack. - fn peek_top(&self, no_of_elems: usize) -> &[T]; + /// Returns `Stack[len(Stack) - no_from_top]` + fn peek(&self, no_from_top: usize) -> &T; + /// Swaps Stack[len(Stack)] and Stack[len(Stack) - no_from_top] + fn swap_with_top(&mut self, no_from_top: usize); + /// Returns true if Stack has at least `no_of_elems` elements + fn has(&self, no_of_elems: usize) -> bool; + /// Get element from top and remove it from Stack. Panics if stack is empty. + fn pop_back(&mut self) -> T; + /// Get (up to `instructions::MAX_NO_OF_TOPICS`) elements from top and remove them from Stack. Panics if stack is empty. + fn pop_n(&mut self, no_of_elems: usize) -> &[T]; + /// Add element on top of the Stack + fn push(&mut self, elem: T); + /// Get number of elements on Stack + fn size(&self) -> usize; + /// Returns all data on stack. + fn peek_top(&self, no_of_elems: usize) -> &[T]; } pub struct VecStack { - stack: Vec, - logs: [S; instructions::MAX_NO_OF_TOPICS] + stack: Vec, + logs: [S; instructions::MAX_NO_OF_TOPICS], } -impl VecStack { - pub fn with_capacity(capacity: usize, zero: S) -> Self { - VecStack { - stack: Vec::with_capacity(capacity), - logs: [zero; instructions::MAX_NO_OF_TOPICS] - } - } +impl VecStack { + pub fn with_capacity(capacity: usize, zero: S) -> Self { + VecStack { + stack: Vec::with_capacity(capacity), + logs: [zero; instructions::MAX_NO_OF_TOPICS], + } + } } -impl Stack for VecStack { - fn peek(&self, no_from_top: usize) -> &S { - &self.stack[self.stack.len() - no_from_top - 1] - } - - fn swap_with_top(&mut self, no_from_top: usize) { - let len = self.stack.len(); - self.stack.swap(len - no_from_top - 1, len - 1); - } - - fn has(&self, no_of_elems: usize) -> bool { - self.stack.len() >= no_of_elems - } - - fn pop_back(&mut self) -> S { - self.stack.pop().expect("instruction validation prevents from popping too many items; qed") - } - - fn pop_n(&mut self, no_of_elems: usize) -> &[S] { - assert!(no_of_elems <= instructions::MAX_NO_OF_TOPICS); - - for i in 0..no_of_elems { - self.logs[i] = self.pop_back(); - } - &self.logs[0..no_of_elems] - } - - fn push(&mut self, elem: S) { - self.stack.push(elem); - } - - fn size(&self) -> usize { - self.stack.len() - } - - fn peek_top(&self, no_from_top: usize) -> &[S] { - assert!(self.stack.len() >= no_from_top, "peek_top asked for more items than exist."); - &self.stack[self.stack.len() - no_from_top .. self.stack.len()] - } +impl Stack for VecStack { + fn peek(&self, no_from_top: usize) -> &S { + &self.stack[self.stack.len() - no_from_top - 1] + } + + fn swap_with_top(&mut self, no_from_top: usize) { + let len = self.stack.len(); + self.stack.swap(len - no_from_top - 1, len - 1); + } + + fn has(&self, no_of_elems: usize) -> bool { + self.stack.len() >= no_of_elems + } + + fn pop_back(&mut self) -> S { + self.stack + .pop() + .expect("instruction validation prevents from popping too many items; qed") + } + + fn pop_n(&mut self, no_of_elems: usize) -> &[S] { + assert!(no_of_elems <= instructions::MAX_NO_OF_TOPICS); + + for i in 0..no_of_elems { + self.logs[i] = self.pop_back(); + } + &self.logs[0..no_of_elems] + } + + fn push(&mut self, elem: S) { + self.stack.push(elem); + } + + fn size(&self) -> usize { + self.stack.len() + } + + fn peek_top(&self, no_from_top: usize) -> &[S] { + assert!( + self.stack.len() >= no_from_top, + "peek_top asked for more items than exist." + ); + &self.stack[self.stack.len() - no_from_top..self.stack.len()] + } } diff --git a/ethcore/evm/src/lib.rs b/ethcore/evm/src/lib.rs index 3548a1fe5e5..486e0d8b0f4 100644 --- a/ethcore/evm/src/lib.rs +++ b/ethcore/evm/src/lib.rs @@ -1,29 +1,30 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Ethereum virtual machine. extern crate bit_set; extern crate ethereum_types; -extern crate parking_lot; extern crate heapsize; -extern crate vm; extern crate keccak_hash as hash; extern crate memory_cache; +extern crate num_bigint; extern crate parity_bytes as bytes; +extern crate parking_lot; +extern crate vm; #[macro_use] extern crate lazy_static; @@ -31,6 +32,8 @@ extern crate lazy_static; #[cfg_attr(feature = "evm-debug", macro_use)] extern crate log; +#[cfg(test)] +extern crate hex_literal; #[cfg(test)] extern crate rustc_hex; @@ -39,18 +42,19 @@ pub mod interpreter; #[macro_use] pub mod factory; -mod vmtype; mod instructions; +mod vmtype; #[cfg(test)] mod tests; +pub use self::{ + evm::{CostType, FinalizationResult, Finalize}, + factory::Factory, + instructions::{Instruction, InstructionInfo}, + vmtype::VMType, +}; pub use vm::{ - Schedule, CleanDustMode, EnvInfo, CallType, ActionParams, Ext, - ContractCreateResult, MessageCallResult, CreateContractAddress, - GasLeft, ReturnData + ActionParams, CallType, CleanDustMode, ContractCreateResult, CreateContractAddress, EnvInfo, + Ext, GasLeft, MessageCallResult, ReturnData, Schedule, }; -pub use self::evm::{Finalize, FinalizationResult, CostType}; -pub use self::instructions::{InstructionInfo, Instruction}; -pub use self::vmtype::VMType; -pub use self::factory::Factory; diff --git a/ethcore/evm/src/tests.rs b/ethcore/evm/src/tests.rs index dd039311ebd..93fe5bd7000 100644 --- a/ethcore/evm/src/tests.rs +++ b/ethcore/evm/src/tests.rs @@ -1,1070 +1,1604 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::fmt::Debug; -use std::str::FromStr; -use std::hash::Hash; -use std::sync::Arc; -use std::collections::{HashMap, HashSet}; -use rustc_hex::FromHex; -use ethereum_types::{U256, H256, Address}; -use vm::{self, ActionParams, ActionValue, Ext}; -use vm::tests::{FakeExt, FakeCall, FakeCallType, test_finalize}; +use super::interpreter::MAX_SUB_STACK_SIZE; +use ethereum_types::{Address, H256, U256}; use factory::Factory; +use hex_literal::hex; +use rustc_hex::FromHex; +use std::{ + collections::{HashMap, HashSet}, + fmt::Debug, + hash::Hash, + str::FromStr, + sync::Arc, +}; +use vm::{ + self, + tests::{test_finalize, FakeCall, FakeCallType, FakeExt}, + ActionParams, ActionValue, Ext, +}; use vmtype::VMType; -evm_test!{test_add: test_add_int} +evm_test! {test_add: test_add_int} fn test_add(factory: super::Factory) { - let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.address = address.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_eq!(gas_left, U256::from(79_988)); - assert_store(&ext, 0, "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"); + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.address = address.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_eq!(gas_left, U256::from(79_988)); + assert_store( + &ext, + 0, + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + ); } -evm_test!{test_sha3: test_sha3_int} +evm_test! {test_sha3: test_sha3_int} fn test_sha3(factory: super::Factory) { - let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let code = "6000600020600055".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.address = address.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_eq!(gas_left, U256::from(79_961)); - assert_store(&ext, 0, "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let code = "6000600020600055".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.address = address.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_eq!(gas_left, U256::from(79_961)); + assert_store( + &ext, + 0, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + ); } -evm_test!{test_address: test_address_int} +evm_test! {test_address: test_address_int} fn test_address(factory: super::Factory) { - let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let code = "30600055".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.address = address.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_eq!(gas_left, U256::from(79_995)); - assert_store(&ext, 0, "0000000000000000000000000f572e5295c57f15886f9b263e2f6d2d6c7b5ec6"); + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let code = "30600055".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.address = address.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_eq!(gas_left, U256::from(79_995)); + assert_store( + &ext, + 0, + "0000000000000000000000000f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + ); } -evm_test!{test_origin: test_origin_int} +evm_test! {test_origin: test_origin_int} fn test_origin(factory: super::Factory) { - let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let origin = Address::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap(); - let code = "32600055".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.address = address.clone(); - params.origin = origin.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_eq!(gas_left, U256::from(79_995)); - assert_store(&ext, 0, "000000000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681"); + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let origin = Address::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap(); + let code = "32600055".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.address = address.clone(); + params.origin = origin.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_eq!(gas_left, U256::from(79_995)); + assert_store( + &ext, + 0, + "000000000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681", + ); } -evm_test!{test_sender: test_sender_int} +evm_test! {test_selfbalance: test_selfbalance_int} +fn test_selfbalance(factory: super::Factory) { + let own_addr = Address::from_str("1337000000000000000000000000000000000000").unwrap(); + // 47 SELFBALANCE + // 60 ff PUSH ff + // 55 SSTORE + let code = hex!("47 60 ff 55").to_vec(); + + let mut params = ActionParams::default(); + params.address = own_addr.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new_istanbul(); + ext.balances = { + let mut x = HashMap::new(); + x.insert(own_addr, U256::from(1_025)); // 0x401 + x + }; + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + assert_eq!(gas_left, U256::from(79_992)); // TODO[dvdplm]: do the sums here, SELFBALANCE-5 + PUSH1-3 + ONEBYTE-4 + SSTORE-?? = 100_000 - 79_992 + assert_store( + &ext, + 0xff, + "0000000000000000000000000000000000000000000000000000000000000401", + ); +} + +evm_test! {test_sender: test_sender_int} fn test_sender(factory: super::Factory) { - let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let sender = Address::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap(); - let code = "33600055".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.address = address.clone(); - params.sender = sender.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_eq!(gas_left, U256::from(79_995)); - assert_store(&ext, 0, "000000000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681"); + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let sender = Address::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap(); + let code = "33600055".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.address = address.clone(); + params.sender = sender.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_eq!(gas_left, U256::from(79_995)); + assert_store( + &ext, + 0, + "000000000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681", + ); } -evm_test!{test_extcodecopy: test_extcodecopy_int} +evm_test! {test_chain_id: test_chain_id_int} +fn test_chain_id(factory: super::Factory) { + // 46 CHAINID + // 60 00 PUSH 0 + // 55 SSTORE + let code = hex!("46 60 00 55").to_vec(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new_istanbul().with_chain_id(9); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_eq!(gas_left, U256::from(79_995)); + assert_store( + &ext, + 0, + "0000000000000000000000000000000000000000000000000000000000000009", + ); +} + +evm_test! {test_extcodecopy: test_extcodecopy_int} fn test_extcodecopy(factory: super::Factory) { - // 33 - sender - // 3b - extcodesize - // 60 00 - push 0 - // 60 00 - push 0 - // 33 - sender - // 3c - extcodecopy - // 60 00 - push 0 - // 51 - load word from memory - // 60 00 - push 0 - // 55 - sstore - - let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let sender = Address::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap(); - let code = "333b60006000333c600051600055".from_hex().unwrap(); - let sender_code = "6005600055".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.address = address.clone(); - params.sender = sender.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - ext.codes.insert(sender, Arc::new(sender_code)); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_eq!(gas_left, U256::from(79_935)); - assert_store(&ext, 0, "6005600055000000000000000000000000000000000000000000000000000000"); + // 33 - sender + // 3b - extcodesize + // 60 00 - push 0 + // 60 00 - push 0 + // 33 - sender + // 3c - extcodecopy + // 60 00 - push 0 + // 51 - load word from memory + // 60 00 - push 0 + // 55 - sstore + + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let sender = Address::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap(); + let code = "333b60006000333c600051600055".from_hex().unwrap(); + let sender_code = "6005600055".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.address = address.clone(); + params.sender = sender.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + ext.codes.insert(sender, Arc::new(sender_code)); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_eq!(gas_left, U256::from(79_935)); + assert_store( + &ext, + 0, + "6005600055000000000000000000000000000000000000000000000000000000", + ); } -evm_test!{test_log_empty: test_log_empty_int} +evm_test! {test_log_empty: test_log_empty_int} fn test_log_empty(factory: super::Factory) { - let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let code = "60006000a0".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.address = address.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_eq!(gas_left, U256::from(99_619)); - assert_eq!(ext.logs.len(), 1); - assert_eq!(ext.logs[0].topics.len(), 0); - assert!(ext.logs[0].data.is_empty()); + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let code = "60006000a0".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.address = address.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_eq!(gas_left, U256::from(99_619)); + assert_eq!(ext.logs.len(), 1); + assert_eq!(ext.logs[0].topics.len(), 0); + assert!(ext.logs[0].data.is_empty()); } -evm_test!{test_log_sender: test_log_sender_int} +evm_test! {test_log_sender: test_log_sender_int} fn test_log_sender(factory: super::Factory) { - // 60 ff - push ff - // 60 00 - push 00 - // 53 - mstore - // 33 - sender - // 60 20 - push 20 - // 60 00 - push 0 - // a1 - log with 1 topic - - let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let code = "60ff6000533360206000a1".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.address = address.clone(); - params.sender = sender.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_eq!(gas_left, U256::from(98_974)); - assert_eq!(ext.logs.len(), 1); - assert_eq!(ext.logs[0].topics.len(), 1); - assert_eq!(ext.logs[0].topics[0], H256::from_str("000000000000000000000000cd1722f3947def4cf144679da39c4c32bdc35681").unwrap()); - assert_eq!(ext.logs[0].data, "ff00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap()); + // 60 ff - push ff + // 60 00 - push 00 + // 53 - mstore + // 33 - sender + // 60 20 - push 20 + // 60 00 - push 0 + // a1 - log with 1 topic + + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); + let code = "60ff6000533360206000a1".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.address = address.clone(); + params.sender = sender.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_eq!(gas_left, U256::from(98_974)); + assert_eq!(ext.logs.len(), 1); + assert_eq!(ext.logs[0].topics.len(), 1); + assert_eq!( + ext.logs[0].topics[0], + H256::from_str("000000000000000000000000cd1722f3947def4cf144679da39c4c32bdc35681").unwrap() + ); + assert_eq!( + ext.logs[0].data, + "ff00000000000000000000000000000000000000000000000000000000000000" + .from_hex() + .unwrap() + ); } -evm_test!{test_blockhash: test_blockhash_int} +evm_test! {test_blockhash: test_blockhash_int} fn test_blockhash(factory: super::Factory) { - let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let code = "600040600055".from_hex().unwrap(); - let blockhash = H256::from_str("123400000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681").unwrap(); - - let mut params = ActionParams::default(); - params.address = address.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - ext.blockhashes.insert(U256::zero(), blockhash.clone()); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_eq!(gas_left, U256::from(79_974)); - assert_eq!(ext.store.get(&H256::new()).unwrap(), &blockhash); + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let code = "600040600055".from_hex().unwrap(); + let blockhash = + H256::from_str("123400000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681").unwrap(); + + let mut params = ActionParams::default(); + params.address = address.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + ext.blockhashes.insert(U256::zero(), blockhash.clone()); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_eq!(gas_left, U256::from(79_974)); + assert_eq!(ext.store.get(&H256::new()).unwrap(), &blockhash); } -evm_test!{test_calldataload: test_calldataload_int} +evm_test! {test_calldataload: test_calldataload_int} fn test_calldataload(factory: super::Factory) { - let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let code = "600135600055".from_hex().unwrap(); - let data = "0123ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff23".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.address = address.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - params.data = Some(data); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_eq!(gas_left, U256::from(79_991)); - assert_store(&ext, 0, "23ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff23"); - + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let code = "600135600055".from_hex().unwrap(); + let data = "0123ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff23" + .from_hex() + .unwrap(); + + let mut params = ActionParams::default(); + params.address = address.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + params.data = Some(data); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_eq!(gas_left, U256::from(79_991)); + assert_store( + &ext, + 0, + "23ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff23", + ); } -evm_test!{test_author: test_author_int} +evm_test! {test_author: test_author_int} fn test_author(factory: super::Factory) { - let author = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let code = "41600055".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - ext.info.author = author; - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_eq!(gas_left, U256::from(79_995)); - assert_store(&ext, 0, "0000000000000000000000000f572e5295c57f15886f9b263e2f6d2d6c7b5ec6"); + let author = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let code = "41600055".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + ext.info.author = author; + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_eq!(gas_left, U256::from(79_995)); + assert_store( + &ext, + 0, + "0000000000000000000000000f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + ); } -evm_test!{test_timestamp: test_timestamp_int} +evm_test! {test_timestamp: test_timestamp_int} fn test_timestamp(factory: super::Factory) { - let timestamp = 0x1234; - let code = "42600055".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - ext.info.timestamp = timestamp; - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_eq!(gas_left, U256::from(79_995)); - assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000001234"); + let timestamp = 0x1234; + let code = "42600055".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + ext.info.timestamp = timestamp; + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_eq!(gas_left, U256::from(79_995)); + assert_store( + &ext, + 0, + "0000000000000000000000000000000000000000000000000000000000001234", + ); } -evm_test!{test_number: test_number_int} +evm_test! {test_number: test_number_int} fn test_number(factory: super::Factory) { - let number = 0x1234; - let code = "43600055".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - ext.info.number = number; - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_eq!(gas_left, U256::from(79_995)); - assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000001234"); + let number = 0x1234; + let code = "43600055".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + ext.info.number = number; + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_eq!(gas_left, U256::from(79_995)); + assert_store( + &ext, + 0, + "0000000000000000000000000000000000000000000000000000000000001234", + ); } -evm_test!{test_difficulty: test_difficulty_int} +evm_test! {test_difficulty: test_difficulty_int} fn test_difficulty(factory: super::Factory) { - let difficulty = U256::from(0x1234); - let code = "44600055".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - ext.info.difficulty = difficulty; - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_eq!(gas_left, U256::from(79_995)); - assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000001234"); + let difficulty = U256::from(0x1234); + let code = "44600055".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + ext.info.difficulty = difficulty; + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_eq!(gas_left, U256::from(79_995)); + assert_store( + &ext, + 0, + "0000000000000000000000000000000000000000000000000000000000001234", + ); } -evm_test!{test_gas_limit: test_gas_limit_int} +evm_test! {test_gas_limit: test_gas_limit_int} fn test_gas_limit(factory: super::Factory) { - let gas_limit = U256::from(0x1234); - let code = "45600055".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - ext.info.gas_limit = gas_limit; - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_eq!(gas_left, U256::from(79_995)); - assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000001234"); + let gas_limit = U256::from(0x1234); + let code = "45600055".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + ext.info.gas_limit = gas_limit; + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_eq!(gas_left, U256::from(79_995)); + assert_store( + &ext, + 0, + "0000000000000000000000000000000000000000000000000000000000001234", + ); } -evm_test!{test_mul: test_mul_int} +evm_test! {test_mul: test_mul_int} fn test_mul(factory: super::Factory) { - let code = "65012365124623626543219002600055".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_store(&ext, 0, "000000000000000000000000000000000000000000000000734349397b853383"); - assert_eq!(gas_left, U256::from(79_983)); + let code = "65012365124623626543219002600055".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_store( + &ext, + 0, + "000000000000000000000000000000000000000000000000734349397b853383", + ); + assert_eq!(gas_left, U256::from(79_983)); } -evm_test!{test_sub: test_sub_int} +evm_test! {test_sub: test_sub_int} fn test_sub(factory: super::Factory) { - let code = "65012365124623626543219003600055".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000012364ad0302"); - assert_eq!(gas_left, U256::from(79_985)); + let code = "65012365124623626543219003600055".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_store( + &ext, + 0, + "0000000000000000000000000000000000000000000000000000012364ad0302", + ); + assert_eq!(gas_left, U256::from(79_985)); } -evm_test!{test_div: test_div_int} +evm_test! {test_div: test_div_int} fn test_div(factory: super::Factory) { - let code = "65012365124623626543219004600055".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_store(&ext, 0, "000000000000000000000000000000000000000000000000000000000002e0ac"); - assert_eq!(gas_left, U256::from(79_983)); + let code = "65012365124623626543219004600055".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_store( + &ext, + 0, + "000000000000000000000000000000000000000000000000000000000002e0ac", + ); + assert_eq!(gas_left, U256::from(79_983)); } -evm_test!{test_div_zero: test_div_zero_int} +evm_test! {test_div_zero: test_div_zero_int} fn test_div_zero(factory: super::Factory) { - let code = "6501236512462360009004600055".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000"); - assert_eq!(gas_left, U256::from(94_983)); + let code = "6501236512462360009004600055".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_store( + &ext, + 0, + "0000000000000000000000000000000000000000000000000000000000000000", + ); + assert_eq!(gas_left, U256::from(94_983)); } -evm_test!{test_mod: test_mod_int} +evm_test! {test_mod: test_mod_int} fn test_mod(factory: super::Factory) { - let code = "650123651246236265432290066000556501236512462360009006600155".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000076b4b"); - assert_store(&ext, 1, "0000000000000000000000000000000000000000000000000000000000000000"); - assert_eq!(gas_left, U256::from(74_966)); + let code = "650123651246236265432290066000556501236512462360009006600155" + .from_hex() + .unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_store( + &ext, + 0, + "0000000000000000000000000000000000000000000000000000000000076b4b", + ); + assert_store( + &ext, + 1, + "0000000000000000000000000000000000000000000000000000000000000000", + ); + assert_eq!(gas_left, U256::from(74_966)); } -evm_test!{test_smod: test_smod_int} +evm_test! {test_smod: test_smod_int} fn test_smod(factory: super::Factory) { - let code = "650123651246236265432290076000556501236512462360009007600155".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000076b4b"); - assert_store(&ext, 1, "0000000000000000000000000000000000000000000000000000000000000000"); - assert_eq!(gas_left, U256::from(74_966)); + let code = "650123651246236265432290076000556501236512462360009007600155" + .from_hex() + .unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_store( + &ext, + 0, + "0000000000000000000000000000000000000000000000000000000000076b4b", + ); + assert_store( + &ext, + 1, + "0000000000000000000000000000000000000000000000000000000000000000", + ); + assert_eq!(gas_left, U256::from(74_966)); } -evm_test!{test_sdiv: test_sdiv_int} +evm_test! {test_sdiv: test_sdiv_int} fn test_sdiv(factory: super::Factory) { - let code = "650123651246236265432290056000556501236512462360009005600155".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_store(&ext, 0, "000000000000000000000000000000000000000000000000000000000002e0ac"); - assert_store(&ext, 1, "0000000000000000000000000000000000000000000000000000000000000000"); - assert_eq!(gas_left, U256::from(74_966)); + let code = "650123651246236265432290056000556501236512462360009005600155" + .from_hex() + .unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_store( + &ext, + 0, + "000000000000000000000000000000000000000000000000000000000002e0ac", + ); + assert_store( + &ext, + 1, + "0000000000000000000000000000000000000000000000000000000000000000", + ); + assert_eq!(gas_left, U256::from(74_966)); } -evm_test!{test_exp: test_exp_int} +evm_test! {test_exp: test_exp_int} fn test_exp(factory: super::Factory) { - let code = "6016650123651246230a6000556001650123651246230a6001556000650123651246230a600255".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_store(&ext, 0, "90fd23767b60204c3d6fc8aec9e70a42a3f127140879c133a20129a597ed0c59"); - assert_store(&ext, 1, "0000000000000000000000000000000000000000000000000000012365124623"); - assert_store(&ext, 2, "0000000000000000000000000000000000000000000000000000000000000001"); - assert_eq!(gas_left, U256::from(39_923)); + let code = "6016650123651246230a6000556001650123651246230a6001556000650123651246230a600255" + .from_hex() + .unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_store( + &ext, + 0, + "90fd23767b60204c3d6fc8aec9e70a42a3f127140879c133a20129a597ed0c59", + ); + assert_store( + &ext, + 1, + "0000000000000000000000000000000000000000000000000000012365124623", + ); + assert_store( + &ext, + 2, + "0000000000000000000000000000000000000000000000000000000000000001", + ); + assert_eq!(gas_left, U256::from(39_923)); } -evm_test!{test_comparison: test_comparison_int} +evm_test! {test_comparison: test_comparison_int} fn test_comparison(factory: super::Factory) { - let code = "601665012365124623818181811060005511600155146002556415235412358014600355".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000"); - assert_store(&ext, 1, "0000000000000000000000000000000000000000000000000000000000000001"); - assert_store(&ext, 2, "0000000000000000000000000000000000000000000000000000000000000000"); - assert_store(&ext, 3, "0000000000000000000000000000000000000000000000000000000000000001"); - assert_eq!(gas_left, U256::from(49_952)); + let code = "601665012365124623818181811060005511600155146002556415235412358014600355" + .from_hex() + .unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_store( + &ext, + 0, + "0000000000000000000000000000000000000000000000000000000000000000", + ); + assert_store( + &ext, + 1, + "0000000000000000000000000000000000000000000000000000000000000001", + ); + assert_store( + &ext, + 2, + "0000000000000000000000000000000000000000000000000000000000000000", + ); + assert_store( + &ext, + 3, + "0000000000000000000000000000000000000000000000000000000000000001", + ); + assert_eq!(gas_left, U256::from(49_952)); } -evm_test!{test_signed_comparison: test_signed_comparison_int} +evm_test! {test_signed_comparison: test_signed_comparison_int} fn test_signed_comparison(factory: super::Factory) { - let code = "60106000036010818112600055136001556010601060000381811260025513600355".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000"); - assert_store(&ext, 1, "0000000000000000000000000000000000000000000000000000000000000001"); - assert_store(&ext, 2, "0000000000000000000000000000000000000000000000000000000000000001"); - assert_store(&ext, 3, "0000000000000000000000000000000000000000000000000000000000000000"); - assert_eq!(gas_left, U256::from(49_940)); + let code = "60106000036010818112600055136001556010601060000381811260025513600355" + .from_hex() + .unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_store( + &ext, + 0, + "0000000000000000000000000000000000000000000000000000000000000000", + ); + assert_store( + &ext, + 1, + "0000000000000000000000000000000000000000000000000000000000000001", + ); + assert_store( + &ext, + 2, + "0000000000000000000000000000000000000000000000000000000000000001", + ); + assert_store( + &ext, + 3, + "0000000000000000000000000000000000000000000000000000000000000000", + ); + assert_eq!(gas_left, U256::from(49_940)); } -evm_test!{test_bitops: test_bitops_int} +evm_test! {test_bitops: test_bitops_int} fn test_bitops(factory: super::Factory) { - let code = "60ff610ff08181818116600055176001551860025560008015600355198015600455600555".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.gas = U256::from(150_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_store(&ext, 0, "00000000000000000000000000000000000000000000000000000000000000f0"); - assert_store(&ext, 1, "0000000000000000000000000000000000000000000000000000000000000fff"); - assert_store(&ext, 2, "0000000000000000000000000000000000000000000000000000000000000f0f"); - assert_store(&ext, 3, "0000000000000000000000000000000000000000000000000000000000000001"); - assert_store(&ext, 4, "0000000000000000000000000000000000000000000000000000000000000000"); - assert_store(&ext, 5, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - assert_eq!(gas_left, U256::from(44_937)); + let code = "60ff610ff08181818116600055176001551860025560008015600355198015600455600555" + .from_hex() + .unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(150_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_store( + &ext, + 0, + "00000000000000000000000000000000000000000000000000000000000000f0", + ); + assert_store( + &ext, + 1, + "0000000000000000000000000000000000000000000000000000000000000fff", + ); + assert_store( + &ext, + 2, + "0000000000000000000000000000000000000000000000000000000000000f0f", + ); + assert_store( + &ext, + 3, + "0000000000000000000000000000000000000000000000000000000000000001", + ); + assert_store( + &ext, + 4, + "0000000000000000000000000000000000000000000000000000000000000000", + ); + assert_store( + &ext, + 5, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ); + assert_eq!(gas_left, U256::from(44_937)); } -evm_test!{test_addmod_mulmod: test_addmod_mulmod_int} +evm_test! {test_addmod_mulmod: test_addmod_mulmod_int} fn test_addmod_mulmod(factory: super::Factory) { - let code = "60ff60f060108282820860005509600155600060f0601082828208196002550919600355".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000001"); - assert_store(&ext, 1, "000000000000000000000000000000000000000000000000000000000000000f"); - assert_store(&ext, 2, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - assert_store(&ext, 3, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - assert_eq!(gas_left, U256::from(19_914)); + let code = "60ff60f060108282820860005509600155600060f0601082828208196002550919600355" + .from_hex() + .unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_store( + &ext, + 0, + "0000000000000000000000000000000000000000000000000000000000000001", + ); + assert_store( + &ext, + 1, + "000000000000000000000000000000000000000000000000000000000000000f", + ); + assert_store( + &ext, + 2, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ); + assert_store( + &ext, + 3, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ); + assert_eq!(gas_left, U256::from(19_914)); } -evm_test!{test_byte: test_byte_int} +evm_test! {test_byte: test_byte_int} fn test_byte(factory: super::Factory) { - let code = "60f061ffff1a600055610fff601f1a600155".from_hex().unwrap(); + let code = "60f061ffff1a600055610fff601f1a600155".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_store( + &ext, + 0, + "0000000000000000000000000000000000000000000000000000000000000000", + ); + assert_store( + &ext, + 1, + "00000000000000000000000000000000000000000000000000000000000000ff", + ); + assert_eq!(gas_left, U256::from(74_976)); +} - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); +evm_test! {test_signextend: test_signextend_int} +fn test_signextend(factory: super::Factory) { + let code = "610fff60020b60005560ff60200b600155".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_store( + &ext, + 0, + "0000000000000000000000000000000000000000000000000000000000000fff", + ); + assert_store( + &ext, + 1, + "00000000000000000000000000000000000000000000000000000000000000ff", + ); + assert_eq!(gas_left, U256::from(59_972)); +} - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; +#[test] // JIT just returns out of gas +fn test_badinstruction_int() { + let factory = super::Factory::new(VMType::Interpreter, 1024 * 32); + let code = "af".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let err = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap_err() + }; + + match err { + vm::Error::BadInstruction { instruction: 0xaf } => (), + _ => assert!(false, "Expected bad instruction"), + } +} - assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000"); - assert_store(&ext, 1, "00000000000000000000000000000000000000000000000000000000000000ff"); - assert_eq!(gas_left, U256::from(74_976)); +evm_test! {test_pop: test_pop_int} +fn test_pop(factory: super::Factory) { + let code = "60f060aa50600055".from_hex().unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_store( + &ext, + 0, + "00000000000000000000000000000000000000000000000000000000000000f0", + ); + assert_eq!(gas_left, U256::from(79_989)); } -evm_test!{test_signextend: test_signextend_int} -fn test_signextend(factory: super::Factory) { - let code = "610fff60020b60005560ff60200b600155".from_hex().unwrap(); +evm_test! {test_extops: test_extops_int} +fn test_extops(factory: super::Factory) { + let code = "5a6001555836553a600255386003553460045560016001526016590454600555" + .from_hex() + .unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(150_000); + params.gas_price = U256::from(0x32); + params.value = ActionValue::Transfer(U256::from(0x99)); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_store( + &ext, + 0, + "0000000000000000000000000000000000000000000000000000000000000004", + ); // PC / CALLDATASIZE + assert_store( + &ext, + 1, + "00000000000000000000000000000000000000000000000000000000000249ee", + ); // GAS + assert_store( + &ext, + 2, + "0000000000000000000000000000000000000000000000000000000000000032", + ); // GASPRICE + assert_store( + &ext, + 3, + "0000000000000000000000000000000000000000000000000000000000000020", + ); // CODESIZE + assert_store( + &ext, + 4, + "0000000000000000000000000000000000000000000000000000000000000099", + ); // CALLVALUE + assert_store( + &ext, + 5, + "0000000000000000000000000000000000000000000000000000000000000032", + ); + assert_eq!(gas_left, U256::from(29_898)); +} - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); +evm_test! {test_jumps: test_jumps_int} +fn test_jumps(factory: super::Factory) { + let code = "600160015560066000555b60016000540380806000551560245760015402600155600a565b" + .from_hex() + .unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(150_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_eq!(ext.sstore_clears, ext.schedule().sstore_refund_gas as i128); + assert_store( + &ext, + 0, + "0000000000000000000000000000000000000000000000000000000000000000", + ); // 5! + assert_store( + &ext, + 1, + "0000000000000000000000000000000000000000000000000000000000000078", + ); // 5! + assert_eq!(gas_left, U256::from(54_117)); +} + +evm_test! {test_subs_simple: test_subs_simple_int} +fn test_subs_simple(factory: super::Factory) { + // as defined in https://eips.ethereum.org/EIPS/eip-2315 + let code = hex!("60045e005c5d").to_vec(); + + let mut params = ActionParams::default(); + params.gas = U256::from(18); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new_berlin(); - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; - assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000fff"); - assert_store(&ext, 1, "00000000000000000000000000000000000000000000000000000000000000ff"); - assert_eq!(gas_left, U256::from(59_972)); + assert_eq!(gas_left, U256::from(0)); } -#[test] // JIT just returns out of gas -fn test_badinstruction_int() { - let factory = super::Factory::new(VMType::Interpreter, 1024 * 32); - let code = "af".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let err = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap_err() - }; - - match err { - vm::Error::BadInstruction { instruction: 0xaf } => (), - _ => assert!(false, "Expected bad instruction") - } +evm_test! {test_subs_two_levels: test_subs_two_levels_int} +fn test_subs_two_levels(factory: super::Factory) { + // as defined in https://eips.ethereum.org/EIPS/eip-2315 + let code = hex!("6800000000000000000c5e005c60115e5d5c5d").to_vec(); + + let mut params = ActionParams::default(); + params.gas = U256::from(36); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new_berlin(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_eq!(gas_left, U256::from(0)); } -evm_test!{test_pop: test_pop_int} -fn test_pop(factory: super::Factory) { - let code = "60f060aa50600055".from_hex().unwrap(); +evm_test! {test_subs_invalid_jump: test_subs_invalid_jump_int} +fn test_subs_invalid_jump(factory: super::Factory) { + // as defined in https://eips.ethereum.org/EIPS/eip-2315 + let code = hex!("6801000000000000000c5e005c60115e5d5c5d").to_vec(); - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); + let mut params = ActionParams::default(); + params.gas = U256::from(24); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new_berlin(); - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; + let current = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()) + }; - assert_store(&ext, 0, "00000000000000000000000000000000000000000000000000000000000000f0"); - assert_eq!(gas_left, U256::from(79_989)); + let expected = Result::Err(vm::Error::BadJumpDestination { destination: 0xc }); + assert_eq!(current, expected); } -evm_test!{test_extops: test_extops_int} -fn test_extops(factory: super::Factory) { - let code = "5a6001555836553a600255386003553460045560016001526016590454600555".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.gas = U256::from(150_000); - params.gas_price = U256::from(0x32); - params.value = ActionValue::Transfer(U256::from(0x99)); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000004"); // PC / CALLDATASIZE - assert_store(&ext, 1, "00000000000000000000000000000000000000000000000000000000000249ee"); // GAS - assert_store(&ext, 2, "0000000000000000000000000000000000000000000000000000000000000032"); // GASPRICE - assert_store(&ext, 3, "0000000000000000000000000000000000000000000000000000000000000020"); // CODESIZE - assert_store(&ext, 4, "0000000000000000000000000000000000000000000000000000000000000099"); // CALLVALUE - assert_store(&ext, 5, "0000000000000000000000000000000000000000000000000000000000000032"); - assert_eq!(gas_left, U256::from(29_898)); +evm_test! {test_subs_shallow_return_stack: test_subs_shallow_return_stack_int} +fn test_subs_shallow_return_stack(factory: super::Factory) { + // as defined in https://eips.ethereum.org/EIPS/eip-2315 + let code = hex!("5d5858").to_vec(); + + let mut params = ActionParams::default(); + params.gas = U256::from(24); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new_berlin(); + + let current = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()) + }; + + let expected = Result::Err(vm::Error::SubStackUnderflow { + wanted: 1, + on_stack: 0, + }); + assert_eq!(current, expected); } -evm_test!{test_jumps: test_jumps_int} -fn test_jumps(factory: super::Factory) { - let code = "600160015560066000555b60016000540380806000551560245760015402600155600a565b".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.gas = U256::from(150_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_eq!(ext.sstore_clears, ext.schedule().sstore_refund_gas as i128); - assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000"); // 5! - assert_store(&ext, 1, "0000000000000000000000000000000000000000000000000000000000000078"); // 5! - assert_eq!(gas_left, U256::from(54_117)); +evm_test! {test_subs_substack_limit: test_subs_substack_limit_int} +fn test_subs_substack_limit(factory: super::Factory) { + // PUSH + // JUMP a + // s: BEGINSUB + // a: JUMPDEST + // DUP1 + // JUMPI c + // STOP + // c: JUMPDEST + // PUSH1 1 + // SWAP + // SUB + // JUMPSUB s + + let mut code = hex!("6104006007565c5b80600d57005b6001900360065e").to_vec(); + code[1..3].copy_from_slice(&(MAX_SUB_STACK_SIZE as u16).to_be_bytes()[..]); + + let mut params = ActionParams::default(); + params.gas = U256::from(1_000_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new_berlin(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_eq!(gas_left, U256::from(959_049)); +} + +evm_test! {test_subs_substack_out: test_subs_substack_out_int} +fn test_subs_substack_out(factory: super::Factory) { + let mut code = hex!("6104006007565c5b80600d57005b6001900360065e").to_vec(); + code[1..3].copy_from_slice(&((MAX_SUB_STACK_SIZE + 1) as u16).to_be_bytes()[..]); + + let mut params = ActionParams::default(); + params.gas = U256::from(1_000_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new_berlin(); + + let current = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()) + }; + + let expected = Result::Err(vm::Error::OutOfSubStack { + wanted: 1, + limit: MAX_SUB_STACK_SIZE, + }); + assert_eq!(current, expected); +} + +evm_test! {test_subs_sub_at_end: test_subs_sub_at_end_int} +fn test_subs_sub_at_end(factory: super::Factory) { + let code = hex!("6005565c5d5b60035e").to_vec(); + + let mut params = ActionParams::default(); + params.gas = U256::from(30); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new_berlin(); + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_eq!(gas_left, U256::from(0)); +} + +evm_test! {test_subs_walk_into_subroutine: test_subs_walk_into_subroutine_int} +fn test_subs_walk_into_subroutine(factory: super::Factory) { + let code = hex!("5c5d00").to_vec(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new_berlin(); + + let current = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()) + }; + + let expected = Result::Err(vm::Error::InvalidSubEntry); + assert_eq!(current, expected); } -evm_test!{test_calls: test_calls_int} +evm_test! {test_calls: test_calls_int} fn test_calls(factory: super::Factory) { - let code = "600054602d57600160005560006000600060006050610998610100f160006000600060006050610998610100f25b".from_hex().unwrap(); - - let address = Address::from(0x155); - let code_address = Address::from(0x998); - let mut params = ActionParams::default(); - params.gas = U256::from(150_000); - params.code = Some(Arc::new(code)); - params.address = address.clone(); - let mut ext = FakeExt::new(); - ext.balances = { - let mut s = HashMap::new(); - s.insert(params.address.clone(), params.gas); - s - }; - - let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_set_contains(&ext.calls, &FakeCall { - call_type: FakeCallType::Call, - create_scheme: None, - gas: U256::from(2556), - sender_address: Some(address.clone()), - receive_address: Some(code_address.clone()), - value: Some(U256::from(0x50)), - data: vec!(), - code_address: Some(code_address.clone()) - }); - assert_set_contains(&ext.calls, &FakeCall { - call_type: FakeCallType::Call, - create_scheme: None, - gas: U256::from(2556), - sender_address: Some(address.clone()), - receive_address: Some(address.clone()), - value: Some(U256::from(0x50)), - data: vec!(), - code_address: Some(code_address.clone()) - }); - assert_eq!(gas_left, U256::from(91_405)); - assert_eq!(ext.calls.len(), 2); + let code = "600054602d57600160005560006000600060006050610998610100f160006000600060006050610998610100f25b".from_hex().unwrap(); + + let address = Address::from(0x155); + let code_address = Address::from(0x998); + let mut params = ActionParams::default(); + params.gas = U256::from(150_000); + params.code = Some(Arc::new(code)); + params.address = address.clone(); + let mut ext = FakeExt::new(); + ext.balances = { + let mut s = HashMap::new(); + s.insert(params.address.clone(), params.gas); + s + }; + + let gas_left = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_set_contains( + &ext.calls, + &FakeCall { + call_type: FakeCallType::Call, + create_scheme: None, + gas: U256::from(2556), + sender_address: Some(address.clone()), + receive_address: Some(code_address.clone()), + value: Some(U256::from(0x50)), + data: vec![], + code_address: Some(code_address.clone()), + }, + ); + assert_set_contains( + &ext.calls, + &FakeCall { + call_type: FakeCallType::Call, + create_scheme: None, + gas: U256::from(2556), + sender_address: Some(address.clone()), + receive_address: Some(address.clone()), + value: Some(U256::from(0x50)), + data: vec![], + code_address: Some(code_address.clone()), + }, + ); + assert_eq!(gas_left, U256::from(91_405)); + assert_eq!(ext.calls.len(), 2); } -evm_test!{test_create_in_staticcall: test_create_in_staticcall_int} +evm_test! {test_create_in_staticcall: test_create_in_staticcall_int} fn test_create_in_staticcall(factory: super::Factory) { - let code = "600060006064f000".from_hex().unwrap(); - - let address = Address::from(0x155); - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - params.address = address.clone(); - let mut ext = FakeExt::new_byzantium(); - ext.is_static = true; - - let err = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap_err() - }; - - assert_eq!(err, vm::Error::MutableCallInStaticContext); - assert_eq!(ext.calls.len(), 0); + let code = "600060006064f000".from_hex().unwrap(); + + let address = Address::from(0x155); + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + params.address = address.clone(); + let mut ext = FakeExt::new_byzantium(); + ext.is_static = true; + + let err = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap_err() + }; + + assert_eq!(err, vm::Error::MutableCallInStaticContext); + assert_eq!(ext.calls.len(), 0); } -evm_test!{test_shl: test_shl_int} +evm_test! {test_shl: test_shl_int} fn test_shl(factory: super::Factory) { - push_two_pop_one_constantinople_test( - &factory, - 0x1b, - "0000000000000000000000000000000000000000000000000000000000000001", - "00", - "0000000000000000000000000000000000000000000000000000000000000001"); - push_two_pop_one_constantinople_test( - &factory, - 0x1b, - "0000000000000000000000000000000000000000000000000000000000000001", - "01", - "0000000000000000000000000000000000000000000000000000000000000002"); - push_two_pop_one_constantinople_test( - &factory, - 0x1b, - "0000000000000000000000000000000000000000000000000000000000000001", - "ff", - "8000000000000000000000000000000000000000000000000000000000000000"); - push_two_pop_one_constantinople_test( - &factory, - 0x1b, - "0000000000000000000000000000000000000000000000000000000000000001", - "0100", - "0000000000000000000000000000000000000000000000000000000000000000"); - push_two_pop_one_constantinople_test( - &factory, - 0x1b, - "0000000000000000000000000000000000000000000000000000000000000001", - "0101", - "0000000000000000000000000000000000000000000000000000000000000000"); - push_two_pop_one_constantinople_test( - &factory, - 0x1b, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "00", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - push_two_pop_one_constantinople_test( - &factory, - 0x1b, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "01", - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"); - push_two_pop_one_constantinople_test( - &factory, - 0x1b, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ff", - "8000000000000000000000000000000000000000000000000000000000000000"); - push_two_pop_one_constantinople_test( - &factory, - 0x1b, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "0100", - "0000000000000000000000000000000000000000000000000000000000000000"); - push_two_pop_one_constantinople_test( - &factory, - 0x1b, - "0000000000000000000000000000000000000000000000000000000000000000", - "01", - "0000000000000000000000000000000000000000000000000000000000000000"); - push_two_pop_one_constantinople_test( - &factory, - 0x1b, - "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "01", - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"); + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "0000000000000000000000000000000000000000000000000000000000000001", + "00", + "0000000000000000000000000000000000000000000000000000000000000001", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "0000000000000000000000000000000000000000000000000000000000000001", + "01", + "0000000000000000000000000000000000000000000000000000000000000002", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "0000000000000000000000000000000000000000000000000000000000000001", + "ff", + "8000000000000000000000000000000000000000000000000000000000000000", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "0000000000000000000000000000000000000000000000000000000000000001", + "0100", + "0000000000000000000000000000000000000000000000000000000000000000", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "0000000000000000000000000000000000000000000000000000000000000001", + "0101", + "0000000000000000000000000000000000000000000000000000000000000000", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "00", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "01", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ff", + "8000000000000000000000000000000000000000000000000000000000000000", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0100", + "0000000000000000000000000000000000000000000000000000000000000000", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "0000000000000000000000000000000000000000000000000000000000000000", + "01", + "0000000000000000000000000000000000000000000000000000000000000000", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1b, + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "01", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + ); } -evm_test!{test_shr: test_shr_int} +evm_test! {test_shr: test_shr_int} fn test_shr(factory: super::Factory) { - push_two_pop_one_constantinople_test( - &factory, - 0x1c, - "0000000000000000000000000000000000000000000000000000000000000001", - "00", - "0000000000000000000000000000000000000000000000000000000000000001"); - push_two_pop_one_constantinople_test( - &factory, - 0x1c, - "0000000000000000000000000000000000000000000000000000000000000001", - "01", - "0000000000000000000000000000000000000000000000000000000000000000"); - push_two_pop_one_constantinople_test( - &factory, - 0x1c, - "8000000000000000000000000000000000000000000000000000000000000000", - "01", - "4000000000000000000000000000000000000000000000000000000000000000"); - push_two_pop_one_constantinople_test( - &factory, - 0x1c, - "8000000000000000000000000000000000000000000000000000000000000000", - "ff", - "0000000000000000000000000000000000000000000000000000000000000001"); - push_two_pop_one_constantinople_test( - &factory, - 0x1c, - "8000000000000000000000000000000000000000000000000000000000000000", - "0100", - "0000000000000000000000000000000000000000000000000000000000000000"); - push_two_pop_one_constantinople_test( - &factory, - 0x1c, - "8000000000000000000000000000000000000000000000000000000000000000", - "0101", - "0000000000000000000000000000000000000000000000000000000000000000"); - push_two_pop_one_constantinople_test( - &factory, - 0x1c, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "00", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - push_two_pop_one_constantinople_test( - &factory, - 0x1c, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "01", - "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - push_two_pop_one_constantinople_test( - &factory, - 0x1c, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ff", - "0000000000000000000000000000000000000000000000000000000000000001"); - push_two_pop_one_constantinople_test( - &factory, - 0x1c, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "0100", - "0000000000000000000000000000000000000000000000000000000000000000"); - push_two_pop_one_constantinople_test( - &factory, - 0x1c, - "0000000000000000000000000000000000000000000000000000000000000000", - "01", - "0000000000000000000000000000000000000000000000000000000000000000"); + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "0000000000000000000000000000000000000000000000000000000000000001", + "00", + "0000000000000000000000000000000000000000000000000000000000000001", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "0000000000000000000000000000000000000000000000000000000000000001", + "01", + "0000000000000000000000000000000000000000000000000000000000000000", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "8000000000000000000000000000000000000000000000000000000000000000", + "01", + "4000000000000000000000000000000000000000000000000000000000000000", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "8000000000000000000000000000000000000000000000000000000000000000", + "ff", + "0000000000000000000000000000000000000000000000000000000000000001", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "8000000000000000000000000000000000000000000000000000000000000000", + "0100", + "0000000000000000000000000000000000000000000000000000000000000000", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "8000000000000000000000000000000000000000000000000000000000000000", + "0101", + "0000000000000000000000000000000000000000000000000000000000000000", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "00", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "01", + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ff", + "0000000000000000000000000000000000000000000000000000000000000001", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0100", + "0000000000000000000000000000000000000000000000000000000000000000", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1c, + "0000000000000000000000000000000000000000000000000000000000000000", + "01", + "0000000000000000000000000000000000000000000000000000000000000000", + ); } -evm_test!{test_sar: test_sar_int} +evm_test! {test_sar: test_sar_int} fn test_sar(factory: super::Factory) { - push_two_pop_one_constantinople_test( - &factory, - 0x1d, - "0000000000000000000000000000000000000000000000000000000000000001", - "00", - "0000000000000000000000000000000000000000000000000000000000000001"); - push_two_pop_one_constantinople_test( - &factory, - 0x1d, - "0000000000000000000000000000000000000000000000000000000000000001", - "01", - "0000000000000000000000000000000000000000000000000000000000000000"); - push_two_pop_one_constantinople_test( - &factory, - 0x1d, - "8000000000000000000000000000000000000000000000000000000000000000", - "01", - "c000000000000000000000000000000000000000000000000000000000000000"); - push_two_pop_one_constantinople_test( - &factory, - 0x1d, - "8000000000000000000000000000000000000000000000000000000000000000", - "ff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - push_two_pop_one_constantinople_test( - &factory, - 0x1d, - "8000000000000000000000000000000000000000000000000000000000000000", - "0100", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - push_two_pop_one_constantinople_test( - &factory, - 0x1d, - "8000000000000000000000000000000000000000000000000000000000000000", - "0101", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - push_two_pop_one_constantinople_test( - &factory, - 0x1d, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "00", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - push_two_pop_one_constantinople_test( - &factory, - 0x1d, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "01", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - push_two_pop_one_constantinople_test( - &factory, - 0x1d, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - push_two_pop_one_constantinople_test( - &factory, - 0x1d, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "0100", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - push_two_pop_one_constantinople_test( - &factory, - 0x1d, - "0000000000000000000000000000000000000000000000000000000000000000", - "01", - "0000000000000000000000000000000000000000000000000000000000000000"); - push_two_pop_one_constantinople_test( - &factory, - 0x1d, - "4000000000000000000000000000000000000000000000000000000000000000", - "fe", - "0000000000000000000000000000000000000000000000000000000000000001"); - push_two_pop_one_constantinople_test( - &factory, - 0x1d, - "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "f8", - "000000000000000000000000000000000000000000000000000000000000007f"); - push_two_pop_one_constantinople_test( - &factory, - 0x1d, - "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "fe", - "0000000000000000000000000000000000000000000000000000000000000001"); - push_two_pop_one_constantinople_test( - &factory, - 0x1d, - "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ff", - "0000000000000000000000000000000000000000000000000000000000000000"); - push_two_pop_one_constantinople_test( - &factory, - 0x1d, - "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "0100", - "0000000000000000000000000000000000000000000000000000000000000000"); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "0000000000000000000000000000000000000000000000000000000000000001", + "00", + "0000000000000000000000000000000000000000000000000000000000000001", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "0000000000000000000000000000000000000000000000000000000000000001", + "01", + "0000000000000000000000000000000000000000000000000000000000000000", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "8000000000000000000000000000000000000000000000000000000000000000", + "01", + "c000000000000000000000000000000000000000000000000000000000000000", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "8000000000000000000000000000000000000000000000000000000000000000", + "ff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "8000000000000000000000000000000000000000000000000000000000000000", + "0100", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "8000000000000000000000000000000000000000000000000000000000000000", + "0101", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "00", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "01", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0100", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "0000000000000000000000000000000000000000000000000000000000000000", + "01", + "0000000000000000000000000000000000000000000000000000000000000000", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "4000000000000000000000000000000000000000000000000000000000000000", + "fe", + "0000000000000000000000000000000000000000000000000000000000000001", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "f8", + "000000000000000000000000000000000000000000000000000000000000007f", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "fe", + "0000000000000000000000000000000000000000000000000000000000000001", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ff", + "0000000000000000000000000000000000000000000000000000000000000000", + ); + push_two_pop_one_constantinople_test( + &factory, + 0x1d, + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0100", + "0000000000000000000000000000000000000000000000000000000000000000", + ); } -fn push_two_pop_one_constantinople_test(factory: &super::Factory, opcode: u8, push1: &str, push2: &str, result: &str) { - let mut push1 = push1.from_hex().unwrap(); - let mut push2 = push2.from_hex().unwrap(); - assert!(push1.len() <= 32 && push1.len() != 0); - assert!(push2.len() <= 32 && push2.len() != 0); - - let mut code = Vec::new(); - code.push(0x60 + ((push1.len() - 1) as u8)); - code.append(&mut push1); - code.push(0x60 + ((push2.len() - 1) as u8)); - code.append(&mut push2); - code.push(opcode); - code.append(&mut vec![0x60, 0x00, 0x55]); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new_constantinople(); - - let _ = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); - test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() - }; - - assert_store(&ext, 0, result); +fn push_two_pop_one_constantinople_test( + factory: &super::Factory, + opcode: u8, + push1: &str, + push2: &str, + result: &str, +) { + let mut push1 = push1.from_hex().unwrap(); + let mut push2 = push2.from_hex().unwrap(); + assert!(push1.len() <= 32 && push1.len() != 0); + assert!(push2.len() <= 32 && push2.len() != 0); + + let mut code = Vec::new(); + code.push(0x60 + ((push1.len() - 1) as u8)); + code.append(&mut push1); + code.push(0x60 + ((push2.len() - 1) as u8)); + code.append(&mut push2); + code.push(opcode); + code.append(&mut vec![0x60, 0x00, 0x55]); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new_constantinople(); + + let _ = { + let vm = factory.create(params, ext.schedule(), ext.depth()); + test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() + }; + + assert_store(&ext, 0, result); } -fn assert_set_contains(set: &HashSet, val: &T) { - let contains = set.contains(val); - if !contains { - println!("Set: {:?}", set); - println!("Elem: {:?}", val); - } - assert!(contains, "Element not found in HashSet"); +fn assert_set_contains(set: &HashSet, val: &T) { + let contains = set.contains(val); + if !contains { + println!("Set: {:?}", set); + println!("Elem: {:?}", val); + } + assert!(contains, "Element not found in HashSet"); } fn assert_store(ext: &FakeExt, pos: u64, val: &str) { - assert_eq!(ext.store.get(&H256::from(pos)).unwrap(), &H256::from_str(val).unwrap()); + assert_eq!( + ext.store.get(&H256::from(pos)).unwrap(), + &H256::from_str(val).unwrap() + ); } diff --git a/ethcore/evm/src/vmtype.rs b/ethcore/evm/src/vmtype.rs index 2ae40e2c110..2a953bbf587 100644 --- a/ethcore/evm/src/vmtype.rs +++ b/ethcore/evm/src/vmtype.rs @@ -1,45 +1,49 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use std::fmt; /// Type of EVM to use. #[derive(Debug, PartialEq, Clone)] pub enum VMType { - /// RUST EVM - Interpreter + /// RUST EVM + Interpreter, } impl fmt::Display for VMType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", match *self { - VMType::Interpreter => "INT" - }) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}", + match *self { + VMType::Interpreter => "INT", + } + ) + } } impl Default for VMType { - fn default() -> Self { - VMType::Interpreter - } + fn default() -> Self { + VMType::Interpreter + } } impl VMType { - /// Return all possible VMs (Interpreter) - pub fn all() -> Vec { - vec![VMType::Interpreter] - } + /// Return all possible VMs (Interpreter) + pub fn all() -> Vec { + vec![VMType::Interpreter] + } } diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml deleted file mode 100644 index 756b76f1f5d..00000000000 --- a/ethcore/light/Cargo.toml +++ /dev/null @@ -1,52 +0,0 @@ -[package] -description = "Parity Light Client Implementation" -homepage = "http://parity.io" -license = "GPL-3.0" -name = "ethcore-light" -version = "1.12.0" -authors = ["Parity Technologies "] - -[dependencies] -log = "0.4" -parity-bytes = "0.1" -common-types = { path = "../types" } -ethcore = { path = ".."} -ethcore-db = { path = "../db" } -ethcore-blockchain = { path = "../blockchain" } -ethereum-types = "0.4" -memory-db = "0.11.0" -trie-db = "0.11.0" -patricia-trie-ethereum = { path = "../../util/patricia-trie-ethereum" } -ethcore-network = { path = "../../util/network" } -ethcore-io = { path = "../../util/io" } -hash-db = "0.11.0" -heapsize = "0.4" -vm = { path = "../vm" } -fastmap = { path = "../../util/fastmap" } -failsafe = { version = "0.3.0", default-features = false, features = ["parking_lot_mutex"] } -rlp = { version = "0.3.0", features = ["ethereum"] } -rlp_derive = { path = "../../util/rlp-derive" } -smallvec = "0.6" -futures = "0.1" -rand = "0.4" -itertools = "0.5" -bincode = "0.8.0" -serde = "1.0" -serde_derive = "1.0" -parking_lot = "0.7" -stats = { path = "../../util/stats" } -keccak-hash = "0.1" -keccak-hasher = { path = "../../util/keccak-hasher" } -triehash-ethereum = { version = "0.2", path = "../../util/triehash-ethereum" } -kvdb = "0.1" -memory-cache = { path = "../../util/memory-cache" } -error-chain = { version = "0.12", default-features = false } -journaldb = { path = "../../util/journaldb" } - -[dev-dependencies] -ethcore = { path = "..", features = ["test-helpers"] } -kvdb-memorydb = "0.1" -tempdir = "0.3" - -[features] -default = [] diff --git a/ethcore/light/src/cache.rs b/ethcore/light/src/cache.rs deleted file mode 100644 index d75e0ff7d31..00000000000 --- a/ethcore/light/src/cache.rs +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Cache for data fetched from the network. -//! -//! Stores ancient block headers, bodies, receipts, and total difficulties. -//! Furthermore, stores a "gas price corpus" of relative recency, which is a sorted -//! vector of all gas prices from a recent range of blocks. - -use std::time::{Instant, Duration}; - -use common_types::encoded; -use common_types::BlockNumber; -use common_types::receipt::Receipt; -use ethereum_types::{H256, U256}; -use heapsize::HeapSizeOf; -use memory_cache::MemoryLruCache; -use stats::Corpus; - -/// Configuration for how much data to cache. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct CacheSizes { - /// Maximum size, in bytes, of cached headers. - pub headers: usize, - /// Maximum size, in bytes, of cached canonical hashes. - pub canon_hashes: usize, - /// Maximum size, in bytes, of cached block bodies. - pub bodies: usize, - /// Maximum size, in bytes, of cached block receipts. - pub receipts: usize, - /// Maximum size, in bytes, of cached chain score for the block. - pub chain_score: usize, -} - -impl Default for CacheSizes { - fn default() -> Self { - const MB: usize = 1024 * 1024; - CacheSizes { - headers: 10 * MB, - canon_hashes: 3 * MB, - bodies: 20 * MB, - receipts: 10 * MB, - chain_score: 7 * MB, - } - } -} - -/// The light client data cache. -/// -/// Note that almost all getter methods take `&mut self` due to the necessity to update -/// the underlying LRU-caches on read. -/// [LRU-cache](https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_Recently_Used_.28LRU.29) -pub struct Cache { - headers: MemoryLruCache, - canon_hashes: MemoryLruCache, - bodies: MemoryLruCache, - receipts: MemoryLruCache>, - chain_score: MemoryLruCache, - corpus: Option<(Corpus, Instant)>, - corpus_expiration: Duration, -} - -impl Cache { - /// Create a new data cache with the given sizes and gas price corpus expiration time. - pub fn new(sizes: CacheSizes, corpus_expiration: Duration) -> Self { - Cache { - headers: MemoryLruCache::new(sizes.headers), - canon_hashes: MemoryLruCache::new(sizes.canon_hashes), - bodies: MemoryLruCache::new(sizes.bodies), - receipts: MemoryLruCache::new(sizes.receipts), - chain_score: MemoryLruCache::new(sizes.chain_score), - corpus: None, - corpus_expiration, - } - } - - /// Query header by hash. - pub fn block_header(&mut self, hash: &H256) -> Option { - self.headers.get_mut(hash).cloned() - } - - /// Query hash by number. - pub fn block_hash(&mut self, num: BlockNumber) -> Option { - self.canon_hashes.get_mut(&num).map(|h| *h) - } - - /// Query block body by block hash. - pub fn block_body(&mut self, hash: &H256) -> Option { - self.bodies.get_mut(hash).cloned() - } - - /// Query block receipts by block hash. - pub fn block_receipts(&mut self, hash: &H256) -> Option> { - self.receipts.get_mut(hash).cloned() - } - - /// Query chain score by block hash. - pub fn chain_score(&mut self, hash: &H256) -> Option { - self.chain_score.get_mut(hash).map(|h| *h) - } - - /// Cache the given header. - pub fn insert_block_header(&mut self, hash: H256, hdr: encoded::Header) { - self.headers.insert(hash, hdr); - } - - /// Cache the given canonical block hash. - pub fn insert_block_hash(&mut self, num: BlockNumber, hash: H256) { - self.canon_hashes.insert(num, hash); - } - - /// Cache the given block body. - pub fn insert_block_body(&mut self, hash: H256, body: encoded::Body) { - self.bodies.insert(hash, body); - } - - /// Cache the given block receipts. - pub fn insert_block_receipts(&mut self, hash: H256, receipts: Vec) { - self.receipts.insert(hash, receipts); - } - - /// Cache the given chain scoring. - pub fn insert_chain_score(&mut self, hash: H256, score: U256) { - self.chain_score.insert(hash, score); - } - - /// Get gas price corpus, if recent enough. - pub fn gas_price_corpus(&self) -> Option> { - let now = Instant::now(); - - self.corpus.as_ref().and_then(|&(ref corpus, ref tm)| { - if *tm + self.corpus_expiration >= now { - Some(corpus.clone()) - } else { - None - } - }) - } - - /// Set the cached gas price corpus. - pub fn set_gas_price_corpus(&mut self, corpus: Corpus) { - self.corpus = Some((corpus, Instant::now())) - } - - /// Get the memory used. - pub fn mem_used(&self) -> usize { - self.heap_size_of_children() - } -} - -impl HeapSizeOf for Cache { - fn heap_size_of_children(&self) -> usize { - self.headers.current_size() - + self.canon_hashes.current_size() - + self.bodies.current_size() - + self.receipts.current_size() - + self.chain_score.current_size() - // TODO: + corpus - } -} - -#[cfg(test)] -mod tests { - use super::Cache; - use std::time::Duration; - - #[test] - fn corpus_inaccessible() { - let duration = Duration::from_secs(20); - let mut cache = Cache::new(Default::default(), duration.clone()); - - cache.set_gas_price_corpus(vec![].into()); - assert_eq!(cache.gas_price_corpus(), Some(vec![].into())); - - { - let corpus_time = &mut cache.corpus.as_mut().unwrap().1; - *corpus_time = *corpus_time - duration; - } - assert!(cache.gas_price_corpus().is_none()); - } -} diff --git a/ethcore/light/src/cht.rs b/ethcore/light/src/cht.rs deleted file mode 100644 index a9bc5d7f260..00000000000 --- a/ethcore/light/src/cht.rs +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Canonical hash trie definitions and helper functions. -//! -//! Each CHT is a trie mapping block numbers to canonical hashes and total difficulty. -//! One is generated for every `SIZE` blocks, allowing us to discard those blocks in -//! favor of the trie root. When the "ancient" blocks need to be accessed, we simply -//! request an inclusion proof of a specific block number against the trie with the -//! root has. A correct proof implies that the claimed block is identical to the one -//! we discarded. - -use common_types::ids::BlockId; -use ethereum_types::{H256, U256}; -use hash_db::HashDB; -use keccak_hasher::KeccakHasher; -use kvdb::DBValue; -use memory_db::MemoryDB; -use journaldb::new_memory_db; -use bytes::Bytes; -use trie::{TrieMut, Trie, Recorder}; -use ethtrie::{self, TrieDB, TrieDBMut}; -use rlp::{RlpStream, Rlp}; - -// encode a key. -macro_rules! key { - ($num: expr) => { ::rlp::encode(&$num) } -} - -macro_rules! val { - ($hash: expr, $td: expr) => {{ - let mut stream = RlpStream::new_list(2); - stream.append(&$hash).append(&$td); - stream.drain() - }} -} - -/// The size of each CHT. -pub const SIZE: u64 = 2048; - -/// A canonical hash trie. This is generic over any database it can query. -/// See module docs for more details. -#[derive(Debug, Clone)] -pub struct CHT> { - db: DB, - root: H256, // the root of this CHT. - number: u64, -} - -impl> CHT { - /// Query the root of the CHT. - pub fn root(&self) -> H256 { self.root } - - /// Query the number of the CHT. - pub fn number(&self) -> u64 { self.number } - - /// Generate an inclusion proof for the entry at a specific block. - /// Nodes before level `from_level` will be omitted. - /// Returns an error on an incomplete trie, and `Ok(None)` on an unprovable request. - pub fn prove(&self, num: u64, from_level: u32) -> ethtrie::Result>> { - if block_to_cht_number(num) != Some(self.number) { return Ok(None) } - - let mut recorder = Recorder::with_depth(from_level); - let db: &HashDB<_,_> = &self.db; - let t = TrieDB::new(&db, &self.root)?; - t.get_with(&key!(num), &mut recorder)?; - - Ok(Some(recorder.drain().into_iter().map(|x| x.data).collect())) - } -} - -/// Block information necessary to build a CHT. -pub struct BlockInfo { - /// The block's hash. - pub hash: H256, - /// The block's parent's hash. - pub parent_hash: H256, - /// The block's total difficulty. - pub total_difficulty: U256, -} - -/// Build an in-memory CHT from a closure which provides necessary information -/// about blocks. If the fetcher ever fails to provide the info, the CHT -/// will not be generated. -pub fn build(cht_num: u64, mut fetcher: F) -> Option>> - where F: FnMut(BlockId) -> Option -{ - let mut db = new_memory_db(); - - // start from the last block by number and work backwards. - let last_num = start_number(cht_num + 1) - 1; - let mut id = BlockId::Number(last_num); - - let mut root = H256::default(); - - { - let mut t = TrieDBMut::new(&mut db, &mut root); - for blk_num in (0..SIZE).map(|n| last_num - n) { - let info = match fetcher(id) { - Some(info) => info, - None => return None, - }; - - id = BlockId::Hash(info.parent_hash); - t.insert(&key!(blk_num), &val!(info.hash, info.total_difficulty)) - .expect("fresh in-memory database is infallible; qed"); - } - } - - Some(CHT { - db, - root, - number: cht_num, - }) -} - -/// Compute a CHT root from an iterator of (hash, td) pairs. Fails if shorter than -/// SIZE items. The items are assumed to proceed sequentially from `start_number(cht_num)`. -/// Discards the trie's nodes. -pub fn compute_root(cht_num: u64, iterable: I) -> Option - where I: IntoIterator -{ - let mut v = Vec::with_capacity(SIZE as usize); - let start_num = start_number(cht_num) as usize; - - for (i, (h, td)) in iterable.into_iter().take(SIZE as usize).enumerate() { - v.push((key!(i + start_num), val!(h, td))) - } - - if v.len() == SIZE as usize { - Some(::triehash::trie_root(v)) - } else { - None - } -} - -/// Check a proof for a CHT. -/// Given a set of a trie nodes, a number to query, and a trie root, -/// verify the given trie branch and extract the canonical hash and total difficulty. -// TODO: better support for partially-checked queries. -pub fn check_proof(proof: &[Bytes], num: u64, root: H256) -> Option<(H256, U256)> { - let mut db = new_memory_db(); - - for node in proof { db.insert(&node[..]); } - let res = match TrieDB::new(&db, &root) { - Err(_) => return None, - Ok(trie) => trie.get_with(&key!(num), |val: &[u8]| { - let rlp = Rlp::new(val); - rlp.val_at::(0) - .and_then(|h| rlp.val_at::(1).map(|td| (h, td))) - .ok() - }) - }; - - match res { - Ok(Some(Some((hash, td)))) => Some((hash, td)), - _ => None, - } -} - -/// Convert a block number to a CHT number. -/// Returns `None` for `block_num` == 0, `Some` otherwise. -pub fn block_to_cht_number(block_num: u64) -> Option { - match block_num { - 0 => None, - n => Some((n - 1) / SIZE), - } -} - -/// Get the starting block of a given CHT. -/// CHT 0 includes block 1...SIZE, -/// CHT 1 includes block SIZE + 1 ... 2*SIZE -/// More generally: CHT N includes block (1 + N*SIZE)...((N+1)*SIZE). -/// This is because the genesis hash is assumed to be known -/// and including it would be redundant. -pub fn start_number(cht_num: u64) -> u64 { - (cht_num * SIZE) + 1 -} - -#[cfg(test)] -mod tests { - #[test] - fn size_is_lt_usize() { - // to ensure safe casting on the target platform. - assert!(::cht::SIZE < usize::max_value() as u64) - } - - #[test] - fn block_to_cht_number() { - assert!(::cht::block_to_cht_number(0).is_none()); - assert_eq!(::cht::block_to_cht_number(1).unwrap(), 0); - assert_eq!(::cht::block_to_cht_number(::cht::SIZE + 1).unwrap(), 1); - assert_eq!(::cht::block_to_cht_number(::cht::SIZE).unwrap(), 0); - } - - #[test] - fn start_number() { - assert_eq!(::cht::start_number(0), 1); - assert_eq!(::cht::start_number(1), ::cht::SIZE + 1); - assert_eq!(::cht::start_number(2), ::cht::SIZE * 2 + 1); - } -} diff --git a/ethcore/light/src/client/fetch.rs b/ethcore/light/src/client/fetch.rs deleted file mode 100644 index 86a3770bf8a..00000000000 --- a/ethcore/light/src/client/fetch.rs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Trait for fetching chain data. - -use std::sync::Arc; - -use common_types::encoded; -use common_types::header::Header; -use common_types::receipt::Receipt; -use ethcore::engines::{EthEngine, StateDependentProof}; -use ethcore::machine::EthereumMachine; -use ethereum_types::H256; -use futures::future::IntoFuture; - -/// Provides full chain data. -pub trait ChainDataFetcher: Send + Sync + 'static { - /// Error type when data unavailable. - type Error: ::std::fmt::Debug; - - /// Future for fetching block body. - type Body: IntoFuture; - /// Future for fetching block receipts. - type Receipts: IntoFuture, Error=Self::Error>; - /// Future for fetching epoch transition - type Transition: IntoFuture, Error=Self::Error>; - - /// Fetch a block body. - fn block_body(&self, header: &Header) -> Self::Body; - - /// Fetch block receipts. - fn block_receipts(&self, header: &Header) -> Self::Receipts; - - /// Fetch epoch transition proof at given header. - fn epoch_transition( - &self, - _hash: H256, - _engine: Arc, - _checker: Arc> - ) -> Self::Transition; -} - -/// Fetcher implementation which cannot fetch anything. -pub struct Unavailable; - -/// Create a fetcher which has all data unavailable. -pub fn unavailable() -> Unavailable { Unavailable } - -impl ChainDataFetcher for Unavailable { - type Error = &'static str; - - type Body = Result; - type Receipts = Result, &'static str>; - type Transition = Result, &'static str>; - - fn block_body(&self, _header: &Header) -> Self::Body { - Err("fetching block bodies unavailable") - } - - fn block_receipts(&self, _header: &Header) -> Self::Receipts { - Err("fetching block receipts unavailable") - } - - fn epoch_transition( - &self, - _hash: H256, - _engine: Arc, - _checker: Arc> - ) -> Self::Transition { - Err("fetching epoch transition proofs unavailable") - } -} diff --git a/ethcore/light/src/client/header_chain.rs b/ethcore/light/src/client/header_chain.rs deleted file mode 100644 index b72a099d08d..00000000000 --- a/ethcore/light/src/client/header_chain.rs +++ /dev/null @@ -1,1225 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Light client header chain. -//! -//! Unlike a full node's `BlockChain` this doesn't store much in the database. -//! It stores candidates for the last 2048-4096 blocks as well as CHT roots for -//! historical blocks all the way to the genesis. If the engine makes use -//! of epoch transitions, those are stored as well. -//! -//! This is separate from the `BlockChain` for two reasons: -//! - It stores only headers (and a pruned subset of them) -//! - To allow for flexibility in the database layout.. - -use std::collections::BTreeMap; -use std::sync::Arc; - -use cache::Cache; -use cht; -use common_types::block_status::BlockStatus; -use common_types::encoded; -use common_types::header::Header; -use common_types::ids::BlockId; -use ethcore::engines::epoch::{Transition as EpochTransition, PendingTransition as PendingEpochTransition}; -use ethcore::error::{Error, EthcoreResult, ErrorKind as EthcoreErrorKind, BlockError}; -use ethcore::spec::{Spec, SpecHardcodedSync}; -use ethereum_types::{H256, H264, U256}; -use heapsize::HeapSizeOf; -use kvdb::{DBTransaction, KeyValueDB}; -use parking_lot::{Mutex, RwLock}; -use fastmap::H256FastMap; -use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp}; -use smallvec::SmallVec; - -/// Store at least this many candidate headers at all times. -/// Also functions as the delay for computing CHTs as they aren't -/// relevant to any blocks we've got in memory. -const HISTORY: u64 = 2048; - -/// The best block key. Maps to an RLP list: [best_era, last_era] -const CURRENT_KEY: &[u8] = &*b"best_and_latest"; - -/// Key storing the last canonical epoch transition. -const LAST_CANONICAL_TRANSITION: &[u8] = &*b"canonical_transition"; - -/// Information about a block. -#[derive(Debug, Clone)] -pub struct BlockDescriptor { - /// The block's hash - pub hash: H256, - /// The block's number - pub number: u64, - /// The block's total difficulty. - pub total_difficulty: U256, -} - -// best block data -#[derive(RlpEncodable, RlpDecodable)] -struct BestAndLatest { - best_num: u64, - latest_num: u64 -} - -impl BestAndLatest { - fn new(best_num: u64, latest_num: u64) -> Self { - BestAndLatest { - best_num, - latest_num, - } - } -} - -// candidate block description. -struct Candidate { - hash: H256, - parent_hash: H256, - total_difficulty: U256, -} - -struct Entry { - candidates: SmallVec<[Candidate; 3]>, // 3 arbitrarily chosen - canonical_hash: H256, -} - -impl HeapSizeOf for Entry { - fn heap_size_of_children(&self) -> usize { - if self.candidates.spilled() { - self.candidates.capacity() * ::std::mem::size_of::() - } else { - 0 - } - } -} - -impl Encodable for Entry { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(self.candidates.len()); - - for candidate in &self.candidates { - s.begin_list(3) - .append(&candidate.hash) - .append(&candidate.parent_hash) - .append(&candidate.total_difficulty); - } - } -} - -impl Decodable for Entry { - fn decode(rlp: &Rlp) -> Result { - let mut candidates = SmallVec::<[Candidate; 3]>::new(); - - for item in rlp.iter() { - candidates.push(Candidate { - hash: item.val_at(0)?, - parent_hash: item.val_at(1)?, - total_difficulty: item.val_at(2)?, - }) - } - - if candidates.is_empty() { return Err(DecoderError::Custom("Empty candidates vector submitted.")) } - - // rely on the invariant that the canonical entry is always first. - let canon_hash = candidates[0].hash; - Ok(Entry { - candidates, - canonical_hash: canon_hash, - }) - } -} - -fn cht_key(number: u64) -> String { - format!("{:08x}_canonical", number) -} - -fn era_key(number: u64) -> String { - format!("candidates_{}", number) -} - -fn pending_transition_key(block_hash: H256) -> H264 { - const LEADING: u8 = 1; - - let mut key = H264::default(); - - key[0] = LEADING; - key.0[1..].copy_from_slice(&block_hash.0[..]); - - key -} - -fn transition_key(block_hash: H256) -> H264 { - const LEADING: u8 = 2; - - let mut key = H264::default(); - - key[0] = LEADING; - key.0[1..].copy_from_slice(&block_hash.0[..]); - - key -} - -// encode last canonical transition entry: header and proof. -fn encode_canonical_transition(header: &Header, proof: &[u8]) -> Vec { - let mut stream = RlpStream::new_list(2); - stream.append(header).append(&proof); - stream.out() -} - -// decode last canonical transition entry. -fn decode_canonical_transition(t: &[u8]) -> Result<(Header, &[u8]), DecoderError> { - let rlp = Rlp::new(t); - - Ok((rlp.val_at(0)?, rlp.at(1)?.data()?)) -} - -/// Pending changes from `insert` to be applied after the database write has finished. -pub struct PendingChanges { - best_block: Option, // new best block. -} - -/// Whether or not the hardcoded sync feature is allowed. -pub enum HardcodedSync { - Allow, - Deny, -} - -/// Header chain. See module docs for more details. -pub struct HeaderChain { - genesis_header: encoded::Header, // special-case the genesis. - candidates: RwLock>, - best_block: RwLock, - live_epoch_proofs: RwLock>, - db: Arc, - col: Option, - cache: Arc>, -} - -impl HeaderChain { - /// Create a new header chain given this genesis block and database to read from. - pub fn new( - db: Arc, - col: Option, - spec: &Spec, - cache: Arc>, - allow_hs: HardcodedSync, - ) -> Result { - let mut live_epoch_proofs = ::std::collections::HashMap::default(); - - let genesis = ::rlp::encode(&spec.genesis_header()); - let decoded_header = spec.genesis_header(); - - let chain = if let Some(current) = db.get(col, CURRENT_KEY)? { - let curr : BestAndLatest = ::rlp::decode(¤t).expect("decoding db value failed"); - - let mut cur_number = curr.latest_num; - let mut candidates = BTreeMap::new(); - - // load all era entries, referenced headers within them, - // and live epoch proofs. - while let Some(entry) = db.get(col, era_key(cur_number).as_bytes())? { - let entry: Entry = ::rlp::decode(&entry).expect("decoding db value failed"); - trace!(target: "chain", "loaded header chain entry for era {} with {} candidates", - cur_number, entry.candidates.len()); - - for c in &entry.candidates { - let key = transition_key(c.hash); - - if let Some(proof) = db.get(col, &*key)? { - live_epoch_proofs.insert(c.hash, EpochTransition { - block_hash: c.hash, - block_number: cur_number, - proof: proof.into_vec(), - }); - } - } - candidates.insert(cur_number, entry); - - cur_number -= 1; - } - - // fill best block block descriptor. - let best_block = { - let era = match candidates.get(&curr.best_num) { - Some(era) => era, - None => bail!("Database corrupt: highest block referenced but no data."), - }; - - let best = &era.candidates[0]; - BlockDescriptor { - hash: best.hash, - number: curr.best_num, - total_difficulty: best.total_difficulty, - } - }; - - HeaderChain { - genesis_header: encoded::Header::new(genesis), - best_block: RwLock::new(best_block), - candidates: RwLock::new(candidates), - live_epoch_proofs: RwLock::new(live_epoch_proofs), - db, - col, - cache, - } - - } else { - let chain = HeaderChain { - genesis_header: encoded::Header::new(genesis), - best_block: RwLock::new(BlockDescriptor { - hash: decoded_header.hash(), - number: 0, - total_difficulty: *decoded_header.difficulty(), - }), - candidates: RwLock::new(BTreeMap::new()), - live_epoch_proofs: RwLock::new(live_epoch_proofs), - db: db.clone(), - col, - cache, - }; - - // insert the hardcoded sync into the database. - if let (&Some(ref hardcoded_sync), HardcodedSync::Allow) = (&spec.hardcoded_sync, allow_hs) { - let mut batch = db.transaction(); - - // insert the hardcoded CHT roots into the database. - for (cht_num, cht_root) in hardcoded_sync.chts.iter().enumerate() { - batch.put(col, cht_key(cht_num as u64).as_bytes(), &::rlp::encode(cht_root)); - } - - let decoded_header = hardcoded_sync.header.decode()?; - let decoded_header_num = decoded_header.number(); - - // write the block in the DB. - info!(target: "chain", "Inserting hardcoded block #{} in chain", decoded_header_num); - let pending = chain.insert_with_td(&mut batch, &decoded_header, - hardcoded_sync.total_difficulty, None)?; - - // check that we have enough hardcoded CHT roots. avoids panicking later. - let cht_num = cht::block_to_cht_number(decoded_header_num - 1) - .expect("specs provided a hardcoded block with height 0"); - if cht_num >= hardcoded_sync.chts.len() as u64 { - warn!(target: "chain", "specs didn't provide enough CHT roots for its \ - hardcoded block ; falling back to non-hardcoded sync \ - mode"); - } else { - db.write_buffered(batch); - chain.apply_pending(pending); - } - } - - chain - }; - - // instantiate genesis epoch data if it doesn't exist. - if chain.db.get(col, LAST_CANONICAL_TRANSITION)?.is_none() { - let genesis_data = spec.genesis_epoch_data()?; - - { - let mut batch = chain.db.transaction(); - let data = encode_canonical_transition(&decoded_header, &genesis_data); - batch.put_vec(col, LAST_CANONICAL_TRANSITION, data); - chain.db.write(batch)?; - } - } - - Ok(chain) - } - - /// Insert a pre-verified header. - /// - /// This blindly trusts that the data given to it is sensible. - /// Returns a set of pending changes to be applied with `apply_pending` - /// before the next call to insert and after the transaction has been written. - /// - /// If the block is an epoch transition, provide the transition along with - /// the header. - pub fn insert( - &self, - transaction: &mut DBTransaction, - header: &Header, - transition_proof: Option>, - ) -> EthcoreResult { - self.insert_inner(transaction, header, None, transition_proof) - } - - /// Insert a pre-verified header, with a known total difficulty. Similary to `insert`. - /// - /// This blindly trusts that the data given to it is sensible. - pub fn insert_with_td( - &self, - transaction: &mut DBTransaction, - header: &Header, - total_difficulty: U256, - transition_proof: Option>, - ) -> EthcoreResult { - self.insert_inner(transaction, header, Some(total_difficulty), transition_proof) - } - - fn insert_inner( - &self, - transaction: &mut DBTransaction, - header: &Header, - total_difficulty: Option, - transition_proof: Option>, - ) -> EthcoreResult { - let hash = header.hash(); - let number = header.number(); - let parent_hash = *header.parent_hash(); - let transition = transition_proof.map(|proof| EpochTransition { - block_hash: hash, - block_number: number, - proof, - }); - - let mut pending = PendingChanges { - best_block: None, - }; - - // hold candidates the whole time to guard import order. - let mut candidates = self.candidates.write(); - - // find total difficulty. - let total_difficulty = match total_difficulty { - Some(td) => td, - None => { - let parent_td = - if number == 1 { - self.genesis_header.difficulty() - } else { - candidates.get(&(number - 1)) - .and_then(|entry| entry.candidates.iter().find(|c| c.hash == parent_hash)) - .map(|c| c.total_difficulty) - .ok_or_else(|| BlockError::UnknownParent(parent_hash)) - .map_err(EthcoreErrorKind::Block)? - }; - - parent_td + *header.difficulty() - }, - }; - - // insert headers and candidates entries and write era to disk. - { - let cur_era = candidates.entry(number) - .or_insert_with(|| Entry { candidates: SmallVec::new(), canonical_hash: hash }); - cur_era.candidates.push(Candidate { - hash, - parent_hash, - total_difficulty, - }); - - // fix ordering of era before writing. - if total_difficulty > cur_era.candidates[0].total_difficulty { - let cur_pos = cur_era.candidates.len() - 1; - cur_era.candidates.swap(cur_pos, 0); - cur_era.canonical_hash = hash; - } - - transaction.put(self.col, era_key(number).as_bytes(), &::rlp::encode(&*cur_era)) - } - - if let Some(transition) = transition { - transaction.put(self.col, &*transition_key(hash), &transition.proof); - self.live_epoch_proofs.write().insert(hash, transition); - } - - let raw = header.encoded().into_inner(); - transaction.put_vec(self.col, &hash[..], raw); - - // TODO: For engines when required, use cryptoeconomic guarantees. - let (best_num, is_new_best) = { - let cur_best = self.best_block.read(); - if cur_best.total_difficulty < total_difficulty { - (number, true) - } else { - (cur_best.number, false) - } - }; - - // reorganize ancestors so canonical entries are first in their - // respective candidates vectors. - if is_new_best { - let mut canon_hash = hash; - for (&height, entry) in candidates.iter_mut().rev().skip_while(|&(height, _)| *height > number) { - if height != number && entry.canonical_hash == canon_hash { break; } - - trace!(target: "chain", "Setting new canonical block {} for block height {}", - canon_hash, height); - - let canon_pos = entry.candidates.iter().position(|x| x.hash == canon_hash) - .expect("blocks are only inserted if parent is present; or this is the block we just added; qed"); - - // move the new canonical entry to the front and set the - // era's canonical hash. - entry.candidates.swap(0, canon_pos); - entry.canonical_hash = canon_hash; - - // what about reorgs > cht::SIZE + HISTORY? - // resetting to the last block of a given CHT should be possible. - canon_hash = entry.candidates[0].parent_hash; - - // write altered era to disk - if height != number { - let rlp_era = ::rlp::encode(&*entry); - transaction.put(self.col, era_key(height).as_bytes(), &rlp_era); - } - } - - trace!(target: "chain", "New best block: ({}, {}), TD {}", number, hash, total_difficulty); - pending.best_block = Some(BlockDescriptor { - hash, - number, - total_difficulty, - }); - - // produce next CHT root if it's time. - let earliest_era = *candidates.keys().next().expect("at least one era just created; qed"); - if earliest_era + HISTORY + cht::SIZE <= number { - let cht_num = cht::block_to_cht_number(earliest_era) - .expect("fails only for number == 0; genesis never imported; qed"); - - let mut last_canonical_transition = None; - let cht_root = { - let mut i = earliest_era; - let mut live_epoch_proofs = self.live_epoch_proofs.write(); - - // iterable function which removes the candidates as it goes - // along. this will only be called until the CHT is complete. - let iter = || { - let era_entry = candidates.remove(&i) - .expect("all eras are sequential with no gaps; qed"); - transaction.delete(self.col, era_key(i).as_bytes()); - - i += 1; - - // prune old blocks and epoch proofs. - for ancient in &era_entry.candidates { - let maybe_transition = live_epoch_proofs.remove(&ancient.hash); - if let Some(epoch_transition) = maybe_transition { - transaction.delete(self.col, &*transition_key(ancient.hash)); - - if ancient.hash == era_entry.canonical_hash { - last_canonical_transition = match self.db.get(self.col, &ancient.hash) { - Err(e) => { - warn!(target: "chain", "Error reading from DB: {}\n - ", e); - None - } - Ok(None) => panic!("stored candidates always have corresponding headers; qed"), - Ok(Some(header)) => Some(( - epoch_transition, - ::rlp::decode(&header).expect("decoding value from db failed") - )), - }; - } - } - - transaction.delete(self.col, &ancient.hash); - } - - let canon = &era_entry.candidates[0]; - (canon.hash, canon.total_difficulty) - }; - cht::compute_root(cht_num, ::itertools::repeat_call(iter)) - .expect("fails only when too few items; this is checked; qed") - }; - - // write the CHT root to the database. - debug!(target: "chain", "Produced CHT {} root: {:?}", cht_num, cht_root); - transaction.put(self.col, cht_key(cht_num).as_bytes(), &::rlp::encode(&cht_root)); - - // update the last canonical transition proof - if let Some((epoch_transition, header)) = last_canonical_transition { - let x = encode_canonical_transition(&header, &epoch_transition.proof); - transaction.put_vec(self.col, LAST_CANONICAL_TRANSITION, x); - } - } - } - - // write the best and latest eras to the database. - { - let latest_num = *candidates.iter().rev().next().expect("at least one era just inserted; qed").0; - let curr = BestAndLatest::new(best_num, latest_num); - transaction.put(self.col, CURRENT_KEY, &::rlp::encode(&curr)) - } - Ok(pending) - } - - /// Generates the specifications for hardcoded sync. This is typically only called manually - /// from time to time by a Parity developer in order to update the chain specifications. - /// - /// Returns `None` if we are at the genesis block, or if an error happens . - pub fn read_hardcoded_sync(&self) -> Result, Error> { - let mut chts = Vec::new(); - let mut cht_num = 0; - - loop { - let cht = match self.cht_root(cht_num) { - Some(cht) => cht, - None if cht_num != 0 => { - // end of the iteration - let h_num = 1 + cht_num as u64 * cht::SIZE; - let header = if let Some(header) = self.block_header(BlockId::Number(h_num)) { - header - } else { - let msg = format!("header of block #{} not found in DB ; database in an \ - inconsistent state", h_num); - bail!(msg); - }; - - let decoded = header.decode().expect("decoding db value failed"); - - let entry: Entry = { - let bytes = self.db.get(self.col, era_key(h_num).as_bytes())? - .ok_or_else(|| { - format!("entry for era #{} not found in DB ; database \ - in an inconsistent state", h_num) - })?; - ::rlp::decode(&bytes).expect("decoding db value failed") - }; - - let total_difficulty = entry.candidates.iter() - .find(|c| c.hash == decoded.hash()) - .ok_or_else(|| { - "no candidate matching block found in DB ; database in an \ - inconsistent state" - })? - .total_difficulty; - - break Ok(Some(SpecHardcodedSync { - header, - total_difficulty, - chts, - })); - }, - None => { - break Ok(None); - }, - }; - - chts.push(cht); - cht_num += 1; - } - } - - /// Apply pending changes from a previous `insert` operation. - /// Must be done before the next `insert` call. - pub fn apply_pending(&self, pending: PendingChanges) { - if let Some(best_block) = pending.best_block { - *self.best_block.write() = best_block; - } - } - - /// Get a block's hash by ID. In the case of query by number, only canonical results - /// will be returned. - pub fn block_hash(&self, id: BlockId) -> Option { - match id { - BlockId::Earliest | BlockId::Number(0) => Some(self.genesis_hash()), - BlockId::Hash(hash) => Some(hash), - BlockId::Number(num) => { - if self.best_block.read().number < num { return None } - self.candidates.read().get(&num).map(|entry| entry.canonical_hash) - } - BlockId::Latest => { - Some(self.best_block.read().hash) - } - } - } - - /// Get a block header. In the case of query by number, only canonical blocks - /// will be returned. - pub fn block_header(&self, id: BlockId) -> Option { - let load_from_db = |hash: H256| { - let mut cache = self.cache.lock(); - - match cache.block_header(&hash) { - Some(header) => Some(header), - None => { - match self.db.get(self.col, &hash) { - Ok(db_value) => { - db_value.map(|x| x.into_vec()).map(encoded::Header::new) - .and_then(|header| { - cache.insert_block_header(hash, header.clone()); - Some(header) - }) - }, - Err(e) => { - warn!(target: "chain", "Failed to read from database: {}", e); - None - } - } - } - } - }; - - match id { - BlockId::Earliest | BlockId::Number(0) => Some(self.genesis_header.clone()), - BlockId::Hash(hash) if hash == self.genesis_hash() => { Some(self.genesis_header.clone()) } - BlockId::Hash(hash) => load_from_db(hash), - BlockId::Number(num) => { - if self.best_block.read().number < num { return None } - - self.candidates.read().get(&num).map(|entry| entry.canonical_hash) - .and_then(load_from_db) - } - BlockId::Latest => { - // hold candidates hear to prevent deletion of the header - // as we read it. - let _candidates = self.candidates.read(); - let hash = { - let best = self.best_block.read(); - if best.number == 0 { - return Some(self.genesis_header.clone()) - } - - best.hash - }; - - load_from_db(hash) - } - } - } - - /// Get a block's chain score. - /// Returns nothing for non-canonical blocks. - pub fn score(&self, id: BlockId) -> Option { - let genesis_hash = self.genesis_hash(); - match id { - BlockId::Earliest | BlockId::Number(0) => Some(self.genesis_header.difficulty()), - BlockId::Hash(hash) if hash == genesis_hash => Some(self.genesis_header.difficulty()), - BlockId::Hash(hash) => match self.block_header(BlockId::Hash(hash)) { - Some(header) => self.candidates.read().get(&header.number()) - .and_then(|era| era.candidates.iter().find(|e| e.hash == hash)) - .map(|c| c.total_difficulty), - None => None, - }, - BlockId::Number(num) => { - let candidates = self.candidates.read(); - if self.best_block.read().number < num { return None } - candidates.get(&num).map(|era| era.candidates[0].total_difficulty) - } - BlockId::Latest => Some(self.best_block.read().total_difficulty) - } - } - - /// Get the best block's header. - pub fn best_header(&self) -> encoded::Header { - self.block_header(BlockId::Latest).expect("Header for best block always stored; qed") - } - - /// Get an iterator over a block and its ancestry. - pub fn ancestry_iter(&self, start: BlockId) -> AncestryIter { - AncestryIter { - next: self.block_header(start), - chain: self, - } - } - - /// Get the nth CHT root, if it's been computed. - /// - /// CHT root 0 is from block `1..2048`. - /// CHT root 1 is from block `2049..4096` - /// and so on. - /// - /// This is because it's assumed that the genesis hash is known, - /// so including it within a CHT would be redundant. - pub fn cht_root(&self, n: usize) -> Option { - match self.db.get(self.col, cht_key(n as u64).as_bytes()) { - Ok(db_fetch) => db_fetch.map(|bytes| ::rlp::decode(&bytes).expect("decoding value from db failed")), - Err(e) => { - warn!(target: "chain", "Error reading from database: {}", e); - None - } - } - } - - /// Get the genesis hash. - pub fn genesis_hash(&self) -> H256 { - self.genesis_header.hash() - } - - /// Get the best block's data. - pub fn best_block(&self) -> BlockDescriptor { - self.best_block.read().clone() - } - - /// If there is a gap between the genesis and the rest - /// of the stored blocks, return the first post-gap block. - pub fn first_block(&self) -> Option { - let candidates = self.candidates.read(); - match candidates.iter().next() { - None | Some((&1, _)) => None, - Some((&height, entry)) => Some(BlockDescriptor { - number: height, - hash: entry.canonical_hash, - total_difficulty: entry.candidates.iter().find(|x| x.hash == entry.canonical_hash) - .expect("entry always stores canonical candidate; qed").total_difficulty, - }) - } - } - - /// Get block status. - pub fn status(&self, hash: &H256) -> BlockStatus { - if self.db.get(self.col, hash).ok().map_or(false, |x| x.is_some()) { - BlockStatus::InChain - } else { - BlockStatus::Unknown - } - } - - /// Insert a pending transition. - pub fn insert_pending_transition(&self, batch: &mut DBTransaction, hash: H256, t: &PendingEpochTransition) { - let key = pending_transition_key(hash); - batch.put(self.col, &*key, &*::rlp::encode(t)); - } - - /// Get pending transition for a specific block hash. - pub fn pending_transition(&self, hash: H256) -> Option { - let key = pending_transition_key(hash); - match self.db.get(self.col, &*key) { - Ok(db_fetch) => db_fetch.map(|bytes| ::rlp::decode(&bytes).expect("decoding value from db failed")), - Err(e) => { - warn!(target: "chain", "Error reading from database: {}", e); - None - } - } - } - - /// Get the transition to the epoch the given parent hash is part of - /// or transitions to. - /// This will give the epoch that any children of this parent belong to. - /// - /// The header corresponding the the parent hash must be stored already. - pub fn epoch_transition_for(&self, parent_hash: H256) -> Option<(Header, Vec)> { - // slow path: loop back block by block - let live_proofs = self.live_epoch_proofs.read(); - - for hdr in self.ancestry_iter(BlockId::Hash(parent_hash)) { - if let Some(transition) = live_proofs.get(&hdr.hash()).cloned() { - return hdr.decode().map(|decoded_hdr| { - (decoded_hdr, transition.proof) - }).ok(); - } - } - - // any blocks left must be descendants of the last canonical transition block. - match self.db.get(self.col, LAST_CANONICAL_TRANSITION) { - Ok(x) => { - let x = x.expect("last canonical transition always instantiated; qed"); - - let (hdr, proof) = decode_canonical_transition(&x) - .expect("last canonical transition always encoded correctly; qed"); - - Some((hdr, proof.to_vec())) - } - Err(e) => { - warn!("Error reading from DB: {}", e); - None - } - } - } -} - -impl HeapSizeOf for HeaderChain { - fn heap_size_of_children(&self) -> usize { - self.candidates.read().heap_size_of_children() - } -} - -/// Iterator over a block's ancestry. -pub struct AncestryIter<'a> { - next: Option, - chain: &'a HeaderChain, -} - -impl<'a> Iterator for AncestryIter<'a> { - type Item = encoded::Header; - - fn next(&mut self) -> Option { - let next = self.next.take(); - if let Some(p_hash) = next.as_ref().map(|hdr| hdr.parent_hash()) { - self.next = self.chain.block_header(BlockId::Hash(p_hash)); - } - - next - } -} - -#[cfg(test)] -mod tests { - use super::{HeaderChain, HardcodedSync}; - use std::sync::Arc; - - use cache::Cache; - use common_types::header::Header; - use common_types::ids::BlockId; - use ethcore::spec::Spec; - use ethereum_types::U256; - use kvdb::KeyValueDB; - use kvdb_memorydb; - - use std::time::Duration; - use parking_lot::Mutex; - - fn make_db() -> Arc { - Arc::new(kvdb_memorydb::create(0)) - } - - #[test] - fn basic_chain() { - let spec = Spec::new_test(); - let genesis_header = spec.genesis_header(); - let db = make_db(); - - let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); - - let chain = HeaderChain::new(db.clone(), None, &spec, cache, HardcodedSync::Allow).unwrap(); - - let mut parent_hash = genesis_header.hash(); - let mut rolling_timestamp = genesis_header.timestamp(); - for i in 1..10000 { - let mut header = Header::new(); - header.set_parent_hash(parent_hash); - header.set_number(i); - header.set_timestamp(rolling_timestamp); - header.set_difficulty(*genesis_header.difficulty() * i as u32); - parent_hash = header.hash(); - - let mut tx = db.transaction(); - let pending = chain.insert(&mut tx, &header, None).unwrap(); - db.write(tx).unwrap(); - chain.apply_pending(pending); - - rolling_timestamp += 10; - } - - assert!(chain.block_header(BlockId::Number(10)).is_none()); - assert!(chain.block_header(BlockId::Number(9000)).is_some()); - assert!(chain.cht_root(2).is_some()); - assert!(chain.cht_root(3).is_none()); - } - - #[test] - fn reorganize() { - let spec = Spec::new_test(); - let genesis_header = spec.genesis_header(); - let db = make_db(); - let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); - - let chain = HeaderChain::new(db.clone(), None, &spec, cache, HardcodedSync::Allow).unwrap(); - - let mut parent_hash = genesis_header.hash(); - let mut rolling_timestamp = genesis_header.timestamp(); - for i in 1..6 { - let mut header = Header::new(); - header.set_parent_hash(parent_hash); - header.set_number(i); - header.set_timestamp(rolling_timestamp); - header.set_difficulty(*genesis_header.difficulty() * i as u32); - parent_hash = header.hash(); - - let mut tx = db.transaction(); - let pending = chain.insert(&mut tx, &header, None).unwrap(); - db.write(tx).unwrap(); - chain.apply_pending(pending); - - rolling_timestamp += 10; - } - - { - let mut rolling_timestamp = rolling_timestamp; - let mut parent_hash = parent_hash; - for i in 6..16 { - let mut header = Header::new(); - header.set_parent_hash(parent_hash); - header.set_number(i); - header.set_timestamp(rolling_timestamp); - header.set_difficulty(*genesis_header.difficulty() * i as u32); - parent_hash = header.hash(); - - let mut tx = db.transaction(); - let pending = chain.insert(&mut tx, &header, None).unwrap(); - db.write(tx).unwrap(); - chain.apply_pending(pending); - - rolling_timestamp += 10; - } - } - - assert_eq!(chain.best_block().number, 15); - - { - let mut rolling_timestamp = rolling_timestamp; - let mut parent_hash = parent_hash; - - // import a shorter chain which has better TD. - for i in 6..13 { - let mut header = Header::new(); - header.set_parent_hash(parent_hash); - header.set_number(i); - header.set_timestamp(rolling_timestamp); - header.set_difficulty(*genesis_header.difficulty() * U256::from(i * i)); - parent_hash = header.hash(); - - let mut tx = db.transaction(); - let pending = chain.insert(&mut tx, &header, None).unwrap(); - db.write(tx).unwrap(); - chain.apply_pending(pending); - - rolling_timestamp += 11; - } - } - - let (mut num, mut canon_hash) = (chain.best_block().number, chain.best_block().hash); - assert_eq!(num, 12); - - while num > 0 { - let header = chain.block_header(BlockId::Number(num)).unwrap(); - assert_eq!(header.hash(), canon_hash); - - canon_hash = header.parent_hash(); - num -= 1; - } - } - - #[test] - fn earliest_is_latest() { - let spec = Spec::new_test(); - let db = make_db(); - let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); - - let chain = HeaderChain::new(db.clone(), None, &spec, cache, HardcodedSync::Allow).unwrap(); - - assert!(chain.block_header(BlockId::Earliest).is_some()); - assert!(chain.block_header(BlockId::Latest).is_some()); - } - - #[test] - fn restore_from_db() { - let spec = Spec::new_test(); - let genesis_header = spec.genesis_header(); - let db = make_db(); - let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); - - { - let chain = HeaderChain::new(db.clone(), None, &spec, cache.clone(), - HardcodedSync::Allow).unwrap(); - let mut parent_hash = genesis_header.hash(); - let mut rolling_timestamp = genesis_header.timestamp(); - for i in 1..10000 { - let mut header = Header::new(); - header.set_parent_hash(parent_hash); - header.set_number(i); - header.set_timestamp(rolling_timestamp); - header.set_difficulty(*genesis_header.difficulty() * i as u32); - parent_hash = header.hash(); - - let mut tx = db.transaction(); - let pending = chain.insert(&mut tx, &header, None).unwrap(); - db.write(tx).unwrap(); - chain.apply_pending(pending); - - rolling_timestamp += 10; - } - } - - let chain = HeaderChain::new(db.clone(), None, &spec, cache.clone(), - HardcodedSync::Allow).unwrap(); - assert!(chain.block_header(BlockId::Number(10)).is_none()); - assert!(chain.block_header(BlockId::Number(9000)).is_some()); - assert!(chain.cht_root(2).is_some()); - assert!(chain.cht_root(3).is_none()); - assert_eq!(chain.block_header(BlockId::Latest).unwrap().number(), 9999); - } - - #[test] - fn restore_higher_non_canonical() { - let spec = Spec::new_test(); - let genesis_header = spec.genesis_header(); - let db = make_db(); - let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); - - { - let chain = HeaderChain::new(db.clone(), None, &spec, cache.clone(), - HardcodedSync::Allow).unwrap(); - let mut parent_hash = genesis_header.hash(); - let mut rolling_timestamp = genesis_header.timestamp(); - - // push 100 low-difficulty blocks. - for i in 1..101 { - let mut header = Header::new(); - header.set_parent_hash(parent_hash); - header.set_number(i); - header.set_timestamp(rolling_timestamp); - header.set_difficulty(*genesis_header.difficulty() * i as u32); - parent_hash = header.hash(); - - let mut tx = db.transaction(); - let pending = chain.insert(&mut tx, &header, None).unwrap(); - db.write(tx).unwrap(); - chain.apply_pending(pending); - - rolling_timestamp += 10; - } - - // push fewer high-difficulty blocks. - for i in 1..11 { - let mut header = Header::new(); - header.set_parent_hash(parent_hash); - header.set_number(i); - header.set_timestamp(rolling_timestamp); - header.set_difficulty(*genesis_header.difficulty() * U256::from(i as u32 * 1000u32)); - parent_hash = header.hash(); - - let mut tx = db.transaction(); - let pending = chain.insert(&mut tx, &header, None).unwrap(); - db.write(tx).unwrap(); - chain.apply_pending(pending); - - rolling_timestamp += 10; - } - - assert_eq!(chain.block_header(BlockId::Latest).unwrap().number(), 10); - } - - // after restoration, non-canonical eras should still be loaded. - let chain = HeaderChain::new(db.clone(), None, &spec, cache.clone(), - HardcodedSync::Allow).unwrap(); - assert_eq!(chain.block_header(BlockId::Latest).unwrap().number(), 10); - assert!(chain.candidates.read().get(&100).is_some()) - } - - #[test] - fn genesis_header_available() { - let spec = Spec::new_test(); - let genesis_header = spec.genesis_header(); - let db = make_db(); - let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); - - let chain = HeaderChain::new(db.clone(), None, &spec, cache.clone(), - HardcodedSync::Allow).unwrap(); - - assert!(chain.block_header(BlockId::Earliest).is_some()); - assert!(chain.block_header(BlockId::Number(0)).is_some()); - assert!(chain.block_header(BlockId::Hash(genesis_header.hash())).is_some()); - } - - #[test] - fn epoch_transitions_available_after_cht() { - let spec = Spec::new_test(); - let genesis_header = spec.genesis_header(); - let db = make_db(); - let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); - - let chain = HeaderChain::new(db.clone(), None, &spec, cache, HardcodedSync::Allow).unwrap(); - - let mut parent_hash = genesis_header.hash(); - let mut rolling_timestamp = genesis_header.timestamp(); - for i in 1..6 { - let mut header = Header::new(); - header.set_parent_hash(parent_hash); - header.set_number(i); - header.set_timestamp(rolling_timestamp); - header.set_difficulty(*genesis_header.difficulty() * i as u32); - parent_hash = header.hash(); - - let mut tx = db.transaction(); - let epoch_proof = if i == 3 { - Some(vec![1, 2, 3, 4]) - } else { - None - }; - - let pending = chain.insert(&mut tx, &header, epoch_proof).unwrap(); - db.write(tx).unwrap(); - chain.apply_pending(pending); - - rolling_timestamp += 10; - } - - // these 3 should end up falling back to the genesis epoch proof in DB - for i in 0..3 { - let hash = chain.block_hash(BlockId::Number(i)).unwrap(); - assert_eq!(chain.epoch_transition_for(hash).unwrap().1, Vec::::new()); - } - - // these are live. - for i in 3..6 { - let hash = chain.block_hash(BlockId::Number(i)).unwrap(); - assert_eq!(chain.epoch_transition_for(hash).unwrap().1, vec![1, 2, 3, 4]); - } - - for i in 6..10000 { - let mut header = Header::new(); - header.set_parent_hash(parent_hash); - header.set_number(i); - header.set_timestamp(rolling_timestamp); - header.set_difficulty(*genesis_header.difficulty() * i as u32); - parent_hash = header.hash(); - - let mut tx = db.transaction(); - let pending = chain.insert(&mut tx, &header, None).unwrap(); - db.write(tx).unwrap(); - chain.apply_pending(pending); - - rolling_timestamp += 10; - } - - // no live blocks have associated epoch proofs -- make sure we aren't leaking memory. - assert!(chain.live_epoch_proofs.read().is_empty()); - assert_eq!(chain.epoch_transition_for(parent_hash).unwrap().1, vec![1, 2, 3, 4]); - } - - #[test] - fn hardcoded_sync_gen() { - let spec = Spec::new_test(); - let genesis_header = spec.genesis_header(); - let db = make_db(); - - let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); - - let chain = HeaderChain::new(db.clone(), None, &spec, cache, HardcodedSync::Allow).expect("failed to instantiate a new HeaderChain"); - - let mut parent_hash = genesis_header.hash(); - let mut rolling_timestamp = genesis_header.timestamp(); - let mut total_difficulty = *genesis_header.difficulty(); - let h_num = 3 * ::cht::SIZE + 1; - for i in 1..10000 { - let mut header = Header::new(); - header.set_parent_hash(parent_hash); - header.set_number(i); - header.set_timestamp(rolling_timestamp); - let diff = *genesis_header.difficulty() * i as u32; - header.set_difficulty(diff); - if i <= h_num { - total_difficulty = total_difficulty + diff; - } - parent_hash = header.hash(); - - let mut tx = db.transaction(); - let pending = chain.insert(&mut tx, &header, None).expect("failed inserting a transaction"); - db.write(tx).unwrap(); - chain.apply_pending(pending); - - rolling_timestamp += 10; - } - - let hardcoded_sync = chain.read_hardcoded_sync().expect("failed reading hardcoded sync").expect("failed unwrapping hardcoded sync"); - assert_eq!(hardcoded_sync.chts.len(), 3); - assert_eq!(hardcoded_sync.total_difficulty, total_difficulty); - let decoded: Header = hardcoded_sync.header.decode().expect("decoding failed"); - assert_eq!(decoded.number(), h_num); - } -} diff --git a/ethcore/light/src/client/mod.rs b/ethcore/light/src/client/mod.rs deleted file mode 100644 index 8205ae2ab3b..00000000000 --- a/ethcore/light/src/client/mod.rs +++ /dev/null @@ -1,646 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Light client implementation. Stores data from light sync - -use std::sync::{Weak, Arc}; - -use ethcore::client::{ClientReport, EnvInfo, ClientIoMessage}; -use ethcore::engines::{epoch, EthEngine, EpochChange, EpochTransition, Proof}; -use ethcore::machine::EthereumMachine; -use ethcore::error::{Error, EthcoreResult}; -use ethcore::verification::queue::{self, HeaderQueue}; -use ethcore::spec::{Spec, SpecHardcodedSync}; -use io::IoChannel; -use parking_lot::{Mutex, RwLock}; -use ethereum_types::{H256, U256}; -use futures::{IntoFuture, Future}; -use common_types::BlockNumber; -use common_types::block_status::BlockStatus; -use common_types::blockchain_info::BlockChainInfo; -use common_types::encoded; -use common_types::header::Header; -use common_types::ids::BlockId; - -use kvdb::KeyValueDB; - -use self::fetch::ChainDataFetcher; -use self::header_chain::{AncestryIter, HeaderChain, HardcodedSync}; - -use cache::Cache; - -pub use self::service::Service; - -mod header_chain; -mod service; - -pub mod fetch; - -/// Configuration for the light client. -#[derive(Debug, Clone)] -pub struct Config { - /// Verification queue config. - pub queue: queue::Config, - /// Chain column in database. - pub chain_column: Option, - /// Should it do full verification of blocks? - pub verify_full: bool, - /// Should it check the seal of blocks? - pub check_seal: bool, - /// Disable hardcoded sync. - pub no_hardcoded_sync: bool, -} - -impl Default for Config { - fn default() -> Config { - Config { - queue: Default::default(), - chain_column: None, - verify_full: true, - check_seal: true, - no_hardcoded_sync: false, - } - } -} - -/// Trait for interacting with the header chain abstractly. -pub trait LightChainClient: Send + Sync { - /// Adds a new `LightChainNotify` listener. - fn add_listener(&self, listener: Weak); - - /// Get chain info. - fn chain_info(&self) -> BlockChainInfo; - - /// Queue header to be verified. Required that all headers queued have their - /// parent queued prior. - fn queue_header(&self, header: Header) -> EthcoreResult; - - /// Attempt to get a block hash by block id. - fn block_hash(&self, id: BlockId) -> Option; - - /// Attempt to get block header by block id. - fn block_header(&self, id: BlockId) -> Option; - - /// Get the best block header. - fn best_block_header(&self) -> encoded::Header; - - /// Get a block's chain score by ID. - fn score(&self, id: BlockId) -> Option; - - /// Get an iterator over a block and its ancestry. - fn ancestry_iter<'a>(&'a self, start: BlockId) -> Box + 'a>; - - /// Get the signing chain ID. - fn signing_chain_id(&self) -> Option; - - /// Get environment info for execution at a given block. - /// Fails if that block's header is not stored. - fn env_info(&self, id: BlockId) -> Option; - - /// Get a handle to the consensus engine. - fn engine(&self) -> &Arc; - - /// Query whether a block is known. - fn is_known(&self, hash: &H256) -> bool; - - /// Set the chain via a spec name. - fn set_spec_name(&self, new_spec_name: String) -> Result<(), ()>; - - /// Clear the queue. - fn clear_queue(&self); - - /// Flush the queue. - fn flush_queue(&self); - - /// Get queue info. - fn queue_info(&self) -> queue::QueueInfo; - - /// Get the `i`th CHT root. - fn cht_root(&self, i: usize) -> Option; - - /// Get a report of import activity since the last call. - fn report(&self) -> ClientReport; -} - -/// An actor listening to light chain events. -pub trait LightChainNotify: Send + Sync { - /// Notifies about imported headers. - fn new_headers(&self, good: &[H256]); -} - -/// Something which can be treated as a `LightChainClient`. -pub trait AsLightClient { - /// The kind of light client this can be treated as. - type Client: LightChainClient; - - /// Access the underlying light client. - fn as_light_client(&self) -> &Self::Client; -} - -impl AsLightClient for T { - type Client = Self; - - fn as_light_client(&self) -> &Self { self } -} - -/// Light client implementation. -pub struct Client { - queue: HeaderQueue, - engine: Arc, - chain: HeaderChain, - report: RwLock, - import_lock: Mutex<()>, - db: Arc, - listeners: RwLock>>, - fetcher: T, - verify_full: bool, - /// A closure to call when we want to restart the client - exit_handler: Mutex>>, -} - -impl Client { - /// Create a new `Client`. - pub fn new( - config: Config, - db: Arc, - chain_col: Option, - spec: &Spec, - fetcher: T, - io_channel: IoChannel, - cache: Arc> - ) -> Result { - Ok(Self { - queue: HeaderQueue::new(config.queue, spec.engine.clone(), io_channel, config.check_seal), - engine: spec.engine.clone(), - chain: { - let hs_cfg = if config.no_hardcoded_sync { HardcodedSync::Deny } else { HardcodedSync::Allow }; - HeaderChain::new(db.clone(), chain_col, &spec, cache, hs_cfg)? - }, - report: RwLock::new(ClientReport::default()), - import_lock: Mutex::new(()), - db, - listeners: RwLock::new(vec![]), - fetcher, - verify_full: config.verify_full, - exit_handler: Mutex::new(None), - }) - } - - /// Generates the specifications for hardcoded sync. This is typically only called manually - /// from time to time by a Parity developer in order to update the chain specifications. - /// - /// Returns `None` if we are at the genesis block. - pub fn read_hardcoded_sync(&self) -> Result, Error> { - self.chain.read_hardcoded_sync() - } - - /// Adds a new `LightChainNotify` listener. - pub fn add_listener(&self, listener: Weak) { - self.listeners.write().push(listener); - } - - /// Import a header to the queue for additional verification. - pub fn import_header(&self, header: Header) -> EthcoreResult { - self.queue.import(header).map_err(|(_, e)| e) - } - - /// Inquire about the status of a given header. - pub fn status(&self, hash: &H256) -> BlockStatus { - match self.queue.status(hash) { - queue::Status::Unknown => self.chain.status(hash), - other => other.into(), - } - } - - /// Get the chain info. - pub fn chain_info(&self) -> BlockChainInfo { - let best_hdr = self.chain.best_header(); - let best_td = self.chain.best_block().total_difficulty; - - let first_block = self.chain.first_block(); - let genesis_hash = self.chain.genesis_hash(); - - BlockChainInfo { - total_difficulty: best_td, - pending_total_difficulty: best_td + self.queue.total_difficulty(), - genesis_hash, - best_block_hash: best_hdr.hash(), - best_block_number: best_hdr.number(), - best_block_timestamp: best_hdr.timestamp(), - ancient_block_hash: if first_block.is_some() { Some(genesis_hash) } else { None }, - ancient_block_number: if first_block.is_some() { Some(0) } else { None }, - first_block_hash: first_block.as_ref().map(|first| first.hash), - first_block_number: first_block.as_ref().map(|first| first.number), - } - } - - /// Get the header queue info. - pub fn queue_info(&self) -> queue::QueueInfo { - self.queue.queue_info() - } - - /// Attempt to get a block hash by block id. - pub fn block_hash(&self, id: BlockId) -> Option { - self.chain.block_hash(id) - } - - /// Get a block header by Id. - pub fn block_header(&self, id: BlockId) -> Option { - self.chain.block_header(id) - } - - /// Get the best block header. - pub fn best_block_header(&self) -> encoded::Header { - self.chain.best_header() - } - - /// Get a block's chain score. - pub fn score(&self, id: BlockId) -> Option { - self.chain.score(id) - } - - /// Get an iterator over a block and its ancestry. - pub fn ancestry_iter(&self, start: BlockId) -> AncestryIter { - self.chain.ancestry_iter(start) - } - - /// Get the signing chain id. - pub fn signing_chain_id(&self) -> Option { - self.engine.signing_chain_id(&self.latest_env_info()) - } - - /// Flush the header queue. - pub fn flush_queue(&self) { - self.queue.flush() - } - - /// Get the `i`th CHT root. - pub fn cht_root(&self, i: usize) -> Option { - self.chain.cht_root(i) - } - - /// Import a set of pre-verified headers from the queue. - pub fn import_verified(&self) { - const MAX: usize = 256; - - let _lock = self.import_lock.lock(); - - let mut bad = Vec::new(); - let mut good = Vec::new(); - for verified_header in self.queue.drain(MAX) { - let (num, hash) = (verified_header.number(), verified_header.hash()); - trace!(target: "client", "importing block {}", num); - - if self.verify_full && !self.check_header(&mut bad, &verified_header) { - continue - } - - let write_proof_result = match self.check_epoch_signal(&verified_header) { - Ok(Some(proof)) => self.write_pending_proof(&verified_header, proof), - Ok(None) => Ok(()), - Err(e) => - panic!("Unable to fetch epoch transition proof: {:?}", e), - }; - - if let Err(e) = write_proof_result { - warn!(target: "client", "Error writing pending transition proof to DB: {:?} \ - The node may not be able to synchronize further.", e); - } - - let epoch_proof = self.engine.is_epoch_end_light( - &verified_header, - &|h| self.chain.block_header(BlockId::Hash(h)).and_then(|hdr| hdr.decode().ok()), - &|h| self.chain.pending_transition(h), - ); - - let mut tx = self.db.transaction(); - let pending = match self.chain.insert(&mut tx, &verified_header, epoch_proof) { - Ok(pending) => { - good.push(hash); - self.report.write().blocks_imported += 1; - pending - } - Err(e) => { - debug!(target: "client", "Error importing header {:?}: {:?}", (num, hash), e); - bad.push(hash); - continue; - } - }; - - self.db.write_buffered(tx); - self.chain.apply_pending(pending); - } - - if let Err(e) = self.db.flush() { - panic!("Database flush failed: {}. Check disk health and space.", e); - } - - self.queue.mark_as_bad(&bad); - self.queue.mark_as_good(&good); - - self.notify(|listener| listener.new_headers(&good)); - } - - /// Get a report about blocks imported. - pub fn report(&self) -> ClientReport { - self.report.read().clone() - } - - /// Get blockchain mem usage in bytes. - pub fn chain_mem_used(&self) -> usize { - use heapsize::HeapSizeOf; - - self.chain.heap_size_of_children() - } - - /// Set a closure to call when the client wants to be restarted. - /// - /// The parameter passed to the callback is the name of the new chain spec to use after - /// the restart. - pub fn set_exit_handler(&self, f: F) where F: Fn(String) + 'static + Send { - *self.exit_handler.lock() = Some(Box::new(f)); - } - - /// Get a handle to the verification engine. - pub fn engine(&self) -> &Arc { - &self.engine - } - - /// Get the latest environment info. - pub fn latest_env_info(&self) -> EnvInfo { - self.env_info(BlockId::Latest) - .expect("Best block header and recent hashes always stored; qed") - } - - /// Get environment info for a given block. - pub fn env_info(&self, id: BlockId) -> Option { - let header = match self.block_header(id) { - Some(hdr) => hdr, - None => return None, - }; - - Some(EnvInfo { - number: header.number(), - author: header.author(), - timestamp: header.timestamp(), - difficulty: header.difficulty(), - last_hashes: self.build_last_hashes(header.parent_hash()), - gas_used: Default::default(), - gas_limit: header.gas_limit(), - }) - } - - fn build_last_hashes(&self, mut parent_hash: H256) -> Arc> { - let mut v = Vec::with_capacity(256); - for _ in 0..255 { - v.push(parent_hash); - match self.block_header(BlockId::Hash(parent_hash)) { - Some(header) => parent_hash = header.hash(), - None => break, - } - } - - Arc::new(v) - } - - fn notify(&self, f: F) { - for listener in &*self.listeners.read() { - if let Some(listener) = listener.upgrade() { - f(&*listener) - } - } - } - - // return false if should skip, true otherwise. may push onto bad if - // should skip. - fn check_header(&self, bad: &mut Vec, verified_header: &Header) -> bool { - let hash = verified_header.hash(); - let parent_header = match self.chain.block_header(BlockId::Hash(*verified_header.parent_hash())) { - Some(header) => header, - None => { - trace!(target: "client", "No parent for block ({}, {})", - verified_header.number(), hash); - return false // skip import of block with missing parent. - } - }; - - // Verify Block Family - - let verify_family_result = { - parent_header.decode() - .map_err(|dec_err| dec_err.into()) - .and_then(|decoded| { - self.engine.verify_block_family(&verified_header, &decoded) - }) - - }; - if let Err(e) = verify_family_result { - warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", - verified_header.number(), verified_header.hash(), e); - bad.push(hash); - return false; - }; - - // "external" verification. - let verify_external_result = self.engine.verify_block_external(&verified_header); - if let Err(e) = verify_external_result { - warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", - verified_header.number(), verified_header.hash(), e); - - bad.push(hash); - return false; - }; - - true - } - - fn check_epoch_signal(&self, verified_header: &Header) -> Result>, T::Error> { - use ethcore::machine::{AuxiliaryRequest, AuxiliaryData}; - - let mut block: Option> = None; - let mut receipts: Option> = None; - - loop { - - let is_signal = { - let auxiliary = AuxiliaryData { - bytes: block.as_ref().map(|x| &x[..]), - receipts: receipts.as_ref().map(|x| &x[..]), - }; - - self.engine.signals_epoch_end(verified_header, auxiliary) - }; - - // check with any auxiliary data fetched so far - match is_signal { - EpochChange::No => return Ok(None), - EpochChange::Yes(proof) => return Ok(Some(proof)), - EpochChange::Unsure(unsure) => { - let (b, r) = match unsure { - AuxiliaryRequest::Body => - (Some(self.fetcher.block_body(verified_header)), None), - AuxiliaryRequest::Receipts => - (None, Some(self.fetcher.block_receipts(verified_header))), - AuxiliaryRequest::Both => ( - Some(self.fetcher.block_body(verified_header)), - Some(self.fetcher.block_receipts(verified_header)), - ), - }; - - if let Some(b) = b { - block = Some(b.into_future().wait()?.into_inner()); - } - - if let Some(r) = r { - receipts = Some(r.into_future().wait()?); - } - } - } - } - } - - // attempts to fetch the epoch proof from the network until successful. - fn write_pending_proof(&self, header: &Header, proof: Proof) -> Result<(), T::Error> { - let proof = match proof { - Proof::Known(known) => known, - Proof::WithState(state_dependent) => { - self.fetcher.epoch_transition( - header.hash(), - self.engine.clone(), - state_dependent - ).into_future().wait()? - } - }; - - let mut batch = self.db.transaction(); - self.chain.insert_pending_transition(&mut batch, header.hash(), &epoch::PendingTransition { - proof, - }); - self.db.write_buffered(batch); - Ok(()) - } -} - -impl LightChainClient for Client { - fn add_listener(&self, listener: Weak) { - Client::add_listener(self, listener) - } - - fn chain_info(&self) -> BlockChainInfo { Client::chain_info(self) } - - fn queue_header(&self, header: Header) -> EthcoreResult { - self.import_header(header) - } - - fn block_hash(&self, id: BlockId) -> Option { - Client::block_hash(self, id) - } - - fn block_header(&self, id: BlockId) -> Option { - Client::block_header(self, id) - } - - fn best_block_header(&self) -> encoded::Header { - Client::best_block_header(self) - } - - fn score(&self, id: BlockId) -> Option { - Client::score(self, id) - } - - fn ancestry_iter<'a>(&'a self, start: BlockId) -> Box + 'a> { - Box::new(Client::ancestry_iter(self, start)) - } - - fn signing_chain_id(&self) -> Option { - Client::signing_chain_id(self) - } - - fn env_info(&self, id: BlockId) -> Option { - Client::env_info(self, id) - } - - fn engine(&self) -> &Arc { - Client::engine(self) - } - - fn set_spec_name(&self, new_spec_name: String) -> Result<(), ()> { - trace!(target: "mode", "Client::set_spec_name({:?})", new_spec_name); - if let Some(ref h) = *self.exit_handler.lock() { - (*h)(new_spec_name); - Ok(()) - } else { - warn!("Not hypervised; cannot change chain."); - Err(()) - } - } - - fn is_known(&self, hash: &H256) -> bool { - self.status(hash) == BlockStatus::InChain - } - - fn clear_queue(&self) { - self.queue.clear() - } - - fn flush_queue(&self) { - Client::flush_queue(self); - } - - fn queue_info(&self) -> queue::QueueInfo { - self.queue.queue_info() - } - - fn cht_root(&self, i: usize) -> Option { - Client::cht_root(self, i) - } - - fn report(&self) -> ClientReport { - Client::report(self) - } -} - -impl ::ethcore::client::ChainInfo for Client { - fn chain_info(&self) -> BlockChainInfo { - Client::chain_info(self) - } -} - -impl ::ethcore::client::EngineClient for Client { - fn update_sealing(&self) { } - fn submit_seal(&self, _block_hash: H256, _seal: Vec>) { } - fn broadcast_consensus_message(&self, _message: Vec) { } - - fn epoch_transition_for(&self, parent_hash: H256) -> Option { - self.chain.epoch_transition_for(parent_hash).map(|(hdr, proof)| EpochTransition { - block_hash: hdr.hash(), - block_number: hdr.number(), - proof, - }) - } - - fn as_full_client(&self) -> Option<&::ethcore::client::BlockChainClient> { - None - } - - fn block_number(&self, id: BlockId) -> Option { - self.block_header(id).map(|hdr| hdr.number()) - } - - fn block_header(&self, id: BlockId) -> Option { - Client::block_header(self, id) - } -} diff --git a/ethcore/light/src/client/service.rs b/ethcore/light/src/client/service.rs deleted file mode 100644 index 9672974fc25..00000000000 --- a/ethcore/light/src/client/service.rs +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Minimal IO service for light client. -//! Just handles block import messages and passes them to the client. - -use std::fmt; -use std::sync::Arc; - -use ethcore_db as db; -use ethcore_blockchain::BlockChainDB; -use ethcore::client::ClientIoMessage; -use ethcore::error::Error as CoreError; -use ethcore::spec::Spec; -use io::{IoContext, IoError, IoHandler, IoService}; - -use cache::Cache; -use parking_lot::Mutex; - -use super::{ChainDataFetcher, LightChainNotify, Client, Config as ClientConfig}; - -/// Errors on service initialization. -#[derive(Debug)] -pub enum Error { - /// Core error. - Core(CoreError), - /// I/O service error. - Io(IoError), -} - -impl From for Error { - #[inline] - fn from(err: CoreError) -> Error { - Error::Core(err) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::Core(ref msg) => write!(f, "Core error: {}", msg), - Error::Io(ref err) => write!(f, "I/O service error: {}", err), - } - } -} - -/// Light client service. -pub struct Service { - client: Arc>, - io_service: IoService, -} - -impl Service { - /// Start the service: initialize I/O workers and client itself. - pub fn start(config: ClientConfig, spec: &Spec, fetcher: T, db: Arc, cache: Arc>) -> Result { - let io_service = IoService::::start().map_err(Error::Io)?; - let client = Arc::new(Client::new(config, - db.key_value().clone(), - db::COL_LIGHT_CHAIN, - spec, - fetcher, - io_service.channel(), - cache, - )?); - - io_service.register_handler(Arc::new(ImportBlocks(client.clone()))).map_err(Error::Io)?; - spec.engine.register_client(Arc::downgrade(&client) as _); - - Ok(Service { - client, - io_service, - }) - } - - /// Set the actor to be notified on certain chain events - pub fn add_notify(&self, notify: Arc) { - self.client.add_listener(Arc::downgrade(¬ify)); - } - - /// Register an I/O handler on the service. - pub fn register_handler(&self, handler: Arc + Send>) -> Result<(), IoError> { - self.io_service.register_handler(handler) - } - - /// Get a handle to the client. - pub fn client(&self) -> &Arc> { - &self.client - } -} - -struct ImportBlocks(Arc>); - -impl IoHandler for ImportBlocks { - fn message(&self, _io: &IoContext, message: &ClientIoMessage) { - if let ClientIoMessage::BlockVerified = *message { - self.0.import_verified(); - } - } -} - -#[cfg(test)] -mod tests { - use super::Service; - use ethcore::spec::Spec; - - use std::sync::Arc; - use cache::Cache; - use client::fetch; - use std::time::Duration; - use parking_lot::Mutex; - use ethcore::test_helpers; - - #[test] - fn it_works() { - let db = test_helpers::new_db(); - let spec = Spec::new_test(); - let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); - - Service::start(Default::default(), &spec, fetch::unavailable(), db, cache).unwrap(); - } -} diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs deleted file mode 100644 index 93e912e1d62..00000000000 --- a/ethcore/light/src/lib.rs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Light client logic and implementation. -//! -//! A "light" client stores very little chain-related data locally -//! unlike a full node, which stores all blocks, headers, receipts, and more. -//! -//! This enables the client to have a much lower resource footprint in -//! exchange for the cost of having to ask the network for state data -//! while responding to queries. This makes a light client unsuitable for -//! low-latency applications, but perfectly suitable for simple everyday -//! use-cases like sending transactions from a personal account. -//! -//! The light client performs a header-only sync, doing verification and pruning -//! historical blocks. Upon pruning, batches of 2048 blocks have a number => (hash, TD) -//! mapping sealed into "canonical hash tries" which can later be used to verify -//! historical block queries from peers. - -#![deny(missing_docs)] - -pub mod client; -pub mod cht; -pub mod net; -pub mod on_demand; -pub mod transaction_queue; -pub mod cache; -pub mod provider; - -mod types; - -pub use self::cache::Cache; -pub use self::provider::{Provider, MAX_HEADERS_PER_REQUEST}; -pub use self::transaction_queue::TransactionQueue; -pub use types::request as request; - -#[macro_use] -extern crate serde_derive; - -#[macro_use] -extern crate log; - -extern crate bincode; -extern crate common_types; -extern crate ethcore_blockchain; -extern crate ethcore_db; -extern crate ethcore_io as io; -extern crate ethcore_network as network; -extern crate parity_bytes as bytes; -extern crate ethereum_types; -extern crate ethcore; -extern crate hash_db; -extern crate heapsize; -extern crate failsafe; -extern crate futures; -extern crate itertools; -extern crate keccak_hasher; -extern crate memory_db; -extern crate trie_db as trie; -extern crate patricia_trie_ethereum as ethtrie; -extern crate fastmap; -extern crate rand; -extern crate rlp; -extern crate parking_lot; -#[macro_use] -extern crate rlp_derive; -extern crate serde; -extern crate smallvec; -extern crate stats; -extern crate vm; -extern crate keccak_hash as hash; -extern crate triehash_ethereum as triehash; -extern crate kvdb; -extern crate memory_cache; -#[macro_use] -extern crate error_chain; - -#[cfg(test)] -extern crate kvdb_memorydb; -#[cfg(test)] -extern crate tempdir; -extern crate journaldb; diff --git a/ethcore/light/src/net/context.rs b/ethcore/light/src/net/context.rs deleted file mode 100644 index 6e38959cc00..00000000000 --- a/ethcore/light/src/net/context.rs +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! I/O and event context generalizations. - -use network::{NetworkContext, PeerId, NodeId}; - -use super::{Announcement, LightProtocol, ReqId}; -use super::error::Error; -use request::NetworkRequests as Requests; - -/// An I/O context which allows sending and receiving packets as well as -/// disconnecting peers. This is used as a generalization of the portions -/// of a p2p network which the light protocol structure makes use of. -pub trait IoContext { - /// Send a packet to a specific peer. - fn send(&self, peer: PeerId, packet_id: u8, packet_body: Vec); - - /// Respond to a peer's message. Only works if this context is a byproduct - /// of a packet handler. - fn respond(&self, packet_id: u8, packet_body: Vec); - - /// Disconnect a peer. - fn disconnect_peer(&self, peer: PeerId); - - /// Disable a peer -- this is a disconnect + a time-out. - fn disable_peer(&self, peer: PeerId); - - /// Get a peer's protocol version. - fn protocol_version(&self, peer: PeerId) -> Option; - - /// Persistent peer id - fn persistent_peer_id(&self, peer: PeerId) -> Option; - - /// Whether given peer id is reserved peer - fn is_reserved_peer(&self, peer: PeerId) -> bool; -} - -impl IoContext for T where T: ?Sized + NetworkContext { - fn send(&self, peer: PeerId, packet_id: u8, packet_body: Vec) { - if let Err(e) = self.send(peer, packet_id, packet_body) { - debug!(target: "pip", "Error sending packet to peer {}: {}", peer, e); - } - } - - fn respond(&self, packet_id: u8, packet_body: Vec) { - if let Err(e) = self.respond(packet_id, packet_body) { - debug!(target: "pip", "Error responding to peer message: {}", e); - } - } - - fn disconnect_peer(&self, peer: PeerId) { - trace!(target: "pip", "Initiating disconnect of peer {}", peer); - NetworkContext::disconnect_peer(self, peer); - } - - fn disable_peer(&self, peer: PeerId) { - trace!(target: "pip", "Initiating disable of peer {}", peer); - NetworkContext::disable_peer(self, peer); - } - - fn protocol_version(&self, peer: PeerId) -> Option { - self.protocol_version(self.subprotocol_name(), peer) - } - - fn persistent_peer_id(&self, peer: PeerId) -> Option { - self.session_info(peer).and_then(|info| info.id) - } - - fn is_reserved_peer(&self, peer: PeerId) -> bool { - NetworkContext::is_reserved_peer(self, peer) - } -} - -/// Basic context for the protocol. -pub trait BasicContext { - /// Returns the relevant's peer persistent Id (aka NodeId). - fn persistent_peer_id(&self, peer: PeerId) -> Option; - - /// Make a request from a peer. - /// - /// Fails on: nonexistent peer, network error, peer not server, - /// insufficient credits. Does not check capabilities before sending. - /// On success, returns a request id which can later be coordinated - /// with an event. - fn request_from(&self, peer: PeerId, request: Requests) -> Result; - - /// Make an announcement of new capabilities to the rest of the peers. - // TODO: maybe just put this on a timer in LightProtocol? - fn make_announcement(&self, announcement: Announcement); - - /// Disconnect a peer. - fn disconnect_peer(&self, peer: PeerId); - - /// Disable a peer. - fn disable_peer(&self, peer: PeerId); -} - -/// Context for a protocol event which has a peer ID attached. -pub trait EventContext: BasicContext { - /// Get the peer relevant to the event e.g. message sender, - /// disconnected/connected peer. - fn peer(&self) -> PeerId; - - /// Treat the event context as a basic context. - fn as_basic(&self) -> &BasicContext; -} - -/// Basic context. -pub struct TickCtx<'a> { - /// Io context to enable dispatch. - pub io: &'a IoContext, - /// Protocol implementation. - pub proto: &'a LightProtocol, -} - -impl<'a> BasicContext for TickCtx<'a> { - fn persistent_peer_id(&self, id: PeerId) -> Option { - self.io.persistent_peer_id(id) - } - - fn request_from(&self, peer: PeerId, requests: Requests) -> Result { - self.proto.request_from(self.io, peer, requests) - } - - fn make_announcement(&self, announcement: Announcement) { - self.proto.make_announcement(self.io, announcement); - } - - fn disconnect_peer(&self, peer: PeerId) { - self.io.disconnect_peer(peer); - } - - fn disable_peer(&self, peer: PeerId) { - self.io.disable_peer(peer); - } -} - -/// Concrete implementation of `EventContext` over the light protocol struct and -/// an io context. -pub struct Ctx<'a> { - /// Io context to enable immediate response to events. - pub io: &'a IoContext, - /// Protocol implementation. - pub proto: &'a LightProtocol, - /// Relevant peer for event. - pub peer: PeerId, -} - -impl<'a> BasicContext for Ctx<'a> { - fn persistent_peer_id(&self, id: PeerId) -> Option { - self.io.persistent_peer_id(id) - } - - fn request_from(&self, peer: PeerId, requests: Requests) -> Result { - self.proto.request_from(self.io, peer, requests) - } - - fn make_announcement(&self, announcement: Announcement) { - self.proto.make_announcement(self.io, announcement); - } - - fn disconnect_peer(&self, peer: PeerId) { - self.io.disconnect_peer(peer); - } - - fn disable_peer(&self, peer: PeerId) { - self.io.disable_peer(peer); - } -} - -impl<'a> EventContext for Ctx<'a> { - fn peer(&self) -> PeerId { - self.peer - } - - fn as_basic(&self) -> &BasicContext { - &*self - } -} diff --git a/ethcore/light/src/net/error.rs b/ethcore/light/src/net/error.rs deleted file mode 100644 index b29327c600a..00000000000 --- a/ethcore/light/src/net/error.rs +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Defines error types and levels of punishment to use upon -//! encountering. - -use std::fmt; -use {rlp, network}; - -/// Levels of punishment. -/// -/// Currently just encompasses two different kinds of disconnect and -/// no punishment, but this is where reputation systems might come into play. -// In ascending order -#[derive(Debug, PartialEq, Eq)] -pub enum Punishment { - /// Perform no punishment. - None, - /// Disconnect the peer, but don't prevent them from reconnecting. - Disconnect, - /// Disconnect the peer and prevent them from reconnecting. - Disable, -} - -/// Kinds of errors which can be encountered in the course of LES. -#[derive(Debug)] -pub enum Error { - /// An RLP decoding error. - Rlp(rlp::DecoderError), - /// A network error. - Network(network::Error), - /// Out of credits. - NoCredits, - /// Unrecognized packet code. - UnrecognizedPacket(u8), - /// Unexpected handshake. - UnexpectedHandshake, - /// Peer on wrong network (wrong NetworkId or genesis hash) - WrongNetwork, - /// Unknown peer. - UnknownPeer, - /// Unsolicited response. - UnsolicitedResponse, - /// Bad back-reference in request. - BadBackReference, - /// Not a server. - NotServer, - /// Unsupported protocol version. - UnsupportedProtocolVersion(u8), - /// Bad protocol version. - BadProtocolVersion, - /// Peer is overburdened. - Overburdened, - /// No handler kept the peer. - RejectedByHandlers, -} - -impl Error { - /// What level of punishment does this error warrant? - pub fn punishment(&self) -> Punishment { - match *self { - Error::Rlp(_) => Punishment::Disable, - Error::Network(_) => Punishment::None, - Error::NoCredits => Punishment::Disable, - Error::UnrecognizedPacket(_) => Punishment::Disconnect, - Error::UnexpectedHandshake => Punishment::Disconnect, - Error::WrongNetwork => Punishment::Disable, - Error::UnknownPeer => Punishment::Disconnect, - Error::UnsolicitedResponse => Punishment::Disable, - Error::BadBackReference => Punishment::Disable, - Error::NotServer => Punishment::Disable, - Error::UnsupportedProtocolVersion(_) => Punishment::Disable, - Error::BadProtocolVersion => Punishment::Disable, - Error::Overburdened => Punishment::None, - Error::RejectedByHandlers => Punishment::Disconnect, - } - } -} - -impl From for Error { - fn from(err: rlp::DecoderError) -> Self { - Error::Rlp(err) - } -} - -impl From for Error { - fn from(err: network::Error) -> Self { - Error::Network(err) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::Rlp(ref err) => err.fmt(f), - Error::Network(ref err) => err.fmt(f), - Error::NoCredits => write!(f, "Out of request credits"), - Error::UnrecognizedPacket(code) => write!(f, "Unrecognized packet: 0x{:x}", code), - Error::UnexpectedHandshake => write!(f, "Unexpected handshake"), - Error::WrongNetwork => write!(f, "Wrong network"), - Error::UnknownPeer => write!(f, "Unknown peer"), - Error::UnsolicitedResponse => write!(f, "Peer provided unsolicited data"), - Error::BadBackReference => write!(f, "Bad back-reference in request."), - Error::NotServer => write!(f, "Peer not a server."), - Error::UnsupportedProtocolVersion(pv) => write!(f, "Unsupported protocol version: {}", pv), - Error::BadProtocolVersion => write!(f, "Bad protocol version in handshake"), - Error::Overburdened => write!(f, "Peer overburdened"), - Error::RejectedByHandlers => write!(f, "No handler kept this peer"), - } - } -} diff --git a/ethcore/light/src/net/load_timer.rs b/ethcore/light/src/net/load_timer.rs deleted file mode 100644 index 9dc033c47a6..00000000000 --- a/ethcore/light/src/net/load_timer.rs +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Request load timer and distribution manager. -//! -//! This uses empirical samples of the length of time taken to respond -//! to requests in order to inform request credit costs. -//! -//! The average request time is determined by an exponential moving average -//! of the mean request times during the last `MOVING_SAMPLE_SIZE` time periods of -//! length `TIME_PERIOD_MS`, with the exception that time periods where no data is -//! gathered are excluded. - -use std::collections::{HashMap, VecDeque}; -use std::fs::File; -use std::path::PathBuf; -use std::time::{Duration, Instant}; - -use request::{CompleteRequest, Kind}; - -use bincode; -use parking_lot::{RwLock, Mutex}; - -/// Number of time periods samples should be kept for. -pub const MOVING_SAMPLE_SIZE: usize = 256; - -/// Stores rolling load timer samples. -// TODO: switch to bigint if possible (FP casts aren't available) -pub trait SampleStore: Send + Sync { - /// Load samples. - fn load(&self) -> HashMap>; - - /// Store all samples. - fn store(&self, samples: &HashMap>); -} - -// get a hardcoded, arbitrarily determined (but intended overestimate) -// of the time it takes to serve a request of the given kind. -// -// TODO: seed this with empirical data. -fn hardcoded_serve_time(kind: Kind) -> Duration { - Duration::new(0, match kind { - Kind::Headers => 500_000, - Kind::HeaderProof => 500_000, - Kind::TransactionIndex => 500_000, - Kind::Receipts => 1_000_000, - Kind::Body => 1_000_000, - Kind::Account => 1_500_000, - Kind::Storage => 2_000_000, - Kind::Code => 1_500_000, - Kind::Execution => 250, // per gas. - Kind::Signal => 500_000, - }) -} - -/// A no-op store. -pub struct NullStore; - -impl SampleStore for NullStore { - fn load(&self) -> HashMap> { HashMap::new() } - fn store(&self, _samples: &HashMap>) { } -} - -/// Request load distributions. -pub struct LoadDistribution { - active_period: RwLock>>, - samples: RwLock>>, -} - -impl LoadDistribution { - /// Load rolling samples from the given store. - pub fn load(store: &SampleStore) -> Self { - let mut samples = store.load(); - - for kind_samples in samples.values_mut() { - while kind_samples.len() > MOVING_SAMPLE_SIZE { - kind_samples.pop_front(); - } - } - - LoadDistribution { - active_period: RwLock::new(HashMap::new()), - samples: RwLock::new(samples), - } - } - - /// Begin a timer. - pub fn begin_timer<'a>(&'a self, req: &CompleteRequest) -> LoadTimer<'a> { - let kind = req.kind(); - let n = match *req { - CompleteRequest::Headers(ref req) => req.max, - CompleteRequest::Execution(ref req) => req.gas.low_u64(), - _ => 1, - }; - - LoadTimer { - start: Instant::now(), - n, - dist: self, - kind, - } - } - - /// Calculate EMA of load for a specific request kind. - /// If there is no data for the given request kind, no EMA will be calculated, - /// but a hardcoded time will be returned. - pub fn expected_time(&self, kind: Kind) -> Duration { - let samples = self.samples.read(); - samples.get(&kind).and_then(|s| { - if s.is_empty() { return None } - - let alpha: f64 = 1_f64 / s.len() as f64; - let start = *s.front().expect("length known to be non-zero; qed") as f64; - let ema = s.iter().skip(1).fold(start, |a, &c| { - (alpha * c as f64) + ((1.0 - alpha) * a) - }); - - Some(Duration::from_nanos(ema as u64)) - }).unwrap_or_else(move || hardcoded_serve_time(kind)) - } - - /// End the current time period. Provide a store to - pub fn end_period(&self, store: &SampleStore) { - let active_period = self.active_period.read(); - let mut samples = self.samples.write(); - - for (&kind, set) in active_period.iter() { - let (elapsed, n) = ::std::mem::replace(&mut *set.lock(), (0, 0)); - if n == 0 { continue } - - let kind_samples = samples.entry(kind) - .or_insert_with(|| VecDeque::with_capacity(MOVING_SAMPLE_SIZE)); - - if kind_samples.len() == MOVING_SAMPLE_SIZE { kind_samples.pop_front(); } - kind_samples.push_back(elapsed / n); - } - - store.store(&*samples); - } - - fn update(&self, kind: Kind, elapsed: Duration, n: u64) { - macro_rules! update_counters { - ($counters: expr) => { - $counters.0 = $counters.0.saturating_add({ elapsed.as_secs() * 1_000_000_000 + elapsed.subsec_nanos() as u64 }); - $counters.1 = $counters.1.saturating_add(n); - } - }; - - { - let set = self.active_period.read(); - if let Some(counters) = set.get(&kind) { - let mut counters = counters.lock(); - update_counters!(counters); - return; - } - } - - let mut set = self.active_period.write(); - let counters = set - .entry(kind) - .or_insert_with(|| Mutex::new((0, 0))); - - update_counters!(counters.get_mut()); - } -} - -/// A timer for a single request. -/// On drop, this will update the distribution. -pub struct LoadTimer<'a> { - start: Instant, - n: u64, - dist: &'a LoadDistribution, - kind: Kind, -} - -impl<'a> Drop for LoadTimer<'a> { - fn drop(&mut self) { - let elapsed = self.start.elapsed(); - self.dist.update(self.kind, elapsed, self.n); - } -} - -/// A store which writes directly to a file. -pub struct FileStore(pub PathBuf); - -impl SampleStore for FileStore { - fn load(&self) -> HashMap> { - File::open(&self.0) - .map_err(|e| Box::new(bincode::ErrorKind::IoError(e))) - .and_then(|mut file| bincode::deserialize_from(&mut file, bincode::Infinite)) - .unwrap_or_else(|_| HashMap::new()) - } - - fn store(&self, samples: &HashMap>) { - let res = File::create(&self.0) - .map_err(|e| Box::new(bincode::ErrorKind::IoError(e))) - .and_then(|mut file| bincode::serialize_into(&mut file, samples, bincode::Infinite)); - - if let Err(e) = res { - warn!(target: "pip", "Error writing light request timing samples to file: {}", e); - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use request::Kind; - - #[test] - fn hardcoded_before_data() { - let dist = LoadDistribution::load(&NullStore); - assert_eq!(dist.expected_time(Kind::Headers), hardcoded_serve_time(Kind::Headers)); - - dist.update(Kind::Headers, Duration::new(0, 100_000), 100); - dist.end_period(&NullStore); - - assert_eq!(dist.expected_time(Kind::Headers), Duration::new(0, 1000)); - } - - #[test] - fn moving_average() { - let dist = LoadDistribution::load(&NullStore); - - let mut sum = 0; - - for (i, x) in (0..10).map(|x| x * 10_000).enumerate() { - dist.update(Kind::Headers, Duration::new(0, x), 1); - dist.end_period(&NullStore); - - sum += x; - if i == 0 { continue } - - let moving_average = dist.expected_time(Kind::Headers); - - // should be weighted below the maximum entry. - let arith_average = (sum as f64 / (i + 1) as f64) as u32; - assert!(moving_average < Duration::new(0, x)); - - // when there are only 2 entries, they should be equal due to choice of - // ALPHA = 1/N. - // otherwise, the weight should be below the arithmetic mean because the much - // smaller previous values are discounted less. - if i == 1 { - assert_eq!(moving_average, Duration::new(0, arith_average)); - } else { - assert!(moving_average < Duration::new(0, arith_average)) - } - } - } - - #[test] - fn file_store() { - let tempdir = ::tempdir::TempDir::new("").unwrap(); - let path = tempdir.path().join("file"); - let store = FileStore(path); - - let mut samples = store.load(); - assert!(samples.is_empty()); - samples.insert(Kind::Headers, vec![5, 2, 7, 2, 2, 4].into()); - samples.insert(Kind::Execution, vec![1, 1, 100, 250].into()); - - store.store(&samples); - - let dup = store.load(); - - assert_eq!(samples, dup); - } -} diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs deleted file mode 100644 index 2fd6c340e03..00000000000 --- a/ethcore/light/src/net/mod.rs +++ /dev/null @@ -1,1199 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! PLP Protocol Version 1 implementation. -//! -//! This uses a "Provider" to answer requests. - -use common_types::transaction::UnverifiedTransaction; -use ethereum_types::{H256, U256}; -use io::TimerToken; -use kvdb::DBValue; -use network::{NetworkProtocolHandler, NetworkContext, PeerId}; -use parking_lot::{Mutex, RwLock}; -use provider::Provider; -use request::{Request, NetworkRequests as Requests, Response}; -use rlp::{RlpStream, Rlp}; -use std::collections::{HashMap, HashSet, VecDeque}; -use std::fmt; -use std::ops::{BitOr, BitAnd, Not}; -use std::sync::Arc; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::time::{Duration, Instant}; - -use self::request_credits::{Credits, FlowParams}; -use self::context::{Ctx, TickCtx}; -use self::error::Punishment; -use self::load_timer::{LoadDistribution, NullStore, MOVING_SAMPLE_SIZE}; -use self::request_set::RequestSet; -use self::id_guard::IdGuard; - -mod context; -mod error; -mod load_timer; -mod status; -mod request_set; - -#[cfg(test)] -mod tests; - -pub mod request_credits; - -pub use self::context::{BasicContext, EventContext, IoContext}; -pub use self::error::Error; -pub use self::load_timer::{SampleStore, FileStore}; -pub use self::status::{Status, Capabilities, Announcement}; - -const TIMEOUT: TimerToken = 0; -const TIMEOUT_INTERVAL: Duration = Duration::from_secs(1); - -const TICK_TIMEOUT: TimerToken = 1; -const TICK_TIMEOUT_INTERVAL: Duration = Duration::from_secs(5); - -const PROPAGATE_TIMEOUT: TimerToken = 2; -const PROPAGATE_TIMEOUT_INTERVAL: Duration = Duration::from_secs(5); - -const RECALCULATE_COSTS_TIMEOUT: TimerToken = 3; -const RECALCULATE_COSTS_INTERVAL: Duration = Duration::from_secs(60 * 60); - -const STATISTICS_TIMEOUT: TimerToken = 4; -const STATISTICS_INTERVAL: Duration = Duration::from_secs(15); - -/// Maximum load share for the light server -pub const MAX_LIGHTSERV_LOAD: f64 = 0.5; - -/// Factor to multiply leecher count to cater for -/// extra sudden connections (should be >= 1.0) -pub const LEECHER_COUNT_FACTOR: f64 = 1.25; - -// minimum interval between updates. -const UPDATE_INTERVAL: Duration = Duration::from_millis(5000); - -/// Packet count for PIP. -const PACKET_COUNT_V1: u8 = 9; - -/// Supported protocol versions. -pub const PROTOCOL_VERSIONS: &[(u8, u8)] = &[ - (1, PACKET_COUNT_V1), -]; - -/// Max protocol version. -pub const MAX_PROTOCOL_VERSION: u8 = 1; - -// packet ID definitions. -mod packet { - // the status packet. - pub const STATUS: u8 = 0x00; - - // announcement of new block hashes or capabilities. - pub const ANNOUNCE: u8 = 0x01; - - // request and response. - pub const REQUEST: u8 = 0x02; - pub const RESPONSE: u8 = 0x03; - - // request credits update and acknowledgement. - pub const UPDATE_CREDITS: u8 = 0x04; - pub const ACKNOWLEDGE_UPDATE: u8 = 0x05; - - // relay transactions to peers. - pub const SEND_TRANSACTIONS: u8 = 0x06; - - // two packets were previously meant to be reserved for epoch proofs. - // these have since been moved to requests. -} - -// timeouts for different kinds of requests. all values are in milliseconds. -mod timeout { - use std::time::Duration; - - pub const HANDSHAKE: Duration = Duration::from_millis(4_000); - pub const ACKNOWLEDGE_UPDATE: Duration = Duration::from_millis(5_000); - pub const BASE: u64 = 2_500; // base timeout for packet. - - // timeouts per request within packet. - pub const HEADERS: u64 = 250; // per header? - pub const TRANSACTION_INDEX: u64 = 100; - pub const BODY: u64 = 50; - pub const RECEIPT: u64 = 50; - pub const PROOF: u64 = 100; // state proof - pub const CONTRACT_CODE: u64 = 100; - pub const HEADER_PROOF: u64 = 100; - pub const TRANSACTION_PROOF: u64 = 1000; // per gas? - pub const EPOCH_SIGNAL: u64 = 200; -} - -/// A request id. -#[cfg(not(test))] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] -pub struct ReqId(usize); - -#[cfg(test)] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] -pub struct ReqId(pub usize); - -impl fmt::Display for ReqId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Request #{}", self.0) - } -} - -// A pending peer: one we've sent our status to but -// may not have received one for. -struct PendingPeer { - sent_head: H256, - last_update: Instant, -} - -/// Relevant data to each peer. Not accessible publicly, only `pub` due to -/// limitations of the privacy system. -pub struct Peer { - local_credits: Credits, // their credits relative to us - status: Status, - capabilities: Capabilities, - remote_flow: Option<(Credits, FlowParams)>, - sent_head: H256, // last chain head we've given them. - last_update: Instant, - pending_requests: RequestSet, - failed_requests: Vec, - propagated_transactions: HashSet, - skip_update: bool, - local_flow: Arc, - awaiting_acknowledge: Option<(Instant, Arc)>, -} - -/// Whether or not a peer was kept by a handler -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum PeerStatus { - /// The peer was kept - Kept, - /// The peer was not kept - Unkept, -} - -impl Not for PeerStatus { - type Output = Self; - - fn not(self) -> Self { - use self::PeerStatus::*; - - match self { - Kept => Unkept, - Unkept => Kept, - } - } -} - -impl BitAnd for PeerStatus { - type Output = Self; - - fn bitand(self, other: Self) -> Self { - use self::PeerStatus::*; - - match (self, other) { - (Kept, Kept) => Kept, - _ => Unkept, - } - } -} - -impl BitOr for PeerStatus { - type Output = Self; - - fn bitor(self, other: Self) -> Self { - use self::PeerStatus::*; - - match (self, other) { - (_, Kept) | (Kept, _) => Kept, - _ => Unkept, - } - } -} - -/// A light protocol event handler. -/// -/// Each handler function takes a context which describes the relevant peer -/// and gives references to the IO layer and protocol structure so new messages -/// can be dispatched immediately. -/// -/// Request responses are not guaranteed to be complete or valid, but passed IDs will be correct. -/// Response handlers are not given a copy of the original request; it is assumed -/// that relevant data will be stored by interested handlers. -pub trait Handler: Send + Sync { - /// Called when a peer connects. - fn on_connect( - &self, - _ctx: &EventContext, - _status: &Status, - _capabilities: &Capabilities - ) -> PeerStatus { PeerStatus::Kept } - /// Called when a peer disconnects, with a list of unfulfilled request IDs as - /// of yet. - fn on_disconnect(&self, _ctx: &EventContext, _unfulfilled: &[ReqId]) { } - /// Called when a peer makes an announcement. - fn on_announcement(&self, _ctx: &EventContext, _announcement: &Announcement) { } - /// Called when a peer requests relay of some transactions. - fn on_transactions(&self, _ctx: &EventContext, _relay: &[UnverifiedTransaction]) { } - /// Called when a peer responds to requests. - /// Responses not guaranteed to contain valid data and are not yet checked against - /// the requests they correspond to. - fn on_responses(&self, _ctx: &EventContext, _req_id: ReqId, _responses: &[Response]) { } - /// Called when a peer responds with a transaction proof. Each proof is a vector of state items. - fn on_transaction_proof(&self, _ctx: &EventContext, _req_id: ReqId, _state_items: &[DBValue]) { } - /// Called to "tick" the handler periodically. - fn tick(&self, _ctx: &BasicContext) { } - /// Called on abort. This signals to handlers that they should clean up - /// and ignore peers. - // TODO: coreresponding `on_activate`? - fn on_abort(&self) { } -} - -/// Configuration. -pub struct Config { - /// How many stored seconds of credits peers should be able to accumulate. - pub max_stored_seconds: u64, - /// The network config median peers (used as default peer count) - pub median_peers: f64, -} - -impl Default for Config { - fn default() -> Self { - const MEDIAN_PEERS: f64 = 25.0; - const MAX_ACCUMULATED: u64 = 60 * 5; // only charge for 5 minutes. - - Config { - max_stored_seconds: MAX_ACCUMULATED, - median_peers: MEDIAN_PEERS, - } - } -} - -/// Protocol initialization parameters. -pub struct Params { - /// Network id. - pub network_id: u64, - /// Config. - pub config: Config, - /// Initial capabilities. - pub capabilities: Capabilities, - /// The sample store (`None` if data shouldn't persist between runs). - pub sample_store: Option>, -} - -/// Type alias for convenience. -pub type PeerMap = HashMap>; - -mod id_guard { - - use network::PeerId; - use parking_lot::RwLockReadGuard; - - use super::{PeerMap, ReqId}; - - // Guards success or failure of given request. - // On drop, inserts the req_id into the "failed requests" - // set for the peer unless defused. In separate module to enforce correct usage. - pub struct IdGuard<'a> { - peers: RwLockReadGuard<'a, PeerMap>, - peer_id: PeerId, - req_id: ReqId, - active: bool, - } - - impl<'a> IdGuard<'a> { - /// Create a new `IdGuard`, which will prevent access of the inner ReqId - /// (for forming responses, triggering handlers) until defused - pub fn new(peers: RwLockReadGuard<'a, PeerMap>, peer_id: PeerId, req_id: ReqId) -> Self { - IdGuard { - peers, - peer_id, - req_id, - active: true, - } - } - - /// Defuse the guard, signalling that the request has been successfully decoded. - pub fn defuse(mut self) -> ReqId { - // can't use the mem::forget trick here since we need the - // read guard to drop. - self.active = false; - self.req_id - } - } - - impl<'a> Drop for IdGuard<'a> { - fn drop(&mut self) { - if !self.active { return } - if let Some(p) = self.peers.get(&self.peer_id) { - p.lock().failed_requests.push(self.req_id); - } - } - } -} - -/// Provides various statistics that could -/// be used to compute costs -pub struct Statistics { - /// Samples of peer count - peer_counts: VecDeque, -} - -impl Statistics { - /// Create a new Statistics instance - pub fn new() -> Self { - Statistics { - peer_counts: VecDeque::with_capacity(MOVING_SAMPLE_SIZE), - } - } - - /// Add a new peer_count sample - pub fn add_peer_count(&mut self, peer_count: usize) { - while self.peer_counts.len() >= MOVING_SAMPLE_SIZE { - self.peer_counts.pop_front(); - } - self.peer_counts.push_back(peer_count); - } - - /// Get the average peer count from previous samples. Is always >= 1.0 - pub fn avg_peer_count(&self) -> f64 { - let len = self.peer_counts.len(); - if len == 0 { - return 1.0; - } - let avg = self.peer_counts.iter() - .fold(0, |sum: u32, &v| sum.saturating_add(v as u32)) as f64 - / len as f64; - avg.max(1.0) - } -} - -/// This is an implementation of the light ethereum network protocol, abstracted -/// over a `Provider` of data and a p2p network. -/// -/// This is simply designed for request-response purposes. Higher level uses -/// of the protocol, such as synchronization, will function as wrappers around -/// this system. -// -// LOCK ORDER: -// Locks must be acquired in the order declared, and when holding a read lock -// on the peers, only one peer may be held at a time. -pub struct LightProtocol { - provider: Arc, - config: Config, - genesis_hash: H256, - network_id: u64, - pending_peers: RwLock>, - peers: RwLock, - capabilities: RwLock, - flow_params: RwLock>, - free_flow_params: Arc, - handlers: Vec>, - req_id: AtomicUsize, - sample_store: Box, - load_distribution: LoadDistribution, - statistics: RwLock, -} - -impl LightProtocol { - /// Create a new instance of the protocol manager. - pub fn new(provider: Arc, params: Params) -> Self { - debug!(target: "pip", "Initializing light protocol handler"); - - let genesis_hash = provider.chain_info().genesis_hash; - let sample_store = params.sample_store.unwrap_or_else(|| Box::new(NullStore)); - let load_distribution = LoadDistribution::load(&*sample_store); - // Default load share relative to median peers - let load_share = MAX_LIGHTSERV_LOAD / params.config.median_peers; - let flow_params = FlowParams::from_request_times( - |kind| load_distribution.expected_time(kind), - load_share, - Duration::from_secs(params.config.max_stored_seconds), - ); - - LightProtocol { - provider, - config: params.config, - genesis_hash, - network_id: params.network_id, - pending_peers: RwLock::new(HashMap::new()), - peers: RwLock::new(HashMap::new()), - capabilities: RwLock::new(params.capabilities), - flow_params: RwLock::new(Arc::new(flow_params)), - free_flow_params: Arc::new(FlowParams::free()), - handlers: Vec::new(), - req_id: AtomicUsize::new(0), - sample_store, - load_distribution, - statistics: RwLock::new(Statistics::new()), - } - } - - /// Attempt to get peer status. - pub fn peer_status(&self, peer: PeerId) -> Option { - self.peers.read().get(&peer) - .map(|peer| peer.lock().status.clone()) - } - - /// Get number of (connected, active) peers. - pub fn peer_count(&self) -> (usize, usize) { - let num_pending = self.pending_peers.read().len(); - let peers = self.peers.read(); - ( - num_pending + peers.len(), - peers.values().filter(|p| !p.lock().pending_requests.is_empty()).count(), - ) - } - - /// Get the number of active light peers downloading from the - /// node - pub fn leecher_count(&self) -> usize { - let credit_limit = *self.flow_params.read().limit(); - // Count the number of peers that used some credit - self.peers.read().iter() - .filter(|(_, p)| p.lock().local_credits.current() < credit_limit) - .count() - } - - /// Make a request to a peer. - /// - /// Fails on: nonexistent peer, network error, peer not server, - /// insufficient credits. Does not check capabilities before sending. - /// On success, returns a request id which can later be coordinated - /// with an event. - pub fn request_from(&self, io: &IoContext, peer_id: PeerId, requests: Requests) -> Result { - let peers = self.peers.read(); - let peer = match peers.get(&peer_id) { - Some(peer) => peer, - None => return Err(Error::UnknownPeer), - }; - - let mut peer = peer.lock(); - let peer = &mut *peer; - match peer.remote_flow { - None => Err(Error::NotServer), - Some((ref mut creds, ref params)) => { - // apply recharge to credits if there's no pending requests. - if peer.pending_requests.is_empty() { - params.recharge(creds); - } - - // compute and deduct cost. - let pre_creds = creds.current(); - let cost = match params.compute_cost_multi(requests.requests()) { - Some(cost) => cost, - None => return Err(Error::NotServer), - }; - - creds.deduct_cost(cost)?; - - trace!(target: "pip", "requesting from peer {}. Cost: {}; Available: {}", - peer_id, cost, pre_creds); - - let req_id = ReqId(self.req_id.fetch_add(1, Ordering::SeqCst)); - io.send(peer_id, packet::REQUEST, { - let mut stream = RlpStream::new_list(2); - stream.append(&req_id.0).append_list(&requests.requests()); - stream.out() - }); - - // begin timeout. - peer.pending_requests.insert(req_id, requests, cost, Instant::now()); - Ok(req_id) - } - } - } - - /// Make an announcement of new chain head and capabilities to all peers. - /// The announcement is expected to be valid. - pub fn make_announcement(&self, io: &IoContext, mut announcement: Announcement) { - let mut reorgs_map = HashMap::new(); - let now = Instant::now(); - - // update stored capabilities - self.capabilities.write().update_from(&announcement); - - // calculate reorg info and send packets - for (peer_id, peer_info) in self.peers.read().iter() { - let mut peer_info = peer_info.lock(); - - // TODO: "urgent" announcements like new blocks? - // the timer approach will skip 1 (possibly 2) in rare occasions. - if peer_info.sent_head == announcement.head_hash || - peer_info.status.head_num >= announcement.head_num || - // fix for underflow reported in - // https://github.com/paritytech/parity-ethereum/issues/10419 - now < peer_info.last_update || - now - peer_info.last_update < UPDATE_INTERVAL { - continue - } - - peer_info.last_update = now; - - let reorg_depth = reorgs_map.entry(peer_info.sent_head) - .or_insert_with(|| { - match self.provider.reorg_depth(&announcement.head_hash, &peer_info.sent_head) { - Some(depth) => depth, - None => { - // both values will always originate locally -- this means something - // has gone really wrong - debug!(target: "pip", "couldn't compute reorganization depth between {:?} and {:?}", - &announcement.head_hash, &peer_info.sent_head); - 0 - } - } - }); - - peer_info.sent_head = announcement.head_hash; - announcement.reorg_depth = *reorg_depth; - - io.send(*peer_id, packet::ANNOUNCE, status::write_announcement(&announcement)); - } - } - - /// Add an event handler. - /// - /// These are intended to be added when the protocol structure - /// is initialized as a means of customizing its behavior, - /// and dispatching requests immediately upon events. - pub fn add_handler(&mut self, handler: Arc) { - self.handlers.push(handler); - } - - /// Signal to handlers that network activity is being aborted - /// and clear peer data. - pub fn abort(&self) { - for handler in &self.handlers { - handler.on_abort(); - } - - // acquire in order and hold. - let mut pending_peers = self.pending_peers.write(); - let mut peers = self.peers.write(); - - pending_peers.clear(); - peers.clear(); - } - - // Does the common pre-verification of responses before the response itself - // is actually decoded: - // - check whether peer exists - // - check whether request was made - // - check whether request kinds match - fn pre_verify_response(&self, peer: PeerId, raw: &Rlp) -> Result { - let req_id = ReqId(raw.val_at(0)?); - let cur_credits: U256 = raw.val_at(1)?; - - trace!(target: "pip", "pre-verifying response for {} from peer {}", req_id, peer); - - let peers = self.peers.read(); - let res = match peers.get(&peer) { - Some(peer_info) => { - let mut peer_info = peer_info.lock(); - let peer_info: &mut Peer = &mut *peer_info; - let req_info = peer_info.pending_requests.remove(req_id, Instant::now()); - let last_batched = peer_info.pending_requests.is_empty(); - let flow_info = peer_info.remote_flow.as_mut(); - - match (req_info, flow_info) { - (Some(_), Some(flow_info)) => { - let &mut (ref mut c, ref mut flow) = flow_info; - - // only update if the cumulative cost of the request set is zero. - // and this response wasn't from before request costs were updated. - if !peer_info.skip_update && last_batched { - let actual_credits = ::std::cmp::min(cur_credits, *flow.limit()); - c.update_to(actual_credits); - } - - if last_batched { peer_info.skip_update = false } - - Ok(()) - } - (None, _) => Err(Error::UnsolicitedResponse), - (_, None) => Err(Error::NotServer), // really should be impossible. - } - } - None => Err(Error::UnknownPeer), // probably only occurs in a race of some kind. - }; - - res.map(|_| IdGuard::new(peers, peer, req_id)) - } - - /// Handle a packet using the given io context. - /// Packet data is _untrusted_, which means that invalid data won't lead to - /// issues. - pub fn handle_packet(&self, io: &IoContext, peer: PeerId, packet_id: u8, data: &[u8]) { - let rlp = Rlp::new(data); - - trace!(target: "pip", "Incoming packet {} from peer {}", packet_id, peer); - - // handle the packet - let res = match packet_id { - packet::STATUS => self.status(peer, io, &rlp), - packet::ANNOUNCE => self.announcement(peer, io, &rlp), - - packet::REQUEST => self.request(peer, io, &rlp), - packet::RESPONSE => self.response(peer, io, &rlp), - - packet::UPDATE_CREDITS => self.update_credits(peer, io, &rlp), - packet::ACKNOWLEDGE_UPDATE => self.acknowledge_update(peer, io, &rlp), - - packet::SEND_TRANSACTIONS => self.relay_transactions(peer, io, &rlp), - - other => { - Err(Error::UnrecognizedPacket(other)) - } - }; - - if let Err(e) = res { - punish(peer, io, &e); - } - } - - // check timeouts and punish peers. - fn timeout_check(&self, io: &IoContext) { - let now = Instant::now(); - - // handshake timeout - { - let mut pending = self.pending_peers.write(); - let slowpokes: Vec<_> = pending.iter() - .filter(|&(_, ref peer)| { - peer.last_update + timeout::HANDSHAKE <= now - }) - .map(|(&p, _)| p) - .collect(); - - for slowpoke in slowpokes { - debug!(target: "pip", "Peer {} handshake timed out", slowpoke); - pending.remove(&slowpoke); - io.disconnect_peer(slowpoke); - } - } - - // request and update ack timeouts - let ack_duration = timeout::ACKNOWLEDGE_UPDATE; - { - for (peer_id, peer) in self.peers.read().iter() { - let peer = peer.lock(); - if peer.pending_requests.check_timeout(now) { - debug!(target: "pip", "Peer {} request timeout", peer_id); - io.disconnect_peer(*peer_id); - } - - if let Some((ref start, _)) = peer.awaiting_acknowledge { - if *start + ack_duration <= now { - debug!(target: "pip", "Peer {} update acknowledgement timeout", peer_id); - io.disconnect_peer(*peer_id); - } - } - } - } - } - - // propagate transactions to relay peers. - // if we aren't on the mainnet, we just propagate to all relay peers - fn propagate_transactions(&self, io: &IoContext) { - if self.capabilities.read().tx_relay { return } - - let ready_transactions = self.provider.transactions_to_propagate(); - if ready_transactions.is_empty() { return } - - trace!(target: "pip", "propagate transactions: {} ready", ready_transactions.len()); - - let all_transaction_hashes: HashSet<_> = ready_transactions.iter().map(|tx| tx.hash()).collect(); - let mut buf = Vec::new(); - - let peers = self.peers.read(); - for (peer_id, peer_info) in peers.iter() { - let mut peer_info = peer_info.lock(); - if !peer_info.capabilities.tx_relay { continue } - - let prop_filter = &mut peer_info.propagated_transactions; - *prop_filter = &*prop_filter & &all_transaction_hashes; - - // fill the buffer with all non-propagated transactions. - let to_propagate = ready_transactions.iter() - .filter(|tx| prop_filter.insert(tx.hash())) - .map(|tx| &tx.transaction); - - buf.extend(to_propagate); - - // propagate to the given peer. - if buf.is_empty() { continue } - io.send(*peer_id, packet::SEND_TRANSACTIONS, { - let mut stream = RlpStream::new_list(buf.len()); - for pending_tx in buf.drain(..) { - stream.append(pending_tx); - } - - stream.out() - }) - } - } - - /// called when a peer connects. - pub fn on_connect(&self, peer: PeerId, io: &IoContext) { - let proto_version = match io.protocol_version(peer).ok_or(Error::WrongNetwork) { - Ok(pv) => pv, - Err(e) => { punish(peer, io, &e); return } - }; - - if PROTOCOL_VERSIONS.iter().find(|x| x.0 == proto_version).is_none() { - punish(peer, io, &Error::UnsupportedProtocolVersion(proto_version)); - return; - } - - let chain_info = self.provider.chain_info(); - - let status = Status { - head_td: chain_info.total_difficulty, - head_hash: chain_info.best_block_hash, - head_num: chain_info.best_block_number, - genesis_hash: chain_info.genesis_hash, - protocol_version: proto_version as u32, // match peer proto version - network_id: self.network_id, - last_head: None, - }; - - let capabilities = self.capabilities.read(); - let cost_local_flow = self.flow_params.read(); - let local_flow = if io.is_reserved_peer(peer) { - &*self.free_flow_params - } else { - &**cost_local_flow - }; - let status_packet = status::write_handshake(&status, &capabilities, Some(local_flow)); - - self.pending_peers.write().insert(peer, PendingPeer { - sent_head: chain_info.best_block_hash, - last_update: Instant::now(), - }); - - trace!(target: "pip", "Sending status to peer {}", peer); - io.send(peer, packet::STATUS, status_packet); - } - - /// called when a peer disconnects. - pub fn on_disconnect(&self, peer: PeerId, io: &IoContext) { - trace!(target: "pip", "Peer {} disconnecting", peer); - - self.pending_peers.write().remove(&peer); - let unfulfilled = match self.peers.write().remove(&peer) { - None => return, - Some(peer_info) => { - let peer_info = peer_info.into_inner(); - let mut unfulfilled: Vec<_> = peer_info.pending_requests.collect_ids(); - unfulfilled.extend(peer_info.failed_requests); - - unfulfilled - } - }; - - for handler in &self.handlers { - handler.on_disconnect(&Ctx { - peer, - io, - proto: self, - }, &unfulfilled) - } - } - - /// Execute the given closure with a basic context derived from the I/O context. - pub fn with_context(&self, io: &IoContext, f: F) -> T - where F: FnOnce(&BasicContext) -> T - { - f(&TickCtx { - io, - proto: self, - }) - } - - fn tick_handlers(&self, io: &IoContext) { - for handler in &self.handlers { - handler.tick(&TickCtx { - io, - proto: self, - }) - } - } - - fn begin_new_cost_period(&self, io: &IoContext) { - self.load_distribution.end_period(&*self.sample_store); - - let avg_peer_count = self.statistics.read().avg_peer_count(); - // Load share relative to average peer count +LEECHER_COUNT_FACTOR% - let load_share = MAX_LIGHTSERV_LOAD / (avg_peer_count * LEECHER_COUNT_FACTOR); - let new_params = Arc::new(FlowParams::from_request_times( - |kind| self.load_distribution.expected_time(kind), - load_share, - Duration::from_secs(self.config.max_stored_seconds), - )); - *self.flow_params.write() = new_params.clone(); - trace!(target: "pip", "New cost period: avg_peers={} ; cost_table:{:?}", avg_peer_count, new_params.cost_table()); - - let peers = self.peers.read(); - let now = Instant::now(); - - let packet_body = { - let mut stream = RlpStream::new_list(3); - stream.append(new_params.limit()) - .append(new_params.recharge_rate()) - .append(new_params.cost_table()); - stream.out() - }; - - for (peer_id, peer_info) in peers.iter() { - let mut peer_info = peer_info.lock(); - - io.send(*peer_id, packet::UPDATE_CREDITS, packet_body.clone()); - peer_info.awaiting_acknowledge = Some((now, new_params.clone())); - } - } - - fn tick_statistics(&self) { - let leecher_count = self.leecher_count(); - self.statistics.write().add_peer_count(leecher_count); - } -} - -impl LightProtocol { - // Handle status message from peer. - fn status(&self, peer: PeerId, io: &IoContext, data: &Rlp) -> Result<(), Error> { - let pending = match self.pending_peers.write().remove(&peer) { - Some(pending) => pending, - None => { - return Err(Error::UnexpectedHandshake); - } - }; - - let (status, capabilities, flow_params) = status::parse_handshake(data)?; - - trace!(target: "pip", "Connected peer with chain head {:?}", (status.head_hash, status.head_num)); - - if (status.network_id, status.genesis_hash) != (self.network_id, self.genesis_hash) { - trace!(target: "pip", "peer {} wrong network: network_id is {} vs our {}, gh is {} vs our {}", - peer, status.network_id, self.network_id, status.genesis_hash, self.genesis_hash); - - return Err(Error::WrongNetwork); - } - - if Some(status.protocol_version as u8) != io.protocol_version(peer) { - return Err(Error::BadProtocolVersion); - } - - let remote_flow = flow_params.map(|params| (params.create_credits(), params)); - let local_flow = if io.is_reserved_peer(peer) { - self.free_flow_params.clone() - } else { - self.flow_params.read().clone() - }; - - self.peers.write().insert(peer, Mutex::new(Peer { - local_credits: local_flow.create_credits(), - status: status.clone(), - capabilities, - remote_flow, - sent_head: pending.sent_head, - last_update: pending.last_update, - pending_requests: RequestSet::default(), - failed_requests: Vec::new(), - propagated_transactions: HashSet::new(), - skip_update: false, - local_flow, - awaiting_acknowledge: None, - })); - - let any_kept = self.handlers.iter().map( - |handler| handler.on_connect( - &Ctx { - peer, - io, - proto: self, - }, - &status, - &capabilities - ) - ).fold(PeerStatus::Kept, PeerStatus::bitor); - - if any_kept == PeerStatus::Unkept { - Err(Error::RejectedByHandlers) - } else { - Ok(()) - } - } - - // Handle an announcement. - fn announcement(&self, peer: PeerId, io: &IoContext, data: &Rlp) -> Result<(), Error> { - if !self.peers.read().contains_key(&peer) { - debug!(target: "pip", "Ignoring announcement from unknown peer"); - return Ok(()) - } - - let announcement = status::parse_announcement(data)?; - - // scope to ensure locks are dropped before moving into handler-space. - { - let peers = self.peers.read(); - let peer_info = match peers.get(&peer) { - Some(info) => info, - None => return Ok(()), - }; - - let mut peer_info = peer_info.lock(); - - // update status. - { - // TODO: punish peer if they've moved backwards. - let status = &mut peer_info.status; - let last_head = status.head_hash; - status.head_hash = announcement.head_hash; - status.head_td = announcement.head_td; - status.head_num = announcement.head_num; - status.last_head = Some((last_head, announcement.reorg_depth)); - } - - // update capabilities. - peer_info.capabilities.update_from(&announcement); - } - - for handler in &self.handlers { - handler.on_announcement(&Ctx { - peer, - io, - proto: self, - }, &announcement); - } - - Ok(()) - } - - // Receive requests from a peer. - fn request(&self, peer_id: PeerId, io: &IoContext, raw: &Rlp) -> Result<(), Error> { - // the maximum amount of requests we'll fill in a single packet. - const MAX_REQUESTS: usize = 256; - - use ::request::Builder; - use ::request::CompleteRequest; - - let peers = self.peers.read(); - let peer = match peers.get(&peer_id) { - Some(peer) => peer, - None => { - debug!(target: "pip", "Ignoring request from unknown peer"); - return Ok(()) - } - }; - let mut peer = peer.lock(); - let peer: &mut Peer = &mut *peer; - - let req_id: u64 = raw.val_at(0)?; - let mut request_builder = Builder::default(); - - trace!(target: "pip", "Received requests (id: {}) from peer {}", req_id, peer_id); - - // deserialize requests, check costs and request validity. - peer.local_flow.recharge(&mut peer.local_credits); - - peer.local_credits.deduct_cost(peer.local_flow.base_cost())?; - for request_rlp in raw.at(1)?.iter().take(MAX_REQUESTS) { - let request: Request = request_rlp.as_val()?; - let cost = peer.local_flow.compute_cost(&request).ok_or(Error::NotServer)?; - peer.local_credits.deduct_cost(cost)?; - request_builder.push(request).map_err(|_| Error::BadBackReference)?; - } - - let requests = request_builder.build(); - let num_requests = requests.requests().len(); - trace!(target: "pip", "Beginning to respond to requests (id: {}) from peer {}", req_id, peer_id); - - // respond to all requests until one fails. - let responses = requests.respond_to_all(|complete_req| { - let _timer = self.load_distribution.begin_timer(&complete_req); - match complete_req { - CompleteRequest::Headers(req) => self.provider.block_headers(req).map(Response::Headers), - CompleteRequest::HeaderProof(req) => self.provider.header_proof(req).map(Response::HeaderProof), - CompleteRequest::TransactionIndex(req) => self.provider.transaction_index(req).map(Response::TransactionIndex), - CompleteRequest::Body(req) => self.provider.block_body(req).map(Response::Body), - CompleteRequest::Receipts(req) => self.provider.block_receipts(req).map(Response::Receipts), - CompleteRequest::Account(req) => self.provider.account_proof(req).map(Response::Account), - CompleteRequest::Storage(req) => self.provider.storage_proof(req).map(Response::Storage), - CompleteRequest::Code(req) => self.provider.contract_code(req).map(Response::Code), - CompleteRequest::Execution(req) => self.provider.transaction_proof(req).map(Response::Execution), - CompleteRequest::Signal(req) => self.provider.epoch_signal(req).map(Response::Signal), - } - }); - - trace!(target: "pip", "Responded to {}/{} requests in packet {}", responses.len(), num_requests, req_id); - trace!(target: "pip", "Peer {} has {} credits remaining.", peer_id, peer.local_credits.current()); - - io.respond(packet::RESPONSE, { - let mut stream = RlpStream::new_list(3); - let cur_credits = peer.local_credits.current(); - stream.append(&req_id).append(&cur_credits).append_list(&responses); - stream.out() - }); - Ok(()) - } - - // handle a packet with responses. - fn response(&self, peer: PeerId, io: &IoContext, raw: &Rlp) -> Result<(), Error> { - let (req_id, responses) = { - let id_guard = self.pre_verify_response(peer, &raw)?; - let responses: Vec = raw.list_at(2)?; - (id_guard.defuse(), responses) - }; - - for handler in &self.handlers { - handler.on_responses(&Ctx { - io, - proto: self, - peer, - }, req_id, &responses); - } - - Ok(()) - } - - // handle an update of request credits parameters. - fn update_credits(&self, peer_id: PeerId, io: &IoContext, raw: &Rlp) -> Result<(), Error> { - let peers = self.peers.read(); - - let peer = peers.get(&peer_id).ok_or(Error::UnknownPeer)?; - let mut peer = peer.lock(); - - trace!(target: "pip", "Received an update to request credit params from peer {}", peer_id); - - { - let &mut (ref mut credits, ref mut old_params) = peer.remote_flow.as_mut().ok_or(Error::NotServer)?; - old_params.recharge(credits); - - let new_params = FlowParams::new( - raw.val_at(0)?, // limit - raw.val_at(2)?, // cost table - raw.val_at(1)?, // recharge. - ); - - // preserve ratio of current : limit when updating params. - credits.maintain_ratio(*old_params.limit(), *new_params.limit()); - *old_params = new_params; - } - - // set flag to true when there is an in-flight request - // corresponding to old flow params. - if !peer.pending_requests.is_empty() { - peer.skip_update = true; - } - - // let peer know we've acknowledged the update. - io.respond(packet::ACKNOWLEDGE_UPDATE, Vec::new()); - Ok(()) - } - - // handle an acknowledgement of request credits update. - fn acknowledge_update(&self, peer_id: PeerId, _io: &IoContext, _raw: &Rlp) -> Result<(), Error> { - let peers = self.peers.read(); - let peer = peers.get(&peer_id).ok_or(Error::UnknownPeer)?; - let mut peer = peer.lock(); - - trace!(target: "pip", "Received an acknowledgement for new request credit params from peer {}", peer_id); - - let (_, new_params) = match peer.awaiting_acknowledge.take() { - Some(x) => x, - None => return Err(Error::UnsolicitedResponse), - }; - - let old_limit = *peer.local_flow.limit(); - peer.local_credits.maintain_ratio(old_limit, *new_params.limit()); - peer.local_flow = new_params; - Ok(()) - } - - // Receive a set of transactions to relay. - fn relay_transactions(&self, peer: PeerId, io: &IoContext, data: &Rlp) -> Result<(), Error> { - const MAX_TRANSACTIONS: usize = 256; - - let txs: Vec<_> = data.iter() - .take(MAX_TRANSACTIONS) - .map(|x| x.as_val::()) - .collect::>()?; - - debug!(target: "pip", "Received {} transactions to relay from peer {}", txs.len(), peer); - - for handler in &self.handlers { - handler.on_transactions(&Ctx { - peer, - io, - proto: self, - }, &txs); - } - - Ok(()) - } -} - -// if something went wrong, figure out how much to punish the peer. -fn punish(peer: PeerId, io: &IoContext, e: &Error) { - match e.punishment() { - Punishment::None => {} - Punishment::Disconnect => { - debug!(target: "pip", "Disconnecting peer {}: {}", peer, e); - io.disconnect_peer(peer) - } - Punishment::Disable => { - debug!(target: "pip", "Disabling peer {}: {}", peer, e); - io.disable_peer(peer) - } - } -} - -impl NetworkProtocolHandler for LightProtocol { - fn initialize(&self, io: &NetworkContext) { - io.register_timer(TIMEOUT, TIMEOUT_INTERVAL) - .expect("Error registering sync timer."); - io.register_timer(TICK_TIMEOUT, TICK_TIMEOUT_INTERVAL) - .expect("Error registering sync timer."); - io.register_timer(PROPAGATE_TIMEOUT, PROPAGATE_TIMEOUT_INTERVAL) - .expect("Error registering sync timer."); - io.register_timer(RECALCULATE_COSTS_TIMEOUT, RECALCULATE_COSTS_INTERVAL) - .expect("Error registering request timer interval token."); - io.register_timer(STATISTICS_TIMEOUT, STATISTICS_INTERVAL) - .expect("Error registering statistics timer."); - } - - fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) { - self.handle_packet(&io, *peer, packet_id, data); - } - - fn connected(&self, io: &NetworkContext, peer: &PeerId) { - self.on_connect(*peer, &io); - } - - fn disconnected(&self, io: &NetworkContext, peer: &PeerId) { - self.on_disconnect(*peer, &io); - } - - fn timeout(&self, io: &NetworkContext, timer: TimerToken) { - match timer { - TIMEOUT => self.timeout_check(&io), - TICK_TIMEOUT => self.tick_handlers(&io), - PROPAGATE_TIMEOUT => self.propagate_transactions(&io), - RECALCULATE_COSTS_TIMEOUT => self.begin_new_cost_period(&io), - STATISTICS_TIMEOUT => self.tick_statistics(), - _ => warn!(target: "pip", "received timeout on unknown token {}", timer), - } - } -} diff --git a/ethcore/light/src/net/request_credits.rs b/ethcore/light/src/net/request_credits.rs deleted file mode 100644 index c3fc139f4f2..00000000000 --- a/ethcore/light/src/net/request_credits.rs +++ /dev/null @@ -1,455 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Request credit management. -//! -//! Every request in the light protocol leads to a reduction -//! of the requester's amount of credits as a rate-limiting mechanism. -//! The amount of credits will recharge at a set rate. -//! -//! This module provides an interface for configuration of -//! costs and recharge rates of request credits. -//! -//! Current default costs are picked completely arbitrarily, not based -//! on any empirical timings or mathematical models. - -use request::{self, Request}; -use super::error::Error; - -use rlp::{Rlp, RlpStream, Decodable, Encodable, DecoderError}; -use ethereum_types::U256; -use std::time::{Duration, Instant}; - -/// Credits value. -/// -/// Produced and recharged using `FlowParams`. -/// Definitive updates can be made as well -- these will reset the recharge -/// point to the time of the update. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Credits { - estimate: U256, - recharge_point: Instant, -} - -impl Credits { - /// Get the current amount of credits.. - pub fn current(&self) -> U256 { self.estimate } - - /// Make a definitive update. - /// This will be the value obtained after receiving - /// a response to a request. - pub fn update_to(&mut self, value: U256) { - self.estimate = value; - self.recharge_point = Instant::now(); - } - - /// Maintain ratio to current limit against an old limit. - pub fn maintain_ratio(&mut self, old_limit: U256, new_limit: U256) { - self.estimate = (new_limit * self.estimate) / old_limit; - } - - /// Attempt to apply the given cost to the amount of credits. - /// - /// If successful, the cost will be deducted successfully. - /// - /// If unsuccessful, the structure will be unaltered an an - /// error will be produced. - pub fn deduct_cost(&mut self, cost: U256) -> Result<(), Error> { - if cost > self.estimate { - Err(Error::NoCredits) - } else { - self.estimate = self.estimate - cost; - Ok(()) - } - } -} - -/// A cost table, mapping requests to base and per-request costs. -/// Costs themselves may be missing. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CostTable { - base: U256, // cost per packet. - headers: Option, // cost per header - transaction_index: Option, - body: Option, - receipts: Option, - account: Option, - storage: Option, - code: Option, - header_proof: Option, - transaction_proof: Option, // cost per gas. - epoch_signal: Option, -} - -impl CostTable { - fn costs_set(&self) -> usize { - let mut num_set = 0; - - { - let mut incr_if_set = |cost: &Option<_>| if cost.is_some() { num_set += 1 }; - incr_if_set(&self.headers); - incr_if_set(&self.transaction_index); - incr_if_set(&self.body); - incr_if_set(&self.receipts); - incr_if_set(&self.account); - incr_if_set(&self.storage); - incr_if_set(&self.code); - incr_if_set(&self.header_proof); - incr_if_set(&self.transaction_proof); - incr_if_set(&self.epoch_signal); - } - - num_set - } -} - -impl Default for CostTable { - fn default() -> Self { - // arbitrarily chosen constants. - CostTable { - base: 100_000.into(), - headers: Some(10000.into()), - transaction_index: Some(10000.into()), - body: Some(15000.into()), - receipts: Some(5000.into()), - account: Some(25000.into()), - storage: Some(25000.into()), - code: Some(20000.into()), - header_proof: Some(15000.into()), - transaction_proof: Some(2.into()), - epoch_signal: Some(10000.into()), - } - } -} - -impl Encodable for CostTable { - fn rlp_append(&self, s: &mut RlpStream) { - fn append_cost(s: &mut RlpStream, cost: &Option, kind: request::Kind) { - if let Some(ref cost) = *cost { - s.begin_list(2); - // hack around https://github.com/paritytech/parity-ethereum/issues/4356 - Encodable::rlp_append(&kind, s); - s.append(cost); - } - } - - s.begin_list(1 + self.costs_set()).append(&self.base); - append_cost(s, &self.headers, request::Kind::Headers); - append_cost(s, &self.transaction_index, request::Kind::TransactionIndex); - append_cost(s, &self.body, request::Kind::Body); - append_cost(s, &self.receipts, request::Kind::Receipts); - append_cost(s, &self.account, request::Kind::Account); - append_cost(s, &self.storage, request::Kind::Storage); - append_cost(s, &self.code, request::Kind::Code); - append_cost(s, &self.header_proof, request::Kind::HeaderProof); - append_cost(s, &self.transaction_proof, request::Kind::Execution); - append_cost(s, &self.epoch_signal, request::Kind::Signal); - } -} - -impl Decodable for CostTable { - fn decode(rlp: &Rlp) -> Result { - let base = rlp.val_at(0)?; - - let mut headers = None; - let mut transaction_index = None; - let mut body = None; - let mut receipts = None; - let mut account = None; - let mut storage = None; - let mut code = None; - let mut header_proof = None; - let mut transaction_proof = None; - let mut epoch_signal = None; - - for cost_list in rlp.iter().skip(1) { - let cost = cost_list.val_at(1)?; - match cost_list.val_at(0)? { - request::Kind::Headers => headers = Some(cost), - request::Kind::TransactionIndex => transaction_index = Some(cost), - request::Kind::Body => body = Some(cost), - request::Kind::Receipts => receipts = Some(cost), - request::Kind::Account => account = Some(cost), - request::Kind::Storage => storage = Some(cost), - request::Kind::Code => code = Some(cost), - request::Kind::HeaderProof => header_proof = Some(cost), - request::Kind::Execution => transaction_proof = Some(cost), - request::Kind::Signal => epoch_signal = Some(cost), - } - } - - let table = CostTable { - base, - headers, - transaction_index, - body, - receipts, - account, - storage, - code, - header_proof, - transaction_proof, - epoch_signal, - }; - - if table.costs_set() == 0 { - Err(DecoderError::Custom("no cost types set.")) - } else { - Ok(table) - } - } -} - -/// Handles costs, recharge, limits of request credits. -#[derive(Debug, Clone, PartialEq)] -pub struct FlowParams { - costs: CostTable, - limit: U256, - recharge: U256, -} - -impl FlowParams { - /// Create new flow parameters from a request cost table, - /// credit limit, and (minimum) rate of recharge. - pub fn new(limit: U256, costs: CostTable, recharge: U256) -> Self { - FlowParams { - costs, - limit, - recharge, - } - } - - /// Create new flow parameters from , - /// proportion of total capacity which should be given to a peer, - /// and stored capacity a peer can accumulate. - pub fn from_request_times Duration>( - request_time: F, - load_share: f64, - max_stored: Duration - ) -> Self { - use request::Kind; - - let load_share = load_share.abs(); - - let recharge: u64 = 100_000_000; - let max = { - let sec = max_stored.as_secs().saturating_mul(recharge); - let nanos = (max_stored.subsec_nanos() as u64).saturating_mul(recharge) / 1_000_000_000; - sec + nanos - }; - - let cost_for_kind = |kind| { - // how many requests we can handle per second - let rq_dur = request_time(kind); - let second_duration = { - let as_ns = rq_dur.as_secs() as f64 * 1_000_000_000f64 + rq_dur.subsec_nanos() as f64; - 1_000_000_000f64 / as_ns - }; - - // scale by share of the load given to this peer. - let serve_per_second = second_duration * load_share; - let serve_per_second = serve_per_second.max(1.0 / 10_000.0); - - // as a percentage of the recharge per second. - Some(U256::from((recharge as f64 / serve_per_second) as u64)) - }; - - let costs = CostTable { - base: 0.into(), - headers: cost_for_kind(Kind::Headers), - transaction_index: cost_for_kind(Kind::TransactionIndex), - body: cost_for_kind(Kind::Body), - receipts: cost_for_kind(Kind::Receipts), - account: cost_for_kind(Kind::Account), - storage: cost_for_kind(Kind::Storage), - code: cost_for_kind(Kind::Code), - header_proof: cost_for_kind(Kind::HeaderProof), - transaction_proof: cost_for_kind(Kind::Execution), - epoch_signal: cost_for_kind(Kind::Signal), - }; - - FlowParams { - costs, - limit: max.into(), - recharge: recharge.into(), - } - } - - /// Create effectively infinite flow params. - pub fn free() -> Self { - let free_cost: Option = Some(0.into()); - FlowParams { - limit: (!0_u64).into(), - recharge: 1.into(), - costs: CostTable { - base: 0.into(), - headers: free_cost, - transaction_index: free_cost, - body: free_cost, - receipts: free_cost, - account: free_cost, - storage: free_cost, - code: free_cost, - header_proof: free_cost, - transaction_proof: free_cost, - epoch_signal: free_cost, - } - } - } - - /// Get a reference to the credit limit. - pub fn limit(&self) -> &U256 { &self.limit } - - /// Get a reference to the cost table. - pub fn cost_table(&self) -> &CostTable { &self.costs } - - /// Get the base cost of a request. - pub fn base_cost(&self) -> U256 { self.costs.base } - - /// Get a reference to the recharge rate. - pub fn recharge_rate(&self) -> &U256 { &self.recharge } - - /// Compute the actual cost of a request, given the kind of request - /// and number of requests made. - pub fn compute_cost(&self, request: &Request) -> Option { - match *request { - Request::Headers(ref req) => self.costs.headers.map(|c| c * U256::from(req.max)), - Request::HeaderProof(_) => self.costs.header_proof, - Request::TransactionIndex(_) => self.costs.transaction_index, - Request::Body(_) => self.costs.body, - Request::Receipts(_) => self.costs.receipts, - Request::Account(_) => self.costs.account, - Request::Storage(_) => self.costs.storage, - Request::Code(_) => self.costs.code, - Request::Execution(ref req) => self.costs.transaction_proof.map(|c| c * req.gas), - Request::Signal(_) => self.costs.epoch_signal, - } - } - - /// Compute the cost of a set of requests. - /// This is the base cost plus the cost of each individual request. - pub fn compute_cost_multi(&self, requests: &[Request]) -> Option { - let mut cost = self.costs.base; - for request in requests { - match self.compute_cost(request) { - Some(c) => cost = cost + c, - None => return None, - } - } - - Some(cost) - } - - /// Create initial credits. - pub fn create_credits(&self) -> Credits { - Credits { - estimate: self.limit, - recharge_point: Instant::now(), - } - } - - /// Recharge the given credits based on time passed since last - /// update. - pub fn recharge(&self, credits: &mut Credits) { - let now = Instant::now(); - - // recompute and update only in terms of full seconds elapsed - // in order to keep the estimate as an underestimate. - let elapsed = (now - credits.recharge_point).as_secs(); - credits.recharge_point += Duration::from_secs(elapsed); - - let elapsed: U256 = elapsed.into(); - - credits.estimate = ::std::cmp::min(self.limit, credits.estimate + (elapsed * self.recharge)); - } - - /// Refund some credits which were previously deducted. - /// Does not update the recharge timestamp. - pub fn refund(&self, credits: &mut Credits, refund_amount: U256) { - credits.estimate = credits.estimate + refund_amount; - - if credits.estimate > self.limit { - credits.estimate = self.limit - } - } -} - -impl Default for FlowParams { - fn default() -> Self { - FlowParams { - limit: 50_000_000.into(), - costs: CostTable::default(), - recharge: 100_000.into(), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn should_serialize_cost_table() { - let costs = CostTable::default(); - let serialized = ::rlp::encode(&costs); - - let new_costs: CostTable = ::rlp::decode(&*serialized).unwrap(); - - assert_eq!(costs, new_costs); - } - - #[test] - fn credits_mechanism() { - use std::thread; - use std::time::Duration; - - let flow_params = FlowParams::new(100.into(), Default::default(), 20.into()); - let mut credits = flow_params.create_credits(); - - assert!(credits.deduct_cost(101.into()).is_err()); - assert!(credits.deduct_cost(10.into()).is_ok()); - - thread::sleep(Duration::from_secs(1)); - - flow_params.recharge(&mut credits); - - assert_eq!(credits.estimate, 100.into()); - } - - #[test] - fn scale_by_load_share_and_time() { - let flow_params = FlowParams::from_request_times( - |_| Duration::new(0, 10_000), - 0.05, - Duration::from_secs(60), - ); - - let flow_params2 = FlowParams::from_request_times( - |_| Duration::new(0, 10_000), - 0.1, - Duration::from_secs(60), - ); - - let flow_params3 = FlowParams::from_request_times( - |_| Duration::new(0, 5_000), - 0.05, - Duration::from_secs(60), - ); - - assert_eq!(flow_params2.costs, flow_params3.costs); - assert_eq!(flow_params.costs.headers.unwrap(), flow_params2.costs.headers.unwrap() * 2u32); - } -} diff --git a/ethcore/light/src/net/request_set.rs b/ethcore/light/src/net/request_set.rs deleted file mode 100644 index f3ec635472e..00000000000 --- a/ethcore/light/src/net/request_set.rs +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Pending request set. -//! -//! Stores pending requests and does timeout computation according to the rule -//! that only the earliest submitted request within the structure may time out. -//! -//! Whenever a request becomes the earliest, its timeout period begins at that moment. - -use std::collections::{BTreeMap, HashMap}; -use std::iter::FromIterator; -use std::time::{Duration, Instant}; - -use request::Request; -use request::NetworkRequests as Requests; -use net::{timeout, ReqId}; -use ethereum_types::U256; - -// Request set entry: requests + cost. -#[derive(Debug)] -struct Entry(Requests, U256); - -/// Request set. -#[derive(Debug)] -pub struct RequestSet { - counter: u64, - cumulative_cost: U256, - base: Option, - ids: HashMap, - reqs: BTreeMap, -} - -impl Default for RequestSet { - fn default() -> Self { - RequestSet { - counter: 0, - cumulative_cost: 0.into(), - base: None, - ids: HashMap::new(), - reqs: BTreeMap::new(), - } - } -} - -impl RequestSet { - /// Push requests onto the stack. - pub fn insert(&mut self, req_id: ReqId, req: Requests, cost: U256, now: Instant) { - let counter = self.counter; - self.cumulative_cost = self.cumulative_cost + cost; - - self.ids.insert(req_id, counter); - self.reqs.insert(counter, Entry(req, cost)); - - if self.reqs.keys().next().map_or(true, |x| *x == counter) { - self.base = Some(now); - } - - self.counter += 1; - } - - /// Remove a set of requests from the stack. - pub fn remove(&mut self, req_id: ReqId, now: Instant) -> Option { - let id = match self.ids.remove(&req_id) { - Some(id) => id, - None => return None, - }; - - let Entry(req, cost) = self.reqs.remove(&id).expect("entry in `ids` implies entry in `reqs`; qed"); - - match self.reqs.keys().next() { - Some(k) if *k > id => self.base = Some(now), - None => self.base = None, - _ => {} - } - - self.cumulative_cost = self.cumulative_cost - cost; - Some(req) - } - - /// Check for timeout against the given time. Returns true if - /// has timed out, false otherwise. - pub fn check_timeout(&self, now: Instant) -> bool { - let base = match self.base.as_ref().cloned() { - Some(base) => base, - None => return false, - }; - - let first_req = self.reqs.values().next() - .expect("base existing implies `reqs` non-empty; qed"); - - base + compute_timeout(&first_req.0) <= now - } - - /// Collect all pending request ids. - pub fn collect_ids(&self) -> F where F: FromIterator { - self.ids.keys().cloned().collect() - } - - /// Number of requests in the set. - pub fn len(&self) -> usize { - self.ids.len() - } - - /// Whether the set is empty. - pub fn is_empty(&self) -> bool { self.len() == 0 } - - /// The cumulative cost of all requests in the set. - // this may be useful later for load balancing. - #[allow(dead_code)] - pub fn cumulative_cost(&self) -> U256 { self.cumulative_cost } -} - -// helper to calculate timeout for a specific set of requests. -// it's a base amount + some amount per request. -fn compute_timeout(reqs: &Requests) -> Duration { - Duration::from_millis(reqs.requests().iter().fold(timeout::BASE, |tm, req| { - tm + match *req { - Request::Headers(_) => timeout::HEADERS, - Request::HeaderProof(_) => timeout::HEADER_PROOF, - Request::TransactionIndex(_) => timeout::TRANSACTION_INDEX, - Request::Receipts(_) => timeout::RECEIPT, - Request::Body(_) => timeout::BODY, - Request::Account(_) => timeout::PROOF, - Request::Storage(_) => timeout::PROOF, - Request::Code(_) => timeout::CONTRACT_CODE, - Request::Execution(_) => timeout::TRANSACTION_PROOF, - Request::Signal(_) => timeout::EPOCH_SIGNAL, - } - })) -} - -#[cfg(test)] -mod tests { - use net::ReqId; - use request::Builder; - use std::time::{Instant, Duration}; - use super::{RequestSet, compute_timeout}; - - #[test] - fn multi_timeout() { - let test_begin = Instant::now(); - let mut req_set = RequestSet::default(); - - let the_req = Builder::default().build(); - let req_time = compute_timeout(&the_req); - req_set.insert(ReqId(0), the_req.clone(), 0.into(), test_begin); - req_set.insert(ReqId(1), the_req, 0.into(), test_begin + Duration::from_secs(1)); - - assert_eq!(req_set.base, Some(test_begin)); - - let test_end = test_begin + req_time; - assert!(req_set.check_timeout(test_end)); - - req_set.remove(ReqId(0), test_begin + Duration::from_secs(1)).unwrap(); - assert!(!req_set.check_timeout(test_end)); - assert!(req_set.check_timeout(test_end + Duration::from_secs(1))); - } - - #[test] - fn cumulative_cost() { - let the_req = Builder::default().build(); - let test_begin = Instant::now(); - let test_end = test_begin + Duration::from_secs(1); - let mut req_set = RequestSet::default(); - - for i in 0..5 { - req_set.insert(ReqId(i), the_req.clone(), 1.into(), test_begin); - assert_eq!(req_set.cumulative_cost, (i + 1).into()); - } - - for i in (0..5).rev() { - assert!(req_set.remove(ReqId(i), test_end).is_some()); - assert_eq!(req_set.cumulative_cost, i.into()); - } - } -} diff --git a/ethcore/light/src/net/status.rs b/ethcore/light/src/net/status.rs deleted file mode 100644 index ecbe1d3cca2..00000000000 --- a/ethcore/light/src/net/status.rs +++ /dev/null @@ -1,575 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Peer status and capabilities. - -use ethereum_types::{H256, U256}; -use rlp::{DecoderError, Encodable, Decodable, RlpStream, Rlp}; - -use super::request_credits::FlowParams; - -// recognized handshake/announcement keys. -// unknown keys are to be skipped, known keys have a defined order. -// their string values are defined in the LES spec. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)] -enum Key { - ProtocolVersion, - NetworkId, - HeadTD, - HeadHash, - HeadNum, - GenesisHash, - ServeHeaders, - ServeChainSince, - ServeStateSince, - TxRelay, - BufferLimit, - BufferCostTable, - BufferRechargeRate, -} - -impl Key { - // get the string value of this key. - fn as_str(self) -> &'static str { - match self { - Key::ProtocolVersion => "protocolVersion", - Key::NetworkId => "networkId", - Key::HeadTD => "headTd", - Key::HeadHash => "headHash", - Key::HeadNum => "headNum", - Key::GenesisHash => "genesisHash", - Key::ServeHeaders => "serveHeaders", - Key::ServeChainSince => "serveChainSince", - Key::ServeStateSince => "serveStateSince", - Key::TxRelay => "txRelay", - Key::BufferLimit => "flowControl/BL", - Key::BufferCostTable => "flowControl/MRC", - Key::BufferRechargeRate => "flowControl/MRR", - } - } - - // try to parse the key value from a string. - fn from_str(s: &str) -> Option { - match s { - "protocolVersion" => Some(Key::ProtocolVersion), - "networkId" => Some(Key::NetworkId), - "headTd" => Some(Key::HeadTD), - "headHash" => Some(Key::HeadHash), - "headNum" => Some(Key::HeadNum), - "genesisHash" => Some(Key::GenesisHash), - "serveHeaders" => Some(Key::ServeHeaders), - "serveChainSince" => Some(Key::ServeChainSince), - "serveStateSince" => Some(Key::ServeStateSince), - "txRelay" => Some(Key::TxRelay), - "flowControl/BL" => Some(Key::BufferLimit), - "flowControl/MRC" => Some(Key::BufferCostTable), - "flowControl/MRR" => Some(Key::BufferRechargeRate), - _ => None - } - } -} - -// helper for decoding key-value pairs in the handshake or an announcement. -struct Parser<'a> { - pos: usize, - rlp: &'a Rlp<'a>, -} - -impl<'a> Parser<'a> { - // expect a specific next key, and decode the value. - // error on unexpected key or invalid value. - fn expect(&mut self, key: Key) -> Result { - self.expect_raw(key).and_then(|item| item.as_val()) - } - - // expect a specific next key, and get the value's RLP. - // if the key isn't found, the position isn't advanced. - fn expect_raw(&mut self, key: Key) -> Result, DecoderError> { - trace!(target: "les", "Expecting key {}", key.as_str()); - let pre_pos = self.pos; - if let Some((k, val)) = self.get_next()? { - if k == key { return Ok(val) } - } - - self.pos = pre_pos; - Err(DecoderError::Custom("Missing expected key")) - } - - // get the next key and value RLP. - fn get_next(&mut self) -> Result)>, DecoderError> { - while self.pos < self.rlp.item_count()? { - let pair = self.rlp.at(self.pos)?; - let k: String = pair.val_at(0)?; - - self.pos += 1; - match Key::from_str(&k) { - Some(key) => return Ok(Some((key , pair.at(1)?))), - None => continue, - } - } - - Ok(None) - } -} - -// Helper for encoding a key-value pair -fn encode_pair(key: Key, val: &T) -> Vec { - let mut s = RlpStream::new_list(2); - s.append(&key.as_str()).append(val); - s.out() -} - -// Helper for encoding a flag. -fn encode_flag(key: Key) -> Vec { - let mut s = RlpStream::new_list(2); - s.append(&key.as_str()).append_empty_data(); - s.out() -} - -/// A peer status message. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Status { - /// Protocol version. - pub protocol_version: u32, - /// Network id of this peer. - pub network_id: u64, - /// Total difficulty of the head of the chain. - pub head_td: U256, - /// Hash of the best block. - pub head_hash: H256, - /// Number of the best block. - pub head_num: u64, - /// Genesis hash - pub genesis_hash: H256, - /// Last announced chain head and reorg depth to common ancestor. - pub last_head: Option<(H256, u64)>, -} - -impl Status { - /// Update the status from an announcement. - pub fn update_from(&mut self, announcement: &Announcement) { - self.last_head = Some((self.head_hash, announcement.reorg_depth)); - self.head_td = announcement.head_td; - self.head_hash = announcement.head_hash; - self.head_num = announcement.head_num; - } -} - -/// Peer capabilities. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Capabilities { - /// Whether this peer can serve headers - pub serve_headers: bool, - /// Earliest block number it can serve block/receipt requests for. - /// `None` means no requests will be servable. - pub serve_chain_since: Option, - /// Earliest block number it can serve state requests for. - /// `None` means no requests will be servable. - pub serve_state_since: Option, - /// Whether it can relay transactions to the eth network. - pub tx_relay: bool, -} - -impl Default for Capabilities { - fn default() -> Self { - Capabilities { - serve_headers: true, - serve_chain_since: None, - serve_state_since: None, - tx_relay: false, - } - } -} - -impl Capabilities { - /// Update the capabilities from an announcement. - pub fn update_from(&mut self, announcement: &Announcement) { - self.serve_headers = self.serve_headers || announcement.serve_headers; - self.serve_state_since = self.serve_state_since.or(announcement.serve_state_since); - self.serve_chain_since = self.serve_chain_since.or(announcement.serve_chain_since); - self.tx_relay = self.tx_relay || announcement.tx_relay; - } -} - -/// Attempt to parse a handshake message into its three parts: -/// - chain status -/// - serving capabilities -/// - request credit parameters -pub fn parse_handshake(rlp: &Rlp) -> Result<(Status, Capabilities, Option), DecoderError> { - let mut parser = Parser { - pos: 0, - rlp, - }; - - let status = Status { - protocol_version: parser.expect(Key::ProtocolVersion)?, - network_id: parser.expect(Key::NetworkId)?, - head_td: parser.expect(Key::HeadTD)?, - head_hash: parser.expect(Key::HeadHash)?, - head_num: parser.expect(Key::HeadNum)?, - genesis_hash: parser.expect(Key::GenesisHash)?, - last_head: None, - }; - - let capabilities = Capabilities { - serve_headers: parser.expect_raw(Key::ServeHeaders).is_ok(), - serve_chain_since: parser.expect(Key::ServeChainSince).ok(), - serve_state_since: parser.expect(Key::ServeStateSince).ok(), - tx_relay: parser.expect_raw(Key::TxRelay).is_ok(), - }; - - let flow_params = match ( - parser.expect(Key::BufferLimit), - parser.expect(Key::BufferCostTable), - parser.expect(Key::BufferRechargeRate) - ) { - (Ok(bl), Ok(bct), Ok(brr)) => Some(FlowParams::new(bl, bct, brr)), - _ => None, - }; - - Ok((status, capabilities, flow_params)) -} - -/// Write a handshake, given status, capabilities, and flow parameters. -pub fn write_handshake(status: &Status, capabilities: &Capabilities, flow_params: Option<&FlowParams>) -> Vec { - let mut pairs = Vec::new(); - pairs.push(encode_pair(Key::ProtocolVersion, &status.protocol_version)); - pairs.push(encode_pair(Key::NetworkId, &(status.network_id as u64))); - pairs.push(encode_pair(Key::HeadTD, &status.head_td)); - pairs.push(encode_pair(Key::HeadHash, &status.head_hash)); - pairs.push(encode_pair(Key::HeadNum, &status.head_num)); - pairs.push(encode_pair(Key::GenesisHash, &status.genesis_hash)); - - if capabilities.serve_headers { - pairs.push(encode_flag(Key::ServeHeaders)); - } - if let Some(ref serve_chain_since) = capabilities.serve_chain_since { - pairs.push(encode_pair(Key::ServeChainSince, serve_chain_since)); - } - if let Some(ref serve_state_since) = capabilities.serve_state_since { - pairs.push(encode_pair(Key::ServeStateSince, serve_state_since)); - } - if capabilities.tx_relay { - pairs.push(encode_flag(Key::TxRelay)); - } - - if let Some(flow_params) = flow_params { - pairs.push(encode_pair(Key::BufferLimit, flow_params.limit())); - pairs.push(encode_pair(Key::BufferCostTable, flow_params.cost_table())); - pairs.push(encode_pair(Key::BufferRechargeRate, flow_params.recharge_rate())); - } - - let mut stream = RlpStream::new_list(pairs.len()); - - for pair in pairs { - stream.append_raw(&pair, 1); - } - - stream.out() -} - -/// An announcement of new chain head or capabilities made by a peer. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Announcement { - /// Hash of the best block. - pub head_hash: H256, - /// Number of the best block. - pub head_num: u64, - /// Head total difficulty - pub head_td: U256, - /// reorg depth to common ancestor of last announced head. - pub reorg_depth: u64, - /// optional new header-serving capability. false means "no change" - pub serve_headers: bool, - /// optional new state-serving capability - pub serve_state_since: Option, - /// optional new chain-serving capability - pub serve_chain_since: Option, - /// optional new transaction-relay capability. false means "no change" - pub tx_relay: bool, - // TODO: changes in request credits. -} - -/// Parse an announcement. -pub fn parse_announcement(rlp: &Rlp) -> Result { - let mut last_key = None; - - let mut announcement = Announcement { - head_hash: rlp.val_at(0)?, - head_num: rlp.val_at(1)?, - head_td: rlp.val_at(2)?, - reorg_depth: rlp.val_at(3)?, - serve_headers: false, - serve_state_since: None, - serve_chain_since: None, - tx_relay: false, - }; - - let mut parser = Parser { - pos: 4, - rlp, - }; - - while let Some((key, item)) = parser.get_next()? { - if Some(key) <= last_key { return Err(DecoderError::Custom("Invalid announcement key ordering")) } - last_key = Some(key); - - match key { - Key::ServeHeaders => announcement.serve_headers = true, - Key::ServeStateSince => announcement.serve_state_since = Some(item.as_val()?), - Key::ServeChainSince => announcement.serve_chain_since = Some(item.as_val()?), - Key::TxRelay => announcement.tx_relay = true, - _ => return Err(DecoderError::Custom("Nonsensical key in announcement")), - } - } - - Ok(announcement) -} - -/// Write an announcement out. -pub fn write_announcement(announcement: &Announcement) -> Vec { - let mut pairs = Vec::new(); - if announcement.serve_headers { - pairs.push(encode_flag(Key::ServeHeaders)); - } - if let Some(ref serve_chain_since) = announcement.serve_chain_since { - pairs.push(encode_pair(Key::ServeChainSince, serve_chain_since)); - } - if let Some(ref serve_state_since) = announcement.serve_state_since { - pairs.push(encode_pair(Key::ServeStateSince, serve_state_since)); - } - if announcement.tx_relay { - pairs.push(encode_flag(Key::TxRelay)); - } - - let mut stream = RlpStream::new_list(4 + pairs.len()); - stream - .append(&announcement.head_hash) - .append(&announcement.head_num) - .append(&announcement.head_td) - .append(&announcement.reorg_depth); - - for item in pairs { - stream.append_raw(&item, 1); - } - - stream.out() -} - -#[cfg(test)] -mod tests { - use super::*; - use super::super::request_credits::FlowParams; - use ethereum_types::{U256, H256}; - use rlp::{RlpStream, Rlp}; - - #[test] - fn full_handshake() { - let status = Status { - protocol_version: 1, - network_id: 1, - head_td: U256::default(), - head_hash: H256::default(), - head_num: 10, - genesis_hash: H256::zero(), - last_head: None, - }; - - let capabilities = Capabilities { - serve_headers: true, - serve_chain_since: Some(5), - serve_state_since: Some(8), - tx_relay: true, - }; - - let flow_params = FlowParams::new( - 1_000_000.into(), - Default::default(), - 1000.into(), - ); - - let handshake = write_handshake(&status, &capabilities, Some(&flow_params)); - - let (read_status, read_capabilities, read_flow) - = parse_handshake(&Rlp::new(&handshake)).unwrap(); - - assert_eq!(read_status, status); - assert_eq!(read_capabilities, capabilities); - assert_eq!(read_flow.unwrap(), flow_params); - } - - #[test] - fn partial_handshake() { - let status = Status { - protocol_version: 1, - network_id: 1, - head_td: U256::default(), - head_hash: H256::default(), - head_num: 10, - genesis_hash: H256::zero(), - last_head: None, - }; - - let capabilities = Capabilities { - serve_headers: false, - serve_chain_since: Some(5), - serve_state_since: None, - tx_relay: true, - }; - - let flow_params = FlowParams::new( - 1_000_000.into(), - Default::default(), - 1000.into(), - ); - - let handshake = write_handshake(&status, &capabilities, Some(&flow_params)); - - let (read_status, read_capabilities, read_flow) - = parse_handshake(&Rlp::new(&handshake)).unwrap(); - - assert_eq!(read_status, status); - assert_eq!(read_capabilities, capabilities); - assert_eq!(read_flow.unwrap(), flow_params); - } - - #[test] - fn skip_unknown_keys() { - let status = Status { - protocol_version: 1, - network_id: 1, - head_td: U256::default(), - head_hash: H256::default(), - head_num: 10, - genesis_hash: H256::zero(), - last_head: None, - }; - - let capabilities = Capabilities { - serve_headers: false, - serve_chain_since: Some(5), - serve_state_since: None, - tx_relay: true, - }; - - let flow_params = FlowParams::new( - 1_000_000.into(), - Default::default(), - 1000.into(), - ); - - let handshake = write_handshake(&status, &capabilities, Some(&flow_params)); - let interleaved = { - let handshake = Rlp::new(&handshake); - let mut stream = RlpStream::new_list(handshake.item_count().unwrap_or(0) * 3); - - for item in handshake.iter() { - stream.append_raw(item.as_raw(), 1); - let (mut s1, mut s2) = (RlpStream::new_list(2), RlpStream::new_list(2)); - s1.append(&"foo").append_empty_data(); - s2.append(&"bar").append_empty_data(); - stream.append_raw(&s1.out(), 1); - stream.append_raw(&s2.out(), 1); - } - - stream.out() - }; - - let (read_status, read_capabilities, read_flow) - = parse_handshake(&Rlp::new(&interleaved)).unwrap(); - - assert_eq!(read_status, status); - assert_eq!(read_capabilities, capabilities); - assert_eq!(read_flow.unwrap(), flow_params); - } - - #[test] - fn announcement_roundtrip() { - let announcement = Announcement { - head_hash: H256::random(), - head_num: 100_000, - head_td: 1_000_000.into(), - reorg_depth: 4, - serve_headers: false, - serve_state_since: Some(99_000), - serve_chain_since: Some(1), - tx_relay: true, - }; - - let serialized = write_announcement(&announcement); - let read = parse_announcement(&Rlp::new(&serialized)).unwrap(); - - assert_eq!(read, announcement); - } - - #[test] - fn keys_out_of_order() { - use super::{Key, encode_pair, encode_flag}; - - let mut stream = RlpStream::new_list(6); - stream - .append(&H256::zero()) - .append(&10_u64) - .append(&100_000_u64) - .append(&2_u64) - .append_raw(&encode_pair(Key::ServeStateSince, &44_u64), 1) - .append_raw(&encode_flag(Key::ServeHeaders), 1); - - let out = stream.drain(); - assert!(parse_announcement(&Rlp::new(&out)).is_err()); - - let mut stream = RlpStream::new_list(6); - stream - .append(&H256::zero()) - .append(&10_u64) - .append(&100_000_u64) - .append(&2_u64) - .append_raw(&encode_flag(Key::ServeHeaders), 1) - .append_raw(&encode_pair(Key::ServeStateSince, &44_u64), 1); - - let out = stream.drain(); - assert!(parse_announcement(&Rlp::new(&out)).is_ok()); - } - - #[test] - fn optional_flow() { - let status = Status { - protocol_version: 1, - network_id: 1, - head_td: U256::default(), - head_hash: H256::default(), - head_num: 10, - genesis_hash: H256::zero(), - last_head: None, - }; - - let capabilities = Capabilities { - serve_headers: true, - serve_chain_since: Some(5), - serve_state_since: Some(8), - tx_relay: true, - }; - - let handshake = write_handshake(&status, &capabilities, None); - - let (read_status, read_capabilities, read_flow) - = parse_handshake(&Rlp::new(&handshake)).unwrap(); - - assert_eq!(read_status, status); - assert_eq!(read_capabilities, capabilities); - assert!(read_flow.is_none()); - } -} diff --git a/ethcore/light/src/net/tests/mod.rs b/ethcore/light/src/net/tests/mod.rs deleted file mode 100644 index 2ca7477f2b7..00000000000 --- a/ethcore/light/src/net/tests/mod.rs +++ /dev/null @@ -1,814 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Tests for the `LightProtocol` implementation. -//! These don't test of the higher level logic on top of - -use common_types::blockchain_info::BlockChainInfo; -use common_types::encoded; -use common_types::ids::BlockId; -use common_types::transaction::{Action, PendingTransaction}; -use ethcore::client::{EachBlockWith, TestBlockChainClient}; -use ethereum_types::{H256, U256, Address}; -use net::context::IoContext; -use net::load_timer::MOVING_SAMPLE_SIZE; -use net::status::{Capabilities, Status}; -use net::{LightProtocol, Params, packet, Peer, Statistics}; -use network::{PeerId, NodeId}; -use provider::Provider; -use request::*; -use request; -use rlp::{Rlp, RlpStream}; - -use std::sync::Arc; -use std::time::Instant; - -// helper for encoding a single request into a packet. -// panics on bad backreference. -fn encode_single(request: Request) -> NetworkRequests { - let mut builder = Builder::default(); - builder.push(request).unwrap(); - builder.build() -} - -// helper for making a packet out of `Requests`. -fn make_packet(req_id: usize, requests: &NetworkRequests) -> Vec { - let mut stream = RlpStream::new_list(2); - stream.append(&req_id).append_list(&requests.requests()); - stream.out() -} - -// expected result from a call. -#[derive(Debug, PartialEq, Eq)] -enum Expect { - /// Expect to have message sent to peer. - Send(PeerId, u8, Vec), - /// Expect this response. - Respond(u8, Vec), - /// Expect a punishment (disconnect/disable) - Punish(PeerId), - /// Expect nothing. - Nothing, -} - -impl IoContext for Expect { - fn send(&self, peer: PeerId, packet_id: u8, packet_body: Vec) { - assert_eq!(self, &Expect::Send(peer, packet_id, packet_body)); - } - - fn respond(&self, packet_id: u8, packet_body: Vec) { - assert_eq!(self, &Expect::Respond(packet_id, packet_body)); - } - - fn disconnect_peer(&self, peer: PeerId) { - assert_eq!(self, &Expect::Punish(peer)); - } - - fn disable_peer(&self, peer: PeerId) { - assert_eq!(self, &Expect::Punish(peer)); - } - - fn protocol_version(&self, _peer: PeerId) -> Option { - Some(super::MAX_PROTOCOL_VERSION) - } - - fn persistent_peer_id(&self, _peer: PeerId) -> Option { - None - } - - fn is_reserved_peer(&self, peer: PeerId) -> bool { - peer == 0xff - } -} - -// can't implement directly for Arc due to cross-crate orphan rules. -struct TestProvider(Arc); - -struct TestProviderInner { - client: TestBlockChainClient, -} - -impl Provider for TestProvider { - fn chain_info(&self) -> BlockChainInfo { - self.0.client.chain_info() - } - - fn reorg_depth(&self, a: &H256, b: &H256) -> Option { - self.0.client.reorg_depth(a, b) - } - - fn earliest_state(&self) -> Option { - None - } - - fn block_header(&self, id: BlockId) -> Option { - self.0.client.block_header(id) - } - - fn transaction_index(&self, req: request::CompleteTransactionIndexRequest) - -> Option - { - Some(request::TransactionIndexResponse { - num: 100, - hash: req.hash, - index: 55, - }) - } - - fn block_body(&self, req: request::CompleteBodyRequest) -> Option { - self.0.client.block_body(req) - } - - fn block_receipts(&self, req: request::CompleteReceiptsRequest) -> Option { - self.0.client.block_receipts(req) - } - - fn account_proof(&self, req: request::CompleteAccountRequest) -> Option { - // sort of a leaf node - let mut stream = RlpStream::new_list(2); - stream.append(&req.address_hash).append_empty_data(); - Some(AccountResponse { - proof: vec![stream.out()], - balance: 10.into(), - nonce: 100.into(), - code_hash: Default::default(), - storage_root: Default::default(), - }) - } - - fn storage_proof(&self, req: request::CompleteStorageRequest) -> Option { - Some(StorageResponse { - proof: vec![::rlp::encode(&req.key_hash)], - value: req.key_hash | req.address_hash, - }) - } - - fn contract_code(&self, req: request::CompleteCodeRequest) -> Option { - Some(CodeResponse { - code: req.block_hash.iter().chain(req.code_hash.iter()).cloned().collect(), - }) - } - - fn header_proof(&self, _req: request::CompleteHeaderProofRequest) -> Option { - None - } - - fn transaction_proof(&self, _req: request::CompleteExecutionRequest) -> Option { - None - } - - fn epoch_signal(&self, _req: request::CompleteSignalRequest) -> Option { - Some(request::SignalResponse { - signal: vec![1, 2, 3, 4], - }) - } - - fn transactions_to_propagate(&self) -> Vec { - self.0.client.transactions_to_propagate() - } -} - -fn capabilities() -> Capabilities { - Capabilities { - serve_headers: true, - serve_chain_since: Some(1), - serve_state_since: Some(1), - tx_relay: true, - } -} - -fn write_handshake(status: &Status, capabilities: &Capabilities, proto: &LightProtocol) -> Vec { - let flow_params = proto.flow_params.read().clone(); - ::net::status::write_handshake(status, capabilities, Some(&*flow_params)) -} - -fn write_free_handshake(status: &Status, capabilities: &Capabilities, proto: &LightProtocol) -> Vec { - ::net::status::write_handshake(status, capabilities, Some(&proto.free_flow_params)) -} - -// helper for setting up the protocol handler and provider. -fn setup(capabilities: Capabilities) -> (Arc, LightProtocol) { - let provider = Arc::new(TestProviderInner { - client: TestBlockChainClient::new(), - }); - - let proto = LightProtocol::new(Arc::new(TestProvider(provider.clone())), Params { - network_id: 2, - config: Default::default(), - capabilities: capabilities, - sample_store: None, - }); - - (provider, proto) -} - -fn status(chain_info: BlockChainInfo) -> Status { - Status { - protocol_version: 1, - network_id: 2, - head_td: chain_info.total_difficulty, - head_hash: chain_info.best_block_hash, - head_num: chain_info.best_block_number, - genesis_hash: chain_info.genesis_hash, - last_head: None, - } -} - -#[test] -fn handshake_expected() { - let capabilities = capabilities(); - - let (provider, proto) = setup(capabilities); - - let status = status(provider.client.chain_info()); - - let packet_body = write_handshake(&status, &capabilities, &proto); - - proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body)); -} - -#[test] -fn reserved_handshake_expected() { - let capabilities = capabilities(); - - let (provider, proto) = setup(capabilities); - - let status = status(provider.client.chain_info()); - - let packet_body = write_free_handshake(&status, &capabilities, &proto); - - proto.on_connect(0xff, &Expect::Send(0xff, packet::STATUS, packet_body)); -} - -#[test] -#[should_panic] -fn genesis_mismatch() { - let capabilities = capabilities(); - - let (provider, proto) = setup(capabilities); - - let mut status = status(provider.client.chain_info()); - status.genesis_hash = H256::default(); - - let packet_body = write_handshake(&status, &capabilities, &proto); - - proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body)); -} - -#[test] -fn credit_overflow() { - let capabilities = capabilities(); - - let (provider, proto) = setup(capabilities); - - let status = status(provider.client.chain_info()); - - { - let packet_body = write_handshake(&status, &capabilities, &proto); - proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body)); - } - - { - let my_status = write_handshake(&status, &capabilities, &proto); - proto.handle_packet(&Expect::Nothing, 1, packet::STATUS, &my_status); - } - - // 1 billion requests is far too many for the default flow params. - let requests = encode_single(Request::Headers(IncompleteHeadersRequest { - start: HashOrNumber::Number(1).into(), - max: 1_000_000_000, - skip: 0, - reverse: false, - })); - let request = make_packet(111, &requests); - - proto.handle_packet(&Expect::Punish(1), 1, packet::REQUEST, &request); -} - -// test the basic request types -- these just make sure that requests are parsed -// and sent to the provider correctly as well as testing response formatting. - -#[test] -fn get_block_headers() { - let capabilities = capabilities(); - - let (provider, proto) = setup(capabilities); - let flow_params = proto.flow_params.read().clone(); - - let cur_status = status(provider.client.chain_info()); - let my_status = write_handshake(&cur_status, &capabilities, &proto); - - provider.client.add_blocks(100, EachBlockWith::Nothing); - - let cur_status = status(provider.client.chain_info()); - - { - let packet_body = write_handshake(&cur_status, &capabilities, &proto); - proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body)); - proto.handle_packet(&Expect::Nothing, 1, packet::STATUS, &my_status); - } - - let request = Request::Headers(IncompleteHeadersRequest { - start: HashOrNumber::Number(1).into(), - max: 10, - skip: 0, - reverse: false, - }); - - let req_id = 111; - - let requests = encode_single(request.clone()); - let request_body = make_packet(req_id, &requests); - - let response = { - let headers: Vec<_> = (0..10).map(|i| provider.client.block_header(BlockId::Number(i + 1)).unwrap()).collect(); - assert_eq!(headers.len(), 10); - - let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()).unwrap(); - - let response = vec![Response::Headers(HeadersResponse { headers })]; - - let mut stream = RlpStream::new_list(3); - stream.append(&req_id).append(&new_creds).append_list(&response); - - stream.out() - }; - - let expected = Expect::Respond(packet::RESPONSE, response); - proto.handle_packet(&expected, 1, packet::REQUEST, &request_body); -} - -#[test] -fn get_block_bodies() { - let capabilities = capabilities(); - - let (provider, proto) = setup(capabilities); - let flow_params = proto.flow_params.read().clone(); - - let cur_status = status(provider.client.chain_info()); - let my_status = write_handshake(&cur_status, &capabilities, &proto); - - provider.client.add_blocks(100, EachBlockWith::Nothing); - - let cur_status = status(provider.client.chain_info()); - - { - let packet_body = write_handshake(&cur_status, &capabilities, &proto); - proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body)); - proto.handle_packet(&Expect::Nothing, 1, packet::STATUS, &my_status); - } - - let mut builder = Builder::default(); - let mut bodies = Vec::new(); - - for i in 0..10 { - let hash = provider.client.block_header(BlockId::Number(i)).unwrap().hash(); - builder.push(Request::Body(IncompleteBodyRequest { - hash: hash.into(), - })).unwrap(); - bodies.push(Response::Body(provider.client.block_body(CompleteBodyRequest { - hash: hash, - }).unwrap())); - } - let req_id = 111; - let requests = builder.build(); - let request_body = make_packet(req_id, &requests); - - let response = { - let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()).unwrap(); - - let mut response_stream = RlpStream::new_list(3); - response_stream.append(&req_id).append(&new_creds).append_list(&bodies); - response_stream.out() - }; - - let expected = Expect::Respond(packet::RESPONSE, response); - proto.handle_packet(&expected, 1, packet::REQUEST, &request_body); -} - -#[test] -fn get_block_receipts() { - let capabilities = capabilities(); - - let (provider, proto) = setup(capabilities); - let flow_params = proto.flow_params.read().clone(); - - let cur_status = status(provider.client.chain_info()); - let my_status = write_handshake(&cur_status, &capabilities, &proto); - - provider.client.add_blocks(1000, EachBlockWith::Nothing); - - let cur_status = status(provider.client.chain_info()); - - { - let packet_body = write_handshake(&cur_status, &capabilities, &proto); - proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body)); - proto.handle_packet(&Expect::Nothing, 1, packet::STATUS, &my_status); - } - - // find the first 10 block hashes starting with `f` because receipts are only provided - // by the test client in that case. - let block_hashes: Vec = (0..1000) - .map(|i| provider.client.block_header(BlockId::Number(i)).unwrap().hash()) - .filter(|hash| format!("{}", hash).starts_with("0xf")) - .take(10) - .collect(); - - let mut builder = Builder::default(); - let mut receipts = Vec::new(); - for hash in block_hashes.iter().cloned() { - builder.push(Request::Receipts(IncompleteReceiptsRequest { hash: hash.into() })).unwrap(); - receipts.push(Response::Receipts(provider.client.block_receipts(CompleteReceiptsRequest { - hash: hash - }).unwrap())); - } - - let req_id = 111; - let requests = builder.build(); - let request_body = make_packet(req_id, &requests); - - let response = { - assert_eq!(receipts.len(), 10); - - let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()).unwrap(); - - let mut response_stream = RlpStream::new_list(3); - response_stream.append(&req_id).append(&new_creds).append_list(&receipts); - response_stream.out() - }; - - let expected = Expect::Respond(packet::RESPONSE, response); - proto.handle_packet(&expected, 1, packet::REQUEST, &request_body); -} - -#[test] -fn get_state_proofs() { - let capabilities = capabilities(); - - let (provider, proto) = setup(capabilities); - let flow_params = proto.flow_params.read().clone(); - - let provider = TestProvider(provider); - - let cur_status = status(provider.0.client.chain_info()); - - { - let packet_body = write_handshake(&cur_status, &capabilities, &proto); - proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body.clone())); - proto.handle_packet(&Expect::Nothing, 1, packet::STATUS, &packet_body); - } - - let req_id = 112; - let key1: H256 = U256::from(11223344).into(); - let key2: H256 = U256::from(99988887).into(); - - let mut builder = Builder::default(); - builder.push(Request::Account(IncompleteAccountRequest { - block_hash: H256::default().into(), - address_hash: key1.into(), - })).unwrap(); - builder.push(Request::Storage(IncompleteStorageRequest { - block_hash: H256::default().into(), - address_hash: key1.into(), - key_hash: key2.into(), - })).unwrap(); - - let requests = builder.build(); - - let request_body = make_packet(req_id, &requests); - let response = { - let responses = vec![ - Response::Account(provider.account_proof(CompleteAccountRequest { - block_hash: H256::default(), - address_hash: key1, - }).unwrap()), - Response::Storage(provider.storage_proof(CompleteStorageRequest { - block_hash: H256::default(), - address_hash: key1, - key_hash: key2, - }).unwrap()), - ]; - - let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()).unwrap(); - - let mut response_stream = RlpStream::new_list(3); - response_stream.append(&req_id).append(&new_creds).append_list(&responses); - response_stream.out() - }; - - let expected = Expect::Respond(packet::RESPONSE, response); - proto.handle_packet(&expected, 1, packet::REQUEST, &request_body); -} - -#[test] -fn get_contract_code() { - let capabilities = capabilities(); - - let (provider, proto) = setup(capabilities); - let flow_params = proto.flow_params.read().clone(); - - let cur_status = status(provider.client.chain_info()); - - { - let packet_body = write_handshake(&cur_status, &capabilities, &proto); - proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body.clone())); - proto.handle_packet(&Expect::Nothing, 1, packet::STATUS, &packet_body); - } - - let req_id = 112; - let key1: H256 = U256::from(11223344).into(); - let key2: H256 = U256::from(99988887).into(); - - let request = Request::Code(IncompleteCodeRequest { - block_hash: key1.into(), - code_hash: key2.into(), - }); - - let requests = encode_single(request.clone()); - let request_body = make_packet(req_id, &requests); - let response = { - let response = vec![Response::Code(CodeResponse { - code: key1.iter().chain(key2.iter()).cloned().collect(), - })]; - - let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()).unwrap(); - - let mut response_stream = RlpStream::new_list(3); - - response_stream.append(&req_id).append(&new_creds).append_list(&response); - response_stream.out() - }; - - let expected = Expect::Respond(packet::RESPONSE, response); - proto.handle_packet(&expected, 1, packet::REQUEST, &request_body); -} - -#[test] -fn epoch_signal() { - let capabilities = capabilities(); - - let (provider, proto) = setup(capabilities); - let flow_params = proto.flow_params.read().clone(); - - let cur_status = status(provider.client.chain_info()); - - { - let packet_body = write_handshake(&cur_status, &capabilities, &proto); - proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body.clone())); - proto.handle_packet(&Expect::Nothing, 1, packet::STATUS, &packet_body); - } - - let req_id = 112; - let request = Request::Signal(request::IncompleteSignalRequest { - block_hash: H256([1; 32]).into(), - }); - - let requests = encode_single(request.clone()); - let request_body = make_packet(req_id, &requests); - - let response = { - let response = vec![Response::Signal(SignalResponse { - signal: vec![1, 2, 3, 4], - })]; - - let limit = *flow_params.limit(); - let cost = flow_params.compute_cost_multi(requests.requests()).unwrap(); - - let new_creds = limit - cost; - - let mut response_stream = RlpStream::new_list(3); - response_stream.append(&req_id).append(&new_creds).append_list(&response); - - response_stream.out() - }; - - let expected = Expect::Respond(packet::RESPONSE, response); - proto.handle_packet(&expected, 1, packet::REQUEST, &request_body); -} - -#[test] -fn proof_of_execution() { - let capabilities = capabilities(); - - let (provider, proto) = setup(capabilities); - let flow_params = proto.flow_params.read().clone(); - - let cur_status = status(provider.client.chain_info()); - - { - let packet_body = write_handshake(&cur_status, &capabilities, &proto); - proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body.clone())); - proto.handle_packet(&Expect::Nothing, 1, packet::STATUS, &packet_body); - } - - let req_id = 112; - let mut request = Request::Execution(request::IncompleteExecutionRequest { - block_hash: H256::default().into(), - from: Address::default(), - action: Action::Call(Address::default()), - gas: 100.into(), - gas_price: 0.into(), - value: 0.into(), - data: Vec::new(), - }); - - // first: a valid amount to request execution of. - let requests = encode_single(request.clone()); - let request_body = make_packet(req_id, &requests); - - let response = { - let limit = *flow_params.limit(); - let cost = flow_params.compute_cost_multi(requests.requests()).unwrap(); - - let new_creds = limit - cost; - - let mut response_stream = RlpStream::new_list(3); - response_stream.append(&req_id).append(&new_creds).begin_list(0); - - response_stream.out() - }; - - let expected = Expect::Respond(packet::RESPONSE, response); - proto.handle_packet(&expected, 1, packet::REQUEST, &request_body); - - // next: way too much requested gas. - if let Request::Execution(ref mut req) = request { - req.gas = 100_000_000.into(); - } - let req_id = 113; - let requests = encode_single(request.clone()); - let request_body = make_packet(req_id, &requests); - - let expected = Expect::Punish(1); - proto.handle_packet(&expected, 1, packet::REQUEST, &request_body); -} - -#[test] -fn id_guard() { - use super::request_set::RequestSet; - use super::ReqId; - - let capabilities = capabilities(); - - let (provider, proto) = setup(capabilities); - let flow_params = proto.flow_params.read().clone(); - - let req_id_1 = ReqId(5143); - let req_id_2 = ReqId(1111); - - let req = encode_single(Request::Headers(IncompleteHeadersRequest { - start: HashOrNumber::Number(5u64).into(), - max: 100, - skip: 0, - reverse: false, - })); - - let peer_id = 9876; - - let mut pending_requests = RequestSet::default(); - - pending_requests.insert(req_id_1, req.clone(), 0.into(), Instant::now()); - pending_requests.insert(req_id_2, req, 1.into(), Instant::now()); - - proto.peers.write().insert(peer_id, ::parking_lot::Mutex::new(Peer { - local_credits: flow_params.create_credits(), - status: status(provider.client.chain_info()), - capabilities, - remote_flow: Some((flow_params.create_credits(), (&*flow_params).clone())), - sent_head: provider.client.chain_info().best_block_hash, - last_update: Instant::now(), - pending_requests: pending_requests, - failed_requests: Vec::new(), - propagated_transactions: Default::default(), - skip_update: false, - local_flow: flow_params, - awaiting_acknowledge: None, - })); - - // first, malformed responses. - { - let mut stream = RlpStream::new_list(3); - stream.append(&req_id_1.0); - stream.append(&4_000_000_usize); - stream.begin_list(2).append(&125_usize).append(&3_usize); - - let packet = stream.out(); - assert!(proto.response(peer_id, &Expect::Nothing, &Rlp::new(&packet)).is_err()); - } - - // next, do an unexpected response. - { - let mut stream = RlpStream::new_list(3); - stream.append(&10000_usize); - stream.append(&3_000_000_usize); - stream.begin_list(0); - - let packet = stream.out(); - assert!(proto.response(peer_id, &Expect::Nothing, &Rlp::new(&packet)).is_err()); - } - - // lastly, do a valid (but empty) response. - { - let mut stream = RlpStream::new_list(3); - stream.append(&req_id_2.0); - stream.append(&3_000_000_usize); - stream.begin_list(0); - - let packet = stream.out(); - assert!(proto.response(peer_id, &Expect::Nothing, &Rlp::new(&packet)).is_ok()); - } - - let peers = proto.peers.read(); - if let Some(ref peer_info) = peers.get(&peer_id) { - let peer_info = peer_info.lock(); - assert!(peer_info.pending_requests.collect_ids::>().is_empty()); - assert_eq!(peer_info.failed_requests, &[req_id_1]); - } -} - -#[test] -fn get_transaction_index() { - let capabilities = capabilities(); - - let (provider, proto) = setup(capabilities); - let flow_params = proto.flow_params.read().clone(); - - let cur_status = status(provider.client.chain_info()); - - { - let packet_body = write_handshake(&cur_status, &capabilities, &proto); - proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body.clone())); - proto.handle_packet(&Expect::Nothing, 1, packet::STATUS, &packet_body); - } - - let req_id = 112; - let key1: H256 = U256::from(11223344).into(); - - let request = Request::TransactionIndex(IncompleteTransactionIndexRequest { - hash: key1.into(), - }); - - let requests = encode_single(request.clone()); - let request_body = make_packet(req_id, &requests); - let response = { - let response = vec![Response::TransactionIndex(TransactionIndexResponse { - num: 100, - hash: key1, - index: 55, - })]; - - let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()).unwrap(); - - let mut response_stream = RlpStream::new_list(3); - - response_stream.append(&req_id).append(&new_creds).append_list(&response); - response_stream.out() - }; - - let expected = Expect::Respond(packet::RESPONSE, response); - proto.handle_packet(&expected, 1, packet::REQUEST, &request_body); -} - -#[test] -fn sync_statistics() { - let mut stats = Statistics::new(); - - // Empty set should return 1.0 - assert_eq!(stats.avg_peer_count(), 1.0); - - // Average < 1.0 should return 1.0 - stats.add_peer_count(0); - assert_eq!(stats.avg_peer_count(), 1.0); - - stats = Statistics::new(); - - const N: f64 = 50.0; - - for i in 1..(N as usize + 1) { - stats.add_peer_count(i); - } - - // Compute the average for the sum 1..N - assert_eq!(stats.avg_peer_count(), N * (N + 1.0) / 2.0 / N); - - for _ in 1..(MOVING_SAMPLE_SIZE + 1) { - stats.add_peer_count(40); - } - - // Test that it returns the average of the last - // `MOVING_SAMPLE_SIZE` values - assert_eq!(stats.avg_peer_count(), 40.0); -} diff --git a/ethcore/light/src/on_demand/mod.rs b/ethcore/light/src/on_demand/mod.rs deleted file mode 100644 index 7d1f4fabf8e..00000000000 --- a/ethcore/light/src/on_demand/mod.rs +++ /dev/null @@ -1,669 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! On-demand chain requests over LES. This is a major building block for RPCs. -//! The request service is implemented using Futures. Higher level request handlers -//! will take the raw data received here and extract meaningful results from it. - -use std::cmp; -use std::collections::HashMap; -use std::marker::PhantomData; -use std::sync::Arc; -use std::time::Duration; - -use futures::{Poll, Future, Async}; -use futures::sync::oneshot::{self, Receiver}; -use network::PeerId; -use parking_lot::{RwLock, Mutex}; -use rand; -use rand::Rng; - -use net::{ - Handler, PeerStatus, Status, Capabilities, - Announcement, EventContext, BasicContext, ReqId, -}; - -use cache::Cache; -use request::{self as basic_request, Request as NetworkRequest}; -use self::request::CheckedRequest; - -pub use ethcore::executed::ExecutionResult; -pub use self::request::{Request, Response, HeaderRef, Error as ValidityError}; -pub use self::request_guard::{RequestGuard, Error as RequestError}; -pub use self::response_guard::{ResponseGuard, Error as ResponseGuardError, Inner as ResponseGuardInner}; -pub use types::request::ResponseError; - -#[cfg(test)] -mod tests; - -pub mod request; -mod request_guard; -mod response_guard; - -/// The initial backoff interval for OnDemand queries -pub const DEFAULT_REQUEST_MIN_BACKOFF_DURATION: Duration = Duration::from_secs(10); -/// The maximum request interval for OnDemand queries -pub const DEFAULT_REQUEST_MAX_BACKOFF_DURATION: Duration = Duration::from_secs(100); -/// The default window length a response is evaluated -pub const DEFAULT_RESPONSE_TIME_TO_LIVE: Duration = Duration::from_secs(10); -/// The default number of maximum backoff iterations -pub const DEFAULT_MAX_REQUEST_BACKOFF_ROUNDS: usize = 10; -/// The default number failed request to be regarded as failure -pub const DEFAULT_NUM_CONSECUTIVE_FAILED_REQUESTS: usize = 1; - -/// OnDemand related errors -pub mod error { - // Silence: `use of deprecated item 'std::error::Error::cause': replaced by Error::source, which can support downcasting` - // https://github.com/paritytech/parity-ethereum/issues/10302 - #![allow(deprecated)] - - use futures::sync::oneshot::Canceled; - - error_chain! { - - foreign_links { - ChannelCanceled(Canceled) #[doc = "Canceled oneshot channel"]; - } - - errors { - #[doc = "Timeout bad response"] - BadResponse(err: String) { - description("Max response evaluation time exceeded") - display("{}", err) - } - - #[doc = "OnDemand requests limit exceeded"] - RequestLimit { - description("OnDemand request maximum backoff iterations exceeded") - display("OnDemand request maximum backoff iterations exceeded") - } - } - } -} - -/// Public interface for performing network requests `OnDemand` -pub trait OnDemandRequester: Send + Sync { - /// Submit a strongly-typed batch of requests. - /// - /// Fails if back-reference are not coherent. - fn request(&self, ctx: &BasicContext, requests: T) -> Result, basic_request::NoSuchOutput> - where - T: request::RequestAdapter; - - /// Submit a vector of requests to be processed together. - /// - /// Fails if back-references are not coherent. - /// The returned vector of responses will correspond to the requests exactly. - fn request_raw(&self, ctx: &BasicContext, requests: Vec) - -> Result, basic_request::NoSuchOutput>; -} - - -// relevant peer info. -#[derive(Debug, Clone, PartialEq, Eq)] -struct Peer { - status: Status, - capabilities: Capabilities, -} - -impl Peer { - // whether this peer can fulfill the necessary capabilities for the given - // request. - fn can_fulfill(&self, request: &Capabilities) -> bool { - let local_caps = &self.capabilities; - let can_serve_since = |req, local| { - match (req, local) { - (Some(request_block), Some(serve_since)) => request_block >= serve_since, - (Some(_), None) => false, - (None, _) => true, - } - }; - - local_caps.serve_headers >= request.serve_headers && - can_serve_since(request.serve_chain_since, local_caps.serve_chain_since) && - can_serve_since(request.serve_state_since, local_caps.serve_state_since) - } -} - -/// Either an array of responses or a single error. -type PendingResponse = self::error::Result>; - -// Attempted request info and sender to put received value. -struct Pending { - requests: basic_request::Batch, - net_requests: basic_request::Batch, - required_capabilities: Capabilities, - responses: Vec, - sender: oneshot::Sender, - request_guard: RequestGuard, - response_guard: ResponseGuard, -} - -impl Pending { - // answer as many of the given requests from the supplied cache as possible. - // TODO: support re-shuffling. - fn answer_from_cache(&mut self, cache: &Mutex) { - while !self.requests.is_complete() { - let idx = self.requests.num_answered(); - match self.requests[idx].respond_local(cache) { - Some(response) => { - self.requests.supply_response_unchecked(&response); - - // update header and back-references after each from-cache - // response to ensure that the requests are left in a consistent - // state and increase the likelihood of being able to answer - // the next request from cache. - self.update_header_refs(idx, &response); - self.fill_unanswered(); - - self.responses.push(response); - } - None => break, - } - } - } - - // update header refs if the given response contains a header future requests require for - // verification. - // `idx` is the index of the request the response corresponds to. - fn update_header_refs(&mut self, idx: usize, response: &Response) { - if let Response::HeaderByHash(ref hdr) = *response { - // fill the header for all requests waiting on this one. - // TODO: could be faster if we stored a map usize => Vec - // but typical use just has one header request that others - // depend on. - for r in self.requests.iter_mut().skip(idx + 1) { - if r.needs_header().map_or(false, |(i, _)| i == idx) { - r.provide_header(hdr.clone()) - } - } - } - } - - // supply a response. - fn supply_response(&mut self, cache: &Mutex, response: &basic_request::Response) - -> Result<(), basic_request::ResponseError> - { - match self.requests.supply_response(&cache, response) { - Ok(response) => { - let idx = self.responses.len(); - self.update_header_refs(idx, &response); - self.responses.push(response); - Ok(()) - } - Err(e) => Err(e), - } - } - - // if the requests are complete, send the result and consume self. - fn try_complete(self) -> Option { - if self.requests.is_complete() { - if self.sender.send(Ok(self.responses)).is_err() { - debug!(target: "on_demand", "Dropped oneshot channel receiver on request"); - } - None - } else { - Some(self) - } - } - - fn fill_unanswered(&mut self) { - self.requests.fill_unanswered(); - } - - // update the cached network requests. - fn update_net_requests(&mut self) { - use request::IncompleteRequest; - - let mut builder = basic_request::Builder::default(); - let num_answered = self.requests.num_answered(); - let mut mapping = move |idx| idx - num_answered; - - for request in self.requests.iter().skip(num_answered) { - let mut net_req = request.clone().into_net_request(); - - // all back-references with request index less than `num_answered` have - // been filled by now. all remaining requests point to nothing earlier - // than the next unanswered request. - net_req.adjust_refs(&mut mapping); - builder.push(net_req) - .expect("all back-references to answered requests have been filled; qed"); - } - - // update pending fields. - let capabilities = guess_capabilities(&self.requests[num_answered..]); - self.net_requests = builder.build(); - self.required_capabilities = capabilities; - } - - // received too many empty responses, may be away to indicate a faulty request - fn bad_response(self, response_err: ResponseGuardError) { - let reqs: Vec<&str> = self.requests.requests().iter().map(|req| { - match req { - CheckedRequest::HeaderProof(_, _) => "HeaderProof", - CheckedRequest::HeaderByHash(_, _) => "HeaderByHash", - CheckedRequest::HeaderWithAncestors(_, _) => "HeaderWithAncestors", - CheckedRequest::TransactionIndex(_, _) => "TransactionIndex", - CheckedRequest::Receipts(_, _) => "Receipts", - CheckedRequest::Body(_, _) => "Body", - CheckedRequest::Account(_, _) => "Account", - CheckedRequest::Code(_, _) => "Code", - CheckedRequest::Execution(_, _) => "Execution", - CheckedRequest::Signal(_, _) => "Signal", - } - }).collect(); - - let err = format!("Bad response on {}: [ {} ]. {}", - if reqs.len() > 1 { "requests" } else { "request" }, - reqs.join(", "), - response_err - ); - - let err = self::error::ErrorKind::BadResponse(err); - if self.sender.send(Err(err.into())).is_err() { - debug!(target: "on_demand", "Dropped oneshot channel receiver on no response"); - } - } - - // returning a peer discovery timeout during query attempts - fn request_limit_reached(self) { - let err = self::error::ErrorKind::RequestLimit; - if self.sender.send(Err(err.into())).is_err() { - debug!(target: "on_demand", "Dropped oneshot channel receiver on time out"); - } - } -} - -// helper to guess capabilities required for a given batch of network requests. -fn guess_capabilities(requests: &[CheckedRequest]) -> Capabilities { - let mut caps = Capabilities { - serve_headers: false, - serve_chain_since: None, - serve_state_since: None, - tx_relay: false, - }; - - let update_since = |current: &mut Option, new| - *current = match *current { - Some(x) => Some(::std::cmp::min(x, new)), - None => Some(new), - }; - - for request in requests { - match *request { - // TODO: might be worth returning a required block number for this also. - CheckedRequest::HeaderProof(_, _) => - caps.serve_headers = true, - CheckedRequest::HeaderByHash(_, _) => - caps.serve_headers = true, - CheckedRequest::HeaderWithAncestors(_, _) => - caps.serve_headers = true, - CheckedRequest::TransactionIndex(_, _) => {} // hashes yield no info. - CheckedRequest::Signal(_, _) => - caps.serve_headers = true, - CheckedRequest::Body(ref req, _) => if let Ok(ref hdr) = req.0.as_ref() { - update_since(&mut caps.serve_chain_since, hdr.number()); - }, - CheckedRequest::Receipts(ref req, _) => if let Ok(ref hdr) = req.0.as_ref() { - update_since(&mut caps.serve_chain_since, hdr.number()); - }, - CheckedRequest::Account(ref req, _) => if let Ok(ref hdr) = req.header.as_ref() { - update_since(&mut caps.serve_state_since, hdr.number()); - }, - CheckedRequest::Code(ref req, _) => if let Ok(ref hdr) = req.header.as_ref() { - update_since(&mut caps.serve_state_since, hdr.number()); - }, - CheckedRequest::Execution(ref req, _) => if let Ok(ref hdr) = req.header.as_ref() { - update_since(&mut caps.serve_state_since, hdr.number()); - }, - } - } - - caps -} - -/// A future extracting the concrete output type of the generic adapter -/// from a vector of responses. -pub struct OnResponses { - receiver: Receiver, - _marker: PhantomData, -} - -impl Future for OnResponses { - type Item = T::Out; - type Error = self::error::Error; - - fn poll(&mut self) -> Poll { - match self.receiver.poll() { - Ok(Async::Ready(Ok(v))) => Ok(Async::Ready(T::extract_from(v))), - Ok(Async::Ready(Err(e))) => Err(e), - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(e) => Err(e.into()), - } - } -} - -/// On demand request service. See module docs for more details. -/// Accumulates info about all peers' capabilities and dispatches -/// requests to them accordingly. -// lock in declaration order. -pub struct OnDemand { - pending: RwLock>, - peers: RwLock>, - in_transit: RwLock>, - cache: Arc>, - no_immediate_dispatch: bool, - response_time_window: Duration, - request_backoff_start: Duration, - request_backoff_max: Duration, - request_backoff_rounds_max: usize, - request_number_of_consecutive_errors: usize -} - -impl OnDemandRequester for OnDemand { - fn request_raw(&self, ctx: &BasicContext, requests: Vec) - -> Result, basic_request::NoSuchOutput> - { - let (sender, receiver) = oneshot::channel(); - if requests.is_empty() { - assert!(sender.send(Ok(Vec::new())).is_ok(), "receiver still in scope; qed"); - return Ok(receiver); - } - - let mut builder = basic_request::Builder::default(); - - let responses = Vec::with_capacity(requests.len()); - - let mut header_producers = HashMap::new(); - for (i, request) in requests.into_iter().enumerate() { - let request = CheckedRequest::from(request); - - // ensure that all requests needing headers will get them. - if let Some((idx, field)) = request.needs_header() { - // a request chain with a header back-reference is valid only if it both - // points to a request that returns a header and has the same back-reference - // for the block hash. - match header_producers.get(&idx) { - Some(ref f) if &field == *f => {} - _ => return Err(basic_request::NoSuchOutput), - } - } - if let CheckedRequest::HeaderByHash(ref req, _) = request { - header_producers.insert(i, req.0); - } - - builder.push(request)?; - } - - let requests = builder.build(); - let net_requests = requests.clone().map_requests(|req| req.into_net_request()); - let capabilities = guess_capabilities(requests.requests()); - - self.submit_pending(ctx, Pending { - requests, - net_requests, - required_capabilities: capabilities, - responses, - sender, - request_guard: RequestGuard::new( - self.request_number_of_consecutive_errors as u32, - self.request_backoff_rounds_max, - self.request_backoff_start, - self.request_backoff_max, - ), - response_guard: ResponseGuard::new(self.response_time_window), - }); - - Ok(receiver) - } - - fn request(&self, ctx: &BasicContext, requests: T) -> Result, basic_request::NoSuchOutput> - where T: request::RequestAdapter - { - self.request_raw(ctx, requests.make_requests()).map(|recv| OnResponses { - receiver: recv, - _marker: PhantomData, - }) - } - -} - -impl OnDemand { - - /// Create a new `OnDemand` service with the given cache. - pub fn new( - cache: Arc>, - response_time_window: Duration, - request_backoff_start: Duration, - request_backoff_max: Duration, - request_backoff_rounds_max: usize, - request_number_of_consecutive_errors: usize, - ) -> Self { - - Self { - pending: RwLock::new(Vec::new()), - peers: RwLock::new(HashMap::new()), - in_transit: RwLock::new(HashMap::new()), - cache, - no_immediate_dispatch: false, - response_time_window: Self::sanitize_circuit_breaker_input(response_time_window, "Response time window"), - request_backoff_start: Self::sanitize_circuit_breaker_input(request_backoff_start, "Request initial backoff time window"), - request_backoff_max: Self::sanitize_circuit_breaker_input(request_backoff_max, "Request maximum backoff time window"), - request_backoff_rounds_max, - request_number_of_consecutive_errors, - } - } - - fn sanitize_circuit_breaker_input(dur: Duration, name: &'static str) -> Duration { - if dur.as_secs() < 1 { - warn!(target: "on_demand", - "{} is too short must be at least 1 second, configuring it to 1 second", name); - Duration::from_secs(1) - } else { - dur - } - } - - // make a test version: this doesn't dispatch pending requests - // until you trigger it manually. - #[cfg(test)] - fn new_test( - cache: Arc>, - request_ttl: Duration, - request_backoff_start: Duration, - request_backoff_max: Duration, - request_backoff_rounds_max: usize, - request_number_of_consecutive_errors: usize, - ) -> Self { - let mut me = OnDemand::new( - cache, - request_ttl, - request_backoff_start, - request_backoff_max, - request_backoff_rounds_max, - request_number_of_consecutive_errors, - ); - me.no_immediate_dispatch = true; - - me - } - - - // maybe dispatch pending requests. - // sometimes - fn attempt_dispatch(&self, ctx: &BasicContext) { - if !self.no_immediate_dispatch { - self.dispatch_pending(ctx) - } - } - - // dispatch pending requests, and discard those for which the corresponding - // receiver has been dropped. - fn dispatch_pending(&self, ctx: &BasicContext) { - if self.pending.read().is_empty() { - return - } - - let mut pending = self.pending.write(); - - // iterate over all pending requests, and check them for hang-up. - // then, try and find a peer who can serve it. - let peers = self.peers.read(); - - *pending = ::std::mem::replace(&mut *pending, Vec::new()) - .into_iter() - .filter(|pending| !pending.sender.is_canceled()) - .filter_map(|mut pending| { - - let num_peers = peers.len(); - // The first peer to dispatch the request is chosen at random - let rand = rand::thread_rng().gen_range(0, cmp::max(1, num_peers)); - - for (peer_id, peer) in peers - .iter() - .cycle() - .skip(rand) - .take(num_peers) - { - - if !peer.can_fulfill(&pending.required_capabilities) { - trace!(target: "on_demand", "Peer {} without required capabilities, skipping", peer_id); - continue - } - - if pending.request_guard.is_call_permitted() { - if let Ok(req_id) = ctx.request_from(*peer_id, pending.net_requests.clone()) { - self.in_transit.write().insert(req_id, pending); - return None; - } - } - } - - // Register that the request round failed - if let RequestError::ReachedLimit = pending.request_guard.register_error() { - pending.request_limit_reached(); - None - } else { - Some(pending) - } - }) - .collect(); // `pending` now contains all requests we couldn't dispatch - - trace!(target: "on_demand", "Was unable to dispatch {} requests.", pending.len()); - } - - // submit a pending request set. attempts to answer from cache before - // going to the network. if complete, sends response and consumes the struct. - fn submit_pending(&self, ctx: &BasicContext, mut pending: Pending) { - // answer as many requests from cache as we can, and schedule for dispatch - // if incomplete. - - pending.answer_from_cache(&*self.cache); - if let Some(mut pending) = pending.try_complete() { - // update cached requests - pending.update_net_requests(); - // push into `pending` buffer - self.pending.write().push(pending); - // try to dispatch - self.attempt_dispatch(ctx); - } - } -} - -impl Handler for OnDemand { - fn on_connect( - &self, - ctx: &EventContext, - status: &Status, - capabilities: &Capabilities - ) -> PeerStatus { - self.peers.write().insert( - ctx.peer(), - Peer { status: status.clone(), capabilities: *capabilities } - ); - self.attempt_dispatch(ctx.as_basic()); - PeerStatus::Kept - } - - fn on_disconnect(&self, ctx: &EventContext, unfulfilled: &[ReqId]) { - self.peers.write().remove(&ctx.peer()); - let ctx = ctx.as_basic(); - - { - let mut pending = self.pending.write(); - for unfulfilled in unfulfilled { - if let Some(unfulfilled) = self.in_transit.write().remove(unfulfilled) { - trace!(target: "on_demand", "Attempting to reassign dropped request"); - pending.push(unfulfilled); - } - } - } - - self.attempt_dispatch(ctx); - } - - fn on_announcement(&self, ctx: &EventContext, announcement: &Announcement) { - { - let mut peers = self.peers.write(); - if let Some(ref mut peer) = peers.get_mut(&ctx.peer()) { - peer.status.update_from(&announcement); - peer.capabilities.update_from(&announcement); - } - } - - self.attempt_dispatch(ctx.as_basic()); - } - - fn on_responses(&self, ctx: &EventContext, req_id: ReqId, responses: &[basic_request::Response]) { - let mut pending = match self.in_transit.write().remove(&req_id) { - Some(req) => req, - None => return, - }; - - if responses.is_empty() { - // Max number of `bad` responses reached, drop the request - if let Err(e) = pending.response_guard.register_error(&ResponseError::Validity(ValidityError::Empty)) { - pending.bad_response(e); - return; - } - } - - // for each incoming response - // 1. ensure verification data filled. - // 2. pending.requests.supply_response - // 3. if extracted on-demand response, keep it for later. - for response in responses { - if let Err(e) = pending.supply_response(&*self.cache, response) { - let peer = ctx.peer(); - debug!(target: "on_demand", "Peer {} gave bad response: {:?}", peer, e); - ctx.disable_peer(peer); - - // Max number of `bad` responses reached, drop the request - if let Err(err) = pending.response_guard.register_error(&e) { - pending.bad_response(err); - return; - } - } - } - - pending.fill_unanswered(); - self.submit_pending(ctx.as_basic(), pending); - } - - fn tick(&self, ctx: &BasicContext) { - self.attempt_dispatch(ctx) - } -} diff --git a/ethcore/light/src/on_demand/request.rs b/ethcore/light/src/on_demand/request.rs deleted file mode 100644 index a183dcbcabc..00000000000 --- a/ethcore/light/src/on_demand/request.rs +++ /dev/null @@ -1,1341 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Request types, verification, and verification errors. - -use std::cmp; -use std::sync::Arc; - -use bytes::Bytes; -use common_types::basic_account::BasicAccount; -use common_types::encoded; -use common_types::receipt::Receipt; -use common_types::transaction::SignedTransaction; -use ethcore::engines::{EthEngine, StateDependentProof}; -use ethcore::machine::EthereumMachine; -use ethcore::state::{self, ProvedExecution}; -use ethereum_types::{H256, U256, Address}; -use ethtrie::{TrieError, TrieDB}; -use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY, KECCAK_EMPTY_LIST_RLP, keccak}; -use hash_db::HashDB; -use kvdb::DBValue; -use parking_lot::Mutex; -use request::{self as net_request, IncompleteRequest, CompleteRequest, Output, OutputKind, Field}; -use rlp::{RlpStream, Rlp}; -use trie::Trie; -use vm::EnvInfo; - -const SUPPLIED_MATCHES: &str = "supplied responses always match produced requests; enforced by `check_response`; qed"; - -/// Core unit of the API: submit batches of these to be answered with `Response`s. -#[derive(Clone)] -pub enum Request { - /// A request for a header proof. - HeaderProof(HeaderProof), - /// A request for a header by hash. - HeaderByHash(HeaderByHash), - /// A request for a header by hash with a range of its ancestors. - HeaderWithAncestors(HeaderWithAncestors), - /// A request for the index of a transaction. - TransactionIndex(TransactionIndex), - /// A request for block receipts. - Receipts(BlockReceipts), - /// A request for a block body. - Body(Body), - /// A request for an account. - Account(Account), - /// A request for a contract's code. - Code(Code), - /// A request for proof of execution. - Execution(TransactionProof), - /// A request for epoch change signal. - Signal(Signal), -} - -/// A request argument. -pub trait RequestArg { - /// the response type. - type Out; - - /// Create the request type. - /// `extract` must not fail when presented with the corresponding - /// `Response`. - fn make(self) -> Request; - - /// May not panic if the response corresponds with the request - /// from `make`. - /// Is free to panic otherwise. - fn extract(r: Response) -> Self::Out; -} - -/// An adapter can be thought of as a grouping of request argument types. -/// This is implemented for various tuples and convenient types. -pub trait RequestAdapter { - /// The output type. - type Out; - - /// Infallibly produce requests. When `extract_from` is presented - /// with the corresponding response vector, it may not fail. - fn make_requests(self) -> Vec; - - /// Extract the output type from the given responses. - /// If they are the corresponding responses to the requests - /// made by `make_requests`, do not panic. - fn extract_from(Vec) -> Self::Out; -} - -impl RequestAdapter for Vec { - type Out = Vec; - - fn make_requests(self) -> Vec { - self.into_iter().map(RequestArg::make).collect() - } - - fn extract_from(r: Vec) -> Self::Out { - r.into_iter().map(T::extract).collect() - } -} - -// helper to implement `RequestArg` and `From` for a single request kind. -macro_rules! impl_single { - ($variant: ident, $me: ty, $out: ty) => { - impl RequestArg for $me { - type Out = $out; - - fn make(self) -> Request { - Request::$variant(self) - } - - fn extract(r: Response) -> $out { - match r { - Response::$variant(x) => x, - _ => panic!(SUPPLIED_MATCHES), - } - } - } - - impl From<$me> for Request { - fn from(me: $me) -> Request { - Request::$variant(me) - } - } - } -} - -// implement traits for each kind of request. -impl_single!(HeaderProof, HeaderProof, (H256, U256)); -impl_single!(HeaderByHash, HeaderByHash, encoded::Header); -impl_single!(HeaderWithAncestors, HeaderWithAncestors, Vec); -impl_single!(TransactionIndex, TransactionIndex, net_request::TransactionIndexResponse); -impl_single!(Receipts, BlockReceipts, Vec); -impl_single!(Body, Body, encoded::Block); -impl_single!(Account, Account, Option); -impl_single!(Code, Code, Bytes); -impl_single!(Execution, TransactionProof, super::ExecutionResult); -impl_single!(Signal, Signal, Vec); - -macro_rules! impl_args { - () => { - impl RequestAdapter for T { - type Out = T::Out; - - fn make_requests(self) -> Vec { - vec![self.make()] - } - - fn extract_from(mut responses: Vec) -> Self::Out { - T::extract(responses.pop().expect(SUPPLIED_MATCHES)) - } - } - }; - ($first: ident, $($next: ident,)*) => { - impl< - $first: RequestArg, - $($next: RequestArg,)* - > - RequestAdapter for ($first, $($next,)*) { - type Out = ($first::Out, $($next::Out,)*); - - fn make_requests(self) -> Vec { - let ($first, $($next,)*) = self; - - vec![ - $first.make(), - $($next.make(),)* - ] - } - - fn extract_from(responses: Vec) -> Self::Out { - let mut iter = responses.into_iter(); - ( - $first::extract(iter.next().expect(SUPPLIED_MATCHES)), - $($next::extract(iter.next().expect(SUPPLIED_MATCHES)),)* - ) - } - } - impl_args!($($next,)*); - } -} - -mod impls { - #![allow(non_snake_case)] - - use super::{RequestAdapter, RequestArg, Request, Response, SUPPLIED_MATCHES}; - - impl_args!(A, B, C, D, E, F, G, H, I, J, K, L,); -} - -/// A block header to be used for verification. -/// May be stored or an unresolved output of a prior request. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum HeaderRef { - /// A stored header. - Stored(encoded::Header), - /// An unresolved header. The first item here is the index of the request which - /// will return the header. The second is a back-reference pointing to a block hash - /// which can be used to make requests until that header is resolved. - Unresolved(usize, Field), -} - -impl HeaderRef { - /// Attempt to inspect the header. - pub fn as_ref(&self) -> Result<&encoded::Header, Error> { - match *self { - HeaderRef::Stored(ref hdr) => Ok(hdr), - HeaderRef::Unresolved(idx, _) => Err(Error::UnresolvedHeader(idx)), - } - } - - // get the blockhash field to be used in requests. - fn field(&self) -> Field { - match *self { - HeaderRef::Stored(ref hdr) => Field::Scalar(hdr.hash()), - HeaderRef::Unresolved(_, field) => field, - } - } - - // yield the index of the request which will produce the header. - fn needs_header(&self) -> Option<(usize, Field)> { - match *self { - HeaderRef::Stored(_) => None, - HeaderRef::Unresolved(idx, field) => Some((idx, field)), - } - } -} - -impl From for HeaderRef { - fn from(header: encoded::Header) -> Self { - HeaderRef::Stored(header) - } -} - -/// Requests coupled with their required data for verification. -/// This is used internally but not part of the public API. -#[derive(Clone)] -#[allow(missing_docs)] -pub enum CheckedRequest { - HeaderProof(HeaderProof, net_request::IncompleteHeaderProofRequest), - HeaderByHash(HeaderByHash, net_request::IncompleteHeadersRequest), - HeaderWithAncestors(HeaderWithAncestors, net_request::IncompleteHeadersRequest), - TransactionIndex(TransactionIndex, net_request::IncompleteTransactionIndexRequest), - Receipts(BlockReceipts, net_request::IncompleteReceiptsRequest), - Body(Body, net_request::IncompleteBodyRequest), - Account(Account, net_request::IncompleteAccountRequest), - Code(Code, net_request::IncompleteCodeRequest), - Execution(TransactionProof, net_request::IncompleteExecutionRequest), - Signal(Signal, net_request::IncompleteSignalRequest) -} - -impl From for CheckedRequest { - fn from(req: Request) -> Self { - match req { - Request::HeaderByHash(req) => { - let net_req = net_request::IncompleteHeadersRequest { - start: req.0.map(Into::into), - skip: 0, - max: 1, - reverse: false, - }; - trace!(target: "on_demand", "HeaderByHash Request, {:?}", net_req); - CheckedRequest::HeaderByHash(req, net_req) - } - Request::HeaderWithAncestors(req) => { - let net_req = net_request::IncompleteHeadersRequest { - start: req.block_hash.map(Into::into), - skip: 0, - max: req.ancestor_count + 1, - reverse: true, - }; - trace!(target: "on_demand", "HeaderWithAncestors Request, {:?}", net_req); - CheckedRequest::HeaderWithAncestors(req, net_req) - } - Request::HeaderProof(req) => { - let net_req = net_request::IncompleteHeaderProofRequest { - num: req.num().into(), - }; - trace!(target: "on_demand", "HeaderProof Request, {:?}", net_req); - CheckedRequest::HeaderProof(req, net_req) - } - Request::TransactionIndex(req) => { - let net_req = net_request::IncompleteTransactionIndexRequest { - hash: req.0, - }; - trace!(target: "on_demand", "TransactionIndex Request, {:?}", net_req); - CheckedRequest::TransactionIndex(req, net_req) - } - Request::Body(req) => { - let net_req = net_request::IncompleteBodyRequest { - hash: req.0.field(), - }; - trace!(target: "on_demand", "Body Request, {:?}", net_req); - CheckedRequest::Body(req, net_req) - } - Request::Receipts(req) => { - let net_req = net_request::IncompleteReceiptsRequest { - hash: req.0.field(), - }; - trace!(target: "on_demand", "Receipt Request, {:?}", net_req); - CheckedRequest::Receipts(req, net_req) - } - Request::Account(req) => { - let net_req = net_request::IncompleteAccountRequest { - block_hash: req.header.field(), - address_hash: ::hash::keccak(&req.address).into(), - }; - trace!(target: "on_demand", "Account Request, {:?}", net_req); - CheckedRequest::Account(req, net_req) - } - Request::Code(req) => { - let net_req = net_request::IncompleteCodeRequest { - block_hash: req.header.field(), - code_hash: req.code_hash, - }; - trace!(target: "on_demand", "Code Request, {:?}", net_req); - CheckedRequest::Code(req, net_req) - } - Request::Execution(req) => { - let net_req = net_request::IncompleteExecutionRequest { - block_hash: req.header.field(), - from: req.tx.sender(), - gas: req.tx.gas, - gas_price: req.tx.gas_price, - action: req.tx.action.clone(), - value: req.tx.value, - data: req.tx.data.clone(), - }; - trace!(target: "on_demand", "Execution request, {:?}", net_req); - CheckedRequest::Execution(req, net_req) - } - Request::Signal(req) => { - let net_req = net_request::IncompleteSignalRequest { - block_hash: req.hash.into(), - }; - trace!(target: "on_demand", "Signal Request, {:?}", net_req); - CheckedRequest::Signal(req, net_req) - } - } - } -} - -impl CheckedRequest { - /// Convert this into a network request. - pub fn into_net_request(self) -> net_request::Request { - use ::request::Request as NetRequest; - - match self { - CheckedRequest::HeaderProof(_, req) => NetRequest::HeaderProof(req), - CheckedRequest::HeaderByHash(_, req) => NetRequest::Headers(req), - CheckedRequest::HeaderWithAncestors(_, req) => NetRequest::Headers(req), - CheckedRequest::TransactionIndex(_, req) => NetRequest::TransactionIndex(req), - CheckedRequest::Receipts(_, req) => NetRequest::Receipts(req), - CheckedRequest::Body(_, req) => NetRequest::Body(req), - CheckedRequest::Account(_, req) => NetRequest::Account(req), - CheckedRequest::Code(_, req) => NetRequest::Code(req), - CheckedRequest::Execution(_, req) => NetRequest::Execution(req), - CheckedRequest::Signal(_, req) => NetRequest::Signal(req), - } - } - - /// Whether this needs a header from a prior request. - /// Returns `Some` with the index of the request returning the header - /// and the field giving the hash - /// if so, `None` otherwise. - pub fn needs_header(&self) -> Option<(usize, Field)> { - match *self { - CheckedRequest::Receipts(ref x, _) => x.0.needs_header(), - CheckedRequest::Body(ref x, _) => x.0.needs_header(), - CheckedRequest::Account(ref x, _) => x.header.needs_header(), - CheckedRequest::Code(ref x, _) => x.header.needs_header(), - CheckedRequest::Execution(ref x, _) => x.header.needs_header(), - _ => None, - } - } - - /// Provide a header where one was needed. Should only be called if `needs_header` - /// returns `Some`, and for correctness, only use the header yielded by the correct - /// request. - pub fn provide_header(&mut self, header: encoded::Header) { - match *self { - CheckedRequest::Receipts(ref mut x, _) => x.0 = HeaderRef::Stored(header), - CheckedRequest::Body(ref mut x, _) => x.0 = HeaderRef::Stored(header), - CheckedRequest::Account(ref mut x, _) => x.header = HeaderRef::Stored(header), - CheckedRequest::Code(ref mut x, _) => x.header = HeaderRef::Stored(header), - CheckedRequest::Execution(ref mut x, _) => x.header = HeaderRef::Stored(header), - _ => {}, - } - } - - /// Attempt to complete the request based on data in the cache. - pub fn respond_local(&self, cache: &Mutex<::cache::Cache>) -> Option { - match *self { - CheckedRequest::HeaderProof(ref check, _) => { - let mut cache = cache.lock(); - cache.block_hash(check.num) - .and_then(|h| cache.chain_score(&h).map(|s| (h, s))) - .map(|(h, s)| Response::HeaderProof((h, s))) - } - CheckedRequest::HeaderByHash(_, ref req) => { - if let Some(&net_request::HashOrNumber::Hash(ref h)) = req.start.as_ref() { - return cache.lock().block_header(h).map(Response::HeaderByHash); - } - - None - } - CheckedRequest::HeaderWithAncestors(_, ref req) => { - if req.skip != 1 || !req.reverse { - return None; - } - - if let Some(&net_request::HashOrNumber::Hash(start)) = req.start.as_ref() { - let mut result = Vec::with_capacity(req.max as usize); - let mut hash = start; - let mut cache = cache.lock(); - for _ in 0..req.max { - match cache.block_header(&hash) { - Some(header) => { - hash = header.parent_hash(); - result.push(header); - } - None => return None, - } - } - Some(Response::HeaderWithAncestors(result)) - } else { None } - } - CheckedRequest::Receipts(ref check, ref req) => { - // empty transactions -> no receipts - if check.0.as_ref().ok().map_or(false, |hdr| hdr.receipts_root() == KECCAK_NULL_RLP) { - return Some(Response::Receipts(Vec::new())); - } - - req.hash.as_ref() - .and_then(|hash| cache.lock().block_receipts(hash)) - .map(Response::Receipts) - } - CheckedRequest::Body(ref check, ref req) => { - // check for empty body. - if let Ok(hdr) = check.0.as_ref() { - if hdr.transactions_root() == KECCAK_NULL_RLP && hdr.uncles_hash() == KECCAK_EMPTY_LIST_RLP { - let mut stream = RlpStream::new_list(3); - stream.append_raw(hdr.rlp().as_raw(), 1); - stream.begin_list(0); - stream.begin_list(0); - - return Some(Response::Body(encoded::Block::new(stream.out()))); - } - } - - // otherwise, check for cached body and header. - let block_hash = req.hash.as_ref() - .cloned() - .or_else(|| check.0.as_ref().ok().map(|hdr| hdr.hash())); - let block_hash = match block_hash { - Some(hash) => hash, - None => return None, - }; - - let mut cache = cache.lock(); - let cached_header; - - // can't use as_ref here although it seems like you would be able to: - // it complains about uninitialized `cached_header`. - let block_header = match check.0.as_ref().ok() { - Some(hdr) => Some(hdr), - None => { - cached_header = cache.block_header(&block_hash); - cached_header.as_ref() - } - }; - - block_header - .and_then(|hdr| cache.block_body(&block_hash).map(|b| (hdr, b))) - .map(|(hdr, body)| { - Response::Body(encoded::Block::new_from_header_and_body(&hdr.view(), &body.view())) - }) - } - CheckedRequest::Code(_, ref req) => { - if req.code_hash.as_ref().map_or(false, |&h| h == KECCAK_EMPTY) { - Some(Response::Code(Vec::new())) - } else { - None - } - } - _ => None, - } - } -} - -macro_rules! match_me { - ($me: expr, ($check: pat, $req: pat) => $e: expr) => { - match $me { - CheckedRequest::HeaderProof($check, $req) => $e, - CheckedRequest::HeaderByHash($check, $req) => $e, - CheckedRequest::HeaderWithAncestors($check, $req) => $e, - CheckedRequest::TransactionIndex($check, $req) => $e, - CheckedRequest::Receipts($check, $req) => $e, - CheckedRequest::Body($check, $req) => $e, - CheckedRequest::Account($check, $req) => $e, - CheckedRequest::Code($check, $req) => $e, - CheckedRequest::Execution($check, $req) => $e, - CheckedRequest::Signal($check, $req) => $e, - } - } -} - -impl IncompleteRequest for CheckedRequest { - type Complete = CompleteRequest; - type Response = net_request::Response; - - fn check_outputs(&self, mut f: F) -> Result<(), net_request::NoSuchOutput> - where F: FnMut(usize, usize, OutputKind) -> Result<(), net_request::NoSuchOutput> - { - match *self { - CheckedRequest::HeaderProof(_, ref req) => req.check_outputs(f), - CheckedRequest::HeaderByHash(ref check, ref req) => { - req.check_outputs(&mut f)?; - - // make sure the output given is definitively a hash. - match check.0 { - Field::BackReference(r, idx) => f(r, idx, OutputKind::Hash), - _ => Ok(()), - } - } - CheckedRequest::HeaderWithAncestors(ref check, ref req) => { - req.check_outputs(&mut f)?; - - // make sure the output given is definitively a hash. - match check.block_hash { - Field::BackReference(r, idx) => f(r, idx, OutputKind::Hash), - _ => Ok(()), - } - } - CheckedRequest::TransactionIndex(_, ref req) => req.check_outputs(f), - CheckedRequest::Receipts(_, ref req) => req.check_outputs(f), - CheckedRequest::Body(_, ref req) => req.check_outputs(f), - CheckedRequest::Account(_, ref req) => req.check_outputs(f), - CheckedRequest::Code(_, ref req) => req.check_outputs(f), - CheckedRequest::Execution(_, ref req) => req.check_outputs(f), - CheckedRequest::Signal(_, ref req) => req.check_outputs(f), - } - } - - fn note_outputs(&self, f: F) where F: FnMut(usize, OutputKind) { - match_me!(*self, (_, ref req) => req.note_outputs(f)) - } - - fn fill(&mut self, f: F) where F: Fn(usize, usize) -> Result { - match_me!(*self, (_, ref mut req) => req.fill(f)) - } - - fn complete(self) -> Result { - match self { - CheckedRequest::HeaderProof(_, req) => { - trace!(target: "on_demand", "HeaderProof request completed {:?}", req); - req.complete().map(CompleteRequest::HeaderProof) - } - CheckedRequest::HeaderByHash(_, req) => { - trace!(target: "on_demand", "HeaderByHash request completed {:?}", req); - req.complete().map(CompleteRequest::Headers) - } - CheckedRequest::HeaderWithAncestors(_, req) => { - trace!(target: "on_demand", "HeaderWithAncestors request completed {:?}", req); - req.complete().map(CompleteRequest::Headers) - } - CheckedRequest::TransactionIndex(_, req) => { - trace!(target: "on_demand", "TransactionIndex request completed {:?}", req); - req.complete().map(CompleteRequest::TransactionIndex) - } - CheckedRequest::Receipts(_, req) => { - trace!(target: "on_demand", "Receipt request completed {:?}", req); - req.complete().map(CompleteRequest::Receipts) - } - CheckedRequest::Body(_, req) => { - trace!(target: "on_demand", "Block request completed {:?}", req); - req.complete().map(CompleteRequest::Body) - } - CheckedRequest::Account(_, req) => { - trace!(target: "on_demand", "Account request completed {:?}", req); - req.complete().map(CompleteRequest::Account) - } - CheckedRequest::Code(_, req) => { - trace!(target: "on_demand", "Code request completed {:?}", req); - req.complete().map(CompleteRequest::Code) - } - CheckedRequest::Execution(_, req) => { - trace!(target: "on_demand", "Execution request completed {:?}", req); - req.complete().map(CompleteRequest::Execution) - } - CheckedRequest::Signal(_, req) => { - trace!(target: "on_demand", "Signal request completed {:?}", req); - req.complete().map(CompleteRequest::Signal) - } - } - } - - fn adjust_refs(&mut self, mapping: F) where F: FnMut(usize) -> usize { - match_me!(*self, (_, ref mut req) => req.adjust_refs(mapping)) - } -} - -impl net_request::CheckedRequest for CheckedRequest { - type Extract = Response; - type Error = Error; - type Environment = Mutex<::cache::Cache>; - - /// Check whether the response matches (beyond the type). - fn check_response(&self, complete: &Self::Complete, cache: &Mutex<::cache::Cache>, response: &Self::Response) -> Result { - use ::request::Response as NetResponse; - - // helper for expecting a specific response for a given request. - macro_rules! expect { - ($res: pat => $e: expr) => {{ - match (response, complete) { - $res => $e, - _ => Err(Error::WrongKind), - } - }} - } - - // check response against contained prover. - match *self { - CheckedRequest::HeaderProof(ref prover, _) => - expect!((&NetResponse::HeaderProof(ref res), _) => - prover.check_response(cache, &res.proof).map(Response::HeaderProof)), - CheckedRequest::HeaderByHash(ref prover, _) => - expect!((&NetResponse::Headers(ref res), &CompleteRequest::Headers(ref req)) => - prover.check_response(cache, &req.start, &res.headers).map(Response::HeaderByHash)), - CheckedRequest::HeaderWithAncestors(ref prover, _) => - expect!((&NetResponse::Headers(ref res), &CompleteRequest::Headers(ref req)) => - prover.check_response(cache, &req.start, &res.headers).map(Response::HeaderWithAncestors)), - CheckedRequest::TransactionIndex(ref prover, _) => - expect!((&NetResponse::TransactionIndex(ref res), _) => - prover.check_response(cache, res).map(Response::TransactionIndex)), - CheckedRequest::Receipts(ref prover, _) => - expect!((&NetResponse::Receipts(ref res), _) => - prover.check_response(cache, &res.receipts).map(Response::Receipts)), - CheckedRequest::Body(ref prover, _) => - expect!((&NetResponse::Body(ref res), _) => - prover.check_response(cache, &res.body).map(Response::Body)), - CheckedRequest::Account(ref prover, _) => - expect!((&NetResponse::Account(ref res), _) => - prover.check_response(cache, &res.proof).map(Response::Account)), - CheckedRequest::Code(ref prover, _) => - expect!((&NetResponse::Code(ref res), &CompleteRequest::Code(ref req)) => - prover.check_response(cache, &req.code_hash, &res.code).map(Response::Code)), - CheckedRequest::Execution(ref prover, _) => - expect!((&NetResponse::Execution(ref res), _) => - prover.check_response(cache, &res.items).map(Response::Execution)), - CheckedRequest::Signal(ref prover, _) => - expect!((&NetResponse::Signal(ref res), _) => - prover.check_response(cache, &res.signal).map(Response::Signal)), - } - } -} - -/// Responses to on-demand requests. -/// All of these are checked. -pub enum Response { - /// Response to a header proof request. - /// Returns the hash and chain score. - HeaderProof((H256, U256)), - /// Response to a header-by-hash request. - HeaderByHash(encoded::Header), - /// Response to a header-by-hash with ancestors request. - HeaderWithAncestors(Vec), - /// Response to a transaction-index request. - TransactionIndex(net_request::TransactionIndexResponse), - /// Response to a receipts request. - Receipts(Vec), - /// Response to a block body request. - Body(encoded::Block), - /// Response to an Account request. - // TODO: `unwrap_or(engine_defaults)` - Account(Option), - /// Response to a request for code. - Code(Vec), - /// Response to a request for proved execution. - Execution(super::ExecutionResult), - /// Response to a request for epoch change signal. - Signal(Vec), -} - -impl net_request::ResponseLike for Response { - fn fill_outputs(&self, mut f: F) where F: FnMut(usize, Output) { - match *self { - Response::HeaderProof((ref hash, _)) => f(0, Output::Hash(*hash)), - Response::Account(None) => { - f(0, Output::Hash(KECCAK_EMPTY)); // code hash - f(1, Output::Hash(KECCAK_NULL_RLP)); // storage root. - } - Response::Account(Some(ref acc)) => { - f(0, Output::Hash(acc.code_hash)); - f(1, Output::Hash(acc.storage_root)); - } - _ => {} - } - } -} - -/// Errors in verification. -#[derive(Debug, PartialEq)] -pub enum Error { - /// RLP decoder error. - Decoder(::rlp::DecoderError), - /// Empty response. - Empty, - /// Response data length exceeds request max. - TooManyResults(u64, u64), - /// Response data is incomplete. - TooFewResults(u64, u64), - /// Trie lookup error (result of bad proof) - Trie(TrieError), - /// Bad inclusion proof - BadProof, - /// Header by number instead of hash. - HeaderByNumber, - /// Unresolved header reference. - UnresolvedHeader(usize), - /// Wrong header number. - WrongNumber(u64, u64), - /// Wrong hash. - WrongHash(H256, H256), - /// Wrong trie root. - WrongTrieRoot(H256, H256), - /// Wrong response kind. - WrongKind, - /// Wrong sequence of headers. - WrongHeaderSequence, -} - -impl From<::rlp::DecoderError> for Error { - fn from(err: ::rlp::DecoderError) -> Self { - Error::Decoder(err) - } -} - -impl From> for Error { - fn from(err: Box) -> Self { - Error::Trie(*err) - } -} - -/// Request for header proof by number -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct HeaderProof { - /// The header's number. - num: u64, - /// The cht number for the given block number. - cht_num: u64, - /// The root of the CHT containing this header. - cht_root: H256, -} - -impl HeaderProof { - /// Construct a new header-by-number request. Fails if the given number is 0. - /// Provide the expected CHT root to compare against. - pub fn new(num: u64, cht_root: H256) -> Option { - ::cht::block_to_cht_number(num).map(|cht_num| HeaderProof { - num, - cht_num, - cht_root, - }) - } - - /// Access the requested block number. - pub fn num(&self) -> u64 { self.num } - - /// Access the CHT number. - pub fn cht_num(&self) -> u64 { self.cht_num } - - /// Access the expected CHT root. - pub fn cht_root(&self) -> H256 { self.cht_root } - - /// Check a response with a CHT proof, get a hash and total difficulty back. - pub fn check_response(&self, cache: &Mutex<::cache::Cache>, proof: &[Bytes]) -> Result<(H256, U256), Error> { - match ::cht::check_proof(proof, self.num, self.cht_root) { - Some((expected_hash, td)) => { - let mut cache = cache.lock(); - cache.insert_block_hash(self.num, expected_hash); - cache.insert_chain_score(expected_hash, td); - - Ok((expected_hash, td)) - } - None => Err(Error::BadProof), - } - } -} - -/// Request for a header by hash with a range of ancestors. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct HeaderWithAncestors { - /// Hash of the last block in the range to fetch. - pub block_hash: Field, - /// Number of headers before the last block to fetch in addition. - pub ancestor_count: u64, -} - -impl HeaderWithAncestors { - /// Check a response for the headers. - pub fn check_response( - &self, - cache: &Mutex<::cache::Cache>, - start: &net_request::HashOrNumber, - headers: &[encoded::Header] - ) -> Result, Error> { - let expected_hash = match (self.block_hash, start) { - (Field::Scalar(h), &net_request::HashOrNumber::Hash(h2)) => { - if h != h2 { return Err(Error::WrongHash(h, h2)) } - h - } - (_, &net_request::HashOrNumber::Hash(h2)) => h2, - _ => return Err(Error::HeaderByNumber), - }; - - let start_header = headers.first().ok_or(Error::Empty)?; - let start_hash = start_header.hash(); - if start_hash != expected_hash { - return Err(Error::WrongHash(expected_hash, start_hash)); - } - - let expected_len = 1 + cmp::min(self.ancestor_count, start_header.number()); - let actual_len = headers.len() as u64; - match actual_len.cmp(&expected_len) { - cmp::Ordering::Less => - return Err(Error::TooFewResults(expected_len, actual_len)), - cmp::Ordering::Greater => - return Err(Error::TooManyResults(expected_len, actual_len)), - cmp::Ordering::Equal => (), - }; - - for (header, prev_header) in headers.iter().zip(headers[1..].iter()) { - if header.number() != prev_header.number() + 1 || - header.parent_hash() != prev_header.hash() - { - return Err(Error::WrongHeaderSequence) - } - } - - let mut cache = cache.lock(); - for header in headers { - cache.insert_block_header(header.hash(), header.clone()); - } - - Ok(headers.to_vec()) - } -} - -/// Request for a header by hash. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct HeaderByHash(pub Field); - -impl HeaderByHash { - /// Check a response for the header. - pub fn check_response( - &self, - cache: &Mutex<::cache::Cache>, - start: &net_request::HashOrNumber, - headers: &[encoded::Header] - ) -> Result { - let expected_hash = match (self.0, start) { - (Field::Scalar(h), &net_request::HashOrNumber::Hash(h2)) => { - if h != h2 { return Err(Error::WrongHash(h, h2)) } - h - } - (_, &net_request::HashOrNumber::Hash(h2)) => h2, - _ => return Err(Error::HeaderByNumber), - }; - - let header = headers.get(0).ok_or(Error::Empty)?; - let hash = header.hash(); - if hash == expected_hash { - cache.lock().insert_block_header(hash, header.clone()); - Ok(header.clone()) - } else { - Err(Error::WrongHash(expected_hash, hash)) - } - } -} - -/// Request for a transaction index. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct TransactionIndex(pub Field); - -impl TransactionIndex { - /// Check a response for the transaction index. - // - // TODO: proper checking involves looking at canonicality of the - // hash w.r.t. the current best block header. - // - // unlike all other forms of request, we don't know the header to check - // until we make this request. - // - // This would require lookups in the database or perhaps CHT requests, - // which aren't currently possible. - // - // Also, returning a result that is not locally canonical doesn't necessarily - // indicate misbehavior, so the punishment scheme would need to be revised. - pub fn check_response( - &self, - _cache: &Mutex<::cache::Cache>, - res: &net_request::TransactionIndexResponse, - ) -> Result { - Ok(res.clone()) - } -} - -/// Request for a block, with header for verification. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Body(pub HeaderRef); - -impl Body { - /// Check a response for this block body. - pub fn check_response(&self, cache: &Mutex<::cache::Cache>, body: &encoded::Body) -> Result { - // check the integrity of the the body against the header - let header = self.0.as_ref()?; - let tx_root = ::triehash::ordered_trie_root(body.transactions_rlp().iter().map(|r| r.as_raw())); - if tx_root != header.transactions_root() { - trace!(target: "on_demand", "Body Response: \"WrongTrieRoot\" tx_root: {:?} header_root: {:?}", tx_root, header.transactions_root()); - return Err(Error::WrongTrieRoot(header.transactions_root(), tx_root)); - } - - let uncles_hash = keccak(body.uncles_rlp().as_raw()); - if uncles_hash != header.uncles_hash() { - trace!(target: "on_demand", "Body Response: \"WrongHash\" tx_root: {:?} header_root: {:?}", uncles_hash, header.uncles_hash()); - return Err(Error::WrongHash(header.uncles_hash(), uncles_hash)); - } - - // concatenate the header and the body. - let block = encoded::Block::new_from_header_and_body(&header.view(), &body.view()); - - cache.lock().insert_block_body(header.hash(), body.clone()); - Ok(block) - } -} - -/// Request for a block's receipts with header for verification. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct BlockReceipts(pub HeaderRef); - -impl BlockReceipts { - /// Check a response with receipts against the stored header. - pub fn check_response(&self, cache: &Mutex<::cache::Cache>, receipts: &[Receipt]) -> Result, Error> { - let receipts_root = self.0.as_ref()?.receipts_root(); - let found_root = ::triehash::ordered_trie_root(receipts.iter().map(|r| ::rlp::encode(r))); - - if receipts_root == found_root { - cache.lock().insert_block_receipts(receipts_root, receipts.to_vec()); - Ok(receipts.to_vec()) - } else { - trace!(target: "on_demand", "Receipt Reponse: \"WrongTrieRoot\" receipts_root: {:?} found_root: {:?}", receipts_root, found_root); - Err(Error::WrongTrieRoot(receipts_root, found_root)) - } - } -} - -/// Request for an account structure. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Account { - /// Header for verification. - pub header: HeaderRef, - /// Address requested. - pub address: Address, -} - -impl Account { - /// Check a response with an account against the stored header. - pub fn check_response(&self, _: &Mutex<::cache::Cache>, proof: &[Bytes]) -> Result, Error> { - let header = self.header.as_ref()?; - let state_root = header.state_root(); - - let mut db = journaldb::new_memory_db(); - for node in proof { db.insert(&node[..]); } - - match TrieDB::new(&db, &state_root).and_then(|t| t.get(&keccak(&self.address)))? { - Some(val) => { - let rlp = Rlp::new(&val); - Ok(Some(BasicAccount { - nonce: rlp.val_at(0)?, - balance: rlp.val_at(1)?, - storage_root: rlp.val_at(2)?, - code_hash: rlp.val_at(3)?, - })) - }, - None => { - trace!(target: "on_demand", "Account {:?} not found", self.address); - Ok(None) - } - } - } -} - -/// Request for account code. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Code { - /// Header reference. - pub header: HeaderRef, - /// Account's code hash. - pub code_hash: Field, -} - -impl Code { - /// Check a response with code against the code hash. - pub fn check_response( - &self, - _: &Mutex<::cache::Cache>, - code_hash: &H256, - code: &[u8] - ) -> Result, Error> { - let found_hash = keccak(code); - if &found_hash == code_hash { - Ok(code.to_vec()) - } else { - Err(Error::WrongHash(*code_hash, found_hash)) - } - } -} - -/// Request for transaction execution, along with the parts necessary to verify the proof. -#[derive(Clone)] -pub struct TransactionProof { - /// The transaction to request proof of. - pub tx: SignedTransaction, - /// Block header. - pub header: HeaderRef, - /// Transaction environment info. - // TODO: it's not really possible to provide this if the header is unknown. - pub env_info: EnvInfo, - /// Consensus engine. - pub engine: Arc, -} - -impl TransactionProof { - /// Check the proof, returning the proved execution or indicate that the proof was bad. - pub fn check_response(&self, _: &Mutex<::cache::Cache>, state_items: &[DBValue]) -> Result { - let root = self.header.as_ref()?.state_root(); - - let mut env_info = self.env_info.clone(); - env_info.gas_limit = self.tx.gas; - - let proved_execution = state::check_proof( - state_items, - root, - &self.tx, - self.engine.machine(), - &self.env_info, - ); - - match proved_execution { - ProvedExecution::BadProof => { - trace!(target: "on_demand", "BadExecution Proof"); - Err(Error::BadProof) - } - ProvedExecution::Failed(e) => { - trace!(target: "on_demand", "Execution Proof failed: {:?}", e); - Ok(Err(e)) - } - ProvedExecution::Complete(e) => { - trace!(target: "on_demand", "Execution successful: {:?}", e); - Ok(Ok(e)) - } - } - } -} - -/// Request for epoch signal. -/// Provide engine and state-dependent proof checker. -#[derive(Clone)] -pub struct Signal { - /// Block hash and number to fetch proof for. - pub hash: H256, - /// Consensus engine, used to check the proof. - pub engine: Arc, - /// Special checker for the proof. - pub proof_check: Arc>, -} - -impl Signal { - /// Check the signal, returning the signal or indicate that it's bad. - pub fn check_response(&self, _: &Mutex<::cache::Cache>, signal: &[u8]) -> Result, Error> { - self.proof_check.check_proof(self.engine.machine(), signal) - .map(|_| signal.to_owned()) - .map_err(|_| Error::BadProof) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::time::Duration; - use ethereum_types::{H256, Address}; - use parking_lot::Mutex; - use trie::{Trie, TrieMut}; - use ethtrie::{SecTrieDB, SecTrieDBMut}; - use trie::Recorder; - use hash::keccak; - - use ethcore::client::{BlockChainClient, BlockInfo, TestBlockChainClient, EachBlockWith}; - use common_types::header::Header; - use common_types::encoded; - use common_types::receipt::{Receipt, TransactionOutcome}; - - fn make_cache() -> ::cache::Cache { - ::cache::Cache::new(Default::default(), Duration::from_secs(1)) - } - - #[test] - fn no_invalid_header_by_number() { - assert!(HeaderProof::new(0, Default::default()).is_none()) - } - - #[test] - fn check_header_proof() { - use ::cht; - - let test_client = TestBlockChainClient::new(); - test_client.add_blocks(10500, EachBlockWith::Nothing); - - let cht = { - let fetcher = |id| { - let hdr = test_client.block_header(id).unwrap(); - let td = test_client.block_total_difficulty(id).unwrap(); - Some(cht::BlockInfo { - hash: hdr.hash(), - parent_hash: hdr.parent_hash(), - total_difficulty: td, - }) - }; - - cht::build(cht::block_to_cht_number(10_000).unwrap(), fetcher).unwrap() - }; - - let proof = cht.prove(10_000, 0).unwrap().unwrap(); - let req = HeaderProof::new(10_000, cht.root()).unwrap(); - - let cache = Mutex::new(make_cache()); - assert!(req.check_response(&cache, &proof[..]).is_ok()); - } - - #[test] - fn check_header_by_hash() { - let mut header = Header::new(); - header.set_number(10_000); - header.set_extra_data(b"test_header".to_vec()); - let hash = header.hash(); - let raw_header = encoded::Header::new(::rlp::encode(&header)); - - let cache = Mutex::new(make_cache()); - assert!(HeaderByHash(hash.into()).check_response(&cache, &hash.into(), &[raw_header]).is_ok()) - } - - #[test] - fn check_header_with_ancestors() { - let mut last_header_hash = H256::default(); - let mut headers = (0..11).map(|num| { - let mut header = Header::new(); - header.set_number(num); - header.set_parent_hash(last_header_hash); - - last_header_hash = header.hash(); - header - }).collect::>(); - - headers.reverse(); // because responses are in reverse order - - let raw_headers = headers.iter() - .map(|hdr| encoded::Header::new(::rlp::encode(hdr))) - .collect::>(); - - let mut invalid_successor = Header::new(); - invalid_successor.set_number(11); - invalid_successor.set_parent_hash(headers[1].hash()); - - let raw_invalid_successor = encoded::Header::new(::rlp::encode(&invalid_successor)); - - let cache = Mutex::new(make_cache()); - - let header_with_ancestors = |hash, count| { - HeaderWithAncestors { - block_hash: hash, - ancestor_count: count - } - }; - - // Correct responses - assert!(header_with_ancestors(headers[0].hash().into(), 0) - .check_response(&cache, &headers[0].hash().into(), &raw_headers[0..1]).is_ok()); - assert!(header_with_ancestors(headers[0].hash().into(), 2) - .check_response(&cache, &headers[0].hash().into(), &raw_headers[0..3]).is_ok()); - assert!(header_with_ancestors(headers[0].hash().into(), 10) - .check_response(&cache, &headers[0].hash().into(), &raw_headers[0..11]).is_ok()); - assert!(header_with_ancestors(headers[2].hash().into(), 2) - .check_response(&cache, &headers[2].hash().into(), &raw_headers[2..5]).is_ok()); - assert!(header_with_ancestors(headers[2].hash().into(), 10) - .check_response(&cache, &headers[2].hash().into(), &raw_headers[2..11]).is_ok()); - assert!(header_with_ancestors(invalid_successor.hash().into(), 0) - .check_response(&cache, &invalid_successor.hash().into(), &[raw_invalid_successor.clone()]).is_ok()); - - // Incorrect responses - assert_eq!(header_with_ancestors(invalid_successor.hash().into(), 0) - .check_response(&cache, &headers[0].hash().into(), &raw_headers[0..1]), - Err(Error::WrongHash(invalid_successor.hash(), headers[0].hash()))); - assert_eq!(header_with_ancestors(headers[0].hash().into(), 0) - .check_response(&cache, &headers[0].hash().into(), &[]), - Err(Error::Empty)); - assert_eq!(header_with_ancestors(headers[0].hash().into(), 10) - .check_response(&cache, &headers[0].hash().into(), &raw_headers[0..10]), - Err(Error::TooFewResults(11, 10))); - assert_eq!(header_with_ancestors(headers[0].hash().into(), 9) - .check_response(&cache, &headers[0].hash().into(), &raw_headers[0..11]), - Err(Error::TooManyResults(10, 11))); - - let response = &[raw_headers[0].clone(), raw_headers[2].clone()]; - assert_eq!(header_with_ancestors(headers[0].hash().into(), 1) - .check_response(&cache, &headers[0].hash().into(), response), - Err(Error::WrongHeaderSequence)); - - let response = &[raw_invalid_successor.clone(), raw_headers[0].clone()]; - assert_eq!(header_with_ancestors(invalid_successor.hash().into(), 1) - .check_response(&cache, &invalid_successor.hash().into(), response), - Err(Error::WrongHeaderSequence)); - - let response = &[raw_invalid_successor.clone(), raw_headers[1].clone()]; - assert_eq!(header_with_ancestors(invalid_successor.hash().into(), 1) - .check_response(&cache, &invalid_successor.hash().into(), response), - Err(Error::WrongHeaderSequence)); - } - - #[test] - fn check_body() { - use rlp::RlpStream; - - let header = Header::new(); - let mut body_stream = RlpStream::new_list(2); - body_stream.begin_list(0).begin_list(0); - - let req = Body(encoded::Header::new(::rlp::encode(&header)).into()); - - let cache = Mutex::new(make_cache()); - let response = encoded::Body::new(body_stream.drain()); - assert!(req.check_response(&cache, &response).is_ok()) - } - - #[test] - fn check_receipts() { - let receipts = (0..5).map(|_| Receipt { - outcome: TransactionOutcome::StateRoot(H256::random()), - gas_used: 21_000u64.into(), - log_bloom: Default::default(), - logs: Vec::new(), - }).collect::>(); - - let mut header = Header::new(); - let receipts_root = ::triehash::ordered_trie_root( - receipts.iter().map(|x| ::rlp::encode(x)) - ); - - header.set_receipts_root(receipts_root); - - let req = BlockReceipts(encoded::Header::new(::rlp::encode(&header)).into()); - - let cache = Mutex::new(make_cache()); - assert!(req.check_response(&cache, &receipts).is_ok()) - } - - #[test] - fn check_state_proof() { - use rlp::RlpStream; - - let mut root = H256::default(); - let mut db = journaldb::new_memory_db(); - let mut header = Header::new(); - header.set_number(123_456); - header.set_extra_data(b"test_header".to_vec()); - - let addr = Address::random(); - let rand_acc = || { - let mut stream = RlpStream::new_list(4); - stream.append(&2u64) - .append(&100_000_000u64) - .append(&H256::random()) - .append(&H256::random()); - - stream.out() - }; - { - let mut trie = SecTrieDBMut::new(&mut db, &mut root); - for _ in 0..100 { - let address = Address::random(); - trie.insert(&*address, &rand_acc()).unwrap(); - } - - trie.insert(&*addr, &rand_acc()).unwrap(); - } - - let proof = { - let trie = SecTrieDB::new(&db, &root).unwrap(); - let mut recorder = Recorder::new(); - - trie.get_with(&*addr, &mut recorder).unwrap().unwrap(); - - recorder.drain().into_iter().map(|r| r.data).collect::>() - }; - - header.set_state_root(root.clone()); - - let req = Account { - header: encoded::Header::new(::rlp::encode(&header)).into(), - address: addr, - }; - - let cache = Mutex::new(make_cache()); - assert!(req.check_response(&cache, &proof[..]).is_ok()); - } - - #[test] - fn check_code() { - let code = vec![1u8; 256]; - let code_hash = keccak(&code); - let header = Header::new(); - let req = Code { - header: encoded::Header::new(::rlp::encode(&header)).into(), - code_hash: code_hash.into(), - }; - - let cache = Mutex::new(make_cache()); - assert!(req.check_response(&cache, &code_hash, &code).is_ok()); - assert!(req.check_response(&cache, &code_hash, &[]).is_err()); - } -} diff --git a/ethcore/light/src/on_demand/request_guard.rs b/ethcore/light/src/on_demand/request_guard.rs deleted file mode 100644 index 1c67ab0c87f..00000000000 --- a/ethcore/light/src/on_demand/request_guard.rs +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use failsafe; -use std::time::Duration; - -type RequestPolicy = failsafe::failure_policy::ConsecutiveFailures; - -/// Error wrapped on-top of `FailsafeError` -#[derive(Debug, PartialEq)] -pub enum Error { - /// The call is let through - LetThrough, - /// The call rejected by the guard - Rejected, - /// The request reached the maximum of backoff iterations - ReachedLimit, -} - -/// Handle and register requests that can fail -#[derive(Debug)] -pub struct RequestGuard { - backoff_round: usize, - max_backoff_rounds: usize, - state: failsafe::StateMachine, -} - -impl RequestGuard { - /// Constructor - pub fn new( - consecutive_failures: u32, - max_backoff_rounds: usize, - start_backoff: Duration, - max_backoff: Duration, - ) -> Self { - let backoff = failsafe::backoff::exponential(start_backoff, max_backoff); - // success_rate not used because only errors are registered - let policy = failsafe::failure_policy::consecutive_failures(consecutive_failures as u32, backoff); - - Self { - backoff_round: 0, - max_backoff_rounds, - state: failsafe::StateMachine::new(policy, ()), - } - } - - /// Update the state after a `faulty` call - pub fn register_error(&mut self) -> Error { - trace!(target: "circuit_breaker", "RequestGuard; backoff_round: {}/{}, state {:?}", - self.backoff_round, self.max_backoff_rounds, self.state); - - if self.backoff_round >= self.max_backoff_rounds { - Error::ReachedLimit - } else if self.state.is_call_permitted() { - self.state.on_error(); - if self.state.is_call_permitted() { - Error::LetThrough - } else { - self.backoff_round += 1; - Error::Rejected - } - } else { - Error::Rejected - } - } - - /// Poll the circuit breaker, to check if the call is permitted - pub fn is_call_permitted(&self) -> bool { - self.state.is_call_permitted() - } -} - -#[cfg(test)] -mod tests { - use std::iter; - use std::time::Instant; - use super::*; - - #[test] - fn one_consecutive_failure_with_10_backoffs() { - // 1, 2, 4, 5, 5 .... 5 - let binary_exp_backoff = vec![1_u64, 2, 4].into_iter().chain(iter::repeat(5_u64).take(7)); - let mut guard = RequestGuard::new(1, 10, Duration::from_secs(1), Duration::from_secs(5)); - for backoff in binary_exp_backoff { - assert_eq!(guard.register_error(), Error::Rejected); - let now = Instant::now(); - while now.elapsed() <= Duration::from_secs(backoff) {} - } - assert_eq!(guard.register_error(), Error::ReachedLimit, "10 backoffs should be error"); - } - - #[test] - fn five_consecutive_failures_with_3_backoffs() { - let mut guard = RequestGuard::new(5, 3, Duration::from_secs(1), Duration::from_secs(30)); - - // register five errors - for _ in 0..4 { - assert_eq!(guard.register_error(), Error::LetThrough); - } - - let binary_exp_backoff = [1, 2, 4]; - for backoff in &binary_exp_backoff { - assert_eq!(guard.register_error(), Error::Rejected); - let now = Instant::now(); - while now.elapsed() <= Duration::from_secs(*backoff) {} - } - - assert_eq!(guard.register_error(), Error::ReachedLimit, "3 backoffs should be an error"); - } -} diff --git a/ethcore/light/src/on_demand/response_guard.rs b/ethcore/light/src/on_demand/response_guard.rs deleted file mode 100644 index c4c2ac23aec..00000000000 --- a/ethcore/light/src/on_demand/response_guard.rs +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! ResponseGuard implementation. -//! It is responsible for the receiving end of `Pending Request` (see `OnDemand` module docs for more information) -//! The major functionality is the following: -//! 1) Register non-successful responses which will reported back if it fails -//! 2) A timeout mechanism that will wait for successful response at most t seconds - -use std::time::{Duration, Instant}; -use std::collections::HashMap; -use std::fmt; - -use super::{ResponseError, ValidityError}; - -/// Response guard error type -#[derive(Debug, Eq, PartialEq)] -pub enum Error { - /// No majority, the error reason can't be determined - NoMajority(usize), - /// Majority, with the error reason - Majority(Inner, usize, usize), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::Majority(err, majority, total) => { - write!(f, "Error cause was {:?}, (majority count: {} / total: {})", - err, majority, total) - } - Error::NoMajority(total) => { - write!(f, "Error cause couldn't be determined, the total number of responses was {}", total) - } - } - } -} - -/// Dummy type to convert a generic type with no trait bounds -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)] -pub enum Inner { - /// Bad execution proof - BadProof, - /// RLP decoding - Decoder, - /// Empty response - EmptyResponse, - /// Wrong header sequence - HeaderByNumber, - /// Too few results - TooFewResults, - /// Too many results - TooManyResults, - /// Trie error - Trie, - /// Unresolved header - UnresolvedHeader, - /// No responses expected. - Unexpected, - /// Wrong hash - WrongHash, - /// Wrong Header sequence - WrongHeaderSequence, - /// Wrong response kind - WrongKind, - /// Wrong number - WrongNumber, - /// Wrong Trie Root - WrongTrieRoot, -} - -/// Handle and register responses that can fail -#[derive(Debug)] -pub struct ResponseGuard { - request_start: Instant, - time_to_live: Duration, - responses: HashMap, - number_responses: usize, -} - -impl ResponseGuard { - /// Constructor - pub fn new(time_to_live: Duration) -> Self { - Self { - request_start: Instant::now(), - time_to_live, - responses: HashMap::new(), - number_responses: 0, - } - } - - fn into_reason(&self, err: &ResponseError) -> Inner { - match err { - ResponseError::Unexpected => Inner::Unexpected, - ResponseError::Validity(ValidityError::BadProof) => Inner::BadProof, - ResponseError::Validity(ValidityError::Decoder(_)) => Inner::Decoder, - ResponseError::Validity(ValidityError::Empty) => Inner::EmptyResponse, - ResponseError::Validity(ValidityError::HeaderByNumber) => Inner::HeaderByNumber, - ResponseError::Validity(ValidityError::TooFewResults(_, _)) => Inner::TooFewResults, - ResponseError::Validity(ValidityError::TooManyResults(_, _)) => Inner::TooManyResults, - ResponseError::Validity(ValidityError::Trie(_)) => Inner::Trie, - ResponseError::Validity(ValidityError::UnresolvedHeader(_)) => Inner::UnresolvedHeader, - ResponseError::Validity(ValidityError::WrongHash(_, _)) => Inner::WrongHash, - ResponseError::Validity(ValidityError::WrongHeaderSequence) => Inner::WrongHeaderSequence, - ResponseError::Validity(ValidityError::WrongKind) => Inner::WrongKind, - ResponseError::Validity(ValidityError::WrongNumber(_, _)) => Inner::WrongNumber, - ResponseError::Validity(ValidityError::WrongTrieRoot(_, _)) => Inner::WrongTrieRoot, - } - } - - /// Update the state after a `faulty` call - pub fn register_error(&mut self, err: &ResponseError) -> Result<(), Error> { - let err = self.into_reason(err); - *self.responses.entry(err).or_insert(0) += 1; - self.number_responses = self.number_responses.saturating_add(1); - trace!(target: "circuit_breaker", "ResponseGuard: {:?}", self.responses); - // The request has exceeded its timeout - if self.request_start.elapsed() >= self.time_to_live { - let (&err, &max_count) = self.responses.iter().max_by_key(|(_k, v)| *v).expect("got at least one element; qed"); - let majority = self.responses.values().filter(|v| **v == max_count).count() == 1; - if majority { - Err(Error::Majority(err, max_count, self.number_responses)) - } else { - Err(Error::NoMajority(self.number_responses)) - } - } else { - Ok(()) - } - } -} - -#[cfg(test)] -mod tests { - use std::thread; - use super::*; - - #[test] - fn test_basic_by_majority() { - let mut guard = ResponseGuard::new(Duration::from_secs(5)); - guard.register_error(&ResponseError::Validity(ValidityError::Empty)).unwrap(); - guard.register_error(&ResponseError::Unexpected).unwrap(); - guard.register_error(&ResponseError::Unexpected).unwrap(); - guard.register_error(&ResponseError::Unexpected).unwrap(); - thread::sleep(Duration::from_secs(5)); - - assert_eq!(guard.register_error(&ResponseError::Validity(ValidityError::WrongKind)), Err(Error::Majority(Inner::Unexpected, 3, 5))); - } - - #[test] - fn test_no_majority() { - let mut guard = ResponseGuard::new(Duration::from_secs(5)); - guard.register_error(&ResponseError::Validity(ValidityError::Empty)).unwrap(); - guard.register_error(&ResponseError::Validity(ValidityError::Empty)).unwrap(); - guard.register_error(&ResponseError::Unexpected).unwrap(); - guard.register_error(&ResponseError::Unexpected).unwrap(); - thread::sleep(Duration::from_secs(5)); - - assert_eq!(guard.register_error(&ResponseError::Validity(ValidityError::WrongKind)), Err(Error::NoMajority(5))); - } -} diff --git a/ethcore/light/src/on_demand/tests.rs b/ethcore/light/src/on_demand/tests.rs deleted file mode 100644 index 49ec35f10db..00000000000 --- a/ethcore/light/src/on_demand/tests.rs +++ /dev/null @@ -1,598 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Tests for the on-demand service. - -use cache::Cache; -use futures::Future; -use network::{PeerId, NodeId}; -use net::*; -use common_types::header::Header; -use ethereum_types::H256; -use parking_lot::Mutex; -use request::{self as basic_request, Response}; - -use std::sync::Arc; -use std::time::{Duration, Instant}; -use std::thread; - -use super::{request, OnDemand, OnDemandRequester, Peer, HeaderRef}; - -// useful contexts to give the service. -enum Context { - NoOp, - WithPeer(PeerId), - RequestFrom(PeerId, ReqId), - Punish(PeerId), - FaultyRequest, -} - -impl EventContext for Context { - fn peer(&self) -> PeerId { - match *self { - Context::WithPeer(id) - | Context::RequestFrom(id, _) - | Context::Punish(id) => id, - | Context::FaultyRequest => 0, - _ => panic!("didn't expect to have peer queried."), - } - } - - fn as_basic(&self) -> &BasicContext { self } -} - -impl BasicContext for Context { - /// Returns the relevant's peer persistent Id (aka NodeId). - fn persistent_peer_id(&self, _: PeerId) -> Option { - panic!("didn't expect to provide persistent ID") - } - - fn request_from(&self, peer_id: PeerId, _: ::request::NetworkRequests) -> Result { - match *self { - Context::RequestFrom(id, req_id) => if peer_id == id { Ok(req_id) } else { Err(Error::NoCredits) }, - Context::FaultyRequest => Err(Error::NoCredits), - _ => panic!("didn't expect to have requests dispatched."), - } - } - - fn make_announcement(&self, _: Announcement) { - panic!("didn't expect to make announcement") - } - - fn disconnect_peer(&self, id: PeerId) { - self.disable_peer(id) - } - - fn disable_peer(&self, peer_id: PeerId) { - match *self { - Context::Punish(id) if id == peer_id => {}, - _ => panic!("Unexpectedly punished peer."), - } - } -} - -// test harness. -struct Harness { - service: OnDemand, -} - -impl Harness { - fn create() -> Self { - let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(60)))); - Harness { - service: OnDemand::new_test( - cache, - // Response `time_to_live` - Duration::from_secs(5), - // Request start backoff - Duration::from_secs(1), - // Request max backoff - Duration::from_secs(20), - super::DEFAULT_MAX_REQUEST_BACKOFF_ROUNDS, - super::DEFAULT_NUM_CONSECUTIVE_FAILED_REQUESTS - ) - } - } - - fn inject_peer(&self, id: PeerId, peer: Peer) { - self.service.peers.write().insert(id, peer); - } -} - -fn dummy_status() -> Status { - Status { - protocol_version: 1, - network_id: 999, - head_td: 1.into(), - head_hash: H256::default(), - head_num: 1359, - genesis_hash: H256::default(), - last_head: None, - } -} - -fn dummy_capabilities() -> Capabilities { - Capabilities { - serve_headers: true, - serve_chain_since: Some(1), - serve_state_since: Some(1), - tx_relay: true, - } -} - -#[test] -fn detects_hangup() { - let on_demand = Harness::create().service; - let result = on_demand.request_raw( - &Context::NoOp, - vec![request::HeaderByHash(H256::default().into()).into()], - ); - - assert_eq!(on_demand.pending.read().len(), 1); - drop(result); - - on_demand.dispatch_pending(&Context::NoOp); - assert!(on_demand.pending.read().is_empty()); -} - -#[test] -fn single_request() { - let harness = Harness::create(); - - let peer_id = 10101; - let req_id = ReqId(14426); - - harness.inject_peer(peer_id, Peer { - status: dummy_status(), - capabilities: dummy_capabilities(), - }); - - let header = Header::default(); - let encoded = header.encoded(); - - let recv = harness.service.request_raw( - &Context::NoOp, - vec![request::HeaderByHash(header.hash().into()).into()] - ).unwrap(); - - assert_eq!(harness.service.pending.read().len(), 1); - - harness.service.dispatch_pending(&Context::RequestFrom(peer_id, req_id)); - - assert_eq!(harness.service.pending.read().len(), 0); - - harness.service.on_responses( - &Context::WithPeer(peer_id), - req_id, - &[Response::Headers(basic_request::HeadersResponse { headers: vec![encoded] })] - ); - - assert!(recv.wait().is_ok()); -} - -#[test] -fn no_capabilities() { - let harness = Harness::create(); - - let peer_id = 10101; - - let mut capabilities = dummy_capabilities(); - capabilities.serve_headers = false; - - harness.inject_peer(peer_id, Peer { - status: dummy_status(), - capabilities: capabilities, - }); - - let _recv = harness.service.request_raw( - &Context::NoOp, - vec![request::HeaderByHash(H256::default().into()).into()] - ).unwrap(); - - assert_eq!(harness.service.pending.read().len(), 1); - - harness.service.dispatch_pending(&Context::NoOp); - - assert_eq!(harness.service.pending.read().len(), 1); -} - -#[test] -fn reassign() { - let harness = Harness::create(); - - let peer_ids = (10101, 12345); - let req_ids = (ReqId(14426), ReqId(555)); - - harness.inject_peer(peer_ids.0, Peer { - status: dummy_status(), - capabilities: dummy_capabilities(), - }); - - let header = Header::default(); - let encoded = header.encoded(); - - let recv = harness.service.request_raw( - &Context::NoOp, - vec![request::HeaderByHash(header.hash().into()).into()] - ).unwrap(); - - assert_eq!(harness.service.pending.read().len(), 1); - - harness.service.dispatch_pending(&Context::RequestFrom(peer_ids.0, req_ids.0)); - assert_eq!(harness.service.pending.read().len(), 0); - - harness.service.on_disconnect(&Context::WithPeer(peer_ids.0), &[req_ids.0]); - assert_eq!(harness.service.pending.read().len(), 1); - - harness.inject_peer(peer_ids.1, Peer { - status: dummy_status(), - capabilities: dummy_capabilities(), - }); - - harness.service.dispatch_pending(&Context::RequestFrom(peer_ids.1, req_ids.1)); - assert_eq!(harness.service.pending.read().len(), 0); - - harness.service.on_responses( - &Context::WithPeer(peer_ids.1), - req_ids.1, - &[Response::Headers(basic_request::HeadersResponse { headers: vec![encoded] })] - ); - - assert!(recv.wait().is_ok()); -} - -#[test] -fn partial_response() { - let harness = Harness::create(); - - let peer_id = 111; - let req_ids = (ReqId(14426), ReqId(555)); - - harness.inject_peer(peer_id, Peer { - status: dummy_status(), - capabilities: dummy_capabilities(), - }); - - let make = |num| { - let mut hdr = Header::default(); - hdr.set_number(num); - - let encoded = hdr.encoded(); - (hdr, encoded) - }; - - let (header1, encoded1) = make(5); - let (header2, encoded2) = make(23452); - - // request two headers. - let recv = harness.service.request_raw( - &Context::NoOp, - vec![ - request::HeaderByHash(header1.hash().into()).into(), - request::HeaderByHash(header2.hash().into()).into(), - ], - ).unwrap(); - - assert_eq!(harness.service.pending.read().len(), 1); - - harness.service.dispatch_pending(&Context::RequestFrom(peer_id, req_ids.0)); - assert_eq!(harness.service.pending.read().len(), 0); - - // supply only the first one. - harness.service.on_responses( - &Context::WithPeer(peer_id), - req_ids.0, - &[Response::Headers(basic_request::HeadersResponse { headers: vec![encoded1] })] - ); - - assert_eq!(harness.service.pending.read().len(), 1); - - harness.service.dispatch_pending(&Context::RequestFrom(peer_id, req_ids.1)); - assert_eq!(harness.service.pending.read().len(), 0); - - // supply the next one. - harness.service.on_responses( - &Context::WithPeer(peer_id), - req_ids.1, - &[Response::Headers(basic_request::HeadersResponse { headers: vec![encoded2] })] - ); - - assert!(recv.wait().is_ok()); -} - -#[test] -fn part_bad_part_good() { - let harness = Harness::create(); - - let peer_id = 111; - let req_ids = (ReqId(14426), ReqId(555)); - - harness.inject_peer(peer_id, Peer { - status: dummy_status(), - capabilities: dummy_capabilities(), - }); - - let make = |num| { - let mut hdr = Header::default(); - hdr.set_number(num); - - let encoded = hdr.encoded(); - (hdr, encoded) - }; - - let (header1, encoded1) = make(5); - let (header2, encoded2) = make(23452); - - // request two headers. - let recv = harness.service.request_raw( - &Context::NoOp, - vec![ - request::HeaderByHash(header1.hash().into()).into(), - request::HeaderByHash(header2.hash().into()).into(), - ], - ).unwrap(); - - assert_eq!(harness.service.pending.read().len(), 1); - - harness.service.dispatch_pending(&Context::RequestFrom(peer_id, req_ids.0)); - assert_eq!(harness.service.pending.read().len(), 0); - - // supply only the first one, but followed by the wrong kind of response. - // the first header should be processed. - harness.service.on_responses( - &Context::Punish(peer_id), - req_ids.0, - &[ - Response::Headers(basic_request::HeadersResponse { headers: vec![encoded1] }), - Response::Receipts(basic_request::ReceiptsResponse { receipts: vec![] } ), - ] - ); - - assert_eq!(harness.service.pending.read().len(), 1); - - harness.inject_peer(peer_id, Peer { - status: dummy_status(), - capabilities: dummy_capabilities(), - }); - - harness.service.dispatch_pending(&Context::RequestFrom(peer_id, req_ids.1)); - assert_eq!(harness.service.pending.read().len(), 0); - - // supply the next one. - harness.service.on_responses( - &Context::WithPeer(peer_id), - req_ids.1, - &[Response::Headers(basic_request::HeadersResponse { headers: vec![encoded2] })] - ); - - assert!(recv.wait().is_ok()); -} - -#[test] -fn wrong_kind() { - let harness = Harness::create(); - - let peer_id = 10101; - let req_id = ReqId(14426); - - harness.inject_peer(peer_id, Peer { - status: dummy_status(), - capabilities: dummy_capabilities(), - }); - - let _recv = harness.service.request_raw( - &Context::NoOp, - vec![request::HeaderByHash(H256::default().into()).into()] - ).unwrap(); - - assert_eq!(harness.service.pending.read().len(), 1); - - harness.service.dispatch_pending(&Context::RequestFrom(peer_id, req_id)); - - assert_eq!(harness.service.pending.read().len(), 0); - - harness.service.on_responses( - &Context::Punish(peer_id), - req_id, - &[Response::Receipts(basic_request::ReceiptsResponse { receipts: vec![] })] - ); - - assert_eq!(harness.service.pending.read().len(), 1); -} - -#[test] -fn back_references() { - let harness = Harness::create(); - - let peer_id = 10101; - let req_id = ReqId(14426); - - harness.inject_peer(peer_id, Peer { - status: dummy_status(), - capabilities: dummy_capabilities(), - }); - - let header = Header::default(); - let encoded = header.encoded(); - - let recv = harness.service.request_raw( - &Context::NoOp, - vec![ - request::HeaderByHash(header.hash().into()).into(), - request::BlockReceipts(HeaderRef::Unresolved(0, header.hash().into())).into(), - ] - ).unwrap(); - - assert_eq!(harness.service.pending.read().len(), 1); - - harness.service.dispatch_pending(&Context::RequestFrom(peer_id, req_id)); - - assert_eq!(harness.service.pending.read().len(), 0); - - harness.service.on_responses( - &Context::WithPeer(peer_id), - req_id, - &[ - Response::Headers(basic_request::HeadersResponse { headers: vec![encoded] }), - Response::Receipts(basic_request::ReceiptsResponse { receipts: vec![] }), - ] - ); - - assert!(recv.wait().is_ok()); -} - -#[test] -#[should_panic] -fn bad_back_reference() { - let harness = Harness::create(); - - let header = Header::default(); - - let _ = harness.service.request_raw( - &Context::NoOp, - vec![ - request::HeaderByHash(header.hash().into()).into(), - request::BlockReceipts(HeaderRef::Unresolved(1, header.hash().into())).into(), - ] - ).unwrap(); -} - -#[test] -fn fill_from_cache() { - let harness = Harness::create(); - - let peer_id = 10101; - let req_id = ReqId(14426); - - harness.inject_peer(peer_id, Peer { - status: dummy_status(), - capabilities: dummy_capabilities(), - }); - - let header = Header::default(); - let encoded = header.encoded(); - - let recv = harness.service.request_raw( - &Context::NoOp, - vec![ - request::HeaderByHash(header.hash().into()).into(), - request::BlockReceipts(HeaderRef::Unresolved(0, header.hash().into())).into(), - ] - ).unwrap(); - - assert_eq!(harness.service.pending.read().len(), 1); - - harness.service.dispatch_pending(&Context::RequestFrom(peer_id, req_id)); - - assert_eq!(harness.service.pending.read().len(), 0); - - harness.service.on_responses( - &Context::WithPeer(peer_id), - req_id, - &[ - Response::Headers(basic_request::HeadersResponse { headers: vec![encoded] }), - ] - ); - - assert!(recv.wait().is_ok()); -} - -#[test] -fn request_without_response_should_backoff_and_then_be_dropped() { - let harness = Harness::create(); - let peer_id = 0; - let req_id = ReqId(13); - - harness.inject_peer( - peer_id, - Peer { - status: dummy_status(), - capabilities: dummy_capabilities(), - } - ); - - let binary_exp_backoff: Vec = vec![1, 2, 4, 8, 16, 20, 20, 20, 20, 20]; - - let _recv = harness.service.request_raw( - &Context::RequestFrom(peer_id, req_id), - vec![request::HeaderByHash(Header::default().encoded().hash().into()).into()], - ).unwrap(); - assert_eq!(harness.service.pending.read().len(), 1); - - for backoff in &binary_exp_backoff { - harness.service.dispatch_pending(&Context::FaultyRequest); - assert_eq!(harness.service.pending.read().len(), 1, "Request should not be dropped"); - let now = Instant::now(); - while now.elapsed() < Duration::from_secs(*backoff) {} - } - - harness.service.dispatch_pending(&Context::FaultyRequest); - assert_eq!(harness.service.pending.read().len(), 0, "Request exceeded the 10 backoff rounds should be dropped"); -} - -#[test] -fn empty_responses_exceeds_limit_should_be_dropped() { - let harness = Harness::create(); - let peer_id = 0; - let req_id = ReqId(13); - - harness.inject_peer( - peer_id, - Peer { - status: dummy_status(), - capabilities: dummy_capabilities(), - } - ); - - let _recv = harness.service.request_raw( - &Context::RequestFrom(peer_id, req_id), - vec![request::HeaderByHash(Header::default().encoded().hash().into()).into()], - ).unwrap(); - - harness.service.dispatch_pending(&Context::RequestFrom(peer_id, req_id)); - - assert_eq!(harness.service.pending.read().len(), 0); - assert_eq!(harness.service.in_transit.read().len(), 1); - - let now = Instant::now(); - - // Send `empty responses` in the current time window - // Use only half of the `time_window` because we can't be sure exactly - // when the window started and the clock accurancy - while now.elapsed() < harness.service.response_time_window / 2 { - harness.service.on_responses( - &Context::RequestFrom(13, req_id), - req_id, - &[] - ); - assert!(harness.service.pending.read().len() != 0); - let pending = harness.service.pending.write().remove(0); - harness.service.in_transit.write().insert(req_id, pending); - } - - // Make sure we passed the first `time window` - thread::sleep(Duration::from_secs(5)); - - // Now, response is in failure state but need another response to be `polled` - harness.service.on_responses( - &Context::RequestFrom(13, req_id), - req_id, - &[] - ); - - assert!(harness.service.in_transit.read().is_empty()); - assert!(harness.service.pending.read().is_empty()); -} diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs deleted file mode 100644 index 309ff6ec115..00000000000 --- a/ethcore/light/src/provider.rs +++ /dev/null @@ -1,410 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! A provider for the PIP protocol. This is typically a full node, who can -//! give as much data as necessary to its peers. - -use std::sync::Arc; - -use common_types::blockchain_info::BlockChainInfo; -use common_types::encoded; -use common_types::ids::BlockId; -use common_types::transaction::PendingTransaction; -use ethcore::client::{BlockChainClient, ProvingBlockChainClient, ChainInfo, BlockInfo as ClientBlockInfo}; -use ethereum_types::H256; -use parking_lot::RwLock; - -use cht::{self, BlockInfo}; -use client::{LightChainClient, AsLightClient}; -use transaction_queue::TransactionQueue; - -use request; - -/// Maximum allowed size of a headers request. -pub const MAX_HEADERS_PER_REQUEST: u64 = 512; - -/// Defines the operations that a provider for the light subprotocol must fulfill. -pub trait Provider: Send + Sync { - /// Provide current blockchain info. - fn chain_info(&self) -> BlockChainInfo; - - /// Find the depth of a common ancestor between two blocks. - /// If either block is unknown or an ancestor can't be found - /// then return `None`. - fn reorg_depth(&self, a: &H256, b: &H256) -> Option; - - /// Earliest block where state queries are available. - /// If `None`, no state queries are servable. - fn earliest_state(&self) -> Option; - - /// Provide a list of headers starting at the requested block, - /// possibly in reverse and skipping `skip` at a time. - /// - /// The returned vector may have any length in the range [0, `max`], but the - /// results within must adhere to the `skip` and `reverse` parameters. - fn block_headers(&self, req: request::CompleteHeadersRequest) -> Option { - use request::HashOrNumber; - - if req.max == 0 { return None } - - let best_num = self.chain_info().best_block_number; - let start_num = match req.start { - HashOrNumber::Number(start_num) => start_num, - HashOrNumber::Hash(hash) => match self.block_header(BlockId::Hash(hash)) { - None => { - trace!(target: "pip_provider", "Unknown block hash {} requested", hash); - return None; - } - Some(header) => { - let num = header.number(); - let canon_hash = self.block_header(BlockId::Number(num)) - .map(|h| h.hash()); - - if req.max == 1 || canon_hash != Some(hash) { - // Non-canonical header or single header requested. - return Some(::request::HeadersResponse { - headers: vec![header], - }) - } - - num - } - } - }; - - let max = ::std::cmp::min(MAX_HEADERS_PER_REQUEST, req.max); - - let headers: Vec<_> = (0_u64..max) - .map(|x: u64| x.saturating_mul(req.skip.saturating_add(1))) - .take_while(|&x| if req.reverse { x < start_num } else { best_num.saturating_sub(start_num) >= x }) - .map(|x| if req.reverse { start_num.saturating_sub(x) } else { start_num.saturating_add(x) }) - .map(|x| self.block_header(BlockId::Number(x))) - .take_while(|x| x.is_some()) - .flat_map(|x| x) - .collect(); - - if headers.is_empty() { - None - } else { - Some(::request::HeadersResponse { headers }) - } - } - - /// Get a block header by id. - fn block_header(&self, id: BlockId) -> Option; - - /// Get a transaction index by hash. - fn transaction_index(&self, req: request::CompleteTransactionIndexRequest) - -> Option; - - /// Fulfill a block body request. - fn block_body(&self, req: request::CompleteBodyRequest) -> Option; - - /// Fulfill a request for block receipts. - fn block_receipts(&self, req: request::CompleteReceiptsRequest) -> Option; - - /// Get an account proof. - fn account_proof(&self, req: request::CompleteAccountRequest) -> Option; - - /// Get a storage proof. - fn storage_proof(&self, req: request::CompleteStorageRequest) -> Option; - - /// Provide contract code for the specified (block_hash, code_hash) pair. - fn contract_code(&self, req: request::CompleteCodeRequest) -> Option; - - /// Provide a header proof from a given Canonical Hash Trie as well as the - /// corresponding header. - fn header_proof(&self, req: request::CompleteHeaderProofRequest) -> Option; - - /// Provide pending transactions. - fn transactions_to_propagate(&self) -> Vec; - - /// Provide a proof-of-execution for the given transaction proof request. - /// Returns a vector of all state items necessary to execute the transaction. - fn transaction_proof(&self, req: request::CompleteExecutionRequest) -> Option; - - /// Provide epoch signal data at given block hash. This should be just the - fn epoch_signal(&self, req: request::CompleteSignalRequest) -> Option; -} - -// Implementation of a light client data provider for a client. -impl Provider for T { - fn chain_info(&self) -> BlockChainInfo { - ChainInfo::chain_info(self) - } - - fn reorg_depth(&self, a: &H256, b: &H256) -> Option { - self.tree_route(a, b).map(|route| route.index as u64) - } - - fn earliest_state(&self) -> Option { - Some(self.pruning_info().earliest_state) - } - - fn block_header(&self, id: BlockId) -> Option { - ClientBlockInfo::block_header(self, id) - } - - fn transaction_index(&self, req: request::CompleteTransactionIndexRequest) - -> Option - { - use common_types::ids::TransactionId; - - self.transaction_receipt(TransactionId::Hash(req.hash)).map(|receipt| request::TransactionIndexResponse { - num: receipt.block_number, - hash: receipt.block_hash, - index: receipt.transaction_index as u64, - }) - } - - fn block_body(&self, req: request::CompleteBodyRequest) -> Option { - BlockChainClient::block_body(self, BlockId::Hash(req.hash)) - .map(|body| ::request::BodyResponse { body }) - } - - fn block_receipts(&self, req: request::CompleteReceiptsRequest) -> Option { - BlockChainClient::block_receipts(self, &req.hash) - .map(|x| ::request::ReceiptsResponse { receipts: x.receipts }) - } - - fn account_proof(&self, req: request::CompleteAccountRequest) -> Option { - self.prove_account(req.address_hash, BlockId::Hash(req.block_hash)).map(|(proof, acc)| { - ::request::AccountResponse { - proof, - nonce: acc.nonce, - balance: acc.balance, - code_hash: acc.code_hash, - storage_root: acc.storage_root, - } - }) - } - - fn storage_proof(&self, req: request::CompleteStorageRequest) -> Option { - self.prove_storage(req.address_hash, req.key_hash, BlockId::Hash(req.block_hash)).map(|(proof, item) | { - ::request::StorageResponse { - proof, - value: item, - } - }) - } - - fn contract_code(&self, req: request::CompleteCodeRequest) -> Option { - self.state_data(&req.code_hash) - .map(|code| ::request::CodeResponse { code }) - } - - fn header_proof(&self, req: request::CompleteHeaderProofRequest) -> Option { - let cht_number = match cht::block_to_cht_number(req.num) { - Some(cht_num) => cht_num, - None => { - debug!(target: "pip_provider", "Requested CHT proof with invalid block number"); - return None; - } - }; - - let mut needed = None; - - // build the CHT, caching the requested header as we pass through it. - let cht = { - let block_info = |id| { - let hdr = self.block_header(id); - let td = self.block_total_difficulty(id); - - match (hdr, td) { - (Some(hdr), Some(td)) => { - let info = BlockInfo { - hash: hdr.hash(), - parent_hash: hdr.parent_hash(), - total_difficulty: td, - }; - - if hdr.number() == req.num { - needed = Some((hdr, td)); - } - - Some(info) - } - _ => None, - } - }; - - match cht::build(cht_number, block_info) { - Some(cht) => cht, - None => return None, // incomplete CHT. - } - }; - - let (needed_hdr, needed_td) = needed.expect("`needed` always set in loop, number checked before; qed"); - - // prove our result. - match cht.prove(req.num, 0) { - Ok(Some(proof)) => Some(::request::HeaderProofResponse { - proof, - hash: needed_hdr.hash(), - td: needed_td, - }), - Ok(None) => None, - Err(e) => { - debug!(target: "pip_provider", "Error looking up number in freshly-created CHT: {}", e); - None - } - } - } - - fn transaction_proof(&self, req: request::CompleteExecutionRequest) -> Option { - use common_types::transaction::Transaction; - - let id = BlockId::Hash(req.block_hash); - let nonce = match self.nonce(&req.from, id) { - Some(nonce) => nonce, - None => return None, - }; - let transaction = Transaction { - nonce, - gas: req.gas, - gas_price: req.gas_price, - action: req.action, - value: req.value, - data: req.data, - }.fake_sign(req.from); - - self.prove_transaction(transaction, id) - .map(|(_, proof)| ::request::ExecutionResponse { items: proof }) - } - - fn transactions_to_propagate(&self) -> Vec { - BlockChainClient::transactions_to_propagate(self) - .into_iter() - .map(|tx| tx.pending().clone()) - .collect() - } - - fn epoch_signal(&self, req: request::CompleteSignalRequest) -> Option { - self.epoch_signal(req.block_hash).map(|signal| request::SignalResponse { - signal, - }) - } -} - -/// The light client "provider" implementation. This wraps a `LightClient` and -/// a light transaction queue. -pub struct LightProvider { - client: Arc, - txqueue: Arc>, -} - -impl LightProvider { - /// Create a new `LightProvider` from the given client and transaction queue. - pub fn new(client: Arc, txqueue: Arc>) -> Self { - LightProvider { - client, - txqueue, - } - } -} - -// TODO: draw from cache (shared between this and the RPC layer) -impl Provider for LightProvider { - fn chain_info(&self) -> BlockChainInfo { - self.client.as_light_client().chain_info() - } - - fn reorg_depth(&self, _a: &H256, _b: &H256) -> Option { - None - } - - fn earliest_state(&self) -> Option { - None - } - - fn block_header(&self, id: BlockId) -> Option { - self.client.as_light_client().block_header(id) - } - - fn transaction_index(&self, _req: request::CompleteTransactionIndexRequest) - -> Option - { - None - } - - fn block_body(&self, _req: request::CompleteBodyRequest) -> Option { - None - } - - fn block_receipts(&self, _req: request::CompleteReceiptsRequest) -> Option { - None - } - - fn account_proof(&self, _req: request::CompleteAccountRequest) -> Option { - None - } - - fn storage_proof(&self, _req: request::CompleteStorageRequest) -> Option { - None - } - - fn contract_code(&self, _req: request::CompleteCodeRequest) -> Option { - None - } - - fn header_proof(&self, _req: request::CompleteHeaderProofRequest) -> Option { - None - } - - fn transaction_proof(&self, _req: request::CompleteExecutionRequest) -> Option { - None - } - - fn epoch_signal(&self, _req: request::CompleteSignalRequest) -> Option { - None - } - - fn transactions_to_propagate(&self) -> Vec { - let chain_info = self.chain_info(); - self.txqueue.read() - .ready_transactions(chain_info.best_block_number, chain_info.best_block_timestamp) - } -} - -impl AsLightClient for LightProvider { - type Client = L::Client; - - fn as_light_client(&self) -> &L::Client { - self.client.as_light_client() - } -} - -#[cfg(test)] -mod tests { - use ethcore::client::{EachBlockWith, TestBlockChainClient}; - use super::Provider; - - #[test] - fn cht_proof() { - let client = TestBlockChainClient::new(); - client.add_blocks(2000, EachBlockWith::Nothing); - - let req = ::request::CompleteHeaderProofRequest { - num: 1500, - }; - - assert!(client.header_proof(req.clone()).is_none()); - - client.add_blocks(48, EachBlockWith::Nothing); - - assert!(client.header_proof(req.clone()).is_some()); - } -} diff --git a/ethcore/light/src/transaction_queue.rs b/ethcore/light/src/transaction_queue.rs deleted file mode 100644 index 65e646d8466..00000000000 --- a/ethcore/light/src/transaction_queue.rs +++ /dev/null @@ -1,561 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Light Transaction Queue. -//! -//! Manages local transactions, -//! but stores all local transactions, removing only on invalidated nonce. -//! -//! Under the assumption that light nodes will have a relatively limited set of -//! accounts for which they create transactions, this queue is structured in an -//! address-wise manner. - -use std::fmt; -use std::collections::{BTreeMap, HashMap}; -use std::collections::hash_map::Entry; - -use common_types::transaction::{self, Condition, PendingTransaction, SignedTransaction}; -use ethereum_types::{H256, U256, Address}; -use fastmap::H256FastMap; - -// Knowledge of an account's current nonce. -#[derive(Debug, Clone, PartialEq, Eq)] -enum CurrentNonce { - // Assumed current nonce. - Assumed(U256), - // Known current nonce. - Known(U256), -} - -impl CurrentNonce { - // whether this nonce is assumed - fn is_assumed(&self) -> bool { - match *self { - CurrentNonce::Assumed(_) => true, - CurrentNonce::Known(_) => false, - } - } - - // whether this nonce is known for certain from an external source. - fn is_known(&self) -> bool { - !self.is_assumed() - } - - // the current nonce's value. - fn value(&self) -> &U256 { - match *self { - CurrentNonce::Assumed(ref val) => val, - CurrentNonce::Known(ref val) => val, - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -struct TransactionInfo { - hash: H256, - nonce: U256, - condition: Option, -} - -impl<'a> From<&'a PendingTransaction> for TransactionInfo { - fn from(tx: &'a PendingTransaction) -> Self { - TransactionInfo { - hash: tx.hash(), - nonce: tx.nonce, - condition: tx.condition.clone(), - } - } -} - -// transactions associated with a specific account. -#[derive(Debug, Clone, PartialEq, Eq)] -struct AccountTransactions { - // believed current nonce (gotten from initial given TX or `cull` calls). - cur_nonce: CurrentNonce, - current: Vec, // ordered "current" transactions (cur_nonce onwards) - future: BTreeMap, // "future" transactions. -} - -impl AccountTransactions { - fn is_empty(&self) -> bool { - self.current.is_empty() && self.future.is_empty() - } - - fn next_nonce(&self) -> U256 { - self.current.last().map(|last| last.nonce.saturating_add(1.into())) - .unwrap_or_else(|| *self.cur_nonce.value()) - } - - // attempt to move transactions from the future queue into the current queue. - fn adjust_future(&mut self) -> Vec { - let mut promoted = Vec::new(); - let mut next_nonce = self.next_nonce(); - - while let Some(tx) = self.future.remove(&next_nonce) { - promoted.push(tx.hash); - self.current.push(tx); - next_nonce = next_nonce.saturating_add(1.into()); - } - - promoted - } -} - -/// Transaction import result. -pub enum ImportDestination { - /// Transaction has been imported to the current queue. - /// - /// It's going to be propagated to peers. - Current, - /// Transaction has been imported to future queue. - /// - /// It means it won't be propagated until the gap is filled. - Future, -} - -type Listener = Box; - -/// Light transaction queue. See module docs for more details. -#[derive(Default)] -pub struct TransactionQueue { - by_account: HashMap, - by_hash: H256FastMap, - listeners: Vec, -} - -impl fmt::Debug for TransactionQueue { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("TransactionQueue") - .field("by_account", &self.by_account) - .field("by_hash", &self.by_hash) - .field("listeners", &self.listeners.len()) - .finish() - } -} - -impl TransactionQueue { - /// Import a pending transaction to be queued. - pub fn import(&mut self, tx: PendingTransaction) -> Result { - let sender = tx.sender(); - let hash = tx.hash(); - let nonce = tx.nonce; - let tx_info = TransactionInfo::from(&tx); - - if self.by_hash.contains_key(&hash) { return Err(transaction::Error::AlreadyImported) } - - let (res, promoted) = match self.by_account.entry(sender) { - Entry::Vacant(entry) => { - entry.insert(AccountTransactions { - cur_nonce: CurrentNonce::Assumed(nonce), - current: vec![tx_info], - future: BTreeMap::new(), - }); - - (ImportDestination::Current, vec![hash]) - } - Entry::Occupied(mut entry) => { - let acct_txs = entry.get_mut(); - if nonce < *acct_txs.cur_nonce.value() { - // don't accept txs from before known current nonce. - if acct_txs.cur_nonce.is_known() { - return Err(transaction::Error::Old) - } - - // lower our assumption until corrected later. - acct_txs.cur_nonce = CurrentNonce::Assumed(nonce); - } - - match acct_txs.current.binary_search_by(|x| x.nonce.cmp(&nonce)) { - Ok(idx) => { - trace!(target: "txqueue", "Replacing existing transaction from {} with nonce {}", - sender, nonce); - - let old = ::std::mem::replace(&mut acct_txs.current[idx], tx_info); - self.by_hash.remove(&old.hash); - - (ImportDestination::Current, vec![hash]) - } - Err(idx) => { - let cur_len = acct_txs.current.len(); - let incr_nonce = nonce + 1; - - // current is sorted with one tx per nonce, - // so if a tx with given nonce wasn't found that means it is either - // earlier in nonce than all other "current" transactions or later. - assert!(idx == 0 || idx == cur_len); - - if idx == 0 && acct_txs.current.first().map_or(false, |f| f.nonce != incr_nonce) { - let old_cur = ::std::mem::replace(&mut acct_txs.current, vec![tx_info]); - - trace!(target: "txqueue", "Moving {} transactions with nonce > {} to future", - old_cur.len(), incr_nonce); - - for future in old_cur { - let future_nonce = future.nonce; - acct_txs.future.insert(future_nonce, future); - } - - (ImportDestination::Current, vec![hash]) - } else if idx == cur_len && acct_txs.current.last().map_or(false, |f| f.nonce + 1 != nonce) { - trace!(target: "txqueue", "Queued future transaction for {}, nonce={}", sender, nonce); - let future_nonce = nonce; - acct_txs.future.insert(future_nonce, tx_info); - - (ImportDestination::Future, vec![]) - } else { - trace!(target: "txqueue", "Queued current transaction for {}, nonce={}", sender, nonce); - - // insert, then check if we've filled any gaps. - acct_txs.current.insert(idx, tx_info); - let mut promoted = acct_txs.adjust_future(); - promoted.insert(0, hash); - - (ImportDestination::Current, promoted) - } - } - } - } - }; - - self.by_hash.insert(hash, tx); - self.notify(&promoted); - Ok(res) - } - - /// Get pending transaction by hash. - pub fn transaction(&self, hash: &H256) -> Option { - self.by_hash.get(hash).map(|tx| (&**tx).clone()) - } - - /// Get the next nonce for a given address based on what's within the queue. - /// If the address has no queued transactions, then `None` will be returned - /// and the next nonce will have to be deduced via other means. - pub fn next_nonce(&self, address: &Address) -> Option { - self.by_account.get(address).map(AccountTransactions::next_nonce) - } - - /// Get all transactions ready to be propagated. - /// `best_block_number` and `best_block_timestamp` are used to filter out conditionally - /// propagated transactions. - /// - /// Returned transactions are batched by sender, in order of ascending nonce. - pub fn ready_transactions(&self, best_block_number: u64, best_block_timestamp: u64) -> Vec { - self.by_account.values() - .flat_map(|acct_txs| { - acct_txs.current.iter().take_while(|tx| match tx.condition { - None => true, - Some(Condition::Number(blk_num)) => blk_num <= best_block_number, - Some(Condition::Timestamp(time)) => time <= best_block_timestamp, - }).map(|info| info.hash) - }) - .filter_map(|hash| match self.by_hash.get(&hash) { - Some(tx) => Some(tx.clone()), - None => { - warn!(target: "txqueue", "Inconsistency detected between `by_hash` and `by_account`: {} not stored.", - hash); - None - } - }) - .collect() - } - - /// Get all transactions not ready to be propagated. - /// `best_block_number` and `best_block_timestamp` are used to filter out conditionally - /// propagated transactions. - /// - /// Returned transactions are batched by sender, in order of ascending nonce. - pub fn future_transactions(&self, best_block_number: u64, best_block_timestamp: u64) -> Vec { - self.by_account.values() - .flat_map(|acct_txs| { - acct_txs.current.iter().skip_while(|tx| match tx.condition { - None => true, - Some(Condition::Number(blk_num)) => blk_num <= best_block_number, - Some(Condition::Timestamp(time)) => time <= best_block_timestamp, - }).chain(acct_txs.future.values()).map(|info| info.hash) - }) - .filter_map(|hash| match self.by_hash.get(&hash) { - Some(tx) => Some(tx.clone()), - None => { - warn!(target: "txqueue", "Inconsistency detected between `by_hash` and `by_account`: {} not stored.", - hash); - None - } - }) - .collect() - } - - /// Addresses for which we store transactions. - pub fn queued_senders(&self) -> Vec
{ - self.by_account.keys().cloned().collect() - } - - /// Cull out all transactions by the given address which are invalidated by the given nonce. - pub fn cull(&mut self, address: Address, cur_nonce: U256) { - let mut removed_hashes = vec![]; - if let Entry::Occupied(mut entry) = self.by_account.entry(address) { - { - let acct_txs = entry.get_mut(); - acct_txs.cur_nonce = CurrentNonce::Known(cur_nonce); - - // cull old "future" keys. - let old_future: Vec<_> = acct_txs.future.keys().take_while(|&&k| k < cur_nonce).cloned().collect(); - - for old in old_future { - let hash = acct_txs.future.remove(&old) - .expect("key extracted from keys iterator; known to exist; qed") - .hash; - removed_hashes.push(hash); - } - - // then cull from "current". - let valid_pos = acct_txs.current.iter().position(|tx| tx.nonce >= cur_nonce); - match valid_pos { - None => - removed_hashes.extend(acct_txs.current.drain(..).map(|tx| tx.hash)), - Some(valid) => - removed_hashes.extend(acct_txs.current.drain(..valid).map(|tx| tx.hash)), - } - - // now try and move stuff out of future into current. - acct_txs.adjust_future(); - } - - if entry.get_mut().is_empty() { - trace!(target: "txqueue", "No more queued transactions for {} after nonce {}", - address, cur_nonce); - entry.remove(); - } - } - - trace!(target: "txqueue", "Culled {} old transactions from sender {} (nonce={})", - removed_hashes.len(), address, cur_nonce); - - for hash in removed_hashes { - self.by_hash.remove(&hash); - } - } - - /// Get a transaction by hash. - pub fn get(&self, hash: &H256) -> Option<&PendingTransaction> { - self.by_hash.get(&hash) - } - - /// Add a transaction queue listener. - pub fn add_listener(&mut self, f: Listener) { - self.listeners.push(f); - } - - /// Notifies all listeners about new pending transaction. - fn notify(&self, hashes: &[H256]) { - for listener in &self.listeners { - listener(hashes) - } - } -} - -#[cfg(test)] -mod tests { - use super::TransactionQueue; - use ethereum_types::Address; - use common_types::transaction::{Transaction, PendingTransaction, Condition}; - - #[test] - fn queued_senders() { - let sender = Address::default(); - let mut txq = TransactionQueue::default(); - let tx = Transaction::default().fake_sign(sender); - - txq.import(tx.into()).unwrap(); - - assert_eq!(txq.queued_senders(), vec![sender]); - - txq.cull(sender, 1.into()); - - assert_eq!(txq.queued_senders(), vec![]); - assert!(txq.by_hash.is_empty()); - } - - #[test] - fn next_nonce() { - let sender = Address::default(); - let mut txq = TransactionQueue::default(); - - for i in (0..5).chain(10..15) { - let mut tx = Transaction::default(); - tx.nonce = i.into(); - - let tx = tx.fake_sign(sender); - - txq.import(tx.into()).unwrap(); - } - - // current: 0..5, future: 10..15 - assert_eq!(txq.ready_transactions(0, 0).len(), 5); - assert_eq!(txq.next_nonce(&sender).unwrap(), 5.into()); - - txq.cull(sender, 8.into()); - - // current: empty, future: 10..15 - assert_eq!(txq.ready_transactions(0, 0).len(), 0); - assert_eq!(txq.next_nonce(&sender).unwrap(), 8.into()); - - txq.cull(sender, 10.into()); - - // current: 10..15, future: empty - assert_eq!(txq.ready_transactions(0, 0).len(), 5); - assert_eq!(txq.next_nonce(&sender).unwrap(), 15.into()); - } - - #[test] - fn current_to_future() { - let sender = Address::default(); - let mut txq = TransactionQueue::default(); - - for i in 5..10 { - let mut tx = Transaction::default(); - tx.nonce = i.into(); - - let tx = tx.fake_sign(sender); - - txq.import(tx.into()).unwrap(); - } - - assert_eq!(txq.ready_transactions(0, 0).len(), 5); - assert_eq!(txq.next_nonce(&sender).unwrap(), 10.into()); - - for i in 0..3 { - let mut tx = Transaction::default(); - tx.nonce = i.into(); - - let tx = tx.fake_sign(sender); - - txq.import(tx.into()).unwrap(); - } - - assert_eq!(txq.ready_transactions(0, 0).len(), 3); - assert_eq!(txq.next_nonce(&sender).unwrap(), 3.into()); - - for i in 3..5 { - let mut tx = Transaction::default(); - tx.nonce = i.into(); - - let tx = tx.fake_sign(sender); - - txq.import(tx.into()).unwrap(); - } - - assert_eq!(txq.ready_transactions(0, 0).len(), 10); - assert_eq!(txq.next_nonce(&sender).unwrap(), 10.into()); - } - - #[test] - fn conditional() { - let mut txq = TransactionQueue::default(); - let sender = Address::default(); - - for i in 0..5 { - let mut tx = Transaction::default(); - tx.nonce = i.into(); - let tx = tx.fake_sign(sender); - - txq.import(match i { - 3 => PendingTransaction::new(tx, Some(Condition::Number(100))), - 4 => PendingTransaction::new(tx, Some(Condition::Timestamp(1234))), - _ => tx.into(), - }).unwrap(); - } - - assert_eq!(txq.ready_transactions(0, 0).len(), 3); - assert_eq!(txq.ready_transactions(0, 1234).len(), 3); - assert_eq!(txq.ready_transactions(100, 0).len(), 4); - assert_eq!(txq.ready_transactions(100, 1234).len(), 5); - } - - #[test] - fn cull_from_future() { - let sender = Address::default(); - let mut txq = TransactionQueue::default(); - - for i in (0..1).chain(3..10) { - let mut tx = Transaction::default(); - tx.nonce = i.into(); - - let tx = tx.fake_sign(sender); - - txq.import(tx.into()).unwrap(); - } - - txq.cull(sender, 6.into()); - - assert_eq!(txq.ready_transactions(0, 0).len(), 4); - assert_eq!(txq.next_nonce(&sender).unwrap(), 10.into()); - } - - #[test] - fn import_old() { - let sender = Address::default(); - let mut txq = TransactionQueue::default(); - - let mut tx_a = Transaction::default(); - tx_a.nonce = 3.into(); - - let mut tx_b = Transaction::default(); - tx_b.nonce = 2.into(); - - txq.import(tx_a.fake_sign(sender).into()).unwrap(); - txq.cull(sender, 3.into()); - - assert!(txq.import(tx_b.fake_sign(sender).into()).is_err()) - } - - #[test] - fn replace_is_removed() { - let sender = Address::default(); - let mut txq = TransactionQueue::default(); - - let tx_b: PendingTransaction = Transaction::default().fake_sign(sender).into(); - let tx_a: PendingTransaction = { - let mut tx_a = Transaction::default(); - tx_a.gas_price = tx_b.gas_price + 1; - tx_a.fake_sign(sender).into() - }; - - let hash = tx_a.hash(); - - txq.import(tx_a).unwrap(); - txq.import(tx_b).unwrap(); - - assert!(txq.transaction(&hash).is_none()); - } - - #[test] - fn future_transactions() { - let sender = Address::default(); - let mut txq = TransactionQueue::default(); - - for i in (0..1).chain(3..10) { - let mut tx = Transaction::default(); - tx.nonce = i.into(); - - let tx = tx.fake_sign(sender); - - txq.import(tx.into()).unwrap(); - } - - assert_eq!(txq.future_transactions(0, 0).len(), 7); - assert_eq!(txq.next_nonce(&sender).unwrap(), 1.into()); - } -} diff --git a/ethcore/light/src/types/mod.rs b/ethcore/light/src/types/mod.rs deleted file mode 100644 index 702654b7fd2..00000000000 --- a/ethcore/light/src/types/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -pub mod request; diff --git a/ethcore/light/src/types/request/batch.rs b/ethcore/light/src/types/request/batch.rs deleted file mode 100644 index 63641b5daa8..00000000000 --- a/ethcore/light/src/types/request/batch.rs +++ /dev/null @@ -1,317 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Request chain builder utility. -//! Push requests with `push`. Back-references and data required to verify responses must be -//! supplied as well. - -use std::collections::HashMap; -use std::ops::{Deref, DerefMut}; -use request::{ - IncompleteRequest, OutputKind, Output, NoSuchOutput, ResponseError, ResponseLike, -}; - -/// Build chained requests. Push them onto the series with `push`, -/// and produce a `Batch` object with `build`. Outputs are checked for consistency. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Builder { - output_kinds: HashMap<(usize, usize), OutputKind>, - requests: Vec, -} - -impl Default for Builder { - fn default() -> Self { - Builder { - output_kinds: HashMap::new(), - requests: Vec::new(), - } - } -} - -impl Builder { - /// Attempt to push a request onto the request chain. Fails if the request - /// references a non-existent output of a prior request. - pub fn push(&mut self, request: T) -> Result<(), NoSuchOutput> { - request.check_outputs(|req, idx, kind| { - match self.output_kinds.get(&(req, idx)) { - Some(k) if k == &kind => Ok(()), - _ => Err(NoSuchOutput), - } - })?; - let req_idx = self.requests.len(); - request.note_outputs(|idx, kind| { self.output_kinds.insert((req_idx, idx), kind); }); - self.requests.push(request); - Ok(()) - } - - /// Get a reference to the output kinds map. - pub fn output_kinds(&self) -> &HashMap<(usize, usize), OutputKind> { - &self.output_kinds - } - - /// Convert this into a "batch" object. - pub fn build(self) -> Batch { - Batch { - outputs: HashMap::new(), - requests: self.requests, - answered: 0, - } - } -} - -/// Requests pending responses. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Batch { - outputs: HashMap<(usize, usize), Output>, - requests: Vec, - answered: usize, -} - -impl Batch { - /// Get access to the underlying slice of requests. - // TODO: unimplemented -> Vec, // do we _have to_ allocate? - pub fn requests(&self) -> &[T] { &self.requests } - - /// Get the number of answered requests. - pub fn num_answered(&self) -> usize { self.answered } - - /// Whether the batch is complete. - pub fn is_complete(&self) -> bool { - self.answered == self.requests.len() - } - - /// Map requests from one type into another. - pub fn map_requests(self, f: F) -> Batch - where F: FnMut(T) -> U, U: IncompleteRequest - { - Batch { - outputs: self.outputs, - requests: self.requests.into_iter().map(f).collect(), - answered: self.answered, - } - } -} - -impl Batch { - /// Get the next request as a filled request. Returns `None` when all requests answered. - pub fn next_complete(&self) -> Option { - if self.is_complete() { - None - } else { - Some(self.requests[self.answered].clone() - .complete() - .expect("All outputs checked as invariant of `Batch` object; qed")) - } - } - - /// Sweep through all unanswered requests, filling them as necessary. - pub fn fill_unanswered(&mut self) { - let outputs = &mut self.outputs; - - for req in self.requests.iter_mut().skip(self.answered) { - req.fill(|req_idx, out_idx| outputs.get(&(req_idx, out_idx)).cloned().ok_or(NoSuchOutput)) - } - } - - /// Supply a response, asserting its correctness. - /// Fill outputs based upon it. - pub fn supply_response_unchecked(&mut self, response: &R) { - if self.is_complete() { return } - - let outputs = &mut self.outputs; - let idx = self.answered; - response.fill_outputs(|out_idx, output| { - // we don't need to check output kinds here because all back-references - // are validated in the builder. - // TODO: optimization for only storing outputs we "care about"? - outputs.insert((idx, out_idx), output); - }); - - self.answered += 1; - - // fill as much of the next request as we can. - if let Some(ref mut req) = self.requests.get_mut(self.answered) { - req.fill(|req_idx, out_idx| outputs.get(&(req_idx, out_idx)).cloned().ok_or(NoSuchOutput)) - } - } -} - -impl Batch { - /// Supply a response for the next request. - /// Fails on: wrong request kind, all requests answered already. - pub fn supply_response(&mut self, env: &T::Environment, response: &T::Response) - -> Result> - { - let idx = self.answered; - - // check validity. - if idx == self.requests.len() { return Err(ResponseError::Unexpected) } - let completed = self.next_complete() - .expect("only fails when all requests have been answered; this just checked against; qed"); - - let extracted = self.requests[idx] - .check_response(&completed, env, response).map_err(ResponseError::Validity)?; - - self.supply_response_unchecked(response); - Ok(extracted) - } -} - -impl Batch { - /// For each request, produce a response. - /// The responses vector produced goes up to the point where the responder - /// first returns `None`, an invalid response, or until all requests have been responded to. - pub fn respond_to_all(mut self, responder: F) -> Vec - where F: Fn(super::CompleteRequest) -> Option - { - let mut responses = Vec::new(); - - while let Some(response) = self.next_complete().and_then(&responder) { - match self.supply_response(&(), &response) { - Ok(()) => responses.push(response), - Err(e) => { - debug!(target: "pip", "produced bad response to request: {:?}", e); - return responses; - } - } - } - - responses - } -} - -impl Deref for Batch { - type Target = [T]; - - fn deref(&self) -> &[T] { - &self.requests[..] - } -} - -impl DerefMut for Batch { - fn deref_mut(&mut self) -> &mut [T] { - &mut self.requests[..] - } -} - -#[cfg(test)] -mod tests { - use request::*; - use super::Builder; - use ethereum_types::H256; - - #[test] - fn all_scalar() { - let mut builder = Builder::default(); - builder.push(Request::HeaderProof(IncompleteHeaderProofRequest { - num: 100.into(), - })).unwrap(); - builder.push(Request::Receipts(IncompleteReceiptsRequest { - hash: H256::default().into(), - })).unwrap(); - } - - #[test] - #[should_panic] - fn missing_backref() { - let mut builder = Builder::default(); - builder.push(Request::HeaderProof(IncompleteHeaderProofRequest { - num: Field::BackReference(100, 3), - })).unwrap(); - } - - #[test] - #[should_panic] - fn wrong_kind() { - let mut builder = Builder::default(); - assert!(builder.push(Request::HeaderProof(IncompleteHeaderProofRequest { - num: 100.into(), - })).is_ok()); - builder.push(Request::HeaderProof(IncompleteHeaderProofRequest { - num: Field::BackReference(0, 0), - })).unwrap(); - } - - #[test] - fn good_backreference() { - let mut builder = Builder::default(); - builder.push(Request::HeaderProof(IncompleteHeaderProofRequest { - num: 100.into(), // header proof puts hash at output 0. - })).unwrap(); - builder.push(Request::Receipts(IncompleteReceiptsRequest { - hash: Field::BackReference(0, 0), - })).unwrap(); - } - - #[test] - fn batch_tx_index_backreference() { - let mut builder = Builder::default(); - builder.push(Request::HeaderProof(IncompleteHeaderProofRequest { - num: 100.into(), // header proof puts hash at output 0. - })).unwrap(); - builder.push(Request::TransactionIndex(IncompleteTransactionIndexRequest { - hash: Field::BackReference(0, 0), - })).unwrap(); - - let mut batch = builder.build(); - batch.requests[1].fill(|_req_idx, _out_idx| Ok(Output::Hash(42.into()))); - - assert!(batch.next_complete().is_some()); - batch.answered += 1; - assert!(batch.next_complete().is_some()); - } - - #[test] - fn batch_tx_index_backreference_public_api() { - let mut builder = Builder::default(); - builder.push(Request::HeaderProof(IncompleteHeaderProofRequest { - num: 100.into(), // header proof puts hash at output 0. - })).unwrap(); - builder.push(Request::TransactionIndex(IncompleteTransactionIndexRequest { - hash: Field::BackReference(0, 0), - })).unwrap(); - - let mut batch = builder.build(); - - assert!(batch.next_complete().is_some()); - let hdr_proof_res = header_proof::Response { - proof: vec![], - hash: 12.into(), - td: 21.into(), - }; - batch.supply_response_unchecked(&hdr_proof_res); - - assert!(batch.next_complete().is_some()); - } - - #[test] - fn batch_receipts_backreference() { - let mut builder = Builder::default(); - builder.push(Request::HeaderProof(IncompleteHeaderProofRequest { - num: 100.into(), // header proof puts hash at output 0. - })).unwrap(); - builder.push(Request::Receipts(IncompleteReceiptsRequest { - hash: Field::BackReference(0, 0), - })).unwrap(); - - let mut batch = builder.build(); - batch.requests[1].fill(|_req_idx, _out_idx| Ok(Output::Hash(42.into()))); - - assert!(batch.next_complete().is_some()); - batch.answered += 1; - assert!(batch.next_complete().is_some()); - } -} diff --git a/ethcore/light/src/types/request/mod.rs b/ethcore/light/src/types/request/mod.rs deleted file mode 100644 index cacfbcbe508..00000000000 --- a/ethcore/light/src/types/request/mod.rs +++ /dev/null @@ -1,1938 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Light protocol request types. - -use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp}; -use ethereum_types::H256; - -mod batch; - -// re-exports of request types. -pub use self::header::{ - Complete as CompleteHeadersRequest, - Incomplete as IncompleteHeadersRequest, - Response as HeadersResponse -}; -pub use self::header_proof::{ - Complete as CompleteHeaderProofRequest, - Incomplete as IncompleteHeaderProofRequest, - Response as HeaderProofResponse -}; -pub use self::transaction_index::{ - Complete as CompleteTransactionIndexRequest, - Incomplete as IncompleteTransactionIndexRequest, - Response as TransactionIndexResponse -}; -pub use self::block_body::{ - Complete as CompleteBodyRequest, - Incomplete as IncompleteBodyRequest, - Response as BodyResponse -}; -pub use self::block_receipts::{ - Complete as CompleteReceiptsRequest, - Incomplete as IncompleteReceiptsRequest, - Response as ReceiptsResponse -}; -pub use self::account::{ - Complete as CompleteAccountRequest, - Incomplete as IncompleteAccountRequest, - Response as AccountResponse, -}; -pub use self::storage::{ - Complete as CompleteStorageRequest, - Incomplete as IncompleteStorageRequest, - Response as StorageResponse -}; -pub use self::contract_code::{ - Complete as CompleteCodeRequest, - Incomplete as IncompleteCodeRequest, - Response as CodeResponse, -}; -pub use self::execution::{ - Complete as CompleteExecutionRequest, - Incomplete as IncompleteExecutionRequest, - Response as ExecutionResponse, -}; -pub use self::epoch_signal::{ - Complete as CompleteSignalRequest, - Incomplete as IncompleteSignalRequest, - Response as SignalResponse, -}; - -pub use self::batch::{Batch, Builder}; - -/// Error indicating a reference to a non-existent or wrongly-typed output. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct NoSuchOutput; - -/// Wrong kind of response corresponding to request. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct WrongKind; - -/// Error on processing a response. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ResponseError { - /// Error in validity. - Validity(T), - /// No responses expected. - Unexpected, -} - -/// An input to a request. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Field { - /// A pre-specified input. - Scalar(T), - /// An input which can be resolved later on. - /// (Request index, output index) - BackReference(usize, usize), -} - -impl Field { - /// Helper for creating a new back-reference field. - pub fn back_ref(idx: usize, req: usize) -> Self { - Field::BackReference(idx, req) - } - - /// map a scalar into some other item. - pub fn map(self, f: F) -> Field where F: FnOnce(T) -> U { - match self { - Field::Scalar(x) => Field::Scalar(f(x)), - Field::BackReference(req, idx) => Field::BackReference(req, idx), - } - } - - /// Attempt to get a reference to the inner scalar. - pub fn as_ref(&self) -> Option<&T> { - match *self { - Field::Scalar(ref x) => Some(x), - Field::BackReference(_, _) => None, - } - } - - // attempt conversion into scalar value. - fn into_scalar(self) -> Result { - match self { - Field::Scalar(val) => Ok(val), - _ => Err(NoSuchOutput), - } - } - - fn adjust_req(&mut self, mut mapping: F) where F: FnMut(usize) -> usize { - if let Field::BackReference(ref mut req_idx, _) = *self { - *req_idx = mapping(*req_idx) - } - } -} - -impl From for Field { - fn from(val: T) -> Self { - Field::Scalar(val) - } -} - -impl Decodable for Field { - fn decode(rlp: &Rlp) -> Result { - match rlp.val_at::(0)? { - 0 => Ok(Field::Scalar(rlp.val_at::(1)?)), - 1 => Ok({ - let inner_rlp = rlp.at(1)?; - Field::BackReference(inner_rlp.val_at(0)?, inner_rlp.val_at(1)?) - }), - _ => Err(DecoderError::Custom("Unknown discriminant for PIP field.")), - } - } -} - -impl Encodable for Field { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(2); - match *self { - Field::Scalar(ref data) => { - s.append(&0u8).append(data); - } - Field::BackReference(ref req, ref idx) => { - s.append(&1u8).begin_list(2).append(req).append(idx); - } - } - } -} - -/// Request outputs which can be reused as inputs. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum Output { - /// A 32-byte hash output. - Hash(H256), - /// An unsigned-integer output. - Number(u64), -} - -impl Output { - /// Get the output kind. - pub fn kind(&self) -> OutputKind { - match *self { - Output::Hash(_) => OutputKind::Hash, - Output::Number(_) => OutputKind::Number, - } - } -} - -/// Response output kinds which can be used as back-references. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum OutputKind { - /// A 32-byte hash output. - Hash, - /// An unsigned-integer output. - Number, -} - -/// Either a hash or a number. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum HashOrNumber { - /// Block hash variant. - Hash(H256), - /// Block number variant. - Number(u64), -} - -impl From for HashOrNumber { - fn from(hash: H256) -> Self { - HashOrNumber::Hash(hash) - } -} - -impl From for HashOrNumber { - fn from(num: u64) -> Self { - HashOrNumber::Number(num) - } -} - -impl Decodable for HashOrNumber { - fn decode(rlp: &Rlp) -> Result { - rlp.as_val::().map(HashOrNumber::Hash) - .or_else(|_| rlp.as_val().map(HashOrNumber::Number)) - } -} - -impl Encodable for HashOrNumber { - fn rlp_append(&self, s: &mut RlpStream) { - match *self { - HashOrNumber::Hash(ref hash) => s.append(hash), - HashOrNumber::Number(ref num) => s.append(num), - }; - } -} - -/// Type alias for "network requests". -pub type NetworkRequests = Batch; - -/// All request types, as they're sent over the network. -/// They may be incomplete, with back-references to outputs -/// of prior requests. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum Request { - /// A request for block headers. - Headers(IncompleteHeadersRequest), - /// A request for a header proof (from a CHT) - HeaderProof(IncompleteHeaderProofRequest), - /// A request for a transaction index by hash. - TransactionIndex(IncompleteTransactionIndexRequest), - /// A request for a block's receipts. - Receipts(IncompleteReceiptsRequest), - /// A request for a block body. - Body(IncompleteBodyRequest), - /// A request for a merkle proof of an account. - Account(IncompleteAccountRequest), - /// A request for a merkle proof of contract storage. - Storage(IncompleteStorageRequest), - /// A request for contract code. - Code(IncompleteCodeRequest), - /// A request for proof of execution, - Execution(IncompleteExecutionRequest), - /// A request for an epoch signal. - Signal(IncompleteSignalRequest), -} - -/// All request types, in an answerable state. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum CompleteRequest { - /// A request for block headers. - Headers(CompleteHeadersRequest), - /// A request for a header proof (from a CHT) - HeaderProof(CompleteHeaderProofRequest), - /// A request for a transaction index by hash. - TransactionIndex(CompleteTransactionIndexRequest), - /// A request for a block's receipts. - Receipts(CompleteReceiptsRequest), - /// A request for a block body. - Body(CompleteBodyRequest), - /// A request for a merkle proof of an account. - Account(CompleteAccountRequest), - /// A request for a merkle proof of contract storage. - Storage(CompleteStorageRequest), - /// A request for contract code. - Code(CompleteCodeRequest), - /// A request for proof of execution, - Execution(CompleteExecutionRequest), - /// A request for an epoch signal. - Signal(CompleteSignalRequest), -} - -impl CompleteRequest { - /// Inspect the kind of this response. - pub fn kind(&self) -> Kind { - match *self { - CompleteRequest::Headers(_) => Kind::Headers, - CompleteRequest::HeaderProof(_) => Kind::HeaderProof, - CompleteRequest::TransactionIndex(_) => Kind::TransactionIndex, - CompleteRequest::Receipts(_) => Kind::Receipts, - CompleteRequest::Body(_) => Kind::Body, - CompleteRequest::Account(_) => Kind::Account, - CompleteRequest::Storage(_) => Kind::Storage, - CompleteRequest::Code(_) => Kind::Code, - CompleteRequest::Execution(_) => Kind::Execution, - CompleteRequest::Signal(_) => Kind::Signal, - } - } -} - -impl Request { - /// Get the request kind. - pub fn kind(&self) -> Kind { - match *self { - Request::Headers(_) => Kind::Headers, - Request::HeaderProof(_) => Kind::HeaderProof, - Request::TransactionIndex(_) => Kind::TransactionIndex, - Request::Receipts(_) => Kind::Receipts, - Request::Body(_) => Kind::Body, - Request::Account(_) => Kind::Account, - Request::Storage(_) => Kind::Storage, - Request::Code(_) => Kind::Code, - Request::Execution(_) => Kind::Execution, - Request::Signal(_) => Kind::Signal, - } - } -} - -impl Decodable for Request { - fn decode(rlp: &Rlp) -> Result { - match rlp.val_at::(0)? { - Kind::Headers => Ok(Request::Headers(rlp.val_at(1)?)), - Kind::HeaderProof => Ok(Request::HeaderProof(rlp.val_at(1)?)), - Kind::TransactionIndex => Ok(Request::TransactionIndex(rlp.val_at(1)?)), - Kind::Receipts => Ok(Request::Receipts(rlp.val_at(1)?)), - Kind::Body => Ok(Request::Body(rlp.val_at(1)?)), - Kind::Account => Ok(Request::Account(rlp.val_at(1)?)), - Kind::Storage => Ok(Request::Storage(rlp.val_at(1)?)), - Kind::Code => Ok(Request::Code(rlp.val_at(1)?)), - Kind::Execution => Ok(Request::Execution(rlp.val_at(1)?)), - Kind::Signal => Ok(Request::Signal(rlp.val_at(1)?)), - } - } -} - -impl Encodable for Request { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(2); - - // hack around https://github.com/paritytech/parity-ethereum/issues/4356 - Encodable::rlp_append(&self.kind(), s); - - match *self { - Request::Headers(ref req) => s.append(req), - Request::HeaderProof(ref req) => s.append(req), - Request::TransactionIndex(ref req) => s.append(req), - Request::Receipts(ref req) => s.append(req), - Request::Body(ref req) => s.append(req), - Request::Account(ref req) => s.append(req), - Request::Storage(ref req) => s.append(req), - Request::Code(ref req) => s.append(req), - Request::Execution(ref req) => s.append(req), - Request::Signal(ref req) => s.append(req), - }; - } -} - -impl IncompleteRequest for Request { - type Complete = CompleteRequest; - type Response = Response; - - fn check_outputs(&self, f: F) -> Result<(), NoSuchOutput> - where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> - { - match *self { - Request::Headers(ref req) => req.check_outputs(f), - Request::HeaderProof(ref req) => req.check_outputs(f), - Request::TransactionIndex(ref req) => req.check_outputs(f), - Request::Receipts(ref req) => req.check_outputs(f), - Request::Body(ref req) => req.check_outputs(f), - Request::Account(ref req) => req.check_outputs(f), - Request::Storage(ref req) => req.check_outputs(f), - Request::Code(ref req) => req.check_outputs(f), - Request::Execution(ref req) => req.check_outputs(f), - Request::Signal(ref req) => req.check_outputs(f), - } - } - - fn note_outputs(&self, f: F) where F: FnMut(usize, OutputKind) { - match *self { - Request::Headers(ref req) => req.note_outputs(f), - Request::HeaderProof(ref req) => req.note_outputs(f), - Request::TransactionIndex(ref req) => req.note_outputs(f), - Request::Receipts(ref req) => req.note_outputs(f), - Request::Body(ref req) => req.note_outputs(f), - Request::Account(ref req) => req.note_outputs(f), - Request::Storage(ref req) => req.note_outputs(f), - Request::Code(ref req) => req.note_outputs(f), - Request::Execution(ref req) => req.note_outputs(f), - Request::Signal(ref req) => req.note_outputs(f), - } - } - - fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { - match *self { - Request::Headers(ref mut req) => req.fill(oracle), - Request::HeaderProof(ref mut req) => req.fill(oracle), - Request::TransactionIndex(ref mut req) => req.fill(oracle), - Request::Receipts(ref mut req) => req.fill(oracle), - Request::Body(ref mut req) => req.fill(oracle), - Request::Account(ref mut req) => req.fill(oracle), - Request::Storage(ref mut req) => req.fill(oracle), - Request::Code(ref mut req) => req.fill(oracle), - Request::Execution(ref mut req) => req.fill(oracle), - Request::Signal(ref mut req) => req.fill(oracle), - } - } - - fn complete(self) -> Result { - match self { - Request::Headers(req) => req.complete().map(CompleteRequest::Headers), - Request::HeaderProof(req) => req.complete().map(CompleteRequest::HeaderProof), - Request::TransactionIndex(req) => req.complete().map(CompleteRequest::TransactionIndex), - Request::Receipts(req) => req.complete().map(CompleteRequest::Receipts), - Request::Body(req) => req.complete().map(CompleteRequest::Body), - Request::Account(req) => req.complete().map(CompleteRequest::Account), - Request::Storage(req) => req.complete().map(CompleteRequest::Storage), - Request::Code(req) => req.complete().map(CompleteRequest::Code), - Request::Execution(req) => req.complete().map(CompleteRequest::Execution), - Request::Signal(req) => req.complete().map(CompleteRequest::Signal), - } - } - - fn adjust_refs(&mut self, mapping: F) where F: FnMut(usize) -> usize { - match *self { - Request::Headers(ref mut req) => req.adjust_refs(mapping), - Request::HeaderProof(ref mut req) => req.adjust_refs(mapping), - Request::TransactionIndex(ref mut req) => req.adjust_refs(mapping), - Request::Receipts(ref mut req) => req.adjust_refs(mapping), - Request::Body(ref mut req) => req.adjust_refs(mapping), - Request::Account(ref mut req) => req.adjust_refs(mapping), - Request::Storage(ref mut req) => req.adjust_refs(mapping), - Request::Code(ref mut req) => req.adjust_refs(mapping), - Request::Execution(ref mut req) => req.adjust_refs(mapping), - Request::Signal(ref mut req) => req.adjust_refs(mapping), - } - } -} - -impl CheckedRequest for Request { - type Extract = (); - type Error = WrongKind; - type Environment = (); - - fn check_response(&self, _: &Self::Complete, _: &(), response: &Response) -> Result<(), WrongKind> { - if self.kind() == response.kind() { - Ok(()) - } else { - Err(WrongKind) - } - } -} - -/// Kinds of requests. -/// Doubles as the "ID" field of the request. -#[repr(u8)] -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)] -pub enum Kind { - /// A request for headers. - Headers = 0, - /// A request for a header proof. - HeaderProof = 1, - /// A request for a transaction index. - TransactionIndex = 2, - /// A request for block receipts. - Receipts = 3, - /// A request for a block body. - Body = 4, - /// A request for an account + merkle proof. - Account = 5, - /// A request for contract storage + merkle proof - Storage = 6, - /// A request for contract. - Code = 7, - /// A request for transaction execution + state proof. - Execution = 8, - /// A request for epoch transition signal. - Signal = 9, -} - -impl Decodable for Kind { - fn decode(rlp: &Rlp) -> Result { - match rlp.as_val::()? { - 0 => Ok(Kind::Headers), - 1 => Ok(Kind::HeaderProof), - 2 => Ok(Kind::TransactionIndex), - 3 => Ok(Kind::Receipts), - 4 => Ok(Kind::Body), - 5 => Ok(Kind::Account), - 6 => Ok(Kind::Storage), - 7 => Ok(Kind::Code), - 8 => Ok(Kind::Execution), - 9 => Ok(Kind::Signal), - _ => Err(DecoderError::Custom("Unknown PIP request ID.")), - } - } -} - -impl Encodable for Kind { - fn rlp_append(&self, s: &mut RlpStream) { - s.append(&(*self as u8)); - } -} - -/// All response types. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum Response { - /// A response for block headers. - Headers(HeadersResponse), - /// A response for a header proof (from a CHT) - HeaderProof(HeaderProofResponse), - /// A response for a transaction index. - TransactionIndex(TransactionIndexResponse), - /// A response for a block's receipts. - Receipts(ReceiptsResponse), - /// A response for a block body. - Body(BodyResponse), - /// A response for a merkle proof of an account. - Account(AccountResponse), - /// A response for a merkle proof of contract storage. - Storage(StorageResponse), - /// A response for contract code. - Code(CodeResponse), - /// A response for proof of execution, - Execution(ExecutionResponse), - /// A response for epoch change signal. - Signal(SignalResponse), -} - -impl ResponseLike for Response { - /// Fill reusable outputs by writing them into the function. - fn fill_outputs(&self, f: F) where F: FnMut(usize, Output) { - match *self { - Response::Headers(ref res) => res.fill_outputs(f), - Response::HeaderProof(ref res) => res.fill_outputs(f), - Response::TransactionIndex(ref res) => res.fill_outputs(f), - Response::Receipts(ref res) => res.fill_outputs(f), - Response::Body(ref res) => res.fill_outputs(f), - Response::Account(ref res) => res.fill_outputs(f), - Response::Storage(ref res) => res.fill_outputs(f), - Response::Code(ref res) => res.fill_outputs(f), - Response::Execution(ref res) => res.fill_outputs(f), - Response::Signal(ref res) => res.fill_outputs(f), - } - } -} - -impl Response { - /// Inspect the kind of this response. - pub fn kind(&self) -> Kind { - match *self { - Response::Headers(_) => Kind::Headers, - Response::HeaderProof(_) => Kind::HeaderProof, - Response::TransactionIndex(_) => Kind::TransactionIndex, - Response::Receipts(_) => Kind::Receipts, - Response::Body(_) => Kind::Body, - Response::Account(_) => Kind::Account, - Response::Storage(_) => Kind::Storage, - Response::Code(_) => Kind::Code, - Response::Execution(_) => Kind::Execution, - Response::Signal(_) => Kind::Signal, - } - } -} - -impl Decodable for Response { - fn decode(rlp: &Rlp) -> Result { - match rlp.val_at::(0)? { - Kind::Headers => Ok(Response::Headers(rlp.val_at(1)?)), - Kind::HeaderProof => Ok(Response::HeaderProof(rlp.val_at(1)?)), - Kind::TransactionIndex => Ok(Response::TransactionIndex(rlp.val_at(1)?)), - Kind::Receipts => Ok(Response::Receipts(rlp.val_at(1)?)), - Kind::Body => Ok(Response::Body(rlp.val_at(1)?)), - Kind::Account => Ok(Response::Account(rlp.val_at(1)?)), - Kind::Storage => Ok(Response::Storage(rlp.val_at(1)?)), - Kind::Code => Ok(Response::Code(rlp.val_at(1)?)), - Kind::Execution => Ok(Response::Execution(rlp.val_at(1)?)), - Kind::Signal => Ok(Response::Signal(rlp.val_at(1)?)), - } - } -} - -impl Encodable for Response { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(2); - - // hack around https://github.com/paritytech/parity-ethereum/issues/4356 - Encodable::rlp_append(&self.kind(), s); - - match *self { - Response::Headers(ref res) => s.append(res), - Response::HeaderProof(ref res) => s.append(res), - Response::TransactionIndex(ref res) => s.append(res), - Response::Receipts(ref res) => s.append(res), - Response::Body(ref res) => s.append(res), - Response::Account(ref res) => s.append(res), - Response::Storage(ref res) => s.append(res), - Response::Code(ref res) => s.append(res), - Response::Execution(ref res) => s.append(res), - Response::Signal(ref res) => s.append(res), - }; - } -} - -/// A potentially incomplete request. -pub trait IncompleteRequest: Sized { - /// The complete variant of this request. - type Complete; - /// The response to this request. - type Response: ResponseLike; - - /// Check prior outputs against the needed inputs. - /// - /// This is called to ensure consistency of this request with - /// others in the same packet. - fn check_outputs(&self, f: F) -> Result<(), NoSuchOutput> - where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>; - - /// Note that this request will produce the following outputs. - fn note_outputs(&self, f: F) where F: FnMut(usize, OutputKind); - - /// Fill fields of the request. - /// - /// This function is provided an "output oracle" which allows fetching of - /// prior request outputs. - /// Only outputs previously checked with `check_outputs` may be available. - fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result; - - /// Attempt to convert this request into its complete variant. - /// Will succeed if all fields have been filled, will fail otherwise. - fn complete(self) -> Result; - - /// Adjust back-reference request indices. - fn adjust_refs(&mut self, mapping: F) where F: FnMut(usize) -> usize; -} - -/// A request which can be checked against its response for more validity. -pub trait CheckedRequest: IncompleteRequest { - /// Data extracted during the check. - type Extract; - /// Error encountered during the check. - type Error; - /// Environment passed to response check. - type Environment; - - /// Check whether the response matches (beyond the type). - fn check_response(&self, &Self::Complete, &Self::Environment, &Self::Response) -> Result; -} - -/// A response-like object. -/// -/// These contain re-usable outputs. -pub trait ResponseLike { - /// Write all re-usable outputs into the provided function. - fn fill_outputs(&self, output_store: F) where F: FnMut(usize, Output); -} - -/// Header request. -pub mod header { - use super::{Field, HashOrNumber, NoSuchOutput, OutputKind, Output}; - use common_types::encoded; - use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp}; - - /// Potentially incomplete headers request. - #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] - pub struct Incomplete { - /// Start block. - pub start: Field, - /// Skip between. - pub skip: u64, - /// Maximum to return. - pub max: u64, - /// Whether to reverse from start. - pub reverse: bool, - } - - impl super::IncompleteRequest for Incomplete { - type Complete = Complete; - type Response = Response; - - fn check_outputs(&self, mut f: F) -> Result<(), NoSuchOutput> - where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> - { - match self.start { - Field::Scalar(_) => Ok(()), - Field::BackReference(req, idx) => - f(req, idx, OutputKind::Hash).or_else(|_| f(req, idx, OutputKind::Number)) - } - } - - fn note_outputs(&self, _: F) where F: FnMut(usize, OutputKind) { } - - fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { - if let Field::BackReference(req, idx) = self.start { - self.start = match oracle(req, idx) { - Ok(Output::Hash(hash)) => Field::Scalar(hash.into()), - Ok(Output::Number(num)) => Field::Scalar(num.into()), - Err(_) => Field::BackReference(req, idx), - } - } - } - - fn complete(self) -> Result { - Ok(Complete { - start: self.start.into_scalar()?, - skip: self.skip, - max: self.max, - reverse: self.reverse, - }) - } - - fn adjust_refs(&mut self, mapping: F) where F: FnMut(usize) -> usize { - self.start.adjust_req(mapping) - } - } - - /// A complete header request. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Complete { - /// Start block. - pub start: HashOrNumber, - /// Skip between. - pub skip: u64, - /// Maximum to return. - pub max: u64, - /// Whether to reverse from start. - pub reverse: bool, - } - - /// The output of a request for headers. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Response { - /// The headers requested. - pub headers: Vec, - } - - impl super::ResponseLike for Response { - /// Fill reusable outputs by writing them into the function. - fn fill_outputs(&self, _: F) where F: FnMut(usize, Output) { } - } - - impl Decodable for Response { - fn decode(rlp: &Rlp) -> Result { - use common_types::header::Header as FullHeader; - - let mut headers = Vec::new(); - - for item in rlp.iter() { - // check that it's a valid encoding. - // TODO: just return full headers here? - let _: FullHeader = item.as_val()?; - headers.push(encoded::Header::new(item.as_raw().to_owned())); - } - - Ok(Response { headers }) - } - } - - impl Encodable for Response { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(self.headers.len()); - for header in &self.headers { - s.append_raw(header.rlp().as_raw(), 1); - } - } - } -} - -/// Request and response for header proofs. -pub mod header_proof { - use super::{Field, NoSuchOutput, OutputKind, Output}; - use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp}; - use ethereum_types::{H256, U256}; - use bytes::Bytes; - - /// Potentially incomplete header proof request. - #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] - pub struct Incomplete { - /// Block number. - pub num: Field, - } - - impl super::IncompleteRequest for Incomplete { - type Complete = Complete; - type Response = Response; - - fn check_outputs(&self, mut f: F) -> Result<(), NoSuchOutput> - where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> - { - match self.num { - Field::Scalar(_) => Ok(()), - Field::BackReference(req, idx) => f(req, idx, OutputKind::Number), - } - } - - fn note_outputs(&self, mut note: F) where F: FnMut(usize, OutputKind) { - note(0, OutputKind::Hash); - } - - fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { - if let Field::BackReference(req, idx) = self.num { - self.num = match oracle(req, idx) { - Ok(Output::Number(num)) => Field::Scalar(num), - _ => Field::BackReference(req, idx), - } - } - } - - fn complete(self) -> Result { - Ok(Complete { - num: self.num.into_scalar()?, - }) - } - - fn adjust_refs(&mut self, mapping: F) where F: FnMut(usize) -> usize { - self.num.adjust_req(mapping) - } - } - - /// A complete header proof request. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Complete { - /// The number to get a header proof for. - pub num: u64, - } - - /// The output of a request for a header proof. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Response { - /// Inclusion proof of the header and total difficulty in the CHT. - pub proof: Vec, - /// The proved header's hash. - pub hash: H256, - /// The proved header's total difficulty. - pub td: U256, - } - - impl super::ResponseLike for Response { - /// Fill reusable outputs by providing them to the function. - fn fill_outputs(&self, mut f: F) where F: FnMut(usize, Output) { - f(0, Output::Hash(self.hash)); - } - } - - impl Decodable for Response { - fn decode(rlp: &Rlp) -> Result { - Ok(Response { - proof: rlp.list_at(0)?, - hash: rlp.val_at(1)?, - td: rlp.val_at(2)?, - }) - } - } - - impl Encodable for Response { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(3) - .append_list::,_>(&self.proof[..]) - .append(&self.hash) - .append(&self.td); - } - } -} - -/// Request and response for transaction index. -pub mod transaction_index { - use super::{Field, NoSuchOutput, OutputKind, Output}; - use ethereum_types::H256; - - /// Potentially incomplete transaction index request. - #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] - pub struct Incomplete { - /// Transaction hash to get index for. - pub hash: Field, - } - - impl super::IncompleteRequest for Incomplete { - type Complete = Complete; - type Response = Response; - - fn check_outputs(&self, mut f: F) -> Result<(), NoSuchOutput> - where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> - { - match self.hash { - Field::Scalar(_) => Ok(()), - Field::BackReference(req, idx) => f(req, idx, OutputKind::Hash), - } - } - - fn note_outputs(&self, mut f: F) where F: FnMut(usize, OutputKind) { - f(0, OutputKind::Number); - f(1, OutputKind::Hash); - } - - fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { - if let Field::BackReference(req, idx) = self.hash { - self.hash = match oracle(req, idx) { - Ok(Output::Hash(hash)) => Field::Scalar(hash.into()), - _ => Field::BackReference(req, idx), - } - } - } - - fn complete(self) -> Result { - Ok(Complete { - hash: self.hash.into_scalar()?, - }) - } - - fn adjust_refs(&mut self, mapping: F) where F: FnMut(usize) -> usize { - self.hash.adjust_req(mapping) - } - } - - /// A complete transaction index request. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Complete { - /// The transaction hash to get index for. - pub hash: H256, - } - - /// The output of a request for transaction index. - #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] - pub struct Response { - /// Block number. - pub num: u64, - /// Block hash - pub hash: H256, - /// Index in block. - pub index: u64, - } - - impl super::ResponseLike for Response { - /// Fill reusable outputs by providing them to the function. - fn fill_outputs(&self, mut f: F) where F: FnMut(usize, Output) { - f(0, Output::Number(self.num)); - f(1, Output::Hash(self.hash)); - } - } -} - -/// Request and response for block receipts -pub mod block_receipts { - use super::{Field, NoSuchOutput, OutputKind, Output}; - use common_types::receipt::Receipt; - use ethereum_types::H256; - - /// Potentially incomplete block receipts request. - #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] - pub struct Incomplete { - /// Block hash to get receipts for. - pub hash: Field, - } - - impl super::IncompleteRequest for Incomplete { - type Complete = Complete; - type Response = Response; - - fn check_outputs(&self, mut f: F) -> Result<(), NoSuchOutput> - where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> - { - match self.hash { - Field::Scalar(_) => Ok(()), - Field::BackReference(req, idx) => f(req, idx, OutputKind::Hash), - } - } - - fn note_outputs(&self, _: F) where F: FnMut(usize, OutputKind) {} - - fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { - if let Field::BackReference(req, idx) = self.hash { - self.hash = match oracle(req, idx) { - Ok(Output::Hash(hash)) => Field::Scalar(hash.into()), - _ => Field::BackReference(req, idx), - } - } - } - - fn complete(self) -> Result { - Ok(Complete { - hash: self.hash.into_scalar()?, - }) - } - - fn adjust_refs(&mut self, mapping: F) where F: FnMut(usize) -> usize { - self.hash.adjust_req(mapping) - } - } - - /// A complete block receipts request. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Complete { - /// The number to get block receipts for. - pub hash: H256, - } - - /// The output of a request for block receipts. - #[derive(Debug, Clone, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper)] - pub struct Response { - /// The block receipts. - pub receipts: Vec - } - - impl super::ResponseLike for Response { - /// Fill reusable outputs by providing them to the function. - fn fill_outputs(&self, _: F) where F: FnMut(usize, Output) {} - } -} - -/// Request and response for a block body -pub mod block_body { - use super::{Field, NoSuchOutput, OutputKind, Output}; - use common_types::encoded; - use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp}; - use ethereum_types::H256; - - /// Potentially incomplete block body request. - #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] - pub struct Incomplete { - /// Block hash to get receipts for. - pub hash: Field, - } - - impl super::IncompleteRequest for Incomplete { - type Complete = Complete; - type Response = Response; - - fn check_outputs(&self, mut f: F) -> Result<(), NoSuchOutput> - where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> - { - match self.hash { - Field::Scalar(_) => Ok(()), - Field::BackReference(req, idx) => f(req, idx, OutputKind::Hash), - } - } - - fn note_outputs(&self, _: F) where F: FnMut(usize, OutputKind) {} - - fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { - if let Field::BackReference(req, idx) = self.hash { - self.hash = match oracle(req, idx) { - Ok(Output::Hash(hash)) => Field::Scalar(hash), - _ => Field::BackReference(req, idx), - } - } - } - - fn complete(self) -> Result { - Ok(Complete { - hash: self.hash.into_scalar()?, - }) - } - - fn adjust_refs(&mut self, mapping: F) where F: FnMut(usize) -> usize { - self.hash.adjust_req(mapping) - } - } - - /// A complete block body request. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Complete { - /// The hash to get a block body for. - pub hash: H256, - } - - /// The output of a request for block body. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Response { - /// The block body. - pub body: encoded::Body, - } - - impl super::ResponseLike for Response { - /// Fill reusable outputs by providing them to the function. - fn fill_outputs(&self, _: F) where F: FnMut(usize, Output) {} - } - - impl Decodable for Response { - fn decode(rlp: &Rlp) -> Result { - use common_types::header::Header as FullHeader; - use common_types::transaction::UnverifiedTransaction; - - // check body validity. - let _: Vec = rlp.list_at(0)?; - let _: Vec = rlp.list_at(1)?; - - Ok(Response { - body: encoded::Body::new(rlp.as_raw().to_owned()), - }) - } - } - - impl Encodable for Response { - fn rlp_append(&self, s: &mut RlpStream) { - s.append_raw(&self.body.rlp().as_raw(), 1); - } - } -} - -/// A request for an account proof. -pub mod account { - use super::{Field, NoSuchOutput, OutputKind, Output}; - use ethereum_types::{H256, U256}; - use bytes::Bytes; - - /// Potentially incomplete request for an account proof. - #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] - pub struct Incomplete { - /// Block hash to request state proof for. - pub block_hash: Field, - /// Hash of the account's address. - pub address_hash: Field, - } - - impl super::IncompleteRequest for Incomplete { - type Complete = Complete; - type Response = Response; - - fn check_outputs(&self, mut f: F) -> Result<(), NoSuchOutput> - where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> - { - if let Field::BackReference(req, idx) = self.block_hash { - f(req, idx, OutputKind::Hash)? - } - - if let Field::BackReference(req, idx) = self.address_hash { - f(req, idx, OutputKind::Hash)? - } - - Ok(()) - } - - fn note_outputs(&self, mut f: F) where F: FnMut(usize, OutputKind) { - f(0, OutputKind::Hash); - f(1, OutputKind::Hash); - } - - fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { - if let Field::BackReference(req, idx) = self.block_hash { - self.block_hash = match oracle(req, idx) { - Ok(Output::Hash(block_hash)) => Field::Scalar(block_hash), - _ => Field::BackReference(req, idx), - } - } - - if let Field::BackReference(req, idx) = self.address_hash { - self.address_hash = match oracle(req, idx) { - Ok(Output::Hash(address_hash)) => Field::Scalar(address_hash), - _ => Field::BackReference(req, idx), - } - } - } - - fn complete(self) -> Result { - Ok(Complete { - block_hash: self.block_hash.into_scalar()?, - address_hash: self.address_hash.into_scalar()?, - }) - } - - fn adjust_refs(&mut self, mut mapping: F) where F: FnMut(usize) -> usize { - self.block_hash.adjust_req(&mut mapping); - self.address_hash.adjust_req(&mut mapping); - } - } - - /// A complete request for an account. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Complete { - /// Block hash to request state proof for. - pub block_hash: H256, - /// Hash of the account's address. - pub address_hash: H256, - } - - /// The output of a request for an account state proof. - #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] - pub struct Response { - /// Inclusion/exclusion proof - pub proof: Vec, - /// Account nonce. - pub nonce: U256, - /// Account balance. - pub balance: U256, - /// Account's code hash. - pub code_hash: H256, - /// Account's storage trie root. - pub storage_root: H256, - } - - impl super::ResponseLike for Response { - /// Fill reusable outputs by providing them to the function. - fn fill_outputs(&self, mut f: F) where F: FnMut(usize, Output) { - f(0, Output::Hash(self.code_hash)); - f(1, Output::Hash(self.storage_root)); - } - } -} - -/// A request for a storage proof. -pub mod storage { - use super::{Field, NoSuchOutput, OutputKind, Output}; - use ethereum_types::H256; - use bytes::Bytes; - - /// Potentially incomplete request for an storage proof. - #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] - pub struct Incomplete { - /// Block hash to request state proof for. - pub block_hash: Field, - /// Hash of the account's address. - pub address_hash: Field, - /// Hash of the storage key. - pub key_hash: Field, - } - - impl super::IncompleteRequest for Incomplete { - type Complete = Complete; - type Response = Response; - - fn check_outputs(&self, mut f: F) -> Result<(), NoSuchOutput> - where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> - { - if let Field::BackReference(req, idx) = self.block_hash { - f(req, idx, OutputKind::Hash)? - } - - if let Field::BackReference(req, idx) = self.address_hash { - f(req, idx, OutputKind::Hash)? - } - - if let Field::BackReference(req, idx) = self.key_hash { - f(req, idx, OutputKind::Hash)? - } - - Ok(()) - } - - fn note_outputs(&self, mut f: F) where F: FnMut(usize, OutputKind) { - f(0, OutputKind::Hash); - } - - fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { - if let Field::BackReference(req, idx) = self.block_hash { - self.block_hash = match oracle(req, idx) { - Ok(Output::Hash(block_hash)) => Field::Scalar(block_hash), - _ => Field::BackReference(req, idx), - } - } - - if let Field::BackReference(req, idx) = self.address_hash { - self.address_hash = match oracle(req, idx) { - Ok(Output::Hash(address_hash)) => Field::Scalar(address_hash), - _ => Field::BackReference(req, idx), - } - } - - if let Field::BackReference(req, idx) = self.key_hash { - self.key_hash = match oracle(req, idx) { - Ok(Output::Hash(key_hash)) => Field::Scalar(key_hash), - _ => Field::BackReference(req, idx), - } - } - } - - fn complete(self) -> Result { - Ok(Complete { - block_hash: self.block_hash.into_scalar()?, - address_hash: self.address_hash.into_scalar()?, - key_hash: self.key_hash.into_scalar()?, - }) - } - - fn adjust_refs(&mut self, mut mapping: F) where F: FnMut(usize) -> usize { - self.block_hash.adjust_req(&mut mapping); - self.address_hash.adjust_req(&mut mapping); - self.key_hash.adjust_req(&mut mapping); - } - } - - /// A complete request for a storage proof. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Complete { - /// Block hash to request state proof for. - pub block_hash: H256, - /// Hash of the account's address. - pub address_hash: H256, - /// Storage key hash. - pub key_hash: H256, - } - - /// The output of a request for an account state proof. - #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] - pub struct Response { - /// Inclusion/exclusion proof - pub proof: Vec, - /// Storage value. - pub value: H256, - } - - impl super::ResponseLike for Response { - /// Fill reusable outputs by providing them to the function. - fn fill_outputs(&self, mut f: F) where F: FnMut(usize, Output) { - f(0, Output::Hash(self.value)); - } - } -} - -/// A request for contract code. -pub mod contract_code { - use super::{Field, NoSuchOutput, OutputKind, Output}; - use ethereum_types::H256; - use bytes::Bytes; - - /// Potentially incomplete contract code request. - #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] - pub struct Incomplete { - /// The block hash to request the state for. - pub block_hash: Field, - /// The code hash. - pub code_hash: Field, - } - - impl super::IncompleteRequest for Incomplete { - type Complete = Complete; - type Response = Response; - - fn check_outputs(&self, mut f: F) -> Result<(), NoSuchOutput> - where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> - { - if let Field::BackReference(req, idx) = self.block_hash { - f(req, idx, OutputKind::Hash)?; - } - if let Field::BackReference(req, idx) = self.code_hash { - f(req, idx, OutputKind::Hash)?; - } - - Ok(()) - } - - fn note_outputs(&self, _: F) where F: FnMut(usize, OutputKind) {} - - fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { - if let Field::BackReference(req, idx) = self.block_hash { - self.block_hash = match oracle(req, idx) { - Ok(Output::Hash(block_hash)) => Field::Scalar(block_hash), - _ => Field::BackReference(req, idx), - } - } - - if let Field::BackReference(req, idx) = self.code_hash { - self.code_hash = match oracle(req, idx) { - Ok(Output::Hash(code_hash)) => Field::Scalar(code_hash), - _ => Field::BackReference(req, idx), - } - } - } - - fn complete(self) -> Result { - Ok(Complete { - block_hash: self.block_hash.into_scalar()?, - code_hash: self.code_hash.into_scalar()?, - }) - } - - fn adjust_refs(&mut self, mut mapping: F) where F: FnMut(usize) -> usize { - self.block_hash.adjust_req(&mut mapping); - self.code_hash.adjust_req(&mut mapping); - } - } - - /// A complete request. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Complete { - /// The block hash to request the state for. - pub block_hash: H256, - /// The code hash. - pub code_hash: H256, - } - - /// The output of a request for - #[derive(Debug, Clone, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper)] - pub struct Response { - /// The requested code. - pub code: Bytes, - } - - impl super::ResponseLike for Response { - /// Fill reusable outputs by providing them to the function. - fn fill_outputs(&self, _: F) where F: FnMut(usize, Output) {} - } -} - -/// A request for proof of execution. -pub mod execution { - use super::{Field, NoSuchOutput, OutputKind, Output}; - use common_types::transaction::Action; - use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp}; - use ethereum_types::{H256, U256, Address}; - use kvdb::DBValue; - use bytes::Bytes; - - /// Potentially incomplete execution proof request. - #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] - pub struct Incomplete { - /// The block hash to request the state for. - pub block_hash: Field, - /// The address the transaction should be from. - pub from: Address, - /// The action of the transaction. - pub action: Action, - /// The amount of gas to prove. - pub gas: U256, - /// The gas price. - pub gas_price: U256, - /// The value to transfer. - pub value: U256, - /// Call data. - pub data: Bytes, - } - - impl super::IncompleteRequest for Incomplete { - type Complete = Complete; - type Response = Response; - - fn check_outputs(&self, mut f: F) -> Result<(), NoSuchOutput> - where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> - { - if let Field::BackReference(req, idx) = self.block_hash { - f(req, idx, OutputKind::Hash)?; - } - - Ok(()) - } - - fn note_outputs(&self, _: F) where F: FnMut(usize, OutputKind) {} - - fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { - if let Field::BackReference(req, idx) = self.block_hash { - self.block_hash = match oracle(req, idx) { - Ok(Output::Hash(block_hash)) => Field::Scalar(block_hash), - _ => Field::BackReference(req, idx), - } - } - } - fn complete(self) -> Result { - Ok(Complete { - block_hash: self.block_hash.into_scalar()?, - from: self.from, - action: self.action, - gas: self.gas, - gas_price: self.gas_price, - value: self.value, - data: self.data, - }) - } - - fn adjust_refs(&mut self, mapping: F) where F: FnMut(usize) -> usize { - self.block_hash.adjust_req(mapping); - } - } - - /// A complete request. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Complete { - /// The block hash to request the state for. - pub block_hash: H256, - /// The address the transaction should be from. - pub from: Address, - /// The action of the transaction. - pub action: Action, - /// The amount of gas to prove. - pub gas: U256, - /// The gas price. - pub gas_price: U256, - /// The value to transfer. - pub value: U256, - /// Call data. - pub data: Bytes, - } - - /// The output of a request for proof of execution - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Response { - /// All state items (trie nodes, code) necessary to re-prove the transaction. - pub items: Vec, - } - - impl super::ResponseLike for Response { - /// Fill reusable outputs by providing them to the function. - fn fill_outputs(&self, _: F) where F: FnMut(usize, Output) {} - } - - impl Decodable for Response { - fn decode(rlp: &Rlp) -> Result { - let mut items = Vec::new(); - for raw_item in rlp.iter() { - let mut item = DBValue::new(); - item.append_slice(raw_item.data()?); - items.push(item); - } - - Ok(Response { items }) - } - } - - impl Encodable for Response { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(self.items.len()); - - for item in &self.items { - s.append(&&**item); - } - } - } -} - -/// A request for epoch signal data. -pub mod epoch_signal { - use super::{Field, NoSuchOutput, OutputKind, Output}; - use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp}; - use ethereum_types::H256; - use bytes::Bytes; - - /// Potentially incomplete epoch signal request. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Incomplete { - /// The block hash to request the signal for. - pub block_hash: Field, - } - - impl Decodable for Incomplete { - fn decode(rlp: &Rlp) -> Result { - Ok(Incomplete { - block_hash: rlp.val_at(0)?, - }) - } - } - - impl Encodable for Incomplete { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(1).append(&self.block_hash); - } - } - - impl super::IncompleteRequest for Incomplete { - type Complete = Complete; - type Response = Response; - - fn check_outputs(&self, mut f: F) -> Result<(), NoSuchOutput> - where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> - { - if let Field::BackReference(req, idx) = self.block_hash { - f(req, idx, OutputKind::Hash)?; - } - - Ok(()) - } - - fn note_outputs(&self, _: F) where F: FnMut(usize, OutputKind) {} - - fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { - if let Field::BackReference(req, idx) = self.block_hash { - self.block_hash = match oracle(req, idx) { - Ok(Output::Hash(block_hash)) => Field::Scalar(block_hash), - _ => Field::BackReference(req, idx), - } - } - } - - fn complete(self) -> Result { - Ok(Complete { - block_hash: self.block_hash.into_scalar()?, - }) - } - - fn adjust_refs(&mut self, mut mapping: F) where F: FnMut(usize) -> usize { - self.block_hash.adjust_req(&mut mapping); - } - } - - /// A complete request. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Complete { - /// The block hash to request the epoch signal for. - pub block_hash: H256, - } - - /// The output of a request for an epoch signal. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Response { - /// The requested epoch signal. - pub signal: Bytes, - } - - impl super::ResponseLike for Response { - /// Fill reusable outputs by providing them to the function. - fn fill_outputs(&self, _: F) where F: FnMut(usize, Output) {} - } - - impl Decodable for Response { - fn decode(rlp: &Rlp) -> Result { - - Ok(Response { - signal: rlp.as_val()?, - }) - } - } - - impl Encodable for Response { - fn rlp_append(&self, s: &mut RlpStream) { - s.append(&self.signal); - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use common_types::header::Header; - - fn check_roundtrip(val: T) - where T: ::rlp::Encodable + ::rlp::Decodable + PartialEq + ::std::fmt::Debug - { - // check as single value. - let bytes = ::rlp::encode(&val); - let new_val: T = ::rlp::decode(&bytes).unwrap(); - assert_eq!(val, new_val); - - // check as list containing single value. - let list = [val]; - let bytes = ::rlp::encode_list(&list); - let new_list: Vec = ::rlp::decode_list(&bytes); - assert_eq!(&list, &new_list[..]); - } - - #[test] - fn hash_or_number_roundtrip() { - let hash = HashOrNumber::Hash(H256::default()); - let number = HashOrNumber::Number(5); - - check_roundtrip(hash); - check_roundtrip(number); - } - - #[test] - fn field_roundtrip() { - let field_scalar = Field::Scalar(5usize); - let field_back: Field = Field::BackReference(1, 2); - - check_roundtrip(field_scalar); - check_roundtrip(field_back); - } - - #[test] - fn headers_roundtrip() { - let req = IncompleteHeadersRequest { - start: Field::Scalar(5u64.into()), - skip: 0, - max: 100, - reverse: false, - }; - - let full_req = Request::Headers(req.clone()); - let res = HeadersResponse { - headers: vec![ - ::common_types::encoded::Header::new(::rlp::encode(&Header::default())) - ] - }; - let full_res = Response::Headers(res.clone()); - - check_roundtrip(req); - check_roundtrip(full_req); - check_roundtrip(res); - check_roundtrip(full_res); - } - - #[test] - fn header_proof_roundtrip() { - let req = IncompleteHeaderProofRequest { - num: Field::BackReference(1, 234), - }; - - let full_req = Request::HeaderProof(req.clone()); - let res = HeaderProofResponse { - proof: vec![vec![1, 2, 3], vec![4, 5, 6]], - hash: Default::default(), - td: 100.into(), - }; - let full_res = Response::HeaderProof(res.clone()); - - check_roundtrip(req); - check_roundtrip(full_req); - check_roundtrip(res); - check_roundtrip(full_res); - } - - #[test] - fn transaction_index_roundtrip() { - let req = IncompleteTransactionIndexRequest { - hash: Field::Scalar(Default::default()), - }; - - let full_req = Request::TransactionIndex(req.clone()); - let res = TransactionIndexResponse { - num: 1000, - hash: ::ethereum_types::H256::random(), - index: 4, - }; - let full_res = Response::TransactionIndex(res.clone()); - - check_roundtrip(req); - check_roundtrip(full_req); - check_roundtrip(res); - check_roundtrip(full_res); - } - - #[test] - fn receipts_roundtrip() { - use common_types::receipt::{Receipt, TransactionOutcome}; - let req = IncompleteReceiptsRequest { - hash: Field::Scalar(Default::default()), - }; - - let full_req = Request::Receipts(req.clone()); - let receipt = Receipt::new(TransactionOutcome::Unknown, Default::default(), Vec::new()); - let res = ReceiptsResponse { - receipts: vec![receipt.clone(), receipt], - }; - let full_res = Response::Receipts(res.clone()); - - check_roundtrip(req); - check_roundtrip(full_req); - check_roundtrip(res); - check_roundtrip(full_res); - } - - #[test] - fn body_roundtrip() { - use common_types::transaction::{Transaction, UnverifiedTransaction}; - let req = IncompleteBodyRequest { - hash: Field::Scalar(Default::default()), - }; - - let full_req = Request::Body(req.clone()); - let res = BodyResponse { - body: { - let header = ::common_types::header::Header::default(); - let tx = UnverifiedTransaction::from(Transaction::default().fake_sign(Default::default())); - let mut stream = RlpStream::new_list(2); - stream.begin_list(2).append(&tx).append(&tx) - .begin_list(1).append(&header); - - ::common_types::encoded::Body::new(stream.out()) - }, - }; - let full_res = Response::Body(res.clone()); - - check_roundtrip(req); - check_roundtrip(full_req); - check_roundtrip(res); - check_roundtrip(full_res); - } - - #[test] - fn account_roundtrip() { - let req = IncompleteAccountRequest { - block_hash: Field::Scalar(Default::default()), - address_hash: Field::BackReference(1, 2), - }; - - let full_req = Request::Account(req.clone()); - let res = AccountResponse { - proof: vec![vec![1, 2, 3], vec![4, 5, 6]], - nonce: 100.into(), - balance: 123456.into(), - code_hash: Default::default(), - storage_root: Default::default(), - }; - let full_res = Response::Account(res.clone()); - - check_roundtrip(req); - check_roundtrip(full_req); - check_roundtrip(res); - check_roundtrip(full_res); - } - - #[test] - fn storage_roundtrip() { - let req = IncompleteStorageRequest { - block_hash: Field::Scalar(Default::default()), - address_hash: Field::BackReference(1, 2), - key_hash: Field::BackReference(3, 2), - }; - - let full_req = Request::Storage(req.clone()); - let res = StorageResponse { - proof: vec![vec![1, 2, 3], vec![4, 5, 6]], - value: H256::default(), - }; - let full_res = Response::Storage(res.clone()); - - check_roundtrip(req); - check_roundtrip(full_req); - check_roundtrip(res); - check_roundtrip(full_res); - } - - #[test] - fn code_roundtrip() { - let req = IncompleteCodeRequest { - block_hash: Field::Scalar(Default::default()), - code_hash: Field::BackReference(3, 2), - }; - - let full_req = Request::Code(req.clone()); - let res = CodeResponse { - code: vec![1, 2, 3, 4, 5, 6, 7, 6, 5, 4], - }; - let full_res = Response::Code(res.clone()); - - check_roundtrip(req); - check_roundtrip(full_req); - check_roundtrip(res); - check_roundtrip(full_res); - } - - #[test] - fn execution_roundtrip() { - use kvdb::DBValue; - - let req = IncompleteExecutionRequest { - block_hash: Field::Scalar(Default::default()), - from: Default::default(), - action: ::common_types::transaction::Action::Create, - gas: 100_000.into(), - gas_price: 0.into(), - value: 100_000_001.into(), - data: vec![1, 2, 3, 2, 1], - }; - - let full_req = Request::Execution(req.clone()); - let res = ExecutionResponse { - items: vec![DBValue::new(), { - let mut value = DBValue::new(); - value.append_slice(&[1, 1, 1, 2, 3]); - value - }], - }; - let full_res = Response::Execution(res.clone()); - - check_roundtrip(req); - check_roundtrip(full_req); - check_roundtrip(res); - check_roundtrip(full_res); - } - - #[test] - fn vec_test() { - use rlp::*; - - let reqs: Vec<_> = (0..10).map(|_| IncompleteExecutionRequest { - block_hash: Field::Scalar(Default::default()), - from: Default::default(), - action: ::common_types::transaction::Action::Create, - gas: 100_000.into(), - gas_price: 0.into(), - value: 100_000_001.into(), - data: vec![1, 2, 3, 2, 1], - }).map(Request::Execution).collect(); - - let mut stream = RlpStream::new_list(2); - stream.append(&100usize).append_list(&reqs); - let out = stream.out(); - - let rlp = Rlp::new(&out); - assert_eq!(rlp.val_at::(0).unwrap(), 100usize); - assert_eq!(rlp.list_at::(1).unwrap(), reqs); - } - - #[test] - fn responses_vec() { - use common_types::receipt::{Receipt, TransactionOutcome}; - let mut stream = RlpStream::new_list(2); - stream.begin_list(0).begin_list(0); - - let body = ::common_types::encoded::Body::new(stream.out()); - let reqs = vec![ - Response::Headers(HeadersResponse { headers: vec![] }), - Response::HeaderProof(HeaderProofResponse { proof: vec![], hash: Default::default(), td: 100.into()}), - Response::Receipts(ReceiptsResponse { receipts: vec![Receipt::new(TransactionOutcome::Unknown, Default::default(), Vec::new())] }), - Response::Body(BodyResponse { body: body }), - Response::Account(AccountResponse { - proof: vec![], - nonce: 100.into(), - balance: 123.into(), - code_hash: Default::default(), - storage_root: Default::default() - }), - Response::Storage(StorageResponse { proof: vec![], value: H256::default() }), - Response::Code(CodeResponse { code: vec![1, 2, 3, 4, 5] }), - Response::Execution(ExecutionResponse { items: vec![] }), - ]; - - let raw = ::rlp::encode_list(&reqs); - assert_eq!(::rlp::decode_list::(&raw), reqs); - } - - #[test] - fn epoch_signal_roundtrip() { - let req = IncompleteSignalRequest { - block_hash: Field::Scalar(Default::default()), - }; - - let full_req = Request::Signal(req.clone()); - let res = SignalResponse { - signal: vec![1, 2, 3, 4, 5, 6, 7, 6, 5, 4], - }; - let full_res = Response::Signal(res.clone()); - - check_roundtrip(req); - check_roundtrip(full_req); - check_roundtrip(res); - check_roundtrip(full_res); - } -} diff --git a/ethcore/node-filter/Cargo.toml b/ethcore/node-filter/Cargo.toml index bd184685259..26b343af61f 100644 --- a/ethcore/node-filter/Cargo.toml +++ b/ethcore/node-filter/Cargo.toml @@ -1,6 +1,6 @@ [package] -description = "Parity smart network connections" -homepage = "http://parity.io" +description = "OpenEthereum Smart Contract based Node Filter, Manage Permissions of Network Connections" +homepage = "https://github.com/openethereum/openethereum" license = "GPL-3.0" name = "node-filter" version = "1.12.0" diff --git a/ethcore/node-filter/src/lib.rs b/ethcore/node-filter/src/lib.rs index 816bb84a838..c32463b3e57 100644 --- a/ethcore/node-filter/src/lib.rs +++ b/ethcore/node-filter/src/lib.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Smart contract based node filter. @@ -39,93 +39,106 @@ extern crate log; use std::sync::Weak; -use ethcore::client::{BlockChainClient, BlockId}; -use ethereum_types::{H256, Address}; -use ethabi::FunctionOutputDecoder; -use network::{ConnectionFilter, ConnectionDirection}; use devp2p::NodeId; +use ethabi::FunctionOutputDecoder; +use ethcore::client::{BlockChainClient, BlockId}; +use ethereum_types::{Address, H256}; +use network::{ConnectionDirection, ConnectionFilter}; use_contract!(peer_set, "res/peer_set.json"); /// Connection filter that uses a contract to manage permissions. pub struct NodeFilter { - client: Weak, - contract_address: Address, + client: Weak, + contract_address: Address, } impl NodeFilter { - /// Create a new instance. Accepts a contract address. - pub fn new(client: Weak, contract_address: Address) -> NodeFilter { - NodeFilter { - client, - contract_address, - } - } + /// Create a new instance. Accepts a contract address. + pub fn new(client: Weak, contract_address: Address) -> NodeFilter { + NodeFilter { + client, + contract_address, + } + } } impl ConnectionFilter for NodeFilter { - fn connection_allowed(&self, own_id: &NodeId, connecting_id: &NodeId, _direction: ConnectionDirection) -> bool { - let client = match self.client.upgrade() { - Some(client) => client, - None => return false, - }; - - let address = self.contract_address; - let own_low = H256::from_slice(&own_id[0..32]); - let own_high = H256::from_slice(&own_id[32..64]); - let id_low = H256::from_slice(&connecting_id[0..32]); - let id_high = H256::from_slice(&connecting_id[32..64]); - - let (data, decoder) = peer_set::functions::connection_allowed::call(own_low, own_high, id_low, id_high); - let allowed = client.call_contract(BlockId::Latest, address, data) - .and_then(|value| decoder.decode(&value).map_err(|e| e.to_string())) - .unwrap_or_else(|e| { - debug!("Error callling peer set contract: {:?}", e); - false - }); - - allowed - } + fn connection_allowed( + &self, + own_id: &NodeId, + connecting_id: &NodeId, + _direction: ConnectionDirection, + ) -> bool { + let client = match self.client.upgrade() { + Some(client) => client, + None => return false, + }; + + let address = self.contract_address; + let own_low = H256::from_slice(&own_id[0..32]); + let own_high = H256::from_slice(&own_id[32..64]); + let id_low = H256::from_slice(&connecting_id[0..32]); + let id_high = H256::from_slice(&connecting_id[32..64]); + + let (data, decoder) = + peer_set::functions::connection_allowed::call(own_low, own_high, id_low, id_high); + let allowed = client + .call_contract(BlockId::Latest, address, data) + .and_then(|value| decoder.decode(&value).map_err(|e| e.to_string())) + .unwrap_or_else(|e| { + debug!("Error callling peer set contract: {:?}", e); + false + }); + + allowed + } } #[cfg(test)] mod test { - use std::sync::{Arc, Weak}; - use ethcore::spec::Spec; - use ethcore::client::{BlockChainClient, Client, ClientConfig}; - use ethcore::miner::Miner; - use ethcore::test_helpers; - use network::{ConnectionDirection, ConnectionFilter, NodeId}; - use io::IoChannel; - use super::NodeFilter; - use tempdir::TempDir; - - /// Contract code: https://gist.github.com/arkpar/467dbcc73cbb85b0997a7a10ffa0695f - #[test] - fn node_filter() { - let contract_addr = "0000000000000000000000000000000000000005".into(); - let data = include_bytes!("../res/node_filter.json"); - let tempdir = TempDir::new("").unwrap(); - let spec = Spec::load(&tempdir.path(), &data[..]).unwrap(); - let client_db = test_helpers::new_db(); - - let client = Client::new( - ClientConfig::default(), - &spec, - client_db, - Arc::new(Miner::new_for_tests(&spec, None)), - IoChannel::disconnected(), - ).unwrap(); - let filter = NodeFilter::new(Arc::downgrade(&client) as Weak, contract_addr); - let self1: NodeId = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002".into(); - let self2: NodeId = "00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003".into(); - let node1: NodeId = "00000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012".into(); - let node2: NodeId = "00000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000022".into(); - let nodex: NodeId = "77000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(); - - assert!(filter.connection_allowed(&self1, &node1, ConnectionDirection::Inbound)); - assert!(filter.connection_allowed(&self1, &nodex, ConnectionDirection::Inbound)); - assert!(filter.connection_allowed(&self2, &node1, ConnectionDirection::Inbound)); - assert!(filter.connection_allowed(&self2, &node2, ConnectionDirection::Inbound)); - } + use super::NodeFilter; + use ethcore::{ + client::{BlockChainClient, Client, ClientConfig}, + miner::Miner, + spec::Spec, + test_helpers, + }; + use io::IoChannel; + use network::{ConnectionDirection, ConnectionFilter, NodeId}; + use std::sync::{Arc, Weak}; + use tempdir::TempDir; + + /// Contract code: https://gist.github.com/arkpar/467dbcc73cbb85b0997a7a10ffa0695f + #[test] + fn node_filter() { + let contract_addr = "0000000000000000000000000000000000000005".into(); + let data = include_bytes!("../res/node_filter.json"); + let tempdir = TempDir::new("").unwrap(); + let spec = Spec::load(&tempdir.path(), &data[..]).unwrap(); + let client_db = test_helpers::new_db(); + + let client = Client::new( + ClientConfig::default(), + &spec, + client_db, + Arc::new(Miner::new_for_tests(&spec, None)), + IoChannel::disconnected(), + ) + .unwrap(); + let filter = NodeFilter::new( + Arc::downgrade(&client) as Weak, + contract_addr, + ); + let self1: NodeId = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002".into(); + let self2: NodeId = "00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003".into(); + let node1: NodeId = "00000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012".into(); + let node2: NodeId = "00000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000022".into(); + let nodex: NodeId = "77000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(); + + assert!(filter.connection_allowed(&self1, &node1, ConnectionDirection::Inbound)); + assert!(filter.connection_allowed(&self1, &nodex, ConnectionDirection::Inbound)); + assert!(filter.connection_allowed(&self2, &node1, ConnectionDirection::Inbound)); + assert!(filter.connection_allowed(&self2, &node2, ConnectionDirection::Inbound)); + } } diff --git a/ethcore/private-tx/Cargo.toml b/ethcore/private-tx/Cargo.toml deleted file mode 100644 index 2ce127a8b7a..00000000000 --- a/ethcore/private-tx/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -description = "Parity Private Transactions" -name = "ethcore-private-tx" -version = "1.0.0" -license = "GPL-3.0" -authors = ["Parity Technologies "] - -[dependencies] -common-types = { path = "../types" } -derive_more = "0.14.0" -ethabi = "6.0" -ethabi-contract = "6.0" -ethabi-derive = "6.0" -ethcore = { path = ".." } -ethcore-call-contract = { path = "../call-contract" } -ethcore-io = { path = "../../util/io" } -ethcore-miner = { path = "../../miner" } -ethereum-types = "0.4" -ethjson = { path = "../../json" } -ethkey = { path = "../../accounts/ethkey" } -fetch = { path = "../../util/fetch" } -futures = "0.1" -heapsize = "0.4" -keccak-hash = "0.1.2" -log = "0.4" -parity-bytes = "0.1" -parity-crypto = "0.3.0" -parking_lot = "0.7" -trie-db = "0.11.0" -patricia-trie-ethereum = { path = "../../util/patricia-trie-ethereum" } -rand = "0.3" -rlp = { version = "0.3.0", features = ["ethereum"] } -rlp_derive = { path = "../../util/rlp-derive" } -rustc-hex = "1.0" -serde = "1.0" -serde_derive = "1.0" -serde_json = "1.0" -tiny-keccak = "1.4" -transaction-pool = "2.0" -url = "1" - -[dev-dependencies] -env_logger = "0.5" -ethcore = { path = "..", features = ["test-helpers"] } diff --git a/ethcore/private-tx/res/keys_acl.json b/ethcore/private-tx/res/keys_acl.json deleted file mode 100644 index 3ec2daf9e9a..00000000000 --- a/ethcore/private-tx/res/keys_acl.json +++ /dev/null @@ -1,43 +0,0 @@ -[ - { - "constant": true, - "inputs": [ - { - "name":"user", - "type":"address" - } - ], - "name": "availableKeys", - "outputs": [ - { - "name": "", - "type": "bytes32[]" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant":true, - "inputs": [ - { - "name":"user", - "type":"address" - }, - { - "name":"document", - "type":"bytes32" - } - ], - "name":"checkPermissions", - "outputs": [ - { - "name":"", - "type":"bool" - } - ], - "payable":false, - "type":"function" - } -] diff --git a/ethcore/private-tx/res/private.evm b/ethcore/private-tx/res/private.evm deleted file mode 100644 index e1da9bb606e..00000000000 --- a/ethcore/private-tx/res/private.evm +++ /dev/null @@ -1 +0,0 @@ -60806040523480156200001157600080fd5b5060405162000d5238038062000d52833981018060405281019080805182019291906020018051820192919060200180518201929190505050826000908051906020019062000062929190620000a6565b5081600290805190602001906200007b92919062000135565b5080600190805190602001906200009492919062000135565b5060016003819055505050506200022a565b82805482825590600052602060002090810192821562000122579160200282015b82811115620001215782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555091602001919060010190620000c7565b5b509050620001319190620001bc565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200017857805160ff1916838001178555620001a9565b82800160010185558215620001a9579182015b82811115620001a85782518255916020019190600101906200018b565b5b509050620001b8919062000202565b5090565b620001ff91905b80821115620001fb57600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550600101620001c3565b5090565b90565b6200022791905b808211156200022357600081600090555060010162000209565b5090565b90565b610b18806200023a6000396000f30060806040526004361061008e576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630d8e6e2c1461009357806317ac53a2146100c457806324c12bf6146101f657806335aa2e44146102865780639326c281146102f3578063affed0e01461037c578063b7ab4db5146103a7578063c19d93fb14610413575b600080fd5b34801561009f57600080fd5b506100a86104a3565b604051808260ff1660ff16815260200191505060405180910390f35b3480156100d057600080fd5b506101f4600480360381019080803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919291929080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509192919290803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091929192905050506104ac565b005b34801561020257600080fd5b5061020b610765565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561024b578082015181840152602081019050610230565b50505050905090810190601f1680156102785780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561029257600080fd5b506102b160048036038101908080359060200190929190505050610803565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102ff57600080fd5b5061037a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050610841565b005b34801561038857600080fd5b50610391610915565b6040518082815260200191505060405180910390f35b3480156103b357600080fd5b506103bc61091b565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156103ff5780820151818401526020810190506103e4565b505050509050019250505060405180910390f35b34801561041f57600080fd5b506104286109a9565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561046857808201518184015260208101905061044d565b50505050905090810190601f1680156104955780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60006002905090565b6000806040805190810160405280876040518082805190602001908083835b6020831015156104f057805182526020820191506020810190506020830392506104cb565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390206000191660001916815260200160035460010260001916600019168152506040516020018082600260200280838360005b8381101561056957808201518184015260208101905061054e565b505050509050019150506040516020818303038152906040526040518082805190602001908083835b6020831015156105b75780518252602082019150602081019050602083039250610592565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390209150600090505b60008054905081101561073a5760008181548110151561060757fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600183878481518110151561065957fe5b90602001906020020151878581518110151561067157fe5b90602001906020020151878681518110151561068957fe5b90602001906020020151604051600081526020016040526040518085600019166000191681526020018460ff1660ff1681526020018360001916600019168152602001826000191660001916815260200194505050505060206040516020810390808403906000865af1158015610704573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff1614151561072d57fe5b80806001019150506105eb565b8560019080519060200190610750929190610a47565b50600160035401600381905550505050505050565b60028054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156107fb5780601f106107d0576101008083540402835291602001916107fb565b820191906000526020600020905b8154815290600101906020018083116107de57829003601f168201915b505050505081565b60008181548110151561081257fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b7fd75b949e4bbba98bcf6d2878e9175f5608dde180a67ba25d0f2020067e17fdac8282604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360005b838110156108d65780820151818401526020810190506108bb565b50505050905090810190601f1680156109035780820380516001836020036101000a031916815260200191505b50935050505060405180910390a15050565b60035481565b6060600080548060200260200160405190810160405280929190818152602001828054801561099f57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610955575b5050505050905090565b60018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a3f5780601f10610a1457610100808354040283529160200191610a3f565b820191906000526020600020905b815481529060010190602001808311610a2257829003601f168201915b505050505081565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610a8857805160ff1916838001178555610ab6565b82800160010185558215610ab6579182015b82811115610ab5578251825591602001919060010190610a9a565b5b509050610ac39190610ac7565b5090565b610ae991905b80821115610ae5576000816000905550600101610acd565b5090565b905600a165627a7a723058205bbab96bfbda16ccdb4900d9280aa9d511c3d3687be1ca598dd784987f55ebb70029 diff --git a/ethcore/private-tx/res/private.json b/ethcore/private-tx/res/private.json deleted file mode 100644 index 0d8f1754361..00000000000 --- a/ethcore/private-tx/res/private.json +++ /dev/null @@ -1,171 +0,0 @@ -[ - { - "constant": true, - "inputs": [], - "name": "getVersion", - "outputs": [ - { - "name": "", - "type": "uint8" - } - ], - "payable": false, - "stateMutability": "pure", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "newState", - "type": "bytes" - }, - { - "name": "v", - "type": "uint8[]" - }, - { - "name": "r", - "type": "bytes32[]" - }, - { - "name": "s", - "type": "bytes32[]" - } - ], - "name": "setState", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "code", - "outputs": [ - { - "name": "", - "type": "bytes" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "uint256" - } - ], - "name": "validators", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "changesOriginator", - "type": "address" - }, - { - "name": "originalTransactionHash", - "type": "bytes" - } - ], - "name": "notifyChanges", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "nonce", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "getValidators", - "outputs": [ - { - "name": "", - "type": "address[]" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "state", - "outputs": [ - { - "name": "", - "type": "bytes" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "name": "initialValidators", - "type": "address[]" - }, - { - "name": "initialCode", - "type": "bytes" - }, - { - "name": "initialState", - "type": "bytes" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "name": "changesOriginator", - "type": "address" - }, - { - "indexed": false, - "name": "originalTransactionHash", - "type": "bytes" - } - ], - "name": "PrivateStateChanged", - "type": "event" - } -] \ No newline at end of file diff --git a/ethcore/private-tx/src/encryptor.rs b/ethcore/private-tx/src/encryptor.rs deleted file mode 100644 index 597cc887969..00000000000 --- a/ethcore/private-tx/src/encryptor.rs +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Encryption providers. - -use std::io::Read; -use std::str::FromStr; -use std::sync::Arc; -use std::iter::repeat; -use std::time::{Instant, Duration}; -use std::collections::HashMap; -use std::collections::hash_map::Entry; -use parking_lot::Mutex; -use ethereum_types::{H128, H256, Address}; -use ethjson; -use ethkey::{Signature, Public}; -use crypto; -use futures::Future; -use fetch::{Fetch, Client as FetchClient, Method, BodyReader, Request}; -use bytes::{Bytes, ToPretty}; -use error::Error; -use url::Url; -use super::Signer; -use super::key_server_keys::address_to_key; - -/// Initialization vector length. -const INIT_VEC_LEN: usize = 16; - -/// Duration of storing retrieved keys (in ms) -const ENCRYPTION_SESSION_DURATION: u64 = 30 * 1000; - -/// Trait for encryption/decryption operations. -pub trait Encryptor: Send + Sync + 'static { - /// Generate unique contract key && encrypt passed data. Encryption can only be performed once. - fn encrypt( - &self, - contract_address: &Address, - initialisation_vector: &H128, - plain_data: &[u8], - ) -> Result; - - /// Decrypt data using previously generated contract key. - fn decrypt( - &self, - contract_address: &Address, - cypher: &[u8], - ) -> Result; -} - -/// Configurtion for key server encryptor -#[derive(Default, PartialEq, Debug, Clone)] -pub struct EncryptorConfig { - /// URL to key server - pub base_url: Option, - /// Key server's threshold - pub threshold: u32, - /// Account used for signing requests to key server - pub key_server_account: Option
, -} - -struct EncryptionSession { - key: Bytes, - end_time: Instant, -} - -/// SecretStore-based encryption/decryption operations. -pub struct SecretStoreEncryptor { - config: EncryptorConfig, - client: FetchClient, - sessions: Mutex>, - signer: Arc, -} - -impl SecretStoreEncryptor { - /// Create new encryptor - pub fn new( - config: EncryptorConfig, - client: FetchClient, - signer: Arc, - ) -> Result { - Ok(SecretStoreEncryptor { - config, - client, - signer, - sessions: Mutex::default(), - }) - } - - /// Ask secret store for key && decrypt the key. - fn retrieve_key( - &self, - url_suffix: &str, - use_post: bool, - contract_address: &Address, - ) -> Result { - // check if the key was already cached - if let Some(key) = self.obtained_key(contract_address) { - return Ok(key); - } - let contract_address_signature = self.sign_contract_address(contract_address)?; - let requester = self.config.key_server_account.ok_or_else(|| Error::KeyServerAccountNotSet)?; - - // key id in SS is H256 && we have H160 here => expand with assitional zeros - let contract_address_extended: H256 = contract_address.into(); - let base_url = self.config.base_url.clone().ok_or_else(|| Error::KeyServerNotSet)?; - - // prepare request url - let url = format!("{}/{}/{}{}", - base_url, - contract_address_extended.to_hex(), - contract_address_signature, - url_suffix, - ); - - // send HTTP request - let method = if use_post { - Method::POST - } else { - Method::GET - }; - - let url = Url::from_str(&url).map_err(|e| Error::Encrypt(e.to_string()))?; - let response = self.client.fetch(Request::new(url, method), Default::default()).wait() - .map_err(|e| Error::Encrypt(e.to_string()))?; - - if response.is_not_found() { - return Err(Error::EncryptionKeyNotFound(*contract_address)); - } - - if !response.is_success() { - return Err(Error::Encrypt(response.status().canonical_reason().unwrap_or("unknown").into())); - } - - // read HTTP response - let mut result = String::new(); - BodyReader::new(response).read_to_string(&mut result)?; - - // response is JSON string (which is, in turn, hex-encoded, encrypted Public) - let encrypted_bytes: ethjson::bytes::Bytes = result.trim_matches('\"').parse().map_err(|e| Error::Encrypt(e))?; - - // decrypt Public - let decrypted_bytes = self.signer.decrypt(requester, &crypto::DEFAULT_MAC, &encrypted_bytes)?; - let decrypted_key = Public::from_slice(&decrypted_bytes); - - // and now take x coordinate of Public as a key - let key: Bytes = (*decrypted_key)[..INIT_VEC_LEN].into(); - - // cache the key in the session and clear expired sessions - self.sessions.lock().insert(*contract_address, EncryptionSession{ - key: key.clone(), - end_time: Instant::now() + Duration::from_millis(ENCRYPTION_SESSION_DURATION), - }); - self.clean_expired_sessions(); - Ok(key) - } - - fn clean_expired_sessions(&self) { - let mut sessions = self.sessions.lock(); - sessions.retain(|_, session| session.end_time < Instant::now()); - } - - fn obtained_key(&self, contract_address: &Address) -> Option { - let mut sessions = self.sessions.lock(); - let stored_session = sessions.entry(*contract_address); - match stored_session { - Entry::Occupied(session) => { - if Instant::now() > session.get().end_time { - session.remove_entry(); - None - } else { - Some(session.get().key.clone()) - } - } - Entry::Vacant(_) => None, - } - } - - fn sign_contract_address(&self, contract_address: &Address) -> Result { - let key_server_account = self.config.key_server_account.ok_or_else(|| Error::KeyServerAccountNotSet)?; - Ok(self.signer.sign(key_server_account, address_to_key(contract_address))?) - } -} - -impl Encryptor for SecretStoreEncryptor { - fn encrypt( - &self, - contract_address: &Address, - initialisation_vector: &H128, - plain_data: &[u8], - ) -> Result { - // retrieve the key, try to generate it if it doesn't exist yet - let key = match self.retrieve_key("", false, contract_address) { - Ok(key) => Ok(key), - Err(Error::EncryptionKeyNotFound(_)) => { - trace!(target: "privatetx", "Key for account wasnt found in sstore. Creating. Address: {:?}", contract_address); - self.retrieve_key(&format!("/{}", self.config.threshold), true, contract_address) - } - Err(err) => Err(err), - }?; - - // encrypt data - let mut cypher = Vec::with_capacity(plain_data.len() + initialisation_vector.len()); - cypher.extend(repeat(0).take(plain_data.len())); - crypto::aes::encrypt_128_ctr(&key, initialisation_vector, plain_data, &mut cypher) - .map_err(|e| Error::Encrypt(e.to_string()))?; - cypher.extend_from_slice(&initialisation_vector); - - Ok(cypher) - } - - /// Decrypt data using previously generated contract key. - fn decrypt( - &self, - contract_address: &Address, - cypher: &[u8], - ) -> Result { - // initialization vector takes INIT_VEC_LEN bytes - let cypher_len = cypher.len(); - if cypher_len < INIT_VEC_LEN { - return Err(Error::Decrypt("Invalid cypher".into())); - } - - // retrieve existing key - let key = self.retrieve_key("", false, contract_address)?; - - // use symmetric decryption to decrypt document - let (cypher, iv) = cypher.split_at(cypher_len - INIT_VEC_LEN); - let mut plain_data = Vec::with_capacity(cypher_len - INIT_VEC_LEN); - plain_data.extend(repeat(0).take(cypher_len - INIT_VEC_LEN)); - crypto::aes::decrypt_128_ctr(&key, &iv, cypher, &mut plain_data) - .map_err(|e| Error::Decrypt(e.to_string()))?; - Ok(plain_data) - } -} - -/// Dummy encryptor. -#[derive(Default)] -pub struct NoopEncryptor; - -impl Encryptor for NoopEncryptor { - fn encrypt( - &self, - _contract_address: &Address, - _initialisation_vector: &H128, - data: &[u8], - ) -> Result { - Ok(data.to_vec()) - } - - fn decrypt( - &self, - _contract_address: &Address, - data: &[u8], - ) -> Result { - Ok(data.to_vec()) - } -} diff --git a/ethcore/private-tx/src/error.rs b/ethcore/private-tx/src/error.rs deleted file mode 100644 index eda08b2a567..00000000000 --- a/ethcore/private-tx/src/error.rs +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::error; -use derive_more::Display; -use ethereum_types::Address; -use rlp::DecoderError; -use ethtrie::TrieError; -use ethcore::error::{Error as EthcoreError, ExecutionError}; -use types::transaction::Error as TransactionError; -use ethkey::Error as KeyError; -use ethkey::crypto::Error as CryptoError; -use txpool::VerifiedTransaction; -use private_transactions::VerifiedPrivateTransaction; - -type TxPoolError = txpool::Error<::Hash>; - -#[derive(Debug, Display)] -pub enum Error { - /// Error concerning the Rust standard library's IO subsystem. - #[display(fmt = "Io Error: {}", _0)] - Io(::std::io::Error), - /// RLP decoding error. - #[display(fmt = "Decoder Error: {}", _0)] - Decoder(DecoderError), - /// Error concerning TrieDBs. - #[display(fmt = "Trie Error: {}", _0)] - Trie(TrieError), - /// Transaction pool error. - #[display(fmt = "Transaction Pool Error: {}", _0)] - TxPool(TxPoolError), - /// Crypto error. - #[display(fmt = "Crypto Error {}", _0)] - Crypto(CryptoError), - /// Encryption error. - #[display(fmt = "Encryption error. ({})", _0)] - Encrypt(String), - /// Decryption error. - #[display(fmt = "Decryption error. ({})", _0)] - Decrypt(String), - /// Address not authorized. - #[display(fmt = "Private transaction execution is not authorised for {}", _0)] - NotAuthorised(Address), - /// Transaction creates more than one contract. - #[display(fmt = "Private transaction created too many contracts")] - TooManyContracts, - /// Contract call error. - #[display(fmt = "Contract call error. ({})", _0)] - Call(String), - /// State is not available. - #[display(fmt = "State is not available")] - StatePruned, - /// State is incorrect. - #[display(fmt = "State is incorrect")] - StateIncorrect, - /// Wrong private transaction type. - #[display(fmt = "Wrong private transaction type")] - BadTransactionType, - /// Contract does not exist or was not created. - #[display(fmt = "Contract does not exist or was not created")] - ContractDoesNotExist, - /// Reference to the client is corrupted. - #[display(fmt = "Reference to the client is corrupted")] - ClientIsMalformed, - /// Queue of private transactions for verification is full. - #[display(fmt = "Queue of private transactions for verification is full")] - QueueIsFull, - /// The transaction already exists in queue of private transactions. - #[display(fmt = "The transaction already exists in queue of private transactions.")] - PrivateTransactionAlreadyImported, - /// The information about private transaction is not found in the store. - #[display(fmt = "The information about private transaction is not found in the store.")] - PrivateTransactionNotFound, - /// Account for signing public transactions not set. - #[display(fmt = "Account for signing public transactions not set.")] - SignerAccountNotSet, - /// Account for validating private transactions not set. - #[display(fmt = "Account for validating private transactions not set.")] - ValidatorAccountNotSet, - /// Account for signing requests to key server not set. - #[display(fmt = "Account for signing requests to key server not set.")] - KeyServerAccountNotSet, - /// Encryption key is not found on key server. - #[display(fmt = "Encryption key is not found on key server for {}", _0)] - EncryptionKeyNotFound(Address), - /// Key server URL is not set. - #[display(fmt = "Key server URL is not set.")] - KeyServerNotSet, - /// VM execution error. - #[display(fmt = "VM execution error {}", _0)] - Execution(ExecutionError), - /// General signing error. - #[display(fmt = "General signing error {}", _0)] - Key(KeyError), - /// Error of transactions processing. - #[display(fmt = "Error of transactions processing {}", _0)] - Transaction(TransactionError), - /// General ethcore error. - #[display(fmt = "General ethcore error {}", _0)] - Ethcore(EthcoreError), - /// A convenient variant for String. - #[display(fmt = "{}", _0)] - Msg(String), -} - -impl error::Error for Error { - fn source(&self) -> Option<&(error::Error + 'static)> { - match self { - Error::Io(e) => Some(e), - Error::Decoder(e) => Some(e), - Error::Trie(e) => Some(e), - Error::TxPool(e) => Some(e), - Error::Crypto(e) => Some(e), - Error::Execution(e) => Some(e), - Error::Key(e) => Some(e), - Error::Transaction(e) => Some(e), - Error::Ethcore(e) => Some(e), - _ => None, - } - } -} - -impl From for Error { - fn from(s: String) -> Self { - Error::Msg(s) - } -} - -impl From for Error { - fn from(err: std::io::Error) -> Self { - Error::Io(err).into() - } -} - -impl From for Error { - fn from(err: KeyError) -> Self { - Error::Key(err).into() - } -} - -impl From for Error { - fn from(err: CryptoError) -> Self { - Error::Crypto(err).into() - } -} - -impl From for Error { - fn from(err: DecoderError) -> Self { - Error::Decoder(err).into() - } -} - -impl From for Error { - fn from(err: ExecutionError) -> Self { - Error::Execution(err).into() - } -} - -impl From for Error { - fn from(err: TransactionError) -> Self { - Error::Transaction(err).into() - } -} - -impl From for Error { - fn from(err: TrieError) -> Self { - Error::Trie(err).into() - } -} - -impl From for Error { - fn from(err: TxPoolError) -> Self { - Error::TxPool(err).into() - } -} - -impl From for Error { - fn from(err: EthcoreError) -> Self { - Error::Ethcore(err).into() - } -} - -impl From> for Error where Error: From { - fn from(err: Box) -> Error { - Error::from(*err) - } -} diff --git a/ethcore/private-tx/src/key_server_keys.rs b/ethcore/private-tx/src/key_server_keys.rs deleted file mode 100644 index 28d9b3cb91d..00000000000 --- a/ethcore/private-tx/src/key_server_keys.rs +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -//! Wrapper around key server responsible for access keys processing. - -use std::sync::Arc; -use parking_lot::RwLock; -use ethereum_types::{H256, Address}; -use call_contract::{CallContract, RegistryInfo}; -use ethcore::client::BlockId; -use ethabi::FunctionOutputDecoder; - -const ACL_CHECKER_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_acl_checker"; - -use_contract!(keys_acl_contract, "res/keys_acl.json"); - -/// Returns the address (of the contract), that corresponds to the key -pub fn key_to_address(key: &H256) -> Address { - Address::from_slice(&key.to_vec()[..10]) -} - -/// Returns the key from the key server associated with the contract -pub fn address_to_key(contract_address: &Address) -> H256 { - // Current solution uses contract address extended with 0 as id - let contract_address_extended: H256 = contract_address.into(); - - H256::from_slice(&contract_address_extended) -} - -/// Trait for keys server keys provider. -pub trait KeyProvider: Send + Sync + 'static { - /// Account, that is used for communication with key server - fn key_server_account(&self) -> Option
; - - /// List of keys available for the account - fn available_keys(&self, block: BlockId, account: &Address) -> Option>; - - /// Update permissioning contract - fn update_acl_contract(&self); -} - -/// Secret Store keys provider -pub struct SecretStoreKeys where C: CallContract + RegistryInfo + Send + Sync + 'static { - client: Arc, - key_server_account: Option
, - keys_acl_contract: RwLock>, -} - -impl SecretStoreKeys where C: CallContract + RegistryInfo + Send + Sync + 'static { - /// Create provider - pub fn new(client: Arc, key_server_account: Option
) -> Self { - SecretStoreKeys { - client, - key_server_account, - keys_acl_contract: RwLock::new(None), - } - } -} - -impl KeyProvider for SecretStoreKeys where C: CallContract + RegistryInfo + Send + Sync + 'static { - fn key_server_account(&self) -> Option
{ - self.key_server_account - } - - fn available_keys(&self, block: BlockId, account: &Address) -> Option> { - match *self.keys_acl_contract.read() { - Some(acl_contract_address) => { - let (data, decoder) = keys_acl_contract::functions::available_keys::call(*account); - if let Ok(value) = self.client.call_contract(block, acl_contract_address, data) { - decoder.decode(&value).ok().map(|key_values| { - key_values.iter().map(key_to_address).collect() - }) - } else { - None - } - } - None => None, - } - } - - fn update_acl_contract(&self) { - let contract_address = self.client.registry_address(ACL_CHECKER_CONTRACT_REGISTRY_NAME.into(), BlockId::Latest); - if *self.keys_acl_contract.read() != contract_address { - trace!(target: "privatetx", "Configuring for ACL checker contract from address {:?}", - contract_address); - *self.keys_acl_contract.write() = contract_address; - } - } -} - -/// Dummy keys provider. -pub struct StoringKeyProvider { - available_keys: RwLock>>, - key_server_account: Option
, -} - -impl StoringKeyProvider { - /// Store available keys - pub fn set_available_keys(&self, keys: &Vec
) { - *self.available_keys.write() = Some(keys.clone()) - } -} - -impl Default for StoringKeyProvider { - fn default() -> Self { - StoringKeyProvider { - available_keys: RwLock::new(None), - key_server_account: Some(Address::default()), - } - } -} - -impl KeyProvider for StoringKeyProvider { - fn key_server_account(&self) -> Option
{ - self.key_server_account - } - - fn available_keys(&self, _block: BlockId, _account: &Address) -> Option> { - self.available_keys.read().clone() - } - - fn update_acl_contract(&self) {} -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use ethkey::{Secret, KeyPair}; - use bytes::Bytes; - use super::*; - - struct DummyRegistryClient { - registry_address: Option
, - } - - impl DummyRegistryClient { - pub fn new(registry_address: Option
) -> Self { - DummyRegistryClient { - registry_address - } - } - } - - impl RegistryInfo for DummyRegistryClient { - fn registry_address(&self, _name: String, _block: BlockId) -> Option
{ self.registry_address } - } - - impl CallContract for DummyRegistryClient { - fn call_contract(&self, _id: BlockId, _address: Address, _data: Bytes) -> Result { Ok(vec![]) } - } - - #[test] - fn should_update_acl_contract() { - let key = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000011")).unwrap(); - let client = DummyRegistryClient::new(Some(key.address())); - let keys_data = SecretStoreKeys::new(Arc::new(client), None); - keys_data.update_acl_contract(); - assert_eq!(keys_data.keys_acl_contract.read().unwrap(), key.address()); - } -} \ No newline at end of file diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs deleted file mode 100644 index d487b4d835b..00000000000 --- a/ethcore/private-tx/src/lib.rs +++ /dev/null @@ -1,782 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Private transactions module. - -// Recursion limit required because of -// error_chain foreign_links. -#![recursion_limit="256"] - -mod encryptor; -mod key_server_keys; -mod private_transactions; -mod messages; -mod error; - -extern crate common_types as types; -extern crate ethabi; -extern crate ethcore; -extern crate ethcore_call_contract as call_contract; -extern crate ethcore_io as io; -extern crate ethcore_miner; -extern crate ethereum_types; -extern crate ethjson; -extern crate ethkey; -extern crate fetch; -extern crate futures; -extern crate heapsize; -extern crate keccak_hash as hash; -extern crate parity_bytes as bytes; -extern crate parity_crypto as crypto; -extern crate parking_lot; -extern crate trie_db as trie; -extern crate patricia_trie_ethereum as ethtrie; -extern crate rlp; -extern crate rustc_hex; -extern crate transaction_pool as txpool; -extern crate url; -#[macro_use] -extern crate log; -#[macro_use] -extern crate ethabi_derive; -#[macro_use] -extern crate ethabi_contract; -extern crate derive_more; -#[macro_use] -extern crate rlp_derive; - -#[cfg(test)] -extern crate rand; -#[cfg(test)] -extern crate env_logger; - -pub use encryptor::{Encryptor, SecretStoreEncryptor, EncryptorConfig, NoopEncryptor}; -pub use key_server_keys::{KeyProvider, SecretStoreKeys, StoringKeyProvider}; -pub use private_transactions::{VerifiedPrivateTransaction, VerificationStore, PrivateTransactionSigningDesc, SigningStore}; -pub use messages::{PrivateTransaction, SignedPrivateTransaction}; -pub use error::Error; - -use std::sync::{Arc, Weak}; -use std::collections::{HashMap, HashSet, BTreeMap}; -use ethereum_types::{H128, H256, U256, Address}; -use hash::keccak; -use rlp::*; -use parking_lot::RwLock; -use bytes::Bytes; -use ethkey::{Signature, recover, public_to_address}; -use io::IoChannel; -use ethcore::executive::{Executive, TransactOptions}; -use ethcore::executed::{Executed}; -use types::transaction::{SignedTransaction, Transaction, Action, UnverifiedTransaction}; -use ethcore::{contract_address as ethcore_contract_address}; -use ethcore::client::{ - Client, ChainNotify, NewBlocks, ChainMessageType, ClientIoMessage, BlockId, - Call, BlockInfo -}; -use ethcore::miner::{self, Miner, MinerService, pool_client::NonceCache}; -use ethcore::{state, state_db}; -use ethcore::trace::{Tracer, VMTracer}; -use call_contract::CallContract; -use rustc_hex::FromHex; -use ethabi::FunctionOutputDecoder; - -// Source avaiable at https://github.com/parity-contracts/private-tx/blob/master/contracts/PrivateContract.sol -const DEFAULT_STUB_CONTRACT: &'static str = include_str!("../res/private.evm"); - -use_contract!(private_contract, "res/private.json"); - -/// Initialization vector length. -const INIT_VEC_LEN: usize = 16; - -/// Size of nonce cache -const NONCE_CACHE_SIZE: usize = 128; - -/// Version for the initial private contract wrapper -const INITIAL_PRIVATE_CONTRACT_VER: usize = 1; - -/// Version for the private contract notification about private state changes added -const PRIVATE_CONTRACT_WITH_NOTIFICATION_VER: usize = 2; - -/// Configurtion for private transaction provider -#[derive(Default, PartialEq, Debug, Clone)] -pub struct ProviderConfig { - /// Accounts that can be used for validation - pub validator_accounts: Vec
, - /// Account used for signing public transactions created from private transactions - pub signer_account: Option
, -} - -#[derive(Debug)] -/// Private transaction execution receipt. -pub struct Receipt { - /// Private transaction hash. - pub hash: H256, - /// Contract address. - pub contract_address: Address, - /// Execution status. - pub status_code: u8, -} - -/// Payload signing and decrypting capabilities. -pub trait Signer: Send + Sync { - /// Decrypt payload using private key of given address. - fn decrypt(&self, account: Address, shared_mac: &[u8], payload: &[u8]) -> Result, Error>; - /// Sign given hash using provided account. - fn sign(&self, account: Address, hash: ethkey::Message) -> Result; -} - -/// Signer implementation that errors on any request. -pub struct DummySigner; -impl Signer for DummySigner { - fn decrypt(&self, _account: Address, _shared_mac: &[u8], _payload: &[u8]) -> Result, Error> { - Err("Decrypting is not supported.".to_owned())? - } - - fn sign(&self, _account: Address, _hash: ethkey::Message) -> Result { - Err("Signing is not supported.".to_owned())? - } -} - -/// Signer implementation using multiple keypairs -pub struct KeyPairSigner(pub Vec); -impl Signer for KeyPairSigner { - fn decrypt(&self, account: Address, shared_mac: &[u8], payload: &[u8]) -> Result, Error> { - let kp = self.0.iter().find(|k| k.address() == account).ok_or(ethkey::Error::InvalidAddress)?; - Ok(ethkey::crypto::ecies::decrypt(kp.secret(), shared_mac, payload)?) - } - - fn sign(&self, account: Address, hash: ethkey::Message) -> Result { - let kp = self.0.iter().find(|k| k.address() == account).ok_or(ethkey::Error::InvalidAddress)?; - Ok(ethkey::sign(kp.secret(), &hash)?) - } -} - -/// Manager of private transactions -pub struct Provider { - encryptor: Box, - validator_accounts: HashSet
, - signer_account: Option
, - notify: RwLock>>, - transactions_for_signing: RwLock, - transactions_for_verification: VerificationStore, - client: Arc, - miner: Arc, - accounts: Arc, - channel: IoChannel, - keys_provider: Arc, -} - -#[derive(Debug)] -pub struct PrivateExecutionResult where T: Tracer, V: VMTracer { - code: Option, - state: Bytes, - contract_address: Address, - result: Executed, -} - -impl Provider { - /// Create a new provider. - pub fn new( - client: Arc, - miner: Arc, - accounts: Arc, - encryptor: Box, - config: ProviderConfig, - channel: IoChannel, - keys_provider: Arc, - ) -> Self { - keys_provider.update_acl_contract(); - Provider { - encryptor, - validator_accounts: config.validator_accounts.into_iter().collect(), - signer_account: config.signer_account, - notify: RwLock::default(), - transactions_for_signing: RwLock::default(), - transactions_for_verification: VerificationStore::default(), - client, - miner, - accounts, - channel, - keys_provider, - } - } - - // TODO [ToDr] Don't use `ChainNotify` here! - // Better to create a separate notification type for this. - /// Adds an actor to be notified on certain events - pub fn add_notify(&self, target: Arc) { - self.notify.write().push(Arc::downgrade(&target)); - } - - fn notify(&self, f: F) where F: Fn(&ChainNotify) { - for np in self.notify.read().iter() { - if let Some(n) = np.upgrade() { - f(&*n); - } - } - } - - /// 1. Create private transaction from the signed transaction - /// 2. Executes private transaction - /// 3. Save it with state returned on prev step to the queue for signing - /// 4. Broadcast corresponding message to the chain - pub fn create_private_transaction(&self, signed_transaction: SignedTransaction) -> Result { - trace!(target: "privatetx", "Creating private transaction from regular transaction: {:?}", signed_transaction); - if self.signer_account.is_none() { - warn!(target: "privatetx", "Signing account not set"); - return Err(Error::SignerAccountNotSet); - } - let tx_hash = signed_transaction.hash(); - let contract = Self::contract_address_from_transaction(&signed_transaction).map_err(|_| Error::BadTransactionType)?; - let data = signed_transaction.rlp_bytes(); - let encrypted_transaction = self.encrypt(&contract, &Self::iv_from_transaction(&signed_transaction), &data)?; - let private = PrivateTransaction::new(encrypted_transaction, contract); - // TODO #9825 [ToDr] Using BlockId::Latest is bad here, - // the block may change in the middle of execution - // causing really weird stuff to happen. - // We should retrieve hash and stick to that. IMHO - // best would be to change the API and only allow H256 instead of BlockID - // in private-tx to avoid such mistakes. - let contract_nonce = self.get_contract_nonce(&contract, BlockId::Latest)?; - let private_state = self.execute_private_transaction(BlockId::Latest, &signed_transaction)?; - trace!(target: "privatetx", "Private transaction created, encrypted transaction: {:?}, private state: {:?}", private, private_state); - let contract_validators = self.get_validators(BlockId::Latest, &contract)?; - trace!(target: "privatetx", "Required validators: {:?}", contract_validators); - let private_state_hash = self.calculate_state_hash(&private_state, contract_nonce); - trace!(target: "privatetx", "Hashed effective private state for sender: {:?}", private_state_hash); - self.transactions_for_signing.write().add_transaction(private.hash(), signed_transaction, contract_validators, private_state, contract_nonce)?; - self.broadcast_private_transaction(private.hash(), private.rlp_bytes()); - Ok(Receipt { - hash: tx_hash, - contract_address: contract, - status_code: 0, - }) - } - - /// Calculate hash from united private state and contract nonce - pub fn calculate_state_hash(&self, state: &Bytes, nonce: U256) -> H256 { - let state_hash = keccak(state); - let mut state_buf = [0u8; 64]; - state_buf[..32].clone_from_slice(&state_hash); - state_buf[32..].clone_from_slice(&H256::from(nonce)); - keccak(&state_buf.as_ref()) - } - - fn pool_client<'a>(&'a self, nonce_cache: &'a NonceCache, local_accounts: &'a HashSet
) -> miner::pool_client::PoolClient<'a, Client> { - let engine = self.client.engine(); - miner::pool_client::PoolClient::new( - &*self.client, - nonce_cache, - engine, - local_accounts, - None, // refuse_service_transactions = true - ) - } - - /// Retrieve and verify the first available private transaction for every sender - fn process_verification_queue(&self) -> Result<(), Error> { - let process_transaction = |transaction: &VerifiedPrivateTransaction| -> Result<_, String> { - let private_hash = transaction.private_transaction.hash(); - match transaction.validator_account { - None => { - trace!(target: "privatetx", "Propagating transaction further"); - self.broadcast_private_transaction(private_hash, transaction.private_transaction.rlp_bytes()); - return Ok(()); - } - Some(validator_account) => { - if !self.validator_accounts.contains(&validator_account) { - trace!(target: "privatetx", "Propagating transaction further"); - self.broadcast_private_transaction(private_hash, transaction.private_transaction.rlp_bytes()); - return Ok(()); - } - let contract = Self::contract_address_from_transaction(&transaction.transaction) - .map_err(|_| "Incorrect type of action for the transaction")?; - // TODO #9825 [ToDr] Usage of BlockId::Latest - let contract_nonce = self.get_contract_nonce(&contract, BlockId::Latest); - if let Err(e) = contract_nonce { - return Err(format!("Cannot retrieve contract nonce: {:?}", e).into()); - } - let contract_nonce = contract_nonce.expect("Error was checked before"); - let private_state = self.execute_private_transaction(BlockId::Latest, &transaction.transaction); - if let Err(e) = private_state { - return Err(format!("Cannot retrieve private state: {:?}", e).into()); - } - let private_state = private_state.expect("Error was checked before"); - let private_state_hash = self.calculate_state_hash(&private_state, contract_nonce); - trace!(target: "privatetx", "Hashed effective private state for validator: {:?}", private_state_hash); - let signed_state = self.accounts.sign(validator_account, private_state_hash); - if let Err(e) = signed_state { - return Err(format!("Cannot sign the state: {:?}", e).into()); - } - let signed_state = signed_state.expect("Error was checked before"); - let signed_private_transaction = SignedPrivateTransaction::new(private_hash, signed_state, None); - trace!(target: "privatetx", "Sending signature for private transaction: {:?}", signed_private_transaction); - self.broadcast_signed_private_transaction(signed_private_transaction.hash(), signed_private_transaction.rlp_bytes()); - } - } - Ok(()) - }; - let nonce_cache = NonceCache::new(NONCE_CACHE_SIZE); - let local_accounts = HashSet::new(); - let ready_transactions = self.transactions_for_verification.drain(self.pool_client(&nonce_cache, &local_accounts)); - for transaction in ready_transactions { - if let Err(e) = process_transaction(&transaction) { - warn!(target: "privatetx", "Error: {:?}", e); - } - } - Ok(()) - } - - /// Add signed private transaction into the store - /// Creates corresponding public transaction if last required signature collected and sends it to the chain - pub fn process_signature(&self, signed_tx: &SignedPrivateTransaction) -> Result<(), Error> { - trace!(target: "privatetx", "Processing signed private transaction"); - let private_hash = signed_tx.private_transaction_hash(); - let desc = match self.transactions_for_signing.read().get(&private_hash) { - None => { - // Not our transaction, broadcast further to peers - self.broadcast_signed_private_transaction(signed_tx.hash(), signed_tx.rlp_bytes()); - return Ok(()); - }, - Some(desc) => desc, - }; - let last = self.last_required_signature(&desc, signed_tx.signature())?; - - if last { - let mut signatures = desc.received_signatures.clone(); - signatures.push(signed_tx.signature()); - let rsv: Vec = signatures.into_iter().map(|sign| sign.into_electrum().into()).collect(); - // Create public transaction - let signer_account = self.signer_account.ok_or_else(|| Error::SignerAccountNotSet)?; - let state = self.client.state_at(BlockId::Latest).ok_or(Error::StatePruned)?; - let nonce = state.nonce(&signer_account)?; - let public_tx = self.public_transaction( - desc.state.clone(), - &desc.original_transaction, - &rsv, - nonce, - desc.original_transaction.gas_price - )?; - trace!(target: "privatetx", "Last required signature received, public transaction created: {:?}", public_tx); - // Sign and add it to the queue - let chain_id = desc.original_transaction.chain_id(); - let hash = public_tx.hash(chain_id); - let signature = self.accounts.sign(signer_account, hash)?; - let signed = SignedTransaction::new(public_tx.with_signature(signature, chain_id))?; - match self.miner.import_own_transaction(&*self.client, signed.into()) { - Ok(_) => trace!(target: "privatetx", "Public transaction added to queue"), - Err(err) => { - warn!(target: "privatetx", "Failed to add transaction to queue, error: {:?}", err); - return Err(err.into()); - } - } - // Notify about state changes - let contract = Self::contract_address_from_transaction(&desc.original_transaction)?; - // TODO #9825 Usage of BlockId::Latest - if self.get_contract_version(BlockId::Latest, &contract) >= PRIVATE_CONTRACT_WITH_NOTIFICATION_VER { - match self.state_changes_notify(BlockId::Latest, &contract, &desc.original_transaction.sender(), desc.original_transaction.hash()) { - Ok(_) => trace!(target: "privatetx", "Notification about private state changes sent"), - Err(err) => warn!(target: "privatetx", "Failed to send private state changed notification, error: {:?}", err), - } - } - // Remove from store for signing - if let Err(err) = self.transactions_for_signing.write().remove(&private_hash) { - warn!(target: "privatetx", "Failed to remove transaction from signing store, error: {:?}", err); - return Err(err); - } - } else { - // Add signature to the store - match self.transactions_for_signing.write().add_signature(&private_hash, signed_tx.signature()) { - Ok(_) => trace!(target: "privatetx", "Signature stored for private transaction"), - Err(err) => { - warn!(target: "privatetx", "Failed to add signature to signing store, error: {:?}", err); - return Err(err); - } - } - } - Ok(()) - } - - fn contract_address_from_transaction(transaction: &SignedTransaction) -> Result { - match transaction.action { - Action::Call(contract) => Ok(contract), - _ => { - warn!(target: "privatetx", "Incorrect type of action for the transaction"); - return Err(Error::BadTransactionType); - } - } - } - - fn last_required_signature(&self, desc: &PrivateTransactionSigningDesc, sign: Signature) -> Result { - if desc.received_signatures.contains(&sign) { - return Ok(false); - } - let state_hash = self.calculate_state_hash(&desc.state, desc.contract_nonce); - match recover(&sign, &state_hash) { - Ok(public) => { - let sender = public_to_address(&public); - match desc.validators.contains(&sender) { - true => { - Ok(desc.received_signatures.len() + 1 == desc.validators.len()) - } - false => { - warn!(target: "privatetx", "Sender's state doesn't correspond to validator's"); - return Err(Error::StateIncorrect); - } - } - } - Err(err) => { - warn!(target: "privatetx", "Sender's state doesn't correspond to validator's, error {:?}", err); - return Err(err.into()); - } - } - } - - /// Broadcast the private transaction message to the chain - fn broadcast_private_transaction(&self, transaction_hash: H256, message: Bytes) { - self.notify(|notify| notify.broadcast(ChainMessageType::PrivateTransaction(transaction_hash, message.clone()))); - } - - /// Broadcast signed private transaction message to the chain - fn broadcast_signed_private_transaction(&self, transaction_hash: H256, message: Bytes) { - self.notify(|notify| notify.broadcast(ChainMessageType::SignedPrivateTransaction(transaction_hash, message.clone()))); - } - - fn iv_from_transaction(transaction: &SignedTransaction) -> H128 { - let nonce = keccak(&transaction.nonce.rlp_bytes()); - let (iv, _) = nonce.split_at(INIT_VEC_LEN); - H128::from_slice(iv) - } - - fn iv_from_address(contract_address: &Address) -> H128 { - let address = keccak(&contract_address.rlp_bytes()); - let (iv, _) = address.split_at(INIT_VEC_LEN); - H128::from_slice(iv) - } - - fn encrypt(&self, contract_address: &Address, initialisation_vector: &H128, data: &[u8]) -> Result { - trace!(target: "privatetx", "Encrypt data using key(address): {:?}", contract_address); - Ok(self.encryptor.encrypt(contract_address, initialisation_vector, data)?) - } - - fn decrypt(&self, contract_address: &Address, data: &[u8]) -> Result { - trace!(target: "privatetx", "Decrypt data using key(address): {:?}", contract_address); - Ok(self.encryptor.decrypt(contract_address, data)?) - } - - fn get_decrypted_state(&self, address: &Address, block: BlockId) -> Result { - let (data, decoder) = private_contract::functions::state::call(); - let value = self.client.call_contract(block, *address, data)?; - let state = decoder.decode(&value).map_err(|e| Error::Call(format!("Contract call failed {:?}", e)))?; - self.decrypt(address, &state) - } - - fn get_decrypted_code(&self, address: &Address, block: BlockId) -> Result { - let (data, decoder) = private_contract::functions::code::call(); - let value = self.client.call_contract(block, *address, data)?; - let state = decoder.decode(&value).map_err(|e| Error::Call(format!("Contract call failed {:?}", e)))?; - self.decrypt(address, &state) - } - - pub fn get_contract_nonce(&self, address: &Address, block: BlockId) -> Result { - let (data, decoder) = private_contract::functions::nonce::call(); - let value = self.client.call_contract(block, *address, data)?; - decoder.decode(&value).map_err(|e| Error::Call(format!("Contract call failed {:?}", e)).into()) - } - - fn snapshot_to_storage(raw: Bytes) -> HashMap { - let items = raw.len() / 64; - (0..items).map(|i| { - let offset = i * 64; - let key = H256::from_slice(&raw[offset..(offset + 32)]); - let value = H256::from_slice(&raw[(offset + 32)..(offset + 64)]); - (key, value) - }).collect() - } - - fn snapshot_from_storage(storage: &HashMap) -> Bytes { - let mut raw = Vec::with_capacity(storage.len() * 64); - // Sort the storage to guarantee the order for all parties - let sorted_storage: BTreeMap<&H256, &H256> = storage.iter().collect(); - for (key, value) in sorted_storage { - raw.extend_from_slice(key); - raw.extend_from_slice(value); - }; - raw - } - - fn patch_account_state(&self, contract_address: &Address, block: BlockId, state: &mut state::State) -> Result<(), Error> { - let contract_code = Arc::new(self.get_decrypted_code(contract_address, block)?); - let contract_state = self.get_decrypted_state(contract_address, block)?; - trace!(target: "privatetx", "Patching contract at {:?}, code: {:?}, state: {:?}", contract_address, contract_code, contract_state); - state.patch_account(contract_address, contract_code, Self::snapshot_to_storage(contract_state))?; - Ok(()) - } - - pub fn execute_private(&self, transaction: &SignedTransaction, options: TransactOptions, block: BlockId) -> Result, Error> - where - T: Tracer, - V: VMTracer, - { - let mut env_info = self.client.env_info(block).ok_or(Error::StatePruned)?; - env_info.gas_limit = transaction.gas; - - let mut state = self.client.state_at(block).ok_or(Error::StatePruned)?; - // TODO #9825 in case of BlockId::Latest these need to operate on the same state - let contract_address = match transaction.action { - Action::Call(ref contract_address) => { - // Patch current contract state - self.patch_account_state(contract_address, block, &mut state)?; - Some(*contract_address) - }, - Action::Create => None, - }; - - let engine = self.client.engine(); - let sender = transaction.sender(); - let nonce = state.nonce(&sender)?; - let contract_address = contract_address.unwrap_or_else(|| { - let (new_address, _) = ethcore_contract_address(engine.create_address_scheme(env_info.number), &sender, &nonce, &transaction.data); - new_address - }); - // Patch other available private contracts' states as well - // TODO: #10133 patch only required for the contract states - if let Some(key_server_account) = self.keys_provider.key_server_account() { - if let Some(available_contracts) = self.keys_provider.available_keys(block, &key_server_account) { - for private_contract in available_contracts { - if private_contract == contract_address { - continue; - } - self.patch_account_state(&private_contract, block, &mut state)?; - } - } - } - let machine = engine.machine(); - let schedule = machine.schedule(env_info.number); - let result = Executive::new(&mut state, &env_info, &machine, &schedule).transact_virtual(transaction, options)?; - let (encrypted_code, encrypted_storage) = { - let (code, storage) = state.into_account(&contract_address)?; - trace!(target: "privatetx", "Private contract executed. code: {:?}, state: {:?}, result: {:?}", code, storage, result.output); - let enc_code = match code { - Some(c) => Some(self.encrypt(&contract_address, &Self::iv_from_address(&contract_address), &c)?), - None => None, - }; - (enc_code, self.encrypt(&contract_address, &Self::iv_from_transaction(transaction), &Self::snapshot_from_storage(&storage))?) - }; - Ok(PrivateExecutionResult { - code: encrypted_code, - state: encrypted_storage, - contract_address: contract_address, - result, - }) - } - - fn generate_constructor(validators: &[Address], code: Bytes, storage: Bytes) -> Bytes { - let constructor_code = DEFAULT_STUB_CONTRACT.from_hex().expect("Default contract code is valid"); - private_contract::constructor(constructor_code, validators.iter().map(|a| *a).collect::>(), code, storage) - } - - fn generate_set_state_call(signatures: &[Signature], storage: Bytes) -> Bytes { - private_contract::functions::set_state::encode_input( - storage, - signatures.iter().map(|s| { - let mut v: [u8; 32] = [0; 32]; - v[31] = s.v(); - v - }).collect::>(), - signatures.iter().map(|s| s.r()).collect::>(), - signatures.iter().map(|s| s.s()).collect::>() - ) - } - - /// Returns the key from the key server associated with the contract - pub fn contract_key_id(&self, contract_address: &Address) -> Result { - Ok(key_server_keys::address_to_key(contract_address)) - } - - /// Create encrypted public contract deployment transaction. - pub fn public_creation_transaction(&self, block: BlockId, source: &SignedTransaction, validators: &[Address], gas_price: U256) -> Result<(Transaction, Address), Error> { - if let Action::Call(_) = source.action { - return Err(Error::BadTransactionType); - } - let sender = source.sender(); - let state = self.client.state_at(block).ok_or(Error::StatePruned)?; - let nonce = state.nonce(&sender)?; - let executed = self.execute_private(source, TransactOptions::with_no_tracing(), block)?; - let header = self.client.block_header(block) - .ok_or(Error::StatePruned) - .and_then(|h| h.decode().map_err(|_| Error::StateIncorrect).into())?; - let (executed_code, executed_state) = (executed.code.unwrap_or_default(), executed.state); - let tx_data = Self::generate_constructor(validators, executed_code.clone(), executed_state.clone()); - let mut tx = Transaction { - nonce: nonce, - action: Action::Create, - gas: u64::max_value().into(), - gas_price: gas_price, - value: source.value, - data: tx_data, - }; - tx.gas = match self.client.estimate_gas(&tx.clone().fake_sign(sender), &state, &header) { - Ok(estimated_gas) => estimated_gas, - Err(_) => self.estimate_tx_gas(validators, &executed_code, &executed_state, &[]), - }; - - Ok((tx, executed.contract_address)) - } - - fn estimate_tx_gas(&self, validators: &[Address], code: &Bytes, state: &Bytes, signatures: &[Signature]) -> U256 { - let default_gas = 650000 + - validators.len() as u64 * 30000 + - code.len() as u64 * 8000 + - signatures.len() as u64 * 50000 + - state.len() as u64 * 8000; - default_gas.into() - } - - /// Create encrypted public contract deployment transaction. Returns updated encrypted state. - pub fn execute_private_transaction(&self, block: BlockId, source: &SignedTransaction) -> Result { - if let Action::Create = source.action { - return Err(Error::BadTransactionType); - } - let result = self.execute_private(source, TransactOptions::with_no_tracing(), block)?; - Ok(result.state) - } - - /// Create encrypted public transaction from private transaction. - pub fn public_transaction(&self, state: Bytes, source: &SignedTransaction, signatures: &[Signature], nonce: U256, gas_price: U256) -> Result { - let gas = self.estimate_tx_gas(&[], &Vec::new(), &state, signatures); - Ok(Transaction { - nonce: nonce, - action: source.action.clone(), - gas: gas.into(), - gas_price: gas_price, - value: 0.into(), - data: Self::generate_set_state_call(signatures, state) - }) - } - - /// Call into private contract. - pub fn private_call(&self, block: BlockId, transaction: &SignedTransaction) -> Result { - let result = self.execute_private(transaction, TransactOptions::with_no_tracing(), block)?; - Ok(result.result) - } - - /// Returns private validators for a contract. - pub fn get_validators(&self, block: BlockId, address: &Address) -> Result, Error> { - let (data, decoder) = private_contract::functions::get_validators::call(); - let value = self.client.call_contract(block, *address, data)?; - decoder.decode(&value).map_err(|e| Error::Call(format!("Contract call failed {:?}", e)).into()) - } - - fn get_contract_version(&self, block: BlockId, address: &Address) -> usize { - let (data, decoder) = private_contract::functions::get_version::call(); - match self.client.call_contract(block, *address, data) - .and_then(|value| decoder.decode(&value).map_err(|e| e.to_string())) { - Ok(version) => version.low_u64() as usize, - Err(_) => INITIAL_PRIVATE_CONTRACT_VER, - } - } - - fn state_changes_notify(&self, block: BlockId, address: &Address, originator: &Address, transaction_hash: H256) -> Result<(), Error> { - let (data, _) = private_contract::functions::notify_changes::call(*originator, transaction_hash.0.to_vec()); - let _value = self.client.call_contract(block, *address, data)?; - Ok(()) - } -} - -pub trait Importer { - /// Process received private transaction - fn import_private_transaction(&self, _rlp: &[u8]) -> Result; - - /// Add signed private transaction into the store - /// - /// Creates corresponding public transaction if last required signature collected and sends it to the chain - fn import_signed_private_transaction(&self, _rlp: &[u8]) -> Result; -} - -// TODO [ToDr] Offload more heavy stuff to the IoService thread. -// It seems that a lot of heavy work (verification) is done in this thread anyway -// it might actually make sense to decouple it from clientService and just use dedicated thread -// for both verification and execution. - -impl Importer for Arc { - fn import_private_transaction(&self, rlp: &[u8]) -> Result { - trace!(target: "privatetx", "Private transaction received"); - let private_tx: PrivateTransaction = Rlp::new(rlp).as_val()?; - let private_tx_hash = private_tx.hash(); - let contract = private_tx.contract(); - let contract_validators = self.get_validators(BlockId::Latest, &contract)?; - - let validation_account = contract_validators - .iter() - .find(|address| self.validator_accounts.contains(address)); - - // Extract the original transaction - let encrypted_data = private_tx.encrypted(); - let transaction_bytes = self.decrypt(&contract, &encrypted_data)?; - let original_tx: UnverifiedTransaction = Rlp::new(&transaction_bytes).as_val()?; - let nonce_cache = NonceCache::new(NONCE_CACHE_SIZE); - let local_accounts = HashSet::new(); - // Add to the queue for further verification - self.transactions_for_verification.add_transaction( - original_tx, - validation_account.map(|&account| account), - private_tx, - self.pool_client(&nonce_cache, &local_accounts), - )?; - let provider = Arc::downgrade(self); - let result = self.channel.send(ClientIoMessage::execute(move |_| { - if let Some(provider) = provider.upgrade() { - if let Err(e) = provider.process_verification_queue() { - warn!(target: "privatetx", "Unable to process the queue: {}", e); - } - } - })); - if let Err(e) = result { - warn!(target: "privatetx", "Error sending NewPrivateTransaction message: {:?}", e); - } - Ok(private_tx_hash) - } - - fn import_signed_private_transaction(&self, rlp: &[u8]) -> Result { - let tx: SignedPrivateTransaction = Rlp::new(rlp).as_val()?; - trace!(target: "privatetx", "Signature for private transaction received: {:?}", tx); - let private_hash = tx.private_transaction_hash(); - let provider = Arc::downgrade(self); - let result = self.channel.send(ClientIoMessage::execute(move |_| { - if let Some(provider) = provider.upgrade() { - if let Err(e) = provider.process_signature(&tx) { - warn!(target: "privatetx", "Unable to process the signature: {}", e); - } - } - })); - if let Err(e) = result { - warn!(target: "privatetx", "Error sending NewSignedPrivateTransaction message: {:?}", e); - } - Ok(private_hash) - } -} - -impl ChainNotify for Provider { - fn new_blocks(&self, new_blocks: NewBlocks) { - if new_blocks.imported.is_empty() || new_blocks.has_more_blocks_to_import { return } - trace!(target: "privatetx", "New blocks imported, try to prune the queue"); - if let Err(err) = self.process_verification_queue() { - warn!(target: "privatetx", "Cannot prune private transactions queue. error: {:?}", err); - } - self.keys_provider.update_acl_contract(); - } -} diff --git a/ethcore/private-tx/src/messages.rs b/ethcore/private-tx/src/messages.rs deleted file mode 100644 index 2990fb9b09f..00000000000 --- a/ethcore/private-tx/src/messages.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use ethereum_types::{H256, U256, Address}; -use bytes::Bytes; -use hash::keccak; -use rlp::Encodable; -use ethkey::Signature; -use types::transaction::signature::{add_chain_replay_protection, check_replay_protection}; - -/// Message with private transaction encrypted -#[derive(Default, Debug, Clone, PartialEq, RlpEncodable, RlpDecodable, Eq)] -pub struct PrivateTransaction { - /// Encrypted data - encrypted: Bytes, - /// Address of the contract - contract: Address, - /// Hash - hash: H256, -} - -impl PrivateTransaction { - /// Constructor - pub fn new(encrypted: Bytes, contract: Address) -> Self { - PrivateTransaction { - encrypted, - contract, - hash: 0.into(), - }.compute_hash() - } - - fn compute_hash(mut self) -> PrivateTransaction { - self.hash = keccak(&*self.rlp_bytes()); - self - } - - /// Hash of the private transaction - pub fn hash(&self) -> H256 { - self.hash - } - - /// Address of the contract - pub fn contract(&self) -> Address { - self.contract - } - - /// Encrypted data - pub fn encrypted(&self) -> Bytes { - self.encrypted.clone() - } -} - -/// Message about private transaction's signing -#[derive(Default, Debug, Clone, PartialEq, RlpEncodable, RlpDecodable, Eq)] -pub struct SignedPrivateTransaction { - /// Hash of the corresponding private transaction - private_transaction_hash: H256, - /// Signature of the validator - /// The V field of the signature - v: u64, - /// The R field of the signature - r: U256, - /// The S field of the signature - s: U256, - /// Hash - hash: H256, -} - -impl SignedPrivateTransaction { - /// Construct a signed private transaction message - pub fn new(private_transaction_hash: H256, sig: Signature, chain_id: Option) -> Self { - SignedPrivateTransaction { - private_transaction_hash: private_transaction_hash, - r: sig.r().into(), - s: sig.s().into(), - v: add_chain_replay_protection(sig.v() as u64, chain_id), - hash: 0.into(), - }.compute_hash() - } - - fn compute_hash(mut self) -> SignedPrivateTransaction { - self.hash = keccak(&*self.rlp_bytes()); - self - } - - pub fn standard_v(&self) -> u8 { check_replay_protection(self.v) } - - /// Construct a signature object from the sig. - pub fn signature(&self) -> Signature { - Signature::from_rsv(&self.r.into(), &self.s.into(), self.standard_v()) - } - - /// Get the hash of of the original transaction. - pub fn private_transaction_hash(&self) -> H256 { - self.private_transaction_hash - } - - /// Own hash - pub fn hash(&self) -> H256 { - self.hash - } -} diff --git a/ethcore/private-tx/src/private_transactions.rs b/ethcore/private-tx/src/private_transactions.rs deleted file mode 100644 index d0456657b06..00000000000 --- a/ethcore/private-tx/src/private_transactions.rs +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::cmp; -use std::collections::{HashMap, HashSet}; - -use bytes::Bytes; -use ethcore_miner::pool; -use ethereum_types::{H256, U256, Address}; -use heapsize::HeapSizeOf; -use ethkey::Signature; -use messages::PrivateTransaction; -use parking_lot::RwLock; -use types::transaction::{UnverifiedTransaction, SignedTransaction}; -use txpool; -use txpool::{VerifiedTransaction, Verifier}; -use error::Error; - -type Pool = txpool::Pool; - -/// Maximum length for private transactions queues. -const MAX_QUEUE_LEN: usize = 8312; - -/// Private transaction stored in queue for verification -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct VerifiedPrivateTransaction { - /// Original private transaction - pub private_transaction: PrivateTransaction, - /// Address that should be used for verification - pub validator_account: Option
, - /// Resulting verified transaction - pub transaction: SignedTransaction, - /// Original transaction hash - pub transaction_hash: H256, - /// Original transaction sender - pub transaction_sender: Address, -} - -impl txpool::VerifiedTransaction for VerifiedPrivateTransaction { - type Hash = H256; - type Sender = Address; - - fn hash(&self) -> &H256 { - &self.transaction_hash - } - - fn mem_usage(&self) -> usize { - self.transaction.heap_size_of_children() - } - - fn sender(&self) -> &Address { - &self.transaction_sender - } -} - -impl pool::ScoredTransaction for VerifiedPrivateTransaction { - fn priority(&self) -> pool::Priority { - pool::Priority::Regular - } - - /// Gets transaction gas price. - fn gas_price(&self) -> &U256 { - &self.transaction.gas_price - } - - /// Gets transaction nonce. - fn nonce(&self) -> U256 { - self.transaction.nonce - } -} - -/// Checks readiness of transactions by looking if the transaction from sender already exists. -/// Guarantees only one transaction per sender -#[derive(Debug)] -pub struct PrivateReadyState { - senders: HashSet
, - state: C, -} - -impl PrivateReadyState { - /// Create new State checker, given client interface. - pub fn new( - state: C, - ) -> Self { - PrivateReadyState { - senders: Default::default(), - state, - } - } -} - -impl txpool::Ready for PrivateReadyState { - fn is_ready(&mut self, tx: &VerifiedPrivateTransaction) -> txpool::Readiness { - let sender = tx.sender(); - let state = &self.state; - let state_nonce = state.account_nonce(sender); - if self.senders.contains(sender) { - txpool::Readiness::Future - } else { - self.senders.insert(*sender); - match tx.transaction.nonce.cmp(&state_nonce) { - cmp::Ordering::Greater => txpool::Readiness::Future, - cmp::Ordering::Less => txpool::Readiness::Stale, - cmp::Ordering::Equal => txpool::Readiness::Ready, - } - } - } -} - -/// Storage for private transactions for verification -pub struct VerificationStore { - verification_pool: RwLock, - verification_options: pool::verifier::Options, -} - -impl Default for VerificationStore { - fn default() -> Self { - VerificationStore { - verification_pool: RwLock::new( - txpool::Pool::new( - txpool::NoopListener, - pool::scoring::NonceAndGasPrice(pool::PrioritizationStrategy::GasPriceOnly), - pool::Options { - max_count: MAX_QUEUE_LEN, - max_per_sender: MAX_QUEUE_LEN / 10, - max_mem_usage: 8 * 1024 * 1024, - }, - ) - ), - verification_options: pool::verifier::Options { - // TODO [ToDr] This should probably be based on some real values? - minimal_gas_price: 0.into(), - block_gas_limit: 8_000_000.into(), - tx_gas_limit: U256::max_value(), - no_early_reject: false, - }, - } - } -} - -impl VerificationStore { - /// Adds private transaction for verification into the store - pub fn add_transaction( - &self, - transaction: UnverifiedTransaction, - validator_account: Option
, - private_transaction: PrivateTransaction, - client: C, - ) -> Result<(), Error> { - - let options = self.verification_options.clone(); - // Use pool's verifying pipeline for original transaction's verification - let verifier = pool::verifier::Verifier::new(client.clone(), options, Default::default(), None); - let unverified = pool::verifier::Transaction::Unverified(transaction); - let verified_tx = verifier.verify_transaction(unverified)?; - let signed_tx: SignedTransaction = verified_tx.signed().clone(); - let signed_hash = signed_tx.hash(); - let signed_sender = signed_tx.sender(); - let verified = VerifiedPrivateTransaction { - private_transaction, - validator_account, - transaction: signed_tx, - transaction_hash: signed_hash, - transaction_sender: signed_sender, - }; - let replace = pool::replace::ReplaceByScoreAndReadiness::new( - self.verification_pool.read().scoring().clone(), client); - self.verification_pool.write().import(verified, &replace)?; - Ok(()) - } - - /// Drains transactions ready for verification from the pool - /// Returns only one transaction per sender because several cannot be verified in a row without verification from other peers - pub fn drain(&self, client: C) -> Vec> { - let ready = PrivateReadyState::new(client); - let transactions: Vec<_> = self.verification_pool.read().pending(ready).collect(); - let mut pool = self.verification_pool.write(); - for tx in &transactions { - pool.remove(tx.hash(), true); - } - transactions - } -} - -/// Desriptor for private transaction stored in queue for signing -#[derive(Debug, Clone)] -pub struct PrivateTransactionSigningDesc { - /// Original unsigned transaction - pub original_transaction: SignedTransaction, - /// Supposed validators from the contract - pub validators: Vec
, - /// Already obtained signatures - pub received_signatures: Vec, - /// State after transaction execution to compare further with received from validators - pub state: Bytes, - /// Build-in nonce of the contract - pub contract_nonce: U256, -} - -/// Storage for private transactions for signing -#[derive(Default)] -pub struct SigningStore { - /// Transactions and descriptors for signing - transactions: HashMap, -} - -impl SigningStore { - /// Adds new private transaction into the store for signing - pub fn add_transaction( - &mut self, - private_hash: H256, - transaction: SignedTransaction, - validators: Vec
, - state: Bytes, - contract_nonce: U256, - ) -> Result<(), Error> { - if self.transactions.len() > MAX_QUEUE_LEN { - return Err(Error::QueueIsFull); - } - - self.transactions.insert(private_hash, PrivateTransactionSigningDesc { - original_transaction: transaction.clone(), - validators: validators.clone(), - received_signatures: Vec::new(), - state, - contract_nonce, - }); - Ok(()) - } - - /// Get copy of private transaction's description from the storage - pub fn get(&self, private_hash: &H256) -> Option { - self.transactions.get(private_hash).cloned() - } - - /// Removes desc from the store (after verification is completed) - pub fn remove(&mut self, private_hash: &H256) -> Result<(), Error> { - self.transactions.remove(private_hash); - Ok(()) - } - - /// Adds received signature for the stored private transaction - pub fn add_signature(&mut self, private_hash: &H256, signature: Signature) -> Result<(), Error> { - let desc = self.transactions.get_mut(private_hash).ok_or_else(|| Error::PrivateTransactionNotFound)?; - if !desc.received_signatures.contains(&signature) { - desc.received_signatures.push(signature); - } - Ok(()) - } -} diff --git a/ethcore/private-tx/tests/private_contract.rs b/ethcore/private-tx/tests/private_contract.rs deleted file mode 100644 index 6365b10eecd..00000000000 --- a/ethcore/private-tx/tests/private_contract.rs +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Contract for private transactions tests. - -extern crate common_types as types; -extern crate env_logger; -extern crate ethcore; -extern crate ethcore_io; -extern crate ethcore_private_tx; -extern crate ethkey; -extern crate keccak_hash as hash; -extern crate rustc_hex; - -#[macro_use] -extern crate log; - -use std::sync::Arc; -use rustc_hex::{FromHex, ToHex}; - -use types::ids::BlockId; -use types::transaction::{Transaction, Action}; -use ethcore::CreateContractAddress; -use ethcore::client::BlockChainClient; -use ethcore::executive::{contract_address}; -use ethcore::miner::Miner; -use ethcore::test_helpers::{generate_dummy_client, push_block_with_transactions}; -use ethkey::{Secret, KeyPair, Signature}; -use hash::keccak; - -use ethcore_private_tx::{NoopEncryptor, Provider, ProviderConfig, StoringKeyProvider}; - -#[test] -fn private_contract() { - // This uses a simple private contract: contract Test1 { bytes32 public x; function setX(bytes32 _x) { x = _x; } } - let _ = ::env_logger::try_init(); - let client = generate_dummy_client(0); - let chain_id = client.signing_chain_id(); - let key1 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000011")).unwrap(); - let _key2 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000012")).unwrap(); - let key3 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000013")).unwrap(); - let key4 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000014")).unwrap(); - - let signer = Arc::new(ethcore_private_tx::KeyPairSigner(vec![key1.clone(), key3.clone(), key4.clone()])); - - let config = ProviderConfig{ - validator_accounts: vec![key3.address(), key4.address()], - signer_account: None, - }; - - let io = ethcore_io::IoChannel::disconnected(); - let miner = Arc::new(Miner::new_for_tests(&::ethcore::spec::Spec::new_test(), None)); - let private_keys = Arc::new(StoringKeyProvider::default()); - let pm = Arc::new(Provider::new( - client.clone(), - miner, - signer.clone(), - Box::new(NoopEncryptor::default()), - config, - io, - private_keys, - )); - - let (address, _) = contract_address(CreateContractAddress::FromSenderAndNonce, &key1.address(), &0.into(), &[]); - - trace!("Creating private contract"); - let private_contract_test = "6060604052341561000f57600080fd5b60d88061001d6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630c55699c146046578063bc64b76d14607457600080fd5b3415605057600080fd5b60566098565b60405180826000191660001916815260200191505060405180910390f35b3415607e57600080fd5b6096600480803560001916906020019091905050609e565b005b60005481565b8060008160001916905550505600a165627a7a723058206acbdf4b15ca4c2d43e1b1879b830451a34f1e9d02ff1f2f394d8d857e79d2080029".from_hex().unwrap(); - let mut private_create_tx = Transaction::default(); - private_create_tx.action = Action::Create; - private_create_tx.data = private_contract_test; - private_create_tx.gas = 200000.into(); - let private_create_tx_signed = private_create_tx.sign(&key1.secret(), None); - let validators = vec![key3.address(), key4.address()]; - let (public_tx, _) = pm.public_creation_transaction(BlockId::Latest, &private_create_tx_signed, &validators, 0.into()).unwrap(); - let public_tx = public_tx.sign(&key1.secret(), chain_id); - trace!("Transaction created. Pushing block"); - push_block_with_transactions(&client, &[public_tx]); - - trace!("Querying default private state"); - let mut query_tx = Transaction::default(); - query_tx.action = Action::Call(address.clone()); - query_tx.data = "0c55699c".from_hex().unwrap(); // getX - query_tx.gas = 50000.into(); - query_tx.nonce = 1.into(); - let query_tx = query_tx.sign(&key1.secret(), chain_id); - let result = pm.private_call(BlockId::Latest, &query_tx).unwrap(); - assert_eq!(&result.output[..], &("0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap()[..])); - assert_eq!(pm.get_validators(BlockId::Latest, &address).unwrap(), validators); - - trace!("Modifying private state"); - let mut private_tx = Transaction::default(); - private_tx.action = Action::Call(address.clone()); - private_tx.data = "bc64b76d2a00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap(); //setX(42) - private_tx.gas = 120000.into(); - private_tx.nonce = 1.into(); - let private_tx = private_tx.sign(&key1.secret(), None); - let private_contract_nonce = pm.get_contract_nonce(&address, BlockId::Latest).unwrap(); - let private_state = pm.execute_private_transaction(BlockId::Latest, &private_tx).unwrap(); - let nonced_state_hash = pm.calculate_state_hash(&private_state, private_contract_nonce); - let signatures: Vec<_> = [&key3, &key4].iter().map(|k| - Signature::from(::ethkey::sign(&k.secret(), &nonced_state_hash).unwrap().into_electrum())).collect(); - let public_tx = pm.public_transaction(private_state, &private_tx, &signatures, 1.into(), 0.into()).unwrap(); - let public_tx = public_tx.sign(&key1.secret(), chain_id); - push_block_with_transactions(&client, &[public_tx]); - - trace!("Querying private state"); - let mut query_tx = Transaction::default(); - query_tx.action = Action::Call(address.clone()); - query_tx.data = "0c55699c".from_hex().unwrap(); // getX - query_tx.gas = 50000.into(); - query_tx.nonce = 2.into(); - let query_tx = query_tx.sign(&key1.secret(), chain_id); - let result = pm.private_call(BlockId::Latest, &query_tx).unwrap(); - assert_eq!(&result.output[..], &("2a00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap()[..])); - assert_eq!(pm.get_validators(BlockId::Latest, &address).unwrap(), validators); - - // Now try modification with just one signature - trace!("Modifying private state"); - let mut private_tx = Transaction::default(); - private_tx.action = Action::Call(address.clone()); - private_tx.data = "bc64b76d2b00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap(); //setX(43) - private_tx.gas = 120000.into(); - private_tx.nonce = 2.into(); - let private_tx = private_tx.sign(&key1.secret(), None); - let private_state = pm.execute_private_transaction(BlockId::Latest, &private_tx).unwrap(); - let private_state_hash = keccak(&private_state); - let signatures: Vec<_> = [&key4].iter().map(|k| - Signature::from(::ethkey::sign(&k.secret(), &private_state_hash).unwrap().into_electrum())).collect(); - let public_tx = pm.public_transaction(private_state, &private_tx, &signatures, 2.into(), 0.into()).unwrap(); - let public_tx = public_tx.sign(&key1.secret(), chain_id); - push_block_with_transactions(&client, &[public_tx]); - - trace!("Querying private state"); - let mut query_tx = Transaction::default(); - query_tx.action = Action::Call(address.clone()); - query_tx.data = "0c55699c".from_hex().unwrap(); // getX - query_tx.gas = 50000.into(); - query_tx.nonce = 3.into(); - let query_tx = query_tx.sign(&key1.secret(), chain_id); - let result = pm.private_call(BlockId::Latest, &query_tx).unwrap(); - assert_eq!(result.output, "2a00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap()); -} - -#[test] -fn call_other_private_contract() { - // This test verifies calls private contract methods from another one - // Two contract will be deployed - // The same contract A: - // contract Test1 { - // bytes32 public x; - // function setX(bytes32 _x) { - // x = _x; - // } - // } - // And the following contract B: - // contract Deployed { - // function setX(uint) {} - // function x() returns (uint) {} - //} - // contract Existing { - // Deployed dc; - // function Existing(address t) { - // dc = Deployed(t); - // } - // function getX() returns (uint) { - // return dc.x(); - // } - // } - //ethcore_logger::init_log(); - - // Create client and provider - let client = generate_dummy_client(0); - let chain_id = client.signing_chain_id(); - let key1 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000011")).unwrap(); - let _key2 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000012")).unwrap(); - let key3 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000013")).unwrap(); - let key4 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000014")).unwrap(); - let signer = Arc::new(ethcore_private_tx::KeyPairSigner(vec![key1.clone(), key3.clone(), key4.clone()])); - - let config = ProviderConfig{ - validator_accounts: vec![key3.address(), key4.address()], - signer_account: None, - }; - - let io = ethcore_io::IoChannel::disconnected(); - let miner = Arc::new(Miner::new_for_tests(&::ethcore::spec::Spec::new_test(), None)); - let private_keys = Arc::new(StoringKeyProvider::default()); - let pm = Arc::new(Provider::new( - client.clone(), - miner, - signer.clone(), - Box::new(NoopEncryptor::default()), - config, - io, - private_keys.clone(), - )); - - // Deploy contract A - let (address_a, _) = contract_address(CreateContractAddress::FromSenderAndNonce, &key1.address(), &0.into(), &[]); - trace!("Creating private contract A"); - let private_contract_a_test = "6060604052341561000f57600080fd5b60d88061001d6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630c55699c146046578063bc64b76d14607457600080fd5b3415605057600080fd5b60566098565b60405180826000191660001916815260200191505060405180910390f35b3415607e57600080fd5b6096600480803560001916906020019091905050609e565b005b60005481565b8060008160001916905550505600a165627a7a723058206acbdf4b15ca4c2d43e1b1879b830451a34f1e9d02ff1f2f394d8d857e79d2080029".from_hex().unwrap(); - let mut private_create_tx1 = Transaction::default(); - private_create_tx1.action = Action::Create; - private_create_tx1.data = private_contract_a_test; - private_create_tx1.gas = 200000.into(); - private_create_tx1.nonce = 0.into(); - let private_create_tx_signed = private_create_tx1.sign(&key1.secret(), None); - let validators = vec![key3.address(), key4.address()]; - let (public_tx1, _) = pm.public_creation_transaction(BlockId::Latest, &private_create_tx_signed, &validators, 0.into()).unwrap(); - let public_tx1 = public_tx1.sign(&key1.secret(), chain_id); - trace!("Transaction created. Pushing block"); - push_block_with_transactions(&client, &[public_tx1]); - - // Deploy contract B - let (address_b, _) = contract_address(CreateContractAddress::FromSenderAndNonce, &key1.address(), &1.into(), &[]); - trace!("Creating private contract B"); - // Build constructor data - let mut deploy_data = "6060604052341561000f57600080fd5b6040516020806101c583398101604052808051906020019091905050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505061014a8061007b6000396000f300606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680635197c7aa14610046575b600080fd5b341561005157600080fd5b61005961006f565b6040518082815260200191505060405180910390f35b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630c55699c6000604051602001526040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b15156100fe57600080fd5b6102c65a03f1151561010f57600080fd5b505050604051805190509050905600a165627a7a723058207f8994e02725b47d76ec73e5c54a338d27b306dd1c830276bff2d75fcd1a5c920029000000000000000000000000".to_string(); - deploy_data.push_str(&address_a.to_vec().to_hex()); - let private_contract_b_test = deploy_data.from_hex().unwrap(); - let mut private_create_tx2 = Transaction::default(); - private_create_tx2.action = Action::Create; - private_create_tx2.data = private_contract_b_test; - private_create_tx2.gas = 200000.into(); - private_create_tx2.nonce = 1.into(); - let private_create_tx_signed = private_create_tx2.sign(&key1.secret(), None); - let (public_tx2, _) = pm.public_creation_transaction(BlockId::Latest, &private_create_tx_signed, &validators, 0.into()).unwrap(); - let public_tx2 = public_tx2.sign(&key1.secret(), chain_id); - trace!("Transaction created. Pushing block"); - push_block_with_transactions(&client, &[public_tx2]); - - // Let provider know, that it has access to both keys for A and B - private_keys.set_available_keys(&vec![address_a, address_b]); - - // Call A.setx(42) - trace!("Modifying private state"); - let mut private_tx = Transaction::default(); - private_tx.action = Action::Call(address_a.clone()); - private_tx.data = "bc64b76d2a00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap(); //setX(42) - private_tx.gas = 120000.into(); - private_tx.nonce = 2.into(); - let private_tx = private_tx.sign(&key1.secret(), None); - let private_contract_nonce = pm.get_contract_nonce(&address_b, BlockId::Latest).unwrap(); - let private_state = pm.execute_private_transaction(BlockId::Latest, &private_tx).unwrap(); - let nonced_state_hash = pm.calculate_state_hash(&private_state, private_contract_nonce); - let signatures: Vec<_> = [&key3, &key4].iter().map(|k| - Signature::from(::ethkey::sign(&k.secret(), &nonced_state_hash).unwrap().into_electrum())).collect(); - let public_tx = pm.public_transaction(private_state, &private_tx, &signatures, 2.into(), 0.into()).unwrap(); - let public_tx = public_tx.sign(&key1.secret(), chain_id); - push_block_with_transactions(&client, &[public_tx]); - - // Call B.getX() - trace!("Querying private state"); - let mut query_tx = Transaction::default(); - query_tx.action = Action::Call(address_b.clone()); - query_tx.data = "5197c7aa".from_hex().unwrap(); // getX - query_tx.gas = 50000.into(); - query_tx.nonce = 3.into(); - let query_tx = query_tx.sign(&key1.secret(), chain_id); - let result = pm.private_call(BlockId::Latest, &query_tx).unwrap(); - assert_eq!(&result.output[..], &("2a00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap()[..])); -} diff --git a/ethcore/res/authority_round.json b/ethcore/res/authority_round.json index 8d542443798..292760444d6 100644 --- a/ethcore/res/authority_round.json +++ b/ethcore/res/authority_round.json @@ -46,9 +46,51 @@ "0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, "0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, "0000000000000000000000000000000000000005": { "balance": "1", "builtin": { "name": "modexp", "activate_at": 0, "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { "balance": "1", "builtin": { "name": "alt_bn128_add", "activate_at": 0, "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0000000000000000000000000000000000000007": { "balance": "1", "builtin": { "name": "alt_bn128_mul", "activate_at": 0, "pricing": { "linear": { "base": 40000, "word": 0 } } } }, - "0000000000000000000000000000000000000008": { "balance": "1", "builtin": { "name": "alt_bn128_pairing", "activate_at": 0, "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } }, + "0000000000000000000000000000000000000006": { + "balance": "1", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "1", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "1", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, "9cce34f7ab185c7aba1b7c8140d620b4bda941d6": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" } } } diff --git a/ethcore/res/authority_round_block_reward_contract.json b/ethcore/res/authority_round_block_reward_contract.json index 31957731232..4adb9a8d43c 100644 --- a/ethcore/res/authority_round_block_reward_contract.json +++ b/ethcore/res/authority_round_block_reward_contract.json @@ -49,9 +49,51 @@ "0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, "0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, "0000000000000000000000000000000000000005": { "balance": "1", "builtin": { "name": "modexp", "activate_at": 0, "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { "balance": "1", "builtin": { "name": "alt_bn128_add", "activate_at": 0, "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0000000000000000000000000000000000000007": { "balance": "1", "builtin": { "name": "alt_bn128_mul", "activate_at": 0, "pricing": { "linear": { "base": 40000, "word": 0 } } } }, - "0000000000000000000000000000000000000008": { "balance": "1", "builtin": { "name": "alt_bn128_pairing", "activate_at": 0, "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } }, + "0000000000000000000000000000000000000006": { + "balance": "1", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "1", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "1", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, "9cce34f7ab185c7aba1b7c8140d620b4bda941d6": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" }, "0000000000000000000000000000000000000042": { "balance": "1", diff --git a/ethcore/res/basic_authority.json b/ethcore/res/basic_authority.json index 35711be0112..463b14afca8 100644 --- a/ethcore/res/basic_authority.json +++ b/ethcore/res/basic_authority.json @@ -38,9 +38,48 @@ "0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, "0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, "0000000000000000000000000000000000000005": { "balance": "1", "builtin": { "name": "modexp", "activate_at": 0, "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { "balance": "1", "builtin": { "name": "alt_bn128_add", "activate_at": 0, "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0000000000000000000000000000000000000007": { "balance": "1", "builtin": { "name": "alt_bn128_mul", "activate_at": 0, "pricing": { "linear": { "base": 40000, "word": 0 } } } }, - "0000000000000000000000000000000000000008": { "balance": "1", "builtin": { "name": "alt_bn128_pairing", "activate_at": 0, "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } }, + "0000000000000000000000000000000000000006": { + "balance": "1", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x0": { + "price": { "linear": { "base": 500, "word": 0 }} + }, + "0x7fffffffffffff": { + "price": { "linear": { "base": 150, "word": 0 }} + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "1", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x0": { + "price": { "linear": { "base": 40000, "word": 0 }} + }, + "0x7fffffffffffff": { + "price": { "linear": { "base": 6000, "word": 0 }} + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "1", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, "9cce34f7ab185c7aba1b7c8140d620b4bda941d6": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" } } } diff --git a/ethcore/res/constructor.json b/ethcore/res/constructor.json index f4e22ea2d82..2e64335b58a 100644 --- a/ethcore/res/constructor.json +++ b/ethcore/res/constructor.json @@ -34,9 +34,51 @@ "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, "0000000000000000000000000000000000000005": { "balance": "1", "builtin": { "name": "modexp", "activate_at": 0, "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { "balance": "1", "builtin": { "name": "alt_bn128_add", "activate_at": 0, "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0000000000000000000000000000000000000007": { "balance": "1", "builtin": { "name": "alt_bn128_mul", "activate_at": 0, "pricing": { "linear": { "base": 40000, "word": 0 } } } }, - "0000000000000000000000000000000000000008": { "balance": "1", "builtin": { "name": "alt_bn128_pairing", "activate_at": 0, "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } }, + "0000000000000000000000000000000000000006": { + "balance": "1", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0": { + "price": { "linear": { "base": 500, "word": 0 }} + }, + "0x7fffffffffffff": { + "info": "EIP1108 transition", + "price": { "linear": { "base": 150, "word": 0 }} + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "1", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0": { + "price": { "linear": {"base": 40000, "word": 0 }} + }, + "0x7fffffffffffff": { + "info": "EIP1108 transition", + "price": { "linear": { "base": 6000, "word": 0 }} + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "1", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, "0000000000000000000000000000000000001337": { "balance": "1", "constructor": "60606040526000805460ff19166001179055346000575b6075806100246000396000f300606060405263ffffffff60e060020a60003504166394b91deb81146022575b6000565b34600057602c6040565b604080519115158252519081900360200190f35b60005460ff16815600a165627a7a723058207882eb60ebce23178b3fa06d4cd8e5adc17711937ccddacb18a04abca2a2c9ee0029" } } } diff --git a/ethcore/res/ethereum/berlin_test.json b/ethcore/res/ethereum/berlin_test.json new file mode 100644 index 00000000000..fc34586490e --- /dev/null +++ b/ethcore/res/ethereum/berlin_test.json @@ -0,0 +1,204 @@ +{ + "name": "Istanbul (test)", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x020000", + "difficultyBoundDivisor": "0x0800", + "durationLimit": "0x0d", + "blockReward": "0x1BC16D674EC80000", + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "difficultyBombDelays": { + "0": 5000000 + } + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x0400", + "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", + "accountStartNonce": "0x00", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x1", + "maxCodeSize": 24576, + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip155Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "eip1283Transition": "0x0", + "eip1283DisableTransition": "0x0", + "eip1283ReenableTransition": "0x0", + "eip1344Transition": "0x0", + "eip1706Transition": "0x0", + "eip1884Transition": "0x0", + "eip2028Transition": "0x0", + "eip2315Transition": "0x0" + + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000042", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x400000000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", + "gasLimit": "0x1388" + }, + "accounts": { + "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x00", "pricing": { "modexp": { "divisor": 20 } } } }, + "0000000000000000000000000000000000000006": { + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0000000000000000000000000000000000000007": { + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0000000000000000000000000000000000000008": { + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, + "0000000000000000000000000000000000000009": { + "builtin": { + "name": "blake2_f", + "activate_at": "0x00", + "pricing": { + "blake2_f": { + "gas_per_round": 1 + } + } + } + }, + "000000000000000000000000000000000000000a": { + "builtin": { + "name": "bls12_381_g1_add", + "activate_at": "0", + "pricing": { + "bls12_const_operations": { "price": 600 } + } + } + }, + "000000000000000000000000000000000000000b": { + "builtin": { + "name": "bls12_381_g1_mul", + "activate_at": "0", + "pricing": { + "bls12_const_operations": { "price": 12000 } + } + } + }, + "000000000000000000000000000000000000000c": { + "builtin": { + "name": "bls12_381_g1_multiexp", + "activate_at": "0", + "pricing": { + "bls12_g1_multiexp": { "base": 12000 } + } + } + }, + "000000000000000000000000000000000000000d": { + "builtin": { + "name": "bls12_381_g2_add", + "activate_at": "0", + "pricing": { + "bls12_const_operations": { "price": 4500 } + } + } + }, + "000000000000000000000000000000000000000e": { + "builtin": { + "name": "bls12_381_g2_mul", + "activate_at": "0", + "pricing": { + "bls12_const_operations": { "price": 55000 } + } + } + }, + "000000000000000000000000000000000000000f": { + "builtin": { + "name": "bls12_381_g2_multiexp", + "activate_at": "0", + "pricing": { + "bls12_g2_multiexp": { "base": 55000 } + } + } + }, + "0000000000000000000000000000000000000010": { + "builtin": { + "name": "bls12_381_pairing", + "activate_at": "0", + "pricing": { + "bls12_pairing": { "base": 115000, "pair": 23000 } + } + } + }, + "0000000000000000000000000000000000000011": { + "builtin": { + "name": "bls12_381_fp_to_g1", + "activate_at": "0", + "pricing": { + "bls12_const_operations": { "price": 5500 } + } + } + }, + "0000000000000000000000000000000000000012": { + "builtin": { + "name": "bls12_381_fp2_to_g2", + "activate_at": "0", + "pricing": { + "bls12_const_operations": { "price": 110000 } + } + } + } + } +} diff --git a/ethcore/res/ethereum/builtin_multi_bench.json b/ethcore/res/ethereum/builtin_multi_bench.json new file mode 100644 index 00000000000..a37273936fb --- /dev/null +++ b/ethcore/res/ethereum/builtin_multi_bench.json @@ -0,0 +1,112 @@ +{ + "name": "ecrecover legacy", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x020000", + "difficultyBoundDivisor": "0x0800", + "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", + "homesteadTransition": "0x0" + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x0400", + "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", + "accountStartNonce": "0x00", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x1", + "eip150Transition": "0x0", + "eip160Transition": "0x7fffffffffffffff", + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff", + "eip155Transition": "0x7fffffffffffffff", + "maxCodeSize": 24576, + "maxCodeSizeTransition": "0x7fffffffffffffff" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000042", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x400000000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", + "gasLimit": "0x1388" + }, + "accounts": { + "0000000000000000000000000000000000000001": { + "balance": "1", + "builtin": { + "name": "ecrecover", + "pricing": { + "0": {"price": {"linear": { "base": 1, "word": 0 }}}, + "1": {"price": {"linear": { "base": 2, "word": 0 }}}, + "2": {"price": {"linear": { "base": 3, "word": 0 }}}, + "3": {"price": {"linear": { "base": 4, "word": 0 }}}, + "4": {"price": {"linear": { "base": 5, "word": 0 }}}, + "5": {"price": {"linear": { "base": 6, "word": 0 }}}, + "6": {"price": {"linear": { "base": 7, "word": 0 }}}, + "7": {"price": {"linear": { "base": 8, "word": 0 }}}, + "8": {"price": {"linear": { "base": 9, "word": 0 }}}, + "9": {"price": {"linear": { "base": 1, "word": 0 }}}, + "10": {"price": {"linear": { "base": 2, "word": 0 }}} + } + } + }, + "0000000000000000000000000000000000000002": { + "balance": "1", + "builtin": { + "name": "sha256", + "pricing": { + "0": {"price": {"linear": { "base": 1, "word": 0 }}}, + "1": {"price": {"linear": { "base": 2, "word": 0 }}}, + "2": {"price": {"linear": { "base": 3, "word": 0 }}}, + "3": {"price": {"linear": { "base": 4, "word": 0 }}}, + "4": {"price": {"linear": { "base": 5, "word": 0 }}}, + "5": {"price": {"linear": { "base": 6, "word": 0 }}}, + "6": {"price": {"linear": { "base": 7, "word": 0 }}}, + "7": {"price": {"linear": { "base": 8, "word": 0 }}}, + "8": {"price": {"linear": { "base": 9, "word": 0 }}}, + "9": {"price": {"linear": { "base": 1, "word": 0 }}}, + "10": {"price": {"linear": { "base": 2, "word": 0 }}}, + "11": {"price": {"linear": { "base": 3, "word": 0 }}}, + "12": {"price": {"linear": { "base": 4, "word": 0 }}}, + "13": {"price": {"linear": { "base": 5, "word": 0 }}}, + "14": {"price": {"linear": { "base": 6, "word": 0 }}}, + "15": {"price": {"linear": { "base": 7, "word": 0 }}}, + "16": {"price": {"linear": { "base": 8, "word": 0 }}}, + "17": {"price": {"linear": { "base": 9, "word": 0 }}}, + "18": {"price": {"linear": { "base": 1, "word": 0 }}}, + "19": {"price": {"linear": { "base": 2, "word": 0 }}}, + "20": {"price": {"linear": { "base": 3, "word": 0 }}}, + "21": {"price": {"linear": { "base": 4, "word": 0 }}}, + "22": {"price": {"linear": { "base": 5, "word": 0 }}}, + "23": {"price": {"linear": { "base": 6, "word": 0 }}}, + "24": {"price": {"linear": { "base": 7, "word": 0 }}}, + "25": {"price": {"linear": { "base": 8, "word": 0 }}}, + "26": {"price": {"linear": { "base": 9, "word": 0 }}}, + "27": {"price": {"linear": { "base": 1, "word": 0 }}}, + "28": {"price": {"linear": { "base": 2, "word": 0 }}}, + "29": {"price": {"linear": { "base": 3, "word": 0 }}}, + "30": {"price": {"linear": { "base": 4, "word": 0 }}}, + "31": {"price": {"linear": { "base": 5, "word": 0 }}}, + "32": {"price": {"linear": { "base": 6, "word": 0 }}}, + "33": {"price": {"linear": { "base": 7, "word": 0 }}}, + "34": {"price": {"linear": { "base": 8, "word": 0 }}}, + "35": {"price": {"linear": { "base": 9, "word": 0 }}}, + "36": {"price": {"linear": { "base": 10, "word": 0 }}}, + "37": {"price": {"linear": { "base": 10, "word": 0 }}}, + "38": {"price": {"linear": { "base": 10, "word": 0 }}}, + "39": {"price": {"linear": { "base": 10, "word": 0 }}} + } + } + } + } +} diff --git a/ethcore/res/ethereum/builtin_one_activation_bench.json b/ethcore/res/ethereum/builtin_one_activation_bench.json new file mode 100644 index 00000000000..e746d7b2a12 --- /dev/null +++ b/ethcore/res/ethereum/builtin_one_activation_bench.json @@ -0,0 +1,54 @@ +{ + "name": "ecrecover legacy chain spec for benchmarking", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x020000", + "difficultyBoundDivisor": "0x0800", + "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", + "homesteadTransition": "0x0" + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x0400", + "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", + "accountStartNonce": "0x00", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x1", + "eip150Transition": "0x0", + "eip160Transition": "0x7fffffffffffffff", + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff", + "eip155Transition": "0x7fffffffffffffff", + "maxCodeSize": 24576, + "maxCodeSizeTransition": "0x7fffffffffffffff" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000042", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x400000000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", + "gasLimit": "0x1388" + }, + "accounts": { + "0000000000000000000000000000000000000001": { + "balance": "1", + "builtin": { + "name": "ecrecover", + "pricing": { + "linear": { "base": 3000, "word": 0 } + } + } + } + } +} diff --git a/ethcore/res/ethereum/byzantium_test.json b/ethcore/res/ethereum/byzantium_test.json index 9fdde9d13e5..bc5ac30207e 100644 --- a/ethcore/res/ethereum/byzantium_test.json +++ b/ethcore/res/ethereum/byzantium_test.json @@ -54,8 +54,47 @@ "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x00", "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": "0x00", "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": "0x00", "pricing": { "linear": { "base": 40000, "word": 0 } } } }, - "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": "0x00", "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } } + "0000000000000000000000000000000000000006": { + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0000000000000000000000000000000000000007": { + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0000000000000000000000000000000000000008": { + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + } } } diff --git a/ethcore/res/ethereum/byzantium_to_constantinoplefixat5_test.json b/ethcore/res/ethereum/byzantium_to_constantinoplefixat5_test.json new file mode 100644 index 00000000000..3aa42605ced --- /dev/null +++ b/ethcore/res/ethereum/byzantium_to_constantinoplefixat5_test.json @@ -0,0 +1,108 @@ +{ + "name": "Byzantium to Constantinople (test)", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x020000", + "difficultyBoundDivisor": "0x0800", + "durationLimit": "0x0d", + "blockReward": { + "0x0": "0x29A2241AF62C0000", + "0x5": "0x1BC16D674EC80000" + }, + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "difficultyBombDelays": { + "0": 3000000 + } + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x0400", + "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", + "accountStartNonce": "0x00", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x1", + "maxCodeSize": 24576, + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip155Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x5", + "eip1014Transition": "0x5", + "eip1052Transition": "0x5", + "eip1283Transition": "0x5" , + "eip1283DisableTransition": "0x5" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000042", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x400000000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", + "gasLimit": "0x1388" + }, + "accounts": { + "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x00", "pricing": { "modexp": { "divisor": 20 } } } }, + "0000000000000000000000000000000000000006": { + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0000000000000000000000000000000000000007": { + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0000000000000000000000000000000000000008": { + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + } + } +} diff --git a/ethcore/res/ethereum/callisto.json b/ethcore/res/ethereum/callisto.json index 3513e8bb761..a1cae6f3c5a 100644 --- a/ethcore/res/ethereum/callisto.json +++ b/ethcore/res/ethereum/callisto.json @@ -72,9 +72,48 @@ "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, "0000000000000000000000000000000000000005": { "balance": "1", "builtin": { "name": "modexp", "activate_at": 20, "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { "balance": "1", "builtin": { "name": "alt_bn128_add", "activate_at": 20, "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0000000000000000000000000000000000000007": { "balance": "1", "builtin": { "name": "alt_bn128_mul", "activate_at": 20, "pricing": { "linear": { "base": 40000, "word": 0 } } } }, - "0000000000000000000000000000000000000008": { "balance": "1", "builtin": { "name": "alt_bn128_pairing", "activate_at": 20, "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } }, + "0000000000000000000000000000000000000006": { + "balance": "1", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "20": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "1", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "20": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "1", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "20": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, "183394f52b2c8c034835edba3bcececa6f60b5a8": { "balance": "100491852286952719463755404" } diff --git a/ethcore/res/ethereum/classic.json b/ethcore/res/ethereum/classic.json deleted file mode 100644 index 393129c12f0..00000000000 --- a/ethcore/res/ethereum/classic.json +++ /dev/null @@ -1,30538 +0,0 @@ -{ - "name": "Ethereum Classic", - "dataDir": "classic", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "durationLimit": "0x0d", - "blockReward": "0x4563918244F40000", - "homesteadTransition": 1150000, - "ecip1010PauseTransition": 3000000, - "ecip1010ContinueTransition": 5000000, - "ecip1017EraRounds": 5000000, - "bombDefuseTransition": 5900000 - } - } - }, - "params": { - "gasLimitBoundDivisor": "0x0400", - "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", - "accountStartNonce": "0x00", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x1", - "chainID": "0x3d", - "forkBlock": "0x1d4c00", - "forkCanonHash": "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f", - "eip150Transition": 2500000, - "eip160Transition": 3000000, - "eip161abcTransition": "0x7fffffffffffffff", - "eip161dTransition": "0x7fffffffffffffff", - "eip155Transition": 3000000 - }, - "genesis": { - "seal": { - "ethereum": { - "nonce": "0x0000000000000042", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x400000000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", - "gasLimit": "0x1388", - "stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" - }, - "hardcodedSync": { - "header": "f9021ba0a281d23475d3d5ff03df8636c9f528cdd91498af274a3b2f8989bbd51bfeb809a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794004730417cd2b1d19f6be2679906ded4fa8a64e2a0d5fdd62e7e29dc3da1cd3fe5ad549e000260bfdb55f523fde008f26390220d23a0370ff78457d6c7469ff333a13165f4bb8057d00cfa68365cb4d1a8c8a1da46d5a0dea37365a20f5bb5ad3766a24a9fe7b04b946e35aaba74f862467a7c5cdb7d67b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000004000000000040000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000100000000000000000000000080000000000000000020000000000000000000000000000000000000000000000000000000000000000000000020000000000001000000000000000000000000000000080010000000000000008000000000000000866fc181925a9783763801837a121d830c317c845c9d1efe9b457468657265756d436c6173736963534f4c4f2f326d696e657273a0bb6e626c7ee3d827da18e1b303d9552ba17e92cefcedfc58bdfc5bac6a8ebabe882d8c26402344c24b", - "totalDifficulty": "598584828374329723203", - "CHTs": [ - "0x0eb474b7721727204978e92e27d31cddff56471911e424a4c8271c35f9c982cc", - "0xe10e94515fb5ffb7ffa9bf50db4a959b3f50c2ff75e0b8bd5f5e038749e52a11", - "0x816e7463af7b5d2fcb804ba55f09e8452182b0ba6c995a34e144245d76333d55", - "0x3793af64c1ddc07ab61b2ba120034d91c02183ff788f07d3120fd4e6a48305b5", - "0x14c6106a17e041032210bfa0ca80d11860a1c6d95175d55eff39f97b8d8acded", - "0x396f832bfa3a9c494e9245471f0e65552613d87b6fe62128103590d95de72c2d", - "0xb060979f095c170a776b2b50a1e2ab0ffea80f6e522753fa36ad6f106ee32e9f", - "0x8f452e7cbd8a333ed04d819a143a8d3a75fe8c58418e7fc420bb2a717c0d4d2f", - "0x37fe1b0cf156bfc07571569af210540be753777903a308d5707538fffed75b59", - "0x6f0561d017cfc123b3f0d37b044e4f7231516b8731a1cab89afb569238643c33", - "0x3c1740c410a88c60fe8ccdc44e0ef2cf7f7314818dbf1648c01d0d94fbffc211", - "0xfb98115a7d6df8aaa40115f883014fe97300869bc016648e918fbf2df9608d41", - "0xef1099ab5ca4b79369048678d3ba78122fc081b00b6fd0f6907302a260d58266", - "0x969575f411dd78fdc5b4def0331fc93702029cc3c78851331a0f47dd0faae70b", - "0x9e53053e362be51c0fd25eaafd9e7c5c969d9f2ce8db4b3d4d830cbff347b0c8", - "0xf9de29944954d2b71a93532fc26916ae12fee72d42a79adaf940b0bf75d0ef89", - "0x28b2ce6635e60e06d643750798779023b2a807d9d089ae9ef7f223eebc15a71e", - "0x5295c06cbaff06f42bd8f5d9cbe94a840885caed02f9c9ba6da44a888ef796de", - "0x576eae673a4cbba4c7c7a56b47835ea64ae5989d67d119ebc8e568df40d908a9", - "0x891c0d38bc5e55620615da42ed77ab33806a042512034bcee134279dde1054be", - "0xbab05999d657426a11a902eb4c85ac52e2b72dd1cf38584cf2baeb2c3727bb44", - "0x3bd7e5a966f6dd2dc456948a8efca5584f5a4e0033f3037843a42073dda1f71b", - "0xc4f773ab1e34290f9a3d9ac6ede4749c5dec547353dddea494169d86f71107a3", - "0x993bf037ea9dd58b52027fb6f39332dab867c1e72af34a49d58a5a12f26bcca3", - "0x48b2d8d506eb8fc9dc0402fef26952111449aca0f90d0079f0526435d4e3183b", - "0xfef8f61df240e956f43759d2f481938421e064a9bd6a3be7a53b1213cc9588bb", - "0x5abf01f5066cf1091acdd1f99fbd5fd963633feafc42f9047534a3c1522004a5", - "0xa0f6205842260988161183b51bc36fae458fa184dd61844617d5c5d26fa78346", - "0x77309182fdc26d15dd8d9dd05040d7dc623412785708d8aac39eedee63931944", - "0x661c93311b94b7d4cdcbc0973225c794e71898a2b906922a6c1e8f7e9e289dc3", - "0x9d5d329ee8d9fffaee0111688d31a308dfaac922dcd61f818edd5303d0955be0", - "0x716ca25b184b64ba273b978de098f9946413f6fcc95bfba5cf1169e7e03dd611", - "0xa2e8d5cefa5804894fb42a106340b00de3286fee0992b5887b2cf471539d74a9", - "0xf846e05c9e9e9cb4cd2b7cbae7ae183a43a59ab02251954db632e538adacc357", - "0xbc01b4e23ea082a193e4c1012b1da91f3b4cb762009ca320bc8ed294af874e79", - "0x9218114a32da3ecf660d4d51b101bb51bb17c771561c1946c099be082f0a96b3", - "0x3b4edf03dfd53081cf40c0b90b35c1ccf7c7fe96cf131172eef5eec62f620ea8", - "0xb15758944263c67bdf528d4d7fe05737fbdbf7ffedce5f891a4ddf76177d2609", - "0x1f119374c385240f7b4ba1ec3d502be2c12c159411d5393ff2bd38cf87033625", - "0x8a8d5a93f3475813926b13a4d53f21b28dc79ade2b50830c0b9043e9fcd81576", - "0xcc22f7e2bb9c06c15ca3d82df852ed9097a2ddc687ee389e662de000db0c84fc", - "0xc2047e0dab711db791aadb642f8102abaacf7231b8dbdbe1f60573b0be015a31", - "0x1b4088ceee7783e4563945f162bd5da67020ca377a18d615923e8564d6709f85", - "0xd73450686e33bbda9eef53a95a86e5a0514156b98a5b7dfc6fdc0adb0b83cbcf", - "0xb374076ec961360e38d3486a31c3f72225440984c4c47ca790b4961d94152159", - "0x4f723f4fbd31d63a5421390e68aba0aff97249875688a7d9ab9a339d9aac7bc1", - "0x5fe51ff982edcad6c0052fcdf9a70e8f325c8140ab75848c5d7b0d670bd7edc2", - "0xc3ad483c7cc23bf8d6ae3e3e829bf126d5eeea9c53b566a6da95bef573b9c779", - "0x3c9e50ed9eb57cc055fd9a65a6cdaba2030d8b41f81348f296d7410c1d24ced0", - "0x0c6dfc1f626ff9e85ff072c154152bb3f122a2c1a45bc2d9e7da9b2d5278149a", - "0x92f4452dbdb4fe70e84ecd47af4b1af90975219797cebd451beceb6997ab024d", - "0x9a3d00686736b5b838308da4b8f0aa9edccfaba64621ce2988cea6ea2a267efd", - "0x8d602d0bef069177102726d5ccd93d19805fb5771a350a41e32755ce740b9047", - "0x6681e4097667a22ad3713acc27b6f87abd54583230581933bed9245c2c457ac3", - "0x53077caeabcd926466319a3ee5c51c32e01e1812a65313f113f814d53e9f1dd9", - "0x4dd4c33e99d86ba84f976c639333fc072e262c0b76dfdf2f589300af54048c0a", - "0xbc3b9837a6fa54616dcdb8088080e276e2e99a23c8e7de4109504293703d524e", - "0x24316b344cecd5e601cc0acc91ff94f481ca3fa26d8478644a9d8bcaeb0359b3", - "0xa7bafd3c5f4e3f6b5c078d50eb318d91e867b0e1c966027e3e7458eb104ccd63", - "0xc8da46b7d778980d120357c8de2bba336f5a2ac7a9f4183a0ee1f7597ed47d25", - "0x7469fc5d8c9c648cd10e538710e0f126542e59a82484e7fe56b73f4ec52c36c7", - "0x993bb7c0487ff61c97e4f1533446ae35b6346642e1230f2441da8b354111d597", - "0x90e3944732f86a2254dc4f30650f8438dfd0b777561fb02a8ab1c60438569c24", - "0x4e8472483679b54bdc600010fdd164f54771d7a99fa9272c683b610fff72507b", - "0xf72a861a2ebc232c25529c0f94c59996e64c59c36a1326a183cf171bddf2a75c", - "0x7f222999ba9113e2a64fd026a8f7244e6d2ae8f2a7e7d8d2d6fbde6fdf0b629f", - "0x3304e769f730522c1c5aa745c448075df026b8f82a4dece84fd70d0457050985", - "0x9ee5e3ccaaf94461dff9df8c4805ca831f58a1586af4ece3cab14a45f3b784db", - "0x21e4364859063e20153d2d06eae4d2c9e99354bf97fbff68406d8825d18dbce3", - "0x4805355b72b1b61b07814059f80b4da0351291cc932292f23069197a74127726", - "0x14474f45f38d7ea51418e5f03751c8bfbfb9b3e2957d3051e862aa3c57a63c43", - "0x69372cee3e2807d10ecb72d404a033568a159a5b15d2007537ed9a758164b29f", - "0x147223b51001166a4e65372c9c706f011f1ae94f4bdc9ba6e8960017e8898703", - "0x11a1e48a5c1d7088c0ccb8177d54db9e9f91a99aa7c24f702cd93f4646f181ee", - "0x809c902b2f4b8760c3d2e820c93d6df69a5d184a43a6c654ebe7067e7212137d", - "0x749367027756c27215b2f57168ef15d3b39062c9f79b3777f7fa19e8073de775", - "0x6a9fae37364f97e36e56df97acb1b7d066a608d8366d7e008854756dd28fe748", - "0xdc2f1b7a8aeda15e6bf4f5f424bc54828cc8520e2e7ba27bd8e28ba2b543aae7", - "0xe0fabc892d5c8b4342ff488b76a0400425ea70774f207c546fbf2f9f5b105dcb", - "0x151fb5e02d8eb3c3192cb8c039bcb4c121c4ebeea5e7f98927b85a730a24bbf9", - "0xfeb2f2ea368d0bd4c0b0bd97b444c365bdc0ac9ce2862b0d0162387727edd236", - "0x1eaf828231ebbae54737111bf3c7181fe3d7e9070def1313470d3f81c89f01c7", - "0x8a1b0565013cff488bbe3f35df86fb41c7aedf4d911130802c473f4ddb74d6d6", - "0xce9158b5c903312fa636e074e3efe413184652581a4877d40a0085965dc0bf9a", - "0x1cf602c6306affe2916fa09d3c8c018f23fc44dec8af8e83fa0008c98b4dda72", - "0x189dc4569e96cab937265ecaea76a0880ec97d5b84ac1fbd0cd2d2b36a8c34c3", - "0xd698bd07e485767c1da30bb218265e1304f6eaf426749ccba67478817af84bd4", - "0x47d7e101de73bb0ca97a0bb70094e81b82c63e519a6b2aa5fe10ca7351232870", - "0xb0d441b6c41072889c4a982306c9a40dff77b43425ecc4d771c22f3199eb7708", - "0x7893071deba67f2fc8e1b18bedcac4dbc05a020f37c764c555eadc42dd9d29d3", - "0x3c6d636db3621757d60b2d0e1804e19528ce60c9feed1ead93731820ff19b11c", - "0xde87aaa462b461c4a33e0739ef4cf56d442b7967ac7c5280816a959046b128b6", - "0xb237b17650adcbcf580b64b500ecaa7ca36921a11ad92c1e8992c57cc1a7f618", - "0xec379725db43fefe61f2495f7f7e0531d852e21f896ae806144c4d9b4b986e96", - "0x65ac5548988825831f0887b9ff0f2c13b7f3b49e4a67c39b1694e76414249f6d", - "0x76053b72ec9e6fcf0a28ef273d3e1b0842c3c2c0e905f5b5a3535ffab216c8db", - "0x2ab1e87489eb1daeaf8882c6baa0a8726aeac522e9c4eb4df71e35af2d22cc10", - "0x8c9c6adcabe253b311f6a9b8165ff9c5e26e4cf41f1acd80837e77fc15526a86", - "0xf143155230223a3f126c757b85e193a9129f1bdf97c0ce1f2785f14d40911f30", - "0x8c510d9dca593534f3ed316f240ffb9343d1e3cd6d005df6a75a1b354da0b36e", - "0x3440975cf818a718beff35a85d19bcbbd67e1b16ca9d78af34dcde31a28b3288", - "0xf56ef9c57109f9cb7a925bbb6d453efc19e8a45b331f76153d20a87d86a8b0d8", - "0x19a360772872003f08508a28a362c6e05650b385c24a928ddad4d562bfccf412", - "0x643720694b3773ecf20437d54a6be701810feff233f435dc701dbe88c9a6a13c", - "0xf8c0babe99aa26ecbfc91b304d9cd54ccfb37354c4fdbeb3207bb6d4647fabd6", - "0x481ccc7213d0188e817c071c4cc3a71c96befb9aa98cab964012fd7a8267834a", - "0x02d83ca8d92e0fe6ce7643ae93af60e38ab5659a84c04beba678ceef654aec12", - "0x24e6b4bcd0d97df196f2371532771593fe17be8fcb89f1e1164bcce8616088b9", - "0x3dc91775c50c04812f755f3b48d3d6a0cc599c586ce9d105e2cf4f3e4527b515", - "0xcdc215f05398ea3942d3a38078a3602cbe8ac549d4bf0e4a54191ceb2aff8f76", - "0xee02874e444b784f4265cc60b86a17382d277d03c8bce8a33241460ea8950699", - "0x35c34bc84736fdcbc4d4e2e089f30bcd186a052b2f6dcb639fc45a0aeb6969f8", - "0xbbb3ff849c36659bf2c00feaad9f7b3a342b5cfcf3555b7c2e467a0dc84e90e8", - "0x0584bc60fbe3fae9088c214fb519030646c3240f77180a0bedecd3e9c9f47f89", - "0x9d18a665d89439ad2c97427bdab3e598f5bc0da6a0ede2378f95c5bc31f10d12", - "0xe8a5bdd0ffd33a6fd03cf003c6d2afbe8493e0f0cb69e6366e22b4d1ff985101", - "0x7ca955f4e01eef756b680d09c25626cf50013faa20a12b0a334fd048a04e7b91", - "0x064254551457bd5e7a260a41ed3643746202d503813ceaf42660f9bd1983be34", - "0x1191044e354ea1e3daa25ed2175a6517659e96733d9065d81492ffe4472fb96f", - "0xc823514cf3566b1bb2e19a35e0ef0980bc483fa820d13ae2cfdbf15fd426c272", - "0x413f941f192d0ab77bac68268f45e2c9adbee23a3324d4ae8748d09735355a2d", - "0xa66c94b9603b3058b730baeba1b79d52f548ceeb5bac487903f92481060f6804", - "0xa84d4a8860bdff1fdae6bcefcdcf700fab7857444ef1e76d8259b005872a4636", - "0x9fa64d44edd9c097458d3901612a4b6f655a1421ebb68541cb1a4bdbbe24911e", - "0x402027770edb387510241a68a235723c6c5c95aed54dab058c43d21a6bb48c41", - "0x776281b7e341a66491603b7ae8ed7cc82b99febac43f94cb1c4dda73b17aab63", - "0xfecbaf0fc5a02dcf49095514ce26df927def3cf51f37e04471545aae2364f936", - "0xc477d9293f0ace7243f8b9c89f01210b8f96b4affc9d3332147ea2e2b693c99e", - "0x4a8b9afb9d9097831b2497296b0fd0fae76ab8a596213daed35cf87e6bbefaed", - "0x594c4e9851eddbb4a6c2ac72aaa244ff35d67262efb20935360360d39f7c7ecc", - "0x14b7ef22c46ba8400979b6c06f3b3023607145a5cc6b5b793daae758cb655245", - "0x3a9d233553d1ca4d9862a70ef133a5fb2df75276fb24297b0bd2927a39459450", - "0x22c5e227d5fa7616603bbf36c5e4ee7dbf285fb0cf403a3ae982da70c825cad0", - "0xc2c8d439c7bd884665da56e3b680a5e58ad1e98627fdbec6fa67d7bbfad33a9b", - "0x7d5682cd9f28493ba4b87be141ec99701bbcd1aaadf9840b81de1fc07d4ffb18", - "0x8845f626c5f78d1bea281f892727437f9de8f976e4c4fec6060b2115f1862db5", - "0x769fdf0bcdbaec1ffe98cd3500ce8341b4d7ea2dad5fadb0258212306ccc75f9", - "0x185569d1980147fcdfcd0e0068ab380f0cfa58a690334a558bc1fd0d07897e96", - "0x0ed70ccff752df46f981043c5279ac3f13e7f62c2bdb9a0a9817a1c119ef6402", - "0xbe121e28349e80d601ab997af844aa03ea6492e88d75d3d46517d8f835e3c3fd", - "0xa7aa5f0bb95566292d22891faa75b7ff2020b69fecc8a22d796cc3a60953d98f", - "0xa2611b092b00f78fe639c4fba0274ae474fa448b3f2e4b8aa4d06c654720d478", - "0x8e425115b98f5e41c8b5d03a9e17d56d30050d85dd06cfff12f002c546a256ba", - "0x988b449fbd8c35855154fb4eb22ba6b7b7095be26203d137f484c67facd40dc5", - "0x567c43ac5dabeff01d6997543ec7abf7998088a355a6ba8e70f41a243dd1343a", - "0x6f560cb650142aed532f17de763d61d021cdec2716b0d2cb27b3a64052abb874", - "0x7e4ce5fab8f4f1fd41f9e5f10204032ae7e0e38093b1d07699318975b33910ec", - "0x91a0820eec5390916bf464b1d16c00b5d94386c4c9f4cdf7e0b3cbe40747fbc9", - "0x9c59451a9a242123efa72c5fbd1564b7bcc0067ea9d025336d228ed26b9ba6c0", - "0x1043a5ab3f5a3bced84faaae0e783abb3b81c2b967bbd976042cb5d897d28146", - "0xfc37b3b3c0be392ad2a5e36c120eace1d14e637ac806e79a750b9a6be3c742a7", - "0xce2ccbada44a8db5144073e69914b322dc015273a75b85ea43fd9e21037c760c", - "0x6cf8336b5a410e10604f93351242cb3a6929968212abdf85b4ca9321115b8fdf", - "0xabbe9781950362be1e206d91ea1bdd6f32ea2c6df65b277cb89050ca1deb9296", - "0x922a6b85add6839494c3edff389aa1b054409c330b4a4e2a6c0e4f9bc85b36a9", - "0xc26dcdfa135a09b7eef1e99b445fb66aefb8bceb6ea715b81d78ba87cd56ed8d", - "0x87d647fa7dcde81a0e133aa949d574befefbceab24a42ae4f3809d2bb52a2d9b", - "0x85ee37fa7154568b9dee8a539340f99c7f1bbc7b9be1f2055636ed9dfc074e4d", - "0x8b0114dc9e249f1de4bb3d055790e4bf18aa28a938f39e8a457ab4a43b0dd613", - "0x3be36db134f4c00fc9e1bc376213c7073389c993b0b0744cba619688d6c037d1", - "0xfaf987eb2e066ad8871c489c23102ec5c58add2d13e62d56f2821cc1f4d66d84", - "0x678478da2955e6876ac49c5146e9f7c376dbf2f170f6404054ae4385e72f3f19", - "0x85d8d9dd6c2a8f6b6a1c0fc0cb55ca870d9a7aae1621c143c3176a3a81fcc29c", - "0x76f4dfa4c3387408c823a75aeb872ff39af3820375ff52f7aecb41c96e4faf2a", - "0x6e530647f2e4232063de2fa8f673989d7834d8cdf529791032888f2833880b80", - "0x64422b4dfff6cba0eb6deaeb4593eefc40a357469a7f7c3be078f80c66161333", - "0x5c7ab740510a4183832bd78e6d6105b0f9f928611f7d62ef96aa8dd8da48a72d", - "0x0bbf405e29f015d24e64f063d50ec6c616af64622b1a4132cde86f926e93850f", - "0xd9ba81ea0790f1f8adccd0bd203c7adeec2b490381b822d6b15293cac2f26206", - "0x4f78619baa34f2278022c509671a38d29366936d6860e79ab540ea46b66ba782", - "0x00c1f10211d7604e59a327239f00dc6d036a93416b7871cb214e8eaa52571834", - "0x1b6636708f97485675c0e5b21eb749ee4a5fd0dd886e6690090856bcc5178ec0", - "0x71366e853968c1bbbbf8e3d6e13100dd589521f8db9e561dd20ff8709b5c1a96", - "0x0d2c35a01646cb09e2b56b5792ac03047848bef7415ae26f787cd54ef8f327da", - "0x1c5b71047f99db30453e502c9acdf422d3bf97b0d42b9223ea1b8b9924bb0cb1", - "0x9988eb36e4a669638e3242e5ada3e6596c5e4ef36a83ed2d3348d35fbed4d3d0", - "0x8f00020f98f02af0749df39fb2f534d356e3dbe809bdb3f435c4a575d661d6db", - "0xf70a509c0d1c60afcafc7cc492c5ad575fdcadf6ca8e0e5f184c62dd52021129", - "0x72cdb6544dd469ab42e270e51d136b314c27ed0d6682f914cf3e0398399d2d5d", - "0xd5584896e649b618ab8257859e42ef7798c37cc85103a8019cec10b1524519f3", - "0xd70636cf5cbe78ba86b8de902f83f9c550a8ee31a019da6fcb0b1ab0a02bd31f", - "0x79a506d61c89cb7b1aef845484956389d5f6077fd10f8d1ede1e92474eac15cf", - "0xa66c0575cfea08bc6abbba03b0d10be7bdcfe6c5da9058cb34c22af2c8f3f1d9", - "0xfde316523b6b41fedcf11d776a53bd27fe3058c3912059197cda083a14410689", - "0x6774beba5e02630a7e4379fed7175f0f3d9f8fb5333451f25d5b044521ed38ed", - "0xb513ee7cf03529c88633176792a6b08585ff6163fe174f68e285d6315ffe33aa", - "0x4482f3d82f65f0fdf71fdf669403e0b835b5458e567dbd295b4f51d22f01650c", - "0x1cf0c0859b1839ebfb872a570e0c17886d8d7f26067bcd16af7f9f0415001aa0", - "0x231be14cb1cec949a4e806a7b3aebdb074d58e5a1c48b85c35138d5d3e967e0b", - "0xe8f0a0ef68efb2ea1bfb5d47e3c9446900329ff89a3ab7eccde41e09ec3e79b9", - "0x16348cb5e49e61010da09a5ad3cef83ab369ee3d0f28079584c23749cfa30238", - "0x6d33bc7f502436bfd0d574c3f6b1155c69f8a80e55c42c353e9e68abb46d932e", - "0x0e5d40ed7351b59846ca3dd8cc9c0eb71d4659e0add0dbfb0bb7f518bf45c821", - "0xb1ba4509de4c0f1212b2b07d949740f15ef8df9af8e7e9d765e6b407a0c5d717", - "0xa99615bb15371a15b92c119f8632f1ad7c29d6eb9a69e0ccf33a9dd268cffd54", - "0xfc3601e7f85e4b8e996bddcf1b34cb6c20462e21c715782da12d8e08a01cd21b", - "0x872b0f4f3ef00cc5cc6fdd71091de96c02f5898826fda4f837832f302497b51a", - "0xb34656439e4474e075d8ef523f6f74ef292a22281e6dc0b8fabdfd2339389919", - "0x048d4dc500031aa56d89e799499a86d6dacddea795ddc4571669fa55d694345f", - "0x684b8762b97a9d650f0f0e5edee73b60a29f6e75573bd6244518b11c4a571533", - "0x5d20bbacb93f7b03d92ae0ce8296bfd113a808ab3a8bd7703838d7e8356b6714", - "0x25efac3c3bc3d4f10ed9918fd9581d68eaa18fb72d3ce7ca8e36525d8cdcae73", - "0x48b593a335aa2699a5bb5a60394845c7e4c78046e050aee1c7f8831249f75b26", - "0x6db7243073caa6e5c0442f2f3926885fae0385e0238a69784ea8a00c854ef8c0", - "0x3a104e4932193c644e2135008d78c5153a9331e6d9dde878357c250a3b42b5e7", - "0x74b3b4666fa9811702d4eebf9680053043160be3a6c31a0105c703e07d530710", - "0x179f67ff0710067d3180ec03d664fb3d9936e8777603b051ebad4cbd0aad7763", - "0x38d5fd43ca73f66127a0166ae074324471b1a92e6f4bf99fde235ac408b35562", - "0x1f43748a027e7731c2fe5343ba7b61d7c6c6933ea45466b439a43eee1a3ad398", - "0x6b130b75bc42dbbf76ad97287a3a130ea29122ce7e48c5a8bd1e80a5f3121364", - "0xcd17f77d87174ab6ad6f2dc60d37144aed40b3620a9e6c9ac3e328aeae3097de", - "0x3b7fe9ef499348315c1a2877bd7fa44b622fcabc588687a6de4d2f75aad3f642", - "0x6c73525865791a7ca8410363d634f6babfaba581d7a0252c7f57dc8c8cec583d", - "0xdb16b0220e129be4c929888a8a46d21d422a352ac7b0360711d786eacb56598e", - "0x44fb22efd89e585079bca47bde1073dc052f8ddbad2c27cd8e2839bd4350b18a", - "0x1e6f1395d417a94162117b9371abf3f781a4b05d787f6a38fb0101bc36e548da", - "0x3eddd0764196fe15d7ac7069c04c4bf23070e57931493e9a0127fc521187b698", - "0xec104582dffc06da3cc1af1c8dc7522d26ab2408dc0f62051da2ebae1ec1cbf3", - "0x3616cc0faf8a5f5c19cbeb482be2ea8de01b2a3e81f067366c715607cf29078c", - "0xd37ca9cd5dc7c3c4e2d2f1b3c8db2a016b52444f1c088680c8544b6cea30cfe3", - "0xc3d85c7899da428a305d941e3637e33eb4981f071ee07c1ee1c82aba7c248167", - "0x62975f10a20de37466b1822859f11774efa4f37fb701f6cc0695d206bbb51582", - "0xd940124857e67e220e3d4dc27eb75ff048aadd9b7fb29b680cc3743b3ab6365b", - "0xc89ac3aa4725191e56fbc87d41caac2c692dd5adae638bf741f0ded040ca66e4", - "0x97454878805915bcb60c9915af0fe0558987dabe5d506e03898dede96544dec1", - "0x6ce55ffc54eec31d980ece5204876a3f366f3148a4b8c10cd190153cfc96defd", - "0xa4e923671f4ff6dfde2f11cca452ed4208808e93e1131de4ce0804cbe2e0d3ad", - "0x772d1c2a0e70fe37ac0ea8d7b4a789f92997bd654809f20f0ff7ad76a6d975c3", - "0x8d5de87bc2484465a4876b462ebc1339bc13b8229e6df4f1a9e9b458f5e9adcd", - "0xdb33cbb2dad0eb38613d69392951c6062eb669035691882fdafc526133d15d21", - "0xb22b8c0887f71de2da3d81a5fec2213ccb8a32060211077e2ed1613cf7962e94", - "0xffbc5a82fe0c2b3f3f34343ba6823f35884c8b1dd80fbaa68fd5f33a960034ce", - "0x9640ded5be08a8a7a2e6291a91bdd58bd108205f4cef5209ddd338ad764fa9b9", - "0xaea7f934206d00a592502b8b85159e64b56def4c72db3a790ab46ca81c75d672", - "0xb99ace258fe4e6be541c6e3468913f4f32ef9e9d1375c889e17ceea0c606e729", - "0xc54ae75381803d00b52ea6fa620766662e6f7946d550208743fa64d3aaf22c54", - "0x4e773cd4fb2347b796595cc67eb2b5c7be6409bd8b1944f4cafecb6fc5a60a0b", - "0x263f3826196c238c24d4c792c3c45fc913d4cb94c2d3871827ba43faecbf4d94", - "0x7ae1714256e21b9b45778795cdedfad1160d571004f5ea6debc16406bc2161f6", - "0x0c271dc055d8fb1ba9bf133f3c85628ac3c2b588091768380a881a6183514b51", - "0xa5f41cb430b02fb1027d8e99cd94dd6666516c785d7f618a0894f38f811bdeed", - "0xbf6665cbf1037e0085808897d8b04932a6ced6755fa52555ac00737e8029c7b5", - "0xaaca2ffc61693a6f379e54af473802770b3971f6accef49e5a2e8fc122e0a490", - "0x7a3eb7782e2c02776aa29964689cb1b880201e1b81c8cef39738d7f7235fb022", - "0x7bf417dda75c46efba6a8344775915d2b69f954afd66d8f52576e106d7a7eee2", - "0x3a324507874480d0f4e8466ed6602c99fcaa7907b61e9f2b3f100740f7866fe0", - "0x3589941fb7bfda9bf50ad93cfed18cfdf199a6468074416aa513cf83cc00dd2a", - "0x66b0965611bba105667a3990de5acdbd398d8d6e2cd0276b83814c4647bfe461", - "0x703258ca6154ec4cb1b9162678e3bb546ca6f9e626702f5f62dda98fdc0fcf26", - "0x2a9a8e3537b714cb3e158f7ecc816239786ea3787b1a3bd40482f02eb0b21595", - "0x46104b558f57296b0775d63ee4da42a716c234f3dbd7479204f35b31f4b3d55c", - "0xe7d9d0a86cc8b76526acb8e260de17508874d1db6ad19a4a84210a010212d43b", - "0x04af6e8bd51cf4c4307b2381b2e0c54cd991ca3c7f49b8cdcaff3aead70efe48", - "0xab8fe05db68e486bf2be0c507b834b6e496c1d1fe560cd3210ed7fbf0e9b867a", - "0x0e6b5f226d0bcfbd1f0a2f61189592d8974b16376fef3d0a67f757b796ad6854", - "0xaab68c29b061f8f72d9f3c6f2e318a7125a01010fb0c547835fa31e72d8eeabe", - "0x0446f90437150e4ec6246be5c718e5054d62cbf5878479457d522948c6e87f83", - "0xd1b4669e21c0b175589c0942d4423cd2b438de6665f0bca10818eb6246a07749", - "0xc20d1a68c015d886ef8fc3dede0d116199480164238617667280f833a4dcbb3c", - "0xe67504ba38aee984e9118960827ddce0eaae3d8797bfc87afd4638cb1867e41c", - "0xd3e985af3bf3e3ad0dbcdbed9ff1b04037bd1ff2e71886db3842a29f0ee8c4b6", - "0x8b809d1ae7a835f318f471ce227f7e7ff563a15d1e2463e8fce5852c9a3f9ce4", - "0xc232b56170a5796aa4333d29ad8ba43dab2233e0cf7b48d100aeaa4b2491d6da", - "0x9c338ecb25290e91a83978df4f5b7076b299ba5d87074c36ec96da0b3aa9351d", - "0x616a6134eee1221e531fc6d6b5861f5ced64e9b56505b169da67ca3c47cb54ad", - "0x4afd1e60cbeb40301c2ccc7129042f9a944f4a383a4f34b8acd7aa454fcd0e7e", - "0xd52d1be650ed156ba12b0d6be4b7fda1fe89927bd7626ec0ae45663848144e7d", - "0xa212644d968f7d3d89c6f12c3c3077184943d986dd9cd391d48f8f98eb1bc6a2", - "0x8e3374acfb9d1724fd7f84c22fda25f91737efde3d667f607b364e51beabecee", - "0xd77eb30cd87046093b27be1a09d93cfa5261b780b99116a79d6c16be7db838ec", - "0x05093b9e39e2d9f4fa95ff386cb2af67861359ea6228242be6b323c1eed5c7c3", - "0x8bb25606225d3451a981af24506a549e2bf62a362149e4c77ac72eef6316e691", - "0xd2749fc4a37792b3716634e3dfb8a80ba3e30fd73bc119069d507bfe7efd8a1a", - "0x3b58bef2d77a04b3281e6cf80f984b9067290bfe02a596b2295ccad38e887a33", - "0x2f69797f1800e5da4a4086909058ec857695a220644e61788b24ccfaf7e77137", - "0xd81872c67fbbd1a69d4805cce578b9f36bfd768d3fcbc2fd610182a7696e23b2", - "0x2d3bc9fd303c12ed1ca7efe27d85c7b5ffb8e079e59c86977a113bbbf863549a", - "0xbaef802512a7ea5006cf816c51c35fdae44a86daaeb6e9dd8fd0c37b4f744875", - "0x2e7fb70924e6f0b74541f2f4cb13f49bb3bd577f5bfe1bc29d805b0e7e1a3df1", - "0xe3918602d83478eb416dcf80103b09a051d5cffc71b0cb21461c5031d38befb7", - "0x87dae7dbb38501d6e84f738c11615dd9eda5f7b77e096a765caebea6a8c691c2", - "0xa19f74ad3f4e218fcfb15e4af95713cfff4f5f58169b789167e2b62617023697", - "0x744930fd0046b3f7de0ecf721e3b36e4b36c0f49eb98bb0c9ed33d40e76a2017", - "0x5bc7194687200989382831785b43f7f5efb23105ee2dd7a620a61622a2afec44", - "0x3672af2176d897cb8f64f2decfd924d74581bd85916be85e53f2542a54a24b94", - "0xe2e2dd1875e9265072d96bda4640ec6beefdfa9a91241ae078cec4c2a1c9b8b3", - "0x5866de65d88610e6123b7a57e28e196afac484045261d1a16b83fa232ba267bf", - "0x7224db0ae652be5fe9017454dd40c744c75e513841b5cd11d5fcaff598265c7d", - "0x04438fdfb56d125bb13f6b8bdaced6946299f8a32610205fabf4a8db9c06af60", - "0x9e9af6a569b87a4717b94d8253a0078409bbce7bc08874e091163b621a75b999", - "0xc8a39c68a74f23d615ad49d9d175086bf4e1047a750165bb071e3cdb70e1d639", - "0x10515734fb6d38cbb9a7ef33ec7831646636f845ac40cb24b08c432422763466", - "0x1604dce1fd615791c66246a7cd82edbfe860a5ac48d000cac1984faebc00cafe", - "0x56311f68cc563946e251d8c0ea74adeee6ed8dd7aba8f6ea85367defd5dbdc41", - "0x3fc81adf318fa6db1c4e7ff5424b235943667f2f3dce5119618e0273eb23c93b", - "0x38576704f6ca62083130dca418a9b68e374944d63521fc3f4b7039754d62f63f", - "0x56e2a402baabf470d0f9c3496d75e2c26c26ac159b996c370b118a313a9e9464", - "0x501af705914bfccf4ad29c38eee21641590cd8d334dce9055d90bff57b8fa556", - "0xa734b7f045d2a23ce602b032357a66763714c6e4785768f07d55c22d2f1de372", - "0xc80e2c739a3d142a4018bfe2074da8da33c471b93f5e7b44bb150b9eb63e956b", - "0x7a0b1355d05b1bf4c634651bbb2b6d65cb0a9772c30e4024f5e781e5d404376c", - "0x73827f7c7a15db5c17a986f31dd92de05579b0bb8def065e5f1cee472d00281d", - "0xcd64ee4b2a912d48e6beb06e7d6c9c236b5815434c0ee21cca0a13ff23dfb85f", - "0x768d2fa63c53689ed899f47c6f78844ace885fa18a36da427428f4af46a9e1d9", - "0x643c26723d5a4831d3d07f8692a6dc4456bb6190ce2abf1abf69159942d5d548", - "0x03e20a75c4546d5f54bbd7fd3e54c795c180b880563bf78ec55530a89188a9a6", - "0xa0ea4095dd9fc6f817c656913f8cde4044e2806488be48770de5574c0b5d5f8a", - "0xeead5fac8f3c83c5a10df161d95bcf1d27184c9fb9fbd813ec5f61347c11280e", - "0x5afba4426fcae0f1769e581fa6af97b5ef8fd417771f10405e1c9d09a74357d6", - "0x087f65be7fc2a14f216d7ce418a03fcf1e6169e8620db65c11d1ed6c0afed240", - "0xf9a7e93c40788db39b241e03afe329b6336187fbafa40c97ac405fefc1eccfc1", - "0xb02450b58c5afdd0907ee745263aa6beef662646b96b9ed28b0dbcea4f358667", - "0x5029f1169c92671ecaad7239f40fd93adf3ec07ce2ed0c4cac38b0cc8034def7", - "0xc8465a93a1ba7ec3296d98e0b01ad27bdbf16a347c5517b450905d3c3668d505", - "0xb85cd82c551bcb30a747b2258327dccb04094f918e36c3f120f55fc35abf59d9", - "0x1721b2fb8963696cdf32385fe87e8ec2c2d7fa34e099bb65498e4a030e20a1c3", - "0x6b6dab262c1a32a67353409d2f8b791b1799ee6a8e3c880877af0bc5cc5b812a", - "0xa634110e4766d3451718061efb890238796370da3c4a53a91faa96c8944d2423", - "0x91ec6c6f807285599e0a179d0d246caf10152e733acf3bb967bae35fb36561ec", - "0xc3b2012d5cc8d796d1890d39e2b1730dd53df0b98274bbfef8c93bd969912852", - "0xd036b9b29589cc551bf27ab95b6366d772e7d692d8fba48f473a2fc2d023dbe2", - "0xc51229a0306e56a53bdab1fda497281e23aba6ab17301c2eef3ce3d01f56989a", - "0x2652cb79e0c18dfdf545562b8569cc2775a1b0b1b465cbdc5880b40ffff22676", - "0xf24e0d6c03961043cb41638596c4ca02e2a2522a3e828dc4613a72ce5a535a67", - "0x01725e03a7cbfe2d6c5623829e4d419bbeecd1c7f925dbf1667979bb4da6650a", - "0x3b0c9824b726b2b556c6c46af48b84f856154490a51d775ba06aad48055bcbd7", - "0x2be2f1cb826d7575e53bc5e14f1882d73bfe145ed91b2ff56a885f66e136db46", - "0xd16752cc862f33b14f5976049dadc4f15f147f2fa76c50bafb38a7cb25c8f881", - "0x66b3ba188143bd421009c082031696bfd6d29fe7b9c3345e7e70bf6a470a05ec", - "0x83e017e8701b533c9fd22e30d63c3781b0ac9ec1dff4433fe7fb5c8f6f4e67ba", - "0xe4782b025953c5980653abd26eb95de1bee0524c14a74b970ec5615f98ed6768", - "0xeda29e9b36262e7c79ef9c0b60fddc66bae542b19caeddcdedf439573f773cf9", - "0xbc02ac1f023993253394ca965f4394bb40f9c7822ed6b2cbdd249e4b72f9b637", - "0x8e1bde0f2541d20b7f7e8179014e0f6b98eee5c1e0278ff1de38f4c13fdb4161", - "0x7b762d3d64aded9ff99e3423d7e676dd307b765ca6f1295e079ac53d5a4788b1", - "0x1027bb44ade6a1f82f11e9f298fd3957a9636bfbb97457c319e3d57ce72146b3", - "0x963864b3164578d4a7e58de16593273067a641de752b6df2c9b8bfaf970392f1", - "0x16bdf92929fe3629a57f737d83328d034c36bbdcd006301f28dcf52e1d1cb542", - "0x69952e47225f1aa86d952afb0fa8c668ae710a10cb6a94477d518c8f771f5c30", - "0xe68895f4ae2e4a35fb7e0730a5ef9c3e3030f6351ff6381f77e6311912ced98b", - "0xf28d799eeec538dcb2f371cfc6aa16f4a6808ddef0e6fd0cf72fde291d94f8ad", - "0x5a404922a9bfe57eb85deb66d8d83c869ddf96eae17e7fdfafef19c19efa1eec", - "0x96b735672e85aa95c2f8b4bd5ac80942923cff64a24991b3103e4ee39fb9a8d1", - "0x83d922f50174810fc45daa5a607a9b4fce69d8ab86f428ac57ffdcd9c2ff2908", - "0x3b0ce5a62116eafbf445afff0674112f01e1dca0e2af2b72d0cbbdc452177d65", - "0xe86cc93417c7dbcd4b5f051f4dca1394d272dcc2101a9e94a140b20f5e4c8b59", - "0x99b3e1d593b682e1b6454675593ed6828f8f4c5888b965981e3a7c602d89d031", - "0x81e0eafc2a2adf3d94938c413cd9f588e7525b91f39a689dfc3d0ce6aeb812a0", - "0x699e692ff89a918eee6d19a63caeb07832dabf1eb28d04ec97150c87045d9129", - "0x90ff00b66a14d821b05f692ee6d100dd61abef3234fd29e94bc84574439ed2e0", - "0x2de5779a122ccd84a88c3adc4edf7c1c03dd1d3e89ab45657885aadaa087e833", - "0x775a5587a907aa5ff13bafed032dd96c312b19dcda1b0e74e8a4bd327fe90e50", - "0x4fa48215f975442e6b9ea0629d308667242a7fe89f0cd0eae55ef1d35a3d6ab6", - "0x2e24c24731902f1b9e4042eae9e946b9d884dfa9f733ea5d4f7e778b68daed9b", - "0xf23a4a6061f45b1ff2095adf02ded238b37a0ffa9653fa9c1b0069e37e8552be", - "0x62b94eeb74bca8d9ea91aaeec5c13a05dae022806df28b92ecee99b47de999c7", - "0xbe6c1797cad2d5d9ddac3b3adcbf1622241e2560e3407139e24dde3fd8d3e435", - "0x7df0788058bea0911f2e30133c835515bd777f7aa9cab6bcd27eb3c0a6e360ea", - "0xd87066a4721ce567f44abf179184381d81c1c487158a6c57b5f2455472209a5e", - "0x0aec7d3081c3ee7d61f36e8c7e62ab74e41f00b664b690a341b9ff7feb5adce6", - "0x16345b31779e80499dc01f9ccaa0e9981b2b336500f33cb1f2943b66ccbf74d9", - "0x9ed6c6685dfa2b65903db0b234f4539906121330c5d55e6b2a2fd30549b2dc23", - "0x6539602958d9166335a7a0396ad72be611661bdd40c786cf9b0f382039c6b46a", - "0x72bbb4a201de75b9a4b5cf7381308953234c62f295df08b919c12535febf6fea", - "0x2d37293863f6b90f43979ba5944bec302008957e07b7c7f2292074a0a3934674", - "0x22677859ad20cf8b924d516b9f979652ac91a27459a4eba5455beac7f5f23128", - "0x704c898c04ead58c2fdb2c753359d10934e66b326f104be28ca7a32ef05a2bc1", - "0xca2b1ac29937067b761b57b58ae4069eecc799dbe089342bea274e56fde3d1bf", - "0x4a9773e6a2e75dcf1ff46c8f9931b8170a3609023f47c0ea9c4af000798bbc22", - "0x7e4dcb6c256eca2cbe9d168cc78c2702c373fd6e69c210d0713e2766baba148a", - "0x399ea7dbb66b95bae693402eec304f9cb6f4c6ab729d90ca569fcc2bb24d6442", - "0x49d0e561773458f834c96e8fb9496d4cdf83f2cfab75824cb1eabf8e8962c18d", - "0x80a0a209e41d0c3ff07ffe6a7f1af67997662494a327fa7f3bcb7209340974ed", - "0x2bdd6d6e7ae3f4386fc655817e92563e80f46e1b277be22f81a689de7637ea1f", - "0xb77f32374161e54c50dbc38822760874d966f9e098a2ea7aedf650adf25cc0ad", - "0xb49b1decd65a998a3dae2e4725eaff56276af3f0c50b2a3a35b6e94738d32808", - "0x319f78ae885011458f47ff1a110881cb4ac6a78c7d4d7a2656ddca73a88b58e2", - "0xd9fa2f47bedc0b405df34c98582b553dde76a46d38ac86d9d357ea0aca60ec2b", - "0x4283ac57e82bd08970ada71f4617728bcc467eeebb19c3aa20208a82b18fb508", - "0xf2ea3a5ac3bc77ff64f7c686305044cfe539856fb2833cbefcb283317012062c", - "0x840a9b8c756c3c3de7170c3597bbd085747b97419c01bbe484fc4cc7924736ce", - "0xa88682b957deb995307ed875c4044246d779e3f8c584cad75162fee119613806", - "0x7b9195c23833f65832a44d097290d8573b43e6f3e47dcb8c3826ef9a52fb4ff7", - "0x57bb0076c87f2e7187ea92f9f7643eb5b4b823b8eae9f6e74c8b676dd86b81a2", - "0x68593a8268b245a5c7506a05860755cce1be795994a7d736aba41ead4c025a68", - "0x90e21d5927d39329874688312eeb5296677ecccbbb9c6bdd4400c50c9bda09c4", - "0x773c0cde2d1f44575c89106a01881eb5d9593bc762a40be03ba979496ad7d229", - "0xc1dd843534e9844bcf406cc03b277e71d4e73026635412e25f3555d099f26a55", - "0xdf3f794bbd98096cbfa17e168c0de845383abf52fea618937ed81d31cfdd88db", - "0x1b05b1f316013609fbf813cae674f193a9bd8a75631b55278bbd37513b85641b", - "0x429321ddb251fadec6b6f794acdd8cc9d93512b98af23d20749d93c3c9fdbc36", - "0x6cac90b28ed13c907d094bad45574aabe2355e13e6a9504b6001e5fbb9c25235", - "0x89b43a3f63a2ce4f67071a121f447d7e843948395616116ddeb57a8714becd3e", - "0x6de560c95a0483d51410f66f38884947dfc787e1c61d14421129773010b46e0d", - "0xa0ba45049971dd4b906e73f917fd16312646d53c0cdfdc3eefe53628a58973e8", - "0x3d4a4f7155eac18fb5a126dcae2035155a140a84718f33bb20d2f1461e8cedb7", - "0x1cc19669bd91087d2046770cdc34e8f995cbdf2a0cc62bda70d6074ed58acefb", - "0x7c1c4aa1817de27c62f35d66927d924376798c954d65ba3ba02c0528d11d748d", - "0xf0d67a8f3c5306165cffd44476249c254898bcb26c937f10e8ae244edab1b972", - "0xcc20c5ecb1d3e83bd56e9213761f8320bd40982ab5fb669bed774b4490637932", - "0x7935073eb3e5c37ed1135cb22bfeb0e21727d170d106465fc35de75e8d56cf41", - "0x7d968e74212c501d0bc26ebb816b57a37a8cd2720caddb5bf66f489e13a61bc7", - "0x2c134dcc35d50c63a13bd8868137e0240280f049d7e392b97ff5f76d00aa1296", - "0xfd046f06c1d46d9125a119f786acdd76a85fc596f21cb15f367933b717ba7d83", - "0x9f5f067e4af3c8e92d2d54dd061620f0a13a66006b162a1eab4b1707499597df", - "0x8a6e1fb6205a423ec2920d448a376b95cac7233d5312287bd850471fb49e4f8b", - "0x2a6ea987659383f9885d24c935fe56de39d45caa89e60ba1768189318974ed7e", - "0xc2df6c8a4eae77eeaf11d7e5e2198ec4a33f19f5995caab4db6577fc1ce7b957", - "0x1e2ae8a42eb937749284820e50f11dfbfb606ddac3efb201e0b2664dd0196d63", - "0x4d63bb91f9f8a9965c460326f0604a27ecb0fc56f2126c6b3519b08a895747b7", - "0x9a46c2ec5dcef6f5c5b76d4b08b9d5085709182243cb8308a2863cca8cba13dd", - "0xea57019cc85f7cece4cea7eadda96dc9e464df2689957ebfe8d817b6996b2e43", - "0xb40e930b31dc1508480ffba351b102c8cab53c6603a0dd88bfed1b0da5347153", - "0xf51ceb070d8e7cb998cf4979ac985b4850949b4456980f523d8b9d72604a43da", - "0xddd28627f5c7bd213149bc4886bf4bcb8304f86068694fc743ac81ed749aec3c", - "0x9ef2b3df587caf086f4a9838a829491d1ef45db132ea71c6bc96a4a40d833e92", - "0xc9f7d4e19501c48dda5b0012cd93564898ad080a653e29f3563ecd40d36de84b", - "0x0565db36e6fb7b881eae309fef6fcdcace7c92a7ff148476b627c012aaefe4b9", - "0x08912abab10f16b92ddaa3663375f6e2b656e4ed89f2285aab6e410fc59e650a", - "0xefe9e68ca3bea929722bcccb5150884eb23c32153b14644b1c7f4e033dc3f718", - "0x4b33e0e078056d9efd857f909d1e409516f23da68105463167e23d71c90f6366", - "0x63cd4d1c69c4168798a3d9f15388207ea10ee4dc6be3681b0c7dbce5649d8f90", - "0xc8e7c25854d49022e9a0ae2eda8e7835a6db9ef7d612312e9deae23621ba240f", - "0x22c05e50f27e8bdaf4abb0a045d0639bd7f80057dfea638df6a7184ac49b738b", - "0x4a34356b5a447591ac66c51370fb6539bdf50fda9600082dadd91d33713a74e1", - "0x7147d53533ae40e886f6bc1c3b65c51570d72398f46e3266f2eba7b707b46b3b", - "0x1e49f75a30695e9bef14b036bd1c0f2b322042b2a02ca56604484e913b284c1b", - "0xf2445d48e823bfa77776234356ae0d3c1a850db236b3d2a95e5b00c4d7e687af", - "0x7034246c6342c26f5ae974576358f993d0e5e3c577e6aecdcc182c78082ab181", - "0x965067896ebcb2628ff10127508df1c811087f45ae258a0d8179d576c21e4891", - "0x384ebc34021ebdaf95e1bf0d8d61777b96a1ae65163cd3bd9b4311970a7918b7", - "0x1ca6e203cf1e058f20f5a8b1e33464801bcbbe04c79a7201ad6036bb8aa54101", - "0xf5aaa21a85fa9d502ef278262acf71789a3755d36dd8b3794becab7fb2d338e7", - "0xdeb7dec7ec133b6ac05c3bc0fea3b0002c8ffe58b135f4ae85b71fe0350dc7ce", - "0xd5773586ddb40d4c772541f743f7a4b08e9b419fe5b5b3536afa0b3b888725bc", - "0xeaff5bc016770c7cce7be21952cd8759a8d0eeb1bd849732c15dbbb82c613a74", - "0xb1a6a6acd39b4428accf9120a46d74cca6451ea4a182206425a8a64c6d6be5b2", - "0xfff27e5023fa1fc365db1e79cf3283bee2f51333059bfff47df39f12875e5fc0", - "0xadf1ce203b0acd4cdaf1d9a91cc158a21c823dcfb0740f089ce0830102b1cdef", - "0xcf20c92fcfa22d6fe7a60d1aab2d5a942db39d447d4ba1e9e76888a64694f1e7", - "0x210a684182ea379b50d641aed1baae3bf3752fd84feac4b3dd4e110c5cfc4ce7", - "0x220c5460803cc8db7a2b645dd5a4525b0703182cce173324d20e1c865a157811", - "0xba87b487bfae148239b44f3ad7663fd66cde8e21aac9e1a50bbca7bb7334aeff", - "0x2728161f9f040b4d92ba838d841404604d4d838157fc66f20d6c4c61034fdbda", - "0x3f8a6c7198528e5084b3e8d52d3101c27c5bed8721beb831ff921eaaca4c4282", - "0x2cc2574320b3c7252404db7c67b462fbc8d87bdbeb782ab1bbd257ee634a35c2", - "0x13aae0ecdc6a70d85412178ec12e971b2c4476d5e624938ca0284ccfa611d11f", - "0x6af7161831c3442db94cbe28ee9385fe79339d533b9c0fd3266213c2a5024a14", - "0x06185136927e5bc26ecfbf2299a0fc13cb447df6ca4a60e968be8c2b7ba1c2e3", - "0x70ccc84931d910a2489d50becf82383a836309bb90dddb21026d9e4e4368c85a", - "0x400ae9e4da0f847dcd4bb65e8f9f661a3b0deb78346b4f1f84fc712fd34410f0", - "0x57aacedf503300888fcd8db2138badbfcd663c63c3cf5b3e35979dad635c24d9", - "0x8fea6cc2da76b7cc7062af870cfacf4952b81f09c3c15d31145fbbfe1c0806c1", - "0x5125434cc5d4137ee31b51ed8306b4d665b8dc66504661b46c227e62a9ef1abf", - "0x254bc0b61211e0a57755d73ac618012938472912f855972b7ce62677f5d0e64d", - "0x2d231389c849ea459a7530ac1bdffa4d84908e2c61125a70bfcda932cc8e5efd", - "0x671ae73d4739bcd4841fdff266803117c5684c61031fff16e831a3bffb4bae4e", - "0x64c4db66cae82a96f29296b481619d79a739e2dfce0ac1f659d45f526ae58ee5", - "0x3f2f08ce2f21253f8c9a3fd650a885ca0e77f720a21ad5b4c0867150a0274efe", - "0x98c48268710592ee4c26620375968f2b8730a1bd1777239e6ffb9f116c6e1284", - "0xdc7a5c095c255e1984b4a5fa75c7a1d95d98097fc4eba898e644ff66951d8dcb", - "0xeee7579812ef09ae31068e8087536362a967b2893b709a458822449ea89a48fa", - "0x296e707796c0b9f9a2f55ad06c42d03625dab94af71c2e1c7016a7ef6645bf5a", - "0xd9a3eb363d4a36300dc4d1903a83447c89ec286f8d219f1156335da283992d60", - "0x039adf5a0cfbc394847d8014d64700ac4b6e78b531a1e0328bab256f7c407116", - "0xf9a92c6b1f0b0b3d7ae33cf5ddfddad516bfd7b21842d76098737533efd4f7a8", - "0x5a5d1fa3b8e05a81173e627f14e689c166776b93df401593db8035a65fba58f5", - "0x34f7fda3053b9d06e217223ee06fd194e2962c4a381482429e596df1fe319686", - "0xb4cfd9a71a98ad52c7705d55e96f04cb9064b1c32d3c346be51370b56ebb0f8e", - "0xd46a93765af68d238b776b240afd464a24d8c8bc869280ad618fd0fb6360e878", - "0xf3c622a4ee05d1ce27d59e7b9b3748547f4efdb1d6ff72a58fa93dccb7b76de1", - "0x6cb017c4bd8bb5186590cc4559fd9600399485ca917b10556b98cd7fb61441dd", - "0x7188f59c892b8754845d73f534587f27b7da67f42dcc1c73390fe2970bf0ad28", - "0xb4b17c93af08b9f587963e42703379c5e4f760502870b8096917b09b3950ee35", - "0x03165bf9bc20c87412a41209901d2bf3c8bc03a8586a1706fe1499641cbc4775", - "0x9ef57b2126a38c2dc456c13c272de53366dd1bf1fe768185a93f3562d064fa8d", - "0xbc3660089065220589409f7063dde34aa080179b3f22464fd9df9eed98d88b0f", - "0xbf1cf6eed0e0270d6be602040a97600ca7e1279db4279a9fb7ee643345a264b3", - "0x4cc471987bfae3b32179aac7018ba574c0315b9832915b5d0b804b38e9def6c8", - "0x51351557582b1d821adfdad36536b59b28f9a1f1243115486ce44d4b22d3952e", - "0x66d6b02183b9def37dce37b66ba4e9939241732b49dbc8addd147a89dd9e0517", - "0x47261859ce169e56d4e7dd75c5e2648597c7906f9264710c7e4dff74f353f739", - "0x38edbf1db358c82d0c945a7fc024f7fdc1165331cc19caf6b8943d3ce76b721a", - "0x5231560201678a39175187469f7e36c1c729ae060225012ff4f679f3fabb8237", - "0x763bfbeeab624de07a6e758368f8f61c0fcfc8cda088d2dfcbeb47a5eea9648a", - "0xce910446cce07477d424f791a71a375830ac26a2543b8bd1343e0d775d161e5f", - "0x950ba34133aea3c2947a5e5fc1836375e81c042ee999b60dd4a27e6492fb41fe", - "0xdc24f20fda563bf74ff7540a06f3631af8ab3b3722874a6b383714f463f5fb43", - "0xf2250ef512d3a11144370747f2c86efc73abaa81152bc6083f87aa217d16ee67", - "0x8760d0bc8f28eb2504f3bde3e429a47f0aed1dc2c7abbaf01c68033c07ad368e", - "0x7ef1c11b1f025f7e2aca2624aa9a11781cd860f24ce3fb525e7196e590fc5ff0", - "0x7c4e717fe77c8f9e742e312081d51418614031745b182746c7ceb4ff54deab6e", - "0x56c82f80b8d52128275529fffb3ccf7376f411d7cf9464a15fdecf617b4e7571", - "0x46f7d3ecd09c29d36a62a1a65bd3c59a14c82687b5466c9c130df14c286c2a95", - "0x1d04deaadcfdc7a1e5e612df6eb836f55caafb4f9bf4f1200fdfef4f14229f26", - "0xa0f5e65e99a22e14ebafc71f093dadc96c68883609a7460f904eb15360e3dd6d", - "0x8ea6c444466d4b6916a1b6dadafb87d75e2fa1ddd1836425dbaaad6e99f4d68a", - "0xd53d052bb709813e6dfe218dc4bd99c5ae83b6f5993b182c36a386502713c7b6", - "0x2e0cc1dfae87825d1a1d8946e021dc514dea384241e0a6cb66d5daee570c72fc", - "0xa419612e2bca19a3a8dc7fac4da86c2df4edcc2989e10d3e9050323011676b1d", - "0xe41649d9af504bd273b9da0fde578dc126afb55feb8b3c31a0d4eeaa9c7b83f9", - "0x0121be707b5666d5a78949b2ee263bbfbd013b69caa9566864711bda4c7ed0f5", - "0x788538d654618bcef0a63e31576e19a0872a92733ef7930ef50f8afd6caf110e", - "0x7777a3a4930828cc158f696e6ddcb87696115f473f219ac5582d8a38e0645430", - "0xe15a9f42ce5964358f862fa7a40bff0c8e8d7429a5ca923c9f0d4d0d574378a5", - "0x187bd59945e1cc6a877fb324b1d7ebdf661383ae7e22c56913f2e920de73dd68", - "0x938f100308d20611bd14372b16da0dcbc888868f8bd183d667064dfa8e67a161", - "0x5e61540787c83bfacbb58967280163f55f5ed00e733d6295099588557ef2dcec", - "0xe6625082f4039ef9dccdbeb9488baedf75fcec616ed9d5009deb4eba95cc680f", - "0xd01542aefe234567f106a4f057173b4f6eb5733e0ab9537af2db309edf38763c", - "0xee0174f3f9218a3418b8dd2bcd4132821eb91b31391b7c2c6e5a84d067d21497", - "0xbe26c679aafdea135aa493bab8ee348b255f50bc69592bbe017dd96b0da58b1c", - "0x297e6634c06193ed4725942cec32ccc9b4e77b5d02fce2ec9fbf580e3dfce248", - "0x820d98bcfbc008480ea32b162d15701357f094b1d7c99a1ff92fc0afd9708a06", - "0x82bce2be0a2d468b2fe0d3ba4ec1e5e8eac2d83f8b2e402b3043119a59cafd51", - "0x63ff3569d9a5661b6773a1a5fc10a522ea12a22399cd337ffef75a0d36735ab1", - "0x8431746d8239126bedde7d5c58aaf7f733dd1542c942d415d876ebf8a062f032", - "0x6bfdf119b93ef4da6f48265f4c526f0837a10c8db9c518d0dfe1edf40ae5fcdc", - "0x55aaba1f40c9089c65623f67eef8cdb827282a39cd0778f26e2f73106d3eee3e", - "0x0022a0b29d8188251bf5c6f37c76368dc9c7ed9e00376901162d1fff111273b6", - "0xf4bda8d3cb5b7ad50dfcf2668253e44b98e87d563ce17720dd1eb1a4e1c32628", - "0x994315a889329452a3e08ef029e7d902308022b74aa5a4eb2178929425c90a84", - "0x86a962d1d436f43f6fcad5b61b615f2bd32f10fe8c62428854ce98f4289707b6", - "0x3ce476498f26fd1d0b276ea639d438d7efd3c10451949efee1c91f279ef15ed9", - "0x199b2fef89c1edbe547e8c0b666b7b138d6f94fcfb2f09f26edc429ac163b127", - "0xdf3fd62e7dd0133ffa23a0da13d720373b57e85c28ee97890c355c44323ad592", - "0x92e0cc3bc262330ed8a1f42ad40a2db6c4e75e2d39e24a6ed5eac0855c12dd05", - "0x5b46f058c21b9447f8faaf78b2549f7f1459fa5ddb4211150bc26dd718f8361d", - "0x316b4f0e5b50cea376307236de36f3a1ebad3c59ae28dcf7838339d8711047e6", - "0x2b72ece0cbeaf94140b99cd9312eb891a1264a4d31fd839017e22cd4cdef058a", - "0x0c86b9b2da38f00150d49ac53ccb43a88a44181c90b492e886c54b0d6a93de22", - "0xba5a671174dfd7f877bffd7fb3179b1f3f8444ab14eaa9a0488207141bda26e8", - "0xecf73cee14b1a8fa5c2de5d78c058bd04772666ff455ca4225ac419606041f2c", - "0x1947b6adf9abeeeb55a66cad4afd016f6522faa641c4e14af94cf6e610959ad0", - "0x5467aaeb96dbe111a0d36fa66a71f489fb33ab8d95692695c09f4680086daff2", - "0xa21fd9195eaf856bd048bdb258507351e9a2c168920fd0c550a2340b5176ba26", - "0x9b0cf5690d3c3764f5c102fe1d5139202a1f982bd5afc8967eddaa6bcfb3af42", - "0x32bb410896733f9f6080a5b574b07c0af3e5ecaf69e995751e392c3905c11d20", - "0x5e98b3dbf58bf5adc0ccc9269aa10f9921afd44283837e7cb419ac4fb89f6164", - "0x051cd6e01ef3ebac9f27e1d473b0abc00d870a78cf894cfa8222d00976948b39", - "0x500edc8298fb83a103f5bf779d1df507644e054ef27ae61ccf31d883d85c2a0c", - "0x80c8fd7e50aaa14da3af3ec622adcf89eea9760ddbd5232a49ba55837be5805a", - "0xbb828dd031299bfd428c22110ff5d9f5612447e346e98401ab4a01278834e476", - "0xe2c5f408029af25cb9c130fc8fb5118660d08da399dbec0fa1709d1c0583de57", - "0x1be80d06b4ec5ef612e16bd8d842484039ca5663234174441f0722ca521958ee", - "0x21d755042a542493e44e92b4355af2f06f161c0e2583aaf6862730be7e9976b2", - "0x49b993b77606eb939ae485e82243e530e392af6d68be4ece5ace66a675a7a70e", - "0x954823b80bea8f2007503ebda5a6ae4610f94cc2c9a6ca22088a52468a960524", - "0x2693662c6c0961a92566deaa4a59204a0c436aadc0581b799e6255fe97d26331", - "0x4e80abe082c0b8ae0602c232ab0f766aefa702e744ff142cb9e101a6050acfbd", - "0x9c47c762c73836210a6bd78e5ddf9f2e817951d52b9fea0c823596c3df2a1fc0", - "0xe86094c8da0212cb0cd96f54c9f1b22c11feadc5599f6aa63285971651f11278", - "0x3031451f37f3e0288d61580e5b20e008a2ef5975e5d12345056949dca2c1d421", - "0xbdf90347d794ab3b41da6fb75b5d8d1f426ca2c4923216393e055dcbc89f3cba", - "0x4f7a0c9ebaa4833e7fd0ae0f3ac8dbecf3d97d0036a41ef30230e634142247e0", - "0xd1cdfbbf34bfcabe4a0eb90fb4d8592738203d245d68b753d418c4bfab8ae4e4", - "0xaa56db3fe5b2edc2ed277656deb51e15f86182de49836b4dfe2636de5488a86f", - "0xf67b05c233797d61eaff641e5bd35ee830bc1a8440e06f85e034902bd1023ede", - "0x49bb9be0064ff46c4b7820e7dd08002f3914fbf9250c96873bb3dcc7bddbe3de", - "0x594efbc23c0a371e3e5478c599466ff3a8d985444583b70f275afda13cc05c9b", - "0xd1dc59771492ee0881398f87a7c90db42874d720812c1e105b1512531d9fb1f3", - "0x7462bfbf8457d1df1288cbaf339861dc91c02d772f3c1ff8c215965e555d6905", - "0xee62a752465879dd62d08d7a15a54af1e813a1bbf2035384289bd634e2e99524", - "0xc7c66334011807d017e0df794df5f2f36c4cf496ce0a3589465662f8aa5433c7", - "0x839af48e65e3e1fd3d177d90f1dafcbb0209b107bf882cfe2fd514be625c1396", - "0xfb4eeb8514ede5bc952beee0e7e78c6d7ae544e6c4d935a6c92375b224e40c3d", - "0x38576e990356b7c44eb1dd531fe28420d01d80130e0ef0a42b9a8b01d2687822", - "0x434bb4312789b8cd93c5f930f4305760f86c54ab225b35bec70954aaf2fb4c2d", - "0x19ae08dadbf2f7da90ce777913d59e74592cacf6f385600f7d50cbbf7c4137a9", - "0xfc9a8ba8d7eff3a582725f8fd1539c4f77a87ecf23505de8a321ec6d568cab5f", - "0x20fb269b1a7908ccae92532424cc94604ca9a0908bd7c5e446a687cb3be9e0d1", - "0x9fe0a0e3511056762698573eee8ee2b0b87a8b6daad2141a9ad00c5b159521f4", - "0x3630751af37fd3ae22e78198868341e86735b03432879fb159628937c6bc28da", - "0x427789cffa2250d89b99d50969d8dd9917b5f4c721ed9de79cf81dc59f94d81f", - "0x5ad7e16bd42e35671230458f8f97c18baa5a1f81b675df259c9d2d7cbb09fb77", - "0x3d58c8ee704a934f6de776ef8373c653140e089b85aedd53219dc0b46ff03b58", - "0xa5a0719b16d8771b34e050660ba965b0ebfaa06bad1973033cbe2fd69fac5886", - "0xcd66e1ce23416fb4662d2b29dd72d9f8f981c66098820058451441213f2947aa", - "0xc7eb6f1df45136c9adeacb03eada4557326aa0a22f9ac0d73df25e21beb6bc0e", - "0x890eb4c610c7036e1494514e1f7ff72f414c51143fbf13cf2cd03d8d37a03662", - "0xa54328a1d2ffd9aa3f38a9e6a0539ad2517c4f6129f01768891acc0a2b2a721f", - "0x147644decd98b450b284d05d7332629e3c6444846f7c71dbcc892d3191f2efdb", - "0x1a19c1a8fc7f9d838cbd736243e66b1f637f49b1d8734c2af417227a11623b30", - "0xa8c14722a6f7e7efe695be4e6a21f2d1c8b8d71e2cf69e7645ecc5cdc7b6355a", - "0x8eb33cc1490499aba376f581b68766c4fd40e7d6027f223eb46e27199acc8d67", - "0x1b6a906a5321b057f453624693d4ae6abe79a5b8fcd63a777dffb8b2ea4184ab", - "0x7185ec1f19e7a84c9f914dd223b382ff56464b01a1b88dabda415a01e1d0a1c8", - "0xa172df0cb06617eeb95d362bb36d5e5ac52cbdb2e5f3c2cf3d9b78ef28fad82b", - "0xa11941ce1c866d077fdc995acf3ff2ee0ea0481eddd142f9b343c8403ed606a2", - "0x1958ac9a77c5b9825c401b204001dea8ee2520983fb3e738a467980a7bf9defb", - "0x6b24d043eab24359787ed7e93543967c9f9b7ccf99894c1ee7768f6235cb0cfe", - "0x6ab8c28ddda71b937d1a6feaa67b071f78ff7ead3a192ae63dac34ff24b8d929", - "0xf101e3da7546249b5b6d4dbe960c713cf152627a7482fc3377aa31f430c54530", - "0x22628e903ea9eb3eda9c93ac92d77b1b8a5ee62706dc5ddbcd079c57d5b721eb", - "0x0050467a543fbf0a232c8ce9f66eaec6c38c3c31b8ce3d590d1b07586374c1ed", - "0x4235a1330a45838b2d2aa9873dfbd59cfc0f0cd16e13ca9292f8342eec255fa3", - "0x2049ff9cebd379b51308220449b3568c6d7843f5b49f61b85808287f3d60441c", - "0x50c2ef0e832df29297dc524fcf3af4cd0988fbfea71987f3ac5cba8d4ec34102", - "0xe7260880b2d822d18c9ac2224d09fa18f5c324fb421470aad5af6c2605b40985", - "0xdf51e1b441b9809b26cd7cbdc4df27eb9c7fbb3bf764971684aab6c63e282a75", - "0x597b5c48840a25294feb135e0318ba6d6ca09aac476b1041748738963136a0a4", - "0xc710bd4b45a991f7f1387c25db70a1964ca4fbab32b738424d35a6e06e2483b3", - "0xd67460a50ec79c90a97e25d4cebdeafed6c897593fe8b24afe47a71c818a55b5", - "0x0ac1f2282e4491fdc8d28f1697026b7fa88f46204610cbaf8811d38dc84539f2", - "0xc3352ebc532273e4224dc0e94ec4cbb83afc2b5c364d71534344793032441006", - "0x42f3d39c81e118a7d515d82b6880104909a7915aeaca8ad64fe74d9fe88a7f45", - "0xac6fa42771e9cd6ba31dcff2455d2234c53c282251ca2399c727fd5521eeff0a", - "0x83ae44822213f59d34543a6d4fcf76b4e22cbe5a90674755072613550d348551", - "0x99cdb65200f9d1c602c5c2ded67a8cf2722cd7c1ae3f11d29a2c1b702923df93", - "0x2adda7cfa12bf5ecbc74ed4f4ea419ad7cd3e7cd03a0e2b32e924aa2ab98ec42", - "0x3da7434a58c60b7fc7fee8e30b3073ff4d3c381288fe6ba791f68d74d24ed19a", - "0x9dbe78f9121e6c0d99fa6d441f6aedf54be14fb39a277cbb5f19fd0b846305c3", - "0x2ded5acc49e2e1fcb57a66a81f59405febb50edb6b41d0fa8e445477028f422c", - "0xa46310f11937ce81ebe8d4c60de1a5c8787a1aac35ff3b6e2f0cc793112b7df2", - "0xfa693856fd1867458c335c6e903e5745a950ddf8a43cd9ee76ce8d0b3070bcbd", - "0xb374a194d9edb55d2946d40a16ac4617e4d0674630c6a970f58db17f21d22c12", - "0x651032646288a0b6fb5322626bbbc5b6b87dc5a5d59f5f39073f2f9574646c41", - "0x7f4346336c95c7fcfc1f849cfa63afd46dde8e346ae0801aaaffa9069f989e99", - "0x8bedf8e9f5095b4275635525fea6169c5afedff2ae434e42c5cde973de77ae3f", - "0xf92ac4f6fdd7801f15c8e0ee163241542d1359372d2189d8cd2a33f925933cd5", - "0x806d437ddf966fc6631a8f81a54eb82080fb2ade64a2b715872d8f648d23b57d", - "0x2151451d68f912b58bb510002407943ef6f2cc87a992e6df0765a4c239e63779", - "0x6a02bab7734d8549be10373e6395c368492b8518df793821962a940d5cf93654", - "0x4de627f1d096c86c1c1dcde2a314ba65700b1ac47b9db95e9ae68f8d28f52b16", - "0xeae202c747b699171115bb56652ed534d9ca6de0e8c3d947d63c6d60b5d658de", - "0x7bddba4a7c160445653f62d27c1622323c784461d52aba5a6f19964194a063c5", - "0xddfcf0c5ff899f19c4ed0604e7b4cc645ce5bdb7b7c7d6bcecfe7f2cfa3bdc0b", - "0x7d27d279f52b83afc9a115c71910d06a054fdc2e20625cd05fe35ecb03734f5b", - "0x415f6bcb216ad524eebd7bf177bce80cc3d5fb3e920ed65a8ba6c02f59fa88ba", - "0x570daa81ed1b593529a07396784c2996d5703f4124f941e99e8f9cf6b608b1f5", - "0xb9da28526fe8300989e16c83c4bfe418c74878be1bf3af12ff3b9a098a4c92b0", - "0x691938f83dd63e39be2fb03422682ee8dae8e0a95bf4d09b4727f8162b2da11a", - "0xde168c58358db826c9c953895b7a0419066e65eec0b7fa479c328719cf70df6b", - "0x65a7b2207932e96c427a6c01efd0c3f01a37e02e7dd98b18ea559c2a6c83c8d2", - "0xfe54ee65b1b8e21292fde2eed9ddda163036cd6745254bc7cb9f3dea737832fd", - "0x543d64c2a6b763cabbdc1a9316f37115fd572d96b5c75184c155e68532fdc8a2", - "0x31b41a4f481a786bcf4029b19e84729c699a8d742fef50040221ddf6785d7335", - "0xa383494d908727fc6198035c24afb3f352a16b29b0c1639062e7169618bcb38e", - "0x3da745966ebad677a703f5db94777fdc307f6e3e66a7c3c5ca24f35cff3f43bd", - "0x32fbcb24d42beb2128d95055706b767f7df7ce16c1613e3195342db84dc9955e", - "0x1723ae6147425f5b01b68de9847d79b918ca0f85a800d981029dcb5e3c62eb8d", - "0x997f18b9977469cbeeddaa1bb31472be3806c03ae77973c857e32d6fe2c4d740", - "0x6c27575f33b1d85fdf9643c9ddd27085f4241518cbd5b776e0d2bce19b152ef0", - "0xacb86fb3209fbf57c42eb86d2617eb631e0ba36da7de5b2c7ed63f168a7b112b", - "0xae02559f4868fbe4f114eb320ff0f3a38086f364a2ee537e6051cedfcee76d6a", - "0xc96e439aaa996d4ea4c276d1592fcc06e829d5f3cebf163aeb75f590896a2648", - "0x87debf5b6912717ec3c0846fcfb5b459a15254660cd5064180c0c514b4b15f59", - "0x37587d340df2b40b3f14746b72c5a72c5f51963d208b02c9671d6c623079b584", - "0xdff2805c029c4e3c249de3aa9f5cb3b48daae4f4496deefc91ebe3251c18629d", - "0xa84f66a457869dfc95d625d40496250ff33894be23a43e53ec892481f1eb4fa5", - "0xf8fb34bf78ad6d52be6fbb5472f13322b506f594ad3b585c04f56ed8d0d9afa1", - "0x546937b89a4d3b59817377b9c2ffe9579d4650cce71aa26bf2c76c571ec495b3", - "0x5ac921894e98005d03aa42e8fbde7ad0af0401e350c8ba98c01543a93b37dda9", - "0x3cc76dc057c73f0e0fdff28d484a092747ca42bc10989c599d0f597ead6024d9", - "0x1992cd7e94e9ecfaaebbd08d91519d6b67857db87e2e67c546371738ae0d2d0a", - "0x3604be694574c05c63d69cbfbbddbafe3cb425d75a13e69d61b50fd8d9c947a7", - "0xd1c82e40206d2a89e3a1a40c4a1b63c62ea6fa0847ddc2b25f63cb9bbb4a55b5", - "0x66ba114d5bf45d50e9ab9beaa879ce18d02a8b4f989c29ea7e9ae604593e860a", - "0x94f74c09ce5fd1c8fcb9cbbcc476af20fcfd9cf01c7ea65c14917da7b9560ffb", - "0xa4e154ebf83215c3843ecbff1dc8f646f221505c21d2a76f47d55466d895f1a9", - "0x28c20a0e95e23a023678443d7bf5b2421975b827dbd617239dcf26bf6db9b255", - "0x55d8ddf586d61e137482e3f4bfc1904ce4e04d21c6e7ac4d8c7d29b1483c8c0a", - "0xa9e1aed191a7a1a92bc99604e3b24c02356ea378b16de109362aefa2fa978451", - "0xd543ad635db78b2518681654f98a32d227fa6a1fa9b7043ad12cf58c91e8f729", - "0x102fb87da8b57948fcb763d7b797233f21523545f78388d8d05c6d7f2a4b388d", - "0x4e7273e2a92e897590988f38f8b899879aa1aea754fd5165364e8b98a66e0d62", - "0x5a06544527f88d9fbbc5905742863d873367d99e574d75496c59caa041e2b612", - "0x6a8957954db14594746daa61f907e4694e2a749ff53ea6b1dbe77d7d1f378d22", - "0x0debcd48486aa0d33a783caec0d6fb1256ffcca39071041fed7f047eecde8640", - "0x9d0a9b13dab1888bf0eedda217c501d76f587ee9a8765bb455cebcca0b705599", - "0x30693bf8c9bcb6bb4187606f98e138700e999d8824cada72d3d07f8c104fd263", - "0x494ef870dff64d1d65b4ae4b49ff13c145a6058876eb657751d58c06f62b5032", - "0xee4150e64f9ead8124d50d56fa4b6e6d185fe8ac385bd2a9db7ee991f6e34c02", - "0xb859d7db7abe48cbb9420d008d94e8d47753cc78962e5dabdded9438bed56b1b", - "0xf8b1fb734c345111704d73fe6944d0a274964a347d7dc5c7cb8677104829f5ce", - "0x5dc002623d9b3b872900b0b313ae8520009de730577b6eb2e2de18ae5cd4ac94", - "0x4d63e346ce7e654db6f067e8348c539ccf622d84020812724b936c19ff4f86af", - "0x2ed43f4e5d6889f5379e42d4a06de7c343080da74ff353960e4cf980f750fb90", - "0xfc61d0c90ecb073c022c87098045b6ca3f2b66c94f3723c679d81ac652b16f72", - "0x7e69914b56984b8c7cc9db1732c3d2d32969a58d6662509846e6faeb1305586c", - "0x0ec9b1b68efc96fc7d831ac7316e0ac0d908bac31be330d905f87b136556c241", - "0x32fdf6c25f6c741dfe468289cd7f019ec3a40c5d8fe882ef662b798491c0dc34", - "0x1ffbc6b29826aa58595d1a412fb90050f2bc3cf0ebc5462f308d4c8d85a1eb59", - "0xf0fc93cc2868cb9828ce16463e4cbd59637672d5815dff03aec7f3e1424eb204", - "0xe404321694894ab375f5a65c339f2cd2a53fb5a10f312082ce019681f6ed64e5", - "0x81ab1f93747c4804a85f56b0c6eb61491b958504ef7b898fb1362eda9b394e1e", - "0x1535d439d0043d1436de963e3eba47335bd7afcabeac4d3fc3f342396d3538be", - "0x42da1b95481bfab6cfec59884bb1ac7572636c8f489df2768fe2140cd2702766", - "0xbc3fd861f6e09efac1782d9d383c4ddb9b4268216fbdf3c25689162628e36cec", - "0xcc74c3d8b44a8cd23676babb4efb0e67871582cd5686fde9de6a052c5bf91ad2", - "0xbcd7fe80e95bbbb5ac93b1df5ff0b8e921e7ebc4960877acc1a95c478c65d64c", - "0x045e5918fa69606b9546f5fe83cd9212000828c54f9cab856f85d3a22536a751", - "0xc0f74dbb5a42bf1656698c37aeb904a33e969099f215a7efeeae0024c3a409d4", - "0x36d537998634f2dcab43b387edb63119c0f4cc68b1979a2ceb373089c531ecf9", - "0x87c20426fccdd225bd3b961c7adc1de3f9ac9640f6d26bd183f6e528089922fa", - "0x195ee1daad5c3d0052d6d633eda1c9f7160d488d4bc54f4ac3babdbb678eeec9", - "0xf9a5e6735f9c5577283e8cd717b56f69dc8306b23781755f2e513d15e6738cea", - "0x3e5bc3e78ae33367ce7450c5da7436f1faf3b1a62d238208df207307762688be", - "0x123e7b4f7e6b9338df2827a238a266376a20a1fd065e9f8a4446fcac4c6c92fa", - "0xf4eb14bfd197d49c42daa919322f18228d2d9ae2dac6dd7113c5e7d12d2ef866", - "0x78836678c20a40fbe09849fe190a9e74ee070f2056d7bef85d0f124676a93130", - "0x7986f7321971519933aa5dd507074c7b007328a7dadb8734eb4aecf732703fed", - "0x14485d24b7e90fc39d91ad6051b7f596d320b1f88f54fee132e032753e59a766", - "0xa80a9a5db758bfacf831a54022c85a838e30c8611ab4b17bda0641994302b59f", - "0xedf1814fb78abf675f3c5671c3618e5e51105647997d6dea6a0cfd1f0330bf6d", - "0xa91e9fcdc4f2b5e029abdc6b2523079bb4a2f5346d8a5a674e3d5582b8871d1e", - "0x3dbe468159a8c068285c92360cab488a4c1ec37487dd54cdc0b70e6d7cf074dd", - "0x69877439effb3388639ef6e1cfa132bc605bd8a0da053bcca23d82aa453d0040", - "0x6751d32ee3a1244532e6698ce57cdd0a59f99130e1e7e004c2751bc49d10b355", - "0xd1760a4f6e5a7967985125d2deae8bc783e47b1b85e19fc273e33eb2ef88d271", - "0xfa934c087923cc16f636615657477c48c082623d7cdd35508571655dc14efe57", - "0xb6b1e4387e04c4ddf88ada0aaa162345cd43e54482fddb4297e1eb6f8ba1ce74", - "0x3988089edd46b57c0bc83b1bfeb0050b503cbefdca83e96f1c12e7ea80688a79", - "0xedcb1bd8b522ba5155965cf18c25a090be2f8d7871ce60f0f371cd033d5a31d2", - "0x2f887b56b317e9896698ec9059d36aad63d54d95a01d389aa196ae12e562ca2e", - "0xe6d6b89e1d851fd24cf54f6b60c6c93a8b014ae30906c156374d73605aab3028", - "0x177928968fef8c6da017b177f6be85851616775be043b64f72925e6b8a4eeab2", - "0x71a3de323d9a600e15c25a5a6e05089d849defc3830fd69738f548fa4c57aff2", - "0x8e7d92f316f96b491e4831f52a799846810dd032bd720dc891195739192f3955", - "0x12a3433e8cadc005ac35da334d557c8cdad63576613f07df8c3ec9f528e846d9", - "0x1406da1f5efe9d3de6a829f1219f731f3ac875c7795f72b5a9ad25a57970b9d8", - "0x15a849a0ea56a777e00325b4af4d9996747486cc9893c08b0773210306193521", - "0x6178e69be88e7e93878b731d062b29a34bf2951082b947f35f60fba2c8de62b7", - "0xc4142d4fe01091e82626178b66c13243c9b35de0f07a49e3c5f2ddc15b39feb6", - "0x8abda6239557236af27a2cdfbb4fb91a6b136931c53067739578ed13e1b0a2ff", - "0xf85615d9337092e25080430cfa0b7a24c97effd422a1947a4c07239d5221418c", - "0xf9534d7db3b5fc1bcd7033ade59fd66bbb94a5bae91c4acebbb1540fc8bd3b67", - "0x5ed0f8035d3920d6e94b881cafac324ce5688f8c97668715733e0d00733b0fe7", - "0xeb6c474a0adfd84c79b86090c793697c0bb39d6cb007c725c2ae7afdc98df5a6", - "0xfbfce3e019b0b29ad03fd9146fa368f9965050b40733ce297bce6acefc4668fe", - "0xeda9ed65fbb1c7fcccc91de519f69933ae66c8ed59fb65f64751ca8aa06030c8", - "0x359ea9df33d466b5dc210ef0e99f3b4416ce03a5439f49b4cc4c1b98b22a21b8", - "0x14b2e8729b70abac62120541229182264b78c7ba1a1a379ac8a582aa0bb0d739", - "0xcec0dbdb55f92191974c2c8ed716578c5ba04c4584a0770fc0d7d5cfbdcb2717", - "0x07334b05a08cab079354cc1f7a945caa3c633de89a89c18244de81ed56da850b", - "0xc58e0bb71287fe92564d00b5094ce36b7899c346679011e52b73eae45bace19a", - "0x9dbef45ce9abb762bb9b30b61456a90b74b0b6f5b41af9500699542d933f9535", - "0x3cf848f770e15e7f682075c77f7e980da6750d3d4cb038479983e341eea3c354", - "0x608a4c5958ef3b0a324a7cdeebcc3abae89c1371c949d6b269b2d3936d9fbdb2", - "0x66422fbb9044305317d900702c2f99702317a8a83ccae0911a2832f623356c8f", - "0xe977f86ae4c5b350b350a3d6af7fffdc9baa96d9a7cb24834e5dc4797fe39fa8", - "0x6f56fc66544e099797cc3b0879dd20b21796ace01a0029e0d8464a3764d1e858", - "0xcaef7878c703facf29fde1467dcd08b03761872f598f42f5c56b4cb367b97255", - "0x9055cc0c11cbbe8477c7dd35b37a57e7994248c6bde9ac05e85717cebd2b970d", - "0xd7b08c0ca8abb9f07e3df1c4ebdcb03b0ac2018a905d1c78e6825d2bb5ea1ffc", - "0x0184cb109267e58d5bc0193a04548f0c2b87286ab6b03a1ff7b6d88a725662ef", - "0x3005e0af1ac0c5fd6d58328c06cc7f5d89c8c4ad173106fee1a7e37c9f2dcb95", - "0x8f7f8800d29c66b4fb12334b622fabe1cc4ef06e4ded44e4315efc381987cd56", - "0xdf74e3be6d22159e0e02ce3b8f0b405e6469557ecbd12e4432d52f4ae4637bfe", - "0x2fa9a889f958ddc41bae5916657f946273447add502464674658bcc257f1af15", - "0xbfbdf04ab62a35b2f7b038b02d8c37ee946cfee18e10ef8a4cd5409a5fe81d19", - "0xc4e834510182950161a75a843352b5b46e246a05b7c7e47240b6cdf7e18b4de7", - "0x2534be362fcc238c530f2ce8f64a3366d4003a21e6f32493a082b7efa1d413d3", - "0x628e5f76af96e64ff34c52cc5d07562e72c53e7bd4b7585cbc83b7c9951d0d2f", - "0x41687c81b22f67e4ab6ba0163da6d58c81d94c5db20569b4a42fb58b7321a442", - "0x50f55f58cd9768b611fce3ff13e8da9195b1eef5d0a618fd27f7052f88c8fd84", - "0x1b64cdcbfc12c42e9dbc7a62a1f8eeb0baaab8ccc867f7a7308c88e4968eb9a9", - "0x77e9fd9a5d64b66cb901c5795b9f66424638b24e457024b5e71ffbb79ed8a863", - "0x35644de61c2108bf9d49efef164414cd2594ad4cca6bc421699458c9bace5491", - "0x172ded87912492ee521f79c1ef22e42b1d22f17c3286575a5d419fc00d928199", - "0x3c8fdc337338b107ff5879b3e95cf285b5128ae395c4b89ccfd05a3d942887d3", - "0xf4b22643ebb6d46cf292531792543f9699a99674978045b0c911818211be6017", - "0x783fdc538e4505d4187a0f341b066007caff8030c3bcc4bf49ebd31a8f6b4794", - "0x38e11f176844f680e75b65d5225533639eb522f39495b9ee426135097e5e8fab", - "0xc5e0da94d4dd6e29c8bf3684177a62051e7555ee87007fb07581c885be598edd", - "0x317dc456dd095f9eef53781c214806beb31351cf78cb1854be257b4039324b8c", - "0x5bbf954741e453e3157dada0a69bafb9ebc63c3dbfc0cf6e3fc937a1b14b7356", - "0x56a5354de5acff2b904c5b6b976c473277ac2364571c54583bd682e76bb3f43d", - "0xc37f17385f4e6015cdc8083fc750499ec8c9063544102eb0e3e3b7e0b5046946", - "0xd616c205adf0ecf00c7563fda837e94a4f48be8560ddd15d93988cfd3242b40f", - "0x1ecefd5cb0c61b120c227274dd60b42e6d25229517b20dc3e37a7c3b436f0e92", - "0xcd447982c518db12b8aeba63b68d8caf1eda6bcc44400c9d83c4e4d64b4e949a", - "0x3763ef2d96a89a302260ea66e22e3255001ba2f003770c03905d4f39d8ef6501", - "0x2fcc41e9b574af5402cdce8dc66d79488cbd5cf960209918863ac9526bcb6a70", - "0x17b71848876a6e0a8857f1e5d04762734678ed1a8addb7e378915a7c3f37c981", - "0xcac9e4641f50d52d77e0e77b1a1b5fdd25cae239864367b3d99026d3fb973610", - "0x55eabf50cf101f65e51dd3a54321c1897a67c50512c534ee9398a716a790ffba", - "0x49101d5719e4de5e4e88645de3c22cb3ab794df815555d4f22d54ac6a59f73f5", - "0x509cd8530c28cc098b0aa80d3006a8f71ac7ad56101b880288658917173b8c8d", - "0xdc0e15e6d321519f34d40c6b2cf5f6955c15af815dbe02d84606ed76a01fdbb8", - "0x0b8e107e7abcf80e85b3288786dd79f949449225315c9125730d7d42f0ab9cb6", - "0x07ba997ea28711f221ba26d724a09a52b2737b8aa8532e890490ad811ffa792c", - "0x713775500194ec8691fb540d63e99a70cd443e5539b5f8e2a993266bb58266ef", - "0x3b2ccbf35ac833e845f00329c1f8d130a3f804c55aa83c35421adb83749213bc", - "0xee82c15eb18a075b00de8fd610621683dcad88a19c1c507351b5be0de0c6c4bb", - "0xf874298782be23045971092d8305c469a309a1a33f6cfde7604e6499d2384cdc", - "0x1a9d88cd641f6468fceb32eced3710569d511848f393c2114ae33d7f36c12f3d", - "0x5b2b8c2ba5d3aa8a0503d14e759154d1f2f46d819b363025a77d4cf5e3d83586", - "0x0082bad01acd43bc2c504f66ae28056ce352257b7ba7e2c27dd36d256c079561", - "0x9c6e4e01a831348ce64da4e4bf04cad5f58749573e54f1062b0e2921bacafe74", - "0x600e1b0101161721066952d71401f8fe6e689b66b26e2e74cc924f5e914e8eec", - "0x8720d215255e4d5e2a688096506d5b25c3a79c511d8c0b3dd7ad3ccf542e9abb", - "0x48baaec9724cadc4f7cb8f10549b8daf87b2572151cdf9308b3e96f02b048f23", - "0x2adf0f56fb9bcdbae394025ae949e694e01599887e50c355c90c3ee5ff32eac2", - "0xcf78410476d781bae1567f3d763af732d2ecf56e741cffd1bd3906af83de1f2f", - "0x4e7e223f6881065ee722d6ff9603f1786e4e99292e9caaa75b7b1fe9aef00109", - "0xfdf0b390b0395f007b1b342065096e0a8da957b26ed4cdcaba432a202ec12b65", - "0xcd40d2df140abc1228b2e1f45b5d65a0f3e2ab8b7e740dfb5376b036f63c1c2c", - "0xa491347f128d31f68cd1ae536d8f982fbfa5b58d855a95219f104db741d3d2ad", - "0xaa91fef9cecb842de4df61fd1650267420c8336758fa450f87ee867ff1520905", - "0x75d1574bf7b23319a7c8ff6a0a7cae649313aca8893ccd223f1f77fd71c9b8e1", - "0xc72491916b25756e3f505081b7f63e32f3289b86cbe0181ac9d33d29f666b9e4", - "0xd446465131b34f091673736f70fa0508ee7065c4011359c302a603b6159cb52d", - "0x1380689a50696e1cf0c19869b9773528f01cdf20b8f8a6c6a7165fda31ea49af", - "0x01cac6d9deb56473681c02dc753351feda402a1a1cc2b4cc8beeb23884f40760", - "0x3f622e134dc529c5cfbf58a3ce91d57850578f45b77c4683b2dfe4530ba0826c", - "0x016c95fc875baa0a5d1ae22c8772eaf574a6c918510875f1000d65f3a779dd04", - "0x86e5f579f42ea54a96e622f4a6becdc5ae85e0d0af87997fae87b6707abc8d28", - "0x53d403f0d0f1f30d919ad7212cc5e2e73cb4870c4fb4e6d260d2573e9bc5575e", - "0x961cad0d17fbec30a8f614ff3565d12698af096e61836cbf1f0ea125ae3ed72d", - "0xc9dcfe7844bb4ea845125bc3674f326c0f178c5cba4349b9461e40bd6ec68c3d", - "0x894243ff80e90c4c4676583b4e428f13e077008d225790a234ae215dc53d33a4", - "0xe5fddd80d3cdbafb53e0cf3c095d33904ac8db83bdeef9816111d20384aed444", - "0x5c7ce294d82fe6502045664f7d13d02063ef24f0f4960e4fb62bb6abf08c63eb", - "0xb33a5699ac121a51c0074b4783545a86a428fb239b4307f1e45108c85af88617", - "0x37168f3f0220f50ba3cad0a558cd8b01a7a435b6c3c5cde98b420ba3b54a1cbb", - "0xd50928e60d00c23adfc916e7f9a5363fc8c94c8edb3aef41ac1dc719041f92be", - "0xa08b27f437ade4d527d883194f79927053ac55a3293487a65060aeaf4c4e5147", - "0x326e2d1f45438741b63d346f0da55066dfe0284382f1b4ee54b1d5552c4f7d83", - "0xbd96baee5835d9d6007b0b5957e452d71d1ee31aa6fde99796cce59b17dab703", - "0x1cbe90df49f8929ce1052049bde7d6169efe0b289ad4e05414a8e7bd61788900", - "0xa0b914df37895be6f5341f3f4013ce5c61f108203dbac4ff205d5f1a581712cc", - "0x1582e61974c0bb5f9f2622d70e772f3e9ce145be97eaf5a87ad794268cf352ab", - "0xe946dda774c96c7878a0daa686e4a22e0d9d36a88dd9c93b1776432adbfd68a1", - "0xf88a07db8c0fd2a1354c38617c16b4d2e4f8bb43d9049321ce47a8c03c8430d9", - "0x2adb489b34c480267927daf3ba3ca7567d542edb83fa82e8040ec57e25e3e6d7", - "0xf1c28c6daa3e97466d50d9780bd3335f3dd096c3389b906bcc12426ae4862a99", - "0x4457a8686a3839b7d3b592a1751c25b216bc64e9d0b8a81eec55601ab8d8a98d", - "0x0ea5d5606c5e81f92d31b49b2e54ce6c6314b13ac223c83094280d0196e003b1", - "0x33a91da49deed50914d998615077c5192be56e482ab230bdb3d480d283502e2b", - "0x5385496abf2e351c0cbd7f6fcb5bf5b5345783b4e512bdfa23b9736e77ea43f7", - "0x98f808b18e5778a1bbdefa3f19991a3d008c27e9976db0ce77ddf9e4e21a0feb", - "0x5381dab139fbb0ad6b61afa35c541aba559e2ac25c4ab1cf8a756c2f27c6bba8", - "0xf4bea5f3ab0bc9ba3ad00ad79402d1d62da125d311884f225ad8abe9fc36d56c", - "0x6018a9f7edb5ee7ada70c3e85f22ac88924d06031cff3f61104ef52bf0baa2f1", - "0x01cc4300f1cd2bb4948329d42e17a277c7da52696d46a60442b6f5600d869faf", - "0x330240c95bec1ac1a476cd202aa74e85db562750f860a9fbd76e813f16cbb639", - "0x0809c487d45161c3b85cda014603ba7efd6b67a8c7aaf314cf20c880ec623a7f", - "0x653f53065726f9a7c1b96ce7d836acca515f563a47c9d7d47aa8c5c030a9fe6f", - "0x37b8dd7b2a844e519f9eadab305efa5d112266cbbc76bcd5afc119e0dd337ae5", - "0x385b7031eafc345ed353d9b35dc53010496db172cd906f8e0b7b891b84a65e4b", - "0x38259038fbf1ab3f0ffac98e6b312457f6b2631f68ae477b9302fe740cfb8e50", - "0x5a963aaeadd708291b41bd419cf2ce0585e162e0d46635902b58ee85e317adc3", - "0x881004f4fabe3e7642c8cb5e99dc3909da28451ec442a299f331790c8065e049", - "0x3c721f828959782052312d30d8256c9351a141923a9c2e2ca5b605f3a92cb27a", - "0x3429b149f860e963ed3819ddf19164977d637489eae313331c71165c7eb8f824", - "0x44e291345b79ff4116797899da20baf28fb9d4d2a5d6b1661a0c83b8952e481c", - "0x00078c007b6c0b3b6603b0838b03ee9e8944fe16f99f0a35eda286a288594806", - "0xbcd3506338253f0df5abf0c2866cdd319fe75bad7ac6f18d1f8201164e0b9986", - "0xc047f5f06cc54e4bb54c8e3bf22245d68c18b4787a01d324deb2139d7405814a", - "0x77ec49a04762af36eb63e2bb5c5cbbf15b580020117639ed5546749e34285195", - "0xfce516ae95eaee4067e95a5752494974ebdd182fa47deab70fadc5afb5ea8648", - "0x2ee54548e56d2cecc3e6cff4e60a7664e55d9b1c1c9a61be74bfff7635078cb3", - "0xa79dce8f10567e060638cb2f09ead0b191e59d972c532a7e91b33d27151cc23e", - "0x436b4cf10f5cdbde72be02ca16602190a56c77f1686533e643c885351d4657e1", - "0x9070c78384138f5e17cc9e6148c7cb7591eb9864f042b82ad38ee263383496c6", - "0x12a8d5285c871a0b05e370a337834458ae2159b5d8cb4bc93b6cc83bf7351b68", - "0x3c07d35ea209492f4aa811c51202f081a3bfd6e31705ffec497d70bd59b7a6f2", - "0xc0a99b851e0fc4655cb3cb43547f4ae6c36c350aa71626b61ed3ad3492f04600", - "0x064404380c5969d8e43d759ef990658d7cf5bcb7b4f8512fe58a7e994e199707", - "0x3d3a61c95ffdd3c7f05b3574370bf4cf0eec605ca27cde051b5d77e062315f36", - "0x9259e0113f1e009fad7454eebb238e0c7f4b7aee8118b63b6c05aaa2f0bc39df", - "0x76ff9818e62b25fbd698ea021e200ac9314090b801e000ecddb71bf4829aec8a", - "0xdf0031d6e1c55717102ca1b3a0bd389cc0d227f0f804396d4b84b5dd26abb1ea", - "0xd0220e77632c2353d51b92b1638e909c1f41a7ab0e6801b65e344ab594f48881", - "0xb15fccf30f298101d5ca0034cb8585cf14ddd76d58f3a8ca71a60aff0d0438f8", - "0x5dcb3817ae05b8c8490e197cfdd3f0b701e61d83b8f6423b3f24f27ba0f4c668", - "0xf3bbdc6651a4ba011443f6c6542b3f45b8aa2ba8bc719e7220578d6362cc441d", - "0x1edc95bab3c88a66246393058ad1a9557b371e726adaf261a5faa535cb8fab76", - "0xe02975b5ecb9fb8abecab35298704ecc476d2e6205c2df900312359d5aebdf9d", - "0x2a5c9b033738fe9e7ba6949df526b5a735753464b746e7d1faa29491a8e5f57b", - "0x46ac373df276af8af25aa1407659a8e85ae12b51ea6ae0150ee2b80a76ea6d9a", - "0xe85513a38b54114f4b6cda2bc81a4dcedbec22749cab6676c061d95c7f38258d", - "0xacfba9967af17aa94f0bf73c3b1cfff113e14625742a7398a25a01aa29bf02e3", - "0xd589df920ebfb3c3c1660a16f74213dd6d487ee577bdaf18f6279eefb9252c57", - "0xb2b66c26bd139976ffc2471f8ec71e353108828bad5094a324d1e4762f5547e5", - "0xeac126eb94bb1ba443373fb2556753d95804891d4763a2cdd1d297f1eba0fe6b", - "0xfcb52c727793003a70941fb01bf6c184890a691b70c0aac3b11feb3987de8628", - "0x1c1b6aeaea826ed0cc776a322454663ef555adb0d3c6f50480957ac4ab7f0672", - "0x187affcea64fb6195ee27cdca1096142898be0fb8f216f10913a744f2005f7ab", - "0xb4e42e3a4c94477a6d5d7f3de429edee7d92cff93b647c71d7a20c1e07a35117", - "0xf960751197118967fcea65f10b51d9568c184a8bc968c609d093a0f6c15a71b5", - "0x0bb9ca3521abf8fe971d371749e5c2258b1fd4c681ded047d6dc7820d303fa9b", - "0x52354e8e99ecb5c2dcc1214c08901d715ba62a7463d56c76a6bd1287a74f5c38", - "0x8768dc0cb22a0ea9fde00170783fe3741501d958c186e2c636d03fdf8a995129", - "0xb4204eddcc9c75372de503645bddaffd3f71e8554c3bca09ec700d928a9664aa", - "0x97c94c63bd30754d51abf48aa830bac3724cf77e366965e9f61a100cd0136714", - "0x60dff72f08506ac9ddabe3f957f88a1ef4935f187b6c536643500d65512fd393", - "0xa94f4c353581e474a091655c78c491c38315e93e287f848c17843d807aae3527", - "0x09d90469c97087fd45cf1a2c5471c6e81e0ec1e7850dd76b0d0cfa9fd49aa13b", - "0x298d03c60b8dfca9ecac182b5c0f6818a4c3d84e55314c083f8913a8746fe335", - "0xbfa7ba8daa97bc681bc6ce413494b85ccfbc10e2bc96e148713e0325e21b3b60", - "0xe61dde9cf0c1c6fb0a37993df24dec2f221f97bdd34f607de13fb1f947e7f284", - "0x91e9a9e65d5076819146b246647b9698954bcf55e7f059db32854f93b325d35a", - "0x7bb2fb25b881772f318a10b6ce8896712c93cc85ce9c7c371337020e86817a3f", - "0xbfdda2258ccd28dfc81f83ad7cba81967120257392279c7412f0bb116605a21e", - "0x0a626f29328872380ebf6a10468307abda5a02c3a7b9e04763c0c8c83f903df7", - "0xaa8ee86e9446a125356a96f8ed47c3821da54b003558f68c823c4ac1ab966c81", - "0xe26f3cbd95e0a26683c26adfb71b6d4fc82b034171f29836cb9bf391cf172376", - "0x7cb80eb383da7d8c1120fbe79cf3bff3e5bf19b9f57aebe11f79ee23dd82e611", - "0xc9b85149830b7a2dcc1f613f40ae232bf5cce7770780bfa7c77ecd25ae0bca6e", - "0x859dc28f93d2f3b17e176aa8248d6720be498fc85017491c68d6af5f4798b437", - "0xdcc4dc79cf88601caf6e038bea275a98208d221ac8d3efdd5db3d2181c14c947", - "0xe1bd63a920d45b05efe5d2fb99437a26bb7049ea5b183e99866d6dae947ff724", - "0x4aa94bfe1501f4d37b0c55d77add7bee9bc7f24d444a3ed1c6cf27d67026bb62", - "0x10931498b823a9784c4aa3ece5e88b477050927fe4016e4b10de7e2e0eadbdb9", - "0x7f30cca2119b5a4107cddca59a88f10ad95cfaa879d97bd1e50ae6a347e71b1d", - "0xcd8a0dd7a4fa9b97641d5288375105273c9025a2c9f3d7d9d04179fe3f55143b", - "0x6c8ec850db640544b7e5b22c19253dd1595899a7663d40c4fe13de4258cd60ca", - "0x06ec895b6dd7f299edab3e2e3a65322fb7f8fa8c9a6182b7af4f0fa25acdc45f", - "0x45f6d74f05f7b366770428b49630e836c2fbcc72133ef03a1bcf9953209e693f", - "0x3d868a3921aede543f2bd8ccbe3b51468e7a87f8278e4601c4a8e740e8c339cb", - "0xc808c8fa7b02023e5c64999a141edf6e0ce0239e37656e0fa7c5156443f555c0", - "0x82c273cc2afd955663515932357f19d657eaaed2824551814706ae3843fa1739", - "0x5785f16929a6a73b4621e75d31958e2e9b00dcbf3a6b38bf3ed59672918b68b7", - "0xc8f84b888e55070682103999cfc315ea3d157d0771f0f035cf2ba03f6fe1b1f1", - "0x343a05e1a5e75e09528f194394caf6ca8caf94904d54d89c9d22abd5cae0d83e", - "0xddca7f95c2d9cfdc9efd9158d29045ba92a71d45488c40d28b257425c1c56bfa", - "0x008a2b681c4691d7e0de476180c969aa511467f27755d0d498eb22c9b5333835", - "0xe33fb40ffd6253b5701d24c59a4e217197f9eb7caf7c2223722734f4c74fca4b", - "0x650e8f1c6c4dbc76ace7d6c29b9a659d520af9b291b389bfe3812c1ccbd07c58", - "0xf88c36b8042df77734f99395eb68aa9f4b67a21891c42350e4d5e3e6d8fdf168", - "0x882c68aaa8d1f6acf005ce2ad3a6fef7ec1212ce610dfb8a7bea9417d87431fb", - "0xda73d75513895d5bd5174814da25cddac9a633507960e78ae6884b92e48ee699", - "0xf07d98594c0ecb16cfed18adb9f7b5b1055630b43444c70e6357cd18ebc09392", - "0xd70c5886426c55dda997d615d325b5cb5b652f5673601a045a113d94a17715c1", - "0x3e8dbda1f53c55fa4de65efc7d294a02d78c69a98d59049dcfcdfff6b7eaa521", - "0x67b14249f9c987298fb00e0bf560a976dcaa7fc0d02e816f84c3bf7a4e7c6901", - "0x4f8affa83492afa72e95a36f23356b46e58a0a19ecfff6960d4d6e5b9877b1d9", - "0x09cd2a17655988d2d8cd5fbe44965a6c15c3f123b75e1229621b9ab74d030e53", - "0x30693dcabd19e89bdefff4753cbc64d00c65a4af98f782e6e67b4e84f6b015a9", - "0xf67db822aaef8bdf7967c0dbe25015ff6cf88ce21e8bdd25feb764828ee64951", - "0x9a587510721ee914cf8b9c863859629ca0c8ed22b0aa023e9efb0756d3f9ee5a", - "0x5d00c0a840b96a19679736a7ba555f3e9bc5263b4b8437d6c3779191eda0a7c4", - "0x3e01cf22757510938a5aeb2ec6cf5046b7d3c186bafad7d57b81c7d2a99415db", - "0x0ff2d3ed3c5acfa5db9f9820a1a314865e266a823ab42f40bef6b0276af0a108", - "0xd3207e322207667c614e50be784f6b4d9c3f48362dbb65b17c62f5f33e631ce8", - "0x2c3330fc9f6394c0b8eaea0d156f2b9e2d2da0ddc8837e0a28d373aa779df9d8", - "0x56c8542686730adabbae6717ed8cc8f0b974037842ea5d1c80901f7c85eef3fa", - "0xc51d8a382df91e9f1fb0dd72d416a7d094aa3b8ca4f90561e82fdbb9b78c28c4", - "0x6507fec75b170f930df2e28dc75ef3dd7313834c8a8a2ec837d4ac27fdc906d6", - "0xea23428fd27ff3e5c6681b0640264ff22964c49ab0aa41f592280ae25d380c83", - "0x8e2a629fee2ba03e333bcf7fa0261dadff518dfb0e386b21cac0cdb2c7c514cb", - "0x4b00742b5349bc9892f2d36abe2c723a30c4a20ea5b899a7fee8fd759f066fbf", - "0x5b8d880357c44a79c0d04b5d347dc7c012f2f5cd7679c4d4131e5c481b45f1af", - "0x65d2859128eac961e29c063fd918c7a2b485bf72743cabb0fae8bb288f155dfc", - "0x3db9720d20d90d7d1ea448488d02015f4b1c36f556d704749723ae1c3a35aab5", - "0xd9676bad19db9f6896b2170e6cba496f5714ff9a70252fa1d4c82029435871a7", - "0xa36e49e6be40418bef884caac3b30e63fdaa1b8f622c70ede69e6d7c9c6f4539", - "0xefb40f7d197a63927f761d99596439b7b69cb7a39214258b372450fd471ebd88", - "0x7420e77f7230c7458f728f7ae5f63f0bb9182fd11ccb9a82bfcad17a8d1b4f76", - "0x7ce9551cd4cc5009d28a6048701049a5decbcf7e11904c588107da90f57149ed", - "0xafdbfc0bd5252cec1654dd24c375bf9f4af647cadad403923d6aa525fa44aa85", - "0xef0a4ef158ab6b06863969c24dc7ce5fabbd36181e9eb6d1662ce227b38f8e61", - "0x4e6d1c00feeb4218a0590fe046705fa35929767430f2c33b2176afb45f26a71a", - "0x604a1bef767235afcb7f84a20ef59d39618593def34034c67cdabec8f9436ea1", - "0x5c91553855f3a32f708dfd76c83a7d7f23aa80e5a6d2a7e9d83e82a333c9e267", - "0x48e35d198e8194ef83496392e05e3e14cc9c7ea1f324fb259c08a0f8385b422b", - "0x9df1a27224c2a20b229cc8686d95efb1570e822402c9df4db0b47f190c33dbee", - "0x020eb0cf345413cf0324f7561031eef27f188de5dc41bf38471547aec4e716ed", - "0xe9f7708c2cc8ee4785ed54a0c153730103fd92c42bb71eb4f86a93111ae11018", - "0xe7b3a27e3f5ff6fcf2925db134ee5db522a008db4d54627b729d5502f3968d08", - "0xc9c4b33c131365224675a89e4e6833181ae50661b114da50b14b0669d6ab7155", - "0x8986fba93ce8e336542d3a640d156029ea1cd99c2cc4e946de6d46e040e52bed", - "0x53b6ba779e5b91b5abe44eacd354f6f9b5b2343e66d60bcf5083ddd1a5147d21", - "0x854e090d7d5cacd3e52fed513055b51ae884965bf1146a629825269506f97371", - "0xcaae36c05b0992f80bfcc8f1c5d3a771feb88aa67f2c87b770637fa24000d84e", - "0x6992bffeb071ba81b30b8a6b19e9335f8eaa2da4361844bfd57f08488dab5975", - "0xdb63608d8eefa64871fa9fba1da1776a67d0c0495502c8eb4005eea4a0089563", - "0x57071fc1829365707d66abfb8e388478cf61a14f34af2c864b7299f6bfc2322c", - "0xe077cb6b750e158f666ea79aebd5e19e25b9d3694ac7a44efee1fe58f2bff202", - "0xc8180f9a9292284d2ba09dc40f89595ae6b554e6eb0c96aff578725076881102", - "0x89eb01b1cfe4089ded962a36ddfce84eb0c4337780446cdd88738e7241257c30", - "0xd1bfe1dbb1c9b6a06a1c18472ed66a82ca5c7ea1fdb5dcb9af1347e6ed97697b", - "0x33695a8c53e9e16e1a2255055eb069d9fc59436ce0ed698aa7323cd7e078cd96", - "0xa1c746d3df0eb28100b84c6c91da402c5c0d6a8aaee66bc05095820764cbdefb", - "0x3dd355f33c841eab61323ebfbe4a608b3dc9779a291651b885383ff70b8418dc", - "0x206c8095f502a995777d4756949d8fa7deeac36a106721d6bd1c536994fc8adb", - "0x95a9ffc34a966a157c5e797e3a1d0029bbb86999f1716cbdcca9bdcc5e77a5d5", - "0xb2c4e8a21c25d7cdaeed3a42b8670602580bc99ae5e85d4e36771e5cc2b7e0ff", - "0xc34e130d5fa9ae3df02c54a5eecf210d8420db5342af4041d77b8ade44d2c67d", - "0x5d733448fdde29b3f3749e70addbf4fcde659aeea24f90dab23997a696db5daf", - "0x8d63fe21beb60523466595253d20fe518dda259cb44fab5945943550b4e960ad", - "0x98e584f6dc76ddf7ab116d2e9244d0a0ecd99d180b1916957054f8440623f727", - "0xeb66965a23c4413fc5adbf4925ec5d133d2579c1f7764d6199eb4f7a5548aacc", - "0xc95df33729fa401a40bb0ca23731b61d5225f3ac6d159f243e1585440e16d529", - "0x2052d0e9ec0a62dcc0d0c892e7e704ed0ce03ef8a8e9c898a3691a571abc5c30", - "0x1f8567628ffc96390df8b7b32ce8ca70f6d8bf57b2ea0bd9e724c521ecd4747e", - "0xf2b48a4fef4c6c6127f7194ac76c57167cbbd65b9cff702daae1ae21dbc2896b", - "0x38dcef1bcf7600494424ae0b24956deaf664b92f3dd60b7a941ea144d789dd99", - "0x267d1afa22f77c5fb78434d822ee1c3fd24740d6a30f3ee4fdac95949ba8c044", - "0x0c01005442f62fd12a49115bfee0faf89d2da7572dca5b0e8eee445a0de8ddf7", - "0xa8d05f61408ed491620865b2cef9548d88182f37784e190b46c36bb09dd2d7e3", - "0x655eba047145af7d76cedf34e144040619d2d416367b57c7b7d9362a9375889a", - "0x5233ac6cb0ff6a892fa475b0074f4b6abd8e244f2a665e6649232e528bfa17f7", - "0xea8b2976e790ed22d708bca311a5b26c113e8c0ea4cae30607d94eb232ccb0f9", - "0x159dafbf20b2e624d22018f214cbbfbbd83de406e9bbaaf53ba46041fc3968c9", - "0x4d34b229e010a2ef72dec76c1449cce6f61d9cb17701803560af6b2072850ef9", - "0x32e7064197d2f739695788a00113452e03306a4f73f80ca62fc5e3ec223aa176", - "0xd1604bdf1cf9253b7b0ae8c92d09a2edec43221394c258d29707ba850004615d", - "0x07e0048d3913db33c6a9b45a1b232a2088341441d398b09319b455f57c18628f", - "0xf6492cd50378901ae238c91685319405d9779a1b78f22bf36129fd31fe709cbb", - "0xecaad91132667d0b1a283da22cdac651a4036224bd0efc79bb747d0c5e64b1d7", - "0x20565b022a8a8429a79a56aadbe2e32db24748238fab33eb2b0be5d83d1343bc", - "0x25e6d936a7b526d45ce1430744f5f4b41cf694d2cc50753cc88330e3e2a0bff2", - "0x7130e4b84c6efae0653dad6e16ec677bee7060a8da8ac607bde7a2b01b7511f0", - "0x93a8bf0a86793725e09b480d9098a43fc30a8420c5c2c2bd01ba8afa837c2371", - "0x2690922d4bf86c9a35e982b10f723edae97bb2a842f362036337396c7d9f9d16", - "0xd26efd262c6605d3be1ccdb03b96318613cf2d5b80350eea826758b9fd8bc28c", - "0xd3b2c89961d6cce736a9994f8707f78068a9806044cc2c96004d659f82753690", - "0x0fecacd3eb8c415a8db644b4dfc0ebf04a27f9b5e53bdac80fb927f40da4a8e5", - "0xd5600a3afd0eaedc221893214d629260cea97d7d5335e361d43183b4dc678360", - "0x340558e8951e1af701e95a963221109154f12c5870e9c9b9c4d7f9975d18ff89", - "0xb2ec154d6a59610822ee2318e3f279e249067893f806b129f4f0497b42aecff8", - "0xcf13e6343d50af1b2ff792e5a694531077e67858d76b39262ad19d6194e62df1", - "0x39db02f0b3f062645478457da8145fec8fbbc8a0981be9183365fbdc11a622a9", - "0xade17860c7f456c0b8014490f3cd15cb5f080e7112b789028f1e86ef428c06ca", - "0x1ab544445bb5d499c541c4fa02c5f850b9d18988e94ac6992525e2ad372d4936", - "0x2c3b4b33a865fce54d1ba1909924b0c6cf9a14a8cc4e3a42e1b4eeaaf86fc50d", - "0x857bb3e657f86c864407a7caf0c6b71e42427302adb85c853f330d00433ba077", - "0x79e42bfeb2cd1191783b7b2bea5e0e2693d9d15ce8d230e03dfbeac2c90451f2", - "0x5ec96430d6d1c1e9395a214e1a685e92c3780a78ca94a173d0e38fa6bca96461", - "0x544858c87e845b1f80043fb44f54a2449d31a0018025f1b41309a745415b1e7b", - "0xa26f04bcd6a580b99e9b81ed739ece117ad357facf1ecea246f891ce740029d8", - "0x857eb2767e67873c3f1ee4659c778feebb69b248950068a828d65750002cec36", - "0x7d6ad830b628a38b1753a33e5df98269d351b42d5bd60329e4c205ee5f30584e", - "0x6cbbf7d83f87bba0d4b4997bf336ac52934866cdd77e7dead51de84bfb717fa2", - "0xea77b839435e70cac6a89e891b8d1c495b39d8d6db38c352e10931e70502ec20", - "0x4d6a29a4487600b0e05ebfca7418dd6b43746ce96a41d4f17e25cd6aa1a2b733", - "0x3e8223ea6ca4ff147b59b025237a2a477495e746e7a551045374b27798ece58a", - "0xbf880aea05a3e440f31777f109995f2a1967272eebd9ff0fac241bee8747a8da", - "0x028cbf04056dc0f407496cfa901a4491c659173083ad73f84d457f016e0431f6", - "0x68548e63888fe0f203a333f5a5c950b36aa35e083a0161304a2b80aa626358b9", - "0x385546df2f083f4781a8b9d5b645516f5a77883eb7dc8d5e399f4f563e8956f6", - "0x358a0c7c14057fef517c38de9e2af33ab44f68c5c99157f9429db596e822c842", - "0x5f89b867c11d5258618595e2afd3f783e8db81dad073d8871a33d179f537cfb8", - "0xfd7328d25120390f5982919333df927ba3c63331321678906256c1ac02af9a45", - "0x406a9797947503dc57776a5c2ed6cad71ed975c05e15ef191b310088049c11d4", - "0x6e719945fb48d4db24d57a92e4d6da5cebc1a3db4a64792dad68b06f92c69c87", - "0xe2e7b80879a7ffc1e4e3f901ac99d252c1625b6824000ffd468b2d09d2cffa25", - "0x179979b54792b23d1f1940550d7c961a1373f6dda048817d087ed6d731af73eb", - "0x10b1ca5637aee0b063bf3a2fe5cc1729929b9932cecad392d7da6d567ca82d65", - "0x89edc6ecf4b7f292dc26c29515ffc3367850e5a8dcd95979244f5c57f81003d7", - "0x2cbdbde44b43c5dc078433925dd423ea51886e880b04e90b156957dd6a057d1f", - "0x6953ef4d3aa2abeac14fa44a0be4979bde0e579cc591370b08774f4e65f95d05", - "0x0735cc7c85194d5abf6ca0dec712db94b6b009c9ed74ff6781b7940064cd4626", - "0xc3f9b62bea678c0683ed688b77767b3fe3e4fc86456bbaa8c0172240321242a5", - "0x3640c7a7a1c2b62492098e0b5b9f4c482add16a86c0f88f8a60eafbf28347ccc", - "0x73820554bf239c8e1b92304f3df8290d18aaf765b45c8820f618d05eeb80fd66", - "0x8b948d61bbdf95e634d5c3827c66266e3ee42b49dc0a29100efe0f7028d100d8", - "0x8c422f252ce4e7c981b93c24db0984962019394f50ef5b62cd34e32ba7602026", - "0xd250921e7c17e016360e0b79d61d6c19ca40ca2d84c9a7c8d9da04ae4676c29f", - "0x4e71a2075113014494b5cf8504d48cefcacf14ca6be5d99f945ddafed4b58079", - "0x40d0dd35a9c91dbb1de1df4202a50df1745b2a5e80b256c3542bc0cdd5a2c524", - "0xf77e21f9edcadcf7753c1daa00720e024f56250766ac6c31a89e8afde10c9056", - "0xc70a38751d11c4240f2e94698c0a13a62d0b3cd825527c1dcc5e653186034f07", - "0x0f4f7881dcee934f71ab555428e89e26112ef6e99935761f3d4f314bb5504a20", - "0x82febe35f996dc229d6f6e89ab4c15f5659860505d64d454f625c95284a307b1", - "0xc557d10c1d19c90ccb9a504d31b0d91bedfe9a82388824fe901e8325a9996dd6", - "0xa241dbe076d02feb2b67d606b8592c970d193b84309196191b19087dd74b5eb1", - "0x4f687e1fdd09e68deac949de2738583e0dc2bf245790b5185e2f4e00f5e8da67", - "0x076b44af8dbdd16f859d9604f91e8809f929024eeaa6eb457c30db2657c03430", - "0x1bf2736aed60997b49084afc7a3d2b429d8b8836183c9ba71fd61afbcd1b5f0f", - "0x18ef59174edf670a07ed5eeae04eaaa08345d384d33cba45abf2a08af2415a5b", - "0x30f123870658c6cb69ad9314e6260a5ece2d5eeb4964601d4243aa1b56f4e021", - "0x36ac3e3193b6fa52a7134b9e4c7db6ce746619bd7f4d07201362062a3f98be0d", - "0x6a9250440bff309071b1493b2db2b4134c725ae364fc8a8add7e108f7434de4e", - "0x557876702f5bc2bbe17f13c76d0cdecfa68b0fe281f084b2f343d130405bb80e", - "0x5bb14f5bb4abe8e79909035a11109385ea2fa77c4208946841b61f1dc8a5366d", - "0xe7602f83ef13f3755b1f99c740fc46211f7ade9088a1942b8ecbd5a33482c093", - "0x30b8ee6c04d787ab137e7bd6cad1073e4e77a74db657798cc74c79676de337f3", - "0x01067e41f6b5fe5a26009378b7ea4b0515aac9449eef2730de5efc22554ed10d", - "0x73dc8e186c096c752f8dc2a69805e1b24eb5edc7553be26d36698b25829ebfb6", - "0x23eb0c950ea1467134caced1d86eb89d4addda6f8ebdbcb85d9127a5ce0427eb", - "0xbd0f1080cc7c2adc0874fc6d89207221155bf414e1cfa3f9360fdc98c820627c", - "0x0bdad5419ef94ee6aa76e4971981e54f7d6560f3faa3531f5fede35cb2e211bd", - "0x8a339a58e2ffda5cc2ef1e18a078fa09c3aec4b2a8cf9b6094ff620fb88860dd", - "0xc9a633a65b909af3133befda06a57b3058d625d816ac978f01894fdd7b3295e0", - "0x6dea631bfde98ab2b7db165e29ea75316ea50137d15a30a9a3b444d50d419e10", - "0x0a74031d338957b9564500f28f0e45aa571f48e4c85cdc52d6ac8d472fb66661", - "0x5f9e646f342e665b2fe387bdcabbb381a71e54f766afe9a5ca6c2cf999b50e4c", - "0xa8691691561a9e3236c0a26593f1c49b42f03c94ac3124b16b4aaf07fffb14f5", - "0xe806b7ac21b2e7d80f0e4583dec620b2b3399f4c1adc82a246bc77c847024310", - "0xd588437c72ddaff5ad3a154915700ade8da4023421008956b1b3d1bf164705e4", - "0x2f12257a5c3c0097b274697e648df08c31a3f8cf0833e45e45fe76091c7e22da", - "0x88eb766b8fc471e455e50a183b728e6e7fd3b5a152440bad5d1c7f49e173dafb", - "0xeee35cb04632e48f5b4c94aae1a40c372eacf5ca773c9dfc2fb3378803a6fd2c", - "0xcf9d9bbdb883ac7d171f808738dc5ad4e632d91f45737be4de2f9a2605141f81", - "0x94ace01f0f2f127e2cb47dc05a491df26fff49f68cd4f382c12e7f332032bc09", - "0x8f261b4ff772d1c675fe16f0764ae644d198231daf66a600d56526c1288144bb", - "0x95062df2fc6e23916b7ec403eb5dfdfd8f0aff49ca15e0b743e5a3294552d619", - "0x9626d392247cabfdf89c0254dee12f5d82118b5449059b78b4fdf7ed62ffd688", - "0xc7b341b1a1464207bc1996364debec53e55a3212a2e18211a994d2c8e864e234", - "0xa5d247c7e7511015cf63aee2909dc9ea7bf56b9c2e15f1603b229dc7ed11a6b3", - "0x551f57a199b684836b3b5d6e6cea2c7cd58f830e870d1689936ceb93c652ad8b", - "0x0f0a17a40f9cafa38559a7b8a1b3848514f4b5dad4c4565f8915d04163f1b51a", - "0xd4ffba1b3fbb46554137cb0114feb898c9a1a689c0fcb4dbdea49a313c593aef", - "0xe72848f46d7f041226ed0cb38549fad946e14ca379fbba4cd52a3bc3d6b94c55", - "0xe34036fa757a07055a5ba89f39f90066237152468c6c3fd8e60484000dee38d7", - "0x0984ba7640b44fefc22e2b53463b91b6686b909d038a29039a6e5f84e25e1257", - "0x2fd4072edee29b9b611c38972835d3cc985befaeea149311fad80dd0a8bab088", - "0xc64954228a4067748447b768bdb34d5a434cfdfe01acf86a11d8a278ee7d7433", - "0x7b10e8fb7d81fe90362c7d3af0c153460ed3df92c1c50b6f9a993fc81e0f29a0", - "0x0c33f7aefb88bb9f6cb9061adcb5fc535b2a3841a6ad94b8a4adce1954bc6f25", - "0x21460cc4c2bd2d828027b9c04b047c06b1eaa83f47640639463cbcf603e4b7d9", - "0x9ea560bd5c0c6a0fa9cf2e0e14ddde39cca6c24b11e02b635bfd4ba7ec826e0f", - "0x47c9dc525e0e1f71e159ac75943a32d541596c9b479639e0e8f00a171a29057e", - "0x6f32319d8d958c89295e38c41c2a6106d9f6bd86ea19ee6cd3df34931a2701a6", - "0xf90b80919f035eaa3cd56d1c54f9c69e60ab2445a4995c7969a4826a28220398", - "0xb9ba6d207d1d7866e6205d540abc36bc3198b072aa5d52378a1148f6a61210c5", - "0x2f494b0db620ec08b2961a12d79585de2f6939bbe679a8ae1a580535437bb0e0", - "0x65ecc619942b4fc71b4477dc5929244161cffe2933c84ef8e2d5eb443394e0dd", - "0x12f573e148bbef501d4325a52b737580d413c8f999cc86cd0e497d9201a006e9", - "0x709c2a2f2276d712070a10d20baeb7d1acc602e6cd0d27438950707175f68faf", - "0x472db00ea142b38a076569205123d2ae53f42c1ff86d38100742039440fef89f", - "0x536a68e966effeda6883266a1f6f00b42dbd874d83956a3312ac30e430607bbf", - "0xfda5031fc6bef618ec55de146ed9834dac41afcb0d0521b34e141cbc0617d14f", - "0xae912c6ae86e69a59c61a3fb95c9a60b37c17c3ce2b6e9ae4930dea5fb5454a9", - "0x426c6889a4d6f3896c5209b662d9caa421adfa1f1770f33262807a3c89983364", - "0x6e72f8cb2b6cd35fb0c090f8be71b1ba790cf2163d0cc18dc0bf62fcb0658fdc", - "0x7259d06d38028ec544eeab2e264f2d4ff975f245c3697ea8e141ebd59733a2db", - "0x37729ab26688e0399334fa3cd2748818425b3fc41304fca56881658dee0b90c6", - "0xcbb02add11c0edd8105b440cd44a041b6f55a94712ed12ffeffd1817ae8a8044", - "0x156e069b3b63fb302693018293bcc0ab60b2ee2cc8aab43e1cca3774e0b743b4", - "0xb3a7a06a4f2c1034ef5a2fa5fde7554f239b0193093c74db11ebb5d9b1a18f03", - "0x323b6d191477aac18e4761baf8ff9ce70b1274a995aad02fd582f87285f8a0fe", - "0x7e4d202c5dc858e3a3c45a5f43b0804a4ff5ed53bce625ced7eb65db74e908fb", - "0x75446e5cc0142cf4b0f26f560995f257f87023312697b3574fe0e1f558bcda05", - "0xf53cfce4dcf415ea49360d84188b33a1b6e3c686c6e59f1a7385f68f3e160a7e", - "0x43ee5e27e64e778f31d641050b5c975fdd9788f2d5821bc8e3c188a91a86ba9d", - "0x6a3a73772d1ed7ebeb82eff2419e05583049939493e4ff396fbbce7e2012ef05", - "0xf976ee31f6b27e1e0f359e1f96b5900f34c49c89e81bc753456599d2e5f4a097", - "0x00ec8886d567bd489582441964bda82f3e297801ab0c5d3e58a3df29f9004a4d", - "0x9bd7ecb0d91d576640cdd3022ff0bfa64838f057e55cf35ca945d47f98d8d107", - "0x1868b1c9dcce55407f81879269ef1296c5eeff32ef4f7770bcddca4bf972df14", - "0x24856220dafd06f20fd0914d4468713654f6d9a8a8472a3cba537c6db45c1981", - "0xa05130241c7260630ce231a427081969d4428d33dcaeec08287e7aa80541384f", - "0x4812c23621b2399a68e81d9dd6fe59b0f0953b279104a69047d0df3678c8b628", - "0x4c00bb65a14a1b995a38afe285ff4594c51c8b10c137e73376da3f2660f00044", - "0x3cb2cc9dc9b1c997b8e73ee151de0cc23f5bc0d7dd27d0402b6d79e1b50ef441", - "0x25c0bda0d092dff16a5cb70ac082a0e3c17a42d74191c9c90d46deaf34c85ad5", - "0x8a75c8a0ff4aa70a0f1389f5ddeb0a1b7aca04b94af03e7c2f3986a6a9e8f8b4", - "0x9d9c63e3b449623e45de0542b1ee4d102204137a2ac36a25442f3797668058df", - "0x932b4ab5c339c9654cc1c5e343aef426dbfee3a98b7835efdba1bb583238aaec", - "0x8001e5592beaa70277723223f033b9f830a23d4dbc64fc42efbb80bfbd7e2a22", - "0x1b84fbbecdf3add7f514e0cda2b1c88b21a65d82da4264d06b332ab216179230", - "0x79cb6fe9cd675e096bc92d449093e78e59c6c4c813b550c25a76c2cdf125e582", - "0x4fad1261fa7d06941dc436c1d38188e3660b300dff7f2001a7efa7b8678bdaea", - "0xbf81d3c1b1e781a706c6acdaa23c12f5e900fc3f191d6edcd1636beb3d29a6b7", - "0x36cc017579e8daaefb5592bb0c5b2d2fb7df5afba9fbf02d0c750287c2353b78", - "0x0cf0b82b4a102f35a06f590481b72f93d570e929bc43fc188e829e01fdf0bf9c", - "0xc0b67d7c6100b042bd39d0b4747e854a0672fa2769dff0dfaea01e05621a368b", - "0xb8c49cfa2147f651bbd73f97f02b694a5cc3cf9fd79d47bf34857804206296f9", - "0xd3836371f2d0071e45176aa82ecb05b5185e06e79043f69fff184121380e1093", - "0xaf9e77ff0a9c1bdb16afd7c1d725263ad04394e4b73f013829e63da9600bb35b", - "0x43082e5b9b11362b6b5cef0c5614848ff4c8cc9dbbc7e2b179330b46e7b3652e", - "0x6cb6fd2e7bd5550dbe63b9875814764e64036a921be1e5413ab1355d771cffd1", - "0x1631a928685b0fe688899b105aefa54fe61e564a4c9d8dec99a34f5140e6655f", - "0x095addeb9c0d14bc7e18f547259a68ea3dc982e5256f4a97e3f627c3b2140be2", - "0x520f53d82a67e09c157f3480b4f504dde53d947b9e2238f814f5832f84b8eedf", - "0x433777fbf6cded05adad6e5877ce0ea3e34342af6f65042c6141b4f4201103ba", - "0x077878ad6fd501074b5c713905aa3caeda3237a1b58087e6275328d9482b0577", - "0x6037000d2014c601b14bab5bbf758098b300e68fd3d6f2a0d5e6ced1657cd6d0", - "0x481b8e11fe6ca5cdc881bb6c3d7a0d96e8cd2aca705f950ac542c089d7ca0cc8", - "0x2b070c54b2af6cea0e0ee76d37a92ab3f4e9e04f3b581f41ef2bd5d2631b8b87", - "0x01ead0aee81e4611b5ff7cd64037ec0039a05bf0d02b18b92da6acbf45d4e6b1", - "0xfdcd33327deab31927db501945c15a82f278f34a09112b2d7f74219e9a364555", - "0x0fbf377a65289b1decfc2fef4614b1e5b3404e0a0c9c7d0d147cd86bed55f23d", - "0x8cef4b3d09f838d4acb5e21f777fbd906358a2759d24b055756e9d154b177ab5", - "0xcced9e5d45e86423eddcdcae5fb2080622279ff6f08a0ee47012b33af58f820f", - "0xb1e6b5dceef79d8ebd0b84a8990f724fd645be2434a0a8339e78fe61c2ad3186", - "0x24a11c3547f5fd5e5eabda12369f90b8f5c8ddb82568631b6e704c2ab5c94ec4", - "0xd6cc197005e41f553308ebed885a4df650a2022d0d40d25f37bfc74a94e5b04f", - "0xcac28ad9ef5e3f9b0d2184fb3427dad0838122a44538bb1e0ad15baf08ed7312", - "0x8070860a9337d8e6e016e0ba5953968bf410100bcfe5e567d7562e74b95b5d0d", - "0x8ab5cf8538537e591e95fb1dcb9522067ab5ac783bfd622e6c9bb131d3288ea0", - "0x345a12de03af922ae9e6651cdd4c4249f75bc2aef1f1651692dee6d27a490813", - "0xe9cbfb97fea7afed66cb3551d9b5f6ba697a4476a5dcb3ecf25bf9a41e54bd85", - "0x4028a3855c3b656c1de109e250412d9596cb6fc4aed22b8bfca6d2a60b1454c3", - "0x85591c0037f549f6d807c91b929fb0290c1b53f23e9305bbf4feed86d5483cf7", - "0xe3178cd8d8b7d9c71d9614744a937add2ae6c21f6eddf508fdec243c6408b647", - "0xcf40b5e2f93922022aaba8558b17b193c50f6440359826dc78b58e9b9e34074f", - "0xaa30846598de0f132d8c18084859e6c67189f9489a2b8019936a151e7d0812d5", - "0xb908ee8113c1e60abe60fcd0d7fedfef07ff0efa562673d0ab86f6399812a8cb", - "0x8f9d9102f709fc1b4308b677e515173a1db857b4989b1de2961b801aa8b39db2", - "0xd74e36d71447b437a4a4405dda388a9779347fe41152b7c5515e02edb567b5ae", - "0x5aca44d35187500d73b544e71ea8a12100cf011c219fd8b83e44b4792204edd8", - "0x7196cd91ee48f598c11527924c83431dec39ed7cd67a225b64a9d04ad23211b0", - "0x52584327a0b33be95ebc7040c0dd9b60d2bd0d42d1247b8d2ee06b0d6aae9f58", - "0xe2517f4a4218e9741b501cd6fdbc7e2080cc8827bea231d829d1717d24ce4e3c", - "0xc94771be6c7b5e858823a03e620670cc604cdba8092da13e429c76c2077eb2af", - "0x3f7f556817b418fccb808f7ede824ff17b119e7fb02ec1a4239a71835af5ac5b", - "0xd611cd2fcdea70eafce698fe405f361ca95b67d5631a9930d858f4a7cca46358", - "0x5f2aa6ad1fed0ce4db64bff6de7bfa68e060d357ddf20414fe5f10b4211bcf7c", - "0xe8b5d5fae7903602ab3699ff27f159367e2fbd8fbdb4f45e7b1628b563081940", - "0x31cef735663de763db98bbb0846d5a1127adf45ce41837aa77dd5e4008718711", - "0x58f0b43df5b6c2527dc3e22f2f76cdf7817e23d7c3b9a9732fb91e6da9a537bc", - "0xd13734d21521985e4c6c14bcdc34139efccf1f0a9ab92a72e0b9e639321dce70", - "0x99b982d744b14279defa1771bb358ab55f4f3730a8feed9e14023b39c44f2777", - "0x827078f5ddff2088e6d16039c439492140337ea661cb361fe87e0bb7fc785bc2", - "0xb1f7f5fcb807c343ebc587314a28ac008f849c24753383c3e40c9a12826c9f5c", - "0xd60f786c664eadca5ba1af303f8d8e88d781ec3611b9f781cffd5b17610c19b0", - "0xc514f1487e061262e2be76dd593300b8402df6812700c41a2611ff5aeeddfe45", - "0xac6350441c04277a76c019215cbec0fdab8f6e46087704ef0b75cad0356dfc32", - "0x724a5ff7c13fe6d21249b47f47082797b2470159b298ea281f9616d6b5c5269e", - "0x70dba710ff65ecb7c9fef28b366f5b175e12a63bd1f7530ec18da36027033497", - "0x014f4aad56ae3ececae45610e6d28475e588fd39897127d20675c1ce18479939", - "0xdeffd38ba5e4e685603ea1da27571d48830018be17755ef7c08db7f6dae63647", - "0x1e6e324967609029e28e768443d11cd5d99ca713a88c9ff47144d0b16b9cefc1", - "0xb2a2a5d5583ba070ab2b8adc6f5eca48b621b5244433a5e491784ccdac2e64c8", - "0xdbb937edd70b36b566e397fa369a5a3a0b45e606ed012277c64234eff00f2157", - "0xf043662cdf2a84618401c619fd8aea26a54519add9f72e43a520fc8129c02000", - "0x9b84dae2d1a0d7e23073c558e13e16ddf3a28daab3569c89271e0f780f134be2", - "0x93c7258e9e78238eec47497c842a643b339ef9ce4f236ac2c0541872a427a1be", - "0x8a8fea29216df1683fedc14a7a066226f9299d1be50021d3d2aa4c0585b29f47", - "0x85bde8bc039cd1820bc1b1262545bbefc07b03de47090008becd1417ad3b997c", - "0xa7758356e4555d213a19f5c343de9cbb986509aade6d8237baf1fc6e07084b9c", - "0x546bb118d2a1176ffc967f90bc342edbab4350f28294ad6aa29eec2b0c9159ef", - "0xcb50bf5a5a25b95962b6caf51b5e23fe60f6449f69b4df5d6b6d7fda2463d5bc", - "0x1730541314d8595b13f01281189321299fc3d2e9d0b9aa354099d6d83538570a", - "0x5b57218719bb9816f4341ee1938263fe92cb95e798193cc2b24fdc3836abac02", - "0xe0860d86c780d3bd7375ef6a0d75eaced9fbd90283f8ca8335205ac3d3a3dbfc", - "0xcd65eab452ceadb388b945eab3342dee7542c76bc912dd738e86bfeea1588f96", - "0xc15efc0c25c4c019937ad27026225024ee6992780b9ba02d3c70101a73b354a1", - "0x30e8665a5ac3f3eb1b9c2e67d61dc2dce761e4123ed801c1a6f207aa3828804f", - "0x6c3b5d4f836b7d349a089280c1f4488de60461c7c1a7a5fb3ddcc69c1366195a", - "0xdca4d348f2b9806cef16e320e0b21a539769746679c1a45a6145f2298db496df", - "0xe8fcbceae567fde12d871fe54233ece0d74ba8508fbfbafac0f154806a9462aa", - "0xd79328e9ca262b6a91652efb67dac1fa5e8c3282dbcda58d4b93b8874d226966", - "0xe0fd3af4b67510c68e8968a5e25e741e1ef71c9347e18b8de6139f918a62f6c4", - "0x70731ec895bdb4914a76b97ddda78a8805a39a56987aefe32556b93e2c085d97", - "0xb387f307e22829ce20c5d7906037a5bc202950197bdd75ba59999c2a09778864", - "0xde1ec63948e3c4aaa005630545cf73bf9d20722a1c35b8176cf44d80f4cb7f0f", - "0xf9156bb506ad9a12b3b2357450c443572a3e3399e97f68cc0166ea22155c9277", - "0x3d5c1ac62e043661edf67446ac9f96b1c887d07ebcffc2faeb317b5c3ab596e8", - "0x70e9f96a6d21fa71ec310b99db848e5dde82ac9410ce8c6d24af115421527223", - "0x366fa3d63bfdfd2fc10ce44522f36ca6b8f815629bd36a26c0a5fcd6f95fc5f1", - "0x34b6b13187d684972edbc097949de7ed4f7ad2658f9889e1798e955845f0de36", - "0x1ebf9da97a23393f6dc5cda5c54718f273eda8ac6d1977981a0e1570863d9833", - "0xcf8888cc905df1ee1127f59c8ea7f9243c4688daba7890908949b027f4af92e1", - "0xea4ba26d5fdff1daaa42625fd88aabf7f91cd400829268b9beee5fc09875c630", - "0x639faea36eb1a652b024df696454c856be53c3667e88c7af5299c050c1092bf2", - "0x1e67283656def3ee933332600071fde44127ea3caa9c42f992ed16e33d1122c3", - "0xa4f53ce6159cae14ebc60a69e9c446a2abdb4f4f5cded75b1c5814a84c487528", - "0xf2840725ecbeb6e7396551fe034d21e75a5d34b4cbf8b3b18dc5ffa06a3e2d85", - "0xed7396b51a1b0d93b2288a4bf60ad49d236bc14b0cfeb40dbbdb6e9682d3fcff", - "0x9de490ca8067c84f922547ab496e57b3e1fc4685b744de22f1cac7dff687b930", - "0x3c3f7c57fa9ef833accca168540766407bbdd2c418f13adac42838f205199462", - "0x14257d1a35fa99b3cdab74c8445bed9bbfd25d1604e1b9123fb5c5bf88cb3a29", - "0xac74953d33cdefa9037a415336672144c53310ff50f75d865272ead5e0460799", - "0xa94b857f4dab303c1d4a5213c7bb8b91a441ee8279e69fa92057718f3aa40a4a", - "0xe7c67bb2e440f1062f95e28f0924d0b29bd789103521cc13d13a95673cc49179", - "0xe90ea613252541d3c5237a27d4d23780b579cfda48c057b7304ac14751166031", - "0xd85a4fd06114870b3fe19cba9924405de1e14e026372e84b24b4ab39e7879545", - "0x7d65651c6e789a3b6113f589f696ac6595f279bddba74a17e955ae6ff93a1846", - "0x5e44df731c9b0905ccc33ed49669b78834d065fe3fedcbd0fbb7df95f706c51c", - "0xc90d3dc4bff702b9f17930d27b039bfcb1b7879076bd82a5650177252a526dd0", - "0xb5d0f4ea7136a5f10e47f6a802b46ba0c7dbcd4ee10b4be65ccd55fd4c940bdf", - "0x4f7647e2529b477aded041ed2f4c8e01d5219839950aca43723993c2432105de", - "0xddf89de514a09c21918af718834060348ad0bfd3b86e0953c247834634eb841b", - "0x325b15b05b3c863822647b25ddb970cf9cb52ecd32cf58e6c5e8cecb1beb9c89", - "0x3cae2ef569b1e74770ed80bc31da2addeeadd59d8605eac8edf3dad0fa9b0c37", - "0xd7409a715ae5f091fad14187121bad8263caa2f60ea0d0bd7c524065defb564b", - "0x5cefbb199b507e3b64008e5d639cac8f623e9ace1292fca190bed4b7aa214899", - "0x2c2919f07eecf553b473f98d06171449838bb03c0a468d8a7cfd8062a574df1a", - "0x5c122d8416b78e0e7328881efaa7fcf61be6851a5b7572ec5c341693fcb9c734", - "0xbb81898e64769f0a6f1c20a2472a5128e969f737d1cba5be885372453ec18d1d", - "0xbe203e6f7c0c86ba994fde55d0daae0f066814938debc3b5a44884dda70e3ac3", - "0x73c7842f2480e3d742aed1a1b7682ca1f322d4d2555cb7fa402313024c2a13f9", - "0x5b95cde26a2c2be298a03252ce36514dfd3c9e84f5c5ffb75a050bb5752b6247", - "0x9c7cfa9294869c1c42db3cab3bfb737696b8f81500742bfeeaf29145e2b5c79c", - "0x6cffe595d85a32ad1656c81c87d476e6e7f602609fac052a0a3e3a951d11eafc", - "0xe2ab30bf8127106a4507034cf6b3589ea3dd19bb10c765434fd3f3f7d75c1a4f", - "0x01d7bfcc2d4c6fccf5e0599f9985af838cdc0281c9c8cdd7006b3b0abc775cbe", - "0x004114dfe63995fd66e6932c62cd7b3f40108e5e2f6916d19dc436cf016392d5", - "0x068c036ea74e85ae8d8897589a8dc775c8516e38bd619ed584ffd51021393fbf", - "0x682f4444c85c46d28f5858a91874e0c903e97def4e2a4302ca90ab69b54b4f49", - "0x3fda79ec159c2878e9ce65c575157daaa9561f17a14a0003d95c0c4264985707", - "0xb41874256b5cd3ef80af1536a45a8016cbade06b4a06b1dc0942c27124d934b2", - "0x0eac150f30020a7651a006324da1ae240d4fe623f077c061a954122b0e17b0a9", - "0x0b174907572651a2453a14e0326e7ba836781dbd939c2d75db84d9b481391824", - "0x082beb1974f7201be32469b9df42d9c30a520f4dfa696fcd991ef20367cb6eb3", - "0xf3c3ebe6040e1ce5c5ad1f6e08965ef387adb6633581ce3fdd1858f1da4547c9", - "0x6ba341211e75ea7346853a3f63ad5502f83aeefeac98a479aae546a70141af99", - "0x3962cc5bb73ab7f48d98bf65f3f7f41a8f3c52b4d7d2f12a0a89cd876f875bcf", - "0x5b49809641f72f5c96bba6b27e0331ce7fc7dd3ef18a4463c93d1f685b7a29b9", - "0xd2b2e8eec102c4b68d94d8bacf71755386bc3008d15d6090772a4739eb763300", - "0x704bf1701cee4f631f4de0e330873b1621490faad46203fab288cbd0892da567", - "0x2f7d29943c2b3ed9f59a66ad175d298f1e4761218e3fc52b9a67b4ca7107e93e", - "0x2b370002b007d4df272104b77c1224c24eae67de3e12aa599968b190e8d58c19", - "0x1f25435148cd815f4549de1f21ec227d42f360d207aa2f7de5466c4d28afdd0f", - "0x86d745be32470f409b5d77f92f2412aee084fc523a7ff7e57b53f39b11cba2b6", - "0x86d57512687518e3db56834f819bed57ca4f2e3845ef6affb15f29d1e166c333", - "0x9ec0e54aa6361293f68c14b7ee9dd32abc52d9649522b0d62637ac25d3a978f5", - "0xc2dcddcdd616160a196f1336e9f11c7685c5eaa4d3f5ca7c35d7eb14199d21c4", - "0x03a14db3cd22bffc91ce24e510421d60431db71bd68b8b040c89f85f7664967f", - "0x3cfa17c8017ee8692c5f440e6ff825b2bbfbf18d01466234bf69ec85c3c793e4", - "0xeb12362e925a0b082268e0e006165720dfa72ec2628dcddb6cbee4a4a0ab7bcd", - "0xb8c68067329a16ccc14fa3a5bb373d8281c79a20e1a15f74dab053efb881d13b", - "0x282be7cff399ee51b3b8ba7d9ce73daf0427cb11151b9a81ee0f6efd837c6f6a", - "0x357989e62c038a11c75120f990adee58fcba6e7fd49cde265296b2ee0c7f6db4", - "0x3ca8e01da313e17c3a089b8b99ee118d10140a497aca58d62c25452f1361f0c0", - "0x9778186733b0e642156d6af17ffd8161608a2ad791cef0dc5d6f31e5ed4ccee1", - "0xc309f7651776d4510a9696c89aa82e8720ebb2c41c208e7ba8ccdf1d26dcfea6", - "0xbe30023b89befe4495b15daf4578f415e8cf25087428bb25b1af06b43b04b114", - "0xe7246483ab7e0afa2a6ebe69819744cbdbc588a5294c1c59cf04232782d89c1f", - "0x54e18aa712987567ad36173ccaf070b127277ad6e9db2bfd1831b7868f56c660", - "0xe5c6a4c0f07bcf3dab39def282e3af9f7dce84a7b2bc71c9bc3bcaf35bc6bdea", - "0x5d78d891bdbb232e7d90370b5108ee03ceb0c5abbd5bc1c49c53431a94fa2309", - "0x91f3efceb05e5c6114ce0fa2477d8e2a6d9979ab1d3cef4d964d7d804399ac50", - "0x29830ce8ede37d875e7203bfca97b104b002aa474fa9aa9fccc11b2baf665f83", - "0x93a0646400cf92d5e7490638b12408ee9fd7696f15005287d23238ee5fdba9bd", - "0x47273e8e24cd886a25e278d327bc0b07e4c3a6b27634fc17705f46a019ec042e", - "0x3469aeb8bb2312fba8a530a002d75b75bce0a4b0e7f48d3e92d595dbec818594", - "0xfa8abfdb2535d9f04933e924e967d97c248b62900ee645fc991a47d5ecde87d0", - "0xe9fc29b24fa7b2a6cff23170d96faa84aba582f68792dba4962e7fb53922c568", - "0x506d80c18b5cd845e0277be32d0553130549a976f781c315c674545b1087516e", - "0x676bec126c59b4f69ddb2ac8141d9e90b78a2aa1b5e55e6458cd479fc7f98a10", - "0x4a99d2f7be333ced3b6faa2aaf15f792e00717da5cf15ba2e9b5b7dc02bb1bc9", - "0x776f5be74f05d1200273ea3c9b1919637fc911c76d1a9c3e1e3accbb9ffe6e37", - "0x09b9f219a053c0c3c56581e32ac15fc6bfb4fe69208a5291dd4860cfee263d19", - "0x65c9e06136563c4648b76cb1e7c5d46bb7501773825a10c610f2b63bfd5ddbdc", - "0x2e31a4c4d6670c2155b3ba877cbc6f086c18059b2903ebfcf2cbeb6f73e67bd9", - "0xdeeccdd2dbb206be5fe2bd4e122d6cfa556d00ca0021384138dd914fa3aa2413", - "0x0c24527744343d79639a382412ad22e5ef2e610151e1a19f09f725dfce287ed4", - "0x4fdcebad349c83d27457ec443a5103b375f26b6da958227ff00916cb900643b0", - "0x4b06063a48575a6f89ae7fb8deeb316c30e6ecd080898c47f24d9e7d4f6db960", - "0x463b0fa1bb74f1473673a2760a7e447def169426f9f7cb57ae4c6d417f58d829", - "0x01050ce1aeb140cc812f24c6629f9a171ddf4891b9150b43312fb052ecb29de1", - "0xa9e4bffd5ca3834b5a80dd84bad6ba4dd71138f02cd386668aa8b73f437f3e0d", - "0x379e501e6acdd0f94195bc851c50e7674e103ce8563bc61d7b0e6ccdadc18def", - "0x8d93115aaaa77767f70287e025a445cf6a4c7f455a67615f38c42e827c95912b", - "0xde2d4d8337849ad10a32b1731a0c281eedad191a09b26764568e7cd9769200df", - "0x575de523f7dde0e52b68c15646d22a31e245a037d1218edf86e295fba9b201bc", - "0xa545dd5a5e5dca568a5a28bd387417bf3743e184189106670b03b423b9e5bb08", - "0x044d13ecdf9cd6989d3e5a1d8354d4f5a7d29439a5da0c57505496fee7b6d054", - "0x4d99efa30d95ba2f0002565ccdc0ade2bc3f21d3153c638fe8ad9977f1da8360", - "0xc72982c95de4754f8b0ac62113a71af4c760c3c63f18d230f1fdbbe3c0b379c9", - "0x7a2dc8e509b0901646afbc1a6f9b5b27ff69f2fdaf4377ba027be555acd128ea", - "0xa3656ace4d66ddbf55477ce61954f2e1f165f570b5c7d028d1653e9f879cd080", - "0x47e4e4bbbc704f65e3979494487b0dd51cc56a928f26f97d6e29d76db80ac14c", - "0x5cec964b6d806da75f96c67c1db66d7c539593c476c69a2207be25934443c82f", - "0x685e812268ffe51415ef5e540bd4d2d65c2f34977ffbd54be14c895e4f004abe", - "0x991b2344ae224901f5ef89be9ed8313ad2c217e3727ed8d24f3a35fb71df3982", - "0x1293f5ce59c36d3189ac40e05bd8c0fb4b69008d8d457b224df1ef1e0285b553", - "0x3d41f27e644e2f7de5173dfe7004e32d6dbc8cb56871f578c3b2b943210a4c47", - "0x2decc2a4e91de0ae9a5584cfb03f6a7a4e0a867e397dc74df8f185bf241cbd96", - "0x0aab7f05bcd3b3633184b45c81dffd70b3f1b1b23f28d46747ecaf54fde443b4", - "0x8bbf3aa92fb9ebba36c5a2756dd394adf505cb753f8d3809393e1d967f78e075", - "0x13544a2979e57f73bebdc7cd1b2a1c9cf8911b8f24868aa1e775c8c53d0e2572", - "0xa7497c9f04590c706fe0a3909bd4af5bccc7ac31d7162b2b856d3a36bccfcef8", - "0xb80de8392864c859aa8e9ca078c258a6992bd848de129350ba29548db6aa4afb", - "0xf334d02277f55288c189e6ce79942ced25ffb9ea7bd5aa5ae562a985eca6e57c", - "0x83a563681180ecc7a1fbc6e47e0ba03f5004a87167fdd451d559a573df30533e", - "0xa648f1462985ba8ff73c7aa4d19153843e6a9a454d976533b08f7a14d2fa8902", - "0xede4ba72831451bfc8651419aedc62e221bd8acfd8d4ebe426d5b84d862d3b71", - "0x2ab8f2dc9b620b637aa27f9303068abfba56bb3438905d29c9a6faba4ad014c2", - "0x225e5b7232c6edb127a71ea313501e49dd86f4df0a037b9c97dd5fffa8c08cd7", - "0x85606456efed98380428d075ac4791140c9595b83707a397b274805c8fdac4ba", - "0xe298a5939a283d03311d1a19f83805f63c83efd2d7affa7586be8868284be900", - "0xfedc0db97df763328cd9a8c8bf1de73c420ca33dc3391acd6e4e847eda793d7f", - "0xbefdc3c7e7ad41deb07ec2821d0b84893440f797fb1603863e274368008b8e74", - "0xba3b21ae193db8215fa341504fc5ca46b58df994c473340bbee3f3fe90d72497", - "0xc45fc6625b0f784ae2039747aa93495b05243c00dc5c2bf46dc372c62beccf4d", - "0x32d182e51018fd1e19b576670ec68d59810c4bbfd406e2bd162e1f17a0555f67", - "0xa8886b1a9d7bba27219fe7f563ac0b592b82a1411392ea71a4a5bb288a98d6a8", - "0x396bac50aadd2dfc853c144091324a12ab8661590fbc738d3cab77910aa2d2a2", - "0x50e35af52bf6eac228c5fbf6fe5dec85989b76f44811099f2e8f4950e4ab86e2", - "0x9ba2aebd73945b6dcb9f5171155c0fff29db8cf40baf83b1689a7ad1004912f4", - "0xfb1cb1ff78859e3d5f0db0656821e12b8d247ab3896e6670426dce2055f1ff8b", - "0x230a6f6567f066e76413b72e7650f7cba9315090abc1bc7f93b861d85aaf0c68", - "0x4dbcac91377ca858927a01f5a5e8659b4e718ceca20d4962c43fc1688c4d7574", - "0xb13061dfac0e8f22dba518fd46ea95024cb97ef825c970cd5e452ccb3d7fc6df", - "0xc5af27a85235fd0e1da29b5afe6955d72fc2245c5709a0f29bebcc7c49eb4a0f", - "0x7937707d20bed2c7b68dcae39ebcd93b84bca9f471250e20f86f8ba58bb10fcd", - "0xe1398b71406488244a0c205db9c793be090349bafd87fb147c1aafb9d7d7deaf", - "0xb383706e1aaf7149b1ac3d258f6628e8e6d40be0dc096647b4bb82a08585b707", - "0x2c07639c073fd57cd719e5c67371e5c8db30f34855ffe0d985070b4b36e27cdd", - "0x7b264ea13f78fdba57d1097be39b6b2dbc614ae36cfea1f99d44b9966b3db035", - "0x04d942f60d32a80d93d12fb7c8ef95439e0da5c8358df2c01e7547596461ad79", - "0x71c93b0ec1e403906ce7e5a38d168b72b14cbb7cd0e832c2a7e73f485c7d6567", - "0xf7060c7f697a68824bbc178e02dcd6b12ca204da9bbf2a17b69ca858e2ea9574", - "0xb3b616f183145bbc77d8e3610504189e00c0aac3e15975e4e01f1b2b25c5b5b8", - "0x137f77c2fa4e911b6915c5a84588bfe78bf54ce39d09ab07e495da9283966d19", - "0x5949274e5465952855c28d70a4089644200927b54faa67d5aebbc4eb2b891aa8", - "0x1c695bcec5602b15de8099c550a3b738aba98acc46377cff7a5e58bd7f402ce1", - "0x8d1ffe39767f285d7f164018cf2fa4ec6509194c086c33af7ec8132e3572fc9a", - "0x31331d50f79ace5444f242b279be905e076d675fe4d4e1d32de728e2654a08a3", - "0xce9c3e8fb9a75e284c81925c6ef742c4f34e6f0bef11578a61be5325692a3a60", - "0xa4cac76b0729c42927d1ffb0c870983830ea66636e9abc6138b943301c59b96e", - "0xbcc505e608198cc3546899012cb59a3da60839c9060d2952f8ecc178be4bfd82", - "0x1b5ab87cc34a38f4be26c5e3e05903538e8e3af350f6e2e1258b0cf344ad9f39", - "0x85849c1cadc5d3bb555b88b1e5720110a1d4f7b79f634427a1f8d7e88c2e043c", - "0xee63ee939a6dbc6678415a1af9969526083bbb754a8c55e97cdefbd2f02900dd", - "0x50c63ebc1248164b057854b8ab8bdee0510a97f75e864e1d83feaf59541a64bd", - "0x58daea24885faf1556b3111c06351fa506b40b9f1ed365633a2246165abf3819", - "0x0357ff6bf4ef1b1bab206c3c3efe770c6493cd85d0540fa29492428d0a878c6d", - "0x52963cc6bf51a64c1f8ccc21c520ed95d5ecd0e43a86b85defd7b0113082e9d5", - "0x5cb8353b62e27e59824ee43a4bbdd384a2af41b52f9ea5444a1a180482ae7856", - "0x2815152239935641536e549578cd0d33c74a377ba350a36ef04b038e5fc49142", - "0xe6df1f1b9949e17e0a683b6fb7ec9ede2b49ca3d479202226ab4180445f86d51", - "0x3af2498e3ae79447c4b6b3f025a37c790622e2979462dee5913fda778882e5dd", - "0xd498a21e50c930d1f0fe0afa65c6f977ab8da037175f34f4478e52f4c1631d01", - "0x12478043148cd0413f56918565cc2fb0fb9802dab776f0a055093ee8ac131da3", - "0x9acd35608a37ac8e309e884b88609957ed24ffcb5816477788e08a874e6c5a61", - "0x53ecc517c7158fb6e99863c54795107258b61399279ecf74f443d49533a104b5", - "0xc74f9359d1b15de475fdeb56497a5745a93c741e5f2c370656b2e7c269c06511", - "0x8afdda4ce115a19309597eee883cfd257182db46070c7cb1f24955d9c872c8f7", - "0xc4036bef9a9e692ebe72c946161b3ce5c588f4421b7d6555b5d65f3311a5b0fb", - "0x4b64a608ae22b5a655cba7ef78624eb69f9f8e2b2f7aad8c04cc6ee95386e9ec", - "0x4b5506aa505815e519b5008fa8d23488b642a1f347f6cdbf53975636a677a6df", - "0xc251b21563070f1b3895bb4512bdac4395440217c5ce01fd85e89399dad72b53", - "0x07a7bc11d48537654d24eddee78d35e4510f22b755f1f36c3b81e98087a1ce68", - "0x66a0e9bf542363d8660cf7817ccfa3f4262e4817fba45d2871d0154b6fa7e969", - "0xb2493d6e033e1c29f62a409342beb82f692e565638736fd088d80acf7666b9ca", - "0x4c55f2a4ceb60544682cded4ae8b23bf217ce7d87ae90abeba156d9b1005f397", - "0x32b3cd5438edac8902527ac353356e99effb1dde5209fbe1015673778f7c5685", - "0x4066c6ad2c7170b434bbf3e7ca0fd678803351c3dcf1c56e84bd6c14ac2f7712", - "0xe74fa1fa353cfe643ad94f6a609726fd3c4b06f9dd7503431f84b13bfd87b06a", - "0xc46c3e5bc3c3cbc604513909999f1523d704578cf5f025a33f1a5273ff6cb81b", - "0x261b489751092cbd70d6d3010b5c5dd68c9041063c3f998b742e81060107e17e", - "0x37992c7e208c0b09309ece681e02e957f6310c6f42401702b31f752646fe738b", - "0x2b09e6bae0a34cc6c8c83e97c3369bc847ef6410551494af91a3a39929e7d949", - "0xebc85167d3a2fb3ecdc5b07d78b6ba3a0d8294901f75d687087449ed78b305a1", - "0x8fc0dda2085f4e515491cf0d5a525f10d50c58a0a86a58954b07264425bf1e16", - "0x5acaad0dd003649ef9705e73232b4f9598c3fa14cbfafa1691d0f987639b914d", - "0x41cb16b9f5120095aacbde94f52b635a1ea9f347af8f4092660e84a1b87f8535", - "0xa5f2fb17e7fb322069fe66bf093b99f54ffd9949fa4cc983d2266013d2dac2ec", - "0x014582ecf0cc003d21b02662531b8c0422d499d8b74069533ee24b9774c1b7ff", - "0xda9554b8c2b3fde14390f91282a91a9eded5473cf58dd2f5a6e6168cb4d24d3e", - "0x55fb7a99e9e6e3d9fafd8c9f65dbfe821598f2b5a63435204c063cb4477b1d2e", - "0xe54e3c51b6eea19c23875beccb7e18094e4db26f3c94431df1dec26e6db98773", - "0xa7c04b495735a1a829c64f06b94c486c0880a7c54461defb31a420fe0104e1fd", - "0xc7299bcf0202163a946ddd5d8ceaa209a1d81e28a27e5f8fc660c00505df769c", - "0x2526f9fcfb45966a26370e08b26cbe29fd5aeb568b0f945399a6ace1778ebb57", - "0x59b3d302dbf5e3919550c0c1b75bf111d513f2d0d948ca8cab6c44fc38459b3a", - "0xa73fc3b85209e722b88374b2ef3fa3df240ef81c9f9e0d4e7e422ddd124a5ae5", - "0x0dfe2812db18ec7836f8d2227e35263eb98fd2046f597c203f2b499b538a308b", - "0x1f7c6caf872ae90f4cbd92c7adc49ee2ddce61baaff37186111ba7d5347bb4bf", - "0xd67aa89ad3f03b9d801d3f1cb1ef58a5d9f6b8e4cd9d6fe9b862b697f60951d5", - "0x9d760e7ea703028be2d197a0ce28d4057bd8fd7638781e9846cf5d5886ef6611", - "0xe9bfd8ff1dfb81f4f5e5c3a3f821384f98d7c6b753ad356952fd10527e08ced6", - "0x95b6333df70dee92ff0d880239c2ec7a4016c6192391c08e797fe24e06bfcd0a", - "0x56651120494e23767700b82c6718a9c1abe469da49c38937582d08220330cee2", - "0x70d3bae96dfe1a093bf9233d9f8f4c149fe4079c5f40087dd49c6e524cdd96d5", - "0xd29445a1ef099b0ce227c88efef59677abe47763149e034c3dc96d999b66cad1", - "0x7e2fc31ca7a02f965757f118797ae5093cc95a89fabac7e25a22193f2346ca57", - "0x3b7ceebb659eacad955fe0e95a6f141eb4f59f4fa70e52da5c85d7a9467b9298", - "0xdabfe5efdd2d7a44022fac35b13c8acc8c3c1d69ff360d45d997887feedffa93", - "0xc883f0d5c38f40861f9466fdc84170f7605e9af5ea4d6f9aa5088c6c9e482b7e", - "0xd5b14197d738e2635f7294eb8993b01db1ec02de38a545fae881a3f11f3917a6", - "0x5d2b9e3f7a490660bc2592cdd4e04fc9f5e32de1de4c9a8c223b177627708bf6", - "0x2e94c911c88a1cd80e94acfc9f27bb5675a6ca79dab6f70f38a4df99f84f7bbe", - "0x23fbe80ce230e0cbf881279ff259642d6ddb579f0711c5f66e93db3896ebd835", - "0xf1eab3ddc0e2c231cc78d123e20f4195808a88f79c21b9ec2149e3ffb9b1d5cc", - "0x5156b02d5fb9cc38a14e3b4cef86fd99d01db773f681404889e6f7b76a6b2d51", - "0x4bef16dfae5442b8d2451abc13f610acb565fde8400ab2ce52c44f54f68308ad", - "0xa726272620270e5649309798d90fc52ab80b779fa03fe639fc8ef14256d5b1ee", - "0x7f327dd9c23774439a2c080274d3111e0487e1e9c848a9e11c14b2e5a9b307ed", - "0x711fde0a0f1474e0ababf802d8cc8da401d1bc4f836868f117f8ad3756179296", - "0xedb4b7af3699fa441ab53a6afaf380b5760fb86c6b5a0f2d73075fd9da23c8ae", - "0xd7062907f805b2b54e9573104b7248cda283ff1af3d86b7d77b4f9a94d420200", - "0x540b2ade8a4d3571094a14e7e337cb4ff66a86e3cc9fa5da85e93c21edfb69bc", - "0x3c06c59332db001ac1cc72e9b07adfe31155df74c920d7a897592fba0d265a48", - "0xe24179dbc1457c23d05b307e0b42c1ab276db666e49b48cd3e1b3dc82e285de3", - "0x70675363d1a4a6db8b803e3876b171c4ad70b25a56790ac7e4cf011ded4c9c9d", - "0x80dc30723f933aee8e126815fbc226186c07d724cb13c60bdd55cb470f159c4c", - "0x9be135832d600074ed784053d80ad3fdefde87db55088a545a1f8403429d86f0", - "0xac742c9ddd3e59c3467473ede8a794a8b5d1c8299a471c7510cca0b11259ffec", - "0x3089d8ef215e1cf33020d6f2dd005c96d9e7c8bacd2e272c788b79e3fe016caf", - "0x7b22b3b82634041abaa917ce6ac8738e1ad5ada1038bd1a583c84f2765640e0b", - "0x5812d6270239d7566288bdf58df80a2f692731062d3a0aca56d3df972616b553", - "0x0cbac8f96a15a1f57b3ea657e5888598f7350466f40fd46735f7a8229ae7c528", - "0xac6d9f1c216f28da4b97cf2f78f4a7d6ab38636e57fd9a4db71bff191a8007e4", - "0x8dace71e1c1a12a57dbb4794cc92d3e6641201ee75a8304b28b670719cd9a179", - "0x03d497bdc46d7189b30bdcea32a7d93ae17aba1851da4b5b7baa241f0348649c", - "0x9b2316358a104fd1a93cbc388c1419b1db9043aff711d20510f012bd135a1309", - "0x88d613d910c664cee5d50fe1733340853900df4000225698ce32816f457c30b6", - "0xcd9d22a2cb07552bf921cddcb89d879e49ac840a5cbca91da3324427d7d2b80d", - "0xe7d213479605f340cf3714234eeebbcdb63805c5e9479dc56d4a4b2733997bfa", - "0xd899fb4bf3a918a9cfd46d23152fb1d5e54f97b86bf270ffe28e6e55e924e391", - "0x14a971dc6aed1e7dfd4dc3ba613548b8966495628182ce99c4fde9124ccd04af", - "0x89800cc5cfd0ecfb45bbd253dad288617123b3690098e41b38c0d51cfc10234c", - "0xedfe6f669b7b9dce540683400260000ee191c3481577aac6efc163afd7af6154", - "0x70b50725e51c7a5fd4818be7286bd1cc9b15ec2ed4ab6c1bf7e7203c4fa8f25e", - "0x22d02fe95c6f959f72ef4c96329dfb31be8ddce701e0c3ecfc8398119bedf656", - "0xd7877a10f7f883975c5498cfcb07f044400a09d906cb94c8cd29f7a4b93e4b5d", - "0x2e47d9590efa111e19419df7cec6b157f4e06dbea4e64108b7101017bd0cbec0", - "0xbbaf38881063dfcece265083468830bebdbdbb833a57bd7844044ea0c57d1e44", - "0x9269c959dfb4540e3d6c815aa0135b945acb7f50b38fff49f6e34425477ef553", - "0xab47e57f72ee8c8d8857247f84d3419edf43fdf6470c9ab070d8733cd8ad617a", - "0x2be450e3b77d2d3377218412be18390e984fefa2df07d02edc07ebb2d64a9312", - "0x6cc64bc8709eb51b5321b25453e77f871f364a8b277ffe51afde4b8c7a181338", - "0xda70fdeebaa9502e8d0af2350824ee0807e31cdd55ce4195f32a639f47f399cc", - "0x2dac4b30d219640aa01a2d7c69528c8d31636711c915827a64a9219524f2885d", - "0x50932f0dbacb5af5785065dae19245148270f1d0d3515b8d7e191c5702047b6a", - "0xd919cd9f3f6bf7ff66340bab7c562bf53eb969b3e00fc950d1d3b0d9d815e351", - "0x76a276f09fc452ac94cac15d5226ccf3110080d2bdbcb01e8ad86597a03f21bf", - "0x83bd36f26e7d087ca641848f5bda2313903ca64f8b61cdac127ec56efbfc6ccb", - "0x6a65903a006c6759aba3191ce06fd45b0702f127d822d9c889df12b2d52c7bdd", - "0x8e440f04254f81737b00d3857d1a95bf8709f1475bc0ed15d7347c94e57cc7df", - "0x20929894b05fe04fe12b11d336bbdf33ef983eaf6c501846d48813a97ee4b5f3", - "0xfc727f2888aa9f47d486f93b266c6d09b09b949821b63bf86aaea830c16388f4", - "0xb9035314e27d890b6362db7cbb0953e953b57163be0d80e5ed36759500f3160a", - "0x6d2ba56b3c06326a0fae498467f439b47bce1174862817b5c703019e8d448c4a", - "0x6b92e2415eb7349014bdc3e705a2170a5cc09796930d94e861868f77c45d5802", - "0x83a57de8623aa9742b33b3c8b3737bfb198f738111e437d9bab12baa0660cecb", - "0x979d381266e5105773f922d3172dd020a6010b9e435f3bad16ce6065b537f384", - "0x3fdaa18e7a2ab12c5705b17ae33e96d8df9ab3be631bb9c46d31052ad423a4be", - "0x439e461837340e67edf1c7b7fcf633d5c3d8636174335dd80023dfd61f66c67e", - "0x3ba40c7198d6c75a1ecd79218edb9608b7cecae9f779b37d26873d040d29b308", - "0x66ca7e3c8f892bea4be87afee3db3c9352641139ea4b8d459417180c2df8ac5f", - "0xd55c6d521f3a145379a1daeadfede8f837928315c9c2f1c2642ed0e1a7e87674", - "0xb848560abba9f0e19af44219c2c1a5011ca43eff4ff6471555b3c1c235c40119", - "0x1fa19a85d008e7af8585eb7cbbcc8d61ed6248708429d0f458f1b497ebb190a6", - "0x72a4e2f8b7fe422eeec318f1127b4b4e4fb03e40a028d45f5444c9b287a4a764", - "0xed88ca02a5918d6f621fa1e945bb1533e33ea5c1d41f7915672e3c4e4dce78de", - "0x60ef0df2f7f5c6de3dd7c02fb202c9b3a13f48102f317b54b25b2f0f1f49b6e1", - "0xc49153808e765883dd21e1b58b5725aa1d242f920ff0ffe0c6089a5e00719c82", - "0x0c8581a354376bbb53379f45c92e914e46676b68305e8fcf16eb069c65e87cd1", - "0xb562721b9433cb712b3969d193e132132786434bde1d45916bd7423194c84678", - "0x060cfe84bbb7842e0a49bcf9f8753635d9fcd694de3b8d5827239606269f0cd1", - "0x84306039a875a723780ec685c34474d35ac3431d6ac8d43b2b6d1bba8572eb9b", - "0xfeaec570403a0190c889e5d2ecf96a9a6c720b3944e4b703049c81a35b56d820", - "0xae002e7d8e1279513b037ad09695e93f80868c60d2d8b0ffe6efc8e0ca8a51fe", - "0x20e1f3248ebde32f2fca51a576abfd0db3fcfe359df399f1c3102ed2183b027c", - "0xdf77e0510d24818676727c9d56b58b2495ad63902cedb04e2f3729b58374c942", - "0xc33b4b53e7eac8725f5b3c27a9431dd6e4f966b1289e3b2a1e158474f4a47f4a", - "0x6466f3aecc922abbe6f995a3ede8ae650be7f1c08679c5f6355e32ae95a4412c", - "0x56322467df2291a50bea2baf66169caf34eddd525655cecf6d14fd2295b2a438", - "0xbd81e7bf5e30f4bc1b87c57a4eda09293cf6be979bd2bb02650ce5bcab8da996", - "0x3d611638ca06abfde10ce7f02152069aa30d97949dabe3dafe8980699d49ca0b", - "0x12723caf905e0c044344c83d085f3d89c2eaa695c9d70c0a831d487b050ed453", - "0x473f61c421d202e15d4a62d4513bd48313d253004feb09d9b6e0502c6ba276b1", - "0x9583746c372cd042a46ada64048ac44b30d3b055b6e22ca3cef5404e57a99f0d", - "0x8decffeda7b7de072da8268776c4b9969cbf3e7ae9433334aa7e1e7c93567f55", - "0x543f5b6b686035462d17ef969fa09e0af3e7cd24d13650c2d3597516e62f9909", - "0x5381b2412f6383d057bab3fefd23d08384f345960cc2fb80d7311a63be5080a0", - "0xd96b7933fd9308f7aac9fc42e8feb9f2f4b586400fdee23798cad69611320461", - "0x0982300c344d44c2f63e1c1fb5fffdb44c23749204a0e012733317cde6efaa83", - "0x6048706531d18c5f9a257448b2826fbcf7a72bd08d5d4feb2928acdaeee61768", - "0x2bb89086896edfbe79407ea2bc8535c6be143be6c64ddb1190e626e091c7e802", - "0xd1d6697db021746970393fc0151bef8aca67d202b0e54f4f2702a5f86c2fbe2c", - "0x5c1e1a43ba2578bf4a302e18f37bbc4f1a67441fe6a71ee856662739a5821db3", - "0x1ed663708c0709027c702ad5ce0eca6d3e0f22d6a01d4517adab0a3d25f5eb26", - "0x6ca247d5ec5ae33cbecf8fe88c1aa64745f7a8d299bfd356bacf764141d410be", - "0xfc020b5ae74507a1475ef8e2c0bb516b67bf79aaa2b61bdbc472ba2d75ef5db3", - "0x1ef0990b9051937ff1fa02880131aed9787e6d7d41a598ce7b5094eea1daea80", - "0x9e9e9a83fd6907dc5407c49ea34bd1745919d9ddced756433f374440fa8c704f", - "0xbb3687f8723712f39a8e9f8865543100e048acdcbe2045803ad5a40b61c4a36b", - "0xe60d8a60edc3d7e31ceb356e493a28fbc845f302ae2a8d1e7cb80f53112900b0", - "0x294039b994a3ff53cbba7718280dc827fd582ae789736a026eee1d39284b35e7", - "0xcf87c0d2ee79a0d2506a3d557880f3ee9a20ff51bb44c20caae0849a85055408", - "0x0d6bb688dcf3430e46de1512ebc9e411db3380590bc72dbaaabc52a5320216ad", - "0x9f2cd8a43695c78c7a8fb1352d581c82dd9dd0ff04e3485d5bbdd5e3942f9c4c", - "0xcb32930f407012e479eaa7b4844107167f519deea6c7fc0351cecb93c500d63e", - "0x17e759d815596e0b89a3dc4ba12b9d10ec59988fb0550e1518a8b1cc4f331190", - "0xfb3b36940c6675249451c4c857f6742bb0e1fd3eb126e370eb4deefe238d7d75", - "0x47d619d7d8a9510290dc0b8b2518af2862ff6f761ff09786dced91e39d8afeb7", - "0xe8f40bd7a7bb8f7dffbc63addf31a27e0a987a27c00525c833f6ea8f508537e5", - "0xca372409bc160e5eb7fa70e2bbfe8daac9763b374c7bdea0cadcc933450443e8", - "0x6e234961784273f80136dbdfe0f5618fce6c0d63c2fbbb5d7fd6823632c0ab6c", - "0x2a6cb327e669de9f9783300ef6b2cdba5585236e286ba94dca114de92728ebc7", - "0x0e91e6a11ea59f840d9bc47f251043e10159880276bf69beb075557e38715fbf", - "0xd452ed733b985db558b960ed14310215abce589d4149952a3ebfc7604082079b", - "0x7fc984fb9d191984b5141a9dc7ed1c9657742c63e7958c7652bd896bc6c9f985", - "0x2ce2f74343ae9e37153b5c071c4ba13adcf67676ed7789379c364107d76e32cf", - "0x0bb95a7856e5f61bbe47d56aae9d7f42b994fc4640b50ae80200ed8a678dfd51", - "0x2ab80f2546564ff01e812a39341ae227b69ccc1b38c7f28e61c31925cf622811", - "0x1b24ab582f306305400947e9e7e43a49de6cfd62819517a8ddd904972bd8eee7", - "0x852b65355f93035fdf4358ffc3566d57dd97c9d59a1e545706bc6a13690025a6", - "0x6a44f88a6097b5e3c822abcf1823c98362b26b0bf3753b6fafaafabe9591a07a", - "0x4e7c0a92561e904eed52f2a2b906293411b97795bb194d9c92e335cf9e4ab200", - "0x6ac01d96e58002f8881cea4e177ec7606c69e21d252d863787e5cca65a839792", - "0x4d95bfeb96b7a7978c890842544e8e780f531512c182cf22a73a71b55f1679db", - "0x84de4313a7d87fd748e9872db1164ad3ff9d48993ba429b74dad497dcda5a3b1", - "0xb5ba8e71bc844413cc3eee7325a949af1e0ea81405a3371512fddbef87e4e9f8", - "0x2378cad3d3f0558c0df47cd179791604af7df805f805f7d1ec67b52b2c330d6a", - "0x260d00d98b976fe3aaed233aa9e1b8fddec1092595c91336a5b884fc07e51a30", - "0x7e3e9859270e3a7bb7c5f6d7170e98a8a0a681216e21567c303fbc9e755a4739", - "0xf9aa5450b6036bc61ea2113fb163997ede1bb212f343ae5df0b55561ff32797b", - "0x465cbec75cc303fed26bed3e2701372f9bfefabfcd54096a9189d73994e7aa17", - "0xa86195c2696fa647cd5971d47587212d48957f3faf69721683c158a1372eccfb", - "0xdd104c0ccaebf94bc3c0a6abae1852a71f9b1eee974d5e622f5386ffe0949b7e", - "0x005f5108714efe19e512aa83608e9cac46976b0afcc7a010bfd682965af39d43", - "0x60289c653af2f12a58b10c0f81ed6072b3112eb270b350facd79d0363be2bc8f", - "0x8e22bcefff3aea2767ad9b118be1253d1aedf1e1da502a4855f6ae7eee54c335", - "0x3ec7f166314a0c594eb8138b75a050082884556f7664702333c096bc93b67820", - "0x605398473b453589bc31312579ef3b5ce0b74e3ccb95b680f8beb30d2fba34f6", - "0x51dfca1880e15660901b5f755530c3fa4d652050c134a769b6ab2e1645755cc5", - "0xb24e9463fccdeb2178e520fec46eaa8f2d1da5380d48815a2fe1816f71b79b8d", - "0x97bd624937de7bb8915d7e396c31cdb4afb739bfd4eb6d96f8773ddd0e4f4933", - "0xbe17101284e09bcaa9eb5cd150983b05248b620465244b603b598bbaf9bf312d", - "0xd3c2ce415ee6bc3908cdfada4fcc76b5e0e9fa4f1c6ab43a3710b2ce81f150b8", - "0x8d4918cfe78489e146c071c149a7271ea81dee2ab0615394ecc35827193e737d", - "0x6a7e51675ea6214d36acc7fb193911d2608e41a2df747e328afc6f5a465261c0", - "0xf012c5f418c7e265bce72f9f4e2900084216c9187900927d9aafc899616ea9e6", - "0x934c51d4f2051bb7aa555b3f44bef81ff782b690e29f7cc0a9411ad03ac74a3e", - "0x9ebbecb11efe195d2ef7681c8f1f8af3b501f7c65a0f9ea916f7ffc9a6bafd74", - "0xe28247949b99de84e721641d5e3b638dde927478df0ed0f53b8970b4febbfeaa", - "0x6491ca6bae8781055660837b72622f19daecc7407aa58d24088b45f5725691f2", - "0x3a56f6fdf34d6a4d742451c1f5b6e575685f9a6703541315df1cb8aafe531f7e", - "0xc204b9d22b7cbdcc9132539ea475792f4ea231cb246747c485632be8df4ff5c2", - "0x6188cd2938f72d2dd5e16525574f240b458316d42daafed892faee9189caeba7", - "0xa272f3ccaa93e016333dbc904183db6309e31d06720da1d18addb7560b9bb427", - "0xdce0b5f11909a90c1c6b8fd2bdb3113a035e7784103e05ffc972364530ac36ed", - "0x6bcf3461ce1447e80bd3da2d91dc4530bb649b98333304fe3e0045743c0b96ff", - "0x523496ab97b10356b5bfa8e17b4927b8f0f507368391e24250f218cc00be4bda", - "0x6dfa5889c65ed8413d6633bffc9c3e648621d38eb70e57ada781b3f4dd62374c", - "0xaf1a300f73429dec7f12d0ebe9706cd113e15e1dee6cab37ff11503a836cb3ff", - "0xbb8aa95b40671760e1d131cb269c2d156f29d89911604db04ee7f87511283b56", - "0x93ca5bec69be4ef613260e2dc850668d5a1c839e511322d95ec25203d74673ff", - "0xd7cd3a14db02b6c705bb6cdb03140b50b6b989ab82d733df84f990a9c4d6b292", - "0x9136b9484831f1417ecc2d5ec93fe31b25e79b35513cf5187e50e3b95f862269", - "0x1519b8a6581123491bd0df07cfa3a40b0d098e5cc6ea981368fd073f7c927ff5", - "0x08ad5c68577e9e09e5b00035c65835b404437b792ff327950d07ef227aed6a84", - "0x4e989dc2f09df3e9fef7e50e7b7a9da85c091c11796665602a0f8294dcfcb2d6", - "0xe4a311a951e3743c0d3d05db87cc5d62ab0ebbe6b2e85e035c6452b0803c4be5", - "0x4a64e0fd19dd4b2dd7be3810f870cfca0cb4303d44204ae329f3ef1cd0a78420", - "0xd13981c9f5bfc2efe70eba25bb4f3d3f7bf13d454527df015e951bd79c649c50", - "0x57af6009a040d74b628416de645cb13a58e16fbd63035c6a6d8c8a0bed102ccf", - "0xfbb76c69f42039da5d672776f9e67fe1fd3d7eb0d7f5353852fa64558a91cf88", - "0x07808058eb2b620907baae2b053ef2c5786deb73df750a1b715bb1e0dbc0e14c", - "0x24f1e75fb9fd88b2628285f3e49233ea6f59b474ca05f5823810a8b49ae4cde8", - "0x5cc1e13c706a5c18aa40d8e5db49bcc87c38a07655a814b2179a35b1a8ca79bd", - "0x8ce52067eb546774ae3796de7cbb58f33395438a7976eef1f191cca9c6f59f76", - "0x094311f783939ded904c263a92d5db558e60352a392d3ba77045ff82114978e0", - "0x399ecdbb06d1421fd3651cd625376cea22cce333c6dc200b9e572a001b794a3d", - "0x6a94f126de560108bdb47dcfa8eaa5e9895b3bb9bdf7e7e3078fb725a1ca18a3", - "0xc0c7a71a8e3547cb067a73a984cdf283f6480524578ff611902eb9ef947c4c80", - "0x8593b4417b4d497d0f1d68c21b5f27433be956b6e8f94d0e6efe983492647fa6", - "0x616dacbbf17052280e093ece29a151df4c78027d652ff2ec3887a48daa916d31", - "0xbf17d700c78628de624a49125b764c1240f3d59b85fe376a64cc02f44fd3f46b", - "0x89f0b2b0b4b0eff45499231c9c5fdb53e95f72c589e7ed04c734b4d07df32a69", - "0xd4399b262ddaeec84cb56417cb84d2065664d79a51750ee566217487f420d8d5", - "0x89874f2ef4e0b53e96afe703f4f9e5d5c7da81a621acf00df29f821098142898", - "0x84a9a966381053a4e933c1ebae4fec74d3452ae5feb2b1c90ed59c92fa0f5250", - "0x748adbe8f803c86ef6f20ef7f178123f46d6c45dcfa332827918f4cfa9417663", - "0x0c78bcfbe149b1b2de2253d443011aefb4e5ea0c7a48e2e13156cd5276437557", - "0xa8d86c719b15c328e5117fcc0f4b381df4b8bab346d522df49aae19820ee128a", - "0x3bce6f3055593daacc3ae11b67d5dcc20475e8eaa91eb3153f83bfca6f2e7c8c", - "0x8af84754ff63a68b7b8ba3948e255fc172c53610aa90fc2f5f633bd8ce1ef7c5", - "0xcacfe2b9181c60d0c3d7b48403b18ee184e7c7032f81eebded3f19078c01d7d7", - "0xde4bc2b17855522804fdd6352f36eec477baafd69823908506b949c017aaf4ee", - "0xcbc7689bc06c9f4f04414835a89650a845884a2d102b6900f3abd4d4c8f8dc3b", - "0xb9779ac13bc739d5bc40bdb927c3543310ff114a83bbe35192c250a5f772d441", - "0x1239fa2d0cbb2255d32b82177a976ed5c9f70f0661a5d5267c60f456b9a7f44c", - "0x3e81ff86adae141c75c8b1a91d47db7c6b196ab00cc883fb2fba7655fdd6dca3", - "0x551e92e6d774825a2a48555268c86ee52e1b613e63c8a6e14275ae877dfa80f9", - "0x27a7d2faa486508c7fa20b818d6aa130a914da688d5bc8c60a3547c241937aa0", - "0xd10995979320d9d9d64051e4f721644579b4984a4337de86901b03fc2c042e21", - "0x6f9308ec6c4eb211b8c2323b68c4670db6a8116ac4a370801ddab1da0066df58", - "0x17d3526dec54f66815d8e4358028b0aa91d3a134d843d2945a47dd5aa56ba788", - "0x3a673e5904cc61906f6806b634ae6375de45f4f0d63713555d13a1e274e1bbf8", - "0x39dc807657774ec32abcf299b9c7f03371bfb51a864d11800199ccc7eaafbb4b", - "0xe2bcb8da657b830794b6d9f1e7471663e31996c1bb08456f4d8802448deb8565", - "0x38c7998e73b21038659140e5ae8539cd8cf993686c90148b38d6308a3e3b92cf", - "0x4b6d2986964aac5d5101ded91aaa4ff78a319dc04ab18e550589ef09b638495c", - "0xd8c4da66b5525427cafd152c365f99a255ce2e78af3e3f3dc408de0dd16fee99", - "0x444dbcafda5c0c74092577141df9fc8fb1822ef3375ef0d43eb876fe29c262f3", - "0xc030bec71e90264bacb7c09f9de3a5e282684f9f1d45a2b177c8f19f9c7180d4", - "0xd529e4c2687c7b40d1bda81201770838320b1183da1e18c59c83cc4772296921", - "0xa1ca4f57cfc4ec4522a42cda37df9aa74406d8aa8dc4b56aa62fc78c70d02379", - "0xdd30dc03a60b0fc21dc4b75629124e8190daff2e3504222fd6fb772531bdda85", - "0x2d9d4a9b9b2a27cf863a292a7fd112c81b0deae05457073fa0d5bf7e0dd755d4", - "0x19815e0f25090a9494cf5bea69edb70e7a56e6bf56bd19ddb1856198f54399e0", - "0xa7fa776c8227838d27496a3c659f3da3f5acce22da70701dfd62953edfa2bb09", - "0xe55efa9386b2e0fbd38af545106f197868361f2dc44051cb4a1ad540396283a8", - "0xc6c313738c3c75d434da4083962f8e8355bb8832e17c7104d037b131682b28d3", - "0x756a6309e8c0a374b99b2b2534a851496bc74db36e5bf1c950c0549914f0d0a4", - "0x36f64ed32258a3d2dc849f4abe98db30d8b04df28420c56215e29190e8a447bd", - "0x5273c4accf93338826feb5bd75117a7a0ab5a01225972532ff4ac1d29b289d8c", - "0x6146cf6b830f0222a8bf9353ea26deb973a85c5c4a005136061ab0fb48fe56a8", - "0x97b07c22662079a3a448b373fcb73320fed4a0877125228b4e9a5b74c0bbf703", - "0x68d28adf72884756b688377fac36d371325a01c65f5b70fcd7ac16a2dcd21444", - "0x2c27150b4cd8aea85723c4d264b8799c2e92bd4430719da6bdd3a0290c024a21", - "0x6b553b884acac18804cd52c7139e4a245f2d263b6d014fb6c28e31d7ea08e63b", - "0x64bc2ff8936395f794702b110041a88c1acc9cba50e12c2c5c9730976e667ae9", - "0xc45b2a7e308777811b6c099ffa3287970e003001dfbfe448a2600edf995513b8", - "0xa89c093a0b77cf4ad1ad7ffee799872a66ee38c3989ad39fb90069ab70b1fb4c", - "0xe8d0823460eba540c0c86016014127c7b77b0afdd199fc7bf8145a556a7c523e", - "0xcb2ce7a29b4c67956c439195a84b53f5c54e273c20b6cada43aa68ca7b1b59d1", - "0xcee4cb506d3b0cd84b763f405cd68e2bdf9ec1741778a004eaac9910d17cb67f", - "0x357376e9a49b6c7dab7c5cc95d4a90cc27516f1b7f56c33c4c0125516153819e", - "0x77090e31db86c2f2b031cec1a6dbfa3c3b2f464db7e8444b5def6468c03f5308", - "0xf2b0dee21d7b016c0c9e1a02a970835f934f66c552d653c9d5fcfd20aaf397f6", - "0xfd662e1a27c14d5e8b1e9bf6fca699a3ed7c5bb883cc626d54f53a721c570557", - "0x86194d492a5adc474f114f9cb7b0f6f6795d7680614b29bdbf25e969513a3276", - "0x2bf3ae6993adb5d85fe62bce02a836beb598779ddd37cc2db1f44b1cbea616ab", - "0x4321e3a94d12161ebdbc1b5c0f9223a7f1dc9050792589ce95771c7951b2f176", - "0x79154a76904f2a3c59e7151fff7cd448a3f74c003b866b9faba330481c6895c5", - "0x9c9e7e1d3753804e3644da89248505116009585c6e6d42071cbc532de20749b9", - "0x4dc1441b23210778e4df8417be9f654619527b5b42247bd7a8490a4a5f50dc49", - "0x79fd11e85b307098e827168a157ef45b0af5f4bc073a2920f42c6ff332392a58", - "0x446e8650a156bbb1b96d22e0d09619f90b8baeed34f8b63ba7bf3da3b764e219", - "0xa86cdf11cf5586d8b0449085e7c153a0dd55aaaf930960f3f31f80d099011d70", - "0x4d30e55035eec495badfd3cb8a2174295b2d2cfbee4e669e61bc148d09ff2342", - "0x2f8f3bc3c59975caf160d4a5581cab3499b84a524cb7977e4977de10a74ab875", - "0xe08b4473276f706c7399f584797cf07d33a91c91272ad79e0a5004c00a0d3cbc", - "0x1e27eb957ea54f1806115824c61d78e265cb75807f68d508da5ace6c7dbcd9f2", - "0x8304bbb4592daf4cc754c67dd78a2e2008b1abd8d966521a2291d170c9551e9b", - "0x8298d1605af17c8f47afe0b34b84bba22441a064a3d5311a2ea7e45f76565c8f", - "0xdc8014aead4dcb61449918deef31c64ad8555939347bf04f543d6a1158eb1771", - "0xca91e9581535b11cef0ebcd2e68dc00c3a18844fb4d6f05acf26eb3cd15f1047", - "0x2b26282d94df586bc7d4bc0cfab8ffe1844800c7045621527b9f9f0d5004949c", - "0x9a92f43c5a697a097e39ad12fcd9279b8fb3c3076ea1d910ff871c5ab3342a47", - "0xe33707e3e852b3b6a5cd375d981810399de6a3f96015bff7a4e5065f9c8713db", - "0x132ddae1f0244abf072e6d8115470ad4afb24748169891be0a4b952271599333", - "0x2c1236a98f16a294a9de1c24ea59739fcd6b5932b3f2535c8d131bb2f92c1dd5", - "0x989b0a915bb07ef5e3673a99ee796639722b9e4cf5086433cf8072fa3264f4e0", - "0xbd871c039e619de1514cb6f9223299c44d0555b5eb88570f79fd0d593953c197", - "0x7fcb1ab57bca2410a274f6339927bc12ea6843c106816fe10fc9576b6c7f7cea", - "0x5692b49ef3c3ffab97ab9ab1bcc10017e6d0527e1fab5b257210633dc4d2658c", - "0xe105a92cb6817fe6fdd585068363e0ffd45686fb6dc3658504c70e2057b165c5", - "0x97e65f3bb842f242f51e9ce978320a90720858dc9c535c1d66bd1c269dda0286", - "0xe5ba47b574c389d060e0c5f99f69829d7b6dd8b3bf6e64c315c28521a329c392", - "0xe971dd283f7ed3ca0e28a73d614c490a959c36f44bc30dc7fd79bda064f23aa0", - "0x0c786961cb4df57adc1011891b90813943a13a06032dcb2b7f9ac38dc8b3458a", - "0x3736f1ec97fdfecd7c2e6139fc7ef236586bd9507f65a99913a23bafdd5f069d", - "0x5c6cefd0c6c9c825668f99d1d595f6fa55a45e6a43e23323e901900e02a32f40", - "0x00373765ace6950b4728871a0165aeff55ea61fa8ee07d51d7b8bb8114c20380", - "0x49abb5d78311bf743008c2401f074e4b190d9b29c59cf6328831decd83068172", - "0x0bc28d7480d5a56588b937a530d026a94becf9ced1fe71c6dd1ae0d2922bde50", - "0x45e4ffdd3b6d480ee76f0ee7f6acd7c039ef564d77e5a91546e8269d5468ff99", - "0x09600f167ac2e81cb9164e048908a5b410cef5b48f92eb9a801cf84104430172", - "0x00ddfefc3d36282e9aa8e163d54df86f10737416d9dcf33e786d1713ab54052f", - "0x5b9e3159e4394f03d05ed8b8127ceed638ebe147beda7b7097b6dfbd8342df53", - "0x85d2ae2bfa381b7f5c1b0305f8e252f703d295edbd7c05908e6ae564d05443fb", - "0x5d93339013a78b3344b994bb474fd714bf5495dbcee84346a30219b214f1ed27", - "0x509921ed07765eff19b5c5b7885c4eeb30a8329417d1d819f3428e278364a192", - "0xe004d5f07b3986916f8cca4ba84f9357d4550c26d408f79fb49dee47c5afc100", - "0xcd0f7446313d80026c33427aad00e4689ef0c9966dc5faff93dd1ad258a032be", - "0x0bf64f9c84ef26104cc0d0bc61594bdc0d22f9bf25b436170df1c47351fc8746", - "0xb2e605741fe4b42abeea0ad054727950485ac432754ac2f98db695988c515d1b", - "0x9b6ef4b72c4c5b8159c308fe0395c8f74119bfc495a64e0529209967a1efab7b", - "0xf682c4da2d193bdc8e23cbd2a35d2d2eefff9281bb053f86c84841a3c88e4517", - "0x19cda96cd533527a9a93fe8c38184778cc79ee8ce389c181e67d79971c82fa6e", - "0xddcf6596740ea21e3c5135e32166e5e7944ab1a37ca60c57d9d005f83015865d", - "0x4932d76141f9f33ea5193e58e1767f67b5017ffc277c41fb8b9cb020304b923e", - "0xcab96730ff161fdecb5c0118b34b2a8c6ca2e53cc38ef76b8702284fd64d862f", - "0xe042dd86029d97b89ed789847c9651d49b2790d68b475f194c63c89974704b95", - "0x548200b226d36c3092fb9d721b74b829564bae7f39a63c2be0a78ecac5c69694", - "0x26a162476b85f3f6e366b3b767eb39227ce358a4c78dcbe6c8e3710c6ae05c1d", - "0x6f388c498dcc8b1e986c17332c8df84af7e8af9fca5c5a59600dda14f74e125f", - "0xa342f25dc1194e225f4c8b4d89b8992bf59517cffee52a1f2c2a0cc4727f909e", - "0x5e0d21aaffd603c4f5d73e5eae97e14988b03e45beb15bc44776e2db9ce20fb4", - "0xebbbed03150c85b1a7c7e7e6a6a67fe0483fc7176bdfe4f94bdb094ffce5bb2b", - "0x40e530eaa7fc24685d8cd9cd94a3f4b01aff4cbea49aafd448cd1f7ab4b0c186", - "0xecb1cc861f12d2be9b1f39bf56363937e6bdd5eee6d7e5e90426e7d29bd75e65", - "0xc0bc404c877856648f5793792aba95c9fcf2768eac65028e0a930c57ff9d158d", - "0x3e50fea97445730cf5c3fe8f05c1d03c2298fcd52a7d9be911d1c989f29fe204", - "0xb512ff3c024347545bc2843bc84f6006c0b1a91f49a7549558ac5d8d331c1fce", - "0xc0fd2941f49fe9ec9ca375d0745803539dd3a9750f1bc46380cad3ce29c76264", - "0x7f37105755852886e86d113ada4b4f1f68ebf0aa36dfa36291584dfe28e773eb", - "0xf0942db3af7cd949ae31d1f690f832c6e4f8a3b9744ef9bbf8e7a90b04a0ebff", - "0x6a9cee1f0f3ae40d63dab9547ea6bf342efb99176fd30d0ed2d8de02f1be6c31", - "0x2c2092ec0adfe352c46ade57b3c115783b7d4a92fb63bd1effef32e162d4b34a", - "0x4aae695ff67508f2d03f8674282d58e4f1038cd0d4c2a6a841ce425433e305a5", - "0x543665bfaeb5c10c0dd4d180475c9aa18a9edc313e52405f1c5fdc151538744f", - "0x7473934014d90b821ca0076d132c716728c19927f5680482b69163e8ca5d79a1", - "0xf3f4efa6bc4e493b5e09f32cab59a9cfc28b5a180a59640270a79927058a4f43", - "0xbfea0aeb82cd88ffa0269c56eedfebe83759a4af9345ebcd2156ec234d99633a", - "0x827eef18a3c2e24021dacda3c00187b8cc87e071ed1e2e9246be0066135fc284", - "0xd567403ba1bcccad81978857599647c0a7008cd3ab1a3bbbffec1217da92b060", - "0x74c8805b9b589e9493ee02bc73131431256d18b79de4fc3820ae5220bb5498b3", - "0x32afddf7ebc9d7efe51e045446a61fef4b156a3379212517b0971a91252e1b2c", - "0x4d050a932e5f971f33353a23c5b3382d163319321f55368598bcc3cd8c67f91b", - "0x9c01431659ec8c4db0cffc566ed9da3d04d5db610c6d6fbae260ce3011fb68fd", - "0x0814da2cbf76f83196ccd5342638df799d881eab6a526eab325c06d75b7f8e01", - "0xe669b4a2569397b901dad843884668d52ce85919ac6703671963d55c0923df5f", - "0x44762f71591afce826580a50fb4af763d7ceddc75d6e74d4f40deb3ec4c6ad13", - "0xd3231536bc6b897a24e7925909da660e7bc2c5154e529be0dc590770b5573023", - "0x8934b9858529b0bfa879142e0242f1dbe68143ac2ab347517be826e2cd8ab087", - "0xe833f8513f4e71ec56adc114106d5a8ae19b1aaf95d91a5ce18398f316253102", - "0x0a5d3578d418e0eacc2be66f8eb365a7cff71e6b287459e25a921befc7c55e79", - "0xc11db91e90bca5f39221330704193e670815920a5319eb06676b5d74e1d7d776", - "0x04f1e0808c62821a8f088ca0172820f469d327dd336db66d9595932826fb73dc", - "0x1861241e353c68afcba164f24f650eb3892f219d984e376336f401902b4038fa", - "0x142c7d1eb631e40562c24d2a271f0fa21be1ae76d6c7d98491599dc0d0580d27", - "0x203231cb071fccadc815f222900beca55777075f7d42563529682e6449138a17", - "0x304bcbcc7b6dfee9437b8f6b255accfd789ed115461b41e933140730a46675e7", - "0xc9f2359de838acb06278d7967f2ffdfd3bda5f46300b21939a760914a7e00568", - "0x1b470dceebbda583263a9373a4ddde80e2523b3837ed3c20b111e6fb9aa99a60", - "0x765ebffc2d48b2fe92c3fb14b301cb2338984f7760d514373b01fb34de466b33", - "0xbe814242513e1ef6903600ffd517b8f678cf306e057e01f3971eac7514b72ba1", - "0xf313f2b3c05a1dac53de10ddcbc4cc38b38be85f89640aafa4502631c0e3511e", - "0xdfb72e306c68cf4b22f62e9bca906ef3ac627b2286d2a261489e2c922d700dae", - "0x0783bc2129066d953ef7aca86ecf53ec3ff2bfcf8920f6079ad0a4dd703fe331", - "0x71f1fbfc2a9a232e2674e3e36547ee874806ab37b22728d8c30ecdb7ed2ad539", - "0xbb098368fb1529222eb83277af668535a1173a78825c7e3ef86f4f45429cb838", - "0xc49eea0b1171263bd8df058e6279092d59d1e8a4f108d1b4004333733bc6e7a3", - "0x6f95c83060775d579005c4731e05eda62753bcd0f95295b395b1de22b21f3b0a", - "0x9869e93ab6e4ea9da4cd12c279580e211b4c870a7e7dca098058897795804e76", - "0x816a76e3c912b639ae1e4b5eecadbb8cc173c3aadd796815a9ccdbf9226d58ee", - "0x29f33d9d30c64af6b372e922414c85004655782d024916909834b95643fd6096", - "0x30a9763617e8337203196a11f3e4b81b82d9642f0cf26b2c654c8504d289a17d", - "0x21eb2108b1ad465397ed3aa8016c09a52000c60aaaf42e13d95c271cf394c074", - "0x59d8b8fe9dd951d5ec540bf46808f01c9ec2a5e9dd0247b34b2622c48bfa498d", - "0x71b8061902f451c6fbd95c29ff960353afaea2b0410ee11cb993ce0ccb8af446", - "0xe69938e7da23b9d7ca611326dacaff95009cb5898abf1858593060ccb5aff4f9", - "0x0aacb29673ba3a0dce850ac772455c2b092b1a0fe61ed34965e4cdf723d659d1", - "0x18bf2fb69dc62ea39c195a702b8e9514be4b9d5eba18af13974a63f74febccc6", - "0x560b7fa0d15ec6bc0add83b6125133b97672b39477092cddcd5e2936f49c51d6", - "0xc8b0d3b55acae88939bc9426181dcc7d965bb5f85cc506d26f966c60a66e4ac6", - "0xaa9c3dea2f0184de1774c47923fb7f6ead25bcdea96c70b30127ad61a6bf67ca", - "0xfa90b6e07511736519ec741ca85d23d2912b509ec608a5d319c2369aa358a82e", - "0x0ab8709a1c9ac4e9e516736b4836febf938a0055d5b4cde960ba651e986f2caf", - "0x03e28cb08abb6912d3a25cb45868d699b89bdbc180eba67590b2cf13718c967d", - "0x1617f5fb8c39fac172251ac4bb1a9b8e20f9c36cc6a47e7e0545ee38fa55a6f6", - "0xf8cbfbad5d5e66d95dbd5e83b2968d08b6eb41d28efed0f1d9205b2297168dbb", - "0x4adf7de19d0498164921d4aa39bb070c6183e8511c9f987bc91d89cb54d8affd", - "0x7b5b34085f3267297b40770c0f27095735fa286ade7521e81f500005ab63d253", - "0xc2943d2b12e44ff193ad957440620e43fd9e8da22d74bd7e4b18846eb93fe67e", - "0xa461d24d7d4cc4fdb8016476b3d5e44017bc27609b7fd09136c3a884915c9761", - "0xdcbd32d171d8cd8702ec3093dc2f149c31542a7a35a01fa63043facfbf7ddea4", - "0xe7c4e881bd4df86737497ae6e3fd65fe9d953b8e1c45ef1cbba2d291be860dec", - "0x1fc56bf433f10fe704662c0f9e69a878f243d8832fe723852215f4d2ce51f9d5", - "0x5d22da26a003567829f9e2fcf0c2be4d0f791d44400936ff1132a0dc367c316e", - "0x985c40c0bda98d02e3bbf9e33d13be1ab4b1b917c94e06255a0d60de8a547513", - "0x5aacaa6e4aa6ec649fa272c1ae2cd0376270a0252c39d1bfdd973f03cacecef0", - "0xf2992b858c902441bd44cfe0e2b3057615b7ef6a7d129bf8402e4270b711b126", - "0x690eccebda6832cdab7adc9a56b5d8ad7842aba0d7dfa0aa073347d835e72703", - "0x41f48f7975d1aff8070e4d787b3bc127613fe74cc0207e6355d80c930577348a", - "0x42286416ff16e06c1b8088e484e1687ccb93c134a880e06ebc621cfd1fecfbd4", - "0x241f3e48bbd35c8743856e8020a2def871ffef7ba897c72464286419a8e67e28", - "0x3ba5d479584f036f4b4f99942a5bc2f51cc9c4af24527dc9ced5c66178fbec00", - "0xecef4c63d3679889106352d9e56b56b7aff5e1f1408e3dbe86e35dfe6ea6c97b", - "0x2a55f99801b5437fa85f91046e933d5cb898ee3150c6a068f658e0091a303576", - "0x0103857974871ee986d9d5d430b5c3533c0b1187353300d25dbecc2aef14cfac", - "0xe2721431cf8bcc77d5652b519e879d0cde2524972d74b6ce3e6a78eb0bdc5c80", - "0xb577b9c2b3348feb5bd8e283940c524d940268a3f90c11e35dc9de3438ff038f", - "0x5bf29a628a6ae340206e02f33eab362240f99ce0e3cd8d440d002ed4495252f4", - "0x5b087ac28a0e6555830f6510fd275bf464ee4d50e6848b251dac4b823de6b995", - "0xd6ca4c3a14090c0204fe4b9ac487debafbf0f984c289cded1de950f2e655abd5", - "0x61e776419693248d8a6b8bb2ef9b9663882b57e60650ea7fa22f1251e201befd", - "0x84d0ee42adb4c9b0e127535cf76a00a0d8129396f861d5d41dea599dbbff3d0b", - "0x509683f63b390fdfcbb699a8a30340abfb5425b257e86015170d7b7296b33548", - "0xe18de77507e00ff5a9097ae81007e793ca9abf2c11f65172c1cb54a189032d3c", - "0x38a8a3c1c0c110f674a7e69039258593e15a9194b1e77ebe6176ef5caf09c6f4", - "0x7e55232c46a25bc79bf9390c2b105d0fb342015cb526cf0decdb9db23d1be43a", - "0xb745498c642fb70abdc71c707bb1e4baabd534c5bc503e35545a6395cdb48918", - "0xa4b968ff3f3760b1fa4b352ca6eb8cc883f4c902f8e7baa2644d5a7726694031", - "0xee106b785aa0d24e57532819d4cb34b5795ab0af3080b38c73168ad565103b2c", - "0x1535e68531fad8e1edcfc2621747620ab962ad38a033ba25d1d6f5947381d03d", - "0x98c83d74aa9e52a7eae636a0fb54475173fddddbb56ca42dc314998c5099c649", - "0xdae0dbf33fc395f96a4dc1a8da5af3285b8dbabe5b34639e47c70b0938a9f64d", - "0x91348b33bec70e0e68756023f1432346ca2aebbb6a43df28b7388f6f9f19bc7e", - "0x575859a6c8463bacb07ff4902b06fe1c31ceb9552bd781003d4ea2e15dc81e4c", - "0xef1a187b7033a67ff5dc0cdfe353f81d8286c533669ed4b0c0ed5168f018cb19", - "0xc671086723929e889340d42392545c10126be24c357d509ff1c6c1a5f9a57d4d", - "0xf4f1fdaf74870f153ea8375391b3c446bc3cb9b428a3426164320c720d755daf", - "0x32caf4a46065b4094869812b25138ebed9b9fe17e27bed2f1e1f18485e29a5e4", - "0xb62d0e1a873015ebd2696616378f4f3f39c5e9396ddeb750110b7ef04f189222", - "0x410c675d1f6583e8364939108dc46223cd9835623473ca18dc333a4b86e69390", - "0xffd66aabe2768327036556eee6fde0c9bcfda2b13280decc37d82798075a0b75", - "0x3acda6ba4917e64e977f4135e8c74e9091499c212e6345c58c399b4dd31d6fd3", - "0xacccfad59b3f59f16431348a3078c1dc0b662876754c1f373351d0725dc1df84", - "0x3a74d9e98aff2b6445b9776cdc21c0dafba808326d917f47d57250f150891bc2", - "0xdd4e0bb3b84db7b6e16783870f7c9bc563cb1576834eb9175721eb9cf75f748d", - "0x35e9a92581c9d402a83a7baa67bb72fcd0d9b3f3d7b1b7a143234fdadd2e67d6", - "0x64ead38ff01f1fb408829012a033eb847aa6ce3e9b99dc5e866338aa9c355b4f", - "0x78607406421e95ff55b3b952d2b4846c8b2224b2a6b46d231482ccec5de407e0", - "0x04fb332ad235e8ec13b07cf30b1c1cb6ba8aba4031f932868fc1da5d82c03dfe", - "0x4fcbf75d2beff58cf1645f74c2ea804c29e1abb6ed8d33eb6da0f2f7939b3cc7", - "0xf58b87be3dde47b6a9615315d2eeda6771fa3b31bdc369ea706f7769bd857af8", - "0x7aa5b0a30ab24a79a54080b1b56befc3c88719219ca5cb004cd45d4ca1334dc4", - "0xd2f1129f7a6a0743995aea48e9c3cfdc5efde47398e6a67a634c1c9865b15366", - "0xca65cece85c379cfdd49b9ffc5ed7e9b8a623e06f99bbec608fc3725e4a1429e", - "0x2e6942d93dc94555d948f3358d7ffef3774889455de4da3565ff883a96a590ea", - "0x063dc2dab7f0c558eda8bf9baf3901e1626416f517352760180557bd45014f06", - "0xc633fc9bad22bf62c85d4ce1c064f3ec161014456c4eff5ee9ee197230aea32e", - "0xabab885f2c04a640062289106e7bef857389db6db0a499468b45f5e8147c5f14", - "0x96eb606a5d165dc66e5c293fa139a9e8e28fb1ddc8515279705afe7a57e318e9", - "0xacb239d8e12502e378326dad3b338700d359095129411ace179e5b921c07e731", - "0x8cf7148ce70d71a754bc04049ae797e930a9b16bf4962dd3eea2f13ac6f75776", - "0xf39d2ae39131332c16508e09eb9d507f4c6bdaa284d1f4a9315af8f6122186d0", - "0xcce4df2d5f9484177965c2440bc54d1fbd0429060e82bb76c3b90f444b5246c6", - "0xbe77cc749e5f436a3b76360998ba7e6e263d39fcf792a6dcd7cc4ea18c836990", - "0xcde5a6e02589515ebcc307e20dcbf163ec85e5692ad9542bab23b43de55c487a", - "0xe7977eaf5cd7cb8c7f4e86a2c3813d52943a12baecc76f2b9d1eeda569c29d85", - "0x1011e0ba87c36df65ecf37d2360f98e0d7351f27ed9199fd7df166806332c139", - "0x9e481944f0e07b91eefc66693b69ea71c852552cf8cdafb1bc93336b971d6989", - "0x2b4d369db84d19be84a7af630b0c8a4ce0974418f407463cad8a8223b208a82b", - "0x7cf34185f5e8229b657c57c3c775987128434e909d72d06956515440678e6a25", - "0x5956c7ee723b161a0e7d74c69e57c96ad28261e9d7358bf25bbc16560a8292a5", - "0xd9a932e6f59d547de8699965c4e5964e0f663b9fd5bb9f6dedb4417d356f234c", - "0x59f2fae38f9ae4b74ba9abc5f06d4354fb9eef65983b1c8b1b025720d1b47d51", - "0x1c418c58dd888b1a546a1245021b52e83b1bd3fcbdcd0fe9bd643fe05fde502b", - "0x0db806610e23383046de4a35f8098597f1889e4dde4bf40a244e4995324b29d7", - "0xed51baa16beffad522b677c19935f07d7182b0a4bdb04330146bba3d8e8f80c4", - "0x2ecce838cf6ac5d0589fea00bab4dc9c7b107ed7c70886ea6519807785096f23", - "0x8df70aa241b84fd616047258f87b04249fe23219808a6e14f3d9fd0181c2f352", - "0xddb9d88567f42cea65b5913369c9791522b4dc4a23b0afab2003dcba337a5595", - "0x2e276865fbc34e7035cd50dab4b9b72c980abf8f7dec50988b86995ec57ae497", - "0xe82487c950c00a1e4a9ac6d2c3d0b80584b44dc27d28d6352dcef8786da3a7b4", - "0x724b23460aeb403ae7f9b3512a1e222a63fe721a2ffa4e2f4f3062da8bb5c224", - "0xe27ede30590638e194e91a392993fd9a9a1f168e32e3b9144d71d46e507d716c", - "0xd29403c683d5b1ff60839587e60eea5a6054588903e7c2c0b6f9bf030bd0d659", - "0x4e9464c5a51b3f2c6a829137b67e266ea6381c069e1701f0bce7ae7742e9e49a", - "0x0b107abcfda0ec094a6946c6057564f60eea9e6a55b2c762d4af5a7ab7899c65", - "0x5f908c956864addc2b57f01074c567155bee41412da6d63ba72971ff2eb8114e", - "0x9c00e148df00d9fb2d5847bef0726a48507871baf12be51373a7bed2a0c43f70", - "0x63a2fc58db6de3a2de3bac57255503791c4204941fcf15549a7839ca8ce26efb", - "0xefd321eb79658c3706f6f2a18663361b3adda792fe9064de3785acd101535223", - "0xa73176de8f5b3a5bfe87273711cc35a042c191aa998dc5a67498b0287e54be82", - "0xb8e2f03d134651957c31c47fff8cee2998738a603267a12f665f577a450746f1", - "0x3276e6d7618e898d6a8e1f014ccf9e06000f7cbb279dc9d078dee38f93231d3a", - "0xd23b5d1399be075454bc8f3613377113a7b65b29c1bc1a515f9bbfcc30b88174", - "0xa577feae921d39efcafe8102252452202fca0532681c2967c06eb63d00d83294", - "0xbb2231f4d795b6ea3894d802ad5f303c19edd2a25dde9c3a21ed1b578726b51b", - "0x81700c9bee6a014df24ac76eeaa2a378ce7febde11903e62a3d223ef1eec39fb", - "0xa1e05fe31cd3a58a3c11cbe47fed738e0add5039a8f97275572c7f66431f3d6f", - "0x8b5d0ea77b923d4faca65b07c9621467f2c891a925ef07009353b9e7ed854760", - "0x3566b43040f753d8a18edf7b3c89d44a2a3565aa0280ec673718ee81b82df24e", - "0xdf0672dd1e1bdaba83c0abd1d7c41d420ecdcab0019cb89b3dc310bab32bc8b6", - "0x2ef7e245c518654108cbea643f8b050257b9d3c4927493671a3a094b1dd0674e", - "0x993a3c5fe23e9636f525a4e5ee514949c5113f3b4db21ffc993a8b6bc3a55332", - "0xa023919b4e8c34cd2a1c7dd1768eb8e856ad2a1f5b101c8fed88257fdede3804", - "0x247fa5256cd95b9cc4431fe13466483f0db6707bd2453b6374934e1edd034b18", - "0xe63370f2acf5893c39f3728b66907ddc78d9dc9947db14ed6a7b5a6a876c69d6", - "0x397eb84160515d8b2beb3d37d7dfe167fb907f8a1a6cfd7457b646b95a18e9df", - "0xd236e18de82fddf7cb7f6df91a4d08fc98d9aba622e50cda7499261d01d9bb9a", - "0x76a526408c824b6c9e8afc5a0d5e5492a71f30cee5d70b4306c2298d348f0caa", - "0x00525d5481988d4c4836dadc936aff84036d99aa42722057b35ebd933c61ef3c", - "0x94e54fe2e05dd7d7e4026c0048d5e64e5314f235607e568cea8c26bd31dc3f51", - "0xab1e0c92b35d49b687188518a7af63584cfb13a74c3a9ad800fd8e1a4541faa7", - "0x5e65f148c1a914b300aea241f07de5c43709c38d279ed118bcb40ab9a3a09226", - "0x9fc8a8843a0eef62d8c16d49c70cbf2f3fe84cc155cb741d647f17e2a664e7d1", - "0x8257ba8acf906e46b7cd0de0d7463ee96c62ccb7dcdbe0fff5fbb6bc0da28d74", - "0x2a57b487f4281cc28a5b107a643944796ef2416d7989ce879d06bfd6d4caffa9", - "0x0bdf8efb30c449b26d5c574000646ac316aff27fa49d6e20420a08e441ed5f96", - "0x5d3b67c6a6324936305f405067ae70a35146759278f77793e3f7c4b90ac0c7fc", - "0x156f094425a09a09f394075d02f5b5d5aa6794bb5cf62d824ece1c57d58ea993", - "0xe31782172a116b92ac0ea9de1cadb81c1586e05d5b3993f10f8f695469ff9e81", - "0xd8f85113147a72b1950948000d75d6005ae183997c798fbbf2dadc5c439be9aa", - "0x9bed32bbf1f626012985392906016db1c993fd8ef351f8c72e2ae832a2efdd66", - "0x1c6f7b17c8bd64e03d21ca9b6dc2df278cf0b8c8f6f60c60741659fe5b34af81", - "0xe41e4494cdfc0e92f16fe6ec78e19b9d8cc3db73700a1bb02fc7670c92e4df2e", - "0xca1032438230b4faea930fc0689dd966604487a8d3abbaf21554b3e078547fe2", - "0x2bb999d1acc6cc7449dc0cb965c3bdff42e49b8ddb09cde885174c45730bc2b6", - "0x4601be8295359bb23f6f09d7f053c637ab2ce7942b762ac55b1da6d312a765c9", - "0x13fcff0af19f7f11af35b360a0199bfe610a09864dd1d42b0a6570bac17803d1", - "0x31b567fe7d9e6a9a3c15c449e9aa0ab8f83a453beb2784132f25b0919abfb428", - "0xbd4af1360219156ba336fba9cababad31ee78e1393061e3ff62f7ac785c41f19", - "0x2e15f44010ce7a966dfd3fea57c5124c120cb60d80a635910c0b2feec59fa977", - "0x68ed80af93e72f7fd9198d036d8b995e5baeb0f97c6f2734379116a5814b601f", - "0x6aeaf5d0decd1a5c8eef52435bdad2a60cf1cc5f79e23b90b7afc0110d830a3d", - "0x349b95da9a3c65e1d34533bd08f8c95c934c49b7c20e6c7e38ce4db60239c94b", - "0x2a0053d5a61602e26d932534d5e9a753f75642edf1410b3ffd61076d27d5ec97", - "0xca53c9d106169cd3a7a870efd2c2c7118ff953b8aea5c5b9896dc2ff9b2c5d59", - "0xd227181ff80d67f95f4fa11f86b1bb5dfcf37385262d82d6025bdeecc6bf00bd", - "0xefa99cf897198be702eda71b3fecf4cc12a27e7c90a258c4230aa847b03e16a6", - "0x0788c17ea693a049784eec30b17c49e66e7a71175893a064db7bc6e85a0b536e", - "0xcf2bdf91c2f44cae1148ae027688f70ae27ee65b8c1159cbca3e41ab7755e5c0", - "0x32882f2cf31033d40d5069e68c799f3e3297a9915287fb317bcf657e078fe6d3", - "0x3401436cb69ba8552d4731fb2f734a9a659cb10ac666f374318420d3e299b98a", - "0x988943c9b35e50f6ab03dc275de1dc770aa32574cf9a90dd0c13e7263b46c129", - "0xc282d894a7e4346a492fe495319c5cc9abf0c7f95e28bdf985710f692c770520", - "0xd7f8e14bbf8c6061a0927e107f53123f78448b878b9562cb8113b5d93d2d9142", - "0xe2c995419467eb379ee043399d89bb13a7d316b9a680f9667dc9defc7fcab80b", - "0x567357333aa3c5cf6b1ba20a3514ad0e21eef11f5a5c4ec999048abb78c5ab7f", - "0xb9f31c6c771e610048dcb0f4553629481b6d243980e1ca9ec3d1400a56ef452e", - "0x924533f9da72137f96ba97b39a95c2c369dba0bb09658aea3a387f3141b5a34d", - "0x2f17e624e0f3213c2d953107e72aca40b5f764f4b31278c8ee32a000119aa3ed", - "0x8d301cf22181c65bd6db20ca01df6e3bf13864f88958fc04861c295a31b9a86f", - "0x3bdb760c302d348d16e634c07f437f793b888bc15fcae52c371675b7eec2e241", - "0xaa20ff689413c62b1d9854ed1c59b1629e9fcd9f99512bee40933e722e4d143f", - "0xd9d0f8cb5b86492abc172c0460cbd819d13c05f055cc1306af6285459223373b", - "0xe0ec50a77f89631ac2a1c38448a3e5d8ee739f1cbcb542b23116005ff0726679", - "0x90238eec2af4ab1e1445ab94f422f90c862afbfa8cd6bec154d2f6f40721a615", - "0x5c2350ae9bd5f2e2355c97b0340e96621bccd43e9a7e4e867ba1523f5a5178dc", - "0x896a7844bc430b330fc094554ef21c162e2c5c890b4525720a612c9f16bd80ac", - "0xc2aac353dfdf9d7ce52240f6b84e8fec9e66d4acbd77402fe7218f55fb76e834", - "0x840ca0ec7af88f0e26d3b9e54473fdfd038d5395165b22a935e941187d529136", - "0x5e1274869d1d7f072dcf83d631844ea7e750f90a6183262622f2db72eefbebc9", - "0xc217f1324e0ba4e36cbc896c2dd4418ca5bfa880cc64bfa913d6212cbcfe8a95", - "0xbd7893a4798b0838629ecbf21a8cea963df8920a792f5282604b59c536ca91bc", - "0x85b22d6088f30f1b262369f869a72e1354036363f70c8778b1560658abf75902", - "0x829935ff52d7148a3c5548bcfc72b736b184e4f4661b251e660313895432d66d", - "0x4b0d3238882111cc0065b1e2c9a71eeccb34876b4ddbcba989a47faff0f505f0", - "0xa619fdf363c41d69d2cd4c8063e3abedb18066b8cb6ed4fcddd99686d684f450", - "0x2dad790e3f5f6be580f97dd37b22a1e80a6555f41726bff0639aef54f0bc47e8", - "0xfe2064175fd61af4c646ff0745a79ad689d9be2969bcca0939393603865ccaa5", - "0x8a18e5e22dd996798181fdf8ae77cbc7929e5bffa0145996b00d83c5e7829c6f", - "0xa7adb5fc7ccbb132a8e22a861cb5e3e1940a4ec7c5add1bc767ecc668d1a84fc", - "0x7ea8e25d742691f53294dbfdac087b07d688cc40a4a2de28fb398b2516de4bfd", - "0xc162edab705ae176b26b495f7ef7205552319a72d56c5ded30be9770860d6530", - "0x5406de06f5e2e819ec8a7efda23e19cde88bf8ea9969e3e3393f1235c4be67ee", - "0xe5a6b4f946655b32549531601ba3571fbcc45bf58a7b18ea24b5f4677fd58815", - "0x5bf157c76ea5a10ca8240c44a72450537678bc949f57cb9a748b445907180d8f", - "0x4f2f7c966d440ab36a1ebf61ecb1a9b1ea4ea6227c29e4fc1214a8c63cee7d05", - "0x81dc3a0dcd5ae99dc7bcda8b30cfe89aee00094258891d445425e8e304a71f08", - "0x915c3f6e8b37680ccfa5dfa941ad2147fce2f99842646a7835e2d45e50e4d991", - "0xe616568cf1a281f42a32686b65dd712e2fd003a84dccbaec32b890440dd206ba", - "0x70526aaeb8a25dbdf93b3a7cf9cc638292f2502b7bf78125e88df90335e594a8", - "0x6b3e974c6a004beea5b38dea401870dd2a68c2902ea8b3ad5a8358e10f04eaf9", - "0x99b17fa583f9f80f81edae98fc54bd5552ad44aeeb6123e99670696541cd0442", - "0xcb13a1b63fb6f0b511876a699ac017aade97d8dec84249680e7e36b3729d9744", - "0x2bc84717485fb9eedb00647c8da26e201fcf05a0b5c42e2e03072ca130d1e3c5", - "0x7bd8ffc908159c25a28a3dd97064ee7a76bfa40628ac54066f83e904596562e5", - "0xb9d16b733cdefe95eed9fb024d5737f90723bdc46260f0aebe2adf23712ebb05", - "0x0d6a943a7bbad9fdbd107c4a1df726f7aa61de4bcf1e1c50c8a32d56828e8c72", - "0x7a264f356294d00e55ef0dd56e8ba793550c4366347d60c7cd31176d79dc8486", - "0xf52baa383738251b1085fd6aebdd0da48414bb993fd3bc427b2a07a6ace8d39f", - "0xe2958f9d5bc0f1b23b5a3923d87567a58dfa31cbc0a63715b76c501bcd022f65", - "0xa760fb572f3a44ac43c0cfb75becf5bec1d7dab5e33222050b95a6cb8938b146", - "0x004437e7463a43de8edcd074a4c2f03ce72e4716f88f03567a50b6440b69ef2f", - "0xd7268e9610100bddb2188bdb0f80ffbc479b67bda2ecbc5aaf18f947667f3ecb", - "0x176ac5d88148cdfd9049d3eca742750edf975c32e649be8372b15e43ce162d77", - "0x118df9c7631bfd903fb46cfa867c8ef861d9e828e6f57de585f17ad351819e25", - "0xea084c972d69e9ffb61665a27fa4f2d0006f0e220007c0b2e4760a95d68c6c01", - "0xd6e2a8f3df290a204efcadd3a34bf57eea6a3980fbb68749029660e661282f2b", - "0xc538e4c7b704fa8c1c123f8baca05bca5fda28df7ad7fec099638a4a7dd4fd18", - "0x419823e612d6a0055fe460ec9593b4fc0298ea94e0ce838fb302ff1ae033bf40", - "0x304e214664a6703ad14fa493ed333cd12f6db8f4f6af1223bf00a348fa3223e0", - "0x4de744c5af198cf5a0048c4a3c9ba578ea3478deae31531b87eba2a0e65b6fe6", - "0x523694986a7277170342c7a53bb710b86a432b0a7a3d96e141fbbbc113ac2e88", - "0xb81dcd531f4ea6eca82860d7f45063ca7de742a2fb41d7b6c1d62202fc1af17c", - "0x205a95c1d7e7d2cd684bb666dbaebd2533f4af28837a6fd2f6a7d4aa68816dbc", - "0x7c48dd0273a7207ceb5a8a1e3b33352f6d438e2a7b45d4c082dd85135e21e5e3", - "0x0e3edf65e5a7502793a8f139a69f3946d567a959ec411088af96da979047675c", - "0xa0b8886550aad717f1b5d57519a4bd82bdfa30e3c8ba4ff81a64be341d828aee", - "0x399f2cd7012257b4991343f191aa688915b4375899ca9519630b5ec6c62a8b49", - "0x56068dc17a965fad007ec7f23dec7d396dff33b0a60c4e9b8d0f75fb26178f05", - "0xbb62f47822b394fa87d70cce239c9e57464157fe2012c2c37e8f6b69515d6112", - "0xd6a068fdc6d4cd90203209a07363ee1eba047703e50ce9b06dab471e0dd1a037", - "0xe5a62c74d81ad110ea0ec13847fe64e4c3e8e48b1eb053f1e381abc0caff7bad", - "0x8ca95a015ea27a649b0bee134eb170f01b5c03cc45a8bb2c40557c1598c3892a", - "0xb124e101738d541ac61b5666c0fa19375417d946a2344ca40baf308fe8449a98", - "0x1b9543937289ca48720675ffd354464fecacd8b251a3f267f42906b7188f184d", - "0x0b48dd3eec04741e0f78cf61937b2f263446d4ba269e8c7d4aff9c00a2fafced", - "0xf72ffe4699fea49e7e67f96fb5688247746c9bf25fb58e41f02b0dde4a8003f9", - "0x08328b322ac26a02e89401565b91a3dd9abee62fe991744e2ecb2458c6c26460", - "0xe9dc223fdf695c9b5480de56cc91fa640a4b73a8cfeff30ecf4db4d36727fe21", - "0xa83123655348b15b947d83c14a241d8e303242614b25c45044fd1a1ee28d928e", - "0x14ac46b1f556ab33fd4199e5a46e0584d00bbcb2592ebfd4d4f66efffe31bfa3", - "0x71ad4b7b5605aebe1059ee484b5ec791e9018b50f92ad2f8d4f3c1548a99411b", - "0xcdde5fea3fec154058e16f7ef877c70404b577cd74f98f7dc45600cb7b598b04", - "0x330f9d20ddac508dcf0e08a4c1eddb55cb5d7f645c13f50b3683a41bced9e244", - "0x76de024aee0b03e96765dc6d7c71acbf253e962b4f3892b94196487821381ade", - "0x1c37a50f5e3c774d9ea4438a629d497705c3bae728e8e89608ee68d7cd2b53b5", - "0xe16a37be64f82bfe54595c9c51625ce3b69413b70e0b119a04e79c5b8d593c22", - "0x525afdc902879df4c23b996f34a5387fac6e41300e1a7cc9e24c630a1a140653", - "0x845164906086e678bfdafbf4719cf8f80d988bca007aece4b031c3ca5df08db6", - "0xa4d4bca2c51e8118d16a4b8ed33d41a73c275bef73072a6674005b9ee7bbb793", - "0xd0759f637aab37f4981b927e688f3e51f17cf90660931ab8bdfa40363a6931df", - "0xf54986144fa83bc3953c4a1c91eebd6a4abb185ab7b188cf7492a7ca430b9130", - "0xa31de426ecf5f98c1381c844e2fb7c959ad8adac39484d71e3bd07d3b1094118", - "0xf64fb1f4de6f01317e242b1198802191a6cdd39e1580d356b9e5442adb69c8ab", - "0xb97a74c8e47d37b3ce7c12ba09b53811f23dcaf4a8d260dd370f45e9ccbacc3a", - "0xf118ce791474d987b9a8d3290be077e8316a6fa3bfcda3600e6c2698264db37b", - "0x547fe45b0ca517f898e6a5fd22929cf533911f505e15de083a5685c8eb1ccd0b", - "0xd74d307e3b5a166a64056f07eb85f60fda1d9c17368a96afc6f8141141f85c86", - "0xa315936672e2737800c6d51fc22b56d918126ec329fa7e1ba5e31af36ea4f87f", - "0xe928505d97d673588a68a08ba6e2a4bc25843592598988c1d168d5c938e230cd", - "0xa6bbd3b96bd66597eb68c4059203275421a529c61471a69cdc1d2d9211aee400", - "0xa46efc7ab47e7a0f57a54203a039a4339d992207b31bc707eac98ade8e8f6fab", - "0x46e903455140f009d1f7e9d224ede2e29d1fb3f72cebeb60dfa64ff059b7ff40", - "0x1eeba50e5530be85464456af3ea0ee38855276ba79bebfe305e49fdebd0dc97c", - "0x30e149bba2c3c0db9038c39871325392bcb41169106131469b6ea7994c5d8d03", - "0x9503edfe4cfb20c38ba2efef043ad824f2b17f39e2ace445fd9ce73caec93c75", - "0x608f294da5e0df707b97c4949b775f64ef76c055459c26b651010888cb888037", - "0x8eb5960dc8e49951c41505ce34160bbd588bceb74468a7dc0898c6744d75138a", - "0xb020dd8cccb6beff1e7d996a740b876609a55ce2041ec0e9f09f683bfb22466c", - "0x9e8333dc01746341ff7181ae95ddb75748cf3efbbed24f3b16885efd0aa19da3", - "0x2a97bcddd5f0a7245c520bbabd77e48432c39f3cb17a0241be9c62ab5d185a1a", - "0xa81cdce57af311f79ae16dfad6808b40e8ece16115978131dde0f3edb29aabfa", - "0x8f321249562fcb94c94a80679adf69aa701f0719515753932548a89a28cdc7da", - "0xda37eaec19bd44bb1892d12d842a77ab7dc5ea43ca0a1a92aaa3ffed0ba9d90a", - "0xab8c920add1f9e922890da5d92ff199b384a5f042f5eb4e3fbe565f5ca50ffa8", - "0x9f52ff4d39142c046effbe6e552500886200c9c3154dc68abe0f9e4bdfa9078d", - "0x1eebaf84ba1e6667e825716be63ddc71a963688f47227e0d17d78144c820e17d", - "0xd2c2c19eacb26695c89b51b75fd616de5c769d0c7169794057575e1dff3800f8", - "0xc5ad683c4beaeb409a68cbaf81532e4edce6f64b60853725e493ffdd93e69f7d", - "0x768303ceca362b8a3996a4823c4d64ea4a4ca5892e91ab0433f2917b96486806", - "0x88e25c3ba8ac976c0382344f4a8fa310bcfc7f51e09efab4faaf222cf6dc5d67", - "0xe05f9dc2db75ce9b0fb59cf1ce12581cd10910dc5b91d744791f564bbb87e176", - "0xd75fad017be9f908c431987b10f25526a9c9d346118901d7e0b84e74a44724ff", - "0x83cd50c6634e2edf21044fe789e82850667862be407faeaa90f9bc3b0c60f9ed", - "0xdf6b6b1134dbe14a48678610c630ba5927f3d75d45d01e56534204b87ed575e5", - "0x3e486fed8ba04ace837ba81d61086b3443ec22a803cffaca18e10e0e0f1cf679", - "0x6b7c8f5e560ce6b6eb94ff8e4f811b06f2ba91048ebc2440e25f2c44eff4e637", - "0xa7a92f9396380b5ce01e3666acf035dc80a297448e1b95530b9a8bd9eb19496b", - "0x7df4150c891991e89a5938c12bcb4e4ca1a3bb3ed17e1d14930e78a3b7287cd6", - "0x9cff0374f59c5c0a8b6b8db636387e0d032922e34049356ab00ffa3f2cac8f23", - "0x85a5ca8ea29b19babb6aa45861de2cd9e44e31ff13f7161ead44139e6b2de019", - "0xe418e0eba34f07d10ba270bf4316b78b15c49d2a920f3370b403dc584cf2bccf", - "0x8500b89c737ce038eb99a443d4ed6dd02e00ab06c836b8f03de3e713e485e99e", - "0x0f324429900c3bb11cf6a0c4099148af93d8953b748fa8974c7cc5d942f6f36d", - "0xaffe95fb50dab84128becf268413fbf2c8ccbc2fde2b6f6d8bdad23cc4ed9312", - "0xcb93bb3ee92074161da7d6345022f415accea11b20c412cbac48247c64d099ae", - "0xb5044dbe63680e5fb8479f899caf746b778524a6c6ef3f56614d3b9832cf0006", - "0x1d9eb2e2fd6d8c78a5a4125733cbe69ff25b95cba736f63627bfab166f736c18", - "0x4bcdb35310f9e23c6b952933f3adb4adcf6b43dd88dcbf80dc2d9ed4f8cea505", - "0xf934917c926b2c975bb0cf0ee0eec66727da942e06c65ecb1e168c015f957a65", - "0x7375ee9c763fddfbbf6fe47b1db34faded65f5123dd92ff469d03a41f1fc47d4", - "0x6b4e2a1c3ad70f07688662e228faa861496294b068e21f040edf90cf9d0230bf", - "0xa64458a7d295ff66658de59d0f166a55dec1f9e9e2be6fc2a8ecdcb77b72509f", - "0x4e50f8712edd21e913834cbf4f8d6baf9b371f0d4592a0c6a11f70a8e95ccd26", - "0xa132bb777e81aa07f8d5a6696e3ec6c800a1c0743a946618d04df4b0668f1c8f", - "0xa552543fb7ce61cdaba1d077338aa31a6b69cbc7e6af69cb5443e2d4e1393cf8", - "0x05c451fa47938025e4fb6835d09e9a92f2db33cc99e21b44b98a1f3b0c6071c5", - "0xac9628c769038d220a38801a43aa1449c24b2277df287b04ebbb4082d1763f7b", - "0x6d188b8c97ecb2e45ff51e573fedeb95083b40c8ae15042289753ae65caeb681", - "0x0de60d18d4f052a11a584be1c4595430ea8f5c943dea397da9af2d27518e2646", - "0x02c7e9849aeadc3011c2d36d4f645aa0d3f23a27d19a8b7eec61e2ee723dc675", - "0x2c974900dc848a8a0eea1e248aa0e2469037038bf9c0768e6e46d2781530ac4a", - "0xbb4b7b7d9ae81c7784f6a8e7c309f30a9794f0240562ab889d4113bb1e775697", - "0x463ece715ed0458cdd0f12cec6ab023fb3a3cbf6eda4b55f3b4115921d20df52", - "0xd535c260190be6f755d84d8812697c8ad8801533b939dc95e2072e2f39066564", - "0x7f3a0e061e646047bab6b8dfa88b092ab003c0e84240dd82f6e7d407bd5a3bd8", - "0xaf4803ac72aa8419999b383e5765544cd61b5ca0071f7dce952d7c24d89ff8af", - "0x1c53d5aa090bb88a8302fefb12080eba29c2f9f198b08b073587049159cf4245", - "0xf71ee084795fb52661d5cb7957c7e710e6171683aa691412d5c816a764f61f64", - "0x9eb207ab5d1bc7d1a44afcb2790f32d7c41f1c54a17b277dedcd8ba1e63eaac5", - "0x2ba6c0454e857b17903fbe79d6396a472216ddd825b81d70c696074291e71b1f", - "0x899f295af86c53f6c27e75e6cb98cf428a5da102be6dd8a8e2e59b446a086a18", - "0xcda6ada90684f614c967b6fa4c37e6159522e2dee2fc8663b6a8478be5238165", - "0xa59f5518f36b97d9fd077f736de67de7856d515f5146d5b1ab1b2a36112c6400", - "0xe7265df96658699ef8781fba631750ab18dc9d62c0b24820e0538809d046a2f9", - "0x4d9fe46ea3b33b6e73cddbadbf8bee4aa31aa830c4a1abbba4567f21f78a014f", - "0xf5d3147fbe7420f7c55f6b06e045ee7d2b2e0ac7f2040ebc8183ef24108ad70e", - "0xc8dfe6d026040fedb27da79dcf77aaf83fdb008792c6366256d5988e356eeb94", - "0xeca183cd9a46eddf04e02e0a82fa295b1f56322e5552a7c7f0aad090d209b9d5", - "0x8e26a0ad84d0c8b5e64b8a57eebf3bb86bdd02e24f24ac95c6222959638a3037", - "0x5b1cdf956d672b244ecc6bd5a828cec7b80867a4ea2a547cf7240e39793cb5c1", - "0x48c8d0fa40f9f6a49fc6378152972b85cb984ec221384484148231ef88cc3a97", - "0xccf14e2b03037c17bd6d47ba41460aa54c826e945c0132692d08140f156fd115", - "0x331791c82f8717e22f27aa77b7530acc83beaaf9536cc74ddb90533327c5072e", - "0x6c621acf6c972322182e5431049874a5e78bbaaf1a3f49219fae2c8425d6b5b1", - "0x5bd86b4984df838b2ced1dd0f3e37ae19d52ab1002a8bea859065f8cdaf6bc38", - "0x3143ad07e92a4b2a24b6482b5502ce98d50df46923ace4845ca35e0948090363", - "0x66e2d61040285db2e895e1aac42b69b41f556e6fa82116c1e84b49e67478abbe", - "0xe4f3a6c6761a3e17a5a978c69d0a6688439d4d6de3bbafd2b830b5066d809e0b", - "0x034280d368d7bcbe1f1cc63bd551c3ac2406f612f8f04801d24f98d89e75c444", - "0x2b9ed64e419ea19e5dea807dbfc08b8d21bc13903e8888aaa15741c6c0481440", - "0x72630a380871aa5ec67bd06bd672fb0128b95499582be5d34a4073645d15eb6f", - "0x9ee6618d35b3fffd4abeee984f1e9f3fbd200e5f60c562cb0ae3200b8963f522", - "0xbb40172754ef37505f4ce8077382eed0c45b0f96c882ac26b7d209b951d7cfd1", - "0x76e973e45252e97e2cb31bb8b79424e5391c3e76893cc7be0c1c1101bbafb57b", - "0xa4218614e301d35b94bc536160007b9b88acb5c9987a44a27c514f6456b28be6", - "0x0227fa09131444917185f2cc802ed389687a4f968f99bb221148ab7effd5fd5e", - "0x0a93fa4f9590996785fd599bf2669b290e8eca7f02ca06aee9582357293a8493", - "0xe1a277823bc99e602027fd18db67550f0d5e60045b94bd2655e49531bb152f0c", - "0xfe8ac01e4e478b4019dad76813449f8155de23b4ba2a6e2091f4c8ba82508bba", - "0xb194b4f530b832a2ecdc88c1bf40580474847b91c300751c810bc5cc408095fc", - "0xf1de598992e61dcc7bc9e1f1490d80d10024c6319954aadae6a4df4ac54ea564", - "0x38138e224065ad8ed4af813bcbb35535bea444f99613d686f0ef4ff00c5a3fe8", - "0x13e6a7016f28c856d777e1a7f54bce788a3e21b129cc1b57d8e5ec21e6522d3c", - "0x1c5d57d845f18557e61b1b3761d9452eb26aed8041a4a3073097259546e2045b", - "0x536848934654d65ec83f7cf9a0f69adec6ae974b8e5944127ca819a30a20b012", - "0x1653d5a4610454d67236e8049dcee0b1a531e25d5c7f0bbce913a6a524a60db6", - "0x7bb7bd63c5985b492c4897d59223335537ed0f8928c5c7383b5ca7a6fa3c3cb2", - "0xe4b8e08d3444b8e14e390e219684a6085fa335d11c1c77c530582e2c3c4c8e55", - "0x5fb6b09c1ac68c1ad0fd2041c27f1fad5a83906d4aea5af0f965cf36bc3cca3b", - "0x40034a02507258439d490a5a282956f9ae304b4ea4730ad6386005c767eab5cf", - "0xec6ae08d9c0033901514ef409a7cf68e1a682a202dc97c5c467259c7b6022e2b", - "0x9082855abacd99a2b634ad1f50d8517b0f1b091cf5c649ba377aa3ad2eccb65e", - "0xf76c0b9744305baeee70b1e274f7eaaf3670b2217fff6655bf69968b5acac517", - "0x917198a97cb8f704471f6500aef3e6f014b09e8da719ee03e87f45c95605f2ae", - "0xfe1a48d29d6ddaec284dc0bca96cc874ecefbbb91b74c8ea6030a9bf80470c73", - "0x9ba5c60cee1a7d901f9832e3024ceb9db24460c6636b98df81d008d1678073c7", - "0x7cba18b1ee4fb25f1536708ad3f4c95c43d60930f67dc2cfaaffaee2f91fe1d8", - "0xbd2b278edd0e75c6a39aa2f43b334d8cbb52900c152b2b8e3ccf7f27221c0643", - "0x65c3a4efdb5a189b46db8b8ab60968f6c16c1b56f9f2311b3380c275b7914383", - "0x45de07d3860d9be6f7b90dfa928cdf0da9592c2054b00b774ba7608f3d99324f", - "0x91a4389a9bd65f3dbaf092a5a49f0dc2333bc5ec7b59c3a48cffe97ba52213c0", - "0xff9342e526815c132251c3202d20acaf0012be44ac7c47f7640f27fb6f2d249a", - "0x48854fe3580cd19aee63f0eda576f1af518f3ebfa9f87d24da87a89ba227acad", - "0x8d292c2072d5ef4f3ab12cefaa60e9b54433a0eb7303ee253408c19394345a4f", - "0x48007c06be01f79cda41ea5791e3ef4bb4e946d7af262b52cc74eb3e20e98a1a", - "0x36a39e5a3035ed57c8227571b2d57317ace61cb9c66dddde57a566d34eb7d894", - "0xca4446f6cfc51f82ded4f4c798da29fde3a3bfa1f30de6cd721ab459e570dcb2", - "0x82812417102865e04fcad17c1682c7608cfcb855267b4ecb8c1e0666537e6a9f", - "0x81ef7d9794f8bec6b04ab899cdfbf3ff735eaad06610258ef7f90c77c83d5387", - "0x4810726311224ee42f1969465daac6f12516fcbbfb26e9febf221fd8d1cfb41d", - "0xb39a309cdf26f5f2011a83a0c1840b61cc5bebfda385a479a6a20d872bd082bc", - "0xe5002eba07d8b69ba9470514d286ab800d28b443531349d641d9df57c3cc1399", - "0x662dc062bcbcf4f45759c85e8a0ff833564beea1ff3894d7c5286133b3615a9a", - "0xb2cca2f7a62870673c70313efe23bfdb57ff9e6bfbb9e73948288fa15a8a2f49", - "0xd9e38758784261f67700452e23a59ab25cf22dcead205a50685d5cb03442c81b", - "0x039acba436cc8d99337c16969a076577a50d609cb2986f42702199e3c2dfd8e1", - "0x7141bec6010aa3d5ad1d543f91a8b10ba3bdab7625fec8a7d404238c09893e48", - "0xe0a2f776956dd92600ed94088b7da0aa416ee83cef35d37063d3df2d3442173c", - "0x7e099f882fa8909a3f67cd5950346a00a20bd0678e7ccfaa03395bbe1a7bdb6b", - "0xb52e8fb670f5386581c89d4b01b0bec691b9e828c9fd8916708287b6c1d49f26", - "0x16b599c31ea0a2c2c23e2845cea74c73fc892f16a2b34091ad9ffc5d03e7f609", - "0x4c04bcbce2e30cb9fa64e6c776c8be2a4cea04512c9d70e2a5d8a377aa77ba76", - "0xad1f20b7e161337e3d7aba00d9dc3585569cd8ca932329b3df13fe5f23574f70", - "0x2961a4a19bee2dee47d9eef2f875e03fb75bbfc30473604c902e76c76620507d", - "0x666c0d915622aba83912b23cf5e48ac57219a8e592f09f9e224af94ed35cac7a", - "0xfa6e99ccb3460635ebbb7ba53b332825f9d5185dd10bc1e2eff5fb6de4fba746", - "0xd3bc3b194941ea52e2b31ac5ea1c789233dea804e24c15000332d41f8bb4a8c6", - "0x07281b9f1ab9e962d46f05c76f29b4f4ff3db35baa777c37c5dac6cd33d49a90", - "0x5bf61ee56f75630605881d34c11fc029f7c2eb96dae86c7dde18af8e34aa6c77", - "0x972d651e4903b97eabbe5a58440bd248eb2ab16a536da686a707770b88612d00", - "0xc953d9cf8cd4a68d7a2d9dfdc3bdad8bd0d511f63cbb2144af7227ba49084a71", - "0x9a90ca2c4dad5386cd71110de906f6d96f80934661afba440ce194e44e5a6036", - "0x8dd2c27d9b327b1a6e2216f561c09c276c9db44c3a87e6f8fe381d7a906c9769", - "0x17362077544107f34233c1fad77f12c9140d0f0f0a5206dd887bb9cf627a20dd", - "0x52afd7ffe299cbfe5f418612ecf625ab1c6debcfebc6bad2569c3b83aa1f9f55", - "0x6597cfda3deb8371e447690b5f90df042b72095551d347eb77b2ee55868634bd", - "0x97a7278a76d38714f16a5b3bae0910b18d2851b2519ef5dff0a791f471cff90e", - "0x0f4124bc20c9553612a8e78b90c80c1341a3a290054c75326753be6a103dae22", - "0xa42039e0a42da736e91ffbea82e58beaef7fa7d88263764611b73ed4775e222f", - "0x13f6c5b099ba6a2ed4701a8b3edd76c2645b68d9c2cf9af924da39bbaa2c18c1", - "0x939ddea54fc48f8c8dcc1ca6c04ff2cd70fd8da9e93abd3ed062da56b75a1110", - "0x3e66eecafa70c79675f26d259fa6718c646ce3828e80652134b792f0366f0c68", - "0xcf507596184c9c4a14286412216f771f0495ea5780bc372d12ba6e8445c67d69", - "0xee64dc03e5fa815ea5e9e3e4d2c87126c8e4c0ff56f1152c349e3f9327fbd5f7", - "0x3b6ba90d23473ab15afa76d95b23b2a86309b0d6e2edd3492c374020e544c820", - "0x51ffa382078573a9bb41ad67b775074ae41f9968e2b38341ae9a64a9f1f74815", - "0x6c3a7ec03f93b7d4b73d8b194ab38a23b512a236731b5e54b8b5bcf44aa05608", - "0x0082c925bab49c6ab52f9b996986123039fec2cd6641dcae3e3ea7d60bd7a2e6", - "0x85185f7d346260f24bad3c767a04bfdfb3356f61138b9fbafd7af0f813e1ddc1", - "0xeb5a6ef56f2926a533d3858eee37cbfb5b37e8fb5bd181cf61448e36d8b48dce", - "0x700da1f1e9339d6d55c54c62cc5523c30901162216c2f182f5f2ea66dcaf9620", - "0x84161dc26196b99ceeea8d64f6ef3d127982b934f49678f6a512c07534e8ab54", - "0xca3f434ced785e9b5922b625e1d2457b156372e4917b9d6c8f4f6a995182a176", - "0xaf62ff81c29e9102077eae515015286765592d454c8e15b0a302adefe22e998b", - "0xafa0ed60bf7417e6f74fa7a9c10ab7c7b0b1e94b699569b0e3f70ab01b234ebe", - "0xd2cdc3211869e030c3e291f4fe2f163ef54b24d7ff0bb4489d73a5159ceb461c", - "0xd74c2a92d3c506bcbc11c1bec086cc419fec1d0bca34d50b74b28f89e86cf864", - "0xcc25ec627504ed9f00af661c72990a36a2f9ee4c4507e5fa4def89303456d7eb", - "0x8edd1c1dcd064b7c693c978152209761cd6c92e449e9444e9dee807fd9f9f420", - "0x660e691453e374d0a584b184a5fdb85894290e1ccc68964a8292db53ca096505", - "0x9cc340e9d997cc319fdf2e00c75d80b80953a90049af766b1a499846c5e08bc4", - "0x58e650852eda00e55bc2d9e024a49b3b7a5d6d5092d837407114120ff143b75f", - "0x5426eb6b6b8965e80dab787befc815c6c430e80231e91fee5511062bb21c3e4f", - "0x4bb9b4512664884f7b63bf27ecc75e0a69e32cf2b980a8b9135e410871ce45ae", - "0x22438f10926c874196f8c65e274d018960cedb55ca43787e18adcb54631df7ce", - "0x68aca6a2e6a51c8543939fd10be02784b1149923652da3f07f91999b75be995a", - "0x12a77edd8cc051f6f991cd3912f5f4afa1e797f5daf010ab17b3925220b12e88", - "0x0d3b929dc2733de2865dab86cf42684e8430c2ed47d19606637f932caf8d2d13", - "0x69f8d5c233a251c35851354b8546d89c7c54012736440c8965c256cc9db7dce7", - "0x6757e97aa449d7878cbbddeec490fd0636b2c51d5b00bb61eb4836685d005d2e", - "0xe1029b22f6ded11ab5ce46aa2594ca90006a04e8848f4152fc40c27735217ac3", - "0xadc4af259ba86cfc93384f3c0eea61a4b661f015209064254bc6ecbcfdb74f5e", - "0x02ff4bd0ca8008db90a121deab0c36e45bfc7acdaccaeae04acf3aaf51716607", - "0x9df6ebb38ba3e1d0a3a82f871bacaf81152bce32265c0eaebd9d9e6026665b43", - "0xc25503a6058ef394c48d1153ee2e70a3d8e86e0a46b29ddd0217906d694a71d8", - "0x83698b68d762e75476caa87411797ebc0050f9296ec35664bcfd3aeffd01d8c6", - "0xb185832a5b8fb209b9a6772673cebb3dcdf3d915886282d6adc47376a48fb578", - "0x32ec2d0710f9681d75819f1e2c28e135b3272dbf1da08e4d64ab2e6ae1064cbc", - "0x13c8aa9abf04b6ec2553b171b58356a1c256563406ced1ff3c84a3ffdfb5c1d9", - "0x80aaed5609e1b594b3c54403ce9977dc558ad292ec472ac73c8d7f39243e116b", - "0x8cbee772d46586087fbbc9b4fe2f54ce2405d2483572bda3c2314e79ca0d8deb", - "0xfbfe6a938e7bb9b05d889f8182e8963344df0ac7b9022302b451b036da261f9b", - "0x39131379b64eb550fa0a2574d504bf7de0219d50e6d55cc82998ac8d2684df84", - "0x5fefe2fe1d509eae61cab218f1bd03196b1e631f5c416e5f2c0e9e54a3d44d98", - "0x5839c1c6580a4c04aa09dfde45ce4616b6f30d1ee794dd12fc745b96b2d48c84", - "0xa75d2328c4e63acdda579000e6df9be72d9348c2001e1927264b1a8c303fd292", - "0x7331a0cbfdfdc200cbbd1723bcad1f1ca6e0591c1ee025bf1842d91e4625c8c8", - "0xe9153c020aedc51102bb9b22a2493db4616364fce9b61681287dabeee881130a", - "0x53b7aad781bea92a14b670dfbf5acd8a55d2721a6146f099b904bbc176771b91", - "0x0de55190007ddb76fcb57cebee9b1ec60d52d150ac83beb66b088a86a234f9d9", - "0xc703435d06a2e1c5e52789daa3587b920cc2aeed658907bb7d6442693c65902d", - "0x6d21f9a28fd36d865af468e2c06ef5f41167f3014251a53aa1d2ea55896ca1e5", - "0x6b24cd5b8a48fd9dbe7e8ea6c492b57f3146009daaa108ae5d1199261cfc3914", - "0x40e49643d669f497785df1e601cb1bedc24ca348f86354d4032b1d4a4c6776b1", - "0xc56f0ff0aa22fa1e19d8785aac33805f7a7a01b48e953ebb126a4728cf502518", - "0xdce90cab31ee9f5a345d03464c69cc5dba71ee601bc22ccc8aae1eb442f3b29d", - "0xa551b514490e45b6669024b539f7bb37e6883ec97ff79744cf1f1c2ba1d21ec7", - "0xbadc6d33cd58aaeabb8b62396e102cdcb6b1ce9dbbf7051f937a890a3f69c2c6", - "0xe544b08143014287bb8d917f66e8f22f2a6deaf6ad4a3996426e2186b8bb56e0", - "0x6ed9b64beac26a5fbbd15c09c7eff96652b710670c2ce52a6856cd998521a27c", - "0x48c14f31b1fcbe0deb7816771f962cacbb7c2c219518f3ce52661a89e9928228", - "0x425fc1308118e8ade59df07136948a210786f8b2b6b0e51ea01ab06653822693", - "0xee9b2ef0dc1dd873a3ccdd77046969a4ae938e5ff39695b891c85921883c3277", - "0x25066a1abffdd64dab20529436c3d9a44e6e837d70b19847e355f63b5140c5c1", - "0x7441baf6efba832751b31b93c9b000d8bf552d0328415937d0a3c78595961ee0", - "0xd9219e7f07c0808d2596cb57e26793c1162ea9efe58305e3a26da6fdd57091cf", - "0x1b42d745a54c5e3c48a4911a27c263bbe42c8f038c826975749c1619c2e21cc9", - "0xc5abf3dcbcee854b06836237de3a461d86a8b76e8081926ca3078531e25556f4", - "0x7eafa90a352f95c8eaa914cfba0aa90db5ab1275d966da5fa445d9dd6ac6e0e3", - "0x4230ac1a58e4d9903022416041d5119991f911b41702d43c1caf0723cccb1d05", - "0xd50f2cae10a5cb3084c9f5ed1f9acfce8fdf3d920d89ef41165aa2522b97582a", - "0x6eabcebb9040f7d6791a0a560b987288d8246db9140b5f188c34a6be7fa17552", - "0xbbcb3c36f228d2589cd7097111ecd708e419bbe034ee6d9aa691d483590750d3", - "0x72eded80c21baaefddc61ecb70ab76cfc68ec8509b4f8786fcf25d037d75385f", - "0x2a01f9310e3116bf3527cf90de19bdc78d9cc677ef5c9bba94f3ec321d700a7f", - "0xfa401d300fed7092c91232a2bd8b0173ddb62b12e8e96a5076172abcd44ef858", - "0x984001b77e32ee2ac8d5678f77e6c425e6ab9fb2ea3f4978b6594b4499f893aa", - "0xfce80ebf8039984ced85b45b5ee5c240df0a6aab809d6bc0974a5b1007c77d4f", - "0x0a964a69795de761278c68843100ee547edea07d790d2efe64d975b65d415159", - "0x5ab1dee36283fc991e00fb52079c232f2d9bfa97377addd05c50fb06a09c11ab", - "0x47676449956192bbed4235f3162d06ba3095aecb9f57d9c8a273052278582fc2", - "0x4e43d79f07711fd4d4a8dfc576f08d954b5d6829dbbb59ae4386b34221472966", - "0x8f5cce2a573190694302db226a2b22a89f4487a4f509ec1a759646d0d5face7b", - "0xe10a0419bb270359d2c190014afc1fe52f8df9454f7a8e0fe205309e5a3b33e7", - "0x4626c976a572110230c540dcedce33a5bbb97919867aaf0520128ceec2e9d8f3", - "0x057b497c49e4b2ba614280de64dd91118172ae4b4646c3031a93fce200c8a234", - "0x8350ba4e211602c38812276000b1caec40dc4376176afcac43eaa96d7af30d1f", - "0x9e93fd7b9b9ca98f26945dd5716ee2a6274f8586d14f05854a0f7f25c80541ce", - "0x9f11403407548c359164f3f93265f12ee390ff3b3631ec7b90b98922dfda2343", - "0x714cca7ddeaeaf302fc0ebf50fd072c89f063061ea329e67c809d62ea9f5fed2", - "0x9c088fa9b25f7a5ca635db39b3c95da6859a5941eb1ae3e4f4717013fe740cba", - "0x634542af50119e4adb4d72bec5d0e3b095df21e76865f683346e1bd544922f69", - "0x2a1fdccea29581063da7db3f2aeca7087ec646b11f1d47e6d48d1dea0122fdb6", - "0x156e283ab987a5da12710b459097b5fe3d79db296551fa289632c0d3f9b33ba8", - "0x3eaa070ffd835043d4b2b25d5e7280f030c615b2bbdcaf3ee12a2b2b4c620747", - "0x863d5bddbffaa9f0ef9f696ac5a7f91191b5cb52383e1d0c0e0d5a60332c8728", - "0x4e29095ca17d15099abb1c6c1eb43cdd6dfed160a7d11731f4d1115beaacc7b1", - "0xa46f6ce03354ca96ddfbfa9e1406dc7ab99b0b4ff1ca5e0346f3ce91338bafa7", - "0x9f19f494df488b250ba31e243c9098fbb7db5801a3f76b219d866bec32d009d5", - "0x763dfe2f87eb816d1bad1ba95448619041cdb3cfb1171063c043d1e02d9f6ca6", - "0xf8ed4dc0fc0c03b964eede54ede5e96a940cc85e1ef2191c11465f2f1e3f6f0d", - "0x04f0d8ff18d26b490f137f23144c1ca0e475e74a7f1599fd2f6a4972a3d1c215", - "0x012ead673926ef6b3d2cbc5332a7bbe5dac977ada6065313e90975360dfaff3e", - "0x773ccf0a297a22c43b29f1bad214ac9e3685a5d0bf1df7d49fd4b92065ee7b04", - "0x10b7f56b8cc84f4be7533ec00ae46c7ba852f8d093c889b7edd46d36c819abae", - "0xd70605a437bfc6a4cc3468f0eb6d59b293d4ebbcd7979eda36f02baa48e68d25", - "0x68a93b7685277a1ca51a341517d10ec1f03c3018dc4d99da00624d4eeb3db229", - "0xef8aef04d3b7771e8519952b4e067503abe6b2698411299edbe7dc22a79c39b7", - "0x8e72feb3f31eb9f13645a089efd4eaf6871bec26a9ee91452db0464a7e6bc5e6", - "0x3189fb0be615c0f2e2758400050e2657e3197546a15039e595b091cefc9e0d7b", - "0xfe444b821fdfb12248cbf371192783e4e0ea0a938f5ac7b205e15cac991b6c98", - "0x0329c441b6c6c56d76de552ad43596760d37262011d97bd91e595dd559e53de2", - "0xbecff3658ca19ed610e826a68621e7b5cf2ece293ff13fe3f0c57c3390954af8", - "0x695b87703e7299f3e862740bbf47ebf026e5330171509fa0927c361935ff612b", - "0xf90373fabdb83b403f67cc50d79aa37fc26d73ce8fda798e96c4a4b17440e2cc", - "0x536eee69a1cf54af816f4eb5d08ccaacfbbaaa165451d0a5e8b93b2e872c59ec", - "0x17b56635255ddc2278cbb7e7139cf7a8f6e2e55581c451cf53b5b53aa89f985b", - "0xb4a14da295a0cc51076c48032fc1cdbaefff2380a428eb03f6b068aee99a8fcf", - "0xd3cb3797197a398711299f816f34b7c98aafc8a845298287b4522cb0fd645b01", - "0x55aea7b1fd7bf0864b577e38a89c153ae8653da26c72f53a05d164b9da327a67", - "0xd4eb63ebc06549dd7419012d4bae13890716430efb843175a9270a6c517ea612", - "0x6958093bd81e477479a53443b0c95c11d8791b282d028bd9a6204af0c1f738f4", - "0x505f55a445b93a2ba8d5ac5531370e2bf760db44509f15d2f243175b12a7016c", - "0x4608a060b7bf832310eb73a157d285ad824b07d78bb471ea89ebf421d735d566", - "0x68c88d220d8ac23e19459d650594dde939aedcd75325ca2314b7a9419b938c37", - "0x891a1154ddc1e3239b1100840e1303e0627a53ad501996e56891839b54becc93", - "0x8b943abf7a572dd64da3c4e58c1f34f6f7a905fe7ce652a044ab8983ac45fced", - "0x06b07efee0be55002f485ec0526336fec4a7f825ff7eda3f954e8cc7068207f8", - "0xcafe52ca4b5198fb4e4e24f4c04ad12eb8c9f3ebf9a35ebc0014e25dd7bf5970", - "0xe0ed792746b780ba8e19ebedfd3922ca937fd5b8c28b03e0fb91789cedc0fafa", - "0xfd7ea98fb764223ca3c587820eda0a0d68301979313ea9cf0113508a05fba7d6", - "0xa874e6edabaf6da36996b6387bda14d12c3eafa026b1ca6ad5f1f4cae67855ac", - "0x29d7c85005517eaf1165be4921deea5afdae1f66b670d7de0981c4e502acbf1d", - "0x949318f8059937f207ea564672c86943c2c9bb86ca81a3b0a9523e7bee00a5b5", - "0x200dc199c37d16b25e207165c5576596266ce8b62aa8a9c6cd66d69d52c1a67d", - "0xd29108a106a8a5a87b5282467a0e8d37f74c2ae53e6dc11ad63005f3f3f878c4", - "0x44ebfaad5b4ce81fda3d4194c87b46e9e31fbdbf0ad28fe00cffa73f78d3957b", - "0x6843e2f6d3d56b55251ef7ceeb635226940dfdd698215f5d5a4c3e88ede5c0a4", - "0xb84d57422889adc19626aad033b38b6968aafbf4c6d0881a0ef5fb5932c6ab44", - "0x842626b82b93a54a3a1765ef6257a0c74e1ad2981d8da41fd8305adbac99bc76", - "0xeb347385fa0bb37b8d5d17ab60e6c2ed4bfd19210503d61beb88b71d8bf9adda", - "0x509a54bd689b7011504e835081bb9d3cbddd8592ad80da399689bc9172b48a27", - "0xafc5c51176c7222097b12310a3ee5513a7660491682ff1fcae5d168d2c19c3aa", - "0x406e9fa3f6687fa6519067e213377a5ff7875f6f0a3080880536ed395555ad30", - "0x2624c181d4ee1775bf687b522a36a7b9bb754ff6e40ef12363c7d675b60f3d04", - "0xd5c32eb980158772133a99d0af96e056726ed89be84289cb5fc1d083d48cc407", - "0x7b1c5cfee09aba12ae0742d9bc1cc2630bf8c1e141033b18724a524c2c1b7c2c", - "0x77b4b55b236cb7d27c77063a280eabbeba0d7171759fbf851f40b6ddbf7171df", - "0xa04791b79cad41718cd8a0bf604b0e956aa85230381070f842667d88760c0de7", - "0xb1558e574944d9e8910da9c34ed51d89caabbf6541f077a55ef2ae3c513a1e02", - "0x3ef8620edf05514c4e90c1eeb0a896ef30573f963a7507ee90eb76e4334412b3", - "0xfff99365402379af3e56fc9b999d316cea7a1f388623433cbcfef0ddcb6f9d4a", - "0x99fca466439472d08ea602bafc67d1d097e486b5e41b5a059818021f612e889e", - "0x094436d10026f1c57f04846690a0444646b762eef1fc0420c6e3f5cf5b845e14", - "0xc57923978a9dffe0812618f2f6420f30ee927658f9f1615ef7cad4519b90eda1", - "0x3a20d5e138cac29e3fa031847086bc62e5f90a5e363a70fd52e2602545faf9ec", - "0xf3b0bd1d597eb2b6ad98fbdcec7dec2639b92bc83c33db4fced5d23e6ddb64a2", - "0x0d5bbdf1dc7d52fddbb3e81e2d757b848f9e79b6847ee8d30352d4b01ba927f2", - "0xf8f51ff9cfdf6b2ad5609a68433c012c92cabeb3053335d784b1baac087516b8", - "0x5777236f251506c22a92f371e4ad3c78bedae22c4527ed5e796727512bc2b8e6", - "0x228bea6da7246c2dc862d9de97bbf824df6396cec583a4db71667306c5f6a02b", - "0x67ccfda86cfccbb139d77d8d2acf0d4bd980b507acd9bfdda6b5af6e10493df7", - "0xfd8f9ab200dd111c04803f62145fd82e6a79221776904be20a90c1c6328cfdaf", - "0xcd8ce973cbfa8a91a55ec09ac9acad4cfdd050b598349b996d252ecb3935d277", - "0xfe8067acaad74db589275d3122d84f04f717cfda870d444872824300ea10f969", - "0x4112ee457d4108c37121ef4ff0ceb1a4c7db3a9e3776d647225bf0838153356a", - "0xadb045193cb85002f2868c1ffd672bb93e60ed2841ad368bc8399e92c996229e", - "0x4243b3645b99ab1468bc421309220529d8ab204542c6901ce618ceb32b93becc", - "0x434e3c4da46b052402d465220e27d0e243bb5b116967d4baffc9325e326c9900", - "0xceeadbd91caca5832cace1624baa5f67b0d18f725627702cbd7e19ae3c691712", - "0xac54af2cdd95c862b3d9115c9550231f75ca8d6f6e607038850892daf1dd6643", - "0xc4ff21314ba2cac393c67982362a39b7ec96192fe37c316bdd4cbc692f729ffd", - "0xc84746b891e6dd8aac32b2ed0b373172df4ef375afe7e4ab826532b6b3fa9680", - "0x0b0e01ec97c12e488829d13dc1fd420c08475dc95d1fcd72c066d76220af055f", - "0xf0b54fcb4a3e075f391bfe937dd6b4f7481877182ddd4413e8b70efc63aa7f40", - "0x1a78c5cca33184c9e404a05f61cc56968b924d9d34b7ca6d573563eb250fc691", - "0x37ca206f795c02f45abc34380d67b15aa48122444fc444d4b307ab2f7f30a9e1", - "0xe03c444efd29f8d7c89b480ffbcb996a642ac6205ae43bdbb815ba1968051e36", - "0x2fcaf6ef7c22cebd36ccfdb037e66d9113d0965f3b5b89d597d91b490019471f", - "0x11a06d63b3395d09a048aee4d4291c0a6ab2d5f07a7667362890ce647153c2d2", - "0xc33a74c178ef5460b53a9a81aee02d3b42612acea7adb853ef74cd6b65f36efe", - "0xcce38b309fd919a930d38c7c7a7bc828e5039c22a7f3f858fd99c5d44b8cdb2d", - "0xc470e1078c305a5eaf7311d7ff652e24c9dbed83c3572a1c2ec7f6fce5788b0b", - "0xa29b6d0bf49a64182aebecc190a9a0b06931096d9984e7e6da9800ce17f7cea9", - "0xab0821217488504e1c255343939d109353c79298b04c69ff65152dd0e934022f", - "0x40c7bf4dd5a2d39c0dff9fb3bec5c8247093cc1bd55a8087d83f5fa7c8a35b31", - "0x3387ef8b199a2656c39407d503b9bd1571714e178d1dc7b1a74bc43cf88ca201", - "0xa5b2b8b2d708d97666281684641b611efc792f7c0cb27e053eb06752661b35f3", - "0xb604a4b7c3661098d4644239295cb9f6e456854651a5b289a2f4b65e33964232", - "0x4bf93500c257f054e6a6f993f344917215c9c943589644474f5cf98950b8fa46", - "0xa9ae0b7679a65498eeb2b460fe678c3452043bafc344eeb393ff1d930dd8751c", - "0xf21e50f55d7be256d14dba4f8909879cdaa3926b03da9ffa0c63ec3c9df34c48", - "0x88e1e824c968441ebf88b31f7a24fa5cd4e8985ec82fce5eeb13b3a30845e146", - "0x85d5206b3136bee840dbbf8d3d2302694dca2d5397aadcc34e1c1d6accac0354", - "0x00f68e845a2331a0704037d75361831d5a0079af3c6186ecaf256bf1da923c27", - "0x64c594c02b35bd5f69beb236ccc08140838c30b67a1576d2780123e29e073849", - "0x93f6540af637f1a630a8aaeaa86d046e09348320ca5103ebe2222f747a5acfc6", - "0xf9b4b9461b765b449191c7ee1acaad88d4dd7260e2b826c75d506a908fe71e6b", - "0x86aff1c986fbad8da2ca9de6315222459e9c47c58fd7f1a543e6719c566af5e3", - "0x68629388371ceb2a08bf7e335cde88a1bad7127990b44135058d62dfac258107", - "0x9b81d6745d5ab0a3459d26b97ab8f19fb38bd38c758a0ef106a025d091f6f9ba", - "0x2b42455bb30c387315cdf205a8a178ae4bd2d2e221fbfe4be55f5b364fe9a7bb", - "0x555e4dafa7d5d3f209d70a5c954da144ef231638f4a63715152bbfa4fbf82b4c", - "0x5d779a8f08d68794723fb5e95bafd7004685ec21d2cbae0e5ea728b36418dc36", - "0x7e5163ea9b1c0494aa997997422b02404a13b690257757f87f69bb42f81ba28e", - "0x3814efbbbfd4001641da7d5c93483f96467d55aa8b5b2aa6f980bb12a0dab6cc", - "0x431b7452a8157e3a6cf89a5a85d22127a1282877750519a8cf95e172c654c3cc", - "0x77ad2749e61c96fa868c7764ea626baef8a1bba56d3a3106e981869f9736ca76", - "0x7c069d14a86017ac36cb9007fc9bfe75ca92cd2522dcfd7bbf9d85c5133ed8c4", - "0x21dd48139667872fa67c98430f68e8d38c2948f19fff534ab2a5b8e0b92a4d51", - "0xcbc2dad5151367e6533963061712ee325f08e7df3cc7fa73eb5220cb32a2ec4b", - "0x398fa6f8d2b76d482a0c312bbf48ba8dbe9fb8ae06245da60ac68ce50f42638c", - "0x4ca92096314fe1d2d8e07aae3df39a6237e718c1396e88f3c0b73fb3124c89b6", - "0x3ae030ef952e2da7b29aacb08fc41776a44fa5a6c42e745d8b7991ff82566730", - "0xb4ef4b02dc130cea24a71a6053639238db2db30d4752dd3fcab1d98442fc3f94", - "0xa6256dc24acc6de428f47aab0b65a1f36855c7f0834a3e730d10a3c0c8d2bc49", - "0x9d8c8bfedbcf9552ba9eaa08a61846c52e240ceff531e7b19415ca148781ebcf", - "0xc6737357b53db7d2a14d7e7d4130fe6a516fce1dd0c7716be6137d93e21ceae3", - "0xafa9d3182fe6246f6c8d7b96852bc5dc200e8c3a97ce7d262a18c2fdaf5bf2b7", - "0x8064a1ee1eb4ddbf301fc983a03fa2983bc3743602bbb96d7fa6ef85780ba79b", - "0xc4616fb2fd5381efb37719cd9998c5051966b254abbdbe12228b2210c6f79116", - "0x05c30fff77a3d751853e3ae4c11678755c0afea16480a94bd99d61ac894300e6", - "0x47757d0104771c7e288f383bbbab1eeca2fc752d11989674305898e047a71bae", - "0xcc0f857a7617c16da7d35363cfc0af0ecccd9b1870ffdf1a2b2e2224ef81f876", - "0xab3a1b65f93df60eaff5df9e0bc1d91152d34d3428961c207f36ac3a94f61257", - "0xd0cd178507f1c3bdcfb7b528933f2d6227933bee2614636f3add19a66cc089e0", - "0x6379b65e140eed64020f496a95c36b3c27ea10387f78e0faccc5b4018f8f73fe", - "0x3b6977a571ac8d59bde2e851fec4384fea35a8d3905735fc8b8f2a8cfa6adcbe", - "0x2f193712529ecc908ee0a61172ec7839a9168cb424d8f108e7d7cc356bf20cf6", - "0xfa3e48832d13748aba9128bf6bfc76652cac371e49945fc33c941a9b294f7033", - "0x7086c7711a91271d7e38998715b4a142d7ec107accecf29f4518efe9f3333c4e", - "0x05c126586f4a2c1dc5814d07f158513d42282726918f71bcd1b4e0b67ee944b6", - "0xb7cc1efbf6880a60abe6b20f80bfe2ff3aa59de805ad964f8a7c5ed3bcefdcc9", - "0x64189964cba347ac35018ed9169ae64b78c3d0430145017cce83d78667b648d6", - "0xf6b1d09181b9fd18a0efa47c15631a6aa0eb01613a5fd1068834c4079e1a95e3", - "0xe760b5c16aaf587bee263f348f9adff4e04628b94ef27c828cc7155ebd3ac902", - "0x523a2c0fae8b2408ffe1c3b43e0aa7991b82336ed136e84c3f7d458d31143fe3", - "0xcc6d742b22b4fe3f41cf783a2f6f8dfc97a4b33866cd4bdfb27b05bc2ed39162", - "0x4d4ae86d282ec414a574370458c99e189062a3a48dc59a8e8e2a44790ce9b841", - "0x6fcba6a9469de1db92468f2b58b3b82c5080698aca4403ba8ecd1cfebd12b657", - "0xe7b584b0bcca0a5a119c8b2cddcc7a73c94f6f6a58503ddba7b0335815302f11", - "0xb1edda9625ea65b96a4485e10c798c707f6926e4ac2b51340a6f9d212a71a737", - "0xd8e2a94d4c0937a2033dc3b70b50934cb5b2ab54f57336cd6a8882af9f14b8cd", - "0x93ec08aaf09bd453c2b53cc7d9aff059681e0c23044f99569d0ea649cc7b885c", - "0x8ea82c1ca6be16616b62a8dbd88c75217470092063e0494419a6a926279230c1", - "0x175d030004e718f39b3986daa3beaf2e1a523583e529d6e90d117cbc844c647c", - "0x34cfcabda8b4f51cce74befb08f6a2e9ce884175f33fab59f26c60085ea73a92", - "0xd0570fbc17dc030bf6e7e3619d56ad2e866868939e4e5b23258adb191997b4f1", - "0x83245688ccb6e6f1b541930a3134d3141ef01a5f9b3f55f0321039b9d4a3cc1a", - "0xd782158768560d84ebc21d8e4f270f30055203b2d83c46ccf53ff8890e833fad", - "0xdaf31d5afb20a502b28329ecf1aadfdd16b4fd9118b10f9bebfe6f986a902dc1", - "0x0e24b26cdfc345d98a6d6dc3f4e3aa2629aab6fb2ca3820a78d58b10b09deb72", - "0x895f7a9b16ef1d1933c009b38a35a41632d00735a906b035f7fbdcaff1665295", - "0x70ee8681968778cb7da20dd794b75f08eda6b4b6e4aa653464436ff3685c6186", - "0x569f51cf2b92bde98085c2c167b773fbb9917a35844059696a1a97e378b5b577", - "0x4ee20277fbe5d2e616dc0b170e40d5b62cd8dd9f058c648e8e0d636316be7e00", - "0x1224d7eb3ae847458af472b19d3b0ddb90ce822f3389619487e68f3ce08cf6d7", - "0x57fb6d2ad5a75648449c99a46d92570b1dd74757dc1c0c11fe4923748eeb1711", - "0xa4ebbd26ed7dce135ce61bea0da82b8262d4a5319a7ab9c94527a7c670752b67", - "0x1533f0c29bd8a7b8d90c4c0755fc0b018439291566dc33bc816c8ca21269c63c", - "0x9cb5a5d41061a2168c0a8175d2712cd82d27cc777a015158c2fab0751e192d25", - "0x479f0a41aa04509beacb2eace213d9375393f583d384dd4e69b5b921c681c845", - "0x5791638640c821778349fc3064f6c5215f973f2b4f098ab38fc2138f6a8700b4", - "0xaeb1dc9ba82aa0c9ef266d1beaeeb5c756fb497607ee680c0a161b89cec21d26", - "0xb13f56956bc48a6b0760639488463cf403ed47f6d94c7b6e5a6de1d3d35800c8", - "0xfeccfb58d300594be16cbc9b5502ce851de36e15d62164317ef6e3dd9d72015f", - "0x3b218812b3219e243f99c6d23f24ba2b70e91b6a1e9bd27a5d3c83622c176b2e", - "0xfc8f86579345c5fba8e86d1263b3e9d01dafacd0c7347fff348c9a661925180d", - "0x50c6d100a8c0977d24c78bb58021df17c880a36f1ed118a0806d1aab347e3b42", - "0xd18166f82e90410a556744042badaebf3282c65d3c1fd81cf7e599695436b2d6", - "0x8f47f418fa13eee50b9a97781a02cc6a8b6818389f1b1f9b94aac17897ef1bb4", - "0xa4b3f32d84a547f66ca91ec35deacb2d5cb4da741059c01419b60b8d526d8a15", - "0x3187128246074cff2e8c0928dfc6b29294b0ad3f2ab839e7ba6d0b72f4f078ea", - "0x213988ced1828cfe0021da680b166b7d830b66a795613953589604d09b58bd5e", - "0x343c1581b9d75f1f72a787a29dba31a73384364672f0a06ec6d675bbc5b81beb", - "0x59a9da5a92994baba7e27688505df673c7131f4de43e6d174389124463eaa0b9", - "0x15c6b483253806002ed293f89d86d66314e55fdc32633f3e15dbd73dacdbde1b", - "0x01c22d029f765b941eb772991d3ef50458319d49ecd1e237ae0d61a05b7ff0cc", - "0x62f5ede9e4ac720b4fb34c239953b3289cb77e4e7bfb5268925727f5d21f0c75", - "0xe330297f6aceac790b3f4741efcaa4e58fbe1c61dd52c1d9bf99220803132553", - "0x47073de2812458f45fb789cb1a6d57f9ae772a38deb7dfd4c8c21c383cae3cb4", - "0x9e5dfc6bfe32b5f56c9799aad47fe1a01746fc9edc5d8583dbd1c0a1f7f81246", - "0x1d9a6fd76fc6a6f359fd917582f6a1af617b0b21984fe7038c323802660fe1fd", - "0x6f50b828af00200b81d65e14f07f8c34a5d485056b017146699fb8ac4ebc39ea", - "0xa8b15957d1dea63b64c56d3e40dffcbeacc5c9b8e35d27cd66f61f5c293def93", - "0x0ec32ee93cc52258a0de9eb53f48132b7101abd3de242b9319e791bc4205f57d", - "0x4405dae679f464fda0959884464e47f7d21e21d69288b66c35878560deefd6b6", - "0x1e490b1858da4d82632f376234e04ef4cae529c3307f3dcc5f5b893e38a418b6", - "0x4683e9dde5e2d66da4c288452eb653e2e6ee3a611f36cf2c80e5870c187c28e4", - "0xa657cd63ffa0cc282221e70ee3dc8a7f2e9eed9b8544f25467208b1954bc1f4b", - "0xd8ce555f85b979a26f207bc1e86d1463ab77f8c7e5f5b3c7db6d79785e388662", - "0xf604274a19dbc99c4c079ac4caed9fafd49c123226c3c223a53aff7f7b253b9c", - "0x4edf0d0aa873fedea6dbe3ad1dfa8a2b5e312e2c2b51c464c9af60a5a5c588c3", - "0x5ee990ce5c4e5eda4d80885409624af7b8075bf7349f386a40e03ae64b578707", - "0x3d34e4bb3284428153db9216319a83c3940ee95d0a47d963dd079a46d14ca105", - "0x7503687ea102d05d92bbcd2397eaa1edb42f757a0ceffd3abc871d2d895214db", - "0x7e7201b9fc6176b6ef993dea53732f335efb98abfa3d92462231bb6a5186bf16", - "0x1ab3a0afd0338ee34e047c60cb2c766521b3085658ae2e019c0688c2397bcfc8", - "0x68b5df43d045043400e3583c4ed125ef0df6f1617e30693525f68fede915349b", - "0x201fe9c6fe4cefb948972c644fb9d67634e30529589b4de597f9335bbd6dc8ba", - "0xdc09d73232f082162df780069a77f180bb30d8b736457b0c746c3e0a29d7dbf6", - "0x7c33f90910049812c0a87aa8321c97320ca8953f654f8ff7b6e3982b13e2cc32", - "0x5658b211c7a5a9d8d66be179b8d6039c6fcb5b5c9d232d4655e536e21fade31e", - "0xebfa63617d9055c50466157b9d8d0c16ae58f02df12f4e23f4bc09304701391e", - "0xb29f737d036e5d7164786e62b14d1f9c427792fc4faba265efc59360129aeba9", - "0xa1271a340258127b442c103959fd8d5f6369487c16575d90f9f1267fc3f78456", - "0x1d5e62fc56da958eaced4c2e6a58ad4acd3e978eeeb0f417e4a4078c6014a8de", - "0x4357d167c61da7d4d0b3f1646508a859d8270842cbf1f1f9f082d8f713da3904", - "0xd9556c35934ffc3ad2970a448d8caacc43e452b509f2cce4cf755b19d888b674", - "0x248efe4782d57127bdeb94420062b742031a4bd51c41d36375a9671b279b1e09", - "0x94d06fff869c9923ce5f524c3ccff9d9609b0b36562f47c65c7f8568d3f9207f", - "0xdea7e81ac2b15c4b145dfe132a1dd41300fae3a86eee16f936f46df16f33c686", - "0x733fb7f221502d0f10a13a5ab0473d50746afc5ed442dd69b4f9378abd304e87", - "0xc680bee47e540c3d706cf461399fcc1d7a6749b4b9f6808afa099c52d28cc6b1", - "0xadf685a187510063c7cf4b3ef6ae478705af7b4dd6c3c4080239ddcb6b069b3c", - "0x4ec68b78bfaa971ad03296b71dca45e36852f04f0cf6d0e8ea681c4d32ef6d64", - "0x407017242e04da4a1d4b16d59c59526a172e18d1d7abd4a343ac28ac883298b1", - "0xc92af4b5d045a6b02f2df7cf5a23173d969c447d7c006457a5be7f0251446488", - "0xfb8367fe9396d369332684b2fa3fad1603c7b572dbc00377bfcef0021192a52f", - "0x1d73c88199bc153c932ba313175344b436b28e5e0b39c3353d076c7e26fe843d", - "0x253fc1aca7544fb93d493684be03d3667628b5a0f3f516d7981e3a01e6656440", - "0xb8fb006c1de093ae15658b7be4b6625d5c6d14a5c4dd2623ac9a31e2479d32dc", - "0xb46abb8860cdd8ae411231d897af8269057060cb43880e52e63254f698af82e2", - "0xefb736142ceab502aeccd9df17018719d1004b554154edc5ac62ec45830a0d30", - "0xd9ce776c58f4e1eaf1ca20f61232d3063e98fee3ba0b078e5ba34736a16a7e35", - "0xb63a06ab391813bcf830a53febcf9553f292e097f127ab610745742f12ff7a02", - "0x225004609e0edef06ede2d08f490ef3616b1c5976027dd03f329605bfbf8a916", - "0x748f462a0fceb4743d8b03de6ecad430fbf35d957483b04e04609e9527fa37bf", - "0xf13227684614cb92591a4c0381ebb87b0e52bd21cfa265b501e231d8c3a0e504", - "0xdb56e9aa8ede4b7b042f32190ea90757dd9268457d43231d1a384bc6da5d6396", - "0xb4880cd0f5e5ef6e30e5c015fad427e418ae0efc70d084b7b6d342e1cb71ed2b", - "0x8a59bdfc0413acf03c39f473bd09b34223b109556c4e0fe976a30bdb1b944c52", - "0xfc2f062e32c3fce97a5f3922d3bb5c0842ea02a71775d6852e256ed0143ccb51", - "0xdc3ba03348f62599f9928101c65a8f13e2eabc09a377ed461636e48cc9e0a5a7", - "0xc5b2ac69eb80b3f2ee751ad4aa8e2eb2cc939e81586088986cf9d6e420330de4", - "0xd6d389727a18def7ce0bcaf65417e3d7543b1381b1b49c6f128f3e13f7d3eb74", - "0x02f48b5d43397b1f22bc7617ffd4d93254744b7fb872c051ef8a02698ebddc5e", - "0x90f674e478cf1dd60cccaa750363efe18cb4ed54415db0ea917a09d3d78a1778", - "0x98bc38a5b90450f89255564e3fb825c53d5fcb5eb9dfd9c07485ed6c42aa3f87", - "0x5b70c7cd74a8a92567686c5785657688f3f864b10d2ed8d9f21f004af2d0a3c5", - "0xb153280a3c86e119b7a03dfc4c8cedf79bdd41ee6128b3f5fda20cc83daa39a6", - "0xea4c28d38762dafc3c6716a760883e33666560cd05173d3844e7e280a6d8188a", - "0x8b498049e107092deee810a68bf8914e00d6759a7e62c8d6fda1c5b1531ff72d", - "0xcbcba70e0a5bb3ddb84e1b115432f025a71f2824061cceba494cb917c689f452", - "0x5906b23bc6b56c4e91706c5204d8873d17252c0291585fe4cad030ab6171e710", - "0xce634ca362c4acf53cf9fc68f0e7c8b199b7f3a1f5a2f9cc1c58a31f9650bea8", - "0x840f429289a783b9b4f8e0f575d1bbf0d2f4b00e09ca6dc83abd17d9718ad688", - "0xace326fc69d8ff3746bfede0d167206d3df6093315f3e3cfca8d41a857c20b52", - "0x905d81ceddb8f4301e62a871e29f1f08769336d7c0b94821b12668354af31fa9", - "0x8decc0ea30a4102baac7ccb3e50e15d50102f05f4a3102ee01a0a275d2c51ccd", - "0x56b67da5e38cfc995831754a64f0ce636f060d51c11b746aefac13638bafddb4", - "0x6672660064846341aa343879a40c5b29f10ebd8120c59bc76af66bd86b96cdf1", - "0x3600a734f1b1df6fe6dfd1cf49d8072bab86717bc5f2d276668c1a482e62acce", - "0x28e4ff2dbfe40b374a103ef1bb9e577c0f2cf1186ba3db324286ec490fd02fab", - "0xcc814edcf5ea3fa138fa7d5e387747334c3ad2095ff08465d7e533250430e25a", - "0x1aa1447a8db11875dc7ff7aa49d6927bc31f3b63cd88d2d937f53b1d85e448ac", - "0xa78c6871cc93ff6db876cff3755a59f8fac71e06c89ffc1200de6da08f56dfb5", - "0xf3c259454de9dd21788084bcbcf1cf0953bf7710bd797295fdfecfebd584d4bb", - "0xf86c0f1dbfb94dbce7368211fd3b19875fc831a422989340bce63102a2af3ac6", - "0xbd192dc3426a9b2fa33e2808a258e9bc27e1f14906b9ba9ec973ce604d9ea8ff", - "0xabd37b505d353f7042ef2ea28168f9d6be1a229bcd5ecdd4d69a96663a169446", - "0xf0f05dba6971b57e32e017285fecd48094e6044584889690dd6e0d70c62c3f67", - "0x15a8053e0694262c4954d8110c505ca101898fb5a5814ae8bcd35b8f24d6f90c", - "0x3f8c3d5b5a03f514d0b2f79fcdf7ea58eca2f85651932220362ec9ab50448aac", - "0xdadb41f6e17beff092dbb9a6dd57d3d0951a188fd041f60e455d63755134fc48", - "0x4017f006a6eec8c71840f7051e91dbcb6b18e52fa43529df1121980670f77350", - "0x358dee5f49761fbfe7e6694144a2de8e33614669da6b773fbfa2e330668f4e90", - "0x080216884b389a463ab63c12640f82f442fca56803b2bd7be45ed789db6c7371", - "0x5eb35f253d864713fbe058df986d8af27badce432ebfda59810f096b4e5126d2", - "0xa18c044fdebbe991e181140df95076de9cc5a70c2656ae99ef68e3c1532d7a14", - "0x69af193591c3eb53b00e9fadb149172df458a9e99f403352fcf866bf1456c6b7", - "0xf3a769187ea83cb9d2a96293ec8c9f2074e8231356d3cabe7669aba13f9509f9", - "0x5f26f2aab6b2a0b0f64312d243b7e3dc80621e134fe08f8189b135cfe7d9b2bc", - "0xd3edb5f8556c1a4983450a7d5ecc26a3dbdd332882dd63822e7c8cfb3712b828", - "0x6fd2e87efa3e7d4fb943852a358a515037af5c5eb9da73c3e3dbceb63106d2db", - "0xe574ee1110f966de904fb61d82995be95f3092511eee8195150099cf69fc7129", - "0x6d9c9b7b400d1f5c07aab8f4d23233d26e19feca2279f814729f122c01189c03", - "0xf0756f64317e10b276c71a678b5b62ca94e354c9b8d1ed7019a022be23fa6645", - "0x106402792c6b9f57f4b471736376a153d12ebab21e4bf034b6732324a1fa4561", - "0x33f10f38b702a617e0598cb20bd5df0067ac5ff155cd6b445f1abe46ee3d625f", - "0xffbfbf3aba272f7b978c9815e9e17972786c24fdee8f9167a47e85e9be60a613", - "0x9ed9567c8720d1450b2b0e45195c5a836484a18746e5d5a0bcbffe0b511ad075", - "0xa6ca434a876c017f522aa24937cd4dc1534da3fab47ec57e22ea7d2ad555fb60", - "0x2aec2650b00fa5886b61b1d7c4390dd4e5fd39097e9ed5b1714b741c7b0d4cff", - "0x0cab7259658221598aa39b98d9bd40e18c777ac3b11f6bc7799615c99858efd1", - "0xd76a13ba9212ef2ba4374b265d0405e41ef7016cd14197a2f7a9071e6d4e0a5c", - "0x2802a2d30f42fcb2c953ac6a83212c23711988998eb7f537f9807751b42652bd", - "0xb971ad9aee88adae769dad238a7044c996ee35596cc1b2a7f6e876eaeb4aa80c", - "0x524520c4c947c57827359ffc5a23c4926262c002e6c92d024ea7926a7e0a8fd6", - "0x0de58ec745307c9a36d5f3e97108f666ac58d86b85ce31e73b69579e913d300c", - "0xc0cd603660568434bfa04f6a06342e5fd78c6d916fb8dd77dbfe060cbaf02aef", - "0xde928fdfb8515d944a26630fdafeaf4b3a17dc40b08418097a465fc2009c2938", - "0x11dcbdd72f1b46be2a24dfa6a3cbd8024b6e05f8fe5cacb35618429d9918dec6", - "0xbac41ac48726fc1708a5fff1a06e674a64b8c3a906ec9beb7ff9a444903798c6", - "0x16142a81797daae5fa1914a473f0b89465b0078c5c042f6e1accab6d3ea77376", - "0x6535db5ab7dadf1187ab7c6f0e0a56fdb47a6e6c8f45627ab993b88eb09e7d1d", - "0xa476955c4d2810e17add2ac9604869e9067998fdce26685fd6cf422f861687dd", - "0x4b2ad0b366c96bdba812d3dad76f432dce8f0e8be1943ed92ee4478e9496d8a3", - "0x74bf0384fd4b8afa1bbc0fb0cb6543442a6bb041911a817b55bfa60fb9039733", - "0x5c3da2ac9d4284456d13270fe7d160178cdf61be8bbcbe8a8536da815ee70107", - "0x62da3f7efe7d1de45be7d134d290f9b3dd50e45e751290d870a4b232496ee71d", - "0xd130203c98355022ffd5389a6e84cdd8fa0c570ac887d80ccff892c168a49c4b", - "0xfe129ea286e85269736b508aca4471e643c4d84864809d031dffd660243e2f4d", - "0x69a60dbee253142008ed0cbde35b425e4a2d07545571d2646a5d2a03f36cbf51", - "0x1e2355fe638bd71688579e07145f147384ae18220c8324f95c54ba994b033cde", - "0x391020697dd4cf20f8afb4b76b25f2e08b5f77be696c2406ee45d0d8499adcfd", - "0xbac11283e3e355ef3466ca3dc7d604ca002e26a98b76caf134ea86efb1523eeb", - "0x328b03bf254908db475888105015b638e7b16a4743bb235b85b160e430feee28", - "0xe9450fb3b361bac3eefe1cbe97a096218c40ccf4530a669ff9e10207c69b26ff", - "0xa574c9dc2d563152d012a1931352ec3b9116f949197efad0868a53fe79a2afc9", - "0x629d3aa0c10926f4bf5d250b44fa6959e534444d9f0e8fcf5b206a78ab5974db", - "0xe1e7bfc1e38f36cc8a094038ffa44ad0ff7432c508516e7a8b3520d71714c608", - "0x13ac2b375558ac0582ec61c4edfbdaebb268efb3dd736b6483530db77b266c2a", - "0xa2ec61b999d453a53aa33105a90d310a5afd6f9df76966c53fe524100515da05", - "0xac58616f19436e53d5eed7b50162713711a3f9a76266ede35da4411008de1b4b", - "0xbe8d7a38169e1a92a1ffa712af5173d5cc18948a477a4db917a7f8cd7013d6de", - "0xfc6997785c292450bdc3dfcf9d0609efaa2eb780a6cb33fbc911efc18325c1bc", - "0xb0d71a0c1c3b2f1a746f8cb70fcb99695950ea488216f83854cb267f88c993bc", - "0xa41cb69cbb562b104aefb77c9f0b0b2a7b43b92c7e1bb40c6e781f3667e78c3a", - "0xc3dbaa1d46c2bfadf7b69d5930e00b3d2ee2f0e459310eb9414be6e7d8fdfbf2", - "0xabf64b834999a679f71fa66acc622afa69a45bdf0befe5c8e0224ee12cd3214d", - "0xf788f916838793b4efc5cc80d9f29f717b9a84e877773dd4791fd6a0c1cdfc56", - "0x289de39a57b30662c600ef8056dd354c67722e1fe198ec2bb749c2af77a27643", - "0x580829bdf1584a58d7da444d360dd552818dd1cfd5f7ff4323d21a0c13506b5f", - "0xba51c5ed86b5303e52a3b618f78614bc1640b1b145e87fe625b51ed80d31017a", - "0xeef60ce9f49954e62854e938873e556386b5045a57980bab45e6fbe6a6e9c657", - "0x655bfa2d02761722b8792ee69367dbf3103d16ed0d07f7f8ff6725dcb8f2d955", - "0xc7a54faf2898c69f76123fe1910bef9be9ecd77509ac74714218c9bb8b4204f0", - "0xf59ca122bb48dc6c0c577b04449bb64d7a1ff5661a016bf50e32005f3295b223", - "0xd96401f3f5031c1aceb13d9a3a0525b5bcb7afe47424a5b76e8381732e789a4d", - "0xc74fb4d8533d438c78710932c95500923716fdfa6625154320625465ee32f07d", - "0x63ba1efd6ee8de3b07dd2863eda5d46070f7eb02d35cf670f7b132e16a958bcb", - "0x61eea2352d91b206a674c4c2af59d8a2a0fa70479d77447e3ee9b7db85d353a8", - "0x04b60e8ed3b12a7fab29bb6a74ab7c9a33d403662c47e72227e7eab515f33618", - "0x9c229a533ba459b852dbce6d96bb72c03f9829b9a984119cc4e1c852992bd2f9", - "0x54695b023a34947fae5230256ba2b0905d3e28a28f02c2764f70495d71441a63", - "0x2c47d3759d9df5b9330e6836835939107269d70a7d465be651a3845e4a1903e1", - "0x5fb3708137f2cd39aefe87c67a6c4f9cb60a9945cfb769ea71d0dbb7f3ee8cba", - "0xacba51629f86d56e806aac1561bec11a697a05f6dd182ebdaa906ff77aee7abc", - "0x30ef858593f9a35889c8c036eda78d012e298cde4a3e7e603282b7f8130a4595", - "0x1c11971b18e9c870e513d099e5a49615e409a255725a61083001313a1d929658", - "0x9c6e3731ca9cee43af8c299c02eb49760141e74e1f781d1ba49b6815565f3ce4", - "0xb59c0d2e7c5ca751b9abf8b7740d8d5c8a6fb1799dd1d5a5e9f0ba75769fb2a0", - "0xafbe8a18d07a30f3ecdb907eb4cc05fdd8df2f9e4e91e61f88ab87dbcca25249", - "0x5233d2380755af7c385b5b50d79c5cd9b1255f0145a36245cc6dab7a86c0cbce", - "0x54520d2d08116f06e38d1a316e7e4080f3bd8c4e52b06f3ec20c3a5327f88adc", - "0xd36158eb493e04911fe79cdf4c48dd43a42aa3a23568c7a6b06afafd3ceb51fa", - "0x88a509ee3b2bcebe15ede2063f8fc9a04fe103a84551f279b6ce8d4d36d3b259", - "0xb1b80927c19a8cd540d126e07c52aec2206eaa37a13f2ab4726649d34357879e", - "0x83b1534ad48437dfc54412e49357b13528f0f80dcc32555a2b6f2391cedecbd9", - "0x13d00415b9888e8f64bd3110b04bd47125a96abfe52e0b6ec004c96e142eca1c", - "0x9e3b31cc2715b383eac51b52a3d912e22d3c1f23fa883028ace6a1e80b1c02c1", - "0xcbd325f1c10db1289d1c46feadabc59b24ffbeefbc4f242cecc3dc0dbe5c2e8d", - "0x7627582f663394f8284a6636bf024683547863409f38cb6ec4f67224eaeaa2b4", - "0xa4a44ba1e2badbb513cdd55fafbe2f5741caf2132dd46c602172711c110e41bd", - "0xc7612fd67e5d4bfcd14eb631181977f5b6737855721aa42bf4e9252dec190a53", - "0xbe3030a24be5db7244f04c43dbf419db3c7fde7e7a2f87951cc74df9b88a4071", - "0xab77045cc03cfb77d55f118f15b3416f8f118327e78ae4cc8a074b3c8550f992", - "0x0fccd266f83598b42b88c40c54670e3f2ab3a4c8bbcc0b4effec5ba92e0f26e1", - "0x5285dfd8a25356ebc198835359f53cb86608d1058a90b20980748eaf5efa2ac7", - "0x182bd336222d0cfa5fa9a0d8f4ca769e2d45829af50bc3a454193628e073b44c", - "0xf1a20eabf959a3c1135943378f31a69c049529785e0556a45450174237d8a2ff", - "0x69387452eb76800b6cb66231cf58dc180bae7aed04ab182dcc3845cb1abfee73", - "0x5136ee55850f0aacbc26fe7bcb58a74ea738ae6edc56bdced078297e8ae44087", - "0x37eb930540d64aaf250e003ad0c2274cd047eae6c28bb861bd880cb53aa972e1", - "0x82d64719d65ec1c1dcb0d269222b1acfe77326ee7d901c8bbc542b7696d98de0", - "0xfbbef382c42f25021b7b5787fe6ee0feb1e589310c615cf26642ae507f2e8e07", - "0xa2da021c8e8ac265fd7c181ca94b372375b3c297aab51811494c07a96536d4e1", - "0x7ca0d6023b41793f7a4dad7aa65428e43332f9fe40efb62c1e74253bf64a28f2", - "0xfbe923af0120629c5dc3c118350d1ae310844e0df4cb8ed48decd520fa7b38bc", - "0x3c7efb35e8c27c90cfc06a18160b34b772a5818cb79a1bf62b107ce93cdee33c", - "0xf59c8f80367b98738620c50a44cad556d73ce17a5cc1f403528d72a52fde5375", - "0x7173aafc1f35fc4eaaabb61d26f908a8d0d834252b951f5607a8cda0c97cd79d", - "0x7ccf293b0d2ab799ce9fc487187d61eef099801d8731fa21300bc1b166c11788", - "0x6ed0dde1a0e7bb58435bb525d897d1a9abaf03f235cceee32f3987e1863d94a5", - "0x5a5d21f5ae95355ccf41cbaad953150d3f1231de6df6650cc712cc57f77fb7d5", - "0x7731f59f98388e028b194d16478c9315050555889c30242a836e84e5f27aff60", - "0xfa682bb7f35f7a75c9490c074b1ef4c99afc50f1e32d829c057c765deb899638", - "0x10403d6937901a289c468f7eedfd69d38fea6c6b468f63bd7cb17cd8db4a22d6", - "0xc5ede1b02d3841558bd8ab6f51e14f5a4e6b00daa31f982874f0bad7132465a6", - "0xbf01b8033add1db5cd02633cf114492800816bf01ad4c7e060ae4c9ffe92c13c", - "0x72d22a8029386eb9c26dc3277e08676609385cc2f9addd4095fc1de0ca6bbb17", - "0xbb77dd7851cc4cc50551914a62c71f8b8e2ad7fcb700469a6b893810b7d71a69", - "0x6ffbe73f30d4dc914a859911afea236c9540920e5fc6ea35a5b15baced0c8d7b", - "0x2d06f3ac10ce2d780f52e9f8dcfeed80bed8c4fb628e8169290bf35b59fce156", - "0x1f6c2f6010356f6a37b0217433d7a1f5a2e0cf51c41b6ae6de4afb236fe4b5eb", - "0x011c13ffb7e71e32bbd4517b994856f6d21b7ecf43d117b8642cfaac37b9a8d7", - "0xe2613913bc4cd8dc69550d32163b251cb1e6ab37d65eb71b3aacd05713158157", - "0x54247c638b2b1fe4efaeb4eb101d5177c84c975c759ddb1ac20207341818c6f1", - "0x34fa5ed94aec05a24c812403ad050ccc96d7a052b0d9509cbbdf83503589e3f1", - "0xd4c86048683eecb1e595cf76988e76be819488421c56b96a6dd4641efddf4de3", - "0x541b476f9a0273fba6340f7185c2ce43333de04bdf3c1a7ed022edf4372b2d0d", - "0x65319f2394a1b531beb788f72246a6035539816c618867b0e1d0fa3baf318d38", - "0xcc6b6ebd1eb606210f5d03e191f303c1c4a438b9fd7b81f61c38da76a6bf9fe2", - "0x66c0dd3e062f95edbf9b26189a302e46ceb5cfedbbd50e43139bdbcfe9fa2b54", - "0x728f5ccd05bef6a64b639ff86642f71078719f442915496c430dcd17b437139e", - "0x85a856f40e911384e7a7a42fc5d9197e063bbb890a81382b4ca6ce2e78af2c77", - "0xc80aed20cda1826d775271f2160b6040682095e51d7deaad7405a2f1c801f980", - "0x95576e4497c07a1738d07bd468e65d251f9876c957d7efab7ab7ed0a6eb2c9b4", - "0xc30785c899138565d799884aa7bc486f1a803901b6b8b8b66eb6d97ca597d8bf", - "0x3990fd6682fee93631fa80ce6cfc68fa57e25850860d4d982d0945f72ad37f38", - "0xa136539e69b53ffb451c817c40776af735c95f3392d24d906b22c0e6c6114be5", - "0x8fdb4d50398d776ef876ce77547d4d9e1a4609be5432121ca1dba983ba07a28c", - "0xc7a547ac0db666820263a76b9f9adac876b972b22d6c8bac407ad6fa5c839987", - "0x87aa61ed60484044960beec4d9c45c9112e6e4a70cfec894d5a9f93f94060acb", - "0x50b05f8c5a72944787041204b8a8acb39faf179838116e04ee7bad4d6f21d4c6", - "0x8b90f04539dfb2aec48e0f7a47a1ec601af5414f3284074911eca3a3afc36e3b", - "0x3f9b8f28853028fbd3cb0bebf22a723907600bc2c616ac5251fff33b2df5e9b9", - "0xcd7153ad8c35638d23fb26282ab9361361fd23385ebb9693d8d628a75aec6c07", - "0x370788f25ab289cbd7f9a4b238bd35efb41917b771d8d59493a05aa7bcb3fa4d", - "0x942223044ff0bb9ae56f259006787e5ae6f21f46a3a2bf11c0e977fa60b50b7e", - "0x7dc0e910fa0a6d722dc78f17644842792e619d7e7c044ad47a5fa32583686e9a", - "0x669d80d664251cffc5c7289ae2195a0cbc6fcc732c2af1bf429a4aa98c889520", - "0xd6d24bd4d8a639ac36b14c6d8599ee8ef5d84ab71dd7e6ab829b730d930ce924", - "0xba692b11b9febad8099415d1c0761f2a339328e598d9e04533103afac8c04dbe", - "0x8e9f7d1c6c2674b3139ff88faffe8339e45d72212234b4531654accd66ee3d4e", - "0x2589b94b36fec67b5862871758f86c2773a963197b0d7ba9984427da3b4d6042", - "0xb7d1c1c35718c23dd4498571bb0f4727f3ebb0e1cd02d8094f4713fc0e858894", - "0xe222068e575041014f38e20a85ef1e989e9bb05279281c59569a627769b9d7f8", - "0xb0746c8dbf22e4fcf025fe5383fba7865fd02f79a4403d89753052bb9118e3ed", - "0xbccfcf626a8e36b6eecbadad1870358088b3e584ce79741bf5167380e97f5e9c", - "0x6215dd20373041734291e4dcac34b207235a5d19c3bd24359168d75778b6dd5d", - "0x025f64d258d97752e9b648e956d114c8defccb80c8810113a3522a77ef3f7d35", - "0xc00ffb03dc524d1ea1984eecfb8d26d860791cdde35c8b262faab927891e3fef", - "0x4ec39fa070e38fb24c41af52ae2ac640a859c61c96661d24e53c817228863abb", - "0x7d943dbec346b6bf55c226cae23ee23086185d9377fad511103bfdda3d677ddb", - "0xbfb16c7a87172cc804008bced344dd6c1105a7f0e925f3b7335b6b4d7c8def25", - "0x359acc4a540fb3b54fe22d81f1e3c1b969a99129e130e9e4e24481d024cc81fd", - "0x33091ed3e9f0e50c8c9aa100d9615e8523d594a99095e83863a58cf54332edc6", - "0x5eeb88207a02fe01699c8796b66ad3a0827432559690bf42e76ce1c7f23c0b30", - "0xf5a521239d0f969c8b5c372b84f739af314645de36efd1190f92973c0a2f9098", - "0x1a634f2d829a24589a25a01a22b139b8342c7aad990a14b5e30715d2a83074ce", - "0xc5f62552f5e25ce475af7eea51eba85991c59b73da75eaaccc3e5571b58a6372", - "0x9fb29bce2aff82c7d3eceb134cb1d5c6e6b309b8a9be5d239e6c6dbe231fca32", - "0xac3e3a2edae826437228f39859aea596a84253070a5e442e04adfd5504b9a108", - "0x717d4b6b147ee5eb42baed2a926241dbaed7e6426aae40972488c08430659d20", - "0xe18c4e7cec87f2e53f1e10683835563bb05e182596fd5252017efdf7d29411cd", - "0x6ddc0db9f04fe0b7f8417507afe16b59479303b5473563723b390149a5f09b2c", - "0xfbb88e97efd86e451817b13e55fe3d310edcb302607127808ffb350799631ccf", - "0x5f61ece4f330c3eb3647d2cf9ceb4f3cf238a324bfff628257ac4aa1de7f1663", - "0xa2a3bd77e36c3ede311ef3fe330e4a2b22dd41313ff4ff6347f629777a88377e", - "0xbed3c97e0d2dc6e56006bcbdeb007c8b63e060d80f46b691d38485b03c17c791", - "0xa4e4b8d8922741c5a767d9c4dd71f9d850231d298a5b5ae741397fd065600a52", - "0x0f49e95e5812f573c00dcab19683c391996beacf61442e52a9c240f08f8ab92c", - "0x3701c4ca890dba3cd43fa3b506cb3d9ce540e87c493c6cb9e80bfe05ec5cd255", - "0x785e2c2e4652a9dbc413c4b7bd54dc8c103f6c933eb9ec41ca44e609cf7e0bd5", - "0x7859e4bfa55b11274bb0e91f608c6d840b7de4385ce8158cc08b38449a61213f", - "0x64221410a8e57fa61ee9d1ea1b5ac161face63a493dc7b0da0f398c0d18b6174", - "0xabeeb213fab38140a8e66b41664626f3ffd831b91e83f3cb7d4f2f09a0b73e5a", - "0x8bc1b2eebc3481e54df34a5f0229a54264ac518dd36f994bbd162442fe1190c4", - "0xeb0753b5fe8706efe7224f354cd92381960212ce4d448d3ba554450a66bb5e7c", - "0x30f73d3082f6ce7f9ece8dc9d6c78610babeb05854415279d615eb24d0d10962", - "0x4bdbb7c38040b9f623b67409d8890070fd5d6f89f023556f2bbee1f40f55056c", - "0x18ff647cfbae0ee093bf85bbbe218802afecd7f45bde5dc29c0c76420311f5de", - "0xdf91143a55752eec775c6c02464a781e16c8e93a4270ebe1bf2127658adeb5a4", - "0x64ba37e403fe57a42440bd2fddc1e301297846f69df5990c2d664d3734f902c2", - "0x2f70192df5b8359e851856351ebe2b0888b1c51ce802766f16ea2cf8b723004d", - "0xa5ebf72bbf06ea040306715900f6bd06564887b52e4692333f880e38acc40b0e", - "0x3b86d989319b0c910e74a79b2177efa068d8601b9889b5e3d17d57c59c6a2238", - "0x1e416fb3c47d77206efcabafdfd3fbd0919e7e6d158ccfddf1d0a2a7cff6a37c", - "0x3db2b1eb730ce08d44e27585207b39d5f8dffe7425949fce9e6e1a4f296c6e75", - "0xf703aca3c59fb4e102018a6b156a83af6c93baaa96c54d9a44485a5b90ff6679", - "0xa103f504db51472ce4d6735684bbf46259e04224d42fbbb87548eeb01aabcaec", - "0x282e099b561c70a9a1a3ff6a4518970deac9a096563e15c01151c0c37f78f737", - "0x87bf6fc28f774c2f6bc83f32751e0d509a2e009e2e3dcbce44db2697dab33520", - "0x97d109285466d0d40ed47f6e1c8e11259e69b90b02731a4c25efff829e9c7e71", - "0x18232c1bca21707fe7b74896e3b1568f88094adcc51b4ad9a29a4c76ca33063f", - "0xc230723357dbd10e7e57ab187d1a1f72a27a53c45b650020cffe672fa8e156ca", - "0x6984c028091bbcf3e40bdb8da39d81d2c9f8a73801276ce3644fcf57cfd3e8d0", - "0x8b66e1d6d3d60f03098f29d9f9857a29ae48a5a34d43786a8fe64e6227ee2da6", - "0x353c9a58633a1fc28b5acf9a0f40e69fdd48252c689a9b8db7610c3d003d5441", - "0x1e020c7d1997e15baddb02742e8846228e4eecb6bb1beafc81a759b95783e238", - "0x50e9738978bafa44eff6b9ad9d344efdf40f7ace0dcb146e2c758cb7b3ca05e7", - "0xec6ca85b0c27f1735eb0822dfe5a0d4e642776e2945a9a22e104b9caed098f11", - "0x94f749840d03dfc1d1cc7f31070a1019cceb1eb36a09f520129457057885fb48", - "0xd29d8d19a1dceb08208369498527ccb0fd4aae41077d4c01c715e6989d5f828e", - "0x40b5cc4710284f1175ce67c1a0f134288428d8ea28f64f41fd061a3a4b139242", - "0x7ea68209ad947c37eb3ff8c779ba7f5f3efaad2d61edca2ae5efef666d7c1f5c", - "0x0af74db9e9195f27f1a04acc3150d6d6200a5f50be63a6dc2fa9acb953759517", - "0xd7a6b221519266e935b12a864c84c6d7daf4e4c8c812a93515ea8325e94f2644", - "0x041fd7dff3672a07a2922226bb0526daf0817fcc61cbde7fd3e1f8efa8dd6c76", - "0x79fe74fe4e56514072881413ba7157cc125051476d42fc9361a58021268ff0d0", - "0x01cfb41ca5887931cb98c347c25d6c03eb5e992fd661085307245dc77cfccc42", - "0xa1e93426afb5ef56a564ebfacfc52149e293c25dd36dcc2319d1ee50057eba41", - "0x6a8e8e62cd387ea56c621a5e085daaeb51003e86fc3c252d115ad417e2e79242", - "0x18e5d5dad6f00c36be4ea9c7898645611887a3c3310b97363699178136989527", - "0x6701bc914e70bced06487f22a16792fe3dff4f1fd15ab87ad42dc91de3cc951e", - "0x75161e0ea68a78e4377837bab6162f1b6d53cc5dd76c423acba9bde4f3ff356e", - "0x9d9ea2b7caf0b67dd97b62f0e25d172f78c984baf97ae3851e52e26c5af87770", - "0x4257fe5a910db425844b2edc8badb4977c819a3c40e356d260b93fd2f9170bc3", - "0xf2c878a894811bd844c30441bf38cfe79f3ede5b0574721f32a24755c40aeaaf", - "0xd45e12f2f3a025090fa81bd95e555c8787c26849410f96aa172d3dd153b40a67", - "0x6204243276bb752e5e2d77fc43cf7c5cc92078c283c4d489b7310ea2753ed71e", - "0x806a0a9e0f4e744b02b5091f72f57377a4d7bbbb27829789070967d35fbee325", - "0x13489b922a69fe05869130674253f90b4f883b199f01cd03c85df0a000e1717f", - "0x4fa87d7ead2b1789c72908523c5aa6ef83b7d9559f5c5edb506b4c2f681b617c", - "0x4bad3e31f50fd2dc570c31bd547d278b92e5c93a7d971c38035c09ec5a1dad84", - "0xa0f547806d376b76b33266d94e7d2ef86584a234b540e74f79756e0331405c61", - "0x4bab9342aed359dbce241e580b46043b160a01e44eb62bc29ee5ac413b8a7ad8", - "0xc7ff1b8f8557acccfaf4dd37d13a12e3f9d0e3612398c3d8b1d675467fca56de", - "0xb412685d085b0d52c72ca3d9987cc188f930f759022f060d5f2f368e77a7767e", - "0x0775a447ddac0050f5cb29e08fca1e7d016b42c882ee15bfbae3de5dd99b6705", - "0x1d33342e6115ca2919ad6d31fcea9f4a9fda5dfadfdb4e206e464ac44e59e178", - "0x26dd97156aa189d9f3c799e943f50ad13c2d0cc8018b054af65eb37f5caa76c1", - "0xc4daf50db81c04055bfeba3534a48af718695e2789aead7c3fea58b064bc86d7", - "0x87e2b037cdf4370ece9a2e4a54329d3e1be0b910af6da8c3850ac75d6ba34920", - "0x1b8fd79a76b40690a3bd1bde5cd3b68b36148eb951f45b798958777527a876d4", - "0x63ef67d88acd91fa005706dc9912991ecaa81cc7334ec367340a2c12147cbfad", - "0xe20cf7a6540722b3062b9f49dde601f210aa8cddb5b5ea65eb7bf44f53ce9d5c", - "0x05b5daade0670fddf401298414b70656294b8c5d3203ef7e21536ab2696195c0", - "0x678791e0898a0fd0dff39cdf16d298162200ab1d93beb96ecbf9e9a664362b9c", - "0x77698208c576cf9f2696280872c6f2938595ae522b37c2b8dc5b60b83ce7a9cc", - "0xe9453e5ae3e39110b65b03dca160f930f39e933ecca604f7b1d9db0f3d4e3915", - "0xf3d84caea9ee5dffd395c7da7131456174cba99f905eb734c0df1e070c16d359", - "0x3b56c6cb8308bfe1d9339e14c713eebd5830cd0c538b196d18743bfb729878d2", - "0xd0a1ed8e3bba6cd1ab36ae3e60952e6ae56cf2157039ef2f7136e09999393650", - "0x206a0bc96ac96836c5230d024aab5c7733e4c769b7cfa9fe4c52ec9ccc2c9ecc", - "0x322a0aae92f406bc6c08dfda0b60f625716af5dadfec8d014c6f863eb33c2edc", - "0x701f25bc99f297ac9e8a8e5d8c376f9a363ba277ec87d2eefe30833035657e23", - "0x98ee55ecb672b58e5d02ac83f716013b2e5ad57894383e792cf1bd82cb47b20f", - "0xd7c63ead8a31b5b188fbf0663e4bca087035291834ee1380da270bc26bd19186", - "0xce3824ed345e2101fe30f43287f428c2cdab4a9ee2b4a047d4c6ba1b46a05c98", - "0x853eb9e976bc0fb95caf99b60c978056b0423f2455e5433f3b9d6d28e8ddaec9", - "0x2fefc2d9f5d26e0e9893b98d6546f593dec46105109fbec9c0efb298dd18e534", - "0x255bfda7bb1a2df1a36e7ebc4414338928d59474d9b33f478dab6066275896bc", - "0x678516c205b1f93ddc8d3558e86ccc9ed5dc317387f4a7849ef158e7759234e5", - "0x58d0e9d13bb841286dc4c579c11cfd659b70e84aab4550a75d1d8f01d31c0b64", - "0xb132975005aac90edeba73c75a8533cb9099a5a7f6e9e31755a22b44289bd950", - "0x77ba37c64ec1db20a6dfad1462bc4cb5d49cae6e3c801a99e223e6062dda2598", - "0xe3a4d6edfccad2c55d2a729e6531cc9c20d668dd057c3a8bce78dc7789384d9f", - "0xf419bd76337344144a1e3f1e7a8d3f768fa97f4f1be87757e3f2e50c78c7ab14", - "0xac248be7b1b0f85f35415e09025f379bfbb02776e854d682bfc93caba3a0cbb3", - "0xd853df355a1b6f7b6921870bc5a00a615d5fbdbd9c5214e57e0461a00af92711", - "0x3696d847c84ecdbe2902eb3200013d332d364c0be47f67cc122bd7d4d644f08b", - "0x843a93026d2075704e731fec65fbef6a234eb6d2368f7da29e8e2d3cae45fc3a", - "0x923e0d97b13c9ff4e051cc43daa92960ec646236d1b02faf28902c5067cf83ef", - "0x4a700c31d34448662af972353f9cb471fd155b6c7b5c0afec7acc09e58ee85f6", - "0x2cf612c948d6d8a6ea9886270d5de1e131c2dfd4012f1811849c332716ca650a", - "0xd9acf143ada3cb5e1eb8c3d14b4c188bfce9efc13d317822dc406e08cb5ad9de", - "0x55b3ab14f3d1c8c7b8660a136c98cb4dbd90f8ee329c1fd08edd612a5b866428", - "0xd07432a9b77607da1fe30c98226a224c171cfabf96a1510849255a58f7040595", - "0xe12a4928faffabff9d4835598e103b6079f359061175ccd18159a6dfa7ece183", - "0xa223cb9afb71fbe269c1f721c22fcb13addb8ec06286f9d5b7e337a262c99104", - "0xc133b48eb3f4946ffec9fa143a85d2e009a3b8a99a68ed1d0121c7e9fa616fb9", - "0xcd63f76df5da2994fd87c43f743cd7d35d94cb8dc973cb7a120cb677db9cd5b0", - "0xde86817e4351951cf1b6402036bea2c75901b6aa7a6dff62b9862fe4783e801a", - "0x2872145a9ea250731bbfdbda0b66367300d88f2d9934c6b1d12960067d717bd3", - "0x86e0674ddc6c584cec7931847ec5283675445d337a89d19047817e46e9047d22", - "0x4aac4bdc309bbbcd210b87b0f50d55fe86f5db69b83e201f156bc9ad25347966", - "0x1c0e32260f903386af7e4cf14ef819fb1245d269d2713faa729f05b019445243", - "0x919711d6689630bb81d59ad2c75ace09b462625180ee8fb8fa2d3ac5aa15540b", - "0x231f8566ebd8ac13f2d6cd5a91566aba9074ffd235c762c26280e52feb5ee4c3", - "0xd5df310c108bbf4dcd5f876e0290c5d9c9c2cf20f4f979bc74d962d7da99a777", - "0x45e6ea115a13d30d8dec6f480a9d0b17cb9288f3d2590af02a40bf3dedf3f1ae", - "0xd9930c2a8d4dfd36b70207175e3ec5386fe91371e4b7fb484022e64b5aa8388b", - "0x529278e0e137a319d3a7516b41bcf4be1390e5646fd74fa8c6ece03b9e734deb", - "0x471c07b4e2c5760ad10759fc0889fafda85c2367f2a9147667ad61b687a52fe9", - "0x39332fd9e3c9c89f6b51e4a5fc43d8da10191d07e71c35383e5bafb997c651fe", - "0x72b4753dfe94ecc08b1ca90329dae352e677b64d3e28281b88765cb2e7e3e428", - "0x9a3d06d9134005224fd1f71f14bdda085f18824b9c86b3ec6254b213d8e349fe", - "0x9e8a68163e7e0d096cb822d73c420324ac7416731b27407d74059903c05f2862", - "0x292c607845fdbec84efa2321bdbda9864c3e7e543890fbedf1b85fa4b7ea6b7e", - "0x31be5706424ae91ed9695b2f8f7cb0a81b7623e02da8595f0affd2411170ea11", - "0x9331ce0a7339609a1a4ab7d82e41f432c430685dc6aae81c50ce18aa0592b465", - "0x37dfd9817be661b6f7310ae6c90b3b97fb5f63e0e622618ecedca0d609c39e35", - "0xa350f656a9cf101ba47b0fa109409f1113ae33405ed766d0308740d5497237cf", - "0x272d62080e7b1c27f7134c60ac9f0dd9c903528b1a32338ddc8fc199d9ff2bd6", - "0x8903ab21a203161e433cbfaf719a95b1fae95e029ee19f0c94fa7f1a68f3a1fc", - "0xd3ba92c896627745c0afbbbc9e3d8b5a1511057b24817d7070e08308812f7ce3", - "0x703ec0121dc8e020de0dce30459da694fb8b7ead68bf5b052320c97b0edff1dc", - "0x0491cb350e466467a1078436a1100a70ed6198122f299b4bcbcf8dbac4b1cbe3", - "0x826cc32fd3097689638c8701164358719b6c8645270b70abfc0b07e103d5403d", - "0x351679f35e52895312c8524262a6e420544b443ca3eee81cc11a11c4e40b195d", - "0x0fb18a66d08f29e90c1a71a648d1b36c2dd3fb84bd2e4b8ac24959233cb7a244", - "0xec7fad9848cdb994996bb9fb63cae6e913fe90c9727ba49ab8c9d293b692237b", - "0xd76f7d686c80c1ef3387ff50925bcb540084d3e8d750a862e76e9dce7a443d40", - "0xfa237f1a2b3dd6e4c646d7c77982558af1633cb8662e1d107d5ad5140ec86024", - "0xe7b5e487328a3398b8716043d3e6bcfb39a996405daa98be49493e46e9a46555", - "0x306c41368e9d1a13d4d20256602c4d4182e45b2b69549be7c7133e01dc930057", - "0x9551a36f384e66ad7c5a7d43f510480448f59fc2b3414cb2dafa8d71314ea4fb", - "0x72c3b4a7c21c0ead562b66abb8e6ac42ab96fc5d01c8a5f934df2393b03b738c", - "0x849f7030b271894fd605067a59e373d903a6a52e9fded2357c05340138d3de1b", - "0x5be66687e522cce14e119c9d4b3b3853fef6cadaff6f6c5c2eaef9b6769b1fef", - "0xe063a6dcd767abe2a9e37d3f18b67e2a2efd68ad4d96b4215eb75e4b23295afc", - "0xa8f6fbac9f159d5a5db1e8d614dbd3302bd5737eeae692ae44155d26637ceb4e", - "0xed37085b1ea6b01638a3a145297f390b63b307479e88c55908fcb46afa1e5960", - "0xd963204f34b83d30e97b458148d7e3cd0143495ff67d893b512d7beb7b225035", - "0xdcf4fdc30c4e90b82c2894b12cf27c2c9ca9e4e5ee88d6052d3526a96b93c55c", - "0x48ae98a481df3d2ec8a9c90d0a838125a5237d5716617d4be50d09b2f1f2f592", - "0xf85cea1c73ca422200a9b41a19fec626d13d70e0ae20dbf8c5db66977091e2f8", - "0xbaa3273782c55be478f5e44af8d6d32771bf837002231621eb82fd5b8c07b519", - "0x1dc0a7713568a6fcad865704e07a405a44c2b0096354115f07af09b760eb9a09", - "0xfa70563c38634999282cde0e088e97953e2d8a81180f5c9b2e99ad74493cebf8", - "0x58c7a37a105fe671a833f01f6c189c6f3204d462a0e119b494b6b0c8bd97501d", - "0x9ad437c83bbe3a45191b5c9fca239d1993960a8316e3c52978c266cbfb4d0ed8", - "0x0e8ce8c69bda4ff65d9bd614bb827def7d0f6bce5d550ed72bd5d91c8c1e33ce", - "0x74e04e71ea4c189be803be1116781a66cdfcc50a067387af6d1d468733185b14", - "0x9d1eeeaa2b39e1e0cc9717e921507d34f35faea1450727825d147ecefcf70464", - "0x6758482ad61721f92137d015d4d07bb238b0976bf14a3c548adbbdbba6b73f00", - "0x794c1b2f6ae68389c51761edde1c96b8bd44904769e85a8d7d0035e76be9f13d", - "0xaafd63d0587a40d5b73ba230c976ca80c46a09e3b7f7df136f8eb0b04b60f5de", - "0x67d184707f6e0b9af49aedbfca464946b4f54321c4eaf3263d4ce52de31f8767", - "0xd2af9a84d73ef353c42517a6c5c0667b1f811ce2bb327e3702b49a178fb94179", - "0x2832ef9283d9ffedb03743b454894472eb31740487ce89ef016a00819ff30c2a", - "0x82ea0cb0f3eb68ead50a1d8106278f464e0126c0d7589e6d4c16142d80989194", - "0xfbdc8b5fbb0109cfb3a3b03e01c90c37cb4ee7781563026c338f455ea708058f", - "0xcc34701c30d0ced752f863fbe56d47abfec0c9f8012d1fccacf3814e5bb824af", - "0xb0cdfcf55823aa634004387d1dbc34246921ca0459cf4b9f751377e3a78ee041", - "0x313e6f0cc985a8a0eb2fcb8d3901fcfecbb7d29e98ef8145996e4cea804420c1", - "0xa64f1d4794cd286788c281b14adaa6c0890f9782ec0d80448b8fef906ed7bbc9", - "0x619d70b2e48cc3b295add6bfbb8fc60f38a01e61f4dfb4872b58c1ce7046efb0", - "0xdb35cea33ddc36b0b91b0dcd84155e214a738cc81c8805127c47fb7312257299", - "0xe101f36ff5b89d028618d46138aed3d6b3269605cbf4f83d68c9bbf233f63d81", - "0xe5925c839b60da70be28b38b2081701e3bb0be98cf763539114609b309c5f97f", - "0xb594135c1794da9f7b7af1366d56b4527223c0471ee3780d65cc03cd70fe2391", - "0xd6a8a3b4c6d33bca9b02b8207aa680835266d9c9d4808e2d57f28f92936b7d01", - "0x640a50f666edfa0c4cc55b6c945a09108fdd53af5ca55ae92de4245d56d5e436", - "0x8dc9acbbf2b71953327924fc2cc711574fb1e82a6746a3606a71071369e77601", - "0xd764b84ee9d6a552e34252322f7a939ae546281136a217f5f7f34e41997e677f", - "0x3ec87053d8ec66bf2449a17989721644050b2341adf9036df6843a414a9a1de0", - "0xdca7784726ae438b274f4a3c5892da0bc69ea694791c1ffb5398683952bdea75", - "0x29114fc2dc70ec997d34e3937c2e54136fd1de4ba7fce8353e19af65aef490b1", - "0xf1407df809e1bb932c725821347954eecc9e5e226cd37b6058b45ef9f4fbd377", - "0x873826f03bea4b73698a17d2b0044f1c689daa17925302edb0f815dbf50c7fec", - "0x3e98eaefd07ff2f1f4d6c67aa7ee3cabffd701aa0150f572521a17772bfd8d01", - "0xf44be55471f0b66b065a2465a04f6f47efb35a9ede93a9d8b22aa26d3c6637e0", - "0xb114c762f1f60a913fa570cdb207c279d20abdfd789d7fa62de06e38c44d650e", - "0x6f300baf19fff0f5af714e63ec81314d7de839d4f5900979cc9761e0ac325956", - "0x94d99e9025cec968ce5a80d82965bf72db62250c49131b14c0329b0aaa462b79", - "0xf1ad3dc366363b5a7c2a238e693600ced352984673cc4d0a07ba0ddc2b25c815", - "0xa48dc388a5dcbbd81e38b3e77178a4248232ffeb23acd7869375323f1aba0529", - "0xe995c7008184fe2d5b39eb0b1229caceccc8e86ef5caaa720f15e133e79a2efa", - "0x8bc6d112254905a89fdc319bc231f4d2c52a22d60baea4a499da91119a6872d4", - "0x16cceba4d6c7ccffe424c9331a78bc3748aa7b629f0fefe88c43dad9324a1fc4", - "0x564aceef2d2c0d4b8d6f73c10c234dd82570a9caeb572dfdf023024b61365f4c", - "0x5630bfb3fbd9625f2204846fc81b98774e95191dc8f7995ef33df915f77ce2d5", - "0x23d02e3cb1729ee715fe84775b969989d11c4ccd470910921b75c63c4e9e24b1", - "0x5984c5d4ed0af67154b12c73e38909b45e46e161d85d66e286d33b5ced150a75", - "0xddfc15bdbbd16b80d442f9a0587da4e304d3d7951ada0dc3d041f5a1df85a9ae", - "0x924c83566150cfdd60410c400078830bb4742b7286a6f8badd3e848a5afd0220", - "0x82847029234edd5c2f1538a71ff2eccf1de43e94f651557ba73caab59ef9faf3", - "0x15949ae538f10280406ebf73fd79fe0e77e937f82cd9d0c623e700ff04fa0a7e", - "0xd93edfcb2fb0c16e18b5c42acb751ff197ebee6ef13e01690b08f8e38e777e08", - "0xcb33ce0679bc3b417095cdecd66cbdd4cf5bfd288037d57e93cb6a542613c06b", - "0xa678db01074492af055c36dcee385b37166ad08a4481309945d227640ece520b", - "0x30b123bd31dab5f0dc65766e2b6067e5caee8a53af647b0c7d35d16ab85b2ee3", - "0xfc3faacebfc2c0ed22820eb332bb2f3d9634abc98d26fd42438276541f2beb86", - "0xa0d90324e7577ab7ab5f9667f9d7a0731d223f8edae77dcc9e38225b0206ba29", - "0x75e3bd20d6378133c8a4f9d0ad1121c485130d65340b0596258466fbc733d61b", - "0xbaef6a6c8b335cd317631b907f692cccdd730c82213755988ff7fb78d08fa754", - "0x03c32b933280b801ee17ade93600bed888caf283e0a058bfc0c73f47072f2459", - "0xabc98360fccd9567e9d555924bd815557cd5612836de1b38b63828db742ac297", - "0xdc6770ceead9f41fe0fabf046cfaf0be83f6c27bf28616a97d58c43e3f591ea3", - "0x4f9a91b13c9af1523071f1d4741ead5b6c7a9e72bc2881ede8309040b8ab686c", - "0x175c7e494c7f0347ffed5ce74eafcede61403cd0829d71a9df99e914774ff33d", - "0xe408f526449bec877c0d5ed25e677e8623ea68b797818b93e443179c9ed4d334", - "0x8f3346b57ad4a5de31df1b0b5fb5cac40ef0152fb5360d50ddc84752beae5965", - "0x4b2398450d192781d3ad48ed398fe7cf9e5e8a417b29661894c6dd43e83e2248", - "0xe94177ce0fc04293688209ff52cf53c548ac5b7022e7e60ee739f5c1711099a0", - "0xee6e5c8a30380746df8d9d3f3de53439fe0d864dab6ab5c349012d384284fac5", - "0x9fc9e919898303f537988a6695329143a77e965802be3566396f7a581962c3de", - "0x5277cff2542cb90c1698c7e673b7ceafd16cf4d0750272ebb51e3aa3fb02f3f4", - "0xea07718095d00493c662b21d0dff8bf747783c93897f6f2421571e2c7e3e8955", - "0xa8bfbfcb73a903d2937025226b38aefcfccbb3f9d3fcb0563ede2d9f1cd8ed84", - "0x795af85d146f9e00cfc5d12c97efa5073f19977afeae31c636cb3a6c9771ba14", - "0x60d169884296b60a45cc87beb366a513a6bb7ac34e6acf6066d9abbb1bb3844e", - "0xd256c59553a03c8069aa3dab431c2868c6dee246fcdaa9683b0284d462ad3fb0", - "0x1a399a1ffe8e7f5eb5fa69e70a620e1b15087988934a754a468ff47a7c2f8add", - "0x25cfa588df60eb1127c4057ba949004ef8f7f2681e435fba925f4bf8ce0eb54a", - "0x282d5589c98668c7a9baf871dbd48a3a8c263ea55452086ebdb3b4db440fb9fc", - "0xdd3cfdffe34633d189d9bea7513b890b6d54c2c0d12067d8f45ae72c258c77b4", - "0xc81f96420ae777252b04116d14bb80762c5169b73abad2e1fb23e8bb9334c2bf", - "0x88bf1fb814490289ecdd3d411134d42155a483b7225a496f24ef29d84df164ea", - "0xc6f0b78237824d0b240b8dcf1e31e75e93b4608a495fb7d47ed47ac70caa5aa3", - "0x10f4272adf03ae39311b049b460dce8579b424d3c169101a80aff74efc018846", - "0x52a6ed4acdf0766fc0a63b221d113e3dc7f9e22435d734972e3facdfb3447a8e", - "0xeb75f393b00cbceeace53bff0725e0fa3382e367450fc1c7f0005bb859467d62", - "0xd58b7c94861e7822f924356cf808c0b6ba19c100e49cd8a1fe2ca8a537bb20b5", - "0xd355030bcb4e35157543fafe7f204040ef0e0ecbd60bfbdb897add425112be2f", - "0xbe3ca964fb22fdb9c6c74f07381dabca0e932310cb88676144cd78cb374416c6", - "0x6417c82c4fa9ec681e767a8b3a26640d72c61ea72b1857bd61930187a54f08c5", - "0x0501ac461bf7f2e813ff8b58fb9ba936abe7eb0dc6ef0072f98c52f83458765b", - "0x6ed677008e96476990029e1ac8a86e4d9af83f812dc73d9b876c772c2beb5a77", - "0x9c35dd84c1d73d78dd3277d96ce4c76beac8c6bab498a8a5853970a03026b2c6", - "0xbb0b70d3be3239ac17b3689d67571c12318b51bc6811dea5e8908393d9a567e8", - "0xbfcc8fb9be4c41f8b26214e8462320e79c2fc126abd6e129f701b27b9ac96711", - "0x18bbf70cfc61152ffc64fa6bd21011e446f9d8d1798076e8841cecdbb2e25ccd", - "0x6ec57e3b3b13f8e3fc9298760ea47907c9e08b89bb7599c5cc50c83157a5b12d", - "0x1e681fa5a5d2d63ac23f45cc30720c5b33a3417ef0adccb10b9d9299f5dfdd15", - "0x63397c3ea42e3ea7dabaec5f3aa3cf7e15a4ff94ec745228c0b099928c2a5f1f", - "0xc8d14199fa6b134b84c48a44b9ef1962a9d08b34dbe78713f84ebc0d307c38fe", - "0x4dc0102f265b0e29cb403b258cfee5c73afb843b0805c9218bea86cc12a08503", - "0x4acb64d573cef699cb2d9036488a28bdb3d68a0d294d04c92cd198d42bb48098", - "0x872df3af6d40c5beadf565ea8f6cc502edcde941c43cd3024c812b2cbc33eaf0", - "0xaa24ebf00a407b1ee7e126cb5f396a1b4489577f160a7ec735e0e47fbf077abf", - "0x15e6c3ebacccecb59426bcab8f5845a60a584a18d5f6875431181a816d5dc0e1", - "0x3ae0bd8b42433be46a280335444e8e2d35c1afec926cd2c1d5ea537992c506f1", - "0x1e423a88a245221c826586cc67ea45dfea247c3e9a36c81ae9f0c0b5d5d3e5d8", - "0x5401f93723132c737589b457216dbf231a753c4c4619444af4eae9bc6153ee40", - "0x70d9a2787990c3dd8166357ab21985abc81a55eb0d50af7e19ec20454b314d4d", - "0x4ab7adbe2f74ac269840ae607a17824e8ec74054fae9d9b3998dfb162962e1a0", - "0x91fabfea828408eb161ac781a0896155ccf7d9cbefc3483cde52b35ec67883e9", - "0xaff4547e50acac2e3851620e541e96d7645682f225400793ebc0b959c6a09a01", - "0x0937506d4c9e37d57560bac5851795d6559e51df255ea2c64f2e119b88cc7511", - "0x88d66c613582fcf6df46773a1456304c4c0eddc212612b2dddab065b6051c8cf", - "0x2d6315542c9b30c8984ca951ab488906c78e6c84aec2090397d4a61ef26375b9", - "0x8ca8d613a382711420966787bf7bd541f517733d21ac4f893c282dd930f69b5e", - "0x1751120f6735feecee772482da8121e9e57d930d23e69cdd5bc039f43520b9b7", - "0xe161051a099cf4b605fc773734e68f46253efcd52fc9c7d7a207f956c407c606", - "0x3742dfd43498b7159cef3dfdb520d34583ac955cf8abb3a453835f3132877d76", - "0x1cb6bf586b0d422a986d235cd2cfccab6aae1ff580434bc9d1eed944d0c54d0a", - "0x3028e937857f2ac53e1d988fe8aac0f18616fb221560c69530c4399bc49db96c", - "0xf5ba682318437ed7770cab6098d356ee63c41cbb8d7a114ae094aba333f78416", - "0x9ff54c1d56a1977d406ef680e80850a4eae13e2f5b0012c0961a87426881088c", - "0xed99d4a9efedd66d3902ea4bf332bbc3649eb84e6a32a239c6fa712019f4158f", - "0xf9fb0e5082e82b4e3fde779d9f42870319e30e01b43f88a89f651c6994f1c513", - "0x69467611b658799b1ed8369ce39b66b5479f9601ee796a93bf57e94c4a840db5", - "0x8e1d27af1e1eef4038af7c34a142ebefc75269fe0991575f06f658afd96b293e", - "0x3a03db5ce3be82a8b47d34e271a7f126509d0f0402ee067395b4a6d7963d178a", - "0x641e2f3a93b6867ff70d8acf1dfe8a82e151c84d7561341107528f58e69df814", - "0x8b33c66fdefed9127a548e2ea61a30188d7a2b16b6bbf5c52d5bccdf860a47a2", - "0xd62cef231735d396b265e3241d51d08a90f6ec82267442ed151fe6484fd2f2b6", - "0xf52f27645bee7eb4c8f9c11e61aa11658240bcec7b6bef9c56149a3dae1d9374", - "0xbe6de14b25ace5ce862be24755393abdd07b3f4c50985062815139073e4c39bb", - "0x60e06bca41e0d1b136a7ef2476c0b495dd3443089f19f2e8c306611e40d5caae", - "0xa9c739fd18f5962d8b71215ca05f129153968e12634ddc7a19d54136de335247", - "0xecb51e8baa0e733048627977d8ec736cea38e501cb9256d7b5b4f264c4851055", - "0x634a99ea3bd706001327f1eb6fb79a9c0aa49a6517e78c1135095160152e3273", - "0xb9846ba9630ef1607061ca5cd2c70c48b632f285bd18fa2db6b4ade2720538d8", - "0x8ca68b47412dc13adfc72489a0371f5f2f0cfd5942d5ffae08a57a79b9c70ec9", - "0x13c9d7b77677291c415253af2508e66ae8fc09b547e66bd0372bfc7371b3379e", - "0xcf3d8c54e42a98852364dfc2a22a9029b944bea2361208262c149dca5fe1717e", - "0x752a1d54477a8120a7df54691c70c21dd399e9d84a576717cfcdbe9320a10686", - "0xfaa8c3a9658b4f096aa4c89aed1fd305062608aa73fb9d734c9d9205d2055760", - "0x96839db4f4ebcbff566b96dcec7c57de674b8075c07e6a17a4120b225618f6eb", - "0xd7accc2bae2cccacfb7b7bca6fa9659bac4bcc7f47fd05b459f8eb271725e676", - "0x37cd77dbc69588fc060a696827004b1d1f48e46b317cb82dc8329b65c4f2ea5b", - "0x83682e40a4dc120e5019eddcf84be4e42563904d1f06bae96cc5c513790762f0", - "0x4c519f5d6664f4441baa7cb15053d7a385c7c31ffaa52044fb48195d9edce9e4", - "0x28e1f389b7b0338f7ae494af7b4f239fe703ac53cb8648c24ed201eb3d0fa366", - "0x65d0b0371e3ab2d49d30ec178921d8410de8223ef4fdc4721d0a3aa1d1fd2676", - "0xe7779ddea50b161ecf429a3d382b7eb564306ad7c050da244a6ea2ec4336a85e", - "0x42ed7b3277a97be9aff896f3bcbb1a3ca9a1abcda194185f40aecff40dd05c9f", - "0x16151d4334d8e9555e2df32d6a58ba3caedc4cee88906391fead30cf7b6c1492", - "0x22454deb333cf072d9a19f52c64c232f4e232100e4097dcefc552c66cd037174", - "0xc77ea7b5df0d670d6828243b057169f25c1dd3a55ee23706aa16859254119d74", - "0x297b0d48dd676057449e91ec41a35ff23ca7101062c92c0dfb10f989be59b5f6", - "0x74b963f879f86a4bfe0233082129bc19095888f0d9ff99e09d835d8214de6e31", - "0x09b537b0d42a9c3a1a191c9295611c1ad9fa5f8002a2ffb990131ef3afdf2cc4", - "0x71b3dd8b8b917826609084149140de5e0b34f71e2baae4685d8be00784231499", - "0xa2666cdcd63be625dd08c7298d6bc06dce126e6a294240a4db43aa3aa0b3428c", - "0x3860cc7900f214952e4b797bc5ae198d59385d8fa747076b497a5b4a2c51df66", - "0xa53b70488cc1e45c1e11460c66ee94c46dae564c5a1cb7e3eb6796796943d880", - "0x06c7f6905ed8d559f757a6d823b876850002ea38697373a77bd9447ac9d34f60", - "0xeca8100c89ce9478abf32238bdad1505f3e1299915b6b67d1f5260ee3d494c09", - "0x06187fc14bd288ff503e878d0300b088dd2f9fa7c4da3193f321fceb4702a841", - "0x4b8d46ce312100e848366f8283558873f17fae5719f7f33f9a73df885fa12828", - "0x1d68bc807568d3d971d36d08f06c2e6d70cfd02ba03d322d7cf144dfc53c3044", - "0x0635b0924e22d1b03d1ae8bf9cd51a6b3479cf9e15e9c341ab97c9578ecfbebb", - "0xd181293e935a777d4b4905e7eda21cf0a6d48842b28228a9ab4e480cee7abfe9", - "0xcd1e363a1e2bf26e5f3c98a7a426e7528e4a24155bfa2a1a968a971d129af069", - "0x32ba79240b478b0b82d06adabfff6caa596987c07d73fec3907f4bb21aab169d", - "0xc4c7b178c63ef2f10860a346acd8d43be342ad103c41cafacff07bf748b6c262", - "0xf7c42fe2f081334fd729629f766de6919c41084dc143999abd637dde580895b0", - "0x2b3c788d5492557137bcbde36a3c77bcad3fbf46e93dc83c43801c13debe4a3c", - "0xfb2fd5480e25e6d50f22bf422edb888194e510ee40d112d21210958bc47d919b", - "0xfbc6dbeb8b5687e5360da7e241bdac501a6ed768e6c9694dab686adde5776cb9", - "0xac01b85031a280013440e18402e378d16e8b8cf582923597d4937bd9502a7951", - "0x6de81b17804834c0390ca350d4027b7a0824ddfb38bdb01f2922e240594a26b8", - "0x18ba82983465250c42294ad5c3fb634d372df537b67c1bd735aef7b3d045a9bd", - "0xdaf21a1151c73bedabb678497324ab939a9ab1dc3ac506e395b164a2daede36a", - "0xda42e99c8b304a39bf2ed10d88bcbf9ff527c1e3b9a5932400f331e9bc51a88c", - "0x563e2b38354aa19dd91ec0914f95852810f3f605e828a727ec0a69f54278be61", - "0xc64bc37db96aeaef1d1b9f5da5b50964690022e0c78015811a8034f2e10be4a8", - "0x96958617222852f4f6dad6bc4cbc79657170d2f9c4ea0a5cdbaf3ae723460a0b", - "0x0abac5a8433b40108cfbd385a3bb0298c069ea4660349927c64945eb409da78b", - "0xe5d5f48418141703f119222ec582abae55448128570da3ddacd758f112e67472", - "0x2491021cbfeee20e3a578b9f0508c85eaed8232d3ae1e6f074f43410ee373a57", - "0x353a4b80cc69d701510ba8711543f1b5677916ecad829c76740f21bbf7b1658a", - "0x74397d86b849a792c4ae5e8e65750a932310d1496a0ccd3f5f4170dccd707d3a", - "0xef520df7a1258440507525a8b1be9e60016a95a7da4eab82dc85d8aeeea3a929", - "0x0182f92a90d14a09e6ad6595d86dae389d297766dcb8d1d7a685bdaad733d2d5", - "0xe2b947deda76a4cc65948c3a40dc7ae7bfecc285f8881856bc55d4d8834f5ec8", - "0xb56dd5d67c8d49b5220cb8901a67525e00cde18ca52a5b26d09974c430b1c304", - "0xd5aaccc68b4fe4694c9cf7bde2d3c27a4c8c5df40232df1b875195404a762977", - "0xba7681f4f365a69625ec74bb1d114124bdb156767992c778ae5b49c4390438d4", - "0x838d298d84516b49a77b86ae27e300b1ad52beef517df8dd4b87d43960e51839", - "0x931e8b07cc0df6eb35d54d24d180e2b98e0ad3bec2525793511bbbb218063853", - "0x94db666ca733d8fa4954091dcc53e24fd1fc7e6135479b803ba45fde46753b74", - "0x4e311786ff11cb12e929f2509b5c7e94a87739f4475a213adf546dfe288966da", - "0x5a7be45e660da5079427166c854a81f2d4bbe2e326d0ae1f1d0ad067120c6c73", - "0xdb863b5b5109bd3f6fb7af19bd608c5a8836e2c0d8512cc438aee184ba97708c", - "0x8e21743089dd75ae94a2970874ccf87d8c57f2bd90c701d9c30fa922ab600f67", - "0xde96e7f8eb356b706754151805417b690e9ea613cb0886df7424f07f90b9f35e", - "0xd9af1f5dfa685f1b0e0b4bacdee2e24cb2448b9c990a21cd32ea85628bf00553", - "0xe61ae40a63d3cc113a9df987512cc6f94cb69caf36ddf68baf7b77089fd8842e", - "0xd32d2de4b6de7d3b9a8b75fe004b1829006473daa4507ad3c2bd74bb5473f2f5", - "0x5032a547596c291d8e0f20797dbe1b52896deb1b13a930b9ead43ca93ffab7f7", - "0xdf75524819354c93547474a730c86e285852736e3aa6a93a1397cbeb8574be5b", - "0x2ad84a52217a7b78ef30fe6ce5b36fac975f8eb996d89423e35d4de57dda944f", - "0x24273629f43c888b4ae4ce0e98babbb86aebdc304ecf7565e8f5cee13713e109", - "0xe340a7207222fbcd3a18df2e95aba462efff7806fd0ab5c79f163a9ad277239f", - "0x14b19088ed16354ec0d508d91100fa39f1c50c5b153edde1c7fe12b6325c3269", - "0x8ad8cf08a5d25b9798457a32ad4b559ceefd8086aa662e210abef814d8dc5c84", - "0x002ca4fd98f15c2f1c8eb360e3cd38eb102d6f93082e3f1bd43220309d53bca7", - "0x5c3df07ac15d8f1e16662b5ed8821e43f45b8adeff87282ba92b1a53434bfa83", - "0x75d277049869aeb977bbd1d7c371b6a9d84c3714834d1a16a32af5286acfed23", - "0x0bad0d61579e2555d6fe7f4cc514b674c67b589ebe5e7c5cc451bacbdd5c1318", - "0x92f15acffbdc999e32e763c32ae7a6f373f4864b2807de08e7af49c70f64c659", - "0x9443fe858cda8b2788eae50b66fd1d44280c58f022471d8c6696716f0322d8cd", - "0x9c3bfc75fbe747bf2c0f1015472e67fcb8ec91f40e9b509429ebd91954b32d25", - "0xd112736eda168795236314f0de72558fcfb104e2f692d952b16533446ba75ed8", - "0xf0671d0460f40c687b9d7d7a5a46812897f3e8289aa7913b3eeeddcf902ad907", - "0xbd8dff979715487464394682bece79900bce98196542df4479086aa7a28d00f0", - "0x615d56554914d61bc4da030292a1056d18f71728de4941d0bcfe8dd123253f84", - "0x826a5d357b7f2a007804bbe75fb89ac6f7ef3011f408a07470b031e476d49758", - "0xceebf085d7d36747965b6a2ea80d1a1e47c71a27f28584ac89e35535d2da35ec", - "0xd4fa150d101967f3d7eb60c23b9993bf9a93ef36bda6bb75e17c3f8e992a7620", - "0x8672059e26d3c2605d82c219d2ead47d94446c8dc1351b109149a0514d618f64", - "0x38064f1f84a646320bcbc7778596c25e9aae2e356548b4559aa447b6bb8d79ac", - "0x6f195e60a921fc96202810a55c4ba9a042685f20761fedf15a22b28198fb0497", - "0x8dc83f591d7eaecdf50fe1e92e0480d1f174007f5b7a351f377ea651ecfe76ab", - "0x6bd195f328b200ec4ebc57f94ddb36be5b77aa16e633b9ea8aea5c8a3f1f1b1a", - "0xdc1f7643b1ac9c9703e5caa148001f2e2cb7168775ad3c6c95c675d6adf85e6d", - "0xdb2aab4822e191e5fbab62ccf4d8b8e548f62d50fd2b0e1008e834edb91762d5", - "0x7100363198ee3bfa9317ef4e8455bcac54276b3a55b8c01873a54f1604e7b24f", - "0x9de02048489d873a3b3c22cf74d275696aa699ec103b4aff251a2f22ebe20b80", - "0x2a3cc09ec20941ee8b04bd89e38b7cdd9e9f0dea006111d0b1a61a47f77e45d9", - "0x082317557cce01fb189870585aaf7e9af411e7c8cf38b4ef7f101c96b0549b50", - "0x64f53c41e32b0b9b2fb925222bec9676a21ea39b558e2bec0132fd045aca03b4", - "0xe407abd21dc0cb6a76932ac56f7b018bb3a1e9aedbc38d59aca4c8776260cacf", - "0xa6686c4193a6b61439b55658605f9429082ead789e70629860260706a62151c2", - "0x190dad8ee494cd10a5fc87d5b33cad2b401fa8e9e60ddc3152182cb15dc4bf37", - "0xbd81f8a9a639cf29aae2534b2e7e9de0ca54329360fd6b5263b27d8c9c3320c8", - "0x60923c3f36a0cdd88877ab46cbfc471eb43313059ec3fc814bfc40710efe24b4", - "0x7bed716f7a843c885688ed7af46771588f563b63507f0862e822ebd8077978f1", - "0xdd4a6184be0103d67fcab1ed07b91257b7c337ccf5dfd651967a9bf51de0ad7a", - "0xb675cafb5bedda4ebd58031ea37b88d6a5cdc6c70d2302ef2c8bbca1a941a434", - "0x1e84b7b0320b305d21864238c1d5309029d6f236705e86ba557ac4ab3286306c", - "0x07fb4aee552ed17b6633df226091a7440c6e8fcef8defba0b74dc26e3f63bd70", - "0xaa2af0c4c0e3b5e10c6bd02f9834ea38650d21ee6267f31f59a94b65211ce71c", - "0x0ffd43f9155d4d98d28bc491a351e354a1597aa54180f5a7525800f366205d0a", - "0x47a3c45224065eebf34ac0c544487b07bc81e977380845ca3599af1c9ad1fbde", - "0x582a6e56abeb12b0904f5e72f292ec8eb7087a9ec904f704bbf4fb4b626c761a", - "0x961aa84e2f69cf7cc86af84bc5eebfa47f16f69f31eab4ddbb6e34b4ad78f368", - "0x2fcc76736c384389f7bcf80af928b1b2d5f2a666ca7c7233855a38e2f5fa7a16", - "0xa5f51bfee955689e2cfefec36f6fa11c8c39191a93d55003414649089dafedc0", - "0xc33e8750e8c0c9be14b844b61a6c1c6a05c762935a28811a1fb61b9ce613b8bb", - "0x75db0ee106632fdce0d6dbb847f756344f044ef0783563c41c2212a47e140d1e", - "0xfd48e7fd4bb7e075dec1c506cf23ce165151bdaf95902a4d33e174658c5fa872", - "0xc06795ff7239caed1d216e9c715b56b180c131cd8e7303518c8bdffda6a9a293", - "0x6aa82ce836cf55ca0180fefb94631376ca40e8d0b8929a75a011de0b617ae2f2", - "0xb9b8a68c9c52fee3911f56dcc301138864c45af8157568df0a25f1c4bbdd3985", - "0x6bf761d69df953d56be46933bc155b6d5cef72fb502470a02dd4f376f6642152", - "0x5a1282a8d75bdd8bfe98800b202b31eb8d00e05a7233bd3b94ebb724ccc78902", - "0xf7ba12d853026203a0ffdeea4b2a7f2c765552bc218006802f2fc3fae24718fc", - "0x20cc11d68ba309de6ddcfebf6af9f3dabc109e4f80b37a553652e3860e4e718c", - "0x1a348abe945c24781cc3130841d9bd7cd3f65a4ded62f03ca43e1edf0a3d5d3e", - "0xe49ee1bc38403dbd29dd9f1d9a0c30658766d190a99d88055beb8ebb3bf68255", - "0x8d11133a1e20b7c98a820626c3c7165b99290814807450582a75fb66cbdcc4a8", - "0x82de0eb96093abd04ea54056408859c5df792f9b9fae157912bc1612e946cbc0", - "0x09a05827e3d8ba4ef391d3a59499d3f4f0368725fdfe606b2037c0da1fddd04f", - "0x27afaeb9233d09de28c35d425a826c1f8ed5d9aeaff974e30bc8267119f181da", - "0x8c20e9a837f4ff4791d535285e97e0db6fbbb6f90650ad599ee8cb539fbc6fdc", - "0x145c283aa63d68999c2cf54b3a1db63e7e1c5cdc513f151620b7601af93c43a4", - "0x33a610d432ac33a5afd2d4f45cbc7d2d38c3b2306cf951354e8caaa6c80078d0", - "0x26b9f9916168a3a3824ec188e9fbb4740e38f2f01571cf6f5c5f797515420a6a", - "0x02b0b674e9b1bee1606e1b04d74cb568d257eae593e53291e02e3c63a5a50a62", - "0xad6a8970d964768a29ae2c0a69870ebb86257043282e901a08c83347f2f576d5", - "0xb3432872f6e00304d0c7f697e4d5bc84fbcb0db519b0b2d10bfc59fb7a66d0dd", - "0x801b0b76518a3871d99475a87ffa410966ba140ab06ba70bf1e0f986a75ea99d", - "0xe30da2b86208f67afe68b136fbad8baa2dc2085798178be34793e4fd28276ed7", - "0x56d15e4935bcd3441337544b0dfb1be964594dd15ede8920c8aea890c0f5f3d3", - "0xf2cd6dd6bde075ed422413aa2a4dcf7ea831e9c0b05e72a11284702ff4648695", - "0x8d0d6a0e37eea6274b66eda85cb955a480744eaa0ccbbf6c53a2138c2ee7f6f3", - "0xde0e7560fafd1f6145705ce76500d117e916a549c331466eefb672d5d1bb683c", - "0x378f9c77586f4204fb0eb4dbde4c1cd5212dff3dd00f78ffe4d0a7fa993b4420", - "0x38d96f59c01f5677a8f0ae74c85ab92f403d3e0d458dff74bbe30cf0bba9bb82", - "0xa167bb791f78123df4933c7ce81adead29863c93383fad587e0736eb9f62f96c", - "0x1e2eee0b810ff2e3647d4241f0ae3c10de6ab1a81783b141929ce29f50f1b611", - "0xb3da422014ba7a2b692577d4831ee8a2fa0e93075c6e3022b9fe87e589b03ab3", - "0x36bc055c7f7b0082ed73b69ddcfee537ad59dcb2f79f11f32302c781a44c1138", - "0x7824e636dc9b3c3082dc8d7dbf90b75a6694a149d1451070cee9234d3d1442c3", - "0x3a7cdb84f95330c17cefdc3618f67d67930923ccbc1a9233a303aeb06c900908", - "0xfd200a9779e710a0474fdaa7c2bf426748bc17fe8da08699c0fa863d0dde4252", - "0x0bb5f63f4fa916b3aa8e92537a98a9741dbd0191e8ebfdd1bdedefee6a03ed27", - "0x58d261dd02d2d38c4d9d64834659bd223a694780f8b0cd287694299099f0bb9a", - "0x246c86e5fcb56893162362ac404d498193ffe74b43516848c73e3d2d2f1ec680", - "0xe5094d30072956be53f7be3d77e4bac8ddcd8a73781cefd6731af1ebdcc3b9f2", - "0xfb3c6d47d72af2421fcabc66965595841a373214d33e5ae4decca29928f9b8eb", - "0x8c88883b7846c76de5512b190be454046d88c529ba80800db53fb6bdc9f5c480", - "0xe3d7ba4090d3273ae1d0a79a999b256d888d1893cdaf0f9bd046b01a2d35149f", - "0xd832fbf93a01f4f99da4c824161dac8fb5ba379d7a62bbb11630fa059eba4bc5", - "0x2743d2076ecb58f172ee8f2a7c1dcd3284a86f4b38346898c9a0e30cd1c016fc", - "0x375ba23cdd92078d0c1eb959a84e83f973821b7c5148c78c2b5c911ed89d4a33", - "0x22037d3589911f11598fbee2ceea39758667a6316b6a05dbe016c8f27c0ef556", - "0x077901299ce69ce115289009bbd864d5bc412b227c9d45b0a258b1249d1aec64", - "0x0f3356271576310287a00a52bddc9da2865ee028b30319aa238018d1dacac7cb", - "0x4a3d3a133af48e90299cdc0ce8360a1374c3ba7697def7d71ee7113172244351", - "0x32734db80d04af59061f913484a2fc22f0b13778ef049e02e8f9fb30e1da9d39", - "0xa3a472ea8349299a3378e0ff14369ee28337f73a475ddbf508e74259b998067d", - "0xbf358991d9c5ec46243f526f8c322c70f8eb6e5fd99954cb8dda31406cf492fc", - "0xcdd3269fc0abeebb8e44e74b5990413755665b3f7ea6ec763dc19091eae902d9", - "0xb2347bc4742f37f82069ad24608916ccb17e0b23097b6f50c6ffa672f9d7798f", - "0xc950786f85a20e53d496a7f6f539dc93ed4db152ff3fa07ec9ff545f93b98a2b", - "0x5524afc5be48482b279a86994b227152a9070e998c7a8beb8164024213cbe865", - "0xabc025567a68660848fd0063a75564b9fb1483d038cd5a191fc9bbde69c91374", - "0xec61a7238f379549a2b267fce0884513fcae479bce9aef046298cfd35225301c", - "0x0646761899d9bd075b892cb4b8ba2444fa83a8674013022b784e91808241624d", - "0xbbda1dec49e56ce2d3554dcad78a37693eb6437a5e21cbf05a357c2663f6e144", - "0xe218e9d37ba3f4544dfdc31e3332eff0c8a9b6d3bb10188531d4e2de4eacc3bc", - "0xf708a6e77a00c47a72d9f019165044d827a26d9adf51ea53cccb6693063313e7", - "0x3c9f9d34166dc61e2e7c0e15f0e61b0ca46462f7e136f0705c49524019cee3e4", - "0x0c4616a6f0f847fee88f1b914118c3acd207f7e2d84260db50137d1daa6e891d", - "0x5e34ae26c1072f07b7815190fdd67f5cb2605c849300bf28e9ea6c3eee400f77", - "0x15ae51496b44825cd74fc5d34c964b9e2764115d287e47bac9744eda09f6204a", - "0xcd6a8ea1da8d5eeba387281ae5e3cc2edb14b8c8ebdd87ee604bc09befd0fd6e", - "0xea39450403c2fefde5f54cba19b8fb2845c282d9076bbddcd89c4d030a7d786b", - "0x51a9808cfb305f3d1d2dcb5cd8355ad295ffd193f503313d0c23b631d70c85fd", - "0x94473e8d272904770c25c158fbb2ff05d41de0a1cfd1f0780b99e0c70e6da313", - "0x3b78c5f500f3723cdcd7225a83749aa6a1e1d6678162ea667df496a9a59fe822", - "0x3c02d338e7ab6b10a91e311dd0d7fe83bc62f4fb90559535e15f01a6a94f8908", - "0x254613902cb39d43f8fd4efdbee4ae29ee1f73df829239eca702c4c6b76a8056", - "0xf5a59c3ee350c1b5360a790f713b054bf34370f18e7fb3e68d5d1544dc142b9a", - "0x7d8c61e608461215a5190422cb8cda548f67de7d396c610a7043a12c38797f26", - "0x21f61007950ed1ff63140a6533b5caa66d6afeb768190870a0bfa56bff4ba249", - "0x35221c26f511f1d356dbc6cd33354ab04270417796b29c26cfc208fcc22a1926", - "0x7493f5f0d913201df01cf514d9209fc689be68f3759cf7487a0d9c942a4f0eb9", - "0xa28f9fd0a06f07196bc4cd235f86781811d26ce87ba510e9d089ff0ac48dfa09", - "0x296f7a79477fc94384c240d103d3899a0979bc7680ae6b0c77dba5b40236c5f6", - "0x4f610acedd15c3df59a079a3edf02ba8c3cb1375cb54a86f9b8b7aadd3bacd4a", - "0x78e6df69fbd9a8a51dcc14a9faa99e591f6b65cd7aecdced5e898c3207c88433", - "0xb1c4470ea0cae862dd84e77be0aea69d045149193ee2ede2280aec5fe2bf9290", - "0x2906203e1e907396b983b5ddc8f53c026fd8d278a7d580d770edacd560abf9f4", - "0x172486825d1472fb99a53996de84a6e58e9d1c25ccc9eff81ff660728fd35200", - "0x0d85f1ac03ed46541c171dc9e521d58e50c1059ed14591f3ac01870b0ce6f4fe", - "0xa327265260bf6252279cb97e7dbf25a423cf979d347251e2cd548f902aaa5333", - "0xd2465f53b0d477871e9979b56e1c4de2fb81f5ea6657d26d9142ba450f60f9d8", - "0x11c5da85c8ef6bc2afb51fb8c85d3e15e4dded355a55bcca7ab03fb9addfd9bc", - "0x8ae739acf676fefc410ffacb01d9e588fd94df577eb88c86b29bc9b97fd0b39f", - "0x9b626245152f0c1fe45650dbea9edf10cd7a4f4a9b9f472a4f5f58365b736b7b", - "0x2ad3f303486ec33231a94fe6899f8a6881c6273556afb9d43df10557ecaa2625", - "0x815535b3b87aa7cdb45dfb6e08248f73d50d2e97b3919b7c6d4be0db82ba736c", - "0xc5b2ee3ef00a78ddd19411529d9df0415b598d8bf8b0e9be14e0db3abf75b538", - "0x4a43c2151e9bc477dcd23bb690f59f764d1b4f40b434d0bbd229c2fa33187793", - "0xc98e003d7f67589e42dde11b6e5c4337e56ba5379ed36ba64e328140b66809b3", - "0x548d00d6d57076dad3ba99c12446fd704459ae26efb2f32f31ed8cc002d70472", - "0x220074ab3e4ab2b1478ea4e671da8f4a29cb63820cf444b8b8c80dbf55dc7ef7", - "0xa605579bdd11648f0569166a6b6b9fff1c5f5314e4a4443fb45e779b6ea67f23", - "0xbbd91c03ba7a25c5ed6d2c3efaadb9beac37bc41371d1627061bbd1b07bba2cf", - "0xe5a0b22f3733c62c253ec1d438a162bb523d600078b137ab64a68acbabbfd591", - "0xd62697c4e5a86182930e5f03370ee5ae429897299b0a089942d0affad839c396", - "0x441bd17de05582bccadff86e92e1093066042c74ee6dcd2a3d372fef2464a25f", - "0xa1c7340ef8700794a780d623c61611ec782a28e89744f2b438cccd582111ad84", - "0x8325267cbc3f24ec1ca175420d0a1efe55f32bba8856cd5649a06c1c2dda7d6b", - "0xa10890568036a3d81ead9346e8b5d272ea5e30425351baf87fbc87c85caf12ed", - "0xf99feda98b47b71aa95ee45df084689e805639fa816c19726e8a3a7bd4fa714e", - "0xafde69e66a08f8d67df3328ffa50bbff36e596342becb1b30de9d63c7ddbe779", - "0xe6b92570ceedb890e38cc820fde3a63f08ad816fe36690a8e0e3d590d2fd767d", - "0x8d8127ae7b04e1c40e690b67e6b0ec7f84ca3048579826f66e6c2f931d0e158c", - "0xbcc4f1260c39444a096c6ad54fdf3b83ace0c5b88d1ed08a2fc36539b50ee6e0", - "0x186074682f41df28b76a245dc37378308fe996b6b1c033daf582bdfdf6a99593", - "0x62d93c7126ac1ae0d815460a87ce1b7a2bacabdd7ce02fe34d4d8c1a62960a9a", - "0x5b6a7a8fce8f9f7181806ab5a3c84a5f60a7bc4ead1247735a43b08d225195fe", - "0xc848fb9925e445d3595c338197ac7d31296d9247f43f2a604494bd63e3ce78df", - "0xf40966a6c7666339a4c136186fd753079739732c2bb724cdcb6047a5e5d2ef1b", - "0x6d8c1765efdff94ab61b3b4872c1acae846e8a98c763fc7924cbb27865295deb", - "0xdda74969fd0e49dc1e9bbc04f8e3dd952ce0e9a98041c7599058bf481b57da8a", - "0x72ff550e2a8f2bc45918e8fc1deb14e4309eb8cf03629ec83217f06e457dabb0", - "0xe03fa58b28a8e1b601846bfccfdec6a290accdc467898caf0be38acf0cbe8e30", - "0x75c3bfbfe321a63b96c376fd1cd3dff0d06308c1749c5544ea7bc36b0dd223f5", - "0x75880a9c922a15652b74b9114bba464ac49626fe1df76d3aff3e6def1f8a9ff4", - "0xec8655c4f3280725ef24930f547a23988f57b407ca7962477921c65aa8d582d4", - "0xaf3830d825b5ba849d7a7fd8de3d74683191ca71eee0c5dc13fcd45892ae739c", - "0xacc6f31736270b88b54c0b51afc6c3d4388769a9a76befc1fe2f8c72e4e62c56", - "0xa534247fc228dab8d5d42a2d8aae86d854deb257a81c7ff19131656a3f1aceee", - "0xe2895527939deb6f2e6367642c0b6b13caa8a9888338ede14c60bf12bb5a54da", - "0xe59ac0c8184eb0936d6f5eaab44aefb441e5e2d1fe69e290868245b04935d924", - "0x48d2dd98705594a63855f3c4ebe02695f6f67b89703c3abeabdc11691d6aa10d", - "0x7e58bdf9588e2449e7ea947d05ea19c698450df9cd188882a23f5007aaca9e7d", - "0xa0974b5f82e5c03e874a4edb97557450a5280904c35d863b5f14eb1860caa681", - "0x4ddd9abaa7c78129efcbd0a47611b735c69d876626e7b13a5c6b6cde4ef6b744", - "0x065e3d364e47bd0f98ba30a89411954ea051e38b487d4344beb6fe59f5bb191d", - "0x4732727e9fd520893173d2a334a0fe55ac63dd9b49956eb13b2fd89a6e686f82", - "0xceb1f7c0b101f66014a7bb3483828026af59328c0a192eaf6e7c04eec97c4077", - "0x87d17806635d4f607567d24f1267b4a55156b92d4021d22dcc312af69a9f4e4f", - "0x1a865d70134d23472644fc40d4ba988f5e9ce3d24c43c39eff3fbdf80ad6a2e4", - "0x757160e251b8c79293c218ea60e6d3b453bcfe8176993a35ab69b067c5e9e2f2", - "0x1590a6f60eb7ade8bf7ff3d1f00bde5e06e1333b1c9c7ab3aacfb7b0d2ccb535", - "0x4262d42ffe1b9b0036b63a4c70df1d9bf0dad17cc646cab635757e9bb97804f8", - "0xdd1e6c9526efe954559d889158c5a0acc6ec7b9184b4323bfdd3205a25863424", - "0x3d713007089684f50ac3bbd9bb4b88dc17652ba2ad0e1e4e980e25ecaee6c63e", - "0xe031bc30250931e60bd8a220659fc868c8b966c34c94c65d981ea99867baf8ce", - "0x646d9776e8465e4cbad03ec86c25704d39f2dee031dff68d8fe4675da0f3a3c3", - "0x73130be5f9c034f7cef8e297497e6d3905ad9ca28ddc6ae4606b2900269204bd", - "0x76e319544cf0c7c037d7e7dfc5a22cbe8e0f08390f93b985942953c1c695ad4d", - "0xf5e6b2572536543da031afe3d1a085191cb363405b2290c95ea2b58a2777502e", - "0x07c89313a0f9582709438509df2eff3dd71d063cbd7465e5e704915583b105af", - "0x4bb2017c31178ebf59a614f3fabf7c3c4a0b52fc560be65a9ca018dd10fafd20", - "0x1aa73d4ca741cbee068e178e9457806cee040aeac414f407ec03be19c57d1427", - "0xc6f1b04e3b6d58a01a3f08a5adbbc29d531301e152b9e952268624472c4e53bd", - "0x99fc97c755b1307b937fec8fe4a90f874f62a734a19761976ed178dd1f66a6b7", - "0x1f60601cfebff679cd55ea8d164984a902aee16f9409916e30f12b83d9d8ddb9", - "0x2777d5ed02d18c45209d28adde18152b50565fccffcbf792fa9ad08092ba4f5f", - "0x081ad560b5fe6c1102d728e35b89294da36e6f2abfa7487b069824ce07e31067", - "0x4348d38b71aac331b929c1db4e33a7e3ee650dc7d7c7944b24b9c815709864f0", - "0x6522d302c92d109bb7a481ff1ecc01b9f9415ea1e447e91721959f5cfc64b6d2", - "0xe6e8719ed1687434dc226626d83e6a8cd317418e3b8feb41b677087e9c848658", - "0xe05f788540f52a58df08bb86344e1a0b8bff2c29cb4cca07b277e729ae20be35", - "0xc939ff4b5cae98c9b65bae38a2a9f0ecf7fab9dffd9d89f4c3c43a645b6b0ef9", - "0x3da552ed48e9cdf2325e58320e79083cd45c6c891e1ca95f30bd9a6cd74dca79", - "0x46543e616a4fecf94cd4d92ec83a836e072d6e075272ab3626927f661647bb42", - "0x10fb4585c8ac92719b0dcf6d735fcf3cfdd3fbd5a56acd53be227a3f96df61a0", - "0xeb58e384a7f55adcc5269da55ef4bde232dd9b4179757619f75a8bdba729877b", - "0x1bc53b776541a38ddf5aa1e21137a94fdba7a76f76b511b767474bcc8d6ede98", - "0x8d7b8db0737d9ee9612f232bdcd1c35be0a8536dd2bea3b4cdf79813391903e7", - "0x36a2279bdc7e5d17c8ba0479b563a5d3304f6e2ff472d534ee698315706cd4e5", - "0x49c45d4b2e3d01230ce8072d8b55f25b715b87a0f8b6c82cf420429435e98133", - "0xa62973c2b8c9e0bbfd28137a2622256e5094dd6b69003a84cb13ec22c57cd41c", - "0x0c9552fc3306720151bef2a6b13a24560b932e0d3db36b1b167d1afd8aa7e341", - "0xd40c221db7809051f8204e132793a7998f7c1b285c3a2f52a1de9d0a0262b703", - "0x77bd0c7abfb7ba1178d75537eef04a618a61e0285a1f4913d414dc1191fec41f", - "0x6d865ddc6512a32b54671ff7c9732326e967aa0deea9ecfbe811be8a66247c6b", - "0x64e4787de14840b123064911dcf83727683f4b59a1a60b2dbd2301aa50e150b1", - "0x1726f22f71e69d3e919702809f5be7b4a855f127427164211bbb83712899bb60", - "0x7a0ce836320b43e8fdb8b1789af8a856f97ed401167309c8b0eb7a99f73bf41b", - "0xcff5ec573970e3984409dd50d911dae041fc3a51d2035c481985f2d36a40a018", - "0xe308ccfd734d243298f65be9dedcf0320e5fc6ffe28bc18a4abf7cf30869fbb9", - "0x6465d11dfd96a8c36c86265748197b9ed81056189467520d048e320da8addedb", - "0x6da91a602c5b73550abac25299b46a2f8727c5f156c1a7ef6912cc30ac60247f", - "0xdf85ef603adbe64ad1d06d903bec6d0a136ec88bf4788acb4b1d6ef256c4f342", - "0xfbf21ccc3a80affb14aa36fc08caf0cc6babb271becbf5d7f8778fc2e8e2cac1", - "0xc9f2bcb111dcbc477ae94b7ad5cdb7747aed186c8336dac2f04c41efb8d9bc68", - "0x54eeadf4dc7b7d9aca271a0e1b86b1a2cec0edb59da9a267d2254a635bb888b4", - "0xcab9c1cb0ed4ee12496d612fede759e40ca4937d21a370cddfca557933795f5e", - "0xaa53a4d43024db7d7d55581b7c690d2894661b137dec89a667ee60a0066df278", - "0xe947f3e7cb27ee9acb3e21771fee420b1b8d95e8deead159fae0cf3d3bceaafe", - "0x9111f637e6470bc6b200fa0af8314fbc2a649432f665fe505ed6c35782941cc7", - "0xe07f2cddcc582df384760910f1e3b01d25c4fb76110dfdcc565e89c179317484", - "0xfe9f06aea3dddcdf1baab052506a89a671354e36ad00b0838414927e2cf7fcc3", - "0x84c6fe378564403f23d29c3d15dd1b15e6d376fcb9a95dcf6bbddbb32e18f154", - "0xd31439e688f850e75895edcd6af76c55a2d9288bca689c6955e3f83e621ab750", - "0xdcc931be9f3ceeb04edb4052abe56b79fbddd84eca955392801c527923178d6e", - "0x72c462d90b888f78410307496dd042aa6aada8de17e2b112bb10f245adb89f88", - "0x3bb25b18d8fcba475e66562b6a685a18b1ddfde6c8bd16bce45e0dfbc3a54467", - "0xe4678a3925c238589fbc5dd61fa3b22f8b6893b206291c5982ac9ad395f45724", - "0x1d493abec856ba109aa3e21afb621510c1abd38875ef1decb5d790f303d8322d", - "0xc8b1820789f1aadc67b6cc5f7f9a0b9018bb4aa274708621c036cf670db899a6", - "0x116d77b470c6d37dafbe4d2dc110d2dd92420891f18c2f5b8f43ed9341304502", - "0xa9b47217de308165e87d8a2df2778469b4304c39cd1d743bebdefc7eae435832", - "0xbb1e6776f78c94e0c37119952ab925acfd70cd9c9b5ec8d2286cceaad547d537", - "0xfbcaac553ac391cfc5578cfde41837465a132606fdde27e93706c64e4024b4bf", - "0xb39f16fc4baf58a30a6cc625bba7a6ef7427541fd3f44ec158463bd74494f8cf", - "0xf4a3704d5217afba230999e16a8af8c0df77a236eae8fc250aae3b0ef10c704b", - "0xb1a8cdad34ec1ef934bdf49dbc910b4f23346fd6fec4ab15617c77b539b9dfec", - "0xe773a6a957a4594421c4b682748df2ce0924c8299caf31e821d7f2da89ef1b0d", - "0x80703bf02ae8c47b20cdf35017460945d31d43c9004307a0f68fe15a79ff4422", - "0x000aac49cfcf7479f54da63f6cfb15c358e58cc50dcd4f178c1d48c7cfc70ac6", - "0x37359a207f2d1a5d483485e175a25ef1dbd1597593489e03da0855d5b73978f7", - "0x36649fa2d11c71a4e009fb10e118d00e6bcfb4947c7da998042766aa65509e2e", - "0xdd367d7be52578d809f2117f011bbfab36e9f10025248764f65b49c3e4c6f2e2", - "0x1100529aba908d0134d124c320ecc5ae5f68e55e2c83d81e153b9b84730e0348", - "0x12b9176f9dfacc4c0e07b233a0eeac51661c9ca7bd8534bb324c72dae99b8869", - "0x141374e0e24f1d70dfb9fcd8f49f42de1208cb7268d18bf11dbdf60047a4373d", - "0x5bc84c71853b2f6ec0e7733d7b12cdf644af07746881174157d25046a3cb0c15", - "0xc0b190e2f49eb4a8496985ac98feeecb61f8f103110cbf4b0f404f086eb90dc3", - "0x1baed721a6b29aaf434b0de72486e92c5dac340f846d0db2533aa74ab58c2fe6", - "0x7bf7d41ee40b8a20bf80ab71b9674ff474a9c0b2b0b3b768f4d177b118f55021", - "0x461bbf7146e4e95ea95add280d7b7199a5dabe4bf369dd0bbb6d74eb363eb399", - "0x38728b104efd235331b7e0e2cd519adcb8333682fd48e404dab19b5a0fdc1eba", - "0x841b6488a00830c28b4b1517134f01a071015124a6202abd48d7440b5f6464ce", - "0x00b07f0c3e6fe366d115950cace606137c284c418e6353adca18df07b4224183", - "0x09bf2e8d2d2ba69603ee2aadc58f87dee12a0a07e091c7cb98e18c00242bfe98", - "0xa94a6b1b9cfc8c6efd37e74635630eb2a5f4dbe1a6cea2d2f0b9ada691f49534", - "0x995162698367b47f8eafb9f2de2e8261ef17ebd9ff4565d6578dd32021b4b49c", - "0x51728725af93c6fab7f955cae9158aa0cae132b7977efb080320bd5540d4a246", - "0x7a901c710bb225aeb851f1f5b92fcf3dd1526018f34bf94cfbf0081b9e2bc252", - "0xff8057f4427717a343ac8a056043fc6f80ace0a9166d263a8d12c52c7d985148", - "0x3f2a6a04434fa68093777e69a0648da8ba8ae27b6264ae26f91a0322ae6b4273", - "0x875175a87bddd42be4f2115630976f143b7804da25737dde75f373a9da7db767", - "0x0ef0d47ca0cb90186b0db9b7f69438dd7d416414833b993d1facea8c190a2cfe", - "0xe70920e1e8f41cc04d34b2381747674d64dd87c892dca35cce6be3c21c5ff74c", - "0xf6bf1b5438ad9b45d88310b11340fd5f961e0c3f97d25cdab298466485ff70af", - "0xa86b8867cca790d03b4508e46425705b5755071f6b196a484edb26eb77d669a2", - "0xb2462de4b1149fe1c462cd4a944c5800209426be3196d28432d22d928a6e3b56", - "0xdde87562cd130db3a4cbdeb300e7eef5ecd884751b9692b3530253da3b426d3a", - "0xef6f3bcf6cd610dce1327131251c1febcfb2c8be013a2cdeeeea1d7ed2145279", - "0x49c449580aebe61fdbbe7b659956cdff5d34a3d7078f2d531e1dbf9e78fc707f", - "0xa7d87aa0df5d0f8c84f0a11ee2c5d00acf63cdbf98be9244ab37ee5c705c1991", - "0xa8ebb8e6ad2015dace964ede93c8014d4fa3439aab2cdca09100c337be0c87f7", - "0xa5e855d01efd43ce43dbf196eb94199ca4206ae95fa293d2d43dc58ab69eb5dc", - "0xde56b2630dd3988a67d4c1a8bbfcbbcd90c99f0162ce697df8ba5a2fafb38681", - "0xd1d6195544c51fb80f41bc2ee4ee73e44536d8b2d2f478d719d72f0b7880c09e", - "0x2691de4f1ef0d2451f07454d5419abeb80502f4460bf365984d46a7343d47239", - "0x3eed848da0595777b65d377857ef1097d7c0e9d812bc0b1c334e94a3cddcadf2", - "0x82b5df95601fcd73e1aca3706974e9aa9f704d02715ebd750fd0603522850e72", - "0xd4597a1d9e91391da18fa148006db4f439ed269360a8415b35ef74eb3e767f5a", - "0x3fa8dfbe6d64ba346d07559d4698263e63d8bd456f114eae1416bb993d99e64f", - "0x4336ba517815c335dec9a08e7d1857fd3e668a12dc8abb0085193cc6dfb60463", - "0x10939a82565c112b767fa5d4c5628a670c5623f1c417a175d09becb077aba66a", - "0xf3211211715089b537ae8c270d7bc0952bc26994d070ae4692f1aa4c850f1747", - "0x719010b1fea904cb96f56b5a16c730839f030fe8f967595a2647b57b67a7ffb5", - "0xf6c883fc56e57a70259f1c1b56e94581efbc18115a13e7a83560e32458a78c6d", - "0xaea535f44b812fae7f3a7e216d695c4b709cfce4869ee4b0470c6df99a6abb06", - "0x9e659a2d3d72b13a65fea0396623edd3c2491d309ab195ddfde6e83832bf6764", - "0x35047f5b4aa180f07d47273ab0d9ea3dac5bcaf5ff3a80ae713427a3aaba2f1e", - "0xbb35cec8f1b8fc9707994df6b93dba18342156add6290dc6f906c8c06988b080", - "0xce5aae3a026f15cddd8bcb904ae984b27364c85a1db01669eb437a1d2ec116db", - "0x5b80480132259b9f159917b9af112113ff6ab2c9cd3587080c4b45118c9f5912", - "0x20ee0b82e04f86a6936971893aae1dd6c9f4a03b78d0ca6e5af1d195d8779b7c", - "0xa4a90b7855d6fe80b403c5cdc32405ef1156ab83015d8a1634560b4046aae931", - "0x32e97984751c36a6ab0f651b4675d70c8b4ee461afa7daae6bbc65760ec7029d", - "0xc1c17b3f900bbbaf392ba82c480c90b706079ae1c559f5edbe9e8cd56b0f41f2", - "0x4314478c7404a573d1e17600f07209b331917459c6756de910a81071f4e468b6", - "0x6256928f29827180db86269ec33ca31e510b8c2bc287aec00281bb4599cffda1", - "0x91ce6107d89e783bfd9887e9f2cf180e029c86886287357cc36cfc29fdfad94c", - "0xd5937778af97763ed36c35a9955b1f840c5bab2032b3418c11537f896596af75", - "0xbc3580fb9dccc7f6a214e4bd3c0c09e2e9a8b8a8e2de20c841082465e9fd64a5", - "0x3a1936cb41e17890be0e162ea95ab908f0612c20fec62c22c2bd7ff1947d0482", - "0xfc4c9e86ee1f824fb446a966073bff965fd116b1618904e9a8397cd586bc7107", - "0x6c66246c94ad6c8e2b8b7bc90690775338c238cdf431e53f4382244256a5bb8e", - "0x3e56991487b66b41f209ee749d817dfcdc1aea634ffb0b2125d4a086053f9a5d", - "0x6bcfbd7381295875e2d7a2f1d35a96320b0d222ce8187987b9ce693d1ce0a5e2", - "0x4580fecc6d2f4f9e68053b3067b931b252e5140598120fc3d593ae2add79da85", - "0x69afbc59d9d5f2aea003558cfc32bc0f59fbb28bcbf048e83e67dacb2f051a9e", - "0x6f20a07738f02c250baf47779acab06b31ed2713891621cbdbc3a33cb1a0f1bb", - "0xec3ae039172c3f35b12d185048c7ae3e9ebaa9afe3b01fbe9af9eaa8d132ab41", - "0xf8953caf4a7078796d54a797424db24f42e22a1257110f429de39365f87eb167", - "0xf83d0408a57377c2ef00481c15f0fa35ff5bbc53913456f2ffdd074614b728d2", - "0x120474940e643c946c9b1d0411ebe85441681cf7674dafc7f437085b78a26fae", - "0x7639039a138a51df89c0c06c08353ff3baa86bb5f4846860b62727ac9e98b75a", - "0x0c334c25b7bc1d64748cf6b1a21b64ca859762c239b2bb63aa66f0192bbe9e82", - "0x91bc6284b38b103e31cf20d99b8f94a5f2a8714e05ac021b8139e51ed97034f7", - "0xa119da5d5daf11ec238315f7eb7153c980b7df420896759b05d6e0cb9c15b099", - "0x478774c200ade9d6bcd6ee55006191dcd262829396f5d1a33c5104838fe6a998", - "0x72fdd84d4358dfb09c172e0e91f57ef0706337e9e9e6de1faf28a5ad1993cbaa", - "0x40d5695aa29352d0b03f4e367f03d50996a7091f5210774bea024421b4ea2efc", - "0x5ad627cc00bc5fca0ad8ddf5a09d4e310948c41c54d8650da28817efa05fd8d3", - "0x090a72d69f2255810ac423e447d0118afe8fd4d656d198f9af6fcf16725c2cb6", - "0x16ab98a77e42ca0084d5b6f0962c8d9729569a3ee5468f404b31046d710be31f", - "0x40f3eed9711a132dc483b6a1f911d386228e67b12d23d9b30c4f476bc7869290", - "0xa92722e58027878eec3a115f41467b420af8c4ec901f8fd8b2cda0208e526deb", - "0x1f341f622cba201761ace6fc164b5ffa90c32cca3379a9bcdb0b215d347b2c1e", - "0xff49ac00c27cbbf5da17956b19371e5ae8a288fd6ff7562b7f8f55d6bb356bdb", - "0x6a856bf3855f801748f3c259465a30574eb720db4e4547feaf9a6a3c8fba9b66", - "0x4367b7f1597bfac50a191888271edb84e9ccbe98340a3e896abc1410f6c2fd49", - "0x3a5dffd2b7db4b4437983e21126581c6f0e5b2b2fd7a62e3b51caaf415a7488b", - "0xb96ba50acd12ffd0e253514b8a9d9f8a2387a35f6eda3a63666e7a0bb5a74536", - "0xedc56a3af991603540ab6d8ac47c383e5429213abe2bcfa6e48ff9b7ac4df978", - "0xd9248991faddf6a9e2939921f8129a2b88ed785c0f0e41a11c16ff2ecc76945e", - "0x788ffda4f94d6cd932b41956dc6d48c04b17e75a019ddc66a452ee0be582d324", - "0xf755360190f6621f8d319a0a3169b62d0ad47561474b705cda88ba44b4ff7f22", - "0x412e8de38edabd1156e6c9f948c9f9aa0c896499f77aa5332b000cc70053d868", - "0x8ed82eae843f37e7b41dad6a309b6e2deb3e7ba09ecda1083e3896e593bf38ac", - "0x450ee983d78f5da457d1d44e83c2994194bf0f88d16365385da07a6475dc278a", - "0x068f39349ae7b94ba18f55e5c56a4d0f4fee5f3d6506c91dfa486956b886cef3", - "0x1e0ba420bf0a554523d73b0d7dde4ce1fe813feb445dad74913fc0d60a2fcdde", - "0x51dc8f1fe4e01e1ab6577a8e82506dad9f0269dcb4fd3f355edcea3565c2759c", - "0xb1dab28ba4711c54adf76b9606b6d7dcf2b9c6e2b5957e9e97350e5ba703a3b4", - "0x51234eb88a707e5d6844d09c1549a2381335d49cdd03dfa92506713724c25591", - "0xfdae48e9ed96dcb8eb3ccc35c84a342e9e5314a8bb04e5e9dde16e9281cb918a", - "0xf9bc67cfc4401b08c467a61590909fff1442eca8f0be2780f28a5a9116a08655", - "0x53212f1b630a1e52f27d7698ba7bc9af6b94178a7e57ff8861a8c9d0ca9924aa", - "0xad4b53eac401436504a3768f2784fb72ad48ec2ae1fd6f80e842a3fe26836be3", - "0x11eca84857ac81b5085dd50a4a10c8f032150c215ab8229ef4335fa8c6340465", - "0x41c288fdc4d9a9406e987d90b11a3728ed3c9a77e20bb7e5ce34d62ca5166b7f", - "0xa73bd71d818dafe4633209ef735c6d58b086b8c805442bb57fc46ceba9a7517d", - "0x80cf0b2ca45900bd5705c14c025c53e1cd4f180130060e8fa8f7112074300c1f", - "0x5795c53dbb9fe7e3a9b3998fc213a4961d6716d58e2c9727d3aa2d5ff7758cc6", - "0x18d7ea60dfd5e763aeddd204fd2654b52cce96a4737e665e67a952d7f9d40a32", - "0x88955311540d5b5333c728f976f4bfce14a07839fd3c4d9d8488a12cf99c38d0", - "0xf818504defde32f54d398b607eceaa073e33f161eb48f606cdcc203c38490577", - "0x845a911cadbadd8800dad80b820fe953953df4d0e2d5cae24bcc2027a6ddc08a", - "0x35a1001cda605ea277494b223a6835667b8ecfe4b5f733444bfafc760efc2d12", - "0xbdfed38c959847df8ac6f43a301355fc1ee46ce0906da434a4db7d05ecf1688b", - "0x5f1d4b9dd2efbb6bd0649c23f214b055a14f02643bb75b14754cc2af4901825a", - "0x6a8d121588d21dfe2e94a38bf6aae3de47e513917a61f5b2d75fb5ffd391610a", - "0x0dc0cfebad3445a400760669f16d711dad869a4e87bf579d0745e6307b1203c4", - "0xee23bf9111193c3cfa4173a224752c6e8c52e2b87327d6aa6164cea231d29b6e", - "0x37a2e1540239c7ccfa424d72f2092601873e8d0ade78e4c5b422a7a3e5ad3bb9", - "0xcd9b85e253cf016c1e1d8a6479387c1d6e460bed771e6283462064c377aade65", - "0xa1e924b638bd740dc0a8710c188d3b266e3e40fd1b1711dc3f30234051d1c552", - "0x6cf65402fc320eb643b795fcf3878732799389d54f59ca521c9d265f93b5ca9d", - "0x88c6a984e2d74c2d6ed90de3e7341764e2c03e640226c36d895254458f18799c", - "0xb8947441ed6817ddb0206da8ce4eedfaa41adcc461f13dd260559f92cac0507f", - "0x92f4e68423c159cee14f2feaeff830ffd973c386bb9e09a8620024a36814d858", - "0x673d7a32c6c60c83431915bb032bb0e41f26bb0175ecae230041070c62a09042", - "0x7229d4e3eae51188ccd8cd1c65dcef8cfcfd42ecf39edbaafdc6ec49b43a8afe", - "0xc1087cfd4fcf9f1447f22ccaff5acf35148162f06957002f0a49e85cb703c1bb", - "0x08c0e8cefeec470ed1c4de8117a55c9ba975e3ea485a284b4944adde16a69c5f", - "0x3c3311722c540c28ff86b1ba17c830c537b5eb8fb60f3957404c3da7746768d4", - "0x2db506655cdd06836d09a478ca79cee997f3dbc58bffd49d89b91f449b7b04a1", - "0x9b648bebb498ff6c37d4101cd78f1b3982c54dbcf9fe2c29ebfd2f9b7e04f099", - "0x82c680a2a3eac1238adddb76f6f18bd28bb4818fb30cde24863dc197dc8ec13e", - "0xcc8ed40abb13449df851c4694088283228a9eebc5d3e30bfb251abc02076bc9b", - "0x6b7b737c5a012fcebc781999145113a89e44f1275938201ebbaaed865b78fbac", - "0x795483f3990d7056e6557356e0ebcd0c1a305534b9ad22c40977a471ff765008", - "0xd50325af06de4c2dd53c51a1b68a57b23a1dc9ba1a1ab01299bf3da48e0263c7", - "0x481ddc79fd4eafaa1405c85748602a4012a3ce668021399a309ad7530f17d1df", - "0x776e02940d65eda493f5925362bbecb2e19b088fd51a044c5b5091bc0908edd5", - "0x89dcec4b0df855daa6ee38c26228d1f0f83e3efbcdb8c9f743ee544559f880ed", - "0xd888802df592a2c8779432354f50abbfd89e826795bc3ad204a8329031e0f612", - "0xf148daf80df025a45bd6767db4f260685ea2c8efca1f4f2e1f07a1b46eaaebb2", - "0x42f87796c2eae0d78af26581f92960e76582636705fa747d52f94b5ff9dd672c", - "0x5a7c25f42a4821d4c2a8621c8fddbb0818df1507fceb2f9bce29b737864e69e5", - "0x7f339843b3a45076d4bbac3edce392ce978318ce4fe363a1a5e8a851229fea4c", - "0x66d1bfef48f6b3c695bcce0e1c406bfb413cec8358c62e718880e9531a2e1188", - "0x1672dc0afd5f480fafa3d5d76632e9fd24bcf2f791c03d4b79ce26f2a6b43c40", - "0xc00de487ae1557f6dec118421611a654c3b6e9315f807e18e9285ab698d62de4", - "0x3fd80e20d89463b793ee947515aaf2478bfaf3b76c476b57c80f594b3601a4c3", - "0xeb5dc70e6d66098fe7f431ec8438c3c0ef664fc449d228fb3717cd643b0662ea", - "0xc95e9fc217820b220a1dfc8d80555f3abde3f7811f7719084d5d1439a65b8cb1", - "0xe80d667231fec8e334dd88133150bc6e5aef23d4dee2b15ff9815c191ce30b5c", - "0xb35c99fbd6e410abd0496238bf7e66d68b2c452848d852a46ebbfece862c6f46", - "0xd434d46f3e94dad8a411881bcae2db9cb99506ac8a0354c50586cc81c1867f66", - "0x360bb1940475abbe7168c9de22e43e1e711883e9fb614419dfc23ee3b301c1d1", - "0x8cbea95b1ddf914937d23916db6083e6044b8c52617a71ed140cbf884dca696a", - "0x3cfc521239386fdb6c7c02dbf20960672c5118b5273bf51890df9ad31a388c99", - "0x6e73b52562133ec4a8cd389a352af64705c2a1318ae99b2dfc78dcff1d72319d", - "0x86e6f9bd9edcca335b9169738198aef3a541a6b41bf58274429a29cd0b3924a8", - "0x9668e3aa0d09467be183c811485203df5e10f4e332b2f033f4cda7369d90aa3f", - "0x4b5e3df053f882204387151c1e7120c0f7086a9edba0d3c2c3a505c0d61802c5", - "0x634c9245fe89685be14edb30de2543a878c97fd7506a9b186df5e064b286165c", - "0x10719cf7fb4a7eb4b70192a8494dc071ffec3fd37a17b7ceb10bbacc4cd3c132", - "0x36766864b6d50c50ac2b75e2b335a2b143a1954d61f57e7827cc205c33dacb8b", - "0x4160390fedc6faa039c11711fc2b9fddb40968b534b370ceed6e37e37a0fa437", - "0xe1a28cfb5ae74d7adc0e0e0dacf0f0e197af1ab27626de8b5a56f4dae938a8e6", - "0xc2251525dcd1a93c21f4911f525f8ccfc36a6e4f90b4e275c8bb788522607618", - "0xad403b7913b7d705db6628354f8621dacdd4c91b6bb60b747e6ba6080e37e7e8", - "0x2f67cb9ecf5c4678d1d27779730833825f0bba23ddb3b3c2090e22ad7deef26d", - "0x07df980ce9fb75bf368e179a5cb7b4af1c695aebb0342f25d00e421016281687", - "0x8a56b31e9b9d3c6b2b153a32c99ef4ef65ac34911da645a4556d5813d6505283", - "0xc49d0910f95d400041dcbc98d8e2d95261c2cc9054c8c36a94cd18042794268b", - "0x2030bbd6de4e34c41df9a5471601df5980773ffd594fb8a80c2619aa20a7d427", - "0xfb1ef5d538b7db8c1b712fbd6002357fe95aa9009ddde8df604eccbd66b57a0d", - "0x7a623749e4bc69d3c1ab3670dfc992e263278714c8876515a99a06ffd91f63a0", - "0xceb9b81c22f6808affbfd3a9e049d7e3890f2b54da827d91a1de11e790272c3b", - "0xd68fc65ea61dcf560631c3ef0f5c078086fd6c7b6b4c0d0ebb231e2a07a4a230", - "0x36dc7267df912c83c92ed9f53ead40bc2ef241bdfc62f0d7a4c24e9aa8568c2a", - "0x3abd8e1e1950b65ca1198bef4bd671546ba59600f017a63ecc656070f77624e9", - "0xcf65057e90df4030c8d37724fe2a81f00728774c9965302aa90f8cea36cfabc6", - "0x7c96f5fee074c4cccb1db5b3b396623a28e41a430ed4c5aa51175f274e65d1ce", - "0xde71773646c63b3a68ed6d6ff26f1760b68e497d1579d0de51e372ce74d6db98", - "0xf0fc0c493d23d02a7822ca306c739608929b4be780125f8be038b3314f57de3e", - "0x6230ecdc6b41bb4e2cdceb84f474c0845a1762aa5a3bbaa6ee11813be6f1c9f0", - "0xb201eb0d085a065c6371973c2c9b13bb783bff3fa888f7f06e7760f8ea31e4c2", - "0x1b2055c094924aa007c23f8970029dd6a4e4042efbd1cb5876092de0abae3af8", - "0xf4adf2731dd675c33673e0a2a0e964f14fc9c26081e7a3d9dc9567b61f194a9a", - "0xa1b61107f1a84d5c629a6126eda275fbc2eb34ed171129d432654345cc5a368a", - "0xbaaab04bce4baa421f76a029729617d0ad72de8926632aeb5e8050deb16c3cb7", - "0xf2555f067f8855d2381b298fd78a308262fe8bfc0fe6807a5cb923dab9a0f519", - "0x86dd603d29b186cbdef1cf6958e9d37ad8f8092aeef7f19f5539853fa26b58d3", - "0xa23cb2599d49f0e93608835c375386936678ac58ae14d87c112b86f0694b5596", - "0xd358582a0445ad0cf49dcf89e5945fba4f62aa42311b2a31eaac068817f555ce", - "0x860ecd3efc5d8dbc40e5cc6786859ca07c85fe40ca959523739b62b0e4e0fee1", - "0xc772b9adacbaaefecdf5ae2ecddcb9a6c2cdc141a211875465d3bd29b00e14fb", - "0xafb210d709cb8b0ba068a9f8fccc5d138e1f84d03d26a6b3d854addab236ec95", - "0xbdc3cb79641e51c9f707d159c9d88c2026258b6f73e65471c0c25527969035bc", - "0x5c13fae2b3043759ccaf48d9f12545ea32c27bf7bb66e6f619eae2f417bf821b", - "0x68a007f921b6e70f29a1d6d16f9656f07476f371fbf9a1c1a7acb5c2b609bb30", - "0x183850e4f92d307db4ae75ba4695f24bef26eba825b4a51575f51fab0e1a6f27", - "0xfff3b9a3742d7ef534f50a079cbcc55833faef098818277d6ca6371ca3d753a9", - "0x78ef8ce32497df07a7bec9add4b07eb81b42590f02597249538075e41ab6dd2c", - "0xe58fa1651c0f44e0926bed37c52a60665d732128a34db9703830cf95df0e7e67", - "0x6b7b688c0b98a58621392de01a9bff28e86115cb9a4f884326d29a2d21667bd1", - "0xb5df489fba833ac4828f5b3283289eb327feb115de64e7b71b9d3128762be589", - "0xd12a2a88f3b740960fc2155dcd81bea7e8ed81a4d02a78ba18e98aec67161783", - "0x0b22142f3c76af00919230cbc5b00d7d263c5e85647d2de8a3e6352ba02cad64", - "0x6b4e299b6771ffa71a09f9b43bb7d573b3b76df27abbb167f3e5ac711ffa751e", - "0x19a3415b944a837037f7efa5fbba4b005b9d4b91b9d8b6365da23e54cb8140a8", - "0x0255c4c289bb1520a8f771e95d3863d9e3542eb626d8448391cd8e9aa2823855", - "0xe304bf675403603fc39c4160e20b47624cfccbea2a371d7613e36e47c8251038", - "0xc2c138a736de8ef11dcc3317ddd89dfeefddbb20f8cbe9b26e6d58f0fa0fbb3b", - "0x00c0f43ab18e8ae57cd4cb88ca1074ba08dfc3e7a493caff5d02321f17d3f96c", - "0x3840eb9a35e137d7104c7bc4e62a2d1445021aa136363e0fb463d0a8ccc24cbb", - "0x6bdcec67ee09dd0cba8a0fb3eee93bbb18f947607b8a0b9c8553ebcd6d63eb16", - "0xf63940a931f3171b375c2f55d0ddfeb17f34e4b37e671591748326e72a5d39b2", - "0x45e659968306110b827b48998208ba102a8c28ccf08da317e34c8951eeb18f8a", - "0x2f6ca4bee975b058f62fc48bc7b17eb8631c4ce36b3f7f1f6f7958811b7a34c0", - "0xc1daa62e5add1fe79f3883cd53f3cfd53ee29d8934a0d49f8c339347cd276739", - "0x90b542089fed6c8240d9367e60357bf7c685775744c57b079867e0a724644184", - "0x62e290067d8abb0e1fe21a488cf54471d6421cda07c80b01ab750d3d352186c8", - "0x3d42eb2d8d2639fd63038f69895b00ab04d92b1ab0a0b7772775615fe2a8424f", - "0x4caec53a8c0977a0f38813cd37458c783709da6d386dbe0a0358be87482aad15", - "0xbd8f3c60b0b998d72cdfdbad6fefd2ab3762583f4efc8606f62657d9c05cabe1", - "0xb6ec64aa2f81f1d5cefef4e819dbf54e8ed9f018faeb99e16c2560ceb3d82533", - "0x0acab9585bf5d17fffb1c0e479646c8a138670f181a1bc9b5fee71a396b24564", - "0xd9ea8713827ed39e9d0a8c308f20c567cda99c591b9777ed40a5b878e2454451", - "0xe7991c113e4fe09610a4061dff84bda3205bdcb49ec137340f596e03c4a961d1", - "0x4e19826671fdd3c0028c4820776eeae96ef435ed23f07c54b2393a517caa968d", - "0x4ff24d17dd4b18d0ca4167c6769d20aac8285779da23cf8f56a12cdd9806933f", - "0x6a2431fc40b88341209023b8898c7e8e8f28b6c0d83225d597bd445382b6fd55", - "0xd8be9caf486a1b8f3ed5b637882d2449a9e89dffd582a5fd55bffc4fb4f85418", - "0x54d6060edbbb771b06997b4dfc6fadac4b76fab74908a169b06201a13a0a22ca", - "0x8b461cd45b90b3c062c801349d4f723274cce36ec256c44e2d749897da891627", - "0x46fa69a77abdce3a0298effcc823f9e69dd22869ba26b4c1d01541bf2de7937b", - "0x13279e1b1ff0c1527e09aa8cd610c1aa54328f88c4dffcebfe41281c53a10aa9", - "0xd1ad1dece0824c9bb0b7e09269f813a1a75e4efa1eb6cb93ab1dd42119223f06", - "0x46243b104f4399a3404db73fecc1767b3308f4d51df8e10c306c648901a064b3", - "0xfce2eb01ca4594199347b46eab5c889371e7d3add681da4d36595976c5f7fc58", - "0x8af66d1c12db728f09018d3839a320f82c7b27b97000f9212989699c78e6c26c", - "0x545d857048fedaa6de9e10d59aea4f3369ae1ea2cfd4d990292c15bd7f37e182", - "0x63c4467f63fdf41ba4bdce830189c6421d6c67f3db531ce357e317123412245d", - "0x6f4305c01be3eae45809e6caf06360fd9690cc39c305ba5a64dd47811882a16a", - "0x751d6be0d942d718f44cd70f10d1611db0c4c89124b76b732b7decc1218959a4", - "0x7db5bfdec57bdc3b74e47bdcb41cfcaa642927e5eb436c0c76e36a43a02b62b2", - "0xa75bbf12e95613fdf3cdaa4badf1d340af7c6251fa2f649737fb19399720e7ab", - "0xb6cdaa8b0737001af40740abe099a51857a35aef0f209e859db01480a2f23816", - "0x1b8f7551cee4eb0e92ce6a9f0b1a4e4d943acef65e2ac65f2466e4fc2272aca6", - "0x164c76393945a764479fede040f13455768aa3ac44071acfff7f96b59f8332d1", - "0x561400d0ca3d1f322296b635e7b58d7266d65739e0d4fdfae898a9c49ce364b3", - "0x56892960f0c37d7513b88b7f99540aa49bc92e8bc7256bc1c2fc990a6c84344c", - "0x493500d343bbc38b73f289a74204d9186b34f477705f9ab32dd92892209f011b", - "0xbb713e726c6c4b3a8206a5a05e3c4b8b2f27aad052fbfaf385f16ae562573309", - "0x5eb20169a44963f8c1011a5940cefaff81139426fa200e6427bc028d8cbbbcdd", - "0xcead0359164f39fd00200de825cecc63bccc8cb76bca930262f5ee625efd234b", - "0x4aea59dbe3908afc1ec06f6d891236e906f135efcd28347c766a2315ee662640", - "0x0fd5dcb637ea55c9ed20147153e7ff1bb578832452c3b56e661c7594f141f712", - "0x3904bbb37b1f292628ca91fcb546e947f7cc52e827487819116449b739aee8df", - "0xbfe3767fca8cabd6d18d4d3c4aff79174a373082d6b6d4a63f2d03d0a64126d3", - "0x3793ae1a9b7887b8cb1f2ca995098b73bed8865c47f02d5ddd4e5b968a404ae8", - "0xfbcccfc208701b5ef22e6390f91b37e1f3d8f7a8ffe47951e5ae9cfcb4f28e5f", - "0xc5bdca16693d60334353a58233521bb12bf80e4312e63618f08400bd51be7876", - "0xd6896c40cb689153480778c4a46edffa531739573f08e3f3d0c9013e9f4532fb", - "0xe4c31595f204a44aeafe75779dca0ca894185b3e2627cc4e932e432054e58057", - "0x1ab27949919c75168ec496faca66141917449e1cc292702b52f165f3c224f5b2", - "0xdcd8131d8a1e98342029c3ae4fd1045d0a3b3a038e7dbd8e13fe3ecab30d13f1", - "0x373636000f911dd05c0c412ef69fd5c71888aced86d721ca6f6d3a097fb6243a", - "0xfbfa33ec677bcf4ad9b9f9404cf6945a2f2d0cb4544436d97b0615dee48e9ad0", - "0x4cee41306c387e673b6ae31295a331ec122ac2dcbc1664fbf4634c55c8f3df19", - "0x0df90b24ac4280df5a90a40bf1e1f6b895d790d7ad36ab863e80ee1923d71c81", - "0xce57e7edcdcfaa1d4d020f955e3c7c84746ba8a77a2dca69914331ae6a588c93", - "0x2857b09b2507973040e63fe5147f8c0722bb31b494f122187bd9ec3a119868b2", - "0x5edd2c25d12dbcb1b0083ee471f3c8d3562f8e812c7368afd39bccc5a8a475a8", - "0x0eb3fc45bed06182648ebc82fce9c599973616c2dff37791c014d81b72bf7ebc", - "0xf0daf44cc73cc1875444b8f97cbf96923ecb1d2406e6afdf30be1ebd67170a8b", - "0xe6cf543cebd12913b99e51f454690b2e6fb54d885e06269c7dd1dc940fae79f9", - "0x9b46655d15c4c8e06d28f97f8655a1211c97b32cbe932a1167040168ed757017", - "0xb48a7111ee89b664c7b3fc3552fa3ce288598880e3c63c86ba2b40d0f1de47be", - "0x733d13d1ecf3101ff428d49e3cd50ec300ada80429f46ec9621d308920e313f8", - "0x02bc80564388ece34922f531ace4b429925990d68c42226303ec2393ac4c8d72", - "0xabb2831b72888718eddc4575d79c31d5075f7687c9e6f7df9c5b1e3f672735bb", - "0x017650f52c51f3cf2e7d82ed8b757206873eb71508161210b4f7320089875b16", - "0x97d26f102797f52ab80bcc786c683b3768f54dad05b0cf5d2f7c69f7c5e4b477", - "0x8e01eb81eadd76e276e853bfd3f052d9a9d05de552985a74f4c51ca7c702cd32", - "0xaa9dc013b931a332f294dc074707216440270a27774c7928882cec0124a84f45", - "0x4d49d26f98115d18353e7c15cfbeda5cbb0dc3757a152fc486079565ec05f744", - "0x6dd93f797d1983445c06e551ab46dde6096110dc602aabf520959cbddaa98e0d", - "0x5af113b1aed1076edb16781bc9116dc2d347a178ed6c1ccab4d89d66a2801dbd", - "0x193c02baefae0ffd3e3a7afea1e7e1ddebd7799eb511fc7c62628fc31866c6bc", - "0xf4b7f803d7e61cbe2a08a4ec9b814fc3d70fa9da89a8eb58e93868ba95fd4f71", - "0x99a2cde1e1b08a6559bd4679df647911294be7a73cec0f8c6bdeb0adbd914073", - "0x3ef3b1292bba31f20b5759d0ae2aedd24facd710f837d5373460ce1be08249f9", - "0xe158ef6d40a5b798a181d561f8494c83157478b09eab7c08b438c55498297d76", - "0x466b6c92326aa974a81f77b3b365d590f5ff3387cf6946d9b26a97e8c6c07599", - "0x71144f5a9b95b05102d1ac52c332de9f3ee054937a0fb6cacec61c230818abe2", - "0x43857db3249a1efb81a8aaa934631ffaf6087d7d95e99b8b7e3a307088babdf2", - "0xd76ab08fcae444fef658d002d0ba8f0fb10ca37e2190aa848fa765db9bf938c6", - "0xbc372c482f74e1c5d04a565ee4ffbb470c376da174ea317d7abd6de7f24cf9c5", - "0x9033c56c26ccb9a08332d0a89eb22560607964e29aff9161f6736f12f7473d3d", - "0x082d2acfa4df63deb929cb4514cde04faa0d4a06d53fdb1a58de3ec3fdf4fe15", - "0x2ea6011771c74d920c395caf9c70650afb4deeacf9d496007d03baff837a689e", - "0xdac8c9829f362a81e7a88306123986375d2c83b4cba0dcf1998e44e4692c256f", - "0x1d3fc9c51b3ccf0ec3620736d22ba7123441a2dc334bf925c75ac395cce6d74c", - "0x4881784250b53fa771634929fc34fa26bff9666701bfea61dcfc1a8b6ad012c8", - "0x8a33423f7fc2f11af8ee8beedc8b7e21cc2cd515a6c5ac1a55918dd5e2b16e99", - "0x3fc7e4707e24b5af35c4659da17186f4ce36d526fa91a121f4b9c2f3084263b9", - "0xe104a42b2766455416e0ee8736c1b3191822bed35fe36c01b5e3436f83157ee5", - "0xb9f270344e24eada673b5edff6f07e6fdcab6dfa89ff5f9197115cfca40428f9", - "0xe10df92ac34ed8b77c25d1f5c3f7e4dfb9785464e80b5752d641d986d86ec718", - "0x5baf89908afca6661198fa8bd6af1a08ad0c055abbc724801deb2f5772532c70", - "0x234c4085cfd920d8a76610df100a57e2c798d7c28481e4ee0dea38429691b061", - "0xfbcd104ca8ab7ffaade1ac59a4d9f62969035ea2fb40b54a0207292eae753e89", - "0xfa617eee82fc7887f71bbe0d38ec6535f58c14194441680de7ed91e6156e338a", - "0xd1e2a7c1b81a765293272287e9cdffc50d36b01bf884b1912f4ef37cc08fd7fe", - "0xd9ff3c03d42cecc78087cde6c9b3c4f699c3f1691adf0d3bef421361cb47c794", - "0xc8dd84e4ddac7535547b103be4023f54f74ec0c24e6140314232e1a65d2b4b67", - "0x80e188b2d5f02e63498e455bcd45b00a97915971c7901f666428e8d17c5071aa", - "0xa3378ec252a08d59233803847d8bd8ad275b78bb64224998b4ae308559ca3937", - "0x362ca494021482f0d9dea4abd844ca11b71fbeb0a98e7db7bfb3bb6f864eb58c", - "0x09ec27c856f50888e4634ffaca66f8185fd13e0bb2bbf522ce6209886502c7a5", - "0x24996d2f96618887c1c5e61d5d7ce6948853ca35811d48f72155a4778308e255", - "0x4f199a8efc8615c623902b5c279dd995ac1df4654a07aab0423e070d14bd24d0", - "0xe6875990cef0d2ce1f9d100654cdf522bbe345a462ebca769b1e288128c13201", - "0x36c141d47991e557f7226d8b1fe791c334f046f867d7865d663f99e2a337d915", - "0x337e59da56cde8e3f9e90fafff54d4fa240ba01aec855fd55626e4b3a45a9767", - "0x67672d3a29c49e3545d25422b6015a1eebe86596f55d4c9b8772e89c99ac9f8b", - "0x6d6e167641bcd2e5241ac2710951f7f0c47fb311e32e998831e6fe38f5bebb25", - "0x6dc4fa5ae3f281a90619216491284d951151f37be648694d1952314f43ffe05d", - "0x3c865ccee7900b39d671c43ae15df71fad294781d74815537de94d02b48dd8a0", - "0x10ba37d2ff46db181ff8a15f38bfeecfd9c21e28bf63cb5ab975a2d7c74c0aef", - "0xba265a598f3af5bf4c2b87bd8d28a55f963cfbd09108aaccd5b00294f662e8b9", - "0xaab422eb98f7b0ab9a01558069a3666efa2be2510097b0054bd5c8fceb1023df", - "0xdbff3ce1e6fb0388029f95bd091c95b88c6539fabfe6f1de8e44c47dab064ff8", - "0xb1aa0c7254f3aaa61b58c9d973f3d89f5f4dc6bfee011ad4c4a3e3ae76aaf7df", - "0x055e36325029db9e089808ca01b0ee3eae53703f3e91ee51e587e7b7dcaa8a3d", - "0x00ec5a25517cf0c0a1bf892e5209fd0185ff1ffc6b2523642bf86f6a3d0c10f3", - "0x7bc6a8d5017760f12ace9ebfcdf5a91abcfd6e4f4131df5c4dbe84d2d6a6add9", - "0xeb5da64c8d0cc93a44da96ee9c3c0a22054a2a2e87a7491a5618ade1f59a1b96", - "0xc0a937729678bb6aca27ae16c662ccd2dd0b53346c610d6e5d5b1655762fa4b3", - "0x55c732917d3894ffeb9844fcb1c690d0956c4e2a8eccb6552cdde4ea90c99db2", - "0xf1dfd2205daf748da9e8e98bc4ee94cd6b2db5560e4cead28910e14c576337aa", - "0x60a271fb20c017f90d99c57783ebb8b1a3233398c49f89d73db14b09adf2893b", - "0x8fbd97d55dc64f2124c79054765515f49d79a05f2962b17875f12b353c33f564", - "0x5afd5951c5e834fbf22071047f72619496e9a9dbc6073b661c44741b67fabf03", - "0x2d05a19223820f92e385d2c942b70cca32804546f3aa7058381ca3f6c1ecde11", - "0x267dab323804f2e7bdcc80dde83ccbff56d1aa0fffd0edc7c02ffd4a24bbe027", - "0xf4e9e580bbcc5a9be1f942c12d4b34eb1a4c07feffd0b95abe27ddff57a6cc8f", - "0x3f146ceffcd100ca9fc1af743fad74acd03a289f4038413acfcfd0eb87d14a56", - "0xef98171ed6b269c0ebd6bdbb1d98694316b639bae6a3ad01f4964c3d2eb3d2cf", - "0x510013a35d5c71863b3385176c794ad90e17af1fd271b933f9f7ab980311b27c", - "0x5eb181d22f30ed203c4d3fad610d31c2338dae6a88eee250af19ae0f58319ad1", - "0x50a28d65b51ff086dcf2110feca7d990db95f078394c56bfb850a3b8cfd7f1a2", - "0x4148b8f2136b84773b7ff71d73aa8ffb98f0f3952939849f324c28b9173efd70", - "0xa980bc7fa736bf6d6d794ffa5e035664c45117c21e40da7ba76de7c2ff8164d0", - "0x794a8ef17e48986df700d6e68799a50695086702d6224ae9a71663fd03f8a55e", - "0xf354c82e1c75d7d758099dcd4d0592363eef81e85cba773aa6d6b30e1900582a", - "0xbb4c8b990add1c4e43ddebf742eb605028ae2daa8b61db7d237386d81e4c3413", - "0x61470a53ef9757fa7c452bdbe26d448dd8bf6590fc11420ebeddf441fe56f381", - "0x603558b2f72405e9625ac561d62c2bbcdda84d8ef4b5510d06bdac0553025bf2", - "0xf52d9ba6ba149d0e4266a8d05b73e4a445e9583560024e0185d4b8b12bd55640", - "0x26ef211f77b4a67ddb0a24c4e63f7d247d6ce8635dd9862894bdc067633ff395", - "0xed4ce5eb08b63534c5de241513a996322d168f6b8bf5c75167e79dd7ca1cc817", - "0x9d5f39f494a36d17db609b7a7a62734ca7222426c3556132bf1e2241d6c45cd2", - "0xe664772da4521e1140cc2c5db744010f57c15374dfb78a97701904787846a8f6", - "0x93faea0d197923b1578ac190fe608203f637fe4991ad4954c15ea94e17ee948b", - "0x2b028c1aaae5217cabb97c46c20541c93e55a868f78e82740f9fb9a0a033d662", - "0x529103419484990ad11c05e0c4c696826f67fbcd491e19aaac461cc340747ec4", - "0xc1e773415513b7bf47340d7b0a29e5d49d1a88bb3d0ee2a79726169d32012378", - "0xf97c43bb4ae25524b180e59cb6cac75858d4a8a0772fcf4f3bd24dfad42169da", - "0x19fa7ad12455fd022cfd15897d553e031ca1adf80c963c770250d36f1810a5f0", - "0xe67ce6e2444b8318a01150952a537ee47b701555c79f60eb90d3c3cdf93fb6b1", - "0x1b49d232180b0922704ebd4e9fabca5dd8ae9cfc2a39ea07226ba903a15037e3", - "0x56540b02ffe1743468342b5664b6e51cb3ef6c48dd74d6cd74e0c0c513c13129", - "0xd4d9d67f352c7f54d68ce994b3765b032108a5bd0b5b62d14b893949b96008d4", - "0x44d1b765df81594fd37e7700019f19b1ebd6d9b50631455931b83ce35238cb6b", - "0x2b67aef7cecfec055853cd63e8519bf506d7a1ae69a768e4db9415bdff532c47", - "0xc30ac2a4acc33cec2cdc83e114fb8237c4900a2fc89dddb6af3a4dd5e4868e51", - "0xe0e7127e086e2011f30fcd0469b35769cae163acb8ee6db8a8e2c032c2b351c5", - "0xb46c428070d15ec926fdf1d7465a9168c56dbb959d5391e26480500a98e36047", - "0x8dab41ce167633da3c0b5332833dfad5d2c28ef30c94973e5f4647e312781676", - "0xc632d30657b39208c254cf4f899d00d7c3196cbdc31248798a760972bf399fec", - "0xb4eb2c6c656406a9bc6cc6176dbdfc0e878046530d830a51e04a4e22c4572370", - "0xadb48a02c9c87b246360edb45528ba23cceed736a2ee0cfd03177428e6243024", - "0x443ac2e63e6d629398e607d6689725dd68fd5254916c1b76c876885456a13338", - "0xb66c4d950a9304025654de37c7d0e9759b755d86226a816d1e3b4c7d654a1bc3", - "0xded624610c3493bc7806844342c37909c1c1d179025c215108a12a0428ae9e36", - "0x28c85177961714ce796f152dcd31c8b19db1c39deebfe383d6fdf2334d4261b6", - "0x6d342534c3a4a875c5455ed3ef546e876cedb5aee7b2ecf194c6e47c6a09d3a4", - "0x65a84879e014085b7a0de5bf604050bc6bffd8ab7748253203fff49573763558", - "0xcb0e4339dfbfc8b420c915149a0d352fda69c31668b32452c432f067462b4325", - "0x3466440e4c88895f2642f2986dea4be482c028592530224d73e67254b2bac69f", - "0xb7c325fec25c7b278724ebb91c64fc2ac37e91e44b136dd6c29bf48b80990888", - "0x7a9a2bd3ab5972c1a4ec5972f959a1e30eb678090ed76947e38d76fc3ed758a5", - "0x74deabf896026cb20e2f5f8a88828b265d6696883dab972fe1bc9a4ca98d68b5", - "0x195d2e25b1ec8deea095bcbaac5277f4586237e68bd9c9ad24e65a8e53d6d27a", - "0xcc343c21f7f18c1d507cc3cf12381d7bbe55d7355363361d05d972343fb4b7fe", - "0xd2637aaef2034203e549453225eb1d75633d2c66e8cad0419395250c1e369536", - "0x0f8052a87aa435deee2826e6dbc944128835264d5770dbb064373de05333f8c2", - "0x2091278a3d6a7ed1ab0458cc307903a51d75c6a1c549ebed1f0aed29ce6533b8", - "0x3f7d25feee0dcf39372ccbad55989442d9b7ce16ac93e4b1dca63bd92d5204e4", - "0xd0f866c38597038e360af960e3e84c79a0027576bf3fdf4358a9442f0c671b61", - "0xc527b3694dc6b46f0562cb36e2f766e5b75793ad2794258d8be5119bff4ea8b0", - "0x47f1d7d32904cb0945e97140c2412d12b1e0980c2f72c7878250170aae4124dd", - "0x57aa1ef2a7ec01ee7acd0c4baed5407f30d6c34f2bc6bce7fda6366517343199", - "0xd392127f856736dd74d16a24309a247682e2736f78fc92713c484a6f13edcdbf", - "0x595aa7e3eddba867389d63b2eaa6b16e4e5163076f03d2c8013e40c3b0fc98c0", - "0x50bb320d8bea03db548be3a3b619159e1993ba8eea83af6f022b9cd29ae4d0ff", - "0x53deeb64ee923912f76a233532f474d76d8f6b8cc42eedcce81bda8ad608294c", - "0x1af301b0eaf36d16a74b2d1f76e9e26659f047cd3466a765049260047c25bbee", - "0x05c1d40227ece15c55f06dc922f5a9be01cc147a96e070e7a81b696f0f40b6ee", - "0xa63ee877c70d8e51e795d153a34ce3bcc212f8fb8e77df52ff83084b9133a280", - "0x07ab3e2107db7ff479f5be0d7a57e3b76627cf6230cdeb61d78ebaa2c391d360", - "0xc893aee6d249c152f5db3d7763f34a3311345dff721ae9c71ab5fb3d2b3e2559", - "0x250caa98ea3e682be9c866990f19647f443d57690052229ac0ccfa0ab30a5a71", - "0xc32fd5318214071a41cd8e98499b2b65942c5837c686a06b536146fd0bf294bf", - "0xac390c012eecd83fa8f4cc77a59992914b5c95af36b28747e07adea13228acbc" - ] - }, - "nodes": [ - "enode://efd48ad0879eeb7f9cb5e50f33f7bc21e805a72e90361f145baaa22dd75d111e7cd9c93f1b7060dcb30aa1b3e620269336dbf32339fea4c18925a4c15fe642df@18.205.66.229:30303", - "enode://5fbfb426fbb46f8b8c1bd3dd140f5b511da558cd37d60844b525909ab82e13a25ee722293c829e52cb65c2305b1637fa9a2ea4d6634a224d5f400bfe244ac0de@162.243.55.45:30303", - "enode://6dd3ac8147fa82e46837ec8c3223d69ac24bcdbab04b036a3705c14f3a02e968f7f1adfcdb002aacec2db46e625c04bf8b5a1f85bb2d40a479b3cc9d45a444af@104.237.131.102:30303", - "enode://b9e893ea9cb4537f4fed154233005ae61b441cd0ecd980136138c304fefac194c25a16b73dac05fc66a4198d0c15dd0f33af99b411882c68a019dfa6bb703b9d@18.130.93.66:30303", - "enode://3fe9705a02487baea45c1ffebfa4d84819f5f1e68a0dbc18031553242a6a08e39499b61e361a52c2a92f9553efd63763f6fdd34692be0d4ba6823bb2fc346009@178.62.238.75:30303", - "enode://d50facc65e46bda6ff594b6e95491efa16e067de41ae96571d9f3cb853d538c44864496fa5e4df10115f02bbbaf47853f932e110a44c89227da7c30e96840596@188.166.163.187:30303", - "enode://a0d5c589dc02d008fe4237da9877a5f1daedee0227ab612677eabe323520f003eb5e311af335de9f7964c2092bbc2b3b7ab1cce5a074d8346959f0868b4e366e@46.101.78.44:30303", - "enode://c071d96b0c0f13006feae3977fb1b3c2f62caedf643df9a3655bc1b60f777f05e69a4e58bf3547bb299210092764c56df1e08380e91265baa845dca8bc0a71da@68.183.99.5:30303", - "enode://83b33409349ffa25e150555f7b4f8deebc68f3d34d782129dc3c8ba07b880c209310a4191e1725f2f6bef59bce9452d821111eaa786deab08a7e6551fca41f4f@206.189.68.191:30303", - "enode://0daae2a30f2c73b0b257746587136efb8e3479496f7ea1e943eeb9a663b72dd04582f699f7010ee02c57fc45d1f09568c20a9050ff937f9139e2973ddd98b87b@159.89.169.103:30303", - "enode://50808461dd73b3d70537e4c1e5fafd1132b3a90f998399af9205f8889987d62096d4e853813562dd43e7270a71c9d9d4e4dd73a534fdb22fbac98c389c1a7362@178.128.55.119:30303", - "enode://5cd218959f8263bc3721d7789070806b0adff1a0ed3f95ec886fb469f9362c7507e3b32b256550b9a7964a23a938e8d42d45a0c34b332bfebc54b29081e83b93@35.187.57.94:30303" - ], - "accounts": { - "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, - "3282791d6fd713f1e94f4bfd565eaa78b3a0599d": { - "balance": "1337000000000000000000" - }, - "17961d633bcf20a7b029a7d94b7df4da2ec5427f": { - "balance": "229427000000000000000" - }, - "493a67fe23decc63b10dda75f3287695a81bd5ab": { - "balance": "880000000000000000000" - }, - "01fb8ec12425a04f813e46c54c05748ca6b29aa9": { - "balance": "259800000000000000000" - }, - "d2a030ac8952325f9e1db378a71485a24e1b07b2": { - "balance": "2000000000000000000000" - }, - "77a34907f305a54c85db09c363fde3c47e6ae21f": { - "balance": "985000000000000000000" - }, - "391a77405c09a72b5e8436237aaaf95d68da1709": { - "balance": "49082000000000000000" - }, - "00aada25ea2286709abb422d41923fd380cd04c7": { - "balance": "650100000000000000000" - }, - "acc46a2a555c74ded4a2bd094e821b97843b40c0": { - "balance": "1940000000000000000000" - }, - "de07fb5b7a464e3ba7fbe09e9acb271af5338c58": { - "balance": "50000000000000000000" - }, - "4c696be99f3a690440c3436a59a7d7e937d6ba0d": { - "balance": "3460000000000000000000" - }, - "fa33553285a973719a0d5f956ff861b2d89ed304": { - "balance": "20000000000000000000" - }, - "67cfda6e70bf7657d39059b59790e5145afdbe61": { - "balance": "646000000000000000000" - }, - "a321091d3018064279db399d2b2a88a6f440ae24": { - "balance": "3200000000000000000000" - }, - "fb3fa1ac08aba9cc3bf0fe9d483820688f65b410": { - "balance": "30000000000000000000000" - }, - "6715c14035fb57bb3d667f7b707498c41074b855": { - "balance": "700000000000000000000" - }, - "d4344f7d5cad65d17e5c2d0e7323943d6f62fe92": { - "balance": "267400000000000000000" - }, - "a3294626ec2984c43b43da4d5d8e4669b11d4b59": { - "balance": "1008000000000000000000" - }, - "656018584130db83ab0591a8128d9381666a8d0e": { - "balance": "63960000000000000000" - }, - "0fa010ce0c731d3b628e36b91f571300e49dbeab": { - "balance": "999800000000000000000" - }, - "3098b65db93ecacaf7353c48808390a223d57684": { - "balance": "449965000000000000000" - }, - "ae635bf73831119d2d29c0d04ff8f8d8d0a57a46": { - "balance": "1337000000000000000000" - }, - "0f7515ff0e808f695e0c20485ff96ed2f7b79310": { - "balance": "1000169000000000000000" - }, - "8b30c04098d7a7e6420c357ea7bfa49bac9a8a18": { - "balance": "8000200000000000000000" - }, - "64dba2d6615b8bd7571836dc75bc79d314f5ecee": { - "balance": "10000000000000000000000" - }, - "e7912d4cf4562c573ddc5b71e37310e378ef86c9": { - "balance": "394000000000000000000" - }, - "a4da34450d22ec0ffcede0004b02f7872ee0b73a": { - "balance": "93342000000000000000" - }, - "34437d1465640b136cb5841c3f934f9ba0b7097d": { - "balance": "173000000000000000000" - }, - "c652871d192422c6bc235fa063b44a7e1d43e385": { - "balance": "155000000000000000000" - }, - "a8a708e84f82db86a35502193b4c6ee9a76ebe8f": { - "balance": "1015200000000000000000" - }, - "5c3f567faff7bad1b5120022e8cbcaa82b4917b3": { - "balance": "2000000000000000000000" - }, - "dbc1d0ee2bab531140de137722cd36bdb4e47194": { - "balance": "200000000000000000000" - }, - "f59dab1bf8df11327e61f9b7a14b563a96ec3554": { - "balance": "6000000000000000000000" - }, - "456f8d746682b224679349064d1b368c7c05b176": { - "balance": "3700000000000000000000" - }, - "5f13154631466dcb1353c890932a7c97e0878e90": { - "balance": "6000000000000000000000" - }, - "f4b1626e24f30bcad9273c527fcc714b5d007b8f": { - "balance": "200000000000000000000" - }, - "a8db0b9b201453333c757f6ad9bcb555c02da93b": { - "balance": "2199970000000000000000" - }, - "a0fc7e53c5ebd27a2abdac45261f84ab3b51aefb": { - "balance": "3008250000000000000000" - }, - "1b636b7a496f044d7359596e353a104616436f6b": { - "balance": "360354000000000000000" - }, - "74bce9ec38362d6c94ccac26d5c0e13a8b3b1d40": { - "balance": "999954000000000000000" - }, - "9834682180b982d166badb9d9d1d9bbf016d87ee": { - "balance": "2000000000000000000000" - }, - "1e6e0153fc161bc05e656bbb144c7187bf4fe84d": { - "balance": "2000000000000000000000" - }, - "989c0ccff654da03aeb11af701054561d6297e1d": { - "balance": "4000000000000000000000" - }, - "78a1e254409fb1b55a7cb4dd8eba3b30c8bad9ef": { - "balance": "100000000000000000000" - }, - "9ef1896b007c32a15114fb89d73dbd47f9122b69": { - "balance": "4000000000000000000000" - }, - "33320dd90f2baa110dd334872a998f148426453c": { - "balance": "999972000000000000000" - }, - "e72e1d335cc29a96b9b1c02f003a16d971e90b9d": { - "balance": "1580000000000000000000" - }, - "0921605f99164e3bcc28f31caece78973182561d": { - "balance": "793744000000000000000" - }, - "fc00a420a36107dfd5f495128a5fe5abb2db0f34": { - "balance": "5960000000000000000000" - }, - "dfcbdf09454e1a5e4a40d3eef7c5cf1cd3de9486": { - "balance": "4000000000000000000000" - }, - "646e043d0597a664948fbb0dc15475a3a4f3a6ed": { - "balance": "20000000000000000000" - }, - "79aeb34566b974c35a5881dec020927da7df5d25": { - "balance": "2000000000000000000000" - }, - "dbadc61ed5f0460a7f18e51b2fb2614d9264a0e0": { - "balance": "40000000000000000000" - }, - "97b91efe7350c2d57e7e406bab18f3617bcde14a": { - "balance": "9999980000000000000000" - }, - "8398e07ebcb4f75ff2116de77c1c2a99f303a4cf": { - "balance": "500000000000000000000" - }, - "f02796295101674288c1d93467053d042219b794": { - "balance": "740000000000000000000" - }, - "f4ed848ec961739c2c7e352f435ba70a7cd5db38": { - "balance": "1970000000000000000000" - }, - "82485728d0e281563758c75ab27ed9e882a0002d": { - "balance": "147000000000000000000" - }, - "427ec668ac9404e895cc861511d1620a4912be98": { - "balance": "40000000000000000000000" - }, - "1bbc199e586790be87afedc849c04726745c5d7b": { - "balance": "4000000000000000000000" - }, - "10d945334ecde47beb9ca3816c173dfbbd0b5333": { - "balance": "1400000000000000000000" - }, - "1dcebcb7656df5dcaa3368a055d22f9ed6cdd940": { - "balance": "499800000000000000000" - }, - "2ac1f8d7bf721f3cfe74d20fea9b87a28aaa982c": { - "balance": "161000000000000000000" - }, - "0a47ad9059a249fc936b2662353da6905f75c2b9": { - "balance": "2000000000000000000000" - }, - "768498934e37e905f1d0e77b44b574bcf3ec4ae8": { - "balance": "20000000000000000000000" - }, - "f46b6b9c7cb552829c1d3dfd8ffb11aabae782f6": { - "balance": "21000000000000000000" - }, - "7aea25d42b2612286e99c53697c6bc4100e2dbbf": { - "balance": "2000000000000000000000" - }, - "af3615c789d0b1152ad4db25fe5dcf222804cf62": { - "balance": "1000000000000000000000" - }, - "92e6581e1da1f9b846e09347333dc818e2d2ac66": { - "balance": "3640000000000000000000" - }, - "240305727313d01e73542c775ff59d11cd35f819": { - "balance": "5931229000000000000000" - }, - "b95cfda8465ba9c2661b249fc3ab661bdfa35ff0": { - "balance": "318949000000000000000" - }, - "1b0d076817e8d68ee2df4e1da1c1142d198c4435": { - "balance": "1550000000000000000000" - }, - "93c2e64e5de5589ed25006e843196ee9b1cf0b3e": { - "balance": "1670000000000000000000" - }, - "0e2e504a2d1122b5a9feee5cb1451bf4c2ace87b": { - "balance": "3940000000000000000000" - }, - "22b96ab2cad55db100b53001f9e4db378104c807": { - "balance": "10000000000000000000000" - }, - "a927d48bb6cb814bc609cbcaa9151f5d459a27e1": { - "balance": "271600000000000000000" - }, - "5cbd8daf27ddf704cdd0d909a789ba36ed4f37b2": { - "balance": "13400000000000000000" - }, - "9adbd3bc7b0afc05d1d2eda49ff863939c48db46": { - "balance": "199955000000000000000" - }, - "ac7e03702723cb16ee27e22dd0b815dc2d5cae9f": { - "balance": "16000000000000000000000" - }, - "1e210e7047886daa52aaf70f4b991dac68e3025e": { - "balance": "200000000000000000000" - }, - "c98048687f2bfcc9bd90ed18736c57edd352b65d": { - "balance": "1000000000000000000000" - }, - "81c18c2a238ddc4cba230a072dd7dc101e620273": { - "balance": "1337000000000000000000" - }, - "cb3d766c983f192bcecac70f4ee03dd9ff714d51": { - "balance": "100000000000000000000" - }, - "44a63d18424587b9b307bfc3c364ae10cd04c713": { - "balance": "20000000000000000000" - }, - "4ab2d34f04834fbf7479649cab923d2c4725c553": { - "balance": "3520000000000000000000" - }, - "b834acf3015322c58382eeb2b79638906e88b6de": { - "balance": "24000000000000000000000" - }, - "7d551397f79a2988b064afd0efebee802c7721bc": { - "balance": "39400000000000000000000" - }, - "b537d36a70eeb8d3e5c80de815225c1158cb92c4": { - "balance": "1500000000000000000000" - }, - "805ce51297a0793b812067f017b3e7b2df9bb1f9": { - "balance": "100000000000000000000" - }, - "085ba65febe23eefc2c802666ab1262382cfc494": { - "balance": "400000000000000000000" - }, - "b1c0d08b36e184f9952a4037e3e53a667d070a4e": { - "balance": "1000000000000000000000" - }, - "83fe5a1b328bae440711beaf6aad6026eda6d220": { - "balance": "20000000000000000000000" - }, - "7fd679e5fb0da2a5d116194dcb508318edc580f3": { - "balance": "6560000000000000000000" - }, - "41ad369f758fef38a19aa3149379832c818ef2a0": { - "balance": "1000060000000000000000" - }, - "6d846dc12657e91af25008519c3e857f51707dd6": { - "balance": "4590000000000000000000" - }, - "c02d6eadeacf1b78b3ca85035c637bb1ce01f490": { - "balance": "4000000000000000000000" - }, - "826eb7cd7319b82dd07a1f3b409071d96e39677f": { - "balance": "1000000000000000000000" - }, - "4ac9905a4cb6ab1cfd62546ee5917300b87c4fde": { - "balance": "1015200000000000000000" - }, - "cf6e52e6b77480b1867efec6446d9fc3cc3577e8": { - "balance": "222010000000000000000" - }, - "2476b2bb751ce748e1a4c4ff7b230be0c15d2245": { - "balance": "4000000000000000000000" - }, - "1a505e62a74e87e577473e4f3afa16bedd3cfa52": { - "balance": "500000000000000000000" - }, - "21d02705f3f64905d80ed9147913ea8c7307d695": { - "balance": "1363740000000000000000" - }, - "7b1daf14891b8a1e1bd429d8b36b9a4aa1d9afbf": { - "balance": "500000000000000000000" - }, - "5338ef70eac9dd9af5a0503b5efad1039e67e725": { - "balance": "2674000000000000000000" - }, - "50ca86b5eb1d01874df8e5f34945d49c6c1ab848": { - "balance": "1000000000000000000000" - }, - "f3cc8bcb559465f81bfe583bd7ab0a2306453b9e": { - "balance": "20000000000000000000000" - }, - "5c323457e187761a8276e359b7b7af3f3b6e3df6": { - "balance": "10000000000000000000000" - }, - "4d82d7700c123bb919419bbaf046799c6b0e2c66": { - "balance": "20000000000000000000000" - }, - "8a66abbc2d30ce21a833b0db8e561d5105e0a72c": { - "balance": "699958000000000000000" - }, - "2ae53866fc2d14d572ab73b4a065a1188267f527": { - "balance": "8000000000000000000000" - }, - "9af5c9894c33e42c2c518e3ac670ea9505d1b53e": { - "balance": "18200000000000000000" - }, - "cba25c7a503cc8e0d04971ca05c762f9b762b48b": { - "balance": "500000000000000000000" - }, - "fda3042819af3e662900e1b92b4358eda6e92590": { - "balance": "118200000000000000000000" - }, - "9bd7c38a4210304a4d653edeff1b3ce45fce7843": { - "balance": "282000000000000000000" - }, - "edc22fb92c638e1e21ff5cf039daa6e734dafb29": { - "balance": "298000000000000000000" - }, - "a1f193a0592f1feb9fdfc90aa813784eb80471c9": { - "balance": "1400000000000000000000" - }, - "e97fde0b67716325cf0ecce8a191a3761b2c791d": { - "balance": "1004700000000000000000" - }, - "110237cf9117e767922fc4a1b78d7964da82df20": { - "balance": "3940000000000000000000" - }, - "e32f95766d57b5cd4b173289d6876f9e64558194": { - "balance": "100000000000000000000" - }, - "f2d59c8923759073d6f415aaf8eb065ff2f3b685": { - "balance": "7880000000000000000000" - }, - "c53d79f7cb9b70952fd30fce58d54b9f0b59f647": { - "balance": "5089200000000000000000" - }, - "9eb281c32719c40fdb3e216db0f37fbc73a026b7": { - "balance": "20000000000000000000" - }, - "2d6511fd7a3800b26854c7ec39c0dcb5f4c4e8e8": { - "balance": "399910000000000000000" - }, - "61ba87c77e9b596de7ba0e326fddfeec2163ef66": { - "balance": "200000000000000000000" - }, - "de1121829c9a08284087a43fbd2fc1142a3233b4": { - "balance": "1000000000000000000000" - }, - "22a25812ab56dcc423175ed1d8adacce33cd1810": { - "balance": "1850000000000000000000" - }, - "518cef27b10582b6d14f69483ddaa0dd3c87bb5c": { - "balance": "600000000000000000000" - }, - "59161749fedcf1c721f2202d13ade2abcf460b3d": { - "balance": "2000000000000000000000" - }, - "3e36c17253c11cf38974ed0db1b759160da63783": { - "balance": "7000000000000000000000" - }, - "cbfa76db04ce38fb205d37b8d377cf1380da0317": { - "balance": "1430000000000000000000" - }, - "a7e83772bc200f9006aa2a260dbaa8483dc52b30": { - "balance": "207730000000000000000" - }, - "e87eac6d602b4109c9671bf57b950c2cfdb99d55": { - "balance": "49932000000000000000" - }, - "9b06ad841dffbe4ccf46f1039fc386f3c321446e": { - "balance": "2000000000000000000000" - }, - "e0f903c1e48ac421ab48528f3d4a2648080fe043": { - "balance": "1015200000000000000000" - }, - "5d872b122e994ef27c71d7deb457bf65429eca6c": { - "balance": "7999973000000000000000" - }, - "f34083ecea385017aa40bdd35ef7effb4ce7762d": { - "balance": "400000000000000000000" - }, - "7f3709391f3fbeba3592d175c740e87a09541d02": { - "balance": "480000000000000000000" - }, - "888e94917083d152202b53163939869d271175b4": { - "balance": "4000000000000000000000" - }, - "bed4c8f006a27c1e5f7ce205de75f516bfb9f764": { - "balance": "16000000000000000000000" - }, - "b3a6bd41f9d9c3201e050b87198fbda399342210": { - "balance": "3622615000000000000000" - }, - "550aadae1221b07afea39fba2ed62e05e5b7b5f9": { - "balance": "20000000000000000000" - }, - "bcedc4267ccb89b31bb764d7211171008d94d44d": { - "balance": "200000000000000000000" - }, - "6229dcc203b1edccfdf06e87910c452a1f4d7a72": { - "balance": "32500000000000000000000" - }, - "94be3ae54f62d663b0d4cc9e1ea8fe9556ea9ebf": { - "balance": "23280000000000000000" - }, - "0e0c9d005ea016c295cd795cc9213e87febc33eb": { - "balance": "198000000000000000000" - }, - "55d057bcc04bd0f4af9642513aa5090bb3ff93fe": { - "balance": "1106680000000000000000" - }, - "ed9e030ca75cb1d29ea01d0d4cdfdccd3844b6e4": { - "balance": "30895000000000000000" - }, - "86c4ce06d9ac185bb148d96f7b7abe73f441006d": { - "balance": "10000000000000000000000" - }, - "2c04115c3e52961b0dc0b0bf31fba4546f5966fd": { - "balance": "200000000000000000000" - }, - "b959dce02e91d9db02b1bd8b7d17a9c41a97af09": { - "balance": "8000000000000000000000" - }, - "e01547ba42fcafaf93938becf7699f74290af74f": { - "balance": "2000000000000000000000" - }, - "c593d6e37d14b566643ac4135f243caa0787c182": { - "balance": "12000000000000000000000" - }, - "2c0ee134d8b36145b47beee7af8d2738dbda08e8": { - "balance": "201000000000000000000" - }, - "0ef54ac7264d2254abbb5f8b41adde875157db7c": { - "balance": "40000000000000000000" - }, - "0349634dc2a9e80c3f7721ee2b5046aeaaedfbb5": { - "balance": "4000000000000000000000" - }, - "873e49135c3391991060290aa7f6ccb8f85a78db": { - "balance": "20000000000000000000" - }, - "05236d4c90d065f9e3938358aaffd777b86aec49": { - "balance": "500000000000000000000" - }, - "d2abd84a181093e5e229136f42d835e8235de109": { - "balance": "100007000000000000000" - }, - "b56a780028039c81caf37b6775c620e786954764": { - "balance": "2000000000000000000000" - }, - "86df73bd377f2c09de63c45d67f283eaefa0f4ab": { - "balance": "1000000000000000000000" - }, - "7670b02f2c3cf8fd4f4730f3381a71ea431c33c7": { - "balance": "267400000000000000000" - }, - "24aa1151bb765fa3a89ca50eb6e1b1c706417fd4": { - "balance": "3100000000000000000000" - }, - "43227d65334e691cf231b4a4e1d339b95d598afb": { - "balance": "10000000000000000000000" - }, - "695550656cbf90b75d92ad9122d90d23ca68ca4d": { - "balance": "1000000000000000000000" - }, - "5281733473e00d87f11e9955e589b59f4ac28e7a": { - "balance": "660360000000000000000000" - }, - "99a96bf2242ea1b39ece6fcc0d18aed00c0179f3": { - "balance": "300000000000000000000" - }, - "b1cf94f8091505055f010ab4bac696e0ca0f67a1": { - "balance": "1580000000000000000000" - }, - "54391b4d176d476cea164e5fb535c69700cb2535": { - "balance": "100076000000000000000" - }, - "152f2bd229ddf3cb0fdaf455c183209c0e1e39a2": { - "balance": "2000000000000000000000" - }, - "affc99d5ebb4a84fe7788d97dce274b038240438": { - "balance": "5000000000000000000000" - }, - "23df8f48ee009256ea797e1fa369beebcf6bc663": { - "balance": "2302671000000000000000" - }, - "3a72d635aadeee4382349db98a1813a4cfeb3df1": { - "balance": "200000000000000000000000" - }, - "ce26f9a5305f8381094354dbfc92664e84f902b5": { - "balance": "230200000000000000000" - }, - "d283b8edb10a25528a4404de1c65e7410dbcaa67": { - "balance": "12000000000000000000000" - }, - "a7859fc07f756ea7dcebbccd42f05817582d973f": { - "balance": "10000000000000000000000" - }, - "b28181a458a440f1c6bb1de8400281a3148f4c35": { - "balance": "376000000000000000000" - }, - "27b1694eafa165ebd7cc7bc99e74814a951419dc": { - "balance": "800000000000000000000" - }, - "66cc8ab23c00d1b82acd7d73f38c99e0d05a4fa6": { - "balance": "100000000000000000000" - }, - "926082cb7eed4b1993ad245a477267e1c33cd568": { - "balance": "374300000000000000000" - }, - "4a47fc3e177f567a1e3893e000e36bba23520ab8": { - "balance": "2000000000000000000000" - }, - "594a76f06935388dde5e234696a0668bc20d2ddc": { - "balance": "2800000000000000000000" - }, - "e91fa0badaddb9a97e88d3f4db7c55d6bb7430fe": { - "balance": "376000000000000000000" - }, - "574de1b3f38d915846ae3718564a5ada20c2f3ed": { - "balance": "4000000000000000000000" - }, - "5816c2687777b6d7d2a2432d59a41fa059e3a406": { - "balance": "133700000000000000000000" - }, - "b50955aa6e341571986608bdc891c2139f540cdf": { - "balance": "1970000000000000000000" - }, - "6d44974a31d187eda16ddd47b9c7ec5002d61fbe": { - "balance": "940000000000000000000" - }, - "80abec5aa36e5c9d098f1b942881bd5acac6963d": { - "balance": "2000000000000000000000" - }, - "294f494b3f2e143c2ffc9738cbfd9501850b874e": { - "balance": "2240000000000000000000" - }, - "bca3ffd4683fba0ad3bbc90734b611da9cfb457e": { - "balance": "200000000000000000000" - }, - "5992624c54cdec60a5ae938033af8be0c50cbb0a": { - "balance": "3621678000000000000000" - }, - "6560941328ff587cbc56c38c78238a7bb5f442f6": { - "balance": "744900000000000000000" - }, - "74b7e0228baed65957aebb4d916d333aae164f0e": { - "balance": "2000000000000000000000" - }, - "8516fcaf77c893970fcd1a958ba9a00e49044019": { - "balance": "196279000000000000000" - }, - "b992a967308c02b98af91ee760fd3b6b4824ab0e": { - "balance": "2000000000000000000000" - }, - "30bb4357cd6910c86d2238bf727cbe8156680e62": { - "balance": "100014000000000000000" - }, - "b8cc0f060aad92d4eb8b36b3b95ce9e90eb383d7": { - "balance": "150000000000000000000000" - }, - "28d4ebf41e3d3c451e943bdd7e1f175fae932a3d": { - "balance": "6000000000000000000000" - }, - "8c83d424a3cf24d51f01923dd54a18d6b6fede7b": { - "balance": "4000000000000000000000" - }, - "7efc90766a00bc52372cac97fabd8a3c831f8ecd": { - "balance": "158000000000000000000" - }, - "7c2b9603884a4f2e464eceb97d17938d828bc02c": { - "balance": "3000000000000000000000" - }, - "9d250ae4f110d71cafc7b0adb52e8d9acb6679b8": { - "balance": "9840000000000000000000" - }, - "61b3df2e9e9fd968131f1e88f0a0eb5bd765464d": { - "balance": "4000000000000000000000" - }, - "9ae13bd882f2576575921a94974cbea861ba0d35": { - "balance": "3160000000000000000000" - }, - "3d09688d93ad07f3abe68c722723cd680990435e": { - "balance": "29999948000000000000000" - }, - "5e58e255fc19870a04305ff2a04631f2ff294bb1": { - "balance": "17600000000000000000" - }, - "bcaed0acb6a76f113f7c613555a2c3b0f5bf34a5": { - "balance": "193600000000000000000" - }, - "159adce27aa10b47236429a34a5ac42cad5b6416": { - "balance": "31867951000000000000000" - }, - "e834c64318205ca7dd4a21abcb08266cb21ff02c": { - "balance": "999999000000000000000" - }, - "7b6a84718dd86e63338429ac811d7c8a860f21f1": { - "balance": "1790000000000000000000" - }, - "2118c116ab0cdf6fd11d54a4309307b477c3fc0f": { - "balance": "10000000000000000000000" - }, - "34a901a69f036bcf9f7843c0ba01b426e8c3dc2b": { - "balance": "4000000000000000000000" - }, - "c7d44fe32c7f8cd5f1a97427b6cd3afc9e45023e": { - "balance": "1580000000000000000000" - }, - "c6045b3c350b4ce9ca0c6b754fb41a69b97e9900": { - "balance": "925000000000000000000" - }, - "cf5a6f9df75579c644f794711215b30d77a0ce40": { - "balance": "2000000000000000000000" - }, - "e2904b1aefa056398b6234cb35811288d736db67": { - "balance": "40000000000000000000" - }, - "7101bd799e411cde14bdfac25b067ac890eab8e8": { - "balance": "1450054000000000000000" - }, - "cc45fb3a555bad807b388a0357c855205f7c75e8": { - "balance": "865000000000000000000" - }, - "ff0c3c7798e8733dd2668152891bab80a8be955c": { - "balance": "80220000000000000000" - }, - "3536453322c1466cb905af5c335ca8db74bff1e6": { - "balance": "447000000000000000000" - }, - "08cac8952641d8fc526ec1ab4f2df826a5e7710f": { - "balance": "300000000000000000000" - }, - "0d8aab8f74ea862cdf766805009d3f3e42d8d00b": { - "balance": "5820000000000000000000" - }, - "8908760cd39b9c1e8184e6a752ee888e3f0b7045": { - "balance": "6000000000000000000000" - }, - "8156360bbd370961ceca6b6691d75006ad204cf2": { - "balance": "40000000000000000000000" - }, - "a304588f0d850cd8d38f76e9e83c1bf63e333ede": { - "balance": "39800000000000000000" - }, - "14c63ba2dcb1dd4df33ddab11c4f0007fa96a62d": { - "balance": "15500000000000000000000" - }, - "a009bf076f1ba3fa57d2a7217218bed5565a7a7a": { - "balance": "1000000000000000000000" - }, - "1c89060f987c518fa079ec2c0a5ebfa30f5d20f7": { - "balance": "38000000000000000000000" - }, - "8895eb726226edc3f78cc6a515077b3296fdb95e": { - "balance": "3940000000000000000000" - }, - "7919e7627f9b7d54ea3b14bb4dd4649f4f39dee0": { - "balance": "1670000000000000000000" - }, - "b3c65b845aba6cd816fbaae983e0e46c82aa8622": { - "balance": "1000000000000000000000" - }, - "eff51d72adfae143edf3a42b1aec55a2ccdd0b90": { - "balance": "300000000000000000000" - }, - "05bb64a916be66f460f5e3b64332110d209e19ae": { - "balance": "4200000000000000000000" - }, - "d5b117ec116eb846418961eb7edb629cd0dd697f": { - "balance": "3000000000000000000000" - }, - "05e97b09492cd68f63b12b892ed1d11d152c0eca": { - "balance": "1015200000000000000000" - }, - "84cc7878da605fdb019fab9b4ccfc157709cdda5": { - "balance": "1336922000000000000000" - }, - "79cac6494f11ef2798748cb53285bd8e22f97cda": { - "balance": "2000000000000000000000" - }, - "bd5a8c94bd8be6470644f70c8f8a33a8a55c6341": { - "balance": "200000000000000000000" - }, - "b119e79aa9b916526581cbf521ef474ae84dcff4": { - "balance": "1470700000000000000000" - }, - "aff1045adf27a1aa329461b24de1bae9948a698b": { - "balance": "33400000000000000000" - }, - "4398628ea6632d393e929cbd928464c568aa4a0c": { - "balance": "1400000000000000000000" - }, - "99997668f7c1a4ff9e31f9977ae3224bcb887a85": { - "balance": "291200000000000000000" - }, - "bc0e8745c3a549445c2be900f52300804ab56289": { - "balance": "33104697000000000000000" - }, - "e5bab4f0afd8a9d1a381b45761aa18f3d3cce105": { - "balance": "1508010000000000000000" - }, - "be60037e90714a4b917e61f193d834906703b13a": { - "balance": "1700000000000000000000" - }, - "8ed4284c0f47449c15b8d9b3245de8beb6ce80bf": { - "balance": "800000000000000000000" - }, - "333ad1596401e05aea2d36ca47318ef4cd2cb3df": { - "balance": "2910000000000000000000" - }, - "22db559f2c3c1475a2e6ffe83a5979599196a7fa": { - "balance": "1000000000000000000000" - }, - "fdf449f108c6fb4f5a2b081eed7e45e6919e4d25": { - "balance": "2000000000000000000000" - }, - "0be1bcb90343fae5303173f461bd914a4839056c": { - "balance": "6000000000000000000000" - }, - "b981ad5e6b7793a23fc6c1e8692eb2965d18d0da": { - "balance": "9999924000000000000000" - }, - "c75d2259306aec7df022768c69899a652185dbc4": { - "balance": "4000000000000000000000" - }, - "6c2e9be6d4ab450fd12531f33f028c614674f197": { - "balance": "3580000000000000000000" - }, - "6dcc7e64fcafcbc2dc6c0e5e662cb347bffcd702": { - "balance": "20000000000000000000000" - }, - "aabdb35c1514984a039213793f3345a168e81ff1": { - "balance": "309760000000000000000" - }, - "d315deea1d8c1271f9d1311263ab47c007afb6f5": { - "balance": "69760000000000000000" - }, - "4faf90b76ecfb9631bf9022176032d8b2c207009": { - "balance": "1000032000000000000000" - }, - "3e7a966b5dc357ffb07e9fe067c45791fd8e3049": { - "balance": "59100000000000000000" - }, - "2e64a8d71111a22f4c5de1e039b336f68d398a7c": { - "balance": "2000000000000000000000" - }, - "181fbba852a7f50178b1c7f03ed9e58d54162929": { - "balance": "666000000000000000000" - }, - "4f7330096f79ed264ee0127f5d30d2f73c52b3d8": { - "balance": "499970000000000000000" - }, - "a8a8dbdd1a85d1beee2569e91ccc4d09ae7f6ea1": { - "balance": "5800000000000000000000" - }, - "1f9c3268458da301a2be5ab08257f77bb5a98aa4": { - "balance": "200000000000000000000" - }, - "fc372ff6927cb396d9cf29803500110da632bc52": { - "balance": "2000000000000000000000" - }, - "4fa554ab955c249217386a4d3263bbf72895434e": { - "balance": "19982000000000000000" - }, - "2a59e47ea5d8f0e7c028a3e8e093a49c1b50b9a3": { - "balance": "2000000000000000000000" - }, - "5e32c72191b8392c55f510d8e3326e3a60501d62": { - "balance": "44000000000000000000000" - }, - "1dfaee077212f1beaf0e6f2f1840537ae154ad86": { - "balance": "1000000000000000000000" - }, - "7eaba035e2af3793fd74674b102540cf190addb9": { - "balance": "1273000000000000000000" - }, - "d62edb96fce2969aaf6c545e967cf1c0bc805205": { - "balance": "85705000000000000000" - }, - "220dc68df019b6b0ccbffb784b5a5ab4b15d4060": { - "balance": "3940000000000000000000" - }, - "45bb829652d8bfb58b8527f0ecb621c29e212ec3": { - "balance": "2000000000000000000000" - }, - "79b120eb8806732321288f675a27a9225f1cd2eb": { - "balance": "2465000000000000000000" - }, - "740af1eefd3365d78ba7b12cb1a673e06a077246": { - "balance": "19700000000000000000000" - }, - "0f042c9c2fb18766f836bb59f735f27dc329fe3c": { - "balance": "10000000000000000000000" - }, - "6dda5f788a6c688ddf921fa3852eb6d6c6c62966": { - "balance": "40000000000000000000" - }, - "96ad579bbfa8db8ebec9d286a72e4661eed8e356": { - "balance": "1070750000000000000000" - }, - "0c2073ba44d3ddbdb639c04e191039a71716237f": { - "balance": "1430000000000000000000" - }, - "1a3520453582c718a21c42375bc50773255253e1": { - "balance": "790000000000000000000" - }, - "efcaae9ff64d2cd95b5249dcffe7faa0a0c0e44d": { - "balance": "401100000000000000000" - }, - "0a3de155d5ecd8e81c1ff9bbf0378301f8d4c623": { - "balance": "4000000000000000000000" - }, - "80f07ac09e7b2c3c0a3d1e9413a544c73a41becb": { - "balance": "20000000000000000000" - }, - "c3631c7698b6c5111989bf452727b3f9395a6dea": { - "balance": "10683500000000000000000" - }, - "4cc22c9bc9ad05d875a397dbe847ed221c920c67": { - "balance": "2000000000000000000000" - }, - "1a987e3f83de75a42f1bde7c997c19217b4a5f24": { - "balance": "2000000000000000000000" - }, - "5b2b64e9c058e382a8b299224eecaa16e09c8d92": { - "balance": "161000000000000000000" - }, - "86caafacf32aa0317c032ac36babed974791dc03": { - "balance": "40000000000000000000000" - }, - "1cd1f0a314cbb200de0a0cb1ef97e920709d97c2": { - "balance": "2000000000000000000000" - }, - "7d980f4b566bb045517e4c14c87750de9346744b": { - "balance": "1337000000000000000000" - }, - "8b5f29cc2faa262cdef30ef554f50eb488146eac": { - "balance": "5818250000000000000000" - }, - "5153a0c3c8912881bf1c3501bf64b45649e48222": { - "balance": "4000000000000000000000" - }, - "d21a7341eb84fd151054e5e387bb25d36e499c09": { - "balance": "14000000000000000000000" - }, - "9560e8ac6718a6a1cdcff189d603c9063e413da6": { - "balance": "4000000000000000000000" - }, - "e49ba0cd96816c4607773cf8a5970bb5bc16a1e6": { - "balance": "1670000000000000000000" - }, - "b8ac117d9f0dba80901445823c4c9d4fa3fedc6e": { - "balance": "15759015000000000000000" - }, - "af67fd3e127fd9dc36eb3fcd6a80c7be4f7532b2": { - "balance": "1670000000000000000000" - }, - "b43c27f7a0a122084b98f483922541c8836cee2c": { - "balance": "715000000000000000000" - }, - "4d9279962029a8bd45639737e98b511eff074c21": { - "balance": "1337000000000000000000" - }, - "c667441e7f29799aba616451d53b3f489f9e0f48": { - "balance": "13920000000000000000000" - }, - "275875ff4fbb0cf3a430213127487f7608d04cba": { - "balance": "500080000000000000000" - }, - "9a953b5bcc709379fcb559d7b916afdaa50cadcc": { - "balance": "100000000000000000000" - }, - "7ea791ebab0445a00efdfc4e4a8e9a7e7565136d": { - "balance": "18200000000000000000" - }, - "6ffe5cf82cc9ea5e36cad7c2974ce7249f3749e6": { - "balance": "1940000000000000000000" - }, - "f1b4ecc63525f7432c3d834ffe2b970fbeb87212": { - "balance": "3000064000000000000000" - }, - "6b72a8f061cfe6996ad447d3c72c28c0c08ab3a7": { - "balance": "4271316000000000000000" - }, - "bba3c68004248e489573abb2743677066b24c8a7": { - "balance": "2000000000000000000000" - }, - "b7c0d0cc0b4d342d4062bac624ccc3c70cc6da3f": { - "balance": "4000000000000000000000" - }, - "fe98c664c3e447a95e69bd582171b7176ea2a685": { - "balance": "4000000000000000000000" - }, - "ce71086d4c602554b82dcbfce88d20634d53cc4d": { - "balance": "43250000000000000000000" - }, - "1c601993789207f965bb865cbb4cd657cce76fc0": { - "balance": "98294000000000000000" - }, - "476b5599089a3fb6f29c6c72e49b2e4740ea808d": { - "balance": "2800000000000000000000" - }, - "3439998b247cb4bf8bc80a6d2b3527f1dfe9a6d2": { - "balance": "140000000000000000000" - }, - "c4f7d2e2e22084c44f70feaab6c32105f3da376f": { - "balance": "1970000000000000000000" - }, - "c1eba5684aa1b24cba63150263b7a9131aeec28d": { - "balance": "20000000000000000000" - }, - "94ad4bad824bd0eb9ea49c58cebcc0ff5e08346b": { - "balance": "1940000000000000000000" - }, - "ded877378407b94e781c4ef4af7cfc5bc220b516": { - "balance": "372500000000000000000" - }, - "699c9ee47195511f35f862ca4c22fd35ae8ffbf4": { - "balance": "80000000000000000000" - }, - "e3a89a1927cc4e2d43fbcda1e414d324a7d9e057": { - "balance": "205500000000000000000" - }, - "4d93696fa24859f5d2939aebfa54b4b51ae1dccc": { - "balance": "19100000000000000000" - }, - "0af65f14784e55a6f95667fd73252a1c94072d2a": { - "balance": "192987000000000000000" - }, - "5b70c49cc98b3df3fbe2b1597f5c1b6347a388b7": { - "balance": "970000000000000000000" - }, - "426f78f70db259ac8534145b2934f4ef1098b5d8": { - "balance": "360000000000000000000" - }, - "58b8ae8f63ef35ed0762f0b6233d4ac14e64b64d": { - "balance": "2000000000000000000000" - }, - "8eae29435598ba8f1c93428cdb3e2b4d31078e00": { - "balance": "2000000000000000000000" - }, - "17fd9b551a98cb61c2e07fbf41d3e8c9a530cba5": { - "balance": "26989000000000000000" - }, - "ab3e78294ba886a0cfd5d3487fb3a3078d338d6e": { - "balance": "1970000000000000000000" - }, - "bdf6e68c0cd7584080e847d72cbb23aad46aeb1d": { - "balance": "1970000000000000000000" - }, - "f989346772995ec1906faffeba2a7fe7de9c6bab": { - "balance": "6685000000000000000000" - }, - "dc5f5ad663a6f263327d64cac9cb133d2c960597": { - "balance": "2000000000000000000000" - }, - "68fe1357218d095849cd579842c4aa02ff888d93": { - "balance": "2000000000000000000000" - }, - "e09c68e61998d9c81b14e4ee802ba7adf6d74cdb": { - "balance": "4000000000000000000000" - }, - "890fe11f3c24db8732d6c2e772e2297c7e65f139": { - "balance": "62980000000000000000000" - }, - "a76929890a7b47fb859196016c6fdd8289ceb755": { - "balance": "5000000000000000000000" - }, - "2dc79d6e7f55bce2e2d0c02ad07ceca8bb529354": { - "balance": "1580000000000000000000" - }, - "19687daa39c368139b6e7be60dc1753a9f0cbea3": { - "balance": "8000000000000000000000" - }, - "c69be440134d6280980144a9f64d84748a37f349": { - "balance": "715000000000000000000" - }, - "3d8d0723721e73a6c0d860aa0557abd14c1ee362": { - "balance": "5000000000000000000000" - }, - "2b241f037337eb4acc61849bd272ac133f7cdf4b": { - "balance": "378000000000000000000000" - }, - "24b95ebef79500baa0eda72e77f877415df75c33": { - "balance": "910000000000000000000" - }, - "106ed5c719b5261477890425ae7551dc59bd255c": { - "balance": "11979600000000000000000" - }, - "5b2e2f1618552eab0db98add55637c2951f1fb19": { - "balance": "12000000000000000000000" - }, - "403145cb4ae7489fcc90cd985c6dc782b3cc4e44": { - "balance": "5999800000000000000000" - }, - "e8be24f289443ee473bc76822f55098d89b91cc5": { - "balance": "2000000000000000000000" - }, - "f6bc37b1d2a3788d589b6de212dc1713b2f6e78e": { - "balance": "5000000000000000000000" - }, - "67fc527dce1785f0fb8bc7e518b1c669f7ecdfb5": { - "balance": "240000000000000000000" - }, - "6580b1bc94390f04b397bd73e95d96ef11eaf3a8": { - "balance": "20000000000000000000" - }, - "98bf4af3810b842387db70c14d46099626003d10": { - "balance": "4000000000000000000000" - }, - "17993d312aa1106957868f6a55a5e8f12f77c843": { - "balance": "450065000000000000000" - }, - "0729b4b47c09eb16158464c8aa7fd9690b438839": { - "balance": "1999800000000000000000" - }, - "ae70e69d2c4a0af818807b1a2705f79fd0b5dbc4": { - "balance": "985000000000000000000" - }, - "38b50146e71916a5448de12a4d742135dcf39833": { - "balance": "32200000000000000000000" - }, - "38439aaa24e3636f3a18e020ea1da7e145160d86": { - "balance": "2600000000000000000000" - }, - "54b4429b182f0377be7e626939c5db6440f75d7a": { - "balance": "1970000000000000000000" - }, - "7179726f5c71ae1b6d16a68428174e6b34b23646": { - "balance": "7353500000000000000000" - }, - "c2ee91d3ef58c9d1a589844ea1ae3125d6c5ba69": { - "balance": "970000000000000000000" - }, - "912304118b80473d9e9fe3ee458fbe610ffda2bb": { - "balance": "200000000000000000000" - }, - "3308b03466c27a17dfe1aafceb81e16d2934566f": { - "balance": "17000000000000000000000" - }, - "10346414bec6d3dcc44e50e54d54c2b8c3734e3e": { - "balance": "4000000000000000000000" - }, - "4fee50c5f988206b09a573469fb1d0b42ebb6dce": { - "balance": "2009400000000000000000" - }, - "9ece1400800936c7c6485fcdd3626017d09afbf6": { - "balance": "310000000000000000000" - }, - "ddf3ad76353810be6a89d731b787f6f17188612b": { - "balance": "20000000000000000000000" - }, - "72402300e81d146c2e644e2bbda1da163ca3fb56": { - "balance": "7000000000000000000000" - }, - "bb4b4a4b548070ff41432c9e08a0ca6fa7bc9f76": { - "balance": "850000000000000000000" - }, - "c3dd58903886303b928625257ae1a013d71ae216": { - "balance": "2000000000000000000000" - }, - "ca6c818befd251361e02744068be99d8aa60b84a": { - "balance": "6000000000000000000000" - }, - "b8d2ddc66f308c0158ae3ccb7b869f7d199d7b32": { - "balance": "844800000000000000000" - }, - "8e486a0442d171c8605be348fee57eb5085eff0d": { - "balance": "4000000000000000000000" - }, - "a807104f2703d679f8deafc442befe849e42950b": { - "balance": "2000000000000000000000" - }, - "bb61a04bffd57c10470d45c39103f64650347616": { - "balance": "1000000000000000000000" - }, - "d1c45954a62b911ad701ff2e90131e8ceb89c95c": { - "balance": "1394000000000000000000" - }, - "5e65458be964ae449f71773704979766f8898761": { - "balance": "528600000000000000000" - }, - "f9b37825f03073d31e249378c30c795c33f83af2": { - "balance": "200152000000000000000" - }, - "e309974ce39d60aadf2e69673251bf0e04760a10": { - "balance": "254030000000000000000" - }, - "d541ac187ad7e090522de6da3213e9a7f4439673": { - "balance": "2000000000000000000000" - }, - "f33efc6397aa65fb53a8f07a0f893aae30e8bcee": { - "balance": "2304850000000000000000" - }, - "d2f1998e1cb1580cec4f6c047dcd3dcec54cf73c": { - "balance": "200000000000000000000" - }, - "0ed76c2c3b5d50ff8fb50b3eeacd681590be1c2d": { - "balance": "100000000000000000000" - }, - "637d67d87f586f0a5a479e20ee13ea310a10b647": { - "balance": "48300000000000000000000" - }, - "1a5ee533acbfb3a2d76d5b685277b796c56a052b": { - "balance": "2000000000000000000000" - }, - "323fca5ed77f699f9d9930f5ceeff8e56f59f03c": { - "balance": "1337000000000000000000" - }, - "a5fe2ce97f0e8c3856be0de5f4dcb2ce5d389a16": { - "balance": "22892000000000000000" - }, - "93258255b37c7f58f4b10673a932dd3afd90f4f2": { - "balance": "1000000000000000000000" - }, - "950fe9c6cad50c18f11a9ed9c45740a6180612d0": { - "balance": "8000000000000000000000" - }, - "ee31167f9cc93b3c6465609d79db0cde90e8484c": { - "balance": "2000000000000000000000" - }, - "6ebb5e6957aa821ef659b6018a393a504cae4450": { - "balance": "2000000000000000000000" - }, - "be305a796e33bbf7f9aeae6512959066efda1010": { - "balance": "10880000000000000000000" - }, - "537f9d4d31ef70839d84b0d9cdb72b9afedbdf35": { - "balance": "70000000000000000000000" - }, - "fe9e1197d7974a7648dcc7a03112a88edbc9045d": { - "balance": "4925000000000000000000" - }, - "99f77f998b20e0bcdcd9fc838641526cf25918ef": { - "balance": "1790000000000000000000" - }, - "76ffc157ad6bf8d56d9a1a7fddbc0fea010aabf4": { - "balance": "1000000000000000000000" - }, - "defe9141f4704599159d7b223de42bffd80496b3": { - "balance": "100000000000000000000" - }, - "7b1bf53a9cbe83a7dea434579fe72aac8d2a0cd0": { - "balance": "199800000000000000000" - }, - "23ccc3c6acd85c2e460c4ffdd82bc75dc849ea14": { - "balance": "4000000000000000000000" - }, - "9f86a066edb61fcb5856de93b75c8c791864b97b": { - "balance": "2000000000000000000000" - }, - "871b8a8b51dea1989a5921f13ec1a955a515ad47": { - "balance": "8000000000000000000000" - }, - "4efcd9c79fb4334ca6247b0a33bd9cc33208e272": { - "balance": "1337000000000000000000" - }, - "35ac1d3ed7464fa3db14e7729213ceaa378c095e": { - "balance": "1520000000000000000000" - }, - "c69d663c8d60908391c8d236191533fdf7775613": { - "balance": "485000000000000000000" - }, - "c2ed5ffdd1add855a2692fe062b5d618742360d4": { - "balance": "1200000000000000000000" - }, - "454f0141d721d33cbdc41018bd01119aa4784818": { - "balance": "6000000000000000000000" - }, - "6c8687e3417710bb8a93559021a1469e6a86bc77": { - "balance": "11126675000000000000000" - }, - "ec5b198a00cfb55a97b5d53644cffa8a04d2ab45": { - "balance": "2000000000000000000000" - }, - "cd59f3dde77e09940befb6ee58031965cae7a336": { - "balance": "10000000000000000000000" - }, - "8eebec1a62c08b05a7d1d59180af9ff0d18e3f36": { - "balance": "500000000000000000000" - }, - "92a971a739799f8cb48ea8475d72b2d2474172e6": { - "balance": "3940000000000000000000" - }, - "bed4649df646e2819229032d8868556fe1e053d3": { - "balance": "18200000000000000000" - }, - "c50fe415a641b0856c4e75bf960515441afa358d": { - "balance": "2000000000000000000000" - }, - "91f516146cda20281719978060c6be4149067c88": { - "balance": "2000000000000000000000" - }, - "54a1370116fe22099e015d07cd2669dd291cc9d1": { - "balance": "20000000000000000000" - }, - "80c04efd310f440483c73f744b5b9e64599ce3ec": { - "balance": "1200000000000000000000" - }, - "a8914c95b560ec13f140577338c32bcbb77d3a7a": { - "balance": "180000000000000000000" - }, - "e3c812737ac606baf7522ad817428a36050e7a34": { - "balance": "1940000000000000000000" - }, - "6d1456fff0104ee844a3314737843338d24cd66c": { - "balance": "141840000000000000000" - }, - "0e6ece99111cad1961c748ed3df51edd69d2a3b1": { - "balance": "100000000000000000000000" - }, - "019d709579ff4bc09fdcdde431dc1447d2c260bc": { - "balance": "20000000000000000000" - }, - "ebff84bbef423071e604c361bba677f5593def4e": { - "balance": "10000000000000000000000" - }, - "e10c540088113fa6ec00b4b2c8824f8796e96ec4": { - "balance": "236400000000000000000000" - }, - "e03220c697bcd28f26ef0b74404a8beb06b2ba7b": { - "balance": "8000000000000000000000" - }, - "e69a6cdb3a8a7db8e1f30c8b84cd73bae02bc0f8": { - "balance": "16915503000000000000000" - }, - "e5fb31a5caee6a96de393bdbf89fbe65fe125bb3": { - "balance": "1000000000000000000000" - }, - "030fb3401f72bd3418b7d1da75bf8c519dd707dc": { - "balance": "3000000000000000000000" - }, - "1c751e7f24df9d94a637a5dedeffc58277b5db19": { - "balance": "3220000000000000000000" - }, - "bded7e07d0711e684de65ac8b2ab57c55c1a8645": { - "balance": "591000000000000000000" - }, - "dd7ff441ba6ffe3671f3c0dabbff1823a5043370": { - "balance": "2000000000000000000000" - }, - "b55474ba58f0f2f40e6cbabed4ea176e011fcad6": { - "balance": "1970000000000000000000" - }, - "b92427ad7578b4bfe20a9f63a7c5506d5ca12dc8": { - "balance": "2000000000000000000000" - }, - "91a8baaed012ea2e63803b593d0d0c2aab4c5b0a": { - "balance": "1500000000000000000000" - }, - "a97e072144499fe5ebbd354acc7e7efb58985d08": { - "balance": "2674000000000000000000" - }, - "75c2ffa1bef54919d2097f7a142d2e14f9b04a58": { - "balance": "2673866000000000000000" - }, - "53faf165be031ec18330d9fce5bd1281a1af08db": { - "balance": "140000000000000000000" - }, - "055ab658c6f0ed4f875ed6742e4bc7292d1abbf0": { - "balance": "83500000000000000000" - }, - "6f18ec767e320508195f1374500e3f2e125689ff": { - "balance": "1000000000000000000000" - }, - "90fc537b210658660a83baa9ac4a8402f65746a8": { - "balance": "1880000000000000000000" - }, - "34664d220fa7f37958024a3332d684bcc6d4c8bd": { - "balance": "10000000000000000000000" - }, - "15acb61568ec4af7ea2819386181b116a6c5ee70": { - "balance": "31000000000000000000000" - }, - "69d98f38a3ba3dbc01fa5c2c1427d862832f2f70": { - "balance": "100000000000000000000000" - }, - "ece1152682b7598fe2d1e21ec15533885435ac85": { - "balance": "4000000000000000000000" - }, - "f618d9b104411480a863e623fc55232d1a4f48aa": { - "balance": "265793000000000000000" - }, - "f9debaecb5f339beea4894e5204bfa340d067f25": { - "balance": "1665000000000000000000" - }, - "5e731b55ced452bb3f3fe871ddc3ed7ee6510a8f": { - "balance": "3000000000000000000000" - }, - "67df242d240dd4b8071d72f8fcf35bb3809d71e8": { - "balance": "4000000000000000000000" - }, - "c4cf930e5d116ab8d13b9f9a7ec4ab5003a6abde": { - "balance": "320000000000000000000" - }, - "01a25a5f5af0169b30864c3be4d7563ccd44f09e": { - "balance": "1430000000000000000000" - }, - "7f6efb6f4318876d2ee624e27595f44446f68e93": { - "balance": "1550000000000000000000" - }, - "82249fe70f61c6b16f19a324840fdc020231bb02": { - "balance": "9504014000000000000000" - }, - "205237c4be146fba99478f3a7dad17b09138da95": { - "balance": "2000000000000000000000" - }, - "fd1fb5a89a89a721b8797068fbc47f3e9d52e149": { - "balance": "236400000000000000000" - }, - "e47fbaed99fc209962604ebd20e240f74f4591f1": { - "balance": "2000000000000000000000" - }, - "a24c3ab62181e9a15b78c4621e4c7c588127be26": { - "balance": "162410000000000000000" - }, - "b6cd7432d5161be79768ad45de3e447a07982063": { - "balance": "4000000000000000000000" - }, - "32a70691255c9fc9791a4f75c8b81f388e0a2503": { - "balance": "985000000000000000000" - }, - "562f16d79abfcec3943e34b20f05f97bdfcda605": { - "balance": "4000000000000000000000" - }, - "dbc66965e426ff1ac87ad6eb78c1d95271158f9f": { - "balance": "18200000000000000000" - }, - "7e87863ec43a481df04d017762edcb5caa629b5a": { - "balance": "39400000000000000000" - }, - "587d6849b168f6c3332b7abae7eb6c42c37f48bf": { - "balance": "880000000000000000000" - }, - "721158be5762b119cc9b2035e88ee4ee78f29b82": { - "balance": "10000000000000000000000" - }, - "84b91e2e2902d05e2b591b41083bd7beb2d52c74": { - "balance": "9848621000000000000000" - }, - "632cecb10cfcf38ec986b43b8770adece9200221": { - "balance": "20000000000000000000" - }, - "c34e3ba1322ed0571183a24f94204ee49c186641": { - "balance": "58200000000000000000" - }, - "ae78bb849139a6ba38ae92a09a69601cc4cb62d1": { - "balance": "500000000000000000000" - }, - "5ce0b6862cce9162e87e0849e387cb5df4f9118c": { - "balance": "1670000000000000000000" - }, - "f52c0a7877345fe0c233bb0f04fd6ab18b6f14ba": { - "balance": "400440000000000000000000" - }, - "e016dc138e25815b90be3fe9eee8ffb2e105624f": { - "balance": "500000000000000000000" - }, - "5789d01db12c816ac268e9af19dc0dd6d99f15df": { - "balance": "200000000000000000000" - }, - "d8b77db9b81bbe90427b62f702b201ffc29ff618": { - "balance": "930200000000000000000" - }, - "5dff811dad819ece3ba602c383fb5dc64c0a3a48": { - "balance": "186000000000000000000" - }, - "af3087e62e04bf900d5a54dc3e946274da92423b": { - "balance": "20000000000000000000" - }, - "8c1023fde1574db8bb54f1739670157ca47da652": { - "balance": "6969382000000000000000" - }, - "bb3b010b18e6e2be1135871026b7ba15ea0fde24": { - "balance": "10044000000000000000000" - }, - "cabdaf354f4720a466a764a528d60e3a482a393c": { - "balance": "1000000000000000000000" - }, - "94bbc67d13f89ebca594be94bc5170920c30d9f3": { - "balance": "80200000000000000000" - }, - "3275496fd4dd8931fd69fb0a0b04c4d1ff879ef5": { - "balance": "446000000000000000000" - }, - "281250a29121270a4ee5d78d24feafe82c70ba3a": { - "balance": "1000000000000000000000" - }, - "590ccb5911cf78f6f622f535c474375f4a12cfcf": { - "balance": "20000000000000000000000" - }, - "542e8096bafb88162606002e8c8a3ed19814aeac": { - "balance": "2000000000000000000000" - }, - "a65426cff378ed23253513b19f496de45fa7e18f": { - "balance": "7200000000000000000000" - }, - "4aa693b122f314482a47b11cc77c68a497876162": { - "balance": "1970000000000000000000" - }, - "d9b783d31d32adc50fa3eacaa15d92b568eaeb47": { - "balance": "34010000000000000000000" - }, - "068e655766b944fb263619658740b850c94afa31": { - "balance": "35200000000000000000" - }, - "9e23c5e4b782b00a5fadf1aead87dacf5b0367a1": { - "balance": "20000000000000000000" - }, - "bf17f397f8f46f1bae45d187148c06eeb959fa4d": { - "balance": "1001440000000000000000" - }, - "8578e10212ca14ff0732a8241e37467db85632a9": { - "balance": "6000000000000000000000" - }, - "2cb5495a505336c2465410d1cae095b8e1ba5cdd": { - "balance": "20000000000000000000000" - }, - "695b0f5242753701b264a67071a2dc880836b8db": { - "balance": "16400000000000000000" - }, - "f2edde37f9a8c39ddea24d79f4015757d06bf786": { - "balance": "100000000000000000000000" - }, - "480f31b989311e4124c6a7465f5a44094d36f9d0": { - "balance": "1025000000000000000000" - }, - "cf157612764e0fd696c8cb5fba85df4c0ddc3cb0": { - "balance": "30000000000000000000000" - }, - "27521deb3b6ef1416ea4c781a2e5d7b36ee81c61": { - "balance": "2000000000000000000000" - }, - "6efd90b535e00bbd889fda7e9c3184f879a151db": { - "balance": "10100000000000000000000" - }, - "b635a4bc71fb28fdd5d2c322983a56c284426e69": { - "balance": "170000000000000000000" - }, - "a17c9e4323069518189d5207a0728dcb92306a3f": { - "balance": "1000000000000000000000" - }, - "6af940f63ec9b8d876272aca96fef65cdacecdea": { - "balance": "3000000000000000000000" - }, - "469358709332c82b887e20bcddd0220f8edba7d0": { - "balance": "17300000000000000000000" - }, - "a257ad594bd88328a7d90fc0a907df95eecae316": { - "balance": "520510000000000000000" - }, - "6f051666cb4f7bd2b1907221b829b555d7a3db74": { - "balance": "1760000000000000000000" - }, - "46bfc5b207eb2013e2e60f775fecd71810c5990c": { - "balance": "1550000000000000000000" - }, - "62b9081e7710345e38e02e16449ace1b85bcfc4e": { - "balance": "910000000000000000000" - }, - "bc73f7b1ca3b773b34249ada2e2c8a9274cc17c2": { - "balance": "2000000000000000000000" - }, - "1adaf4abfa867db17f99af6abebf707a3cf55df6": { - "balance": "6000000000000000000000" - }, - "8d629c20608135491b5013f1002586a0383130e5": { - "balance": "1370000000000000000000" - }, - "38e46de4453c38e941e7930f43304f94bb7b2be8": { - "balance": "2005500000000000000000" - }, - "3485f621256433b98a4200dad857efe55937ec98": { - "balance": "2000000000000000000000" - }, - "775c10c93e0db7205b2643458233c64fc33fd75b": { - "balance": "2000000000000000000000" - }, - "7c4401ae98f12ef6de39ae24cf9fc51f80eba16b": { - "balance": "200000000000000000000" - }, - "17b807afa3ddd647e723542e7b52fee39527f306": { - "balance": "400010000000000000000" - }, - "0ab366e6e7d5abbce6b44a438d69a1cabb90d133": { - "balance": "320000000000000000000" - }, - "194ffe78bbf5d20dd18a1f01da552e00b7b11db1": { - "balance": "7000000000000000000000" - }, - "c45d47ab0c9aa98a5bd62d16223ea2471b121ca4": { - "balance": "593640000000000000000" - }, - "2487c3c4be86a2723d917c06b458550170c3edba": { - "balance": "1000000000000000000000" - }, - "ec4d08aa2e47496dca87225de33f2b40a8a5b36f": { - "balance": "158000000000000000000" - }, - "aaa8defe11e3613f11067fb983625a08995a8dfc": { - "balance": "200000000000000000000" - }, - "50bb67c8b8d8bd0f63c4760904f2d333f400aace": { - "balance": "2000000000000000000000" - }, - "1227e10a4dbf9caca31b1780239f557615fc35c1": { - "balance": "200000000000000000000" - }, - "44a8989e32308121f72466978db395d1f76c3a4b": { - "balance": "7236900000000000000000" - }, - "59569a21d28fba4bda37753405a081f2063da150": { - "balance": "4000000000000000000000" - }, - "c3756bcdcc7eec74ed896adfc335275930266e08": { - "balance": "6000000000000000000000" - }, - "ce3a61f0461b00935e85fa1ead82c45e5a64d488": { - "balance": "500000000000000000000" - }, - "012f396a2b5eb83559bac515e5210df2c8c362ba": { - "balance": "200000000000000000000" - }, - "93bc7d9a4abd44c8bbb8fe8ba804c61ad8d6576c": { - "balance": "3999922000000000000000" - }, - "e20bb9f3966419e14bbbaaaa6789e92496cfa479": { - "balance": "3465116000000000000000" - }, - "9eef442d291a447d74c5d253c49ef324eac1d8f0": { - "balance": "3420000000000000000000" - }, - "db6c2a73dac7424ab0d031b66761122566c01043": { - "balance": "3000000000000000000000" - }, - "704d243c2978e46c2c86adbecd246e3b295ff633": { - "balance": "2012000000000000000000" - }, - "d2ff672016f63b2f85398f4a6fedbb60a50d3cce": { - "balance": "342500000000000000000" - }, - "d2051cb3cb6704f0548cc890ab0a19db3415b42a": { - "balance": "334000000000000000000" - }, - "1111e5dbf45e6f906d62866f1708101788ddd571": { - "balance": "1300200000000000000000" - }, - "6a686bf220b593deb9b7324615fb9144ded3f39d": { - "balance": "1460000000000000000000" - }, - "911feea61fe0ed50c5b9e5a0d66071399d28bdc6": { - "balance": "60000000000000000000" - }, - "3881defae1c07b3ce04c78abe26b0cdc8d73f010": { - "balance": "200000000000000000000" - }, - "ea94f32808a2ef8a9bf0861d1d2404f7b7be258a": { - "balance": "20000000000000000000" - }, - "2eef6b1417d7b10ecfc19b123a8a89e73e526c58": { - "balance": "600000000000000000000" - }, - "dd8af9e7765223f4446f44d3d509819a3d3db411": { - "balance": "10000000000000000000000" - }, - "2efc4c647dac6acac35577ad221758fef6616faa": { - "balance": "8000000000000000000000" - }, - "1547b9bf7ad66274f3413827231ba405ee8c88c1": { - "balance": "17300000000000000000000" - }, - "250a40cef3202397f240469548beb5626af4f23c": { - "balance": "92500000000000000000" - }, - "c175be3194e669422d15fee81eb9f2c56c67d9c9": { - "balance": "200000000000000000000" - }, - "c9e02608066828848aeb28c73672a12925181f4d": { - "balance": "500038000000000000000" - }, - "8229ceb9f0d70839498d44e6abed93c5ca059f5d": { - "balance": "123300000000000000000000" - }, - "39f198331e4b21c1b760a3155f4ab2fe00a74619": { - "balance": "2000000000000000000000" - }, - "3ffcb870d4023d255d5167d8a507cefc366b68ba": { - "balance": "649400000000000000000" - }, - "00dae27b350bae20c5652124af5d8b5cba001ec1": { - "balance": "40000000000000000000" - }, - "fc5500825105cf712a318a5e9c3bfc69c89d0c12": { - "balance": "4000000000000000000000" - }, - "1ed8bb3f06778b039e9961d81cb71a73e6787c8e": { - "balance": "2000000000000000000000" - }, - "530ffac3bc3412e2ec0ea47b7981c770f5bb2f35": { - "balance": "133700000000000000000" - }, - "5f344b01c7191a32d0762ac188f0ec2dd460911d": { - "balance": "1000000000000000000000" - }, - "5cfa9877f719c79d9e494a08d1e41cf103fc87c9": { - "balance": "200000000000000000000" - }, - "f6eaac7032d492ef17fd6095afc11d634f56b382": { - "balance": "500038000000000000000" - }, - "962c0dec8a3d464bf39b1215eafd26480ae490cd": { - "balance": "2001680000000000000000" - }, - "262a8bfd7d9dc5dd3ad78161b6bb560824373655": { - "balance": "1169820000000000000000" - }, - "9b4824ff9fb2abda554dee4fb8cf549165570631": { - "balance": "20000000000000000000" - }, - "bb3b9005f46fd2ca3b30162599928c77d9f6b601": { - "balance": "8000014000000000000000" - }, - "f7dc251196fbcbb77c947d7c1946b0ff65021cea": { - "balance": "1000000000000000000000" - }, - "af1148ef6c8e103d7530efc91679c9ac27000993": { - "balance": "200000000000000000000" - }, - "0bb2650ea01aca755bc0c017b64b1ab5a66d82e3": { - "balance": "1337000000000000000000" - }, - "0cda12bf72d461bbc479eb92e6491d057e6b5ad1": { - "balance": "10000000000000000000000" - }, - "4e5b77f9066159e615933f2dda7477fa4e47d648": { - "balance": "200000000000000000000" - }, - "391161b0e43c302066e8a68d2ce7e199ecdb1d57": { - "balance": "4000000000000000000000" - }, - "c7e330cd0c890ac99fe771fcc7e7b009b7413d8a": { - "balance": "4000000000000000000000" - }, - "d4b38a5fdb63e01714e9801db47bc990bd509183": { - "balance": "5999000000000000000000" - }, - "bc0f98598f88056a26339620923b8f1eb074a9fd": { - "balance": "200000000000000000000" - }, - "dbc59ed88973dead310884223af49763c05030f1": { - "balance": "20000000000000000000" - }, - "0f85e42b1df321a4b3e835b50c00b06173968436": { - "balance": "985000000000000000000" - }, - "d7788ef28658aa06cc53e1f3f0de58e5c371be78": { - "balance": "6685000000000000000000" - }, - "ecd276af64c79d1bd9a92b86b5e88d9a95eb88f8": { - "balance": "20000000000000000000" - }, - "81c9e1aee2d3365d53bcfdcd96c7c538b0fd7eec": { - "balance": "1820000000000000000000" - }, - "5d39ef9ea6bdfff15d11fe91f561a6f9e31f5da5": { - "balance": "2000000000000000000000" - }, - "99878f9d6e0a7ed9aec78297b73879a80195afe0": { - "balance": "3980000000000000000000" - }, - "7294c918b1aefb4d25927ef9d799e71f93a28e85": { - "balance": "197000000000000000000" - }, - "a33f70da7275ef057104dfa7db64f472e9f5d553": { - "balance": "80220000000000000000" - }, - "255bdd6474cc8262f26a22c38f45940e1ceea69b": { - "balance": "4000000000000000000000" - }, - "52f8b509fee1a874ab6f9d87367fbeaf15ac137f": { - "balance": "1000000000000000000000" - }, - "e2728a3e8c2aaac983d05dc6877374a8f446eee9": { - "balance": "197600000000000000000" - }, - "ed0206cb23315128f8caff26f6a30b985467d022": { - "balance": "40000000000000000000000" - }, - "87cf36ad03c9eae9053abb5242de9117bb0f2a0b": { - "balance": "500000000000000000000" - }, - "a929c8bd71db0c308dac06080a1747f21b1465aa": { - "balance": "500000000000000000000" - }, - "9da6e075989c7419094cc9f6d2e49393bb199688": { - "balance": "11100000000000000000000" - }, - "763eece0b08ac89e32bfa4bece769514d8cb5b85": { - "balance": "4000000000000000000000" - }, - "5df3277ca85936c7a0d2c0795605ad25095e7159": { - "balance": "2000000000000000000000" - }, - "7163758cbb6c4c525e0414a40a049dcccce919bb": { - "balance": "200000000000000000000" - }, - "14cdddbc8b09e6675a9e9e05091cb92238c39e1e": { - "balance": "5100000000000000000000" - }, - "b3b7f493b44a2c8d80ec78b1cdc75a652b73b06c": { - "balance": "2000000000000000000000" - }, - "c69b855539ce1b04714728eec25a37f367951de7": { - "balance": "2000000000000000000000" - }, - "052eab1f61b6d45517283f41d1441824878749d0": { - "balance": "4000000000000000000000" - }, - "515651d6db4faf9ecd103a921bbbbe6ae970fdd4": { - "balance": "20000000000000000000000" - }, - "c7aff91929797489555a2ff1d14d5c695a108355": { - "balance": "1000000000000000000000" - }, - "d7ca7fdcfebe4588eff5421d1522b61328df7bf3": { - "balance": "4001070000000000000000" - }, - "eefba12dfc996742db790464ca7d273be6e81b3e": { - "balance": "1000000000000000000000" - }, - "ebaa216de9cc5a43031707d36fe6d5bedc05bdf0": { - "balance": "1969606000000000000000" - }, - "559194304f14b1b93afe444f0624e053c23a0009": { - "balance": "400000000000000000000" - }, - "4ecc19948dd9cd87b4c7201ab48e758f28e7cc76": { - "balance": "500200000000000000000" - }, - "f224eb900b37b4490eee6a0b6420d85c947d8733": { - "balance": "970000000000000000000" - }, - "97810bafc37e84306332aacb35e92ad911d23d24": { - "balance": "1000000000000000000000" - }, - "bd67d2e2f82da8861341bc96a2c0791fddf39e40": { - "balance": "200014000000000000000" - }, - "1b6495891240e64e594493c2662171db5e30ce13": { - "balance": "172400000000000000000" - }, - "00bdd4013aa31c04616c2bc9785f2788f915679b": { - "balance": "13400000000000000000" - }, - "c6ae287ddbe1149ba16ddcca4fe06aa2eaa988a9": { - "balance": "400000000000000000000" - }, - "b7c9f12b038e73436d17e1c12ffe1aeccdb3f58c": { - "balance": "540000000000000000000" - }, - "c1b500011cfba95d7cd636e95e6cbf6167464b25": { - "balance": "200000000000000000000" - }, - "39e0db4d60568c800b8c5500026c2594f5768960": { - "balance": "1000000000000000000000" - }, - "40e3c283f7e24de0410c121bee60a5607f3e29a6": { - "balance": "1000000000000000000000" - }, - "2f7d3290851be5c6b4b43f7d4574329f61a792c3": { - "balance": "100000000000000000000" - }, - "c33ece935a8f4ef938ea7e1bac87cb925d8490ca": { - "balance": "33122000000000000000000" - }, - "57bddf078834009c89d88e6282759dc45335b470": { - "balance": "2148000000000000000000" - }, - "50ad187ab21167c2b6e78be0153f44504a07945e": { - "balance": "100076000000000000000" - }, - "5bd24aac3612b20c609eb46779bf95698407c57c": { - "balance": "1970000000000000000000" - }, - "16526c9edf943efa4f6d0f0bae81e18b31c54079": { - "balance": "985000000000000000000" - }, - "4c6a9dc2cab10abb2e7c137006f08fecb5b779e1": { - "balance": "499000000000000000000" - }, - "02c9f7940a7b8b7a410bf83dc9c22333d4275dd3": { - "balance": "5000000000000000000000" - }, - "b9fd3833e88e7cf1fa9879bdf55af4b99cd5ce3f": { - "balance": "1000000000000000000000" - }, - "7e268f131ddf687cc325c412f78ba961205e9112": { - "balance": "16000600000000000000000" - }, - "180478a655d78d0f3b0c4f202b61485bc4002fd5": { - "balance": "2000000000000000000000" - }, - "ed4014538cee664a2fbcb6dc669f7ab16d0ba57c": { - "balance": "200000000000000000000" - }, - "f63a579bc3eac2a9490410128dbcebe6d9de8243": { - "balance": "1490000000000000000000" - }, - "5d822d9b3ef4b502627407da272f67814a6becd4": { - "balance": "20000000000000000000" - }, - "eb52ab10553492329c1c54833ae610f398a65b9d": { - "balance": "152000000000000000000" - }, - "63340a57716bfa63eb6cd133721202575bf796f0": { - "balance": "209967000000000000000" - }, - "933bf33f8299702b3a902642c33e0bfaea5c1ca3": { - "balance": "15200000000000000000" - }, - "25bc49ef288cd165e525c661a812cf84fbec8f33": { - "balance": "338464000000000000000" - }, - "c8231ba5a411a13e222b29bfc1083f763158f226": { - "balance": "1000090000000000000000" - }, - "6c15ec3520bf8ebbc820bd0ff19778375494cf9d": { - "balance": "2005500000000000000000" - }, - "aaced8a9563b1bc311dbdffc1ae7f57519c4440c": { - "balance": "2000000000000000000000" - }, - "d90f3009db437e4e11c780bec8896f738d65ef0d": { - "balance": "4000000000000000000000" - }, - "5603241eb8f08f721e348c9d9ad92f48e390aa24": { - "balance": "200000000000000000000" - }, - "53cec6c88092f756efe56f7db11228a2db45b122": { - "balance": "4000000000000000000000" - }, - "194cebb4929882bf3b4bf9864c2b1b0f62c283f9": { - "balance": "571300000000000000000" - }, - "4be8628a8154874e048d80c142181022b180bcc1": { - "balance": "60000000000000000000" - }, - "5fd973af366aa5157c54659bcfb27cbfa5ac15d6": { - "balance": "4000000000000000000000" - }, - "303139bc596403d5d3931f774c66c4ba467454db": { - "balance": "1699830000000000000000" - }, - "87584a3f613bd4fac74c1e780b86d6caeb890cb2": { - "balance": "1700000000000000000000" - }, - "77f4e3bdf056883cc87280dbe640a18a0d02a207": { - "balance": "193806000000000000000" - }, - "4de3fe34a6fbf634c051997f47cc7f48791f5824": { - "balance": "1999000000000000000000" - }, - "c45a1ca1036b95004187cdac44a36e33a94ab5c3": { - "balance": "254800000000000000000" - }, - "65d33eb39cda6453b19e61c1fe4db93170ef9d34": { - "balance": "13370000000000000000" - }, - "f65616be9c8b797e7415227c9138faa0891742d7": { - "balance": "790000000000000000000" - }, - "e17812f66c5e65941e186c46922b6e7b2f0eeb46": { - "balance": "1820000000000000000000" - }, - "d47f50df89a1cff96513bef1b2ae3a2971accf2c": { - "balance": "840000000000000000000" - }, - "8ed1528b447ed4297902f639c514d0944a88f8c8": { - "balance": "198800000000000000000" - }, - "a4fb14409a67b45688a8593e5cc2cf596ced6f11": { - "balance": "1790000000000000000000" - }, - "855d9aef2c39c6230d09c99ef6494989abe68785": { - "balance": "161000000000000000000" - }, - "778c43d11afe3b586ff374192d96a7f23d2b9b7f": { - "balance": "2577139000000000000000" - }, - "e3ece1f632711d13bfffa1f8f6840871ee58fb27": { - "balance": "4000000000000000000000" - }, - "beb3358c50cf9f75ffc76d443c2c7f55075a0589": { - "balance": "2674000000000000000000" - }, - "f156dc0b2a981e5b55d3f2f03b8134e331dbadb7": { - "balance": "100000000000000000000" - }, - "eb9cc9fe0869d2dab52cc7aae8fd57adb35f9feb": { - "balance": "1966000000000000000000" - }, - "2467c6a5c696ede9a1e542bf1ad06bcc4b06aca0": { - "balance": "18500000000000000000" - }, - "ec75b4a47513120ba5f86039814f1998e3817ac3": { - "balance": "178756000000000000000" - }, - "9c3d0692ceeef80aa4965ceed262ffc7f069f2dc": { - "balance": "200000000000000000000" - }, - "e05029aceb0778675bef1741ab2cd2931ef7c84b": { - "balance": "5000057000000000000000" - }, - "41d3b731a326e76858baa5f4bd89b57b36932343": { - "balance": "394000000000000000000" - }, - "c346cb1fbce2ab285d8e5401f42dd7234d37e86d": { - "balance": "83500000000000000000" - }, - "45f4fc60f08eaca10598f0336329801e3c92cb46": { - "balance": "200000000000000000000" - }, - "f04a6a379708b9428d722aa2b06b77e88935cf89": { - "balance": "300000000000000000000" - }, - "232832cd5977e00a4c30d0163f2e24f088a6cb09": { - "balance": "3000000000000000000000" - }, - "d2ac0d3a58605e1d0f0eb3de25b2cad129ed6058": { - "balance": "4000000000000000000000" - }, - "a356551bb77d4f45a6d7e09f0a089e79cca249cb": { - "balance": "340000000000000000000" - }, - "b50c9f5789ae44e2dce017c714caf00c830084c2": { - "balance": "394000000000000000000" - }, - "21fd6c5d97f9c600b76821ddd4e776350fce2be0": { - "balance": "1999946000000000000000" - }, - "f0d5c31ccb6cbe30c7c9ea19f268d159851f8c9c": { - "balance": "16700000000000000000000" - }, - "ab7091932e4bc39dbb552380ca934fd7166d1e6e": { - "balance": "3340000000000000000000" - }, - "acd8dd91f714764c45677c63d852e56eb9eece2e": { - "balance": "2000000000000000000000" - }, - "57d032a43d164e71aa2ef3ffd8491b0a4ef1ea5b": { - "balance": "2000000000000000000000" - }, - "5af46a25ac09cb73616b53b14fb42ff0a51cddb2": { - "balance": "4000000000000000000000" - }, - "1ea6bf2f15ae9c1dbc64daa7f8ea4d0d81aad3eb": { - "balance": "4200000000000000000000" - }, - "03337012ae1d7ff3ee7f697c403e7780188bf0ef": { - "balance": "200000000000000000000" - }, - "32eb64be1b5dede408c6bdefbe6e405c16b7ed02": { - "balance": "1970000000000000000000" - }, - "22e2488e2da26a49ae84c01bd54b21f2947891c6": { - "balance": "1730000000000000000000" - }, - "be98a77fd41097b34f59d7589baad021659ff712": { - "balance": "900000000000000000000" - }, - "dda4ed2a58a8dd20a73275347b580d71b95bf99a": { - "balance": "399000000000000000000" - }, - "671110d96aaff11523cc546bf9940eedffb2faf7": { - "balance": "4000000000000000000000" - }, - "5d71799c8df3bccb7ee446df50b8312bc4eb71c5": { - "balance": "200000000000000000000" - }, - "ae179a460db66326743d24e67523a57b246daf7f": { - "balance": "4722920000000000000000" - }, - "198bfcf1b07ae308fa2c02069ac9dafe7135fb47": { - "balance": "20000000000000000000" - }, - "4662a1765ee921842ddc88898d1dc8627597bd7e": { - "balance": "10000000000000000000000" - }, - "783eec8aa5dac77b2e6623ed5198a431abbaee07": { - "balance": "440000000000000000000" - }, - "ed6643c0e8884b2d3211853785a08bf8f33ed29f": { - "balance": "1337000000000000000000" - }, - "5cc7d3066d45d27621f78bb4b339473e442a860f": { - "balance": "9999908000000000000000" - }, - "94ef8be45077c7d4c5652740de946a62624f713f": { - "balance": "100085000000000000000" - }, - "2f853817afd3b8f3b86e9f60ee77b5d97773c0e3": { - "balance": "1451450000000000000000" - }, - "3e0b8ed86ed669e12723af7572fbacfe829b1e16": { - "balance": "1499800000000000000000" - }, - "fa68e0cb3edf51f0a6f211c9b2cb5e073c9bffe6": { - "balance": "291200000000000000000" - }, - "2c234f505ca8dcc77d9b7e01d257c318cc19396d": { - "balance": "100000000000000000000" - }, - "f3f24fc29e20403fc0e8f5ebbb553426f78270a2": { - "balance": "100000000000000000000" - }, - "91546b79ecf69f936b5a561508b0d7e50cc5992f": { - "balance": "267400000000000000000" - }, - "435443b81dfdb9bd8c6787bc2518e2d47e57c15f": { - "balance": "5968500000000000000000" - }, - "3a06e3bb1edcfd0c44c3074de0bb606b049894a2": { - "balance": "10000000000000000000000" - }, - "3a3108c1e680a33b336c21131334409d97e5adec": { - "balance": "20000000000000000000" - }, - "2caf6bf4ec7d5a19c5e0897a5eeb011dcece4210": { - "balance": "139740000000000000000" - }, - "f44f8551ace933720712c5c491cdb6f2f951736c": { - "balance": "4000000000000000000000" - }, - "5bc1f95507b1018642e45cd9c0e22733b9b1a326": { - "balance": "100000000000000000000" - }, - "94ca56de777fd453177f5e0694c478e66aff8a84": { - "balance": "500000000000000000000" - }, - "afdd1b786162b8317e20f0e979f4b2ce486d765d": { - "balance": "20000000000000000000" - }, - "3a805fa0f7387f73055b7858ca8519edd93d634f": { - "balance": "1850000000000000000000" - }, - "8b36224c7356e751f0c066c35e3b44860364bfc2": { - "balance": "998987000000000000000" - }, - "cfecbea07c27002f65fe534bb8842d0925c78402": { - "balance": "4000000000000000000000" - }, - "482982ac1f1c6d1721feecd9b9c96cd949805055": { - "balance": "10000000000000000000000" - }, - "af880fc7567d5595cacce15c3fc14c8742c26c9e": { - "balance": "133700000000000000000" - }, - "acc1c78786ab4d2b3b277135b5ba123e0400486b": { - "balance": "78800000000000000000" - }, - "41f27e744bd29de2b0598f02a0bb9f98e681eaa4": { - "balance": "7760000000000000000000" - }, - "09a025316f967fa8b9a1d60700063f5a68001caa": { - "balance": "38200000000000000000" - }, - "391f20176d12360d724d51470a90703675594a4d": { - "balance": "1600000000000000000000" - }, - "fe4d8403216fd571572bf1bdb01d00578978d688": { - "balance": "9850000000000000000000" - }, - "900f0b8e35b668f81ef252b13855aa5007d012e7": { - "balance": "425000000000000000000" - }, - "c35b95a2a3737cb8f0f596b34524872bd30da234": { - "balance": "7540000000000000000000" - }, - "412a68f6c645559cc977fc4964047a201d1bb0e2": { - "balance": "50000000000000000000000" - }, - "d3dad1b6d08d4581ccae65a8732db6ac69f0c69e": { - "balance": "6000000000000000000000" - }, - "35855ec641ab9e081ed0c2a6dcd81354d0244a87": { - "balance": "1201897000000000000000" - }, - "88015d7203c5e0224aeda286ed12f1a51b789333": { - "balance": "4999711000000000000000" - }, - "251c12722c6879227992a304eb3576cd18434ea5": { - "balance": "2000000000000000000000" - }, - "1f6f0030349752061c96072bc3d6eb3549208d6b": { - "balance": "23891000000000000000" - }, - "86153063a1ae7f02f1a88136d4d69c7c5e3e4327": { - "balance": "1000000000000000000000" - }, - "78355df0a230f83d032c703154414de3eedab557": { - "balance": "2000000000000000000000" - }, - "c5b56cd234267c28e89c6f6b2266b086a12f970c": { - "balance": "4000000000000000000000" - }, - "3e3cd3bec06591d6346f254b621eb41c89008d31": { - "balance": "993800000000000000000" - }, - "378ea1dc8edc19bae82638029ea8752ce98bcfcd": { - "balance": "2000000000000000000000" - }, - "67632046dcb25a54936928a96f423f3320cbed92": { - "balance": "2000000000000000000000" - }, - "ddbee6f094eae63420b003fb4757142aea6cd0fd": { - "balance": "2000000000000000000000" - }, - "b555d00f9190cc3677aef314acd73fdc39399259": { - "balance": "2000000000000000000000" - }, - "e230fe1bff03186d0219f15d4c481b7d59be286a": { - "balance": "36710000000000000000" - }, - "3e4e9265223c9738324cf20bd06006d0073edb8c": { - "balance": "133700000000000000000" - }, - "7450ff7f99eaa9116275deac68e428df5bbcd8b9": { - "balance": "2000000000000000000000" - }, - "021f69043de88c4917ca10f1842897eec0589c7c": { - "balance": "1978760000000000000000" - }, - "351787843505f8e4eff46566cce6a59f4d1c5fe7": { - "balance": "9250000000000000000000" - }, - "ebd37b256563e30c6f9289a8e2702f0852880833": { - "balance": "1999944000000000000000" - }, - "ed41e1a28f5caa843880ef4e8b08bd6c33141edf": { - "balance": "790174000000000000000" - }, - "8d238e036596987643d73173c37b0ad06055b96c": { - "balance": "2089724000000000000000" - }, - "478e524ef2a381d70c82588a93ca7a5fa9d51cbf": { - "balance": "254908000000000000000000" - }, - "4419ac618d5dea7cdc6077206fb07dbdd71c1702": { - "balance": "4000000000000000000000" - }, - "ca25ff34934c1942e22a4e7bd56f14021a1af088": { - "balance": "197000000000000000000" - }, - "5552f4b3ed3e1da79a2f78bb13e8ae5a68a9df3b": { - "balance": "1000000000000000000000" - }, - "4354221e62dc09e6406436163a185ef06d114a81": { - "balance": "2000000000000000000000" - }, - "ca0432cb157b5179f02ebba5c9d1b54fec4d88ca": { - "balance": "1000000000000000000000" - }, - "8a780ab87a9145fe10ed60fa476a740af4cab1d2": { - "balance": "334000000000000000000" - }, - "4ff676e27f681a982d8fd9d20e648b3dce05e945": { - "balance": "2800000000000000000000" - }, - "6c63fc85029a2654d79b2bea4de349e4524577c5": { - "balance": "660000000000000000000" - }, - "1ac089c3bc4d82f06a20051a9d732dc0e734cb61": { - "balance": "700300000000000000000" - }, - "4bf4479799ef82eea20943374f56a1bf54001e5e": { - "balance": "3940000000000000000000" - }, - "08411652c871713609af0062a8a1281bf1bbcfd9": { - "balance": "1400000000000000000000" - }, - "e1bfaa5a45c504428923c4a61192a55b1400b45d": { - "balance": "2674000000000000000000" - }, - "5e1fbd4e58e2312b3c78d7aaaafa10bf9c3189e3": { - "balance": "40000000000000000000000" - }, - "bb27c6a7f91075475ab229619040f804c8ec7a6a": { - "balance": "10000000000000000000000" - }, - "5d8d31faa864e22159cd6f5175ccecc53fa54d72": { - "balance": "26980000000000000000000" - }, - "2dd8eeef87194abc2ce7585da1e35b7cea780cb7": { - "balance": "999999000000000000000" - }, - "0e1801e70b6262861b1134ccbc391f568afc92f7": { - "balance": "4000000000000000000000" - }, - "61042b80fd6095d1b87be2f00f109fabafd157a6": { - "balance": "100000000000000000000" - }, - "fb5518714cefc36d04865de5915ef0ff47dfe743": { - "balance": "2000000000000000000000" - }, - "b5add1e7809f7d03069bfe883b0a932210be8712": { - "balance": "1000000000000000000000" - }, - "c2e2d498f70dcd0859e50b023a710a6d4b2133bd": { - "balance": "1037130000000000000000" - }, - "4ad047fae67ef162fe68fedbc27d3b65caf10c36": { - "balance": "1970000000000000000000" - }, - "69cb3e2153998d86e5ee20c1fcd1a6baeeb2863f": { - "balance": "4000000000000000000000" - }, - "683633010a88686bea5a98ea53e87997cbf73e69": { - "balance": "99960000000000000000" - }, - "6cb11ecb32d3ce829601310636f5a10cf7cf9b5f": { - "balance": "20068370000000000000000" - }, - "a613456996408af1c2e93e177788ab55895e2b32": { - "balance": "6366000000000000000000" - }, - "8308ed0af7f8a3c1751fafc877b5a42af7d35882": { - "balance": "1000000000000000000000" - }, - "e5edf8123f2403ce1a0299becf7aac744d075f23": { - "balance": "200200000000000000000" - }, - "05665155cc49cbf6aabdd5ae92cbfaad82b8c0c1": { - "balance": "400000000000000000000" - }, - "00b277b099a8e866ca0ec65bcb87284fd142a582": { - "balance": "1970000000000000000000" - }, - "4b9e068fc4680976e61504912985fd5ce94bab0d": { - "balance": "668500000000000000000" - }, - "12134e7f6b017bf48e855a399ca58e2e892fa5c8": { - "balance": "1000000000000000000000" - }, - "dffcea5421ec15900c6ecfc777184e140e209e24": { - "balance": "19980000000000000000" - }, - "2132c0516a2e17174ac547c43b7b0020d1eb4c59": { - "balance": "985000000000000000000" - }, - "d39a5da460392b940b3c69bc03757bf3f2e82489": { - "balance": "7019250000000000000000" - }, - "66c8331efe7198e98b2d32b938688e3241d0e24f": { - "balance": "9620000000000000000000" - }, - "bdca2a0ff34588af625fa8e28fc3015ab5a3aa00": { - "balance": "2339800000000000000000" - }, - "7dfc342dffcf45dfee74f84c0995397bd1a63172": { - "balance": "250000000000000000000" - }, - "a202547242806f6e70e74058d6e5292defc8c8d4": { - "balance": "2002000000000000000000" - }, - "3bbc13d04accc0707aebdcaef087d0b87e0b5ee3": { - "balance": "3520000000000000000000" - }, - "be5cba8d37427986e8ca2600e858bb03c359520f": { - "balance": "2955000000000000000000" - }, - "4174fa1bc12a3b7183cbabb77a0b59557ba5f1db": { - "balance": "2000000000000000000000" - }, - "9eb3a7cb5e6726427a3a361cfa8d6164dbd0ba16": { - "balance": "804000000000000000000" - }, - "25e661c939863acc044e6f17b5698cce379ec3cc": { - "balance": "1370000000000000000000" - }, - "24bd5904059091d2f9e12d6a26a010ca22ab14e8": { - "balance": "1880000000000000000000" - }, - "c96626728aaa4c4fb3d31c26df3af310081710d1": { - "balance": "3340000000000000000000" - }, - "0fb5d2c673bfb1ddca141b9894fd6d3f05da6720": { - "balance": "100000000000000000000" - }, - "2de31afd189a13a76ff6fe73ead9f74bb5c4a629": { - "balance": "6000000000000000000000" - }, - "bd09126c891c4a83068059fe0e15796c4661a9f4": { - "balance": "800000000000000000000" - }, - "496f5843f6d24cd98d255e4c23d1e1f023227545": { - "balance": "1754143000000000000000" - }, - "540cf23dd95c4d558a279d778d2b3735b3164191": { - "balance": "10000000000000000000000" - }, - "9b5ec18e8313887df461d2902e81e67a8f113bb1": { - "balance": "100000000000000000000" - }, - "b7a7f77c348f92a9f1100c6bd829a8ac6d7fcf91": { - "balance": "1820000000000000000000" - }, - "2590126870e0bde8a663ab040a72a5573d8d41c2": { - "balance": "5000000000000000000000" - }, - "090fa9367bda57d0d3253a0a8ff76ce0b8e19a73": { - "balance": "1000000000000000000000" - }, - "2a5ba9e34cd58da54c9a2712663a3be274c8e47b": { - "balance": "197000000000000000000" - }, - "3e8641d43c42003f0a33c929f711079deb2b9e46": { - "balance": "500000000000000000000" - }, - "f4d97664cc4eec9edbe7fa09f4750a663b507d79": { - "balance": "4000000000000000000000" - }, - "b1540e94cff3465cc3d187e7c8e3bdaf984659e2": { - "balance": "2989950000000000000000" - }, - "f96883582459908c827627e86f28e646f9c7fc7a": { - "balance": "8350000000000000000000" - }, - "d4feed99e8917c5c5458635f3603ecb7e817a7d0": { - "balance": "300031000000000000000" - }, - "14b1603ec62b20022033eec4d6d6655ac24a015a": { - "balance": "50000000000000000000" - }, - "af8e1dcb314c950d3687434d309858e1a8739cd4": { - "balance": "267400000000000000000" - }, - "4b9206ba6b549a1a7f969e1d5dba867539d1fa67": { - "balance": "7880000000000000000000" - }, - "471010da492f4018833b088d9872901e06129174": { - "balance": "500000000000000000000" - }, - "d243184c801e5d79d2063f3578dbae81e7b3a9cb": { - "balance": "1989700000000000000000" - }, - "3eada8c92f56067e1bb73ce378da56dc2cdfd365": { - "balance": "2210000000000000000000" - }, - "33ea6b7855e05b07ab80dab1e14de9b649e99b6c": { - "balance": "532000000000000000000" - }, - "700711e311bb947355f755b579250ca7fd765a3e": { - "balance": "1790000000000000000000" - }, - "87fb26c31e48644d693134205cae43b21f18614b": { - "balance": "1370000000000000000000" - }, - "001d14804b399c6ef80e64576f657660804fec0b": { - "balance": "4200000000000000000000" - }, - "f9642086b1fbae61a6804dbe5fb15ec2d2b537f4": { - "balance": "2000000000000000000000" - }, - "6919dd5e5dfb1afa404703b9faea8cee35d00d70": { - "balance": "5910000000000000000000" - }, - "9ac4da51d27822d1e208c96ea64a1e5b55299723": { - "balance": "100040000000000000000" - }, - "1bd8ebaa7674bb18e19198db244f570313075f43": { - "balance": "150000000000000000000" - }, - "e64ef012658d54f8e8609c4e9023c09fe865c83b": { - "balance": "28000000000000000000" - }, - "43b079baf0727999e66bf743d5bcbf776c3b0922": { - "balance": "2000000000000000000000" - }, - "06ac26ad92cb859bd5905ddce4266aa0ec50a9c5": { - "balance": "775000000000000000000" - }, - "99c1d9f40c6ab7f8a92fce2fdce47a54a586c53f": { - "balance": "985000000000000000000" - }, - "4ae93082e45187c26160e66792f57fad3551c73a": { - "balance": "21658000000000000000000" - }, - "7da7613445a21299aa74f0ad71431ec43fbb1be9": { - "balance": "68000000000000000000" - }, - "4a9a26fd0a8ba10f977da4f77c31908dab4a8016": { - "balance": "1790000000000000000000" - }, - "972c2f96aa00cf8a2f205abcf8937c0c75f5d8d9": { - "balance": "200000000000000000000" - }, - "b5046cb3dc1dedbd364514a2848e44c1de4ed147": { - "balance": "16445100000000000000000" - }, - "48c2ee91a50756d8ce9abeeb7589d22c6fee5dfb": { - "balance": "3220000000000000000000" - }, - "46c1aa2244b9c8a957ca8fac431b0595a3b86824": { - "balance": "4000000000000000000000" - }, - "21fd0bade5f4ef7474d058b7f3d854cb1300524e": { - "balance": "20000000000000000000" - }, - "1864a3c7b48155448c54c88c708f166709736d31": { - "balance": "133700000000000000000" - }, - "5dd53ae897526b167d39f1744ef7c3da5b37a293": { - "balance": "8000000000000000000000" - }, - "ece111670b563ccdbebca52384290ecd68fe5c92": { - "balance": "20000000000000000000" - }, - "74d671d99cbea1ab57906375b63ff42b50451d17": { - "balance": "1000000000000000000000" - }, - "5717cc9301511d4a81b9f583148beed3d3cc8309": { - "balance": "2600000000000000000000" - }, - "8f92844f282a92999ee5b4a8d773d06b694dbd9f": { - "balance": "1940000000000000000000" - }, - "b5a606f4ddcbb9471ec67f658caf2b00ee73025e": { - "balance": "4325000000000000000000" - }, - "bdb60b823a1173d45a0792245fb496f1fd3301cf": { - "balance": "2000000000000000000000" - }, - "1d2615f8b6ca5012b663bdd094b0c5137c778ddf": { - "balance": "10000000000000000000000" - }, - "82ff716fdf033ec7e942c909d9831867b8b6e2ef": { - "balance": "1790000000000000000000" - }, - "44c14765127cde11fab46c5d2cf4d4b2890023fd": { - "balance": "2000000000000000000000" - }, - "c72cb301258e91bc08998a805dd192f25c2f9a35": { - "balance": "591000000000000000000" - }, - "ad732c976593eec4783b4e2ecd793979780bfedb": { - "balance": "2000000000000000000000" - }, - "d8f62036f03b7635b858f1103f8a1d9019a892b6": { - "balance": "50000000000000000000" - }, - "0a06fad7dcd7a492cbc053eeabde6934b39d8637": { - "balance": "20000000000000000000" - }, - "67f2bb78b8d3e11f7c458a10b5c8e0a1d374467d": { - "balance": "1790000000000000000000" - }, - "4b5cdb1e428c91dd7cb54a6aed4571da054bfe52": { - "balance": "88000000000000000000" - }, - "b3557d39b5411b84445f5f54f38f62d2714d0087": { - "balance": "600000000000000000000" - }, - "0b0e055b28cbd03dc5ff44aa64f3dce04f5e63fb": { - "balance": "2000000000000000000000" - }, - "9b2be7f56754f505e3441a10f7f0e20fd3ddf849": { - "balance": "340000000000000000000" - }, - "0b93fca4a4f09cac20db60e065edcccc11e0a5b6": { - "balance": "200000000000000000000" - }, - "3bc85d6c735b9cda4bba5f48b24b13e70630307b": { - "balance": "1970000000000000000000" - }, - "52102354a6aca95d8a2e86d5debda6de69346076": { - "balance": "2000000000000000000000" - }, - "cda4530f4b9bc50905b79d17c28fc46f95349bdf": { - "balance": "942000000000000000000" - }, - "ff545bbb66fbd00eb5e6373ff4e326f5feb5fe12": { - "balance": "20000000000000000000" - }, - "4030a925706b2c101c8c5cb9bd05fbb4f6759b18": { - "balance": "4000000000000000000000" - }, - "f11e01c7a9d12499005f4dae7716095a34176277": { - "balance": "400000000000000000000" - }, - "a4826b6c3882fad0ed5c8fbb25cc40cc4f33759f": { - "balance": "2068000000000000000000" - }, - "28510e6eff1fc829b6576f4328bc3938ec7a6580": { - "balance": "10000000000000000000000" - }, - "9ce5363b13e8238aa4dd15acd0b2e8afe0873247": { - "balance": "200000000000000000000" - }, - "d97bc84abd47c05bbf457b2ef659d61ca5e5e48f": { - "balance": "122000000000000000000" - }, - "4a719061f5285495b37b9d7ef8a51b07d6e6acac": { - "balance": "199800000000000000000" - }, - "8b714522fa2839620470edcf0c4401b713663df1": { - "balance": "200000000000000000000" - }, - "b6decf82969819ba02de29b9b593f21b64eeda0f": { - "balance": "740000000000000000000" - }, - "c87d3ae3d88704d9ab0009dcc1a0067131f8ba3c": { - "balance": "1969606000000000000000" - }, - "dccb370ed68aa922283043ef7cad1b9d403fc34a": { - "balance": "4000000000000000000000" - }, - "2d532df4c63911d1ce91f6d1fcbff7960f78a885": { - "balance": "1669833000000000000000" - }, - "1fcfd1d57f872290560cb62d600e1defbefccc1c": { - "balance": "1490000000000000000000" - }, - "d9e27eb07dfc71a706060c7f079238ca93e88539": { - "balance": "1000000000000000000000" - }, - "da7732f02f2e272eaf28df972ecc0ddeed9cf498": { - "balance": "205274000000000000000" - }, - "bf09d77048e270b662330e9486b38b43cd781495": { - "balance": "436000000000000000000000" - }, - "619f171445d42b02e2e07004ad8afe694fa53d6a": { - "balance": "20000000000000000000" - }, - "2bdd03bebbee273b6ca1059b34999a5bbd61bb79": { - "balance": "20000000000000000000" - }, - "8da1d359ba6cb4bcc57d7a437720d55db2f01c72": { - "balance": "80000000000000000000" - }, - "be935793f45b70d8045d2654d8dd3ad24b5b6137": { - "balance": "880000000000000000000" - }, - "ee71793e3acf12a7274f563961f537529d89c7de": { - "balance": "2000000000000000000000" - }, - "86f05d19063e9369c6004eb3f123943a7cff4eab": { - "balance": "1999944000000000000000" - }, - "87b10f9c280098179a2b76e9ce90be61fc844d0d": { - "balance": "1337000000000000000000" - }, - "243c84d12420570cc4ef3baba1c959c283249520": { - "balance": "2345000000000000000000" - }, - "6bc85acd5928722ef5095331ee88f484b8cf8357": { - "balance": "180000000000000000000" - }, - "2561a138dcf83bd813e0e7f108642be3de3d6f05": { - "balance": "999940000000000000000" - }, - "7d0350e40b338dda736661872be33f1f9752d755": { - "balance": "49933000000000000000" - }, - "e5dc9349cb52e161196122cf87a38936e2c57f34": { - "balance": "2000000000000000000000" - }, - "543a8c0efb8bcd15c543e2a6a4f807597631adef": { - "balance": "5893800000000000000000" - }, - "0413d0cf78c001898a378b918cd6e498ea773c4d": { - "balance": "280000000000000000000" - }, - "3708e59de6b4055088782902e0579c7201a8bf50": { - "balance": "200000000000000000000000" - }, - "699fc6d68a4775573c1dcdaec830fefd50397c4e": { - "balance": "60000000000000000000" - }, - "379a7f755a81a17edb7daaa28afc665dfa6be63a": { - "balance": "25000000000000000000" - }, - "260a230e4465077e0b14ee4442a482d5b0c914bf": { - "balance": "1677935000000000000000" - }, - "3daa01ceb70eaf9591fa521ba4a27ea9fb8ede4a": { - "balance": "1667400000000000000000" - }, - "7f3a1e45f67e92c880e573b43379d71ee089db54": { - "balance": "100000000000000000000000" - }, - "38643babea6011316cc797d9b093c897a17bdae7": { - "balance": "334400000000000000000" - }, - "84675e9177726d45eaa46b3992a340ba7f710c95": { - "balance": "1000000000000000000000" - }, - "0f83461ba224bb1e8fdd9dae535172b735acb4e0": { - "balance": "200000000000000000000" - }, - "31aa3b1ebe8c4dbcb6a708b1d74831e60e497660": { - "balance": "400000000000000000000" - }, - "a32cf7dde20c3dd5679ff5e325845c70c5962662": { - "balance": "20000000000000000000" - }, - "c007f0bdb6e7009202b7af3ea90902697c721413": { - "balance": "2999966000000000000000" - }, - "05c64004a9a826e94e5e4ee267fa2a7632dd4e6f": { - "balance": "16191931000000000000000" - }, - "f622e584a6623eaaf99f2be49e5380c5cbcf5cd8": { - "balance": "200000000000000000000" - }, - "9dc10fa38f9fb06810e11f60173ec3d2fd6a751e": { - "balance": "1970000000000000000000" - }, - "423c3107f4bace414e499c64390a51f74615ca5e": { - "balance": "2000000000000000000000" - }, - "92438e5203b6346ff886d7c36288aacccc78ceca": { - "balance": "1000000000000000000000" - }, - "bef07d97c3481f9d6aee1c98f9d91a180a32442b": { - "balance": "100000000000000000000000" - }, - "55aa5d313ebb084da0e7801091e29e92c5dec3aa": { - "balance": "2000000000000000000000" - }, - "89c433d601fad714da6369308fd26c1dc9942bbf": { - "balance": "2000000000000000000000" - }, - "25106ab6755df86d6b63a187703b0cfea0e594a0": { - "balance": "27400000000000000000" - }, - "494256e99b0f9cd6e5ebca3899863252900165c8": { - "balance": "14000000000000000000000" - }, - "5f4ace4c1cc13391e01f00b198e1f20b5f91cbf5": { - "balance": "5000196000000000000000" - }, - "135cecd955e5798370769230159303d9b1839f66": { - "balance": "5000000000000000000000" - }, - "ced81ec3533ff1bfebf3e3843ee740ad11758d3e": { - "balance": "1970000000000000000000" - }, - "688eb3853bbcc50ecfee0fa87f0ab693cabdef02": { - "balance": "31600000000000000000000" - }, - "2159240813a73095a7ebf7c3b3743e8028ae5f09": { - "balance": "2000000000000000000000" - }, - "99d1579cd42682b7644e1d4f7128441eeffe339d": { - "balance": "20000000000000000000000" - }, - "8a243a0a9fea49b839547745ff2d11af3f4b0522": { - "balance": "985000000000000000000" - }, - "c1a41a5a27199226e4c7eb198b031b59196f9842": { - "balance": "191000000000000000000" - }, - "7514adbdc63f483f304d8e94b67ff3309f180b82": { - "balance": "622911000000000000000" - }, - "74aeec915de01cc69b2cb5a6356feea14658c6c5": { - "balance": "232500000000000000000" - }, - "76f9ad3d9bbd04ae055c1477c0c35e7592cb2a20": { - "balance": "40200000000000000000000" - }, - "a8a7b68adab4e3eadff19ffa58e34a3fcec0d96a": { - "balance": "6000000000000000000000" - }, - "60de22a1507432a47b01cc68c52a0bf8a2e0d098": { - "balance": "19100000000000000000" - }, - "ceb33d78e7547a9da2e87d51aec5f3441c87923a": { - "balance": "20000000000000000000" - }, - "432809a2390f07c665921ff37d547d12f1c9966a": { - "balance": "30000000000000000000000" - }, - "d5e656a1b916f9bf45afb07dd8afaf73b4c56f41": { - "balance": "97000000000000000000" - }, - "e3410bb7557cf91d79fa69d0dfea0aa075402651": { - "balance": "2000000000000000000000" - }, - "dee942d5caf5fac11421d86b010b458e5c392990": { - "balance": "4000000000000000000000" - }, - "a98f109835f5eacd0543647c34a6b269e3802fac": { - "balance": "400000000000000000000" - }, - "932b9c04d40d2ac83083d94298169dae81ab2ed0": { - "balance": "2000000000000000000000" - }, - "ba10f2764290f875434372f79dbf713801caac01": { - "balance": "955000000000000000000" - }, - "a2c7eaffdc2c9d937345206c909a52dfb14c478f": { - "balance": "143000000000000000000" - }, - "6c67e0d7b62e2a08506945a5dfe38263339f1f22": { - "balance": "1970000000000000000000" - }, - "60c3714fdddb634659e4a2b1ea42c4728cc7b8ba": { - "balance": "13370000000000000000" - }, - "73b4d499de3f38bf35aaf769a6e318bc6d123692": { - "balance": "2000000000000000000000" - }, - "3b22dea3c25f1b59c7bd27bb91d3a3eaecef3984": { - "balance": "100000000000000000000" - }, - "1e3badb1b6e1380e27039c576ae6222e963a5b53": { - "balance": "20000000000000000000000" - }, - "abd4d6c1666358c0406fdf3af248f78ece830104": { - "balance": "2112000000000000000000" - }, - "0c925ad5eb352c8ef76d0c222d115b0791b962a1": { - "balance": "3180000000000000000000" - }, - "be9186c34a52514abb9107860f674f97b821bd5b": { - "balance": "509600000000000000000" - }, - "b7f67314cb832e32e63b15a40ce0d7ffbdb26985": { - "balance": "1060866000000000000000" - }, - "3f30d3bc9f602232bc724288ca46cd0b0788f715": { - "balance": "4000000000000000000000" - }, - "970abd53a54fca4a6429207c182d4d57bb39d4a0": { - "balance": "2000000000000000000000" - }, - "36d85dc3683156e63bf880a9fab7788cf8143a27": { - "balance": "20000000000000000000000" - }, - "2836123046b284e5ef102bfd22b1765e508116ad": { - "balance": "411880000000000000000" - }, - "de06d5ea777a4eb1475e605dbcbf43444e8037ea": { - "balance": "50000000000000000000000" - }, - "9af11399511c213181bfda3a8b264c05fc81b3ce": { - "balance": "14000000000000000000000" - }, - "e2191215983f33fd33e22cd4a2490054da53fddc": { - "balance": "15800000000000000000" - }, - "2eebf59432b52892f9380bd140aa99dcf8ad0c0f": { - "balance": "152000000000000000000" - }, - "dc087f9390fb9e976ac23ab689544a0942ec2021": { - "balance": "1820000000000000000000" - }, - "fd4b989558ae11be0c3b36e2d6f2a54a9343ca2e": { - "balance": "2000000000000000000000" - }, - "770c2fb2c4a81753ac0182ea460ec09c90a516f8": { - "balance": "20000000000000000000" - }, - "b28dbfc6499894f73a71faa00abe0f4bc9d19f2a": { - "balance": "100000000000000000000" - }, - "b0cef8e8fb8984a6019f01c679f272bbe68f5c77": { - "balance": "152000000000000000000" - }, - "f400f93d5f5c7e3fc303129ac8fb0c2f786407fa": { - "balance": "2000000000000000000000" - }, - "f2133431d1d9a37ba2f0762bc40c5acc8aa6978e": { - "balance": "2000000000000000000000" - }, - "9003d270891ba2df643da8341583193545e3e000": { - "balance": "4000000000000000000000" - }, - "8938d1b4daee55a54d738cf17e4477f6794e46f7": { - "balance": "18200000000000000000" - }, - "98e6f547db88e75f1f9c8ac2c5cf1627ba580b3e": { - "balance": "1000000000000000000000" - }, - "009fdbf44e1f4a6362b769c39a475f95a96c2bc7": { - "balance": "564000000000000000000" - }, - "d0f9597811b0b992bb7d3757aa25b4c2561d32e2": { - "balance": "500000000000000000000" - }, - "dcd10c55bb854f754434f1219c2c9a98ace79f03": { - "balance": "4000086000000000000000" - }, - "67048f3a12a4dd1f626c64264cb1d7971de2ca38": { - "balance": "180000000000000000000" - }, - "d33cf82bf14c592640a08608914c237079d5be34": { - "balance": "2000000000000000000000" - }, - "f5b068989df29c253577d0405ade6e0e7528f89e": { - "balance": "1610000000000000000000" - }, - "a9a8eca11a23d64689a2aa3e417dbb3d336bb59a": { - "balance": "262025000000000000000" - }, - "99413704b1a32e70f3bc0d69dd881c38566b54cb": { - "balance": "27382708000000000000000" - }, - "2a085e25b64862f5e68d768e2b0f7a8529858eee": { - "balance": "1983618000000000000000" - }, - "833d3fae542ad5f8b50ce19bde2bec579180c88c": { - "balance": "346000000000000000000" - }, - "c3483d6e88ac1f4ae73cc4408d6c03abe0e49dca": { - "balance": "17000000000000000000000" - }, - "fde395bc0b6d5cbb4c1d8fea3e0b4bff635e9db7": { - "balance": "2000000000000000000000" - }, - "eddacd94ec89a2ef968fcf977a08f1fae2757869": { - "balance": "8000000000000000000000" - }, - "dc29119745d2337320da51e19100c948d980b915": { - "balance": "160000000000000000000" - }, - "640bf87415e0cf407301e5599a68366da09bbac8": { - "balance": "493207000000000000000" - }, - "afcc7dbb8356d842d43ae7e23c8422b022a30803": { - "balance": "30400000000000000000000" - }, - "9120e71173e1ba19ba8f9f4fdbdcaa34e1d6bb78": { - "balance": "2000000000000000000000" - }, - "9092918707c621fdbd1d90fb80eb787fd26f7350": { - "balance": "2460000000000000000000" - }, - "263e57dacbe0149f82fe65a2664898866ff5b463": { - "balance": "38000000000000000000000" - }, - "315db7439fa1d5b423afa7dd7198c1cf74c918bc": { - "balance": "600000000000000000000" - }, - "09b4668696f86a080f8bebb91db8e6f87015915a": { - "balance": "656010000000000000000" - }, - "5c31996dcac015f9be985b611f468730ef244d90": { - "balance": "200000000000000000000" - }, - "b1179589e19db9d41557bbec1cb24ccc2dec1c7f": { - "balance": "100000000000000000000000" - }, - "3b1937d5e793b89b63fb8eb5f1b1c9ca6ba0fa8e": { - "balance": "2000000000000000000000" - }, - "c9127b7f6629ee13fc3f60bc2f4467a20745a762": { - "balance": "16465639000000000000000" - }, - "7306de0e288b56cfdf987ef0d3cc29660793f6dd": { - "balance": "508060000000000000000" - }, - "2aa192777ca5b978b6b2c2ff800ac1860f753f47": { - "balance": "335000000000000000000" - }, - "55da9dcdca61cbfe1f133c7bcefc867b9c8122f9": { - "balance": "880000000000000000000" - }, - "cdd9efac4d6d60bd71d95585dce5d59705c13564": { - "balance": "100000000000000000000" - }, - "ad8e48a377695de014363a523a28b1a40c78f208": { - "balance": "1000000000000000000000" - }, - "252b6555afdc80f2d96d972d17db84ea5ad521ac": { - "balance": "7880000000000000000000" - }, - "60ab71cd26ea6d6e59a7a0f627ee079c885ebbf6": { - "balance": "26740000000000000000" - }, - "f40b134fea22c6b29c8457f49f000f9cda789adb": { - "balance": "600000000000000000000" - }, - "85a2f6ea94d05e8c1d9ae2f4910338a358e98ded": { - "balance": "2000000000000000000000" - }, - "ae13a08511110f32e53be4127845c843a1a57c7b": { - "balance": "500000000000000000000" - }, - "40db1ba585ce34531edec5494849391381e6ccd3": { - "balance": "1790000000000000000000" - }, - "0c5589a7a89b9ad15b02751930415948a875fbef": { - "balance": "126000000000000000000" - }, - "89054430dcdc28ac15fa635ef87c105e602bf70c": { - "balance": "108000000000000000000" - }, - "6c882c27732cef5c7c13a686f0a2ea77555ac289": { - "balance": "100000000000000000000000" - }, - "de374299c1d07d79537385190f442ef9ca24061f": { - "balance": "133700000000000000000" - }, - "b146a0b925553cf06fcaf54a1b4dfea621290757": { - "balance": "2000200000000000000000" - }, - "09ae49e37f121df5dc158cfde806f173a06b0c7f": { - "balance": "3988000000000000000000" - }, - "b758896f1baa864f17ebed16d953886fee68aae6": { - "balance": "1000000000000000000000" - }, - "30730466b8eb6dc90d5496aa76a3472d7dbe0bbe": { - "balance": "1999800000000000000000" - }, - "fc02734033e57f70517e0afc7ee62461f06fad8e": { - "balance": "394000000000000000000" - }, - "a9b2d2e0494eab18e07d37bbb856d80e80f84cd3": { - "balance": "10000000000000000000000" - }, - "95278b08dee7c0f2c8c0f722f9fcbbb9a5241fda": { - "balance": "2408672000000000000000" - }, - "dab6bcdb83cf24a0ae1cb21b3b5b83c2f3824927": { - "balance": "50000000000000000000000" - }, - "94439ca9cc169a79d4a09cae5e67764a6f871a21": { - "balance": "240000000000000000000" - }, - "e06c29a81517e0d487b67fb0b6aabc4f57368388": { - "balance": "401100000000000000000" - }, - "458e3cc99e947844a18e6a42918fef7e7f5f5eb3": { - "balance": "36400000000000000000000" - }, - "0a9804137803ba6868d93a55f9985fcd540451e4": { - "balance": "13370000000000000000" - }, - "40630024bd2c58d248edd8465617b2bf1647da0e": { - "balance": "1000000000000000000000" - }, - "15224ad1c0face46f9f556e4774a3025ad06bd52": { - "balance": "13370000000000000000" - }, - "2e2810dee44ae4dff3d86342ab126657d653c336": { - "balance": "200000000000000000000" - }, - "48a30de1c919d3fd3180e97d5f2b2a9dbd964d2d": { - "balance": "44000000000000000000" - }, - "46a30b8a808931217445c3f5a93e882c0345b426": { - "balance": "250019000000000000000" - }, - "455396a4bbd9bae8af9fb7c4d64d471db9c24505": { - "balance": "161000000000000000000" - }, - "edfda2d5db98f9380714664d54b4ee971a1cae03": { - "balance": "40044000000000000000" - }, - "f5eadcd2d1b8657a121f33c458a8b13e76b65526": { - "balance": "249828000000000000000" - }, - "90e7070f4d033fe6910c9efe5a278e1fc6234def": { - "balance": "100392000000000000000" - }, - "d55508adbbbe9be81b80f97a6ea89add68da674f": { - "balance": "2000000000000000000000" - }, - "66925de3e43f4b41bf9dadde27d5488ef569ea0d": { - "balance": "39400000000000000000" - }, - "b7c077946674ba9341fb4c747a5d50f5d2da6415": { - "balance": "1000000000000000000000" - }, - "c52d1a0c73c2a1be84915185f8b34faa0adf1de3": { - "balance": "1400001000000000000000" - }, - "79b8aad879dd30567e8778d2d231c8f37ab8734e": { - "balance": "2000000000000000000000" - }, - "3aae4872fd9093cbcad1406f1e8078bab50359e2": { - "balance": "39400000000000000000" - }, - "b2e9d76bf50fc36bf7d3944b63e9ca889b699968": { - "balance": "2660000000000000000000" - }, - "405f596b94b947344c033ce2dcbff12e25b79784": { - "balance": "2000000000000000000000" - }, - "232cb1cd49993c144a3f88b3611e233569a86bd6": { - "balance": "15576000000000000000000" - }, - "9e232c08c14dc1a6ed0b8a3b2868977ba5c17d10": { - "balance": "20000000000000000000" - }, - "095270cc42141dd998ad2862dbd1fe9b44e7e650": { - "balance": "1200000000000000000000" - }, - "15d99468507aa0413fb60dca2adc7f569cb36b54": { - "balance": "2000000000000000000000" - }, - "04852732b4c652f6c2e58eb36587e60a62da14db": { - "balance": "20000000000000000000000" - }, - "ecf24cdd7c22928c441e694de4aa31b0fab59778": { - "balance": "600000000000000000000" - }, - "512b91bbfaa9e581ef683fc90d9db22a8f49f48b": { - "balance": "310000000000000000000000" - }, - "a88577a073fbaf33c4cd202e00ea70ef711b4006": { - "balance": "2000000000000000000000" - }, - "00acc6f082a442828764d11f58d6894ae408f073": { - "balance": "60000000000000000000000" - }, - "0355bcacbd21441e95adeedc30c17218c8a408ce": { - "balance": "400000000000000000000" - }, - "4e73cf2379f124860f73d6d91bf59acc5cfc845b": { - "balance": "40110000000000000000" - }, - "2a742b8910941e0932830a1d9692cfd28494cf40": { - "balance": "499986000000000000000" - }, - "41a8c2830081b102df6e0131657c07ab635b54ce": { - "balance": "1999944000000000000000" - }, - "b63064bd3355e6e07e2d377024125a33776c4afa": { - "balance": "38800000000000000000000" - }, - "1a25e1c5bc7e5f50ec16f8885f210ea1b938800e": { - "balance": "4000000000000000000000" - }, - "09b59b8698a7fbd3d2f8c73a008988de3e406b2b": { - "balance": "40000000000000000000000" - }, - "c555b93156f09101233c6f7cf6eb3c4f196d3346": { - "balance": "3000000000000000000000" - }, - "12f32c0a1f2daab676fe69abd9e018352d4ccd45": { - "balance": "50000000000000000000" - }, - "5956b28ec7890b76fc061a1feb52d82ae81fb635": { - "balance": "2000000000000000000000" - }, - "c739259e7f85f2659bef5f609ed86b3d596c201e": { - "balance": "200000000000000000000" - }, - "fae92c1370e9e1859a5df83b56d0f586aa3b404c": { - "balance": "106480000000000000000" - }, - "d5a7bec332adde18b3104b5792546aa59b879b52": { - "balance": "2000000000000000000000" - }, - "4f88dfd01091a45a9e2676021e64286cd36b8d34": { - "balance": "1000000000000000000000" - }, - "102c477d69aadba9a0b0f62b7459e17fbb1c1561": { - "balance": "2000000000000000000000" - }, - "34272d5e7574315dcae9abbd317bac90289d4765": { - "balance": "1820000000000000000000" - }, - "fe615d975c0887e0c9113ec7298420a793af8b96": { - "balance": "8000000000000000000000" - }, - "487adf7d70a6740f8d51cbdd68bb3f91c4a5ce68": { - "balance": "66850000000000000000" - }, - "7e5d9993104e4cb545e179a2a3f971f744f98482": { - "balance": "2000000000000000000000" - }, - "5529830a61c1f13c197e550beddfd6bd195c9d02": { - "balance": "10000000000000000000000" - }, - "2f282abbb6d4a3c3cd3b5ca812f7643e80305f06": { - "balance": "1850000000000000000000" - }, - "7352586d021ad0cf77e0e928404a59f374ff4582": { - "balance": "3400000000000000000000" - }, - "03f7b92008813ae0a676eb212814afab35221069": { - "balance": "2000000000000000000000" - }, - "056686078fb6bcf9ba0a8a8dc63a906f5feac0ea": { - "balance": "499800000000000000000" - }, - "8063379a7bf2cb923a84c5093e68dac7f75481c5": { - "balance": "322102000000000000000" - }, - "200264a09f8c68e3e6629795280f56254f8640d0": { - "balance": "20000000000000000000" - }, - "5a891155f50e42074374c739baadf7df2651153a": { - "balance": "4775000000000000000000" - }, - "80022a1207e910911fc92849b069ab0cdad043d3": { - "balance": "13370000000000000000" - }, - "e781ec732d401202bb9bd13860910dd6c29ac0b6": { - "balance": "1240000000000000000000" - }, - "4c2f1afef7c5868c44832fc77cb03b55f89e6d6e": { - "balance": "20000000000000000000000" - }, - "34ff582952ff24458f7b13d51f0b4f987022c1fe": { - "balance": "2804400000000000000000" - }, - "73914b22fc2f131584247d82be4fecbf978ad4ba": { - "balance": "2000000000000000000000" - }, - "562be95aba17c5371fe2ba828799b1f55d2177d6": { - "balance": "38200000000000000000000" - }, - "648f5bd2a2ae8902db37847d1cb0db9390b06248": { - "balance": "7769965000000000000000" - }, - "6a9758743b603eea3aa0524b42889723c4153948": { - "balance": "10100000000000000000000" - }, - "5985c59a449dfc5da787d8244e746c6d70caa55f": { - "balance": "100000000000000000000" - }, - "56ee197f4bbf9f1b0662e41c2bbd9aa1f799e846": { - "balance": "1000000000000000000000" - }, - "d47c242edffea091bc54d57df5d1fdb93101476c": { - "balance": "2914000000000000000000" - }, - "d482e7f68e41f238fe517829de15477fe0f6dd1d": { - "balance": "500000000000000000000" - }, - "05bf4fcfe772e45b826443852e6c351350ce72a2": { - "balance": "8000000000000000000000" - }, - "f10462e58fcc07f39584a187639451167e859201": { - "balance": "169830000000000000000" - }, - "1aa27699cada8dc3a76f7933aa66c71919040e88": { - "balance": "400000000000000000000" - }, - "24046b91da9b61b629cb8b8ec0c351a07e0703e4": { - "balance": "2000000000000000000000" - }, - "41033c1b6d05e1ca89b0948fc64453fbe87ab25e": { - "balance": "1337000000000000000000" - }, - "369822f5578b40dd1f4471706b22cd971352da6b": { - "balance": "346000000000000000000" - }, - "044e853144e3364495e7a69fa1d46abea3ac0964": { - "balance": "49225000000000000000" - }, - "abf728cf9312f22128024e7046c251f5dc5901ed": { - "balance": "29550000000000000000000" - }, - "d781f7fc09184611568570b4986e2c72872b7ed0": { - "balance": "20002000000000000000" - }, - "6bb4a661a33a71d424d49bb5df28622ed4dffcf4": { - "balance": "630400000000000000000" - }, - "fef3b3dead1a6926d49aa32b12c22af54d9ff985": { - "balance": "1000000000000000000000" - }, - "fa410971ad229c3036f41acf852f2ac999281950": { - "balance": "3997400000000000000000" - }, - "de176b5284bcee3a838ba24f67fc7cbf67d78ef6": { - "balance": "37600000000000000000" - }, - "23120046f6832102a752a76656691c863e17e59c": { - "balance": "329800000000000000000" - }, - "a2f472fe4f22b77db489219ea4023d11582a9329": { - "balance": "40000000000000000000000" - }, - "f0d64cf9df09741133d170485fd24b005011d520": { - "balance": "498680000000000000000" - }, - "8b505e2871f7deb7a63895208e8227dcaa1bff05": { - "balance": "61216600000000000000000" - }, - "481e3a91bfdc2f1c8428a0119d03a41601417e1c": { - "balance": "1000000000000000000000" - }, - "bc69a0d2a31c3dbf7a9122116901b2bdfe9802a0": { - "balance": "3000000000000000000000" - }, - "20a81680e465f88790f0074f60b4f35f5d1e6aa5": { - "balance": "1279851000000000000000" - }, - "194a6bb302b8aba7a5b579df93e0df1574967625": { - "balance": "500000000000000000000" - }, - "264cc8086a8710f91b21720905912cd7964ae868": { - "balance": "26740000000000000000" - }, - "24aca08d5be85ebb9f3132dfc1b620824edfedf9": { - "balance": "18200000000000000000" - }, - "1851a063ccdb30549077f1d139e72de7971197d5": { - "balance": "2000000000000000000000" - }, - "f64a4ac8d540a9289c68d960d5fb7cc45a77831c": { - "balance": "2000000000000000000000" - }, - "c3db5657bb72f10d58f231fddf11980aff678693": { - "balance": "5910000000000000000000" - }, - "b46ace865e2c50ea4698d216ab455dff5a11cd72": { - "balance": "1000000000000000000000" - }, - "9faea13c733412dc4b490402bfef27a0397a9bc3": { - "balance": "310000000000000000000" - }, - "b40594c4f3664ef849cca6227b8a25aa690925ee": { - "balance": "4000000000000000000000" - }, - "672fa0a019088db3166f6119438d07a99f8ba224": { - "balance": "13370000000000000000000" - }, - "c1ffad07db96138c4b2a530ec1c7de29b8a0592c": { - "balance": "17600000000000000000" - }, - "87af25d3f6f8eea15313d5fe4557e810c524c083": { - "balance": "19700000000000000000000" - }, - "d6a22e598dabd38ea6e958bd79d48ddd9604f4df": { - "balance": "1000000000000000000000" - }, - "a2a435de44a01bd0ecb29e44e47644e46a0cdffb": { - "balance": "500171000000000000000" - }, - "549b47649cfad993e4064d2636a4baa0623305cc": { - "balance": "601650000000000000000" - }, - "1321b605026f4ffb296a3e0edcb390c9c85608b7": { - "balance": "2000000000000000000000" - }, - "b4bf24cb83686bc469869fefb044b909716993e2": { - "balance": "2000000000000000000000" - }, - "12d91a92d74fc861a729646db192a125b79f5374": { - "balance": "18200000000000000000" - }, - "7f0662b410298c99f311d3a1454a1eedba2fea76": { - "balance": "200000000000000000000" - }, - "83908aa7478a6d1c9b9b0281148f8f9f242b9fdc": { - "balance": "2000000000000000000000" - }, - "c1438c99dd51ef1ca8386af0a317e9b041457888": { - "balance": "223500000000000000000" - }, - "545bb070e781172eb1608af7fc2895d6cb87197e": { - "balance": "2244000000000000000000" - }, - "161d26ef6759ba5b9f20fdcd66f16132c352415e": { - "balance": "2000000000000000000000" - }, - "d7f370d4bed9d57c6f49c999de729ee569d3f4e4": { - "balance": "200000000000000000000" - }, - "90e35aabb2deef408bb9b5acef714457dfde6272": { - "balance": "100076000000000000000" - }, - "0fcfc4065008cfd323305f6286b57a4dd7eee23b": { - "balance": "20000000000000000000000" - }, - "cd725d70be97e677e3c8e85c0b26ef31e9955045": { - "balance": "1337000000000000000000" - }, - "dcf6b657266e91a4dae6033ddac15332dd8d2b34": { - "balance": "1760000000000000000000" - }, - "31f006f3494ed6c16eb92aaf9044fa8abb5fd5a3": { - "balance": "500000000000000000000" - }, - "cdea386f9d0fd804d02818f237b7d9fa7646d35e": { - "balance": "3012139000000000000000" - }, - "d45b3341e8f15c80329320c3977e3b90e7826a7e": { - "balance": "500000000000000000000" - }, - "0b649da3b96a102cdc6db652a0c07d65b1e443e6": { - "balance": "2000000000000000000000" - }, - "0a58fddd71898de773a74fdae45e7bd84ef43646": { - "balance": "20000000000000000000" - }, - "0256149f5b5063bea14e15661ffb58f9b459a957": { - "balance": "704000000000000000000" - }, - "4438e880cb2766b0c1ceaec9d2418fceb952a044": { - "balance": "133712000000000000000" - }, - "9ed80eda7f55054db9fb5282451688f26bb374c1": { - "balance": "300000000000000000000" - }, - "8dab948ae81da301d972e3f617a912e5a753712e": { - "balance": "400000000000000000000" - }, - "5b5d8c8eed6c85ac215661de026676823faa0a0c": { - "balance": "20000000000000000000000" - }, - "46722a36a01e841d03f780935e917d85d5a67abd": { - "balance": "14900000000000000000" - }, - "d4b8bdf3df9a51b0b91d16abbea05bb4783c8661": { - "balance": "1000000000000000000000" - }, - "98f6b8e6213dbc9a5581f4cce6655f95252bdb07": { - "balance": "319968000000000000000" - }, - "3599493ce65772cf93e98af1195ec0955dc98002": { - "balance": "1500048000000000000000" - }, - "ecab5aba5b828de1705381f38bc744b32ba1b437": { - "balance": "940000000000000000000" - }, - "9a82826d3c29481dcc2bd2950047e8b60486c338": { - "balance": "20000000000000000000000" - }, - "6c474bc66a54780066aa4f512eefa773abf919c7": { - "balance": "94000000000000000000" - }, - "d5903e9978ee20a38c3f498d63d57f31a39f6a06": { - "balance": "10380000000000000000000" - }, - "341480cc8cb476f8d01ff30812e7c70e05afaf5d": { - "balance": "2000000000000000000000" - }, - "af771039345a343001bc0f8a5923b126b60d509c": { - "balance": "985000000000000000000" - }, - "b5a4679685fa14196c2e9230c8c4e33bffbc10e2": { - "balance": "1400000000000000000000" - }, - "2a400dff8594de7228b4fd15c32322b75bb87da8": { - "balance": "95810000000000000000" - }, - "a1336dfb96b6bcbe4b3edf3205be5723c90fad52": { - "balance": "5000000000000000000000" - }, - "e9b1f1fca3fa47269f21b061c353b7f5e96d905a": { - "balance": "500000000000000000000" - }, - "0ee414940487fd24e390378285c5d7b9334d8b65": { - "balance": "2680000000000000000000" - }, - "6ab5b4c41cddb829690c2fda7f20c85e629dd5d5": { - "balance": "1860000000000000000000" - }, - "dd63042f25ed32884ad26e3ad959eb94ea36bf67": { - "balance": "21340000000000000000000" - }, - "c0b3f244bca7b7de5b48a53edb9cbeab0b6d88c0": { - "balance": "5820000000000000000000" - }, - "ed1a5c43c574d4e934299b24f1472cdc9fd6f010": { - "balance": "200000000000000000000" - }, - "b2d9ab9664bcf6df203c346fc692fd9cbab9205e": { - "balance": "438000000000000000000" - }, - "ede8c2cb876fbe8a4cca8290361a7ea01a69fdf8": { - "balance": "7813091000000000000000" - }, - "6a7c252042e7468a3ff773d6450bba85efa26391": { - "balance": "500000000000000000000" - }, - "a106e6923edd53ca8ed650968a9108d6ccfd9670": { - "balance": "9499935000000000000000" - }, - "031e25db516b0f099faebfd94f890cf96660836b": { - "balance": "2000000000000000000000" - }, - "7fdbc3a844e40d96b2f3a635322e6065f4ca0e84": { - "balance": "2000000000000000000000" - }, - "df47a61b72535193c561cccc75c3f3ce0804a20e": { - "balance": "398000000000000000000" - }, - "ed31305c319f9273d3936d8f5b2f71e9b1b22963": { - "balance": "100000000000000000000" - }, - "a6b2d573297360102c07a18fc21df2e7499ff4eb": { - "balance": "4011000000000000000000" - }, - "f68464bf64f2411356e4d3250efefe5c50a5f65b": { - "balance": "20000000000000000000" - }, - "927cc2bfda0e088d02eff70b38b08aa53cc30941": { - "balance": "1852700000000000000000" - }, - "41cb9896445f70a10a14215296daf614e32cf4d5": { - "balance": "1910000000000000000000" - }, - "3ad70243d88bf0400f57c8c1fd57811848af162a": { - "balance": "860000000000000000000" - }, - "63b9754d75d12d384039ec69063c0be210d5e0e3": { - "balance": "2694055000000000000000" - }, - "ad1799aad7602b4540cd832f9db5f11150f1687a": { - "balance": "2000000000000000000000" - }, - "a8b65ba3171a3f77a6350b9daf1f8d55b4d201eb": { - "balance": "745000000000000000000" - }, - "ad0a4ae478e9636e88c604f242cf5439c6d45639": { - "balance": "3520000000000000000000" - }, - "4cd0b0a6436362595ceade052ebc9b929fb6c6c0": { - "balance": "2000000000000000000000" - }, - "c1d4af38e9ba799040894849b8a8219375f1ac78": { - "balance": "20000000000000000000000" - }, - "49ddee902e1d0c99d1b11af3cc8a96f78e4dcf1a": { - "balance": "199358000000000000000" - }, - "ae842210f44d14c4a4db91fc9d3b3b50014f7bf7": { - "balance": "4000000000000000000000" - }, - "10a1c42dc1ba746986b985a522a73c93eae64c63": { - "balance": "1000000000000000000000" - }, - "5103bc09933e9921fd53dc536f11f05d0d47107d": { - "balance": "4000000000000000000000" - }, - "c88eec54d305c928cc2848c2fee23531acb96d49": { - "balance": "1999946000000000000000" - }, - "9a2ce43b5d89d6936b8e8c354791b8afff962425": { - "balance": "2000000000000000000000" - }, - "562020e3ed792d2f1835fe5f55417d5111460c6a": { - "balance": "20000000000000000000000" - }, - "ed16ce39feef3bd7f5d162045e0f67c0f00046bb": { - "balance": "20000000000000000000" - }, - "ab948a4ae3795cbca13126e19253bdc21d3a8514": { - "balance": "200000000000000000000" - }, - "c12b7f40df9a2f7bf983661422ab84c9c1f50858": { - "balance": "8000000000000000000000" - }, - "62e6b2f5eb94fa7a43831fc87e254a3fe3bf8f89": { - "balance": "250000000000000000000" - }, - "423bca47abc00c7057e3ad34fca63e375fbd8b4a": { - "balance": "18000000000000000000000" - }, - "5ff326cd60fd136b245e29e9087a6ad3a6527f0d": { - "balance": "1880000000000000000000" - }, - "79ffb4ac13812a0b78c4a37b8275223e176bfda5": { - "balance": "17300000000000000000" - }, - "f757fc8720d3c4fa5277075e60bd5c411aebd977": { - "balance": "2000000000000000000000" - }, - "0bdbc54cc8bdbbb402a08911e2232a5460ce866b": { - "balance": "3000000000000000000000" - }, - "9ee9760cc273d4706aa08375c3e46fa230aff3d5": { - "balance": "8950000000000000000000" - }, - "d23a24d7f9468343c143a41d73b88f7cbe63be5e": { - "balance": "200000000000000000000" - }, - "46d80631284203f6288ecd4e5758bb9d41d05dbe": { - "balance": "2000000000000000000000" - }, - "3f4cd1399f8a34eddb9a17a471fc922b5870aafc": { - "balance": "200000000000000000000" - }, - "44c54eaa8ac940f9e80f1e74e82fc14f1676856a": { - "balance": "7880000000000000000000" - }, - "aec27ff5d7f9ddda91183f46f9d52543b6cd2b2f": { - "balance": "450000000000000000000" - }, - "203c6283f20df7bc86542fdfb4e763ecdbbbeef5": { - "balance": "25000000000000000000000" - }, - "bcaf347918efb2d63dde03e39275bbe97d26df50": { - "balance": "100000000000000000000" - }, - "974d0541ab4a47ec7f75369c0069b64a1b817710": { - "balance": "400000000000000000000" - }, - "5da54785c9bd30575c89deb59d2041d20a39e17b": { - "balance": "1967031000000000000000" - }, - "1fb463a0389983df7d593f7bdd6d78497fed8879": { - "balance": "20000000000000000000" - }, - "6e1ea4b183e252c9bb7767a006d4b43696cb8ae9": { - "balance": "294245000000000000000" - }, - "c2aa74847e86edfdd3f3db22f8a2152feee5b7f7": { - "balance": "2048852000000000000000" - }, - "a13b9d82a99b3c9bba5ae72ef2199edc7d3bb36c": { - "balance": "1999944000000000000000" - }, - "5135fb8757600cf474546252f74dc0746d06262c": { - "balance": "2000000000000000000000" - }, - "43e7ec846358d7d0f937ad1c350ba069d7bf72bf": { - "balance": "118800000000000000000" - }, - "f2ed3e77254acb83231dc0860e1a11242ba627db": { - "balance": "1980000000000000000000" - }, - "c0a02ab94ebe56d045b41b629b98462e3a024a93": { - "balance": "100000000000000000000" - }, - "f21549bdd1487912f900a7523db5f7626121bba3": { - "balance": "10000000000000000000000" - }, - "886d0a9e17c9c095af2ea2358b89ec705212ee94": { - "balance": "28000000000000000000" - }, - "211b29cefc79ae976744fdebcebd3cbb32c51303": { - "balance": "14000000000000000000000" - }, - "b8c2703d8c3f2f44c584bc10e7c0a6b64c1c097e": { - "balance": "5550000000000000000000" - }, - "ec30addd895b82ee319e54fb04cb2bb03971f36b": { - "balance": "2000000000000000000000" - }, - "b71b62f4b448c02b1201cb5e394ae627b0a560ee": { - "balance": "500000000000000000000" - }, - "e1334e998379dfe983177062791b90f80ee22d8d": { - "balance": "500000000000000000000" - }, - "1d633097a85225a1ff4321b12988fdd55c2b3844": { - "balance": "4000000000000000000000" - }, - "8bd8d4c4e943f6c8073921dc17e3e8d7a0761627": { - "balance": "2933330000000000000000" - }, - "a5d96e697d46358d119af7819dc7087f6ae47fef": { - "balance": "14605131000000000000000" - }, - "d0809498c548047a1e2a2aa6a29cd61a0ee268bd": { - "balance": "2000000000000000000000" - }, - "3cd6b7593cbee77830a8b19d0801958fcd4bc57a": { - "balance": "500000000000000000000" - }, - "ead4d2eefb76abae5533961edd11400406b298fc": { - "balance": "3880000000000000000000" - }, - "6331028cbb5a21485bc51b565142993bdb2582a9": { - "balance": "534800000000000000000" - }, - "163bad4a122b457d64e8150a413eae4d07023e6b": { - "balance": "18800000000000000000" - }, - "c522e20fbf04ed7f6b05a37b4718d6fce0142e1a": { - "balance": "4000000000000000000000" - }, - "2d9bad6f1ee02a70f1f13def5cccb27a9a274031": { - "balance": "1790000000000000000000" - }, - "5ed0d6338559ef44dc7a61edeb893fa5d83fa1b5": { - "balance": "220000000000000000000" - }, - "ec8c1d7b6aaccd429db3a91ee4c9eb1ca4f6f73c": { - "balance": "4250000000000000000000" - }, - "3896ad743579d38e2302454d1fb6e2ab69e01bfd": { - "balance": "1880000000000000000000" - }, - "e73ccf436725c151e255ccf5210cfce5a43f13e3": { - "balance": "19982000000000000000" - }, - "9483d98f14a33fdc118d403955c29935edfc5f70": { - "balance": "459600000000000000000" - }, - "1cfcf7517f0c08459720942b647ad192aa9c8828": { - "balance": "800000000000000000000" - }, - "8d378f0edc0bb0f0686d6a20be6a7692c4fa24b8": { - "balance": "100000000000000000000" - }, - "06f68de3d739db41121eacf779aada3de8762107": { - "balance": "28000000000000000000" - }, - "9909650dd5b1397b8b8b0eb69499b291b0ad1213": { - "balance": "200000000000000000000" - }, - "b66675142e3111a1c2ea1eb2419cfa42aaf7a234": { - "balance": "1000000000000000000000" - }, - "7836f7ef6bc7bd0ff3acaf449c84dd6b1e2c939f": { - "balance": "4142296000000000000000" - }, - "3ddedbe48923fbf9e536bf9ffb0747c9cdd39eef": { - "balance": "16100000000000000000000" - }, - "c47d610b399250f70ecf1389bab6292c91264f23": { - "balance": "288800000000000000000" - }, - "51a6d627f66a8923d88d6094c4715380d3057cb6": { - "balance": "1152044000000000000000" - }, - "6c0cc917cbee7d7c099763f14e64df7d34e2bf09": { - "balance": "250000000000000000000" - }, - "aaaae68b321402c8ebc13468f341c63c0cf03fce": { - "balance": "1520000000000000000000" - }, - "819cdaa5303678ef7cec59d48c82163acc60b952": { - "balance": "14523448000000000000000" - }, - "d071192966eb69c3520fca3aa4dd04297ea04b4e": { - "balance": "110000000000000000000" - }, - "e53425d8df1f11c341ff58ae5f1438abf1ca53cf": { - "balance": "322000000000000000000" - }, - "8ffe322997b8e404422d19c54aadb18f5bc8e9b7": { - "balance": "3940000000000000000000" - }, - "305f78d618b990b4295bac8a2dfa262884f804ea": { - "balance": "4000000000000000000000" - }, - "274d69170fe7141401882b886ac4618c6ae40edb": { - "balance": "955000000000000000000" - }, - "69c94e07c4a9be3384d95dfa3cb9290051873b7b": { - "balance": "70000000000000000000" - }, - "859c600cf13d1d0273d5d1da3cd789e495899f27": { - "balance": "2674000000000000000000" - }, - "c06cebbbf7f5149a66f7eb976b3e47d56516da2f": { - "balance": "2000000000000000000000" - }, - "37bbc47212d82fcb5ee08f5225ecc2041ad2da7d": { - "balance": "3280000000000000000000" - }, - "11e7997edd904503d77da6038ab0a4c834bbd563": { - "balance": "388000000000000000000" - }, - "d333627445f2d787901ef33bb2a8a3675e27ffec": { - "balance": "400000000000000000000" - }, - "16a58e985dccd707a594d193e7cca78b5d027849": { - "balance": "1360000000000000000000" - }, - "f8ae857b67a4a2893a3fbe7c7a87ff1c01c6a6e7": { - "balance": "4000000000000000000000" - }, - "491561db8b6fafb9007e62d050c282e92c4b6bc8": { - "balance": "30000000000000000000000" - }, - "21df1ec24b4e4bfe79b0c095cebae198f291fbd1": { - "balance": "20000000000000000000000" - }, - "e208812a684098f3da4efe6aba256256adfe3fe6": { - "balance": "2000000000000000000000" - }, - "f4ec8e97a20aa5f8dd206f55207e06b813df2cc0": { - "balance": "200000000000000000000" - }, - "29eb7eefdae9feb449c63ff5f279d67510eb1422": { - "balance": "19400000000000000000" - }, - "0d678706d037187f3e22e6f69b99a592d11ebc59": { - "balance": "1580000000000000000000" - }, - "de6d363106cc6238d2f092f0f0372136d1cd50c6": { - "balance": "5348000000000000000000" - }, - "c8710d7e8b5a3bd69a42fe0fa8b87c357fddcdc8": { - "balance": "4000000000000000000000" - }, - "5267f4d41292f370863c90d793296903843625c7": { - "balance": "1400000000000000000000" - }, - "4cda41dd533991290794e22ae324143e309b3d3d": { - "balance": "2400000000000000000000" - }, - "f8a50cee2e688ceee3aca4d4a29725d4072cc483": { - "balance": "2000000000000000000000" - }, - "5ed3bbc05240e0d399eb6ddfe60f62de4d9509af": { - "balance": "193999806000000000000000" - }, - "0befb54707f61b2c9fb04715ab026e1bb72042bd": { - "balance": "4000000000000000000000" - }, - "cab9a301e6bd46e940355028eccd40ce4d5a1ac3": { - "balance": "400000000000000000000" - }, - "64672da3ab052821a0243d1ce4b6e0a36517b8eb": { - "balance": "200000000000000000000" - }, - "eac0827eff0c6e3ff28a7d4a54f65cb7689d7b99": { - "balance": "2856500000000000000000" - }, - "f4b6cdcfcb24230b337d770df6034dfbd4e1503f": { - "balance": "19000000000000000000000" - }, - "7be2f7680c802da6154c92c0194ae732517a7169": { - "balance": "18200000000000000000" - }, - "869f1aa30e4455beb1822091de5cadec79a8f946": { - "balance": "8000000000000000000000" - }, - "c4681e73bb0e32f6b726204831ff69baa4877e32": { - "balance": "1820000000000000000000" - }, - "962cd22a8edf1e4f4e55b4b15ddbfb5d9d541971": { - "balance": "2000000000000000000000" - }, - "131df8d330eb7cc7147d0a55576f05de8d26a8b7": { - "balance": "188000000000000000000" - }, - "19f99f2c0b46ce8906875dc9f90ae104dae35594": { - "balance": "4507300000000000000000" - }, - "91bb3f79022bf3c453f4ff256e269b15cf2c9cbd": { - "balance": "1519000000000000000000" - }, - "7301dc4cf26d7186f2a11bf8b08bf229463f64a3": { - "balance": "2000000000000000000000" - }, - "7cbca88fca6a0060b960985c9aa1b02534dc2208": { - "balance": "462500000000000000000" - }, - "f3c1abd29dc57b41dc192d0e384d021df0b4f6d4": { - "balance": "2798000000000000000000" - }, - "5d32f6f86e787ff78e63d78b0ef95fe6071852b8": { - "balance": "401100000000000000000" - }, - "1678c5f2a522393225196361894f53cc752fe2f3": { - "balance": "1936000000000000000000" - }, - "1cf04cb14380059efd3f238b65d5beb86afa14d8": { - "balance": "20000000000000000000" - }, - "52e1731350f983cc2c4189842fde0613fad50ce1": { - "balance": "11640000000000000000000" - }, - "d0b11d6f2bce945e0c6a5020c3b52753f803f9d1": { - "balance": "200000000000000000000" - }, - "409bd75085821c1de70cdc3b11ffc3d923c74010": { - "balance": "4000000000000000000000" - }, - "0bb7160aba293762f8734f3e0326ffc9a4cac190": { - "balance": "1000000000000000000000" - }, - "7aad4dbcd3acf997df93586956f72b64d8ad94ee": { - "balance": "4000000000000000000000" - }, - "2dec98329d1f96c3a59caa7981755452d4da49d5": { - "balance": "200000000000000000000" - }, - "c18ab467feb5a0aadfff91230ff056464d78d800": { - "balance": "2000000000000000000000" - }, - "c90c3765156bca8e4897ab802419153cbe5225a9": { - "balance": "200000000000000000000" - }, - "85c8f3cc7a354feac99a5e7bfe7cdfa351cfe355": { - "balance": "400000000000000000000" - }, - "f4fc4d39bc0c2c4068a36de50e4ab4d4db7e340a": { - "balance": "25380000000000000000" - }, - "f50abbd4aa45d3eb88515465a8ba0b310fd9b521": { - "balance": "6685000000000000000000" - }, - "4d200110124008d56f76981256420c946a6ff45c": { - "balance": "199955000000000000000" - }, - "f4ba6a46d55140c439cbcf076cc657136262f4f8": { - "balance": "2000000000000000000000" - }, - "fa7adf660b8d99ce15933d7c5f072f3cbeb99d33": { - "balance": "5910000000000000000000" - }, - "84503334630d77f74147f68b2e086613c8f1ade9": { - "balance": "1600000000000000000000" - }, - "31ed858788bda4d5270992221cc04206ec62610d": { - "balance": "1176000000000000000000" - }, - "bfbca418d3529cb393081062032a6e1183c6b2dc": { - "balance": "8000000000000000000000" - }, - "8263ece5d709e0d7ae71cca868ed37cd2fef807b": { - "balance": "990000000000000000000" - }, - "23ba3864da583dab56f420873c37679690e02f00": { - "balance": "9800000000000000000000" - }, - "cedcb3a1d6843fb6bef643617deaf38f8e98dd5f": { - "balance": "477500000000000000000" - }, - "8fac748f784a0fed68dba43319b42a75b4649c6e": { - "balance": "910000000000000000000" - }, - "18b8bcf98321da61fb4e3eacc1ec5417272dc27e": { - "balance": "880000000000000000000" - }, - "776943ffb2ef5cdd35b83c28bc046bd4f4677098": { - "balance": "3000000000000000000000" - }, - "fb8113f94d9173eefd5a3073f516803a10b286ae": { - "balance": "80000000000000000000" - }, - "3e8349b67f5745449f659367d9ad4712db5b895a": { - "balance": "1820000000000000000000" - }, - "79cfa9780ae6d87b2c31883f09276986c89a6735": { - "balance": "1000000000000000000000" - }, - "5006fe4c22173980f00c74342b39cd231c653129": { - "balance": "2000000000000000000000" - }, - "13848b46ea75beb7eaa85f59d866d77fd24cf21a": { - "balance": "50000000000000000000000" - }, - "d64a2d50f8858537188a24e0f50df1681ab07ed7": { - "balance": "38800000000000000000000" - }, - "4f9ce2af9b8c5e42c6808a3870ec576f313545d1": { - "balance": "10000000000000000000000" - }, - "8764d02722000996ecd475b433298e9f540b05bf": { - "balance": "200000000000000000000" - }, - "3b7c77dbe95dc2602ce3269a9545d04965fefdbd": { - "balance": "2000000000000000000000" - }, - "c9dcbb056f4db7d9da39936202c5bd8230b3b477": { - "balance": "20000000000000000000000" - }, - "9ecbabb0b22782b3754429e1757aaba04b81189f": { - "balance": "823743000000000000000" - }, - "831c44b3084047184b2ad218680640903750c45d": { - "balance": "1970000000000000000000" - }, - "ff8eb07de3d49d9d52bbe8e5b26dbe1d160fa834": { - "balance": "3986000000000000000000" - }, - "8ccf3aa21ab742576ad8c422f71bb188591dea8a": { - "balance": "1000000000000000000000" - }, - "ddac312a9655426a9c0c9efa3fd82559ef4505bf": { - "balance": "401100000000000000000" - }, - "9a3e2b1bf346dd070b027357feac44a4b2c97db8": { - "balance": "10000000000000000000000" - }, - "69d39d510889e552a396135bfcdb06e37e387633": { - "balance": "4000000000000000000000" - }, - "83a3148833d9644984f7c475a7850716efb480ff": { - "balance": "3400000000000000000000" - }, - "62b4a9226e61683c72c183254690daf511b4117a": { - "balance": "260000000000000000000" - }, - "50763add868fd7361178342fc055eaa2b95f6846": { - "balance": "66838000000000000000" - }, - "91898eab8c05c0222883cd4db23b7795e1a24ad7": { - "balance": "2000000000000000000000" - }, - "066647cfc85d23d37605573d208ca154b244d76c": { - "balance": "10000000000000000000000" - }, - "aaf9ee4b886c6d1e95496fd274235bf4ecfcb07d": { - "balance": "1400000000000000000000" - }, - "06860a93525955ff624940fadcffb8e149fd599c": { - "balance": "1999800000000000000000" - }, - "e81c2d346c0adf4cc56708f6394ba6c8c8a64a1e": { - "balance": "2000000000000000000000" - }, - "41a8e236a30e6d63c1ff644d132aa25c89537e01": { - "balance": "20000000000000000000" - }, - "6a679e378fdce6bfd97fe62f043c6f6405d79e99": { - "balance": "4000000000000000000000" - }, - "933436c8472655f64c3afaaf7c4c621c83a62b38": { - "balance": "1000000000000000000000" - }, - "abe07ced6ac5ddf991eff6c3da226a741bd243fe": { - "balance": "10000000000000000000000" - }, - "bb56a404723cff20d0685488b05a02cdc35aacaa": { - "balance": "20000000000000000000" - }, - "0d551ec1a2133c981d5fc6a8c8173f9e7c4f47af": { - "balance": "2000000000000000000000" - }, - "23376ecabf746ce53321cf42c86649b92b67b2ff": { - "balance": "2000000000000000000000" - }, - "644ba6c61082e989109f5c11d4b40e991660d403": { - "balance": "4000000000000000000000" - }, - "680d5911ed8dd9eec45c060c223f89a7f620bbd5": { - "balance": "20000000000000000000000" - }, - "cb1bb6f1da5eb10d4899f7e61d06c1b00fdfb52d": { - "balance": "1038000000000000000000" - }, - "303a30ac4286ae17cf483dad7b870c6bd64d7b4a": { - "balance": "500000000000000000000" - }, - "7b0b31ff6e24745ead8ed9bb85fc0bf2fe1d55d4": { - "balance": "800000000000000000000" - }, - "854691ce714f325ced55ce5928ce9ba12facd1b8": { - "balance": "4380000000000000000000" - }, - "a13cfe826d6d1841dcae443be8c387518136b5e8": { - "balance": "140000000000000000000000" - }, - "5fcd84546896dd081db1a320bd4d8c1dd1528c4c": { - "balance": "20000000000000000000" - }, - "3db5fe6a68bd3612ac15a99a61e555928eeceaf3": { - "balance": "1580000000000000000000" - }, - "7a79e30ff057f70a3d0191f7f53f761537af7dff": { - "balance": "400000000000000000000" - }, - "3d3fad49c9e5d2759c8e8e5a7a4d60a0dd135692": { - "balance": "20000000000000000000" - }, - "05a830724302bc0f6ebdaa1ebeeeb46e6ce00b39": { - "balance": "98500000000000000000" - }, - "e4b6ae22c7735f5b89f34dd77ad0975f0acc9181": { - "balance": "1000000000000000000000" - }, - "3f2dd55db7eab0ebee65b33ed8202c1e992e958b": { - "balance": "820000000000000000000" - }, - "395d6d255520a8db29abc47d83a5db8a1a7df087": { - "balance": "100000000000000000000" - }, - "1cc90876004109cd79a3dea866cb840ac364ba1b": { - "balance": "2000000000000000000000" - }, - "c83e9d6a58253beebeb793e6f28b054a58491b74": { - "balance": "281800000000000000000" - }, - "901d99b699e5c6911519cb2076b4c76330c54d22": { - "balance": "2000000000000000000000" - }, - "3a9132b7093d3ec42e1e4fb8cb31ecdd43ae773c": { - "balance": "2000000000000000000000" - }, - "b41eaf5d51a5ba1ba39bb418dbb54fab750efb1f": { - "balance": "1000000000000000000000" - }, - "aa493d3f4fb866491cf8f800efb7e2324ed7cfe5": { - "balance": "1700000000000000000000" - }, - "509982f56237ee458951047e0a2230f804e2e895": { - "balance": "17500000000000000000000" - }, - "316e92a91bbda68b9e2f98b3c048934e3cc0b416": { - "balance": "2000000000000000000000" - }, - "a3430e1f647f321ed34739562323c7d623410b56": { - "balance": "999942000000000000000" - }, - "fca43bbc23a0d321ba9e46b929735ce7d8ef0c18": { - "balance": "20000000000000000000" - }, - "ff45cb34c928364d9cc9d8bb00373474618f06f3": { - "balance": "100000000000000000000" - }, - "8c999591fd72ef7111efca7a9e97a2356b3b000a": { - "balance": "4084000000000000000000" - }, - "8579dadf1a395a3471e20b6f763d9a0ff19a3f6f": { - "balance": "4000000000000000000000" - }, - "c8d4e1599d03b79809e0130a8dc38408f05e8cd3": { - "balance": "2945500000000000000000" - }, - "2abce1808940cd4ef5b5e05285f82df7a9ab5e03": { - "balance": "9800000000000000000000" - }, - "0bb0c12682a2f15c9b5741b2385cbe41f034068e": { - "balance": "1500000000000000000000" - }, - "08b7bdcf944d5570838be70460243a8694485858": { - "balance": "2000000000000000000000" - }, - "c452e0e4b3d6ae06b836f032ca09db409ddfe0fb": { - "balance": "800000000000000000000" - }, - "48d4f2468f963fd79a006198bb67895d2d5aa4d3": { - "balance": "1400000000000000000000" - }, - "f9e7222faaf0f4da40c1c4a40630373a09bed7b6": { - "balance": "2865000000000000000000" - }, - "bf59aee281fa43fe97194351a9857e01a3b897b2": { - "balance": "600000000000000000000" - }, - "da0d4b7ef91fb55ad265f251142067f10376ced6": { - "balance": "20000000000000000000000" - }, - "2c6f5c124cc789f8bb398e3f889751bc4b602d9e": { - "balance": "24928000000000000000" - }, - "c85ef27d820403805fc9ed259fff64acb8d6346a": { - "balance": "2000000000000000000000" - }, - "9aa8308f42910e5ade09c1a5e282d6d91710bdbf": { - "balance": "200000000000000000000" - }, - "9e4cec353ac3e381835e3c0991f8faa5b7d0a8e6": { - "balance": "9999917000000000000000" - }, - "137cf341e8516c815814ebcd73e6569af14cf7bc": { - "balance": "1000000000000000000000" - }, - "889da662eb4a0a2a069d2bc24b05b4ee2e92c41b": { - "balance": "1663417000000000000000" - }, - "0998d8273115b56af43c505e087aff0676ed3659": { - "balance": "3999984000000000000000" - }, - "3e4d13c55a84e46ed7e9cb90fd355e8ad991e38f": { - "balance": "1000000000000000000000" - }, - "abc068b4979b0ea64a62d3b7aa897d73810dc533": { - "balance": "1970000000000000000000" - }, - "d8fdf546674738c984d8fab857880b3e4280c09e": { - "balance": "20000000000000000000" - }, - "aff161740a6d909fe99c59a9b77945c91cc91448": { - "balance": "60000000000000000000" - }, - "92ad1b3d75fba67d54663da9fc848a8ade10fa67": { - "balance": "2000000000000000000000" - }, - "819eb4990b5aba5547093da12b6b3c1093df6d46": { - "balance": "1000000000000000000000" - }, - "643d9aeed4b180947ed2b9207cce4c3ddc55e1f7": { - "balance": "200000000000000000000" - }, - "ab3e62e77a8b225e411592b1af300752fe412463": { - "balance": "9850000000000000000000" - }, - "650b425555e4e4c51718146836a2c1ee77a5b421": { - "balance": "20000000000000000000000" - }, - "ba8e46d69d2e2343d86c60d82cf42c2041a0c1c2": { - "balance": "100000000000000000000" - }, - "f9570e924c95debb7061369792cf2efec2a82d5e": { - "balance": "20000000000000000000" - }, - "4dc4bf5e7589c47b28378d7503cf96488061dbbd": { - "balance": "1760000000000000000000" - }, - "3d7ea5bf03528100ed8af8aed2653e921b6e6725": { - "balance": "1000000000000000000000" - }, - "a02bde6461686e19ac650c970d0672e76dcb4fc2": { - "balance": "8865000000000000000000" - }, - "b0e760bb07c081777345e0578e8bc898226d4e3b": { - "balance": "2000000000000000000000" - }, - "979cbf21dfec8ace3f1c196d82df962534df394f": { - "balance": "2832860000000000000000" - }, - "9f8245c3ab7d173164861cd3991b94f1ba40a93a": { - "balance": "2860000000000000000000" - }, - "c25cf826550c8eaf10af2234fef904ddb95213be": { - "balance": "1000000000000000000000" - }, - "967bfaf76243cdb9403c67d2ceefdee90a3feb73": { - "balance": "970582000000000000000" - }, - "0b2113504534642a1daf102eee10b9ebde76e261": { - "balance": "2733351000000000000000" - }, - "74bc4a5e2045f4ff8db184cf3a9b0c065ad807d2": { - "balance": "2000000000000000000000" - }, - "f1da40736f99d5df3b068a5d745fafc6463fc9b1": { - "balance": "121546000000000000000" - }, - "0fa6c7b0973d0bae2940540e247d3627e37ca347": { - "balance": "1000000000000000000000" - }, - "72b05962fb2ad589d65ad16a22559eba1458f387": { - "balance": "133700000000000000000" - }, - "6ceae3733d8fa43d6cd80c1a96e8eb93109c83b7": { - "balance": "298000000000000000000" - }, - "28eaea78cd4d95faecfb68836eafe83520f3bbb7": { - "balance": "200000000000000000000" - }, - "f49f6f9baabc018c8f8e119e0115f491fc92a8a4": { - "balance": "10000000000000000000000" - }, - "833316985d47742bfed410604a91953c05fb12b0": { - "balance": "2000000000000000000000" - }, - "ead75016e3a0815072b6b108bcc1b799acf0383e": { - "balance": "2000000000000000000000" - }, - "0032403587947b9f15622a68d104d54d33dbd1cd": { - "balance": "77500000000000000000" - }, - "8f64b9c1246d857831643107d355b5c75fef5d4f": { - "balance": "1999944000000000000000" - }, - "15dcafcc2bace7b55b54c01a1c514626bf61ebd8": { - "balance": "9400000000000000000000" - }, - "6886ada7bbb0617bda842191c68c922ea3a8ac82": { - "balance": "1160000000000000000000" - }, - "f736dc96760012388fe88b66c06efe57e0d7cf0a": { - "balance": "2100000000000000000000" - }, - "0b288a5a8b75f3dc4191eb0457e1c83dbd204d25": { - "balance": "4853000000000000000000" - }, - "56b6c23dd2ec90b4728f3bb2e764c3c50c85f144": { - "balance": "1000000000000000000000" - }, - "6310b020fd98044957995092090f17f04e52cdfd": { - "balance": "1580000000000000000000" - }, - "b0baeb30e313776c4c6d247402ba4167afcda1cc": { - "balance": "1970000000000000000000" - }, - "7641f7d26a86cddb2be13081810e01c9c83c4b20": { - "balance": "13370000000000000000" - }, - "07a8dadec142571a7d53a4297051786d072cba55": { - "balance": "22729000000000000000" - }, - "cc73dd356b4979b579b401d4cc7a31a268ddce5a": { - "balance": "500000000000000000000" - }, - "adf1acfe99bc8c14b304c8d905ba27657b8a7bc4": { - "balance": "20000000000000000000000" - }, - "72dabb5b6eed9e99be915888f6568056381608f8": { - "balance": "208433000000000000000" - }, - "9de20ae76aa08263b205d5142461961e2408d266": { - "balance": "252000000000000000000" - }, - "9d4ff989b7bed9ab109d10c8c7e55f02d76734ad": { - "balance": "1000000000000000000000" - }, - "e58dd23238ee6ea7c2138d385df500c325f376be": { - "balance": "1820000000000000000000" - }, - "4bd6dd0cff23400e1730ba7b894504577d14e74a": { - "balance": "206028000000000000000000" - }, - "35147430c3106500e79fa2f502462e94703c23b1": { - "balance": "1999944000000000000000" - }, - "c0ae14d724832e2fce2778de7f7b8daf7b12a93e": { - "balance": "20000000000000000000" - }, - "b57413060af3f14eb479065f1e9d19b3757ae8cc": { - "balance": "40000000000000000000" - }, - "7d04d2edc058a1afc761d9c99ae4fc5c85d4c8a6": { - "balance": "314807840000000000000000" - }, - "1c94d636e684eb155895ce6db4a2588fba1d001b": { - "balance": "2000000000000000000000" - }, - "c721b2a7aa44c21298e85039d00e2e460e670b9c": { - "balance": "140800000000000000000" - }, - "2d89a8006a4f137a20dc2bec46fe2eb312ea9654": { - "balance": "200000000000000000000" - }, - "646afba71d849e80c0ed59cac519b278e7f7abe4": { - "balance": "1000000000000000000000" - }, - "71f2cdd1b046e2da2fbb5a26723422b8325e25a3": { - "balance": "99960000000000000000" - }, - "2c9fa72c95f37d08e9a36009e7a4b07f29bad41a": { - "balance": "16100000000000000000" - }, - "848fbd29d67cf4a013cb02a4b176ef244e9ee68d": { - "balance": "20116000000000000000" - }, - "68190ca885da4231874c1cfb42b1580a21737f38": { - "balance": "3820000000000000000000" - }, - "9adf458bff3599eee1a26398853c575bc38c6313": { - "balance": "280000000000000000000" - }, - "b72220ade364d0369f2d2da783ca474d7b9b34ce": { - "balance": "499986000000000000000" - }, - "38e2af73393ea98a1d993a74df5cd754b98d529a": { - "balance": "1790000000000000000000" - }, - "4d38d90f83f4515c03cc78326a154d358bd882b7": { - "balance": "185000000000000000000" - }, - "aa8eb0823b07b0e6d20aadda0e95cf3835be192e": { - "balance": "32000000000000000000" - }, - "008639dabbe3aeac887b5dc0e43e13bcd287d76c": { - "balance": "310200000000000000000" - }, - "fa3a0c4b903f6ea52ea7ab7b8863b6a616ad6650": { - "balance": "20000000000000000000" - }, - "e26bf322774e18288769d67e3107deb7447707b8": { - "balance": "2000000000000000000000" - }, - "e061a4f2fc77b296d19ada238e49a5cb8ecbfa70": { - "balance": "4000000000000000000000" - }, - "b320834836d1dbfda9e7a3184d1ad1fd4320ccc0": { - "balance": "1000000000000000000000" - }, - "0ed3bb3a4eb554cfca97947d575507cdfd6d21d8": { - "balance": "547863000000000000000" - }, - "32fa0e86cd087dd68d693190f32d93310909ed53": { - "balance": "4000000000000000000000" - }, - "5b759fa110a31c88469f54d44ba303d57dd3e10f": { - "balance": "1683760000000000000000" - }, - "136f4907cab41e27084b9845069ff2fd0c9ade79": { - "balance": "4000000000000000000000" - }, - "3d89e505cb46e211a53f32f167a877bec87f4b0a": { - "balance": "25019000000000000000" - }, - "57a852fdb9b1405bf53ccf9508f83299d3206c52": { - "balance": "2000000000000000000000" - }, - "747abc9649056d3926044d28c3ad09ed17b67d70": { - "balance": "5000057000000000000000" - }, - "5c29f9e9a523c1f8669448b55c48cbd47c25e610": { - "balance": "964320000000000000000" - }, - "30a9da72574c51e7ee0904ba1f73a6b7b83b9b9d": { - "balance": "20200000000000000000" - }, - "220e2b92c0f6c902b513d9f1e6fab6a8b0def3d7": { - "balance": "800000000000000000000" - }, - "5af7c072b2c5acd71c76addcce535cf7f8f93585": { - "balance": "20000000000000000000" - }, - "81556db27349ab8b27004944ed50a46e941a0f5f": { - "balance": "3998000000000000000000" - }, - "987618c85656207c7bac1507c0ffefa2fb64b092": { - "balance": "64419000000000000000" - }, - "e0f372347c96b55f7d4306034beb83266fd90966": { - "balance": "400000000000000000000" - }, - "71784c105117c1f68935797fe159abc74e43d16a": { - "balance": "2001600000000000000000" - }, - "9284f96ddb47b5186ee558aa31324df5361c0f73": { - "balance": "16000000000000000000000" - }, - "a60c1209754f5d87b181da4f0817a81859ef9fd8": { - "balance": "50000000000000000000" - }, - "5afda9405c8e9736514574da928de67456010918": { - "balance": "6008500000000000000000" - }, - "6978696d5150a9a263513f8f74c696f8b1397cab": { - "balance": "6640000000000000000000" - }, - "a9ad1926bc66bdb331588ea8193788534d982c98": { - "balance": "30000000000000000000000" - }, - "e3f80b40fb83fb97bb0d5230af4f6ed59b1c7cc8": { - "balance": "1337000000000000000000" - }, - "e207578e1f4ddb8ff6d5867b39582d71b9812ac5": { - "balance": "3880000000000000000000" - }, - "86883d54cd3915e549095530f9ab1805e8c5432d": { - "balance": "4000000000000000000000" - }, - "6974c8a414ceaefd3c2e4dfdbef430568d9a960b": { - "balance": "334250000000000000000" - }, - "532d32b00f305bcc24dcef56817d622f34fb2c24": { - "balance": "1800000000000000000000" - }, - "761f8a3a2af0a8bdbe1da009321fb29764eb62a1": { - "balance": "10000000000000000000000" - }, - "4677b04e0343a32131fd6abb39b1b6156bba3d5b": { - "balance": "200000000000000000000" - }, - "ef69781f32ffce33346f2c9ae3f08493f3e82f89": { - "balance": "18200000000000000000" - }, - "e3b3d2c9bf570be6a2f72adca1862c310936a43c": { - "balance": "100100000000000000000" - }, - "d19caf39bb377fdf2cf19bd4fb52591c2631a63c": { - "balance": "1000000000000000000000" - }, - "5d68324bcb776d3ffd0bf9fea91d9f037fd6ab0f": { - "balance": "2000000000000000000000" - }, - "1c99fe9bb6c6d1066d912099547fd1f4809eacd9": { - "balance": "2000000000000000000000" - }, - "bbfe0a830cace87b7293993a7e9496ce64f8e394": { - "balance": "6000000000000000000000" - }, - "26c0054b700d3a7c2dcbe275689d4f4cad16a335": { - "balance": "2000000000000000000000" - }, - "7d7e7c61779adb7706c94d32409a2bb4e994bf60": { - "balance": "865992000000000000000" - }, - "d037d215d11d1df3d54fbd321cd295c5465e273b": { - "balance": "1400000000000000000000" - }, - "08166f02313feae18bb044e7877c808b55b5bf58": { - "balance": "1970000000000000000000" - }, - "781b1501647a2e06c0ed43ff197fccec35e1700b": { - "balance": "3000000000000000000000" - }, - "74316adf25378c10f576d5b41a6f47fa98fce33d": { - "balance": "336082000000000000000" - }, - "44e2fdc679e6bee01e93ef4a3ab1bcce012abc7c": { - "balance": "410231000000000000000" - }, - "178eaf6b8554c45dfde16b78ce0c157f2ee31351": { - "balance": "320000000000000000000" - }, - "cf923a5d8fbc3d01aa079d1cfe4b43ce071b1611": { - "balance": "2000000000000000000000" - }, - "0c28847e4f09dfce5f9b25af7c4e530f59c880fe": { - "balance": "1000000000000000000000" - }, - "54ce88275956def5f9458e3b95decacd484021a0": { - "balance": "2000000000000000000000" - }, - "9d4213339a01551861764c87a93ce8f85f87959a": { - "balance": "200000000000000000000" - }, - "e559b5fd337b9c5572a9bf9e0f2521f7d446dbe4": { - "balance": "200000000000000000000" - }, - "dcb03bfa6c1131234e56b7ea7c4f721487546b7a": { - "balance": "1337000000000000000000" - }, - "db6ff71b3db0928f839e05a7323bfb57d29c87aa": { - "balance": "910000000000000000000" - }, - "eb7c202b462b7cc5855d7484755f6e26ef43a115": { - "balance": "2000000000000000000000" - }, - "323486ca64b375474fb2b759a9e7a135859bd9f6": { - "balance": "400000000000000000000" - }, - "2c1df8a76f48f6b54bcf9caf56f0ee1cf57ab33d": { - "balance": "10118000000000000000000" - }, - "2cd87866568dd81ad47d9d3ad0846e5a65507373": { - "balance": "400000000000000000000" - }, - "8566610901aace38b83244f3a9c831306a67b9dc": { - "balance": "3256000000000000000000" - }, - "1c257ad4a55105ea3b58ed374b198da266c85f63": { - "balance": "10000000000000000000000" - }, - "cf4f1138f1bd6bf5b6d485cce4c1017fcb85f07d": { - "balance": "882038000000000000000" - }, - "c934becaf71f225f8b4a4bf7b197f4ac9630345c": { - "balance": "20000000000000000000000" - }, - "1e2bf4ba8e5ef18d37de6d6ad636c4cae489d0cc": { - "balance": "2000000000000000000000" - }, - "9d78a975b7db5e4d8e28845cfbe7e31401be0dd9": { - "balance": "1340000000000000000000" - }, - "16aa52cb0b554723e7060f21f327b0a68315fea3": { - "balance": "250000000000000000000" - }, - "97e28973b860c567402800fbb63ce39a048a3d79": { - "balance": "97000000000000000000" - }, - "4ac5acad000b8877214cb1ae00eac9a37d59a0fd": { - "balance": "4000000000000000000000" - }, - "01226e0ad8d62277b162621c62c928e96e0b9a8c": { - "balance": "2000000000000000000000" - }, - "479abf2da4d58716fd973a0d13a75f530150260a": { - "balance": "20000000000000000000" - }, - "31d81d526c195e3f10b5c6db52b5e59afbe0a995": { - "balance": "264000000000000000000" - }, - "749087ac0f5a97c6fad021538bf1d6cda18e0daa": { - "balance": "1000000000000000000000" - }, - "1565af837ef3b0bd4e2b23568d5023cd34b16498": { - "balance": "393284000000000000000" - }, - "997d6592a31589acc31b9901fbeb3cc3d65b3215": { - "balance": "2000000000000000000000" - }, - "9d207517422cc0d60de7c237097a4d4fce20940c": { - "balance": "500000000000000000000" - }, - "24b8b446debd1947955dd084f2c544933346d3ad": { - "balance": "4324135000000000000000" - }, - "107a03cf0842dbdeb0618fb587ca69189ec92ff5": { - "balance": "1970000000000000000000" - }, - "7f603aec1759ea5f07c7f8d41a1428fbbaf9e762": { - "balance": "20000000000000000000" - }, - "53a244672895480f4a2b1cdf7da5e5a242ec4dbc": { - "balance": "1000000000000000000000" - }, - "7db4c7d5b797e9296e6382f203693db409449d62": { - "balance": "400000000000000000000" - }, - "2ae82dab92a66389eea1abb901d1d57f5a7cca0b": { - "balance": "2000000000000000000000" - }, - "16bc40215abbd9ae5d280b95b8010b4514ff1292": { - "balance": "200000000000000000000" - }, - "bba4fac3c42039d828e742cde0efffe774941b39": { - "balance": "1999946000000000000000" - }, - "5431ca427e6165a644bae326bd09750a178c650d": { - "balance": "2000000000000000000000" - }, - "dcf33965531380163168fc11f67e89c6f1bc178a": { - "balance": "334885000000000000000" - }, - "65fd02d704a12a4dace9471b0645f962a89671c8": { - "balance": "28615000000000000000" - }, - "135d1719bf03e3f866312479fe338118cd387e70": { - "balance": "2000000000000000000000" - }, - "f3159866c2bc86bba40f9d73bb99f1eee57bb9d7": { - "balance": "1000000000000000000000" - }, - "e3a4621b66004588e31206f718cb00a319889cf0": { - "balance": "2000000000000000000000" - }, - "abcdbc8f1dd13af578d4a4774a62182bedf9f9be": { - "balance": "36660000000000000000" - }, - "9fbe066de57236dc830725d32a02aef9246c6c5e": { - "balance": "2000000000000000000000" - }, - "81cfad760913d3c322fcc77b49c2ae3907e74f6e": { - "balance": "197000000000000000000" - }, - "0ab59d390702c9c059db148eb4f3fcfa7d04c7e7": { - "balance": "18200000000000000000" - }, - "2c2db28c3309375eea3c6d72cd6d0eec145afcc0": { - "balance": "2000000000000000000000" - }, - "08306de51981e7aca1856859b7c778696a6b69f9": { - "balance": "3200000000000000000000" - }, - "f814799f6ddf4dcb29c7ee870e75f9cc2d35326d": { - "balance": "1000000000000000000000" - }, - "ee867d20916bd2e9c9ece08aa04385db667c912e": { - "balance": "50000000000000000000000" - }, - "97a86f01ce3f7cfd4441330e1c9b19e1b10606ef": { - "balance": "2000000000000000000000" - }, - "4c759813ad1386bed27ffae9e4815e3630cca312": { - "balance": "2000000000000000000000" - }, - "8f226096c184ebb40105e08dac4d22e1c2d54d30": { - "balance": "306552000000000000000" - }, - "13acada8980affc7504921be84eb4944c8fbb2bd": { - "balance": "1601600000000000000000" - }, - "122dcfd81addb97d1a0e4925c4b549806e9f3beb": { - "balance": "1514954000000000000000" - }, - "232f525d55859b7d4e608d20487faadb00293135": { - "balance": "4000000000000000000000" - }, - "6f7ac681d45e418fce8b3a1db5bc3be6f06c9849": { - "balance": "2000000000000000000000" - }, - "0c8692eeff2a53d6d1688ed56a9ddbbd68dabba1": { - "balance": "2000000000000000000000" - }, - "6a6337833f8f6a6bf10ca7ec21aa810ed444f4cb": { - "balance": "1028200000000000000000" - }, - "209377b6ad3fe101c9685b3576545c6b1684e73c": { - "balance": "1820000000000000000000" - }, - "560fc08d079f047ed8d7df75551aa53501f57013": { - "balance": "7600000000000000000000" - }, - "8e78f351457d016f4ad2755ec7424e5c21ba6d51": { - "balance": "146000000000000000000" - }, - "2ce11a92fad024ff2b3e87e3b542e6c60dcbd996": { - "balance": "4000000000000000000000" - }, - "8ab839aeaf2ad37cb78bacbbb633bcc5c099dc46": { - "balance": "2000000000000000000000" - }, - "673144f0ec142e770f4834fee0ee311832f3087b": { - "balance": "500038000000000000000" - }, - "ba8a63f3f40de4a88388bc50212fea8e064fbb86": { - "balance": "2000000000000000000000" - }, - "ee899b02cbcb3939cd61de1342d50482abb68532": { - "balance": "1760000000000000000000" - }, - "c2d9eedbc9019263d9d16cc5ae072d1d3dd9db03": { - "balance": "20000000000000000000000" - }, - "355c0c39f5d5700b41d375b3f17851dcd52401f9": { - "balance": "3979000000000000000000" - }, - "8179c80970182cc5b7d82a4df06ea94db63a25f3": { - "balance": "727432000000000000000" - }, - "b388b5dfecd2c5e4b596577c642556dbfe277855": { - "balance": "20000000000000000000" - }, - "a9e28337e6357193d9e2cb236b01be44b81427df": { - "balance": "2200000000000000000000" - }, - "04ba4bb87140022c214a6fac42db5a16dd954045": { - "balance": "1000000000000000000000" - }, - "67c926093e9b8927933810d98222d62e2b8206bb": { - "balance": "1910000000000000000000" - }, - "ed7346766e1a676d0d06ec821867a276a083bf31": { - "balance": "4012890000000000000000" - }, - "92558226b384626cad48e09d966bf1395ee7ea5d": { - "balance": "334250000000000000000" - }, - "bdf693f833c3fe471753184788eb4bfe4adc3f96": { - "balance": "1970000000000000000000" - }, - "4474299d0ee090dc90789a1486489c3d0d645e6d": { - "balance": "1000000000000000000000" - }, - "b1178ad47383c31c8134a1941cbcd474d06244e2": { - "balance": "1000000000000000000000" - }, - "979d681c617da16f21bcaca101ed16ed015ab696": { - "balance": "1880000000000000000000" - }, - "6b20c080606a79c73bd8e75b11717a4e8db3f1c3": { - "balance": "299720000000000000000" - }, - "b85218f342f8012eda9f274e63ce2152b2dcfdab": { - "balance": "3100000000000000000000" - }, - "530b61e42f39426d2408d40852b9e34ab5ebebc5": { - "balance": "267400000000000000000" - }, - "76afc225f4fa307de484552bbe1d9d3f15074c4a": { - "balance": "2998800000000000000000" - }, - "1e783e522ab7df0acaac9eeed3593039e5ac7579": { - "balance": "203435800000000000000000" - }, - "0f7bf6373f771a4601762c4dae5fbbf4fedd9cc9": { - "balance": "2000000000000000000000" - }, - "7a8797690ab77b5470bf7c0c1bba612508e1ac7d": { - "balance": "8865000000000000000000" - }, - "2a2ab6b74c7af1d9476bb5bcb4524797bedc3552": { - "balance": "1000000000000000000000" - }, - "523e140dc811b186dee5d6c88bf68e90b8e096fd": { - "balance": "2000000000000000000000" - }, - "ea8168fbf225e786459ca6bb18d963d26b505309": { - "balance": "500000000000000000000" - }, - "20ff3ede8cadb5c37b48cb14580fb65e23090a7b": { - "balance": "42000000000000000000000" - }, - "e482d255ede56b04c3e8df151f56e9ca62aaa8c2": { - "balance": "500000000000000000000" - }, - "2e0880a34596230720f05ac8f065af8681dcb6c2": { - "balance": "100000000000000000000000" - }, - "c674f28c8afd073f8b799691b2f0584df942e844": { - "balance": "2000000000000000000000" - }, - "b646df98b49442746b61525c81a3b04ba3106250": { - "balance": "1970000000000000000000" - }, - "d55c1c8dfbe1e02cacbca60fdbdd405b09f0b75f": { - "balance": "2000000000000000000000" - }, - "65ebaed27edb9dcc1957aee5f452ac2105a65c0e": { - "balance": "43531987000000000000000" - }, - "f079e1b1265f50e8c8a98ec0c7815eb3aeac9eb4": { - "balance": "20094000000000000000" - }, - "867eba56748a5904350d2ca2a5ce9ca00b670a9b": { - "balance": "20000000000000000000000" - }, - "51ee0cca3bcb10cd3e983722ced8493d926c0866": { - "balance": "999972000000000000000" - }, - "88d541c840ce43cefbaf6d19af6b9859b573c145": { - "balance": "170000000000000000000" - }, - "f851b010f633c40af1a8f06a73ebbaab65077ab5": { - "balance": "4400000000000000000000" - }, - "e0aa69365555b73f282333d1e30c1bbd072854e8": { - "balance": "7000000000000000000000" - }, - "c7b1c83e63203f9547263ef6282e7da33b6ed659": { - "balance": "18200000000000000000" - }, - "af06f5fa6d1214ec43967d1bd4dde74ab814a938": { - "balance": "88000000000000000000" - }, - "991173601947c2084a62d639527e961512579af9": { - "balance": "600000000000000000000" - }, - "7a381122bada791a7ab1f6037dac80432753baad": { - "balance": "10000000000000000000000" - }, - "e766f34ff16f3cfcc97321721f43ddf5a38b0cf4": { - "balance": "1550000000000000000000" - }, - "d785a8f18c38b9bc4ffb9b8fa8c7727bd642ee1c": { - "balance": "1000000000000000000000" - }, - "aebd4f205de799b64b3564b256d42a711d37ef99": { - "balance": "1177100000000000000000" - }, - "a2fa17c0fb506ce494008b9557841c3f641b8cae": { - "balance": "20000000000000000000" - }, - "a8aca748f9d312ec747f8b6578142694c7e9f399": { - "balance": "2000000000000000000000" - }, - "950c68a40988154d2393fff8da7ccda99614f72c": { - "balance": "4597943000000000000000" - }, - "075d15e2d33d8b4fa7dba8b9e607f04a261e340b": { - "balance": "1910000000000000000000" - }, - "3616d448985f5d32aefa8b93a993e094bd854986": { - "balance": "205400000000000000000" - }, - "4bb9655cfb2a36ea7c637a7b859b4a3154e26ebe": { - "balance": "16000000000000000000000" - }, - "84949dba559a63bfc845ded06e9f2d9b7f11ef24": { - "balance": "2000000000000000000000" - }, - "937563d8a80fd5a537b0e66d20a02525d5d88660": { - "balance": "2500000000000000000000" - }, - "b183ebee4fcb42c220e47774f59d6c54d5e32ab1": { - "balance": "1604266000000000000000" - }, - "21e5d77320304c201c1e53b261a123d0a1063e81": { - "balance": "86972000000000000000" - }, - "fa14b566234abee73042c31d21717182cba14aa1": { - "balance": "328000000000000000000" - }, - "2da617695009cc57d26ad490b32a5dfbeb934e5e": { - "balance": "20000000000000000000000" - }, - "3326b88de806184454c40b27f309d9dd6dcfb978": { - "balance": "17900000000000000000000" - }, - "95e6a54b2d5f67a24a4875af75107ca7ea9fd2fa": { - "balance": "1337000000000000000000" - }, - "8db58e406e202df9bc703c480bd8ed248d52a032": { - "balance": "2000000000000000000000" - }, - "f777361a3dd8ab62e5f1b9b047568cc0b555704c": { - "balance": "1000000000000000000000" - }, - "83a93b5ba41bf88720e415790cdc0b67b4af34c4": { - "balance": "200000000000000000000" - }, - "8a1cc5ac111c49bfcfd848f37dd768aa65c88802": { - "balance": "10000000000000000000000" - }, - "52214378b54004056a7cc08c891327798ac6b248": { - "balance": "15200000000000000000000" - }, - "ad80d865b85c34d2e6494b2e7aefea6b9af184db": { - "balance": "4000000000000000000000" - }, - "e7d6240620f42c5edbb2ede6aec43da4ed9b5757": { - "balance": "1000000000000000000000" - }, - "d0e35e047646e759f4517093d6408642517f084d": { - "balance": "3939507000000000000000" - }, - "9340345ca6a3eabdb77363f2586043f29438ce0b": { - "balance": "530922000000000000000" - }, - "6640ccf053555c130ae2b656647ea6e31637b9ab": { - "balance": "1970000000000000000000" - }, - "184d86f3466ae6683b19729982e7a7e1a48347b2": { - "balance": "10000000000000000000000" - }, - "84ec06f24700fe42414cb9897c154c88de2f6132": { - "balance": "1337000000000000000000" - }, - "d1e5e234a9f44266a4a6241a84d7a1a55ad5a7fe": { - "balance": "20000000000000000000000" - }, - "e8a9a41740f44f54c3688b53e1ddd42e43c9fe94": { - "balance": "4000000000000000000000" - }, - "6e3a51db743d334d2fe88224b5fe7c008e80e624": { - "balance": "106000000000000000000" - }, - "3e94df5313fa520570ef232bc3311d5f622ff183": { - "balance": "2000000000000000000000" - }, - "8957727e72cf629020f4e05edf799aa7458062d0": { - "balance": "2200000000000000000000" - }, - "cf5e0eacd1b39d0655f2f77535ef6608eb950ba0": { - "balance": "2000000000000000000000" - }, - "f4aaa3a6163e3706577b49c0767e948a681e16ee": { - "balance": "2000000000000000000000" - }, - "97f1fe4c8083e596212a187728dd5cf80a31bec5": { - "balance": "20000000000000000000" - }, - "57d5fd0e3d3049330ffcdcd020456917657ba2da": { - "balance": "1991240000000000000000" - }, - "49bdbc7ba5abebb6389e91a3285220d3451bd253": { - "balance": "1000000000000000000000" - }, - "ae126b382cf257fad7f0bc7d16297e54cc7267da": { - "balance": "300000000000000000000" - }, - "bbf8616d97724af3def165d0e28cda89b800009a": { - "balance": "114063000000000000000" - }, - "adb948b1b6fefe207de65e9bbc2de98e605d0b57": { - "balance": "2000000000000000000000" - }, - "8a217db38bc35f215fd92906be42436fe7e6ed19": { - "balance": "6000000000000000000000" - }, - "e28b062259e96eeb3c8d4104943f9eb325893cf5": { - "balance": "1337000000000000000000" - }, - "6a6b18a45a76467e2e5d5a2ef911c3e12929857b": { - "balance": "82000000000000000000000" - }, - "cb68ae5abe02dcf8cbc5aa719c25814651af8b85": { - "balance": "500000000000000000000" - }, - "4c7e2e2b77ad0cd6f44acb2861f0fb8b28750ef9": { - "balance": "20000000000000000000" - }, - "58ba1569650e5bbbb21d35d3e175c0d6b0c651a9": { - "balance": "500000000000000000000" - }, - "1eb4bf73156a82a0a6822080c6edf49c469af8b9": { - "balance": "1910000000000000000000" - }, - "4103299671d46763978fa4aa19ee34b1fc952784": { - "balance": "200000000000000000000" - }, - "e321bb4a946adafdade4571fb15c0043d39ee35f": { - "balance": "1575212000000000000000" - }, - "893608751d68d046e85802926673cdf2f57f7cb8": { - "balance": "19700000000000000000" - }, - "70fee08b00c6c2c04a3c625c1ff77caf1c32df01": { - "balance": "200000000000000000000" - }, - "7b0fea1176d52159333a143c294943da36bbddb4": { - "balance": "9380000000000000000000" - }, - "d331c823825a9e5263d052d8915d4dcde07a5c37": { - "balance": "564000000000000000000" - }, - "a45432a6f2ac9d56577b938a37fabac8cc7c461c": { - "balance": "1000000000000000000000" - }, - "764fc46d428b6dbc228a0f5f55c9508c772eab9f": { - "balance": "26000000000000000000000" - }, - "1a95a8a8082e4652e4170df9271cb4bb4305f0b2": { - "balance": "50000000000000000000" - }, - "08c9f1bfb689fdf804d769f82123360215aff93b": { - "balance": "1970000000000000000000" - }, - "1572cdfab72a01ce968e78f5b5448da29853fbdd": { - "balance": "5061500000000000000000" - }, - "379c7166849bc24a02d6535e2def13daeef8aa8d": { - "balance": "100000000000000000000" - }, - "e0a254ac09b9725bebc8e460431dd0732ebcabbf": { - "balance": "6000000000000000000000" - }, - "3225c1ca5f2a9c88156bb7d9cdc44a326653c214": { - "balance": "400000000000000000000" - }, - "84686c7bad762c54b667d59f90943cd14d117a26": { - "balance": "20000000000000000000" - }, - "3d5a8b2b80be8b35d8ecf789b5ed7a0775c5076c": { - "balance": "20000000000000000000" - }, - "2ccf80e21898125eb4e807cd82e09b9d28592f6e": { - "balance": "2000000000000000000000" - }, - "dde969aef34ea87ac299b7597e292b4a0155cc8a": { - "balance": "298819000000000000000" - }, - "19e94e620050aad766b9e1bad931238312d4bf49": { - "balance": "2396000000000000000000" - }, - "959f57fded6ae37913d900b81e5f48a79322c627": { - "balance": "255599000000000000000" - }, - "b9b0a3219a3288d9b35b091b14650b8fe23dce2b": { - "balance": "14000000000000000000000" - }, - "3575c770668a9d179f1ef768c293f80166e2aa3d": { - "balance": "474000000000000000000" - }, - "58f05b262560503ca761c61890a4035f4c737280": { - "balance": "8000000000000000000000" - }, - "3286d1bc657a312c8847d93cb3cb7950f2b0c6e3": { - "balance": "20000000000000000000000" - }, - "1d9e6aaf8019a05f230e5def05af5d889bd4d0f2": { - "balance": "133700000000000000000" - }, - "a375b4bc24a24e1f797593cc302b2f331063fa5c": { - "balance": "200000000000000000000" - }, - "108ba7c2895c50e072dc6f964932d50c282d3034": { - "balance": "500000000000000000000" - }, - "b6b34a263f10c3d2eceb0acc559a7b2ab85ce565": { - "balance": "4000000000000000000000" - }, - "a4d2b429f1ad5349e31704969edc5f25ee8aca10": { - "balance": "10000000000000000000000" - }, - "674adb21df4c98c7a347ac4c3c24266757dd7039": { - "balance": "2000000000000000000000" - }, - "33565ba9da2c03e778ce12294f081dfe81064d24": { - "balance": "16000000000000000000000" - }, - "4ddda7586b2237b053a7f3289cf460dc57d37a09": { - "balance": "10000000000000000000000" - }, - "cc4faac00be6628f92ef6b8cb1b1e76aac81fa18": { - "balance": "205410000000000000000" - }, - "5f99dc8e49e61d57daef606acdd91b4d7007326a": { - "balance": "3000000000000000000000" - }, - "b8a979352759ba09e35aa5935df175bff678a108": { - "balance": "20000000000000000000" - }, - "86fff220e59305c09f483860d6f94e96fbe32f57": { - "balance": "42900000000000000000" - }, - "03e8b084537557e709eae2e1e1a5a6bce1ef8314": { - "balance": "20000000000000000000" - }, - "dda4ff7de491c687df4574dd1b17ff8f246ba3d1": { - "balance": "19600000000000000000000" - }, - "2538532936813c91e653284f017c80c3b8f8a36f": { - "balance": "2002000000000000000000" - }, - "5a82f96cd4b7e2d93d10f3185dc8f43d4b75aa69": { - "balance": "1999400000000000000000" - }, - "86740a46648e845a5d96461b18091ff57be8a16f": { - "balance": "98000000000000000000000" - }, - "7e3f63e13129a221ba1ab06326342cd98b5126ae": { - "balance": "1597960000000000000000" - }, - "1f5f3b34bd134b2781afe5a0424ac5846cdefd11": { - "balance": "99000000000000000000" - }, - "39936c2719450b9420cc2522cf91db01f227c1c1": { - "balance": "500000000000000000000" - }, - "967076a877b18ec15a415bb116f06ef32645dba3": { - "balance": "2000000000000000000000" - }, - "a42908e7fe53980a9abf4044e957a54b70e99cbe": { - "balance": "2000000000000000000000" - }, - "5eb371c407406c427b3b7de271ad3c1e04269579": { - "balance": "3000000000000000000000" - }, - "a570223ae3caa851418a9843a1ac55db4824f4fd": { - "balance": "200000000000000000000" - }, - "764692cccb33405dd0ab0c3379b49caf8e6221ba": { - "balance": "20000000000000000000" - }, - "a365918bfe3f2627b9f3a86775d8756e0fd8a94b": { - "balance": "400000000000000000000" - }, - "069ed0ab7aa77de571f16106051d92afe195f2d0": { - "balance": "200000000000000000000" - }, - "bd432a3916249b4724293af9146e49b8280a7f2a": { - "balance": "4000000000000000000000" - }, - "61c9dce8b2981cb40e98b0402bc3eb28348f03ac": { - "balance": "196910000000000000000" - }, - "8f1fcc3c51e252b693bc5b0ec3f63529fe69281e": { - "balance": "6000000000000000000000" - }, - "55fd08d18064bd202c0ec3d2cce0ce0b9d169c4d": { - "balance": "1970000000000000000000" - }, - "383a7c899ee18bc214969870bc7482f6d8f3570e": { - "balance": "10000000000000000000000" - }, - "b14cc8de33d6338236539a489020ce4655a32bc6": { - "balance": "8000000000000000000000" - }, - "448bf410ad9bbc2fecc4508d87a7fc2e4b8561ad": { - "balance": "199955000000000000000" - }, - "06f7dc8d1b9462cef6feb13368a7e3974b097f9f": { - "balance": "2000000000000000000000" - }, - "9c9f89a3910f6a2ae8a91047a17ab788bddec170": { - "balance": "10000000000000000000000" - }, - "5de598aba344378cab4431555b4f79992dc290c6": { - "balance": "1337000000000000000000" - }, - "87e6034ecf23f8b5639d5f0ea70a22538a920423": { - "balance": "328000000000000000000" - }, - "8b27392206b958cd375d7ef8af2cf8ef0598c0bc": { - "balance": "1000000000000000000000" - }, - "49136fe6e28b7453fcb16b6bbbe9aaacba8337fd": { - "balance": "2000000000000000000000" - }, - "6982fe8a867e93eb4a0bd051589399f2ec9a5292": { - "balance": "2000000000000000000000" - }, - "9fd1052a60506bd1a9ef003afd9d033c267d8e99": { - "balance": "1000000000000000000000" - }, - "d38fa2c4cc147ad06ad5a2f75579281f22a7cc1f": { - "balance": "20000000000000000000000" - }, - "6f794dbdf623daa6e0d00774ad6962737c921ea4": { - "balance": "2000000000000000000000" - }, - "e96b184e1f0f54924ac874f60bbf44707446b72b": { - "balance": "2910840000000000000000" - }, - "b5ba29917c78a1d9e5c5c713666c1e411d7f693a": { - "balance": "3100000000000000000000" - }, - "81d619ff5726f2405f12904c72eb1e24a0aaee4f": { - "balance": "20000000000000000000000" - }, - "b02fa29387ec12e37f6922ac4ce98c5b09e0b00f": { - "balance": "2000000000000000000000" - }, - "b7230d1d1ff2aca366963914a79df9f7c5ea2c98": { - "balance": "8000000000000000000000" - }, - "7b4007c45e5a573fdbb6f8bd746bf94ad04a3c26": { - "balance": "15202564000000000000000" - }, - "8d9a0c70d2262042df1017d6c303132024772712": { - "balance": "2000000000000000000000" - }, - "323aad41df4b6fc8fece8c93958aa901fa680843": { - "balance": "970000000000000000000" - }, - "db04fad9c49f9e880beb8fcf1d3a3890e4b3846f": { - "balance": "1242482000000000000000" - }, - "27824666d278d70423f03dfe1dc7a3f02f43e2b5": { - "balance": "1000070000000000000000" - }, - "e04920dc6ecc1d6ecc084f88aa0af5db97bf893a": { - "balance": "182000000000000000000" - }, - "b0c1b177a220e41f7c74d07cde8569c21c75c2f9": { - "balance": "5600000000000000000000" - }, - "7864dc999fe4f8e003c0f43decc39aae1522dc0f": { - "balance": "94400000000000000000" - }, - "c75c37ce2da06bbc40081159c6ba0f976e3993b1": { - "balance": "1078640000000000000000" - }, - "179a825e0f1f6e985309668465cffed436f6aea9": { - "balance": "20000000000000000000" - }, - "2c6b699d9ead349f067f45711a074a641db6a897": { - "balance": "20000000000000000000" - }, - "068ce8bd6e902a45cb83b51541b40f39c4469712": { - "balance": "5240000000000000000000" - }, - "767ac690791c2e23451089fe6c7083fe55deb62b": { - "balance": "820000000000000000000" - }, - "b34f04b8db65bba9c26efc4ce6efc50481f3d65d": { - "balance": "20000000000000000000000" - }, - "29aef48de8c9fbad4b9e4ca970797a5533eb722d": { - "balance": "10000000000000000000000" - }, - "0a0ecda6636f7716ef1973614687fd89a820a706": { - "balance": "394000000000000000000" - }, - "b32825d5f3db249ef4e85cc4f33153958976e8bc": { - "balance": "501375000000000000000" - }, - "7ef16fd8d15b378a0fba306b8d03dd98fc92619f": { - "balance": "700000000000000000000" - }, - "b58b52865ea55d8036f2fab26098b352ca837e18": { - "balance": "18200000000000000000" - }, - "9b658fb361e046d4fcaa8aef6d02a99111223625": { - "balance": "2000000000000000000000" - }, - "b2a498f03bd7178bd8a789a00f5237af79a3e3f8": { - "balance": "19400000000000000000000" - }, - "cb48fe8265d9af55eb7006bc335645b0a3a183be": { - "balance": "3000000000000000000000" - }, - "3cf9a1d465e78b7039e3694478e2627b36fcd141": { - "balance": "1372000000000000000000" - }, - "5db84400570069a9573cab04b4e6b69535e202b8": { - "balance": "9700000000000000000000" - }, - "214c89c5bd8e7d22bc574bb35e48950211c6f776": { - "balance": "18903000000000000000" - }, - "53396f4a26c2b4604496306c5442e7fcba272e36": { - "balance": "20055000000000000000000" - }, - "720994dbe56a3a95929774e20e1fe525cf3704e4": { - "balance": "8000000000000000000000" - }, - "3571cf7ad304ecaee595792f4bbfa484418549d6": { - "balance": "5825500000000000000000" - }, - "6042c644bae2b96f25f94d31f678c90dc96690db": { - "balance": "2000000000000000000000" - }, - "2e24b597873bb141bdb237ea8a5ab747799af02d": { - "balance": "20000000000000000000000" - }, - "08c802f87758349fa03e6bc2e2fd0791197eea9a": { - "balance": "2000000000000000000000" - }, - "297a88921b5fca10e5bb9ded60025437ae221694": { - "balance": "200000000000000000000" - }, - "aee49d68adedb081fd43705a5f78c778fb90de48": { - "balance": "20000000000000000000" - }, - "4cee901b4ac8b156c5e2f8a6f1bef572a7dceb7e": { - "balance": "1000000000000000000000" - }, - "dfaf31e622c03d9e18a0ddb8be60fbe3e661be0a": { - "balance": "9999800000000000000000" - }, - "00aa5381b2138ebeffc191d5d8c391753b7098d2": { - "balance": "990049000000000000000" - }, - "5b4c0c60f10ed2894bdb42d9dd1d210587810a0d": { - "balance": "500000000000000000000" - }, - "c44f4ab5bc60397c737eb0683391b633f83c48fa": { - "balance": "1000000000000000000000" - }, - "50bef2756248f9a7a380f91b051ba3be28a649ed": { - "balance": "1999884000000000000000" - }, - "1bd909ac0d4a1102ec98dcf2cca96a0adcd7a951": { - "balance": "20055000000000000000" - }, - "9ec03e02e587b7769def538413e97f7e55be71d8": { - "balance": "19700000000000000000000" - }, - "9874803fe1f3a0365e7922b14270eaeb032cc1b5": { - "balance": "1124500000000000000000" - }, - "4e2310191ead8d3bc6489873a5f0c2ec6b87e1be": { - "balance": "1000000000000000000000" - }, - "93678a3c57151aeb68efdc43ef4d36cb59a009f3": { - "balance": "30060000000000000000" - }, - "f483f607a21fcc28100a018c568ffbe140380410": { - "balance": "1000000000000000000000" - }, - "2a91a9fed41b7d0e5cd2d83158d3e8a41a9a2d71": { - "balance": "1940000000000000000000" - }, - "240e559e274aaef0c258998c979f671d1173b88b": { - "balance": "4000000000000000000000" - }, - "108a2b7c336f784779d8b54d02a8d31d9a139c0a": { - "balance": "10000000000000000000000" - }, - "9c98fdf1fdcd8ba8f4c5b04c3ae8587efdf0f6e6": { - "balance": "6000000000000000000000" - }, - "194ff44aefc17bd20efd7a204c47d1620c86db5d": { - "balance": "2999400000000000000000" - }, - "1f8116bd0af5570eaf0c56c49c7ab5e37a580458": { - "balance": "2000000000000000000000" - }, - "d79835e404fb86bf845fba090d6ba25e0c8866a6": { - "balance": "2400000000000000000000" - }, - "a8e7201ff619faffc332e6ad37ed41e301bf014a": { - "balance": "600000000000000000000" - }, - "286906b6bd4972e3c71655e04baf36260c7cb153": { - "balance": "340000000000000000000" - }, - "db4bc83b0e6baadb1156c5cf06e0f721808c52c7": { - "balance": "880000000000000000000" - }, - "a158148a2e0f3e92dc2ce38febc20107e3253c96": { - "balance": "2000000000000000000000" - }, - "9f6a322a6d469981426ae844865d7ee0bb15c7b3": { - "balance": "50003000000000000000" - }, - "32f29e8727a74c6b4301e3ffff0687c1b870dae9": { - "balance": "1000000000000000000000" - }, - "19918aa09e7d494e98ffa5db50350892f7156ac6": { - "balance": "10000000000000000000000" - }, - "5a5f8508da0ebebb90be9033bd4d9e274105ae00": { - "balance": "6685000000000000000000" - }, - "6fc25e7e00ca4f60a9fe6f28d1fde3542e2d1079": { - "balance": "792000000000000000000" - }, - "72094f3951ffc9771dced23ada080bcaf9c7cca7": { - "balance": "6000000000000000000000" - }, - "43f7e86e381ec51ec4906d1476cba97a3db584e4": { - "balance": "1000000000000000000000" - }, - "05696b73916bd3033e05521e3211dfec026e98e4": { - "balance": "2000000000000000000000" - }, - "5e7f70378775589fc66a81d3f653e954f55560eb": { - "balance": "2434000000000000000000" - }, - "895613236f3584216ad75c5d3e07e3fa6863a778": { - "balance": "2000000000000000000000" - }, - "4eb1454b573805c8aca37edec7149a41f61202f4": { - "balance": "300000000000000000000" - }, - "d99999a2490d9494a530cae4daf38554f4dd633e": { - "balance": "120000000000000000000" - }, - "1704cefcfb1331ec7a78388b29393e85c1af7916": { - "balance": "400000000000000000000" - }, - "ac4acfc36ed6094a27e118ecc911cd473e8fb91f": { - "balance": "1799800000000000000000" - }, - "a975b077fcb4cc8efcbf838459b6fa243a4159d6": { - "balance": "40000000000000000000" - }, - "9c405cf697956138065e11c5f7559e67245bd1a5": { - "balance": "200000000000000000000" - }, - "cafde855864c2598da3cafc05ad98df2898e8048": { - "balance": "14179272000000000000000" - }, - "8ef711e43a13918f1303e81d0ea78c9eefd67eb2": { - "balance": "4000000000000000000000" - }, - "0b14891999a65c9ef73308efe3100ca1b20e8192": { - "balance": "800000000000000000000" - }, - "47cf9cdaf92fc999cc5efbb7203c61e4f1cdd4c3": { - "balance": "131400000000000000000" - }, - "04ba8a3f03f08b895095994dda619edaacee3e7a": { - "balance": "2000000000000000000000" - }, - "02b6d65cb00b7b36e1fb5ed3632c4cb20a894130": { - "balance": "20000000000000000000000" - }, - "f99aee444b5783c093cfffd1c4632cf93c6f050c": { - "balance": "400000000000000000000" - }, - "2541314a0b408e95a694444977712a50713591ab": { - "balance": "1634706000000000000000" - }, - "3096dca34108085bcf04ae72b94574a13e1a3e1d": { - "balance": "200000000000000000000" - }, - "56df05bad46c3f00ae476ecf017bb8c877383ff1": { - "balance": "197248000000000000000" - }, - "6d59b21cd0e2748804d9abe064eac2bef0c95f27": { - "balance": "2000000000000000000000" - }, - "b29f5b7c1930d9f97a115e067066f0b54db44b3b": { - "balance": "1000000000000000000000" - }, - "888c16144933197cac26504dd76e06fd6600c789": { - "balance": "100000000000000000000" - }, - "dfe3c52a92c30396a4e33a50170dc900fcf8c9cf": { - "balance": "50000000000000000000" - }, - "f76f69cee4faa0a63b30ae1e7881f4f715657010": { - "balance": "200000000000000000000" - }, - "ee0007b0960d00908a94432a737557876aac7c31": { - "balance": "53053000000000000000" - }, - "effc15e487b1beda0a8d1325bdb4172240dc540a": { - "balance": "64940000000000000000" - }, - "40ab0a3e83d0c8ac9366910520eab1772bac3b1a": { - "balance": "976600000000000000000" - }, - "1895a0eb4a4372722fcbc5afe6936f289c88a419": { - "balance": "910000000000000000000" - }, - "81efe296ae76c860d1c5fbd33d47e8ce9996d157": { - "balance": "1000000000000000000000" - }, - "9ddd355e634ee9927e4b7f6c97e7bf3a2f1e687a": { - "balance": "50000000000000000000" - }, - "f2b4ab2c9427a9015ef6eefff5edb60139b719d1": { - "balance": "716800000000000000000" - }, - "765be2e12f629e6349b97d21b62a17b7c830edab": { - "balance": "6000000000000000000000" - }, - "ff61c9c1b7a3d8b53bba20b34466544b7b216644": { - "balance": "2000000000000000000000" - }, - "36a08fd6fd1ac17ce15ed57eefb12a2be28188bf": { - "balance": "1337000000000000000000" - }, - "17049311101d817efb1d65910f663662a699c98c": { - "balance": "1999800000000000000000" - }, - "30511832918d8034a7bee72ef2bfee440ecbbcf6": { - "balance": "16100000000000000000000" - }, - "d27c234ff7accace3d996708f8f9b04970f97d36": { - "balance": "1337000000000000000000" - }, - "a961171f5342b173dd70e7bfe5b5ca238b13bcdd": { - "balance": "3397053000000000000000" - }, - "30bf61b2d877fe10635126326fa189e4b0b1c3b0": { - "balance": "1027580000000000000000" - }, - "4bb6d86b8314c22d8d37ea516d0019f156aae12d": { - "balance": "1000000000000000000000" - }, - "5f363e0ab747e02d1b3b66abb69ea53c7baf523a": { - "balance": "11640000000000000000000" - }, - "283e11203749b1fa4f32febb71e49d135919382a": { - "balance": "1000000000000000000000" - }, - "ac5999a89d2dd286d5a80c6dee7e86aad40f9e12": { - "balance": "3880000000000000000000" - }, - "3f6dd3650ee428dcb7759553b017a96a94286ac9": { - "balance": "1337000000000000000000" - }, - "b3fc1d6881abfcb8becc0bb021b8b73b7233dd91": { - "balance": "50000000000000000000" - }, - "f0832a6bb25503eeca435be31b0bf905ca1fcf57": { - "balance": "6685000000000000000000" - }, - "9d7fda7070bf3ee9bbd9a41f55cad4854ae6c22c": { - "balance": "11027380000000000000000" - }, - "4b0bd8acfcbc53a6010b40d4d08ddd2d9d69622d": { - "balance": "668500000000000000000" - }, - "f3b668b3f14d920ebc379092db98031b67b219b3": { - "balance": "199955000000000000000" - }, - "d91d889164479ce436ece51763e22cda19b22d6b": { - "balance": "3365200000000000000000" - }, - "ffe28db53c9044b4ecd4053fd1b4b10d7056c688": { - "balance": "100000000000000000000" - }, - "c77b01a6e911fa988d01a3ab33646beef9c138f3": { - "balance": "721400000000000000000" - }, - "c0064f1d9474ab915d56906c9fb320a2c7098c9b": { - "balance": "358000000000000000000" - }, - "4e3edad4864dab64cae4c5417a76774053dc6432": { - "balance": "590943000000000000000" - }, - "71d2cc6d02578c65f73c575e76ce8fbcfadcf356": { - "balance": "72400000000000000000" - }, - "9971df60f0ae66dce9e8c84e17149f09f9c52f64": { - "balance": "200000000000000000000" - }, - "58e661d0ba73d6cf24099a5562b808f7b3673b68": { - "balance": "2000000000000000000000" - }, - "84b0ee6bb837d3a4c4c5011c3a228c0edab4634a": { - "balance": "20000000000000000000" - }, - "84375afbf59b3a1d61a1be32d075e0e15a4fbca5": { - "balance": "200000000000000000000" - }, - "9ae9476bfecd3591964dd325cf8c2a24faed82c1": { - "balance": "4000000000000000000000" - }, - "6a4c8907b600248057b1e46354b19bdc859c991a": { - "balance": "20000000000000000000" - }, - "1c045649cd53dc23541f8ed4d341812808d5dd9c": { - "balance": "7000000000000000000000" - }, - "c5e488cf2b5677933971f64cb8202dd05752a2c0": { - "balance": "1000000000000000000000" - }, - "eb25481fcd9c221f1ac7e5fd1ecd9307a16215b8": { - "balance": "197000000000000000000" - }, - "a61887818f914a20e31077290b83715a6b2d6ef9": { - "balance": "1880000000000000000000" - }, - "679437eacf437878dc293d48a39c87b7421a216c": { - "balance": "64528000000000000000" - }, - "331a1c26cc6994cdd3c14bece276ffff4b9df77c": { - "balance": "18049000000000000000" - }, - "75b95696e8ec4510d56868a7c1a735c68b244890": { - "balance": "6400000000000000000000" - }, - "a77f3ee19e9388bbbb2215c62397b96560132360": { - "balance": "200000000000000000000" - }, - "bc7afc8477412274fc265df13c054473427d43c6": { - "balance": "130034000000000000000" - }, - "91050a5cffadedb4bb6eaafbc9e5013428e96c80": { - "balance": "1700000000000000000000" - }, - "24586ec5451735eeaaeb470dc8736aae752f82e5": { - "balance": "17600000000000000000" - }, - "51039377eed0c573f986c5e8a95fb99a59e9330f": { - "balance": "1970000000000000000000" - }, - "fbb161fe875f09290a4b262bc60110848f0d2226": { - "balance": "2000000000000000000000" - }, - "ed52a2cc0869dc9e9f842bd0957c47a8e9b0c9ff": { - "balance": "9550000000000000000000" - }, - "bad235d5085dc7b068a67c412677b03e1836884c": { - "balance": "2000000000000000000000" - }, - "055eac4f1ad3f58f0bd024d68ea60dbe01c6afb3": { - "balance": "100000000000000000000" - }, - "4058808816fdaa3a5fc98ed47cfae6c18315422e": { - "balance": "199800000000000000000" - }, - "3540c7bd7a8442d5bee21a2180a1c4edff1649e0": { - "balance": "1239295000000000000000" - }, - "c5edbbd2ca0357654ad0ea4793f8c5cecd30e254": { - "balance": "6000000000000000000000" - }, - "b5906b0ae9a28158e8ac550e39da086ee3157623": { - "balance": "200000000000000000000" - }, - "4d801093c19ca9b8f342e33cc9c77bbd4c8312cf": { - "balance": "345005000000000000000" - }, - "206482ee6f138a778fe1ad62b180ce856fbb23e6": { - "balance": "2000000000000000000000" - }, - "c0ed0d4ad10de03435b153a0fc25de3b93f45204": { - "balance": "3160000000000000000000" - }, - "29e67990e1b6d52e1055ffe049c53195a81542cf": { - "balance": "20000000000000000000000" - }, - "e6d22209ffd0b87509ade3a8e2ef429879cb89b5": { - "balance": "17260000000000000000000" - }, - "d6644d40e90bc97fe7dfe7cabd3269fd579ba4b3": { - "balance": "159000000000000000000" - }, - "ece1290877b583e361a2d41b009346e6274e2538": { - "balance": "300000000000000000000" - }, - "ab3861226ffec1289187fb84a08ec3ed043264e8": { - "balance": "1000000000000000000000" - }, - "60e0bdd0a259bb9cb09d3f37e5cd8b9daceabf8a": { - "balance": "1370000000000000000000" - }, - "28b77585cb3d55a199ab291d3a18c68fe89a848a": { - "balance": "1960000000000000000000" - }, - "73128173489528012e76b41a5e28c68ba4e3a9d4": { - "balance": "1000000000000000000000" - }, - "018492488ba1a292342247b31855a55905fef269": { - "balance": "140000000000000000000" - }, - "0bb54c72fd6610bfa4363397e020384b022b0c49": { - "balance": "1337000000000000000000" - }, - "520f66a0e2657ff0ac4195f2f064cf2fa4b24250": { - "balance": "40000000000000000000" - }, - "a1432ed2c6b7777a88e8d46d388e70477f208ca5": { - "balance": "7999538000000000000000" - }, - "149ba10f0da2725dc704733e87f5a524ca88515e": { - "balance": "7880000000000000000000" - }, - "b287f7f8d8c3872c1b586bcd7d0aedbf7e732732": { - "balance": "20000000000000000000" - }, - "c46bbdef76d4ca60d316c07f5d1a780e3b165f7e": { - "balance": "2000000000000000000000" - }, - "b5a589dd9f4071dbb6fba89b3f5d5dae7d96c163": { - "balance": "2000000000000000000000" - }, - "d218efb4db981cdd6a797f4bd48c7c26293ceb40": { - "balance": "2975000000000000000000" - }, - "af87d2371ef378957fbd05ba2f1d66931b01e2b8": { - "balance": "700000000000000000000" - }, - "86ef6426211949cc37f4c75e7850369d0cf5f479": { - "balance": "13399196000000000000000" - }, - "fb3a0b0d6b6a718f6fc0292a825dc9247a90a5d0": { - "balance": "199950000000000000000" - }, - "da16dd5c3d1a2714358fe3752cae53dbab2be98c": { - "balance": "19400000000000000000000" - }, - "9eb7834e171d41e069a77947fca87622f0ba4e48": { - "balance": "100000000000000000000" - }, - "e1d91b0954cede221d6f24c7985fc59965fb98b8": { - "balance": "2000000000000000000000" - }, - "85d0d88754ac84b8b21ba93dd2bfec72626faba8": { - "balance": "1000000000000000000000" - }, - "695b4cce085856d9e1f9ff3e79942023359e5fbc": { - "balance": "5000000000000000000000" - }, - "9156d18029350e470408f15f1aa3be9f040a67c6": { - "balance": "1000000000000000000000" - }, - "a9d64b4f3bb7850722b58b478ba691375e224e42": { - "balance": "6000000000000000000000" - }, - "17e4a0e52bac3ee44efe0954e753d4b85d644e05": { - "balance": "2000000000000000000000" - }, - "b8a79c84945e47a9c3438683d6b5842cff7684b1": { - "balance": "2000000000000000000000" - }, - "cfac2e1bf33205b05533691a02267ee19cd81836": { - "balance": "1000000000000000000000" - }, - "6b992521ec852370848ad697cc2df64e63cc06ff": { - "balance": "1000000000000000000000" - }, - "60af0ee118443c9b37d2fead77f5e521debe1573": { - "balance": "1910000000000000000000" - }, - "c6dbdb9efd5ec1b3786e0671eb2279b253f215ed": { - "balance": "1000000000000000000000" - }, - "659c0a72c767a3a65ced0e1ca885a4c51fd9b779": { - "balance": "2000000000000000000000" - }, - "ed1276513b6fc68628a74185c2e20cbbca7817bf": { - "balance": "191000000000000000000" - }, - "5ad12c5ed4fa827e2150cfa0d68c0aa37b1769b8": { - "balance": "800000000000000000000" - }, - "17c0fef6986cfb2e4041f9979d9940b69dff3de2": { - "balance": "4000000000000000000000" - }, - "ca98c7988efa08e925ef9c9945520326e9f43b99": { - "balance": "4000000000000000000000" - }, - "fe8f1fdcab7fbec9a6a3fcc507619600505c36a3": { - "balance": "19700000000000000000" - }, - "4420aa35465be617ad2498f370de0a3cc4d230af": { - "balance": "2000000000000000000000" - }, - "8232d1f9742edf8dd927da353b2ae7b4cbce7592": { - "balance": "668500000000000000000" - }, - "eca5f58792b8c62d2af556717ee3ee3028be4dce": { - "balance": "2000000000000000000000" - }, - "6bf86f1e2f2b8032a95c4d7738a109d3d0ed8104": { - "balance": "1820000000000000000000" - }, - "3ac2f0ff1612e4a1c346d53382abf6d8a25baa53": { - "balance": "2000000000000000000000" - }, - "daa1bd7a9148fb865cd612dd35f162861d0f3bdc": { - "balance": "3066243000000000000000" - }, - "5169c60aee4ceed1849ab36d664cff97061e8ea8": { - "balance": "3000000000000000000000" - }, - "2a5e3a40d2cd0325766de73a3d671896b362c73b": { - "balance": "100000000000000000000000" - }, - "a83382b6e15267974a8550b98f7176c1a353f9be": { - "balance": "3541608000000000000000" - }, - "b50c149a1906fad2786ffb135aab501737e9e56f": { - "balance": "388000000000000000000" - }, - "d9775965b716476675a8d513eb14bbf7b07cd14a": { - "balance": "5076200000000000000000" - }, - "66662006015c1f8e3ccfcaebc8ee6807ee196303": { - "balance": "500024000000000000000" - }, - "78746a958dced4c764f876508c414a68342cecb9": { - "balance": "50600000000000000000" - }, - "e982e6f28c548f5f96f45e63f7ab708724f53fa1": { - "balance": "396238000000000000000" - }, - "740bfd52e01667a3419b029a1b8e45576a86a2db": { - "balance": "16800000000000000000000" - }, - "2bd252e0d732ff1d7c78f0a02e6cb25423cf1b1a": { - "balance": "2674000000000000000000" - }, - "2e2d7ea66b9f47d8cc52c01c52b6e191bc7d4786": { - "balance": "3999800000000000000000" - }, - "3e3161f1ea2fbf126e79da1801da9512b37988c9": { - "balance": "49250000000000000000000" - }, - "7e2ba86da52e785d8625334f3397ba1c4bf2e8d1": { - "balance": "197000000000000000000" - }, - "7608f437b31f18bc0b64d381ae86fd978ed7b31f": { - "balance": "50000000000000000000" - }, - "25a5a44d38a2f44c6a9db9cdbc6b1e2e97abb509": { - "balance": "17000000000000000000000" - }, - "745ad3abc6eeeb2471689b539e789ce2b8268306": { - "balance": "1129977000000000000000" - }, - "09e437d448861228a232b62ee8d37965a904ed9c": { - "balance": "21708305000000000000000" - }, - "be53322f43fbb58494d7cce19dda272b2450e827": { - "balance": "200018000000000000000" - }, - "4166fc08ca85f766fde831460e9dc93c0e21aa6c": { - "balance": "1000000000000000000000" - }, - "99c0174cf84e0783c220b4eb6ae18fe703854ad3": { - "balance": "2074800000000000000000" - }, - "3cf484524fbdfadae26dc185e32b2b630fd2e726": { - "balance": "448798000000000000000" - }, - "fdcd5d80b105897a57abc47865768b2900524295": { - "balance": "6400000000000000000000" - }, - "f22f4078febbbaa8b0e78e642c8a42f35d433905": { - "balance": "1999944000000000000000" - }, - "eac768bf14b8f9432e69eaa82a99fbeb94cd0c9c": { - "balance": "98500000000000000000000" - }, - "2639eee9873ceec26fcc9454b548b9e7c54aa65c": { - "balance": "1000000000000000000000" - }, - "c3c3c2510d678020485a63735d1307ec4ca6302b": { - "balance": "1000000000000000000000" - }, - "b73d6a77559c86cf6574242903394bacf96e3570": { - "balance": "91200000000000000000" - }, - "5ce2e7ceaaa18af0f8aafa7fbad74cc89e3cd436": { - "balance": "20000000000000000000000" - }, - "03377c0e556b640103289a6189e1aeae63493467": { - "balance": "20000000000000000000000" - }, - "6eb0a5a9ae96d22cf01d8fd6483b9f38f08c2c8b": { - "balance": "4000000000000000000000" - }, - "fc8215a0a69913f62a43bf1c8590b9ddcd0d8ddb": { - "balance": "2000000000000000000000" - }, - "4a835c25824c47ecbfc79439bf3f5c3481aa75cd": { - "balance": "1400000000000000000000" - }, - "b5493ef173724445cf345c035d279ba759f28d51": { - "balance": "20000000000000000000" - }, - "b9e90c1192b3d5d3e3ab0700f1bf655f5dd4347a": { - "balance": "499928000000000000000" - }, - "419bde7316cc1ed295c885ace342c79bf7ee33ea": { - "balance": "6000000000000000000000" - }, - "e4625501f52b7af52b19ed612e9d54fdd006b492": { - "balance": "209440000000000000000" - }, - "e9d599456b2543e6db80ea9b210e908026e2146e": { - "balance": "200000000000000000000" - }, - "2c06dd922b61514aafedd84488c0c28e6dcf0e99": { - "balance": "100000000000000000000000" - }, - "06b5ede6fdf1d6e9a34721379aeaa17c713dd82a": { - "balance": "2000000000000000000000" - }, - "d8930a39c77357c30ad3a060f00b06046331fd62": { - "balance": "820000000000000000000" - }, - "b2a2c2111612fb8bbb8e7dd9378d67f1a384f050": { - "balance": "20000000000000000000" - }, - "1f174f40a0447234e66653914d75bc003e5690dc": { - "balance": "160000000000000000000" - }, - "e06cb6294704eea7437c2fc3d30773b7bf38889a": { - "balance": "20094000000000000000" - }, - "cd06f8c1b5cdbd28e2d96b6346c3e85a0483ba24": { - "balance": "1000000000000000000000" - }, - "f316ef1df2ff4d6c1808dba663ec8093697968e0": { - "balance": "1794400000000000000000" - }, - "1e6915ebd9a19c81b692ad99b1218a592c1ac7b1": { - "balance": "4000000000000000000000" - }, - "885493bda36a0432976546c1ddce71c3f4570021": { - "balance": "216700000000000000000" - }, - "18b0407cdad4ce52600623bd5e1f6a81ab61f026": { - "balance": "319489000000000000000" - }, - "187d9f0c07f8eb74faaad15ebc7b80447417f782": { - "balance": "20000000000000000000" - }, - "5d6ccf806738091042ad97a6e095fe8c36aa79c5": { - "balance": "188000000000000000000" - }, - "53437fecf34ab9d435f4deb8ca181519e2592035": { - "balance": "188000000000000000000" - }, - "fd1faa347b0fcc804c2da86c36d5f1d18b7087bb": { - "balance": "52380000000000000000" - }, - "650cf67db060cce17568d5f2a423687c49647609": { - "balance": "100000000000000000000" - }, - "bcd95ef962462b6edfa10fda87d72242fe3edb5c": { - "balance": "334133000000000000000" - }, - "3b5e8b3c77f792decb7a8985df916efb490aac23": { - "balance": "2000000000000000000000" - }, - "f13b083093ba564e2dc631568cf7540d9a0ec719": { - "balance": "1999944000000000000000" - }, - "373c547e0cb5ce632e1c5ad66155720c01c40995": { - "balance": "4691588000000000000000" - }, - "7313461208455455465445a459b06c3773b0eb30": { - "balance": "2000000000000000000000" - }, - "441f37e8a029fd02482f289c49b5d06d00e408a4": { - "balance": "333333000000000000000" - }, - "d30d4c43adcf55b2cb53d68323264134498d89ce": { - "balance": "1000000000000000000000" - }, - "f648ea89c27525710172944e79edff847803b775": { - "balance": "100000000000000000000000" - }, - "0c7f869f8e90d53fdc03e8b2819b016b9d18eb26": { - "balance": "20000000000000000000000" - }, - "c71f92a3a54a7b8c2f5ea44305fccb84eee23148": { - "balance": "49980000000000000000" - }, - "7988901331e387f713faceb9005cb9b65136eb14": { - "balance": "1970000000000000000000" - }, - "e9a39a8bac0f01c349c64cedb69897f633234ed2": { - "balance": "3980000000000000000000" - }, - "ad2a5c00f923aaf21ab9f3fb066efa0a03de2fb2": { - "balance": "999996000000000000000" - }, - "f25259a5c939cd25966c9b6303d3731c53ddbc4c": { - "balance": "200000000000000000000" - }, - "d1682c2159018dc3d07f08240a8c606daf65f8e1": { - "balance": "200000000000000000000000" - }, - "a99991cebd98d9c838c25f7a7416d9e244ca250d": { - "balance": "1000000000000000000000" - }, - "5a285755391e914e58025faa48cc685f4fd4f5b8": { - "balance": "26000000000000000000000" - }, - "4d24b7ac47d2f27de90974ba3de5ead203544bcd": { - "balance": "100000000000000000000" - }, - "21b182f2da2b384493cf5f35f83d9d1ee14f2a21": { - "balance": "2000000000000000000000" - }, - "31ab088966ecc7229258f6098fce68cf39b38485": { - "balance": "1000000000000000000000" - }, - "4977a7939d0939689455ce2639d0ee5a4cd910ed": { - "balance": "1820000000000000000000" - }, - "07af938c1237a27c9030094dcf240750246e3d2c": { - "balance": "500000000000000000000" - }, - "4e2bfa4a466f82671b800eee426ad00c071ba170": { - "balance": "4000000000000000000000" - }, - "107379d4c467464f235bc18e55938aad3e688ad7": { - "balance": "50000000000000000000" - }, - "f7b29b82195c882dab7897c2ae95e77710f57875": { - "balance": "2199000000000000000000" - }, - "56586391040c57eec6f5affd8cd4abde10b50acc": { - "balance": "4000000000000000000000" - }, - "ac608e2bac9dd20728d2947effbbbf900a9ce94b": { - "balance": "6000600000000000000000" - }, - "48548b4ba62bcb2f0d34a88dc69a680e539cf046": { - "balance": "100084000000000000000" - }, - "1665ab1739d71119ee6132abbd926a279fe67948": { - "balance": "100000000000000000000" - }, - "af4493e8521ca89d95f5267c1ab63f9f45411e1b": { - "balance": "200000000000000000000" - }, - "bf6925c00751008440a6739a02bf2b6cdaab5e3a": { - "balance": "1000000000000000000000" - }, - "3fe40fbd919aad2818df01ee4df46c46842ac539": { - "balance": "6000000000000000000000" - }, - "455b9296921a74d1fc41617f43b8303e6f3ed76c": { - "balance": "4200000000000000000000" - }, - "7086b4bde3e35d4aeb24b825f1a215f99d85f745": { - "balance": "1999800000000000000000" - }, - "d4ee4919fb37f2bb970c3fff54aaf1f3dda6c03f": { - "balance": "40000000000000000000000" - }, - "a4489a50ead5d5445a7bee4d2d5536c2a76c41f8": { - "balance": "200000000000000000000" - }, - "505e4f7c275588c533a20ebd2ac13b409bbdea3c": { - "balance": "17600000000000000000" - }, - "3bb53598cc20e2055dc553b049404ac9b7dd1e83": { - "balance": "615020000000000000000" - }, - "52cd20403ba7eda6bc307a3d63b5911b817c1263": { - "balance": "20000000000000000000" - }, - "a211da03cc0e31ecce5309998718515528a090df": { - "balance": "200000000000000000000" - }, - "bcb422dc4dd2aae94abae95ea45dd1731bb6b0ba": { - "balance": "447500000000000000000" - }, - "cbde9734b8e6aa538c291d6d7facedb0f338f857": { - "balance": "2000000000000000000000" - }, - "171ca02a8b6d62bf4ca47e906914079861972cb2": { - "balance": "200000000000000000000" - }, - "d40d0055fd9a38488aff923fd03d35ec46d711b3": { - "balance": "4999711000000000000000" - }, - "3887192c7f705006b630091276b39ac680448d6b": { - "balance": "60000000000000000000" - }, - "3f3c8e61e5604cef0605d436dd22accd862217fc": { - "balance": "1337000000000000000000" - }, - "4258fd662fc4ce3295f0d4ed8f7bb1449600a0a9": { - "balance": "6719600000000000000000" - }, - "4571de672b9904bad8743692c21c4fdcea4c2e01": { - "balance": "4000000000000000000000" - }, - "5be045512a026e3f1cebfd5a7ec0cfc36f2dc16b": { - "balance": "120000000000000000000" - }, - "d6300b3215b11de762ecde4b70b7927d01291582": { - "balance": "2000000000000000000000" - }, - "f9e37447406c412197b2e2aebc001d6e30c98c60": { - "balance": "8346700000000000000000" - }, - "bd047ff1e69cc6b29ad26497a9a6f27a903fc4dd": { - "balance": "865000000000000000000" - }, - "23fa7eb51a48229598f97e762be0869652dffc66": { - "balance": "1000000000000000000000" - }, - "6679aeecd87a57a73f3356811d2cf49d0c4d96dc": { - "balance": "600000000000000000000" - }, - "23c55aeb5739876f0ac8d7ebea13be729685f000": { - "balance": "1337000000000000000000" - }, - "757b65876dbf29bf911d4f0692a2c9beb1139808": { - "balance": "4124263000000000000000" - }, - "e8fc36b0131ec120ac9e85afc10ce70b56d8b6ba": { - "balance": "200000000000000000000" - }, - "1a89899cbebdbb64bb26a195a63c08491fcd9eee": { - "balance": "2000000000000000000000" - }, - "6edf7f5283725c953ee64317f66188af1184b033": { - "balance": "8050000000000000000000" - }, - "297385e88634465685c231a314a0d5dcd146af01": { - "balance": "1550000000000000000000" - }, - "018f20a27b27ec441af723fd9099f2cbb79d6263": { - "balance": "2167000000000000000000" - }, - "a5a4227f6cf98825c0d5baff5315752ccc1a1391": { - "balance": "10000000000000000000000" - }, - "69517083e303d4fbb6c2114514215d69bc46a299": { - "balance": "100000000000000000000" - }, - "1dab172effa6fbee534c94b17e794edac54f55f8": { - "balance": "1970000000000000000000" - }, - "c6ee35934229693529dc41d9bb71a2496658b88e": { - "balance": "19700000000000000000000" - }, - "a8ee1df5d44b128469e913569ef6ac81eeda4fc8": { - "balance": "500000000000000000000" - }, - "35bd246865fab490ac087ac1f1d4f2c10d0cda03": { - "balance": "400000000000000000000" - }, - "4bf8bf1d35a231315764fc8001809a949294fc49": { - "balance": "66850000000000000000" - }, - "c70fa45576bf9c865f983893002c414926f61029": { - "balance": "400400000000000000000" - }, - "fdeaac2acf1d138e19f2fc3f9fb74592e3ed818a": { - "balance": "668500000000000000000" - }, - "bfbfbcb656c2992be8fcde8219fbc54aadd59f29": { - "balance": "9999924000000000000000" - }, - "1722c4cbe70a94b6559d425084caeed4d6e66e21": { - "balance": "4000000000000000000000" - }, - "00e681bc2d10db62de85848324492250348e90bf": { - "balance": "20000000000000000000000" - }, - "5c308bac4857d33baea074f3956d3621d9fa28e1": { - "balance": "4999711000000000000000" - }, - "68c08490c89bf0d6b6f320b1aca95c8312c00608": { - "balance": "4000000000000000000000" - }, - "ce1884ddbbb8e10e4dba6e44feeec2a7e5f92f05": { - "balance": "4000000000000000000000" - }, - "427417bd16b1b3d22dbb902d8f9657016f24a61c": { - "balance": "2000000000000000000000" - }, - "5ff93de6ee054cad459b2d5eb0f6870389dfcb74": { - "balance": "220000000000000000000" - }, - "71946b7117fc915ed107385f42d99ddac63249c2": { - "balance": "2000000000000000000000" - }, - "11ec00f849b6319cf51aa8dd8f66b35529c0be77": { - "balance": "2000000000000000000000" - }, - "610fd6ee4eebab10a8c55d0b4bd2e7d6ef817156": { - "balance": "20002000000000000000" - }, - "a422e4bf0bf74147cc895bed8f16d3cef3426154": { - "balance": "349281000000000000000" - }, - "745aecbaf9bb39b74a67ea1ce623de368481baa6": { - "balance": "10000000000000000000000" - }, - "9f496cb2069563144d0811677ba0e4713a0a4143": { - "balance": "1122000000000000000000" - }, - "c500b720734ed22938d78c5e48b2ba9367a575ba": { - "balance": "33400000000000000000000" - }, - "cd072e6e1833137995196d7bb1725fef8761f655": { - "balance": "6000000000000000000000" - }, - "94644ad116a41ce2ca7fbec609bdef738a2ac7c7": { - "balance": "5000000000000000000000" - }, - "e8d942d82f175ecb1c16a405b10143b3f46b963a": { - "balance": "568600000000000000000" - }, - "f73dd9c142b71bce11d06e30e7e7d032f2ec9c9e": { - "balance": "1970000000000000000000" - }, - "1327d759d56e0ab87af37ecf63fe01f310be100a": { - "balance": "659200000000000000000" - }, - "28fa2580f9ebe420f3e5eefdd371638e3b7af499": { - "balance": "6000000000000000000000" - }, - "024bdd2c7bfd500ee7404f7fb3e9fb31dd20fbd1": { - "balance": "180000000000000000000" - }, - "b4b14bf45455d0ab0803358b7524a72be1a2045b": { - "balance": "500000000000000000000" - }, - "b1e2dd95e39ae9775c55aeb13f12c2fa233053ba": { - "balance": "2000000000000000000000" - }, - "35b03ea4245736f57b85d2eb79628f036ddcd705": { - "balance": "4000000000000000000000" - }, - "eb2ef3d38fe652403cd4c9d85ed7f0682cd7c2de": { - "balance": "42784000000000000000000" - }, - "690594d306613cd3e2fd24bca9994ad98a3d73f8": { - "balance": "2000000000000000000000" - }, - "8397a1bc47acd647418159b99cea57e1e6532d6e": { - "balance": "9169160000000000000000" - }, - "b44815a0f28e569d0e921a4ade8fb2642526497a": { - "balance": "55500000000000000000" - }, - "e24109be2f513d87498e926a286499754f9ed49e": { - "balance": "886500000000000000000" - }, - "37ac29bda93f497bc4aeaab935452c431510341e": { - "balance": "985000000000000000000" - }, - "4a81abe4984c7c6bef63d69820e55743c61f201c": { - "balance": "16011846000000000000000" - }, - "66dcc5fb4ee7fee046e141819aa968799d644491": { - "balance": "1337000000000000000000" - }, - "43ff38743ed0cd43308c066509cc8e7e72c862aa": { - "balance": "1940000000000000000000" - }, - "b8f20005b61352ffa7699a1b52f01f5ab39167f1": { - "balance": "10000000000000000000000" - }, - "1cda411bd5163baeca1e558563601ce720e24ee1": { - "balance": "18200000000000000000" - }, - "86245f596691093ece3f3d3ca2263eace81941d9": { - "balance": "188000000000000000000" - }, - "f52a5882e8927d944b359b26366ba2b9cacfbae8": { - "balance": "25000080000000000000000" - }, - "118c18b2dce170e8f445753ba5d7513cb7636d2d": { - "balance": "8800000000000000000000" - }, - "7168b3bb8c167321d9bdb023a6e9fd11afc9afd9": { - "balance": "1790000000000000000000" - }, - "d9103bb6b67a55a7fece2d1af62d457c2178946d": { - "balance": "1000000000000000000000" - }, - "8b9fda7d981fe9d64287f85c94d83f9074849fcc": { - "balance": "14000000000000000000000" - }, - "91211712719f2b084d3b3875a85069f466363141": { - "balance": "1000000000000000000000" - }, - "4863849739265a63b0a2bf236a5913e6f959ce15": { - "balance": "1520000000000000000000" - }, - "c2d1778ef6ee5fe488c145f3586b6ebbe3fbb445": { - "balance": "1146000000000000000000" - }, - "2b77a4d88c0d56a3dbe3bae04a05f4fcd1b757e1": { - "balance": "300000000000000000000" - }, - "fe9c0fffefb803081256c0cf4d6659e6d33eb4fb": { - "balance": "1528000000000000000000" - }, - "893017ff1adad499aa065401b4236ce6e92b625a": { - "balance": "1999944000000000000000" - }, - "073c67e09b5c713c5221c8a0c7f3f74466c347b0": { - "balance": "19400000000000000000000" - }, - "93e303411afaf6c107a44101c9ac5b36e9d6538b": { - "balance": "66000000000000000000000" - }, - "0ec50aa823f465b9464b0bc0c4a57724a555f5d6": { - "balance": "59100000000000000000000" - }, - "a3e3a6ea509573e21bd0239ece0523a7b7d89b2f": { - "balance": "1970000000000000000000" - }, - "c069ef0eb34299abd2e32dabc47944b272334824": { - "balance": "120000000000000000000" - }, - "28a3da09a8194819ae199f2e6d9d1304817e28a5": { - "balance": "2000000000000000000000" - }, - "e9495ba5842728c0ed97be37d0e422b98d69202c": { - "balance": "2000000000000000000000" - }, - "bba976f1a1215f7512871892d45f7048acd356c8": { - "balance": "2000000000000000000000" - }, - "887cac41cd706f3345f2d34ac34e01752a6e5909": { - "balance": "595366000000000000000" - }, - "e0e0b2e29dde73af75987ee4446c829a189c95bc": { - "balance": "149000000000000000000" - }, - "4a5fae3b0372c230c125d6d470140337ab915656": { - "balance": "1600000000000000000000" - }, - "425177eb74ad0a9d9a5752228147ee6d6356a6e6": { - "balance": "13370000000000000000" - }, - "5db7bba1f9573f24115d8c8c62e9ce8895068e9f": { - "balance": "49984000000000000000" - }, - "fa6a37f018e97967937fc5e8617ba1d786dd5f77": { - "balance": "19999800000000000000000" - }, - "45e3a93e72144ada860cbc56ff85145ada38c6da": { - "balance": "1610000000000000000000" - }, - "67da922effa472a6b124e84ea8f86b24e0f515aa": { - "balance": "20000000000000000000" - }, - "aa9bd4589535db27fa2bc903ca17d679dd654806": { - "balance": "2000000000000000000000" - }, - "16a9e9b73ae98b864d1728798b8766dbc6ea8d12": { - "balance": "957480000000000000000" - }, - "d6580ab5ed4c7dfa506fa6fe64ad5ce129707732": { - "balance": "4000000000000000000000" - }, - "984a7985e3cc7eb5c93691f6f8cc7b8f245d01b2": { - "balance": "6000000000000000000000" - }, - "7746b6c6699c8f34ca2768a820f1ffa4c207fe05": { - "balance": "4000086000000000000000" - }, - "2fa491fb5920a6574ebd289f39c1b2430d2d9a6a": { - "balance": "2000000000000000000000" - }, - "fae76719d97eac41870428e940279d97dd57b2f6": { - "balance": "98500000000000000000000" - }, - "41b2dbd79dda9b864f6a7030275419c39d3efd3b": { - "balance": "3200000000000000000000" - }, - "dd8254121a6e942fc90828f2431f511dad7f32e6": { - "balance": "3018000000000000000000" - }, - "37fac1e6bc122e936dfb84de0c4bef6e0d60c2d7": { - "balance": "2000000000000000000000" - }, - "3a10888b7e149cae272c01302c327d0af01a0b24": { - "balance": "17000000000000000000" - }, - "401354a297952fa972ad383ca07a0a2811d74a71": { - "balance": "14000000000000000000" - }, - "51865db148881951f51251710e82b9be0d7eadb2": { - "balance": "2000000000000000000000" - }, - "bbbd6ecbb5752891b4ceb3cce73a8f477059376f": { - "balance": "36000000000000000000" - }, - "3f236108eec72289bac3a65cd283f95e041d144c": { - "balance": "999925000000000000000" - }, - "dc83b6fd0d512131204707eaf72ea0c8c9bef976": { - "balance": "2000000000000000000000" - }, - "036eeff5ba90a6879a14dff4c5043b18ca0460c9": { - "balance": "100000000000000000000" - }, - "fac5ca94758078fbfccd19db3558da7ee8a0a768": { - "balance": "1017500000000000000000" - }, - "d0d62c47ea60fb90a3639209bbfdd4d933991cc6": { - "balance": "194000000000000000000" - }, - "891cb8238c88e93a1bcf61db49bd82b47a7f4f84": { - "balance": "2680000000000000000000" - }, - "df53003346d65c5e7a646bc034f2b7d32fcbe56a": { - "balance": "2000000000000000000000" - }, - "6e89c51ea6de13e06cdc748b67c4410fe9bcab03": { - "balance": "4000000000000000000000" - }, - "a61cdbadf04b1e54c883de6005fcdf16beb8eb2f": { - "balance": "2000000000000000000000" - }, - "e3951de5aefaf0458768d774c254f7157735e505": { - "balance": "1600930000000000000000" - }, - "f2732cf2c13b8bb8e7492a988f5f89e38273ddc8": { - "balance": "600000000000000000000" - }, - "4752218e54de423f86c0501933917aea08c8fed5": { - "balance": "20000000000000000000000" - }, - "152f4e860ef3ee806a502777a1b8dbc91a907668": { - "balance": "600000000000000000000" - }, - "15b96f30c23b8664e7490651066b00c4391fbf84": { - "balance": "410650000000000000000" - }, - "8693e9b8be94425eef7969bc69f9d42f7cad671e": { - "balance": "1000090000000000000000" - }, - "f41557dfdfb1a1bdcefefe2eba1e21fe0a4a9942": { - "balance": "1970000000000000000000" - }, - "38458e0685573cb4d28f53098829904570179266": { - "balance": "40000000000000000000" - }, - "53e4d9696dcb3f4d7b3f70dcaa4eecb71782ff5c": { - "balance": "200000000000000000000" - }, - "2dca0e449ab646dbdfd393a96662960bcab5ae1e": { - "balance": "40000000000000000000000" - }, - "87d7ac0653ccc67aa9c3469eef4352193f7dbb86": { - "balance": "200000000000000000000000" - }, - "ae9f5c3fbbe0c9bcbf1af8ff74ea280b3a5d8b08": { - "balance": "1730000000000000000000" - }, - "7751f363a0a7fd0533190809ddaf9340d8d11291": { - "balance": "20000000000000000000" - }, - "708a2af425ceb01e87ffc1be54c0f532b20eacd6": { - "balance": "134159000000000000000" - }, - "ac122a03cd058c122e5fe17b872f4877f9df9572": { - "balance": "1969606000000000000000" - }, - "5da4ca88935c27f55c311048840e589e04a8a049": { - "balance": "80000000000000000000" - }, - "e67c2c1665c88338688187629f49e99b60b2d3ba": { - "balance": "200000000000000000000" - }, - "dec82373ade8ebcf2acb6f8bc2414dd7abb70d77": { - "balance": "200000000000000000000" - }, - "47c247f53b9fbeb17bba0703a00c009fdb0f6eae": { - "balance": "20000000000000000000000" - }, - "9a522e52c195bfb7cf5ffaaedb91a3ba7468161d": { - "balance": "1000000000000000000000" - }, - "3159e90c48a915904adfe292b22fa5fd5e72796b": { - "balance": "1008800000000000000000" - }, - "defddfd59b8d2c154eecf5c7c167bf0ba2905d3e": { - "balance": "93588000000000000000" - }, - "ad1d68a038fd2586067ef6d135d9628e79c2c924": { - "balance": "4686168000000000000000" - }, - "038e45eadd3d88b87fe4dab066680522f0dfc8f9": { - "balance": "10000000000000000000000" - }, - "2561ec0f379218fe5ed4e028a3f744aa41754c72": { - "balance": "13370000000000000000" - }, - "b95396daaa490df2569324fcc6623be052f132ca": { - "balance": "2000000000000000000000" - }, - "2376ada90333b1d181084c97e645e810aa5b76f1": { - "balance": "750000000000000000000" - }, - "07800d2f8068e448c79a4f69b1f15ef682aae5f6": { - "balance": "19400000000000000000000" - }, - "adeb204aa0c38e179e81a94ed8b3e7d53047c26b": { - "balance": "608000000000000000000" - }, - "0dc100b107011c7fc0a1339612a16ccec3285208": { - "balance": "2000000000000000000000" - }, - "f0b1340b996f6f0bf0d9561c849caf7f4430befa": { - "balance": "100000000000000000000" - }, - "e1443dbd95cc41237f613a48456988a04f683282": { - "balance": "4000086000000000000000" - }, - "d3c6f1e0f50ec3d2a67e6bcd193ec7ae38f1657f": { - "balance": "6618150000000000000000" - }, - "b68899e7610d4c93a23535bcc448945ba1666f1c": { - "balance": "200000000000000000000" - }, - "a7253763cf4a75df92ca1e766dc4ee8a2745147b": { - "balance": "10740000000000000000000" - }, - "75d67ce14e8d29e8c2ffe381917b930b1aff1a87": { - "balance": "3000000000000000000000" - }, - "493d48bda015a9bfcf1603936eab68024ce551e0": { - "balance": "22528000000000000000" - }, - "7ddd57165c87a2707f025dcfc2508c09834759bc": { - "balance": "1400000000000000000000" - }, - "cff7f89a4d4219a38295251331568210ffc1c134": { - "balance": "1760000000000000000000" - }, - "168d30e53fa681092b52e9bae15a0dcb41a8c9bb": { - "balance": "100000000000000000000" - }, - "99b743d1d9eff90d9a1934b4db21d519d89b4a38": { - "balance": "100000000000000000000" - }, - "a3d0b03cffbb269f796ac29d80bfb07dc7c6ad06": { - "balance": "2000000000000000000000" - }, - "816d9772cf11399116cc1e72c26c6774c9edd739": { - "balance": "200000000000000000000" - }, - "a880e2a8bf88a1a82648b4013c49c4594c433cc8": { - "balance": "4728000000000000000000" - }, - "2a44a7218fe44d65a1b4b7a7d9b1c2c52c8c3e34": { - "balance": "62221355000000000000000" - }, - "cb86edbc8bbb1f9131022be649565ebdb09e32a1": { - "balance": "2000000000000000000000" - }, - "3915eab5ab2e5977d075dec47d96b68b4b5cf515": { - "balance": "61520000000000000000000" - }, - "8165cab0eafb5a328fc41ac64dae715b2eef2c65": { - "balance": "1000000000000000000000" - }, - "416c86b72083d1f8907d84efd2d2d783dffa3efb": { - "balance": "1999944000000000000000" - }, - "c524086d46c8112b128b2faf6f7c7d8160a8386c": { - "balance": "400000000000000000000" - }, - "902d74a157f7d2b9a3378b1f56703730e03a1719": { - "balance": "4000000000000000000000" - }, - "74ef2869cbe608856045d8c2041118579f2236ea": { - "balance": "59724000000000000000" - }, - "af992dd669c0883e5515d3f3112a13f617a4c367": { - "balance": "2000000000000000000000" - }, - "4c6a248fc97d705def495ca20759169ef0d36471": { - "balance": "760000000000000000000" - }, - "974d2f17895f2902049deaaecf09c3046507402d": { - "balance": "14707000000000000000" - }, - "0239b4f21f8e05cd01512b2be7a0e18a6d974607": { - "balance": "1000000000000000000000" - }, - "b97a6733cd5fe99864b3b33460d1672434d5cafd": { - "balance": "1999579000000000000000" - }, - "f558a2b2dd26dd9593aae04531fd3c3cc3854b67": { - "balance": "198000000000000000000" - }, - "b577b6befa054e9c040461855094b002d7f57bd7": { - "balance": "114000000000000000000000" - }, - "73bfe7710f31cab949b7a2604fbf5239cee79015": { - "balance": "2000000000000000000000" - }, - "5717f2d8f18ffcc0e5fe247d3a4219037c3a649c": { - "balance": "3998000000000000000000" - }, - "20707e425d2a11d2c89f391b2b809f556c592421": { - "balance": "2000000000000000000000" - }, - "9a6708ddb8903c289f83fe889c1edcd61f854423": { - "balance": "1000000000000000000000" - }, - "fa27cc49d00b6c987336a875ae39da58fb041b2e": { - "balance": "10000000000000000000000" - }, - "d688e785c98f00f84b3aa1533355c7a258e87948": { - "balance": "500000000000000000000" - }, - "927cb7dc187036b5427bc7e200c5ec450c1d27d4": { - "balance": "216000000000000000000" - }, - "b2bfaa58b5196c5cb7f89de15f479d1838de713d": { - "balance": "21000000000000000000" - }, - "e180de9e86f57bafacd7904f9826b6b4b26337a3": { - "balance": "830400000000000000000" - }, - "a1204dad5f560728a35c0d8fc79481057bf77386": { - "balance": "1000000000000000000000" - }, - "6b0da25af267d7836c226bcae8d872d2ce52c941": { - "balance": "6000000000000000000000" - }, - "0517448dada761cc5ba4033ee881c83037036400": { - "balance": "1998000000000000000000" - }, - "7ed0a5a847bef9a9da7cba1d6411f5c316312619": { - "balance": "39842000000000000000" - }, - "5b5d517029321562111b43086d0b043591109a70": { - "balance": "2600000000000000000000" - }, - "56fc1a7bad4047237ce116146296238e078f93ad": { - "balance": "178000000000000000000" - }, - "6c5422fb4b14e6d98b6091fdec71f1f08640419d": { - "balance": "400000000000000000000" - }, - "108fe8ee2a13da487b22c6ab6d582ea71064d98c": { - "balance": "399800000000000000000" - }, - "0ad3e44d3c001fa290b393617030544108ac6eb9": { - "balance": "1969019000000000000000" - }, - "25aee68d09afb71d8817f3f184ec562f7897b734": { - "balance": "2000000000000000000000" - }, - "c2340a4ca94c9678b7494c3c852528ede5ee529f": { - "balance": "48669000000000000000" - }, - "44901e0d0e08ac3d5e95b8ec9d5e0ff5f12e0393": { - "balance": "417500000000000000000" - }, - "8775a610c502b9f1e6ad4cdadb8ce29bff75f6e4": { - "balance": "600000000000000000000" - }, - "682897bc4f8e89029120fcffb787c01a93e64184": { - "balance": "10000000000000000000000" - }, - "f7acff934b84da0969dc37a8fcf643b7d7fbed41": { - "balance": "1999944000000000000000" - }, - "f05fcd4c0d73aa167e5553c8c0d6d4f2faa39757": { - "balance": "13334000000000000000000" - }, - "c981d312d287d558871edd973abb76b979e5c35e": { - "balance": "1970000000000000000000" - }, - "9da61ccd62bf860656e0325d7157e2f160d93bb5": { - "balance": "4999980000000000000000" - }, - "d284a50382f83a616d39b8a9c0f396e0ebbfa95d": { - "balance": "1000070000000000000000" - }, - "d6cf5c1bcf9da662bcea2255905099f9d6e84dcc": { - "balance": "8349332000000000000000" - }, - "c71b2a3d7135d2a85fb5a571dcbe695e13fc43cd": { - "balance": "1000000000000000000000" - }, - "b22dadd7e1e05232a93237baed98e0df92b1869e": { - "balance": "2000000000000000000000" - }, - "b09fe6d4349b99bc37938054022d54fca366f7af": { - "balance": "200000000000000000000000" - }, - "427e4751c3babe78cff8830886febc10f9908d74": { - "balance": "1970000000000000000000" - }, - "60b358cb3dbefa37f47df2d7365840da8e3bc98c": { - "balance": "20000000000000000000" - }, - "dcd5bca2005395b675fde5035659b26bfefc49ee": { - "balance": "197000000000000000000" - }, - "81186931184137d1192ac88cd3e1e5d0fdb86a74": { - "balance": "2900000000000000000000" - }, - "de212293f8f1d231fa10e609470d512cb8ffc512": { - "balance": "2000000000000000000000" - }, - "1937c5c515057553ccbd46d5866455ce66290284": { - "balance": "1000000000000000000000000" - }, - "592777261e3bd852c48eca95b3a44c5b7f2d422c": { - "balance": "20000000000000000000000" - }, - "bbf84292d954acd9e4072fb860b1504106e077ae": { - "balance": "1500000000000000000000" - }, - "3b4100e30a73b0c734b18ffa8426d19b19312f1a": { - "balance": "55300000000000000000000" - }, - "a03a3dc7c533d1744295be955d61af3f52b51af5": { - "balance": "40000000000000000000" - }, - "4aa148c2c33401e66a2b586e6577c4b292d3f240": { - "balance": "216200000000000000000" - }, - "ff850e3be1eb6a4d726c08fa73aad358f39706da": { - "balance": "1940000000000000000000" - }, - "743651b55ef8429df50cf81938c2508de5c8870f": { - "balance": "2000000000000000000000" - }, - "3700e3027424d939dbde5d42fb78f6c4dbec1a8f": { - "balance": "40000000000000000000" - }, - "c1cbd2e2332a524cf219b10d871ccc20af1fb0fa": { - "balance": "1000000000000000000000" - }, - "e25b9f76b8ad023f057eb11ad94257a0862e4e8c": { - "balance": "2000000000000000000000" - }, - "719e891fbcc0a33e19c12dc0f02039ca05b801df": { - "balance": "6185800000000000000000" - }, - "39636b25811b176abfcfeeca64bc87452f1fdff4": { - "balance": "400000000000000000000" - }, - "631030a5b27b07288a45696f189e1114f12a81c0": { - "balance": "499970000000000000000" - }, - "bcc84597b91e73d5c5b4d69c80ecf146860f779a": { - "balance": "4380000000000000000000" - }, - "095e0174829f34c3781be1a5e38d1541ea439b7f": { - "balance": "6000000000000000000000" - }, - "2e7e05e29edda7e4ae25c5173543efd71f6d3d80": { - "balance": "6000000000000000000000" - }, - "dbb6ac484027041642bbfd8d80f9d0c1cf33c1eb": { - "balance": "2000000000000000000000" - }, - "153c08aa8b96a611ef63c0253e2a4334829e579d": { - "balance": "394000000000000000000" - }, - "10f4bff0caa5027c0a6a2dcfc952824de2940909": { - "balance": "2000000000000000000000" - }, - "2ef869f0350b57d53478d701e3fee529bc911c75": { - "balance": "50000000000000000000" - }, - "70ab34bc17b66f9c3b63f151274f2a727c539263": { - "balance": "2000000000000000000000" - }, - "3201259caf734ad7581c561051ba0bca7fd6946b": { - "balance": "180000000000000000000000" - }, - "84e9cf8166c36abfa49053b7a1ad4036202681ef": { - "balance": "2000000000000000000000" - }, - "4ebc5629f9a6a66b2cf3363ac4895c0348e8bf87": { - "balance": "1000090000000000000000" - }, - "e50b464ac9de35a5618b7cbf254674182b81b97e": { - "balance": "4100000000000000000000" - }, - "2abdf1a637ef6c42a7e2fe217773d677e804ebdd": { - "balance": "5000000000000000000000" - }, - "7a0a78a9cc393f91c3d9e39a6b8c069f075e6bf5": { - "balance": "1337000000000000000000" - }, - "2d9c5fecd2b44fbb6a1ec732ea059f4f1f9d2b5c": { - "balance": "1010694000000000000000" - }, - "7b712c7af11676006a66d2fc5c1ab4c479ce6037": { - "balance": "8000000000000000000000" - }, - "3466f67e39636c01f43b3a21a0e8529325c08624": { - "balance": "842864000000000000000" - }, - "fdd502a74e813bcfa355ceda3c176f6a6871af7f": { - "balance": "400000000000000000000" - }, - "26475419c06d5f147aa597248eb46cf7befa64a5": { - "balance": "1640000000000000000000" - }, - "9243d7762d77287b12638688b9854e88a769b271": { - "balance": "1000000000000000000000" - }, - "723d8baa2551d2addc43c21b45e8af4ca2bfb2c2": { - "balance": "1760000000000000000000" - }, - "f2fbb6d887f8b8cc3a869aba847f3d1f643c53d6": { - "balance": "3999000000000000000000" - }, - "2cdb3944650616e47cb182e060322fa1487978ce": { - "balance": "1820000000000000000000" - }, - "f0d21663d8b0176e05fde1b90ef31f8530fda95f": { - "balance": "1999944000000000000000" - }, - "77cc02f623a9cf98530997ea67d95c3b491859ae": { - "balance": "1354900000000000000000" - }, - "d1b5a454ac3405bb4179208c6c84de006bcb9be9": { - "balance": "500000000000000000000" - }, - "b9920fd0e2c735c256463caa240fb7ac86a93dfa": { - "balance": "1760000000000000000000" - }, - "ed1f1e115a0d60ce02fb25df014d289e3a0cbe7d": { - "balance": "500000000000000000000" - }, - "23e2c6a8be8e0acfa5c4df5e36058bb7cbac5a81": { - "balance": "2000000000000000000000" - }, - "f0be0faf4d7923fc444622d1980cf2d990aab307": { - "balance": "2000000000000000000000" - }, - "0829d0f7bb7c446cfbb0deadb2394d9db7249a87": { - "balance": "40110000000000000000" - }, - "2ecac504b233866eb5a4a99e7bd2901359e43b3d": { - "balance": "20000000000000000000000" - }, - "06d6cb308481c336a6e1a225a912f6e6355940a1": { - "balance": "1760000000000000000000" - }, - "d4879fd12b1f3a27f7e109761b23ca343c48e3d8": { - "balance": "666000000000000000000" - }, - "857f100b1a5930225efc7e9020d78327b41c02cb": { - "balance": "2000000000000000000000" - }, - "3aa42c21b9b31c3e27ccd17e099af679cdf56907": { - "balance": "8000000000000000000000" - }, - "764d5212263aff4a2a14f031f04ec749dc883e45": { - "balance": "1850000000000000000000" - }, - "d03a2da41e868ed3fef5745b96f5eca462ff6fda": { - "balance": "3000000000000000000000" - }, - "4f26690c992b7a312ab12e1385d94acd58288e7b": { - "balance": "14000000000000000000000" - }, - "7b122162c913e7146cad0b7ed37affc92a0bf27f": { - "balance": "1506799000000000000000" - }, - "c87352dba582ee2066b9c002a962e003134f78b1": { - "balance": "500000000000000000000" - }, - "9f4ac9c9e7e24cb2444a0454fa5b9ad9d92d3853": { - "balance": "835000000000000000000" - }, - "ccf62a663f1353ba2ef8e6521dc1ecb673ec8ef7": { - "balance": "152000000000000000000" - }, - "557f5e65e0da33998219ad4e99570545b2a9d511": { - "balance": "11024000000000000000000" - }, - "a5f0077b351f6c505cd515dfa6d2fa7f5c4cd287": { - "balance": "40000000000000000000000" - }, - "79c6002f8452ca157f1317e80a2faf24475559b7": { - "balance": "20000000000000000000" - }, - "3aa07a34a1afc8967d3d1383b96b62cf96d5fa90": { - "balance": "20000000000000000000000" - }, - "7f389c12f3c6164f6446566c77669503c2792527": { - "balance": "98500000000000000000" - }, - "ac4cc256ae74d624ace80db078b2207f57198f6b": { - "balance": "2001000000000000000000" - }, - "823ba7647238d113bce9964a43d0a098118bfe4d": { - "balance": "200000000000000000000" - }, - "f5a7676ad148ae9c1ef8b6f5e5a0c2c473be850b": { - "balance": "200000000000000000000" - }, - "7d34803569e00bd6b59fff081dfa5c0ab4197a62": { - "balance": "1712700000000000000000" - }, - "061ea4877cd08944eb64c2966e9db8dedcfec06b": { - "balance": "1000000000000000000000" - }, - "df37c22e603aedb60a627253c47d8ba866f6d972": { - "balance": "24000000000000000000000" - }, - "529aa002c6962a3a8545027fd8b05f22b5bf9564": { - "balance": "1670000000000000000000" - }, - "eb89a882670909cf377e9e78286ee97ba78d46c2": { - "balance": "802200000000000000000" - }, - "9ac85397792a69d78f286b86432a07aeceb60e64": { - "balance": "14300000000000000000" - }, - "9610592202c282ab9bd8a884518b3e0bd4758137": { - "balance": "268000000000000000000" - }, - "73932709a97f02c98e51b091312865122385ae8e": { - "balance": "1430000000000000000000" - }, - "5ef8c96186b37984cbfe04c598406e3b0ac3171f": { - "balance": "9400000000000000000000" - }, - "b6f78da4f4d041b3bc14bc5ba519a5ba0c32f128": { - "balance": "172326253000000000000000" - }, - "6f0edd23bcd85f6015f9289c28841fe04c83efeb": { - "balance": "19100000000000000000" - }, - "a8a43c009100616cb4ae4e033f1fc5d7e0b6f152": { - "balance": "3939015000000000000000" - }, - "7081fa6baad6cfb7f51b2cca16fb8970991a64ba": { - "balance": "233953000000000000000" - }, - "9de7386dde401ce4c67b71b6553f8aa34ea5a17d": { - "balance": "60000000000000000000" - }, - "54ec7300b81ac84333ed1b033cd5d7a33972e234": { - "balance": "200000000000000000000" - }, - "67a80e0190721f94390d6802729dd12c31a895ad": { - "balance": "1999964000000000000000" - }, - "3a4297da3c555e46c073669d0478fce75f2f790e": { - "balance": "1969606000000000000000" - }, - "c2e0584a71348cc314b73b2029b6230b92dbb116": { - "balance": "2000000000000000000000" - }, - "0a2ade95b2e8c66d8ae6f0ba64ca57d783be6d44": { - "balance": "4000000000000000000000" - }, - "544b5b351d1bc82e9297439948cf4861dac9ae11": { - "balance": "22000000000000000000000" - }, - "3ae62bd271a760637fad79c31c94ff62b4cd12f7": { - "balance": "2000000000000000000000" - }, - "0d8023929d917234ae40512b1aabb5e8a4512771": { - "balance": "148000000000000000000" - }, - "2858acacaf21ea81cab7598fdbd86b452e9e8e15": { - "balance": "666000000000000000000" - }, - "c033b1325a0af45472c25527853b1f1c21fa35de": { - "balance": "2000000000000000000000" - }, - "bbf85aaaa683738f073baef44ac9dc34c4c779ea": { - "balance": "2000000000000000000000" - }, - "6ae57f27917c562a132a4d1bf7ec0ac785832926": { - "balance": "6000000000000000000000" - }, - "88e6f9b247f988f6c0fc14c56f1de53ec69d43cc": { - "balance": "100000000000000000000" - }, - "b72c2a011c0df50fbb6e28b20ae1aad217886790": { - "balance": "4000000000000000000000" - }, - "161caf5a972ace8379a6d0a04ae6e163fe21df2b": { - "balance": "100000000000000000000000" - }, - "2a63590efe9986c3fee09b0a0a338b15bed91f21": { - "balance": "6458400000000000000000" - }, - "50e1c8ec98415bef442618708799437b86e6c205": { - "balance": "6000000000000000000000" - }, - "33f4a6471eb1bca6a9f85b3b4872e10755c82be1": { - "balance": "2000000000000000000000" - }, - "9c49deff47085fc09704caa2dca8c287a9a137da": { - "balance": "8000000000000000000000" - }, - "e1173a247d29d8238df0922f4df25a05f2af77c3": { - "balance": "40007051000000000000000" - }, - "51891b2ccdd2f5a44b2a8bc49a5d9bca6477251c": { - "balance": "310000000000000000000" - }, - "ecaf3350b7ce144d068b186010852c84dd0ce0f0": { - "balance": "2000000000000000000000" - }, - "72393d37b451effb9e1ff3b8552712e2a970d8c2": { - "balance": "985000000000000000000" - }, - "1bbc60bcc80e5cdc35c5416a1f0a40a83dae867b": { - "balance": "2000000000000000000000" - }, - "b8ab39805bd821184f6cbd3d2473347b12bf175c": { - "balance": "118200000000000000000" - }, - "c55a6b4761fd11e8c85f15174d74767cd8bd9a68": { - "balance": "133700000000000000000" - }, - "99d1b585965f406a42a49a1ca70f769e765a3f98": { - "balance": "16700000000000000000000" - }, - "9ab988b505cfee1dbe9cd18e9b5473b9a2d4f536": { - "balance": "320000000000000000000" - }, - "7fef8c38779fb307ec6f044bebe47f3cfae796f1": { - "balance": "168561000000000000000" - }, - "322d6f9a140d213f4c80cd051afe25c620bf4c7d": { - "balance": "20000000000000000000" - }, - "3bd9a06d1bd36c4edd27fc0d1f5b088ddae3c72a": { - "balance": "499970000000000000000" - }, - "5dcdb6b87a503c6d8a3c65c2cf9a9aa883479a1e": { - "balance": "9200000000000000000000" - }, - "6e84c2fd18d8095714a96817189ca21cca62bab1": { - "balance": "340935000000000000000" - }, - "a5bad86509fbe0e0e3c0e93f6d381f1af6e9d481": { - "balance": "6000000000000000000000" - }, - "3954bdfe0bf587c695a305d9244c3d5bdddac9bb": { - "balance": "19187461000000000000000" - }, - "63f0e5a752f79f67124eed633ad3fd2705a397d4": { - "balance": "3940000000000000000000" - }, - "33fd718f0b91b5cec88a5dc15eecf0ecefa4ef3d": { - "balance": "432500000000000000000" - }, - "68027d19558ed7339a08aee8de3559be063ec2ea": { - "balance": "2000000000000000000000" - }, - "96f0462ae6f8b96088f7e9c68c74b9d8ad34b347": { - "balance": "1790000000000000000000" - }, - "f1f391ca92808817b755a8b8f4e2ca08d1fd1108": { - "balance": "6000000000000000000000" - }, - "7fcf5ba6666f966c5448c17bf1cb0bbcd8019b06": { - "balance": "99999000000000000000" - }, - "e9b9a2747510e310241d2ece98f56b3301d757e0": { - "balance": "2000000000000000000000" - }, - "2100381d60a5b54adc09d19683a8f6d5bb4bfbcb": { - "balance": "10000000000000000000000" - }, - "7495ae78c0d90261e2140ef2063104731a60d1ed": { - "balance": "34250000000000000000" - }, - "dc911cf7dc5dd0813656670528e9338e67034786": { - "balance": "2000000000000000000000" - }, - "262aed4bc0f4a4b2c6fb35793e835a49189cdfec": { - "balance": "10000000000000000000000" - }, - "9ee93f339e6726ec65eea44f8a4bfe10da3d3282": { - "balance": "2000000000000000000000" - }, - "a3a57b0716132804d60aac281197ff2b3d237b01": { - "balance": "1400000000000000000000" - }, - "c799e34e88ff88be7de28e15e4f2a63d0b33c4cb": { - "balance": "200000000000000000000" - }, - "c7506c1019121ff08a2c8c1591a65eb4bdfb4a3f": { - "balance": "600000000000000000000" - }, - "795ebc2626fc39b0c86294e0e837dcf523553090": { - "balance": "1000000000000000000000" - }, - "441aca82631324acbfa2468bda325bbd78477bbf": { - "balance": "6000000000000000000000" - }, - "9f271d285500d73846b18f733e25dd8b4f5d4a8b": { - "balance": "722000000000000000000" - }, - "d77892e2273b235d7689e430e7aeed9cbce8a1f3": { - "balance": "2000000000000000000000" - }, - "4f8972838f70c903c9b6c6c46162e99d6216d451": { - "balance": "4610000000000000000000" - }, - "4c85ed362f24f6b9f04cdfccd022ae535147cbb9": { - "balance": "1500000000000000000000" - }, - "3807eff43aa97c76910a19752dd715ee0182d94e": { - "balance": "250190000000000000000" - }, - "3a9e5441d44b243be55b75027a1ceb9eacf50df2": { - "balance": "1000000000000000000000" - }, - "3deae43327913f62808faa1b6276a2bd6368ead9": { - "balance": "2000000000000000000000" - }, - "c270456885342b640b4cfc1b520e1a544ee0d571": { - "balance": "1820000000000000000000" - }, - "77798f201257b9c35204957057b54674aefa51df": { - "balance": "149000000000000000000" - }, - "225f9eb3fb6ff3e9e3c8447e14a66e8d4f3779f6": { - "balance": "2000000000000000000000" - }, - "78df2681d6d602e22142d54116dea15d454957aa": { - "balance": "298000000000000000000" - }, - "283396ce3cac398bcbe7227f323e78ff96d08767": { - "balance": "400000000000000000000" - }, - "747ff7943b71dc4dcdb1668078f83dd7cc4520c2": { - "balance": "60000000000000000000" - }, - "a4ed11b072d89fb136759fc69b428c48aa5d4ced": { - "balance": "262800000000000000000" - }, - "cc043c4388d345f884c6855e71142a9f41fd6935": { - "balance": "20000000000000000000" - }, - "ab14d221e33d544629198cd096ed63dfa28d9f47": { - "balance": "6000000000000000000000" - }, - "251e6838f7cec5b383c1d90146341274daf8e502": { - "balance": "147510000000000000000" - }, - "36a0e61e1be47fa87e30d32888ee0330901ca991": { - "balance": "20000000000000000000" - }, - "bcfc98e5c82b6adb180a3fcb120b9a7690c86a3f": { - "balance": "1970000000000000000000" - }, - "18a6d2fc52be73084023c91802f05bc24a4be09f": { - "balance": "2000000000000000000000" - }, - "80591a42179f34e64d9df75dcd463b28686f5574": { - "balance": "20000000000000000000000" - }, - "881230047c211d2d5b00d8de4c5139de5e3227c7": { - "balance": "10000000000000000000000" - }, - "9eb1ff71798f28d6e989fa1ea0588e27ba86cb7d": { - "balance": "140800000000000000000" - }, - "a01fd1906a908506dedae1e208128872b56ee792": { - "balance": "3000000000000000000000" - }, - "1b05ea6a6ac8af7cb6a8b911a8cce8fe1a2acfc8": { - "balance": "2000000000000000000000" - }, - "6add932193cd38494aa3f03aeccc4b7ab7fabca2": { - "balance": "89600000000000000000" - }, - "2aaa35274d742546670b7426264521032af4f4c3": { - "balance": "10000000000000000000000" - }, - "67b8a6e90fdf0a1cac441793301e8750a9fa7957": { - "balance": "895000000000000000000" - }, - "5b5be0d8c67276baabd8edb30d48ea75640b8b29": { - "balance": "824480000000000000000" - }, - "28d7e5866f1d85fd1ceb32bfbe1dfc36db434566": { - "balance": "7199000000000000000000" - }, - "98e3e90b28fccaee828779b8d40a5568c4116e21": { - "balance": "40000000000000000000" - }, - "2dd578f7407dfbd548d05e95ccc39c485429626a": { - "balance": "4200000000000000000000" - }, - "8ca6989746b06e32e2487461b1ce996a273acfd7": { - "balance": "20000000000000000000" - }, - "a6f93307f8bce03195fece872043e8a03f7bd11a": { - "balance": "2886000000000000000000" - }, - "efbd52f97da5fd3a673a46cbf330447b7e8aad5c": { - "balance": "100033000000000000000" - }, - "52bdd9af5978850bc24110718b3723759b437e59": { - "balance": "1730000000000000000000" - }, - "6e073b66d1b8c66744d88096a8dd99ec7e0228da": { - "balance": "4000000000000000000000" - }, - "a29d661a6376f66d0b74e2fe9d8f26c0247ec84c": { - "balance": "4117300000000000000000" - }, - "7d34ff59ae840a7413c6ba4c5bb2ba2c75eab018": { - "balance": "3000000000000000000000" - }, - "2eca6a3c5d9f449d0956bd43fa7b4d7be8435958": { - "balance": "2000020000000000000000" - }, - "f59f9f02bbc98efe097eabb78210979021898bfd": { - "balance": "9999800000000000000000" - }, - "90e300ac71451e401f887f6e7728851647a80e07": { - "balance": "400000000000000000000" - }, - "05ae7fd4bbcc80ca11a90a1ec7a301f7cccc83db": { - "balance": "910000000000000000000" - }, - "e54102534de8f23effb093b31242ad3b233facfd": { - "balance": "4000000000000000000000" - }, - "c127aab59065a28644a56ba3f15e2eac13da2995": { - "balance": "600000000000000000000" - }, - "ed60c4ab6e540206317e35947a63a9ca6b03e2cb": { - "balance": "57275000000000000000" - }, - "d855b03ccb029a7747b1f07303e0a664793539c8": { - "balance": "2000000000000000000000" - }, - "1178501ff94add1c5881fe886136f6dfdbe61a94": { - "balance": "158000000000000000000" - }, - "f447108b98df64b57e871033885c1ad71db1a3f9": { - "balance": "6916709000000000000000" - }, - "deee2689fa9006b59cf285237de53b3a7fd01438": { - "balance": "450034000000000000000" - }, - "7f01dc7c3747ca608f983dfc8c9b39e755a3b914": { - "balance": "206980000000000000000" - }, - "9edeac4c026b93054dc5b1d6610c6f3960f2ad73": { - "balance": "1200000000000000000000" - }, - "e3cffe239c64e7e20388e622117391301b298696": { - "balance": "500000000000000000000" - }, - "ebbb4f2c3da8be3eb62d1ffb1f950261cf98ecda": { - "balance": "2000000000000000000000" - }, - "38c10b90c859cbb7815692f99dae520ab5febf5e": { - "balance": "13169000000000000000000" - }, - "23f9ecf3e5dddca38815d3e59ed34b5b90b4a353": { - "balance": "204608000000000000000" - }, - "d7fa5ffb6048f96fb1aba09ef87b1c11dd7005e4": { - "balance": "1000000000000000000000" - }, - "9ca42ee7a0b898f6a5cc60b5a5d7b1bfa3c33231": { - "balance": "2000000000000000000000" - }, - "8b9577920053b1a00189304d888010d9ef2cb4bf": { - "balance": "500000000000000000000" - }, - "fcd0b4827cd208ffbf5e759dba8c3cc61d8c2c3c": { - "balance": "8000000000000000000000" - }, - "01ff1eb1dead50a7f2f9638fdee6eccf3a7b2ac8": { - "balance": "600000000000000000000" - }, - "abde147b2af789eaa586547e66c4fa2664d328a4": { - "balance": "247545000000000000000" - }, - "64042ba68b12d4c151651ca2813b7352bd56f08e": { - "balance": "600000000000000000000" - }, - "dccca42045ec3e16508b603fd936e7fd7de5f36a": { - "balance": "19700000000000000000" - }, - "e77a89bd45dc04eeb4e41d7b596b707e6e51e74c": { - "balance": "12000000000000000000000" - }, - "f77c7b845149efba19e261bc7c75157908afa990": { - "balance": "2000000000000000000000" - }, - "fa5201fe1342af11307b9142a041243ca92e2f09": { - "balance": "152150000000000000000000" - }, - "40df495ecf3f8b4cef2a6c189957248fe884bc2b": { - "balance": "12000000000000000000000" - }, - "3d79a853d71be0621b44e29759656ca075fdf409": { - "balance": "2000000000000000000000" - }, - "6de02f2dd67efdb7393402fa9eaacbcf589d2e56": { - "balance": "1182000000000000000000" - }, - "729aad4627744e53f5d66309aa74448b3acdf46f": { - "balance": "2000000000000000000000" - }, - "4e4318f5e13e824a54edfe30a7ed4f26cd3da504": { - "balance": "2000000000000000000000" - }, - "c6a286e065c85f3af74812ed8bd3a8ce5d25e21d": { - "balance": "18200000000000000000" - }, - "fd686de53fa97f99639e2568549720bc588c9efc": { - "balance": "1969606000000000000000" - }, - "06b0ff834073cce1cbc9ea557ea87b605963e8b4": { - "balance": "300000000000000000000" - }, - "72b5633fe477fe542e742facfd690c137854f216": { - "balance": "1670000000000000000000" - }, - "8bf373d076814cbc57e1c6d16a82c5be13c73d37": { - "balance": "200000000000000000000" - }, - "cf264e6925130906c4d7c18591aa41b2a67f6f58": { - "balance": "2000000000000000000000" - }, - "0ea2a210312b3e867ee0d1cc682ce1d666f18ed5": { - "balance": "10000000000000000000000" - }, - "d02afecf8e2ec2b62ac8ad204161fd1fae771d0e": { - "balance": "2000000000000000000000" - }, - "e6b20f980ad853ad04cbfc887ce6601c6be0b24c": { - "balance": "4000000000000000000000" - }, - "4280a58f8bb10b9440de94f42b4f592120820191": { - "balance": "2000000000000000000000" - }, - "a914cdb571bfd93d64da66a4e108ea134e50d000": { - "balance": "1430143000000000000000" - }, - "60864236930d04d8402b5dcbeb807f3caf611ea2": { - "balance": "4000000000000000000000" - }, - "f9dd239008182fb519fb30eedd2093fed1639be8": { - "balance": "500000000000000000000" - }, - "18e53243981aabc8767da10c73449f1391560eaa": { - "balance": "6000000000000000000000" - }, - "c3a9226ae275df2cab312b911040634a9c9c9ef6": { - "balance": "4000000000000000000000" - }, - "4fcc19ea9f4c57dcbce893193cfb166aa914edc5": { - "balance": "7001380000000000000000" - }, - "c1e1409ca52c25435134d006c2a6a8542dfb7273": { - "balance": "34380000000000000000" - }, - "981ddf0404e4d22dda556a0726f00b2d98ab9569": { - "balance": "999972000000000000000" - }, - "e5bcc88c3b256f6ed5fe550e4a18198b943356ad": { - "balance": "2000000000000000000000" - }, - "74a17f064b344e84db6365da9591ff1628257643": { - "balance": "20000000000000000000" - }, - "2720f9ca426ef2f2cbd2fecd39920c4f1a89e16d": { - "balance": "2000000000000000000000" - }, - "8d04a5ebfb5db409db0617c9fa5631c192861f4a": { - "balance": "970000000000000000000" - }, - "f18b14cbf6694336d0fe12ac1f25df2da0c05dbb": { - "balance": "3999800000000000000000" - }, - "56ac20d63bd803595cec036da7ed1dc66e0a9e07": { - "balance": "63927000000000000000" - }, - "92c94c2820dfcf7156e6f13088ece7958b3676fd": { - "balance": "95500000000000000000" - }, - "968dea60df3e09ae3c8d3505e9c080454be0e819": { - "balance": "6000000000000000000000" - }, - "9268d62646563611dc3b832a30aa2394c64613e3": { - "balance": "2000000000000000000000" - }, - "5a192b964afd80773e5f5eda6a56f14e25e0c6f3": { - "balance": "500000000000000000000" - }, - "df8d48b1eb07b3c217790e6c2df04dc319e7e848": { - "balance": "500000000000000000000" - }, - "7f61fa6cf5f898b440dac5abd8600d6d691fdef9": { - "balance": "280000000000000000000" - }, - "929d368eb46a2d1fbdc8ffa0607ede4ba88f59ad": { - "balance": "2000000000000000000000" - }, - "9982a5890ffb5406d3aca8d2bfc1dd70aaa80ae0": { - "balance": "2000000000000000000000" - }, - "bf2aea5a1dcf6ed3b5e8323944e983fedfd1acfb": { - "balance": "1580000000000000000000" - }, - "46aa501870677e7f0a504876b4e8801a0ad01c46": { - "balance": "800000000000000000000" - }, - "8f473d0ab876ddaa15608621d7013e6ff714b675": { - "balance": "470400000000000000000" - }, - "02290fb5f9a517f82845acdeca0fc846039be233": { - "balance": "2000000000000000000000" - }, - "8a5831282ce14a657a730dc18826f7f9b99db968": { - "balance": "4330268000000000000000" - }, - "0328510c09dbcd85194a98d67c33ac49f2f94d60": { - "balance": "11000000000000000000000" - }, - "cf883a20329667ea226a1e3c765dbb6bab32219f": { - "balance": "3038972000000000000000" - }, - "2615100ea7e25bba9bca746058afbbb4ffbe4244": { - "balance": "500000000000000000000" - }, - "b115ee3ab7641e1aa6d000e41bfc1ec7210c2f32": { - "balance": "13000000000000000000000" - }, - "5cfa8d568575658ca4c1a593ac4c5d0e44c60745": { - "balance": "291000000000000000000" - }, - "d3c24d4b3a5e0ff8a4622d518edd73f16ab28610": { - "balance": "20000000000000000000" - }, - "a639acd96b31ba53b0d08763229e1f06fd105e9d": { - "balance": "8000000000000000000000" - }, - "ffa4aff1a37f984b0a67272149273ae9bd41e3bc": { - "balance": "10000000000000000000000" - }, - "cf684dfb8304729355b58315e8019b1aa2ad1bac": { - "balance": "432500000000000000000" - }, - "5797b60fd2894ab3c2f4aede86daf2e788d745ad": { - "balance": "6000000000000000000000" - }, - "a6a0de421ae54f6d17281308f5646d2f39f7775d": { - "balance": "2000000000000000000000" - }, - "08504f05643fab5919f5eea55925d7a3ed7d807a": { - "balance": "20000000000000000000" - }, - "7a7068e1c3375c0e599db1fbe6b2ea23b8f407d2": { - "balance": "2000000000000000000000" - }, - "1078d7f61b0e56c74ee6635b2e1819ef1e3d8785": { - "balance": "1000000000000000000000" - }, - "6e12b51e225b4a4372e59ad7a2a1a13ea3d3a137": { - "balance": "14172200000000000000000" - }, - "6a2e86469a5bf37cee82e88b4c3863895d28fcaf": { - "balance": "519000000000000000000" - }, - "197672fd39d6f246ce66a790d13aa922d70ea109": { - "balance": "1000000000000000000000" - }, - "8009a7cbd192b3aed4adb983d5284552c16c7451": { - "balance": "4000000000000000000000" - }, - "f6c3c48a1ac0a34799f04db86ec7a975fe7768f3": { - "balance": "1970000000000000000000" - }, - "16be75e98a995a395222d00bd79ff4b6e638e191": { - "balance": "36000000000000000000000" - }, - "6c05e34e5ef2f42ed09deff1026cd66bcb6960bb": { - "balance": "2000000000000000000000" - }, - "5d6ae8cbd6b3393c22d16254100d0238e808147c": { - "balance": "719992000000000000000" - }, - "1a376e1b2d2f590769bb858d4575320d4e149970": { - "balance": "4841200000000000000000" - }, - "f6ead67dbf5b7eb13358e10f36189d53e643cfcf": { - "balance": "40000000000000000000000" - }, - "467d5988249a68614716659840ed0ae6f6f457bc": { - "balance": "387500000000000000000" - }, - "aa960e10c52391c54e15387cc67af827b5316dcc": { - "balance": "2000000000000000000000" - }, - "483ba99034e900e3aedf61499d3b2bce39beb7aa": { - "balance": "985000000000000000000" - }, - "86f23e9c0aafc78b9c404dcd60339a925bffa266": { - "balance": "400000000000000000000" - }, - "d05a447c911dbb275bfb2e5a37e5a703a56f9997": { - "balance": "200000000000000000000" - }, - "edb71ec41bda7dce86e766e6e8c3e9907723a69b": { - "balance": "20000000000000000000" - }, - "f86a3ea8071f7095c7db8a05ae507a8929dbb876": { - "balance": "336000000000000000000" - }, - "323b3cfe3ee62bbde2a261e53cb3ecc05810f2c6": { - "balance": "13790000000000000000000" - }, - "936f3813f5f6a13b8e4ffec83fe7f826186a71cd": { - "balance": "520000000000000000000" - }, - "6db72bfd43fef465ca5632b45aab7261404e13bf": { - "balance": "2000000000000000000000" - }, - "9bb76204186af2f63be79168601687fc9bad661f": { - "balance": "300000000000000000000" - }, - "28ab165ffb69eda0c549ae38e9826f5f7f92f853": { - "balance": "1296890000000000000000" - }, - "c73e2112282215dc0762f32b7e807dcd1a7aae3e": { - "balance": "6900000000000000000000" - }, - "f8086e42661ea929d2dda1ab6c748ce3055d111e": { - "balance": "1000000000000000000000" - }, - "4db21284bcd4f787a7556500d6d7d8f36623cf35": { - "balance": "1939806000000000000000" - }, - "c48651c1d9c16bff4c9554886c3f3f26431f6f68": { - "balance": "658000000000000000000" - }, - "9bdbdc9b973431d13c89a3f9757e9b3b6275bfc7": { - "balance": "499971000000000000000" - }, - "560da37e956d862f81a75fd580a7135c1b246352": { - "balance": "10000000000000000000000" - }, - "4b60a3e253bf38c8d5662010bb93a473c965c3e5": { - "balance": "1490000000000000000000" - }, - "64e02abb016cc23a2934f6bcddb681905021d563": { - "balance": "1000000000000000000000" - }, - "ac2c8e09d06493a63858437bd20be01962450365": { - "balance": "1910000000000000000000" - }, - "9bf9b3b2f23cf461eb591f28340bc719931c8364": { - "balance": "1000000000000000000000" - }, - "9b5c39f7e0ac168c8ed0ed340477117d1b682ee9": { - "balance": "98000000000000000000" - }, - "f75bb39c799779ebc04a336d260da63146ed98d0": { - "balance": "25000000000000000000" - }, - "a7966c489f4c748a7ae980aa27a574251767caf9": { - "balance": "3000000000000000000000" - }, - "ea53c954f4ed97fd4810111bdab69ef981ef25b9": { - "balance": "17300000000000000000000" - }, - "03a26cfc4c18316f70d59e9e1a79ee3e8b962f4c": { - "balance": "2000000000000000000000" - }, - "3e63ce3b24ca2865b4c5a687b7aea3597ef6e548": { - "balance": "2000000000000000000000" - }, - "500c902958f6421594d1b6ded712490d52ed6c44": { - "balance": "1970000000000000000000" - }, - "6f44ca09f0c6a8294cbd519cdc594ad42c67579f": { - "balance": "50000000000000000000" - }, - "3616fb46c81578c9c8eb4d3bf880451a88379d7d": { - "balance": "200000000000000000000" - }, - "57bc20e2d62b3d19663cdb4c309d5b4f2fc2db8f": { - "balance": "100000000000000000000" - }, - "1cebf0985d7f680aaa915c44cc62edb49eab269e": { - "balance": "1000000000000000000000" - }, - "c0cbf6032fa39e7c46ff778a94f7d445fe22cf30": { - "balance": "310000000000000000000" - }, - "c58b9cc61dedbb98c33f224d271f0e228b583433": { - "balance": "3880000000000000000000" - }, - "e9c6dfae97f7099fc5f4e94b784db802923a1419": { - "balance": "48800000000000000000" - }, - "9bacd3d40f3b82ac91a264d9d88d908eac8664b9": { - "balance": "20000000000000000000000" - }, - "63d80048877596e0c28489e650cd4ac180096a49": { - "balance": "280000000000000000000" - }, - "e6a6f6dd6f70a456f4ec15ef7ad5e5dbb68bd7dc": { - "balance": "200000000000000000000" - }, - "d418870bc2e4fa7b8a6121ae0872d55247b62501": { - "balance": "1580000000000000000000" - }, - "e2f9383d5810ea7b43182b8704b62b27f5925d39": { - "balance": "400000000000000000000" - }, - "bd5e473abce8f97a6932f77c2facaf9cc0a00514": { - "balance": "1117350000000000000000" - }, - "2ff1ca55fd9cec1b1fe9f0a9abb74c513c1e2aaa": { - "balance": "3000000000000000000000" - }, - "9d99b189bbd9a48fc2e16e8fcda33bb99a317bbb": { - "balance": "1126900000000000000000" - }, - "6e96faeda3054302c45f58f161324c99a3eebb62": { - "balance": "20000000000000000000" - }, - "ef93818f684db0c3675ec81332b3183ecc28a495": { - "balance": "1550000000000000000000" - }, - "2659facb1e83436553b5b42989adb8075f9953ed": { - "balance": "29356000000000000000" - }, - "c4ffadaaf2823fbea7bff702021bffc4853eb5c9": { - "balance": "42233000000000000000" - }, - "e9864c1afc8eaad37f3ba56fcb7477cc622009b7": { - "balance": "79000000000000000000" - }, - "87ef6d8b6a7cbf9b5c8c97f67ee2adc2a73b3f77": { - "balance": "200400000000000000000" - }, - "c043f2452dcb9602ef62bd360e033dd23971fe84": { - "balance": "2000000000000000000000" - }, - "0fdd65402395df9bd19fee4507ef5345f745104c": { - "balance": "5000000000000000000000" - }, - "939c4313d2280edf5e071bced846063f0a975d54": { - "balance": "120000000000000000000000" - }, - "b28245037cb192f75785cb86cbfe7c930da258b0": { - "balance": "16000000000000000000000" - }, - "a80cb1738bac08d4f9c08b4deff515545fa8584f": { - "balance": "500000000000000000000" - }, - "62971bf2634cee0be3c9890f51a56099dbb9519b": { - "balance": "656000000000000000000" - }, - "f2efe96560c9d97b72bd36447843885c1d90c231": { - "balance": "2000000000000000000000" - }, - "0e390f44053ddfcef0d608b35e4d9c2cbe9871bb": { - "balance": "1970000000000000000000" - }, - "61d101a033ee0e2ebb3100ede766df1ad0244954": { - "balance": "500000000000000000000" - }, - "6785513cf732e47e87670770b5419be10cd1fc74": { - "balance": "2000000000000000000000" - }, - "167699f48a78c615512515739958993312574f07": { - "balance": "39000000000000000000" - }, - "68ec79d5be7155716c40941c79d78d17de9ef803": { - "balance": "500600000000000000000" - }, - "a0e8ba661b48154cf843d4c2a5c0f792d528ee29": { - "balance": "400000000000000000000" - }, - "1a201b4327cea7f399046246a3c87e6e03a3cda8": { - "balance": "1000000000000000000000" - }, - "f60f62d73937953fef35169e11d872d2ea317eec": { - "balance": "5348000000000000000000" - }, - "c0c04d0106810e3ec0e54a19f2ab8597e69a573d": { - "balance": "50000000000000000000" - }, - "ef47cf073e36f271d522d7fa4e7120ad5007a0bc": { - "balance": "2500000000000000000000" - }, - "a44fe800d96fcad73b7170d0f610cb8c0682d6ce": { - "balance": "4000000000000000000000" - }, - "010f4a98dfa1d9799bf5c796fb550efbe7ecd877": { - "balance": "8023366000000000000000" - }, - "708fa11fe33d85ad1befcbae3818acb71f6a7d7e": { - "balance": "18200000000000000000" - }, - "b38c4e537b5df930d65a74d043831d6b485bbde4": { - "balance": "400000000000000000000" - }, - "250a69430776f6347703f9529783955a6197b682": { - "balance": "1940000000000000000000" - }, - "2d35a9df62757f7ffad1049afb06ca4afc464c51": { - "balance": "20000000000000000000" - }, - "6aff1466c2623675e3cb0e75e423d37a25e442eb": { - "balance": "1730000000000000000000" - }, - "fc15cb99a8d1030b12770add033a79ee0d0c908c": { - "balance": "350056000000000000000" - }, - "e784dcc873aa8c1513ec26ff36bc92eac6d4c968": { - "balance": "200000000000000000000" - }, - "b1c328fb98f2f19ab6646f0a7c8c566fda5a8540": { - "balance": "2500000000000000000000" - }, - "247a0a11c57f0383b949de540b66dee68604b0a1": { - "balance": "1069600000000000000000" - }, - "1af60343360e0b2d75255210375720df21db5c7d": { - "balance": "1000000000000000000000" - }, - "8794bf47d54540ece5c72237a1ffb511ddb74762": { - "balance": "2000000000000000000000" - }, - "e76d945aa89df1e457aa342b31028a5e9130b2ce": { - "balance": "1015200000000000000000" - }, - "a30e0acb534c9b3084e8501da090b4eb16a2c0cd": { - "balance": "2000000000000000000000" - }, - "7099d12f6ec656899b049a7657065d62996892c8": { - "balance": "400000000000000000000" - }, - "7be7f2456971883b9a8dbe4c91dec08ac34e8862": { - "balance": "3000000000000000000000" - }, - "42746aeea14f27beff0c0da64253f1e7971890a0": { - "balance": "1550000000000000000000" - }, - "736b44503dd2f6dd5469ff4c5b2db8ea4fec65d0": { - "balance": "313950000000000000000" - }, - "822edff636563a6106e52e9a2598f7e6d0ef2782": { - "balance": "36099000000000000000" - }, - "03c647a9f929b0781fe9ae01caa3e183e876777e": { - "balance": "445800000000000000000" - }, - "63612e7862c27b587cfb6daf9912cb051f030a9f": { - "balance": "43458000000000000000" - }, - "d46bae61b027e5bb422e83a3f9c93f3c8fc77d27": { - "balance": "2000000000000000000000" - }, - "5f23ba1f37a96c45bc490259538a54c28ba3b0d5": { - "balance": "1200000000000000000000" - }, - "d41d7fb49fe701baac257170426cc9b38ca3a9b2": { - "balance": "176000000000000000000" - }, - "1ebacb7844fdc322f805904fbf1962802db1537c": { - "balance": "10000000000000000000000" - }, - "9c80bc18e9f8d4968b185da8c79fa6e11ffc3e23": { - "balance": "240000000000000000000" - }, - "e4ca0a5238564dfc91e8bf22bade2901619a1cd4": { - "balance": "1000000000000000000000" - }, - "1ad72d20a76e7fcc6b764058f48d417d496fa6cd": { - "balance": "2000000000000000000000" - }, - "d3bc730937fa75d8452616ad1ef1fe7fffe0d0e7": { - "balance": "83363000000000000000" - }, - "eac1482826acb6111e19d340a45fb851576bed60": { - "balance": "32177000000000000000" - }, - "01e40521122530d9ac91113c06a0190b6d63850b": { - "balance": "1337000000000000000000" - }, - "9e20e5fd361eabcf63891f5b87b09268b8eb3793": { - "balance": "100000000000000000000" - }, - "69ff429074cb9b6c63bc914284bce5f0c8fbf7d0": { - "balance": "500000000000000000000" - }, - "0d3265d3e7bdb93d5e8e8b1ca47f210a793ecc8e": { - "balance": "200000000000000000000" - }, - "5b4ea16db6809b0352d4b6e81c3913f76a51bb32": { - "balance": "400000000000000000000" - }, - "d8fe088fffce948f5137ee23b01d959e84ac4223": { - "balance": "227942000000000000000" - }, - "7e4e9409704121d1d77997026ff06ea9b19a8b90": { - "balance": "2602600000000000000000" - }, - "96b434fe0657e42acc8212b6865139dede15979c": { - "balance": "4000000000000000000000" - }, - "22f004df8de9e6ebf523ccace457accb26f97281": { - "balance": "10000000000000000000000" - }, - "d8f9240c55cff035523c6d5bd300d370dc8f0c95": { - "balance": "285000000000000000000" - }, - "9d9e57fde30e5068c03e49848edce343b7028358": { - "balance": "1730000000000000000000" - }, - "317cf4a23cb191cdc56312c29d15e210b3b9b784": { - "balance": "144000000000000000000" - }, - "79f08e01ce0988e63c7f8f2908fade43c7f9f5c9": { - "balance": "18200000000000000000" - }, - "04e5f5bc7c923fd1e31735e72ef968fd67110c6e": { - "balance": "1611000000000000000000" - }, - "1ec4ec4b77bf19d091a868e6f49154180541f90e": { - "balance": "2000000000000000000000" - }, - "8737dae671823a8d5917e0157ace9c43468d946b": { - "balance": "1999944000000000000000" - }, - "f998ca3411730a6cd10e7455b0410fb0f6d3ff80": { - "balance": "2000000000000000000000" - }, - "6e2eab85dc89fe29dc0aa1853247dab43a523d56": { - "balance": "80000000000000000000" - }, - "72c083beadbdc227c5fb43881597e32e83c26056": { - "balance": "20000000000000000000000" - }, - "5902e44af769a87246a21e079c08bf36b06efeb3": { - "balance": "1000000000000000000000" - }, - "cc2d04f0a4017189b340ca77198641dcf6456b91": { - "balance": "3940000000000000000000" - }, - "bde4c73f969b89e9ceae66a2b51844480e038e9a": { - "balance": "1000000000000000000000" - }, - "adff0d1d0b97471e76d789d2e49c8a74f9bd54ff": { - "balance": "1880000000000000000000" - }, - "397cdb8c80c67950b18d654229610e93bfa6ee1a": { - "balance": "1172938000000000000000" - }, - "a3e051fb744aa3410c3b88f899f5d57f168df12d": { - "balance": "2955000000000000000000" - }, - "810db25675f45ea4c7f3177f37ce29e22d67999c": { - "balance": "200000000000000000000" - }, - "1e13ec51142cebb7a26083412c3ce35144ba56a1": { - "balance": "5000000000000000000000" - }, - "25bdfa3ee26f3849617b230062588a97e3cae701": { - "balance": "1000008000000000000000" - }, - "ae538c73c5b38d8d584d7ebdadefb15cabe48357": { - "balance": "999000000000000000000" - }, - "a2ecce2c49f72a0995a0bda57aacf1e9f001e22a": { - "balance": "4000000000000000000000" - }, - "7e24fbdad290175eb2df6d180a19b9a9f41370be": { - "balance": "1000000000000000000000" - }, - "e8cc43bc4f8acf39bff04ebfbf42aac06a328470": { - "balance": "400000000000000000000" - }, - "c2779771f0536d79a8708f6931abc44b3035e999": { - "balance": "20002000000000000000000" - }, - "ab27ba78c8e5e3daef31ad05aef0ff0325721e08": { - "balance": "468000000000000000000" - }, - "563cb8803c1d32a25b27b64114852bd04d9c20cd": { - "balance": "204400000000000000000" - }, - "08d4267feb15da9700f7ccc3c84a8918bf17cfde": { - "balance": "1790000000000000000000" - }, - "d1778c13fbd968bc083cb7d1024ffe1f49d02caa": { - "balance": "4020000000000000000000" - }, - "1796bcc97b8abc717f4b4a7c6b1036ea2182639f": { - "balance": "355242000000000000000" - }, - "beecd6af900c8b064afcc6073f2d85d59af11956": { - "balance": "2000000000000000000000" - }, - "045ed7f6d9ee9f252e073268db022c6326adfc5b": { - "balance": "100000000000000000000" - }, - "b88a37c27f78a617d5c091b7d5b73a3761e65f2a": { - "balance": "2000000000000000000000" - }, - "72fb49c29d23a18950c4b2dc0ddf410f532d6f53": { - "balance": "2000000000000000000000" - }, - "6ecaefa6fc3ee534626db02c6f85a0c395571e77": { - "balance": "600000000000000000000" - }, - "d1811c55976980f083901d8a0db269222dfb5cfe": { - "balance": "1550000000000000000000" - }, - "98855c7dfbee335344904a12c40c731795b13a54": { - "balance": "1069600000000000000000" - }, - "92a898d46f19719c38126a8a3c27867ae2cee596": { - "balance": "2000000000000000000000" - }, - "ca428863a5ca30369892d612183ef9fb1a04bcea": { - "balance": "1520000000000000000000" - }, - "797427e3dbf0feae7a2506f12df1dc40326e8505": { - "balance": "1000000000000000000000" - }, - "3d574fcf00fae1d98cc8bf9ddfa1b3953b9741bc": { - "balance": "1970000000000000000000" - }, - "28818e18b610001321b31df6fe7d2815cdadc9f5": { - "balance": "1000000000000000000000" - }, - "5f3e1e6739b0c62200e00a003691d9efb238d89f": { - "balance": "3000000000000000000000" - }, - "d9d370fec63576ab15b318bf9e58364dc2a3552a": { - "balance": "100000000000000000000" - }, - "b223bf1fbf80485ca2b5567d98db7bc3534dd669": { - "balance": "4000000000000000000000" - }, - "7b27d0d1f3dd3c140294d0488b783ebf4015277d": { - "balance": "400000000000000000000" - }, - "7930c2d9cbfa87f510f8f98777ff8a8448ca5629": { - "balance": "199955000000000000000" - }, - "820c19291196505b65059d9914b7090be1db87de": { - "balance": "140000000000000000000" - }, - "e545ee84ea48e564161e9482d59bcf406a602ca2": { - "balance": "1850000000000000000000" - }, - "af4cf41785161f571d0ca69c94f8021f41294eca": { - "balance": "9850000000000000000000" - }, - "7a4f9b850690c7c94600dbee0ca4b0a411e9c221": { - "balance": "1910000000000000000000" - }, - "ddab6b51a9030b40fb95cf0b748a059c2417bec7": { - "balance": "2000000000000000000000" - }, - "315ef2da620fd330d12ee55de5f329a696e0a968": { - "balance": "150000000000000000000" - }, - "4db1c43a0f834d7d0478b8960767ec1ac44c9aeb": { - "balance": "872870000000000000000" - }, - "2fef81478a4b2e8098db5ff387ba2153f4e22b79": { - "balance": "999000000000000000000" - }, - "6c6aa0d30b64721990b9504a863fa0bfb5e57da7": { - "balance": "2700000000000000000000" - }, - "33380c6fff5acd2651309629db9a71bf3f20c5ba": { - "balance": "16100000000000000000000" - }, - "4eebf1205d0cc20cee6c7f8ff3115f56d48fba26": { - "balance": "19400000000000000000" - }, - "03cc9d2d21f86b84ac8ceaf971dba78a90e62570": { - "balance": "1610000000000000000000" - }, - "728f9ab080157db3073156dbca1a169ef3179407": { - "balance": "500000000000000000000" - }, - "30ed11b77bc17e5e6694c8bc5b6e4798f68d9ca7": { - "balance": "143731500000000000000000" - }, - "f617b967b9bd485f7695d2ef51fb7792d898f500": { - "balance": "500000000000000000000" - }, - "c0cbad3ccdf654da22cbcf5c786597ca1955c115": { - "balance": "2000000000000000000000" - }, - "80522ddf944ec52e27d724ed4c93e1f7be6083d6": { - "balance": "200000000000000000000" - }, - "4e90ccb13258acaa9f4febc0a34292f95991e230": { - "balance": "15800000000000000000" - }, - "ff207308ced238a6c01ad0213ca9eb4465d42590": { - "balance": "1999944000000000000000" - }, - "35f2949cf78bc219bb4f01907cf3b4b3d3865482": { - "balance": "289800000000000000000" - }, - "68f525921dc11c329b754fbf3e529fc723c834cd": { - "balance": "1610000000000000000000" - }, - "81139bfdcca656c430203f72958c543b6580d40c": { - "balance": "2000000000000000000000" - }, - "9d511543b3d9dc60d47f09d49d01b6c498d82078": { - "balance": "11245000000000000000000" - }, - "084d103254759b343cb2b9c2d8ff9e1ac5f14596": { - "balance": "7600000000000000000000" - }, - "b323dcbf2eddc5382ee4bbbb201ca3931be8b438": { - "balance": "2000000000000000000000" - }, - "349d2c918fd09e2807318e66ce432909176bd50b": { - "balance": "1120000000000000000000" - }, - "b535f8db879fc67fec58824a5cbe6e5498aba692": { - "balance": "1910000000000000000000" - }, - "824074312806da4748434266ee002140e3819ac2": { - "balance": "1507000000000000000000" - }, - "e8ef100d7ce0895832f2678df72d4acf8c28b8e3": { - "balance": "500038000000000000000" - }, - "84af1b157342d54368260d17876230a534b54b0e": { - "balance": "985000000000000000000" - }, - "419a71a36c11d105e0f2aef5a3e598078e85c80b": { - "balance": "5000000000000000000000" - }, - "55af092f94ba6a79918b0cf939eab3f01b3f51c7": { - "balance": "149940000000000000000" - }, - "35a549e8fd6c368d6dcca6d2e7d18e4db95f5284": { - "balance": "499938000000000000000" - }, - "f0e2649c7e6a3f2c5dfe33bbfbd927ca3c350a58": { - "balance": "2000000000000000000000" - }, - "f4b759cc8a1c75f80849ebbcda878dc8f0d66de4": { - "balance": "400000000000000000000" - }, - "21846f2fdf5a41ed8df36e5ed8544df75988ece3": { - "balance": "1999944000000000000000" - }, - "229ff80bf5708009a9f739e0f8b560914016d5a6": { - "balance": "333333000000000000000" - }, - "da505537537ffb33c415fec64e69bae090c5f60f": { - "balance": "160000000000000000000" - }, - "b91d9e916cd40d193db60e79202778a0087716fc": { - "balance": "404800000000000000000" - }, - "bb6823a1bd819f13515538264a2de052b4442208": { - "balance": "25610000000000000000" - }, - "459393d63a063ef3721e16bd9fde45ee9dbd77fb": { - "balance": "1968818000000000000000" - }, - "95f62d0243ede61dad9a3165f53905270d54e242": { - "balance": "1610000000000000000000" - }, - "b0bb29a861ea1d424d45acd4bfc492fb8ed809b7": { - "balance": "80000000000000000000" - }, - "5e74ed80e9655788e1bb269752319667fe754e5a": { - "balance": "56000000000000000000" - }, - "a276b058cb98d88beedb67e543506c9a0d9470d8": { - "balance": "2668652000000000000000" - }, - "8ae9ef8c8a8adfa6ab798ab2cdc405082a1bbb70": { - "balance": "2000000000000000000000" - }, - "e5102c3b711b810344197419b1cd8a7059f13e32": { - "balance": "299999000000000000000" - }, - "c32038ca52aee19745be5c31fcdc54148bb2c4d0": { - "balance": "49984000000000000000" - }, - "13e321728c9c57628058e93fc866a032dd0bda90": { - "balance": "714580000000000000000" - }, - "c2bae4a233c2d85724f0dabebda0249d833e37d3": { - "balance": "5000000000000000000000" - }, - "10d32416722ca4e648630548ead91edd79c06aff": { - "balance": "100000000000000000000" - }, - "d5f07552b5c693c20067b378b809cee853b8f136": { - "balance": "505540000000000000000" - }, - "8668af868a1e98885f937f2615ded6751804eb2d": { - "balance": "20000000000000000000" - }, - "139d3531c9922ad56269f6309aa789fb2485f98c": { - "balance": "4000000000000000000000" - }, - "1d29c7aab42b2048d2b25225d498dba67a03fbb2": { - "balance": "200000000000000000000" - }, - "d35075ca61fe59d123969c36a82d1ab2d918aa38": { - "balance": "2674000000000000000000" - }, - "d6fc0446c6a8d40ae3551db7e701d1fa876e4a49": { - "balance": "2000000000000000000000" - }, - "fccd0d1ecee27addea95f6857aeec8c7a04b28ee": { - "balance": "10000000000000000000000" - }, - "c12cfb7b3df70fceca0ede263500e27873f8ed16": { - "balance": "1000000000000000000000" - }, - "d0db456178206f5c4430fe005063903c3d7a49a7": { - "balance": "706245000000000000000" - }, - "73cf80ae9688e1580e68e782cd0811f7aa494d2c": { - "balance": "7760000000000000000000" - }, - "d60651e393783423e5cc1bc5f889e44ef7ea243e": { - "balance": "398800000000000000000" - }, - "048a8970ea4145c64d5517b8de5b46d0595aad06": { - "balance": "20000000000000000000000" - }, - "dd9b485a3b1cd33a6a9c62f1e5bee92701856d25": { - "balance": "225073000000000000000" - }, - "5b287c7e734299e727626f93fb1187a60d5057fe": { - "balance": "101230000000000000000" - }, - "635c00fdf035bca15fa3610df3384e0fb79068b1": { - "balance": "9000000000000000000000" - }, - "630a913a9031c9492abd4c41dbb15054cfec4416": { - "balance": "5688000000000000000000" - }, - "af3614dcb68a36e45a4e911e62796247222d595b": { - "balance": "2259800000000000000000" - }, - "335e22025b7a77c3a074c78b8e3dfe071341946e": { - "balance": "10178744000000000000000" - }, - "f0e1dfa42adeac2f17f6fdf584c94862fd563393": { - "balance": "500000000000000000000" - }, - "1a9e702f385dcd105e8b9fa428eea21c57ff528a": { - "balance": "1400000000000000000000" - }, - "8ce4949d8a16542d423c17984e6739fa72ceb177": { - "balance": "24999975000000000000000" - }, - "5f29c9de765dde25852af07d33f2ce468fd20982": { - "balance": "2000000000000000000000" - }, - "dbf5f061a0f48e5e69618739a77d2ec19768d201": { - "balance": "152000000000000000000" - }, - "b247cf9c72ec482af3eaa759658f793d670a570c": { - "balance": "912000000000000000000" - }, - "99f4147ccc6bcb80cc842e69f6d00e30fa4133d9": { - "balance": "400000000000000000000" - }, - "ba6d31b9a261d640b5dea51ef2162c3109f1eba8": { - "balance": "5000000000000000000000" - }, - "f05ba8d7b68539d933300bc9289c3d9474d0419e": { - "balance": "126400000000000000000" - }, - "682e96276f518d31d7e56e30dfb009c1218201bd": { - "balance": "20000000000000000000" - }, - "0927220492194b2eda9fc4bbe38f25d681dfd36c": { - "balance": "6000000000000000000000" - }, - "a3c33afc8cb4704e23153de2049d35ae71332472": { - "balance": "799600000000000000000" - }, - "05c736d365aa37b5c0be9c12c8ad5cd903c32cf9": { - "balance": "6002000000000000000000" - }, - "d8eef4cf4beb01ee20d111748b61cb4d3f641a01": { - "balance": "2740000000000000000000" - }, - "16c1bf5b7dc9c83c179efacbcf2eb174e3561cb3": { - "balance": "1000000000000000000000" - }, - "d79db5ab43621a7a3da795e58929f3dd25af67d9": { - "balance": "1999944000000000000000" - }, - "28efae6356509edface89fc61a7fdcdb39eea8e5": { - "balance": "5348000000000000000000" - }, - "c55005a6c37e8ca7e543ce259973a3cace961a4a": { - "balance": "2000000000000000000000" - }, - "ab3d86bc82927e0cd421d146e07f919327cdf6f9": { - "balance": "1910000000000000000000" - }, - "b74ed2666001c16333cf7af59e4a3d4860363b9c": { - "balance": "193600000000000000000" - }, - "1899f69f653b05a5a6e81f480711d09bbf97588c": { - "balance": "1955000000000000000000" - }, - "27fc85a49cff90dbcfdadc9ddd40d6b9a2210a6c": { - "balance": "100000000000000000000" - }, - "cd1ed263fbf6f6f7b48aef8f733d329d4382c7c7": { - "balance": "18500000000000000000" - }, - "d97fe6f53f2a58f6d76d752adf74a8a2c18e9074": { - "balance": "309990000000000000000" - }, - "80da2fdda29a9e27f9e115975e69ae9cfbf3f27e": { - "balance": "200000000000000000000" - }, - "09146ea3885176f07782e1fe30dce3ce24c49e1f": { - "balance": "20000000000000000000" - }, - "393ff4255e5c658f2e7f10ecbd292572671bc2d2": { - "balance": "2000000000000000000000" - }, - "a390ca122b8501ee3e5e07a8ca4b419f7e4dae15": { - "balance": "100000000000000000000" - }, - "6d9193996b194617211106d1635eb26cc4b66c6c": { - "balance": "399640000000000000000" - }, - "999c49c174ca13bc836c1e0a92bff48b271543ca": { - "balance": "3280000000000000000000" - }, - "7421ce5be381738ddc83f02621974ff0686c79b8": { - "balance": "1632000000000000000000" - }, - "6be9030ee6e2fbc491aca3de4022d301772b7b7d": { - "balance": "26740000000000000000" - }, - "81bd75abd865e0c3f04a0b4fdbcb74d34082fbb7": { - "balance": "4000000000000000000000" - }, - "8bc1ff8714828bf286ff7e8a7709106548ed1b18": { - "balance": "10000000000000000000000" - }, - "a0aadbd9509722705f6d2358a5c79f37970f00f6": { - "balance": "200000000000000000000" - }, - "3d881433f04a7d0d27f84944e08a512da3555287": { - "balance": "1200000000000000000000" - }, - "cc1d6ead01aada3e8dc7b95dca25df26eefa639d": { - "balance": "2000000000000000000000" - }, - "35106ba94e8563d4b3cb3c5c692c10e604b7ced8": { - "balance": "2000000000000000000000" - }, - "4d8697af0fbf2ca36e8768f4af22133570685a60": { - "balance": "20000000000000000000" - }, - "1afcc585896cd0ede129ee2de5c19ea811540b64": { - "balance": "3231259000000000000000" - }, - "e5215631b14248d45a255296bed1fbfa0330ff35": { - "balance": "1310000000000000000000" - }, - "e3878f91ca86053fced5444686a330e09cc388fb": { - "balance": "194000000000000000000" - }, - "555df19390c16d01298772bae8bc3a1152199cbd": { - "balance": "200000000000000000000" - }, - "dc3dae59ed0fe18b58511e6fe2fb69b219689423": { - "balance": "100000000000000000000" - }, - "74648caac748dd135cd91ea14c28e1bd4d7ff6ae": { - "balance": "3100000000000000000000" - }, - "cf2e2ad635e9861ae95cb9bafcca036b5281f5ce": { - "balance": "35200000000000000000000" - }, - "14eec09bf03e352bd6ff1b1e876be664ceffd0cf": { - "balance": "20094000000000000000" - }, - "856e5ab3f64c9ab56b009393b01664fc0324050e": { - "balance": "1790000000000000000000" - }, - "632b9149d70178a7333634275e82d5953f27967b": { - "balance": "700000000000000000000" - }, - "2a39190a4fde83dfb3ddcb4c5fbb83ac6c49755c": { - "balance": "1000000000000000000000" - }, - "369ef761195f3a373e24ece6cd22520fe0b9e86e": { - "balance": "534933000000000000000" - }, - "16afa787fc9f94bdff6976b1a42f430a8bf6fb0f": { - "balance": "2000000000000000000000" - }, - "1b0b31afff4b6df3653a94d7c87978ae35f34aae": { - "balance": "354600000000000000000" - }, - "b4d82f2e69943f7de0f5f7743879406fac2e9cec": { - "balance": "40000000000000000000" - }, - "09d6cefd75b0c4b3f8f1d687a522c96123f1f539": { - "balance": "6000000000000000000000" - }, - "01577afd4e50890247c9b10d44af73229aec884f": { - "balance": "680000000000000000000" - }, - "a35606d51220ee7f2146d411582ee4ee4a45596e": { - "balance": "3996800000000000000000" - }, - "352e77c861696ef96ad54934f894aa8ea35151dd": { - "balance": "1000000000000000000000" - }, - "b87f5376c2de0b6cc3c179c06087aa473d6b4674": { - "balance": "1337000000000000000000" - }, - "5b49afcd75447838f6e7ceda8d21777d4fc1c3c0": { - "balance": "4000000000000000000000" - }, - "b884add88d83dc564ab8e0e02cbdb63919aea844": { - "balance": "2000000000000000000000" - }, - "5c312a56c784b122099b764d059c21ece95e84ca": { - "balance": "95000000000000000000" - }, - "4697baaf9ccb603fd30430689d435445e9c98bf5": { - "balance": "199600000000000000000" - }, - "c625f8c98d27a09a1bcabd5128b1c2a94856af30": { - "balance": "200000000000000000000" - }, - "19f5caf4c40e6908813c0745b0aea9586d9dd931": { - "balance": "664000000000000000000" - }, - "1e596a81b357c6f24970cc313df6dbdaabd0d09e": { - "balance": "2000000000000000000000" - }, - "c1631228efbf2a2e3a4092ee8900c639ed34fbc8": { - "balance": "955000000000000000000" - }, - "6f6cf20649a9e973177ac67dbadee4ebe5c7bdda": { - "balance": "5080000000000000000000" - }, - "5fa7bfe043886127d4011d8356a47e947963aca8": { - "balance": "1820000000000000000000" - }, - "6af8e55969682c715f48ad4fc0fbb67eb59795a3": { - "balance": "2000000000000000000000" - }, - "122f56122549d168a5c5e267f52662e5c5cce5c8": { - "balance": "185000000000000000000" - }, - "7713ab8037411c09ba687f6f9364f0d3239fac28": { - "balance": "10000000000000000000000" - }, - "31ccc616b3118268e75d9ab8996c8858ebd7f3c3": { - "balance": "399924000000000000000" - }, - "09c88f917e4d6ad473fa12e98ea3c4472a5ed6da": { - "balance": "10000000000000000000000" - }, - "e796fd4e839b4c95d7510fb7c5c72b83c6c3e3c7": { - "balance": "512200000000000000000" - }, - "a8285539869d88f8a961533755717d7eb65576ae": { - "balance": "200000000000000000000" - }, - "d929c65d69d5bbaea59762662ef418bc21ad924a": { - "balance": "1000000000000000000000" - }, - "f7418aa0e713d248228776b2e7434222ae75e3a5": { - "balance": "2000000000000000000000" - }, - "7f0b90a1fdd48f27b268feb38382e55ddb50ef0f": { - "balance": "940000000000000000000" - }, - "34a0431fff5ead927f3c69649616dc6e97945f6f": { - "balance": "400000000000000000000" - }, - "1b3cb81e51011b549d78bf720b0d924ac763a7c2": { - "balance": "560000000000000000000000" - }, - "155b3779bb6d56342e2fda817b5b2d81c7f41327": { - "balance": "50200000000000000000" - }, - "ecd486fc196791b92cf612d348614f9156488b7e": { - "balance": "12000000000000000000000" - }, - "82a8cbbfdff02b2e38ae4bbfca15f1f0e83b1aea": { - "balance": "84999000000000000000" - }, - "06b0c1e37f5a5ec4bbf50840548f9d3ac0288897": { - "balance": "4000098000000000000000" - }, - "e6d49f86c228f47367a35e886caacb271e539429": { - "balance": "412656000000000000000" - }, - "704a6eb41ba34f13addde7d2db7df04915c7a221": { - "balance": "1820000000000000000000" - }, - "745ccf2d819edbbddea8117b5c49ed3c2a066e93": { - "balance": "4000000000000000000000" - }, - "6d3b7836a2b9d899721a4d237b522385dce8dfcd": { - "balance": "1000070000000000000000" - }, - "856aa23c82d7215bec8d57f60ad75ef14fa35f44": { - "balance": "20000000000000000000000" - }, - "ea79057dabef5e64e7b44f7f18648e7e533718d2": { - "balance": "200000000000000000000" - }, - "9df057cd03a4e27e8e032f857985fd7f01adc8d7": { - "balance": "2000000000000000000000" - }, - "5f2f07d2d697e8c567fcfdfe020f49f360be2139": { - "balance": "2000000000000000000000" - }, - "5efbdfe5389999633c26605a5bfc2c1bb5959393": { - "balance": "69200000000000000000" - }, - "047e87c8f7d1fce3b01353a85862a948ac049f3e": { - "balance": "1490000000000000000000" - }, - "265383d68b52d034161bfab01ae1b047942fbc32": { - "balance": "21000600000000000000000" - }, - "760ff3354e0fde938d0fb5b82cef5ba15c3d2916": { - "balance": "10000000000000000000000" - }, - "bc46d537cf2edd403565bde733b2e34b215001bd": { - "balance": "20000000000000000000000" - }, - "ee58fb3db29070d0130188ce472be0a172b89055": { - "balance": "10021400000000000000000" - }, - "75abe5270f3a78ce007cf37f8fbc045d489b7bb1": { - "balance": "1999944000000000000000" - }, - "5fc6c11426b4a1eae7e51dd512ad1090c6f1a85b": { - "balance": "2730000000000000000000" - }, - "26cfffd052152bb3f957b478d5f98b233a7c2b92": { - "balance": "4000000000000000000000" - }, - "0a4a011995c681bc999fdd79754e9a324ae3b379": { - "balance": "41350300000000000000000" - }, - "6fa60df818a5446418b1bbd62826e0b9825e1318": { - "balance": "13200000000000000000000" - }, - "63d55ad99b9137fd1b20cc2b4f03d42cbaddf334": { - "balance": "400000000000000000000" - }, - "679b9a109930517e8999099ccf2a914c4c8dd934": { - "balance": "60000000000000000000" - }, - "3e83544f0082552572c782bee5d218f1ef064a9d": { - "balance": "100076000000000000000" - }, - "968b14648f018333687cd213fa640aec04ce6323": { - "balance": "1000000000000000000000" - }, - "427b462ab84e5091f48a46eb0cdc92ddcb26e078": { - "balance": "2000000000000000000000" - }, - "df8510793eee811c2dab1c93c6f4473f30fbef5b": { - "balance": "1000000000000000000000" - }, - "362fbcb10662370a068fc2652602a2577937cce6": { - "balance": "200000000000000000000" - }, - "5d83b21bd2712360436b67a597ee3378db3e7ae4": { - "balance": "2000000000000000000000" - }, - "5777441c83e03f0be8dd340bde636850847c620b": { - "balance": "10000000000000000000000" - }, - "c94a585203da7bbafd93e15884e660d4b1ead854": { - "balance": "7000000000000000000000" - }, - "35a08081799173e001cc5bd46a02406dc95d1787": { - "balance": "10000000000000000000000" - }, - "21d13f0c4024e967d9470791b50f22de3afecf1b": { - "balance": "4452210000000000000000" - }, - "fdfd6134c04a8ab7eb16f00643f8fed7daaaecb2": { - "balance": "400000000000000000000" - }, - "fd812bc69fb170ef57e2327e80affd14f8e4b6d2": { - "balance": "2000000000000000000000" - }, - "7148aef33261d8031fac3f7182ff35928daf54d9": { - "balance": "4100000000000000000000" - }, - "0b06390f2437b20ec4a3d3431b3279c6583e5ed7": { - "balance": "194000000000000000000" - }, - "4909b31998ead414b8fb0e846bd5cbde393935be": { - "balance": "4000000000000000000000" - }, - "b70dba9391682b4a364e77fe99256301a6c0bf1f": { - "balance": "200000000000000000000" - }, - "6b83bae7b565244558555bcf4ba8da2011891c17": { - "balance": "2000000000000000000000" - }, - "70a03549aa6168e97e88a508330a5a0bea74711a": { - "balance": "1337000000000000000000" - }, - "0fc9a0e34145fbfdd2c9d2a499b617d7a02969b9": { - "balance": "180000000000000000000" - }, - "2ddf40905769bcc426cb2c2938ffe077e1e89d98": { - "balance": "3000000000000000000000" - }, - "794b51c39e53d9e762b0613b829a44b472f4fff3": { - "balance": "667965000000000000000" - }, - "d062588171cf99bbeb58f126b870f9a3728d61ec": { - "balance": "4500000000000000000000" - }, - "8db185fe1b70a94a6a080e7e23a8bedc4acbf34b": { - "balance": "1400000000000000000000" - }, - "e73bfeada6f0fd016fbc843ebcf6e370a65be70c": { - "balance": "1970000000000000000000" - }, - "79ed10cf1f6db48206b50919b9b697081fbdaaf3": { - "balance": "2000000000000000000000" - }, - "276b0521b0e68b277df0bb32f3fd48326350bfb2": { - "balance": "50000000000000000000" - }, - "2e439348df8a4277b22a768457d1158e97c40904": { - "balance": "776970000000000000000" - }, - "6c25327f8dcbb2f45e561e86e35d8850e53ab059": { - "balance": "1103200000000000000000" - }, - "04d73896cf6593a691972a13a6e4871ff2c42b13": { - "balance": "2000000000000000000000" - }, - "b10fd2a647102f881f74c9fbc37da632949f2375": { - "balance": "40000000000000000000" - }, - "615f82365c5101f071e7d2cb6af14f7aad2c16c6": { - "balance": "20000000000000000000" - }, - "93aa8f92ebfff991fc055e906e651ac768d32bc8": { - "balance": "940000000000000000000" - }, - "0cbf8770f0d1082e5c20c5aead34e5fca9ae7ae2": { - "balance": "1000000000000000000000" - }, - "ffc9cc3094b041ad0e076f968a0de3b167255866": { - "balance": "432400000000000000000" - }, - "46531e8b1bde097fdf849d6d119885608a008df7": { - "balance": "200000000000000000000" - }, - "23cd2598a20e149ead2ad69379576ecedb60e38e": { - "balance": "2000000000000000000000" - }, - "85ca8bc6da2803d0725f5e1a456c89f9bc774e2f": { - "balance": "600000000000000000000" - }, - "c0725ec2bdc33a1d826071dea29d62d4385a8c25": { - "balance": "40740000000000000000000" - }, - "0e4765790352656bc656682c24fc5ef3e76a23c7": { - "balance": "46610000000000000000" - }, - "2ef9e465716acacfb8c8252fa8e7bc7969ebf6e4": { - "balance": "2760000000000000000000" - }, - "0ec5308b31282e218fc9e759d4fec5db3708cec4": { - "balance": "1001000000000000000000" - }, - "bf7701fc6225d5a17815438a8941d21ebc5d059d": { - "balance": "1880000000000000000000" - }, - "c489c83ffbb0252ac0dbe3521217630e0f491f14": { - "balance": "4000000000000000000000" - }, - "8eb51774af206b966b8909c45aa6722748802c0c": { - "balance": "500000000000000000000" - }, - "7b9226d46fe751940bc416a798b69ccf0dfab667": { - "balance": "4200000000000000000000" - }, - "8f660f8b2e4c7cc2b4ac9c47ed28508d5f8f8650": { - "balance": "20000000000000000000000" - }, - "9f19fac8a32437d80ac6837a0bb7841729f4972e": { - "balance": "650100000000000000000" - }, - "201864a8f784c2277b0b7c9ee734f7b377eab648": { - "balance": "4467000000000000000000" - }, - "a6101c961e8e1c15798ffcd0e3201d7786ec373a": { - "balance": "6000000000000000000000" - }, - "d4ff46203efa23064b1caf00516e28704a82a4f8": { - "balance": "1337000000000000000000" - }, - "aa136b47962bb8b4fb540db4ccf5fdd042ffb8cf": { - "balance": "500038000000000000000" - }, - "704ae21d762d6e1dde28c235d13104597236db1a": { - "balance": "2000000000000000000000" - }, - "f17a92e0361dbacecdc5de0d1894955af6a9b606": { - "balance": "2000000000000000000000" - }, - "8b48e19d39dd35b66e6e1bb6b9c657cb2cf59d04": { - "balance": "17844175000000000000000" - }, - "9ad47fdcf9cd942d28effd5b84115b31a658a13e": { - "balance": "3290000000000000000000" - }, - "df0d08617bd252a911df8bd41a39b83ddf809673": { - "balance": "10000000000000000000000" - }, - "4c666b86f1c5ee8ca41285f5bde4f79052081406": { - "balance": "500000000000000000000" - }, - "88dec5bd3f4eba2d18b8aacefa7b721548c319ba": { - "balance": "1370000000000000000000" - }, - "9f9fe0c95f10fee87af1af207236c8f3614ef02f": { - "balance": "6000000000000000000000" - }, - "f7d0d310acea18406138baaabbfe0571e80de85f": { - "balance": "1337000000000000000000" - }, - "9569c63a9284a805626db3a32e9d236393476151": { - "balance": "1970000000000000000000" - }, - "5d5c2c1099bbeefb267e74b58880b444d94449e0": { - "balance": "253574000000000000000" - }, - "8c6ae7a05a1de57582ae2768204276c0ff47ed03": { - "balance": "208000000000000000000000" - }, - "432d884bd69db1acc0d89c64ade4cb4fc3a88b7a": { - "balance": "2483000000000000000000" - }, - "672cbca8440a8577097b19aff593a2ad9d28a756": { - "balance": "80000000000000000000" - }, - "19df9445a81c1b3d804aeaeb6f6e204e4236663f": { - "balance": "37387000000000000000" - }, - "1cb5f33b4d488936d13e3161da33a1da7df70d1b": { - "balance": "200000000000000000000" - }, - "df60f18c812a11ed4e2776e7a80ecf5e5305b3d6": { - "balance": "900000000000000000000" - }, - "c99a9cd6c9c1be3534eecd92ecc22f5c38e9515b": { - "balance": "4821030000000000000000" - }, - "00c40fe2095423509b9fd9b754323158af2310f3": { - "balance": "0" - }, - "da4a5f557f3bab390a92f49b9b900af30c46ae80": { - "balance": "10000000000000000000000" - }, - "f36df02fbd89607347afce2969b9c4236a58a506": { - "balance": "2000000000000000000000" - }, - "c549df83c6f65eec0f1dc9a0934a5c5f3a50fd88": { - "balance": "2910000000000000000000" - }, - "9f662e95274121f177566e636d23964cf1fd686f": { - "balance": "2000000000000000000000" - }, - "5a267331facb262daaecd9dd63a9700c5f5259df": { - "balance": "100000000000000000000" - }, - "117d9aa3c4d13bee12c7500f09f5dd1c66c46504": { - "balance": "206000000000000000000" - }, - "1b4d07acd38183a61bb2783d2b7b178dd502ac8d": { - "balance": "200000000000000000000" - }, - "3c0c3defac9cea7acc319a96c30b8e1fedab4574": { - "balance": "1940000000000000000000" - }, - "e4dc22ed595bf0a337c01e03cc6be744255fc9e8": { - "balance": "191000000000000000000" - }, - "8f067c7c1bbd57780b7b9eeb9ec0032f90d0dcf9": { - "balance": "20000000000000000000000" - }, - "40e2440ae142c880366a12c6d4102f4b8434b62a": { - "balance": "1000000000000000000000" - }, - "f9ece022bccd2c92346911e79dd50303c01e0188": { - "balance": "1000000000000000000000" - }, - "f70328ef97625fe745faa49ee0f9d4aa3b0dfb69": { - "balance": "1000000000000000000000" - }, - "b6aacb8cb30bab2ae4a2424626e6e12b02d04605": { - "balance": "8000000000000000000000" - }, - "154459fa2f21318e3434449789d826cdc1570ce5": { - "balance": "2000000000000000000000" - }, - "684a44c069339d08e19a75668bdba303be855332": { - "balance": "70000000000000000000000" - }, - "9fe501aa57ead79278937cd6308c5cfa7a5629fe": { - "balance": "50003000000000000000" - }, - "3e45bd55db9060eced923bb9cb733cb3573fb531": { - "balance": "1640000000000000000000" - }, - "9c9f3b8a811b21f3ff3fe20fe970051ce66a824f": { - "balance": "1157740000000000000000" - }, - "e99aece90541cae224b87da673965e0aeb296afd": { - "balance": "920000000000000000000" - }, - "2f6dce1330c59ef921602154572d4d4bacbd048a": { - "balance": "1000000000000000000000" - }, - "6a6353b971589f18f2955cba28abe8acce6a5761": { - "balance": "3000000000000000000000" - }, - "98c10ebf2c4f97cba5a1ab3f2aafe1cac423f8cb": { - "balance": "300000000000000000000" - }, - "8077c3e4c445586e094ce102937fa05b737b568c": { - "balance": "100000000000000000000" - }, - "13371f92a56ea8381e43059a95128bdc4d43c5a6": { - "balance": "1000000000000000000000" - }, - "35a6885083c899dabbf530ed6c12f4dd3a204cf5": { - "balance": "200000000000000000000" - }, - "36b2c85e3aeeebb70d63c4a4730ce2e8e88a3624": { - "balance": "10000000000000000000000" - }, - "5ce44068b8f4a3fe799e6a8311dbfdeda29dee0e": { - "balance": "2000000000000000000000" - }, - "6fa6388d402b30afe59934c3b9e13d1186476018": { - "balance": "670000000000000000000" - }, - "8251358ca4e060ddb559ca58bc0bddbeb4070203": { - "balance": "2000000000000000000000" - }, - "17e86f3b5b30c0ba59f2b2e858425ba89f0a10b0": { - "balance": "2000000000000000000000" - }, - "298ec76b440d8807b3f78b5f90979bee42ed43db": { - "balance": "30000000000000000000000" - }, - "ce4b065dbcb23047203262fb48c1188364977470": { - "balance": "500000000000000000000" - }, - "c8e2adeb545e499d982c0c117363ceb489c5b11f": { - "balance": "985000000000000000000" - }, - "9928ff715afc3a2b60f8eb4cc4ba4ee8dab6e59d": { - "balance": "440000000000000000000" - }, - "c76130c73cb9210238025c9df95d0be54ac67fbe": { - "balance": "1500000000000000000000" - }, - "72d03d4dfab3500cf89b86866f15d4528e14a195": { - "balance": "4488000000000000000000" - }, - "d193e583d6070563e7b862b9614a47e99489f3e5": { - "balance": "999972000000000000000" - }, - "4df140ba796585dd5489315bca4bba680adbb818": { - "balance": "2674000000000000000000" - }, - "009eef0a0886056e3f69211853b9b7457f3782e4": { - "balance": "3000512000000000000000" - }, - "6e255b700ae7138a4bacf22888a9e2c00a285eec": { - "balance": "4000000000000000000000" - }, - "aa47a4ffc979363232c99b99fada0f2734b0aeee": { - "balance": "8121800000000000000000" - }, - "9d069197d1de50045a186f5ec744ac40e8af91c6": { - "balance": "2000000000000000000000" - }, - "b514882c979bb642a80dd38754d5b8c8296d9a07": { - "balance": "955000000000000000000" - }, - "17c0478657e1d3d17aaa331dd429cecf91f8ae5d": { - "balance": "999942000000000000000" - }, - "5f9616c47b4a67f406b95a14fe6fc268396f1721": { - "balance": "200000000000000000000" - }, - "f70a998a717b338d1dd99854409b1a338deea4b0": { - "balance": "2000000000000000000000" - }, - "d1ee905957fe7cc70ec8f2868b43fe47b13febff": { - "balance": "44000000000000000000" - }, - "fc018a690ad6746dbe3acf9712ddca52b6250039": { - "balance": "10000000000000000000000" - }, - "5118557d600d05c2fcbf3806ffbd93d02025d730": { - "balance": "11360000000000000000000" - }, - "1ef5c9c73650cfbbde5c885531d427c7c3fe5544": { - "balance": "6000000000000000000000" - }, - "d1a396dcdab2c7494130b3fd307820340dfd8c1f": { - "balance": "17952000000000000000" - }, - "2d8e061892a5dcce21966ae1bb0788fd3e8ba059": { - "balance": "250066000000000000000" - }, - "8834b2453471f324fb26be5b25166b5b5726025d": { - "balance": "573000000000000000000" - }, - "14f221159518783bc4a706676fc4f3c5ee405829": { - "balance": "200000000000000000000" - }, - "c056d4bd6bf3cbacac65f8f5a0e3980b852740ae": { - "balance": "100000000000000000000" - }, - "560536794a9e2b0049d10233c41adc5f418a264a": { - "balance": "1000000000000000000000" - }, - "bc9e0ec6788f7df4c7fc210aacd220c27e45c910": { - "balance": "500000000000000000000" - }, - "54bcb8e7f73cda3d73f4d38b2d0847e600ba0df8": { - "balance": "1078000000000000000000" - }, - "4361d4846fafb377b6c0ee49a596a78ddf3516a3": { - "balance": "3580000000000000000000" - }, - "41c3c2367534d13ba2b33f185cdbe6ac43c2fa31": { - "balance": "4000000000000000000000" - }, - "5dc6f45fef26b06e3302313f884daf48e2746fb9": { - "balance": "500000000000000000000" - }, - "ad414d29cb7ee973fec54e22a388491786cf5402": { - "balance": "14000000000000000000000" - }, - "802dc3c4ff2d7d925ee2859f4a06d7ba60f1308c": { - "balance": "98040000000000000000" - }, - "2aed2ce531c056b0097efc3c6de10c4762004ed9": { - "balance": "10430000000000000000000" - }, - "39782ffe06ac78822a3c3a8afe305e50a56188ce": { - "balance": "10000000000000000000000" - }, - "ec73833de4b810bb027810fc8f69f544e83c12d1": { - "balance": "1000000000000000000000" - }, - "8d51a4cc62011322c696fd725b9fb8f53feaaa07": { - "balance": "1000000000000000000000" - }, - "29298ccbdff689f87fe41aa6e98fdfb53deaf37a": { - "balance": "19800000000000000000000" - }, - "827531a6c5817ae35f82b00b9754fcf74c55e232": { - "balance": "3600000000000000000000" - }, - "9c581a60b61028d934167929b22d70b313c34fd0": { - "balance": "50000000000000000000000" - }, - "0a077db13ffeb09484c217709d5886b8bf9c5a8b": { - "balance": "4000000000000000000000" - }, - "07b7a57033f8f11330e4665e185d234e83ec140b": { - "balance": "4325683000000000000000" - }, - "17f523f117bc9fe978aa481eb4f5561711371bc8": { - "balance": "1999884000000000000000" - }, - "de42fcd24ce4239383304367595f068f0c610740": { - "balance": "45120000000000000000" - }, - "2a46d353777176ff8e83ffa8001f4f70f9733aa5": { - "balance": "106000000000000000000" - }, - "92e4392816e5f2ef5fb65837cec2c2325cc64922": { - "balance": "10000000000000000000000" - }, - "9a3da65023a13020d22145cfc18bab10bd19ce4e": { - "balance": "456516000000000000000" - }, - "1a085d43ec92414ea27b914fe767b6d46b1eef44": { - "balance": "29550000000000000000000" - }, - "3b2367f8494b5fe18d683c055d89999c9f3d1b34": { - "balance": "10000000000000000000000" - }, - "84244fc95a6957ed7c1504e49f30b8c35eca4b79": { - "balance": "2000000000000000000000" - }, - "5e031b0a724471d476f3bcd2eb078338bf67fbef": { - "balance": "18200000000000000000" - }, - "97e5cc6127c4f885be02f44b42d1c8b0ac91e493": { - "balance": "200000000000000000000" - }, - "eb1cea7b45d1bd4d0e2a007bd3bfb354759e2c16": { - "balance": "198000000000000000000" - }, - "72feaf124579523954645b7fafff0378d1c8242e": { - "balance": "1000000000000000000000" - }, - "8d07d42d831c2d7c838aa1872b3ad5d277176823": { - "balance": "349200000000000000000" - }, - "9637dc12723d9c78588542eab082664f3f038d9d": { - "balance": "1000000000000000000000" - }, - "e84b55b525f1039e744b918cb3332492e45eca7a": { - "balance": "200000000000000000000" - }, - "b1d6b01b94d854fe8b374aa65e895cf22aa2560e": { - "balance": "940000000000000000000" - }, - "8161d940c3760100b9080529f8a60325030f6edc": { - "balance": "300000000000000000000" - }, - "d30ee9a12b4d68abace6baca9ad7bf5cd1faf91c": { - "balance": "1499936000000000000000" - }, - "057949e1ca0570469e4ce3c690ae613a6b01c559": { - "balance": "200000000000000000000" - }, - "4bf8e26f4c2790da6533a2ac9abac3c69a199433": { - "balance": "200000000000000000000" - }, - "36fec62c2c425e219b18448ad757009d8c54026f": { - "balance": "400000000000000000000" - }, - "77bfe93ccda750847e41a1affee6b2da96e7214e": { - "balance": "300000000000000000000" - }, - "cc48414d2ac4d42a5962f29eee4497092f431352": { - "balance": "161000000000000000000" - }, - "ddbddd1bbd38ffade0305d30f02028d92e9f3aa8": { - "balance": "2000000000000000000000" - }, - "30c01142907acb1565f70438b9980ae731818738": { - "balance": "2000000000000000000000" - }, - "cffc49c1787eebb2b56cabe92404b636147d4558": { - "balance": "5679305000000000000000" - }, - "f99eeece39fa7ef5076d855061384009792cf2e0": { - "balance": "500000000000000000000" - }, - "e9b6a790009bc16642c8d820b7cde0e9fd16d8f5": { - "balance": "3640000000000000000000" - }, - "03b41b51f41df20dd279bae18c12775f77ad771c": { - "balance": "1000000000000000000000" - }, - "787d313fd36b053eeeaedbce74b9fb0678333289": { - "balance": "27160000000000000000000" - }, - "35d2970f49dcc81ea9ee707e9c8a0ab2a8bb7463": { - "balance": "1440000000000000000000" - }, - "4c0aca508b3caf5ee028bc707dd1e800b838f453": { - "balance": "18200000000000000000" - }, - "514632efbd642c04de6ca342315d40dd90a2dba6": { - "balance": "2674000000000000000000" - }, - "36810ff9d213a271eda2b8aa798be654fa4bbe06": { - "balance": "2000000000000000000000" - }, - "0c088006c64b30c4ddafbc36cb5f05469eb62834": { - "balance": "2000000000000000000000" - }, - "568df31856699bb5acfc1fe1d680df9960ca4359": { - "balance": "1379999000000000000000" - }, - "d48e3f9357e303513841b3f84bda83fc89727587": { - "balance": "1000000000000000000000" - }, - "953ef652e7b769f53d6e786a58952fa93ee6abe7": { - "balance": "2860000000000000000000" - }, - "7c60a05f7a4a5f8cf2784391362e755a8341ef59": { - "balance": "1892300000000000000000" - }, - "7a6b26f438d9a352449155b8876cbd17c9d99b64": { - "balance": "6000000000000000000000" - }, - "68f719ae342bd7fef18a05cbb02f705ad38ed5b2": { - "balance": "1050000000000000000000" - }, - "45ca8d956608f9e00a2f9974028640888465668f": { - "balance": "2000000000000000000000" - }, - "3eaf316b87615d88f7adc77c58e712ed4d77966b": { - "balance": "100141000000000000000" - }, - "1f0412bfedcd964e837d092c71a5fcbaf30126e2": { - "balance": "20000000000000000000" - }, - "7471f72eeb300624eb282eab4d03723c649b1b58": { - "balance": "8000000000000000000000" - }, - "9bf71f7fb537ac54f4e514947fa7ff6728f16d2f": { - "balance": "33400000000000000000" - }, - "1098c774c20ca1daac5ddb620365316d353f109c": { - "balance": "100000000000000000000" - }, - "7dd8d7a1a34fa1f8e73ccb005fc2a03a15b8229c": { - "balance": "200000000000000000000" - }, - "0151fa5d17a2dce2d7f1eb39ef7fe2ad213d5d89": { - "balance": "4000000000000000000000" - }, - "ad6628352ed3390bafa86d923e56014cfcb360f4": { - "balance": "2000000000000000000000" - }, - "02af2459a93d0b3f4d062636236cd4b29e3bcecf": { - "balance": "1910000000000000000000" - }, - "ace2abb63b0604409fbde3e716d2876d44e8e5dd": { - "balance": "152000000000000000000" - }, - "e710dcd09b8101f9437bd97db90a73ef993d0bf4": { - "balance": "386100000000000000000" - }, - "d43ee438d83de9a37562bb4e286cb1bd19f4964d": { - "balance": "1000000000000000000000" - }, - "ea3779d14a13f6c78566bcde403591413a6239db": { - "balance": "197000000000000000000000" - }, - "6704f169e0d0b36b57bbc39f3c45437b5ee3d28d": { - "balance": "394000000000000000000" - }, - "5584423050e3c2051f0bbd8f44bd6dbc27ecb62c": { - "balance": "3000000000000000000000" - }, - "2f315d9016e8ee5f536681202f9084b032544d4d": { - "balance": "1037400000000000000000" - }, - "e1b63201fae1f129f95c7a116bd9dde5159c6cda": { - "balance": "22837462000000000000000" - }, - "2bbe62eac80ca7f4d6fdee7e7d8e28b63acf770e": { - "balance": "2396000000000000000000" - }, - "38da1ba2de9e2c954b092dd9d81204fd016ba016": { - "balance": "10156000000000000000000" - }, - "8a86e4a51c013b1fb4c76bcf30667c78d52eedef": { - "balance": "2000000000000000000000" - }, - "8f717ec1552f4c440084fba1154a81dc003ebdc0": { - "balance": "10000000000000000000000" - }, - "c760971bbc181c6a7cf77441f24247d19ce9b4cf": { - "balance": "2000000000000000000000" - }, - "7f150afb1a77c2b45928c268c1e9bdb4641d47d8": { - "balance": "2000000000000000000000" - }, - "1ea334b5750807ea74aac5ab8694ec5f28aa77cf": { - "balance": "492500000000000000000" - }, - "2afb058c3d31032b353bf24f09ae20d54de57dbe": { - "balance": "1100000000000000000000" - }, - "caef027b1ab504c73f41f2a10979b474f97e309f": { - "balance": "200000000000000000000" - }, - "5dd112f368c0e6ceff77a9df02a5481651a02fb7": { - "balance": "169800000000000000000" - }, - "bd93e550403e2a06113ed4c3fba1a8913b19407e": { - "balance": "2000000000000000000000" - }, - "500c16352e901d48ba8d04e2c767121772790b02": { - "balance": "30239000000000000000" - }, - "d2a80327cbe55c4c7bd51ff9dde4ca648f9eb3f8": { - "balance": "50000000000000000000" - }, - "355ccfe0e77d557b971be1a558bc02df9eee0594": { - "balance": "1759120000000000000000" - }, - "5aed0e6cfe95f9d680c76472a81a2b680a7f93e2": { - "balance": "197000000000000000000" - }, - "f56442f60e21691395d0bffaa9194dcaff12e2b7": { - "balance": "260000000000000000000" - }, - "7db9eacc52e429dc83b461c5f4d86010e5383a28": { - "balance": "1000000000000000000000" - }, - "4b984ef26c576e815a2eaed2f5177f07dbb1c476": { - "balance": "1560000000000000000000" - }, - "9846648836a307a057184fd51f628a5f8c12427c": { - "balance": "19100000000000000000000" - }, - "4af0db077bb9ba5e443e21e148e59f379105c592": { - "balance": "600000000000000000000" - }, - "e96e2d3813efd1165f12f602f97f4a62909d3c66": { - "balance": "2300000000000000000000" - }, - "30e789b3d2465e946e6210fa5b35de4e8c93085f": { - "balance": "2000000000000000000000" - }, - "97f99b6ba31346cd98a9fe4c308f87c5a58c5151": { - "balance": "6000000000000000000000" - }, - "595e23d788a2d4bb85a15df7136d264a635511b3": { - "balance": "3940000000000000000000" - }, - "2f61efa5819d705f2b1e4ee754aeb8a819506a75": { - "balance": "1460000000000000000000" - }, - "3554947b7b947b0040da52ca180925c6d3b88ffe": { - "balance": "66850000000000000000" - }, - "8feffadb387a1547fb284da9b8147f3e7c6dc6da": { - "balance": "837200000000000000000" - }, - "258939bbf00c9de9af5338f5d714abf6d0c1c671": { - "balance": "1550000000000000000000" - }, - "5b333696e04cca1692e71986579c920d6b2916f9": { - "balance": "500000000000000000000" - }, - "5381448503c0c702542b1de7cc5fb5f6ab1cf6a5": { - "balance": "8000000000000000000000" - }, - "7e81f6449a03374191f3b7cb05d938b72e090dff": { - "balance": "100000000000000000000" - }, - "4ef1c214633ad9c0703b4e2374a2e33e3e429291": { - "balance": "1337000000000000000000" - }, - "fed8476d10d584b38bfa6737600ef19d35c41ed8": { - "balance": "1820000000000000000000" - }, - "1a95c9b7546b5d1786c3858fb1236446bc0ca4ce": { - "balance": "1970000000000000000000" - }, - "3b07db5a357f5af2484cbc9d77d73b1fd0519fc7": { - "balance": "500000000000000000000" - }, - "5f68a24c7eb4117667737b33393fb3c2148a53b6": { - "balance": "51800000000000000000" - }, - "d8f665fd8cd5c2bcc6ddc0a8ae521e4dc6aa6060": { - "balance": "1700000000000000000000" - }, - "d66acc0d11b689cea6d9ea5ff4014c224a5dc7c4": { - "balance": "18200000000000000000" - }, - "6e72b2a1186a8e2916543b1cb36a68870ea5d197": { - "balance": "186000000000000000000" - }, - "5102a4a42077e11c58df4773e3ac944623a66d9f": { - "balance": "2000325000000000000000" - }, - "72480bede81ad96423f2228b5c61be44fb523100": { - "balance": "6400000000000000000000" - }, - "e076db30ab486f79194ebbc45d8fab9a9242f654": { - "balance": "4840000000000000000000" - }, - "8ceea15eec3bdad8023f98ecf25b2b8fef27db29": { - "balance": "2000000000000000000000" - }, - "40652360d6716dc55cf9aab21f3482f816cc2cbd": { - "balance": "10000000000000000000000" - }, - "13e02fb448d6c84ae17db310ad286d056160da95": { - "balance": "2000000000000000000000" - }, - "d6598b1386e93c5ccb9602ff4bbbecdbd3701dc4": { - "balance": "224096000000000000000" - }, - "d5ea472cb9466018110af00c37495b5c2c713112": { - "balance": "4997800000000000000000" - }, - "bb75cb5051a0b0944b4673ca752a97037f7c8c15": { - "balance": "200000000000000000000" - }, - "8af626a5f327d7506589eeb7010ff9c9446020d2": { - "balance": "1400000000000000000000" - }, - "318c76ecfd8af68d70555352e1f601e35988042d": { - "balance": "501600000000000000000" - }, - "5c3d19441d196cb443662020fcad7fbb79b29e78": { - "balance": "14300000000000000000" - }, - "27101a0f56d39a88c5a84f9b324cdde33e5cb68c": { - "balance": "2000000000000000000000" - }, - "e229e746a83f2ce253b0b03eb1472411b57e5700": { - "balance": "5730000000000000000000" - }, - "604cdf18628dbfa8329194d478dd5201eecc4be7": { - "balance": "23000000000000000000" - }, - "657473774f63ac3d6279fd0743d5790c4f161503": { - "balance": "200000000000000000000" - }, - "1ddefefd35ab8f658b2471e54790bc17af98dea4": { - "balance": "1000000000000000000000" - }, - "ac3900298dd14d7cc96d4abb428da1bae213ffed": { - "balance": "24730250000000000000000" - }, - "944f07b96f90c5f0d7c0c580533149f3f585a078": { - "balance": "74000000000000000000" - }, - "232c6d03b5b6e6711efff190e49c28eef36c82b0": { - "balance": "1337000000000000000000" - }, - "c87c77e3c24adecdcd1038a38b56e18dead3b702": { - "balance": "8800000000000000000000" - }, - "c4b6e5f09cc1b90df07803ce3d4d13766a9c46f4": { - "balance": "6000000000000000000000" - }, - "d44334b4e23a169a0c16bd21e866bba52d970587": { - "balance": "2600000000000000000000" - }, - "7757a4b9cc3d0247ccaaeb9909a0e56e1dd6dcc2": { - "balance": "20000000000000000000" - }, - "cf694081c76d18c64ca71382be5cd63b3cb476f8": { - "balance": "1000000000000000000000" - }, - "133e4f15e1e39c53435930aaedf3e0fe56fde843": { - "balance": "20000000000000000000" - }, - "f067fb10dfb293e998abe564c055e3348f9fbf1e": { - "balance": "2000000000000000000000" - }, - "94449c01b32a7fa55af8104f42cdd844aa8cbc40": { - "balance": "16548000000000000000000" - }, - "0e2094ac1654a46ba1c4d3a40bb8c17da7f39688": { - "balance": "358000000000000000000" - }, - "738ca94db7ce8be1c3056cd6988eb376359f3353": { - "balance": "25500000000000000000000" - }, - "0cfb172335b16c87d519cd1475530d20577f5e0e": { - "balance": "100000000000000000000000" - }, - "3cb561ce86424b359891e364ec925ffeff277df7": { - "balance": "200000000000000000000" - }, - "5f981039fcf50225e2adf762752112d1cc26b6e3": { - "balance": "499954000000000000000" - }, - "b43657a50eecbc3077e005d8f8d94f377876bad4": { - "balance": "35460000000000000000" - }, - "d07e511864b1cf9969e3560602829e32fc4e71f5": { - "balance": "50000000000000000000" - }, - "11306c7d57588637780fc9fde8e98ecb008f0164": { - "balance": "1999944000000000000000" - }, - "45ca9862003b4e40a3171fb5cafa9028cac8de19": { - "balance": "13790000000000000000000" - }, - "231d94155dbcfe2a93a319b6171f63b20bd2b6fa": { - "balance": "3819952000000000000000" - }, - "e7533e270cc61fa164ac1553455c105d04887e14": { - "balance": "121550000000000000000" - }, - "070d5d364cb7bbf822fc2ca91a35bdd441b215d5": { - "balance": "2000000000000000000000" - }, - "d475477fa56390d33017518d6711027f05f28dbf": { - "balance": "1975032000000000000000" - }, - "cea34a4dd93dd9aefd399002a97d997a1b4b89cd": { - "balance": "1500000000000000000000" - }, - "560becdf52b71f3d8827d927610f1a980f33716f": { - "balance": "429413000000000000000" - }, - "f632adff490da4b72d1236d04b510f74d2faa3cd": { - "balance": "1400000000000000000000" - }, - "2fdd9b79df8df530ad63c20e62af431ae99216b8": { - "balance": "21000000000000000000" - }, - "535201a0a1d73422801f55ded4dfaee4fbaa6e3b": { - "balance": "39641000000000000000" - }, - "409d5a962edeeebea178018c0f38b9cdb213f289": { - "balance": "20000000000000000000" - }, - "9d911f3682f32fe0792e9fb6ff3cfc47f589fca5": { - "balance": "4000000000000000000000" - }, - "9f7a0392f857732e3004a375e6b1068d49d83031": { - "balance": "2000000000000000000000" - }, - "6a04f5d53fc0f515be942b8f12a9cb7ab0f39778": { - "balance": "3129800000000000000000" - }, - "be478e8e3dde6bd403bb2d1c657c4310ee192723": { - "balance": "492500000000000000000" - }, - "007622d84a234bb8b078230fcf84b67ae9a8acae": { - "balance": "698800000000000000000" - }, - "9475c510ec9a26979247744c3d8c3b0e0b5f44d3": { - "balance": "10000000000000000000000" - }, - "df47a8ef95f2f49f8e6f58184154145d11f72797": { - "balance": "1910000000000000000000" - }, - "13ce332dff65a6ab933897588aa23e000980fa82": { - "balance": "258400000000000000000" - }, - "9c4bbcd5f1644a6f075824ddfe85c571d6abf69c": { - "balance": "1800000000000000000000" - }, - "d42b20bd0311608b66f8a6d15b2a95e6de27c5bf": { - "balance": "2000000000000000000000" - }, - "a4dd59ab5e517d398e49fa537f899fed4c15e95d": { - "balance": "20000000000000000000000" - }, - "1a8a5ce414de9cd172937e37f2d59cff71ce57a0": { - "balance": "10000000000000000000000" - }, - "55c564664166a1edf3913e0169f1cd451fdb5d0c": { - "balance": "2399800000000000000000" - }, - "58ae2ddc5f4c8ada97e06c0086171767c423f5d7": { - "balance": "1610000000000000000000" - }, - "fb79abdb925c55b9f98efeef64cfc9eb61f51bb1": { - "balance": "1794000000000000000000" - }, - "e7a42f59fee074e4fb13ea9e57ecf1cc48282249": { - "balance": "20000000000000000000000" - }, - "07e2b4cdeed9d087b12e556d9e770c13c099615f": { - "balance": "668500000000000000000" - }, - "68473b7a7d965904bedba556dfbc17136cd5d434": { - "balance": "100000000000000000000" - }, - "6c5c3a54cda7c2f118edba434ed81e6ebb11dd7a": { - "balance": "200000000000000000000" - }, - "24c117d1d2b3a97ab11a4679c99a774a9eade8d1": { - "balance": "1000000000000000000000" - }, - "f68c5e33fa97139df5b2e63886ce34ebf3e4979c": { - "balance": "3320000000000000000000" - }, - "bd7419dc2a090a46e2873d7de6eaaad59e19c479": { - "balance": "6802000000000000000000" - }, - "1a0a1ddfb031e5c8cc1d46cf05842d50fddc7130": { - "balance": "1000000000000000000000" - }, - "2b3a68db6b0cae8a7c7a476bdfcfbd6205e10687": { - "balance": "2400000000000000000000" - }, - "426d15f407a01135b13a6b72f8f2520b3531e302": { - "balance": "20000000000000000000" - }, - "0394b90fadb8604f86f43fc1e35d3124b32a5989": { - "balance": "764000000000000000000" - }, - "7412c9bc30b4df439f023100e63924066afd53af": { - "balance": "500000000000000000000" - }, - "80e7b3205230a566a1f061d922819bb4d4d2a0e1": { - "balance": "14000000000000000000000" - }, - "ff4fc66069046c525658c337a917f2d4b832b409": { - "balance": "2000000000000000000000" - }, - "f5061ee2e5ee26b815503677130e1de07a52db07": { - "balance": "100000000000000000000" - }, - "49793463e1681083d6abd6e725d5bba745dccde8": { - "balance": "545974000000000000000" - }, - "23551f56975fe92b31fa469c49ea66ee6662f41e": { - "balance": "1910000000000000000000" - }, - "fad96ab6ac768ad5099452ac4777bd1a47edc48f": { - "balance": "100000000000000000000" - }, - "2a746cd44027af3ebd37c378c85ef7f754ab5f28": { - "balance": "394000000000000000000" - }, - "b8d389e624a3a7aebce4d3e5dbdf6cdc29932aed": { - "balance": "200000000000000000000" - }, - "7b761feb7fcfa7ded1f0eb058f4a600bf3a708cb": { - "balance": "4600000000000000000000" - }, - "5435c6c1793317d32ce13bba4c4ffeb973b78adc": { - "balance": "250070000000000000000" - }, - "dd04eee74e0bf30c3f8d6c2c7f52e0519210df93": { - "balance": "80000000000000000000" - }, - "4331ab3747d35720a9d8ca25165cd285acd4bda8": { - "balance": "2000000000000000000000" - }, - "b84c8b9fd33ece00af9199f3cf5fe0cce28cd14a": { - "balance": "3820000000000000000000" - }, - "393f783b5cdb86221bf0294fb714959c7b45899c": { - "balance": "5910000000000000000000" - }, - "259ec4d265f3ab536b7c70fa97aca142692c13fc": { - "balance": "20400000000000000000" - }, - "5d2f7f0b04ba4be161e19cb6f112ce7a5e7d7fe4": { - "balance": "35200000000000000000" - }, - "d54ba2d85681dc130e5b9b02c4e8c851391fd9b9": { - "balance": "3940000000000000000000" - }, - "5cd8af60de65f24dc3ce5730ba92653022dc5963": { - "balance": "1790000000000000000000" - }, - "3b42a66d979f582834747a8b60428e9b4eeccd23": { - "balance": "620400000000000000000" - }, - "4b19eb0c354bc1393960eb06063b83926f0d67b2": { - "balance": "29000000000000000000" - }, - "8cf3546fd1cda33d58845fc8fcfecabca7c5642a": { - "balance": "574027000000000000000" - }, - "113612bc3ba0ee4898b49dd20233905f2f458f62": { - "balance": "14000000000000000000000" - }, - "1f2afc0aed11bfc71e77a907657b36ea76e3fb99": { - "balance": "4000000000000000000000" - }, - "03714b41d2a6f751008ef8dd4d2b29aecab8f36e": { - "balance": "6000000000000000000000" - }, - "25721c87b0dc21377c7200e524b14a22f0af69fb": { - "balance": "4000000000000000000000" - }, - "335858f749f169cabcfe52b796e3c11ec47ea3c2": { - "balance": "200000000000000000000" - }, - "52fb46ac5d00c3518b2c3a1c177d442f8165555f": { - "balance": "1500000000000000000000" - }, - "7a8c89c014509d56d7b68130668ff6a3ecec7370": { - "balance": "300000000000000000000" - }, - "7d5d2f73949dadda0856b206989df0078d51a1e5": { - "balance": "10560000000000000000000" - }, - "be538246dd4e6f0c20bf5ad1373c3b463a131e86": { - "balance": "200000000000000000000" - }, - "62680a15f8ccb8bdc02f7360c25ad8cfb57b8ccd": { - "balance": "1000000000000000000000" - }, - "aa0ca3737337178a0caac3099c584b056c56301c": { - "balance": "880000000000000000000" - }, - "1d341fa5a3a1bd051f7db807b6db2fc7ba4f9b45": { - "balance": "18200000000000000000" - }, - "6463f715d594a1a4ace4bb9c3b288a74decf294d": { - "balance": "1970000000000000000000" - }, - "e00d153b10369143f97f54b8d4ca229eb3e8f324": { - "balance": "152000000000000000000" - }, - "8d0b9ea53fd263415eac11391f7ce9123c447062": { - "balance": "2000000000000000000000" - }, - "cacb675e0996235404efafbb2ecb8152271b55e0": { - "balance": "700000000000000000000" - }, - "b615e940143eb57f875893bc98a61b3d618c1e8c": { - "balance": "20000000000000000000" - }, - "606f177121f7855c21a5062330c8762264a97b31": { - "balance": "4000000000000000000000" - }, - "e3925509c8d0b2a6738c5f6a72f35314491248ce": { - "balance": "1012961000000000000000" - }, - "3f08d9ad894f813e8e2148c160d24b353a8e74b0": { - "balance": "60000000000000000000000" - }, - "40f4f4c06c732cd35b119b893b127e7d9d0771e4": { - "balance": "10000000000000000000000" - }, - "1406854d149e081ac09cb4ca560da463f3123059": { - "balance": "1337000000000000000000" - }, - "ecf05d07ea026e7ebf4941002335baf2fed0f002": { - "balance": "200000000000000000000" - }, - "9a990b8aeb588d7ee7ec2ed8c2e64f7382a9fee2": { - "balance": "33518000000000000000" - }, - "a2e0683a805de6a05edb2ffbb5e96f0570b637c3": { - "balance": "20000000000000000000" - }, - "fba5486d53c6e240494241abf87e43c7600d413a": { - "balance": "1987592000000000000000" - }, - "d81bd54ba2c44a6f6beb1561d68b80b5444e6dc6": { - "balance": "1163806000000000000000" - }, - "5298ab182a19359ffcecafd7d1b5fa212dede6dd": { - "balance": "20000000000000000000" - }, - "d1acb5adc1183973258d6b8524ffa28ffeb23de3": { - "balance": "4000000000000000000000" - }, - "4e7aa67e12183ef9d7468ea28ad239c2eef71b76": { - "balance": "4925000000000000000000" - }, - "509a20bc48e72be1cdaf9569c711e8648d957334": { - "balance": "2000000000000000000000" - }, - "949f84f0b1d7c4a7cf49ee7f8b2c4a134de32878": { - "balance": "685000000000000000000" - }, - "edbac9527b54d6df7ae2e000cca3613ba015cae3": { - "balance": "1970000000000000000000" - }, - "c697b70477cab42e2b8b266681f4ae7375bb2541": { - "balance": "5577200000000000000000" - }, - "86c934e38e53be3b33f274d0539cfca159a4d0d1": { - "balance": "970000000000000000000" - }, - "0877eeaeab78d5c00e83c32b2d98fa79ad51482f": { - "balance": "439420000000000000000" - }, - "5e11ecf69d551d7f4f84df128046b3a13240a328": { - "balance": "20000000000000000000" - }, - "43ff8853e98ed8406b95000ada848362d6a0392a": { - "balance": "22100000000000000000000" - }, - "f11cf5d363746fee6864d3ca336dd80679bb87ae": { - "balance": "40000000000000000000000" - }, - "fb223c1e22eac1269b32ee156a5385922ed36fb8": { - "balance": "2000000000000000000000" - }, - "4e6600806289454acda330a2a3556010dfacade6": { - "balance": "6000000000000000000000" - }, - "cfe2caaf3cec97061d0939748739bffe684ae91f": { - "balance": "10000000000000000000000" - }, - "adeb52b604e5f77faaac88275b8d6b49e9f9f97f": { - "balance": "2089268000000000000000" - }, - "d53c567f0c3ff2e08b7d59e2b5c73485437fc58d": { - "balance": "600000000000000000000" - }, - "fbf75933e01b75b154ef0669076be87f62dffae1": { - "balance": "78000000000000000000000" - }, - "7dfd2962b575bcbeee97f49142d63c30ab009f66": { - "balance": "4000000000000000000000" - }, - "df6485c4297ac152b289b19dde32c77ec417f47d": { - "balance": "1000000000000000000000" - }, - "ffb974673367f5c07be5fd270dc4b7138b074d57": { - "balance": "2470407000000000000000" - }, - "f7d7af204c56f31fd94398e40df1964bd8bf123c": { - "balance": "150011000000000000000" - }, - "4506fe19fa4b006baa3984529d8516db2b2b50ab": { - "balance": "2000000000000000000000" - }, - "f4dc7ba85480bbb3f535c09568aaa3af6f3721c6": { - "balance": "7214962000000000000000" - }, - "d171c3f2258aef35e599c7da1aa07300234da9a6": { - "balance": "2000000000000000000000" - }, - "33581cee233088c0860d944e0cf1ceabb8261c2e": { - "balance": "13370000000000000000" - }, - "1c2e3607e127caca0fbd5c5948adad7dd830b285": { - "balance": "19700000000000000000000" - }, - "fd7ede8f5240a06541eb699d782c2f9afb2170f6": { - "balance": "1337000000000000000000" - }, - "368c5414b56b8455171fbf076220c1cba4b5ca31": { - "balance": "557940000000000000000" - }, - "3e8745ba322f5fd6cb50124ec46688c7a69a7fae": { - "balance": "4925000000000000000000" - }, - "76506eb4a780c951c74a06b03d3b8362f0999d71": { - "balance": "500000000000000000000" - }, - "96d62dfd46087f62409d93dd606188e70e381257": { - "balance": "2000000000000000000000" - }, - "37eada93c475ded2f7e15e7787d400470fa52062": { - "balance": "200000000000000000000" - }, - "26babf42b267fdcf3861fdd4236a5e474848b358": { - "balance": "1000000000000000000000" - }, - "3526eece1a6bdc3ee7b400fe935b48463f31bed7": { - "balance": "82400000000000000000" - }, - "27b62816e1e3b8d19b79d1513d5dfa855b0c3a2a": { - "balance": "99941000000000000000" - }, - "b3e3c439069880156600c2892e448d4136c92d9b": { - "balance": "850000000000000000000" - }, - "574ad9355390e4889ef42acd138b2a27e78c00ae": { - "balance": "1557000000000000000000" - }, - "f0b9d683cea12ba600baace219b0b3c97e8c00e4": { - "balance": "100000000000000000000" - }, - "a437fe6ec103ca8d158f63b334224eccac5b3ea3": { - "balance": "8000000000000000000000" - }, - "7a48d877b63a8f8f9383e9d01e53e80c528e955f": { - "balance": "8000000000000000000000" - }, - "e965daa34039f7f0df62375a37e5ab8a72b301e7": { - "balance": "4796000000000000000000" - }, - "72cd048a110574482983492dfb1bd27942a696ba": { - "balance": "2000000000000000000000" - }, - "6611ce59a98b072ae959dc49ad511daaaaa19d6b": { - "balance": "200000000000000000000" - }, - "0d92582fdba05eabc3e51538c56db8813785b328": { - "balance": "191000000000000000000" - }, - "e87e9bbfbbb71c1a740c74c723426df55d063dd9": { - "balance": "7998000000000000000000" - }, - "9c99a1da91d5920bc14e0cb914fdf62b94cb8358": { - "balance": "20000000000000000000000" - }, - "fe8e6e3665570dff7a1bda697aa589c0b4e9024a": { - "balance": "2000000000000000000000" - }, - "811461a2b0ca90badac06a9ea16e787b33b196cc": { - "balance": "164000000000000000000" - }, - "d211b21f1b12b5096181590de07ef81a89537ead": { - "balance": "2000000000000000000000" - }, - "01155057002f6b0d18acb9388d3bc8129f8f7a20": { - "balance": "1340000000000000000000" - }, - "8ce22f9fa372449a420610b47ae0c8d565481232": { - "balance": "2000000000000000000000" - }, - "e02b74a47628be315b1f76b315054ad44ae9716f": { - "balance": "4000000000000000000000" - }, - "92a7c5a64362e9f842a23deca21035857f889800": { - "balance": "1999944000000000000000" - }, - "5213f459e078ad3ab95a0920239fcf1633dc04ca": { - "balance": "2599989000000000000000" - }, - "c9957ba94c1b29e5277ec36622704904c63dc023": { - "balance": "1923000000000000000000" - }, - "6ac40f532dfee5118117d2ad352da77d4f6da2c8": { - "balance": "400000000000000000000" - }, - "ea1efb3ce789bedec3d67c3e1b3bc0e9aa227f90": { - "balance": "734000000000000000000" - }, - "b01e389b28a31d8e4995bdd7d7c81beeab1e4119": { - "balance": "1000000000000000000000" - }, - "ee97aa8ac69edf7a987d6d70979f8ec1fbca7a94": { - "balance": "376000000000000000000" - }, - "0fad05507cdc8f24b2be4cb7fa5d927ddb911b88": { - "balance": "3004447000000000000000" - }, - "b6e8afd93dfa9af27f39b4df06076710bee3dfab": { - "balance": "25000000000000000000" - }, - "7d0b255efb57e10f7008aa22d40e9752dfcf0378": { - "balance": "29944000000000000000" - }, - "aef5b12258a18dec07d5ec2e316574919d79d6d6": { - "balance": "2000000000000000000000" - }, - "63666755bd41b5986997783c13043008242b3cb5": { - "balance": "500000000000000000000" - }, - "921f5261f4f612760706892625c75e7bce96b708": { - "balance": "2000000000000000000000" - }, - "10e1e3377885c42d7df218522ee7766887c05e6a": { - "balance": "300031000000000000000" - }, - "134163be9fbbe1c5696ee255e90b13254395c318": { - "balance": "200000000000000000000" - }, - "870f15e5df8b0eabd02569537a8ef93b56785c42": { - "balance": "388000000000000000000" - }, - "68eec1e288ac31b6eaba7e1fbd4f04ad579a6b5d": { - "balance": "2000000000000000000000" - }, - "1a2694ec07cf5e4d68ba40f3e7a14c53f3038c6e": { - "balance": "1000073000000000000000" - }, - "cd9b4cef73390c83a8fd71d7b540a7f9cf8b8c92": { - "balance": "90000000000000000000" - }, - "c8de7a564c7f4012a6f6d10fd08f47890fbf07d4": { - "balance": "300000000000000000000" - }, - "c0345b33f49ce27fe82cf7c84d141c68f590ce76": { - "balance": "1000000000000000000000" - }, - "fe53b94989d89964da2061539526bbe979dd2ea9": { - "balance": "1930600000000000000000" - }, - "14410fb310711be074a80883c635d0ef6afb2539": { - "balance": "2000000000000000000000" - }, - "1d344e962567cb27e44db9f2fac7b68df1c1e6f7": { - "balance": "1940000000000000000000" - }, - "fe016ec17ec5f10e3bb98ff4a1eda045157682ab": { - "balance": "375804000000000000000" - }, - "e89da96e06beaf6bd880b378f0680c43fd2e9d30": { - "balance": "601400000000000000000" - }, - "0fee81ac331efd8f81161c57382bb4507bb9ebec": { - "balance": "400030000000000000000" - }, - "40cf90ef5b768c5da585002ccbe6617650d8e837": { - "balance": "999800000000000000000" - }, - "256fa150cc87b5056a07d004efc84524739e62b5": { - "balance": "200000000000000000000" - }, - "1b9b2dc2960e4cb9408f7405827c9b59071612fd": { - "balance": "1000000000000000000000" - }, - "0efd1789eb1244a3dede0f5de582d8963cb1f39f": { - "balance": "1500000000000000000000" - }, - "049c5d4bc6f25d4e456c697b52a07811ccd19fb1": { - "balance": "300048000000000000000" - }, - "02b7b1d6b34ce053a40eb65cd4a4f7dddd0e9f30": { - "balance": "685000000000000000000" - }, - "c1827686c0169485ec15b3a7c8c01517a2874de1": { - "balance": "40000000000000000000" - }, - "d8e5c9675ef4deed266b86956fc4590ea7d4a27d": { - "balance": "1000000000000000000000" - }, - "48f883e567b436a27bb5a3124dbc84dec775a800": { - "balance": "771840000000000000000" - }, - "a34076f84bd917f20f8342c98ba79e6fb08ecd31": { - "balance": "4200000000000000000000" - }, - "21ce6d5b9018cec04ad6967944bea39e8030b6b8": { - "balance": "20000000000000000000" - }, - "0596a27dc3ee115fce2f94b481bc207a9e261525": { - "balance": "1000000000000000000000" - }, - "717cf9beab3638308ded7e195e0c86132d163fed": { - "balance": "15097428000000000000000" - }, - "d5ce55d1b62f59433c2126bcec09bafc9dfaa514": { - "balance": "197000000000000000000" - }, - "7dd46da677e161825e12e80dc446f58276e1127c": { - "balance": "820000000000000000000" - }, - "98c5494a03ac91a768dffc0ea1dde0acbf889019": { - "balance": "200000000000000000000000" - }, - "617ff2cc803e31c9082233b825d025be3f7b1056": { - "balance": "1970000000000000000000" - }, - "1091176be19b9964a8f72e0ece6bf8e3cfad6e9c": { - "balance": "10020000000000000000000" - }, - "4ea56e1112641c038d0565a9c296c463afefc17e": { - "balance": "182000000000000000000" - }, - "e303167f3d4960fe881b32800a2b4aeff1b088d4": { - "balance": "2000000000000000000000" - }, - "773141127d8cf318aebf88365add3d5527d85b6a": { - "balance": "1000076000000000000000" - }, - "b916b1a01cdc4e56e7657715ea37e2a0f087d106": { - "balance": "2406017000000000000000" - }, - "46a430a2d4a894a0d8aa3feac615361415c3f81f": { - "balance": "2000000000000000000000" - }, - "e6a3010f0201bc94ff67a2f699dfc206f9e76742": { - "balance": "879088000000000000000" - }, - "d7ad09c6d32657685355b5c6ec8e9f57b4ebb982": { - "balance": "1970000000000000000000" - }, - "95e80a82c20cbe3d2060242cb92d735810d034a2": { - "balance": "32511000000000000000" - }, - "9a390162535e398877e416787d6239e0754e937c": { - "balance": "1000000000000000000000" - }, - "d85fdeaf2a61f95db902f9b5a53c9b8f9266c3ac": { - "balance": "2010000000000000000000" - }, - "c3e20c96df8d4e38f50b265a98a906d61bc51a71": { - "balance": "2000000000000000000000" - }, - "2949fd1def5c76a286b3872424809a07db3966f3": { - "balance": "5236067000000000000000" - }, - "86cdb7e51ac44772be3690f61d0e59766e8bfc18": { - "balance": "4000000000000000000000" - }, - "749a4a768b5f237248938a12c623847bd4e688dc": { - "balance": "72000000000000000000" - }, - "3524a000234ebaaf0789a134a2a417383ce5282a": { - "balance": "5635000000000000000000" - }, - "7b43c7eea8d62355b0a8a81da081c6446b33e9e0": { - "balance": "4000000000000000000000" - }, - "0eb189ef2c2d5762a963d6b7bdf9698ea8e7b48a": { - "balance": "1337000000000000000000" - }, - "767fd7797d5169a05f7364321c19843a8c348e1e": { - "balance": "18800000000000000000" - }, - "1b2639588b55c344b023e8de5fd4087b1f040361": { - "balance": "1500000000000000000000" - }, - "1e33d1c2fb5e084f2f1d54bc5267727fec3f985d": { - "balance": "500000000000000000000" - }, - "06b106649aa8c421ddcd1b8c32cd0418cf30da1f": { - "balance": "40000000000000000000000" - }, - "3c5a241459c6abbf630239c98a30d20b8b3ac561": { - "balance": "157600000000000000000" - }, - "0f4f94b9191bb7bb556aaad7c74ddb288417a50b": { - "balance": "1400000000000000000000" - }, - "d6f4a7d04e8faf20e8c6eb859cf7f78dd23d7a15": { - "balance": "131784000000000000000" - }, - "61adf5929a5e2981684ea243baa01f7d1f5e148a": { - "balance": "110302000000000000000" - }, - "8f58d8348fc1dc4e0dd8343b6543c857045ee940": { - "balance": "13632400000000000000000" - }, - "a6e3baa38e104a1e27a4d82869afb1c0ae6eff8d": { - "balance": "19690000000000000000" - }, - "67350b5331926f5e28f3c1e986f96443809c8b8c": { - "balance": "352000000000000000000" - }, - "0b5d66b13c87b392e94d91d5f76c0d450a552843": { - "balance": "2000000000000000000000" - }, - "562a8dcbbeeef7b360685d27303bd69e094accf6": { - "balance": "10000000000000000000000" - }, - "b5d9934d7b292bcf603b2880741eb760288383a0": { - "balance": "16700000000000000000" - }, - "6fc53662371dca587b59850de78606e2359df383": { - "balance": "180000000000000000000" - }, - "e069c0173352b10bf6834719db5bed01adf97bbc": { - "balance": "18894000000000000000" - }, - "10a93457496f1108cd98e140a1ecdbae5e6de171": { - "balance": "399600000000000000000" - }, - "69ff8901b541763f817c5f2998f02dcfc1df2997": { - "balance": "40000000000000000000" - }, - "00c27d63fde24b92ee8a1e7ed5d26d8dc5c83b03": { - "balance": "2000000000000000000000" - }, - "77f81b1b26fc84d6de97ef8b9fbd72a33130cc4a": { - "balance": "1000000000000000000000" - }, - "6d20ef9704670a500bb269b5832e859802049f01": { - "balance": "130000000000000000000" - }, - "186afdc085f2a3dce4615edffbadf71a11780f50": { - "balance": "200000000000000000000" - }, - "7ff0c63f70241bece19b737e5341b12b109031d8": { - "balance": "346000000000000000000" - }, - "9d4174aa6af28476e229dadb46180808c67505c1": { - "balance": "1219430000000000000000" - }, - "5fec49c665e64ee89dd441ee74056e1f01e92870": { - "balance": "6320000000000000000000" - }, - "6cd228dc712169307fe27ceb7477b48cfc8272e5": { - "balance": "77600000000000000000" - }, - "fd918536a8efa6f6cefe1fa1153995fef5e33d3b": { - "balance": "500000000000000000000" - }, - "2fbb504a5dc527d3e3eb0085e2fc3c7dd538cb7a": { - "balance": "1249961000000000000000" - }, - "6ab323ae5056ed0a453072c5abe2e42fcf5d7139": { - "balance": "880000000000000000000" - }, - "67d682a282ef73fb8d6e9071e2614f47ab1d0f5e": { - "balance": "1000000000000000000000" - }, - "1858cf11aea79f5398ad2bb22267b5a3c952ea74": { - "balance": "9850000000000000000000" - }, - "39d6caca22bccd6a72f87ee7d6b59e0bde21d719": { - "balance": "2002000000000000000000" - }, - "daa63cbda45dd487a3f1cd4a746a01bb5e060b90": { - "balance": "4797800000000000000000" - }, - "a90476e2efdfee4f387b0f32a50678b0efb573b5": { - "balance": "10000000000000000000000" - }, - "ae5aa1e6c2b60f6fd3efe721bb4a719cbe3d6f5d": { - "balance": "795860000000000000000" - }, - "ac2e766dac3f648f637ac6713fddb068e4a4f04d": { - "balance": "197000000000000000000" - }, - "6191ddc9b64a8e0890b4323709d7a07c48b92a64": { - "balance": "775000000000000000000" - }, - "cc4f0ff2aeb67d54ce3bc8c6510b9ae83e9d328b": { - "balance": "400000000000000000000" - }, - "ca23f62dff0d6460036c62e840aec5577e0befd2": { - "balance": "140800000000000000000" - }, - "97dc26ec670a31e0221d2a75bc5dc9f90c1f6fd4": { - "balance": "50000000000000000000" - }, - "848c994a79003fe7b7c26cc63212e1fc2f9c19eb": { - "balance": "2000000000000000000000" - }, - "20c284ba10a20830fc3d699ec97d2dfa27e1b95e": { - "balance": "2000000000000000000000" - }, - "4fa3f32ef4086448b344d5f0a9890d1ce4d617c3": { - "balance": "1500000000000000000000" - }, - "255abc8d08a096a88f3d6ab55fbc7352bddcb9ce": { - "balance": "82161000000000000000" - }, - "7c60e51f0be228e4d56fdd2992c814da7740c6bc": { - "balance": "200000000000000000000" - }, - "1c356cfdb95febb714633b28d5c132dd84a9b436": { - "balance": "25000000000000000000" - }, - "5062e5134c612f12694dbd0e131d4ce197d1b6a4": { - "balance": "1000000000000000000000" - }, - "ed862616fcbfb3becb7406f73c5cbff00c940755": { - "balance": "1700000000000000000000" - }, - "62c9b271ffd5b770a5eee4edc9787b5cdc709714": { - "balance": "2000000000000000000000" - }, - "3c925619c9b33144463f0537d896358706c520b0": { - "balance": "2000000000000000000000" - }, - "ffe2e28c3fb74749d7e780dc8a5d422538e6e451": { - "balance": "253319000000000000000" - }, - "37195a635dcc62f56a718049d47e8f9f96832891": { - "balance": "1970000000000000000000" - }, - "90e9a9a82edaa814c284d232b6e9ba90701d4952": { - "balance": "100007000000000000000" - }, - "e0c4ab9072b4e6e3654a49f8a8db026a4b3386a9": { - "balance": "2000000000000000000000" - }, - "439dee3f7679ff1030733f9340c096686b49390b": { - "balance": "2000000000000000000000" - }, - "548558d08cfcb101181dac1eb6094b4e1a896fa6": { - "balance": "1999944000000000000000" - }, - "3090f8130ec44466afadb36ed3c926133963677b": { - "balance": "4000000000000000000000" - }, - "d1648503b1ccc5b8be03fa1ec4f3ee267e6adf7b": { - "balance": "5828000000000000000000" - }, - "65b42faecc1edfb14283ca979af545f63b30e60c": { - "balance": "18200000000000000000" - }, - "6420f8bcc8164a6152a99d6b99693005ccf7e053": { - "balance": "999972000000000000000" - }, - "84b4b74e6623ba9d1583e0cfbe49643f16384149": { - "balance": "20000000000000000000" - }, - "b8310a16cc6abc465007694b930f978ece1930bd": { - "balance": "740000000000000000000" - }, - "16019a4dafab43f4d9bf4163fae0847d848afca2": { - "balance": "25060000000000000000" - }, - "479298a9de147e63a1c7d6d2fce089c7e64083bd": { - "balance": "9999999000000000000000" - }, - "030973807b2f426914ad00181270acd27b8ff61f": { - "balance": "5348000000000000000000" - }, - "b07bcf1cc5d4462e5124c965ecf0d70dc27aca75": { - "balance": "1600000000000000000000" - }, - "a2f798e077b07d86124e1407df32890dbb4b6379": { - "balance": "200000000000000000000" - }, - "0cbd921dbe121563b98a6871fecb14f1cc7e88d7": { - "balance": "200000000000000000000" - }, - "6042276df2983fe2bc4759dc1943e18fdbc34f77": { - "balance": "1970000000000000000000" - }, - "be2b2280523768ea8ac35cd9e888d60a719300d4": { - "balance": "2000000000000000000000" - }, - "2f4da753430fc09e73acbccdcde9da647f2b5d37": { - "balance": "200000000000000000000" - }, - "734223d27ff23e5906caed22595701bb34830ca1": { - "balance": "2000000000000000000000" - }, - "5b430d779696a3653fc60e74fbcbacf6b9c2baf1": { - "balance": "14000000000000000000000" - }, - "84232107932b12e03186583525ce023a703ef8d9": { - "balance": "2000000000000000000000" - }, - "4ed14d81b60b23fb25054d8925dfa573dcae6168": { - "balance": "340000000000000000000" - }, - "8b338411f26ccf37658cc75521d77629099e467d": { - "balance": "2000000000000000000000" - }, - "a37622ac9bbdc4d82b75015d745b9f8de65a28ec": { - "balance": "2910000000000000000000" - }, - "1dd77441844afe9cc18f15d8c77bccfb655ee034": { - "balance": "4850000000000000000000" - }, - "65849be1af20100eb8a3ba5a5be4d3ae8db5a70e": { - "balance": "400000000000000000000" - }, - "d5586da4e59583c8d86cccf71a86197f17996749": { - "balance": "2000000000000000000000" - }, - "4b53ae59c784b6b5c43616b9a0809558e684e10c": { - "balance": "1200000000000000000000" - }, - "55d42eb495bf46a634997b5f2ea362814918e2b0": { - "balance": "106128000000000000000" - }, - "959ff17f1d51b473b44010052755a7fa8c75bd54": { - "balance": "1970000000000000000000" - }, - "5a2daab25c31a61a92a4c82c9925a1d2ef58585e": { - "balance": "225400000000000000000" - }, - "24c0c88b54a3544709828ab4ab06840559f6c5e2": { - "balance": "2674000000000000000000" - }, - "7e8649e690fc8c1bfda1b5e186581f649b50fe33": { - "balance": "98500000000000000000" - }, - "4acfa9d94eda6625c9dfa5f9f4f5d107c4031fdf": { - "balance": "39400000000000000000" - }, - "5778ffdc9b94c5a59e224eb965b6de90f222d170": { - "balance": "335320000000000000000" - }, - "825a7f4e10949cb6f8964268f1fa5f57e712b4c4": { - "balance": "20000000000000000000" - }, - "6f39cc37caaa2ddc9b610f6131e0619fae772a3c": { - "balance": "500000000000000000000" - }, - "5b437365ae3a9a2ff97c68e6f90a7620188c7d19": { - "balance": "2002000000000000000000" - }, - "6710c2c03c65992b2e774be52d3ab4a6ba217ef7": { - "balance": "11600000000000000000000" - }, - "896e335ca47af57962fa0f4dbf3e45e688cba584": { - "balance": "1368500000000000000000" - }, - "b57549bfbc9bdd18f736b22650e48a73601fa65c": { - "balance": "446000000000000000000" - }, - "85ca1e727e9d1a87991cc2c41840ebb9edf21d1b": { - "balance": "13370000000000000000" - }, - "cf4166746e1d3bc1f8d0714b01f17e8a62df1464": { - "balance": "1004700000000000000000" - }, - "4a75c3d4fa6fccbd5dd5a703c15379a1e783e9b7": { - "balance": "1820000000000000000000" - }, - "9e5811b40be1e2a1e1d28c3b0774acde0a09603d": { - "balance": "3000000000000000000000" - }, - "763886e333c56feff85be3951ab0b889ce262e95": { - "balance": "2000000000000000000000" - }, - "2b101e822cd962962a06800a2c08d3b15d82b735": { - "balance": "152000000000000000000" - }, - "a01e9476df84431825c836e8803a97e22fa5a0cd": { - "balance": "6000000000000000000000" - }, - "be4e7d983f2e2a636b1102ec7039efebc842e98d": { - "balance": "66000000000000000000" - }, - "9e427272516b3e67d4fcbf82f59390d04c8e28e5": { - "balance": "4000000000000000000000" - }, - "e0d231e144ec9107386c7c9b02f1702ceaa4f700": { - "balance": "5000057000000000000000" - }, - "6a0f056066c2d56628850273d7ecb7f8e6e9129e": { - "balance": "5000016000000000000000" - }, - "d1538e9a87e59ca9ec8e5826a5b793f99f96c4c3": { - "balance": "1000000000000000000000" - }, - "f85bab1cb3710fc05fa19ffac22e67521a0ba21d": { - "balance": "2003000000000000000000" - }, - "f7cbdba6be6cfe68dbc23c2b0ff530ee05226f84": { - "balance": "20000000000000000000" - }, - "4eb87ba8788eba0df87e5b9bd50a8e45368091c1": { - "balance": "20000000000000000000" - }, - "1479a9ec7480b74b5db8fc499be352da7f84ee9c": { - "balance": "1000000000000000000000" - }, - "d311bcd7aa4e9b4f383ff3d0d6b6e07e21e3705d": { - "balance": "200000000000000000000" - }, - "425c1816868f7777cc2ba6c6d28c9e1e796c52b3": { - "balance": "10000000000000000000000" - }, - "8510ee934f0cbc900e1007eb38a21e2a5101b8b2": { - "balance": "106000000000000000000" - }, - "01e864d354741b423e6f42851724468c74f5aa9c": { - "balance": "20000000000000000000000" - }, - "a543a066fb32a8668aa0736a0c9cd40d78098727": { - "balance": "1000000000000000000000" - }, - "f3eb1948b951e22df1617829bf3b8d8680ec6b68": { - "balance": "4000000000000000000000" - }, - "f6b782f4dcd745a6c0e2e030600e04a24b25e542": { - "balance": "400000000000000000000" - }, - "229f4f1a2a4f540774505b4707a81de44410255b": { - "balance": "2000000000000000000000" - }, - "cff8d06b00e3f50c191099ad56ba6ae26571cd88": { - "balance": "1000000000000000000000" - }, - "910b7d577a7e39aa23acf62ad7f1ef342934b968": { - "balance": "10000000000000000000000" - }, - "392433d2ce83d3fb4a7602cca3faca4ec140a4b0": { - "balance": "51000000000000000000" - }, - "8ff46045687723dc33e4d099a06904f1ebb584dc": { - "balance": "2000000000000000000000" - }, - "9ca0429f874f8dcee2e9c062a9020a842a587ab9": { - "balance": "2000000000000000000000" - }, - "160ceb6f980e04315f53c4fc988b2bf69e284d7d": { - "balance": "19100000000000000000" - }, - "c340f9b91c26728c31d121d5d6fc3bb56d3d8624": { - "balance": "2000000000000000000000" - }, - "afa1d5ad38fed44759c05b8993c1aa0dace19f40": { - "balance": "80000000000000000000" - }, - "3969b4f71bb8751ede43c016363a7a614f76118e": { - "balance": "2000000000000000000000" - }, - "2bb6f578adfbe7b2a116b3554facf9969813c319": { - "balance": "7400000000000000000000" - }, - "8334764b7b397a4e578f50364d60ce44899bff94": { - "balance": "92500000000000000000" - }, - "9dd2196624a1ddf14a9d375e5f07152baf22afa2": { - "balance": "1211747000000000000000" - }, - "f242da845d42d4bf779a00f295b40750fe49ea13": { - "balance": "1000000000000000000000" - }, - "c6234657a807384126f8968ca1708bb07baa493c": { - "balance": "20000000000000000000" - }, - "94c055e858357aaa30cf2041fa9059ce164a1f91": { - "balance": "19999000000000000000000" - }, - "74c73c90528a157336f1e7ea20620ae53fd24728": { - "balance": "8969310000000000000000" - }, - "19e7f3eb7bf67f3599209ebe08b62ad3327f8cde": { - "balance": "2000000000000000000000" - }, - "b2b516fdd19e7f3864b6d2cf1b252a4156f1b03b": { - "balance": "53720000000000000000" - }, - "8164e78314ae16b28926cc553d2ccb16f356270d": { - "balance": "8450000000000000000000" - }, - "4d828894752f6f25175daf2177094487954b6f9f": { - "balance": "1459683000000000000000" - }, - "ab84a0f147ad265400002b85029a41fc9ce57f85": { - "balance": "1000000000000000000000" - }, - "f3fe51fde34413c73318b9c85437fe7e820f561a": { - "balance": "1003200000000000000000" - }, - "16c7b31e8c376282ac2271728c31c95e35d952c3": { - "balance": "2000000000000000000000" - }, - "80d5c40c59c7f54ea3a55fcfd175471ea35099b3": { - "balance": "1000000000000000000000" - }, - "7abb10f5bd9bc33b8ec1a82d64b55b6b18777541": { - "balance": "20000000000000000000000" - }, - "095b0ea2b218d82e0aea7c2889238a39c9bf9077": { - "balance": "20000000000000000000000" - }, - "5d5cdbe25b2a044b7b9be383bcaa5807b06d3c6b": { - "balance": "2000000000000000000000" - }, - "323749a3b971959e46c8b4822dcafaf7aaf9bd6e": { - "balance": "20064000000000000000" - }, - "e0272213e8d2fd3e96bd6217b24b4ba01b617079": { - "balance": "20000000000000000000" - }, - "00acbfb2f25a5485c739ef70a44eeeeb7c65a66f": { - "balance": "100000000000000000000" - }, - "52f15423323c24f19ae2ab673717229d3f747d9b": { - "balance": "1026115000000000000000" - }, - "cb4abfc282aed76e5d57affda542c1f382fcacf4": { - "balance": "8136100000000000000000" - }, - "f71b4534f286e43093b1e15efea749e7597b8b57": { - "balance": "104410000000000000000000" - }, - "44cd77535a893fa7c4d5eb3a240e79d099a72d2d": { - "balance": "820000000000000000000" - }, - "eb3ce7fc381c51db7d5fbd692f8f9e058a4c703d": { - "balance": "200000000000000000000" - }, - "f1c8c4a941b4628c0d6c30fda56452d99c7e1b64": { - "balance": "1449000000000000000000" - }, - "277677aba1e52c3b53bfa2071d4e859a0af7e8e1": { - "balance": "1000000000000000000000" - }, - "a5f075fd401335577b6683c281e6d101432dc6e0": { - "balance": "2680000000000000000000" - }, - "e28dbc8efd5e416a762ec0e018864bb9aa83287b": { - "balance": "24533161000000000000000" - }, - "2b717cd432a323a4659039848d3b87de26fc9546": { - "balance": "500000000000000000000000" - }, - "b358e97c70b605b1d7d729dfb640b43c5eafd1e7": { - "balance": "20000000000000000000000" - }, - "293c2306df3604ae4fda0d207aba736f67de0792": { - "balance": "200000000000000000000" - }, - "74d366b07b2f56477d7c7077ac6fe497e0eb6559": { - "balance": "5000000000000000000000" - }, - "490145afa8b54522bb21f352f06da5a788fa8f1d": { - "balance": "9231182000000000000000" - }, - "862569211e8c6327b5415e3a67e5738b15baaf6e": { - "balance": "140000000000000000000" - }, - "5a74ba62e7c81a3474e27d894fed33dd24ad95fe": { - "balance": "18200000000000000000" - }, - "536e4d8029b73f5579dca33e70b24eba89e11d7e": { - "balance": "1970000000000000000000" - }, - "25c6e74ff1d928df98137af4df8430df24f07cd7": { - "balance": "390000000000000000000" - }, - "19b36b0c87ea664ed80318dc77b688dde87d95a5": { - "balance": "1948386000000000000000" - }, - "abc4caeb474d4627cb6eb456ecba0ecd08ed8ae1": { - "balance": "3940000000000000000000" - }, - "8ea656e71ec651bfa17c5a5759d86031cc359977": { - "balance": "100000000000000000000" - }, - "8d620bde17228f6cbba74df6be87264d985cc179": { - "balance": "100000000000000000000" - }, - "b2aa2f1f8e93e79713d92cea9ffce9a40af9c82d": { - "balance": "2000000000000000000000" - }, - "198ef1ec325a96cc354c7266a038be8b5c558f67": { - "balance": "608334724000000000000000" - }, - "6a13d5e32c1fd26d7e91ff6e053160a89b2c8aad": { - "balance": "53480000000000000000" - }, - "e056bf3ff41c26256fef51716612b9d39ade999c": { - "balance": "100009000000000000000" - }, - "2c128c95d957215101f043dd8fc582456d41016d": { - "balance": "835000000000000000000" - }, - "2560b09b89a4ae6849ed5a3c9958426631714466": { - "balance": "1700000000000000000000" - }, - "d3d6e9fb82542fd29ed9ea3609891e151396b6f7": { - "balance": "54000000000000000000000" - }, - "a7607b42573bb6f6b4d4f23c7e2a26b3a0f6b6f0": { - "balance": "1610000000000000000000" - }, - "020362c3ade878ca90d6b2d889a4cc5510eed5f3": { - "balance": "1042883000000000000000" - }, - "14830704e99aaad5c55e1f502b27b22c12c91933": { - "balance": "620000000000000000000" - }, - "8030b111c6983f0485ddaca76224c6180634789f": { - "balance": "80000000000000000000" - }, - "2c5b7d7b195a371bf9abddb42fe04f2f1d9a9910": { - "balance": "200000000000000000000" - }, - "77d43fa7b481dbf3db530cfbf5fdced0e6571831": { - "balance": "2000000000000000000000" - }, - "2d90b415a38e2e19cdd02ff3ad81a97af7cbf672": { - "balance": "109800000000000000000" - }, - "2fc82ef076932341264f617a0c80dd571e6ae939": { - "balance": "7160000000000000000000" - }, - "dfe549fe8430e552c6d07cc3b92ccd43b12fb50f": { - "balance": "83620000000000000000" - }, - "1e8e689b02917cdc29245d0c9c68b094b41a9ed6": { - "balance": "2000000000000000000000" - }, - "21c3a8bba267c8cca27b1a9afabad86f607af708": { - "balance": "8940000000000000000000" - }, - "143c639752caeecf6a997d39709fc8f19878c7e8": { - "balance": "1970000000000000000000" - }, - "02603d7a3bb297c67c877e5d34fbd5b913d4c63a": { - "balance": "20000000000000000000" - }, - "a166f911c644ac3213d29e0e1ae010f794d5ad26": { - "balance": "2000000000000000000000" - }, - "6eb3819617404058268f0c3cff3596bfe9148c1c": { - "balance": "1670000000000000000000" - }, - "7a67dd043a504fc2f2fc7194e9becf484cecb1fb": { - "balance": "250000000000000000000" - }, - "f824ee331e4ac3cc587693395b57ecf625a6c0c2": { - "balance": "1600930000000000000000" - }, - "1179c60dbd068b150b074da4be23033b20c68558": { - "balance": "680000000000000000000" - }, - "d2a479404347c5543aab292ae1bb4a6f158357fa": { - "balance": "4000000000000000000000" - }, - "b0d32bd7e4e695b7b01aa3d0416f80557dba9903": { - "balance": "16300000000000000000000" - }, - "f734ec03724ddee5bb5279aa1afcf61b0cb448a1": { - "balance": "4238080000000000000000" - }, - "c04069dfb18b096c7867f8bee77a6dc7477ad062": { - "balance": "2674000000000000000000" - }, - "80c53ee7e3357f94ce0d7868009c208b4a130125": { - "balance": "2000000000000000000000" - }, - "0f32d9cb4d0fdaa0150656bb608dcc43ed7d9301": { - "balance": "753978000000000000000" - }, - "6ddb6092779d5842ead378e21e8120fd4c6bc132": { - "balance": "2000000000000000000000" - }, - "82ea01e3bf2e83836e71704e22a2719377efd9c3": { - "balance": "3040000000000000000000" - }, - "44c1110b18870ec81178d93d215838c551d48e64": { - "balance": "199958000000000000000" - }, - "7727af101f0aaba4d23a1cafe17c6eb5dab1c6dc": { - "balance": "2000000000000000000000" - }, - "a11a03c4bb26d21eff677d5d555c80b25453ee7a": { - "balance": "69979000000000000000" - }, - "19e5dea3370a2c746aae34a37c531f41da264e83": { - "balance": "200000000000000000000" - }, - "c325c352801ba883b3226c5feb0df9eae2d6e653": { - "balance": "3940000000000000000000" - }, - "ae5055814cb8be0c117bb8b1c8d2b63b4698b728": { - "balance": "32035000000000000000" - }, - "deb1bc34d86d4a4dde2580d8beaf074eb0e1a244": { - "balance": "1580000000000000000000" - }, - "558360206883dd1b6d4a59639e5629d0f0c675d0": { - "balance": "2000000000000000000000" - }, - "a9d6f871ca781a759a20ac3adb972cf12829a208": { - "balance": "925000000000000000000" - }, - "b0ac4eff6680ee14169cdadbffdb30804f6d25f5": { - "balance": "2000000000000000000000" - }, - "f1b58faffa8794f50af8e88309c7a6265455d51a": { - "balance": "999800000000000000000" - }, - "a61a54df784a44d71b771b87317509211381f200": { - "balance": "1000000000000000000000" - }, - "baa4b64c2b15b79f5f204246fd70bcbd86e4a92a": { - "balance": "500000000000000000000" - }, - "a20d8ff60caae31d02e0b665fa435d76f77c9442": { - "balance": "489600000000000000000" - }, - "f3e74f470c7d3a3f0033780f76a89f3ef691e6cb": { - "balance": "3021800000000000000000" - }, - "d330728131fe8e3a15487a34573c93457e2afe95": { - "balance": "4000000000000000000000" - }, - "9af9dbe47422d177f945bdead7e6d82930356230": { - "balance": "3940000000000000000000" - }, - "0eb5b662a1c718608fd52f0c25f9378830178519": { - "balance": "6091400000000000000000" - }, - "fda6810ea5ac985d6ffbf1c511f1c142edcfddf7": { - "balance": "4000000000000000000000" - }, - "832c54176bdf43d2c9bcd7b808b89556b89cbf31": { - "balance": "200000000000000000000" - }, - "704d5de4846d39b53cd21d1c49f096db5c19ba29": { - "balance": "152000000000000000000" - }, - "344a8db086faed4efc37131b3a22b0782dad7095": { - "balance": "500000000000000000000" - }, - "8c7fa5cae82fedb69ab189d3ff27ae209293fb93": { - "balance": "400030000000000000000" - }, - "ad660dec825522a9f62fcec3c5b731980dc286ea": { - "balance": "3000000000000000000000" - }, - "13b9b10715714c09cfd610cf9c9846051cb1d513": { - "balance": "1970000000000000000000" - }, - "40467d80e74c35407b7db51789234615fea66818": { - "balance": "388000000000000000000" - }, - "30e9d5a0088f1ddb2fd380e2a049192266c51cbf": { - "balance": "196910000000000000000" - }, - "b2d1e99af91231858e7065dd1918330dc4c747d5": { - "balance": "16700000000000000000000" - }, - "9f21302ca5096bea7402b91b0fd506254f999a3d": { - "balance": "1246832000000000000000" - }, - "d24b6644f439c8051dfc64d381b8c86c75c17538": { - "balance": "2000000000000000000000" - }, - "8228ebc087480fd64547ca281f5eace3041453b9": { - "balance": "1970000000000000000000" - }, - "29da3e35b23bb1f72f8e2258cf7f553359d24bac": { - "balance": "20000000000000000000000" - }, - "c8e558a3c5697e6fb23a2594c880b7a1b68f9860": { - "balance": "10000000000000000000000" - }, - "6b951a43274eeafc8a0903b0af2ec92bf1efc839": { - "balance": "100000000000000000000" - }, - "d015f6fcb84df7bb410e8c8f04894a881dcac237": { - "balance": "1038000000000000000000" - }, - "6ccb03acf7f53ce87aadcc21a9932de915f89804": { - "balance": "8000000000000000000000" - }, - "388c85a9b9207d8146033fe38143f6d34b595c47": { - "balance": "200000000000000000000" - }, - "429c06b487e8546abdfc958a25a3f0fba53f6f00": { - "balance": "13503000000000000000" - }, - "771507aeee6a255dc2cd9df55154062d0897b297": { - "balance": "334250000000000000000" - }, - "5a2b1c853aeb28c45539af76a00ac2d8a8242896": { - "balance": "25000000000000000000" - }, - "f4d67a9044b435b66e8977ff39a28dc4bd53729a": { - "balance": "200000000000000000000" - }, - "063759dd1c4e362eb19398951ff9f8fad1d31068": { - "balance": "10000000000000000000000" - }, - "cb58990bcd90cfbf6d8f0986f6fa600276b94e2d": { - "balance": "999925000000000000000" - }, - "6df5c84f7b909aab3e61fe0ecb1b3bf260222ad2": { - "balance": "4000000000000000000000" - }, - "deb2495d6aca7b2a6a2d138b6e1a42e2dc311fdd": { - "balance": "2000000000000000000000" - }, - "59203cc37599b648312a7cc9e06dacb589a9ae6a": { - "balance": "148689000000000000000" - }, - "fc9b347464b2f9929d807e039dae48d3d98de379": { - "balance": "14000000000000000000000" - }, - "48d2434b7a7dbbff08223b6387b05da2e5093126": { - "balance": "18000000000000000000000" - }, - "c9d76446d5aadff80b68b91b08cd9bc8f5551ac1": { - "balance": "714000000000000000000" - }, - "3d31587b5fd5869845788725a663290a49d3678c": { - "balance": "500000000000000000000" - }, - "d8715ef9176f850b2e30eb8e382707f777a6fbe9": { - "balance": "2000000000000000000000" - }, - "2c2147947ae33fb098b489a5c16bfff9abcd4e2a": { - "balance": "200000000000000000000" - }, - "d6c0d0bc93a62e257174700e10f024c8b23f1f87": { - "balance": "2000000000000000000000" - }, - "d1978f2e34407fab1dc2183d95cfda6260b35982": { - "balance": "788000000000000000000" - }, - "1bf974d9904f45ce81a845e11ef4cbcf27af719e": { - "balance": "100000000000000000000" - }, - "6e761eaa0f345f777b5441b73a0fa5b56b85f22d": { - "balance": "2000000000000000000000" - }, - "ea60436912de6bf187d3a472ff8f5333a0f7ed06": { - "balance": "19700000000000000000" - }, - "94f8f057db7e60e675ad940f155885d1a477348e": { - "balance": "401100000000000000000" - }, - "8933491760c8f0b4df8caac78ed835caee21046d": { - "balance": "20000000000000000000000" - }, - "a7775e4af6a23afa201fb78b915e51a515b7a728": { - "balance": "120000000000000000000" - }, - "d8d64384249b776794063b569878d5e3b530a4b2": { - "balance": "177569000000000000000" - }, - "be633a3737f68439bac7c90a52142058ee8e8a6f": { - "balance": "960000000000000000000" - }, - "90bd62a050845261fa4a9f7cf241ea630b05efb8": { - "balance": "500000000000000000000" - }, - "552987f0651b915b2e1e5328c121960d4bdd6af4": { - "balance": "1790000000000000000000" - }, - "0baf6ecdb91acb3606a8357c0bc4f45cfd2d7e6f": { - "balance": "1000000000000000000000" - }, - "9e5a311d9f69898a7c6a9d6360680438e67a7b2f": { - "balance": "1490000000000000000000" - }, - "78859c5b548b700d9284cee4b6633c2f52e529c2": { - "balance": "2955000000000000000000" - }, - "d572309169b1402ec8131a17a6aac3222f89e6eb": { - "balance": "13800000000000000000000" - }, - "8e6d7485cbe990acc1ad0ee9e8ccf39c0c93440e": { - "balance": "955000000000000000000" - }, - "75c11d024d12ae486c1095b7a7b9c4af3e8edeb9": { - "balance": "20000000000000000000" - }, - "903413878aea3bc1086309a3fe768b65559e8cab": { - "balance": "8000000000000000000000" - }, - "6d0569e5558fc7df2766f2ba15dc8aeffc5beb75": { - "balance": "4001070000000000000000" - }, - "3815b0743f94fc8cc8654fd9d597ed7d8b77c57e": { - "balance": "738578000000000000000" - }, - "0f26480a150961b8e30750713a94ee6f2e47fc00": { - "balance": "1000000000000000000000" - }, - "ede5de7c7fb7eee0f36e64530a41440edfbefacf": { - "balance": "617200000000000000000" - }, - "763a7cbab70d7a64d0a7e52980f681472593490c": { - "balance": "600000000000000000000" - }, - "6e270ad529f1f0b8d9cb6d2427ec1b7e2dc64a74": { - "balance": "200000000000000000000" - }, - "eb3bdd59dcdda5a9bb2ac1641fd02180f5f36560": { - "balance": "6600000000000000000000" - }, - "f4ebf50bc7e54f82e9b9bd24baef29438e259ce6": { - "balance": "10000000000000000000000" - }, - "882c8f81872c79fed521cb5f950d8b032322ea69": { - "balance": "40000000000000000000000" - }, - "394132600f4155e07f4d45bc3eb8d9fb72dcd784": { - "balance": "2941000000000000000000" - }, - "0be2b94ad950a2a62640c35bfccd6c67dae450f6": { - "balance": "1940000000000000000000" - }, - "d4c6ac742e7c857d4a05a04c33d4d05c1467571d": { - "balance": "200000000000000000000" - }, - "1fddd85fc98be9c4045961f40f93805ecc4549e5": { - "balance": "164000000000000000000" - }, - "534065361cb854fac42bfb5c9fcde0604ac919da": { - "balance": "2000000000000000000000" - }, - "9a6ff5f6a7af7b7ae0ed9c20ecec5023d281b786": { - "balance": "2547000000000000000000" - }, - "4f3a4854911145ea01c644044bdb2e5a960a982f": { - "balance": "4000000000000000000000" - }, - "00497e92cdc0e0b963d752b2296acb87da828b24": { - "balance": "194800000000000000000" - }, - "4ff67fb87f6efba9279930cfbd1b7a343c79fade": { - "balance": "400000000000000000000" - }, - "62f2e5ccecd52cc4b95e0597df27cc079715608c": { - "balance": "143000000000000000000" - }, - "1eda084e796500ba14c5121c0d90846f66e4be62": { - "balance": "534800000000000000000" - }, - "9836b4d30473641ab56aeee19242761d72725178": { - "balance": "2000000000000000000000" - }, - "de55de0458f850b37e4d78a641dd2eb2dd8f38ce": { - "balance": "4000000000000000000000" - }, - "140ca28ff33b9f66d7f1fc0078f8c1eef69a1bc0": { - "balance": "1600000000000000000000" - }, - "2014261f01089f53795630ba9dd24f9a34c2d942": { - "balance": "1337000000000000000000" - }, - "11415fab61e0dfd4b90676141a557a869ba0bde9": { - "balance": "2048000000000000000000" - }, - "88344909644c7ad4930fd873ca1c0da2d434c07f": { - "balance": "131970000000000000000" - }, - "88b217ccb786a254cf4dc57f5d9ac3c455a30483": { - "balance": "925000000000000000000" - }, - "dfdbcec1014b96da2158ca513e9c8d3b9af1c3d0": { - "balance": "2000000000000000000000" - }, - "1ba9f7997e5387b6b2aa0135ac2452fe36b4c20d": { - "balance": "850000000000000000000" - }, - "d70ad2c4e9eebfa637ef56bd486ad2a1e5bce093": { - "balance": "200000000000000000000" - }, - "9ce27f245e02d1c312c1d500788c9def7690453b": { - "balance": "200000000000000000000" - }, - "8234f463d18485501f8f85ace4972c9b632dbccc": { - "balance": "2000000000000000000000" - }, - "994152fc95d5c1ca8b88113abbad4d710e40def6": { - "balance": "500000000000000000000" - }, - "e5b980d28eece2c06fca6c9473068b37d4a6d6e9": { - "balance": "695200000000000000000" - }, - "2d426912d059fad9740b2e390a2eeac0546ff01b": { - "balance": "1400000000000000000000" - }, - "6d9997509882027ea947231424bedede2965d0ba": { - "balance": "2001600000000000000000" - }, - "167ce7de65e84708595a525497a3eb5e5a665073": { - "balance": "575400000000000000000" - }, - "e430c0024fdbf73a82e21fccf8cbd09138421c21": { - "balance": "4000000000000000000000" - }, - "2e52912bc10ea39d54e293f7aed6b99a0f4c73be": { - "balance": "400000000000000000000" - }, - "12cf8b0e465213211a5b53dfb0dd271a282c12c9": { - "balance": "15200000000000000000" - }, - "06964e2d17e9189f88a8203936b40ac96e533c06": { - "balance": "18200000000000000000" - }, - "66b1a63da4dcd9f81fe54f5e3fcb4055ef7ec54f": { - "balance": "201412000000000000000" - }, - "0a77e7f72b437b574f00128b21f2ac265133528c": { - "balance": "2000000000000000000000" - }, - "78f5c74785c5668a838072048bf8b453594ddaab": { - "balance": "400000000000000000000" - }, - "58e554af3d87629620da61d538c7f5b4b54c4afe": { - "balance": "1297081000000000000000" - }, - "37a10451f36166cf643dd2de6c1cbba8a011cfa3": { - "balance": "380000000000000000000" - }, - "fe9ad12ef05d6d90261f96c8340a0381974df477": { - "balance": "2000000000000000000000" - }, - "057f7f81cd7a406fc45994408b5049912c566463": { - "balance": "1700000000000000000000" - }, - "55a3df57b7aaec16a162fd5316f35bec082821cf": { - "balance": "1970000000000000000000" - }, - "c0e0b903088e0c63f53dd069575452aff52410c3": { - "balance": "3000000000000000000000" - }, - "63e88e2e539ffb450386b4e46789b223f5476c45": { - "balance": "6292000000000000000000" - }, - "3727341f26c12001e378405ee38b2d8464ec7140": { - "balance": "2000000000000000000000" - }, - "c96751656c0a8ef4357b7344322134b983504aca": { - "balance": "2000000000000000000000" - }, - "1e060dc6c5f1cb8cc7e1452e02ee167508b56542": { - "balance": "12715500000000000000000" - }, - "18136c9df167aa17b6f18e22a702c88f4bc28245": { - "balance": "4000000000000000000000" - }, - "116108c12084612eeda7a93ddcf8d2602e279e5c": { - "balance": "2000000000000000000000" - }, - "bbb643d2187b364afc10a6fd368d7d55f50d1a3c": { - "balance": "1000000000000000000000" - }, - "ec83e798c396b7a55e2a2224abcd834b27ea459c": { - "balance": "12000000000000000000000" - }, - "973f4e361fe5decd989d4c8f7d7cc97990385daf": { - "balance": "388500000000000000000" - }, - "c0f29ed0076611b5e55e130547e68a48e26df5e4": { - "balance": "3000000000000000000000" - }, - "fd4b551f6fdbcda6c511b5bb372250a6b783e534": { - "balance": "20600000000000000000" - }, - "144b19f1f66cbe318347e48d84b14039466c5909": { - "balance": "2000000000000000000000" - }, - "bf183641edb886ce60b8190261e14f42d93cce01": { - "balance": "25019000000000000000" - }, - "94db807873860aac3d5aea1e885e52bff2869954": { - "balance": "3220000000000000000000" - }, - "7a74cee4fa0f6370a7894f116cd00c1147b83e59": { - "balance": "800000000000000000000" - }, - "cd32a4a8a27f1cc63954aa634f7857057334c7a3": { - "balance": "1085000000000000000000" - }, - "7cbeb99932e97e6e02058cfc62d0b26bc7cca52b": { - "balance": "2000000000000000000000" - }, - "8cde8b732e6023878eb23ed16229124b5f7afbec": { - "balance": "133700000000000000000" - }, - "45c4ecb4ee891ea984a7c5cefd8dfb00310b2850": { - "balance": "1980000000000000000000" - }, - "8b393fb0813ee101db1e14ecc7d322c72b8c0473": { - "balance": "455578000000000000000" - }, - "7b66126879844dfa34fe65c9f288117fefb449ad": { - "balance": "6000000000000000000000" - }, - "162ba503276214b509f97586bd842110d103d517": { - "balance": "9002000000000000000000" - }, - "7dece6998ae1900dd3770cf4b93812bad84f0322": { - "balance": "100000000000000000000" - }, - "ec0927bac7dc36669c28354ab1be83d7eec30934": { - "balance": "2000000000000000000000" - }, - "8d7f3e61299c2db9b9c0487cf627519ed00a9123": { - "balance": "1742400000000000000000" - }, - "4fc46c396e674869ad9481638f0013630c87caac": { - "balance": "1000000000000000000000" - }, - "bf68d28aaf1eeefef646b65e8cc8d190f6c6da9c": { - "balance": "2000000000000000000000" - }, - "00969747f7a5b30645fe00e44901435ace24cc37": { - "balance": "1700000000000000000000" - }, - "494dec4d5ee88a2771a815f1ee7264942fb58b28": { - "balance": "2000000000000000000000" - }, - "ffeac0305ede3a915295ec8e61c7f881006f4474": { - "balance": "98500000000000000000" - }, - "b39139576194a0866195151f33f2140ad1cc86cf": { - "balance": "100000000000000000000000" - }, - "fead1803e5e737a68e18472d9ac715f0994cc2be": { - "balance": "500000000000000000000" - }, - "698ab9a2f33381e07c0c47433d0d21d6f336b127": { - "balance": "20000000000000000000" - }, - "e5edc73e626f5d3441a45539b5f7a398c593edf6": { - "balance": "865000000000000000000" - }, - "dd4f5fa2111db68f6bde3589b63029395b69a92d": { - "balance": "158400000000000000000" - }, - "8c93c3c6db9d37717de165c3a1b4fe51952c08de": { - "balance": "400000000000000000000" - }, - "f87bb07b289df7301e54c0efda6a2cf291e89200": { - "balance": "1400000000000000000000" - }, - "e7a4560c84b20e0fb54c49670c2903b0a96c42a4": { - "balance": "598000000000000000000" - }, - "00a5797f52c9d58f189f36b1d45d1bf6041f2f6b": { - "balance": "5456900000000000000000" - }, - "9da3302240af0511c6fd1857e6ddb7394f77ab6b": { - "balance": "3100000000000000000000" - }, - "2c2d15ff39561c1b72eda1cc027ffef23743a144": { - "balance": "3920000000000000000000" - }, - "9b4c2715780ca4e99e60ebf219f1590c8cad500a": { - "balance": "1600000000000000000000" - }, - "ff5e7ee7d5114821e159dca5e81f18f1bfffbff9": { - "balance": "2000000000000000000000" - }, - "0169c1c210eae845e56840412e1f65993ea90fb4": { - "balance": "2000000000000000000000" - }, - "abc45f84db7382dde54c5f7d8938c42f4f3a3bc4": { - "balance": "200000000000000000000" - }, - "d9383d4b6d17b3f9cd426e10fb944015c0d44bfb": { - "balance": "800000000000000000000" - }, - "c090fe23dcd86b358c32e48d2af91024259f6566": { - "balance": "200000000000000000000" - }, - "9ffedcc36b7cc312ad2a9ede431a514fccb49ba3": { - "balance": "669800000000000000000" - }, - "2ffe93ec1a5636e9ee34af70dff52682e6ff7079": { - "balance": "2000000000000000000000" - }, - "6e01e4ad569c95d007ada30d5e2db12888492294": { - "balance": "4000000000000000000000" - }, - "d4d92c62b280e00f626d8657f1b86166cb1f740f": { - "balance": "200028000000000000000" - }, - "1d36683063b7e9eb99462dabd569bddce71686f2": { - "balance": "1000000000000000000000" - }, - "3a48e0a7098b06a905802b87545731118e89f439": { - "balance": "2000000000000000000000" - }, - "bd9e56e902f4be1fc8768d8038bac63e2acbbf8e": { - "balance": "999972000000000000000" - }, - "4d67f2ab8599fef5fc413999aa01fd7fce70b43d": { - "balance": "10000000000000000000000" - }, - "8e74e0d1b77ebc823aca03f119854cb12027f6d7": { - "balance": "107200000000000000000000" - }, - "7e5b19ae1be94ff4dee635492a1b012d14db0213": { - "balance": "100000000000000000000" - }, - "5de9e7d5d1b667d095dd34099c85b0421a0bc681": { - "balance": "20000000000000000000" - }, - "316eb4e47df71b42e16d6fe46825b7327baf3124": { - "balance": "4000000000000000000000" - }, - "772c297f0ad194482ee8c3f036bdeb01c201d5cc": { - "balance": "200000000000000000000" - }, - "d7052519756af42590f15391b723a03fa564a951": { - "balance": "4615591000000000000000" - }, - "2c6846a1aa999a2246a287056000ba4dcba8e63d": { - "balance": "10020000000000000000000" - }, - "de5b005fe8daae8d1f05de3eda042066c6c4691c": { - "balance": "1100000000000000000000" - }, - "254c1ecc630c2877de8095f0a8dba1e8bf1f550c": { - "balance": "1700000000000000000000" - }, - "f8f226142a428434ab17a1864a2597f64aab2f06": { - "balance": "172473000000000000000" - }, - "a6c910ce4d494a919ccdaaa1fc3b82aa74ba06cf": { - "balance": "8000000000000000000000" - }, - "e587b16abc8a74081e3613e14342c03375bf0847": { - "balance": "2000000000000000000000" - }, - "6f176065e88e3c6fe626267d18a088aaa4db80bc": { - "balance": "3520000000000000000000" - }, - "50dcbc27bcad984093a212a9b4178eabe9017561": { - "balance": "145512000000000000000" - }, - "e1953c6e975814c571311c34c0f6a99cdf48ab82": { - "balance": "50000000000000000000" - }, - "be0a2f385f09dbfce96732e12bb40ac349871ba8": { - "balance": "1610348000000000000000" - }, - "4712540265cbeec3847022c59f1b318d43400a9e": { - "balance": "3500000000000000000000" - }, - "29bdc4f28de0180f433c2694eb74f5504ce94337": { - "balance": "2000000000000000000000" - }, - "2f66bfbf2262efcc8d2bd0444fc5b0696298ff1e": { - "balance": "9940000000000000000000" - }, - "506411fd79003480f6f2b6aac26b7ba792f094b2": { - "balance": "500000000000000000000" - }, - "23ea669e3564819a83b0c26c00a16d9e826f6c46": { - "balance": "1430590000000000000000" - }, - "e3ffb02cb7d9ea5243701689afd5d417d7ed2ece": { - "balance": "78000000000000000000" - }, - "38e7dba8fd4f1f850dbc2649d8e84f0952e3eb3c": { - "balance": "50000000000000000000" - }, - "8644cc281be332ccced36da483fb2a0746d9ba2e": { - "balance": "400000000000000000000" - }, - "e8a91da6cf1b9d65c74a02ec1f96eecb6dd241f3": { - "balance": "1940000000000000000000" - }, - "0631dc40d74e5095e3729eddf49544ecd4396f67": { - "balance": "160000000000000000000" - }, - "83c897a84b695eebe46679f7da19d776621c2694": { - "balance": "500000000000000000000" - }, - "db73460b59d8e85045d5e752e62559875e42502e": { - "balance": "999800000000000000000" - }, - "0dd4e674bbadb1b0dc824498713dce3b5156da29": { - "balance": "170000000000000000000" - }, - "e3933d61b77dcdc716407f8250bc91e4ffaeb09d": { - "balance": "86600000000000000000000" - }, - "58c90754d2f20a1cb1dd330625e04b45fa619d5c": { - "balance": "2000000000000000000000" - }, - "895ec5545644e0b78330fffab8ddeac9e833156c": { - "balance": "600000000000000000000" - }, - "7e1e29721d6cb91057f6c4042d8a0bbc644afe73": { - "balance": "159800000000000000000" - }, - "72b90a4dc097239492c5b9777dcd1e52ba2be2c2": { - "balance": "6000000000000000000000" - }, - "64241a7844290e0ab855f1d4aa75b55345032224": { - "balance": "1600000000000000000000" - }, - "6fd4e0f3f32bee6d3767fdbc9d353a6d3aab7899": { - "balance": "695240000000000000000" - }, - "3a035594c747476d42d1ee966c36224cdd224993": { - "balance": "355890000000000000000" - }, - "de97f4330700b48c496d437c91ca1de9c4b01ba4": { - "balance": "2910840000000000000000" - }, - "716ad3c33a9b9a0a18967357969b94ee7d2abc10": { - "balance": "482000000000000000000" - }, - "bfbe05e88c9cbbcc0e92a405fac1d85de248ee24": { - "balance": "100000000000000000000" - }, - "cfc4e6f7f8b011414bfba42f23adfaa78d4ecc5e": { - "balance": "1850000000000000000000" - }, - "d931ac2668ba6a84481ab139735aec14b7bfbabf": { - "balance": "2000000000000000000000" - }, - "e3263ce8af6db3e467584502ed7109125eae22a5": { - "balance": "2000000000000000000000" - }, - "f78258c12481bcdddbb72a8ca0c043097261c6c5": { - "balance": "20000000000000000000" - }, - "4493123c021ece3b33b1a452c9268de14007f9d3": { - "balance": "6685000000000000000000" - }, - "431f2c19e316b044a4b3e61a0c6ff8c104a1a12f": { - "balance": "1000000000000000000000" - }, - "e63e787414b9048478a50733359ecdd7e3647aa6": { - "balance": "1580000000000000000000" - }, - "e4715956f52f15306ee9506bf82bccc406b3895e": { - "balance": "274944000000000000000" - }, - "f7f91e7acb5b8129a306877ce3168e6f438b66a1": { - "balance": "176000000000000000000" - }, - "dcdbbd4e2604e40e1710cc6730289dccfad3892d": { - "balance": "4600000000000000000000" - }, - "2b5f4b3f1e11707a227aa5e69fa49dded33fb321": { - "balance": "6000000000000000000000" - }, - "01488ad3da603c4cdd6cb0b7a1e30d2a30c8fc38": { - "balance": "200000000000000000000" - }, - "841145b44840c946e21dbc190264b8e0d5029369": { - "balance": "300000000000000000000000" - }, - "bf05070c2c34219311c4548b2614a438810ded6d": { - "balance": "2000000000000000000000" - }, - "38f387e1a4ed4a73106ef2b462e474e2e3143ad0": { - "balance": "6000000000000000000000" - }, - "f116b0b4680f53ab72c968ba802e10aa1be11dc8": { - "balance": "20000000000000000000" - }, - "bea0afc93aae2108a3fac059623bf86fa582a75e": { - "balance": "1700000000000000000000" - }, - "4c997992036c5b433ac33d25a8ea1dc3d4e4e6d8": { - "balance": "29200000000000000000" - }, - "ab7e0b83ed9a424c6d1e6a6f87a4dbf06409c7d6": { - "balance": "2400000000000000000000" - }, - "d71fb130f0150c565269e00efb43902b52a455a6": { - "balance": "200000000000000000000" - }, - "99b018932bcad355b6792b255db6702dec8ce5dd": { - "balance": "4000086000000000000000" - }, - "4b904e934bd0cc8b20705f879e905b93ea0ccc30": { - "balance": "2000000000000000000000" - }, - "672ec42faa8cd69aaa71b32cc7b404881d52ff91": { - "balance": "10000000000000000000000" - }, - "acbc2d19e06c3babbb5b6f052b6bf7fc37e07229": { - "balance": "200000000000000000000" - }, - "cea8743341533cb2f0b9c6efb8fda80d77162825": { - "balance": "100000000000000000000" - }, - "9568b7de755628af359a84543de23504e15e41e6": { - "balance": "40000000000000000000000" - }, - "6ec96d13bdb24dc7a557293f029e02dd74b97a55": { - "balance": "4000000000000000000000" - }, - "d95c90ffbe5484864780b867494a83c89256d6e4": { - "balance": "1640000000000000000000" - }, - "ade6f8163bf7c7bb4abe8e9893bd0cc112fe8872": { - "balance": "327600000000000000000" - }, - "250eb7c66f869ddf49da85f3393e980c029aa434": { - "balance": "4000000000000000000000" - }, - "a35c19132cac1935576abfed6c0495fb07881ba0": { - "balance": "2000000000000000000000" - }, - "d5550caaf743b037c56fd2558a1c8ed235130750": { - "balance": "5347598000000000000000" - }, - "03097923ba155e16d82f3ad3f6b815540884b92c": { - "balance": "1820000000000000000000" - }, - "d6d9e30f0842012a7176a917d9d2048ca0738759": { - "balance": "4000000000000000000000" - }, - "ab9ad36e5c74ce2e96399f57839431d0e79f96ab": { - "balance": "164000000000000000000" - }, - "75be8ff65e5788aec6b2a52d5fa7b1e7a03ba675": { - "balance": "67720000000000000000" - }, - "4f6d4737d7a940382487264886697cf7637f8015": { - "balance": "1670000000000000000000" - }, - "5f7b3bbac16dab831a4a0fc53b0c549dc36c31ca": { - "balance": "1940000000000000000000" - }, - "d843ee0863ce933e22f89c802d31287b9671e81c": { - "balance": "13370000000000000000" - }, - "361f3ba9ed956b770f257d3672fe1ff9f7b0240c": { - "balance": "600000000000000000000" - }, - "6c0ae9f043c834d44271f13406593dfe094f389f": { - "balance": "1517545000000000000000" - }, - "db34745ede8576b499db01beb7c1ecda85cf4abe": { - "balance": "80000000000000000000" - }, - "7be8ccb4f11b66ca6e1d57c0b5396221a31ba53a": { - "balance": "20000000000000000000" - }, - "128b908fe743a434203de294c441c7e20a86ea67": { - "balance": "713304000000000000000" - }, - "df236bf6abf4f3293795bf0c28718f93e3b1b36b": { - "balance": "1337000000000000000000" - }, - "14254ea126b52d0142da0a7e188ce255d8c47178": { - "balance": "775000000000000000000" - }, - "ceed47ca5b899fd1623f21e9bd4db65a10e5b09d": { - "balance": "133196000000000000000" - }, - "30acd858875fa24eef0d572fc7d62aad0ebddc35": { - "balance": "400000000000000000000" - }, - "47a281dff64167197855bf6e705eb9f2cef632ea": { - "balance": "1000072000000000000000" - }, - "297d5dbe222f2fb52531acbd0b013dc446ac7368": { - "balance": "20000000000000000000000" - }, - "adf85203c8376a5fde9815384a350c3879c4cb93": { - "balance": "1147300000000000000000" - }, - "c3e0471c64ff35fa5232cc3121d1d38d1a0fb7de": { - "balance": "2000000000000000000000" - }, - "fdecc82ddfc56192e26f563c3d68cb544a96bfed": { - "balance": "440000000000000000000" - }, - "2614f42d5da844377578e6b448dc24305bef2b03": { - "balance": "2000000000000000000000" - }, - "1d96bcd58457bbf1d3c2a46ffaf16dbf7d836859": { - "balance": "171313000000000000000" - }, - "bd66ffedb530ea0b2e856dd12ac2296c31fe29e0": { - "balance": "200000000000000000000" - }, - "6e84876dbb95c40b6656e42ba9aea08a993b54dc": { - "balance": "1101932000000000000000" - }, - "a1c4f45a82e1c478d845082eb18875c4ea6539ab": { - "balance": "200000000000000000000000" - }, - "2c964849b1f69cc7cea4442538ed87fdf16cfc8f": { - "balance": "2000000000000000000000" - }, - "45b47105fe42c4712dce6e2a21c05bffd5ea47a9": { - "balance": "2000000000000000000000" - }, - "31e9c00f0c206a4e4e7e0522170dc81e88f3eb70": { - "balance": "2685000000000000000000" - }, - "5fe77703808f823e6c399352108bdb2c527cb87c": { - "balance": "1960000000000000000000" - }, - "2272186ef27dcbe2f5fc373050fdae7f2ace2316": { - "balance": "16100000000000000000000" - }, - "b7576e9d314df41ec5506494293afb1bd5d3f65d": { - "balance": "20000000000000000000" - }, - "ac9fff68c61b011efbecf038ed72db97bb9e7281": { - "balance": "9550000000000000000000" - }, - "cd9529492b5c29e475acb941402b3d3ba50686b0": { - "balance": "1970000000000000000000" - }, - "f19b39389d47b11b8a2c3f1da9124decffbefaf7": { - "balance": "2000000000000000000000" - }, - "9e951f6dc5e352afb8d04299d2478a451259bf56": { - "balance": "72004000000000000000" - }, - "8eb1fbe4e5d3019cd7d30dae9c0d5b4c76fb6331": { - "balance": "2000000000000000000000" - }, - "29cc804d922be91f5909f348b0aaa5d21b607830": { - "balance": "4000000000000000000000" - }, - "5c7b9ec7a2438d1e3c7698b545b9c3fd77b7cd55": { - "balance": "1000000000000000000000" - }, - "a16160851d2b9c349b92e46f829abfb210943595": { - "balance": "1790000000000000000000" - }, - "eac6b98842542ea10bb74f26d7c7488f698b6452": { - "balance": "20000000000000000000000" - }, - "57825aeb09076caa477887fbc9ae37e8b27cc962": { - "balance": "100000000000000000000" - }, - "b35e8a1c0dac7e0e66dbac736a592abd44012561": { - "balance": "14974000000000000000" - }, - "756b84eb85fcc1f4fcdcc2b08db6a86e135fbc25": { - "balance": "3220000000000000000000" - }, - "e13b3d2bbfdcbc8772a23315724c1425167c5688": { - "balance": "1032115000000000000000" - }, - "0a2dcb7a671701dbb8f495728088265873356c8e": { - "balance": "152120000000000000000" - }, - "03cb4c4f4516c4ff79a1b6244fbf572e1c7fea79": { - "balance": "2740000000000000000000" - }, - "98ba4e9ca72fddc20c69b4396f76f8183f7a2a4e": { - "balance": "12800000000000000000000" - }, - "f8087786b42da04ed6d1e0fe26f6c0eefe1e9f5a": { - "balance": "10000000000000000000000" - }, - "02f7f67209b16a17550c694c72583819c80b54ad": { - "balance": "98400000000000000000" - }, - "32bb2e9693e4e085344d2f0dbd46a283e3a087fd": { - "balance": "400000000000000000000" - }, - "9c78963fbc263c09bd72e4f8def74a9475f7055c": { - "balance": "13790000000000000000000" - }, - "27144ca9a7771a836ad50f803f64d869b2ae2b20": { - "balance": "4000000000000000000000" - }, - "cc758d071d25a6320af68c5dc9c4f6955ba94520": { - "balance": "6000000000000000000000" - }, - "cb42b44eb5fd60b5837e4f9eb47267523d1a229c": { - "balance": "865000000000000000000" - }, - "aaf5b207b88b0de4ac40d747cee06e172df6e745": { - "balance": "31428000000000000000000" - }, - "52d380511df19d5ec2807bbcb676581b67fd37a3": { - "balance": "13400000000000000000" - }, - "aa1b3768c16d821f580e76c8e4c8e86d7dc78853": { - "balance": "400000000000000000000" - }, - "41098a81452317c19e3eef0bd123bbe178e9e9ca": { - "balance": "2800000000000000000000" - }, - "267148fd72c54f620a592fb92799319cc4532b5c": { - "balance": "410000000000000000000" - }, - "d7cdbd41fff20df727c70b6255c1ba7606055468": { - "balance": "200000000000000000000" - }, - "0e33fcbbc003510be35785b52a9c5d216bc005f4": { - "balance": "1880000000000000000000" - }, - "6727daf5b9d68efcab489fedec96d7f7325dd423": { - "balance": "2000000000000000000000" - }, - "cd0a161bc367ae0927a92aac9cf6e5086714efca": { - "balance": "2000000000000000000000" - }, - "612667f172135b950b2cd1de10afdece6857b873": { - "balance": "1000000000000000000000" - }, - "900194c4b1074305d19de405b0ac78280ecaf967": { - "balance": "1000000000000000000000" - }, - "51f55ef47e6456a418ab32b9221ed27dba6608ee": { - "balance": "4200000000000000000000" - }, - "0da532c910e3ac0dfb14db61cd739a93353fd05f": { - "balance": "1336866000000000000000" - }, - "21df2dcdaf74b2bf803404dd4de6a35eabec1bbd": { - "balance": "6920000000000000000000" - }, - "f0e7fb9e420a5340d536f40408344feaefc06aef": { - "balance": "1000000000000000000000" - }, - "6742a2cfce8d79a2c4a51b77747498912245cd6a": { - "balance": "258064000000000000000" - }, - "8663a241a0a89e70e182c845e2105c8ad7264bcf": { - "balance": "14825507000000000000000" - }, - "18e113d8177c691a61be785852fa5bb47aeebdaf": { - "balance": "1337000000000000000000" - }, - "1bec4d02ce85fc48feb62489841d85b170586a9b": { - "balance": "2400000000000000000000" - }, - "287cf9d0902ef819a7a5f149445bf1775ee8c47c": { - "balance": "16000000000000000000000" - }, - "28967280214e218a120c5dda37041b111ea36d74": { - "balance": "200000000000000000000" - }, - "a0b771951ce1deee363ae2b771b73e07c4b5e800": { - "balance": "1400000000000000000000" - }, - "29f8fba4c30772b057edbbe62ae7420c390572e1": { - "balance": "1000000000000000000000" - }, - "ee34c7e7995db9f187cff156918cfb6f13f6e003": { - "balance": "1960000000000000000000" - }, - "916bf7e3c545921d3206d900c24f14127cbd5e70": { - "balance": "18020000000000000000000" - }, - "93235f340d2863e18d2f4c52996516138d220267": { - "balance": "73800000000000000000" - }, - "7efec0c6253caf397f71287c1c07f6c9582b5b86": { - "balance": "482839000000000000000" - }, - "8d2e31b08803b2c5f13d398ecad88528209f6057": { - "balance": "9993000000000000000000" - }, - "964eab4b276b4cd8983e15ca72b106900fe41fce": { - "balance": "500000000000000000000" - }, - "eea1e97988de75d821cd28ad6822b22cce988b31": { - "balance": "520000000000000000000" - }, - "278c0bde630ec393b1e7267fc9d7d97019e4145b": { - "balance": "2000000000000000000000" - }, - "82e4461eb9d849f0041c1404219e4272c4900ab4": { - "balance": "2000000000000000000000" - }, - "4a73389298031b8816cca946421c199e18b343d6": { - "balance": "631254000000000000000" - }, - "9a5af31c7e06339ac8b4628d7c4db0ce0f45c8a4": { - "balance": "500000000000000000000" - }, - "cb9b5103e4ce89af4f64916150bff9eecb9faa5c": { - "balance": "500000000000000000000" - }, - "740f641614779dcfa88ed1d425d60db42a060ca6": { - "balance": "998630000000000000000" - }, - "a4e623451e7e94e7e89ba5ed95c8a83a62ffc4ea": { - "balance": "20000000000000000000" - }, - "25a500eeec7a662a841552b5168b707b0de21e9e": { - "balance": "10020000000000000000000" - }, - "185a7fc4ace368d233e620b2a45935661292bdf2": { - "balance": "20000000000000000000000" - }, - "9b68f67416a63bf4451a31164c92f672a68759e9": { - "balance": "60000000000000000000000" - }, - "a38b5bd81a9db9d2b21d5ec7c60552cd02ed561b": { - "balance": "6000000000000000000000" - }, - "61c830f1654718f075ccaba316faacb85b7d120b": { - "balance": "400000000000000000000" - }, - "8392e53776713578015bff4940cf43849d7dcba1": { - "balance": "153190000000000000000" - }, - "dc57477dafa42f705c7fe40eae9c81756e0225f1": { - "balance": "500044000000000000000" - }, - "febc3173bc9072136354002b7b4fb3bfc53f22f1": { - "balance": "370000000000000000000" - }, - "d78f84e38944a0e0255faece48ba4950d4bd39d2": { - "balance": "5000000000000000000000" - }, - "a7a3bb6139b0ada00c1f7f1f9f56d994ba4d1fa8": { - "balance": "2000000000000000000000" - }, - "aa3f29601a1331745e05c42830a15e71938a6237": { - "balance": "1700000000000000000000" - }, - "bec6640f4909b58cbf1e806342961d607595096c": { - "balance": "1999944000000000000000" - }, - "9be3c329b62a28b8b0886cbd8b99f8bc930ce3e6": { - "balance": "74500000000000000000" - }, - "e3eb2c0a132a524f72ccc0d60fee8b41685d39e2": { - "balance": "1970000000000000000000" - }, - "90b1f370f9c1eb0be0fb8e2b8ad96a416371dd8a": { - "balance": "900000000000000000000" - }, - "f2742e6859c569d5f2108351e0bf4dca352a48a8": { - "balance": "10000000000000000000000" - }, - "b134c004391ab4992878337a51ec242f42285742": { - "balance": "2000000000000000000000" - }, - "ab7416ff32254951cbbc624ec7fb45fc7ecaa872": { - "balance": "340000000000000000000" - }, - "9795f64319fc17dd0f8261f9d206fb66b64cd0c9": { - "balance": "200000000000000000000" - }, - "64e03ef070a54703b7184e48276c5c0077ef4b34": { - "balance": "320000000000000000000" - }, - "3430a16381f869f6ea5423915855e800883525a9": { - "balance": "17900000000000000000000" - }, - "f4a367b166d2991a2bfda9f56463a09f252c1b1d": { - "balance": "1970000000000000000000" - }, - "77c4a697e603d42b12056cbba761e7f51d0443f5": { - "balance": "680000000000000000000" - }, - "153ef58a1e2e7a3eb6b459a80ab2a547c94182a2": { - "balance": "96000000000000000000000" - }, - "6dbe8abfa1742806263981371bf3d35590806b6e": { - "balance": "20000000000000000000000" - }, - "4c99dae96481e807c1f99f8b7fbde29b7547c5bf": { - "balance": "150000000000000000000" - }, - "d5b9d277d8aad20697a51f76e20978996bffe055": { - "balance": "143250000000000000000" - }, - "0f24105abbdaa03fa6309ef6c188e51f714a6e59": { - "balance": "200000000000000000000" - }, - "1cb6b2d7cfc559b7f41e6f56ab95c7c958cd0e4c": { - "balance": "1337000000000000000000" - }, - "f37b426547a1642d8033324814f0ede3114fc212": { - "balance": "401100000000000000000" - }, - "318f1f8bd220b0558b95fb33100ffdbb640d7ca6": { - "balance": "4000000000000000000000" - }, - "206d55d5792a514ec108e090599f2a065e501185": { - "balance": "200550000000000000000" - }, - "11d2247a221e70c2d66d17ee138d38c55ffb8640": { - "balance": "10000000000000000000000" - }, - "e8de725eca5def805ff7941d31ac1c2e342dfe95": { - "balance": "2462500000000000000000" - }, - "d561cbbc05515de73ab8cf9eae1357341e7dfdf4": { - "balance": "6000000000000000000000" - }, - "0455dcec8a7fc4461bfd7f37456fce3f4c3caac7": { - "balance": "400000000000000000000" - }, - "5161fd49e847f67455f1c8bb7abb36e985260d03": { - "balance": "1200000000000000000000" - }, - "8e073bad25e42218615f4a0e6b2ea8f8de2230c0": { - "balance": "2402500000000000000000" - }, - "6c08a6dc0173c7342955d1d3f2c065d62f83aec7": { - "balance": "20000000000000000000" - }, - "95cb6d8a6379f94aba8b885669562c4d448e56a7": { - "balance": "2000000000000000000000" - }, - "2805415e1d7fdec6dedfb89e521d10592d743c10": { - "balance": "100000000000000000000" - }, - "daacdaf42226d15cb1cf98fa15048c7f4ceefe69": { - "balance": "300000000000000000000" - }, - "e33df4ce80ccb62a76b12bcdfcecc46289973aa9": { - "balance": "6000000000000000000000" - }, - "8f8cd26e82e7c6defd02dfad07979021cbf7150c": { - "balance": "3000000000000000000000" - }, - "77a17122fa31b98f1711d32a99f03ec326f33d08": { - "balance": "1700000000000000000000" - }, - "6f791d359bc3536a315d6382b88311af8ed6da47": { - "balance": "92000000000000000000" - }, - "de30e49e5ab313214d2f01dcabce8940b81b1c76": { - "balance": "197000000000000000000" - }, - "cf9be9b9ab86c66b59968e67b8d4dcff46b1814a": { - "balance": "660000000000000000000" - }, - "7fdfc88d78bf1b285ac64f1adb35dc11fcb03951": { - "balance": "2287900000000000000000" - }, - "c5134cfbb1df7a20b0ed7057622eeed280947dad": { - "balance": "3800000000000000000000" - }, - "fa9ec8efe08686fa58c181335872ba698560ecab": { - "balance": "1999944000000000000000" - }, - "f6a8635757c5e8c134d20d028cf778cf8609e46a": { - "balance": "1459416000000000000000" - }, - "6265b2e7730f36b776b52d0c9d02ada55d8e3cb6": { - "balance": "1000000000000000000000" - }, - "6a8cea2de84a8df997fd3f84e3083d93de57cda9": { - "balance": "100007000000000000000" - }, - "1b7ed974b6e234ce81247498429a5bd4a0a2d139": { - "balance": "2000000000000000000000" - }, - "9ba53dc8c95e9a472feba2c4e32c1dc4dd7bab46": { - "balance": "1337000000000000000000" - }, - "d7b740dff8c457668fdf74f6a266bfc1dcb723f9": { - "balance": "20000000000000000000" - }, - "07bc2cc8eedc01970700efc9c4fb36735e98cd71": { - "balance": "4000000000000000000000" - }, - "3e1c962063e0d5295941f210dca3ab531eec8809": { - "balance": "3000000000000000000000" - }, - "b447571dacbb3ecbb6d1cf0b0c8f3838e52324e2": { - "balance": "30199000000000000000" - }, - "87764e3677eef604cbc59aed24abdc566b09fc25": { - "balance": "3000000000000000000000" - }, - "03aa622881236dd0f4940c24c324ff8b7b7e2186": { - "balance": "3200000000000000000000" - }, - "a4a7d306f510cd58359428c0d2f7c3609d5674d7": { - "balance": "3349000000000000000000" - }, - "3c83c1701db0388b68210d00f5717cd9bd322c6a": { - "balance": "30000000000000000000000" - }, - "047d5a26d7ad8f8e70600f70a398ddaa1c2db26f": { - "balance": "6000000000000000000000" - }, - "43767bf7fd2af95b72e9312da9443cb1688e4343": { - "balance": "300000000000000000000" - }, - "34a85d6d243fb1dfb7d1d2d44f536e947a4cee9e": { - "balance": "20000000000000000000000" - }, - "65a9dad42e1632ba3e4e49623fab62a17e4d3611": { - "balance": "93120000000000000000" - }, - "48e0cbd67f18acdb7a6291e1254db32e0972737f": { - "balance": "100007000000000000000" - }, - "a5de5e434fdcdd688f1c31b6fb512cb196724701": { - "balance": "800000000000000000000" - }, - "6d63d38ee8b90e0e6ed8f192eda051b2d6a58bfd": { - "balance": "30000000000000000000" - }, - "b079bb4d9866143a6da72ae7ac0022062981315c": { - "balance": "760000000000000000000" - }, - "c0413f5a7c2d9a4b8108289ef6ecd271781524f4": { - "balance": "50000000000000000000000" - }, - "a91a5a7b341f99c535144e20be9c6b3bb4c28e4d": { - "balance": "5431790000000000000000" - }, - "993f146178605e66d517be782ef0b3c61a4e1925": { - "balance": "7011998000000000000000" - }, - "966c04781cb5e67dde3235d7f8620e1ab663a9a5": { - "balance": "75800000000000000000000" - }, - "b3f82a87e59a39d0d2808f0751eb72c2329cdcc5": { - "balance": "5000000000000000000000" - }, - "9b77ebced7e215f0920e8c2b870024f6ecb2ff31": { - "balance": "1000000000000000000000" - }, - "fe697ff22ca547bfc95e33d960da605c6763f35b": { - "balance": "1325000000000000000000" - }, - "480af52076009ca73781b70e43b95916a62203ab": { - "balance": "924171000000000000000" - }, - "a9dc0424c6969d798358b393b1933a1f51bee00a": { - "balance": "20000000000000000000000" - }, - "7aba56f63a48bc0817d6b97039039a7ad62fae2e": { - "balance": "600000000000000000000" - }, - "59d139e2e40c7b97239d23dfaca33858f602d22b": { - "balance": "2000000000000000000000" - }, - "8d6170ff66978e773bb621bf72b1ba7be3a7f87e": { - "balance": "200000000000000000000" - }, - "d668523a90f0293d65c538d2dd6c57673710196e": { - "balance": "39500000000000000000" - }, - "bbb5a0f4802c8648009e8a6998af352cde87544f": { - "balance": "95500000000000000000" - }, - "fc43829ac787ff88aaf183ba352aadbf5a15b193": { - "balance": "3960000000000000000000" - }, - "fe22a0b388668d1ae2643e771dacf38a434223cc": { - "balance": "4000304000000000000000" - }, - "092acb624b08c05510189bbbe21e6524d644ccad": { - "balance": "18200000000000000000" - }, - "8f0538ed71da1155e0f3bde5667ceb84318a1a87": { - "balance": "1940000000000000000000" - }, - "06994cd83aa2640a97b2600b41339d1e0d3ede6c": { - "balance": "250000000000000000000" - }, - "9d460c1b379ddb19a8c85b4c6747050ddf17a875": { - "balance": "3340000000000000000000" - }, - "77a769fafdecf4a638762d5ba3969df63120a41d": { - "balance": "2000000000000000000000" - }, - "5f375b86600c40cca8b2676b7a1a1d1644c5f52c": { - "balance": "78838000000000000000" - }, - "15ee0fc63ebf1b1fc49d7bb38f8863823a2e17d2": { - "balance": "1910000000000000000000" - }, - "6651736fb59b91fee9c93aa0bd6ea2f7b2506180": { - "balance": "500000000000000000000" - }, - "361d9ed80b5bd27cf9f1226f26753258ee5f9b3f": { - "balance": "3530900000000000000000" - }, - "c9b6b686111691ee6aa197c7231a88dc60bd295d": { - "balance": "500000000000000000000" - }, - "e9b4a4853577a9dbcc2e795be0310d1bed28641a": { - "balance": "1000000000000000000000" - }, - "36758e049cd98bcea12277a676f9297362890023": { - "balance": "4000000000000000000000" - }, - "6bb50813146a9add42ee22038c9f1f7469d47f47": { - "balance": "200200000000000000000" - }, - "6de4b581385cf7fc9fe8c77d131fe2ee7724c76a": { - "balance": "2308840000000000000000" - }, - "d2a5a024230a57ccc666760b89b0e26cafd189c7": { - "balance": "49997115000000000000000" - }, - "65af9087e05167715497c9a5a749189489004def": { - "balance": "835000000000000000000" - }, - "ead21c1deccfbf1c5cd96688a2476b69ba07ce4a": { - "balance": "72800000000000000000" - }, - "e308435204793764f5fcbe65eb510f5a744a655a": { - "balance": "200000000000000000000" - }, - "9376dce2af2ec8dcda741b7e7345664681d93668": { - "balance": "1000000000000000000000" - }, - "a1b47c4d0ed6018842e6cfc8630ac3a3142e5e6b": { - "balance": "20000000000000000000" - }, - "e2198c8ca1b399f7521561fd5384a7132fba486b": { - "balance": "1015200000000000000000" - }, - "92c13fe0d6ce87fd50e03def9fa6400509bd7073": { - "balance": "40000000000000000000" - }, - "7517f16c28d132bb40e3ba36c6aef131c462da17": { - "balance": "18200000000000000000" - }, - "6a023af57d584d845e698736f130db9db40dfa9a": { - "balance": "98800000000000000000" - }, - "1518627b88351fede796d3f3083364fbd4887b0c": { - "balance": "16000000000000000000000" - }, - "f5b6e9061a4eb096160777e26762cf48bdd8b55d": { - "balance": "254030000000000000000" - }, - "28073efc17d05cab3195c2db332b61984777a612": { - "balance": "1000000000000000000000" - }, - "f06a854a3c5dc36d1c49f4c87d6db333b57e4add": { - "balance": "10000000000000000000000" - }, - "9225983860a1cb4623c72480ac16272b0c95e5f5": { - "balance": "2000000000000000000000" - }, - "5260dc51ee07bddaababb9ee744b393c7f4793a6": { - "balance": "34040000000000000000" - }, - "0f127bbf8e311caea2ba502a33feced3f730ba42": { - "balance": "188000000000000000000" - }, - "17d521a8d9779023f7164d233c3b6420ffd223ed": { - "balance": "20000000000000000000" - }, - "8c2b7d8b608d28b77f5caa9cd645242a823e4cd9": { - "balance": "1820000000000000000000" - }, - "6e866d032d405abdd65cf651411d803796c22311": { - "balance": "2000000000000000000000" - }, - "dc51b2dc9d247a1d0e5bc36ca3156f7af21ff9f6": { - "balance": "1000000000000000000000" - }, - "c84d9bea0a7b9f140220fd8b9097cfbfd5edf564": { - "balance": "123047000000000000000" - }, - "ff86e5e8e15b53909600e41308dab75f0e24e46b": { - "balance": "902400000000000000000" - }, - "d7164aa261c09ad9b2b5068d453ed8eb6aa13083": { - "balance": "3000000000000000000000" - }, - "76aaf8c1ac012f8752d4c09bb46607b6651d5ca8": { - "balance": "20000000000000000000" - }, - "41786a10d447f484d33244ccb7facd8b427b5b8c": { - "balance": "1000000000000000000000" - }, - "2e0c57b47150f95aa6a7e16ab9b1cbf54328979a": { - "balance": "100000000000000000000" - }, - "3f747237806fed3f828a6852eb0867f79027af89": { - "balance": "1500000000000000000000" - }, - "a568db4d57e4d67462d733c69a9e0fe26e218327": { - "balance": "1096140000000000000000" - }, - "1f88f8a1338fc7c10976abcd3fb8d38554b5ec9c": { - "balance": "13400000000000000000" - }, - "d1ea4d72a67b5b3e0f315559f52bd0614d713069": { - "balance": "2000000000000000000000" - }, - "bfaeb91067617dcf8b44172b02af615674835dba": { - "balance": "160661000000000000000" - }, - "b71a13ba8e95167b80331b52d69e37054fe7a826": { - "balance": "200000000000000000000" - }, - "b67a80f170197d96cdcc4ab6cba627b4afa6e12c": { - "balance": "2400000000000000000000" - }, - "35af040a0cc2337a76af288154c7561e1a233349": { - "balance": "1000000000000000000000" - }, - "c86190904b8d079ec010e462cbffc90834ffaa5c": { - "balance": "10100000000000000000000" - }, - "383304dd7a5720b29c1a10f60342219f48032f80": { - "balance": "5600000000000000000000" - }, - "191313525238a21c767457a91374f02200c55448": { - "balance": "116400000000000000000" - }, - "cc4a2f2cf86cf3e43375f360a4734691195f1490": { - "balance": "1348127000000000000000" - }, - "4e020779b5ddd3df228a00cb48c2fc979da6ae38": { - "balance": "2000000000000000000000" - }, - "e206fb7324e9deb79e19903496d6961b9be56603": { - "balance": "100000000000000000000" - }, - "3ae160e3cd60ae31b9d6742d68e14e76bd96c517": { - "balance": "30000000000000000000" - }, - "1f7d8e86d6eeb02545aad90e91327bd369d7d2f3": { - "balance": "20000000000000000000" - }, - "68c7d1711b011a33f16f1f55b5c902cce970bdd7": { - "balance": "152000000000000000000" - }, - "637be71b3aa815ff453d5642f73074450b64c82a": { - "balance": "2000000000000000000000" - }, - "1584a2c066b7a455dbd6ae2807a7334e83c35fa5": { - "balance": "130000000000000000000" - }, - "9c05e9d0f0758e795303717e31da213ca157e686": { - "balance": "1000000000000000000000" - }, - "4f1a2da54a4c6da19d142412e56e815741db2325": { - "balance": "100000000000000000000" - }, - "9a4ca8b82117894e43db72b9fa78f0b9b93ace09": { - "balance": "50000000000000000000" - }, - "26c99f8849c9802b83c861217fd07a9e84cdb79d": { - "balance": "300000000000000000000" - }, - "45c0d19f0b8e054f9e893836d5ecae7901af2812": { - "balance": "5000000000000000000000" - }, - "00dc01cbf44978a42e8de8e436edf94205cfb6ec": { - "balance": "1458440000000000000000" - }, - "de7dee220f0457a7187d56c1c41f2eb00ac56021": { - "balance": "629924000000000000000" - }, - "1c128bd6cda5fca27575e4b43b3253c8c4172afe": { - "balance": "2000000000000000000000" - }, - "666746fb93d1935c5a3c684e725010c4fad0b1d8": { - "balance": "20000000000000000000" - }, - "51d78b178d707e396e8710965c4f41b1a1d9179d": { - "balance": "110600000000000000000" - }, - "68f7573cd457e14c03fea43e302d30347c10705c": { - "balance": "5000000000000000000000" - }, - "9d30cb237bc096f17036fc80dd21ca68992ca2d9": { - "balance": "30380000000000000000000" - }, - "fbcfcc4a7b0f26cf26e9f3332132e2fc6a230766": { - "balance": "8000000000000000000000" - }, - "b166e37d2e501ae73c84142b5ffb5aa655dd5a99": { - "balance": "1999000000000000000000" - }, - "6df24f6685a62f791ba337bf3ff67e91f3d4bc3a": { - "balance": "2166000000000000000000" - }, - "92e435340e9d253c00256389f52b067d55974e76": { - "balance": "268000000000000000000" - }, - "ea53d26564859d9e90bb0e53b7abf560e0162c38": { - "balance": "400000000000000000000" - }, - "e26657f0ed201ea2392c9222b80a7003608ddf30": { - "balance": "40000000000000000000" - }, - "f4177a0d85d48b0e264211ce2aa2efd3f1b47f08": { - "balance": "3593425000000000000000" - }, - "9d47ba5b4c8505ad8da42934280b61a0e1e8b971": { - "balance": "100000000000000000000" - }, - "63c2a3d235e5eeabd0d4a6afdb89d94627396495": { - "balance": "1241620000000000000000" - }, - "446a8039cecf9dce4879cbcaf3493bf545a88610": { - "balance": "7000000000000000000000" - }, - "7fa37ed67887751a471f0eb306be44e0dbcd6089": { - "balance": "1060000000000000000000" - }, - "26d4a16891f52922789217fcd886f7fce296d400": { - "balance": "2000000000000000000000" - }, - "487e108502b0b189ef9c8c6da4d0db6261eec6c0": { - "balance": "1910000000000000000000" - }, - "7484d26becc1eea8c6315ec3ee0a450117dc86a0": { - "balance": "12000000000000000000000" - }, - "ad9e97a0482f353a05c0f792b977b6c7e811fa5f": { - "balance": "200000000000000000000" - }, - "2273bad7bc4e487622d175ef7a66988b6a93c4ee": { - "balance": "20000000000000000000" - }, - "3b93b16136f11eaf10996c95990d3b2739ccea5f": { - "balance": "10000000000000000000000" - }, - "f3f1fa3918ca34e2cf7e84670b1f4d8eca160db3": { - "balance": "680000000000000000000" - }, - "88a2154430c0e41147d3c1fee3b3b006f851edbd": { - "balance": "999972000000000000000" - }, - "25185f325acf2d64500698f65c769ddf68301602": { - "balance": "5000000000000000000000" - }, - "e9cafe41a5e8bbd90ba02d9e06585b4eb546c57f": { - "balance": "2000000000000000000000" - }, - "95681cdae69b2049ce101e325c759892cac3f811": { - "balance": "2857600000000000000000" - }, - "475066f9ad26655196d5535327bbeb9b7929cb04": { - "balance": "3040000000000000000000" - }, - "6685fd2e2544702c360b8bb9ee78f130dad16da5": { - "balance": "2000000000000000000000" - }, - "45e68db94c7d0ab7ac41857a71d67147870f4e71": { - "balance": "400000000000000000000000" - }, - "4ad95d188d6464709add2555fb4d97fe1ebf311f": { - "balance": "346000000000000000000" - }, - "73bedd6fda7ba3272185087b6351fc133d484e37": { - "balance": "5057200000000000000000" - }, - "1ea4715504c6af107b0194f4f7b1cb6fcccd6f4b": { - "balance": "590598000000000000000" - }, - "77306ffe2e4a8f3ca826c1a249f7212da43aeffd": { - "balance": "20000000000000000000000" - }, - "eb453f5a3adddd8ab56750fadb0fe7f94d9c89e7": { - "balance": "20000000000000000000" - }, - "7201d1c06920cd397ae8ad869bcda6e47ffb1b5a": { - "balance": "20000000000000000000" - }, - "821cb5cd05c7ef909fe1be60733d8963d760dc41": { - "balance": "4000000000000000000000" - }, - "496e319592b341eaccd778dda7c8196d54cac775": { - "balance": "9250000000000000000000" - }, - "88609e0a465b6e99fce907166d57e9da0814f5c8": { - "balance": "20000000000000000000000" - }, - "c7ec62b804b1f69b1e3070b5d362c62fb309b070": { - "balance": "13068074000000000000000" - }, - "3eb9ef06d0c259040319947e8c7a6812aa0253d8": { - "balance": "167000000000000000000" - }, - "cbf37ff854a2f1ce53934494777892d3ec655782": { - "balance": "10000000000000000000000" - }, - "02b1af72339b2a2256389fd64607de24f0de600a": { - "balance": "2000000000000000000000" - }, - "a8beb91c2b99c8964aa95b6b4a184b1269fc3483": { - "balance": "400000000000000000000" - }, - "922a20c79a1d3a26dd3829677bf1d45c8f672bb6": { - "balance": "4000000000000000000000" - }, - "c5843399d150066bf7979c34ba294620368ad7c0": { - "balance": "200000000000000000000" - }, - "8cd0cd22e620eda79c0461e896c93c44837e2968": { - "balance": "2000000000000000000000" - }, - "6170dd0687bd55ca88b87adef51cfdc55c4dd458": { - "balance": "2005160000000000000000" - }, - "eed384ef2d41d9d203974e57c12328ea760e08ea": { - "balance": "1000000000000000000000" - }, - "b129a5cb7105fe810bd895dc7206a991a4545488": { - "balance": "30000000000000000000" - }, - "3872f48dc5e3f817bc6b2ad2d030fc5e0471193d": { - "balance": "4000000000000000000000" - }, - "514b7512c9ae5ea63cbf11715b63f21e18d296c1": { - "balance": "1999944000000000000000" - }, - "7ab256b204800af20137fabcc916a23258752501": { - "balance": "20000000000000000000000" - }, - "fc66faba277f4b5de64ad45eb19c31e00ced3ed5": { - "balance": "5640000000000000000000" - }, - "39824f8bced176fd3ea22ec6a493d0ccc33fc147": { - "balance": "4000000000000000000000" - }, - "e338e859fe2e8c15554848b75caecda877a0e832": { - "balance": "1801800000000000000000" - }, - "e53c68796212033e4e6f9cff56e19c461eb454f9": { - "balance": "1000000000000000000000" - }, - "8461ecc4a6a45eb1a5b947fb86b88069b91fcd6f": { - "balance": "2000000000000000000000" - }, - "6b4b99cb3fa9f7b74ce3a48317b1cd13090a1a7a": { - "balance": "57300000000000000000" - }, - "97de21e421c37fe4b8025f9a51b7b390b5df7804": { - "balance": "80000000000000000000000" - }, - "d25aecd7eb8bd6345b063b5dbd271c77d3514494": { - "balance": "1820000000000000000000" - }, - "57b23d6a1adc06c652a779c6a7fb6b95b9fead66": { - "balance": "200000000000000000000" - }, - "0d658014a199061cf6b39433140303c20ffd4e5a": { - "balance": "8200000000000000000000" - }, - "30eac740e4f02cb56eef0526e5d300322600d03e": { - "balance": "1970000000000000000000" - }, - "4eead40aad8c73ef08fc84bc0a92c9092f6a36bf": { - "balance": "26740000000000000000" - }, - "30f7d025d16f7bee105580486f9f561c7bae3fef": { - "balance": "500000000000000000000" - }, - "0977bfba038a44fb49b03970d8d8cf2cb61f8b25": { - "balance": "420000000000000000000" - }, - "b14bbeff70720975dc6191b2a44ff49f2672873c": { - "balance": "143000000000000000000" - }, - "d588c3a5df228185d98ee7e60748255cdea68b01": { - "balance": "4000000000000000000000" - }, - "225d35faedb391c7bc2db7fa9071160405996d00": { - "balance": "167774000000000000000" - }, - "c0e457bd56ec36a1246bfa3230fff38e5926ef22": { - "balance": "1940000000000000000000" - }, - "2a9c57fe7b6b138a920d676f3c76b6c2a0eef699": { - "balance": "9400000000000000000000" - }, - "36df8f883c1273ec8a171f7a33cfd649b1fe6075": { - "balance": "227290000000000000000" - }, - "234f46bab73fe45d31bf87f0a1e0466199f2ebac": { - "balance": "485000000000000000000" - }, - "a2e1b8aa900e9c139b3fa122354f6156d92a18b1": { - "balance": "500000000000000000000" - }, - "517cd7608e5d0d83a26b717f3603dac2277dc3a4": { - "balance": "2000000000000000000000" - }, - "75f7539d309e9039989efe2e8b2dbd865a0df088": { - "balance": "2460000000000000000000" - }, - "4b792e29683eb586e394bb33526c6001b397999e": { - "balance": "600000000000000000000" - }, - "a34f9d568bf7afd94c2a5b8a5ff55c66c4087999": { - "balance": "2444000000000000000000" - }, - "4b31bf41abc75c9ae2cd8f7f35163b6e2b745054": { - "balance": "382000000000000000000" - }, - "e35453eef2cc3c7a044d0ac134ba615908fa82ee": { - "balance": "147510000000000000000" - }, - "7aa79ac04316cc8d08f20065baa6d4142897d54e": { - "balance": "1400000000000000000000" - }, - "f1dc8ac81042c67a9c3c6792b230c46ac016ca10": { - "balance": "200000000000000000000" - }, - "2bb366b9edcb0da680f0e10b3b6e28748190d6c3": { - "balance": "5799400000000000000000" - }, - "a567770b6ae320bdde50f904d663e746a61dace6": { - "balance": "2000000000000000000000" - }, - "d9d42fd13ebd4bf69cac5e9c7e82483ab46dd7e9": { - "balance": "5348000000000000000000" - }, - "27830c5f6023afaaf79745676c204a0faccda0ba": { - "balance": "240000000000000000000" - }, - "3cb179cb4801a99b95c3b0c324a2bdc101a65360": { - "balance": "26000000000000000000" - }, - "976e3ceaf3f1af51f8c29aff5d7fa21f0386d8ee": { - "balance": "240000000000000000000" - }, - "752a5ee232612cd3005fb26e5b597de19f776be6": { - "balance": "5460000000000000000000" - }, - "7d5aa33fc14b51841a06906edb2bb49c2a117269": { - "balance": "300048000000000000000" - }, - "55ca6abe79ea2497f46fdbb830346010fe469cbe": { - "balance": "5730000000000000000000" - }, - "6bec311ad05008b4af353c958c40bd06739a3ff3": { - "balance": "16380000000000000000000" - }, - "30e9698cf1e08a9d048bd8d8048f28be7ed9409f": { - "balance": "6685000000000000000000" - }, - "9afa536b4c66bc38d875c4b30099d9261fdb38eb": { - "balance": "205981000000000000000" - }, - "6b63a2dfb2bcd0caec0022b88be30c1451ea56aa": { - "balance": "809021000000000000000" - }, - "d07be0f90997caf903c8ac1d53cde904fb190741": { - "balance": "1000200000000000000000" - }, - "893cdddf5377f3c751bf2e541120045a47cba101": { - "balance": "100000000000000000000" - }, - "c1cdc601f89c0428b31302d187e0dc08ad7d1c57": { - "balance": "6000000000000000000000" - }, - "8f8acb107607388479f64baaabea8ff007ada97d": { - "balance": "27281800000000000000000" - }, - "88bc43012edb0ea9f062ac437843250a39b78fbb": { - "balance": "20000000000000000000000" - }, - "fcfc3a5004d678613f0b36a642269a7f371c3f6a": { - "balance": "1000000000000000000000" - }, - "f509557e90183fbf0f0651a786487bcc428ba175": { - "balance": "194000000000000000000" - }, - "e3d915eda3b825d6ee4af9328d32ac18ada35497": { - "balance": "500000000000000000000" - }, - "f237ef05261c34d79cc22b860de0f17f793c3860": { - "balance": "200000000000000000000" - }, - "a3a2e319e7d3a1448b5aa2468953160c2dbcba71": { - "balance": "2000000000000000000000" - }, - "3a368efe4ad786e26395ec9fc6ad698cae29fe01": { - "balance": "632200000000000000000" - }, - "8e3240b0810e1cf407a500804740cf8d616432a4": { - "balance": "40309000000000000000" - }, - "5691dd2f6745f20e22d2e1d1b955aa2903d65656": { - "balance": "1969606000000000000000" - }, - "5f93ff832774db5114c55bb4bf44ccf3b58f903f": { - "balance": "192026650000000000000000" - }, - "2c1cc6e18c152488ba11c2cc1bcefa2df306abd1": { - "balance": "1670000000000000000000" - }, - "bde9786a84e75b48f18e726dd78d70e4af3ed802": { - "balance": "5730000000000000000000" - }, - "79551cede376f747e3716c8d79400d766d2e0195": { - "balance": "46250000000000000000000" - }, - "49f028395b5a86c9e07f7778630e4c2e3d373a77": { - "balance": "122735000000000000000" - }, - "6a3694424c7cc6b8bcd9bccaba540cc1f5df18d7": { - "balance": "2000000000000000000000" - }, - "068e29b3f191c812a6393918f71ab933ae6847f2": { - "balance": "1999944000000000000000" - }, - "6e64e6129f224e378c0e6e736a7e7a06c211e9ec": { - "balance": "1000000000000000000000" - }, - "c4c15318d370c73318cc18bdd466dbaa4c6603bf": { - "balance": "19700000000000000000" - }, - "8035bcffaefdeeea35830c497d14289d362023de": { - "balance": "300000000000000000000" - }, - "a997dfc7986a27050848fa1c64d7a7d6e07acca2": { - "balance": "143000000000000000000" - }, - "2fe13a8d0785de8758a5e41876c36e916cf75074": { - "balance": "4000000000000000000000" - }, - "6f24c9af2b763480515d1b0951bb77a540f1e3f9": { - "balance": "1970000000000000000000" - }, - "4c23b370fc992bb67cec06e26715b62f0b3a4ac3": { - "balance": "10000000000000000000000" - }, - "4ac07673e42f64c1a25ec2fa2d86e5aa2b34e039": { - "balance": "2000000000000000000000" - }, - "117db836377fe15455e02c2ebda40b1ceb551b19": { - "balance": "6000000000000000000000" - }, - "ef1c0477f1184d60accab374d374557a0a3e10f3": { - "balance": "152000000000000000000" - }, - "99fe0d201228a753145655d428eb9fd94985d36d": { - "balance": "1939268000000000000000" - }, - "b3731b046c8ac695a127fd79d0a5d5fa6ae6d12e": { - "balance": "1998000000000000000000" - }, - "dce30c31f3ca66721ecb213c809aab561d9b52e4": { - "balance": "2000000000000000000000" - }, - "ddd69c5b9bf5eb5a39cee7c3341a120d973fdb34": { - "balance": "1987730000000000000000" - }, - "216e41864ef98f060da08ecae19ad1166a17d036": { - "balance": "5730000000000000000000" - }, - "6a53d41ae4a752b21abed5374649953a513de5e5": { - "balance": "2000000000000000000000" - }, - "20dd8fcbb46ea46fe381a68b8ca0ea5be21fe9a5": { - "balance": "2000000000000000000000" - }, - "19732bf973055dbd91a4533adaa2149a91d38380": { - "balance": "2000000000000000000000" - }, - "51ea1c0934e3d04022ed9c95a087a150ef705e81": { - "balance": "6280000000000000000000" - }, - "a0de5c601e696635c698b7ae9ca4539fc7b941ec": { - "balance": "346150000000000000000" - }, - "94e1f5cb9b8abace03a1a6428256553b690c2355": { - "balance": "20000000000000000000" - }, - "a539b4a401b584dfe0f344b1b422c65543167e2e": { - "balance": "200000000000000000000" - }, - "50584d9206a46ce15c301117ee28f15c30e60e75": { - "balance": "13400000000000000000" - }, - "856eb204241a87830fb229031343dc30854f581a": { - "balance": "1000000000000000000000" - }, - "9dd46b1c6d3f05e29e9c6f037eed9a595af4a9aa": { - "balance": "500000000000000000000" - }, - "8925da4549e15155e57a628522cea9dddf627d81": { - "balance": "1000070000000000000000" - }, - "a89df34859edd7c820db887740d8ff9e15157c7b": { - "balance": "2000000000000000000000" - }, - "ad9f4c890a3b511cee51dfe6cfd7f1093b76412c": { - "balance": "506600000000000000000" - }, - "f8c7f34a38b31801da43063477b12b27d0f203ff": { - "balance": "494800000000000000000" - }, - "a642501004c90ea9c9ed1998ba140a4cd62c6f5f": { - "balance": "250543000000000000000" - }, - "508cf19119db70aa86454253da764a2cb1b2be1a": { - "balance": "1000000000000000000000" - }, - "2979741174a8c1ea0b7f9edf658177859417f512": { - "balance": "461283000000000000000" - }, - "654f524847b3a6acc0d3d5f1f362b603edf65f96": { - "balance": "8000000000000000000000" - }, - "5cf18fa7c8a7c0a2b3d5efd1990f64ddc569242c": { - "balance": "1000000000000000000000" - }, - "17e82e7078dc4fd9e879fb8a50667f53a5c54591": { - "balance": "200000000000000000000" - }, - "8b07d050754dc9ba230db01c310afdb5395aa1b3": { - "balance": "118080000000000000000" - }, - "5f77a107ab1226b3f95f10ee83aefc6c5dff3edc": { - "balance": "500000000000000000000" - }, - "475a6193572d4a4e59d7be09cb960ddd8c530e2f": { - "balance": "667323000000000000000" - }, - "6470a4f92ec6b0fccd01234fa59023e9ff1f3aac": { - "balance": "3000000000000000000000" - }, - "2fbcef3384d420e4bf61a0669990bc7054f1a5af": { - "balance": "2000000000000000000000" - }, - "bbabf6643beb4bd01c120bd0598a0987d82967d1": { - "balance": "3342500000000000000000" - }, - "41a2f2e6ecb86394ec0e338c0fc97e9c5583ded2": { - "balance": "2009400000000000000000" - }, - "fb9473cf7712350a1fa0395273fc80560752e4fb": { - "balance": "123300000000000000000" - }, - "38b2197106123387a0d4de368431a8bacdda30e2": { - "balance": "20000000000000000000" - }, - "5ed56115bd6505a88273df5c56839470d24a2db7": { - "balance": "65601000000000000000" - }, - "523f6d64690fdacd942853591bb0ff20d3656d95": { - "balance": "1820000000000000000000" - }, - "55caff4bba04d220c9a5d2018672ec85e31ef83e": { - "balance": "2000000000000000000000" - }, - "65af8d8b5b1d1eedfa77bcbc96c1b133f83306df": { - "balance": "98000000000000000000" - }, - "7456c5b2c5436e3e571008933f1805ccfe34e9ec": { - "balance": "1000000000000000000000" - }, - "a6eebbe464d39187bf80ca9c13d72027ec5ba8be": { - "balance": "3000000000000000000000" - }, - "dd35cfdbcb993395537aecc9f59085a8d5ddb6f5": { - "balance": "1000000000000000000000" - }, - "98e2b6d606fd2d6991c9d6d4077fdf3fdd4585da": { - "balance": "901520000000000000000" - }, - "860f5ffc10de767ded807f71e861d647dfd219b1": { - "balance": "10000000000000000000000" - }, - "1a644a50cbc2aee823bd2bf243e825be4d47df02": { - "balance": "100007000000000000000" - }, - "a8455b411765d6901e311e726403091e42c56683": { - "balance": "3380000000000000000000" - }, - "3a86ee94862b743dd34f410969d94e2c5652d4ad": { - "balance": "201610000000000000000" - }, - "a57360f002e0d64d2d74457d8ca4857ee00bcddf": { - "balance": "335780000000000000000" - }, - "e59b3bd300893f97233ef947c46f7217e392f7e9": { - "balance": "1000000000000000000000" - }, - "9f3a74fd5e7edcc1162993171381cbb632b7cff0": { - "balance": "10000000000000000000000" - }, - "675d5caa609bf70a18aca580465d8fb7310d1bbb": { - "balance": "20000000000000000000000" - }, - "77f609ca8720a023262c55c46f2d26fb3930ac69": { - "balance": "17300000000000000000" - }, - "f8ac4a39b53c11307820973b441365cffe596f66": { - "balance": "2000000000000000000000" - }, - "112634b4ec30ff786e024159f796a57939ea144e": { - "balance": "1999944000000000000000" - }, - "49d2c28ee9bc545eaaf7fd14c27c4073b4bb5f1a": { - "balance": "1474134000000000000000" - }, - "91cc46aa379f856a6640dccd5a648a7902f849d9": { - "balance": "200000000000000000000" - }, - "b46440c797a556e04c7d9104660491f96bb076bf": { - "balance": "14900000000000000000" - }, - "e5968797468ef767101b761d431fce14abffdbb4": { - "balance": "8040000000000000000000" - }, - "c0895efd056d9a3a81c3da578ada311bfb9356cf": { - "balance": "200000000000000000000" - }, - "76846f0de03b5a76971ead298cdd08843a4bc6c6": { - "balance": "15500000000000000000" - }, - "5f708eaf39d823946c51b3a3e9b7b3c003e26341": { - "balance": "1820000000000000000000" - }, - "24f7450ddbf18b020feb1a2032d9d54b633edf37": { - "balance": "50000000000000000000" - }, - "cae3a253bcb2cf4e13ba80c298ab0402da7c2aa0": { - "balance": "5400000000000000000000" - }, - "91e8810652e8e6161525d63bb7751dc20f676076": { - "balance": "725000000000000000000" - }, - "543629c95cdef428ad37d453ca9538a9f90900ac": { - "balance": "43250000000000000000000" - }, - "6e79edd4845b076e4cd88d188b6e432dd93f35aa": { - "balance": "955000000000000000000" - }, - "bd325d4029e0d8729f6d399c478224ae9e7ae41e": { - "balance": "3880000000000000000000" - }, - "42cecfd2921079c2d7df3f08b07aa3beee5e219a": { - "balance": "1000000000000000000000" - }, - "3690246ba3c80679e22eac4412a1aefce6d7cd82": { - "balance": "20000000000000000000000" - }, - "577aeee8d4bc08fc97ab156ed57fb970925366be": { - "balance": "333046000000000000000" - }, - "fe00bf439911a553982db638039245bcf032dbdc": { - "balance": "394000000000000000000" - }, - "91f624b24a1fa5a056fe571229e7379db14b9a1e": { - "balance": "11999974000000000000000" - }, - "f206d328e471d0117b246d2a4619827709e96df3": { - "balance": "3001000000000000000000" - }, - "073f1ed1c9c3e9c52a9b0249a5c1caa0571fdf05": { - "balance": "70400000000000000000" - }, - "f56048dd2181d4a36f64fcecc6215481e42abc15": { - "balance": "200000000000000000000" - }, - "ef76a4cd8febcbc9b818f17828f8d93473f3f3cb": { - "balance": "4000000000000000000000" - }, - "1031e0ecb54985ae21af1793950dc811888fde7c": { - "balance": "20000000000000000000" - }, - "8e0fee38685a94aabcd7ce857b6b1409824f75b8": { - "balance": "500000000000000000000" - }, - "f0cbef84e169630098d4e301b20208ef05846ac9": { - "balance": "259084000000000000000" - }, - "bbca65b3266ea2fb73a03f921635f912c7bede00": { - "balance": "1970000000000000000000" - }, - "0aec2e426ed6cc0cf3c249c1897eac47a7faa9bd": { - "balance": "200000000000000000000" - }, - "b8f30758faa808dbc919aa7b425ec922b93b8129": { - "balance": "1000076000000000000000" - }, - "936dcf000194e3bff50ac5b4243a3ba014d661d8": { - "balance": "10000000000000000000000" - }, - "b14ddb0386fb606398b8cc47565afae00ff1d66a": { - "balance": "2973024000000000000000" - }, - "2ec95822eb887bc113b4712a4dfd7f13b097b5e7": { - "balance": "1000000000000000000000" - }, - "0136a5af6c3299c6b5f005fdaddb148c070b299b": { - "balance": "20368000000000000000" - }, - "37cb868d2c3f95b257611eb34a4188d58b749802": { - "balance": "2000000000000000000000" - }, - "cd7f09d7ed66d0c38bc5ad4e32b7f2b08dc1b30d": { - "balance": "1148000000000000000000" - }, - "b5fa8184e43ed3e0b8ab91216461b3528d84fd09": { - "balance": "2680000000000000000000" - }, - "3dbf0dbfd77890800533f09dea8301b9f025d2a6": { - "balance": "1000000000000000000000" - }, - "b553d25d6b5421e81c2ad05e0b8ba751f8f010e3": { - "balance": "2000000000000000000000" - }, - "dbf8b13967f55125272de0562536c450ba5655a0": { - "balance": "2046830000000000000000" - }, - "0f6e840a3f2a24647d8e43e09d45c7c335df4248": { - "balance": "2500000000000000000000" - }, - "fa2fd29d03fee9a07893df3a269f56b72f2e1e64": { - "balance": "10000000000000000000000" - }, - "8b57b2bc83cc8d4de331204e893f2f3b1db1079a": { - "balance": "40000000000000000000" - }, - "7f541491d2ac00d2612f94aa7f0bcb014651fbd4": { - "balance": "376000000000000000000" - }, - "4f4a9be10cd5d3fb5de48c17be296f895690645b": { - "balance": "40000000000000000000000" - }, - "45d1c9eedf7cab41a779057b79395f5428d80528": { - "balance": "2000000000000000000000" - }, - "662334814724935b7931ddca6100e00d467727cd": { - "balance": "637000000000000000000" - }, - "2c52c984102ee0cd3e31821b84d408930efa1ac7": { - "balance": "2000000000000000000000" - }, - "000d836201318ec6899a67540690382780743280": { - "balance": "200000000000000000000" - }, - "81498ca07b0f2f17e8bbc7e61a7f4ae7be66b78b": { - "balance": "101600000000000000000" - }, - "7860a3de38df382ae4a4dce18c0c07b98bce3dfa": { - "balance": "1000000000000000000000" - }, - "5e8e4df18cf0af770978a8df8dac90931510a679": { - "balance": "2000000000000000000000" - }, - "05d68dad61d3bbdfb3f779265c49474aff3fcd30": { - "balance": "39399000000000000000" - }, - "96eafbf2fb6f4db9a436a74c45b5654452e23819": { - "balance": "20000000000000000000" - }, - "d7d7f2caa462a41b3b30a34aeb3ba61010e2626f": { - "balance": "2000000000000000000000" - }, - "0b71f554122469ef978e2f1fefd7cbb410982772": { - "balance": "3880000000000000000000" - }, - "504666ce8931175e11a5ed11c1dcaa06e57f4e66": { - "balance": "11792000000000000000000" - }, - "d00f067286c0fbd082f9f4a61083ec76deb3cee6": { - "balance": "1000000000000000000000" - }, - "02e4cb22be46258a40e16d4338d802fffd00c151": { - "balance": "379786000000000000000" - }, - "1c13d38637b9a47ce79d37a86f50fb409c060728": { - "balance": "1337000000000000000000" - }, - "e30212b2011bb56bdbf1bc35690f3a4e0fd905ea": { - "balance": "8022000000000000000000" - }, - "1df6911672679bb0ef3509038c0c27e394fdfe30": { - "balance": "540000000000000000000" - }, - "2b8fe4166e23d11963c0932b8ade8e0145ea0770": { - "balance": "43250000000000000000000" - }, - "6509eeb1347e842ffb413e37155e2cbc738273fd": { - "balance": "2000000000000000000000" - }, - "8b7e9f6f05f7e36476a16e3e7100c9031cf404af": { - "balance": "1000000000000000000000" - }, - "bec8caf7ee49468fee552eff3ac5234eb9b17d42": { - "balance": "2000000000000000000000" - }, - "38898bbb4553e00bbfd0cf268b2fc464d154add5": { - "balance": "320000000000000000000" - }, - "cbb3189e4bd7f45f178b1c30c76e26314d4a4b0a": { - "balance": "295007000000000000000" - }, - "be1cd7f4c472070968f3bde268366b21eeea8321": { - "balance": "4300000000000000000000" - }, - "976a18536af41874426308871bcd1512a775c9f8": { - "balance": "10000000000000000000000" - }, - "e9c758f8da41e3346e4350e5ac3976345c6c1082": { - "balance": "1930050000000000000000" - }, - "64ec8a5b743f3479e707dae9ee20ddaa4f40f1d9": { - "balance": "200000000000000000000" - }, - "9e01765aff08bc220550aca5ea2e1ce8e5b09923": { - "balance": "1000000000000000000000" - }, - "ba0f39023bdb29eb1862a9f9059cab5d306e662f": { - "balance": "2000000000000000000000" - }, - "2baf8d6e221174124820ee492b9459ec4fadafbb": { - "balance": "2000000000000000000000" - }, - "655d5cd7489629e2413c2105b5a172d933c27af8": { - "balance": "4040060000000000000000" - }, - "badc2aef9f5951a8d78a6b35c3d0b3a4e6e2e739": { - "balance": "6000000000000000000000" - }, - "e64f6e1d6401b56c076b64a1b0867d0b2f310d4e": { - "balance": "51570000000000000000" - }, - "7a8563867901206f3f2bf0fa3e1c8109cabccd85": { - "balance": "137000000000000000000" - }, - "d17fbe22d90462ed37280670a2ea0b3086a0d6d6": { - "balance": "199955000000000000000" - }, - "e96d7d4cdd15553a4e4d316d6d6480ca3cea1e38": { - "balance": "12200000000000000000000" - }, - "f04d2c91efb6e9c45ffbe74b434c8c5f2b028f1f": { - "balance": "1000000000000000000000" - }, - "81164deb10814ae08391f32c08667b6248c27d7a": { - "balance": "394000000000000000000" - }, - "7f5ae05ae0f8cbe5dfe721f044d7a7bef4c27997": { - "balance": "60000000000000000000" - }, - "c982586d63b0d74c201b1af8418372e30c7616be": { - "balance": "100000000000000000000" - }, - "64cf0935bf19d2cebbecd8780d27d2e2b2c34166": { - "balance": "1970000000000000000000" - }, - "cd566ad7b883f01fd3998a9a58a9dee4724ddca5": { - "balance": "58848000000000000000" - }, - "9da609fa3a7e6cf2cc0e70cdabe78dc4e382e11e": { - "balance": "1200000000000000000000" - }, - "0d69100c395ce6c5eaadf95d05d872837ededd21": { - "balance": "400000000000000000000" - }, - "fe91eccf2bd566afa11696c5049fa84c69630a52": { - "balance": "1940000000000000000000" - }, - "005d0ee8155ec0a6ff6808552ca5f16bb5be323a": { - "balance": "197000000000000000000" - }, - "3e5cb8928c417825c03a3bfcc52183e5c91e42d7": { - "balance": "4264790000000000000000" - }, - "9c1b771f09af882af0643083de2aa79dc097c40e": { - "balance": "2480000000000000000000" - }, - "eba388b0da27c87b1cc0eac6c57b2c5a0b459c1a": { - "balance": "6800000000000000000000" - }, - "7529f3797bb6a20f7ea6492419c84c867641d81c": { - "balance": "2000000000000000000000" - }, - "532a7da0a5ad7407468d3be8e07e69c7dd64e861": { - "balance": "500000000000000000000" - }, - "de82cc8d4a1bb1d9434392965b3e80bad3c03d4f": { - "balance": "1477500000000000000000" - }, - "4a82694fa29d9e213202a1a209285df6e745c209": { - "balance": "4000000000000000000000" - }, - "3e53ff2107a8debe3328493a92a586a7e1f49758": { - "balance": "23143470000000000000000" - }, - "b2ddb786d3794e270187d0451ad6c8b79e0e8745": { - "balance": "400000000000000000000" - }, - "6ebcf9957f5fc5e985add475223b04b8c14a7aed": { - "balance": "1730000000000000000000" - }, - "c5c7590b5621ecf8358588de9b6890f2626143f1": { - "balance": "3000000000000000000000" - }, - "ae4f122e35c0b1d1e4069291457c83c07f965fa3": { - "balance": "1000000000000000000000" - }, - "47885ababedf4d928e1c3c71d7ca40d563ed595f": { - "balance": "1820000000000000000000" - }, - "78ce3e3d474a8a047b92c41542242d0a08c70f99": { - "balance": "10000000000000000000000" - }, - "6134d942f037f2cc3d424a230c603d67abd3edf7": { - "balance": "2000000000000000000000" - }, - "1360e87df24c69ee6d51c76e73767ffe19a2131c": { - "balance": "92000000000000000000" - }, - "5fd1c3e31778276cb42ea740f5eae9c641dbc701": { - "balance": "194000000000000000000" - }, - "98397342ec5f3d4cb877e54ef5d6f1d366731bd4": { - "balance": "5910000000000000000000" - }, - "6d4b5c05d06a20957e1748ab6df206f343f92f01": { - "balance": "10020475000000000000000" - }, - "e6115b13f9795f7e956502d5074567dab945ce6b": { - "balance": "100000000000000000000000" - }, - "23730c357a91026e44b1d0e2fc2a51d071d8d77b": { - "balance": "4000000000000000000000" - }, - "fae881937047895a660cf229760f27e66828d643": { - "balance": "182000000000000000000" - }, - "ff3ef6ba151c21b59986ae64f6e8228bc9a2c733": { - "balance": "2000000000000000000000" - }, - "dfbd4232c17c407a980db87ffbcda03630e5c459": { - "balance": "553150000000000000000" - }, - "4429a29fee198450672c0c1d073162250bec6474": { - "balance": "999200000000000000000" - }, - "7e8f96cc29f57b0975120cb593b7dd833d606b53": { - "balance": "197000000000000000000" - }, - "5ed3f1ebe2ae6756b5d8dc19cad02c419aa5778b": { - "balance": "0" - }, - "daa776a6754469d7b9267a89b86725e740da0fa0": { - "balance": "1970000000000000000000" - }, - "139e479764b499d666208c4a8a047a97043163dd": { - "balance": "598880000000000000000" - }, - "5ad5e420755613886f35aa56ac403eebdfe4b0d0": { - "balance": "80000000000000000000000" - }, - "3fe801e61335c5140dc7eda2ef5204460a501230": { - "balance": "2000000000000000000000" - }, - "ce8a6b6d5033b1498b1ffeb41a41550405fa03a2": { - "balance": "4000000000000000000000" - }, - "26c2ffc30efdc5273e76183a16c2698d6e531286": { - "balance": "776000000000000000000" - }, - "71ec3aec3f8f9221f9149fede06903a0f9a232f2": { - "balance": "200000000000000000000" - }, - "ef35f6d4b1075e6aa139151c974b2f4658f70538": { - "balance": "1111111000000000000000" - }, - "26a68eab905a8b3dce00e317308225dab1b9f6b8": { - "balance": "1980000000000000000000" - }, - "63f5b53d79bf2e411489526530223845fac6f601": { - "balance": "30000000000000000000000" - }, - "481115296ab7db52492ff7b647d63329fb5cbc6b": { - "balance": "16100000000000000000000" - }, - "f19f193508393e4d2a127b20b2031f39c82581c6": { - "balance": "3500088000000000000000" - }, - "500e34cde5bd9e2b71bb92d7cf55eee188d5fa0c": { - "balance": "5348000000000000000000" - }, - "65ea67ad3fb56ad5fb94387dd38eb383001d7c68": { - "balance": "100000000000000000000" - }, - "7f9f9b56e4289dfb58e70fd5f12a97b56d35c6a5": { - "balance": "1970000000000000000000" - }, - "60be6f953f2a4d25b6256ffd2423ac1438252e4e": { - "balance": "150000000000000000000" - }, - "ac1dfc984b71a19929a81d81f04a7cbb14073703": { - "balance": "600000000000000000000" - }, - "a3c14ace28b192cbb062145fcbbd5869c67271f6": { - "balance": "8000000000000000000000" - }, - "2da76b7c39b420e388ba2c1020b0856b0270648a": { - "balance": "2000000000000000000000" - }, - "622be4b45495fcd93143efc412d699d6cdc23dc5": { - "balance": "17300000000000000000" - }, - "d3f873bd9956135789ab00ebc195b922e94b259d": { - "balance": "2000000000000000000000" - }, - "975f3764e97bbccf767cbd3b795ba86d8ba9840e": { - "balance": "346000000000000000000" - }, - "fc39be41094b1997d2169e8264c2c3baa6c99bc4": { - "balance": "2000000000000000000000" - }, - "12ffc1128605cb0c13709a7290506f2690977193": { - "balance": "3340000000000000000000" - }, - "9b1168de8ab64b47552f3389800a9cc08b4666cf": { - "balance": "1730000000000000000000" - }, - "9f1aa8fcfc89a1a5328cbd6344b71f278a2ca4a0": { - "balance": "500000000000000000000" - }, - "505a33a18634dd4800693c67f48a1d693d4833f8": { - "balance": "7252000000000000000000" - }, - "d08fc09a0030fd0928cd321198580182a76aae9f": { - "balance": "1000000000000000000000" - }, - "6acddca3cd2b4990e25cd65c24149d0912099e79": { - "balance": "3000037000000000000000" - }, - "397a6ef8763a18f00fac217e055c0d3094101011": { - "balance": "2000000000000000000000" - }, - "4e0bd32473c4c51bf25654def69f797c6b29a232": { - "balance": "1600930000000000000000" - }, - "28d8c35fb7eea622582135e3ad47a227c9a663bd": { - "balance": "18200000000000000000" - }, - "f96488698590dc3b2c555642b871348dfa067ad5": { - "balance": "500000000000000000000" - }, - "4eebe80cb6f3ae5904f6f4b28d907f907189fcab": { - "balance": "1999944000000000000000" - }, - "8d1abd897dacd4312e18080c88fb9647eab44052": { - "balance": "216000000000000000000" - }, - "457029c469c4548d168cec3e65872e4428d42b67": { - "balance": "2000000000000000000000" - }, - "1296acded1e063af39fe8ba0b4b63df789f70517": { - "balance": "100014000000000000000" - }, - "71762c63678c18d1c6378ce068e666381315147e": { - "balance": "2000000000000000000000" - }, - "6cc1c878fa6cde8a9a0b8311247e741e4642fe6d": { - "balance": "985000000000000000000" - }, - "8d9ed7f4553058c26f7836a3802d3064eb1b363d": { - "balance": "90000000000000000000" - }, - "5032e4bcf7932b49fdba377b6f1499636513cfc3": { - "balance": "100000000000000000000" - }, - "462b678b51b584f3ed7ada070b5cd99c0bf7b87f": { - "balance": "100000000000000000000" - }, - "c8aa49e3809f0899f28ab57e6743709d58419033": { - "balance": "880000000000000000000" - }, - "01b1cae91a3b9559afb33cdc6d689442fdbfe037": { - "balance": "200000000000000000000" - }, - "b1043004ec1941a8cf4f2b00b15700ddac6ff17e": { - "balance": "1000000000000000000000" - }, - "5ba2c6c35dfaec296826591904d544464aeabd5e": { - "balance": "20000000000000000000" - }, - "b32400fd13c5500917cb037b29fe22e7d5228f2d": { - "balance": "40000000000000000000000" - }, - "d59d92d2c8701980cc073c375d720af064743c0c": { - "balance": "19000000000000000000000" - }, - "11dd6185d9a8d73ddfdaa71e9b7774431c4dfec2": { - "balance": "1000000000000000000000" - }, - "d4cb21e590c5a0e06801366aff342c7d7db16424": { - "balance": "494000000000000000000" - }, - "5b6d55f6712967405c659129f4b1de09acf2cb7b": { - "balance": "267400000000000000000" - }, - "6179979907fe7f037e4c38029d60bcbab832b3d6": { - "balance": "1610000000000000000000" - }, - "33c407133b84b3ca4c3ded1f4658900c38101624": { - "balance": "2800000000000000000000" - }, - "cd2a36d753e9e0ed012a584d716807587b41d56a": { - "balance": "261400000000000000000" - }, - "8155fa6c51eb31d808412d748aa086105018122f": { - "balance": "1880000000000000000000" - }, - "3ecc8e1668dde995dc570fe414f44211c534a615": { - "balance": "2000000000000000000000" - }, - "d6395db5a4bb66e60f4cfbcdf0057bb4d97862e2": { - "balance": "910000000000000000000" - }, - "b6fb39786250081426a342c70d47ee521e5bc563": { - "balance": "15000000000000000000000" - }, - "510eda5601499a0d5e1a006bfffd833672f2e267": { - "balance": "2000000000000000000000" - }, - "98c19dba810ba611e68f2f83ee16f6e7744f0c1f": { - "balance": "200000000000000000000" - }, - "34ff26eb60a8d1a95a489fae136ee91d4e58084c": { - "balance": "600000000000000000000" - }, - "6ad90be252d9cd464d998125fab693060ba8e429": { - "balance": "4000000000000000000000" - }, - "038323b184cff7a82ae2e1bda7793fe4319ca0bf": { - "balance": "20000000000000000000000" - }, - "dc5305b4020a06b49d657c7ca34c35c91c5f2c56": { - "balance": "7045990000000000000000" - }, - "c9c80dc12e7bab86e949d01e4c3ed35f2b9bba5f": { - "balance": "2000000000000000000000" - }, - "7beb81fb2f5e91526b2ac9795e76c69bcff04bc0": { - "balance": "69400000000000000000000" - }, - "b8bc9bca7f71b4ed12e620438d620f53c114342f": { - "balance": "500000000000000000000" - }, - "d288e7cb7ba9f620ab0f7452e508633d1c5aa276": { - "balance": "4000000000000000000000" - }, - "a2e460a989cb15565f9ecca7d121a18e4eb405b6": { - "balance": "2000000000000000000000" - }, - "7489cc8abe75cda4ef0d01cef2605e47eda67ab1": { - "balance": "133700000000000000000" - }, - "38b403fb1fb7c14559a2d6f6564a5552bca39aff": { - "balance": "2000000000000000000000" - }, - "e55c80520a1b0f755b9a2cd3ce214f7625653e8a": { - "balance": "2000000000000000000000" - }, - "451b7070259bdba27100e36e23428a53dfe304e9": { - "balance": "13370000000000000000" - }, - "8b5c914b128bf1695c088923fa467e7911f351fa": { - "balance": "98500000000000000000" - }, - "17df49518d73b129f0da36b1c9b40cb66420fdc7": { - "balance": "10000000000000000000000" - }, - "c1950543554d8a713003f662bb612c10ad4cdf21": { - "balance": "18200000000000000000" - }, - "fa7606435b356cee257bd2fcd3d9eacb3cd1c4e1": { - "balance": "100000000000000000000" - }, - "e0bad98eee9698dbf6d76085b7923de5754e906d": { - "balance": "167000000000000000000" - }, - "ce53c8cdd74296aca987b2bc19c2b875a48749d0": { - "balance": "3000000000000000000000" - }, - "d0c55abf976fdc3db2afe9be99d499484d576c02": { - "balance": "1000000000000000000000" - }, - "238a6b7635252f5244486c0af0a73a207385e039": { - "balance": "1370000000000000000000" - }, - "ceb389381d48a8ae4ffc483ad0bb5e204cfdb1ec": { - "balance": "740745000000000000000" - }, - "3847667038f33b01c1cc795d8daf5475eff5a0d4": { - "balance": "728330000000000000000" - }, - "a08d215b5b6aac4861a281ac7e400b78fef04cbf": { - "balance": "20000000000000000000" - }, - "2d0dec51a6e87330a6a8fa2a0f65d88d4abcdf73": { - "balance": "185000000000000000000" - }, - "9e8f64ddcde9b8b451bafaa235a9bf511a25ac91": { - "balance": "2674000000000000000000" - }, - "ddac6bf4bbdd7d597d9c686d0695593bedccc7fa": { - "balance": "865000000000000000000" - }, - "22e15158b5ee3e86eb0332e3e6a9ac6cd9b55ecd": { - "balance": "160000000000000000000" - }, - "3aea4e82d2400248f99871a41ca257060d3a221b": { - "balance": "1000000000000000000000" - }, - "fb126f0ec769f49dcefca2f200286451583084b8": { - "balance": "5013750000000000000000" - }, - "1b8bd6d2eca20185a78e7d98e8e185678dac4830": { - "balance": "16700000000000000000000" - }, - "664cd67dccc9ac8228b45c55db8d76550b659cdc": { - "balance": "394000000000000000000" - }, - "553f37d92466550e9fd775ae74362df030179132": { - "balance": "2000000000000000000000" - }, - "730d8763c6a4fd824ab8b859161ef7e3a96a1200": { - "balance": "20000000000000000000000" - }, - "04c2c64bb54c3eccd05585e10ec6f99a0cdb01a3": { - "balance": "100000000000000000000" - }, - "f1624d980b65336feac5a6d54125005cfcf2aacb": { - "balance": "2000000000000000000000" - }, - "0b7fc9ddf70576f6330669eaaa71b6a831e99528": { - "balance": "140000000000000000000" - }, - "fa2bbca15d3fe39f8a328e91f90da14f7ac6253d": { - "balance": "200000000000000000000" - }, - "07feef54c136850829badc4b49c3f2a73c89fb9e": { - "balance": "118200000000000000000" - }, - "3703350c4d6fe337342cddc65bf1e2386bf3f9b2": { - "balance": "2020000000000000000000" - }, - "6d7d1c949511f88303808c60c5ea0640fcc02683": { - "balance": "10000000000000000000000" - }, - "34fa7792bad8bbd7ff64056214a33eb6600c1ea8": { - "balance": "50000000000000000000" - }, - "994cc2b5227ec3cf048512467c41b7b7b748909f": { - "balance": "2000000000000000000000" - }, - "08da3a7a0f452161cfbcec311bb68ebfdee17e88": { - "balance": "2000000000000000000000" - }, - "bbb4ee1d82f2e156442cc93338a2fc286fa28864": { - "balance": "1370000000000000000000" - }, - "7a2dfc770e24368131b7847795f203f3d50d5b56": { - "balance": "11400000000000000000000" - }, - "7cef4d43aa417f9ef8b787f8b99d53f1fea1ee88": { - "balance": "1910000000000000000000" - }, - "c6a30ef5bb3320f40dc5e981230d52ae3ac19322": { - "balance": "182000000000000000000" - }, - "6a74844d8e9cb5581c45079a2e94462a6cee8821": { - "balance": "1082970000000000000000" - }, - "c3110be01dc9734cfc6e1ce07f87d77d1345b7e1": { - "balance": "4999998000000000000000" - }, - "aeb916ebf49d0f86c13f7331cef19e129937512d": { - "balance": "599908000000000000000" - }, - "3e5abd09ce5af7ba8487c359e0f2a93a986b0b18": { - "balance": "10000000000000000000000" - }, - "cdd60d73efaad873c9bbfb178ca1b7105a81a681": { - "balance": "32000000000000000000" - }, - "31eb123c95c82bf685ace7a75a1881a289efca10": { - "balance": "920034000000000000000" - }, - "86e8670e27598ea09c3899ab7711d3b9fe901c17": { - "balance": "200000000000000000000" - }, - "a144f6b60f72d64a21e330dadb62d8990ade2b09": { - "balance": "1000000000000000000000" - }, - "68883e152e5660fee59626e7e3b4f05110e6222f": { - "balance": "54683300000000000000000" - }, - "fe4249127950e2f896ec0e7e2e3d055aab10550f": { - "balance": "668500000000000000000" - }, - "403d53cf620f0922b417848dee96c190b5bc8271": { - "balance": "9850000000000000000000" - }, - "bec2e6de39c07c2bae556acfbee2c4728b9982e3": { - "balance": "573000000000000000000" - }, - "f3c4716d1ee5279a86d0163a14618181e16136c7": { - "balance": "1000000000000000000000" - }, - "e38ef28a5ed984a7db24a1ae782dfb87f397dfc6": { - "balance": "143000000000000000000" - }, - "30fbe5885f9fcce9ea5edb82ed4a1196dd259aed": { - "balance": "5200000000000000000000" - }, - "48bf14d7b1fc84ebf3c96be12f7bce01aa69b03e": { - "balance": "120000000000000000000" - }, - "b8d5c324a8209d7c8049d0d4aede02ba80ab578b": { - "balance": "16889329000000000000000" - }, - "43d5a71ce8b8f8ae02b2eaf8eaf2ca2840b93fb6": { - "balance": "6000000000000000000000" - }, - "f9a59c3cc5ffacbcb67be0fc7256f64c9b127cb4": { - "balance": "2000000000000000000000" - }, - "0e21af1b8dbf27fcf63f37e047b87a825cbe7c27": { - "balance": "3000000000000000000000" - }, - "1c35aab688a0cd8ef82e76541ba7ac39527f743b": { - "balance": "500000000000000000000" - }, - "91ac5cfe67c54aa7ebfba448666c461a3b1fe2e1": { - "balance": "401880000000000000000" - }, - "4ba53ab549e2016dfa223c9ed5a38fad91288d07": { - "balance": "1400000000000000000000" - }, - "99a4de19ded79008cfdcd45d014d2e584b8914a8": { - "balance": "1500000000000000000000" - }, - "4adbf4aae0e3ef44f7dd4d8985cfaf096ec48e98": { - "balance": "150000000000000000000" - }, - "9a633fcd112cceeb765fe0418170732a9705e79c": { - "balance": "18200000000000000000" - }, - "292f228b0a94748c8eec612d246f989363e08f08": { - "balance": "185000000000000000000" - }, - "9f3497f5ef5fe63095836c004eb9ce02e9013b4b": { - "balance": "633424000000000000000" - }, - "0e6dfd553b2e873d2aec15bd5fbb3f8472d8d394": { - "balance": "12000000000000000000000" - }, - "74ebf4425646e6cf81b109ce7bf4a2a63d84815f": { - "balance": "40000000000000000000" - }, - "8ce5e3b5f591d5eca38abf228f2e3c35134bdac0": { - "balance": "2319920000000000000000" - }, - "90c41eba008e20cbe927f346603fc88698125969": { - "balance": "42000000000000000000" - }, - "382ba76db41b75606dd48a48f0137e9174e031b6": { - "balance": "20000000000000000000" - }, - "5d24bdbc1c47f0eb83d128cae48ac33c4817e91f": { - "balance": "1000000000000000000000" - }, - "a64e5ffb704c2c9139d77ef61d8cdfa31d7a88e9": { - "balance": "143000000000000000000" - }, - "a18360e985f2062e8f8efe02ad2cbc91ad9a5aad": { - "balance": "3000000000000000000000" - }, - "d251f903ae18727259eee841a189a1f569a5fd76": { - "balance": "10000000000000000000000" - }, - "efa6b1f0db603537826891b8b4bc163984bb40cd": { - "balance": "985000000000000000000" - }, - "47fff42c678551d141eb75a6ee398117df3e4a8d": { - "balance": "100010000000000000000" - }, - "f2294adbb6f0dcc76e632ebef48ab49f124dbba4": { - "balance": "1443690000000000000000" - }, - "53700d53254d430f22781a4a76a463933b5d6b08": { - "balance": "1970000000000000000000" - }, - "b14a7aaa8f49f2fb9a8102d6bbe4c48ae7c06fb2": { - "balance": "8000000000000000000000" - }, - "9ed4e63f526542d44fddd34d59cd25388ffd6bda": { - "balance": "3885000000000000000000" - }, - "4cac91fb83a147d2f76c3267984b910a79933348": { - "balance": "2167000000000000000000" - }, - "9b32cf4f5115f4b34a00a64c617de06387354323": { - "balance": "105501000000000000000" - }, - "b8bedd576a4b4c2027da735a5bc3f533252a1808": { - "balance": "2000000000000000000000" - }, - "c5a3b98e4593fea0b38c4f455a5065f051a2f815": { - "balance": "20309030000000000000000" - }, - "eaf52388546ec35aca6f6c6393d8d609de3a4bf3": { - "balance": "20000000000000000000" - }, - "4c423c76930d07f93c47a5cc4f615745c45a9d72": { - "balance": "100000000000000000000" - }, - "9052f2e4a3e3c12dd1c71bf78a4ec3043dc88b7e": { - "balance": "267400000000000000000" - }, - "2bade91d154517620fd4b439ac97157a4102a9f7": { - "balance": "4000000000000000000000" - }, - "da698d64c65c7f2b2c7253059cd3d181d899b6b7": { - "balance": "295500000000000000000" - }, - "c6d8954e8f3fc533d2d230ff025cb4dce14f3426": { - "balance": "400000000000000000000" - }, - "349a816b17ab3d27bbc0ae0051f6a070be1ff29d": { - "balance": "10000000000000000000000" - }, - "ff4d9c8484c43c42ff2c5ab759996498d323994d": { - "balance": "4000000000000000000000" - }, - "22944fbca9b57963084eb84df7c85fb9bcdfb856": { - "balance": "4649845000000000000000" - }, - "bfd93c90c29c07bc5fb5fc49aeea55a40e134f35": { - "balance": "28000000000000000000000" - }, - "3caedb5319fe806543c56e5021d372f71be9062e": { - "balance": "40000000000000000000000" - }, - "9a079c92a629ca15c8cafa2eb28d5bc17af82811": { - "balance": "500000000000000000000" - }, - "7d2a52a7cf0c8436a8e007976b6c26b7229d1e15": { - "balance": "438040000000000000000" - }, - "cf89f7460ba3dfe83c5a1d3a019ee1250f242f0f": { - "balance": "985177000000000000000" - }, - "577bfe64e3a1e3800e94db1c6c184d8dc8aafc66": { - "balance": "1498000000000000000000" - }, - "7ffd02ed370c7060b2ae53c078c8012190dfbb75": { - "balance": "10000000000000000000000" - }, - "90b62f131a5f29b45571513ee7a74a8f0b232202": { - "balance": "158000000000000000000" - }, - "6e8212b722afd408a7a73ed3e2395ee6454a0330": { - "balance": "159000000000000000000" - }, - "515f30bc90cdf4577ee47d65d785fbe2e837c6bc": { - "balance": "10166128000000000000000" - }, - "c27376f45d21e15ede3b26f2655fcee02ccc0f2a": { - "balance": "20000000000000000000" - }, - "3da39ce3ef4a7a3966b32ee7ea4ebc2335a8f11f": { - "balance": "2000000000000000000000" - }, - "25259d975a21d83ae30e33f800f53f37dfa01938": { - "balance": "20000000000000000000" - }, - "8ed143701f2f72280fd04a7b4164281979ea87c9": { - "balance": "14000000000000000000" - }, - "5ac99ad7816ae9020ff8adf79fa9869b7cea6601": { - "balance": "21000000000000000000000" - }, - "f51fded80acb502890e87369741f3722514cefff": { - "balance": "20000042000000000000000" - }, - "f657fcbe682eb4e8db152ecf892456000b513d15": { - "balance": "1940000000000000000000" - }, - "62c37c52b97f4b040b1aa391d6dec152893c4707": { - "balance": "1000000000000000000000" - }, - "89fc8e4d386b0d0bb4a707edf3bd560df1ad8f4e": { - "balance": "2955000000000000000000" - }, - "53c0bb7fc88ea422d2ef7e540e2d8f28b1bb8183": { - "balance": "20000000000000000000" - }, - "56f493a3d108aaa2d18d98922f8efe1662cfb73d": { - "balance": "2020000000000000000000" - }, - "e9458f68bb272cb5673a04f781b403556fd3a387": { - "balance": "61000000000000000000" - }, - "be525a33ea916177f17283fca29e8b350b7f530b": { - "balance": "2638000000000000000000" - }, - "4feb846be43041fd6b34202897943e3f21cb7f04": { - "balance": "83226000000000000000" - }, - "15aa530dc36958b4edb38eee6dd9e3c77d4c9145": { - "balance": "2000000000000000000000" - }, - "2458d6555ff98a129cce4037953d00206eff4287": { - "balance": "197000000000000000000" - }, - "8035fe4e6b6af27ae492a578515e9d39fa6fa65b": { - "balance": "4000000000000000000000" - }, - "296b71c0015819c242a7861e6ff7eded8a5f71e3": { - "balance": "1999800000000000000000" - }, - "8f1952eed1c548d9ee9b97d0169a07933be69f63": { - "balance": "1000000000000000000000" - }, - "a421dbb89b3a07419084ad10c3c15dfe9b32d0c2": { - "balance": "20000000000000000000000" - }, - "554336ee4ea155f9f24f87bca9ca72e253e12cd2": { - "balance": "100000000000000000000" - }, - "ffc5fc4b7e8a0293ff39a3a0f7d60d2646d37a74": { - "balance": "2000000000000000000000" - }, - "ea2c197d26e98b0da83e1b72c787618c979d3db0": { - "balance": "19700000000000000000" - }, - "96aa573fed2f233410dbae5180145b23c31a02f0": { - "balance": "1730000000000000000000" - }, - "c23b2f921ce4a37a259ee4ad8b2158d15d664f59": { - "balance": "25403000000000000000" - }, - "d874b9dfae456a929ba3b1a27e572c9b2cecdfb3": { - "balance": "170000000000000000000" - }, - "bf8b8005d636a49664f74275ef42438acd65ac91": { - "balance": "200000000000000000000" - }, - "441a52001661fac718b2d7b351b7c6fb521a7afd": { - "balance": "400000000000000000000" - }, - "812a55c43caedc597218379000ce510d548836fd": { - "balance": "18200000000000000000" - }, - "5e90c85877198756b0366c0e17b28e52b446505a": { - "balance": "374288000000000000000" - }, - "da3017c150dd0dce7fcf881b0a48d0d1c756c4c7": { - "balance": "100014000000000000000" - }, - "6baf7a2a02ae78801e8904ad7ac05108fc56cff6": { - "balance": "1000000000000000000000" - }, - "177dae78bc0113d8d39c4402f2a641ae2a105ab8": { - "balance": "1818320000000000000000" - }, - "01b5b5bc5a117fa08b34ed1db9440608597ac548": { - "balance": "200000000000000000000" - }, - "aae732eda65988c3a00c7f472f351c463b1c968e": { - "balance": "2000000000000000000000" - }, - "d95342953c8a21e8b635eefac7819bea30f17047": { - "balance": "94160000000000000000000" - }, - "8d616b1eee77eef6f176e0698db3c0c141b2fc8f": { - "balance": "500000000000000000000" - }, - "12d20790b7d3dbd88c81a279b812039e8a603bd0": { - "balance": "1604400000000000000000" - }, - "3734cb187491ede713ae5b3b2d12284af46b8101": { - "balance": "3000000000000000000000" - }, - "dd967c4c5f8ae47e266fb416aad1964ee3e7e8c3": { - "balance": "7750000000000000000000" - }, - "3dcef19c868b15d34eda426ec7e04b18b6017002": { - "balance": "1999800000000000000000" - }, - "ce9d21c692cd3c01f2011f505f870036fa8f6cd2": { - "balance": "400000000000000000000" - }, - "d44f6ac3923b5fd731a4c45944ec4f7ec52a6ae4": { - "balance": "10000000000000000000000" - }, - "b424d68d9d0d00cec1938c854e15ffb880ba0170": { - "balance": "200000000000000000000" - }, - "1f2186ded23e0cf9521694e4e164593e690a9685": { - "balance": "300000000000000000000" - }, - "7f4b5e278578c046cceaf65730a0e068329ed5b6": { - "balance": "1880000000000000000000" - }, - "8c50aa2a9212bcde56418ae261f0b35e7a9dbb82": { - "balance": "400000000000000000000" - }, - "1953313e2ad746239cb2270f48af34d8bb9c4465": { - "balance": "2000000000000000000000" - }, - "a15025f595acdbf3110f77c5bf24477e6548f9e8": { - "balance": "2000000000000000000000" - }, - "53af32c22fef99803f178cf90b802fb571c61cb9": { - "balance": "3880000000000000000000" - }, - "d0a8abd80a199b54b08b65f01d209c27fef0115b": { - "balance": "6525979000000000000000" - }, - "2b68306ba7f8daaf73f4c644ef7d2743c0f26856": { - "balance": "864800000000000000000" - }, - "96924191b7df655b3319dc6d6137f481a73a0ff3": { - "balance": "4020000000000000000000" - }, - "6fa72015fa78696efd9a86174f7f1f21019286b1": { - "balance": "1337000000000000000000" - }, - "0b119df99c6b8de58a1e2c3f297a6744bf552277": { - "balance": "2000000000000000000000" - }, - "61733947fab820dbd351efd67855ea0e881373a0": { - "balance": "20000000000000000000" - }, - "8ae6f80b70e1f23c91fbd5a966b0e499d95df832": { - "balance": "197000000000000000000" - }, - "01a7d9fa7d0eb1185c67e54da83c2e75db69e39f": { - "balance": "7623900000000000000000" - }, - "9932ef1c85b75a9b2a80057d508734c51085becc": { - "balance": "50170000000000000000" - }, - "aefcfe88c826ccf131d54eb4ea9eb80e61e1ee25": { - "balance": "340000000000000000000" - }, - "c21fa6643a1f14c02996ad7144b75926e87ecb4b": { - "balance": "20000000000000000000000" - }, - "97d9e46a7604d7b5a4ea4ee61a42b3d2350fc3ed": { - "balance": "2000000000000000000000" - }, - "3cafaf5e62505615068af8eb22a13ad8a9e55070": { - "balance": "1999600000000000000000" - }, - "22f2dcff5ad78c3eb6850b5cb951127b659522e6": { - "balance": "13700000000000000000" - }, - "aaad1baade5af04e2b17439e935987bf8c2bb4b9": { - "balance": "2000000000000000000000" - }, - "298887bab57c5ba4f0615229d7525fa113b7ea89": { - "balance": "40000000000000000000" - }, - "7539333046deb1ef3c4daf50619993f444e1de68": { - "balance": "1182000000000000000000" - }, - "9752d14f5e1093f071711c1adbc4e3eb1e5c57f3": { - "balance": "2000000000000000000000" - }, - "ed641e06368fb0efaa1703e01fe48f4a685309eb": { - "balance": "200000000000000000000" - }, - "d0ee4d02cf24382c3090d3e99560de3678735cdf": { - "balance": "2400000000000000000000" - }, - "47e25df8822538a8596b28c637896b4d143c351d": { - "balance": "80500000000000000000000" - }, - "559706c332d20779c45f8a6d046a699159b74921": { - "balance": "380123000000000000000" - }, - "3a4da78dce05aeb87de9aead9185726da1926798": { - "balance": "200000000000000000000" - }, - "3041445a33ba158741160d9c344eb88e5c306f94": { - "balance": "60000000000000000000" - }, - "08d4311c9c1bbaf87fabe1a1d01463828d5d98ce": { - "balance": "90000000000000000000000" - }, - "6bd3e59f239fafe4776bb9bddd6bee83ba5d9d9f": { - "balance": "1000000000000000000000" - }, - "29eaae82761762f4d2db53a9c68b0f6b0b6d4e66": { - "balance": "2000000000000000000000" - }, - "0b7d339371e5be6727e6e331b5821fa24bdb9d5a": { - "balance": "857738000000000000000" - }, - "4714cfa4f46bd6bd70737d75878197e08f88e631": { - "balance": "11792000000000000000000" - }, - "ad92ca066edb7c711dfc5b166192d1edf8e77185": { - "balance": "36000000000000000000000" - }, - "f97b56ebd5b77abc9fbacbabd494b9d2c221cd03": { - "balance": "1970000000000000000000" - }, - "591bef3171d1c5957717a4e98d17eb142c214e56": { - "balance": "20000000000000000000000" - }, - "899b3c249f0c4b81df75d212004d3d6d952fd223": { - "balance": "2000000000000000000000" - }, - "a819d2ece122e028c8e8a04a064d02b9029b08b9": { - "balance": "1000000000000000000000" - }, - "e341642d40d2afce2e9107c67079ac7a2660086c": { - "balance": "400000000000000000000" - }, - "0329188f080657ab3a2afa522467178279832085": { - "balance": "216700000000000000000" - }, - "03317826d1f70aa4bddfa09be0c4105552d2358b": { - "balance": "38800000000000000000" - }, - "3ac9dc7a436ae98fd01c7a9621aa8e9d0b8b531d": { - "balance": "1790000000000000000000" - }, - "93c88e2d88621e30f58a9586bed4098999eb67dd": { - "balance": "31200000000000000000000" - }, - "cd1e66ed539dd92fc40bbaa1fa16de8c02c14d45": { - "balance": "230000000000000000000" - }, - "e6c81ffcecb47ecdc55c0b71e4855f3e5e97fc1e": { - "balance": "334250000000000000000" - }, - "50f8fa4bb9e2677c990a4ee8ce70dd1523251e4f": { - "balance": "26030000000000000000" - }, - "4f64a85e8e9a40498c0c75fceb0337fb49083e5e": { - "balance": "1000000000000000000000" - }, - "4b29437c97b4a844be71cca3b648d4ca0fdd9ba4": { - "balance": "150200000000000000000" - }, - "1eee6cbee4fe96ad615a9cf5857a647940df8c78": { - "balance": "19400000000000000000" - }, - "29f0edc60338e7112085a1d114da8c42ce8f55d6": { - "balance": "2958000000000000000000" - }, - "23b1c4917fbd93ee3d48389306957384a5496cbf": { - "balance": "4000086000000000000000" - }, - "1767525c5f5a22ed80e9d4d7710f0362d29efa33": { - "balance": "400000000000000000000" - }, - "3064899a963c4779cbf613cd6980846af1e6ec65": { - "balance": "6999908000000000000000" - }, - "68531f4dda808f5320767a03113428ca0ce2f389": { - "balance": "19400000000000000000" - }, - "1db9ac9a9eaeec0a523757050c71f47278c72d50": { - "balance": "1337000000000000000000" - }, - "7592c69d067b51b6cc639d1164d5578c60d2d244": { - "balance": "20000000000000000000" - }, - "cf3fbfa1fd32d7a6e0e6f8ef4eab57be34025c4c": { - "balance": "1063120000000000000000" - }, - "8efec058cc546157766a632775404a334aaada87": { - "balance": "1999000000000000000000" - }, - "faf5f0b7b6d558f5090d9ea1fb2d42259c586078": { - "balance": "6401000000000000000000" - }, - "19ecf2abf40c9e857b252fe1dbfd3d4c5d8f816e": { - "balance": "2000000000000000000000" - }, - "6e8a26689f7a2fdefd009cbaaa5310253450daba": { - "balance": "2049982000000000000000" - }, - "e2f40d358f5e3fe7463ec70480bd2ed398a7063b": { - "balance": "20000000000000000000" - }, - "fa19d6f7a50f4f079893d167bf14e21d0073d196": { - "balance": "530000000000000000000" - }, - "3e2ca0d234baf607ad466a1b85f4a6488ef00ae7": { - "balance": "89505000000000000000" - }, - "f8a49ca2390c1f6d5c0e62513b079571743f7cc6": { - "balance": "3000000000000000000000" - }, - "5d3f3b1f7130b0bb21a0fd32396239179a25657f": { - "balance": "62474000000000000000000" - }, - "f332c0f3e05a27d9126fd0b641a8c2d4060608fd": { - "balance": "5001041000000000000000" - }, - "e304a32f05a83762744a9542976ff9b723fa31ea": { - "balance": "1576256000000000000000" - }, - "f768f321fd6433d96b4f354d3cc1652c1732f57f": { - "balance": "10000000000000000000000" - }, - "147af46ae9ccd18bb35ca01b353b51990e49dce1": { - "balance": "4000000000000000000000" - }, - "21eae6feffa9fbf4cd874f4739ace530ccbe5937": { - "balance": "5000000000000000000000" - }, - "6994fb3231d7e41d491a9d68d1fa4cae2cc15960": { - "balance": "4000000000000000000000" - }, - "51126446ab3d8032557e8eba65597d75fadc815c": { - "balance": "322000000000000000000" - }, - "24daaaddf7b06bbcea9b80590085a88567682b4e": { - "balance": "319008000000000000000" - }, - "cd020f8edfcf524798a9b73a640334bbf72f80a5": { - "balance": "133700000000000000000" - }, - "56febf9e1003af15b1bd4907ec089a4a1b91d268": { - "balance": "200000000000000000000" - }, - "3c79c863c3d372b3ff0c6f452734a7f97042d706": { - "balance": "176000000000000000000" - }, - "e1203eb3a723e99c2220117ca6afeb66fa424f61": { - "balance": "9461996000000000000000" - }, - "18fb09188f27f1038e654031924f628a2106703d": { - "balance": "2000000000000000000000" - }, - "2eba0c6ee5a1145c1c573984963a605d880a7a20": { - "balance": "500000000000000000000" - }, - "4cefbe2398e47d52e78db4334c8b697675f193ae": { - "balance": "4011000000000000000000" - }, - "c02471e3fc2ea0532615a7571d493289c13c36ef": { - "balance": "20000000000000000000" - }, - "ba469aa5c386b19295d4a1b5473b540353390c85": { - "balance": "2000000000000000000000" - }, - "7b11673cc019626b290cbdce26046f7e6d141e21": { - "balance": "500000000000000000000" - }, - "26784ade91c8a83a8e39658c8d8277413ccc9954": { - "balance": "6000000000000000000000" - }, - "57d3df804f2beee6ef53ab94cb3ee9cf524a18d3": { - "balance": "393606000000000000000" - }, - "ccae0d3d852a7da3860f0636154c0a6ca31628d4": { - "balance": "106560000000000000000" - }, - "bfe3a1fc6e24c8f7b3250560991f93cba2cf8047": { - "balance": "80000000000000000000000" - }, - "724ce858857ec5481c86bd906e83a04882e5821d": { - "balance": "3000000000000000000000" - }, - "fb37cf6b4f81a9e222fba22e9bd24b5098b733cf": { - "balance": "38800000000000000000" - }, - "9b22a80d5c7b3374a05b446081f97d0a34079e7f": { - "balance": "3000000000000000000000" - }, - "0a29a8a4d5fd950075ffb34d77afeb2d823bd689": { - "balance": "200000000000000000000" - }, - "d01af9134faf5257174e8b79186f42ee354e642d": { - "balance": "1000000000000000000000" - }, - "7f1619988f3715e94ff1d253262dc5581db3de1c": { - "balance": "900000000000000000000" - }, - "6f137a71a6f197df2cbbf010dcbd3c444ef5c925": { - "balance": "2000000000000000000000" - }, - "11efb8a20451161b644a8ccebbc1d343a3bbcb52": { - "balance": "3200000000000000000000" - }, - "46504e6a215ac83bccf956befc82ab5a679371c8": { - "balance": "518898000000000000000" - }, - "b523fff9749871b35388438837f7e6e0dea9cb6b": { - "balance": "2000000000000000000000" - }, - "c5c6a4998a33feb764437a8be929a73ba34a0764": { - "balance": "50000000000000000000000" - }, - "3cd7f7c7c2353780cde081eeec45822b25f2860c": { - "balance": "200000000000000000000" - }, - "b3050beff9de33c80e1fa15225e28f2c413ae313": { - "balance": "700000000000000000000" - }, - "59268171b833e0aa13c54b52ccc0422e4fa03aeb": { - "balance": "3000000000000000000000" - }, - "7169724ee72271c534cad6420fb04ee644cb86fe": { - "balance": "410164000000000000000" - }, - "6e6d5bbbb9053b89d744a27316c2a7b8c09b547d": { - "balance": "909831000000000000000" - }, - "3f3f46b75cabe37bfacc8760281f4341ca7f463d": { - "balance": "602709000000000000000" - }, - "7a33834e8583733e2d52aead589bd1affb1dd256": { - "balance": "1000000000000000000000" - }, - "e94ded99dcb572b9bb1dcba32f6dee91e057984e": { - "balance": "394000000000000000000" - }, - "19336a236ded755872411f2e0491d83e3e00159e": { - "balance": "940000000000000000000" - }, - "63ac545c991243fa18aec41d4f6f598e555015dc": { - "balance": "600000000000000000000" - }, - "cfee05c69d1f29e7714684c88de5a16098e91399": { - "balance": "1970000000000000000000" - }, - "77be6b64d7c733a436adec5e14bf9ad7402b1b46": { - "balance": "1000000000000000000000" - }, - "233bdddd5da94852f4ade8d212885682d9076bc6": { - "balance": "4000000000000000000000" - }, - "952c57d2fb195107d4cd5ca300774119dfad2f78": { - "balance": "2000000000000000000000" - }, - "e237baa4dbc9926e32a3d85d1264402d54db012f": { - "balance": "2000000000000000000000" - }, - "aa91237e740d25a92f7fa146faa18ce56dc6e1f3": { - "balance": "925000000000000000000" - }, - "2339e9492870afea2537f389ac2f838302a33c06": { - "balance": "2000000000000000000000" - }, - "1d45586eb803ca2190650bf748a2b174312bb507": { - "balance": "1400000000000000000000" - }, - "c61446b754c24e3b1642d9e51765b4d3e46b34b6": { - "balance": "2000000000000000000000" - }, - "ac28b5edea05b76f8c5f97084541277c96696a4c": { - "balance": "1000000000000000000000" - }, - "1a1c9a26e0e02418a5cf687da75a275c622c9440": { - "balance": "5000000000000000000000" - }, - "299368609042a858d1ecdf1fc0ada5eaceca29cf": { - "balance": "2000000000000000000000" - }, - "095f5a51d06f6340d80b6d29ea2e88118ad730fe": { - "balance": "2000200000000000000000" - }, - "751a2ca34e7187c163d28e3618db28b13c196d26": { - "balance": "500000000000000000000" - }, - "75b0e9c942a4f0f6f86d3f95ff998022fa67963b": { - "balance": "1490000000000000000000" - }, - "d1b37f03cb107424e9c4dd575ccd4f4cee57e6cd": { - "balance": "2000000000000000000000" - }, - "7f993ddb7e02c282b898f6155f680ef5b9aff907": { - "balance": "20000000000000000000000" - }, - "a3d583a7b65b23f60b7905f3e4aa62aac87f4227": { - "balance": "1046779000000000000000" - }, - "526bb533b76e20c8ee1ebf123f1e9ff4148e40be": { - "balance": "197000000000000000000" - }, - "2160b4c02cac0a81de9108de434590a8bfe68735": { - "balance": "1970000000000000000000" - }, - "010007394b8b7565a1658af88ce463499135d6b7": { - "balance": "100000000000000000000" - }, - "64457fa33b0832506c4f7d1180dce48f46f3e0ff": { - "balance": "2000000000000000000000" - }, - "b51e558eb5512fbcfa81f8d0bd938c79ebb5242b": { - "balance": "715000000000000000000" - }, - "94f13f9f0836a3ee2437a84922d2984dc0f7d53b": { - "balance": "2999916000000000000000" - }, - "6bd457ade051795df3f2465c3839aed3c5dee978": { - "balance": "999925000000000000000" - }, - "f3dbcf135acb9dee1a489c593c024f03c2bbaece": { - "balance": "2000000000000000000000" - }, - "61b902c5a673885826820d1fe14549e4865fbdc2": { - "balance": "334703000000000000000" - }, - "2acc9c1a32240b4d5b2f777a2ea052b42fc1271c": { - "balance": "41764000000000000000000" - }, - "6ddfef639155daab0a5cb4953aa8c5afaa880453": { - "balance": "1820000000000000000000" - }, - "96ff6f509968f36cb42cba48db32f21f5676abf8": { - "balance": "1970000000000000000000" - }, - "b4c8170f7b2ab536d1d9a25bdd203ae1288dc3d5": { - "balance": "200000000000000000000" - }, - "78d4f8c71c1e68a69a98f52fcb45da8af56ea1a0": { - "balance": "2000000000000000000000" - }, - "dec99e972fca7177508c8e1a47ac22d768acab7c": { - "balance": "2000000000000000000000" - }, - "a07aa16d74aee8a9a3288d52db1551d593883297": { - "balance": "600000000000000000000" - }, - "cf1169041c1745e45b172435a2fc99b49ace2b00": { - "balance": "31960000000000000000" - }, - "526cb09ce3ada3672eec1deb46205be89a4b563e": { - "balance": "2468000000000000000000" - }, - "ee6959de2b67967b71948c891ab00d8c8f38c7dc": { - "balance": "118200000000000000000" - }, - "ca7ba3ff536c7e5f0e153800bd383db8312998e0": { - "balance": "169600000000000000000" - }, - "1ed06ee51662a86c634588fb62dc43c8f27e7c17": { - "balance": "200000000000000000000" - }, - "730447f97ce9b25f22ba1afb36df27f9586beb9b": { - "balance": "820000000000000000000" - }, - "ae5c9bdad3c5c8a1220444aea5c229c1839f1d64": { - "balance": "477500000000000000000" - }, - "a38306cb70baa8e49186bd68aa70a83d242f2907": { - "balance": "2000000000000000000000" - }, - "71213fca313404204ecba87197741aa9dfe96338": { - "balance": "60000000000000000000" - }, - "10e390ad2ba33d82b37388d09c4544c6b0225de5": { - "balance": "200000000000000000000" - }, - "3b6e814f770748a7c3997806347605480a3fd509": { - "balance": "2000000000000000000000" - }, - "fd452c3969ece3801c542020f1cdcaa1c71ed23d": { - "balance": "100000000000000000000000" - }, - "e742b1e6069a8ffc3c4767235defb0d49cbed222": { - "balance": "800000000000000000000" - }, - "d7225738dcf3578438f8e7c8b3837e42e04a262f": { - "balance": "445860000000000000000" - }, - "cd0b0257e783a3d2c2e3ba9d6e79b75ef98024d4": { - "balance": "2945500000000000000000" - }, - "e80e7fef18a5db15b01473f3ad6b78b2a2f8acd9": { - "balance": "500000000000000000000" - }, - "261575e9cf59c8226fa7aaf91de86fb70f5ac3ae": { - "balance": "300022000000000000000" - }, - "7e71171f2949fa0c3ac254254b1f0440e5e6a038": { - "balance": "40000000000000000000" - }, - "96ea6ac89a2bac95347b51dba63d8bd5ebdedce1": { - "balance": "2000000000000000000000" - }, - "e6ec5cf0c49b9c317e1e706315ef9eb7c0bf11a7": { - "balance": "17200000000000000000000" - }, - "2b99b42e4f42619ee36baa7e4af2d65eacfcba35": { - "balance": "40000000000000000000000" - }, - "c6e4cc0c7283fc1c85bc4813effaaf72b49823c0": { - "balance": "276926000000000000000" - }, - "dbc1ce0e49b1a705d22e2037aec878ee0d75c703": { - "balance": "250000000000000000000" - }, - "806f44bdeb688037015e84ff218049e382332a33": { - "balance": "1999000000000000000000" - }, - "1a3a330e4fcb69dbef5e6901783bf50fd1c15342": { - "balance": "4200000000000000000000" - }, - "d2a84f75675c62d80c88756c428eee2bcb185421": { - "balance": "1200000000000000000000" - }, - "c593b546b7698710a205ad468b2c13152219a342": { - "balance": "1550000000000000000000" - }, - "3f627a769e6a950eb87017a7cd9ca20871136831": { - "balance": "13790000000000000000000" - }, - "f2d5763ce073127e2cedde6faba786c73ca94141": { - "balance": "7900000000000000000000" - }, - "162110f29eac5f7d02b543d8dcd5bb59a5e33b73": { - "balance": "2000000000000000000000" - }, - "59473cd300fffae240f5785626c65dfec792b9af": { - "balance": "20000000000000000000" - }, - "4dcd11815818ae29b85d01367349a8a7fb12d06b": { - "balance": "7900000000000000000000" - }, - "9329ffdc268babde8874b366406c81445b9b2d35": { - "balance": "422415000000000000000" - }, - "0ab4281ebb318590abb89a81df07fa3af904258a": { - "balance": "500000000000000000000" - }, - "875061ee12e820041a01942cb0e65bb427b00060": { - "balance": "2800000000000000000000" - }, - "c9b698e898d20d4d4f408e4e4d061922aa856307": { - "balance": "40000000000000000000" - }, - "ca49a5f58adbefae23ee59eea241cf0482622eaa": { - "balance": "1430000000000000000000" - }, - "196e85df7e732b4a8f0ed03623f4db9db0b8fa31": { - "balance": "21165000000000000000" - }, - "4c760cd9e195ee4f2d6bce2500ff96da7c43ee91": { - "balance": "60000000000000000000000" - }, - "024a098ae702bef5406c9c22b78bd4eb2cc7a293": { - "balance": "4000000000000000000000" - }, - "9d81aea69aed6ad07089d61445348c17f34bfc5b": { - "balance": "300000000000000000000" - }, - "76ab87dd5a05ad839a4e2fc8c85aa6ba05641730": { - "balance": "2000000000000000000000" - }, - "c6e2f5af979a03fd723a1b6efa728318cf9c1800": { - "balance": "668500000000000000000" - }, - "5db69fe93e6fb6fbd450966b97238b110ad8279a": { - "balance": "40000000000000000000000" - }, - "a4259f8345f7e3a8b72b0fec2cf75e321fda4dc2": { - "balance": "1910000000000000000000" - }, - "095030e4b82692dcf8b8d0912494b9b378ec9328": { - "balance": "1340000000000000000000" - }, - "4b470f7ba030bc7cfcf338d4bf0432a91e2ea5ff": { - "balance": "2000000000000000000000" - }, - "99c9f93e45fe3c1418c353e4c5ac3894eef8121e": { - "balance": "101870000000000000000" - }, - "ffac3db879a6c7158e8dec603b407463ba0d31cf": { - "balance": "1970000000000000000000" - }, - "ac8e87ddda5e78fcbcb9fa7fc3ce038f9f7d2e34": { - "balance": "2000000000000000000000" - }, - "7a0589b143a8e5e107c9ac66a9f9f8597ab3e7ab": { - "balance": "1510990000000000000000" - }, - "b7d581fe0af1ec383f3b3c416783f385146a7612": { - "balance": "20000000000000000000000" - }, - "bb3fc0a29c034d710812dcc775c8cab9d28d6975": { - "balance": "1066806000000000000000" - }, - "2c603ff0fe93616c43573ef279bfea40888d6ae7": { - "balance": "4740000000000000000000" - }, - "15f2b7b16432ee50a5f55b41232f6334ed58bdc0": { - "balance": "400000000000000000000" - }, - "7f3d7203c8a447f7bf36d88ae9b6062a5eee78ae": { - "balance": "6000000000000000000000" - }, - "f067e1f1d683556a4cc4fd0c0313239f32c4cfd8": { - "balance": "1000000000000000000000" - }, - "52738c90d860e04cb12f498d96fdb5bf36fc340e": { - "balance": "30000000000000000000" - }, - "45781bbe7714a1c8f73b1c747921df4f84278b70": { - "balance": "2000000000000000000000" - }, - "4a97e8fcf4635ea7fc5e96ee51752ec388716b60": { - "balance": "546000000000000000000" - }, - "54939ff08921b467cf2946751d856378296c63ed": { - "balance": "1000000000000000000000" - }, - "6485470e61db110aebdbafd536769e3c599cc908": { - "balance": "600000000000000000000" - }, - "e20d1bcb71286dc7128a9fc7c6ed7f733892eef5": { - "balance": "1003400000000000000000" - }, - "d6eea898d4ae2b718027a19ce9a5eb7300abe3ca": { - "balance": "27475000000000000000" - }, - "014974a1f46bf204944a853111e52f1602617def": { - "balance": "2000000000000000000000" - }, - "6aa5732f3b86fb8c81efbe6b5b47b563730b06c8": { - "balance": "1000000000000000000000" - }, - "6107d71dd6d0eefb11d4c916404cb98c753e117d": { - "balance": "2000000000000000000000" - }, - "dd7bcda65924aaa49b80984ae173750258b92847": { - "balance": "10000000000000000000000" - }, - "4e7b54474d01fefd388dfcd53b9f662624418a05": { - "balance": "8000000000000000000000" - }, - "24fc73d20793098e09ddab5798506224fa1e1850": { - "balance": "200000000000000000000" - }, - "2b8488bd2d3c197a3d26151815b5a798d27168dc": { - "balance": "6680000000000000000000" - }, - "949131f28943925cfc97d41e0cea0b262973a730": { - "balance": "2800000000000000000000" - }, - "60b8d6b73b79534fb08bb8cbcefac7f393c57bfe": { - "balance": "1760000000000000000000" - }, - "d6acc220ba2e51dfcf21d443361eea765cbd35d8": { - "balance": "20000000000000000000" - }, - "c4c6cb723dd7afa7eb535615e53f3cef14f18118": { - "balance": "1999999000000000000000" - }, - "4c9a862ad115d6c8274ed0b944bdd6a5500510a7": { - "balance": "100000000000000000000" - }, - "85732c065cbd64119941aed430ac59670b6c51c4": { - "balance": "731345000000000000000" - }, - "0126e12ebc17035f35c0e9d11dd148393c405d7a": { - "balance": "1999600000000000000000" - }, - "472048cc609aeb242165eaaa8705850cf3125de0": { - "balance": "1000000000000000000000" - }, - "d2edd1ddd6d86dc005baeb541d22b640d5c7cae5": { - "balance": "20000000000000000000" - }, - "4549b15979255f7e65e99b0d5604db98dfcac8bf": { - "balance": "4000000000000000000000" - }, - "c6c7c191379897dd9c9d9a33839c4a5f62c0890d": { - "balance": "4000085000000000000000" - }, - "d367009ab658263b62c2333a1c9e4140498e1389": { - "balance": "2000000000000000000000" - }, - "143f5f1658d9e578f4f3d95f80c0b1bd3933cbda": { - "balance": "1490000000000000000000" - }, - "1a09fdc2c7a20e23574b97c69e93deba67d37220": { - "balance": "1998000000000000000000" - }, - "ac8b509aefea1dbfaf2bb33500d6570b6fd96d51": { - "balance": "1820000000000000000000" - }, - "16ffac84032940f0121a09668b858a7e79ffa3bb": { - "balance": "3879210000000000000000" - }, - "f338459f32a159b23db30ac335769ab2351aa63c": { - "balance": "30000000000000000000000" - }, - "d82251456dc1380f8f5692f962828640ab9f2a03": { - "balance": "4879980000000000000000" - }, - "47f4696bd462b20da09fb83ed2039818d77625b3": { - "balance": "149000000000000000000" - }, - "3dde8b15b3ccbaa5780112c3d674f313bba68026": { - "balance": "1773000000000000000000" - }, - "f70d637a845c06db6cdc91e6371ce7c4388a628e": { - "balance": "20000000000000000000" - }, - "68295e8ea5afd9093fc0a465d157922b5d2ae234": { - "balance": "19982000000000000000" - }, - "614e8bef3dd2c59b59a4145674401018351884ea": { - "balance": "20000000000000000000" - }, - "4737d042dc6ae73ec73ae2517acea2fdd96487c5": { - "balance": "1000000000000000000000" - }, - "cec6fc65853f9cce5f8e844676362e1579015f02": { - "balance": "2000000000000000000000" - }, - "ae47e2609cfafe369d66d415d939de05081a9872": { - "balance": "27060000000000000000000" - }, - "09a928d528ec1b3e25ffc83e218c1e0afe8928c7": { - "balance": "18200000000000000000" - }, - "9b444fd337e5d75293adcfff70e1ea01db023222": { - "balance": "100000000000000000000" - }, - "168bdec818eafc6d2992e5ef54aa0e1601e3c561": { - "balance": "1000110000000000000000" - }, - "353dbec42f92b50f975129b93c4c997375f09073": { - "balance": "1999000000000000000000" - }, - "6fcc2c732bdd934af6ccd16846fb26ef89b2aa9b": { - "balance": "10001242000000000000000" - }, - "6f2576da4de283bbe8e3ee69ddd66e5e711db3f5": { - "balance": "1260800000000000000000" - }, - "3a3dd104cd7eb04f21932fd433ea7affd39369f5": { - "balance": "357500000000000000000" - }, - "d44f4ac5fad76bdc1537a3b3af6472319b410d9d": { - "balance": "1600000000000000000000" - }, - "3d9d6be57ff83e065985664f12564483f2e600b2": { - "balance": "2041600000000000000000" - }, - "88f1045f19f2d3191816b1df18bb6e1435ad1b38": { - "balance": "240000000000000000000" - }, - "ddab75fb2ff9fecb88f89476688e2b00e367ebf9": { - "balance": "19400000000000000000000" - }, - "092e815558402d67f90d6bfe6da0b2fffa91455a": { - "balance": "60000000000000000000" - }, - "a7024cfd742c1ec13c01fea18d3042e65f1d5dee": { - "balance": "11272229000000000000000" - }, - "7f46bb25460dd7dae4211ca7f15ad312fc7dc75c": { - "balance": "6685000000000000000000" - }, - "93f18cd2526040761488c513174d1e7963768b2c": { - "balance": "2416500000000000000000" - }, - "352f25babf4a690673e35195efa8f79d05848aad": { - "balance": "66800000000000000000000" - }, - "f7b151cc5e571c17c76539dbe9964cbb6fe5de79": { - "balance": "2148000000000000000000" - }, - "ff3eee57c34d6dae970d8b311117c53586cd3502": { - "balance": "1700000000000000000000" - }, - "ae6f0c73fdd77c489727512174d9b50296611c4c": { - "balance": "6000000000000000000000" - }, - "7819b0458e314e2b53bfe00c38495fd4b9fdf8d6": { - "balance": "20000000000000000000" - }, - "7fdba031c78f9c096d62d05a369eeab0bccc55e5": { - "balance": "2800000000000000000000" - }, - "735e328666ed5637142b3306b77ccc5460e72c3d": { - "balance": "1968682000000000000000" - }, - "0bfbb6925dc75e52cf2684224bbe0550fea685d3": { - "balance": "1970000000000000000000" - }, - "6be16313643ebc91ff9bb1a2e116b854ea933a45": { - "balance": "500000000000000000000" - }, - "d6acffd0bfd99c382e7bd56ff0e6144a9e52b08e": { - "balance": "160000000000000000000" - }, - "276a006e3028ecd44cdb62ba0a77ce94ebd9f10f": { - "balance": "1800000000000000000000" - }, - "10711c3dda32317885f0a2fd8ae92e82069b0d0b": { - "balance": "4000000000000000000000" - }, - "43cb9652818c6f4d6796b0e89409306c79db6349": { - "balance": "2000000000000000000000" - }, - "7109dd011d15f3122d9d3a27588c10d77744508b": { - "balance": "2000000000000000000000" - }, - "3497dd66fd118071a78c2cb36e40b6651cc82598": { - "balance": "109600000000000000000" - }, - "9bf672d979b36652fc5282547a6a6bc212ae4368": { - "balance": "656000000000000000000" - }, - "eaed16eaf5daab5bf0295e5e077f59fb8255900b": { - "balance": "4000000000000000000000" - }, - "7ac58f6ffc4f8107ae6e30378e4e9f99c57fbb24": { - "balance": "40000000000000000000" - }, - "45a570dcc2090c86a6b3ea29a60863dde41f13b5": { - "balance": "232500000000000000000" - }, - "433a3b68e56b0df1862b90586bbd39c840ff1936": { - "balance": "2000000000000000000000" - }, - "e8eaf12944092dc3599b3953fa7cb1c9761cc246": { - "balance": "1800000000000000000000" - }, - "ec11362cec810985d0ebbd7b73451444985b369f": { - "balance": "30000047000000000000000" - }, - "78e83f80b3678c7a0a4e3e8c84dccde064426277": { - "balance": "1790000000000000000000" - }, - "0cc67f8273e1bae0867fd42e8b8193d72679dbf8": { - "balance": "500000000000000000000" - }, - "c70d856d621ec145303c0a6400cd17bbd6f5eaf7": { - "balance": "20000000000000000000" - }, - "f468906e7edf664ab0d8be3d83eb7ab3f7ffdc78": { - "balance": "1700000000000000000000" - }, - "3c286cfb30146e5fd790c2c8541552578de334d8": { - "balance": "10203000000000000000000" - }, - "c401c427cccff10decb864202f36f5808322a0a8": { - "balance": "3329300000000000000000" - }, - "afd019ff36a09155346b69974815a1c912c90aa4": { - "balance": "2000000000000000000000" - }, - "96fe59c3dbb3aa7cc8cb62480c65e56e6204a7e2": { - "balance": "20000000000000000000000" - }, - "a47779d8bc1c7bce0f011ccb39ef68b854f8de8f": { - "balance": "2000000000000000000000" - }, - "58c650ced40bb65641b8e8a924a039def46854df": { - "balance": "18500000000000000000" - }, - "86f4f40ad984fbb80933ae626e0e42f9333fdd41": { - "balance": "1000000000000000000000" - }, - "b22d5055d9623135961e6abd273c90deea16a3e7": { - "balance": "1400000000000000000000" - }, - "ee3564f5f1ba0f94ec7bac164bddbf31c6888b55": { - "balance": "100000000000000000000" - }, - "cf26b47bd034bc508e6c4bcfd6c7d30034925761": { - "balance": "1800000000000000000000" - }, - "e87dbac636a37721df54b08a32ef4959b5e4ff82": { - "balance": "2000000000000000000000" - }, - "3bf86ed8a3153ec933786a02ac090301855e576b": { - "balance": "450000000000000000000000" - }, - "cfd2728dfb8bdbf3bf73598a6e13eaf43052ea2b": { - "balance": "170000000000000000000" - }, - "85b16f0b8b34dff3804f69e2168a4f7b24d1042b": { - "balance": "317000000000000000000" - }, - "84db1459bb00812ea67ecb3dc189b72187d9c501": { - "balance": "148851000000000000000" - }, - "8c3a9ee71f729f236cba3867b4d79d8ceee25dbc": { - "balance": "100000000000000000000" - }, - "e677c31fd9cb720075dca49f1abccd59ec33f734": { - "balance": "7800000000000000000000" - }, - "8889448316ccf14ed86df8e2f478dc63c4338340": { - "balance": "15200000000000000000" - }, - "b279c7d355c2880392aad1aa21ee867c3b3507df": { - "balance": "1261000000000000000000" - }, - "12b5e28945bb2969f9c64c63cc05b6f1f8d6f4d5": { - "balance": "7722162000000000000000" - }, - "8d2303341e1e1eb5e8189bde03f73a60a2a54861": { - "balance": "100000000000000000000" - }, - "94d81074db5ae197d2bb1373ab80a87d121c4bd3": { - "balance": "9400000000000000000000" - }, - "752c9febf42f66c4787bfa7eb17cf5333bba5070": { - "balance": "1966448000000000000000" - }, - "16816aac0ede0d2d3cd442da79e063880f0f1d67": { - "balance": "2000000000000000000000" - }, - "daac91c1e859d5e57ed3084b50200f9766e2c52b": { - "balance": "400000000000000000000" - }, - "32c2fde2b6aabb80e5aea2b949a217f3cb092283": { - "balance": "5614827000000000000000" - }, - "cdab46a5902080646fbf954204204ae88404822b": { - "balance": "544942000000000000000" - }, - "fdf42343019b0b0c6bf260b173afab7e45b9d621": { - "balance": "1999944000000000000000" - }, - "791f6040b4e3e50dcf3553f182cd97a90630b75d": { - "balance": "4000000000000000000000" - }, - "4b762166dd1118e84369f804c75f9cd657bf730c": { - "balance": "500000000000000000000" - }, - "a76d3f156251b72c0ccf4b47a3393cbd6f49a9c5": { - "balance": "1337000000000000000000" - }, - "c5eb42295e9cadeaf2af12dede8a8d53c579c469": { - "balance": "3820000000000000000000" - }, - "db9371b30c4c844e59e03e924be606a938d1d310": { - "balance": "2000000000000000000000" - }, - "2cd39334ac7eac797257abe3736195f5b4b5ce0f": { - "balance": "99964000000000000000" - }, - "ad44357e017e244f476931c7b8189efee80a5d0a": { - "balance": "300000000000000000000" - }, - "4ca7b717d9bc8793b04e051a8d23e1640f5ba5e3": { - "balance": "1248980000000000000000" - }, - "73e4a2b60cf48e8baf2b777e175a5b1e4d0c2d8f": { - "balance": "100000000000000000000" - }, - "5a1d2d2d1d520304b6208849570437eb3091bb9f": { - "balance": "1970000000000000000000" - }, - "53047dc8ac9083d90672e8b3473c100ccd278323": { - "balance": "40000000000000000000" - }, - "26fe174cbf526650e0cd009bd6126502ce8e684d": { - "balance": "11640000000000000000000" - }, - "e2df23f6ea04becf4ab701748dc0963184555cdb": { - "balance": "2000000000000000000000" - }, - "c1170dbaadb3dee6198ea544baec93251860fda5": { - "balance": "1200000000000000000000" - }, - "8bbeacfc29cfe93402db3c41d99ab759662e73ec": { - "balance": "2000000000000000000000" - }, - "165305b787322e25dc6ad0cefe6c6f334678d569": { - "balance": "2000000000000000000000" - }, - "095457f8ef8e2bdc362196b9a9125da09c67e3ab": { - "balance": "200000000000000000000" - }, - "702802f36d00250fab53adbcd696f0176f638a49": { - "balance": "2000000000000000000000" - }, - "489334c2b695c8ee0794bd864217fb9fd8f8b135": { - "balance": "18200000000000000000" - }, - "fa8cf4e627698c5d5788abb7880417e750231399": { - "balance": "4244640000000000000000" - }, - "3329eb3baf4345d600ced40e6e9975656f113742": { - "balance": "4999711000000000000000" - }, - "b4dd5499daeb2507fb2de12297731d4c72b16bb0": { - "balance": "20000000000000000000" - }, - "88c2516a7cdb09a6276d7297d30f5a4db1e84b86": { - "balance": "4000000000000000000000" - }, - "612ced8dc0dc9e899ee46f7962333315f3f55e44": { - "balance": "338830000000000000000" - }, - "d71e43a45177ad51cbe0f72184a5cb503917285a": { - "balance": "200000000000000000000" - }, - "2fb566c94bbba4e3cb67cdda7d5fad7131539102": { - "balance": "2000000000000000000000" - }, - "03be5b4629aefbbcab9de26d39576cb7f691d764": { - "balance": "200550000000000000000" - }, - "025367960304beee34591118e9ac2d1358d8021a": { - "balance": "2000000000000000000000" - }, - "a5d5b8b62d002def92413710d13b6ff8d4fc7dd3": { - "balance": "400000000000000000000" - }, - "df3b72c5bd71d4814e88a62321a93d4011e3578b": { - "balance": "4000000000000000000000" - }, - "3588895ac9fbafec012092dc05c0c302d90740fa": { - "balance": "3000000000000000000000" - }, - "6021e85a8814fce1e82a41abd1d3b2dad2faefe0": { - "balance": "2000000000000000000000" - }, - "17ee9f54d4ddc84d670eff11e54a659fd72f4455": { - "balance": "16000000000000000000000" - }, - "873c6f70efb6b1d0f2bbc57eebcd70617c6ce662": { - "balance": "1013478000000000000000" - }, - "1fcc7ce6a8485895a3199e16481f72e1f762defe": { - "balance": "1000000000000000000000" - }, - "d0a7209b80cf60db62f57d0a5d7d521a69606655": { - "balance": "160000000000000000000" - }, - "a514d00edd7108a6be839a638db2415418174196": { - "balance": "30000000000000000000000" - }, - "046377f864b0143f282174a892a73d3ec8ec6132": { - "balance": "191000000000000000000" - }, - "c126573d87b0175a5295f1dd07c575cf8cfa15f2": { - "balance": "10000000000000000000000" - }, - "0e123d7da6d1e6fac2dcadd27029240bb39052fe": { - "balance": "1000000000000000000000" - }, - "ad5a8d3c6478b69f657db3837a2575ef8e1df931": { - "balance": "36990000000000000000" - }, - "db882eacedd0eff263511b312adbbc59c6b8b25b": { - "balance": "9100000000000000000000" - }, - "0b43bd2391025581d8956ce42a072579cbbfcb14": { - "balance": "18800000000000000000" - }, - "affea0473722cb7f0e0e86b9e11883bf428d8d54": { - "balance": "1940000000000000000000" - }, - "e32b1c4725a1875449e98f970eb3e54062d15800": { - "balance": "200000000000000000000" - }, - "98f4af3af0aede5fafdc42a081ecc1f89e3ccf20": { - "balance": "9400000000000000000000" - }, - "3b4768fd71e2db2cbe7fa050483c27b4eb931df3": { - "balance": "2000000000000000000000" - }, - "d5f7c41e07729dfa6dfc64c4423160a22c609fd3": { - "balance": "1790000000000000000000" - }, - "d944c8a69ff2ca1249690c1229c7192f36251062": { - "balance": "1970000000000000000000" - }, - "5ae64e853ba0a51282cb8db52e41615e7c9f733f": { - "balance": "2000000000000000000000" - }, - "b13f93af30e8d7667381b2b95bc1a699d5e3e129": { - "balance": "420000000000000000000" - }, - "8a20e5b5cee7cd1f5515bace3bf4f77ffde5cc07": { - "balance": "80000000000000000000" - }, - "2448596f91c09baa30bc96106a2d37b5705e5d28": { - "balance": "2000000000000000000000" - }, - "ccca24d8c56d6e2c07db086ec07e585be267ac8d": { - "balance": "200000000000000000000" - }, - "f67bb8e2118bbcd59027666eedf6943ec9f880a5": { - "balance": "4000000000000000000000" - }, - "7ae659eb3bc46852fa86fac4e21c768d50388945": { - "balance": "286000000000000000000" - }, - "467e0ed54f3b76ae0636176e07420815a021736e": { - "balance": "2000000000000000000000" - }, - "a46cd237b63eea438c8e3b6585f679e4860832ac": { - "balance": "1000000000000000000000" - }, - "6b760d4877e6a627c1c967bee451a8507ddddbab": { - "balance": "910000000000000000000" - }, - "593044670faeff00a55b5ae051eb7be870b11694": { - "balance": "133700000000000000000" - }, - "533c06928f19d0a956cc28866bf6c8d8f4191a94": { - "balance": "292320000000000000000" - }, - "262dc1364ccf6df85c43268ee182554dae692e29": { - "balance": "4927600000000000000000" - }, - "e4368bc1420b35efda95fafbc73090521916aa34": { - "balance": "4000000000000000000000" - }, - "feb92d30bf01ff9a1901666c5573532bfa07eeec": { - "balance": "1000000000000000000000" - }, - "ee25b9a7032679b113588ed52c137d1a053a1e94": { - "balance": "199820000000000000000" - }, - "20134cbff88bfadc466b52eceaa79857891d831e": { - "balance": "1000000000000000000000" - }, - "07b1a306cb4312df66482c2cae72d1e061400fcd": { - "balance": "20000000000000000000000" - }, - "e791d585b89936b25d298f9d35f9f9edc25a2932": { - "balance": "2000000000000000000000" - }, - "2e6933543d4f2cc00b5350bd8068ba9243d6beb0": { - "balance": "2000000000000000000000" - }, - "dae0d33eaa341569fa9ff5982684854a4a328a6e": { - "balance": "1000000000000000000000" - }, - "125cc5e4d56b2bcc2ee1c709fb9e68fb177440bd": { - "balance": "2000000000000000000000" - }, - "ec99e95dece46ffffb175eb6400fbebb08ee9b95": { - "balance": "100000000000000000000" - }, - "c538a0ff282aaa5f4b75cfb62c70037ee67d4fb5": { - "balance": "2000000000000000000000" - }, - "60676d1fa21fca052297e24bf96389c5b12a70d7": { - "balance": "241500000000000000000" - }, - "4b3dfbdb454be5279a3b8addfd0ed1cd37a9420d": { - "balance": "2000000000000000000000" - }, - "cdb597299030183f6e2d238533f4642aa58754b6": { - "balance": "400000000000000000000" - }, - "1ef2dcbfe0a500411d956eb8c8939c3d6cfe669d": { - "balance": "776000000000000000000" - }, - "a7247c53d059eb7c9310f628d7fc6c6a0a773f08": { - "balance": "500000000000000000000" - }, - "9799ca21dbcf69bfa1b3f72bac51b9e3ca587cf9": { - "balance": "1700000000000000000000" - }, - "ddf95c1e99ce2f9f5698057c19d5c94027ee4a6e": { - "balance": "6000000000000000000000" - }, - "83563bc364ed81a0c6da3b56ff49bbf267827a9c": { - "balance": "17332000000000000000000" - }, - "a192698007cc11aa603d221d5feea076bcf7c30d": { - "balance": "2000000000000000000000" - }, - "0134ff38155fabae94fd35c4ffe1d79de7ef9c59": { - "balance": "985000000000000000000" - }, - "80977316944e5942e79b0e3abad38da746086519": { - "balance": "38800000000000000000" - }, - "193d37ed347d1c2f4e35350d9a444bc57ca4db43": { - "balance": "60000000000000000000" - }, - "009a6d7db326679b77c90391a7476d238f3ba33e": { - "balance": "200200000000000000000" - }, - "337b3bdf86d713dbd07b5dbfcc022b7a7b1946ae": { - "balance": "3980000000000000000000" - }, - "7de7fe419cc61f91f408d234cc80d5ca3d054d99": { - "balance": "20000000000000000000" - }, - "f47bb134da30a812d003af8dccb888f44bbf5724": { - "balance": "5190000000000000000000" - }, - "fd920f722682afb5af451b0544d4f41b3b9d5742": { - "balance": "2330200000000000000000" - }, - "0a917f3b5cb0b883047fd9b6593dbcd557f453b9": { - "balance": "1000000000000000000000" - }, - "ce9786d3712fa200e9f68537eeaa1a06a6f45a4b": { - "balance": "1790000000000000000000" - }, - "9ab98d6dbb1eaae16d45a04568541ad3d8fe06cc": { - "balance": "272451000000000000000" - }, - "0b7bb342f01bc9888e6a9af4a887cbf4c2dd2caf": { - "balance": "16000000000000000000000" - }, - "4c0b1515dfced7a13e13ee12c0f523ae504f032b": { - "balance": "50000000000000000000000" - }, - "ac2889b5966f0c7f9edb42895cb69d1c04f923a2": { - "balance": "5000000000000000000000" - }, - "d008513b27604a89ba1763b6f84ce688b346945b": { - "balance": "1000000000000000000000" - }, - "a4b09de6e713dc69546e76ef0acf40b94f0241e6": { - "balance": "322656000000000000000" - }, - "b153f828dd076d4a7c1c2574bb2dee1a44a318a8": { - "balance": "400000000000000000000" - }, - "02ade5db22f8b758ee1443626c64ec2f32aa0a15": { - "balance": "20000000000000000000000" - }, - "0a0650861f785ed8e4bf1005c450bbd06eb48fb6": { - "balance": "3066860000000000000000" - }, - "b75149e185f6e3927057739073a1822ae1cf0df2": { - "balance": "4000086000000000000000" - }, - "84cb7da0502df45cf561817bbd2362f451be02da": { - "balance": "1337000000000000000000" - }, - "c91bb562e42bd46130e2d3ae4652b6a4eb86bc0f": { - "balance": "540000000000000000000" - }, - "b234035f7544463ce1e22bc553064684c513cd51": { - "balance": "249750000000000000000" - }, - "e5e33800a1b2e96bde1031630a959aa007f26e51": { - "balance": "1337000000000000000000" - }, - "ae5ce3355a7ba9b332760c0950c2bc45a85fa9a0": { - "balance": "400000000000000000000" - }, - "e6f5eb649afb99599c414b27a9c9c855357fa878": { - "balance": "2674000000000000000000" - }, - "7010be2df57bd0ab9ae8196cd50ab0c521aba9f9": { - "balance": "1970000000000000000000" - }, - "ca4288014eddc5632f5facb5e38517a8f8bc5d98": { - "balance": "340000000000000000000" - }, - "2784903f1d7c1b5cd901f8875d14a79b3cbe2a56": { - "balance": "22388000000000000000000" - }, - "f8dce867f0a39c5bef9eeba609229efa02678b6c": { - "balance": "2000000000000000000000" - }, - "e020e86362b487752836a6de0bc02cd8d89a8b6a": { - "balance": "6000000000000000000000" - }, - "c4088c025f3e85013f5439fb3440a17301e544fe": { - "balance": "2325000000000000000000" - }, - "befb448c0c5f683fb67ee570baf0db5686599751": { - "balance": "1970000000000000000000" - }, - "2f187d5a704d5a338c5b2876a090dce964284e29": { - "balance": "4000000000000000000000" - }, - "ec0e18a01dc4dc5daae567c3fa4c7f8f9b590205": { - "balance": "315900000000000000000" - }, - "637f5869d6e4695f0eb9e27311c4878aff333380": { - "balance": "1969212000000000000000" - }, - "d1100dd00fe2ddf18163ad964d0b69f1f2e9658a": { - "balance": "5959598000000000000000" - }, - "17ef4acc1bf147e326749d10e677dcffd76f9e06": { - "balance": "39980000000000000000000" - }, - "200dfc0b71e359b2b465440a36a6cdc352773007": { - "balance": "1500000000000000000000" - }, - "efe0675da98a5dda70cd96196b87f4e726b43348": { - "balance": "1164000000000000000000" - }, - "d5bd5e8455c130169357c471e3e681b7996a7276": { - "balance": "841500000000000000000" - }, - "9c7b6dc5190fe2912963fcd579683ec7395116b0": { - "balance": "776000000000000000000" - }, - "b105dd3d987cffd813e9c8500a80a1ad257d56c6": { - "balance": "1999944000000000000000" - }, - "145250b06e4fa7cb2749422eb817bdda8b54de5f": { - "balance": "219000000000000000000" - }, - "d96db33b7b5a950c3efa2dc31b10ba10a532ef87": { - "balance": "2000000000000000000000" - }, - "af529bdb459cc185bee5a1c58bf7e8cce25c150d": { - "balance": "197000000000000000000" - }, - "185546e8768d506873818ac9751c1f12116a3bef": { - "balance": "200000000000000000000" - }, - "51d24bc3736f88dd63b7222026886630b6eb878d": { - "balance": "2000000000000000000000" - }, - "69af28b0746cac0da17084b9398c5e36bb3a0df2": { - "balance": "1004700000000000000000" - }, - "76f83ac3da30f7092628c7339f208bfc142cb1ee": { - "balance": "2842600000000000000000" - }, - "00f463e137dcf625fbf3bca39eca98d2b968cf7f": { - "balance": "5910000000000000000000" - }, - "2084fce505d97bebf1ad8c5ff6826fc645371fb2": { - "balance": "30000000000000000000" - }, - "53a714f99fa00fef758e23a2e746326dad247ca7": { - "balance": "1490000000000000000000" - }, - "0bf064428f83626722a7b5b26a9ab20421a7723e": { - "balance": "133700000000000000000" - }, - "ac6f68e837cf1961cb14ab47446da168a16dde89": { - "balance": "1337000000000000000000" - }, - "4b3c7388cc76da3d62d40067dabccd7ef0433d23": { - "balance": "100076000000000000000" - }, - "deb9a49a43873020f0759185e20bbb4cf381bb8f": { - "balance": "211628000000000000000" - }, - "5bf9f2226e5aeacf1d80ae0a59c6e38038bc8db5": { - "balance": "6000000000000000000000" - }, - "9d0e7d92fb305853d798263bf15e97c72bf9d7e0": { - "balance": "1000000000000000000000" - }, - "2b5c60e84535eeb4d580de127a12eb2677ccb392": { - "balance": "20000000000000000000000" - }, - "d8d65420c18c2327cc5af97425f857e4a9fd51b3": { - "balance": "1760000000000000000000" - }, - "30ec9392244a2108c987bc5cdde0ed9f837a817b": { - "balance": "1560562000000000000000" - }, - "56a1d60d40f57f308eebf087dee3b37f1e7c2cba": { - "balance": "1159600000000000000000" - }, - "a9a1cdc33bfd376f1c0d76fb6c84b6b4ac274d68": { - "balance": "5000000000000000000000" - }, - "a67f38819565423aa85f3e3ab61bc763cbab89dd": { - "balance": "2130000000000000000000" - }, - "62d5cc7117e18500ac2f9e3c26c86b0a94b0de15": { - "balance": "105000000000000000000" - }, - "4970d3acf72b5b1f32a7003cf102c64ee0547941": { - "balance": "140000000000000000000000" - }, - "76628150e2995b5b279fc83e0dd5f102a671dd1c": { - "balance": "40000000000000000000000" - }, - "3d8f39881b9edfe91227c33fa4cdd91e678544b0": { - "balance": "86111000000000000000" - }, - "ff0b7cb71da9d4c1ea6ecc28ebda504c63f82fd1": { - "balance": "1043000000000000000000" - }, - "8d795c5f4a5689ad62da961671f028065286d554": { - "balance": "2048000000000000000000" - }, - "be2346a27ff9b702044f500deff2e7ffe6824541": { - "balance": "20000000000000000000" - }, - "0dbd417c372b8b0d01bcd944706bd32e60ae28d1": { - "balance": "340000000000000000000" - }, - "467fbf41441600757fe15830c8cd5f4ffbbbd560": { - "balance": "10000000000000000000000" - }, - "090cd67b60e81d54e7b5f6078f3e021ba65b9a1e": { - "balance": "1000000000000000000000" - }, - "55a4cac0cb8b582d9fef38c5c9fff9bd53093d1f": { - "balance": "1970000000000000000000" - }, - "3b7b4f53c45655f3dc5f017edc23b16f9bc536fa": { - "balance": "100000000000000000000" - }, - "d508d39c70916f6abc4cc7f999f011f077105802": { - "balance": "100470000000000000000" - }, - "037dd056e7fdbd641db5b6bea2a8780a83fae180": { - "balance": "140000000000000000000" - }, - "660557bb43f4be3a1b8b85e7df7b3c5bcd548057": { - "balance": "6000000000000000000000" - }, - "02089361a3fe7451fb1f87f01a2d866653dc0b07": { - "balance": "39976000000000000000" - }, - "c4bec96308a20f90cab18399c493fd3d065abf45": { - "balance": "14000000000000000000000" - }, - "cca07bb794571d4acf041dad87f0d1ef3185b319": { - "balance": "2000000000000000000000" - }, - "f2d0e986d814ea13c8f466a0538c53dc922651f0": { - "balance": "1380000000000000000000" - }, - "662cfa038fab37a01745a364e1b98127c503746d": { - "balance": "3940000000000000000000" - }, - "3336c3ef6e8b50ee90e037b164b7a8ea5faac65d": { - "balance": "272712000000000000000" - }, - "30e33358fc21c85006e40f32357dc8895940aaf0": { - "balance": "1910000000000000000000" - }, - "41a9a404fc9f5bfee48ec265b12523338e29a8bf": { - "balance": "388000000000000000000" - }, - "6af235d2bbe050e6291615b71ca5829658810142": { - "balance": "3000000000000000000000" - }, - "fd5a63157f914fd398eab19c137dd9550bb7715c": { - "balance": "100000000000000000000" - }, - "8a4314fb61cd938fc33e15e816b113f2ac89a7fb": { - "balance": "432800000000000000000" - }, - "b216dc59e27c3d7279f5cd5bb2becfb2606e14d9": { - "balance": "400000000000000000000" - }, - "f5a5459fcdd5e5b273830df88eea4cb77ddadfb9": { - "balance": "74500000000000000000" - }, - "df31025f5649d2c6eea41ed3bdd3471a790f759a": { - "balance": "20000000000000000000" - }, - "721f9d17e5a0e74205947aeb9bc6a7938961038f": { - "balance": "51900000000000000000" - }, - "08d0864dc32f9acb36bf4ea447e8dd6726906a15": { - "balance": "2000200000000000000000" - }, - "54575c3114751e3c631971da6a2a02fd3ffbfcc8": { - "balance": "1940000000000000000000" - }, - "8f60895fbebbb5017fcbff3cdda397292bf25ba6": { - "balance": "429177000000000000000" - }, - "91fe8a4c6164df8fa606995d6ba7adcaf1c893ce": { - "balance": "17000000000000000000000" - }, - "889087f66ff284f8b5efbd29493b706733ab1447": { - "balance": "9850000000000000000000" - }, - "051633080d07a557adde319261b074997f14692d": { - "balance": "5800000000000000000000" - }, - "59a12df2e3ef857aceff9306b309f6a500f70134": { - "balance": "1000000000000000000000" - }, - "9f64a8e8dacf4ade30d10f4d59b0a3d5abfdbf74": { - "balance": "1000060000000000000000" - }, - "8846928d683289a2d11df8db7a9474988ef01348": { - "balance": "10000000000000000000000" - }, - "dff1b220de3d8e9ca4c1b5be34a799bcded4f61c": { - "balance": "385428000000000000000" - }, - "7e7c1e9a61a08a83984835c70ec31d34d3eaa87f": { - "balance": "191000000000000000000" - }, - "fe210b8f04dc6d4f76216acfcbd59ba83be9b630": { - "balance": "20000000000000000000" - }, - "dc8c2912f084a6d184aa73638513ccbc326e0102": { - "balance": "1295000000000000000000" - }, - "dddd7b9e6eab409b92263ac272da801b664f8a57": { - "balance": "500000000000000000000000" - }, - "86a5f8259ed5b09e188ce346ee92d34aa5dd93fa": { - "balance": "200000000000000000000" - }, - "dc1f1979615f082140b8bb78c67b27a1942713b1": { - "balance": "60000000000000000000" - }, - "ea66e7b84dcdbf36eea3e75b85382a75f1a15d96": { - "balance": "1729135000000000000000" - }, - "039e7a4ebc284e2ccd42b1bdd60bd6511c0f7706": { - "balance": "17300000000000000000" - }, - "36bfe1fa3b7b70c172eb042f6819a8972595413e": { - "balance": "1000000000000000000000" - }, - "039ef1ce52fe7963f166d5a275c4b1069fe3a832": { - "balance": "400008000000000000000" - }, - "f1df55dcc34a051012b575cb968bc9c458ea09c9": { - "balance": "4000000000000000000000" - }, - "168b5019b818691644835fe69bf229e17112d52c": { - "balance": "28000000000000000000000" - }, - "f60bd735543e6bfd2ea6f11bff627340bc035a23": { - "balance": "2000000000000000000000" - }, - "2cbb0c73df91b91740b6693b774a7d05177e8e58": { - "balance": "1850000000000000000000" - }, - "9ffcf5ef46d933a519d1d16c6ba3189b27496224": { - "balance": "1000000000000000000000" - }, - "0e11d77a8977fac30d268445e531149b31541a24": { - "balance": "2000000000000000000000" - }, - "dfb1626ef48a1d7d7552a5e0298f1fc23a3b482d": { - "balance": "1713860000000000000000" - }, - "cc943be1222cd1400a2399dd1b459445cf6d54a9": { - "balance": "12530000000000000000000" - }, - "b37c2b9f50637bece0ca959208aefee6463ba720": { - "balance": "400000000000000000000" - }, - "96b906ea729f4655afe3e57d35277c967dfa1577": { - "balance": "1000000000000000000000" - }, - "7995bd8ce2e0c67bf1c7a531d477bca1b2b97561": { - "balance": "5945100000000000000000" - }, - "96f820500b70f4a3e3239d619cff8f222075b135": { - "balance": "200000000000000000000" - }, - "ad3565d52b688added08168b2d3872d17d0a26ae": { - "balance": "100000000000000000000" - }, - "9e7c2050a227bbfd60937e268cea3e68fea8d1fe": { - "balance": "100000000000000000000" - }, - "7e59dc60be8b2fc19abd0a5782c52c28400bce97": { - "balance": "1000000000000000000000" - }, - "01ed5fba8d2eab673aec042d30e4e8a611d8c55a": { - "balance": "2000000000000000000000" - }, - "59a087b9351ca42f58f36e021927a22988284f38": { - "balance": "18500000000000000000" - }, - "2fe0023f5722650f3a8ac01009125e74e3f82e9b": { - "balance": "3000000000000000000000" - }, - "bd1803370bddb129d239fd16ea8526a6188ae58e": { - "balance": "500000000000000000000" - }, - "c70527d444c490e9fc3f5cc44e66eb4f306b380f": { - "balance": "4000000000000000000000" - }, - "0f206e1a1da7207ea518b112418baa8b06260328": { - "balance": "600000000000000000000" - }, - "6e1a046caf5b4a57f4fd4bc173622126b4e2fd86": { - "balance": "1790000000000000000000" - }, - "84008a72f8036f3feba542e35078c057f32a8825": { - "balance": "100000000000000000000" - }, - "246291165b59332df5f18ce5c98856fae95897d6": { - "balance": "1700000000000000000000" - }, - "7e99dfbe989d3ba529d19751b7f4317f8953a3e2": { - "balance": "400000000000000000000" - }, - "748c285ef1233fe4d31c8fb1378333721c12e27a": { - "balance": "2000000000000000000000" - }, - "3dd12e556a603736feba4a6fa8bd4ac45d662a04": { - "balance": "167450000000000000000000" - }, - "d0ae735d915e946866e1fea77e5ea466b5cadd16": { - "balance": "2000000000000000000000" - }, - "4f767bc8794aef9a0a38fea5c81f14694ff21a13": { - "balance": "512200000000000000000" - }, - "0e2f8e28a681f77c583bd0ecde16634bdd7e00cd": { - "balance": "95060000000000000000" - }, - "d74a6e8d6aab34ce85976814c1327bd6ea0784d2": { - "balance": "100000000000000000000000" - }, - "629be7ab126a5398edd6da9f18447e78c692a4fd": { - "balance": "2000000000000000000000" - }, - "2e46fcee6a3bb145b594a243a3913fce5dad6fba": { - "balance": "10000000000000000000000" - }, - "e39b11a8ab1ff5e22e5ae6517214f73c5b9b55dc": { - "balance": "2000000000000000000000" - }, - "119aa64d5b7d181dae9d3cb449955c89c1f963fa": { - "balance": "700000000000000000000" - }, - "ce079f51887774d8021cb3b575f58f18e9acf984": { - "balance": "180000000000000000000" - }, - "550c306f81ef5d9580c06cb1ab201b95c748a691": { - "balance": "665800000000000000000" - }, - "06dc7f18cee7edab5b795337b1df6a9e8bd8ae59": { - "balance": "400000000000000000000" - }, - "e21c778ef2a0d7f751ea8c074d1f812243863e4e": { - "balance": "5308559000000000000000" - }, - "45d4b54d37a8cf599821235f062fa9d170ede8a4": { - "balance": "324000000000000000000" - }, - "893a6c2eb8b40ab096b4f67e74a897b840746e86": { - "balance": "1730000000000000000000" - }, - "d44d81e18f46e2cfb5c1fcf5041bc8569767d100": { - "balance": "36381800000000000000000" - }, - "c5de1203d3cc2cea31c82ee2de5916880799eafd": { - "balance": "5000000000000000000000" - }, - "7f0f04fcf37a53a4e24ede6e93104e78be1d3c9e": { - "balance": "2000000000000000000000" - }, - "3ce1dc97fcd7b7c4d3a18a49d6f2a5c1b1a906d7": { - "balance": "200000000000000000000" - }, - "ac4ee9d502e7d2d2e99e59d8ca7d5f00c94b4dd6": { - "balance": "1000000000000000000000" - }, - "7640a37f8052981515bce078da93afa4789b5734": { - "balance": "2000000000000000000000" - }, - "76cac488111a4fd595f568ae3a858770fc915d5f": { - "balance": "200000000000000000000" - }, - "ff4a408f50e9e72146a28ce4fc8d90271f116e84": { - "balance": "1970000000000000000000" - }, - "249db29dbc19d1235da7298a04081c315742e9ac": { - "balance": "1801800000000000000000" - }, - "3a04572847d31e81f7765ca5bfc9d557159f3683": { - "balance": "133031000000000000000" - }, - "b6771b0bf3427f9ae7a93e7c2e61ee63941fdb08": { - "balance": "18800000000000000000000" - }, - "30c26a8e971baa1855d633ba703f028cc7873140": { - "balance": "10000000000000000000000" - }, - "167e3e3ae2003348459392f7dfce44af7c21ad59": { - "balance": "500000000000000000000" - }, - "43f16f1e75c3c06a9478e8c597a40a3cb0bf04cc": { - "balance": "2914000000000000000000" - }, - "056b1546894f9a85e203fb336db569b16c25e04f": { - "balance": "169397000000000000000" - }, - "70616e2892fa269705b2046b8fe3e72fa55816d3": { - "balance": "20000000000000000000000" - }, - "8f4d1d41693e462cf982fd81d0aa701d3a5374c9": { - "balance": "4000000000000000000000" - }, - "c518799a5925576213e21896e0539abb85b05ae3": { - "balance": "1000000000000000000000" - }, - "0e3a28c1dfafb0505bdce19fe025f506a6d01ceb": { - "balance": "2000000000000000000000" - }, - "e4a47e3933246c3fd62979a1ea19ffdf8c72ef37": { - "balance": "148273000000000000000" - }, - "d231929735132102471ba59007b6644cc0c1de3e": { - "balance": "1000090000000000000000" - }, - "555d8d3ce1798aca902754f164b8be2a02329c6c": { - "balance": "10000000000000000000000" - }, - "5ab1a5615348001c7c775dc75748669b8be4de14": { - "balance": "690200000000000000000" - }, - "2fee36a49ee50ecf716f1047915646779f8ba03f": { - "balance": "1056230000000000000000" - }, - "54db5e06b4815d31cb56a8719ba33af2d73e7252": { - "balance": "670000000000000000000" - }, - "7c8bb65a6fbb49bd413396a9d7e31053bbb37aa9": { - "balance": "6000000000000000000000" - }, - "c1384c6e717ebe4b23014e51f31c9df7e4e25b31": { - "balance": "500000000000000000000" - }, - "474158a1a9dc693c133f65e47b5c3ae2f773a86f": { - "balance": "200200000000000000000" - }, - "2934c0df7bbc172b6c186b0b72547ace8bf75454": { - "balance": "60000000000000000000" - }, - "6966063aa5de1db5c671f3dd699d5abe213ee902": { - "balance": "8000000000000000000000" - }, - "9225d46a5a80943924a39e5b84b96da0ac450581": { - "balance": "40000000000000000000000" - }, - "671bbca099ff899bab07ea1cf86965c3054c8960": { - "balance": "50000000000000000000" - }, - "f1f766b0e46d73fcd4d52e7a72e1b9190cc632b3": { - "balance": "8000000000000000000000" - }, - "ef0dc7dd7a53d612728bcbd2b27c19dd4d7d666f": { - "balance": "705668000000000000000" - }, - "38d2e9154964b41c8d50a7487d391e7ee2c3d3c2": { - "balance": "3500000000000000000000" - }, - "352a785f4a921632504ce5d015f83c49aa838d6d": { - "balance": "4314800000000000000000" - }, - "743de50026ca67c94df54f066260e1d14acc11ac": { - "balance": "2000000000000000000000" - }, - "b188078444027e386798a8ae68698919d5cc230d": { - "balance": "267400000000000000000" - }, - "53608105ce4b9e11f86bf497ffca3b78967b5f96": { - "balance": "20000000000000000000000" - }, - "3b159099075207c6807663b1f0f7eda54ac8cce3": { - "balance": "1969543000000000000000" - }, - "141a5e39ee2f680a600fbf6fa297de90f3225cdd": { - "balance": "10000000000000000000000" - }, - "44fff37be01a3888d3b8b8e18880a7ddefeeead3": { - "balance": "259145000000000000000" - }, - "c5a629a3962552cb8eded889636aafbd0c18ce65": { - "balance": "10000000000000000000000" - }, - "fdba5359f7ec3bc770ac49975d844ec9716256f1": { - "balance": "1000000000000000000000" - }, - "7c1df24a4f7fb2c7b472e0bb006cb27dcd164156": { - "balance": "1000000000000000000000" - }, - "ab7d54c7c6570efca5b4b8ce70f52a5773e5d53b": { - "balance": "279600000000000000000" - }, - "3f173aa6edf469d185e59bd26ae4236b92b4d8e1": { - "balance": "320000000000000000000" - }, - "a3f4ad14e0bb44e2ce2c14359c75b8e732d37054": { - "balance": "200000000000000000000" - }, - "ac5f627231480d0d95302e6d89fc32cb1d4fe7e3": { - "balance": "200000000000000000000" - }, - "d0775dba2af4c30a3a78365939cd71c2f9de95d2": { - "balance": "1940000000000000000000" - }, - "ad94235fc3b3f47a2413af31e884914908ef0c45": { - "balance": "500008000000000000000" - }, - "eaedcc6b8b6962d5d9288c156c579d47c0a9fcff": { - "balance": "85000000000000000000" - }, - "7ac48d40c664cc9a6d89f1c5f5c80a1c70e744e6": { - "balance": "3008000000000000000000" - }, - "ec73114c5e406fdbbe09b4fa621bd70ed54ea1ef": { - "balance": "24500000000000000000000" - }, - "a690f1a4b20ab7ba34628620de9ca040c43c1963": { - "balance": "4000000000000000000000" - }, - "cad14f9ebba76680eb836b079c7f7baaf481ed6d": { - "balance": "238600000000000000000" - }, - "6c714a58fff6e97d14b8a5e305eb244065688bbd": { - "balance": "4000000000000000000000" - }, - "3e618350fa01657ab0ef3ebac8e37012f8fc2b6f": { - "balance": "2804400000000000000000" - }, - "c946d5acc1346eba0a7279a0ac1d465c996d827e": { - "balance": "16385128000000000000000" - }, - "1164caaa8cc5977afe1fad8a7d6028ce2d57299b": { - "balance": "400000000000000000000" - }, - "7917e5bd82a9790fd650d043cdd930f7799633db": { - "balance": "3999800000000000000000" - }, - "d52aecc6493938a28ca1c367b701c21598b6a02e": { - "balance": "1100000000000000000000" - }, - "98bed3a72eccfbafb923489293e429e703c7e25b": { - "balance": "2000000000000000000000" - }, - "42db0b902559e04087dd5c441bc7611934184b89": { - "balance": "2014420000000000000000" - }, - "43bc2d4ddcd6583be2c7bc094b28fb72e62ba83b": { - "balance": "2000000000000000000000" - }, - "85f0e7c1e3aff805a627a2aaf2cff6b4c0dbe9cb": { - "balance": "20000000000000000000" - }, - "581b9fd6eae372f3501f42eb9619eec820b78a84": { - "balance": "19699015000000000000000" - }, - "541db20a80cf3b17f1621f1b3ff79b882f50def3": { - "balance": "1000000000000000000000" - }, - "4e8a6d63489ccc10a57f885f96eb04ecbb546024": { - "balance": "18500000000000000000000" - }, - "28349f7ef974ea55fe36a1583b34cec3c45065f0": { - "balance": "234490000000000000000" - }, - "a3241d890a92baf52908dc4aa049726be426ebd3": { - "balance": "19999560000000000000000" - }, - "b4b11d109f608fa8edd3fea9f8c315649aeb3d11": { - "balance": "5000000000000000000000" - }, - "5f321b3daaa296cadf29439f9dab062a4bffedd6": { - "balance": "81868000000000000000" - }, - "c5ae86b0c6c7e3900f1368105c56537faf8d743e": { - "balance": "188000000000000000000" - }, - "9a8eca4189ff4aa8ff7ed4b6b7039f0902219b15": { - "balance": "20000000000000000000" - }, - "a3facc50195c0b4933c85897fecc5bbd995c34b8": { - "balance": "20000000000000000000" - }, - "f07bd0e5c2ce69c7c4a724bd26bbfa9d2a17ca03": { - "balance": "5910000000000000000000" - }, - "640aba6de984d94517377803705eaea7095f4a11": { - "balance": "10000000000000000000000" - }, - "204ac98867a7c9c7ed711cb82f28a878caf69b48": { - "balance": "6000000000000000000000" - }, - "9d34dac25bd15828faefaaf28f710753b39e89dc": { - "balance": "1090400000000000000000" - }, - "fe418b421a9c6d373602790475d2303e11a75930": { - "balance": "1015200000000000000000" - }, - "3f472963197883bbda5a9b7dfcb22db11440ad31": { - "balance": "481445000000000000000" - }, - "1578bdbc371b4d243845330556fff2d5ef4dff67": { - "balance": "100000000000000000000" - }, - "dba4796d0ceb4d3a836b84c96f910afc103f5ba0": { - "balance": "166666000000000000000" - }, - "466fda6b9b58c5532750306a10a2a8c768103b07": { - "balance": "199955000000000000000" - }, - "2770f14efb165ddeba79c10bb0af31c31e59334c": { - "balance": "3000000000000000000000" - }, - "7c382c0296612e4e97e440e02d3871273b55f53b": { - "balance": "197600000000000000000" - }, - "1fb7bd310d95f2a6d9baaf8a8a430a9a04453a8b": { - "balance": "3000000000000000000000" - }, - "a9acf600081bb55bb6bfbab1815ffc4e17e85a95": { - "balance": "200000000000000000000" - }, - "f93d5bcb0644b0cce5fcdda343f5168ffab2877d": { - "balance": "209978000000000000000" - }, - "db0cc78f74d9827bdc8a6473276eb84fdc976212": { - "balance": "2000000000000000000000" - }, - "b66411e3a02dedb726fa79107dc90bc1cae64d48": { - "balance": "2000000000000000000000" - }, - "4d6e8fe109ccd2158e4db114132fe75fecc8be5b": { - "balance": "25019000000000000000" - }, - "6fd947d5a73b175008ae6ee8228163da289b167d": { - "balance": "30000000000000000000000" - }, - "32d950d5e93ea1d5b48db4714f867b0320b31c0f": { - "balance": "1015200000000000000000" - }, - "9c99b62606281b5cefabf36156c8fe62839ef5f3": { - "balance": "4000000000000000000000" - }, - "86c8d0d982b539f48f9830f9891f9d607a942659": { - "balance": "13260000000000000000000" - }, - "f2127d54188fedef0f338a5f38c7ff73ad9f6f42": { - "balance": "20000000000000000000000" - }, - "e864fec07ed1214a65311e11e329de040d04f0fd": { - "balance": "1656353000000000000000" - }, - "1d09ad2412691cc581c1ab36b6f9434cd4f08b54": { - "balance": "7000000000000000000000" - }, - "4ea70f04313fae65c3ff224a055c3d2dab28dddf": { - "balance": "19999800000000000000000" - }, - "e0668fa82c14d6e8d93a53113ef2862fa81581bc": { - "balance": "870400000000000000000" - }, - "f0d858105e1b648101ac3f85a0f8222bf4f81d6a": { - "balance": "600000000000000000000" - }, - "0f3a1023cac04dbf44f5a5fa6a9cf8508cd4fddf": { - "balance": "1820000000000000000000" - }, - "5793abe6f1533311fd51536891783b3f9625ef1c": { - "balance": "827268000000000000000" - }, - "8d667637e29eca05b6bfbef1f96d460eefbf9984": { - "balance": "4000000000000000000000" - }, - "d76dbaebc30d4ef67b03e6e6ecc6d84e004d502d": { - "balance": "2019250000000000000000" - }, - "42d1a6399b3016a8597f8b640927b8afbce4b215": { - "balance": "2980000000000000000000" - }, - "21fd47c5256012198fa5abf131c06d6aa1965f75": { - "balance": "7880000000000000000000" - }, - "2f2bba1b1796821a766fce64b84f28ec68f15aea": { - "balance": "20000000000000000000" - }, - "d24bf12d2ddf457decb17874efde2052b65cbb49": { - "balance": "14000000000000000000000" - }, - "88de13b09931877c910d593165c364c8a1641bd3": { - "balance": "3000000000000000000000" - }, - "555ca9f05cc134ab54ae9bea1c3ff87aa85198ca": { - "balance": "100000000000000000000" - }, - "ae9ecd6bdd952ef497c0050ae0ab8a82a91898ce": { - "balance": "30000000000000000000" - }, - "ad8bfef8c68a4816b3916f35cb7bfcd7d3040976": { - "balance": "40000000000000000000000" - }, - "dad136b88178b4837a6c780feba226b98569a94c": { - "balance": "200000000000000000000" - }, - "800e7d631c6e573a90332f17f71f5fd19b528cb9": { - "balance": "152000000000000000000" - }, - "94a9a71691317c2064271b51c9353fbded3501a8": { - "balance": "3340000000000000000000" - }, - "80a0f6cc186cf6201400736e065a391f52a9df4a": { - "balance": "10000000000000000000000" - }, - "712ff7370a13ed360973fedc9ff5d2c93a505e9e": { - "balance": "3940000000000000000000" - }, - "42399659aca6a5a863ea2245c933fe9a35b7880e": { - "balance": "2044000000000000000000" - }, - "ae239acffd4ebe2e1ba5b4170572dc79cc6533ec": { - "balance": "12000000000000000000000" - }, - "007b9fc31905b4994b04c9e2cfdc5e2770503f42": { - "balance": "1999000000000000000000" - }, - "7480de62254f2ba82b578219c07ba5be430dc3cb": { - "balance": "7040000000000000000000" - }, - "917b8f9f3a8d09e9202c52c29e724196b897d35e": { - "balance": "161000000000000000000" - }, - "708ea707bae4357f1ebea959c3a250acd6aa21b3": { - "balance": "500000000000000000000" - }, - "6dc7053a718616cfc78bee6382ee51add0c70330": { - "balance": "2000000000000000000000" - }, - "c4dac5a8a0264fbc1055391c509cc3ee21a6e04c": { - "balance": "6501000000000000000000" - }, - "c1b2a0fb9cad45cd699192cd27540b88d3384279": { - "balance": "500000000000000000000" - }, - "b07cb9c12405b711807543c4934465f87f98bd2d": { - "balance": "2000000000000000000000" - }, - "c7f72bb758016b374714d4899bce22b4aec70a31": { - "balance": "1072706000000000000000" - }, - "0c480de9f7461002908b49f60fc61e2b62d3140b": { - "balance": "10000000000000000000000" - }, - "83d532d38d6dee3f60adc68b936133c7a2a1b0dd": { - "balance": "500000000000000000000" - }, - "12afbcba1427a6a39e7ba4849f7ab1c4358ac31b": { - "balance": "20000000000000000000000" - }, - "f8f6645e0dee644b3dad81d571ef9baf840021ad": { - "balance": "2000000000000000000000" - }, - "40cf890591eae4a18f812a2954cb295f633327e6": { - "balance": "48132000000000000000" - }, - "735b97f2fc1bd24b12076efaf3d1288073d20c8c": { - "balance": "20000000000000000000" - }, - "47c7e5efb48b3aed4b7c6e824b435f357df4c723": { - "balance": "18200000000000000000" - }, - "d34d708d7398024533a5a2b2309b19d3c55171bb": { - "balance": "400000000000000000000" - }, - "64370e87202645125a35b207af1231fb6072f9a7": { - "balance": "200000000000000000000" - }, - "b055af4cadfcfdb425cf65ba6431078f07ecd5ab": { - "balance": "100000000000000000000" - }, - "c7de5e8eafb5f62b1a0af2195cf793c7894c9268": { - "balance": "1000000000000000000000" - }, - "c63cd7882118b8a91e074d4c8f4ba91851303b9a": { - "balance": "260000000000000000000" - }, - "164d7aac3eecbaeca1ad5191b753f173fe12ec33": { - "balance": "744090000000000000000" - }, - "e4fb26d1ca1eecba3d8298d9d148119ac2bbf580": { - "balance": "400000000000000000000" - }, - "613ac53be565d46536b820715b9b8d3ae68a4b95": { - "balance": "3760000000000000000000" - }, - "7f616c6f008adfa082f34da7d0650460368075fb": { - "balance": "1000000000000000000000" - }, - "9af100cc3dae83a33402051ce4496b16615483f6": { - "balance": "2000000000000000000000" - }, - "b45cca0d36826662683cf7d0b2fdac687f02d0c4": { - "balance": "1000000000000000000000" - }, - "93a6b3ab423010f981a7489d4aad25e2625c5741": { - "balance": "20190033000000000000000" - }, - "ee049af005974dd1c7b3a9ca8d9aa77175ba53aa": { - "balance": "333333000000000000000" - }, - "687927e3048bb5162ae7c15cf76bd124f9497b9e": { - "balance": "2000000000000000000000" - }, - "1aa40270d21e5cde86b6316d1ac3c533494b79ed": { - "balance": "20000000000000000000" - }, - "426259b0a756701a8b663528522156c0288f0f24": { - "balance": "9900000000000000000000" - }, - "91c75e3cb4aa89f34619a164e2a47898f5674d9c": { - "balance": "2000000000000000000000" - }, - "437983388ab59a4ffc215f8e8269461029c3f1c1": { - "balance": "20000000000000000000000" - }, - "272a131a5a656a7a3aca35c8bd202222a7592258": { - "balance": "2674000000000000000000" - }, - "bc0ca4f217e052753614d6b019948824d0d8688b": { - "balance": "400000000000000000000" - }, - "cc6c03bd603e09de54e9c4d5ac6d41cbce715724": { - "balance": "98500000000000000000" - }, - "d79aff13ba2da75d46240cac0a2467c656949823": { - "balance": "1730000000000000000000" - }, - "477b24eee8839e4fd19d1250bd0b6645794a61ca": { - "balance": "8000000000000000000000" - }, - "79fd6d48315066c204f9651869c1096c14fc9781": { - "balance": "2000000000000000000000" - }, - "1463a873555bc0397e575c2471cf77fa9db146e0": { - "balance": "10000000000000000000000" - }, - "89ab13ee266d779c35e8bb04cd8a90cc2103a95b": { - "balance": "60000000000000000000000" - }, - "90acced7e48c08c6b934646dfa0adf29dc94074f": { - "balance": "56154000000000000000" - }, - "31ea6eab19d00764e9a95e183f2b1b22fc7dc40f": { - "balance": "20000000000000000000" - }, - "87a53ea39f59a35bada8352521645594a1a714cb": { - "balance": "1910000000000000000000" - }, - "1e1aed85b86c6562cb8fa1eb6f8f3bc9dcae6e79": { - "balance": "4516200000000000000000" - }, - "e36a8ea87f1e99e8a2dc1b2608d166667c9dfa01": { - "balance": "100000000000000000000" - }, - "ec2cb8b9378dff31aec3c22e0e6dadff314ab5dd": { - "balance": "2000000000000000000000" - }, - "3cadeb3d3eed3f62311d52553e70df4afce56f23": { - "balance": "4000000000000000000000" - }, - "3ceca96bb1cdc214029cbc5e181d398ab94d3d41": { - "balance": "80000000000000000000000" - }, - "3283eb7f9137dd39bed55ffe6b8dc845f3e1a079": { - "balance": "66224000000000000000" - }, - "0954a8cb5d321fc3351a7523a617d0f58da676a7": { - "balance": "2506000000000000000000" - }, - "de33d708a3b89e909eaf653b30fdc3a5d5ccb4b3": { - "balance": "177300000000000000000" - }, - "1c6702b3b05a5114bdbcaeca25531aeeb34835f4": { - "balance": "26071500000000000000000" - }, - "e5b96fc9ac03d448c1613ac91d15978145dbdfd1": { - "balance": "200000000000000000000" - }, - "fbf204c813f836d83962c7870c7808ca347fd33e": { - "balance": "20000000000000000000" - }, - "3b13631a1b89cb566548899a1d60915cdcc4205b": { - "balance": "2000000000000000000000" - }, - "a87f7abd6fa31194289678efb63cf584ee5e2a61": { - "balance": "4000000000000000000000" - }, - "c0a39308a80e9e84aaaf16ac01e3b01d74bd6b2d": { - "balance": "136499000000000000000" - }, - "ffd6da958eecbc016bab91058440d39b41c7be83": { - "balance": "20000000000000000000000" - }, - "0e3dd7d4e429fe3930a6414035f52bdc599d784d": { - "balance": "40110000000000000000" - }, - "e0663e8cd66792a641f56e5003660147880f018e": { - "balance": "2000000000000000000000" - }, - "5b78eca27fbdea6f26befba8972b295e7814364b": { - "balance": "2000000000000000000000" - }, - "ec9851bd917270610267d60518b54d3ca2b35b17": { - "balance": "40000000000000000000000" - }, - "bc9c95dfab97a574cea2aa803b5caa197cef0cff": { - "balance": "420000000000000000000" - }, - "100b4d0977fcbad4debd5e64a0497aeae5168fab": { - "balance": "314500000000000000000" - }, - "1b6610fb68bad6ed1cfaa0bbe33a24eb2e96fafb": { - "balance": "152000000000000000000" - }, - "b4524c95a7860e21840296a616244019421c4aba": { - "balance": "8000000000000000000000" - }, - "88975a5f1ef2528c300b83c0c607b8e87dd69315": { - "balance": "83500000000000000000" - }, - "853e6abaf44469c72f151d4e223819aced4e3728": { - "balance": "2000000000000000000000" - }, - "d604abce4330842e3d396ca73ddb5519ed3ec03f": { - "balance": "163940000000000000000" - }, - "d209482bb549abc4777bea6d7f650062c9c57a1c": { - "balance": "320880000000000000000" - }, - "590acbda37290c0d3ec84fc2000d7697f9a4b15d": { - "balance": "500000000000000000000" - }, - "571950ea2c90c1427d939d61b4f2de4cf1cfbfb0": { - "balance": "20000000000000000000" - }, - "cb94e76febe208116733e76e805d48d112ec9fca": { - "balance": "1000000000000000000000" - }, - "fa8e3b1f13433900737daaf1f6299c4887f85b5f": { - "balance": "715000000000000000000" - }, - "162d76c2e6514a3afb6fe3d3cb93a35c5ae783f1": { - "balance": "2000000000000000000000" - }, - "4bea288eea42c4955eb9faad2a9faf4783cbddac": { - "balance": "28790618000000000000000" - }, - "c8ab1a3cf46cb8b064df2e222d39607394203277": { - "balance": "2000000000000000000000" - }, - "318b2ea5f0aaa879c4d5e548ac9d92a0c67487b7": { - "balance": "200000000000000000000" - }, - "53c5fe0119e1e848640cee30adea96940f2a5d8b": { - "balance": "21746000000000000000000" - }, - "0701f9f147ec486856f5e1b71de9f117e99e2105": { - "balance": "173360000000000000000" - }, - "337cfe1157a5c6912010dd561533791769c2b6a6": { - "balance": "1000000000000000000000" - }, - "fd60d2b5af3d35f7aaf0c393052e79c4d823d985": { - "balance": "56400000000000000000" - }, - "0f049a8bdfd761de8ec02cee2829c4005b23c06b": { - "balance": "252000000000000000000" - }, - "924bce7a853c970bb5ec7bb759baeb9c7410857b": { - "balance": "13700000000000000000" - }, - "16abb8b021a710bdc78ea53494b20614ff4eafe8": { - "balance": "158000000000000000000" - }, - "9e7f65a90e8508867bccc914256a1ea574cf07e3": { - "balance": "1240000000000000000000" - }, - "01d03815c61f416b71a2610a2daba59ff6a6de5b": { - "balance": "9553100000000000000000" - }, - "3df762049eda8ac6927d904c7af42f94e5519601": { - "balance": "2000000000000000000000" - }, - "5593c9d4b664730fd93ca60151c25c2eaed93c3b": { - "balance": "200000000000000000000" - }, - "e023f09b2887612c7c9cf1988e3a3a602b3394c9": { - "balance": "2000000000000000000000" - }, - "4c13980c32dcf3920b78a4a7903312907c1b123f": { - "balance": "60024000000000000000" - }, - "a282e969cac9f7a0e1c0cd90f5d0c438ac570da3": { - "balance": "627760000000000000000" - }, - "3b22da2a0271c8efe102532773636a69b1c17e09": { - "balance": "502000000000000000000" - }, - "1aa1021f550af158c747668dd13b463160f95a40": { - "balance": "1470000000000000000000" - }, - "f15178ffc43aa8070ece327e930f809ab1a54f9d": { - "balance": "197600000000000000000" - }, - "db1293a506e90cad2a59e1b8561f5e66961a6788": { - "balance": "2000000000000000000000" - }, - "88c361640d6b69373b081ce0c433bd590287d5ec": { - "balance": "50000000000000000000000" - }, - "3737216ee91f177732fb58fa4097267207e2cf55": { - "balance": "1520000000000000000000" - }, - "a16d9e3d63986159a800b46837f45e8bb980ee0b": { - "balance": "2030400000000000000000" - }, - "ec76f12e57a65504033f2c0bce6fc03bd7fa0ac4": { - "balance": "3580000000000000000000" - }, - "d9f1b26408f0ec67ad1d0d6fe22e8515e1740624": { - "balance": "24000000000000000000" - }, - "716ba01ead2a91270635f95f25bfaf2dd610ca23": { - "balance": "44750000000000000000000" - }, - "42a98bf16027ce589c4ed2c95831e2724205064e": { - "balance": "10000000000000000000000" - }, - "0f88aac9346cb0e7347fba70905475ba8b3e5ece": { - "balance": "10000000000000000000000" - }, - "2d8c52329f38d2a2fa9cbaf5c583daf1490bb11c": { - "balance": "20000000000000000000" - }, - "3cea302a472a940379dd398a24eafdbadf88ad79": { - "balance": "3000000000000000000000" - }, - "a29d5bda74e003474872bd5894b88533ff64c2b5": { - "balance": "10000000000000000000000" - }, - "2d23766b6f6b05737dad80a419c40eda4d77103e": { - "balance": "3820000000000000000000" - }, - "b07249e055044a9155359a402937bbd954fe48b6": { - "balance": "100000000000000000000" - }, - "f1e980c559a1a8e5e50a47f8fffdc773b7e06a54": { - "balance": "30104784000000000000000" - }, - "8275cd684c3679d5887d03664e338345dc3cdde1": { - "balance": "15800000000000000000" - }, - "b27c1a24204c1e118d75149dd109311e07c073ab": { - "balance": "3100000000000000000000" - }, - "451b3699475bed5d7905f8905aa3456f1ed788fc": { - "balance": "2560000000000000000000" - }, - "31ad4d9946ef09d8e988d946b1227f9141901736": { - "balance": "22880000000000000000000" - }, - "52b8a9592634f7300b7c5c59a3345b835f01b95c": { - "balance": "2000000000000000000000" - }, - "b161725fdcedd17952d57b23ef285b7e4b1169e8": { - "balance": "50071000000000000000" - }, - "74fc5a99c0c5460503a13b0509459da19ce7cd90": { - "balance": "200000000000000000000" - }, - "d99df7421b9382e42c89b006c7f087702a0757c0": { - "balance": "480000000000000000000" - }, - "8a4f4a7f52a355ba105fca2072d3065fc8f7944b": { - "balance": "500000000000000000000" - }, - "12316fc7f178eac22eb2b25aedeadf3d75d00177": { - "balance": "19999999000000000000000" - }, - "f598db2e09a8a5ee7d720d2b5c43bb126d11ecc2": { - "balance": "200000000000000000000" - }, - "37b8beac7b1ca38829d61ab552c766f48a10c32f": { - "balance": "400000000000000000000" - }, - "851dc38adb4593729a76f33a8616dab6f5f59a77": { - "balance": "100000000000000000000" - }, - "bf4096bc547dbfc4e74809a31c039e7b389d5e17": { - "balance": "3940000000000000000000" - }, - "98d3731992d1d40e1211c7f735f2189afa0702e0": { - "balance": "8000000000000000000000" - }, - "0f4073c1b99df60a1549d69789c7318d9403a814": { - "balance": "20000000000000000000000" - }, - "a430995ddb185b9865dbe62539ad90d22e4b73c2": { - "balance": "10000000000000000000000" - }, - "898c72dd736558ef9e4be9fdc34fef54d7fc7e08": { - "balance": "1000000000000000000000" - }, - "f9b617f752edecae3e909fbb911d2f8192f84209": { - "balance": "2674000000000000000000" - }, - "e1ae029b17e373cde3de5a9152201a14cac4e119": { - "balance": "99968000000000000000" - }, - "d8e8474292e7a051604ca164c0707783bb2885e8": { - "balance": "13370000000000000000000" - }, - "f476f2cb7208a32e051fd94ea8662992638287a2": { - "balance": "100000000000000000000" - }, - "3a84e950ed410e51b7e8801049ab2634b285fea1": { - "balance": "18690000000000000000000" - }, - "5b7784caea01799ca30227827667ce207c5cbc76": { - "balance": "2000000000000000000000" - }, - "3af65b3e28895a4a001153391d1e69c31fb9db39": { - "balance": "3940000000000000000000" - }, - "95fb5afb14c1ef9ab7d179c5c300503fd66a5ee2": { - "balance": "34225000000000000000" - }, - "a8446c4781a737ac4328b1e15b8a0b3fbb0fd668": { - "balance": "21390500000000000000000" - }, - "4888fb25cd50dbb9e048f41ca47d78b78a27c7d9": { - "balance": "17300000000000000000000" - }, - "566c10d638e8b88b47d6e6a414497afdd00600d4": { - "balance": "99960000000000000000" - }, - "bd47f5f76e3b930fd9485209efa0d4763da07568": { - "balance": "1000000000000000000000" - }, - "1e1c6351776ac31091397ecf16002d979a1b2d51": { - "balance": "1400000000000000000000" - }, - "edf603890228d7d5de9309942b5cad4219ef9ad7": { - "balance": "5000000000000000000000" - }, - "1923cfc68b13ea7e2055803645c1e320156bd88d": { - "balance": "1337000000000000000000" - }, - "8f8f37d0ad8f335d2a7101b41156b688a81a9cbe": { - "balance": "70000000000000000000" - }, - "63334fcf1745840e4b094a3bb40bb76f9604c04c": { - "balance": "3978000000000000000000" - }, - "001762430ea9c3a26e5749afdb70da5f78ddbb8c": { - "balance": "200000000000000000000" - }, - "512116817ba9aaf843d1507c65a5ea640a7b9eec": { - "balance": "50000000000000000000" - }, - "2961fb391c61957cb5c9e407dda29338d3b92c80": { - "balance": "999942000000000000000" - }, - "fc2952b4c49fedd0bc0528a308495e6d6a1c71d6": { - "balance": "2000000000000000000000" - }, - "13ec812284026e409bc066dfebf9d5a4a2bf801e": { - "balance": "1610000000000000000000" - }, - "ef463c2679fb279164e20c3d2691358773a0ad95": { - "balance": "2000000000000000000000" - }, - "3aadf98b61e5c896e7d100a3391d3250225d61df": { - "balance": "234000000000000000000" - }, - "e8137fc1b2ec7cc7103af921899b4a39e1d959a1": { - "balance": "1490000000000000000000" - }, - "b1a2b43a7433dd150bb82227ed519cd6b142d382": { - "balance": "2738000000000000000000" - }, - "c1f39bd35dd9cec337b96f47c677818160df37b7": { - "balance": "20000000000000000000" - }, - "b587b44a2ca79e4bc1dd8bfdd43a207150f2e7e0": { - "balance": "630400000000000000000" - }, - "41485612d03446ec4c05e5244e563f1cbae0f197": { - "balance": "970000000000000000000" - }, - "a12623e629df93096704b16084be2cd89d562da4": { - "balance": "8500000000000000000000" - }, - "3f2f381491797cc5c0d48296c14fd0cd00cdfa2d": { - "balance": "804000000000000000000" - }, - "9470cc36594586821821c5c996b6edc83b6d5a32": { - "balance": "24000000000000000000" - }, - "3605372d93a9010988018f9f315d032ed1880fa1": { - "balance": "500066000000000000000" - }, - "12632388b2765ee4452b50161d1fffd91ab81f4a": { - "balance": "740000000000000000000" - }, - "274a3d771a3d709796fbc4d5f48fce2fe38c79d6": { - "balance": "20000000000000000000" - }, - "d60a52580728520df7546bc1e283291788dbae0c": { - "balance": "999910000000000000000" - }, - "1ab53a11bcc63ddfaa40a02b9e186496cdbb8aff": { - "balance": "1996800000000000000000" - }, - "c282e6993fbe7a912ea047153ffd9274270e285b": { - "balance": "139939000000000000000" - }, - "a291e9c7990d552dd1ae16cebc3fca342cbaf1d1": { - "balance": "20000000000000000000000" - }, - "5547fdb4ae11953e01292b7807fa9223d0e4606a": { - "balance": "98940000000000000000" - }, - "bded11612fb5c6da99d1e30e320bc0995466141e": { - "balance": "400000000000000000000" - }, - "b73b4ff99eb88fd89b0b6d57a9bc338e886fa06a": { - "balance": "32000000000000000000" - }, - "b1c751786939bba0d671a677a158c6abe7265e46": { - "balance": "10000000000000000000000" - }, - "e881bbbe69722d81efecaa48d1952a10a2bfac8f": { - "balance": "16000000000000000000000" - }, - "fe96c4cd381562401aa32a86e65b9d52fa8aee27": { - "balance": "2640000000000000000000" - }, - "683dba36f7e94f40ea6aea0d79b8f521de55076e": { - "balance": "140000000000000000000" - }, - "5ac2908b0f398c0df5bac2cb13ca7314fba8fa3d": { - "balance": "199800000000000000000" - }, - "8914a680a5aec5226d4baaec2e5552b44dd7c874": { - "balance": "100076000000000000000" - }, - "041170f581de80e58b2a045c8f7c1493b001b7cb": { - "balance": "889800000000000000000" - }, - "4665e47396c7db97eb2a03d90863d5d4ba319a94": { - "balance": "600000000000000000000" - }, - "ed4be04a052d7accb3dcce90319dba4020ab2c68": { - "balance": "37547947000000000000000" - }, - "4b0619d9d8aa313a9531ac7dbe04ca0d6a5ad1b6": { - "balance": "2000000000000000000000" - }, - "a21442ab05340ade68c915f3c3399b9955f3f7eb": { - "balance": "775000000000000000000" - }, - "655934da8e744eaa3de34dbbc0894c4eda0b61f2": { - "balance": "200000000000000000000" - }, - "6038740ae28d66ba93b0be08482b3205a0f7a07b": { - "balance": "316000000000000000000" - }, - "99924a9816bb7ddf3fec1844828e9ad7d06bf4e6": { - "balance": "1760000000000000000000" - }, - "6847825bdee8240e28042c83cad642f286a3bddc": { - "balance": "1500000000000000000000" - }, - "a718aaad59bf395cba2b23e09b02fe0c89816247": { - "balance": "999600000000000000000" - }, - "2c89f5fdca3d155409b638b98a742e55eb4652b7": { - "balance": "98500000000000000000000" - }, - "1a7044e2383f8708305b495bd1176b92e7ef043a": { - "balance": "200000000000000000000" - }, - "282e80a554875a56799fa0a97f5510e795974c4e": { - "balance": "1000000000000000000000" - }, - "ffb3bcc3196a8c3cb834cec94c34fed35b3e1054": { - "balance": "1340000000000000000000" - }, - "d135794b149a18e147d16e621a6931f0a40a969a": { - "balance": "20000000000000000000000" - }, - "6b94615db750656ac38c7e1cf29a9d13677f4e15": { - "balance": "12000000000000000000000" - }, - "ecbe425e670d39094e20fb5643a9d818eed236de": { - "balance": "5000000000000000000000" - }, - "511e0efb04ac4e3ff2e6550e498295bfcd56ffd5": { - "balance": "668500000000000000000" - }, - "ff65511cada259260c1ddc41974ecaecd32d6357": { - "balance": "1760000000000000000000" - }, - "9ffc5fe06f33f5a480b75aa94eb8556d997a16c0": { - "balance": "20000000000000000000" - }, - "57df23bebdc65eb75feb9cb2fad1c073692b2baf": { - "balance": "4000000000000000000000" - }, - "207ef80b5d60b6fbffc51f3a64b8c72036a5abbd": { - "balance": "6685000000000000000000" - }, - "c573e841fa08174a208b060ccb7b4c0d7697127f": { - "balance": "668500000000000000000" - }, - "411610b178d5617dfab934d293f512a93e5c10e1": { - "balance": "170000000000000000000" - }, - "9991614c5baa47dd6c96874645f97add2c3d8380": { - "balance": "1970000000000000000000" - }, - "2d3480bf0865074a72c7759ee5137b4d70c51ce9": { - "balance": "200000000000000000000" - }, - "9d40e012f60425a340d82d03a1c757bfabc706fb": { - "balance": "169799000000000000000" - }, - "47648bed01f3cd3249084e635d14daa9e7ec3c8a": { - "balance": "194000000000000000000" - }, - "a5ff62222d80c013cec1a0e8850ed4d354dac16d": { - "balance": "207600000000000000000" - }, - "f80d3619702fa5838c48391859a839fb9ce7160f": { - "balance": "1992800000000000000000" - }, - "7c0f5e072043c9ee740242197e78cc4b98cdf960": { - "balance": "200000000000000000000" - }, - "a40aa2bbce0c72b4d0dfffcc42715b2b54b01bfa": { - "balance": "1000000000000000000000" - }, - "2eeed50471a1a2bf53ee30b1232e6e9d80ef866d": { - "balance": "20000000000000000000" - }, - "0c2808b951ed9e872d7b32790fcc5994ae41ffdc": { - "balance": "102000000000000000000000" - }, - "7f06c89d59807fa60bc60136fcf814cbaf2543bd": { - "balance": "10000000000000000000000" - }, - "8d4b603c5dd4570c34669515fdcc665890840c77": { - "balance": "18200000000000000000" - }, - "d5e5c135d0c4c3303934711993d0d16ff9e7baa0": { - "balance": "2000000000000000000000" - }, - "241361559feef80ef137302153bd9ed2f25db3ef": { - "balance": "20000000000000000000000" - }, - "db63122de7037da4971531fae9af85867886c692": { - "balance": "277000000000000000000" - }, - "417e4e2688b1fd66d821529e46ed4f42f8b3db3d": { - "balance": "2000000000000000000000" - }, - "127db1cadf1b771cbd7475e1b272690f558c8565": { - "balance": "14000000000000000000000" - }, - "48659d8f8c9a2fd44f68daa55d23a608fbe500dc": { - "balance": "2000000000000000000000" - }, - "b3a64b1176724f5409e1414a3523661baee74b4a": { - "balance": "25610000000000000000" - }, - "aa14422d6f0ae5a758194ed15780c838d67f1ee1": { - "balance": "28503824000000000000000" - }, - "a0a0e65204541fca9b2fb282cd95138fae16f809": { - "balance": "10000000000000000000000" - }, - "d2107b353726c3a2b46566eaa7d9f80b5d21dbe3": { - "balance": "20000000000000000000" - }, - "e4cafb727fb5c6b70bb27533b8a9ccc9ef6888e1": { - "balance": "300443000000000000000" - }, - "09f3f601f605441140586ce0656fa24aa5b1d9ae": { - "balance": "1539400000000000000000" - }, - "87fcbe7c4193ffcb08143779c9bec83fe7fda9fc": { - "balance": "100275000000000000000" - }, - "03ebc63fda6660a465045e235fbe6e5cf195735f": { - "balance": "141840000000000000000" - }, - "bdbaf6434d40d6355b1e80e40cc4ab9c68d96116": { - "balance": "100000000000000000000" - }, - "4e2225a1bb59bc88a2316674d333b9b0afca6655": { - "balance": "155000000000000000000" - }, - "4dc3da13b2b4afd44f5d0d3189f444d4ddf91b1b": { - "balance": "2000000000000000000000" - }, - "4ba8e0117fc0b6a3e56b24a3a58fe6cef442ff98": { - "balance": "5640000000000000000000" - }, - "27146913563aa745e2588430d9348e86ea7c3510": { - "balance": "400000000000000000000" - }, - "4c5afe40f18ffc48d3a1aec41fc29de179f4d297": { - "balance": "2000000000000000000000" - }, - "8a810114b2025db9fbb50099a6e0cb9e2efa6bdc": { - "balance": "1910000000000000000000" - }, - "2dee90a28f192d676a8773232b56f18f239e2fad": { - "balance": "18587970000000000000000" - }, - "60676e92d18b000509c61de540e6c5ddb676d509": { - "balance": "1200000000000000000000" - }, - "9bfc659c9c601ea42a6b21b8f17084ec87d70212": { - "balance": "10000000000000000000000" - }, - "5d5d6e821c6eef96810c83c491468560ef70bfb5": { - "balance": "2000000000000000000000" - }, - "d5787668c2c5175b01a8ee1ac3ecc9c8b2aba95a": { - "balance": "1999944000000000000000" - }, - "33b336f5ba5edb7b1ccc7eb1a0d984c1231d0edc": { - "balance": "2000000000000000000000" - }, - "3abb8adfc604f48d5984811d7f1d52fef6758270": { - "balance": "4475000000000000000000" - }, - "980a84b686fc31bdc83c221058546a71b11f838a": { - "balance": "779471000000000000000" - }, - "0b507cf553568daaf65504ae4eaa17a8ea3cdbf5": { - "balance": "2000000000000000000000" - }, - "896009526a2c7b0c09a6f63a80bdf29d9c87de9c": { - "balance": "3462830000000000000000" - }, - "9696052138338c722f1140815cf7749d0d3b3a74": { - "balance": "500000000000000000000" - }, - "3831757eae7557cb8a37a4b10644b63e4d3b3c75": { - "balance": "200000000000000000000" - }, - "62dc72729024375fc37cbb9c7c2393d10233330f": { - "balance": "2000000000000000000000" - }, - "44098866a69b68c0b6bc168229b9603587058967": { - "balance": "188000000000000000000" - }, - "25adb8f96f39492c9bb47c5edc88624e46075697": { - "balance": "26740000000000000000000" - }, - "fd4de8e3748a289cf7d060517d9d38388db01fb8": { - "balance": "250000000000000000000" - }, - "6be7595ea0f068489a2701ec4649158ddc43e178": { - "balance": "2000000000000000000000" - }, - "d402b4f6a099ebe716cb14df4f79c0cd01c6071b": { - "balance": "2000000000000000000000" - }, - "a07682000b1bcf3002f85c80c0fa2949bd1e82fd": { - "balance": "4000000000000000000000" - }, - "eb4f00e28336ea09942588eeac921811c522143c": { - "balance": "2000000000000000000000" - }, - "8f31c7005197ec997a87e69bec48649ab94bb2a5": { - "balance": "4000000000000000000000" - }, - "e7fd8fd959aed2767ea7fa960ce1db53af802573": { - "balance": "1000000000000000000000" - }, - "a8ef9ad274436042903e413c3b0c62f5f52ed584": { - "balance": "10000000000000000000000" - }, - "d83ad260e9a6f432fb6ea28743299b4a09ad658c": { - "balance": "2000000000000000000000" - }, - "b5c816a8283ca4df68a1a73d63bd80260488df08": { - "balance": "200000000000000000000" - }, - "d7d3c75920590438b82c3e9515be2eb6ed7a8b1a": { - "balance": "60000000000000000000000" - }, - "af3cb5965933e7dad883693b9c3e15beb68a4873": { - "balance": "2000000000000000000000" - }, - "6e899e59a9b41ab7ea41df7517860f2acb59f4fd": { - "balance": "20000000000000000000000" - }, - "527a8ca1268633a6c939c5de1b929aee92aeac8d": { - "balance": "900000000000000000000" - }, - "1680cec5021ee93050f8ae127251839e74c1f1fd": { - "balance": "13098657000000000000000" - }, - "ff7843c7010aa7e61519b762dfe49124a76b0e4e": { - "balance": "933580000000000000000000" - }, - "140fba58dbc04803d84c2130f01978f9e0c73129": { - "balance": "400000000000000000000" - }, - "0261ad3a172abf1315f0ffec3270986a8409cb25": { - "balance": "203500000000000000000" - }, - "ab5a79016176320973e8cd38f6375530022531c0": { - "balance": "1000000000000000000000" - }, - "fca73eff8771c0103ba3cc1a9c259448c72abf0b": { - "balance": "1000000000000000000000" - }, - "07d41217badca5e0e60327d845a3464f0f27f84a": { - "balance": "4000000000000000000000" - }, - "2c1c19114e3d6de27851484b8d2715e50f8a1065": { - "balance": "100000000000000000000" - }, - "abd21eff954fc6a7de26912a7cbb303a6607804e": { - "balance": "1517000000000000000000" - }, - "f303d5a816affd97e83d9e4dac2f79072bb0098f": { - "balance": "960000000000000000000" - }, - "114cfefe50170dd97ae08f0a44544978c599548d": { - "balance": "863000000000000000000" - }, - "647b85044df2cf0b4ed4882e88819fe22ae5f793": { - "balance": "1000032000000000000000" - }, - "1b130d6fa51d5c48ec8d1d52dc8a227be8735c8a": { - "balance": "2000000000000000000000" - }, - "0d9d3f9bc4a4c6efbd59679b69826bc1f63d9916": { - "balance": "600000000000000000000" - }, - "c765e00476810947816af142d46d2ee7bca8cc4f": { - "balance": "500000000000000000000" - }, - "b57b04fa23d1203fae061eac4542cb60f3a57637": { - "balance": "191000000000000000000" - }, - "e192489b85a982c1883246d915b229cb13207f38": { - "balance": "5000000000000000000000" - }, - "5f483ffb8f680aedf2a38f7833afdcde59b61e4b": { - "balance": "2000000000000000000000" - }, - "b46d1182e5aacaff0d26b2fcf72f3c9ffbcdd97d": { - "balance": "3139000000000000000000" - }, - "59c7f785c93160e5807ed34e5e534bc6188647a7": { - "balance": "640000000000000000000" - }, - "18e4ce47483b53040adbab35172c01ef64506e0c": { - "balance": "9000000000000000000000" - }, - "296d66b521571a4e4103a7f562c511e6aa732d81": { - "balance": "668500000000000000000" - }, - "bcd99edc2160f210a05e3a1fa0b0434ced00439b": { - "balance": "2000000000000000000000" - }, - "f14f0eb86db0eb68753f16918e5d4b807437bd3e": { - "balance": "200000000000000000000" - }, - "60d5667140d12614b21c8e5e8a33082e32dfcf23": { - "balance": "20000000000000000000000" - }, - "8ccabf25077f3aa41545344d53be1b2b9c339000": { - "balance": "1695400000000000000000" - }, - "8cc0d7c016fa7aa950114aa1db094882eda274ea": { - "balance": "159800000000000000000" - }, - "c71145e529c7a714e67903ee6206e4c3042b6727": { - "balance": "1430000000000000000000" - }, - "c5e9939334f1252ed2ba26814487dfd2982b3128": { - "balance": "70000000000000000000" - }, - "f09b3e87f913ddfd57ae8049c731dba9b636dfc3": { - "balance": "608000000000000000000" - }, - "4349225a62f70aea480a029915a01e5379e64fa5": { - "balance": "2598000000000000000000" - }, - "666b4f37d55d63b7d056b615bb74c96b3b01991a": { - "balance": "4000000000000000000000" - }, - "8bd6b1c6d74d010d1008dba6ef835d4430b35c32": { - "balance": "50000000000000000000" - }, - "7363cd90fbab5bb8c49ac20fc62c398fe6fb744c": { - "balance": "2000000000000000000000" - }, - "b7479dab5022c4d5dbaaf8de171b4e951dd1a457": { - "balance": "80000000000000000000" - }, - "5a5468fa5ca226c7532ecf06e1bc1c45225d7ec9": { - "balance": "1910000000000000000000" - }, - "32a20d028e2c6218b9d95b445c771524636a22ef": { - "balance": "9500000000000000000000" - }, - "1bd28cd5c78aee51357c95c1ef9235e7c18bc854": { - "balance": "2000000000000000000000" - }, - "693492a5c51396a482881669ccf6d8d779f00951": { - "balance": "345827000000000000000" - }, - "bd723b289a7367b6ece2455ed61edb49670ab9c4": { - "balance": "4999995000000000000000" - }, - "1be3542c3613687465f15a70aeeb81662b65cca8": { - "balance": "2000000000000000000000" - }, - "5803e68b34da121aef08b602badbafb4d12481ca": { - "balance": "18000000000000000000000" - }, - "9ac907ee85e6f3e223459992e256a43fa08fa8b2": { - "balance": "10000000000000000000000" - }, - "833b6a8ec8da408186ac8a7d2a6dd61523e7ce84": { - "balance": "16000000000000000000000" - }, - "64628c6fb8ec743adbd87ce5e018d531d9210437": { - "balance": "26740000000000000000" - }, - "566c28e34c3808d9766fe8421ebf4f2b1c4f7d77": { - "balance": "1970000000000000000000" - }, - "171ad9a04bedc8b861e8ed4bddf5717813b1bb48": { - "balance": "400000000000000000000" - }, - "4f85bc1fc5cbc9c001e8f1372e07505370d8c71f": { - "balance": "940000000000000000000" - }, - "6d2f976734b9d0070d1883cf7acab8b3e4920fc1": { - "balance": "10000000000000000000000" - }, - "357a02c0a9dfe287de447fb67a70ec5b62366647": { - "balance": "26740000000000000000" - }, - "44a01fb04ac0db2cce5dbe281e1c46e28b39d878": { - "balance": "1999944000000000000000" - }, - "3630c5e565ceaa8a0f0ffe32875eae2a6ce63c19": { - "balance": "170016000000000000000" - }, - "334340ee4b9cdc81f850a75116d50ee9b69825bf": { - "balance": "2000000000000000000000" - }, - "c0afb7d8b79370cfd663c68cc6b9702a37cd9eff": { - "balance": "1000000000000000000000" - }, - "2016895df32c8ed5478269468423aea7b7fbce50": { - "balance": "20000000000000000000" - }, - "1e2fe4e4a77d141ff49a0c7fbc95b0a2b283eeeb": { - "balance": "2000000000000000000000" - }, - "260df8943a8c9a5dba7945327fd7e0837c11ad07": { - "balance": "200000000000000000000" - }, - "32fbeed6f626fcdfd51acafb730b9eeff612f564": { - "balance": "2000000000000000000000" - }, - "9bd88068e13075f3a8cac464a5f949d6d818c0f6": { - "balance": "6000000000000000000000" - }, - "ab4572fbb1d72b575d69ec6ad17333873e8552fc": { - "balance": "1999942000000000000000" - }, - "e44ea51063405154aae736be2bf1ee3b9be639ae": { - "balance": "4000000000000000000000" - }, - "617f20894fa70e94a86a49cd74e03238f64d3cd9": { - "balance": "5000057000000000000000" - }, - "3e914e3018ac00449341c49da71d04dfeeed6221": { - "balance": "4000000000000000000000" - }, - "590181d445007bd0875aaf061c8d51153900836a": { - "balance": "2000000000000000000000" - }, - "27987110221a880826adb2e7ab5eca78c6e31aec": { - "balance": "4000000000000000000000" - }, - "06618e9d5762df62028601a81d4487d6a0ecb80e": { - "balance": "1337000000000000000000" - }, - "8cc652dd13e7fe14dabbb36d5d320db9ffee8a54": { - "balance": "1790000000000000000000" - }, - "8973aefd5efaee96095d9e288f6a046c97374b43": { - "balance": "141000000000000000000" - }, - "dbd51cdf2c3bfacdff106221de2e19ad6d420414": { - "balance": "1760000000000000000000" - }, - "25697ef20cccaa70d32d376f8272d9c1070c3d78": { - "balance": "200000000000000000000" - }, - "0726c42e00f45404836eb1e280d073e7059687f5": { - "balance": "1623331000000000000000" - }, - "5e0785532c7723e4c0af9357d5274b73bdddddde": { - "balance": "25000088000000000000000" - }, - "38430e931d93be01b4c3ef0dc535f1e0a9610063": { - "balance": "10000000000000000000000" - }, - "143d536b8b1cb84f56a39e0bc81fd5442bcacce1": { - "balance": "100000000000000000000" - }, - "5c6d041da7af4487b9dc48e8e1f60766d0a56dbc": { - "balance": "1457800000000000000000" - }, - "f9bfb59d538afc4874d4f5941b08c9730e38e24b": { - "balance": "40000000000000000000" - }, - "83dbfd8eda01d0de8e158b16d0935fc2380a5dc7": { - "balance": "600000000000000000000" - }, - "0e6cd664ad9c1ed64bf98749f40644b626e3792c": { - "balance": "60000000000000000000000" - }, - "ce2e0da8934699bb1a553e55a0b85c169435bea3": { - "balance": "4999962000000000000000" - }, - "a39bfee4aec9bd75bd22c6b672898ca9a1e95d32": { - "balance": "10000000000000000000000" - }, - "1bc44c8761231ba1f11f5faa40fa669a013e12ce": { - "balance": "203586000000000000000" - }, - "68809af5d532a11c1a4d6e32aac75c4c52b08ead": { - "balance": "10000000000000000000000" - }, - "80cc21bd99f39005c58fe4a448909220218f66cb": { - "balance": "1000072000000000000000" - }, - "1080c1d8358a15bc84dac8253c6883319020df2c": { - "balance": "2674000000000000000000" - }, - "9eaf6a328a4076024efa6b67b48b21eedcc0f0b8": { - "balance": "158000000000000000000" - }, - "1e7b5e4d1f572becf2c00fc90cb4767b4a6e33d4": { - "balance": "112970000000000000000" - }, - "acbd185589f7a68a67aa4b1bd65077f8c64e4e21": { - "balance": "200000000000000000000" - }, - "ff78541756ab2b706e0d70b18adb700fc4f1643d": { - "balance": "43250000000000000000000" - }, - "7f0ec3db804692d4d1ea3245365aab0590075bc4": { - "balance": "4000000000000000000000" - }, - "4a918032439159bb315b6725b6830dc83697739f": { - "balance": "343800000000000000000" - }, - "bc1b021a78fde42d9b5226d6ec26e06aa3670090": { - "balance": "80000000000000000000" - }, - "2f2523cc834f0086052402626296675186a8e582": { - "balance": "16000000000000000000000" - }, - "9db2e15ca681f4c66048f6f9b7941ed08b1ff506": { - "balance": "4000000000000000000000" - }, - "20b9a9e6bd8880d9994ae00dd0b9282a0beab816": { - "balance": "500000000000000000000" - }, - "3bddbc8134f77d55597fc97c26d26698090604eb": { - "balance": "13700000000000000000" - }, - "80c3a9f695b16db1597286d1b3a8b7696c39fa27": { - "balance": "100000000000000000000" - }, - "53194d8afa3e883502767edbc30586af33b114d3": { - "balance": "2000000000000000000000" - }, - "e2efd0a9bc407ece03d67e8ec8e9d283f48d2a49": { - "balance": "12280000000000000000000" - }, - "1cb450920078aab2317c7db3b38af7dd298b2d41": { - "balance": "340000000000000000000" - }, - "ca8276c477b4a07b80107b843594189607b53bec": { - "balance": "6000000000000000000000" - }, - "147f4210ab5804940a0b7db8c14c28396b62a6bf": { - "balance": "2000000000000000000000" - }, - "d3df3b53cb3b4755de54e180451cc44c9e8ae0aa": { - "balance": "659801000000000000000" - }, - "f7c708015071d4fb0a3a2a09a45d156396e3349e": { - "balance": "3000000000000000000000" - }, - "a8cafac32280d021020bf6f2a9782883d7aabe12": { - "balance": "100000000000000000000" - }, - "399aa6f5d078cb0970882bc9992006f8fbdf3471": { - "balance": "1000000000000000000000" - }, - "15669180dee29598869b08a721c7d24c4c0ee63f": { - "balance": "1000000000000000000000" - }, - "bba8ab22d2fedbcfc63f684c08afdf1c175090b5": { - "balance": "99091000000000000000" - }, - "5e5a441974a83d74c687ebdc633fb1a49e7b1ad7": { - "balance": "3000000000000000000000" - }, - "98b769cc305cecfb629a00c907069d7ef9bc3a12": { - "balance": "26000000000000000000" - }, - "c820c711f07705273807aaaa6de44d0e4b48be2e": { - "balance": "155000000000000000000" - }, - "12aa7d86ddfbad301692feac8a08f841cb215c37": { - "balance": "137000000000000000000" - }, - "6ff5d361b52ad0b68b1588607ec304ae5665fc98": { - "balance": "1940000000000000000000" - }, - "2382a9d48ec83ea3652890fd0ee79c907b5b2dc1": { - "balance": "133700000000000000000" - }, - "b2a144b1ea67b9510f2267f9da39d3f93de26642": { - "balance": "2000000000000000000000" - }, - "b3e20eb4de18bd060221689894bee5aeb25351ee": { - "balance": "73535000000000000000" - }, - "101a0a64f9afcc448a8a130d4dfcbee89537d854": { - "balance": "15200000000000000000000" - }, - "1b826fb3c012b0d159e294ba5b8a499ff3c0e03c": { - "balance": "2000000000000000000000" - }, - "aafb7b013aa1f8541c7e327bf650adbd194c208f": { - "balance": "1358000000000000000000" - }, - "96eb523e832f500a017de13ec27f5d366c560eff": { - "balance": "307600000000000000000" - }, - "c7bf17c4c11f98941f507e77084fffbd2dbd3db5": { - "balance": "1000000000000000000000" - }, - "840ec83ea93621f034e7bb3762bb8e29ded4c479": { - "balance": "2500000000000000000000" - }, - "0e9c511864a177f49be78202773f60489fe04e52": { - "balance": "6000000000000000000000" - }, - "f6f1a44309051c6b25e47dff909b179bb9ab591c": { - "balance": "1940000000000000000000" - }, - "63fe6bcc4b8a9850abbe75803730c932251f145b": { - "balance": "18200000000000000000" - }, - "f88b58db37420b464c0be88b45ee2b95290f8cfa": { - "balance": "40000000000000000000" - }, - "9d4d321177256ebd9afbda304135d517c3dc5693": { - "balance": "616000000000000000000" - }, - "8c1fbe5f0aea359c5aa1fa08c8895412ca8e05a6": { - "balance": "1000000000000000000000" - }, - "cb0dd7cf4e5d8661f6028943a4b9b75c914436a7": { - "balance": "120000000000000000000000" - }, - "a3979a92760a135adf69d72f75e167755f1cb8c3": { - "balance": "100000000000000000000" - }, - "ca22cda3606da5cad013b8074706d7e9e721a50c": { - "balance": "6816200000000000000000" - }, - "157559adc55764cc6df79323092534e3d6645a66": { - "balance": "6000000000000000000000" - }, - "4f52ad6170d25b2a2e850eadbb52413ff2303e7f": { - "balance": "3040000000000000000000" - }, - "eed28c3f068e094a304b853c950a6809ebcb03e0": { - "balance": "17300000000000000000000" - }, - "2e47f287f498233713850d3126823cc67dcee255": { - "balance": "14600000000000000000" - }, - "6c359e58a13d4578a9338e335c67e7639f5fb4d7": { - "balance": "218000000000000000000" - }, - "4968a2cedb457555a139295aea28776e54003c87": { - "balance": "10092310000000000000000" - }, - "4041374b0feef4792e4b33691fb86897a4ff560c": { - "balance": "365000000000000000000" - }, - "83e48055327c28b5936fd9f4447e73bdb2dd3376": { - "balance": "2674000000000000000000" - }, - "32b7feebc5c59bf65e861c4c0be42a7611a5541a": { - "balance": "2212000000000000000000" - }, - "21a6db6527467bc6dad54bc16e9fe2953b6794ed": { - "balance": "14000000000000000000000" - }, - "e8ead1bb90ccc3aea2b0dcc5b58056554655d1d5": { - "balance": "7760000000000000000000" - }, - "7a94b19992ceb8ce63bc92ee4b5aded10c4d9725": { - "balance": "16770000000000000000000" - }, - "90e93e4dc17121487952333614002be42356498e": { - "balance": "1910000000000000000000" - }, - "aab00abf5828d7ebf26b47ceaccdb8ba03325166": { - "balance": "10000000000000000000000" - }, - "0a9ab2638b1cfd654d25dab018a0aebddf85fd55": { - "balance": "21801000000000000000" - }, - "b12ed07b8a38ad5506363fc07a0b6d799936bdaf": { - "balance": "10000000000000000000000" - }, - "f4a9d00cefa97b7a58ef9417fc6267a5069039ee": { - "balance": "21800000000000000000" - }, - "04a1cada1cc751082ff8da928e3cfa000820a9e9": { - "balance": "40000000000000000000" - }, - "9018cc1f48d2308e252ab6089fb99a7c1d569410": { - "balance": "200000000000000000000" - }, - "895d694e880b13ccd0848a86c5ce411f88476bbf": { - "balance": "199955000000000000000" - }, - "40a7f72867a7dc86770b162b7557a434ed50cce9": { - "balance": "1000000000000000000000" - }, - "467ea10445827ef1e502daf76b928a209e0d4032": { - "balance": "2000000000000000000000" - }, - "7553aa23b68aa5f57e135fe39fdc235eaca8c98c": { - "balance": "1000000000000000000000" - }, - "31b43b015d0081643c6cda46a7073a6dfdbca825": { - "balance": "50019600000000000000000" - }, - "d82fd9fdf6996bedad2843159c06f37e0924337d": { - "balance": "1688800000000000000000" - }, - "24a4eb36a7e498c36f99975c1a8d729fd6b305d7": { - "balance": "258000000000000000000" - }, - "91d66ea6288faa4b3d606c2aa45c7b6b8a252739": { - "balance": "2000000000000000000000" - }, - "83a402438e0519773d5448326bfb61f8b20cf52d": { - "balance": "1520000000000000000000" - }, - "c2fafdd30acb6d6706e9293cb02641f9edbe07b5": { - "balance": "1494224000000000000000" - }, - "79dba256472db4e058f2e4cdc3ea4e8a42773833": { - "balance": "1460000000000000000000" - }, - "498abdeb14c26b7b7234d70fceaef361a76dff72": { - "balance": "3000000000000000000000" - }, - "7b73242d75ca9ad558d650290df17692d54cd8b8": { - "balance": "2000200000000000000000" - }, - "6ec3659571b11f889dd439bcd4d67510a25be57e": { - "balance": "123000000000000000000" - }, - "ab098633eeee0ccefdf632f9575456f6dd80fc86": { - "balance": "200000000000000000000000" - }, - "f4a51fce4a1d5b94b0718389ba4e7814139ca738": { - "balance": "300000000000000000000" - }, - "8f561b41b209f248c8a99f858788376250609cf3": { - "balance": "1700000000000000000000" - }, - "05d0f4d728ebe82e84bf597515ad41b60bf28b39": { - "balance": "4200000000000000000000" - }, - "dfdf43393c649caebe1bb18059decb39f09fb4e8": { - "balance": "400000000000000000000" - }, - "0089508679abf8c71bf6781687120e3e6a84584d": { - "balance": "1800000000000000000000" - }, - "80907f593148b57c46c177e23d25abc4aae18361": { - "balance": "100000000000000000000" - }, - "94fcceadfe5c109c5eaeaf462d43873142c88e22": { - "balance": "4800000000000000000000" - }, - "e89249738b7eced7cb666a663c49cbf6de8343ea": { - "balance": "2000000000000000000000" - }, - "23c99ba087448e19c9701df66e0cab52368331fa": { - "balance": "2000000000000000000000" - }, - "a68e0c30cba3bc5a883e540320f999c7cd558e5c": { - "balance": "1799869000000000000000" - }, - "88888a57bd9687cbf950aeeacf9740dcc4d1ef59": { - "balance": "1820000000000000000000" - }, - "e9b36fe9b51412ddca1a521d6e94bc901213dda8": { - "balance": "10000000000000000000000" - }, - "a9145046fa3628cf5fd4c613927be531e6db1fdd": { - "balance": "112000000000000000000" - }, - "e82c58c579431b673546b53a86459acaf1de9b93": { - "balance": "1000000000000000000000" - }, - "bd6a474d66345bcdd707594adb63b30c7822af54": { - "balance": "4000000000000000000000" - }, - "6a6159074ab573e0ee581f0f3df2d6a594629b74": { - "balance": "310000000000000000000" - }, - "2e7f465520ec35cc23d68e75651bb6689544a196": { - "balance": "1050049000000000000000" - }, - "ac6d02e9a46b379fac4ac9b1d7b5d47bc850ce16": { - "balance": "1760000000000000000000" - }, - "bd59094e074f8d79142ab1489f148e32151f2089": { - "balance": "20000000000000000000" - }, - "0ba6e46af25a13f57169255a34a4dac7ce12be04": { - "balance": "500000000000000000000" - }, - "35145f620397c69cb8e00962961f0f4886643989": { - "balance": "6000000000000000000000" - }, - "d84b922f7841fc5774f00e14604ae0df42c8551e": { - "balance": "4011000000000000000000" - }, - "44232ff66ddad1fd841266380036afd7cf7d7f42": { - "balance": "200000000000000000000" - }, - "516954025fca2608f47da81c215eedfd844a09ff": { - "balance": "382000000000000000000" - }, - "e5aa0b833bb916dc19a8dd683f0ede241d988eba": { - "balance": "3000000000000000000000" - }, - "80ea1acc136eca4b68c842a95adf6b7fee7eb8a2": { - "balance": "4000000000000000000000" - }, - "98a0e54c6d9dc8be96276cebf4fec460f6235d85": { - "balance": "1969803000000000000000" - }, - "91620f3eb304e813d28b0297556d65dc4e5de5aa": { - "balance": "3820000000000000000000" - }, - "7bb984c6dbb9e279966afafda59c01d02627c804": { - "balance": "8050000000000000000000" - }, - "41f489a1ec747bc29c3e5f9d8db97877d4d1b4e9": { - "balance": "133700000000000000000" - }, - "8dbc3e6cb433e194f40f82b40faadb1f8b856116": { - "balance": "1910000000000000000000" - }, - "889da40fb1b60f9ea9bd7a453e584cf7b1b4d9f7": { - "balance": "40000000000000000000" - }, - "debbdd831e0f20ae6e378252decdf92f7cf0c658": { - "balance": "2000000000000000000000" - }, - "a22ade0ddb5c6ef8d0cd8de94d82b11082cb2e91": { - "balance": "1020000000000000000000" - }, - "823219a25976bb2aa4af8bad41ac3526b493361f": { - "balance": "2000000000000000000000" - }, - "6d39a9e98f81f769d73aad2cead276ac1387babe": { - "balance": "394000000000000000000" - }, - "751abcb6cc033059911815c96fd191360ab0442d": { - "balance": "8000000000000000000000" - }, - "64d80c3b8ba68282290b75e65d8978a15a87782c": { - "balance": "1970000000000000000000" - }, - "6ba8f7e25fc2d871618e24e40184199137f9f6aa": { - "balance": "400020000000000000000" - }, - "25a74c2ac75dc8baa8b31a9c7cb4b7829b2456da": { - "balance": "2000000000000000000000" - }, - "0f7b61c59b016322e8226cafaee9d9e76d50a1b3": { - "balance": "4000000000000000000000" - }, - "7526e482529f0a14eec98871dddd0e721b0cd9a2": { - "balance": "20000000000000000000" - }, - "071dd90d14d41f4ff7c413c24238d3359cd61a07": { - "balance": "36400000000000000000000" - }, - "a986762f7a4f294f2e0b173279ad2c81a2223458": { - "balance": "20000000000000000000" - }, - "e667f652f957c28c0e66d0b63417c80c8c9db878": { - "balance": "601650000000000000000" - }, - "7b98e23cb96beee80a168069ebba8f20edd55ccf": { - "balance": "214500000000000000000" - }, - "2d8e5bb8d3521695c77e7c834e0291bfacee7408": { - "balance": "1970000000000000000000" - }, - "f23d01589eb12d439f7448ff54307529f191858d": { - "balance": "2000000000000000000000" - }, - "abd9605b3e91acfd777830d16463478ae0fc7720": { - "balance": "133700000000000000000" - }, - "eabb90d37989aab31feae547e0e6f3999ce6a35d": { - "balance": "2000000000000000000000" - }, - "0abfb39b11486d79572866195ba26c630b6784db": { - "balance": "121500000000000000000000" - }, - "d56a144d7af0ae8df649abae535a15983aa04d02": { - "balance": "5000000000000000000000" - }, - "998c1f93bcdb6ff23c10d0dc924728b73be2ff9f": { - "balance": "1002750000000000000000" - }, - "bc62b3096a91e7dc11a1592a293dd2542150d751": { - "balance": "1000000000000000000000" - }, - "0c8f66c6017bce5b20347204b602b743bad78d60": { - "balance": "2000000000000000000000" - }, - "4c5b3dc0e2b9360f91289b1fe13ce12c0fbda3e1": { - "balance": "2000000000000000000000" - }, - "b44605552471a6eee4daab71ff3bb41326d473e0": { - "balance": "839200000000000000000" - }, - "fc3d226bb36a58f526568857b0bb12d109ec9301": { - "balance": "2000000000000000000000" - }, - "adc8228ef928e18b2a807d00fb3c6c79cd1d9e96": { - "balance": "22800000000000000000" - }, - "9df32a501c0b781c0281022f42a1293ffd7b892a": { - "balance": "9000000000000000000000" - }, - "e7da609d40cde80f00ce5b4ffb6aa9d0b03494fc": { - "balance": "1000000000000000000000" - }, - "9b64d3cd8d2b73f66841b5c46bb695b88a9ab75d": { - "balance": "20769000000000000000" - }, - "8e9c08f738661f9676236eff82ba6261dd3f4822": { - "balance": "100000000000000000000" - }, - "deb97254474c0d2f5a7970dcdb2f52fb1098b896": { - "balance": "1000000000000000000000" - }, - "b4256273962bf631d014555cc1da0dcc31616b49": { - "balance": "2000000000000000000000" - }, - "23abd9e93e7957e5b636be6579051c15e5ce0b0e": { - "balance": "17188400000000000000000" - }, - "382591e7217b435e8e884cdbf415fe377a6fe29e": { - "balance": "8022000000000000000000" - }, - "f17adb740f45cbbde3094e7e13716f8103f563bd": { - "balance": "2000000000000000000000" - }, - "61ed5596c697207f3d55b2a51aa7d50f07fa09e8": { - "balance": "2000000000000000000000" - }, - "788e809741a3b14a22a4b1d937c82cfea489eebe": { - "balance": "7000000000000000000000" - }, - "992646ac1acaabf5ddaba8f9429aa6a94e7496a7": { - "balance": "1000110000000000000000" - }, - "51296f5044270d17707646129c86aad1645eadc1": { - "balance": "1337133000000000000000" - }, - "6ee8aad7e0a065d8852d7c3b9a6e5fdc4bf50c00": { - "balance": "20000000000000000000" - }, - "30db6b9b107e62102f434a9dd0960c2021f5ce4c": { - "balance": "599742000000000000000" - }, - "63fc93001305adfbc9b85d29d9291a05f8f1410b": { - "balance": "1000000000000000000000" - }, - "df6ed6006a6abe886ed33d95a4de28fc12183927": { - "balance": "910000000000000000000" - }, - "4745ab181a36aa8cbf2289d0c45165bc7ebe2381": { - "balance": "39400000000000000000" - }, - "7bb0fdf5a663b5fba28d9c902af0c811e252f298": { - "balance": "200000000000000000000" - }, - "e0ff0bd9154439c4a5b7233e291d7d868af53f33": { - "balance": "396110000000000000000" - }, - "09261f9acb451c3788844f0c1451a35bad5098e3": { - "balance": "8664000000000000000000" - }, - "2813d263fc5ff2479e970595d6b6b560f8d6d6d1": { - "balance": "2000000000000000000000" - }, - "2cd19694d1926a0fa9189edebafc671cf1b2caa5": { - "balance": "1000000000000000000000" - }, - "05336e9a722728d963e7a1cf2759fd0274530fca": { - "balance": "915583000000000000000" - }, - "e5b7af146986c0ff8f85d22e6cc334077d84e824": { - "balance": "2000000000000000000000" - }, - "3e4fbd661015f6461ed6735cefef01f31445de3a": { - "balance": "16200000000000000000000" - }, - "4f5df5b94357de948604c51b7893cddf6076baad": { - "balance": "3760000000000000000000" - }, - "9567a0de811de6ff095b7ee64e7f1b83c2615b80": { - "balance": "267400000000000000000" - }, - "955db3b74360b9a268677e73cea821668af6face": { - "balance": "30000000000000000000000" - }, - "3e040d40cb80ba0125f3b15fdefcc83f3005da1b": { - "balance": "1038000000000000000000" - }, - "43f470ed659e2991c375957e5ddec5bd1d382231": { - "balance": "100000000000000000000" - }, - "047f9bf1529daf87d407175e6f171b5e59e9ff3e": { - "balance": "650000000000000000000" - }, - "15e3b584056b62c973cf5eb096f1733e54c15c91": { - "balance": "936702000000000000000" - }, - "c03de42a109b657a64e92224c08dc1275e80d9b2": { - "balance": "20000000000000000000" - }, - "e4fc13cfcbac1b17ce7783acd423a845943f6b3a": { - "balance": "20000000000000000000" - }, - "65ff874fafce4da318d6c93d57e2c38a0d73e820": { - "balance": "1000160000000000000000" - }, - "8b997dbc078ad02961355da0a159f2927ed43d64": { - "balance": "197000000000000000000" - }, - "2f5080b83f7e2dc0a1dd11b092ad042bff788f4c": { - "balance": "3338355000000000000000" - }, - "1b3920d001c43e72b24e7ca46f0fd6e0c20a5ff2": { - "balance": "2000000000000000000000" - }, - "5ade77fd81c25c0af713b10702768c1eb2f975e7": { - "balance": "20000000000000000000" - }, - "acaaddcbf286cb0e215dda55598f7ff0f4ada5c6": { - "balance": "1000000000000000000000" - }, - "64e0217a5b38aa40583625967fa9883690388b6f": { - "balance": "200000000000000000000" - }, - "ae648155a658370f929be384f7e001047e49dd46": { - "balance": "13561000000000000000000" - }, - "f7c1b443968b117b5dd9b755572fcd39ca5ec04b": { - "balance": "456082000000000000000" - }, - "de027efbb38503226ed871099cb30bdb02af1335": { - "balance": "1000000000000000000000" - }, - "49cf1e54be363106b920729d2d0ba46f0867989a": { - "balance": "268000000000000000000" - }, - "e7f4d7fe6f561f7fa1da3005fd365451ad89df89": { - "balance": "200000000000000000000" - }, - "b036916bdacf94b69e5a8a65602975eb026104dd": { - "balance": "20000000000000000000" - }, - "e923c06177b3427ea448c0a6ff019b54cc548d95": { - "balance": "36281000000000000000" - }, - "ad927e03d1599a78ca2bf0cad2a183dceb71eac0": { - "balance": "1970000000000000000000" - }, - "ef39ca9173df15531d73e6b72a684b51ba0f2bb4": { - "balance": "1598000000000000000000" - }, - "6443b8ae639de91cf73c5ae763eeeed3ddbb9253": { - "balance": "2000000000000000000000" - }, - "8026435aac728d497b19b3e7e57c28c563954f2b": { - "balance": "1730000000000000000000" - }, - "ed327a14d5cfadd98103fc0999718d7ed70528ea": { - "balance": "1440000000000000000000" - }, - "38a3dccf2fcfe0c91a2624bd0cbf88ee4a076c33": { - "balance": "2000000000000000000000" - }, - "f0b1f9e27832c6de6914d70afc238c749995ace4": { - "balance": "2000000000000000000000" - }, - "770d98d31b4353fceee8560c4ccf803e88c0c4e0": { - "balance": "600000000000000000000" - }, - "ba1f0e03cb9aa021f4dcebfa94e5c889c9c7bc9e": { - "balance": "32200000000000000000000" - }, - "233842b1d0692fd11140cf5acda4bf9630bae5f8": { - "balance": "2000000000000000000000" - }, - "b5dd50a15da34968890a53b4f13fe1af081baaaa": { - "balance": "4000000000000000000000" - }, - "72072a0ef1cff3d567cdd260e708ddc11cbc9a31": { - "balance": "100000000000000000000" - }, - "81a88196fac5f23c3e12a69dec4b880eb7d97310": { - "balance": "2000000000000000000000" - }, - "6c63f84556d290bfcd99e434ee9997bfd779577a": { - "balance": "2000000000000000000000" - }, - "5f167aa242bc4c189adecb3ac4a7c452cf192fcf": { - "balance": "1999980000000000000000" - }, - "445cb8de5e3df520b499efc980f52bff40f55c76": { - "balance": "2000000000000000000000" - }, - "aec27ce2133e82d052520afb5c576d9f7eb93ed2": { - "balance": "65232380000000000000000" - }, - "07dc2bf83bc6af19a842ffea661af5b41b67fda1": { - "balance": "1500000000000000000000" - }, - "febd48d0ffdbd5656cd5e686363a61145228f279": { - "balance": "2800000000000000000000" - }, - "a86db07d9f812f4796622d40e03d135874a88a74": { - "balance": "20000000000000000000" - }, - "5413c97ffa4a6e2a7bba8961dc9fce8530a787d7": { - "balance": "1000000000000000000000" - }, - "e2ff9ee4b6ecc14141cc74ca52a9e7a2ee14d908": { - "balance": "1400000000000000000000" - }, - "2e8eb30a716e5fe15c74233e039bfb1106e81d12": { - "balance": "100000000000000000000" - }, - "fd88d114220f081cb3d5e15be8152ab07366576a": { - "balance": "300000000000000000000" - }, - "e408fceaa1b98f3c640f48fcba39f056066d6308": { - "balance": "10000000000000000000000" - }, - "057dd29f2d19aa3da42327ea50bce86ff5c911d9": { - "balance": "4000000000000000000000" - }, - "ed1065dbcf9d73c04ffc7908870d881468c1e132": { - "balance": "2000000000000000000000" - }, - "bbc9d8112e5beb02dd29a2257b1fe69b3536a945": { - "balance": "2000000000000000000000" - }, - "79c1be19711f73bee4e6316ae7549459aacea2e0": { - "balance": "400000000000000000000" - }, - "1bcf3441a866bdbe963009ce33c81cbb0261b02c": { - "balance": "182000000000000000000" - }, - "e2e26e4e1dcf30d048cc6ecf9d51ec1205a4e926": { - "balance": "4000000000000000000000" - }, - "77701e2c493da47c1b58f421b5495dee45bea39b": { - "balance": "6068279000000000000000" - }, - "37a05aceb9395c8635a39a7c5d266ae610d10bf2": { - "balance": "30000000000000000000000" - }, - "c6355ec4768c70a49af69513cd83a5bca7e3b9cd": { - "balance": "6000000000000000000000" - }, - "e3c0c128327a9ad80148139e269773428e638cb0": { - "balance": "2000000000000000000000" - }, - "f7f4898c4c526d955f21f055cb6e47b915e51964": { - "balance": "2288000000000000000000" - }, - "29824e94cc4348bc963279dcdf47391715324cd3": { - "balance": "1940000000000000000000" - }, - "eaa45cea02d87d2cc8fda9434e2d985bd4031584": { - "balance": "1920750000000000000000" - }, - "e08b9aba6bd9d28bc2056779d2fbf0f2855a3d9d": { - "balance": "2000000000000000000000" - }, - "87c498170934b8233d1ad1e769317d5c475f2f40": { - "balance": "1015200000000000000000" - }, - "352d29a26e8a41818181746467f582e6e84012e0": { - "balance": "6000000000000000000000" - }, - "403220600a36f73f24e190d1edb2d61be3f41354": { - "balance": "304000000000000000000" - }, - "0a48296f7631708c95d2b74975bc4ab88ac1392a": { - "balance": "5000000000000000000000" - }, - "ffe0e997f1977a615f5a315af413fd4869343ba0": { - "balance": "100076000000000000000" - }, - "ca66b2280fa282c5b67631ce552b62ee55ad8474": { - "balance": "1969488000000000000000" - }, - "2b6ed29a95753c3ad948348e3e7b1a251080ffb9": { - "balance": "250000000000000000000000" - }, - "492e70f04d18408cb41e25603730506b35a2876b": { - "balance": "39400000000000000000" - }, - "0e6baaa3deb989f289620076668618e9ac332865": { - "balance": "200000000000000000000" - }, - "b753a75f9ed10b21643a0a3dc0517ac96b1a4068": { - "balance": "401800000000000000000" - }, - "3ad915d550b723415620f5a9b5b88a85f382f035": { - "balance": "1000000000000000000000" - }, - "c992be59c6721caf4e028f9e8f05c25c55515bd4": { - "balance": "20000000000000000000" - }, - "02b643d6fabd437a851accbe79abb7fde126dccf": { - "balance": "7200000000000000000000" - }, - "88797e58675ed5cc4c19980783dbd0c956085153": { - "balance": "2000000000000000000000" - }, - "ac142eda1157b9a9a64390df7e6ae694fac98905": { - "balance": "200000000000000000000" - }, - "656579daedd29370d9b737ee3f5cd9d84bc2b342": { - "balance": "1430000000000000000000" - }, - "9bb9b02a26bfe1ccc3f0c6219e261c397fc5ca78": { - "balance": "1337000000000000000000" - }, - "bee8d0b008421954f92d000d390fb8f8e658eaee": { - "balance": "1000000000000000000000" - }, - "7989d09f3826c3e5af8c752a8115723a84d80970": { - "balance": "415554000000000000000" - }, - "7cd5d81eab37e11e6276a3a1091251607e0d7e38": { - "balance": "62856000000000000000" - }, - "6ce1b0f6adc47051e8ab38b39edb4186b03babcc": { - "balance": "1207800000000000000000" - }, - "abfcf5f25091ce57875fc674dcf104e2a73dd2f2": { - "balance": "19700000000000000000" - }, - "1c3ef05dae9dcbd489f3024408669de244c52a02": { - "balance": "20000000000000000000000" - }, - "cfa8b37127149bdbfee25c34d878510951ea10eb": { - "balance": "2000000000000000000000" - }, - "74863acec75d03d53e860e64002f2c165e538377": { - "balance": "1000000000000000000000" - }, - "59b9e733cba4be00429b4bd9dfa64732053a7d55": { - "balance": "20000000000000000000" - }, - "aeadfcd0978edad74a32bd01a0a51d37f246e661": { - "balance": "260000000000000000000" - }, - "08090876baadfee65c3d363ba55312748cfa873d": { - "balance": "1700170000000000000000" - }, - "e589fa76984db5ec4004b46ee8a59492c30744ce": { - "balance": "2800000000000000000000" - }, - "3485361ee6bf06ef6508ccd23d94641f814d3e2f": { - "balance": "2000000000000000000000" - }, - "5cb731160d2e8965670bde925d9de5510935347d": { - "balance": "40000000000000000000" - }, - "8ef4d8a2c23c5279187b64e96f741404085385f3": { - "balance": "299598000000000000000" - }, - "e246683cc99db7c4a52bcbacaab0b32f6bfc93d7": { - "balance": "2000000000000000000000" - }, - "7d273e637ef1eac481119413b91c989dc5eac122": { - "balance": "500000000000000000000" - }, - "6efba8fb2ac5b6730729a972ec224426a287c3ad": { - "balance": "283152000000000000000" - }, - "0773eeacc050f74720b4a1bd57895b1cceeb495d": { - "balance": "10000000000000000000000" - }, - "88a122a2382c523931fb51a0ccad3beb5b7259c3": { - "balance": "2000000000000000000000" - }, - "b0b779b94bfa3c2e1f587bcc9c7e21789222378f": { - "balance": "1550000000000000000000" - }, - "86f95c5b11a293940e35c0b898d8b75f08aab06d": { - "balance": "29605000000000000000000" - }, - "cf2288ef4ebf88e86db13d8a0e0bf52a056582c3": { - "balance": "2533000000000000000000" - }, - "71ea5b11ad8d29b1a4cb67bf58ca6c9f9c338c16": { - "balance": "1600000000000000000000" - }, - "9917d68d4af341d651e7f0075c6de6d7144e7409": { - "balance": "5660000000000000000000" - }, - "1e5800227d4dcf75e30f5595c5bed3f72e341e3b": { - "balance": "248300000000000000000" - }, - "123759f333e13e3069e2034b4f05398918119d36": { - "balance": "20000000000000000000000" - }, - "f798d16da4e460c460cd485fae0fa0599708eb82": { - "balance": "1000000000000000000000" - }, - "864bec5069f855a4fd5892a6c4491db07c88ff7c": { - "balance": "1000000000000000000000" - }, - "fa283299603d8758e8cab082125d2c8f7d445429": { - "balance": "6415633000000000000000" - }, - "c811c2e9aa1ac3462eba5e88fcb5120e9f6e2ca2": { - "balance": "1400140000000000000000" - }, - "61547d376e5369bcf978fc162c3c56ae453547e8": { - "balance": "200000000000000000000" - }, - "0d747ee5969bf79d57381d6fe3a2406cd0d8ce27": { - "balance": "100000000000000000000000" - }, - "f8962b75db5d24c7e8b7cef1068c3e67cebb30a5": { - "balance": "280000000000000000000" - }, - "35bf6688522f35467a7f75302314c02ba176800e": { - "balance": "17400000000000000000000" - }, - "05cb6c3b0072d3116761b532b218443b53e8f6c5": { - "balance": "141722000000000000000000" - }, - "91c80caa081b38351d2a0e0e00f80a34e56474c1": { - "balance": "1000000000000000000000" - }, - "d75a502a5b677287470f65c5aa51b87c10150572": { - "balance": "907400000000000000000" - }, - "3e194b4ecef8bb711ea2ff24fec4e87bd032f7d1": { - "balance": "2575465000000000000000" - }, - "736bf1402c83800f893e583192582a134eb532e9": { - "balance": "9999996000000000000000" - }, - "c2cb1ada5da9a0423873814793f16144ef36b2f3": { - "balance": "1334326000000000000000" - }, - "efcce06bd6089d0e458ef561f5a689480afe7000": { - "balance": "600000000000000000000" - }, - "bfe6bcb0f0c07852643324aa5df5fd6225abc3ca": { - "balance": "74500000000000000000" - }, - "9d799e943e306ba2e5b99c8a6858cbb52c0cf735": { - "balance": "300000000000000000000" - }, - "f45b1dcb2e41dc27ffa024daadf619c11175c087": { - "balance": "19700000000000000000" - }, - "08e38ee0ce48c9ca645c1019f73b5355581c56e6": { - "balance": "1600000000000000000000" - }, - "2cb4c3c16bb1c55e7c6b7a19b127a1ac9390cc09": { - "balance": "3397053000000000000000" - }, - "bdc02cd4330c93d6fbda4f6db2a85df22f43c233": { - "balance": "2000000000000000000000" - }, - "acec91ef6941cf630ba9a3e787a012f4a2d91dd4": { - "balance": "80000000000000000000000" - }, - "27ac073be79ce657a93aa693ee43bf0fa41fef04": { - "balance": "50000000000000000000000" - }, - "22fe884d9037291b4d52e6285ae68dea0be9ffb5": { - "balance": "2000000000000000000000" - }, - "c3107a9af3322d5238df0132419131629539577d": { - "balance": "492650000000000000000" - }, - "b5cac5ed03477d390bb267d4ebd46101fbc2c3da": { - "balance": "197000000000000000000" - }, - "58fb947364e7695765361ebb1e801ffb8b95e6d0": { - "balance": "200000000000000000000" - }, - "32860997d730b2d83b73241a25d3667d51c908ef": { - "balance": "499938000000000000000" - }, - "c79d5062c796dd7761f1f13e558d73a59f82f38b": { - "balance": "8000000000000000000000" - }, - "fa142fe47eda97e6503b386b18a2bedd73ccb5b1": { - "balance": "850080000000000000000" - }, - "6ca5de00817de0cedce5fd000128dede12648b3c": { - "balance": "20000000000000000000" - }, - "214b743955a512de6e0d886a8cbd0282bee6d2a2": { - "balance": "2000000000000000000000" - }, - "ede79ae1ff4f1606d59270216fa46ab2ddd4ecaa": { - "balance": "146000000000000000000" - }, - "528101ce46b720a2214dcdae6618a53177ffa377": { - "balance": "508876000000000000000" - }, - "b5870ce342d43343333673038b4764a46e925f3e": { - "balance": "1000000000000000000000" - }, - "843bd3502f45f8bc4da370b323bdac3fcf5f19a6": { - "balance": "1476000000000000000000" - }, - "5067f4549afbfe884c59cbc12b96934923d45db0": { - "balance": "1000000000000000000000" - }, - "6f2a42e6e033d01061131929f7a6ee1538021e52": { - "balance": "2000000000000000000000" - }, - "e9e1f7cb00a110edd0ebf8b377ef8a7bb856117f": { - "balance": "200000000000000000000" - }, - "a387ecde0ee4c8079499fd8e03473bd88ad7522a": { - "balance": "1970000000000000000000" - }, - "6dff90e6dc359d2590882b1483edbcf887c0e423": { - "balance": "1000000000000000000000" - }, - "22e512149a18d369b73c71efa43e86c9edabaf1d": { - "balance": "1455000000000000000000" - }, - "a3203095edb7028e6871ce0a84f548459f83300a": { - "balance": "4000000000000000000000" - }, - "93b4bf3fdff6de3f4e56ba6d7799dc4b93a6548f": { - "balance": "19100000000000000000" - }, - "8c75956e8fed50f5a7dd7cfd27da200f6746aea6": { - "balance": "1000000000000000000000" - }, - "afc8ebe8988bd4105acc4c018e546a1e8f9c7888": { - "balance": "500000000000000000000" - }, - "bf9acd4445d9c9554689cabbbab18800ff1741c2": { - "balance": "1000000000000000000000" - }, - "603f2fab7afb6e017b94766069a4b43b38964923": { - "balance": "1656954000000000000000" - }, - "a1f765c44fe45f790677944844be4f2d42165fbd": { - "balance": "3687750000000000000000" - }, - "4dc9d5bb4b19cecd94f19ec25d200ea72f25d7ed": { - "balance": "2000000000000000000000" - }, - "48f60a35484fe7792bcc8a7b6393d0dda1f6b717": { - "balance": "3600000000000000000000" - }, - "588ed990a2aff44a94105d58c305257735c868ac": { - "balance": "16100000000000000000000" - }, - "710be8fd5e2918468be2aabea80d828435d79612": { - "balance": "17600000000000000000" - }, - "03ea6d26d080e57aee3926b18e8ed73a4e5b2826": { - "balance": "200000000000000000000" - }, - "20824ba1dbebbef9846ef3d0f6c1b017e6912ec4": { - "balance": "7170194000000000000000" - }, - "f7500c166f8bea2f82347606e5024be9e4f4ce99": { - "balance": "20000000000000000000" - }, - "9d369165fb70b81a3a765f188fd60cbe5e7b0968": { - "balance": "2000000000000000000000" - }, - "6fddbd9bca66e28765c2162c8433548c1052ed11": { - "balance": "82720000000000000000000" - }, - "8b81156e698639943c01a75272ad3d35851ab282": { - "balance": "344946000000000000000" - }, - "75804aac64b4199083982902994d9c5ed8828f11": { - "balance": "557800000000000000000" - }, - "d6e8e97ae9839b9ee507eedb28edfb7477031439": { - "balance": "2000000000000000000000" - }, - "6c808cabb8ff5fbb6312d9c8e84af8cf12ef0875": { - "balance": "4000086000000000000000" - }, - "afa539586e4719174a3b46b9b3e663a7d1b5b987": { - "balance": "5000000000000000000000" - }, - "f8a065f287d91d77cd626af38ffa220d9b552a2b": { - "balance": "1910000000000000000000" - }, - "30e60900cacc7203f314dc604347255167fc2a0f": { - "balance": "2000000000000000000000" - }, - "796f87ba617a2930b1670be92ed1281fb0b346e1": { - "balance": "128400000000000000000" - }, - "f114ff0d0f24eff896edde5471dea484824a99b3": { - "balance": "13700000000000000000" - }, - "0b80fc70282cbdd5fde35bf78984db3bdb120188": { - "balance": "1000160000000000000000" - }, - "da7ad025ebde25d22243cb830ea1d3f64a566323": { - "balance": "500000000000000000000" - }, - "65a52141f56bef98991724c6e7053381da8b5925": { - "balance": "60140000000000000000" - }, - "bbc8eaff637e94fcc58d913c7770c88f9b479277": { - "balance": "200000000000000000000" - }, - "0469e8c440450b0e512626fe817e6754a8152830": { - "balance": "2000000000000000000000" - }, - "0727be0a2a00212048b5520fbefb953ebc9d54a0": { - "balance": "10000000000000000000000" - }, - "7d858493f07415e0912d05793c972113eae8ae88": { - "balance": "1818000000000000000000" - }, - "7091303116d5f2389b23238b4d656a8596d984d3": { - "balance": "1094014000000000000000" - }, - "3702e704cc21617439ad4ea27a5714f2fda1e932": { - "balance": "1000000000000000000000" - }, - "b87de1bcd29269d521b8761cc39cfb4319d2ead5": { - "balance": "1000000000000000000000" - }, - "f639ac31da9f67271bd10402b7654e5ce763bd47": { - "balance": "399996000000000000000" - }, - "e7735ec76518fc6aa92da8715a9ee3f625788f13": { - "balance": "1997803000000000000000" - }, - "51277fe7c81eebd252a03df69a6b9f326e272207": { - "balance": "59965000000000000000" - }, - "3b8098533f7d9bdcd307dbb23e1777ca18418936": { - "balance": "2000000000000000000000" - }, - "2cba6d5d0dc204ea8a25ada2e26f5675bd5f2fdc": { - "balance": "1330755000000000000000" - }, - "5c3c1c645b917543113b3e6c1c054da1fe742b9a": { - "balance": "800000000000000000000" - }, - "5ecdbaeab9106ffe5d7b519696609a05baeb85ad": { - "balance": "20000000000000000000" - }, - "45a820a0672f17dc74a08112bc643fd1167736c3": { - "balance": "199943000000000000000" - }, - "beef94213879e02622142bea61290978939a60d7": { - "balance": "5728109000000000000000" - }, - "6cd212aee04e013f3d2abad2a023606bfb5c6ac7": { - "balance": "1999944000000000000000" - }, - "92698e345378c62d8eda184d94366a144b0c105b": { - "balance": "1400000000000000000000" - }, - "2d5b42fc59ebda0dfd66ae914bc28c1b0a6ef83a": { - "balance": "206764195000000000000000" - }, - "b7a6791c16eb4e2162f14b6537a02b3d63bfc602": { - "balance": "780700000000000000000" - }, - "fa105f1a11b6e4b1f56012a27922e2ac2da4812f": { - "balance": "9550000000000000000000" - }, - "2306df931a940d58c01665fa4d0800802c02edfe": { - "balance": "1000000000000000000000" - }, - "f37bf78c5875154711cb640d37ea6d28cfcb1259": { - "balance": "200000000000000000000" - }, - "66201bd227ae6dc6bdfed5fbde811fecfe5e9dd9": { - "balance": "594808000000000000000" - }, - "2bafbf9e9ed2c219f7f2791374e7d05cb06777e7": { - "balance": "220000000000000000000" - }, - "8e9b35ad4a0a86f758446fffde34269d940ceacd": { - "balance": "4000000000000000000000" - }, - "1b43232ccd4880d6f46fa751a96cd82473315841": { - "balance": "80000000000000000000" - }, - "6eefdc850e87b715c72791773c0316c3559b58a4": { - "balance": "4000000000000000000000" - }, - "f2c03e2a38998c21648760f1e5ae7ea3077d8522": { - "balance": "2642456000000000000000" - }, - "0625d06056968b002206ff91980140242bfaa499": { - "balance": "1000000000000000000000" - }, - "6158e107c5eb54cb7604e0cd8dc1e07500d91c3c": { - "balance": "50000000000000000000" - }, - "02477212ffdd75e5155651b76506b1646671a1eb": { - "balance": "1760000000000000000000" - }, - "fa44a855e404c86d0ca8ef3324251dfb349c539e": { - "balance": "1552000000000000000000" - }, - "49897fe932bbb3154c95d3bce6d93b6d732904dd": { - "balance": "4000000000000000000000" - }, - "9b6641b13e172fc072ca4b8327a3bc28a15b66a9": { - "balance": "120000000000000000000" - }, - "a46b4387fb4dcce011e76e4d73547d4481e09be5": { - "balance": "1337000000000000000000" - }, - "72bb27cb99f3e2c2cf90a98f707d30e4a201a071": { - "balance": "1640000000000000000000" - }, - "b6bfe1c3ef94e1846fb9e3acfe9b50c3e9069233": { - "balance": "1999944000000000000000" - }, - "e6cb3f3124c9c9cc3834b1274bc3336456a38bac": { - "balance": "427382000000000000000" - }, - "fcbc5c71ace79741450b012cf6b8d3f17db68a70": { - "balance": "9550000000000000000000" - }, - "15dbb48c98309764f99ced3692dcca35ee306bac": { - "balance": "150000000000000000000000" - }, - "2e10910ba6e0bc17e055556614cb87090f4d7e5b": { - "balance": "200000000000000000000" - }, - "e5fbe34984b637196f331c679d0c0c47d83410e1": { - "balance": "2000050000000000000000" - }, - "6d120f0caae44fd94bcafe55e2e279ef96ba5c7a": { - "balance": "4000000000000000000000" - }, - "aa5afcfd8309c2df9d15be5e6a504e7d706624c5": { - "balance": "5846763000000000000000" - }, - "37959c20b7e9931d72f5a8ae869dafddad3b6d5c": { - "balance": "200000000000000000000" - }, - "b041310fe9eed6864cedd4bee58df88eb4ed3cac": { - "balance": "10000000000000000000000" - }, - "986df47e76e4d7a789cdee913cc9831650936c9d": { - "balance": "5000000000000000000000" - }, - "35aaa0465d1c260c420fa30e2629869fb6559207": { - "balance": "704976000000000000000" - }, - "7f655c6789eddf455cb4b88099720639389eebac": { - "balance": "6000000000000000000000" - }, - "9e3eb509278fe0dcd8e0bbe78a194e06b6803943": { - "balance": "940000000000000000000" - }, - "3e9410d3b9a87ed5e451a6b91bb8923fe90fb2b5": { - "balance": "200000000000000000000" - }, - "9e960dcd03d5ba99cb115d17ff4c09248ad4d0be": { - "balance": "200000000000000000000" - }, - "f057aa66ca767ede124a1c5b9cc5fc94ef0b0137": { - "balance": "2077730000000000000000" - }, - "f38a6ca80168537e974d14e1c3d13990a44c2c1b": { - "balance": "6000000000000000000000" - }, - "229e430de2b74f442651ddcdb70176bc054cad54": { - "balance": "13545000000000000000" - }, - "27bf9f44ba7d05c33540c3a53bb02cbbffe7c3c6": { - "balance": "2000000000000000000000" - }, - "10389858b800e8c0ec32f51ed61a355946cc409b": { - "balance": "200000000000000000000" - }, - "fd2929271e9d2095a264767e7b0df52ea0d1d400": { - "balance": "3000040000000000000000" - }, - "44250d476e062484e9080a3967bf3a4a732ad73f": { - "balance": "20000000000000000000" - }, - "0c67033dd8ee7f0c8ae534d42a51f7d9d4f7978f": { - "balance": "200000000000000000000" - }, - "e083d34863e0e17f926b7928edff317e998e9c4b": { - "balance": "400000000000000000000" - }, - "7f7192c0df1c7db6d9ed65d71184d8e4155a17ba": { - "balance": "79800000000000000000" - }, - "51e7b55c2f9820eed73884361b5066a59b6f45c6": { - "balance": "2000000000000000000000" - }, - "4fa983bb5e3073a8edb557effeb4f9fb1d60ef86": { - "balance": "1599800000000000000000" - }, - "5a5ee8e9bb0e8ab2fecb4b33d29478be50bbd44b": { - "balance": "776000000000000000000" - }, - "1f3959fc291110e88232c36b7667fc78a379613f": { - "balance": "18200000000000000000" - }, - "2d7d5c40ddafc450b04a74a4dabc2bb5d665002e": { - "balance": "2000000000000000000000" - }, - "5215183b8f80a9bc03d26ce91207832a0d39e620": { - "balance": "1000000000000000000000" - }, - "5607590059a9fec1881149a44b36949aef85d560": { - "balance": "2000000000000000000000" - }, - "f7c50f922ad16b61c6d1baa045ed816815bac48f": { - "balance": "12566370000000000000000" - }, - "da10978a39a46ff0bb848cf65dd9c77509a6d70e": { - "balance": "2000000000000000000000" - }, - "a7dcbba9b9bf6762c145416c506a71e3b497209c": { - "balance": "1999944000000000000000" - }, - "54e01283cc8b384538dd646770b357c960d6cacd": { - "balance": "5000000000000000000000" - }, - "78cf8336b328db3d87813a472b9e89b75e0cf3bc": { - "balance": "1000000000000000000000" - }, - "ba24fc436753a739db2c8d40e6d4d04c528e86fa": { - "balance": "13000000000000000000000" - }, - "dfe929a61c1b38eddbe82c25c2d6753cb1e12d68": { - "balance": "402500000000000000000" - }, - "2b49fba29830360fcdb6da23bbfea5c0bbac5281": { - "balance": "20000000000000000000" - }, - "76becae4a31d36f3cb577f2a43594fb1abc1bb96": { - "balance": "24860000000000000000000" - }, - "e0cf698a053327ebd16b7d7700092fe2e8542446": { - "balance": "95275000000000000000" - }, - "a3802d8a659e89a2c47e905430b2a827978950a7": { - "balance": "1000000000000000000000" - }, - "75636cdb109050e43d5d6ec47e359e218e857eca": { - "balance": "22886800000000000000000" - }, - "3d813ff2b6ed57b937dabf2b381d148a411fa085": { - "balance": "100000000000000000000" - }, - "a9252551a624ae513719dabe5207fbefb2fd7749": { - "balance": "40000000000000000000" - }, - "c749668042e71123a648975e08ed6382f83e05e2": { - "balance": "14000000000000000000000" - }, - "04eca501630abce35218b174956b891ba25efb23": { - "balance": "1000060000000000000000" - }, - "790f91bd5d1c5cc4739ae91300db89e1c1303c93": { - "balance": "2000000000000000000000" - }, - "009560a3de627868f91fa8bfe1c1b7afaf08186b": { - "balance": "524000000000000000000" - }, - "1329dd19cd4baa9fc64310efeceab22117251f12": { - "balance": "200000000000000000000" - }, - "7005a772282b1f62afda63f89b5dc6ab64c84cb9": { - "balance": "18000000000000000000000" - }, - "abfe936425dcc7b74b955082bbaaf2a11d78bc05": { - "balance": "1400000000000000000000" - }, - "97d0d9725e3b70e675843173938ed371b62c7fac": { - "balance": "170000000000000000000" - }, - "41ed2d8e7081482c919fc23d8f0091b3c82c4685": { - "balance": "1295460000000000000000" - }, - "992365d764c5ce354039ddfc912e023a75b8e168": { - "balance": "18200000000000000000" - }, - "e1c607c0a8a060da8f02a8eb38a013ea8cda5b8c": { - "balance": "805000000000000000000" - }, - "3b2c45990e21474451cf4f59f01955b331c7d7c9": { - "balance": "2000000000000000000000" - }, - "29ac2b458454a36c7e96c73a8667222a12242c71": { - "balance": "4000000000000000000000" - }, - "b8555010776e3c5cb311a5adeefe9e92bb9a64b9": { - "balance": "4000000000000000000000" - }, - "30380087786965149e81423b15e313ba32c5c783": { - "balance": "18200000000000000000" - }, - "a2f86bc061884e9eef05640edd51a2f7c0596c69": { - "balance": "2000050000000000000000" - }, - "9f98eb34d46979b0a6de8b05aa533a89b825dcf1": { - "balance": "86500000000000000000" - }, - "c81fb7d20fd2800192f0aac198d6d6a37d3fcb7d": { - "balance": "259500000000000000000" - }, - "a4035ab1e5180821f0f380f1131b7387c8d981cd": { - "balance": "20000000000000000000" - }, - "782f52f0a676c77716d574c81ec4684f9a020a97": { - "balance": "850055000000000000000" - }, - "261e0fa64c51137465eecf5b90f197f7937fdb05": { - "balance": "18000000000000000000000" - }, - "276fd7d24f8f883f5a7a28295bf17151c7a84b03": { - "balance": "2000000000000000000000" - }, - "a1f5b840140d5a9acef402ac3cc3886a68cad248": { - "balance": "2000000000000000000000" - }, - "d2bf67a7f3c6ce56b7be41675dbbadfe7ea93a33": { - "balance": "400000000000000000000" - }, - "8ee584337ddbc80f9e3498df55f0a21eacb57fb1": { - "balance": "20000000000000000000" - }, - "34393c5d91b9de597203e75bac4309b5fa3d28c3": { - "balance": "194000000000000000000" - }, - "114cbbbf6fb52ac414be7ec61f7bb71495ce1dfa": { - "balance": "3000000000000000000000" - }, - "ab7c42c5e52d641a07ad75099c62928b7f86622f": { - "balance": "335940000000000000000" - }, - "80bf995ed8ba92701d10fec49f9e7d014dbee026": { - "balance": "572153000000000000000" - }, - "4a192035e2619b24b0709d56590e9183ccf2c1d9": { - "balance": "10000000000000000000000" - }, - "376cd7577383e902951b60a2017ba7ea29e33576": { - "balance": "2000000000000000000000" - }, - "f5437e158090b2a2d68f82b54a5864b95dd6dbea": { - "balance": "4010732000000000000000" - }, - "13a5eecb38305df94971ef2d9e179ae6cebab337": { - "balance": "330000000000000000000" - }, - "efc8cf1963c9a95267b228c086239889f4dfd467": { - "balance": "10000000000000000000000" - }, - "db77b88dcb712fd17ee91a5b94748d720c90a994": { - "balance": "2000000000000000000000" - }, - "9aaafa0067647ed999066b7a4ca5b4b3f3feaa6f": { - "balance": "1000000000000000000000" - }, - "ae36f7452121913e800e0fcd1a65a5471c23846f": { - "balance": "164000000000000000000" - }, - "b124bcb6ffa430fcae2e86b45f27e3f21e81ee08": { - "balance": "2000000000000000000000" - }, - "f2813a64c5265d020235cb9c319b6c96f906c41e": { - "balance": "350000000000000000000" - }, - "e848ca7ebff5c24f9b9c316797a43bf7c356292d": { - "balance": "114000000000000000000" - }, - "21a6feb6ab11c766fdd977f8df4121155f47a1c0": { - "balance": "57200000000000000000" - }, - "e95e92bbc6de07bf3a660ebf5feb1c8a3527e1c5": { - "balance": "18200000000000000000" - }, - "0b369e002e1b4c7913fcf00f2d5e19c58165478f": { - "balance": "64520000000000000000" - }, - "0909648c18a3ce5bae7a047ec2f868d24cdda81d": { - "balance": "3820000000000000000000" - }, - "d32b45564614516c91b07fa9f72dcf787cce4e1c": { - "balance": "291000000000000000000" - }, - "cf1bdb799b2ea63ce134668bdc198b54840f180b": { - "balance": "18200000000000000000" - }, - "ae062c448618643075de7a0030342dced63dbad7": { - "balance": "825982000000000000000" - }, - "99dfd0504c06c743e46534fd7b55f1f9c7ec3329": { - "balance": "2000000000000000000000" - }, - "87fc4635263944ce14a46c75fa4a821f39ce7f72": { - "balance": "20000000000000000000" - }, - "27c2d7ca504daa3d9066dc09137dc42f3aaab452": { - "balance": "600000000000000000000" - }, - "cc60f836acdef3548a1fefcca13ec6a937db44a0": { - "balance": "86500000000000000000" - }, - "c910a970556c9716ea53af66ddef93143124913d": { - "balance": "1580000000000000000000" - }, - "8173c835646a672e0152be10ffe84162dd256e4c": { - "balance": "492000000000000000000" - }, - "e989733ca1d58d9e7b5029ba5d444858bec03172": { - "balance": "581595000000000000000" - }, - "86806474c358047d9406e6a07f40945bc8328e67": { - "balance": "6884000000000000000000" - }, - "5395a4455d95d178b4532aa4725b193ffe512961": { - "balance": "1000000000000000000000" - }, - "56397638bb3cebf1f62062794b5eb942f916171d": { - "balance": "2000000000000000000000" - }, - "6958f83bb2fdfb27ce0409cd03f9c5edbf4cbedd": { - "balance": "20000000000000000000000" - }, - "26ff0a51e7cece8400276978dbd6236ef162c0e6": { - "balance": "100020000000000000000000" - }, - "4ca783b556e5bf53aa13c8116613d65782c9b642": { - "balance": "25200000000000000000000" - }, - "15a0aec37ff9ff3d5409f2a4f0c1212aaccb0296": { - "balance": "1000000000000000000000" - }, - "50378af7ef54043f892ab7ce97d647793511b108": { - "balance": "19700000000000000000" - }, - "e7c6b5fc05fc748e5b4381726449a1c0ad0fb0f1": { - "balance": "2000000000000000000000" - }, - "5317ecb023052ca7f5652be2fa854cfe4563df4d": { - "balance": "499986000000000000000" - }, - "c94f7c35c027d47df8ef4f9df85a9248a17dd23b": { - "balance": "29944000000000000000" - }, - "6a63fc89abc7f36e282d80787b7b04afd6553e71": { - "balance": "160000000000000000000" - }, - "5fd3d6777ec2620ae83a05528ed425072d3ca8fd": { - "balance": "2000000000000000000000" - }, - "29adcf83b6b20ac6a434abb1993cbd05c60ea2e4": { - "balance": "10000000000000000000000" - }, - "8c6f9f4e5b7ae276bf58497bd7bf2a7d25245f64": { - "balance": "2730000000000000000000" - }, - "d94a57882a52739bbe2a0647c80c24f58a2b4f1c": { - "balance": "1341230000000000000000" - }, - "7286e89cd9de8f7a8a00c86ffdb53992dd9251d1": { - "balance": "1940000000000000000000" - }, - "5773b6026721a1dd04b7828cd62b591bfb34534c": { - "balance": "27000000000000000000000" - }, - "11fefb5dc1a4598aa712640c517775dfa1d91f8c": { - "balance": "10000000000000000000000" - }, - "c6e324beeb5b36765ecd464260f7f26006c5c62e": { - "balance": "2000000000000000000000" - }, - "118fbd753b9792395aef7a4d78d263cdcaabd4f7": { - "balance": "999800000000000000000" - }, - "f8298591523e50b103f0b701d623cbf0f74556f6": { - "balance": "200000000000000000000" - }, - "ab0ced762e1661fae1a92afb1408889413794825": { - "balance": "1910000000000000000000" - }, - "fa67b67b4f37a0150915110ede073b05b853bda2": { - "balance": "647490000000000000000" - }, - "ca122cf0f2948896b74843f49afed0ba1618eed7": { - "balance": "560000000000000000000" - }, - "186b95f8e5effddcc94f1a315bf0295d3b1ea588": { - "balance": "1999944000000000000000" - }, - "2915624bcb679137b8dae9ab57d11b4905eaee4b": { - "balance": "20000000000000000000" - }, - "0c6845bf41d5ee273c3ee6b5b0d69f6fd5eabbf7": { - "balance": "3000026000000000000000" - }, - "cb7479109b43b26657f4465f4d18c6f974be5f42": { - "balance": "1820000000000000000000" - }, - "8dd6a9bae57f518549ada677466fea8ab04fd9b4": { - "balance": "4000000000000000000000" - }, - "34958a46d30e30b273ecc6e5d358a212e5307e8c": { - "balance": "2000000000000000000000" - }, - "2003717907a72560f4307f1beecc5436f43d21e7": { - "balance": "500000000000000000000" - }, - "55ab99b0e0e55d7bb874b7cfe834de631c97ec23": { - "balance": "1031400000000000000000" - }, - "79b48d2d6137c3854d611c01ea42427a0f597bb7": { - "balance": "191000000000000000000" - }, - "d609ec0be70d0ad26f6e67c9d4762b52ee51122c": { - "balance": "1000000000000000000000" - }, - "e8c3f045bb7d38c9d2f395b0ba8492b253230901": { - "balance": "9000000000000000000000" - }, - "aaca60d9d700e78596bbbbb1f1e2f70f4627f9d8": { - "balance": "999996000000000000000" - }, - "89d75b8e0831e46f80bc174188184e006fde0eae": { - "balance": "1000000000000000000000" - }, - "b3667894b7863c068ad344873fcff4b5671e0689": { - "balance": "20000000000000000000000" - }, - "bc1609d685b76b48ec909aa099219022f89b2ccd": { - "balance": "1182000000000000000000" - }, - "88ee7f0efc8f778c6b687ec32be9e7d6f020b674": { - "balance": "2000000000000000000000" - }, - "470ac5d1f3efe28f3802af925b571e63868b397d": { - "balance": "2000000000000000000000" - }, - "abf8ffe0708a99b528cc1ed4e9ce4b0d0630be8c": { - "balance": "2263600000000000000000" - }, - "8cee38d6595788a56e3fb94634b3ffe1fbdb26d6": { - "balance": "20000000000000000000000" - }, - "19798cbda715ea9a9b9d6aab942c55121e98bf91": { - "balance": "1200000000000000000000" - }, - "e25a167b031e84616d0f013f31bda95dcc6350b9": { - "balance": "10560000000000000000000" - }, - "6196c3d3c0908d254366b7bca55745222d9d4db1": { - "balance": "4000000000000000000000" - }, - "e8e9850586e94f5299ab494bb821a5f40c00bd04": { - "balance": "3820000000000000000000" - }, - "1059cbc63e36c43e88f30008aca7ce058eeaa096": { - "balance": "100000000000000000000000" - }, - "c4f2913b265c430fa1ab8adf26c333fc1d9b66f2": { - "balance": "20000000000000000000" - }, - "26e9e2ad729702626417ef25de0dc800f7a779b3": { - "balance": "1000000000000000000000" - }, - "0dfbd4817050d91d9d625c02053cf61a3ee28572": { - "balance": "340000000000000000000" - }, - "709fe9d2c1f1ce42207c9585044a60899f35942f": { - "balance": "2000000000000000000000" - }, - "7ad82caea1a8b4ed05319b9c9870173c814e06ee": { - "balance": "616000000000000000000" - }, - "2a595f16eee4cb0c17d9a2d939b3c10f6c677243": { - "balance": "1100000000000000000000" - }, - "a8f89dd5cc6e64d7b1eeace00702022cd7d2f03d": { - "balance": "700000000000000000000" - }, - "c0a6cbad77692a3d88d141ef769a99bb9e3c9951": { - "balance": "100000000000000000000" - }, - "868c23be873466d4c74c220a19b245d1787e807f": { - "balance": "1366481000000000000000" - }, - "2905b192e83ce659aa355b9d0c204e3e95f9bb9a": { - "balance": "2160817000000000000000" - }, - "50b9fef0a1329b02d16506255f5a2db71ec92d1f": { - "balance": "1325464000000000000000" - }, - "fc10b7a67b3268d5331bfb6a14def5ea4a162ca3": { - "balance": "200000000000000000000" - }, - "85eb256b51c819d60ea61a82d12c9358d59c1cae": { - "balance": "460000000000000000000" - }, - "75de7e9352e90b13a59a5878ffecc7831cac4d82": { - "balance": "2740000000000000000000" - }, - "d32b2c79c36478c5431901f6d700b04dbe9b8810": { - "balance": "396000000000000000000" - }, - "2d0326b23f0409c0c0e9236863a133075a94ba18": { - "balance": "210380000000000000000" - }, - "d2e21ed56868fab28e0947927adaf29f23ebad6c": { - "balance": "1994000000000000000000" - }, - "2ad6c9d10c261819a1a0ca2c48d8c7b2a71728df": { - "balance": "1000000000000000000000" - }, - "7d445267c59ab8d2a2d9e709990e09682580c49f": { - "balance": "1000000000000000000000" - }, - "b6047cdf932db3e4045f4976122341537ed5961e": { - "balance": "20000000000000000000" - }, - "2b3cf97311ff30f460945a9d8099f4a88e26d456": { - "balance": "2000000000000000000000" - }, - "7f4f593b618c330ba2c3d5f41eceeb92e27e426c": { - "balance": "2775000000000000000000" - }, - "72a2fc8675feb972fa41b50dffdbbae7fa2adfb7": { - "balance": "2853840000000000000000" - }, - "076561a856455d7ef86e63f87c73dbb628a55f45": { - "balance": "900000000000000000000" - }, - "03d1724fd00e54aabcd2de2a91e8462b1049dd3a": { - "balance": "2640000000000000000000" - }, - "7ea0f96ee0a573a330b56897761f3d4c0130a8e3": { - "balance": "1337000000000000000000" - }, - "fe65c4188d7922576909642044fdc52395560165": { - "balance": "4000000000000000000000" - }, - "57883010b4ac857fedac03eab2551723a8447ffb": { - "balance": "1000000000000000000000" - }, - "0729a8a4a5ba23f579d0025b1ad0f8a0d35cdfd2": { - "balance": "9700000000000000000000" - }, - "e75c1fb177089f3e58b1067935a6596ef1737fb5": { - "balance": "99910000000000000000" - }, - "e0e978753d982f7f9d1d238a18bd4889aefe451b": { - "balance": "9700000000000000000000" - }, - "5620f46d1451c2353d6243a5d4b427130be2d407": { - "balance": "60000000000000000000" - }, - "f3d688f06bbdbf50f9932c4145cbe48ecdf68904": { - "balance": "20000000000000000000" - }, - "3aa948ea02397755effb2f9dc9392df1058f7e33": { - "balance": "850000000000000000000" - }, - "20d1417f99c569e3beb095856530fe12d0fceaaa": { - "balance": "1182175000000000000000" - }, - "ac77bdf00fd5985b5db12bbef800380abc2a0677": { - "balance": "1000000000000000000000" - }, - "267a7e6e82e1b91d51deddb644f0e96dbb1f7f7e": { - "balance": "20000000000000000000" - }, - "4bbcbf38b3c90163a84b1cd2a93b58b2a3348d87": { - "balance": "8000000000000000000000" - }, - "4c6b93a3bec16349540cbfcae96c9621d6645010": { - "balance": "2000000000000000000000" - }, - "c9308879056dfe138ef8208f79a915c6bc7e70a8": { - "balance": "10000000000000000000000" - }, - "c48b693cacefdbd6cb5d7895a42e3196327e261c": { - "balance": "1000000000000000000000" - }, - "a0951970dfd0832fb83bda12c23545e79041756c": { - "balance": "600000000000000000000" - }, - "7cdf74213945953db39ad0e8a9781add792e4d1d": { - "balance": "2000000000000000000000" - }, - "75621865b6591365606ed378308c2d1def4f222c": { - "balance": "3100000000000000000000" - }, - "67d6a8aa1bf8d6eaf7384e993dfdf10f0af68a61": { - "balance": "198067000000000000000" - }, - "8f0af37566d152802f1ae8f928b25af9b139b448": { - "balance": "200000000000000000000" - }, - "2c6afcd4037c1ed14fa74ff6758e0945a185a8e8": { - "balance": "17600000000000000000" - }, - "c1b2aa8cb2bf62cdc13a47ecc4657facaa995f98": { - "balance": "1000129000000000000000" - }, - "9e8144e08e89647811fe6b72d445d6a5f80ad244": { - "balance": "10000000000000000000000" - }, - "e04ff5e5a7e2af995d8857ce0290b53a2b0eda5d": { - "balance": "1000000000000000000000" - }, - "03dedfcd0b3c2e17c705da248790ef98a6bd5751": { - "balance": "1337000000000000000000" - }, - "698a8a6f01f9ab682f637c7969be885f6c5302bf": { - "balance": "19400000000000000000" - }, - "d82c6fedbdac98af2eed10b00f32b00056ca5a6d": { - "balance": "200000000000000000000" - }, - "2697b339813b0c2d964b2471eb1c606f4ecb9616": { - "balance": "1154000000000000000000" - }, - "987c9bcd6e3f3990a52be3eda4710c27518f4f72": { - "balance": "400000000000000000000" - }, - "c5d48ca2db2f85d8c555cb0e9cfe826936783f9e": { - "balance": "200000000000000000000" - }, - "da214c023e2326ff696c00393168ce46ffac39ec": { - "balance": "1000000000000000000000" - }, - "86570ab259c9b1c32c9729202f77f590c07dd612": { - "balance": "200000000000000000000" - }, - "a646a95c6d6f59f104c6541d7760757ab392b08c": { - "balance": "4200000000000000000000" - }, - "1933e334c40f3acbad0c0b851158206924beca3a": { - "balance": "7551541000000000000000" - }, - "3552a496eba67f12be6eedab360cd13661dc7480": { - "balance": "300000000000000000000" - }, - "2a9c96c19151ffcbe29a4616d0c52b3933b4659f": { - "balance": "69263000000000000000" - }, - "3b7b8e27de33d3ce7961b98d19a52fe79f6c25be": { - "balance": "100000000000000000000000" - }, - "a1911405cf6e999ed011f0ddcd2a4ff7c28f2526": { - "balance": "40000000000000000000" - }, - "0cae108e6db99b9e637876b064c6303eda8a65c8": { - "balance": "3000000000000000000000" - }, - "3883becc08b9be68ad3b0836aac3b620dc0017ef": { - "balance": "2000000000000000000000" - }, - "d0abcc70c0420e0e172f97d43b87d5e80c336ea9": { - "balance": "10000000000000000000000" - }, - "cbf16a0fe2745258cd52db2bf21954c975fc6a15": { - "balance": "300000000000000000000" - }, - "1b23cb8663554871fbbe0d9e60397efb6faedc3e": { - "balance": "200000000000000000000" - }, - "fbede32c349f3300ef4cd33b4de7dc18e443d326": { - "balance": "3160000000000000000000" - }, - "5e806e845730f8073e6cc9018ee90f5c05f909a3": { - "balance": "9480000000000000000000" - }, - "425c338a1325e3a1578efa299e57d986eb474f81": { - "balance": "2000000000000000000000" - }, - "8bf297f8f453523ed66a1acb7676856337b93bf0": { - "balance": "4000000000000000000000" - }, - "38e8a31af2d265e31a9fff2d8f46286d1245a467": { - "balance": "20000000000000000000" - }, - "7edafba8984baf631a820b6b92bbc2c53655f6bd": { - "balance": "2000000000000000000000" - }, - "aa0200f1d17e9c54da0647bb96395d57a78538d8": { - "balance": "1056000000000000000000" - }, - "433eb94a339086ed12d9bde9cd1d458603c97dd6": { - "balance": "100000000000000000000000" - }, - "cd7e47909464d871b9a6dc76a8e9195db3485e7a": { - "balance": "9850000000000000000000" - }, - "5975d78d974ee5bb9e4d4ca2ae77c84b9c3b4b82": { - "balance": "1370000000000000000000" - }, - "cea2896623f4910287a2bdc5be83aea3f2e6de08": { - "balance": "9359000000000000000000" - }, - "cb4ad0c723da46ab56d526da0c1d25c73daff10a": { - "balance": "510000000000000000000" - }, - "e2cf360aa2329eb79d2bf7ca04a27a17c532e4d8": { - "balance": "102000000000000000000" - }, - "ea60549ec7553f511d2149f2d4666cbd9243d93c": { - "balance": "2000000000000000000000" - }, - "cbb7be17953f2ccc93e1bc99805bf45511434e4c": { - "balance": "50440000000000000000000" - }, - "3549bd40bbbc2b30095cac8be2c07a0588e0aed6": { - "balance": "20000000000000000000" - }, - "6510df42a599bcb0a519cca961b488759a6f6777": { - "balance": "2000000000000000000000" - }, - "ed12a1ba1fb8adfcb20dfa19582e525aa3b74524": { - "balance": "6685000000000000000000" - }, - "135eb8c0e9e101deedec11f2ecdb66ae1aae8867": { - "balance": "20000000000000000000000" - }, - "ee906d7d5f1748258174be4cbc38930302ab7b42": { - "balance": "200000000000000000000" - }, - "253f1e742a2cec86b0d7b306e5eacb6ccb2f8554": { - "balance": "20040000000000000000000" - }, - "ecd1a62802351a41568d23033004acc6c005a5d3": { - "balance": "50000000000000000000" - }, - "558c54649a8a6e94722bd6d21d14714f71780534": { - "balance": "2000000000000000000000" - }, - "ca657ec06fe5bc09cf23e52af7f80cc3689e6ede": { - "balance": "900000000000000000000" - }, - "74bf7a5ab59293149b5c60cf364263e5ebf1aa0d": { - "balance": "115800000000000000000" - }, - "7a6d781c77c4ba1fcadf687341c1e31799e93d27": { - "balance": "274000000000000000000" - }, - "77028e409cc43a3bd33d21a9fc53ec606e94910e": { - "balance": "3880000000000000000000" - }, - "4781a10a4df5eebc82f4cfe107ba1d8a7640bd66": { - "balance": "1790000000000000000000" - }, - "78e08bc533413c26e291b3143ffa7cc9afb97b78": { - "balance": "200000000000000000000" - }, - "03ef6ad20ff7bd4f002bac58d47544cf879ae728": { - "balance": "6895000000000000000000" - }, - "0e3696cf1f4217b163d1bc12a5ea730f1c32a14a": { - "balance": "4000000000000000000000" - }, - "825135b1a7fc1605614c8aa4d0ac6dbad08f480e": { - "balance": "1430000000000000000000" - }, - "286b186d61ea1fd78d9930fe12b06537b05c3d51": { - "balance": "1000000000000000000000" - }, - "8d6657f59711b1f803c6ebef682f915b62f92dc9": { - "balance": "2000000000000000000000" - }, - "da8bbee182e455d2098acb338a6d45b4b17ed8b6": { - "balance": "2000000000000000000000" - }, - "3f2da093bb16eb064f8bfa9e30b929d15f8e1c4c": { - "balance": "2000000000000000000000" - }, - "f5d9cf00d658dd45517a48a9d3f5f633541a533d": { - "balance": "116400000000000000000" - }, - "c5f64babb7033142f20e46d7aa6201ed86f67103": { - "balance": "2000000000000000000000" - }, - "a2e2b5941e0c01944bfe1d5fb4e8a34b922ccfb1": { - "balance": "200000000000000000000" - }, - "6114b0eae5576903f80bfb98842d24ed92237f1e": { - "balance": "100000000000000000000" - }, - "38df0c4abe7ded5fe068eadf154ac691774324a4": { - "balance": "1790000000000000000000" - }, - "1c2010bd662df417f2a271879afb13ef4c88a3ae": { - "balance": "4000000000000000000000" - }, - "918967918cd897dd0005e36dc6c883ef438fc8c7": { - "balance": "140000000000000000000" - }, - "a522de7eb6ae1250522a513133a93bd42849475c": { - "balance": "20000000000000000000000" - }, - "7de442c82386154d2e993cbd1280bb7ca6b12ada": { - "balance": "4002000000000000000000" - }, - "66424bd8785b8cb461102a900283c35dfa07ef6a": { - "balance": "40221000000000000000" - }, - "7bbbec5e70bdead8bb32b42805988e9648c0aa97": { - "balance": "1000076000000000000000" - }, - "fec06fe27b44c784b2396ec92f7b923ad17e9077": { - "balance": "2000000000000000000000" - }, - "95d550427b5a514c751d73a0f6d29fb65d22ed10": { - "balance": "300000000000000000000" - }, - "8dde60eb08a099d7daa356daaab2470d7b025a6b": { - "balance": "197000000000000000000" - }, - "81bccbff8f44347eb7fca95b27ce7c952492aaad": { - "balance": "152240000000000000000" - }, - "3995e096b08a5a726800fcd17d9c64c64e088d2b": { - "balance": "200000000000000000000" - }, - "4ee13c0d41200b46d19dee5c4bcec71d82bb8e38": { - "balance": "7893915000000000000000" - }, - "c41461a3cfbd32c9865555a4813137c076312360": { - "balance": "999999000000000000000" - }, - "3300fb149aded65bcba6c04e9cd6b7a03b893bb1": { - "balance": "18200000000000000000" - }, - "29f9286c0e738d1721a691c6b95ab3d9a797ede8": { - "balance": "200000000000000000000000" - }, - "34c8e5f1330fcb4b14ca75cb2580a4b93d204e36": { - "balance": "2000000000000000000000" - }, - "ec5df227bfa85d7ad76b426e1cee963bc7f519dd": { - "balance": "1000000000000000000000" - }, - "797510e386f56393ced8f477378a444c484f7dad": { - "balance": "1000000000000000000000" - }, - "0191eb547e7bf6976b9b1b577546761de65622e2": { - "balance": "1999980000000000000000" - }, - "615a6f36777f40d6617eb5819896186983fd3731": { - "balance": "5910000000000000000000" - }, - "17580b766f7453525ca4c6a88b01b50570ea088c": { - "balance": "100000000000000000000" - }, - "945d96ea573e8df7262bbfa572229b4b16016b0f": { - "balance": "209300000000000000000" - }, - "2de0964400c282bdd78a919c6bf77c6b5f796179": { - "balance": "200000000000000000000" - }, - "304ec69a74545721d7316aef4dcfb41ac59ee2f0": { - "balance": "200000000000000000000" - }, - "be2b326e78ed10e550fee8efa8f8070396522f5a": { - "balance": "39400000000000000000000" - }, - "1a0841b92a7f7075569dc4627e6b76cab05ade91": { - "balance": "1520000000000000000000" - }, - "5fa61f152de6123516c751242979285f796ac791": { - "balance": "204000000000000000000" - }, - "68c8791dc342c373769ea61fb7b510f251d32088": { - "balance": "1000000000000000000000" - }, - "4167cd48e733418e8f99ffd134121c4a4ab278c4": { - "balance": "3640000000000000000000" - }, - "598aaabae9ed833d7bc222e91fcaa0647b77580b": { - "balance": "1800000000000000000000" - }, - "979f30158b574b999aab348107b9eed85b1ff8c1": { - "balance": "970000000000000000000" - }, - "3ad06149b21c55ff867cc3fb9740d2bcc7101231": { - "balance": "197000000000000000000000" - }, - "7133843a78d939c69d4486e10ebc7b602a349ff7": { - "balance": "329000000000000000000" - }, - "8bdfda6c215720eda2136f91052321af4e936c1f": { - "balance": "1000008000000000000000" - }, - "3e1c53300e4c168912163c7e99b95da268ad280a": { - "balance": "1003200000000000000000" - }, - "e07ebbc7f4da416e42c8d4f842aba16233c12580": { - "balance": "2000000000000000000000" - }, - "bac8922c4acc7d2cb6fd59a14eb45cf3e702214b": { - "balance": "800000000000000000000" - }, - "bb6c284aac8a69b75cddb00f28e145583b56bece": { - "balance": "2000000000000000000000" - }, - "0372ee5508bf8163ed284e5eef94ce4d7367e522": { - "balance": "100000000000000000000" - }, - "17125b59ac51cee029e4bd78d7f5947d1ea49bb2": { - "balance": "22000000000000000000000" - }, - "c88ca1e6e5f4d558d13780f488f10d4ad3130d34": { - "balance": "1550000000000000000000" - }, - "a825fd5abb7926a67cf36ba246a24bd27be6f6ed": { - "balance": "17600000000000000000" - }, - "04241b41ecbd0bfdf1295e9d4fa59ea09e6c6186": { - "balance": "1870000000000000000000" - }, - "6de4d15219182faf3aa2c5d4d2595ff23091a727": { - "balance": "1580000000000000000000" - }, - "b203d29e6c56b92699c4b92d1f6f84648dc4cfbc": { - "balance": "400000000000000000000" - }, - "80b42de170dbd723f454e88f7716452d92985092": { - "balance": "300202000000000000000" - }, - "0a5b79d8f23b6483dbe2bdaa62b1064cc76366ae": { - "balance": "1969803000000000000000" - }, - "32034e8581d9484e8af42a28df190132ec29c466": { - "balance": "3460000000000000000000" - }, - "7ee604c7a9dc2909ce321de6b9b24f5767577555": { - "balance": "5533575000000000000000" - }, - "a387ce4e961a7847f560075c64e1596b5641d21c": { - "balance": "668500000000000000000" - }, - "fcc9d4a4262e7a027ab7519110d802c495ceea39": { - "balance": "6370000000000000000000" - }, - "ff8a2ca5a81333f19998255f203256e1a819c0aa": { - "balance": "224000000000000000000" - }, - "f9811fa19dadbf029f8bfe569adb18228c80481a": { - "balance": "200000000000000000000" - }, - "0d1f2a57713ebc6e94de29846e8844d376665763": { - "balance": "5000000000000000000000" - }, - "eab0bd148309186cf8cbd13b7232d8095acb833a": { - "balance": "10691800000000000000000" - }, - "36928b55bc861509d51c8cf1d546bfec6e3e90af": { - "balance": "1970000000000000000000" - }, - "30480164bcd84974ebc0d90c9b9afab626cd1c73": { - "balance": "800000000000000000000" - }, - "36339f84a5c2b44ce53dfdb6d4f97df78212a7df": { - "balance": "321600000000000000000" - }, - "cfeacaaed57285e0ac7268ce6a4e35ecfdb242d7": { - "balance": "1086400000000000000000" - }, - "572dd8cd3fe399d1d0ec281231b7cefc20b9e4bb": { - "balance": "10400000000000000000000" - }, - "5dded049a6e1f329dc4b971e722c9c1f2ade83f0": { - "balance": "1000000000000000000000" - }, - "9756e176c9ef693ee1eec6b9f8b151d313beb099": { - "balance": "1200000000000000000000" - }, - "01e6415d587b065490f1ed7f21d6e0f386ee6747": { - "balance": "2000000000000000000000" - }, - "b4413576869c08f9512ad311fe925988a52d3414": { - "balance": "10000000000000000000000" - }, - "da9f55460946d7bfb570ddec757ca5773b58429a": { - "balance": "507600000000000000000" - }, - "7180b83ee5574317f21c8072b191d895d46153c3": { - "balance": "460000000000000000000" - }, - "0aca9a5626913b08cfc9a66d40508dce52b60f87": { - "balance": "1910000000000000000000" - }, - "5cd0e475b54421bdfc0c12ea8e082bd7a5af0a6a": { - "balance": "59000000000000000000" - }, - "7edb02c61a227287611ad950696369cc4e647a68": { - "balance": "274000000000000000000" - }, - "b2676841ee9f2d31c172e82303b0fe9bbf9f1e09": { - "balance": "200000000000000000000" - }, - "a2222259dd9c3e3ded127084f808e92a1887302c": { - "balance": "162000000000000000000" - }, - "4b3a7cc3a7d7b00ed5282221a60259f25bf6538a": { - "balance": "1000000000000000000000" - }, - "e33ff987541dde5cdee0a8a96dcc3f33c3f24cc2": { - "balance": "200000000000000000000000" - }, - "1e1a4828119be309bd88236e4d482b504dc55711": { - "balance": "2955000000000000000000" - }, - "9b1811c3051f46e664ae4bc9c824d18592c4574a": { - "balance": "199955000000000000000" - }, - "59fe00696dbd87b7976b29d1156c8842a2e17914": { - "balance": "2000000000000000000000" - }, - "48010ef3b8e95e3f308f30a8cb7f4eb4bf60d965": { - "balance": "2000000000000000000000" - }, - "c90300cb1d4077e6a6d7e169a460468cf4a492d7": { - "balance": "2000000000000000000000" - }, - "6dedf62e743f4d2c2a4b87a787f5424a7aeb393c": { - "balance": "180000000000000000000" - }, - "fb744b951d094b310262c8f986c860df9ab1de65": { - "balance": "52009000000000000000" - }, - "193ac65183651800e23580f8f0ead3bb597eb8a4": { - "balance": "50020000000000000000" - }, - "bf05ff5ecf0df2df887759fb8274d93238ac267d": { - "balance": "800000000000000000000" - }, - "6c0e712f405c59725fe829e9774bf4df7f4dd965": { - "balance": "57413800000000000000000" - }, - "2744ff67464121e35afc2922177164fa2fcb0267": { - "balance": "100000000000000000000" - }, - "d09cb2e6082d693a13e8d2f68dd1dd8461f55840": { - "balance": "1000000000000000000000" - }, - "bc171e53d17ac9b61241ae436deec7af452e7496": { - "balance": "5348000000000000000000" - }, - "71fa22cc6d33206b7d701a163a0dab31ae4d31d6": { - "balance": "1610000000000000000000" - }, - "4da8030769844bc34186b85cd4c7348849ff49e9": { - "balance": "10000000000000000000000" - }, - "c8616b4ec09128cdff39d6e4b9ac86eec471d5f2": { - "balance": "19400000000000000000" - }, - "407295ebd94b48269c2d569c9b9af9aa05e83e5e": { - "balance": "10000000000000000000000" - }, - "d45d5daa138dd1d374c71b9019916811f4b20a4e": { - "balance": "576000000000000000000" - }, - "42c6edc515d35557808d13cd44dcc4400b2504e4": { - "balance": "197876000000000000000" - }, - "0bc95cb32dbb574c832fa8174a81356d38bc92ac": { - "balance": "2000000000000000000000" - }, - "5a6071bcebfcba4ab57f4db96fc7a68bece2ba5b": { - "balance": "2000000000000000000000" - }, - "54c93e03a9b2e8e4c3672835a9ee76f9615bc14e": { - "balance": "19400000000000000000" - }, - "3c03bbc023e1e93fa3a3a6e428cf0cd8f95e1ec6": { - "balance": "1520000000000000000000" - }, - "ba1531fb9e791896bcf3a80558a359f6e7c144bd": { - "balance": "3940000000000000000000" - }, - "aa56a65dc4abb72f11bae32b6fbb07444791d5c9": { - "balance": "748600000000000000000" - }, - "e437acbe0f6227b0e36f36e4bcf7cf613335fb68": { - "balance": "200000000000000000000" - }, - "39d4a931402c0c79c457186f24df8729cf957031": { - "balance": "4000000000000000000000" - }, - "e22b20c77894463baf774cc256d5bddbbf7ddd09": { - "balance": "1000000000000000000000" - }, - "70a4067d448cc25dc8e70e651cea7cf84e92109e": { - "balance": "176000000000000000000" - }, - "aa3925dc220bb4ae2177b2883078b6dc346ca1b2": { - "balance": "8000000000000000000000" - }, - "ad57aa9d00d10c439b35efcc0becac2e3955c313": { - "balance": "200000000000000000000" - }, - "e93d47a8ca885d540c4e526f25d5c6f2c108c4b8": { - "balance": "112640000000000000000000" - }, - "232ce782506225fd9860a2edc14a7a3047736da2": { - "balance": "20000000000000000000" - }, - "49a645e0667dfd7b32d075cc2467dd8c680907c4": { - "balance": "129560000000000000000" - }, - "cf2e734042a355d05ffb2e3915b16811f45a695e": { - "balance": "2000000000000000000000" - }, - "39b1c471ae94e12164452e811fbbe2b3cd7275ac": { - "balance": "2000000000000000000000" - }, - "ffad3dd74e2c1f796ac640de56dc99b4c792a402": { - "balance": "5000000000000000000000" - }, - "a69d7cd17d4842fe03f62a90b2fbf8f6af7bb380": { - "balance": "100000000000000000000" - }, - "2001bef77b66f51e1599b02fb110194a0099b78d": { - "balance": "2000000000000000000000" - }, - "95e7616424cd0961a71727247437f0069272280e": { - "balance": "400000000000000000000" - }, - "c04f4bd4049f044685b883b62959ae631d667e35": { - "balance": "5820000000000000000000" - }, - "ede0147ec032c3618310c1ff25690bf172193dac": { - "balance": "2000000000000000000000" - }, - "66719c0682b2ac7f9e27abebec7edf8decf0ae0d": { - "balance": "20000000000000000000" - }, - "45272b8f62e9f9fa8ce04420e1aea3eba9686eac": { - "balance": "4000000000000000000000" - }, - "d1da0c8fb7c210e0f2ec618f85bdae7d3e734b1c": { - "balance": "1970000000000000000000" - }, - "e9133e7d31845d5f2b66a2618792e869311acf66": { - "balance": "24050000000000000000000" - }, - "ebb62cf8e22c884b1b28c6fa88fbbc17938aa787": { - "balance": "798000000000000000000" - }, - "6205c2d5647470848a3840f3887e9b015d34755c": { - "balance": "1800000000000000000000" - }, - "76ca22bcb8799e5327c4aa2a7d0949a1fcce5f29": { - "balance": "1524180000000000000000" - }, - "6b925dd5d8ed6132ab6d0860b82c44e1a51f1fee": { - "balance": "1480000000000000000000" - }, - "797bb7f157d9feaa17f76da4f704b74dc1038341": { - "balance": "3340000000000000000000" - }, - "ae8954f8d6166de507cf61297d0fc7ca6b9e7128": { - "balance": "300000000000000000000" - }, - "75c1ad23d23f24b384d0c3149177e86697610d21": { - "balance": "6426082000000000000000" - }, - "805d846fb0bc02a7337226d685be9ee773b9198a": { - "balance": "19999800000000000000000" - }, - "c3cb6b36af443f2c6e258b4a39553a818747811f": { - "balance": "1610000000000000000000" - }, - "cea43f7075816b60bbfce68b993af0881270f6c4": { - "balance": "2000000000000000000000" - }, - "e0388aeddd3fe2ad56f85748e80e710a34b7c92e": { - "balance": "500000000000000000000" - }, - "e131f87efc5ef07e43f0f2f4a747b551d750d9e6": { - "balance": "19999000000000000000000" - }, - "c2b2cbe65bc6c2ee7a3c75b2e47c189c062e8d8b": { - "balance": "20000000000000000000000" - }, - "bd8765f41299c7f479923c4fd18f126d7229047d": { - "balance": "4000000000000000000000" - }, - "c83ba6dd9549be1d3287a5a654d106c34c6b5da2": { - "balance": "7000000000000000000000" - }, - "f870995fe1e522321d754337a45c0c9d7b38951c": { - "balance": "20000000000000000000" - }, - "0d8ed7d0d15638330ed7e4eaccab8a458d75737e": { - "balance": "2000000000000000000000" - }, - "36c510bf8d6e569bf2f37d47265dbcb502ff2bce": { - "balance": "30000000000000000000000" - }, - "0eccf617844fd61fba62cb0e445b7ac68bcc1fbe": { - "balance": "387260000000000000000" - }, - "ae10e27a014f0d306baf266d4897c89aeee2e974": { - "balance": "20000000000000000000000" - }, - "1827039f09570294088fddf047165c33e696a492": { - "balance": "9550000000000000000000" - }, - "23378f42926d0184b793b0c827a6dd3e3d334fcd": { - "balance": "56000000000000000000" - }, - "467124ae7f452f26b3d574f6088894fa5d1cfb3b": { - "balance": "2700000000000000000000" - }, - "aae61e43cb0d0c96b30699f77e00d711d0a3979b": { - "balance": "1000000000000000000000" - }, - "15c7edb8118ee27b342285eb5926b47a855bc7a5": { - "balance": "20000000000000000000" - }, - "0d5d98565c647ca5f177a2adb9d3022fac287f21": { - "balance": "200000000000000000000" - }, - "7222fec7711781d26eaa4e8485f7aa3fac442483": { - "balance": "456000000000000000000" - }, - "dc44275b1715baea1b0345735a29ac42c9f51b4f": { - "balance": "1164000000000000000000" - }, - "04d82af9e01a936d97f8f85940b970f9d4db9936": { - "balance": "200000000000000000000" - }, - "45533390e340fe0de3b3cf5fb9fc8ea552e29e62": { - "balance": "1460000000000000000000" - }, - "1284f0cee9d2ff2989b65574d06ffd9ab0f7b805": { - "balance": "400000000000000000000" - }, - "ed9ebccba42f9815e78233266dd6e835b6afc31b": { - "balance": "6000000000000000000000" - }, - "e4324912d64ea3aef76b3c2ff9df82c7e13ae991": { - "balance": "2000000000000000000000" - }, - "94c742fd7a8b7906b3bfe4f8904fc0be5c768033": { - "balance": "20000000000000000000000" - }, - "62fb8bd1f0e66b90533e071e6cbe6111fef0bc63": { - "balance": "17600000000000000000000" - }, - "2c83aeb02fcf067d65a47082fd977833ab1cec91": { - "balance": "150400000000000000000" - }, - "06cbfa08cdd4fba737bac407be8224f4eef35828": { - "balance": "593459000000000000000" - }, - "67ee406ea4a7ae6a3a381eb4edd2f09f174b4928": { - "balance": "1036000000000000000000" - }, - "83c23d8a502124ee150f08d71dc6727410a0f901": { - "balance": "33999600000000000000000" - }, - "f7c00cdb1f020310d5acab7b496aaa44b779085e": { - "balance": "1670000000000000000000" - }, - "d096565b7c7407d06536580355fdd6d239144aa1": { - "balance": "250000000000000000000" - }, - "f8d52dcc5f96cc28007b3ecbb409f7e22a646caa": { - "balance": "149200000000000000000" - }, - "0c222c7c41c9b048efcce0a232434362e12d673b": { - "balance": "10007600000000000000000" - }, - "503bdbd8bc421c32a443032deb2e3e4cd5ba8b4e": { - "balance": "2000000000000000000000" - }, - "77da5e6c72fb36bce1d9798f7bcdf1d18f459c2e": { - "balance": "22380000000000000000" - }, - "e62f98650712eb158753d82972b8e99ca3f61877": { - "balance": "2000000000000000000000" - }, - "87a7c508ef71582dd9a54372f89cb01f252fb180": { - "balance": "200000000000000000000" - }, - "f61283b4bd8504058ca360e993999b62cbc8cd67": { - "balance": "255000000000000000000" - }, - "9ccddcb2cfc2b25b08729a0a98d9e6f0202ea2c1": { - "balance": "100000000000000000000" - }, - "d460a4b908dd2b056759b488850b66a838fc77a8": { - "balance": "1970000000000000000000" - }, - "5431b1d18751b98fc9e2888ac7759f1535a2db47": { - "balance": "2000000000000000000000" - }, - "da2a14f9724015d79014ed8e5909681d596148f1": { - "balance": "48499000000000000000" - }, - "c989434f825aaf9c552f685eba7c11db4a5fc73a": { - "balance": "501000000000000000000" - }, - "2b701d16c0d3cc1e4cd85445e6ad02eea4ac012d": { - "balance": "600000000000000000000" - }, - "78b978a9d7e91ee529ea4fc4b76feaf8762f698c": { - "balance": "32000000000000000000000" - }, - "c89cf504b9f3f835181fd8424f5ccbc8e1bddf7d": { - "balance": "10000000000000000000000" - }, - "e94941b6036019b4016a30c1037d5a6903babaad": { - "balance": "780000000000000000000" - }, - "95d98d0c1069908f067a52acac2b8b534da37afd": { - "balance": "2054053000000000000000" - }, - "8284923b62e68bbf7c2b9f3414d13ef6c812a904": { - "balance": "3880000000000000000000" - }, - "3e5a39fdda70df1126ab0dc49a7378311a537a1f": { - "balance": "2400000000000000000000" - }, - "a2ace4c993bb1e5383f8ac74e179066e814f0591": { - "balance": "100000000000000000000" - }, - "0609d83a6ce1ffc9b690f3e9a81e983e8bdc4d9d": { - "balance": "70000000000000000000000" - }, - "d119417c46732cf34d1a1afb79c3e7e2cd8eece4": { - "balance": "2000000000000000000000" - }, - "fdb33944f2360615e5be239577c8a19ba52d9887": { - "balance": "601650000000000000000" - }, - "dd95dbe30f1f1877c5dd7684aeef302ab6885192": { - "balance": "8372000000000000000000" - }, - "413f4b02669ccff6806bc826fcb7deca3b0ea9bc": { - "balance": "20000000000000000000" - }, - "5800cd8130839e94495d2d8415a8ea2c90e0c5cb": { - "balance": "200000000000000000000" - }, - "65053191319e067a25e6361d47f37f6318f83419": { - "balance": "394000000000000000000" - }, - "9bc573bcda23b8b26f9073d90c230e8e71e0270b": { - "balance": "999544000000000000000" - }, - "97f7760657c1e202759086963eb4211c5f8139b9": { - "balance": "49770000000000000000000" - }, - "126897a311a14ad43b78e0920100c4426bfd6bdd": { - "balance": "973581000000000000000" - }, - "d5276f0cd5ffd5ffb63f98b5703d5594ede0838b": { - "balance": "400000000000000000000" - }, - "e9c35c913ca1fceab461582fe1a5815164b4fd21": { - "balance": "8000000000000000000000" - }, - "b43067fe70d9b55973ba58dc64dd7f311e554259": { - "balance": "200000000000000000000" - }, - "6f8f0d15cc96fb7fe94f1065bc6940f8d12957b2": { - "balance": "1000000000000000000000" - }, - "b1dba5250ba9625755246e067967f2ad2f0791de": { - "balance": "80000000000000000000000" - }, - "72b7a03dda14ca9c661a1d469fd33736f673c8e8": { - "balance": "2000000000000000000000" - }, - "e792349ce9f6f14f81d0674096befa1f9221cdea": { - "balance": "1685365000000000000000" - }, - "1815279dff9952da3be8f77249dbe22243377be7": { - "balance": "4749800000000000000000" - }, - "33481e856ebed48ea708a27426ef28e867f57cd1": { - "balance": "200000000000000000000" - }, - "8eb8c71982a00fb84275293253f8044544b66b49": { - "balance": "400000000000000000000" - }, - "65f5870f26bce089677dfc23b5001ee492483428": { - "balance": "5067230000000000000000" - }, - "8e23facd12c765c36ab81a6dd34d8aa9e68918ae": { - "balance": "167310000000000000000" - }, - "4912d902931676ff39fc34fe3c3cc8fb2182fa7a": { - "balance": "20000000000000000000" - }, - "c09a66172aea370d9a63da04ff71ffbbfcff7f94": { - "balance": "2000000000000000000000" - }, - "e969ea1595edc5c4a707cfde380929633251a2b0": { - "balance": "200000000000000000000" - }, - "4f2b47e2775a1fa7178dad92985a5bbe493ba6d6": { - "balance": "200000000000000000000" - }, - "cab9a97ada065c87816e6860a8f1426fe6b3d775": { - "balance": "1000000000000000000000" - }, - "cdfd8217339725d7ebac11a63655f265eff1cc3d": { - "balance": "4999962000000000000000" - }, - "ab4004c0403f7eabb0ea586f212156c4203d67f1": { - "balance": "1999944000000000000000" - }, - "1c7cb2fe6bf3e09cbcdc187af38fa8f5053a70b6": { - "balance": "9970823000000000000000" - }, - "a951b244ff50cfae591d5e1a148df6a938ef2a1a": { - "balance": "1734000000000000000000" - }, - "b158db43fa62d30e65f3d09bf781c7b67372ebaa": { - "balance": "1999000000000000000000" - }, - "25e037f00a18270ba5ec3420229ddb0a2ce38fa2": { - "balance": "10000000000000000000000" - }, - "2aaea1f1046f30f109faec1c63ef5c7594eb08da": { - "balance": "4000000000000000000000" - }, - "73d7269ff06c9ffd33754ce588f74a966abbbbba": { - "balance": "6600000000000000000000" - }, - "4c767b65fd91161f4fbdcc6a69e2f6ad711bb918": { - "balance": "720000000000000000000" - }, - "92ae5b7c7eb492ff1ffa16dd42ad9cad40b7f8dc": { - "balance": "865000000000000000000" - }, - "a04f2ae02add14c12faf65cb259022d0830a8e26": { - "balance": "100000000000000000000000" - }, - "63ef2fbc3daf5edaf4a295629ccf31bcdf4038e5": { - "balance": "1460000000000000000000" - }, - "749ad6f2b5706bbe2f689a44c4b640b58e96b992": { - "balance": "100000000000000000000" - }, - "4d836d9d3b0e2cbd4de050596faa490cffb60d5d": { - "balance": "300000000000000000000" - }, - "59f6247b0d582aaa25e5114765e4bf3c774f43c2": { - "balance": "50000000000000000000" - }, - "1293c78c7d6a443b9d74b0ba5ee7bb47fd418588": { - "balance": "6685000000000000000000" - }, - "67bc85e87dc34c4e80aafa066ba8d29dbb8e438e": { - "balance": "402500000000000000000" - }, - "a09f4d5eaa65a2f4cb750a49923401dae59090af": { - "balance": "140000000000000000000" - }, - "ebbd4db9019952d68b1b0f6d8cf0683c00387bb5": { - "balance": "332330000000000000000" - }, - "b16479ba8e7df8f63e1b95d149cd8529d735c2da": { - "balance": "846477000000000000000" - }, - "e1b2aca154b8e0766c4eba30bc10c7f35036f368": { - "balance": "19980000000000000000" - }, - "5c464197791c8a3da3c925436f277ab13bf2faa2": { - "balance": "8000000000000000000000" - }, - "170a88a8997f92d238370f1affdee6347050b013": { - "balance": "3000800000000000000000" - }, - "dadbfafd8b62b92a24efd75256dd83abdbd7bbdb": { - "balance": "19700000000000000000" - }, - "bb993b96ee925ada7d99d786573d3f89180ce3aa": { - "balance": "2000000000000000000000" - }, - "f2c362b0ef991bc82fb36e66ff75932ae8dd8225": { - "balance": "74000000000000000000" - }, - "7f2382ffd8f83956467937f9ba72374623f11b38": { - "balance": "600000000000000000000" - }, - "74d1a4d0c7524e018d4e06ed3b648092b5b6af2c": { - "balance": "50000000000000000000" - }, - "24a750eae5874711116dd7d47b7186ce990d3103": { - "balance": "200000000000000000000" - }, - "a8e42a4e33d7526cca19d9a36dcd6e8040d0ea73": { - "balance": "1080000000000000000000" - }, - "3e1b2230afbbd310b4926a4c776d5ae7819c661d": { - "balance": "30000000000000000000000" - }, - "6af9f0dfeeaebb5f64bf91ab771669bf05295553": { - "balance": "400000000000000000000" - }, - "41e4a20275e39bdcefeb655c0322744b765140c2": { - "balance": "10000000000000000000000" - }, - "ceb089ec8a78337e8ef88de11b49e3dd910f748f": { - "balance": "1000000000000000000000" - }, - "e6bcd30a8fa138c5d9e5f6c7d2da806992812dcd": { - "balance": "260000000000000000000000" - }, - "e08c60313106e3f9334fe6f7e7624d211130c077": { - "balance": "40000000000000000000" - }, - "f5cffbba624e7eb321bc83c60ca68199b4e36671": { - "balance": "2000000000000000000000" - }, - "d7c2803ed7b0e0837351411a8e6637d168bc5b05": { - "balance": "29549015000000000000000" - }, - "0f3665d48e9f1419cd984fc7fa92788710c8f2e4": { - "balance": "2000000000000000000000" - }, - "b48921c9687d5510744584936e8886bdbf2df69b": { - "balance": "1000000000000000000000" - }, - "a94bbb8214cf8da0c2f668a2ac73e86248528d4b": { - "balance": "960000000000000000000" - }, - "be0c2a80b9de084b172894a76cf4737a4f529e1a": { - "balance": "1999944000000000000000" - }, - "fcf199f8b854222f182e4e1d099d4e323e2aae01": { - "balance": "1000000000000000000000" - }, - "b52dfb45de5d74e3df208332bc571c809b8dcf32": { - "balance": "6000000000000000000000" - }, - "704819d2e44d6ed1da25bfce84c49fcca25613e5": { - "balance": "400000000000000000000" - }, - "6ff6cc90d649de4e96cffee1077a5b302a848dcb": { - "balance": "28600000000000000000" - }, - "4d9c77d0750c5e6fbc247f2fd79274686cb353d6": { - "balance": "20000000000000000000" - }, - "68e8022740f4af29eb48db32bcecddfd148d3de3": { - "balance": "1000000000000000000000" - }, - "2cb615073a40dcdb99faa848572e987b3b056efb": { - "balance": "799600000000000000000" - }, - "64adcceec53dd9d9dd15c8cc1a9e736de4241d2c": { - "balance": "56000000000000000000" - }, - "2aec809df9325b9f483996e99f7331097f08aa0e": { - "balance": "4000000000000000000000" - }, - "438c2f54ff8e629bab36b1442b760b12a88f02ae": { - "balance": "2000000000000000000000" - }, - "9e35399071a4a101e9194daa3f09f04a0b5f9870": { - "balance": "4000000000000000000000" - }, - "a5c336083b04f9471b8c6ed73679b74d66c363ec": { - "balance": "3014100000000000000000" - }, - "7ad3f307616f19dcb143e6444dab9c3c33611f52": { - "balance": "50000000000000000000" - }, - "455cb8ee39ffbc752331e5aefc588ef0ee593454": { - "balance": "999963000000000000000" - }, - "c4c01afc3e0f045221da1284d7878574442fb9ac": { - "balance": "7419944000000000000000" - }, - "99268327c373332e06c3f6164287d455b9d5fa4b": { - "balance": "2000000000000000000000" - }, - "4367ae4b0ce964f4a54afd4b5c368496db169e9a": { - "balance": "2000000000000000000000" - }, - "2cd79eb52027b12c18828e3eaab2969bfcd287e9": { - "balance": "20000000000000000000" - }, - "b96841cabbc7dbd69ef0cf8f81dff3c8a5e21570": { - "balance": "12000000000000000000000" - }, - "d7ebddb9f93987779b680155375438db65afcb6a": { - "balance": "100600000000000000000" - }, - "0631d18bbbbd30d9e1732bf36edae2ce8901ab80": { - "balance": "3024800000000000000000" - }, - "5fad960f6b2c84569c9f4d47bf1985fcb2c65da6": { - "balance": "999972000000000000000" - }, - "01d599ee0d5f8c38ab2d392e2c65b74c3ce31820": { - "balance": "510000000000000000000" - }, - "ff0cc8dac824fa24fc3caa2169e6e057cf638ad6": { - "balance": "4000000000000000000000" - }, - "c25266c7676632f13ef29be455ed948add567792": { - "balance": "1337000000000000000000" - }, - "9c344098ba615a398f11d009905b177c44a7b602": { - "balance": "1000000000000000000000" - }, - "3b0accaf4b607cfe61d17334c214b75cdefdbd89": { - "balance": "2000000000000000000000" - }, - "6d6634b5b8a40195d949027af4828802092ceeb6": { - "balance": "3000000000000000000000" - }, - "208c45732c0a378f17ac8324926d459ba8b658b4": { - "balance": "2955000000000000000000" - }, - "c24399b4bf86f7338fbf645e3b22b0e0b7973912": { - "balance": "2000000000000000000000" - }, - "29763dd6da9a7c161173888321eba6b63c8fb845": { - "balance": "328000000000000000000" - }, - "9c2fd54089af665df5971d73b804616039647375": { - "balance": "1000000000000000000000" - }, - "0e09646c99af438e99fa274cb2f9c856cb65f736": { - "balance": "1910000000000000000000" - }, - "be73274d8c5aa44a3cbefc8263c37ba121b20ad3": { - "balance": "500000000000000000000" - }, - "ecfd004d02f36cd4d8b4a8c1a9533b6af85cd716": { - "balance": "5003800000000000000000" - }, - "f978b025b64233555cc3c19ada7f4199c9348bf7": { - "balance": "400000000000000000000000" - }, - "705ddd38355482b8c7d3b515bda1500dd7d7a817": { - "balance": "400000000000000000000" - }, - "2b8a0dee5cb0e1e97e15cfca6e19ad21f995efad": { - "balance": "504206000000000000000" - }, - "1098cc20ef84bad5146639c4cd1ca6c3996cb99b": { - "balance": "18200000000000000000" - }, - "afdac5c1cb56e245bf70330066a817eaafac4cd1": { - "balance": "20000000000000000000" - }, - "910e996543344c6815fb97cda7af4b8698765a5b": { - "balance": "103400000000000000000" - }, - "94612781033b57b146ee74e753c672017f5385e4": { - "balance": "3600000000000000000000" - }, - "d03fc165576aaed525e5502c8e140f8b2e869639": { - "balance": "6850000000000000000000" - }, - "293384c42b6f8f2905ce52b7205c2274376c612b": { - "balance": "1400000000000000000000" - }, - "09ee12b1b42b05af9cf207d5fcac255b2ec411f2": { - "balance": "58929000000000000000" - }, - "dbd71efa4b93c889e76593de609c3b04cbafbe08": { - "balance": "20000000000000000000" - }, - "fa86ca27bf2854d98870837fb6f6dfe4bf6453fc": { - "balance": "322061000000000000000" - }, - "61ff8e67b34d9ee6f78eb36ffea1b9f7c15787af": { - "balance": "1640000000000000000000" - }, - "6d4cbf3d8284833ae99344303e08b4d614bfda3b": { - "balance": "12000000000000000000000" - }, - "2ff160c44f72a299b5ec2d71e28ce5446d2fcbaf": { - "balance": "360000000000000000000" - }, - "94a7cda8f481f9d89d42c303ae1632b3b709db1d": { - "balance": "300000000000000000000" - }, - "7566496162ba584377be040a4f87777a707acaeb": { - "balance": "4000000000000000000000" - }, - "bdc461462b6322b462bdb33f22799e8108e2417d": { - "balance": "668500000000000000000" - }, - "7e47637e97c14622882be057bea229386f4052e5": { - "balance": "440000000000000000000" - }, - "3b5c251d7fd7893ba209fe541cecd0ce253a990d": { - "balance": "30000000000000000000000" - }, - "0e498800447177b8c8afc3fdfa7f69f4051bb629": { - "balance": "2140234000000000000000" - }, - "b71623f35107cf7431a83fb3d204b29ee0b1a7f4": { - "balance": "19700000000000000000" - }, - "1d395b30adda1cf21f091a4f4a7b753371189441": { - "balance": "100000000000000000000000" - }, - "2c2428e4a66974edc822d5dbfb241b2728075158": { - "balance": "2000000000000000000000" - }, - "a575f2891dcfcda83c5cf01474af11ee01b72dc2": { - "balance": "100076000000000000000" - }, - "ad728121873f0456d0518b80ab6580a203706595": { - "balance": "500000000000000000000" - }, - "48669eb5a801d8b75fb6aa58c3451b7058c243bf": { - "balance": "30940000000000000000000" - }, - "b3ae54fba09d3ee1d6bdd1e957923919024c35fa": { - "balance": "65513000000000000000" - }, - "0d35408f226566116fb8acdaa9e2c9d59b76683f": { - "balance": "940000000000000000000" - }, - "df211cd21288d6c56fae66c3ff54625dd4b15427": { - "balance": "2500024000000000000000" - }, - "8a746c5d67064711bfca685b95a4fe291a27028e": { - "balance": "40000000000000000000" - }, - "1cf105ab23023b554c583e86d7921179ee83169f": { - "balance": "1970000000000000000000" - }, - "8cfedef198db0a9143f09129b3fd64dcbb9b4956": { - "balance": "2000000000000000000000" - }, - "1e381adcf801a3bf9fd7bfac9ccc2b8482ad5e66": { - "balance": "600200000000000000000" - }, - "e74608f506866ada6bfbfdf20fea440be76989ef": { - "balance": "1999944000000000000000" - }, - "27e63989ca1e903bc620cf1b9c3f67b9e2ae6581": { - "balance": "1337000000000000000000" - }, - "bb0857f1c911b24b86c8a70681473fe6aaa1cce2": { - "balance": "100000000000000000000" - }, - "4f8e8d274fb22a3fd36a47fe72980471544b3434": { - "balance": "200000000000000000000" - }, - "127d3fc5003bf63c0d83e93957836515fd279045": { - "balance": "111890000000000000000" - }, - "95809e8da3fbe4b7f281f0b8b1715f420f7d7d63": { - "balance": "2000000000000000000000" - }, - "28904bb7c4302943b709b14d7970e42b8324e1a1": { - "balance": "10027500000000000000000" - }, - "c07e3867ada096807a051a6c9c34cc3b3f4ad34a": { - "balance": "1788210000000000000000" - }, - "f0b469eae89d400ce7d5d66a9695037036b88903": { - "balance": "20000000000000000000000" - }, - "7445202f0c74297a004eb3726aa6a82dd7c02fa1": { - "balance": "2000000000000000000000" - }, - "c58f62fee9711e6a05dc0910b618420aa127f288": { - "balance": "3980000000000000000000" - }, - "801d65c518b11d0e3f4f470221417013c8e53ec5": { - "balance": "4000000000000000000000" - }, - "41010fc8baf8437d17a04369809a168a17ca56fb": { - "balance": "100000000000000000000" - }, - "a1998144968a5c70a6415554cefec2824690c4a5": { - "balance": "20000000000000000000" - }, - "e9559185f166fc9513cc71116144ce2deb0f1d4b": { - "balance": "20000000000000000000000" - }, - "ed5b4c41e762d942404373caf21ed4615d25e6c1": { - "balance": "2013960000000000000000" - }, - "665b000f0b772750cc3c217a5ef429a92bf1ccbb": { - "balance": "4000000000000000000000" - }, - "febd9f81cf78bd5fb6c4b9a24bd414bb9bfa4c4e": { - "balance": "1990019000000000000000" - }, - "a072691c8dd7cd4237ff72a75c1a9506d0ce5b9e": { - "balance": "370000000000000000000" - }, - "6765df25280e8e4f38d4b1cf446fc5d7eb659e34": { - "balance": "100000000000000000000" - }, - "524fb210522c5e23bb67dfbf8c26aa616da49955": { - "balance": "999971000000000000000" - }, - "e987e6139e6146a717fef96bc24934a5447fe05d": { - "balance": "2000000000000000000000" - }, - "d6110276cfe31e42825a577f6b435dbcc10cf764": { - "balance": "1000000000000000000000" - }, - "5e51b8a3bb09d303ea7c86051582fd600fb3dc1a": { - "balance": "20000000000000000000" - }, - "5c4f24e994ed8f850ea7818f471c8fac3bcf0452": { - "balance": "1724800000000000000000" - }, - "85b2998d0c73302cb2ba13f489313301e053be15": { - "balance": "10000000000000000000000" - }, - "0af6c8d539c96d50259e1ba6719e9c8060f388c2": { - "balance": "1000000000000000000000" - }, - "7d901b28bf7f88ef73d8f73cca97564913ea8a24": { - "balance": "955000000000000000000" - }, - "e01859f242f1a0ec602fa8a3b0b57640ec89075e": { - "balance": "555000000000000000000" - }, - "c66ae4cee87fb3353219f77f1d6486c580280332": { - "balance": "29550000000000000000" - }, - "2d40558b06f90a3923145592123b6774e46e31f4": { - "balance": "1000000000000000000000" - }, - "ccf43975b76bfe735fec3cb7d4dd24f805ba0962": { - "balance": "60000000000000000000" - }, - "1703b4b292b8a9deddede81bb25d89179f6446b6": { - "balance": "19690000000000000000000" - }, - "0e9096d343c060db581a120112b278607ec6e52b": { - "balance": "20000000000000000000" - }, - "f65819ac4cc14c137f05dd7977c7dae08d1a4ab5": { - "balance": "102000000000000000000" - }, - "ca373fe3c906b8c6559ee49ccd07f37cd4fb5266": { - "balance": "1790000000000000000000" - }, - "d28298524df5ec4b24b0ffb9df85170a145a9eb5": { - "balance": "287700000000000000000" - }, - "5fcda847aaf8d7fa8bca08029ca2849166aa15a3": { - "balance": "623350000000000000000" - }, - "bdc739a699700b2e8e2c4a4c7b058a0e513ddebe": { - "balance": "2000000000000000000000" - }, - "0bb05f7224bb5804856556c07eeadbed87ba8f7c": { - "balance": "401100000000000000000" - }, - "ab416fe30d58afe5d9454c7fce7f830bcc750356": { - "balance": "114515000000000000000" - }, - "3eee6f1e96360b7689b3069adaf9af8eb60ce481": { - "balance": "1000000000000000000000" - }, - "9a0d3cee3d9892ea3b3700a27ff84140d9025493": { - "balance": "60000000000000000000" - }, - "5dc36de5359450a1ec09cb0c44cf2bb42b3ae435": { - "balance": "1117500000000000000000" - }, - "35c8adc11125432b3b77acd64625fe58ebee9d66": { - "balance": "2000000000000000000000" - }, - "a5e9cd4b74255d22b7d9b27ae8dd43ed6ed0252b": { - "balance": "766527000000000000000" - }, - "31ea12d49a35a740780ddeeaece84c0835b26270": { - "balance": "200000000000000000000" - }, - "7aef7b551f0b9c46e755c0f38e5b3a73fe1199f5": { - "balance": "1490000000000000000000" - }, - "cc6d7b12061bc96d104d606d65ffa32b0036eb07": { - "balance": "10000000000000000000000" - }, - "322021022678a0166d204b3aaa7ad4ec4b88b7d0": { - "balance": "400000000000000000000" - }, - "b31196714a48dff726ea9433cd2912f1a414b3b3": { - "balance": "2680000000000000000000" - }, - "0f2fb884c8aaff6f543ac6228bd08e4f60b0a5fd": { - "balance": "3145000000000000000000" - }, - "7d9d221a3df89ddd7b5f61c1468c6787d6b333e6": { - "balance": "138000000000000000000" - }, - "367f59cc82795329384e41e1283115e791f26a01": { - "balance": "2000000000000000000000" - }, - "fd9579f119bbc819a02b61e38d8803c942f24d32": { - "balance": "105600000000000000000" - }, - "3e2f26235e137a7324e4dc154b5df5af46ea1a49": { - "balance": "22458000000000000000" - }, - "4c1579af3312e4f88ae93c68e9449c2e9a68d9c4": { - "balance": "2000000000000000000000" - }, - "ffb04726dfa41afdc819168418610472970d7bfc": { - "balance": "4000000000000000000000" - }, - "403c64896a75cad816a9105e18d8aa5bf80f238e": { - "balance": "985000000000000000000" - }, - "5cd588a14ec648ccf64729f9167aa7bf8be6eb3d": { - "balance": "1000000000000000000000" - }, - "24b2be118b16d8b2174769d17b4cf84f07ca946d": { - "balance": "2000000000000000000000" - }, - "d3bb59fa31258be62f8ed232f1a7d47b4a0b41ee": { - "balance": "100000000000000000000" - }, - "cc9ac715cd6f2610c52b58676456884297018b29": { - "balance": "13370000000000000000" - }, - "6f2a31900e240395b19f159c1d00dfe4d898ebdf": { - "balance": "1999600000000000000000" - }, - "d60b247321a32a5affb96b1e279927cc584de943": { - "balance": "2265500000000000000000" - }, - "f7a1ade2d0f529123d1055f19b17919f56214e67": { - "balance": "500000000000000000000" - }, - "bea00df17067a43a82bc1daecafb6c14300e89e6": { - "balance": "1820000000000000000000" - }, - "a2968fc1c64bac0b7ae0d68ba949874d6db253f4": { - "balance": "20000000000000000000000" - }, - "92d8ad9a4d61683b80d4a6672e84c20d62421e80": { - "balance": "20000000000000000000" - }, - "6ed2a12b02f8c688c7b5d3a6ea14d63687dab3b6": { - "balance": "2000000000000000000000" - }, - "7a63869fc767a4c6b1cd0e0649f3634cb121d24b": { - "balance": "77500000000000000000" - }, - "84f522f0520eba52dd18ad21fa4b829f2b89cb97": { - "balance": "4949566000000000000000" - }, - "d6234aaf45c6f22e66a225ffb93add629b4ef80f": { - "balance": "1000000000000000000000" - }, - "e3d8bf4efe84b1616d1b89e427ddc6c8830685ae": { - "balance": "2000000000000000000000" - }, - "a3db364a332d884ba93b2617ae4d85a1489bea47": { - "balance": "1700000000000000000000" - }, - "9f7986924aeb02687cd64189189fb167ded2dd5c": { - "balance": "985000000000000000000" - }, - "2eaf4e2a46b789ccc288c8d1d9294e3fb0853896": { - "balance": "2000000000000000000000" - }, - "a02dc6aa328b880de99eac546823fccf774047fb": { - "balance": "1970000000000000000000" - }, - "873b7f786d3c99ff012c4a7cae2677270240b9c5": { - "balance": "1730000000000000000000" - }, - "1d69c83d28ff0474ceebeacb3ad227a144ece7a3": { - "balance": "5474937000000000000000" - }, - "7b827cae7ff4740918f2e030ab26cb98c4f46cf5": { - "balance": "7460000000000000000000" - }, - "3083ef0ed4c4401196774a95cf4edc83edc1484f": { - "balance": "170000000000000000000000" - }, - "40ad74bc0bce2a45e52f36c3debb1b3ada1b7619": { - "balance": "6790000000000000000000" - }, - "05423a54c8d0f9707e704173d923b946edc8e700": { - "balance": "127543000000000000000" - }, - "22eb7db0ba56b0f8b816ccb206e615d929185b0d": { - "balance": "80500000000000000000" - }, - "66082c75a8de31a53913bbd44de3a0374f7faa41": { - "balance": "1460000000000000000000" - }, - "e3d3eaa299887865569e88be219be507189be1c9": { - "balance": "456156000000000000000" - }, - "ae57cc129a96a89981dac60d2ffb877d5dc5e432": { - "balance": "1110994000000000000000" - }, - "1a2434cc774422d48d53d59c5d562cce8407c94b": { - "balance": "30000000000000000000" - }, - "21546914dfd3af2add41b0ff3e83ffda7414e1e0": { - "balance": "5969100000000000000000" - }, - "4dcf62a3de3f061db91498fd61060f1f6398ff73": { - "balance": "1999944000000000000000" - }, - "6fd98e563d12ce0fd60f4f1f850ae396a9823c02": { - "balance": "1261000000000000000000" - }, - "edf8a3e1d40f13b79ec8e3e1ecf262fd92116263": { - "balance": "158000000000000000000" - }, - "c09e3cfc19f605ff3ec9c9c70e2540d7ee974366": { - "balance": "500000000000000000000" - }, - "953572f0ea6df9b197cae40e4b8ecc056c4371c5": { - "balance": "1000000000000000000000" - }, - "163cc8be227646cb09719159f28ed09c5dc0dce0": { - "balance": "1337000000000000000000" - }, - "a3932a31d6ff75fb3b1271ace7caa7d5e1ff1051": { - "balance": "20000000000000000000000" - }, - "f9a94bd56198da245ed01d1e6430b24b2708dcc0": { - "balance": "749938000000000000000" - }, - "3eb8b33b21d23cda86d8288884ab470e164691b5": { - "balance": "500000000000000000000" - }, - "84bcbf22c09607ac84341d2edbc03bfb1739d744": { - "balance": "500000000000000000000" - }, - "961c59adc74505d1864d1ecfcb8afa0412593c93": { - "balance": "40000000000000000000000" - }, - "f068dfe95d15cd3a7f98ffa688b4346842be2690": { - "balance": "1255160000000000000000" - }, - "291efe0081dce8c14799f7b2a43619c0c3b3fc1f": { - "balance": "1200000000000000000000" - }, - "be4fd073617022b67f5c13499b827f763639e4e3": { - "balance": "2000000000000000000000" - }, - "e40a7c82e157540a0b00901dbb86c716e1a062da": { - "balance": "49800000000000000000" - }, - "6635b46f711d2da6f0e16370cd8ee43efb2c2d52": { - "balance": "2000000000000000000000" - }, - "43748928e8c3ec4436a1d092fbe43ac749be1251": { - "balance": "400000000000000000000" - }, - "b557ab9439ef50d237b553f02508364a466a5c03": { - "balance": "200000000000000000000" - }, - "11928378d27d55c520ceedf24ceb1e822d890df0": { - "balance": "8000000000000000000000" - }, - "61518464fdd8b73c1bb6ac6db600654938dbf17a": { - "balance": "200000000000000000000" - }, - "004bfbe1546bc6c65b5c7eaa55304b38bbfec6d3": { - "balance": "2000000000000000000000" - }, - "a5e0fc3c3affed3db6710947d1d6fb017f3e276d": { - "balance": "2000000000000000000000" - }, - "8ecbcfacbfafe9f00c3922a24e2cf0026756ca20": { - "balance": "5640000000000000000000" - }, - "fb5ffaa0f7615726357891475818939d2037cf96": { - "balance": "20000000000000000000" - }, - "ae222865799079aaf4f0674a0cdaab02a6d570ff": { - "balance": "2000000000000000000000" - }, - "9edc90f4be210865214ab5b35e5a8dd77415279d": { - "balance": "4000000000000000000000" - }, - "9d7831e834c20b1baa697af1d8e0c621c5afff9a": { - "balance": "86500000000000000000" - }, - "046d274b1af615fb505a764ad8dda770b1db2f3d": { - "balance": "2000000000000000000000" - }, - "eaea23aa057200e7c9c15e8ff190d0e66c0c0e83": { - "balance": "2000000000000000000000" - }, - "417a3cd19496530a6d4204c3b5a17ce0f207b1a5": { - "balance": "8000000000000000000000" - }, - "a035a3652478f82dbd6d115faa8ca946ec9e681d": { - "balance": "109880000000000000000" - }, - "4f5801b1eb30b712d8a0575a9a71ff965d4f34eb": { - "balance": "300000000000000000000" - }, - "91dbb6aaad149585be47375c5d6de5ff09191518": { - "balance": "20000000000000000000000" - }, - "d043a011ec4270ee7ec8b968737515e503f83028": { - "balance": "500000000000000000000" - }, - "bb371c72c9f0316cea2bd9c6fbb4079e775429ef": { - "balance": "1760000000000000000000" - }, - "aa1df92e51dff70b1973e0e924c66287b494a178": { - "balance": "534400000000000000000" - }, - "bd5f46caab2c3d4b289396bbb07f203c4da82530": { - "balance": "80000000000000000000" - }, - "4d29fc523a2c1629532121da9998e9b5ab9d1b45": { - "balance": "15800000000000000000" - }, - "addb26317227f45c87a2cb90dc4cfd02fb23caf8": { - "balance": "1000000000000000000000" - }, - "52e46783329a769301b175009d346768f4c87ee4": { - "balance": "2000000000000000000000" - }, - "caad9dc20d589ce428d8fda3a9d53a607b7988b5": { - "balance": "4000000000000000000000" - }, - "95034e1621865137cd4739b346dc17da3a27c34e": { - "balance": "1580000000000000000000" - }, - "0c3239e2e841242db989a61518c22247e8c55208": { - "balance": "263656000000000000000" - }, - "5a0d609aae2332b137ab3b2f26615a808f37e433": { - "balance": "160000000000000000000000" - }, - "2334c590c7a48769103045c5b6534c8a3469f44a": { - "balance": "17443200000000000000000" - }, - "ddfcca13f934f0cfbe231da13039d70475e6a1d0": { - "balance": "1000169000000000000000" - }, - "ee7288d91086d9e2eb910014d9ab90a02d78c2a0": { - "balance": "2000000000000000000000" - }, - "fb91fb1a695553f0c68e21276decf0b83909b86d": { - "balance": "100016000000000000000" - }, - "38695fc7e1367ceb163ebb053751f9f68ddb07a0": { - "balance": "2000000000000000000000" - }, - "65093b239bbfba23c7775ca7da5a8648a9f54cf7": { - "balance": "400000000000000000000" - }, - "73d8fee3cb864dce22bb26ca9c2f086d5e95e63b": { - "balance": "1000000000000000000000" - }, - "f7155213449892744bc60f2e04400788bd041fdd": { - "balance": "66850000000000000000" - }, - "d1a71b2d0858e83270085d95a3b1549650035e23": { - "balance": "14900000000000000000000" - }, - "eac17b81ed5191fb0802aa54337313834107aaa4": { - "balance": "8000000000000000000000" - }, - "bb076aac92208069ea318a31ff8eeb14b7e996e3": { - "balance": "149000000000000000000" - }, - "9f46e7c1e9078cae86305ac7060b01467d6685ee": { - "balance": "668500000000000000000" - }, - "1598127982f2f8ad3b6b8fc3cf27bf617801ba2b": { - "balance": "173000000000000000000" - }, - "e91dac0195b19e37b59b53f7c017c0b2395ba44c": { - "balance": "1880000000000000000000" - }, - "a436c75453ccca4a1f1b62e5c4a30d86dde4be68": { - "balance": "2000000000000000000000" - }, - "11001b89ed873e3aaec1155634b4681643986323": { - "balance": "1000000000000000000000" - }, - "ab93b26ece0a0aa21365afed1fa9aea31cd54468": { - "balance": "1608000000000000000000" - }, - "e77febabdf080f0f5dca1d3f5766f2a79c0ffa7c": { - "balance": "1386000000000000000000" - }, - "1c4af0e863d2656c8635bc6ffec8dd9928908cb5": { - "balance": "2000000000000000000000" - }, - "0c48ae62d1539788eba013d75ea60b64eeba4e80": { - "balance": "2213311000000000000000" - }, - "423cc4594cf4abb6368de59fd2b1230734612143": { - "balance": "2000000000000000000000" - }, - "7f6b28c88421e4857e459281d78461692489d3fb": { - "balance": "2000000000000000000000" - }, - "806854588ecce541495f81c28a290373df0274b2": { - "balance": "582000000000000000000" - }, - "dc76e85ba50b9b31ec1e2620bce6e7c8058c0eaf": { - "balance": "20000000000000000000" - }, - "b00996b0566ecb3e7243b8227988dcb352c21899": { - "balance": "12000000000000000000000" - }, - "f5d14552b1dce0d6dc1f320da6ffc8a331cd6f0c": { - "balance": "1337000000000000000000" - }, - "55a61b109480b5b2c4fcfdef92d90584160c0d35": { - "balance": "44700000000000000000" - }, - "b8947822d5ace7a6ad8326e95496221e0be6b73d": { - "balance": "20000000000000000000" - }, - "492de46aaf8f1d708d59d79af1d03ad2cb60902f": { - "balance": "2000000000000000000000" - }, - "0e0d6633db1e0c7f234a6df163a10e0ab39c200f": { - "balance": "200000000000000000000" - }, - "f8bf9c04874e5a77f38f4c38527e80c676f7b887": { - "balance": "2000000000000000000000" - }, - "15528350e0d9670a2ea27f7b4a33b9c0f9621d21": { - "balance": "4000086000000000000000" - }, - "eccf7a0457b566b346ca673a180f444130216ac3": { - "balance": "100000000000000000000" - }, - "10cf560964ff83c1c9674c783c0f73fcd89943fc": { - "balance": "40000000000000000000000" - }, - "e7f06f699be31c440b43b4db0501ec0e25261644": { - "balance": "500000000000000000000" - }, - "b6ce4dc560fc73dc69fb7a62e388db7e72ea764f": { - "balance": "966000000000000000000" - }, - "f456055a11ab91ff668e2ec922961f2a23e3db25": { - "balance": "18200000000000000000" - }, - "8dfbafbc0e5b5c86cd1ad697feea04f43188de96": { - "balance": "390060000000000000000" - }, - "085b4ab75d8362d914435cedee1daa2b1ee1a23b": { - "balance": "3880000000000000000000" - }, - "e400d651bb3f2d23d5f849e6f92d9c5795c43a8a": { - "balance": "2674000000000000000000" - }, - "851aa91c82f42fad5dd8e8bb5ea69c8f3a5977d1": { - "balance": "148607000000000000000" - }, - "4c935bb250778b3c4c7f7e07fc251fa630314aab": { - "balance": "1500000000000000000000" - }, - "ebd356156a383123343d48843bffed6103e866b3": { - "balance": "1970000000000000000000" - }, - "da0b48e489d302b4b7bf204f957c1c9be383b0df": { - "balance": "2000000000000000000000" - }, - "7085ae7e7e4d932197b5c7858c00a3674626b7a5": { - "balance": "6000000000000000000000" - }, - "5b06d1e6930c1054692b79e3dbe6ecce53966420": { - "balance": "205400000000000000000" - }, - "8df53d96191471e059de51c718b983e4a51d2afd": { - "balance": "32000000000000000000000" - }, - "0678654ac6761db904a2f7e8595ec1eaac734308": { - "balance": "878000000000000000000" - }, - "89fee30d1728d96cecc1dab3da2e771afbcfaa41": { - "balance": "1999944000000000000000" - }, - "59c5d06b170ee4d26eb0a0eb46cb7d90c1c91019": { - "balance": "10000000000000000000000" - }, - "2b129c26b75dde127f8320bd0f63410c92a9f876": { - "balance": "2200000000000000000000" - }, - "3d6ae053fcbc318d6fd0fbc353b8bf542e680d27": { - "balance": "14300000000000000000" - }, - "755a60bf522fbd8fff9723446b7e343a7068567e": { - "balance": "20000000000000000000000" - }, - "947e11e5ea290d6fc3b38048979e0cd44ec7c17f": { - "balance": "2000000000000000000000" - }, - "711ecf77d71b3d0ea95ce4758afecdb9c131079d": { - "balance": "760000000000000000000" - }, - "de9eff4c798811d968dccb460d9b069cf30278e0": { - "balance": "400000000000000000000" - }, - "4e892e8081bf36e488fddb3b2630f3f1e8da30d2": { - "balance": "12003800000000000000000" - }, - "8ede7e3dc50749c6c50e2e28168478c34db81946": { - "balance": "19999800000000000000000" - }, - "0c30cacc3f72269f8b4f04cf073d2b05a83d9ad1": { - "balance": "2001000000000000000000" - }, - "e51eb87e7fb7311f5228c479b48ec9878831ac4c": { - "balance": "2000000000000000000000" - }, - "8b01da34d470c1d115acf4d8113c4dd8a8c338e4": { - "balance": "25220000000000000000000" - }, - "4329fc0931cbeb033880fe4c9398ca45b0e2d11a": { - "balance": "2000400000000000000000" - }, - "540c072802014ef0d561345aec481e8e11cb3570": { - "balance": "8000000000000000000000" - }, - "21e5d2bae995ccfd08a5c16bb524e1f630448f82": { - "balance": "2800000000000000000000" - }, - "5cf8c03eb3e872e50f7cfd0c2f8d3b3f2cb5183a": { - "balance": "200000000000000000000" - }, - "5c0f2e51378f6b0d7bab617331580b6e39ad3ca5": { - "balance": "9600000000000000000000" - }, - "d2f241255dd7c3f73c07043071ec08ddd9c5cde5": { - "balance": "500000000000000000000" - }, - "cbe1b948864d8474e765145858fca4550f784b92": { - "balance": "10000000000000000000000" - }, - "30742ccdf4abbcd005681f8159345c9e79054b1a": { - "balance": "668500000000000000000" - }, - "6aeb9f74742ea491813dbbf0d6fcde1a131d4db3": { - "balance": "440800000000000000000" - }, - "821eb90994a2fbf94bdc3233910296f76f9bf6e7": { - "balance": "10000000000000000000000" - }, - "25c1a37ee5f08265a1e10d3d90d5472955f97806": { - "balance": "1820000000000000000000" - }, - "7ef98b52bee953bef992f305fda027f8911c5851": { - "balance": "514717000000000000000" - }, - "8adc53ef8c18ed3051785d88e996f3e4b20ecd51": { - "balance": "42000000000000000000000" - }, - "007f4a23ca00cd043d25c2888c1aa5688f81a344": { - "balance": "773658000000000000000" - }, - "4a735d224792376d331367c093d31c8794341582": { - "balance": "1900000000000000000000" - }, - "05440c5b073b529b4829209dff88090e07c4f6f5": { - "balance": "1288000000000000000000" - }, - "5e772e27f28800c50dda973bb33e10762e6eea20": { - "balance": "1790000000000000000000" - }, - "a429fa88731fdd350e8ecd6ea54296b6484fe695": { - "balance": "1969606000000000000000" - }, - "e0d76b7166b1f3a12b4091ee2b29de8caa7d07db": { - "balance": "2000000000000000000000" - }, - "7ebd95e9c470f7283583dc6e9d2c4dce0bea8f84": { - "balance": "14000000000000000000000" - }, - "883a78aeabaa50d8ddd8570bcd34265f14b19363": { - "balance": "3879951000000000000000" - }, - "51f9c432a4e59ac86282d6adab4c2eb8919160eb": { - "balance": "530000000000000000000000" - }, - "b86607021b62d340cf2652f3f95fd2dc67698bdf": { - "balance": "5000000000000000000000" - }, - "acc0909fda2ea6b7b7a88db7a0aac868091ddbf6": { - "balance": "22155000000000000000" - }, - "69b80ed90f84834afa3ff82eb964703b560977d6": { - "balance": "26740000000000000000" - }, - "ca4ca9e4779d530ecbacd47e6a8058cfde65d98f": { - "balance": "800000000000000000000" - }, - "5d6c5c720d66a6abca8397142e63d26818eaab54": { - "balance": "40000000000000000000" - }, - "c2c13e72d268e7150dc799e7c6cf03c88954ced7": { - "balance": "700000000000000000000" - }, - "6bbd1e719390e6b91043f8b6b9df898ea8001b34": { - "balance": "2000053000000000000000" - }, - "a9ba6f413b82fcddf3affbbdd09287dcf50415ca": { - "balance": "4000000000000000000000" - }, - "ced3c7be8de7585140952aeb501dc1f876ecafb0": { - "balance": "4000000000000000000000" - }, - "1c63fa9e2cbbf23c49fcdef1cbabfe6e0d1e14c1": { - "balance": "1000000000000000000000" - }, - "7d6e990daa7105de2526339833f77b5c0b85d84f": { - "balance": "20000000000000000000000" - }, - "68addf019d6b9cab70acb13f0b3117999f062e12": { - "balance": "49941000000000000000" - }, - "a77428bcb2a0db76fc8ef1e20e461a0a32c5ac15": { - "balance": "401100000000000000000" - }, - "26048fe84d9b010a62e731627e49bc2eb73f408f": { - "balance": "4000000000000000000000" - }, - "ff26138330274df4e0a3081e6df7dd983ec6e78f": { - "balance": "2000000000000000000000" - }, - "b7382d37db0398ac72410cf9813de9f8e1ec8dad": { - "balance": "1000070000000000000000" - }, - "44f62f2aaabc29ad3a6b04e1ff6f9ce452d1c140": { - "balance": "17000000000000000000000" - }, - "47fef58584465248a0810d60463ee93e5a6ee8d3": { - "balance": "283100000000000000000" - }, - "bd2b70fecc37640f69514fc7f3404946aad86b11": { - "balance": "1200000000000000000000" - }, - "649a85b93653075fa6562c409a565d087ba3e1ba": { - "balance": "2000000000000000000000" - }, - "55866486ec168f79dbe0e1abb18864d98991ae2c": { - "balance": "16100000000000000000" - }, - "d7e74afdbad55e96cebc5a374f2c8b768680f2b0": { - "balance": "99000000000000000000" - }, - "a8c1d6aa41fe3d65f67bd01de2a866ed1ed9ae52": { - "balance": "30000000000000000000" - }, - "744c0c77ba7f236920d1e434de5da33e48ebf02c": { - "balance": "1970000000000000000000" - }, - "9445ba5c30e98961b8602461d0385d40fbd80311": { - "balance": "10000000000000000000000" - }, - "eb835c1a911817878a33d167569ea3cdd387f328": { - "balance": "1000000000000000000000" - }, - "761a6e362c97fbbd7c5977acba2da74687365f49": { - "balance": "183840000000000000000" - }, - "38202c5cd7078d4f887673ab07109ad8ada89720": { - "balance": "1000000000000000000000" - }, - "5abfec25f74cd88437631a7731906932776356f9": { - "balance": "11901484239480000000000000" - }, - "28e4af30cd93f686a122ad7bb19f8a8785eee342": { - "balance": "2101000000000000000000" - }, - "3a9b111029ce1f20c9109c7a74eeeef34f4f2eb2": { - "balance": "4000000000000000000000" - }, - "7bb9571f394b0b1a8eba5664e9d8b5e840677bea": { - "balance": "19700000000000000000" - }, - "50fb36c27107ee2ca9a3236e2746cca19ace6b49": { - "balance": "2000000000000000000000" - }, - "a3bc979b7080092fa1f92f6e0fb347e28d995045": { - "balance": "2800000000000000000000" - }, - "d04b861b3d9acc563a901689941ab1e1861161a2": { - "balance": "20000000000000000000" - }, - "58c555bc293cdb16c6362ed97ae9550b92ea180e": { - "balance": "20000000000000000000" - }, - "8bf02bd748690e1fd1c76d270833048b66b25fd3": { - "balance": "11800000000000000000000" - }, - "fbc01db54e47cdc3c438694ab717a856c23fe6e9": { - "balance": "8456774000000000000000" - }, - "9c9a07a8e57c3172a919ef64789474490f0d9f51": { - "balance": "10000000000000000000000" - }, - "fc7e22a503ec5abe9b08c50bd14999f520fa4884": { - "balance": "6387725000000000000000" - }, - "9b773669e87d76018c090f8255e54409b9dca8b2": { - "balance": "20000000000000000000" - }, - "ffe8cbc1681e5e9db74a0f93f8ed25897519120f": { - "balance": "1507000000000000000000" - }, - "4d4cf5807429615e30cdface1e5aae4dad3055e6": { - "balance": "600000000000000000000" - }, - "cfde0fc75d6f16c443c3038217372d99f5d907f7": { - "balance": "2419000000000000000000" - }, - "818ffe271fc3973565c303f213f6d2da89897ebd": { - "balance": "5734655000000000000000" - }, - "ba1fcaf223937ef89e85675503bdb7ca6a928b78": { - "balance": "640000000000000000000" - }, - "a30a45520e5206d9004070e6af3e7bb2e8dd5313": { - "balance": "400000000000000000000" - }, - "a747439ad0d393b5a03861d77296326de8bb9db9": { - "balance": "1000000000000000000000" - }, - "14d00aad39a0a7d19ca05350f7b03727f08dd82e": { - "balance": "500000000000000000000" - }, - "551999ddd205563327b9b530785acff9bc73a4ba": { - "balance": "6000000000000000000000" - }, - "a4670731175893bbcff4fa85ce97d94fc51c4ba8": { - "balance": "8000000000000000000000" - }, - "f858171a04d357a13b4941c16e7e55ddd4941329": { - "balance": "41984000000000000000" - }, - "a6484cc684c4c91db53eb68a4da45a6a6bda3067": { - "balance": "6000000000000000000000" - }, - "00d75ed60c774f8b3a5a5173fb1833ad7105a2d9": { - "balance": "2005500000000000000000" - }, - "bf92418a0c6c31244d220260cb3e867dd7b4ef49": { - "balance": "99800000000000000000" - }, - "716d50cca01e938500e6421cc070c3507c67d387": { - "balance": "2000000000000000000000" - }, - "82a8b96b6c9e13ebec1e9f18ac02a60ea88a48ff": { - "balance": "1999998000000000000000" - }, - "5a565285374a49eedd504c957d510874d00455bc": { - "balance": "100000000000000000000" - }, - "778c79f4de1953ebce98fe8006d53a81fb514012": { - "balance": "999800000000000000000" - }, - "41b2d34fde0b1029262b4172c81c1590405b03ae": { - "balance": "1000000000000000000000" - }, - "4039bd50a2bde15ffe37191f410390962a2b8886": { - "balance": "200000000000000000000" - }, - "c033be10cb48613bd5ebcb33ed4902f38b583003": { - "balance": "3000000000000000000000" - }, - "5d5751819b4f3d26ed0c1ac571552735271dbefa": { - "balance": "1000000000000000000000" - }, - "b600429752f399c80d0734744bae0a022eca67c6": { - "balance": "20000000000000000000" - }, - "f875619d8a23e45d8998d184d480c0748970822a": { - "balance": "4000000000000000000000" - }, - "71c7230a1d35bdd6819ed4b9a88e94a0eb0786dd": { - "balance": "4365000000000000000000" - }, - "b2f9c972c1e9737755b3ff1b3088738396395b26": { - "balance": "20000000000000000000000" - }, - "a66a4963b27f1ee1932b172be5964e0d3ae54b51": { - "balance": "173000000000000000000" - }, - "53ce88e66c5af2f29bbd8f592a56a3d15f206c32": { - "balance": "140840000000000000000" - }, - "433e3ba1c51b810fc467d5ba4dea42f7a9885e69": { - "balance": "40000000000000000000000" - }, - "c7837ad0a0bf14186937ace06c5546a36aa54f46": { - "balance": "4000000000000000000000" - }, - "c3f8f67295a5cd049364d05d23502623a3e52e84": { - "balance": "6000000000000000000000" - }, - "3fd0bb47798cf44cdfbe4d333de637df4a00e45c": { - "balance": "100040000000000000000" - }, - "a1ae8d4540d4db6fdde7146f415b431eb55c7983": { - "balance": "197000000000000000000" - }, - "5cccf1508bfd35c20530aa642500c10dee65eaed": { - "balance": "850000000000000000000" - }, - "a53ead54f7850af21438cbe07af686279a315b86": { - "balance": "10000000000000000000000" - }, - "8cf6da0204dbc4860b46ad973fc111008d9e0c46": { - "balance": "200000000000000000000" - }, - "8e7936d592008fdc7aa04edeeb755ab513dbb89d": { - "balance": "20000000000000000000" - }, - "4a53dcdb56ce4cdce9f82ec0eb13d67352e7c88b": { - "balance": "4200000000000000000000" - }, - "2b4f4507bb6b9817942ce433781b708fbcd166fd": { - "balance": "18200000000000000000" - }, - "026432af37dc5113f1f46d480a4de0b28052237e": { - "balance": "355800000000000000000" - }, - "e780a56306ba1e6bb331952c22539b858af9f77d": { - "balance": "50000000000000000000000" - }, - "d1f1694d22671b5aad6a94995c369fbe6133676f": { - "balance": "1000000000000000000000" - }, - "7c45f0f8442a56dbd39dbf159995415c52ed479b": { - "balance": "2000000000000000000000" - }, - "b65941d44c50d24666670d364766e991c02e11c2": { - "balance": "600000000000000000000" - }, - "45e68db8dbbaba5fc2cb337c62bcd0d61b059189": { - "balance": "2000000000000000000000" - }, - "05f3631f5664bdad5d0132c8388d36d7d8920918": { - "balance": "20000000000000000000" - }, - "5475d7f174bdb1f789017c7c1705989646079d49": { - "balance": "9400000000000000000000" - }, - "c7bf2ed1ed312940ee6aded1516e268e4a604856": { - "balance": "6000000000000000000000" - }, - "39aaf0854db6eb39bc7b2e43846a76171c0445de": { - "balance": "1850000000000000000000" - }, - "c817df1b91faf30fe3251571727c9711b45d8f06": { - "balance": "1999944000000000000000" - }, - "7d13d6705884ab2157dd8dcc7046caf58ee94be4": { - "balance": "137200000000000000000000" - }, - "478dc09a1311377c093f9cc8ae74111f65f82f39": { - "balance": "4000000000000000000000" - }, - "8043ed22f997e5a2a4c16e364486ae64975692c4": { - "balance": "1130513000000000000000" - }, - "b9a985501ee950829b17fae1c9cf348c3156542c": { - "balance": "294100000000000000000" - }, - "d5cba5b26bea5d73fabb1abafacdef85def368cc": { - "balance": "200000000000000000000" - }, - "6776e133d9dc354c12a951087b639650f539a433": { - "balance": "120000000000000000000" - }, - "804ca94972634f633a51f3560b1d06c0b293b3b1": { - "balance": "200000000000000000000" - }, - "0be1fdf626ee6189102d70d13b31012c95cd1cd6": { - "balance": "2000000000000000000000" - }, - "f848fce9ab611c7d99206e23fac69ad488b94fe1": { - "balance": "48500000000000000000" - }, - "f01195d657ef3c942e6cb83949e5a20b5cfa8b1e": { - "balance": "25760000000000000000000" - }, - "78a5e89900bd3f81dd71ba869d25fec65261df15": { - "balance": "51900000000000000000000" - }, - "d6f1e55b1694089ebcb4fe7d7882aa66c8976176": { - "balance": "19998846000000000000000" - }, - "d5294b666242303b6df0b1c88d37429bc8c965aa": { - "balance": "300700000000000000000" - }, - "3171877e9d820cc618fc0919b29efd333fda4934": { - "balance": "1000000000000000000000" - }, - "2901f8077f34190bb47a8e227fa29b30ce113b31": { - "balance": "100000000000000000000" - }, - "6b2284440221ce16a8382de5ff0229472269deec": { - "balance": "1000000000000000000000" - }, - "1bba03ff6b4ad5bf18184acb21b188a399e9eb4a": { - "balance": "1790000000000000000000" - }, - "80744618de396a543197ee4894abd06398dd7c27": { - "balance": "2000000000000000000000" - }, - "1b799033ef6dc7127822f74542bb22dbfc09a308": { - "balance": "100000000000000000000" - }, - "d513a45080ff2febe62cd5854abe29ee4467f996": { - "balance": "153200000000000000000" - }, - "e761d27fa3502cc76bb1a608740e1403cf9dfc69": { - "balance": "280000000000000000000" - }, - "53989ed330563fd57dfec9bd343c3760b0799390": { - "balance": "6208000000000000000000" - }, - "ccf7110d1bd9a74bfd1d7d7d2d9d55607e7b837d": { - "balance": "900000000000000000000" - }, - "f373e9daac0c8675f53b797a160f6fc034ae6b23": { - "balance": "100000000000000000000" - }, - "abc9a99e8a2148a55a6d82bd51b98eb5391fdbaf": { - "balance": "6000000000000000000000" - }, - "ffec0913c635baca2f5e57a37aa9fb7b6c9b6e26": { - "balance": "805000000000000000000" - }, - "581a3af297efa4436a29af0072929abf9826f58b": { - "balance": "2000000000000000000000" - }, - "924efa6db595b79313277e88319625076b580a10": { - "balance": "2000000000000000000000" - }, - "65d8dd4e251cbc021f05b010f2d5dc520c3872e0": { - "balance": "834956000000000000000" - }, - "6c67d6db1d03516c128b8ff234bf3d49b26d2941": { - "balance": "100000000000000000000000" - }, - "496d365534530a5fc1577c0a5241cb88c4da7072": { - "balance": "1790000000000000000000" - }, - "b85ff03e7b5fc422981fae5e9941dacbdaba7584": { - "balance": "1337000000000000000000" - }, - "e13540ecee11b212e8b775dc8e71f374aae9b3f8": { - "balance": "2000000000000000000000" - }, - "a02e3f8f5959a7aab7418612129b701ca1b80010": { - "balance": "20000000000000000000" - }, - "a7a3f153cdc38821c20c5d8c8241b294a3f82b24": { - "balance": "500000000000000000000" - }, - "366175403481e0ab15bb514615cbb989ebc68f82": { - "balance": "2000000000000000000000" - }, - "5104ecc0e330dd1f81b58ac9dbb1a9fbf88a3c85": { - "balance": "100000000000000000000000" - }, - "a466d770d898d8c9d405e4a0e551efafcde53cf9": { - "balance": "492500000000000000000" - }, - "5fa8a54e68176c4fe2c01cf671c515bfbdd528a8": { - "balance": "330000000000000000000000" - }, - "e2e15c60dd381e3a4be25071ab249a4c5c5264da": { - "balance": "2350502000000000000000" - }, - "0628bfbe5535782fb588406bc96660a49b011af5": { - "balance": "1520000000000000000000" - }, - "04d6b8d4da867407bb997749debbcdc0b358538a": { - "balance": "1000000000000000000000" - }, - "0e6ec313376271dff55423ab5422cc3a8b06b22b": { - "balance": "4000000000000000000000" - }, - "8787d12677a5ec291e57e31ffbfad105c3324b87": { - "balance": "12438777000000000000000" - }, - "58e2f11223fc8237f69d99c6289c148c0604f742": { - "balance": "24000000000000000000000" - }, - "5600730a55f6b20ebd24811faa3de96d1662abab": { - "balance": "1880000000000000000000" - }, - "fce089635ce97abac06b44819be5bb0a3e2e0b37": { - "balance": "92491000000000000000" - }, - "fa0c1a988c8a17ad3528eb28b3409daa58225f26": { - "balance": "200000000000000000000" - }, - "7ae1c19e53c71cee4c73fae2d7fc73bf9ab5e392": { - "balance": "1000000000000000000000" - }, - "bd17eed82b9a2592019a1b1b3c0fbad45c408d22": { - "balance": "250000000000000000000" - }, - "884a7a39d0916e05f1c242df55607f37df8c5fda": { - "balance": "23400000000000000000000" - }, - "ca70f4ddbf069d2143bd6bbc7f696b52789b32e7": { - "balance": "3000000000000000000000" - }, - "7b25bb9ca8e702217e9333225250e53c36804d48": { - "balance": "1880000000000000000000" - }, - "ea8317197959424041d9d7c67a3ece1dbb78bb55": { - "balance": "394000000000000000000" - }, - "5cb953a0e42f5030812226217fffc3ce230457e4": { - "balance": "100000000000000000000" - }, - "d1f4dc1ddb8abb8848a8b14e25f3b55a8591c266": { - "balance": "250000000000000000000" - }, - "6a42ca971c6578d5ade295c3e7f4ad331dd3424e": { - "balance": "6000000000000000000000" - }, - "07e1162ceae3cf21a3f62d105990302e307f4e3b": { - "balance": "1530000000000000000000" - }, - "5d1dc3387b47b8451e55106c0cc67d6dc72b7f0b": { - "balance": "2000000000000000000000" - }, - "5d2819e8d57821922ee445650ccaec7d40544a8d": { - "balance": "200000000000000000000" - }, - "4c24b78baf2bafc7fcc69016426be973e20a50b2": { - "balance": "3000000000000000000000" - }, - "630c5273126d517ce67101811cab16b8534cf9a8": { - "balance": "9422595000000000000000" - }, - "291f929ca59b54f8443e3d4d75d95dee243cef78": { - "balance": "499938000000000000000" - }, - "2dd325fdffb97b19995284afa5abdb574a1df16a": { - "balance": "500000000000000000000" - }, - "4fce8429ba49caa0369d1e494db57e89eab2ad39": { - "balance": "200000000000000000000000" - }, - "712b76510214dc620f6c3a1dd29aa22bf6d214fb": { - "balance": "6000000000000000000000" - }, - "266f2da7f0085ef3f3fa09baee232b93c744db2e": { - "balance": "60000000000000000000000" - }, - "0770c61be78772230cb5a3bb2429a72614a0b336": { - "balance": "6767695000000000000000" - }, - "02dfcb17a1b87441036374b762a5d3418b1cb4d4": { - "balance": "1340860000000000000000" - }, - "5e67df8969101adabd91accd6bb1991274af8df2": { - "balance": "500000000000000000000" - }, - "7d9c59631e2ba2e8e82891f3979922aaa3b567a1": { - "balance": "8000000000000000000000" - }, - "949f8c107bc7f0aceaa0f17052aadbd2f9732b2e": { - "balance": "2000000000000000000000" - }, - "ea4e809e266ae5f13cdbe38f9d0456e6386d1274": { - "balance": "4500000000000000000000" - }, - "cd5510a242dfb0183de925fba866e312fabc1657": { - "balance": "2400000000000000000000" - }, - "a36e0d94b95364a82671b608cb2d373245612909": { - "balance": "150011000000000000000" - }, - "0ec46696ffac1f58005fa8439824f08eed1df89b": { - "balance": "10000000000000000000000" - }, - "c6fb1ee37417d080a0d048923bdabab095d077c6": { - "balance": "200000000000000000000" - }, - "53c9eca40973f63bb5927be0bc6a8a8be1951f74": { - "balance": "2000000000000000000000" - }, - "ea14bfda0a6e76668f8788321f07df37824ec5df": { - "balance": "200000000000000000000000" - }, - "dfb4d4ade52fcc818acc7a2c6bb2b00224658f78": { - "balance": "7750000000000000000000" - }, - "5997ffefb3c1d9d10f1ae2ac8ac3c8e2d2292783": { - "balance": "1000000000000000000000" - }, - "8eceb2e124536c5b5ffc640ed14ff15ed9a8cb71": { - "balance": "2000000000000000000000" - }, - "8f02bda6c36922a6be6a509be51906d393f7b99b": { - "balance": "1019835000000000000000" - }, - "530077c9f7b907ff9cec0c77a41a70e9029add4a": { - "balance": "2000000000000000000000" - }, - "08936a37df85b3a158cafd9de021f58137681347": { - "balance": "18200000000000000000" - }, - "8e9c429266df057efa78dd1d5f77fc40742ad466": { - "balance": "300061000000000000000" - }, - "acc59f3b30ceffc56461cc5b8df48902240e0e7b": { - "balance": "2000000000000000000000" - }, - "f5534815dc635efa5cc84b2ac734723e21b29372": { - "balance": "1580000000000000000000" - }, - "f873e57a65c93b6e18cb75f0dc077d5b8933dc5c": { - "balance": "197000000000000000000" - }, - "25b78c9fad85b43343f0bfcd0fac11c9949ca5eb": { - "balance": "2000000000000000000000" - }, - "aad2b7f8106695078e6c138ec81a7486aaca1eb2": { - "balance": "200000000000000000000" - }, - "509c8668036d143fb8ae70b11995631f3dfcad87": { - "balance": "1000000000000000000000" - }, - "3602458da86f6d6a9d9eb03daf97fe5619d442fa": { - "balance": "2000000000000000000000" - }, - "9f607b3f12469f446121cebf3475356b71b4328c": { - "balance": "4000000000000000000000" - }, - "fe3827d57630cf8761d512797b0b858e478bbd12": { - "balance": "20000000000000000000" - }, - "9d9c4efe9f433989e23be94049215329fa55b4cb": { - "balance": "256215000000000000000" - }, - "9bd905f1719fc7acd0159d4dc1f8db2f21472338": { - "balance": "1000000000000000000000" - }, - "7d82e523cc2dc591da3954e8b6bb2caf6461e69c": { - "balance": "2316058000000000000000" - }, - "74afe54902d615782576f8baac13ac970c050f6e": { - "balance": "177670000000000000000" - }, - "aff11ccf699304d5f5862af86083451c26e79ae5": { - "balance": "1999000000000000000000" - }, - "3885fee67107dc3a3c741ee290c98918c9b99397": { - "balance": "20000000000000000000" - }, - "36343aeca07b6ed58a0e62fa4ecb498a124fc971": { - "balance": "300000000000000000000" - }, - "c94a28fb3230a9ddfa964e770f2ce3c253a7be4f": { - "balance": "200000000000000000000" - }, - "9882967cee68d2a839fad8ab4a7c3dddf6c0adc8": { - "balance": "1336866000000000000000" - }, - "95df4e3445d7662624c48eba74cf9e0a53e9f732": { - "balance": "56000000000000000000000" - }, - "ca9faa17542fafbb388eab21bc4c94e8a7b34788": { - "balance": "1999999000000000000000" - }, - "c8b1850525d946f2ae84f317b15188c536a5dc86": { - "balance": "2685000000000000000000" - }, - "39bac68d947859f59e9226089c96d62e9fbe3cde": { - "balance": "40000000000000000000" - }, - "a9bfc410dddb20711e45c07387eab30a054e19ac": { - "balance": "1154750000000000000000" - }, - "540a1819bd7c35861e791804e5fbb3bc97c9abb1": { - "balance": "1454400000000000000000" - }, - "667b61c03bb937a9f5d0fc5a09f1ea3363c77035": { - "balance": "4250000000000000000000" - }, - "010df1df4bed23760d2d1c03781586ddf7918e54": { - "balance": "60000000000000000000" - }, - "bd51ee2ea143d7b1d6b77e7e44bdd7da12f485ac": { - "balance": "1318800000000000000000" - }, - "fb5125bf0f5eb0b6f020e56bfc2fdf3d402c097e": { - "balance": "5910000000000000000000" - }, - "3f0c83aac5717962734e5ceaeaecd39b28ad06be": { - "balance": "2000000000000000000000" - }, - "f10661ff94140f203e7a482572437938bec9c3f7": { - "balance": "20000000000000000000000" - }, - "bd3097a79b3c0d2ebff0e6e86ab0edadbed47096": { - "balance": "1670000000000000000000" - }, - "edeb4894aadd0081bbddd3e8846804b583d19f27": { - "balance": "2000000000000000000000" - }, - "49c9771fca19d5b9d245c891f8158fe49f47a062": { - "balance": "10000000000000000000000" - }, - "6405dd13e93abcff377e700e3c1a0086eca27d29": { - "balance": "18200000000000000000" - }, - "ce5e04f0184369bcfa06aca66ffa91bf59fa0fb9": { - "balance": "40000000000000000000" - }, - "4364309a9fa07095600f79edc65120cdcd23dc64": { - "balance": "10000000000000000000000" - }, - "b749b54e04d5b19bdcedfb84da7701ab478c27ae": { - "balance": "2680000000000000000000" - }, - "f593c65285ee6bbd6637f3be8f89ad40d489f655": { - "balance": "3000000000000000000000" - }, - "d224f880f9479a89d32f09e52be990b288135cef": { - "balance": "17300000000000000000000" - }, - "85bb51bc3bfe9a1b2a2f6b1cda95bca8b38c8d5e": { - "balance": "321750000000000000000" - }, - "caf4481d9db78dc4f25f7b4ac8bd3b1ca0106b31": { - "balance": "5000000000000000000000" - }, - "51ca8bd4dc644fac47af675563d5804a0da21eeb": { - "balance": "788000000000000000000" - }, - "19f643e1a8fa04ae16006028138333a59a96de87": { - "balance": "20000000000000000000" - }, - "58b808a65b51e6338969afb95ec70735e451d526": { - "balance": "39998000000000000000000" - }, - "574921838cc77d6c98b17d903a3ae0ee0da95bd0": { - "balance": "53480000000000000000000" - }, - "7c6924d07c3ef5891966fe0a7856c87bef9d2034": { - "balance": "2000000000000000000000" - }, - "f9767e4ecb4a5980527508d7bec3d45e4c649c13": { - "balance": "1910000000000000000000" - }, - "f3be99b9103ce7550aa74ff1db18e09dfe32e005": { - "balance": "2000000000000000000000" - }, - "625644c95a873ef8c06cdb9e9f6d8d7680043d62": { - "balance": "1800000000000000000000" - }, - "6a44af96b3f032ae641beb67f4b6c83342d37c5d": { - "balance": "29000000000000000000" - }, - "d3a10ec7a5c9324999dd9e9b6bde7c911e584bda": { - "balance": "600000000000000000000" - }, - "e8ddbed732ebfe754096fde9086b8ea4a4cdc616": { - "balance": "2000000000000000000000" - }, - "235fa66c025ef5540070ebcf0d372d8177c467ab": { - "balance": "33400000000000000000000" - }, - "4d08471d68007aff2ae279bc5e3fe4156fbbe3de": { - "balance": "40000000000000000000000" - }, - "dadc00ab7927603c2fcf31cee352f80e6c4d6351": { - "balance": "1999664000000000000000" - }, - "7393cbe7f9ba2165e5a7553500b6e75da3c33abf": { - "balance": "100000000000000000000" - }, - "77617ebc4bebc5f5ddeb1b7a70cdeb6ae2ffa024": { - "balance": "1970000000000000000000" - }, - "7fea1962e35d62059768c749bedd96cab930d378": { - "balance": "2000000000000000000000" - }, - "243b3bca6a299359e886ce33a30341fafe4d573d": { - "balance": "20000000000000000000000" - }, - "b94d47b3c052a5e50e4261ae06a20f45d8eee297": { - "balance": "2000000000000000000000" - }, - "e727e67ef911b81f6cf9c73fcbfebc2b02b5bfc6": { - "balance": "2000000000000000000000" - }, - "e510d6797fba3d6693835a844ea2ad540691971b": { - "balance": "17381000000000000000000" - }, - "0cdc960b998c141998160dc179b36c15d28470ed": { - "balance": "500038000000000000000" - }, - "3e76a62db187aa74f63817533b306cead0e8cebe": { - "balance": "31200000000000000000000" - }, - "495b641b1cdea362c3b4cbbd0f5cc50b1e176b9c": { - "balance": "1000000000000000000000" - }, - "5126460d692c71c9af6f05574d93998368a23799": { - "balance": "52000000000000000000" - }, - "a008019863c1a77c1499eb39bbd7bf2dd7a31cb9": { - "balance": "137000000000000000000" - }, - "65ee20b06d9ad589a7e7ce04b9f5f795f402aece": { - "balance": "2000000000000000000000" - }, - "f432b9dbaf11bdbd73b6519fc0a904198771aac6": { - "balance": "152000000000000000000" - }, - "85946d56a4d371a93368539690b60ec825107454": { - "balance": "1730000000000000000000" - }, - "26f9f7cefd7e394b9d3924412bf2c2831faf1f85": { - "balance": "4000000000000000000000" - }, - "d4ebb1929a23871cf77fe049ab9602be08be0a73": { - "balance": "1910000000000000000000" - }, - "4fdac1aa517007e0089430b3316a1badd12c01c7": { - "balance": "500000000000000000000" - }, - "05e671de55afec964b074de574d5158d5d21b0a3": { - "balance": "3940000000000000000000" - }, - "20181c4b41f6f972b66958215f19f570c15ddff1": { - "balance": "1600000000000000000000" - }, - "cc9519d1f3985f6b255eaded12d5624a972721e1": { - "balance": "1000000000000000000000" - }, - "169bbefc41cfd7d7cbb8dfc63020e9fb06d49546": { - "balance": "2000000000000000000000" - }, - "175a183a3a235ffbb03ba835675267229417a091": { - "balance": "16000000000000000000000" - }, - "8dde3cb8118568ef4503fe998ccdf536bf19a098": { - "balance": "4000000000000000000000" - }, - "6a05b21c4f17f9d73f5fb2b0cb89ff5356a6cc7e": { - "balance": "1500000000000000000000" - }, - "5cc4cba621f220637742057f6055b80dffd77e13": { - "balance": "39997692000000000000000" - }, - "ecb94c568bfe59ade650645f4f26306c736cace4": { - "balance": "267400000000000000000" - }, - "dfa6b8b8ad3184e357da282951d79161cfb089bc": { - "balance": "400000000000000000000" - }, - "a3058c51737a4e96c55f2ef6bd7bb358167ec2a7": { - "balance": "606093000000000000000" - }, - "051d424276b21239665186133d653bb8b1862f89": { - "balance": "1000000000000000000000" - }, - "d05ffb2b74f867204fe531653b0248e21c13544e": { - "balance": "1000000000000000000000" - }, - "e1f63ebbc62c7b7444040eb99623964f7667b376": { - "balance": "20000000000000000000" - }, - "e5a3d7eb13b15c100177236d1beb30d17ee15420": { - "balance": "2000000000000000000000" - }, - "18fa8625c9dc843c78c7ab259ff87c9599e07f10": { - "balance": "1000000000000000000000" - }, - "64264aedd52dcae918a012fbcd0c030ee6f71821": { - "balance": "1000000000000000000000" - }, - "6f1f4907b8f61f0c51568d692806b382f50324f5": { - "balance": "2000000000000000000000" - }, - "becef61c1c442bef7ce04b73adb249a8ba047e00": { - "balance": "1000400000000000000000" - }, - "7b893286427e72db219a21fc4dcd5fbf59283c31": { - "balance": "10000000000000000000000" - }, - "ce5eb63a7bf4fbc2f6e4baa0c68ab1cb4cf98fb4": { - "balance": "2000000000000000000000" - }, - "66ec16ee9caab411c55a6629e318de6ee216491d": { - "balance": "865000000000000000000" - }, - "30b66150f1a63457023fdd45d0cc6cb54e0c0f06": { - "balance": "1000000000000000000000" - }, - "87183160d172d2e084d327b86bcb7c1d8e6784ef": { - "balance": "4000086000000000000000" - }, - "c420388fbee84ad656dd68cdc1fbaa9392780b34": { - "balance": "187767000000000000000" - }, - "90f774c9147dde90853ddc43f08f16d455178b8c": { - "balance": "4000000000000000000000" - }, - "1e1d7a5f2468b94ea826982dbf2125793c6e4a5a": { - "balance": "999940000000000000000" - }, - "8043fdd0bc4c973d1663d55fc135508ec5d4f4fa": { - "balance": "20000000000000000000" - }, - "7bca1da6c80a66baa5db5ac98541c4be276b447d": { - "balance": "679000000000000000000" - }, - "73550beb732ba9ddafda7ae406e18f7feb0f8bb2": { - "balance": "2800000000000000000000" - }, - "adc19ec835afe3e58d87dc93a8a9213c90451326": { - "balance": "1971200000000000000000" - }, - "821d798af19989c3ae5b84a7a7283cd7fda1fabe": { - "balance": "20000000000000000000000" - }, - "4c4e6f13fb5e3f70c3760262a03e317982691d10": { - "balance": "100000000000000000000" - }, - "664e43119870af107a448db1278b044838ffcdaf": { - "balance": "400000000000000000000" - }, - "8da1178f55d97772bb1d24111a404a4f8715b95d": { - "balance": "878149000000000000000" - }, - "5e6e9747e162f8b45c656e0f6cae7a84bac80e4e": { - "balance": "2000000000000000000000" - }, - "c7eac31abce6d5f1dea42202b6a674153db47a29": { - "balance": "591000000000000000000" - }, - "d96711540e2e998343d4f590b6fc8fac3bb8b31d": { - "balance": "1758944000000000000000" - }, - "9da4ec407077f4b9707b2d9d2ede5ea5282bf1df": { - "balance": "4000000000000000000000" - }, - "f60c1b45f164b9580e20275a5c39e1d71e35f891": { - "balance": "2000000000000000000000" - }, - "eb6394a7bfa4d28911d5a5b23e93f35e340c2294": { - "balance": "78000000000000000000" - }, - "a89ac93b23370472daac337e9afdf642543f3e57": { - "balance": "10000000000000000000000" - }, - "bb618e25221ad9a740b299ed1406bc3934b0b16d": { - "balance": "1000000000000000000000" - }, - "817ac33bd8f847567372951f4a10d7a91ce3f430": { - "balance": "200015000000000000000" - }, - "fe6a895b795cb4bf85903d3ce09c5aa43953d3bf": { - "balance": "3400000000000000000000" - }, - "3673954399f6dfbe671818259bb278e2e92ee315": { - "balance": "200000000000000000000000" - }, - "df0ff1f3d27a8ec9fb8f6b0cb254a63bba8224a5": { - "balance": "4367636000000000000000" - }, - "ff12e49d8e06aa20f886293c0b98ed7eff788805": { - "balance": "4000000000000000000000" - }, - "5aef16a226dd68071f2483e1da42598319f69b2c": { - "balance": "2000000000000000000000" - }, - "0266ab1c6b0216230b9395443d5fa75e684568c6": { - "balance": "1000000000000000000000" - }, - "14a7352066364404db50f0d0d78d754a22198ef4": { - "balance": "1880000000000000000000" - }, - "444caf79b71338ee9aa7c733b02acaa7dc025948": { - "balance": "40000000000000000000" - }, - "64e2de21200b1899c3a0c0653b5040136d0dc842": { - "balance": "20000000000000000000000" - }, - "36e156610cd8ff64e780d89d0054385ca76755aa": { - "balance": "14000000000000000000000" - }, - "0a6ebe723b6ed1f9a86a69ddda68dc47465c2b1b": { - "balance": "1185000000000000000000" - }, - "38bf2a1f7a69de0e2546adb808b36335645da9ff": { - "balance": "2000320000000000000000" - }, - "39f44663d92561091b82a70dcf593d754005973a": { - "balance": "199999000000000000000" - }, - "24b9e6644f6ba4cde126270d81f6ab60f286dff4": { - "balance": "133700000000000000000" - }, - "9b59eb213b1e7565e45047e04ea0374f10762d16": { - "balance": "2000000000000000000000" - }, - "309544b6232c3dd737f945a03193d19b5f3f65b9": { - "balance": "1087440000000000000000" - }, - "b28bb39f3466517cd46f979cf59653ee7d8f152e": { - "balance": "450000000000000000000" - }, - "9da8e22ca10e67fea44e525e4751eeac36a31194": { - "balance": "260000000000000000000" - }, - "4f8ae80238e60008557075ab6afe0a7f2e74d729": { - "balance": "100000000000000000000" - }, - "74ed33acf43f35b98c9230b9e6642ecb5330839e": { - "balance": "681872000000000000000" - }, - "22842ab830da509913f81dd1f04f10af9edd1c55": { - "balance": "2000000000000000000000" - }, - "a8f37f0ab3a1d448a9e3ce40965f97a646083a34": { - "balance": "329800000000000000000" - }, - "582b70669c97aab7d68148d8d4e90411e2810d56": { - "balance": "999972000000000000000" - }, - "d5e55100fbd1956bbed2ca518d4b1fa376032b0b": { - "balance": "100000000000000000000" - }, - "b7cc6b1acc32d8b295df68ed9d5e60b8f64cb67b": { - "balance": "300000000000000000000" - }, - "e081ca1f4882db6043d5a9190703fde0ab3bf56d": { - "balance": "400000000000000000000" - }, - "c02077449a134a7ad1ef7e4d927affeceeadb5ae": { - "balance": "18200000000000000000" - }, - "e09fea755aee1a44c0a89f03b5deb762ba33006f": { - "balance": "1100070000000000000000" - }, - "b3717731dad65132da792d876030e46ac227bb8a": { - "balance": "1000000000000000000000" - }, - "157eb3d3113bd3b597714d3a954edd018982a5cb": { - "balance": "2000000000000000000000" - }, - "dc57345b38e0f067c9a31d9deac5275a10949321": { - "balance": "200000000000000000000" - }, - "40ea5044b204b23076b1a5803bf1d30c0f88871a": { - "balance": "14000000000000000000000" - }, - "2bab0fbe28d58420b52036770a12f9952aea6911": { - "balance": "3820000000000000000000" - }, - "adaa0e548c035affed64ca678a963fabe9a26bfd": { - "balance": "70000000000000000000" - }, - "bb48eaf516ce2dec3e41feb4c679e4957641164f": { - "balance": "3820000000000000000000" - }, - "7693bdeb6fc82b5bca721355223175d47a084b4d": { - "balance": "22000000000000000000000" - }, - "03cb98d7acd817de9d886d22fab3f1b57d92a608": { - "balance": "1600000000000000000000" - }, - "f88900db737955b1519b1a7d170a18864ce590eb": { - "balance": "18200000000000000000" - }, - "757fa55446c460968bb74b5ebca96c4ef2c709c5": { - "balance": "1015200000000000000000" - }, - "da855d53477f505ec4c8d5e8bb9180d38681119c": { - "balance": "5600000000000000000000" - }, - "e41aea250b877d423a63ba2bce2f3a61c0248d56": { - "balance": "260000000000000000000" - }, - "8262169b615870134eb4ac6c5f471c6bf2f789fc": { - "balance": "462500000000000000000" - }, - "66b0c100c49149935d14c0dc202cce907cea1a3d": { - "balance": "1970000000000000000000" - }, - "854c0c469c246b83b5d1b3eca443b39af5ee128a": { - "balance": "1600000000000000000000" - }, - "eb6810691d1ae0d19e47bd22cebee0b3ba27f88a": { - "balance": "2499922000000000000000" - }, - "24dcc24bd9c7210ceacfb30da98ae04a4d7b8ab9": { - "balance": "1000000000000000000000" - }, - "e31b4eef184c24ab098e36c802714bd4743dd0d4": { - "balance": "200000000000000000000" - }, - "99b8c824869de9ed24f3bff6854cb6dd45cc3f9f": { - "balance": "1880000000000000000000" - }, - "2ae73a79aea0278533accf21070922b1613f8f32": { - "balance": "3097417000000000000000" - }, - "ddbd2b932c763ba5b1b7ae3b362eac3e8d40121a": { - "balance": "10000000000000000000000" - }, - "1b4bbcb18165211b265b280716cb3f1f212176e8": { - "balance": "472325000000000000000" - }, - "e177e0c201d335ba3956929c571588b51c5223ae": { - "balance": "2000000000000000000000" - }, - "1945fe377fe6d4b71e3e791f6f17db243c9b8b0f": { - "balance": "2185500000000000000000" - }, - "3e9b34a57f3375ae59c0a75e19c4b641228d9700": { - "balance": "17900000000000000000" - }, - "a4d6c82eddae5947fbe9cdfbd548ae33d91a7191": { - "balance": "8000000000000000000000" - }, - "bad4425e171c3e72975eb46ac0a015db315a5d8f": { - "balance": "2000000000000000000000" - }, - "a2d2aa626b09d6d4e4b13f7ffc5a88bd7ad36742": { - "balance": "4639390000000000000000" - }, - "b61c34fcacda701a5aa8702459deb0e4ae838df8": { - "balance": "35000000000000000000000" - }, - "145e0600e2a927b2dd8d379356b45a2e7d51d3ae": { - "balance": "2545843000000000000000" - }, - "8df339214b6ad1b24663ce716034749d6ef838d9": { - "balance": "11000000000000000000000" - }, - "8fd9a5c33a7d9edce0997bdf77ab306424a11ea9": { - "balance": "2000000000000000000000" - }, - "097da12cfc1f7c1a2464def08c29bed5e2f851e9": { - "balance": "20000000000000000000" - }, - "ddabf13c3c8ea4e3d73d78ec717afafa430e5479": { - "balance": "41600000000000000000000" - }, - "9eeb07bd2b7890195e7d46bdf2071b6617514ddb": { - "balance": "2000000000000000000000" - }, - "819af9a1c27332b1c369bbda1b3de1c6e933d640": { - "balance": "314308000000000000000" - }, - "d7d2c6fca8ad1f75395210b57de5dfd673933909": { - "balance": "340000000000000000000" - }, - "cdd5d881a7362c9070073bdfbc75e72453ac510e": { - "balance": "842000000000000000000" - }, - "e9ac36376efa06109d40726307dd1a57e213eaa9": { - "balance": "194000000000000000000" - }, - "1bea4df5122fafdeb3607eddda1ea4ffdb9abf2a": { - "balance": "346000000000000000000" - }, - "3e5e93fb4c9c9d1246f8f247358e22c3c5d17b6a": { - "balance": "150000000000000000000" - }, - "6c1ddd33c81966dc8621776071a4129482f2c65f": { - "balance": "40000000000000000000000" - }, - "2ccb66494d0af689abf9483d365d782444e7dead": { - "balance": "1000000000000000000000" - }, - "19571a2b8f81c6bcf66ab3a10083295617150003": { - "balance": "492500000000000000000" - }, - "38ac664ee8e0795e4275cb852bcba6a479ad9c8d": { - "balance": "20000000000000000000" - }, - "c4803bb407c762f90b7596e6fde194931e769590": { - "balance": "4000000000000000000000" - }, - "93507e9e8119cbceda8ab087e7ecb071383d6981": { - "balance": "14000000000000000000000" - }, - "b672734afcc224e2e609fc51d4f059732744c948": { - "balance": "295500000000000000000" - }, - "fbbbebcfbe235e57dd2306ad1a9ec581c7f9f48f": { - "balance": "40000000000000000000" - }, - "8c81410ea8354cc5c65c41be8bd5de733c0b111d": { - "balance": "9550000000000000000000" - }, - "942c6b8c955bc0d88812678a236725b32739d947": { - "balance": "1550000000000000000000" - }, - "d2e817738abf1fb486583f80c350318bed860c80": { - "balance": "240010000000000000000" - }, - "bff5df769934b8943ca9137d0efef2fe6ebbb34e": { - "balance": "100000000000000000000" - }, - "6c4e426e8dc005dfa3516cb8a680b02eea95ae8e": { - "balance": "1337000000000000000000" - }, - "f645dd7c890093e8e4c8aa92a6bb353522d3dc98": { - "balance": "134000000000000000000" - }, - "4bac846af4169f1d95431b341d8800b22180af1a": { - "balance": "20000000000000000000" - }, - "0514954c3c2fb657f9a06f510ea22748f027cdd3": { - "balance": "400000000000000000000" - }, - "163dca73d7d6ea3f3e6062322a8734180c0b78ef": { - "balance": "2941400000000000000000" - }, - "feaca2ac74624bf348dac9985143cfd652a4be55": { - "balance": "26148245000000000000000" - }, - "fe80e9232deaff19baf99869883a4bdf0004e53c": { - "balance": "855680000000000000000" - }, - "17108dab2c50f99de110e1b3b3b4cd82f5df28e7": { - "balance": "980000000000000000000" - }, - "837a645dc95c49549f899c4e8bcf875324b2f57c": { - "balance": "600400000000000000000" - }, - "762998e1d75227fced7a70be109a4c0b4ed86414": { - "balance": "20000000000000000000" - }, - "c0a7e8435dff14c25577739db55c24d5bf57a3d9": { - "balance": "49250000000000000000000" - }, - "aead88d689416b1c91f2364421375b7d3c70fb2e": { - "balance": "2000000000000000000000" - }, - "9279b2228cec8f7b4dda3f320e9a0466c2f585ca": { - "balance": "5000000000000000000000" - }, - "36726f3b885a24f92996da81625ec8ad16d8cbe6": { - "balance": "1543723000000000000000" - }, - "3951e48e3c869e6b72a143b6a45068cdb9d466d0": { - "balance": "20000000000000000000" - }, - "f5d61ac4ca95475e5b7bffd5f2f690b316759615": { - "balance": "31040000000000000000000" - }, - "158a0d619253bf4432b5cd02c7b862f7c2b75636": { - "balance": "135733000000000000000" - }, - "e56d431324c92911a1749df292709c14b77a65cd": { - "balance": "8200000000000000000000" - }, - "9976947eff5f6ae5da08dd541192f378b428ff94": { - "balance": "8000000000000000000000" - }, - "83210583c16a4e1e1dac84ebd37e3d0f7c57eba4": { - "balance": "2000000000000000000000" - }, - "dcb64df43758c7cf974fa660484fbb718f8c67c1": { - "balance": "20000000000000000000000" - }, - "d4205592844055b3c7a1f80cefe3b8eb509bcde7": { - "balance": "178973000000000000000" - }, - "d0648a581b3508e135a2935d12c9657045d871ca": { - "balance": "8022000000000000000000" - }, - "e7d17524d00bad82497c0f27156a647ff51d2792": { - "balance": "20000000000000000000" - }, - "21582e99e502cbf3d3c23bdffb76e901ac6d56b2": { - "balance": "100000000000000000000" - }, - "e61f280915c774a31d223cf80c069266e5adf19b": { - "balance": "880000000000000000000" - }, - "03c91d92943603e752203e05340e566013b90045": { - "balance": "802200000000000000000" - }, - "22561c5931143536309c17e832587b625c390b9a": { - "balance": "4000000000000000000000" - }, - "e399c81a1d701b44f0b66f3399e66b275aaaf8c1": { - "balance": "1000000000000000000000" - }, - "7f8dbce180ed9c563635aad2d97b4cbc428906d9": { - "balance": "2674000000000000000000" - }, - "9f61beb46f5e853d0a8521c7446e68e34c7d0973": { - "balance": "560000000000000000000" - }, - "6d3f2ba856ccbb0237fa7661156b14b013f21240": { - "balance": "1000000000000000000000" - }, - "5f742e487e3ab81af2f94afdbe1b9b8f5ccc81bc": { - "balance": "2172412000000000000000" - }, - "b600feab4aa96c537504d96057223141692c193a": { - "balance": "400000000000000000000" - }, - "fab487500df20fb83ebed916791d561772adbebf": { - "balance": "1999980000000000000000" - }, - "f8704c16d2fd5ba3a2c01d0eb20484e6ecfa3109": { - "balance": "200000000000000000000" - }, - "3f1bc420c53c002c9e90037c44fe6a8ef4ddc962": { - "balance": "173000000000000000000" - }, - "82e577b515cb2b0860aafe1ce09a59e09fe7d040": { - "balance": "600000000000000000000" - }, - "bc999e385c5aebcac8d6f3f0d60d5aa725336d0d": { - "balance": "2000000000000000000000" - }, - "e16ce35961cd74bd590d04c4ad4a1989e05691c6": { - "balance": "146000000000000000000" - }, - "eb76424c0fd597d3e341a9642ad1ee118b2b579d": { - "balance": "4000000000000000000000" - }, - "c440c7ca2f964b6972ef664a2261dde892619d9c": { - "balance": "20000000000000000000000" - }, - "460d5355b2ceeb6e62107d81e51270b26bf45620": { - "balance": "2005500000000000000000" - }, - "fcada300283f6bcc134a91456760b0d77de410e0": { - "balance": "2000000000000000000000" - }, - "be8d7f18adfe5d6cc775394989e1930c979d007d": { - "balance": "1000000000000000000000" - }, - "a7f9220c8047826bd5d5183f4e676a6d77bfed36": { - "balance": "153368000000000000000" - }, - "98d204f9085f8c8e7de23e589b64c6eff692cc63": { - "balance": "2000000000000000000000" - }, - "5a2916b8d2e8cc12e207ab464d433e2370d823d9": { - "balance": "2000000000000000000000" - }, - "c42d6aeb710e3a50bfb44d6c31092969a11aa7f3": { - "balance": "150052000000000000000" - }, - "04ce45f600db18a9d0851b29d9393ebdaafe3dc5": { - "balance": "20000000000000000000" - }, - "7a1370a742ec2687e761a19ac5a794329ee67404": { - "balance": "2999988000000000000000" - }, - "da2ad58e77deddede2187646c465945a8dc3f641": { - "balance": "660000000000000000000" - }, - "ec58bc0d0c20d8f49465664153c5c196fe59e6be": { - "balance": "400000000000000000000" - }, - "f8063af4cc1dd9619ab5d8bff3fcd1faa8488221": { - "balance": "2000000000000000000000" - }, - "b9231eb26e5f9e4b4d288f03906704fab96c87d6": { - "balance": "19700000000000000000000" - }, - "6e5c2d9b1c546a86eefd5d0a5120c9e4e730190e": { - "balance": "199600000000000000000" - }, - "e49936a92a8ccf710eaac342bc454b9b14ebecb1": { - "balance": "2000000000000000000000" - }, - "21dbdb817a0d8404c6bdd61504374e9c43c9210e": { - "balance": "9999917000000000000000" - }, - "5cebe30b2a95f4aefda665651dc0cf7ef5758199": { - "balance": "18200000000000000000" - }, - "597038ff91a0900cbbab488af483c790e6ec00a0": { - "balance": "10000000000000000000000" - }, - "0fa5d8c5b3f294efd495ab69d768f81872508548": { - "balance": "2000000000000000000000" - }, - "feef3b6eabc94affd3310c1c4d0e65375e131119": { - "balance": "20000000000000000000" - }, - "1ce81d31a7923022e125bf48a3e03693b98dc9dd": { - "balance": "2000000000000000000000" - }, - "5887dc6a33dfed5ac1edefe35ef91a216231ac96": { - "balance": "250000000000000000000" - }, - "4e8e47ae3b1ef50c9d54a38e14208c1abd3603c2": { - "balance": "2235000000000000000000" - }, - "e845e387c4cbdf982280f6aa01c40e4be958ddb2": { - "balance": "25000000000000000000000" - }, - "71d9494e50c5dd59c599dba3810ba1755e6537f0": { - "balance": "4000000000000000000000" - }, - "6eb5578a6bb7c32153195b0d8020a6914852c059": { - "balance": "660000000000000000000000" - }, - "543f8c674e2462d8d5daa0e80195a8708e11a29e": { - "balance": "63940000000000000000" - }, - "a0459ef3693aacd1647cd5d8929839204cef53be": { - "balance": "1000000000000000000000" - }, - "dda371e600d30688d4710e088e02fdf2b9524d5f": { - "balance": "6920000000000000000000" - }, - "dd4dd6d36033b0636fcc8d0938609f4dd64f4a86": { - "balance": "60000000000000000000" - }, - "3bd624b548cb659736907ed8aa3c0c705e24b575": { - "balance": "2000000000000000000000" - }, - "414599092e879ae25372a84d735af5c4e510cd6d": { - "balance": "400000000000000000000" - }, - "3d66cd4bd64d5c8c1b5eea281e106d1c5aad2373": { - "balance": "1951100000000000000000" - }, - "5948bc3650ed519bf891a572679fd992f8780c57": { - "balance": "197000000000000000000" - }, - "8b74a7cb1bb8c58fce267466a30358adaf527f61": { - "balance": "13620000000000000000000" - }, - "3f10800282d1b7ddc78fa92d8230074e1bf6aeae": { - "balance": "4925000000000000000000" - }, - "32dbb6716c54e83165829a4abb36757849b6e47d": { - "balance": "1000000000000000000000" - }, - "e6b3ac3f5d4da5a8857d0b3f30fc4b2b692b77d7": { - "balance": "1460000000000000000000" - }, - "052a58e035f1fe9cdd169bcf20970345d12b9c51": { - "balance": "1490000000000000000000" - }, - "581bdf1bb276dbdd86aedcdb397a01efc0e00c5b": { - "balance": "1000000000000000000000" - }, - "604e9477ebf4727c745bcabbedcb6ccf29994022": { - "balance": "1000060000000000000000" - }, - "59b96deb8784885d8d3b4a166143cc435d2555a1": { - "balance": "1337000000000000000000" - }, - "37d980a12ee3bf23cc5cdb63b4ae45691f74c837": { - "balance": "2000000000000000000000" - }, - "3bfbd3847c17a61cf3f17b52f8eba1b960b3f39f": { - "balance": "3000000000000000000000" - }, - "49c941e0e5018726b7290fc473b471d41dae80d1": { - "balance": "500000000000000000000" - }, - "f26bcedce3feadcea3bc3e96eb1040dfd8ffe1a0": { - "balance": "775000000000000000000" - }, - "d0944aa185a1337061ae20dc9dd96c83b2ba4602": { - "balance": "200000000000000000000" - }, - "904caa429c619d940f8e6741826a0db692b19728": { - "balance": "1000000000000000000000" - }, - "b95c9b10aa981cf4a67a71cc52c504dee8cf58bd": { - "balance": "4000000000000000000000" - }, - "15874686b6733d10d703c9f9bec6c52eb8628d67": { - "balance": "2000000000000000000000" - }, - "1374facd7b3f8d68649d60d4550ee69ff0484133": { - "balance": "269700000000000000000" - }, - "b0e469c886593815b3495638595daef0665fae62": { - "balance": "1940000000000000000000" - }, - "47ff6feb43212060bb1503d7a397fc08f4e70352": { - "balance": "2000000000000000000000" - }, - "c60b04654e003b4683041f1cbd6bc38fda7cdbd6": { - "balance": "2000000000000000000000" - }, - "3ecdb532e397579662b2a46141e78f8235936a5f": { - "balance": "66850000000000000000" - }, - "b3a8c2cb7d358e5739941d945ba9045a023a8bbb": { - "balance": "1000000000000000000000" - }, - "32ef5cdc671df5562a901aee5db716b9be76dcf6": { - "balance": "2000000000000000000000" - }, - "c94110e71afe578aa218e4fc286403b0330ace8d": { - "balance": "2000000000000000000000" - }, - "9b43dcb95fde318075a567f1e6b57617055ef9e8": { - "balance": "3940000000000000000000" - }, - "efeea010756f81da4ba25b721787f058170befbd": { - "balance": "32470000000000000000" - }, - "c88255eddcf521c6f81d97f5a42181c9073d4ef1": { - "balance": "290793000000000000000" - }, - "dd47189a3e64397167f0620e484565b762bfbbf4": { - "balance": "1850000000000000000000" - }, - "82f39b2758ae42277b86d69f75e628d958ebcab0": { - "balance": "40000000000000000000000" - }, - "e37f5fdc6ec97d2f866a1cfd0d3a4da4387b22b5": { - "balance": "10000000000000000000000" - }, - "62331df2a3cbee3520e911dea9f73e905f892505": { - "balance": "2000000000000000000000" - }, - "8c5d16ed65e3ed7e8b96ca972bc86173e3500b03": { - "balance": "2000000000000000000000" - }, - "8b9841862e77fbbe919470935583a93cf027e450": { - "balance": "2000054000000000000000" - }, - "c8dd27f16bf22450f5771b9fe4ed4ffcb30936f4": { - "balance": "197000000000000000000" - }, - "dec8a1a898f1b895d8301fe64ab3ad5de941f689": { - "balance": "787803000000000000000" - }, - "61c4ee7c864c4d6b5e37ea1331c203739e826b2f": { - "balance": "30063000000000000000" - }, - "3250e3e858c26adeccadf36a5663c22aa84c4170": { - "balance": "5000000000000000000000" - }, - "299e0bca55e069de8504e89aca6eca21d38a9a5d": { - "balance": "55500000000000000000" - }, - "d50f7fa03e389876d3908b60a537a6706304fb56": { - "balance": "100000000000000000000" - }, - "69073269729e6414b26ec8dc0fd935c73b579f1e": { - "balance": "30000000000000000000000" - }, - "14fcd1391e7d732f41766cdacd84fa1deb9ffdd2": { - "balance": "2000000000000000000000" - }, - "823768746737ce6da312d53e54534e106f967cf3": { - "balance": "20000000000000000000" - }, - "882f75708386653c80171d0663bfe30b017ed0ad": { - "balance": "2000000000000000000000" - }, - "a25b086437fd2192d0a0f64f6ed044f38ef3da32": { - "balance": "335000000000000000000" - }, - "5a9c8b69fc614d69564999b00dcb42db67f97e90": { - "balance": "3429227000000000000000" - }, - "a2b701f9f5cdd09e4ba62baebae3a88257105885": { - "balance": "1000000000000000000000" - }, - "5e7b8c54dc57b0402062719dee7ef5e37ea35d62": { - "balance": "2877224000000000000000" - }, - "7ffabfbc390cbe43ce89188f0868b27dcb0f0cad": { - "balance": "6370000000000000000000" - }, - "b5cdbc4115406f52e5aa85d0fea170d2979cc7ba": { - "balance": "1337000000000000000000" - }, - "263814309de4e635cf585e0d365477fc40e66cf7": { - "balance": "146000000000000000000" - }, - "24cff0e9336a9f80f9b1cb968caf6b1d1c4932a4": { - "balance": "200200000000000000000" - }, - "d3a941c961e8ca8b1070f23c6d6d0d2a758a4444": { - "balance": "200000000000000000000" - }, - "a97beb3a48c45f1528284cb6a95f7de453358ec6": { - "balance": "31000000000000000000000" - }, - "4dd131c74a068a37c90aded4f309c2409f6478d3": { - "balance": "400008000000000000000" - }, - "653675b842d7d8b461f722b4117cb81dac8e639d": { - "balance": "31000000000000000000" - }, - "561be9299b3e6b3e63b79b09169d1a948ae6db01": { - "balance": "500000000000000000000" - }, - "dc067ed3e12d711ed475f5156ef7e71a80d934b9": { - "balance": "9550000000000000000000" - }, - "08d97eadfcb7b064e1ccd9c8979fbee5e77a9719": { - "balance": "266063000000000000000" - }, - "6e4c2ab7db026939dbd3bc68384af660a61816b2": { - "balance": "167000000000000000000" - }, - "bf4c73a7ede7b164fe072114843654e4d8781dde": { - "balance": "2000000000000000000000" - }, - "f504943aaf16796e0b341bbcdf21d11cc586cdd1": { - "balance": "9000000000000000000000" - }, - "ea81ca8638540cd9d4d73d060f2cebf2241ffc3e": { - "balance": "1970000000000000000000" - }, - "9944fee9d34a4a880023c78932c00b59d5c82a82": { - "balance": "750022000000000000000" - }, - "12f460ae646cd2780fd35c50a6af4b9accfa85c6": { - "balance": "1000000000000000000000" - }, - "4e232d53b3e6be8f895361d31c34d4762b12c82e": { - "balance": "1760000000000000000000" - }, - "6bb2aca23fa1626d18efd6777fb97db02d8e0ae4": { - "balance": "40000000000000000000000" - }, - "bc4e471560c99c8a2a4b1b1ad0c36aa6502b7c4b": { - "balance": "12000000000000000000000" - }, - "2e2cbd7ad82547b4f5ff8b3ab56f942a6445a3b0": { - "balance": "200000000000000000000" - }, - "21ecb2dfa65779c7592d041cd2105a81f4fd4e46": { - "balance": "1000000000000000000000" - }, - "34318625818ec13f11835ae97353ce377d6f590a": { - "balance": "1520000000000000000000" - }, - "a7ef35ce87eda6c28df248785815053ec97a5045": { - "balance": "4999998000000000000000" - }, - "6a514e6242f6b68c137e97fea1e78eb555a7e5f7": { - "balance": "20000000000000000000" - }, - "9340b5f678e45ee05eb708bb7abb6ec8f08f1b6b": { - "balance": "6000000000000000000000" - }, - "43cc08d0732aa58adef7619bed46558ad7774173": { - "balance": "4443926000000000000000" - }, - "12e9a4ad2ad57484dd700565bddb46423bd9bd31": { - "balance": "19999800000000000000000" - }, - "ebbeeb259184a6e01cccfc2207bbd883785ac90a": { - "balance": "619966000000000000000" - }, - "704ab1150d5e10f5e3499508f0bf70650f028d4b": { - "balance": "4000000000000000000000" - }, - "fc361105dd90f9ede566499d69e9130395f12ac8": { - "balance": "395000000000000000000000" - }, - "c1b9a5704d351cfe983f79abeec3dbbbae3bb629": { - "balance": "20000000000000000000" - }, - "66f50406eb1b11a946cab45927cca37470e5a208": { - "balance": "2000000000000000000000" - }, - "53942e7949d6788bb780a7e8a0792781b1614b84": { - "balance": "15899600000000000000000" - }, - "32ba9a7d0423e03a525fe2ebeb661d2085778bd8": { - "balance": "20000000000000000000000" - }, - "11c0358aa6479de21866fe21071924b65e70f8b9": { - "balance": "36400000000000000000000" - }, - "76cb9c8b69f4387675c48253e234cb7e0d74a426": { - "balance": "7396300000000000000000" - }, - "9f5f44026b576a4adb41e95961561d41039ca391": { - "balance": "250000000000000000000" - }, - "533a73a4a2228eee05c4ffd718bbf3f9c1b129a7": { - "balance": "6000000000000000000000" - }, - "dcc52d8f8d9fc742a8b82767f0555387c563efff": { - "balance": "500000000000000000000" - }, - "f456a75bb99655a7412ce97da081816dfdb2b1f2": { - "balance": "200000000000000000000" - }, - "d0c101fd1f01c63f6b1d19bc920d9f932314b136": { - "balance": "20000000000000000000000" - }, - "dabc225042a6592cfa13ebe54efa41040878a5a2": { - "balance": "259550000000000000000" - }, - "38eec6e217f4d41aa920e424b9525197041cd4c6": { - "balance": "4428166000000000000000" - }, - "8a247d186510809f71cffc4559471c3910858121": { - "balance": "1790000000000000000000" - }, - "4f152b2fb8659d43776ebb1e81673aa84169be96": { - "balance": "2000000000000000000000" - }, - "b4496ddb27799a222457d73979116728e8a1845b": { - "balance": "2610331000000000000000" - }, - "4a4053b31d0ee5dbafb1d06bd7ac7ff3222c47d6": { - "balance": "1400000000000000000000" - }, - "0f7bea4ef3f73ae0233df1e100718cbe29310bb0": { - "balance": "2000000000000000000000" - }, - "c836e24a6fcf29943b3608e662290a215f6529ea": { - "balance": "292000000000000000000" - }, - "1765361c2ec2f83616ce8363aae21025f2566f40": { - "balance": "5000000000000000000000" - }, - "b6e6c3222b6b6f9be2875d2a89f127fb64100fe2": { - "balance": "8008000000000000000000" - }, - "01bbc14f67af0639aab1441e6a08d4ce7162090f": { - "balance": "1309500000000000000000" - }, - "af2058c7282cf67c8c3cf930133c89617ce75d29": { - "balance": "6920000000000000000000" - }, - "464d9c89cce484df000277198ed8075fa63572d1": { - "balance": "20000000000000000000" - }, - "50cd97e9378b5cf18f173963236c9951ef7438a5": { - "balance": "1400000000000000000000" - }, - "cb47bd30cfa8ec5468aaa6a94642ced9c819c8d4": { - "balance": "4000000000000000000000" - }, - "6b10f8f8b3e3b60de90aa12d155f9ff5ffb22c50": { - "balance": "2000000000000000000000" - }, - "09b7a988d13ff89186736f03fdf46175b53d16e0": { - "balance": "6000000000000000000000" - }, - "5bfafe97b1dd1d712be86d41df79895345875a87": { - "balance": "500000000000000000000" - }, - "a06cd1f396396c0a64464651d7c205efaf387ca3": { - "balance": "1999944000000000000000" - }, - "fc0096b21e95acb8d619d176a4a1d8d529badbef": { - "balance": "384601000000000000000" - }, - "a74444f90fbb54e56f3ac9b6cfccaa4819e4614a": { - "balance": "20000000000000000000" - }, - "3c15b3511df6f0342e7348cc89af39a168b7730f": { - "balance": "1000000000000000000000" - }, - "3d6ff82c9377059fb30d9215723f60c775c891fe": { - "balance": "250066000000000000000" - }, - "a524a8cccc49518d170a328270a2f88133fbaf5d": { - "balance": "294500000000000000000" - }, - "8a7a06be199a3a58019d846ac9cbd4d95dd757de": { - "balance": "3000200000000000000000" - }, - "d744ac7e5310be696a63b003c40bd039370561c6": { - "balance": "1670000000000000000000" - }, - "fe362688845fa244cc807e4b1130eb3741a8051e": { - "balance": "1000000000000000000000" - }, - "b2d0360515f17daba90fcbac8205d569b915d6ac": { - "balance": "6000000000000000000000" - }, - "c53594c7cfb2a08f284cc9d7a63bbdfc0b319732": { - "balance": "49200000000000000000000" - }, - "b3c228731d186d2ded5b5fbe004c666c8e469b86": { - "balance": "29000000000000000000" - }, - "63e414603e80d4e5a0f5c18774204642258208e4": { - "balance": "5000000000000000000000" - }, - "826ce5790532e0548c6102a30d3eac836bd6388f": { - "balance": "18000000000000000000000" - }, - "c5e812f76f15f2e1f2f9bc4823483c8804636f67": { - "balance": "73000000000000000000" - }, - "116fef5e601642c918cb89160fc2293ba71da936": { - "balance": "802200000000000000000" - }, - "08b84536b74c8c01543da88b84d78bb95747d822": { - "balance": "200000000000000000000" - }, - "04a80afad53ef1f84165cfd852b0fdf1b1c24ba8": { - "balance": "58000000000000000000" - }, - "2b0362633614bfcb583569438ecc4ea57b1d337e": { - "balance": "20000000000000000000000" - }, - "e95179527deca5916ca9a38f215c1e9ce737b4c9": { - "balance": "10000000000000000000000" - }, - "2c5df866666a194b26cebb407e4a1fd73e208d5e": { - "balance": "1000000000000000000000" - }, - "529e824fa072582b4032683ac7eecc1c04b4cac1": { - "balance": "2000000000000000000000" - }, - "78634371e17304cbf339b1452a4ce438dc764cce": { - "balance": "10000000000000000000000" - }, - "e172dfc8f80cd1f8cd8539dc26082014f5a8e3e8": { - "balance": "3000000000000000000000" - }, - "b07618328a901307a1b7a0d058fcd5786e9e72fe": { - "balance": "30239500000000000000000" - }, - "b0571153db1c4ed7acaefe13ecdfdb72e7e4f06a": { - "balance": "80520000000000000000000" - }, - "ad910a23d6850613654af786337ad2a70868ac6d": { - "balance": "1999800000000000000000" - }, - "4da5edc688b0cb62e1403d1700d9dcb99ffe3fd3": { - "balance": "2000000000000000000000" - }, - "be2471a67f6047918772d0e36839255ed9d691ae": { - "balance": "4000000000000000000000" - }, - "28868324337e11ba106cb481da962f3a8453808d": { - "balance": "2000000000000000000000" - }, - "d8f94579496725b5cb53d7985c989749aff849c0": { - "balance": "17000000000000000000000" - }, - "4981c5ff66cc4e9680251fc4cd2ff907cb327865": { - "balance": "750000000000000000000" - }, - "fd2872d19e57853cfa16effe93d0b1d47b4f93fb": { - "balance": "4000000000000000000000" - }, - "63c8dfde0b8e01dadc2e748c824cc0369df090b3": { - "balance": "3880000000000000000000" - }, - "c4dd048bfb840e2bc85cb53fcb75abc443c7e90f": { - "balance": "3716000000000000000000" - }, - "f579714a45eb8f52c3d57bbdefd2c15b2e2f11df": { - "balance": "1560000000000000000000" - }, - "cc7b0481cc32e6faef2386a07022bcb6d2c3b4fc": { - "balance": "3160000000000000000000" - }, - "a0aa5f0201f04d3bbeb898132f7c11679466d901": { - "balance": "36600000000000000000" - }, - "f3df63a97199933330383b3ed7570b96c4812334": { - "balance": "2000000000000000000000" - }, - "42732d8ef49ffda04b19780fd3c18469fb374106": { - "balance": "425068000000000000000" - }, - "6f92d6e4548c78996509ee684b2ee29ba3c532b4": { - "balance": "1000000000000000000000" - }, - "fff4bad596633479a2a29f9a8b3f78eefd07e6ee": { - "balance": "100000000000000000000" - }, - "ac4460a76e6db2b9fcd152d9c7718d9ac6ed8c6f": { - "balance": "200000000000000000000" - }, - "553b6b1c57050e88cf0c31067b8d4cd1ff80cb09": { - "balance": "400000000000000000000" - }, - "84b6b6adbe2f5b3e2d682c66af1bc4905340c3ed": { - "balance": "619333000000000000000" - }, - "9f4a7195ac7c151ca258cafda0cab083e049c602": { - "balance": "1537100000000000000000" - }, - "2955c357fd8f75d5159a3dfa69c5b87a359dea8c": { - "balance": "2000000000000000000000" - }, - "11d7844a471ef89a8d877555583ceebd1439ea26": { - "balance": "10098000000000000000000" - }, - "34b454416e9fb4274e6addf853428a0198d62ee1": { - "balance": "407000000000000000000" - }, - "308dd21cebe755126704b48c0f0dc234c60ba9b1": { - "balance": "200000000000000000000" - }, - "381db4c8465df446a4ce15bf81d47e2f17c980bf": { - "balance": "32000000000000000000000" - }, - "1abc4e253b080aeb437984ab05bca0979aa43e1c": { - "balance": "1000000000000000000000" - }, - "53e35b12231f19c3fd774c88fec8cbeedf1408b2": { - "balance": "512000000000000000000" - }, - "69e2e2e704307ccc5b5ca3f164fece2ea7b2e512": { - "balance": "7000000000000000000000" - }, - "1914f1eb95d1277e93b6e61b668b7d77f13a11a1": { - "balance": "970000000000000000000" - }, - "50e13023bd9ca96ad4c53fdfd410cb6b1f420bdf": { - "balance": "200000000000000000000" - }, - "46224f32f4ece5c8867090d4409d55e50b18432d": { - "balance": "6000000000000000000000" - }, - "ff83855051ee8ffb70b4817dba3211ed2355869d": { - "balance": "400000000000000000000" - }, - "fb39189af876e762c71d6c3e741893df226cedd6": { - "balance": "4000000000000000000000" - }, - "9875623495a46cdbf259530ff838a1799ec38991": { - "balance": "2000000000000000000000" - }, - "e1b39b88d9900dbc4a6cdc481e1060080a8aec3c": { - "balance": "2000000000000000000000" - }, - "5baf6d749620803e8348af3710e5c4fbf20fc894": { - "balance": "5003680000000000000000" - }, - "9c54e4ed479a856829c6bb42da9f0b692a75f728": { - "balance": "7520000000000000000000" - }, - "486a6c8583a84484e3df43a123837f8c7e2317d0": { - "balance": "323378000000000000000" - }, - "d235d15cb5eceebb61299e0e827fa82748911d89": { - "balance": "4000000000000000000000" - }, - "47d792a756779aedf1343e8883a6619c6c281184": { - "balance": "2000000000000000000000" - }, - "70c213488a020c3cfb39014ef5ba6404724bcaa3": { - "balance": "1940000000000000000000" - }, - "133c490fa5bf7f372888e607d958fab7f955bae1": { - "balance": "1580000000000000000000" - }, - "a9e194661aac704ee9dea043974e9692ded84a5d": { - "balance": "482400000000000000000" - }, - "bc6b58364bf7f1951c309e0cba0595201cd73f9a": { - "balance": "1812400000000000000000" - }, - "2309d34091445b3232590bd70f4f10025b2c9509": { - "balance": "10000000000000000000000" - }, - "d89bc271b27ba3ab6962c94a559006ae38d5f56a": { - "balance": "2000000000000000000000" - }, - "ff0e2fec304207467e1e3307f64cbf30af8fd9cd": { - "balance": "2000000000000000000000" - }, - "c0b0b7a8a6e1acdd05e47f94c09688aa16c7ad8d": { - "balance": "64234000000000000000" - }, - "b66f92124b5e63035859e390628869dbdea9485e": { - "balance": "9850000000000000000000" - }, - "a9e6e25e656b762558619f147a21985b8874edfe": { - "balance": "2000000000000000000000" - }, - "a43e1947a9242b355561c30a829dfeeca2815af8": { - "balance": "3878255000000000000000" - }, - "8b20ad3b94656dbdc0dd21a393d8a7d9e02138cb": { - "balance": "3000000000000000000000" - }, - "aca2a838330b17302da731d30db48a04f0f207c1": { - "balance": "1337000000000000000000" - }, - "fa60868aafd4ff4c5c57914b8ed58b425773dfa9": { - "balance": "8557400000000000000000" - }, - "1848003c25bfd4aa90e7fcb5d7b16bcd0cffc0d8": { - "balance": "1000000000000000000000" - }, - "b4b185d943ee2b58631e33dff5af6854c17993ac": { - "balance": "1000000000000000000000" - }, - "7719888795ad745924c75760ddb1827dffd8cda8": { - "balance": "1999980000000000000000" - }, - "ccd521132d986cb96869842622a7dda26c3ed057": { - "balance": "2000000000000000000000" - }, - "253e32b74ea4490ab92606fda0aa257bf23dcb8b": { - "balance": "10000000000000000000000" - }, - "3712367e5e55a96d5a19168f6eb2bc7e9971f869": { - "balance": "1000000000000000000000" - }, - "8f29a14a845ad458f2d108b568d813166bcdf477": { - "balance": "10000000000000000000000" - }, - "51a8c2163602a32ee24cf4aa97fd9ea414516941": { - "balance": "62904000000000000000" - }, - "61cea71fa464d62a07063f920b0cc917539733d8": { - "balance": "1670000000000000000000" - }, - "6f81f3abb1f933b1df396b8e9cc723a89b7c9806": { - "balance": "280000000000000000000" - }, - "61b1b8c012cd4c78f698e470f90256e6a30f48dd": { - "balance": "200000000000000000000" - }, - "4f3f2c673069ac97c2023607152981f5cd6063a0": { - "balance": "600000000000000000000" - }, - "e2efa5fca79538ce6068bf31d2c516d4d53c08e5": { - "balance": "131200000000000000000" - }, - "2383c222e67e969190d3219ef14da37850e26c55": { - "balance": "2000000000000000000000" - }, - "eac3af5784927fe9a598fc4eec38b8102f37bc58": { - "balance": "1000000000000000000000" - }, - "4fe56ab3bae1b0a44433458333c4b05a248f8241": { - "balance": "2180000000000000000000" - }, - "fe9cfc3bb293ddb285e625f3582f74a6b0a5a6cd": { - "balance": "1970000000000000000000" - }, - "f48e1f13f6af4d84b371d7de4b273d03a263278e": { - "balance": "600000000000000000000" - }, - "1ba9228d388727f389150ea03b73c82de8eb2e09": { - "balance": "7258000000000000000000" - }, - "37a7a6ff4ea3d60ec307ca516a48d3053bb79cbb": { - "balance": "2000000000000000000000" - }, - "e33840d8bca7da98a6f3d096d83de78b70b71ef8": { - "balance": "2000000000000000000000" - }, - "8e7fd23848f4db07906a7d10c04b21803bb08227": { - "balance": "1000000000000000000000" - }, - "07d4334ec385e8aa54eedaeadb30022f0cdfa4ab": { - "balance": "2629946000000000000000" - }, - "d4b085fb086f3d0d68bf12926b1cc3142cae8770": { - "balance": "3700000000000000000000" - }, - "5a87f034e6f68f4e74ffe60c64819436036cf7d7": { - "balance": "20000000000000000000" - }, - "c00ab080b643e1c2bae363e0d195de2efffc1c44": { - "balance": "500000000000000000000" - }, - "22f3c779dd79023ea92a78b65c1a1780f62d5c4a": { - "balance": "1970000000000000000000" - }, - "c7d5c7054081e918ec687b5ab36e973d18132935": { - "balance": "182000000000000000000" - }, - "9662ee021926682b31c5f200ce457abea76c6ce9": { - "balance": "670500000000000000000" - }, - "116a09df66cb150e97578e297fb06e13040c893c": { - "balance": "2000000000000000000000" - }, - "b7240af2af90b33c08ae9764103e35dce3638428": { - "balance": "8464547000000000000000" - }, - "e8b28acda971725769db8f563d28666d41ddab6c": { - "balance": "10000000000000000000000" - }, - "17d4918dfac15d77c47f9ed400a850190d64f151": { - "balance": "2000000000000000000000" - }, - "c42250b0fe42e6b7dcd5c890a6f0c88f5f5fb574": { - "balance": "149800000000000000000" - }, - "5da2a9a4c2c0a4a924cbe0a53ab9d0c627a1cfa0": { - "balance": "733202000000000000000" - }, - "5869fb867d71f1387f863b698d09fdfb87c49b5c": { - "balance": "3666000000000000000000" - }, - "d49a75bb933fca1fca9aa1303a64b6cb44ea30e1": { - "balance": "10000000000000000000000" - }, - "76331e30796ce664b2700e0d4153700edc869777": { - "balance": "2000000000000000000000" - }, - "8a5fb75793d043f1bcd43885e037bd30a528c927": { - "balance": "356500000000000000000" - }, - "fc0ee6f7c2b3714ae9916c45566605b656f32441": { - "balance": "1760000000000000000000" - }, - "bf50ce2e264b9fe2b06830617aedf502b2351b45": { - "balance": "1000000000000000000000" - }, - "0f6000de1578619320aba5e392706b131fb1de6f": { - "balance": "499986000000000000000" - }, - "c953f934c0eb2d0f144bdab00483fd8194865ce7": { - "balance": "2000000000000000000000" - }, - "24fd9a6c874c2fab3ff36e9afbf8ce0d32c7de92": { - "balance": "1337000000000000000000" - }, - "c6cd68ec35362c5ad84c82ad4edc232125912d99": { - "balance": "27750000000000000000000" - }, - "2a67660a1368efcd626ef36b2b1b601980941c05": { - "balance": "133700000000000000000" - }, - "9deb39027af877992b89f2ec4a1f822ecdf12693": { - "balance": "2000000000000000000000" - }, - "c12f881fa112b8199ecbc73ec4185790e614a20f": { - "balance": "2000000000000000000000" - }, - "d58a52e078a805596b0d56ea4ae1335af01c66eb": { - "balance": "267400000000000000000" - }, - "4d7cfaa84cb33106800a8c802fb8aa463896c599": { - "balance": "1790000000000000000000" - }, - "0ee391f03c765b11d69026fd1ab35395dc3802a0": { - "balance": "200000000000000000000" - }, - "a192f06ab052d5fd7f94eea8318e827815fe677a": { - "balance": "131400000000000000000" - }, - "8f0ab894bd3f4e697dbcfb859d497a9ba195994a": { - "balance": "39501652000000000000000" - }, - "387eeafd6b4009deaf8bd5b85a72983a8dcc3487": { - "balance": "4000000000000000000000" - }, - "03b0f17cd4469ddccfb7da697e82a91a5f9e7774": { - "balance": "20000000000000000000" - }, - "11172b278ddd44eea2fdf4cb1d16962391c453d9": { - "balance": "935900000000000000000000" - }, - "33d172ab075c51db1cd40a8ca8dbff0d93b843bb": { - "balance": "5727139000000000000000" - }, - "909b5e763a39dcc795223d73a1dbb7d94ca75ac8": { - "balance": "2000000000000000000000" - }, - "0ca12ab0b9666cf0cec6671a15292f2653476ab2": { - "balance": "210000600000000000000000" - }, - "6b5ae7bf78ec75e90cb503c778ccd3b24b4f1aaf": { - "balance": "800000000000000000000" - }, - "d9e3857efd1e202a441770a777a49dcc45e2e0d3": { - "balance": "223500000000000000000" - }, - "d703c6a4f11d60194579d58c2766a7ef16c30a29": { - "balance": "2000000000000000000000" - }, - "838bd565f99fde48053f7917fe333cf84ad548ab": { - "balance": "200000000000000000000" - }, - "8168edce7f2961cf295b9fcd5a45c06cdeda6ef5": { - "balance": "200000000000000000000" - }, - "de50868eb7e3c71937ec73fa89dd8b9ee10d45aa": { - "balance": "1000000000000000000000" - }, - "087498c0464668f31150f4d3c4bcdda5221ba102": { - "balance": "20000000000000000000" - }, - "613fab44b16bbe554d44afd178ab1d02f37aeaa5": { - "balance": "2000000000000000000000" - }, - "e2ee691f237ee6529b6557f2fcdd3dcf0c59ec63": { - "balance": "5450048000000000000000" - }, - "a9ed377b7d6ec25971c1a597a3b0f3bead57c98f": { - "balance": "400000000000000000000" - }, - "175feeea2aa4e0efda12e1588d2f483290ede81a": { - "balance": "200000000000000000000" - }, - "b51ddcb4dd4e8ae6be336dd9654971d9fec86b41": { - "balance": "421133000000000000000" - }, - "92c0f573eccf62c54810ee6ba8d1f113542b301b": { - "balance": "3384000000000000000000" - }, - "a109e18bb0a39c9ef82fa19597fc5ed8e9eb6d58": { - "balance": "1640000000000000000000" - }, - "f74e6e145382b4db821fe0f2d98388f45609c69f": { - "balance": "100000000000000000000" - }, - "378f37243f3ff0bef5e1dc85eb4308d9340c29f9": { - "balance": "2000200000000000000000" - }, - "84e9949680bece6841b9a7e5250d08acd87d16cd": { - "balance": "200000000000000000000" - }, - "882bd3a2e9d74110b24961c53777f22f1f46dc5d": { - "balance": "13370000000000000000000" - }, - "acce01e0a70610dc70bb91e9926fa9957f372fba": { - "balance": "537000000000000000000" - }, - "c5f687717246da8a200d20e5e9bcac60b67f3861": { - "balance": "28650000000000000000" - }, - "e14617f6022501e97e7b3e2d8836aa61f0ff2dba": { - "balance": "200000000000000000000" - }, - "076ee99d3548623a03b5f99859d2d785a1778d48": { - "balance": "200000000000000000000" - }, - "2c424ee47f583cdce07ae318b6fad462381d4d2b": { - "balance": "4000000000000000000000" - }, - "f98250730c4c61c57f129835f2680894794542f3": { - "balance": "4000000000000000000000" - }, - "ed1b24b6912d51b334ac0de6e771c7c0454695ea": { - "balance": "40000000000000000000" - }, - "ffd5170fd1a8118d558e7511e364b24906c4f6b3": { - "balance": "60085000000000000000" - }, - "bf49c14898316567d8b709c2e50594b366c6d38c": { - "balance": "733202000000000000000" - }, - "65ea26eabbe2f64ccccfe06829c25d4637520225": { - "balance": "700000000000000000000" - }, - "5c5419565c3aad4e714e0739328e3521c98f05cc": { - "balance": "528000000000000000000" - }, - "c53b50fd3b2b72bc6c430baf194a515585d3986d": { - "balance": "20000000000000000000" - }, - "2b74c373d04bfb0fd60a18a01a88fbe84770e58c": { - "balance": "40000000000000000000" - }, - "d97f4526dea9b163f8e8e33a6bcf92fb907de6ec": { - "balance": "284000000000000000000" - }, - "a4a49f0bc8688cc9e6dc04e1e08d521026e65574": { - "balance": "200000000000000000000" - }, - "575c00c2818210c28555a0ff29010289d3f82309": { - "balance": "10000000000000000000000" - }, - "3f1233714f204de9de4ee96d073b368d8197989f": { - "balance": "38606000000000000000" - }, - "f964d98d281730ba35b2e3a314796e7b42fedf67": { - "balance": "1543800000000000000000" - }, - "1deec01abe5c0d952de9106c3dc30639d85005d6": { - "balance": "2000000000000000000000" - }, - "12d60d65b7d9fc48840be5f891c745ce76ee501e": { - "balance": "21359400000000000000000" - }, - "5c6136e218de0a61a137b2b3962d2a6112b809d7": { - "balance": "294273000000000000000" - }, - "cd43258b7392a930839a51b2ef8ad23412f75a9f": { - "balance": "2000000000000000000000" - }, - "db3f258ab2a3c2cf339c4499f75a4bd1d3472e9e": { - "balance": "1500000000000000000000" - }, - "0edd4b580ff10fe06c4a03116239ef96622bae35": { - "balance": "197000000000000000000" - }, - "1d157c5876c5cad553c912caf6ce2d5277e05c73": { - "balance": "2000000000000000000000" - }, - "cda1b886e3a795c9ba77914e0a2fe5676f0f5ccf": { - "balance": "106024000000000000000" - }, - "f50cbafd397edd556c0678988cb2af5c2617e0a2": { - "balance": "716000000000000000000" - }, - "327bb49e754f6fb4f733c6e06f3989b4f65d4bee": { - "balance": "20000000000000000000" - }, - "c44bdec8c36c5c68baa2ddf1d431693229726c43": { - "balance": "100000000000000000000000" - }, - "34e2849bea583ab0cc37975190f322b395055582": { - "balance": "7780340000000000000000" - }, - "9221c9ce01232665741096ac07235903ad1fe2fc": { - "balance": "126489000000000000000" - }, - "ff3ded7a40d3aff0d7a8c45fa6136aa0433db457": { - "balance": "1999800000000000000000" - }, - "10b5b34d1248fcf017f8c8ffc408ce899ceef92f": { - "balance": "267400000000000000000" - }, - "f1a1f320407964fd3c8f2e2cc8a4580da94f01ea": { - "balance": "2000040000000000000000" - }, - "6c800d4b49ba07250460f993b8cbe00b266a2553": { - "balance": "492500000000000000000" - }, - "f827d56ed2d32720d4abf103d6d0ef4d3bcd559b": { - "balance": "26265000000000000000" - }, - "ffb9c7217e66743031eb377af65c77db7359dcda": { - "balance": "40000000000000000000" - }, - "530319db0a8f93e5bb7d4dbf4816314fbed8361b": { - "balance": "2000000000000000000000" - }, - "9c28a2c4086091cb5da226a657ce3248e8ea7b6f": { - "balance": "280000000000000000000" - }, - "db23a6fef1af7b581e772cf91882deb2516fc0a7": { - "balance": "200000000000000000000" - }, - "6636d7ac637a48f61d38b14cfd4865d36d142805": { - "balance": "500000000000000000000" - }, - "b3c260609b9df4095e6c5dff398eeb5e2df49985": { - "balance": "254030000000000000000" - }, - "58e5c9e344c806650dacfc904d33edba5107b0de": { - "balance": "19100000000000000000" - }, - "4f67396d2553f998785f704e07a639197dd1948d": { - "balance": "300080000000000000000" - }, - "510d8159cc945768c7450790ba073ec0d9f89e30": { - "balance": "2560000000000000000000" - }, - "593c48935beaff0fde19b04d309cd530a28e52ce": { - "balance": "4000000000000000000000" - }, - "c27f4e08099d8cf39ee11601838ef9fc06d7fc41": { - "balance": "1790000000000000000000" - }, - "07723e3c30e8b731ee456a291ee0e798b0204a77": { - "balance": "2000000000000000000000" - }, - "0a652e2a8b77bd97a790d0e91361c98890dbb04e": { - "balance": "1000000000000000000000" - }, - "671015b97670b10d5e583f3d62a61c1c79c5143f": { - "balance": "400000000000000000000" - }, - "7cc24a6a958c20c7d1249660f7586226950b0d9a": { - "balance": "1970000000000000000000" - }, - "6ef9e8c9b6217d56769af97dbb1c8e1b8be799d2": { - "balance": "182000000000000000000" - }, - "5c4368918ace6409c79eca80cdaae4391d2b624e": { - "balance": "4000000000000000000000" - }, - "043707071e2ae21eed977891dc79cd5d8ee1c2da": { - "balance": "2000000000000000000000" - }, - "39bfd978689bec048fc776aa15247f5e1d7c39a2": { - "balance": "20000000000000000000000" - }, - "05915d4e225a668162aee7d6c25fcfc6ed18db03": { - "balance": "66348000000000000000" - }, - "3f551ba93cd54693c183fb9ad60d65e1609673c9": { - "balance": "2000000000000000000000" - }, - "a8c0b02faf02cb5519dda884de7bbc8c88a2da81": { - "balance": "16700000000000000000" - }, - "bd0c5cd799ebc48642ef97d74e8e429064fee492": { - "balance": "326000000000000000000" - }, - "0a931b449ea8f12cdbd5e2c8cc76bad2c27c0639": { - "balance": "23031000000000000000" - }, - "2ea5fee63f337a376e4b918ea82148f94d48a626": { - "balance": "1864242000000000000000" - }, - "cc6c2df00e86eca40f21ffda1a67a1690f477c65": { - "balance": "3160000000000000000000" - }, - "e5e37e19408f2cfbec83349dd48153a4a795a08f": { - "balance": "4200000000000000000000" - }, - "f555a27bb1e2fd4e2cc784caee92939fc06e2fc9": { - "balance": "2000000000000000000000" - }, - "dcf9719be87c6f46756db4891db9b611d2469c50": { - "balance": "1000000000000000000000" - }, - "8e2f9034c9254719c38e50c9aa64305ed696df1e": { - "balance": "4728000000000000000000" - }, - "a01f12d70f44aa7b113b285c22dcdb45873454a7": { - "balance": "18200000000000000000" - }, - "bce40475d345b0712dee703d87cd7657fc7f3b62": { - "balance": "7750000000000000000000" - }, - "bb19bf91cbad74cceb5f811db27e411bc2ea0656": { - "balance": "17600000000000000000" - }, - "acc062702c59615d3444ef6214b8862b009a02ed": { - "balance": "1499936000000000000000" - }, - "449ac4fbe383e36738855e364a57f471b2bfa131": { - "balance": "197000000000000000000000" - }, - "ad59a78eb9a74a7fbdaefafa82eada8475f07f95": { - "balance": "500000000000000000000" - }, - "6b6577f3909a4d6de0f411522d4570386400345c": { - "balance": "1880000000000000000000" - }, - "79bf2f7b6e328aaf26e0bb093fa22da29ef2f471": { - "balance": "1790000000000000000000" - }, - "940f715140509ffabf974546fab39022a41952d2": { - "balance": "1400000000000000000000" - }, - "1d572edd2d87ca271a6714c15a3b37761dcca005": { - "balance": "127674000000000000000" - }, - "d78ecd25adc86bc2051d96f65364866b42a426b7": { - "balance": "3877300000000000000000" - }, - "f9729d48282c9e87166d5eef2d01eda9dbf78821": { - "balance": "99981000000000000000" - }, - "17762560e82a93b3f522e0e524adb8612c3a7470": { - "balance": "1000000000000000000000" - }, - "d500e4d1c9824ba9f5b635cfa3a8c2c38bbd4ced": { - "balance": "400000000000000000000" - }, - "a11effab6cf0f5972cffe4d56596e98968144a8f": { - "balance": "1670000000000000000000" - }, - "f64ecf2117931c6d535a311e4ffeaef9d49405b8": { - "balance": "2674000000000000000000" - }, - "229cc4711b62755ea296445ac3b77fc633821cf2": { - "balance": "39481000000000000000" - }, - "fc989cb487bf1a7d17e4c1b7c4b7aafdda6b0a8d": { - "balance": "20000000000000000000" - }, - "ea8527febfa1ade29e26419329d393b940bbb7dc": { - "balance": "1999944000000000000000" - }, - "bce13e22322acfb355cd21fd0df60cf93add26c6": { - "balance": "200000000000000000000" - }, - "19ff244fcfe3d4fa2f4fd99f87e55bb315b81eb6": { - "balance": "200000000000000000000" - }, - "d2581a55ce23ab10d8ad8c44378f59079bd6f658": { - "balance": "8800000000000000000000" - }, - "4073fa49b87117cb908cf1ab512da754a932d477": { - "balance": "1970000000000000000000" - }, - "b6a82933c9eadabd981e5d6d60a6818ff806e36b": { - "balance": "400000000000000000000" - }, - "c79806032bc7d828f19ac6a640c68e3d820fa442": { - "balance": "20000000000000000000" - }, - "577b2d073c590c50306f5b1195a4b2ba9ecda625": { - "balance": "373600000000000000000" - }, - "7f13d760498d7193ca6859bc95c901386423d76c": { - "balance": "5000000000000000000000" - }, - "416784af609630b070d49a8bcd12235c6428a408": { - "balance": "20000000000000000000000" - }, - "fbe71622bcbd31c1a36976e7e5f670c07ffe16de": { - "balance": "400000000000000000000" - }, - "a5698035391e67a49013c0002079593114feb353": { - "balance": "240000000000000000000" - }, - "ab2871e507c7be3965498e8fb462025a1a1c4264": { - "balance": "775000000000000000000" - }, - "9c78fbb4df769ce2c156920cfedfda033a0e254a": { - "balance": "1970000000000000000000" - }, - "95e6f93dac228bc7585a25735ac2d076cc3a4017": { - "balance": "6000000000000000000000" - }, - "3c1f91f301f4b565bca24751aa1f761322709ddd": { - "balance": "1790000000000000000000" - }, - "f77f9587ff7a2d7295f1f571c886bd33926a527c": { - "balance": "1999800000000000000000" - }, - "755f587e5efff773a220726a13d0f2130d9f896b": { - "balance": "1000000000000000000000" - }, - "8c6aa882ee322ca848578c06cb0fa911d3608305": { - "balance": "600000000000000000000" - }, - "492cb5f861b187f9df21cd4485bed90b50ffe22d": { - "balance": "499928000000000000000" - }, - "95a577dc2eb3ae6cb9dfc77af697d7efdfe89a01": { - "balance": "136000000000000000000" - }, - "4173419d5c9f6329551dc4d3d0ceac1b701b869e": { - "balance": "88000000000000000000" - }, - "456ae0aca48ebcfae166060250525f63965e760f": { - "balance": "300000000000000000000" - }, - "81f8de2c283d5fd4afbda85dedf9760eabbbb572": { - "balance": "3000000000000000000000" - }, - "cd0af3474e22f069ec3407870dd770443d5b12b0": { - "balance": "2626262000000000000000" - }, - "283c2314283c92d4b064f0aef9bb5246a7007f39": { - "balance": "200000000000000000000" - }, - "29b3f561ee7a6e25941e98a5325b78adc79785f3": { - "balance": "100000000000000000000" - }, - "cd4306d7f6947ac1744d4e13b8ef32cb657e1c00": { - "balance": "499986000000000000000" - }, - "d9ec2efe99ff5cf00d03a8317b92a24aef441f7e": { - "balance": "2000000000000000000000" - }, - "83dbf8a12853b40ac61996f8bf1dc8fdbaddd329": { - "balance": "970000000000000000000" - }, - "9d93fab6e22845f8f45a07496f11de71530debc7": { - "balance": "1998000000000000000000" - }, - "fd204f4f4aba2525ba728afdf78792cbdeb735ae": { - "balance": "2000000000000000000000" - }, - "99fad50038d0d9d4c3fbb4bce05606ecadcd5121": { - "balance": "2000000000000000000000" - }, - "d206aaddb336d45e7972e93cb075471d15897b5d": { - "balance": "600000000000000000000" - }, - "428a1ee0ed331d7952ccbe1c7974b2852bd1938a": { - "balance": "2208370000000000000000" - }, - "690228e4bb12a8d4b5e0a797b0c5cf2a7509131e": { - "balance": "1880000000000000000000" - }, - "fa3a1aa4488b351aa7560cf5ee630a2fd45c3222": { - "balance": "878850000000000000000" - }, - "0372e852582e0934344a0fed2178304df25d4628": { - "balance": "20000000000000000000000" - }, - "35ea2163a38cdf9a123f82a5ec00258dae0bc767": { - "balance": "4000000000000000000000" - }, - "d1fed0aee6f5dfd7e25769254c3cfad15adeccaa": { - "balance": "730000000000000000000" - }, - "c05b740620f173f16e52471dc38b9c514a0b1526": { - "balance": "140000000000000000000" - }, - "87e3062b2321e9dfb0875ce3849c9b2e3522d50a": { - "balance": "10000000000000000000000" - }, - "303fbaebbe46b35b6e5b74946a5f99bc1585cae7": { - "balance": "878148000000000000000" - }, - "e7a8e471eafb798f4554cc6e526730fd56e62c7d": { - "balance": "1000000000000000000000" - }, - "ad7dd053859edff1cb6f9d2acbed6dd5e332426f": { - "balance": "1970000000000000000000" - }, - "dc4345d6812e870ae90c568c67d2c567cfb4f03c": { - "balance": "6700000000000000000000" - }, - "a6a08252c8595177cc2e60fc27593e2379c81fb1": { - "balance": "20055000000000000000" - }, - "a9af21acbe482f8131896a228036ba51b19453c3": { - "balance": "49999000000000000000" - }, - "86e3fe86e93da486b14266eadf056cbfa4d91443": { - "balance": "2000000000000000000000" - }, - "744b03bba8582ae5498e2dc22d19949467ab53fc": { - "balance": "500000000000000000000" - }, - "d3118ea3c83505a9d893bb67e2de142d537a3ee7": { - "balance": "20000000000000000000" - }, - "b32f1c2689a5ce79f1bc970b31584f1bcf2283e7": { - "balance": "20000000000000000000" - }, - "4828e4cbe34e1510afb72c2beeac8a4513eaebd9": { - "balance": "3940000000000000000000" - }, - "b07bcc085ab3f729f24400416837b69936ba8873": { - "balance": "2000140000000000000000" - }, - "bdc74873af922b9df474853b0fa7ff0bf8c82695": { - "balance": "3999000000000000000000" - }, - "15ebd1c7cad2aff19275c657c4d808d010efa0f5": { - "balance": "200550000000000000000" - }, - "cbc04b4d8b82caf670996f160c362940d66fcf1a": { - "balance": "6000000000000000000000" - }, - "8197948121732e63d9c148194ecad46e30b749c8": { - "balance": "4000000000000000000000" - }, - "69797bfb12c9bed682b91fbc593591d5e4023728": { - "balance": "10000000000000000000000" - }, - "be9b8c34b78ee947ff81472eda7af9d204bc8466": { - "balance": "150000000000000000000" - }, - "df3f57b8ee6434d047223def74b20f63f9e4f955": { - "balance": "250500000000000000000" - }, - "a3ae1879007d801cb5f352716a4dd8ba2721de3d": { - "balance": "200000000000000000000000" - }, - "cb4bb1c623ba28dc42bdaaa6e74e1d2aa1256c2a": { - "balance": "1999944000000000000000" - }, - "e03c00d00388ecbf4f263d0ac778bb41a57a40d9": { - "balance": "1000072000000000000000" - }, - "fc2c1f88961d019c3e9ea33009152e0693fbf88a": { - "balance": "8000000000000000000000" - }, - "8599cbd5a6a9dcd4b966be387d69775da5e33c6f": { - "balance": "58180000000000000000000" - }, - "b7a31a7c38f3db09322eae11d2272141ea229902": { - "balance": "2000000000000000000000" - }, - "231a15acc199c89fa9cb22441cc70330bdcce617": { - "balance": "500000000000000000000" - }, - "3fbed6e7e0ca9c84fbe9ebcf9d4ef9bb49428165": { - "balance": "2000000000000000000000" - }, - "92cfd60188efdfb2f8c2e7b1698abb9526c1511f": { - "balance": "2000000000000000000000" - }, - "5c936f3b9d22c403db5e730ff177d74eef42dbbf": { - "balance": "75000000000000000000" - }, - "931fe712f64207a2fd5022728843548bfb8cbb05": { - "balance": "2000000000000000000000" - }, - "08d54e83ad486a934cfaeae283a33efd227c0e99": { - "balance": "1039000000000000000000" - }, - "a339a3d8ca280e27d2415b26d1fc793228b66043": { - "balance": "1013600000000000000000" - }, - "581f34b523e5b41c09c87c298e299cbc0e29d066": { - "balance": "1131607000000000000000" - }, - "caaa68ee6cdf0d34454a769b0da148a1faaa1865": { - "balance": "7216000000000000000000" - }, - "0838a7768d9c2aca8ba279adfee4b1f491e326f1": { - "balance": "200000000000000000000" - }, - "dde77a4740ba08e7f73fbe3a1674912931742eeb": { - "balance": "19867021000000000000000" - }, - "cbe810fe0fecc964474a1db97728bc87e973fcbd": { - "balance": "10000000000000000000000" - }, - "86c28b5678af37d727ec05e4447790f15f71f2ea": { - "balance": "200000000000000000000" - }, - "dd6c062193eac23d2fdbf997d5063a346bb3b470": { - "balance": "20000000000000000000" - }, - "5975b9528f23af1f0e2ec08ac8ebaa786a2cb8e0": { - "balance": "345827000000000000000" - }, - "e29d8ae452dcf3b6ac645e630409385551faae0a": { - "balance": "80276000000000000000" - }, - "2fbc85798a583598b522166d6e9dda121d627dbc": { - "balance": "200000000000000000000" - }, - "7a36aba5c31ea0ca7e277baa32ec46ce93cf7506": { - "balance": "20000000000000000000000" - }, - "dbcbcd7a57ea9db2349b878af34b1ad642a7f1d1": { - "balance": "200000000000000000000" - }, - "92aae59768eddff83cfe60bb512e730a05a161d7": { - "balance": "1708015000000000000000" - }, - "a5e93b49ea7c509de7c44d6cfeddef5910deaaf2": { - "balance": "2000000000000000000000" - }, - "e33d980220fab259af6a1f4b38cf0ef3c6e2ea1a": { - "balance": "2000000000000000000000" - }, - "8ed0af11ff2870da0681004afe18b013f7bd3882": { - "balance": "4000000000000000000000" - }, - "f23e5c633221a8f7363e65870c9f287424d2a960": { - "balance": "1380000000000000000000" - }, - "96334bfe04fffa590213eab36514f338b864b736": { - "balance": "400000000000000000000" - }, - "fa1f1971a775c3504fef5079f640c2c4bce7ac05": { - "balance": "2000000000000000000000" - }, - "df44c47fc303ac76e74f97194cca67b5bb3c023f": { - "balance": "591000000000000000000" - }, - "4b74f5e58e2edf76daf70151964a0b8f1de0663c": { - "balance": "324020000000000000000" - }, - "e38b91b35190b6d9deed021c30af094b953fdcaa": { - "balance": "33340000000000000000" - }, - "6b38de841fad7f53fe02da115bd86aaf662466bd": { - "balance": "1730000000000000000000" - }, - "11675a25554607a3b6c92a9ee8f36f75edd3e336": { - "balance": "159800000000000000000" - }, - "0ba8705bf55cf219c0956b5e3fc01c4474a6cdc1": { - "balance": "94963000000000000000" - }, - "0f05f120c89e9fbc93d4ab0c5e2b4a0df092b424": { - "balance": "30000000000000000000000" - }, - "fdd1195f797d4f35717d15e6f9810a9a3ff55460": { - "balance": "18200000000000000000" - }, - "63a61dc30a8e3b30a763c4213c801cbf98738178": { - "balance": "1000000000000000000000" - }, - "e5bdf34f4ccc483e4ca530cc7cf2bb18febe92b3": { - "balance": "126260000000000000000" - }, - "d6e09e98fe1300332104c1ca34fbfac554364ed9": { - "balance": "2000000000000000000000" - }, - "5bd6862d517d4de4559d4eec0a06cad05e2f946e": { - "balance": "200000000000000000000" - }, - "7294ec9da310bc6b4bbdf543b0ef45abfc3e1b4d": { - "balance": "22000000000000000000000" - }, - "ae34861d342253194ffc6652dfde51ab44cad3fe": { - "balance": "466215000000000000000" - }, - "f50ae7fab4cfb5a646ee04ceadf9bf9dd5a8e540": { - "balance": "3999952000000000000000" - }, - "dd2bdfa917c1f310e6fa35aa8af16939c233cd7d": { - "balance": "400000000000000000000" - }, - "e0060462c47ff9679baef07159cae08c29f274a9": { - "balance": "2000000000000000000000" - }, - "b7d12e84a2e4c4a6345af1dd1da9f2504a2a996e": { - "balance": "200000000000000000000" - }, - "f5500178cb998f126417831a08c2d7abfff6ab5f": { - "balance": "1308923000000000000000" - }, - "fd377a385272900cb436a3bb7962cdffe93f5dad": { - "balance": "2000000000000000000000" - }, - "a4a83a0738799b971bf2de708c2ebf911ca79eb2": { - "balance": "600000000000000000000" - }, - "52a5e4de4393eeccf0581ac11b52c683c76ea15d": { - "balance": "19999800000000000000000" - }, - "b07fdeaff91d4460fe6cd0e8a1b0bd8d22a62e87": { - "balance": "5260000000000000000000" - }, - "35f5860149e4bbc04b8ac5b272be55ad1aca58e0": { - "balance": "200000000000000000000" - }, - "fb135eb15a8bac72b69915342a60bbc06b7e077c": { - "balance": "20000000000000000000000" - }, - "02d4a30968a39e2b3498c3a6a4ed45c1c6646822": { - "balance": "2000000000000000000000" - }, - "e44b7264dd836bee8e87970340ed2b9aed8ed0a5": { - "balance": "5772100000000000000000" - }, - "e90a354cec04d69e5d96ddc0c5138d3d33150aa0": { - "balance": "499971000000000000000" - }, - "693d83be09459ef8390b2e30d7f7c28de4b4284e": { - "balance": "2000000000000000000000" - }, - "87bf7cd5d8a929e1c785f9e5449106ac232463c9": { - "balance": "77800000000000000000" - }, - "e5f8ef6d970636b0dcaa4f200ffdc9e75af1741c": { - "balance": "2000000000000000000000" - }, - "fef09d70243f39ed8cd800bf9651479e8f4aca3c": { - "balance": "200000000000000000000" - }, - "e98c91cadd924c92579e11b41217b282956cdaa1": { - "balance": "135800000000000000000" - }, - "c2836188d9a29253e0cbda6571b058c289a0bb32": { - "balance": "2000000000000000000000" - }, - "afa6946effd5ff53154f82010253df47ae280ccc": { - "balance": "1970000000000000000000" - }, - "43c7ebc5b3e7af16f47dc5617ab10e0f39b4afbb": { - "balance": "1910000000000000000000" - }, - "097ecda22567c2d91cb03f8c5215c22e9dcda949": { - "balance": "20055000000000000000" - }, - "3e66b84769566ab67945d5fa81373556bcc3a1fa": { - "balance": "152000000000000000000" - }, - "56373daab46316fd7e1576c61e6affcb6559ddd7": { - "balance": "215340000000000000000" - }, - "faaeba8fc0bbda553ca72e30ef3d732e26e82041": { - "balance": "1338337000000000000000" - }, - "f54c19d9ef3873bfd1f7a622d02d86249a328f06": { - "balance": "44284729000000000000000" - }, - "825309a7d45d1812f51e6e8df5a7b96f6c908887": { - "balance": "2365000000000000000000" - }, - "89009e3c6488bd5e570d1da34eabe28ed024de1b": { - "balance": "20000000000000000000000" - }, - "63977cad7d0dcdc52b9ac9f2ffa136e8642882b8": { - "balance": "75000000000000000000" - }, - "c239abdfae3e9af5457f52ed2b91fd0ab4d9c700": { - "balance": "2000000000000000000000" - }, - "1a4ec6a0ae7f5a9427d23db9724c0d0cffb2ab2f": { - "balance": "179000000000000000000" - }, - "a12a6c2d985daf0e4f5f207ae851aaf729b332cd": { - "balance": "100000000000000000000000" - }, - "cbe52fc533d7dd608c92a260b37c3f45deb4eb33": { - "balance": "1000000000000000000000" - }, - "abb2e6a72a40ba6ed908cdbcec3c5612583132fe": { - "balance": "1460000000000000000000" - }, - "6503860b191008c15583bfc88158099301762828": { - "balance": "1000000000000000000000" - }, - "a0228240f99e1de9cb32d82c0f2fa9a3d44b0bf3": { - "balance": "1600000000000000000000" - }, - "e154daeadb545838cbc6aa0c55751902f528682a": { - "balance": "4925000000000000000000" - }, - "8e92aba38e72a098170b92959246537a2e5556c0": { - "balance": "267400000000000000000" - }, - "d23d7affacdc3e9f3dae7afcb4006f58f8a44600": { - "balance": "3600000000000000000000" - }, - "00d78d89b35f472716eceafebf600527d3a1f969": { - "balance": "27750000000000000000000" - }, - "120f9de6e0af7ec02a07c609ca8447f157e6344c": { - "balance": "267400000000000000000" - }, - "e0352fdf819ba265f14c06a6315c4ac1fe131b2e": { - "balance": "1000000000000000000000" - }, - "8f47328ee03201c9d35ed2b5412b25decc859362": { - "balance": "2000000000000000000000" - }, - "453e359a3397944c5a275ab1a2f70a5e5a3f6989": { - "balance": "240000000000000000000" - }, - "9bf58efbea0784eb068adecfa0bb215084c73a35": { - "balance": "5800000000000000000000" - }, - "21bfe1b45cacde6274fd8608d9a178bf3eeb6edc": { - "balance": "2009400000000000000000" - }, - "d1d5b17ffe2d7bbb79cc7d7930bcb2e518fb1bbf": { - "balance": "3000000000000000000000" - }, - "20a29c5079e26b3f18318bb2e50e8e8b346e5be8": { - "balance": "499986000000000000000" - }, - "7d392852f3abd92ff4bb5bb26cb60874f2be6795": { - "balance": "1000070000000000000000" - }, - "55852943492970f8d629a15366cdda06a94f4513": { - "balance": "2000000000000000000000" - }, - "ab5dfc1ea21adc42cf8c3f6e361e243fd0da61e5": { - "balance": "300000000000000000000" - }, - "9d2bfc36106f038250c01801685785b16c86c60d": { - "balance": "380000000000000000000000" - }, - "6e60aee1a78f8eda8b424c73e353354ae67c3042": { - "balance": "3490300000000000000000" - }, - "7e29290038493559194e946d4e460b96fc38a156": { - "balance": "309072000000000000000" - }, - "6006e36d929bf45d8f16231b126a011ae283d925": { - "balance": "176000000000000000000" - }, - "d6d03572a45245dbd4368c4f82c95714bd2167e2": { - "balance": "1162200000000000000000" - }, - "d1432538e35b7664956ae495a32abdf041a7a21c": { - "balance": "19700000000000000000000" - }, - "2276264bec8526c0c0f270677abaf4f0e441e167": { - "balance": "1000000000000000000000" - }, - "c8814e34523e38e1f927a7dce8466a447a093603": { - "balance": "10000000000000000000000" - }, - "688a569e965524eb1d0ac3d3733eab909fb3d61e": { - "balance": "1320000000000000000000" - }, - "90dc09f717fc2a5b69fd60ba08ebf40bf4e8246c": { - "balance": "4000086000000000000000" - }, - "239a733e6b855ac592d663156186a8a174d2449e": { - "balance": "1637020000000000000000" - }, - "bcdfacb9d9023c3417182e9100e8ea1d373393a3": { - "balance": "59100000000000000000" - }, - "ba6440aeb3737b8ef0f1af9b0c15f4c214ffc7cf": { - "balance": "1000000000000000000000" - }, - "322e5c43b0f524389655a9b3ff24f2d4db3da10f": { - "balance": "4650000000000000000000" - }, - "be5a60689998639ad75bc105a371743eef0f7940": { - "balance": "501700000000000000000" - }, - "b727a9fc82e1cffc5c175fa1485a9befa2cdbdd1": { - "balance": "999000000000000000000" - }, - "a3883a24f7f166205f1a6a9949076c26a76e7178": { - "balance": "1820000000000000000000" - }, - "5e95fe5ffcf998f9f9ac0e9a81dab83ead77003d": { - "balance": "539766000000000000000" - }, - "e60955dc0bc156f6c41849f6bd776ba44b0ef0a1": { - "balance": "299982000000000000000" - }, - "af203e229d7e6d419df4378ea98715515f631485": { - "balance": "1970000000000000000000" - }, - "86499a1228ff2d7ee307759364506f8e8c8307a5": { - "balance": "1970000000000000000000" - }, - "1a04cec420ad432215246d77fe178d339ed0b595": { - "balance": "316000000000000000000" - }, - "cc2b5f448f3528d3fe41cc7d1fa9c0dc76f1b776": { - "balance": "60000000000000000000" - }, - "cb50587412822304ebcba07dab3a0f09fffee486": { - "balance": "1370000000000000000000" - }, - "4ae2a04d3909ef454e544ccfd614bfefa71089ae": { - "balance": "442800000000000000000" - }, - "c8a2c4e59e1c7fc54805580438aed3e44afdf00e": { - "balance": "44000000000000000000" - }, - "5792814f59a33a1843faa01baa089eb02ffb5cf1": { - "balance": "499986000000000000000" - }, - "a1f2854050f872658ed82e52b0ad7bbc1cb921f6": { - "balance": "2010918000000000000000" - }, - "92dca5e102b3b81b60f1a504634947c374a88ccb": { - "balance": "2000000000000000000000" - }, - "732fead60f7bfdd6a9dec48125e3735db1b6654f": { - "balance": "20000000000000000000" - }, - "6bf7b3c065f2c1e7c6eb092ba0d15066f393d1b8": { - "balance": "400000000000000000000" - }, - "cde36d81d128c59da145652193eec2bfd96586ef": { - "balance": "4000000000000000000000" - }, - "40eddb448d690ed72e05c225d34fc8350fa1e4c5": { - "balance": "7000000000000000000000" - }, - "454b61b344c0ef965179238155f277c3829d0b38": { - "balance": "2000000000000000000000" - }, - "ac3da526cfce88297302f34c49ca520dc271f9b2": { - "balance": "800000000000000000000" - }, - "c989eec307e8839b9d7237cfda08822962abe487": { - "balance": "400000000000000000000" - }, - "e99de258a4173ce9ac38ede26c0b3bea3c0973d5": { - "balance": "1656800000000000000000" - }, - "ff0cb06c42e3d88948e45bd7b0d4e291aefeea51": { - "balance": "1910000000000000000000" - }, - "0990e81cd785599ea236bd1966cf526302c35b9c": { - "balance": "1000000000000000000000" - }, - "6da0ed8f1d69339f059f2a0e02471cb44fb8c3bb": { - "balance": "935900000000000000000" - }, - "5d958a9bd189c2985f86c58a8c69a7a78806e8da": { - "balance": "10200000000000000000000" - }, - "98be696d51e390ff1c501b8a0f6331b628ddc5ad": { - "balance": "2000000000000000000000" - }, - "09d0b8cd077c69d9f32d9cca43b3c208a21ed48b": { - "balance": "150011000000000000000" - }, - "96e7c0c9d5bf10821bf140c558a145b7cac21397": { - "balance": "1056000000000000000000" - }, - "5b736eb18353629bde9676dadd165034ce5ecc68": { - "balance": "1970000000000000000000" - }, - "e5a365343cc4eb1e770368e1f1144a77b832d7e0": { - "balance": "20000000000000000000" - }, - "4cf5537b85842f89cfee359eae500fc449d2118f": { - "balance": "1000000000000000000000" - }, - "c71f1d75873f33dcb2dd4b3987a12d0791a5ce27": { - "balance": "1015200000000000000000" - }, - "9bf703b41c3624e15f4054962390bcba3052f0fd": { - "balance": "6055000000000000000000" - }, - "145e1de0147911ccd880875fbbea61f6a142d11d": { - "balance": "4000000000000000000000" - }, - "68419c6dd2d3ce6fcbb3c73e2fa079f06051bde6": { - "balance": "1970000000000000000000" - }, - "d8eb78503ec31a54a90136781ae109004c743257": { - "balance": "1000000000000000000000" - }, - "f25e4c70bc465632c89e5625a832a7722f6bffab": { - "balance": "4488000000000000000000" - }, - "7b4d2a38269069c18557770d591d24c5121f5e83": { - "balance": "700000000000000000000" - }, - "27d158ac3d3e1109ab6e570e90e85d3892cd7680": { - "balance": "100000000000000000000" - }, - "d3679a47df2d99a49b01c98d1c3e0c987ce1e158": { - "balance": "280000000000000000000" - }, - "095b949de3333a377d5019d893754a5e4656ff97": { - "balance": "340000000000000000000" - }, - "6b17598a8ef54f797ae515ccb6517d1859bf8011": { - "balance": "100000000000000000000" - }, - "3eaf0879b5b6db159b589f84578b6a74f6c10357": { - "balance": "7253657000000000000000" - }, - "40d45d9d7625d15156c932b771ca7b0527130958": { - "balance": "100000000000000000000000" - }, - "0392549a727f81655429cb928b529f25df4d1385": { - "balance": "26248000000000000000" - }, - "c5b009baeaf788a276bd35813ad65b400b849f3b": { - "balance": "1000000000000000000000" - }, - "6ed884459f809dfa1016e770edaf3e9fef46fa30": { - "balance": "3400170000000000000000" - }, - "439d2f2f5110a4d58b1757935015408740fec7f8": { - "balance": "3830421000000000000000" - }, - "dc46c13325cd8edf0230d068896486f007bf4ef1": { - "balance": "1337000000000000000000" - }, - "8c54c7f8b9896e75d7d5f5c760258699957142ad": { - "balance": "40000000000000000000" - }, - "61c8f1fa43bf846999ecf47b2b324dfb6b63fe3a": { - "balance": "800000000000000000000" - }, - "935069444a6a984de2084e46692ab99f671fc727": { - "balance": "9000000000000000000000" - }, - "fc49c1439a41d6b3cf26bb67e0365224e5e38f5f": { - "balance": "1000076000000000000000" - }, - "e1dfb5cc890ee8b2877e885d267c256187d019e6": { - "balance": "100000000000000000000" - }, - "ee7c3ded7c28f459c92fe13b4d95bafbab02367d": { - "balance": "700000000000000000000" - }, - "a5874d754635a762b381a5c4c792483af8f23d1d": { - "balance": "50000000000000000000" - }, - "cfbb32b7d024350e3321fa20c9a914035372ffc6": { - "balance": "401100000000000000000" - }, - "2bc429d618a66a4cf82dbb2d824e9356effa126a": { - "balance": "1999944000000000000000" - }, - "db244f97d9c44b158a40ed9606d9f7bd38913331": { - "balance": "102000000000000000000" - }, - "55e220876262c218af4f56784798c7e55da09e91": { - "balance": "133566000000000000000" - }, - "ca41ccac30172052d522cd2f2f957d248153409f": { - "balance": "1970000000000000000000" - }, - "b11fa7fb270abcdf5a2eab95aa30c4b53636efbf": { - "balance": "800000000000000000000" - }, - "0ffea06d7113fb6aec2869f4a9dfb09007facef4": { - "balance": "225416000000000000000" - }, - "646628a53c2c4193da88359ce718dadd92b7a48d": { - "balance": "200032000000000000000" - }, - "ca8409083e01b397cf12928a05b68455ce6201df": { - "balance": "1600000000000000000000" - }, - "dbbcbb79bf479a42ad71dbcab77b5adfaa872c58": { - "balance": "1730000000000000000000" - }, - "db7d4037081f6c65f9476b0687d97f1e044d0a1d": { - "balance": "660000000000000000000" - }, - "4be90d412129d5a4d0424361d6649d4e47a62316": { - "balance": "1015200000000000000000" - }, - "e3ab3ca9b870e3f548517306bba4de2591afafc2": { - "balance": "1200062000000000000000" - }, - "5c61ab79b408dd3229f662593705d72f1e147bb8": { - "balance": "22729000000000000000000" - }, - "4f177f9d56953ded71a5611f393322c30279895c": { - "balance": "246000000000000000000" - }, - "e6cb260b716d4c0ab726eeeb07c8707204e276ae": { - "balance": "1000000000000000000000" - }, - "44355253b27748e3f34fe9cae1fb718c8f249529": { - "balance": "200000000000000000000" - }, - "a309df54cabce70c95ec3033149cd6678a6fd4cf": { - "balance": "223600000000000000000" - }, - "ec4867d2175ab5b9469361595546554684cda460": { - "balance": "3000000000000000000000" - }, - "8d06e464245cad614939e0af0845e6d730e20374": { - "balance": "200359000000000000000" - }, - "9810e34a94db6ed156d0389a0e2b80f4fd6b0a8a": { - "balance": "2000000000000000000000" - }, - "dcfff3e8d23c2a34b56bd1b3bd45c79374432239": { - "balance": "5000000000000000000000" - }, - "7d7dd5ee614dbb6fbfbcd26305247a058c41faa1": { - "balance": "2000000000000000000000" - }, - "8a9eca9c5aba8e139f8003edf1163afb70aa3aa9": { - "balance": "660000000000000000000" - }, - "d942de4784f7a48716c0fd4b9d54a6e54c5f2f3e": { - "balance": "20000000000000000000000" - }, - "07dae622630d1136381933d2ad6b22b839d82102": { - "balance": "200000000000000000000" - }, - "abf12fa19e82f76c718f01bdca0003674523ef30": { - "balance": "2000000000000000000000" - }, - "411c831cc6f44f1965ec5757ab4e5b3ca4cffd1f": { - "balance": "425000000000000000000" - }, - "99129d5b3c0cde47ea0def4dfc070d1f4a599527": { - "balance": "2000000000000000000000" - }, - "c5cdcee0e85d117dabbf536a3f4069bf443f54e7": { - "balance": "1969606000000000000000" - }, - "f218bd848ee7f9d38bfdd1c4eb2ed2496ae4305f": { - "balance": "500000000000000000000" - }, - "fe549bbfe64740189892932538daaf46d2b61d4f": { - "balance": "40000000000000000000" - }, - "dc3f0e7672f71fe7525ba30b9755183a20b9166a": { - "balance": "9603617000000000000000" - }, - "0e83b850481ab44d49e0a229a2e464902c69539b": { - "balance": "100000000000000000000" - }, - "07ddd0422c86ef65bf0c7fc3452862b1228b08b8": { - "balance": "2065302000000000000000" - }, - "a68c313445c22d919ee46cc2d0cdff043a755825": { - "balance": "75189000000000000000" - }, - "a9e9dbce7a2cb03694799897bed7c54d155fdaa8": { - "balance": "197559000000000000000" - }, - "18fccf62d2c3395453b7587b9e26f5cff9eb7482": { - "balance": "1000000000000000000000" - }, - "ff41d9e1b4effe18d8b0d1f63fc4255fb4e06c3d": { - "balance": "1337000000000000000000" - }, - "8f69eafd0233cadb4059ab779c46edf2a0506e48": { - "balance": "1788210000000000000000" - }, - "9aa48c66e4fb4ad099934e32022e827427f277ba": { - "balance": "10000000000000000000000" - }, - "f46980e3a4a9d29a6a6e90604537a3114bcb2897": { - "balance": "500000000000000000000" - }, - "801732a481c380e57ed62d6c29de998af3fa3b13": { - "balance": "100000000000000000000" - }, - "0cd6a141918d126b106d9f2ebf69e102de4d3277": { - "balance": "20000000000000000000" - }, - "17589a6c006a54cad70103123aae0a82135fdeb4": { - "balance": "4000000000000000000000" - }, - "8725e8c753b3acbfdca55f3c62dfe1a59454968a": { - "balance": "1000090000000000000000" - }, - "d20dcb0b78682b94bc3000281448d557a20bfc83": { - "balance": "895000000000000000000" - }, - "e84f8076a0f2969ecd333eef8de41042986291f2": { - "balance": "432000000000000000000" - }, - "b3145b74506d1a8d047cdcdc55392a7b5350799a": { - "balance": "129314663000000000000000" - }, - "0d9a825ff2bcd397cbad5b711d9dcc95f1cc112d": { - "balance": "12800000000000000000000" - }, - "0ca670eb2c8b96cba379217f5929c2b892f39ef6": { - "balance": "2000000000000000000000" - }, - "25cfc4e25c35c13b69f7e77dbfb08baf58756b8d": { - "balance": "40000000000000000000000" - }, - "182db85293f606e88988c3704cb3f0c0bbbfca5a": { - "balance": "133700000000000000000" - }, - "bd73c3cbc26a175062ea0320dd84b253bce64358": { - "balance": "394000000000000000000" - }, - "2680713d40808e2a50ed013150a2a694b96a7f1d": { - "balance": "1790000000000000000000" - }, - "51e32f14f4ca5e287cdac057a7795ea9e0439953": { - "balance": "500000000000000000000" - }, - "b1e9c5f1d21e61757a6b2ee75913fc5a1a4101c3": { - "balance": "2000000000000000000000" - }, - "d4c4d1a7c3c74984f6857b2f5f07e8face68056d": { - "balance": "2000000000000000000000" - }, - "4651dc420e08c3293b27d2497890eb50223ae2f4": { - "balance": "20000000000000000000000" - }, - "c74a3995f807de1db01a2eb9c62e97d0548f696f": { - "balance": "1000000000000000000000" - }, - "0505a08e22a109015a22f685305354662a5531d5": { - "balance": "2600000000000000000000" - }, - "39c773367c8825d3596c686f42bf0d14319e3f84": { - "balance": "133700000000000000000" - }, - "0f929cf895db017af79f3ead2216b1bd69c37dc7": { - "balance": "2000000000000000000000" - }, - "bdd3254e1b3a6dc6cc2c697d45711aca21d516b2": { - "balance": "2000000000000000000000" - }, - "ae5d221afcd3d29355f508eadfca408ce33ca903": { - "balance": "100000000000000000000000" - }, - "916cf17d71412805f4afc3444a0b8dd1d9339d16": { - "balance": "14300000000000000000" - }, - "4319263f75402c0b5325f263be4a5080651087f0": { - "balance": "983086000000000000000" - }, - "0f1c249cd962b00fd114a9349f6a6cc778d76c4d": { - "balance": "2000000000000000000000" - }, - "54febcce20fe7a9098a755bd90988602a48c089e": { - "balance": "640000000000000000000" - }, - "2c1800f35fa02d3eb6ff5b25285f5e4add13b38d": { - "balance": "906400000000000000000" - }, - "72b904440e90e720d6ac1c2ad79c321dcc1c1a86": { - "balance": "1550000000000000000000" - }, - "b0aa00950c0e81fa3210173e729aaf163a27cd71": { - "balance": "40000000000000000000000" - }, - "663604b0503046e624cd26a8b6fb4742dce02a6f": { - "balance": "65400000000000000000" - }, - "3c98594bf68b57351e8814ae9e6dfd2d254aa06f": { - "balance": "300000000000000000000" - }, - "9c45202a25f6ad0011f115a5a72204f2f2198866": { - "balance": "5014000000000000000000" - }, - "b02d062873334545cea29218e4057760590f7423": { - "balance": "3186000000000000000000" - }, - "7bddb2ee98de19ee4c91f661ee8e67a91d054b97": { - "balance": "1000000000000000000000" - }, - "9cf2928beef09a40f9bfc953be06a251116182fb": { - "balance": "6000000000000000000000" - }, - "51b4758e9e1450e7af4268c3c7b1e7bd6f5c7550": { - "balance": "1000000000000000000000" - }, - "eb570dba975227b1c42d6e8dea2c56c9ad960670": { - "balance": "2000000000000000000000" - }, - "970d8b8a0016d143054f149fb3b8e550dc0797c7": { - "balance": "1000000000000000000000" - }, - "c7b39b060451000ca1049ba154bcfa00ff8af262": { - "balance": "100000000000000000000000" - }, - "945e18769d7ee727c7013f92de24d117967ff317": { - "balance": "2000000000000000000000" - }, - "d18eb9e1d285dabe93e5d4bae76beefe43b521e8": { - "balance": "668500000000000000000" - }, - "c618521321abaf5b26513a4a9528086f220adc6f": { - "balance": "27000000000000000000" - }, - "dd65f6e17163b5d203641f51cc7b24b00f02c8fb": { - "balance": "200000000000000000000" - }, - "131faed12561bb7aee04e5185af802b1c3438d9b": { - "balance": "219000000000000000000" - }, - "1ced6715f862b1ff86058201fcce5082b36e62b2": { - "balance": "6684522000000000000000" - }, - "a0ff5b4cf016027e8323497d4428d3e5a83b8795": { - "balance": "6596500000000000000000" - }, - "02e816afc1b5c0f39852131959d946eb3b07b5ad": { - "balance": "1000000000000000000000" - }, - "153cf2842cb9de876c276fa64767d1a8ecf573bb": { - "balance": "2000000000000000000000" - }, - "3bc6e3ee7a56ce8f14a37532590f63716b9966e8": { - "balance": "2000000000000000000000" - }, - "f6d25d3f3d846d239f525fa8cac97bc43578dbac": { - "balance": "896000000000000000000" - }, - "2066774d822793ff25f1760909479cf62491bf88": { - "balance": "55160000000000000000000" - }, - "46779a5656ff00d73eac3ad0c38b6c853094fb40": { - "balance": "230752000000000000000" - }, - "22eed327f8eb1d1338a3cb7b0f8a4baa5907cd95": { - "balance": "23445000000000000000" - }, - "ff88ebacc41b3687f39e4b59e159599b80cba33f": { - "balance": "400000000000000000000" - }, - "2874f3e2985d5f7b406627e17baa772b01abcc9e": { - "balance": "6014000000000000000000" - }, - "eb10458daca79e4a6b24b29a8a8ada711b7f2eb6": { - "balance": "3998000000000000000000" - }, - "541060fc58c750c40512f83369c0a63340c122b6": { - "balance": "1970000000000000000000" - }, - "fd2757cc3551a095878d97875615fe0c6a32aa8a": { - "balance": "598200000000000000000" - }, - "be659d85e7c34f8833ea7f488de1fbb5d4149bef": { - "balance": "9072500000000000000000" - }, - "e149b5726caf6d5eb5bf2acc41d4e2dc328de182": { - "balance": "1940000000000000000000" - }, - "2fe0cc424b53a31f0916be08ec81c50bf8eab0c1": { - "balance": "600000000000000000000" - }, - "e3712701619ca7623c55db3a0ad30e867db0168b": { - "balance": "20000000000000000000" - }, - "f8ca336c8e91bd20e314c20b2dd4608b9c8b9459": { - "balance": "846000000000000000000" - }, - "68acdaa9fb17d3c309911a77b05f5391fa034ee9": { - "balance": "8950000000000000000000" - }, - "e77d7deab296c8b4fa07ca3be184163d5a6d606c": { - "balance": "92538000000000000000" - }, - "e6b9545f7ed086e552924639f9a9edbbd5540b3e": { - "balance": "3760000000000000000000" - }, - "2866b81decb02ee70ae250cee5cdc77b59d7b679": { - "balance": "2000000000000000000000" - }, - "60e3cc43bcdb026aad759c7066f555bbf2ac66f5": { - "balance": "2000000000000000000000" - }, - "fcbd85feea6a754fcf3449449e37ff9784f7773c": { - "balance": "3086000000000000000000" - }, - "38a744efa6d5c2137defef8ef9187b649eee1c78": { - "balance": "4000000000000000000000" - }, - "9d7655e9f3e5ba5d6e87e412aebe9ee0d49247ee": { - "balance": "2620100000000000000000" - }, - "2020b81ae53926ace9f7d7415a050c031d585f20": { - "balance": "341200000000000000000" - }, - "4244f1331158b9ce26bbe0b9236b9203ca351434": { - "balance": "10000000000000000000000" - }, - "99c236141daec837ece04fdaee1d90cf8bbdc104": { - "balance": "2184000000000000000000" - }, - "943d37864a4a537d35c8d99723cd6406ce2562e6": { - "balance": "2000000000000000000000" - }, - "d79483f6a8444f2549d611afe02c432d15e11051": { - "balance": "20000000000000000000" - }, - "9fd64373f2fbcd9c0faca60547cad62e26d9851f": { - "balance": "1000000000000000000000" - }, - "b89c036ed7c492879921be41e10ca1698198a74c": { - "balance": "1820000000000000000000" - }, - "7462c89caa9d8d7891b2545def216f7464d5bb21": { - "balance": "109162000000000000000" - }, - "bb0366a7cfbd3445a70db7fe5ae34885754fd468": { - "balance": "6160000000000000000000" - }, - "6c52cf0895bb35e656161e4dc46ae0e96dd3e62c": { - "balance": "4000086000000000000000" - }, - "b9cf71b226583e3a921103a5316f855a65779d1b": { - "balance": "24000000000000000000000" - }, - "016b60bb6d67928c29fd0313c666da8f1698d9c5": { - "balance": "2000000000000000000000" - }, - "9454b3a8bff9709fd0e190877e6cb6c89974dbd6": { - "balance": "2674000000000000000000" - }, - "84aac7fa197ff85c30e03b7a5382b957f41f3afb": { - "balance": "157600000000000000000" - }, - "db6e560c9bc620d4bea3a94d47f7880bf47f2d5f": { - "balance": "89500000000000000000" - }, - "eefd05b0e3c417d55b3343060486cdd5e92aa7a6": { - "balance": "1430000000000000000000" - }, - "3a59a08246a8206f8d58f70bb1f0d35c5bcc71bd": { - "balance": "185000000000000000000" - }, - "9bfff50db36a785555f07652a153b0c42b1b8b76": { - "balance": "2000000000000000000000" - }, - "d44f5edf2bcf2433f211dadd0cc450db1b008e14": { - "balance": "267400000000000000000" - }, - "2378fd4382511e968ed192106737d324f454b535": { - "balance": "1000000000000000000000" - }, - "c94089553ae4c22ca09fbc98f57075cf2ec59504": { - "balance": "4000000000000000000000" - }, - "08ef3fa4c43ccdc57b22a4b9b2331a82e53818f2": { - "balance": "4000000000000000000000" - }, - "e48e65125421880d42bdf1018ab9778d96928f3f": { - "balance": "4200000000000000000000" - }, - "67518e5d02b205180f0463a32004471f753c523e": { - "balance": "1984289000000000000000" - }, - "0da7401262384e2e8b4b26dd154799b55145efa0": { - "balance": "300000000000000000000" - }, - "0b6920a64b363b8d5d90802494cf564b547c430d": { - "balance": "1200000000000000000000" - }, - "a5ab4bd3588f46cb272e56e93deed386ba8b753d": { - "balance": "1332989000000000000000" - }, - "1788da9b57fd05edc4ff99e7fef301519c8a0a1e": { - "balance": "2000000000000000000000" - }, - "17b2d6cf65c6f4a347ddc6572655354d8a412b29": { - "balance": "2000000000000000000000" - }, - "d0319139fbab2e8e2accc1d924d4b11df6696c5a": { - "balance": "200000000000000000000" - }, - "4c377bb03ab52c4cb79befa1dd114982924c4ae9": { - "balance": "1827814000000000000000" - }, - "fb949c647fdcfd2514c7d58e31f28a532d8c5833": { - "balance": "20000000000000000000000" - }, - "70e5e9da735ff077249dcb9aaf3db2a48d9498c0": { - "balance": "1000000000000000000000" - }, - "fe6f5f42b6193b1ad16206e4afb5239d4d7db45e": { - "balance": "1730000000000000000000" - }, - "bda4be317e7e4bed84c0495eee32d607ec38ca52": { - "balance": "2309457000000000000000" - }, - "5910106debd291a1cd80b0fbbb8d8d9e93a7cc1e": { - "balance": "2000000000000000000000" - }, - "ba42f9aace4c184504abf5425762aca26f71fbdc": { - "balance": "37400000000000000000" - }, - "beb4fd315559436045dcb99d49dcec03f40c42dc": { - "balance": "2000000000000000000000" - }, - "452b64db8ef7d6df87c788639c2290be8482d575": { - "balance": "8000000000000000000000" - }, - "66e09427c1e63deed7e12b8c55a6a19320ef4b6a": { - "balance": "170000000000000000000" - }, - "faad905d847c7b23418aeecbe3addb8dd3f8924a": { - "balance": "1970000000000000000000" - }, - "a29319e81069e5d60df00f3de5adee3505ecd5fb": { - "balance": "2000000000000000000000" - }, - "cf348f2fe47b7e413c077a7baf3a75fbf8428692": { - "balance": "2000000000000000000000" - }, - "e1e8c50b80a352b240ce7342bbfdf5690cc8cb14": { - "balance": "394000000000000000000" - }, - "131c792c197d18bd045d7024937c1f84b60f4438": { - "balance": "4000000000000000000000" - }, - "e49af4f34adaa2330b0e49dc74ec18ab2f92f827": { - "balance": "2000000000000000000000" - }, - "f2e99f5cbb836b7ad36247571a302cbe4b481c69": { - "balance": "1970000000000000000000" - }, - "c93fbde8d46d2bcc0fa9b33bd8ba7f8042125565": { - "balance": "1400000000000000000000" - }, - "038779ca2dbe663e63db3fe75683ea0ec62e2383": { - "balance": "1670000000000000000000" - }, - "a33cb450f95bb46e25afb50fe05feee6fb8cc8ea": { - "balance": "776000000000000000000" - }, - "40ab66fe213ea56c3afb12c75be33f8e32fd085d": { - "balance": "4000000000000000000000" - }, - "6403d062549690c8e8b63eae41d6c109476e2588": { - "balance": "2000000000000000000000" - }, - "bfb0ea02feb61dec9e22a5070959330299c43072": { - "balance": "20000000000000000000000" - }, - "99c475bf02e8b9214ada5fad02fdfd15ba365c0c": { - "balance": "591000000000000000000" - }, - "904966cc2213b5b8cb5bd6089ef9cddbef7edfcc": { - "balance": "2000000000000000000000" - }, - "767a03655af360841e810d83f5e61fb40f4cd113": { - "balance": "985000000000000000000" - }, - "ab209fdca979d0a647010af9a8b52fc7d20d8cd1": { - "balance": "9129000000000000000000" - }, - "6294eae6e420a3d5600a39c4141f838ff8e7cc48": { - "balance": "2955000000000000000000" - }, - "9777cc61cf756be3b3c20cd4491c69d275e7a120": { - "balance": "10000000000000000000000" - }, - "bcbf6ba166e2340db052ea23d28029b0de6aa380": { - "balance": "3880000000000000000000" - }, - "9f10f2a0463b65ae30b070b3df18cf46f51e89bd": { - "balance": "1910000000000000000000" - }, - "8d9952d0bb4ebfa0efd01a3aa9e8e87f0525742e": { - "balance": "3460000000000000000000" - }, - "4f23b6b817ffa5c664acdad79bb7b726d30af0f9": { - "balance": "1760000000000000000000" - }, - "b4c20040ccd9a1a3283da4d4a2f365820843d7e2": { - "balance": "1000000000000000000000" - }, - "7f49e7a4269882bd8722d4a6f566347629624079": { - "balance": "2000000000000000000000" - }, - "33629bd52f0e107bc071176c64df108f64777d49": { - "balance": "33425000000000000000" - }, - "6a7b2e0d88867ff15d207c222bebf94fa6ce8397": { - "balance": "60000000000000000000000" - }, - "b7ce684b09abda53389a875369f71958aeac3bdd": { - "balance": "2000000000000000000000" - }, - "ffbc3da0381ec339c1c049eb1ed9ee34fdcea6ca": { - "balance": "4000000000000000000000" - }, - "849ab80790b28ff1ffd6ba394efc7463105c36f7": { - "balance": "34600000000000000000" - }, - "b0b36af9aeeedf97b6b02280f114f13984ea3260": { - "balance": "985000000000000000000" - }, - "4d57e716876c0c95ef5eaebd35c8f41b069b6bfe": { - "balance": "2000000000000000000000" - }, - "2d2b032359b363964fc11a518263bfd05431e867": { - "balance": "149600000000000000000" - }, - "2ccc1f1cb5f4a8002e186b20885d9dbc030c0894": { - "balance": "2000000000000000000000" - }, - "016c85e1613b900fa357b8283b120e65aefcdd08": { - "balance": "799954000000000000000" - }, - "710b0274d712c77e08a5707d6f3e70c0ce3d92cf": { - "balance": "6400000000000000000000" - }, - "3cd3a6e93579c56d494171fc533e7a90e6f59464": { - "balance": "2000000000000000000000" - }, - "fe0e30e214290d743dd30eb082f1f0a5225ade61": { - "balance": "200000000000000000000" - }, - "d0718520eae0a4d62d70de1be0ca431c5eea2482": { - "balance": "2000000000000000000000" - }, - "af7f79cb415a1fb8dbbd094607ee8d41fb7c5a3b": { - "balance": "10000000000000000000000" - }, - "b7d252ee9402b0eef144295f0e69f0db586c0871": { - "balance": "660000000000000000000" - }, - "c3b928a76fad6578f04f0555e63952cd21d1520a": { - "balance": "2000000000000000000000" - }, - "a7a517d7ad35820b09d497fa7e5540cde9495853": { - "balance": "2000000000000000000000" - }, - "e6e886317b6a66a5b4f81bf164c538c264351765": { - "balance": "2000000000000000000000" - }, - "0770b43dbae4b1f35a927b4fa8124d3866caf97b": { - "balance": "1016390000000000000000" - }, - "52b4257cf41b6e28878d50d57b99914ffa89873a": { - "balance": "3930150000000000000000" - }, - "e08bc29c2b48b169ff2bdc16714c586e6cb85ccf": { - "balance": "20000000000000000000" - }, - "2372c4c1c9939f7aaf6cfac04090f00474840a09": { - "balance": "10000000000000000000000" - }, - "ab6b65eab8dfc917ec0251b9db0ecfa0fa032849": { - "balance": "500000000000000000000" - }, - "582e7cc46f1d7b4e6e9d95868bfd370573178f4c": { - "balance": "2000000000000000000000" - }, - "f167f5868dcf4233a7830609682caf2df4b1b807": { - "balance": "2396150000000000000000" - }, - "ec82f50d06475f684df1b392e00da341aa145444": { - "balance": "2000000000000000000000" - }, - "0968ee5a378f8cadb3bafdbed1d19aaacf936711": { - "balance": "1000000000000000000000" - }, - "a86613e6c4a4c9c55f5c10bcda32175dcbb4af60": { - "balance": "10696140000000000000000" - }, - "a5cd123992194b34c4781314303b03c54948f4b9": { - "balance": "2010462000000000000000" - }, - "52f058d46147e9006d29bf2c09304ad1cddd6e15": { - "balance": "1500000000000000000000" - }, - "160226efe7b53a8af462d117a0108089bdecc2d1": { - "balance": "200550000000000000000" - }, - "256292a191bdda34c4da6b6bd69147bf75e2a9ab": { - "balance": "14051000000000000000" - }, - "1b8aa0160cd79f005f88510a714913d70ad3be33": { - "balance": "201760000000000000000" - }, - "d4b2ff3bae1993ffea4d3b180231da439f7502a2": { - "balance": "2000000000000000000000" - }, - "e408aa99835307eea4a6c5eb801fe694117f707d": { - "balance": "500000000000000000000" - }, - "e60a55f2df996dc3aedb696c08dde039b2641de8": { - "balance": "2000000000000000000000" - }, - "73df3c3e7955f4f2d859831be38000b1076b3884": { - "balance": "1970000000000000000000" - }, - "6228ade95e8bb17d1ae23bfb0518414d497e0eb8": { - "balance": "400000000000000000000" - }, - "0f46c81db780c1674ac73d314f06539ee56ebc83": { - "balance": "9850000000000000000000" - }, - "762d6f30dab99135e4eca51d5243d6c8621102d5": { - "balance": "282000000000000000000" - }, - "4ba0d9e89601772b496847a2bb4340186787d265": { - "balance": "1000000000000000000000" - }, - "ca747576446a4c8f30b08340fee198de63ec92cf": { - "balance": "7020000000000000000000" - }, - "99c31fe748583787cdd3e525b281b218961739e3": { - "balance": "1015200000000000000000" - }, - "1210f80bdb826c175462ab0716e69e46c24ad076": { - "balance": "100000000000000000000" - }, - "3f75ae61cc1d8042653b5baec4443e051c5e7abd": { - "balance": "95500000000000000000" - }, - "5c4892907a0720df6fd3413e63ff767d6b398023": { - "balance": "13189467000000000000000" - }, - "17f14632a7e2820be6e8f6df823558283dadab2d": { - "balance": "2000000000000000000000" - }, - "1dc7f7dad85df53f1271152403f4e1e4fdb3afa0": { - "balance": "200000000000000000000" - }, - "5a30feac37ac9f72d7b4af0f2bc73952c74fd5c3": { - "balance": "2000000000000000000000" - }, - "136d4b662bbd1080cfe4445b0fa213864435b7f1": { - "balance": "4000000000000000000000" - }, - "c1ec81dd123d4b7c2dd9b4d438a7072c11dc874c": { - "balance": "2000000000000000000000" - }, - "09f9575be57d004793c7a4eb84b71587f97cbb6a": { - "balance": "200000000000000000000" - }, - "2c4b470307a059854055d91ec3794d80b53d0f4a": { - "balance": "20000000000000000000000" - }, - "6af6c7ee99df271ba15bf384c0b764adcb4da182": { - "balance": "999972000000000000000" - }, - "0dae3ee5b915b36487f9161f19846d101433318a": { - "balance": "1910000000000000000000" - }, - "0dcf9d8c9804459f647c14138ed50fad563b4154": { - "balance": "173000000000000000000" - }, - "bfa8c858df102cb12421008b0a31c4c7190ad560": { - "balance": "200000000000000000000" - }, - "c2fd0bf7c725ef3e047e5ae1c29fe18f12a7299c": { - "balance": "1337000000000000000000" - }, - "d70a612bd6dda9eab0dddcff4aaf4122d38feae4": { - "balance": "540000000000000000000" - }, - "e07137ae0d116d033533c4eab496f8a9fb09569c": { - "balance": "1400000000000000000000" - }, - "7f49f20726471ac1c7a83ef106e9775ceb662566": { - "balance": "5910000000000000000000" - }, - "1e706655e284dcf0bb37fe075d613a18dc12ff4a": { - "balance": "4376760000000000000000" - }, - "03af7ad9d5223cf7c8c13f20df67ebe5ffc5bb41": { - "balance": "200000000000000000000" - }, - "228242f8336eecd8242e1f000f41937e71dffbbf": { - "balance": "5000000000000000000000" - }, - "e8ed51bbb3ace69e06024b33f86844c47348db9e": { - "balance": "165170600000000000000000" - }, - "3b566a8afad19682dc2ce8679a3ce444a5b0fd4f": { - "balance": "2000000000000000000000" - }, - "dc738fb217cead2f69594c08170de1af10c419e3": { - "balance": "100000000000000000000000" - }, - "13032446e7d610aa00ec8c56c9b574d36ca1c016": { - "balance": "2000000000000000000000" - }, - "6ca6a132ce1cd288bee30ec7cfeffb85c1f50a54": { - "balance": "2000000000000000000000" - }, - "b85f26dd0e72d9c29ebaf697a8af77472c2b58b5": { - "balance": "11900000000000000000000" - }, - "055bd02caf19d6202bbcdc836d187bd1c01cf261": { - "balance": "100000000000000000000" - }, - "3c322e611fdb820d47c6f8fc64b6fad74ca95f5e": { - "balance": "242514000000000000000" - }, - "8daddf52efbd74da95b969a5476f4fbbb563bfd2": { - "balance": "835000000000000000000" - }, - "c63ac417992e9f9b60386ed953e6d7dff2b090e8": { - "balance": "4000086000000000000000" - }, - "27f03cf1abc5e1b51dbc444b289e542c9ddfb0e6": { - "balance": "5000000000000000000000" - }, - "d8f4bae6f84d910d6d7d5ac914b1e68372f94135": { - "balance": "100000000000000000000" - }, - "9f83a293c324d4106c18faa8888f64d299054ca0": { - "balance": "200000000000000000000" - }, - "39ee4fe00fbced647068d4f57c01cb22a80bccd1": { - "balance": "6000000000000000000000" - }, - "404100db4c5d0eec557823b58343758bcc2c8083": { - "balance": "20000000000000000000" - }, - "02751dc68cb5bd737027abf7ddb77390cd77c16b": { - "balance": "20000000000000000000" - }, - "d10302faa1929a326904d376bf0b8dc93ad04c4c": { - "balance": "1790000000000000000000" - }, - "cc419fd9912b85135659e77a93bc3df182d45115": { - "balance": "10000000000000000000000" - }, - "10097198b4e7ee91ff82cc2f3bd95fed73c540c0": { - "balance": "2000000000000000000000" - }, - "7e24d9e22ce1da3ce19f219ccee523376873f367": { - "balance": "5900150000000000000000" - }, - "2e4ee1ae996aa0a1d92428d06652a6bea6d2d15d": { - "balance": "2000000000000000000000" - }, - "91a4149a2c7b1b3a67ea28aff34725e0bf8d7524": { - "balance": "1940000000000000000000" - }, - "ead65262ed5d122df2b2751410f98c32d1238f51": { - "balance": "101680000000000000000" - }, - "e20954d0f4108c82d4dcb2148d26bbd924f6dd24": { - "balance": "10000000000000000000000" - }, - "ebb7d2e11bc6b58f0a8d45c2f6de3010570ac891": { - "balance": "26740000000000000000" - }, - "ef115252b1b845cd857f002d630f1b6fa37a4e50": { - "balance": "1970000000000000000000" - }, - "01a818135a414210c37c62b625aca1a54611ac36": { - "balance": "260000000000000000000" - }, - "ea1ea0c599afb9cd36caacbbb52b5bbb97597377": { - "balance": "1069600000000000000000" - }, - "7a7a4f807357a4bbe68e1aa806393210c411ccb3": { - "balance": "30000000000000000000000" - }, - "6d40ca27826d97731b3e86effcd7b92a4161fe89": { - "balance": "2000000000000000000000" - }, - "8431277d7bdd10457dc017408c8dbbbd414a8df3": { - "balance": "39400000000000000000" - }, - "69b81d5981141ec7a7141060dfcf8f3599ffc63e": { - "balance": "5000000000000000000000" - }, - "47688410ff25d654d72eb2bc06e4ad24f833b094": { - "balance": "160440000000000000000" - }, - "6c101205b323d77544d6dc52af37aca3cec6f7f1": { - "balance": "10000000000000000000000" - }, - "fb685c15e439965ef626bf0d834cd1a89f2b5695": { - "balance": "3940000000000000000000" - }, - "673706b1b0e4dc7a949a7a796258a5b83bb5aa83": { - "balance": "16100000000000000000000" - }, - "ecdaf93229b45ee672f65db506fb5eca00f7fce6": { - "balance": "1605009000000000000000" - }, - "ec6904bae1f69790591709b0609783733f2573e3": { - "balance": "500000000000000000000" - }, - "812ea7a3b2c86eed32ff4f2c73514cc63bacfbce": { - "balance": "1000000000000000000000" - }, - "196c02210a450ab0b36370655f717aa87bd1c004": { - "balance": "259456000000000000000" - }, - "d96ac2507409c7a383ab2eee1822a5d738b36b56": { - "balance": "200000000000000000000" - }, - "ae2f9c19ac76136594432393b0471d08902164d3": { - "balance": "698600000000000000000" - }, - "9d32962ea99700d93228e9dbdad2cc37bb99f07e": { - "balance": "3327560000000000000000" - }, - "17e584e810e567702c61d55d434b34cdb5ee30f6": { - "balance": "5000000000000000000000" - }, - "a3a93ef9dbea2636263d06d8492f6a41de907c22": { - "balance": "60000000000000000000" - }, - "2b5016e2457387956562587115aa8759d8695fdf": { - "balance": "200000000000000000000000" - }, - "140129eaa766b5a29f5b3af2574e4409f8f6d3f1": { - "balance": "6400000000000000000000" - }, - "7025965d2b88da197d4459be3dc9386344cc1f31": { - "balance": "2005500000000000000000" - }, - "388bdcdae794fc44082e667501344118ea96cd96": { - "balance": "1670000000000000000000" - }, - "eee9d0526eda01e43116a395322dda8970578f39": { - "balance": "9999980000000000000000" - }, - "6ec89b39f9f5276a553e8da30e6ec17aa47eefc7": { - "balance": "447500000000000000000" - }, - "7e236666b2d06e63ea4e2ab84357e2dfc977e50e": { - "balance": "999972000000000000000" - }, - "68df947c495bebaeb8e889b3f953d533874bf106": { - "balance": "546000000000000000000" - }, - "d40ed66ab3ceff24ca05ecd471efb492c15f5ffa": { - "balance": "500000000000000000000" - }, - "f0c70d0d6dab7663aa9ed9ceea567ee2c6b02765": { - "balance": "2089349000000000000000" - }, - "b589676d15a04448344230d4ff27c95edf122c49": { - "balance": "1000000000000000000000" - }, - "a0347f0a98776390165c166d32963bf74dcd0a2f": { - "balance": "1000000000000000000000" - }, - "d47d8685faee147c520fd986709175bf2f886bef": { - "balance": "2000000000000000000000" - }, - "a1dcd0e5b05a977c9623e5ae2f59b9ada2f33e31": { - "balance": "100000000000000000000" - }, - "4979194ec9e97db9bee8343b7c77d9d7f3f1dc9f": { - "balance": "20000000000000000000" - }, - "7cd20eccb518b60cab095b720f571570caaa447e": { - "balance": "500000000000000000000" - }, - "2ff830cf55fb00d5a0e03514fecd44314bd6d9f1": { - "balance": "10000000000000000000000" - }, - "0bb25ca7d188e71e4d693d7b170717d6f8f0a70a": { - "balance": "336870000000000000000" - }, - "e9a2b4914e8553bf0d7c00ca532369b879f931bf": { - "balance": "2000000000000000000000" - }, - "720e6b22bf430966fa32b6acb9a506eebf662c61": { - "balance": "152000000000000000000" - }, - "7ade5d66b944bb860c0efdc86276d58f4653f711": { - "balance": "2000000000000000000000" - }, - "2eaff9f8f8113064d3957ac6d6e11eee42c8195d": { - "balance": "1970000000000000000000" - }, - "0c8fd7775e54a6d9c9a3bf890e761f6577693ff0": { - "balance": "9850000000000000000000" - }, - "290a56d41f6e9efbdcea0342e0b7929a8cdfcb05": { - "balance": "344000000000000000000" - }, - "d73ed2d985b5f21b55b274643bc6da031d8edd8d": { - "balance": "49250000000000000000000" - }, - "80156d10efa8b230c99410630d37e269d4093cea": { - "balance": "2000000000000000000000" - }, - "0989c200440b878991b69d6095dfe69e33a22e70": { - "balance": "1910000000000000000000" - }, - "ec8014efc7cbe5b0ce50f3562cf4e67f8593cd32": { - "balance": "17300000000000000000" - }, - "de612d0724e84ea4a7feaa3d2142bd5ee82d3201": { - "balance": "20000000000000000000" - }, - "0f832a93df9d7f74cd0fb8546b7198bf5377d925": { - "balance": "143000000000000000000" - }, - "aa2c670096d3f939305325427eb955a8a60db3c5": { - "balance": "2003010000000000000000" - }, - "25287b815f5c82380a73b0b13fbaf982be24c4d3": { - "balance": "40000000000000000000" - }, - "e75c3b38a58a3f33d55690a5a59766be185e0284": { - "balance": "500000000000000000000" - }, - "1940dc9364a852165f47414e27f5002445a4f143": { - "balance": "10850000000000000000000" - }, - "e5b826196c0e1bc1119b021cf6d259a610c99670": { - "balance": "200000000000000000000" - }, - "82a15cef1d6c8260eaf159ea3f0180d8677dce1c": { - "balance": "2000000000000000000000" - }, - "da06044e293c652c467fe74146bf185b21338a1c": { - "balance": "1000000000000000000000" - }, - "f815c10a032d13c34b8976fa6e3bd2c9131a8ba9": { - "balance": "1337000000000000000000" - }, - "cd95fa423d6fc120274aacde19f4eeb766f10420": { - "balance": "200000000000000000000" - }, - "e3a4f83c39f85af9c8b1b312bfe5fc3423afa634": { - "balance": "28650000000000000000" - }, - "768ce0daa029b7ded022e5fc574d11cde3ecb517": { - "balance": "322000000000000000000" - }, - "e3ec18a74ed43855409a26ade7830de8e42685ef": { - "balance": "19700000000000000000" - }, - "b2bdbedf95908476d7148a370cc693743628057f": { - "balance": "4000000000000000000000" - }, - "bbb8ffe43f98de8eae184623ae5264e424d0b8d7": { - "balance": "107600000000000000000" - }, - "090cebef292c3eb081a05fd8aaf7d39bf07b89d4": { - "balance": "4000000000000000000000" - }, - "dd2a233adede66fe1126d6c16823b62a021feddb": { - "balance": "2000000000000000000000" - }, - "d8cd64e0284eec53aa4639afc4750810b97fab56": { - "balance": "20000000000000000000" - }, - "e5953fea497104ef9ad2d4e5841c271f073519c2": { - "balance": "704000000000000000000" - }, - "967d4142af770515dd7062af93498dbfdff29f20": { - "balance": "20200000000000000000" - }, - "fd191a35157d781373fb411bf9f25290047c5eef": { - "balance": "1000000000000000000000" - }, - "8967d7b9bdb7b4aed22e65a15dc803cb7a213f10": { - "balance": "400000000000000000000" - }, - "51e43fe0d25c782860af81ea89dd793c13f0cbb1": { - "balance": "60000000000000000000" - }, - "a38476691d34942eea6b2f76889223047db4617a": { - "balance": "2000000000000000000000" - }, - "1321ccf29739b974e5a516f18f3a843671e39642": { - "balance": "4000000000000000000000" - }, - "4d71a6eb3d7f327e1834278e280b039eddd31c2f": { - "balance": "6000000000000000000000" - }, - "dc2d15a69f6bb33b246aef40450751c2f6756ad2": { - "balance": "1996000000000000000000" - }, - "ec89f2b678a1a15b9134ec5eb70c6a62071fbaf9": { - "balance": "200000000000000000000" - }, - "27bf943c1633fe32f8bcccdb6302b407a5724e44": { - "balance": "940229000000000000000" - }, - "d0a6c6f9e9c4b383d716b31de78d56414de8fa91": { - "balance": "300000000000000000000" - }, - "7b6175ec9befc738249535ddde34688cd36edf25": { - "balance": "10000000000000000000000" - }, - "41ce79950935cff55bf78e4ccec2fe631785db95": { - "balance": "2000000000000000000000" - }, - "5598b3a79a48f32b1f5fc915b87b645d805d1afe": { - "balance": "500000000000000000000" - }, - "5c4881165cb42bb82e97396c8ef44adbf173fb99": { - "balance": "110600000000000000000" - }, - "25b0533b81d02a617b9229c7ec5d6f2f672e5b5a": { - "balance": "1000000000000000000000" - }, - "015f097d9acddcddafaf2a107eb93a40fc94b04c": { - "balance": "20000000000000000000000" - }, - "b84b53d0bb125656cddc52eb852ab71d7259f3d5": { - "balance": "16000000000000000000000" - }, - "1a79c7f4039c67a39d7513884cdc0e2c34222490": { - "balance": "20000000000000000000" - }, - "926209b7fda54e8ddb9d9e4d3d19ebdc8e88c29f": { - "balance": "2000000000000000000000" - }, - "c2fe7d75731f636dcd09dbda0671393ba0c82a7d": { - "balance": "2200000000000000000000" - }, - "30248d58e414b20fed3a6c482b59d9d8f5a4b7e2": { - "balance": "60000000000000000000" - }, - "d0e194f34b1db609288509ccd2e73b6131a2538b": { - "balance": "999972000000000000000" - }, - "e8f29969e75c65e01ce3d86154207d0a9e7c76f2": { - "balance": "2991807000000000000000" - }, - "cb93199b9c90bc4915bd859e3d42866dc8c18749": { - "balance": "231800000000000000000" - }, - "e6fe0afb9dcedd37b2e22c451ba6feab67348033": { - "balance": "10000000000000000000000" - }, - "82f854c9c2f087dffa985ac8201e626ca5467686": { - "balance": "100000000000000000000000" - }, - "63bb664f9117037628594da7e3c5089fd618b5b5": { - "balance": "20000000000000000000" - }, - "f8d17424c767bea31205739a2b57a7277214eebe": { - "balance": "42000000000000000000" - }, - "4ca8db4a5efefc80f4cd9bbcccb03265931332b6": { - "balance": "200000000000000000000" - }, - "c56e6b62ba6e40e52aab167d21df025d0055754b": { - "balance": "2000000000000000000000" - }, - "0d8c40a79e18994ff99ec251ee10d088c3912e80": { - "balance": "114600000000000000000" - }, - "40a331195b977325c2aa28fa2f42cb25ec3c253c": { - "balance": "2000000000000000000000" - }, - "a2c5854ff1599f98892c5725d262be1da98aadac": { - "balance": "314315000000000000000" - }, - "23ab09e73f87aa0f3be0139df0c8eb6be5634f95": { - "balance": "8000000000000000000000" - }, - "b8040536958d5998ce4bec0cfc9c2204989848e9": { - "balance": "24472420000000000000000" - }, - "42d6b263d9e9f4116c411424fc9955783c763030": { - "balance": "2000000000000000000000" - }, - "c496cbb0459a6a01600fc589a55a32b454217f9d": { - "balance": "274000000000000000000" - }, - "48302c311ef8e5dc664158dd583c81194d6e0d58": { - "balance": "3364760000000000000000" - }, - "d5b284040130abf7c1d163712371cc7e28ad66da": { - "balance": "1970000000000000000000" - }, - "d22f0ca4cd479e661775053bcc49e390f670dd8a": { - "balance": "1000000000000000000000" - }, - "e597f083a469c4591c3d2b1d2c772787befe27b2": { - "balance": "280000000000000000000" - }, - "668b6ba8ab08eace39c502ef672bd5ccb6a67a20": { - "balance": "31135320000000000000000" - }, - "a3bff1dfa9971668360c0d82828432e27bf54e67": { - "balance": "200000000000000000000" - }, - "ee655bb4ee0e8d5478526fb9f15e4064e09ff3dd": { - "balance": "200000000000000000000" - }, - "121f855b70149ac83473b9706fb44d47828b983b": { - "balance": "1400000000000000000000" - }, - "20a15256d50ce058bf0eac43aa533aa16ec9b380": { - "balance": "20000000000000000000" - }, - "69bcfc1d43b4ba19de7b274bdffb35139412d3d7": { - "balance": "985000000000000000000" - }, - "db288f80ffe232c2ba47cc94c763cf6fc9b82b0d": { - "balance": "85000000000000000000" - }, - "e1cb83ec5eb6f1eeb85e99b2fc63812fde957184": { - "balance": "20000000000000000000000" - }, - "a419a984142363267575566089340eea0ea20819": { - "balance": "1999944000000000000000" - }, - "8489f6ad1d9a94a297789156899db64154f1dbb5": { - "balance": "358849000000000000000" - }, - "d609bf4f146eea6b0dc8e06ddcf4448a1fccc9fa": { - "balance": "2000000000000000000000" - }, - "df1fa2e20e31985ebe2c0f0c93b54c0fb67a264b": { - "balance": "200000000000000000000" - }, - "efe8ff87fc260e0767638dd5d02fc4672e0ec06d": { - "balance": "2000000000000000000000" - }, - "eef1bbb1e5a83fde8248f88ee3018afa2d1332eb": { - "balance": "200000000000000000000" - }, - "4b3aab335ebbfaa870cc4d605e7d2e74c668369f": { - "balance": "60000000000000000000000" - }, - "8f4fb1aea7cd0f570ea5e61b40a4f4510b6264e4": { - "balance": "4000000000000000000000" - }, - "0b0b3862112aeec3a03492b1b05f440eca54256e": { - "balance": "4000000000000000000000" - }, - "dff4007931786593b229efe5959f3a4e219e51af": { - "balance": "4925000000000000000000" - }, - "fec14e5485de2b3eef5e74c46146db8e454e0335": { - "balance": "179000000000000000000" - }, - "ac21c1e5a3d7e0b50681679dd6c792dbca87decb": { - "balance": "100000000000000000000000" - }, - "796ebbf49b3e36d67694ad79f8ff36767ac6fab0": { - "balance": "60800000000000000000" - }, - "ae7739124ed153052503fc101410d1ffd8cd13b7": { - "balance": "999942000000000000000" - }, - "86026cad3fe4ea1ce7fca260d3d45eb09ea6a364": { - "balance": "200000000000000000000" - }, - "b2fc84a3e50a50af02f94da0383ed59f71ff01d7": { - "balance": "30000000000000000000000" - }, - "bbab000b0408ed015a37c04747bc461ab14e151b": { - "balance": "6000000000000000000000" - }, - "c4ff6fbb1f09bd9e102ba033d636ac1c4c0f5304": { - "balance": "1000000000000000000000" - }, - "cc606f511397a38fc7872bd3b0bd03c71bbd768b": { - "balance": "1000000000000000000000" - }, - "f346d7de92741c08fc58a64db55b062dde012d14": { - "balance": "295106000000000000000" - }, - "33f15223310d44de8b6636685f3a4c3d9c5655a5": { - "balance": "250500000000000000000" - }, - "3c860e2e663f46db53427b29fe3ea5e5bf62bbcc": { - "balance": "98500000000000000000" - }, - "acb94338554bc488cc88ae2d9d94080d6bdf8410": { - "balance": "1000000000000000000000" - }, - "9c5cc111092c122116f1a85f4ee31408741a7d2f": { - "balance": "492500000000000000000" - }, - "5f76f0a306269c78306b3d650dc3e9c37084db61": { - "balance": "2400000000000000000000" - }, - "2c0cc3f951482cc8a2925815684eb9f94e060200": { - "balance": "6000000000000000000000" - }, - "b74372dbfa181dc9242f39bf1d3731dffe2bdacf": { - "balance": "2000000000000000000000" - }, - "3bab4b01a7c84ba13feea9b0bb191b77a3aadca3": { - "balance": "200000000000000000000" - }, - "39aa05e56d7d32385421cf9336e90d3d15a9f859": { - "balance": "26000000000000000000" - }, - "4a52bad20357228faa1e996bed790c93674ba7d0": { - "balance": "1337000000000000000000" - }, - "ff128f4b355be1dc4a6f94fa510d7f15d53c2aff": { - "balance": "2720000000000000000000" - }, - "92793ac5b37268774a7130de2bbd330405661773": { - "balance": "40110000000000000000" - }, - "db19a3982230368f0177219cb10cb259cdb2257c": { - "balance": "2000000000000000000000" - }, - "8d1794da509cb297053661a14aa892333231e3c1": { - "balance": "199600000000000000000" - }, - "9b7c8810cc7cc89e804e6d3e38121850472877fe": { - "balance": "2000000000000000000000" - }, - "ed3cbc3782cebd67989b305c4133b2cde32211eb": { - "balance": "400000000000000000000" - }, - "8532490897bbb4ce8b7f6b837e4cba848fbe9976": { - "balance": "100000000000000000000" - }, - "c384ac6ee27c39e2f278c220bdfa5baed626d9d3": { - "balance": "600000000000000000000" - }, - "b1459285863ea2db3759e546ceb3fb3761f5909c": { - "balance": "1122309000000000000000" - }, - "634efc24371107b4cbf03f79a93dfd93e431d5fd": { - "balance": "1221341000000000000000" - }, - "ef9f59aeda418c1494682d941aab4924b5f4929a": { - "balance": "100000000000000000000000" - }, - "e7311c9533f0092c7248c9739b5b2c864a34b1ce": { - "balance": "2803436000000000000000" - }, - "e6e621eaab01f20ef0836b7cad47464cb5fd3c96": { - "balance": "316014000000000000000" - }, - "cd102cd6db3df14ad6af0f87c72479861bfc3d24": { - "balance": "2000000000000000000000" - }, - "005a9c03f69d17d66cbb8ad721008a9ebbb836fb": { - "balance": "2000000000000000000000" - }, - "a072cebe62a9e9f61cc3fbf88a9efbfe3e9a8d70": { - "balance": "400000000000000000000" - }, - "f2ab1161750244d0ecd048ee0d3e51abb143a2fd": { - "balance": "1235800000000000000000" - }, - "f686785b89720b61145fea80978d6acc8e0bc196": { - "balance": "4000000000000000000000" - }, - "0a2b4fc5d81ace67dc4bba03f7b455413d46fe3d": { - "balance": "197000000000000000000" - }, - "c32ec7e42ad16ce3e2555ad4c54306eda0b26758": { - "balance": "2000000000000000000000" - }, - "f3fa723552a5d0512e2b62f48dca7b2b8105305b": { - "balance": "137000000000000000000" - }, - "6dc3f92baa1d21dab7382b893261a0356fa7c187": { - "balance": "1730000000000000000000" - }, - "4627c606842671abde8295ee5dd94c7f549534f4": { - "balance": "286600000000000000000" - }, - "e39e46e15d22ce56e0c32f1877b7d1a264cf94f3": { - "balance": "20000000000000000000000" - }, - "d7d157e4c0a96437a6d285741dd23ec4361fa36b": { - "balance": "2000000000000000000000" - }, - "68f8f45155e98c5029a4ebc5b527a92e9fa83120": { - "balance": "4436101000000000000000" - }, - "9aba2b5e27ff78baaab5cdc988b7be855cebbdce": { - "balance": "9999000000000000000000" - }, - "66b39837cb3cac8a802afe3f12a258bbca62dacd": { - "balance": "400000000000000000000" - }, - "d39b7cbc94003fc948f0cde27b100db8ccd6e063": { - "balance": "400000000000000000000" - }, - "3db9ed7f024c7e26372feacf2b050803445e3810": { - "balance": "1285600000000000000000" - }, - "3fbc1e4518d73400c6d046359439fb68ea1a49f4": { - "balance": "16400000000000000000000" - }, - "e3da4f3240844c9b6323b4996921207122454399": { - "balance": "11539639000000000000000" - }, - "09afa73bc047ef46b977fd9763f87286a6be68c6": { - "balance": "501500000000000000000" - }, - "1dbe8e1c2b8a009f85f1ad3ce80d2e05350ee39c": { - "balance": "135400000000000000000" - }, - "2c5a2d0abda03bbe215781b4ff296c8c61bdbaf6": { - "balance": "30617000000000000000" - }, - "9a9d1dc0baa77d6e20c3d849c78862dd1c054c87": { - "balance": "880000000000000000000" - }, - "3ccef88679573947e94997798a1e327e08603a65": { - "balance": "807700000000000000000" - }, - "850b9db18ff84bf0c7da49ea3781d92090ad7e64": { - "balance": "2600000000000000000000" - }, - "361c75931696bc3d427d93e76c77fd13b241f6f4": { - "balance": "549212000000000000000" - }, - "c8f2b320e6dfd70906c597bad2f9501312c78259": { - "balance": "1504800000000000000000" - }, - "8dc1d5111d09af25fdfcac455c7cec283e6d6775": { - "balance": "2000000000000000000000" - }, - "cd7ece086b4b619b3b369352ee38b71ddb06439a": { - "balance": "200000000000000000000" - }, - "f607c2150d3e1b99f24fa1c7d540add35c4ebe1e": { - "balance": "3098020000000000000000" - }, - "32485c818728c197fea487fbb6e829159eba8370": { - "balance": "1053893000000000000000" - }, - "8e670815fb67aeaea57b86534edc00cdf564fee5": { - "balance": "3300000000000000000000" - }, - "10df681506e34930ac7a5c67a54c3e89ce92b981": { - "balance": "2153800000000000000000" - }, - "1cf2eb7a8ccac2adeaef0ee87347d535d3b94058": { - "balance": "2000000000000000000000" - }, - "f0dc43f205619127507b2b1c1cfdf32d28310920": { - "balance": "301973000000000000000" - }, - "f2c2904e9fa664a11ee25656d8fd2cc0d9a522a0": { - "balance": "13370000000000000000" - }, - "70670fbb05d33014444b8d1e8e7700258b8caa6d": { - "balance": "2000000000000000000000" - }, - "5160ed612e1b48e73f3fc15bc4321b8f23b8a24b": { - "balance": "562800000000000000000" - }, - "54a62bf9233e146ffec3876e45f20ee8414adeba": { - "balance": "10000000000000000000000" - }, - "26d4ec17d5ceb2c894bdc59d0a6a695dad2b43cc": { - "balance": "2935300000000000000000" - }, - "205fc843e19a4913d1881eb69b69c0fa3be5c50b": { - "balance": "9700000000000000000000" - }, - "e001aba77c02e172086c1950fffbcaa30b83488f": { - "balance": "1970000000000000000000" - }, - "21efbca09b3580b98e73f5b2f7f4dc0bf02c529c": { - "balance": "2000000000000000000000" - }, - "c4d916574e68c49f7ef9d3d82d1638b2b7ee0985": { - "balance": "1580000000000000000000" - }, - "cab0d32cf3767fa6b3537c84328baa9f50458136": { - "balance": "8960000000000000000000" - }, - "7ce4686446f1949ebed67215eb0d5a1dd72c11b8": { - "balance": "2217776000000000000000" - }, - "7837fcb876da00d1eb3b88feb3df3fa4042fac82": { - "balance": "1760000000000000000000" - }, - "71e38ff545f30fe14ca863d4f5297fd48c73a5ce": { - "balance": "3580000000000000000000" - }, - "e528a0e5a267d667e9393a6584e19b34dc9be973": { - "balance": "5600000000000000000000" - }, - "c5374928cdf193705443b14cc20da423473cd9cf": { - "balance": "138139000000000000000" - }, - "e406f5dd72cab66d8a6ecbd6bfb494a7b6b09afe": { - "balance": "100000000000000000000" - }, - "d7ef340e66b0d7afcce20a19cb7bfc81da33d94e": { - "balance": "3000000000000000000000" - }, - "e012db453827a58e16c1365608d36ed658720507": { - "balance": "2000000000000000000000" - }, - "d59638d3c5faa7711bf085745f9d5bdc23d498d8": { - "balance": "2000000000000000000000" - }, - "008fc7cbadffbd0d7fe44f8dfd60a79d721a1c9c": { - "balance": "1000000000000000000000" - }, - "8a3470282d5e2a2aefd7a75094c822c4f5aeef8a": { - "balance": "242743000000000000000" - }, - "38b3965c21fa893931079beacfffaf153678b6eb": { - "balance": "170374000000000000000" - }, - "57dd9471cbfa262709f5f486bcb774c5f527b8f8": { - "balance": "197000000000000000000" - }, - "5a60c924162873fc7ea4da7f972e350167376031": { - "balance": "83583000000000000000" - }, - "b9013c51bd078a098fae05bf2ace0849c6be17a5": { - "balance": "80000000000000000000" - }, - "dc23b260fcc26e7d10f4bd044af794579460d9da": { - "balance": "500038000000000000000" - }, - "45db03bccfd6a5f4d0266b82a22a368792c77d83": { - "balance": "8000000000000000000000" - }, - "3e0cbe6a6dcb61f110c45ba2aa361d7fcad3da73": { - "balance": "8022000000000000000000" - }, - "42d3a5a901f2f6bd9356f112a70180e5a1550b60": { - "balance": "925000000000000000000" - }, - "47219229e8cd56659a65c2a943e2dd9a8f4bfd89": { - "balance": "1520000000000000000000" - }, - "a20d071b1b003063497d7990e1249dabf36c35f7": { - "balance": "1000000000000000000000" - }, - "6835c8e8b74a2ca2ae3f4a8d0f6b954a3e2a8392": { - "balance": "60140000000000000000" - }, - "0c2d5c920538e953caaf24f0737f554cc6927742": { - "balance": "1000000000000000000000" - }, - "eedf6c4280e6eb05b934ace428e11d4231b5905b": { - "balance": "200000000000000000000" - }, - "ffa696ecbd787e66abae4fe87b635f07ca57d848": { - "balance": "1337000000000000000000" - }, - "3e81772175237eb4cbe0fe2dcafdadffeb6a1999": { - "balance": "8800000000000000000000" - }, - "b44783c8e57b480793cbd69a45d90c7b4f0c48ac": { - "balance": "20000000000000000000" - }, - "f84f090adf3f8db7e194b350fbb77500699f66fd": { - "balance": "1970000000000000000000" - }, - "2e9824b5c132111bca24ddfba7e575a5cd7296c1": { - "balance": "17201900000000000000000" - }, - "5cce72d068c7c3f55b1d2819545e77317cae8240": { - "balance": "1940000000000000000000" - }, - "d815e1d9f4e2b5e57e34826b7cfd8881b8546890": { - "balance": "17300000000000000000" - }, - "f901c00fc1db88b69c4bc3252b5ca70ea6ee5cf6": { - "balance": "400000000000000000000" - }, - "a960b1cadd3b5c1a8e6cb3abcaf52ee7c3d9fa88": { - "balance": "1522704000000000000000" - }, - "f7e45a12aa711c709acefe95f33b78612d2ad22a": { - "balance": "66230000000000000000000" - }, - "c332df50b13c013490a5d7c75dbfa366da87b6d6": { - "balance": "4000000000000000000000" - }, - "d467cf064c0871989b90d8b2eb14ccc63b360823": { - "balance": "200000000000000000000" - }, - "b9144b677c2dc614ceefdf50985f1183208ea64c": { - "balance": "2000000000000000000000" - }, - "ea7c4d6dc729cd6b157c03ad237ca19a209346c3": { - "balance": "2000000000000000000000" - }, - "9c9de44724a4054da0eaa605abcc802668778bea": { - "balance": "200020000000000000000" - }, - "d7140c8e5a4307fab0cc27badd9295018bf87970": { - "balance": "109600000000000000000" - }, - "c33acdb3ba1aab27507b86b15d67faf91ecf6293": { - "balance": "2000000000000000000000" - }, - "db2a0c9ab64df58ddfb1dbacf8ba0d89c85b31b4": { - "balance": "4000000000000000000000" - }, - "bfcb9730246304700da90b4153e71141622e1c41": { - "balance": "1000000000000000000000" - }, - "07dc8c8b927adbedfa8f5d639b4352351f2f36d2": { - "balance": "314382000000000000000" - }, - "2d5391e938b34858cf965b840531d5efda410b09": { - "balance": "1400000000000000000000" - }, - "0b5e2011ebc25a007f21362960498afb8af280fb": { - "balance": "2000000000000000000000" - }, - "ed9fb1f5af2fbf7ffc5029cee42b70ff5c275bf5": { - "balance": "280000000000000000000" - }, - "a3232d068d50064903c9ebc563b515acc8b7b097": { - "balance": "2002000000000000000000" - }, - "66274fea82cd30b6c29b23350e4f4f3d310a5899": { - "balance": "2070000000000000000000" - }, - "dbfb1bb464b8a58e500d2ed8de972c45f5f1c0fb": { - "balance": "1600000000000000000000" - }, - "a1f8d8bcf90e777f19b3a649759ad95027abdfc3": { - "balance": "200000000000000000000" - }, - "5bd23547477f6d09d7b2a005c5ee650c510c56d7": { - "balance": "10000000000000000000000" - }, - "ec3b8b58a12703e581ce5ffd7e21c57d1e5c663f": { - "balance": "1700000000000000000000" - }, - "54310b3aa88703a725dfa57de6e646935164802c": { - "balance": "1910000000000000000000" - }, - "8f41b1fbf54298f5d0bc2d122f4eb95da4e5cd3d": { - "balance": "354200000000000000000" - }, - "c80b36d1beafba5fcc644d60ac6e46ed2927e7dc": { - "balance": "13370000000000000000" - }, - "1ea492bce1ad107e337f4bd4a7ac9a7babcccdab": { - "balance": "100000000000000000000" - }, - "aaf023fef290a49bb78bb7abc95d669c50d528b0": { - "balance": "200000000000000000000" - }, - "80b79f338390d1ba1b3737a29a0257e5d91e0731": { - "balance": "20000000000000000000" - }, - "f382e4c20410b951089e19ba96a2fee3d91cce7e": { - "balance": "5054000000000000000000" - }, - "0748713145ef83c3f0ef4d31d823786f7e9cc689": { - "balance": "4500000000000000000000" - }, - "21e219c89ca8ac14ae4cba6130eeb77d9e6d3962": { - "balance": "789580000000000000000" - }, - "ca9a042a6a806ffc92179500d24429e8ab528117": { - "balance": "1100000000000000000000" - }, - "bcc9593b2da6df6a34d71b1aa38dacf876f95b88": { - "balance": "20000000000000000000" - }, - "d1438267231704fc7280d563adf4763844a80722": { - "balance": "200000000000000000000" - }, - "4989e1ab5e7cd00746b3938ef0f0d064a2025ba5": { - "balance": "2000000000000000000000" - }, - "bd4b60faec740a21e3071391f96aa534f7c1f44e": { - "balance": "182000000000000000000" - }, - "8c7cb4e48b25031aa1c4f92925d631a8c3edc761": { - "balance": "1000000000000000000000" - }, - "322788b5e29bf4f5f55ae1ddb32085fda91b8ebe": { - "balance": "200000000000000000000" - }, - "f15e182c4fbbad79bd93342242d4dccf2be58925": { - "balance": "1940000000000000000000" - }, - "1548b770a5118ede87dba2f690337f616de683ab": { - "balance": "527558000000000000000" - }, - "69c2d835f13ee90580408e6a3283c8cca6a434a2": { - "balance": "656000000000000000000" - }, - "a1e4380a3b1f749673e270229993ee55f35663b4": { - "balance": "2000000000000000000000" - }, - "c7675e5647b9d8daf4d3dff1e552f6b07154ac38": { - "balance": "180000000000000000000" - }, - "a02c1e34064f0475f7fa831ccb25014c3aa31ca2": { - "balance": "60000000000000000000" - }, - "517c75430de401c341032686112790f46d4d369e": { - "balance": "388000000000000000000" - }, - "29681d9912ddd07eaabb88d05d90f766e862417d": { - "balance": "1000000000000000000000" - }, - "544dda421dc1eb73bb24e3e56a248013b87c0f44": { - "balance": "1970000000000000000000" - }, - "2ab97e8d59eee648ab6caf8696f89937143864d6": { - "balance": "3820000000000000000000" - }, - "79c130c762b8765b19d2abc9a083ab8f3aad7940": { - "balance": "3940000000000000000000" - }, - "f9650d6989f199ab1cc479636ded30f241021f65": { - "balance": "850000000000000000000" - }, - "d1c96e70f05ae0e6cd6021b2083750a7717cde56": { - "balance": "500000000000000000000" - }, - "88106c27d20b74b4b98ca62b232bd5c97411171f": { - "balance": "197000000000000000000" - }, - "37ab66083a4fa23848b886f9e66d79cdc150cc70": { - "balance": "88510000000000000000000" - }, - "8e6156336be2cdbe32140df08a2ba55fd0a58463": { - "balance": "74480000000000000000" - }, - "2982d76a15f847dd41f1922af368fe678d0e681e": { - "balance": "100000000000000000000" - }, - "209e8e29d33beae8fb6baa783d133e1d9ec1bc0b": { - "balance": "835000000000000000000" - }, - "b325674c01e3f7290d5226339fbeac67d221279f": { - "balance": "2800000000000000000000" - }, - "f20c9a99b74759d782f25c1ceca802a27e0b436c": { - "balance": "1670000000000000000000" - }, - "61bf84d5ab026f58c873f86ff0dfca82b55733ae": { - "balance": "2000000000000000000000" - }, - "0734a0a81c9562f4d9e9e10a8503da15db46d76e": { - "balance": "18200000000000000000" - }, - "0521bc3a9f8711fecb10f50797d71083e341eb9d": { - "balance": "20000000000000000000" - }, - "3301d9ca2f3bfe026279cd6819f79a293d98156e": { - "balance": "50000000000000000000000" - }, - "549d51af29f724c967f59423b85b2681e7b15136": { - "balance": "3760000000000000000000" - }, - "2053ac97548a0c4e8b80bc72590cd6a098fe7516": { - "balance": "187000000000000000000" - }, - "aa321fdbd449180db8ddd34f0fe906ec18ee0914": { - "balance": "685000000000000000000" - }, - "697f55536bf85ada51841f0287623a9f0ed09a17": { - "balance": "10000000000000000000000" - }, - "df57353aaff2aadb0a04f9014e8da7884e86589c": { - "balance": "152800000000000000000" - }, - "6807ddc88db489b033e6b2f9a81553571ab3c805": { - "balance": "29944000000000000000" - }, - "90057af9aa66307ec9f033b29724d3b2f41eb6f9": { - "balance": "121930000000000000000000" - }, - "3ff836b6f57b901b440c30e4dbd065cf37d3d48c": { - "balance": "200000000000000000000" - }, - "91051764af6b808e4212c77e30a5572eaa317070": { - "balance": "1000000000000000000000" - }, - "7faa30c31519b584e97250ed2a3cf3385ed5fd50": { - "balance": "2000000000000000000000" - }, - "fb842ca2c5ef133917a236a0d4ac40690110b038": { - "balance": "306000000000000000000" - }, - "aa167026d39ab7a85635944ed9edb2bfeba11850": { - "balance": "8298000000000000000000" - }, - "57beea716cbd81700a73d67f9ff039529c2d9025": { - "balance": "200000000000000000000" - }, - "654b7e808799a83d7287c67706f2abf49a496404": { - "balance": "1970000000000000000000" - }, - "dde8f0c31b7415511dced1cd7d46323e4bd12232": { - "balance": "1610000000000000000000" - }, - "8667fa1155fed732cfb8dca5a0d765ce0d0705ed": { - "balance": "81770000000000000000" - }, - "905526568ac123afc0e84aa715124febe83dc87c": { - "balance": "17900000000000000000" - }, - "8e98766524b0cf2747c50dd43b9567594d9731de": { - "balance": "1997200000000000000000" - }, - "c6df2075ebd240d44869c2be6bdf82e63d4ef1f5": { - "balance": "20000000000000000000" - }, - "2ff5cab12c0d957fd333f382eeb75107a64cb8e8": { - "balance": "10000000000000000000000" - }, - "3055efd26029e0d11b930df4f53b162c8c3fd2ce": { - "balance": "499938000000000000000" - }, - "b2c53efa33fe4a3a1a80205c73ec3b1dbcad0602": { - "balance": "1918595000000000000000" - }, - "766b3759e8794e926dac473d913a8fb61ad0c2c9": { - "balance": "86500000000000000000" - }, - "882aa798bf41df179f85520130f15ccdf59b5e58": { - "balance": "2000000000000000000000" - }, - "80b23d380b825c46e0393899a85556462da0e18c": { - "balance": "2000000000000000000000" - }, - "51f4663ab44ff79345f427a0f6f8a6c8a53ff234": { - "balance": "20000000000000000000000" - }, - "8d5ef172bf77315ea64e85d0061986c794c6f519": { - "balance": "3940000000000000000000" - }, - "75ac547017134c04ae1e11d60e63ec04d18db4ef": { - "balance": "6000000000000000000000" - }, - "ce1b0cb46aaecfd79b880cad0f2dda8a8dedd0b1": { - "balance": "20000000000000000000" - }, - "21408b4d7a2c0e6eca4143f2cacdbbccba121bd8": { - "balance": "20000000000000000000000" - }, - "9c526a140683edf1431cfaa128a935e2b614d88b": { - "balance": "111000000000000000000" - }, - "599728a78618d1a17b9e34e0fed8e857d5c40622": { - "balance": "14000000000000000000000" - }, - "6ac4d4be2db0d99da3faaaf7525af282051d6a90": { - "balance": "80185000000000000000" - }, - "785c8ea774d73044a734fa790a1b1e743e77ed7c": { - "balance": "238750000000000000000" - }, - "ff2726294148b86c78a9372497e459898ed3fee3": { - "balance": "1970000000000000000000" - }, - "68a86c402388fddc59028fec7021e98cbf830eac": { - "balance": "19100000000000000000" - }, - "6121af398a5b2da69f65c6381aec88ce9cc6441f": { - "balance": "640000000000000000000" - }, - "5a6686b0f17e07edfc59b759c77d5bef164d3879": { - "balance": "1490000000000000000000" - }, - "a2d38de1c73906f6a7ca6efeb97cf6f69cc421be": { - "balance": "1000000000000000000000" - }, - "ae3f98a443efe00f3e711d525d9894dc9a61157b": { - "balance": "295500000000000000000" - }, - "5f1c8a04c90d735b8a152909aeae636fb0ce1665": { - "balance": "6999974000000000000000" - }, - "d687cec0059087fdc713d4d2d65e77daefedc15f": { - "balance": "60000000000000000000" - }, - "845203750f7148a9aa262921e86d43bf641974fd": { - "balance": "100000000000000000000" - }, - "64464a6805b462412a901d2db8174b06c22deea6": { - "balance": "475600000000000000000" - }, - "053471cd9a41925b3904a5a8ffca3659e034be23": { - "balance": "199600000000000000000" - }, - "911ff233e1a211c0172c92b46cf997030582c83a": { - "balance": "1970000000000000000000" - }, - "d930b27a78876485d0f48b70dd5336549679ca8f": { - "balance": "40000000000000000000" - }, - "6ba9b21b35106be159d1c1c2657ac56cd29ffd44": { - "balance": "4480000000000000000000" - }, - "ebac2b4408ef5431a13b8508e86250982114e145": { - "balance": "4000000000000000000000" - }, - "931df34d1225bcd4224e63680d5c4c09bce735a6": { - "balance": "68000000000000000000" - }, - "23eb6fd85671a9063ab7678ebe265a20f61a02b3": { - "balance": "2000000000000000000000" - }, - "b32af3d3e8d075344926546f2e32887bf93b16bd": { - "balance": "200000000000000000000" - }, - "8261fa230c901d43ff579f4780d399f31e6076bc": { - "balance": "2000000000000000000000" - }, - "84a74ceecff65cb93b2f949d773ef1ad7fb4a245": { - "balance": "92998000000000000000" - }, - "da982e9643ffece723075a40fe776e5ace04b29b": { - "balance": "160884000000000000000" - }, - "ba70e8b4759c0c3c82cc00ac4e9a94dd5bafb2b8": { - "balance": "890342000000000000000" - }, - "82f2e991fd324c5f5d17768e9f61335db6319d6c": { - "balance": "500000000000000000000" - }, - "3e84b35c5b2265507061d30b6f12da033fe6f8b9": { - "balance": "1790000000000000000000" - }, - "2895e80999d406ad592e2b262737d35f7db4b699": { - "balance": "1940000000000000000000" - }, - "65f534346d2ffb787fa9cf185d745ba42986bd6e": { - "balance": "500000000000000000000" - }, - "c7368b9709a5c1b51c0adf187a65df14e12b7dba": { - "balance": "9489681000000000000000" - }, - "ba176dbe3249e345cd4fa967c0ed13b24c47e586": { - "balance": "399990000000000000000" - }, - "cff6a6fe3e9a922a12f21faa038156918c4fcb9c": { - "balance": "78800000000000000000" - }, - "bcbd31252ec288f91e298cd812c92160e738331a": { - "balance": "1975802000000000000000" - }, - "5543dd6d169eec8a213bbf7a8af9ffd15d4ff759": { - "balance": "18200000000000000000" - }, - "b65bd780c7434115162027565223f44e5498ff8c": { - "balance": "19999800000000000000000" - }, - "4cadf573ce4ceec78b8e1b21b0ed78eb113b2c0e": { - "balance": "2000000000000000000000" - }, - "04aafc8ae5ce6f4903c89d7fac9cb19512224777": { - "balance": "500000000000000000000" - }, - "fdc4d4765a942f5bf96931a9e8cc7ab8b757ff4c": { - "balance": "87000000000000000000000" - }, - "38c7851f5ffd4cee98df30f3b25597af8a6ca263": { - "balance": "2631920000000000000000" - }, - "0e320219838e859b2f9f18b72e3d4073ca50b37d": { - "balance": "2000000000000000000000" - }, - "bbbf39b1b67995a42241504f9703d2a14a515696": { - "balance": "1580000000000000000000" - }, - "5b800bfd1b3ed4a57d875aed26d42f1a7708d72a": { - "balance": "6392000000000000000000" - }, - "5b85e60e2af0544f2f01c64e2032900ebd38a3c7": { - "balance": "2000000000000000000000" - }, - "c9ac01c3fb0929033f0ccc7e1acfeaaba7945d47": { - "balance": "12459235000000000000000" - }, - "f355d3ec0cfb907d8dbb1bf3464e458128190bac": { - "balance": "4925600000000000000000" - }, - "69c08d744754de709ce96e15ae0d1d395b3a2263": { - "balance": "1000000000000000000000" - }, - "cef77451dfa2c643e00b156d6c6ff84e2373eb66": { - "balance": "188000000000000000000" - }, - "f3034367f87d24d3077fa9a2e38a8b0ccb1104ef": { - "balance": "1000000000000000000000" - }, - "73473e72115110d0c3f11708f86e77be2bb0983c": { - "balance": "20000000000000000000" - }, - "761e6caec189c230a162ec006530193e67cf9d19": { - "balance": "2000000000000000000000" - }, - "e9caf827be9d607915b365c83f0d3b7ea8c79b50": { - "balance": "3000000000000000000000" - }, - "eda4b2fa59d684b27a810df8978a73df308a63c2": { - "balance": "4000000000000000000000" - }, - "065ff575fd9c16d3cb6fd68ffc8f483fc32ec835": { - "balance": "200000000000000000000" - }, - "a72ee666c4b35e82a506808b443cebd5c632c7dd": { - "balance": "800000000000000000000" - }, - "5b30608c678e1ac464a8994c3b33e5cdf3497112": { - "balance": "400000000000000000000" - }, - "b0c7ce4c0dc3c2bbb99cc1857b8a455f611711ce": { - "balance": "4000000000000000000000" - }, - "d7274d50804d9c77da93fa480156efe57ba501de": { - "balance": "1940000000000000000000" - }, - "a609c26dd350c235e44b2b9c1dddccd0a9d9f837": { - "balance": "1000000000000000000000" - }, - "bddfa34d0ebf1b04af53b99b82494a9e3d8aa100": { - "balance": "12000000000000000000000" - }, - "fd40242bb34a70855ef0fd90f3802dec2136b327": { - "balance": "1930600000000000000000" - }, - "58aed6674affd9f64233272a578dd9386b99c263": { - "balance": "3400000000000000000000" - }, - "24434a3e32e54ecf272fe3470b5f6f512f675520": { - "balance": "5910000000000000000000" - }, - "a379a5070c503d2fac89b8b3afa080fd45ed4bec": { - "balance": "19700000000000000000000" - }, - "37e169a93808d8035698f815c7235613c1e659f2": { - "balance": "1000000000000000000000" - }, - "849b116f596301c5d8bb62e0e97a8248126e39f3": { - "balance": "300000000000000000000" - }, - "fe7011b698bf3371132d7445b19eb5b094356aee": { - "balance": "2000000000000000000000" - }, - "f16de1891d8196461395f9b136265b3b9546f6ef": { - "balance": "31313000000000000000" - }, - "6c6564e5c9c24eaaa744c9c7c968c9e2c9f1fbae": { - "balance": "1357800000000000000000" - }, - "8bb0212f3295e029cab1d961b04133a1809e7b91": { - "balance": "2000000000000000000000" - }, - "408a69a40715e1b313e1354e600800a1e6dc02a5": { - "balance": "35144000000000000000" - }, - "ddf0cce1fe996d917635f00712f4052091dff9ea": { - "balance": "2000000000000000000000" - }, - "50fef296955588caae74c62ec32a23a454e09ab8": { - "balance": "1201200000000000000000" - }, - "d913f0771949753c4726acaa2bd3619c5c20ff77": { - "balance": "3000000000000000000000" - }, - "9d6ecfa03af2c6e144b7c4692a86951e902e9e1f": { - "balance": "3000310000000000000000" - }, - "ecbe5e1c9ad2b1dccf0a305fc9522f4669dd3ae7": { - "balance": "5000000000000000000000" - }, - "33e9b71823952e1f66958c278fc28b1196a6c5a4": { - "balance": "100000000000000000000" - }, - "9de20bc37e7f48a80ffd7ad84ffbf1a1abe1738c": { - "balance": "200000000000000000000" - }, - "16f313cf8ad000914a0a176dc6a4342b79ec2538": { - "balance": "2000000000000000000000" - }, - "991ac7ca7097115f26205eee0ef7d41eb4e311ae": { - "balance": "20000000000000000000" - }, - "ddfafdbc7c90f1320e54b98f374617fbd01d109f": { - "balance": "13370000000000000000" - }, - "26b11d066588ce74a572a85a6328739212aa8b40": { - "balance": "2000000000000000000000" - }, - "ef2c34bb487d3762c3cca782ccdd7a8fbb0a9931": { - "balance": "180000000000000000000" - }, - "a9be88ad1e518b0bbb024ab1d8f0e73f790e0c76": { - "balance": "2800000000000000000000" - }, - "4a7494cce44855cc80582842be958a0d1c0072ee": { - "balance": "2400000000000000000000" - }, - "23569542c97d566018c907acfcf391d14067e87e": { - "balance": "2000000000000000000000" - }, - "d252960b0bf6b2848fdead80136db5f507f8be02": { - "balance": "2000000000000000000000" - }, - "2c0f5b9df43625798e7e03c1a5fd6a6d091af82b": { - "balance": "31200000000000000000" - }, - "a7c9d388ebd873e66b1713448397d0f37f8bd3a8": { - "balance": "5000000000000000000000" - }, - "3259bd2fddfbbc6fbad3b6e874f0bbc02cda18b5": { - "balance": "11886645000000000000000" - }, - "f287ff52f461117adb3e1daa71932d1493c65f2e": { - "balance": "3640000000000000000000" - }, - "c852428d2b586497acd30c56aa13fb5582f84402": { - "balance": "945600000000000000000" - }, - "296f00de1dc3bb01d47a8ccd1e5d1dd9a1eb7791": { - "balance": "1000000000000000000000" - }, - "817493cd9bc623702a24a56f9f82e3fd48f3cd31": { - "balance": "2920000000000000000000" - }, - "7adfedb06d91f3cc7390450b85550270883c7bb7": { - "balance": "322312000000000000000" - }, - "8d544c32c07fd0842c761d53a897d6c950bb7599": { - "balance": "200000000000000000000" - }, - "86297d730fe0f7a9ee24e08fb1087b31adb306a7": { - "balance": "2000000000000000000000" - }, - "f64fe0939a8d1eea2a0ecd9a9730fd7958e33109": { - "balance": "20600000000000000000" - }, - "b06eab09a610c6a53d56a946b2c43487ac1d5b2d": { - "balance": "1000000000000000000000" - }, - "bae9b82f7299631408659dd74e891cb8f3860fe5": { - "balance": "1970000000000000000000" - }, - "0eda80f4ed074aea697aeddf283b63dbca3dc4da": { - "balance": "2000000000000000000000" - }, - "ea686c5057093c171c66db99e01b0ececb308683": { - "balance": "384907000000000000000" - }, - "425725c0f08f0811f5f006eec91c5c5c126b12ae": { - "balance": "150000000000000000000" - }, - "b18e67a5050a1dc9fb190919a33da838ef445014": { - "balance": "20000000000000000000" - }, - "8dd484ff8a307364eb66c525a571aac701c5c318": { - "balance": "4000000000000000000000" - }, - "6671b182c9f741a0cd3c356c73c23126d4f9e6f4": { - "balance": "200000000000000000000" - }, - "ba0249e01d945bef93ee5ec61925e03c5ca509fd": { - "balance": "4000000000000000000000" - }, - "b2968f7d35f208871631c6687b3f3daeabc6616c": { - "balance": "156060000000000000000" - }, - "a6f62b8a3d7f11220701ab9ffffcb327959a2785": { - "balance": "506000000000000000000" - }, - "c885a18aabf4541b7b7b7ecd30f6fae6869d9569": { - "balance": "2000000000000000000000" - }, - "33fb577a4d214fe010d32cca7c3eeda63f87ceef": { - "balance": "1000000000000000000000" - }, - "be86d0b0438419ceb1a038319237ba5206d72e46": { - "balance": "999942000000000000000" - }, - "466292f0e80d43a78774277590a9eb45961214f4": { - "balance": "970000000000000000000" - }, - "b33c0323fbf9c26c1d8ac44ef74391d0804696da": { - "balance": "20000000000000000000" - }, - "f7bc4c44910d5aedd66ed2355538a6b193c361ec": { - "balance": "96980000000000000000" - }, - "d0f04f52109aebec9a7b1e9332761e9fe2b97bb5": { - "balance": "4000000000000000000000" - }, - "cb4a914d2bb029f32e5fef5c234c4fec2d2dd577": { - "balance": "1800000000000000000000" - }, - "2e619f57abc1e987aa936ae3a2264962e7eb2d9a": { - "balance": "756000000000000000000" - }, - "166bf6dab22d841b486c38e7ba6ab33a1487ed8c": { - "balance": "20000000000000000000000" - }, - "c3a046e3d2b2bf681488826e32d9c061518cfe8c": { - "balance": "2600000000000000000000" - }, - "d082275f745a2cac0276fbdb02d4b2a3ab1711fe": { - "balance": "30000000000000000000" - }, - "a701df79f594901afe1444485e6b20c3bda2b9b3": { - "balance": "1000000000000000000000" - }, - "dec3eec2640a752c466e2b7e7ee685afe9ac41f4": { - "balance": "1324245000000000000000" - }, - "8134dd1c9df0d6c8a5812426bb55c761ca831f08": { - "balance": "122360000000000000000" - }, - "bfc57aa666fae28e9f107a49cb5089a4e22151dd": { - "balance": "1000000000000000000000" - }, - "c3c2297329a6fd99117e54fc6af379b4d556547e": { - "balance": "6000000000000000000000" - }, - "40585200683a403901372912a89834aadcb55fdb": { - "balance": "2000000000000000000000" - }, - "cd49bf185e70d04507999f92a4de4455312827d0": { - "balance": "1000000000000000000000" - }, - "9c6bc9a46b03ae5404f043dfcf21883e4110cc33": { - "balance": "200000000000000000000" - }, - "1f49b86d0d3945590698a6aaf1673c37755ca80d": { - "balance": "700000000000000000000" - }, - "efeb1997aad277cc33430e6111ed0943594048b8": { - "balance": "2000000000000000000000" - }, - "7c0883054c2d02bc7a852b1f86c42777d0d5c856": { - "balance": "500000000000000000000" - }, - "ff49a775814ec00051a795a875de24592ea400d4": { - "balance": "200000000000000000000000" - }, - "f039683d7b3d225bc7d8dfadef63163441be41e2": { - "balance": "34380000000000000000" - }, - "a3ba0d3a3617b1e31b4e422ce269e873828d5d69": { - "balance": "850000000000000000000" - }, - "d116f3dcd5db744bd008887687aa0ec9fd7292aa": { - "balance": "1000000000000000000000" - }, - "5719f49b720da68856f4b9e708f25645bdbc4b41": { - "balance": "640000000000000000000" - }, - "870796abc0db84af82da52a0ed68734de7e636f5": { - "balance": "300000000000000000000" - }, - "68b6854788a7c6496cdbf5f84b9ec5ef392b78bb": { - "balance": "19700000000000000000000" - }, - "8c2fbeee8eacc5c5d77c16abd462ee9c8145f34b": { - "balance": "1940000000000000000000" - }, - "421684baa9c0b4b5f55338e6f6e7c8e146d41cb7": { - "balance": "1500000000000000000000" - }, - "dd26b429fd43d84ec179825324bad5bfb916b360": { - "balance": "5142000000000000000000" - }, - "3821862493242c0aeb84b90de05d250c1e50c074": { - "balance": "322200000000000000000" - }, - "68a7425fe09eb28cf86eb1793e41b211e57bd68d": { - "balance": "668500000000000000000" - }, - "da875e4e2f3cabe4f37e0eaed7d1f6dcc6ffef43": { - "balance": "2000000000000000000000" - }, - "c2663f8145dbfec6c646fc5c49961345de1c9f11": { - "balance": "690000000000000000000" - }, - "e89c22f1a4e1d4746ecfaa59ed386fee12d51e37": { - "balance": "44932000000000000000" - }, - "eff86b5123bcdc17ed4ce8e05b7e12e51393a1f7": { - "balance": "500000000000000000000" - }, - "6c3d18704126aa99ee3342ce60f5d4c85f1867cd": { - "balance": "50000000000000000000" - }, - "b8d531a964bcea13829620c0ced72422dadb4cca": { - "balance": "169990000000000000000" - }, - "7c29d47d57a733f56b9b217063b513dc3b315923": { - "balance": "4000000000000000000000" - }, - "bc1e80c181616342ebb3fb3992072f1b28b802c6": { - "balance": "4000000000000000000000" - }, - "31313ffd635bf2f3324841a88c07ed146144ceeb": { - "balance": "1970000000000000000000" - }, - "cc4feb72df98ff35a138e01761d1203f9b7edf0a": { - "balance": "7000000000000000000000" - }, - "741693c30376508513082020cc2b63e9fa92131b": { - "balance": "1200000000000000000000" - }, - "aa3135cb54f102cbefe09e96103a1a796718ff54": { - "balance": "57800000000000000000" - }, - "ef61155ba009dcdebef10b28d9da3d1bc6c9ced4": { - "balance": "59100000000000000000" - }, - "b3c94811e7175b148b281c1a845bfc9bb6fbc115": { - "balance": "200000000000000000000" - }, - "96d9cca8f55eea0040ec6eb348a1774b95d93ef4": { - "balance": "4000000000000000000000" - }, - "ce62125adec3370ac52110953a4e760be9451e3b": { - "balance": "152000000000000000000" - }, - "aca1e6bc64cc3180f620e94dc5b1bcfd8158e45d": { - "balance": "2000000000000000000000" - }, - "bc237148d30c13836ffa2cad520ee4d2e5c4eeff": { - "balance": "1970000000000000000000" - }, - "0e024e7f029c6aaf3a8b910f5e080873b85795aa": { - "balance": "1000000000000000000000" - }, - "7283cd4675da58c496556151dafd80c7f995d318": { - "balance": "760000000000000000000" - }, - "39b299327490d72f9a9edff11b83afd0e9d3c450": { - "balance": "200000000000000000000" - }, - "5f333a3b2310765a0d1832b9be4c0a03704c1c09": { - "balance": "1000000000000000000000" - }, - "5aaf1c31254a6e005fba7f5ab0ec79d7fc2b630e": { - "balance": "5910000000000000000000" - }, - "833db42c14163c7be4cab86ac593e06266d699d5": { - "balance": "174212000000000000000000" - }, - "f32d25eb0ea2b8b3028a4c7a155dc1aae865784d": { - "balance": "5710684000000000000000" - }, - "1fa2319fed8c2d462adf2e17feec6a6f30516e95": { - "balance": "125300000000000000000" - }, - "c49cfaa967f3afbf55031061fc4cef88f85da584": { - "balance": "2000000000000000000000" - }, - "43db7ff95a086d28ebbfb82fb8fb5f230a5ebccd": { - "balance": "16100000000000000000" - }, - "cf3f9128b07203a3e10d7d5755c0c4abc6e2cac2": { - "balance": "5000000000000000000000" - }, - "8f4d1e7e4561284a34fef9673c0d34e12af4aa03": { - "balance": "2000000000000000000000" - }, - "934af21b7ebfa467e2ced65aa34edd3a0ec71332": { - "balance": "35420000000000000000000" - }, - "5d231a70c1dfeb360abd97f616e2d10d39f3cab5": { - "balance": "400000000000000000000" - }, - "2d5d7335acb0362b47dfa3a8a4d3f5949544d380": { - "balance": "200000000000000000000" - }, - "d1e1f2b9c16c309874dee7fac32675aff129c398": { - "balance": "72800000000000000000" - }, - "a43b6da6cb7aac571dff27f09d39f846f53769b1": { - "balance": "380000000000000000000" - }, - "779274bf1803a336e4d3b00ddd93f2d4f5f4a62e": { - "balance": "1000000000000000000000" - }, - "a644ed922cc237a3e5c4979a995477f36e50bc62": { - "balance": "583900000000000000000" - }, - "ee6c03429969ca1262cb3f0a4a54afa7d348d7f5": { - "balance": "256100000000000000000" - }, - "4f06246b8d4bd29661f43e93762201d286935ab1": { - "balance": "4818730000000000000000" - }, - "e04972a83ca4112bc871c72d4ae1616c2f0728db": { - "balance": "267606000000000000000" - }, - "df098f5e4e3dffa51af237bda8652c4f73ed9ca6": { - "balance": "502000000000000000000" - }, - "dfded2574b27d1613a7d98b715159b0d00baab28": { - "balance": "20000000000000000000000" - }, - "17d931d4c56294dcbe77c8655be4695f006d4a3c": { - "balance": "2000000000000000000000" - }, - "3ccb71aa6880cb0b84012d90e60740ec06acd78f": { - "balance": "2000000000000000000000" - }, - "e57d2995b0ebdf3f3ca6c015eb04260dbb98b7c6": { - "balance": "2000000000000000000000" - }, - "fb3860f4121c432ebdc8ec6a0331b1b709792e90": { - "balance": "600400000000000000000" - }, - "fa00c376e89c05e887817a9dd0748d96f341aa89": { - "balance": "300700000000000000000" - }, - "c7a018f0968a51d1f6603c5c49dc545bcb0ff293": { - "balance": "4000000000000000000000" - }, - "7d73863038ccca22f96affda10496e51e1e6cd48": { - "balance": "20000000000000000000" - }, - "38ea6f5b5a7b88417551b4123dc127dfe9342da6": { - "balance": "400000000000000000000" - }, - "014b7f67b14f5d983d87014f570c8b993b9872b5": { - "balance": "200000000000000000000" - }, - "8ac89bd9b8301e6b0677fa25fcf0f58f0cc7b611": { - "balance": "20000000000000000000" - }, - "7eb4b0185c92b6439a08e7322168cb353c8a774a": { - "balance": "10165988000000000000000" - }, - "d29dc08efbb3d72e263f78ab7610d0226de76b00": { - "balance": "12000000000000000000000" - }, - "72a8260826294726a75bf39cd9aa9e07a3ea14cd": { - "balance": "2000000000000000000000" - }, - "4cb5c6cd713ca447b848ae2f56b761ca14d7ad57": { - "balance": "267400000000000000000" - }, - "49185dd7c23632f46c759473ebae966008cd3598": { - "balance": "254030000000000000000" - }, - "13d67a7e25f2b12cdb85585009f8acc49b967301": { - "balance": "1999944000000000000000" - }, - "9d913b5d339c95d87745562563fea98b23c60cc4": { - "balance": "170718000000000000000" - }, - "abdc9f1bcf4d19ee96591030e772c334302f7d83": { - "balance": "40110000000000000000000" - }, - "e9a5ae3c9e05977dd1069e9fd9d3aefbae04b8df": { - "balance": "1970000000000000000000" - }, - "1fd296be03ad737c92f9c6869e8d80a71c5714aa": { - "balance": "13370000000000000000" - }, - "2f13657526b177cad547c3908c840eff647b45d9": { - "balance": "1170685000000000000000" - }, - "e69fcc26ed225f7b2e379834c524d70c1735e5bc": { - "balance": "2000000000000000000000" - }, - "bade43599e02f84f4c3014571c976b13a36c65ab": { - "balance": "4000000000000000000000" - }, - "184a4f0beb71ffd558a6b6e8f228b78796c4cf3e": { - "balance": "12000000000000000000000" - }, - "d1de5aad3a5fd803f1b1aeb6103cb8e14fe723b7": { - "balance": "20000000000000000000" - }, - "0bd67dbde07a856ebd893b5edc4f3a5be4202616": { - "balance": "2000000000000000000000" - }, - "6b30f1823910b86d3acb5a6afc9defb6f3a30bf8": { - "balance": "4200000000000000000000" - }, - "9a63d185a79129fdab19b58bb631ea36a420544e": { - "balance": "42000000000000000000" - }, - "df660a91dab9f730f6190d50c8390561500756ca": { - "balance": "2000000000000000000000" - }, - "a1a1f0fa6d20b50a794f02ef52085c9d036aa6ca": { - "balance": "1000000000000000000000" - }, - "4ec768295eeabafc42958415e22be216cde77618": { - "balance": "59600000000000000000" - }, - "c348fc5a461323b57be303cb89361b991913df28": { - "balance": "100000000000000000000000" - }, - "3a7db224acae17de7798797d82cdf8253017dfa8": { - "balance": "5000000000000000000000" - }, - "8bea40379347a5c891d59a6363315640f5a7e07a": { - "balance": "1999992000000000000000" - }, - "2257fca16a6e5c2a647c3c29f36ce229ab93b17e": { - "balance": "4000000000000000000000" - }, - "e492818aa684e5a676561b725d42f3cc56ae5198": { - "balance": "800000000000000000000" - }, - "c841884fa4785fb773b28e9715fae99a5134305d": { - "balance": "2000000000000000000000" - }, - "0d9443a79468a5bbf7c13c6e225d1de91aee07df": { - "balance": "70000000000000000000" - }, - "6d4008b4a888a826f248ee6a0b0dfde9f93210b9": { - "balance": "5460000000000000000000" - }, - "884980eb4565c1048317a8f47fdbb461965be481": { - "balance": "3999922000000000000000" - }, - "985d70d207892bed398590024e2421b1cc119359": { - "balance": "20000000000000000000000" - }, - "d9ec8fe69b7716c0865af888a11b2b12f720ed33": { - "balance": "4000000000000000000000" - }, - "49b74e169265f01a89ec4c9072c5a4cd72e4e835": { - "balance": "16100000000000000000000" - }, - "4c3e95cc3957d252ce0bf0c87d5b4f2234672e70": { - "balance": "2500000000000000000000" - }, - "d9ff115d01266c9f73b063c1c238ef3565e63b36": { - "balance": "680000000000000000000" - }, - "48c5c6970b9161bb1c7b7adfed9cdede8a1ba864": { - "balance": "4000000000000000000000" - }, - "ea6afe2cc928ac8391eb1e165fc40040e37421e7": { - "balance": "2997569000000000000000" - }, - "08ccda50e4b26a0ffc0ef92e9205310706bec2c7": { - "balance": "6077440000000000000000" - }, - "e6e9a39d750fe994394eb68286e5ea62a6997882": { - "balance": "600000000000000000000" - }, - "4b58101f44f7e389e12d471d1635b71614fdd605": { - "balance": "160000000000000000000" - }, - "8d93dac785f88f1a84bf927d53652b45a154ccdd": { - "balance": "158000000000000000000" - }, - "415d096ab06293183f3c033d25f6cf7178ac3bc7": { - "balance": "40000000000000000000" - }, - "c3e387b03ce95ccfd7fa51dd840183bc43532809": { - "balance": "2000000000000000000000" - }, - "da34b2eae30bafe8daeccde819a794cd89e09549": { - "balance": "2000000000000000000000" - }, - "fa279bfd8767f956bf7fa0bd5660168da75686bd": { - "balance": "2674000000000000000000" - }, - "b98ca31785ef06be49a1e47e864f60d076ca472e": { - "balance": "4000000000000000000000" - }, - "b768b5234eba3a9968b34d6ddb481c8419b3655d": { - "balance": "14974000000000000000" - }, - "31047d703f63b93424fbbd6e2f1f9e74de13e709": { - "balance": "2850123000000000000000" - }, - "9a24ce8d485cc4c86e49deb39022f92c7430e67e": { - "balance": "1300000000000000000000" - }, - "e62f9d7c64e8e2635aeb883dd73ba684ee7c1079": { - "balance": "8000000000000000000000" - }, - "f15d9d5a21b1929e790371a17f16d95f0c69655c": { - "balance": "2000000000000000000000" - }, - "285ae51b9500c58d541365d97569f14bb2a3709b": { - "balance": "2000000000000000000000" - }, - "09c177f1ae442411ddacf187d46db956148360e7": { - "balance": "8950000000000000000000" - }, - "12173074980153aeaa4b0dcbc7132eadcec21b64": { - "balance": "240000000000000000000" - }, - "351f16e5e0735af56751b0e225b2421171394090": { - "balance": "13370000000000000000000" - }, - "ac52b77e15664814f39e4f271be641308d91d6cc": { - "balance": "220000000000000000000" - }, - "99c883258546cc7e4e971f522e389918da5ea63a": { - "balance": "4000000000000000000000" - }, - "aa16269aac9c0d803068d82fc79151dadd334b66": { - "balance": "4000000000000000000000" - }, - "7c9a110cb11f2598b2b20e2ca400325e41e9db33": { - "balance": "26000000000000000000000" - }, - "583e83ba55e67e13e0e76f8392d873cd21fbf798": { - "balance": "20000000000000000000" - }, - "555ebe84daa42ba256ea789105cec4b693f12f18": { - "balance": "100000000000000000000" - }, - "978c430ce4359b06bc2cdf5c2985fc950e50d5c8": { - "balance": "480000000000000000000" - }, - "dc1eb9b6e64351f56424509645f83e79eee76cf4": { - "balance": "4000000000000000000000" - }, - "5b290c01967c812e4dc4c90b174c1b4015bae71e": { - "balance": "149946000000000000000" - }, - "e7d213947fcb904ad738480b1eed2f5c329f27e8": { - "balance": "18718000000000000000" - }, - "c517d0315c878813c717e18cafa1eab2654e01da": { - "balance": "10000000000000000000000" - }, - "7e972a8a7c2a44c93b21436c38d21b9252c345fe": { - "balance": "1790000000000000000000" - }, - "9cb28ac1a20a106f7f373692c5ce4c73f13732a1": { - "balance": "1000000000000000000000" - }, - "14ab164b3b524c82d6abfbc0de831126ae8d1375": { - "balance": "2000000000000000000000" - }, - "d46f8223452982a1eea019a8816efc2d6fc00768": { - "balance": "137000000000000000000" - }, - "5cdc4708f14f40dcc15a795f7dc8cb0b7faa9e6e": { - "balance": "537000000000000000000" - }, - "66fdc9fee351fa1538eb0d87d819fcf09e7c106a": { - "balance": "6016500000000000000000" - }, - "e7be82c6593c1eeddd2ae0b15001ff201ab57b2f": { - "balance": "19100000000000000000" - }, - "47d20e6ae4cad3f829eac07e5ac97b66fdd56cf5": { - "balance": "1000000000000000000000" - }, - "0f2d8daf04b5414a0261f549ff6477b80f2f1d07": { - "balance": "200000000000000000000000" - }, - "84bfcef0491a0ae0694b37ceac024584f2aa0467": { - "balance": "1999944000000000000000" - }, - "ec5feafe210c12bfc9a5d05925a123f1e73fbef8": { - "balance": "456000000000000000000000" - }, - "7023c70956e04a92d70025aad297b539af355869": { - "balance": "2000000000000000000000" - }, - "d66ddf1159cf22fd8c7a4bc8d5807756d433c43e": { - "balance": "2200000000000000000000" - }, - "d0638ea57189a6a699024ad78c71d939c1c2ff8c": { - "balance": "2632000000000000000000" - }, - "70d25ed2c8ada59c088cf70dd22bf2db93acc18a": { - "balance": "1056600000000000000000" - }, - "a4875928458ec2005dbb578c5cd33580f0cf1452": { - "balance": "1000000000000000000000" - }, - "b5ad5157dda921e6bafacd9086ae73ae1f611d3f": { - "balance": "2000000000000000000000" - }, - "c493489e56c3bdd829007dc2f956412906f76bfa": { - "balance": "48968000000000000000" - }, - "c57612de91110c482e6f505bcd23f3c5047d1d61": { - "balance": "3580000000000000000000" - }, - "9b18478655a4851cc906e660feac61f7f4c8bffc": { - "balance": "4174120000000000000000" - }, - "b21b7979bf7c5ca01fa82dd640b41c39e6c6bc75": { - "balance": "1999944000000000000000" - }, - "a9d4a2bcbe5b9e0869d70f0fe2e1d6aacd45edc5": { - "balance": "198800000000000000000" - }, - "6f29bb375be5ed34ed999bb830ee2957dde76d16": { - "balance": "2000000000000000000000" - }, - "a006268446643ec5e81e7acb3f17f1c351ee2ed9": { - "balance": "4000000000000000000000" - }, - "42ddd014dc52bfbcc555325a40b516f4866a1dd3": { - "balance": "2000000000000000000000" - }, - "d6d6776958ee23143a81adadeb08382009e996c2": { - "balance": "3000000000000000000000" - }, - "d34e03d36a2bd4d19a5fa16218d1d61e3ffa0b15": { - "balance": "320000000000000000000" - }, - "dac0c177f11c5c3e3e78f2efd663d13221488574": { - "balance": "1000000000000000000000" - }, - "814135da8f9811075783bf1ab67062af8d3e9f40": { - "balance": "20000000000000000000" - }, - "7c3eb713c4c9e0381cd8154c7c9a7db8645cde17": { - "balance": "200000000000000000000" - }, - "f49c47b3efd86b6e6a5bc9418d1f9fec814b69ef": { - "balance": "20000000000000000000000" - }, - "35f1da127b83376f1b88c82a3359f67a5e67dd50": { - "balance": "1910000000000000000000" - }, - "44dfba50b829becc5f4f14d1b04aab3320a295e5": { - "balance": "1000000000000000000000" - }, - "0b924df007e9c0878417cfe63b976ea1a382a897": { - "balance": "40000000000000000000" - }, - "82438fd2b32a9bdd674b49d8cc5fa2eff9781847": { - "balance": "20000000000000000000" - }, - "794529d09d017271359730027075b87ad83dae6e": { - "balance": "310000000000000000000" - }, - "f4b49100757772f33c177b9a76ba95226c8f3dd8": { - "balance": "6700000000000000000000" - }, - "8563c49361b625e768771c96151dbfbd1c906976": { - "balance": "2000000000000000000000" - }, - "0b9df80fbe232009dacf0aa8cac59376e2476203": { - "balance": "2000000000000000000000" - }, - "149b6dbde632c19f5af47cb493114bebd9b03c1f": { - "balance": "12000000000000000000000" - }, - "d7a1431ee453d1e49a0550d1256879b4f5d10201": { - "balance": "1670000000000000000000" - }, - "1d37616b793f94911838ac8e19ee9449df921ec4": { - "balance": "1500000000000000000000" - }, - "d6670c036df754be43dadd8f50feea289d061fd6": { - "balance": "5988459000000000000000" - }, - "02778e390fa17510a3428af2870c4273547d386c": { - "balance": "16163700000000000000000" - }, - "b89f4632df5909e58b2a9964f74feb9a3b01e0c5": { - "balance": "21406707000000000000000" - }, - "76c27535bcb59ce1fa2d8c919cabeb4a6bba01d1": { - "balance": "2000000000000000000000" - }, - "36bf43ff35df90908824336c9b31ce33067e2f50": { - "balance": "346837200000000000000000" - }, - "b53bcb174c2518348b818aece020364596466ba3": { - "balance": "2000000000000000000000" - }, - "b4dd460cd016725a64b22ea4f8e06e06674e033e": { - "balance": "5370000000000000000000" - }, - "cda1741109c0265b3fb2bf8d5ec9c2b8a3346b63": { - "balance": "20000000000000000000" - }, - "feb8b8e2af716ae41fc7c04bcf29540156461e6b": { - "balance": "1555396000000000000000" - }, - "a49f523aa51364cbc7d995163d34eb590ded2f08": { - "balance": "2659160000000000000000" - }, - "a7e74f0bdb278ff0a805a648618ec52b166ff1be": { - "balance": "100000000000000000000" - }, - "5ead29037a12896478b1296ab714e9cb95428c81": { - "balance": "71500000000000000000" - }, - "cdecf5675433cdb0c2e55a68db5d8bbe78419dd2": { - "balance": "20000000000000000000" - }, - "c24ccebc2344cce56417fb684cf81613f0f4b9bd": { - "balance": "1550000000000000000000" - }, - "5a70106f20d63f875265e48e0d35f00e17d02bc9": { - "balance": "20000000000000000000" - }, - "2606c3b3b4ca1b091498602cb1978bf3b95221c0": { - "balance": "400000000000000000000" - }, - "1ad4563ea5786be1159935abb0f1d5879c3e7372": { - "balance": "6000000000000000000000" - }, - "b782bfd1e2de70f467646f9bc09ea5b1fcf450af": { - "balance": "267400000000000000000" - }, - "649a2b9879cd8fb736e6703b0c7747849796f10f": { - "balance": "7358102000000000000000" - }, - "1cc1d3c14f0fb8640e36724dc43229d2ea7a1e48": { - "balance": "1700000000000000000000" - }, - "824b3c3c443e19295d7ef6faa7f374a4798486a8": { - "balance": "20000000000000000000" - }, - "a7758cecb60e8f614cce96137ef72b4fbd07774a": { - "balance": "500000000000000000000" - }, - "981f712775c0dad97518ffedcb47b9ad1d6c2762": { - "balance": "6685000000000000000000" - }, - "26e801b62c827191dd68d31a011990947fd0ebe0": { - "balance": "20000000000000000000" - }, - "95447046313b2f3a5e19b948fd3b8bedc82c717c": { - "balance": "500000000000000000000" - }, - "0b701101a4109f9cb360dc57b77442673d5e5983": { - "balance": "2000000000000000000000" - }, - "5b25cae86dcafa2a60e7723631fc5fa49c1ad87d": { - "balance": "2491200000000000000000" - }, - "f73ac46c203be1538111b151ec8220c786d84144": { - "balance": "294515000000000000000" - }, - "e8c3d3b0e17f97d1e756e684f94e1470f99c95a1": { - "balance": "400000000000000000000" - }, - "8c900a8236b08c2b65405d39d75f20062a7561fd": { - "balance": "1640000000000000000000" - }, - "43898c49a34d509bfed4f76041ee91caf3aa6aa5": { - "balance": "300000000000000000000" - }, - "c85325eab2a59b3ed863c86a5f2906a04229ffa9": { - "balance": "465600000000000000000" - }, - "4a430170152de5172633dd8262d107a0afd96a0f": { - "balance": "3160000000000000000000" - }, - "6e0ee70612c976287d499ddfa6c0dcc12c06deea": { - "balance": "129980000000000000000" - }, - "21c07380484f6cbc8724ad32bc864c3b5ad500b7": { - "balance": "1000000000000000000000" - }, - "ff5162f2354dc492c75fd6e3a107268660eecb47": { - "balance": "1700000000000000000000" - }, - "8845e9f90e96336bac3c616be9d88402683e004c": { - "balance": "2000000000000000000000" - }, - "f23c7b0cb8cd59b82bd890644a57daf40c85e278": { - "balance": "50038000000000000000" - }, - "1784948bf99848c89e445638504dd698271b5924": { - "balance": "6037580000000000000000" - }, - "b39f4c00b2630cab7db7295ef43d47d501e17fd7": { - "balance": "4000000000000000000000" - }, - "3fb7d197b3ba4fe045efc23d50a14585f558d9b2": { - "balance": "20000000000000000000" - }, - "bd043b67c63e60f841ccca15b129cdfe6590c8e3": { - "balance": "200000000000000000000" - }, - "86ca0145957e6b0dfe36875fbe7a0dec55e17a28": { - "balance": "10000000000000000000000" - }, - "dae7201eab8c063302930d693929d07f95e71962": { - "balance": "2687370000000000000000" - }, - "cc034985d3f28c2d39b1a34bced4d3b2b6ca234e": { - "balance": "182000000000000000000" - }, - "40e0dbf3efef9084ea1cd7e503f40b3b4a8443f6": { - "balance": "4000000000000000000000" - }, - "b1896a37e5d8825a2d01765ae5de629977de8352": { - "balance": "200000000000000000000" - }, - "d9f547f2c1de0ed98a53d161df57635dd21a00bd": { - "balance": "98500000000000000000" - }, - "2fea1b2f834f02fc54333f8a809f0438e5870aa9": { - "balance": "20200000000000000000" - }, - "68b31836a30a016ada157b638ac15da73f18cfde": { - "balance": "26000000000000000000" - }, - "bc967fe4418c18b99858966d870678dca2b88879": { - "balance": "8740000000000000000000" - }, - "16bae5d24eff91778cd98b4d3a1cc3162f44aa77": { - "balance": "401100000000000000000" - }, - "f476e1267f86247cc908816f2e7ad5388c952db0": { - "balance": "4000000000000000000000" - }, - "0203ae01d4c41cae1865e04b1f5b53cdfaecae31": { - "balance": "1006054000000000000000" - }, - "bd4bd5b122d8ef7b7c8f0667450320db2116142e": { - "balance": "600000000000000000000" - }, - "a394ad4fd9e6530e6f5c53faecbede81cb172da1": { - "balance": "5600000000000000000000" - }, - "3a9960266df6492063538a99f487c950a3a5ec9e": { - "balance": "24000000000000000000000" - }, - "d8069f84b521493f4715037f3226b25f33b60586": { - "balance": "1910000000000000000000" - }, - "136c834bf111326d207395295b2e583ea7f33572": { - "balance": "100000000000000000000" - }, - "c5c73d61cce7c8fe4c8fce29f39092cd193e0fff": { - "balance": "8000000000000000000000" - }, - "3cfbf066565970639e130df2a7d16b0e14d6091c": { - "balance": "1700000000000000000000" - }, - "61b905de663fc17386523b3a28e2f7d037a655cd": { - "balance": "500000000000000000000" - }, - "fda0ce15330707f10bce3201172d2018b9ddea74": { - "balance": "51900000000000000000" - }, - "f7fc45abf76f5088e2e5b5a8d132f28a4d4ec1c0": { - "balance": "2000000000000000000000" - }, - "c3db9fb6f46c480af34465d79753b4e2b74a67ce": { - "balance": "20000000000000000000000" - }, - "ebe46cc3c34c32f5add6c3195bb486c4713eb918": { - "balance": "1000000000000000000000" - }, - "91d2a9ee1a6db20f5317cca7fbe2313895db8ef8": { - "balance": "8499600000000000000000" - }, - "c4cc45a2b63c27c0b4429e58cd42da59be739bd6": { - "balance": "1000000000000000000000" - }, - "a43b81f99356c0af141a03010d77bd042c71c1ee": { - "balance": "2000000000000000000000" - }, - "4c45d4c9a725d11112bfcbca00bf31186ccaadb7": { - "balance": "400000000000000000000" - }, - "bf9f271f7a7e12e36dd2fe9facebf385fe6142bd": { - "balance": "62760000000000000000" - }, - "e0ce80a461b648a501fd0b824690c8868b0e4de8": { - "balance": "500000000000000000000" - }, - "a1f7dde1d738d8cd679ea1ee965bee224be7d04d": { - "balance": "1127000000000000000000" - }, - "7f1c81ee1697fc144b7c0be5493b5615ae7fddca": { - "balance": "500200000000000000000" - }, - "b508f987b2de34ae4cf193de85bff61389621f88": { - "balance": "6000000000000000000000" - }, - "5f26cf34599bc36ea67b9e7a9f9b4330c9d542a3": { - "balance": "1000000000000000000000" - }, - "d02108d2ae3cab10cbcf1657af223e027c8210f6": { - "balance": "2000140000000000000000" - }, - "952183cfd38e352e579d36decec5b18450f7fba0": { - "balance": "2000000000000000000000" - }, - "eb90c793b3539761e1c814a29671148692193eb4": { - "balance": "12000000000000000000000" - }, - "1076212d4f758c8ec7121c1c7d74254926459284": { - "balance": "35000056000000000000000" - }, - "f05ceeab65410564709951773c8445ad9f4ec797": { - "balance": "299982000000000000000" - }, - "05361d8eb6941d4e90fb7e1418a95a32d5257732": { - "balance": "20000000000000000000" - }, - "a5783bf33432ff82ac498985d7d460ae67ec3673": { - "balance": "1820000000000000000000" - }, - "b1cd4bdfd104489a026ec99d597307a04279f173": { - "balance": "20000000000000000000000" - }, - "876c3f218b4776df3ca9dbfb270de152d94ed252": { - "balance": "100000000000000000000" - }, - "8a36869ad478997cbf6d8924d20a3c8018e9855b": { - "balance": "20000000000000000000" - }, - "fb3fe09bb836861529d7518da27635f538505615": { - "balance": "1399904000000000000000" - }, - "d093e829819fd2e25b973800bb3d5841dd152d05": { - "balance": "4000000000000000000000" - }, - "126d91f7ad86debb0557c612ca276eb7f96d00a1": { - "balance": "100000000000000000000" - }, - "2a81d27cb6d4770ff4f3c4a3ba18e5e57f07517c": { - "balance": "2000000000000000000000" - }, - "c4f7b13ac6d4eb4db3d4e6a252af8a07bd5957da": { - "balance": "200000000000000000000" - }, - "305d26c10bdc103f6b9c21272eb7cb2d9108c47e": { - "balance": "500000000000000000000" - }, - "d0d0a2ad45f59a9dccc695d85f25ca46ed31a5a3": { - "balance": "840000000000000000000" - }, - "522323aad71dbc96d85af90f084b99c3f09decb7": { - "balance": "6000000000000000000000" - }, - "f43da3a4e3f5fab104ca9bc1a0f7f3bb4a56f351": { - "balance": "1999944000000000000000" - }, - "a2dc65ee256b59a5bd7929774f904b358df3ada1": { - "balance": "21319600000000000000000" - }, - "f382df583155d8548f3f93440cd5f68cb79d6026": { - "balance": "266619800000000000000000" - }, - "0c967e3061b87a753e84507eb60986782c8f3013": { - "balance": "100000000000000000000" - }, - "a3a262afd2936819230892fde84f2d5a594ab283": { - "balance": "1880000000000000000000" - }, - "93868ddb2a794d02ebda2fa4807c76e3609858dc": { - "balance": "2027851000000000000000" - }, - "cd35ff010ec501a721a1b2f07a9ca5877dfcf95a": { - "balance": "4011000000000000000000" - }, - "5824a7e22838277134308c5f4b50dab65e43bb31": { - "balance": "6000000000000000000000" - }, - "7f7a3a21b3f5a65d81e0fcb7d52dd00a1aa36dba": { - "balance": "100000000000000000000" - }, - "30513fca9f36fd788cfea7a340e86df98294a244": { - "balance": "447000000000000000000" - }, - "283e6252b4efcf4654391acb75f903c59b78c5fb": { - "balance": "12000000000000000000000" - }, - "eddbaafbc21be8f25562f1ed6d05d6afb58f02c2": { - "balance": "2000000000000000000000" - }, - "0dcfe837ea1cf28c65fccec3bef1f84e59d150c0": { - "balance": "200000000000000000000" - }, - "828ba651cb930ed9787156299a3de44cd08b7212": { - "balance": "1337000000000000000000" - }, - "cfd47493c9f89fe680bda5754dd7c9cfe7cb5bbe": { - "balance": "54508000000000000000" - }, - "0e89eddd3fa0d71d8ab0ff8da5580686e3d4f74f": { - "balance": "2000000000000000000000" - }, - "205f5166f12440d85762c967d3ae86184f8f4d98": { - "balance": "432500000000000000000" - }, - "25dad495a11a86b9eeece1eeec805e57f157faff": { - "balance": "16000000000000000000000" - }, - "6c84cba77c6db4f7f90ef13d5ee21e8cfc7f8314": { - "balance": "2000000000000000000000" - }, - "91a787bc5196f34857fe0c372f4df376aaa76613": { - "balance": "2000000000000000000000" - }, - "b0d3c9872b85056ea0c0e6d1ecf7a77e3ce6ab85": { - "balance": "4999711000000000000000" - }, - "6e4d2e39c8836629e5b487b1918a669aebdd9536": { - "balance": "1000000000000000000000" - }, - "dc703a5f3794c84d6cb3544918cae14a35c3bd4f": { - "balance": "1850000000000000000000" - }, - "47beb20f759100542aa93d41118b3211d664920e": { - "balance": "2000000000000000000000" - }, - "5a7735007d70b06844da9901cdfadb11a2582c2f": { - "balance": "6000000000000000000000" - }, - "aff107960b7ec34ed690b665024d60838c190f70": { - "balance": "500000000000000000000" - }, - "563a03ab9c56b600f6d25b660c21e16335517a75": { - "balance": "1000000000000000000000" - }, - "a106465bbd19e1b6bce50d1b1157dc59095a3630": { - "balance": "2000000000000000000000" - }, - "ca9dec02841adf5cc920576a5187edd2bd434a18": { - "balance": "500000000000000000000" - }, - "572ac1aba0de23ae41a7cae1dc0842d8abfc103b": { - "balance": "1910000000000000000000" - }, - "5f74ed0e24ff80d9b2c4a44baa9975428cd6b935": { - "balance": "2980000000000000000000" - }, - "f2049532fd458a83ca1bff2eebacb6d5ca63f4a4": { - "balance": "3625693000000000000000" - }, - "cee699c0707a7836252b292f047ce8ad289b2f55": { - "balance": "324700000000000000000" - }, - "8b3696f3c60de32432a2e4c395ef0303b7e81e75": { - "balance": "30000000000000000000000" - }, - "13dee03e3799952d0738843d4be8fc0a803fb20e": { - "balance": "2000000000000000000000" - }, - "c853215b9b9f2d2cd0741e585e987b5fb80c212e": { - "balance": "1550000000000000000000" - }, - "851c0d62be4635d4777e8035e37e4ba8517c6132": { - "balance": "500000000000000000000" - }, - "a76b743f981b693072a131b22ba510965c2fefd7": { - "balance": "18200000000000000000" - }, - "69bd25ade1a3346c59c4e930db2a9d715ef0a27a": { - "balance": "4000000000000000000000" - }, - "0fec4ee0d7ca180290b6bd20f9992342f60ff68d": { - "balance": "334383000000000000000" - }, - "ccfd725760a68823ff1e062f4cc97e1360e8d997": { - "balance": "399800000000000000000" - }, - "9f017706b830fb9c30efb0a09f506b9157457534": { - "balance": "2000000000000000000000" - }, - "420fb86e7d2b51401fc5e8c72015decb4ef8fc2e": { - "balance": "1000000000000000000000" - }, - "cb7d2b8089e9312cc9aeaa2773f35308ec6c2a7b": { - "balance": "10000000000000000000000" - }, - "6c822029218ac8e98a260c1e064029348839875b": { - "balance": "5010000000000000000000" - }, - "1c68a66138783a63c98cc675a9ec77af4598d35e": { - "balance": "50100000000000000000" - }, - "f270792576f05d514493ffd1f5e84bec4b2df810": { - "balance": "1000000000000000000000" - }, - "9191f94698210516cf6321a142070e20597674ed": { - "balance": "17194000000000000000" - }, - "c0ca3277942e7445874be31ceb902972714f1823": { - "balance": "250000000000000000000" - }, - "35e096120deaa5c1ecb1645e2ccb8b4edbd9299a": { - "balance": "500000000000000000000" - }, - "e2bbf84641e3541f6c33e6ed683a635a70bde2ec": { - "balance": "502763000000000000000" - }, - "d12d77ae01a92d35117bac705aacd982d02e74c1": { - "balance": "1000000000000000000000" - }, - "dabb0889fc042926b05ef57b2520910abc4b4149": { - "balance": "2000000000000000000000" - }, - "5a1a336962d6e0c63031cc83c6a5c6a6f4478ecb": { - "balance": "1000000000000000000000" - }, - "abd154903513b8da4f019f68284b0656a1d0169b": { - "balance": "1000000000000000000000" - }, - "ad377cd25eb53e83ae091a0a1d2b4516f484afde": { - "balance": "1940000000000000000000" - }, - "08c2f236ac4adcd3fda9fbc6e4532253f9da3bec": { - "balance": "20000000000000000000" - }, - "71135d8f05963c905a4a07922909235a896a52ea": { - "balance": "3000000000000000000000" - }, - "080546508a3d2682c8b9884f13637b8847b44db3": { - "balance": "2000000000000000000000" - }, - "2d61bfc56873923c2b00095dc3eaa0f590d8ae0f": { - "balance": "20760000000000000000000" - }, - "cbfa6af6c283b046e2772c6063b0b21553c40106": { - "balance": "2000000000000000000000" - }, - "ccabc6048a53464424fcf76eeb9e6e1801fa23d4": { - "balance": "49250000000000000000" - }, - "60cc3d445ebdf76a7d7ae571c6971dff68cc8585": { - "balance": "1000000000000000000000" - }, - "fff33a3bd36abdbd412707b8e310d6011454a7ae": { - "balance": "8000000000000000000000" - }, - "d2dbebe89b0357aea98bbe8e496338debb28e805": { - "balance": "4000000000000000000000" - }, - "5f521282e9b278dc8c034c72af53ee29e5443d78": { - "balance": "6520000000000000000000" - }, - "c5a48a8500f9b4e22f0eb16c6f4649687674267d": { - "balance": "812721000000000000000" - }, - "8cb3aa3fcd212854d7578fcc30fdede6742a312a": { - "balance": "300000000000000000000" - }, - "90d2809ae1d1ffd8f63eda01de49dd552df3d1bc": { - "balance": "3998000000000000000000" - }, - "96a55f00dff405dc4de5e58c57f6f6f0cac55d2f": { - "balance": "1962711000000000000000" - }, - "ae842e81858ecfedf6506c686dc204ac15bf8b24": { - "balance": "40000000000000000000" - }, - "0be6a09e4307fe48d412b8d1a1a8284dce486261": { - "balance": "19180000000000000000000" - }, - "c9c7ac0bdd9342b5ead4360923f68c72a6ba633a": { - "balance": "500000000000000000000" - }, - "ea8f30b6e4c5e65290fb9864259bc5990fa8ee8a": { - "balance": "20000000000000000000" - }, - "74d37a51747bf8b771bfbf43943933d100d21483": { - "balance": "1000000000000000000000" - }, - "1a04d5389eb006f9ce880c30d15353f8d11c4b31": { - "balance": "17072800000000000000000" - }, - "726a14c90e3f84144c765cffacba3e0df11b48be": { - "balance": "10000000000000000000000" - }, - "86b7bd563ceab686f96244f9ddc02ad7b0b14bc2": { - "balance": "10000000000000000000000" - }, - "2bbe672a1857508f630f2a5edb563d9e9de92815": { - "balance": "2000000000000000000000" - }, - "a17070c2e9c5a940a4ec0e4954c4d7d643be8f49": { - "balance": "1999965000000000000000" - }, - "f2d1b7357724ec4c03185b879b63f57e26589153": { - "balance": "6000000000000000000000" - }, - "d6a7ac4de7b510f0e8de519d973fa4c01ba83400": { - "balance": "1880000000000000000000" - }, - "593b45a1864ac5c7e8f0caaeba0d873cd5d113b2": { - "balance": "6000000000000000000000" - }, - "0837539b5f6a522a482cdcd3a9bb7043af39bdd2": { - "balance": "6000000000000000000000" - }, - "b927abd2d28aaaa24db31778d27419df8e1b04bb": { - "balance": "27531000000000000000" - }, - "b2e085fddd1468ba07415b274e734e11237fb2a9": { - "balance": "100000000000000000000" - }, - "970938522afb5e8f994873c9fbdc26e3b37e314c": { - "balance": "1000000000000000000000" - }, - "f3de5f26ef6aded6f06d3b911346ee70401da4a0": { - "balance": "354718000000000000000" - }, - "bffb6929241f788693273e7022e60e3eab1fe84f": { - "balance": "2000000000000000000000" - }, - "b56ad2aec6c8c3f19e1515bbb7dd91285256b639": { - "balance": "1000000000000000000000" - }, - "47730f5f8ebf89ac72ef80e46c12195038ecdc49": { - "balance": "3160000000000000000000" - }, - "f39a9d7aa3581df07ee4279ae6c312ef21033658": { - "balance": "4000000000000000000000" - }, - "36227cdfa0fd3b9d7e6a744685f5be9aa366a7f0": { - "balance": "198479000000000000000" - }, - "89e3b59a15864737d493c1d23cc53dbf8dcb1362": { - "balance": "4000000000000000000000" - }, - "bd08e0cddec097db7901ea819a3d1fd9de8951a2": { - "balance": "20000000000000000000" - }, - "533444584082eba654e1ad30e149735c6f7ba922": { - "balance": "1730000000000000000000" - }, - "6a8a4317c45faa0554ccdb482548183e295a24b9": { - "balance": "1000000000000000000000" - }, - "22ce349159eeb144ef06ff2636588aef79f62832": { - "balance": "188000000000000000000" - }, - "3cd1d9731bd548c1dd6fcea61beb75d91754f7d3": { - "balance": "5130285000000000000000" - }, - "8b7056f6abf3b118d026e944d5c073433ca451d7": { - "balance": "999999000000000000000" - }, - "15f1b352110d68901d8f67aac46a6cfafe031477": { - "balance": "200000000000000000000" - }, - "0f789e30397c53bf256fc364e6ef39f853504114": { - "balance": "3640000000000000000000" - }, - "750bbb8c06bbbf240843cc75782ee02f08a97453": { - "balance": "835000000000000000000" - }, - "fff7ac99c8e4feb60c9750054bdc14ce1857f181": { - "balance": "1000000000000000000000" - }, - "5c6f36af90ab1a656c6ec8c7d521512762bba3e1": { - "balance": "1999800000000000000000" - }, - "6811b54cd19663b11b94da1de2448285cd9f68d9": { - "balance": "1100000000000000000000" - }, - "6f50929777824c291a49c46dc854f379a6bea080": { - "balance": "360000000000000000000" - }, - "e83604e4ff6be7f96f6018d3ec3072ec525dff6b": { - "balance": "182000000000000000000" - }, - "d731bb6b5f3c37395e09ceaccd14a918a6060789": { - "balance": "3940000000000000000000" - }, - "372e453a6b629f27678cc8aeb5e57ce85ec0aef9": { - "balance": "200000000000000000000" - }, - "86924fb211aad23cf5ce600e0aae806396444087": { - "balance": "10000000000000000000000" - }, - "18c6723a6753299cb914477d04a3bd218df8c775": { - "balance": "1000000000000000000000" - }, - "e00484788db50fc6a48e379d123e508b0f6e5ab1": { - "balance": "1000000000000000000000" - }, - "150e3dbcbcfc84ccf89b73427763a565c23e60d0": { - "balance": "40000000000000000000" - }, - "8ffa062122ac307418821adb9311075a3703bfa3": { - "balance": "1000000000000000000000" - }, - "21206ce22ea480e85940d31314e0d64f4e4d3a04": { - "balance": "1000000000000000000000" - }, - "ac024f594f9558f04943618eb0e6b2ee501dc272": { - "balance": "2000000000000000000000" - }, - "b2b7cdb4ff4b61d5b7ce0b2270bbb5269743ec04": { - "balance": "2000000000000000000000" - }, - "abc74706964960dfe0dca3dca79e9216056f1cf4": { - "balance": "40000000000000000000000" - }, - "d7eb903162271c1afa35fe69e37322c8a4d29b11": { - "balance": "10000000000000000000000" - }, - "d7c6265dea11876c903b718e4cd8ab24fe265bde": { - "balance": "2000000000000000000000" - }, - "cba288cd3c1eb4d59ddb06a6421c14c345a47b24": { - "balance": "4000000000000000000000" - }, - "8c22426055b76f11f0a2de1a7f819a619685fe60": { - "balance": "1980000000000000000000" - }, - "f463a90cb3f13e1f0643423636beab84c123b06d": { - "balance": "40000000000000000000" - }, - "2b5ced9987c0765f900e49cf9da2d9f9c1138855": { - "balance": "400000000000000000000" - }, - "9bb760d5c289a3e1db18db095345ca413b9a43c2": { - "balance": "197000000000000000000" - }, - "d66ab79294074c8b627d842dab41e17dd70c5de5": { - "balance": "1000000000000000000000" - }, - "0bdd58b96e7c916dd2fb30356f2aebfaaf1d8630": { - "balance": "2000000000000000000000" - }, - "d612597bc31743c78633f633f239b1e9426bd925": { - "balance": "76000000000000000000000" - }, - "140518a3194bad1350b8949e650565debe6db315": { - "balance": "2000000000000000000000" - }, - "daedd4ad107b271e89486cbf80ebd621dd974578": { - "balance": "2000000000000000000000" - }, - "c36c0b63bfd75c2f8efb060883d868cccd6cbdb4": { - "balance": "3000000000000000000000" - }, - "e646665872e40b0d7aa2ff82729caaba5bc3e89e": { - "balance": "400000000000000000000" - }, - "b5fb7ea2ddc1598b667a9d57dd39e85a38f35d56": { - "balance": "500000000000000000000" - }, - "e51421f8ee2210c71ed870fe618276c8954afbe9": { - "balance": "1337000000000000000000" - }, - "08a9a44e1f41de3dbba7a363a3ab412c124cd15e": { - "balance": "200000000000000000000" - }, - "562bced38ab2ab6c080f3b0541b8456e70824b3f": { - "balance": "641760000000000000000" - }, - "1e484d0621f0f5331b35d5408d9aae4eb1acf21e": { - "balance": "20000000000000000000" - }, - "3a476bd2c9e664c63ab266aa4c6e4a4825f516c3": { - "balance": "200000000000000000000" - }, - "8d6df209484d7b94702b03a53e56b9fb0660f6f0": { - "balance": "2000000000000000000000" - }, - "5970fb1b144dd751e4ce2eca7caa20e363dc4da3": { - "balance": "10000000000000000000000" - }, - "d1dd79fb158160e5b4e8e23f312e6a907fbc4d4e": { - "balance": "500000000000000000000" - }, - "7ee5ca805dce23af89c2d444e7e40766c54c7404": { - "balance": "240660000000000000000" - }, - "93e0f37ecdfb0086e3e862a97034447b1e4dec1a": { - "balance": "30000000000000000000" - }, - "e10ac19c546fc2547c61c139f5d1f45a6666d5b0": { - "balance": "4775000000000000000000" - }, - "1c73d00b6e25d8eb9c1ff4ad827b6b9e9cf6d20c": { - "balance": "200000000000000000000" - }, - "d771d9e0ca8a08a113775731434eb3270599c40d": { - "balance": "20000000000000000000" - }, - "e69d1c378b771e0feff051db69d966ac6779f4ed": { - "balance": "553000000000000000000" - }, - "0ef85b49d08a75198692914eddb4b22cf5fa4450": { - "balance": "2004800000000000000000" - }, - "ed70a37cdd1cbda9746d939658ae2a6181288578": { - "balance": "9600000000000000000000" - }, - "eee761847e33fd61d99387ee14628694d1bfd525": { - "balance": "2000000000000000000000" - }, - "271d3d481cb88e7671ad216949b6365e06303de0": { - "balance": "4000000000000000000000" - }, - "5255dc69155a45b970c604d30047e2f530690e7f": { - "balance": "20000000000000000000" - }, - "cabab6274ed15089737e287be878b757934864e2": { - "balance": "20000000000000000000000" - }, - "9defe56a0ff1a1947dba0923f7dd258d8f12fa45": { - "balance": "26880000000000000000000" - }, - "b7a2c103728b7305b5ae6e961c94ee99c9fe8e2b": { - "balance": "50000000000000000000000" - }, - "b498bb0f520005b6216a4425b75aa9adc52d622b": { - "balance": "4000000000000000000000" - }, - "c1132878235c5ddba5d9f3228b5236e47020dc6f": { - "balance": "1000000000000000000000" - }, - "f81622e55757daea6675975dd93538da7d16991e": { - "balance": "2000000000000000000000" - }, - "ce2deab51c0a9ae09cd212c4fa4cc52b53cc0dec": { - "balance": "2000000000000000000000" - }, - "86a1eadeeb30461345d9ef6bd05216fa247c0d0c": { - "balance": "2000000000000000000000" - }, - "7b1fe1ab4dfd0088cdd7f60163ef59ec2aee06f5": { - "balance": "2000000000000000000000" - }, - "6bbc3f358a668dd1a11f0380f3f73108426abd4a": { - "balance": "4000000000000000000000" - }, - "b1e6e810c24ab0488de9e01e574837829f7c77d0": { - "balance": "400000000000000000000" - }, - "03eb3cb860f6028da554d344a2bb5a500ae8b86f": { - "balance": "2000000000000000000000" - }, - "e5481a7fed42b901bbed20789bd4ade50d5f83b9": { - "balance": "2000000000000000000000" - }, - "1f3da68fe87eaf43a829ab6d7ec5a6e009b204fb": { - "balance": "554988000000000000000" - }, - "30037988702671acbe892c03fe5788aa98af287a": { - "balance": "2800000000000000000000" - }, - "edb473353979a206879de144c10a3c51d7d7081a": { - "balance": "6000000000000000000000" - }, - "22bdffc240a88ff7431af3bff50e14da37d5183e": { - "balance": "1000000000000000000000" - }, - "9374869d4a9911ee1eaf558bc4c2b63ec63acfdd": { - "balance": "1000000000000000000000" - }, - "b756ad52f3bf74a7d24c67471e0887436936504c": { - "balance": "20000000000000000000000" - }, - "8bd0b65a50ef5cef84fec420be7b89ed1470ceb9": { - "balance": "11999000000000000000000" - }, - "af26f7c6bf453e2078f08953e4b28004a2c1e209": { - "balance": "100000000000000000000" - }, - "7c532db9e0c06c26fd40acc56ac55c1ee92d3c3a": { - "balance": "300000000000000000000000" - }, - "dde670d01639667576a22dd05d3246d61f06e083": { - "balance": "26740000000000000000" - }, - "5cf44e10540d65716423b1bcb542d21ff83a94cd": { - "balance": "10000000000000000000000" - }, - "f96b4c00766f53736a8574f822e6474c2f21da2d": { - "balance": "400000000000000000000" - }, - "8d89170b92b2be2c08d57c48a7b190a2f146720f": { - "balance": "19700000000000000000000" - }, - "142b87c5043ffb5a91df18c2e109ced6fe4a71db": { - "balance": "200000000000000000000" - }, - "42d34940edd2e7005d46e2188e4cfece8311d74d": { - "balance": "158000000000000000000" - }, - "562105e82b099735de49f62692cc87cd38a8edcd": { - "balance": "6000000000000000000000" - }, - "457bcef37dd3d60b2dd019e3fe61d46b3f1e7252": { - "balance": "20000000000000000000" - }, - "cf8882359c0fb23387f5674074d8b17ade512f98": { - "balance": "6000000000000000000000" - }, - "f0c081da52a9ae36642adf5e08205f05c54168a6": { - "balance": "111000000000000000000" - }, - "551e7784778ef8e048e495df49f2614f84a4f1dc": { - "balance": "600000000000000000000" - }, - "3c869c09696523ced824a070414605bb76231ff2": { - "balance": "1000000000000000000000" - }, - "7e7f18a02eccaa5d61ab8fbf030343c434a25ef7": { - "balance": "66850000000000000000" - }, - "9328d55ccb3fce531f199382339f0e576ee840a3": { - "balance": "4000000000000000000000" - }, - "9d0f347e826b7dceaad279060a35c0061ecf334b": { - "balance": "4000000000000000000000" - }, - "680640838bd07a447b168d6d923b90cf6c43cdca": { - "balance": "1730000000000000000000" - }, - "c951900c341abbb3bafbf7ee2029377071dbc36a": { - "balance": "327600000000000000000" - }, - "ddf5810a0eb2fb2e32323bb2c99509ab320f24ac": { - "balance": "17900000000000000000000" - }, - "2489ac126934d4d6a94df08743da7b7691e9798e": { - "balance": "1000000000000000000000" - }, - "f42f905231c770f0a406f2b768877fb49eee0f21": { - "balance": "197000000000000000000" - }, - "756f45e3fa69347a9a973a725e3c98bc4db0b5a0": { - "balance": "200000000000000000000" - } - } -} diff --git a/ethcore/res/ethereum/constantinople_test.json b/ethcore/res/ethereum/constantinople_test.json index b6db5cddb9b..cc5690e0d3d 100644 --- a/ethcore/res/ethereum/constantinople_test.json +++ b/ethcore/res/ethereum/constantinople_test.json @@ -58,8 +58,47 @@ "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x00", "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": "0x00", "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": "0x00", "pricing": { "linear": { "base": 40000, "word": 0 } } } }, - "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": "0x00", "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } } + "0000000000000000000000000000000000000006": { + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0000000000000000000000000000000000000007": { + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0000000000000000000000000000000000000008": { + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + } } } diff --git a/ethcore/res/ethereum/eip210_test.json b/ethcore/res/ethereum/eip210_test.json index adf7f2964ec..2cfeb7c61c2 100644 --- a/ethcore/res/ethereum/eip210_test.json +++ b/ethcore/res/ethereum/eip210_test.json @@ -46,8 +46,47 @@ "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x00", "pricing": { "modexp": { "divisor": 100 } } } }, - "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": "0x00", "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": "0x00", "pricing": { "linear": { "base": 2000, "word": 0 } } } }, - "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": "0x00", "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } } + "0000000000000000000000000000000000000006": { + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0000000000000000000000000000000000000007": { + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0000000000000000000000000000000000000008": { + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + } } } diff --git a/ethcore/res/ethereum/ellaism.json b/ethcore/res/ethereum/ellaism.json index 946b340f933..082a88aae5a 100644 --- a/ethcore/res/ethereum/ellaism.json +++ b/ethcore/res/ethereum/ellaism.json @@ -28,7 +28,6 @@ "eip161abcTransition": "0x7fffffffffffffff", "eip161dTransition": "0x7fffffffffffffff", "eip155Transition": "0x0", - "wasmActivationTransition": 2000000, "eip140Transition": 2000000, "eip211Transition": 2000000, "eip214Transition": 2000000, @@ -49,25 +48,142 @@ "gasLimit": "0x1388" }, "nodes": [ - "enode://0d88e242aa0b01ee306ca43e956174677c96ec8eba4197f4d8be6fd7d4f2e57731e95d533b88229b66eb1a44399d870e99b7a4fe6547c8c80cdf00407a986e14@94.130.237.158:30303", - "enode://4be9e419d3efb0214faf3ef1794a0c33ebbd7633ece734a0a956faa166fefc496b2692a2a485adc66af805e461ba3e12f8d3941ec207e56bb9f3d3626787a705@94.130.237.158:60606", - "enode://834246cc2a7584df29ccdcf3b5366f118a0e291264980376769e809665a02c4caf0d68c43eecf8390dbeaf861823b05583807af0a62542a1f3f717046b958a76@45.77.106.33:30303", - "enode://d8059dcb137cb52b8960ca82613eeba1d121105572decd8f1d3ea22b09070645eeab548d2a3cd2914f206e1331c7870bd2bd5a231ebac6b3d4886ec3b8e627e5@173.212.216.105:30303", - "enode://9215ad77bd081e35013cb42a8ceadff9d8e94a78fcc680dff1752a54e7484badff0904e331c4b40a68be593782e55acfd800f076d22f9d2832e8483733ade149@213.14.82.125:30303", - "enode://5dd35866da95aea15211fb1f98684f6e8c4e355e6aa3cc17585680ed53fa164477b8c52cb6ca4b24ec4d80f3d48ff9212b53feb131d825c7945a3abaaf02d24d@178.79.189.58:60606", - "enode://6c585c18024eb902ca093278af73b04863ac904caabc39ac2920c23532307c572ad92afd828a990c980d272b1f26307f2409cc97aec3ff9fe866732cae49a8c2@144.217.163.224:31337", - "enode://edd90c4cc64528802ad52fd127d80b641ff80fd43fa5292fb111c8bd2914482dffee288fd1b0d26440c6b2c669b10a53cbcd37c895ba0d6194110e100a965b2d@188.166.179.159:30303", - "enode://9d960373335c1cc38ca696dea8f2893e2a071c8f21524f21e8aae22be032acc3b67797b1d21e866f9d832943ae7d9555b8466c6ab34f473d21e547114952df37@213.32.53.183:30303", - "enode://5120308ebf25261c8423866a3a082e8d0f31106343d8b3b6c4dfe9d41bd900f5e03c64356ba51b6d343a951846a3f5ede5c5dd05925eaea4e4b9c35b1be9237c@95.53.247.188:30303" + "enode://b5e153bf33b2b77e6a09c1c3c20dbffc964b3622d53f25c1088fd5f4b88ec434af3c1ecea3318ad0cc3cdf48fad37dfec748ed6d9ebe271181d7e910fda7f340@142.93.163.95:30303", + "enode://a34e38cecf83bde932aca28f8a66487ef195ea9b253558d50dc3c5e3261909c7057ff102d3ba73bd3e887807a9fec42ec9f0c1c825263c1e4f8de0a5a4e857ed@157.245.181.78:30303", + "enode://2e9a4eaa046b45714ebb6052dee53c320b720e3ea5eee2cc0614d3dbeb19eb1a2e738491b46a3298c101fd6dd24b8b2e1b6e2c50a209e6c467f8b980ba1acf15@69.164.212.225:30303", + "enode://a7c84e5a44c6909ee4046a07101f2705b8cc0964d35c0f1b852e206dbbb787a5b6709ca3ba29d5df32ef431d06d14c1e0008bf1632415ba8b7b525cf80834723@176.99.9.182:30303", + "enode://4a383990e702b7ed4ec2eea42eebed3142905ddbba5c97865125d2e4b0f09990763f127e1781fc6357ae0578f4b6d322a92ce29b4e1704e2744a7ce9908e51e6@185.163.116.77:30303", + "enode://5fc0737f0cad2b9724edf8320878fc6e197b9968e5e9e798bbe24728c6e431858cb2ea4f8b151ee24bbc228820087db3c60fbde24b7d167896a25065f7dfb4c3@84.247.76.138:30303", + "enode://5364bd8aa566b79a4a9b88916f50b0140ea7ee1d4485394f60e09ba9c85927888dd7145b0a7d835663fc142ea44ce8f11405e34cb02d774e4edef94c634c570c@77.13.29.32:33003", + "enode://b9d6b7c82f9e8f6ba6c164da31cc2093dae3e1f1e1c315c71bd17a78b55a6f23e4d87b3ad0e3db6adaae4e561e7354b9d4d4be103b13f65e0638d7c5cdf48e4c@93.90.206.247:30303", + "enode://09284d2f3161e963168e3b69f6a83f27a129f30aa07a57bd8eafdccb66b9d653080721f4c3b51c2a119f113d1b9f9f736b14bbfcca7d2e2f9eb118c2e5ce3e81@83.219.143.198:30307", + "enode://93800f7367b4aba7a76d7a98fed44fa97cbcb62c6062bccdfad328e5786967c24f1f75c468e6a530425ec8839cac9c4b455b49ac5cdf37b6e67ba549439d285b@89.33.253.232:30303", + "enode://f15fcfb4e881d247b28db2ff7195ea380b642eda8601b646502cd31b827113c2f32ae9c98ac2673ce37a5df6ff1fdc737199c608a5defdd8b799e584461a4ba5@89.163.148.73:31058", + "enode://85d95b320452b745e45d4bb9353ad2eb180c98c9ffb85f9a572b972302f06dd2d130fa2ef723ac6cc6418eb842fd08fa5ad9d934288c756f9a63389bd354210e@18.228.30.92:21000" ], "accounts": { - "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, - "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": 2000000, "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": 2000000, "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": 2000000, "pricing": { "linear": { "base": 40000, "word": 0 } } } }, - "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": 2000000, "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } } + "0000000000000000000000000000000000000001": { + "builtin": { + "name": "ecrecover", + "pricing": { + "linear": { + "base": 3000, + "word": 0 + } + } + } + }, + "0000000000000000000000000000000000000002": { + "builtin": { + "name": "sha256", + "pricing": { + "linear": { + "base": 60, + "word": 12 + } + } + } + }, + "0000000000000000000000000000000000000003": { + "builtin": { + "name": "ripemd160", + "pricing": { + "linear": { + "base": 600, + "word": 120 + } + } + } + }, + "0000000000000000000000000000000000000004": { + "builtin": { + "name": "identity", + "pricing": { + "linear": { + "base": 15, + "word": 3 + } + } + } + }, + "0000000000000000000000000000000000000005": { + "builtin": { + "name": "modexp", + "activate_at": 2000000, + "pricing": { + "modexp": { + "divisor": 20 + } + } + } + }, + "0000000000000000000000000000000000000006": { + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "2000000": { + "price": { + "alt_bn128_const_operations": { + "price": 500 + } + } + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { + "alt_bn128_const_operations": { + "price": 150 + } + } + } + } + } + }, + "0000000000000000000000000000000000000007": { + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "2000000": { + "price": { + "alt_bn128_const_operations": { + "price": 40000 + } + } + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { + "alt_bn128_const_operations": { + "price": 6000 + } + } + } + } + } + }, + "0000000000000000000000000000000000000008": { + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "2000000": { + "price": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { + "alt_bn128_pairing": { + "base": 45000, + "pair": 34000 + } + } + } + } + } + } } -} +} \ No newline at end of file diff --git a/ethcore/res/ethereum/ewc.json b/ethcore/res/ethereum/ewc.json new file mode 100644 index 00000000000..632b577815d --- /dev/null +++ b/ethcore/res/ethereum/ewc.json @@ -0,0 +1,214 @@ +{ + "name": "EnergyWebChain", + "engine": { + "authorityRound": { + "params": { + "stepDuration": "5", + "validators": { + "contract": "0x1204700000000000000000000000000000000000" + }, + "maximumUncleCountTransition": "0", + "maximumUncleCount": "0", + "blockRewardContractAddress": "0x1204700000000000000000000000000000000002", + "blockRewardContractTransition": "0" + } + } + }, + "params": { + "networkID": "0xF6", + "maximumExtraDataSize": "0x20", + "gasLimitBoundDivisor": "0x400", + "minGasLimit": "0x1388", + "maxCodeSize": "0x6000", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "registrar": "0x1204700000000000000000000000000000000006" + }, + "genesis": { + "seal": { + "authorityRound": { + "step": "0x0", + "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "gasLimit": "0x5B8D80" + }, + "accounts": { + "0x0000000000000000000000000000000000000001": { + "balance": "1", + "builtin": { + "name": "ecrecover", + "activate_at": "0", + "pricing": { + "linear": { + "base": 3000, + "word": 0 + } + } + } + }, + "0x0000000000000000000000000000000000000002": { + "balance": "1", + "builtin": { + "name": "sha256", + "activate_at": "0", + "pricing": { + "linear": { + "base": 60, + "word": 12 + } + } + } + }, + "0x0000000000000000000000000000000000000003": { + "balance": "1", + "builtin": { + "name": "ripemd160", + "activate_at": "0", + "pricing": { + "linear": { + "base": 600, + "word": 120 + } + } + } + }, + "0x0000000000000000000000000000000000000004": { + "balance": "1", + "builtin": { + "name": "identity", + "activate_at": "0", + "pricing": { + "linear": { + "base": 15, + "word": 3 + } + } + } + }, + "0x0000000000000000000000000000000000000005": { + "balance": "1", + "builtin": { + "name": "modexp", + "activate_at": "0", + "pricing": { + "modexp": { + "divisor": 20 + } + } + } + }, + "0x0000000000000000000000000000000000000006": { + "balance": "1", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0x0000000000000000000000000000000000000007": { + "balance": "1", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0x0000000000000000000000000000000000000008": { + "balance": "1", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, + "0x1204700000000000000000000000000000000005": { + "constructor": "0x60806040523480156200001157600080fd5b50604051620024c8380380620024c883398101806040528101908080518201929190602001805190602001909291905050506000825182603282111580156200005a5750818111155b801562000068575060008114155b801562000076575060008214155b15156200008257600080fd5b600092505b8451831015620001bd57600260008685815181101515620000a457fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16158015620001335750600085848151811015156200011057fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1614155b15156200013f57600080fd5b60016002600087868151811015156200015457fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550828060010193505062000087565b8460039080519060200190620001d5929190620001e8565b50836004819055505050505050620002bd565b82805482825590600052602060002090810192821562000264579160200282015b82811115620002635782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019062000209565b5b50905062000273919062000277565b5090565b620002ba91905b80821115620002b657600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016200027e565b5090565b90565b6121fb80620002cd6000396000f30060806040526004361061011d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063025e7c2714610177578063173825d9146101e457806320ea8d86146102275780632f54bf6e146102545780633411c81c146102af57806354741525146103145780637065cb4814610363578063784547a7146103a65780638b51d13f146103eb5780639ace38c21461042c578063a0e67e2b14610517578063a8abe69a14610583578063b5dc40c314610627578063b77bf600146106a9578063ba51a6df146106d4578063c01a8c8414610701578063c64274741461072e578063d74f8edd146107d5578063dc8452cd14610800578063e20056e61461082b578063ee22610b1461088e575b6000341115610175573373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a25b005b34801561018357600080fd5b506101a2600480360381019080803590602001909291905050506108bb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156101f057600080fd5b50610225600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506108f9565b005b34801561023357600080fd5b5061025260048036038101908080359060200190929190505050610b92565b005b34801561026057600080fd5b50610295600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d3a565b604051808215151515815260200191505060405180910390f35b3480156102bb57600080fd5b506102fa60048036038101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d5a565b604051808215151515815260200191505060405180910390f35b34801561032057600080fd5b5061034d600480360381019080803515159060200190929190803515159060200190929190505050610d89565b6040518082815260200191505060405180910390f35b34801561036f57600080fd5b506103a4600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610e1b565b005b3480156103b257600080fd5b506103d160048036038101908080359060200190929190505050611020565b604051808215151515815260200191505060405180910390f35b3480156103f757600080fd5b5061041660048036038101908080359060200190929190505050611105565b6040518082815260200191505060405180910390f35b34801561043857600080fd5b50610457600480360381019080803590602001909291905050506111d0565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018060200183151515158152602001828103825284818151815260200191508051906020019080838360005b838110156104d95780820151818401526020810190506104be565b50505050905090810190601f1680156105065780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b34801561052357600080fd5b5061052c6112c5565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561056f578082015181840152602081019050610554565b505050509050019250505060405180910390f35b34801561058f57600080fd5b506105d06004803603810190808035906020019092919080359060200190929190803515159060200190929190803515159060200190929190505050611353565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156106135780820151818401526020810190506105f8565b505050509050019250505060405180910390f35b34801561063357600080fd5b50610652600480360381019080803590602001909291905050506114c4565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561069557808201518184015260208101905061067a565b505050509050019250505060405180910390f35b3480156106b557600080fd5b506106be611701565b6040518082815260200191505060405180910390f35b3480156106e057600080fd5b506106ff60048036038101908080359060200190929190505050611707565b005b34801561070d57600080fd5b5061072c600480360381019080803590602001909291905050506117c1565b005b34801561073a57600080fd5b506107bf600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929050505061199e565b6040518082815260200191505060405180910390f35b3480156107e157600080fd5b506107ea6119bd565b6040518082815260200191505060405180910390f35b34801561080c57600080fd5b506108156119c2565b6040518082815260200191505060405180910390f35b34801561083757600080fd5b5061088c600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506119c8565b005b34801561089a57600080fd5b506108b960048036038101908080359060200190929190505050611cdd565b005b6003818154811015156108ca57fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561093557600080fd5b81600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561098e57600080fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600091505b600160038054905003821015610b13578273ffffffffffffffffffffffffffffffffffffffff16600383815481101515610a2157fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610b06576003600160038054905003815481101515610a7f57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600383815481101515610ab957fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610b13565b81806001019250506109eb565b6001600381818054905003915081610b2b91906120fe565b506003805490506004541115610b4a57610b49600380549050611707565b5b8273ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a2505050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610beb57600080fd5b81336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610c5657600080fd5b8360008082815260200190815260200160002060030160009054906101000a900460ff16151515610c8657600080fd5b60006001600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e960405160405180910390a35050505050565b60026020528060005260406000206000915054906101000a900460ff1681565b60016020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b600080600090505b600554811015610e1457838015610dc8575060008082815260200190815260200160002060030160009054906101000a900460ff16155b80610dfb5750828015610dfa575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15610e07576001820191505b8080600101915050610d91565b5092915050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610e5557600080fd5b80600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515610eaf57600080fd5b8160008173ffffffffffffffffffffffffffffffffffffffff1614151515610ed657600080fd5b60016003805490500160045460328211158015610ef35750818111155b8015610f00575060008114155b8015610f0d575060008214155b1515610f1857600080fd5b6001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060038590806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508473ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b6000806000809150600090505b6003805490508110156110fd5760016000858152602001908152602001600020600060038381548110151561105e57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156110dd576001820191505b6004548214156110f057600192506110fe565b808060010191505061102d565b5b5050919050565b600080600090505b6003805490508110156111ca5760016000848152602001908152602001600020600060038381548110151561113e57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156111bd576001820191505b808060010191505061110d565b50919050565b60006020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690806001015490806002018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156112a85780601f1061127d576101008083540402835291602001916112a8565b820191906000526020600020905b81548152906001019060200180831161128b57829003601f168201915b5050505050908060030160009054906101000a900460ff16905084565b6060600380548060200260200160405190810160405280929190818152602001828054801561134957602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116112ff575b5050505050905090565b60608060008060055460405190808252806020026020018201604052801561138a5781602001602082028038833980820191505090505b50925060009150600090505b600554811015611436578580156113cd575060008082815260200190815260200160002060030160009054906101000a900460ff16155b8061140057508480156113ff575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b156114295780838381518110151561141457fe5b90602001906020020181815250506001820191505b8080600101915050611396565b8787036040519080825280602002602001820160405280156114675781602001602082028038833980820191505090505b5093508790505b868110156114b957828181518110151561148457fe5b906020019060200201518489830381518110151561149e57fe5b9060200190602002018181525050808060010191505061146e565b505050949350505050565b6060806000806003805490506040519080825280602002602001820160405280156114fe5781602001602082028038833980820191505090505b50925060009150600090505b60038054905081101561164b5760016000868152602001908152602001600020600060038381548110151561153b57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561163e576003818154811015156115c257fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683838151811015156115fb57fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506001820191505b808060010191505061150a565b8160405190808252806020026020018201604052801561167a5781602001602082028038833980820191505090505b509350600090505b818110156116f957828181518110151561169857fe5b9060200190602002015184828151811015156116b057fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508080600101915050611682565b505050919050565b60055481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561174157600080fd5b60038054905081603282111580156117595750818111155b8015611766575060008114155b8015611773575060008214155b151561177e57600080fd5b826004819055507fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a836040518082815260200191505060405180910390a1505050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561181a57600080fd5b81600080600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415151561187657600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515156118e257600080fd5b600180600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef60405160405180910390a361199785611cdd565b5050505050565b60006119ab848484611f85565b90506119b6816117c1565b9392505050565b603281565b60045481565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515611a0457600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611a5d57600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515611ab757600080fd5b600092505b600380549050831015611ba0578473ffffffffffffffffffffffffffffffffffffffff16600384815481101515611aef57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611b935783600384815481101515611b4657fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611ba0565b8280600101935050611abc565b6000600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508473ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b600033600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611d3857600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611da357600080fd5b8460008082815260200190815260200160002060030160009054906101000a900460ff16151515611dd357600080fd5b611ddc86611020565b15611f7d57600080878152602001908152602001600020945060018560030160006101000a81548160ff021916908315150217905550611efa8560000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866001015487600201805460018160011615610100020316600290049050886002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611ef05780601f10611ec557610100808354040283529160200191611ef0565b820191906000526020600020905b815481529060010190602001808311611ed357829003601f168201915b50505050506120d7565b15611f3157857f33e13ecb54c3076d8e8bb8c2881800a4d972b792045ffae98fdf46df365fed7560405160405180910390a2611f7c565b857f526441bb6c1aba3c9a4a6ca1d6545da9c2333c8c48343ef398eb858d72b7923660405160405180910390a260008560030160006101000a81548160ff0219169083151502179055505b5b505050505050565b60008360008173ffffffffffffffffffffffffffffffffffffffff1614151515611fae57600080fd5b60055491506080604051908101604052808673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018481526020016000151581525060008084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010155604082015181600201908051906020019061206d92919061212a565b5060608201518160030160006101000a81548160ff0219169083151502179055509050506001600560008282540192505081905550817fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5160405160405180910390a2509392505050565b6000806040516020840160008287838a8c6187965a03f19250505080915050949350505050565b8154818355818111156121255781836000526020600020918201910161212491906121aa565b5b505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061216b57805160ff1916838001178555612199565b82800160010185558215612199579182015b8281111561219857825182559160200191906001019061217d565b5b5090506121a691906121aa565b5090565b6121cc91905b808211156121c85760008160009055506001016121b0565b5090565b905600a165627a7a72305820b0d992f257e70d565448b927a3ae764342039a864112d5d5bb1ac1bd52e4104e00290000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000cb1437200aea736788f1fc56f327c0456c3598d00000000000000000000000074dd76e24b2cfb43c1b1a4498295d553d0843746000000000000000000000000eeb4ceee443f9e0d17bdbd6daa241681ee5e51c2000000000000000000000000a005caea55375ae20e3aaef746113535503abc1900000000000000000000000090ae948bb410838bff9d024ed849df6a7267dacf000000000000000000000000740a5c5742833bed72489fe6bf7efc9b79428989" + }, + "0x1204700000000000000000000000000000000003": { + "constructor": "0x60806040523480156200001157600080fd5b50604051620024c8380380620024c883398101806040528101908080518201929190602001805190602001909291905050506000825182603282111580156200005a5750818111155b801562000068575060008114155b801562000076575060008214155b15156200008257600080fd5b600092505b8451831015620001bd57600260008685815181101515620000a457fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16158015620001335750600085848151811015156200011057fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1614155b15156200013f57600080fd5b60016002600087868151811015156200015457fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550828060010193505062000087565b8460039080519060200190620001d5929190620001e8565b50836004819055505050505050620002bd565b82805482825590600052602060002090810192821562000264579160200282015b82811115620002635782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019062000209565b5b50905062000273919062000277565b5090565b620002ba91905b80821115620002b657600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016200027e565b5090565b90565b6121fb80620002cd6000396000f30060806040526004361061011d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063025e7c2714610177578063173825d9146101e457806320ea8d86146102275780632f54bf6e146102545780633411c81c146102af57806354741525146103145780637065cb4814610363578063784547a7146103a65780638b51d13f146103eb5780639ace38c21461042c578063a0e67e2b14610517578063a8abe69a14610583578063b5dc40c314610627578063b77bf600146106a9578063ba51a6df146106d4578063c01a8c8414610701578063c64274741461072e578063d74f8edd146107d5578063dc8452cd14610800578063e20056e61461082b578063ee22610b1461088e575b6000341115610175573373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a25b005b34801561018357600080fd5b506101a2600480360381019080803590602001909291905050506108bb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156101f057600080fd5b50610225600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506108f9565b005b34801561023357600080fd5b5061025260048036038101908080359060200190929190505050610b92565b005b34801561026057600080fd5b50610295600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d3a565b604051808215151515815260200191505060405180910390f35b3480156102bb57600080fd5b506102fa60048036038101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d5a565b604051808215151515815260200191505060405180910390f35b34801561032057600080fd5b5061034d600480360381019080803515159060200190929190803515159060200190929190505050610d89565b6040518082815260200191505060405180910390f35b34801561036f57600080fd5b506103a4600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610e1b565b005b3480156103b257600080fd5b506103d160048036038101908080359060200190929190505050611020565b604051808215151515815260200191505060405180910390f35b3480156103f757600080fd5b5061041660048036038101908080359060200190929190505050611105565b6040518082815260200191505060405180910390f35b34801561043857600080fd5b50610457600480360381019080803590602001909291905050506111d0565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018060200183151515158152602001828103825284818151815260200191508051906020019080838360005b838110156104d95780820151818401526020810190506104be565b50505050905090810190601f1680156105065780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b34801561052357600080fd5b5061052c6112c5565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561056f578082015181840152602081019050610554565b505050509050019250505060405180910390f35b34801561058f57600080fd5b506105d06004803603810190808035906020019092919080359060200190929190803515159060200190929190803515159060200190929190505050611353565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156106135780820151818401526020810190506105f8565b505050509050019250505060405180910390f35b34801561063357600080fd5b50610652600480360381019080803590602001909291905050506114c4565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561069557808201518184015260208101905061067a565b505050509050019250505060405180910390f35b3480156106b557600080fd5b506106be611701565b6040518082815260200191505060405180910390f35b3480156106e057600080fd5b506106ff60048036038101908080359060200190929190505050611707565b005b34801561070d57600080fd5b5061072c600480360381019080803590602001909291905050506117c1565b005b34801561073a57600080fd5b506107bf600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929050505061199e565b6040518082815260200191505060405180910390f35b3480156107e157600080fd5b506107ea6119bd565b6040518082815260200191505060405180910390f35b34801561080c57600080fd5b506108156119c2565b6040518082815260200191505060405180910390f35b34801561083757600080fd5b5061088c600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506119c8565b005b34801561089a57600080fd5b506108b960048036038101908080359060200190929190505050611cdd565b005b6003818154811015156108ca57fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561093557600080fd5b81600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561098e57600080fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600091505b600160038054905003821015610b13578273ffffffffffffffffffffffffffffffffffffffff16600383815481101515610a2157fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610b06576003600160038054905003815481101515610a7f57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600383815481101515610ab957fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610b13565b81806001019250506109eb565b6001600381818054905003915081610b2b91906120fe565b506003805490506004541115610b4a57610b49600380549050611707565b5b8273ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a2505050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610beb57600080fd5b81336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610c5657600080fd5b8360008082815260200190815260200160002060030160009054906101000a900460ff16151515610c8657600080fd5b60006001600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e960405160405180910390a35050505050565b60026020528060005260406000206000915054906101000a900460ff1681565b60016020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b600080600090505b600554811015610e1457838015610dc8575060008082815260200190815260200160002060030160009054906101000a900460ff16155b80610dfb5750828015610dfa575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15610e07576001820191505b8080600101915050610d91565b5092915050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610e5557600080fd5b80600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515610eaf57600080fd5b8160008173ffffffffffffffffffffffffffffffffffffffff1614151515610ed657600080fd5b60016003805490500160045460328211158015610ef35750818111155b8015610f00575060008114155b8015610f0d575060008214155b1515610f1857600080fd5b6001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060038590806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508473ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b6000806000809150600090505b6003805490508110156110fd5760016000858152602001908152602001600020600060038381548110151561105e57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156110dd576001820191505b6004548214156110f057600192506110fe565b808060010191505061102d565b5b5050919050565b600080600090505b6003805490508110156111ca5760016000848152602001908152602001600020600060038381548110151561113e57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156111bd576001820191505b808060010191505061110d565b50919050565b60006020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690806001015490806002018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156112a85780601f1061127d576101008083540402835291602001916112a8565b820191906000526020600020905b81548152906001019060200180831161128b57829003601f168201915b5050505050908060030160009054906101000a900460ff16905084565b6060600380548060200260200160405190810160405280929190818152602001828054801561134957602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116112ff575b5050505050905090565b60608060008060055460405190808252806020026020018201604052801561138a5781602001602082028038833980820191505090505b50925060009150600090505b600554811015611436578580156113cd575060008082815260200190815260200160002060030160009054906101000a900460ff16155b8061140057508480156113ff575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b156114295780838381518110151561141457fe5b90602001906020020181815250506001820191505b8080600101915050611396565b8787036040519080825280602002602001820160405280156114675781602001602082028038833980820191505090505b5093508790505b868110156114b957828181518110151561148457fe5b906020019060200201518489830381518110151561149e57fe5b9060200190602002018181525050808060010191505061146e565b505050949350505050565b6060806000806003805490506040519080825280602002602001820160405280156114fe5781602001602082028038833980820191505090505b50925060009150600090505b60038054905081101561164b5760016000868152602001908152602001600020600060038381548110151561153b57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561163e576003818154811015156115c257fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683838151811015156115fb57fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506001820191505b808060010191505061150a565b8160405190808252806020026020018201604052801561167a5781602001602082028038833980820191505090505b509350600090505b818110156116f957828181518110151561169857fe5b9060200190602002015184828151811015156116b057fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508080600101915050611682565b505050919050565b60055481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561174157600080fd5b60038054905081603282111580156117595750818111155b8015611766575060008114155b8015611773575060008214155b151561177e57600080fd5b826004819055507fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a836040518082815260200191505060405180910390a1505050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561181a57600080fd5b81600080600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415151561187657600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515156118e257600080fd5b600180600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef60405160405180910390a361199785611cdd565b5050505050565b60006119ab848484611f85565b90506119b6816117c1565b9392505050565b603281565b60045481565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515611a0457600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611a5d57600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515611ab757600080fd5b600092505b600380549050831015611ba0578473ffffffffffffffffffffffffffffffffffffffff16600384815481101515611aef57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611b935783600384815481101515611b4657fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611ba0565b8280600101935050611abc565b6000600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508473ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b600033600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611d3857600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611da357600080fd5b8460008082815260200190815260200160002060030160009054906101000a900460ff16151515611dd357600080fd5b611ddc86611020565b15611f7d57600080878152602001908152602001600020945060018560030160006101000a81548160ff021916908315150217905550611efa8560000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866001015487600201805460018160011615610100020316600290049050886002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611ef05780601f10611ec557610100808354040283529160200191611ef0565b820191906000526020600020905b815481529060010190602001808311611ed357829003601f168201915b50505050506120d7565b15611f3157857f33e13ecb54c3076d8e8bb8c2881800a4d972b792045ffae98fdf46df365fed7560405160405180910390a2611f7c565b857f526441bb6c1aba3c9a4a6ca1d6545da9c2333c8c48343ef398eb858d72b7923660405160405180910390a260008560030160006101000a81548160ff0219169083151502179055505b5b505050505050565b60008360008173ffffffffffffffffffffffffffffffffffffffff1614151515611fae57600080fd5b60055491506080604051908101604052808673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018481526020016000151581525060008084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010155604082015181600201908051906020019061206d92919061212a565b5060608201518160030160006101000a81548160ff0219169083151502179055509050506001600560008282540192505081905550817fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5160405160405180910390a2509392505050565b6000806040516020840160008287838a8c6187965a03f19250505080915050949350505050565b8154818355818111156121255781836000526020600020918201910161212491906121aa565b5b505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061216b57805160ff1916838001178555612199565b82800160010185558215612199579182015b8281111561219857825182559160200191906001019061217d565b5b5090506121a691906121aa565b5090565b6121cc91905b808211156121c85760008160009055506001016121b0565b5090565b905600a165627a7a72305820b0d992f257e70d565448b927a3ae764342039a864112d5d5bb1ac1bd52e4104e00290000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000cb1437200aea736788f1fc56f327c0456c3598d00000000000000000000000074dd76e24b2cfb43c1b1a4498295d553d0843746000000000000000000000000eeb4ceee443f9e0d17bdbd6daa241681ee5e51c2000000000000000000000000a005caea55375ae20e3aaef746113535503abc1900000000000000000000000090ae948bb410838bff9d024ed849df6a7267dacf000000000000000000000000740a5c5742833bed72489fe6bf7efc9b79428989" + }, + "0x120470000000000000000000000000000000000a": { + "balance": "10901790566666700000000000", + "constructor": "0x60806040523480156200001157600080fd5b50604051620024c8380380620024c883398101806040528101908080518201929190602001805190602001909291905050506000825182603282111580156200005a5750818111155b801562000068575060008114155b801562000076575060008214155b15156200008257600080fd5b600092505b8451831015620001bd57600260008685815181101515620000a457fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16158015620001335750600085848151811015156200011057fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1614155b15156200013f57600080fd5b60016002600087868151811015156200015457fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550828060010193505062000087565b8460039080519060200190620001d5929190620001e8565b50836004819055505050505050620002bd565b82805482825590600052602060002090810192821562000264579160200282015b82811115620002635782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019062000209565b5b50905062000273919062000277565b5090565b620002ba91905b80821115620002b657600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016200027e565b5090565b90565b6121fb80620002cd6000396000f30060806040526004361061011d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063025e7c2714610177578063173825d9146101e457806320ea8d86146102275780632f54bf6e146102545780633411c81c146102af57806354741525146103145780637065cb4814610363578063784547a7146103a65780638b51d13f146103eb5780639ace38c21461042c578063a0e67e2b14610517578063a8abe69a14610583578063b5dc40c314610627578063b77bf600146106a9578063ba51a6df146106d4578063c01a8c8414610701578063c64274741461072e578063d74f8edd146107d5578063dc8452cd14610800578063e20056e61461082b578063ee22610b1461088e575b6000341115610175573373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a25b005b34801561018357600080fd5b506101a2600480360381019080803590602001909291905050506108bb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156101f057600080fd5b50610225600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506108f9565b005b34801561023357600080fd5b5061025260048036038101908080359060200190929190505050610b92565b005b34801561026057600080fd5b50610295600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d3a565b604051808215151515815260200191505060405180910390f35b3480156102bb57600080fd5b506102fa60048036038101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d5a565b604051808215151515815260200191505060405180910390f35b34801561032057600080fd5b5061034d600480360381019080803515159060200190929190803515159060200190929190505050610d89565b6040518082815260200191505060405180910390f35b34801561036f57600080fd5b506103a4600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610e1b565b005b3480156103b257600080fd5b506103d160048036038101908080359060200190929190505050611020565b604051808215151515815260200191505060405180910390f35b3480156103f757600080fd5b5061041660048036038101908080359060200190929190505050611105565b6040518082815260200191505060405180910390f35b34801561043857600080fd5b50610457600480360381019080803590602001909291905050506111d0565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018060200183151515158152602001828103825284818151815260200191508051906020019080838360005b838110156104d95780820151818401526020810190506104be565b50505050905090810190601f1680156105065780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b34801561052357600080fd5b5061052c6112c5565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561056f578082015181840152602081019050610554565b505050509050019250505060405180910390f35b34801561058f57600080fd5b506105d06004803603810190808035906020019092919080359060200190929190803515159060200190929190803515159060200190929190505050611353565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156106135780820151818401526020810190506105f8565b505050509050019250505060405180910390f35b34801561063357600080fd5b50610652600480360381019080803590602001909291905050506114c4565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561069557808201518184015260208101905061067a565b505050509050019250505060405180910390f35b3480156106b557600080fd5b506106be611701565b6040518082815260200191505060405180910390f35b3480156106e057600080fd5b506106ff60048036038101908080359060200190929190505050611707565b005b34801561070d57600080fd5b5061072c600480360381019080803590602001909291905050506117c1565b005b34801561073a57600080fd5b506107bf600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929050505061199e565b6040518082815260200191505060405180910390f35b3480156107e157600080fd5b506107ea6119bd565b6040518082815260200191505060405180910390f35b34801561080c57600080fd5b506108156119c2565b6040518082815260200191505060405180910390f35b34801561083757600080fd5b5061088c600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506119c8565b005b34801561089a57600080fd5b506108b960048036038101908080359060200190929190505050611cdd565b005b6003818154811015156108ca57fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561093557600080fd5b81600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561098e57600080fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600091505b600160038054905003821015610b13578273ffffffffffffffffffffffffffffffffffffffff16600383815481101515610a2157fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610b06576003600160038054905003815481101515610a7f57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600383815481101515610ab957fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610b13565b81806001019250506109eb565b6001600381818054905003915081610b2b91906120fe565b506003805490506004541115610b4a57610b49600380549050611707565b5b8273ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a2505050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610beb57600080fd5b81336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610c5657600080fd5b8360008082815260200190815260200160002060030160009054906101000a900460ff16151515610c8657600080fd5b60006001600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e960405160405180910390a35050505050565b60026020528060005260406000206000915054906101000a900460ff1681565b60016020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b600080600090505b600554811015610e1457838015610dc8575060008082815260200190815260200160002060030160009054906101000a900460ff16155b80610dfb5750828015610dfa575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15610e07576001820191505b8080600101915050610d91565b5092915050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610e5557600080fd5b80600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515610eaf57600080fd5b8160008173ffffffffffffffffffffffffffffffffffffffff1614151515610ed657600080fd5b60016003805490500160045460328211158015610ef35750818111155b8015610f00575060008114155b8015610f0d575060008214155b1515610f1857600080fd5b6001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060038590806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508473ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b6000806000809150600090505b6003805490508110156110fd5760016000858152602001908152602001600020600060038381548110151561105e57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156110dd576001820191505b6004548214156110f057600192506110fe565b808060010191505061102d565b5b5050919050565b600080600090505b6003805490508110156111ca5760016000848152602001908152602001600020600060038381548110151561113e57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156111bd576001820191505b808060010191505061110d565b50919050565b60006020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690806001015490806002018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156112a85780601f1061127d576101008083540402835291602001916112a8565b820191906000526020600020905b81548152906001019060200180831161128b57829003601f168201915b5050505050908060030160009054906101000a900460ff16905084565b6060600380548060200260200160405190810160405280929190818152602001828054801561134957602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116112ff575b5050505050905090565b60608060008060055460405190808252806020026020018201604052801561138a5781602001602082028038833980820191505090505b50925060009150600090505b600554811015611436578580156113cd575060008082815260200190815260200160002060030160009054906101000a900460ff16155b8061140057508480156113ff575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b156114295780838381518110151561141457fe5b90602001906020020181815250506001820191505b8080600101915050611396565b8787036040519080825280602002602001820160405280156114675781602001602082028038833980820191505090505b5093508790505b868110156114b957828181518110151561148457fe5b906020019060200201518489830381518110151561149e57fe5b9060200190602002018181525050808060010191505061146e565b505050949350505050565b6060806000806003805490506040519080825280602002602001820160405280156114fe5781602001602082028038833980820191505090505b50925060009150600090505b60038054905081101561164b5760016000868152602001908152602001600020600060038381548110151561153b57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561163e576003818154811015156115c257fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683838151811015156115fb57fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506001820191505b808060010191505061150a565b8160405190808252806020026020018201604052801561167a5781602001602082028038833980820191505090505b509350600090505b818110156116f957828181518110151561169857fe5b9060200190602002015184828151811015156116b057fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508080600101915050611682565b505050919050565b60055481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561174157600080fd5b60038054905081603282111580156117595750818111155b8015611766575060008114155b8015611773575060008214155b151561177e57600080fd5b826004819055507fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a836040518082815260200191505060405180910390a1505050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561181a57600080fd5b81600080600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415151561187657600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515156118e257600080fd5b600180600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef60405160405180910390a361199785611cdd565b5050505050565b60006119ab848484611f85565b90506119b6816117c1565b9392505050565b603281565b60045481565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515611a0457600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611a5d57600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515611ab757600080fd5b600092505b600380549050831015611ba0578473ffffffffffffffffffffffffffffffffffffffff16600384815481101515611aef57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611b935783600384815481101515611b4657fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611ba0565b8280600101935050611abc565b6000600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508473ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b600033600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611d3857600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611da357600080fd5b8460008082815260200190815260200160002060030160009054906101000a900460ff16151515611dd357600080fd5b611ddc86611020565b15611f7d57600080878152602001908152602001600020945060018560030160006101000a81548160ff021916908315150217905550611efa8560000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866001015487600201805460018160011615610100020316600290049050886002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611ef05780601f10611ec557610100808354040283529160200191611ef0565b820191906000526020600020905b815481529060010190602001808311611ed357829003601f168201915b50505050506120d7565b15611f3157857f33e13ecb54c3076d8e8bb8c2881800a4d972b792045ffae98fdf46df365fed7560405160405180910390a2611f7c565b857f526441bb6c1aba3c9a4a6ca1d6545da9c2333c8c48343ef398eb858d72b7923660405160405180910390a260008560030160006101000a81548160ff0219169083151502179055505b5b505050505050565b60008360008173ffffffffffffffffffffffffffffffffffffffff1614151515611fae57600080fd5b60055491506080604051908101604052808673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018481526020016000151581525060008084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010155604082015181600201908051906020019061206d92919061212a565b5060608201518160030160006101000a81548160ff0219169083151502179055509050506001600560008282540192505081905550817fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5160405160405180910390a2509392505050565b6000806040516020840160008287838a8c6187965a03f19250505080915050949350505050565b8154818355818111156121255781836000526020600020918201910161212491906121aa565b5b505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061216b57805160ff1916838001178555612199565b82800160010185558215612199579182015b8281111561219857825182559160200191906001019061217d565b5b5090506121a691906121aa565b5090565b6121cc91905b808211156121c85760008160009055506001016121b0565b5090565b905600a165627a7a72305820b0d992f257e70d565448b927a3ae764342039a864112d5d5bb1ac1bd52e4104e00290000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000cb1437200aea736788f1fc56f327c0456c3598d00000000000000000000000074dd76e24b2cfb43c1b1a4498295d553d0843746000000000000000000000000eeb4ceee443f9e0d17bdbd6daa241681ee5e51c2000000000000000000000000a005caea55375ae20e3aaef746113535503abc1900000000000000000000000090ae948bb410838bff9d024ed849df6a7267dacf000000000000000000000000740a5c5742833bed72489fe6bf7efc9b79428989" + }, + "0x0cB1437200aea736788f1Fc56F327c0456c3598D": { + "balance": "250000000000000000" + }, + "0x74dd76E24B2CFB43C1b1a4498295d553D0843746": { + "balance": "250000000000000000" + }, + "0xeeB4CEEe443F9e0D17BdBD6Daa241681EE5E51c2": { + "balance": "250000000000000000" + }, + "0xA005caEa55375ae20e3aAEF746113535503ABC19": { + "balance": "250000000000000000" + }, + "0x90ae948bB410838bff9D024ed849dF6A7267Dacf": { + "balance": "250000000000000000" + }, + "0x740a5C5742833bEd72489fE6bf7EFc9B79428989": { + "balance": "250000000000000000" + }, + "0x1204700000000000000000000000000000000001": { + "constructor": "0x60806040523480156200001157600080fd5b5060405162002d7138038062002d71833981018060405260608110156200003757600080fd5b81019080805190602001909291908051906020019092919080516401000000008111156200006457600080fd5b828101905060208101848111156200007b57600080fd5b81518560208202830111640100000000821117156200009957600080fd5b5050929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415620001e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602481526020018062002d216024913960400191505060405180910390fd5b60018151101562000242576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c81526020018062002d45602c913960400191505060405180910390fd5b62000253836200046160201b60201c565b6200026482620005c360201b60201c565b60008090505b81518110156200040e57600073ffffffffffffffffffffffffffffffffffffffff168282815181106200029957fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614156200032c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f56616c696461746f7220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b6001600360008484815181106200033f57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff02191690836003811115620003a057fe5b02179055508060036000848481518110620003b757fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018190555080806001019150506200026a565b508060029080519060200190620004279291906200064a565b50600260019080546200043c929190620006d9565b506001600060146101000a81548160ff02191690831515021790555050505062000776565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141562000505576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b80600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167fe8ec518081a7aa1fc5d586a5443a858ab130be8b8e39b545172c879a7e242c6b60405160405180910390a250565b828054828255906000526020600020908101928215620006c6579160200282015b82811115620006c55782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550916020019190600101906200066b565b5b509050620006d5919062000730565b5090565b8280548282559060005260206000209081019282156200071d5760005260206000209182015b828111156200071c578254825591600101919060010190620006ff565b5b5090506200072c919062000730565b5090565b6200077391905b808211156200076f57600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555060010162000737565b5090565b90565b61259b80620007866000396000f3fe608060405234801561001057600080fd5b50600436106101375760003560e01c80639f723637116100b8578063b98049091161007c578063b980490914610562578063bd21442a146105be578063c805f68b14610681578063d826b7f1146106c5578063f2fde38b14610733578063f3aeac021461077757610137565b80639f723637146103dc578063a00745b61461043b578063a6940b0714610497578063b3f05b97146104e1578063b7ab4db51461050357610137565b8063455701d6116100ff578063455701d6146102c35780634d238c8e1461032257806375286211146103665780638da5cb5b146103705780638f32d59b146103ba57610137565b8063267fa41d1461013c57806334ba3c1b1461019857806340550a1c146101b657806340a141ff146102125780634183495514610256575b600080fd5b61017e6004803603602081101561015257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107d3565b604051808215151515815260200191505060405180910390f35b6101a0610845565b6040518082815260200191505060405180910390f35b6101f8600480360360208110156101cc57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610852565b604051808215151515815260200191505060405180910390f35b6102546004803603602081101561022857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610933565b005b6102986004803603602081101561026c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610a7e565b604051808360038111156102a857fe5b60ff1681526020018281526020019250505060405180910390f35b6102cb610aaf565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561030e5780820151818401526020810190506102f3565b505050509050019250505060405180910390f35b6103646004803603602081101561033857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b3d565b005b61036e610d47565b005b6103786111b2565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103c26111db565b604051808215151515815260200191505060405180910390f35b6103e4611232565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561042757808201518184015260208101905061040c565b505050509050019250505060405180910390f35b61047d6004803603602081101561045157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611361565b604051808215151515815260200191505060405180910390f35b61049f611442565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6104e9611468565b604051808215151515815260200191505060405180910390f35b61050b61147b565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561054e578082015181840152602081019050610533565b505050509050019250505060405180910390f35b6105a46004803603602081101561057857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611509565b604051808215151515815260200191505060405180910390f35b61067f600480360360808110156105d457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561063b57600080fd5b82018360208201111561064d57600080fd5b8035906020019184600183028401116401000000008311171561066f57600080fd5b909192939192939050505061157a565b005b6106c36004803603602081101561069757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506117d6565b005b610731600480360360608110156106db57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611989565b005b6107756004803603602081101561074957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611be3565b005b6107b96004803603602081101561078d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611c69565b604051808215151515815260200191505060405180910390f35b6000600160038111156107e257fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16600381111561083d57fe5b149050919050565b6000600180549050905090565b60006001600381111561086157fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff1660038111156108bc57fe5b148061092c57506003808111156108cf57fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16600381111561092a57fe5b145b9050919050565b61093b6111db565b6109ad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600060149054906101000a900460ff16610a12576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806124e16022913960400191505060405180910390fd5b80610a1c81610852565b610a71576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806124bf6022913960400191505060405180910390fd5b610a7a82611cdb565b5050565b60036020528060005260406000206000915090508060000160009054906101000a900460ff16908060010154905082565b60606002805480602002602001604051908101604052809291908181526020018280548015610b3357602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610ae9575b5050505050905090565b610b456111db565b610bb7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600060149054906101000a900460ff16610c1c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806124e16022913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610cbf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f56616c696461746f7220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b610cc881610852565b15610d3b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f546869732076616c696461746f7220697320616c72656164792061637469766581525060200191505060405180910390fd5b610d4481611f2e565b50565b600060149054906101000a900460ff1615610dca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f56616c696461746f72207365742069732066696e616c697a656400000000000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610e8d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f74207468652052656c617920636f6e747261637481525060200191505060405180910390fd5b6001600060146101000a81548160ff021916908315150217905550600073ffffffffffffffffffffffffffffffffffffffff16600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610fbe57600060036000600260016002805490500381548110610f1a57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060018160000160006101000a81548160ff02191690836003811115610fa257fe5b02179055506001600280549050038160010181905550506110f1565b600060036000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff0219169083600381111561103f57fe5b0217905550600060036000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101819055506000600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b6002600190805461110392919061238d565b507f8564cd629b15f47dc310d45bcbfc9bcf5420b0d51bf0659a16c67f91d27632536001604051808060200182810382528381815481526020019150805480156111a257602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311611158575b50509250505060405180910390a1565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b606060018054905060028054905011156112d45760028054806020026020016040519081016040528092919081815260200182805480156112c857602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001906001019080831161127e575b5050505050905061135e565b600180548060200260200160405190810160405280929190818152602001828054801561135657602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001906001019080831161130c575b505050505090505b90565b60006002600381111561137057fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff1660038111156113cb57fe5b148061143b57506003808111156113de57fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16600381111561143957fe5b145b9050919050565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060149054906101000a900460ff1681565b606060018054806020026020016040519081016040528092919081815260200182805480156114ff57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116114b5575b5050505050905090565b600060038081111561151757fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16600381111561157257fe5b149050919050565b8461158481610852565b6115d9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806124bf6022913960400191505060405180910390fd5b846115e381610852565b611638576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806124bf6022913960400191505060405180910390fd5b844381106116ae576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f426c6f636b206e756d626572206973206e6f742076616c69640000000000000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611771576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f74207468652052656c617920636f6e747261637481525060200191505060405180910390fd5b858773ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff167f729a19138e072a5a8d3a56d74ae0b5c84530f09aacd6e12b24c5b2fdc3f8a3d060405160405180910390a45050505050505050565b6117de6111db565b611850576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156118d6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260248152602001806124746024913960400191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561197d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260408152602001806125036040913960400191505060405180910390fd5b61198681612003565b50565b8261199381610852565b6119e8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806124bf6022913960400191505060405180910390fd5b826119f281610852565b611a47576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806124bf6022913960400191505060405180910390fd5b82438110611abd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f426c6f636b206e756d626572206973206e6f742076616c69640000000000000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611b80576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f74207468652052656c617920636f6e747261637481525060200191505060405180910390fd5b838573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fbc459bd9db54016b1966d0fe812bbe0a82cd627ae3eacd01727dc63a432ca41b60405160405180910390a4505050505050565b611beb6111db565b611c5d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b611c668161208a565b50565b600060026003811115611c7857fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff166003811115611cd357fe5b149050919050565b600160028054905011611d39576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260278152602001806124986027913960400191505060405180910390fd5b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010154905060006001600280549050039050600060028281548110611d9c57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508060028481548110611dd757fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101819055506002805480919060019003611e7b91906123df565b5060038060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff02191690836003811115611eda57fe5b021790555083600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611f286121eb565b50505050565b6002600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff02191690836003811115611f8d57fe5b021790555060028190806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506120006121eb565b50565b80600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167fe8ec518081a7aa1fc5d586a5443a858ab130be8b8e39b545172c879a7e242c6b60405160405180910390a250565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561212d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008060146101000a81548160ff021916908315150217905550600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a084718a600143034060026040518363ffffffff1660e01b8152600401808381526020018060200182810382528381815481526020019150805480156122da57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311612290575b50509350505050602060405180830381600087803b1580156122fb57600080fd5b505af115801561230f573d6000803e3d6000fd5b505050506040513d602081101561232557600080fd5b810190808051906020019092919050505061238b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602d815260200180612543602d913960400191505060405180910390fd5b565b8280548282559060005260206000209081019282156123ce5760005260206000209182015b828111156123cd5782548255916001019190600101906123b2565b5b5090506123db919061240b565b5090565b81548183558181111561240657818360005260206000209182019101612405919061244e565b5b505050565b61244b91905b8082111561244757600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550600101612411565b5090565b90565b61247091905b8082111561246c576000816000905550600101612454565b5090565b9056fe52656c617920636f6e747261637420616464726573732063616e6e6f74206265203078305468657265206d757374206265206174206c6561737420312076616c696461746f72206c65667441646472657373206973206e6f7420616e206163746976652076616c696461746f7256616c696461746f7220736574206973206e6f742066696e616c697a6564207965744e65772072656c617920636f6e747261637420616464726573732063616e6e6f74206265207468652073616d65206173207468652063757272656e74206f6e6552656c617920636f6e747261637420496e6974696174654368616e67652063616c6c6261636b206661696c6564a165627a7a72305820f9ae418c0431b0892a4b904426be15d82d8486d433a3d6b9814bd5d1a377bb30002952656c617920636f6e747261637420616464726573732063616e6e6f74206265203078305468657265206d757374206265206174206c6561737420312076616c696461746f7220696e697469616c6c790000000000000000000000001204700000000000000000000000000000000005000000000000000000000000120470000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000003000000000000000000000000d65b4c25a4ce1e024ff13425df1e0e574a1a0e9b00000000000000000000000083329c3fd90d7ee2efd546e0dc6453e9172a0643000000000000000000000000de15831ac319dab5eae5fd1fd9d52876c5e50f20" + }, + "0x1204700000000000000000000000000000000000": { + "constructor": "0x608060405273fffffffffffffffffffffffffffffffffffffffe600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555034801561006557600080fd5b506040516040806114d48339810180604052604081101561008557600080fd5b810190808051906020019092919080519060200190929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a361016b8261018160201b60201c565b61017a816102e260201b60201c565b50506104f4565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610224576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b80600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610386576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f416464726573732063616e6e6f7420626520307830000000000000000000000081525060200191505060405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561042d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260428152602001806114926042913960600191505060405180910390fd5b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905082600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f4fea88aaf04c303804bb211ecc32a00ac8e5f0656bb854cad8a4a2e438256b7460405160405180910390a3505050565b610f8f806105036000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c8063b7ab4db511610071578063b7ab4db514610209578063bd96567714610268578063c476dd40146102ac578063d3e848f11461034f578063d69f13bb14610399578063f2fde38b146103e7576100a9565b806375286211146100ae5780638da5cb5b146100b85780638f32d59b14610102578063a084718a14610124578063ae3783d6146101bf575b600080fd5b6100b661042b565b005b6100c0610572565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61010a61059b565b604051808215151515815260200191505060405180910390f35b6101a56004803603604081101561013a57600080fd5b81019080803590602001909291908035906020019064010000000081111561016157600080fd5b82018360208201111561017357600080fd5b8035906020019184602083028401116401000000008311171561019557600080fd5b90919293919293905050506105f2565b604051808215151515815260200191505060405180910390f35b6101c761070c565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610211610732565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610254578082015181840152602081019050610239565b505050509050019250505060405180910390f35b6102aa6004803603602081101561027e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610832565b005b61034d600480360360608110156102c257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561030957600080fd5b82018360208201111561031b57600080fd5b8035906020019184600183028401116401000000008311171561033d57600080fd5b90919293919293905050506108b8565b005b6103576109e7565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103e5600480360360408110156103af57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a0d565b005b610429600480360360208110156103fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b06565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104ee576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f53656e646572206973206e6f742073797374656d00000000000000000000000081525060200191505060405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663752862116040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561055857600080fd5b505af115801561056c573d6000803e3d6000fd5b50505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461069a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180610f426022913960400191505060405180910390fd5b837f55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c89848460405180806020018281038252848482818152602001925060200280828437600081840152601f19601f820116905080830192505050935050505060405180910390a2600190509392505050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b7ab4db56040518163ffffffff1660e01b815260040160006040518083038186803b15801561079c57600080fd5b505afa1580156107b0573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060208110156107da57600080fd5b8101908080516401000000008111156107f257600080fd5b8281019050602081018481111561080857600080fd5b815185602082028301116401000000008211171561082557600080fd5b5050929190505050905090565b61083a61059b565b6108ac576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b6108b581610b8c565b50565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663bd21442a33868686866040518663ffffffff1660e01b8152600401808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509650505050505050600060405180830381600087803b1580156109c957600080fd5b505af11580156109dd573d6000803e3d6000fd5b5050505050505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d826b7f13384846040518463ffffffff1660e01b8152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019350505050600060405180830381600087803b158015610aea57600080fd5b505af1158015610afe573d6000803e3d6000fd5b505050505050565b610b0e61059b565b610b80576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b610b8981610d9e565b50565b80600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610c30576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f416464726573732063616e6e6f7420626520307830000000000000000000000081525060200191505060405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610cd7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526042815260200180610f006042913960600191505060405180910390fd5b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905082600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f4fea88aaf04c303804bb211ecc32a00ac8e5f0656bb854cad8a4a2e438256b7460405160405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610e41576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fe4e65772072656c6179656420636f6e747261637420616464726573732063616e6e6f74206265207468652073616d65206173207468652063757272656e74206f6e6553656e646572206973206e6f74207468652052656c6179656420636f6e7472616374a165627a7a723058204045b88d4bc25183a0d657cfec6f8f8b976fd6aca20ab02e07cc6d76f43e7bd100294e65772072656c6179656420636f6e747261637420616464726573732063616e6e6f74206265207468652073616d65206173207468652063757272656e74206f6e6500000000000000000000000012047000000000000000000000000000000000050000000000000000000000001204700000000000000000000000000000000001" + }, + "0x1204700000000000000000000000000000000002": { + "constructor": "0x608060405273fffffffffffffffffffffffffffffffffffffffe600960006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503480156200006657600080fd5b5060405160408062001bad833981018060405260408110156200008857600080fd5b810190808051906020019092919080519060200190929190505050620000b36200016360201b60201c565b60786000805490501462000113576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018062001b866027913960400191505060405180910390fd5b81600b60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600a81905550505062000878565b60405180610f0001604052806704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704395680a6af46808152602001670438cfa9de4a0e808152602001670437eeee904c06808152602001670436b44ebcb52e8081526020016704351fca638586808152602001670433316184bd0e808152602001670430e914205bc680815260200167042e46e23661ae80815260200167042b4acbc6cec6808152602001670427f4d0d1a30e80815260200167042444f156de868081526020016704203b2d56812e80815260200167041bd784d08b0680815260200167041719f7c4fc0e808152602001670412028633d44680815260200167040c91301d13ae808152602001670406c5f580ba46808152602001670400a0d65ec80e8081526020016703fa21d2b73d068081526020016703f348ea8a192e8081526020016703ec161dd75c868081526020016703e4896c9f070e8081526020016703dca2d6e118c68081526020016703d4625c9d91ae8081526020016703cbc7fdd471c68081526020016703c2d3ba85b90e8081526020016703b98592b167868081526020016703afdd86577d2e8081526020016703a5db9577fa0680815260200167039b7fc012de0e808152602001670390ca06282946808152602001670385ba67b7dbae80815260200167037a50e4c1f54680815260200167036e8d7d46760e8081526020016703627031455e06808152602001670355f900bead2e80815260200167034927ebb2638680815260200167033bfcf220810e80815260200167032e78140905c680815260200167032099516bf1ae80815260200167031260aa4944c6808152602001670303ce1ea0ff0e8081526020016702f4e1ae7320868081526020016702e59b59bfa92e8081526020016702d5fb208699068081526020016702c60102c7f00e8081526020016702b5ad0083ae468081526020016702a4ff19b9d3ae808152602001670293f74e6a6046808152602001670282959e95540e808152602001670270da0a3aaf0680815260200167025ec4915a712e80815260200167024c5533f49a86808152602001670238da1d6b04400081526020016702260c5cdf4d240081526020016702138f83989f90008152602001670201639196fb840081526020016701ef8886da61000081526020016701ddfe6362d0040081526020016701ccc5273048900081526020016701bbdcd242caa40081526020016701ab45649a564000815260200167019afede36eb6400815260200167018b093f188a1000815260200167017b64873f324400815260200167016c10b6aae40000815260200167015d0dcd5b9f4400815260200167014e5bcb51641000815260200167013ffab08c3264008152602001670131ea7d0c0a400081526020016701242b30d0eba4008152602001670116bccbdad6900081526020016701099f4e29cb0400815260200166fcd2b7bdc90000815260200166f0570896d08400815260200166e42c40b4e19000815260200166d8526017fc2400815260200166ccc966c0204000815260200166c19154ad4de400815260200166b6aa29df851000815260200166ac13e656c5c400815260200166a1ce8a1310000081526020016697da151463c4008152602001668e36875ac1100081526020016684e3e0e627e4008152602001667be221b6984000815260200166733149cc1224008152602001666ad1592695900081526020016662c24fc62284008152602001665b042daab900008152602001665396f2d45904008152602001664c7a9f4302900081526020016645af32f6b5a4008152602001663f34adef724000815260200166390b102d386400815260200166333259b00810008152602001662daa8a77e144008152602001662873a284c40000815260200166238da1d6b044008152602001661ef8886da610008152602001661ab45649a5640081526020016616c10b6aae4000815260200166131ea7d0c0a4008152602001660fcd2b7bdc90008152602001660ccc966c0204008152602001660a1ce8a131000081526020016607be221b69840081526020016605b042daab900081526020016603f34adef7240081526020016602873a284c4000815260200166016c10b6aae400815260200165a1ce8a1310008152602001652873a284c4008152506000906078620007e5929190620007fe565b5062080520600181905550607860015402600281905550565b8280548282559060005260206000209081019282156200083d579160200282015b828111156200083c5782518255916020019190600101906200081f565b5b5090506200084c919062000850565b5090565b6200087591905b808211156200087157600081600090555060010162000857565b5090565b90565b6112fe80620008886000396000f3fe608060405234801561001057600080fd5b50600436106101205760003560e01c80634476d66a116100ad57806394f7f62b1161007157806394f7f62b1461045d578063df6a50301461049f578063e6e100db146104bd578063f91c289814610515578063fb1ac5251461068057610120565b80634476d66a146103515780634f4013fb14610393578063553a5c85146103b157806358ceb672146103cf57806360d4b8be146103d957610120565b80631a488047116100f45780631a488047146101f157806330f6eb161461020f57806333ea51a81461027157806337339a16146102b55780633d84b8c1146102f957610120565b8062f380f414610125578063078d8e7a1461016f5780630f411cdb1461019157806318129375146101af575b600080fd5b61012d61069e565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101776106c4565b604051808215151515815260200191505060405180910390f35b6101996106d4565b6040518082815260200191505060405180910390f35b6101db600480360360208110156101c557600080fd5b81019080803590602001909291905050506106da565b6040518082815260200191505060405180910390f35b6101f96106fb565b6040518082815260200191505060405180910390f35b61025b6004803603604081101561022557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610701565b6040518082815260200191505060405180910390f35b6102b36004803603602081101561028757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610726565b005b6102f7600480360360208110156102cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107a7565b005b61033b6004803603602081101561030f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506108ae565b6040518082815260200191505060405180910390f35b61037d6004803603602081101561036757600080fd5b81019080803590602001909291905050506108c6565b6040518082815260200191505060405180910390f35b61039b6108de565b6040518082815260200191505060405180910390f35b6103b96108e3565b6040518082815260200191505060405180910390f35b6103d76108e9565b005b61041b600480360360208110156103ef57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061094d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6104896004803603602081101561047357600080fd5b8101908080359060200190929190505050610980565b6040518082815260200191505060405180910390f35b6104a76109c4565b6040518082815260200191505060405180910390f35b6104ff600480360360208110156104d357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109ca565b6040518082815260200191505060405180910390f35b6105e16004803603604081101561052b57600080fd5b810190808035906020019064010000000081111561054857600080fd5b82018360208201111561055a57600080fd5b8035906020019184602083028401116401000000008311171561057c57600080fd5b90919293919293908035906020019064010000000081111561059d57600080fd5b8201836020820111156105af57600080fd5b803590602001918460208302840111640100000000831117156105d157600080fd5b90919293919293905050506109e2565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561062857808201518184015260208101905061060d565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561066a57808201518184015260208101905061064f565b5050505090500194505050505060405180910390f35b610688610edb565b6040518082815260200191505060405180910390f35b600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006106cf43610ee1565b905090565b60025481565b600081815481106106e757fe5b906000526020600020016000915090505481565b600a5481565b6008602052816000526040600020602052806000526040600020600091509150505481565b80600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461086a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f742074686520636f6d6d756e6974792066756e6481525060200191505060405180910390fd5b80600b60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60066020528060005260406000206000915090505481565b60076020528060005260406000206000915090505481565b607881565b60035481565b600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055565b600c6020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600061098b82610ee1565b1561099957600090506109bf565b600060015483816109a657fe5b04815481106109b157fe5b906000526020600020015490505b919050565b60045481565b60056020528060005260406000206000915090505481565b606080600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610aa8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f43616c6c6572206973206e6f74207468652073797374656d000000000000000081525060200191505060405180910390fd5b838390508686905014610b06576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260258152602001806112ae6025913960400191505060405180910390fd5b60018686905014610b7f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f42656e65666163746f7273206c697374206c656e677468206973206e6f74203181525060200191505060405180910390fd5b600084846000818110610b8e57fe5b9050602002013561ffff1661ffff1614610bf3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061128c6022913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff1686866000818110610c1857fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161480610c5c5750610c5b43610ee1565b5b15610cd2576000604051908082528060200260200182016040528015610c915781602001602082028038833980820191505090505b506000604051908082528060200260200182016040528015610cc25781602001602082028038833980820191505090505b5081915080905091509150610ed2565b60606002604051908082528060200260200182016040528015610d045781602001602082028038833980820191505090505b50905060608151604051908082528060200260200182016040528015610d395781602001602082028038833980820191505090505b509050610d6e88886000818110610d4c57fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff16610ef0565b82600081518110610d7b57fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050610dbe43610980565b81600081518110610dcb57fe5b602002602001018181525050610e02600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16610ef0565b82600181518110610e0f57fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600a5481600181518110610e5957fe5b602002602001018181525050610e9782600081518110610e7557fe5b602002602001015182600081518110610e8a57fe5b6020026020010151610f9d565b610ec982600181518110610ea757fe5b602002602001015182600181518110610ebc57fe5b6020026020010151611145565b81819350935050505b94509492505050565b60015481565b60006002548210159050919050565b600080600c60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610f935782915050610f98565b809150505b919050565b61100081600860008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060004381526020019081526020016000205461120390919063ffffffff16565b600860008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000438152602001908152602001600020819055506110a681600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461120390919063ffffffff16565b600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061110f81600760004381526020019081526020016000205461120390919063ffffffff16565b600760004381526020019081526020016000208190555061113b8160035461120390919063ffffffff16565b6003819055505050565b61115a8160045461120390919063ffffffff16565b6004819055506111b281600560008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461120390919063ffffffff16565b600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506111ff8282610f9d565b5050565b600080828401905083811015611281576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f4f766572666c6f77206572726f7200000000000000000000000000000000000081525060200191505060405180910390fd5b809150509291505056fe42656e65666163746f72206973206e6f742074686520626c6f636b20617574686f7242656e65666163746f72732f7479706573206c697374206c656e6774682064696666657273a165627a7a72305820ef1c2590551666e2ac227034529ee69c33ec91868d23c0a0a887d514094ec6030029526577617264206375727665206973206e6f7420746865207265717569726564206c656e67746800000000000000000000000012047000000000000000000000000000000000030000000000000000000000000000000000000000000000000856d3dfb6e26d00" + }, + "0x1204700000000000000000000000000000000004": { + "balance": "41198207933333333690000000", + "constructor": "0x608060405260006001556a22140f53c2d216263ba2803073ffffffffffffffffffffffffffffffffffffffff16311462000085576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806200150b6023913960400191505060405180910390fd5b620000956200010260201b60201c565b6a22140f53c2d216263ba28060015414620000fc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180620014ba6028913960400191505060405180910390fd5b62001133565b62000136722d4606b65c033769968bcdc63881b90b0853f569264876ff9c85a7a8ee00635df22bc06200105960201b60201c565b6200016a726e371c454a2d081f3966c180ba2c6165d87de66901c3c03b52c63f027880635df22bc06200105960201b60201c565b6200019e72c2f65230815d30eaa1a4d057bcf0b72fe3cc4e6902a5a058fc295ed00000635df22bc06200105960201b60201c565b620001d37303cccdc799d4dc37d56e3f9dba7f9c210fa1086f6902a5a058fc295ed00000635df22bc06200105960201b60201c565b6200020873047d955877a55fbdac768573a9259f29b103a0666902a5a058fc295ed00000635df22bc06200105960201b60201c565b6200023d7306920bb91f7027176cf373996d39b539ba436d876969e10de76676d0800000635d79dee06200105960201b60201c565b620002727306fdb93aa64f33a8fb40a36c462a3f7a074d632c6901c3c03b52c63f027880635df22bc06200105960201b60201c565b620002a7730ad7ba4af33b485e6f2505c417554631a3e5643f6902a5a058fc295ed00000635df22bc06200105960201b60201c565b620002dc730dd959deb4c458cc2ac379898bf2c99f7a8f399b6901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000311731153818a2eb49f0a71b27313c32814fc02e4db50694220bb939da668600000635df22bc06200105960201b60201c565b6200034773120470000000000000000000000000000000000a6a084595161401484a000000635d79dee06200105960201b60201c565b6200037c7315696134ebeed360dc90dc97ddd00bd07e1c11e96934f086f3b33b68400000635df22bc06200105960201b60201c565b620003b1731f0c30b1aa4c468b5beb02bac8df8f27617a2296692346646590efbaf71200635df22bc06200105960201b60201c565b620003e6732540ded041b6fedc0ff6f0cf26b891ec97c954006901c3c03b52c63f027880635df22bc06200105960201b60201c565b6200041b7325ae7b45d8646580dfcae403d29164729eb8642f691a784379d99db4200000635df22bc06200105960201b60201c565b6200045073349ebc5a6e853df121c84e999081e5992928e64f6934f086f3b33b68400000635df22bc06200105960201b60201c565b62000485733885d15573e45228dd54cd4fde9bfac64d702ed46901c3c03b52c63f027880635df22bc06200105960201b60201c565b620004ba733a9d83766c03c465851a38daa364ef7deccd1ece6934f086f3b33b68400000635df22bc06200105960201b60201c565b620004ef733abaa3f24428d6028f5a7fc5b18ce9d04ccec2296902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000524733c9f867d9b3a595987e198786fa9ab722e5c2f9b6934f086f3b33b68400000635df22bc06200105960201b60201c565b62000559733f11b4ad17fde4695cad64e109ae92a679d87bfc6969e10de76676d0800000635d79dee06200105960201b60201c565b6200058e733f12af735238c6e2fa45efb5b2f3fae82df4c9226901c3c03b52c63f027880635df22bc06200105960201b60201c565b620005c373404bb9c13364522133b363d5c4adb7a88056b19d6902d2cd2bb7a39658b500635df22bc06200105960201b60201c565b620005f873428ab4b019ee3a9b9863b2b4bf1885ce6dff9a736902a5a058fc295ed00000635df22bc06200105960201b60201c565b6200062d7347428fc08e56388372e7c81ad4a1140d932d10966969e10de76676d0800000635d79dee06200105960201b60201c565b620006627348ee57faf61c0b963113e7921e6173629e6bc4436902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000697734d0aa1c3459bf41e3ad4e4f40bbf029cb5723d836934f086f3b33b68400000635df22bc06200105960201b60201c565b620006cc7357f33efad76d4b783cf42c9e6cb08f4425dfe96e6902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000701735b3fb4e1d6040615f3e681bec4c80b5d7c9580716901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000736735fbb9c482034d287c5b3848fc2f9272abdd5bfa26903878076a58c80674b00635df22bc06200105960201b60201c565b6200076b73656e5569bef7781bf0db199d32027766053501ff69438ec266600555e00000635df22bc06200105960201b60201c565b620007a073664f991cdb2ffe6b6a568ede65b0208dbcce6f72691a784379d99db4200000635df22bc06200105960201b60201c565b620007d67369af0912dd44dce2b2373db4021788cbad84ff356a0422ca8b0a00a4250000006360c3f9006200105960201b60201c565b6200080b736a0a5da2a48ea87c2a906c53b3373642c29a4b6c6934f086f3b33b68400000635df22bc06200105960201b60201c565b62000840736cf32cc52e220c023c2d92b1d62310f46a6e2a136901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000875736d516767e4068fc331bdb331fba7578bdb07a68c69234b04a2777d0408ee00635df22bc06200105960201b60201c565b620008aa736dd10e41a7a84fe23ab35fefa2f46c9895f87a2d693c8c4bde2deef1680000635df22bc06200105960201b60201c565b620008df737030892dbf9c2048e796296dda597f145754a1856969e10de76676d0800000635d79dee06200105960201b60201c565b62000914737ed62cf71d519d3bf293ef90829508f92f4ccccb6902a5a058fc295ed00000635df22bc06200105960201b60201c565b6200094a73871ba4266793ad11da537d4857de7ad49eab662b6a017293b0a9e69fd9c00000635df22bc06200105960201b60201c565b6200097f73880e8b0ece0171edd0247f8d13d348d77a6b9b296903878076a58c80674b00635df22bc06200105960201b60201c565b620009b373887f2b16847248bc757b69f3c695f24ff344daf268e9062e03b5c2908780635df22bc06200105960201b60201c565b620009e8738c994ada51d35b8519424368807fb99c103366866969e10de76676d0800000635d79dee06200105960201b60201c565b62000a1d739196e46d664ceda55cb45a2cc5ab5bd1b7e614e26902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000a5273943c85b13f24083ec73815f7ba763b7c42ae02886902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000a87739467b762550673f08b14423f8562048d5e3694226902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000abc73949423db1bfee1ddec99c9d24a12a6ea27cb34896901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000af17396a5eb172efdf262ed6beaaf0e20c6af71831fc96934f086f3b33b68400000635df22bc06200105960201b60201c565b62000b2673a69dca0814eaadc89b6dbe94c5e2110497690f6c6903878076a58c80674b00635df22bc06200105960201b60201c565b62000b5b73a720a8ee90f5013cae9bf7bcac1d153e42815454691a784379d99db4200000635df22bc06200105960201b60201c565b62000b9073b080454f190e76eb8e719560fa8cae50c71bcea96901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000bc573b476ee7d610dae7b23b671ebc7bd6112e97729696902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000bfb73b561618a3ea959a5e363643b267c4cb8fe4b1df76a0422ca8b0a00a4250000006360c3f9006200105960201b60201c565b62000c3073b5b6d8885fbf28f843cc7886de242b811d6952056901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000c6573b61c11b6e42d459efaee8995c44db08507e468e169477d5529f68a63000000635df22bc06200105960201b60201c565b62000c9a73b999004b49c6b907d4278067da5c85195dcd7fc769081e0dddaff653f8b500635df22bc06200105960201b60201c565b62000ccf73be4888c5b021e5f16cd254de2d4eaf17625685c46934f086f3b33b68400000635df22bc06200105960201b60201c565b62000d0473c1d441a2ad43af7b4a3d8e3200d2ceb3a973099d6934f086f3b33b68400000635df22bc06200105960201b60201c565b62000d3973c58a20e290e858542d8e8bb07b600aeb9195fe306903878076a58c80674b00635df22bc06200105960201b60201c565b62000d6f73cfe7964b0b6412b013dc019bdf3afef58be565936a055084cc99f0bdbadd0000635df22bc06200105960201b60201c565b62000da473d33d4f83e85c92e0b53ffe4fc0e18b0e3632c0976901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000dd973d44fb8de580d34f44789408cc9335c9a9ce0ce4d691ad0235eb930a0540000635df22bc06200105960201b60201c565b62000e0e73dacd80d8e1d4f117515caa477ee7599cdfc766196902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000e4373db6cc57168c07b83a00f1f8871538446068824fc691a784379d99db4200000635df22bc06200105960201b60201c565b62000e7873de6b493d368316b9078454e37dce4968482dfbe96969e10de76676d0800000635d79dee06200105960201b60201c565b62000ead73e23c7cb60189bb2fd60625d2c2747b1e68f107766934f086f3b33b68400000635df22bc06200105960201b60201c565b62000ee273e6e8a111c89b05337049de9349c7c4880a396ef1691a784379d99db4200000635df22bc06200105960201b60201c565b62000f1773ebbddf28bf3224791b0510a2ab8813f182fe4e2b6901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000f4c73f4e31018a926f64cb780cb9f5f027377bcfb26fc6907f0e10af47c1c700000635df22bc06200105960201b60201c565b62000f8173f8e6ecb4b0f17576525749bdf85524652cbf002e6901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000fb773fd679097fe0f914642af9857e5799332fe2efa296a01a784379d99db42000000635d79dee06200105960201b60201c565b62000fed73fd7a30d3c2bd017a458610274c275059d308b2e76a01ff1675f219f5a8780000635df22bc06200105960201b60201c565b6200102273ffcf98c62c1bad480ab6846717b173a72e2dd090691a784379d99db4200000635df22bc06200105960201b60201c565b6200105773ffd9b871df6e93803c0877e98fc1722b39c00d786902a5a058fc295ed00000635df22bc06200105960201b60201c565b565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060008160000154148015620010b4575060008160010154145b6200110b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526029815260200180620014e26029913960400191505060405180910390fd5b8260016000828254019250508190555082816000018190555081816001018190555050505050565b61037780620011436000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806318a5bbdc14610051578063192e7a7b146100b057806330f0dbe0146100f45780639976e12f14610112575b600080fd5b6100936004803603602081101561006757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610130565b604051808381526020018281526020019250505060405180910390f35b6100f2600480360360208110156100c657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610154565b005b6100fc610336565b6040518082815260200191505060405180910390f35b61011a610345565b6040518082815260200191505060405180910390f35b60006020528060005260406000206000915090508060000154908060010154905082565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506000816000015411610210576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f417661696c61626c6520616d6f756e742069732030000000000000000000000081525060200191505060405180910390fd5b80600101544211610289576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f486f6c64696e6720706572696f64206973206e6f74206f76657200000000000081525060200191505060405180910390fd5b600081600001549050600082600001819055508273ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501580156102e2573d6000803e3d6000fd5b508273ffffffffffffffffffffffffffffffffffffffff167f221c08a06b07a64803b3787861a3f276212fcccb51c2e6234077a9b8cb13047a826040518082815260200191505060405180910390a2505050565b6a22140f53c2d216263ba28081565b6001548156fea165627a7a72305820b0165b18a29fae78ec1f58bf69134a67ab02e21d059e757412318d8362284866002954617267657420616d6f756e742073686f756c6420657175616c2061637475616c20616d6f756e74486f6c64696e6720666f72207468697320616464726573732077617320616c7265616479207365742e42616c616e63652073686f756c6420657175616c2074617267657420616d6f756e742e" + }, + "0x1204700000000000000000000000000000000006": { + "constructor": "0x60806040523480156200001157600080fd5b5060405160208062003ed2833981018060405260208110156200003357600080fd5b8101908080519060200190929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a362000111816200011860201b60201c565b506200027a565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415620001bc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b613c48806200028a6000396000f3fe608060405234801561001057600080fd5b50600436106101585760003560e01c806392698814116100c3578063e30bd7401161007c578063e30bd740146109b5578063eadf976014610a72578063ef5454d614610b17578063f25eb5c114610bc8578063f2fde38b14610bd2578063f6d339e414610c1657610158565b80639269881414610705578063ac4e73f91461074b578063ac72c120146107fc578063c3a3582514610842578063deb931a2146108d9578063df57b7421461094757610158565b80636795dbcd116101155780636795dbcd1461041c5780636a1acc3f146104df57806379ce9fac1461059c5780638da5cb5b146106025780638f32d59b1461064c57806390b97fc11461066e57610158565b806306b2ff471461015d57806319362a28146101b9578063267b69221461025e5780633f3935d1146102ff578063432ced04146103905780634f39ca59146103d6575b600080fd5b61019f6004803603602081101561017357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cd1565b604051808215151515815260200191505060405180910390f35b610244600480360360608110156101cf57600080fd5b8101908080359060200190929190803590602001906401000000008111156101f657600080fd5b82018360208201111561020857600080fd5b8035906020019184600183028401116401000000008311171561022a57600080fd5b909192939192939080359060200190929190505050610d31565b604051808215151515815260200191505060405180910390f35b61028a6004803603602081101561027457600080fd5b8101908080359060200190929190505050610fcb565b604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390f35b6103766004803603602081101561031557600080fd5b810190808035906020019064010000000081111561033257600080fd5b82018360208201111561034457600080fd5b8035906020019184600183028401116401000000008311171561036657600080fd5b909192939192939050505061102f565b604051808215151515815260200191505060405180910390f35b6103bc600480360360208110156103a657600080fd5b810190808035906020019092919050505061134e565b604051808215151515815260200191505060405180910390f35b610402600480360360208110156103ec57600080fd5b8101908080359060200190929190505050611546565b604051808215151515815260200191505060405180910390f35b61049d6004803603604081101561043257600080fd5b81019080803590602001909291908035906020019064010000000081111561045957600080fd5b82018360208201111561046b57600080fd5b8035906020019184600183028401116401000000008311171561048d57600080fd5b9091929391929390505050611a8d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610521600480360360208110156104f557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611bb2565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610561578082015181840152602081019050610546565b50505050905090810190601f16801561058e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6105e8600480360360408110156105b257600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611c62565b604051808215151515815260200191505060405180910390f35b61060a611f58565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610654611f81565b604051808215151515815260200191505060405180910390f35b6106ef6004803603604081101561068457600080fd5b8101908080359060200190929190803590602001906401000000008111156106ab57600080fd5b8201836020820111156106bd57600080fd5b803590602001918460018302840111640100000000831117156106df57600080fd5b9091929391929390505050611fd8565b6040518082815260200191505060405180910390f35b6107316004803603602081101561071b57600080fd5b81019080803590602001909291905050506120fa565b604051808215151515815260200191505060405180910390f35b6107e26004803603604081101561076157600080fd5b810190808035906020019064010000000081111561077e57600080fd5b82018360208201111561079057600080fd5b803590602001918460018302840111640100000000831117156107b257600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612245565b604051808215151515815260200191505060405180910390f35b6108286004803603602081101561081257600080fd5b81019080803590602001909291905050506127e7565b604051808215151515815260200191505060405180910390f35b6108c36004803603604081101561085857600080fd5b81019080803590602001909291908035906020019064010000000081111561087f57600080fd5b82018360208201111561089157600080fd5b803590602001918460018302840111640100000000831117156108b357600080fd5b9091929391929390505050612932565b6040518082815260200191505060405180910390f35b610905600480360360208110156108ef57600080fd5b8101908080359060200190929190505050612a57565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6109736004803603602081101561095d57600080fd5b8101908080359060200190929190505050612b72565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6109f7600480360360208110156109cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612c8d565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610a37578082015181840152602081019050610a1c565b50505050905090810190601f168015610a645780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610afd60048036036060811015610a8857600080fd5b810190808035906020019092919080359060200190640100000000811115610aaf57600080fd5b820183602082011115610ac157600080fd5b80359060200191846001830284011164010000000083111715610ae357600080fd5b909192939192939080359060200190929190505050612d6e565b604051808215151515815260200191505060405180910390f35b610bae60048036036040811015610b2d57600080fd5b8101908080359060200190640100000000811115610b4a57600080fd5b820183602082011115610b5c57600080fd5b80359060200191846001830284011164010000000083111715610b7e57600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061300b565b604051808215151515815260200191505060405180910390f35b610bd0613281565b005b610c1460048036036020811015610be857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613674565b005b610cb760048036036060811015610c2c57600080fd5b810190808035906020019092919080359060200190640100000000811115610c5357600080fd5b820183602082011115610c6557600080fd5b80359060200191846001830284011164010000000083111715610c8757600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506136fa565b604051808215151515815260200191505060405180910390f35b600080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002080546001816001161561010002031660029004905014159050919050565b600084600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610e0d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b853373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614610ee5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b83600160008981526020019081526020016000206002018787604051808383808284378083019250505092505050908152602001604051809103902081905550867fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea878789896040518080602001806020018381038352878782818152602001925080828437600081840152601f19601f8201169050808301925050508381038252858582818152602001925080828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a2600192505050949350505050565b60016020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905082565b600082828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050600073ffffffffffffffffffffffffffffffffffffffff16600160008380519060200120815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611156576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b83838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050503373ffffffffffffffffffffffffffffffffffffffff16600160008380519060200120815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611279576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f4572726f723a204f6e6c79207768656e2070726f706f7365640000000000000081525060200191505060405180910390fd5b8484600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002091906112c7929190613b0e565b503373ffffffffffffffffffffffffffffffffffffffff167f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c91920868660405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a260019250505092915050565b600081600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611429576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f4572726f723a204f6e6c79207768656e20756e7265736572766564000000000081525060200191505060405180910390fd5b611431611f81565b6114a3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b336001600085815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff16837f4963513eca575aba66fdcd25f267aae85958fe6fb97e75fa25d783f1a091a22160405160405180910390a36001915050919050565b600081600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611622576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b823373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146116fa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b83600260006001600088815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156117cb5780601f106117a95761010080835404028352918201916117cb565b820191906000526020600020905b8154815290600101906020018083116117b7575b5050915050604051809103902014156119da576001600085815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd600260006001600089815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020604051808060200182810382528381815460018160011615610100020316600290048152602001915080546001816001161561010002031660029004801561194a5780601f1061191f5761010080835404028352916020019161194a565b820191906000526020600020905b81548152906001019060200180831161192d57829003601f168201915b50509250505060405180910390a2600260006001600087815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006119d99190613b8e565b5b60016000858152602001908152602001600020600080820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550503373ffffffffffffffffffffffffffffffffffffffff16847fef1961b4d2909dc23643b309bfe5c3e5646842d98c3a58517037ef3871185af360405160405180910390a3600192505050919050565b600083600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611b69576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b60016000868152602001908152602001600020600201848460405180838380828437808301925050509250505090815260200160405180910390205460001c9150509392505050565b60026020528060005260406000206000915090508054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611c5a5780601f10611c2f57610100808354040283529160200191611c5a565b820191906000526020600020905b815481529060010190602001808311611c3d57829003601f168201915b505050505081565b600082600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611d3e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b833373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611e16576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415611e9c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180613bfc6021913960400191505060405180910390fd5b836001600087815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16867f7b97c62130aa09acbbcbf7482630e756592496f1759eaf702f469cf64dfb779460405160405180910390a460019250505092915050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b600083600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156120b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b6001600086815260200190815260200160002060020184846040518083838082843780830192505050925050509081526020016040518091039020549150509392505050565b600081600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156121d6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166001600085815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415915050919050565b600083838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050600073ffffffffffffffffffffffffffffffffffffffff16600160008380519060200120815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561236c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b848460405180838380828437808301925050509250505060405180910390203373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612462576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b6000868660405180838380828437808301925050509250505060405180910390209050600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141580156125d3575080600260006001600085815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156125c45780601f106125a25761010080835404028352918201916125c4565b820191906000526020600020905b8154815290600101906020018083116125b0575b50509150506040518091039020145b1561270a57600260006001600084815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006126599190613b8e565b6001600082815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd888860405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a25b846001600083815260200190815260200160002060010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508473ffffffffffffffffffffffffffffffffffffffff167f728435a0031f6a04538fcdd24922a7e06bc7bc945db03e83d22122d1bc5f28df888860405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a2600193505050509392505050565b600081600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156128c3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166001600085815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415915050919050565b600083600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415612a0e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b60016000868152602001908152602001600020600201848460405180838380828437808301925050509250505090815260200160405180910390205460001c9150509392505050565b600081600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415612b33576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b6001600084815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16915050919050565b600081600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415612c4e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b6001600084815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16915050919050565b6060600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015612d625780601f10612d3757610100808354040283529160200191612d62565b820191906000526020600020905b815481529060010190602001808311612d4557829003601f168201915b50505050509050919050565b600084600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415612e4a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b853373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612f22576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b8360001b600160008981526020019081526020016000206002018787604051808383808284378083019250505092505050908152602001604051809103902081905550867fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea878789896040518080602001806020018381038352878782818152602001925080828437600081840152601f19601f8201169050808301925050508381038252858582818152602001925080828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a2600192505050949350505050565b600083838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050600073ffffffffffffffffffffffffffffffffffffffff16600160008380519060200120815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415613132576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b61313a611f81565b6131ac576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b8484600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002091906131fa929190613b0e565b508273ffffffffffffffffffffffffffffffffffffffff167f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c91920868660405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a260019150509392505050565b600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156133545780601f1061332957610100808354040283529160200191613354565b820191906000526020600020905b81548152906001019060200180831161333757829003601f168201915b5050505050600073ffffffffffffffffffffffffffffffffffffffff16600160008380519060200120815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415613439576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff167f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180806020018281038252838181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156135395780601f1061350e57610100808354040283529160200191613539565b820191906000526020600020905b81548152906001019060200180831161351c57829003601f168201915b50509250505060405180910390a260016000600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156135e55780601f106135c35761010080835404028352918201916135e5565b820191906000526020600020905b8154815290600101906020018083116135d1575b50509150506040518091039020815260200190815260200160002060010160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006136719190613b8e565b50565b61367c611f81565b6136ee576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b6136f7816139ad565b50565b600084600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156137d6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b853373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146138ae576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff1660001b600160008981526020019081526020016000206002018787604051808383808284378083019250505092505050908152602001604051809103902081905550867fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea878789896040518080602001806020018381038352878782818152602001925080828437600081840152601f19601f8201169050808301925050508381038252858582818152602001925080828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a2600192505050949350505050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415613a50576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10613b4f57803560ff1916838001178555613b7d565b82800160010185558215613b7d579182015b82811115613b7c578235825591602001919060010190613b61565b5b509050613b8a9190613bd6565b5090565b50805460018160011615610100020316600290046000825580601f10613bb45750613bd3565b601f016020900490600052602060002090810190613bd29190613bd6565b5b50565b613bf891905b80821115613bf4576000816000905550600101613bdc565b5090565b9056fe4572726f723a206e6f207472616e7366657220746f206164647265737320307830a165627a7a72305820fe186a600f547c59523f333a7eb66cd1de74492306acaa3db2cd4c8a1b426b5a00290000000000000000000000001204700000000000000000000000000000000005" + }, + "0x1204700000000000000000000000000000000007": { + "constructor": "0x608060405234801561001057600080fd5b506040516040806108658339810180604052604081101561003057600080fd5b810190808051906020019092919080519060200190929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3610116816101a160201b60201c565b81600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff167fcc57a97257d7703be97e9d538cb08b3fa1ff2784b776d505942d49a95dc2891460405160405180910390a25050610302565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610244576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b610554806103116000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80636ddc4a071461005c5780638da5cb5b146100a65780638f32d59b146100f0578063f2fde38b14610112578063fe64d6ff14610156575b600080fd5b61006461019a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100ae6101c0565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100f86101e9565b604051808215151515815260200191505060405180910390f35b6101546004803603602081101561012857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610240565b005b6101986004803603602081101561016c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506102c6565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6102486101e9565b6102ba576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b6102c3816103c7565b50565b6102ce6101e9565b610340576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167fcc57a97257d7703be97e9d538cb08b3fa1ff2784b776d505942d49a95dc2891460405160405180910390a250565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561046a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea165627a7a723058204d73401a5cbb107ba765457948a3c2a2612436446f9125c3696f0f4cc7e06980002900000000000000000000000012047000000000000000000000000000000000090000000000000000000000001204700000000000000000000000000000000005" + }, + "0x1204700000000000000000000000000000000008": { + "constructor": "0x60806040523480156200001157600080fd5b506040516040806200298b8339810180604052620000339190810190620002a8565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a381600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555062000141816200014960201b60201c565b5050620003da565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415620001bc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001b3906200032b565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000815190506200028b81620003a6565b92915050565b600081519050620002a281620003c0565b92915050565b60008060408385031215620002bc57600080fd5b6000620002cc8582860162000291565b9250506020620002df858286016200027a565b9150509250929050565b6000620002f8601f836200034d565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b600060208201905081810360008301526200034681620002e9565b9050919050565b600082825260208201905092915050565b60006200036b8262000386565b9050919050565b60006200037f826200035e565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b620003b1816200035e565b8114620003bd57600080fd5b50565b620003cb8162000372565b8114620003d757600080fd5b50565b6125a180620003ea6000396000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c8063954029c911610097578063eab7ad9211610066578063eab7ad92146102b2578063ee7ee0fd146102ce578063f2fde38b146102fe578063fbd74f841461031a576100f5565b8063954029c91461021a578063a999809f14610236578063b71da0d114610266578063da5393d514610296576100f5565b80636a564261116100d35780636a5642611461017857806370a05b18146101ae5780638da5cb5b146101de5780638f32d59b146101fc576100f5565b80631bab58f5146100fa5780632c5653e21461012a57806332d91c0e14610148575b600080fd5b610114600480360361010f9190810190611d82565b61034a565b60405161012191906123f7565b60405180910390f35b610132610767565b60405161013f919061233a565b60405180910390f35b610162600480360361015d9190810190611d82565b61078d565b60405161016f9190612355565b60405180910390f35b610192600480360361018d9190810190611d82565b61097e565b6040516101a597969594939291906122af565b60405180910390f35b6101c860048036036101c39190810190611d82565b610c2d565b6040516101d5919061228d565b60405180910390f35b6101e6610e1e565b6040516101f39190612257565b60405180910390f35b610204610e47565b6040516102119190612272565b60405180910390f35b610234600480360361022f9190810190611d82565b610e9e565b005b610250600480360361024b9190810190611d82565b610ff5565b60405161025d9190612355565b60405180910390f35b610280600480360361027b9190810190611d82565b6111e6565b60405161028d9190612272565b60405180910390f35b6102b060048036036102ab9190810190611d82565b611276565b005b6102cc60048036036102c79190810190611dd4565b611371565b005b6102e860048036036102e39190810190611d82565b611673565b6040516102f59190612272565b60405180910390f35b61031860048036036103139190810190611d82565b6117d9565b005b610334600480360361032f9190810190611d82565b61182c565b604051610341919061228d565b60405180910390f35b610352611b4b565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b1580156103ba57600080fd5b505afa1580156103ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506103f29190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461045f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045690612377565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518060e0016040529081600082018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105425780601f1061051757610100808354040283529160200191610542565b820191906000526020600020905b81548152906001019060200180831161052557829003601f168201915b50505050508152602001600182018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105e45780601f106105b9576101008083540402835291602001916105e4565b820191906000526020600020905b8154815290600101906020018083116105c757829003601f168201915b50505050508152602001600282018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156106865780601f1061065b57610100808354040283529160200191610686565b820191906000526020600020905b81548152906001019060200180831161066957829003601f168201915b50505050508152602001600382018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156107285780601f106106fd57610100808354040283529160200191610728565b820191906000526020600020905b81548152906001019060200180831161070b57829003601f168201915b505050505081526020016004820160009054906101000a900460ff16151515158152602001600582015481526020016006820154815250509050919050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b1580156107f757600080fd5b505afa15801561080b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061082f9190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461089c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161089390612377565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206003018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156109725780601f1061094757610100808354040283529160200191610972565b820191906000526020600020905b81548152906001019060200180831161095557829003601f168201915b50505050509050919050565b6001602052806000526040600020600091509050806000018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a2a5780601f106109ff57610100808354040283529160200191610a2a565b820191906000526020600020905b815481529060010190602001808311610a0d57829003601f168201915b505050505090806001018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610ac85780601f10610a9d57610100808354040283529160200191610ac8565b820191906000526020600020905b815481529060010190602001808311610aab57829003601f168201915b505050505090806002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b665780601f10610b3b57610100808354040283529160200191610b66565b820191906000526020600020905b815481529060010190602001808311610b4957829003601f168201915b505050505090806003018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610c045780601f10610bd957610100808354040283529160200191610c04565b820191906000526020600020905b815481529060010190602001808311610be757829003601f168201915b5050505050908060040160009054906101000a900460ff16908060050154908060060154905087565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b158015610c9757600080fd5b505afa158015610cab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ccf9190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610d3c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d3390612377565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610e125780601f10610de757610100808354040283529160200191610e12565b820191906000526020600020905b815481529060010190602001808311610df557829003601f168201915b50505050509050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b158015610f0657600080fd5b505afa158015610f1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610f3e9190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610fab576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610fa290612377565b60405180910390fd5b43600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206006018190555050565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b15801561105f57600080fd5b505afa158015611073573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110979190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611104576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110fb90612377565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156111da5780601f106111af576101008083540402835291602001916111da565b820191906000526020600020905b8154815290600101906020018083116111bd57829003601f168201915b50505050509050919050565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060060154600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060050154109050919050565b61127e610e47565b6112bd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112b4906123d7565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561132d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161132490612397565b60405180910390fd5b80600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b1580156113d957600080fd5b505afa1580156113ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506114119190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461147e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161147590612377565b60405180910390fd5b8888600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000191906114cf929190611b8a565b508686600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001019190611521929190611c0a565b508484600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206002019190611573929190611b8a565b508282600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060030191906115c5929190611c0a565b5080600160008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060040160006101000a81548160ff02191690831515021790555043600160008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206005018190555050505050505050505050565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b1580156116dd57600080fd5b505afa1580156116f1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506117159190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611782576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161177990612377565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060040160009054906101000a900460ff169050919050565b6117e1610e47565b611820576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611817906123d7565b60405180910390fd5b61182981611a1d565b50565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b15801561189657600080fd5b505afa1580156118aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118ce9190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461193b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161193290612377565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611a115780601f106119e657610100808354040283529160200191611a11565b820191906000526020600020905b8154815290600101906020018083116119f457829003601f168201915b50505050509050919050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611a8d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a84906123b7565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6040518060e001604052806060815260200160608152602001606081526020016060815260200160001515815260200160008152602001600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611bcb57803560ff1916838001178555611bf9565b82800160010185558215611bf9579182015b82811115611bf8578235825591602001919060010190611bdd565b5b509050611c069190611c8a565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611c4b57803560ff1916838001178555611c79565b82800160010185558215611c79579182015b82811115611c78578235825591602001919060010190611c5d565b5b509050611c869190611c8a565b5090565b611cac91905b80821115611ca8576000816000905550600101611c90565b5090565b90565b600081359050611cbe81612539565b92915050565b600081519050611cd381612539565b92915050565b600081359050611ce881612550565b92915050565b60008083601f840112611d0057600080fd5b8235905067ffffffffffffffff811115611d1957600080fd5b602083019150836001820283011115611d3157600080fd5b9250929050565b60008083601f840112611d4a57600080fd5b8235905067ffffffffffffffff811115611d6357600080fd5b602083019150836001820283011115611d7b57600080fd5b9250929050565b600060208284031215611d9457600080fd5b6000611da284828501611caf565b91505092915050565b600060208284031215611dbd57600080fd5b6000611dcb84828501611cc4565b91505092915050565b60008060008060008060008060008060c08b8d031215611df357600080fd5b6000611e018d828e01611caf565b9a505060208b013567ffffffffffffffff811115611e1e57600080fd5b611e2a8d828e01611cee565b995099505060408b013567ffffffffffffffff811115611e4957600080fd5b611e558d828e01611d38565b975097505060608b013567ffffffffffffffff811115611e7457600080fd5b611e808d828e01611cee565b955095505060808b013567ffffffffffffffff811115611e9f57600080fd5b611eab8d828e01611d38565b935093505060a0611ebe8d828e01611cd9565b9150509295989b9194979a5092959850565b611ed981612489565b82525050565b611ee88161249b565b82525050565b611ef78161249b565b82525050565b6000611f0882612424565b611f128185612456565b9350611f228185602086016124f5565b611f2b81612528565b840191505092915050565b6000611f4182612419565b611f4b8185612445565b9350611f5b8185602086016124f5565b611f6481612528565b840191505092915050565b6000611f7a82612419565b611f848185612456565b9350611f948185602086016124f5565b611f9d81612528565b840191505092915050565b611fb1816124d1565b82525050565b6000611fc28261243a565b611fcc8185612478565b9350611fdc8185602086016124f5565b611fe581612528565b840191505092915050565b6000611ffb8261242f565b6120058185612467565b93506120158185602086016124f5565b61201e81612528565b840191505092915050565b60006120348261242f565b61203e8185612478565b935061204e8185602086016124f5565b61205781612528565b840191505092915050565b600061206f601383612478565b91507f4572726f723a206f6e6c794c6f676963204462000000000000000000000000006000830152602082019050919050565b60006120af602983612478565b91507f4572726f723a206e65774c6f6f6b5570206973206e6f7420616c6c6f7765642060008301527f746f2062652030783000000000000000000000000000000000000000000000006020830152604082019050919050565b6000612115601f83612478565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b6000612155601383612478565b91507f53656e646572206973206e6f74206f776e6572000000000000000000000000006000830152602082019050919050565b600060e08301600083015184820360008601526121a58282611f36565b915050602083015184820360208601526121bf8282611ff0565b915050604083015184820360408601526121d98282611f36565b915050606083015184820360608601526121f38282611ff0565b91505060808301516122086080860182611edf565b5060a083015161221b60a0860182612239565b5060c083015161222e60c0860182612239565b508091505092915050565b612242816124c7565b82525050565b612251816124c7565b82525050565b600060208201905061226c6000830184611ed0565b92915050565b60006020820190506122876000830184611eee565b92915050565b600060208201905081810360008301526122a78184611efd565b905092915050565b600060e08201905081810360008301526122c9818a611f6f565b905081810360208301526122dd8189612029565b905081810360408301526122f18188611f6f565b905081810360608301526123058187612029565b90506123146080830186611eee565b61232160a0830185612248565b61232e60c0830184612248565b98975050505050505050565b600060208201905061234f6000830184611fa8565b92915050565b6000602082019050818103600083015261236f8184611fb7565b905092915050565b6000602082019050818103600083015261239081612062565b9050919050565b600060208201905081810360008301526123b0816120a2565b9050919050565b600060208201905081810360008301526123d081612108565b9050919050565b600060208201905081810360008301526123f081612148565b9050919050565b600060208201905081810360008301526124118184612188565b905092915050565b600081519050919050565b600081519050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b6000612494826124a7565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006124dc826124e3565b9050919050565b60006124ee826124a7565b9050919050565b60005b838110156125135780820151818401526020810190506124f8565b83811115612522576000848401525b50505050565b6000601f19601f8301169050919050565b61254281612489565b811461254d57600080fd5b50565b6125598161249b565b811461256457600080fd5b5056fea265627a7a7230582045fea88fa096a11a15a5bb8e9f5993ca3ab8a38132e6c7a04eb7cd5458378c906c6578706572696d656e74616cf5003700000000000000000000000012047000000000000000000000000000000000070000000000000000000000001204700000000000000000000000000000000005" + }, + "0x1204700000000000000000000000000000000009": { + "constructor": "0x60806040523480156200001157600080fd5b506040516040806200208d8339810180604052620000339190810190620002a8565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a381600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555062000141816200014960201b60201c565b5050620003da565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415620001bc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001b3906200032b565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000815190506200028b81620003a6565b92915050565b600081519050620002a281620003c0565b92915050565b60008060408385031215620002bc57600080fd5b6000620002cc8582860162000291565b9250506020620002df858286016200027a565b9150509250929050565b6000620002f8601f836200034d565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b600060208201905081810360008301526200034681620002e9565b9050919050565b600082825260208201905092915050565b60006200036b8262000386565b9050919050565b60006200037f826200035e565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b620003b1816200035e565b8114620003bd57600080fd5b50565b620003cb8162000372565b8114620003d757600080fd5b50565b611ca380620003ea6000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c80638f32d59b1161005b5780638f32d59b14610101578063b71da0d11461011f578063ec2626111461014f578063f2fde38b1461016d57610088565b806312127ed71461008d5780633f67c333146100a957806389c3ce1e146100b35780638da5cb5b146100e3575b600080fd5b6100a760048036036100a2919081019061118d565b610189565b005b6100b16107d7565b005b6100cd60048036036100c89190810190611164565b610a23565b6040516100da91906119c0565b60405180910390f35b6100eb610aee565b6040516100f891906117b7565b60405180910390f35b610109610b17565b604051610116919061186a565b60405180910390f35b61013960048036036101349190810190611164565b610b6e565b604051610146919061186a565b60405180910390f35b610157610c22565b6040516101649190611885565b60405180910390f35b61018760048036036101829190810190611164565b610c48565b005b610191610b17565b6101d0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101c790611940565b60405180910390fd5b600085511415610215576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161020c906118e0565b60405180910390fd5b60008451141561025a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161025190611980565b60405180910390fd5b60008351141561029f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610296906118a0565b60405180910390fd5b6000825114156102e4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102db906118c0565b60405180910390fd5b8480519060200120600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fbd74f84886040518263ffffffff1660e01b815260040161034791906117b7565b60006040518083038186803b15801561035f57600080fd5b505afa158015610373573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525061039c919081019061129f565b8051906020012014801561046c57508380519060200120600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a999809f886040518263ffffffff1660e01b815260040161040e91906117b7565b60006040518083038186803b15801561042657600080fd5b505afa15801561043a573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525061046391908101906112e0565b80519060200120145b801561053457508280519060200120600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a05b18886040518263ffffffff1660e01b81526004016104d691906117b7565b60006040518083038186803b1580156104ee57600080fd5b505afa158015610502573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525061052b919081019061129f565b80519060200120145b80156105fc57508180519060200120600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166332d91c0e886040518263ffffffff1660e01b815260040161059e91906117b7565b60006040518083038186803b1580156105b657600080fd5b505afa1580156105ca573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052506105f391908101906112e0565b80519060200120145b80156106b55750801515600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ee7ee0fd886040518263ffffffff1660e01b815260040161066191906117b7565b60206040518083038186803b15801561067957600080fd5b505afa15801561068d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106b19190810190611276565b1515145b156106f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106ec90611960565b60405180910390fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663eab7ad928787878787876040518763ffffffff1660e01b815260040161075a969594939291906117ed565b600060405180830381600087803b15801561077457600080fd5b505af1158015610788573d6000803e3d6000fd5b505050508573ffffffffffffffffffffffffffffffffffffffff167fc9e49a024d50440c73d2d12d0ae05064094dca76b46dc95e99ea6848eb8f27e960405160405180910390a2505050505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fbd74f84336040518263ffffffff1660e01b815260040161083491906117d2565b60006040518083038186803b15801561084c57600080fd5b505afa158015610860573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250610889919081019061129f565b5114156108cb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108c2906119a0565b60405180910390fd5b3073ffffffffffffffffffffffffffffffffffffffff1663b71da0d1336040518263ffffffff1660e01b815260040161090491906117d2565b60206040518083038186803b15801561091c57600080fd5b505afa158015610930573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506109549190810190611276565b15610994576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161098b90611900565b60405180910390fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663954029c9336040518263ffffffff1660e01b81526004016109ef91906117d2565b600060405180830381600087803b158015610a0957600080fd5b505af1158015610a1d573d6000803e3d6000fd5b50505050565b610a2b610dc9565b610a33610dc9565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631bab58f5846040518263ffffffff1660e01b8152600401610a8e91906117b7565b60006040518083038186803b158015610aa657600080fd5b505afa158015610aba573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250610ae39190810190611321565b905080915050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b71da0d1836040518263ffffffff1660e01b8152600401610bcb91906117b7565b60206040518083038186803b158015610be357600080fd5b505afa158015610bf7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c1b9190810190611276565b9050919050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610c50610b17565b610c8f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c8690611940565b60405180910390fd5b610c9881610c9b565b50565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610d0b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d0290611920565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6040518060e001604052806060815260200160608152602001606081526020016060815260200160001515815260200160008152602001600081525090565b600081359050610e1781611c24565b92915050565b600081359050610e2c81611c3b565b92915050565b600081519050610e4181611c3b565b92915050565b600082601f830112610e5857600080fd5b8151610e6b610e6682611a0f565b6119e2565b91508082526020830160208301858383011115610e8757600080fd5b610e92838284611be0565b50505092915050565b600082601f830112610eac57600080fd5b8135610ebf610eba82611a3b565b6119e2565b91508082526020830160208301858383011115610edb57600080fd5b610ee6838284611bd1565b50505092915050565b600082601f830112610f0057600080fd5b8151610f13610f0e82611a3b565b6119e2565b91508082526020830160208301858383011115610f2f57600080fd5b610f3a838284611be0565b50505092915050565b600082601f830112610f5457600080fd5b8151610f67610f6282611a67565b6119e2565b91508082526020830160208301858383011115610f8357600080fd5b610f8e838284611be0565b50505092915050565b600082601f830112610fa857600080fd5b8135610fbb610fb682611a93565b6119e2565b91508082526020830160208301858383011115610fd757600080fd5b610fe2838284611bd1565b50505092915050565b600082601f830112610ffc57600080fd5b815161100f61100a82611a93565b6119e2565b9150808252602083016020830185838301111561102b57600080fd5b611036838284611be0565b50505092915050565b600060e0828403121561105157600080fd5b61105b60e06119e2565b9050600082015167ffffffffffffffff81111561107757600080fd5b61108384828501610e47565b600083015250602082015167ffffffffffffffff8111156110a357600080fd5b6110af84828501610f43565b602083015250604082015167ffffffffffffffff8111156110cf57600080fd5b6110db84828501610e47565b604083015250606082015167ffffffffffffffff8111156110fb57600080fd5b61110784828501610f43565b606083015250608061111b84828501610e32565b60808301525060a061112f8482850161114f565b60a08301525060c06111438482850161114f565b60c08301525092915050565b60008151905061115e81611c52565b92915050565b60006020828403121561117657600080fd5b600061118484828501610e08565b91505092915050565b60008060008060008060c087890312156111a657600080fd5b60006111b489828a01610e08565b965050602087013567ffffffffffffffff8111156111d157600080fd5b6111dd89828a01610e9b565b955050604087013567ffffffffffffffff8111156111fa57600080fd5b61120689828a01610f97565b945050606087013567ffffffffffffffff81111561122357600080fd5b61122f89828a01610e9b565b935050608087013567ffffffffffffffff81111561124c57600080fd5b61125889828a01610f97565b92505060a061126989828a01610e1d565b9150509295509295509295565b60006020828403121561128857600080fd5b600061129684828501610e32565b91505092915050565b6000602082840312156112b157600080fd5b600082015167ffffffffffffffff8111156112cb57600080fd5b6112d784828501610eef565b91505092915050565b6000602082840312156112f257600080fd5b600082015167ffffffffffffffff81111561130c57600080fd5b61131884828501610feb565b91505092915050565b60006020828403121561133357600080fd5b600082015167ffffffffffffffff81111561134d57600080fd5b6113598482850161103f565b91505092915050565b61136b81611b77565b82525050565b61137a81611b2f565b82525050565b61138981611b41565b82525050565b61139881611b41565b82525050565b60006113a982611aca565b6113b38185611afc565b93506113c3818560208601611be0565b6113cc81611c13565b840191505092915050565b60006113e282611abf565b6113ec8185611aeb565b93506113fc818560208601611be0565b61140581611c13565b840191505092915050565b61141981611b89565b82525050565b600061142a82611ae0565b6114348185611b1e565b9350611444818560208601611be0565b61144d81611c13565b840191505092915050565b600061146382611ad5565b61146d8185611b0d565b935061147d818560208601611be0565b61148681611c13565b840191505092915050565b600061149e602083611b1e565b91507f436861696e537065635368612073686f756c64206e6f7420626520656d7074796000830152602082019050919050565b60006114de602083611b1e565b91507f436861696e5370656355726c2073686f756c64206e6f7420626520656d7074796000830152602082019050919050565b600061151e601d83611b1e565b91507f446f636b65725368612073686f756c64206e6f7420626520656d7074790000006000830152602082019050919050565b600061155e601883611b1e565b91507f4572726f723a20416c726561647920436f6e6669726d656400000000000000006000830152602082019050919050565b600061159e601f83611b1e565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b60006115de601383611b1e565b91507f53656e646572206973206e6f74206f776e6572000000000000000000000000006000830152602082019050919050565b600061161e602583611b1e565b91507f4572726f723a204e6f206368616e67657320696e20746865207061737365642060008301527f53746174650000000000000000000000000000000000000000000000000000006020830152604082019050919050565b6000611684601e83611b1e565b91507f446f636b65724e616d652073686f756c64206e6f7420626520656d70747900006000830152602082019050919050565b60006116c4601f83611b1e565b91507f4572726f723a20596f7520617265206e6f7420612076616c696461746f7221006000830152602082019050919050565b600060e083016000830151848203600086015261171482826113d7565b9150506020830151848203602086015261172e8282611458565b9150506040830151848203604086015261174882826113d7565b915050606083015184820360608601526117628282611458565b91505060808301516117776080860182611380565b5060a083015161178a60a08601826117a8565b5060c083015161179d60c08601826117a8565b508091505092915050565b6117b181611b6d565b82525050565b60006020820190506117cc6000830184611371565b92915050565b60006020820190506117e76000830184611362565b92915050565b600060c0820190506118026000830189611371565b8181036020830152611814818861139e565b90508181036040830152611828818761141f565b9050818103606083015261183c818661139e565b90508181036080830152611850818561141f565b905061185f60a083018461138f565b979650505050505050565b600060208201905061187f600083018461138f565b92915050565b600060208201905061189a6000830184611410565b92915050565b600060208201905081810360008301526118b981611491565b9050919050565b600060208201905081810360008301526118d9816114d1565b9050919050565b600060208201905081810360008301526118f981611511565b9050919050565b6000602082019050818103600083015261191981611551565b9050919050565b6000602082019050818103600083015261193981611591565b9050919050565b60006020820190508181036000830152611959816115d1565b9050919050565b6000602082019050818103600083015261197981611611565b9050919050565b6000602082019050818103600083015261199981611677565b9050919050565b600060208201905081810360008301526119b9816116b7565b9050919050565b600060208201905081810360008301526119da81846116f7565b905092915050565b6000604051905081810181811067ffffffffffffffff82111715611a0557600080fd5b8060405250919050565b600067ffffffffffffffff821115611a2657600080fd5b601f19601f8301169050602081019050919050565b600067ffffffffffffffff821115611a5257600080fd5b601f19601f8301169050602081019050919050565b600067ffffffffffffffff821115611a7e57600080fd5b601f19601f8301169050602081019050919050565b600067ffffffffffffffff821115611aaa57600080fd5b601f19601f8301169050602081019050919050565b600081519050919050565b600081519050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b6000611b3a82611b4d565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b6000611b8282611bad565b9050919050565b6000611b9482611b9b565b9050919050565b6000611ba682611b4d565b9050919050565b6000611bb882611bbf565b9050919050565b6000611bca82611b4d565b9050919050565b82818337600083830152505050565b60005b83811015611bfe578082015181840152602081019050611be3565b83811115611c0d576000848401525b50505050565b6000601f19601f8301169050919050565b611c2d81611b2f565b8114611c3857600080fd5b50565b611c4481611b41565b8114611c4f57600080fd5b50565b611c5b81611b6d565b8114611c6657600080fd5b5056fea265627a7a7230582070f7a284b1ab092d7cf8a14ca7bf4b7a68d6e2cb8441f82436e624a898b0ebd46c6578706572696d656e74616cf5003700000000000000000000000012047000000000000000000000000000000000080000000000000000000000001204700000000000000000000000000000000005" + } + }, + "nodes": [ + "enode://cc67008e850c4b64702f1664f18704dd23980fb574138588904270739dca3a9c417ac3f97077b0f4e4872a5db109195f7137c156314dbcc724eabd809044553e@ewc-bootnode-02.energyweb.org:30303", + "enode://34f97743b9c07fac84c2db921337f006b5932e14c29df496951bdcfe3fce71e3d1504c25d66156dae01065623849b4fe940168c7e3f21c49b538af0cabb5805f@ewc-bootnode-03.energyweb.org:30303", + "enode://80e49825bb6bdfbf69576fd4a5f7bb173db98c08439bd7a019e2d42548afbb1f70232b74ede42dbd99cc0de3990a0101d49543c1b299027d2fd6f5a2c81eff75@ewc-bootnode-05.energyweb.org:30303", + "enode://1cd00f54ddaa41793d32500f0cb123d43e95f5ed18220c367d5a4db7d111e5fb59f250c397e40977562881875827f022d88a51e51171daaebf916beaa60e2139@ewc-bootnode-06.energyweb.org:30303", + "enode://3a8588bd883ea3bce606b08e7ca71d55717077cb3d25d3c00421ac371fbae286b94f58ba2e739b17b64c4d0d3e19f1b0ae2e47962703b42d7f36f1b9a448d4ea@ewc-bootnode-09.energyweb.org:30303", + "enode://69749dbe7e6f5fceaeb0a16f60353b3ec04825a12400e6d581fe980275e96fecb464276e61d79fc1628e448f1f74da2985d778ea54e0e8e7c64d980f2b3b6a94@ewc-bootnode-10.energyweb.org:30303" + ] +} diff --git a/ethcore/res/ethereum/expanse.json b/ethcore/res/ethereum/expanse.json deleted file mode 100644 index 073d8fe4836..00000000000 --- a/ethcore/res/ethereum/expanse.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "name": "Expanse", - "dataDir": "expanse", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "difficultyIncrementDivisor": "0x3C", - "durationLimit": "0x3C", - "blockReward": { - "0x0": "0x6f05b59d3b200000", - "0xC3500": "0x3782DACE9D900000" - }, - "homesteadTransition": "0x30d40", - "difficultyHardforkTransition": "0x59d9", - "difficultyHardforkBoundDivisor": "0x0200", - "bombDefuseTransition": "0x30d40", - "eip100bTransition": "0xC3500", - "metropolisDifficultyIncrementDivisor": "0x1E", - "difficultyBombDelays": { - "0xC3500": 3000000 - }, - "expip2Transition": "0xC3500", - "expip2DurationLimit": "0x1E" - } - } - }, - "params": { - "gasLimitBoundDivisor": "0x0400", - "registrar" : "0x6c221ca53705f3497ec90ca7b84c59ae7382fc21", - "accountStartNonce": "0x00", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID": "0x1", - "chainID": "0x2", - "forkBlock": "0xDBBA0", - "forkCanonHash": "0x8e7bed51e24f5174090408664ac476b90b5e1199a947af7442f1ac88263fc8c7", - "subprotocolName": "exp", - "eip150Transition": "0x927C0", - "eip160Transition": "0x927C0", - "eip161abcTransition": "0x927C0", - "eip161dTransition": "0x927C0", - "eip155Transition": "0x927C0", - "eip140Transition": "0xC3500", - "eip211Transition": "0xC3500", - "eip214Transition": "0xC3500", - "eip658Transition": "0xC3500" - }, - "genesis": { - "seal": { - "ethereum": { - "nonce": "0x214652414e4b4f21", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x40000000", - "author": "0x93decab0cd745598860f782ac1e8f046cb99e898", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x4672616e6b6f497346726565646f6d", - "gasLimit": "0x1388" - }, - "nodes": [ - "enode://7f335a047654f3e70d6f91312a7cf89c39704011f1a584e2698250db3d63817e74b88e26b7854111e16b2c9d0c7173c05419aeee2d0321850227b126d8b1be3f@46.101.156.249:42786", - "enode://df872f81e25f72356152b44cab662caf1f2e57c3a156ecd20e9ac9246272af68a2031b4239a0bc831f2c6ab34733a041464d46b3ea36dce88d6c11714446e06b@178.62.208.109:42786", - "enode://96d3919b903e7f5ad59ac2f73c43be9172d9d27e2771355db03fd194732b795829a31fe2ea6de109d0804786c39a807e155f065b4b94c6fce167becd0ac02383@45.55.22.34:42786", - "enode://5f6c625bf287e3c08aad568de42d868781e961cbda805c8397cfb7be97e229419bef9a5a25a75f97632787106bba8a7caf9060fab3887ad2cfbeb182ab0f433f@46.101.182.53:42786", - "enode://d33a8d4c2c38a08971ed975b750f21d54c927c0bf7415931e214465a8d01651ecffe4401e1db913f398383381413c78105656d665d83f385244ab302d6138414@128.199.183.48:42786", - "enode://df872f81e25f72356152b44cab662caf1f2e57c3a156ecd20e9ac9246272af68a2031b4239a0bc831f2c6ab34733a041464d46b3ea36dce88d6c11714446e06b@178.62.208.109:42786", - "enode://f6f0d6b9b7d02ec9e8e4a16e38675f3621ea5e69860c739a65c1597ca28aefb3cec7a6d84e471ac927d42a1b64c1cbdefad75e7ce8872d57548ddcece20afdd1@159.203.64.95:42786" - ], - "accounts": { - "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, - "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0xC3500", "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": "0xC3500", "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": "0xC3500", "pricing": { "linear": { "base": 40000, "word": 0 } } } }, - "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": "0xC3500", "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } }, - "bb94f0ceb32257275b2a7a9c094c13e469b4563e": { - "balance": "10000000000000000000000000" - }, - "15656715068ab0dbdf0ab00748a8a19e40f28192": { - "balance": "1000000000000000000000000" - }, - "c075fa11f85bda3aaba67106226aaf086ac16f4e": { - "balance": "100000000000000000000000" - }, - "93decab0cd745598860f782ac1e8f046cb99e898": { - "balance": "10000000000000000000000" - } - } -} diff --git a/ethcore/res/ethereum/foundation.json b/ethcore/res/ethereum/foundation.json index 5a7fd3be318..663b703f3e4 100644 --- a/ethcore/res/ethereum/foundation.json +++ b/ethcore/res/ethereum/foundation.json @@ -136,7 +136,8 @@ "eip100bTransition": "0x42ae50", "difficultyBombDelays": { "0x42ae50": "0x2dc6c0", - "0x6f1580": "0x1e8480" + "0x6f1580": "0x1e8480", + "0x8c6180": "0x3d0900" } } } @@ -163,7 +164,12 @@ "eip658Transition": "0x42ae50", "eip145Transition": "0x6f1580", "eip1014Transition": "0x6f1580", - "eip1052Transition": "0x6f1580" + "eip1052Transition": "0x6f1580", + "eip1283Transition": "0x8a61c8", + "eip1706Transition": "0x8a61c8", + "eip1884Transition": "0x8a61c8", + "eip2028Transition": "0x8a61c8", + "eip1344Transition": "0x8a61c8" }, "genesis": { "seal": { @@ -180,3686 +186,19 @@ "gasLimit": "0x1388", "stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" }, - "hardcodedSync": { - "header": "f90212a0113ba3b1153987fc483b01ccfe9ecadbdc36a7b264be75d6486cb6b694cc38a1a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479452bc44d5378309ee2abf1539bf71de1b7d7be3b5a0d91db5900e312a1ba8d39505406a95dc0ea20393b723c4bf488ade4e3c4ee4c3a05e63ab076852043b1c2fe010e2f14ab13dcc8e1c546ac497a24ce88c4abf3056a0b72e4a776f895459b6bf0937040990d868b1f45889aeaa26684e07472263fc31b90100b00200000063664554889124822ce2001022840543085d5810042382888542ca3816801780384384cd6851081cdc4d4b2b205a814b049198110a6869d60f4009a79c3854c9000581044889080000611c09142200106552000044027024004038092102814ed6c48040a8715851088a20108e80165f8c0c514019037a0000121304b2343416800b029000024404248238a06818414cb8690244879491855405026bc8250220520992c2380099d10024411c6048424083d1307822442d8444700405147883c4c041300aaa2408bb61084012983825a22830040a180106b5e27182088060b111515832902903a8f1d432a48004d0250437106a503491000048a91587067a897789f4d78371d801837a309c8379ffd7845c9d879e9150505945206e616e6f706f6f6c2e6f7267a0db22736cb3f06f9c86804902731fa6841eaaa1cd6326ea2a30e9c304d48bdd04880323b06837344cb8", - "totalDifficulty": "9633968582330911261986", - "CHTs": [ - "0x0eb474b7721727204978e92e27d31cddff56471911e424a4c8271c35f9c982cc", - "0xe10e94515fb5ffb7ffa9bf50db4a959b3f50c2ff75e0b8bd5f5e038749e52a11", - "0x816e7463af7b5d2fcb804ba55f09e8452182b0ba6c995a34e144245d76333d55", - "0x3793af64c1ddc07ab61b2ba120034d91c02183ff788f07d3120fd4e6a48305b5", - "0x14c6106a17e041032210bfa0ca80d11860a1c6d95175d55eff39f97b8d8acded", - "0x396f832bfa3a9c494e9245471f0e65552613d87b6fe62128103590d95de72c2d", - "0xb060979f095c170a776b2b50a1e2ab0ffea80f6e522753fa36ad6f106ee32e9f", - "0x8f452e7cbd8a333ed04d819a143a8d3a75fe8c58418e7fc420bb2a717c0d4d2f", - "0x37fe1b0cf156bfc07571569af210540be753777903a308d5707538fffed75b59", - "0x6f0561d017cfc123b3f0d37b044e4f7231516b8731a1cab89afb569238643c33", - "0x3c1740c410a88c60fe8ccdc44e0ef2cf7f7314818dbf1648c01d0d94fbffc211", - "0xfb98115a7d6df8aaa40115f883014fe97300869bc016648e918fbf2df9608d41", - "0xef1099ab5ca4b79369048678d3ba78122fc081b00b6fd0f6907302a260d58266", - "0x969575f411dd78fdc5b4def0331fc93702029cc3c78851331a0f47dd0faae70b", - "0x9e53053e362be51c0fd25eaafd9e7c5c969d9f2ce8db4b3d4d830cbff347b0c8", - "0xf9de29944954d2b71a93532fc26916ae12fee72d42a79adaf940b0bf75d0ef89", - "0x28b2ce6635e60e06d643750798779023b2a807d9d089ae9ef7f223eebc15a71e", - "0x5295c06cbaff06f42bd8f5d9cbe94a840885caed02f9c9ba6da44a888ef796de", - "0x576eae673a4cbba4c7c7a56b47835ea64ae5989d67d119ebc8e568df40d908a9", - "0x891c0d38bc5e55620615da42ed77ab33806a042512034bcee134279dde1054be", - "0xbab05999d657426a11a902eb4c85ac52e2b72dd1cf38584cf2baeb2c3727bb44", - "0x3bd7e5a966f6dd2dc456948a8efca5584f5a4e0033f3037843a42073dda1f71b", - "0xc4f773ab1e34290f9a3d9ac6ede4749c5dec547353dddea494169d86f71107a3", - "0x993bf037ea9dd58b52027fb6f39332dab867c1e72af34a49d58a5a12f26bcca3", - "0x48b2d8d506eb8fc9dc0402fef26952111449aca0f90d0079f0526435d4e3183b", - "0xfef8f61df240e956f43759d2f481938421e064a9bd6a3be7a53b1213cc9588bb", - "0x5abf01f5066cf1091acdd1f99fbd5fd963633feafc42f9047534a3c1522004a5", - "0xa0f6205842260988161183b51bc36fae458fa184dd61844617d5c5d26fa78346", - "0x77309182fdc26d15dd8d9dd05040d7dc623412785708d8aac39eedee63931944", - "0x661c93311b94b7d4cdcbc0973225c794e71898a2b906922a6c1e8f7e9e289dc3", - "0x9d5d329ee8d9fffaee0111688d31a308dfaac922dcd61f818edd5303d0955be0", - "0x716ca25b184b64ba273b978de098f9946413f6fcc95bfba5cf1169e7e03dd611", - "0xa2e8d5cefa5804894fb42a106340b00de3286fee0992b5887b2cf471539d74a9", - "0xf846e05c9e9e9cb4cd2b7cbae7ae183a43a59ab02251954db632e538adacc357", - "0xbc01b4e23ea082a193e4c1012b1da91f3b4cb762009ca320bc8ed294af874e79", - "0x9218114a32da3ecf660d4d51b101bb51bb17c771561c1946c099be082f0a96b3", - "0x3b4edf03dfd53081cf40c0b90b35c1ccf7c7fe96cf131172eef5eec62f620ea8", - "0xb15758944263c67bdf528d4d7fe05737fbdbf7ffedce5f891a4ddf76177d2609", - "0x1f119374c385240f7b4ba1ec3d502be2c12c159411d5393ff2bd38cf87033625", - "0x8a8d5a93f3475813926b13a4d53f21b28dc79ade2b50830c0b9043e9fcd81576", - "0xcc22f7e2bb9c06c15ca3d82df852ed9097a2ddc687ee389e662de000db0c84fc", - "0xc2047e0dab711db791aadb642f8102abaacf7231b8dbdbe1f60573b0be015a31", - "0x1b4088ceee7783e4563945f162bd5da67020ca377a18d615923e8564d6709f85", - "0xd73450686e33bbda9eef53a95a86e5a0514156b98a5b7dfc6fdc0adb0b83cbcf", - "0xb374076ec961360e38d3486a31c3f72225440984c4c47ca790b4961d94152159", - "0x4f723f4fbd31d63a5421390e68aba0aff97249875688a7d9ab9a339d9aac7bc1", - "0x5fe51ff982edcad6c0052fcdf9a70e8f325c8140ab75848c5d7b0d670bd7edc2", - "0xc3ad483c7cc23bf8d6ae3e3e829bf126d5eeea9c53b566a6da95bef573b9c779", - "0x3c9e50ed9eb57cc055fd9a65a6cdaba2030d8b41f81348f296d7410c1d24ced0", - "0x0c6dfc1f626ff9e85ff072c154152bb3f122a2c1a45bc2d9e7da9b2d5278149a", - "0x92f4452dbdb4fe70e84ecd47af4b1af90975219797cebd451beceb6997ab024d", - "0x9a3d00686736b5b838308da4b8f0aa9edccfaba64621ce2988cea6ea2a267efd", - "0x8d602d0bef069177102726d5ccd93d19805fb5771a350a41e32755ce740b9047", - "0x6681e4097667a22ad3713acc27b6f87abd54583230581933bed9245c2c457ac3", - "0x53077caeabcd926466319a3ee5c51c32e01e1812a65313f113f814d53e9f1dd9", - "0x4dd4c33e99d86ba84f976c639333fc072e262c0b76dfdf2f589300af54048c0a", - "0xbc3b9837a6fa54616dcdb8088080e276e2e99a23c8e7de4109504293703d524e", - "0x24316b344cecd5e601cc0acc91ff94f481ca3fa26d8478644a9d8bcaeb0359b3", - "0xa7bafd3c5f4e3f6b5c078d50eb318d91e867b0e1c966027e3e7458eb104ccd63", - "0xc8da46b7d778980d120357c8de2bba336f5a2ac7a9f4183a0ee1f7597ed47d25", - "0x7469fc5d8c9c648cd10e538710e0f126542e59a82484e7fe56b73f4ec52c36c7", - "0x993bb7c0487ff61c97e4f1533446ae35b6346642e1230f2441da8b354111d597", - "0x90e3944732f86a2254dc4f30650f8438dfd0b777561fb02a8ab1c60438569c24", - "0x4e8472483679b54bdc600010fdd164f54771d7a99fa9272c683b610fff72507b", - "0xf72a861a2ebc232c25529c0f94c59996e64c59c36a1326a183cf171bddf2a75c", - "0x7f222999ba9113e2a64fd026a8f7244e6d2ae8f2a7e7d8d2d6fbde6fdf0b629f", - "0x3304e769f730522c1c5aa745c448075df026b8f82a4dece84fd70d0457050985", - "0x9ee5e3ccaaf94461dff9df8c4805ca831f58a1586af4ece3cab14a45f3b784db", - "0x21e4364859063e20153d2d06eae4d2c9e99354bf97fbff68406d8825d18dbce3", - "0x4805355b72b1b61b07814059f80b4da0351291cc932292f23069197a74127726", - "0x14474f45f38d7ea51418e5f03751c8bfbfb9b3e2957d3051e862aa3c57a63c43", - "0x69372cee3e2807d10ecb72d404a033568a159a5b15d2007537ed9a758164b29f", - "0x147223b51001166a4e65372c9c706f011f1ae94f4bdc9ba6e8960017e8898703", - "0x11a1e48a5c1d7088c0ccb8177d54db9e9f91a99aa7c24f702cd93f4646f181ee", - "0x809c902b2f4b8760c3d2e820c93d6df69a5d184a43a6c654ebe7067e7212137d", - "0x749367027756c27215b2f57168ef15d3b39062c9f79b3777f7fa19e8073de775", - "0x6a9fae37364f97e36e56df97acb1b7d066a608d8366d7e008854756dd28fe748", - "0xdc2f1b7a8aeda15e6bf4f5f424bc54828cc8520e2e7ba27bd8e28ba2b543aae7", - "0xe0fabc892d5c8b4342ff488b76a0400425ea70774f207c546fbf2f9f5b105dcb", - "0x151fb5e02d8eb3c3192cb8c039bcb4c121c4ebeea5e7f98927b85a730a24bbf9", - "0xfeb2f2ea368d0bd4c0b0bd97b444c365bdc0ac9ce2862b0d0162387727edd236", - "0x1eaf828231ebbae54737111bf3c7181fe3d7e9070def1313470d3f81c89f01c7", - "0x8a1b0565013cff488bbe3f35df86fb41c7aedf4d911130802c473f4ddb74d6d6", - "0xce9158b5c903312fa636e074e3efe413184652581a4877d40a0085965dc0bf9a", - "0x1cf602c6306affe2916fa09d3c8c018f23fc44dec8af8e83fa0008c98b4dda72", - "0x189dc4569e96cab937265ecaea76a0880ec97d5b84ac1fbd0cd2d2b36a8c34c3", - "0xd698bd07e485767c1da30bb218265e1304f6eaf426749ccba67478817af84bd4", - "0x47d7e101de73bb0ca97a0bb70094e81b82c63e519a6b2aa5fe10ca7351232870", - "0xb0d441b6c41072889c4a982306c9a40dff77b43425ecc4d771c22f3199eb7708", - "0x7893071deba67f2fc8e1b18bedcac4dbc05a020f37c764c555eadc42dd9d29d3", - "0x3c6d636db3621757d60b2d0e1804e19528ce60c9feed1ead93731820ff19b11c", - "0xde87aaa462b461c4a33e0739ef4cf56d442b7967ac7c5280816a959046b128b6", - "0xb237b17650adcbcf580b64b500ecaa7ca36921a11ad92c1e8992c57cc1a7f618", - "0xec379725db43fefe61f2495f7f7e0531d852e21f896ae806144c4d9b4b986e96", - "0x65ac5548988825831f0887b9ff0f2c13b7f3b49e4a67c39b1694e76414249f6d", - "0x76053b72ec9e6fcf0a28ef273d3e1b0842c3c2c0e905f5b5a3535ffab216c8db", - "0x2ab1e87489eb1daeaf8882c6baa0a8726aeac522e9c4eb4df71e35af2d22cc10", - "0x8c9c6adcabe253b311f6a9b8165ff9c5e26e4cf41f1acd80837e77fc15526a86", - "0xf143155230223a3f126c757b85e193a9129f1bdf97c0ce1f2785f14d40911f30", - "0x8c510d9dca593534f3ed316f240ffb9343d1e3cd6d005df6a75a1b354da0b36e", - "0x3440975cf818a718beff35a85d19bcbbd67e1b16ca9d78af34dcde31a28b3288", - "0xf56ef9c57109f9cb7a925bbb6d453efc19e8a45b331f76153d20a87d86a8b0d8", - "0x19a360772872003f08508a28a362c6e05650b385c24a928ddad4d562bfccf412", - "0x643720694b3773ecf20437d54a6be701810feff233f435dc701dbe88c9a6a13c", - "0xf8c0babe99aa26ecbfc91b304d9cd54ccfb37354c4fdbeb3207bb6d4647fabd6", - "0x481ccc7213d0188e817c071c4cc3a71c96befb9aa98cab964012fd7a8267834a", - "0x02d83ca8d92e0fe6ce7643ae93af60e38ab5659a84c04beba678ceef654aec12", - "0x24e6b4bcd0d97df196f2371532771593fe17be8fcb89f1e1164bcce8616088b9", - "0x3dc91775c50c04812f755f3b48d3d6a0cc599c586ce9d105e2cf4f3e4527b515", - "0xcdc215f05398ea3942d3a38078a3602cbe8ac549d4bf0e4a54191ceb2aff8f76", - "0xee02874e444b784f4265cc60b86a17382d277d03c8bce8a33241460ea8950699", - "0x35c34bc84736fdcbc4d4e2e089f30bcd186a052b2f6dcb639fc45a0aeb6969f8", - "0xbbb3ff849c36659bf2c00feaad9f7b3a342b5cfcf3555b7c2e467a0dc84e90e8", - "0x0584bc60fbe3fae9088c214fb519030646c3240f77180a0bedecd3e9c9f47f89", - "0x9d18a665d89439ad2c97427bdab3e598f5bc0da6a0ede2378f95c5bc31f10d12", - "0xe8a5bdd0ffd33a6fd03cf003c6d2afbe8493e0f0cb69e6366e22b4d1ff985101", - "0x7ca955f4e01eef756b680d09c25626cf50013faa20a12b0a334fd048a04e7b91", - "0x064254551457bd5e7a260a41ed3643746202d503813ceaf42660f9bd1983be34", - "0x1191044e354ea1e3daa25ed2175a6517659e96733d9065d81492ffe4472fb96f", - "0xc823514cf3566b1bb2e19a35e0ef0980bc483fa820d13ae2cfdbf15fd426c272", - "0x413f941f192d0ab77bac68268f45e2c9adbee23a3324d4ae8748d09735355a2d", - "0xa66c94b9603b3058b730baeba1b79d52f548ceeb5bac487903f92481060f6804", - "0xa84d4a8860bdff1fdae6bcefcdcf700fab7857444ef1e76d8259b005872a4636", - "0x9fa64d44edd9c097458d3901612a4b6f655a1421ebb68541cb1a4bdbbe24911e", - "0x402027770edb387510241a68a235723c6c5c95aed54dab058c43d21a6bb48c41", - "0x776281b7e341a66491603b7ae8ed7cc82b99febac43f94cb1c4dda73b17aab63", - "0xfecbaf0fc5a02dcf49095514ce26df927def3cf51f37e04471545aae2364f936", - "0xc477d9293f0ace7243f8b9c89f01210b8f96b4affc9d3332147ea2e2b693c99e", - "0x4a8b9afb9d9097831b2497296b0fd0fae76ab8a596213daed35cf87e6bbefaed", - "0x594c4e9851eddbb4a6c2ac72aaa244ff35d67262efb20935360360d39f7c7ecc", - "0x14b7ef22c46ba8400979b6c06f3b3023607145a5cc6b5b793daae758cb655245", - "0x3a9d233553d1ca4d9862a70ef133a5fb2df75276fb24297b0bd2927a39459450", - "0x22c5e227d5fa7616603bbf36c5e4ee7dbf285fb0cf403a3ae982da70c825cad0", - "0xc2c8d439c7bd884665da56e3b680a5e58ad1e98627fdbec6fa67d7bbfad33a9b", - "0x7d5682cd9f28493ba4b87be141ec99701bbcd1aaadf9840b81de1fc07d4ffb18", - "0x8845f626c5f78d1bea281f892727437f9de8f976e4c4fec6060b2115f1862db5", - "0x769fdf0bcdbaec1ffe98cd3500ce8341b4d7ea2dad5fadb0258212306ccc75f9", - "0x185569d1980147fcdfcd0e0068ab380f0cfa58a690334a558bc1fd0d07897e96", - "0x0ed70ccff752df46f981043c5279ac3f13e7f62c2bdb9a0a9817a1c119ef6402", - "0xbe121e28349e80d601ab997af844aa03ea6492e88d75d3d46517d8f835e3c3fd", - "0xa7aa5f0bb95566292d22891faa75b7ff2020b69fecc8a22d796cc3a60953d98f", - "0xa2611b092b00f78fe639c4fba0274ae474fa448b3f2e4b8aa4d06c654720d478", - "0x8e425115b98f5e41c8b5d03a9e17d56d30050d85dd06cfff12f002c546a256ba", - "0x988b449fbd8c35855154fb4eb22ba6b7b7095be26203d137f484c67facd40dc5", - "0x567c43ac5dabeff01d6997543ec7abf7998088a355a6ba8e70f41a243dd1343a", - "0x6f560cb650142aed532f17de763d61d021cdec2716b0d2cb27b3a64052abb874", - "0x7e4ce5fab8f4f1fd41f9e5f10204032ae7e0e38093b1d07699318975b33910ec", - "0x91a0820eec5390916bf464b1d16c00b5d94386c4c9f4cdf7e0b3cbe40747fbc9", - "0x9c59451a9a242123efa72c5fbd1564b7bcc0067ea9d025336d228ed26b9ba6c0", - "0x1043a5ab3f5a3bced84faaae0e783abb3b81c2b967bbd976042cb5d897d28146", - "0xfc37b3b3c0be392ad2a5e36c120eace1d14e637ac806e79a750b9a6be3c742a7", - "0xce2ccbada44a8db5144073e69914b322dc015273a75b85ea43fd9e21037c760c", - "0x6cf8336b5a410e10604f93351242cb3a6929968212abdf85b4ca9321115b8fdf", - "0xabbe9781950362be1e206d91ea1bdd6f32ea2c6df65b277cb89050ca1deb9296", - "0x922a6b85add6839494c3edff389aa1b054409c330b4a4e2a6c0e4f9bc85b36a9", - "0xc26dcdfa135a09b7eef1e99b445fb66aefb8bceb6ea715b81d78ba87cd56ed8d", - "0x87d647fa7dcde81a0e133aa949d574befefbceab24a42ae4f3809d2bb52a2d9b", - "0x85ee37fa7154568b9dee8a539340f99c7f1bbc7b9be1f2055636ed9dfc074e4d", - "0x8b0114dc9e249f1de4bb3d055790e4bf18aa28a938f39e8a457ab4a43b0dd613", - "0x3be36db134f4c00fc9e1bc376213c7073389c993b0b0744cba619688d6c037d1", - "0xfaf987eb2e066ad8871c489c23102ec5c58add2d13e62d56f2821cc1f4d66d84", - "0x678478da2955e6876ac49c5146e9f7c376dbf2f170f6404054ae4385e72f3f19", - "0x85d8d9dd6c2a8f6b6a1c0fc0cb55ca870d9a7aae1621c143c3176a3a81fcc29c", - "0x76f4dfa4c3387408c823a75aeb872ff39af3820375ff52f7aecb41c96e4faf2a", - "0x6e530647f2e4232063de2fa8f673989d7834d8cdf529791032888f2833880b80", - "0x64422b4dfff6cba0eb6deaeb4593eefc40a357469a7f7c3be078f80c66161333", - "0x5c7ab740510a4183832bd78e6d6105b0f9f928611f7d62ef96aa8dd8da48a72d", - "0x0bbf405e29f015d24e64f063d50ec6c616af64622b1a4132cde86f926e93850f", - "0xd9ba81ea0790f1f8adccd0bd203c7adeec2b490381b822d6b15293cac2f26206", - "0x4f78619baa34f2278022c509671a38d29366936d6860e79ab540ea46b66ba782", - "0x00c1f10211d7604e59a327239f00dc6d036a93416b7871cb214e8eaa52571834", - "0x1b6636708f97485675c0e5b21eb749ee4a5fd0dd886e6690090856bcc5178ec0", - "0x71366e853968c1bbbbf8e3d6e13100dd589521f8db9e561dd20ff8709b5c1a96", - "0x0d2c35a01646cb09e2b56b5792ac03047848bef7415ae26f787cd54ef8f327da", - "0x1c5b71047f99db30453e502c9acdf422d3bf97b0d42b9223ea1b8b9924bb0cb1", - "0x9988eb36e4a669638e3242e5ada3e6596c5e4ef36a83ed2d3348d35fbed4d3d0", - "0x8f00020f98f02af0749df39fb2f534d356e3dbe809bdb3f435c4a575d661d6db", - "0xf70a509c0d1c60afcafc7cc492c5ad575fdcadf6ca8e0e5f184c62dd52021129", - "0x72cdb6544dd469ab42e270e51d136b314c27ed0d6682f914cf3e0398399d2d5d", - "0xd5584896e649b618ab8257859e42ef7798c37cc85103a8019cec10b1524519f3", - "0xd70636cf5cbe78ba86b8de902f83f9c550a8ee31a019da6fcb0b1ab0a02bd31f", - "0x79a506d61c89cb7b1aef845484956389d5f6077fd10f8d1ede1e92474eac15cf", - "0xa66c0575cfea08bc6abbba03b0d10be7bdcfe6c5da9058cb34c22af2c8f3f1d9", - "0xfde316523b6b41fedcf11d776a53bd27fe3058c3912059197cda083a14410689", - "0x6774beba5e02630a7e4379fed7175f0f3d9f8fb5333451f25d5b044521ed38ed", - "0xb513ee7cf03529c88633176792a6b08585ff6163fe174f68e285d6315ffe33aa", - "0x4482f3d82f65f0fdf71fdf669403e0b835b5458e567dbd295b4f51d22f01650c", - "0x1cf0c0859b1839ebfb872a570e0c17886d8d7f26067bcd16af7f9f0415001aa0", - "0x231be14cb1cec949a4e806a7b3aebdb074d58e5a1c48b85c35138d5d3e967e0b", - "0xe8f0a0ef68efb2ea1bfb5d47e3c9446900329ff89a3ab7eccde41e09ec3e79b9", - "0x16348cb5e49e61010da09a5ad3cef83ab369ee3d0f28079584c23749cfa30238", - "0x6d33bc7f502436bfd0d574c3f6b1155c69f8a80e55c42c353e9e68abb46d932e", - "0x0e5d40ed7351b59846ca3dd8cc9c0eb71d4659e0add0dbfb0bb7f518bf45c821", - "0xb1ba4509de4c0f1212b2b07d949740f15ef8df9af8e7e9d765e6b407a0c5d717", - "0xa99615bb15371a15b92c119f8632f1ad7c29d6eb9a69e0ccf33a9dd268cffd54", - "0xfc3601e7f85e4b8e996bddcf1b34cb6c20462e21c715782da12d8e08a01cd21b", - "0x872b0f4f3ef00cc5cc6fdd71091de96c02f5898826fda4f837832f302497b51a", - "0xb34656439e4474e075d8ef523f6f74ef292a22281e6dc0b8fabdfd2339389919", - "0x048d4dc500031aa56d89e799499a86d6dacddea795ddc4571669fa55d694345f", - "0x684b8762b97a9d650f0f0e5edee73b60a29f6e75573bd6244518b11c4a571533", - "0x5d20bbacb93f7b03d92ae0ce8296bfd113a808ab3a8bd7703838d7e8356b6714", - "0x25efac3c3bc3d4f10ed9918fd9581d68eaa18fb72d3ce7ca8e36525d8cdcae73", - "0x48b593a335aa2699a5bb5a60394845c7e4c78046e050aee1c7f8831249f75b26", - "0x6db7243073caa6e5c0442f2f3926885fae0385e0238a69784ea8a00c854ef8c0", - "0x3a104e4932193c644e2135008d78c5153a9331e6d9dde878357c250a3b42b5e7", - "0x74b3b4666fa9811702d4eebf9680053043160be3a6c31a0105c703e07d530710", - "0x179f67ff0710067d3180ec03d664fb3d9936e8777603b051ebad4cbd0aad7763", - "0x38d5fd43ca73f66127a0166ae074324471b1a92e6f4bf99fde235ac408b35562", - "0x1f43748a027e7731c2fe5343ba7b61d7c6c6933ea45466b439a43eee1a3ad398", - "0x6b130b75bc42dbbf76ad97287a3a130ea29122ce7e48c5a8bd1e80a5f3121364", - "0xcd17f77d87174ab6ad6f2dc60d37144aed40b3620a9e6c9ac3e328aeae3097de", - "0x3b7fe9ef499348315c1a2877bd7fa44b622fcabc588687a6de4d2f75aad3f642", - "0x6c73525865791a7ca8410363d634f6babfaba581d7a0252c7f57dc8c8cec583d", - "0xdb16b0220e129be4c929888a8a46d21d422a352ac7b0360711d786eacb56598e", - "0x44fb22efd89e585079bca47bde1073dc052f8ddbad2c27cd8e2839bd4350b18a", - "0x1e6f1395d417a94162117b9371abf3f781a4b05d787f6a38fb0101bc36e548da", - "0x3eddd0764196fe15d7ac7069c04c4bf23070e57931493e9a0127fc521187b698", - "0xec104582dffc06da3cc1af1c8dc7522d26ab2408dc0f62051da2ebae1ec1cbf3", - "0x3616cc0faf8a5f5c19cbeb482be2ea8de01b2a3e81f067366c715607cf29078c", - "0xd37ca9cd5dc7c3c4e2d2f1b3c8db2a016b52444f1c088680c8544b6cea30cfe3", - "0xc3d85c7899da428a305d941e3637e33eb4981f071ee07c1ee1c82aba7c248167", - "0x62975f10a20de37466b1822859f11774efa4f37fb701f6cc0695d206bbb51582", - "0xd940124857e67e220e3d4dc27eb75ff048aadd9b7fb29b680cc3743b3ab6365b", - "0xc89ac3aa4725191e56fbc87d41caac2c692dd5adae638bf741f0ded040ca66e4", - "0x97454878805915bcb60c9915af0fe0558987dabe5d506e03898dede96544dec1", - "0x6ce55ffc54eec31d980ece5204876a3f366f3148a4b8c10cd190153cfc96defd", - "0xa4e923671f4ff6dfde2f11cca452ed4208808e93e1131de4ce0804cbe2e0d3ad", - "0x772d1c2a0e70fe37ac0ea8d7b4a789f92997bd654809f20f0ff7ad76a6d975c3", - "0x8d5de87bc2484465a4876b462ebc1339bc13b8229e6df4f1a9e9b458f5e9adcd", - "0xdb33cbb2dad0eb38613d69392951c6062eb669035691882fdafc526133d15d21", - "0xb22b8c0887f71de2da3d81a5fec2213ccb8a32060211077e2ed1613cf7962e94", - "0xffbc5a82fe0c2b3f3f34343ba6823f35884c8b1dd80fbaa68fd5f33a960034ce", - "0x9640ded5be08a8a7a2e6291a91bdd58bd108205f4cef5209ddd338ad764fa9b9", - "0xaea7f934206d00a592502b8b85159e64b56def4c72db3a790ab46ca81c75d672", - "0xb99ace258fe4e6be541c6e3468913f4f32ef9e9d1375c889e17ceea0c606e729", - "0xc54ae75381803d00b52ea6fa620766662e6f7946d550208743fa64d3aaf22c54", - "0x4e773cd4fb2347b796595cc67eb2b5c7be6409bd8b1944f4cafecb6fc5a60a0b", - "0x263f3826196c238c24d4c792c3c45fc913d4cb94c2d3871827ba43faecbf4d94", - "0x7ae1714256e21b9b45778795cdedfad1160d571004f5ea6debc16406bc2161f6", - "0x0c271dc055d8fb1ba9bf133f3c85628ac3c2b588091768380a881a6183514b51", - "0xa5f41cb430b02fb1027d8e99cd94dd6666516c785d7f618a0894f38f811bdeed", - "0xbf6665cbf1037e0085808897d8b04932a6ced6755fa52555ac00737e8029c7b5", - "0xaaca2ffc61693a6f379e54af473802770b3971f6accef49e5a2e8fc122e0a490", - "0x7a3eb7782e2c02776aa29964689cb1b880201e1b81c8cef39738d7f7235fb022", - "0x7bf417dda75c46efba6a8344775915d2b69f954afd66d8f52576e106d7a7eee2", - "0x3a324507874480d0f4e8466ed6602c99fcaa7907b61e9f2b3f100740f7866fe0", - "0x3589941fb7bfda9bf50ad93cfed18cfdf199a6468074416aa513cf83cc00dd2a", - "0x66b0965611bba105667a3990de5acdbd398d8d6e2cd0276b83814c4647bfe461", - "0x703258ca6154ec4cb1b9162678e3bb546ca6f9e626702f5f62dda98fdc0fcf26", - "0x2a9a8e3537b714cb3e158f7ecc816239786ea3787b1a3bd40482f02eb0b21595", - "0x46104b558f57296b0775d63ee4da42a716c234f3dbd7479204f35b31f4b3d55c", - "0xe7d9d0a86cc8b76526acb8e260de17508874d1db6ad19a4a84210a010212d43b", - "0x04af6e8bd51cf4c4307b2381b2e0c54cd991ca3c7f49b8cdcaff3aead70efe48", - "0xab8fe05db68e486bf2be0c507b834b6e496c1d1fe560cd3210ed7fbf0e9b867a", - "0x0e6b5f226d0bcfbd1f0a2f61189592d8974b16376fef3d0a67f757b796ad6854", - "0xaab68c29b061f8f72d9f3c6f2e318a7125a01010fb0c547835fa31e72d8eeabe", - "0x0446f90437150e4ec6246be5c718e5054d62cbf5878479457d522948c6e87f83", - "0xd1b4669e21c0b175589c0942d4423cd2b438de6665f0bca10818eb6246a07749", - "0xc20d1a68c015d886ef8fc3dede0d116199480164238617667280f833a4dcbb3c", - "0xe67504ba38aee984e9118960827ddce0eaae3d8797bfc87afd4638cb1867e41c", - "0xd3e985af3bf3e3ad0dbcdbed9ff1b04037bd1ff2e71886db3842a29f0ee8c4b6", - "0x8b809d1ae7a835f318f471ce227f7e7ff563a15d1e2463e8fce5852c9a3f9ce4", - "0xc232b56170a5796aa4333d29ad8ba43dab2233e0cf7b48d100aeaa4b2491d6da", - "0x9c338ecb25290e91a83978df4f5b7076b299ba5d87074c36ec96da0b3aa9351d", - "0x616a6134eee1221e531fc6d6b5861f5ced64e9b56505b169da67ca3c47cb54ad", - "0x4afd1e60cbeb40301c2ccc7129042f9a944f4a383a4f34b8acd7aa454fcd0e7e", - "0xd52d1be650ed156ba12b0d6be4b7fda1fe89927bd7626ec0ae45663848144e7d", - "0xa212644d968f7d3d89c6f12c3c3077184943d986dd9cd391d48f8f98eb1bc6a2", - "0x8e3374acfb9d1724fd7f84c22fda25f91737efde3d667f607b364e51beabecee", - "0xd77eb30cd87046093b27be1a09d93cfa5261b780b99116a79d6c16be7db838ec", - "0x05093b9e39e2d9f4fa95ff386cb2af67861359ea6228242be6b323c1eed5c7c3", - "0x8bb25606225d3451a981af24506a549e2bf62a362149e4c77ac72eef6316e691", - "0xd2749fc4a37792b3716634e3dfb8a80ba3e30fd73bc119069d507bfe7efd8a1a", - "0x3b58bef2d77a04b3281e6cf80f984b9067290bfe02a596b2295ccad38e887a33", - "0x2f69797f1800e5da4a4086909058ec857695a220644e61788b24ccfaf7e77137", - "0xd81872c67fbbd1a69d4805cce578b9f36bfd768d3fcbc2fd610182a7696e23b2", - "0x2d3bc9fd303c12ed1ca7efe27d85c7b5ffb8e079e59c86977a113bbbf863549a", - "0xbaef802512a7ea5006cf816c51c35fdae44a86daaeb6e9dd8fd0c37b4f744875", - "0x2e7fb70924e6f0b74541f2f4cb13f49bb3bd577f5bfe1bc29d805b0e7e1a3df1", - "0xe3918602d83478eb416dcf80103b09a051d5cffc71b0cb21461c5031d38befb7", - "0x87dae7dbb38501d6e84f738c11615dd9eda5f7b77e096a765caebea6a8c691c2", - "0xa19f74ad3f4e218fcfb15e4af95713cfff4f5f58169b789167e2b62617023697", - "0x744930fd0046b3f7de0ecf721e3b36e4b36c0f49eb98bb0c9ed33d40e76a2017", - "0x5bc7194687200989382831785b43f7f5efb23105ee2dd7a620a61622a2afec44", - "0x3672af2176d897cb8f64f2decfd924d74581bd85916be85e53f2542a54a24b94", - "0xe2e2dd1875e9265072d96bda4640ec6beefdfa9a91241ae078cec4c2a1c9b8b3", - "0x5866de65d88610e6123b7a57e28e196afac484045261d1a16b83fa232ba267bf", - "0x7224db0ae652be5fe9017454dd40c744c75e513841b5cd11d5fcaff598265c7d", - "0x04438fdfb56d125bb13f6b8bdaced6946299f8a32610205fabf4a8db9c06af60", - "0x9e9af6a569b87a4717b94d8253a0078409bbce7bc08874e091163b621a75b999", - "0xc8a39c68a74f23d615ad49d9d175086bf4e1047a750165bb071e3cdb70e1d639", - "0x10515734fb6d38cbb9a7ef33ec7831646636f845ac40cb24b08c432422763466", - "0x1604dce1fd615791c66246a7cd82edbfe860a5ac48d000cac1984faebc00cafe", - "0x56311f68cc563946e251d8c0ea74adeee6ed8dd7aba8f6ea85367defd5dbdc41", - "0x3fc81adf318fa6db1c4e7ff5424b235943667f2f3dce5119618e0273eb23c93b", - "0x38576704f6ca62083130dca418a9b68e374944d63521fc3f4b7039754d62f63f", - "0x56e2a402baabf470d0f9c3496d75e2c26c26ac159b996c370b118a313a9e9464", - "0x501af705914bfccf4ad29c38eee21641590cd8d334dce9055d90bff57b8fa556", - "0xa734b7f045d2a23ce602b032357a66763714c6e4785768f07d55c22d2f1de372", - "0xc80e2c739a3d142a4018bfe2074da8da33c471b93f5e7b44bb150b9eb63e956b", - "0x7a0b1355d05b1bf4c634651bbb2b6d65cb0a9772c30e4024f5e781e5d404376c", - "0x73827f7c7a15db5c17a986f31dd92de05579b0bb8def065e5f1cee472d00281d", - "0xcd64ee4b2a912d48e6beb06e7d6c9c236b5815434c0ee21cca0a13ff23dfb85f", - "0x768d2fa63c53689ed899f47c6f78844ace885fa18a36da427428f4af46a9e1d9", - "0x643c26723d5a4831d3d07f8692a6dc4456bb6190ce2abf1abf69159942d5d548", - "0x03e20a75c4546d5f54bbd7fd3e54c795c180b880563bf78ec55530a89188a9a6", - "0xa0ea4095dd9fc6f817c656913f8cde4044e2806488be48770de5574c0b5d5f8a", - "0xeead5fac8f3c83c5a10df161d95bcf1d27184c9fb9fbd813ec5f61347c11280e", - "0x5afba4426fcae0f1769e581fa6af97b5ef8fd417771f10405e1c9d09a74357d6", - "0x087f65be7fc2a14f216d7ce418a03fcf1e6169e8620db65c11d1ed6c0afed240", - "0xf9a7e93c40788db39b241e03afe329b6336187fbafa40c97ac405fefc1eccfc1", - "0xb02450b58c5afdd0907ee745263aa6beef662646b96b9ed28b0dbcea4f358667", - "0x5029f1169c92671ecaad7239f40fd93adf3ec07ce2ed0c4cac38b0cc8034def7", - "0xc8465a93a1ba7ec3296d98e0b01ad27bdbf16a347c5517b450905d3c3668d505", - "0xb85cd82c551bcb30a747b2258327dccb04094f918e36c3f120f55fc35abf59d9", - "0x1721b2fb8963696cdf32385fe87e8ec2c2d7fa34e099bb65498e4a030e20a1c3", - "0x6b6dab262c1a32a67353409d2f8b791b1799ee6a8e3c880877af0bc5cc5b812a", - "0xa634110e4766d3451718061efb890238796370da3c4a53a91faa96c8944d2423", - "0x91ec6c6f807285599e0a179d0d246caf10152e733acf3bb967bae35fb36561ec", - "0xc3b2012d5cc8d796d1890d39e2b1730dd53df0b98274bbfef8c93bd969912852", - "0xd036b9b29589cc551bf27ab95b6366d772e7d692d8fba48f473a2fc2d023dbe2", - "0xc51229a0306e56a53bdab1fda497281e23aba6ab17301c2eef3ce3d01f56989a", - "0x2652cb79e0c18dfdf545562b8569cc2775a1b0b1b465cbdc5880b40ffff22676", - "0xf24e0d6c03961043cb41638596c4ca02e2a2522a3e828dc4613a72ce5a535a67", - "0x01725e03a7cbfe2d6c5623829e4d419bbeecd1c7f925dbf1667979bb4da6650a", - "0x3b0c9824b726b2b556c6c46af48b84f856154490a51d775ba06aad48055bcbd7", - "0x2be2f1cb826d7575e53bc5e14f1882d73bfe145ed91b2ff56a885f66e136db46", - "0xd16752cc862f33b14f5976049dadc4f15f147f2fa76c50bafb38a7cb25c8f881", - "0x66b3ba188143bd421009c082031696bfd6d29fe7b9c3345e7e70bf6a470a05ec", - "0x83e017e8701b533c9fd22e30d63c3781b0ac9ec1dff4433fe7fb5c8f6f4e67ba", - "0xe4782b025953c5980653abd26eb95de1bee0524c14a74b970ec5615f98ed6768", - "0xeda29e9b36262e7c79ef9c0b60fddc66bae542b19caeddcdedf439573f773cf9", - "0xbc02ac1f023993253394ca965f4394bb40f9c7822ed6b2cbdd249e4b72f9b637", - "0x8e1bde0f2541d20b7f7e8179014e0f6b98eee5c1e0278ff1de38f4c13fdb4161", - "0x7b762d3d64aded9ff99e3423d7e676dd307b765ca6f1295e079ac53d5a4788b1", - "0x1027bb44ade6a1f82f11e9f298fd3957a9636bfbb97457c319e3d57ce72146b3", - "0x963864b3164578d4a7e58de16593273067a641de752b6df2c9b8bfaf970392f1", - "0x16bdf92929fe3629a57f737d83328d034c36bbdcd006301f28dcf52e1d1cb542", - "0x69952e47225f1aa86d952afb0fa8c668ae710a10cb6a94477d518c8f771f5c30", - "0xe68895f4ae2e4a35fb7e0730a5ef9c3e3030f6351ff6381f77e6311912ced98b", - "0xf28d799eeec538dcb2f371cfc6aa16f4a6808ddef0e6fd0cf72fde291d94f8ad", - "0x5a404922a9bfe57eb85deb66d8d83c869ddf96eae17e7fdfafef19c19efa1eec", - "0x96b735672e85aa95c2f8b4bd5ac80942923cff64a24991b3103e4ee39fb9a8d1", - "0x83d922f50174810fc45daa5a607a9b4fce69d8ab86f428ac57ffdcd9c2ff2908", - "0x3b0ce5a62116eafbf445afff0674112f01e1dca0e2af2b72d0cbbdc452177d65", - "0xe86cc93417c7dbcd4b5f051f4dca1394d272dcc2101a9e94a140b20f5e4c8b59", - "0x99b3e1d593b682e1b6454675593ed6828f8f4c5888b965981e3a7c602d89d031", - "0x81e0eafc2a2adf3d94938c413cd9f588e7525b91f39a689dfc3d0ce6aeb812a0", - "0x699e692ff89a918eee6d19a63caeb07832dabf1eb28d04ec97150c87045d9129", - "0x90ff00b66a14d821b05f692ee6d100dd61abef3234fd29e94bc84574439ed2e0", - "0x2de5779a122ccd84a88c3adc4edf7c1c03dd1d3e89ab45657885aadaa087e833", - "0x775a5587a907aa5ff13bafed032dd96c312b19dcda1b0e74e8a4bd327fe90e50", - "0x4fa48215f975442e6b9ea0629d308667242a7fe89f0cd0eae55ef1d35a3d6ab6", - "0x2e24c24731902f1b9e4042eae9e946b9d884dfa9f733ea5d4f7e778b68daed9b", - "0xf23a4a6061f45b1ff2095adf02ded238b37a0ffa9653fa9c1b0069e37e8552be", - "0x62b94eeb74bca8d9ea91aaeec5c13a05dae022806df28b92ecee99b47de999c7", - "0xbe6c1797cad2d5d9ddac3b3adcbf1622241e2560e3407139e24dde3fd8d3e435", - "0x7df0788058bea0911f2e30133c835515bd777f7aa9cab6bcd27eb3c0a6e360ea", - "0xd87066a4721ce567f44abf179184381d81c1c487158a6c57b5f2455472209a5e", - "0x0aec7d3081c3ee7d61f36e8c7e62ab74e41f00b664b690a341b9ff7feb5adce6", - "0x16345b31779e80499dc01f9ccaa0e9981b2b336500f33cb1f2943b66ccbf74d9", - "0x9ed6c6685dfa2b65903db0b234f4539906121330c5d55e6b2a2fd30549b2dc23", - "0x6539602958d9166335a7a0396ad72be611661bdd40c786cf9b0f382039c6b46a", - "0x72bbb4a201de75b9a4b5cf7381308953234c62f295df08b919c12535febf6fea", - "0x2d37293863f6b90f43979ba5944bec302008957e07b7c7f2292074a0a3934674", - "0x22677859ad20cf8b924d516b9f979652ac91a27459a4eba5455beac7f5f23128", - "0x704c898c04ead58c2fdb2c753359d10934e66b326f104be28ca7a32ef05a2bc1", - "0xca2b1ac29937067b761b57b58ae4069eecc799dbe089342bea274e56fde3d1bf", - "0x4a9773e6a2e75dcf1ff46c8f9931b8170a3609023f47c0ea9c4af000798bbc22", - "0x7e4dcb6c256eca2cbe9d168cc78c2702c373fd6e69c210d0713e2766baba148a", - "0x399ea7dbb66b95bae693402eec304f9cb6f4c6ab729d90ca569fcc2bb24d6442", - "0x49d0e561773458f834c96e8fb9496d4cdf83f2cfab75824cb1eabf8e8962c18d", - "0x80a0a209e41d0c3ff07ffe6a7f1af67997662494a327fa7f3bcb7209340974ed", - "0x2bdd6d6e7ae3f4386fc655817e92563e80f46e1b277be22f81a689de7637ea1f", - "0xb77f32374161e54c50dbc38822760874d966f9e098a2ea7aedf650adf25cc0ad", - "0xb49b1decd65a998a3dae2e4725eaff56276af3f0c50b2a3a35b6e94738d32808", - "0x319f78ae885011458f47ff1a110881cb4ac6a78c7d4d7a2656ddca73a88b58e2", - "0xd9fa2f47bedc0b405df34c98582b553dde76a46d38ac86d9d357ea0aca60ec2b", - "0x4283ac57e82bd08970ada71f4617728bcc467eeebb19c3aa20208a82b18fb508", - "0xf2ea3a5ac3bc77ff64f7c686305044cfe539856fb2833cbefcb283317012062c", - "0x840a9b8c756c3c3de7170c3597bbd085747b97419c01bbe484fc4cc7924736ce", - "0xa88682b957deb995307ed875c4044246d779e3f8c584cad75162fee119613806", - "0x7b9195c23833f65832a44d097290d8573b43e6f3e47dcb8c3826ef9a52fb4ff7", - "0x57bb0076c87f2e7187ea92f9f7643eb5b4b823b8eae9f6e74c8b676dd86b81a2", - "0x68593a8268b245a5c7506a05860755cce1be795994a7d736aba41ead4c025a68", - "0x90e21d5927d39329874688312eeb5296677ecccbbb9c6bdd4400c50c9bda09c4", - "0x773c0cde2d1f44575c89106a01881eb5d9593bc762a40be03ba979496ad7d229", - "0xc1dd843534e9844bcf406cc03b277e71d4e73026635412e25f3555d099f26a55", - "0xdf3f794bbd98096cbfa17e168c0de845383abf52fea618937ed81d31cfdd88db", - "0x1b05b1f316013609fbf813cae674f193a9bd8a75631b55278bbd37513b85641b", - "0x429321ddb251fadec6b6f794acdd8cc9d93512b98af23d20749d93c3c9fdbc36", - "0x6cac90b28ed13c907d094bad45574aabe2355e13e6a9504b6001e5fbb9c25235", - "0x89b43a3f63a2ce4f67071a121f447d7e843948395616116ddeb57a8714becd3e", - "0x6de560c95a0483d51410f66f38884947dfc787e1c61d14421129773010b46e0d", - "0xa0ba45049971dd4b906e73f917fd16312646d53c0cdfdc3eefe53628a58973e8", - "0x3d4a4f7155eac18fb5a126dcae2035155a140a84718f33bb20d2f1461e8cedb7", - "0x1cc19669bd91087d2046770cdc34e8f995cbdf2a0cc62bda70d6074ed58acefb", - "0x7c1c4aa1817de27c62f35d66927d924376798c954d65ba3ba02c0528d11d748d", - "0xf0d67a8f3c5306165cffd44476249c254898bcb26c937f10e8ae244edab1b972", - "0xcc20c5ecb1d3e83bd56e9213761f8320bd40982ab5fb669bed774b4490637932", - "0x7935073eb3e5c37ed1135cb22bfeb0e21727d170d106465fc35de75e8d56cf41", - "0x7d968e74212c501d0bc26ebb816b57a37a8cd2720caddb5bf66f489e13a61bc7", - "0x2c134dcc35d50c63a13bd8868137e0240280f049d7e392b97ff5f76d00aa1296", - "0xfd046f06c1d46d9125a119f786acdd76a85fc596f21cb15f367933b717ba7d83", - "0x9f5f067e4af3c8e92d2d54dd061620f0a13a66006b162a1eab4b1707499597df", - "0x8a6e1fb6205a423ec2920d448a376b95cac7233d5312287bd850471fb49e4f8b", - "0x2a6ea987659383f9885d24c935fe56de39d45caa89e60ba1768189318974ed7e", - "0xc2df6c8a4eae77eeaf11d7e5e2198ec4a33f19f5995caab4db6577fc1ce7b957", - "0x1e2ae8a42eb937749284820e50f11dfbfb606ddac3efb201e0b2664dd0196d63", - "0x4d63bb91f9f8a9965c460326f0604a27ecb0fc56f2126c6b3519b08a895747b7", - "0x9a46c2ec5dcef6f5c5b76d4b08b9d5085709182243cb8308a2863cca8cba13dd", - "0xea57019cc85f7cece4cea7eadda96dc9e464df2689957ebfe8d817b6996b2e43", - "0xb40e930b31dc1508480ffba351b102c8cab53c6603a0dd88bfed1b0da5347153", - "0xf51ceb070d8e7cb998cf4979ac985b4850949b4456980f523d8b9d72604a43da", - "0xddd28627f5c7bd213149bc4886bf4bcb8304f86068694fc743ac81ed749aec3c", - "0x9ef2b3df587caf086f4a9838a829491d1ef45db132ea71c6bc96a4a40d833e92", - "0xc9f7d4e19501c48dda5b0012cd93564898ad080a653e29f3563ecd40d36de84b", - "0x0565db36e6fb7b881eae309fef6fcdcace7c92a7ff148476b627c012aaefe4b9", - "0x08912abab10f16b92ddaa3663375f6e2b656e4ed89f2285aab6e410fc59e650a", - "0xefe9e68ca3bea929722bcccb5150884eb23c32153b14644b1c7f4e033dc3f718", - "0x4b33e0e078056d9efd857f909d1e409516f23da68105463167e23d71c90f6366", - "0x63cd4d1c69c4168798a3d9f15388207ea10ee4dc6be3681b0c7dbce5649d8f90", - "0xc8e7c25854d49022e9a0ae2eda8e7835a6db9ef7d612312e9deae23621ba240f", - "0x22c05e50f27e8bdaf4abb0a045d0639bd7f80057dfea638df6a7184ac49b738b", - "0x4a34356b5a447591ac66c51370fb6539bdf50fda9600082dadd91d33713a74e1", - "0x7147d53533ae40e886f6bc1c3b65c51570d72398f46e3266f2eba7b707b46b3b", - "0x1e49f75a30695e9bef14b036bd1c0f2b322042b2a02ca56604484e913b284c1b", - "0xf2445d48e823bfa77776234356ae0d3c1a850db236b3d2a95e5b00c4d7e687af", - "0x7034246c6342c26f5ae974576358f993d0e5e3c577e6aecdcc182c78082ab181", - "0x965067896ebcb2628ff10127508df1c811087f45ae258a0d8179d576c21e4891", - "0x384ebc34021ebdaf95e1bf0d8d61777b96a1ae65163cd3bd9b4311970a7918b7", - "0x1ca6e203cf1e058f20f5a8b1e33464801bcbbe04c79a7201ad6036bb8aa54101", - "0xf5aaa21a85fa9d502ef278262acf71789a3755d36dd8b3794becab7fb2d338e7", - "0xdeb7dec7ec133b6ac05c3bc0fea3b0002c8ffe58b135f4ae85b71fe0350dc7ce", - "0xd5773586ddb40d4c772541f743f7a4b08e9b419fe5b5b3536afa0b3b888725bc", - "0xeaff5bc016770c7cce7be21952cd8759a8d0eeb1bd849732c15dbbb82c613a74", - "0xb1a6a6acd39b4428accf9120a46d74cca6451ea4a182206425a8a64c6d6be5b2", - "0xfff27e5023fa1fc365db1e79cf3283bee2f51333059bfff47df39f12875e5fc0", - "0xadf1ce203b0acd4cdaf1d9a91cc158a21c823dcfb0740f089ce0830102b1cdef", - "0xcf20c92fcfa22d6fe7a60d1aab2d5a942db39d447d4ba1e9e76888a64694f1e7", - "0x210a684182ea379b50d641aed1baae3bf3752fd84feac4b3dd4e110c5cfc4ce7", - "0x220c5460803cc8db7a2b645dd5a4525b0703182cce173324d20e1c865a157811", - "0xba87b487bfae148239b44f3ad7663fd66cde8e21aac9e1a50bbca7bb7334aeff", - "0x2728161f9f040b4d92ba838d841404604d4d838157fc66f20d6c4c61034fdbda", - "0x3f8a6c7198528e5084b3e8d52d3101c27c5bed8721beb831ff921eaaca4c4282", - "0x2cc2574320b3c7252404db7c67b462fbc8d87bdbeb782ab1bbd257ee634a35c2", - "0x13aae0ecdc6a70d85412178ec12e971b2c4476d5e624938ca0284ccfa611d11f", - "0x6af7161831c3442db94cbe28ee9385fe79339d533b9c0fd3266213c2a5024a14", - "0x06185136927e5bc26ecfbf2299a0fc13cb447df6ca4a60e968be8c2b7ba1c2e3", - "0x70ccc84931d910a2489d50becf82383a836309bb90dddb21026d9e4e4368c85a", - "0x400ae9e4da0f847dcd4bb65e8f9f661a3b0deb78346b4f1f84fc712fd34410f0", - "0x57aacedf503300888fcd8db2138badbfcd663c63c3cf5b3e35979dad635c24d9", - "0x8fea6cc2da76b7cc7062af870cfacf4952b81f09c3c15d31145fbbfe1c0806c1", - "0x5125434cc5d4137ee31b51ed8306b4d665b8dc66504661b46c227e62a9ef1abf", - "0x254bc0b61211e0a57755d73ac618012938472912f855972b7ce62677f5d0e64d", - "0x2d231389c849ea459a7530ac1bdffa4d84908e2c61125a70bfcda932cc8e5efd", - "0x671ae73d4739bcd4841fdff266803117c5684c61031fff16e831a3bffb4bae4e", - "0x64c4db66cae82a96f29296b481619d79a739e2dfce0ac1f659d45f526ae58ee5", - "0x3f2f08ce2f21253f8c9a3fd650a885ca0e77f720a21ad5b4c0867150a0274efe", - "0x98c48268710592ee4c26620375968f2b8730a1bd1777239e6ffb9f116c6e1284", - "0xdc7a5c095c255e1984b4a5fa75c7a1d95d98097fc4eba898e644ff66951d8dcb", - "0xeee7579812ef09ae31068e8087536362a967b2893b709a458822449ea89a48fa", - "0x296e707796c0b9f9a2f55ad06c42d03625dab94af71c2e1c7016a7ef6645bf5a", - "0xd9a3eb363d4a36300dc4d1903a83447c89ec286f8d219f1156335da283992d60", - "0x039adf5a0cfbc394847d8014d64700ac4b6e78b531a1e0328bab256f7c407116", - "0xf9a92c6b1f0b0b3d7ae33cf5ddfddad516bfd7b21842d76098737533efd4f7a8", - "0x5a5d1fa3b8e05a81173e627f14e689c166776b93df401593db8035a65fba58f5", - "0x34f7fda3053b9d06e217223ee06fd194e2962c4a381482429e596df1fe319686", - "0xb4cfd9a71a98ad52c7705d55e96f04cb9064b1c32d3c346be51370b56ebb0f8e", - "0xd46a93765af68d238b776b240afd464a24d8c8bc869280ad618fd0fb6360e878", - "0xf3c622a4ee05d1ce27d59e7b9b3748547f4efdb1d6ff72a58fa93dccb7b76de1", - "0x6cb017c4bd8bb5186590cc4559fd9600399485ca917b10556b98cd7fb61441dd", - "0x7188f59c892b8754845d73f534587f27b7da67f42dcc1c73390fe2970bf0ad28", - "0xb4b17c93af08b9f587963e42703379c5e4f760502870b8096917b09b3950ee35", - "0x03165bf9bc20c87412a41209901d2bf3c8bc03a8586a1706fe1499641cbc4775", - "0x9ef57b2126a38c2dc456c13c272de53366dd1bf1fe768185a93f3562d064fa8d", - "0xbc3660089065220589409f7063dde34aa080179b3f22464fd9df9eed98d88b0f", - "0xbf1cf6eed0e0270d6be602040a97600ca7e1279db4279a9fb7ee643345a264b3", - "0x4cc471987bfae3b32179aac7018ba574c0315b9832915b5d0b804b38e9def6c8", - "0x51351557582b1d821adfdad36536b59b28f9a1f1243115486ce44d4b22d3952e", - "0x66d6b02183b9def37dce37b66ba4e9939241732b49dbc8addd147a89dd9e0517", - "0x47261859ce169e56d4e7dd75c5e2648597c7906f9264710c7e4dff74f353f739", - "0x38edbf1db358c82d0c945a7fc024f7fdc1165331cc19caf6b8943d3ce76b721a", - "0x5231560201678a39175187469f7e36c1c729ae060225012ff4f679f3fabb8237", - "0x763bfbeeab624de07a6e758368f8f61c0fcfc8cda088d2dfcbeb47a5eea9648a", - "0xce910446cce07477d424f791a71a375830ac26a2543b8bd1343e0d775d161e5f", - "0x950ba34133aea3c2947a5e5fc1836375e81c042ee999b60dd4a27e6492fb41fe", - "0xdc24f20fda563bf74ff7540a06f3631af8ab3b3722874a6b383714f463f5fb43", - "0xf2250ef512d3a11144370747f2c86efc73abaa81152bc6083f87aa217d16ee67", - "0x8760d0bc8f28eb2504f3bde3e429a47f0aed1dc2c7abbaf01c68033c07ad368e", - "0x7ef1c11b1f025f7e2aca2624aa9a11781cd860f24ce3fb525e7196e590fc5ff0", - "0x7c4e717fe77c8f9e742e312081d51418614031745b182746c7ceb4ff54deab6e", - "0x56c82f80b8d52128275529fffb3ccf7376f411d7cf9464a15fdecf617b4e7571", - "0x46f7d3ecd09c29d36a62a1a65bd3c59a14c82687b5466c9c130df14c286c2a95", - "0x1d04deaadcfdc7a1e5e612df6eb836f55caafb4f9bf4f1200fdfef4f14229f26", - "0xa0f5e65e99a22e14ebafc71f093dadc96c68883609a7460f904eb15360e3dd6d", - "0x8ea6c444466d4b6916a1b6dadafb87d75e2fa1ddd1836425dbaaad6e99f4d68a", - "0xd53d052bb709813e6dfe218dc4bd99c5ae83b6f5993b182c36a386502713c7b6", - "0x2e0cc1dfae87825d1a1d8946e021dc514dea384241e0a6cb66d5daee570c72fc", - "0xa419612e2bca19a3a8dc7fac4da86c2df4edcc2989e10d3e9050323011676b1d", - "0xe41649d9af504bd273b9da0fde578dc126afb55feb8b3c31a0d4eeaa9c7b83f9", - "0x0121be707b5666d5a78949b2ee263bbfbd013b69caa9566864711bda4c7ed0f5", - "0x788538d654618bcef0a63e31576e19a0872a92733ef7930ef50f8afd6caf110e", - "0x7777a3a4930828cc158f696e6ddcb87696115f473f219ac5582d8a38e0645430", - "0xe15a9f42ce5964358f862fa7a40bff0c8e8d7429a5ca923c9f0d4d0d574378a5", - "0x187bd59945e1cc6a877fb324b1d7ebdf661383ae7e22c56913f2e920de73dd68", - "0x938f100308d20611bd14372b16da0dcbc888868f8bd183d667064dfa8e67a161", - "0x5e61540787c83bfacbb58967280163f55f5ed00e733d6295099588557ef2dcec", - "0xe6625082f4039ef9dccdbeb9488baedf75fcec616ed9d5009deb4eba95cc680f", - "0xd01542aefe234567f106a4f057173b4f6eb5733e0ab9537af2db309edf38763c", - "0xee0174f3f9218a3418b8dd2bcd4132821eb91b31391b7c2c6e5a84d067d21497", - "0xbe26c679aafdea135aa493bab8ee348b255f50bc69592bbe017dd96b0da58b1c", - "0x297e6634c06193ed4725942cec32ccc9b4e77b5d02fce2ec9fbf580e3dfce248", - "0x820d98bcfbc008480ea32b162d15701357f094b1d7c99a1ff92fc0afd9708a06", - "0x82bce2be0a2d468b2fe0d3ba4ec1e5e8eac2d83f8b2e402b3043119a59cafd51", - "0x63ff3569d9a5661b6773a1a5fc10a522ea12a22399cd337ffef75a0d36735ab1", - "0x8431746d8239126bedde7d5c58aaf7f733dd1542c942d415d876ebf8a062f032", - "0x6bfdf119b93ef4da6f48265f4c526f0837a10c8db9c518d0dfe1edf40ae5fcdc", - "0x55aaba1f40c9089c65623f67eef8cdb827282a39cd0778f26e2f73106d3eee3e", - "0x0022a0b29d8188251bf5c6f37c76368dc9c7ed9e00376901162d1fff111273b6", - "0xf4bda8d3cb5b7ad50dfcf2668253e44b98e87d563ce17720dd1eb1a4e1c32628", - "0x994315a889329452a3e08ef029e7d902308022b74aa5a4eb2178929425c90a84", - "0x86a962d1d436f43f6fcad5b61b615f2bd32f10fe8c62428854ce98f4289707b6", - "0x3ce476498f26fd1d0b276ea639d438d7efd3c10451949efee1c91f279ef15ed9", - "0x199b2fef89c1edbe547e8c0b666b7b138d6f94fcfb2f09f26edc429ac163b127", - "0xdf3fd62e7dd0133ffa23a0da13d720373b57e85c28ee97890c355c44323ad592", - "0x92e0cc3bc262330ed8a1f42ad40a2db6c4e75e2d39e24a6ed5eac0855c12dd05", - "0x5b46f058c21b9447f8faaf78b2549f7f1459fa5ddb4211150bc26dd718f8361d", - "0x316b4f0e5b50cea376307236de36f3a1ebad3c59ae28dcf7838339d8711047e6", - "0x2b72ece0cbeaf94140b99cd9312eb891a1264a4d31fd839017e22cd4cdef058a", - "0x0c86b9b2da38f00150d49ac53ccb43a88a44181c90b492e886c54b0d6a93de22", - "0xba5a671174dfd7f877bffd7fb3179b1f3f8444ab14eaa9a0488207141bda26e8", - "0xecf73cee14b1a8fa5c2de5d78c058bd04772666ff455ca4225ac419606041f2c", - "0x1947b6adf9abeeeb55a66cad4afd016f6522faa641c4e14af94cf6e610959ad0", - "0x5467aaeb96dbe111a0d36fa66a71f489fb33ab8d95692695c09f4680086daff2", - "0xa21fd9195eaf856bd048bdb258507351e9a2c168920fd0c550a2340b5176ba26", - "0x9b0cf5690d3c3764f5c102fe1d5139202a1f982bd5afc8967eddaa6bcfb3af42", - "0x32bb410896733f9f6080a5b574b07c0af3e5ecaf69e995751e392c3905c11d20", - "0x5e98b3dbf58bf5adc0ccc9269aa10f9921afd44283837e7cb419ac4fb89f6164", - "0x051cd6e01ef3ebac9f27e1d473b0abc00d870a78cf894cfa8222d00976948b39", - "0x500edc8298fb83a103f5bf779d1df507644e054ef27ae61ccf31d883d85c2a0c", - "0x80c8fd7e50aaa14da3af3ec622adcf89eea9760ddbd5232a49ba55837be5805a", - "0xbb828dd031299bfd428c22110ff5d9f5612447e346e98401ab4a01278834e476", - "0xe2c5f408029af25cb9c130fc8fb5118660d08da399dbec0fa1709d1c0583de57", - "0x1be80d06b4ec5ef612e16bd8d842484039ca5663234174441f0722ca521958ee", - "0x21d755042a542493e44e92b4355af2f06f161c0e2583aaf6862730be7e9976b2", - "0x49b993b77606eb939ae485e82243e530e392af6d68be4ece5ace66a675a7a70e", - "0x954823b80bea8f2007503ebda5a6ae4610f94cc2c9a6ca22088a52468a960524", - "0x2693662c6c0961a92566deaa4a59204a0c436aadc0581b799e6255fe97d26331", - "0x4e80abe082c0b8ae0602c232ab0f766aefa702e744ff142cb9e101a6050acfbd", - "0x9c47c762c73836210a6bd78e5ddf9f2e817951d52b9fea0c823596c3df2a1fc0", - "0xe86094c8da0212cb0cd96f54c9f1b22c11feadc5599f6aa63285971651f11278", - "0x3031451f37f3e0288d61580e5b20e008a2ef5975e5d12345056949dca2c1d421", - "0xbdf90347d794ab3b41da6fb75b5d8d1f426ca2c4923216393e055dcbc89f3cba", - "0x4f7a0c9ebaa4833e7fd0ae0f3ac8dbecf3d97d0036a41ef30230e634142247e0", - "0xd1cdfbbf34bfcabe4a0eb90fb4d8592738203d245d68b753d418c4bfab8ae4e4", - "0xaa56db3fe5b2edc2ed277656deb51e15f86182de49836b4dfe2636de5488a86f", - "0xf67b05c233797d61eaff641e5bd35ee830bc1a8440e06f85e034902bd1023ede", - "0x49bb9be0064ff46c4b7820e7dd08002f3914fbf9250c96873bb3dcc7bddbe3de", - "0x594efbc23c0a371e3e5478c599466ff3a8d985444583b70f275afda13cc05c9b", - "0xd1dc59771492ee0881398f87a7c90db42874d720812c1e105b1512531d9fb1f3", - "0x7462bfbf8457d1df1288cbaf339861dc91c02d772f3c1ff8c215965e555d6905", - "0xee62a752465879dd62d08d7a15a54af1e813a1bbf2035384289bd634e2e99524", - "0xc7c66334011807d017e0df794df5f2f36c4cf496ce0a3589465662f8aa5433c7", - "0x839af48e65e3e1fd3d177d90f1dafcbb0209b107bf882cfe2fd514be625c1396", - "0xfb4eeb8514ede5bc952beee0e7e78c6d7ae544e6c4d935a6c92375b224e40c3d", - "0x38576e990356b7c44eb1dd531fe28420d01d80130e0ef0a42b9a8b01d2687822", - "0x434bb4312789b8cd93c5f930f4305760f86c54ab225b35bec70954aaf2fb4c2d", - "0x19ae08dadbf2f7da90ce777913d59e74592cacf6f385600f7d50cbbf7c4137a9", - "0xfc9a8ba8d7eff3a582725f8fd1539c4f77a87ecf23505de8a321ec6d568cab5f", - "0x20fb269b1a7908ccae92532424cc94604ca9a0908bd7c5e446a687cb3be9e0d1", - "0x9fe0a0e3511056762698573eee8ee2b0b87a8b6daad2141a9ad00c5b159521f4", - "0x3630751af37fd3ae22e78198868341e86735b03432879fb159628937c6bc28da", - "0x427789cffa2250d89b99d50969d8dd9917b5f4c721ed9de79cf81dc59f94d81f", - "0x5ad7e16bd42e35671230458f8f97c18baa5a1f81b675df259c9d2d7cbb09fb77", - "0x3d58c8ee704a934f6de776ef8373c653140e089b85aedd53219dc0b46ff03b58", - "0xa5a0719b16d8771b34e050660ba965b0ebfaa06bad1973033cbe2fd69fac5886", - "0xcd66e1ce23416fb4662d2b29dd72d9f8f981c66098820058451441213f2947aa", - "0xc7eb6f1df45136c9adeacb03eada4557326aa0a22f9ac0d73df25e21beb6bc0e", - "0x890eb4c610c7036e1494514e1f7ff72f414c51143fbf13cf2cd03d8d37a03662", - "0xa54328a1d2ffd9aa3f38a9e6a0539ad2517c4f6129f01768891acc0a2b2a721f", - "0x147644decd98b450b284d05d7332629e3c6444846f7c71dbcc892d3191f2efdb", - "0x1a19c1a8fc7f9d838cbd736243e66b1f637f49b1d8734c2af417227a11623b30", - "0xa8c14722a6f7e7efe695be4e6a21f2d1c8b8d71e2cf69e7645ecc5cdc7b6355a", - "0x8eb33cc1490499aba376f581b68766c4fd40e7d6027f223eb46e27199acc8d67", - "0x1b6a906a5321b057f453624693d4ae6abe79a5b8fcd63a777dffb8b2ea4184ab", - "0x7185ec1f19e7a84c9f914dd223b382ff56464b01a1b88dabda415a01e1d0a1c8", - "0xa172df0cb06617eeb95d362bb36d5e5ac52cbdb2e5f3c2cf3d9b78ef28fad82b", - "0xa11941ce1c866d077fdc995acf3ff2ee0ea0481eddd142f9b343c8403ed606a2", - "0x1958ac9a77c5b9825c401b204001dea8ee2520983fb3e738a467980a7bf9defb", - "0x6b24d043eab24359787ed7e93543967c9f9b7ccf99894c1ee7768f6235cb0cfe", - "0x6ab8c28ddda71b937d1a6feaa67b071f78ff7ead3a192ae63dac34ff24b8d929", - "0xf101e3da7546249b5b6d4dbe960c713cf152627a7482fc3377aa31f430c54530", - "0x22628e903ea9eb3eda9c93ac92d77b1b8a5ee62706dc5ddbcd079c57d5b721eb", - "0x0050467a543fbf0a232c8ce9f66eaec6c38c3c31b8ce3d590d1b07586374c1ed", - "0x4235a1330a45838b2d2aa9873dfbd59cfc0f0cd16e13ca9292f8342eec255fa3", - "0x2049ff9cebd379b51308220449b3568c6d7843f5b49f61b85808287f3d60441c", - "0x50c2ef0e832df29297dc524fcf3af4cd0988fbfea71987f3ac5cba8d4ec34102", - "0xe7260880b2d822d18c9ac2224d09fa18f5c324fb421470aad5af6c2605b40985", - "0xdf51e1b441b9809b26cd7cbdc4df27eb9c7fbb3bf764971684aab6c63e282a75", - "0x597b5c48840a25294feb135e0318ba6d6ca09aac476b1041748738963136a0a4", - "0xc710bd4b45a991f7f1387c25db70a1964ca4fbab32b738424d35a6e06e2483b3", - "0xd67460a50ec79c90a97e25d4cebdeafed6c897593fe8b24afe47a71c818a55b5", - "0x0ac1f2282e4491fdc8d28f1697026b7fa88f46204610cbaf8811d38dc84539f2", - "0xc3352ebc532273e4224dc0e94ec4cbb83afc2b5c364d71534344793032441006", - "0x42f3d39c81e118a7d515d82b6880104909a7915aeaca8ad64fe74d9fe88a7f45", - "0xac6fa42771e9cd6ba31dcff2455d2234c53c282251ca2399c727fd5521eeff0a", - "0x83ae44822213f59d34543a6d4fcf76b4e22cbe5a90674755072613550d348551", - "0x99cdb65200f9d1c602c5c2ded67a8cf2722cd7c1ae3f11d29a2c1b702923df93", - "0x2adda7cfa12bf5ecbc74ed4f4ea419ad7cd3e7cd03a0e2b32e924aa2ab98ec42", - "0x3da7434a58c60b7fc7fee8e30b3073ff4d3c381288fe6ba791f68d74d24ed19a", - "0x9dbe78f9121e6c0d99fa6d441f6aedf54be14fb39a277cbb5f19fd0b846305c3", - "0x2ded5acc49e2e1fcb57a66a81f59405febb50edb6b41d0fa8e445477028f422c", - "0xa46310f11937ce81ebe8d4c60de1a5c8787a1aac35ff3b6e2f0cc793112b7df2", - "0xfa693856fd1867458c335c6e903e5745a950ddf8a43cd9ee76ce8d0b3070bcbd", - "0xb374a194d9edb55d2946d40a16ac4617e4d0674630c6a970f58db17f21d22c12", - "0x651032646288a0b6fb5322626bbbc5b6b87dc5a5d59f5f39073f2f9574646c41", - "0x7f4346336c95c7fcfc1f849cfa63afd46dde8e346ae0801aaaffa9069f989e99", - "0x8bedf8e9f5095b4275635525fea6169c5afedff2ae434e42c5cde973de77ae3f", - "0xf92ac4f6fdd7801f15c8e0ee163241542d1359372d2189d8cd2a33f925933cd5", - "0x806d437ddf966fc6631a8f81a54eb82080fb2ade64a2b715872d8f648d23b57d", - "0x2151451d68f912b58bb510002407943ef6f2cc87a992e6df0765a4c239e63779", - "0x6a02bab7734d8549be10373e6395c368492b8518df793821962a940d5cf93654", - "0x4de627f1d096c86c1c1dcde2a314ba65700b1ac47b9db95e9ae68f8d28f52b16", - "0xeae202c747b699171115bb56652ed534d9ca6de0e8c3d947d63c6d60b5d658de", - "0x7bddba4a7c160445653f62d27c1622323c784461d52aba5a6f19964194a063c5", - "0xddfcf0c5ff899f19c4ed0604e7b4cc645ce5bdb7b7c7d6bcecfe7f2cfa3bdc0b", - "0x7d27d279f52b83afc9a115c71910d06a054fdc2e20625cd05fe35ecb03734f5b", - "0x415f6bcb216ad524eebd7bf177bce80cc3d5fb3e920ed65a8ba6c02f59fa88ba", - "0x570daa81ed1b593529a07396784c2996d5703f4124f941e99e8f9cf6b608b1f5", - "0xb9da28526fe8300989e16c83c4bfe418c74878be1bf3af12ff3b9a098a4c92b0", - "0x691938f83dd63e39be2fb03422682ee8dae8e0a95bf4d09b4727f8162b2da11a", - "0xde168c58358db826c9c953895b7a0419066e65eec0b7fa479c328719cf70df6b", - "0x65a7b2207932e96c427a6c01efd0c3f01a37e02e7dd98b18ea559c2a6c83c8d2", - "0xfe54ee65b1b8e21292fde2eed9ddda163036cd6745254bc7cb9f3dea737832fd", - "0x543d64c2a6b763cabbdc1a9316f37115fd572d96b5c75184c155e68532fdc8a2", - "0x31b41a4f481a786bcf4029b19e84729c699a8d742fef50040221ddf6785d7335", - "0xa383494d908727fc6198035c24afb3f352a16b29b0c1639062e7169618bcb38e", - "0x3da745966ebad677a703f5db94777fdc307f6e3e66a7c3c5ca24f35cff3f43bd", - "0x32fbcb24d42beb2128d95055706b767f7df7ce16c1613e3195342db84dc9955e", - "0x1723ae6147425f5b01b68de9847d79b918ca0f85a800d981029dcb5e3c62eb8d", - "0x997f18b9977469cbeeddaa1bb31472be3806c03ae77973c857e32d6fe2c4d740", - "0x6c27575f33b1d85fdf9643c9ddd27085f4241518cbd5b776e0d2bce19b152ef0", - "0xacb86fb3209fbf57c42eb86d2617eb631e0ba36da7de5b2c7ed63f168a7b112b", - "0xae02559f4868fbe4f114eb320ff0f3a38086f364a2ee537e6051cedfcee76d6a", - "0xc96e439aaa996d4ea4c276d1592fcc06e829d5f3cebf163aeb75f590896a2648", - "0x87debf5b6912717ec3c0846fcfb5b459a15254660cd5064180c0c514b4b15f59", - "0x37587d340df2b40b3f14746b72c5a72c5f51963d208b02c9671d6c623079b584", - "0xdff2805c029c4e3c249de3aa9f5cb3b48daae4f4496deefc91ebe3251c18629d", - "0xa84f66a457869dfc95d625d40496250ff33894be23a43e53ec892481f1eb4fa5", - "0xf8fb34bf78ad6d52be6fbb5472f13322b506f594ad3b585c04f56ed8d0d9afa1", - "0x546937b89a4d3b59817377b9c2ffe9579d4650cce71aa26bf2c76c571ec495b3", - "0x5ac921894e98005d03aa42e8fbde7ad0af0401e350c8ba98c01543a93b37dda9", - "0x3cc76dc057c73f0e0fdff28d484a092747ca42bc10989c599d0f597ead6024d9", - "0x1992cd7e94e9ecfaaebbd08d91519d6b67857db87e2e67c546371738ae0d2d0a", - "0x3604be694574c05c63d69cbfbbddbafe3cb425d75a13e69d61b50fd8d9c947a7", - "0xd1c82e40206d2a89e3a1a40c4a1b63c62ea6fa0847ddc2b25f63cb9bbb4a55b5", - "0x66ba114d5bf45d50e9ab9beaa879ce18d02a8b4f989c29ea7e9ae604593e860a", - "0x94f74c09ce5fd1c8fcb9cbbcc476af20fcfd9cf01c7ea65c14917da7b9560ffb", - "0xa4e154ebf83215c3843ecbff1dc8f646f221505c21d2a76f47d55466d895f1a9", - "0x28c20a0e95e23a023678443d7bf5b2421975b827dbd617239dcf26bf6db9b255", - "0x55d8ddf586d61e137482e3f4bfc1904ce4e04d21c6e7ac4d8c7d29b1483c8c0a", - "0xa9e1aed191a7a1a92bc99604e3b24c02356ea378b16de109362aefa2fa978451", - "0xd543ad635db78b2518681654f98a32d227fa6a1fa9b7043ad12cf58c91e8f729", - "0x102fb87da8b57948fcb763d7b797233f21523545f78388d8d05c6d7f2a4b388d", - "0x4e7273e2a92e897590988f38f8b899879aa1aea754fd5165364e8b98a66e0d62", - "0x5a06544527f88d9fbbc5905742863d873367d99e574d75496c59caa041e2b612", - "0x6a8957954db14594746daa61f907e4694e2a749ff53ea6b1dbe77d7d1f378d22", - "0x0debcd48486aa0d33a783caec0d6fb1256ffcca39071041fed7f047eecde8640", - "0x9d0a9b13dab1888bf0eedda217c501d76f587ee9a8765bb455cebcca0b705599", - "0x30693bf8c9bcb6bb4187606f98e138700e999d8824cada72d3d07f8c104fd263", - "0x494ef870dff64d1d65b4ae4b49ff13c145a6058876eb657751d58c06f62b5032", - "0xee4150e64f9ead8124d50d56fa4b6e6d185fe8ac385bd2a9db7ee991f6e34c02", - "0xb859d7db7abe48cbb9420d008d94e8d47753cc78962e5dabdded9438bed56b1b", - "0xf8b1fb734c345111704d73fe6944d0a274964a347d7dc5c7cb8677104829f5ce", - "0x5dc002623d9b3b872900b0b313ae8520009de730577b6eb2e2de18ae5cd4ac94", - "0x4d63e346ce7e654db6f067e8348c539ccf622d84020812724b936c19ff4f86af", - "0x2ed43f4e5d6889f5379e42d4a06de7c343080da74ff353960e4cf980f750fb90", - "0xfc61d0c90ecb073c022c87098045b6ca3f2b66c94f3723c679d81ac652b16f72", - "0x7e69914b56984b8c7cc9db1732c3d2d32969a58d6662509846e6faeb1305586c", - "0x0ec9b1b68efc96fc7d831ac7316e0ac0d908bac31be330d905f87b136556c241", - "0x32fdf6c25f6c741dfe468289cd7f019ec3a40c5d8fe882ef662b798491c0dc34", - "0x1ffbc6b29826aa58595d1a412fb90050f2bc3cf0ebc5462f308d4c8d85a1eb59", - "0xf0fc93cc2868cb9828ce16463e4cbd59637672d5815dff03aec7f3e1424eb204", - "0xe404321694894ab375f5a65c339f2cd2a53fb5a10f312082ce019681f6ed64e5", - "0x81ab1f93747c4804a85f56b0c6eb61491b958504ef7b898fb1362eda9b394e1e", - "0x1535d439d0043d1436de963e3eba47335bd7afcabeac4d3fc3f342396d3538be", - "0x42da1b95481bfab6cfec59884bb1ac7572636c8f489df2768fe2140cd2702766", - "0xbc3fd861f6e09efac1782d9d383c4ddb9b4268216fbdf3c25689162628e36cec", - "0xcc74c3d8b44a8cd23676babb4efb0e67871582cd5686fde9de6a052c5bf91ad2", - "0xbcd7fe80e95bbbb5ac93b1df5ff0b8e921e7ebc4960877acc1a95c478c65d64c", - "0x045e5918fa69606b9546f5fe83cd9212000828c54f9cab856f85d3a22536a751", - "0xc0f74dbb5a42bf1656698c37aeb904a33e969099f215a7efeeae0024c3a409d4", - "0x36d537998634f2dcab43b387edb63119c0f4cc68b1979a2ceb373089c531ecf9", - "0x87c20426fccdd225bd3b961c7adc1de3f9ac9640f6d26bd183f6e528089922fa", - "0x195ee1daad5c3d0052d6d633eda1c9f7160d488d4bc54f4ac3babdbb678eeec9", - "0xf9a5e6735f9c5577283e8cd717b56f69dc8306b23781755f2e513d15e6738cea", - "0x3e5bc3e78ae33367ce7450c5da7436f1faf3b1a62d238208df207307762688be", - "0x123e7b4f7e6b9338df2827a238a266376a20a1fd065e9f8a4446fcac4c6c92fa", - "0xf4eb14bfd197d49c42daa919322f18228d2d9ae2dac6dd7113c5e7d12d2ef866", - "0x78836678c20a40fbe09849fe190a9e74ee070f2056d7bef85d0f124676a93130", - "0x7986f7321971519933aa5dd507074c7b007328a7dadb8734eb4aecf732703fed", - "0x14485d24b7e90fc39d91ad6051b7f596d320b1f88f54fee132e032753e59a766", - "0xa80a9a5db758bfacf831a54022c85a838e30c8611ab4b17bda0641994302b59f", - "0xedf1814fb78abf675f3c5671c3618e5e51105647997d6dea6a0cfd1f0330bf6d", - "0xa91e9fcdc4f2b5e029abdc6b2523079bb4a2f5346d8a5a674e3d5582b8871d1e", - "0x3dbe468159a8c068285c92360cab488a4c1ec37487dd54cdc0b70e6d7cf074dd", - "0x69877439effb3388639ef6e1cfa132bc605bd8a0da053bcca23d82aa453d0040", - "0x6751d32ee3a1244532e6698ce57cdd0a59f99130e1e7e004c2751bc49d10b355", - "0xd1760a4f6e5a7967985125d2deae8bc783e47b1b85e19fc273e33eb2ef88d271", - "0xfa934c087923cc16f636615657477c48c082623d7cdd35508571655dc14efe57", - "0xb6b1e4387e04c4ddf88ada0aaa162345cd43e54482fddb4297e1eb6f8ba1ce74", - "0x3988089edd46b57c0bc83b1bfeb0050b503cbefdca83e96f1c12e7ea80688a79", - "0xedcb1bd8b522ba5155965cf18c25a090be2f8d7871ce60f0f371cd033d5a31d2", - "0x2f887b56b317e9896698ec9059d36aad63d54d95a01d389aa196ae12e562ca2e", - "0xe6d6b89e1d851fd24cf54f6b60c6c93a8b014ae30906c156374d73605aab3028", - "0x177928968fef8c6da017b177f6be85851616775be043b64f72925e6b8a4eeab2", - "0x71a3de323d9a600e15c25a5a6e05089d849defc3830fd69738f548fa4c57aff2", - "0x8e7d92f316f96b491e4831f52a799846810dd032bd720dc891195739192f3955", - "0x12a3433e8cadc005ac35da334d557c8cdad63576613f07df8c3ec9f528e846d9", - "0x1406da1f5efe9d3de6a829f1219f731f3ac875c7795f72b5a9ad25a57970b9d8", - "0x15a849a0ea56a777e00325b4af4d9996747486cc9893c08b0773210306193521", - "0x6178e69be88e7e93878b731d062b29a34bf2951082b947f35f60fba2c8de62b7", - "0xc4142d4fe01091e82626178b66c13243c9b35de0f07a49e3c5f2ddc15b39feb6", - "0x8abda6239557236af27a2cdfbb4fb91a6b136931c53067739578ed13e1b0a2ff", - "0xf85615d9337092e25080430cfa0b7a24c97effd422a1947a4c07239d5221418c", - "0xf9534d7db3b5fc1bcd7033ade59fd66bbb94a5bae91c4acebbb1540fc8bd3b67", - "0x5ed0f8035d3920d6e94b881cafac324ce5688f8c97668715733e0d00733b0fe7", - "0xeb6c474a0adfd84c79b86090c793697c0bb39d6cb007c725c2ae7afdc98df5a6", - "0xfbfce3e019b0b29ad03fd9146fa368f9965050b40733ce297bce6acefc4668fe", - "0xeda9ed65fbb1c7fcccc91de519f69933ae66c8ed59fb65f64751ca8aa06030c8", - "0x359ea9df33d466b5dc210ef0e99f3b4416ce03a5439f49b4cc4c1b98b22a21b8", - "0x14b2e8729b70abac62120541229182264b78c7ba1a1a379ac8a582aa0bb0d739", - "0xcec0dbdb55f92191974c2c8ed716578c5ba04c4584a0770fc0d7d5cfbdcb2717", - "0x07334b05a08cab079354cc1f7a945caa3c633de89a89c18244de81ed56da850b", - "0xc58e0bb71287fe92564d00b5094ce36b7899c346679011e52b73eae45bace19a", - "0x9dbef45ce9abb762bb9b30b61456a90b74b0b6f5b41af9500699542d933f9535", - "0x3cf848f770e15e7f682075c77f7e980da6750d3d4cb038479983e341eea3c354", - "0x608a4c5958ef3b0a324a7cdeebcc3abae89c1371c949d6b269b2d3936d9fbdb2", - "0x66422fbb9044305317d900702c2f99702317a8a83ccae0911a2832f623356c8f", - "0xe977f86ae4c5b350b350a3d6af7fffdc9baa96d9a7cb24834e5dc4797fe39fa8", - "0x6f56fc66544e099797cc3b0879dd20b21796ace01a0029e0d8464a3764d1e858", - "0xcaef7878c703facf29fde1467dcd08b03761872f598f42f5c56b4cb367b97255", - "0x9055cc0c11cbbe8477c7dd35b37a57e7994248c6bde9ac05e85717cebd2b970d", - "0xd7b08c0ca8abb9f07e3df1c4ebdcb03b0ac2018a905d1c78e6825d2bb5ea1ffc", - "0x0184cb109267e58d5bc0193a04548f0c2b87286ab6b03a1ff7b6d88a725662ef", - "0x3005e0af1ac0c5fd6d58328c06cc7f5d89c8c4ad173106fee1a7e37c9f2dcb95", - "0x8f7f8800d29c66b4fb12334b622fabe1cc4ef06e4ded44e4315efc381987cd56", - "0xdf74e3be6d22159e0e02ce3b8f0b405e6469557ecbd12e4432d52f4ae4637bfe", - "0x2fa9a889f958ddc41bae5916657f946273447add502464674658bcc257f1af15", - "0xbfbdf04ab62a35b2f7b038b02d8c37ee946cfee18e10ef8a4cd5409a5fe81d19", - "0xc4e834510182950161a75a843352b5b46e246a05b7c7e47240b6cdf7e18b4de7", - "0x2534be362fcc238c530f2ce8f64a3366d4003a21e6f32493a082b7efa1d413d3", - "0x628e5f76af96e64ff34c52cc5d07562e72c53e7bd4b7585cbc83b7c9951d0d2f", - "0x41687c81b22f67e4ab6ba0163da6d58c81d94c5db20569b4a42fb58b7321a442", - "0x50f55f58cd9768b611fce3ff13e8da9195b1eef5d0a618fd27f7052f88c8fd84", - "0x1b64cdcbfc12c42e9dbc7a62a1f8eeb0baaab8ccc867f7a7308c88e4968eb9a9", - "0x77e9fd9a5d64b66cb901c5795b9f66424638b24e457024b5e71ffbb79ed8a863", - "0x35644de61c2108bf9d49efef164414cd2594ad4cca6bc421699458c9bace5491", - "0x172ded87912492ee521f79c1ef22e42b1d22f17c3286575a5d419fc00d928199", - "0x3c8fdc337338b107ff5879b3e95cf285b5128ae395c4b89ccfd05a3d942887d3", - "0xf4b22643ebb6d46cf292531792543f9699a99674978045b0c911818211be6017", - "0x783fdc538e4505d4187a0f341b066007caff8030c3bcc4bf49ebd31a8f6b4794", - "0x38e11f176844f680e75b65d5225533639eb522f39495b9ee426135097e5e8fab", - "0xc5e0da94d4dd6e29c8bf3684177a62051e7555ee87007fb07581c885be598edd", - "0x317dc456dd095f9eef53781c214806beb31351cf78cb1854be257b4039324b8c", - "0x5bbf954741e453e3157dada0a69bafb9ebc63c3dbfc0cf6e3fc937a1b14b7356", - "0x56a5354de5acff2b904c5b6b976c473277ac2364571c54583bd682e76bb3f43d", - "0xc37f17385f4e6015cdc8083fc750499ec8c9063544102eb0e3e3b7e0b5046946", - "0xd616c205adf0ecf00c7563fda837e94a4f48be8560ddd15d93988cfd3242b40f", - "0x1ecefd5cb0c61b120c227274dd60b42e6d25229517b20dc3e37a7c3b436f0e92", - "0xcd447982c518db12b8aeba63b68d8caf1eda6bcc44400c9d83c4e4d64b4e949a", - "0x3763ef2d96a89a302260ea66e22e3255001ba2f003770c03905d4f39d8ef6501", - "0x2fcc41e9b574af5402cdce8dc66d79488cbd5cf960209918863ac9526bcb6a70", - "0x17b71848876a6e0a8857f1e5d04762734678ed1a8addb7e378915a7c3f37c981", - "0xcac9e4641f50d52d77e0e77b1a1b5fdd25cae239864367b3d99026d3fb973610", - "0x55eabf50cf101f65e51dd3a54321c1897a67c50512c534ee9398a716a790ffba", - "0x49101d5719e4de5e4e88645de3c22cb3ab794df815555d4f22d54ac6a59f73f5", - "0x509cd8530c28cc098b0aa80d3006a8f71ac7ad56101b880288658917173b8c8d", - "0xdc0e15e6d321519f34d40c6b2cf5f6955c15af815dbe02d84606ed76a01fdbb8", - "0x0b8e107e7abcf80e85b3288786dd79f949449225315c9125730d7d42f0ab9cb6", - "0x07ba997ea28711f221ba26d724a09a52b2737b8aa8532e890490ad811ffa792c", - "0x713775500194ec8691fb540d63e99a70cd443e5539b5f8e2a993266bb58266ef", - "0x3b2ccbf35ac833e845f00329c1f8d130a3f804c55aa83c35421adb83749213bc", - "0xee82c15eb18a075b00de8fd610621683dcad88a19c1c507351b5be0de0c6c4bb", - "0xf874298782be23045971092d8305c469a309a1a33f6cfde7604e6499d2384cdc", - "0x1a9d88cd641f6468fceb32eced3710569d511848f393c2114ae33d7f36c12f3d", - "0x5b2b8c2ba5d3aa8a0503d14e759154d1f2f46d819b363025a77d4cf5e3d83586", - "0x0082bad01acd43bc2c504f66ae28056ce352257b7ba7e2c27dd36d256c079561", - "0x9c6e4e01a831348ce64da4e4bf04cad5f58749573e54f1062b0e2921bacafe74", - "0x600e1b0101161721066952d71401f8fe6e689b66b26e2e74cc924f5e914e8eec", - "0x8720d215255e4d5e2a688096506d5b25c3a79c511d8c0b3dd7ad3ccf542e9abb", - "0x48baaec9724cadc4f7cb8f10549b8daf87b2572151cdf9308b3e96f02b048f23", - "0x2adf0f56fb9bcdbae394025ae949e694e01599887e50c355c90c3ee5ff32eac2", - "0xcf78410476d781bae1567f3d763af732d2ecf56e741cffd1bd3906af83de1f2f", - "0x4e7e223f6881065ee722d6ff9603f1786e4e99292e9caaa75b7b1fe9aef00109", - "0xfdf0b390b0395f007b1b342065096e0a8da957b26ed4cdcaba432a202ec12b65", - "0xcd40d2df140abc1228b2e1f45b5d65a0f3e2ab8b7e740dfb5376b036f63c1c2c", - "0xa491347f128d31f68cd1ae536d8f982fbfa5b58d855a95219f104db741d3d2ad", - "0xaa91fef9cecb842de4df61fd1650267420c8336758fa450f87ee867ff1520905", - "0x75d1574bf7b23319a7c8ff6a0a7cae649313aca8893ccd223f1f77fd71c9b8e1", - "0xc72491916b25756e3f505081b7f63e32f3289b86cbe0181ac9d33d29f666b9e4", - "0xd446465131b34f091673736f70fa0508ee7065c4011359c302a603b6159cb52d", - "0x1380689a50696e1cf0c19869b9773528f01cdf20b8f8a6c6a7165fda31ea49af", - "0x01cac6d9deb56473681c02dc753351feda402a1a1cc2b4cc8beeb23884f40760", - "0x3f622e134dc529c5cfbf58a3ce91d57850578f45b77c4683b2dfe4530ba0826c", - "0x016c95fc875baa0a5d1ae22c8772eaf574a6c918510875f1000d65f3a779dd04", - "0x86e5f579f42ea54a96e622f4a6becdc5ae85e0d0af87997fae87b6707abc8d28", - "0x53d403f0d0f1f30d919ad7212cc5e2e73cb4870c4fb4e6d260d2573e9bc5575e", - "0x961cad0d17fbec30a8f614ff3565d12698af096e61836cbf1f0ea125ae3ed72d", - "0xc9dcfe7844bb4ea845125bc3674f326c0f178c5cba4349b9461e40bd6ec68c3d", - "0x894243ff80e90c4c4676583b4e428f13e077008d225790a234ae215dc53d33a4", - "0xe5fddd80d3cdbafb53e0cf3c095d33904ac8db83bdeef9816111d20384aed444", - "0x5c7ce294d82fe6502045664f7d13d02063ef24f0f4960e4fb62bb6abf08c63eb", - "0xb33a5699ac121a51c0074b4783545a86a428fb239b4307f1e45108c85af88617", - "0x37168f3f0220f50ba3cad0a558cd8b01a7a435b6c3c5cde98b420ba3b54a1cbb", - "0xd50928e60d00c23adfc916e7f9a5363fc8c94c8edb3aef41ac1dc719041f92be", - "0xa08b27f437ade4d527d883194f79927053ac55a3293487a65060aeaf4c4e5147", - "0x326e2d1f45438741b63d346f0da55066dfe0284382f1b4ee54b1d5552c4f7d83", - "0xbd96baee5835d9d6007b0b5957e452d71d1ee31aa6fde99796cce59b17dab703", - "0x1cbe90df49f8929ce1052049bde7d6169efe0b289ad4e05414a8e7bd61788900", - "0xa0b914df37895be6f5341f3f4013ce5c61f108203dbac4ff205d5f1a581712cc", - "0x1582e61974c0bb5f9f2622d70e772f3e9ce145be97eaf5a87ad794268cf352ab", - "0xe946dda774c96c7878a0daa686e4a22e0d9d36a88dd9c93b1776432adbfd68a1", - "0xf88a07db8c0fd2a1354c38617c16b4d2e4f8bb43d9049321ce47a8c03c8430d9", - "0x2adb489b34c480267927daf3ba3ca7567d542edb83fa82e8040ec57e25e3e6d7", - "0xf1c28c6daa3e97466d50d9780bd3335f3dd096c3389b906bcc12426ae4862a99", - "0x4457a8686a3839b7d3b592a1751c25b216bc64e9d0b8a81eec55601ab8d8a98d", - "0x0ea5d5606c5e81f92d31b49b2e54ce6c6314b13ac223c83094280d0196e003b1", - "0x33a91da49deed50914d998615077c5192be56e482ab230bdb3d480d283502e2b", - "0x5385496abf2e351c0cbd7f6fcb5bf5b5345783b4e512bdfa23b9736e77ea43f7", - "0x98f808b18e5778a1bbdefa3f19991a3d008c27e9976db0ce77ddf9e4e21a0feb", - "0x5381dab139fbb0ad6b61afa35c541aba559e2ac25c4ab1cf8a756c2f27c6bba8", - "0xf4bea5f3ab0bc9ba3ad00ad79402d1d62da125d311884f225ad8abe9fc36d56c", - "0x6018a9f7edb5ee7ada70c3e85f22ac88924d06031cff3f61104ef52bf0baa2f1", - "0x01cc4300f1cd2bb4948329d42e17a277c7da52696d46a60442b6f5600d869faf", - "0x330240c95bec1ac1a476cd202aa74e85db562750f860a9fbd76e813f16cbb639", - "0x0809c487d45161c3b85cda014603ba7efd6b67a8c7aaf314cf20c880ec623a7f", - "0x653f53065726f9a7c1b96ce7d836acca515f563a47c9d7d47aa8c5c030a9fe6f", - "0x37b8dd7b2a844e519f9eadab305efa5d112266cbbc76bcd5afc119e0dd337ae5", - "0x385b7031eafc345ed353d9b35dc53010496db172cd906f8e0b7b891b84a65e4b", - "0x38259038fbf1ab3f0ffac98e6b312457f6b2631f68ae477b9302fe740cfb8e50", - "0x5a963aaeadd708291b41bd419cf2ce0585e162e0d46635902b58ee85e317adc3", - "0x881004f4fabe3e7642c8cb5e99dc3909da28451ec442a299f331790c8065e049", - "0x3c721f828959782052312d30d8256c9351a141923a9c2e2ca5b605f3a92cb27a", - "0x3429b149f860e963ed3819ddf19164977d637489eae313331c71165c7eb8f824", - "0x44e291345b79ff4116797899da20baf28fb9d4d2a5d6b1661a0c83b8952e481c", - "0x00078c007b6c0b3b6603b0838b03ee9e8944fe16f99f0a35eda286a288594806", - "0xbcd3506338253f0df5abf0c2866cdd319fe75bad7ac6f18d1f8201164e0b9986", - "0xc047f5f06cc54e4bb54c8e3bf22245d68c18b4787a01d324deb2139d7405814a", - "0x77ec49a04762af36eb63e2bb5c5cbbf15b580020117639ed5546749e34285195", - "0xfce516ae95eaee4067e95a5752494974ebdd182fa47deab70fadc5afb5ea8648", - "0x2ee54548e56d2cecc3e6cff4e60a7664e55d9b1c1c9a61be74bfff7635078cb3", - "0xa79dce8f10567e060638cb2f09ead0b191e59d972c532a7e91b33d27151cc23e", - "0x436b4cf10f5cdbde72be02ca16602190a56c77f1686533e643c885351d4657e1", - "0x9070c78384138f5e17cc9e6148c7cb7591eb9864f042b82ad38ee263383496c6", - "0x12a8d5285c871a0b05e370a337834458ae2159b5d8cb4bc93b6cc83bf7351b68", - "0x3c07d35ea209492f4aa811c51202f081a3bfd6e31705ffec497d70bd59b7a6f2", - "0xc0a99b851e0fc4655cb3cb43547f4ae6c36c350aa71626b61ed3ad3492f04600", - "0x064404380c5969d8e43d759ef990658d7cf5bcb7b4f8512fe58a7e994e199707", - "0x3d3a61c95ffdd3c7f05b3574370bf4cf0eec605ca27cde051b5d77e062315f36", - "0x9259e0113f1e009fad7454eebb238e0c7f4b7aee8118b63b6c05aaa2f0bc39df", - "0x76ff9818e62b25fbd698ea021e200ac9314090b801e000ecddb71bf4829aec8a", - "0xdf0031d6e1c55717102ca1b3a0bd389cc0d227f0f804396d4b84b5dd26abb1ea", - "0xd0220e77632c2353d51b92b1638e909c1f41a7ab0e6801b65e344ab594f48881", - "0xb15fccf30f298101d5ca0034cb8585cf14ddd76d58f3a8ca71a60aff0d0438f8", - "0x5dcb3817ae05b8c8490e197cfdd3f0b701e61d83b8f6423b3f24f27ba0f4c668", - "0xf3bbdc6651a4ba011443f6c6542b3f45b8aa2ba8bc719e7220578d6362cc441d", - "0x1edc95bab3c88a66246393058ad1a9557b371e726adaf261a5faa535cb8fab76", - "0xe02975b5ecb9fb8abecab35298704ecc476d2e6205c2df900312359d5aebdf9d", - "0x2a5c9b033738fe9e7ba6949df526b5a735753464b746e7d1faa29491a8e5f57b", - "0x46ac373df276af8af25aa1407659a8e85ae12b51ea6ae0150ee2b80a76ea6d9a", - "0xe85513a38b54114f4b6cda2bc81a4dcedbec22749cab6676c061d95c7f38258d", - "0xacfba9967af17aa94f0bf73c3b1cfff113e14625742a7398a25a01aa29bf02e3", - "0xd589df920ebfb3c3c1660a16f74213dd6d487ee577bdaf18f6279eefb9252c57", - "0xb2b66c26bd139976ffc2471f8ec71e353108828bad5094a324d1e4762f5547e5", - "0xeac126eb94bb1ba443373fb2556753d95804891d4763a2cdd1d297f1eba0fe6b", - "0xfcb52c727793003a70941fb01bf6c184890a691b70c0aac3b11feb3987de8628", - "0x1c1b6aeaea826ed0cc776a322454663ef555adb0d3c6f50480957ac4ab7f0672", - "0x187affcea64fb6195ee27cdca1096142898be0fb8f216f10913a744f2005f7ab", - "0xb4e42e3a4c94477a6d5d7f3de429edee7d92cff93b647c71d7a20c1e07a35117", - "0xf960751197118967fcea65f10b51d9568c184a8bc968c609d093a0f6c15a71b5", - "0x0bb9ca3521abf8fe971d371749e5c2258b1fd4c681ded047d6dc7820d303fa9b", - "0x52354e8e99ecb5c2dcc1214c08901d715ba62a7463d56c76a6bd1287a74f5c38", - "0x8768dc0cb22a0ea9fde00170783fe3741501d958c186e2c636d03fdf8a995129", - "0xb4204eddcc9c75372de503645bddaffd3f71e8554c3bca09ec700d928a9664aa", - "0x97c94c63bd30754d51abf48aa830bac3724cf77e366965e9f61a100cd0136714", - "0x60dff72f08506ac9ddabe3f957f88a1ef4935f187b6c536643500d65512fd393", - "0xa94f4c353581e474a091655c78c491c38315e93e287f848c17843d807aae3527", - "0x09d90469c97087fd45cf1a2c5471c6e81e0ec1e7850dd76b0d0cfa9fd49aa13b", - "0x298d03c60b8dfca9ecac182b5c0f6818a4c3d84e55314c083f8913a8746fe335", - "0xbfa7ba8daa97bc681bc6ce413494b85ccfbc10e2bc96e148713e0325e21b3b60", - "0xe61dde9cf0c1c6fb0a37993df24dec2f221f97bdd34f607de13fb1f947e7f284", - "0x91e9a9e65d5076819146b246647b9698954bcf55e7f059db32854f93b325d35a", - "0x7bb2fb25b881772f318a10b6ce8896712c93cc85ce9c7c371337020e86817a3f", - "0xbfdda2258ccd28dfc81f83ad7cba81967120257392279c7412f0bb116605a21e", - "0x0a626f29328872380ebf6a10468307abda5a02c3a7b9e04763c0c8c83f903df7", - "0xaa8ee86e9446a125356a96f8ed47c3821da54b003558f68c823c4ac1ab966c81", - "0xe26f3cbd95e0a26683c26adfb71b6d4fc82b034171f29836cb9bf391cf172376", - "0x7cb80eb383da7d8c1120fbe79cf3bff3e5bf19b9f57aebe11f79ee23dd82e611", - "0xc9b85149830b7a2dcc1f613f40ae232bf5cce7770780bfa7c77ecd25ae0bca6e", - "0x859dc28f93d2f3b17e176aa8248d6720be498fc85017491c68d6af5f4798b437", - "0xdcc4dc79cf88601caf6e038bea275a98208d221ac8d3efdd5db3d2181c14c947", - "0xe1bd63a920d45b05efe5d2fb99437a26bb7049ea5b183e99866d6dae947ff724", - "0x4aa94bfe1501f4d37b0c55d77add7bee9bc7f24d444a3ed1c6cf27d67026bb62", - "0x10931498b823a9784c4aa3ece5e88b477050927fe4016e4b10de7e2e0eadbdb9", - "0x7f30cca2119b5a4107cddca59a88f10ad95cfaa879d97bd1e50ae6a347e71b1d", - "0xcd8a0dd7a4fa9b97641d5288375105273c9025a2c9f3d7d9d04179fe3f55143b", - "0x6c8ec850db640544b7e5b22c19253dd1595899a7663d40c4fe13de4258cd60ca", - "0x06ec895b6dd7f299edab3e2e3a65322fb7f8fa8c9a6182b7af4f0fa25acdc45f", - "0x45f6d74f05f7b366770428b49630e836c2fbcc72133ef03a1bcf9953209e693f", - "0x3d868a3921aede543f2bd8ccbe3b51468e7a87f8278e4601c4a8e740e8c339cb", - "0xc808c8fa7b02023e5c64999a141edf6e0ce0239e37656e0fa7c5156443f555c0", - "0x82c273cc2afd955663515932357f19d657eaaed2824551814706ae3843fa1739", - "0x5785f16929a6a73b4621e75d31958e2e9b00dcbf3a6b38bf3ed59672918b68b7", - "0xc8f84b888e55070682103999cfc315ea3d157d0771f0f035cf2ba03f6fe1b1f1", - "0x343a05e1a5e75e09528f194394caf6ca8caf94904d54d89c9d22abd5cae0d83e", - "0xddca7f95c2d9cfdc9efd9158d29045ba92a71d45488c40d28b257425c1c56bfa", - "0x008a2b681c4691d7e0de476180c969aa511467f27755d0d498eb22c9b5333835", - "0xe33fb40ffd6253b5701d24c59a4e217197f9eb7caf7c2223722734f4c74fca4b", - "0x650e8f1c6c4dbc76ace7d6c29b9a659d520af9b291b389bfe3812c1ccbd07c58", - "0xf88c36b8042df77734f99395eb68aa9f4b67a21891c42350e4d5e3e6d8fdf168", - "0x882c68aaa8d1f6acf005ce2ad3a6fef7ec1212ce610dfb8a7bea9417d87431fb", - "0xda73d75513895d5bd5174814da25cddac9a633507960e78ae6884b92e48ee699", - "0xf07d98594c0ecb16cfed18adb9f7b5b1055630b43444c70e6357cd18ebc09392", - "0xd70c5886426c55dda997d615d325b5cb5b652f5673601a045a113d94a17715c1", - "0x3e8dbda1f53c55fa4de65efc7d294a02d78c69a98d59049dcfcdfff6b7eaa521", - "0x67b14249f9c987298fb00e0bf560a976dcaa7fc0d02e816f84c3bf7a4e7c6901", - "0x4f8affa83492afa72e95a36f23356b46e58a0a19ecfff6960d4d6e5b9877b1d9", - "0x09cd2a17655988d2d8cd5fbe44965a6c15c3f123b75e1229621b9ab74d030e53", - "0x30693dcabd19e89bdefff4753cbc64d00c65a4af98f782e6e67b4e84f6b015a9", - "0xf67db822aaef8bdf7967c0dbe25015ff6cf88ce21e8bdd25feb764828ee64951", - "0x9a587510721ee914cf8b9c863859629ca0c8ed22b0aa023e9efb0756d3f9ee5a", - "0x5d00c0a840b96a19679736a7ba555f3e9bc5263b4b8437d6c3779191eda0a7c4", - "0x3e01cf22757510938a5aeb2ec6cf5046b7d3c186bafad7d57b81c7d2a99415db", - "0x0ff2d3ed3c5acfa5db9f9820a1a314865e266a823ab42f40bef6b0276af0a108", - "0xd3207e322207667c614e50be784f6b4d9c3f48362dbb65b17c62f5f33e631ce8", - "0xc7d5e81b23b741a772af6263a7c5894d13033001fd96898f749050f6b856c94d", - "0x7c40099f6b0bc7f0d54650a56531b2cdefa990d3356c79a9c6bd78033a0aa1b4", - "0xd35ffc6c8de3c7f5d939c8476758c99201113336a6fea177c312ff1cb4f6d7cf", - "0xd6c9ea18bb47feb446998cf2e1374aff33e1282167318c5e5104657a69291797", - "0x66251f4b977561f1fa9906b2a26d31a8b7a6aa62f4b7b901220c44acb93bee11", - "0x1ce1b8ea8492cff1eb95135084d3334bb45394eef0dd3d87aa8b68fa9e950683", - "0xc3b226cc60b5af90984f3a98a55c1d608afef326e0bc504869723d545f633196", - "0x63f93d6ffd8e9c5779721a53d38b754faf582cccb8f5dba6f952ee079a08d8ef", - "0xc4dff799018abe40f08586ecf23fe06afca1791cb7968218c93eb8d70a8956d7", - "0x3231b329c24ca1b8e3610d8a2b5fcd0eb962f04e93bafc554c24d047cf130ba2", - "0xc7b393970a7126afead537405174b2146bd3b1a1e4eeff0508fc8d79b9233a4b", - "0x73ec83b51241245c9ba70d1c5026a226ea2b690d05a01e392225821f6cbc76a3", - "0x8a69ed7b352026316a74f566d9eef099341bef9afe05eab1f85d1bbcbc3499be", - "0xb7e26c2cc9d43bf91577fd258cb3dc7a4ff1819e69aa425d48a31c909742c78b", - "0x7578d8319252e136dac5b721e377463565f545a9786db38cdf048d27a6c7c8cd", - "0xa9136889bf85b078301ce670c54928ef29fcd6a268ded07a08becb440915cdf4", - "0xa5a4b8468a5aa50a2afdd44e2515faf1fe0e7ddd0456372da0db232e00d47d9f", - "0xf433f4018378147994bdee5603e4990f728a1838213c81e655b8b9c224f54517", - "0x1b387891f9ae757f62f9ce1e2f8e8a9a5c8dec29a5d045e41c924b5d3dbbc5dd", - "0x47b8b6748b07b33eb0237d2726325415dcecb2d038be9872834cb5278a5eadda", - "0x131cf8b68d0c779df85e51dbbec3f768903a3ab289a7d39b72602ee3ea7104de", - "0x9b6ab213619ae96805a754947644c7465274c7f686b5ae16e54f90ac0c78526a", - "0x2e34a90151060fc2ebc58f830a87cf33b705b4e3fedb2c5880c0f900656a9436", - "0x092cbefaaa923be1541c221aee81cff83650d8c5fc2503c7cf9f8ea2e7fac0f7", - "0x0b7b1e280b8ea2e59a8f322aa05f7c9b7e717cc21e05abb35f62432a480f1277", - "0xc82f74348b03897beb7c622f63976b700a2e031d0c380b3577a5ac69ce3eab25", - "0x232b12662086b16480d650b7ebd1fb0e5de4e7d6dfe1cca34be044456159f0b4", - "0x2a51e7ff6379e6ec9c3bdb4f7c867f7ba272315d5577f542738db1a4e986b219", - "0x09319ad108934876330c0e72e626eefa5a3d768487a90dfebac5de9df578d5c3", - "0x4dd72eab12d9183ac4c3692cafd33b929cbcfc6c8abf56a9fdda21fa0ae90bba", - "0x014c07ab6f5628be304140dbc98f639ee69914bc47eec73741c3ece25fbdb186", - "0x77c1f0d2effbfca82dc1c3af80124666830f84f1ae9a61399a7df9a7ee9ead8e", - "0xde29aa3faba8e8d7cccd3d64152d787e0957e2e02b85f8b598e27a64f85be92b", - "0x7c86c19f0f598c5972729b14a4a2d89490be2a5aeb9f09fbc47ae11d6e9e5589", - "0x58f4fe437e049d73c3a384856d7c4249d0f9084ae8a1d33e268567f7cfc1d843", - "0x679054ddfda36d5694034de0d9159204f67fba8dcd64ea27ae481751a6a042d9", - "0x00550f8a90888c1266ed3d43558087bbd255de842bb5c7ce5a0ffd7501e217ac", - "0x99f3cfd2d9dcab86723c6c5dde5342e3b0570c26f7f4d497054971c9e0c79714", - "0xb23c9a8cdf75d08c55d42dc9b5a4937c7a2ae389269921da4b0f3824f2fa4ac9", - "0xedb69805d643bc999dc8f42c82fda5c86dbcdeb97f8ca614ecc442b0096ce0a4", - "0x9732275603d65d92f1a2731e4e7f822d504175aacb333b3f011ab12c25c16e24", - "0x74895f04ab02583a72695faaea930e965fe5cf127e78048ee7b6fd931252ff60", - "0xccade78490ce81bda38fc7246986bcaa159ff4210c78a818908e7929b5d5e214", - "0x0f83b40f4169d073918acf1388b052b238748d6c5aeda235800403194da65756", - "0x2e0fc727f8fa2cc3e815eb0cdfb8fc73a8bba70c281c78a77bbe7247a1757b9a", - "0xca6fbe2344aab3a18e8ba3b18dcae9624c995175d77f6d9783cb73a6779d4953", - "0xf93234aadf9c1c1da370bc2e58dec3e40840ffc4456b7ddf7b9147ddfa5c87aa", - "0x7217d998d17977983d7973155777b694997ba1fd9b84c3f723917628a14a8bd3", - "0x81e99babe840021f79c09988425bf91745116970fff4889eac90f4d948617b45", - "0x6028f39dda3291724550ed9d5186328de53b442deaf1e37a01bd1e37746c2d7d", - "0x202131d5985bb64c2cd745f8c57ae870358a66d915fcf64322afc3e907f57a77", - "0xb242517abc3fce2b275d4a88a1689f836c9556ad589be1bf62e6978c042eaf93", - "0xa4e70eaa95e779b934269dd10085060923d9191eaae34a75fb537832140eb1ad", - "0x9f21679b001c396ecfe339f57c798c706c1567547f19f0390154a3419448c8e0", - "0x85ed1f333690e69275ce513e2d633192b0aaccc8e0b912196a154dd0ade30f72", - "0xdae9e867f112bff48300cabda2626cd9de0dcb249aa3ee2a7365f90541bc08ec", - "0x2acb02e30384ad2364b1f83784b5a785820e07ed7a7b51afdb9249d671ee6ddb", - "0xdf7679b1e10f8bc3191e6dbfeb777ec3d27f9bc1b6a46c146cba9a923881d9e9", - "0x744f20162facbc66a246476d4a8ecd50922b5d2b16823b5df599945aeb8014da", - "0x44b7f3359f92e5a2b6c42a443065a3f819d9c381e69bf236aa71b2f9c49631dd", - "0xb1a63564b8f7f9b17aac6b5f5639d9323e6df5080aad9ad0381d3cf2c8985337", - "0xf35537961a22dfd460b78626fdc79cb696e7c2b670e27368836e8c7d698b1ff3", - "0x206a5433880de275cdba7a9bdb5f4ce8ec5eee0115b793bde8071512e593731d", - "0x200ee9535bb4b7e8fd3e7733de7b0aa376e8c837c2955f1fce3310b147d33473", - "0x005bea89fd624ad0b9dad43bca25748eac78d33e786374903665081d7f7b7391", - "0x41f5ddfc45630fdbd7358ab65d38f14b71faec3a1ecab9ae2fd9fc685f527ade", - "0x7ebee909bd7ccd1ec9aefdc1b6abb98f326daf66f5b1bccc05c740f03c362268", - "0x8bccda1bb4f9d06b1f4ac17fb13504ba94edc3c9c833aa6400b3f003f430576d", - "0xc9f4d7be6782ca8377e3290ede54d3d99364173c00cc4f44b3af20d4f297d4ce", - "0x397db0e2df773dab48ad8b207bdecba5677cf38c460cfe3bbada407e392fc0c0", - "0xa3c1612387b2b51fe87bdc4d57e54fde0785fc2c7095f4343bb71b4041cce47f", - "0x43e9ea7beeda42d33658042f72b5e7cb33478df7b0cbe34bb424239e8ed7c695", - "0xeb523d35f5cbf920364d97df1129aa1aec48b0e49ec2121ff9965c34a9b1659c", - "0xd99698427684f0db6c0de0e18fc14136711e38cb65cbbde722d55e05105db03e", - "0x079e80fa24f5059276e3512debb09c453df0c957e06e42f645227622e83ecf7a", - "0xe8cf948ccc07327ce3fa321c5371deb0237d51f147993256246b619b75f9379a", - "0xbf3418e0a245da4ada0849d2fe9c934e95609e344cf4734a20d23a86466138d5", - "0x9c3620d280ea8e6d04f460756fd7e5d7947b15ab87b2a24e31d96384aedef5cc", - "0x0467892273772abede0e7efd081058c5242dc0f474c90405f987c19056d4cae7", - "0x432592be748af66d3b43264e00d46119f65ddff1cebe8d3853f072f21145eef1", - "0xb98a1dbee5daf6f25dbdf731efb0c233470eb4b44126ad8dad31b89794130bb4", - "0x68c7b6c356f3fdf82cff0e4b693bcd2239d72b77f08c587e9f611aa38c6cb67f", - "0x2b3b7b113b17f69188e71507bf5cd66685b976b5ec23ed8c9f70cfbef5b3a641", - "0x360b08f1be4a53a1db2ec1e068da659c3b693d956dd37c5c3ad04c935081f732", - "0x4f220d5102592db62c3ca6bec42a304ba2108fff69d39fb740ede163a7667df2", - "0xb12b9aa1524ed587b6857d0f847a22d2e0b726a253e3dcd5375411263144fa60", - "0x77d7006a8eccc9426be6d0d4e99d2f75db18902d4dc8bbc2a2f651b7737adee6", - "0x711cfeb0c16df71b7140ec86c221f08a4aac7295ef990834cb0c4c6586fcb952", - "0xd0ea4ee8acc3746d4949b9aea555ee8f7dad81cac48c6188526cc8a3fdca0674", - "0x17a460691acd145b00658639b51c9467325b02b0b0552b68111e9bc70f713963", - "0x3ee66ecf3c8622ccc48bff5f77ec7b6ba9bb9ae7162ab99869f4476d786750f0", - "0xf846ff968d826c20e10c1248121d4af6fb6e2cd86d0f8c1d2a6088cdb23c2ded", - "0xedd84b8243b3fe742052a421d9f3e990ba31e3ceab24f2bb5ec2d8f9d7594378", - "0x92cd6c59cd631f02a52434feba99dcc550f2567156a4669a5d2d98077aff7e05", - "0xf3557a61071097dfac3cfb0bdd8871fc759500d6aefb555292128ad785fc7d3b", - "0x3985a6dacd96834a54d581b38690efd4dbd50f6354e238a551aace59610c300f", - "0x194575b6ab2aed3d99269301408b8b9bdc765870fa312c4ecb6771b29d667398", - "0x0c6aca1dc3670f27093e5e3d8397d58cef4c77009c22be3524bd6c9a691ecad8", - "0x1b97718888aa3fbbf8c2d3159f204ad0f646b27bb18973452b9ca4ecd056fceb", - "0x22f223e74b95ac311f3747e410397420297ad63d5e6e7e6259345546af349bed", - "0x0fc0e95d219c422d2eb0bf38f28ed200f4f725a904c5bf85db7b31f609d56b43", - "0x74544c1e115514858a62461584e02fcde29f89ca783b3f73a55cb2d0bfe199a2", - "0x6f75971fd8a9d7d4518d2c3edd580643e148afd67e7c135cab6890a53cea390e", - "0x32ea02d2647e1e02a3a08fb4369cce33d5d7b857ba66ed9ac69411a42c8c384d", - "0xa2c0271cd9c3538ad012ae5c631bf8aae0f36d1c4be4c99e8b0c7dfc3777dbef", - "0xc7d4ae7347249377f473bfee60525031ab18e7818bf7c047dbf78fc6bf178536", - "0x0682d4bc9423be1aa3f07fc7d307d63243383a265e59d8fb963151046579f1d8", - "0x67dd80cee212ae46069b1af2ad98ebcd5a12c5ca23effb34ef48145b867f9a51", - "0xbdacbccb5511ffccba4ad93461ead16697944dd61820f19c88468f142ef81b36", - "0x078ab49dbb49bee1b70eed80685179c484e65f7999fc7f79fe3a711ffd931318", - "0x22a8907100fd6f6b0e3e47410aab0947091465188c3895852f849716033be524", - "0x6a784e15b8b309bfde5743c6b4b5006c86aee4cb56a7cba8c25a7909efc56720", - "0x48f1dcba97a055f366175a40966c01ee499936ebf5602117d547ea99384f3b48", - "0x8e1fb0be5bbfe825b57c5aaef6866794634e42a28e25df77a38348656b57020f", - "0xb3845ce8c6a1e1779f085f409821af631276cb413739c2412a37eda3d260200a", - "0x2230e2aac91929625cc9b7ecac5b84680d2978405db1565a4522d7a895ee7290", - "0x8c23ad236c0fb61af08b665b655313466eaf04433335130945586f8993041b58", - "0xdc9a50633e3b12d420663e6ea9c79c5785fd333ea0feb53567980afc1de70a05", - "0x6c1929f3669d42477844f993387ae6cbc37b7831bba146028ed81e5490dc758f", - "0x9059daec481d4ce8e2dc66c815172933e3e012a8593882a249f78056ad4b50f2", - "0xf9dfb03a341aaf57dff8ccb571735fa07bee708190e98ec0be1065fd66b89373", - "0x15a02a41a22ef5b01754bc9795e04974bcca9a3f2ae168de0c6136cf0aa2548f", - "0x494d825348d46c5bcaee447991111b0aa9e93657c90e268da1d9c901d4e4840a", - "0x3b817750bc438728bcc0b7f8252b33432cfc5453d50907ae45c62c338d2ebcd8", - "0xd529b68e9cfc3482d5be812a8ed91cbb55135bb25cfa9d036183deafc8e460a9", - "0xd6b77501125e0a57f0c0e91905debb2d024d6df612d66cc09174586b1e02bafa", - "0x4e0a88a3a74f58bab4f534c2545689b37548b4616ffd393f2267db8c691f88d9", - "0x17c75e3edf1a030852b274159fa85a77092714e370aa2f0b0127937dcd6660e6", - "0xb6fa7da4857701b71ecfb278cac489abbffffdabc6aa671fbade2d2efac41859", - "0xe8ef5fd31b9673c39399db90bce23c9ec89123aaaa1ce6015ff793d8306afe1f", - "0x3ffbc8309fe01232021798dde65fb6a4635bddaed77d08213203b1c65dbabac0", - "0x2966ce3428110d587f2d9e9809316852813e6bdb69f0a66515c8f36edc054eb9", - "0xcaeb39151adde827f09cdd4ac1d75f73a4b3dfa4c8608937c13f53db96aa7142", - "0x354b88ecead2b00b2bb215168460c2cbafec22a9ccfb84cc969ab497f9f69816", - "0xecff61575ffba36e15d742d45698a669febdf5f47d1677fab4a65cc3427cf01b", - "0x95f96355773843e7104efcfc1dffd6f298fb0a65b2c24791b9e392054f880ea3", - "0xb44ef0452bae93cf91664024e27170818bdcd7b6b5a181ca6959c919f8fe7cb8", - "0xce2a82cb562e0fa81f41ea271274d1f02eed4ea11ae750cc410fe6742be33cbf", - "0xd6a26e0f490eef7443b8dc8221745a02d4003f6a218997c6b796925b999b6ec5", - "0x8a387b6684eee474d241c1c6163a249155495da62aa52ca29aef549acb998112", - "0x72cea28da88a6e131ef42f2a9f757864534cc34c4136217f233e18f1eb3db4fd", - "0x70206a53faa4f230c5f86c891ca4337007ed930e3a7bec26209be8a13bfbc2c8", - "0x2f6bb3d5ab4be435fd07aaee9c2420080309526a70f52341194b7297a0456c0f", - "0x92b3cb4373bf691b83ab02ca22770130bd5f8bb830fa349e55692cb3b96d5107", - "0xceb646228b176e71f96dade90e7d21f2c7db94461b18434c7fb61502a814424e", - "0x1dc9aed27813fd95a2a2793073ce8572b37e18f0431e95172e6f1fc49f9cc69a", - "0x024b753479521357a211ceb85b78f4c3f635bb5c5b77e15bd2092b6b7fa6b0a4", - "0xae547ac87a1ac7be258bf1cccf3bb1e7dbb1b6db37c78e04422353421fdc83fc", - "0x38ddf517c3975783bdeedf862084e8f90ba3ad2df9470321b8b881cd7530738e", - "0xbdf262fd61c1c3bf6d5b3643b48082f57485dfd933a85cf60f6df85a38ea7701", - "0x828e793a6ee29d08f08263b8556468b5d0a7925e8865813628dc28928d5bf731", - "0xdc2f4d695b8e21df1faccaa26d3d8e1495ef7b52300903737d7ba49ad6c5fcf3", - "0xf0cd111e3ce41ce9fcffb27cdd58ce456574635e997751fb6fa1cfee26fddfc1", - "0xc9b303881045a0c4910407bbd58a9af222dba35ef790e6d3b188f3cbc04d0349", - "0xfb2fad6c0000e72f51a853a98cef9037339817c6d84e8cf4b067c1ad1f4221fa", - "0xe1414f1f8a7edfe0427ab063c0ce631ba06d5a3d9cc8dcde071f6302cb45b1e1", - "0x6ad5eeae009879cf0e0a3381041f25828266055220b8214928fedd2effb2801b", - "0x2ef44f7c5bcfc0856d215f380e8792c2bfe5c10736ef0bd0aa0ba7cacc4ed7f2", - "0xb5d7a0fd9101534bd504246c5ac161befdbddac49381327234a1162a7adf2391", - "0xafea64b36067c325fd4d884c79a9728d342cc2f756054739cdeef4ecd6dc1896", - "0xf2937ffe604ae91ec475b70a044ef165d2f61c0953daae90e63992cb5493a1bb", - "0x69a5ea452550bc85b6fd733561946b0ea8be6a1167ef6806d993c4553e02f027", - "0x3f99bfd16beaac4ab3a910dbf79c746ef702b745e0ba46e76332e5f64df83fb3", - "0x871d8703b4a1d6d946b23af9254bca992b31dbd52a54c89dc2dd3d4d9c5c61c5", - "0x908fd554e2e5e426772e774fc832d4b4117377d8fd1ff3f55453405b9d257128", - "0xac6fb9261c0b245f4dc6e7a7ce626c1c69fc57e0c29a223637a99da146e04ab0", - "0x9a03df9fb7a01223780b7cd27e2285102ebef4b9c087f6f32cb1f63016991764", - "0x5330a917aa2c137659a5ecedcd9c0f1621349cbd1ae11562f8ca4cbcfbcf5792", - "0xbe0c26211359510e5ab47e8fec850535e89d03301f5e083487aa421450b3e70c", - "0xaddfee931251ec4df67c6c5c19bf27c57e8a7a60fd6569f62681e32a50a85862", - "0xa1206c42a578a4088ec677eb6454a24aa4ebd427939216c4550fb7d12d51be7a", - "0x6c81c0799d4dd6983cf1065222dcb8c6d3bfd3ce236e3c57417a68e86defa0a8", - "0xc356b1cf9d36bb4e2f8bf64b03869ec7a1bd2c82d407d4df4c0a582cff5ba6e5", - "0xf900240cd95f9685deeaffa1b2e92e6f2211093ba83a4a6ec7c0819d04b53c17", - "0xcf10e728ff9ba17bd1031e4d4976127e6d2b383ee06581c57f22272ebf34e4ce", - "0x7aaa9304c2cfc79d2ed28aa855cf2b6f64f162e47935cfaacb83ed8f4e78fc93", - "0x45b6eae599001b455b5cdc82e83aa8d9ae0900e444a11e212e1a6ea39418e38a", - "0x57259a8fd16a85b0106bf94e360b33406305df5f93b3503d6ba5a17802ab9d31", - "0x59041feb40e4e0561512491b14d250fb6752092a024b70c91af3f1cab358fb9f", - "0xfd6047a708a0b7f7ac20d2bf38ca8873182ec6b55e4615d69073b1e839874364", - "0x6f0c025c2df8deae6c20f94e618d0c29caf5618503d3355fed14b8b26058757b", - "0xf9f5bd47da201f438fb3291b50952a9af0a4c5db743a8fb4015197cfc87793ba", - "0x4921ba6242c5532a45d6cfd87abc9a2f34f3ff103dd0437a26fe01e0854c3400", - "0xc407023695a845165b809bd12ed30227efa885faaa3a0c37633b5cf0ab83f519", - "0x52d0c065f1982e204d03964b362d89ec231d03c740591dff6355faf2860ebb14", - "0x6e2b510edb6032b08823aa67d82e7a7495fbed8b7a1c3afa5c3f901925ca785a", - "0xf56c13b9f11ef46043dccf99b1c40c412bf3fed6ab1c1b02106237891894d747", - "0x838cd04b046dc3ee6102bdd535468a10721d8be7d8dc072c6ac0c08c99124280", - "0x535a1d6a7354fc3d51e07ff67bbf0715ce73600fb6a4d5a966d2879ece256fee", - "0x07ece2732d471cf54f6388cdb24814f68e18b43536d4c0668366952c9cbe9e09", - "0xfca111ce286eed8ceb0aff30c8167fbbe464d528cc46c82a302d751b07367389", - "0x80c131302b043a3c9f03f153c262ab5fc99c1ed690d425ab842db059e820461e", - "0xcdf5f53634cb958038740b5c31811fbe2dff04917926750473e23396faf8651e", - "0x0b251bf674c097a1f6084b7d4f8f9e5fb208cc97c504e4af1c22df24b2bf8aba", - "0x49af2a38ab378c2d611d6142d22721341ffd4c7e4ea77208a6154f5badae0e8a", - "0x497c31900ea5a46025a159d2d005a6a0065f2a954f936f4163ea559a3194fcb6", - "0x6e11a069d89e3f9d8c606fed7666e60ca25e7abd3eb06021dc4cb98528cf5f08", - "0xb0a21c8206796f2f589fc76c220957da326b0ca87c26ee3bd0ec818602be6827", - "0x1c52c95455cc49a789ad9c91ff0fbfc080d01c44508c4f7183c5b52cd8d2a67c", - "0x226e0fdd735620794316a6171ee930629f51b952310b460bb9ad1af8db4c2ea9", - "0xf279db0c181c473379f076b3e9c1b934609983ffff2e4af4bf6bbd5e8b702db9", - "0x346f7dd1b2befc4f2e2394c8f260ed0b4f32e4af43c164c4ea09e96b7477ebdd", - "0x1e84e296acfcace1226ae4bcc983ede21c69d0783403e9c7c584ae72d6718d52", - "0x28afa2327f10d052012d7079d7071b56cefde51f440f82ffca9a54c25e49ca1f", - "0x07115beba550a7120910046697865c321ae7affe0d5eb24d4ee14c5427cfca0c", - "0x01d5cefc62ba9bcd1b7d07e48ac1d2cf9760a2ac4fc3d69f93c7ed347c0c3b13", - "0x954bba3b9e47d949d0879961e4e3c7f199d4952fbd8ac6189e2ef34362f6a42f", - "0x35ef992a7bf7a2b2b0acdb49cab4b9321506cda95130750f28067f04033052a0", - "0x7daf0ada3ad44ac8d78c311b74c664ba36af509b9440e276cc1cd115217dfb58", - "0x8138a1e442554341f20ae16a0f791bf562bb11bdea235ff089faea5b33ddaaf3", - "0x65ea5c69209e6896dba728c2409098b543f807243c9376f51eac4856c64f0487", - "0xfe67020e8b5d36b616c2e8a40517109b6fd06410f7168318a15c7096159910af", - "0xc7aebd89e64d6cb4a3a0688dd84ebf3f448a2e1814402b8391194d15208a17d5", - "0x81e0a798aa0a6ca4ad54a830edfe5e2e78492e5346c58c7763891818ad47b20a", - "0xd6f211f6248bad1e5d52013da24d79226a8e914b899fad73fc39ad9e42c103b9", - "0x341ad9c5497c3f930331137a5d081cdb134fcc055f85a6d50e7d03e44a593a44", - "0x9e2c09b2009f0ad546bcf12deb943c0936307e2d0c5c7d40c096a438a8320862", - "0x652e2126bf65168127ab0f92c480ff8705243bde90206096d1cd2dcd3d1e46b3", - "0x20be65ada96b76f93c0cb556151cee0785d7338b094f6b6f2c8a7f22f9799d49", - "0xfba2ed75cc2d93496e10419ad9d9ce0b33383f03465584340d5a3d1a6a90a577", - "0x16609893c9789ab20196bb30a19ab2bcedf47142e663a713535907ca6daf3e86", - "0xf0a300a152e1f3dd993dec6ff0911b8d92c67958bb0c56c4e499aff48bba3e92", - "0xb77b19d3b07bc7efc626f9ceb73d2f4d324902edab4aae4413250748fcdd96ba", - "0xe85e86b1f9f60661147aeb7df3bc40e1bf200ff1ce57f61c22c916a3203524d7", - "0x6bb9119c972939e3e1fae48d42527ba5dffd8833cd670113a979ef039b623499", - "0x0afc5ce9ac66d41de52b568dd1108727bbc4717a2f04ffeeb74ffb63bc737c82", - "0xe08075d030de513603957add9a27b8e76d3df7d2ef65f42fde161b0de1ee937f", - "0x10c0279bce16c255e8a106e0d3a0dc56981791b6214993f67316649525c85544", - "0x4996458c81f145a6a718cdecef773946b97e3d0c81907d3478f5516637c0fdb6", - "0x845c9b3662816e8c8deb911c6062a31c5296b4ec9e5e507fdd250f1b0ab45a52", - "0x5b5c22c142bebee8d99660e9485a54d8aa42d0c0f6cd5d870d006b0be3a24230", - "0x65a87e4303ad18afbe8528f7a6510ea6a7d2d9caa350a31b501db02867e3cbf4", - "0xb98ce6fc35c269d7cac7769db519d0219d44cce7bcf7075ef9c81878da0b0828", - "0x4c85a8a986de3b492aea89536dfd5312757ff01ba5dfbe51018d97b11429dc6f", - "0xe1822a4a922b0ac1dd66c06069f80f5c3c3a12f6ebd92f07fcdd424b2603c12e", - "0x0de2099a5c47c3442fcf4f294e517d3318e181e23972bfeeecaa3f0066d09206", - "0x0d18960691f8b4834f008832a028e84341daaa69e24ca35fcd3d4b83c10429db", - "0xa84ffca22d21c426548458aa96f0ac16372d5a2e91c1bd15173dab4045214b07", - "0x6fbb13b65eb2b1cdab20c23c627bee73f2f8ee6b1dcd4feafbc54a421c341bac", - "0x9fb42defaa9926a71ca5ada26aab3a0cd9f23b8c36bf542ad69ee7e7a2c139bb", - "0x8558a1f0a23e656c8762af69fe385ba2a4cce72119399945d60dfe7f2b1c79c2", - "0xa9d3f4f851a03f9fe4b1a1c85f42a07b7d7756660ced3bf43242f7750f03e374", - "0xa4b1e0d91f09765bb5bec63f90f3902b06b29efd9272a3248464473e9a07ab95", - "0xe881daae7e35e21a2add885735e54e6e847bcc8fd429a20be45ac749a15ae81f", - "0xc25c69e8c0a61e8103f0f952adc5c49d24368253be6e4a9e3277b9e02a2c0fc9", - "0xa39fc2e497a7efd9e6b0ad2775518e9d01c584cd134900e8a796056e8280330a", - "0xcef1c3a1ebace0a6e7ab1e402dc20ea3917c38f88f2d305cd6939e380a40f603", - "0xbf3bc7cbc84203db96a973047b2f4db2ad204b4c48abf5076fc8f2a0ba049c68", - "0xf4550102b8bc55969f17c47165605996713799aae54b4953ea539fc38a5c4055", - "0x852d591591504b86580e1193a4d2d6f30f1404d64cf5a53dc200b768151ee0db", - "0x26495cbbd2983bdb5a41d5a1b5b186dfcfbddc53e3955f2947269a50634e966e", - "0xd623f346b2f6aa11a73012577728a1b71ca74e3c9458da8e0ce9affb0a927b0b", - "0x6d2a19b145e354d56928ab1960565ab48c0fc487610f1043ad078623d606c343", - "0x5c612211df07f1a91909d1fe8e6cea9a2196e7c95c27445ae66649995e491fca", - "0xe7bea84b4850a2b7bd170c61a699f857c9685f3125c4902e9a21d2292ff926f0", - "0xf7c44f95c701df6a93f15ce8de008ea3c7e087d9faf437613166f226ac433254", - "0xdd7c4c343931c6a3c7bd49e6698e791d6dede3234075a5eb5674df621545d6b7", - "0xba668334a45a2c5393b4ae3feda289818ea261634e6dd277f7a9c284d4b6514c", - "0x019717a5c3fe560bcb9a7df209a156efe550075313c38ed8331ea4117b14987a", - "0xdbdb821a7354c2d64067300221a47c4936307f1a918ac4fdc716eec9b5421217", - "0x94500b9c93da559d73a918faf0cdc9f9cb3a90b0dc219b63bc4ef84634325aa8", - "0x50b4ba5b95eee9bfbbd94c71696395c3cfbac865b11a294349f1f0b57f70ddb0", - "0x61c4e5aa02c5b13bd7c7f8ea3c2fad8814568e822200062f88df9e87b321aadb", - "0xf98a11584eac3ce8032ee5617616da3a8bd897ef5b0c780eb36b4f3005636ff3", - "0x13b2479c0d0d452ade1ccd27f7581a257f4e21fbe7adf16b4041e4b4001af070", - "0x6c58a1f54b7160b42b06fd42da605d8576b82e586bc53fffa424a6ad51d5de5c", - "0x6be95b15b26ad33ea46f8a3d2b6f6770513ca0b315c686c86cc4e87f83257ee5", - "0xe9c8510f7a288699a5558f0b7a0808e568fe0093695bde1e2c7eece351d979ef", - "0x9822e17cbb24d2eef601ccbc3c4e9383b5189a92257159918bea131a41ae9a8d", - "0xfa0a7f7e8d554c6e75aa1d562406feb962394b514fbe96deb27d035219b70efa", - "0xbb4b62a1ebcc8f41e42ca70d570708bbc9a57bf08d5fc4287bc94729d2d391e8", - "0xbb949bed075fa35a9452fc76c09e600d2aa02444db336625420b61302c5b1d2b", - "0x9f1cf8520fdfb59122c3d8ab6e8ee9851b9e852e3ebb1ab940d7d22d1f0fd60b", - "0x11ef3c742cf06dfca2ddec7911e5ceca3362036177368bd15e9d4a7cd11648f6", - "0x9eee5f42a36978479d55632d8d86e57de7cb73c9b1961511cfe9b3bb70cb8d3b", - "0x7ee17dba9f383b15682beb87ab797dbb0ff002d21c898704ef92edaf7049f9da", - "0x5751e43effe6b2a2ab9414d732ba34acb7a7599862fcac4c8335b1e53e98fdf0", - "0x7f476aa4ed7c48da093dc85084df751a00dbdc6e89eeead918a0513879f7a47f", - "0x792ad74f008a7c2dc0b50acea37d0d9805324d7c80efd1754b0e07bc47008390", - "0x48653017bbc645a99e5264c4d5f781655abe4c417a9a6dc1a3e56684dcd01844", - "0xed182e2bc223cf015d21f540c280e9e49469e08d840fa00989dadf68ba643df4", - "0x7a56560bdff2aac53f0cf9c5979ccede352e1ec0f3200cd1cdea5d704e61eb04", - "0x37cdcc0b9f310f7f681b4d044a03d97cc09f7512399dd99b8a43a6fa80769414", - "0x75b7711099ba266a168f5534685ad829c011ca79c79eae892f859fbd9d5a76b1", - "0x1e7739d74174ffe0dc6284f046f73618eb1f854dea28aff87b18f09ad720b7e4", - "0xd695ff00975f61bcc48456903c764afd0ada99460f1ecc65171be258cb164115", - "0xe38746dc9d2544171763b0480939c1c8377102b4125f7d27d05f48d0e434a712", - "0x8ac00f72334ae5b017926bef6f94b5551f17a2373f0f7ac9d225651683fa5457", - "0x7dea798accd9318d2d92e1abdbabfaa902ec40f6c63d3ffaa78e81a5eeca2f06", - "0x5ff02bf62f47617330f64d1c845d5732737cbc397fd54d4167e727533dc81f65", - "0x0c7688c23adee0d14383cabf150933ba9cb448473feef696b4fc740840629b0e", - "0x2c2a277b1513d5446429068213180b0de19599b35fb6448da84dddc8c7676bcd", - "0xefb57782f278582d9d11c6b1972f2ce5799ba3383bffe507922052f09e6056e6", - "0xe308b75bc8f39250d4e02f090076c6f6e200f30904e3147e7cc57be2d8d81a5e", - "0x86b99886fbd1b58295709017cbe9c2143e932f8577a24f31528ffc025a7e44c2", - "0x643118e6e01e1311ec5b12696b59cf2ef6ac531f028b6e23f4a3025235ade454", - "0xe0deaf6d9c1a4da0c5e60fb586c550e30b04ef33b1aff61f9bb7491949e2ad15", - "0xe3ba800c9905ea8556dbb2298bec7009f633772f55f3ef5810ecfc1a391bd3db", - "0x06406d3f51ab5ba9096b76702130f3f27aa5890597996ec8a48954d16f4e29e9", - "0xc1fcb87d9c0c29b70aceefe3ed5b3608d05dde30b6d995dbd7bb7154146e8853", - "0xf65e31030188f5ccffe39cdcb4b847f4547924971aab5780fb446b1f08ddcdb6", - "0x662513354a8b56413cdfaa0b56fa031a0ab1c58b16077162174c4c8b814cb4c1", - "0xc1ca59f2eec9ab3839f85e742ee26e9597febd648df1a4ceb1134211bcc0ff9d", - "0xa454920090cd3907d585781adb9af550e4d927ab83ed88856dfba1bcb4dbdafc", - "0x5472afc931e445cb261e4d13cfc84e04d1c2c2a8c00120c73e055c5e8c0950cc", - "0x9bdd7291f4e3b0b34c20e183c8129eb4e9cdd3430cfeb3fd0047a2fad815c797", - "0x99959d3aa3de615b1d68e236786358a48b0b1c52ba3017a8f9545e97402ab090", - "0xc2a6d1dea0a6091a9f5441e1edb2c99e5a4a4bd07238662da8ffe3845d526b4a", - "0xe1d7948ea5adb5d7dabcef081801606b1c74230e9b7745862b2072434700050f", - "0xba15f27b7f2a19dffddd7f0d72285ebab40edf0fb9035336be6912d72ab5cf4b", - "0x61f06104ee5488bec408aca22be90b37d7b2f76abace5d8e1d9daf8488c10dca", - "0xa601ad3010bb154479bb7f04b3118da08ce7aa66afbb65d5700380566f7dd04d", - "0x87eb8b49206435fb7a6983d6d7ff92fe0cbb173c7be1c2542d6402760f98dee3", - "0xe1c8d190243bc91eb6012c79103d2f91e3c652fe09936b0d194a024a8ec01f1e", - "0x0752aaebb4b1c381e4d9747d5045cde49d99011784c0919329c81ce7d99bbc98", - "0xe74f74673b076b9761a10e98c8ddd456391a2216e9e658cba548ff094192ee92", - "0x840203d6cc8e414dca34b4736501bae836a1febc9f84589b673bc55c2ee05ad6", - "0xf61b094ff4320abb822307cb6501a76ca6ceddd81661531751782feeebf7cde4", - "0x4dda3a80f7d02dcfeb4630ad9680c37f7ea01f0ab9e5d506417f0fe4a1c82e9c", - "0x142f83abc309cc5bdaff2a385bd18dd96340b4d14a472c3ff64b280835b30587", - "0x6bee7fe1f7cf8faa01d094bec19cc20004d11d1f9408473ffde658d087999f67", - "0xd811c2011327ef0c434ba199fb016d491cbd7dbb8ad647967fb6427d7f72cc9c", - "0x4c9a5ec5e2e002c73f8e04c7eb8fa2e4c54188b71b824872234872b44f820568", - "0xb1520f14f6e9acee16c52de5e36d52ec19a0f90a90e8bb0e7d54f3b52c0a9398", - "0x52f8ebb0ab694f94d2f0baab9740b8410f581373bc4ce4bf6e7fd3e3f64685d4", - "0x1a9d0c5df31ecef11e6a1bb402e36fd51ac62c296ec51d1cb4612581112fe940", - "0xb1533ddcb0dadc809b166867c9c06c4983ad794a38014bdcd4eeedd3473dda61", - "0xf4a202576a94c983de3aa2ac0bf6cf403b8f22fdc39c184c702ca9a6503995c1", - "0x8ee1285025696b8908790fdb8fb1e06afbccc29841ff3ce9fe3bf7cd307bff09", - "0xe396c7b228e54e3002b042372a95debef7e9d346bc660f4c00ee5964978042e2", - "0x721acb1e7c918b232ecc98bff0297db492c57bb356b6a8da2673286f241c2b74", - "0xa4fb8ab31c31986031d223b3dbcd0a903c37173113c90da029998ff0f8031db4", - "0x225d7478fc19068a839b44ea477db46b6e044af11278567db75686c007ff96b3", - "0xab3aaa9e46b07d86a4771de32a6f4516543f0e7c8e25aaa78508c9e736b85abf", - "0xe16b1c4f4d0fd76588abec3c00c444c658d74cae60ea1a74c56cdc727e1b7001", - "0x27b8247c8d21156cfcc17d7616e218a41ea1597dfaee951dfae6ecb612140d10", - "0xb7930ab63a5bf124d615b21070f24ff0cd6ce807b9eacbf4f3d681f7725a4b7c", - "0x4811fd297a2c2731e35396886868ca3b3162f144ff4adbeae8804554d2a28de2", - "0xb1afd5fec04effdca7ff5801cb197eebb13d65652c641bfdc4719ceed05f70f7", - "0x7472cecf0bc3728c0f0ee7141ff0513395d012d3c3e1453fe01e17f44e491d6c", - "0x6af7dd9ef565e01df7771db29033dbe061046079886f798fa789707004f61af9", - "0xb0023e5e5b28da4a83187d053fc1d1cd90406f5f058c19ffe3f120cf040aa529", - "0x1f0cf67a9250d1ee3a9a05e3587c8c87961111b9bf10b94dc13e896c701cffc8", - "0x77f16085517da13e00b449abbbd217731fa12beb4364b8a7d650e44930cc16c1", - "0xac630ef3130001a6a6805ba5a00ae763f1f5169e32d50f8e2461e9b53ee1f37a", - "0xc9472d0ef3a22e37a5b5b333173ab18b5d01550cb239d47354b85f85a3dc8b39", - "0x10995cc8a94ed2a5ce0aba717ab0c3598a46634627ac38aeb9142f1fc04f6333", - "0x88f6533bb9efca7f80b6680221f2bf21ea7e8a21d798c8a461e0e8cff55ddddc", - "0xf342f6a8aa3b4deedb9cb461e601fb6d7f57378495f5eb3e489dbf54db3b0f59", - "0x7835d8c68f16abaab0abda4b6e3088a03c9426a838594cf99295bc270e6f648d", - "0x4ef4750a1d701da04680c6b04bd580aa189330bb83241aeed55fb55dd2cee52e", - "0x9f7cd9e7de4c6deb36156b37a73ea8c6dd068c3efa733be7aed94a1a801a7095", - "0xfe0af324ef5a44e7967bbd6de6b916e3291cbd67127356294bebd18b164388e6", - "0xffef694495a0c487f654af6891b9a5f585264ff3f3df7a8ea6438aec56185031", - "0x0c186c1b9ac25ee05118b7fc0627966136ed97e42dd5b433b4a6e732a29eba9a", - "0x8a13dc929b97d7861647c70e2d95546ec10c248ea2a3971e282157d1b1803ef9", - "0xcb332dfa5e1f0761612276bb430c84ba60a76199ebf6856c7450b3007c369d5e", - "0xaae70cacc2b426b77ca0dee01d7988975d73af89f4688d01f2141a06b6261032", - "0x6618c49567dc4b1ee6f06de4ea46f384c7f917e5353cd32910045ba2da2a2c43", - "0xf59ce02369f22c1dbc1480c2f99d7737974742754e49838adbdd95872377fb9c", - "0xa647b3029eb79001e782a6689aaffa70d0b3ad84bcd5acf6e7a0d1d1c279f854", - "0x56b4d39909a387fd5e4ced15060784b8236427cf6192295d98be51f988c47d56", - "0xa23cec0fa0bb943d9b0fe829aa0a21632328d8d67efcf507d570f170ac18200c", - "0x74496da6db136824f209eaa839036e3a7e94363b1e23ba39d1f3a74f43f065e6", - "0xfa2897c07d422fb4d81fa4b22c393ebca0cf86d03848fcef41e4f14e61d06699", - "0xee3020113cc21d2da50590ab8d700d9584e958af1c6289e9291670fbbb9086da", - "0x03121d6a6d3d46737f8010bfb658ba934bab32ae835b248b6f11cf44985b30a6", - "0x35a7206abca809ca24c3de74b82ab231747424aac530c31d1f93e9266d1c79ab", - "0x2089128abc1988f1e2ba2f294cabc7d2aae3a2be249b86fdd7cbfad5c05a3b26", - "0x31579e077a21db02601ea141fe21623899ffe195a3b947d2f55818140f5b9d59", - "0xaf1befab09595e55f83dd8a6c6722e62700ea5791211e27f5002db88bbcd806d", - "0x29242bf7c89946f11899e3425e7fa79d08a1a1b8bdedffd8dd02e823ce787890", - "0x2282493a8131f5ad82b0f5bdeb6b0f392e0f8636e50fd1ba90b09cf6ac442f02", - "0x39f63ff122022bb62c5d8c1421af47ef94991bf3a8a97e5f702de400ec5e1f28", - "0xd9f43250fc143e36f92a12a993e44674fceb81e6ac65be08361fcfe91401b3ec", - "0x88ae3a33d93e8627d827fa95aef374f9d9df8a7e821d3542c77b9bdf9e765cd8", - "0x239a22800e8b887eb6f84a1a7e09b7543e958ffaac59f226ed0b91af2afc2b09", - "0x8b5253760367d4d8c235f54c1cf85883b40c96d42a21e69e7055065dbc266b2a", - "0x48929c8d87f726ba8717a8d93e9eb0085832e900e658b5db11a0ec099915d835", - "0x735f49fd642ead8ced382ac68819104688c4610773d8301efa4f9ff1af9d6374", - "0x1473095adbdae18abd70432823099c217d15d6a394553cd6a4a20daa1867f5cd", - "0xb92e5530fee287bdd20a0985a22a0712ea61f1ec39e37029036aba475726e3e4", - "0xb16db48d134c0a4dec30f81c9f521b9fcbbce57923473d9a0a2d8ccfcd143b83", - "0xfb0e576a64b8f2cbb043c6d55cfd622735d9c2e8e1c73c7c85712a3555fd9300", - "0x0a555d959cc4c9e681ac1def25251f856b82746e568182a1802c662f8b7cf327", - "0xc65727c61aed82e4fc87810915807c5904fbd163722d783b6f9983823f425d7b", - "0x80c5689148bdac882e9b85f402f8e2ae0563f92f0e1214d706ae0f5029508145", - "0x3c81dd3a1e7094877f7ffed704a52a2e177f728e19858d21c0b46296054433d0", - "0x6e765d51cf90b7e021567584e0cdae766c20d4ea61f40d102c5b237bc831f0f3", - "0x184c2d3bc2bd2e96955ae40d7499886e46e2da0b9fdc3c55465f3023879738b7", - "0x74162a72176acab4dbc8ac904ba1ae98db539b90d6010a3f3cdd8e86f99b03a5", - "0xcb3ac6e8d74afc0c2a2908a1f248434026c99b89e1494f17f05717096dc44682", - "0xaa657accea572d043d5f4da5ddf01006748d22362d5512be8f59b849b86737b8", - "0x6165ad890727c2f2bfb07cb4f8bb5702c3b802e7b3608bd0985bef56bb6d8934", - "0xb1bf86dc2a2023f8d6664bbf47cc8f80df8bddc7c71217402d02b485fd3fd7ee", - "0x755f5584be60718fd8e846ecde5ddbf6d160602441f6ef0d688043f6b444decd", - "0xb4561e1c950702bd4248d9dbea09982512640f1fd2380be6d9274c30ad2680b2", - "0x460fe85ab763867e2d544c249da96b476dc97b0585f35efb9c5f75586690740f", - "0x3c3b9419d1d71df4469895601daf1848b25c7525ad0cf9cf84641e5dcc4013c2", - "0x31158f7120694fdb6c8ca57be4a4d55d1fd0dfacacdb1d114f5bc81cf3ad5b84", - "0x87a0735ae331643d6ee5d75ee57b8acd3c096af9fd2bdff96fe21fb2e23e4c03", - "0x86ee7cb0e0c1ce7d68c40bdf308765a91daf9098b6a2d0524e84dfa044319eed", - "0x76b78aac48f86b1698d304c27844542603f59edea7e5d4b5077bb540c4084946", - "0x3dfaa796235f91f05e80c5eeae3759d9044fa47164460afc8eb57b022cdab0f6", - "0x8c4858b78361011f7d35d81e6e5fdaff40abe6f0db70e2e7cb8b9926012b59ec", - "0xe1ca3ad88639975a3094f2d5f2bf812b91732c7e681aadfa9838d48acd2aedc5", - "0x8722628324d16f4d9d98a1c05126ed28a796927a7d92b12433f450f9dde982df", - "0x969ee350352d2b966a70f3ba7209d9eccd11900225f05a894c682db9e91eac37", - "0xa652d00076d380708fdcf9a8af53307f7e8ce8be0895a620305d2ab694a41081", - "0x9126ef241ef900f6eae95ea7fd48b9759fab56486dda0e847705bdea850a156f", - "0x545ca4cae7541ecab49f31fa795974ac2aea0a80faf71feae0a7c778490215a4", - "0x28507604ea28e064be5da5f6fc5fc35d9b19866cc8b349bd97791fd36319b982", - "0x4d81d0d091b500eb8e207c7ae8cec63c32f766b55a4193b5ba0aa505d8828ec2", - "0x472cdd8da3753653dc90e408bc2931296d0c96872770400754f79f63cd42dbc2", - "0x6bffc19062f0d63fcc842db4fb492553335d0340a329bf4b6877e1135fc87c8f", - "0x3bdd494804e2af4270a11defb4f90591a61722d9ab9f8e9ffa36eb2b38f31edf", - "0xd51e3a94dd3a847b40b81550428dc73ffc00ec66aac2751accf904355054046d", - "0x1d52de027023c8a53e186179c90653ad6e72816e3b5f2549776844157538d567", - "0x683a282516a4c1ed3f8964d616570138c545e00d1ef58e3eabaf2b5cb4017670", - "0xa066a6910e96e6ffda5553ec6611ea52a6dc244b4575fec2d0d294240c169173", - "0xce30711f7ed1859d2f9408577ad316b810e48d78f1e9500993eb89b7cef6ec4d", - "0x998c0d9a44997597bba2c9c9f0cf04d0b752b7c3981dbd31a37b433d559aaea4", - "0xe4775536fc7c8a2e15793443c9f81eb32800d339a4e54d12b621b5c3eac69c9b", - "0x30d47f77828169c8831dad3a5af4f59db161c8c639f9647c03af7f88fdf6a0d2", - "0x0d9cb6ae0f5b05ae03eae9f71fa10d8cc4a11d1b33ab4229941062a20979d2fc", - "0xec4b532649c0d685c05d0de3a652f3a395412bf4a67c813c573f2c261a5e143b", - "0x9b105824aa129d95a45c9ae21e83e89bc73f1cec89b4cacbdf81c6754e44978d", - "0xd4f7656af32242c24483894450a6c9a4dc39eebb7702228279a918c831bb4fcd", - "0xdf7d66dc72b157a57b7db88bd90cd61e7c581624847585088ab0d70fefb5be07", - "0xd94cb3d9320157336ebc825a669ebada765ae8d985ed3cb94b2f9344f49b5bd2", - "0xc92f6a9417507c04501e469189bf38dc7de37d2acb9b4b9ab444c9f7eddce74c", - "0x1d69f2b12610020ba4cfbdae081aa2dfd740319dd9afbe523d8885f9ea535f32", - "0xc931317a01775e3549639948b79d2643ba4974f46ba5da05d3a65a5276a62e75", - "0x7a7392150380ae3152ead78a897780eec8f20ebc3fb58c552b49b6fee307cf7e", - "0xd9c6a9c4e19d8d5020894b78dac2b019f53b0c5a906242a538fd105a539771e8", - "0x0d59d2947332b0bc87470704c4dbba5613df8c9233ee7b4810bd0c11e521c784", - "0x4e1e3bd3b80672e7f6ea8a9dad8faffcac275466cc3b09a409928a6c2f6c4054", - "0xf2ce305c9c72f5d02df8c072f1b41ac49a9f991b660a3a0ebb3884ca6e38ccfa", - "0xe53859f4e1ceb58901c9395e71001768d096a355d2fae64b4429889ae60d88b2", - "0x84ee091c71eb9406957f377d9690f2b6b74703da3798cfb801fcc404a22dac74", - "0x386ef8f6c5d07fe658167dfb1aa408b0f51b865e93faeba22571f981a87b4c6e", - "0x5e5f828788586e30ff1b6b62475b5b4510f1730d61a1774cf209be25940b85c9", - "0xb6b0a202ab4224f4050d6685ad2ae7eff550eadb59c34e65e551b428b2b762b1", - "0x1bc0293120588411e41448eed6436d52389fe915de5d99c133caea02abc1d6b9", - "0x8fb61c67f5dfa0bcded55375847f5e94a875a0d0d7f2522de0d0fb064ffcc31d", - "0x6247750212b8158b45ebea4d4e8361b0cd66fe40ce07ab208b46bcf89797be74", - "0x536f1586f67abd4833e3796ced30c1ecd742f9b705ab736321f96e8bd9314341", - "0x379a7f7c3897e4c8545d268d4f786fc92799a50652bb8c3cd60416493c9043bb", - "0x0593835a986174225dcb5d12b9f006e551935714d56dd639928153cd752e37cb", - "0x25b4a18c4e41dd881635e78261120840c9141bf0d98f23cbccdaf3704a9a8682", - "0xc55f91278be72dd3a066ba1b9efb1d39d011a723d5b123cf5ada81637c6279f8", - "0xa17e5f021b8511bb74054fc8cde1583762d36dc706612dae90d326712077af5d", - "0x5723f1ebe3c09c4751b843d091ec3fb2bcc6e5247eb6c9dce7fb0cac1d571a2c", - "0x5ad78014b5d8d35d114c0037a03e3adc7d11435d94319d078d4f8f844eb9689d", - "0x6e85d730f8c4c54280eef864b3c3033ba64c59331eb55ddf3486b0b54f327bae", - "0xa70f92be372e114b08f5e611a5de003a3f8c2f08a131bb54f45310783cf0877d", - "0xc6704e4b5f2344c8dcabc9885ad3eccb4db940478d33c8ab9fa44286588f2476", - "0x1c23d2841154a346fbd28abe3e2276dc83ebc84da8073ec82fdfa414372609cd", - "0x2ed8f84b7eb32b7f290c6a2363ef8d9cbd7c624440a8bc00b1adbe3f378f5619", - "0xf2c91d3ed1c6aa7f736ab4b936081fcaef6654a37aa28fe8b7e1b7795fcf3752", - "0x4275145e36f5873e14fdbaab462d3442d40a132b3975a2432cfb9a08ee083d9c", - "0xde5a4fc0bc197894296f63a7cee228c049e6aeb1a28f892dfca342fb35346bbb", - "0xf92ebf124db0be2296859cf79bbd14263db4bf5206b7dc36b63b786de7b64ab9", - "0x16eacba74f74c808a9902d789337826eaf445e9a131d8ccabe3bde22321c54c5", - "0xcafeef8dd957b566ee6f9673c81107fe9ccb499dee211345593194ad3de2b3e2", - "0x85ff213e7a9dd6c021ee307e6374c76dc08237ea0c08e3f2acdf94fa46a6c09c", - "0x4e4de2fb8adc658065aa6e54d888ada05ebaec39714bd466e1e35742c14b89db", - "0xb443b982f0945e0963f1cd43d85af46864f420bc47c75c59ebd515227a1a8f1d", - "0x5f2493aab4585f81bfef5a113985dca819c5e34693152c191ceb35e747e2141e", - "0x6d30f0784e1edb21a23f634ef2a2da79734ce86e84db6a201c2ea13262ff9074", - "0x266f276556b8b3f90c00212933c9ed2b0336d8abbc2cd8b544cf132aa516e814", - "0x88100121c595cde6a22f901bf8dae321fe8b12ba7a3b67b4fbaf3ca2f8b045b3", - "0x26350d866afab7c488f36b2b97cd252c01a9ca7daabd3932d7e6ae1ec87977df", - "0x7392586c92ef2eada65ef219f34c0fe65bf9042d46923dbcaac02cc3ec7704bb", - "0xe5bfd7a884d8a984993f6f35e4ec626883b473df444de5cb15ad9fbafaf872d8", - "0x6207ef43c9b95c289353d40728d56d323f99d7e815d4e8a8f20d621e4b47a0c5", - "0x4a757a8d31f2ab1176c4f6ce7d9f39fa882c0bac80af99cccffb5acc0b416c39", - "0x1aaa9fb56c4a89d19db5b71ca66f74d1dae1d3aa516f3d8971f3fe5796d4293d", - "0x83297fb846b8e3e76cd8a5de225b0d763b5ea7e713868d80306566fec3990852", - "0x018a26b285d6baf120e00091ce27f1695c58952b23020cc798b227a44bded5d7", - "0x1bdd73cb3f78d8d4f008345290ae5b73007ccc000574929b60007572b9105ce5", - "0xf3f8d1db0c6282e8359bfc61683416eb683c4a69b930b7c653ade0606a91b8e7", - "0xba4788f36ab573953242cfcd98b17c19eec15c25759007d8c56a3790f14f4d7c", - "0x351dca68df8b8386e6efdd4c9b52c00c59bce95ba4f74a851927f256d93c202f", - "0x91aaabedcb0e0b2a838d62f05d9856e69f6df8b417770b8ad8bebe608e327448", - "0x5cc4d0e86aa63c5afdb0c5b1af99354404c99139f59242ddaaab78b8795e4aaa", - "0x763277f6e08bc3339289d15e70fdc74ec3a7690ff0aaefc791108b4da440937a", - "0xde254d61b26f3a7b54e16a7b265d42874815783866151333af03f6bbfe6e7f5b", - "0x8bc4e31e8d5ec897751a54b914c61485bf96e6c1fc87c4b9bc0b19f935423dc5", - "0xf20e609f7df30958c85399402dca144944c5eed049b35b2300ee1ebb56e50077", - "0x7085a365683933d31e6b6b5208974a893da182c6c9ada13abd806e03d506d0dc", - "0x9a80b9d0aa3288bca9613ac41372f675401c5d3d230ca1b2a6add2ec68cb85e8", - "0x82e6d8e7b19c30fe2a651ae7729cc406d49f339f2d43766f973b9d97a25857bd", - "0x646ccccadee90279cfbb56b158944b553db8d9bc725258876b8a78de748f73ad", - "0x94cdc4f42628fdf76b0005121574a33d02a3202e6172a62dbebba6bf62c42d9b", - "0xa75e9362d131b54af8295567d15e8be510572e6453e25db128f74be8d85d30cd", - "0x5772c596993a6667dd424bb5e6666ce2625a3e1bd8e9817dfa77f7f6f3d12d07", - "0x19a40af4d283c39bbf558b8ca585167ec699705d2bd3c4ec407ea30cc5ad923c", - "0x38ad933e8bc4f3da73b32053b6fcf3b6c71ae4bc4f69a517c664ea228aef4409", - "0xfabd9776f037f9da5c80c4c88a160e6f139b08bef321e49c73bfd741a0654fd7", - "0xb072bad767d789f424ad028d5802a0a57116ed39aa831dd7831d70bb7165ece8", - "0x673cf8961606da36c2721d0536b8a3156860eeb08f664305e7a622104548859e", - "0x6c9eefefff1da960879563dae8f724144a96415e75e8a0dc54386db7d43cef7d", - "0xfe4be619b6f8239049e57938e5e1a3cb51bb449aacc71ed4f0d6e37976aeea93", - "0x98e94e7d559eeee117526394013c05745833c7effb89ef544740d775998e2611", - "0x442fa6baeff05720c957a896d00b60d75d7ce84bf688087a0b2a8fe3a3e1e97e", - "0x72789471d2b83f99b78fdf098f3567173301baeb07b5cf6ffe55acda8f9369ea", - "0x80fec35949e332105c99d631157f4b47f43de8bd10e45c9ad92a4acbf236029f", - "0x5baa59d727f7e4ea83cf23efa88110e8191cf5a89c68aba70b642c96c0b72458", - "0xda33eec2a35ff5ebd8ddc2541343e097320b99fb8ae2cc24d055253d3bde22ee", - "0x2daeb33494c6b567e787afa19f46c3573ab7896880ac064cc30c24b582099758", - "0xc72e07ec23ce92a0e602c03daead89176ef52171f5229ea97777e69dab16d348", - "0x3348c37d4fe2b489f7ba29f775c87ca8fa775c2de95dbf7c87498a78a620d76e", - "0x261c881f9be7b5ed77e82db8e834e7c341d942217264d7b0dac1ac29c94d8f34", - "0x6762ff4f6f3d5635156a3eb07f5b259c50566d7d5e42cdf52ca11549574abdff", - "0x4034ab592ada763d4b5e32cfc3714316a599f0949288b9aea686f5d246749197", - "0x40a9eaee7dcba2fc78f5435bf5051f1d9a84026206f47ef02acbb204c34038e5", - "0x26770fc1dd664253579e16671974104ee00835e80ae15d164ffcdd5b194fc676", - "0x8b8fdf4424dba145b4fa00ee2e72dd1f2950120bdcf9768d1b6b19024055807f", - "0x0324a4d2fd82de6bd6dd459d19b2f02a7d97755ea5dbd010be3b108b0072ba7d", - "0x20f243e1c374b7f7729e875af2198eaec2debcacdd513863f86e5597a5608e05", - "0x0b9d49c34b9ee53774fe8dc1643b59d69273b21837a720763285cecd09222186", - "0xc10756e8639cf67571eb0a80946e4ca859cdbc5761fb6aa5ab46cff2dedf74d5", - "0x390f9b716cbd49681056b9e1fca49530e51472c10c2df9397210cee0dbf7cb73", - "0x6bf8f1ad4d14e98e50c4e327acadf8b22002285f06d22c61ed0a574e64e6b540", - "0x9487c95b903f6d33e8ddac0604167fab06bf6f1a62fcfd8b38083ea15712bb7e", - "0xbc0a6f9f5cb5ce4727803f0a6269f14a8f267e7a3cc221f36c3ea6053ab06692", - "0x93de0978f4a5277ff9cc8d5d538a938a7813d1116f6f473333b7417837863cb4", - "0x5e2f071c171923d561fefb8353ea7029f03d0b61f443970177206e8e93e51842", - "0xe3008f65fa98e6231cd4cd619cdd90e613637f2a1f424ec4163efdf11bd03426", - "0x4f065201faf5eedac4c014b27677d00c6b0325aeb75ad5b8ed716dd0f772c774", - "0x077e03f083a4827da5321edc220f6b96695f7bc47212a7a0982d0215d0115308", - "0x21a9cf94043d38a61471a5204a4fc6967587c970632a6683fab048ea27ec54e1", - "0x2ec179d196277a51271fbb2d8a24597f8cb39ecd0746a32bc509187ede47dfe8", - "0x68ed1279809702f81463e8230122eecaf48cfc5c0eec75498985d261755c076e", - "0xdc3fadf8293aefe9b9b95ae2643b48e874e19d47bde7fad6f12723e1e2c9dacc", - "0xf861a8077feb273d45d934549b07e846a16ebb2c6555ebb7e57dd9027e6eb343", - "0x3fb3c7ee945d5782cdcb2f8062148f8335023a42f8a1d94ef19578df3923ae84", - "0xd9d6149c09826a82d19bbc0efe396629ec5265bfc6639bf49f977b971cfe11fe", - "0x856316f73229995d3abcb1d7c760fbfaa8ed4a824a1de6faa2adb62dea2bbf3b", - "0x7695527d9fbb1dd9b3d1d95f2c102937b36c69edb0ac0949895a29cb0eecd3b0", - "0xebd39a0511e64daf095960f4612cc97b92a98319f9c5dae9d13f56c3cabf3456", - "0x862aac10df3f3f8486d07015ec829f4ad2a4ebfc96d9a4c1878b9c17fcc85a43", - "0x921ae497e7cf40b3578d172a373c39b8cc7c05414e2a706741147b3eaa23e8c2", - "0xeba8def627cb48ac443fa362017cb5ce06625252475dd30e7731b94651f41975", - "0x8f9e9090036f690f546d90cb1f74db3804304791947603d2460e2c83e439acaa", - "0xc5fff2c4f0f1847a6e20f1a93e2ffcb305a2b9b34b850532623899b3d450eb57", - "0x4f360b307b8761f87f46e1ad725998e0f94343967cb82bbf415c11bdf81ed1d4", - "0xd6604a5c1de0557b6288ed612da8f3311ded595be696331abda1a121aaeec31d", - "0x80e9135f9f5f0ab84828493327a1f76e22abd9b47bcd765174f99a03a1330b92", - "0xad1896959b309904c28415c9349746a47365172ba2a9f64338b7ef27edb13bae", - "0xdc13ec27d5d5a9799216e01ac08f08f87f0fe27a3ded97f0c79a14490417f05d", - "0x131aa3e6f9de242ff71a78e411a7eac79a6388d93162030fd3e5cbe7ed94b36f", - "0xa2defd1306fbce126ac3fca31f72daca408160b2a8c9f1e1b333fb4735471e90", - "0x565c329d62c9e15a58c00f273ac372431e0948bc1085d1f867bb9d1da5584e5f", - "0xddee498c84407a18f7172931527b6033fe72a76a0c1da15552e83d4f8111d81e", - "0x21e8ad6c7085ad6d9684a6d0cb649ce7cf736aee2a5f0b000deb0562b405df42", - "0x45cd4e94e94e418f254673365013611ced27961529ab90ebea1bf22866ab8f98", - "0x21641687a64d6bb2aeb1fcd1dd6820e8a3263e8dfe1ab16cc28aefd81ef33f03", - "0xd5653e5636d08dcfa9eec120562dacb88362164c1e6cbf22b50b1ed763be783f", - "0x799d28262dd44d6edc81fb2d8ce0537bde7f17c553a793ce0339dc41ffe81604", - "0x056c40b65418757a9faef6e79781d952d4d7faa87f9ab8b543fce95335ce5661", - "0xb25dc95e4495eb262af93a71cc7d7f8c3894949564e00eba64a09393381e2308", - "0x2a117faab32173593c369a077551cf32a4f2ad5ff47db09b02a9c01fc9231a81", - "0xb97263788519d2404cd3e87b3c5f1f60bdafdcd66f90cd7f1cc6e48c91e41386", - "0x81cdfcd9d33a1187be2d48d01700f650a3cb5ad9015be43e5e346f1e74ac0910", - "0xc8def1ee952bd7c6d8e7104ed03afe3b2c3524ab0309d675e6a8440a8d30c707", - "0x510f4396b7b49c7a9d22030ddf7f9ba9028e48a17ff4c528aed714e77f5eaea2", - "0xcd498eccfbc75b2f53fa83e69ccd9609cb5b2b7776593b08b215c341814633f7", - "0x968f34b1265f018a2d654665176690c9a7f9c734bea5647aeeaeb30b79bdc35e", - "0xcc7df5e5ce5e0466d1984bd0dd141bad375efb95955f642588d6dbbca7ccf6d3", - "0xff24c0d896be86bf5b7a634246a697b71034b78a30f1c7180a38806e1666bc6c", - "0x5c7638a57f6d0719176a165f1cf0b9660dc115f1a988450148d52b8ceae47bd1", - "0x8f2dcb63926c2011dd70a5d4232896ddde9c484ed5c79619c1d38bbd8aa3a9ab", - "0xa48817b418c2f53a1df14fefd5df47d5d7d7665bbf479d8cadae010a0089c10e", - "0x1e0c61b8c4e6620e226de19941ba161063993af6cab00368ee08225bb197c609", - "0xb42e3cc1fab01739cbae6297d20d364a9dffa1c90b8090622a004036e956ee65", - "0x8675a534d124fa5f2aa0743ea5e7dfac1364e808e194f2b47a3be40596007fa6", - "0x5e6e41af664b6b76da9c492b590f135213f899cfa71f46e4817dd1e8f4825379", - "0xa3137844d37a0211ca70df2542ddb0515167ac2f9e8730b3f78ddd185ca1f71f", - "0x2c45f83c26e711693b1238e74ef925ea7ca62f8ae87a0bb56be658f50bb85f8d", - "0x57efda2e100f86e0cfad773efedc6b94e8433660b5505bf51a2fcfbf36693d62", - "0xdbddfcf9ee62dd2df5bbe3570593d35ae11174c75fdd2d9fa2a036603617bfd2", - "0xc1426b30576b1cdf484839e173b623bc7fb8e44512add04ae4cf09171bf287a6", - "0x63769d98a465a6d52457f5abd4f981335c68681277a665e5c69670c794e22048", - "0x84ded21d627443ce058449f621be8945fddb90126b0dc4013e020c6587e7e5fe", - "0xbbb52eb6ec5aac2d9ccbcb76244c6fef09ce7da0b4bd082a63f71cb904debe1a", - "0xbf8a7d1fe769344c7a4c1ce30ed1078d430f9b829c91eeeeffe4c512aa3d7bd4", - "0x8f955aa1566d88bcc72c5dc44298c7be8615cd16880551af0d5e85e1e75fc9e7", - "0xf13a984aed4e995a89f46f9e59ad2a0a9e6a35a366a11993e80d5409448010de", - "0x5f2f9addccac7a68888d9a75281a97270eace816acf9a66eb571f0b528a974b4", - "0xfdae3b48b794022a96168d280f966c46f27518ac2871ef5be4cded911d8c3468", - "0x09381a1113a9e8d4e17087b0bad6cdfe178e91b8846be88380a42b68d6c92955", - "0xaa0be3afcf3440d0be507bf2d7709c90dc6de0f9cf60f360996c31d9f0777a64", - "0x897481932de8dd0ac4d50687174fc274bba7f00def2654754c53d6a86a0cc056", - "0x674d3e4625ccfa18dfd8ea715f6ee536501e6935d0acdb962f5d36d185e795b9", - "0x2c4d74a35d981b3ac8481e9b2318b3c8f76c82fa9132d5a0df7e3003d1f56c56", - "0x457d591254d2f393c4c5a586dddc7ec840f27b3725a9e3d966c440e61eb1da1f", - "0x3bef7d4aa7d5fe18678f5cac053db4ee36f2273ec5fb11f2158777fc601311fb", - "0x96aade6ad2e2c5fc13a08c19986739c8c93d6107e3083c24a009728259c97f49", - "0x9be928c4604ecee2797c583f89d3cb730390ef41dc6a71273b051daa783c5443", - "0x5f0f6828a3d60a0d1fc8fce5ae884700c8b68f5ae339aed49740f44d81f3421d", - "0x1bebc8b98f2399e3731930a61e547e073c8e74d09c36017d88c52865240c1a93", - "0x89e58b2f46b86a595e0394bcf99c87f1abb1e3880b16a3eebc50a98fdb558e38", - "0xe9e853f496019d39b542e6d4923abd961514c75a35f1489070e8c058179ecd6e", - "0xc45895680db653abcee58043c646a97271e68ea6be5e440020b763814e76d8a7", - "0x7a9b8cc39c454fdf2c2f338bad6399f47af580d914c0529c3c78eef9c42a279e", - "0xfda0c1a4de2e6ab3aa415a3c0c54dfa72f8e5973f31246dc40932e5dcd2a8186", - "0x590f73f3733a7ad889cc51efb29cf73aab601af049e4ed42f70fb62d663ff4c7", - "0x9ddf5288ddc8cb7f770a9c87d3f3b7bba42b1acabc115bfdf8a210a3677a6e5c", - "0xdbe759f16648b0bb9cecadbfd18bf035f00908db82ea5eb89c92089fba99d20d", - "0xe780ce464772573bf46c8c763f15d0d3efa00f13eb046590a535683327e4d660", - "0x1e1a6eb209d12c4ecb97c5b0b6d4cb40ec6c48718e7f66381e6e70a070f4ecbf", - "0x20956a87f5127d80bf43f94c5b5e201768de55eb49eeafed759ffa93cc99e60a", - "0xb95002df40ab16bab3b6697b1683cca0f40f5a8a87f9cc209a7af5b2d833efdc", - "0x4a1dfee0a96f440c25957d4212a962c8bd105d8fda0136337a534137c84871b4", - "0x3706772e4b873e4901d46b9d1e3c4d47907643b9ea66c2c4a00769c379d9480b", - "0x54bbdab103586d5726c0fc06f4d37073d11d6643a08a7f4383fd2dbea805b22c", - "0x71fade35df17079aa40692fa1f4b0c06ccc923b5fbd352137fec2bc6de163766", - "0x6e68f8681269a5670f017489e5518c305f223d79ab8b7c0c0d8123decebb135f", - "0x6b2738c79521ed4aca1e2d1d1d064c9ceb7976ea419c00c1abdfc1fde2f1198d", - "0x768fddc96428bc51d2da8c29f0382a88ada02fad2c0722c3415e5f0124437cf9", - "0x073b3604346c6ab886df9a256e087fab99bddd2b63993a18d19c2f792d5d7ef9", - "0x0d4fd974576faff3e2b7143c7b9dd497a82fdb40cf6dc7daeb0edb92c04dcdd4", - "0xff968d9401495c2e6498651ded58cfd877ac2cd39a4ee76ed2d38a9df9237f48", - "0x941ede6edb4066a0fab64e43d32b914275bdb99d9260ccf13abac4721664a49b", - "0x74369258816347fc00ad9044cc978b081bff27ac93017017089eedc9d6bc0572", - "0xc4da7669db1532966236b6bb91efe50f8e49b0ba763eaf0edf7da01ba7403c64", - "0xe1f544ae2dda3252861a0a40bbdaee2e07a4853f0f769e2de4f591781b26d8b4", - "0xd96de0aa1e5d38c3a99207eebe8bf6a441bab660a1eae7e2f9740566e2b26624", - "0xe594f15b5572d594fd2d6e04c85700b2a54920a538fc46da1f435278b797026f", - "0x36c7f38d700166af4950cd092a7dafc0f858991cd756ba6e488cbad5733df81f", - "0x188ea063d9741276d6577815ad32078286c0e5935e9a4641ee6fabe86fdd261b", - "0xe83266d0dfe0465169535e16a6329b94334ca26a71ca8a6228bcdc5d6d9e5e3e", - "0x4f72f32b995c669ae359e35f853b32d78c9c10ded0090afeba135342e024c284", - "0xf0c349b2c53ae8922dba6635d5ad6c815cd41a18e80091fb53af888dce73fc5b", - "0x449c94f382b39268f70e7cd127b2e983164f4efa3a1ae7ae0457f18cc903e783", - "0xc2187c3ebf507e7ba02d0e9fc489742b07d5d1c6e56e952dc999543f46906bb4", - "0x55451070dc027493822eb1e25dacd66b4885f6e5d7696fe76054d700ba8fd7f2", - "0xb918488b2d6b2bbbf97132d8627b1e90644d0167b4ece38bfc72ebc59cac068e", - "0x8bff7b7f47013f71703b45acc6967777662d08052df955f51321539e58b9ee21", - "0xc588704ec672fea0cca9a37795c7beceaf977f60ef505d021cbc232e6281ac34", - "0xe70023b5d66c749625852e12b1716484b407d277968ae5668cc19654ff970c5d", - "0x9406fb501b38ac94bddf9c53299e856c4fa043971f56fa728cf02980d0053057", - "0xc472250a88b80bb35bafa79ad16f351c0effd4d529a0885bf308923058459d59", - "0xad80c5dcd33345da4dc627ab0f4db7468b7342a75153de55e527f77383d8f6e1", - "0x4b5a6e47d3d6e9b3e7fd72e0e8075556896415ee1bd4b5e5a803f08217f63727", - "0x689205df58a74261976513827cf5bda9798098a2da603990bc8ab3be295e107e", - "0x5e5f44891634df25e9396bac7ae80f632ce05cc717a27469c0ec3450bd86b8ef", - "0x45c8922e585a3ade1d9692ff7fb632ae93c887e63367e7b8719532a019c214ab", - "0x5656f71b6ac8a82539bb2b50e5355bc31549870f7e950c9f45443d53e5bad4b5", - "0x8fcb0de18049a1d68cdbd7a9986cfe683c0bfbb42cda187709fd1f657ee38305", - "0x3e74faf49aff89e3c73240dd61b2aaef3e9055212ab2a20bb0755cfc4e9a596d", - "0xe93a158e733f4c2d51407dc1cb83023df0ad632e49d1b12c6270cbf945e09559", - "0x7f283b0278e6e84ecd8f66bd6e0d1cd340c586a9fe0785ba454f5cc238f88b78", - "0xf367446836096eaa9ab4ba421edb8f0af0ef73a1fd958ad36e52fe4a98d67084", - "0xbc2caa4dfc803ac078b934e0fa4486438ab5d64eda0be84ac82d12782c68d3e3", - "0x444f24147dc65109475cd063e4a37e6c79d0facbf672e716128a48e313e44fbb", - "0xa6c1765069f2e38e2e3c1f40c90b674e8bf6f5e64a518a966dd158258e57b9b6", - "0xb5071af90877914cc565d5b76ebbde651ed6f5db76b9940ae4176cbb9fd92d18", - "0x29f88ef40f89c3889e159d632c941308ed5085188527a36e23606c50ebfba2a8", - "0xc0c02a3ddf3ad0939fc9df4d3aa48f81cf3771dacf62afc956f5faacb1bde118", - "0x84ec18194d7ae7654da7b1999a019bfa75f2f69e1cb2fb0fecc8f4f61e5c0b9f", - "0x2803dcc01f327f40d67094a16d7991defa101d8ddcef31b6d1357e11c06fa940", - "0x2aefb88c2671c3f2042fb93d022847b60d536a8fa14b5e7c52e9cf62fdefe486", - "0x83b06ea5e66be9d159d28b5851ead921d856aa64e84050ba7644218d8ea616af", - "0x3d00b3dce583ef2c73e8787230f3661c7a6ac2f17478d209e7eca9ef1f2421c2", - "0xe74bbdaf8fc7dd39d06b420393dfe0eb6de555fead817d87477a56ab0bd5c8b9", - "0x51ca229e7e529d7dd9278d174380d75fb7b9597e52f9ccda28c2a9563b0eca16", - "0xfc9c58aa8e07fdfbf2007f7cce4fd620215ac4c027da65dc2fcb173f0bd6ad5e", - "0x334c50b0ec398053d7b87d80d08c2c74acbdc61a3055e04856a70cac680dbc1c", - "0xf4913e603c18910e3058b5f65737aedcee7866b393ee319db746334e2a3efa8f", - "0xe9e6aac42b0fe25a9649ec83326123e05adb51cf7ab1049e984bf4e28d56cdc1", - "0xc8a375c04f6845b72cbaa84d22d393c7e744041495c0589850baf86a824b58c6", - "0x5aa08a6a75c4bf9c356a8853f30a625be8ebaa452d3686070bd9e32a70376f5c", - "0xf5fb959c5b65d44152e61d1b3721f286fac2eb64d5f43367dd8d81533230dbb0", - "0x513e2626d5ed52ec89ff88583ad765ffd9b4ffde79645c90ad2928516306f8d6", - "0x69319f3bf63ce64ce910f9ca90374d56f4a1a0afae4c56d0609dbcdd65570da9", - "0x9b1e05ed52a705140484c879821901dff8972b4fec4c8cf8b5a8ac3af20a062a", - "0x6e926b50a300af65a0da8fc6b36ecb6dbc0f36a322477d496bd2ae9d242e36a1", - "0x5a0c0243305b8258bfeb1910533b90bde09d7294de26ea1dd2c815a262dafb57", - "0x8f8da16c726284c5c8c773af62104728558f4c6a3fccac42226fdd381935c256", - "0xafeffba3c64595e4e202fa144c247f9afac6a1e8aac25628bc31628cc8b884d3", - "0x27c414ca1714c88451fb7ca6dcf5801a42fe6b50488986d31f4d33c8af60fbcb", - "0x9804e738c5d2a417b8234ca7ea552add4541c578cceeeb521f1d539d50d4277c", - "0x227b9cedcb8a98a8bec8429d0b6c32c1cb68ce931efa940a66b4b9a5e6e96b51", - "0xf6123c95f854e21044df359cff00f110dcbd86ce9784b8d428601a7755a86dd2", - "0x8e365936727e87c1816f427fd7f07be72d39111a51c5ff6752aad55c00cf3649", - "0x2ef4a4858a36b00d793bf2a207b1a952b730c3d618fc88bf797f0175b7307532", - "0xc577af7060fe80099f26956e63baaa34f2951b38ffe80804217b7eba2d6c441e", - "0x83241d8159b3b5920206dfdb5428d3dd4a4017d77b3cf1da12b68fb922ef026a", - "0x52532815abe951b5b1c29b31d8525aba3357441d2e7dc434666b750da5f6cb31", - "0xa55855e40293e6127d035383ff4a06009521d1e51ef6c3d4183f278b5209a47d", - "0x291825150b3582dec421ec50cb68e5121d672e353d49ad15b241e50d48f317c4", - "0x1ea01b3f106de9453f76c5da0ed054370ff3e09830f1537c99bbe5180bfa865f", - "0x97712de570a93fb5f4a65edb440ae8d40df884f1632b49cf2a2d677c23854075", - "0xd12d14d9dee8a1511ae6acc678264540efbfe3e0bc680d3753073bfa86237f20", - "0xf3babbc9ed75b5cf6237509bedcf1c5e3c4893533bb491ede3e7c0b794e6e6d2", - "0xe9604ce3ec312a4fa0919c3cb93974cf45b82fb58000cc35f0a9aff8640e6307", - "0x063aeb5ae400d73bc55b33bc48e2360e1cff6634c2a9a8282821e656f1ac186f", - "0x19eb4fc202d084bde1d280ffa2d47cbe13f875d131011e0efbcad50bc56510b5", - "0x025d0406da1470c4f4619d06f68029001e77f32c996c65a64dd19c22f5914187", - "0x439f8ac9ed20bb2f9e93d9ecdfa860a8728aa7f5ad3672aad0960d22ecc4990b", - "0x6d4e93a5cb9132f0504757531822b78552dad48e5760a28037c3160fd19f18b3", - "0x7b4e2fb316f3ac96382f086d9b58cc6f34d924799ddd1526b240789d7207fa25", - "0x206be12aafa8622da5f7073eac42c6f6a706623f771ff5f8e14c32383b256e17", - "0xd7bca9e89221da42f54f184dcfe3dd93705eae29844824bb73601cfcc51b5405", - "0xe931dfc9838059409b959382cd8fef53c26d08a509fec1672b5ae32d36a4f642", - "0xdfc59b76afd96f42ca5299b7d83daeb7bd99515a15fa714348d2dd737bf4123a", - "0x2b80ea34e8f3f334a3935aa27e7990753e0a75cc0f6d66335c449143457a9130", - "0x8f681bb84f9402cd3231472d3103a5fd73a0b00bdf98610a1bfa64ec3a3716bd", - "0x9ae86d50dedb89f36bf6ea98bdf6feac997934d0335e4d576501167624807a02", - "0xd610522f6bc35189643b7ca313d308bd09362310c3c242ab054cd689e9a3ceac", - "0x2639ec5c2d1e74d769f0dd9cebf9f97cf3f8bb7237e2557782fbef69fa289a3e", - "0xc3a1bf00517da2707dbf4a77254326dc7560618df58f3299b971de163cf30b5c", - "0x6ab2f1cce24656ed209dfbd63c50289e5314a8ab4d9a9883d94aafa90e5457a6", - "0xaaeefaa934fcbcde6f7d8d551bb04c59ef02044382ccffc0d391602d54bb08dc", - "0x1219f82ec65424420025c517665acb975e844d2e194dc2865d00b57d9c9bf6bc", - "0xd4a4f53312be916eb3f403246f4dc33025cbcb47932601e43f0634991198191a", - "0xb291089807f2361218966d187216a57e7da75a90affda3ffb9ea9686f200db48", - "0x495c6e323696bfbed94b7b141a412ffca903266294cdd609f9a3919a2e56106a", - "0x55e53a382bef3540ef569b9c4e2c4554ecea88e7f02954b5f553813b0f723f2b", - "0x243c3c576d45baede693313cd297000fb029ed43edef95aecf7870b807defefe", - "0x18605e184cfc5c1fd6deb89e469c541953921b69a1a6d60ce96a9b38477ebf87", - "0xb235315517748d459d6220cbe386c18b4c28afbb3ea4f0dc269def49d84c836b", - "0x0f92014b08ee5286e7158fdeda3e1ec128d75a454cd5377cef82dd605b85b23c", - "0xcc197018d000dcbb535b4d7dcaee1895613aa913910873be4dd18eb8db4d84e1", - "0x5272bd28803e27471c8b74c73501deead26b088337d4d108774bcd20a16e474d", - "0x95994b45cdd5e24d0d62ade16622a2df256b7c34bb6fc3ee1d82c8bd9c9ccaca", - "0xf3540d14e991c1f41f0da0752aa6beafa52b3d2dbed31b11a037fdcf95a91cb8", - "0x1e3f2d520c3c8a5a6a5f4dfc98e34e41b00c7f2373e77c894d2138ddaa4c48f1", - "0x73a32af3ff4f5e32cae216a58ce5806b2ef2c2138fc661626ead2f3594281975", - "0x24c7750911b78aafd028a9bfb8723c23709229228b55cecbec2be3a85e4048c7", - "0x6687c041b158f93684a86d7d2ad32b8fc1a2a189cc84b458acec35f421213186", - "0x7be2d7b949ea309c3edd3aee1df035b3b007394d508371db07e1fe7050b8fd92", - "0xa93cc5b8160519159b0aca9de5e3ba73df420773c43faf77aae11e808cd31444", - "0x48da7fe896d8a7190b88493c007446d7e45b01ec9336196fe590eb7104296890", - "0x00b562260930d36e8ff01819603a1fa7e47defc9afca6f694ac80e258b2259cf", - "0x634774d7f08b45b0a5eb7f6366cfc3d587652280e67e5530c19a59b5e3608e56", - "0x71210aa1bb90f98be4b5d3624aa434794ebef0036317d0ac0c81aa5989bfbc23", - "0x08d73574c11609a5c0d8011d88985770c7a9f6c6b13241a60e97208e70bcd8dc", - "0xaa8f2649733da5542c5ebd9093f6385403e54893cd20159c1904b8a65a93b0fd", - "0xba2b8890f1068d0814e7b9f4ec96122b2f054d9d9a00de7be6f1376588668c7e", - "0xdbcebb99aa1320ba6a73bbf03d0c3a6f6a5a0f448fd82f66684132f466005730", - "0x1bc13dbb79db619549e48b2aff6bd287770dada84871eba6cc3ada263f1fead8", - "0x34bd6250079d5450494313d82d55b6d67b5c88b9c327c12ccd1dc26229fec85a", - "0x9bb7f357e6c2fb16a1785ed1d8329dfdd1a0e153eef63a28426da32c6b3c4f21", - "0xa400305a3b3c3a43cf19db3a8c3bdd798bee0b3823794909c99ad169aaacb5c5", - "0x70e2a9779681594fa2c5134479753290cda06e381eb694b733ddef6f7d68c196", - "0x5310ecb256d7c062e314d3efe63c8e4aeabeb8a28deefb7082932bce1bb0ea2d", - "0xdb31f093dbe2d74e97f89d9d4ee90ce620b10516bb2f67f0f72083ef95a08533", - "0x194435831533350741d784baacfbb72bf29607cd156262b1850ccff9066b1dcb", - "0x34e908f1794a6b6b62cd390af76426dedccc25146ce1ed9655a419eef19ab593", - "0x9ebd77d7838567ecbcb9bf6e01470a6504e1d11b72ac7cfee1252a38b6fa22f5", - "0x890acef390032fcb941b497d0a8d9143f24a2ed38320e563f4cb00aad423f945", - "0x476f91830e89ca1326ecfc1aa170638f69d85ebd2d2e6925a28686ffed118269", - "0x05b82272e619631be587e2e9576651efa30f4aa606a602fde6ba6612c96b0204", - "0xbe52d8c0da80709a0f9f74733514aa89f0803a1c3d6318548db8591f04d5cb50", - "0x505c6cae331c6545e52479084689cc05e9402afa29c39dd2db6929b8c0f343ab", - "0x67a97852d6f51efe6d6936b4b46913e3155706b8bcd5cb84a2e5b4345a0c2319", - "0x38235c94758cbcf5f87801f7fd971307e0c0575f0fdcdb01507c3dadcd866196", - "0x8c8374cde00dc3c8d981e85c7abd861cba034740b088c3b3d0d902e49718fb73", - "0x00d8ef455db3b3176ee73881da23eec72aada4ba69d99e2e9e8854689036f719", - "0xf449e78106ff9feac43cd39874c96b283796703d95f3cc2310eb0891a847f5c3", - "0x59a55088e2bd944ccb6c765c1736680ace188ed3cb4426e7883d6a9b13fa3566", - "0x61a70e0b86bf6c918a7ba9e5cee5ab1b1778ee8bb0c6d95a22fd3b316cc49318", - "0xe76e9eae05121a7297046ce013cb15f9f9f4948f75b11080e8624d8db4c72fc6", - "0xe4e0fe04b626bbf1a4a3c07d83879fa431624a2379d96876d0660aa11558833e", - "0x4669abdd5a4414d5c3477ffbf117282ce30e3f7eb53d51a4558a53a6a218ead2", - "0x9047a215e32e7cfb8cd0f8ed9ee511b97ce6c6d958932f9a69de675fa40be3f5", - "0xe843ad9c00989e64bbc628bcd94ec33d9fb7cab7da04414bd5a8836abf3d77e0", - "0xf74a439fefb92fe2f4a5cbc00b5d0aab98b123ac35ce5b721c822e3f01abe919", - "0x2a180beafbe97688ee4776ea146b7a04e1277f34c6f590a36e757713b8fbd162", - "0x7e6b2b0e1f5ca1710beb9679e9a76198e8c46cd7f78e708f8a221c1b0cf8706c", - "0xd81eeaf24e581409b240f4c939eef367a1202a67b0f3f9624ad2b65320a5b1d8", - "0x0785cb4877a081b0ffe0969659e31e0cdc107e074e0f50441efd0d605e24ba50", - "0xd940ad268d241a3816953423ef21b00accee9ff8ecaa568b9f07d450c83b23ec", - "0xe9cacf9f26f9285a92d9de458f0da70df4865fa339257b9194170e47b1b8508b", - "0x0a609857a03ee4a9a12d4ca433dcf4b1dcce562eb8eaf680246c5677592792fb", - "0xdf39272007002deaf6e3e08d39c301acb019fae9fc1604045970b24ee811ff5e", - "0xbfdcb2fbcda60f56f4894c363616d0b618e69a65d99a28e7a97e7a3e994ffea4", - "0x1911b497e25ac33f47d17a3375a48429f2ed8ddaceaef5d87833200d177abdd3", - "0xcb567fbf2368e282fcce5eb91de6d63978ddd81fcf708a7bf72dbfc67507cc70", - "0x389cf86c4724fed48eb07c84ddc9eb57f6ee7f1289ff784b40b0583adbe17258", - "0xe4df95ab6106b76317b98743c8502f542e4c938c4bf6e7ff8333a806e5b7ce10", - "0x5d6011ffd07ed24f53c89f8e0b7bcbfc9d61bbc7bbf4c75b2d016ccf0ffba894", - "0xaf73a81601a6966ef20a3144f78b58dee5a11dcc62d9c8571bffcf47590f8390", - "0x0f2c2e6f8709204b16e1150113a3c21dee41e07e05c02d2a267db5ba790b9fdb", - "0x413625e84a39b66a76f367e93e7540ba19e449da99b20027bee22ee43d951b91", - "0x8c1d212a9981956be03bbeb02cc9481f49e2dc7771adf2528db022a566c1e0c5", - "0x931f8f9b774998cac650e051437ae2a3e283649c70c99bded3f782590507be50", - "0x3c921258cb54fd6749333647bad45ec2e370e6ea1717378c5c220707057489ef", - "0x4199f7bfa2612d7b55ef0866222e826360ac55c331bb224e1172b23b908e63bb", - "0xb031554c6174037e0e653c770748fe0fef7c2e07878641fd8b6172d407c96f36", - "0x80a29a2e5e0fbbe548688162bbe896e4779c14cc36d2cc8d79625f4e900e2e29", - "0xaeeb107069aabd8f5a493d4eabf07269ba8b138de2c6fc2e45c1cc51b16cd47e", - "0x693796fef4816ac117709bb0897f04f7a7839a63553480c58cc9c9e84819977e", - "0x1f7799d1703c941c22d508c77b9c675b41fc9b3458676f8b68e61bc6f782ab51", - "0xb77b822d6bf3c564e85a5fb807a6f2c327c8e9878f1a8bef5efcfc2b42683ccd", - "0xa7a18f0dfb0ea0fb324e44e3e8f4b0855909243c4d9033c049308c1b5b0230ae", - "0xa0510d0ea833b196befe7e7306d032dcf7118f6441b08c5368446585c2eecd6d", - "0x9a147ae2c6e144acb568987fd0bee3217f3b9b3e93c8838e61456bf8b86a22c4", - "0xe9f1a8278a5ae525ed2a36fd215c8c32d829a7d33d8b304ae5e3b1be87bc2a71", - "0x0b72bb9e24280b927c53ca9e0b5881692a512f5364aac34d7fcd3a74e7afd48c", - "0x769739b3852616af794e5da01fd9c82d4f18ba2cd83e3f4cb91abf373af2fd01", - "0xa08efd521c783110f8bcbd6b10e0fe88589b86cce52e74b79fcac0c2d9c0622d", - "0x2a92ad845e5248c29dde55744617fb89da6274b00ab81b39ca5165e7889f7c84", - "0x97a39e46bfa06d65b82267322c932eb47fd300c9c3ee39317490d5b2cfbefaad", - "0x2b069b2073de7bdd72362c3d660006c887a202455fff0a5ec829dac09317ab95", - "0xc589b08560639f3cf40f7ed21ae23cdb4fc6926b8107bf1c5d43bceba8b73b72", - "0x7e60eedd8908ae15c145697fe52bb5583450cc3823e022ffe99f6b339e26144e", - "0xa9aee5b233f7be5937c774181bfeb00eb1f6ddcfb15c75f32ea8ac25e8b27afa", - "0x3b61b38d28bfa8aa927b5bfba2eb720c458e381081ddeb272f5468e0e337127a", - "0x81a3e1d1085962a90b26d069802a3a222b7514c976cdaecb17991dd14c39abe1", - "0x1f323a73660c294067febfee61dd3ba79e6b6e44b56f3643e6abb46bf23e59f9", - "0xbebd2e901a90f7d00ebfa0a37d6c30fb82df35277bad3fd361bd996aab04e76f", - "0x540045b3dca70445378bd7a5e99e7401a6a34852178617c224070ad6168c68f6", - "0xbfb24b0f324aeb1ddc9896d84243304e7904a5877c2d3905e9dec916aeffb833", - "0xd35e90e38c97392ffd3b18f6fa3355cfe7cafa1fb42a679341d120dcbb00cfb6", - "0xa45d268b64eacba83a08e071a70238f6a09268d93f9ace0239f98b9b480054dd", - "0xf3811936dd4e523e92bece26a6ae9757d082b0c90db890d29dd6db748532146e", - "0xd2f0acc18ea883e914643230ded3c14ac41530a207891a60cc115788603cae9a", - "0x1bacd3ba7d6ede3600338ec74c93a544b53e79607706c78ab8c3628abf086000", - "0x84ed140ab09aea7bb06b372079cee2c55014ccdd7a86256de4203b3edb395d7f", - "0x1eb7929230ceae1a1eb2ad2e621ed4d96fb7519519d67690a0a02028354f5b44", - "0x6ea5e84a1e0044b4ebb4c9889c0f0cb8366067e83b41549fc0b566f8af18f373", - "0xc0a31489bec0a05ebdef2d85f63ec978c7732cd7f85363b5c0adbbc24f8b8bee", - "0xb411b35100cb2e46514f4cb38fbafba9b5d08deb69dd04f5f1d6242092256ad3", - "0xda4f3602b5186ba05a36c6801f9671bd81c0f7bab2958f92f23b53428c6d7d20", - "0x192955fb0de4dc5683143b58cee39643b8dbe650ffaabffaf1901c36f66c2206", - "0x01e6ffda8040936ecf0fb9bcb7ade2c111a1ea60d865eef26e64cf2b61c60e64", - "0x0f65e8a8a15d7dfa88138209a07882256d0fa29598c80565d25031fd9c457a1a", - "0x8d271c3e2b1f46c781d3e0b78a3e32ba6cc4a34dfa64beb67000adff81082a3a", - "0x1797d9d3f8ef921578e44c12e89a3603a1834979f4cb095ff97d587e3ad0fef0", - "0x43b8576ff5188a17105986358fd46476fadc8b012c2b5ebca1a6e2c7ed99c672", - "0x9cb12e9bd8ead00e00267b9059c27805d8f1183e38e77062d6e8596f3aaede4a", - "0x487a0d1136629756c07b721524706153dd3b6b30a99ad21524c6ad7bd1fb68cd", - "0x585e4e9bbec3b503e9febaf2fd948dc570a68a7d00869efec0696ee734793188", - "0xcd6687ecd3a89f6148ad3649b533e6bf73693c3dbc7ac8786346c28b31d46045", - "0x6d84a80a92c4f0e46814b781c9bf810e8951b42a3b4ffdbd069af8cf5df68e6c", - "0xa665dbc194d98bd3241df0a0c3a6ad0840f26b6516d7acacfb14d762dce7332c", - "0xe4be31ef78d1da2809a8893e184c8dfec02c9cc6cb6cf9766b24f5c751ffbdba", - "0xf627f2f2abba302c369d9ef1440fccb76dfe929da867e990cc0849abbbcee381", - "0xa0c92bf12232f1382eb20abc27d456348bed2968498b1f50af47b4ffee978dff", - "0x6f785f8c8ee41dee4919d36a6b93bc79358bad7c6526406572d8322d2c4caeae", - "0x956663cff06f54baef4916c5c1cf2fe2f74ed3adc3d037675e5f560f20fb40c3", - "0x254f23c420ee39fdd9a9855232983dc72ae2732df41a9f5a6f706605aeecd1b0", - "0x9f506bfbca58d8a3c1537223b508ab1a73fa305115d567799887ad3fa358dfd4", - "0x45130075968c58cbb9ff62dcf24720665cd0445b07bfcb48f258e774b7afa696", - "0xc82b3616bbdb734206081ffda98c7ad52c25d6e319e128db9805d47d4c0c91d3", - "0x5514a3752fc41d6fde6efbedc5819451b5f5c447da3165424c0b45eb45b7e05d", - "0xd43b0a7a8004bad15520484cbef390383d383f92fdfff2b565d9c3cce843701d", - "0xff3c2c98321126f95c78489407a7c1f740d91d1097a9db6e7c2956daee4ec037", - "0xae6f03d13a9484763007bb1747aec64fb8af07a6d2b75e4f0ca92b1e5b5cabb0", - "0x77c0de0b69d598bf861d6202a9aff1bf969ac530bc4ea29821f3906786bd5033", - "0xe5304ed9e69f36201a17a15a52b0d55172ded72e43930d07d0031f6d7930982e", - "0xe4d4f0914e9da6ec689721953fa6434b7a6df1ae96d5fbeeae58be9bbbafe91d", - "0x4ca8aa5164de5e03129e0c0fd1b25f8edc0be0095880c1be92f559c9e41088ec", - "0x10c0cfc83a0ce9cd01847cd6608d0d5f1e9556bd601e00daa76df36a5478b4a9", - "0xd3dfa0401755d52369eb3420d9f11faa30642f96ac47cd6df647832885e45255", - "0xc5f3e2ab30f2c04663785ebe8616d721fed26f3b0682978d8d293b71c6e679db", - "0x5d3a3771e375cfa767bb2f79f4eb524e24cc75edc18280f45e9b5684feb8cc21", - "0x5f75a61d6cc535180f4866f131a6b3765f8f887bbcbad8c2341d0f3639f670ab", - "0xaa92f9beedcfb4a89c40c175aae2ab7d714b9e08514521026c04c394b8e691df", - "0x3c308a2526b380b770b440e597ab93a9890a93aeeb0f7358e0d718b8525fd83b", - "0x72d99722cae34e5e98024633450eec36b5c5f210ad8d926e706130b668ed652f", - "0xf355aa777e1d6eb54079aadb9e01f90019f2152867ffcdcd9370890e52f78f00", - "0xece9a8bb68f51b68b1af494c1f9f939852f8b5f02bd8ea7d5ed6184826e53fea", - "0x175ce65c526df144931678b31a52c0482cae8c7c5fc257af2b67db4d3b911eed", - "0xbd08d3f0448c85b347293e37521be9cd023020d5f0dae65a58685c9b38b0d72d", - "0x7ce531279c7237fc6a5cedd136859d2dda2994798573eea5e16c9c4c1f8dd2ed", - "0xb9af8108c38f749245886a5c739a8a27ea4374f1e4f1f80f7cf5d75ca24388ca", - "0xb47fd0ca734229e031302c21e9cf9e722313af7687a017be4cfd7a126d3e3a0a", - "0x535c450febff97088cc5b9a2dc53322464d30ee1e1a4b637eb0af6fd9cedfc4e", - "0xa96eec2bbf07a5a721757c5f822cad5eccfc109ab625f60637640b593fed6e33", - "0x1dc48b9654e75fb0469f31920208cfb8daadc29e474c2ddea26a84e41192b7f6", - "0x4eaa4710b6f59a32654b551e6e6831962244adf7cfde3bcca1eb23f3fa5fd6dc", - "0x974fc45c5d0f29e9a435be9801216e821528d63085f91c8f32926fea6f2fe33a", - "0xae9258fcd4e24377f6575b1372d93c8e1f3f182d69e1dce0f34e5323bb564c40", - "0x936f73a8db5d27ac246628d916076f210512193fb0ed07b177f88f38f1d9f93c", - "0xb1d9add4e81f7e31cdcf939e96aec52343f0fcd1c3945a06fc373582cac5e3b8", - "0x715b33556e8d2c731a3a9f82d62de5fd888406275ee48b0aa1e667c500853f89", - "0xbb069b2facb78c576d0b5ac5b60cb5fd93086c8b2a8f8cc3de93b5ee0a8047c6", - "0x139c62086e489a6dd8df1879e40d3efbfee2a5a9a18a20be0d278eb5b6342a24", - "0x0ec77a5043d047ef4790229df9a6420c40bbb12591100292f7ad1cf01a1b26fd", - "0x3bac40b81c8200311f142b21e74e878bde0741063625f8a88255f618aa7cd33a", - "0x0f40ba7924103e076c023a324cdbe7a066af460e628fdae93c6cad2644af9838", - "0x9e78a97556487bdef14755b016b70ed5ac02de3bb49570dc43c30f356df6b659", - "0x3471964bafe51086c307c784b38cc06ab61f5303aee7aee894df2e7a759c40b8", - "0x0a3f66714a6a0b2a1d5f534be95a510f44d8d238744412a532dddaa49ffe8d7b", - "0x7bec733ae41397642f1f5c3d8c1ccf49b6bc9c1a518223fcf01b5f7cbf6f3705", - "0xb9963b533ac1898cc8bd7ec202395b1a84ffc9f2f170727db472a957404ba377", - "0xeeca7419303169bf94ab4b5387582f8512929e9d3987f14a6917be06195b76cc", - "0xcdf32b3f676c7f972fc32235c8aca016c467a67a441575d473add2ff67c101f4", - "0xbec739da4ef3aef4e7a8d40a58f8343fb692d9d19e5c4c6ed2e1f01910123483", - "0x9ca224e1f36aad793dec4eaee936330bbef547282038de374fa8f812b6ac2731", - "0xd8cebaf48d29db6b9bb818fe2d8e959c40d16ae328292f679bf719fba9eba43d", - "0xfb91283b8e72f2b65fed21efb13841cc1e845ad312275a55590f0114b2118bdc", - "0xd6708f204a771a6f77be76e7b7275bd10cb43bc141dee5219574e14298665087", - "0xcf4684a5a211f183a8ebe5ccbab97419f3cba8e9d060e7842d4d9128db5f82a4", - "0xd4964b9f2546dbd080b764645cfda3a5b24e30c76d30089b3da7f93793830699", - "0x3fde509e67bc090d74da01cfbec10c03bc456e63fad6a57593a5f534beed113c", - "0x70612230eb6bf9bea267f83c3bf62d0f2decd1f43653bdd00a0c7b89b0d32b59", - "0x40803fa27145e3e16373032c2512214f35bd7b5dfe06aea4a42104e43a58fb4b", - "0x0d78c2b135f282debd581c77572eac67618a27a2e72928b531d8c550a83ca95e", - "0x1ff6f0966557746d1dead627307711e35e21125c5356183ba6a7fe67a33dd1f7", - "0x2d0e54e705b6b48a0ecb260421b1b320ec6332f2326b570fc2671119c889fc83", - "0x931569040ddadfcfa6ebd7264a58b6d3a7200819fd83d5be3d107e8fca662fbb", - "0xa152c1e663ceb96c2fb4d3fe992a8df968dafe840c41171ae1115ee4c3f2189d", - "0xe0eaa06be11e49629e893be21e3485edf1c2d1c4e10a2ebd56771bd9c8c43128", - "0x4cef1726c40c846ea4b89412d0959e0c60c2d7d09aea7260c450e65b6959cd0b", - "0x03d60eddaf920b24db0cb177b3e87febf7ff42c70b01f19165811c02e934bac1", - "0x20237d9a59b4c8d7bc324055ed2c09106c5a83f80e3faf9a519ab7fe7dfda4e4", - "0xdeba273b77f09eef6383e543f144981ab2b0a8cf5458d53cd7b62f2f4620b5ca", - "0x792ca8b535585e17448d5961a53d48478f5ff3ad780b4cd5ce400155f99d47df", - "0x9562462d4c50af030ddc1eea7afb2daf174d37e87331012ae7809675977d19dc", - "0x534126a5a45d07b97f8530683387aa7b7d3c542791a591c999a3147120c9df66", - "0x395866222f4b9eda175fad2b5c03d8edaf7dfa8c231e1f63b50a8f7ba8ea768e", - "0x3b7c90bbb4c1d512f3b32c52c11df90362395458b9e5036fc115cf8c7e1fe8db", - "0x1b83d9a38e749c331b456700d809d2fa4d44183ac7931a5bfcc270710db5d9e6", - "0x0d219f3be7def7fdf0a6a4fc7b9d801fb7f82bda18480f5de4bd144b5a07699d", - "0x8b239ffbade64752b0753f0455f80f48bbf1759b578877b4cd633120640e368b", - "0x4952033d5a714fc9f00d6cf86cab40961d6f65f41cea32969015eda8bcd86d12", - "0x9337c08af2b8a8109fee2b5d5760dbbe0ebef717276c4e0ec366a7fbbf089133", - "0xf8b5d552c23243c2388cee3d830353d2617058e2564ea735f9266650e0b384b9", - "0x998403af8da153a188078874fd7733a4975985e478e0bffb88d32f9f73165859", - "0xda6b8575198343fb5e0c383c71c3237e22d8907c732324adad6a1c9a7094b364", - "0x6c4089d09fca65f0e08065fe76fc808da298d15c1be524faa7127bdab491bc0b", - "0x6a5d8d5b0d7ed439bb5794e4a9851ced8dca4b62af2c65adeeb6de85fff25ad4", - "0x26268391661872d1c42f455f4607ad6235e8d4d37228d6f6e68d58cb7757ee3b", - "0xf47c821ff8ef71b25afae26a0e7032830af23663ec7c7eaf9541215bbd0ac318", - "0x660e9ecae596fdbe31d4321e902e723744b036af20acfc7b07af33de3c03426e", - "0xc068e5ef9b354646c4c196b8f4b76f90c653de3bfe5d731b9af6a38f7e22e5bd", - "0x855f15c630f2779849f380f9b72f3eee4188786a14609f4523199b5674eadc90", - "0x184d0cf7f3411e67039040c211b63ccf65e12a34807b2adfccc6a96f2958f10c", - "0x100553b7852201b4cfef0c0b0e3bc9b85973570c9a15d494172f372de2f4c3b3", - "0x316e8756e1e2937c91e38a8dbbba3c9a8306542b2ddea63b38ac987b7cf17880", - "0x86d773822b5bd5a74bfeba611793bffd6b10c44ed6dc1fce06bc69b274fce579", - "0x70260f95b4eec1171c8b46f9a19b83ff561c0e776948f79d0c57326f622d045c", - "0xd2860b14214a368f32c68f4ae7deea94ea239ee0099d26c41932ce0218148271", - "0xeeee56e8202b193a51f2858e5d6d98eea1120343c5a9fdb6b419c732f39ccfe6", - "0xab9fa4a69e08fe455b209c841d02d724e6afcbc051d8c4fcf0f76e7a99baa728", - "0xd6cf37977456356252e1e1aab71b518d4be08713c6a4ead6e1a30f7b07b799b6", - "0xf5ac688debe75fa0064a5a9135fd287a5bebb62a7cf803fd6382e29b008e7ae1", - "0x969119e969f9c55261957f605b579e96f7e9f7ea6002466c70d08c3d72893569", - "0xa07f93f5a103d361989a48acece650873164d1e5f99dadc2ae72cbe8f752ebf7", - "0xbe03f1432dc8b8fde8107328794dbb10075acbb835a9ff9876574bd54e6fcbde", - "0xde96c70df84aa83b2c7c467486fba87df3e1794f48ace7ab2a58120057bbeb44", - "0x98c963171be9bc3cd0e851b1e925021a41ae96425bed1c9171a9806836876722", - "0x4ee4973b6ad260367ec2e85de7c9c488eb237386d8c0af4538480a07bbac6362", - "0xe55e62e4da77ff26e1b74b9cd88a4fb9f6bb09ecbf70748079ee98fd1b29e9b3", - "0x5668d865ec045e0caaea9173ab2e0bc243279cca9889b554bf8fb74be7edc8db", - "0xee0219965a896de8fbd02a9e392e41511dc01b32adc879f47e840fb16748e0ec", - "0x067de7534f4c63eabf619d1a723fbbaa940f59ee92100df80aeec1868cff40a6", - "0x701ffa3418f9232a4902df7668b1fc87ac1d9b685cce9f5d820f626540a0f89c", - "0x16eb30bb64ffa9ad25b579f2f019c7886184631abd0a40e82313fed29eee31ed", - "0x5aa8361d42f511c5a3691bb64308da2796244075e829767386f4cc70f8c2a70f", - "0x85eaef383b0baf4533683496c7f749cff3b07bef1bc971ea64ea3f1b8ec97ae3", - "0x0977f99e26f96fb31d04dbcbce8967118b51398b6f0712cdf55ada5928b5ab5c", - "0xf321eac1b43d232c612e059be9e98d24bab395de5406f9b514d39a614a4a143c", - "0x9e3e81a1e0823b9a166cfa8105d3def2789fbd4d3a2ca9871b75a05cf252f8cd", - "0xfed3f3d4d86bdbda79fb9c9935c65d3e980bde5e9f599ee73071cc4bb0571ada", - "0x864268476104f0de0dcf8a024d808a4170158e74c6ef681910e3497bdceb27c9", - "0xf48691090f8661e5baa46f0025730a30cf57d186fc1458628d61e8c1a847bd72", - "0x41656453dee2ddd1694362eae0b6b741a7ba33cdbcde666b4f7e21b087ef79eb", - "0x48c7cacec3e2161bc35642f96f207bf65561f8c944e2cea48651cddb8a4548ee", - "0x5522526dd64584467b416086a92d9e9736fa07d25d524a0dfddab3694eae37dc", - "0xa73f2a4d69c6cbb2426956019d86e0e278913c613bc241c20e7a04fd46475e7c", - "0x3828ba052d46e2cb9db1e6284d7b57af1234b19ef57e95414f76af00588f5736", - "0x05beff29352d466ceb0fd835239ff491da78baf0c4a49e7e86702e5ba8d9938c", - "0xfd1e5c44abc25920639f0d3323c7a37e187e9c3523f1bdc2f5316f58e0de3277", - "0x5b6e17ac5e43079e136fdf088eb0667f2ae1f3d8d74853271f6450ebf10e5f87", - "0x93eee544e2bfa4b257962690614d6e3b6559a30e1fdc99e2d5c9438e3ef5294e", - "0xa1ac97bb27493dc07b4f9c74c5cc51b06a990a016f4ae8b7b175145c49e5e8d1", - "0x347b031d899061ff61328052398bcd237f611fc737eb3c89a644a0f0583df6a9", - "0x6a21e50a143263badfc63c3558681b8dff02edb87e49cdb4ccd6dd22ee640a84", - "0x72a1fbd845ae2f93570b95899b5604431557b40ceb3bcea49f3a601c7dde8fc7", - "0x7736fa9605d1d96b1e60bc80357b42e1bc1af7ee93fd677510788986b271d8db", - "0x76f89a85221739882eb8868e64bf4cb5bf148c1676f40ae8c21d593448f3c35c", - "0x87e9ca31389a42d0333fa723cd93a414baa8785d4e1adbd60803c642ccef4354", - "0xdb85ecbe1e60b2b7323bfd8b9052ef598c9a3978d434587de7b0acb4e5ddfa7b", - "0x74ecc5544a2022d9ef644e07ab4e7bd2fbaa091a0602378e797a79fd23a39a65", - "0x25875438959aef4b9439837aeef040eb22182fd2eceeebd3164b0e171a4f07b6", - "0x7b02a31c30e1aaaadcbadffa16b5d5b726bd6e7ce69e46f131a2e2ae9028e3aa", - "0x4da810150fe04769897a9dcbc762e57e3143f53b988fa384e9d1e975a10daa1d", - "0x02e8c7c864c9099c9de03ef799ae67538e27d4e5986c2b43f24dbb1e22bf215a", - "0x3a2549524a0542211c9caf8d7c50bd9be376839a7835b671999ee13d16ab2a23", - "0xb797387ea54fd787d5f2f7422929d3fee3380752f4e6f72d8f7c3ca1c18aee53", - "0x591126651f4ae3f82def63d62c3f5006d274951b7d0df241f69e5531441c865d", - "0x25e2bce70b31419aea946dbc890957a718a09cdd51bede651135181220eb2ada", - "0x072e249c21a440f387ebb121db0167e690cf227afad7a79b4dabe13535dbdc3b", - "0xac20848b30eeb59f0e86b913a50a0bf5d16ef48294b5753a78392472d5b821f5", - "0x1b264fcdf4c4664716e5d15f37d30036bfabb7dde156156af0d9a3427587d946", - "0xd1f6eeb6648fcf7bfe2a529a1cd95397dd5637f40446d478102cf77c1b4a0a36", - "0xbc61fb25e983a4e72c8f0b1de3c0ce64af6f81e991a3d2503dd9cd378b307830", - "0xd1e5c22b81c1c97704acc88ea4defd20e38fcd33d172d2e10c88b9e61e42186a", - "0x62429704d8365056cd9200d4b75043c1ed888978f59cacdf4df78a77f241958c", - "0xc75f2eb3ae96a0cdf4bf343998acb1ceeb1698de850be9739345db110c4547d4", - "0x293f31fb7885b5a8b56a806ed39fd4ce21da2b2f2aa3d53ba64ebb69ad3894f5", - "0x25789ebc42d9fce453435b830febbdc9d96b169e58fbf123ddb4c250be46e186", - "0x22374e1bf486955234966e8d5749a12d3fc8f3757deb03635588ba4cbb3b6d05", - "0x5ef1c59731d07948379cb8a2f69bb6c85831dbf4877bcd15dbef2203c466f08b", - "0xc8a399261d6fb17dc44542966b6f747dd9bb79ffd9ffaa4057cab7005add231b", - "0xe86ae035915ddd476aa6ae8b0a5d7efe1b12299749fe26572432e9de84667b9d", - "0x63ad0c23be328e42265531a35cbdc4ce0cb54466c4df2d7c06cc78996cf59359", - "0x9f7a76b739a2c83b3707878f96d738c75339187d18692df863a3080622e521c4", - "0x9de463b7adc2645388e7d304e8fc3dbd41792feb3acac6df07a7e7b738f18918", - "0x91dde10aa2843b61d1ec9698695b2b98e36e4256f89149efa80f05c783a46703", - "0xd5b8ade80a8e8c0a6d3409d1487a75261c7fa09b12f42d688ec3caf2ed3fb845", - "0x928c920abbf2e979b8e083b2126a9e57dd32e9dee159e872132bde12572fbabe", - "0x0df9a734740e8e43a7a609dd1a572b34d03fb6ca0ca2bedc4156dee372d8f031", - "0x687bbc3fafd974f8acc6cf950602684f4dd6c18aeaa0810ba8379c69f1f93e69", - "0xdd0758b5ce6846d130a2a510915cd269e5bfc32190cc192c52014e8ec1152daa", - "0x6cb72a48fc6e2fabf87a168e068da7f2a2e2b6f283bce30ce89225da1a7c7fa0", - "0xc69e84506b768224407215792c4eaeef4e2637c49a3c780529263dcd06008e8a", - "0x3d1c651e092a51426c336aea9d67e24e539b164a6328d3e1dcfd682e308d5b57", - "0xce28ebe86f5c68349465570c4734e67de9077042700cb022f50a1692f86c5ac1", - "0xe1c0f3403dba2a0252851d0de326a77b45bd6348aa77f1fbf50ea400291b7369", - "0x33e8ca0dec08e4926eb078713b29f0b5e3a149c72665fc118a1b390645597872", - "0xa7b1db9f872acba007a3d289bb9fe6a344a598484f05b3d09f86fd8937e4a7e5", - "0xa167cd03858447b05f4b04c7a00dfa47ef401a591c0cd6e21dedc31c6317dd88", - "0x7a36596e1d467bf69fdcf89020961fe226a7a74caf3f6a301a93bb62ae04da64", - "0x6df1255e8d0f1dd26b4d8c2074f6fd6ebd19c63ba8d4f018981fb3c5143869be", - "0x405a94ca8487db8a2848ddb2626780be4d708a524333f337d3a70388911dbaaf", - "0xfcb3c2a50e7b705e9bac79bc767dea39a72453b624f376f2f443cca4d71fd27d", - "0x0df59a47ddc76021493c451a150e89a5feeadb9c0d7eaca5735adda83beeed81", - "0xbcbc312916bcc4384c311770513a6e25966ab0bf4c603abc2e76341bb5e0861e", - "0x6bdcbb81e9ccf9c3ced0b046190510b50ea39dd6f4ac7bcf93ce9460d26b1a85", - "0x2fcd38c30ff75f79b7ae8cea289a994c2818b182f7a717c4e00d955e94c0e4ea", - "0x626aa23fe71966ae5d0ebd2b243ba26a0251221db8dc72ca75128cf8496c397d", - "0x3bd021853dcfb8c802bea2a6fe5bf9e0d9eef8dde8ea427923d373e68690edef", - "0x9185a0aa01c251e8e66bcbdffab85e08beab296e611d32eac6e1df89716b65d4", - "0x3110ce921cde1d11185e96c7202e59557bcd0289404b5daafac3eb0c3369769b", - "0x48dd13aaba98e0b9fbc5bf9e7fb4fb4c2399d6c43d348e7d9bce63d552ba4b16", - "0x949c894608158f17b860305ec50ccb9c261bd91698b4e74d90bfdc2e6eb7f6b4", - "0x03a54f8f490fbb7337edc852ce4db855c23d63952b680f0c7b0b4929ac32b53a", - "0x1772c66225597523609caa0809b52de4a0d23986c2a5f9a10c72138fa0da513f", - "0xc0ebd7935a25a1562f1ee993805e91239ef0e1677c6cb193399c69489aee3372", - "0x92c04fe853c0b20b8818e74353f02ab732277570d53a316009197463159aa998", - "0xd7748a6066710d8cbfb62716bb707d70d2aa35718b0162eadf28efa167bf4247", - "0x40c764559b23340611f6e586c5b3ca3e34109f39e55c1013444e5fb46b6e4ae8", - "0x664f106f2b63400e49e27d642888c2af5c90601ac3421b16d28acffb81980552", - "0x94bbccc56c838a7144f550125bcb6a78239e17186986bdca310b3b567bc04eb2", - "0x50769670c148c4bfd4a439dcd4a439f5a3c562eeb1486beccd5e47bc7ffe7e62", - "0x1dd32b9d03d7db815564a3eaebe223077329a00bfa1b2fb95ce5c3e28e7e1e7b", - "0xda75f4e1904c0046f8b932e15f1ee75ba5b18d619c03c313f91fc67352663dee", - "0x26b8523d80f4b02b48e90aa144aec7b3e8dd2904ddf98026da16785411e650c4", - "0x3d632940e2cb210bae6c004fba156ff936ed5b899cfb8113c3fe81aff90a9d31", - "0x5afbc6c18455b8616f6946c0eb86e25a5958d4ee43aba602d73ddb51f95a120a", - "0x638a5c1e22c63a56923b20094499f7bc341a24977e2da4330b4ccfa7cbb2133f", - "0xd03566f2a41fa9b73dd3008e00290cce8c0b606790dd4eba6954718087b035ff", - "0x044eb0cdab90661aab26ccc50dcedf16bae98e1dc64eb0b3efc0823acbd9189a", - "0x87b06dad58a4570e111b8fca431eed1194c7d6a3d2ddde4c03af3d5dcaa73c1a", - "0xa4a7a4bd57d4df07fd7f724f877f1809ce58267f4cdd6e01b6ebd74a51ba4f1f", - "0x5a677ca1f995ff347a445b19adbc95458ff467fd6e54bc5daac6631965b0682c", - "0x66d2db95f8a2dd6ca5e35dd98e48d65566cd7b49295206503979e1aafd2efec8", - "0x2a084704906271f7b6bf58dc814947bcd7bbbfe946073ab8ed93130442c28547", - "0x009fedea5e258716c82c202710581078c93e5fbbfc6d309f4060b883bdcff7f2", - "0xd05dbac18603ec1410626621f1b9b678139131af740c7bf913e5596b4ab50afc", - "0xee459c9425dc5db61b17204a8f16c7e30ac2d4e0e25dbcb966b8105bb696cabe", - "0x4e5d309f715ece7e3cd39322b70295087a1e4aecfa09935cb1c02f11e9c0b0d5", - "0xf205313bcfdb532ded0a5e3449510915cb951e8b39538295bfde1529f0c8e76c", - "0x425af7920153f9145c21f7596856855d55a985ea3b86007641cc648f721e55d1", - "0x4b7e0997fae4aa62129dc7542dd9c8759b3dc83b295edea0c301c3e5d0c7047e", - "0x66ddd8fb6fd5eaeaca4ab30997078089fe12c23538b263b6a30bad2de240de70", - "0x2aae904d495d75e755879d332388e3ffb794506f7636140d5996d198f7d595d1", - "0x57715fbce48b3eeda882ff4795eafc9e8a78a005c7f9fe29aef34e76c532aa62", - "0x7e292ed2cb56a5d088454c304405ca92357048b1a2b59558cca938868a489e41", - "0xf7d14a8e386b97e0a1bda779de74cca484c7ba777f434e40bfaa38898816325f", - "0xcb97729d67e7af8af5aa450e419ddf517bc5201719c081abea25b7c1b3fd4b94", - "0x8ea13b5666929e667b161611016990588465731f409f27bdc594295ea18f053d", - "0x515ffa2fa1d02fa812ef6cf9accda980ed3de8734cb0e6b0ca779642bb9b821e", - "0xc0f4f27af937f1ca108a68964b99eb1ec541433fa4ed7736ccef44cb34a2e220", - "0xa6ba9d246099cd4ac98171014867ba2099f8af6d5960d4dba2d02ac3519d5417", - "0x576ceb5dcba6fd0abb933edf52c56ab1de4bcc233d84b3335abc106dee26a979", - "0x02756b443a7d7c0147685d3bebae7c6b8ca41e08a420ced3cf96954d35b8f8e6", - "0x76b256b0f219de31672ca96ef25df475cb4cd886714dfcf86ab5f583c6831cbb", - "0xa8b06de1a5e939f400cc040d97023593d94bd6677f775344dd7fbee523e37793", - "0xc7ab90bb0ad571c16f4cf04f5231bee6461bc8ad8d3341022dfec1fa4683008b", - "0xe5dc898a5611a5641e5668498bacad7bcbe8f2f56f5749f108e3e2edc936d612", - "0x3ee0715724eb91abcc8e410bd2dd62329494cb806531a50406bedc790db70163", - "0x2adf081b1f48b98041163816ac7cc1e3d553b90cf2f25957f18d78dbedd28e78", - "0x55ff274524876c6bbeb9cd741a09205be57818692d7bd1f44dbd00fd4c987167", - "0x5bac9d22d8224cb5aa6dcb1d1d0b8e7a19a7920f9c1d60e907dab4ed1de56030", - "0xcb040f8185956dca644d263514f8df9e880ee9e840d41ffebcf2b330c95e084c", - "0x01654829da5415a428777ad66890afa49a9f5c018e9183e75ed5e9ce33edc6f6", - "0x1f5c6aebbfa4a5eaa31e37a2d9e1bda3903d1e60fb9b116ba108d1e728f8d1d6", - "0x2e1285d3632ee31f39c213cf9ff4dd0f8f71e02f6d9e208ea1da72f17ca2efde", - "0xbaf1ea78d8a7059dc640aa88c834a709430a58dbf0000b008598e50c0a918d5d", - "0x1cf4601e3b0455845ff1bd5a69a11ba9b1cb3766552250413ab05d23031d5ddc", - "0x24cbe63ee67f5e46571dee67ce5231d91d37ac9e7db876fa203dd6b711f9468f", - "0x40a40d3e099c678b41ccc96fa40d58176c78fed3a277244296104ad34fe672b3", - "0x5fa9756bfeb3a1b959fa9e0eeb4be5d610d21dfe79b01da9629678a57792f785", - "0x831d02f9bc15c3a107fc0d02af6986e90a4e10c337173e2c694049668494d6d3", - "0x0dea5d14f6b970008d4c568fcaa012c666997c4403003e071398091a4430fb3f", - "0x55ebf8cb23fc19be6f1bc81c01d22a9d68b33ea72057088fa218f5b823d62190", - "0x22c432494ec0fbf4b86deaf9e315f2fe303475954dd7fb334e970f075c836d18", - "0xeb7c33063357ed449a7057975fba7f1534859bc7a2b0a325675c0aca06d498b6", - "0xd1b4c264a6b1a6c5b29053536c94f9212a5361a59ba6c347bb293e2e5f4ac3c1", - "0x1248e24c5585593adbe274c96b6085c27af8d9109bb9e029c990ef8fc05b7de7", - "0x62e71fa6bfd51005525f7022aaed1dff888b494e33811bb8ee093528d22c8e46", - "0x452dec3f2d5aa670be7c63495b5380dd48a740088cb0c83bf3177d95070d30de", - "0xd4a684ac6f08f8ddc950b2fedee72f43fd68e1e9b5c90c49d628975933b0e6fc", - "0x13304a80344874cfd8d3473b3417bcb0125cb51bca1b0c17b1abd273ebdb76e2", - "0x119725b4c9f238e1afec7a1de2486d83659078410088239d0b2dcbb2229ab4ee", - "0x84d0632697d03d038465c69cf09897f590f2c54d4b489cda0933ee78b305159d", - "0x9cb911ea27c7b78d83cd213bf51bb1e704c6f2d3301cf21f7d556461ab4b4547", - "0x916fcca360f78a1cc254ca6a9cc813fbab69ddceda7e2a9d9f50a6efa3807c96", - "0x5e372a3c9a8a9a7152e85f6daf47501c22160873f656e9cfb44f1403ddf1a33c", - "0x3903ee5968ff71b55b8231a2293bb77a62116de299024b237c8008f9fed354dc", - "0x6a06a0e8a57406f09f8491f72a65440a7e8af76f1c94944cbe4431419009a286", - "0x718f21ef626dc8c5d1cdc673b180f83236964a04d65c2fd920ea4ee1292f03c2", - "0x266f181b66322be4694ca2d29334a93cf048a55461d5ea522e3f993b10c11da5", - "0x1329d0ca2ea012dee3c12c1eaff3764ac129b39439f0ab31c99315b845930b7f", - "0x148590d22c32801674a59d19057fa7c4fc2887f8526ae1c4f115c11a94343669", - "0x750be336395c6f4de6ed11ea0b9b021ccba8fc06d24acb28184dbcdc1c2440dc", - "0x13bc3e338a318feed2536031cbb5b0b7598b943f32dc2c88fda0f388688d496d", - "0xa0c0dc51049562f22afdd6c7c09d742c1faa1abfb72d6f1d1af01d90c61c0e00", - "0x62582df180bd65aa98acaf1b92c6bc454ce1ed19839752abcda3c19e6d33d374", - "0xcc86df4a115333f19819b493d17cba26a43bfdbb2223e4931ee6b5eba5f38191", - "0xf2704da9f0b3e4323651948714f738bcd1b98b375c206047b7191e78120e7cc1", - "0x9b7b5c587abff319712424711c2283c63eaff7b6b612c3c3af3db2e49efdf4e8", - "0xf048e6ffa41ec43d6754c3d0aa4b348b8cba9c694dd642101e89bdc60f851acb", - "0xd54a3922322ca33d14d7a76f9112351561af5cdf6938190a9f72dbc743821d93", - "0x224fee97a81d25039af7bb9933154ddcaf3ec019e15e9d8b3faca76e97f5f456", - "0xd05af3e61e93ab21bc4acb5f5a7f5b1c65cb62a0f66418989a33cf5a022b1303", - "0x0b4d26d13aa33c3f18815ee500ff94e50b07744ed653e1730505f34807171fee", - "0x042c4d57c11f67351185f8da9d13a4eaa81c16a0562168f3bdb5dba67695ad3f", - "0xed23904d7f34fda2feaf767b4a159f4bffed7262e5fc6e57134fb9f9e1537179", - "0x86150cce44f1cbc0d70faf4a9a8ef43d4cb379502c3f093edbb23b29da7d69f8", - "0x8dfd33acc56528a2305927a1ee8ad4e6aeb2ad62c1617dab0e433f0349e23d68", - "0xb0265b1a9e0b4329e58dbd4c7330334b89f62208b564d34a36adf63b0a1edbed", - "0x449ad314b97b04a8fe27b49c3ceaf4f48ef4da32ebc838ec92e760cfc69ccc60", - "0xf8f709094f667677a51e6cbea7d80111b3dc605fe85eaa89149db75daff572f2", - "0x958dcbc8aaed9d26e1471decc11f45002248aa3b319474a4476c3ec3c56156a5", - "0x3ff1d72bddba150d37ac533d8cbc79fa0df9dced62d63788ead1f7f0e2f908d3", - "0x1dfd517bacff0cbed6ee00e1aa0860e1132ae0357f1c04cc7fcb3cb6c4b576af", - "0xf7fe6f99b1d498ed42147d0569a367453b43393383bc6766b799041f539f11b8", - "0xbdcf4453234b219089ff1319929db364d41acae2a6852c59324fee210f4648cf", - "0xdc96015b8b607582a66112acacceb00436ae99b1457738b2aaf2d4607b902bc6", - "0xf94d38138af0896d0a5d982d8d2f579ba22602edac3e197d955fc3fc08a1d66e", - "0x69905142ab72223097855fb3948f08820464acb8648d9f17889dfc4fb53f5119", - "0xd5f6eb135b118a63959276d91ab9a243cafad92a5224c1607ac23627459af522", - "0xc648f20006811f74a283f809e7870136493a922f73a570ad183c52460583be52", - "0xdf9ad900f0078907865fa24a2cfa43cfe463414ed730deeefd3346bacc3af191", - "0x4982f194d6c379706f34ce3662c88ffba3e01ad4b1531ddc06bdae8205443f1c", - "0x7b7711b498ba7fd9e409ffff12fb4ca0bb7a35b0a7a52fbd9f4e891fea5e19d9", - "0x7483501ff50750143e8c6105d110ca22e04b47fea2f52e5fe78288093126fc8f", - "0xfe7f95c1819c9aeea3b825223370a12198d83e9e5ea8afe3d7989576b4769207", - "0x7720c14c730ab8e13cc2b6210fd357f580c96fce65432b04ec787089658eec6e", - "0x37597a0cb5f115fbae5304cf0951633845b60c8456f0a8015d7920aa5cc4789a", - "0xe9e6ee709139afeb0f9b18742f10c444d982560ead60430bb6b0c7b21b823fc9", - "0x6b1e527db7795acbc6ab1d62a449d5df9f958ce2cce62bc75ac3c4851f30bde8", - "0x36934bef6115f8524890d77f087e49c6b95d832f7e5c15dd40a99e12c27a7c25", - "0x828f90bdc68ab754862b11e2794284b0a79130c4f9dfabf743eee463dd138210", - "0xc74307172a17ffe3f2b22c397fc16499af6c772c1ca4012504085304af6cb1eb", - "0x3ec72268a78671c0027a5d72d14ca5441a65c385961aba9d79a8b5c60f06e7a7", - "0xd85c033362ec66336cb685af11a577aa8c58f6970c396c1c3d01b373e28bd83d", - "0x4236db4114a216aa3db4ee43dabf042a4fa142f506671db79271e5e87d43dcc1", - "0x0994b83392c79c039ddb3a7a39af10944f9c8d110db91ac6cdd3901fd6162e3f", - "0x05dd8ad9cad24784c538826662c60a5b7793cce0c9b0e516d302c74caf4fbad3", - "0x90ff9715b49ffa4ecde93076fbb26263d5cbbb5cf1da7d733c7969a323a5efc9", - "0x9cdbe11208234c59c0c2d9eacdbaf71ffaa35abddb770a9173b5161aa12822c6", - "0x52508554b3c1774feb39569f3f1d750de3c6378ac1a00dde8d7937499a01aafd", - "0xb1f8172ca90c9de72134885a07e72e8fc586f15cdd939751556e1b1569827fb6", - "0x62c6c1cd398899b1788741c5d38b6d7e5b87ca6d7df9adebb3a7ede15773ae16", - "0x69f98686ed693fb4bd9c559d06cb36b1ca0049b89224a0d0989afaab75f63a73", - "0x922df7c6782fd8d09f8151de65da4a661888a547ea7606bd0bbcff76da9460af", - "0x92fe7525ac7654d9c1afaecd305bf49797c8f170c18a989fade3261859b09c90", - "0x9c6840e8deabcf6a4f9b6d455cce40e507bacf1d2ea21e7be5e886a7fcfca41e", - "0xadc0de536e5084837e63c4c9307f43d483faa0517703461198dbf6fd9d45985f", - "0x96cd5b306365675610295293bd8c85f131fcbe98da01d65b02d586fd0deff6db", - "0xf64265c5166d3e4c7b43a44e1d342ec17573f8914c890a7844b97276c1f4e9d6", - "0xf57e23fc51b49caee68a24cd0a44c1751e453c305559c4c5e962481960920b13", - "0x2a79bac7e52352c86db3d30dd2b30f9e971812e7b90524259052e4dee002022c", - "0xa34ce80d02bce67476c9884650e9c364b18701df17b98eb626ec8a8b8ff94c41", - "0x3d33046ef8bf3c6ac74809bd647125cb3649c50a683d81d189d5a3a1697d5c12", - "0xa9b71e759c32de96cc2e60eabb3babb2fc54ab03b0919710d0991dd96054658e", - "0x03943f2696bf2827b5b185273736a9f196b6b9876f872cfda33a6287a5d8b52d", - "0x64c3155f299850c3dd2af09764526fd1fd66441f9c9e6f5cb0a2e2301584c175", - "0xea2e486290caa2832e51111a84028c6e048cb75b518c01b036c10414f42f8f15", - "0x50a1d1408a591f87d4d68836e1000be260842c29ee01e9f7d4aac7e7d979e9de", - "0xacefd24443c1df58c5eeaef883152021cb4c6fc97b48ace81eea57800bef42ea", - "0xeb6d6aacefa2588be8c725af0e96a54ef419b955873818e7157ec33b4111122a", - "0xc8a041eeb0059a5471425f16584246cbd82599d149cc482c5a5f33deb8a4b51d", - "0x57133965ef6ea219184e9c4f2cc5dd36b06aac56c232db9f13612f29cf7452c6", - "0xe12e43a37f7c8af5993380c9f9d52dbc079cf6a15dfe6d5bef674bb9095522a3", - "0x872f41b87598ac8bd9db28030b619e821f55388e1dbde3fff91b978273dbd2c6", - "0x35da6ae783f5551e7db1afab49ba65064b9d498c90b020efbfd8d3bde4d9149b", - "0x79ca7bb0eaaaadf841b7c439347c8dd3c25eeebe59184cde665774fffdcd9d83", - "0x7e8fe47fffdcc9497c04c84db14ac49876bf67f343e1a86d932fd82a34fb03d2", - "0xf04393e39f1c99b43526b1cf831777bf45cb5bed023ffe01ce649aced0755406", - "0x119f7202cb5e5f6418744e3548577ea718d0cfc4af84ca2ba163932e79a74dba", - "0xe943792863c593b9d12ba8362ac3949f3f4d956a0ced7d36db39d08d0d6554fb", - "0x89374b8ba8638b278f957dde6caa5854e615e95c56f817b4b4d5d8b7578bcacd", - "0x5beaee31eddaaa076512f20314d86c022cb4343960c1a124a14c09d346ab1b49", - "0xe68cf25266b943db13b5d99c978d9a959a52a63e179b45b38abe3b75b1940dd0", - "0xd046209b2ac88f4a900023f47a3965e9621943365bc67398ee83d8e7f5bb9392", - "0x9c2129baf9e5d8ec70aa3d7fd749ecdf2a6a8176e5507e2b63cf435db7969106", - "0xe54d9583e0d28492f2a55a5616e1d116036ff5c79ef18d75f84330cfbc5a94c5", - "0x217bf1f6b520b06cd61fe846ae6238f07e2ce5b164cd0afcd300a072438d35d1", - "0x38c5656521ab8f20c1e3d76b16c1907c6f085d7483e65c677d5772c42f733196", - "0xa41794dac6ba96bf22822c510df1a6185f0091c42bb5f3d42ee52a3b0e819706", - "0x7a35c8be930d2cb2714a313527d0e7adef99ef4769a0923df79e4da0c7735fe7", - "0x641c1160852152427f30b1aa357b38e8d324383e4691ef2341aad1a804bc6ea2", - "0xfbaee404480be652ce473ce8373dc1ee4f0115ef3428965076308f4030663f7e", - "0x40c26cddaddcde3306faaaa204a7f04868701b0b257a69a0d99bd89d6d8b0af8", - "0x72de7931dc0fd19b44424f402956d53e640308d8161fcad541a6298c4e176a30", - "0x66e6aa59bf00b9816cbfa7f890af9982533786c0f79c410aaa6b4584a4fda270", - "0x6fb2113654b31d029496fac61ac915d6f6cf51da90e70a34afd72e8b404f8225", - "0xbb79f6186b2a12597e9ba6f96282c581391bcfa71c74d14d945ce47f5f630a64", - "0x18ef3a7a66c4287f250ae9310fe19c5331ad9b8078417c601dbe38a5f0fb151f", - "0xf5a098a71b8520b687d9f1901ee49d2e4930ac60b8e7d86bbef467cdfcb17600", - "0x295f0734697e04b2ddd7eba80401340e6f1ffb92ffdb4c419b21dfe1743d9c94", - "0x46ff4895f55e08564a01019056b0b8de3a125c281fb73c8d83eb33d4289721f7", - "0xce3b9b708997480c042a02c7c7b7743eeadc948b8d8479d39a561a2447026137", - "0x754977681746a986f52c71baec741978357a29fa85bf450085f048c919f90b8a", - "0xeb57ec1308b1a5df2d2030b42161a1614f0793637c39803951366cbfb47aacf2", - "0x4415343da1a3844f15e5760837e98d3429fded7bc33b994ef40937745bffbb3b", - "0x8518da414093c6dc7e967501e48c6f0f851c5db9fb9a766592be80bc92356a71", - "0x4b4336daf2468eb8268d05f15aebb687b0a93e332967af722c849dabdf8c8983", - "0x9744bd3239db2321672d3fad68813855cd3fbe242e8ef36055242fc38ccb46d3", - "0x83fd1838bf22241cd133ec22143e73f9ab6cc5d60679f069a5ecd8e1a349dfdc", - "0x5a49226a79fe77472836dbb1ebc3438b84913042976d50d420c61e59fca2e895", - "0x1ed763438ad1e3928fa0ce385e38bd686041062c28afe1ab41820ce1f3866fbf", - "0xe052728bdf265d0ee32484db15e1ca727bb138726d728d1b4ca192c0603b8f13", - "0xc80890e61f7f53b78ee423c1b9722af2b6dfdbbbad942a3108eb313a06da160d", - "0xf295b732773ec1a19577ca63bc1dbf6477b252cb86afd562ab95000aa9f27a5c", - "0x32d1c01805a2139bd8754c20ec9ebcb7bec07d2cb77e9ecca6960ebbd7f630fd", - "0x856165ce956f4995e1135eabf21fd9c9a3d1e8ad0acdf654dff5e08f27d74b40", - "0x27574b2df854573a4d349745a137df0f16ce959e6bf7d2f9d5e35a8f81393f89", - "0x8f473f340bc991cf773664e842168192a591f81d6878b8bafcb3a85e9853d670", - "0x634b934693704cbc13397f9e5dd40c290c1993ba21ba072061f67d8419374fe7", - "0x9e368b89eb53fa0c6957bd212caee6fb83cb3c0f3f5e2d7bf0d5e6d40c52f1be", - "0x666ed8a2afd6e3592ca5dc9875364d79ad998a155cc1be9f79b8531ec47be04b", - "0xcc5a705fc30d157b449fbca19f4c3fd090752f01a25c79398e4db09e5d608e36", - "0xac46b4e5fec1a5420e4225d0a06d1835878e99d608ee7dc664f0ed02aa969a26", - "0x75fd6803ea7ef4b76b2efa5d759a18b389808be7c77bd07855704062a409486a", - "0x4eac0f01ce006b9caf205d9d69ea4b7f2588c35d0d4391cd28eb2874d193060d", - "0xc9d114846a34430d25c3ee2787f249fb5d5c48927b0b9088b51a157d4070f1ed", - "0xf855abfa802a2218c6cf6ee9b83e460a10b99d9e4bb00caf401fd255e8ea84b1", - "0x745eaa0ab1a88ab969adbe465cbfe083cd6d9a0e16388afd55a078d319199c1c", - "0xd1385cceb4e842335fd43a11679a1651f5839624d6de72a6fcf62a701aeef71f", - "0x8ca6643f362f14de97c52daedebe7b534c3f9848e47dfbc74ff35ea22eb34509", - "0xa3e33679cbb620d2423ad0cf7ca0fa2392bfcabb3523e43a60768034b435671a", - "0xbaeef325583ddd0d70225158c339a39cae656394d8838f7366abe1e6fa92fd83", - "0x0fd99fd738d3ded6ca70051989b30e5b5fb4885e3250e3cacf13c8fe1a617f70", - "0xaa70c56b85627949399f2c6ce910b901057e27dbb3376e2ac97f5a21eb336452", - "0x70860c214c0902bebb4d2b63e56de710f3f64197fab1db24a55bfa76781be7b0", - "0x1220df73d96074db0b6c565126b1d1f34fe9aa37ea915463e3265610d92e7cef", - "0x30b5f6badd7a1f14f328dcbf4719a25a53c88121a7954b1bf86bdd32ebc10bae", - "0xb3740510fefdf78178071e675e983f50c697bf425329b6fb3d67897cb9abd956", - "0xbd3e1c3a296b038b039944258cc7d9fadd16e9880da82e950dbf0aa9eae1b661", - "0xd10c706488a87c66f225532092138f5da46a66ce2f2f033506474df57c4f6251", - "0x1eb08842fedec4305541a174b5f2f3a1648b51764a8a5a68abb3c3c309ee98e7", - "0x17ea3c35342139775b39c97b8ba3626a9db11be861b201ac3ce65077e4465510", - "0x2f2ad51f712bda042707f5546ae2a4780d8892092babd629100890e4b32485e6", - "0x3699482aca1e43c46ac13b28cddfb7d2e04fa66322b127b1e33e35a14c1ffc59", - "0x1a16b64e41bf3d0cb59318dd8a9860721f4165e1ad4794bbf5049a91ebe8e482", - "0xb0d19db2493f48c4899c88f5d3b4beaac17246ce12c3c3875db8ee3014324b8f", - "0x4a1563d90acfc44fc636c2d65b0064cdb066e5ecb96617f8537f42ebde87eb2d", - "0xa9b3aacf11bb1765cb1f8f5a0059a8620d81833f351dda8d070abcd8d31cc9e4", - "0x553bc48ef25bb01f179b94ee681eb3fe01cade86733cc2a329f73e9225323239", - "0x7f69a7ea675555481f5b48a4f257fc053000cbf11ddca208cdcd3e50b5a2c30b", - "0xf52022fd47313165f2155652d0df83f1144dc466828617fd66ea72e47dc45e28", - "0xb77889379380a01dd6297254d1aeda969ff8f77db141b4df6235b95a850d4fd2", - "0x017c341b5c24a304fc401abf7c4e2e35d1814549d31cc7a7e935437df5e4a0d8", - "0xe07f6dd48974e7eae5bfc5d61419f43cbcee5da15a5cb3562cbbcdb27109c725", - "0xf95c4ff89df473cd0620050518ad21437b3f68ad7ea1f5d80221b5c279d52a05", - "0x09fe043efac912c5ebe8b936ca7a0bbe0878e75a6397048628d0859788488664", - "0x821a1b9fc32182d57edeeeaa0c3e2756bdf40238f6172df71bb570b71ccbaf31", - "0xd550f297c4ab333e2348da72410d5e228ede1054d97619ec3f8d3daabb26cd5b", - "0x968ee9e7837c7f4db9eb845c0cf81e7df775dee47ffb9a78aadbd29749500adb", - "0x9eae40666da91873a377751549e72001b02fcd908104a8a8409920752b870015", - "0x4fb1d2641fa31caba1faed883f8798079010d04630dfa1b4168b0cca1d71fd04", - "0x4a222181b4a19b58a26d2da292e4b47a912c18b96ec276f1cab4cfd33ce17acf", - "0xa2b65494badf68a52576c151fb20ede37b03bd08b6b5be7c59667da48da31701", - "0xca9523d3c6b00be58ec473cb663263939807a4718975acbe4e818d93f5563354", - "0xae67a66d295c82e30b6af1c2175c6d4682c545d1d99f148569e0852915af9d2c", - "0x4cbb8e4c46ffb937beed22468ec09ea68d065262d4126b5f603ff4580d4f06f4", - "0x89d56e23737196666e3b11067f17489ea385293b6510e34343601e5eea536154", - "0x59656043c6c15d02709adbd4b374fd72d429494fb0fbb2d30dc50b45e832f6b3", - "0xe6ccb1c41796a8f25627f1c581b49609bfc0b4618e7c6b56e4ce60ef2e99723e", - "0x87188b1bc21774d9a6fdbf3bb31a40ce50ed6ad04f2c8bb4f68ba5b4bb0db85d", - "0xbcd37880bfaff5fbac65abd7ac269eb91fcacd32a718a727c1f451712975b2df", - "0xd356c7aee30f9a046f13081bf8cf7bd5cdef7dcdcea98ce72b8fef1bc5e18de0", - "0xbb58b6798bb9f141f4a2cf078ddb228e120e0bdb921238a4f55fb5d06d62d949", - "0x0ea0138e4e061b8c7a26235aecc4106b084f2f0a225855d01e0a0e535128f147", - "0xd0509b0e82ec1a686ee4ec2d234e86c2351a4da629d0dd4fa63ea8acb5c75615", - "0xc1dcd1f056f5f091761816a586efc14223c16eb111373fe09340a10bca313646", - "0xf8603b03f239bee22d6080fd4dff7247f3c4c5b840fe6550d726f9dea89d23a2", - "0x11aaf239138e5a91a023ad33b925051a7bef3a047ded716358a9ea37fcdfcf11", - "0xaf5fb28c6363585f0a9e6e07b1ca8abe25a7377c419e92e941fecb988bdd2bdb", - "0x97e6612b804b56899a6352721946f731307ff10ef3a3374902246b79cfe2a39e", - "0x1a188d5c06ce92cb047fe44b365134690d8b0f9d48bdb7c7d0b2144551f879ce", - "0x08326e6d088fb427d567e5f48f9bf2723273194476ab7b1f3fdbd1173da2934a", - "0x5d41ee7fc86758eaeaf7d2f89e129368c87d489833af34bdf6468d97aca71daf", - "0x6276b413f2429de12acc3891e0d6d8d6f84a0222076968ddcd1510c7e4f6360f", - "0x5179b0d269ac7822edd60c2433e14d0ca080e0cc69ca25160f2815b7a9dfced4", - "0x682a7c8e055d651718b96381a44623d3c0cd5482d5abdeb40d5240a662892721", - "0xb0378542ae8eca6b4c62e06bef747cf878ddc3fa02e65a6d2058b59b7346ce88", - "0x8d9f94b18f733b111c595d078ba1b39ea5f497493c0764946b7dc56eeb5a9cf8", - "0x3672c07d311f84f8cc7d3d455dd06e8329a0674158083c69b6947444d844cf4b", - "0x167d0eb0384565323dccba9faaf5afdf8ee69002f00cd865fbc218c3ec5c2f96", - "0x14a307b41d547659a9f9ad0cc99997acd148a0c93a35cb68d6b05380e15ff692", - "0x8ada7de0c892e08e0d77f4df240477611721555e10e57e5ce88f635baa96b1b8", - "0xb2ea07f4d247acd95b4c1bb3fb511100078ca8dbda6a986c5bd57baa45af8c5e", - "0xe7ca9d79e07576fedfcbdb57b33cadf91ae88eed90e49db4fce73ec6307e07e8", - "0xb29e9e28d2d1cf7192b228e0fa47b3164758a595a425493799a6e15c64197ff1", - "0x27108b3a76a21ca696f0e4a8cd9a3088d8f4e9aff87699d6a5f24ac599ce2c57", - "0x07d8bd50d0fd5072d93bb8bf577eab11abc000a7f82e7bc610b57d37d841a355", - "0xda6a6792b382a199336cf02e21941ddc692d37cd9d140313bb991a61e106bc39", - "0xdba10b67be085101d9e64fefaa961c2fb7ce88a8755600c11f36b639bcad5a6b", - "0x89d5fab1659e4260e1b900a051a6388eaffdb3d51c3320a275d8734cf5760f9d", - "0x11e25965923214834c02f7decb81d5ddfabf2b7dae609dcca8ebce3bc521edef", - "0x07ef19ee3a02c7d6fbabbbd446b93a2d6157a130c72a2e70fa464dd22090072d", - "0xc2d114d1d7c92230fda4961e566d6bbf79c59d5f6d834659d0ce541988246e9b", - "0xa806ae811f29b017ef1ab204ea2690cdc0b092fb44ff902d2a2eae98fca66144", - "0xc5d53fc3c5250443fce19ef19e7d3f8ed2846877334e2c9c6fbf6efbec646197", - "0x7638f3a6807181c76c2eac2f5d4121f8ff7fd9af43cd05c761229001f64426e9", - "0xfffa2fe996dde2034f6655108aad0d9b3c203cc165203d2e09912211b0ea45d9", - "0x7ad6d2fe456d57c95342ae28757e73ce3d82b780dc003f29ab49c1ed40904b95", - "0x0594f6bae93b268aec2cf274e02eaa31c050f9e5c83c1dfa0f57b45a92aa6429", - "0x390fb08a7f18f5e4b968ad5d8ea80b5344487c493797c4f244e78e40da3eff6d", - "0xaaa73882ad2e8871bb226de36ffb89ddb4edc83dbc03203adc30f240ccae0a1d", - "0xcb4f1aa82966978efb447b00adbb73bb02966991de7198b0348ab9e1d1263346", - "0xb6b4b5be9659ba7d846cff73a90ef523eb90debb01c09c16cacb7f12ae54d89c", - "0x63f763356f684e6636f3c0d756c04f431014124ab22ea6ecc3b4dc9f19f2798b", - "0xa540c05e13ee03659e7f4793913b17b8223a57e2a06955471abbff55d498d192", - "0xb495ba5f56bb27d3c3474b193d85bd940fb6817185a1e3fece930f95f7e8361e", - "0xec15c20e65bc42edae809d6482b9ff9833e1da3b8356dd85cc859c6cdbc9c12e", - "0x5d3c94aad26e00ba29f32b74f3ad1f98d37417d8e6ffd463aad71e94c2fa5af3", - "0x2625d4e56a57e21806600454a2f302aff282502219bae68bc46cfae2c124367e", - "0xb069c1ed8c1bb38e4e3f7c9b341acdaad8325ca730896f8aecf61cb90caeedac", - "0xa96a8794f83e8be206609673adb252ff36c73fb4b0d4fede4578a56948d7b730", - "0xa67c8fcd63ec561b1ea81a7c33a099ea1ae1df4763717bfc47ec373e29acccd0", - "0x0636616d377f1cc6e0d137dc540d8c1f4524b39c4d06c55fed4f97c5b8e84fae", - "0x72e9ef9eb6e09180f9138bded4cea00d4b498a678345a86b70660f965385ddb4", - "0xbc3934b7bce3c55baac4ca784e71647940fe55644015607a9ce7fab0271c11d0", - "0xdd342fde86e1eabc6454dc64855ea302ac867d6428894e864c44c0de96cfae75", - "0xead66979a3fd9591de9a53d9c6b72b2196e1194bf41570053086bff526101b27", - "0x6188160f02cb74a611ab553aabbdef66dacc06e066dc03a9b2d2fbe535a9638e", - "0x83f28a56f6024056ff8d1721e15a6e64b974b9eccee06b173d60317de96d1025", - "0xfbae5724ebff4fe9f795c9ffc082bafcc1cafadbd87c1c7f41d8a97dd7c57ee7", - "0x66a41f7b13f43920dc32e4331e032c25b40421ac9b76d0fafa473a6903c140ba", - "0x4543e01ac5f747fbb1dafe627833f6b1f7876286fa09e57f9ce4cc0dd90e3286", - "0xa9df713c6914563663a319e911a2345cd14093b6a06612217a474ee83113d1c7", - "0x171785288cad15c71818ad96b7d70aa08d245f27b681945a460fc9150436260b", - "0x45213365acd35d54c504bc05c94edc435601c3fb79616827ade4bef8f6146966", - "0x7b68dda953680a8489afcbee464c57883bb1dba89498a156085e964c0126daa6", - "0x56b9c263ec26fbcfcdc8aca55afd0f108fad308d97d9f1b4ecae25648a119969", - "0xb4a617718b2840e7304084458ed3d31472d1b17943524506b87ba373ae611e64", - "0xee206776b921a0b17f7e66a46d3f46bc59efd3a3d416b9baed26146392b13f2d", - "0x54c16e665f2a06737566a16e4e8d27f1c74bb86e4f515eff33b599d256bea030", - "0x9d3aa984d3593a67ecad729e9d9fabde7e083201c79248dd2b877430f32970a6", - "0x560c8421e918623e2e9efabebbefa77ef47cf2bfb728069a996e493b8de78648", - "0xc2436d5acfa0652ed88cfd6ec03644fa57892124031bd81c769b1260536641f4", - "0xcb5b68284216400fd63fb771dda5f0549b09543ebc2e412f715087db4ae2ca09", - "0xf362505d802d231bb749367d0393c76591c4ade3dd04bf0724ef661f21a24ad9", - "0xe6dd045892e791f52e389fd460ef342af231d2fa9a887167d8ebd099dd4c4829", - "0xaf8144ffe03357b30b52e9bb43221cd5180915338a41dea010d5e2069e9bcbb7", - "0x3b7647787ec9f714bd054673f2372083ca7e0de1538fd12119df6b2b5f72e2e3", - "0x632a04950b5ad2626701a0b0937116e128cd26a36725abcb40c1b847b1fc22eb", - "0xcc132fe637af129e51216fe39cfbe3fa2ea1ef493f9df625383d72165082026e", - "0x3f1af50536229e2c6b19da6e3aa711c0563dddd07e004b6cb7fbea915e3b2f96", - "0xe63a35f258460a52f644ae126adbc97eb267b1ec7bb40b8913ac95637cdc0bd7", - "0x771fc4006b7495f302d8d59d62d4f348701512465c7bcf483288b4a3cf1df544", - "0x00686653ddd2aa48e2982c26217a7f1428cb8df6eafbda49f44b9bceb5282ebd", - "0xf5d2a69a5c1700170ccf4820e7787057a0bb410e2011a835953558e69b3593c6", - "0x6e8c179171ff913dbb36bccfd0e37094d7684be3b3b550b95451de9a2573a337", - "0x21d0f292d3ace367c98d3879515f860687f9a1ddd3f736e9329b10b9566a5314", - "0x9a6723877470597e1c846f0fb1bb881ccfd16784e5b97f7ced6a858bde277d63", - "0x2379e99e79b024a882b95dd020732d620cde340eddc17e506d05a1c86b4eb953", - "0xc2061dcdfe6b0440dbb0b51e0165c02064ae544f5dbfe2fb6461c426982dbf38", - "0x63bff10713da0453a6672d489dded72ccf4526ab6c42d6fa8e94a7d1c2e83cdc", - "0x9b999c4c7e0ab1e6c2e4dcf90b08dc27074ff9c0e4e65e4760a314611cac06cc", - "0x338dd4c1a4bb139a2d7bf1fc5cbf4a5e6a0509f9883ed48989cc4cda508ca92f", - "0xed1c0c0a31e7d387e367d2578d9526adf0e7a95ba8bb78c917ec19e333195fc7", - "0xdefb920d6c97fd9deb9e253bdb0c47e973c10a6ea2e54736907abf2ff87451f1", - "0x2f4356e08d091074eee77a08f8151ca1cfca1b9c3fc0fe3424ec7e282c135c39", - "0xed00aeabd96140d2058352c5b54904033dcce4d7e674eeb70f89b36d3a6d2b82", - "0x0bf77f382f84de47f5d701ea8ad1162853f8fa05474a54ff5a742118a4aedfdd", - "0x772d24608a38fd3bca25493ed12821ed7a99eb6a79fae2e8f8fa59874c2cb1e0", - "0xc593c4ad7f4799665944170c88709d67b32479f0f36e82b0405745984697fad1", - "0xaa69e889ebd6fb54ecbc137487f6e65f9084d1586a2d4d561b03d707e15a4cfb", - "0x388d9dd700717aa8c19afb6e6ad6ed322271199ec9f4ba5795e612ba466a7a62", - "0x68df4c472e669a2477bd123a135d3e1e9fc1454ef9c4001b75041037910c6e0b", - "0x0f71b9f338fbdab556c5e33603f29c011f9dd3c00038e7adba9da28fa606e3e7", - "0x246550ae6ad91f25dc504667e98c12f076fdfc405382aa2af86708e28f011f37", - "0xc8e8f6e387ff32fc1a290bb5882fa4f56bccc716392fb8985cdc40a6f8145918", - "0xde56c970cc97055f75b49be9359cbcc1e815e1b771c51095872aa5538548d4af", - "0x011a5479a5192cb0c5cf2a2e58680ed771ff7d0d3128d1e4e58c7ee8684c12f1", - "0x6b975fe3351ef01d8e88e16ee96aadcf28aea0730eaf3a7d382d4c5b6ecc3cf4", - "0x66e8a35a06eedd293c10a61f79c00c2da418d372d7a41923b49aa269ed90f187", - "0x1ab5924225ec646903e2cc3befa65ed480e07e4bfc7b23a8112e69a898514940", - "0x42aae89f47915704db5614d2427bafe17d4c89f3b2f9a54af75788b0a465b581", - "0x6116b3cf781b5399ff85b53cdb73c8a691768b68691ce6b28f1e3814ceb67e29", - "0x789e62df669a8accf7fced4a94c4af70b1f99105d2fd46d1e7852737408696fd", - "0xf0e0efb701bd33a9cf46285439498ea610fb9fd7e497fc67a12076aae786c858", - "0x4b37c504b97bac7e683060f6bd731675c9a425dd1fdef61f49098fdc3a20d5ab", - "0x10f0cc3ea407f13c912a02f28e9a3175565d3fe3512e21ebe75c419282e609b8", - "0x48e24dbab06428401206805fcf50e533e596ac7f06dcc1ca78145306957ea2d9", - "0x43fc043bfaf30e479b15099995bbdd637678a7545318a8506257eaede34ad5c3", - "0x1ce6e0293bd8a48a6d65894ad0aa814bb1e220f88271d0478a1d35fdccc4368e", - "0x8798a23cc8be0306074e7da374760dd95ed6b3e85f7d3e1b7e08a9403738c7c9", - "0x05a534efd715fbdecfeb19391efb948fba524fdeb7fe5415379328cb92ee0e7a", - "0x8f13ed4eb277b6d570428a320918e2cbf309252a6c677dd63afbd777170cd766", - "0xd95418ce8a701035ca94cc2fcb44161cd1669e75d2f1912103ca52f83433c92f", - "0x681be568f2b05e562134aa60f5a8173cd5b49ac27645aaf3d742b11ae60cda78", - "0x0ead88dd38d9b21c7551fa6927e65551e491f93e17a23f05cf5ef49aff4424f8", - "0xde4fc8cd734fd01fe291c39ed36a3b4a51f448527d90e4bb0eee6a06dde0b976", - "0x95598d98d26fe0fc0b1e3b703e649386dfa52f892b176a862791c49ff1b8ee87", - "0xcc756753a08234dad53004649a1cbcb00a4b5582355bc072569225f8a49c1f2c", - "0x957aef285d8a880b736c4ea2f14d6274dde065ccc8e9c2ff674477f53d4a44a9", - "0xba94da4c4e497aeffbd0d0a2aeadc9e5084ef32528197dcc89c2ed9cde555c6b", - "0xd212db212c103136ee2e69a44e2ad8d3bcb3155170de7896cb68756aa546d5ed", - "0xdc61234e477321b6e7269d24b8c2b8411c7d94260961e88f5e897e534f82f8f0", - "0xdae46238c68e67e1dfded286230787eef026c98fadf9d29be98e0c3b74bd9541", - "0xdbdc6cf4b55526560ce2d4b47fff4a63b46a6f91d3f7af8711c0c2d5fe7a3a16", - "0x8498bb5772d8ec37456aa75752b5efcb92262fd3dc67a74ad62b0f9ea2a34bd1", - "0x4caf29e606a840a3f2678b145d28a29de65e4db25b5e6a7d407745a11afd9e0e", - "0xa422bacad4b0fc07ac3e4917fb4d86e40575b6ee59b4dc1aa22c550ddaaa4072", - "0x09ef8f935683b1c33a49158fca5d9e8c7521da28991ecc7b4a43d15a7f3e8179", - "0x868e02a322001c542de6b8874ae28c6b242d008980651fc8ec165dc26d1325f6", - "0x4ac2ee1b915a6a3fb22c031bf4b869785bb07af260d4aab107e4a2abf58bada5", - "0x9906fe76b1cb5187dab28a1e5b4ba5e3405305e937f1c88c6876ad53ec1b6c1b", - "0x9d6a2109384f7c9dd76271506ef0c4702e4385addfa5268e9a934b0bf4522ba2", - "0x899eb72af76569530958ec1f8725567a26ade27a89294b16ef4f87e8f12c051f", - "0xbbb2ed5029c368ce8cb34e52f36ddd99c2486b026094a9f935a04b106e1133cd", - "0x7835cacb5e7d39db682b8b16acf95504ae70ad08c64f205ba45b6f43998be66d", - "0x79c9dec734826ec7c43b8d37cd56f2a8886474b71244c0b913eeb652e2588b4c", - "0x574ea94ecebcdb9420c5d576a2bf4220b7cf8362ae7f92a0f5268792bb49880a", - "0xffdfe73b6b47fcfaf6b26c12956f96cb80505bdcfce5302d67683c5fd62197c8", - "0xd0cd4977c4fb430f09cd338f5df66e67a7fabce709fcec371ae13819a375b49b", - "0x6eafe501f49d14d093691c5f7948b53b59f38a80624440bcafa0d0308282401a", - "0x2b7ac65ed137696fc2ee8dbd1cb6a5fc9de6cee9748617ed436fffae6f0d9eed", - "0x0329936be953a4850cd53d84333e8f801906f9d5f17068faf8d2c99ed29308fe", - "0xf09ad47fe7e45c9e10266bfc452fa8e15ee1de2842502af44d08043899d39545", - "0x99166364cffd02a748be5a0a7de4590e5238b94640f68ea7c07bb0d9e0473877", - "0x8d37c6a259ac724cc67e8cad78c463d3505d11a14a7ffb30594918307f53deb8", - "0xcebfc571f69509d40dc3f15e872fcb72929245e8e66808f9cb7ae52e1c847214", - "0x1ac22d558b9e4a82008360050940ff6516a0f942810eae028fa4dd087d16d66c", - "0x8b2d70b5463cf663b2bfcf04811f6a29425de37d0fe4ddd64a5420c557a9779a", - "0x192a7331749e24299fa79d33b3c3bbba166d5bc9b955a3c79cba28f1a39f92e9", - "0x500bdd246d9d9a3376e9fef8d02d30d7b0a0950a6714a1310bd4de624b06e7dc", - "0x444e8465fecde8bdd1b53efaf118b3251bd4f2a65296383bbf25550fff8d36dd", - "0x63cd477c06e10cd5f645b93af2368cd151707051ba1e6d921d386b867efd76f6", - "0x6ec5468c3b201931725dab24ed6e72f89cac5dbe49a25041458c25d0d5966785", - "0x2b03d1bd439513feeef7697d4a58e21c5715e863e5d27a748153419a604d9fa5", - "0xdcf64485fe42fb0b483b81124758b61ba27b3f7d6561d449e17febeda6a01cc6", - "0xb557797848147314de5b34965c8756d3e797775edf51be2814b5545603b1f659", - "0xb3f1dbffdf4320e5fec0725cea7b3c98c8dcbccde0f1e032696e0c14d0bea7bd", - "0x3ffe56934a6f659f59aa128ec13c2db95a2b0df18d1a37840db869e914fdd361", - "0x14a51cd6559a2a6f12e903179547e3b76a10b339e978d7aba19222d7a8995e39", - "0xf55f41fc193413eb8dea30afb910f46536c4d2e0087101cc7a1d1945bf3f9d82", - "0x9afdc6e8253c623ff633f4efdbb3109d1f81468222a7de77fbf216c4b2d46734", - "0x2bfcb3ed3c68ac68546cb704695a479a0c15c45047e98b027a43be8c3756da80", - "0x65675b82c05d41a86fb8ddab438dcd0973e7d1044044542d36641ae387e6f9ef", - "0xe56fdcc41be235203967f4ede3bce76cb0f8e078e68ed9bf6e867bd695864c79", - "0xd42dab74de64242fb5eef9b4c239e4d5bde4c58d838c07dc728b3613c929deb4", - "0x8d52b0dc5de4452809aa787e83f76a2a650e7675aefdcc89b8e6ff34114c2a51", - "0xca438b0b85381752e1a066e5235e0d9838fcb375aabaef6e859888018a4557a2", - "0xdfab1e3a231c959d8d154f5509a167272cb647bc368413c74e411b7f35ad32da", - "0xae1d00fd3cd778188bd5df7627b2a02c69a612873bac8df6ba96816be0929b08", - "0x2dbec9851e37b9a54f0cbbdef72cde39a4368a5bfa0537b96d7ce584c5589292", - "0xfe67d721575913df5f0c69ad9e362462db169beb8f7277e15a09861817f0ae47", - "0x7d6b8267bcaede21401ebf6841f7e95a80136c41f5876a680f28e94fa89bd673", - "0xecbb62951ceeb9b49792afdf3323bd8607c8e0c019983babd997cd3df1f2dfe0", - "0x6b6a945705b609715f9e6e222e0e8145118bf97d29f3f331aac194a054a99c9c", - "0x56a7c6b7d616e3806452d6e8a2540f94410ef10c8b57f7f21cdc2df799bb0a02", - "0x73f6f889fe9d8276f2942cfccd3781e8e326e29ccd033a8273041451c9065ef4", - "0xec84330677c2311e85d76dffc692ed3e2d194e90fef2423609c53afe03b10a2f", - "0xa63916671ed57cfc053a4595a854b26aa31a84e775fd3a9d3e3b4763b6f23625", - "0x44e3a21e4b5b1ef0636e0e4e4775b16d4722838c3eac7062e963d46bc2946d92", - "0x910eb99fbd1e3afc013a5f50b475fb324d20c7aad82c602b8e9c21c7bb914d26", - "0xe66fe50308eae671e265d6985c346cb23594746b2c7470a0d3bc8018f3e89abb", - "0x68c0706f4a903432bc423c7b11ed2fd685b0c11cfcc9c7da4b17ea9fe77ea628", - "0x2eadf540de899a1b71827e8cecd8efe280b6f5a9e96cbd3fbdca10ecfa8c18af", - "0xb67b79aee1d96e626a92864a92d0e1536e4c79d1f7980c83ef53506c3795d4c5", - "0xf8b79fa4dcc8b6943c76a1a48ccedcabffe2a309f38682bc7dcfb6cc71900dbd", - "0xaba4ea033adf6944ecec4d49b886d5cf8be86576a2c30a9609292cb087bf2f30", - "0xa38e14dc7b081b096b93a5588e13c18156dfe90a1b0c3f294568f8fb43a1679b", - "0x796373232dc5595162f632d5c169ee186c48fe7c33be7e9d69af68b836e38399", - "0x612c5be74f0bc72c953546a6a2f70d0e7c05bb3e9806218867ebc29924b7e9f5", - "0x9fad004bd232cd89ce84777a1480893821a29718e464cdb662c5dd9c5a276c6e", - "0x4c0a8776183994e83cdd7816ae3dbff33da6754f6a4dd16405d7c44823266c5e", - "0xbeef52ba26b24e9c34a630fb5bceaf97f5d56577855b409bc8b27e5e25b3b870", - "0x020fe496c09044dffdeaccc2a90ded49031829cf75a747c7a27aca06f489283b", - "0xbcd6208f1eb03d16f20fe1cc19fa2992d1e0def9ba1e3c3e95f26a413153a7a6", - "0x45c09c2c26bf73116b2bfb8762d7837815d2ffda196517b7fe9ed23165b49eb6", - "0x99314aaff566a187f89b9634bb9d28325bd368e29b7a42e73653731a1fa42522", - "0x519db26bae12d9b532d541db13dd74c1e19b6390029f3091e99b76f85e74e33f", - "0xd3c0b7413873d883efdf1c42e0de66cd78a9c0a8b614dd88a2e84947a1bd0869", - "0xc087342fc0e8df4176bea79dae4d6b4f33ca9b398fbd357c877c7680ae39d9f0", - "0xce5714dc42019277e8170fa097ab567ccd88cd4e24ce511fa6e93f85ed8c3302", - "0x68e1064b5077861bd9839539a5529a63bc0e4df68e875e6197f6fc9616fc1c2a", - "0xa898f156e8de5f588480a79ee5448f6a1c3f482cbaed2ac2659459cfce08ae34", - "0xcdd2f9e50fbb0eea3058d3341cbe39a7274cdcb3f10df0cb5803fe1bdeba3713", - "0x7eee48ac933e7cafa8cabd7b2868d92f27b0210bc0810e5a1337ea32daf3ab8f", - "0x99e2e041e1b178b8538594e356ac7c7133f24cba67083192dd2bc30fdb3d0d69", - "0xa4f4e1777b831cd05440067982b48aca3b89b9c2b19ab82c788be5301856f7c4", - "0xa44b761db0a2a19eaa9d2086a594290eb48f8fddc3e9209f3ed8c82de35d5fe9", - "0x189fb24a706ede989db9a587e205b66cc4e2ca5329c4726f5cfc72dc0df4563b", - "0x9104e63319bd2ba428a47c170e0a310a67bd90ceab2d232dfacdfb72bee09f0d", - "0x219779e07435f4b4458ea8a5131e796cc79d47a30692798eeabee8c8a47a2405", - "0x2a946612c3dcb2da23fd7696d4d30acc3ec25e292d239f3c01b0923f62ee51c1", - "0x7a244c94d76d458be7acc6c885c357496a77daa119e49a3ae168908bc73bb569", - "0x274139b3f78e5193cf35b7f28439dc4f74e6927a795234a00262175971b99fc6", - "0xd138526ac6cef7792d3dc1993f364f1b0985b9dfa3db1847d785ecb81a70eefe", - "0x1ce8e07f1350000599c342adb2557d8ccf3fa1d4752b6cf2cc71c42fd62a7e8d", - "0xab8f97c1e9a65dcaaac53f799f844d831da02dcbd372b796375115ab19a32eb1", - "0x53a3a62994cb0391e81167207b1a25604f25dc08240666072b0f0eec0311feaf", - "0x786797a4a478d15b758067c82e2ebe1d0ab2240e8db29e050e6765777b2aae3e", - "0xf096ab958bfba2f4aaab3e1bca5bdedf276bec61516ab7602a8a54ec6219920e", - "0x25ae9275d83e34866d66f4896167de7e04aa6fd8070554b11ee847fff73d9e27", - "0x871e675b37b2ebb4f3a7ffeaa0fa6f4a47db92f517b66fda5bea400f13528071", - "0xe706cb07d3f90e7c4f7c9cf09401b9b82a063fec369514247db76efca5e2c640", - "0x0eeab1ee0c30812b9de778016821a933efd94129b13524b9558a74ae84906380", - "0x7f86033772f4b5265042b6ec7f3b14570415db9a148617e63b84331d0decfa25", - "0xd916a6a70300faea19447bde63491ebc21d8447e5a5225f18618cd44d9d25fe2", - "0xe5e632370370812351165ad2d82a6be86e9a2ea0a134cf0b2d92e481a282506f", - "0x48d4ede9a70efe12d9f21bbeccae2857dc5859d4d155da96806cbb392194aed0", - "0x6def1fa1beb67760b1a94ac9da22a7df6a3d3b9722c2e9c2cd503dfb14340c2a", - "0x0b6493b5a5798dd98ebf99f12fe714d11f78eed5800d892f50f6c80d5f9b916a", - "0xce7bd3ceb24946842e452057021e80b7909ab4326fb43725923f5447da7566a7", - "0x3d2bb2934beb966aaff7629a346cdf19c4e3671116d7e202e2fc9be7d1fa4fb3", - "0x07e58703bfd562eaacef11363c7f70b0609ed6d628ee58e06b11122a8254f0ef", - "0x26cc012c66068fa47616f5f1d9bf45751102e99519bc5cbf0b7da14eb0ecb1cc", - "0xcd736566d5d6c8ff5bff349d3a932b94c19f9765193c3e41f70f0345521038bf", - "0x1f400b109e0773845f75f99d107e6146c97172799351597abd435af18b9faa46", - "0x0009b529ec164e1686276b6b1dade0a82f76963c43f27919b193348bf9372bfb", - "0x180b4008aa291f371615cb3e2975b2b29b3db58d2ad61593a52dd9a9bd963319", - "0x86b7aa3798243667c0001cb9a97a7660145b3582fc2232b4625b001e4babe622", - "0x823312d5231a8db9211b141b2483fcb1b6d311e482efff347c9dd17a74c611d0", - "0xcd0e83e63db1c4a6f2638dce68677bea55e9d4b84feaadb1bb085f485318c627", - "0x3f34cb62b347f4930005e39a102d08d506fcf9ee1418c372dc64a70f86b7e02a", - "0xd03e1359280033fe74fff780447b29397894a811859929eefd4d52cf6ca40521", - "0xa6fb9b05db2dfbd6ff4414b5f03633b46f5db77c198e6aaf9ffb366b9bdd3af9", - "0x87af91f40f569df2c64e20accbd3cb7fa21c2cd24a1f9eeb1dea7a39f11ba3f9", - "0x20d35104742d776aee3cfbf6dc1e36a172bfbd2a8ec14f9fd016eea15e480c19", - "0xa388892adcd0c0109f09a3bca25c6d49f2295dac3c61c6d6cca03b8e92d253bd", - "0xc7189645b5cfab9cb71e3d1c671bb439e8c14eb2971bbc21fd5da08158ca07c7", - "0x72f695f9d98843fc4950f8ae78c2bf48c09c92660483bfcf270c2a633f05e852", - "0x866f028434caaf3cbe032ce693c2628295daa219b0462dcd3ae14df71fe746f2", - "0x5d3d16c2f16c48a442e44fcc0a04700c40c1033bb0276a1f27d264ad2fc91b6e", - "0x4bf7678c9db62bf4f8aa92098ff39619b2484f75fae80f6438742c3c5702c652", - "0x8200e4ab04b42c901a2965aa2243976e553c555ecc811ecbdf9bc470ca32351d", - "0x1676a43cfbf10d344c6837d1a139de56085b88acfceb3cbef0056c11ee6cfa28", - "0xfb3ee392b6c300a8be67d4eba8093471c77a6c6ca5682644b16bafb749d9c995", - "0x4887db0505d7f3c7017374a482367ecbe9b69de429d2d0965fc9a4e37093787d", - "0xcc00e39c22808934bc034c2ac322643668711279b300ccc561dd7381bbf094e4", - "0x2d906acbcfae462edd246ce62e07f29e1cc0a0cd23b510b61f4ee523a2061ba8", - "0xe61769fa90b6df16bab14466afc63cb1efcbe46fa54594e8c7c114ece565250b", - "0xae6085d35aac3488d871bee66119d05c889acb428cd5c6a2a673d85a45932ca0", - "0x17c004024e663872c0ba979e00490587977f2237bfd11639c4abe5e4eefd3fc6", - "0xe5ef57a117bec639b4885e6941aea12a6dd96aaa7849f29f000654a2c29c6b7c", - "0x00dee21ac9b8eec2b22982645edf796dd65fe1cc68629bf7f79ba4910b41c8f6", - "0x8c42ca1d5ecd86e445b0c01da6d60202b1371495ff1a3990fa880c43c4681de0", - "0x234e64d7baecc3e4ab3ac0982ee93dd426f8b0313e59d40be4707c40540f0c91", - "0xd7f8b45da51184e9ac15b373991b0c46d48c7cb5776967953e57f9e85f7880b7", - "0x1d895c0700d2db688ce79e987e3b35b5c0d0bf17ea5432cb1db245592054b484", - "0xb12cb54a7f50711f7bfbf03d57c5c1262191019ef9ce69485cdc41d7745411da", - "0x2f54a275e01adc7e6cd5b464f8d033007ef09a0a3ae29f9bdf179ee985d5f29b", - "0xc21039510b8dcbfbacfe8794826b79d3ad3c839287015857d780fb57a1ea4ea6", - "0xb18484c56bf516dba0e6a2b31c0b6f93cd38957a60c588e9e7fb1859d8ad18e8", - "0xbaceef26e6b8a5bdf0a21cda677f71239736a0544bcac5f3d9a90696b8a7b15c", - "0x97838a53f6996d5dff68c3439689d2f87c7e97ea6be519cf8dd70a47b023f2ea", - "0x882be66e5ac63dfa0f5c31789b24f6a25f6c8751ff609f86977dbd0cae890df7", - "0xe462a0b007f8029cc65f40ae1f42abe47f160afaf92da8470990bd94443165c4", - "0x37a46df02258c5965454e0374d08f3650d16f05b083190b787e7d514409daead", - "0x55604327bf00775ed3b2d121acd86563883f597628a76e5f1abc66309fcb09cf", - "0xd893ebd9fad9973e616de074827b3af3a0387fd3b9a7cd4d59f1bf98d8426027", - "0x2e8c05fc5ffede3b58cfbcaf62cdf334357e3786764c715c2a6980f0569aab2d", - "0xa50933e55e80764035403224fef71248bd1bd3c0fa3b40b485113c203ea1197c", - "0xed34b281864aa711320b075dad853fd035aff039490bbf3d029b647bc7ca6943", - "0x0d29603bcdeab05abb491caaab151e0c4fd2e3d7b9dfbb1f3f37d0869ff09862", - "0x49f2318fe7347589b1b0444d98dc79b31ce22225ebd2c463a29bbeb1b65d45e8", - "0x7cd03259ec36ececd895da05e4490770e4a354e0f84a821de440a05562220a0e", - "0x0bceae0593be03526c84e8979522408d01f8b553bb4e18ed9dc81ca62ec058b9", - "0xc4f885819cf0f8a398b59040acab517ab10d3380bd8ac69645ce27836dc19ffd", - "0xd3bbc4fa9b2136118179fe8eb0bd79c113a725bf6cf7c7e4f8b5f59398dc44ab", - "0x73e21d02abd45dc1689e24b9d25141698b683a8add128122ac3e245e23958d70", - "0x4b6814ba380b801a62c4a3d3e8ade70c48cc7e7a5a4d5f6a0a7e8c5d7b8ecaf4", - "0xacce6845fbb4ea44e2b3f81225437c7eda438029557a26419c6d76755cfaa9ca", - "0x8ab24bd5c8ac2081b713634b56f459db67ccd48f833cd9ff170fbf1baa476e3d", - "0x817faf7f5bc035d2c3684ea95ebf27a7c3048e85f0595d4cf8318192b532d2d0", - "0xdb867eba31c6f0b191784bb7ee4858d2db1bf3fec19dedbd08e5fbdd4e1b3559", - "0x4c28527c58ae5e6f29102bc5711d39e25af511bbcb02d9a87f52f76eed517f07", - "0xc0b8bfa43b9544a635ca96d3fafd9747d24680c81af43234dd30b65fb304322d", - "0xe26c80fdcca6e61411fff3991dbc44c4f3725aae057a81770fd8def73f0ce024", - "0x5f044013409beced775746d721dd78b45591e20d3d85dd6c9361aaf47a0ceb65", - "0x71e3284644d22af77474a93e268d0dd265c0c42d94c4ad9fd62430461cf3efb9", - "0xe2487c02ef5a9cd04fcc5544243fb7dfdf90b559bcc8277f7ad6d892753df2f3", - "0x09808d08c0e358c29603f83d201938377a45b543816b079f889c1fbbcb4847f8", - "0xb5f97d9ea4e92b9884d70bd1b0dc301b33690e64f648c770d4f9fc16e3e6fbc7", - "0xe7fdb278d471461a7adf3496eac82bf323a0faf62b2a4d634f56468ede626322", - "0xa9f79b0f2e8e7b8ec5224773f1cb2762078430d8b131a6f22cf7973252212e72", - "0x5cf0e6a30df29b2ad982b1db7283cc4c671488a43602b0d768177af61684124e", - "0x11fd4ff8d42811d05bbde1bb0769cf4cbcd03b6c99048aeee9bd1b608c3927c3", - "0x92b6d6e27443525cc63111623c69b46c3bbfbd9e528d876a6de043d7f82b3a50", - "0xd1a398f4b33acc132e854b49eb51773abf1846cde2dbacff9c4527b466bc8a26", - "0x95628b62aa0fa59b2b49fc6fc7e6fdcc416b59ee5229edd425b1a607e9998153", - "0x82f479a7ab8e6db85296f8cd3df13279ee899e1bd8df95b357ded43d4baffd6a", - "0xd193576f1bd139a319e1493c075e33f317a05308b93ff3ecaf5249c20e025755", - "0x7ad318979fbca17b06a968c11be03713d3d46c8287477723a0c97374f5ce4986", - "0xdd52a80097dbabfb0979368e56fb93087cf9aa1e5e3047faaf6ba475c5d3adfd", - "0x3f54e8d5b1ea24ef7fd8e5df54ab472b1611925df5c8431336ad647687d942e9", - "0x13602b947455a57cec85f485f3b06bef3268ef99f5d0d80ec3278948354dbc78", - "0xb6d186e6caa9662af33cb661605d18cdd96d54a62fddf40a8b59957c95794009", - "0xf26e692ac554a77ca5f56aa4f49dcf03a7b9706f6814f0d8e09007f81d8a79ca", - "0x31fbcf8df989b08262cd60f604edf8149974245f020acdedb10d274f59bf77d2", - "0xcb796442847e9eae7bd397c45394fca256f885c921d9757c3eb3ac6a7c4d21d4", - "0x29643bac94a2c041e6bac56bec1cc46acb340ed5ab34606b2fe088a804a3459e", - "0x4204834ffbd39aefc2a735c2f9ddd312104481866d1e7cf169eaa1d95528085c", - "0x86de4f25fa0c49fd019141a1386cc52e296651684d13679f4f67a814155eb215", - "0xcf32d0cf2c694df53580cfdbec9a6b54eb8e83c7403d3359c61ce85eb5418ce5", - "0xe081edea49229b8f7b276b2e3bdfd2b8ad12afbd071997a0e5443949d1f5fd7b", - "0xd43f8f081c96f2639add57b7d7a112106de124b7d9c504f792cd422358aeb7c5", - "0xec11b96266eb477f0b998ced2832efb936ca3ebc1da4146342627d9657f9d31a", - "0x89927e87d7b5522802f654da80dc70b8a14a3db83a8ba1c0e162d5bdd6327a00", - "0x052526cc801a21f0bc0b145d45be93d500f48127a6fc1970bf10ee2d5aac34ce", - "0x07f53db511f45a428f6508c51fc608730a53e1ab0d425c8b44196507c3288f76", - "0x380ff3fc61929d4297b598c725a1bfdb651e878ef0e26049648a622c126a19da", - "0xeb9b3e7bfc792d6ab85d00d45fee76cc84f81692d5554bee8f1790f027bc615b", - "0x10ef177a4d3610f9105e6b35318f2d12df4f7c248e639999961feda96a5f2f30", - "0xedb17228c2bde8e124c0fa9dd99e9af6bdb920b30e71ce55c3aefe30740ff377", - "0xcc110bc17f21ae92054bf94b3109efa6df3240fd54d7a22a87492590e5fa7c21", - "0xd946e901eed5e8f0ab584258f78e76436f6aeb5c16f49d0bdd5c7b1ecaaf62ea", - "0xc7fa65c5e8c48d4ea354f3900623950a0f6a319572381624258b5cf6b757a4af", - "0x262f129de130f709b4c98a31c208c2edf21b10d005507762dafdadf0195f61ab", - "0xb71ac11bffc2f2085699142bf75c7723624454cc3f3f44390e21c50a43918ed7", - "0xd0fc736551e28fef1816584f4e80fbff2b92dbbef3c6b33de9fff624c9f30050", - "0x423f396ef11a9100a30ad47e0953efad3014797c97b468d68e9da1cbff620ed5", - "0x819259e80f60bbd32cbab7027ff4ec134778f9254bd5c12020dc743b31a4ea49", - "0xe3306484604c7e73f844720524303484126db195e3bfa429feb6189a1685b018", - "0xdf1cc368134b50f3a2cfd8660e1fc8cc27e12f58c979e0416b391c140baea6c1", - "0xbda9fee3730f730df0ffa9c0051ce991d88e9b97106de6429d125b0b477d61ec", - "0x3d2fa3a51f1cc993baf397ae89fd9153e7842ff613e7b96b5e572b5a3558394d", - "0x3ba82a36da3d9732daa74bf593207b49a071cc4db8a1327ae0d5ef9534181697", - "0x19b4b261ddc43476412c8b8c537d9e26014c6c632ba98466003591cc947a5805", - "0x568f44291c13efc908db42d2473bc91ebb16e062e9b4368bcb770a3033d67741", - "0xe5ecad510448855ff0aafb92a8c7aa54aca0fb390bec3c14ad5d2ba200380aec", - "0xa40aa7655c1458b76c04ea5934ae171fb01c72c8c375bb61a0c27b0ebd70f21f", - "0x770ad5107ac47fd983979b016553ca8f340a13e2647b9140e65c980dcf349cff", - "0xfd074cacab08d2d5f113669672632ab0e94e03bfbbe17fc3de27a30e9f0e8327", - "0x2063bf5ce8eed483242157113534b314296deb123cb33083cb0cce04db610c9a", - "0x9dfb8274e842fd3cb7d73506e64adacc39eb12ebd8a972b7101385ac4eb5b12c", - "0x8294c3d4892cb0bcba9acdd31d53af5bfc97daf124f08d903297da8bbb28cdfe", - "0x9dc2d04fe197009f24ebd4a0c95a0b91d4ff0093387b61569af993fa91edb3fa", - "0xe88e256abdc6447b1f141c2fcf2c46721d9717a9fcb302513c93ed6e31b7c11c", - "0xd2ad4ca6091f6ae6c323b6565522c208a839e36958a1675e5e9b48b13068e7c8", - "0xb168169f2643b8cdbec58f3f94d7889af37117a35389726563ac770f5205faab", - "0x09f32b423ca728b8fd04f9872325c0e2a4c9b1eb36fab49b31ae51254c69ebab", - "0x0b47f8f28a3242c9ca705fd11949e648f190053de023366f98e6c736eb2f0de1", - "0x870a8067623b35b5f0146ec95047869357617fe3773a8285a622c18d44963f9f", - "0xd14e05d8f3238294ffd8885c1f1710f64f2d8eb3d3b4e8a921f7f5c94a0a6288", - "0xf9cdef6ffacbea7692e9fef3de1b778bd11399492486dca0209b27bd474eb04a", - "0x5dc41201eaa00957d070c68bb289fc00eb31b7e5fff52f0603d52d27a40abf81", - "0xae3a53054ff4e3e5562795b45777e1e3e82abeed84e2fab748af50aace8bc8e2", - "0x93fd6f9af3b1efd27487477edccd690b19902300a4eebd34cb0d9b2e60b3cccf", - "0xbe475a9dc045e70ac8218e5a65450edf026d70f3f814162251f83a88098245c2", - "0x20566435d247cc1e7d0d81e143c01b6649fa1fd3cd5a9f97738e6045b29460c5", - "0xb37273228fe8b0c1679bb2dab7176ee1282400daddaaeb0db415c716f5dd1f71", - "0x494bb2cea59fc43629b0f8be3fba9ee5b52948c72fd1f1c94d31600756bfb675", - "0xc690eb3d8e65d5efc211def8fe8f198e42c0321000b8998a6ab218d897d593a5", - "0x26821d73690b9d1bdd9594c92b086291474cd365bd41f7de5edc49dfc29c26df", - "0x026a57937338c9e168d254f99dc9dbe3fb41508049ac1a5b6d7df868e2935e6c", - "0x50dc451812556bd69244f3728c6a17e958b2fa125248103942ac94142610bcc7", - "0x0ffe7ea32c3ef43049d8041a16bd3381ba89b1439dc5329e839546aa01a64726", - "0xab1e06f13dbaed341fc9e33ef387ebbe187787ccd8961a47560f7e02617a06db", - "0x8b72166b0d16ebfd9c3156f5972e3778e16b3d7edf3decd4b6efbc779990ca3c", - "0x149de0dcdbe03bf6d0ae9ccc989a074a4cf2e78311ce8cb6dee3856c2a374caa", - "0x42147f5960814dc31cc38b692089e6d1d53b85cc85a69e4cc01954dc61c268a6", - "0x1460ceccff1ac00f3e126383c3102c424b75c2abeb973d0baa8bec8e7bdda692", - "0xdbbbf28a5d25c832c5f625868dea79a481dce880d5145f4119d3e9eb32356a6b", - "0xd8e5b32a88bdb502fbd62888b9906a0c7955ccaaad1ead31636572a07fe7419b", - "0xdbc42abbec7d223666da11767d9862cd1458f91b5bf2f6499da4ae5bff281888", - "0xc3138499753f545de139238bdc8cd4cbad254c6b87bf0eb37dc1fe7a85168e79", - "0x42be700f627bb081543ab289fcd01235bedea5e33a9779d6d14744685f5a652a", - "0x8660e4130c98f6a8024c1b587dd24ebe402126d4d9a2adf610c62bf033b749c5", - "0xdab926bab8c4ff9e492f992b5696e8f7e3ba3ce77e49fe0e2ca36d3efc8791b6", - "0x0e3911bcf379fd57014bee6a1f796b884c62f2392cf6e8ac5a145126e4622b88", - "0xbd52346714664eeb7b4034bc6a54804824bdd40d9e5bdbae9375a302926babe6", - "0x641e58eaeb404f7a40b9c4be686c26721563b310f7624a654185a60374c1512e", - "0xfa07b0f7b970413ae5085f042224861bb1bc5f2f6ad72a44c3681d47a817c0a9", - "0x70cf3fffa7e27f03d5cbb1664bbd0e1315dfc43df76f1b0e3dce494255e54752", - "0x1d5b0118939994aa8be89dc365f909309df84e7c92530aef25d62118b09e0572", - "0x8b34c5a5cd5a42a58bbc60166f3f2a4be7d79057691c708e7a9639c21b6f57c7", - "0x7b149005e7f8378189c70c33c073d897cb07f62ba5ae319088b3be204beea8e4", - "0xf2a41e8feb5d294faca47dd1f7a90f66b7b616addcd179ae60c5f173cd44a85d", - "0xa2875c8e5a226f2cf69f07e19b13a628ef6a8662f3a5de4c9d666ca5ebddb001", - "0x4231ca8bdccd59526900a487608912a4fc0918a6c693250bd67787bc61a9d09b", - "0x2b06301a6372e0fca0da8a15d4e31a08522ecb91efbda58c3e495e2ab7f4fceb", - "0xd0e19f62b07275097b56e7f409cd52826a1dbc0be26eeaae20562420559153dc", - "0xa4ecf1ef04bcc95ba0ed9214e9118129ae5497c37db94dda284dd536b4dd75c9", - "0x3dc87ba482aaac6b59678c899bfb1da9b768e1385e0f8e8574852efeee2db8c0", - "0x07f8df3f6ec63d926c3a76b17ad1750a9ac6c62a8cdffa549440e651f6087178", - "0x96952f8ba7c83d640119868a030cb7397624ef322b2d153aa3843259da5f468c", - "0x08eca2fb4295f069f3fa62849b354a9e10b8a8e9b771cb8d2790b28b7b09d70c", - "0xfd5832f531c8819c0ce9d031434c33eb442578e81cf711e8ba23e9b21d096e13", - "0xfee9a8bae124565c963b65fa47dcf7f0c9b5ef7e778c2d99539b8a04efd04522", - "0xead692739a0723a73dd38b3aa02f93d72b3b6922c2520ea0b74981eaafe7f6a1", - "0x890dd8befeee6c7c7b4942b02a3bf1a9d1b9e1140ecd1c07de570f38b6c323b6", - "0x690343489ed214b9195db6181c42237b7a62fc935a18ca1a5dc883a2045ceae7", - "0x4bdfdc1bef8c19b89344230c0f48841abfeea788a4973c34afff4d2039ce4417", - "0x6ef6a94338d794400f95f8c0b6d5a40df8b56c31e9bd0037bdd7e41234bb32a1", - "0x00b7568bfdf2d4780b8650a49f707752a12fe9a88302622e68f264622877acdc", - "0x83e96e0b864df30b53ae08075acc83f785e9b8b2bc80e8976b1a954f995df012", - "0x664305b610d0b3167dd25210acba7eea291a37a224546a6f6d59aa7b71d16e5f", - "0x3d3a6401ab83eabc2af15a23b9c0e6d123f661b3599c6131d5923a106f562ac8", - "0x7a11833fbc8ce3d9883088649b930238625e6f6136dfa06a00daf8e646cd020c", - "0x112c33932097c4ec4e18f6308b2a6502cbf746605f1e404555dd07703d60ba2d", - "0x9e77cd028749d69caf352a95f75dee842c0d9323e3618afa2927b2aaaf7fc4a8", - "0x7fe0a62bfd04e263b4c67c5808f6231ad6faca7ff0f5bc0c02177034608d6991", - "0xea820d62b423b68da2f8ce345dbb56e2205fdfae1fce261d64acc959fb30cccd", - "0xd8f6367448dec55e8e2e26b5d304d92969abde131d402de313b1705c0bfd528f", - "0x4b97f3b4e0bff7cfe322e4f50176f6bfb379a706d42308ae75b2a46735762a95", - "0x7425ff9cea5e4869332ce12ef727fbcb004add827eb567424fd4c17717def390", - "0xc7dcd4fd005ae8d4d9e125727b54a64e900b3fee9cdc82fb5057c012178c8483", - "0x8ba88bfba201db1db716f762b5517369d3ae232ca045d2932fc27f6c47b15806", - "0xad2f63fa4fddd0cfc7f6cb01116d285e8aad4b0c51fa9445d39a4cb4949ed020", - "0x366874aec4ea26ece424f5402604ec7df9f40fc4cb9087cd3f45e764b1e36ebf", - "0x7f27eb75010b0d5da72794c129042511e24794b6c8491c1ff2e257dadcc7052d", - "0x27eadc596f6eaeb9247704c3339f7fe4e649f683fd815f9d71270caf5d9e38cd", - "0xba3f72ce8f45b1575554bd0c64feda3959c2a68f0300f021b67880b56f7152e2", - "0x50833c82dc63533f7ec918cd6d58ffbef4e2d597f354589b8eb66c9de0fc9697", - "0x30fc354a8893f683ef03bb024254574b550710f3c05496976cd39166a29e1c98", - "0x1b6b8d13fca6583d358b173c1a255c3db9efed8ad72084eadf10e29868a26fdc", - "0xb1b2a80122d1210160d8649d31422a1edc26b23e8499aa3a88b047dc52b42222", - "0x4e6c85a13cc0c739fbdf844a4f52c78979a84a86db728e4a5d2043ee3b2fcb3e", - "0xe98c28b49aa690aecfa43527acd3e5a010c8f90faf79ef5b4b479a715fe66a99", - "0x5b56e2b50ec96f7bda3f529ed71321650d688e2ee5c16723f6199256532ee660", - "0x1c9b8f5106a23096f2f57dfdb2a7a82876f4374be97b6a818fdc3c742b216b09", - "0xcb6973f775356ec7596ed6f9a036258e98677c7c5b2358da75af28f553327506", - "0x26b42a54252893f59e8eca923a0d2f2c0fe507a680e8907e2904702791c42aea", - "0x25fec26921bb5b6cd9657588b09813270cad72947abc6cb2726764db44181ff2", - "0xdfbd186df632b63f4cf74db660c59e658e0b4b725fe244a563c6698dd94adaf4", - "0xd1e00635e2399f6fa625f5c204e4c1643a7b619271394141433d26d902479bbe", - "0x3d3323fea45851eb0bc0b6c80a79cc0d0582abd8032aba957e719c31bb7901e6", - "0xe7d7b4c68d4f55ea8b71b43ad20bdd86402d36b6360ed0ca18082942736a7e41", - "0x1436749eca0a72b74bf8d596e3499414d412fbea0b936ee3564c4f83de907092", - "0xa828c16af52bd4adcb64928fada82a02d0a49fd3f53c5083ca9acfd998f8af1d", - "0x0fe559ad45cde755dd41876c5a65a690d32fc3d294fafeaa43e3fe133ae48da8", - "0x8f91d2082e5a1e6c1c905936515ab5f03889ac67261ef1d892fd909a9379a8ba", - "0x3881a9fe4ba6a24dcfa86351be2ca741dc9b2078409b2e7e0140569f19733505", - "0x4a3295525bfdda29bb9a552d8dc5c992649699f5694f96ff5bb0647910386db2", - "0x337389b3e800bae87fdbe5271f2167f169ffeb4710ecdcea30f08b2cefba57b1", - "0x2978e1e3c2b5dfe0b41ceb5c9c4029f42d346a2123a199c52ba6efdbf1d5fb68", - "0x8abbdb4f1f88fe8900afdfe15f3d03244435d4fb871606a689c11f6420145b45", - "0x986dd2ca80e46f37a604b7186ce5683d3e5e114384ed6ccc39102228fbdd0eaa", - "0x27fed9eeaab228907e106872d14473d621559176063f3c19c98393215ec87e02", - "0xb743e115e42ff9ea8240082a5fcc9f5878221ab742d0db8f1b0edc6317484f30", - "0xf6d2172dff821efc35df518767fbbf59eac9fcbbf6d89246bf845b588f5277ab", - "0x1c608dc9a9114a38c14d81a75c038efb591259dbecdf00a41f0bf402f4dbbd92", - "0x0ec49c3e2e0617fa336c1f9e36bcdf6ed25e548281abf1ae0286c5d92571752a", - "0xcdd7dd5d936b3b685352a9797b52419c6f9aeadf33e5dc6dd8b0ff35a85d2a35", - "0x9bc44252049c89b94967579c0ab737e14dfbdc95eb23d14579a73f3e68a81def", - "0xe67fffbb7a6dec1a39c46e838d72f6bf7c48023a2759f2c3e4340bcbdba057fe", - "0x55008d64590ccbf16e1288121b2a885d4852dda11cdf4bc21c578f85fc7eca70", - "0x0ab47947ffc76c87f5a4d8c5eed162bf0b56b0cac11e425b44f270868f22fd8b", - "0x7f5810f6e39bca9e268e1d0f2fe8b3b172f60a84e51e61f2b9056854c2dfbfd1", - "0xea68e4e38860b5fbb0f509266afd110d50b6d34c7bc083f772878716469f7202", - "0x203cddd6d6a1ed1174ad13faf518d08a11bca1ed4809bfd9d2e8947ff21664b7", - "0x00e1378e25cf45e210f0757da309b30328b3ba3a1d270a065bf797acfd696eaa", - "0x4327597fcde5e099327889cb075d1db345f5fc996888b058d3d17a0e49708e51", - "0x6ca6195a4887f9dd5112621a13fa67463bf945d7aea39578feb1a1107c88eb79", - "0x5ea64f4ead2b127c29cb51c79b3c28b81e345e2eecd237ef57fde7adb8702972", - "0xb2c8f6e30f4b70a2983b508502cb50e6150fdac3b6d60c8458f9bc3e9b0c1fdb", - "0xdcb684901513685c0bb7582aed9ee01b98297e05e9b054f3ad6917515e7a8620", - "0xab7ac631c98ebba065ca7d6d81b58e8cee0d1304b6828ed4699f07ff2d7b0f18", - "0x21f12ed6ecd4fcb2455ce8472f01ef78081fe3f3d4eee5a7edc7b546bec5e01d", - "0x0735e851626195dd9baacc806903c5a623eb3d3902ac2514db9b088292f6ce31", - "0xf8008dd2e9b354b284425bca8b378fd2081449c30cd6bdec3a8859b0021953c2", - "0x809f2803965be44e3dc219d279acd5b3bee6c25780cd498d15dae3325d8d568e", - "0x5fe269bcbeb9d2d2b253cdf4f0f2bf669c93be6cf428996b2137b68911ea0724", - "0x41e04646fcf4a232b331a15619978ecfb13e9095ef266edd812aa1793e1a8a54", - "0x0553fdd5e662e92852697a7f41679b1a82940b5d2acdea812b0925b7ea3892b9", - "0x6e77324bca3ecd57316541eeb110f007a06359dab94131d98c245c0a137015ce", - "0x217b4e8911ca232af5cf1673679137cd720e530dd704d11987eb8fbd612a8b16", - "0x42bb0c8c8d5a2c49dce7bc4ed6f97869fe1335d3ac3f8df89d4701ebe8a609e9", - "0x2cefcc4dfddaa49857629290b1b97af2ed03ef3b8e3fc67271315c6961038972", - "0x6db8fc507494b4b5b6d7c95e056fe15571cee06adf6e7d21dbe4c5d1787b9bd3", - "0xaf5c85ac940764c2e50ec0cf57c37febdbf1e4539dbccea24e7e0dc499e4b38a", - "0x063f930786584fec4dc4aca21922770de0547b455e437a7b0b245edb9d161005", - "0x94dcc780bc2c40ede15992b2ef6667965dd346bcf00cba42e718d8f83c68f325", - "0xbf5f4beb305c3cbe0ef9142f3f588804f0437c9696fd2190c35face26db36c6e", - "0xe8ab9b3503167c91116debe3ce48d0d76e23ed1c1cd12da09876e4ee518a39f2", - "0x1fae4431c7874fa5d2787a520b27b8a0826f095398d7ca1e94b234650c04ea98", - "0x7f5d261d5c29232b5ed9c55a26a59fedda566fd3f6bfbaff11d63c4ff161f5fd", - "0x763d7abaa36d8262b659783a6a310e965a10b362e9bb7d907bef107a70f14e50", - "0x0dc66388e318c83fa3cfdd1136b621323e9d80cc2b97a84029bffd53eba93cf1", - "0xb422530c15aa6e8b4fa35535040e4b7b64de68a41937a439592be5d9afd4f699", - "0x3fe4f704e67fd2d9ac629ce20c622f1634deae9fbe0a42b02da21c4f119250b8", - "0xdd6ce24b6084576c646a2d8e42aeee919ecd2b2842964812c31c6c18adbe8676", - "0xad0b9133894fbeef5ca4562aff4caae74218922542f6b20f73f99a4e5f30096c", - "0x751d56d8e50e1f6b1473ceeed9ddf34030f0b203c9e705a8703a4988d1b0a3aa", - "0x777800d29fb6fd9f7fbab607b4d9852f50f2aa3bd52b7383f7cdc287771181bb", - "0xa34077e2a2e59e10649fe850757e3db69cf1fe0be2cd34fd4ac1a2793668f0d5", - "0xea7d03f2cbf31f66327d8dcc7eab05c9f7fe98517f3d88cedc1c590742829106", - "0x9c834993c9a684c8c726866c0a0013c3a92c275002137fa0288368268f513c8b", - "0x30134d868fdd3c9d3e17c19c2813605dd3d5fdc9867baac66312b3089f1931d6", - "0x35c44b2ab3def27f323b69a679c1536d8c4ca8e8c5dd33fb4db1c28214db48ea", - "0x4da76c0233f32f66d1117d436aab4e73a7c7d3675aa2fa0abbef5498973fbbca", - "0x02aa3319e1c7710604fc26a1622651dc319b5119a68ee4e887aba1b66f85e4a3", - "0x642722b31dd1cf026be151a590ae81c8886812d858159c1ea9c40fb93b88e73d", - "0x84942ffdfe4af44680244f7810239ce734981f2c9b6a42a951261a3955bd31fe", - "0x4bccaf057f4fc0914ff58b56f4ffefd096ee71c508d4e94f70d30f59a3c8ef07", - "0xfbb306bf85220f2a93533747e5d081281d323419adc3458327be9f50ce700dfe", - "0xd3ebe4dca06c2005fd62be1448f92d0bc8cbddec22564e3adadd11e4001432ee", - "0x4537177c35266327008bf899944a740739fce625930df5b89fec5cd43401c665", - "0x3346cfb4b3b5f6a2c34b478f823d5a5cea08bcd026d1dc1f8cf885064d6a696a", - "0x6d7d217e6f196549a651bb51500e693299a5838e23651902e54a36aa26d3cecb", - "0x03fc17e1d0df60cb54874c848627ea0a86fdd8eba989faf64fe5ec3ece8293d1", - "0x3a671486102a9dc591f67b937106a1dcee58680062a328f2fadf8fecd8fffd7d", - "0xdfa484a44d55c86e2c3b05429144bbb3e6758e31e12283226eee25d58fce1e04", - "0xe01ef4307108875450a351b5df82c5fea4a3d34a662cc87f7071cdabc7e5e4b6", - "0xd04e6b7fca513c797b05b49ca345a2102243b6e893407f45247b0b406fa7cc6b", - "0xc54725a8216701640e068f8a8ef8e77db86c7e30cf4add44f1fd125a6ac4dc11", - "0x10851f13c23fed1edd5434deeb6a07d466d922f1516a282c23ce7834faeaaf62", - "0xb4c3613b7c51412a171f1a2b36ffc01801abe4594b05ecc5eba6026139892f0f", - "0xccfa7a95e0a7eabc97d234da53c80e94b811f270bebb6f6ac8759657e3750e3a", - "0x3adf3ad22a66a6205a76c91d488b8f315c12df4955c52605724082d1f3b93da1", - "0x9cd530ef2e1e14f8003b8d8651802bd1b0a4dceba53dcedb93a115ce4ee127a5", - "0x803bf4629682228ddb883c5a81bce39a234768de6c299f65a07a3ee5895087e9", - "0x8b6af66caee2b87a3fd2643c72e8d84ec9c0c75fa271456c48cbde2774b2b134", - "0xc361af073d0a9044e2e28cace7fa6dbbcf7c05e41cc372ae13c2c5e3b1d42ea8", - "0xaf73a3936f76a401a3a137bb6479978acbcaff7b8b0f6758a72a0c772fc2ca1c", - "0x53259e3f0ed789a4397fb6897f768d526de648c4cfe50e4b88a95ea93232e404", - "0x2145a286266f525fcaefe549221f470a5b8472db76d35e820c62e9dd09f41ef3", - "0x9834dc8c6a845e685219a66157f77809c2dc52dbebdbf36a7a768aa4d2a89edb", - "0x300e7319a340f894b96281edb2697fe70c650d5e1a112760f22f10deb32ce547", - "0x0465a059e6b40cc6ac10a2bc78b6d064d6765c0adb0c20a45290e65560bd1e1b", - "0xe935237f4f93f5de4d7b768627ce849aae62d8a5d807e0398f142677165fc3ba", - "0x943f9abefc54fe4f118401b9682c44c9d7d97d6cf03ddf38d32cb694f9af1c32", - "0xf658af875a69361211c9894ff4a77e595026d4e815b3c18e9cb83774790a5cca", - "0xb5ace322f7b347062e8b728b312ebcddf6bb95dc24842c6ab783430cbd5e52a8", - "0x8ac46515d61b24567ee93b19a3bb407a14016ee4b3b99b954c74923504e5baf9", - "0xb7c2a9de470c3a43b7618fa31d4cc0b9ac879a2168ef6ed1348fc66c486340cd", - "0x9eb593e57bc9784336500e4a593200274abd0e9da1c8a16b39acdee94a0be676", - "0x361e998b3e24f213348db959f4cb65a04ffd1de7a705047fc10733034fbe1c66", - "0x47b2b5d68a3080d6f11ada3bf0af1d90759f8f08421151f6a2ed5af64974f262", - "0x002fe24eae8abd356bf631051897ac8ae1a754f7c5bdbd5fc521bcd364fdfd96", - "0xff53d1ae5e5902c1a1b66bc36f88f3264bb01f14dd84def0ea0f94c7c7eadec3", - "0x93813bf1b6a660f87410f862123d1ee694eedf4e7464a1a9e4556e3e3e2f9a18", - "0x077cf660cd2cc0f4c4cda9cec68d8dda36d7c95d04cdace3b70c8d010441a68e", - "0x367029ca5dc7cf420efed09c07561e9981b0dbd8dd1b1126ebb829cec9b5bafc", - "0xa111113ef2ef722bd548b5adf4d75ddea2a7530d4d843b022c2a54ec25ba8c41", - "0xfb2337d6125ede563770a84191a6f158ff45a00ad5006153765bbb72f51f6e83", - "0x1ede4c16ea063d9ab7e7962957b4c067b0fe25c2403deffa5b38af79c563aae9", - "0x067bc5f7409eac44326e4adced1eb688e3d9834310de73f141e90c4c63342dc9", - "0x909d8a9ea7bd84097eb6129b8609773b90296d9d01729f592b4bc50981d9f1f1", - "0x11f09438cd48f7895d90a6bb2dc9fdc342b8f11074d988b272f2c95b76f99fbf", - "0x7ed2111c775c418ac81e55886fdf8fefab1f639e243f47f2c5526ac85b3309fc", - "0x376919526e0fff65b9655bc34020d9df4c91300c0747b225ba9ac5e9adbf97c8", - "0xf6ae43cfd33c14899af2575283288239210f3c00de11364175c3a1556637830d", - "0x544063d4d8cc6b471b354b0e57773d841052ecc0110870b37f6e8657d4256381", - "0xc1f6d5b592da819a2ebaea65a0a6448f63ce09fdfcf1c15f93fa20fb8b67e074", - "0xc337d4acdae3bbbe7275c21b07b04b2ccf72a87b5666ee002ed54b7f56c6a276", - "0x8956d8aed9c2ae1b76b1aeec8210cd24cd900ad5fb14cf0e86e86ba35f5b04f6", - "0x06219a3301edd50166ff67184586c2caf2b3553b73e863cddd7a3c35b7f88022", - "0x52b99f92dd3ffa353c0b54466fea2bd1876b3af2b64953c12cbbabc266197e04", - "0x5c69f7312b8786144aa2161b3c965cbef42cfd41c8b586d117336ae8717b65f5", - "0xe57747520a141a1be0140ce4b45e2973479ee8f4a50a85162dcdcf5090380528", - "0x1f73420c464ceb6bd25e2eadff6a248ba092dbbeb5b3046ee809567dff1f34cc", - "0x1df7ccc998af1e233e61adeef0f757caa4a80270eb6d5be5e920d6b085a4cfc7", - "0x514024457e175885e55034f5c28c666611ce6bc1b8be8c3f253028f24091672e", - "0x7574d79098c1aa62802b6ae31f43b47fc5e17ff1cb3b79b7065ec42dc1ee402f", - "0x11329c45c6c4691bcd77cd97b9ecd27fc9108d909877abbc5ce894f081e0a4cc", - "0x098a826b2a276c2cbf3c67b88caaedbb62316e37a570f5bdac257f9b901d355b", - "0x56b14ea493e2280d63db496606a64f44646e2565e52ef36590bfa40e9a5dba63", - "0x16b83703c9ffd53c3deaffab57d5d3c7252cebeec4a0b1ae957c17e56613a19b", - "0xfc004311f603989357c1bffde7a42a59227d0ae6ce052ef010f8c9f4c31926d3", - "0x1da81ae9ecb576ef9d74b05d15364e904cb252a54403fb2427eef5b49c8655fd", - "0x2260c1c27d3bac445984b282a3432361d68f4b2f61165cb330854d3b8507c4ce", - "0x0225d0c83107075de447a805d0892a8affec5e24b8229d79f327021a0848397c", - "0xf1fb6333242f439dc53aff6bc68cd267f21f77fbe805b824710412b2f45b5468", - "0x9fd2c82902d6890fad571d4125c02a1308dcc05ec53ace2b3a0c7d3cc46d16c1", - "0x7b9248e0a83b6cc1e47e4828ad33af3b37cbb586295727d9b19fdf2ef62ec4ba", - "0x352a0c2a06c5036b590a640c7d080a248dc1ddc1dbae67a5cf6698108b75955b", - "0x6737bb1c9b05f740e28f1813f57a8f9d18ebfd042f260edef963fafc553994ca", - "0x40d02212c95bfac0971766e71a339890ce86cfa96e2bd0ef0b270976062c5344", - "0xa493f9c86347f4b0c5d0d29232afdd5681e7b0582575ca88574a2e04d3dfd061", - "0xb748487ec35f299c2527260495fc2155ee354c488040f8b8de7aab0c232f6a5b", - "0x76ba5a1f7c4d314bc773e241dcd15e0237de6d5d1a7f990d1d750da9e4bfe2ea", - "0xcd8a379b7dff8c0732bfd151fb4c77b631e7d018b513cd34a2c7e1be3ed5b10f", - "0x29d471c421d1624ffe4093894d79aa57e818dcf4042cf58a10fe244f1d2b43b3", - "0x50a0d79e38620fb0f28dced7689486bc9790389041fc6b2731be8908c8f0027b", - "0x1587ad177b7eef5a475045374b69ab13a38101a92fa9bf3c2d7b686b5058b0f9", - "0xd79d13f96b5a91cde57290a87678630ce212aa11974f4244e2468f2a245796d6", - "0x8f86548742ea82892829ced29ecf75947b42743e25c4c0b485f66160e0a2c1fc", - "0x09650fb97435d4c7ae472f2b6cb3b9efcfde8f2c67e6c007c6c7b6f89529af16", - "0x60221758839c34e2841c76a443675df9934027e1d365c91136d9d7089fc8c373", - "0x4a524bd23dce7b76d06158ae7c8c8b973be2d8edafab5b6921fa1c2cba3cc4d9", - "0xd6e51f25afb4eb839766ffde785a598185dd1e34870538a4a8b4eb9115a045fd", - "0x84d6430ed5924bbc86da700f650536f3474f093bc9f61e52ee4f7f74a207464b", - "0x9d2cb8b8ba17dfb9fb7562f902a43219c977ffd38697b990bb11b3708ff3718a", - "0x89909eb848d74ee94b1b73ea6b1af8058ff771491ad131d3e13e2d95c2115903", - "0x118b3ae7ad25ab96fe8a63973312c758d4ce9ecd39cc24913c26a65b4b5534de", - "0x19cd088af8dbe2d3e6ca7987d9ee1564ea2256f482840b1d2f0da85060de9a86", - "0x98bc07422cf8b0c4d1428afb759300d9a7637de2518528d34f7d237be7e863be", - "0x0c64526b393066911c7da3f17f9e652cfa38112ae324e3c84416e811d3fe7cad", - "0xa30cbfaf518996ba776b426a7068faad4ee49775db45565ebd327f9c679a45b4", - "0xd3e1a807f5940ee1a321b20b7931bef90515132ea9959df94e55529e05802cab", - "0x338aef579d9ec8acc1a0411c1674bcf213d03aa7d4bbc56707e081829ce30004", - "0x68c7a603089a220273f019001a39bfa9194590a6fa6d8ba960ddf4888b105a6b", - "0xd6a9d2c354e1dd77322800d24774eb03b589dd94bcdb3cc2b70437ed70411e6a", - "0x39c017c42ad571564792bf5741b3ae786ff0c24ebcb5ee46882ce0545b8a2262", - "0xf5caea6b23f4085c9f94c880d89b1c23eb69c03dc098b426143ae4b28969a2d8", - "0x959eebc05ff0dfae8c7e6699f069b38b5f2e5bc8c155bc35fc7f578d2d112993", - "0x199e90557d4d9e13c3e7a4b5b4ef6fe52cd2c724c36eaa44b7fa151efebbbeee", - "0x0bfe35d253227696e76f92ff13e4c545c57fca51186a16f687e76d2e6707d34f", - "0xadf8b7678f98b0e5009130d9d5d77add6e460b67b0529abc5315c44dadea0cb4", - "0x86582f3a98b218939aefd7eea438ee278d1faaf41920e8c72922c46fd56f1c32", - "0x001b728a4737fdd53cde20341fa0adec9aa8ab7c7c1db244fbd509a6c4f3f364", - "0x9207900bfdd6c87e2ba8498c0372706799604a207930eeb331580459d17f89cb", - "0xd2192fcf74cad70e6f7986a0b088de8658a14638a4c03d7ae616a88ceea00ba2", - "0xbf6f6b91742eebe70204eb7a70196ef636fc2db4d4c4f89cd5826fbf990a945d", - "0x5c1210951949402fe3b012577f1af0d3e285a0b39c3fe19c84f7930e003c06de", - "0xa16d57f777f94f032f7f2f75b2e25ebd11559effee98b39a5a1e7cc804cfbf06", - "0x1b9561fb8035ec6955454d6710f053d7ad3d8e0753aaac568ac3bc98f874465e", - "0x1a622da786425e0b65b9083a451a419c75e16908fa04d89ddc2c11d94ffe65a0", - "0xbfcb9b1d847eb40b6808e45bf3d2fb8f6588d6f103167be65f246d0733afd1f7", - "0x2317640589ab9d52e7f5e8dda95ff3b1beceacc5832341e9053b71209bfad07f", - "0xa10611b829dbb533e565ad01632b26b1fd642a4393e7fdd9b8f235f11bb606c8", - "0xe4ea4173982f342e396356b0bb0eb47e6748461ecce0f34dbe8fc084cf6a9fbc", - "0xd58399c0d0ae878338d2915eaf2d65f2d1e29eb8d551d254e68bce7a8235adcb", - "0xc0c8b73ffc675a207f73903c49d81131e6831e4c8e071b988ed9f2a5d2277024", - "0xe1ca77bafa66bb055c671978b3d1bd5df32f8e269330507f071afd627012b6af", - "0x67a1093cbddf41264009d1dbbc33fbc25a337339be0727e70c512b585897749c", - "0x0fd615782db5cf4c0a3686721cbc6245696c1bc9b403a9eaffae00968d2c8ece", - "0xc3c2dcaee8954ca86d9b3e4e98c4bb4f6b6bc183f9eb062016c5a25b2280717b", - "0x70265915f5ed94d589afafa3d0d0eab6310195cc690aa82bd65e4a488b398c58", - "0xde3fead8ce29c04a86dcb081da2cabda5366d28de5ebfe2f8064780413f71edf", - "0x0ae43394fcf6ebdabdcc1ede314fe779eb61a12eab807a9d3437d9167e2247e3", - "0x3241127e2c7fbb3db9fe0c602b0c94e22c684b7910ffcf09c1c443e567f95e4d", - "0xcb94ba286eeaa1129b490bb5891e603fd35e85c97c2132c1216d1774f9017f35", - "0xbf396cd23c29ef21fb535880ef621457fb71981f856f2a09c494cd005d38f981", - "0x3df0c90ed7aba260e820ca1d28ce1778149e163524e306309aa346bbadedcf2b", - "0x66a2724f7481aa3ab83a8f1bae2caef8a7bc607c7ffd5bbb1cf3766741db804f", - "0x7a0c3492f4022322e10d81bc10a5db9aceec81d1ab70cdffd31418b79d750fd2", - "0x96a826cb667924ed75ec708bf07cf4c7c05f84a0132e154b71eaf6e193590e87", - "0xcc7030fe617c318a31984d04e3d7f2ff2196894bc429f3f64bfd69b969dc9b56", - "0x3fa94aae223f5aeb593246f1a93d6d694b48946c09d879e791f1188a9dedc4c0", - "0xd49e51fc324fe58159575c0c24171f4eb1aaf58ed8e1311c3849538c8cec3ce1", - "0xc474123906eae5cd48a10e0d93fcbbd653f0c6d25275f827d4fac51e696c3d91", - "0xacd7b790a19026fa3f3b0a354878d4fcd79dc600f7ba5cedd2eefffe1ceda76c", - "0x41b311a188cd7ce1444d258dc379994b81a895226276c7bfe5e6cb29a5f92142", - "0xda0ae01db7f73f07fce46b94c24d6e400598378a6baf01123dd710c5425fb8c9", - "0xe62e08175a02b575e28b9e029a838c365e3ce4278f60c2d3b5d529768a4b47c4", - "0x99b804c8d0feda7f9df961527d634fb8b2d477a362e1d8158856885a13425fa9", - "0xe73597be8ef7d78f862c7a94a8ccff17f559816eba2f830821c6f6436898f9fb", - "0x36de9ee4c80853c865b16904cd8f6c0e9a99ff9e7bf05100bfbc76789cedd4d1", - "0xf480b762872373102393461ff3a21323a1df799c315fd167780a45d7bfaae84e", - "0x303b66babd21e72449cad413e04bdb0bc3ebcbb84a79dd30ed7c972c5341b82b", - "0xbf111684fbe44a973594f31cdee2c94e807bff9cf7584c22dcd609d8234f6e62", - "0x79b26cc3bbf49b6f25afbff7e97e4e45f2dcb359095fdbeb7fb7addee692afc3", - "0x2839d620cc140ba838ecba6e7e52db8cf7b5cd4cf4857f72f3bfbc9b1cf0fbd9", - "0x93074136f4eec367adcf27955d38efc0dc6da514693bfc97935c7871793e35ea", - "0x21f5af18a4cf0096b6e6a3d4c98f4043cfee5c4ee085ce106f86b713160144b8", - "0x90d16b403e2deca6cd5c80e52eba0b84b2875e1dfd75fffb1a2f82bc91eb6942", - "0x8a5cb6854c19a865f51e3ee9eaf8e843a97b272f6467634ba40e547a435ef624", - "0x9afe42a0dffca8ec063c83908fd6237d6130c9dfeab57078bdd02b6ac6d0ea07", - "0xa05cc6108b475d3e68e280e98f514cfb6df4f004e1b7708fcfd4528d346bea6b", - "0x71f10879b875caefab46669e8525b9c0487bbe3247e43a6cdb1dedbfb4d4ba33", - "0x7debdafd2b1410469fd76e2a39dcd1b8f400292f5359ecd39186a5be0f9e44b8", - "0x076f8741b668f3f715a7142f5c1592ffb58ca13e8612edc340619a1ca6ce3f2a", - "0xe312bb85cd945eb45f72868e6651633964f22ce3ffcbd0f3701bf57ac0c13edd", - "0xe6eae7aad7fed9c690a14eb7aa107c83a5e54736940ac6d82e251181e1103eae", - "0x794c8ade9063600442f83d4be14d7bbb191c692314253a2c62dcb33f3e7d28bd", - "0x6fd9db41dc1074a4349a6d3881757ad931bdf9ec261fc4e0a5ba9cfa5fafbf41", - "0x37557ead7100564651992a919949af3f39f3eacc03aa0ea158dd59abc4e2a93d", - "0x7a3b01cf210dfc42e4e41c95af1af357d15409fecae888abf4fa35149f44ed2e", - "0x89e952ed9afda8aa0ff80ceb90e5857dc7fc3cb6259ac6089da5fed49223cf42", - "0xf1ae8d3769142a1f3c8bc0ecf8454f05b09354a2ad5c921d0a6845417bc2f244", - "0xa04e022b2afd83a1e14d0d67595070b29f196dcfc05d92b2ce7c582d67af4a00", - "0xa3a0c85dfc3d8055a29de34492b3e231691bb6e9ca06684a205837ab9870c72e", - "0xee2529ad0af60748cc0bd8d97189d878643a52710dd200f79f27d8a4a4c8c093", - "0xac88fe07805a058f1341ed89795f0e5e9ace050de0742f863c1c9c63b17aed12", - "0x8fddc716ab43397541f6b22644949c6d7214b01ca8223310a34840df37c47746", - "0x3cbb4d1a2ea6fce170de64b1f4bbb8d9239fe191018b737e62eda77aeb7d0306", - "0x3807421ad84d6f2f9d5d830364a0455d6846e114b87cd08a3cb9a33e40c7c084", - "0x283199cbf0e1c997025bd36522619417d9e6184073b830fb22c3de106a68b598", - "0xcbc85a7ee2122abad27b1f5a07bd15d864af382a62fd552092b7feec4d2e4856", - "0x84586eae7108f2e1217d53be81f26bff7cc35409c64c1af827bb5263d813302f", - "0x5d2d93535aa7db5e2204e419ecf4d7c0647ce40522806bd24ecf348eb49a1f17", - "0x3a6118c84312f120c0d3e4ae4ec0b8103448da31953fa27a8400ed7e4875897e", - "0x4a0d1639274d31dc5d9eb49338b204e9cc9610913c0320e7da74a02ed2486fb6", - "0xa7770b7cf4f83c387344cff6ddd3441dd131333df0f210a57a271fc1a61f08f8", - "0x35066ce607eafc9bdd3386097a98c844bda683ce29faf25708b1a2f3e25c9aca", - "0x7068b1f5c017ff5b995f2c65db7daed3cd9fd0e658322ee2debbba7de3ebdd72", - "0xfbcf8cec0ed675d12ab4fab45754fd00a329d19ad77afe4413a0581bb6dab8dd", - "0x78035e412813b0a3f7e3f46bafe436b57b83278499fe8b8a1bdb3066f538d0b1", - "0x28bfbcd7eb38793a1ea711b6dd73c55da0faee3a9707c743737f0692dc6b56a1", - "0x9fb1607599329b61570a0daeb7b1a93210ac286e02a64ae309e1950cdd76c5e5", - "0x8903d98196291218a50fe03a5cd51592e1b458315491d735187cb39d0e21f397", - "0x97aa20d84821d36a44ce18eff501df95549c8266220ad612d0ba9d293d65271a", - "0x3245e35a2c6d46002dbf5bfedee31fd28a88409edbc55ee1ef7972a249b1cce2", - "0xd6149428168dbf78ab2f706f041a61e3074e3d18ed47bd343d54c3e4f2e051fe", - "0x9f4a04d0e3a2b8773a258bfcdee5242e44761aa1a1d2f9a7162f04a2b85de6c8", - "0x0162a3449c2be2a0a45a771027e5fb67169c5a56c4ef6f386093fa79f9942fce", - "0x5c1196a72f2c6fb5771e0390ef25499acd52c6aeeac3615ff17dcf2c405069ca", - "0x319097551b7df30bc67f5ee6f068cf3b34e71371b91635e9009af7acfe97e12d", - "0x8b89e2e091824d0c3276128543991cf7c35a82bed4b92b4e67aeb4652033a823", - "0x75d6dd431d5568625946b3578e68d47523518cb2e141369aee5273fb7cdc4013", - "0xc473b7b23c164759487af8c96865f0f3af3f3e40e9607665f94f937c3334f604", - "0x1f61783925fc03f3f5465f37aad2276c466ddb91a47db036f042c0b2edbbf643", - "0x3f6370976528cc31e4d099711329d5c670a2538623621b164d470fd7ee2cfb17", - "0x3705bd7548fcdeb0dbb3162394edb2adcd5128524eb837b71a04ff0c9c21c22b", - "0xe15e99e427efae16230af47e59ff89b76df25db44fa2dfe95d2419b6de35ee7a", - "0xbb4a17b5e4879b52e6a4838df9525ae01b168197db2910caea805011588559a7", - "0x60e6cc67da461bafef5c3b7e4f9e964a24c3146c4fc22c2e046301e457d1cdfc", - "0x72ef37234da6fe157e0e87b11528c3e41d115f10f1dcfce60e54d83c1406989a", - "0x63c3a73812427f73be785d4690a6ff81912d3ac17c8d76970ded284383ec97a2", - "0xf88c0457e00ec47c27ddc6d99fe6c0b82bf9249018a96b1c7e6b90e906adfaad", - "0x9258fa102e450844e091b58dfff2fd05b8fa099d53667626e340fda7ff7f9bd1", - "0x68adeaa1063528386db31407596442f815769731da8b560272ba3e1b8817a11a", - "0x222a41b6e681d2acf8a9fbd9310d34145a3c49160c29681b95c85176abadddf2", - "0x00745c4bb8cf5fb63700444f9258eb94e4b527ba8739fed4bbd7b13083854929", - "0xf27199044245d103b296bb81188982baa87175843b06424e0b5fe9dc6e005705", - "0xe00c3bfee8d71930faae9ca720f25f342ad166f438c07b0664c0c2d6b9f82c58", - "0x05e2bd6caee8a926c0f61bd0e5553b304656cf4749f54bb45b72d2a84fcf2378", - "0xcff8627a22e37d1087c3b858079a4f90a1fac65cd397dce26be8c3efe2515e1e", - "0x4885a5f8fb2e367a9edb32b02ad4390a4ef8f317a0e7d3bcfd5eccb9d8d05b1d", - "0x194638cb01699bfdb67b09ce5e2e8d5dbcfd403ae325a7a0311923ba5207c5b6", - "0x20e7c0ec1db7ca6adeeeb0c7950b7911e1627bb173425c4eaa105cfcc9c43db6", - "0x1010eb2f28d04da879c67f4c9e51fd480753081eac6123c5161502f3180764f0", - "0x09a0d0aac41a4e2fadb48c1751011fb46774f10ee263567d823e275aa0a33ec3", - "0xde190e4dde46d967a4e32ecfda15323c7f4081f40d5f245916055af44b008e81", - "0x21870402899f8c05003a175f2c0d37fdfb52c9bda3eea3ac8616c832fd8ac173", - "0xbec735492b66890aeb0ccb5611a84a908477269c2336e076c42cbb620cee8861", - "0xb9daeedd739766f82a95505a9e71daa503a5f0c3ee1a6a97f842b2ffb6ddc6ad", - "0x9f30d706fc86c7322a10a3c572df2ef4f2f49bcb49c5afc2c6299cf051e2e38f", - "0x9dc1d641fab155885db408328e60c06d97a0e3327b64a18d46e7ced7cacff446", - "0x252594a4da3cc4e794d954e88c15a2c847dd84d895eca06b9365a85a71fc46eb", - "0x979ea7682319cb0674066b10eb7919a87ad1ec0618ddb24723f13c55899cbb6c", - "0x570b4dce3b4613a8266688291f06bafa9d3b7828e3e6a9bfd27e7d2f3ba59125", - "0x39e97e3c20af2dcfa347ec193cae2c00100effd4f3987bfe21973db8c5e11fc5", - "0xf66b0859b41bf7dc53dd23762ecc593f4572a8a9f82b4c51257af1eda6344bcf", - "0xfc32a5b84537ed5947874eca72d189f74676855be5ded4d32eac2ebc0c3a3306", - "0xa89c577f12bedcc225cdfbf86ebe8112aef97b9c34a504dda52cea4f40f72be1", - "0x67e4b71c08fd93f1c37cc60826c5d8c18e14b89a7f271e0828b114de39aff694", - "0x342279789cd6a721f5f4866e3a16d19f88b24ac277563ec7b0716ecdf8dace3d", - "0x220a5babb290dc16440f4b01bc117c41af5073110b1375cae0317f9fd364d44c", - "0xbc1019eec10ff493e095915f66d46f0855b55aa681352774dc300d54147efb00", - "0xa9cc80ffc08c2b4c38fa178e3ac56ef982d0487b7139ed241fdda3894f4b67e2", - "0xb35267a4b6020a2e53e112f4ba7807ec50ba78a3b2d64fc3e3bfe918bb25bd9e", - "0xd5021c0c199dbb1b61e5e0d4bcd59fd064ddb6d6611421e57161f9c37fba91f0", - "0xea0af5bbcc015b06951c9ff3df3c233f4bdd6ef8eab97f0b3a9e10b01efd7a5a", - "0xc060f20f45851e6de2fe1dcb6d8996add5335c34892f84a1db711d0c47c5da4a", - "0xbd30199b078305b459565598fea6740a4d187e4813d3887f72d7958ebcc8da48", - "0xeafa07e96737a0e53ee7a09bc1b394ae4c1ec6c937901a8066b0cb6b6908aff5", - "0x68abed404e0a66ded23d152fc5544b14b0a4fe918a51b667f2dc827575c12136", - "0x27da4b5111565097dba6e6dade4f0275ea8234b50f391aa270bb3cfb361e6331", - "0x85126b95e3532b5045710f60d0aa3f6b0e801130722bc7d85a67479e0b1fba25", - "0x658afe5a3d3b0916372785f952d92069045b747fc67be6c8bce3d695a434d058", - "0x9017ad7725867d37402da5ef1727289abcdf9982a0c7dbd7fa6f175ef8ff5f90", - "0x892a34192371a1de6d4c2ceabeff5aa9e26c3f41e74260d305872f366bdb73d1", - "0x786a848c13f84c87ca554654f0ab35f7fea5014669d919595e087e6463c47253", - "0xf1235641dd0ac33ed19e39ee58e282f2e0dd8edc0949e0510f2c65469d85e1ef", - "0xa55273939373f7a4052d8bc36501fe17d9b571fd170041ada7b4385d49ecb5e6", - "0x79ba81c39c2e7dad9768cdbf754242dc2b5b74fde750c32ee153ab121a633406", - "0x74ef128355bdc842c1b109d4b1d3dc6f85a21a110b32fe1b4ce71cd282a20ba5", - "0xf0a05137693ab9dae7624cbf4abce1554d4921014cda61584d649c66e9eb09f0", - "0x5566a8d7495aae3cb42e66bcc683bb194f53894e3281812484bf36da583b7d9a", - "0xbe65f10ed3eb5e862850eb4e30c73172f5417634b087b1dece7df97dda2857c9", - "0x5ad40161397a2d0f53d71284d878e711b6d73930d2a79a789e4bf08223e9eeb9", - "0x1a88c4e12ba89abf67c8f8de223964e4f3d0ee8951b9a1851c9172b278b956e8", - "0xc1bae22721de65936975b20b01380c7707cb603622106a166c1472bdcaaaf94b", - "0x68106522840c17b0e93b0cacfdbb1d2c5b219cb90aa61a5d0b04a0291caa4b82", - "0x0e8a214b18b92fc025bd374a8d9859244b3855a7d3a364d1a0e092546a17b2b0", - "0x584294fe2fc330adfcbbaf251480f2c727da2d38ce54ffcc1b363adb9c0fdb0a", - "0xb86b9042acd8d6dd4eaa967b3dce6e4e49cbad95f4a7cfea95299da0cb3aecab", - "0x99d4f215dabbe6bcd894d4ce7ee6d16a4543c38d9ab19a4f3b933f37eaa7fa5f", - "0xd078bfda23ecf966b656aabf0bb69d27902d33773b2471145268426846a5a1b8", - "0x87234af13d71e681c987c38fcc3661889b406e298e3a1ffbd5115fe6fe6e65c8", - "0xb9f78fdabc8cd4fbb08fd1e1d00ed552c4bc7cba0672ce91c52deb2834a7b223", - "0x76704df0766b51c9327acf08ffd35fb9c3f041b73808094b0d4ae1f7e7d7d0b8", - "0xb5e4ec16c993e2ade6e951099512deb2f8fd07956e4d97dfc639a7eeef96d336", - "0xc9c919862f5a82a5381e7def2883049fbbb99f24be9902152323811e32db40bc", - "0x8df21edd784b8b776ffd603a6da87a6602e6424cb3905d977b909a7b10160abe", - "0xd5c524bf24c3c8f2e9cccd5aff20b96fbb8f2045f37ef1014eeea1b82787361f", - "0xb64756c799db5e1eca16416cb833605916a9bbbcb3c82a54def34f16e6fcfdaa", - "0xfac273bab2facbfc28e2c53c03a44e69ddadfbf57b08499bb43855abae456f1c", - "0x43f8990cdc4735bc47fd89cd23c9a99d5453885b1903d4b3185b6b0413735ff6", - "0x7420a69d819df5f4106fc9e315e8c125c8876a3a1cae8ef7198a921c121792ec", - "0x696861af19fa56f07d5e2eacffa3508cc3638b7acfe4789c002929dfdc47fc3a", - "0x86c80d2150fca01803a41a900e66b764ce82af2e0ec19fde53971afbb6390ead", - "0x9cf14af73b44b555ceec13119567723ae8e8af927a6f24846635ff5000acfec6", - "0x95b50e682e4b6b2a08606a62bce427682ce484c2056d891dc206fae9e062d3f0", - "0x789755e975f432a059cb5cbeb067c8f9dfd97b54d8d1a45a1e47592d9eb770d6", - "0x87b682b780890ad3d542c76548d7d5287fb244291d62619ed079340293a245d6", - "0x8c0155b066048d9e95f5eb1b6ecfcc179feb71ee0426301b03f778573eeee7a7", - "0x0fe45f09ef25d05e5c42751a35714af86a5d7a4fa235956edaa36dd0e5ea7533", - "0x3b5ee72615fcc04f48cec067ba2d1d9bc0e64c4cdf9d5bcf97c1a999ae940245", - "0xcf773973676c9b0e31a549d78a6bbc79826c471b6fed42078d9b35a08d1d28a0", - "0xd5ecadc4dba308c0d790adea1e118d6984716600091d341276311631acfbb267", - "0xf344c0cf6516f0fa6617e48076726aefbdaaf5a31f67ad8199bc3f6e426bf904", - "0x3f3d2d33f36ba9009e9a72f3f5bbcb5df5392a19fc7afc8d37823aaf52b03477", - "0x346a89411f090d559ff90e670bf0a385b1b09f117fc9ffa18b09d3b6d5d8e45c", - "0x5bc5689e2b4572b8ceea472cc7827e22cbfd018920beebf5c5b25f65f5cd5357", - "0xda418efcaa0076f77e4d2f0c57fc32fa67179a5631d9df52d56497113d0e87af", - "0x5a8050832e835202695129f6f384652827e61ea5f1be7ff300183201d8bd6b4d", - "0xd9f444c382da42c310bd2f05955187163ae7b224e5efd44ab95af332e197d374", - "0x9ef2c5bad361117eedbc2adcb72a2ef5eba4caf3a99a0cbb2a65a94d185e48ae", - "0x7e3e089bc46b00a4174d90003379c382ab5bd84d092b9c4db3189d2bdc24f00b", - "0x94f50fb12eed909d251fe69adb1a1f214776cb029d487360b55c3a2abb663d7e", - "0xd3e1f4244dea40d0741255db2dae72103e263390e0ccfdefcbb2da59ecc5ec9f", - "0x6808bf0cb7d4b677527de762b6db8ddf74a1b272349f34f44505912bd95d62f3", - "0xbf7672ac474b5b849bc086ff8455216f015c8fc7660436dee153522ef6991c04", - "0xe79d27a369cdd5455ddbc6bd9158cd1870aa895b3c3971d07f1555b95ed02ac3", - "0xfa9e20a36c11b0dfbf7e9c62872a6423f5460dfd18e447481461a41176678262", - "0xaafb6c407910341bedc82c0f260cdef75ce5653f644b93a465cb990247a32986", - "0x5058e655e0c179e6c20f48fbd08c2f34f9341f6c07972ff40f55bfabbc783b12", - "0x28d2e7c852de8602a764ff693b6881af18ddadd67fc7eff481f48ac20ebf32f6", - "0xf82e09e7916f61b5cfdc3dcf193bf9d535f2b33f93a06c90fbdc78b3aac6b7ef", - "0x626f3cca9e1a9e5e123e34485c8697c758ffc32213a727665065dd6abd2babe5", - "0xb7f1c07f673d903daa61dec649eb12286a7a0568ee36ecfb1023ec41427c8dd0", - "0x8d1d42bfe88dbe4c621cf68d380dc57e7768121a815546bb4aab29b7486da9ee", - "0x79835acd7266bce85978f481aa3c58f3bab9106d72892df8579e472dc95c6899", - "0x0911c9c804bbe9be0aebab6c92f5b71a893f72a9d0cd35a51b0e8cd19ab0c02a", - "0x7fd2eff10936d8d12fd9a1c6d27e77cbec4e48253465eb7e65876134ff60c8ec", - "0xc739ad4255415e2831c6996673f3d02dc79f6e6d6822f7dee23bff5b94833c3a", - "0x2559faafbae0852fe5a1c924f0e4f6ccdf4fd22f483148b3672a3e7b3692b669", - "0xca37f0aa3d375dbddc0b426c9564fe68f10b0a4cbbf1ab87f97b27b44878f2fb", - "0x00ba40205d1bd46ad5b5e73cd5b1f3418bd892586d5a4647ac9a6d158f15bd93", - "0xfa6d25c829299535e6b80af81a2416d10ed6903117e73c656b979a5f5abe3ee0", - "0xfd82d8944315cfb228a8fa416c18ff82cbd8869c3babbd3389dca6dd66797785", - "0xd8834cc29788cb40ec901725419df8c031a13e190756a6352696de870eaf4671", - "0xdf6843a52bf55e0f4404e7bdf144bb17d5c47a72ef9482e712090ac9730a7f52", - "0x4c2c562f835966c72985f7cca89a3b1a7b0d4cb04623dc96e337daa35a2f5925", - "0x49d2afd87e83a04059dbf3ef4e2598b8d0c495ab1cf91ed3004e16a608e910c2", - "0x5be64774739c001c239efae1ce9f2a5706cc6e3054ddf24b03c09358f2f4852f", - "0x678f789dc8c409653b36f4d2015338165d3bc6a73f2a77ebfe438676b8412d7a", - "0xf87c8fbf02d8e84cf72680e6b9a8b8be39fbae9f1eb1047c536d77535494a301", - "0xe2428b952d2c6d60d4925f56b3d8227cd6bc608da2c1b20264befd8b1ad89454", - "0x561a95eb50c663462bb8af3aab336bd745b0571746b10fefa791bc11be777763", - "0x6945f40e3499d2769ceecf499c701015d93fddb607720b18dbbd5a6a2aa46639", - "0x9c35b0367a2b82270d64f11c5299336b21b9f454077dcf7af3b2e434677a31b6", - "0x454dc6bb2443509381e478f1836cb36808e2ecd1a9944072056c292b710072f4", - "0x0c80566f34a46477592560a883a9c01fa393f7a2c9dbb28a54e46a5c017e8596", - "0xeff6a1255090509eccfdea2e591516886c91191f1f02eaae4808ac95009086fc", - "0x37cf60888e5ec75841e7f0533feef7200185a1c9f3253073216d83923c864829", - "0xb169ebb9e418809a96529835bc293782e4fc6310dba450afe3e95a7abdf7cc01", - "0xa3c8d5c71ce0477a247f56bfe95272ea07f0b7f10a8526b6e3ff9a8de8faa9ab", - "0x2bf18db4ee84bafbbabaf05d1d4d383c0d5fc91be6ae902334496996eb3a8e48", - "0x6c116f0d5809a2c28351a737ef3dbd1808685e1fd656e37df6b6e524aa82c918", - "0x21e2c8e019c687fdb360c9bdd4e3a5133488cd2e0365aee3b823120734aa6f27", - "0x20c9a1db9de894ab4f576265da25f391b32c0805c3da76fbfdd0aaf300f88a39", - "0x23ef1f43af87be7396449fc1f89d9766c59e8adf2660812293c65a27482ddb8e", - "0x04a82d3a4a5e7f2507688ecdcdc300d7fb97aa8be92a671d7d42c0b60fa4532b", - "0x99e204c42afd6d4040faad384517d99bd0e077b03310d32223234d2251d6a07c", - "0xe342c0c4295665b9e25773fc9998e18c460e723d0a14efdb59c19b27b9c7011b", - "0xb654b1b8ede0d54a605cda54b4635d2b3c2bb8efd01ebd416e52cc87b590d4f3", - "0x5daabc41eeb6de98336411a03ec0323995e81549941cf32b7e15c765d1b7b39e", - "0x5103fc7f0fc6df43fb081b580bb01476f2b1cbde73e4d0f9d1fa6d8427fae789", - "0xe2ecf5daba51d2f7b22106033fc43f956bd1db0c5ad02bd941bd3d2b96ca21c5", - "0xf152bce5c6d1efb7e22cde72d6b8ca37f556ffb686a13770c5fab46e04837c92", - "0x306007d8091caa5baaa78643307f5abf9a5f03996fc072a9016ba6b487b2017c", - "0xf57308d0c02c6b8e2416c070554c7e29911fa84ef4cf2d934e2322ca262e987c", - "0xb234fe7433d7fd71fe0c6dfc834e4bcbf84a261b95760a6c4eb67d222b9ff392", - "0x753059f3405f60da3aa7cd1aa0cbcfa4d5ef4f2a6ed34b853b2c5ab2181fd383", - "0x096c6630e821816d9f4bd83fbe0ccfd223282f34aae5a49f969ba30b98c324c3", - "0xd3eec9dedb057fbc839c474fc99cb54d89f3f47d896e06e758c98f1cd194b61f", - "0x0d44cb2a83b9a3fa18daac280cf08b46cc637d705488fd9400cd7300475d0a1c", - "0x2e37a3036db99c4cb1c135f5ca6b527fa13b2e80ee421805b7be5d8b16983602", - "0x381e0ca505308b7d3a083e60b0f9cb44c89f84942430ec9e4c5571796ab6a8eb", - "0x90b04d35906c6f5a59c266c3bce7c2b63cea1486f714e272592ef9ecab25b0ee", - "0x9cbea70e760f2ee97537d058d57f395886a2c3a6e769ccd3433b797b8716517b", - "0x4e2167846e8d6f0f6495b5f1443f59bea143b63f242e40186fc6429434d1136e", - "0xcaa0512739d000bb9783fceb46d0427098886e2b7f2e1140855f1a91f843d5b3", - "0xc14df4e379e84591f618e60b5953aa6764146c7822aa1f0e3c2287e20753985a", - "0xf4443154c04fd378b2c3812fee84b774b37d6e12778674403fb5c995379df866", - "0x1a501c2733cc138fb6ff3716899e08dbcd4d75edc18af8972e8a749e45eaf67a", - "0xfc8cb80bb0d0fb490f29aae3067641eef72e9225c558e7e299e0796a2086969d", - "0x2b7895550febf03070485c02d521e7ddd80b94b7fe33a60b7d7ea3545b13e7dd", - "0xfc4137c3cccd45050b5770a40b2f38c43c62b70b07d17bb6d762b405f3d753dc", - "0x86ed22bbbb9fc6600112b91601af4fff56d0ecbe9b3099f91d4477cab8e300f5", - "0x2273a60405ffb04bd024d880c79010f18d58e3c8ca0dc82795a0125364679fa6", - "0x00dfbfe7be3bb2116d9a603a01ac428c0088a2c1477810cd5d3be0d1bd86beab", - "0x7acfb03315585c79e2a47dbe847d24cab0785791f6af7f179fea4f9d6ecb0e0f", - "0xbf6a2e20ee1da5eec12b792bbaec2531e20766ba54bac423011c1057215851db", - "0xb5e94d1e3ba7363d1d79fb62dedd0b6c26b0485052dd64a7093d41ad2d41b890", - "0x9b0cc26f08708814960de8f280ac26d8ed5089a19bcbd2d765059306da22c196", - "0x22d8af121d3e395d3cb4f6ee43c06e6292f1b5ffda672d2e40dba69a2885f5ac", - "0x04bc174272a57189d76aa17de0f76806e8481f4903575ed8c4df12b042637e0e", - "0x06ebd2b6ec4b80280969a92726df5f9cb12d4288b60af617b7040876116656d3", - "0x0e9430513e63b5173271c89b1c91af0b4818d5d14a3034e1228c56c94186a109", - "0x8dc5422ba98d9e58112b052a00d4b82b1db32e22dd7ff2d845619899bd47f277", - "0xde513d40bdbb1e4956b468cece598d77134626a900066b92fb2ecd6fcb5f81c2", - "0x90746299ec75af1eb444ad14ac666ee444aa020fac3fb57796516d8772ec8f45", - "0xaa91c30c62b24f943ee1eec7586b682289541c0355c2726e44424da8686ca24d", - "0x76eb68baae9fb7ed126097f93842dcadfe6e7188d61549d9c0922a9b3ef8e80a", - "0x5aa5b4045e7fe71559a6e93f4a89b135eaef38b9a7f3a84e383ab1ff902ceca9", - "0x504b78f8fc3646e9722e96a5e97d99f2560d4fa3337fa5faf1cc8c8a05f3520d", - "0xffd7a5d7c3b21e8144f7678a9ddc039cf85eb32b09000a600c9f12aa7d6083ed", - "0xcbb4010000e96ff0b50b9627dae032bd50782ccbd51af8af7cfcd6cd184675f7", - "0xb96fabbdd02371bf4a6a0dc00e3874cf43d47246e27163c910c141b6759a4249", - "0x7358419f4e994ff296a37f2e88b238b3de6ba73062073c9467dec52a2df64422", - "0xca90be9f190a1fd0548becfa719a6e4763e92de0e4da4283a33b5f7d2886b425", - "0xa629364f7d6329b008d9c6a0262327bcc12953aa515cdb7b8817e7fe1d746d46", - "0xc5167bd8cac1ea6d14f305c9d4fe075e1875d96353e5236473b6daca5ae9b4fe", - "0xaf4ce2490e9504172a4393cf14e691e947c86a0ec7b53416384a5832b213d6c5", - "0xbfa4853ef2eecc5d99a90e1abfef37ca10c1f823c1d0ad59a1bb19339861241f", - "0xbb5a6584cdc7e4d06ec5fc1514233cc42970f6c332c3a9590978dc9908e58c0a", - "0xe69d7a0766db411e504f09a8f39f0583b2869016bbe95f21dba432bbe8b88442", - "0x89cf4caaaf200881779f5fa6da8ae91ff1c962045dd0622b5ca65c830d3a9d4f", - "0x82d66c631f4c4167e5301d896dbdfe24d8245b1de041fc85eaeb6e35117ed9a0", - "0x957907bc93879681d8682a188622f9bf2c7d2595dbe3e2e34bb01711cc4124d1", - "0xccb3a3380550586696abd3ac267e85c7516b2b682b3c48f66aca94d57500f3b3", - "0xaf56d4650406e70748dc860a7879d8d522599081f8e7011056c976b860703e43", - "0x5d96ac1d2dff8a054d880a44f5d45a1bd18aba29085fcd633b0608351ff1876c", - "0xe051736dad8b9f93a8f1c13031c2b63249925e152685a2e7ec188ee089861b20", - "0x0db8987339e1fae41af5f08e6fa15da5fd80de3431b54e82cf8edbdc792f870e", - "0xcc99097678110af2be8dc07da8d642dce928b7d9e2728fe6fef1fe2eaa81a72a", - "0x2428c1f94ca57c7913b011a68281eee9ee4855e4ed2c97e34a370e649b21acb1", - "0x501ee9580c89b1f67c5b3b69ae5fd1f83852a2f9330f53565bcd04d8a7c0b776", - "0x16ae47cfa19e8046f93a579fa2557b17aeca7892fc7a82b6d539930c8b7c95c9", - "0xda62590043ca70c1cdfc7969cdfa853bddbcef0ef62aabb9f372805322511014", - "0x481b4aeaaa60504c94dcfea966840b381db85183c34cd25b4857300b5c189003", - "0x035dcc47f8670a9f648bcb0232e42fd4876243a7a3bf737b88d723ba187929a7", - "0xebe9bf09e3577865aeb341a06f67bb6e607f10b04ed9f9d733492a9d0e9ceb1a", - "0xad5d85b58af6aef7f81bd6b2407c6e4884ec82b2ae2aeaa24e379a3d35902375", - "0x0f0dd63d7c6c284659283825624140b31a3adaf7cdbb2255faca443e52ebfe84", - "0x40079be1c9394e95b4823895ed380d79333ca2085aed2abd0d766f84d21b7b42", - "0x7cc40ed01b436ce225a3f9c5c2bc7f6f81aee40bb83a54bca2fe899b15f3e2b6", - "0x1b6356e1a83ca5b0eefda1fd62fa959b118d2a19a6a90f182a53414b3fc7f9f0", - "0xae4a71712cc96a5b30b45e3b92c339c2e975e4ed683f4d1fcadcdc121ff7c6bf", - "0x226f6d8c71ec32c5eaab6b01c0fc1d00ae95e60b383d09560e90549b79eb1447", - "0xf3dec779841c9384df93bcefbba8700a292b570b29d286a7c9c5a442b4788a20", - "0x63ef48e80efa45383857adcb0f31076393260cbad058d1938345ad13faae50b4", - "0x7ba213077278bd01ff874e760c3a0bfd4e5cf65bb18eed8b0f8076d81e16d05b", - "0x92eace1cff535745aae658d9ad90c372d41ce318a20716ae3b2c0958bdb48b33", - "0x76b2607fae8dbe532d8516c4c33e309f2c0a963bb68035f4361a2859d62d2daf", - "0xfb0c46a588438f4f217e183ef7772b649b5a45a318253496b01676253dedb568", - "0xd6dba12c700a0c5ca59b9d17f628066405de8e4399f92472cccce2e25e70ba4d", - "0x2390984444cbc9de301adaf0853f4f6ed2fb56fefeabce717306cf1e40da9618", - "0xa6992b0db82c50ce623febc7f16c8a316ce262255de11ad1914316b3d986fe66", - "0x96787cf8e0a70ffe5b84151ae8219362ba6d9465223c23c135590fe4cb9ab592", - "0x0b4e7fba24f6bed3790b8fc289e2715d9a3f4235c9316dd9d4f889f710a65ae4", - "0x49585a7af98ad032371df01c458fab933ac399ff4d2d35731a2d86fc136b5eb8", - "0x517947f22d8d56466dbb496f3e98fcd01864176af19be2112f2260e55bddbd58", - "0xbc0aa38cbc79c3557fa81e446071e6d531989d088c6311060bff971e430fa36b", - "0x5a0a88750db7ad3190dcaaf5bde2649e6fd99cc5b05979fff84ae689ed47b51b", - "0x9ad902d6abe59faabfa5fde485c2fe16f6f3e2c41aa8bdaba021e94a3c292dc8", - "0xbd6e7bf8b466940b9f619e3f433fb68c68d344974798996e454c925e2628b1b2", - "0x112b5e9d438eb79aa9d7b4488b6aa9f7edf7599dce805d8781510302da45e9a2", - "0x881255ae2cab543ee6b429e1b201c197cb35051d53f881cb51fc58f9c760e51d", - "0xcf56a4ed6b771e4905e6ba888390526eba8ba3483ead7585dee00c0eaea58cbc", - "0x12705d1609ee34bce0c82bab9da730452db57863371f5b84de84c4c135537237", - "0xa9abc081a75bfcde86ea830483be240cb3bcfa72ae34a16dfc35cb964dd7a457", - "0x65cf0c91f8567d8567ac3f00ddc19da0b916179266c7e42768fd5d7271e91092", - "0x07f15ccc91a47edfe9b67a79d05ed1da1410e3ac16e4a4c2147c3ef0de26ae62", - "0xb44eb7db9e4dbe8901bfac43ad54ef27576a3ac0c78218138715e03b3f9a782e", - "0x9ef66d20aeccdea66ade0fe8ccc0a85dbe9498305509c8919b670ae8e854a450", - "0xb10a7dbd4857f910ae8cca5d1b7a85923adaad65c4b00d1255813efef00c646f", - "0x20410bc64b58a1c221f7b38f293700591692c73a785d94a5548b336bd5db10da", - "0xed7176f398eb75ae6c1d4348d3882aade99573f8369ef00e809b1ff91b5a9191", - "0x35f06785e1f7e362682e969586322b1588624f6025692ca87db21cb199c382f4", - "0x9a6b06141660e2bd106f6d46afd2f3e64d72711ffe6a9c495887bc3932f7934e", - "0xd64e602a976e86d3948d0f4cf0b8f13a63558d5bdea5affb36eeb91007a653b3", - "0x71050553ca1da7234764a5486e4647fdad00652560eb47102c1d0fc039b4b63e", - "0x53ba43f79de58a957e8ac0d998ecb4999c68f423b1769d9e8df2405259dcdaa6", - "0x17b33d269609bd90fac57eaeb298bf489a3b8a985cfd9998d297c7e0628bc84c", - "0x9b47bba7ff6b283e76100e36246c0dcad836ff29a674fbb1c6d2f58e38e29f92", - "0x262f8387628500fa26e2cdca6b5938262c8fa81888daed41a09d880df136167f", - "0xb6c6d0ed2824a9818fa9476c99a810cf33a0c1fd3f2fb6b7a44603f2442525e7", - "0x6f3437e677dcc60cb587f6100eaa2e7ef5c76b57023b63ae61a6a8ed2750512d", - "0x4bb5aad759b0c6390cf4dbc6c32e78f2721c2be3f40ca3d11de880fbe90e456a", - "0xfbeced3e048769a07bfd50010de59cd24f2c534234abc1ee7f3fe9d7e3d9f482", - "0xbe871fdd431974370e8156fb9347377d1bb07990ff60296abbaaf2c38b4059d0", - "0xcbdf566fceb5adc33608a847c77b6f3501cdc4182ba98e04e1f7e8b8ae66ba5b", - "0xa06a5de4aeafdb5981ae02004a38c524116ebe557dda83abdf03141e4dfbd4cd", - "0x73660bb361b94287b85b6c41394bea5447e8e3ccffaba1772a765a8fc0fa9b27", - "0xf80439908d6173fe4fea42aeb07ddea2c644e581373dbe9dbb9d092f12fd2125", - "0x32d935f078bdbb9ae693bef4ab8316e7da701edf42f156568b639193b0ea24bf", - "0x8ef0b9f247b5b9d9e5a813877d03a518d1f06900e2fdf5743feda3537ab3777d", - "0x59f92f14340a77b94839b6ea61389691a6fa5bb2ddc5b2241e873b066d37fc61", - "0x0288bdae302901b701df46263f4b12bdd6bd76436c3ae1626936e1b4bb631ca7", - "0x2fcfd5a54d6bbe07d4063ef64fe4b164bd8cb89a8e03cb5fabee98bfef280e43", - "0xd8d1169b3a9aca48f167cd43e9617d3ecc74658ea61ace251beb370d3c05f86c", - "0xdb2c3ef7b8a8a18ad3a8ce7cb8a6be2e1ef2cd694fb3f1b2e0732f2e27c758cb", - "0xfbffbf26f4756bcf1cd217686597ffe9c1282a70b279c8b01ee5076ec31b1c2c", - "0x2cf1404cc8482698eccde40185740753c47319b1aa30d9204fe53922feaae9b8", - "0x553a9e079d09d3f8251947ab37891027fc3d041246f2e43f01321fc3523b508f", - "0x35954f131f276933c7ef3938f35c6011806c7dd637fa249451379003d740cbaa", - "0x1ee7a658e8a2368391810667c35b8b0e321d381907e2312e7789a3cc7b0341aa", - "0x4cb3c908526bc6ccbdd39d855b3b3649e59eec4322bf48f07598d98240101d0c", - "0x996eb99b0cb27fb5ec07873af17933f377ed2c131f02926bab74341c475a95a9", - "0x68a90de2c8ea439db0417b77a23eb66e7421268335be7e4f593a52b1093681fa", - "0x4325afa26bf545951a73c016d866f1c71a5855ef52202b7ad0c54dc23906936d", - "0xaf5111b606b0e6d699bb4fb61c8a0592af7eb45dd7357b3e896ebc3e798919ad", - "0x4de03498b2d532a8eb01a12edea2a15b8a907d6d23ccfc380507a5d294abce99", - "0x5d642ba55dc046ef1b84fe594ae2e46b844cc6590d580e2591ba9c0c1c942c24", - "0x8477bb69d6d23ae4f45e56c372d66f42ba7417cf5107136081b5ccdd208b1545", - "0x924f641a7cea7cf76ad8920b9ee7c39e74304169571b028fbe40e6ed255a90f4", - "0x4b39dbfae8635f286284009a0213c6d267d5f48485a25f2df855c5cac5e7d6ca", - "0x61827bf3a120a079111665f78ba2e5ad15943492e4ffdfc708e235f44ce92842", - "0xdc0c0dafc340277058a6f8e03bf4e5e733e05fa638e2a8c4ebe4521960bbdc20", - "0x7038c52c4e92d6ed20b19481ed63d432325f94ee2a3ad4c0d5e25fafe5b32bd6", - "0xe1f5a1123679ce45005d53672018e16648fd44c4a2251802f295d29ed876a0db", - "0x173a332438a181f6cf1773b2603556d229254861534f34ad65b2170b0cf47fb5", - "0x22a19ab1d828b5d05057f40f5cf798e27ec068fc584968987b7bbe440f52ac84", - "0x15b3e3b6e1de6b8a407ad7e325c53c4bfa9e73f23c6f3acedcdf68d759f73860", - "0x0347a8e310cbc921daf829002ba0c44399c4dd7c930ac1fca203272949e09dc9", - "0x9babeec2d3003ddb15fdcb89ec7b61c0d7f795145b58f55a17810447d5afe5be", - "0x62c14ff2ed414ede1e3fa7fc2fccbab0b8f284b9f38127823c3dc9eb55a08b44", - "0xb301a7c865abe0f42d9444b5c3b775dc6ab4c99effbe8d54cec29745ff4af01d", - "0x3fabed16363d78f34a13ca6b99cfc592671668d4281c5eb3ab07f4b9017a4ebc", - "0xb14af7c7331f7b5518e04bc31c8c2b63d9d497acac405bc22e1ab2431105b867", - "0xb7b90e8852ae132f858cb98635fd6e1267b141de7da6773ec4092d5c83def42b", - "0x1a5c8bed811fc72de8146516cc4d668fc4557baf48082503a2a5cc0e473b6e08", - "0x0060158b427095081a18fc57c4f56290873188960dc0e4d2b16cdd57b5761100", - "0x2f0dd546f4982d47b6e04ce282b19be8e165b5c89b5f03c1ac8c6d8be2af2b4f", - "0x229d31a50f46afeda23a2535c8b011a827383c981e7e59f67622359e4b307a10", - "0x10da7b2e78954845b506b9672ff93d3a617f187521116d442b364a1178f10f08", - "0x511325c034c772ff3a61b1c8d94e3c13f19cf1e93523a8924666833bc1e5b0ba", - "0x3a8b0251b2f7c99774d0a7863388422723b5405fd1db035ec5e30df90306e251", - "0x885c3b0507dcdd6e1cec187cf3d97ee12ce101ccf63d99c1c7fc77dd57ee0439", - "0xa76f1fdcf546ccb62f86ec7b85bd8eb662f696d7aed2f2ed2b3c769af90897b2", - "0xbec073b15a35de54487b60f92615cd44dbcb5a5fa9d1bcca2ea31437b2e45756", - "0x662013eba4201630ccc4a9b6fb1dac36f439cc6cd2b6f295c0260713d075108e", - "0x81b0148f7e690088330650b01c52831d50f3d30ba2fd2074bca5513c179ad30a", - "0x01614cb8f2f2bd6794a20513fffe99a36eee526b7145405db15f9c2ede980093", - "0xd3d8664a5464c8f681e37265948c4046e373843e602bee66c8583277397db258", - "0xdaf61a9e7157100cce6ea5bc405926fd4190fda7354edaafad9b6e468bb24422", - "0xeb05144e032621f7b1792dacbdc79c044a268442a9a9284ef1b2d1d60cadb29f", - "0xb1916e802e8e3eca2570d1a190d317cf38cf9f6b58119b65ed0d60b9749461ad", - "0x17e103cb26e7c5c7678a523f99f90387490bcca00916190e7e1b38274c52a375", - "0x105b8126ef037364f46a44fdca4fea1ec4c099de5a05918688249fb8844dfea0", - "0x7ca09f536bb9f0d7b3809c086918ba4750722edfd47153a8412fc761eb4783c8", - "0xc988cc2d92dcb85eaff8cf2f70ca49646cc5aff6c8e10b43a0581193137d3b92", - "0xfdd2130ac413b161515a29d01f0ea38bbe234e30f9753ad3eb122b0728d8e1bb", - "0xe8945552bc76c1db98d0d6453993472e75c489d696964a660d816a31d3f2c717", - "0x3c6130c0f35013d1d2992067488a8a7bf5a7b15d3ce00cb5708d79dcf8eb45aa", - "0x7be79941a43f391ea4366caeb6482bc5b4f7d85f55ce6cc91d6300822550417f", - "0x91ad8008733234c80946a16cf729423499a4b811d5b457f56268e0bb2825ad83", - "0xc3b17bec09d31d9a4a0902666e5278cc7458de8b663a564a53c7fa04948f18ec", - "0x7f527fa7d7332df12c625bb575d5a02e86b4a9188a9d75757b172d7a43595e9c", - "0xe3e2e24bb98bad170871a3803860b30cdd9e5bf05f4df69ffe859f0cad8999a9", - "0x395e940948704ccbafbc84b561d7e86f6c414b8fd34cf23692a0b1d2849b880e", - "0x0d90ba6d9cec7bfdcd8c089dfed671de95c7496c6b47953739e92a56b5236bc8", - "0x82132c33f678819d7da7c778e4884b0f7d10e1f0912885301277a2ab1db2a043", - "0x546f0ee2077c529d97ac29fc0a29e4162b6fe95bef2d0cc1a073a3c595185f5a", - "0xa08ad1abadbaf6ac33ed71893b2953c30c82b537f2b7efc4a823936f3ce407d9", - "0xb9a456deded38627d6e464e28c5c8e8c562b2f6aecbbb66e338184621aac5893", - "0xfd848c379f615abf2c6052f9f91c98e0ad6f45b5760f3b230bb10a02ca8aee01", - "0x49c99b4e21846ebafc7823adaa6e399a0ae22cdd67ce991ec58b958c871504b1", - "0xf5e1d32f10b281eccccc28b26254f032bec66a00620d46bb0dfa32fc14fc6d57", - "0xaea8fcc3dbc2f9af82c6eeccdea1030a74bb42dce190cf661471ae4fa052cc72", - "0xa30cd039fff05049b1ac82a3fce3aea92ab1ab058da97add8aa8fcb53b5fcc31", - "0xc8c87727a220a04b5cdc90093fff70aeab14edd25b90599caa559515e9f472f8", - "0x84f99e738a285033f43854e9b639691453d65171830dcf8c56cde6aa9a60100b", - "0xeaf3d87e7ecaf90c4a261df30540595c84f0fb43b9ac3b569120bf434b4fc521", - "0x8dfbe9deb689741bf6b8869b9b812a339bd70503474d75842ec4f721ba5e5aee", - "0x31e62e35c4c24954e6844dd9acc31cf21c44295d3744a76a5b264ed9ab19291a", - "0x764195f9c0d486dacca241ba5bb0b4d74d936080687243c9c6d2c0b608e684cf", - "0x45f0b003dc4dd663bc90b40be14a5d98be20f5d1041d2007210ddf257aacd866", - "0x1a2f1cd3cb0e86824ff55d52bff48d8627ba8407f5f8e3758f61edcb20084752", - "0x786f0ccd1548ff58c4ca0c3a5d648d3cf7bb6588acb6f1f84938d0fd7ae10b11", - "0x9e2d4bb1a3de0a602d1596cbbc712eb1259779cdc285ff8de412f0f53169a74a", - "0xbc3375f64ffccd58c1240d89f8cece571668280c567bd694607b10320e19d5ad", - "0x06b2e9aebfb6c5c7fcf3d1e33120c0f3ffd6a02b4ca1901dd2a7ae3b282fed24", - "0x54372b1e0f7456e69d4854937f5a3472d531e0e8be670fd1e744c1041025d2ab", - "0x70ea1c88193cb99b052c07319dcf3546e34559a28fba6542f8149e46d76a2836", - "0x36d80d0a12903b84079c4a3da17f44368db717512412f60e8394612d3d24edaa", - "0x64eb6681ee0a352b342b978bd937828dec322782927a63db587fb8c2fdacba0b", - "0x582bdc0da00c07fc327fb269a5990cb33e3b6bcd0e71d9193707fd7677935f45", - "0x43af6e0a2ccfe62ed7cfe8651e34a39ae114a1b08c15cfb7aa587f1639665008", - "0xe3e9cfaf0484e3d527009c370f9bf2f2ceb399bbfc9157033df34b1900598451", - "0x61d947ec543d5c700690aeb6f9f217a375a5ac1fc5e6899ab0a3ca3b2426a699", - "0xbcaba983f9d702008ce43e3f25f148596f128c436c35bbd05010c1153329121e", - "0x5305ada22ff089aeb56ea3bc61c4c67e4edaff1240a401f40d4cb15535825891", - "0x631e3fe402db919ad0b62228dde50a7703845d43fb86ba165ca3c36b88d35f07", - "0x87d89ac00db143c114f115942290ecf0284e62a3d0273c712015f6e391830714", - "0x74294af565afe5649315b50402ce0ee4ee93a0fec3de6a44a336b50dfbd33965", - "0xe13faed5afced183f1f468830b31e973d3b626b5d29102a6dd156ddad2b52a19", - "0x9fa97b97accf83a4d798da116d267eb6ee94e3e8a45ac96c15c6ef532ed583c9", - "0x13b60328e4fcb88f1bbe1a7dc31bf8035bea971f2a955adfafb1c80cb50540fc", - "0x4ef647f57565fe4010895ce9192b20f14ad1bfb229787d31360dc1fba528f097", - "0xe663f00eaf821cea5cbb669e700c6fe96b39633d58d0cadfcfa0c305566ddaf2", - "0x857b5e1cd8bdcdd477544ba847f2bb71a80839be83cd486c80f49ef5f03aa05b", - "0x04fa22e5b2e1295eae08c160d89aacf5988fcf234abcd6559d08ac0ec819b7b6", - "0x606b03cd72b48f84170541a8ba5e7473f7fb98663bd275767f4e8895dc5fbd77", - "0x8c1760c6e57e8fa0b34b521db41a91002398ae3e4b85b7cfaac447a4fd7b27bb", - "0x686f9726353dc21d8fd91a5bdb6720c815cc1b069e01a3baf51ebf21646ae27a", - "0x60b2747f88f74b6b8a5fb0bcf4f206806d9c69900f31f397145b1ce324bc4b0b", - "0x96bb8834eec4e8d652ab2d1e9904d4032707265d2531435305286ec7565ee333", - "0xae1372ae1fb3147d1e4f94a3b840b11df364781c60dcc2304aff67a1888b08d7", - "0x9bdf500fcdf7f47b16dad00f193de77d2feaeec4a3844c7a97d9d6fe754be73d", - "0xa3ae272c20bed715b96a6777f9927d654b1dfe285e3bf077a6c144118b3e689e", - "0x7b20afa1fcef7bb2c6196d8316210d7b9e8aeecab72093a2bb26779e440246f2", - "0xb6045904522cbe4e29efc67632ef662486ad4185e608b1a43abc276c869d3a44", - "0x99ca4bc614cd61d858cae5fd2c32e2fe8df6a713e5958b59eb5933146bb795c6", - "0xbcb2fbbdd294aefdc65c965cbce4f690cfd08a39fcb0fe4c4500efaa679fc5eb", - "0x5bb1c10c53c017dcd92a4fcb9ca99310062e8f2689d521afc4536777468a2c0c", - "0x8090b4334ccb0abca3e6ff662a6d6f296d162a38405600d0e229fe6622c06e5a", - "0xb148fe3d9929ef770c7760c5c2412622523727e59aa79f42d8e8f668112b1058", - "0xc452602f20af7e252a3d796dd685b6fd71140e522368f0fce78eddd3f310f696", - "0xb7da293297b71f6d82b4b56d4b02b20e795745d55933df8f32b3990723dd7200", - "0xaf1c9b7a8176f3696d8a5771f5a7d526fbe65c7ac17b6160cc0a7f492ad15522", - "0x675d783d4c5fc56106aff1de55c569fcc86d7ccc9ce18352c84e8d657942babb", - "0xa4cc61043fabb8a135225ee23b39639a21b75c225e716e78d19ab3eb5f39570a", - "0xc5967661349eeedd834aac16a2ceef215408baf032f410cae4cf7dd083448954", - "0x203aa3b0b91c11255c488e0685205222841d3547b0e8f6389fdbbd0ccdc5627a", - "0x707e3c95eab2259eb32d56153732d885d4685b9b4768e0c4493080c158942d10", - "0x56d6ba323b3e0aed6db776187e4a134c0329771891f0852218d7dacbc782786b", - "0x426e78c3799216353a6f1cbe4b821ded6c2ea2ac5abafba43ef59498f3d963b8", - "0x136a788a8f606960fdd307226f17208ed25ec11e20c8add70f4d7bf5a7094674", - "0x20f28a3ea3e2a1673ab9bcf21718ce65e9992542761df135db1516d70b7d1048", - "0xfc671303a07b11fb347a93ae05fb845c87497e8875dd52f6fcb208889c7a8ba2", - "0x7b1f68460d3722d0eabffaee4292cf3a8a2c2332bbf2b485ac29fb67e181d10f", - "0xea79ea4b46e60c2807a89a4e4495fcb27d1c0b232ab0dff897c1c29b04302c4a", - "0xc25e2f5b510d3948ebdad351b1010ecdb5eb9c15b2b635a2f319984d292b592c", - "0x9b798bbcbcd0b558ca5d0e99b1df69e717025712d3d0246277ae5f0f00a379f9", - "0x78cb4f6097f57d685707514f9923b440eb752c50e81e092e76568ce4071dc6f8", - "0x624c3d908482f6b307c41fd261ceb2e8e7705769a625a707581fba8f7cf6880f", - "0xc1096286b083b2b53df8ebc639ab336103e5071d5ccdccafbd51b2611e98263c", - "0x82e87c7e6dfee36822f908d9b83ca0ad01d8cb04b71076e7c095f277ba2f9721", - "0xc59deb5cab98581f72a30ea5b1d97d58d60090c3e4a38a80d5e95dee4d1beb49", - "0x46f6ad8a11ab6018449094c73fd6f12c2a02dd611817bc477e5896728a9ba20c", - "0x4fa31cbc172bce1b96902fb4668af7c5edb9ad0aa4aa88556bdc92133ac917ea", - "0xb1ce16f5fda7050f441a68ce38878f3b0f704cb9d07b09333223fd0db1e224eb", - "0xf9e6d6beb3d3cee105a56558c1c80109b7c3f640f92750d32a953490c8e49a03", - "0x1cc0660cbea7fb394122f05c509432939df206198be388f597a63ff345542371", - "0x8a990233ea772a8b1ae1a05067efde8e2bce4c0aa868010c9c930d9578a52b6e", - "0xa8898d9d95ee70ef7b7ffa5341ab778d66fca613a81c527668451dd2f0187be3", - "0x4976c79c14b4128a2dea82ecace675729d7197d06d512725595efb5d2aafe544", - "0x7459ad72c83d2250fc3056ce9a8619dfd4cb3f3b79530ec176fc1b65a88344e4", - "0x6d3cef3bd8181100de92a00157eb1d3452f74d5a650a3e5456e31c8fa64ccd18", - "0x8d1345230024f66514d00c72909572866ac422ade832ef17960de0172e288b7c", - "0xb31c61b316759513fb717ac7276813cf6e3fc6c8f69d745aa88131fa4e21e2ec", - "0x1fbac8f9060db69f1aa812bf2a5c051494aa65585835c0e04a14eec0c257a4ca", - "0x10970beda37aef785a26c1f67a6825f52315902c4acd0c28f17c28ba5228d56f", - "0xfd5355b1205e4f257c805a42b37b748e026f8a640667b58ef3f365d65b3ad7ab", - "0x289aaa44b27ebd9b581eec7fd02acb26f97bbec0941c0263bdbd52887cce6ad0", - "0xb12a4b6602795d577ef7e0c08d495efc81597a9720383cf5c62799c69dabfd33", - "0x4632d3a3a2017d16da2ece45589f41336a902fffccfd3916b62bf5c07e4566fc", - "0x848fbfa0ddcaeca6215ba16631e1a8e0481b5fa8d6c10bfae812b9db3db0314d", - "0x70c99cd948afce544597d8a24d4f8e836ce41a6dfba6ae92159070422a978fb9", - "0x2016276d24935e2fd488692618db088a6028456c30b97bf25bbc25d06620f2bd", - "0xb72f4cb68e8a5f7cf2095dcf5e7622a988ca2116a1f32c9e0b2f10d4a2f9c5c5", - "0x160d284bee200cfe4a094e7fba3d2e8aaf79eae7886f9455a72afe3a4d34c1c1", - "0x1d5a091116bf6097c870c4180074fe5370ccd190d8495fbaa6678843ddb568fd", - "0xf3cc06c8bdc245b17df05526873ea94b29016820bc7a207e84bcdcbff3d22bda", - "0x1ee426c0c1b7520ecdf50a1f15aa8d39849f4b61307bccdea0deeebd3a231eb6", - "0x018590cf655da681e4556da40944bc47a039356d0ce2bb677d00526c19fe7d56", - "0x2c58a5c7c224c771884d5252ab800b86b68934ed08ba39a13eb93bf2fe5bb4a7", - "0x36327e853671045ceee2390b141992d00f2e28fdee633d9638338830b638e5aa", - "0xbd197bdbc4720b20de016cb3e4ae5bf01c6982813e2b7c8ed68c76193070db49", - "0xd5f211f9b9671781c853dd50b4532ba7623611934410d71358b879ad13cb3def", - "0x18cefffcecc9763d7b8e7b5617e54286576ee981ea7ba886ab88435e321b8620", - "0x1ea84c5209f9a523416b82d780a08f7316c8355eb80d24dd84539dd33ae9cb3b", - "0x6cdad35eeb25f6b09c0a03fe367f859b66719463b63b280d8e15128fa598fa6f", - "0x6cfae8ad85b389eb5895f46c65b3d9277153177ad41ce7817bb8b9d2061a8149", - "0xc96a4f02330fa948bdefd1361b0be7cff9db6819007f624a1916fb234b60052b", - "0xf0020b10c9b330a73ae05d8bfe6defb738dba6e95571bd0897a7df2045df2bd3", - "0x8c31b99fdc9b619c1269c1724155b368586dd98a6324c2be78b1e96a265ab213", - "0x7f7beacc5e2eac7708d5df32d6a3c1d57d7133ab1f23f0c9de10482faf48ac76", - "0x72a18edb95e9c46bac4295caeaeabd0225dad790570496f32af12719eb201be1", - "0x84827cf9a51542f4b596ebde2e7a63620438e4404dc41e5b08533bb52bea883c", - "0x1bdaa51141889bf15e35bd1f7d7bf8e9bdb1e45993d80d131496aa552190cca2", - "0x999f63f4fd586baf6f00b56b8a60ba31d60353480c49a49f6196de0ff0bf3264", - "0x315474d3df7fac96bc940d288ce7286629b6f2d3a6d9375c1ecd8878079dbef5", - "0xff65bc9b2cfe0d26b16dd6d7f1ee247f7198d67b9075c7bfbfcf0af8e0fabd03", - "0xaf21db7dfa5c94b1d1276cb18af97dd57286fcff3395fe6897b2b4c21d3492a0", - "0x7bca2491e67626d968ed3c9eb953ef55005f6e92cd8d3879aa8d1c746091bc72", - "0x1ca1e8e053bd1ab7df8ee667e40eb672d1bfc6230f2f1f8cbb37a911dd154b9c", - "0xff0a0993dfe362458247b45863a45b53698d600798945d21a61185817195c473", - "0xf8ebd8060f16df4bc3688e1ae038205fd5b67079e2fc80251f6fb93f977dec8c", - "0x51602de9fa527f23030de58e60a7318ed5b6f13b16e74b549ed59936ba09510f", - "0x3eaab0754ddd2bc2be0c0e72974906d3b68dbeefb710735e22f15b5fbbcf102a", - "0x94f81d99473387ca87816038c2eb4ea655f66b64f22a4d0bb853989184362f88", - "0x81af931fe1599467aeaee038e1a78543f5df47f4244b1943edd5aacabe2f001d", - "0xc49179808d729115588351ab65972a7b2c2b18868c9a578dd512adfb4673c5a4", - "0x43b9d76f1c2bbab800dadc6ccb88b966ccda198e30068364cb9c0b9a346f5f03", - "0x1c9ae4b3977a1d4969ef3d950d2c74548c8b44bcee652a65f7df128511b84e48", - "0xa06a1a6ba91e429f805640faf789214f42be0d8d574065b7daea7daa37b05865", - "0xf53ed26aeec7ba846e10a84814e7264197a0b2bf485c74d98e8315f0ddfd4b3d", - "0x57fa0c3eee29055cf5232125619ff171a60d2e8f0246af1ea8120969e03dc3f7", - "0xeca0088d30058eac4ecb7ce8dfa454e46e642154b7851db0faa430a01bf33744", - "0x3f43e499ddd06a059b192a9cb9881d15078157508c1ac30d687951454a22441f", - "0xd99f60df5d3b55202089a3a69147e43a021c5760485e0899116f56060ba56a92", - "0xe662a0a21e688b7452d0d5b6372bc8aeb00c589849f63da9c3fb4c280e6236b8", - "0xcc83d5becb2f1580e7771c576b97709778a8502096948677f1274e9b0ff444de", - "0x17cfc1a415766b30cfd1ecdb2bc8bca535928e1048803b9820e81d7058aa3b68", - "0x8e1410621e8b0edbfd9c98cd1dac02e6b5dc8336da445cdb00c16c457ca27750", - "0x3d8bc5d8acf9ff44f9099594290a91d47040eba0f766609f1dc034d6bb127f4d", - "0xbc32d05ddd709d4f7ba929353a618d70b8eecbe1d3b06010cdc7dbf053569e25", - "0x818d10c0a40a9675a1d87673d9dc55895b8ee337a0cdd99dda88d6c1d506a301", - "0x633d32a1adbc08ab373324175e0205412bd17ed1be4ee5882d20944c3439f77d", - "0x8107273c0ecc4179096f1492d21de076f84d0c4529ff6d563acaf85a8f5db5c9", - "0x5d8c25ebc9468667d043926dc4a6d28caaa6be696ba3e4d248ab7150b20fd771", - "0x5a8cdc39a49bff71df23eb7fb01811ce114edf6829f946a6155113b39480d023", - "0x72f7ce230da5fe9ad521901d2e1f7c88a20c98ea82fc09c240501ba327ff3518", - "0xc9c91729c335752f0e04f091dc181ce8659dfc13a77034b319c47554a5ff0bee", - "0x3773c7671613b65b8c6fbedc97c96ba6e3b8a6cf64acf6d1947f2881dfec93b1", - "0xb378b0953123036f313210f7e020441f41c81344b0437e5c947aa3d2e76cc357", - "0xcf3b0163249f3b6d1c9b756eddcabc9fb9d11e121dd96499c750350dd56cfa3d", - "0x8baa2ead915e991532ce72a856a001f7cc493a58016d3a0255cd416108357396", - "0xf748b8ba84812b2f5ac3b91b51402194fe08c1d15382d10895fcc8d4622b58f7", - "0x7ec5d0b6b989137882c20bebe11d2facc3e68e9a93a9b2c793d804d9e973304a", - "0x4512ed8b4f3586a79c971cba695d727684c6485ccadfff6a3db45ff93fb1e1dd", - "0xe6edc389d0ade0c7e558ffd357a7557af9f2d38b0b20c84a67936914acf088de", - "0xf2947281385a9b2077e6e8657a6bdfb9c93f82311bd739d8e9b96bf97454ade3", - "0xc768e9216d0dfa3da3fd3727ac4a673897567570653a8517219c079edd191496", - "0x9bcb33bab81663ac501a5a4d2f3d739668b42a6bd2cf7645c3571d58b37c60a6", - "0x540fda7f30ecbf8a61855b9619a139e502d8b9e1a3e72179f2399efb3aafab0e", - "0xde605b760659834d38aa120d97d5b0721eddcdfeaa69a716e44b35ef721c85c7", - "0x8979afdd57a677913bfd414f3c7cf88c3c7144f5168476e7c08518396cf8134c", - "0x6826b8b3aae4d8f97707e9751a4313738ad42861bac31090afb77a34f7214b7e", - "0xade12591565e41be9e81ac8a673324a680b0a9e6d980ede210c966b1b9252938", - "0xb2f3710b4e6c805bd20ad93fa0f254bd645b5c9b5fa299f1f8f9bb57d299e21d", - "0xdb80cf61e5a9d6b6237855eb518851f474d12db1fde3c534762e1f7d9367763f", - "0xc40c3599108f6057fb67f09e7aee418490af2a887d59f428af222519a7c03a12", - "0x0916f11f929196f5834bad46ea7eaf55716ec2d269aef9f486cac38fa7734831", - "0xd5362e4d43e3da51e12debe7966c2a06ae0fdd327cff666aeb7185b5d6cc71e2", - "0xaed4937eb0e5c9a5e6803cff864691f7ac63fa9f82d3c2e1ec9a2777d4d3f263", - "0x49f63292bb091a6d6e6515e5f822903da4a9e652847a7de5b0fba5ca69336ae2", - "0x7347591d5dc652262db2e95f65a570ae894f28a631dace959f6264027ae5f3c3", - "0x300ebb8f363f2921f5baa4ad46332bd7d10eb113679afa0851ede28a33ce62b7", - "0xff1b93baa7285220352ab656d513ee19c12da79c0c87d2035c148744164cf32f", - "0xd1793b3309372878fea3c14f2e4dae39588ddaa2396fe246b3c4cdb534a167dd", - "0xe2c7d728bb7899ab8b0ae0d07d585cd380a38f63fb9079532419fb5cbeacbe01", - "0xc972d3a0a76889c41d1130c52bf175526a5560d2fab7b28bba7c914a9b9621cc", - "0xebd731a2040b488d736e846efdb41ac5a6020e7c2b287249c105c71bf59b9504", - "0x8745a726f0e971fbe22c9b28234c3d93333c0f8d5c126ff9ee5000e26da7031b", - "0x3033928e0f79cc47d31814e409312127ef73a15a6aa915786e4410677942dc6b", - "0xeb77e57fc7de01107d1adfbf479a4c3a6ab5ddd008dab67069c5fb488551634e", - "0x6eae31a9b04e7d30e7781e33323f52e9f411fc7143b8feeefeb71cf6b4e86081", - "0x39ac81712892e1ca0cec0b63cb7d526edb0a276e6ff41369288f788d4eeff7a3", - "0x9de1c4046104a2bfe58e94215f59e1ce5898e3efcaa2ac6a7916817648e3b304", - "0x3d6fa25cdb3aa2cda4bf65a85927fa5b5c79d3ca427b6b128d34dd24aa08b5aa", - "0x8f5d95fb92d3eedb02da88535e4d0c9ed713fb2a6ded42ad8d2036306abd3b31", - "0x36abaa49a58fd335ecfe8b0ec61a68da28af30833018788fb710c3847dc3e258", - "0xe0224a4df20ed2e5cd78db19cd906d29bcf3aeb6f79135aeb62be16f766572ab", - "0x7584341a036595e957e7f7c09a5932c1e75e5a5a73ccd31fc2806b134a899082", - "0x1dd6909a77af37732b2e59d4341047ad85b610f470684433f2929673399c2fb0", - "0x5d9bf04171804ff9f2927a14176988349c0d0a86654d8a5897ada06ca4a18d02", - "0xa1714a70cbbaffc82a9018ec48acd7aaa70a5b6ed00800249fdd76326c5eb32a", - "0xb8fe04ca47841ffa34b5f17ce8922c86a9b98bb6d7d13c4b0fd27df931200422", - "0x308d034f42f0beba3aedda196573bff7b9c9852888d6f9e3362b4259d2c1cbe0", - "0x05be7b2c1b9f84273af920a4af58b183619b016aa20199fd6845eeacc479c744", - "0x613880fff823c896127092c2c4e0319e6cca955b650829ffa053c4b228429d61", - "0xca039f943e38b017fafd119e870ee80bcd19e7994b15449a03b7bf35c1a69add", - "0xfe29c86d46c9db3f2332f2109a81199c5f48730826755b56715a98eae6e6bef9", - "0x6e95261130af62956647ad47ebf45dba97a9dd684940d5b241a6b0cc36fc0b45", - "0x113eff6173e9551800e70882942c37723aa83d573f2349ac614e6d245411af19", - "0x07e9fbe087e6b8f4a2cf41373b1977823875a9d3be56b937b977503a20996437", - "0xae0792daa47529f8690d30dddb9e0ccbdcd3e4a3b3f0386f098b147c4cb3dd58", - "0x6ad953988a96ea288c407afc70581289c4dd6b8ffceda65f6d4a51e8cb7228a8", - "0x949d0b15d25fb79f31900f0e48ac3fef50b3d1b69f24f505fad87ca56ad883ca", - "0xf0e01643fc4c21dc3adb6dc1a0bc9f601af1bd4b5ed50ba156a280996d521db9", - "0xcf396c2e2018e5dc71ce2e8d8d2716565fd9ef6ae097b871b4349a487e0a6727", - "0x5f22e44bdf7a346985839c4a395f66e2fbed238f0463ab2b02d7dd23c4e25748", - "0x8808ab6e652afa85738443a808d140c103757cd1a356bbe2b409e95cced6fa7c", - "0x9ab3eb50f50a357020c69c9501cb2864febf0be1c04edd20cb1fce3fc075b7db", - "0xc360f24716b701218d2c486938e17d7b56a85b818b4e808f7c12c85778914f68", - "0x0e8388813ad36963923d68e9a223087e2e29282dec830d13e04ca444a6e1ed6f", - "0xf77bc7d25e80792e69d8900f5e77a9eb276bc13d391720c8b8a1c608deaca05e", - "0x0979a99fa0f1a046c1da8204f2912b3c09c6dc7d37e0b6dba11935e5af8b1fb5", - "0xf9327d2fb74c22853685a3d41d6f63984a354115af69c7873650c9f8bc2d31ba", - "0x9fa02b5ef88c4496cd054f6dd26f292e389976b38a22760cbb505f068754a60f", - "0x1dff7dbca8c5d169d2ad52a4434a01ef647f29707e59d4543053a8bc7afc664c", - "0x051dc8f86a07ed084afe4d14f0b39268fc5858c908056401586acd0c9bd628d4", - "0xe6776e7bffe6398d66564b7e5993ef70c7347287842319e585c7f844002d9555", - "0xc5bc5e2c1e26900fe2492019e11bc4ec5595e1c8eb68bc1ddde65aed94ab83b1", - "0x6f868ac5f8f7071b3742573d8f4a3aea1bff632f6c5c15988572ea1983e9e535", - "0x086a27e853ff55ef97cf876e0d703898eaa0769ee4efadffacbb8c03de35657e", - "0x00aa9b9ffb2837aec2fd6444802437a5dcd4654ddc2df584e1ca351832a9e163", - "0xd395a77dd37f2e397456a5385e1fe4e781458db37d3a6d8da3b1d5fb7c5558fe", - "0x7567bd55454dc888c6aca9481ac1fd6641a93b3d2f5d1bc81bab97dd78f490ad", - "0xb7e980a03060221442dc0f9487a6f0614f5b83ea23350d2de3ff0ae4e93d02a6", - "0x1b43c09f68af74141ab23bcc3402932257deece48af74503e6ca452ecd59d9a3", - "0xfde1e03da25883b8eb78e3ac92914feab4e7294eb78a94e801876d59aa559f8b", - "0x807aca1ff5c2132a7c2b5d67de9c536968755dcfc32e8873fdd296f807148d56", - "0x8f3160048010ee14d59c26c0c2767b78350023327534ca27b086feea4cde5910", - "0xb26f5f50bdef8212578eb07f7afb52785944811975e1671a405013a0fcfb88cf", - "0x4e14680b46a09bdf8b9046f62ec5a28b8edd5704c3cc91fefb83646a5dd3eee9", - "0x792f84baa0644328b9cf5af899a154faae97fbbae7a1e7874e6219085b271265", - "0xc4d26ab903ed6478fd9a0fa80382f7b4eecd25125b623c6dcbc4ebaf0d8b3fc1", - "0x906b46aba1de6a6e3692bea009597d0a8dc40b76a1c5a4476a9daeeed65c2b25", - "0x167fb1cbf8026a1af6c16ff3b18285dd9d934158a9b9ae75b90ba7895ca2b7c9", - "0xaa659098a8ed4e33e511872bc82cf758eb6c2b47e318899b4e4385bd32fae051", - "0x757c86586e3307882c6d966c2a88745e4bc1a9002337a5d137d83ef3d4496768", - "0xf342de6ecb6bb5579cfca4e7dd4dedd2c1a685287dcbd891fb918226eb82cacd", - "0xb025cb5c7c1d64a8138ddd439aa95cd9a4c1162d628dae1f09bbf8a225a49f22", - "0x6ab070192b7919718111a5b1d74862c48a5705d4e9c6b47a3a81564180c3526f", - "0xf9a23794eddea371183902ae36ae4b54387b4b8b170bf848dc24a5ea39649545", - "0xfb9dfb946dd83814681f93091ceb299d417d8ec8d28e2ddb7843d913b699a6a3", - "0x44ea56c8eabaaef19c9fde38ad0f92c074039d93960b51238c1b11ed2315163a", - "0x66f8ce000b102ea085a18cfb17e99f242e0458d0b0d9d39b6a8653008fdaa98b", - "0xd573e736c4609d90ade296503843c9a12ad8f3795eefb32635dce70f2fb17381", - "0xa783abcf83917308a63bee26ef64b010e15b62014ed560881adc3ce78f80c3b3", - "0xf8406110d71475627fa02fe434b3270c353eca0ea700bd196a9c42dd2e6c472d", - "0x5e3350f159e6370a741af5879bdcc8b08c44538dabd162fef2b745e9ff2828c6", - "0x915c517a16a1dc3095bfb9550f24afc3a60f442c9205a7ebddc7eed37fb267e7", - "0x9ec7f72f0e8d5509e3ec337b0c879f730c16c55e4259526c0816d6891feb1dc6", - "0xc294284cac5de499512f9b12707b5f66dbb3b635c1cc6d4419b17f40e272e339", - "0xfbda44ddfe4b7c31b915fa94a27aa01fcfb22a9fcf0b557f9788bbf48b3f7f32", - "0x4418fe011a16af7da4645f300143a6cb005650ea96ce23c6adb4a82f60f72beb", - "0xfd3a00e52f4cc05610976b0def190199543ff3e423f09f8bbe5cef8315f36d7a", - "0x5c9fee100da6ec53ab1cf96ca9f369401853753eeaea30bef75f6989faddc504", - "0x713e2e28396b62bc63ab0a3dcfeabe977bceece4057a687d157320358f2dd08b", - "0x62ba90cbd06efea1d7bb7128e37e33d94a21b246b7eb7159d2708b5552247a75", - "0xe13d2697cfeea63e973c7493b4c54917a639fcf13eb1b327e02acee353bf84bb", - "0x8ed199e2fd654bfc9dfd5e7ea7dc82bea9f4535eb480c95984a5a6bc1533bf8e", - "0x4e5475dae57c548460d4ddd15583223bf2a4a2046c2ee56ca670892794a37d90", - "0x80409971b3e59ec71deb1a158a92ab6fe8318cb9adebcf583586e23e42d370c4", - "0xeb35d7c78965874110f67b77307deae4bb29dc98c01fbac3737409ede61df7c6", - "0xd358b672db99b007ac54ef3ea7c77c0eb0b4e9cc1437ca4eb70d71aaaf84b6f2", - "0x0ca4c79c1709b947794ae73dd3b73b357273c4e5cfc83ec85c4c2b649e10740f", - "0x704635b2335b5ad2be0e586e60770dc1e0548794ee2c1670cbfbc2ed0c1f64ae", - "0x6a6f2425c865216b49e7e73026fd2e9cc02f131c0050ce849f274c37828909f0", - "0xce31e62ac098db0fef0823941009b826d4e5b8e11f2e01208ee6abccb160c0cc", - "0x5669c0e6ccaa8abff08e944fe8bdcf0001dce9c10ef8b90d7f821819e028dcb6", - "0x37b59281d4eec501ed45d68c2bfac450d336ee5a18ecb7d2d812763d2cfd4321", - "0xfb52f084a5ffd63001f47acdb2ea62e91714af63f896c151100350d83bd2c7e4", - "0x67bfc4a94fde6e81b54ebe52914c0d73a053bbac22d6d64862e987e53c78ed61", - "0x49c141a8ac1124f40d06c569f49363a8af092d0028b02d8f4c895af2f86d0f28", - "0x5c81c85021692d5dd1bdbd3a4543ec86d49e674380a0e5f59d4400902d7e753b", - "0x9b46d19b0cee63461453a487bbf855fb4a7d70933ce2b7445d1dce8dbbc11cf9", - "0x50fe5cb0ddf34ed2ee8341848bc00df89fda05fbf0ebb1b34d12afe9e5ae4d1a", - "0xd56b4c4880798175c2db50c5c5d6845b5c1e7b6b33172ba107f5de075ed09def", - "0xe0928a4092e5ab5b9a34a8748c3ddf86a6a9ab50e85777c4e1e8088ac7b44ae7", - "0xfcde6d53ff8286afecafa27d608e94c07a4d78813edeccb73a1076772d1f7e83", - "0x1bff0dc76b0d8877a9ea133a023a5dc0f88559f16f0adffbf7b4d58b6be487a1", - "0x5252a71f6c9784b538ecd810bded0520f349f5aa748657a9b4422474eee08388", - "0xc4cccd5ae51babfe8e9f76af2c64227b0fd63bc2f4336b7820a32846516d9835", - "0xd07bc360f06a492a13047247448ba37fc9a167bd81ee63607976be269d245e14", - "0x9499022a6b509dd05eae2a7c799c332382b810843065e82ec72891b771cc3a8a", - "0x0ba258fb2666908035e53ec37a96ee13719779de43f14a57223b704961ef667a", - "0x4dec79cac5e3b414aa835c69bb2c580ccc03eaed675b61070f37b606ff4715bd", - "0xac640ffa67870bd91b1ff36450256fb1e5f59e65f4ef844875185c38f7f986b5", - "0x0af93eccdfbb75dc353ff31949329b3c96ffa93f63e9ae4a515f6e177e201d35", - "0x5a2d5ba9defce6db1bff0bbbe4eee170fe3b9c0ecb4a1ec6e16f904b55c07beb", - "0xd92f1c26de95255b1e0926341c35b5bfd106820dbdcb381400516d247062f3fd", - "0x447ec0cf1a32e8201cb92d0ad13adc6e664a07612bf38ce9c598c799cd947fb1", - "0x28460192df35aec41fe3992efc94da904d69e4b65e518fd2c530b38c1ec88819", - "0x6b0557132b0efe568a96f77fad4f1e18cf05670c007488052f347cf3461ee98c", - "0xb82f3f400a0edc100ddd6e82c2ea587eb5d56ea0f8faa379f8fa259f31ea0838", - "0x370b650e0bd14d9e5d749f628fca91ff47184473b6cba948e9ad3492a68bed5e", - "0x794e423feca451babc044923a79ceab62ff3a9a7e8947dcadd0d91f78a11cff6", - "0x7fe410432bfc7048e6685eb6bf0b4ae9808864a2e09ed1d373dc211f40c59da7", - "0x9f53d260f145de7b48c04829abd97989bc11d748af7d119ebf56948ca438913a", - "0xad5ad5cd5ff452e16c07bba12f0b34bb1c3c8c33d54db45b515cf6761d6a1cc8", - "0xcc6e322ea2c52af09d720524c85629019eac5ca01dcec5da887ad4b32157b9e4", - "0x5d3ddb9fa275898453e8430b26270e2812709717898683f0a3291291b29d688a", - "0xeff621bef47694a862a320a661b691b491e4a5c44e1d2026c9756f84903a6375", - "0x110e7059d20e4531323dcf981ad6cb9d67d56815c54394ab1b0b7af4235aa218", - "0xe44d27931b9429204cd2e9cba838f0ed707d5668ab55ecf2c906b2748fa1f16d", - "0x9d7166683764cde8445c58697df637cd48e35b3ad00f6e6d4bbf12844ba217c5", - "0xf5f6625754100a91cd7283d47e72cca73c39e5fa2e0405d427430bccd49cc2bb", - "0xd6b27ee81a557ff5d8947225c19f3ba023af18415a413acdf438548b24902427", - "0xccc665895dbabf0d8775c5fddf167b41579b0e6bff8fe99c8da00622334c8861", - "0x1986e5fd526af0a0e74ab83cb82c18dbd5a73fe72b94c8ca10318f8f6bd2d98a", - "0xd3f245d6f37b0c47506fc4a2b12d85ff87eb35c2f45f82c3e5bebdc7362d8cd6", - "0x53b97494c77ef9a843ce61b2a58231bc145b492b710fc3dcf79c297187993bbf", - "0x00d4cd1f9460a8625ed0a539257e58a9ab8051ff239172f0a9437cb9f0d1a11f", - "0x1c3d92f1e67b04c24db2eb04184ca7ccd5533540aca4dcbe084a70648c347ee5", - "0x200e39cb704bcda42f547c0cae54c0dd4c0b1aaf17469d69deaa67d3ef0d95c5", - "0x5aa208366dc50c2e9f0075b0e8aad396851f54a42c9ec1114c2994da0c2fcace", - "0xbea70811c324fb1553c4fc492521ce062756da0a469d8f98cd6c0f7a3a7b5e3a", - "0x79aa191ef9975049a5f2b8b7e2921a3f78d045b99b7a569a93680c5ee2600a18", - "0x233fd477509141aed423948a0c5c5b8f0ab8cc3c4836c0f44c8cd6c0811b419c", - "0x1737e144530678804416cd7c3c8200f54f395bf7712c9ade8340988e1e123af5", - "0xbdad2ae32cbfc13446adfe9e4101f32fb52b1e336c165a1125c917d6c1a1fceb", - "0xc20dedeedd63be4c10d92d9d0e7b47e20d503f331cb31d47ec9e99b915305ba0", - "0x262281f50e2892d8cfca3b8f8a6645b5931fb4601755f02fb863d719cd690381", - "0xb0a717f11ff1e3f1a4bd775792c7e5a75af6e56967ab1615be6b4941ca9315e3", - "0xe8f45b1aee0fb0e87148b316ee0ce662ae3bb2abd05e9666429fcf30893641b1", - "0xe7a439e2577c4f892921f798efa19353d236146b849c20253b07f03194b36286", - "0xf4eda704d4864d8fccefecc246fb683dbdeee5468a53dd11d71d71477d170422", - "0x308d977f739b7a8cb4810242fb447a8f6cad45389a127f5822ca2df48fa9df86", - "0x3115d6d0f174e00bf3e5704f57e44b62f7e279894ce62f39568ffe4367fc5a44", - "0x6b6e6354b8d1e1ab0430fe2a5068b2b2263ff453676dbfdce5da60a6d625f20f", - "0x9b8a7b122572f459d946e67a1c78973040bcf67a64327f689d4016c53afcc4cf", - "0xa367c0bc2e6029afec518db94fa79288801f5ebf2cba50980fc9754a40784d19", - "0xc1a017142eac944b5fda4cd66a9b6b6dc64551c3ce958e8811797433b96ec5d8", - "0xc9e3999a3f162d42ae52644ce4f765b5ccc7fe2a4c91cc59048eeb8c8086718e", - "0x67c135523c9703aa1f1b653d9279804af4cd81cd3de99e10a853b42d78143914", - "0x9af2390bb01b245c65f76454f1c80102871131417cc8fbff4b1ada056d04dd47", - "0xf4cec7d4f4a76c769021ab0dab848d668fd36e910259e45094d88defabd13448", - "0xe2a224fd4510ac5f8a6782d2eed14ee164e6bb92d3f0a7e5e71497046fe9f30c", - "0x9884ca2b5533c36e03fecf28470fd3c45292dc623fb220c5eb0a580781fbe724", - "0x078b1365c5dc77fdea6bb010fab6c4dede06e27386d4d5f205bf7e7857b03865", - "0x8f930fa645409fc1d7fb28637360489a5e5eaa6554857441c3fd8990243d9972", - "0x2e21ac485ed90007e8b69cf247c28b25361729bcc7501a3296f9c06dd0742888", - "0x46c3ea9e86ad55d87b6f5d50fd9c145bcb6ca90a4d570c1f86b5532b0762d8c0", - "0x87edc87f5ac353d64b0f157ce3e3180ce73fdd1f0ae9db1406e49581f1780de7", - "0xc940108991c82f871c3eea33ec93951c9bc33cd0aba1c77262cb6fc6ea16f4eb", - "0xba2f1d37cebd9bcde635bee4c0f260af8fddb43a84508f244b562dd1f7bdbe92", - "0x738e8d02870cf37ba27db2eb621d5f7b32a225010de41e3983ed389ebaa0e364", - "0x84af8e6e7ceb53e068bdf16091bfe966458bb5d3e4a925360411a213aaf46a5c", - "0x548302ac8a0af1ef5bb91d469f66f8669cdb2a07c432e7504a806e00487adb63", - "0x35bc3a284170d090e6fe053d3bd2a8fc3aa299d113b69b90340ca4c7e019016d", - "0xfe7b61c4388f2615642f9ffb44279f31a98c0f0d2f3f37b5f77ba09a67c12737", - "0x864581a4e526055847c252f6169bc54b5a1615de8e1cb7e80c649315b04a9aab", - "0xb8b4c67301cf9d3e91ae59a0394c8085737358bde297c58688b48a9d33c04205", - "0xa1ee0d1c7d50d37f96505ed06b166722f47fdd784b71bb3d47539ae40be96262", - "0x63b2658609397ec6a9e9d9d0a5b318b79b1e39348606ca53e0f84f6466d3aad4", - "0x2211abc4f8bbd6784aa191805465b10520a6c505d6257063aa9b911d5781fb89", - "0xefed35d1327328deece22922f08d3c3931c80c1ea9a8bc719b8226f38823bb6d", - "0x64b862e2d5a6c24d569f3352b8524ebfecfd5a3205a3200ec78df72d79a66838", - "0x6da8edf169a9c78307258a723c1ac1d96db20a7131018efad16f0606683c0f07", - "0xbae1427beab8c3e71cea57e5f9cdd55bc278c6d6073ae2628f0d3efbf9894a42", - "0x389bbd1b3fa390e8d3339cf5b018ec64d9cfc02bbcb801acad0857fe377ed83b" - ] - }, "nodes": [ - "enode://81863f47e9bd652585d3f78b4b2ee07b93dad603fd9bc3c293e1244250725998adc88da0cef48f1de89b15ab92b15db8f43dc2b6fb8fbd86a6f217a1dd886701@193.70.55.37:30303", - "enode://4afb3a9137a88267c02651052cf6fb217931b8c78ee058bb86643542a4e2e0a8d24d47d871654e1b78a276c363f3c1bc89254a973b00adc359c9e9a48f140686@144.217.139.5:30303", - "enode://c16d390b32e6eb1c312849fe12601412313165df1a705757d671296f1ac8783c5cff09eab0118ac1f981d7148c85072f0f26407e5c68598f3ad49209fade404d@139.99.51.203:30303", - "enode://4faf867a2e5e740f9b874e7c7355afee58a2d1ace79f7b692f1d553a1134eddbeb5f9210dd14dc1b774a46fd5f063a8bc1fa90579e13d9d18d1f59bac4a4b16b@139.99.160.213:30303", - "enode://6a868ced2dec399c53f730261173638a93a40214cf299ccf4d42a76e3fa54701db410669e8006347a4b3a74fa090bb35af0320e4bc8d04cf5b7f582b1db285f5@163.172.131.191:30303", - "enode://66a483383882a518fcc59db6c017f9cd13c71261f13c8d7e67ed43adbbc82a932d88d2291f59be577e9425181fc08828dc916fdd053af935a9491edf9d6006ba@212.47.247.103:30303", - "enode://cd6611461840543d5b9c56fbf088736154c699c43973b3a1a32390cf27106f87e58a818a606ccb05f3866de95a4fe860786fea71bf891ea95f234480d3022aa3@163.172.157.114:30303", - "enode://1d1f7bcb159d308eb2f3d5e32dc5f8786d714ec696bb2f7e3d982f9bcd04c938c139432f13aadcaf5128304a8005e8606aebf5eebd9ec192a1471c13b5e31d49@138.201.223.35:30303", - "enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303", - "enode://3f1d12044546b76342d59d4a05532c14b85aa669704bfe1f864fe079415aa2c02d743e03218e57a33fb94523adb54032871a6c51b2cc5514cb7c7e35b3ed0a99@13.93.211.84:30303", - "enode://78de8a0916848093c73790ead81d1928bec737d565119932b98c6b100d944b7a95e94f847f689fc723399d2e31129d182f7ef3863f2b4c820abbf3ab2722344d@191.235.84.50:30303", - "enode://158f8aab45f6d19c6cbf4a089c2670541a8da11978a2f90dbf6a502a4a3bab80d288afdbeb7ec0ef6d92de563767f3b1ea9e8e334ca711e9f8e2df5a0385e8e6@13.75.154.138:30303", - "enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303", - "enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303", - "enode://0cc5f5ffb5d9098c8b8c62325f3797f56509bff942704687b6530992ac706e2cb946b90a34f1f19548cd3c7baccbcaea354531e5983c7d1bc0dee16ce4b6440b@40.118.3.223:30305", - "enode://1c7a64d76c0334b0418c004af2f67c50e36a3be60b5e4790bdac0439d21603469a85fad36f2473c9a80eb043ae60936df905fa28f1ff614c3e5dc34f15dcd2dc@40.118.3.223:30308", - "enode://85c85d7143ae8bb96924f2b54f1b3e70d8c4d367af305325d30a61385a432f247d2c75c45c6b4a60335060d072d7f5b35dd1d4c45f76941f62a4f83b6e75daaf@40.118.3.223:30309", - "enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303", - "enode://4cd540b2c3292e17cff39922e864094bf8b0741fcc8c5dcea14957e389d7944c70278d872902e3d0345927f621547efa659013c400865485ab4bfa0c6596936f@138.201.144.135:30303", - "enode://01f76fa0561eca2b9a7e224378dd854278735f1449793c46ad0c4e79e8775d080c21dcc455be391e90a98153c3b05dcc8935c8440de7b56fe6d67251e33f4e3c@51.15.42.252:30303", - "enode://2c9059f05c352b29d559192fe6bca272d965c9f2290632a2cfda7f83da7d2634f3ec45ae3a72c54dd4204926fb8082dcf9686e0d7504257541c86fc8569bcf4b@163.172.171.38:30303", - "enode://efe4f2493f4aff2d641b1db8366b96ddacfe13e7a6e9c8f8f8cf49f9cdba0fdf3258d8c8f8d0c5db529f8123c8f1d95f36d54d590ca1bb366a5818b9a4ba521c@163.172.187.252:30303", - "enode://bcc7240543fe2cf86f5e9093d05753dd83343f8fda7bf0e833f65985c73afccf8f981301e13ef49c4804491eab043647374df1c4adf85766af88a624ecc3330e@136.243.154.244:30303", - "enode://ed4227681ca8c70beb2277b9e870353a9693f12e7c548c35df6bca6a956934d6f659999c2decb31f75ce217822eefca149ace914f1cbe461ed5a2ebaf9501455@88.212.206.70:30303", - "enode://cadc6e573b6bc2a9128f2f635ac0db3353e360b56deef239e9be7e7fce039502e0ec670b595f6288c0d2116812516ad6b6ff8d5728ff45eba176989e40dead1e@37.128.191.230:30303", - "enode://595a9a06f8b9bc9835c8723b6a82105aea5d55c66b029b6d44f229d6d135ac3ecdd3e9309360a961ea39d7bee7bac5d03564077a4e08823acc723370aace65ec@46.20.235.22:30303", - "enode://029178d6d6f9f8026fc0bc17d5d1401aac76ec9d86633bba2320b5eed7b312980c0a210b74b20c4f9a8b0b2bf884b111fa9ea5c5f916bb9bbc0e0c8640a0f56c@216.158.85.185:30303", - "enode://fdd1b9bb613cfbc200bba17ce199a9490edc752a833f88d4134bf52bb0d858aa5524cb3ec9366c7a4ef4637754b8b15b5dc913e4ed9fdb6022f7512d7b63f181@212.47.247.103:30303", - "enode://cc26c9671dffd3ee8388a7c8c5b601ae9fe75fc0a85cedb72d2dd733d5916fad1d4f0dcbebad5f9518b39cc1f96ba214ab36a7fa5103aaf17294af92a89f227b@52.79.241.155:30303", - "enode://140872ce4eee37177fbb7a3c3aa4aaebe3f30bdbf814dd112f6c364fc2e325ba2b6a942f7296677adcdf753c33170cb4999d2573b5ff7197b4c1868f25727e45@52.78.149.82:30303" + "enode://d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666@18.138.108.67:30303", + "enode://22a8232c3abc76a16ae9d6c3b164f98775fe226f0917b0ca871128a74a8e9630b458460865bab457221f1d448dd9791d24c4e5d88786180ac185df813a68d4de@3.209.45.79:30303", + "enode://ca6de62fce278f96aea6ec5a2daadb877e51651247cb96ee310a318def462913b653963c155a0ef6c7d50048bba6e6cea881130857413d9f50a621546b590758@34.255.23.113:30303", + "enode://279944d8dcd428dffaa7436f25ca0ca43ae19e7bcf94a8fb7d1641651f92d121e972ac2e8f381414b80cc8e5555811c2ec6e1a99bb009b3f53c4c69923e11bd8@35.158.244.151:30303", + "enode://8499da03c47d637b20eee24eec3c356c9a2e6148d6fe25ca195c7949ab8ec2c03e3556126b0d7ed644675e78c4318b08691b7b57de10e5f0d40d05b09238fa0a@52.187.207.27:30303", + "enode://103858bdb88756c71f15e9b5e09b56dc1be52f0a5021d46301dbbfb7e130029cc9d0d6f73f693bc29b665770fff7da4d34f3c6379fe12721b5d7a0bcb5ca1fc1@191.234.162.198:30303", + "enode://715171f50508aba88aecd1250af392a45a330af91d7b90701c436b618c86aaa1589c9184561907bebbb56439b8f8787bc01f49a7c77276c58c1b09822d75e8e8@52.231.165.108:30303", + "enode://5d6d7cd20d6da4bb83a1d28cadb5d409b64edf314c0335df658c1a54e32c7c4a7ab7823d57c39b6a757556e68ff1df17c748b698544a55cb488b52479a92b60f@104.42.217.25:30303", + "enode://68f46370191198b71a1595dd453c489bbfe28036a9951fc0397fabd1b77462930b3c5a5359b20e99677855939be47b39fc8edcf1e9ff2522a922b86d233bf2df@144.217.153.76:30303", + "enode://ffed6382e05ee42854d862f08e4e39b8452c50a5a5d399072c40f9a0b2d4ad34b0eb5312455ad8bcf0dcb4ce969dc89a9a9fd00183eaf8abf46bbcc59dc6e9d5@51.195.3.238:30303", + "enode://b47b197244c054d385f25d7740b33cc7e2a74d6f715befad2b789fd3e3594bb1c8dd2ca2faf1a3bf6b4c9ec03e53b52301f722a2316b78976be03ccbe703c581@54.37.94.238:30303", + "enode://5f7d0794c464b2fcd514d41e16e4b535a98ac792a71ca9667c7cef35595dc34c9a1b793c0622554cf87f34006942abb526af7d2e37d715ac32ed02170556cce2@51.161.101.207:30303" ], "accounts": { "0x0000000000000000000000000000000000000001": { @@ -3920,11 +259,13 @@ "0x0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", - "activate_at": "0x42ae50", "pricing": { - "linear": { - "base": 500, - "word": 0 + "0x42ae50": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x8a61c8": { + "info": "EIP 1108 transition at block 9_069_000 (0x8a61c8)", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -3932,11 +273,13 @@ "0x0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", - "activate_at": "0x42ae50", "pricing": { - "linear": { - "base": 40000, - "word": 0 + "0x42ae50": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x8a61c8": { + "info": "EIP 1108 transition at block 9_069_000 (0x8a61c8)", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -3944,15 +287,109 @@ "0x0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0x42ae50", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000 + "0x42ae50": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x8a61c8": { + "info": "EIP 1108 transition at block 9_069_000 (0x8a61c8)", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } }, + "0x0000000000000000000000000000000000000009": { + "builtin": { + "name": "blake2_f", + "activate_at": "0x8a61c8", + "pricing": { + "blake2_f": { + "gas_per_round": 1 + } + } + } + }, + "000000000000000000000000000000000000000a": { + "builtin": { + "name": "bls12_381_g1_add", + "activate_at": "0x7fffffffffffff", + "pricing": { + "bls12_const_operations": { "price": 600 } + } + } + }, + "000000000000000000000000000000000000000b": { + "builtin": { + "name": "bls12_381_g1_mul", + "activate_at": "0x7fffffffffffff", + "pricing": { + "bls12_const_operations": { "price": 12000 } + } + } + }, + "000000000000000000000000000000000000000c": { + "builtin": { + "name": "bls12_381_g1_multiexp", + "activate_at": "0x7fffffffffffff", + "pricing": { + "bls12_g1_multiexp": { "base": 12000 } + } + } + }, + "000000000000000000000000000000000000000d": { + "builtin": { + "name": "bls12_381_g2_add", + "activate_at": "0x7fffffffffffff", + "pricing": { + "bls12_const_operations": { "price": 4500 } + } + } + }, + "000000000000000000000000000000000000000e": { + "builtin": { + "name": "bls12_381_g2_mul", + "activate_at": "0x7fffffffffffff", + "pricing": { + "bls12_const_operations": { "price": 55000 } + } + } + }, + "000000000000000000000000000000000000000f": { + "builtin": { + "name": "bls12_381_g2_multiexp", + "activate_at": "0x7fffffffffffff", + "pricing": { + "bls12_g2_multiexp": { "base": 55000 } + } + } + }, + "0000000000000000000000000000000000000010": { + "builtin": { + "name": "bls12_381_pairing", + "activate_at": "0x7fffffffffffff", + "pricing": { + "bls12_pairing": { "base": 115000, "pair": 23000 } + } + } + }, + "0000000000000000000000000000000000000011": { + "builtin": { + "name": "bls12_381_fp_to_g1", + "activate_at": "0x7fffffffffffff", + "pricing": { + "bls12_const_operations": { "price": 5500 } + } + } + }, + "0000000000000000000000000000000000000012": { + "builtin": { + "name": "bls12_381_fp2_to_g2", + "activate_at": "0x7fffffffffffff", + "pricing": { + "bls12_const_operations": { "price": 110000 } + } + } + }, "0x3282791d6fd713f1e94f4bfd565eaa78b3a0599d": { "balance": "0x487a9a304539440000" }, @@ -30634,3 +27071,4 @@ } } } + diff --git a/ethcore/res/ethereum/goerli.json b/ethcore/res/ethereum/goerli.json index e3ba75927bc..4c00cc43ec0 100644 --- a/ethcore/res/ethereum/goerli.json +++ b/ethcore/res/ethereum/goerli.json @@ -26,6 +26,11 @@ "eip1052Transition": "0x0", "eip1283Transition": "0x0", "eip1283DisableTransition": "0x0", + "eip1283ReenableTransition": "0x17d433", + "eip1344Transition": "0x17d433", + "eip1706Transition": "0x17d433", + "eip1884Transition": "0x17d433", + "eip2028Transition": "0x17d433", "gasLimitBoundDivisor": "0x400", "maxCodeSize": "0x6000", "maxCodeSizeTransition": "0x0", @@ -48,8 +53,14 @@ "timestamp": "0x5c51a607" }, "nodes": [ - "enode://011f758e6552d105183b1761c5e2dea0111bc20fd5f6422bc7f91e0fabbec9a6595caf6239b37feb773dddd3f87240d99d859431891e4a642cf2a0a9e6cbb98a@51.141.78.53:30303", - "enode://176b9417f511d05b6b2cf3e34b756cf0a7096b3094572a8f6ef4cdcb9d1f9d00683bf0f83347eebdf3b81c3521c2332086d9592802230bf528eaf606a1d9677b@13.93.54.137:30303" + "enode://06333009fc9ef3c9e174768e495722a7f98fe7afd4660542e983005f85e556028410fd03278944f44cfe5437b1750b5e6bd1738f700fe7da3626d52010d2954c@51.141.15.254:30303", + "enode://176b9417f511d05b6b2cf3e34b756cf0a7096b3094572a8f6ef4cdcb9d1f9d00683bf0f83347eebdf3b81c3521c2332086d9592802230bf528eaf606a1d9677b@13.93.54.137:30303", + "enode://573b6607cd59f241e30e4c4943fd50e99e2b6f42f9bd5ca111659d309c06741247f4f1e93843ad3e8c8c18b6e2d94c161b7ef67479b3938780a97134b618b5ce@52.56.136.200:30303", + "enode://67913271d14f445689e8310270c304d42f268428f2de7a4ac0275bea97690e021df6f549f462503ff4c7a81d9dd27288867bbfa2271477d0911378b8944fae55@157.230.239.163:30303", + "enode://a87685902a0622e9cf18c68e73a0ea45156ec53e857ef049b185a9db2296ca04d776417bf1901c0b4eacb5b26271d8694e88e3f17c20d49eb77e1a41ab26b5b3@51.141.78.53:30303", + "enode://ae8658da8d255d1992c3ec6e62e11d6e1c5899aa1566504bc1ff96a0c9c8bd44838372be643342553817f5cc7d78f1c83a8093dee13d77b3b0a583c050c81940@18.232.185.151:30303", + "enode://ae8658da8d255d1992c3ec6e62e11d6e1c5899aa1566504bc1ff96a0c9c8bd44838372be643342553817f5cc7d78f1c83a8093dee13d77b3b0a583c050c81940@18.232.185.151:30303", + "enode://b477ca6d507a3f57070783eb62ba838847635f8b1a0cbffb8b7f8173f5894cf550f0225a5c279341e2d862a606e778b57180a4f1db3db78c51eadcfa4fdc6963@40.68.240.160:30303" ], "accounts": { "0x0000000000000000000000000000000000000000": { @@ -119,11 +130,13 @@ "balance": "0x1", "builtin": { "name": "alt_bn128_add", - "activate_at": "0x0", "pricing": { - "linear": { - "base": 500, - "word": 0 + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x17d433": { + "info": "EIP 1108 transition at block 1_561_651 (0x17d433)", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -132,11 +145,13 @@ "balance": "0x1", "builtin": { "name": "alt_bn128_mul", - "activate_at": "0x0", "pricing": { - "linear": { - "base": 40000, - "word": 0 + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x17d433": { + "info": "EIP 1108 transition at block 1_561_651 (0x17d433)", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -145,17 +160,28 @@ "balance": "0x1", "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0x0", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000 + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x17d433": { + "info": "EIP 1108 transition at block 1_561_651 (0x17d433)", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } }, "0x0000000000000000000000000000000000000009": { - "balance": "0x1" + "balance": "0x1", + "builtin": { + "name": "blake2_f", + "activate_at": "0x17d433", + "pricing": { + "blake2_f": { + "gas_per_round": 1 + } + } + } }, "0x000000000000000000000000000000000000000a": { "balance": "0x1" diff --git a/ethcore/res/ethereum/istanbul_test.json b/ethcore/res/ethereum/istanbul_test.json new file mode 100644 index 00000000000..0139d10c2da --- /dev/null +++ b/ethcore/res/ethereum/istanbul_test.json @@ -0,0 +1,121 @@ +{ + "name": "Istanbul (test)", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x020000", + "difficultyBoundDivisor": "0x0800", + "durationLimit": "0x0d", + "blockReward": "0x1BC16D674EC80000", + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "difficultyBombDelays": { + "0": 5000000 + } + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x0400", + "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", + "accountStartNonce": "0x00", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x1", + "maxCodeSize": 24576, + "maxCodeSizeTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip155Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "eip1283Transition": "0x0", + "eip1283DisableTransition": "0x0", + "eip1283ReenableTransition": "0x0", + "eip1344Transition": "0x0", + "eip1706Transition": "0x0", + "eip1884Transition": "0x0", + "eip2028Transition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000042", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x400000000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", + "gasLimit": "0x1388" + }, + "accounts": { + "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x00", "pricing": { "modexp": { "divisor": 20 } } } }, + "0000000000000000000000000000000000000006": { + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0000000000000000000000000000000000000007": { + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0000000000000000000000000000000000000008": { + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, + "0000000000000000000000000000000000000009": { + "builtin": { + "name": "blake2_f", + "activate_at": "0x00", + "pricing": { + "blake2_f": { + "gas_per_round": 1 + } + } + } + } + } +} diff --git a/ethcore/res/ethereum/kotti.json b/ethcore/res/ethereum/kotti.json deleted file mode 100644 index 06d1d31ea37..00000000000 --- a/ethcore/res/ethereum/kotti.json +++ /dev/null @@ -1,855 +0,0 @@ -{ - "name": "Kotti Testnet", - "dataDir": "kotti", - "engine": { - "clique": { - "params": { - "period": 15, - "epoch": 30000 - } - } - }, - "params": { - "accountStartNonce": "0x0", - "chainID": "0x6", - "eip150Transition": "0x0", - "eip155Transition": "0x0", - "eip160Transition": "0x0", - "eip161abcTransition": "0x7fffffffffffffff", - "eip161dTransition": "0x7fffffffffffffff", - "gasLimitBoundDivisor": "0x400", - "maximumExtraDataSize": "0xffff", - "minGasLimit": "0x1388", - "networkID": "0x6" - }, - "genesis": { - "author": "0x0000000000000000000000000000000000000000", - "difficulty": "0x1", - "extraData": "0x000000000000000000000000000000000000000000000000000000000000000025b7955e43adf9c2a01a9475908702cce67f302a6aaf8cba3c9255a2b863415d4db7bae4f4bbca020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "gasLimit": "0xa00000", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "seal": { - "ethereum": { - "nonce": "0x0000000000000000", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "timestamp": "0x5c2d2287" - }, - "nodes": [ - "enode://06333009fc9ef3c9e174768e495722a7f98fe7afd4660542e983005f85e556028410fd03278944f44cfe5437b1750b5e6bd1738f700fe7da3626d52010d2954c@51.141.15.254:30303", - "enode://ae8658da8d255d1992c3ec6e62e11d6e1c5899aa1566504bc1ff96a0c9c8bd44838372be643342553817f5cc7d78f1c83a8093dee13d77b3b0a583c050c81940@18.232.185.151:30303" - ], - "accounts": { - "0x0000000000000000000000000000000000000000": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000001": { - "balance": "0x1", - "builtin": { - "name": "ecrecover", - "pricing": { - "linear": { - "base": 3000, - "word": 0 - } - } - } - }, - "0x0000000000000000000000000000000000000002": { - "balance": "0x1", - "builtin": { - "name": "sha256", - "pricing": { - "linear": { - "base": 60, - "word": 12 - } - } - } - }, - "0x0000000000000000000000000000000000000003": { - "balance": "0x1", - "builtin": { - "name": "ripemd160", - "pricing": { - "linear": { - "base": 600, - "word": 120 - } - } - } - }, - "0x0000000000000000000000000000000000000004": { - "balance": "0x1", - "builtin": { - "name": "identity", - "pricing": { - "linear": { - "base": 15, - "word": 3 - } - } - } - }, - "0x0000000000000000000000000000000000000005": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000006": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000007": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000008": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000009": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000010": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000011": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000012": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000013": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000014": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000015": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000016": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000017": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000018": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000019": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000020": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000021": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000022": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000023": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000024": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000025": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000026": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000027": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000028": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000029": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000030": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000031": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000032": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000033": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000034": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000035": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000036": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000037": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000038": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000039": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000040": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000041": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000042": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000043": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000044": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000045": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000046": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000047": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000048": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000049": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000050": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000051": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000052": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000053": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000054": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000055": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000056": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000057": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000058": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000059": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000060": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000061": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000062": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000063": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000064": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000065": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000066": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000067": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000068": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000069": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000070": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000071": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000072": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000073": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000074": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000075": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000076": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000077": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000078": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000079": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000080": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000081": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000082": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000083": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000084": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000085": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000086": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000087": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000088": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000089": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000090": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000091": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000092": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000093": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000094": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000095": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000096": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000097": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000098": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000099": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009f": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000aa": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ab": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ac": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ad": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ae": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000af": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ba": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000bb": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000bc": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000bd": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000be": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000bf": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ca": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000cb": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000cc": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000cd": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ce": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000cf": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000da": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000db": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000dc": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000dd": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000de": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000df": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ea": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000eb": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ec": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ed": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ee": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ef": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000fa": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000fb": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000fc": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000fd": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000fe": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ff": { - "balance": "0x1" - }, - "0x25b7955e43adf9c2a01a9475908702cce67f302a": { - "balance": "0x84595161401484a000000" - }, - "0x6aaf8cba3c9255a2b863415d4db7bae4f4bbca02": { - "balance": "0x4a723dc6b40b8a9a000000" - } - } -} diff --git a/ethcore/res/ethereum/kovan.json b/ethcore/res/ethereum/kovan.json index 22a1974d325..e41127c3c0e 100644 --- a/ethcore/res/ethereum/kovan.json +++ b/ethcore/res/ethereum/kovan.json @@ -7,17 +7,31 @@ "stepDuration": "0x4", "blockReward": "0x4563918244F40000", "validators": { - "list": [ - "0x00D6Cc1BA9cf89BD2e58009741f4F7325BAdc0ED", - "0x00427feae2419c15b89d1c21af10d1b6650a4d3d", - "0x4Ed9B08e6354C70fE6F8CB0411b0d3246b424d6c", - "0x0020ee4Be0e2027d76603cB751eE069519bA81A1", - "0x0010f94b296a852aaac52ea6c5ac72e03afd032d", - "0x007733a1FE69CF3f2CF989F81C7b4cAc1693387A", - "0x00E6d2b931F55a3f1701c7389d592a7778897879", - "0x00e4a10650e5a6D6001C38ff8E64F97016a1645c", - "0x00a0a24b9f0e5ec7aa4c7389b8302fd0123194de" - ] + "multi": { + "0": { + "list": [ + "0x00D6Cc1BA9cf89BD2e58009741f4F7325BAdc0ED", + "0x00427feae2419c15b89d1c21af10d1b6650a4d3d", + "0x4Ed9B08e6354C70fE6F8CB0411b0d3246b424d6c", + "0x0020ee4Be0e2027d76603cB751eE069519bA81A1", + "0x0010f94b296a852aaac52ea6c5ac72e03afd032d", + "0x007733a1FE69CF3f2CF989F81C7b4cAc1693387A", + "0x00E6d2b931F55a3f1701c7389d592a7778897879", + "0x00e4a10650e5a6D6001C38ff8E64F97016a1645c", + "0x00a0a24b9f0e5ec7aa4c7389b8302fd0123194de" + ] + }, + "10960440": { + "list": [ + "0x00D6Cc1BA9cf89BD2e58009741f4F7325BAdc0ED", + "0x0010f94b296a852aaac52ea6c5ac72e03afd032d", + "0x00a0a24b9f0e5ec7aa4c7389b8302fd0123194de" + ] + }, + "10960500": { + "safeContract": "0xaE71807C1B0a093cB1547b682DC78316D945c9B8" + } + } }, "validateScoreTransition": "0x41a3c4", "validateStepTransition": "0x16e360", @@ -50,6 +64,11 @@ "eip1052Transition": "0x8c6180", "eip1283Transition": "0x8c6180", "eip1283DisableTransition": "0x9c7b61", + "eip1283ReenableTransition": "0xd751a5", + "eip1344Transition": "0xd751a5", + "eip1706Transition": "0xd751a5", + "eip1884Transition": "0xd751a5", + "eip2028Transition": "0xd751a5", "kip4Transition": "0x8c6180", "kip6Transition": "0x8c6180" }, @@ -63,5209 +82,6 @@ "difficulty": "0x20000", "gasLimit": "0x5B8D80" }, - "hardcodedSync": { - "header": "f90247a0d0c0f490e8e5045fa96fd77ce45ed983900feaab964b2b0ba3a2d3a6b5e0f842a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479400d6cc1ba9cf89bd2e58009741f4f7325badc0eda0c91e59c81193c4fead4f64ecaa349e97056409dfbb787772ea3c1e55e7582b2ea08be1332f5e7eac286c7e5e5ccf3760e47f4a919548c15e748ad4351bb8405f9aa054e0533aa3433f4d07afa226230c867fc1e2844b5f49b25aba8df17630d9f8c8b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090fffffffffffffffffffffffffffffffe83a26801837a12008306f78d845c9da0b09fde830203088f5061726974792d457468657265756d86312e33332e30827769841727682cb841d6db93a25c84287603ee26e48db0dd301753840cc7283d411d3febd0485e02b2520086bebfb3aec1ab69403f45f0d59249bf0d458f33aa268dc4badefc73c8d300", - "totalDifficulty": "3571337622391237938633151520660827524104528124", - "CHTs": [ - "0xdb9557458495268ddd69409fc1f66631ed5ff9bf6c479be6eabe5d83a460acac", - "0xd413800c22172be6e0b7a36348c90098955991f119ddad32c5b928e8db4deb02", - "0x7f2f84afc6caad6efe59a0fb9ff523bdd96efbb5bd22a0f67b3be8bfb8bb036a", - "0x7b7d86ae379af1c88f75a4b44d3097213b3461b9b7fd7333eac581537982dbb8", - "0x8525f5fb8149a1a64a83ec1915a78f3ce03a4a9afa4683544076f40f0ddbeb59", - "0xcc4a4b3e1bad427ad0d8b8ffd1c1c0789a08a67a09fdb0d1ffa4b1e71eb43aea", - "0xbaeb6b0b2fded26cf7f731603df0838397489bc12fdf8713a7cff865f009c5ec", - "0x3dbef76befeb39249e5c65d068fd97b8aea736714001a6dc2353b46d44f4bf35", - "0x306d007ef62b7cfed18e711d1cf449c009fbd5a8af7c9af66f3cda47678b478e", - "0x6b6038335d65726218d1d0301e12e792488a7ea879367c95263a20870ac91aa6", - "0x85ab7fde05c7cf468c4a65c5f7817e902f16038b3ce5e359490acae9c7a4f6b8", - "0x6784db2e15b6dffe0db864b2738804cf7b9804ae8932a2bb93a6bd2f2d4a8c2b", - "0x634119343bbd64c312a540e290b227adf6a84872ca9ea0fb7b3a7f5c2a4ba293", - "0x5836715c071dcaed0a79c77c12a02d54e6af55baed2c9325e10debc6fb07726b", - "0x7508b06556911cddfe4fc80e8a24603fd93cc79081759d4b7f19b2a6550757be", - "0x85de35d1995609af96aab65b1509dae77281a72932cda1e52e6af1d2c81cd46b", - "0x6575f31d4a44a479af6e4f53b7f0a5a1156c6965767b6e06cc3da8b46fbf0355", - "0x1d2ca9c1629b0e0775756536de5afef7dba628856e87dc84e5e6d16b08e12bf1", - "0x776edcae4d9a2e2a461fdf99eaeb1e717e3599f77934e058094b769c0a51f0b6", - "0x7fdbd14ca3a7408577c4e5b3b982edec1e7a95db784a34f9828ef5a30878f85b", - "0x64bdaef77b520a11c7ba92f494caade113a7682a565126cc4231d825412cee6d", - "0xc413f58e336df38771b3616ff1c9a1cbc881e4729dfa0a4bcd18dfca8e22f2d3", - "0x8da02b95474762995773eea74254861bf3eb62c099488aea97d0d271677a513a", - "0xf8d0c469352bd199cf28c997530afc31eb6b47662e1de2a1af05bcf31e6d5d00", - "0x88d9d2f7c1c44d4d0498c1f5ec21ea5d2ed2987e62f8dc6b17359b40200dfff0", - "0xe389fde4b8b06ababced10fb3756ce80b01a36751defff9ee0528accbf979b0b", - "0xb1f3785c2d15132fe17cfd455d015a01628ccf0116006d57de6425ef32bcc63e", - "0x9b2b9e6c015d85d3ba14bdf6ba6889db43cb8dd02dd020d1a01a4e7831604f03", - "0x8b22134cbd6b94ed1d17c89dc73771a3d3df57bc5c4b3d1540631c18dada7e4f", - "0x0a08bcb5fc682d91f0f95f934b4a38a71afe763b4924c8d049e407dea2bbc467", - "0x1d67c81b3c9316621088b42599abaf7e7cce42d60da44d2eafc8b904413b2c77", - "0x7bbbc5f13015a3ddfed8d3abc1d85ddd4646af588b534656256d894677c08c0c", - "0x2a442700726b19f5c82f756b08d07e9bc2777fe1557c079cce7a391684fdb59f", - "0x94a08a1519a9261f664c972e0c56b4628c3a0ab25a245abb4957cddc8a8f4754", - "0x5982827a354ee3703427518938a9b5b281249c04265db359bc47a7061f994a3c", - "0x3dd33939d000789b3ae1221b47d80ab4b8b248f79c97ea69c10148a4f8e946b6", - "0x37e5bf322012483f8e702f8a2a511b5f1225293b7c1be743cc83c637ec83c5f5", - "0xe671341e7a0275c306d55183e36b06665d2380bec4865c4c7cba51612446cef7", - "0x57ace89c46a589ca5150fc99028f9d586c4a8b1b6cf7af8b17ec1a16ebbdf8df", - "0x633ea492c2391b957bdac23f5a9428a5ce69882dc90db968e2882304bdf2b560", - "0x16aaa8ad98f3d2c127ca4dee2c9e1a36a0d4009447240e3297484d7cb2d5ce73", - "0x21abe93df9c382e5897599f54a31d3f46ce8e5e969c655c61da1125e6abe45a4", - "0xb01895100c1b028bfaeb1a126c4b4208075ec2a15fbc29a6499edbf939e7866a", - "0xd635cec21335fe1a644ec8913ea79ea987b815964ff883f00fc01c3a18c9d7a6", - "0xe5c761d35900479ea482e7804a056ca810043389f96ad208f55282e2e7617000", - "0xfa73e547f6a32b8154abe367a0138219514bd0d3fc304c742cb8b3d1eb8f70c3", - "0xf9de607cdd47e7337c815f4fbd44d983da6e38d653c3d3fbf2c6ac8d33707a63", - "0xa0d2cde0aec685c1d6cc05b21bf59817f123700e778cfe5eb80eb11add7e3dbf", - "0xaaaa3bd9f11785b477762ea0a3fcbc983c8a11b34d64475d16bc8c7d43904b9b", - "0x0031a12ba8c6e7408c7ce7987179b7534f1aea95a10aacbba0dade542ef4e1b9", - "0x101544e606fc49a7555855b72be050aa8a7a77e1e8985c3f54dec8ff3bb49a83", - "0x62202e7ed22ddc5d0cb3ccdf81770d4ed935e771be3d689c4dfd24afdb9c20e0", - "0x312cfe1c896a2624e9b2096ef5f3579b543d76ae166cb468e76a3b3b7fccaec7", - "0xcf1820f096d4b80808c50fbbf8de1b57da7b399a6f40312a293c4b5c7eb6d73f", - "0x1dde45a5e8ee62605753259a78e2c34a74cd5cf053a4c045ec5467f9e1311161", - "0x6b6ee017250b468ef3ded726892be7e8dae77c9e9e998b7e9889a34ff5da0cfd", - "0xb82bad6b19786930dd7159f35a4a24336d3f3b5b905b43f2a58165e0845b881c", - "0x77d20bc08ac7a25e115a1d23157c35c6ec22afd89948daba7ebd4809c3b95a2d", - "0xbe24e34aeb20602c184302bb10ac1f424226a985741f34faba4f346d8937e737", - "0x505e5b92245ecfde4a870197f7ece1ad181b6e8be4efce08763a37947122f4ce", - "0x6aeb3807dfe7a6db67278b5b6b6ee9ebb8996034c5ec82b3446a8c2efd3588fb", - "0x495f38afce7862a6614ed3d35531994e8d1e81dede4a4c9702922a3c06084e20", - "0x4acef831186426af96b90e1007561bffe2e8074bb33955dc36cdffb85e61174b", - "0xb2d7638804d6ee3e0747a4de335a26506898cdfd9221fe7e7e464feb268136b1", - "0x1aaa327ed400e0f6780fbaf818fc1f69c6cf407ecf80aa4f769ce42bffed7cdc", - "0xb955281446f41ea2330d07603803e148d5f1a2519f6233cad87dfd1ce4985259", - "0xec5f0c5f001e7897464a5570543060957fe20f88a905d3769615355c240e4859", - "0x18f872e3796787ef015a99bb3bc5ee4d31eeeec260213da70bd452f64de14c6b", - "0x9df2268daa9354a8264b5e7428d390a8d813266188df966fd58549dbb66898bf", - "0x6fdf62c4c161997e854846ed44e2096ea948c40efcdba2479896ba87fe484138", - "0x206bf3386af35a010906ea7a5263a71fa47fd317d23b57e448e49a36082916ff", - "0x4f627219247a8a93fbaa32b7f95e84b45146752d245e54299e7f74ca7070b2a8", - "0x3235819426f3a8b7affa6b38c0627c8efe0c2abd1eac7f8d6545e4332ebbf4f7", - "0xb5e514ee180f781ffadbfacc1831926f24cb305c352bb76ed7ae0a8247e792cf", - "0x6f287758e18c7967a5d52c05f8458e2d3ab2ec86fe0ef1d3146b99d7c3e3f5b7", - "0xc17dee632a1da285bc78d6c83715810e0e003c65c61c4105899b440487bfd126", - "0x173bab1413b65b801e136fe2dbf335b7d1f8689a127eb549bcd24f55b0bfd979", - "0x7e85be26cca16f2f34468f1aadd30a4766e4fe9d3746455f301f7f6e1baedc6e", - "0x85a0a9be1ab79a754c8875688b19a12719087e09d4cbe03f29965bbffee8437c", - "0x9c1c826a39e48b1b8d9d88ed29ca2d1864425d28e273695e35168eb01179ee62", - "0xf88831327094e7ec631fb9cf42719f89c4bceefbf25130784f09f4fa0a3b0a05", - "0x813e01a9713693c11c2781ac98604e51d7c7f610effdac795f74d538f7e5ec89", - "0x35ad80a70b0086c0a762ec29c577b031ea85132f777a324410c3870128aa91b3", - "0x6db9c2349d95bd51dfcb499ff26e90bf73e999f25b693ffa232c573f67164104", - "0xcff82a9fddf588d3d40e642f1eba1b9d24ece27131104e2e3995d6643e8afbf7", - "0xc6bffabb187ad144e33e7e3867f94f754e7e479c503d03de4fa891d294b175e6", - "0xd360ab92cf13a519f5bb41ea825e9baac0bfdf2298220c5ea60304ee9ac15617", - "0x8920024d447d0635cbe88f642966da52c1ef74d3ddc6c1c2df1df80c0a0014a0", - "0xc64a9294f5ca5fd1e1e05403e625222568c32bfd2f2f95bed2ad532b4c3a33f7", - "0x258ec76b8532f1f5169b44d8f5e0c1ddfc19fafab4cfce7c6afddc8aa4978cab", - "0x47f2aac68456bc931a0a8c458b802651173b1ebda2e032fb9541139352148156", - "0xa87a36800ed7e27bbee7ad937c1ace3ae08e2ae50a6673af70a2ec6e44ef1ec5", - "0x9182c6debf58629bd0d37043cc70b3c5e4af18b8aaa23cc5b455f009d1686371", - "0x0fe5abb9ff345ca06fa55cdfaa0804a6523c3ae162638e95edee4ba092eec443", - "0x24d7d4eb0c17869860d83e8be8f7579212cab60655aea5fcf070cd1461191357", - "0xf0971d8a37bc9b6a54d37ac7cd90aa4766cfe78cf8a6f376dac2304cea05683d", - "0xdb3e2e62d2da694788d2a0aecb2226c01de2b9d655421a079e2bb34092e7fd8b", - "0x57100a2ff274daa465da031c86f9b282fa03724c93823399c08d6b014054d207", - "0xc81fd4efe86a9ad97415928ae213959f2a716361da4a2937ceaab7358f105382", - "0x5836a5c844e45ae687b388603d62d28d81cc98ee1eeea6a34dad79d7ea6a599c", - "0xc98ad6c12f3f371a69eccb37407b43d3185b68380dcefae448b2e43db41afeaa", - "0x33ea1db64fad9787532d20f7739dd53a35fa810db902f2c33dfec4d652921073", - "0x3219209ffb0a9c5ae33779564dbf8a9618fabd1c0830d32a06dfd5a3990f7dad", - "0xeb6a367ee7f10c5d2882d86850f4a2680f78ce9374b7218d7cff11f0b01ff336", - "0x026bb678fb224a4e62c0d31450cf5394f4f71b9519106a5e92d58464edfaf63f", - "0x040ae0a25cc2c70fa710d394be7212b81128d7593a5c210549a96bf391a2f58d", - "0xa32caa1d1b36281e2c917f1951b3b0a0acde7c78f775681dd09c7cb81a2f14be", - "0xfe532be3ae441c5e9ec98fa0e006fe8529dad0ae6ba384300eb2198841014391", - "0x49d3b4aa6742d68b59eea7b44aaeb0693670c7b3b43788e93cd99f10d7ddbb7c", - "0xb205da5396369be6b68fa19264df3326c2bc825d88814843ac4f654cc7f921a7", - "0x57521341b29c1ed0cb02e8a64c43155e6020c4d8cda103ff56b2d2bb0cacc749", - "0xbbfc055f310e3930447bd33092081917d7661056ded31d3828b9a4eaff3649ce", - "0x52fefff36b4a0a9c2ce8cda94cb792a8260507df759ae2348ce79a6df3a2aa17", - "0xc4a7420741c5e8e041b8c7765b5c48ab0ee01fdf780efc95f820fbf549e5331e", - "0xbf90dab5cfd58fe4899475a911afd460ed0bed6e79487d9739399f7749520642", - "0x1f27056f98106e007efed121b017e82fd5c9b2c05de65cfb28fa9f67edaa6eee", - "0x6ebc2d4f7bc27d116b499dcbbaef72f8dd053559dce743706ecae5ce5338b508", - "0xdc6eb02678ccd7b2db8a9a9272ee53fc8110415ae5e84fbb8bf981eb25ccda51", - "0x3e261edddd8d34ba379cf163dab618aabfc198ce81bcdb1484b71a5bc4b50fc5", - "0x128bdd0f27a6d741399010b26f4056f0a919e7c1bdebb1bb69f22d32c80a2b88", - "0x68a749488b6e5bbeef10c98b678f67d1a7e53fb62bf502dc3743bf3c9138247a", - "0x52dc462248ddfead4b4aa85f4b2179280799b902519e83f721a849cc2f2b6037", - "0x7d70e4fab02e6a462e4688e96f7fc56a4fb9a945da942925ae039c6f13907c29", - "0xc746bf3861b3b63bd15b3cf0f0b6c0c331da578c35705176b90dee8a5b1e0d6c", - "0x4e251d7c4b2ae114b2f11a532fa060f6f7b1c4f0bc760034fe4e69c053a43746", - "0xe67185a040a731762897ac2e07ec2b955644d0784d18c6351c805d28e83f776b", - "0xa8ed7fd02d1dca89683183877cdc2a4c9985b48141d7967b10b38b59b831cc6f", - "0xc1020b2e8587f3c967ea138ea755437d2660e69abe8caa0d9bf9da9201b50750", - "0xd789a8a4a025f5725759e359859f2e15f7901a077d7683e3572d9ddba15ae94c", - "0x54f4372c804a66eb887614df08364d0e6b1f10c090584619c89c43962f513eda", - "0x3aa00d2431ba1c4c03452536047bdad3a5af547d2e16baad0bfeea0e7a8c3722", - "0xb341d41f92def7f5bf2d7c9a39b9fcdb2892b95a63ffd7b643bffaa523783dbf", - "0x9796b217efb64cc5f829a668989bb1915206e016c9714325f068b8cb2cd303c7", - "0x5deef3457cc95fae69c47f35422ca8731c80da50fd7f664583d13bb44d2f9ac4", - "0xa381a97eb8cf01a9b162666aaf9dd3142c6b2a2247830faf693e894ce99809de", - "0x7418829d2890fc2514b6e4da7b5643af269016ce2dd31d1aa7333c66c1273a6b", - "0x65466713071b4b5abe6a9c9b0e9fab247d56d72fea481e96b65999e2a085de95", - "0x0f97a6523532b4b80a7b54c1850ac2b062ff99d0c7848457fc3465d0d60623dd", - "0xdd1826c79f92c089522f853f87b551104b8b173f1c1797a752f2e8da2fd506f0", - "0x86a635a38b956903ce95ed3af65257383e2d7d86e6b63a61deec3ea5c8acc081", - "0x4afbdcdbe4a647f1137d417ac3580a116022c87cd6e7511330c4f2494cd90a0e", - "0x1d179aa8b0cb3b27844da4f06734f199fca43b66319cd2f16a60722eda994121", - "0xb1f1e42972a0b99268f42f6149452019259565cfeecc3459a58c17ba69ec4ad1", - "0xb80f8b8bee43d9e98eba909691ed6c9e900e8757326f7d20b6e7ba741d404a4a", - "0xadfea6390b526ec42aec881cf8226b9ad94f28ca1e682237914b30564d887208", - "0x50619f2ddec37c184798dfe50b067ae4bc0401bcfc1ab4d42cc19627b7b183a6", - "0xa53a92d3d223652e6572a48ad7624aeb2bb24c10872d17ebefd41ead1579edb7", - "0xeef7725747b978d578e5b576bc18d5c6e7148b68c2389478e0102b7656e7f3e6", - "0xbda668b4961ca5f6951f6ac55ca3c03a66e475e21905dfa8e797cd4a5ef1381e", - "0xba8c3be48f94186114afaab72a27a21db0316856619b400645469511b2bcc165", - "0x44d80fc17e0d131e6e98a4567885cdfa5224aeba8616d8f369cc84f3b85566e1", - "0xdce65f6c57bb81f01fe80daa1c6869c890a4bdfaf0f016456098bbd13c376f72", - "0x0b63a4585671669622a3c751d5ff5433f07cb0e9f4dc0acd9dff493c6ed01495", - "0xa287abbc612e2d318488a370e792845ff01ba5f7d387a2f0d82c21fc3d236992", - "0xae83dd70c6cc9cbd2a418397401461687bfeb8fa6d606fb7105153e50b5dfce0", - "0x02e31ae89b0dd8f66b6590a1f65711145bb9154825b7c68ae5d76228d92f016b", - "0x1e38ed1613141cd046a2449540f1450ffdca2c1a8d9ec2f1ce8d9c74ee189d89", - "0xc8f4747dc7b311c1a234f7d7eff3e9000e11c38624c23519b889543d8d33dfb3", - "0xbb7aa989e7d116e3c288ed8fdc0c7884fb3c6183ced8a0ea63a9de037ebf49ee", - "0x88d5a00fda80c28fa6e6d0bf6a6891b492cd6c85526955cfdfbba2428c9353c0", - "0xab03bd8ecad4f1afd0ee84a2caee745d4b7ade0f32bfbd61db50be15688e4493", - "0xb9ee61e8ab9ad32da0728394b5ec53452014f6fd99a5409923cf75c8d739da5d", - "0xa4c357be81081b833b5d4fd3cf4644b2ef1a28fdf67a8e0b0b2af3e8cd6ade4e", - "0xfb56d0c3b65882317ba962e2953a75d9d9b43c9f40ea0894629dc09bb29f6712", - "0x0f354b29bf19612285957523fc7d487922795d8fe5fb0aa4019b94a1f13d89d7", - "0x61c6ec536ebec0a740ad39900a2a71cfae8e8ff81ce3a37c5a4354ae48d39ada", - "0x769d94d0b3fcdcf22eb6c36a7c5b18711c79f69c27e9a396476d03e92ab0d058", - "0x5009c84cc24149c0134790682e0a9bc88fa5a8d2bd650207309002c25bc345a4", - "0x32b697d3cf2ab3bfb9ef79d27f3e61eb423ec9d802f2256b1d8875df78ca5887", - "0x3b8ba94c607acdcaaa7f0f7ad30028ecaf4d505d0675df95c358607210a23eae", - "0x433ea023715de94c5885d16762c0caf40079f5377f3cf3f5ee871c6812182ebe", - "0x4758426d43b67ad4d6e7a36272a3d772b94dc497d45292b061ac655111c41240", - "0x12542964fe7e8c837cd8e20a0f80de473127bab6aecfcc5be266bc7bcee87ccf", - "0xbe7362114d69220ba08ce42396e92d87027a63c3287be0fcf633c8a2853b8c71", - "0x722e26ddb7fbcff7dec266ac0ca388353c3a34d8ee23bbaef579b7acda48241a", - "0x04ede2fbedd140490e0b215e89850b8bccb059cfd7a72bd2fc3118495aa4e811", - "0xf47ea26f501cc95c62c4da4c57e743bb20c4caf6e81b394ceea58bc64b1a77a6", - "0x6f208393c290c02f4a505a6e7b2420da859e7ee83583e9a41d709333e67dcf9f", - "0xbedadad9293e12d706c963f31fc06535dd23432f7b27d157e529cf73e4f5603a", - "0xbc3572c828c9c1d1f0c90f56bcee1fe414a413fdbb7362a416bf4033405276ee", - "0x084bdee3faf8a9e45990094d668f6ae0050c1427e3aa918223849357f7865633", - "0xec5a32f5ff6f3887e5aa33d6dd61d2d63e36520ff0647a9eff2355a7311a0fe5", - "0x13fd23c260c0298286e193f23295b67497fb00c60047017bc62f94f6a25d4574", - "0xda66b3d4773530c3a3b563a8721523dd10f4b598b3976aa2f3becbc15fdda495", - "0xfa981ea461800e9eedd73c1423291c69bb43555321e1c5710a052a12bdc68707", - "0x232b0ab7709616262fb48d32ff36284ceb5dafc823ff4f6d5b79e8ed12c7040a", - "0xd90bc0dc26f812e0f05433c30fd8e19e81a81ac589c49a528e86027e8a352e78", - "0x3666c6eaef02990c263e0611e90d793c82815bc0032c2ae16813e957659c6412", - "0x5b46e134edbbc9466540878a637ce22194f040f519efd98a52da8e9bff05595b", - "0x4bff68f29ddbf33950af965ccc3fad46c81b6e2371922abf27b88fb2d669b83e", - "0x67dbb166d4afd3d1c32b9ce9ed5da75a25eaa160f90baf0d46a74fb7ddb09dd2", - "0xdba720e169d3c1dfde88d08bdf445eb428908560a520ccaa1da84d2dc07369b7", - "0xeee7477387db069e7de5d10a8e77d7f3ac7d692b4d582b5256bac44ba3d8381e", - "0x968ffed5244efe79aa4dbe1bcc7a05e24b5e50d1fd4d92117e1b654707bd3c43", - "0xcff2abc22b0cb5865f9767b5f6fa4b1420e3b774ff4de6f593516db3511d9fe6", - "0xd710ff4e765bd0d5e2284c8a11726c4924b6ba159702214a6016555110c2a251", - "0x31523dcadfbc24f1d3011cc1cff03a4df5f2c10fc4bef7cf0bb807edd1c97520", - "0x079cf6974e4e64675de7ce75b1262e2fed194440c2789f6fd72110f105503754", - "0xb121c3ac31a421442faf07a4b553de22be8e1726a67b0feaa4d426e5b48930c5", - "0xc39a8aa81b5c0a95d0037e20f3043e16832cd4b4cfa78e59a424493cd8322754", - "0xcb30c5710457eb0d9ece9274fcf01a96758e6e48db64093b1a08f773f5bdd594", - "0x916203fb9c6cb117f46f66a6693501711481691c0bef0ba0563369841762c657", - "0xf6711b05e901583fe0b51177597d44354ce57364282e042e143f37185af0b13e", - "0x3f22f516c9fb33c327d2184b842edc2535dd6cf4969178c56ab98ac11996ea46", - "0xd51b6361e37ceb481bc6e37eb61432fdfe23d52f2b64aea70c0a12a3c79d2784", - "0x9a1e99c578e7f7a69b7d914c0f748b3f94b6b30bae1be056849bfecc74e31751", - "0x5539c971cb882506860bf724b3f3d05cc399af4640775ed805288468dd83cfd7", - "0xc54c50d46acb1fbe017364ebcb66b4563bc0ab60753b055a19b126a55c476bc7", - "0x3adb55f5b09858cb849ec9edc3adf5e3ffb8eac43434c1ccb817038610b57c08", - "0x5fa06980ab83374613079b9d61c53d6ba93d41af996f5d9fb5e5ef05d36d8aae", - "0x1b66a155b1ebb780d501ac9f6d14f8ce6c644af88b7f86f87b5815379258c494", - "0xb75860039c1886823bd489d9f3b92c4f6bc46629d3fedc9f7d6fde59a318518d", - "0xaffab6d2682c1641658270a5b63e9e46b0bb90c05eeded11079b8f7d33899983", - "0xb5aede6209f50349dcbdaf85ff444e2252bd3e8e2faac12b517552b493ce4cde", - "0xa7c3e97ca8efbde3c829f2e363c5aabde3afe2fde9a1d106f868e87c0fc0682f", - "0x84f7405932b9aabbda33ac93170ffb73ccd5c584f0f55c17af4cd66fc938f739", - "0x7eb2a3d7ef2dd3fc0fc337070f1429e9b472a5912f9217ce4777bbdd07911034", - "0xb208f1088e65776f684d8b097b847c98cee4e274d49b07918e4ebae9585c443e", - "0xe0cec73204567174e0594f4082d01d7b8e8d382a6602e40b35b3197ba2eead6b", - "0x06642dd13587031239255686a40625c53358c4ced6eab78c696413f3cad9c6d8", - "0xb91a9a676da327548f5ed89ca74608c9403b994ed8f0cefeb53a1b5b09e76da5", - "0xe7dec1d92e8d551a54e7e310ca217933375c3a3944371ad5975d1601d5c6d0ac", - "0xee0d0b46a607e2b7df37f24ee652dde4ff65c95eb94b93092452a56f6ef9c754", - "0x8ef8f5852fd8e1f96f82cd3f4bdc716824070b3eaccbc2cca5da345298b752e4", - "0xe801666d13de5a51eba7c9e1d149339023b1b56c513bcba2e37dcf22bdb261a2", - "0x37a53559f626a819de48d74f7e6b484ec90c1fac766adfe08bf9234a139b0fec", - "0xeb8d703867e169bb31f5390ec877a7c6c82aab53f144593b75fc1599604d6c80", - "0xfed3012f3f281de26b447003799eca69e9845c6b5be85bc42670a64487c92a3f", - "0xf70358dc2c4e9d449727affd9cf45ddefa0f3480d964f85aaeb1543427e062a5", - "0x6491b2c05e217b7f21094e96c3381164f9db86c480b0970a27d8e25c875f5b7a", - "0xb4561f6329dd718c03d7ef6be0224612d776b72511294d29b293eb2e0a7b5cad", - "0x89f4a1b613ee34e1728899eeb294c48a21c28da7035d5d036cc46439fa42c965", - "0xe78f95f15c015a18e4598e33f06f6517c4455cbd2efd0dec1bead6080f050b35", - "0x7c64eb0c96598f099d0e9383eeabdc303002819b35223f744bf8a2bb80cbe394", - "0x98e5b4eaf42e390156a730d9e80606d80e2c0c9203e5511af1cea5361af63165", - "0xa481b0af82146b7aeb690456acab564fe356b2b8a560c4b5ab46817167bbed33", - "0xddac876def8620f40cc43a04c8d939fa12bb6d985223caf955e8174c46578bab", - "0x10a97a5a30805c28828d6e4eb4205905845d121149b7146e7373cdad4850ac88", - "0xe14ce88a794100c126707edb5a99922d394e004ce5fe178fbd02c8a34202d4b4", - "0xc3f18b1d11de7706063e2aeee7159589f64539fee39db233ce6983b24ffff076", - "0x6ae533d2bf3f35c6f1f256b9d1fdd863d5394956ded7a80b56ab1e12da78c93c", - "0x79ec4573d7df2f3b48ea706582d48d29a8d1483ae29577fd85b11c346a22e3a9", - "0x263652485fc477fc5407ca079eb494d4618943d01ffbc27ba3824fbc34e6bd63", - "0xcd414c2dfd20a303c9fd623d847e0387cb93cff53d006f335358894be35ac0b3", - "0x75d23d879b23aca1f2152147212506b0cc789c3e15fa8644a6c89a10ac926336", - "0xe166233fed0bfa647b84b95b4a04553087b4f42784334f5017dfd2a8e246f0d5", - "0xc48f212eef40b227aa429405e6c63d25fb35e9889a33007fd1afcc2bbb57a8df", - "0xfe6e67fe4057baa0701a7bc7f98f1b59e8c0ef03401e564d51b9fbbb49a5d11c", - "0xc31d73c0443dad07f6eaffa3171c77c8da87c54780d4f374128adb79fee4ed54", - "0xa0ea13b2425ec84fea7bb9f121f0aa72cfb00e36019e63fee991606246569281", - "0xce7e8e4515b0ebfcdd0afdb002f9017076de88e278b914c902bafca65a8b12fb", - "0xd14a72a5605124a8545dfc4d7419f00e85083d558fc662dc17b3bb289107da83", - "0x3e89c3740c294a0f54068cf769091b1a976fe4e60ab4fa48b9c38c034c08e9a8", - "0x2ee8355965ba13e91d71bde2a72347709c97cc33bcaf1ecc6952c860b07c60c4", - "0x63a87a93a0e410e53146a60a46ca870604f8301e221fb5b470402aa2dd5b9c54", - "0x0bbdfc34316855dfa828a58923acd7ebc556c88a43b655d32bd46ec6008d8351", - "0x4721db737d99128133928670bd229a0a7fd2889e00e222378fe5f663e35587f5", - "0x4f52db42c0f03f342883d78d387ec913a65f92961276b0e718d4b5fa268aaadf", - "0xf7d28588843f736c341b8572322d1ab397e0c5863ac9a30facec3eae0126f3b8", - "0x48940fceeebf3fca0496483b58228516b9c54404fddd31e573c951b12d017d2e", - "0x87542780e6dad24843883c420ef6fbcb2ebea31b073fe54cbdbd5ffb2c4b30e2", - "0x35bb6ee5defc7d7489b24e9e8659b7131ab9f7673960f3ab9bc9324d6034f3da", - "0x11719cf1a1d10bc3c4a48ab177ad65039ff09d1dbb34b1b7c15fa78b5506b5ce", - "0xd086c0fa8b0a1f7cabd9bcc5a1e36137f59b7ed6d91057defc6f62fbbce73956", - "0xeb461e410c324c9ef9a0d240fc5f153e1af9e88810f1563767022723bbac62bf", - "0x10a384a0f6c927bc9efc21ae1b2988e79268de24065b91065b582aff4e0cbcb2", - "0x7020503d8d86fe9bce39301207283c9f9bea9d7789d0f1488506a4073d482d6d", - "0x93657d3b8bd95e33b9b13c3a4130a623d4ed6d8eca8cfde3e6fc9e16b3f1f898", - "0x40727606bcdee8714dc15b349561d8b7d293ca4231227384c56f1a8fab986041", - "0x5ba216663e223675ed7da19a7bbcb08483f6871cf11d911c3ba992829e40ce2d", - "0xe9eabb151a2d735f0ca36ac9ed65c612276a21e28b1a63b5fe632dba27207969", - "0xed88ead1835a70d30368a33955de7c4736bbdc22ba95f04aff7097589887b2bc", - "0xca47f392632e4684e54fd50f5cb8e6983c8c2085d4df8550e3398c4e2bfb9bb7", - "0xeb8699fcad43fac0bb54ab553c4f29c7d1a3ea6079e9d5218a14b675477576d8", - "0x912ab118b513c06e618f29f9a7273e8035b4723b3a21521a91a43100486759d1", - "0xc89d31d9d5bf2fcdcfbf8ba8c61307656bb5673474ea605d1e5d70ab1d7ba43a", - "0x8fbcad4bb94b91cec5d9ac2534666f019c807f8889d37e3107fba28002c7dd1c", - "0x3f606a227cd3a38e2d5da4c611b9069c109e9c291d390e5c1bb3b427be68c5d7", - "0xa69c47f146ba22a6f53562f4ec3d13e1cc5499db587095dadb4a89f6456be664", - "0x36a9e5d14b3fbc061dc83eccc0e6dc3f2a56f0a56e88a987f09d89257c2d2ccd", - "0x9d77184cdeaec05bdde7df7005467af592a625bb0a470e5b071d6cc6537a24b8", - "0x3f9634b50a524ff350dbd4e764d3c01d848b44400485f792d4ce4e17a3afed80", - "0x142e648622d2f13dbfa4c0e58f91ec0073455a6b327a2a2273fef05ca9fa7553", - "0xa4348c80308a3e0b0b4d290152bd09b24d2420281ff3240d2b54c406ba2ac003", - "0x95e86927eca33fadf4d35feb4514ecb90babbcf2ab5c801533a4d36ea8ccc87a", - "0xbefc0239b823cfca21afc0f57c144226a8a72f6acf86a1152d8a80ccb27475e1", - "0x1bdc16bd7d21c2cf818f4d3f5f1cab4a7d256ebb8b32a8464c6b6ca111c9ab87", - "0x04aa15ecdc0277166a77953b4596e2558af4295a342330ecf9d21b6221f21a40", - "0xb432d2660f59648ef7929901a0ce38ff577b4162a69ec92609c8ca1cbea5f8e1", - "0x985a13bc2bafb56e5ffd1b0e582790eff30786bbacc6e28ca57017d398f1552a", - "0x57c5399f6606f02bd06c9470246948e3d482ff9dec5de270f6c06dcc49c8c8dd", - "0xee471268956c3947f22dc8a6077c850f10c1227f50cd24ab7a059b94f3496274", - "0x97cc4534898ab427418bad275c9c21c18d6f51cead840bd5b800e27e1bb1f76f", - "0x1370774538f3c6aff104060091a573c94269421e5347d9ee26c9c5ec90956174", - "0x7b29a2dc9f4e8c3d43d3ec93d01484205583cfc6c8fac71ed0dd6eeab3c7e987", - "0x2127f737690dac5f24595b5d0b96e869f750b732fd8f004860b5510376b154a7", - "0x196f4545946a3a4b9d5a1c2ce3813e55245cefcbbe424eb50dc7126a6f7ce294", - "0x89a6c2dd239f741797b47166d4a97d8a6147b26c7f61471a272035fa6a19ca04", - "0xd4cec04b0a1a8a2240cd2745e53e55dd560c3da0bef625e3472a03c60e5e1346", - "0x482aa6954d7bfb528abdd0d2a44ca7256adc9d2b5c885f67af0424add0f85039", - "0x16d73722fc18c4c5fe53ec609695dab204ea3a0ac8d52356acc56377495dc715", - "0xa2fc87f2deaf252bc56f50d67e5ea24cdd23fc3a0931c69e5c7f832c57e6cf5c", - "0x95702d0ce28a00a2c52cac13db300b535e6be5aab6f1acd8f7f8f3664d8f1942", - "0xd7423e6ac1d49688437234bfcaff2f8075a6237b35b719084c6f9257d6b095ea", - "0xc1efed370db76aa0e6e31680e91ae03788ffb343c13700ed30012102a727ab05", - "0xad108c165dfc00e6db95fe8fb28a51a21f471d16093f761af92dc3c1a76cde18", - "0x579ff43e25ed4183ab6638a5b3b8ca5ee06343b410a47abecb154fa98fa701db", - "0x58446a3cb729e43ef984f52bd3a1c0c5bc2986b355993dc04826987c9a4c2b0d", - "0x916b6e17ed534b2ed79c4139250ced98cf81a39ce19092ca7593b8046b010fe9", - "0x5a85afb15b437acc1958c1a35beb35ea25a4e4bb2856fe82f4d2c257ad92e4ec", - "0x74f15f12f6acbbc1ebe68f82e5832c2581660451eecec1d2369ca842c0384927", - "0xe3724b93bfba09e69c4d3991f5bb7560dfa58f264a2ecefc1fae2a933fd81c75", - "0x4651a72f64ecfaa2b51804f6c02f0e341d43163b408786df50569668d6b1afc5", - "0xd67008562a459791d2de81c71369507475fc3727fb46f68ded3bdf313ae3dbaf", - "0xcfd841df8cc01abfdb09b47afd98caaaba19b900c7d4249eac819f24ef7ae98b", - "0xedb8f1bb736b4b16985f41b97209730b6f99d981fe3ca0c614d138cc9f3651e6", - "0xf03d65be04a3b5011007b48aa3eeae54b39166013f8f0977fe185036a2302360", - "0xdbc7e5449a0af2b69668ef3f860dbaf2b0e0053eb5f96bd5879f59d1535aac78", - "0x44e3d860cf489a017a5a0c99b7fe2facf87c7bbe8c641bc2ac020bd45ae278f3", - "0x8f2d4fb90e6fb5d1465e5e5368b36d44d2081842b2c3b1ac3c2789cc8e2061e7", - "0xb47a9bfac379a6414b66c2b79ced91d87f130b0c2f86c67ca5295fa656547b24", - "0x8975e42bd19f5ff5a6336f835a53e0e7d8ec1288b9f59c266e1da463fff8f24e", - "0x52a573f0165af4f0a8bbd90c8ec2539ff430ae8bb9818c2cbc2c91f82ec056df", - "0xba647605f77311d053ff9c76ac71e88b6d1dd6b4dd5296eb0b7fe3e07eb43880", - "0x1c6d4757e48f80ac0a247bc7b2649239d0284b2580248a8b6706f04ff47c1f1c", - "0xfe42314e663a9196f7bc6b795bdf2763f49467c9b2183eb02c25b5c79e20fc44", - "0x15b6054f899df7793f03ab925c1d8df2f94d6221552b890af6de5d8a5b7948f5", - "0x694798b4238b93838948377161ef5165af4dfbda5f0f9d8d2d37c083c3718393", - "0x58724b920890120f1f90ce8bc3e9c6639042b3cfa78123bc9917b8854315e31e", - "0x21f057c8e538eee76a95659618bccefab9a7a5ffc955781141a3eef71f3b3423", - "0xa5fec657cc7ab1b633023791470bb2c5a7f1dbfb3ccc4e6442565a791754f319", - "0x67a17493b80cf75ab905ccee157746c7ed048a8406fdd94af1c6916cfcb29220", - "0x9d5132754a73e0ba316da2f824f5264e851526c6a75bccd153cd41f232e1ba42", - "0xe9f6b8e973183e7b89ab72479003bcf07cd0d141521c23e4e4269f58f51692ad", - "0xe52cf37c47ddd837146288364ba448980c24dda74c6a2560a99ece3174c94a57", - "0xefb69d2b602cd8b073314ae5af7d6200d23335f1e46980c205ba0bdd979d0ec1", - "0x3e2f6e13459a82666bfc366941b39506d84b1b6a1c94e4361d51d30cc1bf4218", - "0xac8562bd8210325b1dcc8cc3c4214a97d04ce795c3fd9049c7296f0d855045a8", - "0x22b62db26899a50ef8630a9aac5cef645512355901ba14e8f1a3468610473756", - "0x24f7a40ba3243eed13a4c2ad7fb273a1fd822a3d2b8ec729b0e5184c3f791e76", - "0x1ea72f2b13fbc945e3ca51bd681b2e12e4ead38f42622ac3be54a1b89c826baf", - "0x95b26a476c67aa211235b0b177964422d037768a1c162526a53e73b2777b85ed", - "0x1ed2cbd748843651a8ab0a31b256d4d3c6bdf3cbd3db7e73090a679756880a6f", - "0x20e857d94a0038a51313c8a67d30f7aed8b335e7cebdffe8795ee03e62e5443f", - "0x9b807ff518a22a7a40521537b137cc25904b8d2607d7d9ce1f0e50f2327801b3", - "0x35e573f1dc47520f16eb43240dcddb95d167eb8e59430c608f17303a9c86b639", - "0xa06a21571951ec87f6cba9dec2cc9f1e55b4b4d5d3e2e9415d30fbe50370af12", - "0x7961da55526e7a67feeac00e625a1ca93607b7bc4d4d68142ba791e46d333e78", - "0x5accacce3901feda7004414e0d514129bcbb14ea727f34f58fddf4b928774589", - "0x0d66a4004a235178a69941894ccadc2e91a843c849284db82e90a088c3d3c6bc", - "0x8255a1650988cee8c57e30a5a8fa159f71fc2f3e23f799e11f5bb31b09c54667", - "0x7d6936163556f21f56185718a6b7de5bec0375a01f4c88467820d8c66dd983db", - "0x3e3c08ff6cd5f69aa8f85e9a5d8c91462cc228a41e392b7a544e2efb6af87cd8", - "0x4f401bca18b8cfab219430fd6807aff86867ea52f12b45ec224fa9dc318dd789", - "0xe14e32f37af0204cc56fb10ae891cb09b0c27a116ad42b5946dd1815f445cfe2", - "0xae46c70ae8bd612e760b614689d48b34358bf504a2414b660224312622917e4a", - "0xe8870fb901d8234646d87be0eb0b15d46f55c8d3765beb631dbd06da88d50038", - "0x997b7b18cccdd31b611bc01767a3be138f5ffb2c92de11aa1fa486a578c94799", - "0xecc5dea6b6e3d233f7caaf17e07a5f58febf9fa6d0736228da43b28cb5538dc0", - "0xb56b9b982a7915d85fef49df531e0a1e8e8d000b30da66104265eaa7317ee2cd", - "0x09601975476271e885e83f96799f15cfa6c0a67037b9d31eebf01cf510fdd752", - "0xab2e7c3c19f0d7aad30ff986e9b2c8a56f09906b851c53455af4d35d61b20a32", - "0xe5590d245bcc42d8efe40d677c42d840da1b32c49369bf352f46690bed9a4dfc", - "0xb46810ae0d11af6412e219bb99be0ac22c033433b1567a36a69cd962c9456d41", - "0xc296086ad6481ed8954c5542e4083388a4048132a952f500b2c362a19e8333c8", - "0xc39bf0d7e42ecfe35ab387932eb54ce0c1e502168ba5aa74b838aa17ec294d27", - "0x1ea48989819dcdf6aa0e9b0cc41d52a4d348fa80c8aaadb24091c517e3fd203b", - "0x58974a726103f506b2d8c904a8b942e247ca648678b7b98e484c203e4bf03ae8", - "0x0ccee08094a04e4db707f8d570c6098cb87b271a2aeeed08e0f589d2c162afa2", - "0x5a8620a1f85f856a8cfc105a05d4a935cfe626de46eac68e2c78c7b8fbb08c31", - "0x070101ef142836edb475e9e5bddfc821fbac8b2f8605ad9f52a1ae00bd0d1f6a", - "0x822efecd485d634912a4fdad41d72479c31b7cfaf664f3a0f78dc9ebb624cede", - "0xc6c7f14ff1b5618cfa096080c6fa59d301b3c2f516ed9f4eb0c3acdcac3e1ee2", - "0xbe85e53c24900f6d22564f33f05f1250c9f80b1b4616c0dcbb6f500f22d376b5", - "0xa84385d0d3f54995864695c96c5fdd30affe8dd9548b754edd12d80802706ef4", - "0xe12c3324e68b4efbb14fa5522ef6d2418ef7c397f390c2b91e30352ffb538c48", - "0x06cafdf319f45ac908dccc5e510b67be82635a488234d9b3fd016dd2f09667d3", - "0x236822c1c20ce528a5d9a985c2a2eaf8349a3ff1dc13cb7f9a8c7bb5bc39e9de", - "0xc438b7d70a69768bb81ab867d6772133a592f7cf770931eb51e893c106461143", - "0xa49a416419f910f762ea095d4e8eddaaa7b01b406e730d3bc1846d24b6952302", - "0x08c83875d8ad54befb643c8d8dc684358aa22c144b9448820c0a62e5ceb9e9a0", - "0xd81a30c959e06eeeab28bc3b95a1c8a3466f2552d51ec0bde69a1d1f107f5405", - "0x76803ab074a7170033dbcc33a68d2b497998dfde8b9ff86101fd54539d1bd393", - "0xf7b9503ce1753623091cd263ff23af68851445bdf97c166358b890ff9dc78f17", - "0xbef1f09e8233542c73ea380abf0115279469e2bbe3abd557b89d12b514bc7a7f", - "0x8ef2348cff8d0d40e6f1b0973636915adb3ca63fec663383b8afac1a46dd0e8b", - "0x9724f0ce677572033e30756e4fd356680bc64d171acf7483d5dfc936267d3d8d", - "0xe5fe7147af6d1d1d52d34714de41a7cf7bedf38bff1a29a25263beab0f59fe61", - "0x3782ed2e5a049f3d877c8480b590a5d2055918c17804fad7ee7a6c2a4358bdc6", - "0x9bb40bd9cbb5d20c8987aa4e75b0a8036d0d10d87c46abee889d5fc23d10c98a", - "0x75d9d23fb96bde45550b2cb8ae4759ad36fbcbed51f00cf1bd1d04d0f5d0400f", - "0x5a5f79793464475b35bd7fc427a745b76541ab65d62e500d93e5357e588948ac", - "0x903e3a38194c6eb3cc98c857b98ef68bb1fc988220b12138ab40141335b2b9af", - "0x9c5c6d1fc8f1242a0f3f41e6dd9cb7c1ce6dc644b1ce552b3dc0ff7db83251f6", - "0x77b89a725a743a7b31b01022e4ffd1f35f733461828f204cc75f4f00290a01ba", - "0xabc38d580c92f199cc0aece60899fb6967ca48a096b98991dd757ed1fba6c36f", - "0x61742ed64d417b66d67e4638d0bb60d95f88cca17cb7630b1254ac3c47739df5", - "0x47f7bdedee6e8eec229eb0f66fad208a8a05450c8b649e2dc400e1b5759f4b1e", - "0xdeea16c07180d8a4eb104e309343225a354a3adfbf1b8af733456a185f3ca669", - "0x6df5b47f5b11511f42b4e981d92d55cd9aa3373fd90735cfc4c594c516159027", - "0x5a5af9e8656afd3b52f4e27002f1be9b09c5e7a3746fac7e64f5973ed423c14f", - "0x1ccb86d370ee32261f76a3e9d5948c40489c26f2f4216535484d7bd586f2851b", - "0x313dda0f2ce1542eb1236a1d555959516abe06951460d77664650b6cb7613d5f", - "0xfcf5408ed6675b5d1c15629e02635d726777e63817c3ccac36d4726bf95fc4db", - "0xbf5f15c67971a4b624a8ee8f81086fe24f1940a084bbfa031d0b69b865d553c6", - "0x9fbc4b0ebc2a128d391b68be84f6ef7833d5413bd95f751b503799a54c55c3c5", - "0x7ba3b1dec981980a957680b3c125328bf3683578828412c3de197ad07d06565c", - "0x4d2ea255cff42df73c96d18fc2c372e5ea9fe80eab5c98a6c6579725bf4c6fa1", - "0x9793db50b81ac75df1846e8b8898dccfa822d69bc4f99f6bcdc95ace2e3317fe", - "0x21c1959416e88792275d6eba88e8fa503c40a1f4a4b78cbab5b94e6c1ef714ea", - "0x3161a8a5b42bcf792b44bc3f7fef3606978b6bf716effb5ca1b0fa6b85518ae1", - "0x59ca1b7fd1f09941c911a4a6d0365e7a852a420a43af73ee0bd76f6413c6f3f6", - "0x86db4d45c982f195667fefc3de1af4a17b083b6f3700e47568ea43829e686e06", - "0xe5a713d52d696cbe65220fb30a4183ff9121ae1868967eee002cc032fa41b07b", - "0x446668d8777d9666222a04bc9963f89873387535f485490adbbd15094e6cb9db", - "0xda139e22348743ae9d600e9e2f651a717b8864f7bc8a4881a875945daa2645fe", - "0xae22c6f51fa8c11daf898addd39665d7bdf6d266d42ddabd790accd6538a6778", - "0x1059bd6151a5a3de93e45f7b3c498225b759a46444717cef5e66879b6e0a43ab", - "0x893e490bd7a3cef98d878f4f55d22aa0456f934e16a5ed635bab5d993909b006", - "0xefff7bff0a97e0e51718eff0d61540e164cb131ebaf466f01516ebe956b6e0f5", - "0xe6eef674ead50579daf941c526fdb904419a37f009fd432ce0521835b7ea09d1", - "0x7cb9c0e3aed016176058c80a03eea8f70e6f0467cc744ee752d937422ddf455e", - "0x995c33746119fe51b3a3c71311fbd4d91810a2ad95a558101bd11ff1a9913da5", - "0x926048c8fa6053aaf47f8986f195d19f1a86a4e01d5b34c45c91a09f57be0cfc", - "0x18ff8ac452c3f84463c8d8d92fa6f5550149f0d9cc0bf9b9a6ff6f05a8e3dac4", - "0xb495eef7e11375f1fdd6fa2fe3f97e1b834c39abe60ef1eb75333f36474a57c8", - "0x8dd386ddc871b764849a057f98a838f0f66883cdaea5ce4a9728910d3e1d124b", - "0x344c77e81e60bb40be5a20fa14b7fdc8c4c696d523cfcabc408e04df7fb79259", - "0x6b55d08a7eeb8f800866f031cf194c0ac2292923fe0df0460fedddcc77ba59ae", - "0xa007e70a725287607f23c03a782cb36ebecd692b45c1273b62769ddd1d0caf1d", - "0xb196d7291e437b6a26d006634de1edd3096367faacaa2b64b6c52a5d2228fd43", - "0x7f4f82aaff92c6e348d8b52e3bffddc7d06780b3750630d40022d54e668ec761", - "0x942e657c2125a5f57351b6e5fc76cf6ff6690529034f65d1bfa18ec8827bb241", - "0xce436799308200b480aa234ee2a4ad407bed1142999a360ce8a3d2230bfd4285", - "0x9f3fe03371f56c4afa64fbf2379c7dda3752eb19077a4de4b412fb7b5a4721be", - "0x53f63ddffee8a93759f68730026c3a8c9026a1a61a17ecd8b98d29e3d24dd019", - "0x08c2112cf1abe207076a67b43f67402df0d9d99990cec96e10ca46f4799134c0", - "0x41c4a5929a07431a1a0918caa5294b9b0f706960afe8e75b0e4555e40e182d7e", - "0x188b2fbd1b54709384dbc8b62e21cb1cde0b5c743aa3c25d8ce5ba4557728a54", - "0x92cc75e85ad9a09407bd0c09d68436fc419eda5c29614e125ff99964e304c0e8", - "0xfad03371877721fb4477f73575cee54356114f0842222f6a343357a15d409568", - "0x6072b8482ccdfe974790727775e8c0c43228a724068a39e351a5c702e7086fe3", - "0xdea9c30a8f46fe7e34b191d90b14fe09aa61275a32063ccca2d138886419c60a", - "0x3c24861389a3d1a8ed5d2c71e052914d4dabca490be956b7ed63a2195d65bf60", - "0xa2dd585af11cc6d60b55ab7b5295db6f5fd49e34c58948ece2ba6a4417f53fed", - "0xd0cd3dfcdee7cf3b00e718b6086ffbad37e61b80df49c373371b6ea449e5dfe9", - "0x018a9cccbbee610c3d978afce046e1e729013fc7ae71a0d80b2c22054acc32bf", - "0x6cd7707d257de792f35bc5d5080ae5bcfc50188ec61b406743f0390e8f5e4ee0", - "0xf036437c80029ef8595f65cede2446255b436ab2b47cacebcbc513ee09bbc6dc", - "0x438c8373efeb9d010e459097f7eef12dfd469a5d447f5494742321fcad2f5fbd", - "0x97174ad7515662e1cde2300a93986316ce4b7ae00b7f35a0908cde607ef6070e", - "0x8527198f8104df2b8559ff578ef5e2a84a9f0ffd63d8faedf26a50b44a19932c", - "0x7e4b79ce0fa7dfb5b4cfb92a98a0f4de105ac1a931a34efe94c16e29d5b03fa0", - "0xc240072b872710ac52ef170ca53ab03b3f76f1e2c1cc40dd48ebec3d0636d6cf", - "0xf957c0f8495186575f4a1bd2fc19350c9fed57fd60a884999d943e5fdca35038", - "0x814f0dfbdf3cddeaa502feb0f13f879223d9f0a14d01d756c3b3e773b44ce581", - "0x999f07e2f640408c58cb9cc0d15b4ff6c90db7b85e98e6888f74beefce295c1d", - "0x71e4362451b4d259a942b2a0425a070a9f0053aebface053d9c02f9ca4a5ff3f", - "0x28a29356d506b43e9dc9fc05bf6c7533e6670f5ab1122f49ac572bc2c9b94b49", - "0xa6dc1aeb3352b65845baa0329491789262c71b444171e36a55f3e20ab5edc026", - "0x6a0d71aee995e2857459513c124ccf8f81212ef0f14ea8a99b24b395e763e2b5", - "0xebd35e5b9134079efbdb4759e0c2af7728b73caeabc2c2bfdc3f590d5eaabc68", - "0xf1a7367e7b712b141602d204924a0c8808c68b013006b344e30d207a3b8c179a", - "0xb142818bb0edf0b1cc120389d0bef91b1a7b3e1fe0058810bbf73d04cd573147", - "0x8aef2ed196ca7cea305211c656084a46438ccdb7d2549f24fe4bd3fabf11af57", - "0x934cdfed19bc7321061d3ad217879cd14def18b18f6d8d2baa625626214d39a0", - "0x16f1b202a884faa4dcccf5d408b3acb639a78aa1104d1bcae40182f71d94a1f6", - "0x5cdb53c18168fa65c252659ba594552ed95150a4f0f2b9391ea3fb56badfe814", - "0x304558b9ab585c1201af4e88848d81f5fbc380341bfe8df9e9ebd279a94a1c4b", - "0x7c13cf0890b8fea28d01b7446750a5f1429d343ba69525e3909f054495d30f41", - "0x38ff978ee475767c435e5cbf5c0ba9c10c48426a9839735b67e2d32028a1582e", - "0x7fc90ebe34bd2cea4744c1ca0f3968a304f7cbb133353cd0aa76b45d0e88a23b", - "0xb0da2f67974a7cd5a291665ee3d216d5f57df1b66e7a2f36dabbd97cc68bcd0b", - "0xc220942a0821ea338d4307365bd1be586dc8b3a89f2b0f60065c92375b1bfcf5", - "0xda1019e4bac45eae0717a35e7aad0c63e0063ba18a8e970cd631112a9baa3d3f", - "0x7efc5e52c0a590ceabce9eb4eab90c50a1bdf595b7a59b0ffb3e3091317377c0", - "0x2347c7c6cc6f741e484eac237688427292a65d01f037ab25419d3c17d538db6f", - "0x207e2f3f0e856faab65882a7d3cf09daa37f08406cb96e5bb4252c8d0fa7f27d", - "0xc425eaba8494a111289148c65db0c8d6a6dc27d16986b0b70ee4995723c878f1", - "0xf7778a3d1b7fd83b3d4926d2022cf823801a2749283c9ab4f86f87126b9c7e80", - "0x1316c7953bb3170efc234d13e9632e017ec4f58a30e4e062ead373b206461f1d", - "0x9b4cd6a0e47f2c64c0ba4d9a7565f74e956cf881bdc33b1176617bf9f25af5de", - "0xa972bf247d8428e97218ec0e9a36e3b7fad10ce9a3126bdeb2cac7e9b4e48e46", - "0xc6a4e22ff93e8c9483dfd8eb8f2c384c72fe69cc4f5644e8d68d419b92509c61", - "0x601a9a84099f3205c6aecd034b53f586382379dc39be605facbc2da5f793b990", - "0xb7ed45da8acd237dc9c265afbaa15300cc7cf72c740bbcc00eabaf4dd90cf9b1", - "0x37bcee935e0ef47df22a7f9833b3b3a3830efb1656059c450a7e6a144798552d", - "0x06e7ecbc0f4d4b16a24d4260d77d0161c5a627083589747c72f765281ca8d3c4", - "0xc45d35b6c6a81240767ee9d994c8e2f5823d8a4e0f51abb7187e234d6dd5076c", - "0xd3c726980fdf096fa314533ca3e882cb1c1a2a5eba4b9ccef7193e9ab7ce34b6", - "0xa6c23887788743ac8bdc123c7b21873889d538f7acc389c28d6ac9dd26da06ec", - "0xb8b8e38541b9143da71d68cb705ad5076b089b6174d99bab4b87428d8828ac9c", - "0xa70ec737a97e306a66b7ab8a7866d03d496bdac04a08a0ccede2b84583ee5d21", - "0x6d43013f53b03cbaa4c82a2d42f5336306301bc7a624542387cb0a822ce89a50", - "0xc24661eb76bee7b5db00df129db5c2038dff544a90de77809a447530e8200d02", - "0xfd291a2a58d472bb262b3e9b4b2c08f8ca40536082e05c081ccd502c77a78fde", - "0xa34be368c12bd1c05ada73b647d72da9595b0d618679905c2df760ff1a2b6dfa", - "0x4e68946f1b2d9f9a1c10f90b0d354084561bf5440a823945c2d95f76fe23a765", - "0x44e04796d2516db3ec1d1b47b47776999614e81f6d300e2dacbd4d15746982ea", - "0xaac3047a84d57dac9b3f3c8ba907bec354f99ec2d35b915f115f4af6a658cb41", - "0x58489c0ccf49e147ec986ffeca33e037dedabc57690bdefeee7af8a70b781fbf", - "0x3707fd39b11fa9b79749a84848deba1c70534a493769788de1610798b30eb70a", - "0x891483f79855a712f99bf6bbad19a3291c4d417a4f27d66868951c454ad1bbbf", - "0xbac1fbd157279efe1b895138c55e8da4be5dac3b78c179c4cf25a0c8b5e7c5a2", - "0xed050c00def0f6b5936324a13f426cda7229ffc68c7f0eb0ff6dadda957c737f", - "0xb0e759e352ea7ccca080e4ed51a6a4aa4573c7c0d703183da7a98918fa9bed81", - "0x914fb73a4233f0368088073688994cbb0bf5f973a5afadc11076cd64f92ec5d3", - "0x153136092380ba6be166df58738c72d5be76acafcab31474a72b7f7e70e9296b", - "0x7d4d6f2fef46e1b87a73acc8c6eac29195286af79ed7e07998aa966c536f8f80", - "0xa0d10b259aa47815403230b836be272c3dde8cea738868ded0d55d89f2692dae", - "0x13ba7e74aeb0ad66c77cd5112e1ae1b6d3eea84a7c306d161408ef592f2f3399", - "0x491fe1f14cc862fa156f04901a7a93ef26ad272cf98e4f29d28a6250f8b4738a", - "0x92a8a01853ee89642fcf51e42e6a39616f8374937a98ea579d9fe35557d37bdc", - "0xc140bc3ebebc1509940644318dab91a6b8722a54bc32ac7500d94d1aa24ae1ed", - "0x55a5a8541d36a244b1d73b32fbe99263cd6a7b3d9b88312747de6d66302e2a63", - "0x510caebe6714728c47beb114e95177c3a0fc2438369842278a17da9c4327caa5", - "0x8ec8d3e19017cdd4bb4b789ad29bca53d8635596e6771e09becb9e261df64be9", - "0x2e832a7e47a73b47ae881794122f86e1a06e1d84c5bd0a946bb61175dbb5c0bc", - "0xbdb48c7ec1f278b646faca580983ddf4088835803fc0dc2e505be6b092442d04", - "0xcee8eee843b37076f11f69b77094872966f88b4629240e04cea69d4eb16d642c", - "0xede0d0302e40a6e71dd63a52fc1bea3828c4dd56c754b576af9c441ffe2945aa", - "0x07c84f827310d73cc44181f612f5f5ee0ac1207b3f72ae857f9f1fc0180e8454", - "0x40c4dddc5fff7a7a5372de4d64945900e1d6908f378d87b4bb75e425d1fd3daf", - "0x61d9dec3abd29d998af2cadfdc7d22344b651efc717797a486712dd10a726c01", - "0x9de133af95e03c3bacdc17ee428c0ee05b70ef2b1b515edd6fe488db3d091e88", - "0x109aa9704907a39615ccbd296974e428bf5fbac0bce5af02d9c3a8e45e47e160", - "0x8a1f2230da7f048dda7d17e7347cb1cdd2bbcc2ed0908980f22ec070d158689f", - "0x9e8215bd095daa6870eca13dd025edfc700bb5f5ae4f0ef9d44a85b04f15596d", - "0xf93f90b45c5e3358a73392f21fe640c6aff9cbf352eb7a1725088313630d6226", - "0x4e4c1ceb1177bd15944b238624f6e41aa0713564eccbd7f0976f22cfb03c90b9", - "0x2b7d1e8792dc0271b3802e79e4531e42d93475471d880de288dd4cfdd85155df", - "0xbffb3529844e19c7e24a3bad1fd0b90d8319c6982badd3716ec897ae01b0ff57", - "0x497725904d682be0024f5327c62907f1507530231fe6437341e85373aa941d03", - "0x20c7929ae689311a8edc8672b1ff85d79b2187c46da7d0f1e0decdda16eec6b9", - "0x87fec4a78f73a200c7798a07e9f7d956aafc83ab4a90309591bb76152b8e11e3", - "0xabe053b8ae209ab83d827b63e43e08b9b4228b04d51026bcbf044531a2e8f86f", - "0x4220e78d59f01c20f45273e5183a2a36d6d89c2f406df441e76fa8b3f540c8e4", - "0xbbf658cff95c4f1cff08b95069d5eecd60ce5960818014edd66b4f0761708827", - "0x885d316eed1f99685d4dfb1d50701b7a0c9892131a99bf1c676d53c15ba1f7d7", - "0x0fd86d1ee76af5eacc36255f29606e4574b93b1501762118173cfb9997996c78", - "0x400114dc52b491898bd47923cd5d91384ad6a728f725ffe87c916ba858e58a95", - "0xd489355bf13693c3c70b031421c885aa09772cb9d10381d6a5dad584f2055790", - "0xbbe033546ee5909557cb5b96d466ca32973b455f34bba60ddcdee782d8b6cadb", - "0x207bdb63fcf0b403bdbeef4f714da0393b1760adcd1f1f5b21a178bc404c9617", - "0xa30b9d2cffd283cba9de68eed716761ea78170a8d7e6444068d64563c141dd2f", - "0x995015315151329c91ad8ad28aac545c50196dabf0ae393c72d9f369524c9fc8", - "0x3928c0b178546ff50c5668677d2605b701b06f4d28af0c189e746b73b9d5c53d", - "0x46bc7b35d38eb9efecb3082436f28e3d0e36465e41222dd8b8b47fd9857de499", - "0x93d83ff35c0e00277205f12b3ccc89ae35c28177023f2c37b562df491dfd0e1a", - "0x11442d3d7778f2e463da87bfe957768e36087cd2b777e8b0d78fedc624b9a9e7", - "0x2c4ca247e8c280db854ac3ff7d270ca3100b30377b6ff807b853da46c7321113", - "0x0f72e138a21dd3ddd2913dd1eeae525cd78aa578b909672b4450158bcae26aa4", - "0x6a02b4d5e27880290806d410461c10558f33ea04ab655d291e45a7baae5d48d6", - "0x5698a8526618fe20a06bbaa970590e97ca9677ad95b0b532cc816b2577ec1bc7", - "0x7fb913cf94da8bfd05526a954c1f2e1382ae275a060b646d2ba78f004d67d30b", - "0x1aab7d83cd25cceeadc8352a9de9600b0b69acf33a3e157e5e54044a4b1baa60", - "0x03df372e7293b449518d07c4d672c76157c97c6bdc8d7ea75b5d37daca19dbb7", - "0xa928453adde66a9ea45a6b19560fef2619fde93a0a0ee463d64ce538fed0f8a2", - "0xf9cb142d5728bc1dfd59812afcaff8877e6357a46ac9da8beb54c1cdb53bf4f2", - "0x3c67d2318e1867ee0bf234129e7f9ff7aced4d6a94bc2493df3d49d3ef7a1f1b", - "0xe7995735d058d5f1281298652cfec16318f36d90aabb73bec620db045a4127db", - "0x796b4fc8ff8d962425ad653c20cd5e3cf5802edf8fc363824cbb4a117e5dd9c6", - "0x3e1f5f3519dcd5649bed8a4fce463bce7b8a28ce4853b2a34c219c76d8e64238", - "0x0b56a06fcc04ecab448189e170470bcee6415c859a59a2d18b5e9a838bcf2ff3", - "0xa8ceb396b19d4c72d6379f27d0a8283a06c7920811c045dd2521b20d719c9f60", - "0x42c02012a9f184d3bf976ecdb7f65e7abd70b4a32493d75cbf0c3403f5ca358e", - "0x79aa61d2303214ca5099ce8d8c8b59d7d3f64d4af9a009683ca4fe2ecd1ba4a2", - "0xbf626f7b1e4be0c3fbd962e021d50f40e580f133b6127a71ee06c621fe6508f1", - "0x3a8e93479758ef89aca8a3311a16fb82fef78b19ed11c058625e6bc6d4a4dab0", - "0xb44da67d7c6114b0578183dd1287bf06f0f8185a41c77ea75728e48954b4959b", - "0xf49609ba698b8085cccdd8f95a51996d9df32cfb8d0293c01182482dd03b56f2", - "0x94226b7a92c9aea67f94df39675af2f27cdc9a95c847eeedf85ffa17238b3b58", - "0xcb27475102016a2ac05f8c8718e110b74fffcd6c2a97ad0b344ff07a6a806e2b", - "0x84b0c9983378884ad2ecd01c7851efa8c0906b4741d42581c3f1c52c47a88151", - "0xa9071e8c2f56b564873a782630fbe3bd292337b2b620eaead27a397ee3b3aafd", - "0x8c057d25493e712878bd8b628b3cff7bb1249ae41dd34c25ff57a3bd12b9a96d", - "0x7c2b71dbda144cfea3c1e492f0a1e30f2699f3f1968da76a97fe39f3867d37bf", - "0x63b9d33e4d168389637e6fff44d525b67b5e0c52bfab9fbeb53df0ecdd601984", - "0x2a74b62ae8feff55542905a34d6f45c1441891130351bc9959c62f53864385b5", - "0x725a488e62ba0c296ee78267ce714a8ac84ecc3fb8dc4b5147024ee500821dd7", - "0x6c54d3f508c1421a60a707000e1236dd6b45becf483b1f21a53723188c1629b6", - "0xea9072804289a3b9fabd8652a56c2da6e661f14ba764f72949f7fce38e517e7d", - "0x65118fa4f148eb7b0feabd60c0ab9d42802a5ad7685b70bee9ef8eab668c046b", - "0xb07416ee467668d8c7f92a260a6fc430bf636e29b9d5ded3dfac9751e006eb4a", - "0x55385976c32b170335a70a213780cada5fb0c6dd5402cd3056a60a40a6f13afd", - "0x7d9f44575cfa89ae8f4e160a1f432e3e05d02c40b9d25ca0c06df6d35226985c", - "0x12774d382f48d5be794157e2a601c3814135f8cb961d534e685368c3d605f430", - "0x2be48bbedbb5d66af410fa612328524aba00374c152a0b533fb2f15e98bcce10", - "0xcceb249b515238c46a805cbd452395633f28864f16959cef39ca13b69acdcc2c", - "0x90b5204c90b256708cc946daad4a70e3cbbcae36356b377ea6cb251568d8e87b", - "0x468a2c9bd83eabbd10a5e301201e54b9530b9a376452096bbed509240c6d7b08", - "0x322456bcf4495d9787fb66d0d8f21242210167a1dc622706c647e1495c19d064", - "0x1708f1ba83981d7d6d21b8a7d55a4bd9c6355195bf42eb96e870a36480f90cd1", - "0x904c7c58505bb73503c5baefa79936d96df91a15a6ce8121c7f38c632dc7172a", - "0xb2592d5491583e30e4b0e89820f6cd518d4b082436ccbb3a7685c365fb6e9aa4", - "0xf2b08ac926804a68d4c1dce8fda9cc1eaf14e963499f29527caf4797fc47933f", - "0xf9274e64683f5717fc4bce6bb2590921be5a67c1c3f9275bc390a438bdee8295", - "0x7d6c4c1ddfd161753d2782c80532479eac3ae593ca4decbe0b4308123759a218", - "0x3215152f21280b1baf712191a11b3bb80d48f936221a02d199f52437d878c402", - "0x748ac2bba96f92574ff048d2e3100b8e8543c181c8e817f3770e405f24b4e3be", - "0x056d9dd029b79d40d3599b6f6bf240603931a247979db979dd9e6dacdcc53d94", - "0x56c57e769cb7f53c4ca489db4de4eec62071cbed1bd33759c27662e411de0221", - "0xfc7a497be0b417bc1557eb04ec43f9ba4432cb029b6684c295b84e500527d5da", - "0xb505ce1021402ccac337fd5c5346fa162c6843b2d945b44ed45afee0139df1c3", - "0x267595787775e5a0d97e1cdf0a4deaaa785c03b8456a6e5b433767bbadb00ab8", - "0xdfd8e51ed2325e8f2d2bbcefcd24bf9242fbfd75dac246ac8186cf3a72b35de9", - "0x11004185cc8175d91364e878229f5aa5a5366130152e1dd8132cd22cb0333086", - "0xf5ec1ef381b33decb885d21749cc32ad02cfc66392281967758c3fb30158f9b2", - "0xef7fce98b3054d4fc282b38aeb4bddab1c35b085ad4ce803856157d7fc9025ce", - "0x7acbcfac776f80cf46614121866a2621df9302b7be20c079e224cc1377637586", - "0xef8433ec6eca61afc8bf7e8ccef7e1284aaacbb4972938fe061086307a6417a4", - "0x1f76cba9e66ca16339c3effb745c71443bcae09a22f1b07e39155a1805402890", - "0xd720f64f8ae3da220074c029256cb459b1ee8be67ed4dcaec10933cad35fc58b", - "0xf841bac34a5db54625473826eab74d3a53f7b3448e5835cdf4df370624d24aa7", - "0x7b784f45b8f2b0ec9229924fcbe26f66c306297cacec42af1c474566e1f83f6f", - "0xeb333d1de57ffe52d60603c66bbe9f2941fdb01f81832d418307596c156cf54a", - "0x06abb43d2b3733811dcf3059f48be59587425b5d24c086689191e5bc69fa03a3", - "0x76ffcb35a00362f10fb36d68eb7676093c3e36a3fe7ad65acd0f58a064865f2d", - "0xbfa99b31b08807772e417d9ed67daebed645ccfa0d94607dc0cce754bfdce0a5", - "0xa91db056c961f435693a047611028c7fb784ca015f450e146bc8e2b85ac8d592", - "0xf2489993da543373e00b5b42b8db157eae15026a65635e5784d06702d6574de4", - "0x1629e6d410d12987f412c01d14a21ae700e4939f45ec4ef0e1820905649b7899", - "0xf127b506db1c5b3a1aa6bcbb2c530e822bea1ab3deb497c54430fec6a60824f5", - "0x4860301daa24a54ae1902388dd072e7eae2ad449f4a86c123caf783284cbb7a4", - "0xa54be84ce7432042663e40822685cd4c3c1a33bdddb295dc32f9170ba7c95a14", - "0x576136e6b6f941a6861e863989da38d7234f524886f278f8a57e88bf925088a4", - "0x08e84b8d1e15a7aa660666a346a0b90db8cbbee7cc086378d4020d86b1385511", - "0x9229a29e091b9184dda6d66e244af33b0c536eb7e61b7777a5bb0d02f0a283dc", - "0x58e34e5a073bacffd29a05f85320aa52625a9cd27675954f739f3f9229e3a163", - "0x25d2f9f20d7cedf8582931d6dd9ef68233f3af9caf6c9c6d5a10f2b9372358ab", - "0xad43fec2e1ea1ff81772fadf5e38a4f280305cb225567becda2cd518ce2f1dd9", - "0x05195e944c209b1eefbe1925827e976c19d065d18b3e23d883ae2086471c73ac", - "0x62232485944c27ed398d4928cd9ad1680765a4efa652a925b97ae26220229fed", - "0x735c916d78f40581506a4cf286e044a6d61afe6d70daf97d80f8c584ce41d940", - "0x8b50f9bc93ce7c7582ebbd77bddcbc3a123e90d43434d63aa4b5c397a18bd1e1", - "0xa270240950e22fd66879927f8c51322f0feaffaf09e7bba8829ce5dd841b9e56", - "0x6791446d6f864992e3b50fb590719af98aadd7f8af54e4c71ec474cf0db56cb0", - "0xa22feb28061c72ce02f7d6bf27c7d04049900ec24394a73ea678b90c100de62e", - "0x0fbb9f4ef4d048767b036f40babd8769e185c549cd71e2e46cde94b2fc1916e6", - "0xb32bf314ae6b3994bf2caaa716691eb2116289a796f968ace5c49482965d2037", - "0xf68f7edc391befc772458ce2dc9e5a1c3eb9d3c709c4e177535a14a863eb686f", - "0xee3c3122cf1f7890fcbf409897c6ca4a634b6a90df2d33cafd8dbe1fd2895ea5", - "0x44c407d967d458d6cdbec3e4fd90e0f5fce309f702f08df5f7fa7d4d66ab8d90", - "0x1e4c7644e30228dd70194e600a57f98c3501a598835ba35a3b0c99f53101b108", - "0xb619a4deb408b73810fd4c5a827c0cdc3f7f9885470f63c468794c7a0796c184", - "0x69ad24f8814b17603ff6ffc1f585346e243a296f74bc3781453a512bfb746a9b", - "0xd311dacba946134de52ed8f98e5a643c0b4f8d5c6d2c928ebc5087b33b309422", - "0x59ac40ae911e0a13aafdb4961c459a9cc8168e444f9288cec782911aea1e5548", - "0xfbf447c35dabf67ad4e88a41e30c40133f166adbebaa154f0c571028f86bb183", - "0x64792ffea472b80e6241942b1191a4e4228e79c4e0a436873ed3eb714338aff7", - "0x618ea5b82192c2e43742b8d74e78234efbcd6332d74c18c97ca087be66baa8e9", - "0x8e659da466b2150870fd5ccd6b4c7e879f3d5e248bb386869bcef965fef02837", - "0x4ef68ce8190a3430e4aa1497ccfc26e31d17ce522f1f16bb7280fa9ca2d377b4", - "0x79a10cac12b7e9b922d9c8d1cac99ff5616826a5f176a81536402cb968377e9e", - "0xa4fe7b019557ab1cd8d212da8704a0a84e8078eb9ab53952c2ebc0c3914835b7", - "0x59916f12d4cdc91b9a0e135d84630d10c48a803dd9b84ae35fe00c29d1acaf58", - "0x802690bc2c2f03e08e2d2dc7216a033be7eadcce3180f220fa5baf411cfce958", - "0x40fce55d2c349a7f97a252f6ae7d10013c396710905abdac17ca794ee1e247e1", - "0x73dc6101cee72bcc15f9ef398f90f2c5e3f616c97b0c6714b65e9a6a83edc190", - "0x7a179bc76526a5752b82b1556692acb502a72e364ba1295aee91abc726c23de6", - "0x313f3a435058fe29e7e9c490553bd9791dad94ceea2ba672a2a2a3e7f69039c8", - "0xb528f654e6456ae46ca764295454d78f4d5a4334d49340a754b5d8ac24b0d21f", - "0xc06aefcbe61ee635fefaf4e2e933c22c73c48b55f8672cc6a3c8ee09836af286", - "0x6626a0134d8ead3f18aa09af7b1c05c01be0cd88c286535115edf3ff19e8f06c", - "0x90051f7019b8d99acbeb816ee256899d8f0a5504f156475cacc631f427273c40", - "0xaa93485a22e1fc2da4c5b5483ad81208bbac2595e758e23677e93521106c1665", - "0xb334853c4ed01341742733a9b1d7ba7430f3353fc1a52d0751808c98f621ab51", - "0x67d2672662726fb24d884ea92668fea97d9e0d908a11f70bd29126fdcaed009d", - "0x09cc08cd5c0da5caaf74d0e8e417fe151129ee7ee9b5082f415f2ede57351dca", - "0xf87ef937db4618c977d871e06bcd6e2eabc7a20faa0ec0f08deff573200b0aef", - "0xaab334b96f82ec056e3c8e1e95b9326f35d57408ce24e63071e182dc38d552b9", - "0xa0d5f838bf15f771f9adf19f82e01308ec34111bda56e2edadf5806dbe80aa88", - "0xf288f085a08d5edafdd58a3a0637a48f0f6cbc730b0d3a0530a6780cea152281", - "0x8a9e915a3e1ac769bc5e3b308fb05cd49d6cc40e97f6cf8cf789f1cbebf7bd6a", - "0xfabd2bf75fef724ead94c7b6e24026264ec2728bf792bd0595aad04900c69640", - "0x3b4a6d3a6e62b3fdc14173ff99419ef02b18134050f8433080e65322bb0934a2", - "0x8d72cbb4e088da0a6725163af3aa47a69b63120f3bd02b93e12788b144e1a26f", - "0x0a112bde7c1434c444519ab9371a553ac25250067c72171fa6779efee4bdb7d6", - "0x8b89d89dfa4a2cd35757629c84edf71815311ade9742e48b6f28d1886f1741b5", - "0x538bd079c14a70a3f6db4b45b528e405a210fd6e2343fbdccec4235a09f016d3", - "0x95ea30334671da97054445f307f3b991ccf1fc965ef148000253bca44f6f4209", - "0xce98031baee0108eb9695be729938f22ee71abd8f368c7284fa7c102c2c03672", - "0x916fa5e6d29e5b17d29e6393f96f2707b2e245ef49a671ca30987c9d31223ef8", - "0x1caceadd646069b69b86699e555e0695c756108dce2c74ebedaa00b8f4c1d4cd", - "0x46a5419c22b3bca18ad94d7b0f34af90c62bb3cf660a4b0b6d28bdc11b079390", - "0xa83044af490b0706b350357979a9e648e041ea747ffec7a50f952b71b9c42a66", - "0x677dff9b0b4ca0f39d526ef4a8fac9bb49d83b5618f73d7ef78c3ace311fd071", - "0xc85d8672b034eaca3715eb6db5408d00d8157c991fbfb55b8fe29ce168aaec9d", - "0x89869cff97616604b696706b28474ae54d88387c2d5a30e690c0e5b31bffffd2", - "0x168585a4f4a9a745a588d609d3eced8e550f1d69a7399547a60456df218bf4d5", - "0xcb4d9270dd367735a6844dcc314782b708ea9b2de82e5f1fc11bc83b0970938a", - "0xb3b2ba42395b4c15d6de64f7ce010486ce1dae4e6b14f01901cf1f4b6a538d30", - "0x11601920286560aeb98fe1436a1f66e2c9ee69ed2ce11ef1eaa6b4b3be447787", - "0x45cdc9aea631ac46062e1d848c3a737da5f7f1d396177196e52b4ae1019a73b7", - "0x113956d33be8473ea7b52f062945a1fafe98a424d50b8b418eb1b4f8403f7036", - "0x2d5a740fe5eabb6fd783b9c47ee75166e2ef7916e75bd5fbc40fa0a6680d97e1", - "0x3bd16b4f04c059d78b8a28627039d6e47954d6a87543c611d6fc8e2e941a04de", - "0x86b1f14be4ac193baaac8092ca3d377f47dfc4e41b6b81e02dd51655fb590c67", - "0x1cc63d91541476745e3732b4220403bc3c994be2e62c6fb2e788c5a47af7445b", - "0x5d09f5a6d1570b09f03b79e869b7f55b344a2c5087bd4c2c06c8d4a85f49095e", - "0x6b48201724527b22e31898ab54616f0008afdf463b9afb6f0c516e7347bf0519", - "0xe79894ec014f7835965959a9914ec4620c6dedaeeabe7aaa791965b30d503908", - "0xe549b3679ff2dd8cca080bbf27826ebdcc14a6ef6ff6f06a2f2ec93a4fb1baf6", - "0x5bbb4f98a26c9c9fa8ecb2b71bbe032c0c4d349bfeaa81790eeb066412de248f", - "0x614dbf21c81332c690c934bd9d9f4c640f3df9c9510e9aecd105f5d2563a6071", - "0x44231b331eabb55fc08363ce5da10d7df42626b94fc9dc4d08f99ac220ee145c", - "0x3e22a80bfbb6e99438d4394700bd26964ca97de1214b20c8a69fd1788c50a4c0", - "0x280ee234189ec732bec0b7a25b9bff60b04e7be32c54fcb95121b14725bf62a4", - "0xd94627e7ecf1abc160c432aed8c677eeb4a0502b3100737567bb2d19b1c81478", - "0x6b2de1031473d511a197f131e579522f8bb2bf9abfe7ee561d8e7ef9b1912b6b", - "0x80b1447503f606ec08175cdf5cadf774f3d0d2a1f447ba41ee66941811690b68", - "0xd0b95ce90aa06affd35cfe7d1f3b45fc121bdf2565ee5d3a1998a932c2fb6db9", - "0x4575abb623e43a73f1be5ee52988e2820cf35170b68c6ba8caeaff086783a1eb", - "0xa03708f13596c11a3b76c49b44102350c06aee7ba92b3f2bf3829952f6fd6c86", - "0x4916a6e3dbbcbc8a13c91f4db3f0695f94cca48b81cfd56b9836e7ea44af8daf", - "0xb73b5c9471a175c8eba25a4df896302b65af142819e047928b8847a5261e13ce", - "0xc5e4bf50a5d4825fcc44eadc3877bbbae0589fb5a2e9ce1b16bf9fd60056af5c", - "0x43abc2f7f189448316cec758251cc7b63526e95b16ad919e74fb50a9831b76fa", - "0x072a4368d93c89d6bf3beee22c6df5acac53b9c8225e53bf3e368739890c2bf3", - "0x395bc2072a773c314c9cfa3c9983f7d8d23fd77276a6cacc93f580405ffd03e3", - "0x50528bae45e8dff5a7d4533bd413b103edd5097c78f35f49d0674c6f4378f7d0", - "0x02c2b72bb7fcdc07939d7fcd641dc148ce0699e5b9c76e7af3984c36a7da08ad", - "0x0b6ce8ccf42bce819e02e2b0f725830fbf136095e361806a2b5eee5ec975af90", - "0x59da0fc5802651044d72946d162d95412e98e3a3bbc170139a5a54a9d0330a70", - "0x2d2f3bf4346e6f38045e925bcb90656bbac77f7edf2182a3c6835b2f6e61f9f8", - "0x213ddcd0341b130de4699b81b7eeaf93126907e52ee92213ecac458bd957eb5f", - "0x0ffbbec5a21fa439c09f975abc684ad9884d396433aaa4f4890e5e377d7cb4c6", - "0xf52462396c136854067622763c21ee796e880115bdf0fa08a07a130ced1031b4", - "0xfc9bd5b2de2b2ca5b16a7f7c608ed4218f3ab60499a691843548e530123ffdae", - "0x8eb92cab2abcc01f735011f27566324816450dfc5ae2d0af14137028b046bc29", - "0x0795e1456a6894c0ddfc3ff3cfa979855e39179b03c15285ce084bd483067a63", - "0xb8b428f8ff1ceea1e694133996663d000a9bf0304abceb7cdaeb6db5be74983d", - "0xe682c8aefc32e0ea3899f6708057ee708ce46125462e8dc2b2d4605f456e3539", - "0x100f58fdafde5a4e2070784f54088d7522f2537ab249095966cfc9b8250f6bc7", - "0x8efb5cf8633396437ab174527ced3ede99be7e20790ef382b5e25e49b7f49c23", - "0x196ee4cee920a9faa42cd5f3262af5b35dd212169550664a3c9c15bb3a3fd86a", - "0xd6781eebac997c5b8eed3b0058a058b3f6da487d143fee14711398a017d16333", - "0x096aaef445abfcd0de2a662b16cba39ce371d053fae63c27f3599df3d169ed0a", - "0x3eee47ffe2e9d5b29c5e1139911d57d3a3d8d87b4ef290dd95e88a8d9a56d890", - "0xacb1b75fdbc4c4850565ab27978e7d10f7c29d6528e7bf198958238a4be4846c", - "0xd0cd5035132f5f8b303aaaf10d6873482b4870a74965d8055c48e8449935b76b", - "0xe65aa97a1cd3aa5b9cc906157f7d9352cf6592abbeb66647623632a9f3a93b07", - "0x76d08e7ef145ee5f393fad5faca53efb07b9fb493157a6e0b290525127b5cabd", - "0x533af4ec13c168f2e7c7368cba5ab0a138a5633656b8c19f284c98c42018d849", - "0x0d61ae94270aa651231f1933cb968bfe0cd2f4e097ae2f0ad8d41b8e2a785327", - "0xc22f8e6418938369b4e505425a28488c54571c4a803df35a233f70ebb933c635", - "0x743c0c8d16b790edf5e51a8453601ada99965007cb4dda51c82cd68e7d77a224", - "0xe5a62da310591a98f6b3138ee367ad2a334cd48d430399c425b71a57ba44a46b", - "0x5a7dad70619d620e5991b7476d125af8db51b67935a49c786b57f09d3fbc4035", - "0x1459d52e06d83b9ef94f035d08af0d63f66ce87bdfc3fa25984292aa2680ec68", - "0x2139af4823664ad7b8f2d6222f86a5feb7a5df0f1a934e8af139d73a6d008d84", - "0x31a7000f3b576d91c543c5ffc4dff45205aee1120d9732c3a342318bbc4d72a7", - "0xe046d4e9ea0c367a2b47a0a23181e144d95f9a3b8d77244fd70384f031fba4b1", - "0x294de2ab05861fb25db6e1392e8eca830217680610165a2dc3f6131eea1d9918", - "0x1f2dc7721a6ec2f13b9c9dcfe3d166b323c53dac5ed49c883fb6104caa841184", - "0x3b2797e3eb732182ff4928045800ce8837b7b4796d9af977b118c080ebc1099c", - "0x4f076d935900c3cdcce4ca42e206caa7e63dbc926dbe230f640807bbb7965c60", - "0xcf8dd9699ad2fb397f19b16b851d85a565b4d197735f8e3c279de61fc4e45bdc", - "0x3d3544db3dc8e8fe1b672ec5ae8c341524bc7e1eb3c96bec3867ae910d7b85a7", - "0xb7d7bfdba9ead1c68bcd54c46c7ac2a34f5292333f62e7b91b193baee6c09037", - "0xa8fc4eea921532a7fe31196f68a915f6f935b8ab480b9943dfddb7279cc069f6", - "0x6c749c6ae083721c5f5613a581435fb837166bcb3f07325873ba321de17f4526", - "0xed17ee542d4953c8bd4a7035bd34be4e6e80b73ed19f86d3198c7efb661ea78d", - "0x24e11ece5e106eb20534043c2c9452d5faf327e0555bc3b8f923c108781dc1d9", - "0x094a0636d62e402344d2bc9c01fa145ba0f32e03383e5eeaf85b257eeac3acc1", - "0x0e02c54a2ddff2892841d6e450d8deb610057e559ade4b8c3c943bca63b31384", - "0xcf147eda0c90b66e59c407ae4b2edb236270672ffc4bb68ac7da7a23b0274c84", - "0xae8054abfb1b72499642a11f80348857e49aa315b8debd3d61cad8662ef56b08", - "0x8f9429544ab9309d5a18a2af44ae6bcaf2e04de344d9d55cf22d8c503caab64b", - "0x8c6288b1027063bde39e399be8e7c82a7f8f9b853fbc04a32f73404945002e4e", - "0x189a06ad980a6e9710acb970572b8c04c2fc041fdd169ccde19b5a939b69cd81", - "0x0ae24fe54c7724a8c075eed2813b90e1c520119e2eaab0a0578e8759868e3ae4", - "0x5d22a1a2f551aa1bef7b552ac797f5b2a22441f7c3fbdbd017f4c5dea89b7842", - "0xc1827d89c778f3fb6c35842aaf940bf8a829079e008421db792cf04018bc2b03", - "0xcd9547011c12cef89331daa486d5a89bf36c8dab81d5d0e4abdd9c2e35d2bd8f", - "0xf115534a66431250450d68d8c1ef80ab9a3c68e63a203094ccee81fcce0213dc", - "0xbe9a16558434d1f940ac08ce7bb0ed0b1e400583b7bcbd06dbd9fa933d1de6c6", - "0x692dc4f061262a3f2a50765773294909d03477b0663f47f4aa0f675f2c0cf0d4", - "0x6127590ebcd139f736331080139c1b3bc6aed2f3c961732395ace62edec7ca5f", - "0xdc75a2ea1a80516dda32d61a2d671fcd421ab9ecd81473230ff9d3be8aab444c", - "0x369e3a3621551d67f48e544d76a815cfcd9a72b188972b02c12ec433e54c5f7d", - "0x195110af607c6bf30e268ae88dc0245c6d3a34d392f0d7698e51eceb1e10ef4e", - "0x950743ee4492e360e7a594dbc4b786ef4ca1b061210fe967bb5acad5622f43fe", - "0x926f538cfba0853b453169710a7acd6c2a002b87d2826374bb55790ddeff417e", - "0xb93a4ff2a88a8ef3ac4c77b55e828172f20b9b9ed655ba7a37b00ffb524b5138", - "0x08947d220add3e64d2db582c988faa58556b11257b9b4d8ee817122085145340", - "0x28ce559274d5455f0f9db83b263ceb6f1a2bfc00a2512931b04cfa23e7887612", - "0xdeea519fbf4ff441c8e61ec1f0ade9258a50a10a4702d0be90b408f30b27aed8", - "0x3aa93241858d4d882409dacdb7e716e6fc8bbd19587c15e349ddd5a7d90e287b", - "0x87a42a8eb92cd20a8dea17bb9a241354559443dbb1b4f549e2f9764321a1549d", - "0x365f92d4e3e88e73a155edf264ff3adad231b6a8eb9de11a1fdd473d9f2fd27f", - "0x85056e53a4b8368bf1ac1327db65d01d35e72da358274dc7c0d47a924615dc87", - "0x0fa77604f4d0a3bfbcbe9019207fd21dfdb118d94b391c8f3467b7ede0085494", - "0xc58d6bcd06d9a666bd5217cc75ec70c65d90f45d3266d78262d44dd1c48853bc", - "0xce225ea3d493c85c9ff2719af2d7316577c7c87e43598210446575fa10263038", - "0x010a27979371a2f4c5790ec5d61aa1ea443347d1e8622b4869fa33816306afda", - "0x34cffe4d5ce1449e6c1efc3c6ef2dfa24b3502370dd847b536a41976cc1e7e55", - "0x423da7e80d01cdb061b17cce2dca3f31fb62c25ce0b25c7ee5a45df3ca02e72b", - "0xff680f2c9fe4896d1e117bdf7e655d7d86baeb0753ed21b373a621d954be262b", - "0x9527f4e412a189e2e1bdccf0217cb6159b912c0c3765d5ea7f9e54e2cb393b48", - "0xa14f94bab1a626a844886e0338510b09edc8eaf9f3b416787b72bb2729d22c3c", - "0xde5afc9aba2c6db5ab784212bc940193db7f03b695049dc969f1d5ddadbd722d", - "0xdb39c68d462695c642bb704f1fd8de951d4752bd15e25115032a5b36fbc6d2c4", - "0x64987238a6b0d048f5747a81ed04806e89108ac247e5ffb3fba66fd875797690", - "0x0eabaca3229eed1341b2e330bdfa87432bbb685a116d02f943dda9ac66b4e877", - "0x2247273340e0c7954df9a5db6700911a4f786eea68c98106a29ba0c3abe96baf", - "0xaa035892630e9168378963dc34c427fe2146d6bffc6c8d1c5026ef8c3ccf395d", - "0x26a8e42afb55511f170429010e07fcefdce8bff03e3acbb3f8571be05960bb6a", - "0xa95bd3416ceb31df25f3b5e262109680341f742c11580e2e83da5f36d8b84ea2", - "0x09929d531b919f43ccf5b6ab7e87265ede17ee5b4103fb3fe2bf608fe9075c25", - "0xe6ec4893fa4840d90006defa51d5599b5a164f0e19c54a624430b5e7be1f6280", - "0xc21953eb5683f71adf5e5229286e6167c48dd68f2599d7cc35817b4b9ea0dc35", - "0xf30d268431af3d017f42363da727dc3f4fb42c6c1397a2417f8a6d862131610e", - "0x8a686f06e784dedc2b9e015d7ca5232deda4df832428050361c4c24639898c40", - "0xac5ced4388aa2f66869f6c3f72de0e531787bd415c8472e1a491cf750b4d2d41", - "0x5f4bd5ed18dbc2dbfa1662c45363fbb662337f8253cb1260dfa54ef496b51ce5", - "0x67b8eb13416e83606e2b1544a02e0172a1a9c1b9cba1d56613d2c1ac4c243966", - "0x3f450fd3a47b94ad683939f8af79603cc390bfe4747c6c013321e39f6f1807ed", - "0xf7f5ddfaf17bdbf632bab24d9aa0bb6a960e0e7bf1ea3efb3b60bd558aa357fa", - "0xe00cad28e524f273884ed368866865d466c0afed78390d8586c2660944e0d007", - "0x370dc3ee72f58da1a1cfc6d5311c688f171251c46b322d07f61548c41948ee36", - "0xd5b426cb386ed0f0017206e0c551cb2ef9f3cf02a3d94f68cb499f8f900f740b", - "0x837bbfb8d1db4e394c5917798b251d7281e4e1d024d95a0f8e8963ffa0a3fd95", - "0x2e7b8bd8121d24134418f88bbf49f8888357fedb1ffe1f928ab0ed4d3ef96a1a", - "0xa695fa51c83d2eadfc3b3775c200a8b39a239aeec091b281a022c9a3aed96b7a", - "0xff4ce70579c4ebed37684fc8f2d774cc358ca29a54d2520989cfb66444a4449d", - "0x7a34a4cf28f43b160218d77f742251b92f8ce3b627c23f632fed544e9234b2d9", - "0x67a36d6b2f04573dce9a2f0adc37bb9922ab038d2e706330ab6bf72b60afd88e", - "0x5c81c0bf60c07bd82794389b682be408a09b34dfbf016453a174229152e0b763", - "0xbc48cdfd853ef43a5982252eabd16b0ce575690195fa2cfe12a66fe10b3555dd", - "0x2125afff5e4a9e2451df3955e3bd17c8f6b10e628f8c3060a30ebf6bc4c616da", - "0xcf447b12f03d41da20fce5d8046559be590173f902e96ebf3c9b037398fae548", - "0x58b29b2b214e0c58bd8e71fdceca3ba0825bf7614a504e7bceb87c7f13ae41d0", - "0x1683843f47ac40ad0ebee6ba68a87894fc94e37a11433882a8d9363bf7bf4b44", - "0x17d702c3071014ef41b20d93cf42084dc31db7c97121f2f41e1b0a8ba2c7d557", - "0xc3f57d154b1047b3cfcf25b393aea31e0c87dc20cd601a62fae58bd56158e30f", - "0x1034d9216d4db99620ed2a256c454cdfd9e803e1c066eda49bd2ba1288d59d2c", - "0xb4f23a426c5458e52a667cb01c734d6d7473cf0900139da17d355cb679d0406a", - "0x16b7beff5ba9edc9442bda4b46e34afb8fc1ed917d226a4beeb6d9d2410d8fdf", - "0x3b2cf0a7782ef7e1a1b4590df0930c44ec2053ec2f519612b0f20f6e48f47107", - "0xbde784b24ded1a05e6d650b238021e904c72cb35d02dfb6fce107925ac947873", - "0x7a42213f3c331c3d183703dcf4e7704b696728c6de4296161b1fe73c78277a01", - "0xaaa0c71970eee0f71ee064dc6c908dfa1d2c0637983a18a78ad6284a72a5e010", - "0x72f2af281d44553d2cc0645c0b1a01a9a089c282344a4d2d2e5271594c58ec3b", - "0x46c6cddfaaf1d204e735c4804be955fbd0c3aa2720a6d40640641b4e777b18e1", - "0x7e52fbfa76e745f3cfd6e0e0620e7597afb4cac20374e8a54c56dec4fb1d040b", - "0x69ac36b338093a55343f7b1e3b2288965e7113f255d34add5d08211932c35b1d", - "0x9cb03dc2371657f840b5da9fd420c4202176168b1411fbce0eb93eec3c342229", - "0x14eaee01119803c9c427f990418126a03d1ee9b829b23d78b11ad94e29f92c1a", - "0xfee4f3621e875f9b69254865849a58538cc742e79843ba229df2a179bceb5b82", - "0xdfbc9322655f9f2ab5e918c2dcf819f0b4d6de07905dd2f94607e3b564275f57", - "0x07c42d1a39c3fa351ae4aa0adf903c39f3f7ba020776293b0ea5250b09c4cfcd", - "0xad6101974f08962920d9edfcfe8bfa4181d9465c1a571b128a5767406c7c890a", - "0x9f29341ba8b368f9c4dacb12094979a0e9bf2f6969c5262a880e974ce98c2aee", - "0xbcb3768fca1e380de80628bc55e911aac7a3e9810794ab9b86fd2ccbf3a727cf", - "0x18d8a90af389f112f1229b591f3898ea5a0f2a6ea46eab7aec3c99d82ea40760", - "0xfcb7860baa54275ba72b34069fd8874ef29a22331b65586671ca2ce88410cc14", - "0xe8323d318b6d03ddedf163b07961e747830f51a8b7ba90ef4b568305d5a8589d", - "0xd7d9d5853392e3cf51e6a4b9b8d60e5e1eb0e00962bf70270ae7f3a64e2efcab", - "0x2f6cae0f3f4facc6d5b1e87f4dfb2698211cbb6429c00af32af1b8963aab2750", - "0x4fe11c9667cecc0cb8ffa78a2ae76cda09adb8c993746149bc322dd4dbb389f3", - "0x1d7c6bd7731617983089a3a450e3cabb5c9e48873a516d08646c9b90ec152615", - "0xf3cb5662cd5414b486fe8d664a799550957422ce0b990f36bdbd9403ceb9afbd", - "0xe0da2757036de2ef8539c522ecaea947e47b351f68a1426c86ec621ed4bf54cd", - "0xa865a7275662e1f977b260ba85d5ecf1236fba2ac01a69aed047b2d261ed2610", - "0x780cfe59c907e3db653c573888464c41c4b8883875fceb57501f60ea34fb1322", - "0x754f80993771441ad0836d84a0233bfde1a4fc0fd20e199c1a0b6a755932b83f", - "0x9253cdd043387ddae4da68098b995268b3983a17a200c8990b4de3c3102042fb", - "0x4aa535ff3fcf8ca9ba6ceabee3d59fedfa3c7632cf68fbbd3743ea9c86686fb8", - "0x4f5110933431db2c0880d9d4699e33e8f4bacc31e4971801ad968cc25f064d44", - "0xe4443aff0965d78b747dbe5b7e92b03b4c1898e7fd9911177f3afc3f8e765fc9", - "0xc7113de3a6ed094a63f9d1cc5f68d29801ec550d8dd48fcf1296f10b73f97988", - "0xd707b5f7b09c7b264e323df29236aadbc03389a9fd75da07c73b9eb76ef928ec", - "0xc671f6c1003e8a2fa46a0f6100b1ebe5fb4657b8a3bcc8c4190de8a5770e2bd7", - "0xa1f8030d4bb2d37062ecd95a87dce8b3ce7bc7be55de7aa1028adcdab436cb96", - "0xef9a06d2dc5c209a7f7747128c22b17c2724d62b655f07b57fbde1ec5926a168", - "0x82b15ac8a4341283ca4177e0e7b89378367e205ff8fd59e3e43439dae8d6101e", - "0xcf641779c36577bf02101d739b7b1bc19f1788b22d72f473bb4129a44869c26f", - "0xee111a9312dde0a65002434b38bac07e127167463068d047fc95eb53469dfa5a", - "0xc21dce0be4bf464ff63c59b104f3a9e8ac9a5900cdd77b7fc6676e4e2a17f260", - "0x546772808ee95a5453a95e581b86ccada861e2f37840de932c70ddf65450779c", - "0x748dd0b051d0dc1e5cf18584c1111b1fd7648b62d0f340539eaaf2ca155e292b", - "0x668719274b07db2b66d0f2bd7e5efb6e18455f25e49b07bad9d6c046ae8543bf", - "0x305c577aaa37c8f67c631de5a2ff8833841daa6a1f52e8f860e7b6cfaba5e921", - "0xa7a197c39bcfb74d5f3c523de12af1651b76106f422c1f52fed35e6628749804", - "0x6b46e5b4a447ab9f64a529756a689db73706c626a94b5fee28415c33c0175e94", - "0xedd9664015849f4dba53c83f75e8f86e44f8b79d3fb9bd48e526cabc85b8501d", - "0x218bf5e9a2db20afdbeb167f75574adacbf64463f11665f2b8be2b76d741fb95", - "0x9f1c7175bb294b7e24a5eb61de4da073a5af547e7dc911c9b05d98ef64358d58", - "0xd6d859b88c49be0bbc83b1d34d7c87331bf281136146651d3320ad49db320f30", - "0x3b4ecdff0b3eb94c8d7acc0e101b7fbee9aff87992fb2431fe2c4d9f44a394af", - "0xd33de0cf69370a33f13d9d7db35e3490c88dec0ee23b71ed040338049616e274", - "0x84d004e9a8ba6b1bac42e8d6bc1253ff21b47a1fb494e5d19caa1bcf34b6c22f", - "0x71057ea68aacfbaa3655aa22c75a22fae502a9afce8aa206a220d96654b9d4e0", - "0x8a47b01548946acdd553570ad9b9bf3c78bda4f96fba851ee9b0a94b355d1c55", - "0x53b7a7ae02459b17760ad47f69dec212ed628d3fe856af16a0568468e7a0b752", - "0x66ee4b75bd15f446fbbb3f1a9f0f3a159b8f6c66f708917156d7fee5fe64e6b6", - "0x706b81fb39b3e09eba9e83250f2b3e2b567f9fb1c397125fc0157e346d5ea352", - "0xcca91f2ce2b51595cfb86e0a7458d45654f61d35a66e49d25e6dab01ebb350c6", - "0x4397d4d33ec5ad07b3a583e9f39df02f5756dbca92fbfdfed59a11cbcf9e08b5", - "0x88ad8498c15ebfbdec6b1f68deb7515746e5c110910150eabadaafcf715a50ad", - "0x715d175a3badf17771c686793223fbd837f2579ed674539eebb0ce1244fd45c6", - "0x96acfa1d0cc7475359b4f1ee596aa2975a87f314e80d4baa0a6af7c7c77bb92b", - "0x26b4c14547c255841bac2852456f386e8addf8cd8c45cc9f4be0377e05411bbc", - "0x124524955310b1a35fdd19104b6c0bfef503541fa55d0b7c24efdb8d0cb33a4c", - "0x9806202158a5a5421e94f69a904670c161bf61ab1c41bd5d104ad9ba63866067", - "0xf156a5c71bbe49c45f91ec3185654ef8cc446e9d18d2ae6d51ea0f0409fc89b1", - "0x7003d7fb1b01fb6e036fa33ca535bcfb836415e7661277634c47c628d81bc140", - "0x6f23e519c11871c12185c1af12b8840ede513e28877f7e860603703b3853b14b", - "0x3aafd215a348dc20c5ee5444cc007cfa0e90157bd33c8a4c4de4fb5b7a231ea6", - "0xe8f6cdda4ae600ab617fb40d96d0b2c13ead6ed5e36eaac9f8cff9df918b35f1", - "0x75e63daa0a330a4b52efe0e0e447b345622fe35586037ae88e6e48a8b544a087", - "0x6c4c8865b2673508e4c62dcc711e9886e57043d758a60787c6241578670550bf", - "0x21ed5b252ba5e9607045ec6ec85455db4615a1fc43e22138ab3a087c5e6236ba", - "0x2e2ccc6f66368ae083905358a182b0cf3e85330dd2a3875a846baba61c9be3ca", - "0x4a8987c10833a298ff22cc199be4816aa05f25e39a10bf007e72790081ccc26c", - "0x7a4b05fa47d37c86aabe456bda0ae7bfea3b28a375697d4d19ea2b1676425016", - "0xe203aec00f8bed45f3cb0911860093a5fe228c22476f189f2dfe4621145f49ba", - "0x7b2ba1b4da99ad50a33de156eb412d0020efc63c85d0840af6d9ff9199ff7dd0", - "0x0b84a8362a67c9cd715fbb09e0a48e046afafe8814ea7e1c0bc99da9ca670bce", - "0xb9457e83e503f6b65bd357b0c1cfb655b4c157e213c1aa2e4ebe6b88524c20fe", - "0x401fa17417f9c8f21156a3e660fa2b0a27eea8fb287bda37ca309a3c918d1c91", - "0x8bd1e0e83f35107a65777a1889afef35f2d89e70e9ef3fd56a0fe77b5a5a07f7", - "0xafba338c738de0a748fff79650392894482f094f79bf0c88b871324a6db0b1a2", - "0x40e66643c2f93c60de74a976f7f05d753ab09d33601cc1421da1e987378be84b", - "0x4a5d0c268a86af032703b688745b91bde7090b4e3b245f235056b5d675760685", - "0xa8d7233658eb85c4a309293e35aa9768768e78b9635ff989b4a5a1b64f6f26b1", - "0x7b30cf0f31a942afa35ee2b30bd3e67cf0fc3c366eb35b1e331ad8805e9e0d40", - "0x5730206b218be1da571ff8022b975412746f88606ee3014626a03eac8893deb0", - "0x4dc767b54a2e685d7588f6fcc1468cfa7c7e47474297b29b15957b2f21fb90d2", - "0x528933096ece3ea9bd4b3e0fa78ecc95915eac29b8b608af3b18eb7fd4adaf50", - "0xbbbe02a2b2949c73839afa56e959b23d3dff141776137e5e804d5d37a69ecba7", - "0xd8c4081e5c0ffe7797f07db6e5696e04ff097410b1e45a649bca43362366bd87", - "0x46402c1f08238694bc32bcc396093230cd2e522c54dbf7d00d042986d22f00b3", - "0xfcfff397df45c333b4fdf271b63a4510c45310a3c9cc78b97b867372203228d6", - "0x6d2350213ec1c6a79fb7dfcb5a66f065fbc41d4250075d81b7647e4db991543c", - "0x393995f9a4a358d13b5fd9684510068f9fb86c71f2382230999bb36b41586a56", - "0xb3b65d3c835ed1aeed86f34117431fc46d62ee2209db054a5e04cc81c1f795fc", - "0xd1ed46b6e64544acd6f9fffb126fea4b74de84664c2b22c760eb89878cd44ad7", - "0xf21c944b3e41a4f630ad5452a28633c885427b386ca81ec9814ca14bc94bb2ed", - "0x54c5c6c093bd6cf2cc72e058fbeb26012d71fc8e2bc6987fbdadaaa48ccffec4", - "0xa2e79a266c6044703233fea5d0c31bcc22ae247eeb55a982bc2e1c0c64af1468", - "0xafd38477bf1cbad227b489818e335a79d38bd052f7957efb2d07b9adaff6c68c", - "0x4872bf318a2066d5338e31929da36fca3e2390d1dc8dfe88eae5ef854b81bf97", - "0x896b1e0fb7a6daa96f00f7ef633ad67bc4462c962ec288d7f924c64c978f21f9", - "0x0c3617575c8a625e7fe35c30312b147be400b2777ac4b1c0fd84ee233175faf1", - "0x6f47126daaecfa7b84568c26b02ee779763c9f63b4b7f5cb0cf0ccd4a2c0a499", - "0xedf2e0e03c0d321911693c1554254d82c764f2987dcb5c76a4204b1184906abf", - "0x5af810bb6ef491ccccf2dd00c658bddd02a1124ca26be59c97a69199605a2d8c", - "0xf8a597f22125a6f3fd76e28db6ca04fde9f5eff62b78e162855b9a48c51a0cf9", - "0x9b50cf0abbc883c6af57fb52c48defb2ae67f77de2f2ef5aa4fd42f6236f3f43", - "0x6eca9c995386aa4f161e6f4c6ff999cf6bf1bb4184d9beab567abaccfce9fc1d", - "0x7d7dd300658f784ec68cc3021a54680ac5767085657bf87ab6f76f05e505e6eb", - "0x1eab27e441a43a3534507ba0b5390a13c7df373813131b09b2eaabab179b5cdb", - "0x45efe5848f21e2b4f670bf06f0067fe453eb29ef45df102588c7b8f902005066", - "0x086f17e6047e9476d828eab4ba0016e3a4a0f51f25eace2c27445b5fc28926be", - "0x63547a7274cbc857d547291923cc9e507e3f4b6fe85b1ea88a527674f15403ce", - "0xdb0d2c2f050c4235e9fd03de4c491e09061ddfb4e55fda8ea25a8c8f013193c4", - "0x5f5bf14f72834ddb7ef96affc52b3461d0512fd9bb0337313cb7cedbfd858228", - "0x92c4250a5ba9ec5890c39a608770d500ac31ae5da1cc26c05aca55a453e15a25", - "0x4fb2c385632f512303dd76f4c9a3888837227bbb5e5b949a7b9d74906ea6d015", - "0x4814397680a72cd024c3df1faf854d03c0d308b74c93fb846e620de95de7bcbf", - "0x068512253a6b5ccc302e494d1b17ab082f2ff4ee710c58d345d6de4a436fc3ed", - "0xbcba2fe1b54c6fbe1438fcc26e955ecce58c5f30f4f19219195a413fbf1c6eba", - "0x29ad9a11da3f646ae4ef3a5202abab2ed8a7058f336a6039a5c41d3619752d6d", - "0x5fc2a5edf9264de88df58c3bbc705b8e73bba19179d097f414598bd4b5fd63bb", - "0x9592c969a049ac723d756bd2ffd95cf599087a1da14ee9f472ca07e7e54f8ed4", - "0x735ace051a5e3896a9dcc788654f95cf0dd9cc98eea1b5d8d7340f9e51eba698", - "0xed5ec9f70d613be406c237ab7e8e2480c7a81cc666316d984bb3f9c67e10d6da", - "0x3b8248b4de2768d9a4ed2ce07c8735103b480e23bd17ef1910d75d29c2791886", - "0x2a9ce5a8366862e7b790d865b0406d9d5d4013999f42caa9235d4c2b2813449a", - "0x6c67c00db439fbc9d548cbdb57377064419ac47211be90747c74c13b29d4e562", - "0x1695a7ad7e64d438a9b749b07180910a7af207bb46063a92d41be315cecfda67", - "0x6fcb36c8b8ae4d6e4bc21fd14e80dcb402561a011fb4e71408f05807b1141f49", - "0xf6ff3dd9e40b6bdf881654e12fc9959a928bb7d83a81189db1520bec70386ec8", - "0xf17f8b88f04772598eb87c8bd1b3a03f5d96ac68de7d36be6c1fbe5fd4a2ef4c", - "0xf5d9c9e25075f8ab6c34e9231d36becfe90df0ae6bece4ad1847dc4ea938ede3", - "0x5d409a230ffc6a7e149e94b0184bcd00eb8705fe173c67ccf4f81e2f0d3cb0a2", - "0x80647dea82a0c218462836c14cab2550f558391a901a708d7e4e6c28b4b71869", - "0x4ab906af7910a57f1f4bc338d5e952a899ed256e2a28e1947890a372aa29814d", - "0x6e6e180aac3ebd616af90069674ce54459f169d33d30d95d98351259d90a38ee", - "0x5d30699b02a45d5cafc14c945bbd208943ea83ac6a10229c199a92b87dc0c76f", - "0x19c26fe48c497a4b64a4d537eea9646cab7704dc28c46fc3510a14b226b9a690", - "0x5ddc881ec8fd1bd34601f06a5ae0cd23680e2e8bf12b78df48a5f86ed6e96ab1", - "0x39b933493defd81502420663a0c85df3a6cf727e4270816bb3cdec3dc58bc7ef", - "0x62a9e911426f267561f84210d7356069f6168c4e7afb9aca4a4a70d18fb34ba1", - "0xae792d0011eeabbae89f6de39ad44f0620e50e5ffe7392f9bb792a966d1f83f7", - "0x737b257fc55f1da44584687b3cca4bffda13ae014f089e75f0c75b0b2d08c426", - "0xe923546185355abaade908d18f3223bb9c404c4a399ff94419c3a5798c90d9ef", - "0xfbbd3e2dfe2e9e6f985d139dbcfa5b95da918acbf49de992d01875bd44b61e28", - "0x781d9962f2cbe94e355b93e4c0eb3a3de44d9779eabdc3c6d18e7120b3c0c01d", - "0x2722229fd2cef3564c38d8e4120a6ae89728ab4063c8199ec098f139b83ee0f2", - "0xe29866bef5051041d25449f334e4ca246df606c5d7c0bce7067c6c98cafd0534", - "0xe0320de1a972b563113eddbecfc90fe902f2ca80befe15378c94bd073b44eb92", - "0x9227e1d6c68bdd60cd0deb8ed760eee67af6623c44d05136251d89654f3f2e7d", - "0xfc3c7dd2d36ca954545f3a978cbd7e4c413ae38a0fb5cb59b84329b5f5ff2347", - "0x934c332cb52521de4216c29e40601d3250078fe9314a364064692daeb38f71a6", - "0x77aa1b31191caaf80acdf5d411c4b6b834a6e50f2f0ceb17e1f06a807e3eef01", - "0x43eb0928a0ab9a29ed084cbf9ae75d1e5593595d55fc8a32151bf56b9977a1dd", - "0xc6f4fe473048728c5031a465c9157e38c72367c7f937aa9633768b32839e765b", - "0x21dd6e252382965adf71b94fc0b9b2e6e5b0b9dc9732987e1fe8d8610b1125f9", - "0x9808b8c9e07babfb30c120a4a7d3582cf4259f8309e586ad42bdb9e9cdd3dbb4", - "0x72ec40a055dd6c5f21333f9540bbe7e38f28f39f90204f6f87f3c94faebabeb2", - "0x3a04743cd849a1a48e0b012f39fa4eb690c314eae7b0500ff1ea487a6d399734", - "0x484892e8d3585d871f7105a49cdda18af036d787b6519f9d32f8dac5a3eb9546", - "0x3a73f64acae91b70d39cb20c1ace35547e733b253b47339a1c47ad0841c7a27f", - "0xf33014797bb0e339f2f0dbcda6706dc6b33c8491cb9ef777ee2cb88b8fd1e35a", - "0xf94ec9b178e1e287fc2aa53d397960e89485ad22f306bdf0226212415900648f", - "0x8aac6aad1127202909e112df54be070e353fb9079dc5cbeac146ed09faabd4dc", - "0xb8fdae091491634efcb9b2f78f1258d401a9439d806fac04de4a0f9503d9259f", - "0x4e872ddaeda3ae79efbdfd3ad8e5ac3597003a933aa793c50fe74fb59ef43637", - "0x15293110604e842775b59198cb8dafbdf07b4069fa0c3e2d61ce6665ec8f6742", - "0x7423d6d010ca3aef37831e003164b583c2cde4e859705ea191e6ec687f08cc49", - "0x73828a90a0f97e5bd83c4a6484f84ef8af04d325b739a2cfb96b8cad36717b09", - "0xc9af1d054e4ba3af1002ff0ed63fc361be99f8c7bbe90fc951357ba28fafb8c2", - "0xff2849e97d79e7c25a58266dfeab64f512ebc57620d45d6f5106a71fa43063ec", - "0x98da87856c0aae3af6dece2efbe0a2f7bae295ece2b90b633ff894e5c83fd407", - "0xa17f5aec76c8f4aac84741baae18e179ce5c1ce0d23d3e2e5b4fc7f79ee91171", - "0x79c9a766323f5381dce7241fa53da4c7fa018a43556352c54b52fca209f3ee37", - "0xeccb07e66f967613c13a1e867f8a96bb2c96b7c0347e4262ebdb583eca48e71b", - "0x36c357f0249aff34dc519f420b77fe14357a009022262cca7f1467c2dff7d0cc", - "0xd52a496e2c176cd98b94e8dd4c18a9651f46ce47d70c51b21e5970bc7d81c96e", - "0xd3fbff9d80c163f6b9d4ad4660f4504101e167ba866266ff905f8564ca7ffb68", - "0x321b832e81f461e871bd05e6847f8548825464425036a1a92bc44fc30961fab1", - "0xb58be0aa640c7047e2b27c6044dfc44c216b4b69f48751686cf29e66a8dc985f", - "0x2c9153f20a845b8ec8e70cb1dba8436c7af618367c3344130defb1e7c39afe1d", - "0x6d129dba3e7c6e5ba8412d9290d7dcbd29b36f3e93eee6b6d203740de06802ca", - "0x5f8573d1de1c9b4725fb639b565a2ab6dc79d138c3d6495325e6ee609073965c", - "0x5f78ebcd3f73da424734bb2ba0c711fc54be4f21f29afb1c33af44c598d31dd1", - "0x6b48dbe0e509dfed74f60e16782d268076e53863b9f86e4078c90f4c6619b644", - "0xec30ba6ff376fc1ce9f4b87693e586b5fef2cc5a5ef26726d8e756b3c8e28920", - "0x8ac0cfa126e35c8dd715bb0ce13bc70c8d55c34be0abcb85576393a460762659", - "0x8825a63c211a92ba8f8f5aca7a27d1921df071072467c88ec9932a41d43ab5bf", - "0x39cf82416d543f45afcc6aa0cf68522d0d5ed4f76b07b9f742594a88309b393f", - "0x105449b542eb5ab0b1ec7ce69c5e8bed9a72b6470b644e0815f15c6d9122663f", - "0x10fd8c442d6d44b533675b937e5defb004271a87d4ca3b238a395002da42adf9", - "0x390a40177f4155c30bc10539a54991f4759ecea69c31e9b2904743834bbbc618", - "0x0f11e934cc159af39a4b22bcd6817b1abc21bc6fe81c442dece1c178d695787d", - "0xcfeb9c70d8e2d4e40bca485200c0b5db1152d9440237f76a58d4cebaf43cb1b9", - "0xc45c5247fcea2312642437f90870b6fb4b9927eb2af3adda30b5c738eefaa952", - "0xb6886cd5b6329ca5f5e36d1c49b7426b1a20d3e3455f660e662128193b6e1a32", - "0xb708efc6f2f6ab093482d8b53483a3d8216043a437da09433ad0eccd4c9d607b", - "0x12b76069143a66c55fa3ba54a27e2cf51a94d8300a6a6a317a65df345a9d77f8", - "0x0d9b6dacca16c1be986db368dc92d652984da9aad149ba7c22831ac94719139b", - "0xbe298f54460f273d2106a102dfda3e86d7eed783bac4ab88d8caadc0f3116ab0", - "0x82fbddaa5cf8847d2166d40f9fef5fad4933bf338422bc5457e5d5c29774e5b3", - "0x2a75f2442f4ed494666f48168ebe8620819d97f9749b279eafa550f20b1b622c", - "0xc874be785abc89e0fa92d089889ce06e1b2297f647c31cddd24d29017ba74b0c", - "0xa6984ff3e4f7ab1da131668f77342bb0182cd23593dded5e487c134b1b13a9cb", - "0x4293f4fb5bbd41aca9fb12f58e2427e20b791ea523fa125c40b72699ed89ce02", - "0xc316b1b066bf437d3933d7fbc8a8546a10f7c274d2ea84781d8a99633bdc0c3f", - "0xb40cf7adca885ffa6148d8008c5df235e938659d6249fd883960b847a409747c", - "0x3285b34b3e90a83293292221a89bb244a91ddf4647cf2d297c2116826c605640", - "0x465af97dbd1276496387568d41ec69961f70b382c6e0686c103a73bccf0f7830", - "0xda2cd161e715ffd3ca0eab361e66e55450230f9acddf8ee635b412807a9067d5", - "0x3136630cc6b24f59ba9c0347c6c612feb043f9fb58d1d26598331aa623fcee68", - "0x24014d00bd6ed71da4044cbbf256ef99927950736f084cba2a61046ed7c8d471", - "0xcf1824f9a407009243ade6df4e3f241dcb60bd7257d843792d12fcd42e659076", - "0xcfafe5abf154f2ca9b6f9d70a91149b01cc8fb6b8bd083e24559b860e439f04b", - "0xa4de7f7024d405f6738301df2132107b135ecf824777f1b844685bfaee6b30e7", - "0x507abab909bf25bd3907ff757a69acca54af0c21ed6faa975d20106908e8540f", - "0xd6e584b876926c3989b84151eb574d737bfd229151dbb0f653b693b2bac7c45b", - "0xed4eedd2952e96c9298f68a1f8ffc4772652517356ed4a02762a3355e7dc89f7", - "0x36d0a7f0f5ebf3c35a576ff56c8aaa1caa04dda95edda5d496381c9601949451", - "0x9beca310b79181f7695a0b6b49e923e7b67462904457df0af82c88b92fe4ac3b", - "0xcf792a534a0b5126116e9f7c9b92aa4d32083fbdb539c91dcb8de347000b49f5", - "0xd4024c9ca180adef5a9311c8a2fb0fe75e64728af00ca4ea1484548f182b5488", - "0x5517e825a0d6e1bd9ab4435278b8691ad2772b9edb581b58725483c4393187f4", - "0x9e4ae4c36d00217af787ee52862f87bd37cf391adb23a8b51fc05ed1ec3f6631", - "0xe428dd2523abc537b868bb6f5588597175a622767b600b1ed9486d09624c8ab1", - "0x2fb95198976ddfea942f737322470f9de6635f7116839e5148d3f64766f6fa2a", - "0x211ff8c237bba495c03bd6e8eea2a64f05c8a0eb15951751cfcc4c337f1cbd02", - "0x44c49c62604c39e38561c7d9285b9a2e168fa164a47c7b17f1fe83faaa3c581a", - "0xfe663bd908aa64290ccddcc300aa5f616455d8f10032af9317ddf717c94d0bd8", - "0xb04add424a6780e81e2cdafbd81699fc5ad621c666e1ab5583f3953ce8b3abdf", - "0x5f2f6958e89ad99e28e96059aa8b6e2d9518009f824874e327dbce226918e0a5", - "0x491c9e17210c2770c0cbe566aced7dcfcb6781e70a288f91d4eaac8dc4fa07a0", - "0x73651853f625c534d5848d1bf7f93433b09d9297b13d46aed9b022ddc37c4b85", - "0xb1333ff43af73637e2235f97a1a7a5f42c590cb15f93a733cbe16d1a9c187e0e", - "0xddca0492f5c36c8eba140575b4521227ad1216d04a7e60da7c9727cb2a756df3", - "0x6da48a25ef31b1e25e33b1df1c539b14e6d3837f8061c6a4faeccc861b7c908d", - "0x7d250fc602fefae7409a7edb1260f4c520d4717b881c8afb6551a89cc781efc9", - "0x043db87fb979042ae7ea709cebe4ad58028916f035e99382be9826f1236e5753", - "0x01b125ca25e2a254e0ce54e4f3da04c647ff6fe982f1df158c3caa90b1e2a2b6", - "0xec617f330285f2d4c6ad161250688cbe57bb81a5e45fb11354ac033e2fd724cf", - "0xf7cc72b3307f0ed731b990d2c10f71cf195b02f7e1d2767873c3ffc192cc43ae", - "0xf19619c5e62c5f244ef4156cc2c8f6edb6c230664e4c19ab32e1fa137c9ae173", - "0x91c313a770e9eeb92fa74c335f9cdb735bd14582b385d2a380bca534cf7a6dbe", - "0x14c1ecb6b06c18c240932c705154f009d9032dfe576ddb545a3a4cfab9968fc4", - "0x7d2cf6029c764b4c676c3a30567efa13f0fdd3cd6587631bd79cc08f26d75505", - "0x8c889efbf596d20043d654a6cda1a58d480ed2e3c3db65f51aabfd25f85784fc", - "0xe0bcdec3debf12856f3b4fc781d407a52b6b3d544b56cddf847105760a99775e", - "0x32c229b6b99b2173338bd7615a1a0c2c4fb5acafd41e332c9be7dba91b8e89dd", - "0xb24b187ffb16385b85e6782fcb4563309afefa8a7900cfdb81d024c2257913fc", - "0x81bc38df942c4c9c309f19e4fe5119f3d7fc2347fceb83f7d31e990a042581c0", - "0x1e6ec7caf14747f470d65bea046f7cc03e081271775be615905632ec9a18a637", - "0x4eea8052ac23fae83fd1fa63b328bb4c2c66277480b78ecc00132c1afa53d37c", - "0x8810dfc2084515339a0c6f1b3fa771aa0819b47498e57b3733aed0d40b9a55e5", - "0x3cefd28b10c2ada59c1bb66a832f1ce79c6d96dc0eae3b2759515839cad25ce0", - "0x8fb0cd31102c06d1e33f782fd9dd7f6f9af8e975e10fbe560183ca366d088039", - "0xbd7842672dba588021f7b395a55b949d41090039b14741ac667aa75977e00d62", - "0x7c4ceca9b53e12dcf2bd5a625274f30e2a825d5075d640c7bf78abe4bdab907a", - "0x945f8fd6e3171159a57fe5ad95e46cf93c494613247051b941bf4ee7fd6d4e29", - "0x01e9b5d3ec0f6f100907603f591e5007d086086476dcdca849996b98089b924a", - "0xec9545ab618538789965fd828fa8830ac17ee52489dcbbd71b296abcf12c11ba", - "0xdb1466a3705b6a035fc7794fafb497fd2e7606b003e73c6cae16b8d9b2aed314", - "0xdc36ec38da93a79b5a1e70d1cb3453f3ff324793d2fe64fc1c4e8fb1b22e7f29", - "0xc1f2a76b96cc31541362b5d6ae9271740bd540631b4f3f0274ecc5928e17e10b", - "0x4a5108aa8e22f9555d94caa9c29b9793b4a97e2c7270b8e24b2b3717d9efd8f1", - "0xc8938a2890c4f884837acb06c4a3854d1c03286d58d5d62c85c06e00d8597fe9", - "0x61e1708488fca9b334a1682d4f11bb046b5088151a7333655c733766ba0e5e5d", - "0x75705cfc2a41a44eada0a51ad75eb93972d84122658c487803287235c0a5a405", - "0xd818c5f5dcb79e84b2b41341261bf3e4e97f1bcbe572c1eb5be5f7e3eaa52db6", - "0xee9b8a62f9bb12306b38c129ddca08e1481db588b6e07b45a7defc3750cec078", - "0x1bf73759029c056f0e59b27a8d05714c916daf36517080e724662bffebf9e380", - "0x4ad981191be35ab37c559020fd42db86f57c56f51a902017aad1deb2d71debb8", - "0xea76176e1ae162188a798fc6ba2426c5e39b98a2832ef5f730c06d51f0b6d71f", - "0x1e425a410f888435fe7c1d6912efddf8ddc3a763a136ec730c31ca3ed0a3c162", - "0x5f6ba29d31dcbaa0220a7c2f901b987e968233bf19f398528e91973b421aacf6", - "0x935266f8976c34566a762204de924295a5203630aac2d2bc9c9cd0cc8286819e", - "0x323fa129d881b6406662cf69f01c720060a13cb7c93ea7fca3f089cf47d1b9c5", - "0xe17499346fcb6ac5f95e10c794f05ad12ba85d201c4d9a2571837b287dd90fb1", - "0x8bd288a3aa8be55687979fa9d94ef1d1ef8a0036bfc5615419920c6df3c31bc3", - "0xf05028fd3368402e82d4f6c6a4b12e622b048a612f80567d6e874541998b87b7", - "0x7e35b77cce743749b57b7614bf4f3724226ea82cc1f76e6f6232ef25b42eb6e3", - "0x58cb381e320b2ee7ebf17aba08316d86c6e8f2dc038f8fc6839e94f16b411a3c", - "0x24c1e91b745127f12bd8603ca59bacde6fe1b990a1d4de58c5e91c29da27e371", - "0xffb99ea8a8fc58a1c7e33ffcc199519165a452ed9b87196cbfb48ca23198e571", - "0xce37cc2df44d4308d1e5b846d46c9d7f2bd78f6b771b1eebcda1142858cbd4e9", - "0x0babd6dfc4f5a3da90ffd6f74297db7ecc7964f23bfdd3404f49c47cf7019856", - "0x97ba6463ece697d1b340dbea1e4e51e2f59e1f1283fef71d2aef12060b188b6c", - "0x3cb4d7dd238435f316ef70fee4bda79bce6b9a333bc10a36c2e58f7fa4bc24de", - "0xe44502680a574938a0f483c72cf5843ffc506bd2d9eae270bd108a4b7dd55422", - "0x3c9ef69fbc9a23ff7646a9d99f27584f3969b20bc56249dddc7f83fef68742b9", - "0x555a8316eb846760d2996998291e81d5f80bb0d8db71e5c02c42791ce83e456d", - "0xd4d68b9b231fa41e4571e258fb5d4770e843b3e50832aee6c7e327a568c062b7", - "0xe0a8bef3a6755a4d50f379f9f324f0404fe677372f307fc67ef2b938eedf986b", - "0x57f32b8c263c9fa6e3b5df8603307216469dff93a400290cc54f3a76b54a2a75", - "0x3749d954f6fd6dd710a0a9b4abcb0da071b3c50d125206c0caf4f4786d590d4d", - "0x58efd182fb42171c896accf391b30f1916503f616102251b9dccf2a45a823c5f", - "0xe44810336515f4f45693cbc57ad0eff2105d70faca32097f7ad6aa505c4d3b67", - "0x0d720fe2f29af2607ee5582016bd0f122f63d0f82a84c4931446a184843594dd", - "0xb3106d35e5a904b6d460ffa8cb494f6a95045d717b3f532847105bad0dc5ecc6", - "0xd1bcc88ff9f5ed1b7625e7242ecddb2b615401dd8fcd01c9b9f03a6d22d144cd", - "0xd4d4702668cc3673bd4266be291aa0f25e8ff835df3c43d556df01342986a279", - "0x9fb62301f00d441319f81de73fd49fa1126a29b3a11b022b3a92401dcea15772", - "0xdac3eda26ad4eda3d83c0e83d65c74ca4c4c3ae47eb4e5e82c6b315f80266125", - "0x7b24a259081a547d55e9af822fc54d5dc6f69ee3e961ebd0a0248ac36f6e25dc", - "0x64e96157f5e4be77c9f50df8ea9c518026ce1d7db6adc52189c9354922504635", - "0x35ee981b1a7d307671605b25399cad71503fc9ffc00d2f9ca80e868901e9c269", - "0x6965520591a7697f7c62ce4aaa6bd90691ae1a89a5f8e29237ce5241d8003636", - "0x30c305cc886b538fb8808be71bddeaec3a1276986906b7f028347511bbb653e0", - "0x5f63caf0aba563f62811903619a10786761011d2fa6fb58e835b8dcd5cbcb3ff", - "0x6d7219f08e8288e4dad53a7fe227951e81d9ff2633bbfa636a3d0b147b98342d", - "0xef315cb919bc5537bdc9bc47d2dda7442e4a372c74163fc2134173773ebfd7a6", - "0x83016cc642eb5b55b5a667d36acccc35eb9c33cec2caa28ec263f00800bca2c1", - "0x0c855426475046350b58fb7e179202cd794f482598d73ad05bb0072774561493", - "0xcc8dcd45a49fa55fdd2871245cbc611613f203203ae4ffdb1e647e7143b8513b", - "0xefdb25f8a05ae4c869bbfbcac9c302f3dcd8a917eca4d5807deb09833e876820", - "0x055646d6adfd108be923b275e21c07162227694df780512d0dbd7a0b58cdda91", - "0x4abdf1b201aaaf4724e81ff29e8d48a223f7b98ed6d9c9696375047b7643280b", - "0x929ef44a9c004029274bc1dbe13b49bd6452ea3a3ee6d378dc296c9baf0b1e9b", - "0x909a57470dee2764e5ca646d6711de3908b3b35d98e73f67adc23a3a8aa3c48b", - "0x42e5bfa041faf65f224d7985ad464a526a18fe591a156c0b70df9d397beb9aa3", - "0x241d0b24f3c0e08a17f10d579e49e72af7d2e4d0f0d621ea8975d50a3901950c", - "0xb1f3ce2968fed1bfcf363a031d79cae0174d642e931862adff7ee97f947522ed", - "0x7e4db35d3aaf0bdba5cf1d5465f77e6c927f42b775e8ec2bbfb43be36270b5b7", - "0x7995fbf9d69f7222ddb9d28bacc6d97c61780755e933d3dd47998f1f3747aa32", - "0x31317d422307751b59eeb45270518af357bcbf7ef413f81c8e7c6bd48d79d696", - "0x5c58fe54e2781cf91a9d09e080a9ae5b6a8124cc06275aea65ffe422567aca2d", - "0xa4da628ada51d57b378717218e6753fda428e2b8ce88859f37837b4c2fcc5076", - "0x9c9f744e7e3275808f9913767657af8a33a7717e45adfd84305cfbcce328a065", - "0x1381182d1b1ecd75969148742706a27a044cdeeb5cd984886945e5de5f3311d5", - "0x71a4759a9096d67a4061ad639ea4ff7df35c811eb1c7b10638aac889584567f2", - "0x1bf9536eb1692291213d4f7f340647025c10793c2c84e8e775db5cb5afb5022f", - "0xf2af02ebd1cf78bf30a215d80133c7d5f7361a58b827efa23e940e7c91430af0", - "0xa90ab1a925b660baff138369712be90a6cf8ebb658ccccaa9e8b0305adcb4ab0", - "0xbcaff27b022e0873b10da43c78b1395f1878e2ab1c8cf33368988a6060c6132f", - "0x6154955bb4a81d1b96e618ab29a53c393ec78153a733f90227c9f83683131eeb", - "0x451b4a606ef40653e1ccd38b07fd4e8efd9fe796e01b5a5772ff903b45042187", - "0x011e748c7fbb39346bba2b3235c185fde946d28627e2f6ed39c68076d5e6fcec", - "0xa849d075319c4f0cb13f3115ab99e0cdf0341d719f5c65a36b100ac1bec98826", - "0x1e61220e9a615b1ec127001aaa19f307c268bfbf14bca900ba1005539a7ced1d", - "0xebc6254608585c487baf6e312130a06468551cf4ea171335480881f42ec626a7", - "0xce073708172f6fd1e6d792f4b9fe088dc06e164422c5e85e4098c0c8672bdecd", - "0x8102653797d93a7d372b1ddbd92c8c8cebf79fdc8213392668d62181c2639a48", - "0x2062a79855e50c96f3eceae62ab0f873eb3f051f66931efdb00649f07b82061f", - "0x28302f6a2106296e587b03f2f0963ae9c8f8eca56198f7d5e0fb52d4485e1da0", - "0xdb69e062fd4f03ad8327c3b33fce7efdd2a1455cb9c1c90a7e2e9127b12ca041", - "0x984232b5675edfe8312be559135281ff49be1219e3ab17e84560221368166aae", - "0x303557f3d60ebeaa591cf88e583c366a7db4adf1031b3de9c8d3381466e15b7a", - "0x2179d8b03104be3b8d2e6f4ab4c6b9851bf85ed4545aee93fc40d3320d3b5d2c", - "0x708174830261838f9d87279a51c4d65814f7da2495c1a7ba1eb867ae7217bdce", - "0x5c5b77681b0d9d6789aea3494c60571311f2b6690de38c2e4c2a8caa738afe5b", - "0x68782d0db788b3e7441e94e4300a93c50d641c060677c6ac98ac50c69ee972ec", - "0x3c3acde8f5a99d8e131d33c7d55ed4415f0c230ed6fb711627b11e464faa3264", - "0x0289fabcc6d32e03cf75d7958e22ae050c8fa6b31c03221d78ddab3469d9b95f", - "0xf10cc55e619ba8b9aa5ab92c272ed1459020ae6f43e45fd9d427075d86230c36", - "0x20ae1b4f3c79618339a0f0627919ab94809328164ec1dfa6b9e1f556e68ce770", - "0x071e6f74436830ace1c6bca63d915218d8f561e184d15cd83ddb199319907dad", - "0x7334a6ac9991ef0876f0b4bbe18dfc407e748c65ee79bcf114c1a2a71b46f41a", - "0x4f44e709a1e59bfc7315843f399add75d5c1e8c3cbd0f485afee94e1c4cb0d33", - "0xec0514297634ea605be68932d68de290e15306061e20a16b2e3bf675fe573bc8", - "0x9c72bdae94b6f57b594c11872ee21af41c704ba1addfaf8ec4d828d6ac6101a7", - "0x1d1a60f27879b14930bb0351e39d3d31233e7d55f3ecae5c9cad4affdd2edb67", - "0xff4fe06f62b19f1e6079955baafbcf5b74627785b5edf90cc49ac75490a07f8b", - "0xcfc332e8af474ce72548c5f0fd50d4e6c10858376b0ddfb706e99703177b31e5", - "0xd7d5eb515bad41cf09df99d5fe0400ecab66ebcc9f7af53ff8cad256d9ad2bea", - "0xe6f7024457d9e945da6737c34090f689bbaacd479849a7c64afb6aee85b6950f", - "0x15fff05c7ff12fb455b77b5430532011748a52abce46919f69ee0a4c30b59fd0", - "0xaccd46e8d8482209373407310b988ba3e1f39d6eb17f07861d33bfaefeab3347", - "0xd97650055032b7bdbabc7b49b7343d0362d6241aa45fb17ce9ca1815e7d58983", - "0x0a4b45c31d3a1dab81b47da37e896360fe5a1e45903f59d6ad0f0b8ceb2a7747", - "0x8b2a42405ceba8740c02c526023247a262088e35588fa4253eb6e229c273463f", - "0xf43516578e7fd03970d57b08384b0bb3441ee7f50213c2ac0d1830d43c857abb", - "0xe2aa1bc333310c27fd699455b06398679a100f6a942c0eb37f70da89d7f025f7", - "0x1f13aa876a2a348d7de6adaf51e8af65ad7bfe0332c4c9cbb4dd709dbde8e1a2", - "0xd766018ae514f5b7daddf6e716bf4570456cdd46e7a3be68c1c515f2df441bf3", - "0xa2ce855ff00054aea57559662ac95b2445c1fa2491d9f4448183a474396daa53", - "0x8583ceb1254f6546539fdd57d1fa1e3580d81f1aa44dbc875de8b0f56e5e7ea5", - "0x7e09579f21572f2ee72399c3ef67c01a5f3f27ee4a3c43e8b6f692e231fdcf1d", - "0x6153e318c7eb123db88f9afbe8ff1c8f66e1cc0e8704db45730912a8281b3366", - "0xd31dc325333526bfcfd29e50e5fe13cd696e5948910abf46e66dc73f7d271942", - "0x9089fab0aa038217485edcae76bab74735a1fd2897c30a610f5e43f1d2da5425", - "0xe193e06c904f53031be5910209b7aab785bca6b65e917351764e2f231800bc4c", - "0xf732414a066d5a8fd13241f5aaa9570e5c6285119a1f7c69063a8b4cb62729ff", - "0xe149ce7e516f9d54df933717a11040e4a9bbf8ce13c5a3612288009a99724295", - "0xf29212544b867ec2505e35cc5ce79ede41fcafd676f41df15af7f178d6dc5cd7", - "0xcdbad6c9eab35d13dc6461ffaa793b180e28085b8047cc56a5328f4066ca944e", - "0x13f5597439e924b31cc8ab8b9cce0776a8d89bdf7e6a0a306763ae259bb92787", - "0x791e0daad106b9ba1ad684994744d2ce89c6e413c51954321d9eb3d87e0ad14e", - "0x892436f41d7a6d8385d66f6ff19fc7235dc6241477d2745792bf3a38085c8d5b", - "0x68b0efdb41ac9e0c2cf0df78ff6f9f790c3c1ba63ecec6bdf9426d860952f81f", - "0xcb4cca4320ab1a4ff11f868d769ce9fbae1e98d154207acc85acf2f8dab8c4f4", - "0x3dd2dc9e0d99e9ff820f22283ccae495545ff37b6e7e89b3d721bdb5ee169eb8", - "0xad216b8e962ca5b8f388c89bdca543059065c7b6573c5c0868be031b74c2e184", - "0x36953abc02bfd8d72b7667b068f63f6c441e78938b15591440a9550ef796b8c8", - "0xbf85fd9301cb248c0e616e4297e2d4ec43783bde218ab67a7e2049059236f633", - "0xd6764c6e1f3e8d671f1cfc1e55c4d65b274fccdf783ab1b27eab1099b9f46299", - "0xe1f4b5fc4fa5e50ea8a7129f2786d7b13531d608ad6e413d3acd0637ddbfc5ae", - "0x225f99906c6ffebd2c77eda9c7b2e318740a36f84b8d6424b2f479f3e98b1fed", - "0x7850e352fda7e9b524fb1b1c04ee4695ba3613441ce0c5321b26b9cdb3317625", - "0x4913a582ff57cb276667a4eef0e85d31c79ff5f048b8b017dc89653fece5e88e", - "0x1cf357321372b71199a20da7e7490bafa5d9f837fdd3e433a68e8f84d03fa550", - "0x978cd04d02821dc54a1ae97385ca618a91ab42e9c227b5da2be433369825aff4", - "0xef0098b9b604b3bf4b7898d887b5f36ba8c196606b5688476547e89981e84a21", - "0x1c9cf750c7a5188cd346781612291b20ab07039831a77e55ed890987a876a412", - "0x6bd6e5e5f57b23bab83994c68760951a0a5126976fa9c46a99bef5b329ec7cc6", - "0x14db2e7acd6fccb3b019382385a1f988dc368d5da69cb96956901bc4ce06ec1d", - "0x48c20e5365cd6febb128db3fb93ca7a88b7f2cd3dfc5b7a6fda331e62d8f6875", - "0xec6ae4cd53c6c40f3b77e82cbf386d478120145c0c18f26220eeeefccc54b883", - "0xb27738db32dcb1fb5637ee6b86c528aa9ad75a47777931d45285f0745d6d9618", - "0xfe9671bdffedf2170714da275ff0dfdd533ba1be2ce2590bc1ebdea9208bc951", - "0x25cbe60b7936b87c09e92b9982cfd1bf80ee1d6ce65133434ac330065c9a60c1", - "0xc777ee866895e41d8e7320601a8341fcd99c98ba9d600888397f1788ed79fce9", - "0x63425e84adaa76af6e58807d3fc3dc294e9bd9c32a170a60d8571c667ee804bd", - "0x086c8a54c5d1c2f4160d7f0f5f6c1a333ca91875ba23727025d77225e2f4db95", - "0xd8da17ce51dc8419c40723d1c3a88f74fe336f9a4f98eca61bf1349b0d4a89da", - "0x0d2169c32328187125474e97c7a917253b90dfecec0682f3ca7982e064fffdf7", - "0xc83f335b6903eb3ad4b0f94fd6bb3a8b5b463d0137547a4252043d58ac00df75", - "0x175f57c174875ebc474879251e4bca53bb7efa9f27f456f10266864719e073e2", - "0xb0ea4c419abf7a74ed69807b3d14e0de6def5f55e645fbf0a625c7e9e3765201", - "0x4a246d6ec272afa613f9b6aa288477795bceb920d6b3ed1a78286314cccc2e35", - "0xd3af9fcf65dce23d27b46704753c0b4b24dc752db5389876d31181313cf91ec2", - "0x7301d3627cd3bc2ab317c377e4f81ef6b07ba64939813b20050b53c8f63a533c", - "0xcf785d9ca7c95247ae005537d0db37ae9c54494b0a2c1e655e9c5ee1292e8338", - "0x3de4a30d38f8ef549ae45b777149a547c8cc785c6e6f48e5dba40b7bc7fb4c82", - "0x27fbff002680b942c31cc8f04211be03c8646f024857ae52121dec778163452f", - "0x2a37be7d47abffbfd08f1aff379a27e1d9dd79833f67b466a59b8ebad854e3ea", - "0xedf73b72736afb79d07c6e726c74b88039da0c21285ae55b5e2c8191d3f802f0", - "0x99a6e045680aaa9aa3c175140a9337aee7967d78e97b41a0cb37c03f60c24b63", - "0x5baf77fcc6542689195c10fedca877c6d59ec418867f25c79d4c3ffd36493b84", - "0x85d64816bc21349e07c5dc852a8790c6169d26274dc4a7fc35c53c199c8f6dff", - "0xb59cca680fc9baee6d3d80e11e374bfe8c111b0f47be83157e54e6d18ca93e02", - "0xae13047af776510ee9d13cd6af0e669495704651d5894f30e5e49c6c9d33bd0c", - "0xb2cc9c6705f969f07f700e3d9fc62fbd9de299ca3510985ae5e6fe339e187c75", - "0xb198d0787de7b8e5e9beae4821f587608770fab12845a901edb33da4c50dd217", - "0x7fec95f60944c1796bf42a978a3a771613b889fbb14e6cfcaad143819bafff9b", - "0xda81d0a99cb64b0c8828a05f11bbe38719cd6ddd6cdbdc814a317cee492b1503", - "0x554823c8958d8631a06ea110de162710e66da426210c4c89bb210684c7dd9394", - "0x0914908175ce4d9fb7415d5fdb52367c616a1b95198c9421459cc1c769911cc2", - "0xfbb629523797ff98da78d0f41e2acc45acbb20e4a1e0e1dacbce45c69216a964", - "0xd88684c269bdced43c0bccbf8c041aa6fa2fa9354a6aa67d7053aaa0f2074962", - "0x3920c4c386860756180fb1f7fe605801f7696d5b9e4e1576e1aae0e8525df880", - "0x5deb5c080a346877454cd98ee36312bc3e4a79b3cd9f3c39971d892ed955e927", - "0xe099ae7dfa63b764ef3952ef921cd229acead0b0e047af94030ae09a34b0be2a", - "0x7e754b454ded479b4bf36835b0d52074183c9eb6c42398d3e7338948992d08bc", - "0x1b22378457b71df9263473eceba4f57933f11d33d31be11ec5edcb36df0dfd95", - "0x6605794adab619d57a2ad55913afe54cd5ec219f3d6740185409ac61d32a2567", - "0xa1039d1a4bb3e763dae6dca5b8c00deac9bf0b76c178eff53527a95852c587c0", - "0x9d43d1daf7c4295431136143ae7ad2192b71c16e8da4cd1ae9a282d113122c6f", - "0x38d5c14dc112d21963208fb735e8f76d9842c2ad13e2cdf32b0f6e70006f911c", - "0x09396c809912d9403208968d6f13d8845398b26e93f444acf7538e5c357df7b3", - "0x1ed6f3b995bfbabb0d55b107b2b3ec11c4be5cd68f207d9d059a8eb7671ccb8a", - "0x69db9592c01441747056fc5c646057d4bb992e4affd23654af7f24c47a002900", - "0xef015d193b0137175d8de710c2141d23cae1130c368a20f19bd59f9289d1a17b", - "0xeb63f8ea306392d336b2a8e0568e1a067d8f40c4e2b28f7911ebe48a67638942", - "0xfb12bbb145b5dc7ead7ad984045e62b922649a70ec00da06ebd9eba65fc83c23", - "0x10bdd7c613ce8584ac10f75891afefacc98cb0c711504e0f0284f634d19c3714", - "0xab851b2b35fcf427bd0407865a5989c533b855e65a9891e39b2a5e161a345af1", - "0x6bce48ac8c3553878ff831b672de737cfc7d7c3293e86fe322d229ea3f5d2c78", - "0x0c7313d927f845de9f7c03c09775e2c8fb8e9f2fe63413fff066e9f38d799ed0", - "0x347135f2e53f4002b5687d287b5134de9134eeef4cfa4d4494b086d957157870", - "0xedde43ae708664ffeb9d92cbeb03daa5fc0c7d1d03ac3e9d6541dabe9ac50c65", - "0xd14aabe2a11d404dcbabfbbd8d7c73ec05af5a0f9fcb79bc8f198f00420a2179", - "0x2c9d2e1c881d46c0fd68fffbd5ac45e3dbde2a4d1ddbb77d0ddc4f520ab83c94", - "0x42ea8109042da88b6ed9c71dca34907f54c356b46db55955e3a6cc1541904a0c", - "0xf81c890a5e8660bee4d5a0f18cc0f3a709ce48a5a49a36f0138ab6431783263c", - "0x492144091be2113571cf9cc84c53390fd00db10eafa2f5533f78b8a5d6bdc7b8", - "0xb42ff2a6b3d1e736c56a37a0a46f927caa4ba9fa6bd3f2d1a98459c7c30eb262", - "0x464c4d7daf1acf7786041d3c797f15354cc03cc5620454abdd88679659df1292", - "0xd5dca22e9c436660ee18a941129578021359c0bf2f22008b161c91494ed80594", - "0xb54d6f7ab969f0f95ca94d0a2070b5f2aca9f991e3b64a60c903a03d3d48e938", - "0x1261a3e911769c5c55151982e994aefd4d773fd96e8583f41a44cda306b4e1ab", - "0x669a364ae954397361f4c703c102513315552af241e306406fafb3dc70db8ec9", - "0x54d5a3162659886b7a0aad65839ef2cb72233dab58bb91961d6b6ccb1476da8d", - "0x449242a1b0ecd0bcfe9acdfb2b6779c9f291c98c09add8e13981617486423bbb", - "0x4dda9dcd34dbf574f62d475b7e3052b2e393be6331ab4b0ddb3f1a6270041e8d", - "0xd23f7682f463c5f58940bd199136afba80496fab562aaf665710ff1d5cddee66", - "0x70503762d711f0ed6eac70a12b728f3d84152094b1e4f9495870433592cfa268", - "0xcba23bd9ebcf36500a69fb8be017f0ebfe9872abba5070c6702047e3814dfd0e", - "0x0e2fd927efdd930e1843103c837526ea8f846230ef6ec9f0e8af61f9d3b19aff", - "0x518a527d8d9af923c12692405837e35d58a4625ca36c2041e539d9e369589192", - "0x0a697ad4d29f33cf10f6e08024a18d36a09f459f8ca427cc8b36def0fbdabc1f", - "0x5d96d5562fd252ca3b5f2bc6e6439572ec842764594463b349fc50cc8681d8d0", - "0xa093f86f7e70f80c308ce12554c6c23cdfc02dfda87579335ff1983d549619a5", - "0xef0d1f6787ea178b22a66632a6e44ff464b7de08afd8dfc780f39720b4746a08", - "0xd3a6e3eeaf50eb34d9af9ea4fc461fd4784ecbc6a2b7a8eadc37796d7f00218c", - "0x4c1dac930f116474ffb251df529847732dc575ab404342e651d6fcd67708be96", - "0xd5ef4aa7047c1ba5f18e81417886490624b5520743a1cfacbb04df4dc7724af7", - "0x2c0b01e814e880c5d9b9e01c08671e9a5bce1aa1190e58e0fcee1acf14b36cf8", - "0x464c6952f2738c8b28819299565a7ad027ffff4bd74d06ffe210ac5a44760975", - "0x5bb7a03847d5d77fa84567f913b2da4b87e4d2d453bd5194dcb15a2d07fb284a", - "0x7dd9b9c68e2df4a415c1c4898e979229a764cbda85c215bdf9b8eb2fc0af21e0", - "0x75048e56ab8cdf0cc6031e3a57fce809e540664872bfef2aa32fcedffc32e597", - "0x6c570e74626bdc74454167d24b0e755469e57930c0c5514847db10dd224beede", - "0x511151b41b26e5137b1e564295744e6b9115a277fe569a355a63cd483e2af7c3", - "0x885899509c8d7dcbbe3f9b6fc3ab2b1ec3df00909c92450dee7b329ba7abbe7d", - "0x40bc202e2040897eb3e06346d5d5c71276985470ed2e582b6f32ab8e4107984a", - "0xb442612b838cbc33bd65416baca317ae931bb2b6724bd565ca181a4aae95b590", - "0x23be2219b6842dfbe704c7fdb2bd4723cc4c12fff81ef96e0368f0feed5486ba", - "0x8ce2344dc7c2f11924f54185e191f22b536ec7a9ed82930e223e0dcac3ea6649", - "0x68b8de1cd88ae7fd5be9d8eaa51af21efe55359e5dba2aac1125232d01ddaf1a", - "0xc771dc187da76a5c761c9f00dbd9f50bbc0f149ef98f897c519457104c9f1a0c", - "0x697ef9d13cb317e94d4a40b9c6f45cba2680d6a2247e50ceff250be0f0934fa0", - "0xeae45a6899bdc251611165055e02b3fe3fe65dd938fbef43b01da4282d7fbe8d", - "0x3a352faefaf059235c46420d95a5d26618933e993ec97dee1a87cf55be10d4d8", - "0x59a105b2a42cbdec8ac080fbd09c98bd671ba47ac654c69fcffa0089ab9f4d95", - "0xb2e2c32ca966983c1951db62ef157be6824228d14468d1cb9856d53f9be6a9f7", - "0x67371fdd266043a11d59d4f05f0c5d920eb2f60b434928d7b558099623050c9c", - "0x0d0b97a7b38358d661718b2b77f50d1ab580632f030985d9e44ef3116cafa38c", - "0x2d19591fcdb7bbfe41412891fa43201363d0a5b18a48994674dfeb211701507c", - "0x7b42ac2c855a619963157ad7f3d36bcd1de0ea5689dd3e454aa2afc7b28dd071", - "0x16298885164e7c509b99d5a05549f95f57f3401f723cab1719b76a2bbce8b0d5", - "0x91b028e3ebca0b60cbc4080fb0f816d834f0898fd00ff42080b3e8dceb28d66d", - "0xe3802595c56c2981e3a4b35c149e29c421107c47ee6df2b27e979f59bb06cd3e", - "0xd285c7cdd2fa2eefce09e902c86a1856ab4cfe33e6695e94dca8fa74695a5424", - "0x2b78d2e0c0770ffc67b92d56c4b26a675e7f3639ea394842edf9019ad6920b2c", - "0x495f1461f887371cd29e86bebfedbd429f7eecbc7c759ccfa56500d7845db4df", - "0xeec765f7d794010adaf8dd7f07674eefed3f6d5b9dc43e39a3b3537288b9c9ac", - "0x1a4f6372274bdce3bc6ea83f768561d6c9ed996e72145d524211df0130f3b7b7", - "0x3955a47abdd98eebd3d38244e408aab387096dfa74443b9f607fb6fa59456e64", - "0x7930f2d3fb73869045d7b144061eb1445ea74202b8f090f4f017c27e1c4350ed", - "0xfe0e4fed81793226f51942fec125072e439e701bebbb049337a5f7b24576d29e", - "0x87a4cad6943d444f9da79e4fcbffc198c96752ae72ee3fc2487a82e724a045cf", - "0xde75d28f56b1fc3e411dbfd9a56fd748de3f7490b931328f3a5131c143832218", - "0xffed1a9fe59006557ecf383d162f0f6011b59f63bfc693d61b77232329f8d956", - "0x1cf303aae3e5d342a33d369736563133519e1434f981bbe6067f444e16d89230", - "0xf3be3a8b280dde0fd0aa8322cb7836136b8801420602e45c8ceb84ebed7c7e02", - "0x2813d9eec11134a3987955f4389d4bf07fb4b986b700d982f89d2c81caa2ac1d", - "0x28f236d43bab078d0e9662be120609f8de33bb4661596317905815e7771b42d4", - "0xd72f262c68e657dac4247a7db98fd115b1865942071d0eb06f68ddbb64b392d9", - "0x4215eb42c3bb1333a0acfc8240f1b5d9938b68acdcf0c048fa037d90cd6c59aa", - "0x5a70ad627a347a1884a9e7fdd6c87cb7c0d5922ad69054a3b9d56e96aee436c0", - "0x8ad7e96941d03426434d6b7e0eac92a04e94108d2b1fe71178b582f6d861e2c3", - "0x9902aa9f7e7f1e9e1da269bb387739c103b3ff7c67032097e309df96768b21ed", - "0x0af467ddad68dce1180ecaf84a397e5c8fc98281ac52c10d064e5972fcf87d52", - "0x4f945046721c0071c9b53a8324b391126d26c8bef20435d60a6aaa69e2d156f1", - "0x78bacb8175c8dd89e4c12b74aa687211114b47906d763e0f26cba8410e5e3300", - "0xdf20aab8a9e472d877262d3985813365acdab5dffd05139f0fb87eeae10a4627", - "0xda39d3fa1f48b617d3586f3700119f60a294c1e3d2546393e3a4bf09ba5e3030", - "0xb6ee6e1a15c36710e6d37d2cee67f4646ac94de616c3ad65b08cf6b7aa3b32c7", - "0x17fdf37e749a06f9b8768d73993bb7fcff8b6da75ff4545c1facd812c5c89031", - "0x17c88f6d416ac2e913cf054b9116f8639a566dca223243fa9dba0e0dbe047597", - "0xa04fa8fb398b38a48e134c62d9e31fabce109651e25fab8d32318bc6409edbcc", - "0xfcc5dd8bb39b3782154608c5e3c2a6b050382ded90f0b8352c645ca2b80a88eb", - "0x6b3f1cc76318f33d63b204bdd72a009a7e2acc1a81ff6530739c41990de6552d", - "0x5f4e8c17b0f337f546abdc595f953cfd056015e3add1aacc4e058b318277a3cd", - "0xd9917cc1fe0d4b12b85b2713c9942cdb07178e4c6f594baa4fc2617491d0ff07", - "0x2f6df9e5f1b821365dae46222c7f252a13693fc34a9ff96c1038f154496b49a1", - "0x5807dc551b31c69db61bd1619ad0f9631483957bafc20d4ab1a3e6902332fca5", - "0x04b97d025d8a5b1ac619c36ba6dc8185c05b4f1244993f684299f7368ebeae3f", - "0x16c71e2b84a27105527ae46ad63c9e2eb8d2145168c285ae2f7065e5354d768e", - "0xa562d2718f87e2086106f87bbb574bcb1e767cbf9471999018eb3cc21515b004", - "0xc2ff97b7e8736ef6de59a226cf0ce16353eab76b4a8b8ee147995238dff57d73", - "0xa9abdc4a8f70bc74134eed2e64045b24a1e9290ddbdc6d833179fdb4122c2293", - "0xbf0473bfd49593b516300cae47d7e603f9a2ac0767db39e2ee6e028083099490", - "0x8479dc0704190c2e34795dcb6dd447a4aa25672b9e45dd2e39f3864fc9521203", - "0xe03fe87b29737c7e663196fb33c69f02a35caed2e791f2a15a562bd2989d8cd5", - "0xf50fecb879d837e8556ebf2a5ae833649ff8f97118caa2e3b617b59153b65b4b", - "0x0eddf32c7032a26f6a0e0cce24ad9cb266d11500db33b863dc5d3470e3ee0a60", - "0x85161c9ae941dac62edc22409804ff1ae3111d9f903843a7f86b7a138e702da8", - "0xaba0acb42e5acf464de70e965b47872964d7241a57e21fe753535540b8e04a53", - "0x70de7226fe37eec9dfd4c7902761d0ee25a24748d47dd73202f9c10269bcfd18", - "0xe5f9e07e4757f99c0b3042102abbcc59b1472f45b53313626fa49fffb13a318b", - "0xe4e4be40e0d945d84851d54c2a317f0c56cb1a2a78b9efe1c58d996f9f7be4d8", - "0x5f45e651327d2fed7f559b97e56f2c954e82b843c59cd2e550628639f7f36d26", - "0xdb33bd941740ca4d547b52f857ab2fb06a5d144eef147b5dca309b6edfa730cb", - "0xdbedd24c3f6fb0f3d1fb2eefeb658076338a5274f01bf988dc65c7062f211d73", - "0xbe54c3757785c7346e4e94e2f975e97c6a9c542b2a621281b95529b58d3ba7cb", - "0xff9a08a7d3bad55b530fc2ce5b8437ad524b176f151b10366376e8752630a1fc", - "0xb8e1cc41b404d9b605c67ca266bb338f4b94db9d04f71c33b6205a7aad5e581f", - "0xbc06897480daec866c9faf1723f19873448feedff8237f8163ccd796dcc2a4c2", - "0x1681fa8a577b819397aef568125a2f81aeb94d313c5f37c8041a6fc8cd4619ae", - "0x91b95548f18cd07f37ae2b7d53c56a37dba797361f986d1dbf290e169a8f9bcd", - "0x2e0daea275060cf0d7759fea5f50a5d22bc13c2ad85b1776154409f4d4eb30c4", - "0x1999fb0544b4155419e2e84fed331c401f6a2e3314117acc21da81a135bdbb47", - "0xa35676895a52c8b1de46ee4059d57d1fb5efd480828fdb120048294347c3c18f", - "0x8892717f9562e3f1e5dd2f88b45c299ad91e8ab05dcf6be1c0de7a95d0ee7a3b", - "0x91ac521bd8ddbc2efc821a82c6336d01cca79b55c36fc05932ba588d68bb62ef", - "0x76850eae9c3e3984a06d664df14c1e3ab86532b16624e93603fa098df1136ec9", - "0x4379c37011e615085ad33e48b21781b943437e073964033d6bf2dc9fe85fad21", - "0x15cf24d1b72cf5c797531314204207e899820c07f2c611377fa4a8d2b23e9e64", - "0x964b36d4050d4eacf96a2e71bdde62d6908efc0291795432b45b3032f78c5fca", - "0x5e8abbf414ebf24bca3f1db28a81e34ab73491ef8535fd661661ff7064b989c9", - "0xc1499796680c37d46d0844a902f0ef966efc8117c2460e73f89b1d81a3b35f76", - "0x16be9d9569bc82d5245e0d2f5f2efe4a52ef09277d84efdfecf219ce5c7368e4", - "0x05c7f5f0b28e1fca9441830a38e54813e4ea3a73c7c4f7a873781040e4718c95", - "0x0902c45ce41d4e6cbddd66046e9baac2ff2b6fe4b4b5fce0ee91932d57f74edc", - "0x523cb2017561cba6cabdbcbb54ba2f97d6a52a5ada114c533912c457a4fcd02e", - "0x9df4d35c4f05636670123ee3d9a6255c9adddfd7d17cac6ea2b6e8dd33becc09", - "0x05638d9e998b4eff7249981556c9493fbcdc39f78d34f6c120cf87a0b4153316", - "0x451c25c490adda5cecad4a7569450e4be71b08e6541f0e122683ea3544b74375", - "0xa82e2fc3c1d30e7bc9f5a4a83ac3efdd57a7d05617889001c075086dfdb0d206", - "0xb2e0beed5b15c9db6e0d4f31a2111c4dcc193c0dae3ad9fd320d0e2bd4410ca1", - "0x40183f421d938e028836b39156862b8af8f88b207ac17b9f7535df0f6e7c8bcf", - "0x311e6dfe62efa8e3b5ee21ed0abb73d1ec69b357e689f22d04d7010b3e6c9ed0", - "0x7abd56b8ecedeb87fc6ae95d2a108829f8ccb109e05d75ae19ea67bfa73a13e1", - "0x46771d8f8adaea3d100fa756a801c22d277e1ada2256fb7e613730484a856de3", - "0x3463faf96b0c752199950ccdc1ba7b16c07df750fb76c10c8ecb1b155e3ce021", - "0xddd0b47ff5d1fd28ea7bef1247f683c3c2ae6356b8260376b390b0e4770c2c70", - "0xe50cc4bf0b3278ec508e7d9d0f233e942721dc9116c56d65182c5afc04219574", - "0xa567e9287f6e919a716ca159f6f59e0ebc6e27bada9e0312e4e7a248f3a1a195", - "0xfdec56f24cd59d833907cb21a4311c47d4baaea891efdfd4cc1fe48f875ba73d", - "0xda2ecee4f7f28fd5893d7db76b248fd5a5c8ad18002230177438f1209e92449b", - "0x267e150fd803d5dfa94dbd099f8960c52278fe338d1786b1678ba7de6c101625", - "0xf70ae6d7f25e486d08489d70481e7e537eedd8303b71c656e80ec04359dba398", - "0xa02efe6e14630c760bd69af9a6fb3f8391b5e7ba3cfb8bad4958421b3cd067f5", - "0xa5c4ac4a8cf93b8695d2f1b080ea00745b52a656b6f55e4debfed402226f3605", - "0x434c8b7d449c62e779aca1daba7e2c5b7f264dca8879768dc0b9c02c02ff77c1", - "0x67268d79875e46e0abd507e67b75e5cbbf9aa3de463284e96c057ce2f3be893d", - "0xc3d6c58cd4418eea6fa0e0be7de002279c761588135be2dd2dc152c6f580d75d", - "0x52f6b52e67e84c9cf8d56fd1dfa7d8e45e4e4129d33f9a942307929d28ff2859", - "0x8833fecfb8236f00abb49621694b0fd2a59a41f41035d2704a81d05a196fc4ae", - "0xca068fd3b005b08c2f3daef99d7447332a7720880ad94aaec632d54c591063f9", - "0xa3de1f4bb4c142ad685773b15e9a4e97c35267eac119e3c29e0aa36f2b814487", - "0xe8433c0f128f18299bda56175b279a5e271a8af30e3176aa48f6a734352e5b1f", - "0x9727cfeb52499b591e360f88de264235661a21fbe7242ace5d849d90bc506c74", - "0xa0c2265b8ab7baccbf56801e5d06baf4ce6af5879bd7e193f5fbc67a717cf2b4", - "0xb6303d8b21a2d08fb82154142bbd014329841a5564adbd217468597302d4cbb2", - "0xc6571041af75a5bbb7552811ded08fd4d93b8e33e974451771a629593df7d656", - "0xcdbd84961c2639aa0159de12abaef96152026ba3710385ca44e2080b5ef6008b", - "0xec8fab8e1adf154822c799dd35e2627420663b4d1f9439c79d89f360467fc216", - "0x8aaafbfb4414ab5c8ea23ccc43b5abd2349194cdf98792bbff46b7734a886d00", - "0x1e2462355a74d1a705e3c3f28d826a6b229a134c472f8d2541e9d03ac37d8d2f", - "0xd4e33157363845de8fa0564d1145e4a46eb22fc909c6b182e6bd7aba4b380613", - "0x7949178782807772e6f9160a14b54fb0ed503d5c1630932cf17cd9e385b00ee7", - "0x089d7d1b5a0f3ec62db8d1c11a95365688a47bdb14954b72770d5cdb7af3a365", - "0x69fe40a2c3b725eb25be1310ba0b97e8974b17ce9eb233eafc1306a6ccd7a463", - "0xfb4cf8727985a7d97917d3b020ee11dcdb08cded73085363f625a37ed0d8b04f", - "0x5ffa2a923b9a50832644a8bd39f0ee569f9e4ad8f50c4e1d57c9ec96a1c03477", - "0x8360233860f889d96a7e077d221b4ab6d9e3f7f1c5fcb20287ce486e9c244979", - "0xeef9332e4fdb2022128df42a2f311dc5ac080c7273e1a24b50774058b1d9adf7", - "0x89d790c825dd9b3de1635ba3162fc0a3a99467747869b409db97d95f7ff6cf59", - "0x04a088709bb769fcb5b670fa79cd97ec2dee21e62992cb2df693e1f18b3a2d11", - "0x86519becccecba9b91d0af72c176a414589b056dab914f877fe8032ca1b8ef9b", - "0xe61b3d8ddc856aa2ab755d4097ab9040ddf3554a1d8493fc5b93b5fd58aa0fa4", - "0xd8efd5557426e233870b8bcb53e0fc89fe79545d4c2d1aaf13409a7fba84acef", - "0x0b74a2eefcd26ca8322495468afa0c2ed24db0e4cb8ddf30bb84f9d2bd9dc4a4", - "0xa9fbda8887f5ab139d567d9a14c507e574352003b6fed49d3cc3ea49ccb544c0", - "0x078261958231fa17c1b904ec7b80ec7c2a0e49925a2309abc701521b90306b6b", - "0xc4d9c5af76a0b6c55ed8243985779bc77d4ef86adfa63a8ceac44efe908ed012", - "0xb02867e224a85e4a929894919fdd1f3460f909ac276278129d7fbc859f56915d", - "0x7a66267c51322887433994e3a01fdff4600e596a06e1c627c5a50c42e2bf203e", - "0xa66849c9503997ed600daabeb23c1e316d938d7c36561013f41d9a994d819735", - "0x572641c0124606eab803d11c3548ea828cf67b3d238eda5efde6098a210ba6fd", - "0x0a920057716b8b6b968bd5a28478ff99224b687bc6c97e26669fa1dbaf99850e", - "0xe198e1c691d14b3ad0a6b1888f7ae40ca60042ac91ffca0d6df3b4bd12090898", - "0xcf8cceabc0ccdcb4951cc37f625c1e8c120620e42ff3f1ff4a72e4f6dd6139cd", - "0xfb06a9c8215be6da3ade6c298f565f7c3116d12c87da1fc9fa48d17240e3b545", - "0xc967f1f8b116fb2faac0ae0760622a488cecc6498c074ee0f013d985475c914d", - "0x7351f762b23b48612b1369c3a7754ac2414e4d337f94cbee4fc1adc29759eba4", - "0xc3410fbbcda7e7bb868ed399d8950db593b7e79d5fd482a5fdb6e6bf342fe6e8", - "0x68c7626ebe19b8d30af71d3cf3635dd7c7912aed7a47bec3c369b72eedcffd05", - "0x3de93c3bb133dcfdf07ede31fb2933d8acda317e7680e7bd18cc1485f30b9225", - "0x4f2c0cc1f9f2f2c1b84448cd9b033d95f74940f6dde28dff0cffeb2840b6eb67", - "0x4e2934f4981c6a3591ba4048b0f3794a6b92bbad76e94d68ec4a99ff95601d06", - "0x8ae0d36b4e3212e9055cef96398fa706d9df90c2ced2c89c4828758b62154840", - "0xde41a196177d404534cc4b46f6b877ca4dc22a443f6a4680915e43046396e8ef", - "0xcd273ae452ad455f430d5f57d6411454fd2fef1698f9b8c23a4fec298936e47f", - "0x95c9ab467c02ae75c80cf924e81ff6c9f9f9176b828bc3dead827d4707fc5cdb", - "0xf9ad1ac9623d7b8b55dc1b363ac5e6b81fee9cb0bba042ee9802b74f3d6d12ef", - "0xb77f49f9fe7da05e6233e1aafd35cfa8042f8ac8220bb04fba898bf75b485237", - "0x6c7dd1ae1b2c378203b2be05abaff1e6f24d9a16d2fc4fc76bf237e61f5b0951", - "0x50c1046183493f9a0b860effdce20c193a7c7e89cbb509cdbfc839c522c99677", - "0x38a84d66ab731cdc96ae16a7f0dfb8c61c586bd0755cd409f4a785d426ab2e74", - "0xedf3c6c8ea853068d511c74639236e1daafb2caf96964e5b0daa22d354e39658", - "0x3445d411938c39b0168b96f8a4b0e32206a531910bbf52743faf37b3581f3236", - "0x4c418ec9d20b934a3f237b4c13682cc1638ba8e517e86d168e8c990971098f97", - "0xa610b6a8777a0467100a3581087f5bbcc1e7d4b697d9f53d00d9fa6f0d77247c", - "0xdc9e46a16000ec340bbaf70284f6a1528dbec79e4e8c371d4b04847f8c844089", - "0x34e76ef7897de33eb66e4ae37b5534c6240540048c525f858dc832b166593181", - "0x36dda3dbe43f18880aad5a4f25ec29bfa2da4a6c7f964e6418c0cd421bdbea36", - "0x64221de18086e9361ec07e4cede9e0103109d315d97684f26f05e831ee2878fa", - "0xd0fdddc2384a132f50ae0670cac8e62e918aff264277d350faa66a0e95430a2c", - "0x4da406b43c8247743de53b0a395e47204ca3f0d15f2a29d8715e11a71001257d", - "0xd0e67a0d70cd2140f55941d6ca90cd15ca6886ae013ccca40c0afc0bb8bbe016", - "0x70e6b1561209e95921f3800b1c48ee3bf3e532f5571bb2e4a5d82e9b288eaa33", - "0xbbe85ae238d623b1434dc75fb5e5d09a75578d2b85c59b8e69d635befc784248", - "0xc617186c1cc1c807b9626ae1a68d8d4fcd6b42aa153dc586d5ee2192cf9918ef", - "0x018329ceb98202471fcd740a34c7c987c2daa081b0d105b86b5b948c8248ab63", - "0xa1bd431642f0e1dd5f0bf01e610c29e5d695b8f578faeb0f511a7a9a2fa3116e", - "0xc8df41c2aae79331b86e7b007a28e2731f27f50609e9b4924678b8ff24d509e4", - "0x377c56c1c8b9686225cfed2710e4934c17ce890222c7482e7fe7fc77ebccc52e", - "0x0262617467f33b9eb0f3b28f368f5d08483a38dc9423510442eb88537c6d49f6", - "0xcdfd57a4737a718352b19c48aa72c41b2be0a7267ec7abbc3b539c81fb5a4b7a", - "0xace32a66c3c9540cbbd81c26a429487e3ee135677b8a43ee7468c0fc0c584c05", - "0xd4a50046105d5d01c4c928a0ade2d1a82db88ec52dc1f02b7c2daecbe9985841", - "0x350c5fd3734cd35dc6e02080e087858bef8d8dfdee44bd8e2bf75aef795a961f", - "0x5e597461c6e1468b3056a2cd4483e85a8b38dc27195ba60d50ef9952aa640a49", - "0x4f9d19414803bee7dc37e16413e033bb710543d86b38d6133adb1ab4ce90cd0e", - "0x3470068fedcdb6183c16ab34a1743e2414151eeb7a46d71961c1359adff1e214", - "0x66054a35bf4b53cec8302e2c94b8be80f3692fcbee87da4f894769799453771e", - "0x752d55b28bc47a388b098fc5b6b5f60ffe153459ddc1c0da007cafb3be151be8", - "0x3b969e58d42a927a7b214ed99c34ca6a77234703908bbf4ffa17e17e21fb8cc1", - "0x6f0157bcb211940600bb88d08f026fe4af881dd11d8dd1351ca014c88c3db72c", - "0x8592dac47e72c7bc32099c102a582c00543a0d35f2dfbdb9b57068c79d077815", - "0x8c95e048954d689645df63ae447101dceed5e75d154502c61f37990e5a97ddca", - "0x9f4a5e8e60a88088c391040bc01c46538f0cfc032344a882bf5f00cfe1b10d3d", - "0x04817ac392930b9b4d81cd529703b6688717da8101fa8a327fe49c503c0694ea", - "0x446d3e1470d3f965fce24932f5f1824f998c9cbcd9eac1c9ed7fc7a323658bdd", - "0x8603a9e9b6b852768b751175cdd2b23e354777e38408903b42d67a108c1b0999", - "0xd04177d8e00ab233b6336da228ac1b9d683128c66a85f1eaf2d462e7d52c9ac9", - "0xb01c3a41c7864d5f14f7b2f18bd5e4576c2d4d296d78927f75dcc50f488b54ee", - "0x6c7f1c0024685c819230e8accfc6a0e5f967b440d069bfff8c79185f152f940f", - "0xa2106a57c2faaea7c6ed8c7edd48e64ffadca5c03674672da9a972b4d32ffc7c", - "0x2c245fd6206db27c4f95be2fc01b04d24febc0e7781efb69623bc5214e7ddb69", - "0xf4aadfddb0d2d00668dbb5a7aa1cbd4302d0778d3697af4cb2ceed68e5dd1639", - "0xca6ff39151d73d4ff9aafb89ec0843e5d93eceacdd69b1c95cfc745857397c15", - "0xa18081aa619e1ee71c310a810c8b3198d515a0954edd34994fedc974f24bd0a3", - "0xc05f8f372ddc52ad91874e45aa48683c969331a3c472708714c19af1d8e3dc07", - "0x57a2027a86cf6e524d7a4e7b0b920a98ddbcce80f018bf402f7fbe73cb366614", - "0x3fcbb5d3f51987fb786b0ff83f39526e2aaf1791abc5aabf2f11a6ec8ea4bad4", - "0x61dbfda0b2940f034dd239375ecb5c8fa7385547a6ffd4bc5951bca8f5c8bf0e", - "0x662820a806693485407041ced34740b6ed57649bdb2d346aba5cb77108f697bf", - "0xe368491b3e2fd70d2c6f06c6e05a41f2bce9fca28df109882fd1bf3e47c82287", - "0x6cedbe3468d4903c73316b43534912e1b69cba4623791121b309fe37b6ad9b64", - "0x6517d421dbdf6e9041df293e6112572b8229208430277c85a7e52acad8a760fb", - "0x8a1602ba31438ed89e0f922aa165e0740e9cd9caccd82ca7ac79c176fdd0a2e3", - "0x09a07f8791bc59a20a326a727803822d1dd8de5e9281870818469145ee90f114", - "0xf6c3a82062633e2072f5b2c59b78b3f6613663afb2b35546c1240c696aaff8a5", - "0x1b4ce49dc9f835afcb3c34e92caa9a30db92c0fc2cec32c1746cc5eda1caeff0", - "0x2bea42fdae58791ac76d7bc2fbfc08712cb8ecb70b011a271572f047a678293b", - "0x01964b0ad4072d281dd451f41ce9e2d61f990bb104fecf29baf012779652f8e3", - "0x773cf0d41e03244bc688be7660101ba39fbdfa2cc94e6bfb02a1dd291c5c7265", - "0x651f0346eac119d14bba1a1078536dd986024c4dc5cc5953b4d9cad09735f97f", - "0xa4389eb16c42465582dbcbb6af80198f8412091c6ef2ecc7ec84c093f8925306", - "0xed75036dbba0069f415af01cdc723c0901676182960e2e2b51994f0e0c6ce0e2", - "0xac433ea4fa502fe9d126115a386eabeb86df2c895e3908bcd02a9d7d125dcb00", - "0xa65d04561f73a2f6e1f107c3759143c136b12619ab498ff7ad31994e7db5438b", - "0x6d9815c2f081d54013ad118b2c0ff456aa1396eacae1dffef461c2f2ba066527", - "0xab192c650d80aeba5b885a67d07512a6f532fae12ea69a3eb39af7da95c64b8f", - "0xa1d329666928263f1980f72c9a21414c7302fb5e77cd1dd7a91c4dc5d6d748ef", - "0x9e84e47ddab8900337e35ae9287dbd041d0b1d059582f95d375f3231eefd428f", - "0x89c11687a6c5e5a5b45255b4a8ffbe337d8706470c3a39063a960d103ade6af6", - "0x3547ba85cf24a64983968f8587f28bace1a797a93ecc16ce6787263f0de18185", - "0xa98634b1d1c39ec41c7cfa6a4f353c4c276f0618fbb3bd257008b43bb33b262b", - "0xec39ca2cdf7a3798a1530c8bd1424622822f6824415b2964d0467ba313faa940", - "0x8e66c39bc71e16085bba945ac0aed19f3f169d9d2dc7c0ec090583bb896e1a51", - "0xfd7f6e227a8c9025a57c73ff8c82265bc69c9aaa185eebfa924ef608234f1937", - "0xf55d1247afbfc35128a41465800daf2f90c1d57866e4bd37f92bc8278baa95a7", - "0xe82faf145ba98fb5af7e32f5aa90e7b2aafe9e2074c86bcb2aabe17ff35a025b", - "0x62abf9c26e0e5a7c2ca3047c8ae9f27c5215846595b4f41aff72c8a05ce42549", - "0xfc5cd59f7ff8b54d787cc77e29af8f9f78102be33ab4fd561481dbae04426b3d", - "0x69af5e5777a5ee39cc76a049e268845af3e1ecfd0b8de8644197469460880a31", - "0x8c1ea859c343697564a88b170973903e588967d89b63d511f63218bafa4c4aa5", - "0x8f9c38834fee0d5653d4df327232737d342f9b389df61f5cd377164e38a91e6a", - "0xbdaff64768fcdb9210cf8e33fe94a6cb38ba068c2d21d970efe6cf5cfb919eb1", - "0xf64aac8df844e56afa1413e9765a955b6879af44202a43e3f9aa15db421f6643", - "0x06762bddba350befcfddef5148ea1f05729760cafe5f018eb71285b92e30ad96", - "0x536ff0949c7f34762613747c2b7d14f605d6bf0ac328346e34b4fae516771af3", - "0xc30d536522242b0b805546882d5a283d517efc518abbdd674156b6f67dbaecab", - "0x818fcbac4a40b856bed993b9021286fa5f59f0b76b25af731f4123a83b5eb90b", - "0x2e6c3d4de85428210eda505421b253bf3964f5b2403084a1202d940de2d6dfdf", - "0xb31f7199cc69689b24ee2f52c8b50746ba3ecba904c49e2b352f8d6a64f514f5", - "0xf910446802e79809cb62cf6d958d570186ec7a477a16ebf6c0eb45c245c514ae", - "0x36e60616ab618daa724963f56bec00da5cbfac517a41f291da6e6cda890d68ff", - "0x4ae4e0c39ed59901fc74ef1fe52643f1f4341457aab052be71382d9945115b47", - "0xf5038c706b2cdcb2337a88ff34ed3e8e3093fe95d48e0342fb3bc3c0adc2ce51", - "0x021c48a8ce17f06cf41f89033a70b201b73d2fdc5c314bc313e8cff57cda9fd5", - "0x363c94da2187a9e497c77688f70baec16e64c4454db1f0dab37925fbfb9139c8", - "0x2d7c8407dd4ac5ea3212b18e103f10626d8bbc0cd18fe261409043399f026b99", - "0x05c3dea4c6599a259c3297c51c38c13f618ee5372c6b43e1945396bdd7a91229", - "0x7eb49d1da78b683659a33b9346c65bddcf6154043c97438274394e3ba868d0ea", - "0x17c008f4a8fdde5d0dce03adc57310580912599fca0535a42144526b4a630503", - "0x0011edd3b9175a765f1ab25a3123b91ec9ff9f233e8f782a2001ced391da2f05", - "0xcb62dd00e7a67019c782abd7e4bf7e3eef12614b03f90219434bba24e3070097", - "0xee086e5721066d4d8886e61e228facef0f518f8140926067be9f0f55b261558a", - "0x683c738a40a8fb871f3f7ef575a422d9fc60511ef4c84cf0fa9161605c10ef2f", - "0x43a4ef42030692e416d28c72f899fa83b4e3d3066fedc72d8c1ce0c3f7f60dcd", - "0x964a579e0415c2875382d1dc5953a54850494ace6077f7ed247f00eeada0bf1d", - "0x286bca4d5a0ce6fe9c9ecab2110fef1c13f71f8e11c37ab5667e164d2f4cb3e5", - "0xf2faf12c85d82d434a5db7d43d51bc99508bb11db731d7a22136d08f57901639", - "0x645944ae04d68cf3c993f75d75f217c8a78531ea3f17718aea08c849bb705382", - "0x7e4c87b6078ae798210fc39cb080772ad2034dcd9ec0f7a28b2de7d7e15377b0", - "0x4760f5e4af303ec759aea4fc2add0759caa3ae96bff4f727c0654f81d02a763f", - "0xe2f15372e72f6e8852c7289b192e959fb797fed0fb9581afd755e4352ec288e2", - "0x760975db833255fb7f325477153462be406b63ce08e6ed0da0dc48f9dac5375b", - "0x2ea20f22253680c49663ee8df8c74aa2f0703223c5d0dcb3db37b8c3ae7475a7", - "0xe7622392c52ce0d6cd698806142e6e26c9d86c81f1877bfbe9022ef150cae57b", - "0xe973760c42ebed38e37252019fd73f4ef14f2503bd3ffb2d859124b7e25ee557", - "0x7d0c3cb847b2cb7c1a5341e0214e830ef886073983e08498f89617b5833c22e0", - "0x5f5939c73fb684dd39227cdc88af51936e59a9881f8fb63e071366b9df4a6b36", - "0x081d5b53b84e2fe7f8cb04a6a6a045e4c11dc726710b6e9994f426fa9d32ed9b", - "0x436e895e16cc13777cd0b740538a64bb3a1360a7038e6fe8efa3ed3f590d05a3", - "0x55a611a3cc6106a86768053e268658cb541c8ca17a66a4a90a375772f0b5e016", - "0x583da6ef703398a2b781acd935f9916932050fa0cbbe1548b86d40b3b1941fd4", - "0x055531e88f18a0d79546a3bf00cce987c33ee0f97ecb7550b3c301530b279bec", - "0x4408c0007dd2dabe7814546fdc336f3ebc5a1dcc7daa1d8a38a9db819eb86374", - "0xcf6e7dab9c72babdc051fcf35ea89e793aa6fe380830d61870cfb4ebc3e7d919", - "0xdb02f15887419e3f89e1930d620748ca53e7f0c026ea8b60cd6e9ebc62f9da1a", - "0x4d1bb9da4321c7f2962a275272c6c1b1c3da6ca9549c19134dfbd43cccd76359", - "0x6ea8fc2a9138c404993edc83b3f54b0d5d94c85367c8e6884ef5d889ccf1bfa3", - "0x4ff5c77a56a90c20872cc7ab5b868557f7fd5d0103503d4217ea2d0079690ef9", - "0x5edd2bd35b9d24b9d5a3c68025061137c82e04261291c4d958d9e0e9a2763329", - "0xefc287bb7ef397298052013ab79d5a46a3a80a55f60e42c66746c9a25ef2dd15", - "0x986fbdb20fba887f3e4441a5b4b98824e1ab4df9aa43cc1d151eeacc55bbf4b3", - "0xbc70117b2b1204e3868a610bf3d2819095185245be082c96ffd3e0c2e406be51", - "0xaaacb52b835b235c76b02960517159cd1f2fb08681f13c8352a04820a990fbce", - "0x43e8b0fb11a657e4afc022573de30184947153497936d6cd47dfb2ccb61bb110", - "0x63fc1cbad428e16b1e2063db10c85ceb6684680c044adc2aed184054492cb19e", - "0x776674ec78cddc642e69ee9b16f2ee876655ca3efaf6a24a72c99a998d55348d", - "0x09dfc550074184d46ec04de265047ce529bdf2c02f7b44e428742f674c31a0de", - "0x897263eb076edb3071a306922207d17cceba6ad179d0f7fead2912ee42c69774", - "0xbbd230729752cb6d078eb853681e85823d2946095ba318d44b28eca479bf217f", - "0xe731c6c7938b7f2dafa8b5935cff095289b993d5fa4dcbaa715e6817d83fc823", - "0x04fe3b2d9727b05033eb4955f0c6693b0e63de665db904bc62b4dad5ce59a487", - "0x881aec4ca776991226a24f1ba9c736f9e86a051cffd1ed42d563c01a602fd126", - "0x9dd5adf0aba333d3cf13bbd8636dcec128911b675f159d11fadbf75137e1b766", - "0x26fb6742fa3be87da75218a1a138a3ed64cfa939791cbc718c181e226b5ecebb", - "0xad0c9794ce6109aa72c3b0ea6110fe96191ce3efc89bef5a99696ae84380ef3c", - "0xd710b6759276fc3149afc75ede514256c092a9e044555ece9fe27e27364181e8", - "0xeaf3b11fee973c4aae9fd19de92a7b14ecd347275c64b2343a2e4f97d1204785", - "0xcfc68b631ffa430c95ea583a1a1cef0a43e3ca182deb646c399a451ce55aa921", - "0x52057b9108fdf67ce8b7a28596e933ff57369b9ec9f1183b79cb0d0c9f32d651", - "0x1b99aa1987d512c71cfecbe56d6bdb4828ff254d055dfe1b23c472ffa4a6c3ba", - "0x64968b18cf678d322a84d54b2a91e77f44c108e2d722cb1f52708cfb824d227e", - "0x51be4bb756c2a3b74d4552310d49c7edf084014ea2787e349bc32143295380c3", - "0xf98742fa2e646e8add000e8ff892eae0fbee6f61a2a200eb129758285938a881", - "0x0f963737d026fc353f0576ae146a0dc7a8a27f31138b3753101a07f0e833f1f3", - "0xc3af9d91a1e44a3e865f44e75cffd5e9b135b04fe6d423895fb1911ec08573b3", - "0x366072ca53e1b7c312088a2c3504894c2bbbe1f1fc3204e22a22b5b619e09a9b", - "0x46ca1ff3099150438131eb934083404728921e498773b5d91ea59814e8078588", - "0xe2b18d6bba927615a8f9252188c141864cd61cfe570ba65f7290e4ac0739b403", - "0x6844fe922e79f335ff103ec551476f4433d4617bf9f3e25efe5340cff0c9406e", - "0xd433ef7f7b8d1d517815e5f77f95a2f3e5100a08a1ba9e5de78fa57076d1d307", - "0x6a2a72db31e9cad268f13dd07c3c91d2c2c23e0c92c6f1047a6f3d59f447b821", - "0x9f0ceb8da593cf1c70f4eeb08a3f7dfb6f682ac4bc1c175efac43b10e100ac46", - "0x5ac594092618aeb04d6ab2c5b2612f1d4ad57162e62aff2d04decb0ace6658ae", - "0x003792b77b6c078c98e3ebdc29014a2c24c238a79e71ed5e36e27c1e273583af", - "0x15e869175252c89249a7728c33bb8e4c67f90f3c802d80356d25344ea05e9249", - "0xfa67b16cab4a3f1f8c4e8f6b3fe98f5be59e0f13ebc50d32f9d1dcf55fa4c984", - "0x3259027d8095d69e4bdaa0ed9c7d80681f4e40222b1d03347a141a634de40344", - "0x8aa2f2b46d0f85d66ebe39c00fd999b67d576e2fa119fad6783d6af4939fb25b", - "0x0f6073a427daebb861b9e1c45ab46ca9b8410e8f094137b979f6d17662fe4ed1", - "0xc9a4e1be21bb3dbde859c1bbd9ef9b63f8312f95c3d961414338ee8f9c8611d4", - "0x1c24d8de991c7daffe35d65f90c9eaeda59163f481140928c737d1066a95c01a", - "0x94f41fc0f4257b24ed6ee005e62486dff71f6ce8176b9fddede2eaff25d9fe55", - "0x9fda793acc69eb63981e28716bd20e8fa3ac091595929d943c651f73f6559593", - "0xf7eb671c5e563de2698cf69b2976e25a91c2c462eb7583d754211b252b4f8479", - "0xaac71c1761b78cb396167f177572144e68e1051ca3c332e94514041b9c7833e9", - "0xaec9323ccb0314b0407a591c05a0eb83e48052eda78fb5e9116cf32c902a79da", - "0x73a2019eee50ec26354b6f7d47416bc10982ac44ef47ce4720b6e73aed28f869", - "0xa45d192de4836829a913a20b14a9d8c95dadc04edaa07edd2599f2a75f85f5f4", - "0xcd8f6adf06fc2f226bcb713fcde720e205e4bae7a188d5135e3728d752835dea", - "0x59614e66b31bcaee5066f04bffdf318879126e7bbebdefe39b921fdfa5c1ba67", - "0xb27c406a25b24d78aa543a26b8321e43aea21523c7805215ded3d72c9043e0c0", - "0x1485ccd46c327cdae534d95c4b61611213377dbde0728f2ab309a65b69012deb", - "0xfd69566c9456b5af6a515aa031b326011cae67e88708557cefc5f1e67d76cd49", - "0xc7e49938469e6a6d27fdde544dcb48ef3f761d60ba4671163f505de1ca644228", - "0x96cdf2b037a658f75433883813f9edae930769dde761fde991e141fe383d82e0", - "0xc0ba35adef71f25b99450a0bb0a46b450dddc56ecea4c831a53dbea55463c594", - "0x97de7293e71b1779b23c3b51fd85e46d7ac86c55c18385d9c54d3da1c458a387", - "0xa72bf43cb3dc7ae59052542fa2bb2d5b921f49ac14485c1e9bfd1286af59abc3", - "0xbe7d7862602bb9911073bd4a8ec5421901a3a66698ed478102f02df87727fc12", - "0x7d17171e3edbeeb85326d785c2a68d6666526d8912c6aac44bf16fd0c0ef9834", - "0x3f3a36b17fbcc5ad93d99e71ac0bb2acb10b24fbb914773a0991c52287c201ce", - "0x0ddcef7f14d933eb8b28e5877e71595116a98e408b416f01de80be49a8e52957", - "0xccd5bc27319844f5b389b3b15e564ceecb55a91d748f145709025913fbfdaf91", - "0xaf27df2c5e88c00e482e78e44de581fd17edc2ae91e3b7d1017badf00aa8bc51", - "0xa850950e04076c8653eda7cd11761d6a8e9b46a070ece44dcbf5b8e99d9fb0c3", - "0x6c934e50d34130b0ea5d5776ab74fbf4ecf48f8a178115f7e93670599b406653", - "0xc6d3df6d3c0ea6c8573b314e7e0a8065d76d27333c0d6032b97637fc7013aa5e", - "0x4f7cfed4bad1d65ddbb1e28c8d9814450af8d573c0f1880d5647eb008a13a44f", - "0x0a3162cbd7a81248f449393fbd62f65ca948e9134cfc722554f28b2ef7ba4f89", - "0x6cb294c5c312be01110ff0566dd76d4b36e86c76587084faf6ff7f28b717873a", - "0x799574f8dee1769273651e19b56c17d3aa27d3422feee1d18080608b4882e2de", - "0xec0537da4c25bb664270f90fa72c0ede19127f7bfce5724762700715986f5b2e", - "0xb955c2ff50d42cce782c991be3eef70de85a1a382aaa6f7775c456d87a987592", - "0x79f931169917ff38a71b45d85a5f8e068bf45e8ece7a8e5530a0b4eb01efef9a", - "0x8d20869224be1328197ed93a88fd53d0b31ebeff49870bf7851d6e8dab77f730", - "0xe6323bef31280b55572d866f315b97f5be96148a3d640284110a23115a59ae27", - "0x47c847e3a20412135b22a2bed001bd32dcae67a70e5681a5e14a210553eff25d", - "0xb71b250754e11a7c9fe804a33e15a662321d9449fc63b1038a17d58273fab04a", - "0x89128e0b4180512ba5c75216b3030192d9b1574df87f1b926c5c7aecec92a7d3", - "0xbad078b9e9b0328b48dea551a962e437b51902c056c5b0776482712f9db786bf", - "0x4d5aed467fd88e4e026bf006aca0a78717d6b52f311d32449b1104f415bb5515", - "0xe72015b43036bfb62431d15335e719c549c61162f4879c0eaa81421017922d15", - "0xf70009d15065bd1570ef3fe2273da35e894206361590d38a4bd414ede7d69d06", - "0x2a4a2d96e00f06616d77b29a72d7c494cb6c29f50811ea616452ac769910005b", - "0x21f9218ff5737a4e6e6a167f83691dd6445a23732cdf4491ba7e24747fa0391b", - "0x26461e3689a21987bb38091f0fdc4f371002f4a84eb3b618e8c7ffdd6b3f1a31", - "0xd683d8a1f060a5e9cd282d641dd852ae20b7b1c0f9ba785d5ea15b234ace728e", - "0x6b4c803a6643293b9160d82212e5b3405b69a4115dde4de0258a24832ac658bc", - "0xe9a00afe1af35d7811d82e2312d02c5daace99f050e42388e76e5c1b745d8779", - "0xe756e9edf9bb83f450ba9791a245adcb89ee4fe56770d1894086ed6be3d089aa", - "0x729d6bfa3c1d41089d53e08164418779c9abf94d8276a8a8e7f1e4ff492183bd", - "0xd582a6b03425d0ab29d1405232e088822467053d470a99c3127c46ecc02dc93f", - "0x69ac67b95b725e5102ac7ccb59b46e4a4457e368b90f80624d84a37104ab166e", - "0x8de7f79cb5eb8dc096e9307968dd1b46acf940994dc1fc27156c5168e1ae5364", - "0xaf5fa4455d5b4ce635f9394f1ad4840e4686d80dd52696fc392c3eb89b7a536d", - "0xcfd7922b5650cab0a29e0468cf1e816f741b8b7af1768308f78ec0ac800870fd", - "0x5e14d14657d534de1846182929bcbe7c086d0bb9c418019193406ddaf34a7e5f", - "0xd727f88dbb4a62310cbdbdcbbe40023937a1f681b7d8f6186665129b9cbd32d1", - "0x5739e5a0f02a3239eb0f2b1242c7e8e0042bf7bede814cfdc6775e172d34bcde", - "0xd426e8bf6dc9171c6850a492b883bd4c92468b861f0c539ca890e444f854f44f", - "0x8f9b7d20a62ec75492dba773b54ed3416ff18adf070be3b3a35a803da5aba931", - "0x8af025719c903aa861de42fe4d082321f600c17e4b3a5f342ca408787957922d", - "0x43133e4fac22d85951ef650fada9a746b464a0659f370ee40ac6286d5ac3968d", - "0xd95ef78d2685255b58a7a0a581d953837b1d891f8764d2842c76137bb8f92628", - "0x5fc07f1fe21167cb65405edef11d30206ed7907d396252ed460365c9d6ea89fd", - "0x3b86a46c42d2ce31c39ac0bd352ac1feac8705e825c17b06d0f9db4f15f0b793", - "0x3672191b6d718f4ffe5e17039c25be00b258336cdf959abcb1615d7e77fbf415", - "0x017faf0c950fd86809e7a0acf0d7dea5b13d41b21c60d4844ce57459af471963", - "0x5236905646defeaf3666262c1781392fbafc496693b0f26167e14622ffd5e224", - "0xac45332d1c62566039b653fe67725544d531d0d0d385cb31d18d461e36ff3c4b", - "0x2ddde66714e4fefcf8d1130e2847d92bc758b39fe7a2b87dcecf9b3d0d1bc5f6", - "0xe8c6c341b96f9771c5897a0423dec64b2d3f57aa5923b0f694988302c99c1864", - "0xd31b332e66e44f8804ab8d224d179b3dc20f718dcc6b254273f2539311ae9038", - "0x2776a9d5ded89c9a8bb243b41bda3760dc173c41f16bc477a0c6e2de420f7aea", - "0x19c9b95e82cd6277e16bdbb383593459eae5584e8b746de7262b447f313f1052", - "0x6d602a973ee15804f76848823844da1ded9e2fe49b689e4e4c62948bbfb699ed", - "0xf8feaafdb855524369a0d0f011cee5f545b7e2dfc210148b13f84e0cfc5a7a49", - "0xe242c86f0d4421c505b4722b38c13e73c760d0d82bfb77fe6677a738e3f19679", - "0xe1d5352fdf59b76318738f0932c4960a590e46a0073694d9518a6e8a779206e6", - "0x7db333e369dc65cd5f8718ef0cbf38def00cd60bcc47c1ce2034203c6b2f8b38", - "0x1a57adefa64ef6f07306db76e9425624d106e73bc554822cb99b4697bb317f02", - "0x4a6acaff19b541b0b2fd43d9165f179fbefd353f8fda107d53926e71ecf68a8b", - "0xe5e886e9b31a3378ba585b16dc81f0d5cad83f4fc5aae85945e9a8898dd038c7", - "0x4d59ae8cb31c36c39c2aec16ff682862c097aadc499da6de1ffa72a6ed87b4d2", - "0xb92d833f46038c267f09c500c2ae557810709d7db9bd210ad4df55c65bc0826d", - "0xf1b971e2530edc4703ceb21b58a750a37399c05bec8a02409895af98df7e5e6d", - "0xbf1508b24e5d01809d39c110816f3ad5d703ebaeca4f218b24925dd9577fad40", - "0x2e25a66f7a580845ecf04e66fdb0ddc0e17237f6812f5f2a1640c3511c9f9db4", - "0x5e4f36c5ec099fbc35ca767f548304f8631700326b3a4497f8977d72c3727f13", - "0xaba60fa3aa0661858a1143d01d9408834c2be440cf06d72a93e4df1df6617853", - "0x42d149eec229a03ad942d1745ec4742f9f39a03534df077977f3f4691c939eb3", - "0x6b276de3cd30f0d7659292ba6e8bea190cc24badf88c10bf269fb4d6927b16e0", - "0xd978546ce32bc1123773b6562ae0a7a55b7d82c72f2aef2a56394c9940e7cd12", - "0x297a80b1aad7e95f57ba4f31c4835728a48f7af0e9adfa10d40fae8bd18cce12", - "0xef0d03d9db22500fae80ddba96bed81bf4769e645b25dd717c5ab0748e3da691", - "0x1a094a2ca17fad3f0e2703cd172bcdf4a0ce28e01840655a0bd19cd398153601", - "0x78f08815cffd7e21af3392d2ba9ce33335d8f25758631f9eb6352b826723d093", - "0x827646562aaa1ff4a12d97e59da6aa7c9166418dd2ba204e448bb12b321b7be7", - "0x5c0e92edb551cc6d182b35b9408d2538aa982b105003606575b5514c8e40729f", - "0x416dc98309f2e650eaf9a122508c163cc9e3c9d719812e0a6388b84fd48016ce", - "0x4f8382c1eee160e6877d95ce97b3a53cf7607c59483176cf55b40b47d198efcc", - "0x8acbe66fee2f75c73255b39489f15e6eaee75585426406d96ce786bf53076b6e", - "0x74ab3f7057275e0769c3356bb7515f108c69b42f4501a9d3873433e22639a240", - "0xe0bb35e880ee793203ad5ea844081b6fa99eeed85f570b6bfbbc0efbdd7f79ce", - "0x5761b5e7f8872c8cc13278f3c2ff836c325f4179b89897d0c5b6846752c312dd", - "0xeda698bc3ec12d8004406a7c7c9e3a16fa085a8448901352ce3713efdac58930", - "0x22138b1713dd83a25b1434813492910fa8e54d8feb9eebe1aa5d2bcb6ec2c69f", - "0x4ce3cfc5b1fe6f87e9830840e85b81ff6117ab62300953524c39bfdbf7868980", - "0x6a6c7372b5c90808a6a966c0b90419b5efc739e5ad5ca1fd0d6c9583ac37f240", - "0x257fb60e826d4a92c030a645375fb46b3790c5b318b56b1a299e09e0e7ee3911", - "0x5f577cc4d21c818bd18e427e6d9ebd8f207fc71ad5f883d0e24eb9f856e3e10a", - "0x0f387595af36d3d5df451ff3de879016c6cf575fc1f02d40d547ee91f6aa36c5", - "0xd8ae5a19d1327025446b5e16d0585740839bf32d9c5798bd39e3e0697113efac", - "0x10e80ae2881a780061bac8bae4514f0df0077152639aa0da1b9d8b3ae234e184", - "0x377f9a9fc5a6fdc69cc76f6f36282cedbe9c83bc43dc345fb49db28ef6b37cf9", - "0x2d52e3af5f7ca108b3ac7cfc495b96e05e2d7f82f7f83ee3234c84b77a1874ab", - "0xaad9190dc0748ae3f78e9e5a3b4ae10eb6bb0b2617cc2d16ad3806bdd994688c", - "0x83b863f21344a6a576f805ed480eb72d3ae3dd06a2979b539a43f2c955429a24", - "0xd4d0622b957b87f71610bf3af65ab018360d320ebf328222df10899830ca33a0", - "0x7ca92b8bf0337d0bc56aab4c1a57e29addf6f0a4b9070482f06a7ee89ccbde84", - "0xcb9c71abfdb931e30a67e7b77d247be70b005076b64fb138be12f260f9c73334", - "0xfe44663a9c2067fe4bb8b43d241438cc63af9638cc1859b5d4c7c7f89318e558", - "0xc962b737dd9438cff5272e16038ef0ca55c4e1b5497d75ef5a0aa53ac6c09448", - "0x2b3a80ef8b20644facd31b5d1618814507c8f603dd8079ba40da09b550f3a2ad", - "0x4664f49d6f114ae491a0364d007724cffe144f4085d91f24f8d3fe87f5b8e393", - "0x1e2456e2e055decfa092a838d2052b12527c6e56b43a787518b121b61f7e316b", - "0x2f45b1ef6d6ae7196818f81278f226f3e0fd535d3a27b63497a6fd208f195c0b", - "0xaf265121e6406f84f7711447d3118c02b446c0d6db9c4ef266208adaa1acc0ec", - "0xbdf275c92443f0755251473e89ff46d741681c7eb2e7811eae25a0f1853c0bc3", - "0x5adf59b0a9cc02fb5bb5c469a52582efec5c317ee1896d8df49d15c18db944d5", - "0xd9aec0aa26bdd240b709a688a0b0565f3543a9b3686b78efeaf070140f2eaef2", - "0x1f4890ecb2250b7ca57a092a0a40b0e4b0abeceaac015189e0f6e9486ce4bc7b", - "0xb0be545c813daf899627b982dae9edd9de72ae54094dd8eb25570423e5c5dbf6", - "0x0f98f4ac26d2966ff4b129950dfcb287098fef2e60c3bfeb1156425fab8db92b", - "0x5622210376d1016d0b7615552cf3d66cbbc2d39d3eeaa2bb100cc2328a81cd95", - "0xe3523e80affdd0ccc3090ce202e7aa408ebaea9fcaa1e705242b948eb15f3cca", - "0x98154cd06a09aa5f9d3e4b4c824f1efdf6eb5ad4b5c0deb1a16a8372bd073a94", - "0x6680dde58fd0b4ee4626a8564f827c8c2cb9c7d0ceb4437412f30a15ad486689", - "0x61f7582008e42c3bedf33342c9c47003a6ce9264c977204ab5ef778f6e6d234b", - "0xa8ffa5ea4717d9f8d55db7d5626799aa102f1e340d80c68896a14204ac38c8f2", - "0xb83aed2db0ce9eb79c45259f66d72cd00546caf8e39e5efe91bd03ec64cf96ee", - "0xae616f4c1416b46a21390e0bd5336935490b9894eaa7c653fd9909086a1df5be", - "0x18f84c5cbad9477e0571574a0bed0714d3fa587ca21e39839a95752e6e3f0299", - "0xdd5d5c64b54437f649d7004d54863160da8db8518ccc99ddaf6942fb551dd798", - "0x72b7fd1a0758b72010c216bc7394a19c1a0cb6620386a8a91c21d3db69004b49", - "0x2dafd23aeae504b4ebd17ed518648939cd63f5e0e7448bd8c0fcc78dceec4059", - "0x787827158ba2f53c21c6d05eeca56fd0fc2fc08026bf09c76cb17583340944e4", - "0xb1a4b60e4d00c8189d940ed2fc070c1238d03d58d572ddd029ae794e47352df5", - "0x33e1420671ccba2d938f5b75d45bd31d90c5b424d9a02e7e7eb4ca60ae402443", - "0x094b7129d1087747705a683bbbe61b13c546decbed3953c7867aef485865c7df", - "0xde05a46ee5e26ff79801bf32d1f4bb4dab2127ea60f7bb037dfafbc16678e1d0", - "0xc81b90e47d6aae3641ddbfade2f8146324456ef372da470b319528114131743b", - "0x2b835f2a36503a9ef3591f1913924ab134d3fba8c95153f1cc80d503c74a1af1", - "0xfe99568c1fc3808ad05d99fe2261b27308c97bb39a965f96119e012ca8ae63e1", - "0x2bc9ac9d5a461286cbfbdc7d6181c795321ea0779835322b5e09381db7169be6", - "0x6cf2449053f134a8ae48a4f0007ac1f8f39d30d9c3269155b8922581a64b5d43", - "0x7ff755adbbfee435992589e1eb09e92532624a9888375497ab3f46d921aa3aad", - "0xe708615d47cd53d7856688d2ce264311f7f94631f318588726b88ef29dafd93b", - "0x96e1a15735e93923060b3954925e8d52b0a4b5cf6516190efb907bdfe6bc34ec", - "0xd33e1c73fa37307c1199ee2dcdf3a0e92152f3f6f9f667ebafa190837a77cc66", - "0x57fa01905f12ba8decb5f5beda857a607e32e9781d39a46001679f537d57391b", - "0x2e5f2df2fb0d327f4b574c0a37c243b7fb4210af7a0796cc2c880252ad8438cc", - "0x26714de3dfeb00f0d8c27bc6729b89d3b55b82cc1eed2999f742c89fab424fb5", - "0x7bd11bfbafe1e87b58428db1f819f4ff2455a4f90db5e60e3d84b28d818a1173", - "0xfcf5fc7965f836dd04e54a9ce577aad76d10464b678259ef1c8bc2a0a0c67723", - "0xdb2e6587cea3bcc6f109e350d6c138cf919229e884a2597e4d6e599c26625736", - "0xd23ff7e12ed5f3177dfdbb51edba35d879fda9fc4ecb2c6f89bbd6addb772697", - "0x0a64a15947b2f5a87070fc3b8171485aac3e131619fb8c88b6bb2cdc1969a5f2", - "0x17d617aa1c6ccbd70ead8237a312152f42926333e8d9165266c497ee9a18da61", - "0x74082588643fb771a733c66b4ddbefcb50daf6742323e05ef4a2604c5d56eb83", - "0xa2cbc98dde5cc72ef8b0d222037f01d308a550ede1f3097c4bf7fa3e36914258", - "0xb7e7d0fc915050bcb17f685f798292ce93d3776096c2b3a252f0fafb4c799230", - "0x6f4f736ebd9941ebb79da6456d8ad423a3711f26d3cb643b81c8e02fc5689af1", - "0x21a21b15bf03238f62f610971eaa96dc5dd41ad45a6be2867afe6206f7ddaa20", - "0x459ef1b2c13a6bf1ad48e62b2020851a4505bcb88923bf0d5ec2408c309b973a", - "0xccb6429f43f6447cb714d4bed7e003239ff6926804185c6c51aabab4b355939f", - "0x5e580a7635cbfb695c97fd68a118ea37680c7408f1504b723189470826016f8d", - "0xc523d6c489334472d1af61a7ba1732195edc25bbf68eb821d1acb14ed5c98663", - "0x31a01c69fa6a86680831350cb67fd5fa06fe8accbd5cb6e2ce0ec689dbb23114", - "0x4fd4d85570c94d03c49e9aa60cd6bd6e684bc77d3b04eb5fc6b2b7f206ba9bc1", - "0x29b8a2ddb612c8c135eeb9f3d8791b603e7bf4d860c0d0b6bab53deb22b2070c", - "0xb156a9272bc83b3b85b93ae2c43f7accaceec85e1d77ddb1343b18356e00d572", - "0xcb08ef4355bb9e2ecc478bb4e80425acb497650f20509e10e134f3f60b216866", - "0xe2f0614ef7265b21deeda6d174341d59b7d13655ee7d473e5c4636dcf788d674", - "0x0727823720192b8a7a66ff04cf1913a31f0dd5b71dc145840533c30e0b7eed06", - "0x4b321b6181092414a3816b076bc86551fb8763ddb401a6923daa9c54c788ec5b", - "0x3831bf3c54c1aad7f557b8c914dc6e3d4c106169175d1e988eb028d160d49473", - "0x95d94729d493223bbd8649cb5117f14c279fac44fe40d1de1e82b7bd1d9261ea", - "0xe2632a472234dc31a473885d7a72e84c24f7883069afed9dbcb643cc5c32af55", - "0xc9bfa80366e9c466df9cda1b1c97de5f627a0142552e97f0554e597b8bc6563f", - "0x345a832166ef2f750ff143a57fadcb439233cb8292124d20bdcc294fafc5cb92", - "0xf8e032c077431b49212307adb00743b054e996e6d3dd101a40959cf19b9ec796", - "0x2243b4956a00a8d2d09275010330f4268d2f536cfa14741313231e6a7b33f445", - "0x12e0f3ff844f21b058d0e311e266cae6d0ab5dce8952d6133a95fb8659e337a7", - "0xf6d80062e8efaef3021b7a747220e713003c37ec733ff8cd15ace6b58ce60fa7", - "0x58c2f923852b77377eb36cb9c26f1b73be98a55c9b0dc65982098e5580ac8e26", - "0xb517d1be70a5d39a4da4ce46821d678257836e6f70621f529c4019d974eaaedd", - "0xa25aa7b0b4c5a3a0f063d4d2493d8b9ab46afc091161df9b1840f47d42334433", - "0xe1795c2a1fc25ea19e6599b070aa9209f2e89a10f0524d8aa78e921c12251514", - "0xdbec5c0577039e79efcad6ba30e0693127b50dd3b108281cfbb4adee3f212ca1", - "0x2c4835b03837ea2310484879212f94fdac6bd2dafbee292ed67f55851053f969", - "0xe0d9e5d1f86eb15c09f41181c96d64fa1ce3a9c69b3ceadad476353e0de1423c", - "0x2a0cff721302a055d3aefdfce14020b3f2418110084c0b1ac1c30a07e3ce988c", - "0xc8ab63d730c578afbb7611167850159fed6d1e73fc3c0b406db19934073ac0ca", - "0x3855c134a5a94513c46470411794ed81d83b29a8da353709035f6e224a9da80e", - "0x6b54eb434f5cb21a1e01b9f067647b857d9b236044220dcfadca3617ef2b568a", - "0x710ae824655688ac255d8046d8fd8438b110d768bdf9bcf4debc9381e02e873c", - "0xde3196ef84179c193b82b1175a417820a9403bdc06c4bfbb5010c8d83172edbd", - "0x5f4e1710e4896439b22f34cf4485b27edf1c35a2b49328eb8f018cb690d4ab04", - "0x37aaadc6f1e57a61a8fd925a6b583105eac77105e167735fc117b347ca58ef47", - "0xc02093ebafb3fc424cef40f5d8699681a4ef9fcad5c8b52002bd911f23812a58", - "0x34d274e1699647b5883100c125da1fce744238bdfe4e1ca2a694a5efb03a44eb", - "0xc14618c6491fe32dcc5848c2ae2a1aa26832343a1ca21bf327ae2a0ad182c85d", - "0x434b07e53641dd82eec95f4b472c1d1d9855ef8492ccd53137ecc82b2bfa5845", - "0xef6763df32b5ab25d8371067949efbdb7f2b9924685918ce3d8068766d6e729d", - "0xe534aacf1e9b44d4464b82ee17ed9cde79ca3a574c5869ff5b6f44d292d1a926", - "0x45ee62bf59e4bfcc76208dce86cc8234727de291de27a69a72bf15ce78488a1d", - "0xb170601af6545cbbab9d707a4623a07b9cb69471f994b769bb534338d3b556d4", - "0x73c8e569840ff39b4dbb24a7adc678e1e7b63621aa77a5533ca968e260d8b638", - "0xad8002631bf3e364dd09f094028a9f4faa5c67ad311838cf65215d6a17d58a04", - "0xee81a28db1d369d17ae97346196672e6f00fd622865cd618ad8447acab9bea7c", - "0xa8caab152258db32f3f03f44ce090e3759538b33d5e557f4a9a4169a0d436754", - "0xfc037393a2fc3a24a567923bca89a7714c72c742c1c466dc0d47296eb6d81b2c", - "0x1ff9e9455c7aa193842f453554c89ab25dd9033b0f7dce5c795e19b1c84b44f8", - "0x894d063e6911b107a6a42a542d99bca6f77192a0852029c0b5cd31a47c8bd2fa", - "0x6df6ca69e3a40334a4c081d74474e00c98ad6793f5cc58b5520be41eff4b17cb", - "0x688504ac6a0ae42368f496f0651e4c1f8ce13fa707a6b98eb52c1f264eac14b3", - "0x6f200669f73cfce5f2f0d9318673ed75c38371476e94ec96dbfcc683aa583816", - "0xff4a6e25786931e08a89007848dba7f62f84a776f7d926bd6983c2d9dd3a14a6", - "0x496c57858748bccddfeee4c5e496119f29a627a659349a4f8f65d1016e92d841", - "0xa5fefc6e3ea89e87875efe37fa9d796f678c6abfdd651897184b52d966fbdbc1", - "0x08e6f4c847dd3aff0e6e45c63939869cf7b007f712ffd79a16313eebf77eb4b1", - "0x00351e602b0508184ea47bad9e47b77071640422296e6574e1ab52cc02552a59", - "0xe1980e52f5fd5b81c96c6c9f1e6baf14bac191388d7e29e1191110fe68d6c88f", - "0x2ba58560d35e709c358a6caa9347c51961642756f4cd28d09b8aeaa532f7c382", - "0x3b8c4425cf002597f470d30c40ca0e40f019d72e33ca6ac54a5c80509bb4e45e", - "0x25da4aab586d49e195e1f9e0d8371d3368e88e3801ab9db750a40df31b2556ac", - "0x9b2693658ec603d976ec25a76c8ffb492d0647581da186a5b7b49bc6c18eafb8", - "0xd27630e4ef91c7144b5db0de4fd83fa53aafc8a463991819a919916fccb66aa5", - "0xd296657df9243ccfa00571774c0bfc0b85a64ceba954b876cb40713aca41d24f", - "0x9cd59fb8b4582d88a9af204976da07e57e066d48bc15b7c707c17dd11536f735", - "0xd6789d7ca6f21a983e7ff5baddd5c7743bf9a76419136384662a685060f16eb0", - "0x38e752a76cf82b63556556042e8cd6e4304d08338aea8746c55ee5864eae341c", - "0x56efa0c6d7d82b0d6b9ef82ea4b8d792d2786ac54b001d77e8493129accec545", - "0x9bb4717f3ab9f8d8a8fa059ef6d09716dea547ad0a1d99eb41d5ef3d21a46d78", - "0x2a616d50d6c4199351074e4bfb6701c3a8a6a65a235a7de581fcf40c6b50eff5", - "0x5a7f0e8b42847b84813d9129819636a60d7f50271f4c1dfac09b7bac0e00be54", - "0xd046e59d18b22e4ad6b9c83a41599afcd2d89d6bfa59f95dae874e8491b1469f", - "0x851c439d105b3622e675f986d249065d7936a7013a05d920bef87e44bc2bb08e", - "0x8e8c4e96b687a26e22a340465a8334fb8594544e7cd652f0e667ae5849cc082b", - "0x355ef21e4e57693374780b2a871b20f361f472074e938e9f33e7b4ebbfd9f447", - "0xbc97e95990cd4a23b97759f2a5ccc45359118ba88dd99895b2e5a3736753b756", - "0xf01b5e69d8d5871473833acaca6d2a1adaea66b76aec97e258bf72f50bcdec41", - "0x3eef80b3124ee89c5fda3a87aeb870dc5fbc781df98e7b0fc1a868acc3b01259", - "0x218a98c02e2b81ccef1bfc460703e57eb8ee5696553cab7b8ce9c95da70ea058", - "0xed6a555d0a858e362e15791f49cca517b74f0f764d8b032c0278becacb694739", - "0x23d9e9207f88148f7648c0955bcccae4138b029ed5b4bf2d574987a568d8283a", - "0x28af6fc0dcb7f57f8541a8b2bb3e781a0ff4d9020cdf26a8119a93401d45eab7", - "0x01989d5a75479a5a6eeb10430746b76a639a9716b8557b7864a1b7b778d265bf", - "0x475611d8792a80560752a9573f864f9ef3153a4c30bb16f7bf1bccb69241a8a4", - "0x0e52e291214aa6300ab1d875db31fbe6274d9769cf7dabef852b5d847e3c6a2b", - "0x04ea3fdf2d26c297c4276344bfc70b4a7645bb22382127f8ce9a621967a54ae4", - "0x64694ef5067706b01581730944a112fedcca32ce9e9103119d9ea4b4b75efd23", - "0xf37ab55394c6233443e1341d03ff07009f1b0771d12b202a78e2789a97e83c7f", - "0x4d723e3c6d46625adfaeccdd1d8bc3811539a7b658050ea0bc9dec625f180df3", - "0xf44e4c0d2e30b8bf0aad255325dba7e2093bb518b56e9c89625f67d5c139a65d", - "0xcd9bdda98c027168fcba6eed7c551291b59613aac11e6e02a00989c93a13225c", - "0x5fae33477d301fda6e6e0e29bb6ddf39c1720aacdf33a4b854806f8a09cf5e6b", - "0x464ae4d9549ea34f25c28ce8daae16fc129adc7ecf71527cfe6df6e1fed72234", - "0x01079ed69a40bd07152e241433be91d61bc1338d83a686d619fa1a21e8c57727", - "0x1f36d242a382eadc149704d8d525e4ac25e0fd2741876022e8bb474d3c726ce2", - "0xe54d3b597d9e37ff213472332bf31a0b16ffc809540c593b1478666019205dd9", - "0x24fd9f8ef011c060d698711a163444b20dde695a1b0db81dc3dc171ad227619a", - "0x01fe14f246bcec9826c6b5dd668b256baba5939749ed90932a603b1cab47fdc8", - "0x6e4f521a4824ae2306c046f5545c2f7ce911104d9ac1204095b8951ef5d63cb5", - "0xd8f0f8bfa160374ebfc90fa7966df4391c1aecea802e3b04a03c087080f01fb0", - "0xb4720294d8b145f29af00f220e076b271b587e9af98653efa47e2d47bbe6d95e", - "0x93223951fd8a31a3e6c98fa17cd33086e8155779930440a7c2fd25eca31f682e", - "0x752fb11e15acc591e78509137beb18cbf177877e4a9846bd33acb1a6426d1999", - "0x3304e2f07aafd6b10cdce727c41ed85ce0fb80e59fee6b96b9a2e98f73280979", - "0xa756425b9dedd5e6c2197ab4a76c14e76b250d7ce4e2b8ada00fc61e9f62dae7", - "0xdcc034de60f855c63dd2c5a9cd4930a45a54e416558c6945cf8e058d55886d32", - "0x77ab21e42cd147f95466d22f5e95e965e38ff22938eaa6b5bca2810d6a9e2e4a", - "0x65e59fd9132b911d65da3c1094a6b8523ed5d9b7eb8822b8d2273b55ce4aac61", - "0xff6aba7d3fd54e3948829cb59ae47084eb809481665c5c09300b834567ee4b66", - "0x0a2b95075de7b0579ccc23f174b3fdf6a4ce6aa9a42f1ace679953fba0956d41", - "0xcc6f0e601cfdbb0631c13df4decb454d5cbd554363694faeaccd4edf16a9ec26", - "0xf2937d3b0c62b8f47cbac34a29d0a7fe92ddda227f984ab360521d5168274583", - "0x9f0e54b594397e29868adafea8686c5e3536fe8ea767274e277d77a5ef2917c5", - "0xfb8ec9c26eae16db5dcd2f05476d89416d2c56a841157685dbd1653274ded2e7", - "0x07d71ac9ba4b3f0a616205d182155d7a34b2463c2db95a47b8d03a45382b8e18", - "0x6e5d854c2848a36b6feef20c80a87a88a40af50888e787652ca54601a7d37d90", - "0xf9bb21fa1e3d6af9962a9946d7fa4430708193335cf32766ed56aca50fc368b9", - "0x7750970996ed6e687ca27f08481be0fd426318aad87cdba015ce129dc896b41b", - "0x4ff8c843185d1b5b4291788c869ba3723969d2dde2d74a9b8fe27ba71da67ae5", - "0xd5730d698ac2ab71bc360f24bcd7a71b8001ddc86205d7274e636bda83d8e3a3", - "0xde41dece62466d3ca8700150723f0782fd624162030086be716485e12d5a93bc", - "0x7a709bc4ed84e8bf45818e0e918115d686504fa0dce6a3bf8513d0bd488dd39e", - "0x620fe2d0affe0c46bbd0b74ab6d7c3bc6b4950915cb83742c832c6117ea42347", - "0x542ee63637f2115e11a3e132bb4246d809232772e08b23401eb8a8f0067a4ed9", - "0xa284a3dfebff214c436ad67d8a36162a1810bd7a51387ab68188267a1e166d2b", - "0x0ea68982e12031ad38aff543ca7d623ca8945e649527eef2afca28993de42ebc", - "0x1249a7d56112d4146105a1f257bfd399f18aaaa94ed5198df8634c89c807d4c3", - "0xa691469bf6b8f7caa8559865c0c99f77350f41e0de9851dc887d850be5809cf6", - "0x34ee8cca77c5b32a79cc0a6811cc73910cd0353fb860171443f08d17e3f18174", - "0x73a7b7f8bb18df6038c0df35ee55b0fcb4735c7e0977efc98008c710530216ba", - "0x0a8d0b414621d022fbaf4a1c0a07169e879a857fb105637cd8193633ce755f11", - "0x36f21a7a7d142d026e9222b884b583387f10ba692b1cc9232a32ab401affd684", - "0xb5b69e29e0558368a8abc4ff3ca022d158fb11ac6f2138cbe0be84b387e1ab7c", - "0xa57d1845ab38ef86763a374b0323b7ff84722941b06dde3c5cec61de40c9afca", - "0xe8063bccc4fec0a51a6d57ff999a7169e15d93c0d7cc3ed57c065cf8afa912f9", - "0xe9b89463cfd719b357f878a927bbc72ca3d06f8b88f9a5c8568ef2afd3118ed0", - "0x4f2ae01579376ef5a2ac6e849cb2d3befdc4e5e814a5b8de31941183697e51dc", - "0x6356b87dcc8f1c6c83a2601be987b44f77180da8b8d8186e6c61eec301ffab06", - "0x4f2918a79394961e5802b8aed07d89b3b5b459a3d9706f0749ec65f3b3d9ebc9", - "0x145016d42f0052f68aae089538ffa956cfb2c5a0f26763fdaa940e427bea7ff8", - "0x897e9dd36d2c12e8941763ac6090321db8cf73de4319c99f5e4343d5eacc856f", - "0x7c1cafd66922a53506f93d8aeb80dfacc9da69e6b73cd3e4a9779cfee5e4f64b", - "0xbd76e3eb8ceeb1f47ed865351b54cf491173ef562a51f068966b71cd294766ee", - "0x600135fd75adae0801296f92e1a7219287ec588fb4627d18095887da1c71ee2d", - "0x1bbd55de061070b1060b4184583e9b5b2e307197235c986dc1a3b942f64cb107", - "0x07e182e958faeacdecdc43de50c8d4226c6f697d7dd4e2ca9754687605719068", - "0x3591aa6b10db91679824f182cb58ecbce7210444314eb4b4e657cbe6518a55a6", - "0x8948a1a48823911e4e16b54b3cf789c8b04a3c26008f97f3c71577a22c23cb02", - "0xd37594cbc0353af27e1e70e249db20f3f16bbdcdbfee1ded9bbbb178c3454d3d", - "0x639159845a1397fe73d7ab041d7d9a74b99ad8009e3d63d37f329df35afcec0d", - "0xe41e444d9f8b0daec348208914d665db316136f42614013a35e3c7e97e219dc7", - "0x2ed6880fd4b6631ee593d3161dddf19021a1365650009d01e264243e032f4f6e", - "0xa6e5eb641f39345184365aad2c9258d21ef4b7c65ee5dba93825aba5e8e481c4", - "0xe2880fe0d6e512ae4e0e78a9249f6319407ec2def2c5745292b750d16cc41786", - "0x54729b299e086a2ce795d339e5e20cf87afdfeaab0dc6fbbd0ce5f55194c319d", - "0xd3d3dc4b51e0d0487c9a09281e1115f593bd24cd09a113c0f1a89468ca1ea2b5", - "0xc86f8dc093f2adaf17fbeda345eb2f4ace3df6d5bde0c99e85200eae84cb892c", - "0x84ba70a7754f31ae366693375c5daf4fa38f7f086791c1aded55198adfb91392", - "0xc7ccc21a8a1b96591c32e98d2c7ad358279c2c1bf6bf51dadb18cb9504a1d751", - "0xdce9df5d5bc2852e8fa61e254a9aff0f03628659df28f51dca37a5a6816bc479", - "0x6d0a555491a6fb3f592fad8b97754d02bc6166257e5a83131fe9d859e05d1531", - "0xdf805b41cf7c7b9346ed152953e6334cca6725a6428e81b9fa4227329d231ce1", - "0xf94a98ffcfd4e28d863e790d1d6f507445f9e64165b3937b0c40af11789d5296", - "0x56723b13f5112f637e1e84c18bc3dafcdbc40508bb6a88c68f3a46e56f5783d6", - "0xd550a9d0bbb3745e7f5b7e290f6755b030cab20ecb6f5daf701dd3850737692f", - "0x7d88f7a5af354de5678a270b8083ce441be4c1bd6875a337b244d93bd3cf0d2b", - "0x52b48af317cb248300cebad6fa682a741f3dde7da4bf0e7c4162f9f1607e7d68", - "0x04a7147b282fead783019f3453ad8d95618f416eb7ac0d80cbe3be8f68a13c31", - "0x4be24f18efeb4c1f8b12f124bd1f48d3edd1777629094292616bd89ad580682e", - "0x146f8719d2e7f74deb3619ce6dbb782850431bd5a5fdf5f5d2dd33a54874f536", - "0x6497f392deeeb3359e35622360f6416da4d0986ef0770ba22876cf1990e5903d", - "0x858d7fc84e4504c9e5f54cca2c9facdacd309d742aa7657c6f90e25e4d7b6f84", - "0x6fc455af373dfa77825bbaa65b1539e9eda251850479b83db15b9be2b5e144c4", - "0x3893304198a242335c96423344d68ffeda11c17c9b6c5785bc59482c385f60d4", - "0x3b2a22d6d87679b173a1e82db970e6cec44179be8218489a8aa863b7603adc3d", - "0x52534879ad0d442147ca743ea21fb5dde78510c11da353fc21636f9725f9609a", - "0x194e094c77eaf3973447de9faa29007b7b895e3c855cd01c0d5609e478df10ae", - "0x64ca084e56ecda7e9f2a10b0b2262f29d5bb8bd361019367dc41bc8f97963961", - "0x05754bcc136f5b541e59f1c40c99572fe188f6f2eec3d91989072ff3f3c1ceee", - "0xaad0bc1df434492344bf9351345d3288340aff851f687f952e84d968e1a29afa", - "0x90c5f0e124e125b2fae5d2ae5c8a1f5fe065ac2cdaf62829733599c69f66d674", - "0xc20d9e82fda10425082d631b6705815ac17aea924aa2ca534066d76734ed1bdf", - "0x68fc07e734ecb1c3951262cf804b831ff32e285246ebeffda3949e62d31d937d", - "0x7e472ad7b556415586fb5950e8d0b85e895b8c9c7fa31952936247c8117fa0b6", - "0xf5c3457576bf1b624f0f558bdba237ec349349c60926eff5c3ba8c55b32fd6be", - "0xf9c3dd5d7fcadfe33b2dabfcc4f732661985a72166afb8d16486028b89f35bc2", - "0xa493336b47637629c4bbbb89e8c8e1272722db0f7f781f4f78185d6125f0e5a9", - "0xc8cab7f153c08cdc9607263c5129fa8278a7228188c950806d5732db541cc59e", - "0x85c0c18f11c32eac54537d2c492eb9cabea9a39d2e1ac56d8087c02f68f64bd0", - "0x26a001beca07a641ad1e3ea64c63e395c436836d098818f937b4354b7a925122", - "0x75cdeed9ee0eacc02eef853a54a6f0201693cd1a60d26e504d5ef16110ede59a", - "0x8e4a5fd1a18f9513cf70dcfedeea862dfa2b61cbee80fc204bb0315a2c051424", - "0xa42896ce30565041da64ffff0a80ee6a6e280d0459e5c29c0fa3a79587d4a330", - "0xe65f4193189ca1da6d6fefef1c2f14bb340e56271b2bae3024dce98dea451cca", - "0x48c844ef658f2270efa4be7b85879cdaa2d7cb27f5fe9cdce0ec4389a5801707", - "0xedf37c427ac85a7c317bce25f06acf626cbe86e79f99f148667a95c03ccd34c9", - "0xbeca93d3b7e0f9d2f2a6b9b9ff7fcfff658c0d417830e46a3aa233f31908782c", - "0x61dac332da33e3533b9eecc1a287e732cfb9a666ef32de6bf2d2706792264927", - "0x622624f3810a1e24d48f54082f0df3ab3227c9cf9d39b007875a3428e2d70208", - "0xc1276025a1c848b7dbdd6f4dc1b7b2f4b19dcc7b0e312897758ca538ea334bff", - "0xefa6b7d35c2aeebf0d472745aaf6d4118279a88273b7513d5a21c3f619af8f76", - "0x2250fd4931ae5059e99572a7bfcee0117090c362075c0d90eda1ba2be2c864bb", - "0xe9b7860868856d1ba5d17cfcc78020485a36814482e1e8812555094dd4e85ff0", - "0x4df6b40297b258ae69a8858e54ea4f45d58b0ab1b0f1b35c3bcc4f96c928c58a", - "0xd17bcb3447f309b3ce0a48b651de3d979810cd9cb7edecb9b44366644e25c729", - "0x7292155c0cf9a6466c80ac2930f8305cf9458f64f40523a6b5e08d2ddc180080", - "0x536bc35368df05c3d3773b27110217c2df04d2f86e6e8a8dbf9dcd13cbbcb984", - "0x0f4dd55ebf1e1ea736108e62612972036dc533bc4e52a8480e3c875a7961ef69", - "0x902334a20d083b4634a79aae1a2b4158df3832d4c7f9105e8aeb8e38ac0be589", - "0xbbce2d76e50b1150179456ec4ce4faabcddc8700435675ebe94204325ee6f710", - "0x6cc4afc371970811c0cbeed73acc72641066edf1d81a585f28987ca1c11e8ed8", - "0x7cf1f9f26bdc8c7ca419400a03d683564bd5947068e4a01670200358d339b12b", - "0xf1e37f56a2eefc9688ebd8a95772f98b9f995e3fa9ba6601095f973cd34a13aa", - "0xfd7ee922a80adc2f6719e6e1e90add658033c7165441e399a3f61e93ed56af2b", - "0xd8ee732eedd804de6063af6b828fd6c4d9496115a367cfdd424a035646cc9e02", - "0xbf6658fb5940aabd9cf82fd9e808c9a23d04d171a62d0601d2c9dd67009f0b36", - "0xaaf7099280336cb78b87d9cc3c06694c4a66aea2a095ed614a4578fa8d61cbf9", - "0x46e23a8b56b9061bb89a42253998e7fda17289a12c63fd239a0c2866df529f3f", - "0x19b3323694c1dc611fe3722987d1a927dbf1a1ccd211b3c148798cce7b814fea", - "0xc16ac7ee79c7ac2223f79bd97a193c3bb68f609affa1596b061e9ba32a4978b2", - "0x5b929b2451c9c284c40c745bd441e1db499e336ea8a0b616e925ae780a6553bd", - "0x954a69859410a4a8309ddedda3f1af305c0baf02b1c0cffb27c0f89e629e4dac", - "0xff44052a4c53837e32b9bf4bc6080e78d3a78108a69100bcb05a85d4cf9e65e7", - "0x6199a3fdaf20c64b9587cf303682571f0de0bfcbf6ae34b4cc20485228a39043", - "0x0472d6b6c1efa88008d9931b3a1348a8892a61897e8634f2416758667da516f7", - "0x566bfd31096a3829990cc587e64e2a4477349e91b1b37ad6100be6b3de6250b9", - "0x5f51ac85ca1ebfbd382b4d60281d3746d98950aec6cd6bc1ca5ab22eff929a20", - "0xd45228a818a9f5d8325d2fe4873ddf5b0cccf4bc6ca907a08887adc19223936e", - "0x71d00ba260caf196d91b48601bb92052272067d1eddc4492f8709c3494ca253e", - "0xea6234921dde61659d45b0c36da969b7d3ed641c53cb54392e5a56da9f8df1d3", - "0x5a0dd2c3c892862da0f65bb4be26de9984c65aa2ff90526e6deaa1a3c079e7ac", - "0x18fde3435086b1d71fd5d24888c99bfcc2d4e7146ef4715192f4213ee9af70fa", - "0xbdf9a94e2cc27a784153c66df5d7f25316300d43f5a802cb7c5d6979a502c23b", - "0xb111d486bad8fddd2cd7d22e49d3ea3f4a0177af6fbebcdcff7f7f6585dc97ac", - "0x56921a22df9c98a49aa2602e551ac3e3d07f857faa228b56dd55ff3962f2646d", - "0xf8c7cc4743849e6cae415696d543445b22150afd4f4badbe9365f169a8aae3fb", - "0x8b9945119a385c746b1eae86d57e2a53453ab4183d49aea9412003a5e815ae8b", - "0x0dd84a3fbdb5a44b934a223503e9dcc73f893d35c77c3110c005398c640d011c", - "0x0960711a40b3c268874fa251c6c642c9e43ab14adf98daff1bc44949ca4a472d", - "0x4097f47259eea82fa8a531ec8c32b20b0064a0c47ecbeb1c426514498e5cb703", - "0xb17fab14bf8cc4aa51a1209711533b566b907d83f17b5ba62187681951307e74", - "0xcf522b0a5f9c427dcd8a7c6e358b7bea8d683bcb1fcee19fb3ecc05b85afe281", - "0x61eba4f90383706a2220ed3ba48020c14b3b6f1b76bedfdd3dd84cf66e7b93dc", - "0x112dffaef31440102f9660a5d73880a7df7b4cbf3c5189b9cc86c7641ac87f21", - "0x908edf9e1920e01592cc0478bc15011699771fba5a3624296521b1997e409d06", - "0x44636c1768f04f94d933c5d93679572d01e9d69f88251e67e8ccca7a50d63bb3", - "0x146add71dd815d75bd46324c8820e2c3d485567ad276f0ce1e2b63c294c989cc", - "0xc3cd14435b5cbd2657f38726908aefd18b21d9fcc82e80b7e1ab0be96a13a2a2", - "0x7d9cee568a4ecbf2d1f980d8946595bc599bf56ad6b07ffb2a83e3b479b1dd07", - "0xfafb0df45cb663db42cdbfb09f7381a49fabaf9266f1ff788dd31ce22c30bd55", - "0xd16d87925d29d26760244c938cefeb1366bf9551545342fdb7583325e08850f6", - "0xc6186d21f0f3391a8456d1b1753da24806d02cb9c3391a40c9fe84143d5ce31d", - "0x14b9cc8f7d3e37d7af07f80c72ae6528b326b146c6380ad0bbdd8fda89d225a4", - "0x5580ba4d7233e13cce87ba2e2607db7a4e87e571d5b1b1a75887f261e03d393a", - "0x07b144469f1d123ef7249ef2c445760a995c2c70f6e48e6e166d5f3508f4333b", - "0x0d883f633ceb91e413494b00e9dd563cdf121d2a2dd397baf4d29f7664633475", - "0xc619796115f48419208b0122a9b18a0bbefc05f8d96c39248973d66583ffd8dc", - "0x994e6fe6c27ba71693d04b2e02ba9c0d8679d1b10fbe733240ee5c323ac5f936", - "0x88b987ef2d0f823fcd17ed43ab28bdcdc23d23127e462ddafe56163e4be4a980", - "0x67e115f2ec5c6f6f7983b4bc2c2ecd21f639a8da639da2a594b018be2a3bfe64", - "0x8e4b8ff44b5028560b047780ce05353c94fba885e186b1b198cac5e90a6cb990", - "0x0a0fcd91048ef7e0833552ca444ed59d74fe7151190d57e5b7fddeb02f119300", - "0x9a5203fa1af89bb99c8d2228d396731ede004ae03afab9566195b89ddfa51a8e", - "0xd540a1122c44c8e728a78048884f79fbe5367873fcc73cc46a9d4b5b4848e3c9", - "0xbf8a42d56b46f6036c6382bd1e970b1d6b5f72ba54d1d5b8d0a3edaa165b7165", - "0xe0262a446e33431abd61bf3e199fca97a575c7ccb97710c2a1847188157909f6", - "0xe7f717feae9bdcd172fa4b6b701f0ca5b58671f9608b5b5a01d51a048241418d", - "0xc0ee34d09069301fa5ac3378ff176efb2b526665925eaf67965699c8fa002c8b", - "0x241837a9dea17bf12b78b30255177b3f14f1871a7b9eaf5c964d30eeb2038212", - "0x2e5204320b7ff83e16ba9727b5df0eab080dd2ea374db22a664f0045540ad936", - "0xaf4e24e4430a008265a6f113c097729ba1802e23d119ae0efef2f2a8bfe4ca0a", - "0x462b2de54860e6d8efcbfde08b381d74eb8e61cd2b311cf14300853aa2f9e88f", - "0xfba00f6c3e6262fa9f282d179b976894376e7e644c1d745302cfdd21cbc3e311", - "0xfbd6361624060852c33c3199ecf0fcbd8e56549d64786d320850ef3911dc9fdf", - "0x7be237a921be1921f90265288026ab1ff7d951e1412bc93470e09bd40a17ce97", - "0x64f51dd23a43c312e57bb2c0da1fde00b48b36eb1c9142589bb66407acde869a", - "0x721c44f4450bc4d92330caf7faf277faa1f10ee8f4614f9f903b27e8b82d8e9e", - "0x71a65acf3a07914cfa45698674a9fb1a35618e6f849aac0be337e44afa7b814c", - "0x620969bddd7756c5d3990528b1ac4dbc81becbac02843b3c4c75ee991ebbf4f5", - "0xef0f2a4fca1191d5615ac096dbae87ecd3aa1a83c46c43a3cd48395a47b2b65e", - "0x5f9bdefe969c0554757a1bf17d990451a58cd2ac60cdb521679081699ba4c2df", - "0x8ffa4537562335a6e21036f6c3af75614887e1c0e54bdd719f53c4675e4e8948", - "0x700d67449ab43081029dd61cec4b31cad71335d822df7ae4053e5040e47090ed", - "0x22a9ede76ca8f831125f7e6d1600aa373934f0cefa888e84a0481247b2c98d2c", - "0xb5b7c6c9ca26d8981191b27dee2aad72dfcd7e6d135d09c3b0b5bbc6e9d64849", - "0xa24232b8ea1e1ef8a07cce001d37d148a07ecbf0792690470c106804476e61fc", - "0x254935114b97e601fc5653001b01faa3f0fac11653926e749cb114cdb8239874", - "0xdec3d44fc5130107d8270358b91d7e0d933e74c4bec6d97cdc9b6f77a1fbe1db", - "0xe947a36694956d7d65315377c81225aee6128c7f075c2a37db61b7b917599008", - "0x4451d920ca782f22a3dba977235c49a4e3b97d8de1fdf9cc9d819dbe7237ddc7", - "0x234caa79670cfa3aa39b37887e2cce3f88ab5472bf2c2e7063b9c9b9424b20d2", - "0x79d8c448a7c637fd4f4e545968bfb5fa59a62948bc7b9031eb4c17493efb41d2", - "0x0b8cc0552966a5ffa6567c51ca7e36f5ad1bbfb5dbf5a52759314721d4e5f463", - "0x774983d323706ba773f8058519a450ae096be9001790cb5e51d00ce1ff206b46", - "0x0dab876db72ff5b208e61e18360e37b2fa6425a3eadc178fdcf654ca273374b0", - "0x20339939f9a89a1c38d6a2dc5ac9a505f2f899a158966cb18775831b045baee4", - "0xa42e2405b8a4a4da00dbe1d2e2a7244e2270f9ce16f054128b93b076af58cb08", - "0x149f8b8bcc176aed12a041b55d040b306ea7d34cc84d5eae4dee06d7a1f204a9", - "0x9b8a117cd1948845b4b98086d80c3ba055a138e471fd3ce38173e3b06b55499f", - "0x22db704c6cd8a8efb8ba974ec4d0ddcf64b1396aabd57cfc271e47ddc5c66363", - "0x25366f36babf4dcd6a41a218cfc8007521c9934f3b70a6835eb5490ce5c32937", - "0x5d21555e54954b075214c3eea858256b109667ebba80b117eaf08a11d4c9a9e7", - "0xcd876855021bdd0ccfc05ae507c711b9ef1ddd7aeebb7e2d330d1205528fc2f3", - "0x09355ca9b36a3aeac9c496af56af3719df2e1e052a878731061720e1b77ff35d", - "0x488d20b385f25d7c6519042136ed3c959a7b341d0118220407034b9527bfa040", - "0x0c516cb443e22322e176465b7c2e0359823374cd4aba574d4104a70e002c1f37", - "0x586593c505b94eba3ea1f7dd3eaa1ffab88548e150997d05b336dea37cd9a971", - "0xa1ebea5af17b8c85755ca5693fafdb4fce32c5e1f6b87127b3306138612d3ca3", - "0x7cb91701609765f3df12a2c051219622cb4a662507c6a14384553364de076dd3", - "0x3d5540773e424e0a92c40204f7c531efcba2467d27d204e0f3c6e655b44e5c87", - "0xc59531ab703c789a20fdbbdd08179893bf671c0d2041631c75d4221bb865b3c4", - "0x7df727560b0f89ef9af22de693230ea551e315918b0cf268cdc07f10690ef2e1", - "0x6d95bde1d89676d0efde7c58d71f91cecf8777e701f7f02a59b9679058e67ac8", - "0x225dae1c7d795471bb2498f380f2a144bdf457734cd3090925027375303e840e", - "0xf14008b13b4db1498bb9a902f1468367e0040cf4d21922ba288adb2a4fee0a93", - "0x36f293cff22d6d738d3cee9e22772f9e3a344a5eee2caefbba321054226c4762", - "0xca2c1e34994d1c454d4abd4b2bf939534a2ceadd6c6a3103d40de4725f77e403", - "0x4c1c6a5ec4bae84cdb0f3252b9f721da9ad348ad1ca6986ffa5e5735603cb005", - "0x0d35e0d8950362606c804221ef0b5feb04b0ad62d5d90134bf1fe119bc5a9eb9", - "0xa8bdf0d3fc34cb80cd14429fbadd0a2d73273e454d96aae4448f60c42f0150ab", - "0x11cc9d0d3064d5c7468a8c0a4146df476f254b7bd8f748fb42d899ce9964ffff", - "0x6d8752e6f51856b90d4723a34d94db4aec1f8da55f2b0d94f263ef0393dc57d9", - "0x038a4fb7f9cea5e3779a026873da89b85d03ba8c9038124eeadf88fa85696f1d", - "0x9d55a956aa38c72d7fb45f856c440b012ce8969c2990c7e1fd48357daf8ecfa2", - "0x163ded59e9ba07e781e07a5329c2921babb49be1a36bc7a54e22b3bc325840f5", - "0x0afd0e16f53b05aab3a77749fd114b353fc848d5e17dd8d701c37baf5c976298", - "0x5ca8f5796743e66d24f7dd342da174ee646c60dd8bf1e7564a814ff28b0922f7", - "0x5e856feff23db5b9892430e9e493b10e2fa2db28bf6833523f158ec1946eba82", - "0x59dec8e839a2d9ba33a5a687fcb92d8b450585864660c202d7e79bbccfe49f57", - "0x8b0a8363e98ac24707164bc767c0f6df7ba6b4ed5584f14693a06b08c3a23c4b", - "0x9214f12639d8f39488579e8265d33850dffe16aee099318da700475551da4ba4", - "0xc98baaa91e6f46d6c218fdc79aeab7d46063fc22107e457eca0559d500489d8d", - "0xeed3ee9c8c71a9035585dbc7a69f91966ff45ca33d81ff0ac69dd9ecbc05df05", - "0xa4b2f4ff38e28613f5ddbccf9f8b4cd17cf50704e398ac3b9b7be043d631ac36", - "0x7de22d2449411bc1e342ad3ee83af0aab2461dfdd7dd51b915a17fbf4350a660", - "0xb644191de416e8385167f5ce348cc3fd3020b69aa7f8e4ce4b58d796b401e3aa", - "0xfa137d84153b3ddcbbea95c810592ca1d219a825a3a9144a1f60a2f15890d959", - "0xb297211d2aa65b91fdedbdc6445e56c012e7d4e99250727f0f18471a204097d9", - "0x87c07e37c34bf4fbaaa2cabcd7c72842828fe0bd4e0af3dbda59a8b7dd8f5832", - "0x60a8ed3981341120c3e6c821f09332c6b83770357dbb1bbcb535fa771ab9ce56", - "0xe79330fb9dab7ff5ebd8511290b629e0feb97ed3907199f47b422e2b62192cbe", - "0x699c36af1b18f84f1443cb7dda84e24eeba099c59b2a3d45a9757b528a459ecf", - "0x0cf4295cec116acd9b74e3ab126b020e82dc216259a40003f493f15d1c89a9d0", - "0xf89bc33459d999b54f77f66ec80613d2b4a7d7e5ca5d58d5d2d9f9f65d9947d7", - "0x2d3a04ea977f6aecbfb671f1ece353c899c6597660a945c78be66a9c3868ede2", - "0x064e090bcd4bfe32f0b9a2756a01f2764d82b7fbc71ecd6eeab2f018ca1a0237", - "0xa3d1be5a6aea2bd18f5ce206e0f8c2ca898e4d4581d2d9ef3bc1b62b86b4d6b8", - "0x3be703b1204cb9800e53bf8b1521eae921f7f6567080f9fe385e712390d92161", - "0xee5c0b22d83a67a2a9ac8661c79d8212252dcf7d4062420cb57878ab8b9dbc66", - "0xbb9813100f3153ff7472f61f97fbaf603f228eb6cd715031018b913c70070d52", - "0xe416f16bb22dba18d653ef75e374b73722723a9f426696ee7477598e1aad6458", - "0xe2d51294dba9887f33cb3009ce096a4e9d086b3bdb114fff71f3438bbd784a0a", - "0xfb6aabfa3219f4401ac3ee4bf697052aec4cba80f755208b42fffbb1b0b3b1e4", - "0xf4d28c88355ed07f3c2007b9e8b7f4e6b1ccd988ba2b32fec0733d57a178d123", - "0x824ff2ab95f74ef02726a483f38342338c16d895a1da8cee5177ee1e4719e465", - "0x8aeec0b4a40bee3ae28552df14d4b6855ffacfc3d01ccde285e66228abc0771a", - "0x5573c773bb5bb5c5236109ef3eda585d2da1c5c38eac46b908fe283c4d519145", - "0xb82014148f595f5e440b729035cbdc2490db0523b7d00f9f0fbd4c383090fd4c", - "0x3bd847f5f5514f396e663821c8865296e3d3f7e817da4e93ba1c9b98b1758812", - "0xe5bd2846840a2049c0f78fddcb4892639156ece271097b56a74fd338f3862066", - "0x916ea84d9ed21144b3f9d73b03d02fbec444449c3a1f982c30bb20c0f75e64f5", - "0x2c7c705d9ec24d6cfbfb7f2bbbc46aee8e3c81a41becf39a23ac19df32dc19a5", - "0xfb9693cb2fab44966e6a0ec4c118a68354de660ae0bbbbe4da349df91e231fdb", - "0x64503206586c60381d9cb74e5f548e417ae80767d48e15c61d2f08f50548b7dd", - "0xc7bcdb552852328cc3d511538e3a0c94dfe1b77d489f3a44cbfab30b3b781036", - "0x4d68f37bbe1a45eaa3e72e3543d23063a4eaa1d38ae56909fe11b309230c8632", - "0xc21a5df0d75ec8d06498535c40ee76a330eb90add0cb99b9f0f4671f504ab1a0", - "0x5558d71b23fd39caf7021402520090f13b50cba5edd669ecf7eac05196e03531", - "0x2164e1ba3b7d927af0ba644481273ccebc5646e2c49825d456a1e75fc7c80db8", - "0xf10702b40b03ef7ae672004cd1747705636f7432a7493021aa8c900397b0123c", - "0xdef29e2febf8fd4a3313106832f3f723469f352f614f20e7d0e6c2e298240060", - "0xe62566d6d85c3ca3d33b8daf61b518abe76b28ec26b91a0b747ec3affab41285", - "0x1c286207dfee4b99fc86818c7614804782c8921d8d8f8815cd2f190f42b992a8", - "0x3670362acbc89076aa41149033484fa2cdaae47c8c10461f6e886040cbabd1df", - "0x8a74d7452f3a76bd20e2d1686243b90243d3d0b40b5b98fd313ff33c8d932e70", - "0x13f1f59beccce0f8d8066742249156a5dc0f21918588b6b145ab40d3eb4bb822", - "0xacdff4a0adff4cfd4756dac55df04e23cc1b9a6447d11d9bd86361fcfb6534cf", - "0x54fa4bbcab952b44cd3cb6042d6ef87fd9131f1850b8de0a44f221ca20a490cc", - "0xa06748708ef2e6c391d0d11c6c8043d91f9f1abe8292d55d70d616ebc5a94ed6", - "0x64dd68699969109d334ab59d64d8d22e114382bf2ebec5fc7ec66875a795276e", - "0x6e66b02d463c76d3a217202c527f798a4c472b37b234bbe0d3d8e6c16a34719d", - "0x3773853e93233b5c39d7bd53b89b769aff1504d9ef433c46b3caca15637ffe93", - "0x817633f3cc0665d68f497362bba1d32d3512016b2490047a245175654cc3d851", - "0x41e14c9198fd548c4b70963eeb24679cd7138dc061988cdd452f2de7cb85c14b", - "0x9ef5c3f4acda962833a68cf1298198b68ad38cd3ef9f8dde0add0dce93e4925f", - "0x7336e43b284042dabf2326e15f9717399fe3d3aee9586118a6047024ce3fbaac", - "0xfd037d85e4f6d39887ea64406302acaedb64a72c1ebb122519c2fbd236d789ca", - "0xa28129819bdab4818cd82aa1bfb0d31d642f66b6f072a079bfc414cfa6d7385f", - "0x02b7d36f55c39b97e2a882d47bb150dc74bab3e3d3c44a13d0149bd87f653f82", - "0xdc6609b3e7e5acd44d8bdd69913d864061c4c5b5d34a24a25c04affed384e687", - "0x0c5adc52eb0e98c0945a821a270e00c1b8424fc88eab4e4e53d8b047c2990509", - "0xb47060a6278b3da926c799a4c01e9682b2c38b9a255427499c03ee9305156939", - "0x8cdfbe9548608021b7fbf4a9845bdc03d2187885a25972f434af7706d8e9743a", - "0xf29db31d7986d96f18766658ae38aaf4210b22bfb14ddd1ee7c8e6a534292fcf", - "0x2be2578382bf5a26aced019adf5e1be8eaada7226996ad0968b41e0211950064", - "0x0fdcf225eb3c840d6a9dab17976b86c02a3a2ad6c457d8ae90a90120cb71239a", - "0x04412b8786da3f17b29aefcd1de44359ca334d551e4b84ab93ef562c16c34bfe", - "0x0be08908e549fb47570db3f9850a43dc3bea22fb569e7e56763029c2612f28dc", - "0x43312d35db155b984f27756d5f74c5028a9d182c346287b6d22318d6d09fa287", - "0xd99d026f8d05e8301728fe546ac68d0b7161d4c5f39d321d81c2d84af8dd9479", - "0x2aa222445dc7ce013979a51aeccbb0331d0b9618bd44d6eeb41978402d787a4e", - "0x7106550433791e214c8d1bc44a6bb74c13cb04ef44d888e4e86618a7699a6c0d", - "0x4cafa83063379aa62aec4ab93ea53b6ee3340b567bd8bea89b1f576ab2b32def", - "0xaa19fc5e04747c49682a08a04e26588ff2097586546c7e6307ebae3faff31ccc", - "0xc95bd63ec24007b5bfb3f3b62b358e32b7c4b562a3904d858b30131ae5146df5", - "0x33aca1207ba456f03f62dc351c975ee927b48d2c9ba394a56e955ab1b2cda20d", - "0xa3d6317111e98d624e5a68256f142ad3c99c3d2fe997650fd65d584579f9d6dc", - "0xfa3d58b3b49da8411398ed8b288f70757be569475c02f9ba23a4d913679b8ae1", - "0xeb470c4d3513dc24664d00944e9fc6da9fbdaacad66900accf0467ecff11d6e4", - "0x9a339d9d68706dbb4300222a5ac8b5823ac55af237273e0aad0999aeab8c72b4", - "0x0b67fe0a96dd8688b8fa98ce128e025402173dc49e0de904c390ddb828f059ee", - "0x634ac8f82795f1cd08f8cafdf00f869100f84350157e12d5a87de7d013a856cd", - "0x1530833bee82f5fa0aa8dab062da230024ed503e352de3956831a92cdbe36199", - "0x69b26efea6cca6ac3ae1bba540db8fb9288e421163bcc50172669053e7751584", - "0x8a4fa2c094135582d17b7733f74e7fe4dbdd0ccd0e5671696739639b64752c5a", - "0xfbff6abda57f9b1689cb7ea8809555db3efd4b21b0b2b1351199654d044fcca4", - "0x16425718846535b46733b84761e16648fa7eabae0e5995108a2fc6c3f324ddf4", - "0xc72eb88c5161469517e7980f86ed414b4e5e54dcfa734e2255908100795078ee", - "0x91e0e6cf1fabea993097496cf7ccfb023e798363bdf8a85bb5e73866f1442ab4", - "0x2530aacd1b1238514aba9ac1d13de20aad119ac4994984bfa04518a31051b973", - "0x481970f2e7e34a0064d136e87cd59fbf6131341027ab27458b9803aeb7e79983", - "0x99beb276484210f5b55e2b3662304a73c86a46633f3ff5aedb133217b7b593b7", - "0x0541564d394f368aa15e88097bb26d951d099e103b5866a3186455f422af1b8f", - "0xdab00c8928bddd0b3773af6dda612de649c18e4029c54eaf8467f82b153af878", - "0x343a7e702237917aa5d06778a592d1f279eac470beb8f340ab338c41a31826e6", - "0xce9fb58528596c0da68cfcd52d4556c41d7d6e8b858d665bfd110bb95a76b3cf", - "0xd40e49ffc0eec56394bb73f469b0aa1e1667eb4f912f5c3e82cf395545af07b5", - "0xcccf0af25ebe7d4e84acedcc9a88e885a4b99ab77b9881c524bf0e7aab724192", - "0x0ff189bfed3b2f56a3448bc08ac2dc6eb1d64125c95971ea0ce0fd5b7ea74416", - "0x60d25468039d7e8a89117eb468b4f561ea072554520d7010047680c1ddf5e70c", - "0xcd7a3c31ab25c421547b379a508fa0cf6d86cb9ce96377a5174ddcc315fee2ee", - "0xf681f339b4698b7f5016e9c7cb9a516d230a3f769b0da08096372bc87664ce5b", - "0x93dfe64853f84944856a2b79789ac7c73d18826e9a4fa6d970dbef77d000f7c3", - "0xc599ae73c9dee91ddef71fe0303080403753e3610ab73f242f3f8fe49b598b4c", - "0xf87d38124c8014802c3f523d56eb08ddffe645ed5d84ec1f11d025a88c594cdb", - "0x556e5962355580a0c44e5d80b28abcbf5a55feb5c81bd7b7a74c4aa664a7bf6b", - "0xdbb37e17df80f67cb8958590469d6d8a602b11b4dd28c8bdf8cbe873a386e644", - "0xb4932582d58d113231ead5a2d31eb8ccc0aca98a8dbdd9a450bf66fb4d65e18c", - "0x91ef1100276650f3dcdf96395859c123ee6fcb4686eecbb97bbbd6ed6ec6a656", - "0xfd5d42dfcb6c00aed36fb6f5a38aea10198758e59fdc4ca80b6baef694793fd6", - "0xb9d56c16bc71828af33e2a58b504ad96c96ea445c4fa56ba3703d6c8a971d9b0", - "0xa6ea2c5703efc858f9ec4a96062d32cfa23feb7d7bce6c25a12535c23b395809", - "0x72c1fc542e61af3181e988774989c782875d3b978df6e837b3a601839225dbdd", - "0xf332d4922e029f84d9aa2f49f910191df88f7f1d96d59ffe03d8956b0a5d54ff", - "0xe45ca95879c07250f3a317bb34d7f5bd0188fc947bbeb04262afd9c78039e197", - "0xc8c33079ba577066cb8e5af8c0682ff5575aa64cb30e400e5ed73fc749933d9f", - "0xc3e6fa3de28bee05a531d588d4895e63452eca2a188385727d466b87462c54c0", - "0x6740e11063142bf755d0bef9c254673af1f72807c3be4c531212efff63636f6e", - "0xce52344424c05c60bc7130b05febd30f611a081fd287757442687ef4adda6948", - "0x99491636ce876cd89f40d38ead2ba1579a050c16400052d516dad0a89b092069", - "0xbf6cd1f7f026023683c23d9f0d891e765823885b211420b9a6b33947138776c9", - "0x7b478202b808e4a54037acc93daa1136c6fb27d89b9f897b9fd3fe9afba73ee8", - "0x2cb32599960b97177a94c84ad7a047967eb2e212415afb7f1626a75a75cf1b46", - "0x62ef86c77b59fc2261a10ff39d74aceb1b7f7051758e5372081bbd911f56ccb6", - "0xcdd56a98c6b30e17a3239158ff7d91a415a28e7bb7e9528cc9996c8db8745667", - "0x8e432c62c747d730b197d6462d9369e5e9c3fadb7f36702c4c76803c5dccaee3", - "0x9eca51d69547f45fac4b49f0c6f126e9b5c67259880d28112dab89ab8264ed9f", - "0xdfcf59e4a92c389ed131b40b048abcd0725aab626f4fdd3575a37b4a3a206549", - "0x5152046f56a54c8f926ed10300d516f30a4a624aa7fbb888e465ba7359c4f3cf", - "0x54135b5e02b568eeeae74e59f1796650424233966758fcd1d007a727145509bd", - "0x9253844423f0fb792a2248fb887ffd10be43114b810b2635486ca0f6d43bdbf5", - "0xf3394a79eb65ff0184fad992adf3d0a86b5e6df85b629abed224a85460261db3", - "0xa4a1b651f300133e86986fd3a881e6577c6fcac410bc58c63f4b031e2ee2bdbd", - "0x5e074eec6c53dc49c55b50c584b0855e8c5a3c64dc72e73e5cdc1b086d07596f", - "0xf110a1c5c23d3bb9ac48d2bb03972e11e04b52da5eb62db65a4f4a2ac8d2c068", - "0xad11be682bcc25d30ac1eb6ad15990499d488c402ac6ef78085ef8faa5d4a248", - "0x0e13e09b2e3b8de9e5fc9e93382f06b64f03f98c3e26a5b3af811d32136ed0d5", - "0x494944f0a5afa4efbd967941129275dd6046535ad489443db1a384798cd7a02b", - "0x38474b45706c7cc93dd2ce11c41ac7f12f494c38f3a00c9cd38e44bc8771e04d", - "0x9a986ee21db81a38be8d1ed319f2d2558eb22de3899942fdb6395a188320de30", - "0x13ba368a9882ee28efcdfe67f4bfbb263fc8bc1c07b6f1369d3bd5ffdcbb3a01", - "0x451ff9f8e52bedb6a0f47ca9093b83b7d9a61e94329403d8eb913868945e0b6e", - "0xa04bf418215767d2aa8e6a8c6bf6563c80fd6f439d8d8759d2a43d8f4fa69630", - "0x551df5f968cdd4d0b78c1927b20383ddb6183a62f80480983d23f734be98c265", - "0x0df43d41084f9aaead6b9d77cc8f7760772ea1d27bc574bcde7061985e2891eb", - "0xcd8fb777f3d81581c5ad765b4f6d7f9c8d45c92466c65fdfb02637f226aca9f8", - "0xe4a92928a67ce00b8569eebd95c446ab82ba5beefdf467ffb0ac1bb08f4f471e", - "0x3ae0b6e5ed51401ac85ed96a84326cc1e894fb89a24269c9f3cd25ca157cc698", - "0x8363e4315a1a914a3d98eaadc9d985324073cc04e55b5859264c997d6d22834a", - "0x1e77238479593a1ab25b61990f3876869d2d87c9817e061dd82eb43e6404e0e9", - "0xbf35e78bd78e97326d6cc6c87f9f9c89362234327d06a65344cc9e692cd0d739", - "0xdbfa8ae2f7df4c5c0391e50c5cf9e144b45763a13ee250033d133c08b33f8029", - "0x828df5dd874a8e47e198ef4751dfec528c34348378574baeb2cb3b85f2376dfa", - "0x60deecf89aa73bf16f800255387ce9b9b74e02820b335cd96eef4b41939461c8", - "0x4e6a6312ff2847e8e419be96cbaf266ffa7aa91139d9d59da4b29c7e28aa813f", - "0x6c0d32f28bd65f020ac3c803fbed871422d6c6f02cab380056721825cb05cc22", - "0x7639acfede0e940dcea1965352cacf2b08f8ba0cd4254608a1033e2c80b1f12b", - "0x235155d27105a7f73b46d0c4a91c5a4978eda6d69db1af58d13b89076ebb98d8", - "0xcd1cb877b7b138f598658a39d1e135ae211a2169b2afefcd80923f244630233c", - "0xe9744fc928cf00f5ce170b81d28f5bc8f324d37fa535f2cbc2a23d82406d0423", - "0xe376bdbf2b052cc9d5f00f36b0af4d360174ae54b52620439c25d3430f405cb5", - "0x5a00ca653123aa856545902da24e3066386d1df00be733f95a4d105bb722b085", - "0x8e7995454fa8c9e7da6f19d9860cd0ddcaf1232033d43325b211c39023ca4d7b", - "0x8047e3cf7584e57729cc92c406f2c9caf41d9f5b3fc3a1f14e9357c954972474", - "0x7de15b023b1f923008d08ef6312fe8c3db5f499a26fcb3594795f47a30d28c85", - "0x7924df166e716bd934f98797b4a7f344693bacef336bad132ce068c6b70e5ab8", - "0x9ba0b15b18f2735800255de7b9e52fa971f2ec7bdc7de455c2e989abd27d35c3", - "0x9a5707a0ee4ff0d88fd8f9b5382ffddc558dc6c21ed9c8daad043a4ff4a561e1", - "0x898ea840f2e1e07c23fb9e18045e8addf5291661a72d6517b029620b52b0eea2", - "0x8f5ff6dfcc7c8ea2022c7b5d8ebcdbf713d303f164230a74192ca06fcd2ef20b", - "0x1508febeb95c2d3ffc8f1d13787bb9883be3529f8d71accdd55e747211fa8c08", - "0x2c7e4797847fe62a55f64c64977462cb792d9ec899e16f1a46851aa670d09eb3", - "0x825588a04dc941ba395deb99c00c282373e042a064cfc1ff557f54d8e0479187", - "0xef56430f5950a91b9a2028a549d4aedefd7322e36793d3cfaad18f97b58e9e3a", - "0xb92537e03ab350a4fd855bbe5175e8ff0e0df0fa8588885fdfd33bd2e37650b8", - "0x57b54ecd05c5582b98b62caadaef9e3386962a15c302caadde0f3ab2de98ab8b", - "0xa3f9ebe4db242dbfe248bdf7f504fe1f92079c60babdfad99a1a1adf446b3750", - "0x0939ffdb6c07b240cc1448f7276d7c8b436051e9488942d4b1a5afe5a0103aa6", - "0x778210367f625571f9d075ba06aa1ef5a8b59a411ec25b4e9540e7dede11e013", - "0x12087182676e11950138448ec4258ba3bd6ae31662f13db6cbc46c52a266dec9", - "0x7dd65f0f816152a53d8221e5554f4d3dd293219b0537a600caa6c5b758deb4cf", - "0x060370c4afa823b9cac3b2a87af1a989f8478169da3e12ad4073731a6729112c", - "0xe57c5b36d7372b192549e2ed987cc16beb412db8e22c3acdaf1f1c95ce1af81b", - "0xa9946d302b6f3dc7ccae4e4a69c21c862da4dcb9df371a8b6486534d8d3c6e76", - "0x350669a215b44b8cabfb6019d89f5268b299d6fd9db56fbea93cdd9f4991a977", - "0x4bbfa211e42c2b92c1cc979f6303e2eb428292500c6b59f0ddeb87c3fdb4b314", - "0xd243e1fe99ff82487def980022b204794fe4eb5b8fd657b851f7f5f323eb81f6", - "0x0eba7b7031613d9c2cce87ae3a9ff6e7b57b8e537913657c5c97f80a65f774c8", - "0xa5ad33c7f0755f82a0d6f47ed774bd8f2a00a66f2a967a52372dcee62f52c099", - "0x36d42846ce8b3021f00a615c0423d1bb1f00ed05df1748249a79c8a515a5cff7", - "0x7808d7655fb173387cc5601c469a26402b631c1d3e490b1c1d25e2e2e4dc3807", - "0x533b133383362a66e34e9e261442d4b867ea762b4422d2ebae579c144621ce20", - "0x46e2dffa796139a441b4b2a2ecc9530924ba032bee783cfae1ea1623508d55a7", - "0x0acaaa36ef65ec28c8a5447a2d594e48e4382737fac248b7895d3521cf2730b7", - "0xf191dcf174b7a1b026a9e2ad51a49a684abbd84556988b8cef315c3c5804f032", - "0x940284119cf5d5667d45103ef0cc790cb6540926c0f645911c669ccb78f6f127", - "0xc8be6193a1a094924999b527d81561f1ab47966c6fee7699418aeff2e16dd27f", - "0x3aa137a876b08b74d0e507ec47e72ae5d2049e01a83c68b90860ed7e9b594f4c", - "0xe375dba7c6b94fef6314cf569f887010165f40f930156e15c2897ac50db96e4a", - "0x82c766f09a89d2d8644603c21005c342208e2a61922eb13b476dcfa9de9ebc4f", - "0x06bb8f251decb2ec72365235d98bb9c0e0d0a18f7ef7b069f70eb432455ebb7c", - "0xdc4d5d8aa9f4a4e224510a32df3ac984428b8bbf38a9638fad1c74a22cb32c0f", - "0xfa394e11c7ea81618a3735ff52c67d182a013caa52897b10e1057de7a95b6f92", - "0xbe3dfee256c7cea8b110e9915e167d56479f5593cc5a8bd7e6375539c9ca8984", - "0x3caf7941bd973212ba085982ea0707889aea831d3df45468060a1f8afff8da93", - "0x17ac6c1fa00755480ab3fdd4d5969e8477d55ecac99ad1b1dc123e677560062b", - "0xdaa4e5841582d605767a49d59dcdde3067a61681f40e55e19adf73fcabdc65a6", - "0x465b0a0f2c6dc1583d1517a303947d372af513284cf2fbbda30831032c45674b", - "0x450b14d7773bb9ce708f8dc12aa7b8a9a83161eb8086d6fbe91fdf940f24cea3", - "0x0893e90abc7481e9413d33560ee9087d5e39305c0ee7fd8168d09da19c9a9e26", - "0x3b3e3615b3cb2402f807b1f7568174a6972421f3c6fada50c489428ac6d3a3e0", - "0x8ca9eb6c550474b93362bc8eaf6cfd7f39808dc115242310ff4ff6e5acfc121a", - "0xc55d95d03f1359410a23debadc8691ea0b53f4cc50073b449546c4422b16c037", - "0x26adc021bed5d143a3933824ecea25e0b6c577d8cdb9b899a86fbf706338dbd5", - "0x81b350f0026673f871a8981d899a31e2892730fb58e4b0273351b256e74151df", - "0x8a97e6e927342a4b9ebfeda5f5eede61ddd23b8b4421a0d2a8baedfaca168cfc", - "0x96ac659b574a4e63a8874a91fbf045f90f460b6c5b1222d72d8ddc240c870907", - "0x77c0baada7e37994e9f91075fc8ec335ce0a5d4f1627ceeb0a79034bed0b0f41", - "0x3b626e112b641b7ac01efabb2faa0b0a9166be0f156becd1254f0365d6f08a12", - "0xa15072f15329fb0c090eb5f1f91299421abd81efb05a00efd1361fa10228d584", - "0xb44bd4b0648b95e0d427cc5173d605fe21b00a5ee2c26d44d7c9a4f49e0fb521", - "0x0794d131e1ad17eb35301fab7566269e99b48b2e4cddc5b75f0aa15996220f03", - "0x994a7c6159433a62ebae86bb4212983478372b94fb3712bf025fbe3c5169038f", - "0x154a2ac1a23bf367f6873a7b51014d4377dc1008767510440a490365c4c1fedc", - "0x5881a0306e88c24bff0354b5df839f86178d2ba5e54d3cb80889df8e72952c38", - "0x770ae9a90625a5424e470f484f2c97b4f191eed61e15813dd59cafcc5bd0f127", - "0x6eb956fc5c743e7a2e02d694cb9c643e5ef97f960e90b75989aa91d3da1edc5b", - "0x3100b5a08d157428c6c10271486d050f913f436fc99e6fcc9b8eb1be9dfc39fe", - "0x507a78e12cf89269bc156b2fe5d991cffcd6ca6d7d085aadf8fdbb44d7689b16", - "0x0d2d43bfc425c8686487ca97d7ae5888535f5bf4d5e4feff6769ac36e1ee0946", - "0xfe866c43ed650b6fb1d3719df09ccb5126c36add42428bb8048a6841a88b4520", - "0x73ee2d004147c22f7beb21ec84293d6cd5974289a6f4eaba670da364df657a9b", - "0xe20395be53c10d9885bb2c7cfd78927051903ff4ad8301cb6f4e54c471b79308", - "0xc039903cfa0af94172d211f2e5c2c653d7d31905a1c194aea0a4be85c2dae075", - "0x468490362f1a7781b4e08d05bc1417107daf8dbeb092f1ab1a9e2d8e604741cc", - "0x186a3a6e4e1ce30ec18c1d72ecc4fac270a7d1ba9cdca0b7937794913f25c603", - "0x2fad6bab068378729dc7edeaa8ed516b6274396f21d24382d889f1146deedf70", - "0x257f8eb447ab096ce41e774fc4874335e77f750cc0d6bf4bf3e6b9d699de203d", - "0x4ace53b3bf327fd95451be5144dd2d62d906a25234959e33410bfd28e615bf11", - "0x0dbe710b12cd27f1196c9d7f1c5a981194ffae507b36b287cb758c32e264cc17", - "0x2c6def5c3a93b3c718839efe26835b0096efea132f6870ece1c33a7e91a2ba8a", - "0x2781ca465483c0079336532daf646104b9260c29cba0b6f4399feb557760825f", - "0xfcdecd6aa3e2c1b4d9f1991894cfe5d75f198dd445cf61ccce1c32fa8ec19e98", - "0x951185273ba63162c2d920f5acc888a7e4c1c6e755ab1215d8df5089276caf43", - "0x3e092b4e7488ba0172ed20dd9189f06d19a74269231edb0a592979eaf7c0ab88", - "0xd03afe649dcaae2de18474e82cd88935f50091394511a1f334e7590249d2583b", - "0x20c54aac1af04178d8c6ad55dced6e5cd2b83963f53ca65cdcf503708206285f", - "0xd8102d6cb1ab6c9f5f13a38ad0c375587877b22a6836a882f128804d5577b527", - "0x323445a4e659b00a2abd18bbddfa87dbd824877eab5693930c63a4704f0072f8", - "0x21caf56fdbf708f486b7756928bb74f117cad771a850dabc2a69291faf2e8d6c", - "0x4227752d2a7118b37d2089ed9319095d388c9048c0082f337d62d5275abb1145", - "0x78b96472d555ffb616105b2b471e2893ad6e1f8b735c4596be9d74f028932693", - "0xb11adf3fa7f2922095d6811564831e7371f87f4d417c730927949cc6b5227bda", - "0xebc5565e3b1338fd34f4427bb60075f6461dc072b097ea9a95237a411cd8fc8f", - "0xf4becc05db259bcf2790f79cc1fd1c88396db483c2c85c127818374be47da818", - "0x8178c8a0e6eebe2be426cd30266ef6574973d200e84339196bf6401dff1fe746", - "0x3d5a6f60d5351d577ffd38306e3a12606a1a3aea9023f2b8b8bd2452847d6b9b", - "0xa80644fc78939a7e41d390c6156ddc0d2b96f80265e5ef333c49e167dc585ef9", - "0x3e141f89a256b3b45cae79ac0c604dd9a19c09af6f685157872b42031bd62e1b", - "0x01a6910b5403b23b6e0102f4e012e0a0c06dedb944d36ad4c4e2eba8cb3be4ba", - "0xea5bed79158402d5f0c295f363a211b26d9ddf4d4d04e95564cffcfadbcce518", - "0x3a9d2d41ee14a76564ccf9a56d7c6d1ce732475ffa64499706b50e8ecd0e2050", - "0x8be57936b28e208f26fd382bc1404ea474001287a2c4d21e5213bd9327253ce2", - "0x672af36bc6de92544997f7cde2ea8870ae0f09b4cb2c22e8fd2f64bd88911f60", - "0x0d8d34a4ca78aea4627d1439402d4f0c64f130043e45bd6db1a0511dd16328f1", - "0x376539fe4c0de33e48d4c308800fbcd3606b82482ef9addbf5bf9092d6a55f92", - "0x93a25650ba0615e36facc75fa2ec8e86d6135022a31d86722307432871d6b72a", - "0xe95b8505b1ab584e83efd0bfb62a0d3c45785ac2ab74ff5e58178b2e742aa6d7", - "0x6fb4db8b3eb5b55916e2b06536b179ffec93b7300e3be36234e4ebb915a840bc", - "0x5d7545abcd38bffaf08f6745872e0dec47b0867df5102f42d83fce722dfe4406", - "0xb6eb5b0ef418cd1158ce9588e003e944d8afbd704eaab53d2643defa365115c3", - "0xfcf5ae4e3d7e2ba5e29653d5b732a1d86f18e2aa79ea2354958ab5dda5c57f4b", - "0x95aaf721f3c015305eaa78a8879d2f33b9270deee9d03acce855f0f44480dad0", - "0x054b3629ed6ab0be7ad07d3c658acc3ecfba8e9ab68492838b35690556dd722c", - "0xd87e3fd945b27bc037299325752c775a76f1c3b5b7c825fb9b6e2f6dd3680975", - "0x827fb42c7548a9f034a835a54ccc598398d2a8281677638197bd8d2bba4fdd31", - "0x7db908e4263cf7cdb822e42acf0fc9aa94999a1ddc327b6535e0a33c5237d1e5", - "0xa022198a7f3027a864615df968dd6c77d03878e12b9f6adeee611deffc82f33c", - "0x19269380075fff6af87dd26b7824ea70398e3802ea3c6fb2b4c2ee630562dc75", - "0xa13f3c9a337d9aaf86b0a5e5d5bda796d2f1d99c7548278d0b752e304392c3c3", - "0x13067c9ed1f15b7365541cf00ee6d7790aa0cf24ef3c60b10f16d0b5d0d67266", - "0x8d17d62ac8abfbe2b9696f918d92912199177e94ec49628baba2ff3897ce8323", - "0xbf65bee541ac0ed5f7529637a156446214c77018c5c59f0774b410bc9d2812c3", - "0xb3980e23fd6ad0683e19db55b7a8837a14c5c809f52de2950788ddc7f6d2187c", - "0x2e633f27e48af14dbd484a42b002517456028a2323c49916d777ec5d00accd2a", - "0xa44bfae290d8ac9e0ade331b4c362f5008152a38199996bc71d17e41d4a90069", - "0x8cfa7989fe18f803b1fcd926ed6e835286acbcd4dda22ffefb24cd3fdacf2389", - "0xf5b997a3440078a7c1322b70bae558cffc1234bf459cd295d5cd3d9be135242f", - "0x2fb715d33fe3c624ed35cb931c0a5b38475c1b97b743c15d5f0835d4836c1e02", - "0x9d1f7e02c66eeb6a75899233c02467624b46b7720a0a38b9af62a363480bf8dd", - "0x0c0a8f695812e88e92db4304d43c7a12c4958350b4df66813235dd0a69fdc643", - "0xca7e816b73d005cf04f08dcb5fcba1657fb6d339a2435fd74670155d097d6fda", - "0x4160cfa822311731d67d0f23948b99abd2fab077dde4efabc3a9625686a5aa73", - "0xdf9702f40c2564b7d460c35d25642b58179e12e859f935f7b87d41bcdddb517f", - "0xd24e79820676effc95265384449ee4bd8edfc7b747f2285b99518a2658470c5f", - "0xc79e6494fb1d7f01a4e8c36b221956e16097e92da140c3d3ccf336d5d99252e4", - "0xff84f37720a37852a94d31e3552d7605e403ca42c74830cd2856055448bd60f6", - "0xef0c3c8ca70dd9227d2cac50c3c6dbc272124b74eade2f26e6d20e21de527f92", - "0x82a1e299f56312e65bb42a054c044d58914109a9e914792ae888fcd94e30897c", - "0x948894d3791476e20b63650460960af12d2a33a4d9b23b6509ebe216e77e1cc3", - "0xd7dc6732db63ff742830bd429766ae73ee21ff37fd3831f5ca0e39c3cc16e284", - "0x594af612776eaa16e4f10c2416d5912f025df7c1284653ba844bbb3985e53b1d", - "0x348ee29dea90b5e6cd96abe5aac2537734efc4f5564e854dfd8153408f6222d4", - "0x9eab3769bdda9b57cf3b2a8d07a6dc2e07c856a15a79faa7890582b43f26e6ac", - "0xb97f381205bb10d19ffa24dd62f64b8687422c1a2967ca3b1b1bd59e30f5b5b3", - "0xf2bad3504422193b7f8dec65b2edb5bae77a3ec12a6403be0cf98e6372c59886", - "0x36a4a3f86f14ddf99c0822988616018d1d569a55f3e51cceab26d2a5ed6d1f3e", - "0x2976ced02e0fd21ee9992f847e912e4cb480d676347c721a1d8cf088242d2208", - "0xc6fe83273f10de94767eb3d67bd5e7ded408d674248cd87ed64e1e8384f15998", - "0xbdbe5d93b2812f44914fcb97b0fcc61a6cce96b9bc811e3cfdd6c9ac05619faf", - "0x415e343e7ae318153d4a9930f9779aefdfed85eb31a5f63810836beee854a370", - "0x33767db7f68c7432d03ccc405554fcb662044f6e43a5e073a8dd473f012bba66", - "0x32a7cf4985194e23827f89e1589da83b9158e98219b173f7295fd0c25310fc79", - "0xa304a15516c340a220b119243565f8a75bf6edf678029e0344ed6c1a49fa96c2", - "0x88c0c9b589f7c89d38f3f83a18dc38d9a3bcd752c214805c0876ddf2a3b45b86", - "0x216643d8038ec1cb6809b8ac77cf909dd160cf433da853e0572705e00aa210f9", - "0x4e5bfb91e5929da7d07e2e4ec30ca006cfc8399439cbab8c0a612e1f951fe35e", - "0xb20afc9312798f01965cfd83deb2a254f477636a565eb0578d8b6a86abf16fe4", - "0x83ad6dabfd5de39138c7dbac41b9548ec3fd601487ef3215fd520b4d8fd505f0", - "0xe96fe232f32f9f4a45842d502ca714f933412210eda69d28eb11bfefe1f60786", - "0x838711b43402e0c6ffa32b6c96ff20d47d8e00cfa39cab1526e7a6a6ab48a239", - "0x1d29b1af9a80c63e675ce5e957a98f31c57975fd6ff48adc3f62192393881c9c", - "0xece5ad70b3b260c8c7247d1e29c95a6347ade7034c26e9a470c0469bc9a91900", - "0x2cac591b892b14911202dc80a32e50c1c80a82a6f78a8a704bbc3e0c4f726cf1", - "0x1999c48903a3a6ab132189d27889c19c29eb04e65aa7d9a8c35722dabbc66cde", - "0xd03f863788d323b508d695a6ac3fcbaf93b44401efaebd5b7942c635c10a7eb8", - "0x57e92bc43ab1a66e45c054e6b46240838422dcaed9701d65e3fe41035b7479ce", - "0x848a6f66c7f5c3c25ae9f0783891c5f004600f274967870a4e9aeb91c34f11cb", - "0x193448bef47c66cfdf060191e4bc58820a3e1c9424a8c9933a1315bbc874d4c6", - "0xb9821736ea8e93609f701ebd178f00e87d4c7931deb9287a76ee40424eb377fd", - "0x88b01f8ec4bed91861870404d1191cd0144e0615943c609e006aa7570462ca0b", - "0x4014996b1a6ec5e1bc625a304ff1bcbd310b7094065dc6a75cb4070f44fd343f", - "0x1b1db3c8dfd7843c993cc50b4dea25179310ba77d1ce0459bad775bad54c7cf8", - "0x7ac6bef10622a0863b979573f8b9b34b9008980a6752027838ba7a50e68b077b", - "0xcaf393ba83566504254674bb9e8cabec2b1c366f4e7a867b8da663fd0dee1a01", - "0x05769a695b0b171e849df667fd873d1de4d1b0fcf67588986569e631417df8ae", - "0x13c76a5de115bd5bf30c939e21504d27e10d7e0ac5ea1ba4e71cec387b469e1f", - "0xfa24eacdcb99a7d18c49e095649ca414b3477d4188a3129120847dd513083030", - "0x907c3b7af8b74131260997f69c41850437dd00ba24b660371f23a1706835336b", - "0x4e812bb9a20644e2c48296e39688409d3499bb2a250abfaf8b0cf56ad0125aab", - "0x3238205f0b9d4062ae1ad8a5191d65f2f50bb3c0b7e1351fb8b66fab97a314d4", - "0x9130f27aba4aae0e7d48375a67f25fff231998d7b7815f24a2a8e86fff39ed6f", - "0xcf32280598cd5a0579a5f250b8e418567d31f3334fe8245c0a874d508eac745f", - "0x150f4bcda2e49b6d09a975335d33dc480db17ac59a508f28f6f05dd1d3caa22e", - "0x379b96da6a733c88a0888fd3a270c889f5e2c0728272e3ed1d654c7fe582d9f2", - "0x8df473f7e356f0dabe0d6f917728732a3f9e0519cb8e399ade5dd98762d0c1fc", - "0xf8b8368aa697e7748404e589f2f2f4d2808e7cb35086dce48437702afb8b882b", - "0x2b3560074c07e7955151dfc09b1293fd759d286b65dcdcf7b2c9f3dbff404816", - "0xa6361ee7b8af32325cb36304f400c9c685767c96d0b5181e7503ddc8a36eafb2", - "0x51405ec92bb2c7fb57f95c555150afb6351035e7553a63d26fb87e1827a6c3b7", - "0xa0290cadd15d77123903184806de7f04723cbe2eb4bcc2da4c926bfb3195cd54", - "0x4bbfd31db415a8bcba7188529ccf09d7cc10037f2c3dc7671ace6d4dd60a35c1", - "0x918152123a239ae6c50d9895940626e7f77ea624596d1144989a5fb7e10ef48e", - "0xef73abb8745093394fc130c7145e2de31c9457151ed78bcf63171b7633e22dec", - "0x09f80e170b2a1d599185c562344821d0e36bb48f91d8205653eba143deea6cb6", - "0x793055e11ded973ae3b55bb95ef6e985ecef03497e76e4e23ac477f6ad070fc2", - "0x2ae7e9c72203cc2aacbb79cd7cb5cb7fc4b997c9c740578253cea957ffd847e0", - "0x9f8caf269df06cc8c81cde32f1ee4f081d8a2ea8d945663e0339456e583c4703", - "0xb8533ef2fdac7587034839d1ce877bbccee50c473d4546c3fceeb7ecbf661f77", - "0x31a51d443ca2a588596b3c6a0ddd5fddf7abe8138177babd8205d165de21e76a", - "0x0a60c5ce88246c2e999725b2be6c330dd2482145fb0859ac0dad38f11e24e5f9", - "0x87d81ca8b664f33920634c62eab8f0ba3fafdf28ca19279b4d0c60a71eb738cd", - "0x1070087084fc2cc2e47fe731b8f08736cbef46194d16f418c87befc693c8a8b4", - "0x09140a959753b2c1622f0ad55f8f5d34b5f52a5ad6de7b6112e4ea1b8fda9e1a", - "0xf3af8d94fba3f4a6e018ebc0fe5088710876209a39e37b85e6b6bfda70f097ca", - "0x3346983bc9ed1f7f4f112b38526a92e7b416f30e4d1c7a934c7d74974f86dfec", - "0x587472df09a53693596d6bd1f60485df25fdd54fdcfa4c20f3558107f3bd4588", - "0x4c408ab0922f27796fd4fef68bc20c6385647a8ad4397a4a553fb68c5ce1710d", - "0x2eddf19c4a6ea3c2ec855e5b90ef78f3f861bc6317369db137f1a7f89110e69e", - "0x30391283286a30ea6df7b5739c7955a1a659bb0c46f3ffef8a58c0140d7386c2", - "0x8ca8497844280911bd97ce06b69d9e09a86ea135ca0adce596614fb6c584d0e0", - "0x9a699d399b3ea784ecc1e4f2dedd9ebf9a383fe44f40b4cc67dcce4ce98cd61a", - "0x013681943970f17a86dacea448bdbcd0d0ff56b243ef2c147ba77e58f1b47c8a", - "0x8717f032bc745c9a16eeacd20f3d86872e9cef3c4c777a8dfd867ab898e73fee", - "0xe3d4059812ed8f3d4e23583544131dde2a56276dd513d2679be07a36ee08dc48", - "0x265ff09a3d0413bc7bef45d66e34180c432b5f66ca96f8c39d390a83c5821411", - "0xddcdf72b890fafd78daf04a9e2afa824de34b761c25876763d9814fe41a9fba0", - "0x75168bbb488d49dc440360206d5b36d8934b1d9c50d8d8239cbdcbd27f238796", - "0x57aeafa912ef24841935fbe295c7f922b61bd729afce4537ed4985d06fed8eb7", - "0x7be35f0ba773bbabb596b674d73d8f0b9e6746688322dc99536e52d6f0100183", - "0xceb32481ad49ec6c259273d2b302352f121fe3c0d6a38c2576fd0667ef1c1088", - "0x1e1a7e3edefa2943844e452e8d4959f06681449e91622a537e196c7f3c069f5f", - "0xc4c5f628c0e705d6ed6e84b26f74d76a4ae13b9a602f4ca8fec8d94318b1e05c", - "0x324a02a05df574b12be04d2ae137c409a87963d524eac913d2b671d950aecfed", - "0xb1c115671fde450792a1bf3541d8024e84fa4239b3fd894af3ffa25bf4d10da3", - "0x9e9db6ae30894084f36122ca1946b96851c0e2282cc4361fdcaec5381552c26d", - "0x230b1fdb77d049d548a6cbb9b4dc8ddb983c3225591a323820b908cb79ac5ffa", - "0x88bc17b5d10e4b474c9f85c935ae3cd914aa437185f9bc7c03d66cf407e102ff", - "0x6ee030f383a99122c498a9c43bb51ed46932be9cab7efad96fac65f3b39d032d", - "0xd600f1dfab24e5b671a68e56d2d27f288e9b15ecd870b0fb3d811c1cb6fe17ae", - "0xe892c488faa1e15b040acfee5411b0c6cc93d1d9082a605436a0757a0cec7a85", - "0x8607ed1eca84dcec9c9c2d9ca8f9dcc952de7ea3c54c051d0d41e78fe725cebf", - "0xd9f378c67ea3834d89a85d72bfab542d6877370c8b542d1be2766e867a25229e", - "0x725ca420d55211930c77e292f602f58c281c12a2d10d77ac70f128c122272485", - "0xc141d38375f3269e92d6ed85dde2cfff491b9fa60b3554ca9909b944838f7781", - "0x572f4aa12ec73e99f35c523c864ccc964422946a0ffc5606792cb65944a1c61c", - "0xfb2f827d05901a25debd15a6b5a45d6a42f2fa84e212f79493e46e760cf8d620", - "0x9f08663b613bf973e1861e244e61e4b842853b8a388360234c85982e94ad96e0", - "0x17ce1320123e19c42bf806a0f204c9475cb3340a0434eddf3d8954b08d8a3c26", - "0xb3d935cccef673064e94e186c4649d19dbd7d6fba4ab7802eeb44fe4e6448bb2", - "0x2a04d4c2b009e424a88196f109923ab308052c6709b0f37f31131a74db07ca2f", - "0xd30500ba7ad6548049313227f37e094507e03d2a71792fe28f2dabe6c7a477fd", - "0x133f0204075c4a44764bd55409878a85f730f412a64d8098bf26246ed89fb88d", - "0x0dd3f70cb92468b09475aa687e81d84b7b5551038813b1dcecf9ad62394bf29d", - "0xe9b2107b3efdfa5b52ebd9d25bc17cbf934190af323cbb821aa01454809cc5b7", - "0x5a10e855a18fb6e2beed325f9d4df38645e52c8098761996a455eb3320c2819c", - "0x7ff65f7a24f95a7408272ee4122f202e2d651c084495b5173bb046bc4618f96b", - "0x06b0e4bd3381c9983715fcfa8c3f48b4beb289754fe7bfe8caf5eb21901c8ad9", - "0x5ef4e799c3d7bc81fddaf790f00166b63255836f5e03bdf584ad2535e05d3cc1", - "0x9efde50127e985d2c255ef17a7a5c82b46242f18230cc68bac49867184e897e9", - "0x78309317ac6418cbf58090dc8091b3746ae1b5f05b8b082610eb28d101e519b8", - "0x81851a87801abc649ddf08e1e372e7d0a50c8d18661bb3a13193389b06854ca6", - "0xc5170992c381aff40d4d02f0b1978ee689dc9ea00d0665d53c642c6e1617f82d", - "0x9c4d06f36bfd2a08a02715c5eea44ca07adc04b1176f7a47a3404af181feefcb", - "0x7f1910a654df3160f91affee5f286299958a5665ed0d37e39ebf429b281f5b4e", - "0xb60950ce3005d7854f9f957b4f1f49f0b4e781728697ad312769ee4ab80433eb", - "0xda6666bca71087804de7b3567e1f0047bd69f8f62d0bc7f63aec65ce8222ca2d", - "0x7e9625d6fe07985ae5a378a35cd5108c1db05e429328f639504fc17527b2e04a", - "0xeb606494a39941d272aaa9cc3e18d2c502b0438de5ae974e6687e2c2c433104b", - "0xfbbdb8353f8288217930f085c51e0c67eb9fa268cc66c0a96673918501119802", - "0x071dde3764caad8163d85a5a7d9a05f31b857828db55819afc95b08e10d6c87b", - "0x262a03e39027887a11b93963c7ca4676cc2352fbe46a05ca2e432902ca4235ee", - "0x1366a718f5e2a2b328943afa839241903a4bdb776b6f732d6a0172ff8c9f544c", - "0xe04d325284bd27091857f289c6bc8ded91dd16ff7ecb157e39965c3c98058e34", - "0x648d5113a9c413874f2e8187905045f9d8e044e29cd4dca0658d283d0944a130", - "0xe7706395d101ed29949e59b10f69214eeed0377be7af3cfa8231b917c6793023", - "0x7d37f50db8c210bec2d202a7211565d53ccac0e9b17baf887a9b865f4a57ddb6", - "0x73302fd966c3e65c0d0e7d610dcfd1a87970d26fd2ac15deb73d812a878fb8d6", - "0x7cb0a793aa40ea9894377e0f941b284a792065a8fb20a0bf60e8dc51744f07cb", - "0x1c2435e5e922dbd482e3279cbf88a632e34fdfc1e7bbadba3247e11089871ef4", - "0x8fbf585e305c90965c47c190b2e125276a5cf5747ea57e60e9bd36feefc7225e", - "0x320d5616868006a5f311c79c86761b47f683ab7ea1025e794a9ee45b699793a2", - "0x8706208d64c31ebf34d6c4126f72787f3d604dc7c0f72ea74075bd21afe21686", - "0xac6a123d378afdcc284d34bf487714e4cf9a090da9d9f3d67bd5510ebde00415", - "0x623376328e2e3c6ffb9a25250a24a5b8486915317fab74c46c06cbda943dd74d", - "0xbbb4c4513c3902167bc14cb17738633113cfe22ed5846d409fde2446740f6b9a", - "0x62ce10523cbfd602cf3e3e5b8c09b310f4bf1d7c645e3a2cca9d20d156b51f4c", - "0xf427a9a8c4390d7a4237f0b72cd19343c4986e771b180421c21dfb37e6912c74", - "0x52980fbe4416be30e88b1fa6e3325a0037feec3b6c40ae66f9d5e884f2deb153", - "0x9aaf58f469e9c8362e3016de4b12350e8f3c28f86ce58702fda69be65267dd04", - "0x0fdf4d58461e0e9477d39c18eb528501eeaa066dd61aed414a50613182057934", - "0x708b1667152c18475d00a25c8b87de03dfa6b2270bda21278c0dc0faf21464cd", - "0xf7903da3c49bd894edc260ac372926d90763b459539eca32eb3865d4a16c9bd5", - "0xf9b6180481cb5b2f4692793de59cd707c71b3f6f7396a40e506fffecd3c4fe7d", - "0x6f298c8d88b0e5120805b453e0ad10f6d65809fb0558c4bb5a344ee00aab4745", - "0x4bb3b6e0cf86c6937668b4a298da229492d98d99212c88e3e60af9e89fe7a4b1", - "0x0e2b0211c17ff6e28c459ea410d3bf33838eeba869c4d46238a25994fcb9dfbf", - "0x113e8f5c4c21ba5909963c3e65b112c78c19b8f3d6526c55de1af9a4b1eb8d60", - "0xf19f2f2e0a8de0d04a26dd1a10e96257f4a6a764b92d7c360453db32d65e89ea", - "0xb20efac16095e63f5f530de22271ecebb1fa7bf4d8ce6f49057327d692caa5cc", - "0x8781f616535ce6b3352820758ea0ddc74ac97e49a3ae9eae6ab0bda4e8741606", - "0x205895b72b02e22c73c3fc2e814c121d6b899c821d72f1b02bfd8c122fd817dd", - "0xee8e1b3a255140333295253ecfbf9a2d375d5ba1ea2879bd72088f6c62b3f9a9", - "0x4d3c138f077007962fa3fc56c7e5bb409c860d11ba48ca9658e67473fc4c4417", - "0x283ee3738c2bda7631f3f30fa2c29d3f3bed26c63180c61e1de03504b48f7c38", - "0x900a45ce2bc0f4b331d3df872f3d34c350f6b7dcdd704e36670eba878776b303", - "0xcd148e1b7d3a4ed41c51129d14cb226ac20ff975de1de2a802ba93b0e872f2f6", - "0xeccc9ff6f75fc844cdc8a723ee3f168d3f54788aa99a5f81aa9e609f8b4e4ba4", - "0x9e4ba2027ba44f31bdeb8ef27db30474de2f3728fa0607f098eaf8f03faa2fee", - "0x8d7289beac6c06009f0b72c2634c176a0784afc8bbd484bcd8040500b15f902e", - "0x9624bce0632d9a49d9b040a45da65e1d686dc6e6eed7eb3ec1536384b5cb3530", - "0x8187e9b62c764ebb82f5bda0bde0cda2842c47dff910e81c03db5c83dcf02da2", - "0xab7be6424ecf0041a1a0aa6c57cc231270ee6e50b80e6a8e07f6e40e8e11931c", - "0x43d8ffb420ac0cfec24a8d378d7d35230aef8887268414779b09247543b2f044", - "0x185cd7124a200639a0f60f1412ec44083f742960e46897a85d45a04bf5ab2516", - "0x91025f84bb714cd201f4d3539f7c5e6c7cf9fa83b399704ed0130b311c6886ba", - "0x13100633274ce0d423732cbc1bc1610123959f15974868af0b1900b74ca4cda5", - "0x3b00927007417f0d762f6b37c7ec0441e963a9afcf3d54e5be1adfa4278010b7", - "0xa8e59f21955c983044955aca7bca29aad4d408a1c958ddbc2ae526a87e3fabbd", - "0xcb836def3579f067bc45c0dee5f9a80eba544348c7bda65964c6b1fa2866e0b2", - "0x404d40e1bcf36d7d4e34b32eadbcf5cfa858be1499b088fe8ace025a2e73d3a4", - "0x2d9a5f05bec8712d206349bdc8fb8ec897cf2ed28d295cd275c31c5ce46c4fea", - "0x48c577fcf9d050f2b35ba0195606d8b8d91ac17e524fe0b6ec16be1ce7cda0d0", - "0xcffe1c3ab5caf25224c870cdfd63b4faa738362299cd37cea8b66cb1542f0201", - "0x7bc007a452ddb66827cef27c5bad01e87265cb38cf8e111ee7eebb21cd263bb8", - "0x8c26cc0839859eba7744b73a03f6318e3ea249736ae20bd206461e79bd7e401d", - "0x165e4a10b343dc952ee54c56f21b74b6486929dbb59ff24e066115bf35c223b1", - "0x2ea42214e74cd75bea948323313453dc6242fb0593f7e267abf5e544e0ad7d2f", - "0x2caa11a0a255bb4db80e305ccff40608534e638a17cb8bbfe6580a035cbc8ffa", - "0x0ad0490a3008883d02cc92624352fb7caba9a97cc46f82d1e64480559778edd9", - "0xfb24953b551a2b544ed6c6cad2755e6aa634112c3e225607a0efcb0d811ffaa9", - "0x4ec8e3591d27b911b1495f6fcf5e6dcc9461e9e602b67a5bd536bb0e6bc68f88", - "0x790b7eecb797e391d27085b27cdc5d4be20bf78de9918c3d1ed72ece2738dfee", - "0x7b73acfbce9837bbc5f821412c92a1b58b3af2bdf08b4c4cbbd12e151efcc90a", - "0x16e576b05c5c95fd38ac7d53699ee8b8b3431d78a5f671535590cc1c59c78039", - "0x2b4bddc77ed675df91af490a565c6ece5247335c90de744d6863e24e624bacbf", - "0x277efde1689605ebdaaaf4b9364449345ac664b5fe270736b2a7f2a97301dbbf", - "0xf727799af7a95e92eaea1bb2e3a0f79cecea663113e4781797101acfde46c234", - "0x54eb62372f7c5893d6be0a031edd80a25cbea3db2a0edde69ef4bcbfeaa24b4d", - "0xcab1e0eaa0aa475de501b2e3fbb5ed790744040a465a4256c4397e85a31f9eca", - "0xa0c54864bb507baf47aeef61433ef7418f68295f08d24c8acfac1eb18982163c", - "0x9a76cba0046b4e4cc89aa333a0f633147056044624c1b4c1c61a8a62f1f98573", - "0x510d25772f6e14f8e14905b05d8cfd6384561994ec3c92cc76fd37ae6a9812dd", - "0x39ffb78991cfab4236e4bc04325331dd8be2e06c4162f8e51aa592b27e1fa0ed", - "0x45c4d7eed33f4be6f8c0aba3ef42ad6a6e5f654b104895d9d662ea28704d6b43", - "0x21887c54231da9d400b964c63cb2f5378ca3c15f118fa105c0b2545528cf302d", - "0xb71cd47b4a6d40f9b18133c456c2aa7eb2bfe6170ca723a0c584ea28483d13dc", - "0xfb3c2158485851f4f564370c590f4a6b8f6481e6e4736ce9280a89d946b0e016", - "0x5c3ff34c067986eea52db9443be346eb1576498cedefc4a66379b24a3244fcc3", - "0xb27349be4de4d9be3cc9e904f72455c2f131c0beae6757592f49b2f0efd546e1", - "0x15877d8cccfa9f9cfd60408c5a6a173597c2e091a47fcdbaaf0a944a2232edda", - "0xa004d11f0d305763a613cb6d3d44a493ff669c5098b63b3f03e6402adc207bb9", - "0xc033417010a0f390418ff55348055e8b3e676c1d060f0cca2dad80aadc3b253b", - "0x035224e9c4e562b1f0c080ae58a7e9b8d48e565ab0be332fe580303a6b3661ca", - "0x6c7f4fcba6dbe1150b5bc6ebcf176ec3427e555978ca7978bdafa674cd50f4fe", - "0x4f62b3544c9828f9b7aff8fd5991e32b4f18e72c12b1d582c671a71aab4cbacc", - "0x8124e36227c5788e3608c63a93a8a0d966e4d8e12a81e2fa2378a969dabe0ef2", - "0xfffb09addea705c2ae3583b45e80908640b728867cdb8d49b2b1150280795b49", - "0x6ff99c0f0c3b3e208373fa3b7f031b8023b6660801a8ddbde0fe8668310ba332", - "0xfff2fb5a358d3f2bcaf5040e7e0fb9ad96fc918f3be7fc077a8aba08edc64a9d", - "0x3d734511c149e8c1f617fc48fc9edbe27da5e2837dc634ad4a359cb4fc1ce32c", - "0x0c8903bcec94ac45ebe8ee7c5875c23216d9399f7d14fcae79b7af4efcb4e503", - "0xf020a8e3254695e8c5a7b0a952531af6c886719b9354d4142946304220f18b53", - "0x4e1b5e445e923e783b878b943b0d9c9d2427cd8b71c346b79968faaf2a3ad337", - "0xea488d31ea2b67854997c6a88db87a13b4594b429b0dcb531eefede8e22308c0", - "0xc5dd53b4f8c9379c0f86e6b1270b6838d6a6e689865542791917251273c5360e", - "0x1646e9b43ac92494a90ae04647a4047f8de9640aa6f2c2c137962bbcfc80c8bf", - "0xf2f64a9c2e2de9ea1e314d84d44769a114c7035722f5cf7633145c13c5986216", - "0x62f06eb562f3c838c3b24a3da792769e9de8e2bfeefbd70c7aeb9271f19f439a", - "0x1cb36e1cfcfc9e4d8292cccd8d8b8f0bf90db7b63878e147188b16ec19754110", - "0x0316d23ebb039aa69d6b3b9b4603d9092f800b191735abe2a8ea2937b943f725", - "0x4c85e7f1e285380f4d80efd1d572781a93234149b8628350df7d93cb7625155c", - "0xde0010a2a469394e8422f52d128b930aa52a773368c3b8bf38a0dc830f161f8b", - "0x05ef4316554e9e05ca9a16c20f178481b630322c8dd438cbbc18c2bda644fc7e", - "0x64673e06bfcbe84232442ed29179e0b235d29e398e2867a484c2b60727377bc0", - "0x8dfe2659a262b307fb1cfd68fb18925a310691dbadab254c0a81aa9f9ea05c7e", - "0xb0a9fd59b0cbf68d53654d131aff4449c0fdd18217970c3de8d1adfe58d1f4c9", - "0x4ba2ff3b6232b74f661271aa29654638b05ce829083f54be06d8ad4290182910", - "0xe087ab9202b948f3c20cf02cb21f5f1b97d508773d8d7c2e5baf14fd52845076", - "0x702e6b8f663d10c1054de9751594d33f9e0915f1d3c71b36cd9c974574ebc85d", - "0x4dbec1c6654555295d0dada515dc606c18fd72e55b3cdb63ae018b1661c4385a", - "0x151d1c8c60780242caf26e5aeb8860f0ee9bcc4968e96c8c540d3ed0690d9635", - "0xf385a1e79bf23ea44d14b3f8668175342846ebc31e5a6790fca01b52074aa6eb", - "0xf5e94b06a1fbff820e98af762dcbeab9d38a45fe4099728543dc4784ec34b1e4", - "0x2396c1dc9e8b6cb903f97aff55b026511d64b88b08a05c83bc2ffd6becf60ee3", - "0x4b76c7764b5b098c3168779d95ca4aef2b861201c427c416c97b81372436d9ce", - "0x1a5d26c1839a09d7116868abdb2626467ed948bda413adeeb09c1eb146863369", - "0x25d61eb2338186246b78e3a4c4857c89cc6df0c77ec0f19b2d58ee9277c02d38", - "0xb9fab6c7fe62208f5f9c2714747714883987a513f115ba8d4c6904df172e9b0f", - "0x44f2066089cbd0ebae33e1b58179db26332ae99534ae4f864abc94fbcbdf1acd", - "0x97ffcb0dc94906687481210847ad2522c7cbff5758f6242c8a6ead1a010c0423", - "0xd681901709d666912f87058a300cf15b0bad137f4f0993809c37372ae1a73bc8", - "0x1e89c647ffabf82e228aa2ad65a989da66fb9f4a8d0f3efc7d66d72ef75f449d", - "0x40177f69aa2c7f7711c6e2212c2278a861100f1a46a99c340992e776f278bd99", - "0x8c09a1a9e84305a94b702fe21ed39db90d2c0eee6ff21185c555496e99c9807f", - "0xe13af929bc3b6b693936cbe82ffec90cd2d2e177758af41d5c7c7f8b3922582b", - "0x4ed170e95d7b677fc97533837ea0e350a30df6a3862d7b7794e051fbba68adca", - "0x5603af56d81026197df080dc437f9dbedfc82f1acc26f2ade39db63033b7a053", - "0x815bce3f12b5c5b5ef1585ab98a025aaa8117efdb75d2b677abb4522e4b933eb", - "0x603c0103be82ae58d4af604be625016b64c4985e3f2aae31ed620d1635d85a7d", - "0xcec52fdf68b04fccc6c7dc2990412c955d08b6bd4a7a3f0f9fa77d77d1c3481e", - "0x1b3a7d5f6206a15645ed55669485dc4e1cdf7ba41f3488427e8b7202d6175552", - "0x59946fdb729dbda327e825d64932da21571c83e4170c1c7748c527bc81937fee", - "0x676793de267e7078dc3b6927cd472eb1babd4f537bdd8386ee65b88166cd592c", - "0xc5379a7f5ffd7942f9b7590ff83592d91fdbddfa09c040006564434379868110", - "0x44860e8cc625991899e5a5c16d805f6b1aab9e1a79fa462e4ff2f92d6c40e76a", - "0xaa31dc38a358e3a1fc78c4cabbf05517b2ff78e23befdfcf7595074bc0b5498d", - "0x9e7d9885b3a84dc90dc25f1a48987d8d4cc1cd08f51baf1c3f33e86fd17341fd", - "0xd6790620e79c46699c21b0bd61cd09baa1e2b350fddf6d125c4c06fe30a3244c", - "0x4f60d8a3dc5859f69f1b96507527d2afbf899854e72d0d12ef27086dce561a53", - "0x67bd7c84772d709e1276a1863ab4b26ec84eb02767a804ffbda23eba3a8af0d8", - "0x707155713c08309b0ce29e867a9c025e36876935398a87d1153a997801cd7b96", - "0xe94c592379266a58679af9df945fb443b790e1c5a7318a5a1738070ec479d1eb", - "0x00205e95873d1670a9212fdcae09b06fc941128e4db1427fae7b3de33105c292", - "0x0a730cf91406030fea04d74b78d1625a3e5c73a1e7f39fda6db9225e064697ec", - "0xc32b3134dab797f09bc4986a7d9a969b9331a99e431b4026abde8d06a7910f19", - "0x7b9fc8ad3f2bc9e4c680e2400fad27bd0de271f193cb4f7e9727a87e999aecf1", - "0x39d65165378d375fed612489145d41dd4f9d57fcdce1a5fbba35a09210edb201", - "0xebe86247c803ebe65de42feab2d95a8d45775f352a0079e6a868692f444fabfd", - "0x04eb04c01e29b4a0f96cc9072576a8f7f3cda3fbf9b4050f3853ebf763bd85f8", - "0x701a86ebe017b55312ad8277eee062bf0a1b1ea2595631c9c28c740da3d4c117", - "0x57d6119ae2469213c921ac6606f41ed137181c179d6e207d21693399d3b21e67", - "0xdc189a5837c11ead4c6a1b7530b718476a8d0ca879fa4e499d72f7d8fec348de", - "0x96511948f65eed76fd3e17c9f1201e605ad2ee59778df04fcfeab5206dd29192", - "0xdb3930b8a1e2a8eb35940084eb0e98dc53aa1d3aa563e3ca6b260a50cb3a68a9", - "0x806d567cb6fd15fb18007354be55fee1c2b5f0aa1b1e5229ed0e758910af617f", - "0x6e2e8273a970e21164be1e548b92963ab241a68da41e9e231c12ee08bfa4129d", - "0x168e45c60dec2496e48b471b7d2fe1b829e9111191e6bddc936d0787fd42eaf7", - "0xab1ec5c8aeee05f24428c291a4d8afe776a92826f6e58dde95f8bf63e5c8ea29", - "0xa9db30a831b1ff69e38b342ee4fd3c9a8c3f5cf7b647a0c7f5735a4682eb42db", - "0x2b55ec4fe76bde0bc4f8d769d3b182e4ac7fcfd85a28c89fd7fa5d511f41969d", - "0x74ca1b5889e8920833042da86aa8d1259463827bc0cc9e7796144927757e7db9", - "0x5a3d4a8bfd449265b74b85e62305c3184209f12f4f55271cebd7d2df56b9983c", - "0x0b450e356dedc2d73ce19c6028dc5d36818cd3714879e5a402dd8cf7465b2253", - "0x073f03a0316a886c4cd71d4cec42ee7705b6cd897932b6547612f3cbc5c6b8c6", - "0xff2b0244634cc4fc8cae0b44f737dd47db2cb60e05578a34b336c46d0ddc8f09", - "0xedf8c719f93e1bd0cd421bdc14c03f2795dce10bbec5647eb98f5a64da5e7f74", - "0xe9192cd76b33b1d4b09e7d8c3e1ccecdb7a532420dac942934ab76d6491fafb5", - "0x5fa7a590d22896c2ae781644ca8db3ecf779691609dee9d0554ce562624a1e9e", - "0xe6af198683501ff90b9d14a343553e0c5d7c5b9fdf653311e022ad4a33eab72f", - "0x3c28170602ddcb77078d6f715a6629aebe4a35b2498a2cad37057f3357598dd5", - "0x29aa81bc8d9c54f146917aca4b1ea1a0f147f6ced0d23a647a51af942826bbf1", - "0x0056b54bb953c389159a761d3a405b71cb54162481f5cae225b86c8d2f409960", - "0x3c4d9ec5c31c3c087545487bfcda5db713f1ad44450eb310a66d66330b67a0d0", - "0xbbf3002577b95053f241733cdf991602a570a6f33935d50ad64bd31ea9a8714b", - "0xed526f4ea68cbb5a15baaaf6ea698e2d293afe848f3056b803646046aa6d5526", - "0x0162b7210309d0144b8464d21afdbd80b5ac8bc3cf5a93f32fbb87565fd4c30c", - "0xb051288210e1d0ef546dffc2e52026815261e2ea866e1276809000a3e7e3d060", - "0x76397a5e4c77029699eb10959f51ab090437888ea06b1c5d0cdf16b74bc44d3a", - "0xb5cd5928e3dae6cee3ef0fdf7f1deff66628ca8ec29743bf1aedba2e10fcf9b7", - "0x6d1f9aa0f4ab673ca6d0d0f10bdb491882fd1d582c2489eddfe7084eaca0b034", - "0xc636f215b9918856a0ef971a2340dd3d3019ba671c0118c352a1bf64e50f0e61", - "0x8ed6b16c3b89411cdb2a92cd42f2ba4983b26a83eb3112a6a376015458aea55c", - "0xa964e5da59c92492270317ca6a0b86402cb22e0c568dcd20a90b3e826846e634", - "0x2f16af813410d4c3519d9bce71c3c31ff09799d42760f49790f3789a2d365046", - "0xefe1139708e6ec67886b9cc3d6efa9b2b67868c626c3541e7d7a17c75434f2ae", - "0x199135b37713cdf4e1ef3766e331ea84f8e4911552de1e3c133ce9a3a3b52e1f", - "0x400cd17cba12320d710833bc5089c070c482f6fd4188b704628ff47611ca12a3", - "0xaf26e7080fc110911a649b03f8212b43626cf75c867b2af9e70cf35fa99092b3", - "0x346dcdf1ea179f89187527d73747c6bad12ee0efe4c02eda34ad3704d24638a3", - "0xa6df7e1b1a8fc69ad26110c0166383e5d4db331e98fbebb1b56611e0f7539a46", - "0x9d2b7acf84de840c1cc035a355360919ab5599ffce825dcf9598b6c8c23bae24", - "0xf18a92492752d0a118efb2ea386767122acfaa728a39d788cbe1e836ea0f6378", - "0x28f49f4f5a61322658ca40035c0340af61bbde7bd42b74c4a1867b19992dc787", - "0x890dd6356256b7bcdcb27d415c10709c6e898e5f947ca209389b42872102b36b", - "0xa24f6907d5fef0a0c28e79c3be437bab1e4470d29365b9b4779378099a9c3007", - "0x7ce1d586d950a3875954ef50e2b6bacb51e8cb11db45c34b709868aa7eb6bb4f", - "0x5cd316a998fca726c58a56079b8793d208f2324e399413aad3fafae077d834c3", - "0xd367cca6c37fe087c9acf4cedc12ae875eccfa34a4798bf916a2ff1cb245a8b0", - "0x78a1dc488545388a618cfb6eb0047236adaf32c0b5bb3dd35a1843720e41afd3", - "0xb7bdc303b26e920a184e250c398ffae03fe29491523b68e2a9fd658c7f55a734", - "0x72ea58270b1512daa51c08840dfc33e657c4495a5f50bb6ade1fe4e7f2e5d35b", - "0x3af82e240d734c66ecf715b2fa6d42a5976f5451694a9669ed49cb4ce6d495c8", - "0xd875ad205979ae78c6d334e5c2c0c539574172be4f2113e53dce70ac6320e1d3", - "0xa808ff0a2eba422021f62c0de2cb9ea114dcf340fbd97ab0506461578db78104", - "0xc6d5c1cbcb505220f3f47fd100470180620e47cb5b7ea81b4e6b27976ae093e0", - "0x3f788657a09cc34dbf670108046d4debe140154ea8283f5a762b2a6f1927cc89", - "0x0441ef2e2865848be1d3bbf10dd99bd9b307dc6a3833cc8066f71fcc9fd4657c", - "0xa033b833635e284605c2a25cf4b9ed595a8f6b7b6928a60f849019b68e412197", - "0x0c6bf4f59f2369da915c5cda4358a1b84326c94cfde7886fa99c3cd8cce00d3a", - "0x16f8cd5f6cab6aa0968931090a06cd78804da1332ae794954e43e880e6436380", - "0xc44d1853d161e5970cc4b0541aa7498948da07226d9747191d677c1c6c61260a", - "0x63fa006b716a07a172580b605dec90a99f4eb1caa2b6272918e21206b3b08186", - "0xdb0d7fe87810eb83bad89d04f279a06a316e9cc9afd6b249f77ff8da54dcffcf", - "0x04a1e3ca62cc77963b58ef79a9e2ad8ef54216034bd160728a7fa4806c0ccb62", - "0x3db1db1cc10010514a98f45142815604a945544ecb6438c63de232009f525ddf", - "0xfd6619df5d873b255c6e516cf0e12ec1114613a5d803e877fbfff779ec20a1a4", - "0x581ba19ac99cbbcc948c61e0614c64008eda6a58604e9197aa8470266821f331", - "0x316dc2404582e1ab45bef960907fc16e6223639d1875f6cb7dca9c7744cbdd30", - "0xa5c585326fda6f89e33d470abeeeb12a6a65ce9624cf01c5e2ffe5e397ec8efd", - "0xf08862fef3ea5a98b6b4bcbf0b57b482dfa149ba4d5e530829eecef1b5e0ecf9", - "0x7bcb4c42db39ab193745ac34c75e68ab457aa03c83da11f7396df2491a300666", - "0x69424ab7bf19ccf3583048e9c315a5a5d4a7d85359d6b4a3a7ec001be96d7212", - "0xa686da5793c201b92867db72a5f8671ff473cc12617437874644012ff242984c", - "0xf8c11302b69a3b2ad86e7826f8cc85d0ebe21e6420ebab0a7093feb7f15b985c", - "0x6ed81186ae9545a36b50f9a0fa0c82f2f52bbda7e8e936819628629e29402018", - "0xa91851b0638a92d2733b63125085982e5ff2acfffe66f71d3b218ac4ddb4d268", - "0x895ec76e33463b1e9f6a38a15a787ba84812d8d3fa6385824618b7de7f8235d7", - "0xa2c45ceebbb227ca7396e407e26c88bbf0432483608dfd169a085c5372ebbe5d", - "0x8cd8558d7c920df4bff9226cb91dc674d8788376c694ecd27bd84291255b237a", - "0xd9e3c84a853e9b551d296c560048283aff22e0ea3a0853e60505932f06fc0b40", - "0x5148cbb68497c824a428f87cac9512dbe313abf6f4c2b9fe6ee78cf909d4322c", - "0xd5d0a750818cd28793a1238c194503635e07870a65d5a8eae084a6a7d152069e", - "0x83c94a89524371c5bfc66e3ebfdab49eafaeb77fddd5b31bf3eba609cdccb083", - "0x4c3e549fbc7854bb417e50dd281890c745e9748ac7d830acd9c867a2abc64db5", - "0x642f75489711b36d89b0432f92f1ee19d3b8cc1fa99d2c55e8659426068da0c0", - "0xa4a944abbeff1a3d718e28a194bee6bc066f01aafcd0d674919e19c4ce1f473e", - "0xc9336cd020b35855fc7c4c3febdc2a19d4466e22d76ea44977f50fcf00d30772", - "0x0ea3c8090512fea72c5d457e0f3aa72fa0fdda928124177fc07587db60ee4c8a", - "0x4792e45b5fe8a3e2179b0745521a8269d887f8751be3f2c8e1cc8aa9ea0ecb7c", - "0x7ecb169f378c9577eb1af269377c16d25b09dd2aae49705bef1d143b099ffd48", - "0xf9722b9bd88dd8712c2df0826798b903e4e46ebea1caacec33e626b1954bda88", - "0xc7596d6ba28215314a8c0cdda3929284a0f98b14b67eee8bdeeb245a042bf5b9", - "0xb14ae9ee5e30cdf8fcaa8839e06f1dd1c273b9a7f8aa33d6a7f8a2761d6411b6", - "0x160de9ec9947611c32bed5b39fb688fd8521e0be0c992f318e8ea596e932aa85", - "0x166b7e4682502a245b971d27420a50d028febe29a06ada52709290dce6ff65c0", - "0xb3c6774d9d1415508baa55827001283840a3cde35900f0274eb78cc169b20e48", - "0x067c6e4818cb6dc6e098d2f41a0f0d4d555d3079a271e15e24b40c2b33d48c91", - "0x5f43937304d8f1c5915bed915daf11310fe77358a83b3dd2579986e9e63a418d", - "0x361e8400ee55784c4edb63507bc047de04fef5e5c96c4f70c9a61cb5d2fd3f5d", - "0xd6e24a0733e82cdd74b1ac6ec0a54ef3bbad0f68461f804cefe07a4d7e15b359", - "0xa3ef3d7ed01caca083c8cd24b0b2e6b93f307ab3bcd7fb15cc49de6e76a85054", - "0xcb22fe8e323ef120f5ea5f6604ccf7d84adeef7b15f3d81247a676604d127979", - "0x39094ded1f8ba3edb8f9628d72375ba2700942e0870a267ccfa75419231f8ef5", - "0xcdcde1fbcfc0138689d35a44fc9dd9f0b9b43f6bfeb9fe29ca02018ee5e2c358", - "0x75759de7eafcd4114962bde0db7b64e175787ea56aecb37f5b1045900ee8cc0d", - "0x53cc30b0160d81c37e5b9ace29a55bf07beef84f1cd71307b140e2bfb533d0b0", - "0xea48a27931ef94e6babd0342424cafdc835663b381983dd325b00c8b12ef9f28", - "0x3189d2f6c9623c4570d93e4c26ec30cad659f8482fd4bfae5325932fe57afda0", - "0xf0822c0d02fdfa65e48362ca1915c07797df6f87e0a98391ab4046ee4266b758", - "0x4245d5e75abe515824bebc31ec8e2ed9e5ab027fae1c9c74b49e9522c496f00e", - "0xe4c84eb52baf4e9bade65b3711b6bab7e16fd8a21d1162a24911ecdda4ec4c97", - "0xd73d7c7f12b4dc0ef75f4aa6f006a2efd64a6f54f848daa05efaf9fbbc1743c9", - "0x3e73cab1286dcb25621a108bf70103b8a67aad50d03b2a27b15f32e74d2ecf76", - "0xeb61a019301e6d597516b59bc2314f75afd9f8c76ce5ee4b2abcea2d5af65d22", - "0x6e1beb679490b6480aea261f776194bc2943aa7b4aed0edcc06a96472b6a5012", - "0x5c753161fc23f9bb1fc76df8bdfaab24fe0091337a36a56d0f5f959e4a72d903", - "0xc60ec5c3fd1986c8478f5714691a31acf469a5ad47d31e1e6137969328e86707", - "0xae9bfdfb4a7e54835186e3f161249d435420f83a238786f23c261ff0705cefc0", - "0x3a7ca5b57771c703881e05743b76622360ffec308e21b649fa9d2b77eb0485e3", - "0x093ffff3c8d30950e82d67887d44844d21f5d0d333534c65115146d92d667778", - "0x6b460d7cdcced9aa43dd87660e073e6ad6c068c7ff84aeb86decae8a18f0b0ed", - "0x1e23074207fa45234ca76dff1a5b4d78ee87839029b9bf4235cf5484a14a2ef0", - "0x4236c643b14fe734a6fb1e4f9ccd7b6b502ee5d24fee3af1201d6fcd5e2a4991", - "0xebf1cabb6606d20194f82bdfc22fef39e8b73691c802c6421015615cb8651ae4", - "0xa065c07d917d768a40ef583dba8570be0e67c5c067286fa6ea55183ed17aff9e", - "0x2ffce3fba6265f1a82be15315ed6fbaf8710133f2b8242c6a3a3b40da66e98c4", - "0xc814a7b871102332fe8d4daf0c8f7333a9751e3f29b7b02ed4d15a8c217d1490", - "0x29a0b87325be7115d4605df2b65991421f6594b3d3348238a6101deccc88742c", - "0x1fa4239e68c4fb68ec4e8786801d25ccf225336d095888879953c4543a47ca57", - "0x2bc5a052f0832c656a03210175d28fdd0109676e5cd6daf08614b6d9380e0156", - "0xfbea1e39f1d77d1cab4b111962e1b0bc8580fa4d353efe89b95d1ff5f9d249ce", - "0x4e3c512ca97b1d6dc6ad8f0c17e336aadb740ac907924ca6e037c3820d484a90", - "0x26e3d31503539f6fb1220ec960fa450ba15db330a82b97584f48af03e71d3fe7", - "0x37c3e1d006c31c06526b0c02df45329ce4ecbae209231a2222dd835ead7bd813", - "0x9eec04ec09974b5bcea99b021c6fca264f32b3ac895dda421482b865e9d0c340", - "0xa7a9d9fee641431f2b2f217a57a32ec61f03a1c692304768d58d48ee1c7411c6", - "0x87a5638751fc6b716bc25e92292a977f0de1b5f53fabc89b342a3f8c096e509d", - "0x084dfd89d9e63f625d7bc139a9584182937321d0a5e2b61e280cfcce053813db", - "0x30b73b647de41f3e753770f967615d695df8b77fc0e1f770d9cfd8a8b7bfc1cf", - "0x8b05b1906879d64f6534f7231ae5aef00d86c62915537ee54956b2cadccd9c7f", - "0xf1c883baf6073d2244d5b1a40b7e7ab99a22252e6ed34627fdca985ab87d94cd", - "0xb6c207b47b98f0b550eec7dafb5e6bb23a4f1061055542bc1805b6e89dfe763f", - "0xb5a37524abfd343f821cef4a3d6c66d5343bb56dbb4f5217f7c32b888d72c3fe", - "0x326124edc233211d1dec747ca38045125b3891938e8b449b0980be57fab79a26", - "0x4857dc3260a16bba60639a37fe309f46351d2bdc15c1689b6b86c16ee8b4d2b0", - "0x6728195d486a624099eee5c9b07ff62cb8abc7a66aa7f2aa3cbc565ee614a17f", - "0x0a7ac687337258206323a3c49d88ad4ed495e9a18c848a3d8ff51be0eebfaf68", - "0xe0c1882697acb508290737788cfab9880ed09469e4093f6c95e7282f4bdae4cd", - "0x80f763c0e43b60aad58f01aac1275f310b3cde5e3e8f2fb47dc6587b4a5b21c3", - "0xf7b62180b642b826340dd1f69477a136146d341983e4bd875823d804ba205c94", - "0x6e775aba73a0596d398c5ccb9d0adb8d6236cd7dc235e833e69a0a2a369d225f", - "0xa3c65ff864601acedf9ebea681039ae9d19b961b84c39c40561c2ab42b9956dc", - "0x5f5dd206d841a2a0a85186e2dff2f7d862e47e1bc11fdf0c883412643cff72d2", - "0x39df5dbc5ebeb3d6e94e02e90f987070d9f73060e3751cb5fb6c00d737d53a24", - "0xf3a78e92493d0fdf6430671c820949fc39729a366b8b3355634522bccc6fc7fa", - "0xd12bf11fcd9ad2026a340671edf4e0fcdc6d107f338b5e8cd1e110ce8b2b1f97", - "0xf69a0ce968327b516a50bbad9d035e3eaf78e501c11807516ffbe6d847103c12", - "0xbdbe28bf74fbfc5eedbd363cf4cc2792e948ffd66df8006372396ab2b901d9b7", - "0x10fed02d557ad1ba3794d9e97efaa8bf7d6ec86b08c04c1a3c63e099ac3c6fdc", - "0x0735058d7226f806a87f80fe5a55bc6756c96da1fb92b058c08bb2dc6356f4a2", - "0x9c63cf37aa1e235f4fde0cf88c13c3e87396646136de26757dfce9373bbad078", - "0x54105bc5dae2cfc5860308caa5efddaf6d6fa16b6506900851bf8d9311d85945", - "0x26a920994c14e7ca38d62bbecc09c4885acd455160910ad3b778338fe08dba20", - "0xb7d2bd9ec2f0c1824ed85476d70eb3ae85c132545b09b5a3fc89bb647db61c29", - "0x44f8a25051ce2d5c3b6edd22364657af990fd2b327acdd258bd818dcea5d8f10", - "0xc504b1cc18b99305f83cfb82a9452ef1b6650acc7a7513771a9f1b9c4d258894", - "0xae14ce142031a2a1500aefe7d5bb5f2c48fef2d40bcb3cebc4465b5f588dec0d", - "0x26a6a67c3642f7b63505fba8bc8f4f2db35875820853a7e93b7015cb59ad5288", - "0xef2cebfed16dc3ec1ed7adf4d31a907b1c48986f3bc678f0e0d42260f3e5366d", - "0xcde8bd5a558e4b4aca53d0d164ab030a9206e0a3dc3d52750454cd5dbde67a87", - "0x80213b7c8404bcb599c6acd3d0c7b97c821f9eb36d1a261c7d59c66aaf783557", - "0x84974ac71d8599e8b481f4ef1ddd2365540c97d8a14bc55b036477b8b930a1b0", - "0x22d700ec6faf569754dace5bd720287156b44a7c647338568df8316e4a3a1590", - "0x0f174bb7672e56e087429fc4ada9fdfd4dea9f94abd976a2d9c1b3b2103883d8", - "0xa31dce1e1597f2603aba9b84f545c56947496b648b770ee9bee76b26b076ed0a", - "0x3bcdc44880d4cf88f9d3a107bb254607cd799cab841346ea3dd2e10dc461947e", - "0x54d99c8c46ffb8d4a6c2453cb2ef92abbea6cf8d666f627afcb1f7383e248c9b", - "0x4bad0aac0f99bbe49041b0d7b7da2d0b379cd9a1a4e845f04ee6c399020062f4", - "0xadb0bb1b69fb15d78f835ef5d2de20273faef16889952c0578d3be609573ad68", - "0x04adca8a68fa1db84b226f2e46d3328be74cb9596e037d9e2bc612e5bfa0910a", - "0x9630ded627a97f9641e9606a5ea8eb5e04e0a7e52dea484b2498049e26a596c9", - "0x146544215d83359045dd52eb7821325178f25f950585174c97ce6431e84c432b", - "0x743b7f7b7e0e4bd94ce1cf7fd26a42fe3e60946b46f12094336624b22799abfa", - "0xbe5842fcf8d27fd42b2dd344f4fa80a5ec75576ab0d3bf7df076d2c62c87ee63", - "0x0913189b9ea0086155d331989622986163ceb9d8df937b013466350d5b6cb9f0", - "0x179863d8ee06cc5012fde594f93fabd674cd718e529214acf0855ba7920e0e5d", - "0xaba24275c0e3fc84593d75c0ed0ef64562b2ca29a63c9651ce2972f466f588c6", - "0x59953a3a63270197712f70bc8c1f3fbb974f3373967c76c6ecb05b36624c4e9e", - "0xb03b28ea463ee70e33fe1431556e4186a773476100a61200a72dae7b5a917400", - "0xbe29685ad0f0e4f4e06f7aef6ac68716345a503096669c02f9a4cf2645b20fb4", - "0x77349196f4b655fdf90ac091bf217cb4afc46efd8b3738438fafea3ec8bf6afa", - "0xe0f3c1b6b1f9a822f765a8657ba73b6dac6856b86d73aee29ac8c67870cb5d49", - "0x1e9967c1aa6cae9e6d6ce588cc7be4458e30f004f07f0886bcea9f1894409541", - "0x48e90887d177e254c035c4e6341e43e2fd4b69934f357ef0633a4876af886ba2", - "0x18b962c2749d6fcc59615c7fe48242c4593b18d9c777bc8929f663bb2e500a62", - "0x437628a508e2589d87073ae2cce6867feceab7ed948913a5f367aba70ac752d8", - "0xc76db66e84e99a50d9d43255bd34937dd98666850f01c7abb61a59c61c48a51e", - "0x9c97b43a11c959976be2dfa43d3e427c81c9f65f0cec38a30174753911a0aa62", - "0xe7a8db25a547dc1ec612c3270d98ecc5d358edf0093e9a47f97bc162e2563c56", - "0xf8e9f99e26b147e30bd22a71a02c2e87a4f816947b23be23d7a998bab0bf42e3", - "0x9cc232de33ef4d9c2701403ee75627560a4a290ec00e9fe2bc3e48d22729e4f6", - "0x83b328313a8256d67a76a199607871c9d7d91ac4bba03e4c7eed2850b863f014", - "0xf785f6f42d160759627f956c381c0b2afe133191361126e1dfdcef8ef96a19b6", - "0xba15c02abdb13795b7ffbd05ae808df3ba6dd49d3f6bdbb7594301fdcdafefce", - "0x7cf481f442daaf09047af092254ea96b3e6fceff47e7354dca59ce92a7f0192f", - "0xaa09cc16199e82bf7ca1c664df1683dd68f60392383aa86f7a4d70db4fcc9ae5", - "0xf48584eb70849f03500c2992ca9d8a25c6b4af1dc6b267bf96b8daa844653671", - "0xaaea21fc3ffc8af49d5b6c8ff06d212fd5cecdcf1ee8bbbf11081b610e5efd56", - "0xc3b611e4a4769a4cd86fb97594b05eeaa1438a531661d0c46498ad1d5ff0533b", - "0x86ed5238ab44b097b901bbf7a2f76c4e554227377fd05cbde62ad678b9b40521", - "0xcecc96f7e1da3a85044251681174c283cdf818549338d0ac8c55ac32941c4c52", - "0x8e476a409ebebb50a6b4ea63a57fb97a94325048b98e9038155f44cad234c5b3", - "0xb0cdd01f69adb4482f4a95869c27e7f488b8d645e9da2a1eed7c50db91bddef4", - "0xd59c2ef502068560309402eeafba81b6104e4481d08d294fec8f4a14ebe421f8", - "0x117637dcdf602facd5ba4ebfb788cc6e894172a41ccd461371fb6dc3b29ed1e3", - "0x2088af0cc8a2d219cea2658be188bce545e095d526152531ae04a0c1026e15f7", - "0xbe9d09c236fc2464f9e96468304cb107c1015618c0777414048260ecf3073a80", - "0x0e21d49b429787ecc39920a7b1e57bbef8b54f6dff555afa96c6d664eba18002", - "0xe93a96a4f59c51c991822d4ee0ce843f194df8ef1df1aaa3b15ffd4a3deb2902", - "0xfe432910348f2ae69089ab89571297f755f70d9e52918b761b7cf059bc8fbfcd", - "0x7589450a89b93dd85dda9fad10249d5662653eeb34135a8c5dcfeee2723e714e", - "0xdbcbd8f60a88aec563061478b12c5ab4ceabe1deb3f2df53fd862fd6742c69ac", - "0x4c77ea8aa197a0446c2c85bac546f8eeb7bbce07abb092450f11bb3fb6410575", - "0x465ecdf71fee3c0e42f2dbddafa3cbfb53f4c9a886777cf13dc55064dd29b07b", - "0xd220779d8eba431d33a23b369241b357b399875205cc432069b445426d7a3099", - "0xc6b84a981cf211d41e16ad44b1e90fdcb71a92230c9ac679874f0cecaeb25254", - "0x31841dbf1cfba19ac78cd40136a8ed9f35a77d48fc927e049d58d62b3ff7592e", - "0x0dc4eef3839d33dd5a516a3d4227a3ce23a9a5532d146f37dee88898ee75e7f9", - "0x6859f52e3c1ceab008c2fd85fba4901d7543c99ce5aaf36ccd6534f4898894e6", - "0xd16539f006277c2a3dcd96d3f7e9fac7d5bb0d63afb1d208960e597ce29ac429", - "0xe0ef33cdd414f24ac2e67ab2329d3d728f3924c50032857e57dcc9999e37a182", - "0xf50b311a95bb72916c6d4dad0db9779144a85915a12f895829ca5de9d697fa52", - "0x3f773b516c1779a4c5cec58f6e3a0c1bbbefb939db70bd84ea4370548df29c06", - "0xa3348605b4ee98db29b6bf80eb8181f4200d691e27345732cdcdd09c395f0256", - "0x4f2970a9d6dac5eaabfbb17a916bef4571e1147ffb5e241360c6941ec5628815", - "0x1b7f448f52b39279d47b572e55bfbcf893e7d106436a46db2a0ecd888d7806c2", - "0x89196aa22dbf319c88fadac3c051c445c6d59eb60a62ee71158e4893bdff8706", - "0x1ecc71d15d386ece87caaab915d0a10ad763cdd38bdac269108e17da3183d0ec", - "0x99b80ec779eb253869cc4351371ac8f1896016de0c501271a0a15bc4130f40e0", - "0x09d601e2ef7b509d5343a8b6de63d70f99a453540138d0596b80e2f39212fe4d", - "0x2e0412de93ff148a1365719ef562f54addcfaffa64a630d2bebbe48275377d14", - "0xfcde011b08437d8db6c4e8661b98ec829e204729badd675782a7792648a32c40", - "0x075e3439595f2e5953419638c391ad68d634da037df91cd75ab062f5635232a0", - "0xe1c770b2b8f17b25736880a7d28e09845626879f61fa7bae77202178d26e2db4", - "0x10eb4649692dc022f7542da5156c8005f2a9d6c4b0ac81b5ef087bd989a83534", - "0x612745b3b43e42cb44d302016ab33eeb1924b8ed1a1bc4ea86985d88184a1704", - "0xbc23be71d7cc509307ebd055d02e396e867e74ff401583b5801546b12cfbce30", - "0x55be4d6f914bf5041a111b949ba61807b53707ad6aaaecb347e9bd53251cfc98", - "0x6207ddddfa073ecbeac270e0490bd4f1b126e25523350e45713f8f76074035ea", - "0x7c2806322ae925abe44606def3a91a9546ce5557e61517d22c94a6eb358aeea8", - "0x2660eb9fe82891120afbbf8d17c3a0c6e71a7373bdc961b398fb3924d769534c", - "0x8e94fda030985bee8161a349c74a10715ab29b4c404c4200332a124f8e6c8eb6", - "0x77c264bbd45e806388df32d1d30b7567af8463c310b2e00ac075f202594df289", - "0x11307dbd441966c22a78712e8026ef0be95c009a075b1d75881f45a9547c9469", - "0xd487cb10ea6f81c67836eefb9cf326cb01464b8a4c640359da0ffd0dfb5ddfab", - "0x32e5ceb716c69e4aa115de40404f0406841a43b9d1a7503629edfd830b8070c4", - "0xfc69abcbaaac051ad68462b2b6fb289f093cc8f965a31e4914fc1c3ecca0a62a", - "0xdb2c7749b7f5e8e60861730a3f39531c9fae68bc6d5cfd884b054b14ddfea8a3", - "0xa641637ef64ad91eaa7cb24851870874c04c74e3c57825cc00bcfca56cd671d5", - "0x6e55a7184ed79701a582293b72c847df32be67859a34cb73d1efddcf92c7f1ae", - "0x0d30de75a74723ccaf326a096048686a6236a5d9728e398b531ea34a20273e35", - "0xaf9e881b47ab25b2b667a9232cf562ddb07d30194f6a3358cd6aacc414e98f3a", - "0xbda5685b8567d1c43530fca95e4c671d4f090e50d0ae631eb7082f260036d5d9", - "0x21fe5a7e105e0e2f68698e20f88a9662bceb20bfcc884d493cfbed72fc78ae8a", - "0x1b45a77d8ce14a769f196ac4a195207a63241ac8c8ee42e17c3e0b340b1c1492", - "0xf8fc91a39b6e08d874500e240a6abea002794f104592d18ea01449589b06dc22", - "0x45d9330d6eecf2eaca322e83df77277c76f9a95b99c5fddd55bd77697cd1159d", - "0xbbd726c3aedf917c4741952ee3a21d3c1655aa27cdd98f44d15dddeda185c520", - "0xbce90dfdf79213f5e6c69adfd7efeabe74edd6e2f07a1af7324cea9d70615386", - "0xe50e0dbb2106fa5433506694325d6ebdfdf5270c17bfd255bf3e5e7c0177015f", - "0x22ff7ec5117adf2f0aae8532dd371ad9d902a69babe3b26cfce4a324854092a9", - "0x638b7c73e85e4aa5074daf2daf66c002c00de5e8196c2c01e6945d7aef6661a2", - "0x322d5f71ce4a0e743ce017f6ce76944023e858109a0c36fbab41c796b6f9c8ba", - "0xb55e7340a551bc76ec89ff3ce1c7bda807c75b6a89f0e97ff4a4ea59d232e91b", - "0xe7084bc046f4975a04be1ae50f4e46e69b7d0a3a3886e60f989ebac4a7d3ccd8", - "0xc540174cecd421363ed6b4a95fc554f0ed0a7e4622cb2c0643555eaddebf287a", - "0x03e84b7ce420aa276d9adce1da92c950688839cdd3dd621f797eba5b6986b3f8", - "0xb75090e43bc5790bdfc86a5c9eac4384d099daef3d40e9fde443409f4efe91ca", - "0x7e72b4a7ece28fef076331bac790e49ebf926423f2414c9cd78788c8cca28761", - "0x9b9b702deaa509fface95de5048fb864b80d7891fabdc7a9770af1b24777f5dc", - "0x1b056b381e1c60a3c126f2e5c4ed98252755a5d3b7f3528584dfd128eedfc89f", - "0x8711c4892f49f2c1fb0739de8eaf08ae8c8c279ec73e42fb4da5b25f22d73fc6", - "0x70ae1c1cd310003a4b4e36dfb0dc7351b6f49aafa2cfb5d691fb59a724fbd387", - "0x796d8689110932ef6582af180818bd11bb9df2b06ab1107879612dccc190c334", - "0x58ba45b6ff40da3f3b76da55a0ea9ca63aa135ce308c5d262886e6b1e8869621", - "0x8c2a83da544b209010c6b52618c3137c1066e4fec0e1731d6be408ee881d5844", - "0x1591af374ba5612ece1d642e9bd280a9318c5bda73ed67bc1e7d65ef5616cf6e", - "0x666b3f1a401843af2bfde18ce659965b5c200972da47752fc2b4bfce55625af8", - "0x23331ecfd36f8dc53c388ec3d97a1f364a55b8d701389fd56b867a828eedd187", - "0xd995e1e27b596233e33acabfe03adfeed6d34b421b16d5700fa4b3c234b91ef9", - "0x819d8d3a0471a292c89d0093c3458d12bf83b60394f8c03053bdde51cc08b217", - "0xca27ca64436b71096f37d6e0442a9a93097029d5f4b6d042e2a6bdb3edf94103", - "0x65d5ed59626e98c2bbd6350e4a10e3e45d7479eb2cb6538c5e020fbcb900fef9", - "0x812b423882226db0bfcf2bcb9f1bac80eec3388755dbcf6b613c9f6faf1517ff", - "0x000182526c28c1fb862e74300fa89181cb137a39dcc02fea67bd3a03900fdf4a", - "0x1cfc60d220c883c273603f885193858926f072d3e0094a337d12268007644d5d", - "0x453d43c8dfc8df47751045173fe9e5d200fff28042282d67c0ca9230728329be", - "0x591091aad61a44c41c91b9ddbd1b6159fc9eec6e9c5839c6a0252dfa11436e08", - "0x464a2b049caf36d77652c2d721850d1f8db2fa70ca0964c7f020152612bea380", - "0x8ba06aad1a433193692ffeba643a481e2932f8a2ac35ebeb04c9ff81866d294f", - "0x4d915cf4c14d518463805c6bc92edbec4e8e8689e141c542d743c40640559bab", - "0x0e72daf5ad4857e563921671bcc26eb186f37b9f9d44655341311a6b529200ed", - "0x07ee736bd010d419da4e5c37ed17cf5739a56a78947b4257264acea62b5d6334", - "0xfa03e571d821780ce501373b529ca3de36a53f58e197d64b57f0941288487734", - "0x3293babf49116d584283c915c0c8a26f9c696eff261e74d107dff21e8b2d08ac", - "0xb2f169d84ee6d4ba54b1d526282ab04b7539f216e62362fb20a569f129d2f89f", - "0xdfbdf7d63f67a6986a6c5f9ea2b49d375147b224dc1e489a45fd7731230038cb", - "0x827d46b120f6b1501977fd5ad96fd96090fda549d59922d8607c90ada10ec8c0", - "0x8e15c4714f44297868b988e72b2326eec72e148a849cd39c0447862a9f34fc9a", - "0x311aaecf73140d37dc35479828c96923690265c3591fca9b01b8cad0aa411f23", - "0x47de3df6c970ea79568dc7b2c65cdf8bb630d427d67ec9552cdc928b749a113e", - "0x72e70bd58cd9fe64b27016ea29637bc6d5f13507a803431a14465cdf371dd9f4", - "0x46367ab41a9815df566c20f6f0223ddfec61f02e087e5498402688770739f94e", - "0xf3238ded6fc3b6f389a84766a8ee6663f26a54c077b49ceab1661bd08b4a37af", - "0x0770db8ba56663e8590afb552b42e4db3748e44ff33cf19c483c2bd5cab61742", - "0xe88be10da232234c33a267b9b534417385bd21f9d670fdc0b3e73ad155871ec4", - "0x725cd4f97cd34753d7c39a5a072d2de361f41a4489720a99974df1b821e576ac", - "0x10778832a2f1bfb25ce014c78a2aadddfa245e5e539719fad270cbbed5958e55", - "0x4467fe919d607e0dfd78119a648aae22fc56e508eb68052ad1c9bae0d7c5a2e0", - "0xedcb7a0986dd70d8fed81c4a50c864bc3086a3b34ddaf437be7f445f454d7c88", - "0xb278f42c1518ba3b6d6aec679936d2130915f02f8002f8563b855d7cb3b79ff6", - "0xaa4b012061ef7e6073c95147a118266c6d3417ccd0133eb57d7fdb7b1ba3a5c4", - "0xaa263496af99304030953d11d771ae5e337bcc24050e9c944c9e89650f6ae256", - "0x3f99f4bb34f441ea82655d5ac40ae0a307c6ea000ae1a2eeb2709ffc0c624362", - "0xeedc934109c18a446c42cf36bb5d0b9755b439eb62c18c7d334c13c38d379707", - "0x663b6e16d36687a79cb14792580fd32a7c35bd15df6e6fd36bb498ca12b98b93", - "0xa4c2d103e57cc53f50c63066fb7802b6d77c474ef603e6e6b8ecd759c8161737", - "0x3d381365e830f7fcefa8a466f348f5be81a3dd5c72d918501881244417c67594", - "0xe3ee1ee45c13527eada86f390f69d112b97bb0bd20b40e609ac6be9dee116de4", - "0x9479836ff23cafc57b92900c5349180e90afc75571ec738021dff05f9e2681e3", - "0x7ec50e215e840a94aea029294fd6a48d27905886e53a1239699558631661dfd2", - "0x8c58dc1ee71c06c9a94d5fd15aa8fa4f7d90c8bc8d0eaab7d8d0bb2baa9ae4e5", - "0x017a3cf0c797ceedf53dec3cd522ffd6ad4f10ebf709b20dfe669ba1fe0a3287", - "0xe733cb71d8dca2d6e3c71d9cfdf792c8a6fd242af91fb17bf599eea1b6b75cfd", - "0x6e820e5269a6b6fb9e9c2f477cf796078a3c2edfb7ac8b99150d6660ffd22f0f", - "0xbf1d3b3dae3b10422e2c354533f8bd504b1f5852c942618a0f6b415c3b162175", - "0x12fc9bd3a29e5ceae601c6a4e7c92bfe6b22684cc1f828dbd2127ea56e1aa300", - "0xc19e28d20c121a0345d89b097acbb6c15d97253895d06b8dec219f11f84159a5", - "0x73f03ac8b09a769db2a30f44b77d4d5dd6d61fa97afebe6795acc9c5c10cd7a5", - "0xa92cac86cf8bfc4fda37a94e5f55763840084dabe413f2432c4b50ddcd3523b7", - "0xf7eb6d87cdbb534507ca9ce4cc42a03eb843d608d6e2bb42c79bde78d0dd05b9", - "0x3bffe93f111e1883a29b897fc4b33c50f32db2470d819316577bc38e1a5ed179", - "0xe44f4633ce949bd8312b23178b6ff0e3d72b30dabc546ed5b5f2dd97b490be38", - "0x5e6dd4f45c09aebeff5c87b5e8c3b7b77ce4991f230dc7f0dc62f460e684f371", - "0xf1e0440dc83803d73ae9a3bb6454a824194bf9ce4670adc598ee97d1e150dbe8", - "0xef43649067273a56948dcae0271f9a336067610769b2ca4553eb7bd891ff6731", - "0xd9bf0ef6e8c057bea8046402e5bdf0977eb34bfbfc9f14a37576a8ba09232dae", - "0x38196dcd11fd1c406b5201be0286eed3a94a263a1990b66272c9b8764a5f2f43", - "0xa7e55d25456b2cb9541239879d751e489adfc4c0e232445a9232f4462aa17294", - "0x379ea9b1194e78792ddb00740e0c87585ab9a1060e6a2239fa6c5a9130a2a490", - "0xea0d1449333060d955f4dbbd0c0e70593489585465b37202cb4936164296addd", - "0xac2adc5be7eae9e0a63b5758c964ed5b203f37309d92ffb1d22aaae041fd9f0b", - "0x37be79f2f0ebbd92f82502d12dfe42f98f2e75517f444a295426b6829a6b0739", - "0x51a86fba7b41a59b84cc56d264f5d6d83a40f233f7d316c143c29bf424fbf6d3", - "0x248215d9d66288e4c2ee0c182abece81ffdeabea2b16eb986c14d10d69ae4efe", - "0xd8affec4833fd827b2ec6d4c1905e8cc3266e8a825a7502b59585fe7d5ee653c", - "0x00cdc19f0fa69766a3aeae1dd17cd9f61afe15ef6128a957f72fb3cec4cdc02d", - "0x8c8b43d75591539f17fd442b604a58763abbf17a408958ca181c3593dcf0afe6", - "0x055f6cd0e80234feb885657182cd2fc44fea12c6041d20cad8d257f2b4f16319", - "0x487a48fce29c08736e352e4217ba5714f361f02fb8500ce6a2e9ece79b1ac783", - "0x807974b9c1da9879f8967e7dfe02e8e3fa23d08526963c8b22688ef10de36374", - "0x080fcd507842ea3176b14ca52d844df2f9796b4f1281f7d03180c0461d28b801", - "0x95e5e32410daebb3a023678845b2e1fb9097161681c150450c7b76fb8c5c1311", - "0x420611006cd3a391071c6fa22e82d836b5db225fc266b99e126934d5fdf4ac84", - "0x4bcb71688e5ac4271354485f2a28a3c63d67e028183db2a16b15dd718bb89762", - "0xd428d57137d70ec31c6299ed366f69057d9bf93ad06e62332db04db5c3cb425a", - "0xa384c72f5ac5864dca1aef91fb19d3233e4ae3333d68bb320a583f532cd7766e", - "0x9df6e7ec6a1f455ade59bf1cebf6ae1ffb94d1b05249c35964aece90f2ec9f80", - "0xe53ec67b4d7c04b2c7cc7abd797d6a7c8b759b24c8389d103222f3fc83ade921", - "0xf6031d3cc54fe050642891cb57466ad51e0e8f24a8ac52d592e531b0fb87c971", - "0x842c6b20d8a93c3b23e679094fa903feee295c933c9092ca444bb12f966dd650", - "0x38b30dce467bc4347f65f7c7cc4c8a17f58d58b1779e302954fea9e061bb16d4", - "0xe23303957fac2f117e5749e48e11842d6f5a03e97acefb4667630d281351da5a", - "0x47a9c1072f12cf0f42d209cdd33ec885e6e21d13587cd8f75eeb1f879ab55f05", - "0xaed88507af6c779fdce8b5afe124a51528828e734744d8c12cc289c21d7eaa47", - "0x1b6146a607cdc5dac7cc1a88f260ccc2f5883c8a95665b2c9761d9a119b09009", - "0x6ed83952b5bc9b18ea070cddbcca46e42d1f4419c17a51a2dbbe47a0fe7df731", - "0x7544a913c1e6bebe68a6c04dc903860e5e6fb66d4845dd0d98b3ff4e5c33e3ea", - "0xfb6eea6e7399be0c2673dda0ab64a6802d6b2eef1550341d894f1f4146ce9270", - "0xddb39bcae9dc685e7bee0757743f2dd67b56d26247931f4f184f9e8f0f603f28", - "0x363c1bca93f3ff58a48f7d233bf4617769da4774faef4154abc84a99f39c05d0", - "0xd3a56170fd60ab7c75b33943a5f56ec935471b45e9b31825da160498eeee4c69", - "0x347834e86b3ae5fba9a7873e36bd780317cbf7649c8c01875dce04db8e97f450", - "0xe2295dc1850609e564a072b4592b62ec6a0781477d929c9293e65ec494ff027c", - "0x4ecca654f4c6f701c975301f051efc05e9adf0e908fba6dbeec42dc99ea0c2f1", - "0x6b2662a790d931d1f22062a5dd2518b30d980c84d3bf5b69ff0182aaabb124cf", - "0xcf838b75182078fdd33855bec6cd789f0d4975edc3387aa04246cc73f423da0d", - "0x791a7252fa21bf3183b621d65d6a7b584ce1de93a05da8e44a52572fb72a4b4f", - "0x82cbedcb5b48e87ed79935f8a2f93fa5367c32fb14de3ffd2e8947fc8a61fd76", - "0x84a1a28a08d0b6cf71128636355ea3a045d564cacdf67fc29868deb32ba0df3b", - "0xf97f154b9d62135748f94d714ed0b87012584d784df893ad581c36b5b04ef2f5", - "0xc03cec8562c67e6bbf59f97590560141778f5a265b314a6213c440f5703db388", - "0x50735a0e040ca5596ded470e5068b9b7d93676d8f3d181edfc26b5a52981cf0f", - "0xbbb1b6b1366bef92cc978ce644cfcb951ed00d7969e7d89b77bf2de745210797", - "0x116d30e2696ee09337bf9e1a78ab4d8e2cee7c53505afe2b8f027cdeb34640cd", - "0x294855f629441397876e7a0649a1e5ec1cc3ae8f425c24be34ca45e005bcc3ae", - "0xe54763eb5c965fc0c6ff3e975802f9a5049fc3404fe790c31df8a3dd5b366a24", - "0x0413d30f913089871fa15d643ec78f2503d13d025d2b73a6b1a879a910da2ec1", - "0xa430e9153e8e0d9fbb50f0764a0b70e5add25fbd6d5d29bafa77ee93c483e4fd", - "0xa4c61e3928031abbc4b02a892f19706a313bbdfd5bb413eaf852ce84005f186c", - "0x4e242ca7106cf988584cc61bba6e2185f6b6c377a2c4db291e6a801a5b66e1d3", - "0x23b37072cd63dfe8ae9b84b9bb74130a90bd603a5b992ce78b6e2c068ef35b43", - "0xef7e50ea520ca6a595541606ad895beb80bd06cb68984572396e1ea0ceb2428e", - "0x29e48294e6dfc86bea2bf76ee9daa816a59840ad1c0878d77cca43a67a217891", - "0x419809205842b124efb16d84b5defd6985b3c774fb6bd443f1ef72a2801fffa9", - "0xb25461244545d4b6f9c1c846dd09dbf25459d6492b1209814f22a3323ba74832", - "0xee26501773c29c20a4e860047ce14e4af4f6234736e1156002ddfceadde41c79", - "0x3705a24875542eb6b893c0b48266b141af1b5eac2826e88ca4645cff47a75400", - "0xeeb8999c6eff73cee7ae33ddc065e3b3857f213e80a94d832693356f0bddf504", - "0xcddca06cab74354633b9fa50fcd22c838a9d69a129fbb3c17dd0008df9b23524", - "0xfdb23fdaaf80f26f0e95aee174446f7e7182283d652a332ec075d86f6f3f8730", - "0xb3cf34c2dde0952c0d46633ec3e4fca78a3ee8242f2b46b4fc4444afac7d611e", - "0x2d9485c467207fc78b2de05decaffb155b01446050b7a6679923181dc05a646f", - "0x508eb22f40e6e60ecefdde86c50c81f9e73253ca0658a63ffee65f6fa39d898f", - "0x4af2ccccefa90b3028576e09f522bb667a428560f8c94cb159477313b551e384", - "0xd85f3073e44e61bc52696ff86194679a1ebc3e7dce62805c8b03372e03dbafa8", - "0x86ed94dbc666a541088ec977764bc543ae892d73feb39b0b09dd47664768a53d", - "0x36934b9186a56e71ed0c0f51331a99b3f16f07d5b50a1b770fd0ad91562bd0c1", - "0xebe80ebae860665714be6e5b9e8e7cd5608c8b4466453a15ca2fdf5d21b193eb", - "0xcbdccd3919f1b52732a89ff592a6f1dc34da53f5dc409cfec7fc01ef1f61cfd8", - "0xac2a1fc08c3d4b20db35ed4f879921df5e61a6596e655dfb2c6d5ec916ad6bde", - "0x4e072a9ed4432b5f205808f79515fa1d023cf0e6c2137a2a8876b5aaebee28b5", - "0x096aaf5a90b5e6ed00510bb1af1c475c32fd3de2c800b281d2558c20c0288e08", - "0x0f60a5da86c795d88bc025909c8f4c9484ea6489e900c4070d851e91f27afbe5", - "0xc6532945ae1b3fe27002f5d035eff11c62f6b1e9866f23c52af52cdfc8c19c2e", - "0x7404e33f0864651428778f7a39fdaf11a236c8a68a30674d619546518c380928", - "0xc44a5ea175a1d64b02326e7e49782968d9f808a7854c1e338314766bd96b95d0", - "0x435c5d7e5f41b84a55a11606992a4d8ec543cb5062b46f897954035755f9c9ba", - "0x854e9945fb93e485d4da05069f354093750e8eae98a4ef140a4018f3baacd246", - "0xd9e43cac6d5ec4f110baa3c940c5cf35125ab44a102a621e78d824f1abfacde3", - "0x99e780c379a3a94186a99ce600fe88963e58d61d851c6437f7a5ec3c2a4cf6fa", - "0x32786b25ba696ca57c302d136bb7453a5dfc065fcd17e8c38a47a3806e8d4990", - "0xe4d0e757ec64fc8f485839bd34c5caa72ad0f33405fdea6fe437280b427ee6b1", - "0x65e36e757ffb5386f130d783da437e3c7eba37e80ecb8e3017fa6753cef1753b", - "0x3b5be8acf141f95673e207a7c4df7c81fe88767ade0f785f5165c7f03e284008", - "0x988ccc28f0fe0f667ccab304d7cd0d56354419ef8b7149099d1515aa384a0ab2", - "0xeb198fa97ce093081073e2f6d531df421a9692d086387221f215677e9626c6f0", - "0xd6b98b2c6ffbf467dfd0a6365ccc478aefdd8872cfff6f8176023be4ac1d00bc", - "0xa14078605f856243d5195898a5f1e91878bd5ce04458d2a64b0a6ea7f608a18f", - "0x9ccc3a6730ffaa7488c75a31772c956f911eb7002fc245d803e2ca8d45810a13", - "0xc220e09c2646b44edbc09b43748855653861e2211eadc84e7a9cf9b233c85483", - "0x64ffdfad8e5becd8a87aa11a5feec484fa9bf13ea9e73beb7d92e396589a4f93", - "0x791b62a9fec44f975f11e5e5ee56ca70202d0de728eb6a7acd7c40cdeabf0088", - "0xe0359eae31308bf682f09147977b20f40425ae465fddea0515c89a75cd569305", - "0x16199dc2b3e744a4928b767467d02053576f98a5963a9c123dc760c0e4944987", - "0xbb0a149368f44e9b033245d758f1d906d061bd7b226adc1ddffb9f6118e85339", - "0xc600086572f4ecef9f3388a1fc242b099807e61463a934a42dd6bb683193b8d5", - "0x5adca1ea45a4113c166eb85779efc6836c072293e47d2c746b73b2956237b05f", - "0x0bc8f8aa233108727de0e439264f9220a6d5f085112521868c10934dd4100f7c", - "0xeb9edb2bfc6d973c762e283348290da7f3ea45ac449a521e0b3bc41e818aa702", - "0x4684a177b066091979171a6d0ad0a3eab2e7fb19c1bd173786d2db8b1795e49b", - "0xc877565ea3cee7cc08aef811c986cb58d31358c5bc1595bf8f41da9d2b6833df", - "0xe56178ad5772adca0b3f5eebdaa03af33df4451e2797892d12ccc98caee4f401", - "0x3f02d423f1769519c746b6ff632ecc88f6c6161300579c05b704353a13225814", - "0xc2cb58c5152a16f20d9489af5c259483e46ea76f76778947dadf653fb17703cd", - "0x9598d6265c31d75e15323b7125eedb124361b08f1e695e573b4743a3b54bf137", - "0xd401867574d59042a58837137cae986c1ccf45051165816a28f77274d78e6e1c", - "0xd5bd34c381e34dc538839a4a20a5de9e5c87d944f05f889a6c0bf0abf39b583d", - "0x5802fa750e2a8b4ace972d927c4fee3db2c1c3e12f52a20bcc17a04d1f15b01b", - "0x9b13a2989252a0e796ee57e61889ca55bfcfa49d9285ec859e20ddbc8ae59f57", - "0x134da834b7cc2f81f2582366d270d566dd95fa1ad867c795fedc1d74f1dd78c0", - "0x3cc3ddced0ac6b0833ff5bc9763e5323c375a6c32d588b53b98c7e30b2660c05", - "0x8f79d40ed56567479f2510c4e1e81edaee9eb15c3fab934bbfb6c19583032f45", - "0x423e59300c8a57adb8a20cf17b6686238d3d24ed6b0887573539772d209c03f8", - "0xfb633f9f16d9d9f3889495193d5d5250c533b2d2feacfa8b77a598bd1573e59c", - "0x1a2c990aac12d4e85479edd998b7cd84419e64e1d64b71b6e5de354ac220aa5e", - "0x7d37124ba47aed15bd9f12aba6eb1e4ba760965cdb0b0b8049a2dd1a4590bd0d", - "0x5305defebd2a61fee315fbd92c33a971f901310a837b78a89132a3e6757d9ad5", - "0x90c9890e4e15d5285b15596d61471c5e699222bcab2c40d613a20b1e4d0a448a", - "0xa9f12a1a2b93a8f62ce5c31eafeaabd0f3053ca10382048f5493e2f4a3def390", - "0xd32998eb20d224ebfa0f2c87c23cf9cf707dea92f12dbb5c1cce2ee1ef010573", - "0x58259819f210344220294ce925298a770d4b945f69bf6e310d74428943d513a7", - "0x7a7e8cf127d88a93a5ab3e59f9fec2e0a35d9ddbf90396830c37c0a29352099a", - "0x27a39ae87afd6ea727aa75062cf53d35aa420787f0679a9817b15b77c2fd4243", - "0xea8404bc312c47dc19ecfd815780e1fddabed7e1c2268194de2755767f8c6c2b", - "0x2726489dedccf450d85a877501537c51b0037fd7fa3f46150bd5004160c56d24", - "0x1a6752f65e4199b1d7bfa5385a4de60ac0786a0c2276fcd98ec12480ce9ab3f6", - "0x2d6c57ca96d71f4d23b37bfcd63f70c8877afdcd20b9061372c5a9f4f5ec883b", - "0xd7d2d6c6b39a8bd276806810d77303e1fffbc2065f971462d3549b0841bc5159", - "0x72389b3e6dbb07c0cd4ec60f80d67ea24c5997e36457896c7047dba6bd388c0c", - "0xa683c35204e5d8378c5994a2b2cb66ef7cea7f72ce4a014f9de96e270d141f93", - "0xe73c7804f0a92e71739f049cc6d1f4296d6763694ce0f02222cce3a802b95e0c", - "0xe1d2a0db423aae642cf9e0d0a3a0e033ec287d72ad5d6775fb2ca0b754b5ccd1", - "0x2836a91b6f0a5da378fdcb358773b86fe4e2a497b5a8b0c6ea770f8168950cf5", - "0x207c070a56e477b151ac222d7d5582e4233a84f1760561b2c5ec26b6913b5be1", - "0xc6b82e66925ef9ac29b147459a0c7cf6810167b81d5a0d8119409bfde901e5fa", - "0x54f789a01b71afa8ea9a693866109c0fdbed99cbe60e976593a45ecaa2c51d86", - "0x67428f7da1b0ec6712491af063524e25bf846d1bcf1341fb5cd16de664e55253", - "0x9cb30239c73ca41a78aa23de7a5259e5c5320cb742d86e248e800cd4b7842f47", - "0x4ec478de462ba0bb5ee4f2bfcaafb9ee3fd65e2bcc18b39624db625d72ebbdb9", - "0xe9ba31abc5a0305325522cedfd3106cd0994a1be4462264ae630190ea3fd24e9", - "0xa293c713fa4280c0bc8febf0625bb7abe075f875e923cdeea554a48511eb1e57", - "0xa75d09ee8fa14ef49c5dbe05589f169ef70ee94963b7099fe61e4cc8b4c013ab", - "0xf0942330d11fdae546533e347054610329e3ea14b4f219d510f7e6535c9a3ee7", - "0x026d67e672413886db8a70f720b14cfdfb41adb82b7f439d5ba21b589b54e122", - "0xd49061710cb1c459d4a3386b2885cf835e9817d10381382ef8b4212c6d56d4a0", - "0xda04b4d64907968d284ca3072752af0cfc15ef3e5b866a22477bda9b1ad4bcf9", - "0xc3132e5a5899adf1daf3af4215cae7871cace74e8d876f450ebf0fdebe5372da", - "0x5305450d6a19385eec8c77fbf5be40e36551d7febcf1f7ebe6cabf3190e8fd76", - "0xbd7583e3d3d1af3619f54ae1824316f6d23a780647fb2b357db18076e602ae1b", - "0xcd515185622f58e69ad4f90618097d4c81177daad8719f046d3c14d6ce8375c3", - "0x5d518bf18a83f88660fdba41c8996b1e73dedd6cf5f0debd51703edb9e79296e", - "0xae7b3276b47d580f0275b8aeea0ed17f341a455275b5d8c321525a1258629dd5", - "0xf04a5b0379da532ef5a47e391f9db4a0224bd5de6661840ed9fea3b498bb3f69", - "0x617a8557906db76f6fbe284ef9df52298ecf840346589fc51f624b9686d5e218", - "0xc194d85f5a618d76aab6e49f5de491da2f602429dd772f364fe7fdfd3c6f0daf", - "0x6b138060c922aceeb0254e17c3a3548dd3e6904c61a67e058f961e080b3d9ad5", - "0x4de0534ace9c02822d3cf3d81d6dfbfcbd0964114503b56fba7db23b5f2befe9", - "0x06633e9de11b22610b6ef1fc1e8b952252099961f34d71789d8aee0e2f5785b6", - "0x8bfc843363eeb99dae04524455ae4df46ed6e6664edbb640c835305005dad194", - "0xf2f6f86a2e67a269cfdb62df43b9740b42a6eae3f52639acc93e9a8035194628", - "0x1584d00e4fa8d3f0e989f87002f9ff1851351044f99e6194fca52262da38f447", - "0xdc0801def9cf3897fd30f8200f0b6dc4966209e8879f26a7993154b135b4513d", - "0xb1b0fc31bfedf7bfbe445412e84ebdd76c3a09821a4436e72b95ffde019b955d", - "0x3c4e13e63d1bd76e1db089bec2acdc6fe39bbc6588bf4f4af117682aa8a7235f", - "0x2a6bb94e42fc21a26dff4db8004e9043a7a98d6d9583016cecad2baf37b4e882", - "0x7a0981e129e50d8abf6b5a8e0375b8631ca58ef7f9d39a455a4a4913b8951494", - "0x026e94b9b4b3fd407c2c39f8de81e0d0ecbdc0fc82c9ee8675e1a211ab24dc2f", - "0x60b20d6f87e1f3005df1b1bd5fb68ffe20f1fa54f21843ba5510c8169b917d72", - "0x8fa31f4aa8e666c9bd9d42395e08260525bf5ad2ff5719071ca84bdf6b671740", - "0x3e1e9d1a96c083b9e8c07e1ce163a13e5917db9731b6e29f51e2571fb4a8aadd", - "0x36645bfddd95120b6fbcdc9ad54ef1b1ffc8d0a3ab45851b692351ce2327d22d", - "0xc4589b30dc638fd5699e5e26300aac149843985846cec28590df72ab210c7145", - "0x6a050a8820f15c057644330083fa0b93deaf677a5569caeda70bc641051f9a38", - "0x9e2adb3df7bbb722d9ae61237166ba29ec84075f1e1c0b69077119222326803f", - "0x2e41470d47f517c606fe7cd40eb625fa20e07688e40cb1f9caddd80f27877547", - "0x248391e2db6d16b0d1fff77cfaafa82dd191e83ec7991c9a594a78b15b30183c", - "0x484e31e2b718d4803e71ce2223d1d24f1ea1e882ef0a01cc7704d6b306912e3b", - "0x0a14dc947165f37c01a8e225df6c0449347d987533f3aadcb25abe8066a31f5c", - "0x822ea0893ce74adf36d3138df4a6916e27d622abc1cb055ea8c82ddeb6f1b6c3", - "0x72ef874be609700ae58ae6706b6f4d6aadd5bd3fbfa5e017ac88125250ced8f0", - "0xf7e54a1ed7a15b3c5d99f09c5748d9e0944d93e88c7dbff78d33a9f0174f7856", - "0x5ae68f0dc97744f54a5e4aa402beaa4af17f8dedc81e5e98da566d802f6251df", - "0xa378c978831cf0853bdcd75e74218d0401eba6e8879a280d0f6552b7d4681c21", - "0x5229296eb8b7f594144a34635c87f1bf97f070249800af63827b8c3efc0d8426", - "0x3cecae54847f7732bd075cc4546c97d38963f633aeab68cac58a7d5a3177e6eb", - "0x7c14a4b4bd76b14f95a3cc9c046d609902090d7f15c694be9651ad437ac52683", - "0xb3f4b86defe35acb24a651602124c0595702de42fb3eed8d1366196e6c16402d", - "0xe50e7c9bf26de635ae23f434127b15f7640694988221b210969978a3fe8601e1", - "0xdd2fb9e00863ee8bd9e7f9b1f797aaa2127e66aa8d4cf6feeb7936fa3d46acbb", - "0xc543bb209f2682fbdefa8dacf51720954f37734dc3b1a34af1adfb6f0476bec1", - "0xd377c42d4e61fecde1ef150b4f2fd6fdbab18fc3c13bd6d3cda845e69516d3cb", - "0x622b34f8f3a6e6cdc035ae3359530551437c1e04aaa48c85d3da759b3afc7325", - "0x3e56eb277800889b4ca4a12a20eb33e17c78736636ed761882682ec91468de47", - "0x9c704ca0434b69c8c5731c139bb15f7fad0ac71b8c6863d5f85181e98d08a224", - "0x68499ddc9a08d967055b2e2faa974c93ec7da5d362912f7a164e8a022787f8bc", - "0xd2bb02824aba917a5ba22255e64f8576c72befd94daa60b7aad3065babbcebc8", - "0x8f6cb9bc8007964e4beca887943dc2ce64bd4bec91afb62d9d4449c5705c7086", - "0xd553a7e7d878b1747b87e6306b3a822167a945bc63ce650c57e194d8cd41beb2", - "0xc62ac1e135b490551bc99c70ca634bc3e47502b4be25d7a0e406cb26ba03ca18", - "0x4327232514afa77b8fe208a7a2094051b78b8c16b8eaf9da6dc30403c4c9896d", - "0x6b888ffa7a69d53fa6f70a9b083f7b49de19f680e29f838309aeec7f1821b00e", - "0xd3019c640f0413a1b787e99cbdf7f9d45f9a2c0afcce3288402cc797ce4f06f3", - "0x1baa3e5575457458859b2f2fc0f2bb09ae3f593787c62f1479e84842f69de91e", - "0xad1b8e08cf8cbf3d717e6766123974cf2450dcc0bf746133d71c2f15077eb8d1", - "0x817174986957d2d04f538fe5ca307d1f524a69c80cf97831002d1ddfe56bcb5f", - "0xaa2892eb967f4e588dfc6b6634e5b2aa88263f4b1e1e85998bea7f54f5015664", - "0x7bcaef11f9680e02b0008fc057819f06a1d117e318a43aee196dcdbc6b991dce", - "0xe56ad921fe297cdf000f04756cefabd75d309add549622cf3a7604d0d02d4d1f", - "0x988d447115c47d44f9d7abfdb380fe8927bce830a37b9be320d5154db934df2b", - "0x431001a2fc696bdc0e682f591434dde3344d994232191931c0a6e5d2e5498e90", - "0x6ed8c0e4597fca7c1c74ac524dedfb830714330b020dc1d1b2aea51bc311c4e4", - "0x8fb32fdb2670a4796df796379fb142e6ed6fa2e5f1fd51fb582a60befaf7df55", - "0xfcb88679cc8b85dbdb25dfc1b9f0a4602aa0d787e9e70ebd0a5728ff2e3b9572", - "0x00d82f45bf1088446a2706e923d8f43c800071c8f988239cfce7ad6a8c31b6dd", - "0xdf684190b2ce730df2b1cd78563dde0cab5539b30d0a06247d79f3aec73d617e", - "0xb0f93511970266249814b3073424d5ecf9b47edf7133009deacd317435940676", - "0x073ec7ff65ee722dcc829b69e80791a856a2200560f992316be6bdf497b5eac4", - "0x2d2a90d480df14e521db3dd380b470c29232e44e12838b54b51216a95659733c", - "0xd72e78f576726e546906e0e936baa1fc6625777edebf69da76c489b9ac057029", - "0x03ff5273dc2b190a1f52fa97e28945dda7b88c6d866551c9928a73d6ffb3f133", - "0xe23267e18401d49d5c2d130d91757d17d7dc074d055665fb33d4778559504f17", - "0x89fd9be047d599e128f9ab20b96fc506ddb8c30dd5a9b6782b05f18165f939a4", - "0x732b8ef837394ff6a580c3c21e72a72a2976fa7ee07a7152e0ba5101d5891f7c", - "0xe9999336ab64957ddaea70edad6e72a29b37bec1498deabc5d8341a93ee67197", - "0x49f60577477613545b37efb5317d9142d9f5d6d081908e1c061f6be452216b73", - "0x6fff08789f48c852189fcfb0327acce4e20281ae54c3b821790b2856c7ec1ff3", - "0x447f89ebe76365d8e308034e90241562bde8834dc1075eac89e28183b2a4769b", - "0x469eb409c46e96cd0e75878cde67974e319f00b0d336bdfedc098021ddc4f407", - "0xdd8365129aa6aa4ce914b5f1a917a573b7ee3a3ced4de5318859e8ea2d512de4", - "0x1ccda602493afe6ec11be1d523d3d76c7e79bb93aaa8dcb298e8bf3b9d5a13bd", - "0xfa3a4de9d5c620eb4d160ca7b49e86d99d109bf7866f2baa3d9c99346174dad9", - "0x7b9fb6a8d32f956fe50bec11e142cbfc320221a89e25ea1f579d76b4b2c9bf1a", - "0x01bacc1068711052a28f3e4f7e9fd80b996fe89a0a2bac647ecdc9d9b83e8b1e", - "0xd24d7c8b06dbf1db024c250d7ac4c8d2bfc8be39a0e8990de46b0ddd79aa4cec", - "0x422fd18a2b8735f348c092ca2bda2b69c676613b595f3906944f869e28070d23", - "0x938a51069120a3b4f3081fe083d9246d32b811d9f2e37a0fe6e1cef0e7ff4792", - "0xa9116e660bfc36372df2d1b87c309781403b48116c95f2f73669048bebddf81b", - "0x858728b26fcc100ae1a2bf373e2cac05537fc347790a9169710974efea7be830", - "0xe17c0bafaf4a96427becb15242bed29534970937cebbf3b6759bf470a9884b4e", - "0xe40f175756e1815e0d514af34a8da49518173c7f0b9fe1ef24b1a46d2aeb1ee9", - "0xc29562af361c92883d4ce12f04170e189eb62f473c2a24ccef7145a26e1de030", - "0xf8d3f3f63b252b8bd0a7ec90d5694c36cc1472c261bd45382bb485d11ede5a56", - "0xfcf7d3ab6cc4ea23882638e9de6531459ee27a3140ab491f756c723a52378ab4", - "0xe74cf40070946b562555a93e868313d46613164c5e23236851c8c35a7258947c", - "0xa79ccf4a86e8a34faa518079edabfc8884c52aa41571c45e05b171acb2c53f15", - "0xbcbc83dab8dbcf748bcad7f14c08340ca4f07318a09c9a32399f47dab5e60a47", - "0x51dd90b2af2f1766134ae9b2a82d885a4a194bce1213369ebf3e315d03ebaa73", - "0xcf18e42db84f9501927d969e9a7e4b42553473c63c3995d8e761b76f79c6d3f4", - "0x95d14b250f97721031748210fb7845f4ec78e05804a87e1ddd1269a220758643", - "0x1c73593545255b2bea59520ee29d30291b4731112d39f507ccb8c98252a8d39e", - "0xf6d582117cabecb8408ae4e9520369d72dbca7eaab8ce6842d2febb5a1b22354", - "0xa2c0b519f6b635f06e64f697948248e57eddcdc5919be502ec1a53e7501297b6", - "0xf2268cc03cba0244623a414fc7550f868157deef46a8c424bc1e6f4b70595975", - "0x16f40825d69784d4299039d7b361f075d1336f29b93947e57ae963f119de68aa", - "0x7d63f07c24dc55307dcfc3160433e7d382272ea411b931221ae30dcebc3068c2", - "0xe469c2b1e0a2370b5a9fd6db791e0ffc5bed870df922684706fc6dd425176c02", - "0x13c52a53e3d7d5524f163d70de3be5dc4a5c8bc95c64041aa21fbd14fc0bddb4", - "0x094a9ed475b0cf2c3d845cfbed5104adbb97b8e233c860470bd9c2f967fafc81", - "0x15f1e7ff285656c4b445af048f4c95224345c4879ebe4c753f3f7b3ba4e625c1", - "0x58128af04dd68678ec8a9cdc54183f495fea8353a89fb304ecb674d1312dbc2e", - "0x32081afac331a70b6174ec52c776de2ec27369136dee0a25b58985a4607a7935", - "0x7f48dc400b525c33b2ea17d47260032965a03b6d076ab4d02cebb7b571d82549", - "0x3321b772ad4ffbb3466734db9ab81e4aa749916ce8ae56349698dcc0834dfa1b", - "0x79846e34b081b22517d5514f26bd67190812501c430042b28e510d5fea386442", - "0x8fa6d5ca4cd3b175ba6fee940a941bd149b050c46a0661ac52774652fbef045d", - "0x506db869b12f2c0b153f314477ecaffa9b7c53df20d5b2dbcdcd073a3c76141b", - "0xe69339d7a63e25eac8dcdd9a70c621561bf32ccc1f89624e235819d39cfe5ca4", - "0xd506504ddbcfd1f91ec9d5d71fb5eb6c05566da7c4631f58cb99d20e7bc1d846", - "0xf20c78eb7a7560c9933d6a583ea23f403e6cb9176e0fc9807752633fce2bff7b", - "0x16392a1f31ba9f84eab7172705e01567853808f17748c87d3d2b112bd297696c", - "0xf55d43b56a371be8e174d3f617d662e7b45eb1326c8cc878409180cb516abb00", - "0xf9df7d04c863b1aa10e03c68dee16d0e17f7e514de9847a20a63affc6e1211ba", - "0xef814a0a1681df3972a6b20187ffbf58ca58f9fea6df0de32c1036723164d9fb", - "0xf8b49fffd0ffb70ff19c3c3781ab43e121f90c447d73ce242dd0a0e2bce56336", - "0xa6e89a02913d0e909104b67e7422339d3c93a564b97171db0260c3a8540be2ab", - "0x5216d9be46b9697a34181ca7b0d4627ad99311fe84c8fbf1da4e804bdd91d39e", - "0xd30d40124a55222fd69799407da38503c04f742b45f0127fc273be3fd5cb35ee", - "0xcf121f66227509e3829949153738f6cd1eba7210a4f4b960510a86d999cb9118", - "0x2e1f8a60fc9427a0325fd8150a8ea6fdcde91a13b40e999400c586c672798a49", - "0xb014af1c33675b75ff8c965b0f7f8eeb32320e42ba14db790a5c60896d774282", - "0xbc4867d1c344c81c33554abdf3384b544ee616f5909143ddb6b2bf4a0ba3d41d", - "0x0079e37734bb661b5bed93acfaa18179fda2617c4c37bedcee6654f79f0b2d9a", - "0x2d3cc57aff3730fcfb2e5552376cdff7cbd452842e5256dbe115aa3b1ad18dee", - "0x15288da0a82c6d0146bd29b8d31aa31b6f516f9997156ce91fdb367399e5b81d", - "0xb344ac87a84a52e90de58bab979ec4a2e5fd935836405880dd1151e5683085e4", - "0xde8156dd2e7abb20044a23844a06ddc1590c9ac6732923c21269dabc36bb246a", - "0x6580171454e664a67c320e63ffe7b950df9173af211983c730f40888db58643a", - "0x5ad58caa9bb6f379dc30ef06b339d724a91000acc92a15486c86b2271396bb3e", - "0x0479a5fdaf588b170bc2d8c11301a463448249c848a4f2ecd39cdd5f6fd94a09", - "0xed9335181e2fefe38459807029ae5614e98d1ed80fc5bcad68046d63fa7bd965", - "0xacef0d3fad5cc33c98daff6f5580a7e843c5445007b3edd4508372f221992901", - "0xa147d774480b83904768d78b654f01de94c50d54c2112900b23cc18df088c14e", - "0x620ce3590e2996f10089121597a6c05f2f8ccc94dde95fda12a194492f4a96a0", - "0x606815f9dcd9fc31a2485fd5bb82ab9c4e9edf31c7e31770e89365d643f32a71", - "0x0a8b6fc63adbc84796423326834fb9876a91b585300310964119019182d420b1", - "0xded997869b600c4fcc61119595da1b8574027d64f9c678f442e9f7a33ac90fef", - "0x448ee745b1cab75979a3ac3f99e941ec291c921af8f65f10cec4707fab2aa97c", - "0xa3c7a6580857935652f73fdfdffc5a315020a22a778e584e0b78eddd70a6f59c", - "0xd56555316bb971bcd4ed2dc26b53a667e0def14eec160230b3bc18cd03a3c65a", - "0x030f8abafc222277b1c9e39e2b75ca6c09afeda33517d8cb5fbba73af7e1e1de", - "0xce35fea1dbc514f8d540a72295e2457863d7ae2afb76ee3bf1cfae1a8ef91fcd", - "0x8d890590196c7af780fdd854989e2c776d472a6e04c82b4fc3c3bd3e30d01aa1", - "0x7fbdddf9f6d14fbd3e6217b184e9b43450ac3109da621c919a499db6575dd34a", - "0x41e0a2e9ca2781a6722b1ea5b302e1f01d1b34c80c6f494e248c030f375ba1ec", - "0x7991a805d4f1237190f5b28a55fa603dd5c7a7cf3325024bd2eaad981ca0d70b", - "0x27537dd0a5d7084451cedb82bbba192fc59dfb60bee59bfecb5f26c49019396f", - "0xd3e67cc97c90c09f19b528908d9a60fef68fde3335c041434960405210ccf860", - "0x68b51ea61f57127d3a75b46e44c97d588904bfb880f5774e21440b902c1b7831", - "0xce0b41fa28c47f7a420ea5efe6ec77e413f22b6f67e9fd8f6728ddd73e5c6baa", - "0x9e7b2c49c92a224a444f4f91221ac2173993decf40b21118ff5962dea3969fd1", - "0x6a611eaca85f609f1f79bfce34df97fb975f450e87e1f24d9a93b59ee0b0083f", - "0x70ec7e2b800c43d093f7b1dec60e6743244b4516394ff861e8fa9f265efdd28d", - "0x7e80326c6eaf085eb48567fe017f21354bf202072b1a2d7a94e9d76d0dde38aa", - "0xea7baad3d12481364a0cd85ca78d71e3d86738938c4073a90d9e5b71162dba6f", - "0x4e3b7094fdeead9a0275de1aaa590fa5d45fc57d164709ca56f209f1285c3707", - "0x12e56845b0e6206f75fbc6fe75fb28167fcbebd2571fbfa2373707af352aff2a", - "0xc7dc260bf1335f5002e7351b0ecd446379c71520e9d0f086dae2eeb37301b644", - "0x50f25018c84c40c0b854dc197a82e15fd95ff58bcae5226aff17ddf9f44f1e2d", - "0x0c71edfd14a52a3c18f56c0e8e94bb8a6b78a1e50736293983d8539bf02b0914", - "0x9f914ff32cd2535bf2f0e430a6d4f85a144480049d29717c0a28d0c8ed02858c", - "0x97c1ebee13b480651350851b8438fe7d709ca397985c6fb392c6e56102ac926f", - "0xd75ee7dddc445d9f6361f2eb079a20a0d0a0ee9e7356c219647081f3d778ea60", - "0xbbc2f0f7dcb1bdcc0390c75d85e37e1b393b8cf7529ce79fdca968ad2b330691", - "0x0eea82ff391f47f7df4e04e0307621e1d8b6aa976995f6eba3cc95253c9aca29", - "0xeabe8946289ac1a562ef5ee5c553129356a4fce4e3c108dd723678bda915e4a5", - "0x4e3d7341bf8f7269481cafacff7dc5146f33d27e5df25a0504a42caa1f94c3f5", - "0x4681710108d0aa64c8d0ea1405431334ff66977169c12c3e6f7cf76479dd5c56", - "0x62cfebf32a022ba6667365f686352407e6129c107cb845105780910c9c405421", - "0x9894915fe24dd0aa6b4b639b17bfc60d9ca0848738f143c6287d33d0cdfd2a1e", - "0x00d0d949ed8c01b85b8b9da7d833668eafe52760d4725c4266347948073b6827", - "0x5ecdc5570106bf27ade344bef187f64bc8591a8d9905e56ae52ae697566c5575", - "0x754ea4f76f4b3ad9c7afdd856dff50eb161214673504b232d4a63a0a42883145", - "0x8641f93482761679fa7397dab41d8a9495a95b9d693c6be903ad2e5517b6278c", - "0x78ae59068cb5011d88567d919a3ed02f05beee15bfeab463f38fc39d7b25aa47", - "0x8f8024181efe6a6709a9957d4895734d4552d63e399d806f31305e50d94c6c16", - "0x40f098cc8a06180c95ac29f82a86d78951bfa1a14cc25e4cd8da929a0871e979", - "0x1c76272e776ef34c5e202b476577ec3ea7613a332dad195afa1340b874648ea3", - "0xc5492ca8fb2784ce1c3ad733137fd1b9d946fc349e06f899530d37fd6628e9f6", - "0x0b7034da8e247df9b12dc65b577f3b75c4cf6ecc557ed3fce1a40f4455da5066", - "0x2d5b70732a72057a6bc6d5d74e2e2fb4854dcc17159faffdbda00611173e50a8", - "0xd92b2ed7c324bd17569cbb18b317aea888e361f9b998b58ec8af5a838f7988a5", - "0x709d9dd243ad51600cebd449527debbeb8230afec92e6061b6c0e6010ff765d2", - "0x277d7db1198e452cc42f7c814c8796e448a8321aa730e6351979f56f64f2aa08", - "0xd1ebf26edc0e6dab23c7a6b9a26a6ac6b5a5eb8c17c355c7b5b6306c35d25634", - "0x7c8d98fef8ae97c2d84b0ba15534656340c776e0e2f303ee6c0b5d72c1be048c", - "0x98d4de7e589c593df79125171b2e02ea165d548fce644563a3ca1f455daca5c7", - "0x4a69319a826ab82fdd111defb028c67f582063335325a680597e4ca972881210", - "0x57f90e73b19a459899a25eac9958508fbb015edbcffa33a8a09ee66ec3ebee3a", - "0x9772aa0cae90e3b96a5a907b6bf975e05e664b6f6bbc98681fc71ad5a3e0b42b", - "0x81272c15403b6bc823541a12502b89a5511c198fa2f2a316a81975c77678f938", - "0x68ab134f9289300446f3dec929a2385d6ebe5e087aecb806181377a911602e70", - "0x135c03ac38c2830f5e43b09541e0f69899e79a83d9ca2d5a97a37fd44f4c1901", - "0x3f579b811867a044bb1837f3591cb92b3cdb806651d87a7a887456338c644f74", - "0x17d3e74034424ee46c93a106f8322ff9fc3f3cd499ee347f0b2e388dad853028", - "0xe6fdee346df741d742eee7112aa685b5addfd907ad08401c9c1b27475f71021c", - "0x1c0ce4a7d444672e9118044e3ac1e141d85c9030f14f62c0bba3f2b409b93ae0", - "0x76548fbb7c106bb0ff13db5c7153710d3bcd4439aed48cee4b68a8e4cc978054", - "0x1723acc5b7d8a7456471c598abc14feba6f3702aa732ffa009036cd9773d7733", - "0xb01c82d4eaa7c5e1cb4ff0b0dda7ad1558418c3576705cbce343b340b03ade5c", - "0x1d0326e10f506633798707287553cd7025246773fe7975c8a19f4ae3e59cb9bd", - "0xed9eb55217788e468d7e5a586429a80822e89fda2d24151882f1716a6491602e", - "0x0b4fc5e070deeec3d5d33ac8c5a9da85e594f03e2550a0d84a7e40571dba6f4f", - "0xa56a7fd51a720aa2c05c3b1d73bdaa9cf8e417381731f25a4d534db59ef5e83e", - "0x44522186801bc1e053bb5dceb8450117b6e16de04a789bba4aaba25580fa9d98", - "0x3b74841d03b2a2cf79b7c4592ac9ae180cab171462b55507f2126a3dd7884851", - "0x8bc3afdea84e0a559bf844f964fff3af35d2b2578c3b666b749d8f7583b0192d", - "0xfb9ab198c8aeee666cd69fe20598722cd187cf349bb5b1db396fab23187d2335", - "0xb56912ae6bbe67f55b1803e2a93728ff2615b65f54f3d88424de4454f6b0a92f", - "0xfc3ec0c31f6999bca315e3f122dcbd1bba7f31dd45b5e936f79231b1e61b4d2e", - "0xca247a4196143fa609f4ef7b5160528b89a14732d33f84748911aeda600e053a", - "0xa48b8477fd330f3432a928b3fbb6c2b16b37dc065703f892b20c86c16459d4fd", - "0xa793b8d41d239f047e1a2b446323f557e89307dca9a48de78f147f62ea078c33", - "0x1f4543fc48bd23e0b043719ac1bb39afea92ddc5774f34e06e0a14a58036534e", - "0x3ed6a22fcbb2344725c7228f2ac4543fb9d3fc835d4ba9d94fb807481557b7d4", - "0x121d1e123ca87801e5336b9de23dfb3c5bd42452adb0c52197136b210ab38520", - "0x4ad96b36710da80dab7128cbcd36bf946824b45322ff10f21ec4037d09104e93", - "0xa30733e12726b6855ff857358cf26191734c811ff91979ce68f3f4bd0e3740a1", - "0xaff3716f450ad70cf575313f3c311edc425140deb4121d744d1a9e366772618d", - "0x9771fd12411da7ec1bdbb3bdd5d9293e1e691f2c9a4a7b705710cda9e6204458", - "0xbd88569eea71d0833810135d6e2898db7acfd85d00c586ff755332e44d2039ad", - "0x214e1662b559cb1efb7a49a97b01778e606789d641025e1e255e7474265963f4", - "0x1b92313a0f069097213fe62b51680d050f6292e5ef9b2f2a4d5217b03d1ea3e7", - "0x88b0faf7930fd989537d1c3e8b1f2f7a6e9cb3b1117eeb5829030f9250277dce", - "0x22c4f4dac7ca24b2d6d524bc3293eef5fd73286e2928bc3554652b75c12111c3", - "0x45af9039d3783f75bec64d71dcbe829bedefe2de5cadb592dd2e9af0585b125c", - "0x60fbced492f778db766f3171c1e346e5a0498086b657da5d17817e05ab194496", - "0x14d6adbb6fda237b95e8f220844759f8f32e976147138c674c3c060466bb9d8d", - "0x1b27c87183f787d614d4e187ce3f1c19b794f933b33a1cf8b8c7f66443968d7a", - "0x08471ad5316cfe0bb68cee4d624d76c3725f7fea983a42c49473a06ccd072175", - "0x45673d37daa58c1a84bf6676386b008b2c80287a648490629d2fbd6fd6095b36", - "0x19e4615dae6265f6bd96aea8614d2212a5d0b1895f2d6fca5e3a61b96a9b4fc6", - "0x9b01ab59ae3b7da27f73c8578036d3295a76f529e7c3460f3e05f8d04f3c204b", - "0x5fe63007389fd90552fc916ccb5bd547c23e15dbc7293d930148874d59400776", - "0xa862a5eed290236b6e87f643cf80890b2d021d68952812765c97fabcb37d4a0a", - "0x38c18afb3ab2908625feb753e4c5f9b0738aa6f92641a6d74cedd742085389ab", - "0x7045a71a34952d008c7a45f774ad095115b2b39c66ac399666875c091f66a080", - "0x4e5df6d0367eb18a6b3a6274d54a85529156ef43e089d01ea270217bd91bc099", - "0x12f5b3943a86799e2601fc3a4a1fb165b113e8b6e30af6631cc96c8b02691338", - "0x75816b7bc3777bc579337dcc1623acc080eaa28a0d269ba12998f8ac28672fd0", - "0x93da08b8deb0256046bc0fcc1f7f320f374d778fa822d9266936ee14b7b605cf", - "0xa7a8837e083b1126b978a647ffc8ac454fb341feb0187b1bb740a311e27d794b", - "0x947942f80495b860e402648969c13b1d67644a610a8655e6331d917ce9d9ae66", - "0x8e59cbbb6225ab2e0a905f26cef3885727fe29435e83edabcc119cdcf01edfa6", - "0x27382765727cbc0df44ae8b56c973e8da67d4fde2e04455d00f299f9731b94a5", - "0x6333067c15a31397bcf1e16dcaad3e9eee2ff3a4ec6fa203575110fbcc9a404b", - "0xb624f0fb2bce352a20bda177528fd445efbca0fac5575dbec526df4b6ba1f967", - "0xe211244c1887ec9267c7455bf64cdc473845df8500a090bc1886d4ef15d94bae", - "0x459b10cb3605a7bbb537b237d7877e5ec16fedfe417ac434214f1e48d6cb9db7", - "0x00c6cb05293954996edad819dadb9dee132c47e0bff4f40883b553bd46465861", - "0x75fdcbbae564cc730b8b29811efe8550d3b2d889f33788c4c9c65ec8420be2b5", - "0x48a6349cdd9065fe2312b7434e5b382864b71a3a30f6bef527fb767641033024", - "0x2d524c91dc1e4c0bc7af72af23a23dea03023f4ac88d5360d39fde979d8c7c12", - "0xdc785ecde7cb892d935cddc32f3d2e803c36a1349e7c7303a063813ef1a29e6e", - "0xc918b72e98f95d38f7dcdd74c222582bb8647d54ac05e607ec71a5230007b046", - "0x201c1d511a764472724c906c15d754584dee39b682104876e916b83662409039", - "0x8524adc2f89a500dcbb273fdc8338800e4a12df36880dbf004e85d6dee27d0ed", - "0x19ff16cb234e3f3a251b57a572e05025c5f469d40580f73adeb7997774678e5a", - "0xad697abefff1cf702f041a4ff551ec2dea735e8614143553dabe82232eb1fe23", - "0x2f82cb8f448965874012ebe4291b98c9c2069237f35dcc86fd44bf3665ce8582", - "0x5204cdefbf66a6ca03f3095785754349ac03d148cb260040c30dcd245358e69a", - "0xd9678cf8987d88e40c7dc0fd369c10aa0149d78d8795f2180c0b6261847f6d68", - "0xb83cdf2e9cb8ad90c17d04d9a7645c6a3d63892c500354c95266d6e727d33748", - "0x3c448dbe297ec1e8bab5481f381d0e81700970e64ee3148b80f255e7d3c4431f", - "0xa535dae65ec4ef42aa05c7a110db91d419ef56cb78a528b6e06825488a539bcf", - "0x60660ad6d027f00f2e84df6f8ac7514b834553a3933ec17e7af6be3f4137098e", - "0x558c04aa7d8d828d40d55e1d27cb7371c89b83b5482909f6a490e1f8f12c0e78", - "0xcebd138a8d5eaabc37145801c95f5fb3073f98bf08a4670419561e2d0f798b8c", - "0xe185cfd8b4dfede3bf2eb898cc51436cc506705b8c8df97a0c62312428b9eaa8", - "0x8329c45d28e8384be558cbfe2c9af7d0338d098935081429a2a5323d065a4f6f", - "0x3846ef3ec37b442c2b94abea75141bda5b08fe6a4381c777dc7c15499399aa11", - "0xbfe141ecac9cc63cf17a5e5fc95745fa7808133e1bf8898ec9c06810a29bb84b", - "0x3d81adb143559eed09d410aea093d162cb5e378ac688665cedff53e37362c7cc", - "0x48bbdc92d5236ccd98a8f1803d512495ba412021d02f723fb8f7209a11198cde", - "0x588929461728d5f15fefe68a26218cedaebe9890282cc6a2be9f8170812b3f46", - "0x54b514ccc3cbdb1cd902ae0ed7b5ae0f91ae4118f54ac831b76f8ca8dd009323", - "0x6387311a39c020e1fbd3aa4de7a92b36da549f088043343629ed912a47207ba5", - "0x20c6399f2509cfde9093485d84aae197351bf688ef700f6b75ec43acabd635f5", - "0x6b7db93566738b7a09f004490f02794085c8a49b8b669984fd6492477696df70", - "0x70e080cb818578443f092d2426e76b898b077bd09a83449b1d615e241097944e", - "0xb2af0965e3a22fdc87b46cd70bf9fb69a8111974cf0133f63ff2fd6bc6e41367", - "0xaad34d97ead1e29ebb1bde538691b50690f6860e4ed8d9bb423c395ff0a37456", - "0xe55ad2c514b12c91354b18ae577a229b338c37c620a7cad86f7b4bdccb56da1d", - "0x4f0b9cd3797887659aed4dcef6ecd2f022fbb77b83779abf068c12ceae6f15bf", - "0xe4087d8f1bdef1a7b3a5144cdb5f5d2d553115a4cfe6b72beda5a929eaeec57a", - "0xc3c1aff8ca588e7fa86d2517718d76ee301e7a4fe6f1c2341b02a12d1534fe12", - "0xbf4d3ea7101078738cdd2fbd691b6b60ab7b3dba56b7595f7b6f706178981da0", - "0xd24b2320e7c3401797aebaf1efd0212596788a23507eba9e92b51551e1b2fbb0", - "0x6b14253803767b19ca3cb24a95086c8d7d3eabdc2bc2196be00139f89afd6d80", - "0x3e3781d97b5d8285ea66c68512157a70ed198c3c9fac4e4c797d3b0e878fd19a", - "0x6c1665c4b9b68d0eecda5be2530b142af404e6e6d6dca6802877fbc64d52a16c", - "0xc9a1e9ef2dc8d230c7ffcf46cee9b7b93e4bffcacf3ff9a5baf5103156ee1621", - "0x2c9d18bdcd673d8c3cfbee0b109255a83a83e0a70172676739b55d71519cd91a", - "0xba052c6ec43b5226718ae924a2b2065d52dd42c53bcde249a4fca0ea9e2bf32d", - "0x2183368bf77cf039f4e22ff970ab84f6eef3d69a2a081d2d0b0fdc023346277d", - "0x25347d86261fecbf1aa99b6a1e245bfb7a4d3ca3a1caf87a70328ea86d528c6b", - "0x4506c19fdeff0b1763516120996b864174bdf33cd6ed8f3642cf522d7453897c", - "0xfa0dbce75686d24e02884c7c44ffda3ad81c99d2c2c820533cf0983e6cafa258", - "0xea20b4d719c85b5f0f9bc3581cf0b9db484e5ebae5b9275678710d9a4bf82937", - "0x3871c8de36feba00ac1e0a5fde6770dd4259b934c49db43232372d4bb9ed8c30", - "0x0630cb6a0da32bdf5aed6adf8f75eede08dd8ad4ffc6f662d63c7d4f8700a838", - "0x09f0b8b297163a9689dde20f383dcbf3accf9b9fa266b74240fb4e26e737a13d", - "0xfed679cdce753b97c3d19778ebeb7616f1f308d6cf57143637c23150dbbe1114", - "0x0559b097b7974cc3bcf176f5d4826b4fe47487c9bc461b1fcd89182a5fd739c3", - "0x799d0651b0e9a12c74dce11ec9af3ed15b762a1f8ae35d7516ad73833691e17e", - "0xda8e0a23e9ef595ebe087eb6ddffe63a933a54783ff0238cd232c6345bfe24b3", - "0x454b1c9f7130ed4bc573eff46cec616ddedef3c14a3e06ab0e03b48dd98709eb", - "0x73b0f4158f7b98180f282768776cb1b37d2f70b7a4c923324e20933f289f7c14", - "0x2f8266751554c995018c4497b150a0f76e8a70715c3e33f5745136227a830164", - "0x0a1a9d2252e93028c9bb69cf1fa9ae1090699d960baa15b68f6aef9678788820", - "0x8b13ec3689127f51d5e82862b380a04162880aa1e1950a82fb9115f83bfea27b", - "0xd0c56341ecd38ad5ecc8c24867d64a75747596757afdcbcec26174d92e42ce59", - "0xb75d2027372551198eab90a3c88beb48a61224aa50b3f30a9deae317a02e3dba", - "0x4941ee21957780ceae3e609baa52a94afb7dcd9fd7ff8446fa044d0bd3ca5095", - "0xe99f45bff96e3015cc698a19dd2b7f25611e642fbc4cf814bd391278a3a5fc63", - "0xeb7847c727c6fed9604c19d1cf156d9c1d382eb338d62b600da4fe5a9cb32f9c", - "0x8718e416eeb9975a729671c15e0812de63dd45875ffc1dd2f07d3353e00b78e5", - "0x3c249822618a0f5861a90866d95f758d1af741951acd38575e7e80ffe4fa3d61", - "0xcf53052c009a76eaf5f629c471e71ed0f850a201c216f140646b804f5143c8e3", - "0x68d4ce1820fe96d02aeb10a178ebd16aa6cf5dafb36e6190be20642bdf5156b8", - "0xf45f0d504dcb1d37fdf73a0cbc0a3c1c0def4c47f5f3fb1290db85e86eff05cc", - "0x9786adb9c499160b9c57af19c40d3cd1c8f49544084527f1145db275320109a9", - "0x2de8bf50d3f2bdfc7a69d9d84d8ca0e41cbb9bf193364633eb984dc5777391c1", - "0x7f39b95ce80f4b91541d81741d59357c4f1feadf6beb85968a5baf4eececa375", - "0xda4faa35feca143f1f5dd4503c456a448e2e8fbe0033ef501146698f72ae07a2", - "0x69d03b07980a0256ed44d16c0084c7b72b07ad433b8b77b9c175858a80113f3a", - "0xc4f0286c31e1f69ec8611efbd82798add4247162ca01674dfdab4e89c807403c", - "0xb3d4788c109ced056b7837b1c6a9ebc7d296bbee42df618c2d32b2ba7342c2e4", - "0xff111f1b94658b12baf7e655cb92f713f4166580981207f13a55e55b13fc375e", - "0xcccac759f059e428771fb21ddd4fde11fc4342d6d9554912887c975dee85203e", - "0x88c0f0974ff6b07f53b464123924e2066cfbc07e9b8782bda5e116e175268de4", - "0x7b1a526fd2af68abf236f9ea30e4802eb49c3ed2b9ed983916fe8cc11a3ef08f", - "0x1032304f9e041de697abf23a0b50858008efd95c4e2e6cf24c524b234af758c2", - "0xdfb69e60d16a021b267f15659c72dd566da9bae1775ee003c207b3fbfc3daef1", - "0x4de8251aa14a1058c827391b62e70b1b2a09b4d169982656029ef39f734a73c0", - "0xd04aa0f4fb73f96b1948d9099be3a3ef9f4bb1755007d692f995fd49c344a005", - "0xe8bf94d4615e95aafbde3669413e40670184088cfb023f9fdc128fd482c78d63", - "0x3ba788d9abf5242973b1cb7f8f6e2d5d08093b8a736897e4414e524826643fe7", - "0x16ceff7c5463b0080c62547529b4b96a08dd01d49d3b2527c61ed151097e614a", - "0x0d7d9a889d28c14047b740fe86b68918bcb7f3cd1c29e410ce9ee2cfbdd95764", - "0x2d31827b3af0bc45349bfe388d503215bc882d90bcbf2d35bc100ae95f1cdead", - "0x84b77d4f0b6076c88d985125e73118e892a3d9c41e4ebebdfe16bcb435253fc6", - "0x2a47008833dea7abd3858a580d87106b57355712df26e0cd2ca4436fb6deca34", - "0x44a5f14487bfc776bdaeccf1672040548afe787b1237945aaa4ecaa73ff5a41c", - "0x3ee99634e1f6fef4520538f32fdbf31dbb77e45a88e9ca56e67c45aa9c3461d1", - "0xfc26a8fc353fdfd173571954755d8a9a68157754f1162f4b31f6d8f2b9c86577", - "0xe360df35dfb599811c35572a67e21c6e9fb53ab13c833155e6f320b23ce5984b", - "0xfb9da18c11f56c2c43d8f38d4efaceb681ccac318cce0bbb0823b4f954fafc9a", - "0x0b9b7b663ff8d9d42050aabf2c9d8a1cbc39aae18ec7e1d1e17cd213e89d8998", - "0x3e991008da81b6f383e6e5b7499b5af8ef6f5182e5af863d1d0087c6015d75f5", - "0x74b1a66ef34f589deb51f8f2fd9e1e422c8703ea027c21387cd45e522f4146a3", - "0x4285fcc0deceeaa9bd076fbe956950eb4f3678f2917357fbcb642befd3c5c2a8", - "0x52792742145a34104b19b72655cdeab1b5425476fcd68c9596c265cb7becd9c2", - "0x1a8b6fa728cc7ead440ec82a9ce3aa0e9211d8a9b210eff5009ced5e6fa9d9a7", - "0xc8e72083635887f82299e02d331ac39c61b2749f916a91c1db66943fad9efb75", - "0x587bc54ae5c8944ed2d0dcb8beabc55988dc32c4c69ce67f0c4b187badc3807d", - "0x19629653ee703764df75ccb9e03c4bf73cbaa0a441c95ea1ecf0cad9f63ee997", - "0x9f0d38b7b39838df6ddc474aca1ab5a485bc95dff8cd7ffc9e1001dbf58eff5a", - "0x3973c16d0d93ae934cf37c626545e2d938a28fccf359aba3b6676f7be02331fe", - "0xa61692812f3ee1abf7c3bc4ed63d94cac20b6c29214ba3489e8259a292a986c7", - "0x7ae72f35e70c9b7da0103d34ec208a87b07242eb7f2e62f5c1e3def6dea24f9a", - "0x5b026f1a5cac5375a40db884e19f508b175417e7a2994864e321f12494367f40", - "0xbc1fb39c00f9c4fd425b4322122174e5eafd71457e8391b551712b3237d0534e", - "0x7d7bddabff7dd77f2a4814dc6e78bf75024ad2a399939fcf81a937dc2f2badce", - "0x4c978814039087ac7b5f4c9f648d3f1fcb6c8384763626c0897ff5ef32919057", - "0x13ebc79e9f13a2592fcc8f9133463f504087c1b948ce044e05c62a8804f1b07d", - "0x80bd912a6162263b0d37344089605fa4a947c04f6e7eda2639ae95452296b167", - "0xe882df4079584defee65af2156ea7e2591bdea9fb74f0b088c4d0ee10a05f400", - "0x4db1886f3f468f3ff17599334c8c74d89d454ca9ce8961f91d55d6d582edc522", - "0x086f713234cdade930fccf4d61df5002bd1991b9ba8c5fdc117ab43bde3af22c", - "0x1c90c79b72d152595023e861c5a5c7763c84f2b235d18ccde15885d0c714b085", - "0x1485bc3b230da6cd97dd81db8afa2a0c60f8a4ed73d6d82b86670f7d26f69be1", - "0xb9cd873b3b93734040fa9abf5c867a6cfd9c7a58bd04a9d68949f9d513495cb4", - "0xb3b85ae9c30c9a66783730c1e32b7c120543852e7ef2bbb5827d8a15c537478c", - "0x36452e6b3b7fd4b17476a429ea6708b8c06f0f2786923e2bf68ae52cddc84df0", - "0xb9032a6baa87da1f7b6c48cb00cbf21c0304ca49e9951c08588b9cde7492eb01", - "0x76bbe74500f790556ff66bce48c5456192436ced60355aa5aaa45cd549651a8e", - "0x3b765f68302bc19b2682dc5de0fc5c6e8e82953d62414c74df1274a29cf404b9", - "0x5a797683d466f5a256a20d6141c239d079c3d1b003eb9891c3767b2281273844", - "0xb789ec584bb0811cfd1d5491df32deb73660fa2f64329ddb3d71b4fa76b8bda5", - "0x0b0e69b821f708f5b86e4d75d9dfa9870ec72b82a0ca96d3e68c6d4f671d530e", - "0x506fb874a33e8e80b65bf55fc5efdd23734e86abe150ced7e18c518de3c93b81", - "0xd51363ce9a53449678b83eb8a083a8137b3c87e2e6750f2834887c4d3cec325f", - "0xfd03fc9a8810f5b5066529b8af38de0f404728ddf0f0813aef7c78cd644f4e93", - "0x1f32b92e19891cdb1fa6771ecba7edefbfe97613d1c75b863b9d1def3929a114", - "0xaf209ec861712fda0c95e228cc04c1789e4e432ee93abb08aacd0f2777872370", - "0xdab1a1858b608c092204e1b2000879090d9c356fee44bb2c79d738d24973d36a", - "0x3b8eae3caebf4a011cf24e35d42da5a769b779425b2be793726cfd29a0c25aef", - "0xfe661a177cc0932fc78619d2ca605c2b5cb21cc314aec3d07102977e86da2b26", - "0x81099b456321a56766f425572d86f5140a1c5adac0c325968aee54801c6cbaf7", - "0x9629ebcc5d107a338f4d4d6d90554bd6beb44e009493fc2957d15022331307d9", - "0x411d843c497015655abc8ae017f6c8adbf1ce52adae67d13e3adadd88116fdbd", - "0x458ceb9ef6549feb3a8a6938f5dea17bfdc355609168091df4911fc1595cad53", - "0x2b71649e13089a86dd8c0c1c5081342943d3603ad690e5ead98959c1d686e7eb", - "0xe8b298ddfcf62f30b19d80fa80d99d020b3b09f7130349842b1231b3a70645af", - "0x36c514c85f3caf8d20523a47572d0475c54a9c42a243feef9291dc679b4ff9da", - "0x457a8fa8ae3aea0f7ba861ed2e2b690d38c423c495cce91f0f9dce7488981966", - "0x8d195066df807b88acde7f29a9e9ad3498ab8a90f2de2efeb804a8b1139db3f7", - "0x3d4bdc98bb6dcd363d9cab40816294086b85633999f7fab221d064c35e329b1f", - "0x6a8f2c0cb4cd11cbf2fe39e7c1385dfdebc18db53fdfd553d18c632c46e2dde6", - "0xfa818fa1ebc8848b03890051d1b24bf8d151b494b57d7f744615d36aec0db675", - "0xac2b249ff31fbdb19aca82718353f2535b45a403495b1b9dd468307f60d98c1e", - "0x043e4829c45e250b5f468f5a5ce6da7cb80cc22e8b39cf8032cc86012432cc84", - "0x1e576b0877c05ca0e3bd93f8ac2d5396bcd141401751601015907430408a1527", - "0x08eadfcc05f4c72efa77bfbae1da0be69ab0fb3c35c7eb568ba93d5d9a072354", - "0xaf584d18fed3cd45fa374e9b2b7b9c2c92b1a13d3710d8a9c85e3d8a4e9c87e3", - "0xbba83c31c86df1f18007bf62eeeabc364512d490ac6f4ac2a19d5c94e02892f9", - "0x64ba9a791d21bf9ebc9b7eb5906006ca99c2f8d82533bc575e910c459b6c837a", - "0x1d9e2af1472c96ca43fbbaa293dd7d0934c5604f97cd83f5ecc8d1192205f832", - "0x73b035d933685c94ad6ce8ee9cd330f6de4e58e69a4b67ede772c625d08336fd", - "0x15bd38181e750225222645a6a0a994266f3961dbcf922f4b9094bbe883e7d283", - "0xbcf07b08a9d71b3d35621436d4b07e6b9f54cf250a7c100ebec46b0e85c64cca", - "0x1916e862ac6ac13cd4d536a785cdb5fe9e7900e9cd0116e495516ac7f8eaf9e7", - "0x64667df61f3ea30a8df061dc9c3063f7e53d9d617f7c97351a47ff2bfec22d4f", - "0x854a14169dec6c155afaecbb4d511b6c513be9da27568c0d64ef94b88151e8f7", - "0xdba8470330f3b6190f1fcdbeb6e1bda7705e95ffe539633e4ed39f05b9ef4bd7", - "0xc71a75c73354d8accaab246eafb170be2e740e800d2b8fa269e710c0ae5e0318", - "0x1e50fdc54fddb8b9ec8cc5559dd09537f6062caae18f377b9638aa15572cd74b", - "0x3608775dac141092631d66b6a722b58588dc4217a8b6a33dace5d9beddbec657", - "0xcc4929d85b2d45ef071b8bb74ce6ad9c7a1820c5838615b94b0d8c9b5d0c4488", - "0xeb82d368783b186067be65faba3ed37051b4608f29469dbe746d77171e8c1b5e", - "0xd4c595dbf9937b36d1ff1212431e4d0b7ade86a49dc56b62afe2eb7b8cc3cb39", - "0x8b2b6c343f36af2dc6a30ef3e9954c789f1236cdd9ee970cc34bd126d5e1c8b8", - "0x2bcb0aafc9b29b5c6f7d416dd3aa4ea2b3711c1a551cd9173bf3469cafc6a0e8", - "0xe6f04922427e5d7f4c292b9d8f17128fafef19bf20acbb179402c4b83575d024", - "0x7c8ee3a97999f0d1c2d65d74ca72ecdd592ed0f504d7cfc02a691a2a2ee87ace", - "0xbccdd3bae30bd8a51cf23d218dedcf7d2a2d19fd7f16b3ce2965411d6a8b5000", - "0x7e30ab210475c0c73047e2a19ef5c00de6f88cad38fc45e1c7324b2d9b1b7848", - "0x0f388ae201bbde36fd43522ba45beb8a6aa9c63bffb0c8937ddcf52f1d6a119b", - "0xec6b556c591fe61f349d3b1ef8452b3ed73b7d1dd532372f6fce7595c20453ad", - "0xdef410ea480cc214857c404f3560128abf451a131c8bbdc9ac9c726b09eb876e", - "0x53cf6a57275ab72a3a0c0e7ab573f39de720d254ab2f3898b5d9446c87c84747", - "0xf7c0b80b9a3ddacb729249bc117531902bdb4e0f32b2bb9f289bf2e6da8d40ec", - "0x4f27b8550a1ccc34f235fc0cbd2c8a9ebc23ee63f849541af017a474262ea9ac", - "0xa463469b7e38dd31f077c838bcfd7e0863f8dbfd73e5066eff1a54a4d05f4ce0", - "0xff74aff9fb5807dbe34a595ea457e37412dbca7572b5a44eb5a01fed29a6ba88", - "0x908a052d390b315a0f6a96be93ec83a944a9f0364d3d66408db0fc89646f0c92", - "0xa44a0ad82a8261aebc439dbb855ab79fc16e32b5379a02cd9a35bf7540de7568", - "0xcaa390abae456f7341ad03a6c3f75f5e8ce37ad7b475040fd9da26c1b4759c58", - "0xd27db4849e8d13d09bae2edab4ebb53f4a9e3a482b42aba03a5da8c09f313d61", - "0x64a2804dd657f03b432f511609a7c3f4ccdcb95d7a0795f93ad78ac25f16f6d9", - "0x8df793bf7ea14f04f97402e7e32b38bef2a67338a4b07e2fd9eb56bde2a2bf07", - "0x61af9dff3186bf0683a32e5d9c4f4b19c486c015257fa010fb5a78a13e6ddf5e", - "0x95b7445c6f7a4829ebc26ce2810571cfb0feff98baaf4bb614f119f5f1da28d9", - "0x803ed381cee8191cfab87f046d8a4a52c44430a27af9ed734dc45fe098c66ed5", - "0xa0403d2f502ed8c0276b89d5266e2f4e0a6239808cb597e64273c065676a3d47", - "0x6f873dd4b1c1212fc1d27db1244e7b05b92cba90a2cc7def330662d3dfa883d6", - "0x777f773b7458d3cab3d343d1f5ec39902465aa33d8c93dbea0a7b3d780c3c334", - "0x5105901dbcb4680e2fe9ac03f95d014073ca54513ba9f9e7e83a2de46c0a6bba", - "0x0183af52777466a1777064712f9255965c73fa24b69de42aa6bb259c19beccc3", - "0x938bd32da6b99e0ce404e79790115d9d6678d13ee73e7ca41a6f62d39250f288", - "0x7ce5d07372133ed0b39e39e2d342f6eb307171bf58025f48be599a868308ee6c", - "0xaeab8e20a6a7933d03a240481df80881df64f96363e114e75af8c95ef37840d9", - "0xbd3a5c163974691b00ff0c2fb8a5c99461df825f803f685342fbe8963fa113e5", - "0x11b0bf19a5e7f6431ce978222aa961d6b4807462a7991fc79cdbb730bb0573e1", - "0xe52aba6a911de3310416eaaa6566280f6c092f36a2c5265290a94e5d55116f16", - "0xe1c44c8dcb79e1f4fb5fb104deba26de64d640120e183c5b7e49a1b645743d17", - "0x6ef7c7469ee5f5578cdf12ef9b1392df8268e3cf6730697a8a38cd3794e7a9a4", - "0xb63357d84981d93c270bbc2b930d783098901a529d338683c2af494c59032bc2", - "0x43bf340e40f73e5e6130892e3b9e7febdaa8ab3010d02a2fc2b5be529509fab8", - "0xecb0e609e25033c3ab24565ae07580c0c3433ec51fc1338e9071d1a13de2ee31", - "0x0912295aa7a8ae5b781b6704b0c47ddc4f4a0c91b4ef79038f889070806781e3", - "0x7ded59d270f1dccac585627bb1f3a577b5f507a1e45de94796e0c8d1f861fe63", - "0x48750dc05ec66a4e9d9889ae06c58b36b80cd0b8019b7dacc7c3c441ad428e98", - "0x53b757f762c6b8f1fd2f9e9ff6dff31d3db0bf325cee0d595773f17585ace7b8", - "0xe00cd3d3e7e92feba2b431d7978188dc54feef4effb980c78f18c3646e04749e", - "0xc0327f98dad58853bb02588058fcfeaaca268f379228da1dcd1ab05672a055c9", - "0x8affeb37939d7ec174ea9c6ae6cd32433bade565494346eeb1c221f1559e1643", - "0x78b3c061f2cec2e0324b400933f979f2e8ce920b9c8555b9a5c366b75c3e2e5f", - "0xf84eb7d08007ae4891c274208b6f1ce98be7201078e53493f1ef94b9795345ee", - "0x6b549294005d285ba7375596c060f2c2dd8d42ba18f533a2b836f7e33c1294f8", - "0x2db95fee415c28b95957a54168d542d2659383b602e490f7a76881706f4cddff", - "0x558f76d84c39fd6b226672758172f0fb94599061b00700f7c3e360d1464f2faf", - "0x5ddca9fb2881b9ae4d0e3ff488987b04856e72ab13b227aa90b277ac18e63c4c", - "0xd4a5c132154217e9d026f005fbaf367fccff273091102f04fbd4a7e18fdd2036", - "0x6fcd2319ee9439bcc80b6b43e7e4e7755dff4c43a17602a3749c8cb0b5af899b", - "0x2159d0c84d1bd380f110b144017096f3307cb229bd33b48ad06796fb95c27179", - "0x3ae630c2eab81c778e997ad714e6633d6b71d833c35073853785a53075eed2f9", - "0x45b99759dd456a15b1dc318bb4e8067618f18ff18c54fce71655d14b91a32d9e", - "0x472ee91d3312972f8df1839027818f5cb890dcab810aed0636a868490d0038d1", - "0x4e31c58a256bf19d1e715de52ed0a909fbfa1466292bf00ad759178d4e1745eb", - "0x8de82fa65607d7de5bc24a7d44664db7cce9edd606fb7f63efcf2c1e5ac696a7", - "0x9b9c7b302722ffbe115c3d3e04871ea434485baaaa7132eef6f0b2113bc8e41e", - "0x2e1f2b7dd68827ec0e15e96b67159dc07d5ff796be6f6dae578afc42a3a335a4", - "0x9fe4d2e36e10377b604112c507929a43bcd9929034d06d82a83e060614738ef6", - "0x5ae4fb3f4b6194ff92b565ce3044de3b8f0fa4b6c026b3df743bf5d5f1defedb", - "0xcad14e9d379330699382f121820a82b9465b1f34fe369d596309da29b49d4e0a", - "0xd13c8ad553db419a6eca3a9202f8fbdac08e878a4e7a58300135e65a8f1d9bf1", - "0x5d71aff2a12d4f1d05c7f760d07b417a39eb0eaa72a01333befc6a2eb6b7d72a", - "0x8dd7de9195d2852aeb6812638ba22e73ff5ca0a8ad921c6e924cae1dd5952255", - "0x8f1828b4cdc6c38c112b1ffee7790953112dd2225ec82581a5095e5ae4d71cae", - "0xfeac88ae6c8529e87a55a259f475b7d162d01e8fa5f36c90d4665dd6105b1743", - "0x2e37011bd97c6e8a24e130fdc2c60c39b14ab3eb426a4f654bf3158a19aca88b", - "0xdb59b565de21902c50e2e204374ae1ce487656eb74145c103a86707b45a63eaf", - "0xf75c26a7214acf2d050ff5c7cc8b76e1a90540410b5c8b2bc9edbfe8fb2268e6", - "0x6ca7554f2abfd22951bec80f9d280abb6f060dde4a9a829ff7a0457d67a99edd", - "0x0324bceb8b61fa7092396764d7e1933697806c6d785446e3bbab3fc3be0ab259", - "0xeb4880f177e3e673f8ee04be1451a38bd8a2c0bac681d82a19327ae2d9769d32", - "0xec6a868cd9fba9e4f5b0d4276f44aee71056fbb7f425f717d0ce9d1fa5442ded", - "0xdc12b36d165eae197487ec930e35489545d2867b6fb9f8604d279337b6a8f949", - "0xcf1241b1c9b054df34638e99447bb0359aa01ad13c38650b872f2d727e6f68f6", - "0x713765e9b76c73c2de58c480600a7125972246fdcce2324993cd6bbe49cce67f", - "0x81e096e97dd8bf1d206d0ed41c9feaad24d344323ff74707112ee8fac218994e", - "0x8943c2246d5ae3e8db5fc012e9613642c6b713e5f2a89d00f09fa73246f88d5f", - "0x5b2d0bcbd893fab4e58d4ef698d1e8d3001799b61e758b7711f319e2b8eaa645", - "0xf3e4da2cf4579f52b7e4d632d93b79714487ef179a8f5d5c46af9154efad20fd", - "0xa2348ea2cd7a5a32779e9d292a9428fa475fae790f08e42f7699ef5eb2489188", - "0x62dd923966e02db7d0d27cfdd4aeac081f9827288c8b54d9f19035331a109f53", - "0xaa1aafa8f9d9d3e0668eff761fbfc2657d1e1906c077d93200ec000643c6c272", - "0xbf40cb21561989004434d6d908451d5b63045c89c2ac9b1eb617ec0054dc18be", - "0xdae53edbb07fe84623451da0e25da631ad3465e5bc12ef8fd8323c8a72f57130", - "0x685115b38f307f984c7c90e85d167d62b0ec2c924a0bca5f23b1cac12a8f72fa", - "0x2ad7e8f86c872504d7c2c48b0db141955d770acc84222fd725ffbd2dc4095b1d", - "0x9268b3b175aa6025b959b8fcdb416c39dd339e6f1fde3c427bd1bada36e4384c", - "0x502ee814cecc8454abb95591b0d07ed5170db94af7fda8878b9b4287fd68c9f8", - "0x6b19baf6b7eae36c0b1d2754645ff282fc2905802ca6394ac00bc22eb1582eb9", - "0xecad17ae39f897ab38850cac40480b4fafcafd6624f8fdfcaa69849f3fa101ec", - "0xb0804719b391b4b5f554a49f99df3a9d1c3a9884cefc268dcb27e3821aebe385", - "0x3018401c9a31f97881852c1bd65a964bfd3011659725c849a8de4b5ad8f26490", - "0x48a6b687e62a42dc44ecb56f4187e293d6f87d328b1edf409c8f2fa6568dfddc", - "0x8e3bc1ec926a68e22ed525485186a4d9160a54bd3e80107bb77c09911564effa", - "0xd7a7cce632e1746120476a3271ea09689380eb833d5538310fa0029f9174e0de", - "0x7bae074c51f3b547568d18e85b73fcb9b5f8040ed5f96f3523f53197150517a4", - "0x9ba39f376b9444dab04e0a52e4728ec842a0aa4880d9aa9819de3f0694f46e60", - "0x40a2b84bd3d05d28d51a39deae5f23b4f7370c70ac70d1cc81224eac4939d69f", - "0x2b9f57c8c43284ba929df8f896a966afbdd341f145b6b2b2fa98382950ad915d", - "0xcaf3d2a336cf17c9b2d7116a14e654bcece012b07bc020ac30feb71d7f6cead1", - "0xd3b68cf2337ca26a9c4bf6b6b286dc65bf66641d5e9c241f4c1b147994253ac5", - "0xefc11a5944a8061c87f274515e810fee13f4c350c625a988c27ed276b6c55b6d", - "0x7a93732151f7145059424aa823b24e26341cfa57f612e6de3bddbe23562ae918", - "0x2594b625d0f5ed52425ccda4e7898a8a554300791027af6f3a19239a15868ea8", - "0x1db00091145b1b0830983b5eaa5cd3d0e4ad71c09d1b2dc20c47815bc2de5917", - "0x60563bce11f028691cf78da7326f22a4ab01d980020e61bcf2e4bdb5912b7b1d", - "0xe6515bdf1f22469a4218f54791d12698d1bc555b3e54b04cf46b10effa8ce74c", - "0x990831a56958a6bf131697e2f35ab2a45fa228eb7435c7e65814ba28778d513f", - "0x1b6e4085f0e291a8ee4d7e90158dcf15702b4e6e634d1d3bb5c4bab11bf70068", - "0xd0d4a6061bcab0f8e645ea16b285eee7f2ff84c7765d7543aa318edbced2408d", - "0x2da1a609eb1b572a47f187dd5a1e9f4cb1e1885c841f91ca82137e01a9eb4288", - "0x1293686df427f9ee1c2116000735642b3d09511cf2889dde21e1bee427d8c273", - "0x8a990d66370eaab3a46d9e4fa9b7d0c621020cd3b897d9e5b3e5fea6a6979f3c", - "0x10030534d5a06bce47d72998ad4c042f5e445a505920723d14b1d93a3a23af82", - "0xf29a399e8879386e4c2bd8e873dc8aed612cfa8dfa9e5b56f9c51d4c4d1774ad", - "0xb252062ccbb11c3181338d9912ed0d4dabcfa4d61860c211f2f702a641fe936e", - "0x822c7abd11b80c862bd67d39e16a208c8462936ca86b5cd5dd20c51d35cbfbb1", - "0x6b62aea651f1407f94906db704a364f70d827e0efba981a8515d1f515a46b266", - "0xa57b7661c0471cbd7eb35becdf468622df8338a48a075b722ebc5550bbf6b9ae", - "0xefc593d38afead5ee1fea5d8b41a52bd2a5b5a059774d0b951bb7eadaa41a46a", - "0xa556e684c26e7fd6f902b82ecdf721fc292c35e0d2240c1f362c4861c366c4c6", - "0xc9f274efc308d1e97ba9b59c92735d1ab2a72d033ed02a543dab301610e96e33", - "0xd7b43559126f88c59392fc54e2b416d2a67014abce12ddf61764df989a897bd3", - "0xc860842132de3d8c1d0ba2cdc0a7c2853ee568789f1190738862667e3539a958", - "0x2f330354084635eb507cd54549ab89fecc41886d295b1d8c91efd8ca27fe4f7d", - "0xda6edc97997b9cea1bc90662c6a8180fa03cef6189e09c86f36adb91400abe74", - "0x0d2853b5a3f02ae9e4a1c5a0534a5329d2e877d4d77f60dddb36b84b4fdf7e2f", - "0xf048b5acc9e9191ce0ab8c390bfe03d89cbed91db5b9e7c7452b01bf56bcf5ac", - "0x30fb6f6093bf59794d37ddf850c315dd9491a4cf5df378b4468dca96acf78e77", - "0xa04eeaa9d1d767c0f93553af3a259390a9576e8b6015ffa8f0e4fdf37f41f28e", - "0x02411cd53ac55407e0d31520a8c3274a2f4d1fbb2541bb140f30f25843c76860", - "0x65bc790d632d5fe2446c65325df875ba59af3c3aa7357bbbbf47c7e5f7663a7d", - "0x5f364562fed351d932eff956c3ac489ea51ad2cb12a81bd8db4fe0b76e3cbb92", - "0x104f7070d5da1aca5c8b20a96d36638e4b5f8be4e86be83f0aed7234fcece445", - "0xb2fc7a73d8d859d531e51532928671ea59ae6538d4572fd5c2d76c70920aff7f", - "0x7ebecf446825dd0010fbf47afce8b9c3b3901c839ec46269eb8744bd799699ca", - "0xc64002b5a70f18b7c51ea3d3f5246fe8db16781e146d777aaa12f3b765d108e9", - "0x9d136068c7002d2780abfefcaedc79424d3e89b17718ec369d9e64ab7b63a81f", - "0x7b9819e62e92dcead89329bcb3f1a1d6bd10794d1a22d30c3d0369a264029543", - "0x8a8c9e66f343d09d5b7897c491c851143ca4f337ddcb5c2b1462b150e22c6f47", - "0x9d86faa1a5d355d6071e09c8cca50e2dd7e7dc117c2e4f6c0136a2789e84aa8b", - "0x234e156c10a80422ca1aafd49d614e02b698a0d89a7080f87db5dd5169b419bf", - "0xafbb572b8d9119ba8126fdd5a593663db3a3e6165a60f1f439902a6321a8d243", - "0xcbfeb8af8c93b11eaddb05df2f2a8772da694f9cc16878d3154d0857f17274ae", - "0x7253cf6bd2b4d158c2bd60acf5fda9dff6cbc6468ca9b549efcd824410d2e719", - "0x67fa14af20fe738ec5c9414401149a1f198207969768a5b982454146ef720b85", - "0x6c0ddef53c7d58839c3013f2accb72190b7dcc52fbe5d71f4a20f849dd72725f", - "0x89e77c0dd9fcb4b4d4d10c25903ff5b905f5d64b6bed9a0736e9dc2035ade3eb", - "0x42466344821fc29dcdb5fe7dcbda00b3779736ca90f87ca1802207f61633877b", - "0x6e2303e8ed41e6d39488c57dff8d50c548a7079a26c3fb86d925c0aca5b8f67b", - "0xb8a14969606442bd94048f2b2dc7b87662d801097537f51c8eb8f026c52f1fe9", - "0xd7ee25e0bfd080d89c475942575fa055ce5ae268a6f01b916b399f6ff7e94a39", - "0xaf5e7da13dc0ab08ba5185074657acaa2ce753a20a364f3a97d230c247bc8d1c", - "0x92f9106bc9ccbb4e0b4c0d1b96c5bf347d856884eca91a0bcf29e896789fd9c2", - "0x4bc12bfadacd6af0737646b813b1a4a9c005e97ef4b090bb74b7475b50c85dfe", - "0x91e4ef72d0bdef33e2e2c8acbdd7926182568f559397bfc086b96979e7c4f53b", - "0xb2f57e050021b238e0b47b0b46a641644645db5bbd987e8d957038655d1fa83a", - "0xaf221401f4cfa3db7879237b40c68300060df9f65dbf68bb02694837eaa8af7a", - "0x2dd280e98a0d6bc950b7f3f0bd11a9e74105d4d15463184a3f8c03d40f3193ef", - "0x2538560e5b802a89a021685f994acbdb3181f25d0cae17160aedfc619ca2954b", - "0x133cfa3f5d6a00f7392a6b0c6a97a9ba10ec50b24cfbbe6028995258cbaa065e", - "0xc120807872f07adeb144905ff323ea79880389a2815f8441befbfa82835555b2", - "0xa2c5c5a3cbf6db506dfc13285b07c24ca7e70d9e75d80fe711c3082ba119d77a", - "0x837622b7bbb8bb18b3d42058426ef97e3fbc2842d2b64b339733b52313557562", - "0x29eda2468556699747beef75f015e6772980056cf367b819d05a4822b7d54712", - "0xc6ec29323368dc29a1d4c3c11e945ef7fdb56db78808c4c9688ab5f9eb7f27cd", - "0x44b368ab63e1fe0e0da031a4e6628ed1a4fc69fa4083cc056dd9443400be3326", - "0xf153dbc5fc03a89e80d2808ecd45e9fe6453d112508d32ffc6305d5a577d14d8", - "0x32452c0d51df7a85eac3edec46bbfe376c971ac4df4512d7e050e6fa711373ca", - "0x70cb25f3d60126cf0104a40b2a49b44ef8a42f60b93f929594938ac00647b639", - "0xed63fb33d707b93e45e958d5d5f9d3f0898c3676e43eeca55c788cbb2df3170d", - "0x93ff103a026350b0e9c2b30c36e079145892e8e3756678e8ae4b0065fb6a04ec", - "0xa466cdf6063bfe155ed75c115484ce113227385eff2cdc07dd90405239842f3f", - "0x2883c296d9ce6b1e6e7b4f471e841437603ecbb867570cbc46d86f0f26871600", - "0xea5e5b84183a3a709a51c1c6f3e7b039c4663d7495b9bdcfbd4ecb0a95ea994c", - "0xc0e97318bd3ee2957c09d6acedac77ce6b219608e5dd63ef512aa08d63c3a114", - "0xd3ccc7fcd24076afdf6249f671de402b515a131913df2da17118be47b3720b33", - "0x4f5be5a6edd66ec496207b33cefd00722f3167b9e6f2a44d9613c4c7d6541aba", - "0xfecdd1844517d0523a7b45d7b51769728902b881b7fc308f486e850c870eef5b", - "0x01a22dea1d0e25885e0a03e56489bcaf6747e712ceb5a42c74430fa6ffcc1c21", - "0x06cc521a05ce856dc55041b90dc4bbafffee578c1c315b5cfa4cc2d1ccba891f", - "0xac21d4b08fbf2891e0b3645b870695434fe703101f74e7fcf0d0e1304ce65b54", - "0xd62584c47aa1d8554cc08cf675bed128e541df54900fbfae958877595ad168ff", - "0xd8f1806ddaee8e729218fea1911efc5e663666ffb3acad4a2fda3757700d6d88", - "0x9dab2acfe01506a185276145deeecdb5c8fe0937feacffd40fb25a83e8eecc72", - "0xf0b74c6b1a441fc2ee8b2f25d2f03307164014d42a0784683a9d6cb7d2179064", - "0x7d590f0bcc891e30996adf8583803a9dd1271442c3f0e69502addbd371437767", - "0x3a66601fff95b0aa0d0660c12788ad56d2383cae290ceb2fb9ff41794abbc55a", - "0x36e94b03402f18c689f5234973ce1e626a82aac085dbdd682b51cce21f8c1872", - "0x00abd1d34c7e55f58681866558cb844c11faa55e8cac70ede75811f55341cfde", - "0x9983fc20e63e77ec0680522035b03167403681674ec62293cd6b7fe360c69157", - "0xe98b658fb8b6b7fba7463562f86348bf1e3534bc9148e8559423b3ee5ab68472", - "0x77ea189d3a408a8c5b1792881d93ba7a471f90a976ed334cfbdf865ff94cf20f", - "0xfbe2fb93b3ecb384679870205f2be2f47140f8d832f841ba8653e00d68056c82", - "0x330f5f04314ae57097db5258d861b993cbdc2ca0522421917463b94636a99d1b", - "0xa59cbefffd0e1cff04bd4004d21fbb64c4ca542b98220487ccc79a39a6a0fba8", - "0xe08f21b0f5ab216c1009c904939515d0ef8d8ca80f8c3cd5465944bb6550e728", - "0x37f1db42a7e99ea8b6d7f9ec5bfb39f9613cf79d98345816533b85726c64dab8", - "0x8c226434c5115b7ac0f894d8c4389a1e38a3118fa0993b59a42291286b85bd7e", - "0xa9977dc362eba9d32d13188dffa6cd0178e0b8c29cc24a3c04cd6cb0781fc133", - "0x502a68fcd1c187d662ff6852179d7602b04b5ec3141ffb39841b571bbc6cce25", - "0x09a716a32626ad3443d7f5f6e5fce5145e8b962a531464e36aae6356ae93b15f", - "0x2c165daaf0e8a4433d8bf97091bb573f1a7cbc7e82f2af353af9c580fbffa8c6", - "0xd8b47ed7ed3bb85bf44ac2b5e5916bbb282353f71dea6e172434f05cb1276c4b", - "0x2bff102b5209c88370b2deaf941653c364a90b39a6c3402336b8b3c9b47731a9", - "0xf6011e416be00e243d9b6b57df1bec2458d271b2a8a964292521f6f8bc7355de", - "0x8f4a6d08359aa81c14391d8483bb8e621c24bac1e32b2c5c505d5617487bd06d", - "0xd2f89aa90547e1956491879dd5924a50142d76460532f00423cfe70f36546873", - "0x929b13fbde5275f999f145a273219d6f7f4c25ac29b4eab3f89d68305e7f4341", - "0x1e7e79d55594c3555eb7d90e4b5bcc8b59ecd98848884b81ac5c2d53bebd5cbb", - "0x11c2705c4a89fa9187f63dfc7a8bd4e2c087d6e937e777393f1e2856f6f00104", - "0x294b40af144ac5046bd2a5451d1cc9900858e0e351f3f78b50b2dbc203111600", - "0x26432c2ba81f919c085a4d2326c22bac341c17f456d8a93a1971c0fd741ea79a", - "0xc488baa35e04ed6bf20753fcf856f31a98c00be65d51b73c6a5490d18fb89d71", - "0x915e1768627ebbee21159a0562d8a5df69b05f057fdc8b4bb69bcadaeadf2ae6", - "0x2303b2be6801e3fe8c02c0d0355069240da4f617b47a4ce6bbfaaae56a1e0915", - "0x0e4af9d5e2cab6a62d9203979db08996bd1f111a315049859e2093e83e465262", - "0xe54756890992736382616fceb3999d22ae462b9b5f8b1da072277da2df6b5f4b", - "0xb5aa407d54e2ea651563333d3d330af0cf45a992bf89db0c229962454c4da4cf", - "0x88832a6db2c8c3d899dc59154ca323c08c5eaa8c05d142978490246e7b4ba7ad", - "0xdca9619220c991618eca5cb18b1c14c6fe4167614a8360c9e1737c93a129e714", - "0x3fad9a66462792be9f771b39b6f64e537683a74441b85b53f9c5e6a2fb687100", - "0x6dc5466ae00317d52511d9e153ffa37cb6060639a010c8db25b33aed702b486e", - "0x2a2ca3148598d3106bae23b6f66a24e3e93afeb576d283ba6822220780008e95", - "0x480f699ec76a16530e382c2e8d91c7edd3ec0d393ccdeb28c7d692eb52b0af96", - "0x082d3f5dcff2a41e2fad715d64eef361271648bd976d895b40765089c544b65e", - "0xed14dc5ed74dfdda8b45dfcfe99e8581767cac57505805a4945b79e2110bf92c", - "0x6890d1a6c90f5c1020ea4439957ea54df3cc22f14e22047f63c9a93696210b95", - "0x55ae4e30fc02206a0f389e03c0ef5b1409f8fe34d6a5dbd5487af608e3b82369", - "0xa709e308a7899591337d59ea459098364c1d6638202047583851acc5e6df5017", - "0xaed8371c95bccbe53effffee29f40a3e9656bc95cceefa364f7191415dbe15bd", - "0xa7450bb8e1ff3c7b53bada0cb8ae783e561a3ef74cd3be0d19cb9ac5ae807aea", - "0x9fdebd0ae46437e3421d7f0a95a76f47b34f0e0eda37d69c078ef5b44411c027", - "0xa18a165a98307fb994401171c41e375a4637fcf2beeaec8c18fd24304b068570", - "0x930f3cd1edb90c020c19a6f6a8b86c86981c3ebb8fc19073f09c345fa4b0ea6e", - "0xe4c0549658dbbf4f541c60606c8b85dba190f58e5af0bcfbed101e0ff4a8f455", - "0xcfd5428494534819ee14e002c69987fc54207917e83a2b6e97a1e1b801a5d939", - "0x0cc3a6a2a5de66f64f5491908b5585eb3464fda42c7a58a9100a8cd0ce763b04", - "0xcf635d0b4522c64a9b00902770ae33b618580983bb32b94134a69ca8f6c6258f", - "0x2cf5c41cf911dff328ef15253a7419733a104cf9cd1846af43d5fdcd914b321c", - "0x6d4ea125dbf83b8a9dcba40c4a81673a365ce81dd8eb1fc10510ee7e80a40b1f", - "0x9c68f243fdf1738f9218c30664318278afa8f461c13bf5e1bf30133d3fa6347e", - "0x62aa880648363c1f3b5bbba3e73dc58566870ddbde0911c5e11aca003a13b525", - "0x286a2b6f2e35c2a6900a02d37d82263dc55a3c40d6c40488258af61f8cb8b589", - "0x296136ec9fba18ad43a5435acbc3006e52a182aea1e3eb005f25ceaafde2d0de", - "0x9ebd2127fa6f5f43a03fcad8dea37bde3893f627b48e63b9e2c6e33af811a95a", - "0xe8e58e2b6fe45ba1881eef4ec1e1d954e12793ee68060d50b9c9502009725317", - "0xf831613def863279307f637be60e9608179e9a148557585034d20816ed7d5553", - "0x9ac311437bfcaa1c66e1c32fa18f8964ca9d9ffe3cb3be306b64519b35051e48", - "0x931a4862f94054ec0f96037a225f1c3421ef99d8e4a8f774407cf913273bf4ec", - "0x07dead0442ebccefbadb2859e0f462fe8d3e6677c7fd04b0f35165ceb017c171", - "0xc4afb80e1dcb56ab83d5b20475d2212b6c55ab63fee058a914661657d2b1187e", - "0xe198b8ed96c1b7dcefab5197036f1e5789ea6622dcca2d817e63537b00710b6f", - "0x728b8b91918a059e3f46ec7658bc5948016f76ec200d91ec3a163bad8ad6bfd6", - "0xacdebce23301e49e068c83a73ad8484d8af7a0c28d066e62787542e154b42559", - "0x7f66807da0c2b155126b1d3a69488b10de7f0ca38f6cfe6fa670dce511a1f667", - "0xabf510ce1a016b5b3d4610b3cc5c9dec43a6a66f3555a70e1c16cd451e8894c6", - "0x56ee11eb3233fefdc056827fccbea1ff2be8a3f460038eef68a1c730bc1104cf", - "0xbc4df43d4acd0fc2cea064abc5b5d1754b29073533652448d44c09adc1d32998", - "0xf61b97d2d1e537a6d20438f4729bb59ffc8db6b7375c6e710b4be9e136f1e19d", - "0xf89f04d5cde25522103d1e175d68dfb4955b4519a5c2cda1a041298579174be1", - "0x702c60e25d6a10ac9657553e376f2385cc94737ee292a3ae671ec02af982ad84", - "0x5194589410d5f02de764bba1c25da027861793270c956c56e67e017d89323799", - "0xc448a3b8bb1b6efed04d84f82d63aa0c7ca19d3cbf817efe4db82b897ad6a019", - "0xdee875d8c517db5cf20b745be7ea1d389612e47880657d003c6bfcfdbc8a6303", - "0x0528ce8bc52f4878268be5e00b5028c9af85cf61a6279e75240e2adf7b498ac2", - "0x6cc80f5c62a1510d9cc97ca8b4c6245938ddbefce782635dde0ca2516932fa21", - "0x184f9e305ce4fa0bdb47ad00bc7d10691bfbd7cb13b9c9d4d82c37678e8df080", - "0x69456f4aae64f57b3709d6c6701a1894802a4db96374da6b979b87aeaa5e301e", - "0x60d1bd0acd9013472acc0b8ce0b90a088f35d5c19645146ed8b3c6330ec92052", - "0x09f87c83c6be698b2eff45ffbeef79254e5ff8d3534831c79346e5b837110c1d", - "0xc5fc2dab2c43c27132f31a915423a6d6a30a560e4aa266483ef2ef1ac48d0fba", - "0x87a147b03150b429b0c86569db27ecde32c1bb9a48b1ea4426d8dfb895339673", - "0x1a68fd5e6efa8e5183843fad88d6445fe8b07c01b884f30563ce527e6b10749d", - "0x4123e02f2c2869bfd99f3138aac82bcf1923260ac28f33bc4ff2dfcd54c48e4b", - "0x452345c2de5fb18422b4327a79053cfff33f277f0e3fc69bf2073d0bda4dfb74", - "0x830eb26c402a88c6281b0a48a93ab16a64a8e4244f87d589b471a3ceb4577f6a", - "0x84445f1f8805d911009a3e55ebda5e12ff347e5a34d0028ca44a2a26846ba903", - "0xf91a600ecc8aa47f838a8d877487c15babc10ff97a5be8bbe4c3eb705fb04dc5", - "0x01e749439d413c66340934dce9d4c55179375208069fefbf89aef7c3c7f02bb8", - "0xacfb6917a7365046ccbad1ca8fdaccf8164c216ba8d96cf64af5340ee3aec2c7", - "0xa1c20a4bb21a0a99f3a38818c05a3ba66e8321f24dcc7ab26487810be8211dbc", - "0x7c88ea06f3c8cab9ac0b29376f53fff4b56672a1704520fbe8de0e3ca0bf8acc", - "0xc19e59ef2361963b94bb211b8f6c172670ed83fce3d54b3e00d1a82f292d1d2f", - "0x386ebf9ab1a551ddaf599d9384e70a34a961ec55d5f609c088c6fdbf38add80f", - "0x8a635842c7d38cb2bae11ebe3f6ec09e2d41ef6b7093e4ea8cbf079df97f06e8", - "0x2d12afbf38c0378d287693cc7fd49f3e068d454ac5ea7bc76f6b3e505bb09f27", - "0x33b7eb72362caff0e54cd853549bc4a802345d774856558a8b128e494ec63948", - "0x31eef3a0c2c9f586ad2305d8ed9831619ad6ce2aee9a16800cf4aa048b1d593d", - "0xfbeb707dd62204a0f3a06ef9046fba5b3b88fff3a38b5b08cc3213f5965c69c8", - "0xc8913325245c39d0609c6af407354e9d309c5d582c8957a90c86c944d2f5c6aa", - "0xbedd0ccdb57b01e688c14d6230aa84f5912cf13a72839f654eede63ca05ff52e", - "0x0b0b7435e02cbc3654eabd042c32409f4e178cb60f5e306ba041c985378cc286", - "0x749b3fade2cbe924b3d76f2b9e216c1675e8687bf7ede81b090ea36a4468871a", - "0x9a59125cd56262bf0b6d2393be4ba8f769b9d5a53b644a0700c9905dc4d91c14", - "0xdd2001f6cbcaf2d29a7d022e6f723e4691390f56ed6a599cb58bee74c64c67a7", - "0x05870eb07cf5d1c1013fedd8cbab73b7341e930a0c02e2dfe898d5299981e34d", - "0xef78e2f60d74c818cbf8996913c08a7c5abedb3f0f7ca412737c071cd6a38ccc", - "0x636a6d39d02bc22c139d9e203b1ae8b2dc485cb596641ba6e05929296e87ffc4", - "0x8cd6e1340deed5cf75bb30c3183807b653c7ccca712f379d5f885ef1a4100945", - "0x549d904ec260af5776c241a80c2075e31869b8da7026a078fe158c0b0d646b11", - "0xc73927c3be804cdab32e543e78f38f8e0899d7f0418e0b37af336c2bb37269cd", - "0x62a4d4dc610995d0f08756056ac199c3f0959674eee8d4503067b822e37bd1a5", - "0x4b4a3cb0ac65532d63592978b1afe838cceb7cac04d30a58cd4d73af4b99d2de", - "0x2c5aa96bb3b58a76b4acefd0275f9f7f82418501ac2c2052e0a5f43eaa4eed64", - "0xd6b8f1a8e502a93e14a8177bd750fb7e7974b7527f50bee37690b523dbeb077e", - "0xaf38bbf2941d5ac311f8ab4cec22642049473bb3a8493ecc27cce73dad188797", - "0x778b83f79934655d67e52a38c09410e5b5cc28b26f4f70b84d2bdff5519bcc74", - "0xcc016a4cf3d1f7119704551809e89c1b4f95d1dfc4cb7f95db2c5d2a6ff1036a", - "0x6b904799fc1df35a787bd7b1ce4337697bad98b25b2bbf264a7bc68d452df41c", - "0x915536046dad19310976f8d158795e4237bec7fd5c2d2fc8ab1fd6a3b5b75e43", - "0xc84167f477d317ed1e6b1452cd746d43ed679f4ff2eae84b1676ae99d3bb0fca", - "0x71758a9fb0e66a7a04a076569cd3bb41d5b91cd1cc26b21a773e67524af84e63", - "0x54f60d40ea84652f938d2856533d82a7c0b91091a0ad009f48e599a917b52bb1", - "0x618cec420aeabdbdedb8afba71b0d0408da5095ea3231455b6c817f9a585d437", - "0x57cf387db60cc35262bfa939b0272af7c490720e496b5f8cbbed6b4b26397824", - "0x7c8973551ad57c278a0d8702cfed004dac293e2b9464debdc48fa88be033ba5c", - "0xc80eed1b2bf137a5574d40252d4d02d0e3f87618c79b764407d8009a2b4932e2", - "0x09ab540be725d669810c498557b61a53b343104c5303cccaeb31e518782d64d0", - "0xc996eeca57ce9a7ed79f6db45ecefe1710abc4b3e21dec8f707eb5f7632c9c43", - "0x160af8ed3f5f55bfe0a9129da94ed002277580b297e29cbe23a962c41eed1cf4", - "0xb8935e0d55dad821ba5605607cd6a025d6ff76ac3d9d855fbdb3d000ef864dec", - "0x10f6dcd55318225d362a40859030fed3199ae015791ccf4c4bb343e7cb97822f", - "0x6f3a76c93761f73bd148c57f2bd00670c20bbd19338b7fddbbca65e598b70ab5", - "0x6e99e5531942dde6d881eac1f03c6fd2ba6f9d79b6f88408eca815d84ff48157", - "0xb05ebff5d64b736d36b372da80fe28d6e76b64c74e1ca94f1a859936c7ecfe7b", - "0x815a140ac8538595dd724ab2c31e13d30c03ed3b64d2cbc72871fe402c7c88b8", - "0xf4376c81472e0b3995618f7845887159eec1e929b8a485c2693219f4d5ade069", - "0x30ff47cd807666e5ac48659bbfdcf8883fc1c9ea4e9be8b397dbc969a82f8d95", - "0x74e35254636ae2f72005ec69e62273e9cb7387fbd4234aa58184e11725e2f569", - "0xb4f3de4e9a22e787077a53017e72bcc8ec26a1499c98305dabb61ce6582cbb09", - "0x3a05acc943f2b7d0c93cb4c5fcbbfa3ab9e536a2cda7f1c325fe3748401b4f71", - "0xd75d428c14fcc7ceb929328989235a5fc46f8ed3e0136d81dfc15866756455a1", - "0x20833cf410b3a0626a6c7e07d5fe09bee17b218416725ec3fac716597e1f6576", - "0xed31b1b593289be968e483888fc80e5c360beb66626702f388a0848bf104687a", - "0xf55fdb35314f9b873a70bb207536eb2e22958ff9264f1e088b0dd82bb9358b2a", - "0x701d2b5d8794e244d85366d13fa0382827ccbcf557ae2735bc3f88bcb5c778d9", - "0x0492794944f7d179da85f68eec5ce4575a90b7a6f161ae199373ac4685c6c665", - "0xb1fb246d93a6ca7a0ce6518a4d0cbaab074bf2ada05fad7a6d199cb830bebf8f", - "0x77172f0acbcf0eb3ffc2e314b548210fc74c10826162b2d63195cb984d606623", - "0xc9f684762b2378428ebf3dc8e207fe8d2b33c5a0ae1510ef44e2dda7e5979faa", - "0xeb864bdea464cbc95b1e3a4be62231f91d1230179b7bf61f755abf5f05cbc9a3", - "0x10fb3751407e792cd70fced2271f125d438d9cf3843d9ac6f89681616d0cb1ad", - "0x79f96b934d4b22eb9315230c113fcfe0ee2828133e10b14cf2e9b3a8007dd7ec", - "0xf8c0546c0c5eb3d9ee16295c5e0fb510a0ff3f55cb654313db2d8ec6195ee996", - "0xcdc41342d4045064634291c9bbce68efd7227bc2832b372502fd4907f1957ca3", - "0x7c09a67309e6a0993fe1cabb7528e5cab4ba6a79042b22b3a6cbb10778cdecc3", - "0x7d5c881d1a199b82608d7e27456788164041cb1e5ece5e3b813db550cb402203", - "0x4f7238409440404425f9f60c034696e4713bab4f4e507b76bdb2697d1117ad70", - "0x8e8642cc65aae464b068178c4c2276dc8860b63cdd8f3447ba07a0e7a9b20c71", - "0x59fd79b1b77ddb040cb6bd45f10ec5ebbc7ba421e14dd79755e568774010041f", - "0xc8bfd004b479e0ac8e2e798045343198909a6f5657625028bd28af69de58d9d0", - "0xb550f5483d01e8d32258708d82d463f28658d5155a15da214bb2c748e27365e6", - "0xeb7de7083d7c73f3f443e6d009e97783ecefd77157b830cab2ae7d41a91b0b0c", - "0x6f3145fafbfa23c5667b646b3b5fe12b0fb08e45f09e62e9254efed6ed812880", - "0x66cbab429d401739e655ee55de9dc3ee6223230e38daadb1fee88da03c46197e", - "0x410eb1081b98fc38f2447bcd358a0996bb9660f949b4d9d6a6596aa41de59557", - "0x2fb84f43e9750be8f37cd293742776ec76d95170689710254d7847369626d6c8", - "0xf1d6c7c8a81aeb7611531452bc46b69db1b56a31c1c835ec5b497b6f4aad2b8e", - "0x1449b942575209a4bfcac57d594cba694bf494e98e154142d5423deb193ad2dc", - "0xa14bbe0f97892409b86989212bf815a5024769707bb8fea7479417402a346f05", - "0x1ebaf8a3c224720c6cb4079b8e355411f0181318dff7766faa204d4292e6593f", - "0x70169dab4258b561057c427b3d31de403d87302180db843d3ac6fcece4a8bff6", - "0xeafa1e0854e1d0dedffc92fe173f2333d3a0581199fd3c63ea69356206329d65", - "0x54de1a943017c40959dbfc25e318b0055b0e041cb170b0f34e1fe76f41bf2f15", - "0xb4ce2d897abe390b9b53be9809fbc1dce8d23c2d726dfa1d0e1419d8caf2c6a5", - "0xca872995f8268bb0855fc685e69d590defd10fc6befb61a57ae75255ef92cc23", - "0x3e3606562cdf9f8460c18efeca2d7f3649ac96fcd81d2bbd501e73a01c729f27", - "0xec09b7014e049e25e0749f0a0899a2893605003f128bca3bfbe92a00c9dbbda8", - "0x9421e443b91c741c80138137362f7b30dd9a0db6a815a3b4a799ec12ba6de4c4", - "0x78c95ab7995ca6058f64f7fc4af9575923bfa70a092ad2268f0b2a3a20dcc247", - "0x386b36edeea33668de333683b1ec3d9c665f150aa96cf78be27fdfd8f185479e", - "0xc732d05a6122e07f7566a2a751adf5da1cd1ee23dc0b34c0cfe7cc12f0970c5f", - "0x283d97620ed18ac85ab1d2c613d64df485b6efa26aa6efa71f840240a95cc240", - "0xce779d27aacf6e512f5b52f96507325568f754b31db766cb4aede53c9e1845af", - "0x50bbb90f2a3cf4c479be3e118936246db591f98e37caef06fbee553502b4c364", - "0xcdf95c14c577f913331709c5d044fa536a4afbc06cd8d60ea7b5d9ac463e86e1", - "0x3a7d03a8ed71d0ef15ed70677ece7a2ea32ad1e96ea5c3266a1d9f580efa862e", - "0xda132c0fa137c542b06170fe085b483afdbc97c63b4546079262ed0fe1125c1b", - "0xe4cc4056951325ee41cbb70fb823eccad0f266e032e839a2e3a8e851f0a6c73a", - "0xc301415694835a5bf36e226ffa42d2fa8069dba6be6becbc2a7a6653c295e8a4", - "0x1856c6fbe4494ba46fb87deed5c10e361a83ee73bfbc8f1b604c6fc0301f62db", - "0x1b822bd73acd2fed2f8daf6936ddc8175507f373e701217b645cd6cb2ef2019f", - "0xdaec59b4c520d6b5be4d82b1b1338a5dd55c9df443d697fbab750c337f6986d3", - "0xce6ff73593693f7cda1159724457a2aebf0ca608105a5926fc98afe5010349b1", - "0x53c50cc4da6192ddfa851aa104f92c865ce212a87500098d18100394140ad6ce", - "0xf015a6887a543a0849a128cec761f10f7d94f7be083f739c767c727aa9ed95d1", - "0xa54bdb8a7ce7327aee991715a7d673d403f2248574bbc0ae5aeb9a32b3d9528d", - "0x47d46b4777a303430fa4e45305b31a113acb4aeebf205ca215447f9d27ea401b", - "0x7a9504b64ca570814194b95acb8400357667122b5b1fa4cef386b35e28de2c61", - "0xa63e5964474672c45b8b021b7cc408e30364c163c800fc302f342f119689e022", - "0x988eaa51716d4cb523fdd5fed04dcc195c5fe00a8e4b6135fe70b6f09520bdb0", - "0xfb8a8f98809114b3d433875844e5fe90cf0321b772aaf86863f96e8013b8de7c", - "0xc893ef50496aa272414f6a06af7d53281fbc7265a3e389a42c7c49d32d24fc46", - "0x4956f7747cb6489eeeb92b1c759f54d534397074c9cd017b2f5181187248449f", - "0x4b48dfaa0b7984d4eea56cc40c5130936f98752e1c2d02762b5caab600c39691", - "0x23df05dd92b7e6c024c02fef9cc6aef2ceea2d0a4827de1f87bc7eb4f8c8907c", - "0x47ba2e8a2e5d4fd0bb04be0d76a2d553d4bbfc574d1b5c715393fea03672f4dd", - "0xa8553d8cc2aa6cfa89c96564022be9e89aa5a6b0b98d88fb7976583cb2b4d2ef", - "0xaa3e7684a3a9daeb343a10566c21ed1fccb46f4ac4b80efaa8725127468b7072", - "0x55acb0e629a6b654ba723f330770a4238cee9aa9b98fc4536a7c8bcf6050b9e0", - "0xcf2cc1a66e1d9e2fcd8709c6e695bef0ea95e4d14523d34a544adacaef0bf786", - "0x6d955c9a59acd3c90593b8eb22dcd614a89ec947d554a0fc8323e817cea7b332", - "0xe29b3ce7a0e8325135a33a1127d1db2f17637090712a0b440bcc3a3bd6301186", - "0x47187e16fd16d67eb94e48e2f35eb520191ce412b95c1d18443487887700c516", - "0xe2ea962ec229af7a26687783a7ba8f65490d9aed8f34bdf7aeb978c901f67c1f", - "0xcbbc65451fd646ce67f78593806e3b66379d1a3aed301e6e67df9e3ecfd3cdea", - "0xedc8308273c7ec9b50c8c328c62cda10e4e8505e3f67d134d0961d0fda239747", - "0x57d5dda883a2dc7ef9b36453edcf481d97c06113d7a060131dd39133ded06e6a", - "0x9392ffa1001ca3a85455471b660ee9d89c8d6d421fb4b00d7ef1eaef22747009", - "0x0751d3a58d8bfce5febe6980c821c92f9bf5beb8685358375640f751fc5c8369", - "0x6ad5fcb53f602b55dc728c7850f628ed5c500b65c6b0d22c077fc4df980d9306", - "0xb9f2d92291fede8a3def3f527a8449f6e330d587df1ec3daedc1f42cdcbd6af2", - "0x85eef96182742e4090221c1ac4433c106467e48f8c46ead864e6150d7d50946d", - "0x6e800d685f7f68839978c124f300aedb6c34dd7ccd7b2dcd3144223d0e3db527", - "0x9395edff6ff47843cda1e5cee061f449dcb7b5a0ca07daf9788079aa7c5b2ca3", - "0xa2995f2cd9a79024f92ecfaac2cbb17a9325b3f54480bf2ae5f3d171132780f6", - "0xef907147aaa023015d9b8f3e7414dd4b84e551af0d9f19fb918ad5f632096657", - "0xa44d76467ee5fd7acc90c13bb12740b3fa8a0096ad0be0e3619841e67323c5ba", - "0xaa574a790a0a50cdac6be3819be64c67427f5a891a0029333da60a01680d83f3", - "0x531d9c8a5d68299fd55cfe498922d54af8b119500f7e6bc0c1b66b45234dab1d", - "0x45cc89de803b6eea973d45c26b4b4287d322dae61163ecebdd3db92f8a40be52", - "0xb305c50ca54418553d0a08c2a01908f3d413ee3eafb55eeb4cfe32aaad32597c", - "0x5767e979f2322c81537baf5d300d24e736373eb399c0e7ae989fc0f177c40bcd", - "0xe3646fedcf2aa89d6ab6a6a225cbfcf539317e4c59be2655b46f0d89142d9b18", - "0x9edc1e15d3b2e5eb5f0b60f15e3b39b0be95ddf0bb6e4d18f4c52322b66c7867", - "0x5bf071715890e9f227fc66c705e782add56e39eadd30dc982066fc067ac64fcd", - "0xddb572bdf8c2ff6ce8a0d9b6b2c95cbb33dfa9b96bde4b74812ac016a93770e2", - "0xf0687ab1fb94e49d1e113b1fb0127548d60ca1dcf341a1952d16dec090aa2736", - "0x94308074a4ed0dda1ebaf569b6cd5b4b6f7e77bbc41c737c78462a1ab330e3c1", - "0xa7141bddbfa78144aae11810acf034e48bffc3368c862d4f750112899150936d", - "0x1560476607eb8c091ffbc0ec21cb3091c3385f9c83f8d27c4a301ac200a92820", - "0x8c0b77737383dde0abf21d1350f0d2c285344720c314c564717ffa034acb1739", - "0x2782b37efd53e9a34ed2742be32de2480de342a384d81bdf3359d3686a3b504f", - "0x6e9848f3a434e019bc209a02ec769dc63e084b9c0f00472c218589dd3c256017", - "0x721d8f1bc957b825d3abe06a6ad9d8bba2e03b915aa779ac42fd9e5bc247411a", - "0xb937b6dbba5291fd35427afb0126ffd47962a07c3bbd6c77e4b80f6bf751c684", - "0xa5461d8dd8d05bf13875c6b2b62c82d371163d2f8ca80c2c0ee4d94061355a7b", - "0x58901227df5457e57c74db16ed72b9b6f9303de046e7adf6fc269db7ee9e0a28", - "0x33cb303cd28fa770e6c9f067831cb1829a8ccce845ac6bdce7d3da5d4ba8f3d1", - "0x3ea819ffcb3839d7a16ce09958849118a801152ecedc92de0ae5154f6c8a5d1f", - "0x001ed88b63827a6c71d7214b42551990e3f313720d982ea5f1049ede4f9d228a", - "0x2e346b0acbeeeb88e08ca6af0f2bcc3669e093475d8c224f23a45d4e2fca462c", - "0xb4ed38878080e2128b706ca1ac4c92c0def02a8ec094d07312068c10f6b144ca", - "0xd0d3c577cf73421791431a6f00452f33fd06f771de9ae2caa32ef2c93bd03951", - "0xb3dc9827308478b197b3995e39aa930d40df8cb98fcb2958c6ff8ece2bbd4c16", - "0x44547163a565de79110c1e42b81151a6e304df14953e648742558efa6f63672f", - "0x487a072363419adfe51ce0b8be352c0f497e5720617e9a237500293459cb7bc0", - "0xe7e9d6bbc5de3d62822c92cec484926d935038fff8be29ff3743008499000d9f", - "0xe151cd789208eeaa791f146f7e7efd46c17e2f8aaf061ba5bdbd7357a32cea39", - "0xf5e2de235cd7ecbc34828c244da80de6c9c452c253e6c876d9843bf5bc665daf", - "0xd0bdde209238ddc6ccd1e06e6e69c2c16b38ebac8b9f3202d46bd6bebacdd893", - "0x5c3cab37f5d83ad8fe9349ba88ead08f36e71f149f10642c819ae53b26ccb0a6", - "0x1d069657a6a467ecb3a781a3d72a8b701faf497e1098fbb9c04ae29820fa4e75", - "0x8fa114b013195af63e697432f3be7017ce8fd6e7ec26f23f832b003e82f17ce0", - "0x2664fa9d45f83ddb5c8a758c6fa3201abe7ea14571c886815585c543f245b301", - "0xd6c3528e946a4461efa56df07020008870ed64fde08cd72e7f8be581207f0d1a", - "0xdc4c0f579f3e44278905dbc69610f7d1025e068f75e5475400d96c97297fe5a3", - "0x06124cac7f9a1c10d48216905db968c9580b3744ff89b37b9b49e7308b04da8a", - "0x5c0c6c98bce9c64b7b62d62ecf02f435ce54b76c58883da177611774c79d5fba", - "0x424553682070092aa2ab3e454c66dac9d6bf6baae6379d91405a47fa48a3f200", - "0x3d09aa437ff840909ec6024c1e407ee7b5622561e4991b76968c146ae1c4976b", - "0x3ef50c81169af169c100f58f3afcb8e2f926d957b2adbaca8787be5d4e8d7233", - "0x8783eaeb56ca2d7fec84e0e272b77271fdfd6c14452a2e1dd83de770c5d99a1a", - "0x861024460895378ba100c5d0c05e62bb6cac8b21ae529ab5cab39eb6c6cabd90", - "0x1c741ed9eda60e5ac585e2f48f06fb988367c2c40a0d8111bb04b260fe44ec6b", - "0x6051d77e0596a911bce132c4bc12be2ae5cf29d113dd52a41b3bc166861149ce", - "0x92c049df5ddb238644015d4e039e169614ed1d926de070952f2407912906cb4b", - "0xa897567fc1ee9437f2876deb3de2b11b8fc00aa07340564031573f0351ec556d", - "0x3e54a8e15218db168960d28369003cdb1a76f8db19384e9e2696ae66a6693d6e", - "0xc5db7ade97cf28f8b61f2c63a0773201ba64f37dadc19c03943b6772aa7a1a50", - "0xda784d1bf64b7efd06558b90cd2436f3e61dc0f7a8370ff92516ed062f461091", - "0xa1d10b0a36ec5169d2df740878d051bf4d38ebc5dc04ae5558daaabc2bfa1471", - "0xcc89a8be2ff74a7bb9e967cfea3cac067aa84cc455a2fdd5449577b52a2b4ff3", - "0xbd23a3e6d3198d81d798c2851c36b954fa6f359bc8fc6e04a0b757e3d0ba053a", - "0x74640f825b9d9f95be69763845aaa0269d3a6ed5aaec88bfd9b5c4139ba7ef41", - "0xc01a29e41af3cc0d0a13ea83f131f3e4828ec3e83dd2fdf9739c139938dfc2b3", - "0x832509e705972acc7efe91475e8d76ac00a12750e194847093825e6c4db9e83b", - "0x63139d1224766ada1318613b9ec5894308efa2473e809d9e37c8305c6965f2cc", - "0x76547e54dc59473093c3fcca1166307cc7d0f4f0e8a35d850507bec216b76476", - "0x3a6a14785272391982cfa690762f5b2aeccc1dc0bb13eab6b9fcfd056f40703a", - "0x603e32b52795c04416d800b6a936343aaaa09898fa97cadc2b157eeaaf3bd6f7", - "0xf241102a3d3f3a9fdc5a1a586b16fdce4280c6c6da04290541eb3cb9c28c7325", - "0x6db6de041bcc7c00104a21bbd487a1e1ddd5e4953f7a503aa992d68a8a7bbc43", - "0x8377d795c55eac07c0acca674e775ed7d8eea35867990c8a776f40965c9ddc68", - "0x48d62d562279641043e405f4d7fbd76050d773103871c5de2c8acc25992db502", - "0xa9ef42d314e15c419537e022753ab46d41318f1fa8784e4363494f395eb6d236", - "0x99572f567eb602a1d9839bd23b41562bb3782eccf9a8893e54b002e685ab378c", - "0xd8cf2fa2291efed46c1a36e1b8837be62e86caacc380aa6397792ae8baf8f3a1", - "0xed2e800df1acb7bba5ee6251592c397a604debd7b0bfc28c8b0002dc40faa8bf", - "0x9ccece195d9e67e318f6d2952bca9486d09f4207c6d8be266cc0eebe41290920", - "0xb20580a5c96c25bb59e1bae6ced3ea5cb69d903f64e648bcb38b799141b3cd5b", - "0x1488647e697452306d2744ca6c709007cf75e2e37da3c7c05006211ba0720824", - "0x009c3dfc5494962c77900fb8da67d7bf2a2f4b855c521b9d50c4aafb1e0735bb", - "0x482428835dfff3ee1da335b36ba3aa1969fa35e89150e5b3c1991f28272d14f5", - "0x6a972044f2076e98833b243c9ed18162d96b46823170ef7c20b1a02d8bbd48c5", - "0x676242effe0fae84110c4933beecfe5ff549b439e54ff5a588add229329e5365", - "0x2441bce77589ebf8019fa8ae870a8529479c6eaa0fed7e0fbd3cc7439dbd4a09", - "0x0b20c25d2c6897c1a8dc9ea1364d3c72d33c97b4d70b9176c3f0a1e3b6ce08a7", - "0x685aa4e279118f8326a90c78e9896e40d9baa62144e2425887dcc704106979c3", - "0xabcab60973f6bc9ec3b596452e7434c4dc89c55c8eea925fc0092d1103c6f86b", - "0xcbf44f106f3f2c0050906b5e344ad22f0e0034067d35402d447311d254516dd0", - "0xcaa67796a8ac69283d7b6304181a988992130ad8441d47b4fdaf236686dc1caa", - "0xad06e6db230bd0bfaa0df59d1ae517ced29d5f11b34f76ef9bb6a73407128b59", - "0x93ef56a4951e4e5c19230918b1219c1f07e9356363503c1410e71486ed338f87", - "0xc6fef02b5bdd4909906c40cf5b999fe9e08e4c0d8bfe59d3c9aa99011136f780", - "0xfed633749700ffdeb0921a537a215ae31c25b85e4f80727376e50c247b4c5a38", - "0xc39d8cc15f4331fb7db2c24ee1163bd164e81ad2ebc43271f841fb25d03835c6", - "0xb13962dcb364ee49e2d0a34dd1a555fa8df363041504ef1e987ce78646d64146", - "0x97e0d3047e2151d53cbd1358da627453558362c6a830910b33f241848b20cffc", - "0x587ea98cbd1da50c0af1986f6ee5e676658c06442e893304708db831fec8e804", - "0x0a1d21212d9bd85a1a39e046c897d1dafb496bfd80762beda2fd3eb1cdc72eb9", - "0x46aad83612f04e7a51fd642de742f713601992e58de4daf24148a3e6f3318aa2", - "0xa65a8a9ed4fb28fcab6ee3af5df4647083c2e735fc652568759fe0426e9a294e", - "0xacd7ed5525cad187f053ba98487cc4abf24f76c8c0e97e71a696d553a3a41b7f", - "0xdcbdcfbaee764bb404bfa5261b5037b9c7ce567a3c1aa9f7280071990320da18", - "0xf195aae79a232b2170a98602efaa2efbdebb3c40d2438e63bf0954e4dc779cb0", - "0xfbb2675a62e2e67baf85e56fcd4cdf2bf89ff7905952155d3cfd4e625fb674d9", - "0x5b955473a35f6b0d24fa8be8009734ecee62f6c4bcf0cafc2335f07c51752fb1", - "0x66f37b268338f4ba1e21eef6884aef245bc36935be1f5eb14ee1d23618f00f5b", - "0xaab809ee86773263043201b83bd445d98a634d8a6da4c389b2336f68381dd481", - "0x509fc38118491458e45c7e8ab1d60c687f50d85fc1c0bf104b531a3b352198ea", - "0x20d1e4f38e83b27b77d55281af40e9f96be098fdbb90730170638c88ab7e435a", - "0xb33711864d62709a98f81d9c5f0a301bd5808d0e8ecef1063c97347af754c8c2", - "0xd69fd6c0fea478bb380b948f5b054f91831cf26d304991d40ebdf0b00a97503c", - "0x87157d452bf57e617ac1dd2372438b0777b83f6087d8223008d823652c634882", - "0x9c54b0172ae0223e6b23f7e000cb6887144e615efb02c74596002dc26d43eb5c", - "0x5b0f87baa8e40f0a2bbc1a76afbe0b21b5e8aae1443f0d38c3ac55c5f942db42", - "0xeb68d93e19860fb9fb76847080edc345972e29ab1ffd417ae5727d3cec79c0eb", - "0xd27026033bba2557c79c4babaf669a399fbc72a2a5cc06c707e24eaacee83bce", - "0x420d887bd82cccac29711c52f4d362b6a7d854e694f8d597d208d0a094fbad8e", - "0x02ff085c6c3c47879a91f511ea4c54a214af8160e07dce8e82a6be9e8299e237", - "0x1f0384e0afaf47ba59aff9f224905950768674c48de0fb0312749b16edb0a347", - "0x55cefaac814e132ff335882a366ea6173bc21fa713e93d8ad92260c84cfd2d85", - "0x58a8dd6e036a05a937a7053be916c0e7f719f2a1905186e7586a9d2dafd5a1a4", - "0x8714d03549461e32a467cefdad60a96788c97172db05c18eb9debf6e6a4d39e8", - "0xd141656c1f57c12feed31dbff3817e1d2af4e1b5cf6aa75d1bb29ea2c0a3ae69", - "0x5ec365177e19fca3c1063e65a9342008aff04ba9d03d53837b598b143504b97a", - "0xc620e23ae73d423bf2628a3de70b1a1f915d80173e0c8d1443a44b91400c5a8a", - "0xc72c2356ed53eae5a4a56bc248d9d2f4e9154f1404780b84781f357cbc7ad2d2", - "0xe60bfe30e5a1a9457ccca65810675e129948b474f391ce64d270200be7ea6beb", - "0xf679887baad8f8e497d60b015156f194b94fc30c6cb1f83fbc4575e99b95a8d5", - "0x654463146799fcfa18a74ffe4f2423fa04c8747c16b789dd24da26d0338d381c", - "0xe8b5406278d9e4622d088976af8b5e6b14cc146a9530c862a42fa5566a247355", - "0x8ecb4735132f769663781f96fb531115190e68390c54e33b250db874e90aebaa", - "0xef13bf38c2ba993c9dea5777e5db348339273d0e6dd1f41867d3b258f24ac4d4", - "0xbbbbeedf7276a857c513f4ebce88e3b531c99cf206eacd1c6c29d3cabab45df4", - "0x89cd50cde2de3ef40de7502241b78e664de53dd4a5e2ed85db62c55be0a4d8a4", - "0x0da2cae061e7dff539c7e39b0b9f63af3217f1a51bc597db957b6a3972cf7186", - "0x57aa87a6daab3c65519de7c1c1360ab33b830d46f169d4e0d3c38e7dadef289b", - "0x85fb1241c4110b4f3a6c197450af8ac47bb24d531219f6cefcc079717b208c84", - "0x52194cfba6bd7d5eb8b438054fbaf5fef387cdb8b1a7ebafe44cdcf4da47b1dd", - "0x7d24eb47a1310f7f4244e825847f634fd4a4224f695a3609c5250dc6052de6d4", - "0x38edeacb93b10653624f77dc05063499daa770b74d6b63ebe656be5a3630b7b8", - "0x4e050f7b9d73c1aea3ce60c8eae8e46b55b6d4c1cd1eae22faf982895871dcd1", - "0xf22b284ed4d97b7d3553600388748721a328052daaf92a58ed5403fd4020a496", - "0xc5fcf858d9a9748045fa0ca1271ba5af780a788c51d693815e0490671be3885b", - "0x2342efdb88226e68173ef84060a0d4dc6c8aa9c9431883beef4a5588f3157fae", - "0xe1599bf452eaacb8dcd51ff835a9ef5761dbce83cfc719813d6a10772ca5fdb3", - "0xb754797393b3216778ea6389361ca5951f365ae4e7ed99ed4cd4c9c76ff442d3", - "0x3fb5f9f3754764155296c6ff4c469109512264c603ece7c78c1231942bb8ac35", - "0xbb347d23c7d703cd2801e2763f1a6c375b5cb2a666ba137c4d6442c3f94688dc", - "0xed3806645b55fd7027dfb7f5f796933049ae558d26ca695a01e1b11333f5e453", - "0xeadb7740432ede4f90c0bc490c15fb377b68de0fc1ee3a56e87e21e7771211fe", - "0xa8c0e907e0b544e7fc3116d47e4cdfc8e8688f5cc4a67cdf600f74be6b79775a", - "0xc6f6b94f2fb4c56066e3c722123b8e85f80ce8baa0427b62c5a2ff937702c481", - "0xfff0b94553a7daaee58a7e15daf9845d1a3ad4917d81d4f23dad27d0262b48ec", - "0xb9084676613e1a063c2b491bef1b984acfbd2dce60a8ed970688239524e31962", - "0x196af717eab2cf09b47db13605ca4864cb0c4189d40c9b618d8a7d3f92831d78", - "0x4fac369653dcfe74d86b7422354d68f7580b1ae0ab359a8b8f8be8582590ea7a", - "0x035ff04f84478354706945480266321d31790f5445028f3e964801fd9a16c78c", - "0x63df70a24370a408bffabbe1c7a4c9b9e40be1cb326ab10d63fe54bb9de50d34", - "0x37b5c558d31128595425ca68deddf5ae7539abc6da838837eb1e0457e092d9ea", - "0x41a9ce82ab27afbc84669368c2e75a15e6386b77034ec316795a896ef9de577d", - "0x08bc6cc18842df4130280823f7676f418f4797d3ddfc544e54267e6456cfac68", - "0xeabc09ffceeb35cc4ec18518d4920bea2f43bf746f23b5524fa405bd874e9d34", - "0x40336744dcfe6f312e17eea83f53538f9999864c41bec43576cfcbef68d12e7d", - "0x3e780dc9c8f2b708527f1eebca75d18507e00e226a00e1b1ddd0b715aa8dd561", - "0xfd6a3c50c4a4d6e1a6fe27fa96f6aa2654573cbb9b839ce8e09a75993e2bf8e5", - "0x166c03d381d6ab94666099024adc95de0ecc9818e5ceb49965767682ca0c73fe", - "0x9805810b802a51a3ae18ce44f6b2c68abfccef2119df2430e4693e291059e222", - "0x353ecd1a0922e819ffdcd634385ebbdb674d247c4fe75e2d5437b659c98424a4", - "0xe75fb8682b706ed6596699d6151db4dcb19f6e71a3b6e34aabc3508c919f5c17", - "0x6e9bab64b10a2341f49e81d862ef3322d3117842e3f1aabc8b774c68484a2a31", - "0x82b5e79ee8d72c3613458c975530bcbba359734a4e9f07015686dfc521230329", - "0x86d48d57ccbe1f1986a4043748b1a0d8d76fd56bc74e7c48c6fd742affa0ee11", - "0x46ee65b9c2fa3e69aa1cd6ba5aaafa7f7aef59224098b60e22994996c927c9c4", - "0xb6983761b177e21899799410dc018f1acd3d417fe35943fbe57207f9f799a100", - "0x594057a8386db6e159d43d136c464c5e3980eae75a73900f7a84ba94803fc6c4", - "0x53c78073c4a4c44d17db85be06f38ab47ecbb7f36ffa87b9db707fd2bc87f391", - "0x4a976673044732e3e8a0987fc8f3c36375e3c4fb3722fcde5259af492ec458c7", - "0x1e2fc8db341a4d9e123ae4ff4f4d8096d8afef47c5d2915c665922bd1de3b00c", - "0x565cd8eb410c6e0b4d67b54d37ff42f6189095965896b0f81566ca502bea34e3", - "0x32d030e4ff6b2f5a560cb7525b5e66ab1f34a1e06531f9b81c48b8a257bd5637", - "0x25a91d756023bb9ae538034bd39b6e698d05fb1393d1328c4fc7e5c14209cce1", - "0x8036f74c4cbaff3bd98820ddd84bc093c95e88d357b341154a3189715225d068", - "0x8bc6bc61f7a57a145b8d728f583e027c8630f0c07e003b189f390ffd11d6f150", - "0xfbeb53fa167d067c9b0a2c0710f7f5931484f7dd90b9456c52c578a15f402d9b", - "0x8d355b208a16b8aa3f7bc5d8864dc7d6a1c4917a97c523274b86e82998d60b63", - "0xc94e45da800d7b56456d55a9aa36ddf9df45e9cfeeacb1116b8c51a0cea34ebd", - "0x81761ec04a8d219aedb2f58aee529e876043b0a476e771957bc03fef9f0780de", - "0x29264094c720151f7448cae053a403aa86fc20649bcf383517e214d1677e893e", - "0x9ac97c7eef9b69dcd73ec7144a0cddfbf0973791beed405202fb4c2d932ec59e", - "0x89611f8e2f9e2e1629f83ec14aaec1656876718c05088e5087887c87b8414c39", - "0x67244fcdba97905472631378fa3a228b649880c2efdd57e5a6c95e9b70ad8456", - "0x0d554cfc4df02560c3e76159d1964c69c39f5df9489bba5516f28a32a4be202e", - "0xce9274a36a0f25a13edef679a5b286bf91a9dc5274354bb6f1ce0ac52557e650", - "0x6017e68689d9f6dca78f42b93a224b445c18b70288a6e4c0d6cc295627dbb1b9", - "0x76791c90f887355878d0a4d8c84ec3990a3159933ecc8d868d196b363153bd5f", - "0xacb6a6c9ec937b3d5cedba26ab6d581fc41cf9a58b0867232e2c8c73d9978cc3", - "0x89f17989ef556a0562c2aa5a2a1d71e5132b89d656bead1ef88ad31073b80cd1", - "0x06efcf8dbadaf28ee719a5b9c017a093fde84a7f4b9966fe3052c0b2fe410ea6", - "0xce16909616f1d97e5853818938b4798030259ddd41e3468f35b940ca901d6817", - "0xd3ff9e9ad14a605a94bdf05dd2639b6fbda28ccf7b2b228f064b0de52410df5b", - "0xb3e8ca9ee88d4c3ce347d82e8f22793ba22b7adc350fd694b1b00b0764c584f9", - "0xe690268a4ec089aced00f9654aa95acb7a8d7270d9428205b103c30a08d142eb", - "0xd685e1460799c51f14273361e31b9739e5212fa538fb8dfbb8e81e8b1d329bbe", - "0x664c293680fb7c5a89ff3c31e81ec8d0c30a6274ef44e4e76bdb9bba83f3c0b3", - "0x44027fd23526685d920d37b032f912159e308286eaac018244006690b4191d4e", - "0x7ea934c3d75a9ecb6a2055dcd5feaf2d4c851eaf360a648d5d87ef40fba2fbd0", - "0xfd97fc801315e5be630ccb3dc983c409a58fc1fc307adc1e4a48fc60c89ea40f", - "0x15aa0c3c732a2c6684d521729dfeb93f62e22e155d85d20e5488e2c86b043142", - "0xba235420ac54100da28cd6f30ff64b8594e73c42f45ca8494fb3d3c4d66651a9", - "0x9948e8489cd94bed4b8e90a8bd35e01ffe38e7c077f587c6c1949caa99cc98e0", - "0xcf66ccdfa85655d7d4c94cffd41f499afdfa2bbddcdaac547223e6ac4d1f9cf1", - "0x7e5382881f710530720b420a3f3ac08211565ecc8fead8ec649cea11f9385c3d", - "0x104576fbb1760c16ce85c3e5757832d53bda83d618500ef677a6a192ff14a5fb", - "0x9e4689bb1ee34635e1106e38ca41833d2dbc1cfacb7635ede5761048a8637c7c", - "0xc8c7f7ac271015da443320f4af650fc71ea0914f4c41252a5b7ec76f329d5268", - "0x46a93ae992001a54119c8d27788e3ef8927dee0a9949b22ece0196a90932c1da", - "0xa69467f9944f1a5e3a46718a99d3cb14930cab6d971baa37bb774cc757e55c2b", - "0x33f7272fdbfb91428a1344df5867300e256fc3cc2e439c777c3feae1cb27b781", - "0x0aaa367f4c7f399edc64ac1754f47aa5c28b0fa208238276de6bd9e424021ce3", - "0xf5f363c3bfa4a23bf221951f4b53a77b27613938babe40f0832d05fdfd252233", - "0xec315af99bdfdcb3cab1f1dcaa5b42ef53f4e3fcf4d921578892a5896fa20e9c", - "0xb580a8e51e875446d7096a20801dded1f7e5b5fac9f47e9361dfc9dd80214013", - "0xb877df38d8f4cebdfb89f26868bdb97ef945da187b44e1cbeafc1d4b7059d713", - "0x78613b9d2d6b639a54ecf1d50a56af80560b436fa632ae636cf354d4a6dd4af8", - "0x80a9d0a5e43558f1d24256baa6940c0074fa84d4b8e7e236054943f9ad5fbe2b", - "0x60f79f699ba1a740c9784f2a8f1b652d4e695ad2d230b110472b95914fd35c8d", - "0xae20de288eb7362a36a1ff236faaed6ddaacf58783d098118bc9fe66b8780651", - "0xcd08003531d6094cabdbe4d971a01b41552784c246bd2a2f749ee9947d1394d6", - "0x676720accf739c380f64748390c1acd2f88d454539866f7326a517c9b629b545", - "0x086b71ac681c0ea369c16b22ca49753b2083ec25b46ba659206433eb060d98c3", - "0x78910ab7d67e67da722ad53b669d8c3a312de3cf362c6254c09581088e920acb", - "0x5bc6e98a830c114cb432091679ac5b3efd25c362d6f99585ce3a027dff95e524", - "0x8d0daff5a97327b615d1535fea44fa33610fd645d93035e1e5e2bb49d4dcef24", - "0xbb46662b884bc6676d98ebf3f2a35ff9190339b72d68520fe40100b4eafaa2a2", - "0x9aa8faaf935c95a60ffae0487844860084a963792ae0bb90a831f825339810ac", - "0xfd77b5d6b6b87bfb0ddcad7b0ed3992e5fe897b16db06b118230b2d292e317e9", - "0xc465a3384c694bc50cbe97ce9f3bc364884651a97a491f7f64e65dc319d1c9f0", - "0xc4634431867d7a302be79e83fb50d01df7f3b950aeede21fcb59b883399b06e4", - "0xfd524c29525cb97a89026ff68048ca6e2a9f522791eadd74447a6c278151d7df", - "0xc7df516c295a58cf4cd5614eee3d2f773a412dcd4926eadad7e935ecae6d8907", - "0xfb915abde0108d6e84354e21a513fa564f5201277e060bb916a9153537fba1f7", - "0x1d3c6a780f1b259e096f4a141ab83cb6bd035407421e2468e743daec211e536f", - "0xb2f47534f060c70f61a7c16f920d0e11b957bb3ef912ed9292f35b8ceda2acea", - "0x03e0ebe6e9992f6921362d463b68f91518d91079c001c6bea7b3452879fdc29c", - "0xd9a7de173a1617ad813a554a56d7c7d2f010ac78d7782e524b35b5c676cb72dc", - "0x90d05d99167e53d34a02c5b66ed6920190370656905465f20efe56499aa0ba6b", - "0x17702606dc895aae35aef034fddf8f7235efcc66e5c9d252347063209c2177b0", - "0x3c416492193d81fc03b5c1964989a314e5ee6d689c638c996f6761b4d7acd6be", - "0x3c6c1162ea9b277f831989ea26e14bb23ce4d72bb9c865e354992559266ceb16", - "0x96de93f849613bb2ffc117bf111d4798b9252649f94f21187da324a3fe363833", - "0x91e50fc6e564cb9d6b7aab3a6e93f6b32944d5a781196a9a8b12ac7f6f527565", - "0xdbefa2bb2ee620d75295d0f3103e06b428f955dba1a792421e435051c46f7933", - "0x78f29df98ef7dce9fe7b4414da90fb4df5d99231ab0a3b7a3e70659986580fe4", - "0x56cf56899c2388d55eb1496ccbe62041d14cf655c9dbc53984d86c22ed281acd", - "0x099f52c675171088550a9e93e1ab17f003190fa3388d956724d422e5925c4813", - "0x9913e4ad8405b8a60fa512fb616c544c6cdc415cb1023aad0669d58cc3810161", - "0xbf5d51369b2510bb57f8fc8e9342890e8bb37049079dc79ab97afc0bcbf3cbf2", - "0x3a012d45d250c818b641fb18b71b622f5bdf0b7a541e0d8de54f61e516ee3ce7", - "0x0233833414d2cff3da0326f7baccf1bd522db5fee290ab4fc0a976934a20358e", - "0x38a0978c955f20cdc32e2013a5373efbfc50924e45e9c4c756291a903f4162b4", - "0x4107f33a14052662a0469ddd646ab6659006df131c4b0f6b0e6cfd331b46fea2", - "0x8074fb5054c755c912bc68b1dc22ae40ba13c06912c8af1c12652eb4d84c6503", - "0xf6d151b8f9c26c3a31366d967dd7338e80e8107b9b81da0a98faf16df9cbc91a", - "0xcebb0256d0a8a4b22d2341ec7c48292c3226caf4aeaa2003ee36dde25cff833b", - "0x5fa9ac499a2642b0cb7ca365062c02588f9c555bcdf584f533ee8e8544b9928d", - "0x800c7f04db30247318b8d4c11d575dca66bf615674fbeb9e8c20f387d907c8e2", - "0xb0a43de06c9d48afefd5411d759e3c6293cbea4a7c6d862b119182ea02af81b7", - "0xb6e7ca0075d28959cf87d716fea885e9e3a0062fc7da1b6e06089c808a632b8a", - "0x734c1b19f0b5972b5215f675cf60c68c12cf6d6bda7b5a95ee9a781482e68365", - "0x1995b08fffb20dedbef592ac23a81d87129ceb396e065265dd4a6cb876beaf09", - "0x051082047a6b579684b5444ce5b75bc630277ec06b0087779387b9d7fcd18fec", - "0x4aabfe145c368e6878e2ccbdfbecf2f1db5c9078650696bb3a584c14fe17177f", - "0x42811ab68b304ce30fe896c52b53d861abc3c8b5e4e740fa97b1695db9a6691f", - "0xd90cdb12ad64f86b2aa7afb781c00301f50206b05f1543b111c2b971ed209c94", - "0x385435507c2ef42b5f1760b97497e8a02a4b5ec4926c3cce8569fc0f4be59ce8", - "0x2d7a4908350c9cf022920cc51e0cad9c3c05d1d14a92d72310b52f984c857101", - "0xceea9c58106f4f806a256f64dc04e1c4b53e6cc5eb048f3df7a14f8de3506e96", - "0x7032f864eb3eae8d198c3f8edd9cc2dfe88b9971cd01b33318dcba004f9b044b", - "0x71bfeb4c183b20fba60e225524c809b0864fa14f5c0137accc36649ed0712e5c", - "0xef0ec5a2761c46827110c20e14fc4aecadc2407541ea046de09a58cda3b2e839", - "0x5e6debf328055c9413fc3eeca28583f917b361a5b5bda9af4306929931a4116a", - "0x1aab81df07eab969189333e5b2930fcc1b88a525ec5bc6af6626fdcb202b8f34", - "0xbdbf97e1558711d4872821b9400e03a811c61096bb838d3126b1c2154f8fb776", - "0x7d8aaee482933ffaa97777af3e4bf69ce7d99afb24e546d2e365d445d3d0190d", - "0x9da421621b14164582b2b877090c9a956f3a7c917031bf743a9ce457b6292369", - "0x050d717f0433a72b17a0e9a1340f26aed5bf17f90c08a5b73e675860ac9c24de", - "0x80551d3ff835aaf987b9ec056a73a3890985ef551431daa9d4aca10c81cac7fd", - "0x625a5b5aed2660d32d2fd8c4d1bfc248365a5cddaf9b5695e3f131629739ec60", - "0x7d86bc2dc5914d16b3d0d882a5db0230b4b688cbd8c81098d2efc5080e589646", - "0xfe42cd832cdffe56426031ba7d837c56d86be72b89ca9f5474bd08db80cfe903", - "0x0ca30e1fd3bf3e16a0e295ecbb442757248b2ad47baf88fc37d6c55901e709f6", - "0x2a83ed111b99844e17fb7aa69854525958255ffea04e0bfdc365264e72b349db", - "0x709779de19590b69864f5b9228b3a1c334724e20be006ef5ae38f8c05eb6f37e", - "0xf09c664d0e2e88ad5418d14481fefdc9e9c46158bc5439ffe0bf6d6d5ecb2eda", - "0xf350785dd3617ef73b0a5bf439ce5c49adca0c041b6b5047a664e5e33967ddf7", - "0x8fcf87571154dc4eb0a73c6ee31cc0db5f4e064cf23a255a408b2f2c7cc9c0e9", - "0x75801fc8867ce7c75b3148c6c022d7702143b93d93c1fc2349e3e969d0179cae", - "0xf14c18bf68ae881d3fb07f631340b00557a83860d0ba0efbfe55fe199176aff6", - "0xbe48c727fb6a32242229eaa09146c76522dcf6bed6d1c6fc1bebf86b5e4ccdb4", - "0x8487b971e383272df82cd812a0bf3a2026b85bc3897b4ce9ce48afa00849fe00", - "0x60d18b465172f59c0d71594b5273a90cb41db24a5d4c9fc37020f9d8c467a4a2", - "0xab4e36d9f17c748c87d89c23b667e3f4e3265e77b62dbd9c92659026f8a53d12", - "0x2c711e8eec1be3caef6e16a03dcca83dac3a565c93327c67f4e8ea9f2697d9e9", - "0x21d7a9a3f22a163767075aa693d82a962e4458b074bd8f3485c4ded1e47c1172", - "0x1509cac83867db4b888107974d5c8547dc4aa1db3b8e886291f0f9eb6bd7af58", - "0xa555eb6001982080479c9bdd3da9e8668bf90ca9538f16f27a45ae698ec85fb7", - "0x1d1f4019fa3ea7ec85ee0a411560738791f2d45fe5fe6242d07267183a852b96", - "0x2a8994caa5a27eb3081cc749cda5694da7ed3fbb8a1b4c67d7e92306cbd3c6ab", - "0x8389736ba114022ea97a6b3e755d75e74c3c370e9eb916c0f2d73a46a6f6c396", - "0x6a2e5d08ea64195a4153fc7bf26b7a99dcc9d31d8f58faa07510a1e87fb1ade4", - "0x78b66b38fd9b9cd3a5bc9c91c6f816153c2c28c1055f7ac9ad12ab61f9464850", - "0xb2f77834f7a88c4354763a28080d95be44dcb380d01f09292e679a6ed274b179", - "0x2070456b2bff0c30ae26b2435af82e153094aeea8102a75541e02a39e9ebe717", - "0x4e3bb2fbdc71602a62f0c423aa45398af09cf3ad24f0437027b4590e7056b882", - "0xe877208750e7569ce78215659ef78c1656e98b63ab6cc3e1381d7581afbea99f", - "0x8c9d8132d01b83cffd1a1fd9d19a3d2fb3a58a0a28602018db44f8b6dd5fe1aa", - "0xcece8a371b72873a660f4077f98b04d6a3cde1a150852db3a194a293e6a08b72", - "0xe72c8c7211acf2d67bf904cb3428c6fd69c8bd679d52deb3b2e749d5a3124b86", - "0x504f53693e747fbba7475dff1aad887ad0124d48cc7892ec488fda56e31e0de5", - "0xddb3ce7ac0c7e2d141df857e9e5c083d66f4a62ead742bf6631756a72fb643e3", - "0xdc1c6a45dd8308ea319671732de7578a65a025852a3cbe88aa9c5d770d662990", - "0xb6a9df772bdb5e7cdff569bb9d3078c081ed53e870f9171a5b0369d84fe6ce50", - "0x1b436a7d8c9af357c8175430e1d0d3057096c92bd8bd24fe285fa55673c68b72", - "0xa57ce7fed7521e26a908ead89253bc57939459b4c34fba583eececd0f9104e2a", - "0xb16c4f6bdce8de6657d049e2e41f91a1cef8623671eacb9ab02fff45cee7c0c3", - "0x8d23f3ea29767da41a2090cfceb56a65236a405031b69e614c78fb79c1847651", - "0x33149d4876fa24dea4e872ad1da120e01b14e8f9f8270611fced328a36df25ca", - "0x266c558486a85de2536e8849d49443e7d39797a6ed817ad7217633c28166bf06", - "0x2073a22a1f157025228f8fc82bdaff48d3cb1c9b004ac2cb867c2cbbe7cafd95", - "0xcf643d70f96101c99cedcc2d169b0196c564e7a8b235ae093ad25b1ba8981d01", - "0x1489101bebc1b60e4c0fa37a94bb946f1b51ba284c667e4b2f3270ef9d264d41", - "0xd2eceef739e2601390d872bdbb58f8d67864b000f7fbab07b0354c44279379e8", - "0xf67a02d7e7e6da127015e5137f90e30ab43c2e828e62c3f8fe68ad5a0a3011fb", - "0x01fe94220a07efc2bde2404c584f67e9b06e47eba05e50e71ad59710d9d6a9fd", - "0x509bc80d1a464116b4f234001c3f2d7aac7772cd6ec5fa6ef9606ab8ab273127", - "0xea497db1233691b3b86d8fc80ebffe28e6863f445a0b352a1286412a94ad2f6f", - "0x80074f0e2cb668b6a8e5b454db4ae5cf2482894f3ccb7648cfa26b3365a0a54b", - "0x56c8cd15e5e1a21c6f32f7211d18e1e56c89da688cf11b80c9e55718514eab24", - "0x6705989758f03a8c4ee93d44f53b4114a10337694197186b62ac2220188a6879", - "0x7581fbe2b0ae6e0a61d073fd1700c3111d2e3e8d9342d50800ba05d026c9edd6", - "0x254548091369ad8e0abbd2eb4e31e2c08c8e9dc0256fd0097e50e75831946924", - "0xac3119a5ba40f303a9a70ecdef9b6d4722c6adb6592a0cf52e5a312a1a0819c2", - "0xbdf30bc3cd852b882c5d410e6d4419e0f36ed17b761b58fe58ded980829d1cf8", - "0xebaeb5965563f6fa71006d8c2d285e093a167630c319cee2dfa961c4a583b60b", - "0x6cd6717885bd50263a97574e8a56a3ccbca88229bce595b38a454c1184ea94d5", - "0xc9bba3a4a72f0f08663e3af36e2bac2d4c8422b2e3c3bd29705e5b86e7c42a20", - "0xcc442230a4b6aed5a1eb2b8744a09aefe3cfb9e477816edfa829dfbd3f7c2bc7", - "0x4ee48471e3326144c7a48b15fa5f39691ad9f24b82430ca281eb961b18019e02", - "0x35016779dd877530f99b1d3372a0a77eff7ae831208d2bdfcd0979b838190acd", - "0xe3e9b2a4adc4b4e167376b33b8bf2a00bd64d52f66f4e7b291ade123e8c04402", - "0x5a7d1466e2538b62ceada9577079248788e912e23c4cbc52d7b82a1afbf28ec7", - "0xb8529c5a2b2dbfb72e70165b9c9291e7c02b43f157092a9b59f3b4cad85fb587", - "0x55e90fb666a950f38ae41732269cb69afca8643b75a99bce508a16e06685fc03", - "0x651f45996ed080d8a8dcb78550089331ad67c2df33330d1871dff956463aed3e", - "0x9c6b4a961663c59dde3a4f0fdf7b68b0b5f049719ba0d10d841855cffc7ee166", - "0x15c4d98e0ee4fa1535f83625a20bf1a491852884f8fe608eae6b9bde45986779", - "0xc7891cf34fcc61935a43f37bd7f23b674c785ef3b2718358fb627364f3d8b09b", - "0x8105fb76c9e1281f0f0d7c520dc6cd1d546d1b4b29cfa398eecd7965af57f408", - "0x2a141f1c4e1f1e7be5d10c444dde3d1b5fe1a69f23506d5ed1842957375a1208", - "0x9d2313d34a5a6873f2ae1e2684db00e3b693ba962796fe7e78d26a2c49104471", - "0x84d0245b1537ec1fae89fd39741d3b6427ceda3933f750befe4e2d8ec022fc1b", - "0x93d281ff6bfefc7f94058a0d87cb826b97728c9593ef4876b88c35d26b2163bf", - "0x9018a352b09cb36311d0b54bd83a33dcd5dca5ae7032f1d399fec3a0e5a9cb7a", - "0xf01486ea17dca0bb45c245c9ba76d20d1a2f4eb718b7d07fb5183736e5cd08c5", - "0xf797fec0a8cddb31ace66223e1355211e41211c4bfd33a416c8a1a86e497f630", - "0xf24aef69daf4e2b90b4f3bdedddbabbae86a7fd1182947da7306c8145f82cc88", - "0xdd14e34cd000ddb6ae6426dcd78cabbf6bfbe5b3ecd7553c4989b3891e506257", - "0xc01ece645c7430801ee26b20ad215e221f7de0e060fe004b450b372a6c4438a9", - "0x64c836653383bcb165207b80ee8f9377ad25ef1bcd6cf7ba166cca78419898ed", - "0x6b1cdfa195df272d7e35ccd843191d5725919c0d8b71f1abf4b950a954770503", - "0xebe51f94a9f445d83180c4f075d944fa2f05b5b784b7e3cb4bc1544020bd35cf", - "0x7007f6b2c04420d390fbdb6a9fc21edd3249c1a098f8a9383131b8be50c6f975", - "0x6b7e93d96f74fc2b068104a017fa5fa74836e8be5599865309361ad9305d9bb9", - "0xcccf2dc238c68464f3b0446e06fc48fedf202e366ace5734cdac5a7fe7c43342", - "0xfec77750c277cd6bb0937d25d7f598d07cbffb1117b6232e932a296f32b45ce5", - "0xb04e6883c11ba3f2b7f0301936c3dcb622f6f78b3d3d57e48c6dcba063038d6e", - "0x23fe83ea2f197cb97a3ce5f2afd75652cae993f30bc627e8b9572a0069ae088b", - "0x44cc6bf2cee513c826ecc0c130a32b61b0153129721ad2a70d4503b71c128247", - "0xb1a11be5f9b37faf1901b1405494af32084641dc02685ec33be7f6e82e360a42", - "0x284bdf9a6aa0ac964b0f5e72d0d47c713c919d72fa9e24f42b65d749efb6e2c3", - "0x96a0a0e92ed87946a322c9bca6c0ebf70e51e0e825e2ede85b975349f9bb2caa", - "0xd73becb6a3d9a155d46a96d17412cf015724a79b166d2961d24125d92e355947", - "0xd87f142746611ce081945999f42b8da49cbda03a10bf953ba043417861e27c77", - "0x4af010485b5385579a534693bd21ef3768c8920c5b5dc6f9ddf722cc68fd6550", - "0x65a2f4ec5cf3b37530fe0672418214618393db47ad1185f285b445cf5f53cf26", - "0x1d208df2a2785599565a40d934eb2c0cc1673c1dd43d11685d698122ecf24d6f", - "0x046b7211427f0027ed4b69b06cb43986d3cd53d1579a2e078f6904a06ee6587d", - "0x053fb3ccc35edc1a1623f8652dd8c239045b02b58ffe7d51337950ae43365990", - "0x0721f82b52ff87272e1b0af5f4dc16b90dfaaaf3ba56b91bef555d283c98a8ae", - "0x26e93e4e2d0df49a857bfcede7e6ac234d75b71617b561ce39c3db130449e4fb", - "0x5ed6d53c35a0aa52d9dd0a244f31be00426292c11676737c7283f567ba485dbc", - "0x047add17bd1601a06a32b721165ecb2afb55290e025a828696f234b95c52eae3", - "0x6f9e6884745cad7091646f7767a6b17ebffb6482ee4726f5c1f3b9e02d26b77f", - "0xab29ed5be2938fb19614da621ea9828eb338514309b3d56efc6f19b57b8f4f6a", - "0xd5f14043479e8e37c6545527b2e93a76763a654661af9a25adae2b37dac40672", - "0xc43635c1489c3cf3aa231419bfcf7bcb2455a812e4781e5947bb76cf0219d1e4", - "0x72117c92f8bab83ccc7fbe5fc6cd931c765238ebd67ad8c4fcf1353eb78c8df2", - "0x5deed7df3559ed75d7c2388a6e583067222684741143350f85d236bc5fb4075f", - "0x655bbc0590587c030902e32d88cc9113fcbdd5c3371820868768abaeacecb79c", - "0x1641c875e8e44b1fa71badcbb4f724a71e2f916d9cd212f09e9039ef41b1242f", - "0x40bacdc3f0b53522cdf2aefafa2bbd9c8e9f8fd25b4b33bc19a87d72a5de293d", - "0xc35c6cffaf93170662a6f3d4a0152c988b0f4916a33d037801f6f589a6140d08", - "0xd4e37125c0ca7844e048bac4e4c6069ec24b0493e4547f53679fc8df2a1e0d66", - "0x34c6bcf0e01dd55a1d3a82df0a2edcb2caccbc4be93fdfde32c7718276e59bbd", - "0x78e50d394d9d42ca8aae3f4dfccfb32fc3923073a6643c010fd49ec3897c4fe5", - "0xeba75c237fb213e893083c5ed0e28dde3ec1f5f1b60edfdddc17a54de9cdfaf3", - "0x4f35b9a67fc66492fea331378c479eba921fe0a6f0589b5ac07a44b1ea9b4995", - "0xbb4d09d98b27d672259b608f5075575cca49f87cce0519813c1ba4a614d369ee", - "0x805550ff80be2d3a543d5076961442f70dcd86e82762149c125d2ce371a32f69", - "0x7d81d766cc2d6c66c7bf07be0a416f2a4c0dd2bd94026270a410ea6a051dd4aa", - "0x30edcef101041c0e0acecb8d6c15a28c70b8894e56196cddefb5fb6b39658ceb", - "0xe5e8d21a00ccabb5cb50b4d04ee1dcc217fb9908e72bf0efab69d4ce7a0b7eab", - "0x3793f78e2e582b56fb3d24e3bd4aa69db4741e768795f9681285b0fd34ebd602", - "0x4a9378b4ad76df084739b2a63459ac67abf8eff7499210936fb9e6644265054a", - "0x2e47b62375a267e104ad2f8bfaf5c985fa7c6f4c438739810517e41141a825bd", - "0x73dd638affe08c104b37d53e6cb7884b8a9b4cd4afa4bbec6ef608bab4559dd9", - "0xc566dce73da1cc0d069a032045c44ac4ac86b145efd2cb1c363a2d403fd8d7cd", - "0xa1a04d193c5408d0edf85e771c6976c5dc8de08c87d906b6c9de1efa4b4fcbca", - "0x3245d987f205b01c54448d9a9d76bc7e4174c1021726a68f5feb6748ac373f8d", - "0x1f963274b936dde92281f2acd47d3685261b03845efc7d10ab8b02500c239af0", - "0xa1e6d69b28aea5d24c3dc301dfb0d6df8ee8fe693d1d0f419cde3b4d20d22a89", - "0x31b0711795c77cae9e1b62b87244a5eafa4b1764aa1620d53be7e779db060984", - "0xea141234dd00c54ec7912c80baca591b53a058b81fed1673317fa4ec37c2c715", - "0xc5902f3941d647da304e94062951b09f4d6da5bcff71517c7a35e8b49c6ac569", - "0x54c5677af7e31b84010f8b2941ffed5080e9b4d18b608fcbd230f163a5e04ba5", - "0x1a12c56542a01bc58288a58e8e526f2b2c81a5238c83bbfe4808d206b3b9d0e4", - "0xa398984bab81b7640674893496b2346eaa8257aad4cc8bb52474f7162fb6fa2d", - "0xf454a592314f1b7459d21eeb6ffe54c96ae2e25a7051dab1f8129518d3788cd2", - "0x12964f85bcc410fb40e89f518d749c7dc2be7546a960849dcf560eea2ec3d333", - "0xb176775c2ff94aedef2c099b936468986874d17eb563086215525acbee2fdbd1", - "0x250de6e94d60125bb680e862fcb70823d06e4f27308ea1ecea8a465d5febc860", - "0x731cc6817fec665290d7637c67fdbc72bbebae70a13f9346f47f3b368eb1e4a5", - "0x489e955cc77f16d8aa16bada320127380315eac978f0fd1033baa04911123332", - "0xa88623d9f89221dce46b55ca7248581b4e3ace94a7cccc9fd44a3b03412fa729", - "0x4a65c1808d79577b36dfb5fa74d0deb96e90d0105ee373d552685cc4f73d35d8", - "0x7aa8228e6f8fb98a23e1599e12ecd84c879475a23f10660e763e338a347c9002", - "0xa825d02023ab76478b83a2a61dcc5cf6c05ca741b960043adba3f13d1c99612a", - "0xd3c90e414048510d9594ae7a593e25641b6baf93f9625cc4a3caade094f02e07", - "0x4d631d9024a4e75111189835b4ec0007a6951d710512dd7b91a8d56da95e57b2", - "0x0158c7f49affa675f04aa4ebdf11e88bab8c6dc0abfecc0100d32489b0d59f1a", - "0x4ec96496694ef53e17b11b3122cc45cce3a4fe1a252fe9c0d379b302db1318ac", - "0xeec44b94d9f6c7359047d257ffd5f1f5cee87b3ebd6845dc1fe48be686a9ba69", - "0xe24c7b667382186196e5da605ce75a5cd9c7e4803fb2e268b4de5848737db026", - "0x14a551850f945f02fa2ab84c6ffe3d17930c81f5b15e94ce92342c3646b8cd9a", - "0x7e9a72469a52662bbcacd449dc7df8fa75e5cd1480dac3b635148a34e0f9e375", - "0x2dd4fd0394574c5d8c9272c42175e3a88d8501753bb25a77fa937123d0c2538d", - "0x6f044eb54fd2b8e51d89f6db2ee6e654b144d9e69cddf79b7899e06af5bf39f5", - "0x091d894513fca5d2239f6b5ce73737aa2657c90c2af196b2b893ba0b0205859c", - "0x78b9ca580e3eca0808e60420ead000365eeafa60c6394e055a653a7f536c8b6d", - "0x595cfe325aa600ef0d7de6b2eb717b33c92dfbb1e3ecb9487fe332c878e9f121", - "0xe1dcc22aa44a0243f67dcb0aef7e0eb1ceee269b0faa67633698f8ee29155115", - "0xddd3354d4167161c6b18173ae24fffac2cff12a8bcb3ddf1d964907716685cb3", - "0x55c02c02eb758f8c9aab60a2432d29e73b567e3e3d0148611ce4569fa074c5d9", - "0x5d07d5a7d45fe16a0521c55b3dd898b922c619e5b938535e2f974f11b4edfdc7", - "0xf75100a1a2f8d6daf0dc36569cbf579ed6e52838b163fe50b5deaad8fe7bd07d", - "0xb6391d028fbc88825eabf1dc4566e58112e6d3b7cfe6d387d0fa58df387df78f", - "0x8a58fab34ada4d30eecbad64a001015c91e446cf14e95c9d7a3d894530787b03", - "0xac13b36d0eb0483ab8cf21f49c78f9e32092c74c878ff520ed3a3c0080fdf5fd", - "0x358c1021fbdf028ec82b811cdeb504adc2611a1ab477e8a191f780592d598361", - "0x6a7af25eb6d089ad63712187d1414e8bd04fb6bfbac26e6086b1a3e2d66f6f7e", - "0x6bebf617fe66129dc8c9dadef1a74df05567ed2fc89f4e5c4dd36327d6b0a5e5", - "0xc658d76eb9f3fb91327c64668b08d3e9bfbbb2099bf5d9aeb6a61b8e4f893c39", - "0xfe68ecc66eaa625842068f8b5ad4bdf5cbce0c82ecfafa4bf48b08de49c58595", - "0x26eba0a168f2e5f3ffcb37955839d37432b39f2f447497854d42d802826fb1ee", - "0x8b4f8964062d0f00e40a41d21852a2e22abba1f4cf75f594797e368e4df21f7d", - "0x916993123d91b181d145e2668efed6eae3845fd76d4765df3a04d8adde8c7142", - "0x21b91051b5c5fbd22164a655de8485cfc2a9eff58ed09d6fbbb454560898daed", - "0xb52ca76065bcf43f06335977ab87d6c809dcf4e6cdfc452ac2018b77c64089a5", - "0x9437d47e7f7fb4c95aacd5e1322a9c7b457888dbfb1e485142cb16ba805b5ded", - "0xb7cb3f7394fef7de5884e165c8441d441c98c1080865b604dd21766759f7fffa", - "0xfaff33c1aae8882f3a9d84678b55b8945b037b1f05785f2874079da71caf4aa5", - "0xe032d77db9e882bfb3d5adc89d9a9812fb9775f98c03765ffdf0c40d0dbf11f8", - "0xc9c4ded5821040df911b23d92d8270fc83164531c9d631c4d8e228a71e189ba1", - "0xa27f4dd47059fdd4afe78ce8c9029c9a7c0916d10cc7dba1ce011c886d2275bb", - "0x6dd0d3b0e07535d8350cb888c75ca355c4abb07bfc6ceba3eae02095324679a0", - "0xee4a3c6125a5e69848457d35f9614705b651c68857b03968bc5420a809d1d9b8", - "0xc14947239d0a7061d796313aca233659059b13740e63f967f8bd4806feb895ff", - "0x36d9ad9d6ad2e1a1aca489524303aaa8534b137c5e9ef708d589b7a75cbf6500", - "0x497c4cf7509b12ebd34d5c0520a24ae1b6e1c5c050bb3412d7d1a702c48d07d3", - "0xac81f9bcce95283b7f188f16eb68d22adb22dc1cff0853739c46f27523a653b7", - "0x2f0818ee594d2d1fbfe9a6a03cf4ae08dee69fbb9c289c2789d792f14a3e4e7d", - "0x9beb888d86489512a9d1d8d6461490e23599c1595aa5b4d4b113d18c0eb99697", - "0x7708aa2a88f9b41cde9f338da6d2f9dde2b57f1538f7aeefb66a0e9dce488ff3", - "0x548a59ae0782fc5202431a472a8dc5e21ac83b68dc88afb2b0afd56cd3e880dc", - "0x271397f861034f73fe228e3921d835a313041c260a6c2ca3d0c28d407d17ba89", - "0x732b95d3ebdf035182863a540bc002cef70f98b91f90ee8dcef2285a970ade6b", - "0x605d419ecbcdaa05d6f09c9734469847f52164df26417d7807f959986b843869", - "0xdc56e2800a637475662632ac37e9eec90d9f3962a454d6f32ee7f56426ab9f42", - "0xdcd35988aa6982f14b3a2834f841c755ad42dcc61eb0dc6122f2aa9db65fcf92", - "0x659b06e2aa0359e3743f59d2b128cf85bfabc20ac9d8c42a89d9cdda2bef7697", - "0x45377222c3910f95b3f5d0adef823dc4184189cab9eed54b9997d2b30fdbae43", - "0x15d66d0b9347f12bf9c408910edf41623f460244f309076d55cf969dc158b36e", - "0xe5de2158113351c0ea314eb12b28f9502c307decf883ed480c53c1310c9f7c11", - "0xc7342c08dd73fbe5801cc6c8764c53f924d6a77cd5a5b8bdbb22d2b6d3e0336e", - "0xf10486dc44a10f7eec6739dd56e0f7519507777e78e3974d974ee0bd931f03d0", - "0x5985752f0b3849e9cc6a5d38b65617be10b52b8fd0c2642c328b864d59a23f9f", - "0xce899213bc8a178cbf2a8eb86f045f59895530b0af5e209b60553869000070f4", - "0x724dd7b5e5d0e93017a62faffd534e423610c494ef0b5689304bc80a68df0e46", - "0x318073daf925850c412d6a1f2db2d2093268896a88c49e822ccae4070b406408", - "0x74fd03921ea801d4618fa7c2375c4369a15c7774564dfb0373b5913bdc9a6df8", - "0xb10896436461c2660cfe36a60c433b32ef8a1e772f3f9c16ae243840a1aa3aa8", - "0xecfc10478a368b186e4299447ec9d3ce27ca8386274c545f3edac3608ad6cd0d", - "0x288c697b29a0d2c5d45b9507aaf62bc4bfecca1b34e1a5ce13a17711af0df3cd", - "0x429fc79b89ad00887096417282a571de77fe73dbfd1564c13bec5830b38890cd", - "0x4d859982815d597e31d0e5374d7077b59a4b9beec01040608bad05c6a9d0be2a", - "0x1addc6e300b6120cd196bcb7340ca163879ac55f7c9069be77947aa01e778cf9", - "0x2e648a4eefda9d35e26b148028a0b336ef7d75809454e36795edb9d3fdeff550", - "0x814e65df8c720e8c9c7d64c76b6b9554328d6c780b5f3dff96aa3e29506e7e49", - "0x4841c38f2a920bb1a072b9a7853dd7d2e44dcbc35e2737aa2f3cf6652dabfdd2", - "0xdc63afe7974211db82b2602c26d6ace8d93f34736c0fa68eac3bd5d2c1af7484", - "0x2052d12d3534c5d7693d420c8e080bcee99ce10ccebc7451272b7ef08f8a3dc2", - "0x4985769df508ccc2add583d187095a8af4ca73a5be2506a506146e23dbc67d55", - "0x4a6744d48fc2d85cc99e419483ec1111ccb6fc7ab13e4eba28a29269acb30952", - "0x527b0514dd83336c0524add1f01799512517378b2045c4ed43ccc0e977553bac", - "0xfc33e6a20033fff41a9b885dc5bbaa848a0f244fe89b4a1af261d382c544fbaa", - "0xadce691a1b4224bd56682784ec106dc93af89185721db8b427eab6b0ff22776c", - "0x0fdef2a9525541c1e96172c8a33ccc08ed759dc024d437b82126ff03b65e1684", - "0x7aa9e8c1e9da3c68080f099281b367de26838ec7bcd474590ae101fcd68a7116", - "0x7eb70f0a4c42726f5b3a67ab12bdf5b4e6bf5e4c347b6c32f6fd3a04c4a003e5", - "0x546f5f3c2e44b7a61d6d0dafd802fcc114db7acb7f887f0dd0b1878979fd3eac", - "0x5c5294fd77081b1a23037c34170b568358011be2fdc0e3ffa2230b1a017ec5b9", - "0x6c854d3de1f2a28dd8f6320a534b915b032078377e3b880eaa8e9a3775768e2c", - "0xc2be1a4c7a5351063084c5907f19e63f25e048f3e27648a6d9056d3d4b635c19", - "0x5d0673ecaecd00cc8847827bbda1dd7bb21f537e5e10f10d8b5944496b55e9e6", - "0x8a1bfbf9dd51b727c17e8920019caae599f53d37563935c584c84cbdebf942e2", - "0x75649056bcd2a520811d8f9d5c0c629f492e93fcdcd298c0657a8c857fdf2cc6", - "0x71421a3eb42eeba485fb90918511b0a430311856c27a968e52096c6cdaf0cce1", - "0xdf19c2d595ec81bacaabd39217e100e6731f8bd7c3d6c5bfd987b27f89be146d", - "0x573839677ae138670609c499107217fcae5de5fa9905c78a21b07f7a906a0a53", - "0x1e8db73865049c1cde4004c2a60945edd9ce9847c360a2feb36205e1f1cbd5da", - "0x52a602e7c2f462a39288465c733d2133b7f77a7a77d7e60e214d4d4eff917258", - "0xdc30b1767c78f2ac953c5046e6440b7de488933cdece18fa3566f00d8a96c92f", - "0xfc0baf76004e7d87b36ea52de7d886c9b3be44bb8063549bba31b63b87ac3f92", - "0x3426c56db720ccd184f2e49385e96e5a37fd378cd486de8fadcc7d5e727aebb8", - "0x39fc1cbf20cab093c75b42931fc6371e9851cc4357badb6961359e07b9d97fca", - "0x6b3725ada81d881bbe245d4b55c7be12926b42d0e58dfc8fa38d54f68ff79ee4", - "0x69bbab0d123bbf03499984698c162f9dfb81375f6bf9f2e643badcc8e0281ef8", - "0x101cf365b31d4b37910433ffa265e1a978d4f80c478936809e1bcca7baff59b1", - "0x65d26a27a34ca4ca98c2b5f609e4596344514698f2137547e815dfcf91f5bb36", - "0x3b8598589a6ef43efe0b7a64832360ab02e150c4b4ec02f2a028d2522f4c0ba7", - "0xd8776563bb522b42a25cb2714f9ea294fffa4c4fb5ad040a9458e10637861f34", - "0xb1b85b8c9bea99a2bde16ab99b2d7e42571ba15dfeaf28a2897322bb444171b1", - "0x803cc4e5c23bd3842736b36a425364416dc69a3efb95f3c7fdbf7782d6de7edc", - "0xf5803ed2a130a19600ca887ad06813482b678ac0eee049cbb2e56e39a90d9867", - "0xd3f6125b061023531cbd3d625c9382631e2fecc75945dd6f78f883353ec98962", - "0x7b1d59f8c9869a9c73a09385c82762b86fcc995e75db00bda388e1423be19df4", - "0x2b7a8289bf9f93c589866af1a224c34943433b6a5a571df7dc4524bcf2adde42", - "0x9199d73dd306fc9038b36212c3ed7706d8b0f30cfcf3359408a8e6fd8ebab0a7", - "0x0cc8446d21ccc31bee04addd48b50f394390ef3340745555f9d7ccc3c13eefbe", - "0x80052b2097d7f836090ba9f94ad7c33066453d8ee62a2e215a82003ed92568f1", - "0x231c2d4e6a6039342ac03f766fac9f0a587ef6828d071e63a1e3f660fff13263", - "0xcfa0cae426e4b6b9422d72a687d50139654fc3351993b5d38ea4fc6f8944e662", - "0x9dc837a60a2c18493fd3b40083996f18a44f37a7a762ca649bc5d8c8b272aaca", - "0x30b4651c3496efed5997116b7a12f9cdc24040a4e110478f1a2330fbba48fbec", - "0x99567d98d73fe92c81c69aa465bd62b02492839519f0380686349da64caaf758", - "0x9618fd333f89357a22bb3101cc386003c2745f8a24b5affd2be9cc3c26a93e34", - "0xe539d1a4de2a57dd65cea42302fac83aed570f2fd7a318c6d226dea58d5f94c1", - "0xe69e3ef5d9d8cf08d3a354da7179d5bab9c68b4b6ea6a7e5a58cae9d901b5f35", - "0x97cd995441161fe4f50a339c6323020db6f56b9221b549194a93b9895a043e59", - "0x803e04ee8a22299e779491a6749b7d57973a59fe714d080f03749bccde30d5c6", - "0xace95a4dc00b3fef757aa76445b1edc582246eeebb7c2bdc71fa648e06428485", - "0x1c8bd27b857dd8dd5a671fb4aa6cec3c41ca1c747c9f86edca8f389ac637e09f", - "0x554b4d1bbe92718dd1aba2a9d10f48f803287d2c26108bf3cab2574a91375511", - "0xd5856bc0794ddc23049639fc438c360d66e4b3eb1f96516ba0c02c46cda8fb49", - "0x25a4c78e490c1d5fc6c1252b2ba13ca9ad5113a5c64e02220969146c5bbe9730", - "0xa8dd024efc7f03b61ff9926421107b54c42f5f9c936d956e9b6c6eb36f9b242b", - "0x26d488bff06eff3fe1912ce985ced65325e09e1c01a95bc16d0df22481690640", - "0x01b89e65f1a82ea7e456e96e2ddf29be42dced7c7ea9b12e841bd973506dcc25", - "0xf0f5d3392e3fb73697e44755d38c84bd99394ec2ef9dec841049baf8166e6d55", - "0x91c4f65d698130b132d0a33e44c27ea55f477cfb134224139ba0264b5737ed42", - "0x1d2295b21e1bb810d35471fe17adc80e59c6c57b5037de90c88e99021ac78b24", - "0x667cc89337d382967727f0740819a2f6298dd5f2b7b5e82dc45e3f1c15c5e998", - "0x908ac480019af2d3c3017c413077a73905e27ec01721aa499c24fa7e13bf8ad9", - "0xbeb7ebf8ffae71221e5ff149e3333fdefe6b11780d439b45a874129b2c25d71d", - "0x235c2ed0e3fd286df5cabca5f0f61bfe356ad0d7c8bb9c642814d01f2fd26699", - "0x22fc5189fb1945ae98d89e50b258514baaf525e8137e9581c99339e1097e33c1", - "0x148256253b74711fc61f8af7d4efcf8694752426715a0a4c788556feb68c1a2d", - "0x26ad59543cfd91bbe37cd8fd1995b81d0f095ac4e02cda0b4f5f40c9db6d320f", - "0xee340b0f25450c23a5c9b16cd5aa59657a9992c1c341172bcc8c9038eb6789fe", - "0x5da22a0b434f041d76be1d53ef63ae24031861dce1ff4baa3511d67664060bbd", - "0x6009815cd4da0162884d0be4cfe6936f1b251f50e9c89d9fdc2f0af430012e11", - "0x9342474f10ce871dfeb70fde89b181075b18e973d4806364d52f83f6be4d69f1", - "0x3c4c598faaf3c9fa8b9c1bf856872e65e3d53435177992b29a7674198e7df000", - "0xdf0d952cca28797dbb503cb8c7192e811a7dfc0797379391f839341e210078ee", - "0xdd48052c8e9a9ec62a58a580abe1c420b89393bfffe53fbe6b7f9079b5ec4d3c", - "0x028846c41592546d1d11375d00c1d10a18c06ee5f968b2ce8e8c42bee7aa263d", - "0x4aa08dc77484d15f8423a1fb2df0fb0c67729de1d48e61831884e49d7697674d", - "0x084c72f0f0fb1cdd4ff80ebc9379c9d29c8d1e35cc45901a67b231ac201ff9b4", - "0xe904eec627d475fcb3e61b2db0366a24ebcc286ccda2b1614049fa17ae9a9d85", - "0xc3fb5ed46f30f566b1d5547d5e00c4c999960dcbd1b60f0b337bff8da30c054d", - "0x1503b399ce9dd9f1cd8005036006e4bae2b3f05fbb1393b8f5d868332605c98b", - "0x70de442d35565513b92d8cdb95c6537f72d7e099e217af7c260fdf92499ecfae", - "0xc6b86174c627fdb002d187feada8c72b9bc6b1fa5d11785988c72b06480ef1c8", - "0x06212692407b9b701ed1b115a8ae1cf07668cd7498cbd7c5c58e9b52a0a0093e", - "0xfdc722f761f07d5c52d36c147ab8e80a666af5aa77fdefa37e86c7b2518aca12", - "0x7396bd40c5677c5f4388d956ec437071cd4352cac44b7b97eecebb6c5c99b507", - "0xf634b80145415b2086d4a20746bef5c7d34034ac6601487792889be39bac54c8", - "0xfb8cbee41b441d5424cbd12b064b04f5618f0b360b1c2ae5ffb90a0194e61106", - "0xf77c56810755fe69822c1250295092fb8e7c88d789706d6769d0cbdeb386137b", - "0x9e0f084e1184804e850784a4e376b8acbf58f4235a3829d49217c888e4e5d535", - "0x6b39d6de64090febb49dc6f6a31eb606d8e278b7ff4fa9e1a9774d3d9dc71f81", - "0xb5d9e67fd6cdd93c74536e5a74dac8eb8ea6914c2315c22e079d61eaecb18c29", - "0xac3dbb4fa1da752bff5884154e02ab975e993201e69ef9ca532c8b1e00b1628b", - "0x747b42a7005c41ae222f8c2ecd4bf7000c274366237e68b2e95a64cef3799ef2", - "0x8f06b121d921274a0d8da269d19ffd6a417cec988f0018647966960ac822fd60", - "0x13451d3ecba978d3c0d88c2dd4068cf060e949889ea514306c23a7a1ec574ca2", - "0x737f8bd85cce3a392da15c8e68c3ea9748a6523ab92dba8d1c6c8081311c3ef7", - "0x83cca9d97f8c4b2776b96d887c5ab1e8e6172970d6d237da98dd5c6ce07db7f3", - "0xb5bb3dd88ecfe2e4ac4e4be2b605cc7c5e79024782628a95df33c4967dc8251a", - "0x640c6050e63c965742cdfc6d3217eca459a24b3acf3a5ec64eed9bd7bbcefaad", - "0xe74478890f186d41b5157dbcef1f2754a35a5af332095dd25d34caf01580062c", - "0xfaabbc9703e049e49d1d22da051cfe6cae0697e7e728e954bd5abc40d0e2014c", - "0x8aa4a19c4ec50a68a6541ecc11d4473cf02773a0ba7bb4012ccd76ae11ce8bf9", - "0x01abdedb1886ec8220a4f6ce6a43863ab77215f03e8116b03f3efe44bee658b7", - "0x18e44060426a05d3d9500b7d6ae6d99133109bac39942ab825ae81c551c0ef39", - "0x19a60001bc92289e73d3a8320f7601eae1bcf70f65c3a43ff6af1b2c2c17355f", - "0x2dbc82c8ba30fe69aa00c7abf129cd1a65e8fb9398b2ebca0843e526e001ce1b", - "0xec773bb4cf7d35feb70ea19cb53cd64af2cd34c1e4f1e282bd3cd0b39720bd2e", - "0xc966e10e5568661e0930bbff1561e2de413f9f89d984b46224ced75d57213401", - "0x29658e4a7e4dec3e9a449902af34ab93fe760c6a1695c1a97e0a093e3d187782", - "0x98b0b147d3f6a8a21dd9e3fe01082dc9031c193dec4def76e53ad1c13269c3d7", - "0x288b646ad44d5f4ea299917a9b69c9c07b5ddc50e93d03fc1c1ed653599d7a7d", - "0x87c26362fb801ef77354375eadedc1489a4fc39401521cb06b604fd9288e4d82", - "0xa5cf4098a62d2dcc87e589c8269dc712e2ac2be5adf07ca33c520556d4da0cc1", - "0x4f253c49f87e135e4a0941409fe70ec6b29f6b0d9aed3f654f5eee7f4eb0a486", - "0x5370ae8dfa2a982dab86dfb45950a50ffd29039781b2b065ef5057f7b43b84d2", - "0x044f950f82d97f0fa164ec4174c238203d15f4cf32dd739931c3e291e0902ed4", - "0x792c3af8c8808b95f38d6e21979450e3ebeae129f0a9a9d0cf2951252801dd7d", - "0x141e09e2ca0a96d50e7c12aae794ed0f22c0441fcc51d1c4e179f891cb06ee1e", - "0xc475b7c5293efed9c9f0a5e1d8800e4dd10810bc7dd33c406da131b15524ee48", - "0x0018d2c2e814bff6fa8f5e162de402db71dd9eea45d320f60e280a00aadafc2a", - "0xc68336fc8904777bf8ec4d5a61f79f2e847c028889bce39ae0f307d11efad818", - "0xd6439bee30ae288496a022e04f5c21b9b047edbb370ea3e33ed236a217c6ca0c", - "0x03e096d6ce43823e4543ddbfd8f55528d49c38b5c701c520e95a51547b5b193b", - "0xfb80f18124198c12b6359ea7805f110f9f25a6686e7a917c110ad511554e634b", - "0xcf9a0aa896f5c54982a827e07314a81b58beb3de97fdf112ee1539ee47158f59", - "0x948cb5dcc88a5502d82a292e7337d5d010055ae4964c09b2c6d55b0401e1781b", - "0xb91614b49ef967aef3b113983cea703a60097e0cd4c0d9cf1ad6f36d594750a9", - "0xe29abd32597fbfcffb8d185931986a8519fcabca73abfc5be6e42bc8b6439374", - "0x9ebbe1b1037327c4f2f3c9b6191b6f28aaf9b04dc58f3314ec2c16ac1eaba9b7", - "0x098529aee4331ef9ee00ee624912ed1a7cd8d63ebf27a96059f56e60ab9a95b2", - "0xcab9578772277f3e1f7a6920b96df6ce3e6d7f60c42925b7bb1ac1d71d733718", - "0x43cb7ebd27e7207228ecee10cfb11aefe077bf03053877b53c42a41e4f39f607", - "0x12ef0a09e0f5dc9d89b01cfd66b34182e80821f0f9d86c80327086a9960ab9f8", - "0x30bafa474c54d2912897a13911cd1df20988be102333bcb9fc54905789f87718", - "0x383205a3e0b07e359127b28c3ad1b451564091893edc87d3d92e081111dd20e7", - "0x74382d6b160ac8b1e7b86abf22cb943b43afc4480be4b09c87c8b796f22eafe9", - "0xe84cbe2ed5a30d7b25ec540b1934577387626fb894e4248c719d417ff1b0f171", - "0x068dd7e31af946d3427dd6822c22577886c32a7f1e3a3bc61ee061be7c456751", - "0xff5309cf09f24f3666205ae09e542634bb848c47031ebc10079d96819095b60a", - "0x82991fe8d442572aafde5e2f6d2c0ba0823ae72110c2f4b65a0d39244db460a0", - "0xb86d9c8e378420595feae0f728e00736f5b72ffe37e289088c03235b3ae8f556", - "0xd678cbfb25960efc5a64d48f2ada784a5b43123da5db8636804c2ab7d1a6cc36", - "0x1ceacf6ae9acb6e7438e6971b133aa7a8ba7b648c130962c3f60b7449266f3ff", - "0xe81d2bbf5efd047428040f86caa351a8785a0ee864e74760569318c997b25116", - "0xde530285d36c4a50d0f91d48a42e9b85de919e51246b6aa54a55eb2041b17289", - "0xd5b86ba5c24729d199007133a8000314476135cc5b84dbc49de41a7d9f3060c7", - "0x95b2fc8a95f67d4d67cba712a98bb3b35fc4b92581f5eab9e737f2555592bf3d", - "0x9808b933e2c732a2b4d6229467430cd73715ac93d1000c470dbe403d0f14b6ce", - "0x8a708bd8ec41b6aeb1f822ed4892c4d685ae3fd1a752a0b79a10a6cf288c8a26", - "0x689c7f32af0a20de771b9c03efeb6304b08a421cbc2f178b63cd284f3d74407f", - "0x0375180a004265f4a38946353455c221785c3f6da1dcbde2c09c373fa42c434c", - "0x764f8f063124e8226a65af4823b260354924781b339a93f6eeb0622e10333741", - "0xd0a28ce8c50017ad2e0bf1c1f8193792228b879688f01354552e656272bd7936", - "0x20b12fa78b655cce826e87bd18cf938917d37c306cb620afef671b0548b1e1d0", - "0xc94540910d93236aff53413af46597dcb51c02508f14bf81817966ef32799c00", - "0xeeda2d6df1baa5dd3cf904c4402e4e99aaaaa16b6f3a0832bffad5492fd9e725", - "0x74de84a1158dfb1af0c1478cf0b048ae8b0ae00139588e91d37a6874b524522b", - "0xe9c92a1b7b298d7222890fffbd5ae4d2f8a8e0c5519cd06c3e34fd94b1f12767", - "0x18261f839aab59220f72e4e4bfa7a8dde2d3c5cd8c0179d2ff32a151f7af92c2", - "0x35fd53473534a3bdbf0ada29529991bfe6ac1865cc6786306b92dc26a6aa8116", - "0x6f40f093a08a00eefae4cb848462ba663b62f089005acb49a8f5ee3f1a767375", - "0xb0d49e32ac2ddef6bf0729ce9212c4bcc053aaa7cf8dd6649247749bab68cfac", - "0xb616c68f680b5cc9be3fc5bca40621514109dbc1316b848f1f3ce18b05e76d33", - "0x3ac8fe2f016f98eaa25451e36ce4fcfcbd160c0306c57f2edea049cdf541538a", - "0xb45e54eca70d4943eecaebfef3dc2219e79bdc71bddaec6adf53a1d058a65a30", - "0x74284120c5fa1ddcb7c294533eedd0b1ca2703626b7361543cb8e2a922177aae", - "0x8b15208a8b5d6ddfbe8b09148d497a5e3fc267b8f1d24272814d72f4826f8e88", - "0x3f9d4dcd96d5c902d4ef5655c10620c3e5a25edfa6fa093b0546d05c7c724d8c", - "0xf91ef06068659865a7c94e955f9c01ba4794214ebab33c9ec7030b2dfa47604a", - "0xadd5bb3d2f06ada2edb35754833e1bf1e317edff3e33535e135e67b4e4c5fdca", - "0x5aeb51beab2c98d3f77f2f04a39c2dfaed3ff759de9f5c16d4d88692a95eca60", - "0xeab1ab2c49609df8e29041ffefea082f92941aefae697fd915b9aeee8665c57b", - "0xbcf847e97f7f6fa5a16075ea54a67254e21fadeaa38d7aa42d16f223fd179faa", - "0x07f25413e56e9dfbd9bedf4b0676755f0cdbbf10b8f13ac7ef360bef7ac905d1", - "0x373e2f4dca323c9c4b4cd0cf4a1eb04ce7fd4d7548078200d9ffde2e3fa4cee7", - "0x5b9fd47a0d9cb386ccdcb2755934bfb8b27be896e52afaf18a0d130bcdff5432", - "0x1050a77797e0fc3a98c1d172496b8a3bbb2b045ebaa6b8b4af4e1542180f464c", - "0xea29629a5b08050c92412a25506d4d6dd8ea0f6bd8c962422d8f4913090a6d2c", - "0x1cc76b2b9f505bc358df15e285ac7cdde0009a2a19de9fcb5a44156ff76c5585", - "0x03cbbc18173700023643a8d6d7ff57cb0503336794c5fbe0b705a95034b7af80", - "0x9e2a5b70615a5c057d2956ac21838616ffd5cfb26fdf7398c4ad988a8cbc27ed", - "0xb2ef5e7c15e169ad2ec00e254415f4fa6c1f0635f728a98724dca4335e1c7801", - "0x50f9e432ed8c0c999685735ad95a4c2b3e0c0002d1159056392ed541ddca9b11", - "0x40ecd3140c4c8b02ce66489b39842d92ca64d05c30a9eddbfe675dded3fb6f3f", - "0xa1417d5ef7d185323ae3452fd99d54d5336f69ff866cfba06838da5efe273ebc", - "0x93b51cb9e7c9ca4e5f67fae55992ef6b0869c8c2effc8d244791cd2275f5f136", - "0xd9889fbd8c71e14316b5dfd46a6d049ec05127e5d3db6f0a31a935cb86b42d0f", - "0x384e1275612d71431d80d155015010e22b8b14a0648328bf1b32f92b1673308b", - "0x5eeac9b703836463fc0019c1b6f4be32282f4ade6778f897f6cf9d4c33e16085", - "0x2bdd040915ac28a0a604b727f062cf49fd413c44bcfaa2cc5eaaa9224248ef51", - "0xd7c85280a461c43df6c82f6ef60a973f2581c428394996191eeaa0d06a4b6473", - "0xe57d7c71a989601a066f8447a5be928ad0ce47d5413eceb709761654a30667e4", - "0xe1514ee44d1db8307cc08177a343887b5bd843cc510c5153b7f251aa10704f87", - "0x2898a69c885bb4cfcffa45a8aa63a8e81a40d4e3a7e1ff218ca3b4c70b836e08", - "0x27b66b51bb80e11582319b2fe176490daff05f0ce72a9dbfe79691676a455e0c", - "0x321d7bade5b8a1adee64bb0362620d99a5528831cdc0f1bd0258c9d66e4515f1", - "0x0a5443109753ff0845752dc998e82148d1de44d8347e51accd03cdbdcf405acd", - "0x1ef3a558361ee68b83346d40624b13d3170519e10dd18f60b6c5d7804f3c2d1b", - "0xf81fadde2aa798e6c151b35f764009d754f01aa0d8dacdd1a8f3109bd5d711d7", - "0x1af5b4d637429fab034b33b5fb3add567b5896608bcdc9e8d86a7d7ce762b4c2", - "0x8dcae50013fcabdc708c7b99c46fb4a68c75971775c135b7f7727bdf8db645a2", - "0x74dd3719b6229c2f675e41bae47aa0de9587608e8ad76e9dd911d695cbe3727a", - "0xca02fb569c275518e1cb95018a3b67dde118e82ade5cc9a43e118afa8da5e3d4", - "0xd5a890bef2beb2920a7ac22be90d0ed203dcf6b37ed0b3d2cea1fcc8f68c6d98", - "0x917c9cc04ac1f8afae96f0dc9b49161c352ed4e1da970e17d52fca31b8c1978a", - "0x009f16eb012717beb87fcc2b4417c8096a621fddc2421d02d84d0bfe92cd3587", - "0xe5ff9e2a1ffbcf1f53cf716b7caaa1317151ed8b81bd641bcf41eeadcdb0790f", - "0x2dfd67dc149772d4aa52588315df62edbc8a9340f2b3afb367f7f1564c1b8503", - "0x830df571a176933f6a72ac634933d61e602f744d1e3a046b7b639c9d2110580c", - "0xb2a030fd70ad2290c69e9b41220c14800d9adc33d7f8af049120fc6526a654d4", - "0x8a013c2432c60aa6315b1adc7155dd022df24129106cccbdf463273051ee0efc", - "0x55040dc6b554877d752f2f0bf0d97f71b34583b3d939587106affdacaf5d7c0a", - "0x251817e5c81aba6d675d47d688f19d5cb6957cea13ef3e15c66a6573b84af432", - "0x708cc1d141308870ea3ac36e5781cbfa3b5af327c565231123496354b5a9e74b", - "0x4efb2006e58b45c648d27654d45f1f797d4798217b2d0b54958fc576f115d81c", - "0x7d1bc87fd32a1b2ce74fe3784be5c0fe2e625ceac523ad55da7ebcd3cd1cb94f", - "0x575cc8bf8070ea53a54d027cb7d42eec9dffb85489013634350a96a434cb498b", - "0xc69812969e85bcd2e7e048b4323a726691c78d1902c1f1dbdd3893c11b9df8f6", - "0x6193928c0a4e47c9409c1af5d945a8c92c9e28a76570dfaaecfdda8071d6558d", - "0xb8982114b6b5da8849c27ee7d23280e15aa780248ebd36e5d364f2a05ae1fe45", - "0x49577950e5a3d4a06629686f9353adecdad6670fc9f4e6d3eaf6735296cc6775", - "0x6444155176ebe3b62f028ad79f058271d5b317360c0b97b7fe8c2a642da88229", - "0x9a3f0adf9c7b9380f60baf1d524590bec23b4d3790b3909ec8da3e79303252c7", - "0x591f122777ae7a313b6c4ba8c052d483f2aa3a687b5d85ed39e19d7e0c0e6de2", - "0xe3d68466d0747ab6245e5d8620eaae080a0f3c2aa34a56defe8adaef56d5474a", - "0x8d0f53bb5f7610133f04a763263815e092e43b0016369866e959d22eca466d85", - "0x8c8f0fff76977593112a64826f15ec0e909d9a1f1589e121a37f73d881e6b696", - "0x9ab1411e7ef5b295ab1a9dfe4ee1cd1a740af5b7e4b0a0e9c4a5f37e803c7c94", - "0x7a842e2c606aba52515ed7f114116bc782810bdade34b582077c57844e6af18d", - "0x40b14c755b9cecf8264622d80dcb5864241c0f41e9665908ef3683e83d33e9f1", - "0xd0faf39bcb44f583ded1cffebc93a8acb49a10b3bb2b0c9ed794b8aa4bd9e30c", - "0xe1eb93e1abdc400b49eda22b5818fcf61c56309f10d5e3cf3794bcd7760c4c0c", - "0xededdd50db9028797406a8559fd824a3e024b2362bb24f708c4f8423c657b31a", - "0x20f6ed1b586555773ded5606e711a4b9bc47b84f45bdb97653de8eb4c9aa40b7", - "0x77ade166b067475ddc450b0d6991b5a8de93c3b60d76d2b6a3f15ace449ea6d7", - "0x766c057ee18d96d84ac85caff6e0ff4954d3add40ed9488987ec5f4865f06ea3", - "0xe5dc0692cb0da1b9778ac99fb26b01ee7da45da56996dc7e5191f28e60a83bba", - "0xb86ddaab167daf9b8d4827e12f19201cb7ba2fdc7767aa7054f478650360b5a0", - "0x570f60bd9224065c7ac611e89e9737354b3186440dd328efa7ee0078850fc296", - "0x0438822dca67b58f7c5ede70758f24ad164e1c0e3f6628274535101c13121201", - "0x709382d302a6e5b21823af6ed14f22dfa79d7632b8649c67cff749fd9cad1d54", - "0xd42c07bf201d8025e9202ce752e25207cae1804877149a756536348853478dfb", - "0x82944c59acd3d43c731cc820a38f4b9ea4fafb3c5a5cc474ca0612c2e50f88a1", - "0xe9c2665ac57f450a47b8f5407dbfa7575ef4fd3bc4c90a1e64e7e0f597c6c4f2", - "0x862decca6c5c15a52cfc88a629237dc1740d533dea40a0850d8d0cff47672c43", - "0xeb453cbb85db88ed6ab19e57fc0b90c045c3c0dd6aca67f3be579c6e75e6a1ab", - "0x8f32d04dacbfaf1bdcde08205fe539f24a5e453c686257d5e39217ecf17e2a01", - "0x81f1d7ed305f8981fd6ef9adc7374a3bab51b9f5d042c35e7cb16c7eadaf37ed", - "0x8301660b9f830042d66b7197ca95eda84e9220d74f48ccef41aabcd8bec18c57", - "0x07c00b7b9e698074ac994f5a96cfbfbc6f3e8ba34bdd727deb60aa3ab9f0f1ff", - "0x44c59c12a76078144f62fc9646f8a37ac38a0db2db3cb002712834c0bf2a1bbe", - "0xb3abeacf4280e9fa9357fcdd9b4e866938c37513e0f8a31b9f8c5733c48498f5", - "0x72dd15a76cc64e9091fca937497027b77517334012d71b47502ec28f4cf31433", - "0x0f86404545ab226dda8789da56d1d67edaf201062dc4f3268e0a4c3314ca6628", - "0xf2c60e7441c1cd8b448b9d93fcc7e5f5a107d3b502454fb2f6b8607c49e12a01", - "0xc272b28ebdd6cb2cc76cc255f68602e731579bc79ee8c9f6a3471474f14febcf", - "0xed87c1f9b73e4f757d822e5161c6299aee52be1465ca389f0ce008eab6f51c17", - "0xcec988f7b79b0b5b9f4b1e43466b56dcae86823e45997fe208e7f2cbe4604947", - "0x0583ed42e92bf0cc3a54a4b80edde80606aa9a16d6254fe89b809faa0c7c5f46", - "0xc61e76957a49acc14fe008c525de69838370fdb7c3063bc04dd98b031b12eb92", - "0x5495eb0d9ac61871a364a5bd697628f4e480b8eea4c454cb583b0b11e59373ce", - "0x3ee00540b8bc71257573acbf77c97969e0af184589df1decccc5490858b1399b", - "0x6c10a21b5e7a430ed342bf38a88522919f217fb7a087576d7040f71b7d6a8e0f", - "0xc0410a1981bbae319921e5af78c1e80e4539e1ba9140adb9139ff40816255910", - "0x300f10eb6ba9d4ebd662710a91bfccde83a561b0dad7e14256a7b62e8b720662", - "0x9f5de17b88d3bbc6efbab33a7b2d8f2c120643b3844f8e37d19e10f1526195f8", - "0x2918baee5948882cfbbb57f6ba18a0434d2a0c3928eaea3d1b12de0054c00b5f", - "0xf5cb3b2a6d29bf9a0988877cbe22a01726883159a787a324bf1e685b92ab8018", - "0x08a9570f38daac2ca30d32e582f9f8f3a80e82020668b5020b29bf0456779b9a", - "0xf807d91faee554d99a4a728b219d285834f231129a6225a5a4340bd74693b133", - "0x7798ef990ab47b908f703dccfcc0d5507dcb7afaa9e5b98841825f7d2ac6b655", - "0xe5d3b714c47a7f5b8bc95dacec03facff800774c72f4379051a1c574b919bfee", - "0x5c5f6fdd4dc6d353865ee1edd5893b634f7239b327840b4973509027c8622f74", - "0xf713adae992084b0593ac33894fea127fd73a6439533cc94d07b89c50b071fc0", - "0x548d8bfbc57c4773fa2918dd13efd1f55a68d4a86b526f4560abcf48665ed637", - "0xf47bdfe91932a9b4e968e64ad23690b7c3421137b44030000ffdd78ac798b3bf", - "0x4ec25256a43c957c1cef4be253a69fc771889ad563ae677fe3e5ea6936cd760e", - "0x9b8a28041d701c32f976fada9ce9a73fcf5dad79587c9e02b5abd7a299d74292", - "0xc7c6285cea2145104924ab507bbd5aef09403f61e90ad04e0fa8a67e2abb140e", - "0x2e839dc3048ebe96490dacdf688b017f388288954704c9a99cdf25f9203fddce", - "0x37e37dbf1f4cb8b825cd5b68ea18a80a0bcbed46cbdb54c5ebb136ba4e88ff95", - "0xcdebce72ebb15b5fe081d0f3290cd8412c56e93269541af2461598fa2a05cd1e", - "0x37716cf58953f21168a83613bfb13db775fc96175384aee35f76e82095d19716", - "0x7f7ed8d8526f0586f08dcfba6a279689d0f7aebfb0b11e627c7cd0a31ea99256", - "0x1066be85b9d156b32227a7b74cd23b330f3c80e62f8b671b1dd03d2fbaf038b5", - "0x53b3ba2bf9f02a52c7caf9ef6b46df80d9fa1ab12437774b2f2d073e886dfd5b", - "0xad963410b3f77f2399b7ab925c7ef5cff512534c2e12e7e3e9573152f197f28d", - "0xa3b75316cb456a516d1e26ad8df52a0a2894f26ff3a2938f9e09cbd9354981d7", - "0x522ee88e34308bbdff564080e665cde9b3b4ae4a61afe558e5609996c1b2f855", - "0xd7d377a1ea94fdab72ce796d3e62cffa96b2b2226bbc9fcea51817414fc367a0", - "0x2ad4691181f4947c25d521a9d593e25c5e67c0b6150b3d9e27515c7808223633", - "0xf76442cc901ec7207c33840e6a029e3655d279394261b8e2fc350547f2fd4cd0", - "0x39688b8d86b6cacfea5d71e7311b01e2009a939c6531eb8af8e88b1d1b7a04a9", - "0xcc35ca75b97530d9c19199827d373210bc9624fa0ef572f7c3aaf2672918da58", - "0x85a6af5bf38ef40d2be7a529f3f370f56f380406fb8c5165f5bdff75a182b2ec", - "0x54296a0ca61a8a80d4ce9333c985983f953975f6bd7eb6beec593c149a3359cf", - "0xaa1f116c6fad9512afa8329eb499f197c5716a53ee283f06c417f45d9ce2fcf8", - "0xb9799045a8f20c5885649ec1a632eacd44a72b794dc7492469f8c1abff3edb24", - "0xd86348fcec2db2efc3deafcc18ca121de0176b1e89698a3c5244f377792784e7", - "0x7267a4a5e59e848f1c3c9e4c6f43e7aa128b73261adb296a89a7abccdfd16cd9", - "0x817a39c73cf85997721cb067b4e00812ab7fcde3e4506e37e6514c8c4e87846f", - "0xb8e7a1c1aa51637ee68590a2d5be7a8124fae067cb3b0ab3898f5c45fb275010", - "0x43f77794f4bb485488cf082b416885292a7b254057e7a75e331f4641c1ea2839", - "0x9bf3050d09ec32ddf7c40fb96ae136085587c3a4ef6cd413f73dd42615327218", - "0x069923933c215e6c60c8285f0afb83de8d4d9911a2c6417178f5f149388af874", - "0x01e1fb2dcd7bdf5e30e5569a677f39e9356d70ad5e7c30cbc8c73eec66bba5dc", - "0x39fcb505c1365111a0837993dfdf527a716a441628f248aeda785b32e04eeac4", - "0x7837393e026631af009ee2f61e1a39037f97f77f98690ce92fcdd934b80d3ddc", - "0x7695c9c3257358c759fd8c9018b2834ff56c42abecebaae806a281c03b887aa6", - "0x0446616edf3b74a68fbe0c22029b61d16b67c300022211e013572b90815b6050", - "0x274d5faf73eac9078b9d9c870bf07103aa749b0273871fa8e166932e3c0c1556", - "0x34fa06554a8bf6a2305d7ad293d1617f5295c046cd6de60689af2af9d44decc5", - "0xb641c3b6baa2370b85fb8bbb8c054fd9e7d44425a095778e859915f5b3931006", - "0xd4db8f0bebfa97e5ba93fe7aa444eb74782a98f03735720482b5080a4d3d980d", - "0x4261a0741221a7bcfa4d8bd1d6e8a62fe47ded969cc2b1446a7a76da9a23e844", - "0x6664826f16716dd01d707896f892a24b29aa9b78b2a162bfd01aade882314815", - "0xd9cacec24e60731fffc19e6fe8bf0241499ca78c51f38f841f6c1d91cb4f686d", - "0x57755fffef02b43c1be01eed12caee89bf5cb4ffa8b823aaed947083cde2d0ee", - "0x5d9bfb52ffa30989655e77512a3f40db66c790311c9cfb10d161b62b7542e334", - "0x302296acbdbd6737cdb060223f289ddd71516c8c42cc5128bdd545de6df02bb6", - "0xee1c6c48a63232e8dba59646f8d50dd6eded16ab310f39548300249efeaf7b45", - "0x60d151e272c9e6874c9c9bd90e8b241c0ec1e978b5b1db2d6632542f9b02488c", - "0x71c8d8c3da03c317eb14b12b5d403581432b3f0cea447da99dde8b60c9dc9cb7", - "0x9fd4de13da3b68bc5682debfae937d4ba4fb67f87cbc30677941ca53f5029945", - "0x2fb70f11714902235b3ca78b281c8ec80aab89f77e3fc59136bfbba44a2c46e2", - "0x30109aa2d8e89ef3669e4abb89870c77c5acf5a34bf273fd56b925d1a4fca03c", - "0x8d5c57cfcf34f5cd277a7d51c5a2fd1405b26231646db44043e735b8aae78478", - "0xaf961653148c2e6007d011e20d008c25a2c842f5dea15e2fefa76b3f324424ba", - "0xb4cc2c774796dd0c44923ecb3904e8f0c64839d32b7afd0c7f87eb0ba5e52949", - "0xf4c16b1aeaf622d35b4f897c5b9c96358af2fde5ede56d377132a6e57549565e", - "0x15c2a9f6eddc424c3ff3f91ebe6ac55d98442923267c80f59023d18b9d29cd9c", - "0x65abebec89e4a4d6e674a29f3e5ee304050da861a3a3c91ebe79a3a8aa159db5", - "0x46030219478d3c508cdbfe4cf76ce3924942cfbeb9ec6ac9e83dd5414cc8714a", - "0x4712aadd9407b29cdd5d3f0747374fea712eb3202fd2c13795268d498dd0843e", - "0xdc5ed31d4a9c4fce3f91f75102feef6da9a190638bf1f090df64175b531b1cc1", - "0x077992451f048591ea0bb7b46818c602b6fb1d535ddfab2dfbdaa6731745fb66", - "0x889307e6b877b390b86024c2f48afa3df0c9b375512e38ef778dde91dc950f55", - "0xc6127728eccce59452504b58e10c29ab854712f248d6c90b43360c2786f38647", - "0x76516eefb0f149ab9fbf2dace9fbdd4d435e583b3bb19820f45801abbb8309c8", - "0xa3771e4be252fc17bac8dd8cb9cce1168e531061b59be4fc9115838af2d3217e", - "0x1a0218061cd1c34a080b3a70cbb0c9f66420816a21a2f18f67e5fca6ce62b3f7", - "0x42751bae5dfa6d27b92cb15bf0c490d8184b97fb647563bbb4a7612f31369473", - "0x03bf5030aed483c4d07ba6a5be51ff968ce869decc37fe264b98661bccc55668", - "0xaf604a8c41a8a2fb37e317b2a565afa4e25c289a17b3e7030106849094d517ed", - "0xdbf8ed6fc0f94eddd863c883c17c974dd91f679194d4cc74316092327eb6bb42", - "0x27624ca6ec82b25f6bcaae3b0159c79803228ef5a5e4443498e39e7ca8280195", - "0x19338a8eb7d6884c8e6614b408ff2e5d44ed84e208673910f10fed7f1bf95c37", - "0xb612ba1448b74093a5be0f2ba8d330ca56647eaa2c84c3a1e3d2ef765f430126", - "0x523a7f824cb76717e0985ea2cabb23de5751c67ea03d6b569c30552d8bc9caf2", - "0xdc4f2e1b704ed39f818c36b26df95184c39927246f52a74772be1eebbddc78a7", - "0x4b5e3bc7c2e4f098b829f59ed096527216ea84bf9041d2a3c53c00cb7b5e7a56", - "0x3cec3a6e9f95bb0e2a63194ac6353ac6fe9a12fad00e8f603703454638e8d819", - "0x29b860f98a7239c9fe3994b68019ae8fd4d4009c07a088d23cd70ae6033fc727", - "0xa8d1900bc2c3156c6bc8a41378c0d2c646f0328d4f8b8963f52ac0dd506731ed", - "0xd9f8c709f2a6cbcda62ceae4c5244554af106e7f7fa0837d8887e23887d32e31", - "0xea7c17fa8dbc43b3680741957d5a020cf0df2d491aebd6955bf6027a63f75dd9", - "0xf764705990e0a12e1a97ea2cfe3d5fc0c28a601d1bb6c1bca0d24e61175d7403", - "0xbd5d6454bb6824df5a458fbd1a29476e76c645564c42064eb6cad79237e17a99", - "0x39e99ee5f716670380fcba0312b8b9d902dc3fe4e49e19bceaa4b2f3c2041366", - "0xbdb6860183ab7aa4362223c519791e7a0b63379b87ef4c30739e57515aae764e", - "0xbf6e060baab8b162bdf78e1996e30cdf4d6dd60b5daef909a5e7fada00090928", - "0x1ddc651ab0d26eda2c160911e4be92c8ef843ef510ee28f1d0b8356760c595f3", - "0xdfc38ad4e3225dcdd2dcadf32b024f754236601cb60c7aaec61a692a9f367acf", - "0x326cd0b07c68ed36049ef9ce9af1db0593a26193d9988d21d8ab05f7ffd91adf", - "0xfd76ef987cf758c89c04a221c10e50794132d3866bbf4e739e288a9b3014a9e8", - "0x590f03df3ea2123eac0957d2a84a4a0e0184be0eb75851643c0b876f364942cc", - "0x0fc92d4be9d7f7d5c49f3552d89203090fb7f4a4d34ac1a9925036c84d5d0d97", - "0xd90702e78e72b71a3f7db3510c25d0b26d098da80003cfa4abe3c525e3212d2b", - "0xec682fda87e3e66fc89855bd1ec01a80b515d6dda5f4b2d207cc5b2ac840532d", - "0xa75c0c5ae9cc31a14badd2f0cd66b9c081aa541e4f862ef0ee5aa8a692baa89c", - "0x9c237fd14d83ea449bc37922861be5fe81531264bb802d6a07c149b6883c826f", - "0xab2240195acb14c2cb5f3e6bdd352c4c36765ba6d2d3042bf15b3942b50471d7", - "0xe56052f2729f3d7d08b7958c0630b9c3ca2c6fcd9fd5655314bd1d5ca94e7852", - "0xd774822e8bd746a6b253e9c71f80fa6d6f9ad95bdffad3310eecb98785c549d6", - "0x4e478c8d5034a0e51e1e760fe258afc0c1f56cfa145f89bf34156426ea837b0e", - "0xf84cba0083c61307c45e7caa3844053ca78e2a1ce181edf17041130f314f4d00", - "0x24db4dc31929f9c0521a63d1b6ffd14939130c7d25df7a0827513a6ff0b28159", - "0x969f5efaeff2db44bcebbe23b9ff916a15087a313e814dd9f8140388fa62a48d", - "0xbdf6deadf158fbbe80670ae75744a419b999b747cc0804ec2a81b9082b5a4ccb", - "0xd25da9760de21e6483493f5f781cf526527aa0bc17ee9c9abefebda4e8bc5b71", - "0x83d5382a1a2132d7fa2bd48e06e59243f6ec71ee50626b65a84a7e5d3543f8f7", - "0xa5720e9755e2d04563583af4a2581a8a1761e82eb769dc7695b1f9654e36f753", - "0x97ceee5cb98a9e917223523650405aaa2e71cb12e568319baae1ee75df592fd8", - "0x1e1e8d3d81ae5be21cb8445833d9b8928b6c9a6b9217532b15ce0ba6cefa64e4", - "0x1c3ca12af63f1121e93b5fb602ab8c8ab1773d07bab5a60e78cb8e70e190bd4d", - "0xb343c9f8dbe3e65462838749865a748d4a5eeebe5b6622b56325e2e6680626ef", - "0x04eda02f974da35a3f4308d6b6be16b602d9317256209bf674f3282b2bda878a", - "0x0f85c864e5ac359d9a0ff54a4e0b44a31109c57d7c2171185138dee5638c627f", - "0x476f9f649f2365455d4756f541e8b8f8e34b32f8c67196a06fff2f71abddf5e5", - "0x185b523fa66564ed0e4cafadabe1c04a399b51114f5fb743136fcb2a0a53e477", - "0xc87106d70447c962f367b567b37281e4d840fc16d7b97dd349ecd073d0419cc6", - "0xc0e624f73d54230b99169c8919c965319ab2a93a31923bd22b50350bf0d25798", - "0xd78f2ea276fce38b53569cc64a6ce7ccec2fdf4f1a295106a1f2c54330a0db40", - "0x4a1c2807ccd57ecd989a2b0d4ed2d3ec609449e15c5652cff515ba5506cfb745", - "0x453e4ded943f491b41f0522188e88b28667172202b8a015f113171c29de193b2", - "0x9ea9a96b93c19a00f2238e15bb3c74409eca441e6d0ecb47b574fa8fe7390c67", - "0x9833f04cfbab3190d2d831116f1f0e1871018d862cc2389511bc266756963540", - "0x48ccbadc3d160d322bfc61d44962628b088da09d045c43d028b1b9fb979462bf", - "0x33d90a0c09d27087c15b01228250cac34fd4307d45593e7239096bb77822f6cc", - "0x545e2a8e511ef59a5fd7f183b0bed449d59788e4e377350b56bdb387e82ad4f8", - "0xe7e31fa668cee51a20ad4f2bbd459016639bcd47421836cc1bb3e0e2a4705998", - "0xaf6e1ffeaea0bcb61d5d90fb6d6146bf4f6102f49c3e5f653e1bcd6b043ab3ab", - "0x98bee111ed4d2cbd72725011d5a565b1873c86b8f3e17f16389ee009dfe3c6e3", - "0x2107b120a64b8fe21702af4bd26e8d981fec6d0051c83046bfa5dd05354ad05a", - "0xc982a8ce403f43557d0a812b8be06827d88e2410f6e7920971972fb205bc8a4c", - "0x5d58c764a39ccd3edc5e1b37f3ec73ddcfafde249e066c6185712c0d566b2e33", - "0x9afff9b16248767858ce0520877d7f414764eae8ad77d9810d615540c119e476", - "0x6154b7412dc1fabb80fd7ee43a3b91651e07b62d15afba8c004611d8ce2a88a9", - "0xfa859b6679bd6a2b283ad4ba43b8b3d96b19292d017e933cb307100e0e2a9c36", - "0xf32a85527d705fa439494f314800609a6c5f6657b205b0127b64cd595aa2c31c", - "0x02d1fe3725fad05e13189d6d9e078af8c825bccb5775dd1a6c90be3b57081d02", - "0xb003929860e22e0a4bebf5be75be8ac86a511e3f9e9b421aa9a99fe0fa4d9144", - "0x583341c3a9526d29c43bf22619df050676fc5e56974a8588a91bfef7409818f4", - "0xb94ebbab275786dbeff9189ca740bf324f916c76b800da87e6e507d546128ca9", - "0x5f26114f75c4f839fd6776b3a67b1a74fe6afe5fee1d3053fc5b09b38a2f31f5", - "0x053d6e94968bde9f906aa52065c0626810d138908f1645f2e563d3aca9660276", - "0x458fdb60ca1d0b7ffd846744ee6fc7219ce6700b4e5cd97603c4885563a0d14b", - "0xdc09316757c1ed63b9d984647db8fb69fc9e8e6153424a6e99016f868dea613c", - "0x409e2e26caddccbabf10754bf500262fca5aeaf6ebc937062e1d59b087873a70", - "0x288ee1a1e4b4437ac97c7130d4260bb0298ed2038b57c801fcf2389762a8dbe9", - "0xb82f496c7c8c8d2847ea7434e15c61883f4510e3151f56636394d88e902e19de", - "0x5fe701b2ac79e258c7201cadcf7892cd37e76bf9f7f9b9b46d39ba0fd359076c", - "0x32a841cf4e27eb60b5a6825665cc257fd1db222d501b42b5c6a814cdcddc1038", - "0x5b76c42acd108597e0f0ea8c92acb039a047dccc7f294205d1e404981bcffe0c", - "0x7084da3532f91d33a42701d9c580905f4cbc4137f33db33a579aec2ce5db1c53", - "0x11e58ad87c481e2d1b1583bed5b6e71ffbc1114bed29b7ac14a75d852a4741b2", - "0xdbbe146f3e58fa32d465a175205d757f18929cea4870bccbaa2698c58cd32f6c", - "0xf1dacbb951e0663d5db612046e090c04f18bbe31271d3d786f20deffd44b4205", - "0xa11dd2266cbe0af5b7f1f7955c28fc43a88abb8d82ac18e835723c85e8fd2d22", - "0x716ed679abca531ed520deed7b6d955e1c07c5ea483cb57a2322ed8c7a5a31e1", - "0xca4e5b466b978777d7a60a8bd841271da8643bdf0085379677131bf4b3604f33", - "0xe3ab5b13f5cb1bd8dc38c5182a7f16c02abdcae7a310d6c0233b96070d9d30f7", - "0x5f29236e49036d429f283d25a2e4cd6ee8f01b314201a34713b50871ac1ab628", - "0xf1e41d548090c62f207208b575e0bb7b4eb08c30abe40525effbcbb8b5511482", - "0x088fcad31ede2ffe1cb9e67becae368ba3be97a02d4837be59bef650f4b90e4c", - "0x4f4cadc003d445a2014dd5f819a2b67e67620a21af8302469f9ec264cee75e2a", - "0x23b331796e1c5c95a701a1f791ca392da953a7a9d5d2a834d0cc941ef6567250", - "0xb147414487aa60ae8ef7aef8577ce50b4a750b0ab77175aaa39fe78ca5d2e12c", - "0xa2250e2b126ef24cb732c79cf87afc5186b88402b70e0c9b7d3139205c7dfd09", - "0x5c40cffeb999c74452e77145e22c3c1d1080deadf4f49e40cd5184c683f628d8", - "0xe2f8ad15a61579535dc9afc59d9ad16308b80e409fed46af55b7938856485c33", - "0xec81eb466fd503be4a807bad9b2d0b6ccc24d1dce3f82f69adc99dc81d69a3b5", - "0xb87d20dbc44c1a473048e96a2957a4b9fb2ea606cf7b3de9d44937d18695f0d9", - "0xbf0dd81b0e07b07b22deb6b97d5cbd1d03a2726a1e7bf9f795c8104d1ff1c458", - "0x6aeed28f1590a1c96f1085a1867a4957673cbb8e4251e5876a43182e7b40e4ec", - "0x24df74cf385560d990f59f8c59d3545662f71b42736bbd1c1361b5a1c45d2b47", - "0x881647b453a1e3ff0b6e64a3667db62add41599f003f9032418576323a5ce1f2", - "0x9fb9c9bdcfcf35e02f0029f74b86d131be7cb49f409055cc075008e9ae33bff5", - "0x5f17c4c7b4d1b0cfd3929eadb46bf1060b8394f6bd5c0095623f73de5f8acf03", - "0x63c4b4aa80474a3cf70243f3a2d891266760f6f54693f4f189c0aa9b3d6302ec", - "0x97921aed12f6d78f6052050f7401caca6bd07cd643fd7a30f681a72a45b64a0c", - "0x82e6c73da95617192ccbc9374bbdec4c4323f8f63a27197cdb52a0d73b88e16d", - "0x3e24fcbe6a8d8ba769994150cc7a17dbb38d2248af14bc8328f729f4e01fe173", - "0x8390ef908e1dd84308347bdecd7f7e1f176551e291c6b8851c1f90d0ee4d87a3", - "0x016143668addb39c55a8c5397cf097567faeec1787643a89f8feaf817ea10d5b", - "0xde78935eace5f67e88d141d650c35b2b0c5144b12833a3c4bb64441aca1afc23", - "0x129345fd67f04b97d9e76fc9758c80b0616a68710f51decc61f85fed9250396a", - "0xcf6625191fe20056831cfa3839cd3f8fe60d17e6e4248dc46ecd35401986770a", - "0xda56c24559455221d6e465dc3385bb8fe8b547f96952e07647e46e10127873fb", - "0x85045ce82b529f7055bb8496f4084283edc86b6b79f0f1fa060ed9050419c542", - "0x4b94dbf2a96a4749a04d47fe563644b7f396f4c81753d8ec3a70e5065df0a3b9", - "0x4b025b46575b54136db392c9075b5973a421fb521ab37d8760a04f90b4f8526b", - "0xcfdf125601a1939cca129c4d38dc89a6157cf0a51400bfd8cbc1ea9a3ac5e859", - "0xbb9b417a9d9c975a1707778fa83950a2a6f6c7c20620e623e913509cc256c26c", - "0xc2dc9b5d9413508462391fbac33d1d4ac2fc4b3167d152d0888c5ef9b18a9bf8", - "0x918d35dede6db7175e5a11d9c26ed38fe8198da231d3955b1eee020ad32fa3d3", - "0x12515f32629d7e71e2f1ef0de1a08ab2ed2cf9cb2ffea11aeab06e7dab64eec8", - "0x3484f89e98c0436f603319b8c5d5e324f3e2cb6995be3334641e14a21e998b21", - "0xb045c079ca13a4a6760bae38dccad8b35503437c4643b1180f99b2bef15b9eff", - "0xf81cb06b474d642ef15ae0382f3d792071862d4a00a967ed352fcf5fc55529b0", - "0x2edc394894bdc21c9827c789cd1f1226459e5489f7a87dd941efdcee56d0be78", - "0x7a1993168a29b0869fbb337144031dd0bc47520ab9498c9065b71fc90a100294", - "0x1963f11d488f5db4ea7f6e99b0bfe5006702e911930a7a95fab8a45ea1c4c955", - "0x7e12d33b54970a2fba8539d57c02b8318fab2f22ceb8b0ef69b6cc9ccfbd0658", - "0x32655b99bb59604c91af6270b15834688de512ca3088544785c0f0304d1faa20", - "0xec8984da3bc415604c9facb5cec0238f162b3da22fc4d20bcfed387c647728fe", - "0x7ba1756f8fa6637d8cb6cc588a679f6366827bc2044865744837c63e8a5aadcc", - "0xfc1a94a88f1e52bb78a0862eea4b996444f0264a3b301c1a31e59744d0216de8", - "0x79472bc4de84a0c430f15bcf8569486cddd2de985409ceb8e48abec8aa3e3fe7", - "0x02bc578dcc715304c651a43d8dcb4a761d2073a93cca2c39da3ee513f0c6d415", - "0xcf6175cfa60c20178241c18111d64f8837ec008751c5c333bee96c616d3cb6d1", - "0xd8281202c7b6da2a2541cd7f8d4270066f6546bec601e83647896ebdf0f2eb66", - "0xc4975ca8b2b71f61d2ba2825833df91096bd733301d54a3c7b6eca016ac14153", - "0xd01d6f14e93c8ba17a602378bce1c9bdb51373675109d6233c497b6c638a03a0", - "0x805057e19b7dd857ec014b4e6333bb60f48c02d29b128fe37a69f952df8ef16b", - "0x69c55bdbaf74bfa3d53ed32b79dd35ac2c24dd723a1c0bfd70871cefa4dbb8a8", - "0x523efe8059e6e8e2abd85d77d22123f97cbbd9864b4a3ac1fe0c0e4b8a7131eb", - "0xe5a4f129016bcfbd71913d1eff01cec924f9d130b8762c921d43fe5bbbbdbbd0", - "0x07f68773d16907348ac6e7c5be293ee1163ef41b91a2a8abb17a004c4796fb42", - "0x90742f88c71f9e689b6d2ca0236c48bba0e8ddeace5fa4d53a5af082055cfd73", - "0xaf7af6e9f695c4ca9e9a3c0229d94cbf02fcafbc4ed978a6b662862182308a72", - "0x0208eac99df5025cab13d9767a2ba3e42ba1eccb72cfa01d87e834d15eea3751", - "0xee9ff9384cef76b51a9d5b8eb12fd7e00ee7f8142f81bd68393e7fcf0eaef90a", - "0x4770986e657683d901d73df444836f11f169f94ac55ec81e4e19f2212052788c", - "0x9f9c16d009da6e8ee20d9d5f3bd7f1a77b853dfd19beef68d980fca6bce2b707", - "0x468efd7c8dad29cfff0e278fd950f89e2d5a57d6824c6c1a7a7659bd7e7c0ffe", - "0xd0391bd0a2c1fb2260d89bd5e519d4ef26b4945a7ad0c1b695687fe1b24698dd", - "0xdef9ee668e09f8769b2c706a8de426e06f0bd9c264e67bf0c0bb177b851cef23", - "0x10c5b79048e611c6e65f92dd54754753e595cd8f8f0f1005d7a647fa59deeb78", - "0x4ce6d5812d2565237a9b018ed6ec386cf3e9fe1f2f429605288f2ef744c5a940", - "0xbbacf6662d816d5eb14fc1271c1a3404de94e12c175545bc8273c231c280b789", - "0xb216fd154d3726f16907ae571728d92ae2199872f180e4cd6be3b53a8dd43ae0", - "0x2b76e6685604c69f29399451c66ce053ae033ab011eb52c97c112df968e37232", - "0xf93b8076ce0dc6742196d2c9af501fad39f570cd927e8fe3b8dac4127a20a6a4", - "0x35027cc654f998af75b8379fac0a9d51703f058b518880e25e658151619f1611", - "0x3e7f59319228d40e5e07c06b696ad6478237ed0c8a71cff418ac259c6b5579e4", - "0x7295e21fccabab1545c1e64a57f0401e62689057ab4fef26f70c8d02b63867ab", - "0xb06bcf07fe1345c41ba16df213d699e2e185ef2fd3b5e19b194459d0b7baffc1", - "0x1b13d005a29e35fff33373e2d8abc81805a85e7970b48dca8fd605069c8a0d85", - "0x90a5a61ce554bb73253d7f62c78b1764f03fb0fd8f65b6aae4f9e529acb1d991", - "0x1b1eccf7565339748947358caf473ba0cea743da2ed4790763d3ac8c402f261c", - "0xef9b05ae7ce93febce342336e40922e0884891da43aac06f3e9bbfc0aec688f9", - "0x11e1075b50de92e960d0d65e9b37b3cec81095f5afc2afccc6a9a8a325264049", - "0x128e6dba4205701f673dda9e980dc0c02f3c0c8cbc570f9a240257a261f81b39", - "0x6fd9d3eaffaeebd52ce93ac8fcd0f69b146e2c0b05218850de1c9218db8ecd8f", - "0xc47e3487ad63b7db773f413cd5302818061f168409c27ff2daba4579c0a4263f", - "0x0a55695d4a132063735f7319d3a90a2e0b83fd6ec6b6f9994068f928f68afe12", - "0x39403d84aa319edbb18e0313a61ae075ba9c5dae40b9850eccbfc3e09e6b27a7", - "0x79ba98eede4cd0b7031472baacab20df85c704a26e4cfaa6e01e3d4a9d26ca39", - "0x219240759d36a317f269e4301256164cb3709628e0ba07e90a6bedd664cea059", - "0xec793562f8498937f270c6ae5490ab8f2ad21a20510699dc4d7d4d0139efeb3b", - "0x0a3e9c209903b906013c8876cb4292e6ccc8b76dbf4af2ca25d8260f9f8e7c22", - "0x0eb347bb16d897b4909dcaf31a0009e198cd197f722075c112fe29bc9f017f20", - "0xac1e6a97c51f5051dc8f3a1fa33c1071e29aff3d7040972dbb5481ba215010b3", - "0x7e1be83ac51c7e68110c6984da43885af18bc9fe988a57ada7b2379d0a122563", - "0xdb01b1cb4084a43c9a57bf8a65a58245edee5bbbdcf0573a3626e9290b4a2e6a", - "0x6c97523c89da4c9325fe17d2e879bbd6b4693f3e6d807564de83ccf801e66737", - "0x7511c0b30bd4dfa6f9a4f38793ee11e2a616de5d9d2212024c0875052ef2559e", - "0x6faa338fd78b41f2ecb623d578520b373e0c74caafce95938161dbd95edb0e24", - "0x0622bf3e00fae0e244d138f73d16035cd05aa9e0c4c16d20b36adad77951c2d4", - "0x72e1ed876e0bcdbdd200dbd55a9d7e8248a4c7ca75588681b61f6f76eb980060", - "0x981a052ee33c36c52fc33598c2563ff3698e1efd130090658cb457bb814ecff8", - "0x2b732d67211042b340c1bc49682518bb00dfcaebd0cfe861b5d38df9c127ed64", - "0xfcba6702d24950030403f872be646d058039d735cf297fcedb5daa255d47ce72", - "0xba3bdc1bd47ab43c2d39ee67a512f306e987b577166d7037705a1fda772bbeec", - "0xa39705f9efc0c0b9051742b705dab9fed7a08b3a00ff15583e15de95a9933356", - "0x551658242a3237fda7b61eae6dc40f368ffbbe3e7f292bf1b3d291d1f21a631d", - "0x631783a116fb5af727d2617276bf310e9f018e1f072d9f3d5432773ec29a193a", - "0x7d2bee7cbf5aa7a5247f047b1147f55380e035a5ff1ce958037d96a539fada37", - "0xfbe38f5210d9fc7c3474be4ff932d1ca393a565426f5d1443537e962beabbbca", - "0xfc9b71f941a0a9dafd6356c4a42c369f22144b43f32f5d50fe3d4c1cf3ee3cda", - "0xa4eb11bb0a0e210af085dc446766f4bb0d19dfec04e9d5d0d2c0ee390597906d", - "0x42e8e9e3d4d6de275146369fa7c010a10979793276a4406bbfd77a543809e762", - "0xb2fef38ac3b58b498cdad7d195bdb3604ca0cfc0ca3c7282163636d3022def43", - "0x923269e9a526ddaa446a2e7acaea5189726ff77e664c20a341f6ad2f6b810d5f", - "0x02e876ba6df7c9ddd7a9ad9bbc15f5918b096e299260fc85a8c7ebaa71d7dde5", - "0x660442f393884c6c4b879168ab3c80c57318c6c62a67ba8df3788edf9ad7303f", - "0x37f7ab248fded056ae5ecde87bf51c1f4baea786fc7c9f5b6760ad13c90659ac", - "0x36fda244e2896e45d3a8a9ed67d7c09a2974c75944a7233f571928a497b55f8c", - "0x4914478536196c3c813fe151cdf37097c6dc9e8d5c0cf9b370fe7567905fc4be", - "0xcedb88613a1d6fbc24909963911efbc5ff56ba09377350b5e824123b50d1a910", - "0x236fe31e63e426111507b2f972547255bd12c6f5df9079953a8b8346179bba57", - "0x2f08e1b0ad4024814428237b4f0e7ca5a83257d25a1cb97f42db14307113916f", - "0x41e45a3a2239551e8cb9598fb855f3d8922386c2e004426e294d111a94856b92", - "0xd5d385b54446abfa8c78950aacae52edede3a7a65d9c7515043431a4498e4f85", - "0xf2d2eb5169760d81d16067b9453979783a2feea91dfa253baa3183f2419478b0", - "0xa5aac8d064c4f4cdfdd2c7f7326c1e0260aeb44be48b391616eb01fc6d30c95e", - "0xb2a298085b0a082fc1781aad1abd263e9cc2c5eeeaa70acd88e8bdfbc0be7d15", - "0xe2d19c438ddb9b96e77a9bc70566f49021bb6444569eea3cd039ecab4d61f73a", - "0xa8549fc117d3faba2ca53efb94bbb37e9cb82bed8273e408da5ed47f5df7aab4", - "0x161c898240dd6e0a0a1f1f5948c652a796bb6586995783a35e7ed6c3c02fb82b", - "0x57214b17853934f1a548a7e9942849a04b9220f5b783a9d2be7395108bdc12e7", - "0x7902ef6c69f5d0a3de1e45d8b46aebcf35aa87cc5f4644186f1c34614a429175", - "0xc290a119f495ff04f406c28461e2de50abf92743b7fdd680048e491044fc59b7", - "0x09bf0fa4aa8b65dcc8f937e46287eb2585c85c587b425f8a0af69d02948f35d9", - "0xff20130699483dee0caacca392c2ad58738f61ea2443869616a3bf006e0cd1ad", - "0x937c629f7307f7411a4b1cc5b92f6a1f1f42f716463f15c449c681d16525996f", - "0x7cd028f48f117d8da3d537f0bb0fb0c832d3111d16fc116bd06454ddd513a095", - "0x4bf15bb783cf917375b5494e00ce003d8da01c65fcbac40d44e62a6172f2cca8", - "0xaff5ceacf024e9146631c772e4e87f223a241e789c88ae083da716c9aa47b860", - "0x68c1db9a727678d2018ec98d0bbc369faec2fb1283b479abd5d6f2b1b04036aa", - "0xe70962a032b4cb81910cea9911eef50ede2eeaa6f487f1e9f8fbf88f133249b1", - "0x1bc2bd4440a1412fbf582d167e0d08629ad2bfe0bc0dfaeb5eb7730a4baf007c", - "0x2d4cbafea30b01e466b41f6911deae979f4f5c82dcbdd76f711f7bf9300c23f4", - "0x252e39a51862ebaf28d9b38f9fb842c70b5bf0669ca008b4c83c542e5236a1af", - "0xba04c89c7ec2867d778f8632e7f51e11f06e56004520444ca922eabdab623473", - "0x74b8dc3bf589a6d9051fbed9af3021e8fb775eab0f457cfd5d38b2ec8f88a24c", - "0x8c0a98cd1665f50b702b44e10dfbfaa70f08b65ede72e39306b1d31b75c07afb", - "0xa38c3526f5f6bce7e64a195cf8659160bc7318b8862ba206535626c338485c64", - "0x4ad34e3c6be8cac9be3fba22eb7e99d951baf5827df5ef921f2b01d63862e116", - "0x596670c729beb030c8756bf2ec6c884f9b4edc433a94f5dc5d4d337dbb712d76", - "0x39611d27f11938df810165987ee7edbe87cfb7e4068216cbb45848b4029f8419" - ] - }, "accounts": { "0x0000000000000000000000000000000000000001": { "balance": "0x1", @@ -5329,11 +145,13 @@ "0x0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", - "activate_at": "0x4d50f8", "pricing": { - "linear": { - "base": 500, - "word": 0 + "0x4d50f8": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0xd751a5": { + "info": "EIP 1108 transition at block 14_111_141 (0xd751a5)", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -5341,11 +159,13 @@ "0x0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", - "activate_at": "0x4d50f8", "pricing": { - "linear": { - "base": 40000, - "word": 0 + "0x4d50f8": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0xd751a5": { + "info": "EIP 1108 transition at block 14_111_141 (0xd751a5)", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -5353,11 +173,24 @@ "0x0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0x4d50f8", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000 + "0x4d50f8": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0xd751a5": { + "info": "EIP 1108 transition at block 14_111_141 (0xd751a5)", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, + "0x0000000000000000000000000000000000000009": { + "builtin": { + "name": "blake2_f", + "activate_at": "0xd751a5", + "pricing": { + "blake2_f": { + "gas_per_round": 1 } } } @@ -5367,6 +200,9 @@ } }, "nodes": [ + "enode://f6e37b943bad3a78cb8589b1798d30d210ffd39cfcd2c8f2de4f098467fd49c667980100d919da7ca46cd50505d30989abda87f0b9339377de13d6592c22caf8@34.198.49.72:30303", + "enode://16898006ba2cd4fa8bf9a3dfe32684c178fa861df144bfc21fe800dc4838a03e342056951fa9fd533dcb0be1219e306106442ff2cf1f7e9f8faa5f2fc1a3aa45@116.203.116.241:30303", + "enode://2909846f78c37510cc0e306f185323b83bb2209e5ff4fdd279d93c60e3f365e3c6e62ad1d2133ff11f9fd6d23ad9c3dad73bb974d53a22f7d1ac5b7dea79d0b0@3.217.96.11:30303", "enode://56abaf065581a5985b8c5f4f88bd202526482761ba10be9bfdcd14846dd01f652ec33fde0f8c0fd1db19b59a4c04465681fcef50e11380ca88d25996191c52de@40.71.221.215:30303", "enode://d07827483dc47b368eaf88454fb04b41b7452cf454e194e2bd4c14f98a3278fed5d819dbecd0d010407fc7688d941ee1e58d4f9c6354d3da3be92f55c17d7ce3@52.166.117.77:30303", "enode://38e6e7fd416293ed120d567a2675fe078c0205ab0671abf16982ce969823bd1f3443d590c18b321dfae7dcbe1f6ba98ef8702f255c3c9822a188abb82c53adca@51.77.66.187:30303", diff --git a/ethcore/res/ethereum/kovan_wasm_test.json b/ethcore/res/ethereum/kovan_wasm_test.json index a4e19b5dd72..70143d8a8de 100644 --- a/ethcore/res/ethereum/kovan_wasm_test.json +++ b/ethcore/res/ethereum/kovan_wasm_test.json @@ -60,9 +60,48 @@ "0x0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, "0x0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, "0x0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": 5067000, "pricing": { "modexp": { "divisor": 20 } } } }, - "0x0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": 5067000, "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0x0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": 5067000, "pricing": { "linear": { "base": 40000, "word": 0 } } } }, - "0x0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": 5067000, "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } }, + "0x0000000000000000000000000000000000000006": { + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "5067000": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0x0000000000000000000000000000000000000007": { + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "5067000": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0x0000000000000000000000000000000000000008": { + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "5067000": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, "0x00521965e7bd230323c423d96c657db5b79d099f": { "balance": "1606938044258990275541962092341162602522202993782792835301376" } }, "nodes": [ diff --git a/ethcore/res/ethereum/mcip3_test.json b/ethcore/res/ethereum/mcip3_test.json index 4fafcb09d74..aee04b36574 100644 --- a/ethcore/res/ethereum/mcip3_test.json +++ b/ethcore/res/ethereum/mcip3_test.json @@ -120,38 +120,44 @@ } } }, - "0000000000000000000000000000000000000006":{ - "builtin":{ - "name":"alt_bn128_add", - "activate_at":"0x7fffffffffffff", - "pricing":{ - "linear":{ - "base":500, - "word":0 + "0000000000000000000000000000000000000006": { + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x7fffffffffffff": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } }, - "0000000000000000000000000000000000000007":{ - "builtin":{ - "name":"alt_bn128_mul", - "activate_at":"0x7fffffffffffff", - "pricing":{ - "linear":{ - "base":40000, - "word":0 + "0000000000000000000000000000000000000007": { + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x7fffffffffffff": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } }, - "0000000000000000000000000000000000000008":{ - "builtin":{ - "name":"alt_bn128_pairing", - "activate_at":"0x7fffffffffffff", - "pricing":{ - "alt_bn128_pairing":{ - "base":100000, - "pair":80000 + "0000000000000000000000000000000000000008": { + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x7fffffffffffff": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/ethereum/mix.json b/ethcore/res/ethereum/mix.json index 7cdffdda273..ae247a92f83 100644 --- a/ethcore/res/ethereum/mix.json +++ b/ethcore/res/ethereum/mix.json @@ -48,13 +48,23 @@ }, "nodes": [ "enode://aeb6070deb50efeb41c5e4283a6a3b08ff701fef90e3522161c145f30df2852af3dfc51ba74591f7c9d96b11ca4c3c2b354bf58dd243f2d877f6eecc2373fd1d@139.162.15.124:30313", + "enode://cb4b0568607c97a3af4857700152fad9c79908a89319310268c7f49184e3f7e0c88c8d3504696f521c8aafcd00644f9325f76cba9427181b1493ff7ce230df00@139.162.15.124:30303", "enode://e0c926dcdc5c1cf58b2ecba371c577c28c28c91f9b210093178a812389b65e5b53f0e478753b94fceb0b36645b779a915ca57c0c48507fe4d7f786508653656c@74.207.240.177:30313", + "enode://37fe8c0f8d667f110316de3109dcc2e9c9247219998e9ac395db39511bef697f9646f73d86a43da491efce35458f53ac3ac0994b85f7086bbf244809d4e7eb7f@74.207.240.177:30303", "enode://a2a2adb8c12b9b189306050013a44f28db30f92fb3670db9675a049b98b96eb18901d6ff7b961b6e96cfa3923ac29e8f647ef452f0a23ddfef3903ac1cf826af@173.255.195.214:30313", + "enode://ed6fea8e7389bb75ff34846e4aaefa9157c9c79828c6404f4a0ab6b997a613f777aecaed5092c3f51abdf918926c925f7ee4f4cffdb0ac37775d03070a3d6b55@173.255.195.214:30303", "enode://5460fd1ad217941befd0f8d060e6729a0535a0738770aba56827d1313c09aeb68e3098d458aace59faba2c6780b8c9c30cb140b80cd8e30ca3a074ce6d3344d3@50.116.38.52:30313", + "enode://9109e01dec60b67afa5aea77da17e0cb9a716a547469be378eb122c545a3fda9082e553624ff9e673195a715d5628ebf046a6c4ff315e566420e3bdde003bd9a@50.116.38.52:30303", "enode://99fff4ed887d6a6a7b6e03a657c35c06d0eede1909ec289a362bad9d37dd4085886461bbce83aa484ce1327badb3c5958365caa851d71de49dc4530e075b64bc@45.79.128.151:30313", + "enode://4b4c06445175b77ac07eba03ac624f72e4e86065ee8bbdbca5f882a2a333e2608e6ef2ae5b5779816abd8d07ae6ec08f75888edeab3bc06f2b75871feb14afef@45.79.128.151:30303", "enode://fd80e04c75559cfdd9ed8c08ef2c39c5bc95021f7cbaf31acb601914bc7dac7c34b470b90a05e519bc8a8435a46e1ce51053ae07fac31a83567285c34a79c6bf@139.162.224.203:30313", + "enode://d2e678247450c0a7aefdcf03787d4905deda2a6889f02071b241a1309a6bec6ea1c8b1160f69635dee0b4e00cc83656a601b4528cccbd85744b811654ab24b13@139.162.224.203:30303", "enode://4742134a153c108855eb16563424887ed3aa5b6b74e4b713c8e93a10c376d954ff3041442716bdf9ee28fab2ea09f04d07e3366f834ea472c19820b7337eb27a@172.104.130.233:30313", - "enode://799d0a8836e17ef7fcc58b3d5ced5bb1fe474b31a09851f938d381f4556fa8954ca308f6a178d22ed56769a8b878ac8f9cc62c889f9cafab45a3bd4f6024bb29@172.104.68.7:30313" + "enode://9aaeae0129af1bbb2e3590a71cd1ad0c320aa1c03e15c9eb3563cee2d8a7ca43473f43197b6dc0befe5bcef6185196360fa8b4b0d82d8ef11e2ff65553a1efa5@172.104.130.233:30303", + "enode://799d0a8836e17ef7fcc58b3d5ced5bb1fe474b31a09851f938d381f4556fa8954ca308f6a178d22ed56769a8b878ac8f9cc62c889f9cafab45a3bd4f6024bb29@172.104.68.7:30313", + "enode://ab7b1aab2439aafadcb52f8353e60fc1eea55ee5a01b4ddf46ecdeaa2e869c4bf305249757dc74baa78cf05c5d98ffe5c2a008851f08cab6096c78a08dee7c17@172.104.68.7:30303", + "enode://ab4a7fb0963e4951bebbefd2ec09ddac018bb27600a6063d755dfc10be118cbfe3954ada561afbce4cbda6f62364c12902b9e6bdef258aff36b8a73db3c0f161@172.105.16.240:30313", + "enode://30ff1bd89f58a1adae1f2b2ed1e36fa95caf5c2c7e085cf3a49f14705427ca7d7b2508060d058ceda57c708e8aa4661304f3c17eaae4da2e9cea6b64b3893c2f@172.105.16.240:30303" ], "accounts": { "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, @@ -62,9 +72,48 @@ "0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, "0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": 3000000, "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": 3000000, "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": 3000000, "pricing": { "linear": { "base": 40000, "word": 0 } } } }, - "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": 3000000, "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } }, + "0000000000000000000000000000000000000006": { + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "3000000": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0000000000000000000000000000000000000007": { + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "3000000": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0000000000000000000000000000000000000008": { + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "3000000": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, "97c7f4f8f0bbf384578a9f5754ae73f37ff49ec2": { "balance": "55000000000000000000000000" } } } diff --git a/ethcore/res/ethereum/morden.json b/ethcore/res/ethereum/morden.json index e626152adc9..d9fef8e7105 100644 --- a/ethcore/res/ethereum/morden.json +++ b/ethcore/res/ethereum/morden.json @@ -8,11 +8,10 @@ "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", "blockReward": "0x4563918244F40000", - "homesteadTransition": 494000, - "ecip1010PauseTransition": 1915000, - "ecip1010ContinueTransition": 3415000, - "ecip1017EraRounds": 2000000, - "bombDefuseTransition": 2300000 + "homesteadTransition": "0x789b0", + "ecip1017EraRounds": "0x1e8480", + "eip100bTransition": "0x4829ba", + "bombDefuseTransition": "0x231860" } } }, @@ -26,11 +25,20 @@ "chainID": "0x3e", "forkBlock": "0x1b34d8", "forkCanonHash": "0xf376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145", - "eip150Transition": 1783000, - "eip160Transition": 1915000, - "eip161abcTransition": "0x7fffffffffffffff", - "eip161dTransition": "0x7fffffffffffffff", - "eip155Transition": 1915000 + "eip150Transition": "0x1b34d8", + "eip160Transition": "0x1d3878", + "eip161abcTransition": "0x4829ba", + "eip161dTransition": "0x4829ba", + "eip155Transition": "0x1d3878", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x4829ba", + "eip140Transition": "0x4829ba", + "eip211Transition": "0x4829ba", + "eip214Transition": "0x4829ba", + "eip658Transition": "0x4829ba", + "eip145Transition": "0x4c4cbd", + "eip1014Transition": "0x4c4cbd", + "eip1052Transition": "0x4c4cbd" }, "genesis": { "seal": { @@ -68,1952 +76,59 @@ "0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, "0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, "0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "0000000000000000000000000000000000000005": { + "builtin": { + "name": "modexp", + "activate_at": "0x4829ba", + "pricing": { + "modexp": { + "divisor": 20 + } + } + } + }, + "0000000000000000000000000000000000000006": { + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x4829ba": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0000000000000000000000000000000000000007": { + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x4829ba": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0000000000000000000000000000000000000008": { + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x4829ba": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" } - }, - "hardcodedSync": { - "header": "f901faa0542ffdc248bf2c071b74d84e73ae9a2b8bd11bd6d202430d0f204cd88cb3aa76a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479497b4ec458abf3a8f88a206ca43c8d9a6934f6f11a06d11abe2a71059fa61264bbbe9a34b1f5be8fb4ada52a705637f8f110236f1dfa056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000833b7d7e833ca0018347e7c480845c9d6a1480a0845d9ab9164c875ceece68e674bd2369b9f9eb7916ea1e23f439f2b3a6904939880dc60854f15ab6a3", - "totalDifficulty": "557317515715884", - "CHTs": [ - "0xc807dd41b69a19ac6c380c2a0b4823c3878ed073b43cd978e2a77a64df7f3ae2", - "0xe5f9137adaf6d6d8566cf206c012248bf24591578436d990789adebbbf57cbad", - "0x23a3c9bcf4eedd6c0c515aec8c30cf1cf769c829a30ea31d267b07b5e199a7f2", - "0x6ba3c43460794f494efd505fea3a2e519513f199ea9148c9e2d106ce42041128", - "0xa9266cf07f4bc9cc436829752e4ff0581d7a6f811d136d0a43a213eadafd6213", - "0x9f9668d12083d72e1309f0ea9643f721ad8363464ea8b82c97a10f46f455c281", - "0xe65d639f27dc2a1c3942e6685618699f1d6bc880fe027887f62028a6baf4aae9", - "0x102089b9a2c7a1d7954cb550403ec61da257fdc244d43c10d3ae0071dd9a8b8d", - "0x05b953b7920f1ab12d8da8dc4b885ea506bf6de0a1da696415c7e181c9b4b4ee", - "0x811a33e154c19dc735c6a88f59860b33f0022d453d8073ab50437368eaf95654", - "0x33123d65bf01fae8cd8ee499737deb366bf22805e3ebf0bd085bf2e8a6839676", - "0xb5f7aed6d5a438494df68b4e408fdc11beb8aeaa116241d08ec59a0675f547f7", - "0xa342b40b1d75f398d9973a5233e5073a8d0ed46c3ce56390d37abf3856716c58", - "0xf596fe04fa052d40c3f163e895a271b84d33f5433ae3756d0eb3c4e3f736afec", - "0xbf740fe291640e5efa787aaf5f6e5c1e785549f64e82fc63a5f286c71fa2416c", - "0x40800be2869e05982f1bcb20e7e69e671b9983201993c731d9719446384dc8a9", - "0xa2e55c78e611d6f8b66090b7b5d503624a3283c3ec8d2c44f0f303fa60565371", - "0x259a40837fa15a1ff862c673efe47e8cc6d3357d629a3ed541877ca53a23df4c", - "0x8720da23454e67059c90d4d17e5f593c81c7f39fd03fab1450a6f37d7422f3f8", - "0x0ce848313e625f8dc5c47cfb6a724d2c1a31d93c73a50e249fc05cb8e9ed037b", - "0xfcfe0c8034e349e97371f7212868b2accb215c5af01588951404cf72eb22baf7", - "0xe755886383ea0cc4d91adf37a307bf4e1cda6dac9beae697016799df4c555c25", - "0x163968f7d35ac07bab096aeee286446b69a4f73d5adb3f86e0159f0b1ea96697", - "0x4a470e0bbf6eb96388d6e737ae12acfca3e43189a83dba465e58ebe9149eeaaf", - "0x1888c221c04b7aa45a9861f1de385830f6a571e1582c10ce77974fad24e81212", - "0x854e426e6cbaa0ff1607fd5fbc0a66359886e2dbaea606440e1d914f460f5e99", - "0xafe3c2e3d8dd08fbcacc2e4111dcd5673a7fede0e4dceac6b0e31947cf425932", - "0xef40d19cc969283d769845f483c76cdacfb70d9d546281d2febef109fe4a844b", - "0x8729785f16e26626dea3a212059bfceb66f0d5e6a188888013817bfb246cc152", - "0xa66ec51b44a6b7f8fc6b2347e1f4ef93a7edf1cec752693e237e3c6ed853a4bc", - "0xfb2720906e0da3d302881963289954200c6dd2578800c712ef2db702fb2b1810", - "0x030ddc8f72c3070b7c7303c71138bf4fb1b638b1ff54aea16a304682bbbf5267", - "0x7892407bc680352974e4c64722e3be4d3a88dd73275d3176ab00e8b7bc427753", - "0x991e25dadfb80022c044cfcddc5bb814ccc6b7ba46efbee757236e467f8dfe57", - "0x37d1d97d1a440b6534d8e7f8997b3993e804a384b18db0fd1142fd2ddec9ff8a", - "0xccce382a99c1fa1062468b9a1f444854bddb36391536ad87a433c148bf908cee", - "0xfbc43e9f5a631c12c7fc73813be957dbdd2e91a33d8f6c928f026464e8a710ec", - "0x152c55912b4a751800117c2d628cda1ef651470d9236388c7974a6ef7653129d", - "0xacb187f272f5eeb3ee36d3f7d0f4b6f9fdc3f4cb701984ffd4023f9fcb2dc9ac", - "0x79b52e935e6838d3ab159aa7454c15431a63b09d554c6650c058c7c014ac6001", - "0xdf2a1ae101b560ba5e34118a748a28cc5241c9a51b5c8343c0e8912904b82774", - "0x2c1ace44b890710096696cf715851b77392229644ac8dd6a3497b46ec383dbbf", - "0xdd2da6958538e572ac83dfdeee1714650a5065ea1fafc0f431539ff38616491a", - "0x599c359830f0cdf99989cdca29e96263b9b760ff3c33be905f4257c2e5301bd1", - "0xf96202d560e1b2161bcaa5f315e5b1e2eca93dd3d8072b3ccabbc8135dd12e9b", - "0x7d93ec83e59fd401c711df9026bd72a87bf343d799e1282afde6aa1b1a43cefa", - "0xb5a859b110f68ea54b38e93ab8ac2f0345b7fd067b08aa51d67c154c80312ad3", - "0x7cefd82fbbbe8dacf4c55871368b8b5eec505f60883a85640242f4ac4c5deecb", - "0xedfdaf2bdd69df02c1e8dbadb65712e0504eee5e5ff3fc0f539469710dd5a5a1", - "0x8ff0e0e4855c0890d71ba87386aa3d5f6478212e19613cf05b6bd067f5a0adc4", - "0x7830f4ee53e26ca607905bb9c127d6915cd7534854270acf1decdd45bfc82c90", - "0x6eac559c95bca27833457ca73be5d93fae6f30b75bfcc53610fde590eb251805", - "0x624112cd83afb5f5078de2d1523f69beb9bd21f8646472d8c87e9b282bfb9463", - "0x0fcf00311346ece9b518b3946c71ab18b4e0068b58daacb60ae44f7b7f9c6159", - "0x580dfc57fe900b2621048d7d689ecae98a567645601449d7d7c703bfc3f93e47", - "0xa1558a82f05714aa7afc11e8d0b964d6087a4454dcc218f85a4d4c34f1adc2a2", - "0x2a95556e84c7524fd21e8e7807100f52ea278e25f43f16530d4c4a3d98434bff", - "0xc9cfbdce22901b6a2a2890883daa122501dab5d6fce92f527d699a1c6bb21e73", - "0xd1b86b1c3af0fa51d068ab86c59bfa33e564454b31afa8e4e38d71af50744b49", - "0x731468089781f91a8851f7765ed37939bf6a128e1ec1519182bad053d28e6335", - "0xbe15dc806e540e017226c5c70293005b4897efe5fa2fc250458c69158977053e", - "0x47b02ef97cfae1cebb39c9d586243583566f7a8c1a94a7e0cee03b9f69852397", - "0x630012d012d1401ccc0d0fb2e00b3cf5ecde470bffce52f93c55db24fc8aad50", - "0x462eb1e05f12c8509051c0a49a62eded4aaea9ac3e6971a1e938eb205db4e235", - "0x771bf93a1c5b137c4ac01300cbea8bd784eca03c19669eb107c2e6d52884bee4", - "0x118f4942e1a52a92e034c63fd99ff027e3669fc31967e8faaf6ea293e0b8709c", - "0x20db323c06beca205a373f294b05f299fb60861b7e86b61e20e298acba3fca65", - "0xce32378b733330dafce88de5b97c64ecefa43bdb0aa81b29b29bab6fe275650c", - "0xbbac315984167276c7116f7704589fed29fc34d63d188563ff7c8110bd6e97e1", - "0x5d40637104a9bb5524e2b3ef41d37e2634a2cf894732353a6fe04432c09bd762", - "0xb9a69a32753eb0ecd5e259e64c3953533efb8b8fe164b973a16c77a9e6146114", - "0x4aa4df7a0849f424c5975c28fa0b5b449eca26022a7d6fe65620c1d93bec9820", - "0xf099fcf9f8108254dd60531362341e641c4c150a3848373c82932bc1d51925b2", - "0xc603041029c1d86644ad750208997d2c72c061fac7fa4af0bce0701275295a0e", - "0x15d16b41757f81830c23faf58c499cd09497a474f46c5ae511aeb45aa48bdc3f", - "0x964a3f4efeab9fcf290566afa41e210276c1db10a0b1155f4615974e575c09de", - "0xe5fc4556103e0aac0047784ad3eb75c4c4c5561834b3e60692828c1f98d4253b", - "0x46927e7b6a30ccf6abc8f1ac967743dad7af1b6002cb408ec04f92d1666dd5e8", - "0xac4ef88238077f309c4d487f8b7347b768074b54bdc32b9d86dc4d884fdc3bac", - "0x41116a0be7226adf1968baaa3b5b58267eb228019cad151397ab0bf98954109d", - "0x7e7e9d375928de3025a59d90da01d7e0225a9cfd9296f219ac25218596725acf", - "0xb7a35780b22190c0c17e43e8287440e4975d380421160e75fea99cceae2330c5", - "0xe38c7f40a3a760425d6f27183c4ce827313503a126d0a6385c5e41e83b3dba2c", - "0xe431d175d47cd897c227a1657cdc89811984f95b94993bb61ec54d9c7b23f8d5", - "0x4f47b0e4c5443c11a7862ce98367cadd11b042a4991c9f1bbda88d29f3ac8569", - "0x19e6f17eb87ca400b3aa7a5f3b28d8dd0062b858fdcd9ffaaed1bcad9e378e51", - "0xd1144f5c397c11ce4bada4394881046a5be59d2160d1e6c4ad231b7582fce79c", - "0x01b05affa98baf640abf73b370a2730758f492eb2e639134523ebb4b0d4d9c27", - "0x009ee372593740561b641c5d8a84b0e65d88677c85623b4d331b87cdc9b95394", - "0xe0869946fe60ec13ad38d8d20b8e3a4cae09eeb023aeec2ccea4635a92359cdb", - "0x1f5a36ce048ca23f44ccc485ef9497e5c990c3e9db0ce4dd5a7b8ae334629504", - "0xbee0980f08ce37a634a4423cda8d50c4cd1fbacc093565c14e3a24a35632ed2e", - "0xac61b0e93d7fc80f1fd21b13d27825e44373c413eea519b57ecf203c0f028bc5", - "0x53a90ce7fa7dbedbc8e840fe2d405415539aff25fca430314736e50c6f9ee984", - "0x81064e012d19da85422f9fc09cf9bf1e716dfccc51286974d25b2f8ab6d31fd3", - "0x66fb9aebbb257df30adfdf66aa63c7e3c1daea27d4ef563d97743da88863a86e", - "0x5a7b6908f93ef5c779a4ece2232f84a4803359b40c851f53cc949b8ccca22c8c", - "0x5ca0093f965d120ede845e1c53e9eea3e0aba705fe3351245250bdabec550aca", - "0xfccb96ec021b946ab6d957df39288e58bd7d0f965f731ed9ddc634134d831aea", - "0x1ac0a3e2bde62f28e81edcb8586677ecad77108ac3186dc9ac695a1ece429e2f", - "0x2baec4e11e355f77599f6b5e51d43cd21ada652cc38147073d246a54b8c74be5", - "0x349f045d2d0647b7728c48de3ec8c137b01e5ec2180c97633fce75665847032f", - "0x5f072dbdff5e4fa6525fd7cba56fb3f614bb67b5f0d3307de35d19ff9d22ee94", - "0x1117e7dd45312ad180d28c76c40385397a27d6572c34dbbd6a4f04918e6370e1", - "0x3ed396cc8c99b9784f21688ee6d960330b72dcc4014f920bcea28417b9e2b231", - "0x4c654a1dcbc0763b6e4ea6024e81fd7393bf4b921661fee3f2ec9080d7960b0a", - "0x6750fec450fee7f6b710db501098f7da59fe76e644940700f511648ba6580594", - "0xa539a7d3f460dda620fd08fe11b15bd1d7f93d5987fd05166eea95bb133608a1", - "0x9cce533ac9adf70b3c5de99f6d4c25b7493fc62ed4e086bc0cac5e78ae6b434e", - "0x196e7a933f28e4f70229e702198ee364ac095877bc255be915e468af637b2cd8", - "0x64adc83833fd478f41ec97f20691e6e0dfe66994e9341710a7ea6deb66739268", - "0x48c8fadb1a46fdf9f10a5ca6d8c1156879007d39dd4621eef2a3af50a971cabb", - "0x19c0b693193def13b28111fcf1fb06fc1a1268b40b49124fb606e69b497952de", - "0x6ffccde7aa6d1987680db26332077859cc42713d7b479b390601e9d382ed4970", - "0x4685c98bf99101061453e23a4994157ebf86780d73c65325f1f053471f4658ba", - "0xa8c0250d3c708b22c01bd468052afe784628d8fa39c12836a872d144f03d500e", - "0x60c76dd584982a3f05b715a6c90f9c1e9ef46d2827e97940e1b67feb011bd5cc", - "0x0f5a92f5573ce6f4be3ae58ee5e9131cc45f4794480a4974ee80ea50ea2a2dae", - "0x2495bf976e3877303dc3647cdd2ae47cba05b82a6069b7721a6e6a731054315a", - "0x16a120253b7c4f074bbfc659ad65af3534aa86217e133f2678fa503485b61fe2", - "0x85181fbf375026def264b1ff000dcdfbd487be6d18aae3272f181d92c9f0337c", - "0x3c7181cf51f5e05aa1f4642e3b51811a94f1cf4a48f2ca661c15bf21937a0f91", - "0xf65a4c993a941a28ee135e56aff76a9c1a65cd90b2e3bea8c52b97e322b2c6a3", - "0x2650635481a5ec0b92d46fccbc2b959759eb1506642244570d34d279720cf950", - "0xcf0415f4a01e2dc759b8c4df61c20d9b61e001326b9964cc08a24d1bcaba79a9", - "0x497626c7a99d2383832c282bb9276f9458989bb51dd2cb8beab19069f145325a", - "0x258105ceab92adcf7e1071085f4881963cc0cf98c28b5da174813e80b564acac", - "0xfc8e8f4d831f9176b13e214af17977495105128e55bd58886cb6b699ab0b82e8", - "0xf1fe70de2aa4bf8832839027edc5273b4e1c9b68557bc64b9dd41639b76f6da9", - "0x67b2609150cab4fdc60735dfd251f58e0ab4c37f4c5e06b742088b0299bbaef1", - "0x991be3047b87a9c7df5d14c8b994e225c18967ffd8f165fa0f11701ec9893bb5", - "0xa55540cd2b397abcd88733896795d3f36bbfdd1024d18ea7d08f5c29713de23b", - "0xc72955a6debdbb088a6588a5613bd2e329612e366ffbefb246244563c78e3a13", - "0xf2e8bf5f11b5d144fcb2e9c26dd1c4b7acbfaf7ea9221a4db35361275bc02d78", - "0xb61d47b97a001e07a63212bb473fa8486d1da002ee774d30fa03a704f575984b", - "0x373146b0da2d2269aefc099d0e1ad48acb1dfa018af540433abe1785fb84ad7a", - "0x55b5675e4a7418ef176fc57e339bbd438a73e9d07496ee3044aeb9c55001b176", - "0x758af96b4ba6fb256a7f30378dc0d46ca067e3e7cdab7ea3e974551d5b9aa2df", - "0xb37b1858153b6d2d2f914625067dee98501b768641e30f12e4596fb0a6603093", - "0xedffe2510bc146b7d1fbbb226b9fd3370f6b711b1cffc1f6951485be80ec2246", - "0x19985ecc6a8750b3326248776c85a4270e6d125718eab5d8d988a2b7c75606cc", - "0x48b5d00c0063db48b907c2a03ee9f33015c7c07ccc845caa7a158899b358b85d", - "0x99d95d31ec4d06281f0b08173851fe4ab50b5c787f2e6f9ce20466cb7121b31b", - "0xcc104b8bab53ee3753bace59a4ef59775e3665af65c728b301ca3fa5befcc1b5", - "0xa852c92b55f55c16a99f0d110d4ea7451a172a3be46745ab7d8fbc5eee9b9283", - "0x9b2b3660afa61c4e56a25b8cfca058102c2f2b16e97f9e26e80fe61d5af7fb8e", - "0xee87d114b2af91345deea4ee759c989e854b5456fc5b1c11ff4cfea40192f0de", - "0xdf865eb8ff849b25b12ad4aa19964baefdce7945ec032da2e3c1705a53f020d0", - "0xc367c8e3f196b16d93ac86459deda6ee469846072f2ea72e53c84f152d576ccc", - "0x60ce704c4addb76ddce1e3d81476e98e11bf2d2ef46087eda113d7e6edbd8f55", - "0x18c87d7573c19dc3f66dc3c9756c40573e66e59d15afa69b007fc51bdf0d357e", - "0x0b5b583d706437fb2cc549c39c802852cec9c50acaaff8c9151a20a6ca8d2ef0", - "0x0014b94a1acf7461b89b0506f30c26ebda6d319e5a6ee63751d320b3f4affcf5", - "0xb6ac380ff03a0a66a152437ed43434fa786d6d3c22335852f806f3943c3fe132", - "0xea8da8fed31e69e2698dc928b67b5353d35525f25dce1f6ae45865b9ce90169b", - "0xfe9e5bbdd55aff1c660ac89c54fcc0547abbd671b210ccf4af31b70404820884", - "0x7347c74c6be6312e284904e5b60115d5ff48a032777e91423d5356aa9d415a9c", - "0xadc1b5c371a79c723166c3ddae708b9eb788bfa6d04794bfab5553b43153c38a", - "0x32f150c20cc07e1ef70f305a3270f351bae9dc13b6e0d67c661de5412cf0db0e", - "0xd914de07ff853dbdaee5231be517ab681ed340e552458d640815c0e44da377aa", - "0x5a01f086ca42b793754187d98dfb333996a649852e51b48ba0eab1716fa6f224", - "0xe5ad5f6ddac1eb411bfb05d2ccd41d3d1743ad1c9cbb96738a9979399ee1dbd2", - "0x574517aadfc912986e188b0ff0818312fe9f016007f4a30759a92fdfa069a6ab", - "0xc46db2db2552e53f2fa6a169ce803bb18172cf20d5a36b872a5c4d9963c7d42b", - "0x8ad27ac3db4da1eac8d6415b5aebe303cfcd681950269f0d1ce57215788cfe23", - "0x2aa7a60b25904d1e968f0dbeab510c06355b052b80872ab07cc58d809b58ad15", - "0x3031132b43c9ff4b2c96dcf7c04b31d18b6e8bc2e0cba24e8fd786b02aa00fc5", - "0xe1fde8dc15e887c6423dc4b6ce04d6e18a65831b073836a921dfc04ee016b299", - "0x5dbad0a396f6cd16ada2369dc2d08ab6af60b7da48fb0f6c320a566927745f3b", - "0x4d5d4913791fb3d819ad4c45e9f0ecbe062d0198e25bc6b20bc695e931d35d86", - "0x8825cb707e721a61c4bfe7f80a83b2a734d8406146b11ffed638259da5f91dce", - "0x6c686743164839f169f94ae4c7f80298df5b9eefa955cd6615def97b5fe2375d", - "0x5afee77d12298cd027840f0c1a26ed057f6af4192c19eefc6df965cf502788c6", - "0xda9312700594f18fd5dfbc2782fb440d3b79073b31b58f85275aa907b25e0b98", - "0x94eb93407bea97e074f639256839e2b1e955fc36b1fad232b19b278dc89006c5", - "0x81f48cf3fd6183fdf5e109c5818392c8b23ec98cf22a8b330cec22802950bafb", - "0x7ba2755d9dd57bfa22feb05fb10ebd1a887d016d660083eeab1c447f5d2aaea4", - "0x770be40b4c51529a27ed6e34e4d7caa52e1cc4e645eef2a5597b566972eef290", - "0x4dd8f1711f834aa709485ea0be3f948cf4a32ac4157cd217262266ee282083eb", - "0x76d2d40a7ddf60cbcd63154570fef14ef41f60b6400347cd9e66f2be51fadcaf", - "0x2bb51be5bd7fe41a05fa9a719aeb4b26f0e624b5ca5e874723c88f8af34a8448", - "0xa62d0bdefa2428c6347e1575029824b1e6c985a5a8496d2b862850bfb038b55c", - "0x4afd8ffc47fa73d5f34752278974358d1979c62dd4d0ccffc65aa16a3a748764", - "0xdc2cd7b80144147d26f66e44a03b3fd81f88d8c92c967b056b12ed54a09c1bc4", - "0x2e08104188c24e988c719984149d16dc4aa90bac4339cb4c3390f6a7040bd122", - "0x5957803ea74ae2f332137a7283c87c9412e66ea0ac9f6d3512ece8761933d4ed", - "0x66ea33306be78fc4cac257e19ba5856ea60e171f359d2611916bb6c1a1e8f01d", - "0x6f1b23cb0739caac8cf2e9bb61a2c1d390f6032b8fd2cfd229def9319856703f", - "0xfac23cca5ffe9719b9e60549a88da5a2accbaf1a8848184882b2ce94f66ffde1", - "0xe8f6f317143515b3fee7802be18866e8b33e8939f0c4899ac69c84dd484168cc", - "0x450bda8ac1c25a5e864e7a4dd3c0bf7ef9b4672b7bd886b4449dd6a9c2520d5d", - "0xe54ee7a1a9f089c66895c681a6876187dc3995e2299e17112a6d60295e1c8812", - "0x23d7c3d49ebb33312168eefb03ffe680a6eb969b719c573bcbca5ace4fadd182", - "0x961abd32c2fb7fde7054ee5232cbd485de5f83c427b36802f27b2efad7817d7b", - "0x98d83a56dfbf64cfffbdf1d631d0df7a4d2036a629d36794f9bbfeb106d94b09", - "0x7e06ea4d5059d55dfe9df764004261f907d08ce4043e6af73f09561974d57169", - "0xdb208e24e9f6193ca94486801ab570f7cbf8844803609ba03f7c4524aa92a38c", - "0x663f20eacbe6847073b04e7b2d98e820bd85cf29370d71c8c3573d0764360861", - "0x7bb39b75c462f7812f1a458dd06db76362bfd10d27425eec78a88228a48f1805", - "0xcf07ca736e96ab42264abc426229019165a3ee4ce8eeb2558ff8a086aec30918", - "0xafdc6af1dc2f93556a4aad36d0695dc2e1ec84d5dfe928c66d55df7270d5be4b", - "0x4f9172ef6b6fb4fc08c8b4878bda85bc58ed557b18aa3a3aaae449c773c1bf04", - "0x81ffe0561836f05fabf77b79e2aed4941d90d0910451c3863caa5ff3451b941a", - "0x112490bb941dcdc267ef27e3dcac89fe2690947575df1e53657577af72e6fead", - "0xde4a0bc4ded2a7d6a181ecdd9cf064858293bc313a84c2b33f51d42d7fa9d96c", - "0x25364f970061fce0d3b481cb97fd386a6c6e7b978e40015cc8357cb4b3e6f89c", - "0x524eca86148baee8391217d99caa2a644e5aaee2591fb90962ce2a34e7bbab36", - "0x56e971166db8d020b247c33a5ad59f713e1241808a2441c47ebd6bf5189f01a7", - "0x13e761f3f512358b4963547418ccec496b1205347cd3ebe2a510ab18f7cf2115", - "0x45bc998da3b99baebae0fcf5703f1ef5884fa0d049bafd51d1a491a762c80193", - "0x6727277814838b616dc4e78831cfefa4b9ff0026d2e7f52a33a8e7c99b5e0846", - "0x216e20d230f00e5b2c788f6073bb68c4a0afe5291d833350dc555259a8f37e0f", - "0xdf1cc61ea687ed6bf1bd576899003f08a13cc7b5e5ccfe08649887f866888973", - "0x9301d182de849055c299983a7c99a59166460e37637fc227ee038380dca42680", - "0xdb0fd7bc019418c8426879d453f6e72c38305091cb7306a8f503c6461b72aeeb", - "0x4a8183ad7c2a0301b5ea7c6554476e9d7747dd8931330b8b7841394b8178018e", - "0xe1014a500104e29335b7d5a423cf2504b6d2e4fb313021ef8d4051d07f612cff", - "0x823de414bae4688460c5697d22d3b95a882b74c29a14f0565c54ec61e1449b69", - "0x80f8e29216dffab2e4350de36925b60895cdb6968e497db432e12026d6cd56e9", - "0x900673ec8035f93ad51b3ae2e3ebb26d09c50a6a7f5436fc95e5ebfa6ad16d37", - "0x7073bb4f9dfa46161d314a87a6d77c661ff1bbb507fbf4030a461625fba06d32", - "0x397feec1f80c15e2364c776d69d78fcbca750bd19fb4c30cfcdc1b361caa659c", - "0xa881765daf0b5dbf10c34832cdbad13f5cacb9812514241b443d26dfba8bc448", - "0x9e0b130bed0a59e804c4c380665757ffcd0ee6e300a1b17639afd55f6cde9e16", - "0xa3e0f7e5e174fff98c24d3f31ecff842443178ca09234029bd5d0549694bb4f1", - "0x887201dd3dda8405c4a29fc49a3ba22a3ee26f227b4a8079e7c839dcb014cadb", - "0x20604a57bf1bcaa1ba7885135efde3fb4e8b118c85c6448a2f9fbddab44bdfae", - "0x4f9a6712963d57774c919b82b8ba48026dc61c01b94b77668829b5ccde82f2ec", - "0xd9e91f925e3eb83f8361c37e03aa0aa60641e5a5f9b01f92fcfab199b6493ddb", - "0x339bfe6603d8e2f0bf2c8fa925ed97a41f589653268aab0f494b79fb5092562d", - "0x2e927b1510753f5a83d707cf89d3b62e1e5ef7433d4e0b194863ff27125861e7", - "0xb177f311494d3d52e4161f072d24ede73d0c18256ac43a8ee5db30c525245fad", - "0xf873b085f8a42bdf1508b478aea9106cdb4f27f347ec997c4cdd5664c35e0e78", - "0xd80fd167784537b68ff33e2e6423b6b9647d82af112a5299c21da4d5b8e6d20d", - "0x34ca5d08b8d391778cac1d821fa1d6e94989ada35c2c52b691eeb0e8183d8552", - "0xbf80f2a0081854b4d2d0b91dcdd199741b92167dc0f5581cba76a7fe52b96150", - "0xdabf29b0fff0d327a111e17eec9160b178f6898650e45829c0c3afba6dcdbf02", - "0x025dfabea1e942dbafd9b0ad5f0b6982fb52315acfcf1270141f924575124609", - "0xff960681061cb1590f38af8a44b6597394687f780d3ce0afc9cf4203926cca6c", - "0x72ef02bcdbd5c43463104f0b2f22750556b0c73e862c89d1750baceae6e47d5f", - "0x1e4cad2aed7ee0951c21015d12919a51142ce902ec15b08f457ca5f962099b10", - "0x8aa5bb7bd5cf82a5696f523e16aeb4f9b48aa22d252b8338b403bd0b19b3de37", - "0x5762ad980b237f1a70fab5f6ce570f68eb1ba25caa87ef66e0f653b02e6bf808", - "0xb4121faa65ed4fc53f6f377e4da1a3c0f65848f0fc3363a6d66da7bcad514a30", - "0x67959aedb680f267d61dd647b88a6e177d5d84213f94ab4a071870a51df2d476", - "0x715647d7da67f6114f16ff0f6c9277a186cb4adcdbbe9e990a901118a84254ce", - "0x0cbdf473c4f3646c32f8c0002d7f993f7876f8c9086222d5a82c678eef60ce9c", - "0x551adbb1ce65205710014bf69a5a6bf5a98cc67f98e6df1efd22bcc1fa6d5d08", - "0x3bb1e51f43a2d13049d15492b8b78cea716f3d3f884d35ec65c72d24a6112c9d", - "0xbc91486359e3e62f22c39791afe7b2b4e09843d8a3ca301897684cb8d05988b8", - "0xb575d4642418c78e9f29f121e82886b23d7a061edd7bc6d762155a1bf34f552b", - "0x3c4b2299588786594bce7d50e060d26725182e92f8de40a86dc18a4434ac292b", - "0xafa9cd14e23ec071c5a22e2d0ff21656c8cabb710578d31acb6cc8e1e3315ded", - "0x9f6a198a59246aa15d3b0573cc080d0ed31f5f41e1c4efe16ada8656fcc94c46", - "0x4667eb3aea65369ebdbfea2683e172295d6b28436e4a5c7fbefebd8e15a01fe3", - "0x89ec767b3b1e41da68ad53f11c5755e61a18a0d07fdf6d7d53cca6849087f135", - "0x2046d27b46d093c35eef24698d1fffc5e7532007a2279f646c7bde034402af71", - "0x8629705c85e0d6060906ced37a5be8fb7b5ac351eca5a1934f9a084a47fe7420", - "0x42f465868c164eebdc6efd395ef39936aaa9e65e753850e6d2c2cde9837e1647", - "0x766d160e4d26eba8675552a90dff4f63ab598e1955bbe0ba1a5963f2a524b090", - "0xd05c5c90fa9bc35c43f2a6ab9f5cffa6f9bc2557f9019cc2e9c8aae269041fa5", - "0xc1a11fd05c80a1c9512ce10c89ede225405ac58321f63a9d1335261454dc0036", - "0x05092398d5beed07cf2744b91561083dab7040b09d3b3bf64d2b68ea0d4a817b", - "0x7fa4113fc5be42a29c84e7665fd3d18c06ecd482d645a7556e2f5bfce25754a4", - "0x06e59dcfd141ed7be88b602f2ebe9efa4091c80de4353500b21d597e26f22caa", - "0x03da4109fcc40e21edc20001b619da6184d64eddb1b3fc91d2a2ab6ecf74bab3", - "0x64ca02e31ca8df66861511a9a20f4297bda741972ffc636f6187fdf7f09fcf17", - "0x7c561edead7f97f117f9d7b556fb5dea0c4d8ec5bc79897328c4acfe069b73a3", - "0x64b7d9a23879a3fd86d561ccc911791c2f0d5616ef0c0cffd33ccde84c5b7da6", - "0x083fec3acb799318048eed14ed35a85cf45010c70e482fd99749ae25c1d0b782", - "0xe9529f45f938dd2576195d7d2798cca2eb5df1f31e0f72a4645393b641e77a1b", - "0x47f289d3467b967a0f8034098efb542172420e00cc29e1552cba377597916969", - "0xf4416df2dd30effcbeb3b80bd035a4236a0a5cb85eeebd861ff1d58d699eda50", - "0xa3fec237960e77871219fc08473c948ce4c0f9ac61ad80ea2bed7996f9507d69", - "0x6f3d597bbdc64c01d432bea19b720e72a705f843a5f1eb8330d7115b55e60f71", - "0xf67e1f41826290d68f6b0e900be97212ab334280112dd5e9f848db18ac3b9271", - "0xf0230633f08dca03a3647c4f6ed4f093df94d7fbe59b51609460a6b466837bf1", - "0x0484bc99cd286afe2d3e33f30f0e6887bdd3c314a5e81e844f21940ecfb1ff9b", - "0x8ce05e8d880ab3498a7de438eae5f6080b048b3e9b9f5a23c111c1db7ed9ed83", - "0x5a0109a7920d888d4cbb0431eb0b34ed1fa1e2681ede169ebc845252c4aebe14", - "0x7f28a7a93453c7aad30ef6b3b85f38ed40bfd4f7469a0a2f66055c60a3276281", - "0xf48b88a653ec5bc25bd78a14d038a517545f83b274a8b3cbf81102647e5482ba", - "0x55f6cdc8ac367916e81eaeb3e7255eba26a9c5495b6b1442ba30f1856f4ba85a", - "0x97120be21f046e4ebd57ceefd9f5e4583c33103017fadda5c05bc6e6f847eb14", - "0x8ce01af1b8f413b97c1aca65b18602bc48a84135b84c425ffade62535f6d177c", - "0x2ba45b460e684678f4e7605c4c912f2480ee9c2b17b7f75f018423951c2ce03f", - "0x932155dd74599a7896ee933838b632593175e350f8db1ee001c8c8b55d156630", - "0x9592f1f50289aa899f796c73c62583e758617ba66ce3ad2f5a1e3e4009ea0e44", - "0x68f2510fa98cb203caef5b5434d8ec4b5a63f7af17aee2eb83cedac637e162df", - "0x28dcca4c376df916bbed16d0e1d73c0568af5b08b4db3a86f108333638d61f76", - "0x980002c98d465e0011c438939c6a97b8b8182733da14ae198ea077a257f01eb2", - "0x7f280b8d3da47619e505063394c7e84beef045eca57c22a9ac71d2c0e4f9ff1a", - "0x6798250b993f3b9b83d30139a2d497f93fd825c1dc94fd57c10ef5c8818852c5", - "0xf5b978c4ff28deaf652b1ea3d7b7367cadc440baf440735a62b25adad4cb776e", - "0x74d37be2d2addd3839d9541111d72c98f09900305e891d94ea74879ac975851c", - "0xc77c2caf24dddd1204a856631c8dd2358e2c35bbd5732b53c1469fe91f975b82", - "0xcd6ab2ed9cb320e22fc345c58e23121efb68654810cee0dcadb265e06c3ff7bf", - "0x204b4e66ce38c8396f924799548aafc99d268694262207bb5fd5d45ebafdba17", - "0x3c67f0ee0d1125ee7ef93f0ac38bb14c490b4d85be9bedced7e4761cef9eb40d", - "0xdeef5f3116639ab746da2d6481b1d6ce75a80fcf9d495bcc863e4fadbb4d9de6", - "0x30570d79646a5cc3a81c10976b310892030d339b679b56f453d78bee39a70b7a", - "0x4c5faab35c94e41bc9e1d19984d184e396483d98ca546fd700abb5b332df9412", - "0x1f3e674220a9274313dd76dfe3a84777e04051a9dfb8d87799da979739561d18", - "0xf74999202a6e5120d01c97e857bd8aff0f36f1946b57f6b63e9766fda3b87724", - "0xae65d50f5ff120b96489289d97952d46a3697a00424b309bc3840dee2c9aecf6", - "0xfbda361f2512dc8e64dc15a3253c5242a2ff8cea29e435da91bcd3b2c6d979c8", - "0x7e9bd0797f03dc6d6e0c5d0493968883488efb507844ce749de8101de40c0b21", - "0xc60a8ba7bda89df7e936559df5067e6d1c7924962e6abfab40c708b0174d2264", - "0x8d5700d40e1f396af84d673852afe843090f573fe874b602cf5815fd011ee501", - "0xa05a1aa13f640a250df26e37d48ee70e5216f9224fbb541e6d4b2f4858c76f71", - "0x2ad9058a64d93621095801ce8272d2208e90e71a1a0b38dc24f71123fda8d8f6", - "0x40c96a4cc194d009b6609b154fbe63a833cc702dc2fff5b837e5eb6c88c7e005", - "0xa2c233b6964e1625a43ab70f1837543e5b3868dbc0fdb4732ecf8a4155d29d79", - "0xcd627d5278f9400834eb48b54f331a74734846497d86acc348e02a41d513b201", - "0xb34b79d45abf13ca51f88f7d7f144a260a44308390573eb2b8dd292b70b02e77", - "0xb8986ff28901817523ad8d796dadf9e5284257fb34709a98d5a3e5738dfe4379", - "0xb096d14fff72a2c833b112281f5f52ac5dbb4d48f2245570a81b85b140f40948", - "0x3818f5b06bc70eef065d7d481f80c97c6806c4207e600c63c663c1da210d8be9", - "0x7e3090a67933b2f83e6152fb75178f79e26f82ad07c4358ae463414a0b02780e", - "0x21aa5f90daa5ad68746f55e7f2510459ef32760867fece3f0224d65b3258afac", - "0x377b4d870eaf1d610e9eda269e4eb85382f527ac683e5ab53d04b3b0caef05fa", - "0xe6b2742b454708e3db3dbff642643e5159931a211a999ce57cc633e6c4d8da93", - "0x39ef67462038d2e25a9146727ead5ba921e9080e2c999a67ebce9d724893fae1", - "0xfc86a21f2f521f6d20e57bd3af58f42cab41add721580831a3b3bbf3680c0ccc", - "0xa2f54cdf34b1c869f5b248b3ef849859971a23eccbfb94ae8acf4370cec11924", - "0x8900fc71a6af6e496b5ac6ddd94fcf9a0656a342362019e971c44038e867d3e4", - "0xd1c988a3136c1521f70a547eb40c9ef40eac2750b940a951afed3dff7789590f", - "0x39e49739745830f5d91649e8032d30e22e45265ff92676f7d50c9a2335e21d84", - "0xcc964177439b593bcdfee8bab03bbedb9036de7b856e6b7d43c95e055f2a8f45", - "0x76e4575fdeee92408341b576d00ba3de2bdb964de3630ba0d494d79093d74536", - "0x3915002188b2d3e81ecebf0e1406148f0b20aba94e78a5b68969cf62b10ee7f9", - "0xf18f968a8f3f6f926b88be7b62230c9ecf6d44420971331886b7cc3c6bc9074b", - "0xe605ec94dd9a2e7e2c214a49f8eac960b12862a6aa435ddb8b4c74288b343c95", - "0xe6500a2b86b56f1acdd1012440835cba227ac7841550f30415f83b0cad5e6b16", - "0x0c20a4fbf0c1c766560016cb42ddb2f30aa6f841d8962e98dc98b723c8729be4", - "0xdef58db01c902fb676f8c515b708dc4762c3151fb9d80724eb1f3a02573071d0", - "0x44f0d489aa086c5b6cf6d3198cee057f60dfd6baac0529fa9b9b49e15d6209e2", - "0x91d2cf4797ca2c817d38db0ade3de064f66760d4f0604f90e209f5cf1b541977", - "0x873666cca9e593788f770f7cfa928755dc706647466b4cebd0362079a13b41e5", - "0x992ed0a28457f993bf57ada40110f6ee947fa127348ff6d211bba26a0fb3696c", - "0xfbcb1fd57adc08ee435b077e17be9b087df6d0e71bf39060a0f620955c1dd07f", - "0x544771081accc2219221cf353e97ccada6aef8aa94048981a7d060a37899a932", - "0x768ad3e04bec129183a33fc136476e1130d9b167177c6de4849613ec220083f0", - "0x44b0a9afa8de7b437c6c199f0fcbf0aaf377c1b847c9e8c64be7f14cb3792b56", - "0x10b9c4a0da33edf3db0b0aa33f2ed1d1bdc08ef6d7bfb8e910fe0a74ce809efe", - "0x6831f98b09864abbeb1177d50bdddf12acccc7abbbe1af173982814ac04275db", - "0x3e0466a93aefe563285ea1b7b6fff1c64b274161cdb5a37542e888a0219677e7", - "0x01315c879852a7492c6eaba9819f843f60456a8e2fde0a9ee2cc5192b3a00b66", - "0x0ac3011320ca4cde5af849ef12dc87f318315681ecc0f303c62f29fc556a2490", - "0x91fc1434251b487ff854c41c65dfc5ab63d309c0d88dea41af268e0d7cbf5df3", - "0x59d8dc5031c170ae9042d44f1d7a0b95669a15edbfadeebeed0f290c2afaceee", - "0xf636e4550a81c8f1b7e318f15c85cb57149fb4bfe3b879fd45f809fd57ee7db7", - "0x68f9a9de0600d560c7693bc0728741751bbae831bf358ffc195d7c304d9b280f", - "0x6c1358206b4fffb9716a634a5804bb4fb9331bd2c356d0064054a9263804a400", - "0xe9bbe71942123394e1615bc1935f0ea7a7079c708234581a708c38d304a1f783", - "0x41511870cd7271c3e3b1bfcb3fa043a4db8ed6510543e35dd8d4ab588017f4a4", - "0xe90ae56748f84a0885a7362993155b47b4d0c5532d7862c6fa422e28f09b32a1", - "0x37e43842eea4ac64b65d94b5fa1ad7a2d6d16f5941c43586913f574d5998ed7a", - "0x1117f28caf34809d49a0b1ef2f890680116b44c76c9aa27efa0357885dc1f2dc", - "0x699f428493f3cc759565072dc237c01636e283b808f95842e2be46e45a58f3dd", - "0xc91f3d366051b95a6a5b3290b501e7e435ea967cf39df1ac94e8769a87744f34", - "0xe28d8d82d81fb59b445e16df7493a74d1540b43ce61692605bf0d5287af9418f", - "0x667313aa96fd2950367a671e5ac01e685fcca1fb45f325510d38fb2c622ab67f", - "0x48860cdb6aaaa321d2accbb8f1e9769e70dd6d4c29c8baf490f4f0e23c40dd05", - "0x3dd9f9705c4d72c2d69c90cf2fd68c0aa5d0cedefd7dc012e9b7f2231f3a0786", - "0xc7aa31f7bccc6ce532d673132cd980d51b00cbb2768c9846bb60dd6ecfb584b6", - "0x403229d6b2a73c367262bb93d02de563ead582921fc763b8fdf495fada77bd55", - "0xffcd7f454695bd9f3b89a460b1c98de5ba3a989888e2331c885896e49c19f64e", - "0x5f9091b21020642ba4c1027902906f7e6246c42392ef333f00990d62ed8cd008", - "0x2d7c6d6bcb42dcbb9a7868cf3c3df439ec18f28ea232a7aee34feed6cba38027", - "0xed6ad62ff36c517deab2f8a266eeccfb74548baa3b61ce4e6b6577628a9a0732", - "0x4f301038bb42b93f85eef264ffc59e8695265cddb4084c38ad08d9260ed99681", - "0xbd54f734c93e0c71128409dde4a423543b080b3e16c49bf61fd967b56b22d53f", - "0xa32f3f4e4763b4567b2613bf15c6999789213849588e756bc6165bbc9e58d072", - "0xa9398a4b294a26fc9cdbbcac4c32dc3a57a88a5f269b41bd49a0e696728f57a9", - "0x757e6d5b5b128b028599ca3ce3cf637aca013140864a382704fbd73f91ef1059", - "0x9d35eeb49fbb88ab329141001d211dc074223f992f7faa25453b31ad1686fe23", - "0x32fc286389aafda1f3911062dbb61841ba8b8943d221d553a97276aec4b18221", - "0x45f5f7330f02814ec0a491feef7accf09dfe0d4acf6295799894f793efc5082a", - "0x1063c89a6cf98b0ff44456df9925bc3877e96c3fd1811940d94fc0ee554d108a", - "0x2dd74888cffb6d854fa2acfc2f9780ba82a3fd849b3031d0a48bda091ae787d7", - "0xfc589908c8b621b7aa96b4c5675ebdf61ba09a2758dcfa8a8ed741edc1c0b946", - "0xdf8bf0a37b01ee0e2ff7b9e6a1186ba41b6a3cc1f2dbc776664b86df5159e8bc", - "0xd880db83f1091fb064ebc0143110e0de9b2471be1bf09d033e73f586203c789c", - "0x5dac7c9f4a5925292ed8fe41fec20063bf7a08046fcf31c7fc782119ffe422cf", - "0xbf3bf656c9170df01e9ffc1192ffbd0976a68c325767a6006245e7b885523a72", - "0xf56e3fc84c71743bb8955503ab1b5ff18b673b90982127524d98ad734323840d", - "0x393141d2b434845ca225588e2d94e5b082b425b9e627ab4417f11dedc1a3e3f1", - "0x4685ebaf81be0f1d89eff701f49d8436fe2f451e557d5635d199aeebbdb0e2ff", - "0xe1cfe4c9836cba7d13abc522cac6ae5513c396d8b46c7f61f8aa17da54205060", - "0x39b272decd976241df1fe242a2cb2a98f73d9a40ee3fd445b6830752174c4718", - "0xb656eb1cd8bd2c42b6ebe6231bd6d78556ad2424603eaba3f4740e7497483078", - "0x14ffcbc3d98ccae3ba2414c17f12a9a70365856729a6317a9c80e46575f89563", - "0x36ac3e640f31e48b5fffabdba7fd751fd803a1707b6ba7c587a7508b3b1b4231", - "0x1fa3f177431e20f569a2c10c79dce754eb8b7791baf5f2aa1a389c64c9f4c547", - "0xf7e53b29648da476b4df26086445adea1cbba0ebf2271461417bcf64bde0713b", - "0xd8a0a1781144535cba0e4b7e9ef72d439af344d6d13e9f31d35791e82aea57fd", - "0x730110ffac5fdcad1c3b82c00ba16e43eef88f3f4fd90eb5403922fe5bedff42", - "0x24cc38d4eb2b82fbfeaf2bf91bf7fa470a5ef49e344e75973418915fe218b42b", - "0x18f05c3a8f656c831d4e4f61100bb6619216770eb66775cb9ff9141849f9cb0a", - "0x0eabb089aa92365dd492fb0f81fdb2ff6f4575976154b1ead5d8b06ba5b4fafe", - "0xfa557813e8cba6fadbd410aaaa5d7e01a94c2eae756c7d064b8c19a26d5f8964", - "0xb424772a132e6a59b63715ea1e2689fcbc00b1d98368f530af840bef4ef72b7f", - "0xdc5738d415e77cd5a0ef5dd3d4a52f911b1e9dd4e386924e28b9b0b6dd7e6884", - "0xbb8596337aff09d46c4a5d6ee9bdfe369e41b2384a57f45b5a5151d4bf2be89e", - "0x882a79f76f1e876bfa8802c7e2305537f72aa1a7ca4ab34e09bdf28dac815776", - "0x6d3cc146b6db4257fd768280a58146bab3d6bbb6651c5bdfc12645d132afd904", - "0xecf827c44a85e1250ba58b311f1160007b29675570223ed5c642ce7f9b9a5cbb", - "0x64c58db8a9534ce75f56bba7f2fe704a4333d56641fc2eac3cb39217b3c10e76", - "0x2d3b0e05f4d190813b4dbec787f4a9bfb3b029c44bffdb193a1abb9577c8a9f8", - "0x514ecd87c88b6ee3b984d389b66ed5c96ee48f334fa5ddcd03c145f4e4d3dcce", - "0xe856f1c77b345793bbf9ebd77b812ce2f393d2993e8b735f1b1482a2b2425d13", - "0x05fcc98827976ee1d39a0d31e34e7d9239dbdf287159a07d0353c077e19e4350", - "0x7166e2b9120fae430512ba36205080875b12a5c0bd92ec1de24fff377d380cf5", - "0x67cd75d24ce797449761038f174c7b1aebad5e83a0e39179a011e0ba389f67c1", - "0x16769224b8cb92072bab6506514c78142f7bc02f8f87b00b65845d21b6bc5b19", - "0x0f915099c11b5d1a9fe07ad11f684a84c60bb78a4b1324d287a3440d1b043beb", - "0x07eb4a64b36b479ab579cd25a68da5091e63cc19b03b49e55da80f5cfd85df10", - "0xd7c98fd3e2f8290d670c96ff39df33df4a803f9f5f43e01d2f09a2bc13cab132", - "0x339a8c9604ea03a4ba70ba357c49af83bf845731d3f9c2ac52f8cf5fdad401bf", - "0x9dcfa65fe12ef7232d3d1c052ff4e5afaf115b7d4fa3eb09d07c684fb2cfabd7", - "0xe27eaab5c986d687f3f943d0b6b983e620494cba1e7e2148e7934bb5c4dd0869", - "0x5f6ed69c6633e088e922cfa705aed670a32f100afeab35130b38c323037697e5", - "0x2e3314c00a75e9f17471b393619b3d08ce276bf5de32a6231f7aa270030748ac", - "0xbc86dd6a1f7e07fc84340ecc774e3489571dc61e6c4423e64f148eca272ddf61", - "0x3761f5b746aa79e23ab83692589717ff225b03b02e668f5d51cd715570777f85", - "0xede23d7929b19affed9906d02bc39d519b228a19802b16a4ae9151426688ca05", - "0x6219acadecec83334f3c3ffec13c8a1715430f06f0e7a0039f5271aa334cfb49", - "0x28e09034b7e939e9f0cc236e66b2fac2070ce28837a8267e375cc76761fcfee8", - "0x0c4a9991804e019de882955447687a27117f8480eac60667989f641b6824f836", - "0x67160c6ecbe0913a51ddd163af202d3dfb9d4e826f2e7abcaebe57f9cef2d808", - "0xc9b590712145ac72c81a6e24ea96f1c5398fa8650955d25c9c102bb7ccd0ae1d", - "0x461c328503f928a03640021ea12980077317209ed4fc3d0c21b550316e8992d8", - "0x694f2afb90b35c87cfab511f9518160861a9ce1388f6b2d8900dcc887a943003", - "0x2399aa9e302c40a25d8154b8a7a884455a0a81f08b3b8228ffe6cf56919c2a3d", - "0x2faf9b05da0dcd0e712c95d7c8cde660b6a8dbf145106220a0a2f38919f2fd93", - "0x36fd9ff349ae7bff8945b90d5b4b3576a6b87624feea2153867b1bba89e0c1fc", - "0x024e47593d7509fc49e8c4d0717de52f7b63c702ee191c5ebc15a772a0325711", - "0x78954a4bcb0317e4147a8efff4abd7192bb941cd7278e397e1ed3c282fc54d9e", - "0x2735e04d281edbb1f0907e7565a36255028f8c4b084f0524ba9ff0cd5192a481", - "0x953b4e2340d04b1dd78771a65cd051b983ed8d8e870af85196135b2e10bee666", - "0x281655ac912af297ac13e4193163563c6c4181ebd3132a309aa8e9bc02758ad1", - "0xeeba9581563ada6481807a06b91beea20e05f191def9a31607e49296c6f994f6", - "0x2d4b2fd56623c139066a9d2ae72b728d5ce732eac2af2bfb834c03ce3b1cc752", - "0x0a6d56ba67da39811db01841dbdedf9d34e07bb954792151208670fc12c4c1a8", - "0xa4536f1f2ea3e046503373e88d9f8bcfb06f9f15a245ec73e6daddf1a9a2a025", - "0x28061410720a6908463d604e3375aee44f65a5882fd3f9bb05dda4291824f7a7", - "0x74c50d4fbedb21f96c21a9b1fcacdcdc4300155198a88f66333d843e8fd97cfb", - "0xef12f70d5f61d0cf33ae969ef303690b317a5622efb9ae2206f574ae53b4510a", - "0x00992e5fbc8a9963123e1c3a4cf8be6d1dc696480a3d130f1d6fb5c6612115b8", - "0xa104ba3752a76261008fe458d3214851903d3d6ee7b2eed5672815b8805b8511", - "0xe4da5ca2fdc5e2c8e0d6a93dfcde2e4273e1c837dc17dad1329fd57d0467e131", - "0xa1ccc561a0f5c87e5c36f3e98940f682bc1d5bc97615803ffc100189bbf3e906", - "0x95096f7f0b65d39e2f12a47281e046a31ab4bc01f402279c17aafd5f91a933b4", - "0x8f3cfeae59dde8f3b80f925fbdf104b99b43c661bb8b425d85953a6b63971a27", - "0x3b6544fbea14b28004b9a03a79688b86266e5e7714ea5ba27c8184dd0eff6916", - "0x8dddb96c7499b8e79410ca6b22f03d579c00446bbefa861e7121fb46b1644a7a", - "0x3cfe8d71fd57f392328d45a4fa4d6af05495fc45bd3efc943c35a59dd0feab97", - "0x9dff138167f92a5cb5e19d92545be2f6967e64faec4ce3d912261d13b0f77381", - "0x700a13c03623de6a15525b4b80ca7388305d4b526f0c52795935e02e6c899953", - "0x41d6d84b0af2d06e3c24798a4f430760a70adec3236accd6495e62c51b935602", - "0xa0d4b65158ca6876e04ead358ac8f262ec190cb20383f922fc56bd924f08a2f3", - "0x6fcd99bf39e3e02b159ea0f8f88af3e3d9106cab7ef3e0ca85aae1b7d65df2ab", - "0xbb3433e9eb7540ece7178ba2980221741563600e5203775c253625de9188bc18", - "0x52162680f1accd300e3cf145353a92259a67165072634c170285abe8aa146d14", - "0x4dbbbb9328a0ef70c7b94e7dc68a176078a744e4e37e1bf989aff3ad983f5751", - "0x0925afed938ddd0f2776cad39599d47f162859e5de58f1fc8144a01e14de394b", - "0x0b3ec75ffa53ee08e9ed403921c2ef6190777bed173a1645fdbd51ec3e17ca38", - "0x34e0993e94f3277e54d3497319ce1abddd09ba29a661eb3713bc1ecc07ffbc0b", - "0xb65467d4115d3b13075b298cd8e668c52ee976b776c99809e87fbd31ee945c18", - "0x642f85a0005debc3cac8549728ac17071e6fbfece30cf83e21a36bc36430acd0", - "0xd5991eb67e45b265ca67269dba2a17b56259406c309018e8494781e9a6384ac7", - "0x853d5cffc988d4a2a351f139a6ac36be440fb3a2ca81df18cc24ea842d004490", - "0x97900dca52f34da453abc39cbbe40edd6631381f0cae85f01afe9b66b649b260", - "0xb626879fd793990d2a7debbb0fd9cd4008ce353fd933395bc94555bad3563b7f", - "0xe287bff3e7de4a2ed9480a35d6ef5690abeb159663c2a166d338c4b757703cb4", - "0x2335cd471f8e16049e760f0233590a43f56940e45d6b38d21baf8db8658aab68", - "0xbee5e01c215dedc9a4531b73d6c2d8d71bccc390bcf34113dbda1d02cf3e790d", - "0xb2a661f1d7ddc11dc4b2dac43f1d88be575999ad25a4d7d7b181b11751eba19e", - "0xf18b71981cb3781230b8518216c324f706b13b1a871af74aed365c90f86cffad", - "0xce84447a3903e632751ba244999b7a61a403e92da74f07357523f1f067d48c26", - "0xbddb34858e366611a3e025cf6a357e891526972d408e1e080f3085decf603e67", - "0x7574828f83c1a7265aca08da6fc5d113cdd491fde3962a6c7dce44fb3352cafd", - "0x542527be1b6c42ceec8a75923d97b99416adc53c92a832f8e8e842d1027c9568", - "0xee384399858b14a3aae705182148a5aa513532d91a234d7c930330fde596d98f", - "0x618fd6df8a93242a489c5b630a4ce7e30a8af759536903f450329fda19596441", - "0x2c7129b3ebca24f916e6e252d71fb7d96666b1b55a9693e1bb30a8cc3327307e", - "0x1693e500ccc7a5005a8b52185014037b470bdf5fe112d90aa0e51db9eac80b28", - "0xf2cf8f47b5d3150e36815794f11a2ce96dc4fa4996d801e56e12cf7eab0aea48", - "0x088c123295156fd640dd0badcb95fc471044f0bc26d04aca7ba6fd7272b954c7", - "0x4403d7e8a25d39ea9c4bebb7775997065c90a3d4dbe0f5f2a4c7a53df79b164b", - "0xd0d82b3ae96c163b3a4617f69bdc9d4a9a65b90240675b85e35f91ca23d3b92a", - "0x0d284d21e9bfff443922dce492c3ae89fe99d9b27f9ceb4f894abde85d82a25d", - "0x5772ece7a44d81afff29b6bbba44bb30d4ff31b5fb5b7ad310b459eb48eb7473", - "0x15a33c4e5be3f9ff56b827de07e7614150d735409eff0bd9621aa04fe1d2d48c", - "0x84a218e73e539262c04d38f7b3bedb35e8cf08130dd46d91b7bf22a1c7f086bf", - "0xf7edd133a8830a2730a78659737973a324f455bf0cc1f9df4f7ae0d6eb67fcb0", - "0x6abb178f84769cec981c17606c3f2acdcbac76b8c7c21a1cb1ea5ed84a4e7bc5", - "0x538a4287396f9c38fe3c94ec45b38243f31211bc657cb753f6854297fa43911b", - "0x375bb5fde1e5169cd125a4c177dcab5ba60e41f2cda1c9b79c6e63f02ba235ef", - "0x4b72b6eed046c0f6295e55138f6733c322228d0874cf62030ad110dae30b0407", - "0x44794c2ebb781ac49e8693930da2aa2636fa683dc4c30927170b061b4385fe3d", - "0x0f9166e9ae260c9d6f950533649bc47a91ed78e57e358d2818b3cf8b66b413a0", - "0x2a25690b31702e586a4d1a21462d45a5a973d86ca6d545a41c35ac2efce48074", - "0xe9be0dd81c29721f6b8a35e4bb0fb8a1a8dd437a1932562407ffe727f3750834", - "0xab92028712829c8db7422fa7d93e8e6162ad820fb067e2a7ac1ee224f1cb54fd", - "0x2406f251b937c7236abdf78d7bc58c8f8f00beb689c9999669edd01558e9afc2", - "0x347201dd79965382f2ba90a230bbfc979cc35c37fbb5f21a4b8f384082434a43", - "0x94eed3164903b05cec96cc2d70f5ecceed69a7b1cbf2c2ff77a97042744b3c6a", - "0xfb8d2ddfaf7d81424ca748056c3997ad923e269bd2153169b297ecd02c74b113", - "0x8713d9cc0e3d9705bbfab9ee1f19a9f0df18c8157f6597dd53064ec05e873734", - "0xf4292118ad14c9a3644019696a3bd601589182215076426f6d12f9ce29f45424", - "0xb9fc592f9322030fe018ffbb80cf7ea0996970fa598be9abd6c97a2aed49fb37", - "0x5d9ac03a1a45cd56e7ec438b24ab98c52c7350cd5001af24c444dd451c4c77e8", - "0x6355e320e8310469b8e64784c2c1c13c939d61f55f53088b23634f9bff3a1d57", - "0x77ab799d4eb740269549f99235653da650d2457f95dd3b1cf95bf3855e517d5a", - "0xedc16c860af601fa7c6461f672ed82f8cc22749e53f084f98b08b0737f3b55b5", - "0xa217aaa12d7f566f29048848a586f5f43d787ea663a0e32eddcffa79769a39f7", - "0x1cda6f3902b67eb9681764ed2929c2d4ecb5e8d8b73e7d434557c56f2f2c4660", - "0xb654fbe4f8908c69911f2fa7fda440a015fdecc22c1eaf891d7125a5ae9cf0db", - "0x75dc3b61e81e8d7bd8665cb9ff254ac8efcbba3bbc0d406adbca9b68980cd52f", - "0x3752c044a4fd0d9721046f7bb68afdc920095cf7d107ebe8519b3b205b807b0c", - "0x680359cce41e959f611b5141ec02a42ba1b56041ed549fe3f7fb7c248b75322b", - "0x5342c1d8815e03ec965dcb702cad4a6b496e8000e07aaa737f2d6f02d4d979c8", - "0x52beae62a4d9cf632aea02c0bf7c3c9362fc0b1548cfdc2949edad071db77631", - "0x51555863ee848a88fa5c55af7fcafd5bed4c329207cf7983a6ba8d3e5b93de78", - "0xaac2cd4b648da07cabe53e355e50964604f65f0dcfed74df8ac5da4fbaab93ee", - "0x5124816feacb7422233640b7f2916e24c48cd5ce922046404af6cd517664b630", - "0x69837d6add2cffe312fe18c8a6bcd0244b3d1646fb5fc81cf25a15555e89d278", - "0xb18671100e02cf1d0280f04153e34d85c1bc79e9b18d464f4c840852cd2b7e96", - "0x464d0f22a9b071a24860815710deb7c724f63788ce1e69dfc5958426b7b43887", - "0x165e2391d76a02b3c15c2ba5d418598f9b6b43b9b62006583a1031e0dc1acb64", - "0x19e8655c389628902e2c1b0a143572da7287d0e7b1e30f61062f3d886a170144", - "0xb01f342e0fb810cdede0a82697c139671e88f9b542dc5e2df2e2715e11b9ab09", - "0x5b7c4bd8311a53ff3f56dc9f5a7b1e001b7c4a02188bc58e2d9d92203c211707", - "0xef874c2625b4abe006207e36af5ecd98a92098bcab4238b3e3275a92fb476b54", - "0xedc237012b0e6b3552e05f9360a49fadb1a174a74d7046218daf98da19d4f633", - "0xe5a7ca7180a946e7a8fb73e6f172914c82bd9571649f7ce17efd2cd96d89d875", - "0x4de22e8a3ee27deefb58f39c3d420cd6ef2c7d94b10394d0bb6ef7c7b347b6ba", - "0xc34745225c53661da2e6c6cc8a462be2741916917dc387d7eb6dac8a55063c37", - "0x562928f070776132a3b035c3860b1dcab90843f077336bd13ec7ca0e330b3ffb", - "0xa2673c9aa322bb6d2ab24d65934c7730585cb1613e4ab4e117f25849c88f0119", - "0x8c7b3edb36156e6d4e36a514b42d3cd074295a13f3f095a116ae5198acb64f69", - "0xafc6957c2f88c2bdfb63e89e83fde2167548f913e62297936e0cdbbe4200fd1e", - "0x075a81ff82b52704a862108bb2a4a1839b462e52c8562c1161cd56b00fb7f1f0", - "0xc9ce0f30ab9d986b6f1cad16a747224ac9566ac044dea341f29e0b0fc7a94504", - "0x7afbb952c21475725af2856f114277e2b76b8a0480f8272cb3be41cb514ba442", - "0x2c4f4fcc44f7e2a5892bc232f703ac03f8d4214c02c997ecb57cea861cc6b0f5", - "0xb909cfd7699a69da5342d1918d179fdd3747795392f2caf4619b3294217516ab", - "0x21a802b502b2d4265b9f4a736e5cfa5a7c9346c17b42b7161d45534cd39eb38d", - "0x72429dcbfd5a80324bfe744bca71edd4d71001ba7f7e37c717d84c05277fdece", - "0xf9d1266cbf5d78fbf7d36bf81fe0f4d6fdc39f6e3463c7e3bd334e703fc24b1d", - "0x910367ea4a43e9d466f93c73025dd99cb535bcb1ec551c9a2f5ba97bced399ed", - "0x74f5f129ddf7d2c306e55c2388eeeefb6f8b7e5f610a0006f649588d46f748f3", - "0x8f6d359b43e9d9a265a7c20bab54c6df0fa9c4f3d38cdb84537cc4b5b15acf30", - "0x97bd0d30c019aa3828ec283910fc420a58251837195245dd36352db4027bfe63", - "0xce920942f6bea6d1eb1817c480c6414d1409a63df38f141a3675968b8ad20769", - "0xa5d06dea02d06548aa83e8fb16a8741071a4353a60b9ba1ca530a51a7d945de2", - "0x60af9aca626191064847d505646c185266658e1f22b3eba2f407ea21eea69ab4", - "0x9207b93c6b728cc55dfa0dae22cb777e05da19e20199d40abedc926f0e510d79", - "0xe425b207ab396a9b546da766671bc8d304986c728a01d164e7661c5cd46713b0", - "0x2e207fe33286a8a9a47f40ee828162e74b60262733bf8e59b7f3b0ac1c35dc41", - "0xe72aba9e82b668caff99c2c8abcff3c7cf95f441eadc8f54b712d53cc256e41c", - "0xcc4987f0ed4e51f2f0be181808720ccbd89521b7797770393001d37df2d2d5fc", - "0xb6be663793329373d1007bd4b87d72f003e3bc6c405d729ce667d7d5a4730d33", - "0x90b961b68b506c048a3bb22fd906329e23b0e397f321c3ff55beb63726859778", - "0x86e0f5e1b9a03358bd5c9af44957f12054599d7c3ff54b1328e3658fdb9769f9", - "0xd4578ff34c20c7587703366d08db79df13eeb4222d6e4b59c8d19cfa2b30ea1b", - "0xf9f6f3e0290bdf5d3e61467ea4fb2ee5e7d398d69c3ad18f05ee983a694cbe4a", - "0xc3f71a9c233ecccb30db6d8dced87b1243ed9fb87095c528bda85c855bb8a315", - "0x0d31f22335d9f802c73c57477812974e3ba83bf66547bb2227e28c4b4db92ae3", - "0xe2ecb8549d9887fb377cbb921f44a4aea87d8951b21ec4f678b870edccebe6fd", - "0xce62c9fbce8b074c8c8da53b5aea8d47bdaf0318346bb5027ec4a37e02b43fc3", - "0xd3f0cc456ccd881bc4c9a8b777eb88c1eb0235f362f647eb413ba90433878ce2", - "0x26ed42fbdb81cf01c78345475504bb24933bcf227ec7135d95306218cf47865d", - "0x37aa161b3dfa8716f22f62bd132ee6d149312e079e43c60efa638a1a24ca6770", - "0xae8cc778b277a22245c3b3346b96995733219899228190ce912aec079b576077", - "0x246ad94e203f268d4f0a0f18b23c6fae5d1f43612b22bf8e981526c7bdfedbd7", - "0x466bd42fc5d96c92818771501b188660cb97e48f9d9f12cde6258a85a88890b0", - "0xeca775ea1a5e03baaf47c5bcd3b70c029c309812151e7298bd16080248365dd6", - "0x3d47dc150027f55c6c1810d7b86ec79390a7611771b959cc51fb02f5115742ae", - "0x3aa1f15ce5e5df5ce604fb6904b370bcf5ce9aba7187d73ca90d0a0995425aa1", - "0x4cb879eb362837a7423f051b426a8df627143dfb22c32ee73c969fd47fb9bb2c", - "0xed8ad7f8356fdc4943ad2da2e34bb45b4b874e2fec94b7c62b1c80b0d558010f", - "0x177d8d82d8be1c35db8608c61c478e72828901e405a5dd679b1fea6ed9f30eeb", - "0x3b895fb2e93e4a34df9688ad144bada044c76249842855f991912c1a281601b9", - "0xfa0b99e265454de96a05424e4f33e252297f99238d8db0a7ef82cbc9bca6272e", - "0x81f15f0ddab76af9dabe0d7baf8d14af2afc88332d76833453118ca9798041ae", - "0xe0fd68f5d09a2de8b00eca9295367f73f53e4cf8b890cf61b9178148bdef13ad", - "0xb5cfffd68192f9bf8e9dad4c26f8520883a1f8ece8da9c77326aeb8bee69daec", - "0x4b676f13ee697d50569b222d0bf9b17dcf37efc299ca7cfbffa1e67fe5237434", - "0x00f61c387fb1f436bda1d0ea9f29e107056593e6290582f3640c673de97dbfe5", - "0x2e982786a7951f28a99f8bac00ed2e778dfc2893e68f89000a60763b8a456278", - "0x13483c74333ee06b47d832d832e5f37eca6c4f736ef7b7aa199ea730bb9c670c", - "0x6b12d14476194ce0d9d279bd8c5f83d58be571144b7987b4a481dc8fa10e8e9f", - "0x4b732e2ba405ab706988a602378da042a0a7a6c81965974a1eae029f6e659064", - "0xb4383796f2772c11fc056e60b9a9ba4d23a05c7bda1b23082e7b7fe281f97ca3", - "0x1ad86ac7e2a91ecf27b202746fc8dd57f66e721d14c3cef8cabfaa66ea845a40", - "0xde67d8828e9f223bf8bce95e293c68d8c1678eb23cf7ad81a333914b5b9864b4", - "0xca00948f426ed34a756f36691f3b3b17368d5568296326c06894bdd358271f6b", - "0x1588b3dae135ebc9479b9236c08654175568425930fc3c08c0a3cec3d5390780", - "0xfa5581ab4a047ddf2e4e11897e8361e8ce24603e53228136c9eab918580afe97", - "0x0d3769ed93c991e5b78d4a6c76782f3356b1d24204815f649e6a8e853dc04472", - "0x8f1ecf593080524d701f4cda40f8f1f2704e3994948b32ddd222003609958af5", - "0xe0bd90aed7b8c294794adb61a94ca02fb9fab693771c2de1b0aef8790c75b185", - "0xb00d53d20179d5702f140623c913c5e42e09cbc36e571fb15b820f91539ec5d0", - "0x0bffe46166ec23ba7f066df0cf14179fb47c126beb8a83a0f1b5e06495ae9cc8", - "0x07e3dc084c4f702ac32dbe9db6ac078b19b9fbdfb2305d51d4da9e2d33c87d4d", - "0x5bea17dfd96f81ae273151545eeaac0402ee6007621fab42c74b92833d190e34", - "0x3f078eaf9025ab2f7759848b5c33b0ebb9aab95f028e0ae0f5085c73a3fdef26", - "0x2a6e8f2715be40302b7530e63d9de7ae64b81bd2b77b3d378c496c9d42d3511f", - "0x9b000c787f530038dffc5d69e15dcef0c39dee75289c32c9ae2f11139056fa45", - "0xb96dbaac29d6322da225208d61c756f63df4a7a9a2d41591a27f7ea1a7af4e78", - "0x4b20f795b7e7044361f319a0ba06389a9825a138a30c9e2cd5b25481f2b1331f", - "0xb90f6c8dd72b689af1d1f92de10de4ba48c10acc8d9921b50a872b4ca2fdc86b", - "0x09d51ca40d7ae1775bc1ebae767b7981f7a66c39f303d024793dd73f48a8c512", - "0x71fada2f87226f9cdb9554620d07b42f8279c40db9ab220737769b440e1ba50a", - "0xb52e3c9c0ced2ab02d5d4d7c4735ff444bc8f016ff9a8ce8bd9fbc0a27f783d7", - "0xeb49ef79318d9f7d7cac1ef41a82021b1f84f5bec0d1223d34bd0b067797bb25", - "0x78fb3c876e62793664cde7117b9268f443fbf1e23fae935f6e6e710e111098b5", - "0x3aaeb697dbdc34598493556cf9eb57e35b61e718fbc050cccab4bca362e89549", - "0xd75400305a5327eb5a0036c70e015a2d286f8814be35abce2d5cebd3402f587d", - "0xa362f91fba2627ba450aa041f02f4644ae2fc1c5a19cc02c63b01491fd650bfd", - "0x1fd2dbeb82034a11d5c14d0e376dddede1f8cc2aa660c6a2f57ede57e2c0d2fc", - "0x444e4ad03ac7305087566e07dad8b81046f293db32064afa64e31bd9b2268a21", - "0x7f7e7ce8b73c2a6f8e6a9d9bcf166c7cf67e2c04d9a2a5dbed89d271ebfb529a", - "0x4ef8be82e0bcce97ef82daa382af63539c3258fad8ab4e7c7f815d753a3efacf", - "0x1b0d1400ddf579ecda24a8b0c9adf346b51613febce9ed41f5dbe28e3dc3f544", - "0xf900a2c448235dcb11f46e05aa04f9214732a5770da9f6cf230462a644698a4b", - "0x8cf03541b11d9a0763160367c1fff20b72a377526bded7d3f1deb6c165a10cb7", - "0xd3f1c1b6ab0f0284e788182e1d6a8c7fa92525ed8621b84a2b45938d8d3fdcd5", - "0xf00edeec9fc8082272ba3996e6a2e3ae8835d6930e9625f20a599e579bc13eb8", - "0x971f80bb7cd7d81c0d719e69a908140438a0a3f04b44fd573afe70212e6d204b", - "0xd09d192e746b64f924dad1349488c563103b6a51f4e90438a69ee1d0ddf5a145", - "0x4a1446a3d4ef051a1ea3dc1e461179a2a3374b78c312209e32f62575ba2d3902", - "0x17f4a89d23160692755fc601893f501abd0528dc9adf0d62fd02e77f0c7c91b5", - "0xc122bc9dc4fea4352c119974d7de78b1f6e6a9372f7d0886798445432889c635", - "0x0027a25ffdb229b2681216719ccbcf770efe806655c906a7e7c025c9a855904c", - "0x4dbcfeff4e831313e911ace5367d855ac4b661a3af6c81a4039dbaae5883e8fc", - "0xde01cc015a926be23495402e93019c8647dee8a58dbe6a872107e41bc5c807d6", - "0xf53866c20b7e0fb124e50f21fca74072f33581b1330e106596339f775626eb75", - "0x474ef22e2b54d652719ed75c01d84546eab8ecd94df1c081539e0f7f04efc281", - "0xb655eea027d9c6ac99917dd0b0edbc38551aeff00e18df5189d3784b848ba9ca", - "0x4142871ec25245d2b8172d7e8965a21761f3c9841831b7aa0e039d6540046f77", - "0x4c31f0670a4ab4485d377baa440742393f648461b741696c9eca4d1b0f7f3e55", - "0xf47c0076e4ca3702f22856f7524c563a8efece0ce3062e0112b75d018a01552b", - "0xfa7c6eedd06a74bbba072b43ab37a86b836e6e60ab89b68ecd14ce9153f9d98d", - "0x2dbe33126365c2d5787fc4c095402b2353b84d818a867305aa323ba2fc06fcb2", - "0x31c5c6a750102bb70a45e88b5a42f34734c7b6945d5505e53cda29819d9d4b34", - "0xc94162d9b47606325c024de23f77df8abce2f5b797c9b11296af55df5720ac98", - "0x24a99f9791b2952dd6923033c68b2af947be8017a6a43ddb66e7974566533a55", - "0x0c7cae3072bac27cf58898d9cf027d3f3f662a144635b25e068135006925a244", - "0x2706bd0afceaae0cecaff80d91f5fcc023039d338e028d72142df40f5484c16d", - "0xeeca13bdc1c0f8fb83c839277efbf01e13bb27eb88b64fc137e876bc750c5e8a", - "0x1c16006c6abfb954121c52d04797ccf15a1ffa6df81d70088fcc7cdce14730bc", - "0xa8f70bb282ae54297c053fb0781f4de76d888d3f9b5144b4c6dbf40e40652c2c", - "0x5fae325f5e511c10bc6f084897665c0fbfd3ed319f2e2c2230a4563f5e22caf6", - "0x6c8c138b8aacaf293882b71eb3d15b5de6610ee192816be02b7a048bf57b0a37", - "0xc05764cdc3bdc40b0ab31f55863cbd8b7bb2206e49de9adcc05f52fa6fa98c24", - "0x38ec63286c922eed91e1049fb5bfbfdeca387fe8dba9f15cf6ebb3ffcb3c2a97", - "0x7214dc68ee2c70357ffc20a5074d1a952a4eadac92f87f0a0025d98a896afed8", - "0x8a723d4ddd08df26a6c0cd5755e03d2641448f0440e2dbfcaac5cff7a602e202", - "0x9567fb04ded1384f2ad1cde98ee4fc47e6c85adeec299952ffe48e8c788411e0", - "0x2db99612e0813b685b023f596c66f5a8835b76c54fd0fd8a4a4036266bc9fde7", - "0x6f1895f5f59a5065ba07f7ada8ad706d861b6414d2a0fb80f4ea1a437ce79af9", - "0x338f901d0024719b2fd7b74793770471e049f55cd421c021d8326a38700d4b06", - "0x56c0494b5617c4f8146014381cce5a7bf281774ff31e6d62b66aaac9488799bc", - "0x0c5365039c79856fccfdf5f7103ff2d9dfe63224d323009f364e61b716e8726b", - "0xf047dbeb0fe9062ac0339c104b8c05698a373cfacda540e760105da80d46c36d", - "0xe905e3beccd5ba7666f7a825927400e51fcb9271ef7a7df95f564181cfa98ac3", - "0x15fa4d3d0b5dfe1231edcdb9e2fce7b3af4f6616f6b36fa989864a3f9d928287", - "0x8f1f7b2ddeec2c7beb8b1991cbae9e160ab8b9955ad38727b4c54a841430ddc2", - "0x3cd200ea95174cb21d1b2f36f20e9a53528636925365dced2579fca38d5356c3", - "0x255fdc3224022b132381a13a678edc07300b8069fcd9f21c47d20fa53180ad4a", - "0x155fd13b7e4d8864d27ab0194a89dd23b63d17eb8b72aae64f6f7519f3c4101d", - "0x40f3a4e9436b85432633bc85ee8d84f40ae928d8f9ee8837478bd9f6e050d46f", - "0xe5abc38fbb49e4b3674dd42acae031dbb4bcf6874bf26ca0c7319fe2a519d81b", - "0x7cc31f9b707830adc72477ff2ab79ea168311d0916bf2754e13daf01d4813e30", - "0x19d333afa0cb54a5ee3745cf1156e2daacb6e8f65b03f464320d4d33077c5c06", - "0x38791f1a32f4028172dcdef9050ddf5e83f03604dc86dd8833b65d833e347ec4", - "0x53caec8dfaf64faf8ee9fb49b9d439a1839cfd2a114e3ea59e80e5c924b0973b", - "0x401faa9732506edebd8b33e35f5054a1ce443bcd3f2b2e2fc4d131851cee8433", - "0x24280a731445dba6180ff7c0e99982c967541e21b5e46dbcc52e75e10b198d52", - "0xa61fdad71e4fceba924a9879b2ceb789a5161e9fa75df55303156bcfdb66b79a", - "0x765cf16a2d32f2b63f0b418ee2a46a3b304466a57f6e190aafce53053b925246", - "0xf2bb839a01d78d92b65f9f10ae712ba4ae9fdc187e5f41cc165d3e1bf21d7a37", - "0xfcf2a12414334e043d436e0bb8a2b0e9a0c5e8d390bd0947f737a261d8e27e3c", - "0xb85aa727377d00d02551ebeee6ea4180c8ec32518b208750890aa5c3a7927d8f", - "0x2da1a4d44bb0ed50267a8f7988b5dc7d9a01b621e863990b5b5f2006a1980f9c", - "0x0f9ce379a089978484ba7f865dbd8224bba2cec1e3274e477faa3d098e861daf", - "0xe4dbc55e6939aff20fb0b052ff19a2098b7fa4b69379b8d72462a22a8ff00546", - "0xbebddeb98b2ff22f6677176b065eaef5846fb6b78d77c0ca14929a6a8db5df62", - "0x2028557352218dcbb7a6e019be318d57acde6df729af6c4db59af74a4606f253", - "0xda6f0ac6e72cf93bf863af1e9e80f1da1c48ffcef6d7caf0fbc59d16780f5cd3", - "0xc3bbb6df580ce3a65e4fe1c675dc252fa693f70361f7039eb889ee7566abee27", - "0x371c7ee758803a6858ffed0313e1f778cc1c05b421583984ead810bb4685bbd6", - "0x7fa797720e3f3fc8b0e29a3058b07fd30bce95705bdbf0a976362c1e0b3edf6c", - "0x7dc0a39d699c351a188e485e5dd1ff85c53bc8a91866b34078c15c304b989b35", - "0x999e2886bd5f6c7c32ddbc08470ccd1f4590cdc460a845a0ce6ef49354c9d207", - "0xb25308afe1d597488fe80a19a5d0ce2204a0d03f79eb301f69f778fb13dd0c34", - "0x767809b36c8dbac06bbf20bbbb7f289f55a053d67e6f36fa94218d3d97212623", - "0xfa7a7c8bc8bd8af5cc6c8aa0259a64187a7b78cd93de0ce14ec13df9b2141ce9", - "0xc11f585e553400d55c7a4c3e50f368e615ed5adc3c0e17b51e36a10ff780c395", - "0x8b12c9644197b3cd18543bf57473d48e76a9877dcd0430d1401e47be8cee2ead", - "0xed66cd26d319267898cbcabaa969a20a5f72012e9f9891f30f51b3deca75fda8", - "0xb5bb169c156368c3ef7af609fc0fc57fcc34cf33bdd55abc2805cffe5a6298f5", - "0x134fa3abd5a48083a735a1fdc3c261a56e9cc1a3c51fc889298463d040dfcbc0", - "0xb7009411f50f3c68b77424ffa4e1fbde8da1ea29ee7bbe60c35ee0bf281cf3af", - "0xe9c1cc94016e1c4fee612a5a84e1cfb267ad3613ee087549c2be29ede59883c1", - "0x9aee62d9e40adb47b5390467e2de8e26a412f2d23e7ce07be29e120e73107c86", - "0x560f92c67de947b45e68badb021a1a6db67aa40ab15a9c3d3af6caf077741c19", - "0xdb78ceb6ba758cb9519c04b48ee5f13aad1bf74e219c6793c62a2e66eb2ae486", - "0xf23ed67dbe442d5d0df10cdbc0d4f7081cc9deac7720a7245ab487df5c436b08", - "0x4963e648bb6d493050b48e5063c30c8eecda734b298ee1911b2d494fe200e853", - "0xf77b9ec5dcbebcb725001af466af4b51e6df0f56dd888955d8bf11ceb613acbc", - "0xfe1844dc936a73c836f70bd3ace2f71a1887679f86bcce0d8ec051f8395d58e2", - "0xce072d500d2a3abbd538dbdd8809b7a8de9b16ceb46759c2e5c62d8335b9ac3e", - "0x628480dec741850e8fc78b39b0012e21d2210ba21ff506303b3a30ae7120242b", - "0x5c003242439be0c9231d029329366a8ebe26242e3f7d79216cc360f77194b996", - "0xcc9b55e2949e9f1fd5787b09fabb7f48140743ddb4bfac049d53c07c6901c936", - "0x8c809a16fe0c1c49d5243d7c808a00b80d090e76af66f139cf6ecd713b664a2d", - "0x297f44efa90f5a5c9b82e83d9fd972e0db020e128ec55b769268c1a90ee1ff98", - "0x14b10e0a6f2c4b928235a5f904ed196e8f2f0b7caa6cb22702ff301b0df9fdbb", - "0xaddc83c036d1c4a53ef9779196982402dacd2d18fdf65c59281a2707045d3403", - "0x8b1301861c953e3014841c61bf82c5a29e2c2f275c272c45145e1bf75e11bbe3", - "0x9ad184b5e60c0bd82d1e1cc97c0bf9342da816fdc6929f64c2f47f20cdb8e9a0", - "0xc0a59c2f537d5c7bf8c5df55f02fed2ff08041b261aaa41acf62c951a1c826b7", - "0x9284511705680c802e137409d590c84ae7b29c793d06c596190cf1bcfb868808", - "0x10e7c8f871f620e2a85a36874e58d8ce093a47a2472b5a91a5f18893808491a2", - "0x6617aa1817809081a7fd620506fa60b5b21ddb2b58444108de37ff279ae24ac8", - "0x2eee26c860c64ac6efd4c9490d2ad09079ba71e4326df3f44af24b4e8edb1e7a", - "0xb902f640d25c45aeca0cf43e382976fd6e69fc85e440d589e95d959ccbd1fdb1", - "0x6446d62f00c80734166de70f3567619b72900344c89b2ffe1eea9c30941fb74e", - "0xe1fd088edf1f823f441863a1ac3befc256a20c0aa1735f9a3143a948984ae301", - "0x2095465f0c5b89694f404f88734c02a989e28a41e8b8c2034043da962ac97588", - "0x97f70cd16dd81987bfa047b8944771331a5c31b328064dc8b56fd8b2b38d8a89", - "0xa18f31897f4bd29e1b77525f4458990e8bb7608a12221163037a55f39e7641fd", - "0xebfa2dc85ce0486ab35e5a7236f5747a3bb972162cce1a08a88e8775217581e9", - "0xc2a10452948ec591e4109be62c0d5a8bfefef2134246082a61c1d996f11ac4e6", - "0x4c24ad9e32fcbd76d0d78262e816b427ecf946ebbcbf7969461baf3435a022d7", - "0xc533d00b4a0526cef84e147aa77a9ad4f6787eb4f838aaf0d539c33ecb769494", - "0xdf6926a0b2c8a72abf58a5e309fb236412d541b33fa8324bb2ce92fb0d0ceda0", - "0xdb89855b6b608d1c71689b46f0af66bd918bd98d9653073c9a875a6dc1f7a273", - "0x72b64c8154a8365e8905ef0372608755b70608af7ffa588da0978a59f985d0bf", - "0xc1a4486e65e4d5430c4e63ea471b52057fc1a2fb4343459605fba4e764143fc2", - "0x557443144308f0bba5bc64fdb1deff50eddb5728d64da8b0254ec63845095d47", - "0xc42ea586d77568d096e8bd45289fbb4f42e89176a59b143b5cfe2893fab27db8", - "0x7e880ce359b0589c85a219a545d994c38d1ce05bb365906f41c55c06038e635b", - "0x3daa1c97b81387cc076b1251bef684defca282480002265fe122e99769a0e84c", - "0x2e04dad2a0e7d29fb5b4a357af29d0cc6de6831940622bb59227bd6cb200f0dc", - "0x542ef39a7f12a13bb20cce43191f018360554e56d624b23fe73e452d40601bc5", - "0xe77d21c5b989e6848b7145ceeaf96bb3ce53aec82783124efb8b64ba106c7adf", - "0x6d1591ca6b8988e6bba0fd54532c2c2791960e8f3e82a70d336cc3a78ec8eaab", - "0x0a8390be2eaace41828986333caff47e69935549d6012054d8c5ad8383f041c7", - "0xd90e8723f2d6eddb7f7bd77b01be62a9dcc86fd701f21fc456173e233ba5cc6a", - "0xa21bba87e6683a9e16ece09590fb96239c34c64c5f94e421f16a21e07d4d07b9", - "0x80b11c2f9f3dfb333dae65e77b155f789900096be59d18aea7098a8431f57903", - "0x52d749a1e59a55474f013323cba83e31d37069383a2e8d6e65cbd741d2e8eb3c", - "0xffc5f4ebb7d58d1efac8831238950ec1c7067dfe61f91ffc9285679c21ab17ff", - "0x6ee683f4215196416fd9703ca94ad672f13ae00464d1818478e02cde6bb70982", - "0xa1d9b5d02bb39166ddf32c9cd08f405104f2205d1ab0c7e54aa181f29d827402", - "0x5a782d8541372e665cdbb2b42f178681f40b2d232f73d52bd54765a23cbd5418", - "0x76b8a5648028a7cf9fc4c3eb60697a1a08b6c2f3b81811bd393c7608f7140ff1", - "0x20248198f84d13e945335ff385ccb3668a64225ecd1712412b1f6f224f8c8480", - "0xa922a3c4d2707224fea1639492948c91ec14ddc1b3ee46e03627544cec57dcc7", - "0xa4a95f2d10a2d809f8fcb8f16cc6f8179b62fcb26b789705d0686bba1845489c", - "0xe8df8182eef6fdb724d192e0254573c829127a4064122c9507a1735c009ff6b9", - "0x0dd37e064f7ae08c0207e1dd7699436aca14f9e5d39436e31539bde492a2b1b0", - "0x8453b3edb890a8c6b6a519683a149565ae64920f168a40b35c383a8734e9945d", - "0x59d569c95da5ef5841344f6de16e262afb86039ed3419fbc92c5d2ee13c1beb8", - "0xf154537d82d894a8e7b4226339fcdc953a4a9a11d1213c440166b6a58fefbf89", - "0x7a422d65f4befd89e1a7c2da40d482a6219169e5cfa309a364d1ed0663515146", - "0xecc79e782017eea87f640bc9d9ac7546aaf34d21adf1a3badfef6c48aa76211f", - "0xec44728bc6c94a2763b466d212017f2645c7156518ce4e3ecd485499be3ba6d4", - "0xc9a98ee4d90084697a5aab3665104dd11ef36adad2c21ef003e40e485ea444c9", - "0xf8a4273e27177619d8fde9db3c0659cd5ba9c084f717b697eba3e9ee7dfb1bbe", - "0x89fe4ef0342c6ec695cd14c518fd4e9504dff3f084a802cb0905fb5f6d03d6ca", - "0xf659cc0c0edabecac9a0d95fccb0eea911fecf4e53b9bb3b28e6eb9f9e83c682", - "0x548e708e7a2a136ae6ca621502fe690d6b1e31781cd64123fcbae8a373c11b2d", - "0x1eb65d47369949fca2f497a11f19df8d33fd8f5ca58b804cf504831d08f2fbd0", - "0x494b951ddde767f53900fa2f5c7d1f0dd8638d7fca55203283fc30049ff06196", - "0xcbd0b45603ebb72c7283fded1302eb15a46016ec46f2a64f4a9d7ef59ab6429a", - "0x99adb9987a54647d7b02aa589268bffd35ece71e82d99419b93ad2d67e071c83", - "0x73f4eca410495d4c91d9b979ec8dcd8a575ca89ea089ae3f09199b1a8c3ac9a3", - "0xdb421ebd89476a4cba795edc92e7138433eececa609c17b0f808e9f3573cd992", - "0xbd858de7472f57e2ffa0f326efc6f6834775741317057a4d016d5a95d35a63d2", - "0x54203ed14df2da3d572b61797d9287bf245f9939aa25a2bf29da5db6f7738738", - "0x2ce99eb34c785227f2fe479ad4cfefcf1c6df52201478fed66868f070789fb68", - "0x3ef8a4b6565c6a5840b235d759f7afd7837f5ffbf99640e1587a0ca468376add", - "0x42d89367b255a60f260b889fe98d2add3c77955d5c0aeca0d6f587b0d7709ba2", - "0xe04ce561b160f5fe3beb5de71a97f9f69817928d6138ce956701843d07a78903", - "0xa1c1d23fabb55d905627b1dc61cdbd2d657b981b3ff205f64b8ba04b96b766f9", - "0xf64bd7b3dc213d170be5f21ffb557affe15fdb8384e598c7a262eadea8812b35", - "0x0f50953c45cf831988de2e75f5089cec774b44cbf37391f83f4550e026f758de", - "0x5855905648b09a786527b259051cd04ca511d373849e794dbaffd751b6d47fd5", - "0x209170773e78428bfe4d424ba4b20e174949c1c475f1dbc0e3a77739cad4014e", - "0xd4d057271afb9983e24516e13f690d3a5946ab0bed28e294a5919782298f95d1", - "0x02c16a3265a5e3bf8847e29e57fcc5f7138b3ab479067c6d6d682d2904442069", - "0xb1d1a67f9eeec0a9e8971b860031ec9f71c0b83d2696a2041d0e8e7976683f2c", - "0x813cc8abc863d66a7e60c7e91debe714dbd50dd8f51d6769fa083c561b6f1292", - "0x18f0b5bfbb64a57634088b9af13f06030723974c5176325db9dbf71443ece8ff", - "0xbc2511f70e93a9fc24763c14f37b75582df78cc627b934681fdad73e5f94aaf8", - "0xe04f4a7390a9af34d31314e7610a051574dae583fb31112b468e85b230b08a27", - "0x68afd2ab749095cbf8cdbfdea79d99cb69560c00cd1cfb18992e7bfa278ab60b", - "0xeda982aeeabab6b0f305564901941794984ba5a752ae48f01a9755fbbc3a43c4", - "0x394778dfc7329fc401ecf8a15109b23b8b8ecbbf4e7c800b47cf9468eee95986", - "0x9c0b1978ed25aa551972fd6d90180463d5a9539c06ce3adbe3b83bee26a038d2", - "0x46220a848c83a60e656085cfb7f3c977d8bcfb6368e53fb0877e8a013ec1c3f7", - "0x71354663325aba30a19ab2f2125f9ee901c7d142654af898e6f36e62b9d47d21", - "0x701674d55517850c17882abb2d702a42c52a26e4f341ea33b7204a9245bc7fa9", - "0x13723d73f227a389518f9dcfd81ebbc49a8edaaae8ba63c9765ec62bee0c4d85", - "0x61f9b96a2f3980056aad14d9906835a5a13f0894c530752c6ab0a6a1b316653d", - "0x31fe952a648a9a2f7b8d89a3a57758be3cb74028076237c70318915677885b0f", - "0xd566d66d853468077573024a844328a58aa8ba989424d66bc135db308bcfcdd0", - "0x5aac4e8b1bc902c7210ab0b56f8b58eac98893d2c6d70dd33bcaf63b97cca60a", - "0xc570c28ddf514637c8e07427b895f23d52be931a492220d6bd3dc3d200d105be", - "0xf7f7bffad28ce00b8254d4df4b5079cebd236de2acd9a253c09636c9036b99a0", - "0x59ccd0578c26e64a359d45367b64a97b97f43a85189a109ff8bf7d9acb871dca", - "0xa9315a3b2e7decc5a653c7510f5ec7737737a325767c469b5aac929b4581de5e", - "0x576ef478c948410ac57c074cd25d5d7da97e90fdf9bb7db70a866931ffcf1ffd", - "0xa9ca2b5a515934c38c66d2588408941f1f19f0b898c6ebdd777b8b4110387311", - "0xe8481a5871b10cc7c278c090d0115811824941f119f9e66a391b43bbfa2a6ded", - "0x5dd99890bc491b58398406dc9b16226cc47c207882f94c2a58330579c16fc256", - "0xa965ccf30e1cbb5fa76359622da6e8d979f3ec487e7f788399da9c3b8af4853f", - "0x09113412961999f8d3e80628b8d058a3a6f8416f4be682402b91047a73e69b46", - "0x725b185229d9ae3b5a1c2c15c2e5a9154518df50afafb8c100020e875233a7e6", - "0xcd871a14e5672abbbb45c142a44f937b868c4e265dfa68b10bae64076c68d7ff", - "0x0a257cf0c92e10a16c8fea72fbaf2e6f56c840f2a031589b70a5492f0a9944c6", - "0x64848061f987980ca330eb74b640663ca391cd9735fd2e8bb863dd42961ada7d", - "0x51c27314780be532857fcf9d678d4ab05481f6c43da4527aece3c39a4ba6975e", - "0x78de86801da812219bd71e972e621b437bcdd23ae6a2db4139d9b62e5c40a463", - "0x5c98c32dc0ed6d507965236eb7a718ace9678f6f12bd5f81b68ae44144a15d51", - "0x3725833155294cf2ca6e1b5d272f0dc57130ba6e3a2df55111900f7caea47818", - "0xda487e7cd0f45f93431237f604218ac3d9d439607037b153ce985ce92d16be03", - "0x3dae2a9c0418a46d752c7cb5a1447f8b4c3788788bd4c6b85583c3c149eaf1f9", - "0x53dac7e119929c41a352b46eb54ba52ce59ed5b7283d0883ed48d0f562bef921", - "0x7e302413f400b0ab38f202140ff892e93700c4484e8cc132d7cc115606684cdc", - "0xd21bb71e163b1b4aa2ffd0ce2232cab26b8002a999b901a03484bfd6253f3fc5", - "0xb0284e9fe043f920de245b35e0770c75782b9bfc81ef2ede04ad382a51c6daa4", - "0x9d2efeccfadc80b413720f27779590b366edfb599d81efdbc366846daed1b77e", - "0xf39a139bbaaa2880481a3aab168fb350db74e3b6d6ac34159b1ca4af4d47a850", - "0x51bb2f5edef77de7906c0816826288657eeed00a1111d79df311bb904cd784c3", - "0x071529d3fcd41761d977a0b344932013fb1289eac00d2ba19a3ac5e91e9281c5", - "0x0fe0a5f987e8de4355bae9f7eff1a664d2d2175af90bbab4eef82f3c2dd035bb", - "0xd1094c0c293d5678e7a05b7e537ae0040ba4202183c682075da12c6e02d3f4d9", - "0x6319ffec1f60e3b4659329566b596ec0c875ee654a8fa2ce2cc7b96aece2038c", - "0xe8fc2d78e8023adb761ea580001393a9e0306fa84f9abcc9d6f5d8875db9cdf4", - "0x7e75553809968c9e97b2ca64cb05809e45345d91daac452989a55dfabfa22bec", - "0x985fde0528b9843d4e76f2c23f76731ad14d8fa61f4b8e35d2a32cf7cc31a568", - "0xb0b679f35bbac65ec2f7e134dedc76d2b9f23a14c70faadb2bef1d914da6d791", - "0x1586492da00013d3c6ccaf65690ab7111ae6a83d3f9034d6c26d9d8072eb464b", - "0x46c861b96700ed966bb9b6ea6c5e87e188372eb2e08a1ebaa01d0a46e91b3cb2", - "0xd0457c63e7fe1e0992d1a617cd04a51eff1524d1a18afd66e71c04a321a7c999", - "0xd4863d6fb4d8cc7b62568f3960da84ceda8fec5e84fcdf031af7099942e93148", - "0x4b47db507a812eb9db6b6b2eae64e2b1ed9c51e6877e44beea9132309c9af199", - "0x7ed56768a2fa6c29f5cc1eeac629f32e017076664d5739fa62fa55e3138b18c0", - "0xcc7b6315c0006ae1e93701b345b83e01ede46164f8ac0a0410f5d1a1b0ae63b2", - "0x242f2b4ab0febb86bd74b9da83b1efed9949a05d3b19297f928a3501aabe644a", - "0x8e1b7b621ca8b594ed4b10b52beffa8079eef58c3153943b71dfafa2bc6a7a22", - "0xce4c217d7ba4180883f09301d1a77e9a0c44cbbccc099658dfda1c3eb715ee95", - "0xccf2da237cda4519c41dea0a3067bfc3870f3a6ba073d44999ea3b10bc32c5be", - "0x0d83386aa9ec920477eb85492389f45659b57be79678df596a0781a1405bb6cd", - "0xc74c70ccd1d4487e4f6a8e77ca09cc394b6870e343b4285ade688d1b6df60e03", - "0xa8f22312c6ef7a0ff629dd0c64739f055dd09ac4245c913976e4e6c168f8266b", - "0x9c116ad329b4a3023c5c7bf711c3dc4dc0f9581d68847354b9ceb57687a76643", - "0x292024c45930a03895ab4b1e7478d7d784eeca1f5d61fb1fdc03eb8c345963fb", - "0x0b21a6dac04948393361c6be1abe46f5be0b28ba158893702fc80b0aeefce2ab", - "0x1f5799dfe87f6ad4931696e36b3c5813dd740ac1d025f0db490cdf3335af28c5", - "0xbcf97b72036215bfc439b55440103e37a7931531795d93acfd00d078e6e88cca", - "0xace36522a0c56067bd29df38c2d1e620b9abd880a37425dbb6f5ab857f9c5743", - "0x90c3dcc279a85988c6e9de3ea871bc89d54202e03355bae21c63a4d866a4cf89", - "0x8aa5e6a53075c91ddcc28fc72170c4af931ae9cd63d512b596e9863fed4d198a", - "0xa80aed681718e923660067910680e3eb057e3de6acaf0ca99f005a796503d0ea", - "0x96e8541615584f709fe77778589ca0a17f07770da17089f976e8b7fc302ec77c", - "0x8e2f41b07ad87192b79e3d96c18fcf07a170cdc9b23bf4624b8eb80053a2dc37", - "0x16543b7ba202f359d888640cace56e300346a475999a8122667c48894083dbd1", - "0x7107ab2fca188dceb5e4b0a44eb53252c54da2dba0aefcbe00625e689c2b5bf7", - "0x25f871a59182a4b0f043159c7a716562c7d063025257b89dbda19f5aef68e9c4", - "0xe70b7a990644ee1278e92f3ba1cc6a9a40b3761d8973ee4df91d3e3bd3ad1a4c", - "0xd9948dc162a681b4102d82d01faca492bbf057d2a0ab1ad1aa2d7ad2f5d8d591", - "0x6b911fe9fa1b430a1b7eaf963670cade7032466eccf1fddb6afa9954316c7378", - "0xe3453267f7c86550138eae575f5874fabb4df7cff52a9b7498281788d5be2df9", - "0xd1de756ee8b3e650137e2f1f81be3cef8389fa685a8c2606edce0f470c5b94e6", - "0xcd78d6840591a94a41b6c43486738e3f9537ca7fc529b73c77403e282eda5b05", - "0xb6107d17b47d368d236930768d2329917a155355a17c244e9b6a70df8b899d2a", - "0xeade2b36e555380abb2926f45a8809ae4bb9ca9fc4bc5c2848b08a6fc157ebb0", - "0x6e907d42d9292279e00b5e3791646cdad8bfc5802f02efe7a2bad8beadad3832", - "0xddc9d4963a1708e7114011f41b7003cbc351fd3cc6907638138f968aacfc2b3e", - "0xb9702fee4e5e00b451600c2f6479ff852f41cd760ddfedcaa9d42912d199da8d", - "0x6e0b2e6230e55b58b0caca85b86459fd94a17dd2427ed23ef4d7468e200b436f", - "0xf0965c9e876cc2cee03a67098bcc0bb8ff91c29d110101d05bdcb06e21a94eac", - "0x79902f46ccb4f63b07dd38c88a495b2837a0a984ccf4f2643bd817f353e947b0", - "0xcee76907124f92bda1e5e492de67fcb0e1900302f69682f5aab251e55ea7df0b", - "0x9df0c40c5e452168dd9debad103645074686e09110a50d205c8941d27d14f74e", - "0x4128d10935b67e5dc21e8e40e5c343fc4c4a716455abc0dad24578d4aabe402f", - "0x1b49733ff37e03ac5cfdb1f84a5d93beb0013dad0adf8ff4435b44b20dc0dfcd", - "0x31d0defb61c386a751690bdb4f32e6377ae2a761e52511e524ad9e69961fcd0a", - "0x438cdb21294f0547ffdf5269eb9099cf52c66e45aa235eade26636a13694c90f", - "0xff42d94f2f917a3c4ab43d4375a9bc63a396f3fd120f0295a4b6597e53815b44", - "0x6564791af7389ec58bf575435a3ff1e0d3ce0955c7dcc3b75cb4c85c1a1c8b46", - "0xcf988277bbaf273ae926cdcc57e88b9a2bd916a6d0edb1d7cd0a92cf85d98ad8", - "0x45b8aa768c9ed9d99a3b12cea4ebf803c458b908a65f014a779a79c3d31020c4", - "0x2d96dff93a3df327747c6a2c6e7c08612a9d40923207d8d6d52dccf33caf8661", - "0x9dad452c7da8aa798c9489ba142710769c03e5ca3701673649baf731edb41e4d", - "0x6418305ef9d02d3382450684eb222c9761e94b66a933d9ef8d833c08ad4c6b9d", - "0x1f58fc05ac706d32ff31ae9dbd533c1df2481b1976be571efe22f8810e7487dc", - "0xa9af008f96370a80f1034840d00e072a391d523065ababe94eac561005c02408", - "0xa0384a9854110164b17189b10c9b6ecb22718d515256d12189b4799c1b7460e8", - "0x17f88aa7eb0072037f2b6305a2859c8100a3e2c8dcb6e13d8b58e01abba31292", - "0xc834fc15604decfb4bbc68987f20efca8db47e1d3fbeab2a1f04bf2151b4366f", - "0xfb92c461809fbb8ac4abcab126229d5d25f5476d6f732d27ffc3e31e2db18e4a", - "0xfcb1cd8040e7bb860eeb20cad1575747c9ca129192c8aa69a9cd373e24b1825f", - "0xd8efbd48289db20663d5b087c678025a232fb83c4be1731468ad2f10f69afb8f", - "0xeb5c8eeb8c1e6bbc15df061c358ff7cb5e89e6e3ed51eb63805bb98f0121c683", - "0xaf9d7a05d1d1c3c9d03b008907b30762a435df18c754ed1fa27c9c84a8905953", - "0x06743818d15eccbc13397607121a89575a6da7f9d23e36b07911fcc516e4a9e1", - "0x32a05f9a1c08b68500074915f04daefd0a6835adc985558b62c3b2867109ff18", - "0x40e0b74757199b6863f97f14fbb394b2bf518ac06d8d499615dc087b2d105b89", - "0x09736a30759c20cfe24bc34a40aa85eb511a0f49021934d856588181969dad44", - "0x3dc24b923a7c9593ec6b77220c873a06420bd8f5ce2c2768364074457bc31cdb", - "0xa7e78dfbf9fff15ebd110198711b24f35dc7dc6cbed06b5008036570f4072abe", - "0xfc51a0ea5862e57dec45581827c35aacdc58c6dc410919b1b47e3ad3c707f630", - "0x1c5b2600a9cc29f7d3587a5aabf29b27e9f85acb99acff6a5316312a713d1f77", - "0x080a076a84bdc34236149a6ce92885c0b28d5484c80bc40b7207c45a1c2487fb", - "0x7fd2a3a08ecfdceeaa56952968836be5e8adabcc7a2a00e2629604db5efbc14a", - "0xd214eab26d637a7820a0babe4bca0ef9dadb0482b3847a71fae571f44cc93e33", - "0xbdc71731bd7440bee58f131d47db5c2d4ebe39bfbd9e2fcbb4970a7fedd631f6", - "0x617e379b135d0265b15679e464dc7fcbe696fb8afb64b6b74cde6fcaee043ac2", - "0x96ba4dd6b5bcba95f7d97f6bb3dbf6812825f5de86e12f14dbeac7041854d8db", - "0xef77cf072d0edbd8d5243ca73384a7c2e58cdc3cbcbc5f8895c65c62da30206c", - "0xe75c1cf441c9dd52a7185e3c1f1e6850fb03bc39d06d422da1ebc81afba7b910", - "0x5bad1ba4e6e0a7fa060cf8766bf8b3500adfcbcb922368da9225a36e61e7a469", - "0x1ded514f946945a7f04d26757ff39d9731d8bb28ae304d803bab9a484cf4cb8b", - "0x40a6f34a19594944e96bd5df119c1a06a2615c04924b7b8763c4d9d5c0b8363b", - "0xe0dcb9dc1ad2f5653c369d9f25d4f9058fb8c512e5efd208b814e9fd45540550", - "0x1a07a9808e43a3c473d1e9d2875ab32ec2eaf9ddec432f711554503f927582fb", - "0xc196ec42bc1603eaf0a2348dec8d850d96dd4c275bbd3de356e5a23519002018", - "0xc793895f8aa0e9ed1c22f0aca3b1cf07ad1bd69445d9eb5d51c453bdfdf0dc72", - "0x1f0312147d7bf69ab363ca3a3b9b5a0e43316d7e2c36be97fe0553d2d3225fe8", - "0x09d8080e41bce3b07fdabe7ab36d0f45880c0cdf5d2119930d61233d1d079751", - "0x5863676dae69ac932a7a463e93d312365f563a59acd4086d7c60b61d56438b28", - "0x5c9d57ed61a4994fdca105841fd91eebad100ea8704c9a12afe488103daa6177", - "0xec9c5ef3fe34be04465270a7a215848a00a31324562e97c3d94b7b799b6474e1", - "0x4ee12a6911ab23524291f800c361490221460ae9d72085ed69055126b3fac836", - "0xfc6be696f5c7687c511ba1fc8f4581c792e17876eb968bf5d1e7c6555259b98c", - "0x82ac807f85d8c430464c8fa8f6eadf6cedd5707e8b4aa653ac66f85dd39bd1b8", - "0x08ddb03882de2be4d7ae9165a41746fc76edadbaedaa6d8587c0560c5c989ac2", - "0x7f1e6e84f0ce5f9bdf199f579f4c57a8d35ca5b4bfde55d20e2d7c092e6249d8", - "0x7caa4b3f32f0608e86a112afb9425831ddb735bb5b914599a11355102de0ef65", - "0xc52c467d635c36695e1fe850976745926250b0bcbf12835ec344231e326e7bb9", - "0xd31f10dd9effe25dd19d3eda5863f452bd3dab36cad1cda545137aefe1971eab", - "0xe3c9f6858652e0eb2314c8019fb633a14716a3c130b767577b2a28ee7af0a581", - "0x39a476f8a5268f38e24775566403135d6cc26e46a7480eec576cf9036e96a9a3", - "0x78c798b3ef7f7054b427c22e2d6ec9ef54ad3edb12edcb43ed46fe63c624646c", - "0x2d9eb24d4922203e2d55292c4b5c983b86f56c23685a3a6d57af191c65a06db1", - "0x434d433a6316106fe94059d2fc0116b7181f13846df95892d78261322fa724a6", - "0x5bf38bcbc125fc6feead6a7cffbe6db1674a1d52f4a3f91a29833a730b711218", - "0x852f05410a45326c6118bd9ba185dae319464ff3c0a603a2ea02290646bc53b6", - "0x80db0d9f5a7f2e02f3c19c3553bd0e3151fab7f36f7d899f29770e9d440a8073", - "0xdaec85979550d9dbe8d86e665ecebfb2b055836f1691aac6d64d1379d0c39e7b", - "0x1d1b18c8b2bb47b70c276a0c32fa04a4d7f5b8646c28094422faf0e177bd1b8f", - "0xe248542589724264685768bb7108622542c2d6b2e4659e2fb97d17ef7256f1af", - "0xf095b4169b0e1d33b1c24f6ccb835f8d0d6af4581f4f34feaf96d6713d70ef65", - "0xc24267de7db63ee856dda7184d50cd71890ba7017602191c3bc44e2bb1a1d4d1", - "0x624dc6dc69c1484d7531688ddda389e0ca00dc09210429d6c97b17a0d4f4e9e4", - "0xef3610a3412574d14a7549c5e992ce9fd54772f820b9485c5689e135832692f4", - "0xca58354e960aee45575f4d5abd2ae9d7aff32a7b1bd0df6e3d0d22792030dadd", - "0xbf7b2f80533f34efbe4f6c6395fe8eff19e08c9d964e619e35bd161c4e08a70c", - "0x85482474a66dbff699ccc3756dee5fe88e447448d44fdf2627bd488cc8abe984", - "0xcc598f943c693fa06111c77f475bb528a08e859b2bc6f2871f4ae50eae40de4e", - "0xed41aa8bcb0330da03b360350601f68752dc060873c6f0fe53cdc4f3c8da662a", - "0x047b5d2a97a701b76115ce5b8d71bca40594d9e163752db3e38ed8c6b0448ecb", - "0xb2ff504219c1b24b1483a7e671809202713f9b2dc888d1b46df2878ef830a8bc", - "0x01038d50899f0167b59681287f6fac87cb53dbcf7b4992631809933afee11870", - "0x306c6b040960341a198c88b01bb0eb3e2eac36b483c343de16fb8cd6f318720f", - "0xfde3d4cf83046bcbf18e6a7df3857c9e1bb75f441a4265135837085262814b02", - "0xdaebb2b71d2443003e6efecdbcaff086d0d725c88684787110135bbd15291833", - "0xc0a0198a6e57544f119523402f97c084a1afbb06f0b4d2a41bea0434761b6054", - "0xdcce26167709e6cb4fd29584c83faffcf76c982c5e17726fe4dbd1105bab7439", - "0xad717a26abd29eeb240e986e635fc0add4a6f7c8c9ae116af49b2e81b4eb62a1", - "0x47d513873b8c72791867009b293b401b7ec05cf87dc9f9537581805768369d00", - "0x497839c4688753a5db5eb98e7968e9ef363ce85cb275d40c3dd46b6e5113c3a4", - "0x3aad7265aa7bb39710356bfc3e6c555725c357b323fc281b2763a0f0267c8b8d", - "0x36e21dcfab3aed1c7134789149da4d61b2fee5307f033013380b892065e14991", - "0xbbdedc8436db019fba5aff87ed3c97a78ec2af8b2aa390f1b0f925064604061c", - "0x8dbd104b838055f34d628ff1fbf097152483fe53e6d930ae4a02d37648b758d1", - "0x365e37d424fb7ef1048d0f032f75e7dca20465f0709a3994c6ee903b1818a726", - "0xfb32c65070bc2f46ad8d8378384adb8bc3dae96dd96a8914204c607ce601c3de", - "0x30fe50586990daf14e84ef2d337f0819faf98f0bff730c77c547d34c20ff14bc", - "0x8b2977d229646c3552e4e6a7657fc3372f0a9e744b4fa11edee9f5ffbab99e60", - "0xca3830b76e052f65833a3c8c31c98bcdf8d81f74952f5c4ba32ac8f4c3791d38", - "0x4a8a34bd2ebcbdd69e0492a9e784263b73db418f20f80ad6f1b3ea25702c3bc9", - "0xe9730415f60bcb2f412e676d768ec3e25af7d6439517497e7b8f86950e83b5c8", - "0x42fb83f3376821f23f3316d9d01ad319bbe6b6cefe1dea1c2cfe89639961e668", - "0xfcc901dd86c62e3b1da4af67bb1729b28ece9efbaabd086810151e908218920f", - "0x1a40ff9ada25ab7fc14832c33a3c75cd0595cb904eac7d7a521c67ce75db0dfd", - "0x690eebd8fdd05117d5a4c7335af0c487e4ba70c9352a8c03ae7c8c53963a82ca", - "0x47582399dbb91cacadaf022957b48abe25c7f301a1232587ac3f0298bf4f03b8", - "0x900fc815fba237e026a0f27de6af96dd1055351b7a78df050e8063e67a03c4ed", - "0x807880d09bd8ce9e27b2a7df7e22c46a3f28b81db767a08269006e27a776e0f7", - "0xda996478eb696f963ea50b28c899293f1682de5bc56297b7f14bdf64e35003d8", - "0x630a1fe703d16ec4eb67043ff86c2980a956bbc45961ab343059b449a58016c6", - "0xdddd65b35951ec094cf2c01e505b4545845bd4a1727dad0eed5592e3f91323d4", - "0x6d6b2560db0d26b56f0ec336215ab3ea430307aea23e94d11ebc7f8ae0476ebc", - "0x942d35f09119613dc505286bd8e5b692c5b6ab6b456bfde3bb5e60e6875d41ee", - "0x2336d4efeffd6e88b0bc6e7b2c1759b35a7147e8a441568d374deae1c2f9a964", - "0xde9da773da3969677a8171d1c20ab7cd5fbac9ac5eeb05e161dc3f62c5ca841a", - "0xd4f4fae337a4237f02e8620fbbf077c8d39864d543244f6ea7d08a68220838c8", - "0xcb59a0f2c5ab1feb68118a8c59aa873813a0adf65730f2e937814bec1e149151", - "0x256f444bbb37350ca565dd620b71b28ea15e53994f2eecb394a0881ffa42d575", - "0x5349078ec5fa82d5cedb4709ef0a2354208a7f85b37ef7054bb687ec8bac265c", - "0xe577d5e1dbd5819a83089c701a31df8ccfce478ef9655fb7aa21399745b5c59c", - "0x2fd29cfb8c1352cc78d930de9bbab8ef007d0541d8fd1a8db7cc5cced076dc04", - "0x6ff610d29ac82ac1a5b92ed47297c6213c90146df4d668985d9d157891e81a54", - "0x376e36ba81ac50e32e314bc8860af7624d31307d6af0b1951c7bb03ec2e4b0d0", - "0xc2af74f9c437ef0f7b89b0663d900a13e98c5da81aa5020404a665c211feabd3", - "0x4de6b756c071a6e93acc37ff7fe513c585667be3b64c186c6e45ba2b3d7ae131", - "0xa31a0bfe97be03ebffd4a7f0565ff4372d34b44aa9b9db8b3943cfe2049a25f3", - "0x2a4135dd0986c731ed35cb770389b8494adfd6fb9e323f8b3c62e612ce9ae7ca", - "0xc5a683d3880f1d34d515c753f0bdb68750984df456345a7649fbd3f723539ce7", - "0xc89381d12ff71c930dcb24deb043c3837e6f8c221d986babc9a37585a724adbe", - "0xabbea8d56c9095aec399d7188aa12c8542fdaacb2c25b440d112778c3c8b914d", - "0x9257be6dec259bf4650857f8b8ffccb42d6ef93e55127bc23596ec4ca543d63d", - "0x0acdfb858aa8dc8e693018c2fd5f1d9d0192138fe5fc461284b0f737edb857f4", - "0x8d9507c1f8c22256288db6bed77a16b09bf2e83748503e5d04bcb971c2f49bde", - "0xb5cba97b5e52ddba979b4e74b1a02cf0a423fe9232327ae80fd935d9b2923305", - "0x5634d57b4b9c7a5ed15aa70d59b102b628a0aa47a785f84466ba549b60e49706", - "0xc41bdbc86fcff5dc2a77cc6997b99e75927096c3d56baa56f385ca8df583f6a4", - "0x1b81a4e0f85fbe1520c00931736061fca9a08a8b7f7dd94a65b863817326a296", - "0x12d983159015d26986b0b673451fb362496530a550ae6137f92251f99db2b290", - "0xb6f0dec7ff4e61da0006c02164bea709ac4fe11c20257f154c6a2400acbeae6a", - "0x7b98792ba33a549d893727b0df38352c0262de5dd3f34716e59843bc2588eb4a", - "0x8d33bd97dfa8b29def7899dacd161ba8b52825c281611e5fe3abecad7e57c112", - "0xfb15e7a292a8df0e9c5bf0be2f97e30f81dedcf47c3035c49f0a124b0bc82a25", - "0x6e003471f1c803451676cb993c9d05fbf50d390b8774e4dc891c7a76bd6810f0", - "0x6a5291b1e97e6423545db5831efe391e030337da2a6f207f71484e59835ff627", - "0xa6ebdbc3a608f5cd3e8e6238626a52ee541ede98cc9a8133e7c3ae18c0dcb6d9", - "0xb1b60d7fa6f641156dac04c81d1b5fa640d6790239235345d19fec323abf5d70", - "0xcb21b419047253c8472675a31f71aa694fd2753cd53125f3eb33fc7cb2bc0d1d", - "0xca6444ed3205fb31e0ca9fd84809b725e99476428a7949bc1bbc27bda16e9eb3", - "0x86ee41c48989906e9f5444a4b0862b9e55cae62512f75d1cb6126ea79088a386", - "0xc3d9d6c407c7254854407004426a0edb3b6ea2a9e37f4b3646b0573070f5387e", - "0x52979e891b3479834488cf0da863d6fa051c3f5fefaee825ae8f605db7a4bc56", - "0xc3921490107cacd64b454ce5cf113e868d32b786c00a5582930e5dc93a24ebe9", - "0x4b393efe4ee6de87a9ea89b81d7e5851f2c2d660cde41d22fa1a6e8c8d0fdb31", - "0x2c5b85643df44155127113d73c6c7a30d32e81dfb974ad18b676b82e911cb6fe", - "0x55b84b4c5cf71bbd5fcd9c9bc5130f7b0b70828ec142e9980a1f8e6aee36d0eb", - "0xdbb9f30584c3548de0bba916766ebfea6eaeac0efdbb81e9cdf7403138890115", - "0x85aeaaf7baee871ca9949025ad5b28fd704f0b1d4b4bc702fbf2f197463cacf7", - "0x976056b44f1af84778b80657a52779e4ebae64606253fc2e5502b1b8fc3da207", - "0x3647ad36808ee65e944f785f96eeb53182c0c6300893656901cbd59c30c6aaf6", - "0x159e33c4c437853e340c93bfd494dd4bfad9336e6ce4b55c4b488438672efb22", - "0xcb07707c4f0ce2ad6e62be5e3d9f7ee256ca7e767078a70860f7a11d86d1f969", - "0xd89a699a681bfb1d98e349747faf9b77b2733652ff252800d11a1c5e42c45805", - "0x66487f37ad00673178dfb2785ae9daf32bc6cbc5025e18c6791fe562e4ffb880", - "0x5314e684aa9d1c10297945dbd2258ad847c67c9855d3f3c1ed5a1121650c647d", - "0x5be3a4672b03615e7e2e980d0c68851cbae4753bfaec77f69d62fbc3f69f6a80", - "0x9d59c9afd57f37f565675c8730494238a7ad62a26026adc8c644274e615c8973", - "0x76c06d84cfd419b2465339e377186f316f0231c91999d2069f965350923a2ba9", - "0x677364bbe63180f825bd178974047c40d98e28e48ac2e43ef23f9f863aa4ae09", - "0x90a7f396716df1e03cfddd6c91a3acdadbdf761d7868403d1ebe2affc68a41d4", - "0x44af61cb5be5bf4cee207c7e2c3fdd4951c98385c530a2af8a41e96b18e60ca7", - "0x010d2f7fbdc7d82c47b33d66a5f3b8d2d86255df3a27c37ab44b1ad192ed344b", - "0x4b66f935d07a0cd83f0dce67e9cb7338cac9c343763af39fd4d5317b0e223d31", - "0x95788a4c1ad664283ce7c5e986aa45ffcc3f3ff78342765c0a0537359795cede", - "0x1d0cc84b5bcddada93d89a0c8165e0f62975e2e3f42be87cac48e286e8a58593", - "0x383c470137b4b3c0d5442bd5ffcebff656a0f6f93d8316f97ffa2530446c9a17", - "0x8f85d0673e9ee21c9d74cc3a5e286fd9fcbfbad8a67312bd9fdef4a84604ecbb", - "0x856f549c4bfe8c101e61f5f6907294b186c467b53bf1e5a365ae4c1058688b53", - "0xa02863faa09cc7e8b7d467a1e49aee2740fc72eded6b372566f7f24d39d98c61", - "0x6cc6b1d8eec2d925730f5b0d7a53a7ec5501e75bbe532d01ccf9ee4fbf04f2d9", - "0xa101c9828116728e738096a5bf3693e4c6034567280cf79f301e9331e8c0da20", - "0x08a8d10878ea9f75e586eed72d9972d961e0bae3fb18cdb59b97f13d4573e937", - "0xbe05d30470150602f7d2e7f880bcca34260abd3004f00176454275f1f5ba12b6", - "0x2293224a8b616daf818331ba7ca2984eba091d7fd2c5c021ce1ab8891d1d02d5", - "0xffd50a8a3afdfe098a5f7d8a4df1d98989c6f2154910801b44df299f3a9ca970", - "0xb2bc8f78673c10a805d2a02eca6ecff625108acabbdc1f0c083ae738a6bf80e3", - "0xd3d7800d9ad71590207b86ce8be28af5d3b844451d26335843345235adb33482", - "0x19a2050c8fafc25c023fc874645146c9d69e58cfa62c55d6fa6e414acc7230ef", - "0x1607618728cf21588549167c963f4decc154c39b9ca7535e4ae2898424a5b9d0", - "0x77ad7d04df11bf2568b6b4a43a859fd0c41019e3fc3622011af8bfdc9f2fab39", - "0x8a2f2bdb426c94200183bed7500ef3a5645a936b2b69de6607d5e0809735f3f7", - "0xd31e090eff9d737da218d052287c7bc575eba68e4c04c61afc1bf71fa1294d46", - "0x929e93fc4bba1d6824585e9c63c00b142aa010954d2659557aa2abf020aaa754", - "0xfdc40de28666194855d21df5466739de17b8569a6416cf8e06e6a5f923cbbc9d", - "0xcf47393b9ef0fb5f68c4203c01cb52acb6d3a7633dada6ef171fdc5f70ae3e4c", - "0xd8c41ec1d0a78a2310a1e9a10d69017f91fd73c95ba26743d118f989d29d6f6d", - "0x31c3e809f24ce309488c611bbc62fde0394852c627502be078bba6e4a345d5e9", - "0x4f3ef90c7a078e74d707cfe628d9cc23d2e592b25727f25ef875cc7926f7b58f", - "0x53dd78267af64a3c11157bc09f4c16603bef5423b46a736b3baa082029d409d9", - "0x2c8e9f60300fcddb98d044686dfdc94fb34df87b0088873eae57540d07446f64", - "0x7100344f97940317ff6df6a5bc8e22bafcce7aaf46c8621c2dd0f6bcc3b6898d", - "0x8958726b7851035b1ea0f5889ff74280ad428912b5273752b9e13c627411f4a7", - "0x6ef0d28e7a2e2faa51cc8e06bf00a4ff182977a14a3f45ab71a02869d21cc9db", - "0xbb703737301efe6a34ea929b205de1536fb1aa5819def94ff5cc56e872b3e825", - "0x69614a352941e7e09adfabb909bb9619232cef1ce8866abf0a4c43eb0a255a48", - "0xfad7b1630f6ac59bb9819cb0e33c93838d44cbf9182f598ce4bfabb416153aeb", - "0x2a4c80b6f28493e4fc78ba4b604c927a80d8152aa13e6bbe3cd5487354ca5451", - "0x88c56bbeadf8e475f8476663c308678c5bd7f5673fbf20a154b478ba0a83993e", - "0xe7265c624cdf5bb984c24466035e418a8ed08e5db12e76dbde40287d11dde5e3", - "0xddc2c7e6d23361d39cf3bd6af4b8125ac1f2d32e68f725d77144ed14d19a4ac6", - "0x8adc4525b31039f9d6b7fe2ce7948ebb389ad0fe1b2d0d9ba0675101377483da", - "0x92104f08380fada7c0cac602e66c4d754e703829f9272e126241262fdf7423b9", - "0xe7ad92563a04b8ddc6aa93079a456339c5e631b5e00274b4512d16b28fcaa0ea", - "0x05e51bbff3db812ec3e64f34bc4ca899345b69d7511d25d2b8c05a6f73ba91f0", - "0xbddf31021ebe16aac248f3167d34fb9fa00b885c0e859a1aed26f866379b4762", - "0x87d4a8d3a89be7d5eb725a472c7105451d796ac2e8d41c7089c4604b0908dd3f", - "0x2f01e7b47dd42062a2ec8e4961fbcc8db7ff6575cc82751afa362b6ad21e6907", - "0x8aa4b672c4a537b8c8cadc5900b15c87def5cbbf585760fd080c6369894bedfc", - "0x795a0298b19a0e660a41cbc7c772872f95739e56e95f3b48c6520a5c5e55eee0", - "0x10d0342907960372382e5c3a7ec2dac0e6f5c2bf5f6b57ad548a31f9fe11c249", - "0x073b3efb41e2e904a4e83e01a1b1b34e7b6bb082f41e4f76aa38b55de5d84a2c", - "0x12a10cb67c7337e71aaa7f79829fc0abebcfdd3077ded20de0460e100a1b6604", - "0xd76ad354d8ca67f0b7b33e9a87d5b8c3e9aeb190b160c01562976edfd341691a", - "0x0b0129b4a178efb5cd71bc65ae4e8288ac76449c39b2827284a8a59a53a8b04b", - "0xc9702a1ceaaa255e2c7db468857d47f08538280f704e595270b3d1801905c33d", - "0x67b6eeff27327be016b742cad1297b0a02459e4e5c1b1e16e15932a18698691a", - "0xfa25b1e7d3233021414f61158758dd1721838d41113866cbc763051f16417966", - "0x51af765c6a8b3aa1537da71d29a9bce4ec6a3beb83eb2451bac170d307635ea6", - "0xa93dedcdc389d03b011add42daa6b1aa4c77065b776e8ed2de3bad78afbc1c8f", - "0x278a468d7e1da09d9b5d017f180bf14e48825f9ad09f4238114f4746a5c29168", - "0x8969a4c15171d04fc703d8377b03ec827ee8c29803eae6692ae52d31eb18e055", - "0x69fd35b051fa7a280c6c0abbf340c572f6022c614bdd3acddc89cb0495c5c75d", - "0x2f148c2bf404ffb3041043444c5e589656571f42ff6b101f2f03b4afbc3c1051", - "0x24913a97dc2244e58f154efee2b772f4dfc8bfc41899c66b2cfcb49b5bf9970c", - "0xf8aa8972332906671779ed9639ff4092629758028d02f77529d56a9c89140915", - "0x78c99dc9c7f1d356ffcd80cdde5559c8742e3186b293ebad19f5f5c2ab172949", - "0xab301f68fc9ea59de054f4a14102e5d1ba312416870141319bd846ad804bf9ee", - "0xe0d75a30786e55aa94fdfade60676c098664d4bbda32f01467535b25f8eb2a7c", - "0x6a55d687db9a48b19fe238bd249fa58d7d963ebd156c32bc366f8d65a71526b9", - "0xb640570971229244b145c01474ee04a685a40bb51758a4d68310b4ce63ccabfc", - "0x1fcc3851ea21fb8ab94445b36830318b896edccd64c2d54a3001e198f639ab0a", - "0x70504f1c582a0f3e7d6d8ca8fe82cfbdd69b606cc000d34ddcbe0cd841cd9eaf", - "0x6fcd5480b8e6ae07f38c891d4202402fce91c3144f380abb041dc4cb61485380", - "0xcd817e8ea14464d8a65643d32a4ca07badf0330e96c9e3b4835e982ef2e32667", - "0xe6f62abe81088e3ba52c653b7d748352ac6248a8d1a0dc61a63a13b39f3adb3e", - "0x61a0b71216d09ce57e739a0690c7c833ab74a41cdc7c0a136a5a261da5d5606e", - "0xa5cb275a9a6ab6a127a2ebf6afee6d6d3ed802ab4728ab3ff7ac12f4050a69dc", - "0x97b3c2da2b3a9110705929bfb299b69f9e6d3c05cfc391dc43b339f6994c49e3", - "0x273ca1ea98412abc4d162eec30d669072ca8e59c24ebc17d7b74dd42721b1e06", - "0x392f8b4e7d706a08828684d868acccd647e781219f97477cad4e4755d5feda93", - "0xf3b821c71d040e10e515418a21f73a3ef0ef855cdafa99240e60dba2816e5cea", - "0xe527f8c605a625031c7dc74793ee6d1b1554cf9d5d344347a4a2bf5b6824f059", - "0xbc5e66d951bef421db1595db40e3638a2b8f253a8387bd4d43ded54c4c0846f6", - "0x7fcdc1fe31f1891fcd82c2eea392274adfb26ebcd7ee915b484d0c272838d2a4", - "0x58b7888f6776e36d6c065b3f8de88ec8de76f224ac0f15dedaddc36351ced36c", - "0xbf5037084ff4124e311de573330f6e482621e06df744c1086977e6c7465521f6", - "0xb76efd283b00b528fee1857ff599fbeda6a7ea648c62b37590dc9a1f8f872e5b", - "0xd2a86a75f25cae510761c30a843069e889d5caad4fb45cdc1f0caa80c32481e2", - "0x5fa32d5dbb1887ecf3b93aa544496b8d7af7aaa2242ea50ec96cc5c07da31c00", - "0x8c6894e707d014dc7a2639f07101fb1164ceff5687b3ea1af014e63318113c52", - "0x018a3d80bb4ea6edd284f6146918c905cc3c1c754e9694cb04c6e939e2c3e8f3", - "0x763a1742e8f9e998fcb5f0e5ccf9bf1c0c58a299075ceed7b273e5155ebd7a66", - "0xeec702dcfdef81a97fbc330f028427a5ec979081c3ea6499ef1f0c36fca59c8e", - "0x18ec0a483c6bc0ef114a9ce6f4dc609ca66167d6715b32720938a180889a6ffd", - "0x5a948e7fdc5150ef90985ec51c833ba144efa1cba509124273492a4597ab5923", - "0xe88fee3b7ce1ad4a6dc608a3a5a8412945290c97371959c740eb3ca74c65d254", - "0x2dbc9f004a225f235b5710f22f0f2c77c75455869c50c311a5617e4b795a353d", - "0x3d402479e699d0317ef4b93cab54df4a62d183e628c5b105455fe66c90c73e9e", - "0xc9f150ed85090f0df6b7939a9ad1496208e920fd927728d739fbe5357511b56f", - "0x8c34fe488619a0d70ec30c41eb3f9008ffe47f9c188e63461a6a1448660cfcb0", - "0x1f2db08ab9b278038c3dd9c36b2d279d71000758748cf089283dff7d04e937c7", - "0x98cb2dd0ca201bf90725b6efb8dccbb6d05776b6833ac780aa14fc44e901df3e", - "0x0d0b8e72e7834f89638a466420fbe3e2ab5e2893b7d9c8c84394caef0c45a468", - "0xc054cfc39bfedb31b8c7949b2ac1bd6a07b36c62020188ad336c6039efe1032a", - "0x8f25593a99b7667087b329a8f69535db5c695abd92fc16eea39322e28a5a7577", - "0xd38c37dd354a85a0a2279a87a6f2742e0f5c7aa61085ce608b9bd8eadd7201d5", - "0xe38245a68b4746d911097fc0bdbd0094673f8ec345d789b77850330063dbb9b7", - "0x35a08b8b3d7e4852812693b611e69972fb8c66853bc3b06d6efbcce14117ebaa", - "0xed25487bff2f3aa0db580f6f6579e0fb30f2bfa72de70e3c480d3d77ce393093", - "0xe88653f85db98d947ffa42a789eb9fad86abb4048df4df70dd4b1f94b8726ddb", - "0x500343b55dc6a2a74bea48858dc8dde4a091b9120782d57a9807f1db21ef9a19", - "0x356340a610fff9b3999ba7c20e2fe65500f5defdc8852b4320223649d7591627", - "0xf59edb887df597825e2c68d7f8e8e58320acfd46a31c904c58f38c4084488a95", - "0x8f94067314834a0dbeba084049c6f4508038238795bf45bf623bbdb8eb2cfecc", - "0xd255abad0c2f585bc88bc9db4211cb34353764af1121e0f75709670be507e946", - "0x3914fc87ce6cc781590078758aa3330449de03c6479bd5996ffe1ca533b7ea1d", - "0xdafbeafb230e2e8b44f963026475acc1e7260e7de6e817b7ba9f0340f6c27f50", - "0x7fb53cd64dd1045eb4efc6fb24d4b609db52bb342529783de0b4e44b5b3d3b81", - "0xca94b25fad418cf1eb161a6a3d269b8e3e2224ca95d7d79e903c1c77ee14a020", - "0x7980f221287a1574111ebbe83bf8c5aa443a93e3ff95f5d51da3026cf6606a9c", - "0x3e103b573e171ac4bc2d19ec450d8ab74c696f1b2cb444975a612a8b3f4f5ee9", - "0x45a7beb63386b87bc180332345f0f08eff981871555a5bea3265c312641fda00", - "0x3a779223233d6ed94bc336dffa268cdae36367c7f08a8b0204ce2d79521b707c", - "0xa9763a8dec8ae703b006f71f6b83a034794a91649878b6bf752d55aad4acfbaf", - "0x654869da214419be4d42aaa1f8964a867f24f5db590ac4823ef13c278e7dcfa9", - "0x3698360e59cd6586b0242b8a30e28f9ac0b20996f24607a0e3826ffb743011da", - "0x85a6b0319f93315c1a90d5b44ba88985e1d628c6bde9c370f7bc5e4ff21c05fd", - "0x3f6c395956cf1646cd0d29daaa38c04348808f1d4b4ab9b10fc982fe7f7859d5", - "0x382629e494c41827cd2aa72222335ac3a30eeebe4b4aa96c46c7b7d030cbcc90", - "0x1bcd92ac47810169deecf5ba89398ccfce61bce2575758a0ab4a2c8f85058422", - "0xd0b960c59bf7ee3f06f26d6ad1bf16d0800338f495573a18a264146e5d1a68e5", - "0xfeada9fbcec6d22a9a7e3b0e643257a1c6c827d53553997571afd49eb80c4bc1", - "0x647bdf6ab4a88731217f8610b57c98daacd424d89325c6bd0f2b1a090a7bf527", - "0xb67c9cda2ceae27f1a59b38215067f902bfbb8adedd10a3379dde408eeed0a5c", - "0xeab82329a4d0f75125ece37caa4cb4e3c2a65f9fb39722d28f9567fd09a94dfa", - "0xdd94ff13aeb4191c18446ebdcd1d2449824386b081ebcc056f8f2d8972b0b105", - "0xc9319c95ccac9755526ec36a88d6384f288087f994d66e3e00759b9981652d0d", - "0x0b4cf4f195c7227ebc79cc647cbcf90d852cfa4cc1cc6a305fde8ae870b637c3", - "0x25a7434a908f27b204eff7221d4c3deca22c20331c5aa650a1455c863fa6b422", - "0x2bddd823ea545f3aca6b2fa00a3a2c83dd358eb4cc4cc40b1cc7ae4167bf0f78", - "0x18adf129bd6f7bccf32f1b5ff4cf8d855f17093a4eec217aacc4e0c96e6e06ff", - "0xe0a2c9da78d9fcde12fb306b339eff25b9f29699cf292e59ac50c5553a744b46", - "0xcbaf7f6c3d2af021e9780fa1fe40eb2126e7aec616537fa5d2746404a5753ab7", - "0x8826d7342fa3469ee3ade0d9b169542d5cefcb0edfef48e14c320bc97adcf24a", - "0x6274f27b101ee6f88299b222ad630e3bca648d86ca9f1a6e96fb811656dbebf5", - "0x1811856349dc02b06179e224fa22fee284d367674339407f1af8afc2d4940491", - "0xd8dbaca1f92be6c60dd3637d812f27f82b5a29adada0cc07f36257add080c3b8", - "0xa861c65b35da72c78d5948f433def655288ce2d3fa2c314de9ddab022b0c2833", - "0x19621786ddee5d87bca26828ab9ce5dafd613d2443f2469d87a2764875c64d53", - "0x62c927df988349a637fab53cee5ae65c81bbcfa6bc9bdb4723e7bbfb81760489", - "0xbd07c71513a8d04aa90a2ad6c2bbcb320cb0cc03ca104c2aabd7dc5d49cf7873", - "0x7bc71b4726da12ca677435e46dae08b7bfb2c2a8f3a3c5b98aedbf35bcf06e4c", - "0x858a08cb660417f9c32218d9e1cd0698d60b40b25e185f62373f7747eadc1a79", - "0x8962c42bd8fba724ddad94d51776bbac5a77fb98a68d7993a0c01fb83da6bd9c", - "0xa52f685230c3ae71cdaefc5758a66273ede8c4be2dc3525f911d42a5fda2de45", - "0x62153ab8a4a28289e73bde016a4fe58811cd02e5d659cd9f577c3611493eec63", - "0xa30e5e6005b1a0d9d4b7e86f7203a8139c26c729509469b3f8050b03bc39ca10", - "0x5ce8d37d13cb9a4d32db0b907f035b29ed39b59522e8485d1e4495bb3dfc1572", - "0x67719bbd7f98e2869713bd54329da3e3733f411b29a1193db3fc4398c8505ef3", - "0x00f2dbf1944283fe0f820c499c52a4ead45c5393f30455a28a2e7fa06dddfdf6", - "0x6b6526b0c31b074ff0712dcc57167dd50483dbd7250c64a2d93e126c7dbd10bb", - "0x36eea96f8dec2bc06f364dcb217264187b4541ee750fb8d8fecc1b2126a52a64", - "0xed6890d6723ed588fe2710cb30561aa92c788e3931c6baf1ded01bf9793422e1", - "0x5ba666ce9f3a0cfbb7b1bb7a309066164d4acee27fc19fd565ff55b0611bd04c", - "0x6e2a394127418f2825a8fde981198256797ab14608373bddf13a71062ce3e4f2", - "0x72f4b4f67f06f76b5fab3295a7e56e0f7b7e4185fb383807005a74ec01ddc50c", - "0x0dd58d0c0eddcde1173a19b9a4f6ffa8ca7f400e2300d0af3722f41f0c9b3f52", - "0xfa7118a28e8ef78bc0156a6a2fcba9e81544c91fba55c325ec4794fe012e20b2", - "0xc49f629ec1453253928a8eb4bb5cf74f0f0442d86d0d84a7dd0cdb99d03b3a71", - "0xfebc1806fb5560b28bc14b704febef947911904bcb665fee2cdeed790855466e", - "0x6dc18cb8f447987b1a80c1f4795de09e144e12407edca405dbcc288ac719e94c", - "0xa35b1a31d28f990ea63ed366caae0f75871076d22357cc66a87a999a6e09e127", - "0xe186aba91e09aeaa77f470cc71bf8bc6b18560d69b399df8ead5fb63989a3b82", - "0xb352ec9a9cd2d2ae801dd10bf737588e4620a4d6c636455d33124aa1e5361400", - "0xb12c148effd6855c73c44a78dd9f939c7e687fd44f522277ae4a1f93f53d26dd", - "0xb86482d56ee61d400cc8d9a38224b8ff94a6c9beab33a6edca746e5f0b8f6dcf", - "0xa6dff0601988221497746ede4a09c19d5a7a62737b0f40349a6c24e8ceb1a469", - "0x837e26990d18a624aa08b87e97d608e7f4b75a99fc57a77d130f1c44d417b1aa", - "0xee1bd06e75e4930f67c75358f64e43de6d854e7add62b8d802574ad808690903", - "0xc0f1b5145d241a642bf06baf7dfc49949df0c10afbd8e8560a6d06be3b760b19", - "0xef4cabe0abdb9273e38dbb9e09a63c0d1193bca951d82332d45d01f260fe8717", - "0x4cf2bfeb5beced066e4b7f79615091a2f81e50bac5c8c2d9ed2ef2ac888d86a6", - "0x9ce4c946df64447b7ad033bc7b5f8a62ad50df16853784b8cca241ad5191bcff", - "0xdb5fa1651fdde9cf52b418c2b070ddbee097136327f6b818b8bd41840fa8f920", - "0x45f69323d4e6c4294b382019053ad089572cffc3839f3fc8ac1f9924a0edea32", - "0xb686d73075b45237319c2ff2f743fc2a6f60dc0dddbbe6f414daf712ed6796ca", - "0x34750b64a414231766d84ea92a374932d885d95a18be77d5d16d1b94efb048ee", - "0x6beaa04579373acb926103909f7c03ced095df81ca87452e6d69535dee549eb1", - "0x50830466f05d582254340f6ee211d4432cd4020d4be1cb7830141bec8cad8481", - "0x5c9c21a5a614cf3cff7afe70e35fdabee67c8e90782ef0cdb774d909de3d1509", - "0xf0f07c550a8e6b939fb24ea1de8ce1571e8d6c329421e8a3c897d94ef3198b45", - "0x70c0a4c9b70e9653fdf91c7020a5118b727322702aadd318809ce083ac646aba", - "0x0cde06c1b9d6f5c0b895fbccbd8edc839d9bc21af4bd206f3f4c1a28f7e849c7", - "0xd3ea6feba310996dfa1a6e4e10eb1c4c4edc0fe0d3825553deb601979da10fd2", - "0x7ae5715186b00b86b0aecef3b0d487c96f4cdb4a94711f2237ecc185a29013eb", - "0x1b2828b6d94ea3e2ecd3ae0b417339148710af02a5054f256c217b67f8f55fcc", - "0xcdeac54edecef790a8c212d86c45ee9e097927a04ebdb13891e5b94370e200fc", - "0x9869b5fcf70766bbf76223cae9ca3b8e3ed7e01c065f475f29fd5eca977816a3", - "0xb80fdcce699c4c07b0265f35467da77977c33a558b215b0b9211105b6cb2eb97", - "0xd5d9a3f5d8d9faead565d5de6fd5be3d5a85621c09f49bd3ed7217d59e63ef42", - "0x226531c505bddf99730a2bbad5a84856f3ebf64833368ef14626b331ae882046", - "0xfe6a0ff6492f8e2d0f27785a71aae65868c7b7278f45ac9c13031c6933e5ff58", - "0xf0d19b703b6e7aa0873ce3a64d77fd4dab6553ab6ef5c8db4ccb360d381e4ec3", - "0x1a6379ca31f6704f9643c2944c38c4894150029be5953205e09a5c49d5a83666", - "0x5dd3e7414ac5b924e9d9431d4f69e87901bd066045d6bd0583e92104265a99cb", - "0x7417614538f404ab719354131664f75a5565934fdb35fe2b329a034d052aefab", - "0xbdde0aa58fe227c3a8e01f0d3ed21bfb13145c551cb3b75cee85f0e62c52825b", - "0x25b1b36e72306d5de03b22c4f4a99c94c67b1d7275fa7346ebc990feafa95819", - "0xddbad1f32840a3155a3a17e4710d770b0d0c8d4b9c959b434da223b66882f8c1", - "0x4ecc783780b225cfceeeefa44dfcb4863e4377946511cbebf730b77857ae1379", - "0xfd15b7b9b6240bea86a898d11cfe6d3fa8623e416857910bd9b4213331c4276b", - "0xf710711f4f26da985803eaeb05c5b07e4147e5ddcf61f400a3cbd141299ace37", - "0xc9b5c5012c4f380ff6b25bfd1b46dc97fbf71d0cd25a7eaf799ac5f7dd3f2b18", - "0xe6a1cc39b7dbd7c2a5ba211960422d65b87fda2f38d2dc309f06250f7b8a686a", - "0x19556445cac490ab941f7a43e65ed8856d59bcefd50f15862ecb2e14840c9d04", - "0x8a7fc62d190322fa9fd9a2a70937ad05fe89160b9cca33ad287fb6c24fd0eb32", - "0x9aa81f8a637bca3e0c43cb0d961dd5fe175a3c1d8417a61fe059447dc27dde9c", - "0xe45922992cb23f9b60bd5f3e3687d7c68f39ade4976aa17791d880a2f0068198", - "0x6b4d46adddc2335abe24e9f969b094a0495264607906191b38e0a176e9e280d2", - "0xf5bad7f048acd435c6d0017165ecd85b376fe9e264523b186c3439787fc7126f", - "0xe6029636af0630d9d84718419c0d540cd6738237cc3c5138b189af530bc65e7c", - "0x5f93c70bec8a8cd6f5de0d7d929d70a978d1dce3fc693fc6a8954fd0c2c0e739", - "0xa7aa14a7b0b6773c552eb1b8aa66451e4b368e51d6711902f932e80a7d54fb76", - "0xb48effcbb7bf47c28a17318971a9ee671ed249ac1932c86b1748715d78fb5419", - "0x38fcb78dfa98a0d2bcbd6e883a19d8d8a8db86e9f021b9fd25f5a309b3e91375", - "0xa16e055ffcedb90aaaea09441c16c94aa90beeecdce9f90888e45e7915e49115", - "0x4d536f2bd58b252a36b77738fb4b6ec932d914d27c343a77c3a87e2b37cac722", - "0x7f8e944dffdd1d797cfb1c9484f757f9860f61bdf5bc369d4ec26b740109ec0e", - "0xb86ec6ea0e69c1928fbe009e5fe1da9c938c36581b97130474a1fda44a1bd294", - "0x268d430425915224ec7453fc7113eeddabf368a6e60c00af6af4d46147a5d434", - "0xf2f2a68002878abe219f5a1f1a13bb2b2e84dd2d39c685e2868068c4b613ce11", - "0x691a1eb375b6b4e66c3dee0f03c15a847716a4a76f561f4742f7a7774925f6a3", - "0x1e13e49ce69ca70e4bbd74b020daa2851d96ded8279deaedc846339739eb790c", - "0x058ece03bb6385a48ea9eca0e35e5853ab040c6ca5ba372b2045b0597b426f55", - "0x2f2b782b6bce115d03df9c13c6a7adb7413531ee9568dbd9655bbe5ab6b06141", - "0xae45abdd224d09b9a05a4503cfda3d2e24db0cb597d370e9b5d5da2e37111525", - "0x3c51f84f818ee4ec36e79e3c42abaf50973bfdeed693f6d0f42a2b8c097723a1", - "0x642d6459728b8d6036beed800d20d59280b8d5ea2e210677ed04a5e7973fc1a2", - "0xfd9ec30b1a0d27c8217e3bceb75aa545e659609ebc144b6e4a23fca1d5e90d35", - "0x75e0526f30a2c6c6ca9add64d1c3ca20ff988f22db1e6fbdbeb4dc982bc8f0e8", - "0x1f13605cb2f94d3c8743c8ad3eda5bf7b2a6d3c1c117945744aee8b8ca3289cf", - "0xa62c5d59536a4dd61c0403f85be6f247421549ea7b251e44c21b010a3ae4a76a", - "0xce13aa4fe702750ae5561cbc6f2bcbe8b0d979f7d1751c80d48fafe85b9125ff", - "0x4c34c6c651fb179649b65ee60f93ed64a2ca11f6699e06f9e8e8087e60f81a3d", - "0x53a18b307f80340116beca13bbb10b05c7fb6d65cc4bae466aeb0d40d9da74db", - "0x62897236794db02fdb58a255f08c7abd253f2885134e64ecbbcd1a2e20154705", - "0x996a4c7e9143b80e5767fa637d5370b94591ee336e0f23def3d83f7bad0950d9", - "0xf7e28bd31a3f39a93ccd647030680f970a358bcb84e7a555dc16c6cedfedfe22", - "0x54793ae31a612c9d3d4480b110fe2d2603484db28336459606f672bb0e01390f", - "0x9635672405c8519bba38c1a046f309958d3ee84ca9cce5722146725bf2ef933e", - "0xbd69316d6df7de8a3191030317505d4c4cb6ca8ff5bfd2fa517b76329b912fe4", - "0x3cbe0d66905ae6da05163c24ea90012b8c3acae38fa277f2d8c0e7547a9d7410", - "0x7de8ffaca8d51bb4e59ac89e4b45668c1065996374537204a57870494304bb38", - "0xec1e60f536cd00ee2eb039c48f2100f86743e20e9e8911fae2afbc27d03212d6", - "0x643275d0c81982562e1168553ddf03756d6a813ad92981140879f886133d7203", - "0xfa9a2ec8e05caac288b5fad60462e47fdc64d7b15a0a7f09032431195fbf6e92", - "0x69413e7a6027e5d92d2288b5f028b9aeaf028c3926c779432ed15fac245d19a7", - "0xef20cfc76f417979ce7077c979576bb534f5d5bc5d052af9311cd3cd4d2175e5", - "0x6e560ad9ca9d868231be72819523df157459ee5cce76fbe643e9498369b3e585", - "0xe7739c40398901cd392e091c3a89eefcde07f9bf8e9ee7fda288e3ab24e199ce", - "0x3ff97067449cc13213dab1a46bb66a69997ff1d18efc13d22673145eea8420d8", - "0x1c8b79f893de6e0b46c6f624b1976b47de4ad1568dcd90486dc8283f570bea04", - "0x9fd5f70a4b995d6507e6a42b48ab18f94ac1623e81a147b40c2455ac7ec5ad85", - "0x69373e6e19f7128556c1f21660ea17556e02a530c10bbcfb2de5627fd8e07b9e", - "0xacdbf097f72170bc86f923b172cc4315a8e81cde71b034e2f8fcb9d6752938f1", - "0xbbe3ef5451e45de728039133ee6447b9c2c605aed3a8f808d547ce0412a84a51", - "0xce506260a6f1a254d233e3b32246ba6b0d5ed91d384f55d1253a28bba8534c09", - "0x0a9d69289d09eb6b010640e7e79256d585d73e3dd6a09701c5a8aeb80148e3c3", - "0xaea8a91738082e2038f741b2b66e763ee4490db3e219e7a13e573adc54574d59", - "0x954fc41b9313524ef8584d96fc74b8f81dbb5ca23d7997498716250cfce8d4be", - "0xcc551b51b81be31d97868efa1335801e393b595cd9e4b303d3746bee55869f16", - "0x77797967766e311fcf5d4a3e89a08a7def1b3561346e3f7815b20ed609d505d0", - "0xd2ca2e8da6be2144ce29093e936a59079d9063f887335e7344a2515a8f0827d6", - "0xba3c00208b678c67d753904474b980e77ee05935633697933cae0feadd4ffcb3", - "0xa9f0edc2d5e83dd6b3f6a8fa6cb24f5a11e290e9cb0f671e8dfbfb66e338ac5b", - "0x7f4944b4fb1fed2b17011748925d7d3d88c6a4bfe3331d03da00cd2303afe9c1", - "0xf8961b65f1d6d7be13006a64bc4094d57a6b44ca314b6cc136172e434ebebb50", - "0x4f51ef2c609c7d26d4323cd169dfeb667a841d646a8174830a34370d869c8c71", - "0xa45c68290713fb2c841916ea5b2b9bd32f3fd1c004328b99ce07fde9ada41710", - "0x3623a5202ffa0877e3f5050afd668810e725415c6ebe3003b866ae80c54590d1", - "0x688b978803343067b7004c81f391f8e32bbe6a729289ee2d34bb2cfb86611342", - "0xca75d366a63824765081bdec6292e4605607b1a7580451eadf72efde7135906e", - "0x19ee65d9a8ee8246ae0f0f3c769ecc7b2ad30763872c10a48d502514b30c46ce", - "0xbb08f5f21189afd3bb2049b05d0e6486ba7c2f06ffd968ff7c81cb0071ad672d", - "0x9abdd1d768e82aa8ea6e33178ba898f8295679c6a27c13063b15688720354919", - "0xe6092e5410ad28586c919c8b28cc213ad00d98f356863cf18182532386e4b615", - "0x22ecfe7c8a242323151b1232d30264cdaebbc3540f9ebd91b7ed5b79305293c9", - "0xb88a824d760f95bb0849c1039ec0d55b0a5685f414e53006b918742f4ae29266", - "0x02766f872ed3b29b795d6276beed7bc06f900de45d2333b07776f05d4aa958bc", - "0xa7c8fcfe9b14231731b904f255b56038b711ca83928c86e7224d8930f3aff9c3", - "0x7f992f4b04e56a4d7e63339e5647dba19a7686253f23b17300e238fb7dbaf020", - "0x66df9c89dd28454097f8d3c1c74db2f770b1dc2ac30b77fa6935efb12e16b1d7", - "0xa61ee0699ef1f5e541f1dc14117dbc210d9f36987ac5094394f5987f6bfcc1e6", - "0x2c45e5dc2953619d479810eedb7ca9ee5154ac9f78d639efc05c61e6c60ceb8a", - "0x762c10a410896623963ee11e20cc0ff22aa3a190d7c6ae92ff8071ed2d772624", - "0xab87f1244ba144a65d19dc78fc21d9a096e81bfeca1a4ca81c245d72d91137cf", - "0xa25d89d0591fcc9599acd434a65c3c56beab1602cec01160b1ea4a83bd7a980b", - "0x47ebd358245683f93f0ca6aba8a68dded9926ea691b08fa74757065af6d5d74a", - "0xa0ced4565fd4da621b5fa8793a527dfb87f2ac0577a7e4d7c1130ba2824b56d8", - "0x53a4c9bb0de061ff05bb41356f89d7776164ef279609c0579e0bb60d46f7a1da", - "0xdb16dd0810092b27e08ea9d58ffa0dc2ab9e0244f038ebb789aeb1910ccb1d50", - "0xc0f2f502541a18fc1237bd03b704cc40e0795005b18ef174ad05580588c83767", - "0x94252514f5049544c1a1f9424940d9ad00628fe879222195e7e943418719126a", - "0xd8b3bbd5dffa897a5f0899246cee2159befa2717c38a42a0019bbe3079d44312", - "0xc9218c6b8a4e717d044a4a25e0faeaae12a822bebf8047a22964dbe9ea7fa58b", - "0xe5e9a54f1f97724ccbec0ab1b660de4373134fdef6566723d25f80100e11cd39", - "0x68a65e4b75b51fc1db5549b95dc302498e76fd7ab360624d39ed847dc0ff870f", - "0x9224d78122627202226721b229cfaee68e61ba965ebbe244b5f31af820cdea06", - "0x24f4ab3878d24b7a290bf841b4e7172b7c8ffc05ca7d610b36424275640fa75b", - "0xafafd828658c92c1bf7f0fe6852d278e35661723a0fb8371afa6d7c79f886e8d", - "0xad3cca1c6bcb850b2c44064817f31575d48c56ac214f55a1134f907c495e2a32", - "0x98cc755ad41cd8c7984b103a920fefa77a208b5739d3a14c58d4829f925d975a", - "0xc99b328941a669e0095023db31ed38a45757a0a3eb4f7ef32cec80f0cd354c95", - "0x7c616e1f233cc33555d6f5f43884e385c3af408a586e32ebd52253f1f376ce38", - "0xded9c6184a8f2e426555d10ba5157612056ee4caad0d78b9575d5f3cc5d2a04a", - "0xcb1a955d0dff31d0b2164932d7f3e41e0bc033d353fb80bac5b931ac2b22a5b3", - "0xacb110c37539a77c5bedf5f116274839182d44f5ba45b52a08a0d87575d3a228", - "0xfa9151e46b2fae374129f5592e2350f40ebe194dbe14403cc0dfe1153cefd964", - "0x43982605d335a36b1a46e6dc072240ab39050219e4f241666d4c88a96f480a7d", - "0x2364261ec4ff16a55ddc406d50c1343e439ef9d4ac29d52c0582c95b796013e9", - "0x173bfe3a62c88d2df9793dc247b09d379027224c4287fbe6bc2c7c8d624fd86b", - "0x9573d72cf4be5158a7039ddf51074bbdf912cf044130cebc6369e20c0fdb9594", - "0x1c74b9c52d285c62684fd3c7e58c402be0ba4b6b0a7e50a67401b3020f5bc57e", - "0x21b4c16200f7312b91c3afdf72c99f57a1a28754c0b3fa9420ccd79230eb0175", - "0x28987ce9d2d35bf51a5b1e9fdfe5ac9676a3b7ea0175c89ec84edfa66fcf1ad4", - "0x8e0c39870ca2944a19a399648f16776066726c54a86391579f6ab72b7ec0d47e", - "0xb9de199f2be5ea32384a74c56b06393c51685aec5e52eb8360e8e2b5a32c892d", - "0x9b8156716b03f820ef52f12a68229e716f2c2c1017e08c3b7e77be97c6d0d4dc", - "0x68c5259cee376134bf35ce33ce5729e059fc1052e7c134d1bfeb19c9b9acd0c7", - "0x7a3876b9ac278cfbcdd4830880ef438a430cc830d78f6cc005eb4781380522a7", - "0xa487271258604ee7dec9a306177d5216536f9d62ba20dfd057d60f046b8e4f74", - "0x7f5bd998d4ea80cc0830bbbc0f1b015a198e1d48e58fb03427e45d7e1d805e6f", - "0x680280c7ea2419d4c9e909aba9c469e8ed7829e69a8101b1205df69edf6493ca", - "0x403ecfd702230060d50d1473a243d91738e7df75c4f76c9fc8359a1eb1e866e2", - "0x59fed28f3f4783b297b66a7254eb2a089f082534ca12e8e39243b527cec98c6b", - "0xe05f2b55e5a9180fda374df06b3ad23063950b5e91a3e8745802ce0dc3ffe1e6", - "0x9b266d0df4cb0bd394ca42108e6167d4bc2bd56717ffaa467126cbedc2edf17b", - "0x49ad1b4b721dc959b911767980557a70ba8c616ea5cef04c2aa82dd4b7b4fab6", - "0x584440e213b2551c6ad58ab9e36e707f892888fcc14b15ff789d1084ed0f9a29", - "0x679fcc5e202da23085560424a550b0027f3954a98c47ad80ad2baba5f0e8f3f4", - "0xbcee5d17f10c56fc1bb2cf1b011f76232b6b90d09517213868822e573c8f90c1", - "0x783e66e42aad8192058086573b8babc38951813ab42e97c52738e8ca6fbe8798", - "0x56108248a8d19a507bcfb1d34dcbe52dea56bcfe39bca5fa100501beb6539fee", - "0xd487860b420b5cd5f6e30783cb122dafd8f95f8cb490fb41d7413ba242b50927", - "0x6cb932740f9ca018fa81068501b073572b59094e08c2ba40dd1fac3b9736a0f2", - "0x9213b4cd1d643ad6e6dd78ecc50901c3504f3228bc38480d4e2d80a835d7da84", - "0xb9827c89e2c4ded0193f841d1d93ba9a50c125b165b6cc578ebbb59e1c1e3c2f", - "0x4a60fbbd3d4cdd3a5afaacabb91d9907b54f24cc2c0b472a102ebbc04c6e1d5e", - "0xc34e850a8ba61ccdddc521b6c78889e8f44cf842ad051312842b727478b0dd48", - "0x428440624d658328e7f71e2b244f6ec1d76f00bcfd38ee1a6505041909dd9a3c", - "0x86187fe09c6da9685ddf29a8c707a8b8c07dee71af385570a9f7688dade56860", - "0xeaf224bd9a1646e0936a58ec76bea3f3c15b0905ddc1e42a65d916790696f804", - "0xa0323441bb548307f09abf7c633c0939af2ba068d330177702017865126fe665", - "0xbaae7f994479b34cc6c3a187ce69f769314e4b16cc75d8db676cd471539cb8b9", - "0xd6a97586d7d492d4eb7d4207438d3b260927913040e665b04dc16fac1790b7aa", - "0x93958dcf84bae3ac5abbcf8d663b512bd4698e52729622ecb110387004b3278f", - "0x726ebcb9f4402bcc67749b9bf0e898d6884a34bb88dc42bcaba0928b99de1ce4", - "0x7023076f7ec758a3e7cd7084938e54aa4dad0733c6f7ce5bd3f9316671afb41d", - "0x7cb1c651811bb0af1a5a20072295422f10108dff1792df72fa7300b228d24a30", - "0xc67142637f2d4e57e87562d3eb21d1c9f00743393b8741e2a48cb9c1e7c92501", - "0x3d1ece56ece2a5a8f9ad8bc6489fb9e7d4cda7aca8e9cf787a55e44d40c0eb08", - "0x222a3adca2d078165e2a23f7f5b9ae308ef2d1ec5df16e54c1d523b6f12c9c49", - "0x8e3eddb190424b16d955ef2a352fa69d0c3c91406903e20fc93baa163c448494", - "0x08d24146ee54b8685ac2bfbb67b2fc2d7da0c8cc1b31343cf83e36a076d588ea", - "0x029b75327ea0a6607c83b834ae5afb64fe7870d0e65f0411f4c0011d1bd304bf", - "0x51df1dace22285f5e23451d54f792df6b7dc901b15140e0227b883fde1bae7bc", - "0xc3cab27d73a2321c525e584a726f1c2ad950dcbab18be2c1f7a4e299c8c3a2cf", - "0x432581bd4637fec04aac93de42524f33f2cba4621f78a8405a2b41a4dd18a2f3", - "0xd0557870826a74c353e934ab21e08e35081ff7879f8b323e2c9ebeb23e3e3bf4", - "0x31fbf4768d13bd4ba011c0adfa7ec2182485444299797cf7d51bac3c2445c6a7", - "0x2c17643506d7fd5eff17b207aaf55e51b9cc67c2784db80b9f135a52625c3323", - "0x1f581185e5f13fb76ba2f381f37c1498400ec61578e5d562519a1fb7247b66c7", - "0xaa843b8dc6968a5fb88de5979d4883dd05bf23ddbf076d148547941ac5cd4e26", - "0x650486edb00e65b08acfc08e7d4ad89a9d1237463512af6aff53c7daf16a1725", - "0x234ac9fce1857f817601764d452ac136ae3f89d9f3fd5ef4253120faea881de4", - "0xf568041a9ee5d4ffcc8187e9d6149f282a4d329f1516d408ab6106aaadae74bd", - "0x3ff2bdb830235072ea6545663a12174a17a2f5efa070568d63a734cd70c3361d", - "0xd3678c705a33a9932e625164726fa3dbcb3fad8835fde4675223acaca58b6c58", - "0xe865e21f5e13042da52a933d94e447b90cd93c7b2acb0d9abe223ea5aa268214", - "0x14fff852295e2a1c907aa54b3472c88293df8b51ab2192292709bc92585d5b06", - "0x8a48c21de13833a70b1f91128ddc9819ddcab4cbacef213b0ee63a72d0c87f47", - "0xcb0925187bb428b7742600ccb0692612b49ac9c6ac289055cc668be17d0aad07", - "0xcd9555b8791abcaa3b670328e82ce9c9cc265241d49cd1c77c167b4abe48f3ee", - "0xd2c33c47d9890500febad6a4e2b78a9740917d47a228b3d10948483274cd4b5b", - "0x7861de7794ab5d325fe5b78ed99413dc5976d541555a0304f39be7d1302d99b1", - "0x3a3e2432c3bd72c53d8ed99ab60b5e1a53218fc08ea62f315d05da7766da5b15", - "0x6905579f8ac0193e528b0f86cbd679ff4155e32b8a6fb4a1a0cdbda543dfb81d", - "0x5f0e4686fd7c5ac11413571e716b03dde138c444606d44244ce4d3bab2916107", - "0xb1627edd75717823e0824e3e3689dec2dae9692c91a611d5afdc08779d65cdd0", - "0xed51b24152f2595ae3200f4aeabe1a72c2c996f7574db1e072fed52ec531775a", - "0x4202fe9aa1fbcd3cacf8c1ca8c616a531a1c13b63578925c3fadf66cd02fb62a", - "0x3875a92a2a1fdcdc0fea135aeb6bcce59cce6a493037dbd9ca3c19f6d96cbb6e", - "0xc865ea4be94423d94a6b84cc9f31cd5eca878e461d433ae3275512e442c910bf", - "0xf28c5b3dcc1f343561953f64ec545c89ced8b509c396d1812ca19e2041a17055", - "0x7cbbdd412cfb0b140a1e121cec554a418818bc17f80c1df69a2b43228c37f82c", - "0x1ad7f4a3431f8d2e5670ca885797b7eedc9d5ec04577e79a81406c2e503d0431", - "0x76f07c15d91f17028356f09f4407c020b5b9548f3c8e5cd2de0b37989091f298", - "0x75382bb178f190aae8dc02d266e9fc07b2e4faba551e619c0434682439e0269d", - "0x2b564f0a6e8eb4f829cd70ad7d97f379bec1d2326508e155b8d8252b9c2a305b", - "0x8418bf6714467d26e8341aaea2ca1442cf34d86fbbcd7c3e63da0e895d8b29a7", - "0x5a17e1b9ad15058155679d701e90f92989b75f12f02c7568dc31cfd8659d25a1", - "0xb21f76fb217213c6457654149dab85b87305fe3bf83b1805791c8dc2376e1268", - "0xf3ea43ffcdd0302c11fcae3551982e90af2f748b9ccec50b04e8435327cfbe99", - "0x4fef91b730cbedaed0049e0dc36bf14360256dd2449e3e925178cc46d3156697", - "0xec7074e2607bd5e0f228af7b73c7e39aad72d17019c2afffc3ab647d78939f61", - "0x53f34be1711c68f9a765da967c60fa39b72df4778e8ca7967404492f7a353910", - "0x6f0492df8bae724540b7a1462371edd74c4863c2c8cb6915a86545a36f06f77a", - "0x35898b077c19c49dbdd9415a6c3365636e9a8e98e0760d8b537ca12d5329c9b3", - "0x1a1c020207e42587d674c1275673584f0e239e8c1f2b4f6d8c98c97338bc182a", - "0xc46a3a7d6be035fa17a55b963030326d25c01542f4ec5cec431c012e5ac6d52d", - "0x0b29f6f0e29310a49fcbf18c8783d1dcee2997a62cc19e9c20ab524e6bcccd89", - "0x4b6a5d044ea31f51124c3b8f4c4a58eb9e7c922444d7ceafc76e9767ec2927a6", - "0x003e2e364412086eeee06b7eab2489e258aee32adf9c8700b3e33cbe8c594d7c", - "0xb1713dbc4a35cb1b3f4484eadc891d745dd5584f97012ccd5164f5662bf12c57", - "0xaed111e5b0ba48c69b8b765c11d74e5554fb0655d29ee73f8d38a52071bdac1c", - "0xe7d15702135fc0b830c65f60f74ef407291a634610b39d1dd6538b18f238654b", - "0x30d7bd4e83df5535c96391a6e4bd46d4081437db2911f8cba7ec40d3d626da37", - "0xb659fe1e992a428c553596567583c5aa42a297eaa3fc5a73b2705b03eee588e2", - "0x7174740915226124623dda7ccbe6a559246b8162cf5cc942ed4d1adf7ba86c06", - "0xf2847d813669654e775dff15b5484fb78f940a61f170021f0e74e5b0c4ddabb2", - "0x1acf93e0e1d6af4c48ee251766ef1bb2492fc25f94471265b940fba3ad55819b", - "0x3a8946be87b92d13f5668a3d48b8258c6c8d585c008d5a42ff460ef14a3ac6d9", - "0x104838e64202e4a90e8e8659f0508012e6aac3d75ad48be54fce69c1b536cc65", - "0xd80fa0b84016cfc95a15f4b01d50ecf4b06c7affff144ba226b24674b4a7b96b", - "0xe67240364fceb1d0b730e15e03aa92e1d88aac5c064da3a9225533ec3f7a3cee", - "0x74924dd757f836abaf12789a0f241dba89a42f310282f3e126fd88c96184f299", - "0x2998899e920039c7d933217e35af122b0e22d8fb8de987d5a607d31cd29d2f03", - "0x7e103626c39bf10a4b8eef446d6266994c5f841748307031f70aa23a27565a45", - "0x16446aff8ffb1ddb6894a7f62fa813d7c92c8ad77a797f388c9102055bd34524", - "0x8bae81870b92e523bdce4153102a4c46a660fa36e40ff0228043efc956adeeaa", - "0x0d5e765b9a18172b9a374782f22c3f7b1d5c8191c5b85f99539fe4728a8221eb", - "0xe256bd6c4c35dd174b2119941bcf44af1919616987b3fd3f2d2c67a56c0cb727", - "0x515b83d93a92b76b4d341b75c5b087509045451cd4c97c4d8f753f606df4048c", - "0xed27094e0e5a132112ea97fb3e870080b8a79c0b24e874c7f93dcad5b5a5928f", - "0x549b8ad126d1256023759a2e2258b35faec200d467baca8c37fb616c845977f3", - "0xea971d1ebe07c22175addaa244ecc91b2412bada3e72b5c437300f34c3ba07bf", - "0x3a892e84b04f593005861047dc80db8291fa6bcd064079b377034c9f68ddde20", - "0x7bbd6284646d4668a09ed02df281cc9e0d8f668ed1170df627e5469fb1892037", - "0xce47a83c1c1239e1457b46356719f525efb37a5a60a2e5acc135afb784acf00e", - "0x7d023404e05a3bbdd246b45d6af0af6cecf6366798ba49f220d5335d3baaedf3", - "0x5e4f0a84c2ea318e7711dbaed10bb0941799d4f7f5cc1042af3635c4c43369bb", - "0xf20c0ef0af68d2cf69e09e241b0211c32264875aadf5483677e53e221ca90573", - "0x77ba703780e366b14f7ea143c2319911c97397fb2a35f4cbff1486f276c483b4", - "0x1dd840bfb6128f00c5239c3164375ea008ea7bf5dc953c144f3971c72fa13050", - "0x0ca150e42d75dafe6437aa92ce4bc59557a7baa8eee01697b78b2bf889ddee4b", - "0x470142189b056c2962e3169f119ac26b43579a91aa27c61354ec283f88c4d205", - "0x61d1b32df70db397fda56f0035968f9d3d2bfd2fe1c3e7ee3dd65ead896be04d", - "0x4cbb9a330c46cbe61c2242813d01e4b9d96c9024c82a817d4b4c7118f30dac5f", - "0x8252357b329a1e68a32c75310704b51974b750062567aa88adc165c185a0633d", - "0x9d3aaeea1952351c07167400958033ec5136e884ebacc52b829b51459746326a", - "0x7a3abb411568a0b5b40909bbf547d5c39c53e839ccd68a86fcdf015ca52ecd0e", - "0xb51056fd9332adc740bcb96d7132c749abf02f1e281d676c451904f3efa38320", - "0x89d4bdebd8b6619a5d6609cd37c4ee4fc544df7a86c75d5f026a065a5053d7ad", - "0x7bfd3be6e1d68dad69c38855b80b289dece0a79ee4b9c6d0dd36cd4eb20fc401", - "0x0ddd4151934060f65f7fbcb7fdb2a617aee279367d4085e10674b16d8a5e4426", - "0x65ee7d3d96e335b086c4f6f5b8e5a93b4d0054f3f264c666acd39bdb86781ff2", - "0x79eda3ff5ecb743404c332e9c8710fe2c22e8233568e27b98e5e13021c0446a0", - "0x117ebf4d1e01bae39b6f95b66d95e057160d2177d24d26d628e5642b83250efd", - "0x29c35bf3a4cd0fcfc685dd5ef82bb23a539bdfa06907e10966829ef2c56d8854", - "0xb30ec9f98ec0cec48714d80d6322ac6badb4d0293c3beea4540da8f1115e0e9f", - "0x9d0ec950a87bb27eccea63196e9c4be8b91ee8cb8f14d58309c317124c8875cf", - "0xc3b5819646075f997c2fd4f79b7e7805440c1ae294dded722ddb22ee7fd7a099", - "0x098cd76c6dabdd013f588a48c75336631af0d14ed3b7748d12c85a7e0e93a313", - "0xe01ccb6a5f21f3c2a1c3761e7700ee64b7ed6192aac8b3f0e160f7b9d3bd0ac1", - "0x6ab889153a958cb336577c13db174174813360284c8f4609409ef57b7240565d", - "0xdf67e4ef6e11e4f52c052820d005d857c0eca882878b0a71bee7f22bdc629d53", - "0xc60703dda6612fbbcd3ba479f28c169b2049d3ab1f597c558149bc062d19313c", - "0x6e749f8217a0a51a926c88060c8d349ccd85d387c802c40b20cb87f51586df1d", - "0xf29fbc40017a9bbfea1c28a74c3063af72a510f9c99c0e981c3fc9d83ddc5bcd", - "0x76cc861f436ee8a3f1c07b644842ed5457563812f60756edaa4927a047ecfc43", - "0x84e7eadae3bfb61d67e53e00aa45449f04bcb50e215835687d63665d6bb7df63", - "0x8deb4b71040ffa0c5cde5675af9b8f9f4982a88bd01981258e871073e19c759b", - "0x3a7ac758caa8e2def1d290c066fad3d7daf68ebdb7f1d5b5230b3c94262315e1", - "0x9319124b08501ed1b53e06290face78c0f7b201fc8f812f59b469f6fd30581aa", - "0xa2855c6f26e090daf60fd17c4bcb771fd5611b9e1413136a3fd21eea2be7a3b2", - "0x82ed06497f1bef352cf027589d608b622569559d29f3cdf652f68cbbfb684c19", - "0x74321929990b5436cdd6e0953bee4fd5bc02f84e19c9a3bfb40addac61fda88d", - "0x47d4b298d5335cdfa6d09c16cf23cb98bfa4672a0fbafdd784590cb705f14daa", - "0x3d7863b734e1ce7d3a7bb0c955704106649890b4d673e744b8aaf8ffed36be6f", - "0x14abd96e4b25c3583df37755c51e384087f639fd5ebdec4fdd50d49017cb90db", - "0xbad65145878c7c313373306d6321256f8bbbd06cf58126f79c356e7ceb6d0301", - "0xc1f71b60be43810973082ae2a0c690e738992a3613b7c8286eea6b211b1b459e", - "0x1d18d20cc0279a7ef9921859fd3d0fdcacd66ee295f0e5bf6812346fe54a014f", - "0xbe49d176e2855dce010fe941b6098d6ef74e271ea846644072b6e508bc239044", - "0x828eb859148f295e5a0b2ba813b43caff82afbf32946e0bb211a6034b93ebfe5", - "0x7402137aa16d58ebad277698c5412c5b756796150594de6ca0f392b04b7848f8", - "0x58c83c4de55a8a562642bbd571d4c23a8847f8bd14e99401680337e474c05fb7", - "0x213aeb2dd7b400827c08dd326699edb7c5d1dc2d10f2f7cd43f4f3363b3a053a", - "0x7c4b1c80296fc55182b032ce8786473d56688777b4603313f990685e6512bb02", - "0x5857ef59c14c702bca8c358b55a2a67291064849e320363f484fe6552d8a4ed7", - "0xc4f927602edf49bd2f50c993bc27ebdae82192ab786a734679b6208b9d0c480f", - "0xf5756d9aa1f0f203011b0fdc18279ec476cd24e9fe3393e870326a4922c8b6f6", - "0xd3d892f6f4ce7862ea7b47995f8e80264f460792682632f8bd2cb01c67570181", - "0x347baa7431a293543af9c144f83b2b8d8103118c81420d61eadea1820903e9e2", - "0x01ab0e1aca21b0875155eaa8eb561ee417127a3afcfdb93dd6a0971e410ebd35", - "0x7c72ced65914db2173b71ce0e3f3ddd08f1dac47bcaae0f9083b164058d5c056", - "0x77a88cd8f15083f876b0f6f928fc4effcd877c837fc2fdb58be16f8172cc7784", - "0xb40653715c8da901293838858c2173213ee3a49d9e85619686f2e2c2ab839c6b", - "0x5d3f9e2521cdc44f2effd25d56778790464ea1ad23d141e93a30bfda7157c9ab", - "0x3e0beddf45d506c7c6c25f5478074d54d7f28f627a8df2eda5db1543dd1e834a", - "0xee28b0e24ca590a5a1655ec6415c1f57dda0183d6b542501d1e964dc61ea3d6b", - "0x3932f898858fb695d39a02c641eed26246be0754b7cedaf91518ee8a2b051fca", - "0xd9f7788a59b6d6e43748e1f1abf8060bd23555fcc5c9b4987977de0641f28fc6", - "0xd5297bfa280857285d58a24f591fa5a7c4b575fe7318cb531a445d24136ebac4", - "0xcae4abea8b7698ae9e55d133a676b5cd611698492fdf0f7d24ae3decd7d1a389", - "0x0aa0ff5bd3e0056d6663bd54ca7663aa2bbea781bfea228a4467f1d0192a7a94", - "0xfc872737974fef46448fab89d5811a61d8758aeec65b20c03a006a98e3754edc", - "0xbe60d4cb85ddd60c07bb1584456852bc36b72cedcd63d5c9b302a09b14cf1bdf", - "0x755aa31ceb9213270dd40124315d413cb053474ed4a60253a5d6dbac512c7744", - "0xb970b237d42706d556c9571b87466457d6e2de4d63fb405c9c7fe3420f35839e", - "0x977e976fa982e2a5ead22edd3dbaedad3e6ae885f00c0f48b4555c63802ee930", - "0xb4db2e220ff78e7a91cfd2cc31140f27d45b2043cd5147e9ddb9fee77c57a5e2", - "0x063e555581897db6caadd7a1c3a1a2a87719f729b0f43b9da7cc86460237cbad", - "0x4405a8f586646aa49d47073bac9c864c7346119a977cc5a154aa6ad70a50f894", - "0x87e09cc5759cc1b358d20955f97ff0e403c31a06d1f6cc6dc96f51a80570db40", - "0x432c4d29b6908bfdcbf466e3ea1cc93b64a6160748d4940ae6f95b45a804916d", - "0x64d99506de841b4b8e741f021be26327fe9d7ed2e4f0f635dd4043b7b174fd26", - "0xfd40a7dcd533928a2ee74ff589ae38231b84e72d7528e90a8c904d8762e07c4c", - "0x907626552f68f88c36192023bada749a6666f9a0b295f58c2e77515baa1e6a2b", - "0xee4c1ea1d8bca82456345d114085f34485bc44ef62aa60a4a74d7c2a0a681e90", - "0x81c59a12b3c1ff323be43f0d9d1fcaba87edbb344493ee1c426cb814b7e11509", - "0x11ac288f0d0dd1bbcfb335081a0ba30a74b6690b0dfb6290312b99450f257448", - "0x82194511f886f7f93b8b243401c34f80afdbfb5a78774b71a4334de6128c7a94", - "0x29f7f0319e09462e906ee0175dd3c10b09600a896cc909e2c0669a90642059ca", - "0x5f747311bd625822725d2d665a3ae4e3d39d0476bab0b4ab4d314b034bb4b581", - "0x82d29ecdbb2b66248fabe328ce0f441c904b0935f0c572fd860b22cef7037e29", - "0xb1ae4eac81c4df41cd26df2dc365a09696eaffb0bea906d3a24921666298f3db", - "0xab305997885a76e7adebc357c78149ab13191eac73807d6bbcf189ec24447eca", - "0xb93e7ee92ceda5b17d5923e967817c84239ae3dfc4f932f28f4ff12e3bba4b25", - "0xdcbea5c2455d6bd4c01ac3da32108436a11d8095af5d02382c12e9adf64ea941", - "0x379af62a4cbc14781860bced0eba058b58167966d5ae31a9545e80eeb8d63207", - "0x61e403db38f76e0d93baeab28958999670da2a94c489502bd2e3b2dc14049e64", - "0xaf921bc1dc0550891d487a700625147493f3a2521545f0c8e2b479578bf5469c", - "0xf18e89c738315e1e071a7595b3e78f142d5e97e6b55b8bacd2de3cd66f5d44c9", - "0xafdad2f30f695ef2e7a959b3c2189e27bf99e161e62bdfb2aa2c6a2cb38f4a33", - "0xbf17fe640225f213c991e813d23e532f5b28425d5e0505442522e7752c916d40", - "0x75a408cbad5625e898f00272476660c2638392103edecf964e17af303d35c67b", - "0x5069c2e3c907e243d4128b4d47e9738273f5f8c2b230b2172ab53787179a66a1", - "0xfb05bd9d9025de9c9b9c1d618f7e3b965b78ef6c05933ea01600e0dde4c920de", - "0x0348dd9d08fc716d85e68ce68508f7565daaae39ff9cfd1242bdb7be4ca9eadb", - "0x356e5fbcfda0909e6881beaf83310addf858aa5c26987eaf6226bec120035557", - "0x2c86f13922af8b1d58416b1fe079176167ee91df9a494396f13c1961b71b1826", - "0x58b5511b329949547f268318f4105580adfff4f25769e50e418bc9a418ea4494", - "0x3115256a0d6e2991da187eb0af2060a4bdb8afc5af246d9399b7cd5861520dcc", - "0x9512ff6fe3a05d2d322286a1ba4eee64c2bf00d7b868b98000cafedfb0f7c963", - "0xe0a81678dd4ce59096b25a2d1d0e56c3610de0b2044655813192f97b36e71f42", - "0x9a796aaf1293ff33270ca51338eb3eb4a180b37a9a6e8e960c310de6cf885f11", - "0x4fcdd484426e7c1311a334b8250f700579b4a62604a4ebd158a1263ab47e1253", - "0x52ffcff66546b02f93ecf999bf048f0e8bd80616e0560984b65c7f946dbd33db", - "0x10b276984aad9404c37747daf9afb88478894e44a38f33f645f9da1f165192d5", - "0xee534544d636177fa1974cdf8f038d807653d7f4841a3f1cdedd39e7fd58fb07", - "0x18ebf306aeab781847e0c3da286ca9fd3cfa5acb3b68cc8f0b96cc271bb5172e", - "0x994e4471831fefd58bb7ba9e03c36cc1449b03841f03dbe9fab64fb2084c5a54", - "0xca6c753d0d81b8b693d7ebb550e4d2718b495f22739930957fb17cbc15cd869c", - "0x351225ff4d086375ed5fac1c0ae4c6cf31752f8e65b8088447d006887f00a42a", - "0x4773707163e4797efec803c6b85afbf8875e789b3257df2b33358774d8971a0e", - "0x6424bf0fa6621a2fb9c166ee73750058fe733ebfd2fff232fac6bc73b55e6357", - "0xa5c1f44a899f63c1450f8ad09055fdf96ef84220fad238727ba0b04d4d6a3754", - "0x2b03c1dbf99334ea961cd71a89d66fe760dfe87817b4880b34b5a9e85c2dd917", - "0x6b13ade490bb62ec4276e334ae01628eaf41dcd7e773a1c13b25810535c4f4b2", - "0x7719de48b8d8c359f0159c87e6fd1dfce479bb886e4b002512c4a3cc2ea6bd05", - "0xed248d16dc7f1ec4f2c7c162149622109fbdf7dc68d1ccbeb90472813684e36e", - "0x2469f1bbaa3a55609ed6d005293b9d332feef0fedb660f3b09734687b48ed88b", - "0xe40e469aea7e30348e37b7c2459954ba02a4968fb713cea60c4bf9fcdd3bbc8d", - "0x66555b4b6a22f8252c71ab1f044f3ffd6474b88bb62fcc55216dfd94f457c916", - "0xf9c6bc9f84e7d0c31db16b7bcd54954427b4b43abb3af59ed122239c2b9f1d07", - "0x5d0471159044b364f1c26fe60154ee5cbbcd87a3c9d5b1fe86b5990f1c095dcb", - "0xda4d4cc6682bf9e162921107d9b6959a0fdbb779067ed659bb5140876527add3", - "0x7025c5eab4bbb5c5a0c8a7e6b845af8c9c3d02ef962e68bc0d0ab8862e2c3562", - "0x6c49e64964290c82a76c34a204bb445a0a00d68853900097325ba2c0c2ca5900", - "0x00e005674c684df70927a02e34cf1dc29bb341730abb46c89a764cc82f4a798a", - "0x3ce484b749d7f4c1b998f50bc10758871fcf426c8d70271c023b0fc45107554f", - "0x76900e52ff88ff356bdf3fcfd22613e064078fec47702a8760d85ddb592111d6", - "0xfc4979821c684ffe76894b18686e80fb3b67c8c317a22d3d25644421ace52acb", - "0xf226c6dfdd8a4d2a1ce8254b16d4ef99c48085fdade102aacb883d3e56e41a06", - "0xf858bca588f24f7efece3e03303ead92f66612723a0f68b6d1a871634974b8fb", - "0x672c9adb58bea50dd89e5d41196c93f4ee712c65681c918a971ac2afca43557b", - "0x1ebfba17221b1b101ab5119477cebb8fa6c2f72b9e282564dcbefdc8aca27688", - "0xec5ec768be531c250cb18850b92217ab32e6b8b9c080d4396f4e03518d328986", - "0xe12f9d598479d2462e429f4e3e249f676b383e51edfdf7ec6006dc23775e1372", - "0x93831b7cf4cc14676927d3aef0ed451dc1cea97945774d397b0588cc06be33c6", - "0xc5548136a5ad06f9a92ff95739223bce42a6bc19a92fbab0dcafcd9a4fae9511", - "0x7f6d793bf0692dd27327e1644cecd04d2dc0269c500ac192b504fa0b415fc23c", - "0xc389a64653eb7f37a0101cb3a2f989bbf03f0f6e07810883843199a33b5c8688", - "0xafd6cba91cc7041ea560ae01f5178828c8b934f349f1f87f6a22a6bd4b68b2d6", - "0xddaa7e0081eb38fcfa889e04af7703bac86baaa0f40aede815da9376657122ec", - "0x3b7c7dd378b00dfa18fc416fe471bfb07ce82c113591e4d1f2ba742409788302", - "0x22152aceee8ac16057189e13f89d118c5555d1061c6276634177071c4e85f4fd", - "0x82bbc14bc483184e2eea54bea9f105d69dee2ffd8d598a181e9894f0c0419e3b", - "0xd9b019cf461627e2142b04ef8cd394f14ef76c3abeb8de18b24e4ed9451bdd2c", - "0x93f6304908849980c14193a9a28b1976193263962bb85c18c45ec0e4c98c7dd3", - "0xd117cd674d8649fff979ed3e20bb63ad3a0bc7395d9428a8f7bd10171f5793b1", - "0x58f9a406c5c1cda4207f4f21def9118b4711ea39d8b93786f03458e5f32b144c", - "0x0120c549d680bf513e54793ad1794e34fd1748897e9b04d57ff8f1f3638dc926", - "0x5340b317835453c590fd71b022f4c984ecdc7e77f63ae694d50531a2cd8874e0", - "0x0a15038665e01f512ae7f9a5eb5d94600192470fe84b4fcc30e43d13a2c820b2", - "0x74815b0ae52ab519317e97cccc47e0b29a3e811451454adf7dbe2e5917497109", - "0x9221b6625678681475dec3689d5aaaa73e1098fda3786a72e04e04591a79c91e", - "0x7f7e9a8e73f9319020ea8ea7186c097910ad88979e1885a37989e1950e4a6c21", - "0x331f70cebd073b64bd396a96084de6da235b99eb18e9b3a608f7674b7c84dabb", - "0xbee7c0dcfaf95f85e6c58821f4aa09dc31285be7dd79160ab16cd68ac16ec96f", - "0x8b9791f9b84251d68b2125b61cd919dee74ede9d9b272a313cdcb3a9a01ec0bd", - "0x4194670a50bc7fb2ad1bd9fe60c3cdbaef8a7f9d5338ada0f4f9da97ecfc33e8", - "0x84a52e69f543539031ebb0aaaeaad905243fdeccfe87f58f38afaa0ff51c3641", - "0x12bbf63c2c6f31b2e57005cf99d7ef172df7ae01581e487f7e405a2eb8b2712b", - "0x6e1c0adfd156c3a64e0f342e0ef852d24780bc0c132a680c4d90bbd83278296a", - "0xf47713191d8afe922e2ec776edb9d9fe421821ecc2c51a4a5d1934e2566b660b", - "0x3d5ab526f48ea24e0fab729855e6fe4537ff1f4a37d423a797f261e936cb489a", - "0x999b153fa9a46778f806bbc1c110293f3f983b85195b55114c4cd605475c01cc", - "0xf4c934c4caff6611fa407b37c7fe60110c5b37ca6476f7da664110ce606ec887", - "0xa0e3ce8072926c2828384be41b8f2eaed77b0abf80b85bad1ac4ecfe50a29d52", - "0x1e0fafbd1b5044023a711acfbfd67f706827fa538f1016dffb14d78d742133d8", - "0x6544bbbe74274109833f0df8f5de938e5020f642f59dc15f44ef131e71364168", - "0x7070469ad96756a3b8eb3edf8af72c34426fd54b543a50bf90f7df4f9e6cf502", - "0x17d3adc89215b44e0ae066793fa92aac625a4625faac2f1af0dd7443ae88263c", - "0xac82f48569287d701ce51f0fb8d9ea9b58389bf1ccb14f51af0150ae5520d067", - "0xa345a1f7a2b62e083a9145bf72156dad96db87cadbb097eba49c1dbef37e7678", - "0x226cda69f3165f38c62fb320575370763c38ce1219612c8fb780d62e3724edc9", - "0x90066ef34898e0bed612ea988b9d7dba8d2752e5e4433eeee338ac46d0ca0e6a", - "0x13a52bc644a897f92b9c04f92153c3af7cbb7a8c8322eec5d2cd7acc955e5f14", - "0xab1151bb1fcfe04cfb138f6115007798156fe6ca34bd2d5450e7260369c46cf2", - "0x1ada842caa8d11d6ddb1f866920ce9d211d12681f0cfeb1fa953412ee72a6202", - "0x0775361415f30fc354811de5a09d152ddea119a45777e324bae06f605aac9a1e", - "0x3341e88e37257fb40d904b52a555e5ab4da77946feee8d0180bf0d02b84b2743", - "0x6de86152df7502a805cfe3ea13e6e92fed6591bb6807bf52ca6cc1ad12581960", - "0x2df8c525fcdb9e3338521ca003ecf34c0accaeb1ef8e8a8c53a4760e2a2f76f5", - "0xbfb79ba6c601d99fe9da4fa5d3156d43361e19d31cee9d8f6d86ccf8fca677ec", - "0x242c88cb05a573e013355f668be14ac02be3366f4529825e970a824b63ec955d", - "0x28cf8683b9301699cbd52ccd2e2382f0214b5375d45ddd1f512418955cbc75e0", - "0x962491665ead2c0a67a89c9cb10a95df482bde95bda60de9f9e26640262d8df7", - "0x89d44ee38abfe8a782b4c8ec4a15fb613da1fb54df1216b401e2b9feb5722640", - "0x22c65f9ad5d54461e990a01a32b3c0b816cb76fb6263916c7439d3a38c1f6e6a", - "0xb9e22056d59bac9dfaa70f3cf7e882ec54d12b5c651faae9db639d514358b19d", - "0x9e87bf3cf128facc840148f5992319455da79eb5b073366ab55935927325deaa", - "0xfda17f28aacb58b521e21980e1690292ec53ace1a331eb3f3e39ab47891b6818", - "0xfc89489cc181a41c11db271d24d745c602d021175bd8a3216466222f36beb894", - "0x70a368c3eec8a26721407652dafb467accd432a781c38c394811d4165da0536a", - "0xfdaacc8fc0d6a88b09f6e82a854eb5846b534cf709f74412a52ae1b73eb71eca", - "0x64dec3cbd894ef0acfbd4c756c57d1158b8735b9daabb8867a8fcb314c0d46d1", - "0x717f95c3cf0ea9fc9b2aa7da862e67dc5734dab7b901ba843fda932c2f4635ab", - "0x9832bf9af748a6a9f92fc7f808a10055c8134f6b9698b7a22c65889629eaa321", - "0x27556bf6b3b9f142af4b8b164dbc7320d838fc2035392f544e8acf891993908f", - "0x698c66e02954a0a3a9e20531427de5e111324dd63a3c17b3c9ff381591b209e2", - "0x15df8156e8722be5454f3a18da8f9c327626dfcadf8a43ca7053c2343b688eb2", - "0xc9012320a383a0daf25db92d3b2551542d09365e52ae17b0b8aa164804cacba8", - "0x3ec2163f659c467408d6885e2ebab399bcfed5e82eeaaa18ba9b11b6a7092c67", - "0x7c3c061c68f913425190d54c5b3085edb6f7b644938067a22d05b07252a2a577", - "0x65e7a85d7c9b44b46249204e20f5ced1725aaaa46c298e3103733116023af990", - "0xec90bbf55a1dc23fd2dce20458e9dc4155d40f35a6508476693f8f33e3c5c603", - "0xf5c3fb3cd7defd40896125d0b0651ea51faf248b1074630d8522267773f70794", - "0x098cd8401364b469a398ce063cd4c0dd245780b80535dda05d1cd318059a0e15", - "0x36a2403988d8284e774c519c6d5d60737de41f5e79a3e68b21c6e3cc31d99e3d", - "0x24c048089205c06055be2af692e5695e0b21c862beb1d2c76b73453be1aea237", - "0x4c6bff0317e1d45668e67bba2e4abd69fab7d49ebcf25b71596eb00b7da022fb", - "0x751d9aa08945bb12fcb312d8d9582cecbdad4ba10443a11c76f2336354db309f", - "0x4d15a64bdfcc838b9673813a073e6dca2e7cb8eef5c2ca9cdea367197d5d77c0", - "0x1857138e6b90b38417d9524923c4afee7815cb4b0a023b5edaa7e1b5d67e956a", - "0x9bb20548865f77267249f8934e8a7ef78630ca2b1a698c20a1831d57c40663d7", - "0x05f1132fc8f3e9b6e1fb8a798324249eb5aa7410e87b28549dcdbbd981995276", - "0xc62c8b41c48fc728ddda25dc5f7f112569b1ed7fdd6c9cb365012b6c92c063ba", - "0x36a336dce4c079c097c13378010261ae60d7614758bb1a644c1431aa535fcfdc", - "0x861762f37bac1e99454d486daa5586b7fc16ed3d5f8a3d4ceb76b2430b07176e", - "0xd52e37befd22d62f41bbfe77d3236bdeec8c1898b66fa74b8cdd6f35a9b02d76", - "0xceefff0fe45933ef72a62168b853824feb4af74ae82804fd635a3cbd554994ee", - "0x2743e9884c5b82e2c96f51859c4488fa62c174285a884d8d5fe37c134cb460e9", - "0xd14b5c588e28af7b69c0f62a2de16a19e10a89b4e0b77fa9eb2220de12431049", - "0x7a6720ca354c4749f0fd338049b84e2c018bda8180885e137b43aea736d013ae", - "0x33d57d2acd57e925d5e72e9a9d98bff821e9c0c3f3ce0862b5e68d1a81578509", - "0x0376d061cd94b3add12e0a224fc702bf0c661ed7540388fe2d1c0e45b33a9575", - "0x1b7f9abc9e995ad6d501085bce4ec8cfa283a6c818f172361839a03463c1c823", - "0x91f920bc30f075c30faf5ca2800a3d98ce11f1ef4dfa59d59c48b0e3c72d77cb", - "0x12bf80752456d779accd274428b53aeb15a1f1a47c0f85150ffeebc3ab1509fb", - "0xf75bc818dd47ea5e36a4007bfdcc855d48e057ffad411bda1874c4ea9290560e", - "0xb3373cdff2cee48761a5646faa2f470bddd2dd7f4e5b3879f5c57233581c11bc", - "0xcde2bb6a64542ae77dd1823ee1abd29e5bbdc9a8a987c4cc0c9e961ec01aa64c", - "0xac9986cffa116e9b974dd5e9eb8951b6ee7ff8feaefe33bbabd072c492098e52", - "0x175ce1d341322690675405440dbad5dc80d5c62db7883bd58887ae4e8991040c", - "0x2607c713b1a38c26ed1ba5574fcc1ec9e8e68a7c269c1cd687d4afb71aa6607a", - "0x1c83dcd846cb1ba5a56311a799bbf6033d3846b62bc022fa8c78a2f045a36b6e", - "0xf07919693e20b7f9b50c0745ded6f76e3b2ae6fd301548b297eae87c4f096d19", - "0xba1e5393889501428a866e4e1f1181eefe7ce96c023341b9a724f243c20efd98", - "0x5c8363770a2fcf8e6e2ba1f384769100490509fa776a609206177a4cc91baf49", - "0xdc1a4b1c138f9e7157f7fe476ca93579f77b436b512d7b1a92cd40137fed5a32", - "0xaf5630fa700a41ea1896d8745c484dc51599a45f4420b155f532acaf70159930", - "0xbc42efe8ef2ad47223310e252763f03081e72d3b0fce31cb8a92e535f918cf7c", - "0xb3c82090aa8e61d3cf66e68324b89bf0c8abb3a90a7689fd0e2af475eec12796", - "0x0b9292e757146f5c82e38b8c704ca9d17bed72c8f05abed2c72e4b749ca95cac", - "0x1f001856caed8f92c04803e3673c36c1e6d44c54fe5a2fa6dadc0f647ce5a5cb", - "0x4f5d9de1ce98d143c5e4230da545a92ce2adef539b28b3ea12b058dc50516396", - "0x92eab7aa903c1c9672cd846dfe59daed08bf7eaac3649acf82cc10d257c4ee83", - "0xfe53e9046375699c9d6d1e97f5394e4d9dc47247962cb12b1f19b14280c36e68", - "0xf2b4013bf20360c30a02719ac84a1fbf1d4f2f75a13708fb33698b404f89643e", - "0xa2c42fab1a55e7ad87d3d6ddd539cc015672bc7d9945a0b095d321ac5f2d269a", - "0xaa19e88d2f0331d9eb6982cf6adc3867765a30aacbad4636c6c14075debb3f14", - "0xdad3369b52216bd1d5670937e8d02011be203600f9e0a5971083478a751c158e", - "0x95606ec86211580a457dbd3ce345582cb63fa1e51bbb26e0fc7f66b346c5b165", - "0x3724ec7e644d1c9217a4530be31b164d132103e137d78ec65d7d2b331a58e176", - "0x3c9a30656517e28897e13397a1aea4320ce012365186c3780c284284188820d8", - "0xb1e5c6e1fd3acb6071ddfa21930749088701625cb28b632605f428034eb40599", - "0x16cab690f62af9c4532518d5d44adc79294509e7d468c8320ddc6a2af947e00b", - "0x77dbcc77ed87f3abfba8593ca42e5566509be4045df7b1360a147380f4923a18", - "0x50a81427038cbbfa9d80e7c8bf81275e87ed12329dccf818e97a2cf8fbf18bb6", - "0x535a32f20b009046f36d7ddd94803075813f018dec533ed87cf106589f1a9365", - "0xc09b4db18f62c39052fe388f8d94aa894cfce591bf868dfdb6d9598015c373ed", - "0xcfd40ebb21db79e7afde2605d7fa23eaab3db5ed9c0fdfa3310b80e839c3d687", - "0xfc99454fc2feecdc233ddfb15f8248d2e5c3c3b47c928a4bc47347cddf4abbe8", - "0x6591ce7a1118faba148011194404fca2a0d6731171ae29a287fc00aeb02adb66", - "0x109290c341c44b3e85d45d772853217e13388f406f95ae6f1127d574ea12ec53", - "0x623a639e40ebb08f05055465be038aadd7f8db549f1eea2ef084afaf199e8a66", - "0xbbf06223a713f88a1f571517792c0af039985c8f15e0fc803b7dd7c464595715", - "0xac7d49ae849acccf45e6318e4cfd18c03416b9bacbde64ded23514d59b716a97", - "0xd62497729af1b32f0e22fdb503a750c85a10299ee966e5c5ab1ac7e71e5524d0", - "0x29c2cf3cc0cdb9799e0593902ee453fba917f096f7eb5d04418ac292d9d40463", - "0xa5524f355a9fd8bfb4bfca14fcfe58d227a0b8d723943ae925bd88613f62927f", - "0x558f4140defa8a45ce60cd2297f60656454fbb1306d416229d07f27f18531f93", - "0xbb3506870630199af605f63e105c9acc9de29869b2c67661f3278d4d33b33d5a", - "0xc3cc4b6e3d14f45f0624652df9aa2aae5bdd6e9824a06df6d66572afc3ae7dae", - "0xe1070ad4c886c4e4e118541e6c140926d2b2463701b293527a91a165418ecab1", - "0x22fe26c7de8606e08d7cc1e0e37a7061c9fc50da3581c1441c0331ae32fa18d9", - "0x71800db5261f61c5525744ada55e570bcd2820eca92f30bb2e83e3f08e03edba", - "0x032839dd738ec7d5e43b84ac14adb11bd08660921c1f12932cb64ffd0179d343", - "0x85a762a2ab18668af249733829cdef9e3787bf2d2e6ab9938644918c0c1b51da", - "0x16d578dac1dd575ff991c2d25e728973c1ebc2b61a3032c8b85b62258667bc1d", - "0x3f1455a483663459733cf450c0f0e16678eb1f5485a6cfb6174630eac026ca6d", - "0x029933e53cad5e16256663fde9bda097abba5d7327e9c0fab07abbd013e14ea0", - "0x657b61028b161a1a3bfca40928375621133b0435893dd1d2562944ca85afdb05", - "0xf541e675935600a62e60bf5bd98093ffebc0169f23629db977dd8771958f3cb5", - "0x8c43b0d7c2293e089724fed36851744200d9b87b6600e3ca351f3f65852e5eaf", - "0x07c6c657a80c5718804544332c2f68b445e8d7fc58f01a650d7a476c036193be", - "0x0d38f9aa48346458b8ec0710b28869f4bf979abb80a4ea0464cdd9da659e0db6", - "0xe39b8a792df028169115125cc8466701df553de20d1ea385f3249a1d1c13703a", - "0x77180591ba49b6118cfc4ef772aa9a1cf2b3d1d67be1f15c308cd0727dfeb068", - "0x088d7d35f2b14bd5d0a1b6f3510ffb4f8ea2d054f3f6b99de8fcd7bc7dcb37cb", - "0xdd0bf242332c127c0e63e535ad3ecae82588ca70eb04973ed89e29a81a65a17d", - "0x218dfd445fd02c980736856e5af70942dcad8a0600fb905f0b4684f317ce4f19", - "0x1574b6fd0411b9d22f10a2d19b0a681d163c7b969e9a608499ca06414e579434", - "0xfb0919cf4bb52733e14989e7c4de84cfe6f06baf792f7936f3d3ea00efe5712f", - "0xceb92288daee1a5e2022206783d3d069b7bd1edc00d3fb6a5841ee1cab207e47", - "0xe2cacbb8bb3bf3877506499ebf8e8ab83306482f87c24ef6ba95dd40a68ebffc", - "0x373dd6cf736c6623843087cc276480576edcbfa873cc8e25dfa6f4ed396ca9f2", - "0xf7872e8c04a5766a8773d1af21d79e63004240b9f9caccbed57f58162e4175f8", - "0xcfe8a827fc4c36fa63990b95b90c68a684d4b792d20b8d786a4e1b1d13d3f935", - "0x7641f61df9b6f40c57815df7ea6ccadd757094a7420438ab310bce4642412ac8", - "0xddc8f2276bea73f13a3b127eb637265190c5655f4a8ab75cee2fe739277dffa9", - "0xd7858cc623ace8f1d0d22128da9729c9e9d3f0b56ef3ccd33464c1096d4f5811", - "0x4230798246b8939b83db483d395829d1d306708d11e783e507dc1e3b49490b3f", - "0x89209a8acd2568a49a7e623b5ef5f43664c41b9d7923d0dfa5c5ef6c7b8799b1", - "0x104b1b2826fa7d1b22770c1bdf6810b5e72d384ebd22cf9faf6ad3f348beb0c0", - "0x1a7c94386ce7d3028d3370aef277d852a7beb4d10127701cc5ce10a1b044018f", - "0xb98519653520206971618cf9b6ce2c1df8e9f471e8889c7b5d57d274281442d6", - "0xc890e325cda72421b2aff2785b0d917b02722e48d7d4b04b0848ae112b2676d3", - "0x571c5944b46c3f9a0cd2370864355d5a8a84688094cb216cf525541c36c11396", - "0x0ce2c597798c13d44ced6fc8d2a25f2e2570f2042a2563a5b9e44e162ca34852", - "0xa6fdfe84cb052109bf80190a9b031b8ef4af89fee72171477eb0cbe77a231350", - "0xb3710d99697ad3eb32af332e1e1466ff14a36eca12f49353134a3ebc89673174", - "0x46dc128448fefeb7356c6a2d4c7d2d067c04aa1973d6d5dc5d0001a08641ef4d", - "0x7e29853c2fb2d83356d63ed3984a068aa77570352cb488982f4852d52a82c2f0", - "0x796684dfac249c4ff804ad476035254b150ced67fd5090a870d684e7343bde65", - "0x3e8e5a22737865fc518ba785a18d3fba861c131411e2795920a1d8714ff76f90", - "0xd60787dd997a22b2faeb6e3257613591f73966e39b477f196566031d7d97f79c", - "0x143b777a1ab7f233c88ac126fecdd2b139a73d27c2374a14f59d6b2a3d274c07", - "0xa0423fa816199bacd23116e13196c2dfea68467f0bb7211a54edf40cf8266a23", - "0xaac1db2e966c0786bbb0c426a40327cb230231f105a27e99c8dc8d5a776f2aa4", - "0xa5925ea9ca18ec46e986133e5bce4381b4bdb34a2929da9266d63fed308330ad", - "0x9486ff72244d82f0521aa89bdd4e4ef287c7c8b9f11919fe5e169eb680162bb0", - "0xe5590a8ad9f00a25d6b1c7bb7972322d4c7aa0d168c2d64dc1c1a2cc3e1b2c5d", - "0xa4d19ff7d20981880824a2212a5ebcde16ed294a1b486648eefdb4e61985e628", - "0x42008316906fde068656372ce9cd086d98cf48ae6c8ebef4cf2c318f6196ee42", - "0xd997400eb0679ee898e3591f612f12fe62a8a512e9f29bb5d892828a717cba44", - "0x8cb77326590d26e47866b96c7b0d4a2cb098e9c3302973c355d4cc6b7bfb9a39", - "0x5aa47047d8dcfdc52cf5eb6850a29e18bdd5458479a62ce73e390da48019908e", - "0xb7db162e7bd3eca38c26b81c280060e0a80afdcb1b1227f2be53a104b7bde92d", - "0x6118fb183a607ad2ba7977b23d55a13a13c23fb4d42ffd025829684619cda56c", - "0x57d6217b90c06b85cb935a7e662a0bfff6bea628d67148fd77cfb6d4aae46b3c", - "0x8f6ad6d46e34c419d675f2e11fd9415dc0051034dd2dad492992cab39a4134e0", - "0xf02b43c671a09c3496057bc12d546331de2bc68cb18c64c4562a7b4ea09400fe", - "0xc0715798ea2098d50a16240d7764ec79ef0a888a46acfac383e72d2e50c6fd45", - "0x3f87cf2fb3973cef48f8727096633c517030c4fe3b80c8de5c46b7aa7688a7b1", - "0xb3eb15c04da4ed796f22c6f9a6d9b650e49092dd775870ffaf4538efaa9143a0", - "0x0858a7432451452d8f99395811d8b9605b6e493875cd8f6efd38ac933f843c21", - "0xe8d6f6cbe3fcbcf1a8b670b57045c9163e28e62c2301518667869fa6154778ac", - "0x134b94a104a8fe9c8fb5b74d01e8137b998f9691844fe8f6a22634d967c70c29", - "0x5ad0ec6ef0efeda0f2a0ad2a693eea176358a6298e0d16014c86e17f45a91418", - "0x1439519977d98a556eb1f32924f6206faf316ded77c7e6624e68e139f99d2ab9", - "0x50095fb3ffea338cf7304154c4603c41eb187b7f19d356f1f97a12aa4009557e", - "0x40d1b045015f62509696d3e0c71ac234ad594c46814d04d7cdc76f91d848de89", - "0x5f1fbf6945d348a53089d2c9d3feae12b7b2981b33ad5242eeba5d8aca474211", - "0x016e294adf2b7ed329e71d054920efc2399eaa9be90ad0edcce1a1ecfda74b2b", - "0x13dc29c28adef710976342863d04ff778fab90c34b36932cdd41d176621c517f", - "0x3cdaee5da98612994b8ab88c32601824112c15dc74f2438fd6dfcf26697c6041", - "0x0792f243e6cfd99f708f30d1dbacebf43a69e3fea671a53eab7580f7ad0cf896", - "0xfd37acc097563f9dbd330592b8b0d84e13460aaa9cb299efbf55b87911a42171", - "0xccc52c8e84bcf512e0641a383bd59565a91d7a2cc4fce191890e17318fd550d8", - "0x42c275730ae7b7931c354cf6825876f09bcb721ef2a49423c7062316c2d6f2fb", - "0xe7fa209c2e52f36d1142265890f2d4cf6a65ef6bcc9aac8b98679c4d9fbf50ca", - "0xbfc25b3401aa288d08193c95645a23509d7f91d186438a713ea7422053615112", - "0x59600d2e03b83317882349f9ba9a29e378c99f5d400b66a4a61dacc45a10a06c", - "0x5bb42c1dcab25fa7119605ee2b039418433659a9d44d2607423077735d06d646", - "0x1c88606c1fb90348b6639e0b5ac351aaa7be9d8e7a54949b04115be31c19b210", - "0x0c3688bb4e69ee1d2f6829d207ce327baf2ad913755ded680006516f29042f0d", - "0xe416bd0839a09b9e043cba3cd29151605c8713c0a87060e721236c42dba557cc", - "0x47e198d26bbeac4883a8faee1409f01cc5bb8f4a3e9103b3d79452a8d0d7256f", - "0xdb13ad2d508c4cfd32da5d0feeb2ccc02551f5f0764496849c2ef79d65f81e46", - "0x28b168561a00f5b4888fb3921702e178289dcb4ad8ff9e6a05f13c7b2063fca5", - "0x2d15236fc32bf37335549ca7b8aed0af93c417dd58ac8943893b37b29c9be194", - "0xfba066e3e867af94ffb35c83d90f91dbca703d2b1e1270d3134adcb269e1d3aa", - "0xb39dc5605015af1563f509d43ca979c0b2e91bfcf92ceee5da72ade5c757cd2c", - "0x3944c7239cb201f6541d75c7d316a48dd13d574e1ddf4176630e90c67c05a9b7", - "0x2abfa889595997cf93345c17b16afec7b1935c25f489a9908571b239dc76585e", - "0x73ee344a77f74f57a2a3fac1ce4bdc060910584adb0188cb7c80c2e31b772aa7", - "0xa627e9f5da335b70be159292845e084fc845263843751fe19f9f1e2bc90b919a", - "0x77461a4012c70824575618bcb3a0d876752b3628266d30ebf032ee0895d6a934", - "0x3c8fd955012a02303155bd221e5416ccaa350c6bf53a7a0fbd481cd48f67aa32", - "0x4d6c89db5620f12ddf98c91597b6fbcce717feb4774a4cfd0429af94ae4ae329", - "0x49e687e61dc125632cd4bcf99284545ee457755ac8908586304eff9bde1e8e09", - "0x330f578fa2bb1b92f568d94c0ddcb12aaeea8a618dc753ab8be143cd0356009e", - "0x30b04622465e3bc19178ba1fdfb3e0c9e552248614357c2372a4d64366b925c4", - "0xcf73d465019666510b2f02790d21d9fc5fd1fe7ca6e670bc00efc0b6407777c5", - "0xfd7819a5d0a680319c54034683f153c89ef8a5175132d82a20e602645cb5b54f", - "0xc9dd44245feac5089313f2940d1d1a27dad3350ddb33c0d32190b44382b99976", - "0x777ce6cc23757f54a5f25323a19731ab48fff867460631afa8119d77d32503ab", - "0x7b6734afac66e9bd705d4366fe75b25ea1033a61dae0075e8cb3c3d7f5383b8f", - "0x2a47d485b255bbe4327c97e996704c9a613ee3dc82c43eac266f36774ca2b866", - "0xb6d6659eef76a48ea53e89b2b44291f908aaafd6937942f0fbf4bf7a80249e17", - "0x8fa106b3bcc701e850b0ed9496a8b3331d1c7dcdc236f7c1750eab30a4eaf5a9", - "0x7f5c12db2b31a5739529dadc239b677a7bc44a9bd84a54174556fb7c91bc4e40", - "0x17f82052564250a89d1693e2c11c23f945051562d9e95d10d9ce07175e8e258d", - "0x48318d87fcd2681f1cb40c94137b43ee955602244da4638b1f182a7c6d75e964", - "0x0f85a07673865086c9637ce92bd1ecefaf9399830b12e65173a5243c792a71ba", - "0x200f34e1e23a98e581457ce0b9667dccc207ef85611258971b40b543a477b32d", - "0xb24111bb847669bea0ebd5b5cbc7c850d0e814577ab5291f7b6bc3a570ac8cdb", - "0xfdbbdf9c55f43933d30a61497dac581c1c999f1730b1fc884965099d4c2ffdae", - "0x163d60e452407f19083203274a9a4b6681bf0c314f48f7cc09d2e4fa4ae26ad3", - "0x7316ec58ab0a808fdab04265125af16582e75c4ecefb70f6d23eea42e072ba13", - "0xc3908756583f8b291a3ae74f4abb96bea69da503887a9b5db8dbfa84d361808a", - "0xbdc3969cabdca434e5a1ce565075d3dbc4ca918a385bc60f62ed7505f1755074", - "0xdf98f4eaca81432edfd51d138a6b85d77f9a219e7565f4987a5b8f34bba40b79", - "0x426a0dfbce9121ea7babb30cd7e8f74605dd781a844403be8def56b45792b7f6", - "0x617cbe91494aef61910ecbac7233faaa1d580e2f1131be446a2e8cc0854849c1", - "0xc68bc3472fe90e10606d2f3a40c117e9730140107b423e5225214998145d7c66", - "0x84c464268ab6581830fb5281310016317919ae759f8151de4e1454c65af27eb1", - "0xa0376b6e3c691729b059e3779f18d52d7c321d528f33f59b0fc37ff34dedb0f8", - "0xe2c6cc9f2c06cebfb32f4eab5e7589c7b47310e44aa577c32a6851c59b80a824", - "0x07a018ca5dbd9d3611f24829000e27f828273988a957f190df6d315706bde47f", - "0x7deb3005e91ce21304fdce15a13a7a001779b7f021a17af7e889c1982c35f2af", - "0x2cc679cb44882cb685ec9a8d9ae3058a61e646c26c6635bc12068820615f450a", - "0xa5351381cac3b4c897825ff5acd817fa2682aca353d6c5df5a0736f940772361", - "0xa8afb3dd19f6c9aaa2b94742ebf50abedf904dd8b513991607e5c5b59539b0af", - "0x9a95f3ff3fb05b834539d98cb8aaf9422a9112f5f232b6aebd3153d7064fad3d", - "0xe2b2dcd9f84e3d913ccdefc439c650036708ec4e8e6d71c57d5a36e29007f00a", - "0xc1434b636f30b48f7a1db83cd094fb46455044a6ca8ed9996cbe1b0586353e56", - "0x32f4583854febc4ce209f90fbec4af4ed5da4cbd06adcfce0683853d7645b43b", - "0x70f338d6a745fccb2700f6632709f8b94120810d4bda076d38ab84dc5a4b60b1", - "0x1ca817244ef70d749235354dbe73bc02adbe1a9bd679531c9529b7f03eb08ce7", - "0x07a568e8a9b53dcf9fd6e7326d4b1d5f243f0dbc49f3332cd1331db5b9f4d49d", - "0xa0aa8b4799e91b0c2585e073d43988edfc581decf56c64b9083f9692956d1f9c", - "0x0e7f230a15895a83f437782ba8695ae81699c84429d30d2dcd22d842d6a36143", - "0xaad29638bcd49431591ee0dbf117e035c35cf0038267e4f2b3a484e4791f4dee", - "0xbf152e8cda2dd12ca52aa7cff7ddebc4e54a2c78bcd2bf6a158f45257333a09e", - "0x7f7fec07a6d7b2200c3e9b2dc66c5c49d2f2badd6604cc5aabc63a3ec5038375", - "0x8ea37350a5d0c81e11b76eb29d269025d28b26bd29a196330465997b3d82e1e3", - "0xba45a6e6042f86544892daa1a7731b9840a9171d3d834926d4d5bdac32889873", - "0x3269e10a8871a97b07dbdcd20eed5cfdc8c5e064d576dfbb508ee4082582913b", - "0xe7fb5297849c3d3eb0b3bd068cd2e9dfac549dc43a44c65e3b2005ba391d8a88", - "0xe42cf98685e159d10a3e4fbc19b6df03d8ad42d0f3d398f826a806a48a87497c", - "0xa448b7bf0faace82dc4255364fd8fd029c5c5d828888cb131e649472c6150775", - "0x1043325717080c977ddb22086d496de9b3308ccc7452665851839863cece60db", - "0xa1e34e462ecf988dfacc1aa371f1b188120ce3c1e06dfa06f639799889bbf5ac", - "0xd4f33c1c390ad6244e6c3b6699579f2f351b1a0b583550fe2f4b189e211c9c30", - "0x4cce900d2ab1dc78e568fb63e02ecd06fef0cd68d7cfe20cf9fa657b62a94dc8", - "0xbf85a695d507b9fc42ef3f81b76483f70f9e8767534a0ff35eb2f3315798d72a", - "0xd96ff1488e2deab994f27f204037bddc099766f1955c916003a4c632b48aa500", - "0xc963ce4671607cec2c36d8050318cafce4f835f20b5a859fdca14844a7e3a456", - "0x2e5838f879f8f698b3a7efeaafeb7ba324940aeb3f8b5bd16aa5ccf8737f4e63", - "0x0fccc49208367d119399101e5e0b1ee6ce5ef0f61f4a9f0389dae06de2038efa", - "0x63920bc887a208585f3ff310990c24c8e594942e371e7c2b59ed53f4d33bd1cc", - "0xd675a402fca8c1f1a9d977365c5269b93d963eb832ca1adc45b1b0251897093a", - "0xbe37d401abe3adad6d87befe126ea1883c4d117ac85b6c27965a1bc320f1bd30", - "0xe7182036132dd24e782cdd5fc41bd0a31b36a3eef1327ac211df3aa5fdc9df2e" - ] } } diff --git a/ethcore/res/ethereum/musicoin.json b/ethcore/res/ethereum/musicoin.json index 109432e58fd..cc60dc1bd5b 100644 --- a/ethcore/res/ethereum/musicoin.json +++ b/ethcore/res/ethereum/musicoin.json @@ -128,38 +128,44 @@ } } }, - "0000000000000000000000000000000000000006":{ - "builtin":{ - "name":"alt_bn128_add", - "activate_at":"0x21e88e", - "pricing":{ - "linear":{ - "base":500, - "word":0 + "0000000000000000000000000000000000000006": { + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0x21e88e": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } }, - "0000000000000000000000000000000000000007":{ - "builtin":{ - "name":"alt_bn128_mul", - "activate_at":"0x21e88e", - "pricing":{ - "linear":{ - "base":40000, - "word":0 + "0000000000000000000000000000000000000007": { + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x21e88e": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } }, - "0000000000000000000000000000000000000008":{ - "builtin":{ - "name":"alt_bn128_pairing", - "activate_at":"0x21e88e", - "pricing":{ - "alt_bn128_pairing":{ - "base":100000, - "pair":80000 + "0000000000000000000000000000000000000008": { + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x21e88e": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/ethereum/poacore.json b/ethcore/res/ethereum/poacore.json index 9fc34d22a2b..ecdd04c51fc 100644 --- a/ethcore/res/ethereum/poacore.json +++ b/ethcore/res/ethereum/poacore.json @@ -34,7 +34,15 @@ "eip140Transition": "0x0", "eip211Transition": "0x0", "eip214Transition": "0x0", - "eip658Transition": "0x0" + "eip658Transition": "0x0", + "eip145Transition": 8582254, + "eip1014Transition": 8582254, + "eip1052Transition": 8582254, + "eip1283Transition": 12598600, + "eip1344Transition": 12598600, + "eip1706Transition": 12598600, + "eip1884Transition": 12598600, + "eip2028Transition": 12598600 }, "genesis": { "seal": { @@ -48,7 +56,6 @@ }, "nodes": [ "enode://6e3d1b39cbd2a9c4f053a27e68fd90d0bac83691dfdc4a13c59f2555078a71e63c5daaee5a82aa6db500512760a5456f86076bf8bbe8011c27c82ed7d6f5fb26@45.77.140.210:30303", - "enode://f4698ad485a027497e1cc992bb5f7cecee2b32a44c47202738d8d0eecfab719541988d0cbcbc5ea94c6c959e5cddeb85fc6ae75fb63dc3bf87cdbe9e6f615e9d@206.156.242.64:30303", "enode://31dffed97f8fed1f34fe66453280a89cbeeda60cf28f6fbb212ebbefd7c7566a02c1c7d5c00bbbb49b9fa8a49f157e0f786f379ca9bcbf2fea24de70d70a22b6@206.156.242.61:30303", "enode://6bdc7553ab2e4914cb47774c1e6d8c8f47ac7c3981891f85f65d06f208ea1bc4d3bf982b330950e0a0cd127efd7145c4df7113159a1d4a06ed722e6c16d0ac6c@45.32.215.190:30303", "enode://872d82a24144bc007658fb6fac0dcdfb9b63aeb05ef563a06d0186f2d1e5ffbfc5c4f1244891a8a86ef70682b9d24382e654b305224883698862e2df647a4d23@45.76.236.247:30303", @@ -56,19 +63,89 @@ "enode://96678da10ac83769ab3f63114a41b57b700476c5ac02719b878fa89909a936551bb7609aa09b068bf89903206fa03f23e1b5b9117ca278de304c2570b87dcb27@35.175.15.164:30303" ], "accounts": { - "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x0", "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": "0x0", "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": "0x0", "pricing": { "linear": { "base": 40000, "word": 0 } } } }, - "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": "0x0", "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } }, - + "0x0000000000000000000000000000000000000005": { + "builtin": { + "name": "modexp", + "pricing": { + "0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0x0000000000000000000000000000000000000006": { + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "12598600": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0x0000000000000000000000000000000000000007": { + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "12598600": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0x0000000000000000000000000000000000000008": { + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "12598600": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, + "0x0000000000000000000000000000000000000009": { + "builtin": { + "name": "blake2_f", + "pricing": { + "12598600": { + "info": "EIP 1108 transition", + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } + } + } + } + }, "0x0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { - "linear": { - "base": 3000, - "word": 0 + "0": { + "price": { + "linear": { + "base": 3000, + "word": 0 + } + } } } } @@ -78,9 +155,13 @@ "builtin": { "name": "sha256", "pricing": { - "linear": { - "base": 60, - "word": 12 + "0": { + "price": { + "linear": { + "base": 60, + "word": 12 + } + } } } } @@ -90,9 +171,13 @@ "builtin": { "name": "ripemd160", "pricing": { - "linear": { - "base": 600, - "word": 120 + "0": { + "price": { + "linear": { + "base": 600, + "word": 120 + } + } } } } @@ -102,9 +187,13 @@ "builtin": { "name": "identity", "pricing": { - "linear": { - "base": 15, - "word": 3 + "0": { + "price": { + "linear": { + "base": 15, + "word": 3 + } + } } } } diff --git a/ethcore/res/ethereum/poasokol.json b/ethcore/res/ethereum/poasokol.json index 8ee91c90c06..31fb85c49c7 100644 --- a/ethcore/res/ethereum/poasokol.json +++ b/ethcore/res/ethereum/poasokol.json @@ -42,7 +42,12 @@ "eip1014Transition": 6464300, "eip1052Transition": 6464300, "eip1283Transition": 6464300, - "eip1283DisableTransition": 7026400 + "eip1283DisableTransition": 7026400, + "eip1283ReenableTransition": 12095200, + "eip1344Transition": 12095200, + "eip1706Transition": 12095200, + "eip1884Transition": 12095200, + "eip2028Transition": 12095200 }, "genesis": { "seal": { @@ -55,28 +60,99 @@ "gasLimit": "0x663BE0" }, "nodes": [ + "enode://bdcd6f875583df2bd8094f08ae58c7c2db6ed67795ca8c0e6415a30721d3657291aec9b933d15e17e0b36ad7a76424a1447ddbfc75809a04f7a0ffef5617dd56@3.91.206.172:30303", "enode://8e0af07c86ec36590bb6368e7ad0c45b6dc658f5fb66ec68889a614affddda5e021bd513bcf4fb2fae4a3bbe08cf0de84f037cd58478a89665dfce1ded2595c7@34.236.37.74:30303", "enode://f1a5100a81cb73163ae450c584d06b1f644aa4fad4486c6aeb4c384b343c54bb66c744aa5f133af66ea1b25f0f4a454f04878f3e96ee4cd2390c047396d6357b@209.97.158.4:30303", "enode://0d1e0372f63a3f0b82d66635ea101ecc0f6797788a078805cc933dd93e6a22f7c9fa51ab4e2d21da02d04480ef19f3bbb9a2b41dd1c262085d295a354bb8b0f9@18.217.47.209:30303", - "enode://ab083db73da15b3995ac9c68035cdb32901835a823cb848fccb672e43dd21f14428706118d6fe5b921d8e741f122f35aad0255bc86807b1d17bcfa1e86e40a14@165.227.37.104:30303", + "enode://875e1bd1b98019a5d6d588c23f68534b75462dd6ecbb3dd058221dbf7aa923f0ab782ab93bb82d42edc9996f7f0816a318bdc761e55c02b95e1169cef66f7edc@159.203.24.35:30303", "enode://8e0af07c86ec36590bb6368e7ad0c45b6dc658f5fb66ec68889a614affddda5e021bd513bcf4fb2fae4a3bbe08cf0de84f037cd58478a89665dfce1ded2595c7@34.236.37.74:30303", "enode://182ee200ca134dc4d6390f3d5aadbcd80df0f7f24335830335d142573eacce4eeb919d30e82c5df588034e167e6ba6dd11187502ac9264a71005127f6b146a99@159.203.95.241:30303", "enode://b022ff70b5fcaf9596ae5efed99a8198b4ae0578ee9d17b733609d803a75cef95d3a2a18e50dca9a7c3b26139f158c59eaf8b5fb8d1d331c9a46934a78acabe8@206.189.76.128:30303" ], "accounts": { - "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x0", "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": "0x0", "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": "0x0", "pricing": { "linear": { "base": 40000, "word": 0 } } } }, - "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": "0x0", "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } }, - + "0000000000000000000000000000000000000005": { + "builtin": { + "name": "modexp", + "pricing": { + "0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0000000000000000000000000000000000000006": { + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "12095200": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0000000000000000000000000000000000000007": { + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "12095200": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0000000000000000000000000000000000000008": { + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "12095200": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, + "0x0000000000000000000000000000000000000009": { + "builtin": { + "name": "blake2_f", + "pricing": { + "12095200": { + "info": "EIP 1108 transition", + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } + } + } + } + }, "0x0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { - "linear": { - "base": 3000, - "word": 0 + "0": { + "price": { + "linear": { + "base": 3000, + "word": 0 + } + } } } } @@ -86,9 +162,13 @@ "builtin": { "name": "sha256", "pricing": { - "linear": { - "base": 60, - "word": 12 + "0": { + "price": { + "linear": { + "base": 60, + "word": 12 + } + } } } } @@ -98,9 +178,13 @@ "builtin": { "name": "ripemd160", "pricing": { - "linear": { - "base": 600, - "word": 120 + "0": { + "price": { + "linear": { + "base": 600, + "word": 120 + } + } } } } @@ -110,9 +194,13 @@ "builtin": { "name": "identity", "pricing": { - "linear": { - "base": 15, - "word": 3 + "0": { + "price": { + "linear": { + "base": 15, + "word": 3 + } + } } } } diff --git a/ethcore/res/ethereum/rinkeby.json b/ethcore/res/ethereum/rinkeby.json index 7d9cc80237b..ee02408f6cc 100644 --- a/ethcore/res/ethereum/rinkeby.json +++ b/ethcore/res/ethereum/rinkeby.json @@ -25,6 +25,12 @@ "eip1014Transition": "0x37db77", "eip1052Transition": "0x37db77", "eip1283Transition": "0x37db77", + "eip1283DisableTransition": "0x41efd2", + "eip1283ReenableTransition": "0x52efd1", + "eip1344Transition": "0x52efd1", + "eip1706Transition": "0x52efd1", + "eip1884Transition": "0x52efd1", + "eip2028Transition": "0x52efd1", "gasLimitBoundDivisor": "0x400", "maxCodeSize": "0x6000", "maxCodeSizeTransition": "0x0", @@ -119,11 +125,13 @@ "balance": "0x1", "builtin": { "name": "alt_bn128_add", - "activate_at": "0xfcc25", "pricing": { - "linear": { - "base": 500, - "word": 0 + "0xfcc25": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x52efd1": { + "info": "EIP 1108 transition at block 5_435_345 (0x52efd1)", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -132,11 +140,13 @@ "balance": "0x1", "builtin": { "name": "alt_bn128_mul", - "activate_at": "0xfcc25", "pricing": { - "linear": { - "base": 40000, - "word": 0 + "0xfcc25": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x52efd1": { + "info": "EIP 1108 transition at block 5_435_345 (0x52efd1)", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -145,17 +155,28 @@ "balance": "0x1", "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0xfcc25", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000 + "0xfcc25": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x52efd1": { + "info": "EIP 1108 transition at block 5_435_345 (0x52efd1)", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } }, "0x0000000000000000000000000000000000000009": { - "balance": "0x1" + "balance": "0x1", + "builtin": { + "name": "blake2_f", + "activate_at": "0x52efd1", + "pricing": { + "blake2_f": { + "gas_per_round": 1 + } + } + } }, "0x000000000000000000000000000000000000000a": { "balance": "0x1" diff --git a/ethcore/res/ethereum/ropsten.json b/ethcore/res/ethereum/ropsten.json index 8c288fd7ea1..f76cbce3c42 100644 --- a/ethcore/res/ethereum/ropsten.json +++ b/ethcore/res/ethereum/ropsten.json @@ -16,7 +16,8 @@ "eip100bTransition": "0x19f0a0", "difficultyBombDelays": { "0x19f0a0": "0x2dc6c0", - "0x408b70": "0x1e8480" + "0x408b70": "0x1e8480", + "0x6c993d": "0x3d0900" } } } @@ -28,8 +29,8 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID": "0x3", - "forkBlock": "0x40E80F", - "forkCanonHash": "0x3e12d5c0f8d63fbc5831cc7f7273bd824fa4d0a9a4102d65d99a7ea5604abc00", + "forkBlock": "0x62f757", + "forkCanonHash": "0x3a024a13310ec9b4805f681b17c3ae6c94167d1c6494e83d70a887ebc27df5ea", "maxCodeSize": "0x6000", "maxCodeSizeTransition": "0xa", "eip150Transition": "0x0", @@ -45,7 +46,12 @@ "eip1014Transition": "0x408b70", "eip1052Transition": "0x408b70", "eip1283Transition": "0x408b70", - "eip1283DisableTransition": "0x4b5e82" + "eip1283DisableTransition": "0x4b5e82", + "eip1283ReenableTransition": "0x62f756", + "eip1344Transition": "0x62f756", + "eip1706Transition": "0x62f756", + "eip1884Transition": "0x62f756", + "eip2028Transition": "0x62f756" }, "genesis": { "seal": { @@ -61,2600 +67,8 @@ "extraData": "0x3535353535353535353535353535353535353535353535353535353535353535", "gasLimit": "0x1000000" }, - "hardcodedSync": { - "header": "f9021aa0a8da98b6ef1e12b6c49e85b0e965f1ed1688f5e3605f06bb3c6ce4f857aa0bc6a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794635b4764d1939dfacd3a8014726159abc277becca0d03c319fe68a91e22fb3b945a8dfc73b817976e29cf57e6c8425e6a02e9bf034a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000849fe1546f8350d001837a121d80845c9d55f29fde8302020b8f5061726974792d457468657265756d86312e33322e30826c69a04bf72e97bcf64717bfd655e2bca9ed1a5253cce5373268729161b1786ca4710488db3c50627f9321c4", - "totalDifficulty": "18787961645682286", - "CHTs": [ - "0x614648fc0a459451850bdfe353a932b5ff824e1b568478394f78b3ed5427e37a", - "0x1eae561c582dbb7f4e041998e084e165d0332c915d3a6da367638a8d24f3fafc", - "0x6fdc50234f74fd6eb5b8bb28378583da28963d26c7226e1254e04b676951c4b4", - "0x0d40b71e8f08c4d93a0f62d93eaee4c6ba92ee1ff6e46dab57dc64c992873245", - "0x7f129d232456d600332b59fb30434e4c71aa95b0e3a4f9328c74ec5b97102ecc", - "0x0aff522a9d28bb1a32af4524d049ce9b3e66149e8d2644efbad5736c9fa044ba", - "0xf9adf08eb7bca9237babc37df559297c6e1b54b585867867a2aed7c10cd191b9", - "0x5b90ca8fa35cad63ad662e3696f41c4159fa871e5f77637e018595a626282150", - "0xf33aa648935eb7dd99906d0855c71893b4c0d0c9b32ab8c8b7ae316e503ba35f", - "0x79946d20c81f7aae18f427d991f21888169c96424813461d04a61758bbe9f214", - "0x80950805fc209691ddaea24e8afecd0096e84e3c465940508131f41f569d559e", - "0xd58de59cb3b7f0323ff3edaf9e3af56843258c457f490346cf25a73a35163ed1", - "0xcf52cf0f195caa769e2d5857231f31bf17efed369c358e7ade257f6278b1df0b", - "0xce570fce622e9f3bd03942c63537417f96250bb3605d2182e5a567e961739e46", - "0xe6add695b25d4b06bfa08a80e9e9295c84948fc34202cf24b42c1cc325075d04", - "0x4699a5f1df0d7b2dab8ba282b6e83af0d0b31b604443e13ccf097fd46f346bcb", - "0x1a16f27083787c5dc1dd8f0be868338c78ce147d0cc043b299870f568de20a3b", - "0x2dc5c4538c1812e892e0a53c112e8e4c3fd9f6f60d1e8531a9df5419df86d013", - "0x40de720fa5ec3b58b3be838f189ae59bc4f21bc8d8f406e569339ff054f93459", - "0xc7274c13bc7462d09cbe60044ea6c9e92622204c41dfddfe864d388f830ab35e", - "0xfb6b099472c5457a813698944d4901b766b4bb8a177c4d357897f1974ba6c431", - "0x9e6104a79f7bbf96d117607ec87be8604417d80af11f558ef1e74067168dff0d", - "0xb8deb38b0b58be7b3eb89a5a3ca1eedc5726ab14186baba17ac520a592f68827", - "0x59a58beef1250fb3315291f0b84480b947672deb331980cda261c94ba544e22f", - "0x0fa4b28513f4a3639b427b5abb87902273fdc4077cc0a51361c3dddc264871f5", - "0xf9e1e92a1de2346fbd8b54987960f08a8b21cb0a8a6c561edcccd6cdfacb46b0", - "0x89f4a9590d0865e126622cc47ce2a1ba50aad880161335e8c88c8a24af709fd9", - "0x5f014ca414e0663c92fcbda0c7250153445a04808c7d062859d000c970003834", - "0xbdfc2347806fb9641788978b5d0a6bccc81b11203cd751dfae45a8dfd3bde7ea", - "0x41420d311b7b527ec68770306d97ce3352d7fee50fdba3b05bb54e70c7250459", - "0x3ad7001f1b2b2635266141327ed45b017f334fc3a5f410e3e99624492c489def", - "0x2beb717939cc0404dc173e50dfbe1477ff32c1ccb59f40d7900c2337021e2d6e", - "0x558b53c5eda0866f7515f113ea3254cb85424b739a5dd6ad48bd4d9221006985", - "0x294c62ce078bf950361111adbca5ebc963d5f12c7014b04e3d86d3cc95c6faba", - "0xec18a7c338bbebfdfbd03f1896d726dbb1d21a29e32e92fd9949478078bb19d9", - "0x6e61bb0641c13be0e0858bc3da3a0b06f6a8ac462301a33fab7425e99d6ae14e", - "0x7a4378c70cb339cb4e3805d013f6ecd66ebcfed3dd1df972c8124bd7b888c8e3", - "0x5911bdce0868655abb581c9ce87a51b85b449101d6d52350c05ff1db30deb900", - "0x081f65d017c48967f02efec9781ac4bc124f42a31ad20104cf4ac6553fe788e4", - "0x89a055c33ecf13f71637fc1dd818c6c26e86f4751c2e09c5b8970195b39ddcfe", - "0x83d33e35f31824d849e27d2cd1d4d02812cb84c8508bbb391ba563264ba2a429", - "0x144d66cd98cbac72f3dd9af9b2656529a00fe7fda8f70e02d0ee025fdf2de9d1", - "0x35d44c6849d379e58398189d06b149eee34a267e94230ae5e24a0c58ba3b7d30", - "0xe4d67e58b9be3711015ac016983bcbfeaf2ec71ee85bc08fc2e53e6d173ff348", - "0x67f7bfc9457f1a1566d9acd3700c399a2fbeb2e1071b3f5c908eb56f2bbb9716", - "0x2546b96a4a88411aa7b3aaffd71a0e22484cf526a099d58f1dd330b3209e4aca", - "0x91f9c4aa8357a4835ae2fc913ff21eb4790b711d2a813e94b38fbf52ced0c4f6", - "0xb4623b136bd01bbf2dfe3ccdcda850cdbc6da79b69324a582d3e1c29703a70ac", - "0x68649bddb9b142d43905ef85df986ccc35c5b0796d860b367c21085c9f1d0220", - "0xa007ebec8e3242c209a178e01eb7aac3666fe09370dc1064bfbc46651c5c4e01", - "0x614125ba7478c7ba2ecd062eb8014ad3b50f4d9a7ba546c932e3f0a3c43c13c9", - "0xfa579127b6d13731bddb5c27cff6bea2d0ec13bb6d17679d4d9e1ffce8d7dc2e", - "0xdb11e78fced06b1bd768da0d345f19c023d5708e7b57bf09c37009887e0bcde8", - "0xda9a47b636cebceb2f378d9149a4a5bdeb6841746e7f677942c26629d3d1d4ba", - "0xeaf7cf3816297b48236ea2cf3f60ece988ca7cc97f5c8efaac1433d23c3a5ae2", - "0x1b7746aeeeb0651a867a3f1d34eb0662660aa667847c04d6761507f043d0eb71", - "0x4e6a1631fc6702fef39cc0e383f4fb5608e8a1f0c721b1622c8f25ea14aa86ce", - "0x9e96f3b348c2170685fbaa99c968fadb4bd9dd621f6e913784851ab1bcad60a2", - "0x65df62cc768c87ec256f7308d9f199040aafc24c03d5c6d527bdaef9da365e4e", - "0xdf067410f903d878471fc3b53d020bce9d0b672ebd51df16ad65f4a3468be124", - "0x358cecbaca6506567a60b08d00c888c7b1dd66ccc292ba7c4c9aa3e37b018391", - "0x5102782ac246337226e6527ce651294bb5c3787cc68219208d6dfe6ce398f0b5", - "0xe6a48cff6189dc26c3d7242eacaf1e34c81503327b18087840a755c322665ca7", - "0xbf599651ec19034ca45bec20673f41813b43387ebbc596a57384a813eb6a21b2", - "0xa5a5242eb4e6fecf1b497a62de8138401cae35cad4a9c136d9efdfc12fd53d60", - "0x05eddef47694a7dfc61e79f78b65bcf9c5e0e65513cfadd2957ac3a324f54b55", - "0xa4b5589bca8121c6bc00a846ec459faa99441d0ffa4b577a45984c32d63128ac", - "0xc8a2f9d7974a1aa77b35188d9dbb3fd56d5a2f6c796d2a665360374886c5a386", - "0xe9d430f782c0275eab831108f934ea3bd589c6c72f9da930e9fd392de833e486", - "0x71e85321e2d1a4b0eb48d8493a97ee3634fe153f30a85d1dd55fb0f5f8c82b91", - "0xfed03a8dffe6cb8043a11ccad791096151f0803b020e4dcf33adc4b180551fa1", - "0xaf1beb7795169537916bf4c9b1515effd365cc0e3988cd3e296fa075831fb63d", - "0x06ffb84050198793b580803a41e78d4a6fbcaaba0901a6f938a5ac5f9982f1e2", - "0xb416bb6b94d5076df719edfd2e2bb9237eb2171b9ebbcdb082e1bb1016244371", - "0x60d6170c0fe77eada1dd85af74079ea0387a167341df0ee8ae110d0047c00fef", - "0x15f03d5f617db0bbf963d2daa06816a768b21794d05873131deb677bc3ff2a07", - "0x0b9fa1e7fb11e3c0b33cc609a87dba8c1acca7476a15da2116518d72ea019e0e", - "0x9bba5ba8b88b76613a52504bdde96ccc1064f3e868881cfb4b155d24d237e093", - "0xa8a77eb3fa43914feb609274d1ed96d51ae29028fa59e7f0d174b085d106fd27", - "0x142d2e126b1faaa6997553835ce1933083a5c2cfc9818ae71d559c8571757fab", - "0xe9241f795bd887173fa93f9e72bd7f07ee58d69dede512fc31f9faf0ddea8e29", - "0x526d5379c00d1a318409ea42ceeb7fa9d33a4bdb71793c940e0d1b42cc848d36", - "0x2045cd5b32670fc4c36fd32e45437db69f468aab51e10e32da423ad28bdf85b1", - "0xf0d0bdc64abd76e66be96239efbd0f25ae30bcad096a4cf28b8b5bd9658c8c3c", - "0xfa5cf979a921b2c5ba8fddc330cf1df7a445936f7bc990553c578dc3ca45de49", - "0xdf2ccce3cf15b30d7bef7ca83b45875e96f02e41c6aee9a1a97025f0dc75585a", - "0xb61fe42d47707d473a2ba8483dbd4b9a40c1e7b79c2bff15ff52aec560d505a6", - "0x068ce2cc3d65a152aa574be6f0617ff7e2f688db97b020d8ba84e81ae9037f57", - "0xef3c631c846ef30f6dfdaa901d8d1424cd100710335805e14226976e719cc2b2", - "0xd582c2e6adb0d112786bdf27dbf1c77fc7bd002511e53d0a10f4dbe780156478", - "0x8a028e29f728e414cf7e3d403bce9de7766feae6090ae0f7f787944569f2b001", - "0xafa8f78b22031dfb0b06004d32b6699421586ae779624fdb7153854d1ea5bd4a", - "0x7ff5e562415a97c4f731c7f8a6b82e401705e34a78751a6e8039b0930eb0dc59", - "0x4cff7c84dfe821e917e48089d3a25a88a84983fa5836d7a78fe050915ea86374", - "0x2ba7416bb329d81424f9fb12b43a285eb3077799e71cd21f0095845d2d165c87", - "0x613ca539b0ec3a5819424d8df2f4ee08e03e5e95a2607503bfaa8574270d163d", - "0x3f6ce1315b8feca300da0bd990b582c647b4440d4a218fb4bb6c841a789da37e", - "0xba8b0c45d55e44cec801c724ad6b6b699fa78505d87a5f356f6ef054628f0e0f", - "0xed1069e471b099408ceb19fdca42cb57bca2389dec54b8fd283db0a53ebe2c30", - "0xcbb375230e9aa4f7aa8536f42f4df3a6bd7572f603c03e716a934b0d3bbe475e", - "0xa8954ae2e84ed2c0f45db6e41731a09ebffec9b064cbd12e020e66b7575d2c9a", - "0x9229f2300310a0675f281f76c695b06214960621aaa4592e3bba558c23b5bad3", - "0xe821e771c08bd42b6d926bc1ecef87dd846110693f59d71cc79a55d7bdec4cf4", - "0xa7e54537a3a01524894a0f602bf010043a52c8c7ac7f6cba10a6ebf86ca11820", - "0x3ef166570a593e608afe6b848ab8543957963e63448b7a1d0febb8127003b527", - "0x1308ea66fbc0fdf84680f227d6425e63dd850286de1783300b64ce77deb696ce", - "0x14676109d2f68c5cee4f58c27f40579cc04663dcd01017963402aa2f06fd315b", - "0xfb9434241629e83ec929e1f3feeb1943945e8ce87590eb69a9ae44e3fdbdcb4e", - "0xb695c39514548fe5240723355a6a2eb2d233a48018485435b2d9a63a1097ef55", - "0x9caf7a3660201b0118736033e479b74e488f64a07a79e33f4782048af424840d", - "0x82f188de672c6e1826d70aa5f7d54f499aac011139ecde9d4d05360cd8b9aed7", - "0xdf7367edfa34b79be80c06aab16a54e7d1f74eb9f813a080a8c4af916e3a61c5", - "0xbd7cf6ed130f745c853777d762706e9050c2a45d01839b124d517e3e73206c6f", - "0x474b7557c158d93d732fa0b8829b40c2d9e593206c3c36d7743b6eadb0067689", - "0x1ff6d56aa85903b2a9710b56ad4760825edc5343cab855bc966cad9bd11aef7b", - "0x52c4c3990413706a86714b1dcac8d9c4e55a36eddd176dc55066cc39e308175c", - "0x7a63b6427abc9780a14a4826c45a313fa2d020ea33ae91d48e4cea28ebda51a5", - "0xfa7ec58ce730e3d66c79b2616e14f11b87077c5d911db6bd89cf7c69f9fb2559", - "0x58f1b88f3ae0b76dbaae0ac58673ab38118f5253b07ee2a309cffa1c3f9f3a30", - "0xca98407f9cbb2911422f5a0716c325e1c2ef053355796e47eb619094790da45f", - "0xad072d0b4fcb8ab2662e3d27f6c6baa2f914bedcb91d178a9ab02b9646e6203a", - "0xd8319f885666fd19a5a9801005530e4a490fe29765f9d60c38b60c5b9ea92d5e", - "0xe95daec9ea36ec00b4f9f7478ae136c1453bbbc3530f839e93483fdef4a61cc9", - "0x6eeb1a044cf3cddaa87f70dbca704fe9cad675e82659380fb733153ad452761d", - "0x89c53801a896ebb9cb6c3a881269a93f610122fe9c3132ec19d64a6f22e9f7bf", - "0x81e57e25ce8817d0cf3ca17d099ec20e75f11d32464c2480d2f63a53a22dbe47", - "0x42ddde6c79ae71e64ab091b24e3db68c0c3b560669aeda58107d07f61d9bd39d", - "0x6936a2002771717810dacc23da43097c7c1bf5c2619975f55132b5f450c03047", - "0x0dbc448620312bdf04e118caad4ce1fc5058d0c14b1bf655b650677a2d1eaaa6", - "0x108dc62f87065752fb77f6a3f00e9141e1fd170b9c0538124739bc75d5632b4b", - "0x9b555f75bbc6b9fa5ab1b63b5e7a4f6efc7e9c659a8829a69ec591b968133478", - "0x3f31b6d6e18c7afa96a95ba0855b52a2b0e3158d338ce3a20f14c14078abbeff", - "0x78940ccb7637be74bf6f1735c615054e632ce53410b77e9b1d9f256699a54861", - "0x71b5505cf3b75c328113ffdc94e8ef89b673838a26bee4bd7ce582049592e54a", - "0x68ecd3b26c50447e6483aa0adfd4187a4f99bd1b8517772a9b90f939b322687e", - "0x6c291cfad552325bb806ce314f066f3a7af0ccb5950fede839023b8140b3d06a", - "0xa32f442c4134ff4559c3411aafc87649ba6776f54e0148469b7538c243c19787", - "0x06ab81f6da34fadcc2d1b52982cfc69e20d9451801da18672dbbabe4abfb6983", - "0x8ad902710d008f2b7bf8dc50b66a11045c7e10f80be12f6c5e622c05ad48f2dc", - "0xb85a2abe729f4f7bfebc6a7eddf085c99992d6af700f487a9a2a64164dd14abe", - "0x11039611b2f406125519fa4d3f1cc7f4a89a284be02f0d23971b2990883b4b1b", - "0x69e21960b1ed6da34778848a92b200c8275ec8ae1763e07b8edab481cbd5c1cb", - "0xab4a7ffbe54a07e8b1af0eccdd1fda88d72a25d61bf6b12c9f5022afae936fc1", - "0x8d8fef9f7b0df3e2cbe6cd99ae3ec231c3bf7a583028c0f6accc38d2ac294019", - "0xf8c75352c289c74a797e8673193c505600611433730e415ed877087cad99d3fd", - "0x28b756fe2c8e5c6cfb5df29e8c4f3a9ae149c9d7408efc31c57e5164e9333da1", - "0x3912bfa54cdcb8e6eb1900d7b8d464fab0aa97593b9795802d8a084d66d82288", - "0x0db191d64e9a75039a2fe0817308692f3a163bcd1cd6b476fb5e04cdc8af7808", - "0x7df00cf34abc055979b1dd70e38b364f0b1f9b6ca8a2cdaa8e72dc1c30bf5471", - "0xc4ebe3625665babdf6b8862da0ab84be8fd7b5c1e4849e9c0c2277df99c1ce4e", - "0xd6b86c60afd43553bced67a5e202b5dfdd62e668359eb25e29c4768328136fb8", - "0x46be2d0335c8157496ce74edc96bba09b3873acbe46c285c54e6985615519fb7", - "0x876940ef8cbf54dfda6ffe0d469ac0fc9adb375fdfeaafe3da8345a9db0c964d", - "0xde7c02533643f05dcefdefa9c7cce1f0583a655fa2a9b4a5040ba6a7489c650e", - "0xdfb9bea572e6a60edf6406a5e87ef3fe60f5e5884ff2fdbd9ab5ccc7f0bc602b", - "0x56b30ee04caa1d1e8367f58855c361b43c2c17361e5b242e3b6dc1e53778df5e", - "0x5da53224eafabe23f1a5637ae74cac54adffca2f18fb4a2f506a617370f06378", - "0xe1f7ba27cc89f68862c9cdefcebd4b63419d01e6cc49122f7e6187912f3298df", - "0x67d0b1d84379e0f67466a6ea295a94cd55ed67392e51a6012994218be921d805", - "0x9db5e16e54e0f9310e578d5dc943a5e45ef5b3f5e66bf758d99eb8a7528d2013", - "0x4e0b154a0b06d6544950634de6bbdf511735226649bd694b4194c43aa7e89bc6", - "0x8146ae0e77da83a036ed8b78046f204defbc771ae61bff4016cda3ccb6597cc2", - "0x00cd2da8855d464a368ccbd92907baef0fd0fe2aa8c24e48e4bb6854a011d746", - "0x261fe32ea2985be3ac260b68aea223c4b8e4db240e5fb182490407a062acea5d", - "0x65eee7ac2b1b90a0f5a4e0950b2a0f3a9b32e0928aad7f4243d4bea2f056641a", - "0x7d9e7db3a97919ff2204736eb3920587d8fdbdc97d91f14049f1d41a1eeb78d7", - "0x9c2edb4a5bb85fe484094b0e033cd7d31408a2e2d4b801a001f6f886b92e73ef", - "0x1e191b4674a3a80e09b68efe4c4dd4f38d344ac0b42a1fa152aa1242a5b53e1d", - "0xf498295a3d4e3b72ae1a11fc1d1f031cdcb68aed705be3fd698b5b92bcdc8cf3", - "0xcc89f9c163149bc17d5b0a1d79bf5538a86c4aaa3374e202a7b41cd4a562c6e1", - "0x11b5219b9b2daa6832f206bba106c64a2b9c3aba39f7dbad1feaefd788c9ee42", - "0xd936254f54f45c34c79c8a1ded63133c9437e82c99a26a8bf3efb95ad9b5cf54", - "0x8e8b502743a0ea7075c89a88bd149b8f97846dd915918ab8cb21c7b7b710a4da", - "0xdf0e67381564465619d90165d5677c19a396674d832530c18cef2a348f9671bf", - "0x6481f7abc12e1a5655b0c88a693ecbbe5ab2685c776eb5a9a3485167eeaf95c2", - "0xe9ebad32fa87a2a77c82a6d276a18b0efd6f615361f0d6e72c0a037b35bb396a", - "0x3e052b0aed9cee87f4d70626837c1a49ec169b2e8e03c37fc5616bb0573fe35a", - "0x15245833a84147b5df07f999c6baf27655bda682a5a4db93879f41a2ea023f31", - "0x4820d25f27aec110817a81bea0d7b9bedeeb8d9af55ace79de81380093a02176", - "0x4e156aedc00095dfff29092326b02fe2863a6aba3ebf8add79485096f20267ff", - "0x3b63cf9b1decf710ac331a5db829a5fd2eb0a7da6724c0a90b9525334092d6e9", - "0xb54145d2b41dd0142f5d2487b0db7fa96f42621c08d63edc036dee4b072eda08", - "0x8084c6690651740db4e67dbb0623b5d1342cec0ce95b6349058d7051fbf186c9", - "0x9c6b1c3b15784111ae80cdf7398e1283f4d68a9c2a6a2cd95b8594e0acee752d", - "0x71322541265c9b5d5404e9f089e505ef1814fcad641a81ba3e1bec009abcb920", - "0x1e028666b6fe44f8c8b2c9f3fa8c8ae5f062a7e117d79d661446fcb8a72b5db7", - "0x3a6cd5395b4be2aa1ee4f7d24d1d1433f46db1307c7ae823803ca1df7843ac6c", - "0x64b8b040c6c0cc2ebb2f011039dcb091d4c8f4d338b82fc1a68235c6eeb8c8a1", - "0x89f98b1f7e78b539f83d45c5ce8cb455e8ad7686f42d39f9c195d37ee9cf5fe4", - "0x14abc82071dae6f563f272da9e1ba7f9ac6f0547544cf9cdec088ac6283eb464", - "0x6aa6490aa64733dab57d954b9907f421c6c571ab2b2cebbd833d59d74ba19be3", - "0x62e4524b07b9b8d8a53cd29e457a58dd9f940141797f847b889023c22330db0d", - "0x534a4c4ffd28cc8c3f9823826644138c9207255958f3dac5d0a5f279d57508d5", - "0x3e7f74f25707ae6686334c585603253a21764cc1adc730353c02a56f6b7f3e81", - "0x59cc225b1c9cee2ece86b3093a8bf6ad3ae0260ee760384f1299b5af75eaa1ff", - "0x15ee9a6c566674f1f7742d1e07015fcd66e22d7648ba25be50428006efccd026", - "0xc0668ec3f88e6a9f1097050e999f0082e9a39ba43fccb194790e51a41602e704", - "0x03b0cdd4ca4ca3d8b1270eb9b73dd28f22ce5681c3e0dd5d11107719d2614de5", - "0xffad54056c851b02f83948e7d665980f623643ef40aebe91936c3bf0d24a3a2e", - "0xdd587650149a81f7a5e92739c18af1216bf32ff2cd7b1c908a6d3100c052b0b0", - "0x1fb346da4fa3c0b7d1acf87e4e8b9c0b05cf41d21794d9db58b14e321f9b6bbb", - "0xc15a230b3105e4c549291228b28a3ded073e7061f56bd31a8bbe4570e2e11910", - "0x9c01dbeda0dd9cfb4e70e8b63551c590edf5be26c879d319781154c44520aba9", - "0x8b418cd11db8823868c9495d213b9e5c5e0d8b25d5f7c262f21b3d2b9f591ea2", - "0xff7cbadcb60f8e9574e1a1ca57c0cf1b5b26725a7683a870b581734ce906df73", - "0x0efe88c68a8404cf9e1cee6d4b863c48ba502c5b5f504dd8a6f251122138e651", - "0x87994dbe88b7749a347e438c76273ba5a7b8835449d32ba75bbc877979e5a3b8", - "0x366779f7f70fc10da80427ba5f0f6af0e1b5d60c37c0c7e524e408a1e2f261eb", - "0x616c1a9eeb13a026372b099a101eece0cf0d1715dddf192147d401c722b6a6f8", - "0xb35494e9758f3bd7bb23fdd9c86f60a8bc73e3422cc9be80c7f592fbcef26b9e", - "0xbfcf52fcbd301f4b5d32e62f284f95d01bc545e16f806ab64038d11be74f8638", - "0x3f155939a40b556e30ccc4b32273a3586fd5aa89c4209337da1ecf145416c6f6", - "0xc23227be04320beca30ce9ee2b9f74bc8802414ee78754d7af1bc887e1e260f0", - "0xbc45e7b6bab510e72479ed0a675a05217244a9879be26d24780a39537c233f10", - "0xf651ff7ae3c7570a95245216c9461fbf90293a3962f8883d502a92277f0e4ecc", - "0x92b1b6ba3f6bb8a949225e51a480dd63152147e98d96ff5aca2ddc81eadac6c5", - "0x368b2b21a1411fd2d651c12cba2f2c3d4d9dee020e98a63aeea861e4b6837e12", - "0xe52565a7d159275cbe9e024130faeec3ddc3c3e27507d35d92565c8a56e13c64", - "0x3252703f9e4b7ba715810126ba57d87bf585fbec8e5928aa598c577f11daf792", - "0x6283fbe3a436d7860c0b564f2c7a32814c189093c775a8d800ae24160dd3e72e", - "0xe76378b3adf8f756728c553a5d822afa016d8117cb04a24b1c2a1eadd62421a3", - "0xc0a2504fcbb69d1adce70da836a73fe8e8fd74aa7aa4cb7d22bf844e7b91aa89", - "0x4d3e70bd5f122962b3cf64de57c69d85194126d71e79c964fc4397afe252da25", - "0x7efda1eb1564ba1d295eb6d403d9900b3336f081f2bb6de2c375889883f64097", - "0x6bdce19bc957c2bfc7e49f91d436367374847fdc6daae2e621f00aa024f78094", - "0xf42d152632eb0a55173ce7ce4cca3a91c7d7971a7456eb6ffe88dea54c3182df", - "0xa6a2353ab4a3bda46687cfd3ec8e349f2ed61e449875b9d1580d9a2370ccd54d", - "0x3aa4b80ff21dc79fac0b4f913d46687c4f991e5005fa6af731c9b2d10dad9c81", - "0x7d1022dc5a7fad9c0291448c56eb99283e1e64c37f6a55431843644102959d6b", - "0xa918366830e62330c423ce2f03f99206f6f3c1bf7660e01fafdddc40c8a0b650", - "0xace8ee8ad7fb40d481f20b165ac8012f3ff69f0bd8fd3ee290d894236549d065", - "0xb03858a13f584d7128989a47ae71d945dd67a3c2f54ecb3368ab76371d944c6d", - "0x56c78acc4c4dab9994018a969c3d3451300f8229d018f6af2de91fa4da6d4230", - "0xfb4313d83488e1003d38869fd8e8788d7e74c124542774ad7fa75971bdc61131", - "0x1decf505cb4473ffb91dc428710d2a2188d57bbcca71b7b5e277eaba0c7252cd", - "0x9db499b44cf0bd96713ec99cc769c8454fba3729014d87b67c18b55f0b4b9fc0", - "0x3cc717a489465b21a09c3fa19453d2a8cddb914b7d2789c21db6c311386e0f80", - "0xce4868287d923f8b22aca7a1df1ee11685d7c50c6384994ded2e99fcd2c01906", - "0x06b502cc70d1ae3c79e44a421b6ffb9a1a6f0bc647b4421cb375ab5d8474c713", - "0x4803b5e78af29ff140e37b23bd2f3de9fa6cda3a1bea2361e838061490708d0f", - "0x874051f592d34ea3684460f094a78d0e1be3bcf49586b1467be0386f1f9e3d94", - "0x8b66caf3a57d9998e2d88b0e4ff296825b6dbafbded01e384fd87bf4a92c5bbb", - "0x61f6244709692f492cf345586212374f4b209f1d2f92d3ba0e4000ad0634bda2", - "0xc58788c015dda34717586db2395a654f49ef0bfb4642459cc0ac5a5d74a2fb67", - "0x5161b49a84d5c614d72bb3e220878afa94d9acd30cdeef78873dcd91233de390", - "0x5e358365657197bab7481eab0dfbf8a6d66eec8dc62aa345c5364f75c07b8675", - "0xb358c9e7d218bdbb42f91752d03365945a474d49e84dbb15982e368def01cffc", - "0x908bf24327cfa0001d13fb5a0a73b536132e7ecf66d47f2c77215b03bb5b1693", - "0xb041e9e1a1087afbf41d87aa9e56195cc548527c81023d43416720c025b896ef", - "0x48ff5fcf252fa38a00aaf887e108928755932f45d7cef5a8fa0b72d711789f66", - "0x16f99b5d528b9ea7a92e5f7ac59e60d4305b71c9b20485c7911f84e3ddd1802a", - "0xeadc395d16b240b8412e14d5065a33847fcf8ae122d22f24438d7f79d8cfc40f", - "0x560faaf4e0471cfe39aafa10c9660d61c8489d2c5d49eff2a8e62ab31b7ec08d", - "0xf1c8bac46e0764d0faf89556b0080cc7b16433070e1e945e83f1ae36595da7fe", - "0xcb7409cbd3d3104a4b5d856887fe6d390643b4c433de855644f2184218631662", - "0x6eec345c4baf4e699f0ea48bdf54b2e74f98020395759e275e7cda0138424ab0", - "0x48a8bb061ad00561541b4e19009d3e8c2e2b9064337ef431fde45fb2988136c3", - "0x458b21c6ca362ec731487499486c4c0ea69905c8f36617810009edac608b9e20", - "0xb338584ad517b679ea52ea9abfc8d786ce1eb55cd3f221782de658f75b23ed47", - "0xdfe64bd8632a87d995972376311f78a0fd390fd2635e002e3dcd7838a3b28516", - "0x49d9ca4420847c21e2969911b7b5f321fbdac5037d0d8644d6f357444f91238f", - "0xd654571d72fc11d7a86ed2408cd1f78e8009dcd117323c8970614ed8731d66d4", - "0x2133c8e084efa57df6e1b4f1af5515a4662809d722ec002676eaa99987a2607d", - "0x5bc530b5fae32bc84127fbe7feb7eb07432313915e2d579a668f46fdbe46bd1d", - "0x8070890263bff747db0122240249361f5aa66cf121c3b6c4ac40deff6e42af5a", - "0xecd7aef2e4b4419539fa40570a61a967a0f296f524c25de9cdbb012b03d8d837", - "0x4376d56fa5e09c64156c6d1371151a33f7ac1e46635afb37823ed75287083bf1", - "0x50d32b39873466597d5fd29a2bc3596f54051d064559c46510f643f2c24e6319", - "0xc44c4284e5cb873846761cd012e0d221763c972242c0c4a161f3fe0e2da74026", - "0x141d2894e4cebe94df908cf463d5aa6a95e22624f5183d03cb60011c0677eaab", - "0x1bb68700d0f6e052ca64c983352731ccb0c3947ec73d67ecbbcb72c5ae3791e5", - "0x73095bc9acdbad7044390e72ae6c471c8636dc0116e2ec322198bc080af7fb11", - "0x66bbec6ab32528486c3ad89c36fe750d9c6e5bec862ea683988ba578302db503", - "0x979820d20c32a076297e3a940ef4b9f3ef02c9ed551996fa106b6db778852e52", - "0x4da1d4babd26763bb29976f0f2c439d3cdc06089951d0eeab7df2da59c128454", - "0x4c70dd6d755e7fe3da5f94dd2af53d037bda9d7b1eabb68bdb75111ecd190e04", - "0x87ff13d01ebf46a5a45f5b29e7026117e8b41d135829457d80918dc00a325f0b", - "0x78d573b16ad8a3c3b0a704a01fa79915907fbddeaca6c7e189c5c586855ea882", - "0x95b9c87ed9b7365b1ac15b02a388d47f75ae8036bf658850ebbd8b829ded9186", - "0x812fb8d5c2d962725245a182d0d267fbf97358d5c236cfece3fb398fe641f78b", - "0x31db1fe9db91df6684660f1eed0928bbdac477bd1bd376399e27a53ce760ffd8", - "0x4bb0cc2765ae336b21fd1f983f21464e907c6fa4831b047386f64e021550b2e7", - "0x7f6317d849a40784edf0d2c7b32f57569e561efcb4fb3c5f857b30319a93e2e6", - "0x5921a9e8ccdb11e574a012cafcb05a6dcf86ff8006595837fce805675202b8df", - "0x5357bc6834cde24334aa697f050c51c0456f7b03f938819baaf8c5be1dc10df5", - "0x8fb8b01b64eab496c9496179016adac0dae5ff6793970b3c307202fb17a93af1", - "0xd4ad4478bee0d5bb0852461107c3bef946f147f66a478789b39f7efb517b3bea", - "0x630bd6a0422ab626b28f205ec9bca83c499462556cd8116556bb40ad0cdf0e66", - "0xb2d07716e5e20312f314cde99480110794d3cf70ccc786828fa25ebf97ffdcad", - "0x87fc5c3371e3ed8f9201f61768fb2683bdadf8c3f6a3039c3504107b07b508d0", - "0x9e67bf7118e42faff0aaf8aae3118867c692b27c35efcbfd057d562e8d4390d9", - "0xd96833f813ac20074274531949bce0637e06636b6ee8b875b4811f09cbc8c116", - "0x4e9f29f98bee2fdb5c5692d94bad8f87b6b2904258549a937603da0ab5d36b65", - "0x77c3b0bee822bab79529cbe96d6847649d9528399a488ba9e94484f7059c46c4", - "0x948d3eca01db52a52679fbc33ca30aceb7e3ff6449463a2748fad0982b75313b", - "0xb3084fdf4a2ea5228c35adb20017039f4ef01e97d99aae08d4adf591b96330c0", - "0x5bfb4369df41aa75c957c1fffd6b498be3e7ae858cd53f67c1c688b9c78afc4e", - "0x6a2092f54cfb6c26b0d5c8f559e5e9995c79873db7631a8f6397ef5ef815352c", - "0xb286ce1787331698f1f860e1232f6766a658d27390b7623d460136a6be90a444", - "0xf84ba9ae60fe7bae5d3eb5be6c6b85aa564f8d0a7ffc479d4b0d1c8bd69a2adf", - "0x467550339dad58cba021e3fe967bd5d81be04e6670488adfcfeeb0dab07d6b2f", - "0x5150476eb3075df5b6ab3fb54ded34ce76e06660ee401951652e484e9a3fbd62", - "0x80845f54e9c21ee96f4aebacad6d199416a6b30fd741d6e6c9966f58c54f1364", - "0x8264045a020782a7dfc047efdfaa103bc186e88c920f2c25a016911fcf33fa2c", - "0x52399e942b721929b9875c274ec6b5034c26f7749f6d26eddd99972370dad3ca", - "0xf03d8bd2bdb8ea0b6ae2e7bb55703520cff714143501f291a3e7199be5d466a8", - "0x40eed1aa25f1a87c849f5e02f846c991e7473af9fa0985e3d61bdee01cf0a5b8", - "0x3d18c888cc8729f63ee415348121e10b5d990c8f091fccce7e2d8f5c0132a20a", - "0x2e72d969de06d28196506263e6dba8310b6854b5069fdf94317a6d3e90752560", - "0x2bf249dd628dec9d4cb267a9013e050f0710daa2915e468a623748dde1a036e1", - "0x91c578ed45a55d0108656d1533d209de851f77d9abdb58265bf867d7aeeac5e9", - "0x88dabe83eea11db59d67db99a159c623498cbc648c79d829a317719b836540bf", - "0x5fac0c3efa49004a1af631263097978f16f7e6710ee7a1557432aa727e44e09d", - "0xa8506487d3c54e70b3d6e83eb72af7858f583527ba6f6a6aee6a3eb33750e538", - "0x62641830df38adc76a043baad1fbbdd8e49356c11b6565ae0b04429743011b08", - "0xf053195e1bc2db0d0451ab69471520d0a03370a8f27a4f62a1885702a36d583d", - "0x7197d7a7897505dde22c7a518054d9df7f3368fcbd109cb596872d92f6a86fcf", - "0xa5092fedfd64b4985e70ffbcd7b4522eb45f9bcabe4c478b1f1452b063d6d551", - "0x027251c22748451514c27aed0f091e7589600bdfe816ffb0a5ba99d55265b65e", - "0x590271f52852c220ae38b56cca9960a1fb94d5343a0090bfeca5e8b5bf82b2c5", - "0xf8df310ab62fd52c452122deb0f2e79bfb85bbcd0bcba7e6aa5a99a541233431", - "0x5d313f0071f2d885da1b1b5d104b8d78ae49842ff9a636ad2613ad9e90b4ea52", - "0x2672988155e5c27a3af3ae26e74d164d9f52b4c71b74905ae90466113e991692", - "0x330c8cb081da0372f6dded08c0036199e11aa8ab51f6f9869995cecd3eb16f24", - "0xd65721d9678bf4667c6688af76d2dba106fc51e8396b0af78bcc80bf4149629a", - "0xf5019f4da39e5ca6102679e5a0388a3850eae7bcd91c632d26515caef12c472f", - "0xe9c747dfd6af4dc29b5836495be42673a20bb2475264a2f9bed00eb8250fea7f", - "0x2486ab886ced7bbdd774371aa0ed1c177d6d187bda235a4a8fa390b704eeee8e", - "0xa91824527f5a40050aafe359817594b43cfddbe3977113207a7d5868507caa19", - "0xd44591357b62e80b8f5606eebd4cac5052efddfc3e6a8c5689660d6617f93b33", - "0xfe5628e6d9dc75f413cb12ee1004e1514769ad7085b5434f762e93db9115b78e", - "0xd0393986e30a8961e01ede613787fd2e6fab42db329d0e57bfd16b42cacc4045", - "0xbe40cc6ffd2d51f7658f48359d4b726c64a354a9d1becf189ed83775f8fb4086", - "0x1d06de0ea61fd233ed701c9520f18012143277ca9b379d39db62fcd0a4e7681c", - "0x463ffe1d54544945f4abd7e35cb7c90a4a809c25e1919215207b7d2a8d3b4fe9", - "0xdf128c4d6c4048d4fb0f7db6db091ce4de10d509c45736f5521ebc4ac93a7ddc", - "0x5ed827eae4b7e95cac552d60b60fa209157f1a8c6e3611c1a5f6f7a49e8a6a92", - "0xd7fd28964beb50a410de115f290a0140fbc78d0d4c47ba02afe9f47d80df5e53", - "0x1996f9b8f7efa3d860233141774f46c00b63f207219c2eac5e61318a9464d7a7", - "0x4cfccfc8a9623fc9b5ab38c991999f8a52c6537d984ecbb0c490ff37559cc51b", - "0x4aeec05c7d47781008273281bd01b36c5ce21916cf7fc9dbf79aa2c477923c1f", - "0x96cb38c2449f97c92c81f5f4317b15267c9c2f4ab62240885280ced5f54bbc53", - "0xab5e6b9e4c4a27aa725183699ae30a6f8bc8148bfe85d1e4d0bb8565502e6e73", - "0x23fcdee9cd75706e640aa55c4e8acccd7ba9591e724bf351a7070104cdc698bf", - "0x5f3433fb2aff1e3843785b2f2cba22811cac84061803a2c5fd045b1983c8ba81", - "0x5613366fa3c323b091a44d10ffdfb1f485160fb45094cbd52707484f820cdae5", - "0xfc43bffcd36963706e2bac564dea4d593c35c5156de02eb747bc557988d628cb", - "0x4f23fab93388bc79a879f85c3efe0f15606828e6034cdc6b66810a90cdacb793", - "0x62ebc41d0592d1569001358ff3f1641c0a6ad89e66b0fd1e784f060c9101dee3", - "0x12bc43cf16c8965f106f3749bb6b5d9ff3936a205d2ef5719ee637308f81ea24", - "0xe991829e5d9c26cd365f6907de1fe2f2153cd88a7d7e8e56224c9dacce442fa8", - "0x55679ce947be929966abf9953031fd172480b7da8be0b34344512406d93764ac", - "0xae1f80cf386c6cbdd1ee0a1241e738c245eb12665f2fd1a614391ba54c741847", - "0xad12a941df30e25584f19f5aa65069d919a2e0dcbcce73de8724f975abdceef9", - "0xb3478980a68ed409b58d35759d4648ce21ed97758a1eaa0d0f1ce6b059b6da41", - "0xbec5fc3e9184d2e46bf0ae39c408a7cc243a41b69a923fb2d1224638153b3ce0", - "0xec95c1df965aef7e6061a770e0c82b6dab0027dccca5ea3791ed207b868098ed", - "0x2bd64bac83283cbb0bb5f7c1d461261a089a40904574e2daaee97110425092cd", - "0xeed53a58f04a07af388b098bb8c1e49abab852736c84d69bcbb78b945f23b86e", - "0x2c2c81b8a0c362105ccd0251119ddb406b65562611b2c3b3fb9825dba0cf6ac7", - "0x91c93ff5548a0462f1cc7cdf0a5c57dfc1f21932dee38e25759c9e5719036fbe", - "0xbb1c1bea058893b30e38cf0ea2f53b943fd6bcafb86b0eb647ccd152aa42dad8", - "0x079da567e28f61687250a49aa9fca4c465245cb0ce02ff075b500b203c459605", - "0x691de81b04aab67b3276168be7dc43e220114005a4d66a1ce9ef2665cd2bc867", - "0x3deccfb1fdf3e57aa2d63d0692cb37f375aa457da52d88cc9cf277c32021bf45", - "0x6cd1a84bb6976f2599898aa46fc67627663738cdeeb815ef1f45762cbb14dae5", - "0x29c96ea5d11a5a761ff7c64b24a0892daeed506b8f7ab33e93d6eb6023b9b37c", - "0x0bd8890f06cc6187a1d1a029cb319df75018b067bf796591b0b0a07a2c33c008", - "0x6a3d0b949241a493d2eab974adb173daee1e3c791c2dce6bfe59b127f8557ba8", - "0xd102ccc0476081c8d399083930dfa59486a51f83d33096ce77897b9b0fe41b1b", - "0xf9ddd66ee324bc7f06544cd53f792dba6697c1ecfd4c40a44733944f7e7ebc91", - "0xc14b245a2f2d02ecb944acb218d76e311659cfabcf72f9eb49db59541550b6d9", - "0x190781209df0cb64a4ed6ceb15428699e6b67f3cff28b7a987988a8e29829171", - "0x46605aba1cbee0bfdf90b57e320823ad4607d9ea1d6698005820021128c472b0", - "0xfac04645717514b9ec56f85cf0e6cae5aab46e25e69e3cd010513d9598d56609", - "0xfd0c9f959513a72149bfa23b32c5006ea766ba2e41edd47f450090568d44cef6", - "0xf0560a14936e66cced54d4002599808b127f9e56260f04f144fe9da412577c61", - "0xc9448c9e8a3b09fd51ee93b77feecb8a8ba03e1781a3f5bccc93b4d1815232fd", - "0x3c41d49f2aa53e90f5fed6f372c620fff5a80f442894c71bd7b6c2f55ed76f47", - "0x0d9517fddbd945294c72ed49bf6bcbd88d84c9b3b2e257c05f64bb69c4d7815f", - "0x1a67fe64cb906429f47250e1a30858366e0204c3f39b7d4b3f121a46a51566a4", - "0x9833e385110465d72ab133f02348cb33433f38454f6e985b022720a9836e5015", - "0x89f653efbe7bdc469e2896aedb2a22ce5982b919a7c6dd14046db4eee3950b6d", - "0xe92427a7878630f65cd4f38dcee4c5d87ea0dc3d0ba1720c0d9f53cd84411b7f", - "0x1f692c127e4d0e37f55f24f5c20516c804dad968b5dfcfa000595b4682aabab6", - "0xa3557041302da75734accb2e95db335abb208626e1b9d20a54ec0e0d23a5e003", - "0x96bfe30b54d1f39b5693272ec7cea523e0531cb7fb805e69a820888ea747b3ba", - "0xca8f5d8fdd17be423ad5bf84d7f591466137a54388b65f5d853aa83ef25e9b42", - "0xafd729d01c437b299b29da02e34eef0b8a762a24447b96c0499231a17a84fe7b", - "0xf95f528a8abcdc0eeb66250fc3d1df11d0831a08b30d11f0f6f0f96b02f7ef82", - "0x483de90f340fd0035676305359fb8c83b709374676796c86c4d4b615ac779b5e", - "0x433f6ce08d28351388ef6310847d7949f4e92ec99d585c3cbb5fd7152baa507f", - "0x77dc56a6b802ffae812c3e4d8768fc3d80bf96b95a55b1b9736a9073ed0093ab", - "0x5c3c2bfb8cda1218a08084c4645e2a84fde3aad03ce00c3310b3ae5d80e25be2", - "0x3321f6475c8ed111a1373bdeb68609f52c4740f22e4f13952e8a6d98fbfae542", - "0x7a0c172b717acba8a7b874e6c88257cfe68d41855506ee909d84d23b028a7ffb", - "0x7ae3757e9cb71b9063d79db76109af96ff1a5e6aa1183b1b70150757243a94e1", - "0x4ce794f4c0c9273c14e6faa680e8837247c6a0ab503a82432f69529d4f5530b0", - "0x8a0ff3d87111ac19f2d95d813e384a96dae3cb15caf12e50b99816393db3a374", - "0x35adc4577b9532f5e821d6a39029cb898886ec9a8b938bde92ccb1df67d2dc2c", - "0x84976050b7ccc3c953976162a6406d125edae798bc1bf850b8867b3242076a30", - "0xa64d9c9681b1192185c9570a6512d8b21483534bf0ca7f457acc96f91070358d", - "0xb27f0ed2428dde6c5506770651e3cab0fc62b71be0f245a8f6a84c326ebdf327", - "0x4f71b19b200ccaf3d359eee3ad5d7c155616b86495a43e2a7c18d0740304aa2a", - "0xf22988f8c5443f4400af9895f655a84b31e871a68e74a396ea5334297c0fa190", - "0x37dbed2e2f7408ce439ab0ab09af040972021ecd606f517af45c195a80e3f3f6", - "0x5165c41b008baa5fdd010a9a17f0c2fae2a0f16b4d230c21bdcfc737e9279c23", - "0x96ebfdbfbc02fbf12acdf622ad7ffb8dc10299ee66c4ca956eacebc6073fad75", - "0x47ff8c523b69bd123ebc4ee8a81240f2bc1c4e8dafcf9b9cb5db5b7a8f4c884c", - "0xcc3a09c1108d216199d7b3412f86d73d4fa7585c393dd3ed95ef0320f0f9bba6", - "0xa79133fa77c790f4f12a9bebaf21531c831c72db93e4a36a8f2ea672260d74a6", - "0xfaaa6544245fbf22f3016befbd4acf93135e64f69a7aeab07599af3a52f6323a", - "0xf489992872d1dddbba3c6b2fee1eed0f604f02e7350d747398944bcf4fac3171", - "0xb49c4387382e339ed00cba9469275920e116ea556bbefeecfc9c154d2f1c1f77", - "0xfff493a58d86d6160d9bdaf60504b07d6f33412fee522af09c1a7de8ec9fcb05", - "0x584d1e0a2569cff0fc1b71c850eee0c1f680037cbb678bbaff035c49ea5a6275", - "0x91682c3606944042c10a5fed5e6b5662c39cf980471b8bc603042cf0d455842c", - "0xb979f301dc2bed6c7e60bafb112b92b22b5f173f1d9e8fd665415bace1a580e0", - "0x71d439075aede2a781f1a872dcb8c89846056f5317be5df5dbf47904d106938b", - "0x9c2353de49861c4d10309f99457e6f6d9e5b7a57360837aaf0a1942c0440fe9a", - "0xc2c08c3e2d8741a4b4bb180fee1b1d0cb54f87f2739aa3881d534a9c73fd2d1d", - "0xcd75923a659ed6e79c9a1e22201374bada6bb2e030e3664763dda54552bddb15", - "0x60749dd09da7517a03cf6baa9b4623198f4209f1274c00bfb234c61d467da12d", - "0x11b1893d89913c9c57f32ab875ed403fd9802475e3c861472dab770d4882192c", - "0x26ddfbfde82876e17d6872f31aca7f233f790b3ec0618d6b5c51871c9700039f", - "0x8c9ceba1e4cc81ee6103acda0bceebf5b0413023966dd5c3c5641453c96c29b4", - "0xf30e9e6f8456fb427f96f356eac399fbee64dee3ec4a2806701c3484608c0edb", - "0x7773f80e3cadcae69cabda956228cac8cf774bfa84e2ce92ab2872ed19946858", - "0x4d510670cd4036f534fda9c7b4ddc14aba28ac985b90a0bc831483621d44996b", - "0x4e7d6a0fef6ac54f4f0336961561398600d0c6a04862f613ed6eb648d45bd348", - "0x55c0bb04ee6e1aa15ae55e1de229ab5a4b553864c43cf527f2ee42d441bb0bd6", - "0x92a9c4000171a6534d7546749d7392a9a18974be969bd773b4ff3f5c39c91a17", - "0x892daebee1b6c833ad425d5606ac7562c135688ba8e83178c0f8438679e84e7f", - "0xfb1438e6e00a88a26dc46bdb1ff1696cfac94bebd02f16891c08b52de7accd84", - "0x46404590a94fdd59cdd4ce607c1ba016a78be8bfd8dac361b8cccf6191aa0bdb", - "0xc1a63495f12681da588396c2d30096cdc8c52fafbc0bb0d6f11de5ee2c5a7c51", - "0x86ce595e0805026d106024d9d13e0fca7b208155901b67cb0011138b31bd947d", - "0x7bd81277da5da97359e67e2d5c12cb238daa009f4220166e13ba1f067125888e", - "0x2dbf4b23c2178c8dea6066d43b5f1285894d9e74a8b1056d0ee02091b84dee50", - "0xe983e1211c5b12ccb547dffe850e4adff01eadbb6717adb150248900332482ae", - "0x2f42bd119e06fde6cf7e95c59caeb189afcec85ee9968196f7a15917f98962c9", - "0x1e54e7e17e28b7c2de1c8391684a938e1adc2011fdf8f4a6da900f3664f2f25a", - "0x657e31fe41599356929abc24f7d751e49aa769ef9a4b9d5b37e6bf019dd567a7", - "0x33de4f2776dcd8b17b846816e02c4dd1694891b6827f87a80b31ed37ac0c6048", - "0x2b3b692269fd869b7797c78de9a521705aa223d78652dfbb32c1b8b612bc4351", - "0xa57d205b64565acb103e962a83d300a302efdcd96a13f4c79e80e2733341a70c", - "0xbcdcd2d22c7e21d24e48d28c43d09186a15568feaa24f44f126e5bb96ffbde99", - "0x3a6de68513eb363878604d054f46224e8567f2654cbcc0d9f81fd37fda804a65", - "0xb762285a5d8172a6df584ed15e954f432ddd8f41ca342db7d1d14c54af21e420", - "0x97702988d087056b85f9046bb8d3e37365f4e5811507c1e2daf544af76f1825b", - "0x8d1dbce8efa6eeb4269e22b45c2a9e7c53f7ecafb83e8219b14d43f66b6fa68b", - "0x54a2b87cc430e350eba1271a9161fd08157e196a4d4a7ea29a7461dfab6370a1", - "0x253d68032f383891fbf9c1898fe7d41c6362ebf759b3e3216bc14bad63250181", - "0x175102f1ccd3712675505c3d74609f2c46053c2501998a91b69b2eac9e1e476b", - "0xb07dfe916a1c61b07571327de80a478aeccd3f6f26aa4ecd7081a5f16556e946", - "0x0c87d079935642a1d3673d9b41ed2d3da33ef124acc081e236b23c50d78ac968", - "0x0f3718936f3be6ac78445fe56dc92e4806032d67e014cbe0a92e3193999c8acf", - "0x88bc8add9c916ec21c2f83e398bee626533eb038900c787eed34cdc1ef273a2c", - "0xccd4eebce003d0415c0ecf2d40916651b5f336e5ac64652b69a2ef653d777995", - "0xb9bbc12a4b92aad64875c00d7536c7dee7d62b87813b99a9a77bdc066c169a89", - "0x9ec43027a7229ab550391eb6dfd0cd5cc9b3b103eeb0b79dc9d2f4d8ce3c0aa3", - "0xb4e8c06e7424fdeee2f241365828d9a407f117a0811cced5ac35dbe9abe70a81", - "0xa5a9149f1002457f7c9c3dc2a4aebf7d5453f20c1d7e3a3a732ae92b04b8c00a", - "0x28d20d654b1e7a024956a6c8f53bd1c097d1df3a6de91036092c88c8070474f4", - "0xa11eebe47ce50f4076209307fd58c75b0ff725469ea0875c658119f7abc644de", - "0xfff2020661ecfbb50cd6263686f903127b9d1e5f43dd8bd2d5ecb09eccf6090c", - "0x253576932466ee619cbd3f012915380f1e7a79972864fa0e98a755f902f1199c", - "0x554258855ef348fa770e3af434da722350e998e930f234b5b8c33d3d2f806595", - "0xad11b76ad542ca1e825b251a6cf410b6f86c8c4b42b3c8690a2871e7cfb27882", - "0x3c3d7cddca90448af6119481e08b388df75389ac3c7dcc26ac532680b23ce2a3", - "0x94c4ede45ad5b27c5a6b051fca3b055d548e4c29d2541a9cb4c37bcfff4f19a2", - "0xabf8f7c1b9828d61e3aa72ded1ceeafd6ad539b79b5d56c5eb93c3327b9d83ee", - "0x6e2aa49e5942f19f19181ae911e7951534aec1a7fe295bbf921d8f28a4b7c0aa", - "0x0f49df5c80ca4ff33623bcab40e9ded6159d3f167f7754c3dfaa2eea0ccc368f", - "0x070b14922cfbdf78ead68019e520a08b987ad203db005f4085cd25e6ab587016", - "0x8501cdc568d6c64cef9b87e639d5a0e3f5a4b7d77e8510df07bf0fc50d89d020", - "0xacb70d4cd2010d149f382dbc0f7fc1b74f0fa6bfb5e770017fad960aeb7c80e8", - "0xf49878d3473b21f4ff2c331fe48074ba47ff78198f839dfc7d1d69abab81e499", - "0x9a11ca4ae2a7a68bfdbf54d7f0978150176aa4012ccb8d1781c10b58e01b851c", - "0x58729c03b10b2ec8b4abd91b876ab57a2c0bf05ff02218bae9765a90037562b3", - "0x3b5b033d76798ad041842b9ecbe223dbcfc2d5babdbe864480126dd932d29fa5", - "0xe08ce9149dae8cbc8968dbabbd97624f917148f26f45b858e49c2c389bb22f20", - "0x6b532db41ccc7cdd970d5cb0166dee2272346f23e0acfd8d751491d0c3ecaec0", - "0x99fbdf39928b3f855b2390bb845a96478221330b7c350e83e7b4dbf7a35e2685", - "0xd2a2a108c0a4dfe5ba0d0488da0de7f6d15994f6c14b511712dff4af33bbcf8d", - "0x75ddbc2ad15fc25c2ae71750f1d5bde5e825b40d29d631a27d8801590d5e1321", - "0x226995d806f5d03722266554109395921f63ba1f66f5ed1ece7341bd860a635f", - "0xf78d4178efcc128e86b2e1df767e59c76a4bc9db0ea294bec79b08448133568c", - "0x160e1fa8fde835d6e91187a3da5caee245d4d0b9ee30894170617b680d275166", - "0x361d71c972953283f8ddbab4a45a01b39f30290dc1dbf1b6b744af5afb2e70d0", - "0xb8394cc0d42a0b60ec40984f753b8eb5b9175022bc2ca7f80d64d26106ce86b5", - "0xa0f6ff5ee727a3106e045468b5ed9ee8ef387eb9a0355f2249a71441819b7bd8", - "0xf441bc00d94f06485368e043f933ec9f8e181b11ab0212d728d83e2d039edfb6", - "0x1da12d2213cd65f5fce71c06e44b40a487d5e676558e6f617372244f8e98d549", - "0x7d988011d5c7da97b37c6a6936aa499a70aee8b38f8ed60959f576c108c54d1a", - "0x3d5ff693e4ec163fecbfffc95024cdddc4d2c561297124bb69c8d6aad4ae85b9", - "0xf7db67de101d00409e1ce3430445fe3d38a073e1c81e1453d63f4228a51615ce", - "0x335a94506606e70ca259348ae7157066b55faf09a4ac11bcc34a0b865f4a8057", - "0x137d0de6cfa1e475f6d94248c1e30bc10c17b98d40829974ed4ccfd7de0ffec4", - "0x1d63a7c1c54bc740875f3b5d8750aa6bc85d1c10e3caa4c9d67b4b010a83c29a", - "0x04e0208220bd1218b6180dcdfada95b3a8391bb380b0b0ad5a1a8a02f1a3687b", - "0x02a3dbb2d221bb8e8be58e12982445246928b9965caee73383e54edadc017315", - "0x549dfa1efb8c32071dac220aa4c76ccc038cde6ec9a39e2745a2d55e92bc0890", - "0x1f9bec0a6b3415194528a23443c9904f578d57d7024ceb964ab09a9e8e3d9c03", - "0xbfb25a68c40fcc0dd996e992b18dc73a48525a323995a0eee03a0a04c572b68f", - "0x7244057fe32846105479bf704b871bd1a081acefc57b37aea76116442805a70d", - "0xe6a48159dc041cc9a0d8eb124aebcffad394a400724410e956f8f46e1d89e8b7", - "0x812d9147b6661f54af0c39c1f4de79588ad9cb62b25befd4334ef5f3ebbc2cff", - "0xf9aa945fe2eb3d1e2a01db68d4502a4f33afcfa967fc06944e035c582dbf383f", - "0x63a437b920c6b916eed44003b8d35872acbd0d1859b6596b4f45e39d04057e0c", - "0xffbaa46c582530498f30e51d18deb6973824b850d09bf4a4535699890a548eca", - "0x6ab7423011299d3a82d2037cfac752bc0e88a82696f6aead69ae940bf202792a", - "0x2fc6f5dbcccf16340f2b3a5e341e364c3cb1dbb3e899c7acd6e50e7ab333f45a", - "0xd354b48b2d3cf13487ac8138caca0740408b42613b50f056ea595741ba275d7c", - "0x8e1cbf4bb7a83b1e47f69c08f3fbb133bd36d8fbce923db79a248c21e9657f8e", - "0x414c7f4d86ecf78c3988723ab584ff735633a37267b1f5986d0c2e6375f878b8", - "0xe0b0dc519fccf20ef45b2b185d791575d88f93011bcf5205f4880f7ed755a54d", - "0x528a7ac7c6a17775d207a983b76ff54f8fbb84b8ceb3f6fbb60a80d9f306c497", - "0x458bfe441e32d15fd95a33de1665a94ff4f2a4c357f4fdef5c9e7c82013f59be", - "0xd478a55408477b3559d50bcc4b5c4cc2713e0666f40d87c5faa7328c5b7db489", - "0x9977a36db4a48e7e1f4029efc494a341b8eb2e40c73fa9ed3be2f16065df8900", - "0xe2cb22eaad423da374bbe1cfb8f4f353eab0f9874084aac4f42578525e26586c", - "0x0225069114efef02e188a1a35320d6522c115706c7b87b3b54805aeeb9e60db7", - "0xf82e17b405c2e19957b63706c0d7405e09be93b129ecf850e1941d62f1c309eb", - "0xe26f6f439b0d33f00a973b12f5d3c619bc4cae8662ba25173fa3398b8fa19d02", - "0xeec38666a51fc161736b7b48c8befc98cf9683975abb8432cf735edb907b05d7", - "0x8d8c5ec1f83108372f361b0679a65bc72f1b5055a3b9a12f01855a61437c9ebf", - "0xb53f85551f811c60b17b48df2125648aacef9a74af403cbd877c1e539c045ede", - "0x498d608f0ebf85809b9f5a9270e481d03f4e26cfa38600e50347b968cc05b13c", - "0x8e6d9c2693bb876101c2c0ced0976fe71db31b0a725fcccc88cf9a69b47cc9bd", - "0x5ac71d4859c028d90da98406f2c739ce6280cc227529ef8f1be3d3547ef84da0", - "0x5e72e33c2d66fb9b31bfd4d423aa1ae7d4aa88a30758e29610eaaee778ad75ef", - "0x97345508a573bf5036243f384e1584dea6a723ae342a876b572526cf992f956e", - "0xccb2773ad04c605e9b4a6dede3cc41d69f43439f19e4cd97f54de3a28052d0e6", - "0x0d48cd6317b72fcdd315a6f912646831ed52257ebeb03fa70576a918282e43ae", - "0x688754d57dc88205d6223c88e802461852c5f930252bf74e08b73a79e4f0ebd9", - "0x2860c5f26b4eb0896b4ce041582a8d38b1c8aa21b6c163784277df2658d7e191", - "0x35257fb9aa6c9e8ac16068786fda9c06ece1cb2219106da3216ef4648d28be29", - "0x1b81179c25932e14098aad725e59bcb8da8867dc4a5d145e90103beff868f2ca", - "0x60309b91add03a291fce9fc3f8804eeb993508d7cd5ec4c13a41d31b71d09997", - "0x04d2e5e78c8d8c0059d829c16ea7397834cff5914a5c0618e5904a9037338deb", - "0xd6d345d35827336d086eeb801e331fe77491d47f0df668234e05c3755e413e13", - "0x7f29ddcbfc971fa8ec8e73ed1e77301310e99f677c17320a62fa32ca225e3e97", - "0x036d5fdecca68a54be820fd086e1185312bc9d434b332db547d65d9fd0adc5d8", - "0x07d7ebf143ec977f4134d782bbb3da778b1add224fc3617987d9b21c7bf7d09c", - "0xf27fcee747ad3f1acfee97988403537b85f51ac1157aba723ace11945bcb5115", - "0x5cb6c6cf872823930abb5798d4f1db9d0ae200d21ca3716445fcfe675432cb68", - "0x8226cce1ff6639ac646ec09fd71a0de3d6ffd323df6ea24446dbae7eba66a69c", - "0x6e07367cd7df8d5e756e9e4c535f0933df76941640a7bee501f8af8b653543b0", - "0xa60f86078624b3c727784d4a95bd6dee72210c801e7cd0a4fb01a2ef5f7e24e5", - "0xe3ff30fa3e754fec8605305377cf6a3842d2cb323950db58e5bf02794135bb2a", - "0x74203c76eba1b90e3e76fd493f66fe46130e13359547ce4c94919e9c8909ddec", - "0x4ad657902a564df2f15d7fcad6e4171c96c65798d8b49d3976c7d77869c06ab9", - "0x043cf965b0b5b2dae096d8c73190ad9b834b665097e6b77212359c5029fe8f1a", - "0xb370bc64167f8c6be57238f35a56b840688138e860b08c2c8694401ca32d22ad", - "0x1c242203268f4d543b7087b0b4506f4b5e5e6fa4f4b495c6bd15f94c7ccad31b", - "0x1250c4ef1fd54888a4e377127c9f27889fc5bae3455010733e8f48d7aef65c99", - "0x83bd5c6109c0937f109307dfae9b347fe7da69fceac9444da2d6394d26911956", - "0xbc25fde0522d9ded3c69f9ce8233145b5b15242a8f5d6d6feb2b7aaf0aec5cc8", - "0x0b7fd860e34f21a54293ba2dbd61b28fe5ea3399a29ff7af4f577e681ed579c5", - "0xb2d96f1f4fd0f26e226386aa94e306d8a4a2a3be2860951e0c2e39c77dc31bb8", - "0x4b9794e3a3fa08b8a71f55afd71f2c5ba34fc9d773c32242246c721c87e4b14d", - "0x01d6ab151bd597e2681e9c67a0c3f445c85bfca4f8cd73e3d088f49dd669ed2c", - "0x686280902ed294c99c49b8c2366686af45b9c6ad0e7bfdb1c52f1c901a6be2b6", - "0xe0a05a29c78f04794b3957ccb343be88cf11b00f5d2454a6003e2b363f88d5aa", - "0x798aead8eccab3a4d4dc1283cda04057038bbf80944a87c9e796fa7565e34ad8", - "0x866ba6664beb3b953f5b5524190685eacdd1830248bcb5b0cdcf686101be0ab0", - "0xf31b7929f0cd73d652d1ded75d5ac19d95720b386fdc660e0829d122e66a576f", - "0x20fb041619114e9beb6d70ed0a488a1ed8aa149ab506e7bf62244934441fe0a2", - "0x91f9dfc54ea16eea1700cf1d729e77408d037f43238545136f6cd9c72bb2d260", - "0x7f633029a0d96a3e4232ca3b02189b59b10e6edeafa9756f8db1e8844e9cba44", - "0x0b4f8a2e3547fb13fb7ee5555281ee325d068a76a67ddb1dcef60a6ae4b41597", - "0x35b3e9e377449e7b1a023573123f5d132b50a829091183d279590edc73df67a3", - "0x8ac0d74f10c0f6a60d2e5729d150ffef0467934f8c3e0b37eb7112eaff246a60", - "0xa8f65245b7fea166560a0d775db32688a0249ae3df937a42847f4f0f358af085", - "0x6d5812337f22e0e436ee2dee8bd3a45b237df7e045a4bee1458c55a1a269cfbb", - "0x6f79965282b544af683f2cd74cb056c092d5aeadea1d16e34382cd97113f4436", - "0xf91f555495a762ed98f946487f9aac89b4485c71fde9d700cf562164773b119f", - "0x92397cb86fcd4bdfd448fce9e3fd6fcda60586482217ab74884182d91f3a33e2", - "0x3f694bc4515ce3389bb57feb21ffeb3078e67c3a13bb53a345f7d837d92e91d4", - "0x81694d138eb5afbb7137ed7de9e4fd7010528da30014d06feded8cb2db307994", - "0x56d59ea9f640664f6a1ea3d92860f15ac38ec313d520b24b0ff4e365dff53b4b", - "0x5056000d0e11cf10bf36c57b44eff8fe9560e261264ba2b29e97323c3fdda5b6", - "0x427230af29e403291fed2784ea8852a68d4bb0c07a514dba4797644c8150ddad", - "0x21d5f70bde1ffe09c38545103cb9e6a349e62c1316f1e7e78c2f93e577a6dad1", - "0x385df64391152fe6e1f574efc1be371bf46606ba571420c601877dacfc3d1458", - "0x4076eeac8e1b22684b6fd996a11db49ef6b52539e481c1dbee3f1ad3f59d4872", - "0xa8239c7d2d32a3ef6f887799bef8b470dc52d0c82af8de31bf3a8e4a6bdf3535", - "0x52a6ab7c89019f8fed6761845f9071bed1748aa461b87b0a587b7f59489b2dbd", - "0xbf80c8df3c0202192d1b888f80668c5ebb9d4f8c7d168570afc54276af4de578", - "0xddb5784df235570dcbb1a08fd293d58f63b7afac494b1f8f947677f2dd36a87d", - "0xa741791df39f377bec8cede1f1c491eae35a710de095b5ca5c56d77f4431e87b", - "0xb119d39b47e1cdffe5a4f912d7bb19c6e7776803a386ef62226f08ac1fa0f4ad", - "0xf71e68699da769cf06d77c0432ecff635cedbed284256336c682d542e33c96ac", - "0xb600381fb3bbfa7f7c3b555c51999446b148a1507bd6279752e7059e2e0052a9", - "0xa596f51a92c38e63a7172461962834dd700bc847b1e29c03e27b8e49cca19b4a", - "0xee28f2ef5162f0dedb5de1c4b38a1049e98c82a34570b8bfa19735e915909579", - "0x1672d010862647d074aeddc4189a5bc3a128b8084eba6d011046b11ad23f445b", - "0xc99102d4079edccd947367846ae35e2b40ab3f0e1aae8d68885b1753c2c64177", - "0xcd1003ed721269cc94a0736f70714394c191e7a663191268e2daadeeae2790f9", - "0xd33a0f2338d2d95eff701d0bd287e70e0093c6954455bbc63be7de7c39f6d9c1", - "0x97fb57fab06fee889ca8db09d5dad12007024c3dc59f867170d65540d64808e5", - "0xc4a6963627634d4baf5697343904c4c803b2f875f05260602d9ecd45e0e34d4b", - "0xdf3bafd9380eda31fa5f606fa915253ee8b9a41cdbc0ad12e2dc1cbdb76ccf88", - "0x141dc873ceca100df1127451dbd96e117ffefd054076327b68b1c76a4651cb92", - "0xebcfc5da5a1e1882637b9714fd899d59fb957dc9e4afdaac199264e8d29fe5a3", - "0x087504ed686f8e598560ba1f70dbfc72eeefb7cbee9559927a98bbb31b7bb9fd", - "0x767d65e905169fc44546a7c7828c2f1b76898d93df663291db08a0a6f0fd8610", - "0x0ce5170b5c9ec6377c3ef465d986159f12e65c32ad577d8005fba498fc6e5d9f", - "0x76a78e8c3799f93f1359313a6b42f3a4bf92a9c52c7b495b8d82946d471ca965", - "0x1b5006aceffd1d61936cb327b7420f3a88ecc4ed8808db9f245cabdcf3da6fe0", - "0x5588ef96fb1e960284211b143bb5b5a1e431a015b5ae8aad1a86ba1b49fad459", - "0xce5cf0c647eadd6de376cfa52e4a3008bdc0ea3e09beef3be5e2369978bdadf2", - "0x2e55c31e3dec040cbf0f2bd0816bcf6d41ea29376b9958a40bd799c7c788d8d5", - "0x871c62991b05eaece3ac9fd843c62f6a635230d885b075df28f3421c64fafa55", - "0x36004f4df1cd2809aa89c4edfe3ce3df7a70c1cea1b99d906a64491f002b0374", - "0x91ce82776a39a4a32979f0df16d17c4ca0544e7086c90562c66868b588058106", - "0x23d8c25e9c94779c445bc7aa84ed86189617463e3a9e62dd974a23a29eada969", - "0x249e41cd4fd36f6bd6860286bd29945bb98cf66c0348f73b26bb4a28c3103a52", - "0xf158ef6bbe5a54a85f11c58e5c569dc927089ebb641c9d0f6a4641770e551cca", - "0x4e30a215bc185573c02cddf66c40a1c829b86dc639c2eca7e6a4026fd8a27933", - "0x15f0ad2cf92788cf0905aa6f8d092b1350e3f1530f1b06e07ffd8f37d9d867ee", - "0xe7d081057d6d3f5d85be6e28cff22e4f4e2c842d31599885f4e3f1c96049c101", - "0x342f966910c67104f934395a34ffddb97260920bd9043e2f1aace5062520ceee", - "0xd09b337b1ec7e7a3188b2a8981d14dfb89bed807807a6fc40a21ce82a3814b63", - "0x5bfcb62ce51ed31f94636e5e55a4fbbb8d63342818814d9167d690f96ebd9153", - "0x9f46f879a2b469812d61674250053eb01ff91b3d61b66ccffcf8b0566bb0ab0a", - "0x9e7c1698af64e08be4d5d2bc9262a7e0d2ddbab9b5c9499b505245869a1e791e", - "0xddc3dde6d397fdc437fc95bdc98b1bac052a1b4fb1bd81b90d18d63093bc0efc", - "0xb8180ff01aa196209573375a70c590e9ca8fb5eb94c921436c534fa57b93c802", - "0x795c03cb817afda30f993770ee0718774aaec6d0c9c01cb91ab2061539ab7349", - "0x45cbd728f2329f5bca7f7d188e64b9b307eabbd816365d0352e2ea661d923511", - "0xbc1e8acc0b620fe7c44f4d661950981e8388e3f8bbd58546157a1dc716d43414", - "0x4e796ee801061efacf4d84372f6daab14a98c3e91b569a0d580217442e1b821f", - "0xda432dc7a649547983d6ccba6ad31b7cf97ba3c7eebb5eab05f34561759e6090", - "0xd51b0632c736b3403bcc07399e9be4e40e04928ee4e63e87710002b066e462ff", - "0xa3076a6242b4e2e450f227294940d5aaef5b62ad68d63affc390f874958c0c0a", - "0xeed1d4e115039d92f2d82401aa631248a9c5b8de06bcf784adb918ff28d70bd2", - "0x6267b83c5d6acf41cbe965d1bbb8871228a060e46435a29c6a7ade18e44f3ce7", - "0xbe79880b453639b576dfc891353dcabbff3d16479ceb74ad2184a1dc89daadeb", - "0xd5356e5ff86c455718ddc8527750b69b3658c9746c0e47ac93291b039bc2db8a", - "0x079545539cfb2ab2b1d5341de50c6c71b8725cbf3a6ef168869d392c2e6f6ad5", - "0x0e3aaea1d36e3945580c3cd8e26456260a19ec0630476dc3ed444c517baaf5dd", - "0x28d6d2c1e9e4080e0bfdfa907f6d7c9f533cc477a729eb83ffd0f96f6d6f5018", - "0xebb8535debdfb417acf2c13e3ce6107d7e286cc573ec1c06ea28ef4cf3729c8c", - "0x96440ac46648fcf69de403e4c146dd1a37eecea957370f223753ec93f32298e3", - "0xca28ae763d5602c91d437349aebdb5cf9dbc89e6cfc53c124370b67e31fe8f9f", - "0xe27ba10642ff994e47ab1fc5a29ce071d80b82d6f584eaa56ac1a62a771eb21a", - "0x34e7db6c5ad2d646e6791b58ec4d429b8bfd69f76b1d9bf71f860b1faa4911ad", - "0x0b83663dee1daa173203e106ee4996ef147a9940f2318b912364bcfefcb07271", - "0xb164c1d879ad665dafe81929e0db557cf2c573f188cd86fe35ef24271cfd9b26", - "0x6587efc2bfa2ebd6da9816538e3abc5f03c75b5bc425f7e5e9c676f97b1449ff", - "0x668316bbac3e4cfd0c4fee48957d9c8fda962bc556c5ca35246b88b6c43fe291", - "0x264917f66c2d927b7f632744f9dd7b844514af80bf3729f30832b222410a08aa", - "0x38b162eecc129bb2e1c82e65d785e84b5a4a9822bf1e6506492a25b082309bf6", - "0xeaa3ee39cfaed2aca6c1560608bc1a2ae32805812d792471c9c6a9ebc80aabe2", - "0xd7738ea58e4319f98250874c16bc4ba12f2362a8f09a7158ca4e2ecacd954d99", - "0xd26d6e2dd1b62f7086673cf153c3249b15c652a7bd43a4ef7b796dcc2bc44a96", - "0x11a9a4f1f8d10712e79a219eac2fc81b3360c60aaa9bf179e720ec04d69956ec", - "0x6ea3422e40bbd52abd6660d9ee8ff351ccc61d997082e470197a474509ae1383", - "0x1ab854452eb96365cc3ab33c6c45b296864ee61b3a477716dd9eb715529787b6", - "0xeecb8949e3be53db9ab660c71d1bf702d8e919ae6487812181858992e7668c8b", - "0x2a2ee4c33009656e5e38e44a7314c40254639bd46e32cb09eb1b9643d92f9ffb", - "0xa14c5b1993fb968f7e1dfe4ad24716e8d9a5be662cb0af13f43e174e0bc2c5b6", - "0xd5603250b1933ef1cd1b27932627f27599f81ca29c9f02e3627eb621f613c935", - "0xcca950815341af147085a53b4108ce368db1a634f204ad75f3c687b5b7ea3985", - "0xe14e1ce237fe627e8047d5d8f8153157ff1ff3d2650e0e2ba870ece940421470", - "0xfe2bd09d81d1525b996c67fb8407636e06723b4327803a106c19ee60a4a960ce", - "0x995cd4b1f16450b64098fda2e678b14833a3b382f76e14cea71a0d297ade2080", - "0xefbf5dfa769a1e6132741600fa9459b8a1d11b11c3b2563b22c79cddb34b2ff3", - "0x1618c918d173a9af0daf3af314d576a6419456636a46e2c6b8126f2365d627ec", - "0x3b9a773b1c0e16037a429ab42fa3883803f320beb9a34c8e36c80b2b7b814331", - "0xbe3b816b72cc63cdb63e2897064965c1b4e9cbca1ae26c770a20508be1d9695a", - "0xc57f2c5ff8087d6cc2039910f00323a90a0bdc3d2164e0f52bbfa920d62a15a0", - "0x0f11a1d5bf509afb2694bc08584718bb93288355a7bf0f801e478eeb9ef52da2", - "0x829bcd6a2392c957300253f7e9abcba7513908d3013a74387e918dc57053c035", - "0xae4d9d51eabed53e9ba4dcfecfc449bc165b3bd40d87830fe044811f4056580d", - "0x2a8a21d59547009a533695b5808d928992547937478ff9a61fa4388e9afc1147", - "0xcca2ff6985f20f7d1633c35447f45a5b27fd5a93802688e2afe494a2c350917f", - "0x2b8a2080fdcdafddc5d94292ab4fde01d91e7ad2c1b5b5a2f04dca9a729c3778", - "0x211074e33dcaf13183115f89252435b4efd8e1f01c38ed484ca69c14bac72eaf", - "0x5de84a9ddb48a4e2134be8cdd5c274471055234d428daaa1039ae2500e59ebd3", - "0x3f71bb8768f5c8d0db2ec8360278279f4382f3a50a939842d80be98685c536df", - "0xd26b7cf7c6f987f4975010eb9e77becf7c0c93ace9eb53d289beab868ab5c93b", - "0x889d8c294f942cc322a197b4f746c688a15e1692cad0d2d9fa9a125f4998a6d8", - "0x7a0c807c41e209d12128445df83dc1931d5a24923b38d259a7bb9e8e4459db43", - "0x564e904caabec0630b52e056ef9ac08a2fd276ec99d81644bf38dc0b09eaf820", - "0xd5804d2547786f7d26282726d3f9f22e53e0ee2390ff0de8b97c33a4e4237535", - "0x5999f87486f0c721b038c659831c836980a2f7ee9c887c759710d62691588670", - "0x9fa08ce53c180bb53d5352dbb2312603a0c3a5087cb32f81bcff368c457a498b", - "0xb1279296a8cef2e6b80117a48db7291169ad35b39140debe3ee71465b2a93f85", - "0xd3ca8ebe2a6ce6db208b4bf1853b8625e8b12f7792df1ece14f696d1694addbc", - "0x7fea211ff413b60098ababd9e3077f58325ec14d9fe23bbb1cea0025d95896dc", - "0x270f61667793c26f67cbae50931919b8370c0b395704640837d66aba90a393b3", - "0xe81214151eae79a310d5bd8fbee6358a3d897b2b9ed1282337b71a653db17384", - "0x5f2928cf34dc3da9a559ab89d7496510b3c2b5d00d8de730b6df083d03103abd", - "0x619e0c1627f0802d660f1b06bb55c39c0b570514e71c69d5de41232fc6bca961", - "0xc8384233eaacb714f29163a0a9b507b9c9d0ad537dd64c25bce91cec6fa6c2c9", - "0x54c2e035312faaff2fd08fea36df91f2add2ce39298a1987f4286f3592405e8d", - "0x32bd9a7d486a43926319510505b5018b9ae951f4ff0837f8b9058e93bc303876", - "0x6df5062af0c06aed44dc45026fd74a287e15f5a60f122e49343e5a1d10e5f371", - "0x6b3ffbe23bec6a576dcac6756375e6420123ef1eb39750aa6fdce9d874ab790b", - "0xcdd08aecaa642133a9b93ef888c5a808cb2c84c48943e7ffb6d531dfc1c6c205", - "0x563f9e429bcfdaca1ec2ee7007f286b1b3e4358b76d12d94015d9989dd50b2da", - "0xd3a709568bc87059c1a11909396d655c2f107c999bd75ff5104449f53d590f99", - "0xaadf85b25fd426d50f3a22404616141211dbe541eb116b71c6ad960fa453e172", - "0xc411d65a0dd44d5725a401d4a479f9b5aec10334938bbec7ec216572bddd84fb", - "0x4ba49c0fcf4dc7858eb6b77c942b528a349ad39859a2a1f8995593f04ae8cb2e", - "0x17f327906a7bcee96532e912374523b88f474beca4e40661113121ea626a209f", - "0xb0fce9690ee2a2cd55a0e088c0e96fa0e922b4119df230d4b903c4b18cd6da7d", - "0xf7070d16a82595a5782717429dc6d00b1f8abf6778db1836a23954cf66bcdac9", - "0xceae3600d842c5d03611dcdfd0a0231e469c65b151e247b2205cba2916324707", - "0x6d1e0d4ef9387d070ccfb68f0866937d44ffe7d9c4059a21bbb4b0c87b7a6461", - "0xa7c125405cda699de778318152df327cfe79cfbd999c5cc45db15bfed216b146", - "0x5d90e851ff50eff60b8c92d63274c9f9b5efec6efec3c2a412b003693ff014c6", - "0x03efe16b91c66ac58204c952a9cd24818ca61fc6b1ce4d81a31263a7c045aabb", - "0xfe9324d99b23bd0502d6c1020564a850c932a0d010593e1894ae21641ac5c248", - "0x969138be8e3cf0bf9bd0963d2cc6467d8690fc40797bb2c2950de0df1a8329bd", - "0xdefc0e0c25782165aeca15723fb74d95d96d1ed7420be7f3c85ce7cf7440322e", - "0x0bf69f52b595f75df550078e4293602593e3bc2d7ac528b7be44f556435d9e42", - "0x142f2883383cb22f5aa8e863756bcdd154ff3ed8a02d5cde8fc1572db65003bf", - "0x0fc8980e2ce7498656aa9012eff375b6b32216a3142fb974c357a553bc9b248d", - "0x2714b77d484d7639adb40e1158734058dc02dc761c7349c4f3818c70347b348d", - "0xaac5d4ca615ede62571ea4150e4c01cf31dc410b06d5b443267117197e60478a", - "0xcf9f45357d06955730a934d2de24b859d39ac0ac40c8b29430ffe061e3bbe4e7", - "0x52330a846f5d2fbfc26c48a5a95c9d2e3c88a654ee8f5fcb7f9eeed39b2eeb17", - "0x807c2b8bbeec7e6d43bb781f2d7405f9389cef647751c1fd30fccde0f004aaaa", - "0x8cceb13ddcdb1b080075083091120c394a94d6f8f00a2eb4c4498a3b1a7d2f5e", - "0xef770e4d62f2912430987ad5ea59974aae40a81771295fa1380e417b3df88562", - "0x92cefcd22fbf95dbfa766e7c49a4e211596cc6d997976b04efd818ba8ab49bd9", - "0x809d91cdc51f42438312ae27ea1fa94fea4bcb2c9341c274ea18513ba20ec931", - "0x10538534fabee44df110346114f62797a31cf363d4979f881b721043b2250e82", - "0xe252558398e892e85351274a5064fff650f0323f38b0a4456b14a435c9a40d82", - "0x7d313335d362826f589dae0164a04ea681cfcd54f10d8e339ad1978ee63d7045", - "0x713414b22fd3719c42775449895089e6122064b90258a1b495704fbc4677d92c", - "0x748b6883462e9706a8fcab3b0c71272fb0bedac622144d5d81fe710562b8dedd", - "0x2945d4809258eba164089c2a5605a7c9e72759bf615bdce0aacf96ceb7d6ec12", - "0xfd89a991d0e20bc05d484b9cab61d27a0406fa741e0be6c8c1c2655d1310cdf6", - "0xaa296f6295ade70466ed9c185480e186596f2183947f65bd04fb5644dec93d33", - "0x5286efb4ae287693b85bb3eb47ac0b697ceb72ed6331aaaa521d58ad7dcd7974", - "0x6b3a5802ba251becdf8ba2508a6fb1d3db0b5b99646465b12844b8bc23f062fe", - "0xfbeff1e7cea22e0563407d57d9b16518a0355b676c48605adfb13945d7bb2f60", - "0x7c0764932e56518167f4ee45d912c1a7c78f30271b41ee16038f358d9865258b", - "0xfcb998af040eb99c3393b165dbc904a197341fb7dec2d8430346b7772935dfd0", - "0xe46aec72c95d0accef6ebf564e9264de932458fc1143570ddc28eef3801430c0", - "0x08f97afe124dfb2a18c6c624d9d1bac4cdb5dd102da6bec9c676e7fa000a43fb", - "0x50a62c4cd65e3591651f8fa56831ddb37ff966cba87c12d83708aba72591a060", - "0x1abe248da61411aa6b6801dc188e3f834c3fc4600ccef36ad9d7bc517441a61d", - "0x07e38e7dee4062c20f4b3f221df72590c0838619617e051243087250defd0028", - "0xbab90921cb56563d69347a4197b869a8c8bc34d01d753b509eeb5895e77eac81", - "0x620404dca85a89624483cdd7e2b143b12a38b737c0eb783f5e4b378fa1d578e9", - "0xd70f44dbb657c8fcdb90bcc24a47485fdc99c35ce746d8eb2302d529be43bdf0", - "0x425c47cba9cde3b337c451eb7828b0789a76bdbe1f86757292e7c3768cc51b6f", - "0x11b521b56aecb173dff0358678298ac7f55015dd8b217d81dfe52129d03b54b7", - "0x98ef31c7277a68ba0a2a2809f8e7b4099c0e50cd47ab030f5e1c3ea38b92d39e", - "0x82ae5ef4f71c43cc296f1f6279eddc71766102997b0534ca737c0c1a63b0b3cf", - "0x44e2f38922e64f0aaf27607673724e5c91ed5a8176899e935c73c0fc09826355", - "0xa0361b8e3d7709c4a68ff24623856c54d16ead1762578eb562c295f86f453b14", - "0x91726ead73260dcf30eb478615968be91e4e2b6aee522ff6d814bae4aa595c64", - "0x8afe8954bd7cf7a6c40e3903015830f516ecb1a98c1b028b6799e1605892b49c", - "0x142bfadd2c9e988cb07d36bab795231b9582fe424a62de129a24fac85fc47b88", - "0xe369b5c6a44aa592c1d2c55a6fb9e2a0ec3274be74fea84d2e05451db7ff9b8c", - "0x10030041949c30aed18afceb0b6ba1b9bf58c338e275ae6ed9f2dead02a1b6d1", - "0x993fb9325d7be082347cf9361f665efd4d38466de4234233f272b1c09854657a", - "0xe2cdddac0a6984c0ed9d4232d8b58bc2c61e404c3e60b58b90b5876640e8e19d", - "0x4adb2f59c6704b8be33a046a07d2585328465dc1b45a41bf30fba1ebf6b2f7a4", - "0x2c47e906f61c60bbe994a54e3d0a68ba8dfb519928cf350b6cf8421e5c1779f2", - "0x4b712c7fc97f4784b2f562ffad648d29884ce68c505644e2eb4195fc43c00d9a", - "0xcc0fd5276fae744a1ebf274df70eeefbf301ca7e3d1bdd6cbaf425fe0d400758", - "0x0d3e81f37d9d96efad22ded24f6cb7174606c3b2d294259f37e4f754b28fa2e1", - "0xf80aae34aff371a9911fefdcae1289c126c5876b3e4d2477784196a4ed6dc156", - "0x916469d84a13c2971d02ceb166dbe0c9cdaa88c23e57772cd3c260db7d2f9ea0", - "0x37a5b98e5934c2bf16b048f76c06b4ab17945c1f549a20b76ecbf99ac99c3698", - "0x1dcff6c4816141355ac06f9e31a73f31e4b1cd947de42a078eb60ae828d5cf58", - "0x099bbbaa5915dcbc3f975ef1ee1e5da2bf248e96e87856323151a08dee9b5e7d", - "0xd0f2f3478ed51f2454d21cb838b89a3569b3b2704af4d702526af646b42dfbfb", - "0xd906aac9b70f76522bdaff3840df5d9714d352c8a632f44e52370786ab631167", - "0x7f99f6d3add64e2381d7ddbdee1306e6f6749ce116642f993bc4c06970295663", - "0x58988b3a6a1437814e0057b08ce6e86f58cef845f8df78a6ec563d056592b81f", - "0x7476333d9709b4295a63b55e1d5eeb7c23a752c4211bc8696016b70a4c13dadf", - "0xcbe4bdbaa8e34a22b883371b21d5e7cd474368ef0df5ccfeea3edcb377e071ac", - "0xffc358eec58c4c6cc0031562809154edc13b9aa091674e473304682dcc4c2c1e", - "0x9dadda32c544767bd65dce5804a695b8e5c35d2f96c203c350001e5be78c044a", - "0xce2a6bb7c0bceedce3111d97294b8672d0f6ae8eb537ea785398fdf06d6ca2a8", - "0x5ed79a31acc649619c09c9b5df4d595c806a02e93e92c4fdbe882899832f3cff", - "0xb3e94be6cf733ad114b1c1fe76565c448faa9f14131b30aa79aa36a0edc615c8", - "0xeb33e3a43cc5068b611f7a95fef45d5a83d0219906515a9ff02e31c0f1aa4e39", - "0x63a2f545af614f075469aa9f8d4e49fcac12336722936def26c0a4cd01a31b6c", - "0xa6d5ba412c63ff5903e2f30bdf2d1e93118514a8c82b532fda0259b5c3c21986", - "0xd170edc5d55910ee2671397a9b04890faf8b4a3aed0e98852d5732af897989d5", - "0x508e7b9e80ea2fee221ee8a527ded1d480d6bda70d7fcc2d8d521e35c184deec", - "0xeb07494e1b639b09c49b145d51ecb9c2f8b98ea596779d1a14a71e2eed22975d", - "0x50b37a16f4233ba9287fcda52030e2a7d92db254746bc42f7358c9fa0d4e3116", - "0x447b12ecbd02d12bc80c990d632cc2032b72a441b418b74dcbcf3a76ad8d69a0", - "0xdfa6b21b930c94454ae6845041fb6f4e345a11440077c905b4f3386bf4824ecf", - "0x7eab803353df60fa522a5c1c7d155939e1059ce4d6c83d7e4e3ca756bd0fd9d0", - "0x25a35bba3fb585339e9c9df30a10246774a72f47e99fc329d64d36c67df4fa59", - "0x17ca37df7ec4f94f41dfa272553fd5dd3d641a986fa5a9dbb262fc395dac8ea2", - "0x766bb71133abdf384d8c24aac3f663f858bc8283817ec2dc082bb008739eeb37", - "0x5748e015a55f336461f5b1c4bfc80e5d76dcd9bb3c72d583cb7e585c35d67111", - "0x39909825449948d25a7f9346dd07d83f087e50c82e9fe9b9d474c10eb7bab0ea", - "0x441bf9c03a11ecb3b0472fcbed6aff62ce1c44b237c3357a4c3f5e8ca190dba7", - "0xa0456b6f725f0c4cd0917cc463ad42e8d7ac9496cb0c34f067b691d89dc1f2dd", - "0x6571a9b298c2180c91a8436c7ed81da8a69cde6c1a20c78f8327e9cb7bf9d929", - "0x152f3642af6d21630770d739d3f86b5a0a8b8ae5806be4e1b568499349d36882", - "0xa7dad74a9ed1bde34b6cb0baa940b7d565afb8dd6c4e2bb7fabcc30673f255b5", - "0xaa829dc659ea777c2d84851ca7eeaf8fa1e1409df8ec151ff9d9a87bbc8cd848", - "0xf70ff1f39678b75cd7e9c7cf7de35bab6d54997cce23b769ae452cbaf13db690", - "0x2ebfa2e52ab6f68c8b7f2a8d656c1fd997df4cb680ab4106cacbc50c4f1beec7", - "0xdcc2ac91b7ba5294b3de1eb7db158cfa53db057a4956d403732be9aee6899fbb", - "0x65dd99a245b52befb5099b69afa332a9a529bdb5c3b57dedb3fee52bc11ec426", - "0x83f08c95ecaee8710f687b9ef06fdae143fb7581ff1498bce187acdcc1af566e", - "0xffd22fe4049110b55b117b38f5ea4c268737db4fe92b9b5fe691630b39092dfc", - "0x7b4dbfce2925a674b0de23746b226bd901b7aa49140e400af6209e9877d11617", - "0x8c8d8762e05effacf2e70742f9c563458065f7a0abcf9cb6d6f605450aef08bd", - "0xb97df188cf546690574e316b144c8db55c7fee2d241ebfdba1424739b96a4197", - "0x83cb74821c0295a9d11e4be54cb7f2fd4fdcdeecf9a61a3cf7d168338194342e", - "0xc05d2a626a448cd6b89824fc6ceca1b2295361d7e94ce86005f7d527a0bbf10c", - "0xd0fb8122dfe38416a100bbf00ec04cfb14526296e746da85dc4487f2c7597efd", - "0xdef0c26f8ac9f6704a016635dc46386a95e014a8637733f054cbe87e59e0089b", - "0x084e741565cde3ad7fb2c60e0cb1244b1f8f6b87b210993abf2959fd380127c5", - "0x7986d8c793b5bcf0977260c761f5ab4af1d0bdec8a5dc49230f63258f6a0f403", - "0x584a5726cfe9772deb22093a30ff2b9b30ebc018cf861b8aa0fb28bed97ee0ed", - "0xf046b4649898fbdce79870896dbc2752050b5f654faeff320ed2184c7bab659e", - "0x744c1495610301a14d82e49dafbfce8c70f7f78bc0679f2c6fb964a476fcbecf", - "0x4edf5a73306621d2c4966d4aa6cb2935cd9484f1f0ba709ed5d5b91160a5de2c", - "0xb5faa128c00d7f97a3e5204995065e99aebc76706e2e96ccf4b37d7641690045", - "0x729bbb468965401fbb0f2ad1ba0b8ffc7072f9b18ef63e95f47cd98285b89854", - "0x0550d90dc0f6e8d2017e14324d404ec8a200c45ab4a17ac24cb17ca9b68881c2", - "0xdbc02a7ebd9bf74121fa2b4b05c1abf4bdf00a973b43e27faefd095d6cf80390", - "0xabd6ef9629eaafe2d8170d511a39ea29c30d9ca0f071d5ea0b80fcbaffbebabd", - "0xa9dad7abbf1bdbc32d229a67dac65cb5a14cb0e25e8dc57ef7538122471909fb", - "0xd1e063a59f032a9e0ccd9b926bcd6639e197fce15e28dc6b1bb1a4aae5862e75", - "0xe3187db1ce4e4faf95ae3b2136c34ec2ecf028e4054995fa59aacb94d0e16d75", - "0x4aaff2417ccb6bb6be1e3bb759f745053a3d45ce144db0fb627edd9900000276", - "0x3b108aeee36e6fef8d74ec08c937143facb90cfa5616ab7af6d37c00b3a4f1b1", - "0x1ad317c91fcc12235526ed9d6a45c3854f2fd421ce36a2d475fdf92d8afcb524", - "0xe39f66c3ab33ddb0d1fb51b969bacda7ce0f0f53af7012dcd4530435666b7dae", - "0xe071c158a9c418c34270cf27035afb0896251616ebbc57e7e5a2fbd503cb30c2", - "0x41dae0f056d18719bb5568f0893fae512f623e16effae772d35398da2bd8931f", - "0x426b1e3b67fb576cca9a180523ef9979aaf4d14649637f69347ad3876bdb6fee", - "0x2678a16324ded3abcc20b8545ad9a4155184e63b12d1d3a1959fc9d08dda781c", - "0x3f18f5def267a73abf0543135da57d752c57e4344ea941199055a38a46758ae3", - "0x031b50935a859999c1de5b772eb4363615388dbb9d12bfa6ca72111a069052a2", - "0x57bfecf554524f9dee811f59e9ddb287b04338323e4932c44b208913611533ea", - "0x870b1bf5feb1bb5297f34a80b058c81db9c97cb79609c537641f02753f665b68", - "0x3bcadf9d3dba81c74a178f533c75f1e77c64baa6ae274bd25a0cee762da43a26", - "0x38cb775e5437c113ad72ee04f0721390b7b8fdec2efd0eeae24d22cfd3f0abb0", - "0xc6b545762e567ad0fb73b49e3af783035e77e252f5f26fb56c39f2ba280e4937", - "0x922ba1cbae3ae6beb2e01ce1ac2b3a73383aeda9cd3825cdc6206d04fa04cbf4", - "0x9e5899be114b856b51e0de7a25cc1c5c6247fd2c06f0381d5cedb2c26c2081e4", - "0xc7c57fa4cfd2f5d2df0bbca134f48d152d8e284f1c0d946fcab4fdfde9235b22", - "0xf35e4daf46b5b181b6c6adb53f5d2f93acc93b76b422ad82ad996fcf95d5a942", - "0x0cf08cbe8ab573727328f5e5809baba6b8620536053d023dddbb9cc310df3246", - "0x6ab0f99c1cb99bd763af36647d29d6caafafa4aa119ec34527999f992de25104", - "0xa8aabd97d98e4a657980b581b26abbc9280a288301c33c45961f5e35303c31fd", - "0x3c92814886acb8f2af1342904d0b2688be4f269a24849507ffc0a7fd0d67584d", - "0x582781b1bfd4cd4d619ee98db64eed9a11c0562565fedb6613accc03670dc376", - "0x832c0574c81bc58cadbedff928fd08fcf1b4c80d7b323670bf67a29e312f4151", - "0x932b7bd4b919fdd18acaad39228563d65d19da6c7e8737f9c34fa3d58a57c7fe", - "0x153059b97cc89de7bdf0171150670085b850a63125d3e51525bba6765be313b0", - "0xba341526b613c0f3927d604bac37232dcd5ac104bf256720c62ed14ab05370a4", - "0x2b015d7a5486e2023aa93a00261be3ab97d38a1e7e1b14d8878e6c109d28ba51", - "0x554e29e53bd94dfe253d703050d8f54c703a009b412232847fe01e1b138f64db", - "0xfc603400b18e2570276dd994aa168b7ebf2fa9c8a3d7f4edcc8bd6bf5c008109", - "0x66e1cb6677eb595cad35fd028a36aa416f698b6366c73d179b8bd451b0dc3ce2", - "0x9219571b2c2860cfd4bf49b92439dd76304c2b5b2542f036392566b2b431db86", - "0x84c510b2dcbadb5562a66a1312b6016a4d6fdba205b5ee698978f2570fa191a7", - "0x483e7d9599fe6da557b4f5e35bc66b9d4bbaee82e2bcf29aa245c4aa679918e8", - "0x84dddf3ab9a135de68eb188476c23d1460c45c377e9a62c14015b2fcae515d94", - "0xe03b4b8470073abefcb0d4db364624116a046e85be9a28a52bd6fa5b4d2264a9", - "0x342a467e79121dae9c3d203615f772383d1e700fb05ff8ccb5a48961b423ec9e", - "0x025c53fc663b8cb0bae6a439bc0d4f3996618926337bd80797510b67000c6f94", - "0x0ebcae3ede50801c88b517af406fec7a3922b4876f28b3096425d4dec3f2ec1c", - "0xff78e7b509fbac13400ebbb8955bfadc0f35a4b568549dae44b55d15bdcf2a9f", - "0xe19e3bb2648377f25d4a190fc44b14e80b3e2c8dd17427fdcf50451af8b5a8b9", - "0x50ad2c5b2732b7f2c34fc0e2e103884effb23f7672a5290c7a3ee7f7c4609fd7", - "0xe5a000a849f39efb9bfa550d959e86d2d0d92b6f4034a3e75a84a4bcd34b647a", - "0x56fe17466ab440baaa576898ffb22735f4fb680db4ccb9a7dc6f19a060fe81e0", - "0x1ba71ed6a71d823c35464cafd2f4b35a047de73666f0bddc272398d1758b4aa0", - "0x801ac73a07eb37ee08ec538f723dcf2c41ba8f0f0e553c740a73c1b486b29567", - "0x732703b2b96778d10970cd6f90634e8e383d41f3b863c424a71aacdf3d567ef0", - "0x5295a9cea073059a0c806b5d84e95dd449bd15e081ec1543f04709737e12665a", - "0x25aa778a07dfa614a7554d7b00ed9c5360e1bda16df694279d45fb4c0a4ce11f", - "0x3a9e9b5dcda51fd92f8dbd18caa817f53f096f3b66f953a1f731adf41577eef9", - "0x554d4c03a8f7f4b16bee2ecc809a4a03b3e6bec0b0dfeffb45acbeac698241cf", - "0x567f2220013d4694394b054b9f93fd33f9b24d165ec92eb86eb216f2658a35a6", - "0xfec8e9f64cbf08d5dd3e19ecf7e685ac89e8430221f05166f84eed16e6e42935", - "0x1398f794bb66f9bb26eb2a17b32bcff6c6c8796df52e7a5f8c2b58a94e2d8912", - "0xd32d51d30aa51d571b89bea7eadbb3d32792c5971832880053e6da18a6644f77", - "0x753244cb3d3813f9a59b396fff82253ecafa836b37dd4f4572d2ae033142436c", - "0x74077018c89bbebdba21b096ddeb988a40943985fd479a4a95b172d6d89b3f12", - "0x20ca619770c23b5522ee50b629697763767a61f17652ae06f3f30bef7f300605", - "0xbe0de8f6eef387c01546944519c3bd9761c69bce0e9620d9995dfd4dc66e10ec", - "0x101bc2c69d14c170de6b3ecab9208f68b7b1b8ff0001a400f7373b77454113a7", - "0x8cf7437d2474278b2646a71baf196c21994f640e9baf0be059d820f57e0c66cc", - "0xc5751a0d6f9f5b859382b9840127d837a015a7b33d0a775e935ac2f2e57b1488", - "0xbabcfa88c03ae1d413a6165c7dfbf204d5511999a4fc74ea33a694aabb55e054", - "0x66b70f7c56c40e818fb1cb842a13be3e44d864d9ac5f1a4336dd00330685cb80", - "0x790a401845ed04ea517fe4721da3cda115adba8cbd28775e8fdc9bbe7a5bc670", - "0x2696bb11d905914f93ed07d71b99a6a4f2e90aa7bd5eca5e32275bd4870ac95d", - "0x5aea5a8a2b8586491f85ba8a387e6a6cfe8970c5f47a77f517afafef9a009fc1", - "0x7ccf035e2483a2dfdf068c0cbba769d25e21347dbb40302dd2a5c396cc082237", - "0xc7cfd5568e4d9769465645a1726b4889656c99befb7aa02a5b8ef8daaa8b03b4", - "0x5d5436654bf6c9b428c8ad12645ea5d5fe95aeb5e639ec46d4324dd54b0e7e8f", - "0x82f4a8a018dffaa850312b375503a6325562e75786393810a383498e71dc2c6c", - "0xcb82be0f881fb2794314fc3e2cfdd57ea5e12fa7105a824ed033615c4ae22f34", - "0xefe0e3f97c4e6b5be518d58f398ded35421d54c33e0c93e3ca0b527ca50e927b", - "0xe6ebb787f18f2d0d042eec05f2886020262ac20f3c14e8c03885e04136028823", - "0x1e41e40d2da6017775457223d1cdfb06bf41762b8bf47040357592315f627dea", - "0xada6d7bcc5013069b5d0676df3b067110db631d07020104a556b61423a26d3b3", - "0x8e0840f9e38908e49fd12635be7735c7293833c65929d5aeb5035d45942301d0", - "0xb01ea85a249d3c640f5e2ae43b340a5f0455350719b13cc8bf1a50c8941a8ad3", - "0x4e5524cca029a2729d27a3d2a712979f35e07cf9d436ca7dfc8c414eaf5d5a52", - "0x72208f2a837b1fe9e7445a72f7d57e414db9e0bd87f7e5daa8de14752a2a87c2", - "0xfcf05de95385cee5449c1ffc63cff009b585ea5896b2ad96d43e747bed4aec71", - "0xb9ba0e212f4dc38e5a0c2b62bd0c1bda73459e72233ea38cae8f4a91f0a6d764", - "0xa8946ec7aef9ee1402442e7cfb4363df529b915c96698110ec1e3c15d445299d", - "0x07349344fc0ac74be9acd9f616a08b3d1bcfbb08c2f95d079c235f52a0322fcc", - "0x223fbcf9b3a552057f728cc0e4dd283176735668beabba942f62006a902249b3", - "0x0e28903b5f5db741536eaff97cca54fb98357ffa69b84bbe365312d77374e47e", - "0x6588620c06642d029a1f8dc0b95a14c198b7af2648acdf1c8d73c80d87cdfda6", - "0x021f66c28e1fc7678af4f05726d8670bd88a143e749a96edcf7cf8239607e9f3", - "0x339ca9feebab8de1672036fe85ef63bb591f5466da074793436a07919a7740d5", - "0xee504bd35830555e345301e108b4ee3698c9a0739a8d4210ac975f1aebdfb944", - "0x61a020081cf4fd487b0e9b694465b94016d461897d4e18990e2d34a26e6a6541", - "0x539ed46b92da9b97e94f0bb089b034af3e6c85b9812f5ac18b64026906ef4f33", - "0xd244eaa108e905c9160cba9f75b3d4f46e428749c0a2de7b165e95992e63caf5", - "0xd88315191813c72230222f636d5de64bf5ed7bbeefd574b1d0c2fa61e16d5919", - "0xd428f280ca6b4ba0ff8bdc58a1b5c8ca5991f9d2dcb8522b17b3c09647a9c8a4", - "0x0744cac8522e0366aa470de75f1dc92e307f82e2e300fac1f8adb359e7f5d9ef", - "0x6e504ac6d3e63679f9db61c3e37bc9416d687156354d1fe82f7f434c4e43708a", - "0x34a5c50161a9b06229234a23f210458cf64fbb5531298735826599bc46179e87", - "0x03a94978bc793490395c9cbbae9fba0f7a725f5de2737e65f7e4f55763631585", - "0x183f981a62d19c3a4edc055aba9d04e642f89f203381a4e63ed7496ba7a1074c", - "0xae5492ed7758112e7fd6c21582df31dfe0b1d6b3fcece4d2e8bcb7d5bf56a00d", - "0x8f5062a00e823791785fac6c0eb7c1aa732a70cb1fdd23d4336b2323a99d9f5e", - "0x74584b0ad5236526dc1a4dce8f4e65f87ba3d529749a702217f46ac9a6acef7d", - "0x06f7debcf4fa4511a1182d992fb0ee265714e70354a08fd496f2550a9e268b70", - "0x8a6b101b16aefa233c158e0392f0897efea6b4144a98ab3050551f8f530ffc94", - "0xc65d3267fb49c21636e1cee8d39c515b9028058e8b9e0ec5c2d6e7adc9293e45", - "0xb9286dcd79def251104af4f3b68de8b9e5717ad5275665b6c079f49afab7770d", - "0xfa735c22373482f2017aea5547def3bbe6f0b8f08b7054b775dcd04c3a3b8597", - "0xe3d360213b5720573864c6a1d9204151491bad4235e56c35b340b22e8c29f5c4", - "0x87ee0e69a5db0bf2dcf753e2e1dccb92725f9d1d597aae378743038121071b9b", - "0xda446379789bac65f03bc0d2bddf4da37867069743ddbc4f2e846c90db8ab568", - "0x553465cc2c0ab7fca15424c8507d216d95f53a25e053657fd8eaad58efd45346", - "0xd3eda9aac5e84aa9cb845cbbaa180854868f497fb79c5053becd16f6d9ff13a2", - "0xf8bff7af9e6fc79a875bd7cc4c1a0a347572c5f83c8a6864313e5f182a8bcd96", - "0x3d49c1fc6ca0f9eef57064a5c125c24464d1ae6f29989f2e7f4942c50128f31c", - "0xfa78f4fff3ce4c2b34935ec5692b9bb0f326a65ec6b14299f990e697ceb89a34", - "0xd7de5c5c86a0ada9f328e8b67393f4002714c5d5f0d1ff68129c68b882d5c6d3", - "0x89f2fb3dc33363365ce597c0e5c4d14c0f66b60a8c6ae3f6d45d72af301cef4b", - "0x218c78843492895ae5ab088ed80e82eed320cb6f98595a211340b19428d94985", - "0x5380a88382839684d7d9fc7047eca486ec0383966b262e5e46b9aa0859ec1547", - "0x19cc514f0518b3cedbe31529f8506fe60c93122cc7da49ac6f855abdbb729e9d", - "0x93e2d29e5c272ba8dd20f621132c60ddeea7779309a4d14fde6e6d58b0643f98", - "0x3158d8140bea277184329a7f2f2bd7e956725dc80573ccb169c40bfdcf3f2c70", - "0x7505475407491f5a4619a25b97b1d5cc1e1ba38764be61b694b6c1d3ca51c216", - "0x40c556618336fdf46e581183b7e57a2f6e2665d8adf0d44b39d9a5150a56a13c", - "0xe8d0da5275d1280e0906d62421bd64a3397b5fade799a701e0a37111d13602f9", - "0x28fc4cfdd74093fc78d100681b770d7aead4d57552c9366d56bee6e7776653ce", - "0x81eb57db158fe3ae4e62785ab8a5c4fa07bf5a7815f1dcbe25bb997b89392691", - "0x361a76dda932befd901f101507a238fe1dd43ab563d09e5e3c6ca43e6de48f19", - "0x9931f8e7444209b91572ff581bee13380706e6d6de933ad96f185f71eeb1ca9f", - "0xf9b544a3c3090c494eda2657a527288e48b5f4c9032ed26eba3dcc6558508d79", - "0x16936de0caabfbe2e395583091c3b90305cd1fa734a5ecacc8db0ba8c40fa89b", - "0x998492ec7efc0178ad691288e36758800b139a26aa493bef28f2e96c460be402", - "0x626f93a0163fffb94c1cf227e6a0ef125f7930f3a21971dd3a9d443162a0b176", - "0x64a7ed78b519d1f04b5b3b46c6804c0647889475d4fe991c8a7e405a4a2334f8", - "0x4aedeb8f7470b9c97c2fec75b4c44252a855d36b73c6a4550d15368dbee9d0c2", - "0x17095f0fb67a7d24910c086ad4cac72e493bd7276afef463d05e26fec1f39381", - "0x05b5fc483f428f6c854403b365ad11f4cb170a2229ec66ba671b94ed50686858", - "0x76bd14300b8f845500b2991e8a78df8dca3054016ef6b5069b77ef05b806caab", - "0x718aafdcb0223d7010c9b6c012c38aaf82e02c8737488bace9a7f8db15c60047", - "0xeedb09efda45baaa9034a18259f1aae1275a86fc81a218d9982e0b17f8d743fe", - "0x2608c1d2f2f8513ff1ac6e33d21659624c16e00218f984ce971eb9bee4825101", - "0xa5331637e8221d95d318cd4f32d5d37698731f569c4879c151086974e2cd4e95", - "0x2f9faaf3d219ae56a94fb4ab12f940d63a1d86776aef6541d40ed63d03d44682", - "0xd91d881ed4fd4b6197d77713d2c42758d9e550343ab67fd761c7c3e7a3724fe5", - "0x2404c00a40e6d67254ded0ff5a5413776351b8587ab1fdc4cde74c316435a9df", - "0x8591c719906e31c2150ff3183382f665f756c9a324ed470d0916deaddc240edf", - "0x8d670ef59f2237573019bb60cceb14f7198698773b572f6acfe3d3bad6c33d3a", - "0x3631004b8ebaff324494cc7320bb3c0deca53480a21d2d7fdd516271af7c97ab", - "0x4dc37b8650d5e49ab1a87a9e88912a98a60099cdcfc33a5c21627f0bb05ff124", - "0x95a8b189f6335a7d1523c470aedd2fd41f66043019da9a1ea46b379372ad4d6b", - "0x7219f1318a197b919a83dd51417803c752c24dc3a005cd08c091b0339634e806", - "0x81d850ed79388dd32265463d5283b02b11f012e08eddddfe389d55790cd1ff52", - "0xe170a7511431b18ed7330446569a253047f4fd0653915e107104380ac40affb3", - "0x9c38e78c1851edfe159971a455a70e1d61b1ae71ea0cf828ef40d4166741fad4", - "0x1ee49fd6dc9fc4f6e8bdee61a3e010a0eb9d06597c256200538ec73580c42bec", - "0xc0e415e7a108c241fe301e6ac22c81738470b499aa65317fb9344d59ed763a09", - "0x4fd8e4224aa6f2863ec655c10e829e113882646230a3dc753d80c111093ba167", - "0xcd7f4ab1b3900488068618eca688f2fbf85e58b265ec0dc4c0ea55f8892be5a4", - "0xddcfaa6613a08b4d0a5f4259abae86cbe95fd5ec5ec77fad9ff2c5662efd4025", - "0xf8bc573cc98ace8805ea12e49c664d8d9cccd650038a4c6ad559780d1d2392cc", - "0x65d8dda84a5666f872d4a744a7c98b6dac5e1082e06625d49d32212a37bb1291", - "0xffc19cd76943cd82bd7aeb8d25d6dc4bb71f834cafe50a5ca2ae257d76a93db2", - "0x3efd8f977e31b77007ae5f290a63c22cb417b52d9bb36899baf826049d169785", - "0x9e225513beea6034be1edc5c51a3f12718713578a2a69bafe7af00bdfc477d02", - "0xebd0e1994eb8dc81f7ed767aa664ee7fc0514caf96fa5080c1ea120bc9803ce0", - "0x340d016d76c6f0db1853ef43ac32c2ebcca11a936c19f0f6814d64aff8d63ee7", - "0x57ba48a6bee7cc99a609f5ec1c9dc2aa1ad26589b292d0e913bbb7e6700895f4", - "0xb0b3f638edd6161784754f858844e77f9dc13a52943120680171deba7272e9d4", - "0x901ab47b6f412b57c0b09020defa44b5375c4e80749199c084f6ce0ecb89ab0c", - "0xb8c7e255dffdbe496fa3d949ed552e36ebdd6d14acc4049491ec6a033c086616", - "0x2f1780d8df1d77937e6dc3ec8483d39e3722eee9b102d84cb412b6765e1de74e", - "0x88712075a1174fbe444b715cad257d16547968ac267142ef293897332feac87f", - "0x6287016bcf8d4d4b67412f0b94b080d6b09b35d2a969d333fec03a114f49852e", - "0x7bae2942ac4d0c98a53feae67f5486415b9a59305b6adb3c7fdbf78642004eb7", - "0xe2dcb80caabe834c0897449540154ba5102f7c7a534c40f05dad77d42e26c571", - "0xc1856ba8c970c81104603472986449d9a6ad17f56cf67dd243c707c689e2faf0", - "0x3dc698054a61289b4b39f89c7f09dfdcf72c4a5a54276e508b095374cbbdf69a", - "0x0b27594e85ee0393ee4b16ac44751b9a1d8a9e7d704f42cc7c46bc1cb30dda54", - "0xfd47f64c6b87363b36d1de4950c72a7ae7fc61d941d6d046f89eec5bd63d863d", - "0xa420ed29af2c8a076098250c29704638476fd014558fdc68524d7a1c9b4043cc", - "0x20e516a19060c4f95559484dcff148812ba875407c1e0548714b630439985f65", - "0x779971f5b6aeb2717e6e336f91b3babc6b96621fd93352f0444b96043c34c1f0", - "0xa57891c1f318ee62a989a151b1be70176b8a86466e3f1669a87b3131b3f7190f", - "0xb09340884952e9fce0a543bd8e98617caacc63d4c87485da51cdb906aefd479f", - "0xff2c2dc309fb4c05b90a80a3fc715427f540be0788b33525a5ea00f0376ecf8a", - "0xaff098da407d643f4d60ce864184a0cd2001301833da3f379659ea4462da737d", - "0x0650daa5593e4d835532cb1cc353bd2e93fbae818e1f334491539ef4d363c2c0", - "0x1d6e8666bf720b0cc1fab78d8ea982ecd2b44305dac8edb2f5b9b9881722399a", - "0x673a6337982d71675566e7e6acf703f9c5b18718856bfae59edac11f4cf2ad3a", - "0x6f68da412db64aea7ea33dd930e145f45b36cce9d285ccb878d33594bceafdb7", - "0xbcc221ad6460fb4066d8c630720a519557064183fd3ff3feffce36b0d80b8da3", - "0xe671736f8a72834e5944c296f64a0d35743edaf75becef65203409bd369615d0", - "0xe4b50f9724453eef07bb00e56c32f6e056bddf38fed0cc80d3d9d2f86258810e", - "0xfa0594818b10ecd7be1deba272ee23b3cce007382e6974645a505df7d289ca39", - "0x35cfa0772e326b9325a613ac39ec350241a62d9f51cc0a1b0de4875d2ce48059", - "0xb6575e9d2cdfe08b13fce3badd0a991f5d331cff4444821932973767677ca13a", - "0xd4821ae28196784c066b254b2fb3b6230d5f4a0e769d8b85fc7ba453d3e93f1a", - "0xfd9f762aa42f1eb43e75c3c00d292f9afe393a14e272037d072872f820e710a8", - "0x9f78b0168eddb9acecaeb07fea85080fb638aca8874a9ff15d3bca7e3e4f9e73", - "0x8c2f0986e10039e96cc6695884401991d714adf5c445e9bb0f90a14cb4a12477", - "0xb7059b47417c4a06bcb11c6ab78221835279d130f52f5cfc316e80f87977b87a", - "0x56fcf49a8a7a69d4f8604190d79eae7e2957a1d129cfd813c9447249ace909fa", - "0x9401c3ee1a458253b3da8820c1e9b8945710839a826582f3a86e8a482dbca0f2", - "0xff3f9e5724cfbba1f837a10f3844cd437c0f1b5e31ddf61c4e56ec504fafac55", - "0x8787e48bce6ac94e62c13b82c544c2265b42ec08d959c18722c3f722cc8ff2d4", - "0x18cfd60669f16555f0b0a429a6b98bb64e936863d5524e23c3d263dce8177866", - "0x33a19dfb8f79494cd2b1b5983884992f637099735e67b2197ff48b00fa28ed0a", - "0xcd2d1272d5982579a63f951c3678c5ec440dcbdc2a5fe4ee8c765fd16ff07353", - "0xa7bd53be4ffe9029d5bf0c15051158d4fa1d9857861d9b3fa63dd0d5052ac98f", - "0x6e039f06324bfe8b9604711719bee4c840526f8c3ce9bc5c3c5584315362f0e8", - "0xafd6c666917c47aeae4d44703d891a3498211054535295178903665328d24291", - "0xc0f0dcd1102ae4eb932e3ee71f1417dc852f3f1ba6a8f2b12cfd8b86ceb3c335", - "0x6845dbf33355716e4161cd3e03ad296a4bec741255d91bcdb6e36950c7010c75", - "0x832d9dfedc39989c97066d465f256ceb8922f99e01c730865f62bba3a0aa786b", - "0xdf99981c644b3dd68728aa9801d5ae41579cc499a534d5213797b3fc35bb64d6", - "0x0115b4118eaf40a9f0fc1cdb8f26ed41e88de8c99e572f72d79dffad9a776e20", - "0x8b09da2fa7f9bd66158c3d469cd22d3f8ade9d86d0cfbc49ccb48335eac53df1", - "0x1edaf123ca42fef6c94b334ab5584c2837949abf3b3ad0b05de4e1da16a5fdeb", - "0xa7bf42422c1beb01742120f78b46ac1eae595f442b0f246f53a919343b4b3b37", - "0xcf72cd32a99afad5a5d9db18819131219a92c1404b57f64215a9e9e891c9428c", - "0xb0d4dc75898e16ff0c4b8bb317669eaba1e54f8d1a1cced03e6565ef3063fde2", - "0xde58a7597969ab3d35a4269d0623ba77254fa140ee0dc00f85e1d256f515c26c", - "0xc0c52dbd3d1febd3e5b4a59d9a6c1a4023d612160bccc5733f2006738baeb149", - "0x42759b763eef5c70b4fbb982cd247c29b0570dcfcba9649084622bc93bf0440b", - "0x02b44944241f25120667fd0c83797915239018c4b370378719be93a809d0fe31", - "0x176572271efc8297d8472120996569abdc26e0b2695cc1c69f72d8e27fd927d6", - "0x2c2f7eb653428b3a5329b339094caeeb4924d322b4362546ade060f8ce89ba65", - "0x3b548a06156cb3ce3ee222cd68a95ecd69bce5914a699b020f0890719a5f97fe", - "0xe74cc69a92f69d63c16a4baae5cb692e57f2e2efde17e0c46fc4cb338eadf215", - "0x9b2a05590bcfc56c432ddda8607b7902c1195ac72fae25c7f18e547a20fc1654", - "0x52f4d9509d31fa6100c7fa8d644dbfe2122581e8a277c6fe80b68aec78035ce6", - "0x380c3d4f876907fed243d11a8caf436445ac5ec55bfb3596091791bfd80ab8d3", - "0x386a91979bb6c8e42d282132f6903e517204fb9ce1379fe5f2a90b66b1f708c2", - "0x333d6dcba02dc8418059d95d43e22b9bc33539bda31f424c762c5b62676b04b1", - "0x50b91f399926865ca64ef68dfa540257f660759ced1276da1033bba6c2500da1", - "0x78266d3dde661a3b10f43d31375993ff002a7b49717a075c6367259baaa58fb1", - "0xb5066418dc050952f056082ef0f6d6b47fb423e9e5458176296aaa6edb7e7af0", - "0x3b8320734691ac54f2a231b296b889c9c8511843984a9ad80f3c43b8fdfb2391", - "0xf85d88e47f6b7ec5f5107785f80fd8b51ad6d5488be9b147e03b5697567ec932", - "0xc352f5af398bf837c89b42eeb48c322ac8eafdbf57af5c1c33ab0880623c7599", - "0x450a16022ae364a4f3ffd5e554aa9a324ebf8b9e6e635e753f4872ed6f63051f", - "0x0438e9c7d357912b1527a7fa5d6e30ec428e472a617d30e33946e47a1f648dac", - "0x4cea137040ab131bca9c9cdd41760c181244c736ca1d9d19370012935de0de6f", - "0x757728e69874bcea324af5348e76db22c3eaf8348843f61b82271570a5a96a93", - "0xf45bd1383b1d53e95dd4d0515f20035cecfb0707442c525c0f06b55e7ff52a61", - "0x156d898c3abc5a55d9860d679d97c36b2e48f833c1342bbf455b13874743b1bd", - "0xb3e80e0fe51cd1ab5d8ef201a011bf6a0a5878d070eabfdb4a6d7c77d06dfbe2", - "0xf7302c6bf2d04fff65957a18c8b988bf0e3dd7f3933229a6f1700e6447560d5f", - "0x73ad7c6627035301b9815d82626fe958ec0ddae62060f0c7f431865dc1b9f6b5", - "0xf10b5b53d915c1bb2eb9979dabd2e917dfbcfff17f1d09cb616b2a5179014ce8", - "0x93570e1d259272e6b825a5ed34a103fffcd27f792fb5b09a2442cc83a3bd172a", - "0x8c5538b8ff9ce17625018bc309af258e4f5acbf9c7c2d4e948cee8b30b336f92", - "0xe9a6cf047c81e965dd32c57edb680c2aaffe2e367976eaf49da5dfc957fac2f4", - "0x7b972cd72e31473b463ae197e30b121afdda7878b9bf0e1961d3813734663435", - "0x9690c83cc3a1b1b8649a89a9bb1e1fed606e4c949d46ab8f66f16c8320ff5127", - "0x8326afe249d762313e5e089474f174825821099a5e9136a7783c3eca7a160d05", - "0x240410eba6ddc5b60a0bdc83fca59d615d95c64b0afaed0a7f17b96ea65684b4", - "0xc48969c8f72df638ef2e8b98f0d67dc6d9eae1d5bfba2b02e584ac2dab51f3e9", - "0xc4fa4b201243c20d35df1ba466a4c40139471ec86bb55f872d54f6404d4bb05b", - "0xfd085495f4ebfd30f7ee4ad9f122ff3da3932fb87ee9aeaa7c8103602671b96d", - "0x17ef24bb8c605d6a27b3b44744353bfcd127488be96ac77a3d8c706015889040", - "0xfc2fa0f9a8aabf5fe515cb5b4a4ec6047f08e6140e81bab8624b02dbb3f99d71", - "0x3ea5269fd6cc38f8a2167cb1619d643ec9de3deb9ece6c19542626f7284b7b0d", - "0xd6ae9f4d8afee0374c662a86b489c603b4e16fb82dee3f57ddfa971e68531fbc", - "0xcdc97e7c7aba5cc5939382dc37142c9d59683d3188d642009edb492ccba1ed77", - "0x847886244064d2edd6791561638b63acd09336b4c88eecdb09493e8ee5324dc6", - "0xc3695209de581e3e3ee849b3dfd93d8102c9e047edc1596aa332d8137d0bc3e4", - "0xdf1399b24c2588519cbf19e2e7218d0fdd793cae1eeec52f9fda7c729a0d4e02", - "0x9718b2e289feaf4a2acff80f5d1c61400e917200fe97b5b9b6200d4c48e7a526", - "0x6fbd4948fdbf124fd0a9c958953abf372854bb6e29adfe9190b0101b97a342f9", - "0xb54aabb1fcf4c8b701734624fb68c842b500f5e71c39dd04083f8a3eaf7d7c51", - "0xc3710e6042dce5652464ec6891ec5b9f436208c12afeda5fd3f454edd8b5d79b", - "0xb6e3d629f78477b79bb9368f1611b62d717b3214675c64f1eb737dfa17b03b04", - "0x6fa6a372c4f33ec50d56a4039c0eb7d6652fb934b24ca468c1fa8f034aa043f5", - "0x51ae789b75938201ca463bd4e7c36152aeafc4b63544293e689e15ec80ed94a2", - "0x4fbb4816f1ea23ea83a425969223be5b9cbcd69113ff43e622ba9753a3a25901", - "0xb2df755c413220d7b684b063f81a800072f2f552e37fc057f555b252f70e858d", - "0xb5d01c218c65539914a8b01c40bcf629c83bbb4ef0c6289c127f583b133aa4ca", - "0x1489588ee88b1922296b2ba27abdd0d393a774009941f4779a63d3ccc015c34f", - "0x093c64995cd670084f2a908be6d6a508f490641474bfc722eb8605e0600d246e", - "0x9addbfaec75c7de95a89691ac3c23ac7c557c278155ba6026aa73258870847f4", - "0xedc09711042a5e3e94a1fff57e7d00e0193f1fb292e3625c6c230c66bfadf1e5", - "0x99a935be3a0aeeef44a096fc8b18ed302bf7224b6b7abcf5e897ad7fd44ad9b5", - "0x00d804cff32284e41ec123482da2a86019ad97d8b36c0b743873438e0cc7438d", - "0x669eabe8771c02c109a7d46ec07b346be0901078f5cd4419b05f3f62442f0b1d", - "0xbf26eeacefd0dfe498f7672aab4a5262d75af5a7e8f5a5dbf0a3e9cce3573b1e", - "0x9b43c92edad88c5825f99c8490b627b68602701a8358baa22103d2b11ced2d77", - "0x8e8988d54263d7e2d9692ad884d44575d3fcb7bfc37333a814ad19328df3c690", - "0x208d671c12795da6d89847247d9707cade2dad378a840d281a637e2af57d137e", - "0xfe83e593e005e58c47571fa00075d440286e02674394eb46956a25ac2e2d3622", - "0x41a61f08717bcb479edd0ed1fc9d8bcc13b9ca3ddede35dee6e0d0fc347c4c45", - "0xe9fa1018977dcb1a2dd82ef24105dcd827bf815985fa97afc0d294711997f686", - "0x7deebf65689aefa15a6d15f7b6e4818547f56b01f622f94fbc42ef968762ebf4", - "0xa8fad9841e2c5d7c1131c16145c57829f22b62566b5a7366d6653157dbddbc83", - "0xe0614431ceb7f622aa4f43c03fae28318aa726dc1ce0f95d69aae011eb62a017", - "0xc731b4b0341ff915979233c76f3562e8a28f268b5d3e531d8d4f3c927837f123", - "0xef265f9c899303b2afcf6cf2418cfc8f9c71d4254321aed87827c0c749f51546", - "0x40edf9f2122834fdbfa84483c376a6bd8b67eac8b8bf454258dc7810f8e849e9", - "0x63093778c25926c58ca06014bd1b668b4d89e641acbb94bcb13c9ca0519fdcc6", - "0xd8b930be700bbc53228755ecb5074d16308f8dd3187cd3a0efe0c2414001458e", - "0x75b0cc9e892313d9c1ecfef4bf4b3f5976f1c830a7283a82fc331277a4a97eb8", - "0x4f102fe9ed705a74524f2598ee6f22adfc1d4186e2ebcc7331bb48e900795cdd", - "0x48a257229a0e91207cc31ef82f00532ec12cb605abaeabf1235676dc57306357", - "0x9ed64622acf60a2bc6fdb2bb738517aae8dad9cffa738ed4f95b02d3be04176d", - "0xd23a29871a1fe22d8bb7e7749aadf6d77c0140fd383cab55b099714af617a371", - "0x79c92002b2077df1c7127a4c49575d7caea872ca1175011d47e019ff14ed8535", - "0x9a9434b2d09c181c7d9f8ce3e10fafd8c3b1e27b807243d74075e747570fb391", - "0xd3c946d1ed5a76e6e17a599d06bbae3319dedfa047e8443f9df3c4b09aca3b88", - "0x4f3ef090e35ccddb78598a581800ea6f2709c691e3024c2f5d13635d3fdba27a", - "0xf958691aa9a520e8e399e466e390679ce3781b82f1d0dfc62bb53d0b3f640176", - "0x4a7faaf47a5031e0a798dc00ab8248aae16866cf74935ac0703d3f1730617d79", - "0x6d350d1568d7db6c029f32d7c8fe47cfbe99984d5d24a9470800f4b70207f653", - "0x6553729bf5616496d7d3ff3e120ddf1c4e64857c53303003e4c9c2546f8458c2", - "0x05491ee727fefe4ef46b9922856a5282c4ba27a90150b967a04e62d85cb3dce4", - "0x61e8d5b5143c0c532adc9f466dde430afc1d1755775dd39095a51a5aa2d4422a", - "0x2b5a26a051940c5f804db637ca77a835ab50f6c62648adb5da7ea05b39b04b38", - "0xbc391789c853b9ed06fa0d2533f1f56b55937a49061f1cd1a102fbe88657a764", - "0x4206d3b075128ba118dd06bc273999ab5ec097f1690c2abfdab351efcfddaca8", - "0x78075f3f0420f490386d419fe2b187d8c6a3b0bbb2158e0f9bf04071ad235bb2", - "0x0cf1b70db8922a59f11eb3a40ee22264a231905cabcb270dc3df11d22c8cb780", - "0x093ca9659cfc716634b38ccc97cb8193baabdb6569f4b33e56e3d8ec1157dbbb", - "0xfb17fcc35e7609264b5dde51f3847f98f555808c693fc345e6b4da55dec4fb9e", - "0x7b6ff07aa7bd0decf690c24d510a2d08ec07fa78276311205004b521f15a161f", - "0x2323e4a69094bff2e821a49caf30ccd91717bfd9eef8609331621071d4fcca2d", - "0x9f05738aa053bd67194099d8bf7baf3166c4cc18b76aaf7cc1a197d4af911765", - "0xb46c0513fd784d2ef4029eb3352e4423d627861431c160988bdc901c01957114", - "0xe1b5c3328267a91718980d369530a7ce76c6f7e404030e880c5dd57508abb03e", - "0x66d908a91b10d229bd478a9ae64021757a22edebf9d41e4a58e2168e8445d137", - "0x9de8289dc95f5b61cdea4a32c7064750adaf034123380c68251df2b816790893", - "0xf9de4b4229f1d152bf4d00864d8d5c37e8cf38264b2fe304931392f848fbe8ec", - "0x356654f74408077036e642d6ae98d7d3893190f6169110c682bbeffad006e33c", - "0xe5a731364889cf98c6136de7365fc53b3f8cd622ae7480fc35a9aca8f4e615fc", - "0xecb8b7af4e850d0854aacb48f034d96a74a81e588f900e1acf0f421e8ea2e59a", - "0xd3d25554bfe2217268be1364b9f74ece500ba8e49260218f22bdb86558edeea2", - "0xee9e40ade104510e4d04b9141864d3d14ddf947f7dd8d05ca29fb3b4d4efce7c", - "0x440556c74bba8c2d89d9eae72f713529f0694a72a741951d5144f7f977ba745a", - "0xfb63981c40b30bc7675280cec8063e7f64f44eb60d983b462cbfbd923a18c46f", - "0x666810ed3d8a8cb87b9d3d908285b7428380cbffeaa2ac9f50b9acfe3727a334", - "0xf87e07add78b9ff8c8b8c4e81b1086ae1fc9f97903a0b8d5e30442781abf5747", - "0x7919d5dac99d403b646b4785205c3ec2635023816e534eef9f14aefe01a0c8e5", - "0x99e1935acf142897bcc10ca07285849b08e5d8f0f8ab73bfb5b6f79cc65f4919", - "0x182a38b8286b5a6ecc93b431a20356a178cba89ac025327e28b7bab5e815ae1c", - "0x666d49f7785b6d3db5ad92f1403e2e447743351bfd31f1b3b61665a79037a1ea", - "0x898f91e453cb2349f6a222f23aa848bf01426fb40fc7c00fea3015847da37edd", - "0x6919143735b6f85a9d0e37bc4b81a92d664b5ef75e40be58fd1b269f5627fdc1", - "0xb854a51fa1ac7182b6f92d48cd90d8a576e75f769147916565acfbeeead664e7", - "0x3924e051c439c6a49a7af5d5a07d3b9b193eb1a6c1511ef406aee6f8e94b2f94", - "0xc6a2b425de2dd99bfa1c0970d29df185045a56cfd3b4021a3eaf630d4e87c6b4", - "0xe66104bd368ce9ce40920c6a0a1dace7be55bdd5ec594cb9169e42cf07282168", - "0x876d3a55345136b3079c260cdb312f8c708f9ae8afd3a6a90f8c018ba58be19b", - "0x5c0849ab35f38121bfceec816cdcc2525e88530ad874086bfa4056c8dedb1e77", - "0x3028fba2e7ff0c70acf12e33e744a881b94865d6f373a50d5d5eef23202568f5", - "0x9ac128a61197f07dd4b0fbac847b864a6496f1047a085e9a26f1618e9ae83406", - "0x55c9428bc7a5017957e025f046231005d4199c8e92627074923280cc3f6e3cc1", - "0x474004a4237145fc86484ae385c21523250746c662e791d5c366441c3b9d2189", - "0xa53ff4b977a2417289f9b532a20b758a2b0bbaa84e71c82e783c7ef045f16eec", - "0x0418a7320d961c5dede728027a0be6f4979211ec600b564edd408a1e9377d5b9", - "0xc8ed8d992f2b75be024eafc15ff7d7c5c0b0599cdd6b603437b4e737a60993e8", - "0x3f5e222d8b28b38a8c7b190448286d4a14afd1b0cb2697fd9356158f5215a0e8", - "0xcfc961c5ff3b8615089248635d900fbea9ae9f7b4d86ebee3703453567a67485", - "0xcb88b820997d440812da7192f44c6debd27da3a41f04b595f515e65cf8a80d50", - "0xce6f3fa44aa7a177ea56d97f00ba344852f614951e665e3742429a6aaf698c37", - "0x21bee516e3366f5770dc4f6af36f799a5c967eaf398f2b70c3a8ca694096a9d5", - "0xe5a99d3fddf70f8e732f7f4a14fe81de8cf1ffc945a47d9eaa7a28c5eaf6ef84", - "0x0d9e3847d768c28783981c6313bc9b0e60ef4c2a8693cc25d399e9db50f393da", - "0x728f54d10e7b531b464ec7e86f3320a4e853967c14061e727f58ee3c115cf65b", - "0xe1a0b9237f50b5e47c98190589842f0b72be2acb3b6a57c075b0c9933ee23100", - "0xe42d8bd36eb740ad018367109ad59f8a74ab017eee893be54a4d1b04b7cf37f7", - "0x72bd0949deab66aed968e725b4434158741c70007855105dd17ab9cec2cb1a91", - "0x450177933434050053831a4c162399286a92c18f9899c413d1c579ef95379fad", - "0x8c950374baef552acae9affd2238ce1b0a1c27e463a03e518b99f23f269f805c", - "0x7a09cddc74127b837571d245fc039489cd0be46ef3618460013de17c2bbf7933", - "0x6ad520bcc098e50d371d504a1e4f4e37b63484066606556debb14c1341880f9d", - "0xca0232939f26cf17430ab3299f1970c334f8dc132724c6e6b01a2467f4dbc149", - "0x9edc115c3af74d77a3267df2b87a77d82b825d07993b74fc31668a09915ae070", - "0x0147d798bf772842ca2e33c28b65fc6934e41e8775b5cfd566dd2f60641da26a", - "0xca005d851a4c27070a0706bfb3b95303b7d79008f49a681f052700c1b4275c57", - "0xc365df99690cb05b89c7efc60a7af3bb4e01c22e5f6d51c9631cf737717965c6", - "0x80aac988930e5f1de8d1db964645abb114e145f301db96925bb02cae2f6bbdc2", - "0x7c67c1aa7d047bbb20131ba231a55d0b596394b06b89c574d23b8aee7231b65d", - "0xd2e03510fc847b2890ea18eba3ed90ebdaaed83b55bf9cb5eb37b9fac10517bf", - "0x4047b15bafa722bf1f5ed69691e671a64c54eda20f11d25143ac7e5dd4e9ccad", - "0x746ab5985f0ef9bc18cf60d1216c69d1fd40df2c1bec2262bae7438acdcd42b2", - "0x7fdfec7be4c42f0d16b66818334e49c97d7b06ea6a2befa361a4aba3c511cb7f", - "0xaedb8c3201c978069b5b2b562252cf617e2a0b0cdae62dabef3a04badce1a084", - "0x24dc7b367353aa7ac0c386f4f5768ef1212fa2cb0de8317300ee54b6b8e420fc", - "0x6bf5c551d97c93ed425eefb8f2998b5fb3735f96bd71b1384799a3330347074b", - "0x9d0be98f9e31ad94e6badd0bbaeb85dc69322649b825f8c41dccc8f9a8d0b4a0", - "0x0d1e4ef567e933a591dd361bb254c080f91351b04ded52e022ed296c2c06580f", - "0xb80d22650285ec257007fb6ff1de2c209cc6be02303b3e2bac43c6a57c0d7004", - "0xe2cd2f349288db25e7315e3c8b4257367f082a377ba4384275a506e814940caa", - "0xf56100200b9fee76191568e49bbfa0eba66887f24cbd6cfc0145db6f31da07b9", - "0x13beb775c7a572d1c54631405b45f3e96938af0d5c740c4762d5ebe5f7ceb1b6", - "0xb5b20d1527a14d733fdb3a94f178dcbecfc9f4312e2c3c0dbfe57996e143682b", - "0x5153bf8c2d061de36ebbc21a31c3755f96296936e79584d03ea169d7450700f7", - "0x8d59bf8350b7eb84d5cceecbf735db04270e4d76b66eb9961243af61317a30e3", - "0x90e0af4b3a2806ea91799ee9d973fe9a2a3c7c07574acb7c72665af11a515bc2", - "0x171fc19c2c2e6fa95af7789a01078a34a628f998ed882884e6f514d45642cd6e", - "0x5f73cd024051e26bf239f11b35b757e896dffc144a2cfabb50aee58e2b4c8afd", - "0xdb7401010dd1db0e8af735200f9bfea39f868425afa01bcc404e5136db7cafad", - "0x809dfd2cc6df05328ef813bc5ab86b6ae90bfadf37107dade647e5a49fbda950", - "0xc731c27a9d048ff7ac2f08d1d68d46fa57e61e620e044b929dcd7d076d157eb6", - "0x1d443d7c2a3974ce9d3211aa39efb84d779dde08e132628c77d72cf092ece1ec", - "0x71410564b523d40ccd091e3e19deaad26d74d97963a171cd6e2f739abfacf63e", - "0xab33110fe291f5cc2f469cec31c7d21409345545446ac33c0f49e1e004542f6d", - "0xa775e97c8fb2b70449066c1d33efbc44a10ed6c931b7d4a193e649b853f23794", - "0xf07dd49b627af4aa3253f0403aeef8a3bb452dc2c69a1227325a1c88731ada5b", - "0xfc648f6b75782491734b7d24fde9c7b4bd939d0b338a74a0b36daab26ca3555e", - "0xaa88a296c0cd44bbaaf8148c005f180d7a2757b4ddcc623a43a88a43deeee936", - "0x1ee2fb788d4dc943f726b8455957bc175ebac3e4b8427177a053d7f299a3598b", - "0x13e7c4da8f8982f0ad250a425a24ea8f8b8574abe033e236dfbf596fe2efac0e", - "0x8acdcc6356fc748e03c06ff4878d0fa96f18e4f03272ce01f02ad29d8211f611", - "0x230d99e6323448f1ebcf7e6792e2978a5d74ef654234be97ebaec5c56a684457", - "0xad35598f4bcf7bfa1852a9265a59d0f9914b3b6289a7aa3015e3d808d504aca6", - "0x4d9956ec8c5887e8cce4bab5b6819cc13a8703cb527f14a5e9db99cbfd83b665", - "0x99a277612b78515fd153c88e0060c274b1ab4a60202b575a9b9892420ae657a3", - "0x656acdfdfdf9b76d499176bc3a259aa9722b31c7f3f4ae5c2da8bc3833c12d66", - "0xd06448c98e529a17170aae72cf359a1e2808ff529ca9f37df3360b92790f925e", - "0x9afd24c01afe4856a47db81a6818899d93a5096853b395a24cb9edd693bd24e0", - "0x03b7a5d8eb6ae24ebe158e411df31514229a2f302108984597317196f6226ccf", - "0x6f97a76567724eecec6c55e811085e7a83cd5f6551924f6f8de590b16cd255cc", - "0x5fa0d567d50e49df1d24f243a2ff97feecaacacb6a5d7e34620a9f7e18cb3f84", - "0x727b25a1d44fa0eaea544ac593fd8bbde356a71a48162160feace5596251f1e9", - "0x3d8340ffdc8e124785dcbf85d3b12a65808c49d91a77ed7a1ef54e404acb56cf", - "0x9057ce97cd01e362ac771d04ecdc45b10cfd45d0c3a1070e2c0fbbc81fcdcbd4", - "0x016b166a2ff70c7beec4ecf9a408a8fa4c556e0e4382911057a984454fd9ab45", - "0x9c2a27134a95f9f28d9f313ed2ff1dba33d5a080cd2d00626b8b6efab87d6957", - "0x66f3583ab4095411135af58756f72480d888a24aeda166136e18e25f42d56e39", - "0xf2cbde4bb5fef74ce2342be3f2178a7ec92ec819721456687705025708a2ddcb", - "0x5ce0fddd2a1a63fbe9bb28c01ba7d1c120f2eab2eacb0dc6200fea8c170e5d2d", - "0xeee2051e5b3a37bed40c32e05f141ea33283556a20e42cac05e5efded1b046a4", - "0x0a38951aeda2ad0227d5db28597901dd2f15fb8a53027241941f705f84f3462d", - "0xd532a9afec12700d8a6976dd148621347df54aa5f78fd64581835027a88ef6f1", - "0x2e328120b2bdbe717d673b04c9fe2c66d9320f668798b9fa11c257a5719c12ca", - "0x46a2cc57ba5798731135b891a9cb9af4b1f780a168d91a9d745f5eb39bdf72f8", - "0xe350043ef30f36168e8f68da553667cccd2eaaa4a1ef85c187163b9e5ab243a0", - "0x183ba0d0cdc68e3ac433eae5822bf39e1e351bac9faa4ec279fe9d3304bc6dd7", - "0xdbbfda6ab37d6b32188cbfa975f513f6024e84364bd6c59d513357598c92f80f", - "0xa44361a80cd53311a9e2de8e244013ea6a926cbd5854920ae3e162bd25d8ea21", - "0xd832af8bb66b36e85828711a95a77a2b6f4b42a5865bebbca59209e6585e8c9b", - "0xc7f64dc286ba1f7b6ddb15638662314813113bf058c7a9065597aaa4452c752a", - "0x178b152e9820020d4245e8a997deb02767796841a0220ac22544aa1250f84296", - "0xa182cbcf24b7d477c0efb568a8b120ca341c4b82ff0fd0d6d0b856a65dcf1083", - "0xb1f0f8c06b2743e1f8149ce4dc215f5dfb89f8f1da9718f3f312e500ef06aacb", - "0x5027caa12ed653330800d4de4014e24fe9251ddbcafea58e2c27012dac58c371", - "0x35a7801cd57644a1df91656b0566c62265159106b963368cdaf429472913c189", - "0xe80e4e8f12abcd5a79aeca6efc64bafe1893075bbec001338f625642fc047bee", - "0x994fb4f27b15427028aba87c42b08eb0919e12870a05405e0ef8f2458cc2b279", - "0x67cc00cc4591d984d9c2270fbdadb8c0d4ec9b40c6879aad8176f9cc7131e31b", - "0xd73e02e0c536cabfda569a8da06c99a28f3314d2761e7c0271aa5480dfad3246", - "0x8d709e7a8435b1d5507859d0e4406589b03d1b3ca646f9f26ec3004c6b2d973a", - "0xd5b64c36bc4614f9ee9bdfadab6c095ad183a8cdfa87a9e6858c4b12d9067aac", - "0x9d1d6d5c1eedbffcfcfc2af6aa9a6df958897cd81cac630dcb998f41a6b14638", - "0x39a3945ad321e02ffda18c8072e74f0dae525a57c7197dfeb86453ad208ecd98", - "0x878708a865debc222b72daa2611b7cb8368313c05f7f41e7f8fbbf33f602a582", - "0x305cf1edcfb804a104d777371850939c7c028f50d45f2fc4791524e24b6adfa7", - "0x6f5dac19867a9f14de66769c9a8f0813d7ac7fc4590eb3a862cd14ef63381491", - "0xdd7d64e12a1391437a07fa88b6ea7184676cacfc7cc8db544ab1808dfae8c87e", - "0x1e97a0e1992b75202522362a4edd8004a430f99c024a6503105cc55bcc7b843a", - "0xcc78f11a9f6996cf742875333c8274c68800658fd5a7465bdbf9c13cf2cf72f1", - "0x1fcfc22ea13dc96c9f8f3546f3f1b8f458bf7d1d46445c92deadd848bfee0c09", - "0xaa5d6969d152ebbb075a97c4a051195db68d90a75c37c104321131c073fdd843", - "0xf4f6e0c5bd269c7de9d1473c5074b3bd4d42b207742b918ab1481c633d05a38a", - "0xbba5eb96150613be2e45e1ea8ecad915415cb1e091a85b6858a7d1d5a8a005f4", - "0x1cdd61d797c519d6295196df06cffd0c9819ce0d9315c66e5309f14eacab63ce", - "0xb49771b39eefe9791d25ff5a1c94f0dc7d560a90cd9ca9c997625c05c0299783", - "0x5232fc02627cf406984a2f4f2e3813ad6260c1b07da797c2d1b7218d7eb05ad3", - "0xecc5ee2ea8e3956d964bed681f61323f608bbfe52397b5b28ef26f660e3c214b", - "0x49ce943abece2026b0c51a5ad5bd35613c92f480027bc47954eb9bc7cca5bb0b", - "0x27f731e278a0221daf21ab225b0297276db74ab79edc4806aa6a794e730f615e", - "0x55a9b21ac8387ad6aff87660d889be69d8b506a5de230043fe5937d0bfa53771", - "0xdd2ba4ff24bd512d48b553d031130799e46f3812c4b782c9423235ccebd7e655", - "0xffff9cc54e4484d4e70a6b3770d11fb9e13e583522ab4bc376cd6c7498021d65", - "0xb4bfa3c62b210ad81177faf4f6a170dd801713e5f9e3e4bcb7301c81cf60d083", - "0x5c3f5d4c0836dc02a32d7059c9598bfe46a813684f3576ec24aa8215a5de6270", - "0xd6d8e57c79dc17f19eebdce62de1adc0940d881d61c4a1099d51e6cf0b1009b4", - "0x2d3b6873546a1e6d026249e0cc45ad822fbdbb2ebcbb13832b7862ba9bc09982", - "0xd0d2b37a28cd798dd9d1a1e0892e86d90ebd54e6be912ff122402f6d923becb1", - "0x0345ca4471a47d8d12421bb39b0b0f7e8475d04181abfaa9050b95cde9aa4a8e", - "0x78a603e3f62c47845e657de355bc40d877612062b2f42b63c50645f3c12c4b15", - "0x1e973f2f79722999a5caa72528d1920917d7e7eb6839976ca19165c3a8bd26fb", - "0x3f5242f783e2336e5cf0cfee1427fbb15a833930b22187b3659367563a529039", - "0x181667e480e8017caf91681eb04cfaf0560254028a903b620485cb9771cc26d1", - "0xeaf92b93fcc996d886bca5fec1f6a7dcdb58cd7acd8033637a8fd9d4bda169b0", - "0xe5164f795fac046d3106dd707c4628d731bb73a3fed8d15ecf915de5b2f0a48a", - "0x556f9c42a01e441bf0e74c4a89323bdea6ce402dbbc1f500d5b00146be5439fa", - "0xe59b25cc5415f5b84b41ab7085b854f64f6d839085e85e6b2a30c41e6f689494", - "0x9e2c804effa40cd7a92ac8901798c03a5e66f47496cd89fd3b8314e7973f3d06", - "0xad4eec2bac36a3ea837c95bef538000dbc75374a4c4f8ffa7d2705ba300aebd9", - "0x12212ae8872a77a5d57a4f5fd91797c2fbf64ac2fa5af09aa55742b3950d8e88", - "0xd987ccc0c413e4390b5b5b69370f54b5d35f79c9cf4c1c534598a7e383399ebb", - "0x8d30d2dc8afe7f50e160fb4d3cfc5c54a962f31ae5b4f65c2362ceb7b8cc4733", - "0x617cb9b3cab2b8c43b11bc3412cab6f889197ba608c068dba2eaf1fa051b0f26", - "0x75bf3d53043c8375f1e51194be0942f3ec73ffbd61926d33910fdd00dddf5b87", - "0x72bd67478fa033abe7905125b728485f401aaa31694bf9ae7eb1184adff2db12", - "0x23d7a73706b970688eac1e8affa5524f0f563228aa7601fe7d790f844c186bbf", - "0xea350aacae57df34a533519b3448435879faf637e3f4a7cd3bbc891489d31caa", - "0xbff826ed9cc685de15275d59a299f5272cfab31750d39141e5ceb8f5d3645741", - "0x020ecefd03dde3543cea261d265a265898f6804230e95f044360d4aedfe031a6", - "0x71645432ccbc0826c83dfdf044ad7e3b2df4bc979e68513ca382ff93e24d1d77", - "0xfcd3731fa2259ace8cce5eb1a3fd74c723f997cfd8d4192105c41f2874d22d55", - "0xf13985444c5a6873217da182e9784fa87576829b9e841dc209a2592f0f5522ad", - "0x0e065da66efc90bd88217d6e9a5721280c4037de294d9e6ee8c1207b664217af", - "0xc6f893863ef60cf57991974de8edfa93c28c55c3af96f94cef9360be9a3a05b8", - "0xae2bfbe04bfe046166f0d6871d689a809fe443c9a6cdebdd5b2e32a36d7fa67c", - "0x733fbd82f21056a2e402ab050e32cc83aae96a67dfd3fa9c50732a3fd576e7d5", - "0xf4e29375a5f989dfebdbc9101d411f062c1090a9869058f6b87fe19b3a259489", - "0x95f902f47c21700bb030841890c47d759dbeca59983f1f2d5353674a4f45088f", - "0xb02a6050eaa94106ee9d4577b10f6adbfe7705a3ef9c3019412c4f60ee8d0e1a", - "0x20d64b55c9e4c2eb51e5af544ed068ce9d323c194c5b18bebea4bb0182344ea2", - "0xb6e40dd521e524b30139719580bd7441a35a1bc304c338c426b706673e179320", - "0xc54752027833b57236b5f043fed133b2461f199b75d52da8578c6e98dc2fc5da", - "0xfd3f9c65d1b05ccbf980a35cd433da01443ff8fc7be680892ea3185914d86727", - "0x7175b164364820f36d10f9e079707a40b21004d0687f90bab1b132c9c34ee2a9", - "0xe4c4df954bf0d3d0eff516775d60cfd29d0e780da4f07589ece3f11ba8925b2f", - "0xa927f17776d750c0b6996f1d8d0db22547fffdb006c0cff367d0a0edc41ddb01", - "0x27050f4807ec856a205518e9d13821b2cda0231cd8959f47ee34832181eaf8fa", - "0x4ebe3a5760406a59770cce09a0a44225dd5a2fade1d877b219558029e30ac4e1", - "0x290c328e60c366b0d18e55c5578b9b3bd83bf1b0600faf0822a1f6f1ae85edb5", - "0xd7bab11bbd83da583e3fcceb0f84bb551cda363c33c67f93e02800176c29bcee", - "0x3cdcfb49d11cfa3ba1809271869fdd9071017e5498f8acb01c595dedcd3ff109", - "0xc75582264ee66717ee40ff52a6689d03121ce0f1abb2c354cff6d92e532a81e5", - "0xe7004c5a4d09fc89e8737a5535221b552e67c3a0fb5851021e9f9a0de4672ab1", - "0xd6b8f94deccddb6d4f8b066c0302faa4b562f7a04e3a092b7d87b3a08485d933", - "0x2e726cd7212cdfd26cf0d928932b05ac8258003254e6558c87eca8a82cf6bb02", - "0x6c8786a64069e3f422dc5000e6a614adf14215ebce0aa8ab8e22c5f239a3714d", - "0x2f736729adc62370324b478cacfc6256fa0cb9119219a2a000d2c34c3293f6c8", - "0x8afc8743b959ab340dc86c6ddd9ca1726cf4bbd13054616b3d1d64bf656e0827", - "0x661832363afb109885055ee90663ea7514059a2c6d69144f8892138e89ed037f", - "0x2279ecda37192639297242d97723c6b635456cb2c98266493552e618067fc5ea", - "0xec7b510975a50daf40effbc94c977f508c84a9712cdb28d60c80b30adb3fd24c", - "0xcaea827e123419fe48b7f72dd67eb8c3965a4504f13e4ad7443bc8e87bd7c832", - "0xff012579799171dada6dc819977c222836b9cbb4f521142e74e9ef0762f73131", - "0x70ec4b383c92a8bb37f733825d60e76e75469a71b1d5361a84e37b38e3b8a34b", - "0x4dc584e8bce8efed0a55021d334b87dc5973de45effbc43e84acbfc03592532a", - "0x0fdabd0093aaf388fdc53d49570d3304c8c583415eadabc98635dc472f366b90", - "0x1b29d10d1443bb37e47668c9b6a124a1964a3f12075ab9be1620891579d8db31", - "0x3d350137648ba1e593182918a4ddb6b5983001356d6e406bcb88753449ea55f6", - "0xfc3911c59399e8c329db2dadcb5f7a58855f19401f513b9f6bdeca01e73edbcd", - "0x89a4277d6bf214c50fc0a7219ae59bdb719b71094d6a02c4ef5e07b50501e74e", - "0x7158b6d3debe3b6ef5dc0d40616691c804581d5614714f2284e63df42b5ea578", - "0x46d73cd458ee5a26724a68b2f8186930104aa9674fe4d94ce75215c61d0ede81", - "0x100c71337ae5379ca5d43f2177f2f6e6e57c628bbae4bfb70abdd1f23279787c", - "0x3c9e2e0161341c82207498608091846726ee4dba0fe1fe69c07a92a0ff7675c2", - "0x5c4885fe3f85ee61e50378943a48732843d47184619f40954daf24a165f2ba9b", - "0xce300dbdbc2c8eb91c75f7e0e8e91dc03ba8894478a9e65529241ff462abf713", - "0xebcc160dcb2f38f4607cfd3ae74a15286eb0aae05c8fc4f87abb751ca88a699a", - "0x91aa4f2af47471ac51160d0d1c81137c7c6a22ada295356668427a45d21c6a07", - "0xf30c1a0842faa5df4ef896e192d5902445ed7b01cfc83dc287492b1896126310", - "0x0e069a0772dd736ea68fbbef47302ed6e802868d0847518ea5ea2eb34a79705a", - "0xb49c6e624b584d49ba226f31611a0c4f3ff0033a746b370890533b209da2ac3f", - "0xf2a4cb9c4877795e0ee2964e5aec8828941bef07777ff4db07423b4a2eae5a31", - "0x2e9cee7389233b6b4937f5997be0a6f37a8c347c58886b75e3013060c10c4a82", - "0xd2318412b7dd0eb4f6b3da69c2c3cb7d8a6b7028ea8f0a39bc3bd513ec7c6d3c", - "0x74c00a1f66bbe517e7166993e18a1564d43877110c54be28053321721e619e86", - "0x37de40b83aff733b4792e27dd6211687ad891f8d95e397e3a3e07d481a5c44a7", - "0xb607adc39f83da6b0a7da38893a95cf33582d836c967496ea7636e61c6cb7191", - "0x518708d6d86b3a15dfd50d0003c340998be42ff59d533703be30763aa080c547", - "0xc9cc7282488774cbdb9d6511ebae8f6eb9947cc5360585d75197532e37e9c7ae", - "0x9c73d1c1a937f888d29da748772785d1a8269282003e7cbab2af4140039312ed", - "0xadc5c8b549e56222f6634a308348eda613afa171b5991ede1b127f241d25c570", - "0x0bcab0832ec43772bb1aa95f8ba9eac83b4b47bfa776876276b47722aaf5c3c5", - "0x8b37849d079adedcba7ed2be598a6d84464ccdd807ccdef2d711e5ef7bdc003b", - "0x8e2e677e5f20a78caa0ab3726528209bca791551d0d33346144c5e7b1c8c6bf7", - "0xda0666298d3184c66e54af103e988fa0c9a80a12e32921e5bea0233a8638e23a", - "0x1ffb4873b85e71b067e3ac99bb26e7f5ee5e036bba4835989f1477ac8b03466f", - "0xfc89d4900e3e7a475b8923bad15bf68f4cb967cb50fae648c2efb3d90cde84f9", - "0xb62eb8ea8e54ed6358a752943acd7a2a1fef9553533b56ca17f3cfc0ba719ec8", - "0x795e2eecc65f357e0b7a9c97d2c028b7503524809adc6e783f05eb0423362980", - "0xdb06a90870bce8df7a88118d485eb691a0043c695ceb64af565ed62d37770c98", - "0xa28bb65d12c4875edd64e643da53c1b65a154d9e40a4cd37c6f770940235740f", - "0x136902f6e7104043a9d828a027df5f23c61a7d085e49907042c55d99d72c16d7", - "0x058424a9554f7a52bb6516ee51653aee32ea09d96247fce31dd8b31af52a07aa", - "0xcce2dbdca699f427d620870ae23b96b38da3414c57e71990ba8af36a58e6b936", - "0xd1feadaa1927f77eae9bb7260fe84ada5d524bdf1239a6e688080da9f7b71af0", - "0xebe906165c9808c0b368392dd2cd950b55e1f95394d28255d7cb9848bd4f3839", - "0x639198f58b4712fde6064dc49f73b3db52726e76ab9bdc00f6ef9a0487fd6d5b", - "0xe9e2f1d0afa3d3b0883fc7269618ca1a50ea5aa828ea07f92fae48b2b2fc54fd", - "0x811ee53510a20a012a10ed6a02407dcaf98099823a35e54849b381d4a6e5b5e8", - "0x221c81c3c384717eabd545830b62427d11fc62bd1df260921a8e121456d719e0", - "0xdc62196c03031dfd983ad4a211a514e25c4c97c9cd9ca62ac18b44cc0da4e0ea", - "0x3f60edc7443b2ae2a4ede7f52c2c4de9d58f90d6be2f0fb25772b51c352aee17", - "0xb5cdbf0eda83613a613ea26b14401361a2de1be89412367f39d4ab01449a87f5", - "0x10d0156c9a828e9b8d61e615079beebbaa525a07f32d792e5a67d4f03846bf4c", - "0x4be38f1cac74f26e2e5bed0d2b416482a74aa425564ccfd3e0ef10b5f57a7d59", - "0x814aa2e560adae5e7192ada4ee11567bfebf3ff8a2a93aa8dd432df30c77f44f", - "0x187c004ce45887043188c222842da10e062a9be8a3db326d3ebdf860209e723e", - "0x60036829b2cce1932f2d4198b5207d70a2e0634c929a63df122ea38f620f89eb", - "0xc323959ae94b9d4e5246a35ccb887b8fcdd2b5de43ab581e53fd3090b4dc88b8", - "0x37a114fb8c7c973708d5dba9c37df501db5e0d074c757954e52ef716299239ec", - "0x8cb29fadf7071a43d8208036d986abe64a1974786c4ab573f4dcca88f8db83a9", - "0xfc4485a7b2ddece8cefbc0f45c949920b9412a47b3e21efa9aa381e0cba0ee72", - "0xfdd52d72e5f42cc1cf1bdcbdc97b15f623b78d78425c338fe588e890be995597", - "0x92e26312a466aff28597bf3444b0bd298b12872f099885ab10949c0d263446a5", - "0x420d6f0581dc5c914b0da7339fb69bb5c9094ea8715c778e9dccd922317c36da", - "0xdd8641edece5a0ab2b024b4ceefdc3f524cb0cc9549c200528962eb0fa341310", - "0x740c0963ec5c29ca9fa48445ddc604bec138df538edb18844182556924d09fa6", - "0xe66653af6368613260ba344cc051d6086a2e08331e8bc5ddc2d25ab06ead9eec", - "0x86e1aa0ab7a4e47828216a25073d6c355e2f4ac4773cf1d2d859f62fd8fb8a24", - "0x3be2cdbd947172907f5605beb7863e2edc5f176e0960904550358ed6c2670704", - "0xc7d4869adae5a7593dbea4f802ab068044dab40fe0063d5b7c7727a3bf288525", - "0x620611d92e256bf7cc471c8883801be5d64fc8b77c1f39682bf840031e0dcd3e", - "0xd89f0456b5a3d041d5309ef623e259003383277f7f509096ae5dd02d312543d6", - "0x7d91689ff4e212d6f2c7d62cdef0593686ba04aaf455c576535d097120f1353d", - "0x568a6763698a23b4b2289b851b46907ec5777284741d3a7304d7df297fa4129e", - "0xb9dec1f84c9b71d5939f40982fe5c88d15f868e265d4fd9fa0560c2b64191998", - "0x6620f3e9e7fae45ca0178116a9e478f95bc0a69fab3a8341dca673453741bb6d", - "0xbb418a7f8415e5378cca2ee7056c6c78641b085e30bb9f9da033ea193bbfc77f", - "0x002fe11d761d3135edbb3dcb50533a1b143593145f7b4cd914d84494b1df652e", - "0x5f76ba2c72c3e4d93d98668455def3d335b03f517648c6cfde1d0a37178a0ec1", - "0xda49060ebc8b7963a9ff4095c9777d902075967d75c47ee3e9f86f03031b0a3e", - "0xde325b667d85828b7416d437a91c19a14b0217323650dd5880a8a40ea85f451c", - "0x9c14e5953fe56c60599dea8284b0948603334fc7aa795cae80308eff4cbd67da", - "0xa9e82869a1be8247c665d86f52900784005853c5249393fadabe37d08752ac9b", - "0x3feff38ddc1917cd5cace417b4e86762126997a5420ac2cb32abafa769f91ac5", - "0x01144f9a6fa0b4396560cdee8bc6e873bdf3ec5c3ab3f7409e59884e6d2dbd05", - "0x96778169c9f2b805e67cf18324c292c8b67c57db72aeebfe5369d91aca0983b3", - "0x373f81dd55fb3bdc9de4c42028a5bdc970cfda825723b6f0f7804b50a1754bda", - "0x3af64226f4eb3ea6bd6d0dae027bce6b361b30c50b4a65a4e1661577268953d0", - "0x35b96aa54e1c50049a0c91281c75d3d06c4b609a2d3d92cb22d1328369adcdc6", - "0xb02c9650fc610b3f5a0b547e8bc505b4a840e1dea123b4cb2c8af6232ae1359f", - "0xae53d65eebce31c14b787f1df5b021334312e8d5b8302b84f6a56b6608e25d97", - "0x4901a419abac1937602d98c21e937a03db8a48d088e56895788da845829225cd", - "0xf5636fb8f3a381476267e6713f10763cef51071b6d981a8f661a24ff6d368e71", - "0x059e8319866374d8eb8372e71998cc9277c5f5e5968c126a81d87d0d7a6fe8fa", - "0xd5fbcd3d7a4387000e014e67ef61d4a7ce09a70374ad4fcdb6ca0b6b50b13ea5", - "0xc37ef88d74d307090a0dd49b69d5705c8e65ad606548b0880eef33b8346c2af8", - "0xa316c8a8ac949af5957fc905ba39c247a477224cd9c4cbabccd0671dbc1e17e7", - "0xdb313f10f0dfb56b8684e6ded5e89c4d5a3cd958a99b1773680db8682645b9f4", - "0x0c81ab956b95426f5c327c6ac6db2fbbaa8fd0f41cc9f58971d5089a0f3c52c5", - "0xb09827a7c1bfba1fb7e5c7acee1da5d8fda1c10da19046151f55ef7fefb784b9", - "0x4332b07750799b436dc5b8521feb0927d7e59b25832ac83876da4eb9df333aec", - "0xdf6895278968d74b85e4bb2c53d70772a6176322200f864b004ed914bcc3cf2c", - "0x9a4a6427c038efbb77e255b8e74df6f897590f734328e5be0ab6b16a6ca58e46", - "0xcf683bc5fd50f6c444a84a5524b3e6ba381fe62c39828650f8c70d9b5b6e8d47", - "0x3282bab4eea18b9a8147d6dbf26073882e7f859b498b2d17608cc96548f5ba7f", - "0x04684c3e47911134ccfa142c85d283f26ed315b5818a523a79e9be31c17b4dcc", - "0xa8efff633914b0ed52a2eb3b75d6efbe1efb0f640136807db13d8809289e40a4", - "0x4502554b0b48e3230e24efafc4f8eef7ff50347cea1a27a15ba46187ed0fb2a9", - "0xe0d6508774e4d8772bfed391286d5f13b986518dfe49d6cc9fd55c415e2382f8", - "0xeefb070835b6cacf8237566bfbc77b177ad0e3e6293587d76904f5cf56cfe62d", - "0x06162414a2b278bf48122286cfd04e83ab148c91ff45d5b90bb3a55d374debb1", - "0xf87fcab4bf30f560c5359ee594e263859f1160f3ddd4186918093275e271d520", - "0xf3ea8332782f88dfa789e42eebba3ec9672589d090c557e36e05c7eda9c7372c", - "0xf05f9c5ec95884e6da30f95d56ad58a025dd696215ff57683d6946ebce25d497", - "0x6738c17f58a1e7d41a634984b98a5efa47e910640da0fcd80d2ad0603c0ec9f9", - "0x8c2029217e14a9bd4dd88eefbe6ddf886d30c56df42035464a5acfe567406dcd", - "0x5d0434b926b112a81e7276b96d1dcff0391e782dd68003173a5d11701a8fc190", - "0x4d9a5c9209364544eddbc545822bea83af527fa1f7b24dbedbdda61b69e18250", - "0x3e5800a59c58a56bd2cb7659db0705f45b6b8e827cf06eb0fe98586abfc83368", - "0xbc3f4d3a46d5fe6df5ded66fc47b5b73baa878c9b8fc3798e87a17995614c515", - "0xf715cbf347c5308a4d50c99eeb4882c6244a03ca77349d126337ed3861e454a0", - "0xe831c5b327832f9440cfc795e7f703cae82648704d05f7bd41b2acd4e07b2c1f", - "0x46c6926581d617ac8384583726ec7088f1e0df60bb075a1b43ab51fbdaa108b7", - "0x236eb9d731c98bc8407bcb9954e3297c51ed6a116f90f6d1ccdd8f4c89bf5dd0", - "0x6ea6994bc6b93be59e0e8f48e32f2afbbcc653d4c1b845625b8a5e280db809bd", - "0x3af35cfbcdd184c7579a273a73361271ae9e1a97ef89fb1f0e6dfe89a776c237", - "0x7553093da896b06cda700a41c0233274a089e633dcb5fd8d81ff0393491db2c6", - "0xc555ae61e8f86c775768be4a07045042a8d5898ca6dac14687327b89303d9ad2", - "0x4fc388b57b8d35c704e8c03be70f1c9ba03e638ffac704791c0aeec6d3dd8e2c", - "0xfd0ec61b6a525012d03781c254697a98ac0d4b1e55f46e3aa1ab819e7a90436f", - "0x1d7e68aa18a225ddede2856f421613f88872e89a7024919590930c43326a5bf4", - "0x37865c43f2db20bbfd51a1440e7cf95923c6b980ac6b7e3173d3f867d60d9b32", - "0x44de3419b85216c1d2ab63cf3a002c28835dd084c2e3ee0eb4ba92dc49d5a1b5", - "0xd5fd4d94d504e021eb7865b8aff7ed53c6afa96970fbacd678c2b78fd8c5badf", - "0x479043958f7b84ee3c20a9503fb10b3ecbb895b2b4eb4f69b3ea0b5243f881c8", - "0x683669e82fd55e4162c00a97b2af897395e3a1d3dc51c7f12f4e01e4e3cc3ef6", - "0x9dfc309c9a3382fad72a40825cf621e1dfe095aa77e3b00bd5a0f0c5445d6a34", - "0x63def4af1a927f696cf1abaeba3c172de583cdff6278d8db46e23ac8063ac042", - "0x682e98b0482b1282c896bc2920ce48328246d8d3301b5059988325950f19e0ae", - "0x929abbe6a8ebc0ac32f5ec6a8c1f728d733284e81c02477f606163521d86f642", - "0xd3deb41a543048541beb8b96a1f5b029da6ddb513327d025a5176e95dd4ab1ed", - "0x886432bd185ec9c30a459182ac4467aa9c0f590de8825712c8fe3d9cf7bef9eb", - "0xcc9f64743d0672261a614e9e5c09e9f8416d754f9c536e456a08ef885569a39d", - "0x55bd4b7d2d2ffe8ffd7054d1c8f912119ae793a23dd2d9a10f0ba94ee4b8ac9c", - "0x00a86e2d5dcaec027c9c49853eba467e910f39b4cae50bee85c40b48d34313c6", - "0x89ebc269c4a123f9622e89fd221d22776def13c4a9637a7f69176504ed48ac2e", - "0x808d7a4793aa0afd91d35a6a46eb4f3ed522c77716fbd27765ad2104d2638d75", - "0x5766e58d79181a27639e4e2b1c141de7825e6abe3996d3d06a518bc87044abb0", - "0x53e304b2ae212fac8b059d283f8f97553cb16fb3332e8833305a32db18abb5fa", - "0xae7cbb3a3c753f730905201d09056662cbdd85c288fe94265aec6f1791ecea09", - "0xf6521ebf26b89ec9e3f437a8e3f60aee8063a821aa8e2e0694a90835b782b2df", - "0xb1e372bf9048f7e44d08b12c45b0a17028571fa84238954760da69bf7565cfa5", - "0xfce7f2160459a7e6aae2c85d9d48b5005179c05d703ce890a74b21d993fdb05e", - "0xc630b644d1083d77a698f658819ff7440c3e688aa89e18480167e651f8d1ec67", - "0x15ce3af581eaafaf4456af97352c4feb66b25aa20d3bd711221bdf8532639406", - "0x84600535f9a3338270d3055cb823c2d5a3e7e8a0ad2cc8be198557ea69d15c01", - "0x970435f2511428138573c6f1d67c860703ba5504cefcd675c0a77c37b17640f5", - "0xa6c0ac36032f2848a2ca230a5773760f2ea3afbd1294c0c8c8734d24f9a2c6ac", - "0x3cc9fcbd313d84afa7277112910029782b3b083500080ff34e10507b8b85c249", - "0x84cb76db3547f6ae1c91b7cf3196f8db451c020353776199f44b8b529c810ae4", - "0xff50e061106f378ccee04ddeaacb2aaf2af4ee90b1504f13a88ee325de268eb7", - "0xfc0d53a97fcc0a95f8681674e2f174f789becf7301c0742405c6cd28749acf3a", - "0xc0853233a2d93a9fcd5c67da4c233a248df9bd424d3765546c1ed39c42a437e5", - "0x8fdfabd694bd2266c71796cf19e8d4967ab9a42dc0d6aaac495c47ed4da9373c", - "0x2a8dc4cb7db4cf55beb0eb8662747d464e2907a3dfd0d58ce1067c85de2be0e8", - "0xf082d07fdff2f3665019d0fceece5b202c79f8c3b13d4f533f2eab2a83925f23", - "0xc155e24d5513d109af50284b49b723cdb1f812ded6d65eb1336cbc2d95b91f0b", - "0x435a39e77aa40920f0e7a67fa4547f67e2f099775f3f8a3db3dd1e48b0b2c376", - "0xc9a68f80733f9b39794101dc6750747cb6550b7858773de7e323140b1248839d", - "0x482140091ed9921f8774a0a1acc3644d2ec7b7019a5d50a0e8f42d9cc729e94e", - "0x88e75515699bc267d85f9410411ffa8ea0a852d7ee2fd2826e1260ca0218d497", - "0xee78dc1b01b7938dab9691b3a44d10497684fdecbfa4b175c3c688b22c9f563c", - "0xf7982d19a9779ea1077bf9cf6392057aa68b834ea3f8b301530484c0b9bbd567", - "0x125ef0450026699707ec50bee796181e783efddbf4141f3cdaa000ceada9b77a", - "0x3091b7728400f191e3764d5e9ecb8b4887e5e99fdc36b67d1a6f006c16e75ff1", - "0x169d3504fb8dd8fe74afef89d5eecaba66adc07f119f1fdc03b4b22419d6c586", - "0x6fe28b55a792cb900d1b830a047653e15d12374408a4b5d261e6b0daaa6a654f", - "0xe4adf68e5daeb9bedcb6329924c76eb6ff1770e93a7b3ad77d456bfb414c0b94", - "0x2ca7094ccb9beb1654ef7aac79af810e31f48e0063525e96ea4cc7548283b120", - "0xf087f8821c63865ada8f912bd4da2f6590d00a8a8e530c50d09600be91ea879d", - "0xbc106c39ae71e3d8d9c0b8869c63d21a2833cd09104a7fe13c988a1bc115dcfd", - "0x8310fa11f25180a7eaec83ad49e58f8344bc1b7b48f15ec12c16afb4399d4b28", - "0x5d9902bf98d0edfdf77a8605f15dc36c0d9fbe8fc16330099d8491450ed97819", - "0xe1715793831ca9e851f293dca235f53d1985ade89ec0dd08d8478ffac8837fac", - "0x86bd483291ed25a967f742e4eabd3f77e178be0fe9af43ce61595fd26e4089a5", - "0x3c01d8d2282c6406878471d453f97bc4247ba285ce0bd1fe7bafd21f617fa760", - "0xbfc1bfa277a866dc1b1a6739648202e15a90718c67db76af083ea57229e9ea03", - "0x1ab9c0778dfb859bc6e13cf425f29c796de5889593130ed9b591e1f854de51f9", - "0x235936c148f7a6c4875dfbf30b9a4101cbf329e3d682ec3bd2a6c79473efcd61", - "0x70c8282991bed0d69a19adf4c95312dc44f24822f59b915c8aa93a2e8bc322f3", - "0xeb2111dc14eec6dc250a382b529af8314a7d8a25bb7672c35685a53d0a6eaf2e", - "0x98242a2cef0a5d9258fde45048730a73321b46af2d218203bfd3da274bc3ecea", - "0x41de7ce88b052390dedb6dae965ef4b3254dfdb91ef0cff77ed0f990e6d53d1d", - "0xb307a83fb32e43ce73d5be05b8dec5791b55ea97abfa2916c3a118540cd8cf14", - "0x19c74a02ae96e9581017b8d1845344c0c20f7cb8b8f1cc0e298608e25429ba40", - "0x8fd7b83114f3294d9ef1994f96cc5dc3309b80feb86d7384c4797bd88914b4eb", - "0x6cbe4c1f28f393db37851436a41d739bc6d64e32553bc3b74a6d390390e29c9b", - "0xaff1d11010b3a90a863334bc65678fd1b719a424271c6a384321eaced720521c", - "0xb30947d573b684f02f2bdaf32241f9984e0fe1ccd2398013d7f55d0de0e8db7b", - "0x2777c4133201edad3b7478cf06b4418573bbb0b35a94956489f2c207a8ce07d1", - "0x852612bd283bec671358a009c4c02f57d7b150c466a673ffb7094ab8167e195b", - "0xb9b88021540165e714518c5ea97152e7a57214c2bb30143d0aeecd650b69c011", - "0xfa3a3d52ffe85f56b28a7d3613c50ab16a98dd0e5b14690676a0917b0d9821a6", - "0x1b4251f32144ee3ff35ec96c13739bf11940a7b7c0ad7819fb14508e37318179", - "0xb47b35b1a9efb47e5267e75bdd43cca5730b1dfe7c74fe70c854c26bc67eac20", - "0xcfcb740a8d76d44484ac862bc7bdf6b932a76a1128c77f4543e3310ad408416f", - "0x3c01a05b0ca98646fdcc76b2a95ff1ff958f61aa6f7ff199bdda9e354a7de21b", - "0x337c18196aa613d56cd098d128126403cb03b5172175f9b0cb3599c76499c358", - "0xe1773100b144b6612c5a811e80b6c4ce002ca6c0afedf9e2c82228c0d499586a", - "0x7f10533e666fe853571ed79da196262e8a69c2cb99222d26e0165808ab6d7582", - "0x2532a4dca10fcdb5d33c84c0b295dc6bddaec6f29030f4a74585a7718aa04bd7", - "0x1f6c65ac839c5e76c955848ef97b3aa1a095b9b77faa155278f793996cb8d438", - "0xc80d98e457a316951cb28828744885346b14e545dee625ce24c062ae30fb7867", - "0xb12c58437021718ad76e64e66259186a53421366acf78f871d53ecceab7bfaf3", - "0x4fced8fe6a8c19e32144376daaf080b4d6c8a77ab8e7f91d099881fbbfc091fc", - "0x1c2afc3229797fcaeb2df0293259564f45b4cf1fba17c2e5e266747fd24472d1", - "0x087e44d23f6cbc2233738ab793cf30306c222445e2404e1346c1609ccafae040", - "0x75e290747c7001e7e2fd8d26ea54c3b733e697a6dc5832e9c518041d58810a31", - "0xe7e457e551cd6797d18ce084d19d4d87ed31d07cde475f2e25f5ed1b750c2c64", - "0xde5c26097beb3a1d3212c9aa0de1970f599a16ecd9197abee5b3a3b281e57cfc", - "0x45a1df846c4b8b4e2ff8d674d7133bac3e98098ca6b594714007d2e5f211950e", - "0x6617fad5c634c52d5fa9118e6cf0a53d5f13eec7c1ac9220f15ef53746c82aa8", - "0xd34a1393ebb316793e8fce4212a9853a4f44710fc9a9838caa605518b4fca573", - "0x3e8a846225e07e62bc18df6e31929b830aba74aa96fe661d5cc0b100fd999b17", - "0x3193c7cfbe7d2ecca6d37e6385d69463362d4b12e6f9e4d2f6ee996e6413afdc", - "0x2ce3aaebfe489553bca7435ae95401731f4cdfa2256a570f64abfb407f7063ed", - "0x3173dd10922eb73900cf8e32f91dac0ad05d347c9068905b549f743a8f16a751", - "0x62fd37c8031ef4693dc0de52dbe06c293249ce2ce4b3be0b7a02c607bfded1c4", - "0xc2580dae73e6b96af85676116914b8a247ab8b368ebb1d2d38640b8a726fe0fd", - "0x8025d1c443ce28c8d35974af5e31d934bb3b08cd10a24c95bbd1efc78bc7eb10", - "0x7a05e4e6aee417c062142c03a47a8be9674d57b445ec0f8629936dc9f5bfd52f", - "0xac55c7dc4cd5f7a90bfd9a386d598e95501b8aad159cdd0d8ddab691eba76049", - "0x3296a64eb4b70230366b27a448b42177c5cee1fa7913938d4767696a6570b4f8", - "0x478ecfbe71160700d04a9052cbb91d62920db80489534887c382be5399776faf", - "0x4530a0a6e3fe3fee4e79c60bd0599c76dc82e841523ef667885d4b1cd02ddd4f", - "0x25d4fa59ecec69d0ff70e70fcba3c7fa6ca5b0689048ce038141293775a8dcd0", - "0x03020dc01c0785c3e3fed74535db3aa3546916d25cd49876839632a929e51268", - "0xeb9ba2f7ea0d80cf7a563ce5e7edbc4ef638962a5a4c16f74ba8feca76668a0d", - "0x6a7a46ffefc8ff2edaffb608a0b7f57ff514398e54f4dc10a1d4cf218b20c7a9", - "0x5bc4c7f447e2821b1b4c94e2eaf754e9478c50d2488d246243b524e8addd18d3", - "0xd3eed3c14d29c81ec39a8ff6f75bbea5a8eae9f713e2e2e54bb6b824ed05028c", - "0x39429d962be1e6aa0b47cef6fb92c3433c54775718d865d5cf7110463613b7ae", - "0x51f19c8404e99f31c464e2cdbc5e44ed0d9486cd0efeea7894c740fae39ea641", - "0xc3695934786b04d41b3c5da84445a06e1fbb5a20e344146c1065845acc9abcd1", - "0xf177f104e6bb76e81e553ed80eb7064d3e6b74b2617c96a446ab9742e2d62002", - "0xe827aacb085e41a63526fa9809b3044f2a5186a04ef4e257dff1621c9052cb32", - "0x9fdd2ae7875a3129242552756462870f1bb831077f48b436608fbc9c11a0d629", - "0xacc8c222a52ca74e17aafa6f0f1d962107c0ab9ce4445a429ad955692d7c7aac", - "0xfea8015b875507e5621ad868954241c2b6de02545a2acaeb2f9109fa30384215", - "0x869cfeeb4898e17ab4850bd5e3797bc141f4814e5409df02725d345b57c64ad3", - "0x38b6c1377ada6abc07a7e7f3a92779907ce2f1064abd0b9be038d6ae51a4b276", - "0xf54091e4e9e9a758fdd8b00b9a78b4c91e17c01ce305041b4bf69d21c743fc45", - "0xd230e2b5c01fbb2ae97c27d70a8a1264c003e815809fb4240f7b500c63bfe82e", - "0xc126f31bd32f9aecb8012f206c64e6c05b60f863af3a9225c8971903290990ad", - "0xf6cdbf25100cc3839c4944180eb9f3f1beec798737a3f6f6074e612520d3de30", - "0xea3098d0600c35c4c62b440181092fd61676c207bddf3482c31bcd19fb352c87", - "0xc0c9da7c976544e331c40b34830f208b9638670fff5103d0c800542ee3f81b9f", - "0x5fa65f0d1c70d8340feb251a52e955a10151b0cb8200c00514b4a8df1995ca8e", - "0xeb44462413e46dcd9f49c3d5cb556eccc8e881861459fa35d83e256e7e007dac", - "0x3e44a1713f55e1f6f9130b8846dce91f710c1fb8c77298de2d09425c725c04e7", - "0x66fd8f8922d09de2f4e9dbd8d45c04a9461bdb5d88c5df9e897acf165f7634f5", - "0x6ad50a6f3f0db27728a9e948bd18db4d8ad025878bc1401fe8f9786446163bc2", - "0xe93822a027e88efdf7089adc382857f2fdc960c906314284759b6b5b6d3d4181", - "0x883772056f1957b772374333addcd927ca88de3f750ef89cf8dd4ba9f6f212dc", - "0x588861a7f2b707bdc3c85168ae2116c9775321b2d8c6f877309fb02e17a42c84", - "0xc8a88e9261c63b7a96b2345b7ce9d3a9b94d99469b68a400d73d808d84c5b0e0", - "0x1d7e0b13963d58e279ef24c9ce3d67d010dea519afc4bfad84e518a719f73755", - "0x057ab7682cc73ccdde80b76f64ceaf1702da7010953691e1d2d305daaa0e2770", - "0xb3e06d340bedc968190ec1aa0f7c2798472bce88eb07e016ab81033037b7ca96", - "0x6c4a17d4fbfd0ee8ced914ba9c2c83e394527fa36e2027e1b22cfcc49f069de7", - "0x9c66d88937ac802e5fd961fd0da8f5e493de32f6ff63bb8d2ab785e8f87192f5", - "0x3fdb9093f1a679d472e4ea81dacaaec8b3a060f71422ce815e3318ba212fad97", - "0x17792f6c2d5c2be0666702249fcb36904fe029bb7f6b1dca95af32182295f3e1", - "0xe6aafe170dac781c3e576d7616bf963dbfd5d1d27738e0cd6fdd759e6174700a", - "0x6321683aa65da9013794d27333fd3b7d570c4e100156d44e999a930a634a3cdd", - "0x8aa56c35f7c70629a4b9272b1391e13c080c97c03b3ed675bd46737119f341b8", - "0x0f8384a649c8dfc242484c669ac4a798bb19b89f85d4d245b8415aa4dad0711d", - "0x041669c526354ff960ef721d72306b3a9d5e5a59501d565aaf1036f8d6d1aa0d", - "0x0af7915a53ade49d28eaefc8f43f24500a5a12acd06f23c80b8ab675220ade3e", - "0x5aeee4a33bacd934feac2c3e6a46cae5d00cac6ff8fa57167100d66e5ee73456", - "0x44aa02ff76be04a0ac59b4491ae9a6ae8cd475fb058dbd4c94220d26d554126a", - "0xed5b8883a073d192663e45eaaa55eaa840155bb9a201065cc61eefe2917c6ed2", - "0x4d856173cdfb61fb83d755b1957e39609a915c45f3b29950acb4cffd012cc3ad", - "0x50af3f28ce9ce9e95f664d2fa59afa461e88e949a0584407a92c113828248844", - "0x2ab900c46ed0980623eaa1cb61df89ff92d949bfdccc568ccb2183f83ef622a1", - "0xf2765636da2615454790b314148be63ad8008d0f6880dfc7767d36b88b753c31", - "0xc5df53c2589fbb388cae91b2efd03c1c707615e05e12f3e681def70e0d913697", - "0xa386bbfb9634a6f910e7052e188a81c9a6a1a318d340703c8e29dcee4b57eea6", - "0xad19889349d7d41230cb039ed46289e7e9ea0fb76ef2ee427d5a05892fe680ef", - "0xda6802024bf8ce4c2152fd2f1c166868159a2c4c70df00b28ede48644ee731cc", - "0xa563da16e6fa8a65b523d67399fdb3b5492508901037ad76f5f41aaa174a780d", - "0x9ee367b021d6ac2e516f16f48961de800b89f9b7115ae616b52d746b7ba8ef0b", - "0x37aeefeb48b2613b4afac289022e1707da0c596d6d8b85e59505ec5a3f47b7f5", - "0xbd0a3c7cb0d4c60411db3290cd2d0cda5c21cf22d9e36b8dc77fb7f572c806b7", - "0xe5d875bca8c6da0c24c0b7ee191168e22af7c99d3b7d58246eb1675bd3c090f4", - "0x9365c55d9be42230ba376054ca9df4346803e63ea4cb683261de4c497f92b329", - "0x0b8024d771e5f167db261b3da6f16e745198fb5ff550c51e689c3387fafc3efd", - "0x9cb78e33aed15fa5b6094e978883d147e8329ccd7371518cbf6da96b97dc9f70", - "0x6f6dcaf8f0ae184c7c065e322e390f27e27f3c4c554158bee433430aa5319108", - "0x0c729da3730254b379cc130776a9ea79575e7cd88071423a0635aa18f4aed0d5", - "0x0f918fff241f4492e630717a91c58af660b02201a64bd14d3a577b1c9390f5bc", - "0x6a33af88b454aeb3fcddcfa5043af313158de701d5227915e2d835bd8f2eb7db", - "0x7344a8e8ceafb36aee8bc1e3a1ca73168f583ecc7959e72b8a223fe252ad60dc", - "0xf457f0572ddee6c71e79f5f8d0643e4b59abd1798ac9dc5f158ea75efaff389d", - "0x0cbe584ca5e7a45b7fabdda306eb5c0f9b015cfe6f30ca451065bb38541b2bfd", - "0x91f35848c79274053eb0294be8395082ce4e5c08b5e6560fb9f3758609066ccc", - "0x4999f6428a7e0077f71eb127218270468f57561da1799470cfd736992013e074", - "0x042f9db538d5b8344279fadd5d3c8da5b4b9b455ee1d94aac1606272d052eca3", - "0xfb28b022f556e4344360b479d809cdad7ba7ee86a5e4943928f1dcf3f57a133d", - "0x687178fcd4ced35dc148a0e142276524d03240f750fe5cfa3d034e3fb9fcb4eb", - "0x441172f8e8b24698a50668aa6136b489bc85b7b21648e29f8ed1683399e7c56a", - "0x5f8c54d03b4fd64a2e5b526058bad1a27ba26f0feafc914fd6fb01b7fd31eb57", - "0x112af02bf0fafb42cc9e7b2b3985c5be78c3320550768d8bd271d62743a3a04c", - "0xf84b6ecc88fdd759a1c2263a0a4226a753b62a10df3fd7f094a1c9df7ea5918e", - "0x54ae04f6da0a8351f4ee4a2e36e1832329d5b6dd22452374abe1335914b36baa", - "0x52ab4533e536b7649979822857b1369df6cb0299f0f4ab32da6b8a7d2cff864c", - "0x8fc44fa1f6fe9e0b96b1f72da8b9936419a10a49ea5c02ba543a22b9f32e0618", - "0x95ee4eeba33f81a8d5eda57de81c3617fe03f785e7d9e90c085b91fe23a57a1c", - "0xadbc77bb5afc1c93a42d799fdff05e15998a17aa17d477611d9ba1d68b4abaa0", - "0x80ef051da1139c8f714f152629ca8b4607e82abc097d5f67bf66e26e3ec4b83b", - "0x6dad58c6230b60017db9536548b99c725644168f55d4cb2a1a0353a5e948b0de", - "0x8baa0703e1a050c40f85dc850fe477881f432c951b1cc1b2b71ffb68ab7fe0d7", - "0x14ca94dfd343548e32ef5659c043d6e28f0e577fd38da1ee12f11c08e281d775", - "0xef25357970c76a8b72a6e52f49bc30651f711c7df70444d4667e80febc0e3b2a", - "0x41b8b4ebd5919dad3bc609ded524b97403d88f019367f0f4f622561131644ffb", - "0xb29a8ad1157621d0120aedbd8dfeb4b318979bd43a5b018bf7b9ce33d85da312", - "0x9ddf78b5d67ef40454867ed33de83a01cbd8c18fe09da3d9f991a196811dfccd", - "0x3604121f9cfbb5cf552cf8bfc9a7958332eb97131158e4f40f4eda481e553991", - "0xf1778830f694720a6f990f9d476b0f365e8a74880253b55ee16f5cbd6c8082a4", - "0x89831626d154fbe84a4c62c3e2638cd00e42b3844c7c7b98cfad113abdbc5347", - "0x650573f5ef274b2aeb40642e25fbd661cb0eff66245d7cc8f6fb9e9daa80fc12", - "0x479f6c652173efe94abaa850bffe1557847b26f286467013a4d72973e05e8e54", - "0x7096619d5716c34592ac2d9907ac28a74c6f6b1ebb1962a0217df82bf3e714d4", - "0xb7c7edbd8ae7eed58e973dc0750adfd04042ed56baabf372b111fce3e4b4469a", - "0x25529b597bd15317e55767b3fbfecad0657aaeda99f098186b41b70811f7af2a", - "0xd790747b09f925fd155b7bbdb5ccd89d873277163e4fe7054bbf71c0b26b8072", - "0x3aa0b221d1c4743a06692f645f38a8128d55f1c07cfa6e9711b0d2e0f2e0e738", - "0x26fb5017218cbe4250d2ceae751e99b9a34d7befa162dc248ac008c5d1221e71", - "0x0e4ada59854027601f8f81fbdceb95228db667eb65fed97cefbb35dba21d3b52", - "0xd3be75ae2da3e271dd85cd8226d789aa12d108a4b0d2681462f6539637572e50", - "0x6f041891c8219b508138f67c95a1765d08c5ec06b9ce585f52f837806fce0609", - "0x9e106515d0e80b41397b2e8e98adbb7333e76265917339a62063a07d9a7ed311", - "0x25f47483ecc5ec32f94b3dbcb4d42a4cdbf5c279e93567e14afc742a4619d3e0", - "0x5d81afc54f6b68bd820dcc629ff7e9d8397da56ffedef9addab0eec620de0757", - "0x36e192130485f248925d4b0d2fd98745a76099eb13f53093621d216d0aff0d6c", - "0xb906e5e33b63f6cc355c13b8461a260ee25376ee96909fad2c6ac121ad831496", - "0xd68e7e0d136e30f67ccb7c21e4dc43b0ad5536584e4b77c9da8d903a04e9d212", - "0x5aa7847a4bddda7fbffa62325da920a21a11524ac1b691bc3b55e4d1790f24bc", - "0x331f95f062ed1d38dd02b5ab6a95dd238ef97c5ee9777c938697340c902b4b5d", - "0x8c6a580c8b07567f747bf20946023c3b581c51075cce5cd5d47b0d81d8922135", - "0x8b89483810a49626f90846a0b49ad3e2172657dfa3003d58fa2a43d12c8f4090", - "0x944f9a5754800a33d903c8a464d602fc9da6af8ff3990c3eb669ac9cd17891f9", - "0xc75c82a2c1ecd875d16f343a58835d756740902c246d3b1deae97e49aa19f98b", - "0x1d2e0f2f87ff2b08514b18855c343966d42e0f1a048ddd3d316dca6e06292db9", - "0xa6508507463e53b3a840dd55ed9be57c8a56e1533c9001276750bdf19796f8ea", - "0x01eb6b636b1852e8f9066c12d4e6b7b06b90a325be4d97e08f7f560bab4796a1", - "0x8a0d77fb41f50808ff0a46dc9f3831a2b5093f55ec2e94a3d2e92373ff3b5695", - "0xea3383ba1d30891d1e236db2b31373541f51a9e5c4b4d017cc4960480dd20311", - "0x01129e8d7eff516a225cc0db090e4e38362d9eb2d0571ce00f4417836de2e375", - "0x92feaefb7f9466814a0220e536fe5ee73560507d071e059827d406329e609f87", - "0xac149150c11b3bdc660320a0e955f154fa3137549a73951207659e2b903c145b", - "0xcb68cdb224f9b3b0b0f3ca0056e70817146c9ebc75876dd952e6ca8ea896f2ac", - "0x157565282a12d790452e343c9762c2124456039729f3a8f97a2cee60d85628fa", - "0x42eadc181d59d8d8b26b37e0e9c9052e45bde72090d330bf9cf21d9d3c7d9048", - "0x1ea0ec8879b200e259a3a2a0f2a7aa292301784fa422f7c32ed5d945183948b2", - "0x06aeb2956be9d74ae4ff0b8a6c1874ed8ba46a186616356dc060bea1cbe5c628", - "0x814b0382b52a155a4e35639aeb3d8c859afc4fe5d151de3b0f1bac646e40f2eb", - "0xb30bf3e85be41a2a9e53321ee9f03c7078516c72c7e2d8e7e3134de709b61c36", - "0x1f97f5d334b5e6ebc72f5b846f24c7911f4fd1653f89b3477ce4b8108342810f", - "0x84c6fd181c28ad159ff18d203d14f966668468c9ef0a5d6dbd863886a7e0af1e", - "0x4b2e6947d55ea504bf205bae9dfc0e5402efd33757eea4da00a8ed2a6a3838ae", - "0x85f31d45128bb91cd3490b58a0a641ef77246ea9c83de30fa89b621307fd96f3", - "0xd362f5e6f8cbb216e66eaf49e4df25e01504ac729da86c530871a34e11d302f6", - "0xb7860983b043bc13ce5a27135eea12ffaeff71879404b18af3079b98da156bf2", - "0xf2ff82a679b2b90cb9f4a3bb903eb7ab36ee1c47cbe40024d8d570f5e16bbf4e", - "0x7e34a7e6673146b6bb7f78593b6093ef15b8e9fd1271b33dc5f7d17876b31871", - "0x725c97f83b4cf213296ef353e1c8d64854ef08983fd61320088b8d9e2ab33849", - "0x18085800d10fc7845148835d0ef0ac980a82eeafc44e12bfa296f9c38fc6e19d", - "0xc6c3cf95310cfd0254f0f8e93a3c25bad2b17df04f9c51a25927b80d02e06b69", - "0x822213c1b03cf68ecadc0b7572d37266207d5fe4efd5e56a924b0a1aab8a8e84", - "0x1ff46ffd2dd880cca76244f6af1fd8bddbb4b9ec58f86639821a16f2ff08f3a8", - "0xe9d00df19d716dc859922f2e6c907263191c8e531498ea557869ea1115317c95", - "0x6d3f1edebd562e9d1a236ed7a1d9104fd8f5a086cd78d35c7a65f27c269d98ca", - "0xfea701ced5bca0d5043512700598d3eafa0b89dc02f3c157cd1d52bcf4d84d9b", - "0x556c1cd8ff3ebc2ccd4eee9f1ad3837e346ecda961da17c0ee9cd4d084a47653", - "0x5606be2fba065424af76c94d4156ea82f77d9872ddac7a4c2517957a169e58f9", - "0x8d0223425b48487db1b371c966c7688435f4b9fcda75b088f0aac203d6657cb1", - "0xfceb55d8f3048a3f2255562e0a9ee342439253abcd048fac151ef4b910048e22", - "0x360f76e4f2ef49632e3bf8cfc3afeccff6917e98a48d3568148c3bb13f9d2d7e", - "0xd87bbf8397204cc2af883362646b0ae95392303935ec1997ab052c194e0ef117", - "0x9f1dad9dfecaaf117ab5277caf672b70540578e703c2024d3f23bb7cf8d6410b", - "0x5e130ccb23b7b66dd2fbdd912d6006d2820071dafe2890f593f952028aaa19c0", - "0xccd2f182107992fb9b002b87cdf7990cb2810b202b2ae5d6ef5e0b3bd69632e2", - "0x4b40cd83205f8b946ca9f11fc3306872650e658e631511fd4080bc8ca749d913", - "0x652acc59b71ca20bb65ca195d1a4b3e177f6a3985bdcd6120e1a45b7d4a0c7ca", - "0x49a5e2580ceb329665244e489592aea27d54da8189a665d9435e037ea70c46a5", - "0x379801356beb3a8e5fa7311792c69c7ac1f675a9c08c837f9f0e9f53c243d6a7", - "0xfeff4543c70356b7d9767c26b228cac8452c95bc4875e92a60d7e6fc50468667", - "0x82eb18827313399cb3b0d3c975eb9a9480c3aa5587ce72d321bedccaaab56115", - "0xa644c582a751a6d8cbf30e59d6e770a6f441c017b486cdb23e6b9c48c614967d", - "0x75e6f4d4185515a3c58dea60b55d5e50af053bb261db2d3de00f812a4072ee2c", - "0x9018506876afe91cd2cded037f41c5ee8503fede2a59c47dfe7ea1b36c460f73", - "0x0b8bc06bf211f715291846c6c34b805b6aee91ef4e8c63cf3d15ec79b44ddae8", - "0x150e43fee00d798b8611d4e03838072d8b9c8ee3771e840ee2c161a541d6b643", - "0xe98c938110bd4227c660a9bdccba3b5ce5b8414d909514502e8f354f96abce69", - "0x0fdc3bd666d74a99e623f6d4957bcbfb17395bbdacb52b18ecf7aa5d2e4ad2ce", - "0x2c72d738e803c0522b5f424731ad6327548ef08ded4caf4ae0466b2cf171ac4b", - "0xa7709a082f8a2ed01e2e88c4a2d18930f510fe5b831d2a1bbb1a85a858f19e54", - "0x8ccb960803f6b6e87515ec20a36734128da0dd6e65845013d89996070d3cbc69", - "0x4ab2e49286c8505f32ca54314d1b5c3b2952578d191c69d1cc7ba78a000a01d4", - "0x6bb792b52949c952f20b9d11274c8a05edb44c4070d949bdfddcb0eef2464454", - "0xf61eccaeddac91487b2f5d7b3ec57d1ccbdd0e1307d2f624319fff525149941f", - "0x2dcf01b0d9ce31c13c20959618f2e5e0c7f8c48521f00c12c99d28bfcf202f57", - "0xb1e83745e6013706904b7322350c8b453aaaba0a61ba89f35eef8f19759ceeb4", - "0x8a2cd4944bee70696aa216b453774f4d915dee4bb78c8f7ff55410cddec318fe", - "0x49a78ff97bd4938cd030df23e5ff4d84ace59ef3fdc0099941d8ad7f82235894", - "0x61e2096919419c8856a5473f135f4f9febb102f80eecc90ad60baaed77a99d29", - "0xb95709562a26564e57cafa00d0969305ffdd0aea7e523ba90957a6cbad6ceb70", - "0xb7a6438f176aed77a9c6470e28ab8cf19dad8f77dd3313cfbbbc9276761f454b", - "0xc85a1c37b5a4e1e8ae67df31b369f0b44cdcf66418b42206e5b4a738a0a0bae7", - "0xf034b0d125eb1bd1bbde1fe413aef80fdc136339eed062b97cb9da2f4bc94ba5", - "0xcddbaba3e9e1680a2d1586a1e9e2268afb51b4894f2b41fb2eac5f667b6cb655", - "0x971b2fbd25a97bc710c30b6b98efc90428a7e423c09fe772622e7ea37d18e49c", - "0x8571bcf05c836a59a5d3216634ed1f691edb7bcddc7b5412bc513ae9e1fc0423", - "0x0543d8abe5e1bd541a5bc994b0c7ea5a556575d2e9302d1dc36348ebb7ab9e82", - "0xebd8f9095619980043f234bf4f7682534ae6a15b4584e9d1ae524138471467c3", - "0xd0699a768c873ca5bd615ab5b0fd9c7c6c649c4245b58cb622210ac2bfcf3111", - "0x35bd5a50b11033878751d985459657bd4c0206584cd1def98a355d7ffb0539f4", - "0xd476d2b8b62b720520ba0990137629f83db9c6896f1e788fa5bf08614af1f2bb", - "0x2a8034765c582c07580c0da45c4216bb513171d073a99cbca2fa2adc0c05dbf8", - "0x084b45a575706daccfbbbcc321a9546b0c911861f2639d81f210b77680f60fb7", - "0x499bfc54ba17718aa8f74ceb412dd9b6b6c7d1cbde4d8ad2385a5d5d77e98915", - "0x8638684b06607afb53385f477f0f295453f14e36161da6dc7728336475303a9d", - "0xa686ad8ccc3507627712d203f7ff3be65c289da67269388d0ad8949ad3a5a589", - "0x6d262ff8babed3a603f400501cd77344585fff92e5bab5f5848873042ab87cb0", - "0xb5755234055b3534fceeafcc84d0d77dac220d3466e1dbbbe7d7d44c345e1eec", - "0x92a54b989a1aafd36f979ff0f1fc73f9b1508628f054430234f4460cd9f3959d", - "0x1a48d9bb57e9a70b03c066c97ff9520c177b8361f2b3c343ac71fd8cbdbadba5", - "0xc9e103919e8b6827a1fd57a3e99155a24810f524fb69a7e3fbf486707b2da690", - "0x22a5ba2899ce696fe691a2c545b98ddecb7c0a844cff2375e50e0a0f0f9cf9b3", - "0x154890ebebe7f014aa88f8da52c9713e2b0cc8b9b6c7d29b354afa000c2eb429", - "0xf9a72e3ce36943a442d0dbc7a869a6344003d6a7ee4d344c1899ab8b96746a6b", - "0x270fe45a526aa3bcc8e4a5be075610ae9b15d6fe48b04c6ff0c82a1c7f56e0d8", - "0x9eacbdd695faa85bdbff10bcefcd5edfcc489c3380cb1c73ab5ca6cba2492995", - "0xca15124d292e0465c7e50276cfd374c82391bb668d68a3b19c67fbb9e68b0bba", - "0x9a8cfd3f1b8ad178c982feed1fc306dc996da3d928e99a6f8ad240e55f52cc3e", - "0xc173b14778977c5d6dcb1079023c493406911c6d6791ad08104f847323aff7cf", - "0x82b3e9b9a407ff0596aa7eabdeba10dded85fbf3258d12949ed4f46484e0f649", - "0xb8a009c28a2328b7c95531bc64e072388d797130652ea0a84159c801af562a5f", - "0x80e45ff8b60d28dbb45530b7b56e049e605d28087df7133766de0e151dfbd01c", - "0x7667e9cd22c5427e6f37f215b1d9cd90b2659d5db0bb499daa382e09fcfd58e3", - "0x825014f354bee6aa4cf5283f86e8293c0ea6faf07862abe2cfa12740a0d846d3", - "0xbe9116073311dc303fa29f7a226ff79fd39ec2ffbd2d2270652b972a701aef54", - "0xb3fd19eb6b0163156a350156b15e660853f68a903ea6ed758bf80b2fbc5a655b", - "0x3c5a408909bf485e8c3d8e683d8ae4e7b7149d1f01874807428a3f88e1293d4f", - "0xc759f51aceddb90e6e4273fb9439908592dea649f5202f530d15d455c5c8c7c8", - "0x1fc97f79d1b1d72da309bf2027ae193f0594ecb77c07a472421baadda577a24c", - "0x09c4fd284e5c0672ad031629e58fb2776d1e772c6041c91433f287304dfdcd1a", - "0x8c6b40f8700a0638ca5b7bb003c2de6b54e731881a072988cb78ba89557db1d6", - "0xcbc701507f4f970d8dbf15306d6ac5bdd46b6f58bfd2dd07db4ee01c65aab5f7", - "0x6b911e52f43696cab429c0e511fc40011846af92a2631f4d3f2cd040567f021f", - "0x4fa5373f6eeacba61818052da8a9787c4ed86be0bffe0672c0aea792cc3d2d49", - "0x518ceeb36cf1a406b89040ab0c60e380068f5f6571d042d3bd9a9d7af1b12204", - "0x82039214db7d3beae631682e9249b06f98e16153f864c2026a6edcb0727a6532", - "0x9a9e534af83084007e54dadbce2a0224008a8c42356c2ea650606e8e72d2ad8d", - "0xc4dc0b662ac8d4d987b8c2c65cd27fb8992f65d598bbc25f017c45b7032451f9", - "0xa921f124b5275f07b2c064d477d5779963096632f03c54ac9c87917bc4aa6e52", - "0xb4fe786c84a19c8db63117c421ce53244436d0c04384c62d508a88589f4e5c83", - "0xd57ea382802130e3ee8e7671be2b40a1392ebc6528e2e10d9d51f2899ac7ea54", - "0x224523fcd11abd078840803bbde2c3d1d359a0c2cfdd6f9110a93ac4d3c49845", - "0x5da21f79c0f1030e2a5dbfa283becced75e7ff223ef14a6cb5527434503ac71a", - "0x3e315746e89fd3e662f4dfe28245457c3b8a4ea6d9450b93639424a6d8ef7d41", - "0xe142bd09a6967021c4c5a5c7491b0df3b1bcf13564ac934a2668e1b2252e4723", - "0x2e9ab88b366cba155d1f6093e4edbbafd80032b07a192a35942aeea1bf0f1074", - "0xbcbd1b446853135abae9f330d63a06119a93fb53b63c621941f5a74324c384a1", - "0x7816de8933a433aa2719c99db24e692e6ac6551416b08f803302c7751fcfa3ee", - "0x52c53032dd84842dff6580013ab1edca7c251598ba5fc8b903c40e3d9e537095", - "0x771222b46cdcbeba729436ef39f81732aaf4893da720c1e7b59ebb3fd38ab9d6", - "0xfc440de1896e1b2848f2cc5c7d62c0a6b0019008de8aab41df5395586d340d4c", - "0x0df9b803307465e54cc77ee60dd6988e3d713d496ac0209509c5312ad0f19888", - "0x88179429086844e69122c70b6af8b689abea11e13ba0809743532663e3047c3e", - "0x503898d86c4038eb76124e18459e0d57ec25b177fc79bb87eeccce3fa75f0037", - "0xf46efd490382e4e4a3ac0e10926b46ec56e833769acfe3879acffb13030cfe9d", - "0x6efde5293a9d22e688165f2a51bb83e3b53584f9ef5bacc0bd1d99f5c7df18d4", - "0xc03dc313d9be55674000d061851c139f4c57c6fb46106b98c3442832a51c5f3a", - "0x8ec2accb7c89019d0fe2a31ddada0b8251ebe88a439de9d68be73c0994a6523a", - "0x36ec73d7e8c0181f6335c668ad7713f8eed607e0c302b43413f904b26160bdc1", - "0xa53e90d88a751a78397adc6d2fb4f93ad2b38f0ebd00b32932c4b2a39a276d9f", - "0xea2a25111ca37a53c8fade1ef65097f0cb85e00881b1ff201d36c9151a8e1a0f", - "0x8c1a24ac75821a4b0570c22b682c6030c678244b059ffd4b21e4d3cd05afb585", - "0xd230d46b14f9586f7b1947add191a69ed381cb6fdaa9b28c28869da8c6724dc6", - "0x4e8c717de6c94b4be274f052d9df627c945afe18df35c58a90a035759dce6b48", - "0xdefefe94ca6d93b78ef9f92911eec4c2ae0c10148691755f1624f86445a3b2b2", - "0xfa6c031ec69c7dac9f9b6524054806e2fa524c3200fdeac030d3bf7029ab776b", - "0x9204683278cad559a3f9ed2ec1f6539f34efb17957830d0aaa92b73cd0994732", - "0x2bb26a0cc5eb7424033e8bf552c7964573a0954e5b6aae32a51d95d951fc044b", - "0xf6e3827687af53fc532ba163d7ebd5ce18ffa6384157982b6dfdb1f4be82faf5", - "0x627bda246bf499f4498e8c512c8f601e36a1dcd778f21a598736f83b5b33fbd8", - "0x5816773273e4ef45fbacbe920dc3420602d8134fffe302459891cd6e65627f6f", - "0x7cd637fa8a5bb32dfd7c076c34e0e536d7c6710f1888f5ec18d64191a9930744", - "0xfe016a6e9ffd59237b349a817ba8c36f7775fef6513c4d430fbfc20ce72fcb1d", - "0x7c466b30cf23cb7115a3855d9d9c537c29f380ecfd7aaa0a6e59d9680bc43971", - "0xc49551e5eb138d28f5557ea8e8a8bbc7c058084ba0edf8f6bf64471928ecde43", - "0x7366cd68072f52c5644dc9fbe2e72339705dddd2b03690dd748265723944e754", - "0xbfb7c3bed071283cc18cca4192e2bdc1f97aee9b7185d493a29f20ed4d52ae3d", - "0x40c9fbfde126c8c92cf6d15a18be4b5a4da39ff60ce45cf5e9a2c1782244947a", - "0x1358333dd1b82e7c805dced5dcc2edd5d5784fe8104b64eabc598096c26d05ed", - "0x0a9777f7711b6d9f6cc6ad4a78b716199b57f026d1f30f04ded5d2cd8286322c", - "0x7a848ef083a03e4112c75bef258633779cc495df4ac5529e12c8eadc484baad7", - "0x17025c4c255f509163d11df3cac35c1de823e5ec71cfd66693393882d81fddb8", - "0xed0a2fe0dfe85c7d606d0d1b355518a48220e91202ec1c1178c8b37c86e1c9cb", - "0x4674e5e97dc0c615b03c34c702ceb07aec48cd7e8edc953a188d194a8cab35c1", - "0xa004c31b7a6f6511d7eeda4c400c5ede847ce2ea4339641b6c419f688cc4e574", - "0x1bb6c124e94df05e091f6aea45e7658351fd21318e91105274a15be4f7a7a632", - "0xc365b8bab2d6b902508d075195a1e23b04c97e40217c4ffa49fe16bb13e5b575", - "0x93c1383e2ab52ded5fc86e1b6f6e292c0d0977e8756e1fcbf87b099682060e25", - "0x2921b874e3855d9afca966d5088e9b1cfe59f37db54bf0c1e8bc5c5a97e7ef6c", - "0xfee916dad70495cb45c6ac632241a73869f0f553bf7172cc4e63fa382a5e1077", - "0xc63d63b401941d11edf755d7b6564b4fcba1f84ed99cd08f1580faf2c224478c", - "0x8069f00ca36baba06b310e28f6873e78b3ded25b456d95dad7eef4c517e7d096", - "0x48dc0968af32e91a8a3f252bc0082d7c22f8dd9797e4fa774d5b5365386a4602", - "0x861ffb24def147c4577aaa332aa2b5d3165542c4fd1e1882a9050e5de10f1f57", - "0x9fdfb7305249e7a38068f8099b74c5584a2f2df3f0514371f20042a6c0d2c85e", - "0x90212f15c5d6b686810e1895a139bbc26141181c3c472ee28ef9904553a2faed", - "0xdf967dfde0b63153142628dae7522307afb35b24146d9fc1d35874914244eec8", - "0xfb17a9d9e9a5ab6d742f55c0e8d80c5e5d722e9411e84bc47b1d2fe5fc33dd71", - "0x56b2c30b802088f9ebf0918c7ce44b50a96a0eac98f89e1711f53452006259c2", - "0x4c38b33bd4089a48a65369955540219b44af520000b8f8c346bdc96d47dc2479", - "0x7d3487d3823204a94e557b7a942d7c657c1b49d9f40f2d739a9099d4438695b3", - "0x2f82f94eb74aa2c0fb91bdb38ba0b416ce7e033c7e2a74aea113d201b225e24d", - "0xce8ac4b2f0f74601280bc3df0f5955d033e5c511372d812b36dffbca4b2e4736", - "0xb6ea6c89e91ecca1fbd723188a77370e9d23c3df8aac282cbcbe000b13d31b57", - "0xb8b8b7cb900482562227a0a1256ddbe822cdcf134a196e29b1aa6fd92423a4ed", - "0x0f47ab9f4985c3175e0a30a237d4f7c42db4a58f95344a86a818e459296610fe", - "0xc26a55a23eb390fc83fbdc9f07ee3ce09e8a2b28bc09386323ffc0844e13be24", - "0x1d5e97a7fdf5f34192e9b6dd255c41b63e24e1b4ef4ea43ae510fd1f50abb022", - "0x58b6120c4f0a1004e51703e77decb1c85bdd40bc83654d3f8ce5f7e3b4b6ca25", - "0xa2f4b27f5641d9294ee0411bcabc574f94b294ed8c50c9ae0c3d58caea7d6ba9", - "0xc4d6d3a8b4debec7ec7f3e2a6e1c64de46ec1187ed6e87fa2d87412c9874a3a6", - "0xfb59d9b66471893988d0fed17898fe2b7873d85aed217c20b6f438ed70fc340b", - "0x850df664737f288ae16d701878ad04f399b367fccaa2ddbf975d77868bea7cf5", - "0x4dfe47362c005896f82ac2d02a12ee9418693cd2f5d1bcfdc321980897171993", - "0xb652952de1bf9e1174e5f6a37b069b437792672a37a9e0159c4f36b6e64306b4", - "0xb72dd6cb5df1b00dbbd84e097e2da79af2ce60559697ab4c93b0a8b85b2ee406", - "0xb96fd4a94ac30c10f757691f7f06f25a4900fe424f4eb7ccf322e2f95249b914", - "0x99fd442599036f161ccef1ae8088c5ef694c1819f5b76d9d2fa8f979935f69f8", - "0x3e53574f6ae31a45ef928f9c37bea6c61e6d728a5ade9851567d3167f5ca3314", - "0xd7e3d08c5b71a7ad8338e8b51ec54cb11ad4d643d129a371af07376f8c47c1d4", - "0x1033c8aed4ec46377f75cc9a6b3297e1da0a7d1e74df20bae9fdf6d037afdc28", - "0x924d621544f3301f9e212fbb95872fce9eb4a4172a11693674de733bfc2b0018", - "0x7f61884149ea4def1444a70c022da1c23f31ecc51bb175905b492236a57c7fde", - "0x40c50785bc0665ab4eb3cec95405e17510c571570a5859ead804530dbcbd1387", - "0xf806491cf778f4796c0f73428e6eaf237da8488af99e9b61d72c56fa03e7051c", - "0x7a9670842dcb12c66f11e357a84849cee227ea5a7351e7c6c9370e9ef2560129", - "0x1c974da4e1073157c10deac8b256c8ced77a030e0500b2b8a90b6ca1d32ab4fa", - "0x97ebcc81ba9c1e04865ee4617daa967dec39f65501be21fbbe929db869d57dd8", - "0xa36e4506065d8b9c662697b18ffe50ed2f6ccfe6d07a065bdad048778cc53668", - "0xb9d5566eb0d40bbb03114d333d1d1dc85b0e780ec63229f3b93b2c84af5f9509", - "0xcd16693573724880c3c83834d516be65c35a861b76b43878e28aa7fcbc961361", - "0x4f60ecd7811acc087fc4557fdfaa1a0b522fe30da1cbae5e7740eec3cff04c00", - "0x9e58573b152bf5008e0ea3fc0d64573211916521a62fb08ba0f1b44c5da12e7d", - "0x2c6693cfd7e5bf9a3d8cef9e186b3da25d07af983564ced6238f9191b020f105", - "0x8cc6149caeafef85ec7b2456f33530459c52b30a5365a2a0022b1c308357f7b4", - "0x6f66863bd9909f687523128569cd0894c4cf41e2eddd5cd9c20d30c446b1711b", - "0x402317752053e7b6d7e2d5512d6397112d80ace0873f5f9d32c023a402ec03b3", - "0x2fcd50a79495057908bd34875e3531e6298488f0d06d043fb6fb0b140895d379", - "0x533ba9669dcee2c6e35713c7eca3bca168a326a36b0e39fcde76cbd35ab3d99d", - "0xdc2e86503e8066bc5fac91fe63544e33568a3c744967b9360458101c3d9df096", - "0xf994b38ba312d8bfb00d428b13a088738d93965b525eae81b45b9be344f99fd2", - "0x0721f3f772958d6a58dba638453b8d004e0c76dc8b4cf6d595b712edddcf002f", - "0x3c650c2c7ebbe7879a15882c3157552e8ae1adebea8f0c65a2dda272cc4ed838", - "0x649fe38e87546703245a7adf5925e8c7a07942750e14d39553a56ca3fcbd8c65", - "0xad204bf42d2a444faa864df8e9d023483a6b6daaa8001e00bb5373a45ed064a3", - "0x2c5cdc73d8ddef2e5c0d47358ac180043e7e246e590a7e8ad2b0a3f9b4e9375d", - "0xf38f6c364bbbbe626e849ca9bb9324c54cf0ba8dfc0b2741a3ff87ce7734adbc", - "0x317efc1cea774849d6219d31c8464a15956da4f3810bf15d4353443f79d98e75", - "0xb6796dccdf4d3cab16b5ec9567237cb988ee94131f3262c2a581180b775e76de", - "0x1fde3fdf2303d080d400c43345a424f50f6551a6a06ad50c6e277d49e8034df3", - "0x4d7bc44a3b56f5e69fd3e5e8c0cd8f5f839a775c4ee381b4b1d0a36656cf91cc", - "0x6051b60fdced0c51aa6a1cab2418c8f21c5d174109d514a4c6de758b2056611b", - "0x3c2f7be830078af3c2c6d1557b3da74d1d5bbfd8094f98886a959aa71ce70b15", - "0x8f296b90a0ece0a3dbec19a801072497c5840f9c0491062cd402db00c2b69f2a", - "0x6c14c4697f8291dbdfdbfea5522798e3f8b17204f80d8370e6d379e6ee659e77", - "0x4e98f63afaa50f8a30b0d352eb5fcb5403c635cf54b41545aa8b48465d23fb1d", - "0xad3059433e981ff12cd0d7dbc11a8d92a65cb39c6e936e9c7db5934d45806492", - "0x1cbb21f28ad2d191d6850c97487e5a733306f2f6ba370723fd5ed37cf6c880a0", - "0x82a0010a1b20d383bff0e5d7ba3751bc0d9161a4817554432558c5c2825babb3", - "0x33e54e93443e87c003d582dc51d0b9981ddcaeac4df0993877739651cbf52a58", - "0x1de8bc150f4142cd45b5d0784e5952abd8de7cba9654af959498c0fd0bcac404", - "0x3ee852f48a1a930d671e53c9c8d8c3c38353ee1737c093960c3f841e6c682e94", - "0xa9c6e05ec91e2a2f2f003419063fe033e37e5353c6e233706e29c08693e35eb8", - "0x649f7328064c55c03249d527dadaedcdbb4cb0e939d94c866844192d99469e05", - "0x3a407d00efcd5fe7bb765347b1a3f231b744349269b3aeb44099f4bdd068eb9e", - "0xa1a20af2f7e61082810ce7e7afe6118bc0ad95e9641e6129027f46af28048107", - "0x0d68fc5e58cacb2d16d99a0e9e612d674754ea51cbee2c68a21f4b0aa926688c", - "0x9b3e58144c014343271c9dc90daa8d2f642954b3eda223d64bbb0ac41380e512", - "0xd3de08b676d4f06bbf4322ed4340caab76e6ab7144c97af91c2bc9c749e65b38", - "0x21d626c9c38087aac6262b64f09398be6e4cbf246100d8c2416cab57e9ac1b68", - "0x563a450e35f40279f5946641a823f596ef3ad22a45b8ec280128546aeb0faf14", - "0xadd9c7128e14e670c7d21d6dfa5c09a11dfd237e90709b087e3329d3cd89b5fd", - "0x258cc0f845d8e7438a707f590f55203c6c51302cef4cfbf788b1c7054688da14", - "0x4309676aa14fa8244e0a089c7013b89c9adf57fa952295b8ddb23fc6545c9870", - "0x5db769765dfb41aefc0f40f06f3d005b30ce1f14f04f653e0c5500f651cd61cb", - "0xbef131c9f19572b05d295d7122fd1a09fe4a8afd4e20c5a0f3cd2785b7eb9882", - "0x3f235228ea537332a041ec75cc6cb5663edaa1c2ed1c1700273af73a5d49bf1c", - "0xc081811bb077c6ebe224b560eb6b81f3f813b26789cb06d96110071ffc25fcb4", - "0x912444c19a5e458b79c89969ed5336f2873267baf2fe729b6f218b74d269b797", - "0x5846fc726eb9627e9d070041b92d76191c4b32e315d33ad18121b8acd01634fd", - "0xc899f45494660034d343670856c13a32b230d047434a4d54a772132ddfe3e182", - "0x11a699c18b04e8cdcd96a43b7465f7bd81f8f64d7ebe79dcaf9201cc897f2746", - "0x8e09b134dc8a1735c060175e9688fd001974bf2e3baa5a8e88dc4c87365e0e07", - "0xa086797ebca0a1d446a9289b7eda920b926e1b595c288a9dea31ad225e6de86f", - "0x0cc04369b6036dff78a9856a5173bb2dde380359a8dbe0126e06d6e763a01c36", - "0x4b5efcac86e03d1f67774769b8bcc5d131c181cd4fa297eaa6cea5ec0cdfaa6f", - "0x47272a21a07ad5e14e3f97b237dab7e33344da4db5b2d31bc7cd0cc2d2c9f1db", - "0x9540755fd321d125b73cb6f1884c2f0b2a821d29362194f5029a5e7ba2d3ed44", - "0x229b88922fe52a78090673775f264cd665fe222002d6add2ed29b7ffd98de717", - "0x8fa2d755d5cc0efb01d9fd6f5ae1f7864404ae111d8ba17e23686ea9b6566336", - "0x33a8f2e0775fd19b1302b985bd6c29d4ab5fc63060bcf3df2c3685ab1b19ce67", - "0xf6d6bebb541ef9b84d779c62adb76774bb38a8eba3823e74e0790dc7401bebbc", - "0xa1f421108d49ed23996e55012613fc05e0f86e00f17251b1ff1e0824d35befc7", - "0x2cc572ed83dc6c604bb455ab050c550184a923f4b13815f06d10ef19dffb3c7a", - "0x28220e7d1a9583d68656f03ef4d6fa3e249c71d1b42698f87ba1fc582493e194", - "0xe8aa37b3214abb1bc167fdb6f10119a4019541f31c76b3b3f8c363bb138bd09e", - "0x825189c2c836dda454b457a03ff83d422bf78df1f368434768690fa7f51c57e0", - "0x5dad65d275e69478c81ecaec5b872660205735d9649ac020f65f5ea6ae972dda", - "0x84a1184d8f94fab280e0593479179348f9184d6fe5a2b2ea9697894c42574473", - "0xbef5a05bc7e1fb94465570144499672d95f31fa241b4c510011f6677e2bf72fb", - "0xd08235ebe6d79a8549bcd3d2414cd8afd2a3e2ca22ced226c60aacad1361ff89", - "0xbab5204ad45ec52860023e7474579e7c95397f3c4ac01db7e446e92c19dceef0", - "0x6c81acf2ff161d423a904c457166ff454ef41571d01e73d56bf9ab892790248d", - "0xaf4a603b808e3ddece42e3e123ea02defb9f8ef2546a95c5a617b6ecdb89c306", - "0xeecdbda25b04eb764e322d9a1e5eefad399c9ced8c77b1e4ecfbefcc90bb403d", - "0x9463f4677a2039ca372b61b16d5bcb7c043b26af04aea4d3f0dcdec7bd222070", - "0x27bfd92799b4cf9699d2bfcb158f6727bb986fc0dee780fc1052366ebc4e6364", - "0x63c3faa1a8fc0d531261cd241b1299d4fc13629abb4cd357eeb130505fbddf94", - "0x9a4535b07ff68862f3396b14b88fa07cff7abdd5744775aeeec6868606eb4712", - "0xae59e7c3e0a1df32f6e027da2983d3c55b4ba4d99e85329361561bd7f13ac629", - "0xcc5dc26b9be8fd8432537d967afe12fc668949e4fcf72d97a40f9214975fa57a", - "0x8f11634c83c7a43be8b98335ba617a64c6379f5f92664055c5e1620791134ddb", - "0x14ce2a69d844e6a46aa244c5aca9fb74c127f2151c7c16f4611ca030df365d8b", - "0xb06f220566a5e62570b9e9e49a8b9d5663501ba145b12260fbf9d4a18a4b19e3", - "0x6274f3cf553c45e6ba7ef644d75bf208e08a8c6325e336aefd35dda9cca3c4d2", - "0xd0d685497c2f2b923d0b9f1590a748da8c684a915a470db58c3105c83d8304e7", - "0xf37fab515f96e655f182f0b6e6aa3602f2cd74773329094772151e8c33d1f9a4", - "0xf6efd731481e8553f1d18b5735166499e787009b484b0dfbe4d35e7930f0d837", - "0xc96132b510863e553e08c54e98b5e9c0067f26e421980a6a3bfd4f07480c4396", - "0xdca9d8182c573871b6d6a184cb9819256398080bcb7fd765e6c69cd972a28d8d", - "0xd632ca6f5d45646726ecd2977ffea5c71a867890633f571b359657c0d096f840", - "0xfe3884dbca6bd3b0087466b04e6a5857ad59d7a25021e1d994d059d20005185b", - "0x7f40eb6fb94b05bb43873a98e9d4eb5f7ac90fb8913240bc0909c6be42922b30", - "0x5113a0808666815cfc52b8ed63c649d96f35c365def36ae623f536241b163c3f", - "0x8e6dbacfb5c593d7d7c2650d3d0115c3702cbb55f73011823a202e69ca33cc70", - "0x8f069ac7caa48bce09fe93f4aaef6784d8a6f7a3a09edb82c7512ec18acc3ab9", - "0xa5525e51fd789c59d3b208efffe09abca47cfd6981d36ab44084b86706c69888", - "0xcb4a7e60d5e8b9d22887ef1e8ce339cfcea0ae1fcbfa9adb766ad05d84182de7", - "0x0a14f23f9066ebdb67df31e66f6b8ab1c089025c0ba56ea56d15f73749f47cb7", - "0x0963e3eba12e41d21af7625b8dc487b637b1789a6ac05fb23062e0166942df68", - "0xcb7ec271b2f42cae0027d22b688b19b9288f2b5d9c43bc5b1ea23b35f5542828", - "0x9b97e6f4b2eeee29ecccf9584dc020c8caa3cae51c82f5b58d279eaf0c6ab4e3", - "0xad7f1963ce9993e6172c2ae90c6e1d4d3d3c52e14284fcc1b1e9a56776afb97e", - "0x52ef2ad7bc2921742dcbac9772f13d5c31be938eb1ad6aceb2fa8a163389cefa", - "0x369ead6d900e64ae0b5028df8574e59b67c61dca418c87ce6461eb4c8535fd30", - "0x7e1a18f6199f05f21f9eb5463e9ffd87637d2fd24a23047fe095895c533cb6a5", - "0xe1b8813a95e511aaec9b358d515e624fbc20e551c56328f843ae90b3c895d3a2", - "0xc2ea59f3d1e7bbe115390a4c210142fe9f9dcb1959764450f5b5292ad90e0fcf", - "0x97d235c3f18e6819c08dab4efe66d0f11f0d06f8ffc9686e3f28400e057e6f4a", - "0xea64f817770252b77b08ca2f579b440ec02e833fc88af7c9c96a8e1e07b2cb2c", - "0x185f5fd1f7001b533dc01783c83b7ab0828a4e2f188cc4e26768c515b4c421f6", - "0x0c9de9844e856a1e4340bf54dcaf9dc66b489304765b5c3c6ca20284f5a0dca6", - "0x4dd1d52da1d260d1f0f63bafc4c816b30cea8ec3434e7d4b63a0eff86997254c", - "0x0b3eb94aa246f7c8c871535ae2d3abe5c1b951e76b77510140ef52d5ea2457ee", - "0x27102708eea5d715799642f213049d8ac9abc3b12c76d147ce443dab28af96d8", - "0x81fb3c4e8dc6c658af2901b7aebf7467b9ae045dd0f58fe8d77f8770ac517fb6", - "0xf68dba4eee635d7494bae6fb9f0c44e739b3121d4bc6f6f21b495a716af3cf52", - "0xcf87b723dc473d313bf9ddfa233056036c5658777e831796f1f56647cd040c8d", - "0x49927c2100039ac496d9c16dd12f0a05c9441b8616c69c726fd2e79ae65e130c", - "0x088195c7251f6b9fa2105e77df5620211b8ca783a77f1a98de6752fc442c26c7", - "0x604de480bcb88e908b90451c4142b99b9cbb479167473befca7bea9b4ca427a3", - "0x642fdaf6bc1abbf261a9480fcf9bb48cf03fb80bdd434c6ab63401856c74fa39", - "0xe6b596393fce7a3023a316ac34a1fac18e18779ca5983791866f857f257592e1", - "0x40384a52564fae5df8c3e41827cdf584e38f3f555a54ca749b7b516619071d85", - "0xe52f7c17a4106594563ae7b724e5613e87d8442177f39a36b946b0e335be0e5b", - "0x7726202a7c255a9d7be47af6f3b44c1e379cda61237866554396fb6ec043482c", - "0x665da353f296cde80e3cbcb9c7f93b107d5676e5cd694b0bd33334d74789beb9", - "0xa1157fb181aaa945793b029d3915b37103d050f1f695862d9cda90df4755c189", - "0x449c9daae1c38b3d3f6861b63dc611f147b8a29029927f85c32b5e549df8ee9d", - "0x5739fcf908f960416e5227ff6f95aeb00696f8eb7192968239458a0aebf42533", - "0xfaa6017362d6e64f9ae1b6d11764ddba77cd980261acb5bdbb17b7cdee2d3024", - "0x97fee586909cf8b3cfb2f2dd4e251cb642eb551f1e5d9fa557a21dcb66f430c0", - "0x9934d90e0b4ec5900107784116323b0f76d1d71491cf9c619520478b6eb97ded", - "0x8f5afe4fefb0de810442e27b2bce63e3a424f9e339a0e1f46f6ed26617a4a404", - "0xcd12ed1b75e624f93d9e4fe7dc65407b6d7963196e4082fb459ce354977355a1", - "0x6858ff0c07de87aa2a88498ae948efe6cffade00f3f21086f0a71b8364090846", - "0xaca944e99122e4fe41c62b88e67940332483c8aae2a3a615508f848ceb1c044e", - "0xa8bd3a6b9197bec8849737e6771f563d14d7415707776ce4bed9460588c55c9a", - "0x20e5f18e795844cfcf2326f52ab1e56f4a53a1ce405fd355e0d37de5f6d552ee", - "0x038821cd156d2d9e2aff1ce3525726c42f581ca3e9e2cc75df524a3dbdfe0feb", - "0xb79bd23e10d1e6c7ae749cecaa732966ce3c2e2ea8b640d43638f14d7b78a21c", - "0x2c4d3bcae4a76ffe6924555b320847211367ee37899b187d8fd4358d91264c2e", - "0x0896db7657d2aa8d02166bd4ceba7515b24147fcf0aa34223590fb80db64f848", - "0x95f3b077d9cff92709d46185a7dc5d723f257fb77641e436b0f1bf07947844a1", - "0x6fdd931a93229698b92e6419074bc308a372e0923b35321b725916c1c151cae0", - "0x8c337c36c2027d0f2fb96405f76d3190cdd01237f6a602e07976e09c4a2339e4", - "0x8091a859cf86c1e2c22c870ebc10334f50e05bac2a41153102bf3f698bf5156b", - "0x382435cc836bf1873e489cb4d42d1c1673c5d1b452ba2d16dbd1ca77b7b87a8c", - "0xac5a288b16ed708b00cbe956a14e2a64b321f40851ea25ab1aded7b096822ec6", - "0xee95229b736827a21e0b7154873da3da59e38a1ba3e3b1770de2ed973495ce23", - "0x5168b6e3b5274054fec17b94a82931bc229d0270e5e4c9aeb61539fe8ba4f4a3", - "0x588c6144dfbb33922873946a44f03092bd65d2f89a01b3973002ed38f48917bc", - "0x6c51a964953dc036043b6e8c7fba4a3f6e83295306767bcc3562d9a8cdb04497", - "0x2cfdde11cc2669c5b504c4a82c31ff03e3f0b3aff044519b44bc39405bbf3c9b", - "0xd93200e550400a7179ac7afc394b22b7c710192eadbf429389450e7167191fdb", - "0x5e32f497c606c01c9be8ce1cca5f3cf475f62cf38b2195244a7c93df7d064dde", - "0xb0b74e34fd3983aef31a0584b6a7483ee936622b5fffbdce1411a9db6171c685", - "0x69d0a96a2eca8d6a5c32e700d3f910330930a3348e98e5b276013fcf70c0fe5f", - "0xf49cbb26f2463d38fde553e483de2d4bc6dd85efe2d0e56a2a79810df6af6db9", - "0xe58aa167a26eadf11257f1fcb5da7eef50f9985b590320cc6c015176965ea58e", - "0x582f08d14023a661a9d9dfd1db8324be88fc631cb9136e92f379ad7703584414", - "0xb9c5e584f5e231c2c41b41799a945d60b7ec22487e587ef55e2d9710489c3c00", - "0xd8b70c6657a5bf1fa4b67c4fc046c1eda284d677c610dbacaf0d2d84e4d782dd", - "0x1e29fb536e468abf66b59dd6f48d177a5100a192f1807afc12bb5c97368b8c95", - "0x595ac042acdb51370bf6bfd2b2058dda4781dd7ec330474defb2d5dd00e7f50e", - "0x3d00887c6509b148c5fadde21014aae94924fef363fd5723d80d6a2028df1de5", - "0x9632d419c9fdc7e676b130806cb9c86631aade0adc6a129d3769908ff97e7ef3", - "0x2f91c7ce1158228c8264a6147c6ca396b0d536cdde997a922cde2772b786743f", - "0xf4c731ac2e2612b7e7afcf3ba5911a58a03ef075133923218f54a736b25acb81", - "0x6748a5143ad25f0a461257a47eeab8f057782a438274ced580ca2bd3725d6be3", - "0x05a113b04baf81b6396dd32d59a53908d07a374ebdf17314ec334b3330a16697", - "0xb1bafaef874da14b8a9f883f89a896ec37d9af1b0958f5a3867780278bfe940d", - "0xd93c67d29befe531330c37dfa8b64db7339c8ac35b8cb674d741a414d90d77ef", - "0xb4319281a05f00968367a349e626ec63227412193c30aaface14ac6f7a480024", - "0xf6dce891724a49024535047bee44f889d5228066a931a8f40c18c7efed60f31b", - "0xccdeb308e3402c7dc72588871c23fb4f6fdc60e2a63230f628c5a2951878535b", - "0x90958033b917eea42ffb8bf077ccab8b19c2448113e8a128815ab3441846404b", - "0xed3436553039948094f33cffaff91a7e2b1eab86b638de5c8eb09538c8df0a8e", - "0x0910ba3093e37abaf70ffc0ef6a3b9b82e8c5f7ae497eec14b753f23d0a25343", - "0xdc78d9fceec3add33b92e1a0ee402b80cd87946e0def46f284d26eac46be6bcd", - "0x1d71a82b8d4b89f73747d8073a36acf8496757d35bb452e9cfd74d758774f492", - "0x5399612937722b6fbcb90c1768a92bb35acd4da6168814c1d9deb6156cf35134", - "0xc7156ee3591ab91824b2d4868d28baf11855a63a801b3793f99fdde7867eac7f", - "0xd25b288f3333ea8e33b33f42edf7b64976281e4840599aa49b07da39f895073a", - "0xc6596a87c544e84cfaa2baaa4fb0a93f3fa9d1e6938426ea05a418568a1589b0", - "0xdbf30aa7f1cb3a6d7661934d7ea2ec0665843ea1ec9b05fddd77fdb33fa7d834", - "0xfac2628cb5d70c177e8defbf62b0448297ecb13962638b9a26c5d05203cb2937", - "0xe8c5a65edee74d59e7b9e2fb4779485958d95303fff95bfebc0c1f4bf24ef9bf", - "0x67d21b95f40ca7c991f37981393dcec3218e70d5402d82ecbfcb21ae532872bb", - "0xba19c10601ed47bffb755f791325c50af9229d5c25294f315116fdb3640ca35a", - "0xf4c026f65e31d4606541c4e679d30d7336cfeeb81e9893465eb08c310792c061", - "0x5425ff602e06947bda22cfb687ace85c10896b5d48de144149cba26bac9a35ae", - "0xe11f82e350c209213da7d977e2de5199559ad3793a24a226cb4be367c0aa3c4a", - "0x13fb0194f473a4af296184d3293005dffcd28f1cf4b986bdaacede4a0a096fa6", - "0x3dab80c9fc23caf409e38791f07e107da3b5d0d9d4a64f87164e6d455660591b", - "0x5901803b234ef0445acfcd884baf0777970d3b600b80ab137dc1015a5b790885", - "0x3735e2d0ac017cd2c0570830615ed9eb642183fee5b2c2155e5fefc1d4de2561", - "0xea9030afc249a1e4f6c86f4b19660ea63783824e8d1c3de30e38802f1aea9d12", - "0x9def3e76927de026a1a76f39c242263ffbf94b3ef42fb6e98626b67ec3d8a308", - "0x504886149f29d5eb02412202f04b45f23cc97a82206bdf237c433e83c52e386b", - "0xc84d3b9e84727c2d9279dfebcdf8104762e3b101dd9f39c9482a456f87bcf976", - "0xf4737cd864b198c8049ece5a4f18eea18b48a649997c42504b560188adfb6f2b", - "0x410894a208ff99bd3ec5f925aa7ff6932c4404390df9e337601fa7b7251d09ff", - "0xe6e7e7e2b54bcbe37eb7066b9a3c9b01b632d228c8ed5ef8f1871d9b7235a55b", - "0x8af02fe4657f956a9ede1a9b0a83d67367b43a8f5efdf6bd753d09f428b58960", - "0xdd056794fcb6f5b694934b17ef35577dbec8d04159fe447e0eba02e4ff4e9d96", - "0xef94a3289e9e197b04c17886373adbe66a451efe052fb419846006b5f659b91a", - "0x5558e5612dc3c222d76e7190409fc5d38479e08f6fdf4b7df299e6da968c2811", - "0x5f38cc5046a503d424c4b63cb060956babe0c1de050517d0067eccfa23cb664d", - "0x4c6a04827f46e51378e9ff3b811c878dead62da3ae20ef7d392c57512a09a719", - "0x68fd9806bea1d937063778b245a6788cd992e4fc614d5bce1ffe06e125f67701", - "0x0751a79f86c4220e81bbf6b4692976ed4c315f9b62937b23083f4db2370f04eb", - "0x5651840a2065a3468d97bf824e78455eb5e22b82b45fde7bb69b297c4c40e853", - "0x959d277feeb492ed3393fcd5774f1dde27c6018b07cc4fc93b937ee266ae12ea", - "0xa145f2ed353350a89de97e83433188adbd14107c16c654c131203442c34f2899", - "0xf888fc918eab6f88a5f6c6545d47a32dd558f10d00e86a9949cb3f144b7264e2", - "0x2fef7f5f1c149c87602cbe38766631abc992efaee26dd5b59e5bc361f6901cf4", - "0x5e0735a9be3eb97e790a93502a6f138a8863b0f0c20905bee0ac14d5aff4206a", - "0x9c40cbdab8e769c2c6f9de57c23621c5d8a25adaedd1ba9df17fa1680cd4e63f", - "0x995e6a00f5bc63f0a5d837a6671260771f12995b82da19c72a9546ae7b93f38f", - "0x10f647c754615b475543fca5ed9a4491503867fae619513dad966af63cb7ed2b", - "0x7e45b18bd06e946d20bc3807aa926aa5c0a10555b9dc18f9574136969b8f48d5", - "0x8a419fcb515eef5a139a37241872c095818fd276f77e6388c74b82ae49e6386f", - "0x2cb2d82e278d8ba47830ed3dbd6edf69d2e49721182488ffe0a01698071e7d40", - "0x1e9e5cb7a68e7a2decef900bd250bd31c1f024ddd240799327263c72940c7e59", - "0x3904821c4388b16e9851e3a7318f4177102da38525cd3e5e5708430a243470a1", - "0x7785b9d9ff6dfa45d945aaa393270bf8b62bbde46e941aa068606c173963e518", - "0xbff782fbbadfc752f5c2a239e49ab13200fc35cfba29a5332fe936a0367d336c", - "0x294a3fd73f102c7017c9208857dfb91ec7c323916dd1bb3c9f1fac2c7d952b4b", - "0x3a9fe3685814b6cff7a918e514a9d5375a1fc8268a48d5b78da93dfda115332f", - "0xc033a1836983d485e28e5a0825c953a76d4c7f4a8ec5f7eb0b11baf5ae2beb79", - "0xb088df91127cad5d72f8db96d7ea86dfdef55374bf982222a45df7d2b631ceb6", - "0x1b949204d4713d568d40effb39002052a6d3a568bc81aeeea6d1253d198f1be8", - "0x12a2ca58e35fba0e6807cd6342ec56b3694a463f49804926349f4539be68d438", - "0x100b99dedfaec985e782b9b35858100eea3912b9aaf55fe7a8a767c93898546d", - "0x13d52bcec2eb86e2362570e1660726c03ab838fd5a00e75db7708941b4eb7f78", - "0x593965331b69f02f8ff23ff0530448376ea605ea05cfc28cf39e9db21b412521", - "0x0253f2a3b472affd04d8da76383653ad987d0cb533fd4bdde32a346ce441d74d", - "0xb43ab0c15315108cfcebbbb139eac536205a8fbde0b92e32937ef799d664127c", - "0xc4cb2191f244ba3720fbafe44d13c0d1a3a2e32f80a98363ef748731f54f180d", - "0xb28508b83729ac435202a034701f560fb4fc26e95b4a359da21309c66c082e7c", - "0x4826172b9fc3571e8b6193668e47ae6fd0c44a6a0cef1135333ac0213241f0d8", - "0xb5447c4fd18da34ffec6774325b3b486b625890aa5a383dd5f5ab4e4dc455523", - "0x92d2150387bc841a0a335417e3abe535a1fb00389ca22c22c68f82c8d4e78d7b", - "0x46a6298ee531dd085c8744359fa14217337e9dab9c28a22272185031198feb90", - "0x70cc7e64749cd76006b7553a3bd6a6d3da7840bb5544a27bf281563a82ad1d7c", - "0x727ca0fbafc16b0d88921b72ede2ba344988eb34339e0beb319906e94b99a50d", - "0x83f40d526191f18869fa9a3b4110f402d2304e4061cf90ef807ffdda90569bae", - "0x4267c3eda8a33a7de851bd9793c7ad5287b16204939a367f6139fcc63ed906e1", - "0xda06431a6cde3975a8d2279a1397714d898550e8e40506fa2b14cad7446745e3", - "0x841648c4d39e19e486d139799bdc0cf70976cdb455d581623c7a78dd3a681f82", - "0x88cf67a8dc5788f2c2423f9ba1a183114e58d51abc359f43a14691b14a49b35d", - "0x52caae2977f3bb88aeb21c8ed26821b69409d9de0356ecb042b39863df018dd3", - "0x2c993df9d2e2d312481f8e2fa518e1e49fbc01e70b1b82aaf3c7450d66b4b6b7", - "0x7fc40087df9f67f334d5b38845bd9a38d28e63f7ecd8ce72bbcf5e40954cfbfb", - "0x7ddcc5b059d9740b3692bdfc5734c024c54657bc0fcb06995455b72821e203c5", - "0x136ea4817f923a3f67706f0c2cfa69ef3dce06fd133206281e8eeca484c349d0", - "0xab84a618b9fd1f69deeb2fdccb86cf43be4d2e207e70928b29cd9c25d219c931", - "0x02408b3f518474db68ee4c1bf09d6f03e96df2628631b1bbc0614652851077d9", - "0x03d20cdf5fc553b833b5a09bc0c1aaef7de687815db28bc72ba0245ec266f818", - "0x6ef4844c3615faa2e9116e1a3413205c0100df07510b94ff889ae00ad7a6b735", - "0xce3ec8047d2051701d0adfbecb07eb72a95b37f7b87f01f8900391b543be9a5a", - "0xf461948fe25cfb681d9be95c34980f19e529ae0e2d958bfbf5034ca3626e6cd8", - "0x9cb75fef2a2f75204c125438c1841af419e92f0b490c7c6fccffe5e89f3f97b1", - "0x7132ee9c9db9faed4ade82b126b3b32ebc755114203a6eac2584fd720f9f86d9", - "0xaf1d9e8cf0734fda38af74ba1425fdf2121c3c597be9f3dd39e22d80e301e52e", - "0xc0ac3a2304310466215b8ac6c423719ef4da871d71fb774e180dd8712be96101", - "0x5fb5fbd3db4e37f637286e9cb2f07e5b878d482569d763549d4954c43215db32", - "0x34de5161e1b351e4e125a0235b7b9844f74b51a467f36e89641381f6fce5a7e4", - "0x606782a845f84ac4107a51b9c24209841173aa3f8e9cc2e51c57bfe72e332633", - "0x058d394d8a87c1e70b4ea57fce960eda9ace177d321b52fc90944ade4407022b", - "0xcf8f4925c630c8021aa1e00a1a001a05642f2adc2465dd52bb7a9a80991e84d2", - "0x0dae85d04c42da27288bdae0c6c8e564ec13f23e85da729cdc510bdc04934f55", - "0xc5b9dd7e2d0e55074e96b8b63159126a9c8ab43d42f56af3e5a326f042786b2c", - "0x9c1989e9163d7cb4710d82a708eb1d46b9cb7a9dcbaf1816f71e37090e94f836", - "0xe8d5dc22434014023f70461aa1c011c880a0f846255575353106b52df595509d", - "0x760d3fdc3d34548004d1aecedd84fb248467975572eb87f782009dc7c7c2d277", - "0x6b93dce72d74101a95b6b00d084972f4e35abce76ee4a58b6c260835f9a28c01", - "0xb6e8237d3d8a290f379f08e38f82f18be7e2d480ddc4f44df3e5f3f6c971cc81", - "0x77ebbde0737b668b3d16d764d055d29ccfac7ece19ba2e8d861f937f54df328e", - "0xa8c044ed1d2d77991561677ad8f9e58c07f2dd2caf110717bbd4cdba4cb60b84", - "0x86dd579810c664218ec61784006191f8511374db0078bc8782f52cc618ee4669", - "0x823fc59a1257302917fb1b53ac9dcb4056958ba764c2811b27db4691538d1a0d", - "0x5cc49ee5c1999eb52922e4be082eee8919bdee082d797a50c1ffa29b6ae4edc8", - "0x4a2e06ce47577ed442cbc35c5b232877793372091fc6efd6d60b77ab26bfb985", - "0x30b8c3227529b8da9c3e9ca74d67b62a594743b0fed7c2d66a9a04c9c2e05847", - "0x3c698f90a3cb7c32c3d909df5e2a6f3bc733ee415b95cc40edc52eab95345239", - "0x1210c6ae4167486def5fd9cf2b7042c7b35066e633145b4ba01413329d80095f", - "0xd5baa536453f7d6062dffb1dd1ee1eb1eaa8449e4a8091351a69d6222022b694", - "0x914297de69e782a4292254de92b5e754a1de92a841da73401eacd62404ef4fd8", - "0x0f052f1b2f73c32c032cd52589c08dd365503f2602bc41afbe143790ba86d89c", - "0x7f492b234cfe8681cf41cde74b479fbe8aa81dd47499cc27b07d9a06007af152", - "0x88bb5da748c6f96f3743edba77c172d061489ec2d8da82bc7f858660ae32c319", - "0x9d5db583ead51d5ed207f9b4aa7c0dc45b64517dc14906b9de0b01adea464bbf", - "0x92b9d0d7883b433d65cf3e2ef5af4c817216692816e1f8a3b27a079b622e251b", - "0x6bde93b8499664d61aedd67898071d1711e565f0565db89df8c2dfc2c7f1c08c", - "0x8bc8bbd4e865d6c446c4840ddbde800f3de2725fdeab0cce5e6e0e1acc23eb09", - "0x66e1fe40cbfede3b8791913b327c391f6a5d81137c5da5367cfeeaea5e6e1a8d", - "0xf2f87039b7beb30d4358b651b8566d509caca0fa4f50148b80b44cace72bd058", - "0x25dedfa2522ec37b9b607ccc7f8a260c5d8fe34b12771028524507bde815f004", - "0xf5808873db2b2b21c719ffa31382d0017ba9c4cc408148bd39ef161ab87307b2", - "0xb979dc5a24b39e9c4272c69d858bc1ee8ec49ae5259cb600b146ad8f27756397", - "0x60c3fa777a9c5d91775a173e137a79ca785989c5c3d71fd163bdda852ddc0cc8", - "0xb5ced19b8f2025e69f79511988f634c2ab0abb37dbce6dbd1e3df5cc58063a62", - "0xcc9d411691a188855b529bd49bf00b08689099b31fbb16e6420282c54167d3ac", - "0xc3d8398954555c2e1c7aff2f5908170892769035889ed3c64bcc842c87ba7499", - "0x8ed00b0d107969f06692868cf8d5bee85be943d417198b44e7c95962a1ddd7a4", - "0xf38a2c1e2179a3739fe8b53812d1ddef8acca6df5389050113992a10652f471f", - "0xf32225385feb683115b88285204d56922bdf59e0f58e332949d0f93aeaace4b5", - "0xc4559a00cd9b5b801c21278ed7a2b2a8e4884fd6baaad9fc986ce8cf85019edb", - "0x328bc1a550b6a9a9732e39ef04917f11a5c88f99a9b1f36009d339313159b0a3", - "0x322ce0bfe3aa6729e20ad2548aaed69e54ea1b94ee99dd7c2ef200677e5198ca", - "0x5b758330b7fc118ca6b36ab6b3dee1cae8dc315cdcebb3cd757d25be9718e2c1", - "0x3969413a5efff28ed9f4187ae712dbafe74ae712d801c15644a3961824e5fa2e", - "0xfcd6eb558bd44c5073aaf04f8c1d2a937a338ae6b75cc6d59d35b40dc707f151", - "0x27e43e27370c02660c742c83b6e8976358cc5e01ed04ec82ae087b5519345f86", - "0xf47ec3250d2cbd2f82e5ad70f93630e17d14ff43c791ddaf1c86715e03ebd5c6", - "0x308aa120dff142e89c16ffe5fd7d2204993ff3657b6ab0aa0dbb0d83d14b2afc", - "0x98fe524716333ba24337ca9d6d913a0cfba21362ea50b6e7285bd71956faa75d", - "0x938cd945ef8deed0eb2703d9875ce64712de9a78900578b85a0de6995323e3c9", - "0xbc31b5581f994b3017d6552a6934ca9c52553a4d42a8100af941edbdb2e91a9d", - "0x7327f0976fe152ac14e7d7d3fe9cc2a6a75b968a265e7e0172c0f350d3e152eb", - "0x6c2479beead9c745fe1060956af733766cc80da7188d2988bdc1340d6cd284d0", - "0x5fed2cf969c6cb9028a4e9bc7648022cc4a4f87522a553af1ab21928cdc55daf", - "0x7e8edab36495ede01b8705c5fdcaf89463371ebe10f57173ea857a9fa2192937", - "0xfb58d0ccbeaaacb3b4519c805255fb590596b54e4fc4430a6ec6c5c7fc9766ce", - "0x937a1898488e374bee45ca21d771793526f6a52b30199040760da726247f7cc3", - "0x6e85be1630192947170547f83f5f5278812841c1097decb2cdd7e7438643053f", - "0x12b0b60b027afc8a39e6c32b65aa828e12a7f9df6c8c1e970f08e18fca430513", - "0xf7037c53bc45bc06d8670e846f84824d109578068eec905054f666ee5510fc2b", - "0xc047b653ccbdd24a41214add8b7076bef8916bb040d47d5345e54d7b8438a099", - "0xee03d8ffcf729e8a46f817143763bdb98d646b9945eb573ee52997622e08bdc7", - "0x1adbbab8b4d4df3914a0dbbe70b6815a8c773f7000fef03b55665939d29a2d4f", - "0x475f1fbb7642240b7abff801b13dd369733c45f3103c836106e99cdd43c0caa8", - "0x2aaad3b7050cd04da0f9e613cadf75862c7e8250ed59163ae113e77d63818992", - "0xcbe88d935e66909bd168057b1601b1abfdd68db2a0c0e4ca61fdcf1dc0408774", - "0x576d6f60f196a7736d9197bca8bf9c82fafa9c934cf8988446e5c86859d89f43", - "0x5ec11602479ff16704b3413fcaf368cd8a6a87ff9f5a3a075ceadea2badeab05", - "0x6be482890a8e1b9de9d3d5d93a75fb3fec750c1b8440f7c9ab072c699ea35351", - "0x621797956f53edc280890b015baf2d0b73db85d3b186498e7e9f40fa3408a1d7", - "0xa622bc30127840cc1c1b83bfcba8410bde851070517b39f73fa0753e39f818ba", - "0x8f2c09f74bfbadbfd75ffa2af29e8ad6f6a67d3891ef8ae8d4d785e99af75757", - "0x2fea6a50503eaa6dff3275ec99f56e67e6125307697e4bdc2c489a3cb69029bf", - "0xe5bf069d84493e9f8fe20682f6241a99dfbce8e0580d26014245b28a15b63a51", - "0x5ccc1e2e1cc064acea0ee9299bf1c9469199bd2bd0c6769dd764a065e1d911ff", - "0xb912bad85e4c8b295e3ba2d68c8c5e0d45ba25d756ab24b0626257de8225ab49", - "0x7477d10d6c663d64dd03ad5051f7482fd5a6e5d320e9740041b13cf555e6a227", - "0xe7e04c26dedb78211e39660347214cf4751f19eedb86bb59f4b56855cbd0d2bf", - "0x61e04f4540f1db5e6acbda2b6b20fa5931a1f19607fa4717239acd737490e7d1", - "0x172a7fdf55b224b2bcd76c248216cea3e45a25f1783ac02150759495288297e5", - "0x304a01bca8244bc1ac8568fac49ebe00c83ac93df08362d3fd80f86dadbb779d", - "0xd495196cb5d83bdb3f962d3222d010e95949e2192f914ca256f25755e36b5bc8", - "0x5f4036ef1381adbdc0ae7a0647ca9051d21d5705a8c5c4753aacced054a86796", - "0xb6b2823ac77073022924ef5eccce0fafbf20cda23ba8ca9cca3972c6adcf772b", - "0xb3b34c3b6f3f3e96b4aa487a2bbfb5cf2b906bf3ab9ec411b6a1ebe19bc38364", - "0xc0c9826164bfacbe623f858bb51fefe867511023a8250c187d3520792d4972a2", - "0x1537fdcf26b6705f4585fe555151a59a557c672b91f39567762076d22de0d9bc", - "0x593d8b50367c9d3daafc403a38950e9c13ee3d3e38367f8ee6f0298cf41d8d5a", - "0xfa22314c812f3f8cf6d91c16a81fdbf93aa8606cf7010dd4711099ae87a3b81f", - "0x6ba7db726fc78312ab135893a5691db6efba59fc92efb206405ca863f289d721", - "0x8a13d09d91065c4d13fa532b0eafe3311ecc8a35b18092b042d7bd4df77b7e72", - "0x7d0afb3306161abe2a76e966d8ed01816930633c11d9caad92e7f45d195f0dd3", - "0xc8ed07d535b55af4ccaeb9c78a330fd051efb34b3d70f81a96f8221ea500efc5", - "0x4b1a35a1d95c3a6a2694938b071810412e6bdaa3681ef263efed5d1f4c93b55a", - "0x457cee6b8d1778eb00415c76c239966559e04b86b0ac597daf918f269b41b082", - "0x27177d9985954a57f90b6ca8b4592e285fec13196c3e2e01407fd3e123b6b516", - "0xdea7e638ca66b09790d12fb6fc8f6d6177aca1a209e090de296b9ecb3e0213d7", - "0x98e7e5327a7d2543822260ea2ed427d4f332dfa691d4547b4acef272010e90a8", - "0xc86428edf6c0b39db083970f80a7c2b04954cd572c49336772aaf27dd87b9849", - "0x17db87fad8a68694ca810bc2e5cefdf7c0f695004e4450d0cede7dbb2f7cf753", - "0xcc2face264b38e98ccb2bfb504675f599091617ac7430d25bbc5fc9481abc8b3", - "0x0495d951e99119e7e66f2372007269a6d76341cc0670acc196fa59df089c0af7", - "0x1a105f9a0ceb3b4ee2205b5626ff63335a9ad16f9abd16bc77b7a782f6769c77", - "0x685745060da6f81b54f0d74dbff6169e00a4fa4e28357d18745246e39c9e6220", - "0x3b5cc9eed80730746770c803eda7737854f0bc15a5adad1eaf4dc1e079613aae", - "0x050bc98e087c86272a5ab2eae1b81a37969c9762a36fb069a5751ff2fa1a1a5d", - "0xfc6f6cee79d69ed119a23bd2111ebe1bf9eb984b6d42440a31e2e9331cb3cb1e", - "0x24fe575b0aeaec871d13bfec4c00e3b48b023008e3c29e193e33c3fa267e6efd", - "0x1764cae60df974607c2da7058b73edf4071603722f32676e7c6d577995d6b37f", - "0xd9aa22b7c733a78ba533536250bb0e5837fc9a38fa73fd7f888b8ce0da88c954", - "0xf3e78ee05410dcd0f991c3ab1bf4a3637e61c54648c3d04cb594965f3463f645", - "0x21c2b2f3635984f79a1798d5705301736928c705bac8627045d4dbca46270e5c", - "0x6869195743c986a275e717728f0b35f5badddadcd4723864f2ccb7752edccf2d", - "0x41d7d273d54fb5bc454c69e8328130064472e89408a313befedcbd0dd5e1fb6c", - "0xf0fba130c22ca0753ee1e4424e767bc6d3fd2bbfa68ba85b195979def650bd88", - "0x1d4da114f9132fbe49907afe713a80866710ca533ff5c4a993ee598b2a17bac3", - "0x172165a85ca4100bcad3fe3082c59fdf3aaf0bed23a938b4b326694f64f7e349", - "0x12cf00d3b11cbfaf2038ca1e9de63e4dff65a3352da5824043978ba8fe7e08cf", - "0xd64011bda551b01b0b33e1065b0069fd1fe103e2e68c50fc5bdf1f99b4ab5e0f", - "0x3e3bef8f3d76d1f410327cfd8f4453e0fb9ed392da173e26ffaaed662411f7af", - "0x435816db536c536e0470a3fbfa32fcd6adab877831ffa66417e19bfaa2c9c62b", - "0x19d27119d0c702eb7735d70b30c26567d6ae4c5d204870a036f2273f027a80a9", - "0xd48c41e8365e9872d953b89e2b3dd0fa762b3360958ec9928124f54c79df5f19", - "0x7da805659d8b9e3f6b14aab3a5bd8d25e9d2d960076b4baf6c394a02fc763d12", - "0x5491cc6940517e3a790bf6e32437537e0241c9fcb3302a3bf94133ceb65b8862", - "0x0568a4d9b4e91fd1046fda2cb8a718ba57ba1526610013f465be7777e94356c1", - "0x0831b1ce49717503b49502ffe6b854ab2eb2c4db30f5ea5033c011f5e5e3d309", - "0xabd50d7ea56144421474763563ecdb591ec1df8645ccba4e28990d8b9b8ed16e", - "0xfe4ceb9425449bde581ef04b46c1fc7bdbd8f422c43d64b2d1bbbe3ff4dd7a26", - "0x489137c52ef8c71e620220d8e9e842c2a5befa812b70b0a709ab4603a729047d", - "0x8fa480e08230c6b9d964bca0bc5d5a246e4eac6c8d45c75a4cfac8c4601f455d", - "0xe5cde020c71c0ad3e7f9a789247e5518bc1b824b4c6297e12d09a42e265e4bef", - "0xd4c0b9e39b70ef4f229884817c317f20f53386969f8c1e2ddc69cfa57b1014f9", - "0x75b91e206eb0a2fa7752da4d6421fc294a4cd7d6d06dbdeb7b7df50e157ae99e", - "0xa650044c8fb1c0d98b1e671d576f389b0b7be37e946b7623ff6203ca72c574aa", - "0xe6dd07898de5688be966dbc612764a9956ef1105a7a02695ea269b6e40ccb077", - "0xd1c7d40ae3558d4b42cbf6bce332e718ed8f9b7026b81195beea713ea9fe81da", - "0xdfd6c816adcdd1a588db0bbb4c09050527a107bcc47d76be91c7e309a54dec92", - "0xa67e60d39fad4b6389908ced2c0b6fc4d0b1ae78379de0452963589cde410618", - "0x88a629bbfd9f5ec014580d47210562cc0ba162fc2e7f609dcc34babef9d2f89a", - "0x685dcd734516a666b7e9018a420cc1ed6f1cd13d13f54dc782d3d2529bafd375", - "0x00adf1bec319f440e54009e1ffc45d58f38a120644a111239aef09d1f91c20ed", - "0x0ec349f19c0374334a6f9463b349e97c6a6696984c7779cf29bf048a6317224e", - "0x0f190a67c3804e6aa618106b554578687d0f3983c8350709362fb7e4728adace", - "0x7af75c91424ed2a01f0b796ea4772780395cf77d4051c91552fcb962a384a04c", - "0xa43e2914fa364effea580b8d8ae6d7c5480acf7b9330b226438c12fb5e478ea2", - "0x38deda38720cb7918d43b4b7296fa0168d77ffefee1217bbe198b382fefade22", - "0x40e97169e2871117267eedd1095f76d97719f6e9dec56b3423a9258bb09815d7", - "0x3368d112a868cabf7d21ea1c5c851fe0a37551c03d72cfd6b235e3a904eb5889", - "0xaa25f4d9f97ca1cdbda79c61f1ab84883a037eed60d9ec2301cdd5c404c9805b", - "0x21132089753a085a0a982388817137c591a9a34d2cd9e6651c57c76a8b80b716", - "0xdf0c2df6ca5680424ac369b411ce78031614632c5fe44e2d8af1ddac30cd729a", - "0x4f55598eecadc5136e90d15fcb9f87e1eec65ce18121127b49cd159c69c94098", - "0x8a6b7276e3def38187b0cf9156aec251bab76637505f97583dc4d426ec86be50", - "0xe8a516509c5bcb7e81372ce88f7d51982e092ea93f895e5dc666ddaea58edd54", - "0x3edb18c971bd2f867ab6dbb3d63228aa94ed0d0eb9f603af71ebfb201095b836", - "0x0e7de89c28113289c1f84ed91c289328b9cac13495b289ce11cd1c5942be1188", - "0x2610da85e8fb400db625442fecb8cbe20435e8736fedf3bbf715501697ae6be0", - "0x5668c3a61683c91b17467e48b2f27c7e377010eaee64c69d53ecb1ab89dee1e1", - "0x58bb1cf79609e7223f1883dcc6d80538e278e514b1e11c6c2871db55dcdd9914", - "0xfd8faec518007958057845c7b9f204c8dd473db5dc19ad5f2460802b1fae2693", - "0x304b287a3513bf02a35fa61a8cb6aeb214d453d9d53213f458c207d2e15a537e", - "0x7b8f9c4775a775637d9e3274068df7c5330096a52750d604e6f3818c4a7f0b15", - "0xaac892295bff6dc1d99e51a4114857a5fffbc3c8b2b24bd057156c91a5e3bf7a", - "0x34266cdb180c5227ebae64b8ddbecec44a6144214b9412d723924a92fb4a93c4", - "0x1ed56966b89b57904056fcec5d2a23ca5a5d809a7f5c889c5123e3994d660398", - "0xb44dde8ad3b29068b5d3b3551bef7082d8f09366ac622e57f6b494bd594c7a03", - "0xabebaf98aa2e70020b83e45d5ab56164dcc58a4493264cdd6dfff3e921b395aa", - "0xdf26e98e5499f58665912d987eaebe8234eec92c9cb4e788b274b9e9cc8b527d", - "0xdb1f809c70c5633bac0f1642ad929984ff56154b16e0d81b8fa6b10706336c3e", - "0xecc6cfaadef8dab02149f15d1f39932e53b3fcf25fdb1d0379ef9b933222c018", - "0x17351e250078e53f17a144aad0933172ddc648a8f2fc530f0d4468623328b43a", - "0xb832ca94746ea0be489afd60548e74d654278b8a08bffdda83f87e58459769c8", - "0xbe1d486c25ec6042a110acb68d2fb9daeafb830f1d739506213b5a0de51887b5", - "0x560e8231a58aa914a6b82977734a8ce364d970af688f014e8957933ca3026d66", - "0x76b6b1959f5e0579fef78f6549b171028811c2fd64265bafd701e0a2de9d64b4", - "0xba8ee49b308ea0c0475cd30b90252f4909063b86119e4d1cd53dcf6b4671b0bb", - "0x168bfb6c2fd68fd7194561d6b0f6f76e733dfa092d550de243a2a7c0e6b14fa0", - "0x8d5dbd6b82c31c1237d84256a837ff86e005033d83622531bb00de243da0e5f4", - "0xd12f68a021b6f806c9d75468486f2c4fbad295ec51c0f507c445fc535a0caa81", - "0xdbe40f0f8ee88a80512e2f9162776abf02218c6dfb61a36042ba973ab55aad3a", - "0x6cf853351f1371aacafc02da272b304b246176c2867377185b349c5f5880257f", - "0x3f37a65caa18187a3552e300aab18b27d4a2b2a25c049f29e5d8d7a2c57af78f", - "0xad59d1d4be0ce243a38e94ffee6b21fe073b28b0bee9b0249f560219e9bf604d", - "0xe28ca7ade73017341acecc09a647c6c0e638f6ebdf1566b534dd27d5e9ad67ce", - "0xe3577a39c6d3670d2fcee9f36bfb4e79bcbea5695013ae037a7a7b4c5302979a", - "0x56a8566c6bb04d61331e9950a97248e152742d6f0408a77258c102efde79a0bd", - "0xf199dbb8b24df16441c45f5db31af6597172dcff5bcb84306a3f8ee9e7ebdea1", - "0x85a9e253bae3c1c5064f320911e7f964c7315602e83374f5df7ba532cd2acefc", - "0x80ce75ad1b2de3054cc12adc1774ef8e37f3123387d06daeb545953b7a2bb044", - "0xb26d9c8ece65562068826bc872a6fd93e76634b8b7b598769d3df142ed5d17d2", - "0xa43a1662961c4bd1bfa93fdf911067aca94460c733f5a9e296f9e47740b6f8a2", - "0xf84716d76638301b784430585722e4c112375a69329ac03fb8e98882fbbd2fb3", - "0xbdb1551fc7d0e24f2096586ea482b83b8603db1642ba6aa12d8369214af5e4c4", - "0x8f8f8de670e0dc95346e2b14b82bf8a63d9a2a4eca385b1fafc19964cf98f710", - "0x6d3f91249ce7f7f852a4ffab23b7e949117df088f2e2aa3db0fb386755a16c92", - "0xeb9c005485707baed2b7ab034e68597bb8be65bd6f8283b690cd338bf7d1826b", - "0xcaa884959fbea1f5110d7cc4360c24b2ede43d32f8cba522d28946ffd2d900fa", - "0xa561dbe805f8ba3cb6d3d1524c4b3bcbc95ac330e6d71d33642412f8f0a1a2a3", - "0xf158b9329dbbb90083c88982656840cb134713ba122e7e39b287bca4b0605dd4", - "0xee3ce79371c0f0a1ac399a3a722a1c7515f6b02d2e03118e3fb6d521e245ab6e", - "0x1083d6dba68c10cb19935b627e5576b5bc4e7904c8ff59657ced1c8397468017", - "0xa893338e4a49166bd36f4361799c7cccaaee211b512e0c8821136cdb823e8ac8", - "0x1327e2948ee57993f1b721e9fedc8f7d2e91f041b3e01340375bcbbdead83bfc", - "0x6bc88c663e475911e9fbdfb9a33931c34bcfff5e533e82059d33cd69ee414d94", - "0x81d95020affda45f536e01b1a4b840eb72afe2e82633d81de11acdeada45d93b", - "0x91e59cb7e3729c776c4dd441c3377659b37fd881760b9e70c73d5288e99bfa0a", - "0xbf69325ef2072cd5e3d2e93b8e6e2abbaa03434212e1ba4d36459a0f064a64ba", - "0x11f4a2d21c3145af81ba0edb80a9c27cf1e2d53b1a43a42b58b49d4340ce4fa2", - "0x8c366dac642346ca05e2fc047999f9adcab3b8166482854b06c77c2439e0655d", - "0xdcae4952619f997d791aa6a973f1c8c1d3a0c21859b0c37bec64533a6f7c0630", - "0x5300c16e42bf43e34c9e59fc5748c3e05d38437943956eb80ae91cebb4e33d87", - "0x40ec29594f14d9d9a76b13342face2565914b050cc5e5eb96299c51ec92a529d", - "0xa7da6ccf5a72ad5b8cfb6d00aed5f9f7e2bd70670c57873735ce976249fdd5d9", - "0x177a1ca473ae2fb06ed39e180567da24fd14faffa062edbdcb0cffe4520045b9", - "0xea7c63d98ea30c23e0f4df53566214eda57c96f9449aab5a02c165ddb8e61bd7", - "0x6bf4bac02febf1f9459a433d08b373143f44c0beabd3f7bce4218259e8d91311", - "0x6883559d2ed97c78d0aea12c0d14f0dbc911db36a3bd9e3f8a7142e1083e118f", - "0x999416083ffc7371c9b95b6d1bea32da9c2b00827e1f0e1f615354dfe76dd5aa", - "0x82873c88efa3041aae1d36c2a787e592a7af56c3d86f321b1dc619b974c88ff6", - "0x6bfb4371b0a7b9eb9e3fb1f6987854d6e4210286b98bf8dfe6f71f12014ffe59", - "0x5502ec7d23c5559afca6ac2d6178c1f2c778e4dca22a6bc8b16619082cafdc00", - "0x97307b650b949382bc80b6ea446b629c0fbd37bb65a36ac1b33b1e4ac310c056", - "0xe33b1569d06b2b5d05065c11a0dc4f22d214a7a4e4003c1888f7469094330a8d", - "0x691870ffdeabba194cbc10492332d51702d75c40681662fcbd5489753bd5fcaf", - "0xd9676a94b8e3eeac02389ca8c0a6d730e2718c0cfc3a5bab3110aa170d64bbf5", - "0xf558d9d27beca0657bd8ca7e82d1788b1777dbe06b474b7598acdfd46e8030cf", - "0x381d766d66669a50d13a141535c441887cad897984d7b2bd52e9bf33c385669c", - "0xacbc99a6cf54ca6fcc9ad8f844f8bbd7639791b663956cda2ac59fed34027024", - "0x04e5b5dfedaa9369bed72fe0ce827e3c16e7a07a274661d76ec92347baa8deda", - "0x2a905b1aa12a30fb04f56b2d9a26a38ce10cc2e9dc910f213ec3c6b368b15bf0", - "0x0f40416a8bc6d699c404972d27c9b69093b387a78f22295cb0a6001cd437a4fa", - "0x05a52a1501ff0d2c044f0c1932e5eb217aa734cabd527d865796fdd120fd8856", - "0x36c70cc55f4b571882ecab711522e4bffd3ae8f6873d940a0e4626ade7797612", - "0x4a7af1b12b32d3bb38ec4aa0cda6535cd96043466a56f0641253c7b21abe9205", - "0xf637f992df45cd4b58d2db4d5c8360a65cf05a11f14dba70638e50b5bf70bff4", - "0x5b271fd0d5a0adaa16218fab9f75e0599d85c093233761a76c03ba060d5b7948", - "0x8e3554617c506afbd2eed41a4c8e2ecc1bf619b163843a983411c884e5b07320", - "0x96c35e763be9f9a95f76c81fbe1659d22715f5cdc0cb36c168c67a1062818914", - "0x1d1235e2789d0d283a3d4c06e391a1e38869c788255facbaca7e23c4e76e5ba4", - "0x25246df476679b7364020ebe600ff596b6eb17b23c68ab05885a02616bd2e5f5", - "0x0a516b13c9e864bcaabf531c43ed3ca725d4521c9d34ee08280fd13500cdcfb5", - "0x15b0defb12ba7d05f4c5b387efbcf5eb41e325ece9a0b83619a5b7e19283b677", - "0x5000d5c4cad2ef87de73eb2f10d6733a840d069200cb30d4221031381e8c8f36", - "0x15e98f25d14ef227fbca0b760e2d436e291d3668498f486611a6a3118f5a8556", - "0xdc0d786b90d673f30f84706d29643bf0c392464f30ea66052b153ec28ee4b7cb", - "0xe4da2a6b7a254b85df06dbe8f2bf24b4ca3ef6d5c3b0fc8c17e0ebd32d5d69ac", - "0xe2e6fe80f327eded1c263684cbc525e27839bc816b237588b902c66623c38eb7", - "0x1b8c963829103769fab68bdf45dfe4d6130abc9560590871665f23b190e959e6", - "0x5d2e7cb7a42acaadbc832fb8974756f9473b18263ea50aa281fbcfe217a15762", - "0xcf46e1381d84a33aa488d4674b3ac43a1b6afb102c7a425fcc1447769ef5e6f9", - "0x00532839fe3e0691e406d230c8d384b6bc562923048ea0d89f1be054252eee58", - "0xa68d5545328145a3dabe1b143a57a13057d14866c6b9c9da8b4781f4efed4945", - "0x5223699aa191c964875eb16507aacefde636e88c0f0093d92bc38be221fb3792", - "0xa9b807ba38a8434b3befcc91f6b93427dc81f365354f62d5ee69730e319ce5d9", - "0xfd0dd9977372d780c38981010a339dd6552e8f81fc7654362590e7c6b9fd1f6f", - "0xca3900d2a87a6e0b04d679bb87dc81bd1ac11e79e4f0355e314c7e41d76de4e3", - "0x78ee224a33ce3b2629f7640392846f17c228288b89d6c2d9db42726cf64225cb", - "0x48a4acdd0758838c9ce2b322bc77459c1cc0fdca3d588d60697c8a5aa3035273", - "0x6eb8aeeda5a4b26e73ac2bd7f838c974b5bc45de138d14ba58b51bfb6d51cf93", - "0xd6895877c885791db924b0ab1a1d666c06a10e6d210e02cf9c2f29726d3c565b", - "0xcffa80c70e6246e7b17173c071384a7b46dfdd5ab4b4b524c88871ca02c7e658", - "0xbf64411370653f8e08373438b03ccff7fc6ae1d70385cd26a7e73b57da07dd75", - "0xd9a39f3b774d208608c7d1d9d1fcdacf0c0ff6e8cb7915bca57e8056a8b914c9", - "0xd6a1f8fae6440d4ba8b2e392dbd07f6dd6290651a221b3749cd2dd6b0f39ba0c", - "0xa1ad925960651423f598ab111a0f2bcc5d3fc7f62fc72eb2753815277183a4ac", - "0xf3f6feaa55dcb3eb9c572404a0da1cf7dbb53983a98873b3f0398aad91773b69", - "0xdb902ac0d33bba7b0bcfbbe3b5533b970d07381fb00a973b4dd92c8f7af7bcb8", - "0x5f97ac6dd3f55bbf179eafc50c64042f2daccfcb3bae9c4305045b709cf19ae2", - "0x7061adfb4514170759ff4ddf3922b10f2c85c1a65c361936e504ec77df84b703", - "0x07ae6b39e66142978d35c4c2a517c55c23ec47ae341557e8cbe8a0d001df0f6f", - "0x0e6b9fc3fec4e75d812128fbbae0946dc48b511b198a5301833992e763423da3", - "0x7e1da14382f9908299fe46dedaf1878a6d01e9070554f1436514d320e0a4ebc8", - "0xdb6cdc7239c51386bb9eb371765cccb84e4e4de38acc5ad7e5503bd0f60a31a4", - "0xcd0069c9fb9ba8d136a2020580827799eac95f613f7b75bce8322fe67ecdc2e0", - "0x06932b0dc851ffb7ce68ed9083cecb5ba074716f188c5bb4df94de8e7e057046", - "0x3a2e83e02240263c4e416898f9d54bd0b579ddb0076537b58c47c3a17df15208", - "0x61b166d6dee81bdf6d101f23bf8ca40b74c2eb3f47561b4ef024d30f410f88a7", - "0x7a9e85a9000cb7308a5c57c7b1421b22f63e64f48630a79e449e7a207cf586a1", - "0xd573fcbaafb3882fdb1985c1b939961d35dd7bcbd5865be82276d0cea1afc7a8", - "0x697152abd7502169e0d801346a41c815d8bd8e75e55845d4a4b251def64a9583", - "0x77e5cfae8dc5913c4bbd4f4d84a797bf8919a0c21a4eab4f5dfb42fe8c5019d3", - "0x6e7af8bd163f679b81489d3ec3cc4948cc57ccb3e0c8cbc80e387783bb87641d", - "0x4bd5ece25cfda291179b8977a84369ab1590c9d4d5b45a24865ca010e5ffa676", - "0x6331f61c715be1fe8e0c90740cb89c0a7cee24524be2258ad49542959f845477", - "0x0af729bb18715399a1b07ffe2e0d562f71c27ee2bc01921c25327d421165bef7", - "0x98ee97e5bbeaa0715143a39f38ac56736f54fe5b971584b4061c58721c051a3f", - "0xd06e19176339b1fb5cf469b1f0ee4350a416d20aaa9c1be50bdd26687b2424bc", - "0x2385cde28be71e40f64ba5db5bd1a8e5f4b767a9a17c21a96fd2642ead428394", - "0x3d2ef2f0e4bae5c2ca6fb9fcfa1a9ffc435471a7a533b3b7df2b28402d335f58", - "0x2ecc6ed70661e692248c80735650c253dfe6b3c2213bfc259ebc70c898ee8e6e", - "0x8a68b3eb663796ca2189e49b9fe5902e36615d03d28ac34c01f63a135b930f0b", - "0x563d7efb4175fc12dab431175b5097186c2379a5aca1901e39d641d02865d426", - "0x1a55cf9842f9008e0730ae9fc6af9f288edba323803d0ae787fc0fdda99be8c9", - "0xccdf47cee7142cad27bbf224f0afd1a6e5178345cab510f2f8ee27115ab27ff7", - "0xc5b3582937ad993c5c234d059e6cd3b98f2fa4e3fe4ad591ecb6ae710b51fee9", - "0xd651ab199355d61c49e63259e6a20d17bf72b27891271e5175a24c23fb2dc29c", - "0x800e1e33b0cd48afc6f8f7c69b85902ad93e94bb290b35a862cbad67706a2f3d", - "0x57c7349a6b630cb52c3c1205165df428ed51e25cc167c08739f86bfa46c69e48", - "0xb393d362daff182dafcce65eda9038fc0c8908b3cb1ec1a1218570211c34e142", - "0xb2f49f7a836cd0db9e4743158ff80dd97d23de7a39a0438d19c79c355339c43c", - "0x3f46776ec25b5c67468c32c1b2ca02910a7f46b4b62b5dc5d0cc6b9d3381b958", - "0x754c4ea722e501668aa48d6151ff423263b8a4e2f33b9e44e189ee4e78462741", - "0x12e79ee2724763a8a7f9196bf4d0e855e0ae3f75f5a697d90a263bf562acc5a3", - "0x407b168fea70128a8090ca86274b0b8e7965f16d54ea2b3bbabb378485b22a02", - "0x0d1557ad0cb1466965e6ee86c5ce887eedf462dbec2e719c5f396f8ed95edf08", - "0x29c3a0db9f83698e6ae703174dbeec06f3d2055eb933799e4ae3bf73f4570dde", - "0x8ade20e56fcacb40f54259696bea1aa04d839b88c503d6775d7b4491e0f9e7fa", - "0x327ef0f173632c5049988b312b1f49e9948b02125f70e117ea127088191c32d5", - "0x2989c11c234b7fc5901e168d7adfa655ff6165098eb94d244b623ba43c6abbd6", - "0x10b56dc8f023de6a8f97696dfe2993b56156a9b35f671438fee5830f52b633b5", - "0x46cd37fffe94a01297f2dcef322ae72f58bbf9f58bc9f930cbf56e73f8d5ce13", - "0x2240214683eafc37396f3078446b43ae96bc576e45db650b953ff87a895ee7d7", - "0x0db4d500e5755cba5b962f6b83942e72fd1504cf52531916084852179669a08d", - "0xafc686a8325a30966db0f54b64a4e7235ea54e300d280a4d2a502651a53424b5", - "0x59d46f6fb8eadcf18e31c9fd0da6844d63a2dc19eeeaf9ad5b80113aca5bc42a", - "0x153ae496364bf7c34c8c967e044cef69e401f0e4aff27b04808e2c299dcf15a8", - "0xc85b8d3156729c7f8ff1989f320464144f4918140d608cf03e7dfd5b51ec2acf", - "0x78fd0613f6f5291d4131152225fbded5464841a119d854a7550a1f178a8cdfc7", - "0x763dc1a000b949a45f792515fed6af70047641fa3d7beeb50b141c27b4f8cd33", - "0x42567a1e6caadaa10a13feff343ae1e2a903564affacc48aebc14f257db58ee1", - "0x62c208337234d101cc95cb6be54c17631e26c956758493f5bb7978faf0ace984", - "0x7977aec997a3029025ef789d3492755c80c2d028046aae8c4b926f247e1981d9", - "0xf927505a0c29231ed187a884af15a82dbc48a1cb002f6d91496f6569c673682d", - "0xefd2c54f6d2483a3dd7b994d8afa5441cfffa956d7da6e81bbd1691e07ac7519", - "0x7bcf94d8927a39dd00558763cdd2edaf40d55728d720dccfa0f5e1e827079cc0", - "0x19c7e5115ec25b14bd85e3d368e15d2bbe60451ed9a02f03770a3e845521b77c", - "0x60e32d48de6979c70dcb3e3f068fc3632aba5844703d50851a34975338b3a837", - "0x6722f01dc1d6dd1f0678e5b527155beb0616e33887697a56ef4cc1bac642f394", - "0x59bf17368d69e0379f0f20ec1b1b856298b8d6b4c36354ee765c8a894e58ab9b", - "0xe2559e7b67a15a3ddfbf1ddc597a1309d1fd780fe26da28aace16ad31754883c", - "0xbf687ac704bf02a3877e72f738691bf5027c21f64c68a50235876a8d015ae682", - "0xde01838df621b7368adf4fda268c827dfaa6753a39d51956c0445a2244585ec1", - "0x4ba14d3860d1dee44904719e45a5bfe54dd1e4c9bdafd0a41279e432a021f2e6", - "0xa2380cb32538cc80e07fda5575ecd7b35e0c12932687bd8002da652ad8c16dc2", - "0x82d0f5c8421d6803e52e3d6a37eea0cd978686792a2f6d175d77e9c75fb4ad86", - "0x23701b29d07c64c998ee4bf8199d5ac0f3d9724af2544cc29665a1d92a6dadce", - "0x8bbd490220c823506e111c668f923d18addf5a23d9b5244103cc135c4335f23d", - "0x190506f2d9decb8f9423662f406e61c99606abdcb369e25a1a89c2bdc44c6158", - "0xb9504b090b276cbbf86349bc4dbb66b46a42ce6ae8d44d5cd7f39773fdf6112a", - "0x38fc273344dc7230790963dfb3dedfabe142c18886d6542d77ace8ed6d117c4f", - "0xc76929e9a3a984f6ab6395c4811f007eb6d73a86fe62f8a1c02e860769f2bcde", - "0xc4fc2465f71d06102d232858cfd9737e6e82311a45b1cb2ac203de80c881a2f4", - "0x34427ee5534de01e7f0510b87e6f45aac6210897472978aee3c051c39776c561", - "0xec4f3c6349dac1e7e43b0541b1132b3d0cc8bd422436f31832184d1aa6f9c822", - "0x024a848067a1156852c78495581392774b00f22e32021ca531afeab1aafb683e", - "0x9bdf793e30326a181d3d8fb3f27818475b755e3d1fea200d8c514cf17024979a", - "0x55a1d92354a31354b4b1d9366e98b27c0467b01a108d67dab7feeeda23f981d9", - "0x74614ac3de92173d07434dd80d83aad5c1d551127896298808ad7fba96051e06", - "0x7bdb77183ca1e7f50d292e50ef701ad3bfff3e6e28d36629c5325da03cb8d013", - "0x3a35dc3d12de1053d204410a577b9e5c36d32fbda761f797aee248c27f7dbb02", - "0x430aaeb1dc02bb792375d1702999cda7c384dde1c6f46ef26f1e26e5a55dfd87", - "0xe12805d1713ba730fec6aae824bdcd0a896c6d5e12b9bfa5b6e0a2c2be251c17", - "0x5a297bd3be2352e56136db83a76c7041ef825128152366b2d8f032abb1d6656f", - "0x111782d82f50ea25d684af38689cea53319f1a88557d82c57e46ee5e75f99c05", - "0xe12520cda10064d74fdefba22b5ff4c5eb95508f77fd7c8c3fe82fb89e165bf6", - "0x6af12e8f4db19aee3865c3d681073e7433d902832bc651b8fc3f5fd038c8bca6", - "0x82a4a2ce19ca3fd99007279d62dadba547b6818607800e963b3bc53c5541298a", - "0xbf3a444d1b15d01a29ea22d05e71021ee3581338ecc9e770ee23ab6549a19d55", - "0x745ff92172b5c83cdd69265b919028b350ef95a01c84810366d630162fd9b07f", - "0x6128cd2ff1be9e56396a676362a2049f3e670c70ad2a77be724c090fd4085f0b", - "0x8ffc6bf8165e52ecf30ec955d4be1bb093e6074c92a527382a8892ffa3d3e7f7", - "0xd65bc11ef24c0fa476640d9effdc57030a86f547b7aa79b209954a6abf4072c4", - "0xcd5b5e184a5c0d1c5bdd12f80736ed1a4530210b4ef5f35dacd3feba722a334a", - "0xa80657ecf79ef03fb919bf312e02d94ab528c1b5c3858332d9bf82f807b1c232", - "0xc67dfe4aca5001e97ba6f401f15b72be85ae45972ba98b965a6dd4954d5c2386", - "0x59201e892aeab9e40aef69945585b81b47a7444ed61ba6c6e23e006103ed7edd", - "0xf535c920c5316f12f0bfedc298fbad028dd84161927414b14243c4c5ddd4f4f1", - "0x66c6e42c8137c043eb49528c718b10e5dbad9164bd3ffcc6b55b4b0501dc507b", - "0xa60b9485cb54818e0b0bfe4faeca923915f85bbac0525d09d96f375d0b2b9f81", - "0x8f9377dddacb6ed605c8ed8ed2bc6e2323a4e5d0c9b29bccdfbd27f57a9ec315", - "0xc48a1940c424c2df4803ba8d5573066ce4bbf0e0fbdaa87abcb2d2f51b0a4602", - "0xc7bce918e9f897aeb1351d2c9cbc2fbc8fd674017cb25c49846b05d609f00ed9", - "0xcdaa86e0c026c91954c6305cb7ce6010560f691a2667baf0e95bc18cd3e067df", - "0xf131e0910a8088fc5b0b1d2e93e31632eec67fa88f75bc4f9c3c1b0a317e1d54", - "0xb4f8d72a85c4c2261c7b00f43b96d7d749d0e53359993f52517da36de4c9559b", - "0x73f84bb4774a81b39b3a10d18c7a2404d21f3efd26301dbb7c8136e96304281e", - "0x5559435a987f1444e4ec78b8530009e49c52431b37c7e9f80ce2e056d44e876a", - "0x070664d11ee10c4e0475a7ece219eb0e606e055ee0fa1266b669e593b5ba1d87", - "0xfdfafeba7b5551d1e2d6f179be5ddfdbd0350bd2e9dfea40e272fb608549e8c2", - "0x50a1356bab9b56d8ed46a3e8f55b8a16af42df6b15fcc68aa548216de484c7eb", - "0x681161a307552ff12175074601005bae0c6f7b38cfa6dcb87975a1df205e28d5", - "0x2b39456efc2e8863197c95a4d6eef5069612cc2aa6414f0991393ffa672a1a15", - "0x571b916a82371fafcb456524655758bd42b4f7b768e13807a1a995e642ec205c", - "0xb2821504201eea0e6040a131a709547fc9afb44ba7bfa6a188201735753ba3b7", - "0xb16a8af1bfdde1fae0e28f29c6db0b361840df4b55e73bddffbc1cc11bcc5584", - "0x1df38b594f536cee38acad293a818bf83fc67830fc71bc19790d7733a2caab60", - "0xebb3e8f76f3b6a95285154dc11d4bd94ac4c3a150383ed69f5373499b1983dc3", - "0xb0919ed300acac5f912f01611a428861db27ffb8129a80495f735f0ac608ab35", - "0x2ee321d9d805b78a97210df2977ab62b352705e308773b90e0f4e923adec377c", - "0xee00cb02e9b86978ae10b119924bbe6c38f730c1d1b621d32c9d697e11105871" - ] - }, "nodes": [ - "enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303", + "enode://d6cb8cba18828397e22e8852324af7e970b57cadbbd94aba6124790d1895728311b1f274e45d44a7a22b4276726903130a11ac2de19af5bc9294998f948eaad4@144.217.72.209:30303", "enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303", "enode://30b7ab30a01c124a6cceca36863ece12c4f5fa68e3ba9b0b51407ccc002eeed3b3102d20a88f1c1d3c3154e2449317b8ef95090e77b312d5cc39354f86d5d606@52.176.7.10:30303", "enode://865a63255b3bb68023b6bffd5095118fcc13e79dcf014fe4e47e065c350c7cc72af2e53eff895f11ba1bbb6a2b33271c1116ee870f266618eadfc2e78aa7349c@52.176.100.77:30303", @@ -2734,11 +148,13 @@ "nonce": "0x0", "builtin": { "name": "alt_bn128_add", - "activate_at": "0x19f0a0", "pricing": { - "linear": { - "base": 500, - "word": 0 + "0x19f0a0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x62f756": { + "info": "EIP 1108 transition at block 6_485_846 (0x62f756)", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -2748,11 +164,13 @@ "nonce": "0x0", "builtin": { "name": "alt_bn128_mul", - "activate_at": "0x19f0a0", "pricing": { - "linear": { - "base": 40000, - "word": 0 + "0x19f0a0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x62f756": { + "info": "EIP 1108 transition at block 6_485_846 (0x62f756)", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -2762,17 +180,28 @@ "nonce": "0x0", "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0x19f0a0", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000 + "0x19f0a0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x62f756": { + "info": "EIP 1108 transition at block 6_485_846 (0x62f756)", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } }, "0x0000000000000000000000000000000000000009": { - "balance": "0x1" + "balance": "0x1", + "builtin": { + "name": "blake2_f", + "activate_at": "0x62f756", + "pricing": { + "blake2_f": { + "gas_per_round": 1 + } + } + } }, "0x000000000000000000000000000000000000000a": { "balance": "0x0" diff --git a/ethcore/res/ethereum/runner/full.json b/ethcore/res/ethereum/runner/full.json new file mode 100644 index 00000000000..58c25e65839 --- /dev/null +++ b/ethcore/res/ethereum/runner/full.json @@ -0,0 +1,60 @@ +{ + "chain": [ + { + "path": "res/ethereum/tests/BlockchainTests", + "skip" : [] + }, + { + "path": "res/ethereum/tests/LegacyTests/Constantinople/BlockchainTests", + "skip" : [] + } + ], + "state": [ + { + "path": "res/ethereum/tests/GeneralStateTests", + "skip" : [] + + }, + { + "path": "res/ethereum/tests//LegacyTests/Constantinople/GeneralStateTests", + "skip" : [] + + } + ], + "difficulty": [ + { + "path": [ + "res/ethereum/tests/BasicTests/difficulty.json", + "res/ethereum/tests/BasicTests/difficultyMainNetwork.json" + ], + "chainspec": "Foundation" + } + ], + "executive": [ + { + "path": "res/ethereum/tests/VMTests" + } + ], + "transaction": [ + { + "path": "res/ethereum/tests/TransactionTests" + } + ], + "trie": [ + { + "path": [ + "res/ethereum/tests/TrieTests/trietest.json", + "res/ethereum/tests/TrieTests/trieanyorder.json" + ], + "triespec": "Generic" + }, + { + "path": [ + "res/ethereum/tests/TrieTests/hex_encoded_securetrie_test.json", + "res/ethereum/tests/TrieTests/trietest_secureTrie.json", + "res/ethereum/tests/TrieTests/trieanyorder_secureTrie.json" + ], + "triespec": "Secure" + } + ] +} \ No newline at end of file diff --git a/ethcore/res/ethereum/st_peters_test.json b/ethcore/res/ethereum/st_peters_test.json index ee88008f668..ac404c7dca1 100644 --- a/ethcore/res/ethereum/st_peters_test.json +++ b/ethcore/res/ethereum/st_peters_test.json @@ -36,6 +36,7 @@ "eip145Transition": "0x0", "eip1014Transition": "0x0", "eip1052Transition": "0x0", + "eip1283Transition": "0x0", "eip1283DisableTransition": "0x0" }, "genesis": { @@ -58,8 +59,47 @@ "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x00", "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": "0x00", "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": "0x00", "pricing": { "linear": { "base": 40000, "word": 0 } } } }, - "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": "0x00", "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } } + "0000000000000000000000000000000000000006": { + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0000000000000000000000000000000000000007": { + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0000000000000000000000000000000000000008": { + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + } } } diff --git a/ethcore/res/ethereum/tests b/ethcore/res/ethereum/tests index 725dbc73a54..66a55cd42f6 160000 --- a/ethcore/res/ethereum/tests +++ b/ethcore/res/ethereum/tests @@ -1 +1 @@ -Subproject commit 725dbc73a54649e22a00330bd0f4d6699a5060e5 +Subproject commit 66a55cd42f63845e34767504d0a7a62b452a7e7a diff --git a/ethcore/res/ethereum/tobalaba.json b/ethcore/res/ethereum/tobalaba.json deleted file mode 100644 index 043367cc72d..00000000000 --- a/ethcore/res/ethereum/tobalaba.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "name": "Tobalaba", - "engine": { - "authorityRound": { - "params": { - "stepDuration": "3", - "validators": { - "contract": "0x1000000000000000000000000000000000000005" - }, - "maximumUncleCount": 999999 - } - } - }, - "params": { - "maximumExtraDataSize": "0x20", - "gasLimitBoundDivisor": "0x400", - "minGasLimit": "0x1388", - "networkID": "0x62121", - "eip98Transition": 0, - "wasmActivationTransition": 7250000, - "eip140Transition": 7250000, - "eip211Transition": 7250000, - "eip214Transition": 7250000, - "eip658Transition": 7250000, - - "maxCodeSize": 24576, - "maxCodeSizeTransition": 7250000, - - "registrar": "0xb8624dc8cb3ca3147c178ac4c21734eb49e04071" - }, - "genesis": { - "seal": { - "authorityRound": { - "step": "0x0", - "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x20000", - "gasLimit": "0x800000" - }, - "accounts": { - "0x1000000000000000000000000000000000000005": { - "balance": "1", - "constructor": "6060604052341561000f57600080fd5b5b60048054600160a060020a03199081167310000000000000000000000000000000000000061790915560028054909116731000000000000000000000000000000000000007179055600080546001810161006a8382610115565b916000526020600020900160005b8154600160a060020a036101009290920a9182021916734ba15b56452521c0826a35a6f2022e1210fc519b90910217905550600180548082016100bb8382610115565b916000526020600020900160005b8154600160a060020a036101009290920a9182021916734ba15b56452521c0826a35a6f2022e1210fc519b9182021790915560038054600160a060020a0319169091179055505b610160565b8154818355818115116101395760008381526020902061013991810190830161013f565b5b505050565b61015d91905b808211156101595760008155600101610145565b5090565b90565b610a208061016f6000396000f300606060405236156100ee5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166303aca792811461013d578063063a54c91461016f57806311ae9ed21461019e578063170f9291146102055780631cd3e85814610237578063480b4619146102585780636c1247e5146102bf5780637a1a9e60146102ee578063830f0bc6146103205780638da5cb5b146103525780639b2ae4c614610381578063b6f783f2146103a6578063b7ab4db5146103d5578063c55642be1461043c578063e2de215e1461045d578063fe5d935c1461048c578063ff7a071b146104f3575b34156100f957600080fd5b5b600454600160a060020a0316600036604051808383808284378201915050925050506000604051808303818561646e5a03f4915050151561013a57600080fd5b5b005b341561014857600080fd5b610153600435610518565b604051600160a060020a03909116815260200160405180910390f35b341561017a57600080fd5b61015361054a565b604051600160a060020a03909116815260200160405180910390f35b34156101a957600080fd5b6101b161055a565b60405160208082528190810183818151815260200191508051906020019060200280838360005b838110156101f15780820151818401525b6020016101d8565b505050509050019250505060405180910390f35b341561021057600080fd5b6101536004356105c3565b604051600160a060020a03909116815260200160405180910390f35b341561024257600080fd5b61013a600160a060020a03600435166105f5565b005b341561026357600080fd5b6101b16106d1565b60405160208082528190810183818151815260200191508051906020019060200280838360005b838110156101f15780820151818401525b6020016101d8565b505050509050019250505060405180910390f35b34156102ca57600080fd5b61015361073a565b604051600160a060020a03909116815260200160405180910390f35b34156102f957600080fd5b610153600435610749565b604051600160a060020a03909116815260200160405180910390f35b341561032b57600080fd5b61015360043561077b565b604051600160a060020a03909116815260200160405180910390f35b341561035d57600080fd5b6101536107ad565b604051600160a060020a03909116815260200160405180910390f35b341561038c57600080fd5b6103946107bc565b60405190815260200160405180910390f35b34156103b157600080fd5b6101536107c3565b604051600160a060020a03909116815260200160405180910390f35b34156103e057600080fd5b6101b16107d3565b60405160208082528190810183818151815260200191508051906020019060200280838360005b838110156101f15780820151818401525b6020016101d8565b505050509050019250505060405180910390f35b341561044757600080fd5b61013a600160a060020a036004351661083c565b005b341561046857600080fd5b610153610918565b604051600160a060020a03909116815260200160405180910390f35b341561049757600080fd5b6101b1610927565b60405160208082528190810183818151815260200191508051906020019060200280838360005b838110156101f15780820151818401525b6020016101d8565b505050509050019250505060405180910390f35b34156104fe57600080fd5b610394610990565b60405190815260200160405180910390f35b600180548290811061052657fe5b906000526020600020900160005b915054906101000a9004600160a060020a031681565b600254600160a060020a03165b90565b610562610997565b60018054806020026020016040519081016040528092919081815260200182805480156105b857602002820191906000526020600020905b8154600160a060020a0316815260019091019060200180831161059a575b505050505090505b90565b600080548290811061052657fe5b906000526020600020900160005b915054906101000a9004600160a060020a031681565b60035433600160a060020a0390811691161461061057600080fd5b600254600160a060020a038281169116141561062b57600080fd5b600680546001810161063d83826109a9565b916000526020600020900160005b600280548354600160a060020a036101009490940a848102199091169184160217909255815473ffffffffffffffffffffffffffffffffffffffff1916908416179055507f78603ac34f42fe53d8aad96b7b37aeee79dc7ed07c26f57a13cdf64ac72b0f1181604051600160a060020a03909116815260200160405180910390a15b5b50565b6106d9610997565b60058054806020026020016040519081016040528092919081815260200182805480156105b857602002820191906000526020600020905b8154600160a060020a0316815260019091019060200180831161059a575b505050505090505b90565b600254600160a060020a031681565b600680548290811061052657fe5b906000526020600020900160005b915054906101000a9004600160a060020a031681565b600580548290811061052657fe5b906000526020600020900160005b915054906101000a9004600160a060020a031681565b600354600160a060020a031681565b6001545b90565b600454600160a060020a03165b90565b6107db610997565b60008054806020026020016040519081016040528092919081815260200182805480156105b857602002820191906000526020600020905b8154600160a060020a0316815260019091019060200180831161059a575b505050505090505b90565b60035433600160a060020a0390811691161461085757600080fd5b600454600160a060020a038281169116141561087257600080fd5b600580546001810161088483826109a9565b916000526020600020900160005b600480548354600160a060020a036101009490940a848102199091169184160217909255815473ffffffffffffffffffffffffffffffffffffffff1916908416179055507fadcbcb6339ee0b34abbe8d1524c53b813794e1537a43136c6a7768019599625781604051600160a060020a03909116815260200160405180910390a15b5b50565b600454600160a060020a031681565b61092f610997565b60068054806020026020016040519081016040528092919081815260200182805480156105b857602002820191906000526020600020905b8154600160a060020a0316815260019091019060200180831161059a575b505050505090505b90565b6000545b90565b60206040519081016040526000815290565b8154818355818115116109cd576000838152602090206109cd9181019083016109d3565b5b505050565b61055791905b808211156109ed57600081556001016109d9565b5090565b905600a165627a7a72305820e9d3839061bfeb56c1cc57b2b2ec8dd7882afd848b4ae489c218d6f9674316660029" - }, - "0x1000000000000000000000000000000000000006": { - "balance": "1", - "constructor": "6060604052341561000f57600080fd5b5b60028054600160a060020a0319167310000000000000000000000000000000000000071790555b5b610abb806100476000396000f300606060405236156100885763ffffffff60e060020a60003504166303aca792811461008d578063170f9291146100bf57806340a141ff146100f15780634d238c8e146101125780634d655aff1461013357806375286211146101625780638da5cb5b14610177578063a6f9dae1146101a6578063c476dd40146101c7578063d69f13bb1461022e575b600080fd5b341561009857600080fd5b6100a3600435610252565b604051600160a060020a03909116815260200160405180910390f35b34156100ca57600080fd5b6100a3600435610284565b604051600160a060020a03909116815260200160405180910390f35b34156100fc57600080fd5b610110600160a060020a03600435166102b6565b005b341561011d57600080fd5b610110600160a060020a036004351661064d565b005b341561013e57600080fd5b6100a36107a1565b604051600160a060020a03909116815260200160405180910390f35b341561016d57600080fd5b6101106107b0565b005b341561018257600080fd5b6100a36107ed565b604051600160a060020a03909116815260200160405180910390f35b34156101b157600080fd5b610110600160a060020a03600435166107fc565b005b34156101d257600080fd5b61011060048035600160a060020a03169060248035919060649060443590810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284375094965061084495505050505050565b005b341561023957600080fd5b610110600160a060020a0360043516602435610926565b005b600180548290811061026057fe5b906000526020600020900160005b915054906101000a9004600160a060020a031681565b600080548290811061026057fe5b906000526020600020900160005b915054906101000a9004600160a060020a031681565b60035460009033600160a060020a039081169116146102d457600080fd5b600254600160a060020a031663b31610db8360006040516020015260405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401602060405180830381600087803b151561032d57600080fd5b6102c65a03f1151561033e57600080fd5b50505060405180511515905061035357600080fd5b60008054600019810190811061036557fe5b906000526020600020900160005b9054600254600160a060020a036101009390930a9091048216925082916001911663b31610db8560006040516020015260405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401602060405180830381600087803b15156103e157600080fd5b6102c65a03f115156103f257600080fd5b50505060405180518254909150811061040757fe5b906000526020600020900160005b6101000a815481600160a060020a030219169083600160a060020a0316021790555060018080805490500381548110151561044c57fe5b906000526020600020900160005b81546101009190910a600160a060020a0302191690556001805460001901906104839082610991565b50600254600160a060020a0316635caf5a6a828263b31610db8660006040516020015260405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401602060405180830381600087803b15156104e457600080fd5b6102c65a03f115156104f557600080fd5b5050506040518051905060405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401600060405180830381600087803b151561054257600080fd5b6102c65a03f1151561055357600080fd5b5050600254600160a060020a03169050635caf5a6a83600060405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401600060405180830381600087803b15156105ae57600080fd5b6102c65a03f115156105bf57600080fd5b5050506000194301407f55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c89600160405160208082528254908201819052819060408201908490801561063957602002820191906000526020600020905b8154600160a060020a0316815260019091019060200180831161061b575b50509250505060405180910390a25b5b5050565b60035433600160a060020a0390811691161461066857600080fd5b600180548082016106798382610991565b916000526020600020900160005b81546101009190910a600160a060020a0381810219909216858316919091021790915560025460015491169150635caf5a6a9083906000190160405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401600060405180830381600087803b151561070357600080fd5b6102c65a03f1151561071457600080fd5b5050506000194301407f55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c89600160405160208082528254908201819052819060408201908490801561078e57602002820191906000526020600020905b8154600160a060020a03168152600190910190602001808311610770575b50509250505060405180910390a25b5b50565b600254600160a060020a031681565b73fffffffffffffffffffffffffffffffffffffffe600160a060020a033316146107d957600080fd5b6001805461079d916000916109e5565b505b565b600354600160a060020a031681565b60035433600160a060020a0390811691161461081757600080fd5b6003805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0383161790555b5b50565b73fffffffffffffffffffffffffffffffffffffffe600160a060020a0333161461086d57600080fd5b7f8498b6f07a5f800443a5fd85ac8171cf40cda44faea60caabe9b297d9dfa8424838383604051600160a060020a03841681526020810183905260606040820181815290820183818151815260200191508051906020019080838360005b838110156108e45780820151818401525b6020016108cb565b50505050905090810190601f1680156109115780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a15b505050565b73fffffffffffffffffffffffffffffffffffffffe600160a060020a0333161461094f57600080fd5b81600160a060020a03167f31bc112157435aab6dd7e9a059abea7f0fce172e3e68e272a6375bbb01eb96c18260405190815260200160405180910390a25b5050565b81548183558181151161092157600083815260209020610921918101908301610a36565b5b505050565b81548183558181151161092157600083815260209020610921918101908301610a36565b5b505050565b828054828255906000526020600020908101928215610a255760005260206000209182015b82811115610a25578254825591600101919060010190610a0a565b5b50610a32929150610a57565b5090565b610a5491905b80821115610a325760008155600101610a3c565b5090565b90565b610a5491905b80821115610a3257805473ffffffffffffffffffffffffffffffffffffffff19168155600101610a5d565b5090565b905600a165627a7a72305820cae3ca62c5821766e8122461884affeaabdc6c2fe79f5a3200207a536e5b0e260029" - }, - "0x1000000000000000000000000000000000000007": { - "balance": "1", - "constructor": "6060604052341561000f57600080fd5b5b60018054600160a060020a0319167310000000000000000000000000000000000000051790555b5b6101bb806100476000396000f300606060405263ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166318def8ef811461005e5780635caf5a6a1461008f5780638da5cb5b146100b3578063b31610db146100e2575b600080fd5b341561006957600080fd5b61007d600160a060020a0360043516610113565b60405190815260200160405180910390f35b341561009a57600080fd5b6100b1600160a060020a0360043516602435610125565b005b34156100be57600080fd5b6100c6610161565b604051600160a060020a03909116815260200160405180910390f35b34156100ed57600080fd5b61007d600160a060020a0360043516610170565b60405190815260200160405180910390f35b60006020819052908152604090205481565b60015433600160a060020a0390811691161461014057600080fd5b600160a060020a03821660009081526020819052604090208190555b5b5050565b600154600160a060020a031681565b600160a060020a0381166000908152602081905260409020545b9190505600a165627a7a7230582085b261e548fa6e3065a32e485c6417d200c7145f3548c0097d4c92022ac7fb1e0029" - }, - "0x4ba15b56452521c0826a35a6f2022e1210fc519b": { - "balance": "0x7E37BE2022B2B09472D89C0000" - }, - - "0x0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "activate_at": 7250000, "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0x0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "activate_at": 7250000, "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0x0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "activate_at": 7250000, "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0x0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "activate_at": 7250000, "pricing": { "linear": { "base": 15, "word": 3 } } } }, - "0x0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": 7250000, "pricing": { "modexp": { "divisor": 20 } } } }, - "0x0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": 7250000, "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0x0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": 7250000, "pricing": { "linear": { "base": 40000, "word": 0 } } } }, - "0x0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": 7250000, "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } } - }, - - "nodes": [ - "enode://a9327d37d07799817d4a3e13d49fb4f5cc1486d4adf3ec8a6b98be62c4d7a5453914a5139dbe124809a388514cb0be37f9fa799539abe2250672f6d3d778b821@18.191.209.251:30303", - "enode://8185e15b0e269e19b9051ba2c3bab9160f0e52a8e5e2cef626013142d957c4256f0b18e80965a9fa9acabdb2ba07890c995ad354cbda0fa812ded5a5ce878321@3.121.61.202:30303", - "enode://38fab1370b042170b37ebd758d07c17b7aa4fd4ff21db8e8f120ade9cf17835ae67fd014a047ee171952fba2a05a90505fedbe98c883b20e4501d437aec8b831@3.122.42.125:30303", - "enode://d2fdbd9efe681080410775dbe986014e21a6a096b6dfd7d2c499f3b893951adf3aae0164392404f6269d231192735bd0c8da3d022639e8a36d8d17299daa632a@3.122.18.27:30303", - "enode://9745ffa93cde2a0e22528fbd4a4f8b5102035ab8c7a781918c9ef92dee6e5a21635dfd106ccfbcf24f4fdd52e8fb513b7f3f55ced90c9b61f2cd756bccd7f660@18.210.141.224:30303", - "enode://4c36427e744783bcbc595a7fbbe785951130d8d4fe9f2206c78538fcf43fb19172df8a577c4ae0c91b7627b057bdf148666a7e4e428650e677f9c443e59479e4@3.86.127.87:30303", - "enode://b447c1eaad456996ae4ce965a01d543b8f4b0d7e78e23d2cc2d328fe0e0b87dac55eaebe9a27ae24fb879cf056b52b11d967d37a731f5f5987ec4fc0dfb4d908@34.236.121.163:30303", - "enode://916dba32ba88b7a5554d862b5a01b5eddf805788545f2ba6ee682f1cfe08eac132b71a4d725bc73c7b687496e525bf414e7d90cd223dacebe1946e3bb464bca0@18.136.95.237:30303", - "enode://a46393687ee9fbe798aba517d8f92443e45ed7e5ea85aa996c62998088607d57b000001945063045584861652802325d93828cef29a9eb966772f75521dff8c6@3.0.157.214:30303", - "enode://94b92761837031a7afbbc7e6a4363baf6c5f04c7766ab63f4f5b47c98be30faef6e5e91d30cc08dd682a6dc16cdd4f85ec6843f4ac9f18887d4da20b04878ea6@52.220.46.9:30303" - ] -} diff --git a/ethcore/res/ethereum/transition_test.json b/ethcore/res/ethereum/transition_test.json index 3f1c420dd68..901f4dfded3 100644 --- a/ethcore/res/ethereum/transition_test.json +++ b/ethcore/res/ethereum/transition_test.json @@ -57,9 +57,47 @@ "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, - "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "5", "pricing": { "modexp": { "divisor": 100 } } } }, - "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": "5", "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": "5", "pricing": { "linear": { "base": 2000, "word": 0 } } } }, - "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": "5", "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } } + "0000000000000000000000000000000000000006": { + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "5": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0000000000000000000000000000000000000007": { + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "5": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0000000000000000000000000000000000000008": { + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "5": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + } } } diff --git a/ethcore/res/ethereum/volta.json b/ethcore/res/ethereum/volta.json new file mode 100644 index 00000000000..02058dfbfed --- /dev/null +++ b/ethcore/res/ethereum/volta.json @@ -0,0 +1,208 @@ +{ + "name": "Volta", + "engine": { + "authorityRound": { + "params": { + "stepDuration": "5", + "validators": { + "contract": "0x1204700000000000000000000000000000000000" + }, + "maximumUncleCountTransition": "0", + "maximumUncleCount": "0", + "blockRewardContractAddress": "0x1204700000000000000000000000000000000002", + "blockRewardContractTransition": "0" + } + } + }, + "params": { + "networkID": "0x12047", + "maximumExtraDataSize": "0x20", + "gasLimitBoundDivisor": "0x400", + "minGasLimit": "0x1388", + "maxCodeSize": "0x6000", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "registrar": "0x1204700000000000000000000000000000000006" + }, + "genesis": { + "seal": { + "authorityRound": { + "step": "0x0", + "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "gasLimit": "0x5B8D80" + }, + "accounts": { + "0x0000000000000000000000000000000000000001": { + "balance": "1", + "builtin": { + "name": "ecrecover", + "activate_at": "0", + "pricing": { + "linear": { + "base": 3000, + "word": 0 + } + } + } + }, + "0x0000000000000000000000000000000000000002": { + "balance": "1", + "builtin": { + "name": "sha256", + "activate_at": "0", + "pricing": { + "linear": { + "base": 60, + "word": 12 + } + } + } + }, + "0x0000000000000000000000000000000000000003": { + "balance": "1", + "builtin": { + "name": "ripemd160", + "activate_at": "0", + "pricing": { + "linear": { + "base": 600, + "word": 120 + } + } + } + }, + "0x0000000000000000000000000000000000000004": { + "balance": "1", + "builtin": { + "name": "identity", + "activate_at": "0", + "pricing": { + "linear": { + "base": 15, + "word": 3 + } + } + } + }, + "0x0000000000000000000000000000000000000005": { + "balance": "1", + "builtin": { + "name": "modexp", + "activate_at": "0", + "pricing": { + "modexp": { + "divisor": 20 + } + } + } + }, + "0x0000000000000000000000000000000000000006": { + "balance": "1", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0x0000000000000000000000000000000000000007": { + "balance": "1", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0x0000000000000000000000000000000000000008": { + "balance": "1", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, + "0x1204700000000000000000000000000000000005": { + "constructor": "0x606060405234156200001057600080fd5b6040516200240138038062002401833981016040528080518201919060200180519060200190919050505b600082518260328211158015620000525750818111155b801562000060575060008114155b80156200006e575060008214155b15156200007a57600080fd5b600092505b8451831015620001b6576002600086858151811015156200009c57fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161580156200012b5750600085848151811015156200010857fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1614155b15156200013757600080fd5b60016002600087868151811015156200014c57fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055505b82806001019350506200007f565b8460039080519060200190620001ce929190620001e3565b50836004819055505b5b5050505050620002b8565b8280548282559060005260206000209081019282156200025f579160200282015b828111156200025e5782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019062000204565b5b5090506200026e919062000272565b5090565b620002b591905b80821115620002b157600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555060010162000279565b5090565b90565b61213980620002c86000396000f3006060604052361561011b576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063025e7c2714610177578063173825d9146101da57806320ea8d86146102135780632f54bf6e146102365780633411c81c1461028757806354741525146102e15780637065cb4814610325578063784547a71461035e5780638b51d13f146103995780639ace38c2146103d0578063a0e67e2b146104ce578063a8abe69a14610539578063b5dc40c3146105d1578063b77bf6001461064a578063ba51a6df14610673578063c01a8c8414610696578063c6427474146106b9578063d74f8edd14610752578063dc8452cd1461077b578063e20056e6146107a4578063ee22610b146107fc575b5b6000341115610174573373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a25b5b005b341561018257600080fd5b610198600480803590602001909190505061081f565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101e557600080fd5b610211600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061085f565b005b341561021e57600080fd5b6102346004808035906020019091905050610b02565b005b341561024157600080fd5b61026d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610cae565b604051808215151515815260200191505060405180910390f35b341561029257600080fd5b6102c7600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610cce565b604051808215151515815260200191505060405180910390f35b34156102ec57600080fd5b61030f600480803515159060200190919080351515906020019091905050610cfd565b6040518082815260200191505060405180910390f35b341561033057600080fd5b61035c600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610d91565b005b341561036957600080fd5b61037f6004808035906020019091905050610f99565b604051808215151515815260200191505060405180910390f35b34156103a457600080fd5b6103ba6004808035906020019091905050611081565b6040518082815260200191505060405180910390f35b34156103db57600080fd5b6103f16004808035906020019091905050611150565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200184815260200180602001831515151581526020018281038252848181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156104bc5780601f10610491576101008083540402835291602001916104bc565b820191906000526020600020905b81548152906001019060200180831161049f57829003601f168201915b50509550505050505060405180910390f35b34156104d957600080fd5b6104e16111ac565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156105255780820151818401525b602081019050610509565b505050509050019250505060405180910390f35b341561054457600080fd5b610579600480803590602001909190803590602001909190803515159060200190919080351515906020019091905050611241565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156105bd5780820151818401525b6020810190506105a1565b505050509050019250505060405180910390f35b34156105dc57600080fd5b6105f260048080359060200190919050506113a2565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156106365780820151818401525b60208101905061061a565b505050509050019250505060405180910390f35b341561065557600080fd5b61065d6115d3565b6040518082815260200191505060405180910390f35b341561067e57600080fd5b61069460048080359060200190919050506115d9565b005b34156106a157600080fd5b6106b76004808035906020019091905050611696565b005b34156106c457600080fd5b61073c600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611877565b6040518082815260200191505060405180910390f35b341561075d57600080fd5b610765611897565b6040518082815260200191505060405180910390f35b341561078657600080fd5b61078e61189c565b6040518082815260200191505060405180910390f35b34156107af57600080fd5b6107fa600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506118a2565b005b341561080757600080fd5b61081d6004808035906020019091905050611bc0565b005b60038181548110151561082e57fe5b906000526020600020900160005b915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561089b57600080fd5b81600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615156108f457600080fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600091505b600160038054905003821015610a80578273ffffffffffffffffffffffffffffffffffffffff1660038381548110151561098757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610a725760036001600380549050038154811015156109e757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600383815481101515610a2357fe5b906000526020600020900160005b6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610a80565b5b8180600101925050610951565b6001600381818054905003915081610a989190611fe8565b506003805490506004541115610ab757610ab66003805490506115d9565b5b8273ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a25b5b505b5050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610b5b57600080fd5b81336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610bc657600080fd5b8360008082815260200190815260200160002060030160009054906101000a900460ff16151515610bf657600080fd5b60006001600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e960405160405180910390a35b5b505b50505b5050565b60026020528060005260406000206000915054906101000a900460ff1681565b60016020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b600080600090505b600554811015610d8957838015610d3c575060008082815260200190815260200160002060030160009054906101000a900460ff16155b80610d6f5750828015610d6e575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15610d7b576001820191505b5b8080600101915050610d05565b5b5092915050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610dcb57600080fd5b80600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515610e2557600080fd5b8160008173ffffffffffffffffffffffffffffffffffffffff1614151515610e4c57600080fd5b60016003805490500160045460328211158015610e695750818111155b8015610e76575060008114155b8015610e83575060008214155b1515610e8e57600080fd5b6001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060038054806001018281610efa9190612014565b916000526020600020900160005b87909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508473ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25b5b50505b505b505b50565b6000806000809150600090505b60038054905081101561107957600160008581526020019081526020016000206000600383815481101515610fd757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615611058576001820191505b60045482141561106b576001925061107a565b5b8080600101915050610fa6565b5b5050919050565b600080600090505b600380549050811015611149576001600084815260200190815260200160002060006003838154811015156110ba57fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561113b576001820191505b5b8080600101915050611089565b5b50919050565b60006020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169080600101549080600201908060030160009054906101000a900460ff16905084565b6111b4612040565b600380548060200260200160405190810160405280929190818152602001828054801561123657602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116111ec575b505050505090505b90565b611249612054565b611251612054565b6000806005546040518059106112645750595b908082528060200260200182016040525b50925060009150600090505b600554811015611322578580156112b8575060008082815260200190815260200160002060030160009054906101000a900460ff16155b806112eb57508480156112ea575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15611314578083838151811015156112ff57fe5b90602001906020020181815250506001820191505b5b8080600101915050611281565b8787036040518059106113325750595b908082528060200260200182016040525b5093508790505b8681101561139657828181518110151561136057fe5b906020019060200201518489830381518110151561137a57fe5b90602001906020020181815250505b808060010191505061134a565b5b505050949350505050565b6113aa612040565b6113b2612040565b6000806003805490506040518059106113c85750595b908082528060200260200182016040525b50925060009150600090505b60038054905081101561152b5760016000868152602001908152602001600020600060038381548110151561141657fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561151d5760038181548110151561149f57fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683838151811015156114da57fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506001820191505b5b80806001019150506113e5565b816040518059106115395750595b908082528060200260200182016040525b509350600090505b818110156115ca57828181518110151561156857fe5b90602001906020020151848281518110151561158057fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b8080600101915050611552565b5b505050919050565b60055481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561161357600080fd5b600380549050816032821115801561162b5750818111155b8015611638575060008114155b8015611645575060008214155b151561165057600080fd5b826004819055507fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a836040518082815260200191505060405180910390a15b5b50505b50565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615156116ef57600080fd5b81600080600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415151561174b57600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515156117b757600080fd5b600180600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef60405160405180910390a361186c85611bc0565b5b5b50505b505b5050565b6000611884848484611e6c565b905061188f81611696565b5b9392505050565b603281565b60045481565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156118de57600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561193757600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615151561199157600080fd5b600092505b600380549050831015611a7f578473ffffffffffffffffffffffffffffffffffffffff166003848154811015156119c957fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611a715783600384815481101515611a2257fe5b906000526020600020900160005b6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611a7f565b5b8280600101935050611996565b6000600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508473ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25b5b505b505b505050565b600033600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611c1b57600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611c8657600080fd5b8460008082815260200190815260200160002060030160009054906101000a900460ff16151515611cb657600080fd5b611cbf86610f99565b15611e6057600080878152602001908152602001600020945060018560030160006101000a81548160ff021916908315150217905550611ddd8560000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866001015487600201805460018160011615610100020316600290049050886002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611dd35780601f10611da857610100808354040283529160200191611dd3565b820191906000526020600020905b815481529060010190602001808311611db657829003601f168201915b5050505050611fc0565b15611e1457857f33e13ecb54c3076d8e8bb8c2881800a4d972b792045ffae98fdf46df365fed7560405160405180910390a2611e5f565b857f526441bb6c1aba3c9a4a6ca1d6545da9c2333c8c48343ef398eb858d72b7923660405160405180910390a260008560030160006101000a81548160ff0219169083151502179055505b5b5b5b505b50505b505050565b60008360008173ffffffffffffffffffffffffffffffffffffffff1614151515611e9557600080fd5b60055491506080604051908101604052808673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018481526020016000151581525060008084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550602082015181600101556040820151816002019080519060200190611f54929190612068565b5060608201518160030160006101000a81548160ff0219169083151502179055509050506001600560008282540192505081905550817fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5160405160405180910390a25b5b509392505050565b6000806040516020840160008287838a8c6187965a03f1925050508091505b50949350505050565b81548183558181151161200f5781836000526020600020918201910161200e91906120e8565b5b505050565b81548183558181151161203b5781836000526020600020918201910161203a91906120e8565b5b505050565b602060405190810160405280600081525090565b602060405190810160405280600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106120a957805160ff19168380011785556120d7565b828001600101855582156120d7579182015b828111156120d65782518255916020019190600101906120bb565b5b5090506120e491906120e8565b5090565b61210a91905b808211156121065760008160009055506001016120ee565b5090565b905600a165627a7a72305820f1129b699b3017535535a920e15503cd06af1f5c77637c0637cc29355b1dad3400290000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000cb1437200aea736788f1fc56f327c0456c3598d00000000000000000000000074dd76e24b2cfb43c1b1a4498295d553d0843746000000000000000000000000eeb4ceee443f9e0d17bdbd6daa241681ee5e51c2000000000000000000000000a005caea55375ae20e3aaef746113535503abc19" + }, + "0x1204700000000000000000000000000000000003": { + "constructor": "0x606060405234156200001057600080fd5b6040516200240138038062002401833981016040528080518201919060200180519060200190919050505b600082518260328211158015620000525750818111155b801562000060575060008114155b80156200006e575060008214155b15156200007a57600080fd5b600092505b8451831015620001b6576002600086858151811015156200009c57fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161580156200012b5750600085848151811015156200010857fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1614155b15156200013757600080fd5b60016002600087868151811015156200014c57fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055505b82806001019350506200007f565b8460039080519060200190620001ce929190620001e3565b50836004819055505b5b5050505050620002b8565b8280548282559060005260206000209081019282156200025f579160200282015b828111156200025e5782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019062000204565b5b5090506200026e919062000272565b5090565b620002b591905b80821115620002b157600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555060010162000279565b5090565b90565b61213980620002c86000396000f3006060604052361561011b576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063025e7c2714610177578063173825d9146101da57806320ea8d86146102135780632f54bf6e146102365780633411c81c1461028757806354741525146102e15780637065cb4814610325578063784547a71461035e5780638b51d13f146103995780639ace38c2146103d0578063a0e67e2b146104ce578063a8abe69a14610539578063b5dc40c3146105d1578063b77bf6001461064a578063ba51a6df14610673578063c01a8c8414610696578063c6427474146106b9578063d74f8edd14610752578063dc8452cd1461077b578063e20056e6146107a4578063ee22610b146107fc575b5b6000341115610174573373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a25b5b005b341561018257600080fd5b610198600480803590602001909190505061081f565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101e557600080fd5b610211600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061085f565b005b341561021e57600080fd5b6102346004808035906020019091905050610b02565b005b341561024157600080fd5b61026d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610cae565b604051808215151515815260200191505060405180910390f35b341561029257600080fd5b6102c7600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610cce565b604051808215151515815260200191505060405180910390f35b34156102ec57600080fd5b61030f600480803515159060200190919080351515906020019091905050610cfd565b6040518082815260200191505060405180910390f35b341561033057600080fd5b61035c600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610d91565b005b341561036957600080fd5b61037f6004808035906020019091905050610f99565b604051808215151515815260200191505060405180910390f35b34156103a457600080fd5b6103ba6004808035906020019091905050611081565b6040518082815260200191505060405180910390f35b34156103db57600080fd5b6103f16004808035906020019091905050611150565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200184815260200180602001831515151581526020018281038252848181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156104bc5780601f10610491576101008083540402835291602001916104bc565b820191906000526020600020905b81548152906001019060200180831161049f57829003601f168201915b50509550505050505060405180910390f35b34156104d957600080fd5b6104e16111ac565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156105255780820151818401525b602081019050610509565b505050509050019250505060405180910390f35b341561054457600080fd5b610579600480803590602001909190803590602001909190803515159060200190919080351515906020019091905050611241565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156105bd5780820151818401525b6020810190506105a1565b505050509050019250505060405180910390f35b34156105dc57600080fd5b6105f260048080359060200190919050506113a2565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156106365780820151818401525b60208101905061061a565b505050509050019250505060405180910390f35b341561065557600080fd5b61065d6115d3565b6040518082815260200191505060405180910390f35b341561067e57600080fd5b61069460048080359060200190919050506115d9565b005b34156106a157600080fd5b6106b76004808035906020019091905050611696565b005b34156106c457600080fd5b61073c600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611877565b6040518082815260200191505060405180910390f35b341561075d57600080fd5b610765611897565b6040518082815260200191505060405180910390f35b341561078657600080fd5b61078e61189c565b6040518082815260200191505060405180910390f35b34156107af57600080fd5b6107fa600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506118a2565b005b341561080757600080fd5b61081d6004808035906020019091905050611bc0565b005b60038181548110151561082e57fe5b906000526020600020900160005b915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561089b57600080fd5b81600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615156108f457600080fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600091505b600160038054905003821015610a80578273ffffffffffffffffffffffffffffffffffffffff1660038381548110151561098757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610a725760036001600380549050038154811015156109e757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600383815481101515610a2357fe5b906000526020600020900160005b6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610a80565b5b8180600101925050610951565b6001600381818054905003915081610a989190611fe8565b506003805490506004541115610ab757610ab66003805490506115d9565b5b8273ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a25b5b505b5050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610b5b57600080fd5b81336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610bc657600080fd5b8360008082815260200190815260200160002060030160009054906101000a900460ff16151515610bf657600080fd5b60006001600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e960405160405180910390a35b5b505b50505b5050565b60026020528060005260406000206000915054906101000a900460ff1681565b60016020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b600080600090505b600554811015610d8957838015610d3c575060008082815260200190815260200160002060030160009054906101000a900460ff16155b80610d6f5750828015610d6e575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15610d7b576001820191505b5b8080600101915050610d05565b5b5092915050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610dcb57600080fd5b80600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515610e2557600080fd5b8160008173ffffffffffffffffffffffffffffffffffffffff1614151515610e4c57600080fd5b60016003805490500160045460328211158015610e695750818111155b8015610e76575060008114155b8015610e83575060008214155b1515610e8e57600080fd5b6001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060038054806001018281610efa9190612014565b916000526020600020900160005b87909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508473ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25b5b50505b505b505b50565b6000806000809150600090505b60038054905081101561107957600160008581526020019081526020016000206000600383815481101515610fd757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615611058576001820191505b60045482141561106b576001925061107a565b5b8080600101915050610fa6565b5b5050919050565b600080600090505b600380549050811015611149576001600084815260200190815260200160002060006003838154811015156110ba57fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561113b576001820191505b5b8080600101915050611089565b5b50919050565b60006020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169080600101549080600201908060030160009054906101000a900460ff16905084565b6111b4612040565b600380548060200260200160405190810160405280929190818152602001828054801561123657602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116111ec575b505050505090505b90565b611249612054565b611251612054565b6000806005546040518059106112645750595b908082528060200260200182016040525b50925060009150600090505b600554811015611322578580156112b8575060008082815260200190815260200160002060030160009054906101000a900460ff16155b806112eb57508480156112ea575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15611314578083838151811015156112ff57fe5b90602001906020020181815250506001820191505b5b8080600101915050611281565b8787036040518059106113325750595b908082528060200260200182016040525b5093508790505b8681101561139657828181518110151561136057fe5b906020019060200201518489830381518110151561137a57fe5b90602001906020020181815250505b808060010191505061134a565b5b505050949350505050565b6113aa612040565b6113b2612040565b6000806003805490506040518059106113c85750595b908082528060200260200182016040525b50925060009150600090505b60038054905081101561152b5760016000868152602001908152602001600020600060038381548110151561141657fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561151d5760038181548110151561149f57fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683838151811015156114da57fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506001820191505b5b80806001019150506113e5565b816040518059106115395750595b908082528060200260200182016040525b509350600090505b818110156115ca57828181518110151561156857fe5b90602001906020020151848281518110151561158057fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b8080600101915050611552565b5b505050919050565b60055481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561161357600080fd5b600380549050816032821115801561162b5750818111155b8015611638575060008114155b8015611645575060008214155b151561165057600080fd5b826004819055507fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a836040518082815260200191505060405180910390a15b5b50505b50565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615156116ef57600080fd5b81600080600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415151561174b57600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515156117b757600080fd5b600180600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef60405160405180910390a361186c85611bc0565b5b5b50505b505b5050565b6000611884848484611e6c565b905061188f81611696565b5b9392505050565b603281565b60045481565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156118de57600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561193757600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615151561199157600080fd5b600092505b600380549050831015611a7f578473ffffffffffffffffffffffffffffffffffffffff166003848154811015156119c957fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611a715783600384815481101515611a2257fe5b906000526020600020900160005b6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611a7f565b5b8280600101935050611996565b6000600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508473ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25b5b505b505b505050565b600033600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611c1b57600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611c8657600080fd5b8460008082815260200190815260200160002060030160009054906101000a900460ff16151515611cb657600080fd5b611cbf86610f99565b15611e6057600080878152602001908152602001600020945060018560030160006101000a81548160ff021916908315150217905550611ddd8560000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866001015487600201805460018160011615610100020316600290049050886002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611dd35780601f10611da857610100808354040283529160200191611dd3565b820191906000526020600020905b815481529060010190602001808311611db657829003601f168201915b5050505050611fc0565b15611e1457857f33e13ecb54c3076d8e8bb8c2881800a4d972b792045ffae98fdf46df365fed7560405160405180910390a2611e5f565b857f526441bb6c1aba3c9a4a6ca1d6545da9c2333c8c48343ef398eb858d72b7923660405160405180910390a260008560030160006101000a81548160ff0219169083151502179055505b5b5b5b505b50505b505050565b60008360008173ffffffffffffffffffffffffffffffffffffffff1614151515611e9557600080fd5b60055491506080604051908101604052808673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018481526020016000151581525060008084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550602082015181600101556040820151816002019080519060200190611f54929190612068565b5060608201518160030160006101000a81548160ff0219169083151502179055509050506001600560008282540192505081905550817fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5160405160405180910390a25b5b509392505050565b6000806040516020840160008287838a8c6187965a03f1925050508091505b50949350505050565b81548183558181151161200f5781836000526020600020918201910161200e91906120e8565b5b505050565b81548183558181151161203b5781836000526020600020918201910161203a91906120e8565b5b505050565b602060405190810160405280600081525090565b602060405190810160405280600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106120a957805160ff19168380011785556120d7565b828001600101855582156120d7579182015b828111156120d65782518255916020019190600101906120bb565b5b5090506120e491906120e8565b5090565b61210a91905b808211156121065760008160009055506001016120ee565b5090565b905600a165627a7a72305820f1129b699b3017535535a920e15503cd06af1f5c77637c0637cc29355b1dad3400290000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000cb1437200aea736788f1fc56f327c0456c3598d00000000000000000000000074dd76e24b2cfb43c1b1a4498295d553d0843746000000000000000000000000eeb4ceee443f9e0d17bdbd6daa241681ee5e51c2000000000000000000000000a005caea55375ae20e3aaef746113535503abc19" + }, + "0x120470000000000000000000000000000000000a": { + "balance": "11310499300000000000000000", + "constructor": "0x606060405234156200001057600080fd5b6040516200240138038062002401833981016040528080518201919060200180519060200190919050505b600082518260328211158015620000525750818111155b801562000060575060008114155b80156200006e575060008214155b15156200007a57600080fd5b600092505b8451831015620001b6576002600086858151811015156200009c57fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161580156200012b5750600085848151811015156200010857fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1614155b15156200013757600080fd5b60016002600087868151811015156200014c57fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055505b82806001019350506200007f565b8460039080519060200190620001ce929190620001e3565b50836004819055505b5b5050505050620002b8565b8280548282559060005260206000209081019282156200025f579160200282015b828111156200025e5782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019062000204565b5b5090506200026e919062000272565b5090565b620002b591905b80821115620002b157600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555060010162000279565b5090565b90565b61213980620002c86000396000f3006060604052361561011b576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063025e7c2714610177578063173825d9146101da57806320ea8d86146102135780632f54bf6e146102365780633411c81c1461028757806354741525146102e15780637065cb4814610325578063784547a71461035e5780638b51d13f146103995780639ace38c2146103d0578063a0e67e2b146104ce578063a8abe69a14610539578063b5dc40c3146105d1578063b77bf6001461064a578063ba51a6df14610673578063c01a8c8414610696578063c6427474146106b9578063d74f8edd14610752578063dc8452cd1461077b578063e20056e6146107a4578063ee22610b146107fc575b5b6000341115610174573373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a25b5b005b341561018257600080fd5b610198600480803590602001909190505061081f565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101e557600080fd5b610211600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061085f565b005b341561021e57600080fd5b6102346004808035906020019091905050610b02565b005b341561024157600080fd5b61026d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610cae565b604051808215151515815260200191505060405180910390f35b341561029257600080fd5b6102c7600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610cce565b604051808215151515815260200191505060405180910390f35b34156102ec57600080fd5b61030f600480803515159060200190919080351515906020019091905050610cfd565b6040518082815260200191505060405180910390f35b341561033057600080fd5b61035c600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610d91565b005b341561036957600080fd5b61037f6004808035906020019091905050610f99565b604051808215151515815260200191505060405180910390f35b34156103a457600080fd5b6103ba6004808035906020019091905050611081565b6040518082815260200191505060405180910390f35b34156103db57600080fd5b6103f16004808035906020019091905050611150565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200184815260200180602001831515151581526020018281038252848181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156104bc5780601f10610491576101008083540402835291602001916104bc565b820191906000526020600020905b81548152906001019060200180831161049f57829003601f168201915b50509550505050505060405180910390f35b34156104d957600080fd5b6104e16111ac565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156105255780820151818401525b602081019050610509565b505050509050019250505060405180910390f35b341561054457600080fd5b610579600480803590602001909190803590602001909190803515159060200190919080351515906020019091905050611241565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156105bd5780820151818401525b6020810190506105a1565b505050509050019250505060405180910390f35b34156105dc57600080fd5b6105f260048080359060200190919050506113a2565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156106365780820151818401525b60208101905061061a565b505050509050019250505060405180910390f35b341561065557600080fd5b61065d6115d3565b6040518082815260200191505060405180910390f35b341561067e57600080fd5b61069460048080359060200190919050506115d9565b005b34156106a157600080fd5b6106b76004808035906020019091905050611696565b005b34156106c457600080fd5b61073c600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611877565b6040518082815260200191505060405180910390f35b341561075d57600080fd5b610765611897565b6040518082815260200191505060405180910390f35b341561078657600080fd5b61078e61189c565b6040518082815260200191505060405180910390f35b34156107af57600080fd5b6107fa600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506118a2565b005b341561080757600080fd5b61081d6004808035906020019091905050611bc0565b005b60038181548110151561082e57fe5b906000526020600020900160005b915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561089b57600080fd5b81600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615156108f457600080fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600091505b600160038054905003821015610a80578273ffffffffffffffffffffffffffffffffffffffff1660038381548110151561098757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610a725760036001600380549050038154811015156109e757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600383815481101515610a2357fe5b906000526020600020900160005b6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610a80565b5b8180600101925050610951565b6001600381818054905003915081610a989190611fe8565b506003805490506004541115610ab757610ab66003805490506115d9565b5b8273ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a25b5b505b5050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610b5b57600080fd5b81336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610bc657600080fd5b8360008082815260200190815260200160002060030160009054906101000a900460ff16151515610bf657600080fd5b60006001600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e960405160405180910390a35b5b505b50505b5050565b60026020528060005260406000206000915054906101000a900460ff1681565b60016020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b600080600090505b600554811015610d8957838015610d3c575060008082815260200190815260200160002060030160009054906101000a900460ff16155b80610d6f5750828015610d6e575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15610d7b576001820191505b5b8080600101915050610d05565b5b5092915050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610dcb57600080fd5b80600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515610e2557600080fd5b8160008173ffffffffffffffffffffffffffffffffffffffff1614151515610e4c57600080fd5b60016003805490500160045460328211158015610e695750818111155b8015610e76575060008114155b8015610e83575060008214155b1515610e8e57600080fd5b6001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060038054806001018281610efa9190612014565b916000526020600020900160005b87909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508473ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25b5b50505b505b505b50565b6000806000809150600090505b60038054905081101561107957600160008581526020019081526020016000206000600383815481101515610fd757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615611058576001820191505b60045482141561106b576001925061107a565b5b8080600101915050610fa6565b5b5050919050565b600080600090505b600380549050811015611149576001600084815260200190815260200160002060006003838154811015156110ba57fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561113b576001820191505b5b8080600101915050611089565b5b50919050565b60006020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169080600101549080600201908060030160009054906101000a900460ff16905084565b6111b4612040565b600380548060200260200160405190810160405280929190818152602001828054801561123657602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116111ec575b505050505090505b90565b611249612054565b611251612054565b6000806005546040518059106112645750595b908082528060200260200182016040525b50925060009150600090505b600554811015611322578580156112b8575060008082815260200190815260200160002060030160009054906101000a900460ff16155b806112eb57508480156112ea575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15611314578083838151811015156112ff57fe5b90602001906020020181815250506001820191505b5b8080600101915050611281565b8787036040518059106113325750595b908082528060200260200182016040525b5093508790505b8681101561139657828181518110151561136057fe5b906020019060200201518489830381518110151561137a57fe5b90602001906020020181815250505b808060010191505061134a565b5b505050949350505050565b6113aa612040565b6113b2612040565b6000806003805490506040518059106113c85750595b908082528060200260200182016040525b50925060009150600090505b60038054905081101561152b5760016000868152602001908152602001600020600060038381548110151561141657fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561151d5760038181548110151561149f57fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683838151811015156114da57fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506001820191505b5b80806001019150506113e5565b816040518059106115395750595b908082528060200260200182016040525b509350600090505b818110156115ca57828181518110151561156857fe5b90602001906020020151848281518110151561158057fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b8080600101915050611552565b5b505050919050565b60055481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561161357600080fd5b600380549050816032821115801561162b5750818111155b8015611638575060008114155b8015611645575060008214155b151561165057600080fd5b826004819055507fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a836040518082815260200191505060405180910390a15b5b50505b50565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615156116ef57600080fd5b81600080600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415151561174b57600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515156117b757600080fd5b600180600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef60405160405180910390a361186c85611bc0565b5b5b50505b505b5050565b6000611884848484611e6c565b905061188f81611696565b5b9392505050565b603281565b60045481565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156118de57600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561193757600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615151561199157600080fd5b600092505b600380549050831015611a7f578473ffffffffffffffffffffffffffffffffffffffff166003848154811015156119c957fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611a715783600384815481101515611a2257fe5b906000526020600020900160005b6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611a7f565b5b8280600101935050611996565b6000600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508473ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25b5b505b505b505050565b600033600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611c1b57600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611c8657600080fd5b8460008082815260200190815260200160002060030160009054906101000a900460ff16151515611cb657600080fd5b611cbf86610f99565b15611e6057600080878152602001908152602001600020945060018560030160006101000a81548160ff021916908315150217905550611ddd8560000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866001015487600201805460018160011615610100020316600290049050886002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611dd35780601f10611da857610100808354040283529160200191611dd3565b820191906000526020600020905b815481529060010190602001808311611db657829003601f168201915b5050505050611fc0565b15611e1457857f33e13ecb54c3076d8e8bb8c2881800a4d972b792045ffae98fdf46df365fed7560405160405180910390a2611e5f565b857f526441bb6c1aba3c9a4a6ca1d6545da9c2333c8c48343ef398eb858d72b7923660405160405180910390a260008560030160006101000a81548160ff0219169083151502179055505b5b5b5b505b50505b505050565b60008360008173ffffffffffffffffffffffffffffffffffffffff1614151515611e9557600080fd5b60055491506080604051908101604052808673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018481526020016000151581525060008084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550602082015181600101556040820151816002019080519060200190611f54929190612068565b5060608201518160030160006101000a81548160ff0219169083151502179055509050506001600560008282540192505081905550817fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5160405160405180910390a25b5b509392505050565b6000806040516020840160008287838a8c6187965a03f1925050508091505b50949350505050565b81548183558181151161200f5781836000526020600020918201910161200e91906120e8565b5b505050565b81548183558181151161203b5781836000526020600020918201910161203a91906120e8565b5b505050565b602060405190810160405280600081525090565b602060405190810160405280600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106120a957805160ff19168380011785556120d7565b828001600101855582156120d7579182015b828111156120d65782518255916020019190600101906120bb565b5b5090506120e491906120e8565b5090565b61210a91905b808211156121065760008160009055506001016120ee565b5090565b905600a165627a7a72305820f1129b699b3017535535a920e15503cd06af1f5c77637c0637cc29355b1dad3400290000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000cb1437200aea736788f1fc56f327c0456c3598d00000000000000000000000074dd76e24b2cfb43c1b1a4498295d553d0843746000000000000000000000000eeb4ceee443f9e0d17bdbd6daa241681ee5e51c2000000000000000000000000a005caea55375ae20e3aaef746113535503abc19" + }, + "0x0cB1437200aea736788f1Fc56F327c0456c3598D": { + "balance": "250000000000000000" + }, + "0x74dd76E24B2CFB43C1b1a4498295d553D0843746": { + "balance": "250000000000000000" + }, + "0xeeB4CEEe443F9e0D17BdBD6Daa241681EE5E51c2": { + "balance": "250000000000000000" + }, + "0xA005caEa55375ae20e3aAEF746113535503ABC19": { + "balance": "250000000000000000" + }, + "0x1204700000000000000000000000000000000001": { + "constructor": "0x60806040523480156200001157600080fd5b5060405162002f1538038062002f15833981018060405260608110156200003757600080fd5b81019080805190602001909291908051906020019092919080516401000000008111156200006457600080fd5b828101905060208101848111156200007b57600080fd5b81518560208202830111640100000000821117156200009957600080fd5b5050929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415620001e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602481526020018062002ec56024913960400191505060405180910390fd5b60018151101562000242576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c81526020018062002ee9602c913960400191505060405180910390fd5b6200025c8362000473640100000000026401000000009004565b6200027682620005d5640100000000026401000000009004565b60008090505b81518110156200042057600073ffffffffffffffffffffffffffffffffffffffff16828281518110620002ab57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614156200033e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f56616c696461746f7220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b6001600360008484815181106200035157fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff02191690836003811115620003b257fe5b02179055508060036000848481518110620003c957fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018190555080806001019150506200027c565b508060029080519060200190620004399291906200065c565b50600260019080546200044e929190620006eb565b506001600060146101000a81548160ff02191690831515021790555050505062000788565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141562000517576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b80600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167fe8ec518081a7aa1fc5d586a5443a858ab130be8b8e39b545172c879a7e242c6b60405160405180910390a250565b828054828255906000526020600020908101928215620006d8579160200282015b82811115620006d75782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550916020019190600101906200067d565b5b509050620006e7919062000742565b5090565b8280548282559060005260206000209081019282156200072f5760005260206000209182015b828111156200072e57825482559160010191906001019062000711565b5b5090506200073e919062000742565b5090565b6200078591905b808211156200078157600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555060010162000749565b5090565b90565b61272d80620007986000396000f3fe608060405234801561001057600080fd5b506004361061015f576000357c0100000000000000000000000000000000000000000000000000000000900480639f723637116100d5578063b980490911610099578063b980490914610594578063bd21442a146105f0578063c805f68b146106b3578063d826b7f1146106f7578063f2fde38b14610765578063f3aeac02146107a95761015f565b80639f7236371461040e578063a00745b61461046d578063a6940b07146104c9578063b3f05b9714610513578063b7ab4db5146105355761015f565b8063455701d611610127578063455701d6146102eb5780634d238c8e1461034a578063715018a61461038e57806375286211146103985780638da5cb5b146103a25780638f32d59b146103ec5761015f565b8063267fa41d1461016457806334ba3c1b146101c057806340550a1c146101de57806340a141ff1461023a578063418349551461027e575b600080fd5b6101a66004803603602081101561017a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610805565b604051808215151515815260200191505060405180910390f35b6101c8610877565b6040518082815260200191505060405180910390f35b610220600480360360208110156101f457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610884565b604051808215151515815260200191505060405180910390f35b61027c6004803603602081101561025057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610965565b005b6102c06004803603602081101561029457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab0565b604051808360038111156102d057fe5b60ff1681526020018281526020019250505060405180910390f35b6102f3610ae1565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561033657808201518184015260208101905061031b565b505050509050019250505060405180910390f35b61038c6004803603602081101561036057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b6f565b005b610396610d79565b005b6103a0610eb2565b005b6103aa611328565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103f4611351565b604051808215151515815260200191505060405180910390f35b6104166113a8565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561045957808201518184015260208101905061043e565b505050509050019250505060405180910390f35b6104af6004803603602081101561048357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506114d7565b604051808215151515815260200191505060405180910390f35b6104d16115b8565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61051b6115de565b604051808215151515815260200191505060405180910390f35b61053d6115f1565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610580578082015181840152602081019050610565565b505050509050019250505060405180910390f35b6105d6600480360360208110156105aa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061167f565b604051808215151515815260200191505060405180910390f35b6106b16004803603608081101561060657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561066d57600080fd5b82018360208201111561067f57600080fd5b803590602001918460018302840111640100000000831117156106a157600080fd5b90919293919293905050506116f0565b005b6106f5600480360360208110156106c957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061194c565b005b6107636004803603606081101561070d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611aff565b005b6107a76004803603602081101561077b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611d59565b005b6107eb600480360360208110156107bf57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611ddf565b604051808215151515815260200191505060405180910390f35b60006001600381111561081457fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16600381111561086f57fe5b149050919050565b6000600180549050905090565b60006001600381111561089357fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff1660038111156108ee57fe5b148061095e575060038081111561090157fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16600381111561095c57fe5b145b9050919050565b61096d611351565b6109df576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600060149054906101000a900460ff16610a44576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806126736022913960400191505060405180910390fd5b80610a4e81610884565b610aa3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806126516022913960400191505060405180910390fd5b610aac82611e51565b5050565b60036020528060005260406000206000915090508060000160009054906101000a900460ff16908060010154905082565b60606002805480602002602001604051908101604052809291908181526020018280548015610b6557602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610b1b575b5050505050905090565b610b77611351565b610be9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600060149054906101000a900460ff16610c4e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806126736022913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610cf1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f56616c696461746f7220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b610cfa81610884565b15610d6d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f546869732076616c696461746f7220697320616c72656164792061637469766581525060200191505060405180910390fd5b610d76816120a4565b50565b610d81611351565b610df3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600060149054906101000a900460ff1615610f35576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f56616c696461746f72207365742069732066696e616c697a656400000000000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610ff8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f74207468652052656c617920636f6e747261637481525060200191505060405180910390fd5b6001600060146101000a81548160ff02191690831515021790555060008090505b6002805490508110156110dd576000600360006002848154811061103957fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060018160000160006101000a81548160ff021916908360038111156110c157fe5b0217905550818160010181905550508080600101915050611019565b50600073ffffffffffffffffffffffffffffffffffffffff16600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461126757600060036000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff021916908360038111156111b557fe5b0217905550600060036000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101819055506000600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b6002600190805461127992919061251f565b507f8564cd629b15f47dc310d45bcbfc9bcf5420b0d51bf0659a16c67f91d276325360016040518080602001828103825283818154815260200191508054801561131857602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116112ce575b50509250505060405180910390a1565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6060600180549050600280549050111561144a57600280548060200260200160405190810160405280929190818152602001828054801561143e57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116113f4575b505050505090506114d4565b60018054806020026020016040519081016040528092919081815260200182805480156114cc57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311611482575b505050505090505b90565b6000600260038111156114e657fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16600381111561154157fe5b14806115b1575060038081111561155457fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff1660038111156115af57fe5b145b9050919050565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060149054906101000a900460ff1681565b6060600180548060200260200160405190810160405280929190818152602001828054801561167557602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001906001019080831161162b575b5050505050905090565b600060038081111561168d57fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff1660038111156116e857fe5b149050919050565b846116fa81610884565b61174f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806126516022913960400191505060405180910390fd5b8461175981610884565b6117ae576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806126516022913960400191505060405180910390fd5b84438110611824576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f426c6f636b206e756d626572206973206e6f742076616c69640000000000000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146118e7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f74207468652052656c617920636f6e747261637481525060200191505060405180910390fd5b858773ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff167f729a19138e072a5a8d3a56d74ae0b5c84530f09aacd6e12b24c5b2fdc3f8a3d060405160405180910390a45050505050505050565b611954611351565b6119c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611a4c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260248152602001806126066024913960400191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611af3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260408152602001806126956040913960400191505060405180910390fd5b611afc81612179565b50565b82611b0981610884565b611b5e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806126516022913960400191505060405180910390fd5b82611b6881610884565b611bbd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806126516022913960400191505060405180910390fd5b82438110611c33576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f426c6f636b206e756d626572206973206e6f742076616c69640000000000000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611cf6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f74207468652052656c617920636f6e747261637481525060200191505060405180910390fd5b838573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fbc459bd9db54016b1966d0fe812bbe0a82cd627ae3eacd01727dc63a432ca41b60405160405180910390a4505050505050565b611d61611351565b611dd3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b611ddc81612200565b50565b600060026003811115611dee57fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff166003811115611e4957fe5b149050919050565b600160028054905011611eaf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061262a6027913960400191505060405180910390fd5b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010154905060006001600280549050039050600060028281548110611f1257fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508060028481548110611f4d57fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101819055506002805480919060019003611ff19190612571565b5060038060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff0219169083600381111561205057fe5b021790555083600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061209e612361565b50505050565b6002600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff0219169083600381111561210357fe5b021790555060028190806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050612176612361565b50565b80600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167fe8ec518081a7aa1fc5d586a5443a858ab130be8b8e39b545172c879a7e242c6b60405160405180910390a250565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156122a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008060146101000a81548160ff021916908315150217905550600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a084718a600143034060026040518363ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018083815260200180602001828103825283818154815260200191508054801561246c57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311612422575b50509350505050602060405180830381600087803b15801561248d57600080fd5b505af11580156124a1573d6000803e3d6000fd5b505050506040513d60208110156124b757600080fd5b810190808051906020019092919050505061251d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602d8152602001806126d5602d913960400191505060405180910390fd5b565b8280548282559060005260206000209081019282156125605760005260206000209182015b8281111561255f578254825591600101919060010190612544565b5b50905061256d919061259d565b5090565b8154818355818111156125985781836000526020600020918201910161259791906125e0565b5b505050565b6125dd91905b808211156125d957600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016125a3565b5090565b90565b61260291905b808211156125fe5760008160009055506001016125e6565b5090565b9056fe52656c617920636f6e747261637420616464726573732063616e6e6f74206265203078305468657265206d757374206265206174206c6561737420312076616c696461746f72206c65667441646472657373206973206e6f7420616e206163746976652076616c696461746f7256616c696461746f7220736574206973206e6f742066696e616c697a6564207965744e65772072656c617920636f6e747261637420616464726573732063616e6e6f74206265207468652073616d65206173207468652063757272656e74206f6e6552656c617920636f6e747261637420496e6974696174654368616e67652063616c6c6261636b206661696c6564a165627a7a72305820c6b083565ee91eecaf8e5a6f14e1677206b36466bdfe0248a47919657b68255b002952656c617920636f6e747261637420616464726573732063616e6e6f74206265203078305468657265206d757374206265206174206c6561737420312076616c696461746f7220696e697469616c6c79000000000000000000000000120470000000000000000000000000000000000500000000000000000000000012047000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000300000000000000000000000036f67dd84e7327c10c7ead6c429a47189798fbdc00000000000000000000000020df7a4e8408add37c6a5c4afc1b1509924619fe00000000000000000000000077901f14183b1669c80e8c6137ff6721c9a26b25" + }, + "0x1204700000000000000000000000000000000000": { + "constructor": "0x608060405273fffffffffffffffffffffffffffffffffffffffe600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555034801561006557600080fd5b506040516040806116c18339810180604052604081101561008557600080fd5b810190808051906020019092919080519060200190929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a361017482610193640100000000026401000000009004565b61018c816102f4640100000000026401000000009004565b5050610506565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610236576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b80600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610398576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f416464726573732063616e6e6f7420626520307830000000000000000000000081525060200191505060405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561043f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252604281526020018061167f6042913960600191505060405180910390fd5b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905082600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f4fea88aaf04c303804bb211ecc32a00ac8e5f0656bb854cad8a4a2e438256b7460405160405180910390a3505050565b61116a806105156000396000f3fe608060405234801561001057600080fd5b50600436106100d1576000357c010000000000000000000000000000000000000000000000000000000090048063b7ab4db51161008e578063b7ab4db51461023b578063bd9656771461029a578063c476dd40146102de578063d3e848f114610381578063d69f13bb146103cb578063f2fde38b14610419576100d1565b8063715018a6146100d657806375286211146100e05780638da5cb5b146100ea5780638f32d59b14610134578063a084718a14610156578063ae3783d6146101f1575b600080fd5b6100de61045d565b005b6100e8610596565b005b6100f26106f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61013c610722565b604051808215151515815260200191505060405180910390f35b6101d76004803603604081101561016c57600080fd5b81019080803590602001909291908035906020019064010000000081111561019357600080fd5b8201836020820111156101a557600080fd5b803590602001918460208302840111640100000000831117156101c757600080fd5b9091929391929390505050610779565b604051808215151515815260200191505060405180910390f35b6101f9610893565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102436108b9565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561028657808201518184015260208101905061026b565b505050509050019250505060405180910390f35b6102dc600480360360208110156102b057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109d5565b005b61037f600480360360608110156102f457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561033b57600080fd5b82018360208201111561034d57600080fd5b8035906020019184600183028401116401000000008311171561036f57600080fd5b9091929391929390505050610a5b565b005b610389610ba6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610417600480360360408110156103e157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610bcc565b005b61045b6004803603602081101561042f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ce1565b005b610465610722565b6104d7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610659576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f53656e646572206973206e6f742073797374656d00000000000000000000000081525060200191505060405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663752862116040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401600060405180830381600087803b1580156106df57600080fd5b505af11580156106f3573d6000803e3d6000fd5b50505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610821576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061111d6022913960400191505060405180910390fd5b837f55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c89848460405180806020018281038252848482818152602001925060200280828437600081840152601f19601f820116905080830192505050935050505060405180910390a2600190509392505050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b7ab4db56040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b15801561093f57600080fd5b505afa158015610953573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250602081101561097d57600080fd5b81019080805164010000000081111561099557600080fd5b828101905060208101848111156109ab57600080fd5b81518560208202830111640100000000821117156109c857600080fd5b5050929190505050905090565b6109dd610722565b610a4f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b610a5881610d67565b50565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663bd21442a33868686866040518663ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509650505050505050600060405180830381600087803b158015610b8857600080fd5b505af1158015610b9c573d6000803e3d6000fd5b5050505050505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d826b7f13384846040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019350505050600060405180830381600087803b158015610cc557600080fd5b505af1158015610cd9573d6000803e3d6000fd5b505050505050565b610ce9610722565b610d5b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b610d6481610f79565b50565b80600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610e0b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f416464726573732063616e6e6f7420626520307830000000000000000000000081525060200191505060405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610eb2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260428152602001806110db6042913960600191505060405180910390fd5b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905082600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f4fea88aaf04c303804bb211ecc32a00ac8e5f0656bb854cad8a4a2e438256b7460405160405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561101c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fe4e65772072656c6179656420636f6e747261637420616464726573732063616e6e6f74206265207468652073616d65206173207468652063757272656e74206f6e6553656e646572206973206e6f74207468652052656c6179656420636f6e7472616374a165627a7a72305820df8e72037cf9237ba09d135b3962a00a0186ba17dcbbe421274543975b2c346100294e65772072656c6179656420636f6e747261637420616464726573732063616e6e6f74206265207468652073616d65206173207468652063757272656e74206f6e6500000000000000000000000012047000000000000000000000000000000000050000000000000000000000001204700000000000000000000000000000000001" + }, + "0x1204700000000000000000000000000000000002": { + "constructor": "0x608060405273fffffffffffffffffffffffffffffffffffffffe600960006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503480156200006657600080fd5b5060405160408062001b22833981018060405260408110156200008857600080fd5b810190808051906020019092919080519060200190929190505050620000bc6200010c640100000000026401000000009004565b81600b60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600a81905550505062000825565b60405180610f0001604052806704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704395680a6af46808152602001670438cfa9de4a0e808152602001670437eeee904c06808152602001670436b44ebcb52e8081526020016704351fca638586808152602001670433316184bd0e808152602001670430e914205bc680815260200167042e46e23661ae80815260200167042b4acbc6cec6808152602001670427f4d0d1a30e80815260200167042444f156de868081526020016704203b2d56812e80815260200167041bd784d08b0680815260200167041719f7c4fc0e808152602001670412028633d44680815260200167040c91301d13ae808152602001670406c5f580ba46808152602001670400a0d65ec80e8081526020016703fa21d2b73d068081526020016703f348ea8a192e8081526020016703ec161dd75c868081526020016703e4896c9f070e8081526020016703dca2d6e118c68081526020016703d4625c9d91ae8081526020016703cbc7fdd471c68081526020016703c2d3ba85b90e8081526020016703b98592b167868081526020016703afdd86577d2e8081526020016703a5db9577fa0680815260200167039b7fc012de0e808152602001670390ca06282946808152602001670385ba67b7dbae80815260200167037a50e4c1f54680815260200167036e8d7d46760e8081526020016703627031455e06808152602001670355f900bead2e80815260200167034927ebb2638680815260200167033bfcf220810e80815260200167032e78140905c680815260200167032099516bf1ae80815260200167031260aa4944c6808152602001670303ce1ea0ff0e8081526020016702f4e1ae7320868081526020016702e59b59bfa92e8081526020016702d5fb208699068081526020016702c60102c7f00e8081526020016702b5ad0083ae468081526020016702a4ff19b9d3ae808152602001670293f74e6a6046808152602001670282959e95540e808152602001670270da0a3aaf0680815260200167025ec4915a712e80815260200167024c5533f49a86808152602001670238da1d6b04400081526020016702260c5cdf4d240081526020016702138f83989f90008152602001670201639196fb840081526020016701ef8886da61000081526020016701ddfe6362d0040081526020016701ccc5273048900081526020016701bbdcd242caa40081526020016701ab45649a564000815260200167019afede36eb6400815260200167018b093f188a1000815260200167017b64873f324400815260200167016c10b6aae40000815260200167015d0dcd5b9f4400815260200167014e5bcb51641000815260200167013ffab08c3264008152602001670131ea7d0c0a400081526020016701242b30d0eba4008152602001670116bccbdad6900081526020016701099f4e29cb0400815260200166fcd2b7bdc90000815260200166f0570896d08400815260200166e42c40b4e19000815260200166d8526017fc2400815260200166ccc966c0204000815260200166c19154ad4de400815260200166b6aa29df851000815260200166ac13e656c5c400815260200166a1ce8a1310000081526020016697da151463c4008152602001668e36875ac1100081526020016684e3e0e627e4008152602001667be221b6984000815260200166733149cc1224008152602001666ad1592695900081526020016662c24fc62284008152602001665b042daab900008152602001665396f2d45904008152602001664c7a9f4302900081526020016645af32f6b5a4008152602001663f34adef724000815260200166390b102d386400815260200166333259b00810008152602001662daa8a77e144008152602001662873a284c40000815260200166238da1d6b044008152602001661ef8886da610008152602001661ab45649a5640081526020016616c10b6aae4000815260200166131ea7d0c0a4008152602001660fcd2b7bdc90008152602001660ccc966c0204008152602001660a1ce8a131000081526020016607be221b69840081526020016605b042daab900081526020016603f34adef7240081526020016602873a284c4000815260200166016c10b6aae400815260200165a1ce8a1310008152602001652873a284c40081525060009060786200078e929190620007ab565b506208052060018190555060008054905060015402600281905550565b828054828255906000526020600020908101928215620007ea579160200282015b82811115620007e9578251825591602001919060010190620007cc565b5b509050620007f99190620007fd565b5090565b6200082291905b808211156200081e57600081600090555060010162000804565b5090565b90565b6112ed80620008356000396000f3fe608060405234801561001057600080fd5b5060043610610132576000357c0100000000000000000000000000000000000000000000000000000000900480634476d66a116100bf57806394f7f62b1161008e57806394f7f62b14610451578063df6a503014610493578063e6e100db146104b1578063f91c289814610509578063fb1ac5251461067457610132565b80634476d66a14610363578063553a5c85146103a557806358ceb672146103c357806360d4b8be146103cd57610132565b80631a488047116101065780631a4880471461020357806330f6eb161461022157806333ea51a81461028357806337339a16146102c75780633d84b8c11461030b57610132565b8062f380f414610137578063078d8e7a146101815780630f411cdb146101a357806318129375146101c1575b600080fd5b61013f610692565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101896106b8565b604051808215151515815260200191505060405180910390f35b6101ab6106c8565b6040518082815260200191505060405180910390f35b6101ed600480360360208110156101d757600080fd5b81019080803590602001909291905050506106ce565b6040518082815260200191505060405180910390f35b61020b6106ef565b6040518082815260200191505060405180910390f35b61026d6004803603604081101561023757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106f5565b6040518082815260200191505060405180910390f35b6102c56004803603602081101561029957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061071a565b005b610309600480360360208110156102dd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061079b565b005b61034d6004803603602081101561032157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506108a2565b6040518082815260200191505060405180910390f35b61038f6004803603602081101561037957600080fd5b81019080803590602001909291905050506108ba565b6040518082815260200191505060405180910390f35b6103ad6108d2565b6040518082815260200191505060405180910390f35b6103cb6108d8565b005b61040f600480360360208110156103e357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061093c565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61047d6004803603602081101561046757600080fd5b810190808035906020019092919050505061096f565b6040518082815260200191505060405180910390f35b61049b6109b3565b6040518082815260200191505060405180910390f35b6104f3600480360360208110156104c757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109b9565b6040518082815260200191505060405180910390f35b6105d56004803603604081101561051f57600080fd5b810190808035906020019064010000000081111561053c57600080fd5b82018360208201111561054e57600080fd5b8035906020019184602083028401116401000000008311171561057057600080fd5b90919293919293908035906020019064010000000081111561059157600080fd5b8201836020820111156105a357600080fd5b803590602001918460208302840111640100000000831117156105c557600080fd5b90919293919293905050506109d1565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561061c578082015181840152602081019050610601565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561065e578082015181840152602081019050610643565b5050505090500194505050505060405180910390f35b61067c610eca565b6040518082815260200191505060405180910390f35b600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006106c343610ed0565b905090565b60025481565b600081815481106106db57fe5b906000526020600020016000915090505481565b600a5481565b6008602052816000526040600020602052806000526040600020600091509150505481565b80600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461085e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f742074686520636f6d6d756e6974792066756e6481525060200191505060405180910390fd5b80600b60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60066020528060005260406000206000915090505481565b60076020528060005260406000206000915090505481565b60035481565b600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055565b600c6020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600061097a82610ed0565b1561098857600090506109ae565b6000600154838161099557fe5b04815481106109a057fe5b906000526020600020015490505b919050565b60045481565b60056020528060005260406000206000915090505481565b606080600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610a97576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f43616c6c6572206973206e6f74207468652073797374656d000000000000000081525060200191505060405180910390fd5b838390508686905014610af5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602581526020018061129d6025913960400191505060405180910390fd5b60018686905014610b6e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f42656e65666163746f7273206c697374206c656e677468206973206e6f74203181525060200191505060405180910390fd5b600084846000818110610b7d57fe5b9050602002013561ffff1661ffff1614610be2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061127b6022913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff1686866000818110610c0757fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161480610c4b5750610c4a43610ed0565b5b15610cc1576000604051908082528060200260200182016040528015610c805781602001602082028038833980820191505090505b506000604051908082528060200260200182016040528015610cb15781602001602082028038833980820191505090505b5081915080905091509150610ec1565b60606002604051908082528060200260200182016040528015610cf35781602001602082028038833980820191505090505b50905060608151604051908082528060200260200182016040528015610d285781602001602082028038833980820191505090505b509050610d5d88886000818110610d3b57fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff16610edf565b82600081518110610d6a57fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050610dad4361096f565b81600081518110610dba57fe5b602002602001018181525050610df1600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16610edf565b82600181518110610dfe57fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600a5481600181518110610e4857fe5b602002602001018181525050610e8682600081518110610e6457fe5b602002602001015182600081518110610e7957fe5b6020026020010151610f8c565b610eb882600181518110610e9657fe5b602002602001015182600181518110610eab57fe5b6020026020010151611134565b81819350935050505b94509492505050565b60015481565b60006002548210159050919050565b600080600c60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610f825782915050610f87565b809150505b919050565b610fef81600860008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000438152602001908152602001600020546111f290919063ffffffff16565b600860008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060004381526020019081526020016000208190555061109581600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546111f290919063ffffffff16565b600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506110fe8160076000438152602001908152602001600020546111f290919063ffffffff16565b600760004381526020019081526020016000208190555061112a816003546111f290919063ffffffff16565b6003819055505050565b611149816004546111f290919063ffffffff16565b6004819055506111a181600560008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546111f290919063ffffffff16565b600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506111ee8282610f8c565b5050565b600080828401905083811015611270576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f4f766572666c6f77206572726f7200000000000000000000000000000000000081525060200191505060405180910390fd5b809150509291505056fe42656e65666163746f72206973206e6f742074686520626c6f636b20617574686f7242656e65666163746f72732f7479706573206c697374206c656e6774682064696666657273a165627a7a723058209c17c14dc9f6fcdd3ddef3913263be4e76376e948facfc131a975ceda79ba0d6002900000000000000000000000012047000000000000000000000000000000000030000000000000000000000000000000000000000000000000856d3dfb6e26d00" + }, + "0x1204700000000000000000000000000000000004": { + "balance": "40789499640000000000000000", + "constructor": "0x608060405260006001556a21bd8334ca74c3834c00003073ffffffffffffffffffffffffffffffffffffffff16311462000085576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602381526020018062001b5b6023913960400191505060405180910390fd5b6200009e6200010b640100000000026401000000009004565b6a21bd8334ca74c3834c00006001541462000105576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602881526020018062001b0a6028913960400191505060405180910390fd5b620017b4565b6200014a73120470000000000000000000000000000000000a6a084595161401484a000000635d408564620016da640100000000026401000000009004565b62000188735a3977e000000000000000000000000000000000691a784379d99db4200000635db8d244620016da640100000000026401000000009004565b620001c673aae0debd2a4c519364e6098537145369913b26ec6901c761ff4c24e9210000635db8d244620016da640100000000026401000000009004565b620002047336874a6000000000000000000000000000000000691a784379d99db4200000635db8d244620016da640100000000026401000000009004565b6200024273108dd64c9e0d603d56304e97656e011831ed07ff6901c761ff4c24e9210000635db8d244620016da640100000000026401000000009004565b6200028073294f3750000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b620002be732fb294a0000000000000000000000000000000006902d2cd1fdffd73150000635db8d244620016da640100000000026401000000009004565b620002fc73102fb040000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b6200033a732b250010000000000000000000000000000000006902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b620003787335c468d000000000000000000000000000000000696abafbb89b2adcd80000635db8d244620016da640100000000026401000000009004565b620003b673c8be7a00000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b620003f47353f3170000000000000000000000000000000000692004e50f922be25a0000635db8d244620016da640100000000026401000000009004565b6200043373dc918900000000000000000000000000000000006a0215a6ea9b07d650380000635db8d244620016da640100000000026401000000009004565b6200047173b476ee7d610dae7b23b671ebc7bd6112e97729696902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b620004af732908b900000000000000000000000000000000006969e10de76676d0800000635d408564620016da640100000000026401000000009004565b620004ed733b2ef740000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b6200052b731153818a2eb49f0a71b27313c32814fc02e4db50694220bb939da668600000635db8d244620016da640100000000026401000000009004565b6200056973330d6100000000000000000000000000000000006923466459b949a9950000635db8d244620016da640100000000026401000000009004565b620005a7733e5518c20876eab3a42969d032fa7c30599af912691a784379d99db4200000635db8d244620016da640100000000026401000000009004565b620005e57357f33efad76d4b783cf42c9e6cb08f4425dfe96e6902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b6200062373a87e88f0bbc43468cb657294f2479c6f35179b80692004e50f922be25a0000635db8d244620016da640100000000026401000000009004565b62000661734cebef90000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b6200069f73511909cef97475819de66b645f13464285c227ce6934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b620006dd7322fc2310000000000000000000000000000000006902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b6200071b7347919fb8d4e7e360bef0d5a7a2411593dbcc0e776902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b6200075973949423db1bfee1ddec99c9d24a12a6ea27cb34896901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b620007977343ec5680000000000000000000000000000000006907f0e10af47c1c700000635db8d244620016da640100000000026401000000009004565b620007d57367cf1c40622f39fa067b614f13aad6da5dd95f326969e10de76676d0800000635d408564620016da640100000000026401000000009004565b620008137310b9390000000000000000000000000000000000692ab13175efe0a8630000635db8d244620016da640100000000026401000000009004565b620008517376b707604cbd862050b938739637b7bde1b7ad486901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b6200088f73568075f0000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b620008cd73d44fb8de580d34f44789408cc9335c9a9ce0ce4d691ad0235eb930a0540000635db8d244620016da640100000000026401000000009004565b6200090a727801b12345b6f10b69263cd6d4fc50b58c8d80696d688e69e3a9742b0000635db8d244620016da640100000000026401000000009004565b62000948735d66a150000000000000000000000000000000006969e10de76676d0800000635d408564620016da640100000000026401000000009004565b62000987731d258680000000000000000000000000000000006a020057f2c74d5a91080000635db8d244620016da640100000000026401000000009004565b620009c57330e311c00000000000000000000000000000000069355d7ddc4d956e6c0000635db8d244620016da640100000000026401000000009004565b62000a0373428ab4b019ee3a9b9863b2b4bf1885ce6dff9a736902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b62000a41738081f20000000000000000000000000000000000691a784379d99db4200000635db8d244620016da640100000000026401000000009004565b62000a7f735305c8071b604da7fb45059e7ebfa1d674ddfc3569038780827d32a3ab0000635db8d244620016da640100000000026401000000009004565b62000abd7335007170000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b62000afc733484c850000000000000000000000000000000006a0422ca8b0a00a42500000063608a9f84620016da640100000000026401000000009004565b62000b3a73ffd9b871df6e93803c0877e98fc1722b39c00d786902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b62000b787396a5eb172efdf262ed6beaaf0e20c6af71831fc96934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b62000bb6732dd2e140000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b62000bf473656e5569bef7781bf0db199d32027766053501ff69438ec266600555e00000635db8d244620016da640100000000026401000000009004565b62000c32731d53db7000000000000000000000000000000000691a784379d99db4200000635db8d244620016da640100000000026401000000009004565b62000c7073b5b6d8885fbf28f843cc7886de242b811d6952056901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b62000cae734549ef8e287b94f1c0a8b88f55ed8707d74d843c6934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b62000cec73dacd80d8e1d4f117515caa477ee7599cdfc766196902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b62000d2a731f603e000000000000000000000000000000000069d3c21bcecceda1000000635db8d244620016da640100000000026401000000009004565b62000d68735548f000000000000000000000000000000000006902ab1310b5b095920000635db8d244620016da640100000000026401000000009004565b62000da6735bb9ba00000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b62000de4736dd10e41a7a84fe23ab35fefa2f46c9895f87a2d693c8c4bde2deef1680000635db8d244620016da640100000000026401000000009004565b62000e227316337db0000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b62000e6073b999004b49c6b907d4278067da5c85195dcd7fc769081e0dd1d85030b50000635db8d244620016da640100000000026401000000009004565b62000e9e733dbb2290000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b62000edc73559da540000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b62000f1a733a9d83766c03c465851a38daa364ef7deccd1ece6934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b62000f5873b61c11b6e42d459efaee8995c44db08507e468e169477d5529f68a63000000635db8d244620016da640100000000026401000000009004565b62000f9673e803d7955a911106cb8fd79049a2374ff059000369038780827d32a3ab0000635db8d244620016da640100000000026401000000009004565b62000fd473509b057000000000000000000000000000000000691aaebeee26cab7360000635db8d244620016da640100000000026401000000009004565b620010127383980db85394d7c1610f37a90be744432368bad4692393a931b168245d0000635db8d244620016da640100000000026401000000009004565b6200105073db6cc57168c07b83a00f1f8871538446068824fc691a784379d99db4200000635db8d244620016da640100000000026401000000009004565b6200108e735035fba0000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b620010cc73d96c8300000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b6200110b7331178cd0000000000000000000000000000000006a042b4dd5360faca070000063608a9f84620016da640100000000026401000000009004565b62001149732f531158e2305ed4fb4144a2f4085e3d96e1982e6934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b62001188735d5da310000000000000000000000000000000006a017293b0a9e69fd9c00000635db8d244620016da640100000000026401000000009004565b620011c67318b9347000000000000000000000000000000000696abafbb89b2adcd80000635d408564620016da640100000000026401000000009004565b62001203722d4606b65c033769968bcdc63881b90b0853f5692648770b742bb90b0000635db8d244620016da640100000000026401000000009004565b62001241731faa8d10000000000000000000000000000000006969e10de76676d0800000635d408564620016da640100000000026401000000009004565b6200127f7333445720000000000000000000000000000000006902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b620012bd730d1d4e623d10f9fba5db95830f7d3839406c6af26901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b620012fa72c2f65230815d30eaa1a4d057bcf0b72fe3cc4e6902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b62001338739c3a5ec7bd63ecac1b92abe4b01b12ffd50bf3f26969e10de76676d0800000635d408564620016da640100000000026401000000009004565b620013767345635660000000000000000000000000000000006969e10de76676d0800000635d408564620016da640100000000026401000000009004565b620013b2739be61e41490f5227080fa1adbe3ec0a973d59732678ac7230489e80000635cc83884620016da640100000000026401000000009004565b620013f073572f91b0000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b6200142e7339350e4a75d1e50a5072950325328884c11c12326901c761ff4c24e9210000635db8d244620016da640100000000026401000000009004565b6200146c733c96a530000000000000000000000000000000006969e10de76676d0800000635d408564620016da640100000000026401000000009004565b620014aa730ad7ba4af33b485e6f2505c417554631a3e5643f6902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b620014e8735eea7250000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b62001526733ab790e0000000000000000000000000000000006902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b62001564734cf84620000000000000000000000000000000006902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b620015a2735425f6f0000000000000000000000000000000006969e10de76676d0800000635d408564620016da640100000000026401000000009004565b620015e0736d516767e4068fc331bdb331fba7578bdb07a68c69234b04ae4f23156b0000635db8d244620016da640100000000026401000000009004565b6200161e73106a8ff0000000000000000000000000000000006902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b6200165c73102fb9400000000000000000000000000000000069038780827d32a3ab0000635db8d244620016da640100000000026401000000009004565b6200169a737ed62cf71d519d3bf293ef90829508f92f4ccccb6902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b620016d873199a8c5000000000000000000000000000000000691a784379d99db4200000635db8d244620016da640100000000026401000000009004565b565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506000816000015414801562001735575060008160010154145b6200178c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602981526020018062001b326029913960400191505060405180910390fd5b8260016000828254019250508190555082816000018190555081816001018190555050505050565b61034680620017c46000396000f3fe608060405234801561001057600080fd5b5060043610610069576000357c01000000000000000000000000000000000000000000000000000000009004806318a5bbdc1461006e578063192e7a7b146100cd57806330f0dbe0146101115780639976e12f1461012f575b600080fd5b6100b06004803603602081101561008457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061014d565b604051808381526020018281526020019250505060405180910390f35b61010f600480360360208110156100e357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610171565b005b610119610305565b6040518082815260200191505060405180910390f35b610137610314565b6040518082815260200191505060405180910390f35b60006020528060005260406000206000915090508060000154908060010154905082565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050600081600001541161022d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f417661696c61626c6520616d6f756e742069732030000000000000000000000081525060200191505060405180910390fd5b806001015442116102a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f486f6c64696e6720706572696f64206973206e6f74206f76657200000000000081525060200191505060405180910390fd5b600081600001549050600082600001819055508273ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501580156102ff573d6000803e3d6000fd5b50505050565b6a21bd8334ca74c3834c000081565b6001548156fea165627a7a7230582078bf501dd1d053c49557b82491b940e47ebf94b95608375e22da53fcc7120a1a002954617267657420616d6f756e742073686f756c6420657175616c2061637475616c20616d6f756e74486f6c64696e6720666f72207468697320616464726573732077617320616c7265616479207365742e42616c616e63652073686f756c6420657175616c2074617267657420616d6f756e742e" + }, + "0x1204700000000000000000000000000000000006": { + "constructor": "0x6080604052670de0b6b3a76400006003553480156200001d57600080fd5b5060405160208062003e58833981018060405260208110156200003f57600080fd5b8101908080519060200190929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a362000126816200012d640100000000026401000000009004565b506200028f565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415620001d1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b613bb9806200029f6000396000f3fe6080604052600436106101d4576000357c0100000000000000000000000000000000000000000000000000000000900480639269881411610109578063df57b742116100a7578063ef5454d611610081578063ef5454d614610d59578063f25eb5c114610e17578063f2fde38b14610e2e578063f6d339e414610e7f576101d4565b8063df57b74214610b62578063e30bd74014610bdd578063eadf976014610ca7576101d4565b8063ac72c120116100e3578063ac72c120146109c5578063c3a3582514610a18578063ddca3f4314610abc578063deb931a214610ae7576101d4565b806392698814146108855780639890220b146108d8578063ac4e73f914610907576101d4565b806369fe0e2d1161017657806379ce9fac1161015057806379ce9fac146106e85780638da5cb5b1461075b5780638f32d59b146107b257806390b97fc1146107e1576101d4565b806369fe0e2d146105b45780636a1acc3f14610607578063715018a6146106d1576101d4565b80633f3935d1116101b25780633f3935d1146103ad578063432ced041461044b5780634f39ca59146104915780636795dbcd146104e4576101d4565b806306b2ff47146101d957806319362a2814610242578063267b6922146102f4575b600080fd5b3480156101e557600080fd5b50610228600480360360208110156101fc57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610f47565b604051808215151515815260200191505060405180910390f35b34801561024e57600080fd5b506102da6004803603606081101561026557600080fd5b81019080803590602001909291908035906020019064010000000081111561028c57600080fd5b82018360208201111561029e57600080fd5b803590602001918460018302840111640100000000831117156102c057600080fd5b909192939192939080359060200190929190505050610fa7565b604051808215151515815260200191505060405180910390f35b34801561030057600080fd5b5061032d6004803603602081101561031757600080fd5b81019080803590602001909291905050506111ff565b604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182151515158152602001935050505060405180910390f35b3480156103b957600080fd5b50610431600480360360208110156103d057600080fd5b81019080803590602001906401000000008111156103ed57600080fd5b8201836020820111156103ff57600080fd5b8035906020019184600183028401116401000000008311171561042157600080fd5b9091929391929390505050611276565b604051808215151515815260200191505060405180910390f35b6104776004803603602081101561046157600080fd5b8101908080359060200190929190505050611553565b604051808215151515815260200191505060405180910390f35b34801561049d57600080fd5b506104ca600480360360208110156104b457600080fd5b810190808035906020019092919050505061185c565b604051808215151515815260200191505060405180910390f35b3480156104f057600080fd5b506105726004803603604081101561050757600080fd5b81019080803590602001909291908035906020019064010000000081111561052e57600080fd5b82018360208201111561054057600080fd5b8035906020019184600183028401116401000000008311171561056257600080fd5b9091929391929390505050611acc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156105c057600080fd5b506105ed600480360360208110156105d757600080fd5b8101908080359060200190929190505050611bb0565b604051808215151515815260200191505060405180910390f35b34801561061357600080fd5b506106566004803603602081101561062a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611c73565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561069657808201518184015260208101905061067b565b50505050905090810190601f1680156106c35780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156106dd57600080fd5b506106e6611d23565b005b3480156106f457600080fd5b506107416004803603604081101561070b57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611e5c565b604051808215151515815260200191505060405180910390f35b34801561076757600080fd5b5061077061208a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156107be57600080fd5b506107c76120b3565b604051808215151515815260200191505060405180910390f35b3480156107ed57600080fd5b5061086f6004803603604081101561080457600080fd5b81019080803590602001909291908035906020019064010000000081111561082b57600080fd5b82018360208201111561083d57600080fd5b8035906020019184600183028401116401000000008311171561085f57600080fd5b909192939192939050505061210a565b6040518082815260200191505060405180910390f35b34801561089157600080fd5b506108be600480360360208110156108a857600080fd5b81019080803590602001909291905050506121ea565b604051808215151515815260200191505060405180910390f35b3480156108e457600080fd5b506108ed6122f3565b604051808215151515815260200191505060405180910390f35b34801561091357600080fd5b506109ab6004803603604081101561092a57600080fd5b810190808035906020019064010000000081111561094757600080fd5b82018360208201111561095957600080fd5b8035906020019184600183028401116401000000008311171561097b57600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612422565b604051808215151515815260200191505060405180910390f35b3480156109d157600080fd5b506109fe600480360360208110156109e857600080fd5b8101908080359060200190929190505050612982565b604051808215151515815260200191505060405180910390f35b348015610a2457600080fd5b50610aa660048036036040811015610a3b57600080fd5b810190808035906020019092919080359060200190640100000000811115610a6257600080fd5b820183602082011115610a7457600080fd5b80359060200191846001830284011164010000000083111715610a9657600080fd5b9091929391929390505050612a8b565b6040518082815260200191505060405180910390f35b348015610ac857600080fd5b50610ad1612b6f565b6040518082815260200191505060405180910390f35b348015610af357600080fd5b50610b2060048036036020811015610b0a57600080fd5b8101908080359060200190929190505050612b75565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b348015610b6e57600080fd5b50610b9b60048036036020811015610b8557600080fd5b8101908080359060200190929190505050612c4e565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b348015610be957600080fd5b50610c2c60048036036020811015610c0057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612d27565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610c6c578082015181840152602081019050610c51565b50505050905090810190601f168015610c995780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b348015610cb357600080fd5b50610d3f60048036036060811015610cca57600080fd5b810190808035906020019092919080359060200190640100000000811115610cf157600080fd5b820183602082011115610d0357600080fd5b80359060200191846001830284011164010000000083111715610d2557600080fd5b909192939192939080359060200190929190505050612e08565b604051808215151515815260200191505060405180910390f35b348015610d6557600080fd5b50610dfd60048036036040811015610d7c57600080fd5b8101908080359060200190640100000000811115610d9957600080fd5b820183602082011115610dab57600080fd5b80359060200191846001830284011164010000000083111715610dcd57600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613063565b604051808215151515815260200191505060405180910390f35b348015610e2357600080fd5b50610e2c613297565b005b348015610e3a57600080fd5b50610e7d60048036036020811015610e5157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613648565b005b348015610e8b57600080fd5b50610f2d60048036036060811015610ea257600080fd5b810190808035906020019092919080359060200190640100000000811115610ec957600080fd5b820183602082011115610edb57600080fd5b80359060200191846001830284011164010000000083111715610efd57600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506136ce565b604051808215151515815260200191505060405180910390f35b600080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002080546001816001161561010002031660029004905014159050919050565b6000846001600082815260200190815260200160002060010160149054906101000a900460ff1615611041576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b853373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611119576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b83600160008981526020019081526020016000206002018787604051808383808284378083019250505092505050908152602001604051809103902081905550867fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea878789896040518080602001806020018381038352878782818152602001925080828437600081840152601f19601f8201169050808301925050508381038252858582818152602001925080828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a2600192505050949350505050565b60016020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060010160149054906101000a900460ff16905083565b600082828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050600160008280519060200120815260200190815260200160002060010160149054906101000a900460ff161561135b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b83838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050503373ffffffffffffffffffffffffffffffffffffffff16600160008380519060200120815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461147e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f4572726f723a204f6e6c79207768656e2070726f706f7365640000000000000081525060200191505060405180910390fd5b8484600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002091906114cc929190613aa0565b503373ffffffffffffffffffffffffffffffffffffffff167f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c91920868660405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a260019250505092915050565b6000816001600082815260200190815260200160002060010160149054906101000a900460ff16156115ed576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b82600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146116c6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f4572726f723a204f6e6c79207768656e20756e7265736572766564000000000081525060200191505060405180910390fd5b60035434101561173e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f4572726f723a206f6e6c79207768656e2066656520706169640000000000000081525060200191505060405180910390fd5b6117466120b3565b6117b8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b336001600086815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff16847f4963513eca575aba66fdcd25f267aae85958fe6fb97e75fa25d783f1a091a22160405160405180910390a3600192505050919050565b6000816001600082815260200190815260200160002060010160149054906101000a900460ff16156118f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b823373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146119ce576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b600260006001600087815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000611a4f9190613b20565b600180600086815260200190815260200160002060010160146101000a81548160ff0219169083151502179055503373ffffffffffffffffffffffffffffffffffffffff16847fef1961b4d2909dc23643b309bfe5c3e5646842d98c3a58517037ef3871185af360405160405180910390a3600192505050919050565b6000836001600082815260200190815260200160002060010160149054906101000a900460ff1615611b66576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b600160008681526020019081526020016000206002018484604051808383808284378083019250505092505050908152602001604051809103902054600190049150509392505050565b6000611bba6120b3565b611c2c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b816003819055507f6bbc57480a46553fa4d156ce702beef5f3ad66303b0ed1a5d4cb44966c6584c3826040518082815260200191505060405180910390a160019050919050565b60026020528060005260406000206000915090508054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611d1b5780601f10611cf057610100808354040283529160200191611d1b565b820191906000526020600020905b815481529060010190602001808311611cfe57829003601f168201915b505050505081565b611d2b6120b3565b611d9d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6000826001600082815260200190815260200160002060010160149054906101000a900460ff1615611ef6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b833373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611fce576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b836001600087815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16867f7b97c62130aa09acbbcbf7482630e756592496f1759eaf702f469cf64dfb779460405160405180910390a460019250505092915050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6000836001600082815260200190815260200160002060010160149054906101000a900460ff16156121a4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b6001600086815260200190815260200160002060020184846040518083838082843780830192505050925050509081526020016040518091039020549150509392505050565b6000816001600082815260200190815260200160002060010160149054906101000a900460ff1615612284576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166001600085815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415915050919050565b60006122fd6120b3565b61236f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b7fdef931299fe61d176f949118058530c1f3f539dcb6950b4e372c9b835c33ca073073ffffffffffffffffffffffffffffffffffffffff16316040518082815260200191505060405180910390a13373ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff16319081150290604051600060405180830381858888f1935050505015801561241a573d6000803e3d6000fd5b506001905090565b600083838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050600160008280519060200120815260200190815260200160002060010160149054906101000a900460ff1615612507576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b848460405180838380828437808301925050509250505060405180910390203373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146125fd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b6000868660405180838380828437808301925050509250505060405180910390209050600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415801561276e575080600260006001600085815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020604051808280546001816001161561010002031660029004801561275f5780601f1061273d57610100808354040283529182019161275f565b820191906000526020600020905b81548152906001019060200180831161274b575b50509150506040518091039020145b156128a557600260006001600084815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006127f49190613b20565b6001600082815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd888860405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a25b846001600083815260200190815260200160002060010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508473ffffffffffffffffffffffffffffffffffffffff167f728435a0031f6a04538fcdd24922a7e06bc7bc945db03e83d22122d1bc5f28df888860405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a2600193505050509392505050565b6000816001600082815260200190815260200160002060010160149054906101000a900460ff1615612a1c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166001600085815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415915050919050565b6000836001600082815260200190815260200160002060010160149054906101000a900460ff1615612b25576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b600160008681526020019081526020016000206002018484604051808383808284378083019250505092505050908152602001604051809103902054600190049150509392505050565b60035481565b6000816001600082815260200190815260200160002060010160149054906101000a900460ff1615612c0f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b6001600084815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16915050919050565b6000816001600082815260200190815260200160002060010160149054906101000a900460ff1615612ce8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b6001600084815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16915050919050565b6060600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015612dfc5780601f10612dd157610100808354040283529160200191612dfc565b820191906000526020600020905b815481529060010190602001808311612ddf57829003601f168201915b50505050509050919050565b6000846001600082815260200190815260200160002060010160149054906101000a900460ff1615612ea2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b853373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612f7a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b83600102600160008981526020019081526020016000206002018787604051808383808284378083019250505092505050908152602001604051809103902081905550867fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea878789896040518080602001806020018381038352878782818152602001925080828437600081840152601f19601f8201169050808301925050508381038252858582818152602001925080828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a2600192505050949350505050565b600083838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050600160008280519060200120815260200190815260200160002060010160149054906101000a900460ff1615613148576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b6131506120b3565b6131c2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b8484600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209190613210929190613aa0565b508273ffffffffffffffffffffffffffffffffffffffff167f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c91920868660405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a260019150509392505050565b600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561336a5780601f1061333f5761010080835404028352916020019161336a565b820191906000526020600020905b81548152906001019060200180831161334d57829003601f168201915b5050505050600160008280519060200120815260200190815260200160002060010160149054906101000a900460ff161561340d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff167f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020604051808060200182810382528381815460018160011615610100020316600290048152602001915080546001816001161561010002031660029004801561350d5780601f106134e25761010080835404028352916020019161350d565b820191906000526020600020905b8154815290600101906020018083116134f057829003601f168201915b50509250505060405180910390a260016000600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156135b95780601f106135975761010080835404028352918201916135b9565b820191906000526020600020905b8154815290600101906020018083116135a5575b50509150506040518091039020815260200190815260200160002060010160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006136459190613b20565b50565b6136506120b3565b6136c2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b6136cb8161393f565b50565b6000846001600082815260200190815260200160002060010160149054906101000a900460ff1615613768576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b853373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614613840576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff16600102600160008981526020019081526020016000206002018787604051808383808284378083019250505092505050908152602001604051809103902081905550867fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea878789896040518080602001806020018381038352878782818152602001925080828437600081840152601f19601f8201169050808301925050508381038252858582818152602001925080828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a2600192505050949350505050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156139e2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10613ae157803560ff1916838001178555613b0f565b82800160010185558215613b0f579182015b82811115613b0e578235825591602001919060010190613af3565b5b509050613b1c9190613b68565b5090565b50805460018160011615610100020316600290046000825580601f10613b465750613b65565b601f016020900490600052602060002090810190613b649190613b68565b5b50565b613b8a91905b80821115613b86576000816000905550600101613b6e565b5090565b9056fea165627a7a723058201b0da92162b975c882f9c56423cbea6ec9afb46d8dccc92abe125e91c84d52de00290000000000000000000000001204700000000000000000000000000000000005" + }, + "0x1204700000000000000000000000000000000007": { + "constructor": "0x608060405234801561001057600080fd5b50604051604080610a5f833981018060405261002f919081019061028d565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a381600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506101448161014b640100000000026401000000009004565b505061036c565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156101bb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101b290610309565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000610285825161033a565b905092915050565b600080604083850312156102a057600080fd5b60006102ae85828601610279565b92505060206102bf85828601610279565b9150509250929050565b60006102d6601f83610329565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b60006020820190508181036000830152610322816102c9565b9050919050565b600082825260208201905092915050565b60006103458261034c565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6106e48061037b6000396000f3fe608060405234801561001057600080fd5b506004361061007f576000357c0100000000000000000000000000000000000000000000000000000000900480636ddc4a0714610084578063715018a6146100a25780638da5cb5b146100ac5780638f32d59b146100ca578063f2fde38b146100e8578063fe64d6ff14610104575b600080fd5b61008c610120565b60405161009991906105b3565b60405180910390f35b6100aa610146565b005b6100b461024c565b6040516100c191906105b3565b60405180910390f35b6100d2610275565b6040516100df91906105ce565b60405180910390f35b61010260048036036100fd91908101906104ec565b6102cc565b005b61011e600480360361011991908101906104ec565b61031f565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b61014e610275565b61018d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161018490610609565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6102d4610275565b610313576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161030a90610609565b60405180910390fd5b61031c816103aa565b50565b610327610275565b610366576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161035d90610609565b60405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561041a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610411906105e9565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60006104e48235610678565b905092915050565b6000602082840312156104fe57600080fd5b600061050c848285016104d8565b91505092915050565b61051e8161063a565b82525050565b61052d8161064c565b82525050565b6000610540601f83610629565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b6000610580601383610629565b91507f53656e646572206973206e6f74206f776e6572000000000000000000000000006000830152602082019050919050565b60006020820190506105c86000830184610515565b92915050565b60006020820190506105e36000830184610524565b92915050565b6000602082019050818103600083015261060281610533565b9050919050565b6000602082019050818103600083015261062281610573565b9050919050565b600082825260208201905092915050565b600061064582610658565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006106838261068a565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff8216905091905056fea265627a7a723058207424d262effee3c4b2cfbe03927ef8931223cda85f89bf647b88b2f7e64cc5516c6578706572696d656e74616cf5003700000000000000000000000012047000000000000000000000000000000000090000000000000000000000001204700000000000000000000000000000000005" + }, + "0x1204700000000000000000000000000000000008": { + "constructor": "0x60806040523480156200001157600080fd5b5060405160408062002b838339810180604052620000339190810190620002af565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a381600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506200014a8162000152640100000000026401000000009004565b5050620003ad565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415620001c5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001bc9062000332565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600062000291825162000365565b905092915050565b6000620002a7825162000379565b905092915050565b60008060408385031215620002c357600080fd5b6000620002d38582860162000299565b9250506020620002e68582860162000283565b9150509250929050565b6000620002ff601f8362000354565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b600060208201905081810360008301526200034d81620002f0565b9050919050565b600082825260208201905092915050565b600062000372826200038d565b9050919050565b6000620003868262000365565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6127c680620003bd6000396000f3fe608060405234801561001057600080fd5b506004361061011d576000357c010000000000000000000000000000000000000000000000000000000090048063954029c9116100b4578063eab7ad9211610083578063eab7ad92146102e4578063ee7ee0fd14610300578063f2fde38b14610330578063fbd74f841461034c5761011d565b8063954029c91461024c578063a999809f14610268578063b71da0d114610298578063da5393d5146102c85761011d565b806370a05b18116100f057806370a05b18146101d6578063715018a6146102065780638da5cb5b146102105780638f32d59b1461022e5761011d565b80631bab58f5146101225780632c5653e21461015257806332d91c0e146101705780636a564261146101a0575b600080fd5b61013c60048036036101379190810190611f97565b61037c565b604051610149919061260c565b60405180910390f35b61015a6107b5565b604051610167919061254f565b60405180910390f35b61018a60048036036101859190810190611f97565b6107db565b604051610197919061256a565b60405180910390f35b6101ba60048036036101b59190810190611f97565b6109e8565b6040516101cd97969594939291906124c4565b60405180910390f35b6101f060048036036101eb9190810190611f97565b610c97565b6040516101fd91906124a2565b60405180910390f35b61020e610ea4565b005b610218610faa565b604051610225919061246c565b60405180910390f35b610236610fd3565b6040516102439190612487565b60405180910390f35b61026660048036036102619190810190611f97565b61102a565b005b610282600480360361027d9190810190611f97565b61119d565b60405161028f919061256a565b60405180910390f35b6102b260048036036102ad9190810190611f97565b6113aa565b6040516102bf9190612487565b60405180910390f35b6102e260048036036102dd9190810190611f97565b61143a565b005b6102fe60048036036102f99190810190611fe9565b611535565b005b61031a60048036036103159190810190611f97565b611853565b6040516103279190612487565b60405180910390f35b61034a60048036036103459190810190611f97565b6119d5565b005b61036660048036036103619190810190611f97565b611a28565b60405161037391906124a2565b60405180910390f35b610384611d63565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801561040857600080fd5b505afa15801561041c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506104409190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104ad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104a49061258c565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518060e0016040529081600082018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105905780601f1061056557610100808354040283529160200191610590565b820191906000526020600020905b81548152906001019060200180831161057357829003601f168201915b50505050508152602001600182018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156106325780601f1061060757610100808354040283529160200191610632565b820191906000526020600020905b81548152906001019060200180831161061557829003601f168201915b50505050508152602001600282018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156106d45780601f106106a9576101008083540402835291602001916106d4565b820191906000526020600020905b8154815290600101906020018083116106b757829003601f168201915b50505050508152602001600382018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156107765780601f1061074b57610100808354040283529160200191610776565b820191906000526020600020905b81548152906001019060200180831161075957829003601f168201915b505050505081526020016004820160009054906101000a900460ff16151515158152602001600582015481526020016006820154815250509050919050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801561086157600080fd5b505afa158015610875573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506108999190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610906576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108fd9061258c565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206003018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156109dc5780601f106109b1576101008083540402835291602001916109dc565b820191906000526020600020905b8154815290600101906020018083116109bf57829003601f168201915b50505050509050919050565b6001602052806000526040600020600091509050806000018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a945780601f10610a6957610100808354040283529160200191610a94565b820191906000526020600020905b815481529060010190602001808311610a7757829003601f168201915b505050505090806001018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b325780601f10610b0757610100808354040283529160200191610b32565b820191906000526020600020905b815481529060010190602001808311610b1557829003601f168201915b505050505090806002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610bd05780601f10610ba557610100808354040283529160200191610bd0565b820191906000526020600020905b815481529060010190602001808311610bb357829003601f168201915b505050505090806003018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610c6e5780601f10610c4357610100808354040283529160200191610c6e565b820191906000526020600020905b815481529060010190602001808311610c5157829003601f168201915b5050505050908060040160009054906101000a900460ff16908060050154908060060154905087565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b158015610d1d57600080fd5b505afa158015610d31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610d559190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610dc2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610db99061258c565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610e985780601f10610e6d57610100808354040283529160200191610e98565b820191906000526020600020905b815481529060010190602001808311610e7b57829003601f168201915b50505050509050919050565b610eac610fd3565b610eeb576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ee2906125ec565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b1580156110ae57600080fd5b505afa1580156110c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110e69190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611153576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161114a9061258c565b60405180910390fd5b43600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206006018190555050565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801561122357600080fd5b505afa158015611237573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061125b9190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146112c8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112bf9061258c565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561139e5780601f106113735761010080835404028352916020019161139e565b820191906000526020600020905b81548152906001019060200180831161138157829003601f168201915b50505050509050919050565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060060154600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060050154109050919050565b611442610fd3565b611481576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611478906125ec565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156114f1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114e8906125ac565b60405180910390fd5b80600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b1580156115b957600080fd5b505afa1580156115cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115f19190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461165e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116559061258c565b60405180910390fd5b8888600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000191906116af929190611da2565b508686600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001019190611701929190611e22565b508484600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206002019190611753929190611da2565b508282600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060030191906117a5929190611e22565b5080600160008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060040160006101000a81548160ff02191690831515021790555043600160008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206005018190555050505050505050505050565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b1580156118d957600080fd5b505afa1580156118ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506119119190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461197e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119759061258c565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060040160009054906101000a900460ff169050919050565b6119dd610fd3565b611a1c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a13906125ec565b60405180910390fd5b611a2581611c35565b50565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b158015611aae57600080fd5b505afa158015611ac2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ae69190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611b53576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b4a9061258c565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611c295780601f10611bfe57610100808354040283529160200191611c29565b820191906000526020600020905b815481529060010190602001808311611c0c57829003601f168201915b50505050509050919050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611ca5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c9c906125cc565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6040518060e001604052806060815260200160608152602001606081526020016060815260200160001515815260200160008152602001600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611de357803560ff1916838001178555611e11565b82800160010185558215611e11579182015b82811115611e10578235825591602001919060010190611df5565b5b509050611e1e9190611ea2565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611e6357803560ff1916838001178555611e91565b82800160010185558215611e91579182015b82811115611e90578235825591602001919060010190611e75565b5b509050611e9e9190611ea2565b5090565b611ec491905b80821115611ec0576000816000905550600101611ea8565b5090565b90565b6000611ed382356126e6565b905092915050565b6000611ee782516126e6565b905092915050565b6000611efb82356126f8565b905092915050565b60008083601f840112611f1557600080fd5b8235905067ffffffffffffffff811115611f2e57600080fd5b602083019150836001820283011115611f4657600080fd5b9250929050565b60008083601f840112611f5f57600080fd5b8235905067ffffffffffffffff811115611f7857600080fd5b602083019150836001820283011115611f9057600080fd5b9250929050565b600060208284031215611fa957600080fd5b6000611fb784828501611ec7565b91505092915050565b600060208284031215611fd257600080fd5b6000611fe084828501611edb565b91505092915050565b60008060008060008060008060008060c08b8d03121561200857600080fd5b60006120168d828e01611ec7565b9a505060208b013567ffffffffffffffff81111561203357600080fd5b61203f8d828e01611f03565b995099505060408b013567ffffffffffffffff81111561205e57600080fd5b61206a8d828e01611f4d565b975097505060608b013567ffffffffffffffff81111561208957600080fd5b6120958d828e01611f03565b955095505060808b013567ffffffffffffffff8111156120b457600080fd5b6120c08d828e01611f4d565b935093505060a06120d38d828e01611eef565b9150509295989b9194979a5092959850565b6120ee8161269e565b82525050565b6120fd816126b0565b82525050565b61210c816126b0565b82525050565b600061211d82612639565b612127818561266b565b9350612137818560208601612748565b6121408161277b565b840191505092915050565b60006121568261262e565b612160818561265a565b9350612170818560208601612748565b6121798161277b565b840191505092915050565b600061218f8261262e565b612199818561266b565b93506121a9818560208601612748565b6121b28161277b565b840191505092915050565b6121c681612724565b82525050565b60006121d78261264f565b6121e1818561268d565b93506121f1818560208601612748565b6121fa8161277b565b840191505092915050565b600061221082612644565b61221a818561267c565b935061222a818560208601612748565b6122338161277b565b840191505092915050565b600061224982612644565b612253818561268d565b9350612263818560208601612748565b61226c8161277b565b840191505092915050565b600061228460138361268d565b91507f4572726f723a206f6e6c794c6f676963204462000000000000000000000000006000830152602082019050919050565b60006122c460298361268d565b91507f4572726f723a206e65774c6f6f6b5570206973206e6f7420616c6c6f7765642060008301527f746f2062652030783000000000000000000000000000000000000000000000006020830152604082019050919050565b600061232a601f8361268d565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b600061236a60138361268d565b91507f53656e646572206973206e6f74206f776e6572000000000000000000000000006000830152602082019050919050565b600060e08301600083015184820360008601526123ba828261214b565b915050602083015184820360208601526123d48282612205565b915050604083015184820360408601526123ee828261214b565b915050606083015184820360608601526124088282612205565b915050608083015161241d60808601826120f4565b5060a083015161243060a086018261244e565b5060c083015161244360c086018261244e565b508091505092915050565b612457816126dc565b82525050565b612466816126dc565b82525050565b600060208201905061248160008301846120e5565b92915050565b600060208201905061249c6000830184612103565b92915050565b600060208201905081810360008301526124bc8184612112565b905092915050565b600060e08201905081810360008301526124de818a612184565b905081810360208301526124f2818961223e565b905081810360408301526125068188612184565b9050818103606083015261251a818761223e565b90506125296080830186612103565b61253660a083018561245d565b61254360c083018461245d565b98975050505050505050565b600060208201905061256460008301846121bd565b92915050565b6000602082019050818103600083015261258481846121cc565b905092915050565b600060208201905081810360008301526125a581612277565b9050919050565b600060208201905081810360008301526125c5816122b7565b9050919050565b600060208201905081810360008301526125e58161231d565b9050919050565b600060208201905081810360008301526126058161235d565b9050919050565b60006020820190508181036000830152612626818461239d565b905092915050565b600081519050919050565b600081519050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b60006126a9826126bc565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006126f182612704565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061272f82612736565b9050919050565b6000612741826126bc565b9050919050565b60005b8381101561276657808201518184015260208101905061274b565b83811115612775576000848401525b50505050565b6000601f19601f830116905091905056fea265627a7a723058207e60ec05ac38073f6d98c9cff2cce094d00db0238d6d4b0c183253539a43fe2b6c6578706572696d656e74616cf5003700000000000000000000000012047000000000000000000000000000000000070000000000000000000000001204700000000000000000000000000000000005" + }, + "0x1204700000000000000000000000000000000009": { + "constructor": "0x60806040523480156200001157600080fd5b50604051604080620021cc8339810180604052620000339190810190620002af565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a381600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506200014a8162000152640100000000026401000000009004565b5050620003ad565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415620001c5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001bc9062000332565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600062000291825162000365565b905092915050565b6000620002a7825162000379565b905092915050565b60008060408385031215620002c357600080fd5b6000620002d38582860162000299565b9250506020620002e68582860162000283565b9150509250929050565b6000620002ff601f8362000354565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b600060208201905081810360008301526200034d81620002f0565b9050919050565b600082825260208201905092915050565b600062000372826200038d565b9050919050565b6000620003868262000365565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b611e0f80620003bd6000396000f3fe608060405234801561001057600080fd5b50600436106100b0576000357c0100000000000000000000000000000000000000000000000000000000900480638da5cb5b116100835780638da5cb5b146101155780638f32d59b14610133578063b71da0d114610151578063ec26261114610181578063f2fde38b1461019f576100b0565b806312127ed7146100b55780633f67c333146100d1578063715018a6146100db57806389c3ce1e146100e5575b600080fd5b6100cf60048036036100ca9190810190611450565b6101bb565b005b6100d96109dd565b005b6100e3610b98565b005b6100ff60048036036100fa9190810190611427565b610c9e565b60405161010c9190611b14565b60405180910390f35b61011d610d85565b60405161012a91906119ab565b60405180910390f35b61013b610dae565b6040516101489190611a5e565b60405180910390f35b61016b60048036036101669190810190611427565b610e05565b6040516101789190611a5e565b60405180910390f35b610189610ed5565b6040516101969190611a79565b60405180910390f35b6101b960048036036101b49190810190611427565b610efb565b005b6101c3610dae565b610202576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101f990611ab4565b60405180910390fd5b6002856040516102129190611994565b602060405180830381855afa15801561022f573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052506102529190810190611562565b6002600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fbd74f84896040518263ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004016102cb91906119ab565b60006040518083038186803b1580156102e357600080fd5b505afa1580156102f7573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250610320919081019061158b565b60405161032d9190611994565b602060405180830381855afa15801561034a573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525061036d9190810190611562565b1480156104e257506002846040516103859190611994565b602060405180830381855afa1580156103a2573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052506103c59190810190611562565b6002600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a999809f896040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040161043e91906119ab565b60006040518083038186803b15801561045657600080fd5b505afa15801561046a573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525061049391908101906115cc565b6040516104a09190611994565b602060405180830381855afa1580156104bd573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052506104e09190810190611562565b145b801561065657506002836040516104f99190611994565b602060405180830381855afa158015610516573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052506105399190810190611562565b6002600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a05b18896040518263ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004016105b291906119ab565b60006040518083038186803b1580156105ca57600080fd5b505afa1580156105de573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250610607919081019061158b565b6040516106149190611994565b602060405180830381855afa158015610631573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052506106549190810190611562565b145b80156107ca575060028260405161066d9190611994565b602060405180830381855afa15801561068a573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052506106ad9190810190611562565b6002600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166332d91c0e896040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040161072691906119ab565b60006040518083038186803b15801561073e57600080fd5b505afa158015610752573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525061077b91908101906115cc565b6040516107889190611994565b602060405180830381855afa1580156107a5573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052506107c89190810190611562565b145b801561089f5750801515600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ee7ee0fd886040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040161084b91906119ab565b60206040518083038186803b15801561086357600080fd5b505afa158015610877573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061089b9190810190611539565b1515145b156108df576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108d690611ad4565b60405180910390fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663eab7ad928787878787876040518763ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401610960969594939291906119e1565b600060405180830381600087803b15801561097a57600080fd5b505af115801561098e573d6000803e3d6000fd5b505050508573ffffffffffffffffffffffffffffffffffffffff167fc9e49a024d50440c73d2d12d0ae05064094dca76b46dc95e99ea6848eb8f27e960405160405180910390a2505050505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fbd74f84336040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401610a5691906119c6565b60006040518083038186803b158015610a6e57600080fd5b505afa158015610a82573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250610aab919081019061158b565b511415610aed576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ae490611af4565b60405180910390fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663954029c9336040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401610b6491906119c6565b600060405180830381600087803b158015610b7e57600080fd5b505af1158015610b92573d6000803e3d6000fd5b50505050565b610ba0610dae565b610bdf576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bd690611ab4565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b610ca661107c565b610cae61107c565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631bab58f5846040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401610d2591906119ab565b60006040518083038186803b158015610d3d57600080fd5b505afa158015610d51573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250610d7a919081019061160d565b905080915050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b71da0d1836040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401610e7e91906119ab565b60206040518083038186803b158015610e9657600080fd5b505afa158015610eaa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ece9190810190611539565b9050919050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610f03610dae565b610f42576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f3990611ab4565b60405180910390fd5b610f4b81610f4e565b50565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610fbe576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610fb590611a94565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6040518060e001604052806060815260200160608152602001606081526020016060815260200160001515815260200160008152602001600081525090565b60006110c78235611cd6565b905092915050565b60006110db8235611ce8565b905092915050565b60006110ef8251611ce8565b905092915050565b60006111038251611cf4565b905092915050565b600082601f83011261111c57600080fd5b815161112f61112a82611b63565b611b36565b9150808252602083016020830185838301111561114b57600080fd5b611156838284611d91565b50505092915050565b600082601f83011261117057600080fd5b813561118361117e82611b8f565b611b36565b9150808252602083016020830185838301111561119f57600080fd5b6111aa838284611d82565b50505092915050565b600082601f8301126111c457600080fd5b81516111d76111d282611b8f565b611b36565b915080825260208301602083018583830111156111f357600080fd5b6111fe838284611d91565b50505092915050565b600082601f83011261121857600080fd5b815161122b61122682611bbb565b611b36565b9150808252602083016020830185838301111561124757600080fd5b611252838284611d91565b50505092915050565b600082601f83011261126c57600080fd5b813561127f61127a82611be7565b611b36565b9150808252602083016020830185838301111561129b57600080fd5b6112a6838284611d82565b50505092915050565b600082601f8301126112c057600080fd5b81516112d36112ce82611be7565b611b36565b915080825260208301602083018583830111156112ef57600080fd5b6112fa838284611d91565b50505092915050565b600060e0828403121561131557600080fd5b61131f60e0611b36565b9050600082015167ffffffffffffffff81111561133b57600080fd5b6113478482850161110b565b600083015250602082015167ffffffffffffffff81111561136757600080fd5b61137384828501611207565b602083015250604082015167ffffffffffffffff81111561139357600080fd5b61139f8482850161110b565b604083015250606082015167ffffffffffffffff8111156113bf57600080fd5b6113cb84828501611207565b60608301525060806113df848285016110e3565b60808301525060a06113f384828501611413565b60a08301525060c061140784828501611413565b60c08301525092915050565b600061141f8251611d1e565b905092915050565b60006020828403121561143957600080fd5b6000611447848285016110bb565b91505092915050565b60008060008060008060c0878903121561146957600080fd5b600061147789828a016110bb565b965050602087013567ffffffffffffffff81111561149457600080fd5b6114a089828a0161115f565b955050604087013567ffffffffffffffff8111156114bd57600080fd5b6114c989828a0161125b565b945050606087013567ffffffffffffffff8111156114e657600080fd5b6114f289828a0161115f565b935050608087013567ffffffffffffffff81111561150f57600080fd5b61151b89828a0161125b565b92505060a061152c89828a016110cf565b9150509295509295509295565b60006020828403121561154b57600080fd5b6000611559848285016110e3565b91505092915050565b60006020828403121561157457600080fd5b6000611582848285016110f7565b91505092915050565b60006020828403121561159d57600080fd5b600082015167ffffffffffffffff8111156115b757600080fd5b6115c3848285016111b3565b91505092915050565b6000602082840312156115de57600080fd5b600082015167ffffffffffffffff8111156115f857600080fd5b611604848285016112af565b91505092915050565b60006020828403121561161f57600080fd5b600082015167ffffffffffffffff81111561163957600080fd5b61164584828501611303565b91505092915050565b61165781611d28565b82525050565b61166681611c8e565b82525050565b61167581611ca0565b82525050565b61168481611ca0565b82525050565b600061169582611c1e565b61169f8185611c50565b93506116af818560208601611d91565b6116b881611dc4565b840191505092915050565b60006116ce82611c1e565b6116d88185611c61565b93506116e8818560208601611d91565b80840191505092915050565b60006116ff82611c13565b6117098185611c3f565b9350611719818560208601611d91565b61172281611dc4565b840191505092915050565b61173681611d3a565b82525050565b600061174782611c34565b6117518185611c7d565b9350611761818560208601611d91565b61176a81611dc4565b840191505092915050565b600061178082611c29565b61178a8185611c6c565b935061179a818560208601611d91565b6117a381611dc4565b840191505092915050565b60006117bb601f83611c7d565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b60006117fb601383611c7d565b91507f53656e646572206973206e6f74206f776e6572000000000000000000000000006000830152602082019050919050565b600061183b602583611c7d565b91507f4572726f723a204e6f206368616e67657320696e20746865207061737365642060008301527f53746174650000000000000000000000000000000000000000000000000000006020830152604082019050919050565b60006118a1601f83611c7d565b91507f4572726f723a20596f7520617265206e6f7420612076616c696461746f7221006000830152602082019050919050565b600060e08301600083015184820360008601526118f182826116f4565b9150506020830151848203602086015261190b8282611775565b9150506040830151848203604086015261192582826116f4565b9150506060830151848203606086015261193f8282611775565b9150506080830151611954608086018261166c565b5060a083015161196760a0860182611985565b5060c083015161197a60c0860182611985565b508091505092915050565b61198e81611ccc565b82525050565b60006119a082846116c3565b915081905092915050565b60006020820190506119c0600083018461165d565b92915050565b60006020820190506119db600083018461164e565b92915050565b600060c0820190506119f6600083018961165d565b8181036020830152611a08818861168a565b90508181036040830152611a1c818761173c565b90508181036060830152611a30818661168a565b90508181036080830152611a44818561173c565b9050611a5360a083018461167b565b979650505050505050565b6000602082019050611a73600083018461167b565b92915050565b6000602082019050611a8e600083018461172d565b92915050565b60006020820190508181036000830152611aad816117ae565b9050919050565b60006020820190508181036000830152611acd816117ee565b9050919050565b60006020820190508181036000830152611aed8161182e565b9050919050565b60006020820190508181036000830152611b0d81611894565b9050919050565b60006020820190508181036000830152611b2e81846118d4565b905092915050565b6000604051905081810181811067ffffffffffffffff82111715611b5957600080fd5b8060405250919050565b600067ffffffffffffffff821115611b7a57600080fd5b601f19601f8301169050602081019050919050565b600067ffffffffffffffff821115611ba657600080fd5b601f19601f8301169050602081019050919050565b600067ffffffffffffffff821115611bd257600080fd5b601f19601f8301169050602081019050919050565b600067ffffffffffffffff821115611bfe57600080fd5b601f19601f8301169050602081019050919050565b600081519050919050565b600081519050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600082825260208201905092915050565b600081905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b6000611c9982611cac565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b6000611ce182611cfe565b9050919050565b60008115159050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b6000611d3382611d5e565b9050919050565b6000611d4582611d4c565b9050919050565b6000611d5782611cac565b9050919050565b6000611d6982611d70565b9050919050565b6000611d7b82611cac565b9050919050565b82818337600083830152505050565b60005b83811015611daf578082015181840152602081019050611d94565b83811115611dbe576000848401525b50505050565b6000601f19601f830116905091905056fea265627a7a723058204ed2d756516ea8df8dbc4fbf2bd76bb43e93ab01c8c274fc9169dfd0be6254d06c6578706572696d656e74616cf5003700000000000000000000000012047000000000000000000000000000000000080000000000000000000000001204700000000000000000000000000000000005" + } + }, + "nodes": [ + "enode://59c9250cb805409e84c9cd0038e97d8e5e4605b928663675869ebdfd4c251d80ccad76267a5eb2f4362ddceb5ec671f7595463adfc0a12e9f68dbf233072db41@54.70.158.106:30303", + "enode://e487ebacbdad3418905d2ed7f009fa5dbd17d73880854884acc604c0afc1a60a396aa90cb2741278c555a4e30ffc6ffc1c29e83840aa22009ec92fe53f81ec04@99.81.92.124:30303", + "enode://563f12602a117201b39ebeea108185abb15d9286830c074640c9fccbaaaabcc7fe2c95682cc43f95b95059f6d0dc4c9becbc1b2bd78e0c5ef5fddff07d85ba0e@54.201.62.74:30303", + "enode://5903b3acebdc4a34800f6923e5f3aec3ca7e5d1285bec4adb9f20ebb0f87a3bebdd748b1849ca1108a9f1e37ff9ced0b475292b8effc29e95d49ec438f244b02@3.121.165.10:30303", + "enode://8f8e35a6dcacfee946f46447b4703c84f4e485e478143997f86b1834e1b0bb78dab363d700dff3147442b9d3e2a1c521f79340c436eb7245a97c7fe385b89a5d@54.93.159.98:30303", + "enode://bd228aa03cf4a88491c81c5f3ab4a1437df3b463081cc93943c4d3ab37f1e4f8081c6995eca076f717d4fdf9a277c750bd0289477ac151f1e2b024747dcd1747@52.31.129.130:30303" + ] +} diff --git a/ethcore/res/ethereum/xdai.json b/ethcore/res/ethereum/xdai.json new file mode 100644 index 00000000000..cecc79171a8 --- /dev/null +++ b/ethcore/res/ethereum/xdai.json @@ -0,0 +1,199 @@ +{ + "name": "xDai Chain", + "dataDir": "xdai", + "engine": { + "authorityRound": { + "params": { + "stepDuration": 5, + "blockReward": "0x0", + "maximumUncleCountTransition": 0, + "maximumUncleCount": 0, + "validators": { + "multi": { + "0": { + "list": ["0xcace5b3c29211740e595850e80478416ee77ca21"] + }, + "1300": { + "safeContract": "0x22e1229a2c5b95a60983b5577f745a603284f535" + } + } + }, + "blockRewardContractAddress": "0x867305d19606aadba405ce534e303d0e225f9556", + "blockRewardContractTransition": 1310 + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x400", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID": "100", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": 1604400, + "eip1014Transition": 1604400, + "eip1052Transition": 1604400, + "eip1283Transition": 1604400, + "eip1283DisableTransition": 2508800, + "eip1283ReenableTransition": 7298030, + "eip1344Transition": 7298030, + "eip1706Transition": 7298030, + "eip1884Transition": 7298030, + "eip2028Transition": 7298030, + "registrar": "0x1ec97dc137f5168af053c24460a1200502e1a9d2" + }, + "genesis": { + "seal": { + "authorityRound": { + "step": "0x0", + "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "gasLimit": "0x989680" + }, + "accounts": { + "0x0000000000000000000000000000000000000005": { + "builtin": { + "name": "modexp", + "pricing": { + "0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0x0000000000000000000000000000000000000006": { + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "7298030": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0x0000000000000000000000000000000000000007": { + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "7298030": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0x0000000000000000000000000000000000000008": { + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "7298030": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, + "0x0000000000000000000000000000000000000009": { + "builtin": { + "name": "blake2_f", + "pricing": { + "7298030": { + "info": "EIP 1108 transition", + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } + } + } + } + }, + "0x0000000000000000000000000000000000000001": { + "balance": "1", + "builtin": { + "name": "ecrecover", + "pricing": { + "0": { + "price": { + "linear": { + "base": 3000, + "word": 0 + } + } + } + } + } + }, + "0x0000000000000000000000000000000000000002": { + "balance": "1", + "builtin": { + "name": "sha256", + "pricing": { + "0": { + "price": { + "linear": { + "base": 60, + "word": 12 + } + } + } + } + } + }, + "0x0000000000000000000000000000000000000003": { + "balance": "1", + "builtin": { + "name": "ripemd160", + "pricing": { + "0": { + "price": { + "linear": { + "base": 600, + "word": 120 + } + } + } + } + } + }, + "0x0000000000000000000000000000000000000004": { + "balance": "1", + "builtin": { + "name": "identity", + "pricing": { + "0": { + "price": { + "linear": { + "base": 15, + "word": 3 + } + } + } + } + } + } + }, + "nodes": [ + "enode://1c19ba0a77dd663b843c33beb9020e7eb41fc34b47b98424dbc427f74692115d74c7c27b6c0aa2b59bb1d8f710650cae1090153d10b6909ca7bdcdfb183b1c59@54.39.190.172:30303", + "enode://c1c3a604950119f82d78189792b73f5a82a239017c77465e3c32fc51c1d758a9a772ffddd58436d465342f2cfa6d4a442a49e526743f4d8354d7c5ce794c3ee5@95.179.222.48:30303" + ] +} + diff --git a/ethcore/res/instant_seal.json b/ethcore/res/instant_seal.json index 3ec998f0e5c..b2199ebc35a 100644 --- a/ethcore/res/instant_seal.json +++ b/ethcore/res/instant_seal.json @@ -24,6 +24,9 @@ "eip211Transition": "0x0", "eip214Transition": "0x0", "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", "wasmActivationTransition": "0x0" }, "genesis": { @@ -43,9 +46,51 @@ "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, "0000000000000000000000000000000000000005": { "balance": "1", "builtin": { "name": "modexp", "activate_at": 0, "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { "balance": "1", "builtin": { "name": "alt_bn128_add", "activate_at": 0, "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0000000000000000000000000000000000000007": { "balance": "1", "builtin": { "name": "alt_bn128_mul", "activate_at": 0, "pricing": { "linear": { "base": 40000, "word": 0 } } } }, - "0000000000000000000000000000000000000008": { "balance": "1", "builtin": { "name": "alt_bn128_pairing", "activate_at": 0, "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } }, + "0000000000000000000000000000000000000006": { + "balance": "1", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "1", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "1", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, "0000000000000000000000000000000000001337": { "balance": "1", "constructor": "0x606060405233600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550670de0b6b3a764000060035534610000575b612904806100666000396000f3006060604052361561013c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306b2ff471461014157806313af40351461018c57806319362a28146101bf5780633f3935d114610248578063432ced04146102b75780634f39ca59146102eb5780636795dbcd1461032457806369fe0e2d146103c857806379ce9fac146103fd5780638da5cb5b1461045557806390b97fc1146104a457806392698814146105245780639890220b1461055d578063ac4e73f914610584578063ac72c12014610612578063c3a358251461064b578063ddca3f43146106c3578063deb931a2146106e6578063df57b74214610747578063e30bd740146107a8578063eadf976014610862578063ef5454d6146108e7578063f25eb5c114610975578063f6d339e414610984575b610000565b3461000057610172600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610a1f565b604051808215151515815260200191505060405180910390f35b34610000576101bd600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610a81565b005b346100005761022e60048080356000191690602001909190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803560001916906020019091905050610ba2565b604051808215151515815260200191505060405180910390f35b346100005761029d600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610dc9565b604051808215151515815260200191505060405180910390f35b6102d1600480803560001916906020019091905050611035565b604051808215151515815260200191505060405180910390f35b346100005761030a60048080356000191690602001909190505061115f565b604051808215151515815260200191505060405180910390f35b346100005761038660048080356000191690602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611378565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34610000576103e3600480803590602001909190505061140d565b604051808215151515815260200191505060405180910390f35b346100005761043b60048080356000191690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506114b4565b604051808215151515815260200191505060405180910390f35b34610000576104626115fb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b346100005761050660048080356000191690602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611621565b60405180826000191660001916815260200191505060405180910390f35b34610000576105436004808035600019169060200190919050506116b2565b604051808215151515815260200191505060405180910390f35b346100005761056a611715565b604051808215151515815260200191505060405180910390f35b34610000576105f8600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611824565b604051808215151515815260200191505060405180910390f35b3461000057610631600480803560001916906020019091905050611d8b565b604051808215151515815260200191505060405180910390f35b34610000576106ad60048080356000191690602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611dee565b6040518082815260200191505060405180910390f35b34610000576106d0611e83565b6040518082815260200191505060405180910390f35b3461000057610705600480803560001916906020019091905050611e89565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3461000057610766600480803560001916906020019091905050611ed2565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34610000576107d9600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611f1b565b6040518080602001828103825283818151815260200191508051906020019080838360008314610828575b80518252602083111561082857602082019150602081019050602083039250610804565b505050905090810190601f1680156108545780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34610000576108cd60048080356000191690602001909190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803590602001909190505061200c565b604051808215151515815260200191505060405180910390f35b346100005761095b600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050612236565b604051808215151515815260200191505060405180910390f35b3461000057610982612425565b005b3461000057610a0560048080356000191690602001909190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050612698565b604051808215151515815260200191505060405180910390f35b60006000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290049050141590505b919050565b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610add57610b9f565b8073ffffffffffffffffffffffffffffffffffffffff16600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f70aea8d848e8a90fb7661b227dc522eb6395c3dac71b63cb59edd5c9899b236460405180905060405180910390a380600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b5b50565b6000833373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141515610c1d57610dc1565b82600160008760001916600019168152602001908152602001600020600201856040518082805190602001908083835b60208310610c705780518252602082019150602081019050602083039250610c4d565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208160001916905550836040518082805190602001908083835b60208310610cdf5780518252602082019150602081019050602083039250610cbc565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902085600019167fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea866040518080602001828103825283818151815260200191508051906020019080838360008314610d82575b805182526020831115610d8257602082019150602081019050602083039250610d5e565b505050905090810190601f168015610dae5780820380516001836020036101000a031916815260200191505b509250505060405180910390a3600191505b5b509392505050565b6000813373ffffffffffffffffffffffffffffffffffffffff1660016000836040518082805190602001908083835b60208310610e1b5780518252602082019150602081019050602083039250610df8565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390206000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141515610ea45761102f565b82600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610f2d57805160ff1916838001178555610f5b565b82800160010185558215610f5b579182015b82811115610f5a578251825591602001919060010190610f3f565b5b509050610f8091905b80821115610f7c576000816000905550600101610f64565b5090565b50503373ffffffffffffffffffffffffffffffffffffffff16836040518082805190602001908083835b60208310610fcd5780518252602082019150602081019050602083039250610faa565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c9192060405180905060405180910390a3600191505b5b50919050565b600081600060016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561109b57611159565b6003543410156110aa57611158565b3360016000856000191660001916815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff1683600019167f4963513eca575aba66fdcd25f267aae85958fe6fb97e75fa25d783f1a091a22160405180905060405180910390a3600191505b5b5b50919050565b6000813373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415156111da57611372565b6002600060016000866000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290046000825580601f1061127c57506112b3565b601f0160209004906000526020600020908101906112b291905b808211156112ae576000816000905550600101611296565b5090565b5b5060016000846000191660001916815260200190815260200160002060006000820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550503373ffffffffffffffffffffffffffffffffffffffff1683600019167fef1961b4d2909dc23643b309bfe5c3e5646842d98c3a58517037ef3871185af360405180905060405180910390a3600191505b5b50919050565b6000600160008460001916600019168152602001908152602001600020600201826040518082805190602001908083835b602083106113cc57805182526020820191506020810190506020830392506113a9565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020546001900490505b92915050565b6000600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561146b576114af565b816003819055507f6bbc57480a46553fa4d156ce702beef5f3ad66303b0ed1a5d4cb44966c6584c3826040518082815260200191505060405180910390a1600190505b5b919050565b6000823373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561152f576115f4565b8260016000866000191660001916815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1685600019167f7b97c62130aa09acbbcbf7482630e756592496f1759eaf702f469cf64dfb779460405180905060405180910390a4600191505b5b5092915050565b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600160008460001916600019168152602001908152602001600020600201826040518082805190602001908083835b602083106116755780518252602082019150602081019050602083039250611652565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390205490505b92915050565b6000600060016000846000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141590505b919050565b6000600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561177357611821565b7fdef931299fe61d176f949118058530c1f3f539dcb6950b4e372c9b835c33ca073073ffffffffffffffffffffffffffffffffffffffff16316040518082815260200191505060405180910390a13373ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff16319081150290604051809050600060405180830381858888f19350505050151561181b57610000565b600190505b5b90565b60006000836040518082805190602001908083835b6020831061185c5780518252602082019150602081019050602083039250611839565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390203373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561190157611d83565b846040518082805190602001908083835b602083106119355780518252602082019150602081019050602083039250611912565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390209150600060016000846000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614158015611ab4575081600019166002600060016000866000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518082805460018160011615610100020316600290048015611aa15780601f10611a7f576101008083540402835291820191611aa1565b820191906000526020600020905b815481529060010190602001808311611a8d575b5050915050604051809103902060001916145b15611c79576002600060016000856000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290046000825580601f10611b5b5750611b92565b601f016020900490600052602060002090810190611b9191905b80821115611b8d576000816000905550600101611b75565b5090565b5b5060016000836000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16856040518082805190602001908083835b60208310611c1c5780518252602082019150602081019050602083039250611bf9565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd60405180905060405180910390a35b8360016000846000191660001916815260200190815260200160002060010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508373ffffffffffffffffffffffffffffffffffffffff16856040518082805190602001908083835b60208310611d215780518252602082019150602081019050602083039250611cfe565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f728435a0031f6a04538fcdd24922a7e06bc7bc945db03e83d22122d1bc5f28df60405180905060405180910390a3600192505b5b505092915050565b6000600060016000846000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141590505b919050565b6000600160008460001916600019168152602001908152602001600020600201826040518082805190602001908083835b60208310611e425780518252602082019150602081019050602083039250611e1f565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020546001900490505b92915050565b60035481565b600060016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b919050565b600060016000836000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b919050565b6020604051908101604052806000815250600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611fff5780601f10611fd457610100808354040283529160200191611fff565b820191906000526020600020905b815481529060010190602001808311611fe257829003601f168201915b505050505090505b919050565b6000833373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415156120875761222e565b82600102600160008760001916600019168152602001908152602001600020600201856040518082805190602001908083835b602083106120dd57805182526020820191506020810190506020830392506120ba565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208160001916905550836040518082805190602001908083835b6020831061214c5780518252602082019150602081019050602083039250612129565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902085600019167fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea8660405180806020018281038252838181518152602001915080519060200190808383600083146121ef575b8051825260208311156121ef576020820191506020810190506020830392506121cb565b505050905090810190601f16801561221b5780820380516001836020036101000a031916815260200191505b509250505060405180910390a3600191505b5b509392505050565b6000600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156122945761241f565b82600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061231d57805160ff191683800117855561234b565b8280016001018555821561234b579182015b8281111561234a57825182559160200191906001019061232f565b5b50905061237091905b8082111561236c576000816000905550600101612354565b5090565b50508173ffffffffffffffffffffffffffffffffffffffff16836040518082805190602001908083835b602083106123bd578051825260208201915060208101905060208303925061239a565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c9192060405180905060405180910390a3600190505b5b92915050565b3373ffffffffffffffffffffffffffffffffffffffff16600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156124d65780601f106124b45761010080835404028352918201916124d6565b820191906000526020600020905b8154815290600101906020018083116124c2575b505091505060405180910390207f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd60405180905060405180910390a360016000600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156125b05780601f1061258e5761010080835404028352918201916125b0565b820191906000526020600020905b81548152906001019060200180831161259c575b505091505060405180910390206000191660001916815260200190815260200160002060010160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290046000825580601f1061265d5750612694565b601f01602090049060005260206000209081019061269391905b8082111561268f576000816000905550600101612677565b5090565b5b505b565b6000833373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141515612713576128d0565b8273ffffffffffffffffffffffffffffffffffffffff16600102600160008760001916600019168152602001908152602001600020600201856040518082805190602001908083835b6020831061277f578051825260208201915060208101905060208303925061275c565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208160001916905550836040518082805190602001908083835b602083106127ee57805182526020820191506020810190506020830392506127cb565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902085600019167fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea866040518080602001828103825283818151815260200191508051906020019080838360008314612891575b8051825260208311156128915760208201915060208101905060208303925061286d565b505050905090810190601f1680156128bd5780820380516001836020036101000a031916815260200191505b509250505060405180910390a3600191505b5b5093925050505600a165627a7a7230582066b2da4773a0f1d81efe071c66b51c46868a871661efd18c0f629353ff4c1f9b0029" }, "00a329c0648769a73afac7f9381e08fb43dbea72": { "balance": "1606938044258990275541962092341162602522202993782792835301376" } } diff --git a/ethcore/res/null.json b/ethcore/res/null.json index 7fa41a8def5..82062366d8d 100644 --- a/ethcore/res/null.json +++ b/ethcore/res/null.json @@ -36,9 +36,51 @@ "0000000000000000000000000000000000000003": { "balance": "1", "nonce": "0", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, "0000000000000000000000000000000000000004": { "balance": "1", "nonce": "0", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, "0000000000000000000000000000000000000005": { "balance": "1", "builtin": { "name": "modexp", "activate_at": 0, "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { "balance": "1", "builtin": { "name": "alt_bn128_add", "activate_at": 0, "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0000000000000000000000000000000000000007": { "balance": "1", "builtin": { "name": "alt_bn128_mul", "activate_at": 0, "pricing": { "linear": { "base": 40000, "word": 0 } } } }, - "0000000000000000000000000000000000000008": { "balance": "1", "builtin": { "name": "alt_bn128_pairing", "activate_at": 0, "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } }, + "0000000000000000000000000000000000000006": { + "balance": "1", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "1", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "1", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, "9cce34f7ab185c7aba1b7c8140d620b4bda941d6": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "0" } } } diff --git a/ethcore/res/null_morden_with_finality.json b/ethcore/res/null_morden_with_finality.json new file mode 100644 index 00000000000..ea503ed29a8 --- /dev/null +++ b/ethcore/res/null_morden_with_finality.json @@ -0,0 +1,38 @@ +{ + "name": "Morden", + "engine": { + "null": { + "params": { + "immediateFinalization": true + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x0400", + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x2" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x00006d6f7264656e", + "mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578" + } + }, + "difficulty": "0x20000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x2fefd8" + }, + "accounts": { + "0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" } + } +} diff --git a/ethcore/res/spec_backward_compability.json b/ethcore/res/spec_backward_compability.json new file mode 100644 index 00000000000..e8ce98a6ec4 --- /dev/null +++ b/ethcore/res/spec_backward_compability.json @@ -0,0 +1,155 @@ +{ + "name": "Volta", + "engine": { + "authorityRound": { + "params": { + "stepDuration": "5", + "validators": { + "contract": "0x1204700000000000000000000000000000000000" + }, + "maximumUncleCountTransition": "0", + "maximumUncleCount": "0", + "blockRewardContractAddress": "0x1204700000000000000000000000000000000002", + "blockRewardContractTransition": "0" + } + } + }, + "params": { + "networkID": "0x12047", + "maximumExtraDataSize": "0x20", + "gasLimitBoundDivisor": "0x400", + "minGasLimit": "0x1388", + "maxCodeSize": "0x6000", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "registrar": "0x1204700000000000000000000000000000000006" + }, + "genesis": { + "seal": { + "authorityRound": { + "step": "0x0", + "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "gasLimit": "0x5B8D80" + }, + "accounts": { + "0x0000000000000000000000000000000000000001": { + "balance": "1", + "builtin": { + "name": "ecrecover", + "activate_at": "0", + "pricing": { + "linear": { + "base": 3000, + "word": 0 + } + } + } + }, + "0x0000000000000000000000000000000000000002": { + "balance": "1", + "builtin": { + "name": "sha256", + "activate_at": "0", + "pricing": { + "linear": { + "base": 60, + "word": 12 + } + } + } + }, + "0x0000000000000000000000000000000000000003": { + "balance": "1", + "builtin": { + "name": "ripemd160", + "activate_at": "0", + "pricing": { + "linear": { + "base": 600, + "word": 120 + } + } + } + }, + "0x0000000000000000000000000000000000000004": { + "balance": "1", + "builtin": { + "name": "identity", + "activate_at": "0", + "pricing": { + "linear": { + "base": 15, + "word": 3 + } + } + } + }, + "0x0000000000000000000000000000000000000005": { + "balance": "1", + "builtin": { + "name": "modexp", + "activate_at": "0", + "pricing": { + "modexp": { + "divisor": 20 + } + } + } + }, + "0x0000000000000000000000000000000000000006": { + "balance": "1", + "builtin": { + "name": "alt_bn128_add", + "activate_at": "0", + "pricing": { + "linear": { + "base": 500, + "word": 0 + } + } + } + }, + "0x0000000000000000000000000000000000000007": { + "balance": "1", + "builtin": { + "name": "alt_bn128_mul", + "activate_at": "0", + "pricing": { + "linear": { + "base": 40000, + "word": 0 + } + } + } + }, + "0x0000000000000000000000000000000000000008": { + "balance": "1", + "builtin": { + "name": "alt_bn128_pairing", + "activate_at": "0", + "pricing": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + } + } + }, + "nodes": [ + "enode://59c9250cb805409e84c9cd0038e97d8e5e4605b928663675869ebdfd4c251d80ccad76267a5eb2f4362ddceb5ec671f7595463adfc0a12e9f68dbf233072db41@54.70.158.106:30303", + "enode://e487ebacbdad3418905d2ed7f009fa5dbd17d73880854884acc604c0afc1a60a396aa90cb2741278c555a4e30ffc6ffc1c29e83840aa22009ec92fe53f81ec04@99.81.92.124:30303", + "enode://563f12602a117201b39ebeea108185abb15d9286830c074640c9fccbaaaabcc7fe2c95682cc43f95b95059f6d0dc4c9becbc1b2bd78e0c5ef5fddff07d85ba0e@54.201.62.74:30303", + "enode://5903b3acebdc4a34800f6923e5f3aec3ca7e5d1285bec4adb9f20ebb0f87a3bebdd748b1849ca1108a9f1e37ff9ced0b475292b8effc29e95d49ec438f244b02@3.121.165.10:30303", + "enode://8f8e35a6dcacfee946f46447b4703c84f4e485e478143997f86b1834e1b0bb78dab363d700dff3147442b9d3e2a1c521f79340c436eb7245a97c7fe385b89a5d@54.93.159.98:30303", + "enode://bd228aa03cf4a88491c81c5f3ab4a1437df3b463081cc93943c4d3ab37f1e4f8081c6995eca076f717d4fdf9a277c750bd0289477ac151f1e2b024747dcd1747@52.31.129.130:30303" + ] +} diff --git a/ethcore/service/Cargo.toml b/ethcore/service/Cargo.toml index 7db0ad2770b..68c73b4f905 100644 --- a/ethcore/service/Cargo.toml +++ b/ethcore/service/Cargo.toml @@ -1,4 +1,5 @@ [package] +description = "Parity Ethereum (EthCore) Client & Network Service Creation & Registration with the I/O Subsystem" name = "ethcore-service" version = "0.1.0" authors = ["Parity Technologies "] @@ -9,7 +10,6 @@ error-chain = { version = "0.12", default-features = false } ethcore = { path = ".." } ethcore-blockchain = { path = "../blockchain" } ethcore-io = { path = "../../util/io" } -ethcore-private-tx = { path = "../private-tx" } ethcore-sync = { path = "../sync" } ethereum-types = "0.4" kvdb = "0.1" diff --git a/ethcore/service/src/error.rs b/ethcore/service/src/error.rs index c73cb0dfc16..1c1840344c2 100644 --- a/ethcore/service/src/error.rs +++ b/ethcore/service/src/error.rs @@ -1,31 +1,29 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . // Silence: `use of deprecated item 'std::error::Error::cause': replaced by Error::source, which can support downcasting` -// https://github.com/paritytech/parity-ethereum/issues/10302 +// https://github.com/openethereum/openethereum/issues/10302 #![allow(deprecated)] use ethcore; use io; -use ethcore_private_tx; error_chain! { - foreign_links { - Ethcore(ethcore::error::Error); - IoError(io::IoError); - PrivateTransactions(ethcore_private_tx::Error); - } + foreign_links { + Ethcore(ethcore::error::Error); + IoError(io::IoError); + } } diff --git a/ethcore/service/src/lib.rs b/ethcore/service/src/lib.rs index 7828fff8b44..289ded738ab 100644 --- a/ethcore/service/src/lib.rs +++ b/ethcore/service/src/lib.rs @@ -1,24 +1,23 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . extern crate ansi_term; extern crate ethcore; extern crate ethcore_blockchain as blockchain; extern crate ethcore_io as io; -extern crate ethcore_private_tx; extern crate ethcore_sync as sync; extern crate ethereum_types; extern crate kvdb; @@ -43,4 +42,4 @@ mod stop_guard; extern crate kvdb_rocksdb; pub use error::{Error, ErrorKind}; -pub use service::{ClientService, PrivateTxService}; +pub use service::ClientService; diff --git a/ethcore/service/src/service.rs b/ethcore/service/src/service.rs index c16a071892b..acfeefd3ad3 100644 --- a/ethcore/service/src/service.rs +++ b/ethcore/service/src/service.rs @@ -1,210 +1,154 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Creates and registers client and network services. -use std::sync::Arc; -use std::path::Path; -use std::time::Duration; +use std::{path::Path, sync::Arc, time::Duration}; use ansi_term::Colour; -use ethereum_types::H256; -use io::{IoContext, TimerToken, IoHandler, IoService, IoError}; +use io::{IoContext, IoError, IoHandler, IoService, TimerToken}; use stop_guard::StopGuard; -use sync::PrivateTxHandler; use blockchain::{BlockChainDB, BlockChainDBHandler}; -use ethcore::client::{Client, ClientConfig, ChainNotify, ClientIoMessage}; -use ethcore::miner::Miner; -use ethcore::snapshot::service::{Service as SnapshotService, ServiceParams as SnapServiceParams}; -use ethcore::snapshot::{SnapshotService as _SnapshotService, RestorationStatus}; -use ethcore::spec::Spec; +use ethcore::{ + client::{ChainNotify, Client, ClientConfig, ClientIoMessage}, + error::{Error as EthcoreError, ErrorKind}, + miner::Miner, + snapshot::{ + service::{Service as SnapshotService, ServiceParams as SnapServiceParams}, + Error as SnapshotError, RestorationStatus, SnapshotService as _SnapshotService, + }, + spec::Spec, +}; -use ethcore_private_tx::{self, Importer, Signer}; use Error; -pub struct PrivateTxService { - provider: Arc, -} - -impl PrivateTxService { - fn new(provider: Arc) -> Self { - PrivateTxService { - provider, - } - } - - /// Returns underlying provider. - pub fn provider(&self) -> Arc { - self.provider.clone() - } -} - -impl PrivateTxHandler for PrivateTxService { - fn import_private_transaction(&self, rlp: &[u8]) -> Result { - match self.provider.import_private_transaction(rlp) { - Ok(import_result) => Ok(import_result), - Err(err) => { - warn!(target: "privatetx", "Unable to import private transaction packet: {}", err); - bail!(err.to_string()) - } - } - } - - fn import_signed_private_transaction(&self, rlp: &[u8]) -> Result { - match self.provider.import_signed_private_transaction(rlp) { - Ok(import_result) => Ok(import_result), - Err(err) => { - warn!(target: "privatetx", "Unable to import signed private transaction packet: {}", err); - bail!(err.to_string()) - } - } - } -} - /// Client service setup. Creates and registers client and network services with the IO subsystem. pub struct ClientService { - io_service: Arc>, - client: Arc, - snapshot: Arc, - private_tx: Arc, - database: Arc, - _stop_guard: StopGuard, + io_service: Arc>, + client: Arc, + snapshot: Arc, + database: Arc, + _stop_guard: StopGuard, } impl ClientService { - /// Start the `ClientService`. - pub fn start( - config: ClientConfig, - spec: &Spec, - blockchain_db: Arc, - snapshot_path: &Path, - restoration_db_handler: Box, - _ipc_path: &Path, - miner: Arc, - signer: Arc, - encryptor: Box, - private_tx_conf: ethcore_private_tx::ProviderConfig, - private_encryptor_conf: ethcore_private_tx::EncryptorConfig, - ) -> Result - { - let io_service = IoService::::start()?; - - info!("Configured for {} using {} engine", Colour::White.bold().paint(spec.name.clone()), Colour::Yellow.bold().paint(spec.engine.name())); - - let pruning = config.pruning; - let client = Client::new( - config, - &spec, - blockchain_db.clone(), - miner.clone(), - io_service.channel(), - )?; - miner.set_io_channel(io_service.channel()); - miner.set_in_chain_checker(&client.clone()); - - let snapshot_params = SnapServiceParams { - engine: spec.engine.clone(), - genesis_block: spec.genesis_block(), - restoration_db_handler: restoration_db_handler, - pruning: pruning, - channel: io_service.channel(), - snapshot_root: snapshot_path.into(), - client: client.clone(), - }; - let snapshot = Arc::new(SnapshotService::new(snapshot_params)?); - - let private_keys = Arc::new(ethcore_private_tx::SecretStoreKeys::new( - client.clone(), - private_encryptor_conf.key_server_account, - )); - let provider = Arc::new(ethcore_private_tx::Provider::new( - client.clone(), - miner, - signer, - encryptor, - private_tx_conf, - io_service.channel(), - private_keys, - )); - let private_tx = Arc::new(PrivateTxService::new(provider)); - - let client_io = Arc::new(ClientIoHandler { - client: client.clone(), - snapshot: snapshot.clone(), - }); - io_service.register_handler(client_io)?; - - spec.engine.register_client(Arc::downgrade(&client) as _); - - let stop_guard = StopGuard::new(); - - Ok(ClientService { - io_service: Arc::new(io_service), - client: client, - snapshot: snapshot, - private_tx, - database: blockchain_db, - _stop_guard: stop_guard, - }) - } - - /// Get general IO interface - pub fn register_io_handler(&self, handler: Arc + Send>) -> Result<(), IoError> { - self.io_service.register_handler(handler) - } - - /// Get client interface - pub fn client(&self) -> Arc { - self.client.clone() - } - - /// Get snapshot interface. - pub fn snapshot_service(&self) -> Arc { - self.snapshot.clone() - } - - /// Get private transaction service. - pub fn private_tx_service(&self) -> Arc { - self.private_tx.clone() - } - - /// Get network service component - pub fn io(&self) -> Arc> { - self.io_service.clone() - } - - /// Set the actor to be notified on certain chain events - pub fn add_notify(&self, notify: Arc) { - self.client.add_notify(notify); - } - - /// Get a handle to the database. - pub fn db(&self) -> Arc { self.database.clone() } - - /// Shutdown the Client Service - pub fn shutdown(&self) { - self.snapshot.shutdown(); - } + /// Start the `ClientService`. + pub fn start( + config: ClientConfig, + spec: &Spec, + blockchain_db: Arc, + snapshot_path: &Path, + restoration_db_handler: Box, + _ipc_path: &Path, + miner: Arc, + ) -> Result { + let io_service = IoService::::start()?; + + info!( + "Configured for {} using {} engine", + Colour::White.bold().paint(spec.name.clone()), + Colour::Yellow.bold().paint(spec.engine.name()) + ); + + let pruning = config.pruning; + let client = Client::new( + config, + &spec, + blockchain_db.clone(), + miner.clone(), + io_service.channel(), + )?; + miner.set_io_channel(io_service.channel()); + miner.set_in_chain_checker(&client.clone()); + + let snapshot_params = SnapServiceParams { + engine: spec.engine.clone(), + genesis_block: spec.genesis_block(), + restoration_db_handler: restoration_db_handler, + pruning: pruning, + channel: io_service.channel(), + snapshot_root: snapshot_path.into(), + client: client.clone(), + }; + let snapshot = Arc::new(SnapshotService::new(snapshot_params)?); + + let client_io = Arc::new(ClientIoHandler { + client: client.clone(), + snapshot: snapshot.clone(), + }); + io_service.register_handler(client_io)?; + + spec.engine.register_client(Arc::downgrade(&client) as _); + + let stop_guard = StopGuard::new(); + + Ok(ClientService { + io_service: Arc::new(io_service), + client: client, + snapshot: snapshot, + database: blockchain_db, + _stop_guard: stop_guard, + }) + } + + /// Get general IO interface + pub fn register_io_handler( + &self, + handler: Arc + Send>, + ) -> Result<(), IoError> { + self.io_service.register_handler(handler) + } + + /// Get client interface + pub fn client(&self) -> Arc { + self.client.clone() + } + + /// Get snapshot interface. + pub fn snapshot_service(&self) -> Arc { + self.snapshot.clone() + } + + /// Get network service component + pub fn io(&self) -> Arc> { + self.io_service.clone() + } + + /// Set the actor to be notified on certain chain events + pub fn add_notify(&self, notify: Arc) { + self.client.add_notify(notify); + } + + /// Get a handle to the database. + pub fn db(&self) -> Arc { + self.database.clone() + } + + /// Shutdown the Client Service + pub fn shutdown(&self) { + trace!(target: "shutdown", "Shutting down Client Service"); + self.snapshot.shutdown(); + } } /// IO interface for the Client handler struct ClientIoHandler { - client: Arc, - snapshot: Arc, + client: Arc, + snapshot: Arc, } const CLIENT_TICK_TIMER: TimerToken = 0; @@ -214,114 +158,119 @@ const CLIENT_TICK: Duration = Duration::from_secs(5); const SNAPSHOT_TICK: Duration = Duration::from_secs(10); impl IoHandler for ClientIoHandler { - fn initialize(&self, io: &IoContext) { - io.register_timer(CLIENT_TICK_TIMER, CLIENT_TICK).expect("Error registering client timer"); - io.register_timer(SNAPSHOT_TICK_TIMER, SNAPSHOT_TICK).expect("Error registering snapshot timer"); - } - - fn timeout(&self, _io: &IoContext, timer: TimerToken) { - trace_time!("service::read"); - match timer { - CLIENT_TICK_TIMER => { - use ethcore::snapshot::SnapshotService; - let snapshot_restoration = if let RestorationStatus::Ongoing{..} = self.snapshot.status() { true } else { false }; - self.client.tick(snapshot_restoration) - }, - SNAPSHOT_TICK_TIMER => self.snapshot.tick(), - _ => warn!("IO service triggered unregistered timer '{}'", timer), - } - } - - fn message(&self, _io: &IoContext, net_message: &ClientIoMessage) { - trace_time!("service::message"); - use std::thread; - - match *net_message { - ClientIoMessage::BlockVerified => { - self.client.import_verified_blocks(); - } - ClientIoMessage::BeginRestoration(ref manifest) => { - if let Err(e) = self.snapshot.init_restore(manifest.clone(), true) { - warn!("Failed to initialize snapshot restoration: {}", e); - } - } - ClientIoMessage::FeedStateChunk(ref hash, ref chunk) => { - self.snapshot.feed_state_chunk(*hash, chunk) - } - ClientIoMessage::FeedBlockChunk(ref hash, ref chunk) => { - self.snapshot.feed_block_chunk(*hash, chunk) - } - ClientIoMessage::TakeSnapshot(num) => { - let client = self.client.clone(); - let snapshot = self.snapshot.clone(); - - let res = thread::Builder::new().name("Periodic Snapshot".into()).spawn(move || { - if let Err(e) = snapshot.take_snapshot(&*client, num) { - warn!("Failed to take snapshot at block #{}: {}", num, e); - } - }); - - if let Err(e) = res { - debug!(target: "snapshot", "Failed to initialize periodic snapshot thread: {:?}", e); - } - }, - ClientIoMessage::Execute(ref exec) => { - (*exec.0)(&self.client); - } - _ => {} // ignore other messages - } - } + fn initialize(&self, io: &IoContext) { + io.register_timer(CLIENT_TICK_TIMER, CLIENT_TICK) + .expect("Error registering client timer"); + io.register_timer(SNAPSHOT_TICK_TIMER, SNAPSHOT_TICK) + .expect("Error registering snapshot timer"); + } + + fn timeout(&self, _io: &IoContext, timer: TimerToken) { + trace_time!("service::read"); + match timer { + CLIENT_TICK_TIMER => { + use ethcore::snapshot::SnapshotService; + let snapshot_restoration = + if let RestorationStatus::Ongoing { .. } = self.snapshot.restoration_status() { + true + } else { + false + }; + self.client.tick(snapshot_restoration) + } + SNAPSHOT_TICK_TIMER => self.snapshot.tick(), + _ => warn!("IO service triggered unregistered timer '{}'", timer), + } + } + + fn message(&self, _io: &IoContext, net_message: &ClientIoMessage) { + trace_time!("service::message"); + use std::thread; + + match *net_message { + ClientIoMessage::BlockVerified => { + self.client.import_verified_blocks(); + } + ClientIoMessage::BeginRestoration(ref manifest) => { + if let Err(e) = self.snapshot.init_restore(manifest.clone(), true) { + warn!("Failed to initialize snapshot restoration: {}", e); + } + } + ClientIoMessage::FeedStateChunk(ref hash, ref chunk) => { + self.snapshot.feed_state_chunk(*hash, chunk) + } + ClientIoMessage::FeedBlockChunk(ref hash, ref chunk) => { + self.snapshot.feed_block_chunk(*hash, chunk) + } + ClientIoMessage::TakeSnapshot(num) => { + let client = self.client.clone(); + let snapshot = self.snapshot.clone(); + + let res = thread::Builder::new() + .name("Periodic Snapshot".into()) + .spawn(move || { + if let Err(e) = snapshot.take_snapshot(&*client, num) { + match e { + EthcoreError( + ErrorKind::Snapshot(SnapshotError::SnapshotAborted), + _, + ) => info!("Snapshot aborted"), + _ => warn!("Failed to take snapshot at block #{}: {}", num, e), + } + } + }); + + if let Err(e) = res { + debug!(target: "snapshot", "Failed to initialize periodic snapshot thread: {:?}", e); + } + } + ClientIoMessage::Execute(ref exec) => { + (*exec.0)(&self.client); + } + _ => {} // ignore other messages + } + } } #[cfg(test)] mod tests { - use std::sync::Arc; - use std::{time, thread}; - - use tempdir::TempDir; - - use ethcore_db::NUM_COLUMNS; - use ethcore::client::ClientConfig; - use ethcore::miner::Miner; - use ethcore::spec::Spec; - use ethcore::test_helpers; - use kvdb_rocksdb::{DatabaseConfig, CompactionProfile}; - use super::*; - - use ethcore_private_tx; - - #[test] - fn it_can_be_started() { - let tempdir = TempDir::new("").unwrap(); - let client_path = tempdir.path().join("client"); - let snapshot_path = tempdir.path().join("snapshot"); - - let client_config = ClientConfig::default(); - let mut client_db_config = DatabaseConfig::with_columns(NUM_COLUMNS); - - client_db_config.memory_budget = client_config.db_cache_size; - client_db_config.compaction = CompactionProfile::auto(&client_path); - - let client_db_handler = test_helpers::restoration_db_handler(client_db_config.clone()); - let client_db = client_db_handler.open(&client_path).unwrap(); - let restoration_db_handler = test_helpers::restoration_db_handler(client_db_config); - - let spec = Spec::new_test(); - let service = ClientService::start( - ClientConfig::default(), - &spec, - client_db, - &snapshot_path, - restoration_db_handler, - tempdir.path(), - Arc::new(Miner::new_for_tests(&spec, None)), - Arc::new(ethcore_private_tx::DummySigner), - Box::new(ethcore_private_tx::NoopEncryptor), - Default::default(), - Default::default(), - ); - assert!(service.is_ok()); - drop(service.unwrap()); - thread::park_timeout(time::Duration::from_millis(100)); - } + use std::{sync::Arc, thread, time}; + + use tempdir::TempDir; + + use super::*; + use ethcore::{client::ClientConfig, miner::Miner, spec::Spec, test_helpers}; + use ethcore_db::NUM_COLUMNS; + use kvdb_rocksdb::{CompactionProfile, DatabaseConfig}; + + #[test] + fn it_can_be_started() { + let tempdir = TempDir::new("").unwrap(); + let client_path = tempdir.path().join("client"); + let snapshot_path = tempdir.path().join("snapshot"); + + let client_config = ClientConfig::default(); + let mut client_db_config = DatabaseConfig::with_columns(NUM_COLUMNS); + + client_db_config.memory_budget = client_config.db_cache_size; + client_db_config.compaction = CompactionProfile::auto(&client_path); + + let client_db_handler = test_helpers::restoration_db_handler(client_db_config.clone()); + let client_db = client_db_handler.open(&client_path).unwrap(); + let restoration_db_handler = test_helpers::restoration_db_handler(client_db_config); + + let spec = Spec::new_test(); + let service = ClientService::start( + ClientConfig::default(), + &spec, + client_db, + &snapshot_path, + restoration_db_handler, + tempdir.path(), + Arc::new(Miner::new_for_tests(&spec, None)), + ); + assert!(service.is_ok()); + drop(service.unwrap()); + thread::park_timeout(time::Duration::from_millis(100)); + } } diff --git a/ethcore/service/src/stop_guard.rs b/ethcore/service/src/stop_guard.rs index 168219520a1..34438fd59fa 100644 --- a/ethcore/service/src/stop_guard.rs +++ b/ethcore/service/src/stop_guard.rs @@ -1,40 +1,39 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Stop guard mod -use std::sync::Arc; -use std::sync::atomic::*; +use std::sync::{atomic::*, Arc}; /// Stop guard that will set a stop flag on drop pub struct StopGuard { - flag: Arc, + flag: Arc, } impl StopGuard { - /// Create a stop guard - pub fn new() -> StopGuard { - StopGuard { - flag: Arc::new(AtomicBool::new(false)) - } - } + /// Create a stop guard + pub fn new() -> StopGuard { + StopGuard { + flag: Arc::new(AtomicBool::new(false)), + } + } } impl Drop for StopGuard { - fn drop(&mut self) { - self.flag.store(true, Ordering::Relaxed) - } + fn drop(&mut self) { + self.flag.store(true, Ordering::Relaxed) + } } diff --git a/ethcore/src/account_db.rs b/ethcore/src/account_db.rs index a389c009bf7..d0464cf2e69 100644 --- a/ethcore/src/account_db.rs +++ b/ethcore/src/account_db.rs @@ -1,23 +1,23 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! DB backend wrapper for Account trie use ethereum_types::H256; -use hash::{KECCAK_NULL_RLP, keccak}; -use hash_db::{HashDB, AsHashDB}; +use hash::{keccak, KECCAK_NULL_RLP}; +use hash_db::{AsHashDB, HashDB}; use keccak_hasher::KeccakHasher; use kvdb::DBValue; use rlp::NULL_RLP; @@ -29,255 +29,284 @@ use ethereum_types::Address; // leaves the first 96 bits untouched in order to support partial key lookup. #[inline] fn combine_key<'a>(address_hash: &'a H256, key: &'a H256) -> H256 { - let mut dst = key.clone(); - { - let last_src: &[u8] = &*address_hash; - let last_dst: &mut [u8] = &mut *dst; - for (k, a) in last_dst[12..].iter_mut().zip(&last_src[12..]) { - *k ^= *a - } - } - - dst + let mut dst = key.clone(); + { + let last_src: &[u8] = &*address_hash; + let last_dst: &mut [u8] = &mut *dst; + for (k, a) in last_dst[12..].iter_mut().zip(&last_src[12..]) { + *k ^= *a + } + } + + dst } /// A factory for different kinds of account dbs. #[derive(Debug, Clone)] pub enum Factory { - /// Mangle hashes based on address. This is the default. - Mangled, - /// Don't mangle hashes. - Plain, + /// Mangle hashes based on address. This is the default. + Mangled, + /// Don't mangle hashes. + Plain, } impl Default for Factory { - fn default() -> Self { Factory::Mangled } + fn default() -> Self { + Factory::Mangled + } } impl Factory { - /// Create a read-only accountdb. - /// This will panic when write operations are called. - pub fn readonly<'db>(&self, db: &'db HashDB, address_hash: H256) -> Box + 'db> { - match *self { - Factory::Mangled => Box::new(AccountDB::from_hash(db, address_hash)), - Factory::Plain => Box::new(Wrapping(db)), - } - } - - /// Create a new mutable hashdb. - pub fn create<'db>(&self, db: &'db mut HashDB, address_hash: H256) -> Box + 'db> { - match *self { - Factory::Mangled => Box::new(AccountDBMut::from_hash(db, address_hash)), - Factory::Plain => Box::new(WrappingMut(db)), - } - } + /// Create a read-only accountdb. + /// This will panic when write operations are called. + pub fn readonly<'db>( + &self, + db: &'db dyn HashDB, + address_hash: H256, + ) -> Box + 'db> { + match *self { + Factory::Mangled => Box::new(AccountDB::from_hash(db, address_hash)), + Factory::Plain => Box::new(Wrapping(db)), + } + } + + /// Create a new mutable hashdb. + pub fn create<'db>( + &self, + db: &'db mut dyn HashDB, + address_hash: H256, + ) -> Box + 'db> { + match *self { + Factory::Mangled => Box::new(AccountDBMut::from_hash(db, address_hash)), + Factory::Plain => Box::new(WrappingMut(db)), + } + } } // TODO: introduce HashDBMut? /// DB backend wrapper for Account trie /// Transforms trie node keys for the database pub struct AccountDB<'db> { - db: &'db HashDB, - address_hash: H256, + db: &'db dyn HashDB, + address_hash: H256, } impl<'db> AccountDB<'db> { - /// Create a new AccountDB from an address. - #[cfg(test)] - pub fn new(db: &'db HashDB, address: &Address) -> Self { - Self::from_hash(db, keccak(address)) - } - - /// Create a new AcountDB from an address' hash. - pub fn from_hash(db: &'db HashDB, address_hash: H256) -> Self { - AccountDB { - db: db, - address_hash: address_hash, - } - } + /// Create a new AccountDB from an address. + #[cfg(test)] + pub fn new(db: &'db dyn HashDB, address: &Address) -> Self { + Self::from_hash(db, keccak(address)) + } + + /// Create a new AcountDB from an address' hash. + pub fn from_hash(db: &'db dyn HashDB, address_hash: H256) -> Self { + AccountDB { + db: db, + address_hash: address_hash, + } + } } impl<'db> AsHashDB for AccountDB<'db> { - fn as_hash_db(&self) -> &HashDB { self } - fn as_hash_db_mut(&mut self) -> &mut HashDB { self } + fn as_hash_db(&self) -> &dyn HashDB { + self + } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { + self + } } impl<'db> HashDB for AccountDB<'db> { - fn get(&self, key: &H256) -> Option { - if key == &KECCAK_NULL_RLP { - return Some(DBValue::from_slice(&NULL_RLP)); - } - self.db.get(&combine_key(&self.address_hash, key)) - } - - fn contains(&self, key: &H256) -> bool { - if key == &KECCAK_NULL_RLP { - return true; - } - self.db.contains(&combine_key(&self.address_hash, key)) - } - - fn insert(&mut self, _value: &[u8]) -> H256 { - unimplemented!() - } - - fn emplace(&mut self, _key: H256, _value: DBValue) { - unimplemented!() - } - - fn remove(&mut self, _key: &H256) { - unimplemented!() - } + fn get(&self, key: &H256) -> Option { + if key == &KECCAK_NULL_RLP { + return Some(DBValue::from_slice(&NULL_RLP)); + } + self.db.get(&combine_key(&self.address_hash, key)) + } + + fn contains(&self, key: &H256) -> bool { + if key == &KECCAK_NULL_RLP { + return true; + } + self.db.contains(&combine_key(&self.address_hash, key)) + } + + fn insert(&mut self, _value: &[u8]) -> H256 { + unimplemented!() + } + + fn emplace(&mut self, _key: H256, _value: DBValue) { + unimplemented!() + } + + fn remove(&mut self, _key: &H256) { + unimplemented!() + } } /// DB backend wrapper for Account trie pub struct AccountDBMut<'db> { - db: &'db mut HashDB, - address_hash: H256, + db: &'db mut dyn HashDB, + address_hash: H256, } impl<'db> AccountDBMut<'db> { - /// Create a new AccountDB from an address. - #[cfg(test)] - pub fn new(db: &'db mut HashDB, address: &Address) -> Self { - Self::from_hash(db, keccak(address)) - } - - /// Create a new AcountDB from an address' hash. - pub fn from_hash(db: &'db mut HashDB, address_hash: H256) -> Self { - AccountDBMut { - db: db, - address_hash: address_hash, - } - } - - #[cfg(test)] - pub fn immutable(&'db self) -> AccountDB<'db> { - AccountDB { db: self.db, address_hash: self.address_hash.clone() } - } + /// Create a new AccountDB from an address. + #[cfg(test)] + pub fn new(db: &'db mut dyn HashDB, address: &Address) -> Self { + Self::from_hash(db, keccak(address)) + } + + /// Create a new AcountDB from an address' hash. + pub fn from_hash(db: &'db mut dyn HashDB, address_hash: H256) -> Self { + AccountDBMut { + db: db, + address_hash: address_hash, + } + } + + #[cfg(test)] + pub fn immutable(&'db self) -> AccountDB<'db> { + AccountDB { + db: self.db, + address_hash: self.address_hash.clone(), + } + } } -impl<'db> HashDB for AccountDBMut<'db>{ - fn get(&self, key: &H256) -> Option { - if key == &KECCAK_NULL_RLP { - return Some(DBValue::from_slice(&NULL_RLP)); - } - self.db.get(&combine_key(&self.address_hash, key)) - } - - fn contains(&self, key: &H256) -> bool { - if key == &KECCAK_NULL_RLP { - return true; - } - self.db.contains(&combine_key(&self.address_hash, key)) - } - - fn insert(&mut self, value: &[u8]) -> H256 { - if value == &NULL_RLP { - return KECCAK_NULL_RLP.clone(); - } - let k = keccak(value); - let ak = combine_key(&self.address_hash, &k); - self.db.emplace(ak, DBValue::from_slice(value)); - k - } - - fn emplace(&mut self, key: H256, value: DBValue) { - if key == KECCAK_NULL_RLP { - return; - } - let key = combine_key(&self.address_hash, &key); - self.db.emplace(key, value) - } - - fn remove(&mut self, key: &H256) { - if key == &KECCAK_NULL_RLP { - return; - } - let key = combine_key(&self.address_hash, key); - self.db.remove(&key) - } +impl<'db> HashDB for AccountDBMut<'db> { + fn get(&self, key: &H256) -> Option { + if key == &KECCAK_NULL_RLP { + return Some(DBValue::from_slice(&NULL_RLP)); + } + self.db.get(&combine_key(&self.address_hash, key)) + } + + fn contains(&self, key: &H256) -> bool { + if key == &KECCAK_NULL_RLP { + return true; + } + self.db.contains(&combine_key(&self.address_hash, key)) + } + + fn insert(&mut self, value: &[u8]) -> H256 { + if value == &NULL_RLP { + return KECCAK_NULL_RLP.clone(); + } + let k = keccak(value); + let ak = combine_key(&self.address_hash, &k); + self.db.emplace(ak, DBValue::from_slice(value)); + k + } + + fn emplace(&mut self, key: H256, value: DBValue) { + if key == KECCAK_NULL_RLP { + return; + } + let key = combine_key(&self.address_hash, &key); + self.db.emplace(key, value) + } + + fn remove(&mut self, key: &H256) { + if key == &KECCAK_NULL_RLP { + return; + } + let key = combine_key(&self.address_hash, key); + self.db.remove(&key) + } } impl<'db> AsHashDB for AccountDBMut<'db> { - fn as_hash_db(&self) -> &HashDB { self } - fn as_hash_db_mut(&mut self) -> &mut HashDB { self } + fn as_hash_db(&self) -> &dyn HashDB { + self + } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { + self + } } -struct Wrapping<'db>(&'db HashDB); +struct Wrapping<'db>(&'db dyn HashDB); impl<'db> AsHashDB for Wrapping<'db> { - fn as_hash_db(&self) -> &HashDB { self } - fn as_hash_db_mut(&mut self) -> &mut HashDB { self } + fn as_hash_db(&self) -> &dyn HashDB { + self + } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { + self + } } impl<'db> HashDB for Wrapping<'db> { - fn get(&self, key: &H256) -> Option { - if key == &KECCAK_NULL_RLP { - return Some(DBValue::from_slice(&NULL_RLP)); - } - self.0.get(key) - } - - fn contains(&self, key: &H256) -> bool { - if key == &KECCAK_NULL_RLP { - return true; - } - self.0.contains(key) - } - - fn insert(&mut self, _value: &[u8]) -> H256 { - unimplemented!() - } - - fn emplace(&mut self, _key: H256, _value: DBValue) { - unimplemented!() - } - - fn remove(&mut self, _key: &H256) { - unimplemented!() - } + fn get(&self, key: &H256) -> Option { + if key == &KECCAK_NULL_RLP { + return Some(DBValue::from_slice(&NULL_RLP)); + } + self.0.get(key) + } + + fn contains(&self, key: &H256) -> bool { + if key == &KECCAK_NULL_RLP { + return true; + } + self.0.contains(key) + } + + fn insert(&mut self, _value: &[u8]) -> H256 { + unimplemented!() + } + + fn emplace(&mut self, _key: H256, _value: DBValue) { + unimplemented!() + } + + fn remove(&mut self, _key: &H256) { + unimplemented!() + } } -struct WrappingMut<'db>(&'db mut HashDB); +struct WrappingMut<'db>(&'db mut dyn HashDB); impl<'db> AsHashDB for WrappingMut<'db> { - fn as_hash_db(&self) -> &HashDB { self } - fn as_hash_db_mut(&mut self) -> &mut HashDB { self } + fn as_hash_db(&self) -> &dyn HashDB { + self + } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { + self + } } -impl<'db> HashDB for WrappingMut<'db>{ - fn get(&self, key: &H256) -> Option { - if key == &KECCAK_NULL_RLP { - return Some(DBValue::from_slice(&NULL_RLP)); - } - self.0.get(key) - } - - fn contains(&self, key: &H256) -> bool { - if key == &KECCAK_NULL_RLP { - return true; - } - self.0.contains(key) - } - - fn insert(&mut self, value: &[u8]) -> H256 { - if value == &NULL_RLP { - return KECCAK_NULL_RLP.clone(); - } - self.0.insert(value) - } - - fn emplace(&mut self, key: H256, value: DBValue) { - if key == KECCAK_NULL_RLP { - return; - } - self.0.emplace(key, value) - } - - fn remove(&mut self, key: &H256) { - if key == &KECCAK_NULL_RLP { - return; - } - self.0.remove(key) - } +impl<'db> HashDB for WrappingMut<'db> { + fn get(&self, key: &H256) -> Option { + if key == &KECCAK_NULL_RLP { + return Some(DBValue::from_slice(&NULL_RLP)); + } + self.0.get(key) + } + + fn contains(&self, key: &H256) -> bool { + if key == &KECCAK_NULL_RLP { + return true; + } + self.0.contains(key) + } + + fn insert(&mut self, value: &[u8]) -> H256 { + if value == &NULL_RLP { + return KECCAK_NULL_RLP.clone(); + } + self.0.insert(value) + } + + fn emplace(&mut self, key: H256, value: DBValue) { + if key == KECCAK_NULL_RLP { + return; + } + self.0.emplace(key, value) + } + + fn remove(&mut self, key: &H256) { + if key == &KECCAK_NULL_RLP { + return; + } + self.0.remove(key) + } } diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 56cfc1c4c14..0b49a267c6a 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Base data structure of this module is `Block`. //! @@ -31,18 +31,16 @@ //! `ExecutedBlock` is an underlaying data structure used by all structs above to store block //! related info. -use std::{cmp, ops}; -use std::collections::HashSet; -use std::sync::Arc; +use std::{cmp, collections::HashSet, ops, sync::Arc}; use bytes::Bytes; -use ethereum_types::{H256, U256, Address, Bloom}; +use ethereum_types::{Address, Bloom, H256, U256}; use engines::EthEngine; -use error::{Error, BlockError}; +use error::{BlockError, Error}; use factory::Factories; -use state_db::StateDB; use state::State; +use state_db::StateDB; use trace::Tracing; use triehash::ordered_trie_root; use unexpected::{Mismatch, OutOfBounds}; @@ -50,18 +48,20 @@ use verification::PreverifiedBlock; use vm::{EnvInfo, LastHashes}; use hash::keccak; -use rlp::{RlpStream, Encodable, encode_list}; -use types::transaction::{SignedTransaction, Error as TransactionError}; -use types::header::{Header, ExtendedHeader}; -use types::receipt::{Receipt, TransactionOutcome}; +use rlp::{encode_list, Encodable, RlpStream}; +use types::{ + header::{ExtendedHeader, Header}, + receipt::{Receipt, TransactionOutcome}, + transaction::{Error as TransactionError, SignedTransaction}, +}; /// Block that is ready for transactions to be added. /// /// It's a bit like a Vec, except that whenever a transaction is pushed, we execute it and /// maintain the system `state()`. We also archive execution receipts in preparation for later block creation. pub struct OpenBlock<'x> { - block: ExecutedBlock, - engine: &'x EthEngine, + block: ExecutedBlock, + engine: &'x dyn EthEngine, } /// Just like `OpenBlock`, except that we've applied `Engine::on_close_block`, finished up the non-seal header fields, @@ -70,8 +70,8 @@ pub struct OpenBlock<'x> { /// There is no function available to push a transaction. #[derive(Clone)] pub struct ClosedBlock { - block: ExecutedBlock, - unclosed_state: State, + block: ExecutedBlock, + unclosed_state: State, } /// Just like `ClosedBlock` except that we can't reopen it and it's faster. @@ -79,629 +79,771 @@ pub struct ClosedBlock { /// We actually store the post-`Engine::on_close_block` state, unlike in `ClosedBlock` where it's the pre. #[derive(Clone)] pub struct LockedBlock { - block: ExecutedBlock, + block: ExecutedBlock, } /// A block that has a valid seal. /// /// The block's header has valid seal arguments. The block cannot be reversed into a `ClosedBlock` or `OpenBlock`. pub struct SealedBlock { - block: ExecutedBlock, + block: ExecutedBlock, } /// An internal type for a block's common elements. #[derive(Clone)] pub struct ExecutedBlock { - /// Executed block header. - pub header: Header, - /// Executed transactions. - pub transactions: Vec, - /// Uncles. - pub uncles: Vec
, - /// Transaction receipts. - pub receipts: Vec, - /// Hashes of already executed transactions. - pub transactions_set: HashSet, - /// Underlaying state. - pub state: State, - /// Transaction traces. - pub traces: Tracing, - /// Hashes of last 256 blocks. - pub last_hashes: Arc, + /// Executed block header. + pub header: Header, + /// Executed transactions. + pub transactions: Vec, + /// Uncles. + pub uncles: Vec
, + /// Transaction receipts. + pub receipts: Vec, + /// Hashes of already executed transactions. + pub transactions_set: HashSet, + /// Underlaying state. + pub state: State, + /// Transaction traces. + pub traces: Tracing, + /// Hashes of last 256 blocks. + pub last_hashes: Arc, } impl ExecutedBlock { - /// Create a new block from the given `state`. - fn new(state: State, last_hashes: Arc, tracing: bool) -> ExecutedBlock { - ExecutedBlock { - header: Default::default(), - transactions: Default::default(), - uncles: Default::default(), - receipts: Default::default(), - transactions_set: Default::default(), - state: state, - traces: if tracing { - Tracing::enabled() - } else { - Tracing::Disabled - }, - last_hashes: last_hashes, - } - } - - /// Get the environment info concerning this block. - pub fn env_info(&self) -> EnvInfo { - // TODO: memoise. - EnvInfo { - number: self.header.number(), - author: self.header.author().clone(), - timestamp: self.header.timestamp(), - difficulty: self.header.difficulty().clone(), - last_hashes: self.last_hashes.clone(), - gas_used: self.receipts.last().map_or(U256::zero(), |r| r.gas_used), - gas_limit: self.header.gas_limit().clone(), - } - } - - /// Get mutable access to a state. - pub fn state_mut(&mut self) -> &mut State { - &mut self.state - } - - /// Get mutable reference to traces. - pub fn traces_mut(&mut self) -> &mut Tracing { - &mut self.traces - } + /// Create a new block from the given `state`. + fn new(state: State, last_hashes: Arc, tracing: bool) -> ExecutedBlock { + ExecutedBlock { + header: Default::default(), + transactions: Default::default(), + uncles: Default::default(), + receipts: Default::default(), + transactions_set: Default::default(), + state: state, + traces: if tracing { + Tracing::enabled() + } else { + Tracing::Disabled + }, + last_hashes: last_hashes, + } + } + + /// Get the environment info concerning this block. + pub fn env_info(&self) -> EnvInfo { + // TODO: memoise. + EnvInfo { + number: self.header.number(), + author: self.header.author().clone(), + timestamp: self.header.timestamp(), + difficulty: self.header.difficulty().clone(), + last_hashes: self.last_hashes.clone(), + gas_used: self.receipts.last().map_or(U256::zero(), |r| r.gas_used), + gas_limit: self.header.gas_limit().clone(), + } + } + + /// Get mutable access to a state. + pub fn state_mut(&mut self) -> &mut State { + &mut self.state + } + + /// Get mutable reference to traces. + pub fn traces_mut(&mut self) -> &mut Tracing { + &mut self.traces + } } /// Trait for an object that owns an `ExecutedBlock` pub trait Drain { - /// Returns `ExecutedBlock` - fn drain(self) -> ExecutedBlock; + /// Returns `ExecutedBlock` + fn drain(self) -> ExecutedBlock; } impl<'x> OpenBlock<'x> { - /// Create a new `OpenBlock` ready for transaction pushing. - pub fn new<'a, I: IntoIterator>( - engine: &'x EthEngine, - factories: Factories, - tracing: bool, - db: StateDB, - parent: &Header, - last_hashes: Arc, - author: Address, - gas_range_target: (U256, U256), - extra_data: Bytes, - is_epoch_begin: bool, - ancestry: I, - ) -> Result { - let number = parent.number() + 1; - let state = State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce(number), factories)?; - let mut r = OpenBlock { - block: ExecutedBlock::new(state, last_hashes, tracing), - engine: engine, - }; - - r.block.header.set_parent_hash(parent.hash()); - r.block.header.set_number(number); - r.block.header.set_author(author); - r.block.header.set_timestamp(engine.open_block_header_timestamp(parent.timestamp())); - r.block.header.set_extra_data(extra_data); - - let gas_floor_target = cmp::max(gas_range_target.0, engine.params().min_gas_limit); - let gas_ceil_target = cmp::max(gas_range_target.1, gas_floor_target); - - engine.machine().populate_from_parent(&mut r.block.header, parent, gas_floor_target, gas_ceil_target); - engine.populate_from_parent(&mut r.block.header, parent); - - engine.machine().on_new_block(&mut r.block)?; - engine.on_new_block(&mut r.block, is_epoch_begin, &mut ancestry.into_iter())?; - - Ok(r) - } - - /// Alter the timestamp of the block. - pub fn set_timestamp(&mut self, timestamp: u64) { - self.block.header.set_timestamp(timestamp); - } - - /// Removes block gas limit. - pub fn remove_gas_limit(&mut self) { - self.block.header.set_gas_limit(U256::max_value()); - } - - /// Add an uncle to the block, if possible. - /// - /// NOTE Will check chain constraints and the uncle number but will NOT check - /// that the header itself is actually valid. - pub fn push_uncle(&mut self, valid_uncle_header: Header) -> Result<(), BlockError> { - let max_uncles = self.engine.maximum_uncle_count(self.block.header.number()); - if self.block.uncles.len() + 1 > max_uncles { - return Err(BlockError::TooManyUncles(OutOfBounds{ - min: None, - max: Some(max_uncles), - found: self.block.uncles.len() + 1, - })); - } - // TODO: check number - // TODO: check not a direct ancestor (use last_hashes for that) - self.block.uncles.push(valid_uncle_header); - Ok(()) - } - - /// Push a transaction into the block. - /// - /// If valid, it will be executed, and archived together with the receipt. - pub fn push_transaction(&mut self, t: SignedTransaction, h: Option) -> Result<&Receipt, Error> { - if self.block.transactions_set.contains(&t.hash()) { - return Err(TransactionError::AlreadyImported.into()); - } - - let env_info = self.block.env_info(); - let outcome = self.block.state.apply(&env_info, self.engine.machine(), &t, self.block.traces.is_enabled())?; - - self.block.transactions_set.insert(h.unwrap_or_else(||t.hash())); - self.block.transactions.push(t.into()); - if let Tracing::Enabled(ref mut traces) = self.block.traces { - traces.push(outcome.trace.into()); - } - self.block.receipts.push(outcome.receipt); - Ok(self.block.receipts.last().expect("receipt just pushed; qed")) - } - - /// Push transactions onto the block. - #[cfg(not(feature = "slow-blocks"))] - fn push_transactions(&mut self, transactions: Vec) -> Result<(), Error> { - for t in transactions { - self.push_transaction(t, None)?; - } - Ok(()) - } - - /// Push transactions onto the block. - #[cfg(feature = "slow-blocks")] - fn push_transactions(&mut self, transactions: Vec) -> Result<(), Error> { - use std::time; - - let slow_tx = option_env!("SLOW_TX_DURATION").and_then(|v| v.parse().ok()).unwrap_or(100); - for t in transactions { - let hash = t.hash(); - let start = time::Instant::now(); - self.push_transaction(t, None)?; - let took = start.elapsed(); - let took_ms = took.as_secs() * 1000 + took.subsec_nanos() as u64 / 1000000; - if took > time::Duration::from_millis(slow_tx) { - warn!("Heavy ({} ms) transaction in block {:?}: {:?}", took_ms, self.block.header().number(), hash); - } - debug!(target: "tx", "Transaction {:?} took: {} ms", hash, took_ms); - } - - Ok(()) - } - - /// Populate self from a header. - fn populate_from(&mut self, header: &Header) { - self.block.header.set_difficulty(*header.difficulty()); - self.block.header.set_gas_limit(*header.gas_limit()); - self.block.header.set_timestamp(header.timestamp()); - self.block.header.set_uncles_hash(*header.uncles_hash()); - self.block.header.set_transactions_root(*header.transactions_root()); - // TODO: that's horrible. set only for backwards compatibility - if header.extra_data().len() > self.engine.maximum_extra_data_size() { - warn!("Couldn't set extradata. Ignoring."); - } else { - self.block.header.set_extra_data(header.extra_data().clone()); - } - } - - /// Turn this into a `ClosedBlock`. - pub fn close(self) -> Result { - let unclosed_state = self.block.state.clone(); - let locked = self.close_and_lock()?; - - Ok(ClosedBlock { - block: locked.block, - unclosed_state, - }) - } - - /// Turn this into a `LockedBlock`. - pub fn close_and_lock(self) -> Result { - let mut s = self; - - s.engine.on_close_block(&mut s.block)?; - s.block.state.commit()?; - - s.block.header.set_transactions_root(ordered_trie_root(s.block.transactions.iter().map(|e| e.rlp_bytes()))); - let uncle_bytes = encode_list(&s.block.uncles); - s.block.header.set_uncles_hash(keccak(&uncle_bytes)); - s.block.header.set_state_root(s.block.state.root().clone()); - s.block.header.set_receipts_root(ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes()))); - s.block.header.set_log_bloom(s.block.receipts.iter().fold(Bloom::zero(), |mut b, r| { - b.accrue_bloom(&r.log_bloom); - b - })); - s.block.header.set_gas_used(s.block.receipts.last().map_or_else(U256::zero, |r| r.gas_used)); - - Ok(LockedBlock { - block: s.block, - }) - } - - #[cfg(test)] - /// Return mutable block reference. To be used in tests only. - pub fn block_mut(&mut self) -> &mut ExecutedBlock { &mut self.block } + /// Create a new `OpenBlock` ready for transaction pushing. + pub fn new<'a, I: IntoIterator>( + engine: &'x dyn EthEngine, + factories: Factories, + tracing: bool, + db: StateDB, + parent: &Header, + last_hashes: Arc, + author: Address, + gas_range_target: (U256, U256), + extra_data: Bytes, + is_epoch_begin: bool, + ancestry: I, + ) -> Result { + let number = parent.number() + 1; + let state = State::from_existing( + db, + parent.state_root().clone(), + engine.account_start_nonce(number), + factories, + )?; + let mut r = OpenBlock { + block: ExecutedBlock::new(state, last_hashes, tracing), + engine: engine, + }; + + r.block.header.set_parent_hash(parent.hash()); + r.block.header.set_number(number); + r.block.header.set_author(author); + r.block + .header + .set_timestamp(engine.open_block_header_timestamp(parent.timestamp())); + r.block.header.set_extra_data(extra_data); + + let gas_floor_target = cmp::max(gas_range_target.0, engine.params().min_gas_limit); + let gas_ceil_target = cmp::max(gas_range_target.1, gas_floor_target); + + engine.machine().populate_from_parent( + &mut r.block.header, + parent, + gas_floor_target, + gas_ceil_target, + ); + engine.populate_from_parent(&mut r.block.header, parent); + + engine.machine().on_new_block(&mut r.block)?; + engine.on_new_block(&mut r.block, is_epoch_begin, &mut ancestry.into_iter())?; + + Ok(r) + } + + /// Alter the timestamp of the block. + pub fn set_timestamp(&mut self, timestamp: u64) { + self.block.header.set_timestamp(timestamp); + } + + /// Removes block gas limit. + pub fn remove_gas_limit(&mut self) { + self.block.header.set_gas_limit(U256::max_value()); + } + + /// Add an uncle to the block, if possible. + /// + /// NOTE Will check chain constraints and the uncle number but will NOT check + /// that the header itself is actually valid. + pub fn push_uncle(&mut self, valid_uncle_header: Header) -> Result<(), BlockError> { + let max_uncles = self.engine.maximum_uncle_count(self.block.header.number()); + if self.block.uncles.len() + 1 > max_uncles { + return Err(BlockError::TooManyUncles(OutOfBounds { + min: None, + max: Some(max_uncles), + found: self.block.uncles.len() + 1, + })); + } + // TODO: check number + // TODO: check not a direct ancestor (use last_hashes for that) + self.block.uncles.push(valid_uncle_header); + Ok(()) + } + + /// Push a transaction into the block. + /// + /// If valid, it will be executed, and archived together with the receipt. + pub fn push_transaction( + &mut self, + t: SignedTransaction, + h: Option, + ) -> Result<&Receipt, Error> { + if self.block.transactions_set.contains(&t.hash()) { + return Err(TransactionError::AlreadyImported.into()); + } + + let env_info = self.block.env_info(); + let outcome = self.block.state.apply( + &env_info, + self.engine.machine(), + &t, + self.block.traces.is_enabled(), + )?; + + self.block + .transactions_set + .insert(h.unwrap_or_else(|| t.hash())); + self.block.transactions.push(t.into()); + if let Tracing::Enabled(ref mut traces) = self.block.traces { + traces.push(outcome.trace.into()); + } + self.block.receipts.push(outcome.receipt); + Ok(self + .block + .receipts + .last() + .expect("receipt just pushed; qed")) + } + + /// Push transactions onto the block. + #[cfg(not(feature = "slow-blocks"))] + fn push_transactions(&mut self, transactions: Vec) -> Result<(), Error> { + for t in transactions { + self.push_transaction(t, None)?; + } + Ok(()) + } + + /// Push transactions onto the block. + #[cfg(feature = "slow-blocks")] + fn push_transactions(&mut self, transactions: Vec) -> Result<(), Error> { + use std::time; + + let slow_tx = option_env!("SLOW_TX_DURATION") + .and_then(|v| v.parse().ok()) + .unwrap_or(100); + for t in transactions { + let hash = t.hash(); + let start = time::Instant::now(); + self.push_transaction(t, None)?; + let took = start.elapsed(); + let took_ms = took.as_secs() * 1000 + took.subsec_nanos() as u64 / 1000000; + if took > time::Duration::from_millis(slow_tx) { + warn!( + "Heavy ({} ms) transaction in block {:?}: {:?}", + took_ms, + self.block.header.number(), + hash + ); + } + debug!(target: "tx", "Transaction {:?} took: {} ms", hash, took_ms); + } + + Ok(()) + } + + /// Populate self from a header. + fn populate_from(&mut self, header: &Header) { + self.block.header.set_difficulty(*header.difficulty()); + self.block.header.set_gas_limit(*header.gas_limit()); + self.block.header.set_timestamp(header.timestamp()); + self.block.header.set_uncles_hash(*header.uncles_hash()); + self.block + .header + .set_transactions_root(*header.transactions_root()); + // TODO: that's horrible. set only for backwards compatibility + if header.extra_data().len() > self.engine.maximum_extra_data_size() { + warn!("Couldn't set extradata. Ignoring."); + } else { + self.block + .header + .set_extra_data(header.extra_data().clone()); + } + } + + /// Turn this into a `ClosedBlock`. + pub fn close(self) -> Result { + let unclosed_state = self.block.state.clone(); + let locked = self.close_and_lock()?; + + Ok(ClosedBlock { + block: locked.block, + unclosed_state, + }) + } + + /// Turn this into a `LockedBlock`. + pub fn close_and_lock(self) -> Result { + let mut s = self; + + s.engine.on_close_block(&mut s.block)?; + s.block.state.commit()?; + + s.block.header.set_transactions_root(ordered_trie_root( + s.block.transactions.iter().map(|e| e.rlp_bytes()), + )); + let uncle_bytes = encode_list(&s.block.uncles); + s.block.header.set_uncles_hash(keccak(&uncle_bytes)); + s.block.header.set_state_root(s.block.state.root().clone()); + s.block.header.set_receipts_root(ordered_trie_root( + s.block.receipts.iter().map(|r| r.rlp_bytes()), + )); + s.block + .header + .set_log_bloom(s.block.receipts.iter().fold(Bloom::zero(), |mut b, r| { + b.accrue_bloom(&r.log_bloom); + b + })); + s.block.header.set_gas_used( + s.block + .receipts + .last() + .map_or_else(U256::zero, |r| r.gas_used), + ); + + Ok(LockedBlock { block: s.block }) + } + + #[cfg(test)] + /// Return mutable block reference. To be used in tests only. + pub fn block_mut(&mut self) -> &mut ExecutedBlock { + &mut self.block + } } impl<'a> ops::Deref for OpenBlock<'a> { - type Target = ExecutedBlock; + type Target = ExecutedBlock; - fn deref(&self) -> &Self::Target { - &self.block - } + fn deref(&self) -> &Self::Target { + &self.block + } } impl ops::Deref for ClosedBlock { - type Target = ExecutedBlock; + type Target = ExecutedBlock; - fn deref(&self) -> &Self::Target { - &self.block - } + fn deref(&self) -> &Self::Target { + &self.block + } } impl ops::Deref for LockedBlock { - type Target = ExecutedBlock; + type Target = ExecutedBlock; - fn deref(&self) -> &Self::Target { - &self.block - } + fn deref(&self) -> &Self::Target { + &self.block + } } impl ops::Deref for SealedBlock { - type Target = ExecutedBlock; + type Target = ExecutedBlock; - fn deref(&self) -> &Self::Target { - &self.block - } + fn deref(&self) -> &Self::Target { + &self.block + } } impl ClosedBlock { - /// Turn this into a `LockedBlock`, unable to be reopened again. - pub fn lock(self) -> LockedBlock { - LockedBlock { - block: self.block, - } - } - - /// Given an engine reference, reopen the `ClosedBlock` into an `OpenBlock`. - pub fn reopen(self, engine: &EthEngine) -> OpenBlock { - // revert rewards (i.e. set state back at last transaction's state). - let mut block = self.block; - block.state = self.unclosed_state; - OpenBlock { - block: block, - engine: engine, - } - } + /// Turn this into a `LockedBlock`, unable to be reopened again. + pub fn lock(self) -> LockedBlock { + LockedBlock { block: self.block } + } + + /// Given an engine reference, reopen the `ClosedBlock` into an `OpenBlock`. + pub fn reopen(self, engine: &dyn EthEngine) -> OpenBlock { + // revert rewards (i.e. set state back at last transaction's state). + let mut block = self.block; + block.state = self.unclosed_state; + OpenBlock { + block: block, + engine: engine, + } + } } impl LockedBlock { - /// Removes outcomes from receipts and updates the receipt root. - /// - /// This is done after the block is enacted for historical reasons. - /// We allow inconsistency in receipts for some chains if `validate_receipts_transition` - /// is set to non-zero value, so the check only happens if we detect - /// unmatching root first and then fall back to striped receipts. - pub fn strip_receipts_outcomes(&mut self) { - for receipt in &mut self.block.receipts { - receipt.outcome = TransactionOutcome::Unknown; - } - self.block.header.set_receipts_root( - ordered_trie_root(self.block.receipts.iter().map(|r| r.rlp_bytes())) - ); - } - - /// Provide a valid seal in order to turn this into a `SealedBlock`. - /// - /// NOTE: This does not check the validity of `seal` with the engine. - pub fn seal(self, engine: &EthEngine, seal: Vec) -> Result { - let expected_seal_fields = engine.seal_fields(&self.header); - let mut s = self; - if seal.len() != expected_seal_fields { - Err(BlockError::InvalidSealArity(Mismatch { - expected: expected_seal_fields, - found: seal.len() - }))?; - } - - s.block.header.set_seal(seal); - engine.on_seal_block(&mut s.block)?; - s.block.header.compute_hash(); - - Ok(SealedBlock { - block: s.block - }) - } - - /// Provide a valid seal in order to turn this into a `SealedBlock`. - /// This does check the validity of `seal` with the engine. - /// Returns the `ClosedBlock` back again if the seal is no good. - /// TODO(https://github.com/paritytech/parity-ethereum/issues/10407): This is currently only used in POW chain call paths, we should really merge it with seal() above. - pub fn try_seal( - self, - engine: &EthEngine, - seal: Vec, - ) -> Result { - let mut s = self; - s.block.header.set_seal(seal); - s.block.header.compute_hash(); - - // TODO: passing state context to avoid engines owning it? - engine.verify_local_seal(&s.block.header)?; - Ok(SealedBlock { - block: s.block - }) - } + /// Removes outcomes from receipts and updates the receipt root. + /// + /// This is done after the block is enacted for historical reasons. + /// We allow inconsistency in receipts for some chains if `validate_receipts_transition` + /// is set to non-zero value, so the check only happens if we detect + /// unmatching root first and then fall back to striped receipts. + pub fn strip_receipts_outcomes(&mut self) { + for receipt in &mut self.block.receipts { + receipt.outcome = TransactionOutcome::Unknown; + } + self.block.header.set_receipts_root(ordered_trie_root( + self.block.receipts.iter().map(|r| r.rlp_bytes()), + )); + } + + /// Provide a valid seal in order to turn this into a `SealedBlock`. + /// + /// NOTE: This does not check the validity of `seal` with the engine. + pub fn seal(self, engine: &dyn EthEngine, seal: Vec) -> Result { + let expected_seal_fields = engine.seal_fields(&self.header); + let mut s = self; + if seal.len() != expected_seal_fields { + Err(BlockError::InvalidSealArity(Mismatch { + expected: expected_seal_fields, + found: seal.len(), + }))?; + } + + s.block.header.set_seal(seal); + engine.on_seal_block(&mut s.block)?; + s.block.header.compute_hash(); + + Ok(SealedBlock { block: s.block }) + } + + /// Provide a valid seal in order to turn this into a `SealedBlock`. + /// This does check the validity of `seal` with the engine. + /// Returns the `ClosedBlock` back again if the seal is no good. + /// TODO(https://github.com/openethereum/openethereum/issues/10407): This is currently only used in POW chain call paths, we should really merge it with seal() above. + pub fn try_seal(self, engine: &dyn EthEngine, seal: Vec) -> Result { + let mut s = self; + s.block.header.set_seal(seal); + s.block.header.compute_hash(); + + // TODO: passing state context to avoid engines owning it? + engine.verify_local_seal(&s.block.header)?; + Ok(SealedBlock { block: s.block }) + } } impl Drain for LockedBlock { - fn drain(self) -> ExecutedBlock { - self.block - } + fn drain(self) -> ExecutedBlock { + self.block + } } impl SealedBlock { - /// Get the RLP-encoding of the block. - pub fn rlp_bytes(&self) -> Bytes { - let mut block_rlp = RlpStream::new_list(3); - block_rlp.append(&self.block.header); - block_rlp.append_list(&self.block.transactions); - block_rlp.append_list(&self.block.uncles); - block_rlp.out() - } + /// Get the RLP-encoding of the block. + pub fn rlp_bytes(&self) -> Bytes { + let mut block_rlp = RlpStream::new_list(3); + block_rlp.append(&self.block.header); + block_rlp.append_list(&self.block.transactions); + block_rlp.append_list(&self.block.uncles); + block_rlp.out() + } } impl Drain for SealedBlock { - fn drain(self) -> ExecutedBlock { - self.block - } + fn drain(self) -> ExecutedBlock { + self.block + } } /// Enact the block given by block header, transactions and uncles pub(crate) fn enact( - header: Header, - transactions: Vec, - uncles: Vec
, - engine: &EthEngine, - tracing: bool, - db: StateDB, - parent: &Header, - last_hashes: Arc, - factories: Factories, - is_epoch_begin: bool, - ancestry: &mut Iterator, + header: Header, + transactions: Vec, + uncles: Vec
, + engine: &dyn EthEngine, + tracing: bool, + db: StateDB, + parent: &Header, + last_hashes: Arc, + factories: Factories, + is_epoch_begin: bool, + ancestry: &mut dyn Iterator, ) -> Result { - // For trace log - let trace_state = if log_enabled!(target: "enact", ::log::Level::Trace) { - Some(State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce(parent.number() + 1), factories.clone())?) - } else { - None - }; - - let mut b = OpenBlock::new( - engine, - factories, - tracing, - db, - parent, - last_hashes, - // Engine such as Clique will calculate author from extra_data. - // this is only important for executing contracts as the 'executive_author'. - engine.executive_author(&header)?, - (3141562.into(), 31415620.into()), - vec![], - is_epoch_begin, - ancestry, - )?; - - if let Some(ref s) = trace_state { - let env = b.env_info(); - let root = s.root(); - let author_balance = s.balance(&env.author)?; - trace!(target: "enact", "num={}, root={}, author={}, author_balance={}\n", + // For trace log + let trace_state = if log_enabled!(target: "enact", ::log::Level::Trace) { + Some(State::from_existing( + db.boxed_clone(), + parent.state_root().clone(), + engine.account_start_nonce(parent.number() + 1), + factories.clone(), + )?) + } else { + None + }; + + let mut b = OpenBlock::new( + engine, + factories, + tracing, + db, + parent, + last_hashes, + // Engine such as Clique will calculate author from extra_data. + // this is only important for executing contracts as the 'executive_author'. + engine.executive_author(&header)?, + (3141562.into(), 31415620.into()), + vec![], + is_epoch_begin, + ancestry, + )?; + + if let Some(ref s) = trace_state { + let env = b.env_info(); + let root = s.root(); + let author_balance = s.balance(&env.author)?; + trace!(target: "enact", "num={}, root={}, author={}, author_balance={}\n", b.block.header.number(), root, env.author, author_balance); - } + } - b.populate_from(&header); - b.push_transactions(transactions)?; + b.populate_from(&header); + b.push_transactions(transactions)?; - for u in uncles { - b.push_uncle(u)?; - } + for u in uncles { + b.push_uncle(u)?; + } - b.close_and_lock() + b.close_and_lock() } /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header pub fn enact_verified( - block: PreverifiedBlock, - engine: &EthEngine, - tracing: bool, - db: StateDB, - parent: &Header, - last_hashes: Arc, - factories: Factories, - is_epoch_begin: bool, - ancestry: &mut Iterator, + block: PreverifiedBlock, + engine: &dyn EthEngine, + tracing: bool, + db: StateDB, + parent: &Header, + last_hashes: Arc, + factories: Factories, + is_epoch_begin: bool, + ancestry: &mut dyn Iterator, ) -> Result { - - enact( - block.header, - block.transactions, - block.uncles, - engine, - tracing, - db, - parent, - last_hashes, - factories, - is_epoch_begin, - ancestry, - ) + enact( + block.header, + block.transactions, + block.uncles, + engine, + tracing, + db, + parent, + last_hashes, + factories, + is_epoch_begin, + ancestry, + ) } #[cfg(test)] mod tests { - use test_helpers::get_temp_state_db; - use super::*; - use engines::EthEngine; - use vm::LastHashes; - use error::Error; - use factory::Factories; - use state_db::StateDB; - use ethereum_types::Address; - use std::sync::Arc; - use verification::queue::kind::blocks::Unverified; - use types::transaction::SignedTransaction; - use types::header::Header; - use types::view; - use types::views::BlockView; - - /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header - fn enact_bytes( - block_bytes: Vec, - engine: &EthEngine, - tracing: bool, - db: StateDB, - parent: &Header, - last_hashes: Arc, - factories: Factories, - ) -> Result { - - let block = Unverified::from_rlp(block_bytes)?; - let header = block.header; - let transactions: Result, Error> = block - .transactions - .into_iter() - .map(SignedTransaction::new) - .map(|r| r.map_err(Into::into)) - .collect(); - let transactions = transactions?; - - { - if ::log::max_level() >= ::log::Level::Trace { - let s = State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce(parent.number() + 1), factories.clone())?; - trace!(target: "enact", "num={}, root={}, author={}, author_balance={}\n", + use super::*; + use engines::EthEngine; + use error::Error; + use ethereum_types::Address; + use factory::Factories; + use state_db::StateDB; + use std::sync::Arc; + use test_helpers::get_temp_state_db; + use types::{header::Header, transaction::SignedTransaction, view, views::BlockView}; + use verification::queue::kind::blocks::Unverified; + use vm::LastHashes; + + /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header + fn enact_bytes( + block_bytes: Vec, + engine: &dyn EthEngine, + tracing: bool, + db: StateDB, + parent: &Header, + last_hashes: Arc, + factories: Factories, + ) -> Result { + let block = Unverified::from_rlp(block_bytes)?; + let header = block.header; + let transactions: Result, Error> = block + .transactions + .into_iter() + .map(SignedTransaction::new) + .map(|r| r.map_err(Into::into)) + .collect(); + let transactions = transactions?; + + { + if ::log::max_level() >= ::log::Level::Trace { + let s = State::from_existing( + db.boxed_clone(), + parent.state_root().clone(), + engine.account_start_nonce(parent.number() + 1), + factories.clone(), + )?; + trace!(target: "enact", "num={}, root={}, author={}, author_balance={}\n", header.number(), s.root(), header.author(), s.balance(&header.author())?); - } - } - - let mut b = OpenBlock::new( - engine, - factories, - tracing, - db, - parent, - last_hashes, - Address::new(), - (3141562.into(), 31415620.into()), - vec![], - false, - None, - )?; - - b.populate_from(&header); - b.push_transactions(transactions)?; - - for u in block.uncles { - b.push_uncle(u)?; - } - - b.close_and_lock() - } - - /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards - fn enact_and_seal( - block_bytes: Vec, - engine: &EthEngine, - tracing: bool, - db: StateDB, - parent: &Header, - last_hashes: Arc, - factories: Factories, - ) -> Result { - let header = Unverified::from_rlp(block_bytes.clone())?.header; - Ok(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, factories)? - .seal(engine, header.seal().to_vec())?) - } - - #[test] - fn open_block() { - use spec::*; - let spec = Spec::new_test(); - let genesis_header = spec.genesis_header(); - let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b = OpenBlock::new(&*spec.engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); - let b = b.close_and_lock().unwrap(); - let _ = b.seal(&*spec.engine, vec![]); - } - - #[test] - fn enact_block() { - use spec::*; - let spec = Spec::new_test(); - let engine = &*spec.engine; - let genesis_header = spec.genesis_header(); - - let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![], false, None).unwrap() - .close_and_lock().unwrap().seal(engine, vec![]).unwrap(); - let orig_bytes = b.rlp_bytes(); - let orig_db = b.drain().state.drop().1; - - let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let e = enact_and_seal(orig_bytes.clone(), engine, false, db, &genesis_header, last_hashes, Default::default()).unwrap(); - - assert_eq!(e.rlp_bytes(), orig_bytes); - - let db = e.drain().state.drop().1; - assert_eq!(orig_db.journal_db().keys(), db.journal_db().keys()); - assert!(orig_db.journal_db().keys().iter().filter(|k| orig_db.journal_db().get(k.0) != db.journal_db().get(k.0)).next() == None); - } - - #[test] - fn enact_block_with_uncle() { - use spec::*; - let spec = Spec::new_test(); - let engine = &*spec.engine; - let genesis_header = spec.genesis_header(); - - let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let last_hashes = Arc::new(vec![genesis_header.hash()]); - let mut open_block = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); - let mut uncle1_header = Header::new(); - uncle1_header.set_extra_data(b"uncle1".to_vec()); - let mut uncle2_header = Header::new(); - uncle2_header.set_extra_data(b"uncle2".to_vec()); - open_block.push_uncle(uncle1_header).unwrap(); - open_block.push_uncle(uncle2_header).unwrap(); - let b = open_block.close_and_lock().unwrap().seal(engine, vec![]).unwrap(); - - let orig_bytes = b.rlp_bytes(); - let orig_db = b.drain().state.drop().1; - - let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let e = enact_and_seal(orig_bytes.clone(), engine, false, db, &genesis_header, last_hashes, Default::default()).unwrap(); - - let bytes = e.rlp_bytes(); - assert_eq!(bytes, orig_bytes); - let uncles = view!(BlockView, &bytes).uncles(); - assert_eq!(uncles[1].extra_data(), b"uncle2"); - - let db = e.drain().state.drop().1; - assert_eq!(orig_db.journal_db().keys(), db.journal_db().keys()); - assert!(orig_db.journal_db().keys().iter().filter(|k| orig_db.journal_db().get(k.0) != db.journal_db().get(k.0)).next() == None); - } + } + } + + let mut b = OpenBlock::new( + engine, + factories, + tracing, + db, + parent, + last_hashes, + Address::new(), + (3141562.into(), 31415620.into()), + vec![], + false, + None, + )?; + + b.populate_from(&header); + b.push_transactions(transactions)?; + + for u in block.uncles { + b.push_uncle(u)?; + } + + b.close_and_lock() + } + + /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards + fn enact_and_seal( + block_bytes: Vec, + engine: &dyn EthEngine, + tracing: bool, + db: StateDB, + parent: &Header, + last_hashes: Arc, + factories: Factories, + ) -> Result { + let header = Unverified::from_rlp(block_bytes.clone())?.header; + Ok(enact_bytes( + block_bytes, + engine, + tracing, + db, + parent, + last_hashes, + factories, + )? + .seal(engine, header.seal().to_vec())?) + } + + #[test] + fn open_block() { + use spec::*; + let spec = Spec::new_test(); + let genesis_header = spec.genesis_header(); + let db = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let last_hashes = Arc::new(vec![genesis_header.hash()]); + let b = OpenBlock::new( + &*spec.engine, + Default::default(), + false, + db, + &genesis_header, + last_hashes, + Address::zero(), + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + let b = b.close_and_lock().unwrap(); + let _ = b.seal(&*spec.engine, vec![]); + } + + #[test] + fn enact_block() { + use spec::*; + let spec = Spec::new_test(); + let engine = &*spec.engine; + let genesis_header = spec.genesis_header(); + + let db = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let last_hashes = Arc::new(vec![genesis_header.hash()]); + let b = OpenBlock::new( + engine, + Default::default(), + false, + db, + &genesis_header, + last_hashes.clone(), + Address::zero(), + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap() + .close_and_lock() + .unwrap() + .seal(engine, vec![]) + .unwrap(); + let orig_bytes = b.rlp_bytes(); + let orig_db = b.drain().state.drop().1; + + let db = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let e = enact_and_seal( + orig_bytes.clone(), + engine, + false, + db, + &genesis_header, + last_hashes, + Default::default(), + ) + .unwrap(); + + assert_eq!(e.rlp_bytes(), orig_bytes); + + let db = e.drain().state.drop().1; + assert_eq!(orig_db.journal_db().keys(), db.journal_db().keys()); + assert!( + orig_db + .journal_db() + .keys() + .iter() + .filter(|k| orig_db.journal_db().get(k.0) != db.journal_db().get(k.0)) + .next() + == None + ); + } + + #[test] + fn enact_block_with_uncle() { + use spec::*; + let spec = Spec::new_test(); + let engine = &*spec.engine; + let genesis_header = spec.genesis_header(); + + let db = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let last_hashes = Arc::new(vec![genesis_header.hash()]); + let mut open_block = OpenBlock::new( + engine, + Default::default(), + false, + db, + &genesis_header, + last_hashes.clone(), + Address::zero(), + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + let mut uncle1_header = Header::new(); + uncle1_header.set_extra_data(b"uncle1".to_vec()); + let mut uncle2_header = Header::new(); + uncle2_header.set_extra_data(b"uncle2".to_vec()); + open_block.push_uncle(uncle1_header).unwrap(); + open_block.push_uncle(uncle2_header).unwrap(); + let b = open_block + .close_and_lock() + .unwrap() + .seal(engine, vec![]) + .unwrap(); + + let orig_bytes = b.rlp_bytes(); + let orig_db = b.drain().state.drop().1; + + let db = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let e = enact_and_seal( + orig_bytes.clone(), + engine, + false, + db, + &genesis_header, + last_hashes, + Default::default(), + ) + .unwrap(); + + let bytes = e.rlp_bytes(); + assert_eq!(bytes, orig_bytes); + let uncles = view!(BlockView, &bytes).uncles(); + assert_eq!(uncles[1].extra_data(), b"uncle2"); + + let db = e.drain().state.drop().1; + assert_eq!(orig_db.journal_db().keys(), db.journal_db().keys()); + assert!( + orig_db + .journal_db() + .keys() + .iter() + .filter(|k| orig_db.journal_db().get(k.0) != db.journal_db().get(k.0)) + .next() + == None + ); + } } diff --git a/ethcore/src/builtin.rs b/ethcore/src/builtin.rs deleted file mode 100644 index 499e3c8f6cc..00000000000 --- a/ethcore/src/builtin.rs +++ /dev/null @@ -1,1062 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Standard built-in contracts. - -use std::cmp::{max, min}; -use std::io::{self, Read}; - -use byteorder::{ByteOrder, BigEndian}; -use parity_crypto::digest; -use num::{BigUint, Zero, One}; - -use hash::keccak; -use ethereum_types::{H256, U256}; -use bytes::BytesRef; -use ethkey::{Signature, recover as ec_recover}; -use ethjson; - -/// Execution error. -#[derive(Debug)] -pub struct Error(pub &'static str); - -impl From<&'static str> for Error { - fn from(val: &'static str) -> Self { - Error(val) - } -} - -impl Into<::vm::Error> for Error { - fn into(self) -> ::vm::Error { - ::vm::Error::BuiltIn(self.0) - } -} - -/// Native implementation of a built-in contract. -pub trait Impl: Send + Sync { - /// execute this built-in on the given input, writing to the given output. - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error>; -} - -/// A gas pricing scheme for built-in contracts. -pub trait Pricer: Send + Sync { - /// The gas cost of running this built-in for the given input data. - fn cost(&self, input: &[u8]) -> U256; -} - -/// A linear pricing model. This computes a price using a base cost and a cost per-word. -struct Linear { - base: usize, - word: usize, -} - -/// A special pricing model for modular exponentiation. -struct ModexpPricer { - divisor: usize, -} - -impl Pricer for Linear { - fn cost(&self, input: &[u8]) -> U256 { - U256::from(self.base) + U256::from(self.word) * U256::from((input.len() + 31) / 32) - } -} - -/// A alt_bn128_parinig pricing model. This computes a price using a base cost and a cost per pair. -struct AltBn128PairingPricer { - base: usize, - pair: usize, -} - -impl Pricer for AltBn128PairingPricer { - fn cost(&self, input: &[u8]) -> U256 { - let cost = U256::from(self.base) + U256::from(self.pair) * U256::from(input.len() / 192); - cost - } -} - -impl Pricer for ModexpPricer { - fn cost(&self, input: &[u8]) -> U256 { - let mut reader = input.chain(io::repeat(0)); - let mut buf = [0; 32]; - - // read lengths as U256 here for accurate gas calculation. - let mut read_len = || { - reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed"); - U256::from(H256::from_slice(&buf[..])) - }; - let base_len = read_len(); - let exp_len = read_len(); - let mod_len = read_len(); - - if mod_len.is_zero() && base_len.is_zero() { - return U256::zero() - } - - let max_len = U256::from(u32::max_value() / 2); - if base_len > max_len || mod_len > max_len || exp_len > max_len { - return U256::max_value(); - } - let (base_len, exp_len, mod_len) = (base_len.low_u64(), exp_len.low_u64(), mod_len.low_u64()); - - let m = max(mod_len, base_len); - // read fist 32-byte word of the exponent. - let exp_low = if base_len + 96 >= input.len() as u64 { U256::zero() } else { - let mut buf = [0; 32]; - let mut reader = input[(96 + base_len as usize)..].chain(io::repeat(0)); - let len = min(exp_len, 32) as usize; - reader.read_exact(&mut buf[(32 - len)..]).expect("reading from zero-extended memory cannot fail; qed"); - U256::from(H256::from_slice(&buf[..])) - }; - - let adjusted_exp_len = Self::adjusted_exp_len(exp_len, exp_low); - - let (gas, overflow) = Self::mult_complexity(m).overflowing_mul(max(adjusted_exp_len, 1)); - if overflow { - return U256::max_value(); - } - (gas / self.divisor as u64).into() - } -} - -impl ModexpPricer { - fn adjusted_exp_len(len: u64, exp_low: U256) -> u64 { - let bit_index = if exp_low.is_zero() { 0 } else { (255 - exp_low.leading_zeros()) as u64 }; - if len <= 32 { - bit_index - } else { - 8 * (len - 32) + bit_index - } - } - - fn mult_complexity(x: u64) -> u64 { - match x { - x if x <= 64 => x * x, - x if x <= 1024 => (x * x) / 4 + 96 * x - 3072, - x => (x * x) / 16 + 480 * x - 199680, - } - } -} - -/// Pricing scheme, execution definition, and activation block for a built-in contract. -/// -/// Call `cost` to compute cost for the given input, `execute` to execute the contract -/// on the given input, and `is_active` to determine whether the contract is active. -/// -/// Unless `is_active` is true, -pub struct Builtin { - pricer: Box, - native: Box, - activate_at: u64, -} - -impl Builtin { - /// Simple forwarder for cost. - pub fn cost(&self, input: &[u8]) -> U256 { self.pricer.cost(input) } - - /// Simple forwarder for execute. - pub fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { - self.native.execute(input, output) - } - - /// Whether the builtin is activated at the given block number. - pub fn is_active(&self, at: u64) -> bool { at >= self.activate_at } -} - -impl From for Builtin { - fn from(b: ethjson::spec::Builtin) -> Self { - let pricer: Box = match b.pricing { - ethjson::spec::Pricing::Linear(linear) => { - Box::new(Linear { - base: linear.base, - word: linear.word, - }) - } - ethjson::spec::Pricing::Modexp(exp) => { - Box::new(ModexpPricer { - divisor: if exp.divisor == 0 { - warn!("Zero modexp divisor specified. Falling back to default."); - 10 - } else { - exp.divisor - } - }) - } - ethjson::spec::Pricing::AltBn128Pairing(pricer) => { - Box::new(AltBn128PairingPricer { - base: pricer.base, - pair: pricer.pair, - }) - } - }; - - Builtin { - pricer: pricer, - native: ethereum_builtin(&b.name), - activate_at: b.activate_at.map(Into::into).unwrap_or(0), - } - } -} - -/// Ethereum built-in factory. -pub fn ethereum_builtin(name: &str) -> Box { - match name { - "identity" => Box::new(Identity) as Box, - "ecrecover" => Box::new(EcRecover) as Box, - "sha256" => Box::new(Sha256) as Box, - "ripemd160" => Box::new(Ripemd160) as Box, - "modexp" => Box::new(ModexpImpl) as Box, - "alt_bn128_add" => Box::new(Bn128AddImpl) as Box, - "alt_bn128_mul" => Box::new(Bn128MulImpl) as Box, - "alt_bn128_pairing" => Box::new(Bn128PairingImpl) as Box, - _ => panic!("invalid builtin name: {}", name), - } -} - -// Ethereum builtins: -// -// - The identity function -// - ec recovery -// - sha256 -// - ripemd160 -// - modexp (EIP198) - -#[derive(Debug)] -struct Identity; - -#[derive(Debug)] -struct EcRecover; - -#[derive(Debug)] -struct Sha256; - -#[derive(Debug)] -struct Ripemd160; - -#[derive(Debug)] -struct ModexpImpl; - -#[derive(Debug)] -struct Bn128AddImpl; - -#[derive(Debug)] -struct Bn128MulImpl; - -#[derive(Debug)] -struct Bn128PairingImpl; - -impl Impl for Identity { - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { - output.write(0, input); - Ok(()) - } -} - -impl Impl for EcRecover { - fn execute(&self, i: &[u8], output: &mut BytesRef) -> Result<(), Error> { - let len = min(i.len(), 128); - - let mut input = [0; 128]; - input[..len].copy_from_slice(&i[..len]); - - let hash = H256::from_slice(&input[0..32]); - let v = H256::from_slice(&input[32..64]); - let r = H256::from_slice(&input[64..96]); - let s = H256::from_slice(&input[96..128]); - - let bit = match v[31] { - 27 | 28 if &v.0[..31] == &[0; 31] => v[31] - 27, - _ => { return Ok(()); }, - }; - - let s = Signature::from_rsv(&r, &s, bit); - if s.is_valid() { - if let Ok(p) = ec_recover(&s, &hash) { - let r = keccak(p); - output.write(0, &[0; 12]); - output.write(12, &r[12..r.len()]); - } - } - - Ok(()) - } -} - -impl Impl for Sha256 { - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { - let d = digest::sha256(input); - output.write(0, &*d); - Ok(()) - } -} - -impl Impl for Ripemd160 { - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { - let hash = digest::ripemd160(input); - output.write(0, &[0; 12][..]); - output.write(12, &hash); - Ok(()) - } -} - -// calculate modexp: left-to-right binary exponentiation to keep multiplicands lower -fn modexp(mut base: BigUint, exp: Vec, modulus: BigUint) -> BigUint { - const BITS_PER_DIGIT: usize = 8; - - // n^m % 0 || n^m % 1 - if modulus <= BigUint::one() { - return BigUint::zero(); - } - - // normalize exponent - let mut exp = exp.into_iter().skip_while(|d| *d == 0).peekable(); - - // n^0 % m - if let None = exp.peek() { - return BigUint::one(); - } - - // 0^n % m, n > 0 - if base.is_zero() { - return BigUint::zero(); - } - - base = base % &modulus; - - // Fast path for base divisible by modulus. - if base.is_zero() { return BigUint::zero() } - - // Left-to-right binary exponentiation (Handbook of Applied Cryptography - Algorithm 14.79). - // http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf - let mut result = BigUint::one(); - - for digit in exp { - let mut mask = 1 << (BITS_PER_DIGIT - 1); - - for _ in 0..BITS_PER_DIGIT { - result = &result * &result % &modulus; - - if digit & mask > 0 { - result = result * &base % &modulus; - } - - mask >>= 1; - } - } - - result -} - -impl Impl for ModexpImpl { - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { - let mut reader = input.chain(io::repeat(0)); - let mut buf = [0; 32]; - - // read lengths as usize. - // ignoring the first 24 bytes might technically lead us to fall out of consensus, - // but so would running out of addressable memory! - let mut read_len = |reader: &mut io::Chain<&[u8], io::Repeat>| { - reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed"); - BigEndian::read_u64(&buf[24..]) as usize - }; - - let base_len = read_len(&mut reader); - let exp_len = read_len(&mut reader); - let mod_len = read_len(&mut reader); - - // Gas formula allows arbitrary large exp_len when base and modulus are empty, so we need to handle empty base first. - let r = if base_len == 0 && mod_len == 0 { - BigUint::zero() - } else { - // read the numbers themselves. - let mut buf = vec![0; max(mod_len, max(base_len, exp_len))]; - let mut read_num = |reader: &mut io::Chain<&[u8], io::Repeat>, len: usize| { - reader.read_exact(&mut buf[..len]).expect("reading from zero-extended memory cannot fail; qed"); - BigUint::from_bytes_be(&buf[..len]) - }; - - let base = read_num(&mut reader, base_len); - - let mut exp_buf = vec![0; exp_len]; - reader.read_exact(&mut exp_buf[..exp_len]).expect("reading from zero-extended memory cannot fail; qed"); - - let modulus = read_num(&mut reader, mod_len); - - modexp(base, exp_buf, modulus) - }; - - // 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. - if bytes.len() <= mod_len { - let res_start = mod_len - bytes.len(); - output.write(res_start, &bytes); - } - - Ok(()) - } -} - -fn read_fr(reader: &mut io::Chain<&[u8], io::Repeat>) -> Result<::bn::Fr, Error> { - let mut buf = [0u8; 32]; - - reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed"); - ::bn::Fr::from_slice(&buf[0..32]).map_err(|_| Error::from("Invalid field element")) -} - -fn read_point(reader: &mut io::Chain<&[u8], io::Repeat>) -> Result<::bn::G1, Error> { - use bn::{Fq, AffineG1, G1, Group}; - - let mut buf = [0u8; 32]; - - reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed"); - let px = Fq::from_slice(&buf[0..32]).map_err(|_| Error::from("Invalid point x coordinate"))?; - - reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed"); - let py = Fq::from_slice(&buf[0..32]).map_err(|_| Error::from("Invalid point y coordinate"))?; - Ok( - if px == Fq::zero() && py == Fq::zero() { - G1::zero() - } else { - AffineG1::new(px, py).map_err(|_| Error::from("Invalid curve point"))?.into() - } - ) -} - -impl Impl for Bn128AddImpl { - // Can fail if any of the 2 points does not belong the bn128 curve - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { - use bn::AffineG1; - - let mut padded_input = input.chain(io::repeat(0)); - let p1 = read_point(&mut padded_input)?; - let p2 = read_point(&mut padded_input)?; - - let mut write_buf = [0u8; 64]; - if let Some(sum) = AffineG1::from_jacobian(p1 + p2) { - // point not at infinity - sum.x().to_big_endian(&mut write_buf[0..32]).expect("Cannot fail since 0..32 is 32-byte length"); - sum.y().to_big_endian(&mut write_buf[32..64]).expect("Cannot fail since 32..64 is 32-byte length");; - } - output.write(0, &write_buf); - - Ok(()) - } -} - -impl Impl for Bn128MulImpl { - // Can fail if first paramter (bn128 curve point) does not actually belong to the curve - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { - use bn::AffineG1; - - let mut padded_input = input.chain(io::repeat(0)); - let p = read_point(&mut padded_input)?; - let fr = read_fr(&mut padded_input)?; - - let mut write_buf = [0u8; 64]; - if let Some(sum) = AffineG1::from_jacobian(p * fr) { - // point not at infinity - sum.x().to_big_endian(&mut write_buf[0..32]).expect("Cannot fail since 0..32 is 32-byte length"); - sum.y().to_big_endian(&mut write_buf[32..64]).expect("Cannot fail since 32..64 is 32-byte length");; - } - output.write(0, &write_buf); - Ok(()) - } -} - -impl Impl for Bn128PairingImpl { - /// Can fail if: - /// - input length is not a multiple of 192 - /// - any of odd points does not belong to bn128 curve - /// - any of even points does not belong to the twisted bn128 curve over the field F_p^2 = F_p[i] / (i^2 + 1) - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { - if input.len() % 192 != 0 { - return Err("Invalid input length, must be multiple of 192 (3 * (32*2))".into()) - } - - if let Err(err) = self.execute_with_error(input, output) { - trace!("Pairining error: {:?}", err); - return Err(err) - } - Ok(()) - } -} - -impl Bn128PairingImpl { - fn execute_with_error(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { - use bn::{AffineG1, AffineG2, Fq, Fq2, pairing, G1, G2, Gt, Group}; - - let elements = input.len() / 192; // (a, b_a, b_b - each 64-byte affine coordinates) - let ret_val = if input.len() == 0 { - U256::one() - } else { - let mut vals = Vec::new(); - for idx in 0..elements { - let a_x = Fq::from_slice(&input[idx*192..idx*192+32]) - .map_err(|_| Error::from("Invalid a argument x coordinate"))?; - - let a_y = Fq::from_slice(&input[idx*192+32..idx*192+64]) - .map_err(|_| Error::from("Invalid a argument y coordinate"))?; - - let b_a_y = Fq::from_slice(&input[idx*192+64..idx*192+96]) - .map_err(|_| Error::from("Invalid b argument imaginary coeff x coordinate"))?; - - let b_a_x = Fq::from_slice(&input[idx*192+96..idx*192+128]) - .map_err(|_| Error::from("Invalid b argument imaginary coeff y coordinate"))?; - - let b_b_y = Fq::from_slice(&input[idx*192+128..idx*192+160]) - .map_err(|_| Error::from("Invalid b argument real coeff x coordinate"))?; - - let b_b_x = Fq::from_slice(&input[idx*192+160..idx*192+192]) - .map_err(|_| Error::from("Invalid b argument real coeff y coordinate"))?; - - let b_a = Fq2::new(b_a_x, b_a_y); - let b_b = Fq2::new(b_b_x, b_b_y); - let b = if b_a.is_zero() && b_b.is_zero() { - G2::zero() - } else { - G2::from(AffineG2::new(b_a, b_b).map_err(|_| Error::from("Invalid b argument - not on curve"))?) - }; - let a = if a_x.is_zero() && a_y.is_zero() { - G1::zero() - } else { - G1::from(AffineG1::new(a_x, a_y).map_err(|_| Error::from("Invalid a argument - not on curve"))?) - }; - vals.push((a, b)); - }; - - let mul = vals.into_iter().fold(Gt::one(), |s, (a, b)| s * pairing(a, b)); - - if mul == Gt::one() { - U256::one() - } else { - U256::zero() - } - }; - - let mut buf = [0u8; 32]; - ret_val.to_big_endian(&mut buf); - output.write(0, &buf); - - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::{Builtin, Linear, ethereum_builtin, Pricer, ModexpPricer, modexp as me}; - use ethjson; - use ethereum_types::U256; - use bytes::BytesRef; - use rustc_hex::FromHex; - use num::{BigUint, Zero, One}; - - #[test] - fn modexp_func() { - // n^0 % m == 1 - let mut base = BigUint::parse_bytes(b"12345", 10).unwrap(); - let mut exp = BigUint::zero(); - let mut modulus = BigUint::parse_bytes(b"789", 10).unwrap(); - assert_eq!(me(base, exp.to_bytes_be(), modulus), BigUint::one()); - - // 0^n % m == 0 - base = BigUint::zero(); - exp = BigUint::parse_bytes(b"12345", 10).unwrap(); - modulus = BigUint::parse_bytes(b"789", 10).unwrap(); - assert_eq!(me(base, exp.to_bytes_be(), modulus), BigUint::zero()); - - // n^m % 1 == 0 - base = BigUint::parse_bytes(b"12345", 10).unwrap(); - exp = BigUint::parse_bytes(b"789", 10).unwrap(); - modulus = BigUint::one(); - assert_eq!(me(base, exp.to_bytes_be(), modulus), BigUint::zero()); - - // if n % d == 0, then n^m % d == 0 - base = BigUint::parse_bytes(b"12345", 10).unwrap(); - exp = BigUint::parse_bytes(b"789", 10).unwrap(); - modulus = BigUint::parse_bytes(b"15", 10).unwrap(); - assert_eq!(me(base, exp.to_bytes_be(), modulus), BigUint::zero()); - - // others - base = BigUint::parse_bytes(b"12345", 10).unwrap(); - exp = BigUint::parse_bytes(b"789", 10).unwrap(); - modulus = BigUint::parse_bytes(b"97", 10).unwrap(); - assert_eq!(me(base, exp.to_bytes_be(), modulus), BigUint::parse_bytes(b"55", 10).unwrap()); - } - - #[test] - fn identity() { - let f = ethereum_builtin("identity"); - - let i = [0u8, 1, 2, 3]; - - let mut o2 = [255u8; 2]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o2[..])).expect("Builtin should not fail"); - assert_eq!(i[0..2], o2); - - let mut o4 = [255u8; 4]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o4[..])).expect("Builtin should not fail"); - assert_eq!(i, o4); - - let mut o8 = [255u8; 8]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])).expect("Builtin should not fail"); - assert_eq!(i, o8[..4]); - assert_eq!([255u8; 4], o8[4..]); - } - - #[test] - fn sha256() { - let f = ethereum_builtin("sha256"); - - let i = [0u8; 0]; - - let mut o = [255u8; 32]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); - assert_eq!(&o[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855").unwrap())[..]); - - let mut o8 = [255u8; 8]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])).expect("Builtin should not fail"); - assert_eq!(&o8[..], &(FromHex::from_hex("e3b0c44298fc1c14").unwrap())[..]); - - let mut o34 = [255u8; 34]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..])).expect("Builtin should not fail"); - assert_eq!(&o34[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855ffff").unwrap())[..]); - - let mut ov = vec![]; - f.execute(&i[..], &mut BytesRef::Flexible(&mut ov)).expect("Builtin should not fail"); - assert_eq!(&ov[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855").unwrap())[..]); - } - - #[test] - fn ripemd160() { - let f = ethereum_builtin("ripemd160"); - - let i = [0u8; 0]; - - let mut o = [255u8; 32]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); - assert_eq!(&o[..], &(FromHex::from_hex("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31").unwrap())[..]); - - let mut o8 = [255u8; 8]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])).expect("Builtin should not fail"); - assert_eq!(&o8[..], &(FromHex::from_hex("0000000000000000").unwrap())[..]); - - let mut o34 = [255u8; 34]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..])).expect("Builtin should not fail"); - assert_eq!(&o34[..], &(FromHex::from_hex("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31ffff").unwrap())[..]); - } - - #[test] - fn ecrecover() { - let f = ethereum_builtin("ecrecover"); - - let i = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap(); - - let mut o = [255u8; 32]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); - assert_eq!(&o[..], &(FromHex::from_hex("000000000000000000000000c08b5542d177ac6686946920409741463a15dddb").unwrap())[..]); - - let mut o8 = [255u8; 8]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])).expect("Builtin should not fail"); - assert_eq!(&o8[..], &(FromHex::from_hex("0000000000000000").unwrap())[..]); - - let mut o34 = [255u8; 34]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..])).expect("Builtin should not fail"); - assert_eq!(&o34[..], &(FromHex::from_hex("000000000000000000000000c08b5542d177ac6686946920409741463a15dddbffff").unwrap())[..]); - - let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001a650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap(); - let mut o = [255u8; 32]; - f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); - assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]); - - let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000").unwrap(); - let mut o = [255u8; 32]; - f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); - assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]); - - let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b").unwrap(); - let mut o = [255u8; 32]; - f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); - assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]); - - let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000001b").unwrap(); - let mut o = [255u8; 32]; - f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); - assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]); - - let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap(); - let mut o = [255u8; 32]; - f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); - assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]); - - // TODO: Should this (corrupted version of the above) fail rather than returning some address? - /* let i_bad = FromHex::from_hex("48173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap(); - let mut o = [255u8; 32]; - f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])); - assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);*/ - } - - #[test] - fn modexp() { - - let f = Builtin { - pricer: Box::new(ModexpPricer { divisor: 20 }), - native: ethereum_builtin("modexp"), - activate_at: 0, - }; - - // test for potential gas cost multiplication overflow - { - let input = FromHex::from_hex("0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000003b27bafd00000000000000000000000000000000000000000000000000000000503c8ac3").unwrap(); - let expected_cost = U256::max_value(); - assert_eq!(f.cost(&input[..]), expected_cost.into()); - } - - // test for potential exp len overflow - { - let input = FromHex::from_hex("\ - 00000000000000000000000000000000000000000000000000000000000000ff\ - 2a1e530000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000" - ).unwrap(); - - let mut output = vec![0u8; 32]; - let expected = FromHex::from_hex("0000000000000000000000000000000000000000000000000000000000000000").unwrap(); - let expected_cost = U256::max_value(); - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should fail"); - assert_eq!(output, expected); - assert_eq!(f.cost(&input[..]), expected_cost.into()); - } - - // fermat's little theorem example. - { - let input = FromHex::from_hex("\ - 0000000000000000000000000000000000000000000000000000000000000001\ - 0000000000000000000000000000000000000000000000000000000000000020\ - 0000000000000000000000000000000000000000000000000000000000000020\ - 03\ - fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e\ - fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" - ).unwrap(); - - let mut output = vec![0u8; 32]; - let expected = FromHex::from_hex("0000000000000000000000000000000000000000000000000000000000000001").unwrap(); - let expected_cost = 13056; - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); - assert_eq!(output, expected); - assert_eq!(f.cost(&input[..]), expected_cost.into()); - } - - // second example from EIP: zero base. - { - let input = FromHex::from_hex("\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000020\ - 0000000000000000000000000000000000000000000000000000000000000020\ - fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e\ - fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" - ).unwrap(); - - let mut output = vec![0u8; 32]; - let expected = FromHex::from_hex("0000000000000000000000000000000000000000000000000000000000000000").unwrap(); - let expected_cost = 13056; - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); - assert_eq!(output, expected); - assert_eq!(f.cost(&input[..]), expected_cost.into()); - } - - // another example from EIP: zero-padding - { - let input = FromHex::from_hex("\ - 0000000000000000000000000000000000000000000000000000000000000001\ - 0000000000000000000000000000000000000000000000000000000000000002\ - 0000000000000000000000000000000000000000000000000000000000000020\ - 03\ - ffff\ - 80" - ).unwrap(); - - let mut output = vec![0u8; 32]; - let expected = FromHex::from_hex("3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab").unwrap(); - let expected_cost = 768; - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); - assert_eq!(output, expected); - assert_eq!(f.cost(&input[..]), expected_cost.into()); - } - - // zero-length modulus. - { - let input = FromHex::from_hex("\ - 0000000000000000000000000000000000000000000000000000000000000001\ - 0000000000000000000000000000000000000000000000000000000000000002\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 03\ - ffff" - ).unwrap(); - - let mut output = vec![]; - let expected_cost = 0; - - f.execute(&input[..], &mut BytesRef::Flexible(&mut output)).expect("Builtin should not fail"); - assert_eq!(output.len(), 0); // shouldn't have written any output. - assert_eq!(f.cost(&input[..]), expected_cost.into()); - } - } - - #[test] - fn bn128_add() { - - let f = Builtin { - pricer: Box::new(Linear { base: 0, word: 0 }), - native: ethereum_builtin("alt_bn128_add"), - activate_at: 0, - }; - - // zero-points additions - { - let input = FromHex::from_hex("\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000" - ).unwrap(); - - let mut output = vec![0u8; 64]; - let expected = FromHex::from_hex("\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000" - ).unwrap(); - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); - assert_eq!(output, expected); - } - - // no input, should not fail - { - let mut empty = [0u8; 0]; - let input = BytesRef::Fixed(&mut empty); - - let mut output = vec![0u8; 64]; - let expected = FromHex::from_hex("\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000" - ).unwrap(); - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); - assert_eq!(output, expected); - } - - // should fail - point not on curve - { - let input = FromHex::from_hex("\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111" - ).unwrap(); - - let mut output = vec![0u8; 64]; - - let res = f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])); - assert!(res.is_err(), "There should be built-in error here"); - } - } - - #[test] - fn bn128_mul() { - - let f = Builtin { - pricer: Box::new(Linear { base: 0, word: 0 }), - native: ethereum_builtin("alt_bn128_mul"), - activate_at: 0, - }; - - // zero-point multiplication - { - let input = FromHex::from_hex("\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0200000000000000000000000000000000000000000000000000000000000000" - ).unwrap(); - - let mut output = vec![0u8; 64]; - let expected = FromHex::from_hex("\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000" - ).unwrap(); - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); - assert_eq!(output, expected); - } - - // should fail - point not on curve - { - let input = FromHex::from_hex("\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 0f00000000000000000000000000000000000000000000000000000000000000" - ).unwrap(); - - let mut output = vec![0u8; 64]; - - let res = f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])); - assert!(res.is_err(), "There should be built-in error here"); - } - } - - fn builtin_pairing() -> Builtin { - Builtin { - pricer: Box::new(Linear { base: 0, word: 0 }), - native: ethereum_builtin("alt_bn128_pairing"), - activate_at: 0, - } - } - - fn empty_test(f: Builtin, expected: Vec) { - let mut empty = [0u8; 0]; - let input = BytesRef::Fixed(&mut empty); - - let mut output = vec![0u8; expected.len()]; - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); - assert_eq!(output, expected); - } - - fn error_test(f: Builtin, input: &[u8], msg_contains: Option<&str>) { - let mut output = vec![0u8; 64]; - let res = f.execute(input, &mut BytesRef::Fixed(&mut output[..])); - if let Some(msg) = msg_contains { - if let Err(e) = res { - if !e.0.contains(msg) { - panic!("There should be error containing '{}' here, but got: '{}'", msg, e.0); - } - } - } else { - assert!(res.is_err(), "There should be built-in error here"); - } - } - - fn bytes(s: &'static str) -> Vec { - FromHex::from_hex(s).expect("static str should contain valid hex bytes") - } - - #[test] - fn bn128_pairing_empty() { - // should not fail, because empty input is a valid input of 0 elements - empty_test( - builtin_pairing(), - bytes("0000000000000000000000000000000000000000000000000000000000000001"), - ); - } - - #[test] - fn bn128_pairing_notcurve() { - // should fail - point not on curve - error_test( - builtin_pairing(), - &bytes("\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111" - ), - Some("not on curve"), - ); - } - - #[test] - fn bn128_pairing_fragmented() { - // should fail - input length is invalid - error_test( - builtin_pairing(), - &bytes("\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 111111111111111111111111111111" - ), - Some("Invalid input length"), - ); - } - - #[test] - #[should_panic] - fn from_unknown_linear() { - let _ = ethereum_builtin("foo"); - } - - #[test] - fn is_active() { - let pricer = Box::new(Linear { base: 10, word: 20} ); - let b = Builtin { - pricer: pricer as Box, - native: ethereum_builtin("identity"), - activate_at: 100_000, - }; - - assert!(!b.is_active(99_999)); - assert!(b.is_active(100_000)); - assert!(b.is_active(100_001)); - } - - #[test] - fn from_named_linear() { - let pricer = Box::new(Linear { base: 10, word: 20 }); - let b = Builtin { - pricer: pricer as Box, - native: ethereum_builtin("identity"), - activate_at: 1, - }; - - assert_eq!(b.cost(&[0; 0]), U256::from(10)); - assert_eq!(b.cost(&[0; 1]), U256::from(30)); - assert_eq!(b.cost(&[0; 32]), U256::from(30)); - assert_eq!(b.cost(&[0; 33]), U256::from(50)); - - let i = [0u8, 1, 2, 3]; - let mut o = [255u8; 4]; - b.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); - assert_eq!(i, o); - } - - #[test] - fn from_json() { - let b = Builtin::from(ethjson::spec::Builtin { - name: "identity".to_owned(), - pricing: ethjson::spec::Pricing::Linear(ethjson::spec::Linear { - base: 10, - word: 20, - }), - activate_at: None, - }); - - assert_eq!(b.cost(&[0; 0]), U256::from(10)); - assert_eq!(b.cost(&[0; 1]), U256::from(30)); - assert_eq!(b.cost(&[0; 32]), U256::from(30)); - assert_eq!(b.cost(&[0; 33]), U256::from(50)); - - let i = [0u8, 1, 2, 3]; - let mut o = [255u8; 4]; - b.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); - assert_eq!(i, o); - } -} diff --git a/ethcore/src/client/ancient_import.rs b/ethcore/src/client/ancient_import.rs index 2a0a970cd69..c5ff56aa35f 100644 --- a/ethcore/src/client/ancient_import.rs +++ b/ethcore/src/client/ancient_import.rs @@ -1,24 +1,24 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Helper for ancient block import. use std::sync::Arc; -use engines::{EthEngine, EpochVerifier}; +use engines::{EpochVerifier, EthEngine}; use machine::EthereumMachine; use blockchain::BlockChain; @@ -32,70 +32,78 @@ const HEAVY_VERIFY_RATE: f32 = 0.02; /// Ancient block verifier: import an ancient sequence of blocks in order from a starting /// epoch. pub struct AncientVerifier { - cur_verifier: RwLock>>>, - engine: Arc, + cur_verifier: RwLock>>>, + engine: Arc, } impl AncientVerifier { - /// Create a new ancient block verifier with the given engine. - pub fn new(engine: Arc) -> Self { - AncientVerifier { - cur_verifier: RwLock::new(None), - engine, - } - } - - /// Verify the next block header, randomly choosing whether to do heavy or light - /// verification. If the block is the end of an epoch, updates the epoch verifier. - pub fn verify( - &self, - rng: &mut R, - header: &Header, - chain: &BlockChain, - ) -> Result<(), ::error::Error> { - // perform verification - let verified = if let Some(ref cur_verifier) = *self.cur_verifier.read() { - match rng.gen::() <= HEAVY_VERIFY_RATE { - true => cur_verifier.verify_heavy(header)?, - false => cur_verifier.verify_light(header)?, - } - true - } else { - false - }; - - // when there is no verifier initialize it. - // We use a bool flag to avoid double locking in the happy case - if !verified { - { - let mut cur_verifier = self.cur_verifier.write(); - if cur_verifier.is_none() { - *cur_verifier = Some(self.initial_verifier(header, chain)?); - } - } - // Call again to verify. - return self.verify(rng, header, chain); - } - - // ancient import will only use transitions obtained from the snapshot. - if let Some(transition) = chain.epoch_transition(header.number(), header.hash()) { - let v = self.engine.epoch_verifier(&header, &transition.proof).known_confirmed()?; - *self.cur_verifier.write() = Some(v); - } - - Ok(()) - } - - fn initial_verifier(&self, header: &Header, chain: &BlockChain) - -> Result>, ::error::Error> - { - trace!(target: "client", "Initializing ancient block restoration."); - let current_epoch_data = chain.epoch_transitions() - .take_while(|&(_, ref t)| t.block_number < header.number()) - .last() - .map(|(_, t)| t.proof) - .expect("At least one epoch entry (genesis) always stored; qed"); - - self.engine.epoch_verifier(&header, ¤t_epoch_data).known_confirmed() - } + /// Create a new ancient block verifier with the given engine. + pub fn new(engine: Arc) -> Self { + AncientVerifier { + cur_verifier: RwLock::new(None), + engine, + } + } + + /// Verify the next block header, randomly choosing whether to do heavy or light + /// verification. If the block is the end of an epoch, updates the epoch verifier. + pub fn verify( + &self, + rng: &mut R, + header: &Header, + chain: &BlockChain, + ) -> Result<(), ::error::Error> { + // perform verification + let verified = if let Some(ref cur_verifier) = *self.cur_verifier.read() { + match rng.gen::() <= HEAVY_VERIFY_RATE { + true => cur_verifier.verify_heavy(header)?, + false => cur_verifier.verify_light(header)?, + } + true + } else { + false + }; + + // when there is no verifier initialize it. + // We use a bool flag to avoid double locking in the happy case + if !verified { + { + let mut cur_verifier = self.cur_verifier.write(); + if cur_verifier.is_none() { + *cur_verifier = Some(self.initial_verifier(header, chain)?); + } + } + // Call again to verify. + return self.verify(rng, header, chain); + } + + // ancient import will only use transitions obtained from the snapshot. + if let Some(transition) = chain.epoch_transition(header.number(), header.hash()) { + let v = self + .engine + .epoch_verifier(&header, &transition.proof) + .known_confirmed()?; + *self.cur_verifier.write() = Some(v); + } + + Ok(()) + } + + fn initial_verifier( + &self, + header: &Header, + chain: &BlockChain, + ) -> Result>, ::error::Error> { + trace!(target: "client", "Initializing ancient block restoration."); + let current_epoch_data = chain + .epoch_transitions() + .take_while(|&(_, ref t)| t.block_number < header.number()) + .last() + .map(|(_, t)| t.proof) + .expect("At least one epoch entry (genesis) always stored; qed"); + + self.engine + .epoch_verifier(&header, ¤t_epoch_data) + .known_confirmed() + } } diff --git a/ethcore/src/client/bad_blocks.rs b/ethcore/src/client/bad_blocks.rs index 6af24cc4093..e38d663399a 100644 --- a/ethcore/src/client/bad_blocks.rs +++ b/ethcore/src/client/bad_blocks.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Stores recently seen bad blocks. @@ -25,57 +25,62 @@ use verification::queue::kind::blocks::Unverified; /// Recently seen bad blocks. pub struct BadBlocks { - last_blocks: RwLock>, + last_blocks: RwLock>, } impl Default for BadBlocks { - fn default() -> Self { - BadBlocks { - last_blocks: RwLock::new(MemoryLruCache::new(8 * 1024 * 1024)), - } - } + fn default() -> Self { + BadBlocks { + last_blocks: RwLock::new(MemoryLruCache::new(8 * 1024 * 1024)), + } + } } impl BadBlocks { - /// Reports given RLP as invalid block. - pub fn report(&self, raw: Bytes, message: String) { - match Unverified::from_rlp(raw) { - Ok(unverified) => { - error!( - target: "client", - "\nBad block detected: {}\nRLP: {}\nHeader: {:?}\nUncles: {}\nTransactions:{}\n", - message, - unverified.bytes.to_hex(), - unverified.header, - unverified.uncles - .iter() - .enumerate() - .map(|(index, uncle)| format!("[Uncle {}] {:?}", index, uncle)) - .join("\n"), - unverified.transactions - .iter() - .enumerate() - .map(|(index, tx)| format!("[Tx {}] {:?}", index, tx)) - .join("\n"), - ); - self.last_blocks.write().insert(unverified.header.hash(), (unverified, message)); - }, - Err(err) => { - error!(target: "client", "Bad undecodable block detected: {}\n{:?}", message, err); - }, - } - } + /// Reports given RLP as invalid block. + pub fn report(&self, raw: Bytes, message: String) { + match Unverified::from_rlp(raw) { + Ok(unverified) => { + error!( + target: "client", + "\nBad block detected: {}\nRLP: {}\nHeader: {:?}\nUncles: {}\nTransactions:{}\n", + message, + unverified.bytes.to_hex(), + unverified.header, + unverified.uncles + .iter() + .enumerate() + .map(|(index, uncle)| format!("[Uncle {}] {:?}", index, uncle)) + .join("\n"), + unverified.transactions + .iter() + .enumerate() + .map(|(index, tx)| format!("[Tx {}] {:?}", index, tx)) + .join("\n"), + ); + self.last_blocks + .write() + .insert(unverified.header.hash(), (unverified, message)); + } + Err(err) => { + error!(target: "client", "Bad undecodable block detected: {}\n{:?}", message, err); + } + } + } - /// Returns a list of recently detected bad blocks with error descriptions. - pub fn bad_blocks(&self) -> Vec<(Unverified, String)> { - self.last_blocks.read() - .backstore() - .iter() - .map(|(_k, (unverified, message))| ( - Unverified::from_rlp(unverified.bytes.clone()) - .expect("Bytes coming from UnverifiedBlock so decodable; qed"), - message.clone(), - )) - .collect() - } + /// Returns a list of recently detected bad blocks with error descriptions. + pub fn bad_blocks(&self) -> Vec<(Unverified, String)> { + self.last_blocks + .read() + .backstore() + .iter() + .map(|(_k, (unverified, message))| { + ( + Unverified::from_rlp(unverified.bytes.clone()) + .expect("Bytes coming from UnverifiedBlock so decodable; qed"), + message.clone(), + ) + }) + .collect() + } } diff --git a/ethcore/src/client/chain_notify.rs b/ethcore/src/client/chain_notify.rs index 5f9b8ed314c..b8b439a9fa8 100644 --- a/ethcore/src/client/chain_notify.rs +++ b/ethcore/src/client/chain_notify.rs @@ -1,193 +1,197 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . +use blockchain::ImportRoute; use bytes::Bytes; use ethereum_types::{H256, U256}; +use std::{collections::HashMap, time::Duration}; use types::transaction::UnverifiedTransaction; -use blockchain::ImportRoute; -use std::time::Duration; -use std::collections::HashMap; /// Messages to broadcast via chain pub enum ChainMessageType { - /// Consensus message - Consensus(Vec), - /// Message with private transaction - PrivateTransaction(H256, Vec), - /// Message with signed private transaction - SignedPrivateTransaction(H256, Vec), + /// Consensus message + Consensus(Vec), } /// Route type to indicate whether it is enacted or retracted. #[derive(Clone)] pub enum ChainRouteType { - /// Enacted block - Enacted, - /// Retracted block - Retracted + /// Enacted block + Enacted, + /// Retracted block + Retracted, } /// A complete chain enacted retracted route. #[derive(Default, Clone)] pub struct ChainRoute { - route: Vec<(H256, ChainRouteType)>, - enacted: Vec, - retracted: Vec, + route: Vec<(H256, ChainRouteType)>, + enacted: Vec, + retracted: Vec, } impl<'a> From<&'a [ImportRoute]> for ChainRoute { - fn from(import_results: &'a [ImportRoute]) -> ChainRoute { - ChainRoute::new(import_results.iter().flat_map(|route| { - route.retracted.iter().map(|h| (*h, ChainRouteType::Retracted)) - .chain(route.enacted.iter().map(|h| (*h, ChainRouteType::Enacted))) - }).collect()) - } + fn from(import_results: &'a [ImportRoute]) -> ChainRoute { + ChainRoute::new( + import_results + .iter() + .flat_map(|route| { + route + .retracted + .iter() + .map(|h| (*h, ChainRouteType::Retracted)) + .chain(route.enacted.iter().map(|h| (*h, ChainRouteType::Enacted))) + }) + .collect(), + ) + } } impl ChainRoute { - /// Create a new ChainRoute based on block hash and route type pairs. - pub fn new(route: Vec<(H256, ChainRouteType)>) -> Self { - let (enacted, retracted) = Self::to_enacted_retracted(&route); - - Self { route, enacted, retracted } - } - - /// Gather all non-duplicate enacted and retracted blocks. - fn to_enacted_retracted(route: &[(H256, ChainRouteType)]) -> (Vec, Vec) { - fn map_to_vec(map: Vec<(H256, bool)>) -> Vec { - map.into_iter().map(|(k, _v)| k).collect() - } - - // Because we are doing multiple inserts some of the blocks that were enacted in import `k` - // could be retracted in import `k+1`. This is why to understand if after all inserts - // the block is enacted or retracted we iterate over all routes and at the end final state - // will be in the hashmap - let map = route.iter().fold(HashMap::new(), |mut map, route| { - match &route.1 { - &ChainRouteType::Enacted => { - map.insert(route.0, true); - }, - &ChainRouteType::Retracted => { - map.insert(route.0, false); - }, - } - map - }); - - // Split to enacted retracted (using hashmap value) - let (enacted, retracted) = map.into_iter().partition(|&(_k, v)| v); - // And convert tuples to keys - (map_to_vec(enacted), map_to_vec(retracted)) - } - - /// Consume route and return the enacted retracted form. - pub fn into_enacted_retracted(self) -> (Vec, Vec) { - (self.enacted, self.retracted) - } - - /// All non-duplicate enacted blocks. - pub fn enacted(&self) -> &[H256] { - &self.enacted - } - - /// All non-duplicate retracted blocks. - pub fn retracted(&self) -> &[H256] { - &self.retracted - } - - /// All blocks in the route. - pub fn route(&self) -> &[(H256, ChainRouteType)] { - &self.route - } + /// Create a new ChainRoute based on block hash and route type pairs. + pub fn new(route: Vec<(H256, ChainRouteType)>) -> Self { + let (enacted, retracted) = Self::to_enacted_retracted(&route); + + Self { + route, + enacted, + retracted, + } + } + + /// Gather all non-duplicate enacted and retracted blocks. + fn to_enacted_retracted(route: &[(H256, ChainRouteType)]) -> (Vec, Vec) { + fn map_to_vec(map: Vec<(H256, bool)>) -> Vec { + map.into_iter().map(|(k, _v)| k).collect() + } + + // Because we are doing multiple inserts some of the blocks that were enacted in import `k` + // could be retracted in import `k+1`. This is why to understand if after all inserts + // the block is enacted or retracted we iterate over all routes and at the end final state + // will be in the hashmap + let map = route.iter().fold(HashMap::new(), |mut map, route| { + match &route.1 { + &ChainRouteType::Enacted => { + map.insert(route.0, true); + } + &ChainRouteType::Retracted => { + map.insert(route.0, false); + } + } + map + }); + + // Split to enacted retracted (using hashmap value) + let (enacted, retracted) = map.into_iter().partition(|&(_k, v)| v); + // And convert tuples to keys + (map_to_vec(enacted), map_to_vec(retracted)) + } + + /// Consume route and return the enacted retracted form. + pub fn into_enacted_retracted(self) -> (Vec, Vec) { + (self.enacted, self.retracted) + } + + /// All non-duplicate enacted blocks. + pub fn enacted(&self) -> &[H256] { + &self.enacted + } + + /// All non-duplicate retracted blocks. + pub fn retracted(&self) -> &[H256] { + &self.retracted + } + + /// All blocks in the route. + pub fn route(&self) -> &[(H256, ChainRouteType)] { + &self.route + } } /// Used by `ChainNotify` `new_blocks()` pub struct NewBlocks { - /// Imported blocks - pub imported: Vec, - /// Invalid blocks - pub invalid: Vec, - /// Route - pub route: ChainRoute, - /// Sealed - pub sealed: Vec, - /// Block bytes. - pub proposed: Vec, - /// Duration - pub duration: Duration, - /// Has more blocks to import - pub has_more_blocks_to_import: bool, + /// Imported blocks + pub imported: Vec, + /// Invalid blocks + pub invalid: Vec, + /// Route + pub route: ChainRoute, + /// Sealed + pub sealed: Vec, + /// Block bytes. + pub proposed: Vec, + /// Duration + pub duration: Duration, + /// Has more blocks to import + pub has_more_blocks_to_import: bool, } impl NewBlocks { - /// Constructor - pub fn new( - imported: Vec, - invalid: Vec, - route: ChainRoute, - sealed: Vec, - proposed: Vec, - duration: Duration, - has_more_blocks_to_import: bool, - ) -> NewBlocks { - NewBlocks { - imported, - invalid, - route, - sealed, - proposed, - duration, - has_more_blocks_to_import, - } - } + /// Constructor + pub fn new( + imported: Vec, + invalid: Vec, + route: ChainRoute, + sealed: Vec, + proposed: Vec, + duration: Duration, + has_more_blocks_to_import: bool, + ) -> NewBlocks { + NewBlocks { + imported, + invalid, + route, + sealed, + proposed, + duration, + has_more_blocks_to_import, + } + } } /// Represents what has to be handled by actor listening to chain events -pub trait ChainNotify : Send + Sync { - /// fires when chain has new blocks. - fn new_blocks(&self, _new_blocks: NewBlocks) { - // does nothing by default - } - - /// fires when chain achieves active mode - fn start(&self) { - // does nothing by default - } - - /// fires when chain achieves passive mode - fn stop(&self) { - // does nothing by default - } - - /// fires when chain broadcasts a message - fn broadcast(&self, _message_type: ChainMessageType) { - // does nothing by default - } - - /// fires when new block is about to be imported - /// implementations should be light - fn block_pre_import(&self, _bytes: &Bytes, _hash: &H256, _difficulty: &U256) { - // does nothing by default - } - - /// fires when new transactions are received from a peer - fn transactions_received(&self, - _txs: &[UnverifiedTransaction], - _peer_id: usize, - ) { - // does nothing by default - } +pub trait ChainNotify: Send + Sync { + /// fires when chain has new blocks. + fn new_blocks(&self, _new_blocks: NewBlocks) { + // does nothing by default + } + + /// fires when chain achieves active mode + fn start(&self) { + // does nothing by default + } + + /// fires when chain achieves passive mode + fn stop(&self) { + // does nothing by default + } + + /// fires when chain broadcasts a message + fn broadcast(&self, _message_type: ChainMessageType) { + // does nothing by default + } + + /// fires when new block is about to be imported + /// implementations should be light + fn block_pre_import(&self, _bytes: &Bytes, _hash: &H256, _difficulty: &U256) { + // does nothing by default + } + + /// fires when new transactions are received from a peer + fn transactions_received(&self, _txs: &[UnverifiedTransaction], _peer_id: usize) { + // does nothing by default + } } diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 0b07d710308..f075d6b963d 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -1,92 +1,106 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::cmp; -use std::collections::{HashSet, BTreeMap, VecDeque}; -use std::str::FromStr; -use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering}; -use std::sync::{Arc, Weak}; -use std::time::{Instant, Duration}; - -use blockchain::{BlockReceipts, BlockChain, BlockChainDB, BlockProvider, TreeRoute, ImportRoute, TransactionAddress, ExtrasInsert, BlockNumberKey}; -use bytes::Bytes; -use call_contract::{CallContract, RegistryInfo}; +// along with OpenEthereum. If not, see . + +use std::{ + cmp, + collections::{BTreeMap, HashSet, VecDeque}, + convert::TryFrom, + io::{BufRead, BufReader}, + str::{from_utf8, FromStr}, + sync::{ + atomic::{AtomicBool, AtomicI64, Ordering as AtomicOrdering}, + Arc, Weak, + }, + time::{Duration, Instant}, +}; + +use blockchain::{ + BlockChain, BlockChainDB, BlockNumberKey, BlockProvider, BlockReceipts, ExtrasInsert, + ImportRoute, TransactionAddress, TreeRoute, +}; +use bytes::{Bytes, ToPretty}; +use call_contract::CallContract; +use error::Error; use ethcore_miner::pool::VerifiedTransaction; -use ethereum_types::{H256, Address, U256}; -use evm::Schedule; +use ethereum_types::{Address, H256, H264, U256}; use hash::keccak; -use io::IoChannel; use itertools::Itertools; -use journaldb; -use kvdb::{DBValue, KeyValueDB, DBTransaction}; +use kvdb::{DBTransaction, DBValue, KeyValueDB}; use parking_lot::{Mutex, RwLock}; use rand::OsRng; -use types::transaction::{self, LocalizedTransaction, UnverifiedTransaction, SignedTransaction, Action}; -use trie::{TrieSpec, TrieFactory, Trie}; -use types::ancestry_action::AncestryAction; -use types::encoded; -use types::filter::Filter; -use types::log_entry::LocalizedLogEntry; -use types::receipt::{Receipt, LocalizedReceipt}; -use types::{BlockNumber, header::{Header, ExtendedHeader}}; +use rlp::PayloadInfo; +use rustc_hex::FromHex; +use trie::{Trie, TrieFactory, TrieSpec}; +use types::{ + ancestry_action::AncestryAction, + data_format::DataFormat, + encoded, + filter::Filter, + header::{ExtendedHeader, Header}, + log_entry::LocalizedLogEntry, + receipt::{LocalizedReceipt, Receipt}, + transaction::{self, Action, LocalizedTransaction, SignedTransaction, UnverifiedTransaction}, + BlockNumber, +}; use vm::{EnvInfo, LastHashes}; -use block::{LockedBlock, Drain, ClosedBlock, OpenBlock, enact_verified, SealedBlock}; -use client::ancient_import::AncientVerifier; +use ansi_term::Colour; +use block::{enact_verified, ClosedBlock, Drain, LockedBlock, OpenBlock, SealedBlock}; +use call_contract::RegistryInfo; use client::{ - Nonce, Balance, ChainInfo, BlockInfo, TransactionInfo, - ReopenBlock, PrepareOpenBlock, ScheduleInfo, ImportSealedBlock, - BroadcastProposalBlock, ImportBlock, StateOrBlock, StateInfo, StateClient, Call, - AccountData, BlockChain as BlockChainTrait, BlockProducer, SealedBlockImporter, - ClientIoMessage, BlockChainReset + ancient_import::AncientVerifier, bad_blocks, traits::ForceUpdateSealing, AccountData, + BadBlocks, Balance, BlockChain as BlockChainTrait, BlockChainClient, BlockChainReset, BlockId, + BlockInfo, BlockProducer, BroadcastProposalBlock, Call, CallAnalytics, ChainInfo, + ChainMessageType, ChainNotify, ChainRoute, ClientConfig, ClientIoMessage, EngineInfo, + ImportBlock, ImportExportBlocks, ImportSealedBlock, IoClient, Mode, NewBlocks, Nonce, + PrepareOpenBlock, ProvingBlockChainClient, PruningInfo, ReopenBlock, ScheduleInfo, + SealedBlockImporter, StateClient, StateInfo, StateOrBlock, TraceFilter, TraceId, TransactionId, + TransactionInfo, UncleId, }; -use client::{ - BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient, - TraceFilter, CallAnalytics, Mode, - ChainNotify, NewBlocks, ChainRoute, PruningInfo, ProvingBlockChainClient, EngineInfo, ChainMessageType, - IoClient, BadBlocks, +use engines::{ + epoch::PendingTransition, EngineError, EpochTransition, EthEngine, ForkChoice, MAX_UNCLE_AGE, }; -use client::bad_blocks; -use engines::{MAX_UNCLE_AGE, EthEngine, EpochTransition, ForkChoice, EngineError}; -use engines::epoch::PendingTransition; use error::{ - ImportErrorKind, ExecutionError, CallError, BlockError, - QueueError, QueueErrorKind, Error as EthcoreError, EthcoreResult, ErrorKind as EthcoreErrorKind + BlockError, CallError, Error as EthcoreError, ErrorKind as EthcoreErrorKind, EthcoreResult, + ExecutionError, ImportErrorKind, }; -use executive::{Executive, Executed, TransactOptions, contract_address}; +use executive::{contract_address, Executed, Executive, TransactOptions}; use factory::{Factories, VmFactory}; +use io::IoChannel; use miner::{Miner, MinerService}; use snapshot::{self, io as snapshot_io, SnapshotClient}; use spec::Spec; use state::{self, State}; use state_db::StateDB; -use trace::{self, TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase}; +use stats::{prometheus, prometheus_counter, prometheus_gauge, PrometheusMetrics}; +use trace::{ + self, Database as TraceDatabase, ImportRequest as TraceImportRequest, LocalizedTrace, TraceDB, +}; use transaction_ext::Transaction; -use verification::queue::kind::BlockLike; -use verification::queue::kind::blocks::Unverified; -use verification::{PreverifiedBlock, Verifier, BlockQueue}; -use verification; -use ansi_term::Colour; - +use verification::{ + self, + queue::kind::{blocks::Unverified, BlockLike}, + BlockQueue, PreverifiedBlock, Verifier, +}; +use vm::Schedule; // re-export -pub use types::blockchain_info::BlockChainInfo; -pub use types::block_status::BlockStatus; pub use blockchain::CacheSize as BlockChainCacheSize; +use db::{keys::BlockDetails, Readable, Writable}; +pub use types::{block_status::BlockStatus, blockchain_info::BlockChainInfo}; pub use verification::QueueInfo as BlockQueueInfo; -use db::Writable; use_contract!(registry, "res/contracts/registrar.json"); @@ -99,2352 +113,2745 @@ const MIN_HISTORY_SIZE: u64 = 8; /// Report on the status of a client. #[derive(Default, Clone, Debug, Eq, PartialEq)] pub struct ClientReport { - /// How many blocks have been imported so far. - pub blocks_imported: usize, - /// How many transactions have been applied so far. - pub transactions_applied: usize, - /// How much gas has been processed so far. - pub gas_processed: U256, - /// Memory used by state DB - pub state_db_mem: usize, + /// How many blocks have been imported so far. + pub blocks_imported: usize, + /// How many transactions have been applied so far. + pub transactions_applied: usize, + /// How much gas has been processed so far. + pub gas_processed: U256, + /// Internal structure item sizes + pub item_sizes: BTreeMap, } impl ClientReport { - /// Alter internal reporting to reflect the additional `block` has been processed. - pub fn accrue_block(&mut self, header: &Header, transactions: usize) { - self.blocks_imported += 1; - self.transactions_applied += transactions; - self.gas_processed = self.gas_processed + *header.gas_used(); - } + /// Alter internal reporting to reflect the additional `block` has been processed. + pub fn accrue_block(&mut self, header: &Header, transactions: usize) { + self.blocks_imported += 1; + self.transactions_applied += transactions; + self.gas_processed = self.gas_processed + *header.gas_used(); + } } impl<'a> ::std::ops::Sub<&'a ClientReport> for ClientReport { - type Output = Self; + type Output = Self; - fn sub(mut self, other: &'a ClientReport) -> Self { - let higher_mem = ::std::cmp::max(self.state_db_mem, other.state_db_mem); - let lower_mem = ::std::cmp::min(self.state_db_mem, other.state_db_mem); + fn sub(mut self, other: &'a ClientReport) -> Self { + self.blocks_imported -= other.blocks_imported; + self.transactions_applied -= other.transactions_applied; + self.gas_processed = self.gas_processed - other.gas_processed; - self.blocks_imported -= other.blocks_imported; - self.transactions_applied -= other.transactions_applied; - self.gas_processed = self.gas_processed - other.gas_processed; - self.state_db_mem = higher_mem - lower_mem; - - self - } + self + } } struct SleepState { - last_activity: Option, - last_autosleep: Option, + last_activity: Option, + last_autosleep: Option, } impl SleepState { - fn new(awake: bool) -> Self { - SleepState { - last_activity: match awake { false => None, true => Some(Instant::now()) }, - last_autosleep: match awake { false => Some(Instant::now()), true => None }, - } - } + fn new(awake: bool) -> Self { + SleepState { + last_activity: match awake { + false => None, + true => Some(Instant::now()), + }, + last_autosleep: match awake { + false => Some(Instant::now()), + true => None, + }, + } + } } struct Importer { - /// Lock used during block import - pub import_lock: Mutex<()>, // FIXME Maybe wrap the whole `Importer` instead? + /// Lock used during block import + pub import_lock: Mutex<()>, // FIXME Maybe wrap the whole `Importer` instead? - /// Used to verify blocks - pub verifier: Box>, + /// Used to verify blocks + pub verifier: Box>, - /// Queue containing pending blocks - pub block_queue: BlockQueue, + /// Queue containing pending blocks + pub block_queue: BlockQueue, - /// Handles block sealing - pub miner: Arc, + /// Handles block sealing + pub miner: Arc, - /// Ancient block verifier: import an ancient sequence of blocks in order from a starting epoch - pub ancient_verifier: AncientVerifier, + /// Ancient block verifier: import an ancient sequence of blocks in order from a starting epoch + pub ancient_verifier: AncientVerifier, - /// Ethereum engine to be used during import - pub engine: Arc, + /// Ethereum engine to be used during import + pub engine: Arc, - /// A lru cache of recently detected bad blocks - pub bad_blocks: bad_blocks::BadBlocks, + /// A lru cache of recently detected bad blocks + pub bad_blocks: bad_blocks::BadBlocks, } /// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue. /// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue. pub struct Client { - /// Flag used to disable the client forever. Not to be confused with `liveness`. - /// - /// For example, auto-updater will disable client forever if there is a - /// hard fork registered on-chain that we don't have capability for. - /// When hard fork block rolls around, the client (if `update` is false) - /// knows it can't proceed further. - enabled: AtomicBool, + /// Flag used to disable the client forever. Not to be confused with `liveness`. + enabled: AtomicBool, - /// Operating mode for the client - mode: Mutex, + /// Operating mode for the client + mode: Mutex, - chain: RwLock>, - tracedb: RwLock>, - engine: Arc, + chain: RwLock>, + tracedb: RwLock>, + engine: Arc, - /// Client configuration - config: ClientConfig, + /// Client configuration + config: ClientConfig, - /// Database pruning strategy to use for StateDB - pruning: journaldb::Algorithm, + /// Database pruning strategy to use for StateDB + pruning: journaldb::Algorithm, - /// Client uses this to store blocks, traces, etc. - db: RwLock>, + /// Client uses this to store blocks, traces, etc. + db: RwLock>, - state_db: RwLock, + state_db: RwLock, - /// Report on the status of client - report: RwLock, + /// Report on the status of client + report: RwLock, - sleep_state: Mutex, + sleep_state: Mutex, - /// Flag changed by `sleep` and `wake_up` methods. Not to be confused with `enabled`. - liveness: AtomicBool, - io_channel: RwLock>, + /// Flag changed by `sleep` and `wake_up` methods. Not to be confused with `enabled`. + liveness: AtomicBool, + io_channel: RwLock>, - /// List of actors to be notified on certain chain events - notify: RwLock>>, + /// List of actors to be notified on certain chain events + notify: RwLock>>, - /// Queued transactions from IO - queue_transactions: IoChannelQueue, - /// Ancient blocks import queue - queue_ancient_blocks: IoChannelQueue, - /// Queued ancient blocks, make sure they are imported in order. - queued_ancient_blocks: Arc, - VecDeque<(Unverified, Bytes)> - )>>, - ancient_blocks_import_lock: Arc>, - /// Consensus messages import queue - queue_consensus_message: IoChannelQueue, + /// Queued transactions from IO + queue_transactions: IoChannelQueue, + /// Ancient blocks import queue + queue_ancient_blocks: IoChannelQueue, + /// Queued ancient blocks, make sure they are imported in order. + queued_ancient_blocks: Arc, VecDeque<(Unverified, Bytes)>)>>, + ancient_blocks_import_lock: Arc>, + /// Consensus messages import queue + queue_consensus_message: IoChannelQueue, - last_hashes: RwLock>, - factories: Factories, + last_hashes: RwLock>, + factories: Factories, - /// Number of eras kept in a journal before they are pruned - history: u64, + /// Number of eras kept in a journal before they are pruned + history: u64, - /// An action to be done if a mode/spec_name change happens - on_user_defaults_change: Mutex) + 'static + Send>>>, + /// An action to be done if a mode/spec_name change happens + on_user_defaults_change: Mutex) + 'static + Send>>>, - registrar_address: Option
, + registrar_address: Option
, - /// A closure to call when we want to restart the client - exit_handler: Mutex>>, + /// A closure to call when we want to restart the client + exit_handler: Mutex>>, - importer: Importer, + importer: Importer, } impl Importer { - pub fn new( - config: &ClientConfig, - engine: Arc, - message_channel: IoChannel, - miner: Arc, - ) -> Result { - let block_queue = BlockQueue::new(config.queue.clone(), engine.clone(), message_channel.clone(), config.verifier_type.verifying_seal()); - - Ok(Importer { - import_lock: Mutex::new(()), - verifier: verification::new(config.verifier_type.clone()), - block_queue, - miner, - ancient_verifier: AncientVerifier::new(engine.clone()), - engine, - bad_blocks: Default::default(), - }) - } - - /// This is triggered by a message coming from a block queue when the block is ready for insertion - pub fn import_verified_blocks(&self, client: &Client) -> usize { - // Shortcut out if we know we're incapable of syncing the chain. - if !client.enabled.load(AtomicOrdering::Relaxed) { - return 0; - } - - let max_blocks_to_import = client.config.max_round_blocks_to_import; - let (imported_blocks, import_results, invalid_blocks, imported, proposed_blocks, duration, has_more_blocks_to_import) = { - let mut imported_blocks = Vec::with_capacity(max_blocks_to_import); - let mut invalid_blocks = HashSet::new(); - let mut proposed_blocks = Vec::with_capacity(max_blocks_to_import); - let mut import_results = Vec::with_capacity(max_blocks_to_import); - - let _import_lock = self.import_lock.lock(); - let blocks = self.block_queue.drain(max_blocks_to_import); - if blocks.is_empty() { - return 0; - } - trace_time!("import_verified_blocks"); - let start = Instant::now(); - - for block in blocks { - let header = block.header.clone(); - let bytes = block.bytes.clone(); - let hash = header.hash(); - - let is_invalid = invalid_blocks.contains(header.parent_hash()); - if is_invalid { - invalid_blocks.insert(hash); - continue; - } - - match self.check_and_lock_block(&bytes, block, client) { - Ok((closed_block, pending)) => { - imported_blocks.push(hash); - let transactions_len = closed_block.transactions.len(); - let route = self.commit_block(closed_block, &header, encoded::Block::new(bytes), pending, client); - import_results.push(route); - client.report.write().accrue_block(&header, transactions_len); - }, - Err(err) => { - self.bad_blocks.report(bytes, format!("{:?}", err)); - invalid_blocks.insert(hash); - }, - } - } - - let imported = imported_blocks.len(); - let invalid_blocks = invalid_blocks.into_iter().collect::>(); - - if !invalid_blocks.is_empty() { - self.block_queue.mark_as_bad(&invalid_blocks); - } - let has_more_blocks_to_import = !self.block_queue.mark_as_good(&imported_blocks); - (imported_blocks, import_results, invalid_blocks, imported, proposed_blocks, start.elapsed(), has_more_blocks_to_import) - }; - - { - if !imported_blocks.is_empty() { - let route = ChainRoute::from(import_results.as_ref()); - - if !has_more_blocks_to_import { - self.miner.chain_new_blocks(client, &imported_blocks, &invalid_blocks, route.enacted(), route.retracted(), false); - } - - client.notify(|notify| { - notify.new_blocks( - NewBlocks::new( - imported_blocks.clone(), - invalid_blocks.clone(), - route.clone(), - Vec::new(), - proposed_blocks.clone(), - duration, - has_more_blocks_to_import, - ) - ); - }); - } - } - - let db = client.db.read(); - db.key_value().flush().expect("DB flush failed."); - imported - } - - fn check_and_lock_block(&self, bytes: &[u8], block: PreverifiedBlock, client: &Client) -> EthcoreResult<(LockedBlock, Option)> { - let engine = &*self.engine; - let header = block.header.clone(); - - // Check the block isn't so old we won't be able to enact it. - let best_block_number = client.chain.read().best_block_number(); - if client.pruning_info().earliest_state > header.number() { - warn!(target: "client", "Block import failed for #{} ({})\nBlock is ancient (current best block: #{}).", header.number(), header.hash(), best_block_number); - bail!("Block is ancient"); - } - - // Check if parent is in chain - let parent = match client.block_header_decoded(BlockId::Hash(*header.parent_hash())) { - Some(h) => h, - None => { - warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash()); - bail!("Parent not found"); - } - }; - - let chain = client.chain.read(); - // Verify Block Family - let verify_family_result = self.verifier.verify_block_family( - &header, - &parent, - engine, - Some(verification::FullFamilyParams { - block: &block, - block_provider: &**chain, - client - }), - ); - - if let Err(e) = verify_family_result { - warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); - bail!(e); - }; - - let verify_external_result = self.verifier.verify_block_external(&header, engine); - if let Err(e) = verify_external_result { - warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); - bail!(e); - }; - - // Enact Verified Block - let last_hashes = client.build_last_hashes(header.parent_hash()); - let db = client.state_db.read().boxed_clone_canon(header.parent_hash()); - - let is_epoch_begin = chain.epoch_transition(parent.number(), *header.parent_hash()).is_some(); - - let enact_result = enact_verified( - block, - engine, - client.tracedb.read().tracing_enabled(), - db, - &parent, - last_hashes, - client.factories.clone(), - is_epoch_begin, - &mut chain.ancestry_with_metadata_iter(*header.parent_hash()), - ); - - let mut locked_block = match enact_result { - Ok(b) => b, - Err(e) => { - warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); - bail!(e); - } - }; - - // Strip receipts for blocks before validate_receipts_transition, - // if the expected receipts root header does not match. - // (i.e. allow inconsistency in receipts outcome before the transition block) - if header.number() < engine.params().validate_receipts_transition - && header.receipts_root() != locked_block.header.receipts_root() - { - locked_block.strip_receipts_outcomes(); - } - - // Final Verification - if let Err(e) = self.verifier.verify_block_final(&header, &locked_block.header) { - warn!(target: "client", "Stage 5 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); - bail!(e); - } - - let pending = self.check_epoch_end_signal( - &header, - bytes, - &locked_block.receipts, - locked_block.state.db(), - client - )?; - - Ok((locked_block, pending)) - } - - /// Import a block with transaction receipts. - /// - /// The block is guaranteed to be the next best blocks in the - /// first block sequence. Does no sealing or transaction validation. - fn import_old_block(&self, unverified: Unverified, receipts_bytes: &[u8], db: &KeyValueDB, chain: &BlockChain) -> EthcoreResult<()> { - let receipts = ::rlp::decode_list(receipts_bytes); - let _import_lock = self.import_lock.lock(); - - { - trace_time!("import_old_block"); - // verify the block, passing the chain for updating the epoch verifier. - let mut rng = OsRng::new()?; - self.ancient_verifier.verify(&mut rng, &unverified.header, &chain)?; - - // Commit results - let mut batch = DBTransaction::new(); - chain.insert_unordered_block(&mut batch, encoded::Block::new(unverified.bytes), receipts, None, false, true); - // Final commit to the DB - db.write_buffered(batch); - chain.commit(); - } - db.flush().expect("DB flush failed."); - Ok(()) - } - - // NOTE: the header of the block passed here is not necessarily sealed, as - // it is for reconstructing the state transition. - // - // The header passed is from the original block data and is sealed. - // TODO: should return an error if ImportRoute is none, issue #9910 - fn commit_block(&self, block: B, header: &Header, block_data: encoded::Block, pending: Option, client: &Client) -> ImportRoute where B: Drain { - let hash = &header.hash(); - let number = header.number(); - let parent = header.parent_hash(); - let chain = client.chain.read(); - let mut is_finalized = false; - - // Commit results - let block = block.drain(); - debug_assert_eq!(header.hash(), block_data.header_view().hash()); - - let mut batch = DBTransaction::new(); - - let ancestry_actions = self.engine.ancestry_actions(&header, &mut chain.ancestry_with_metadata_iter(*parent)); - - let receipts = block.receipts; - let traces = block.traces.drain(); - let best_hash = chain.best_block_hash(); - - let new = ExtendedHeader { - header: header.clone(), - is_finalized, - parent_total_difficulty: chain.block_details(&parent).expect("Parent block is in the database; qed").total_difficulty - }; - - let best = { - let hash = best_hash; - let header = chain.block_header_data(&hash) - .expect("Best block is in the database; qed") - .decode() - .expect("Stored block header is valid RLP; qed"); - let details = chain.block_details(&hash) - .expect("Best block is in the database; qed"); - - ExtendedHeader { - parent_total_difficulty: details.total_difficulty - *header.difficulty(), - is_finalized: details.is_finalized, - header: header, - } - }; - - let route = chain.tree_route(best_hash, *parent).expect("forks are only kept when it has common ancestors; tree route from best to prospective's parent always exists; qed"); - let fork_choice = if route.is_from_route_finalized { - ForkChoice::Old - } else { - self.engine.fork_choice(&new, &best) - }; - - // CHECK! I *think* this is fine, even if the state_root is equal to another - // already-imported block of the same number. - // TODO: Prove it with a test. - let mut state = block.state.drop().1; - - // check epoch end signal, potentially generating a proof on the current - // state. - if let Some(pending) = pending { - chain.insert_pending_transition(&mut batch, header.hash(), pending); - } - - state.journal_under(&mut batch, number, hash).expect("DB commit failed"); - - let finalized: Vec<_> = ancestry_actions.into_iter().map(|ancestry_action| { - let AncestryAction::MarkFinalized(a) = ancestry_action; - - if a != header.hash() { - chain.mark_finalized(&mut batch, a).expect("Engine's ancestry action must be known blocks; qed"); - } else { - // we're finalizing the current block - is_finalized = true; - } - - a - }).collect(); - - let route = chain.insert_block(&mut batch, block_data, receipts.clone(), ExtrasInsert { - fork_choice: fork_choice, - is_finalized, - }); - - client.tracedb.read().import(&mut batch, TraceImportRequest { - traces: traces.into(), - block_hash: hash.clone(), - block_number: number, - enacted: route.enacted.clone(), - retracted: route.retracted.len() - }); - - let is_canon = route.enacted.last().map_or(false, |h| h == hash); - state.sync_cache(&route.enacted, &route.retracted, is_canon); - // Final commit to the DB - client.db.read().key_value().write_buffered(batch); - chain.commit(); - - self.check_epoch_end(&header, &finalized, &chain, client); - - client.update_last_hashes(&parent, hash); - - if let Err(e) = client.prune_ancient(state, &chain) { - warn!("Failed to prune ancient state data: {}", e); - } - - route - } - - // check for epoch end signal and write pending transition if it occurs. - // state for the given block must be available. - fn check_epoch_end_signal( - &self, - header: &Header, - block_bytes: &[u8], - receipts: &[Receipt], - state_db: &StateDB, - client: &Client, - ) -> EthcoreResult> { - use engines::EpochChange; - - let hash = header.hash(); - let auxiliary = ::machine::AuxiliaryData { - bytes: Some(block_bytes), - receipts: Some(&receipts), - }; - - match self.engine.signals_epoch_end(header, auxiliary) { - EpochChange::Yes(proof) => { - use engines::Proof; - - let proof = match proof { - Proof::Known(proof) => proof, - Proof::WithState(with_state) => { - let env_info = EnvInfo { - number: header.number(), - author: header.author().clone(), - timestamp: header.timestamp(), - difficulty: header.difficulty().clone(), - last_hashes: client.build_last_hashes(header.parent_hash()), - gas_used: U256::default(), - gas_limit: u64::max_value().into(), - }; - - let call = move |addr, data| { - let mut state_db = state_db.boxed_clone(); - let backend = ::state::backend::Proving::new(state_db.as_hash_db_mut()); - - let transaction = - client.contract_call_tx(BlockId::Hash(*header.parent_hash()), addr, data); - - let mut state = State::from_existing( - backend, - header.state_root().clone(), - self.engine.account_start_nonce(header.number()), - client.factories.clone(), - ).expect("state known to be available for just-imported block; qed"); - - let options = TransactOptions::with_no_tracing().dont_check_nonce(); - let machine = self.engine.machine(); - let schedule = machine.schedule(env_info.number); - let res = Executive::new(&mut state, &env_info, &machine, &schedule) - .transact(&transaction, options); - - let res = match res { - Err(e) => { - trace!(target: "client", "Proved call failed: {}", e); - Err(e.to_string()) - } - Ok(res) => Ok((res.output, state.drop().1.extract_proof())), - }; - - res.map(|(output, proof)| (output, proof.into_iter().map(|x| x.into_vec()).collect())) - }; - - match with_state.generate_proof(&call) { - Ok(proof) => proof, - Err(e) => { - warn!(target: "client", "Failed to generate transition proof for block {}: {}", hash, e); - warn!(target: "client", "Snapshots produced by this client may be incomplete"); - return Err(EngineError::FailedSystemCall(e).into()) - } - } - } - }; - - debug!(target: "client", "Block {} signals epoch end.", hash); - - Ok(Some(PendingTransition { proof: proof })) - }, - EpochChange::No => Ok(None), - EpochChange::Unsure(_) => { - warn!(target: "client", "Detected invalid engine implementation."); - warn!(target: "client", "Engine claims to require more block data, but everything provided."); - Err(EngineError::InvalidEngine.into()) - } - } - } - - // check for ending of epoch and write transition if it occurs. - fn check_epoch_end<'a>(&self, header: &'a Header, finalized: &'a [H256], chain: &BlockChain, client: &Client) { - let is_epoch_end = self.engine.is_epoch_end( - header, - finalized, - &(|hash| client.block_header_decoded(BlockId::Hash(hash))), - &(|hash| chain.get_pending_transition(hash)), // TODO: limit to current epoch. - ); - - if let Some(proof) = is_epoch_end { - debug!(target: "client", "Epoch transition at block {}", header.hash()); - - let mut batch = DBTransaction::new(); - chain.insert_epoch_transition(&mut batch, header.number(), EpochTransition { - block_hash: header.hash(), - block_number: header.number(), - proof: proof, - }); - - // always write the batch directly since epoch transition proofs are - // fetched from a DB iterator and DB iterators are only available on - // flushed data. - client.db.read().key_value().write(batch).expect("DB flush failed"); - } - } + pub fn new( + config: &ClientConfig, + engine: Arc, + message_channel: IoChannel, + miner: Arc, + ) -> Result { + let block_queue = BlockQueue::new( + config.queue.clone(), + engine.clone(), + message_channel.clone(), + config.verifier_type.verifying_seal(), + ); + + Ok(Importer { + import_lock: Mutex::new(()), + verifier: verification::new(config.verifier_type.clone()), + block_queue, + miner, + ancient_verifier: AncientVerifier::new(engine.clone()), + engine, + bad_blocks: Default::default(), + }) + } + + /// This is triggered by a message coming from a block queue when the block is ready for insertion + pub fn import_verified_blocks(&self, client: &Client) -> usize { + // Shortcut out if we know we're incapable of syncing the chain. + if !client.enabled.load(AtomicOrdering::Relaxed) { + return 0; + } + + let max_blocks_to_import = client.config.max_round_blocks_to_import; + let ( + imported_blocks, + import_results, + invalid_blocks, + imported, + proposed_blocks, + duration, + has_more_blocks_to_import, + ) = { + let mut imported_blocks = Vec::with_capacity(max_blocks_to_import); + let mut invalid_blocks = HashSet::new(); + let proposed_blocks = Vec::with_capacity(max_blocks_to_import); + let mut import_results = Vec::with_capacity(max_blocks_to_import); + + let _import_lock = self.import_lock.lock(); + let blocks = self.block_queue.drain(max_blocks_to_import); + if blocks.is_empty() { + return 0; + } + trace_time!("import_verified_blocks"); + let start = Instant::now(); + + for block in blocks { + let header = block.header.clone(); + let bytes = block.bytes.clone(); + let hash = header.hash(); + + let is_invalid = invalid_blocks.contains(header.parent_hash()); + if is_invalid { + invalid_blocks.insert(hash); + continue; + } + + match self.check_and_lock_block(&bytes, block, client) { + Ok((closed_block, pending)) => { + imported_blocks.push(hash); + let transactions_len = closed_block.transactions.len(); + let route = self.commit_block( + closed_block, + &header, + encoded::Block::new(bytes), + pending, + client, + ); + import_results.push(route); + client + .report + .write() + .accrue_block(&header, transactions_len); + } + Err(err) => { + self.bad_blocks.report(bytes, format!("{:?}", err)); + invalid_blocks.insert(hash); + } + } + } + + let imported = imported_blocks.len(); + let invalid_blocks = invalid_blocks.into_iter().collect::>(); + + if !invalid_blocks.is_empty() { + self.block_queue.mark_as_bad(&invalid_blocks); + } + let has_more_blocks_to_import = !self.block_queue.mark_as_good(&imported_blocks); + ( + imported_blocks, + import_results, + invalid_blocks, + imported, + proposed_blocks, + start.elapsed(), + has_more_blocks_to_import, + ) + }; + + { + if !imported_blocks.is_empty() { + let route = ChainRoute::from(import_results.as_ref()); + + if !has_more_blocks_to_import { + self.miner.chain_new_blocks( + client, + &imported_blocks, + &invalid_blocks, + route.enacted(), + route.retracted(), + false, + ); + } + + client.notify(|notify| { + notify.new_blocks(NewBlocks::new( + imported_blocks.clone(), + invalid_blocks.clone(), + route.clone(), + Vec::new(), + proposed_blocks.clone(), + duration, + has_more_blocks_to_import, + )); + }); + } + } + + let db = client.db.read(); + db.key_value().flush().expect("DB flush failed."); + imported + } + + fn check_and_lock_block( + &self, + bytes: &[u8], + block: PreverifiedBlock, + client: &Client, + ) -> EthcoreResult<(LockedBlock, Option)> { + let engine = &*self.engine; + let header = block.header.clone(); + + // Check the block isn't so old we won't be able to enact it. + let best_block_number = client.chain.read().best_block_number(); + if client.pruning_info().earliest_state > header.number() { + warn!(target: "client", "Block import failed for #{} ({})\nBlock is ancient (current best block: #{}).", header.number(), header.hash(), best_block_number); + bail!("Block is ancient"); + } + + // Check if parent is in chain + let parent = match client.block_header_decoded(BlockId::Hash(*header.parent_hash())) { + Some(h) => h, + None => { + warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash()); + bail!("Parent not found"); + } + }; + + let chain = client.chain.read(); + // Verify Block Family + let verify_family_result = self.verifier.verify_block_family( + &header, + &parent, + engine, + Some(verification::FullFamilyParams { + block: &block, + block_provider: &**chain, + client, + }), + ); + + if let Err(e) = verify_family_result { + warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); + bail!(e); + }; + + let verify_external_result = self.verifier.verify_block_external(&header, engine); + if let Err(e) = verify_external_result { + warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); + bail!(e); + }; + + // Enact Verified Block + let last_hashes = client.build_last_hashes(header.parent_hash()); + let db = client + .state_db + .read() + .boxed_clone_canon(header.parent_hash()); + + let is_epoch_begin = chain + .epoch_transition(parent.number(), *header.parent_hash()) + .is_some(); + + let enact_result = enact_verified( + block, + engine, + client.tracedb.read().tracing_enabled(), + db, + &parent, + last_hashes, + client.factories.clone(), + is_epoch_begin, + &mut chain.ancestry_with_metadata_iter(*header.parent_hash()), + ); + + let mut locked_block = match enact_result { + Ok(b) => b, + Err(e) => { + warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); + bail!(e); + } + }; + + // Strip receipts for blocks before validate_receipts_transition, + // if the expected receipts root header does not match. + // (i.e. allow inconsistency in receipts outcome before the transition block) + if header.number() < engine.params().validate_receipts_transition + && header.receipts_root() != locked_block.header.receipts_root() + { + locked_block.strip_receipts_outcomes(); + } + + // Final Verification + if let Err(e) = self + .verifier + .verify_block_final(&header, &locked_block.header) + { + warn!(target: "client", "Stage 5 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); + bail!(e); + } + + let pending = self.check_epoch_end_signal( + &header, + bytes, + &locked_block.receipts, + locked_block.state.db(), + client, + )?; + + Ok((locked_block, pending)) + } + + /// Import a block with transaction receipts. + /// + /// The block is guaranteed to be the next best blocks in the + /// first block sequence. Does no sealing or transaction validation. + fn import_old_block( + &self, + unverified: Unverified, + receipts_bytes: &[u8], + db: &dyn KeyValueDB, + chain: &BlockChain, + ) -> EthcoreResult<()> { + let receipts = ::rlp::decode_list(receipts_bytes); + let _import_lock = self.import_lock.lock(); + + { + trace_time!("import_old_block"); + // verify the block, passing the chain for updating the epoch verifier. + let mut rng = OsRng::new()?; + self.ancient_verifier + .verify(&mut rng, &unverified.header, &chain)?; + + // Commit results + let mut batch = DBTransaction::new(); + chain.insert_unordered_block( + &mut batch, + encoded::Block::new(unverified.bytes), + receipts, + None, + false, + true, + ); + // Final commit to the DB + db.write_buffered(batch); + chain.commit(); + } + db.flush().expect("DB flush failed."); + Ok(()) + } + + // NOTE: the header of the block passed here is not necessarily sealed, as + // it is for reconstructing the state transition. + // + // The header passed is from the original block data and is sealed. + // TODO: should return an error if ImportRoute is none, issue #9910 + fn commit_block( + &self, + block: B, + header: &Header, + block_data: encoded::Block, + pending: Option, + client: &Client, + ) -> ImportRoute + where + B: Drain, + { + let hash = &header.hash(); + let number = header.number(); + let parent = header.parent_hash(); + let chain = client.chain.read(); + let mut is_finalized = false; + + // Commit results + let block = block.drain(); + debug_assert_eq!(header.hash(), block_data.header_view().hash()); + + let mut batch = DBTransaction::new(); + + let ancestry_actions = self + .engine + .ancestry_actions(&header, &mut chain.ancestry_with_metadata_iter(*parent)); + + let receipts = block.receipts; + let traces = block.traces.drain(); + let best_hash = chain.best_block_hash(); + + let new = ExtendedHeader { + header: header.clone(), + is_finalized, + parent_total_difficulty: chain + .block_details(&parent) + .expect("Parent block is in the database; qed") + .total_difficulty, + }; + + let best = { + let hash = best_hash; + let header = chain + .block_header_data(&hash) + .expect("Best block is in the database; qed") + .decode() + .expect("Stored block header is valid RLP; qed"); + let details = chain + .block_details(&hash) + .expect("Best block is in the database; qed"); + + ExtendedHeader { + parent_total_difficulty: details.total_difficulty - *header.difficulty(), + is_finalized: details.is_finalized, + header: header, + } + }; + + let route = chain.tree_route(best_hash, *parent).expect("forks are only kept when it has common ancestors; tree route from best to prospective's parent always exists; qed"); + let fork_choice = if route.is_from_route_finalized { + ForkChoice::Old + } else { + self.engine.fork_choice(&new, &best) + }; + + // CHECK! I *think* this is fine, even if the state_root is equal to another + // already-imported block of the same number. + // TODO: Prove it with a test. + let mut state = block.state.drop().1; + + // check epoch end signal, potentially generating a proof on the current + // state. + if let Some(pending) = pending { + chain.insert_pending_transition(&mut batch, header.hash(), pending); + } + + state + .journal_under(&mut batch, number, hash) + .expect("DB commit failed"); + + let finalized: Vec<_> = ancestry_actions + .into_iter() + .map(|ancestry_action| { + let AncestryAction::MarkFinalized(a) = ancestry_action; + + if a != header.hash() { + chain + .mark_finalized(&mut batch, a) + .expect("Engine's ancestry action must be known blocks; qed"); + } else { + // we're finalizing the current block + is_finalized = true; + } + + a + }) + .collect(); + + let route = chain.insert_block( + &mut batch, + block_data, + receipts.clone(), + ExtrasInsert { + fork_choice: fork_choice, + is_finalized, + }, + ); + + client.tracedb.read().import( + &mut batch, + TraceImportRequest { + traces: traces.into(), + block_hash: hash.clone(), + block_number: number, + enacted: route.enacted.clone(), + retracted: route.retracted.len(), + }, + ); + + let is_canon = route.enacted.last().map_or(false, |h| h == hash); + state.sync_cache(&route.enacted, &route.retracted, is_canon); + // Final commit to the DB + client.db.read().key_value().write_buffered(batch); + chain.commit(); + + self.check_epoch_end(&header, &finalized, &chain, client); + + client.update_last_hashes(&parent, hash); + + if let Err(e) = client.prune_ancient(state, &chain) { + warn!("Failed to prune ancient state data: {}", e); + } + + route + } + + // check for epoch end signal and write pending transition if it occurs. + // state for the given block must be available. + fn check_epoch_end_signal( + &self, + header: &Header, + block_bytes: &[u8], + receipts: &[Receipt], + state_db: &StateDB, + client: &Client, + ) -> EthcoreResult> { + use engines::EpochChange; + + let hash = header.hash(); + let auxiliary = ::machine::AuxiliaryData { + bytes: Some(block_bytes), + receipts: Some(&receipts), + }; + + match self.engine.signals_epoch_end(header, auxiliary) { + EpochChange::Yes(proof) => { + use engines::Proof; + + let proof = match proof { + Proof::Known(proof) => proof, + Proof::WithState(with_state) => { + let env_info = EnvInfo { + number: header.number(), + author: header.author().clone(), + timestamp: header.timestamp(), + difficulty: header.difficulty().clone(), + last_hashes: client.build_last_hashes(header.parent_hash()), + gas_used: U256::default(), + gas_limit: u64::max_value().into(), + }; + + let call = move |addr, data| { + let mut state_db = state_db.boxed_clone(); + let backend = ::state::backend::Proving::new(state_db.as_hash_db_mut()); + + let transaction = client.contract_call_tx( + BlockId::Hash(*header.parent_hash()), + addr, + data, + ); + + let mut state = State::from_existing( + backend, + header.state_root().clone(), + self.engine.account_start_nonce(header.number()), + client.factories.clone(), + ) + .expect("state known to be available for just-imported block; qed"); + + let options = TransactOptions::with_no_tracing().dont_check_nonce(); + let machine = self.engine.machine(); + let schedule = machine.schedule(env_info.number); + let res = Executive::new(&mut state, &env_info, &machine, &schedule) + .transact(&transaction, options); + + let res = match res { + Err(e) => { + trace!(target: "client", "Proved call failed: {}", e); + Err(e.to_string()) + } + Ok(res) => Ok((res.output, state.drop().1.extract_proof())), + }; + + res.map(|(output, proof)| { + (output, proof.into_iter().map(|x| x.into_vec()).collect()) + }) + }; + + match with_state.generate_proof(&call) { + Ok(proof) => proof, + Err(e) => { + warn!(target: "client", "Failed to generate transition proof for block {}: {}", hash, e); + warn!(target: "client", "Snapshots produced by this client may be incomplete"); + return Err(EngineError::FailedSystemCall(e).into()); + } + } + } + }; + + debug!(target: "client", "Block {} signals epoch end.", hash); + + Ok(Some(PendingTransition { proof: proof })) + } + EpochChange::No => Ok(None), + EpochChange::Unsure(_) => { + warn!(target: "client", "Detected invalid engine implementation."); + warn!(target: "client", "Engine claims to require more block data, but everything provided."); + Err(EngineError::InvalidEngine.into()) + } + } + } + + // check for ending of epoch and write transition if it occurs. + fn check_epoch_end<'a>( + &self, + header: &'a Header, + finalized: &'a [H256], + chain: &BlockChain, + client: &Client, + ) { + let is_epoch_end = self.engine.is_epoch_end( + header, + finalized, + &(|hash| client.block_header_decoded(BlockId::Hash(hash))), + &(|hash| chain.get_pending_transition(hash)), // TODO: limit to current epoch. + ); + + if let Some(proof) = is_epoch_end { + debug!(target: "client", "Epoch transition at block {}", header.hash()); + + let mut batch = DBTransaction::new(); + chain.insert_epoch_transition( + &mut batch, + header.number(), + EpochTransition { + block_hash: header.hash(), + block_number: header.number(), + proof: proof, + }, + ); + + // always write the batch directly since epoch transition proofs are + // fetched from a DB iterator and DB iterators are only available on + // flushed data. + client + .db + .read() + .key_value() + .write(batch) + .expect("DB flush failed"); + } + } } impl Client { - /// Create a new client with given parameters. - /// The database is assumed to have been initialized with the correct columns. - pub fn new( - config: ClientConfig, - spec: &Spec, - db: Arc, - miner: Arc, - message_channel: IoChannel, - ) -> Result, ::error::Error> { - let trie_spec = match config.fat_db { - true => TrieSpec::Fat, - false => TrieSpec::Secure, - }; - - let trie_factory = TrieFactory::new(trie_spec); - let factories = Factories { - vm: VmFactory::new(config.vm_type.clone(), config.jump_table_size), - trie: trie_factory, - accountdb: Default::default(), - }; - - let journal_db = journaldb::new(db.key_value().clone(), config.pruning, ::db::COL_STATE); - let mut state_db = StateDB::new(journal_db, config.state_cache_size); - if state_db.journal_db().is_empty() { - // Sets the correct state root. - state_db = spec.ensure_db_good(state_db, &factories)?; - let mut batch = DBTransaction::new(); - state_db.journal_under(&mut batch, 0, &spec.genesis_header().hash())?; - db.key_value().write(batch)?; - } - - let gb = spec.genesis_block(); - let chain = Arc::new(BlockChain::new(config.blockchain.clone(), &gb, db.clone())); - let tracedb = RwLock::new(TraceDB::new(config.tracing.clone(), db.clone(), chain.clone())); - - trace!("Cleanup journal: DB Earliest = {:?}, Latest = {:?}", state_db.journal_db().earliest_era(), state_db.journal_db().latest_era()); - - let history = if config.history < MIN_HISTORY_SIZE { - info!(target: "client", "Ignoring pruning history parameter of {}\ + /// Create a new client with given parameters. + /// The database is assumed to have been initialized with the correct columns. + pub fn new( + config: ClientConfig, + spec: &Spec, + db: Arc, + miner: Arc, + message_channel: IoChannel, + ) -> Result, ::error::Error> { + let trie_spec = match config.fat_db { + true => TrieSpec::Fat, + false => TrieSpec::Secure, + }; + + let trie_factory = TrieFactory::new(trie_spec); + let factories = Factories { + vm: VmFactory::new(config.vm_type.clone(), config.jump_table_size), + trie: trie_factory, + accountdb: Default::default(), + }; + + let journal_db = journaldb::new(db.key_value().clone(), config.pruning, ::db::COL_STATE); + let mut state_db = StateDB::new(journal_db, config.state_cache_size); + if state_db.journal_db().is_empty() { + // Sets the correct state root. + state_db = spec.ensure_db_good(state_db, &factories)?; + let mut batch = DBTransaction::new(); + state_db.journal_under(&mut batch, 0, &spec.genesis_header().hash())?; + db.key_value().write(batch)?; + } + + let gb = spec.genesis_block(); + let chain = Arc::new(BlockChain::new(config.blockchain.clone(), &gb, db.clone())); + let tracedb = RwLock::new(TraceDB::new( + config.tracing.clone(), + db.clone(), + chain.clone(), + )); + + trace!( + "Cleanup journal: DB Earliest = {:?}, Latest = {:?}", + state_db.journal_db().earliest_era(), + state_db.journal_db().latest_era() + ); + + let history = if config.history < MIN_HISTORY_SIZE { + info!(target: "client", "Ignoring pruning history parameter of {}\ , falling back to minimum of {}", config.history, MIN_HISTORY_SIZE); - MIN_HISTORY_SIZE - } else { - config.history - }; - - if !chain.block_header_data(&chain.best_block_hash()).map_or(true, |h| state_db.journal_db().contains(&h.state_root())) { - warn!("State root not found for block #{} ({:x})", chain.best_block_number(), chain.best_block_hash()); - } - - let engine = spec.engine.clone(); - - let awake = match config.mode { Mode::Dark(..) | Mode::Off => false, _ => true }; - - let importer = Importer::new(&config, engine.clone(), message_channel.clone(), miner)?; - - let registrar_address = engine.additional_params().get("registrar").and_then(|s| Address::from_str(s).ok()); - if let Some(ref addr) = registrar_address { - trace!(target: "client", "Found registrar at {}", addr); - } - - let client = Arc::new(Client { - enabled: AtomicBool::new(true), - sleep_state: Mutex::new(SleepState::new(awake)), - liveness: AtomicBool::new(awake), - mode: Mutex::new(config.mode.clone()), - chain: RwLock::new(chain), - tracedb: tracedb, - engine: engine, - pruning: config.pruning.clone(), - db: RwLock::new(db.clone()), - state_db: RwLock::new(state_db), - report: RwLock::new(Default::default()), - io_channel: RwLock::new(message_channel), - notify: RwLock::new(Vec::new()), - queue_transactions: IoChannelQueue::new(config.transaction_verification_queue_size), - queue_ancient_blocks: IoChannelQueue::new(MAX_ANCIENT_BLOCKS_QUEUE_SIZE), - queued_ancient_blocks: Default::default(), - ancient_blocks_import_lock: Default::default(), - queue_consensus_message: IoChannelQueue::new(usize::max_value()), - last_hashes: RwLock::new(VecDeque::new()), - factories: factories, - history: history, - on_user_defaults_change: Mutex::new(None), - registrar_address, - exit_handler: Mutex::new(None), - importer, - config, - }); - - // prune old states. - { - let state_db = client.state_db.read().boxed_clone(); - let chain = client.chain.read(); - client.prune_ancient(state_db, &chain)?; - } - - // ensure genesis epoch proof in the DB. - { - let chain = client.chain.read(); - let gh = spec.genesis_header(); - if chain.epoch_transition(0, gh.hash()).is_none() { - trace!(target: "client", "No genesis transition found."); - - let proof = client.with_proving_caller( - BlockId::Number(0), - |call| client.engine.genesis_epoch_data(&gh, call) - ); - let proof = match proof { - Ok(proof) => proof, - Err(e) => { - warn!(target: "client", "Error generating genesis epoch data: {}. Snapshots generated may not be complete.", e); - Vec::new() - } - }; - - debug!(target: "client", "Obtained genesis transition proof: {:?}", proof); - - let mut batch = DBTransaction::new(); - chain.insert_epoch_transition(&mut batch, 0, EpochTransition { - block_hash: gh.hash(), - block_number: 0, - proof: proof, - }); - - client.db.read().key_value().write_buffered(batch); - } - } - - // ensure buffered changes are flushed. - client.db.read().key_value().flush()?; - Ok(client) - } - - /// Wakes up client if it's a sleep. - pub fn keep_alive(&self) { - let should_wake = match *self.mode.lock() { - Mode::Dark(..) | Mode::Passive(..) => true, - _ => false, - }; - if should_wake { - self.wake_up(); - (*self.sleep_state.lock()).last_activity = Some(Instant::now()); - } - } - - /// Adds an actor to be notified on certain events - pub fn add_notify(&self, target: Arc) { - self.notify.write().push(Arc::downgrade(&target)); - } - - /// Set a closure to call when the client wants to be restarted. - /// - /// The parameter passed to the callback is the name of the new chain spec to use after - /// the restart. - pub fn set_exit_handler(&self, f: F) where F: Fn(String) + 'static + Send { - *self.exit_handler.lock() = Some(Box::new(f)); - } - - /// Returns engine reference. - pub fn engine(&self) -> &EthEngine { - &*self.engine - } - - fn notify(&self, f: F) where F: Fn(&ChainNotify) { - for np in &*self.notify.read() { - if let Some(n) = np.upgrade() { - f(&*n); - } - } - } - - /// Register an action to be done if a mode/spec_name change happens. - pub fn on_user_defaults_change(&self, f: F) where F: 'static + FnMut(Option) + Send { - *self.on_user_defaults_change.lock() = Some(Box::new(f)); - } - - /// Flush the block import queue. - pub fn flush_queue(&self) { - self.importer.block_queue.flush(); - while !self.importer.block_queue.is_empty() { - self.import_verified_blocks(); - } - } - - /// The env info as of the best block. - pub fn latest_env_info(&self) -> EnvInfo { - self.env_info(BlockId::Latest).expect("Best block header always stored; qed") - } - - /// The env info as of a given block. - /// returns `None` if the block unknown. - pub fn env_info(&self, id: BlockId) -> Option { - self.block_header(id).map(|header| { - EnvInfo { - number: header.number(), - author: header.author(), - timestamp: header.timestamp(), - difficulty: header.difficulty(), - last_hashes: self.build_last_hashes(&header.parent_hash()), - gas_used: U256::default(), - gas_limit: header.gas_limit(), - } - }) - } - - fn build_last_hashes(&self, parent_hash: &H256) -> Arc { - { - let hashes = self.last_hashes.read(); - if hashes.front().map_or(false, |h| h == parent_hash) { - let mut res = Vec::from(hashes.clone()); - res.resize(256, H256::default()); - return Arc::new(res); - } - } - let mut last_hashes = LastHashes::new(); - last_hashes.resize(256, H256::default()); - last_hashes[0] = parent_hash.clone(); - let chain = self.chain.read(); - for i in 0..255 { - match chain.block_details(&last_hashes[i]) { - Some(details) => { - last_hashes[i + 1] = details.parent.clone(); - }, - None => break, - } - } - let mut cached_hashes = self.last_hashes.write(); - *cached_hashes = VecDeque::from(last_hashes.clone()); - Arc::new(last_hashes) - } - - /// This is triggered by a message coming from a block queue when the block is ready for insertion - pub fn import_verified_blocks(&self) -> usize { - self.importer.import_verified_blocks(self) - } - - // use a state-proving closure for the given block. - fn with_proving_caller(&self, id: BlockId, with_call: F) -> T - where F: FnOnce(&::machine::Call) -> T - { - let call = |a, d| { - let tx = self.contract_call_tx(id, a, d); - let (result, items) = self.prove_transaction(tx, id) - .ok_or_else(|| format!("Unable to make call. State unavailable?"))?; - - let items = items.into_iter().map(|x| x.to_vec()).collect(); - Ok((result, items)) - }; - - with_call(&call) - } - - // prune ancient states until below the memory limit or only the minimum amount remain. - fn prune_ancient(&self, mut state_db: StateDB, chain: &BlockChain) -> Result<(), ::error::Error> { - let number = match state_db.journal_db().latest_era() { - Some(n) => n, - None => return Ok(()), - }; - - // prune all ancient eras until we're below the memory target, - // but have at least the minimum number of states. - loop { - let needs_pruning = state_db.journal_db().is_pruned() && - state_db.journal_db().journal_size() >= self.config.history_mem; - - if !needs_pruning { break } - match state_db.journal_db().earliest_era() { - Some(era) if era + self.history <= number => { - trace!(target: "client", "Pruning state for ancient era {}", era); - match chain.block_hash(era) { - Some(ancient_hash) => { - let mut batch = DBTransaction::new(); - state_db.mark_canonical(&mut batch, era, &ancient_hash)?; - self.db.read().key_value().write_buffered(batch); - state_db.journal_db().flush(); - } - None => - debug!(target: "client", "Missing expected hash for block {}", era), - } - } - _ => break, // means that every era is kept, no pruning necessary. - } - } - - Ok(()) - } - - fn update_last_hashes(&self, parent: &H256, hash: &H256) { - let mut hashes = self.last_hashes.write(); - if hashes.front().map_or(false, |h| h == parent) { - if hashes.len() > 255 { - hashes.pop_back(); - } - hashes.push_front(hash.clone()); - } - } - - /// Get shared miner reference. - #[cfg(test)] - pub fn miner(&self) -> Arc { - self.importer.miner.clone() - } - - #[cfg(test)] - pub fn state_db(&self) -> ::parking_lot::RwLockReadGuard { - self.state_db.read() - } - - #[cfg(test)] - pub fn chain(&self) -> Arc { - self.chain.read().clone() - } - - /// Replace io channel. Useful for testing. - pub fn set_io_channel(&self, io_channel: IoChannel) { - *self.io_channel.write() = io_channel; - } - - /// Get a copy of the best block's state. - pub fn latest_state(&self) -> State { - let header = self.best_block_header(); - State::from_existing( - self.state_db.read().boxed_clone_canon(&header.hash()), - *header.state_root(), - self.engine.account_start_nonce(header.number()), - self.factories.clone() - ) - .expect("State root of best block header always valid.") - } - - /// Attempt to get a copy of a specific block's final state. - /// - /// This will not fail if given BlockId::Latest. - /// Otherwise, this can fail (but may not) if the DB prunes state or the block - /// is unknown. - pub fn state_at(&self, id: BlockId) -> Option> { - // fast path for latest state. - match id.clone() { - BlockId::Latest => return Some(self.latest_state()), - _ => {}, - } - - let block_number = match self.block_number(id) { - Some(num) => num, - None => return None, - }; - - self.block_header(id).and_then(|header| { - let db = self.state_db.read().boxed_clone(); - - // early exit for pruned blocks - if db.is_pruned() && self.pruning_info().earliest_state > block_number { - return None; - } - - let root = header.state_root(); - State::from_existing(db, root, self.engine.account_start_nonce(block_number), self.factories.clone()).ok() - }) - } - - /// Attempt to get a copy of a specific block's beginning state. - /// - /// This will not fail if given BlockId::Latest. - /// Otherwise, this can fail (but may not) if the DB prunes state. - pub fn state_at_beginning(&self, id: BlockId) -> Option> { - match self.block_number(id) { - None => None, - Some(0) => self.state_at(id), - Some(n) => self.state_at(BlockId::Number(n - 1)), - } - } - - /// Get a copy of the best block's state. - pub fn state(&self) -> Box { - Box::new(self.latest_state()) as Box<_> - } - - /// Get info on the cache. - pub fn blockchain_cache_info(&self) -> BlockChainCacheSize { - self.chain.read().cache_size() - } - - /// Get the report. - pub fn report(&self) -> ClientReport { - let mut report = self.report.read().clone(); - report.state_db_mem = self.state_db.read().mem_used(); - report - } - - /// Tick the client. - // TODO: manage by real events. - pub fn tick(&self, prevent_sleep: bool) { - self.check_garbage(); - if !prevent_sleep { - self.check_snooze(); - } - } - - fn check_garbage(&self) { - self.chain.read().collect_garbage(); - self.importer.block_queue.collect_garbage(); - self.tracedb.read().collect_garbage(); - } - - fn check_snooze(&self) { - let mode = self.mode.lock().clone(); - match mode { - Mode::Dark(timeout) => { - let mut ss = self.sleep_state.lock(); - if let Some(t) = ss.last_activity { - if Instant::now() > t + timeout { - self.sleep(); - ss.last_activity = None; - } - } - } - Mode::Passive(timeout, wakeup_after) => { - let mut ss = self.sleep_state.lock(); - let now = Instant::now(); - if let Some(t) = ss.last_activity { - if now > t + timeout { - self.sleep(); - ss.last_activity = None; - ss.last_autosleep = Some(now); - } - } - if let Some(t) = ss.last_autosleep { - if now > t + wakeup_after { - self.wake_up(); - ss.last_activity = Some(now); - ss.last_autosleep = None; - } - } - } - _ => {} - } - } - - /// Take a snapshot at the given block. - /// If the ID given is "latest", this will default to 1000 blocks behind. - pub fn take_snapshot(&self, writer: W, at: BlockId, p: &snapshot::Progress) -> Result<(), EthcoreError> { - let db = self.state_db.read().journal_db().boxed_clone(); - let best_block_number = self.chain_info().best_block_number; - let block_number = self.block_number(at).ok_or_else(|| snapshot::Error::InvalidStartingBlock(at))?; - - if db.is_pruned() && self.pruning_info().earliest_state > block_number { - return Err(snapshot::Error::OldBlockPrunedDB.into()); - } - - let history = ::std::cmp::min(self.history, 1000); - - let start_hash = match at { - BlockId::Latest => { - let start_num = match db.earliest_era() { - Some(era) => ::std::cmp::max(era, best_block_number.saturating_sub(history)), - None => best_block_number.saturating_sub(history), - }; - - match self.block_hash(BlockId::Number(start_num)) { - Some(h) => h, - None => return Err(snapshot::Error::InvalidStartingBlock(at).into()), - } - } - _ => match self.block_hash(at) { - Some(hash) => hash, - None => return Err(snapshot::Error::InvalidStartingBlock(at).into()), - }, - }; - - let processing_threads = self.config.snapshot.processing_threads; - snapshot::take_snapshot(&*self.engine, &self.chain.read(), start_hash, db.as_hash_db(), writer, p, processing_threads)?; - - Ok(()) - } - - /// Ask the client what the history parameter is. - pub fn pruning_history(&self) -> u64 { - self.history - } - - fn block_hash(chain: &BlockChain, id: BlockId) -> Option { - match id { - BlockId::Hash(hash) => Some(hash), - BlockId::Number(number) => chain.block_hash(number), - BlockId::Earliest => chain.block_hash(0), - BlockId::Latest => Some(chain.best_block_hash()), - } - } - - fn transaction_address(&self, id: TransactionId) -> Option { - match id { - TransactionId::Hash(ref hash) => self.chain.read().transaction_address(hash), - TransactionId::Location(id, index) => Self::block_hash(&self.chain.read(), id).map(|hash| TransactionAddress { - block_hash: hash, - index: index, - }) - } - } - - fn wake_up(&self) { - if !self.liveness.load(AtomicOrdering::Relaxed) { - self.liveness.store(true, AtomicOrdering::Relaxed); - self.notify(|n| n.start()); - info!(target: "mode", "wake_up: Waking."); - } - } - - fn sleep(&self) { - if self.liveness.load(AtomicOrdering::Relaxed) { - // only sleep if the import queue is mostly empty. - if self.queue_info().total_queue_size() <= MAX_QUEUE_SIZE_TO_SLEEP_ON { - self.liveness.store(false, AtomicOrdering::Relaxed); - self.notify(|n| n.stop()); - info!(target: "mode", "sleep: Sleeping."); - } else { - info!(target: "mode", "sleep: Cannot sleep - syncing ongoing."); - // TODO: Consider uncommenting. - //(*self.sleep_state.lock()).last_activity = Some(Instant::now()); - } - } - } - - // transaction for calling contracts from services like engine. - // from the null sender, with 50M gas. - fn contract_call_tx(&self, block_id: BlockId, address: Address, data: Bytes) -> SignedTransaction { - let from = Address::default(); - transaction::Transaction { - nonce: self.nonce(&from, block_id).unwrap_or_else(|| self.engine.account_start_nonce(0)), - action: Action::Call(address), - gas: U256::from(50_000_000), - gas_price: U256::default(), - value: U256::default(), - data: data, - }.fake_sign(from) - } - - fn do_virtual_call( - machine: &::machine::EthereumMachine, - env_info: &EnvInfo, - state: &mut State, - t: &SignedTransaction, - analytics: CallAnalytics, - ) -> Result { - fn call( - state: &mut State, - env_info: &EnvInfo, - machine: &::machine::EthereumMachine, - state_diff: bool, - transaction: &SignedTransaction, - options: TransactOptions, - ) -> Result, CallError> where - T: trace::Tracer, - V: trace::VMTracer, - { - let options = options - .dont_check_nonce() - .save_output_from_contract(); - let original_state = if state_diff { Some(state.clone()) } else { None }; - let schedule = machine.schedule(env_info.number); - - let mut ret = Executive::new(state, env_info, &machine, &schedule).transact_virtual(transaction, options)?; - - if let Some(original) = original_state { - ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?); - } - Ok(ret) - } - - let state_diff = analytics.state_diffing; - - match (analytics.transaction_tracing, analytics.vm_tracing) { - (true, true) => call(state, env_info, machine, state_diff, t, TransactOptions::with_tracing_and_vm_tracing()), - (true, false) => call(state, env_info, machine, state_diff, t, TransactOptions::with_tracing()), - (false, true) => call(state, env_info, machine, state_diff, t, TransactOptions::with_vm_tracing()), - (false, false) => call(state, env_info, machine, state_diff, t, TransactOptions::with_no_tracing()), - } - } - - fn block_number_ref(&self, id: &BlockId) -> Option { - match *id { - BlockId::Number(number) => Some(number), - BlockId::Hash(ref hash) => self.chain.read().block_number(hash), - BlockId::Earliest => Some(0), - BlockId::Latest => Some(self.chain.read().best_block_number()), - } - } - - /// Retrieve a decoded header given `BlockId` - /// - /// This method optimizes access patterns for latest block header - /// to avoid excessive RLP encoding, decoding and hashing. - fn block_header_decoded(&self, id: BlockId) -> Option
{ - match id { - BlockId::Latest - => Some(self.chain.read().best_block_header()), - BlockId::Hash(ref hash) if hash == &self.chain.read().best_block_hash() - => Some(self.chain.read().best_block_header()), - BlockId::Number(number) if number == self.chain.read().best_block_number() - => Some(self.chain.read().best_block_header()), - _ => self.block_header(id).and_then(|h| h.decode().ok()) - } - } + MIN_HISTORY_SIZE + } else { + config.history + }; + + if !chain + .block_header_data(&chain.best_block_hash()) + .map_or(true, |h| state_db.journal_db().contains(&h.state_root())) + { + warn!( + "State root not found for block #{} ({:x})", + chain.best_block_number(), + chain.best_block_hash() + ); + } + + let engine = spec.engine.clone(); + + let awake = match config.mode { + Mode::Dark(..) | Mode::Off => false, + _ => true, + }; + + let importer = Importer::new(&config, engine.clone(), message_channel.clone(), miner)?; + + let registrar_address = engine + .additional_params() + .get("registrar") + .and_then(|s| Address::from_str(s).ok()); + if let Some(ref addr) = registrar_address { + trace!(target: "client", "Found registrar at {}", addr); + } + + let client = Arc::new(Client { + enabled: AtomicBool::new(true), + sleep_state: Mutex::new(SleepState::new(awake)), + liveness: AtomicBool::new(awake), + mode: Mutex::new(config.mode.clone()), + chain: RwLock::new(chain), + tracedb, + engine, + pruning: config.pruning.clone(), + db: RwLock::new(db.clone()), + state_db: RwLock::new(state_db), + report: RwLock::new(Default::default()), + io_channel: RwLock::new(message_channel), + notify: RwLock::new(Vec::new()), + queue_transactions: IoChannelQueue::new(config.transaction_verification_queue_size), + queue_ancient_blocks: IoChannelQueue::new(MAX_ANCIENT_BLOCKS_QUEUE_SIZE), + queued_ancient_blocks: Default::default(), + ancient_blocks_import_lock: Default::default(), + queue_consensus_message: IoChannelQueue::new(usize::max_value()), + last_hashes: RwLock::new(VecDeque::new()), + factories, + history, + on_user_defaults_change: Mutex::new(None), + registrar_address, + exit_handler: Mutex::new(None), + importer, + config, + }); + + // prune old states. + { + let state_db = client.state_db.read().boxed_clone(); + let chain = client.chain.read(); + client.prune_ancient(state_db, &chain)?; + } + + // ensure genesis epoch proof in the DB. + { + let chain = client.chain.read(); + let gh = spec.genesis_header(); + if chain.epoch_transition(0, gh.hash()).is_none() { + trace!(target: "client", "No genesis transition found."); + + let proof = client.with_proving_caller(BlockId::Number(0), |call| { + client.engine.genesis_epoch_data(&gh, call) + }); + let proof = match proof { + Ok(proof) => proof, + Err(e) => { + warn!(target: "client", "Error generating genesis epoch data: {}. Snapshots generated may not be complete.", e); + Vec::new() + } + }; + + debug!(target: "client", "Obtained genesis transition proof: {:?}", proof); + + let mut batch = DBTransaction::new(); + chain.insert_epoch_transition( + &mut batch, + 0, + EpochTransition { + block_hash: gh.hash(), + block_number: 0, + proof: proof, + }, + ); + + client.db.read().key_value().write_buffered(batch); + } + } + + // ensure buffered changes are flushed. + client.db.read().key_value().flush()?; + Ok(client) + } + + /// Wakes up client if it's a sleep. + pub fn keep_alive(&self) { + let should_wake = match *self.mode.lock() { + Mode::Dark(..) | Mode::Passive(..) => true, + _ => false, + }; + if should_wake { + self.wake_up(); + (*self.sleep_state.lock()).last_activity = Some(Instant::now()); + } + } + + /// Adds an actor to be notified on certain events + pub fn add_notify(&self, target: Arc) { + self.notify.write().push(Arc::downgrade(&target)); + } + + /// Returns engine reference. + pub fn engine(&self) -> &dyn EthEngine { + &*self.engine + } + + fn notify(&self, f: F) + where + F: Fn(&dyn ChainNotify), + { + for np in &*self.notify.read() { + if let Some(n) = np.upgrade() { + f(&*n); + } + } + } + + /// Register an action to be done if a mode/spec_name change happens. + pub fn on_user_defaults_change(&self, f: F) + where + F: 'static + FnMut(Option) + Send, + { + *self.on_user_defaults_change.lock() = Some(Box::new(f)); + } + + /// Flush the block import queue. + pub fn flush_queue(&self) { + self.importer.block_queue.flush(); + while !self.importer.block_queue.is_empty() { + self.import_verified_blocks(); + } + } + + /// The env info as of the best block. + pub fn latest_env_info(&self) -> EnvInfo { + self.env_info(BlockId::Latest) + .expect("Best block header always stored; qed") + } + + /// The env info as of a given block. + /// returns `None` if the block unknown. + pub fn env_info(&self, id: BlockId) -> Option { + self.block_header(id).map(|header| EnvInfo { + number: header.number(), + author: header.author(), + timestamp: header.timestamp(), + difficulty: header.difficulty(), + last_hashes: self.build_last_hashes(&header.parent_hash()), + gas_used: U256::default(), + gas_limit: header.gas_limit(), + }) + } + + fn build_last_hashes(&self, parent_hash: &H256) -> Arc { + { + let hashes = self.last_hashes.read(); + if hashes.front().map_or(false, |h| h == parent_hash) { + let mut res = Vec::from(hashes.clone()); + res.resize(256, H256::default()); + return Arc::new(res); + } + } + let mut last_hashes = LastHashes::new(); + last_hashes.resize(256, H256::default()); + last_hashes[0] = parent_hash.clone(); + let chain = self.chain.read(); + for i in 0..255 { + match chain.block_details(&last_hashes[i]) { + Some(details) => { + last_hashes[i + 1] = details.parent.clone(); + } + None => break, + } + } + let mut cached_hashes = self.last_hashes.write(); + *cached_hashes = VecDeque::from(last_hashes.clone()); + Arc::new(last_hashes) + } + + /// This is triggered by a message coming from a block queue when the block is ready for insertion + pub fn import_verified_blocks(&self) -> usize { + self.importer.import_verified_blocks(self) + } + + // use a state-proving closure for the given block. + fn with_proving_caller(&self, id: BlockId, with_call: F) -> T + where + F: FnOnce(&::machine::Call) -> T, + { + let call = |a, d| { + let tx = self.contract_call_tx(id, a, d); + let (result, items) = self + .prove_transaction(tx, id) + .ok_or_else(|| format!("Unable to make call. State unavailable?"))?; + + let items = items.into_iter().map(|x| x.to_vec()).collect(); + Ok((result, items)) + }; + + with_call(&call) + } + + // prune ancient states until below the memory limit or only the minimum amount remain. + fn prune_ancient( + &self, + mut state_db: StateDB, + chain: &BlockChain, + ) -> Result<(), ::error::Error> { + let number = match state_db.journal_db().latest_era() { + Some(n) => n, + None => return Ok(()), + }; + + // prune all ancient eras until we're below the memory target, + // but have at least the minimum number of states. + loop { + let needs_pruning = state_db.journal_db().is_pruned() + && state_db.journal_db().journal_size() >= self.config.history_mem; + + if !needs_pruning { + break; + } + match state_db.journal_db().earliest_era() { + Some(era) if era + self.history <= number => { + trace!(target: "client", "Pruning state for ancient era {}", era); + match chain.block_hash(era) { + Some(ancient_hash) => { + let mut batch = DBTransaction::new(); + state_db.mark_canonical(&mut batch, era, &ancient_hash)?; + self.db.read().key_value().write_buffered(batch); + state_db.journal_db().flush(); + } + None => debug!(target: "client", "Missing expected hash for block {}", era), + } + } + _ => break, // means that every era is kept, no pruning necessary. + } + } + + Ok(()) + } + + fn update_last_hashes(&self, parent: &H256, hash: &H256) { + let mut hashes = self.last_hashes.write(); + if hashes.front().map_or(false, |h| h == parent) { + if hashes.len() > 255 { + hashes.pop_back(); + } + hashes.push_front(hash.clone()); + } + } + + /// Get shared miner reference. + #[cfg(test)] + pub fn miner(&self) -> Arc { + self.importer.miner.clone() + } + + #[cfg(test)] + pub fn state_db(&self) -> ::parking_lot::RwLockReadGuard { + self.state_db.read() + } + + #[cfg(test)] + pub fn chain(&self) -> Arc { + self.chain.read().clone() + } + + /// Replace io channel. Useful for testing. + pub fn set_io_channel(&self, io_channel: IoChannel) { + *self.io_channel.write() = io_channel; + } + + /// Get a copy of the best block's state. + pub fn latest_state_and_header(&self) -> (State, Header) { + let header = self.best_block_header(); + let state = State::from_existing( + self.state_db.read().boxed_clone_canon(&header.hash()), + *header.state_root(), + self.engine.account_start_nonce(header.number()), + self.factories.clone(), + ) + .expect("State root of best block header always valid."); + (state, header) + } + + /// Attempt to get a copy of a specific block's final state. + /// + /// This will not fail if given BlockId::Latest. + /// Otherwise, this can fail (but may not) if the DB prunes state or the block + /// is unknown. + pub fn state_at(&self, id: BlockId) -> Option> { + // fast path for latest state. + if let BlockId::Latest = id { + let (state, _) = self.latest_state_and_header(); + return Some(state); + } + + let block_number = match self.block_number(id) { + Some(num) => num, + None => return None, + }; + + self.block_header(id).and_then(|header| { + let db = self.state_db.read().boxed_clone(); + + // early exit for pruned blocks + if db.is_pruned() && self.pruning_info().earliest_state > block_number { + return None; + } + + let root = header.state_root(); + State::from_existing( + db, + root, + self.engine.account_start_nonce(block_number), + self.factories.clone(), + ) + .ok() + }) + } + + /// Attempt to get a copy of a specific block's beginning state. + /// + /// This will not fail if given BlockId::Latest. + /// Otherwise, this can fail (but may not) if the DB prunes state. + pub fn state_at_beginning(&self, id: BlockId) -> Option> { + match self.block_number(id) { + None => None, + Some(0) => self.state_at(id), + Some(n) => self.state_at(BlockId::Number(n - 1)), + } + } + + /// Get a copy of the best block's state. + pub fn state(&self) -> impl StateInfo { + let (state, _) = self.latest_state_and_header(); + state + } + + /// Get info on the cache. + pub fn blockchain_cache_info(&self) -> BlockChainCacheSize { + self.chain.read().cache_size() + } + + /// Get the report. + pub fn report(&self) -> ClientReport { + let mut report = self.report.read().clone(); + self.state_db.read().get_sizes(&mut report.item_sizes); + report + } + + /// Tick the client. + // TODO: manage by real events. + pub fn tick(&self, prevent_sleep: bool) { + self.check_garbage(); + if !prevent_sleep { + self.check_snooze(); + } + } + + fn check_garbage(&self) { + self.chain.read().collect_garbage(); + self.importer.block_queue.collect_garbage(); + self.tracedb.read().collect_garbage(); + } + + fn check_snooze(&self) { + let mode = self.mode.lock().clone(); + match mode { + Mode::Dark(timeout) => { + let mut ss = self.sleep_state.lock(); + if let Some(t) = ss.last_activity { + if Instant::now() > t + timeout { + self.sleep(false); + ss.last_activity = None; + } + } + } + Mode::Passive(timeout, wakeup_after) => { + let mut ss = self.sleep_state.lock(); + let now = Instant::now(); + if let Some(t) = ss.last_activity { + if now > t + timeout { + self.sleep(false); + ss.last_activity = None; + ss.last_autosleep = Some(now); + } + } + if let Some(t) = ss.last_autosleep { + if now > t + wakeup_after { + self.wake_up(); + ss.last_activity = Some(now); + ss.last_autosleep = None; + } + } + } + _ => {} + } + } + + /// Take a snapshot at the given block. + /// If the ID given is "latest", this will default to 1000 blocks behind. + pub fn take_snapshot( + &self, + writer: W, + at: BlockId, + p: &snapshot::Progress, + ) -> Result<(), EthcoreError> { + let db = self.state_db.read().journal_db().boxed_clone(); + let best_block_number = self.chain_info().best_block_number; + let block_number = self + .block_number(at) + .ok_or_else(|| snapshot::Error::InvalidStartingBlock(at))?; + + if db.is_pruned() && self.pruning_info().earliest_state > block_number { + return Err(snapshot::Error::OldBlockPrunedDB.into()); + } + + let history = ::std::cmp::min(self.history, 1000); + + let start_hash = match at { + BlockId::Latest => { + let start_num = match db.earliest_era() { + Some(era) => ::std::cmp::max(era, best_block_number.saturating_sub(history)), + None => best_block_number.saturating_sub(history), + }; + + match self.block_hash(BlockId::Number(start_num)) { + Some(h) => h, + None => return Err(snapshot::Error::InvalidStartingBlock(at).into()), + } + } + _ => match self.block_hash(at) { + Some(hash) => hash, + None => return Err(snapshot::Error::InvalidStartingBlock(at).into()), + }, + }; + + let processing_threads = self.config.snapshot.processing_threads; + let chunker = self + .engine + .snapshot_components() + .ok_or(snapshot::Error::SnapshotsUnsupported)?; + snapshot::take_snapshot( + chunker, + &self.chain.read(), + start_hash, + db.as_hash_db(), + writer, + p, + processing_threads, + )?; + Ok(()) + } + + /// Ask the client what the history parameter is. + pub fn pruning_history(&self) -> u64 { + self.history + } + + fn block_hash(chain: &BlockChain, id: BlockId) -> Option { + match id { + BlockId::Hash(hash) => Some(hash), + BlockId::Number(number) => chain.block_hash(number), + BlockId::Earliest => chain.block_hash(0), + BlockId::Latest => Some(chain.best_block_hash()), + } + } + + fn transaction_address(&self, id: TransactionId) -> Option { + match id { + TransactionId::Hash(ref hash) => self.chain.read().transaction_address(hash), + TransactionId::Location(id, index) => { + Self::block_hash(&self.chain.read(), id).map(|hash| TransactionAddress { + block_hash: hash, + index: index, + }) + } + } + } + + fn wake_up(&self) { + if !self.liveness.load(AtomicOrdering::Relaxed) { + self.liveness.store(true, AtomicOrdering::Relaxed); + self.notify(|n| n.start()); + info!(target: "mode", "wake_up: Waking."); + } + } + + fn sleep(&self, force: bool) { + if self.liveness.load(AtomicOrdering::Relaxed) { + // only sleep if the import queue is mostly empty. + if force || (self.queue_info().total_queue_size() <= MAX_QUEUE_SIZE_TO_SLEEP_ON) { + self.liveness.store(false, AtomicOrdering::Relaxed); + self.notify(|n| n.stop()); + info!(target: "mode", "sleep: Sleeping."); + } else { + info!(target: "mode", "sleep: Cannot sleep - syncing ongoing."); + // TODO: Consider uncommenting. + //(*self.sleep_state.lock()).last_activity = Some(Instant::now()); + } + } + } + + // transaction for calling contracts from services like engine. + // from the null sender, with 50M gas. + fn contract_call_tx( + &self, + block_id: BlockId, + address: Address, + data: Bytes, + ) -> SignedTransaction { + let from = Address::default(); + transaction::Transaction { + nonce: self + .nonce(&from, block_id) + .unwrap_or_else(|| self.engine.account_start_nonce(0)), + action: Action::Call(address), + gas: U256::from(50_000_000), + gas_price: U256::default(), + value: U256::default(), + data: data, + } + .fake_sign(from) + } + + fn do_virtual_call( + machine: &::machine::EthereumMachine, + env_info: &EnvInfo, + state: &mut State, + t: &SignedTransaction, + analytics: CallAnalytics, + ) -> Result { + fn call( + state: &mut State, + env_info: &EnvInfo, + machine: &::machine::EthereumMachine, + state_diff: bool, + transaction: &SignedTransaction, + options: TransactOptions, + ) -> Result, CallError> + where + T: trace::Tracer, + V: trace::VMTracer, + { + let options = options.dont_check_nonce().save_output_from_contract(); + let original_state = if state_diff { + Some(state.clone()) + } else { + None + }; + let schedule = machine.schedule(env_info.number); + + let mut ret = Executive::new(state, env_info, &machine, &schedule) + .transact_virtual(transaction, options)?; + + if let Some(original) = original_state { + ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?); + } + Ok(ret) + } + + let state_diff = analytics.state_diffing; + + match (analytics.transaction_tracing, analytics.vm_tracing) { + (true, true) => call( + state, + env_info, + machine, + state_diff, + t, + TransactOptions::with_tracing_and_vm_tracing(), + ), + (true, false) => call( + state, + env_info, + machine, + state_diff, + t, + TransactOptions::with_tracing(), + ), + (false, true) => call( + state, + env_info, + machine, + state_diff, + t, + TransactOptions::with_vm_tracing(), + ), + (false, false) => call( + state, + env_info, + machine, + state_diff, + t, + TransactOptions::with_no_tracing(), + ), + } + } + + fn block_number_ref(&self, id: &BlockId) -> Option { + match *id { + BlockId::Number(number) => Some(number), + BlockId::Hash(ref hash) => self.chain.read().block_number(hash), + BlockId::Earliest => Some(0), + BlockId::Latest => Some(self.chain.read().best_block_number()), + } + } + + /// Retrieve a decoded header given `BlockId` + /// + /// This method optimizes access patterns for latest block header + /// to avoid excessive RLP encoding, decoding and hashing. + fn block_header_decoded(&self, id: BlockId) -> Option
{ + match id { + BlockId::Latest => Some(self.chain.read().best_block_header()), + BlockId::Hash(ref hash) if hash == &self.chain.read().best_block_hash() => { + Some(self.chain.read().best_block_header()) + } + BlockId::Number(number) if number == self.chain.read().best_block_number() => { + Some(self.chain.read().best_block_header()) + } + _ => self.block_header(id).and_then(|h| h.decode().ok()), + } + } } impl snapshot::DatabaseRestore for Client { - /// Restart the client with a new backend - fn restore_db(&self, new_db: &str) -> Result<(), EthcoreError> { - trace!(target: "snapshot", "Replacing client database with {:?}", new_db); - - let _import_lock = self.importer.import_lock.lock(); - let mut state_db = self.state_db.write(); - let mut chain = self.chain.write(); - let mut tracedb = self.tracedb.write(); - self.importer.miner.clear(); - let db = self.db.write(); - db.restore(new_db)?; - - let cache_size = state_db.cache_size(); - *state_db = StateDB::new(journaldb::new(db.key_value().clone(), self.pruning, ::db::COL_STATE), cache_size); - *chain = Arc::new(BlockChain::new(self.config.blockchain.clone(), &[], db.clone())); - *tracedb = TraceDB::new(self.config.tracing.clone(), db.clone(), chain.clone()); - Ok(()) - } + /// Restart the client with a new backend + fn restore_db(&self, new_db: &str) -> Result<(), EthcoreError> { + trace!(target: "snapshot", "Replacing client database with {:?}", new_db); + + let _import_lock = self.importer.import_lock.lock(); + let mut state_db = self.state_db.write(); + let mut chain = self.chain.write(); + let mut tracedb = self.tracedb.write(); + self.importer.miner.clear(); + let db = self.db.write(); + db.restore(new_db)?; + + let cache_size = state_db.cache_size(); + *state_db = StateDB::new( + journaldb::new(db.key_value().clone(), self.pruning, ::db::COL_STATE), + cache_size, + ); + *chain = Arc::new(BlockChain::new( + self.config.blockchain.clone(), + &[], + db.clone(), + )); + *tracedb = TraceDB::new(self.config.tracing.clone(), db.clone(), chain.clone()); + Ok(()) + } } impl BlockChainReset for Client { - fn reset(&self, num: u32) -> Result<(), String> { - if num as u64 > self.pruning_history() { - return Err("Attempting to reset to block with pruned state".into()) - } - - let (blocks_to_delete, best_block_hash) = self.chain.read() - .block_headers_from_best_block(num) - .ok_or("Attempted to reset past genesis block")?; - - let mut db_transaction = DBTransaction::with_capacity((num + 1) as usize); - - for hash in &blocks_to_delete { - db_transaction.delete(::db::COL_HEADERS, &hash.hash()); - db_transaction.delete(::db::COL_BODIES, &hash.hash()); - db_transaction.delete(::db::COL_EXTRA, &hash.hash()); - Writable::delete:: - (&mut db_transaction, ::db::COL_EXTRA, &hash.number()); - } - - // update the new best block hash - db_transaction.put(::db::COL_EXTRA, b"best", &*best_block_hash); - - self.db.read() - .key_value() - .write(db_transaction) - .map_err(|err| format!("could not complete reset operation; io error occured: {}", err))?; - - let hashes = blocks_to_delete.iter().map(|b| b.hash()).collect::>(); - - info!("Deleting block hashes {}", - Colour::Red - .bold() - .paint(format!("{:#?}", hashes)) - ); - - info!("New best block hash {}", Colour::Green.bold().paint(format!("{:?}", best_block_hash))); - - Ok(()) - } + fn reset(&self, num: u32) -> Result<(), String> { + if num as u64 > self.pruning_history() { + return Err("Attempting to reset to block with pruned state".into()); + } else if num == 0 { + return Err("invalid number of blocks to reset".into()); + } + + let mut blocks_to_delete = Vec::with_capacity(num as usize); + let mut best_block_hash = self.chain.read().best_block_hash(); + let mut batch = DBTransaction::with_capacity(blocks_to_delete.len()); + + for _ in 0..num { + let current_header = self + .chain + .read() + .block_header_data(&best_block_hash) + .expect( + "best_block_hash was fetched from db; block_header_data should exist in db; qed", + ); + best_block_hash = current_header.parent_hash(); + + let (number, hash) = (current_header.number(), current_header.hash()); + batch.delete(::db::COL_HEADERS, &hash); + batch.delete(::db::COL_BODIES, &hash); + Writable::delete::(&mut batch, ::db::COL_EXTRA, &hash); + Writable::delete::(&mut batch, ::db::COL_EXTRA, &number); + + blocks_to_delete.push((number, hash)); + } + + let hashes = blocks_to_delete + .iter() + .map(|(_, hash)| hash) + .collect::>(); + info!( + "Deleting block hashes {}", + Colour::Red.bold().paint(format!("{:#?}", hashes)) + ); + + let mut best_block_details = Readable::read::( + &**self.db.read().key_value(), + ::db::COL_EXTRA, + &best_block_hash, + ) + .expect("block was previously imported; best_block_details should exist; qed"); + + let (_, last_hash) = blocks_to_delete + .last() + .expect("num is > 0; blocks_to_delete can't be empty; qed"); + // remove the last block as a child so that it can be re-imported + // ethcore/blockchain/src/blockchain.rs/Blockchain::is_known_child() + best_block_details.children.retain(|h| *h != *last_hash); + batch.write(::db::COL_EXTRA, &best_block_hash, &best_block_details); + // update the new best block hash + batch.put(::db::COL_EXTRA, b"best", &best_block_hash); + + self.db + .read() + .key_value() + .write(batch) + .map_err(|err| format!("could not delete blocks; io error occurred: {}", err))?; + + info!( + "New best block hash {}", + Colour::Green.bold().paint(format!("{:?}", best_block_hash)) + ); + + Ok(()) + } } impl Nonce for Client { - fn nonce(&self, address: &Address, id: BlockId) -> Option { - self.state_at(id).and_then(|s| s.nonce(address).ok()) - } + fn nonce(&self, address: &Address, id: BlockId) -> Option { + self.state_at(id).and_then(|s| s.nonce(address).ok()) + } } impl Balance for Client { - fn balance(&self, address: &Address, state: StateOrBlock) -> Option { - match state { - StateOrBlock::State(s) => s.balance(address).ok(), - StateOrBlock::Block(id) => self.state_at(id).and_then(|s| s.balance(address).ok()) - } - } + fn balance(&self, address: &Address, state: StateOrBlock) -> Option { + match state { + StateOrBlock::State(s) => s.balance(address).ok(), + StateOrBlock::Block(id) => self.state_at(id).and_then(|s| s.balance(address).ok()), + } + } } impl AccountData for Client {} impl ChainInfo for Client { - fn chain_info(&self) -> BlockChainInfo { - let mut chain_info = self.chain.read().chain_info(); - chain_info.pending_total_difficulty = chain_info.total_difficulty + self.importer.block_queue.total_difficulty(); - chain_info - } + fn chain_info(&self) -> BlockChainInfo { + let mut chain_info = self.chain.read().chain_info(); + chain_info.pending_total_difficulty = + chain_info.total_difficulty + self.importer.block_queue.total_difficulty(); + chain_info + } } impl BlockInfo for Client { - fn block_header(&self, id: BlockId) -> Option { - let chain = self.chain.read(); + fn block_header(&self, id: BlockId) -> Option { + let chain = self.chain.read(); - Self::block_hash(&chain, id).and_then(|hash| chain.block_header_data(&hash)) - } + Self::block_hash(&chain, id).and_then(|hash| chain.block_header_data(&hash)) + } - fn best_block_header(&self) -> Header { - self.chain.read().best_block_header() - } + fn best_block_header(&self) -> Header { + self.chain.read().best_block_header() + } - fn block(&self, id: BlockId) -> Option { - let chain = self.chain.read(); + fn block(&self, id: BlockId) -> Option { + let chain = self.chain.read(); - Self::block_hash(&chain, id).and_then(|hash| chain.block(&hash)) - } + Self::block_hash(&chain, id).and_then(|hash| chain.block(&hash)) + } - fn code_hash(&self, address: &Address, id: BlockId) -> Option { - self.state_at(id).and_then(|s| s.code_hash(address).unwrap_or(None)) - } + fn code_hash(&self, address: &Address, id: BlockId) -> Option { + self.state_at(id) + .and_then(|s| s.code_hash(address).unwrap_or(None)) + } } impl TransactionInfo for Client { - fn transaction_block(&self, id: TransactionId) -> Option { - self.transaction_address(id).map(|addr| addr.block_hash) - } + fn transaction_block(&self, id: TransactionId) -> Option { + self.transaction_address(id).map(|addr| addr.block_hash) + } } impl BlockChainTrait for Client {} impl RegistryInfo for Client { - fn registry_address(&self, name: String, block: BlockId) -> Option
{ - use ethabi::FunctionOutputDecoder; - - let address = self.registrar_address?; - - let (data, decoder) = registry::functions::get_address::call(keccak(name.as_bytes()), "A"); - let value = decoder.decode(&self.call_contract(block, address, data).ok()?).ok()?; - if value.is_zero() { - None - } else { - Some(value) - } - } + fn registry_address(&self, name: String, block: BlockId) -> Option
{ + use ethabi::FunctionOutputDecoder; + + let address = self.registrar_address?; + + let (data, decoder) = registry::functions::get_address::call(keccak(name.as_bytes()), "A"); + let value = decoder + .decode(&self.call_contract(block, address, data).ok()?) + .ok()?; + if value.is_zero() { + None + } else { + Some(value) + } + } } impl CallContract for Client { - fn call_contract(&self, block_id: BlockId, address: Address, data: Bytes) -> Result { - let state_pruned = || CallError::StatePruned.to_string(); - let state = &mut self.state_at(block_id).ok_or_else(&state_pruned)?; - let header = self.block_header_decoded(block_id).ok_or_else(&state_pruned)?; - - let transaction = self.contract_call_tx(block_id, address, data); - - self.call(&transaction, Default::default(), state, &header) - .map_err(|e| format!("{:?}", e)) - .map(|executed| executed.output) - } + fn call_contract( + &self, + block_id: BlockId, + address: Address, + data: Bytes, + ) -> Result { + let state_pruned = || CallError::StatePruned.to_string(); + let state = &mut self.state_at(block_id).ok_or_else(&state_pruned)?; + let header = self + .block_header_decoded(block_id) + .ok_or_else(&state_pruned)?; + + let transaction = self.contract_call_tx(block_id, address, data); + + self.call(&transaction, Default::default(), state, &header) + .map_err(|e| format!("{:?}", e)) + .map(|executed| executed.output) + } } impl ImportBlock for Client { - fn import_block(&self, unverified: Unverified) -> EthcoreResult { - if self.chain.read().is_known(&unverified.hash()) { - bail!(EthcoreErrorKind::Import(ImportErrorKind::AlreadyInChain)); - } - - let status = self.block_status(BlockId::Hash(unverified.parent_hash())); - if status == BlockStatus::Unknown { - bail!(EthcoreErrorKind::Block(BlockError::UnknownParent(unverified.parent_hash()))); - } - - let raw = if self.importer.block_queue.is_empty() { - Some(( - unverified.bytes.clone(), - unverified.header.hash(), - *unverified.header.difficulty(), - )) - } else { None }; - - match self.importer.block_queue.import(unverified) { - Ok(hash) => { - if let Some((raw, hash, difficulty)) = raw { - self.notify(move |n| n.block_pre_import(&raw, &hash, &difficulty)); - } - Ok(hash) - }, - // we only care about block errors (not import errors) - Err((block, EthcoreError(EthcoreErrorKind::Block(err), _))) => { - self.importer.bad_blocks.report(block.bytes, format!("{:?}", err)); - bail!(EthcoreErrorKind::Block(err)) - }, - Err((_, e)) => Err(e), - } - } + fn import_block(&self, unverified: Unverified) -> EthcoreResult { + if self.chain.read().is_known(&unverified.hash()) { + bail!(EthcoreErrorKind::Import(ImportErrorKind::AlreadyInChain)); + } + + let status = self.block_status(BlockId::Hash(unverified.parent_hash())); + if status == BlockStatus::Unknown { + bail!(EthcoreErrorKind::Block(BlockError::UnknownParent( + unverified.parent_hash() + ))); + } + + let raw = if self.importer.block_queue.is_empty() { + Some(( + unverified.bytes.clone(), + unverified.header.hash(), + *unverified.header.difficulty(), + )) + } else { + None + }; + + match self.importer.block_queue.import(unverified) { + Ok(hash) => { + if let Some((raw, hash, difficulty)) = raw { + self.notify(move |n| n.block_pre_import(&raw, &hash, &difficulty)); + } + Ok(hash) + } + // we only care about block errors (not import errors) + Err((Some(block), EthcoreError(EthcoreErrorKind::Block(err), _))) => { + self.importer + .bad_blocks + .report(block.bytes, err.to_string()); + bail!(EthcoreErrorKind::Block(err)) + } + Err((None, EthcoreError(EthcoreErrorKind::Block(err), _))) => { + error!(target: "client", "BlockError {} detected but it was missing raw_bytes of the block", err); + bail!(EthcoreErrorKind::Block(err)) + } + Err((_, e)) => Err(e), + } + } } impl StateClient for Client { - type State = State<::state_db::StateDB>; + type State = State<::state_db::StateDB>; - fn latest_state(&self) -> Self::State { - Client::latest_state(self) - } + fn latest_state_and_header(&self) -> (Self::State, Header) { + Client::latest_state_and_header(self) + } - fn state_at(&self, id: BlockId) -> Option { - Client::state_at(self, id) - } + fn state_at(&self, id: BlockId) -> Option { + Client::state_at(self, id) + } } impl Call for Client { - type State = State<::state_db::StateDB>; - - fn call(&self, transaction: &SignedTransaction, analytics: CallAnalytics, state: &mut Self::State, header: &Header) -> Result { - let env_info = EnvInfo { - number: header.number(), - author: header.author().clone(), - timestamp: header.timestamp(), - difficulty: header.difficulty().clone(), - last_hashes: self.build_last_hashes(header.parent_hash()), - gas_used: U256::default(), - gas_limit: U256::max_value(), - }; - let machine = self.engine.machine(); - - Self::do_virtual_call(&machine, &env_info, state, transaction, analytics) - } - - fn call_many(&self, transactions: &[(SignedTransaction, CallAnalytics)], state: &mut Self::State, header: &Header) -> Result, CallError> { - let mut env_info = EnvInfo { - number: header.number(), - author: header.author().clone(), - timestamp: header.timestamp(), - difficulty: header.difficulty().clone(), - last_hashes: self.build_last_hashes(header.parent_hash()), - gas_used: U256::default(), - gas_limit: U256::max_value(), - }; - - let mut results = Vec::with_capacity(transactions.len()); - let machine = self.engine.machine(); - - for &(ref t, analytics) in transactions { - let ret = Self::do_virtual_call(machine, &env_info, state, t, analytics)?; - env_info.gas_used = ret.cumulative_gas_used; - results.push(ret); - } - - Ok(results) - } - - fn estimate_gas(&self, t: &SignedTransaction, state: &Self::State, header: &Header) -> Result { - let (mut upper, max_upper, env_info) = { - let init = *header.gas_limit(); - let max = init * U256::from(10); - - let env_info = EnvInfo { - number: header.number(), - author: header.author().clone(), - timestamp: header.timestamp(), - difficulty: header.difficulty().clone(), - last_hashes: self.build_last_hashes(header.parent_hash()), - gas_used: U256::default(), - gas_limit: max, - }; - - (init, max, env_info) - }; - - let sender = t.sender(); - let options = || TransactOptions::with_tracing().dont_check_nonce(); - - let exec = |gas| { - let mut tx = t.as_unsigned().clone(); - tx.gas = gas; - let tx = tx.fake_sign(sender); - - let mut clone = state.clone(); - let machine = self.engine.machine(); - let schedule = machine.schedule(env_info.number); - Executive::new(&mut clone, &env_info, &machine, &schedule) - .transact_virtual(&tx, options()) - }; - - let cond = |gas| { - exec(gas) - .ok() - .map_or(false, |r| r.exception.is_none()) - }; - - if !cond(upper) { - upper = max_upper; - match exec(upper) { - Ok(v) => { - if let Some(exception) = v.exception { - return Err(CallError::Exceptional(exception)) - } - }, - Err(_e) => { - trace!(target: "estimate_gas", "estimate_gas failed with {}", upper); - let err = ExecutionError::Internal(format!("Requires higher than upper limit of {}", upper)); - return Err(err.into()) - } - } - } - let lower = t.gas_required(&self.engine.schedule(env_info.number)).into(); - if cond(lower) { - trace!(target: "estimate_gas", "estimate_gas succeeded with {}", lower); - return Ok(lower) - } - - /// Find transition point between `lower` and `upper` where `cond` changes from `false` to `true`. - /// Returns the lowest value between `lower` and `upper` for which `cond` returns true. - /// We assert: `cond(lower) = false`, `cond(upper) = true` - fn binary_chop(mut lower: U256, mut upper: U256, mut cond: F) -> Result - where F: FnMut(U256) -> bool - { - while upper - lower > 1.into() { - let mid = (lower + upper) / 2; - trace!(target: "estimate_gas", "{} .. {} .. {}", lower, mid, upper); - let c = cond(mid); - match c { - true => upper = mid, - false => lower = mid, - }; - trace!(target: "estimate_gas", "{} => {} .. {}", c, lower, upper); - } - Ok(upper) - } - - // binary chop to non-excepting call with gas somewhere between 21000 and block gas limit - trace!(target: "estimate_gas", "estimate_gas chopping {} .. {}", lower, upper); - binary_chop(lower, upper, cond) - } + type State = State<::state_db::StateDB>; + + fn call( + &self, + transaction: &SignedTransaction, + analytics: CallAnalytics, + state: &mut Self::State, + header: &Header, + ) -> Result { + let env_info = EnvInfo { + number: header.number(), + author: header.author().clone(), + timestamp: header.timestamp(), + difficulty: header.difficulty().clone(), + last_hashes: self.build_last_hashes(header.parent_hash()), + gas_used: U256::default(), + gas_limit: U256::max_value(), + }; + let machine = self.engine.machine(); + + Self::do_virtual_call(&machine, &env_info, state, transaction, analytics) + } + + fn call_many( + &self, + transactions: &[(SignedTransaction, CallAnalytics)], + state: &mut Self::State, + header: &Header, + ) -> Result, CallError> { + let mut env_info = EnvInfo { + number: header.number(), + author: header.author().clone(), + timestamp: header.timestamp(), + difficulty: header.difficulty().clone(), + last_hashes: self.build_last_hashes(header.parent_hash()), + gas_used: U256::default(), + gas_limit: U256::max_value(), + }; + + let mut results = Vec::with_capacity(transactions.len()); + let machine = self.engine.machine(); + + for &(ref t, analytics) in transactions { + let ret = Self::do_virtual_call(machine, &env_info, state, t, analytics)?; + env_info.gas_used = ret.cumulative_gas_used; + results.push(ret); + } + + Ok(results) + } + + fn estimate_gas( + &self, + t: &SignedTransaction, + state: &Self::State, + header: &Header, + ) -> Result { + let (mut upper, max_upper, env_info) = { + let init = *header.gas_limit(); + let max = init * U256::from(10); + + let env_info = EnvInfo { + number: header.number(), + author: header.author().clone(), + timestamp: header.timestamp(), + difficulty: header.difficulty().clone(), + last_hashes: self.build_last_hashes(header.parent_hash()), + gas_used: U256::default(), + gas_limit: max, + }; + + (init, max, env_info) + }; + + let sender = t.sender(); + let options = || TransactOptions::with_tracing().dont_check_nonce(); + + let exec = |gas| { + let mut tx = t.as_unsigned().clone(); + tx.gas = gas; + let tx = tx.fake_sign(sender); + + let mut clone = state.clone(); + let machine = self.engine.machine(); + let schedule = machine.schedule(env_info.number); + Executive::new(&mut clone, &env_info, &machine, &schedule) + .transact_virtual(&tx, options()) + }; + + let cond = |gas| exec(gas).ok().map_or(false, |r| r.exception.is_none()); + + if !cond(upper) { + upper = max_upper; + match exec(upper) { + Ok(v) => { + if let Some(exception) = v.exception { + return Err(CallError::Exceptional(exception)); + } + } + Err(_e) => { + trace!(target: "estimate_gas", "estimate_gas failed with {}", upper); + let err = ExecutionError::Internal(format!( + "Requires higher than upper limit of {}", + upper + )); + return Err(err.into()); + } + } + } + let lower = t + .gas_required(&self.engine.schedule(env_info.number)) + .into(); + if cond(lower) { + trace!(target: "estimate_gas", "estimate_gas succeeded with {}", lower); + return Ok(lower); + } + + /// Find transition point between `lower` and `upper` where `cond` changes from `false` to `true`. + /// Returns the lowest value between `lower` and `upper` for which `cond` returns true. + /// We assert: `cond(lower) = false`, `cond(upper) = true` + fn binary_chop(mut lower: U256, mut upper: U256, mut cond: F) -> Result + where + F: FnMut(U256) -> bool, + { + while upper - lower > 1.into() { + let mid = (lower + upper) / 2; + trace!(target: "estimate_gas", "{} .. {} .. {}", lower, mid, upper); + let c = cond(mid); + match c { + true => upper = mid, + false => lower = mid, + }; + trace!(target: "estimate_gas", "{} => {} .. {}", c, lower, upper); + } + Ok(upper) + } + + // binary chop to non-excepting call with gas somewhere between 21000 and block gas limit + trace!(target: "estimate_gas", "estimate_gas chopping {} .. {}", lower, upper); + binary_chop(lower, upper, cond) + } } impl EngineInfo for Client { - fn engine(&self) -> &EthEngine { - Client::engine(self) - } + fn engine(&self) -> &dyn EthEngine { + Client::engine(self) + } } impl BadBlocks for Client { - fn bad_blocks(&self) -> Vec<(Unverified, String)> { - self.importer.bad_blocks.bad_blocks() - } + fn bad_blocks(&self) -> Vec<(Unverified, String)> { + self.importer.bad_blocks.bad_blocks() + } } impl BlockChainClient for Client { - fn replay(&self, id: TransactionId, analytics: CallAnalytics) -> Result { - let address = self.transaction_address(id).ok_or(CallError::TransactionNotFound)?; - let block = BlockId::Hash(address.block_hash); - - const PROOF: &'static str = "The transaction address contains a valid index within block; qed"; - Ok(self.replay_block_transactions(block, analytics)?.nth(address.index).expect(PROOF).1) - } - - fn replay_block_transactions(&self, block: BlockId, analytics: CallAnalytics) -> Result>, CallError> { - let mut env_info = self.env_info(block).ok_or(CallError::StatePruned)?; - let body = self.block_body(block).ok_or(CallError::StatePruned)?; - let mut state = self.state_at_beginning(block).ok_or(CallError::StatePruned)?; - let txs = body.transactions(); - let engine = self.engine.clone(); - - const PROOF: &'static str = "Transactions fetched from blockchain; blockchain transactions are valid; qed"; - const EXECUTE_PROOF: &'static str = "Transaction replayed; qed"; - - Ok(Box::new(txs.into_iter() - .map(move |t| { - let transaction_hash = t.hash(); - let t = SignedTransaction::new(t).expect(PROOF); - let machine = engine.machine(); - let x = Self::do_virtual_call(machine, &env_info, &mut state, &t, analytics).expect(EXECUTE_PROOF); - env_info.gas_used = env_info.gas_used + x.gas_used; - (transaction_hash, x) - }))) - } - - fn mode(&self) -> Mode { - let r = self.mode.lock().clone().into(); - trace!(target: "mode", "Asked for mode = {:?}. returning {:?}", &*self.mode.lock(), r); - r - } - - fn disable(&self) { - self.set_mode(Mode::Off); - self.enabled.store(false, AtomicOrdering::Relaxed); - self.clear_queue(); - } - - fn set_mode(&self, new_mode: Mode) { - trace!(target: "mode", "Client::set_mode({:?})", new_mode); - if !self.enabled.load(AtomicOrdering::Relaxed) { - return; - } - { - let mut mode = self.mode.lock(); - *mode = new_mode.clone().into(); - trace!(target: "mode", "Mode now {:?}", &*mode); - if let Some(ref mut f) = *self.on_user_defaults_change.lock() { - trace!(target: "mode", "Making callback..."); - f(Some((&*mode).clone())) - } - } - match new_mode { - Mode::Active => self.wake_up(), - Mode::Off => self.sleep(), - _ => {(*self.sleep_state.lock()).last_activity = Some(Instant::now()); } - } - } - - fn spec_name(&self) -> String { - self.config.spec_name.clone() - } - - fn set_spec_name(&self, new_spec_name: String) -> Result<(), ()> { - trace!(target: "mode", "Client::set_spec_name({:?})", new_spec_name); - if !self.enabled.load(AtomicOrdering::Relaxed) { - return Err(()); - } - if let Some(ref h) = *self.exit_handler.lock() { - (*h)(new_spec_name); - Ok(()) - } else { - warn!("Not hypervised; cannot change chain."); - Err(()) - } - } - - fn block_number(&self, id: BlockId) -> Option { - self.block_number_ref(&id) - } - - fn block_body(&self, id: BlockId) -> Option { - let chain = self.chain.read(); - - Self::block_hash(&chain, id).and_then(|hash| chain.block_body(&hash)) - } - - fn block_status(&self, id: BlockId) -> BlockStatus { - let chain = self.chain.read(); - match Self::block_hash(&chain, id) { - Some(ref hash) if chain.is_known(hash) => BlockStatus::InChain, - Some(hash) => self.importer.block_queue.status(&hash).into(), - None => BlockStatus::Unknown - } - } - - fn block_total_difficulty(&self, id: BlockId) -> Option { - let chain = self.chain.read(); - - Self::block_hash(&chain, id).and_then(|hash| chain.block_details(&hash)).map(|d| d.total_difficulty) - } - - fn storage_root(&self, address: &Address, id: BlockId) -> Option { - self.state_at(id).and_then(|s| s.storage_root(address).ok()).and_then(|x| x) - } - - fn block_hash(&self, id: BlockId) -> Option { - let chain = self.chain.read(); - Self::block_hash(&chain, id) - } - - fn code(&self, address: &Address, state: StateOrBlock) -> Option> { - let result = match state { - StateOrBlock::State(s) => s.code(address).ok(), - StateOrBlock::Block(id) => self.state_at(id).and_then(|s| s.code(address).ok()) - }; - - // Converting from `Option>>` to `Option>` - result.map(|c| c.map(|c| (&*c).clone())) - } - - fn storage_at(&self, address: &Address, position: &H256, state: StateOrBlock) -> Option { - match state { - StateOrBlock::State(s) => s.storage_at(address, position).ok(), - StateOrBlock::Block(id) => self.state_at(id).and_then(|s| s.storage_at(address, position).ok()) - } - } - - fn list_accounts(&self, id: BlockId, after: Option<&Address>, count: u64) -> Option> { - if !self.factories.trie.is_fat() { - trace!(target: "fatdb", "list_accounts: Not a fat DB"); - return None; - } - - let state = match self.state_at(id) { - Some(state) => state, - _ => return None, - }; - - let (root, db) = state.drop(); - let db = &db.as_hash_db(); - let trie = match self.factories.trie.readonly(db, &root) { - Ok(trie) => trie, - _ => { - trace!(target: "fatdb", "list_accounts: Couldn't open the DB"); - return None; - } - }; - - let mut iter = match trie.iter() { - Ok(iter) => iter, - _ => return None, - }; - - if let Some(after) = after { - if let Err(e) = iter.seek(after) { - trace!(target: "fatdb", "list_accounts: Couldn't seek the DB: {:?}", e); - } else { - // Position the iterator after the `after` element - iter.next(); - } - } - - let accounts = iter.filter_map(|item| { - item.ok().map(|(addr, _)| Address::from_slice(&addr)) - }).take(count as usize).collect(); - - Some(accounts) - } - - fn list_storage(&self, id: BlockId, account: &Address, after: Option<&H256>, count: u64) -> Option> { - if !self.factories.trie.is_fat() { - trace!(target: "fatdb", "list_storage: Not a fat DB"); - return None; - } - - let state = match self.state_at(id) { - Some(state) => state, - _ => return None, - }; - - let root = match state.storage_root(account) { - Ok(Some(root)) => root, - _ => return None, - }; - - let (_, db) = state.drop(); - let account_db = &self.factories.accountdb.readonly(db.as_hash_db(), keccak(account)); - let account_db = &account_db.as_hash_db(); - let trie = match self.factories.trie.readonly(account_db, &root) { - Ok(trie) => trie, - _ => { - trace!(target: "fatdb", "list_storage: Couldn't open the DB"); - return None; - } - }; - - let mut iter = match trie.iter() { - Ok(iter) => iter, - _ => return None, - }; - - if let Some(after) = after { - if let Err(e) = iter.seek(after) { - trace!(target: "fatdb", "list_storage: Couldn't seek the DB: {:?}", e); - } else { - // Position the iterator after the `after` element - iter.next(); - } - } - - let keys = iter.filter_map(|item| { - item.ok().map(|(key, _)| H256::from_slice(&key)) - }).take(count as usize).collect(); - - Some(keys) - } - - fn transaction(&self, id: TransactionId) -> Option { - self.transaction_address(id).and_then(|address| self.chain.read().transaction(&address)) - } - - fn uncle(&self, id: UncleId) -> Option { - let index = id.position; - self.block_body(id.block).and_then(|body| body.view().uncle_rlp_at(index)) - .map(encoded::Header::new) - } - - fn transaction_receipt(&self, id: TransactionId) -> Option { - // NOTE Don't use block_receipts here for performance reasons - let address = self.transaction_address(id)?; - let hash = address.block_hash; - let chain = self.chain.read(); - let number = chain.block_number(&hash)?; - let body = chain.block_body(&hash)?; - let mut receipts = chain.block_receipts(&hash)?.receipts; - receipts.truncate(address.index + 1); - - let transaction = body.view().localized_transaction_at(&hash, number, address.index)?; - let receipt = receipts.pop()?; - let gas_used = receipts.last().map_or_else(|| 0.into(), |r| r.gas_used); - let no_of_logs = receipts.into_iter().map(|receipt| receipt.logs.len()).sum::(); - - let receipt = transaction_receipt(self.engine().machine(), transaction, receipt, gas_used, no_of_logs); - Some(receipt) - } - - fn localized_block_receipts(&self, id: BlockId) -> Option> { - let hash = self.block_hash(id)?; - - let chain = self.chain.read(); - let receipts = chain.block_receipts(&hash)?; - let number = chain.block_number(&hash)?; - let body = chain.block_body(&hash)?; - let engine = self.engine.clone(); - - let mut gas_used = 0.into(); - let mut no_of_logs = 0; - - Some(body - .view() - .localized_transactions(&hash, number) - .into_iter() - .zip(receipts.receipts) - .map(move |(transaction, receipt)| { - let result = transaction_receipt(engine.machine(), transaction, receipt, gas_used, no_of_logs); - gas_used = result.cumulative_gas_used; - no_of_logs += result.logs.len(); - result - }) - .collect() - ) - } - - fn tree_route(&self, from: &H256, to: &H256) -> Option { - let chain = self.chain.read(); - match chain.is_known(from) && chain.is_known(to) { - true => chain.tree_route(from.clone(), to.clone()), - false => None - } - } - - fn find_uncles(&self, hash: &H256) -> Option> { - self.chain.read().find_uncle_hashes(hash, MAX_UNCLE_AGE) - } - - fn state_data(&self, hash: &H256) -> Option { - self.state_db.read().journal_db().state(hash) - } - - fn block_receipts(&self, hash: &H256) -> Option { - self.chain.read().block_receipts(hash) - } - - fn queue_info(&self) -> BlockQueueInfo { - self.importer.block_queue.queue_info() - } - - fn is_queue_empty(&self) -> bool { - self.importer.block_queue.is_empty() - } - - fn clear_queue(&self) { - self.importer.block_queue.clear(); - } - - fn additional_params(&self) -> BTreeMap { - self.engine.additional_params().into_iter().collect() - } - - fn logs(&self, filter: Filter) -> Result, BlockId> { - let chain = self.chain.read(); - - // First, check whether `filter.from_block` and `filter.to_block` is on the canon chain. If so, we can use the - // optimized version. - let is_canon = |id| { - match id { - // If it is referred by number, then it is always on the canon chain. - &BlockId::Earliest | &BlockId::Latest | &BlockId::Number(_) => true, - // If it is referred by hash, we see whether a hash -> number -> hash conversion gives us the same - // result. - &BlockId::Hash(ref hash) => chain.is_canon(hash), - } - }; - - let blocks = if is_canon(&filter.from_block) && is_canon(&filter.to_block) { - // If we are on the canon chain, use bloom filter to fetch required hashes. - // - // If we are sure the block does not exist (where val > best_block_number), then return error. Note that we - // don't need to care about pending blocks here because RPC query sets pending back to latest (or handled - // pending logs themselves). - let from = match self.block_number_ref(&filter.from_block) { - Some(val) if val <= chain.best_block_number() => val, - _ => return Err(filter.from_block.clone()), - }; - let to = match self.block_number_ref(&filter.to_block) { - Some(val) if val <= chain.best_block_number() => val, - _ => return Err(filter.to_block.clone()), - }; - - // If from is greater than to, then the current bloom filter behavior is to just return empty - // result. There's no point to continue here. - if from > to { - return Err(filter.to_block.clone()); - } - - chain.blocks_with_bloom(&filter.bloom_possibilities(), from, to) - .into_iter() - .filter_map(|n| chain.block_hash(n)) - .collect::>() - } else { - // Otherwise, we use a slower version that finds a link between from_block and to_block. - let from_hash = match Self::block_hash(&chain, filter.from_block) { - Some(val) => val, - None => return Err(filter.from_block.clone()), - }; - let from_number = match chain.block_number(&from_hash) { - Some(val) => val, - None => return Err(BlockId::Hash(from_hash)), - }; - let to_hash = match Self::block_hash(&chain, filter.to_block) { - Some(val) => val, - None => return Err(filter.to_block.clone()), - }; - - let blooms = filter.bloom_possibilities(); - let bloom_match = |header: &encoded::Header| { - blooms.iter().any(|bloom| header.log_bloom().contains_bloom(bloom)) - }; - - let (blocks, last_hash) = { - let mut blocks = Vec::new(); - let mut current_hash = to_hash; - - loop { - let header = match chain.block_header_data(¤t_hash) { - Some(val) => val, - None => return Err(BlockId::Hash(current_hash)), - }; - if bloom_match(&header) { - blocks.push(current_hash); - } - - // Stop if `from` block is reached. - if header.number() <= from_number { - break; - } - current_hash = header.parent_hash(); - } - - blocks.reverse(); - (blocks, current_hash) - }; - - // Check if we've actually reached the expected `from` block. - if last_hash != from_hash || blocks.is_empty() { - // In this case, from_hash is the cause (for not matching last_hash). - return Err(BlockId::Hash(from_hash)); - } - - blocks - }; - - Ok(self.chain.read().logs(blocks, |entry| filter.matches(entry), filter.limit)) - } - - fn filter_traces(&self, filter: TraceFilter) -> Option> { - if !self.tracedb.read().tracing_enabled() { - return None; - } - - let start = self.block_number(filter.range.start)?; - let end = self.block_number(filter.range.end)?; - - let db_filter = trace::Filter { - range: start as usize..end as usize, - from_address: filter.from_address.into(), - to_address: filter.to_address.into(), - }; - - let traces = self.tracedb.read() - .filter(&db_filter) - .into_iter() - .skip(filter.after.unwrap_or(0)) - .take(filter.count.unwrap_or(usize::max_value())) - .collect(); - Some(traces) - } - - fn trace(&self, trace: TraceId) -> Option { - if !self.tracedb.read().tracing_enabled() { - return None; - } - - let trace_address = trace.address; - self.transaction_address(trace.transaction) - .and_then(|tx_address| { - self.block_number(BlockId::Hash(tx_address.block_hash)) - .and_then(|number| self.tracedb.read().trace(number, tx_address.index, trace_address)) - }) - } - - fn transaction_traces(&self, transaction: TransactionId) -> Option> { - if !self.tracedb.read().tracing_enabled() { - return None; - } - - self.transaction_address(transaction) - .and_then(|tx_address| { - self.block_number(BlockId::Hash(tx_address.block_hash)) - .and_then(|number| self.tracedb.read().transaction_traces(number, tx_address.index)) - }) - } - - fn block_traces(&self, block: BlockId) -> Option> { - if !self.tracedb.read().tracing_enabled() { - return None; - } - - self.block_number(block) - .and_then(|number| self.tracedb.read().block_traces(number)) - } - - fn last_hashes(&self) -> LastHashes { - (*self.build_last_hashes(&self.chain.read().best_block_hash())).clone() - } - - fn transactions_to_propagate(&self) -> Vec> { - const PROPAGATE_FOR_BLOCKS: u32 = 4; - const MIN_TX_TO_PROPAGATE: usize = 256; - - let block_gas_limit = *self.best_block_header().gas_limit(); - let min_tx_gas: U256 = self.latest_schedule().tx_gas.into(); - - let max_len = if min_tx_gas.is_zero() { - usize::max_value() - } else { - cmp::max( - MIN_TX_TO_PROPAGATE, - cmp::min( - (block_gas_limit / min_tx_gas) * PROPAGATE_FOR_BLOCKS, - // never more than usize - usize::max_value().into() - ).as_u64() as usize - ) - }; - self.importer.miner.ready_transactions(self, max_len, ::miner::PendingOrdering::Priority) - } - - fn signing_chain_id(&self) -> Option { - self.engine.signing_chain_id(&self.latest_env_info()) - } - - fn block_extra_info(&self, id: BlockId) -> Option> { - self.block_header_decoded(id) - .map(|header| self.engine.extra_info(&header)) - } - - fn uncle_extra_info(&self, id: UncleId) -> Option> { - self.uncle(id) - .and_then(|h| { - h.decode().map(|dh| { - self.engine.extra_info(&dh) - }).ok() - }) - } - - fn pruning_info(&self) -> PruningInfo { - PruningInfo { - earliest_chain: self.chain.read().first_block_number().unwrap_or(1), - earliest_state: self.state_db.read().journal_db().earliest_era().unwrap_or(0), - } - } - - fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error> { - let authoring_params = self.importer.miner.authoring_params(); - let service_transaction_checker = self.importer.miner.service_transaction_checker(); - let gas_price = if let Some(checker) = service_transaction_checker { - match checker.check_address(self, authoring_params.author) { - Ok(true) => U256::zero(), - _ => self.importer.miner.sensible_gas_price(), - } - } else { - self.importer.miner.sensible_gas_price() - }; - let transaction = transaction::Transaction { - nonce: self.latest_nonce(&authoring_params.author), - action: Action::Call(address), - gas: self.importer.miner.sensible_gas_limit(), - gas_price, - value: U256::zero(), - data: data, - }; - let chain_id = self.engine.signing_chain_id(&self.latest_env_info()); - let signature = self.engine.sign(transaction.hash(chain_id)) - .map_err(|e| transaction::Error::InvalidSignature(e.to_string()))?; - let signed = SignedTransaction::new(transaction.with_signature(signature, chain_id))?; - self.importer.miner.import_own_transaction(self, signed.into()) - } - - fn registrar_address(&self) -> Option
{ - self.registrar_address.clone() - } + fn replay(&self, id: TransactionId, analytics: CallAnalytics) -> Result { + let address = self + .transaction_address(id) + .ok_or(CallError::TransactionNotFound)?; + let block = BlockId::Hash(address.block_hash); + + const PROOF: &'static str = + "The transaction address contains a valid index within block; qed"; + Ok(self + .replay_block_transactions(block, analytics)? + .nth(address.index) + .expect(PROOF) + .1) + } + + fn replay_block_transactions( + &self, + block: BlockId, + analytics: CallAnalytics, + ) -> Result>, CallError> { + let mut env_info = self.env_info(block).ok_or(CallError::StatePruned)?; + let body = self.block_body(block).ok_or(CallError::StatePruned)?; + let mut state = self + .state_at_beginning(block) + .ok_or(CallError::StatePruned)?; + let txs = body.transactions(); + let engine = self.engine.clone(); + + const PROOF: &'static str = + "Transactions fetched from blockchain; blockchain transactions are valid; qed"; + const EXECUTE_PROOF: &'static str = "Transaction replayed; qed"; + + Ok(Box::new(txs.into_iter().map(move |t| { + let transaction_hash = t.hash(); + let t = SignedTransaction::new(t).expect(PROOF); + let machine = engine.machine(); + let x = Self::do_virtual_call(machine, &env_info, &mut state, &t, analytics) + .expect(EXECUTE_PROOF); + env_info.gas_used = env_info.gas_used + x.gas_used; + (transaction_hash, x) + }))) + } + + fn mode(&self) -> Mode { + let r = self.mode.lock().clone().into(); + trace!(target: "mode", "Asked for mode = {:?}. returning {:?}", &*self.mode.lock(), r); + r + } + + fn disable(&self) { + self.set_mode(Mode::Off); + self.enabled.store(false, AtomicOrdering::Relaxed); + self.clear_queue(); + } + + fn set_mode(&self, new_mode: Mode) { + trace!(target: "mode", "Client::set_mode({:?})", new_mode); + if !self.enabled.load(AtomicOrdering::Relaxed) { + return; + } + { + let mut mode = self.mode.lock(); + *mode = new_mode.clone().into(); + trace!(target: "mode", "Mode now {:?}", &*mode); + if let Some(ref mut f) = *self.on_user_defaults_change.lock() { + trace!(target: "mode", "Making callback..."); + f(Some((&*mode).clone())) + } + } + match new_mode { + Mode::Active => self.wake_up(), + Mode::Off => self.sleep(true), + _ => { + (*self.sleep_state.lock()).last_activity = Some(Instant::now()); + } + } + } + + fn spec_name(&self) -> String { + self.config.spec_name.clone() + } + + fn set_spec_name(&self, new_spec_name: String) -> Result<(), ()> { + trace!(target: "mode", "Client::set_spec_name({:?})", new_spec_name); + if !self.enabled.load(AtomicOrdering::Relaxed) { + return Err(()); + } + if let Some(ref h) = *self.exit_handler.lock() { + (*h)(new_spec_name); + Ok(()) + } else { + warn!("Not hypervised; cannot change chain."); + Err(()) + } + } + + fn block_number(&self, id: BlockId) -> Option { + self.block_number_ref(&id) + } + + fn block_body(&self, id: BlockId) -> Option { + let chain = self.chain.read(); + + Self::block_hash(&chain, id).and_then(|hash| chain.block_body(&hash)) + } + + fn block_status(&self, id: BlockId) -> BlockStatus { + let chain = self.chain.read(); + match Self::block_hash(&chain, id) { + Some(ref hash) if chain.is_known(hash) => BlockStatus::InChain, + Some(hash) => self.importer.block_queue.status(&hash).into(), + None => BlockStatus::Unknown, + } + } + + fn is_processing_fork(&self) -> bool { + let chain = self.chain.read(); + self.importer + .block_queue + .is_processing_fork(&chain.best_block_hash(), &chain) + } + + fn block_total_difficulty(&self, id: BlockId) -> Option { + let chain = self.chain.read(); + + Self::block_hash(&chain, id) + .and_then(|hash| chain.block_details(&hash)) + .map(|d| d.total_difficulty) + } + + fn storage_root(&self, address: &Address, id: BlockId) -> Option { + self.state_at(id) + .and_then(|s| s.storage_root(address).ok()) + .and_then(|x| x) + } + + fn block_hash(&self, id: BlockId) -> Option { + let chain = self.chain.read(); + Self::block_hash(&chain, id) + } + + fn code(&self, address: &Address, state: StateOrBlock) -> Option> { + let result = match state { + StateOrBlock::State(s) => s.code(address).ok(), + StateOrBlock::Block(id) => self.state_at(id).and_then(|s| s.code(address).ok()), + }; + + // Converting from `Option>>` to `Option>` + result.map(|c| c.map(|c| (&*c).clone())) + } + + fn storage_at(&self, address: &Address, position: &H256, state: StateOrBlock) -> Option { + match state { + StateOrBlock::State(s) => s.storage_at(address, position).ok(), + StateOrBlock::Block(id) => self + .state_at(id) + .and_then(|s| s.storage_at(address, position).ok()), + } + } + + fn list_accounts( + &self, + id: BlockId, + after: Option<&Address>, + count: u64, + ) -> Option> { + if !self.factories.trie.is_fat() { + trace!(target: "fatdb", "list_accounts: Not a fat DB"); + return None; + } + + let state = match self.state_at(id) { + Some(state) => state, + _ => return None, + }; + + let (root, db) = state.drop(); + let db = &db.as_hash_db(); + let trie = match self.factories.trie.readonly(db, &root) { + Ok(trie) => trie, + _ => { + trace!(target: "fatdb", "list_accounts: Couldn't open the DB"); + return None; + } + }; + + let mut iter = match trie.iter() { + Ok(iter) => iter, + _ => return None, + }; + + if let Some(after) = after { + if let Err(e) = iter.seek(after) { + trace!(target: "fatdb", "list_accounts: Couldn't seek the DB: {:?}", e); + } else { + // Position the iterator after the `after` element + iter.next(); + } + } + + let accounts = iter + .filter_map(|item| item.ok().map(|(addr, _)| Address::from_slice(&addr))) + .take(count as usize) + .collect(); + + Some(accounts) + } + + fn list_storage( + &self, + id: BlockId, + account: &Address, + after: Option<&H256>, + count: u64, + ) -> Option> { + if !self.factories.trie.is_fat() { + trace!(target: "fatdb", "list_storage: Not a fat DB"); + return None; + } + + let state = match self.state_at(id) { + Some(state) => state, + _ => return None, + }; + + let root = match state.storage_root(account) { + Ok(Some(root)) => root, + _ => return None, + }; + + let (_, db) = state.drop(); + let account_db = &self + .factories + .accountdb + .readonly(db.as_hash_db(), keccak(account)); + let account_db = &account_db.as_hash_db(); + let trie = match self.factories.trie.readonly(account_db, &root) { + Ok(trie) => trie, + _ => { + trace!(target: "fatdb", "list_storage: Couldn't open the DB"); + return None; + } + }; + + let mut iter = match trie.iter() { + Ok(iter) => iter, + _ => return None, + }; + + if let Some(after) = after { + if let Err(e) = iter.seek(after) { + trace!(target: "fatdb", "list_storage: Couldn't seek the DB: {:?}", e); + } else { + // Position the iterator after the `after` element + iter.next(); + } + } + + let keys = iter + .filter_map(|item| item.ok().map(|(key, _)| H256::from_slice(&key))) + .take(count as usize) + .collect(); + + Some(keys) + } + + fn transaction(&self, id: TransactionId) -> Option { + self.transaction_address(id) + .and_then(|address| self.chain.read().transaction(&address)) + } + + fn uncle(&self, id: UncleId) -> Option { + let index = id.position; + self.block_body(id.block) + .and_then(|body| body.view().uncle_rlp_at(index)) + .map(encoded::Header::new) + } + + fn transaction_receipt(&self, id: TransactionId) -> Option { + // NOTE Don't use block_receipts here for performance reasons + let address = self.transaction_address(id)?; + let hash = address.block_hash; + let chain = self.chain.read(); + let number = chain.block_number(&hash)?; + let body = chain.block_body(&hash)?; + let mut receipts = chain.block_receipts(&hash)?.receipts; + receipts.truncate(address.index + 1); + + let transaction = body + .view() + .localized_transaction_at(&hash, number, address.index)?; + let receipt = receipts.pop()?; + let gas_used = receipts.last().map_or_else(|| 0.into(), |r| r.gas_used); + let no_of_logs = receipts + .into_iter() + .map(|receipt| receipt.logs.len()) + .sum::(); + + let receipt = transaction_receipt( + self.engine().machine(), + transaction, + receipt, + gas_used, + no_of_logs, + ); + Some(receipt) + } + + fn localized_block_receipts(&self, id: BlockId) -> Option> { + let hash = self.block_hash(id)?; + + let chain = self.chain.read(); + let receipts = chain.block_receipts(&hash)?; + let number = chain.block_number(&hash)?; + let body = chain.block_body(&hash)?; + let engine = self.engine.clone(); + + let mut gas_used = 0.into(); + let mut no_of_logs = 0; + + Some( + body.view() + .localized_transactions(&hash, number) + .into_iter() + .zip(receipts.receipts) + .map(move |(transaction, receipt)| { + let result = transaction_receipt( + engine.machine(), + transaction, + receipt, + gas_used, + no_of_logs, + ); + gas_used = result.cumulative_gas_used; + no_of_logs += result.logs.len(); + result + }) + .collect(), + ) + } + + fn tree_route(&self, from: &H256, to: &H256) -> Option { + let chain = self.chain.read(); + match chain.is_known(from) && chain.is_known(to) { + true => chain.tree_route(from.clone(), to.clone()), + false => None, + } + } + + fn find_uncles(&self, hash: &H256) -> Option> { + self.chain.read().find_uncle_hashes(hash, MAX_UNCLE_AGE) + } + + fn block_receipts(&self, hash: &H256) -> Option { + self.chain.read().block_receipts(hash) + } + + fn queue_info(&self) -> BlockQueueInfo { + self.importer.block_queue.queue_info() + } + + fn is_queue_empty(&self) -> bool { + self.importer.block_queue.is_empty() + } + + fn clear_queue(&self) { + self.importer.block_queue.clear(); + } + + fn additional_params(&self) -> BTreeMap { + self.engine.additional_params().into_iter().collect() + } + + fn logs(&self, filter: Filter) -> Result, BlockId> { + let chain = self.chain.read(); + + // First, check whether `filter.from_block` and `filter.to_block` is on the canon chain. If so, we can use the + // optimized version. + let is_canon = |id| { + match id { + // If it is referred by number, then it is always on the canon chain. + &BlockId::Earliest | &BlockId::Latest | &BlockId::Number(_) => true, + // If it is referred by hash, we see whether a hash -> number -> hash conversion gives us the same + // result. + &BlockId::Hash(ref hash) => chain.is_canon(hash), + } + }; + + let blocks = if is_canon(&filter.from_block) && is_canon(&filter.to_block) { + // If we are on the canon chain, use bloom filter to fetch required hashes. + // + // If we are sure the block does not exist (where val > best_block_number), then return error. Note that we + // don't need to care about pending blocks here because RPC query sets pending back to latest (or handled + // pending logs themselves). + let from = match self.block_number_ref(&filter.from_block) { + Some(val) if val <= chain.best_block_number() => val, + _ => return Err(filter.from_block.clone()), + }; + let to = match self.block_number_ref(&filter.to_block) { + Some(val) if val <= chain.best_block_number() => val, + _ => return Err(filter.to_block.clone()), + }; + + // If from is greater than to, then the current bloom filter behavior is to just return empty + // result. There's no point to continue here. + if from > to { + return Err(filter.to_block.clone()); + } + + chain + .blocks_with_bloom(&filter.bloom_possibilities(), from, to) + .into_iter() + .filter_map(|n| chain.block_hash(n)) + .collect::>() + } else { + // Otherwise, we use a slower version that finds a link between from_block and to_block. + let from_hash = match Self::block_hash(&chain, filter.from_block) { + Some(val) => val, + None => return Err(filter.from_block.clone()), + }; + let from_number = match chain.block_number(&from_hash) { + Some(val) => val, + None => return Err(BlockId::Hash(from_hash)), + }; + let to_hash = match Self::block_hash(&chain, filter.to_block) { + Some(val) => val, + None => return Err(filter.to_block.clone()), + }; + + let blooms = filter.bloom_possibilities(); + let bloom_match = |header: &encoded::Header| { + blooms + .iter() + .any(|bloom| header.log_bloom().contains_bloom(bloom)) + }; + + let (blocks, last_hash) = { + let mut blocks = Vec::new(); + let mut current_hash = to_hash; + + loop { + let header = match chain.block_header_data(¤t_hash) { + Some(val) => val, + None => return Err(BlockId::Hash(current_hash)), + }; + if bloom_match(&header) { + blocks.push(current_hash); + } + + // Stop if `from` block is reached. + if header.number() <= from_number { + break; + } + current_hash = header.parent_hash(); + } + + blocks.reverse(); + (blocks, current_hash) + }; + + // Check if we've actually reached the expected `from` block. + if last_hash != from_hash || blocks.is_empty() { + // In this case, from_hash is the cause (for not matching last_hash). + return Err(BlockId::Hash(from_hash)); + } + + blocks + }; + + Ok(chain.logs(blocks, |entry| filter.matches(entry), filter.limit)) + } + + fn filter_traces(&self, filter: TraceFilter) -> Option> { + if !self.tracedb.read().tracing_enabled() { + return None; + } + + let start = self.block_number(filter.range.start)?; + let end = self.block_number(filter.range.end)?; + + let db_filter = trace::Filter { + range: start as usize..end as usize, + from_address: filter.from_address.into(), + to_address: filter.to_address.into(), + }; + + let traces = self + .tracedb + .read() + .filter(&db_filter) + .into_iter() + .skip(filter.after.unwrap_or(0)) + .take(filter.count.unwrap_or(usize::max_value())) + .collect(); + Some(traces) + } + + fn trace(&self, trace: TraceId) -> Option { + if !self.tracedb.read().tracing_enabled() { + return None; + } + + let trace_address = trace.address; + self.transaction_address(trace.transaction) + .and_then(|tx_address| { + self.block_number(BlockId::Hash(tx_address.block_hash)) + .and_then(|number| { + self.tracedb + .read() + .trace(number, tx_address.index, trace_address) + }) + }) + } + + fn transaction_traces(&self, transaction: TransactionId) -> Option> { + if !self.tracedb.read().tracing_enabled() { + return None; + } + + self.transaction_address(transaction) + .and_then(|tx_address| { + self.block_number(BlockId::Hash(tx_address.block_hash)) + .and_then(|number| { + self.tracedb + .read() + .transaction_traces(number, tx_address.index) + }) + }) + } + + fn block_traces(&self, block: BlockId) -> Option> { + if !self.tracedb.read().tracing_enabled() { + return None; + } + + self.block_number(block) + .and_then(|number| self.tracedb.read().block_traces(number)) + } + + fn last_hashes(&self) -> LastHashes { + (*self.build_last_hashes(&self.chain.read().best_block_hash())).clone() + } + + fn transactions_to_propagate(&self) -> Vec> { + const PROPAGATE_FOR_BLOCKS: u32 = 4; + const MIN_TX_TO_PROPAGATE: usize = 256; + + let block_gas_limit = *self.best_block_header().gas_limit(); + let min_tx_gas: U256 = self.latest_schedule().tx_gas.into(); + + let max_len = if min_tx_gas.is_zero() { + usize::max_value() + } else { + cmp::max( + MIN_TX_TO_PROPAGATE, + cmp::min( + (block_gas_limit / min_tx_gas) * PROPAGATE_FOR_BLOCKS, + // never more than usize + usize::max_value().into(), + ) + .as_u64() as usize, + ) + }; + self.importer + .miner + .ready_transactions(self, max_len, ::miner::PendingOrdering::Priority) + } + + fn signing_chain_id(&self) -> Option { + self.engine.signing_chain_id(&self.latest_env_info()) + } + + fn block_extra_info(&self, id: BlockId) -> Option> { + self.block_header_decoded(id) + .map(|header| self.engine.extra_info(&header)) + } + + fn uncle_extra_info(&self, id: UncleId) -> Option> { + self.uncle(id) + .and_then(|h| h.decode().map(|dh| self.engine.extra_info(&dh)).ok()) + } + + fn pruning_info(&self) -> PruningInfo { + PruningInfo { + earliest_chain: self.chain.read().first_block_number().unwrap_or(1), + earliest_state: self + .state_db + .read() + .journal_db() + .earliest_era() + .unwrap_or(0), + } + } + + fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error> { + let authoring_params = self.importer.miner.authoring_params(); + let service_transaction_checker = self.importer.miner.service_transaction_checker(); + let gas_price = if let Some(checker) = service_transaction_checker { + match checker.check_address(self, authoring_params.author) { + Ok(true) => U256::zero(), + _ => self.importer.miner.sensible_gas_price(), + } + } else { + self.importer.miner.sensible_gas_price() + }; + let transaction = transaction::Transaction { + nonce: self.latest_nonce(&authoring_params.author), + action: Action::Call(address), + gas: self.importer.miner.sensible_gas_limit(), + gas_price, + value: U256::zero(), + data: data, + }; + let chain_id = self.engine.signing_chain_id(&self.latest_env_info()); + let signature = self + .engine + .sign(transaction.hash(chain_id)) + .map_err(|e| transaction::Error::InvalidSignature(e.to_string()))?; + let signed = SignedTransaction::new(transaction.with_signature(signature, chain_id))?; + self.importer + .miner + .import_own_transaction(self, signed.into()) + } + + fn registrar_address(&self) -> Option
{ + self.registrar_address.clone() + } } impl IoClient for Client { - fn queue_transactions(&self, transactions: Vec, peer_id: usize) { - trace_time!("queue_transactions"); - let len = transactions.len(); - self.queue_transactions.queue(&self.io_channel.read(), len, move |client| { - trace_time!("import_queued_transactions"); - - let txs: Vec = transactions - .iter() - .filter_map(|bytes| client.engine.decode_transaction(bytes).ok()) - .collect(); - - client.notify(|notify| { - notify.transactions_received(&txs, peer_id); - }); - - client.importer.miner.import_external_transactions(client, txs); - }).unwrap_or_else(|e| { - debug!(target: "client", "Ignoring {} transactions: {}", len, e); - }); - } - - fn queue_ancient_block(&self, unverified: Unverified, receipts_bytes: Bytes) -> EthcoreResult { - trace_time!("queue_ancient_block"); - - let hash = unverified.hash(); - { - // check block order - if self.chain.read().is_known(&hash) { - bail!(EthcoreErrorKind::Import(ImportErrorKind::AlreadyInChain)); - } - let parent_hash = unverified.parent_hash(); - // NOTE To prevent race condition with import, make sure to check queued blocks first - // (and attempt to acquire lock) - let is_parent_pending = self.queued_ancient_blocks.read().0.contains(&parent_hash); - if !is_parent_pending && !self.chain.read().is_known(&parent_hash) { - bail!(EthcoreErrorKind::Block(BlockError::UnknownParent(parent_hash))); - } - } - - // we queue blocks here and trigger an IO message. - { - let mut queued = self.queued_ancient_blocks.write(); - queued.0.insert(hash); - queued.1.push_back((unverified, receipts_bytes)); - } - - let queued = self.queued_ancient_blocks.clone(); - let lock = self.ancient_blocks_import_lock.clone(); - self.queue_ancient_blocks.queue(&self.io_channel.read(), 1, move |client| { - trace_time!("import_ancient_block"); - // Make sure to hold the lock here to prevent importing out of order. - // We use separate lock, cause we don't want to block queueing. - let _lock = lock.lock(); - for _i in 0..MAX_ANCIENT_BLOCKS_TO_IMPORT { - let first = queued.write().1.pop_front(); - if let Some((unverified, receipts_bytes)) = first { - let hash = unverified.hash(); - let result = client.importer.import_old_block( - unverified, - &receipts_bytes, - &**client.db.read().key_value(), - &*client.chain.read(), - ); - if let Err(e) = result { - error!(target: "client", "Error importing ancient block: {}", e); - - let mut queued = queued.write(); - queued.0.clear(); - queued.1.clear(); - } - // remove from pending - queued.write().0.remove(&hash); - } else { - break; - } - } - })?; - - Ok(hash) - } - - fn queue_consensus_message(&self, message: Bytes) { - match self.queue_consensus_message.queue(&self.io_channel.read(), 1, move |client| { - if let Err(e) = client.engine().handle_message(&message) { - debug!(target: "poa", "Invalid message received: {}", e); - } - }) { - Ok(_) => (), - Err(e) => { - debug!(target: "poa", "Ignoring the message, error queueing: {}", e); - } - } - } + fn queue_transactions(&self, transactions: Vec, peer_id: usize) { + trace_time!("queue_transactions"); + let len = transactions.len(); + self.queue_transactions + .queue(&self.io_channel.read(), len, move |client| { + trace_time!("import_queued_transactions"); + + let txs: Vec = transactions + .iter() + .filter_map(|bytes| client.engine.decode_transaction(bytes).ok()) + .collect(); + + client.notify(|notify| { + notify.transactions_received(&txs, peer_id); + }); + + client + .importer + .miner + .import_external_transactions(client, txs); + }) + .unwrap_or_else(|e| { + debug!(target: "client", "Ignoring {} transactions: {}", len, e); + }); + } + + fn queue_ancient_block( + &self, + unverified: Unverified, + receipts_bytes: Bytes, + ) -> EthcoreResult { + trace_time!("queue_ancient_block"); + + let hash = unverified.hash(); + { + // check block order + if self.chain.read().is_known(&hash) { + bail!(EthcoreErrorKind::Import(ImportErrorKind::AlreadyInChain)); + } + let parent_hash = unverified.parent_hash(); + // NOTE To prevent race condition with import, make sure to check queued blocks first + // (and attempt to acquire lock) + let is_parent_pending = self.queued_ancient_blocks.read().0.contains(&parent_hash); + if !is_parent_pending && !self.chain.read().is_known(&parent_hash) { + bail!(EthcoreErrorKind::Block(BlockError::UnknownParent( + parent_hash + ))); + } + } + + // we queue blocks here and trigger an IO message. + { + let mut queued = self.queued_ancient_blocks.write(); + queued.0.insert(hash); + queued.1.push_back((unverified, receipts_bytes)); + } + + let queued = self.queued_ancient_blocks.clone(); + let lock = self.ancient_blocks_import_lock.clone(); + self.queue_ancient_blocks + .queue(&self.io_channel.read(), 1, move |client| { + trace_time!("import_ancient_block"); + // Make sure to hold the lock here to prevent importing out of order. + // We use separate lock, cause we don't want to block queueing. + let _lock = lock.lock(); + for _i in 0..MAX_ANCIENT_BLOCKS_TO_IMPORT { + let first = queued.write().1.pop_front(); + if let Some((unverified, receipts_bytes)) = first { + let hash = unverified.hash(); + let result = client.importer.import_old_block( + unverified, + &receipts_bytes, + &**client.db.read().key_value(), + &*client.chain.read(), + ); + if let Err(e) = result { + error!(target: "client", "Error importing ancient block: {}", e); + + let mut queued = queued.write(); + queued.0.clear(); + queued.1.clear(); + } + // remove from pending + queued.write().0.remove(&hash); + } else { + break; + } + } + })?; + + Ok(hash) + } + + fn queue_consensus_message(&self, message: Bytes) { + match self + .queue_consensus_message + .queue(&self.io_channel.read(), 1, move |client| { + if let Err(e) = client.engine().handle_message(&message) { + debug!(target: "poa", "Invalid message received: {}", e); + } + }) { + Ok(_) => (), + Err(e) => { + debug!(target: "poa", "Ignoring the message, error queueing: {}", e); + } + } + } } impl ReopenBlock for Client { - fn reopen_block(&self, block: ClosedBlock) -> OpenBlock { - let engine = &*self.engine; - let mut block = block.reopen(engine); - let max_uncles = engine.maximum_uncle_count(block.header.number()); - if block.uncles.len() < max_uncles { - let chain = self.chain.read(); - let h = chain.best_block_hash(); - // Add new uncles - let uncles = chain - .find_uncle_hashes(&h, MAX_UNCLE_AGE) - .unwrap_or_else(Vec::new); - - for h in uncles { - if !block.uncles.iter().any(|header| header.hash() == h) { - let uncle = chain.block_header_data(&h).expect("find_uncle_hashes only returns hashes for existing headers; qed"); - let uncle = uncle.decode().expect("decoding failure"); - block.push_uncle(uncle).expect("pushing up to maximum_uncle_count; + fn reopen_block(&self, block: ClosedBlock) -> OpenBlock { + let engine = &*self.engine; + let mut block = block.reopen(engine); + let max_uncles = engine.maximum_uncle_count(block.header.number()); + if block.uncles.len() < max_uncles { + let chain = self.chain.read(); + let h = chain.best_block_hash(); + // Add new uncles + let uncles = chain + .find_uncle_hashes(&h, MAX_UNCLE_AGE) + .unwrap_or_else(Vec::new); + + for h in uncles { + if !block.uncles.iter().any(|header| header.hash() == h) { + let uncle = chain + .block_header_data(&h) + .expect("find_uncle_hashes only returns hashes for existing headers; qed"); + let uncle = uncle.decode().expect("decoding failure"); + block.push_uncle(uncle).expect( + "pushing up to maximum_uncle_count; push_uncle is not ok only if more than maximum_uncle_count is pushed; so all push_uncle are Ok; - qed"); - if block.uncles.len() >= max_uncles { break } - } - } - - } - block - } + qed", + ); + if block.uncles.len() >= max_uncles { + break; + } + } + } + } + block + } } impl PrepareOpenBlock for Client { - fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> Result { - let engine = &*self.engine; - let chain = self.chain.read(); - let best_header = chain.best_block_header(); - let h = best_header.hash(); - - let is_epoch_begin = chain.epoch_transition(best_header.number(), h).is_some(); - let mut open_block = OpenBlock::new( - engine, - self.factories.clone(), - self.tracedb.read().tracing_enabled(), - self.state_db.read().boxed_clone_canon(&h), - &best_header, - self.build_last_hashes(&h), - author, - gas_range_target, - extra_data, - is_epoch_begin, - chain.ancestry_with_metadata_iter(best_header.hash()), - )?; - - // Add uncles - chain - .find_uncle_headers(&h, MAX_UNCLE_AGE) - .unwrap_or_else(Vec::new) - .into_iter() - .take(engine.maximum_uncle_count(open_block.header.number())) - .foreach(|h| { - open_block.push_uncle(h.decode().expect("decoding failure")).expect("pushing maximum_uncle_count; + fn prepare_open_block( + &self, + author: Address, + gas_range_target: (U256, U256), + extra_data: Bytes, + ) -> Result { + let engine = &*self.engine; + let chain = self.chain.read(); + let best_header = chain.best_block_header(); + let h = best_header.hash(); + + let is_epoch_begin = chain.epoch_transition(best_header.number(), h).is_some(); + let mut open_block = OpenBlock::new( + engine, + self.factories.clone(), + self.tracedb.read().tracing_enabled(), + self.state_db.read().boxed_clone_canon(&h), + &best_header, + self.build_last_hashes(&h), + author, + gas_range_target, + extra_data, + is_epoch_begin, + chain.ancestry_with_metadata_iter(best_header.hash()), + )?; + + // Add uncles + chain + .find_uncle_headers(&h, MAX_UNCLE_AGE) + .unwrap_or_else(Vec::new) + .into_iter() + .take(engine.maximum_uncle_count(open_block.header.number())) + .foreach(|h| { + open_block + .push_uncle(h.decode().expect("decoding failure")) + .expect( + "pushing maximum_uncle_count; open_block was just created; push_uncle is not ok only if more than maximum_uncle_count is pushed; so all push_uncle are Ok; - qed"); - }); + qed", + ); + }); - Ok(open_block) - } + Ok(open_block) + } } impl BlockProducer for Client {} impl ScheduleInfo for Client { - fn latest_schedule(&self) -> Schedule { - self.engine.schedule(self.latest_env_info().number) - } + fn latest_schedule(&self) -> Schedule { + self.engine.schedule(self.latest_env_info().number) + } } impl ImportSealedBlock for Client { - fn import_sealed_block(&self, block: SealedBlock) -> EthcoreResult { - let start = Instant::now(); - let raw = block.rlp_bytes(); - let header = block.header.clone(); - let hash = header.hash(); - self.notify(|n| n.block_pre_import(&raw, &hash, header.difficulty())); - - let route = { - // Do a super duper basic verification to detect potential bugs - if let Err(e) = self.engine.verify_block_basic(&header) { - self.importer.bad_blocks.report( - block.rlp_bytes(), - format!("Detected an issue with locally sealed block: {}", e), - ); - return Err(e.into()); - } - - // scope for self.import_lock - let _import_lock = self.importer.import_lock.lock(); - trace_time!("import_sealed_block"); - - let block_data = block.rlp_bytes(); - - let pending = self.importer.check_epoch_end_signal( - &header, - &block_data, - &block.receipts, - block.state.db(), - self - )?; - let route = self.importer.commit_block( - block, - &header, - encoded::Block::new(block_data), - pending, - self - ); - trace!(target: "client", "Imported sealed block #{} ({})", header.number(), hash); - self.state_db.write().sync_cache(&route.enacted, &route.retracted, false); - route - }; - let route = ChainRoute::from([route].as_ref()); - self.importer.miner.chain_new_blocks( - self, - &[hash], - &[], - route.enacted(), - route.retracted(), - self.engine.seals_internally().is_some(), - ); - self.notify(|notify| { - notify.new_blocks( - NewBlocks::new( - vec![hash], - vec![], - route.clone(), - vec![hash], - vec![], - start.elapsed(), - false - ) - ); - }); - self.db.read().key_value().flush().expect("DB flush failed."); - Ok(hash) - } + fn import_sealed_block(&self, block: SealedBlock) -> EthcoreResult { + let start = Instant::now(); + let raw = block.rlp_bytes(); + let header = block.header.clone(); + let hash = header.hash(); + self.notify(|n| n.block_pre_import(&raw, &hash, header.difficulty())); + + let route = { + // Do a super duper basic verification to detect potential bugs + if let Err(e) = self.engine.verify_block_basic(&header) { + self.importer.bad_blocks.report( + block.rlp_bytes(), + format!("Detected an issue with locally sealed block: {}", e), + ); + return Err(e.into()); + } + + // scope for self.import_lock + let _import_lock = self.importer.import_lock.lock(); + trace_time!("import_sealed_block"); + + let block_data = block.rlp_bytes(); + + let pending = self.importer.check_epoch_end_signal( + &header, + &block_data, + &block.receipts, + block.state.db(), + self, + )?; + let route = self.importer.commit_block( + block, + &header, + encoded::Block::new(block_data), + pending, + self, + ); + trace!(target: "client", "Imported sealed block #{} ({})", header.number(), hash); + self.state_db + .write() + .sync_cache(&route.enacted, &route.retracted, false); + route + }; + let route = ChainRoute::from([route].as_ref()); + self.importer.miner.chain_new_blocks( + self, + &[hash], + &[], + route.enacted(), + route.retracted(), + self.engine.seals_internally().is_some(), + ); + self.notify(|notify| { + notify.new_blocks(NewBlocks::new( + vec![hash], + vec![], + route.clone(), + vec![hash], + vec![], + start.elapsed(), + false, + )); + }); + self.db + .read() + .key_value() + .flush() + .expect("DB flush failed."); + Ok(hash) + } } impl BroadcastProposalBlock for Client { - fn broadcast_proposal_block(&self, block: SealedBlock) { - const DURATION_ZERO: Duration = Duration::from_millis(0); - self.notify(|notify| { - notify.new_blocks( - NewBlocks::new( - vec![], - vec![], - ChainRoute::default(), - vec![], - vec![block.rlp_bytes()], - DURATION_ZERO, - false - ) - ); - }); - } + fn broadcast_proposal_block(&self, block: SealedBlock) { + const DURATION_ZERO: Duration = Duration::from_millis(0); + self.notify(|notify| { + notify.new_blocks(NewBlocks::new( + vec![], + vec![], + ChainRoute::default(), + vec![], + vec![block.rlp_bytes()], + DURATION_ZERO, + false, + )); + }); + } } impl SealedBlockImporter for Client {} @@ -2453,319 +2860,679 @@ impl ::miner::TransactionVerifierClient for Client {} impl ::miner::BlockChainClient for Client {} impl super::traits::EngineClient for Client { - fn update_sealing(&self) { - self.importer.miner.update_sealing(self) - } - - fn submit_seal(&self, block_hash: H256, seal: Vec) { - let import = self.importer.miner.submit_seal(block_hash, seal).and_then(|block| self.import_sealed_block(block)); - if let Err(err) = import { - warn!(target: "poa", "Wrong internal seal submission! {:?}", err); - } - } - - fn broadcast_consensus_message(&self, message: Bytes) { - self.notify(|notify| notify.broadcast(ChainMessageType::Consensus(message.clone()))); - } - - fn epoch_transition_for(&self, parent_hash: H256) -> Option<::engines::EpochTransition> { - self.chain.read().epoch_transition_for(parent_hash) - } - - fn as_full_client(&self) -> Option<&BlockChainClient> { Some(self) } - - fn block_number(&self, id: BlockId) -> Option { - BlockChainClient::block_number(self, id) - } - - fn block_header(&self, id: BlockId) -> Option { - BlockChainClient::block_header(self, id) - } + fn update_sealing(&self, force: ForceUpdateSealing) { + self.importer.miner.update_sealing(self, force) + } + + fn submit_seal(&self, block_hash: H256, seal: Vec) { + let import = self + .importer + .miner + .submit_seal(block_hash, seal) + .and_then(|block| self.import_sealed_block(block)); + if let Err(err) = import { + warn!(target: "poa", "Wrong internal seal submission! {:?}", err); + } + } + + fn broadcast_consensus_message(&self, message: Bytes) { + self.notify(|notify| notify.broadcast(ChainMessageType::Consensus(message.clone()))); + } + + fn epoch_transition_for(&self, parent_hash: H256) -> Option<::engines::EpochTransition> { + self.chain.read().epoch_transition_for(parent_hash) + } + + fn as_full_client(&self) -> Option<&dyn BlockChainClient> { + Some(self) + } + + fn block_number(&self, id: BlockId) -> Option { + BlockChainClient::block_number(self, id) + } + + fn block_header(&self, id: BlockId) -> Option { + BlockChainClient::block_header(self, id) + } } impl ProvingBlockChainClient for Client { - fn prove_storage(&self, key1: H256, key2: H256, id: BlockId) -> Option<(Vec, H256)> { - self.state_at(id) - .and_then(move |state| state.prove_storage(key1, key2).ok()) - } - - fn prove_account(&self, key1: H256, id: BlockId) -> Option<(Vec, ::types::basic_account::BasicAccount)> { - self.state_at(id) - .and_then(move |state| state.prove_account(key1).ok()) - } - - fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option<(Bytes, Vec)> { - let (header, mut env_info) = match (self.block_header(id), self.env_info(id)) { - (Some(s), Some(e)) => (s, e), - _ => return None, - }; - - env_info.gas_limit = transaction.gas.clone(); - let mut jdb = self.state_db.read().journal_db().boxed_clone(); - - state::prove_transaction_virtual( - jdb.as_hash_db_mut(), - header.state_root().clone(), - &transaction, - self.engine.machine(), - &env_info, - self.factories.clone(), - ) - } - - fn epoch_signal(&self, hash: H256) -> Option> { - // pending transitions are never deleted, and do not contain - // finality proofs by definition. - self.chain.read().get_pending_transition(hash).map(|pending| pending.proof) - } + fn prove_storage(&self, key1: H256, key2: H256, id: BlockId) -> Option<(Vec, H256)> { + self.state_at(id) + .and_then(move |state| state.prove_storage(key1, key2).ok()) + } + + fn prove_account( + &self, + key1: H256, + id: BlockId, + ) -> Option<(Vec, ::types::basic_account::BasicAccount)> { + self.state_at(id) + .and_then(move |state| state.prove_account(key1).ok()) + } + + fn prove_transaction( + &self, + transaction: SignedTransaction, + id: BlockId, + ) -> Option<(Bytes, Vec)> { + let (header, mut env_info) = match (self.block_header(id), self.env_info(id)) { + (Some(s), Some(e)) => (s, e), + _ => return None, + }; + + env_info.gas_limit = transaction.gas.clone(); + let mut jdb = self.state_db.read().journal_db().boxed_clone(); + + state::prove_transaction_virtual( + jdb.as_hash_db_mut(), + header.state_root().clone(), + &transaction, + self.engine.machine(), + &env_info, + self.factories.clone(), + ) + } + + fn epoch_signal(&self, hash: H256) -> Option> { + // pending transitions are never deleted, and do not contain + // finality proofs by definition. + self.chain + .read() + .get_pending_transition(hash) + .map(|pending| pending.proof) + } } impl SnapshotClient for Client {} -impl Drop for Client { - fn drop(&mut self) { - if let Some(c) = Arc::get_mut(&mut self.engine) { - c.stop() - } else { - warn!(target: "shutdown", "unable to get mut ref for engine for shutdown."); - } - } +impl ImportExportBlocks for Client { + fn export_blocks<'a>( + &self, + mut out: Box, + from: BlockId, + to: BlockId, + format: Option, + ) -> Result<(), String> { + let from = self + .block_number(from) + .ok_or("Starting block could not be found")?; + let to = self + .block_number(to) + .ok_or("End block could not be found")?; + let format = format.unwrap_or_default(); + + for i in from..=to { + if i % 10000 == 0 { + info!("#{}", i); + } + let b = self + .block(BlockId::Number(i)) + .ok_or("Error exporting incomplete chain")? + .into_inner(); + match format { + DataFormat::Binary => { + out.write(&b) + .map_err(|e| format!("Couldn't write to stream. Cause: {}", e))?; + } + DataFormat::Hex => { + out.write_fmt(format_args!("{}\n", b.pretty())) + .map_err(|e| format!("Couldn't write to stream. Cause: {}", e))?; + } + } + } + Ok(()) + } + + fn import_blocks<'a>( + &self, + mut source: Box, + format: Option, + ) -> Result<(), String> { + const READAHEAD_BYTES: usize = 8; + + let mut first_bytes: Vec = vec![0; READAHEAD_BYTES]; + let mut first_read = 0; + + let format = match format { + Some(format) => format, + None => { + first_read = source + .read(&mut first_bytes) + .map_err(|_| "Error reading from the file/stream.")?; + match first_bytes[0] { + 0xf9 => DataFormat::Binary, + _ => DataFormat::Hex, + } + } + }; + + let do_import = |bytes: Vec| { + let block = Unverified::from_rlp(bytes).map_err(|_| "Invalid block rlp")?; + let number = block.header.number(); + while self.queue_info().is_full() { + std::thread::sleep(Duration::from_secs(1)); + } + match self.import_block(block) { + Err(Error(EthcoreErrorKind::Import(ImportErrorKind::AlreadyInChain), _)) => { + trace!("Skipping block #{}: already in chain.", number); + } + Err(e) => { + return Err(format!("Cannot import block #{}: {:?}", number, e)); + } + Ok(_) => {} + } + Ok(()) + }; + + match format { + DataFormat::Binary => loop { + let (mut bytes, n) = if first_read > 0 { + (first_bytes.clone(), first_read) + } else { + let mut bytes = vec![0; READAHEAD_BYTES]; + let n = source + .read(&mut bytes) + .map_err(|err| format!("Error reading from the file/stream: {:?}", err))?; + (bytes, n) + }; + if n == 0 { + break; + } + first_read = 0; + let s = PayloadInfo::from(&bytes) + .map_err(|e| format!("Invalid RLP in the file/stream: {:?}", e))? + .total(); + bytes.resize(s, 0); + source + .read_exact(&mut bytes[n..]) + .map_err(|err| format!("Error reading from the file/stream: {:?}", err))?; + do_import(bytes)?; + }, + DataFormat::Hex => { + for line in BufReader::new(source).lines() { + let s = line + .map_err(|err| format!("Error reading from the file/stream: {:?}", err))?; + let s = if first_read > 0 { + from_utf8(&first_bytes) + .map_err(|err| format!("Invalid UTF-8: {:?}", err))? + .to_owned() + + &(s[..]) + } else { + s + }; + first_read = 0; + let bytes = s + .from_hex() + .map_err(|err| format!("Invalid hex in file/stream: {:?}", err))?; + do_import(bytes)?; + } + } + }; + self.flush_queue(); + Ok(()) + } } /// Returns `LocalizedReceipt` given `LocalizedTransaction` /// and a vector of receipts from given block up to transaction index. fn transaction_receipt( - machine: &::machine::EthereumMachine, - mut tx: LocalizedTransaction, - receipt: Receipt, - prior_gas_used: U256, - prior_no_of_logs: usize, + machine: &::machine::EthereumMachine, + mut tx: LocalizedTransaction, + receipt: Receipt, + prior_gas_used: U256, + prior_no_of_logs: usize, ) -> LocalizedReceipt { - let sender = tx.sender(); - let transaction_hash = tx.hash(); - let block_hash = tx.block_hash; - let block_number = tx.block_number; - let transaction_index = tx.transaction_index; - - LocalizedReceipt { - from: sender, - to: match tx.action { - Action::Create => None, - Action::Call(ref address) => Some(address.clone().into()) - }, - transaction_hash: transaction_hash, - transaction_index: transaction_index, - block_hash: block_hash, - block_number: block_number, - cumulative_gas_used: receipt.gas_used, - gas_used: receipt.gas_used - prior_gas_used, - contract_address: match tx.action { - Action::Call(_) => None, - Action::Create => Some(contract_address(machine.create_address_scheme(block_number), &sender, &tx.nonce, &tx.data).0) - }, - logs: receipt.logs.into_iter().enumerate().map(|(i, log)| LocalizedLogEntry { - entry: log, - block_hash: block_hash, - block_number: block_number, - transaction_hash: transaction_hash, - transaction_index: transaction_index, - transaction_log_index: i, - log_index: prior_no_of_logs + i, - }).collect(), - log_bloom: receipt.log_bloom, - outcome: receipt.outcome, - } -} - -#[cfg(test)] -mod tests { - - #[test] - fn should_not_cache_details_before_commit() { - use client::{BlockChainClient, ChainInfo}; - use test_helpers::{generate_dummy_client, get_good_dummy_block_hash}; - - use std::thread; - use std::time::Duration; - use std::sync::Arc; - use std::sync::atomic::{AtomicBool, Ordering}; - use kvdb::DBTransaction; - use blockchain::ExtrasInsert; - use types::encoded; - - let client = generate_dummy_client(0); - let genesis = client.chain_info().best_block_hash; - let (new_hash, new_block) = get_good_dummy_block_hash(); - - let go = { - // Separate thread uncommitted transaction - let go = Arc::new(AtomicBool::new(false)); - let go_thread = go.clone(); - let another_client = client.clone(); - thread::spawn(move || { - let mut batch = DBTransaction::new(); - another_client.chain.read().insert_block(&mut batch, encoded::Block::new(new_block), Vec::new(), ExtrasInsert { - fork_choice: ::engines::ForkChoice::New, - is_finalized: false, - }); - go_thread.store(true, Ordering::SeqCst); - }); - go - }; - - while !go.load(Ordering::SeqCst) { thread::park_timeout(Duration::from_millis(5)); } - - assert!(client.tree_route(&genesis, &new_hash).is_none()); - } - - #[test] - fn should_return_block_receipts() { - use client::{BlockChainClient, BlockId, TransactionId}; - use test_helpers::{generate_dummy_client_with_data}; - - let client = generate_dummy_client_with_data(2, 2, &[1.into(), 1.into()]); - let receipts = client.localized_block_receipts(BlockId::Latest).unwrap(); - - assert_eq!(receipts.len(), 2); - assert_eq!(receipts[0].transaction_index, 0); - assert_eq!(receipts[0].block_number, 2); - assert_eq!(receipts[0].cumulative_gas_used, 53_000.into()); - assert_eq!(receipts[0].gas_used, 53_000.into()); - - assert_eq!(receipts[1].transaction_index, 1); - assert_eq!(receipts[1].block_number, 2); - assert_eq!(receipts[1].cumulative_gas_used, 106_000.into()); - assert_eq!(receipts[1].gas_used, 53_000.into()); - - let receipt = client.transaction_receipt(TransactionId::Hash(receipts[0].transaction_hash)); - assert_eq!(receipt, Some(receipts[0].clone())); - - let receipt = client.transaction_receipt(TransactionId::Hash(receipts[1].transaction_hash)); - assert_eq!(receipt, Some(receipts[1].clone())); - } - - #[test] - fn should_return_correct_log_index() { - use hash::keccak; - use super::transaction_receipt; - use ethkey::KeyPair; - use types::log_entry::{LogEntry, LocalizedLogEntry}; - use types::receipt::{Receipt, LocalizedReceipt, TransactionOutcome}; - use types::transaction::{Transaction, LocalizedTransaction, Action}; - - // given - let key = KeyPair::from_secret_slice(&keccak("test")).unwrap(); - let secret = key.secret(); - let machine = ::ethereum::new_frontier_test_machine(); - - let block_number = 1; - let block_hash = 5.into(); - let state_root = 99.into(); - let gas_used = 10.into(); - let raw_tx = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 21000.into(), - action: Action::Call(10.into()), - value: 0.into(), - data: vec![], - }; - let tx1 = raw_tx.clone().sign(secret, None); - let transaction = LocalizedTransaction { - signed: tx1.clone().into(), - block_number: block_number, - block_hash: block_hash, - transaction_index: 1, - cached_sender: Some(tx1.sender()), - }; - let logs = vec![LogEntry { - address: 5.into(), - topics: vec![], - data: vec![], - }, LogEntry { - address: 15.into(), - topics: vec![], - data: vec![], - }]; - let receipt = Receipt { - outcome: TransactionOutcome::StateRoot(state_root), - gas_used: gas_used, - log_bloom: Default::default(), - logs: logs.clone(), - }; - - // when - let receipt = transaction_receipt(&machine, transaction, receipt, 5.into(), 1); - - // then - assert_eq!(receipt, LocalizedReceipt { - from: tx1.sender().into(), - to: match tx1.action { - Action::Create => None, - Action::Call(ref address) => Some(address.clone().into()) - }, - transaction_hash: tx1.hash(), - transaction_index: 1, - block_hash: block_hash, - block_number: block_number, - cumulative_gas_used: gas_used, - gas_used: gas_used - 5, - contract_address: None, - logs: vec![LocalizedLogEntry { - entry: logs[0].clone(), - block_hash: block_hash, - block_number: block_number, - transaction_hash: tx1.hash(), - transaction_index: 1, - transaction_log_index: 0, - log_index: 1, - }, LocalizedLogEntry { - entry: logs[1].clone(), - block_hash: block_hash, - block_number: block_number, - transaction_hash: tx1.hash(), - transaction_index: 1, - transaction_log_index: 1, - log_index: 2, - }], - log_bloom: Default::default(), - outcome: TransactionOutcome::StateRoot(state_root), - }); - } + let sender = tx.sender(); + let transaction_hash = tx.hash(); + let block_hash = tx.block_hash; + let block_number = tx.block_number; + let transaction_index = tx.transaction_index; + + LocalizedReceipt { + from: sender, + to: match tx.action { + Action::Create => None, + Action::Call(ref address) => Some(address.clone().into()), + }, + transaction_hash: transaction_hash, + transaction_index: transaction_index, + block_hash: block_hash, + block_number: block_number, + cumulative_gas_used: receipt.gas_used, + gas_used: receipt.gas_used - prior_gas_used, + contract_address: match tx.action { + Action::Call(_) => None, + Action::Create => Some( + contract_address( + machine.create_address_scheme(block_number), + &sender, + &tx.nonce, + &tx.data, + ) + .0, + ), + }, + logs: receipt + .logs + .into_iter() + .enumerate() + .map(|(i, log)| LocalizedLogEntry { + entry: log, + block_hash: block_hash, + block_number: block_number, + transaction_hash: transaction_hash, + transaction_index: transaction_index, + transaction_log_index: i, + log_index: prior_no_of_logs + i, + }) + .collect(), + log_bloom: receipt.log_bloom, + outcome: receipt.outcome, + } } /// Queue some items to be processed by IO client. struct IoChannelQueue { - currently_queued: Arc, - limit: usize, + /// Using a *signed* integer for counting currently queued messages since the + /// order in which the counter is incremented and decremented is not defined. + /// Using an unsigned integer can (and will) result in integer underflow, + /// incorrectly rejecting messages and returning a FullQueue error. + currently_queued: Arc, + limit: i64, } impl IoChannelQueue { - pub fn new(limit: usize) -> Self { - IoChannelQueue { - currently_queued: Default::default(), - limit, - } - } - - pub fn queue(&self, channel: &IoChannel, count: usize, fun: F) -> Result<(), QueueError> where - F: Fn(&Client) + Send + Sync + 'static, - { - let queue_size = self.currently_queued.load(AtomicOrdering::Relaxed); - ensure!(queue_size < self.limit, QueueErrorKind::Full(self.limit)); - - let currently_queued = self.currently_queued.clone(); - let result = channel.send(ClientIoMessage::execute(move |client| { - currently_queued.fetch_sub(count, AtomicOrdering::SeqCst); - fun(client); - })); - - match result { - Ok(_) => { - self.currently_queued.fetch_add(count, AtomicOrdering::SeqCst); - Ok(()) - }, - Err(e) => bail!(QueueErrorKind::Channel(e)), - } - } + pub fn new(limit: usize) -> Self { + let limit = i64::try_from(limit).unwrap_or(i64::max_value()); + IoChannelQueue { + currently_queued: Default::default(), + limit, + } + } + + pub fn queue( + &self, + channel: &IoChannel, + count: usize, + fun: F, + ) -> EthcoreResult<()> + where + F: Fn(&Client) + Send + Sync + 'static, + { + let queue_size = self.currently_queued.load(AtomicOrdering::Relaxed); + if queue_size >= self.limit { + let err_limit = usize::try_from(self.limit).unwrap_or(usize::max_value()); + bail!("The queue is full ({})", err_limit); + }; + + let count = i64::try_from(count).unwrap_or(i64::max_value()); + + let currently_queued = self.currently_queued.clone(); + let _ok = channel.send(ClientIoMessage::execute(move |client| { + currently_queued.fetch_sub(count, AtomicOrdering::SeqCst); + fun(client); + }))?; + + self.currently_queued + .fetch_add(count, AtomicOrdering::SeqCst); + Ok(()) + } +} + +impl PrometheusMetrics for Client { + fn prometheus_metrics(&self, r: &mut prometheus::Registry) { + // gas, tx & blocks + let report = self.report(); + + for (key, value) in report.item_sizes.iter() { + prometheus_gauge( + r, + &key, + format!("Total item number of {}", key).as_str(), + *value as i64, + ); + } + + prometheus_counter( + r, + "import_gas", + "Gas processed", + report.gas_processed.as_u64() as i64, + ); + prometheus_counter( + r, + "import_blocks", + "Blocks imported", + report.blocks_imported as i64, + ); + prometheus_counter( + r, + "import_txs", + "Transactions applied", + report.transactions_applied as i64, + ); + + let state_db = self.state_db.read(); + prometheus_gauge( + r, + "statedb_cache_size", + "State DB cache size", + state_db.cache_size() as i64, + ); + + // blockchain cache + let blockchain_cache_info = self.blockchain_cache_info(); + prometheus_gauge( + r, + "blockchaincache_block_details", + "BlockDetails cache size", + blockchain_cache_info.block_details as i64, + ); + prometheus_gauge( + r, + "blockchaincache_block_recipts", + "Block receipts size", + blockchain_cache_info.block_receipts as i64, + ); + prometheus_gauge( + r, + "blockchaincache_blocks", + "Blocks cache size", + blockchain_cache_info.blocks as i64, + ); + prometheus_gauge( + r, + "blockchaincache_txaddrs", + "Transaction addresses cache size", + blockchain_cache_info.transaction_addresses as i64, + ); + prometheus_gauge( + r, + "blockchaincache_size", + "Total blockchain cache size", + blockchain_cache_info.total() as i64, + ); + + // chain info + let chain = self.chain_info(); + + let gap = chain + .ancient_block_number + .map(|x| U256::from(x + 1)) + .and_then(|first| { + chain + .first_block_number + .map(|last| (first, U256::from(last))) + }); + if let Some((first, last)) = gap { + prometheus_gauge( + r, + "chain_warpsync_gap_first", + "Warp sync gap, first block", + first.as_u64() as i64, + ); + prometheus_gauge( + r, + "chain_warpsync_gap_last", + "Warp sync gap, last block", + last.as_u64() as i64, + ); + } + + prometheus_gauge( + r, + "chain_block", + "Best block number", + chain.best_block_number as i64, + ); + + // prunning info + let prunning = self.pruning_info(); + prometheus_gauge( + r, + "prunning_earliest_chain", + "The first block which everything can be served after", + prunning.earliest_chain as i64, + ); + prometheus_gauge( + r, + "prunning_earliest_state", + "The first block where state requests may be served", + prunning.earliest_state as i64, + ); + + // queue info + let queue = self.queue_info(); + prometheus_gauge( + r, + "queue_mem_used", + "Queue heap memory used in bytes", + queue.mem_used as i64, + ); + prometheus_gauge( + r, + "queue_size_total", + "The total size of the queues", + queue.total_queue_size() as i64, + ); + prometheus_gauge( + r, + "queue_size_unverified", + "Number of queued items pending verification", + queue.unverified_queue_size as i64, + ); + prometheus_gauge( + r, + "queue_size_verified", + "Number of verified queued items pending import", + queue.verified_queue_size as i64, + ); + prometheus_gauge( + r, + "queue_size_verifying", + "Number of items being verified", + queue.verifying_queue_size as i64, + ); + } +} + +#[cfg(test)] +mod tests { + use blockchain::{BlockProvider, ExtrasInsert}; + use spec::Spec; + use test_helpers::generate_dummy_client_with_spec_and_data; + + #[test] + fn should_not_cache_details_before_commit() { + use client::{BlockChainClient, ChainInfo}; + use test_helpers::{generate_dummy_client, get_good_dummy_block_hash}; + + use kvdb::DBTransaction; + use std::{ + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + thread, + time::Duration, + }; + use types::encoded; + + let client = generate_dummy_client(0); + let genesis = client.chain_info().best_block_hash; + let (new_hash, new_block) = get_good_dummy_block_hash(); + + let go = { + // Separate thread uncommitted transaction + let go = Arc::new(AtomicBool::new(false)); + let go_thread = go.clone(); + let another_client = client.clone(); + thread::spawn(move || { + let mut batch = DBTransaction::new(); + another_client.chain.read().insert_block( + &mut batch, + encoded::Block::new(new_block), + Vec::new(), + ExtrasInsert { + fork_choice: ::engines::ForkChoice::New, + is_finalized: false, + }, + ); + go_thread.store(true, Ordering::SeqCst); + }); + go + }; + + while !go.load(Ordering::SeqCst) { + thread::park_timeout(Duration::from_millis(5)); + } + + assert!(client.tree_route(&genesis, &new_hash).is_none()); + } + + #[test] + fn should_return_block_receipts() { + use client::{BlockChainClient, BlockId, TransactionId}; + use test_helpers::generate_dummy_client_with_data; + + let client = generate_dummy_client_with_data(2, 2, &[1.into(), 1.into()]); + let receipts = client.localized_block_receipts(BlockId::Latest).unwrap(); + + assert_eq!(receipts.len(), 2); + assert_eq!(receipts[0].transaction_index, 0); + assert_eq!(receipts[0].block_number, 2); + assert_eq!(receipts[0].cumulative_gas_used, 53_000.into()); + assert_eq!(receipts[0].gas_used, 53_000.into()); + + assert_eq!(receipts[1].transaction_index, 1); + assert_eq!(receipts[1].block_number, 2); + assert_eq!(receipts[1].cumulative_gas_used, 106_000.into()); + assert_eq!(receipts[1].gas_used, 53_000.into()); + + let receipt = client.transaction_receipt(TransactionId::Hash(receipts[0].transaction_hash)); + assert_eq!(receipt, Some(receipts[0].clone())); + + let receipt = client.transaction_receipt(TransactionId::Hash(receipts[1].transaction_hash)); + assert_eq!(receipt, Some(receipts[1].clone())); + } + + #[test] + fn should_return_correct_log_index() { + use super::transaction_receipt; + use ethkey::KeyPair; + use hash::keccak; + use types::{ + log_entry::{LocalizedLogEntry, LogEntry}, + receipt::{LocalizedReceipt, Receipt, TransactionOutcome}, + transaction::{Action, LocalizedTransaction, Transaction}, + }; + + // given + let key = KeyPair::from_secret_slice(&keccak("test")).unwrap(); + let secret = key.secret(); + let machine = ::ethereum::new_frontier_test_machine(); + + let block_number = 1; + let block_hash = 5.into(); + let state_root = 99.into(); + let gas_used = 10.into(); + let raw_tx = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 21000.into(), + action: Action::Call(10.into()), + value: 0.into(), + data: vec![], + }; + let tx1 = raw_tx.clone().sign(secret, None); + let transaction = LocalizedTransaction { + signed: tx1.clone().into(), + block_number: block_number, + block_hash: block_hash, + transaction_index: 1, + cached_sender: Some(tx1.sender()), + }; + let logs = vec![ + LogEntry { + address: 5.into(), + topics: vec![], + data: vec![], + }, + LogEntry { + address: 15.into(), + topics: vec![], + data: vec![], + }, + ]; + let receipt = Receipt { + outcome: TransactionOutcome::StateRoot(state_root), + gas_used: gas_used, + log_bloom: Default::default(), + logs: logs.clone(), + }; + + // when + let receipt = transaction_receipt(&machine, transaction, receipt, 5.into(), 1); + + // then + assert_eq!( + receipt, + LocalizedReceipt { + from: tx1.sender().into(), + to: match tx1.action { + Action::Create => None, + Action::Call(ref address) => Some(address.clone().into()), + }, + transaction_hash: tx1.hash(), + transaction_index: 1, + block_hash: block_hash, + block_number: block_number, + cumulative_gas_used: gas_used, + gas_used: gas_used - 5, + contract_address: None, + logs: vec![ + LocalizedLogEntry { + entry: logs[0].clone(), + block_hash: block_hash, + block_number: block_number, + transaction_hash: tx1.hash(), + transaction_index: 1, + transaction_log_index: 0, + log_index: 1, + }, + LocalizedLogEntry { + entry: logs[1].clone(), + block_hash: block_hash, + block_number: block_number, + transaction_hash: tx1.hash(), + transaction_index: 1, + transaction_log_index: 1, + log_index: 2, + } + ], + log_bloom: Default::default(), + outcome: TransactionOutcome::StateRoot(state_root), + } + ); + } + + #[test] + fn should_mark_finalization_correctly_for_parent() { + let client = + generate_dummy_client_with_spec_and_data(Spec::new_test_with_finality, 2, 0, &[]); + let chain = client.chain(); + + let block1_details = chain.block_hash(1).and_then(|h| chain.block_details(&h)); + assert!(block1_details.is_some()); + let block1_details = block1_details.unwrap(); + assert_eq!(block1_details.children.len(), 1); + assert!(block1_details.is_finalized); + + let block2_details = chain.block_hash(2).and_then(|h| chain.block_details(&h)); + assert!(block2_details.is_some()); + let block2_details = block2_details.unwrap(); + assert_eq!(block2_details.children.len(), 0); + assert!(!block2_details.is_finalized); + } } diff --git a/ethcore/src/client/config.rs b/ethcore/src/client/config.rs index 59fc4f8135e..d53b825486b 100644 --- a/ethcore/src/client/config.rs +++ b/ethcore/src/client/config.rs @@ -1,172 +1,177 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::str::FromStr; -use std::fmt::{Display, Formatter, Error as FmtError}; +use std::{ + fmt::{Display, Error as FmtError, Formatter}, + str::FromStr, +}; -use verification::{VerifierType, QueueConfig}; use journaldb; use snapshot::SnapshotConfiguration; +use verification::{QueueConfig, VerifierType}; -pub use std::time::Duration; pub use blockchain::Config as BlockChainConfig; -pub use trace::Config as TraceConfig; pub use evm::VMType; +pub use std::time::Duration; +pub use trace::Config as TraceConfig; /// Client state db compaction profile #[derive(Debug, PartialEq, Clone)] pub enum DatabaseCompactionProfile { - /// Try to determine compaction profile automatically - Auto, - /// SSD compaction profile - SSD, - /// HDD or other slow storage io compaction profile - HDD, + /// Try to determine compaction profile automatically + Auto, + /// SSD compaction profile + SSD, + /// HDD or other slow storage io compaction profile + HDD, } impl Default for DatabaseCompactionProfile { - fn default() -> Self { - DatabaseCompactionProfile::Auto - } + fn default() -> Self { + DatabaseCompactionProfile::Auto + } } impl FromStr for DatabaseCompactionProfile { - type Err = String; - - fn from_str(s: &str) -> Result { - match s { - "auto" => Ok(DatabaseCompactionProfile::Auto), - "ssd" => Ok(DatabaseCompactionProfile::SSD), - "hdd" => Ok(DatabaseCompactionProfile::HDD), - _ => Err("Invalid compaction profile given. Expected default/hdd/ssd.".into()), - } - } + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "auto" => Ok(DatabaseCompactionProfile::Auto), + "ssd" => Ok(DatabaseCompactionProfile::SSD), + "hdd" => Ok(DatabaseCompactionProfile::HDD), + _ => Err("Invalid compaction profile given. Expected default/hdd/ssd.".into()), + } + } } /// Operating mode for the client. #[derive(Debug, Eq, PartialEq, Clone)] pub enum Mode { - /// Always on. - Active, - /// Goes offline after client is inactive for some (given) time, but - /// comes back online after a while of inactivity. - Passive(Duration, Duration), - /// Goes offline after client is inactive for some (given) time and - /// stays inactive. - Dark(Duration), - /// Always off. - Off, + /// Always on. + Active, + /// Goes offline after client is inactive for some (given) time, but + /// comes back online after a while of inactivity. + Passive(Duration, Duration), + /// Goes offline after client is inactive for some (given) time and + /// stays inactive. + Dark(Duration), + /// Always off. + Off, } impl Display for Mode { - fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { - match *self { - Mode::Active => write!(f, "active"), - Mode::Passive(..) => write!(f, "passive"), - Mode::Dark(..) => write!(f, "dark"), - Mode::Off => write!(f, "offline"), - } - } + fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { + match *self { + Mode::Active => write!(f, "active"), + Mode::Passive(..) => write!(f, "passive"), + Mode::Dark(..) => write!(f, "dark"), + Mode::Off => write!(f, "offline"), + } + } } /// Client configuration. Includes configs for all sub-systems. #[derive(Debug, PartialEq, Clone)] pub struct ClientConfig { - /// Block queue configuration. - pub queue: QueueConfig, - /// Blockchain configuration. - pub blockchain: BlockChainConfig, - /// Trace configuration. - pub tracing: TraceConfig, - /// VM type. - pub vm_type: VMType, - /// Fat DB enabled? - pub fat_db: bool, - /// The JournalDB ("pruning") algorithm to use. - pub pruning: journaldb::Algorithm, - /// The name of the client instance. - pub name: String, - /// RocksDB column cache-size if not default - pub db_cache_size: Option, - /// State db compaction profile - pub db_compaction: DatabaseCompactionProfile, - /// Operating mode - pub mode: Mode, - /// The chain spec name - pub spec_name: String, - /// Type of block verifier used by client. - pub verifier_type: VerifierType, - /// State db cache-size. - pub state_cache_size: usize, - /// EVM jump-tables cache size. - pub jump_table_size: usize, - /// Minimum state pruning history size. - pub history: u64, - /// Ideal memory usage for state pruning history. - pub history_mem: usize, - /// Check seal valididity on block import - pub check_seal: bool, - /// Maximal number of transactions queued for verification in a separate thread. - pub transaction_verification_queue_size: usize, - /// Maximal number of blocks to import at each round. - pub max_round_blocks_to_import: usize, - /// Snapshot configuration - pub snapshot: SnapshotConfiguration, + /// Block queue configuration. + pub queue: QueueConfig, + /// Blockchain configuration. + pub blockchain: BlockChainConfig, + /// Trace configuration. + pub tracing: TraceConfig, + /// VM type. + pub vm_type: VMType, + /// Fat DB enabled? + pub fat_db: bool, + /// The JournalDB ("pruning") algorithm to use. + pub pruning: journaldb::Algorithm, + /// The name of the client instance. + pub name: String, + /// RocksDB column cache-size if not default + pub db_cache_size: Option, + /// State db compaction profile + pub db_compaction: DatabaseCompactionProfile, + /// Operating mode + pub mode: Mode, + /// The chain spec name + pub spec_name: String, + /// Type of block verifier used by client. + pub verifier_type: VerifierType, + /// State db cache-size. + pub state_cache_size: usize, + /// EVM jump-tables cache size. + pub jump_table_size: usize, + /// Minimum state pruning history size. + pub history: u64, + /// Ideal memory usage for state pruning history. + pub history_mem: usize, + /// Check seal valididity on block import + pub check_seal: bool, + /// Maximal number of transactions queued for verification in a separate thread. + pub transaction_verification_queue_size: usize, + /// Maximal number of blocks to import at each round. + pub max_round_blocks_to_import: usize, + /// Snapshot configuration + pub snapshot: SnapshotConfiguration, } impl Default for ClientConfig { - fn default() -> Self { - let mb = 1024 * 1024; - ClientConfig { - queue: Default::default(), - blockchain: Default::default(), - tracing: Default::default(), - vm_type: Default::default(), - fat_db: false, - pruning: journaldb::Algorithm::OverlayRecent, - name: "default".into(), - db_cache_size: None, - db_compaction: Default::default(), - mode: Mode::Active, - spec_name: "".into(), - verifier_type: VerifierType::Canon, - state_cache_size: 1 * mb, - jump_table_size: 1 * mb, - history: 64, - history_mem: 32 * mb, - check_seal: true, - transaction_verification_queue_size: 8192, - max_round_blocks_to_import: 12, - snapshot: Default::default(), - } - } + fn default() -> Self { + let mb = 1024 * 1024; + ClientConfig { + queue: Default::default(), + blockchain: Default::default(), + tracing: Default::default(), + vm_type: Default::default(), + fat_db: false, + pruning: journaldb::Algorithm::OverlayRecent, + name: "default".into(), + db_cache_size: None, + db_compaction: Default::default(), + mode: Mode::Active, + spec_name: "".into(), + verifier_type: VerifierType::Canon, + state_cache_size: 1 * mb, + jump_table_size: 1 * mb, + history: 64, + history_mem: 32 * mb, + check_seal: true, + transaction_verification_queue_size: 8192, + max_round_blocks_to_import: 12, + snapshot: Default::default(), + } + } } #[cfg(test)] mod test { - use super::DatabaseCompactionProfile; - - #[test] - fn test_default_compaction_profile() { - assert_eq!(DatabaseCompactionProfile::default(), DatabaseCompactionProfile::Auto); - } - - #[test] - fn test_parsing_compaction_profile() { - assert_eq!(DatabaseCompactionProfile::Auto, "auto".parse().unwrap()); - assert_eq!(DatabaseCompactionProfile::SSD, "ssd".parse().unwrap()); - assert_eq!(DatabaseCompactionProfile::HDD, "hdd".parse().unwrap()); - } + use super::DatabaseCompactionProfile; + + #[test] + fn test_default_compaction_profile() { + assert_eq!( + DatabaseCompactionProfile::default(), + DatabaseCompactionProfile::Auto + ); + } + + #[test] + fn test_parsing_compaction_profile() { + assert_eq!(DatabaseCompactionProfile::Auto, "auto".parse().unwrap()); + assert_eq!(DatabaseCompactionProfile::SSD, "ssd".parse().unwrap()); + assert_eq!(DatabaseCompactionProfile::HDD, "hdd".parse().unwrap()); + } } diff --git a/ethcore/src/client/evm_test_client.rs b/ethcore/src/client/evm_test_client.rs index d6c03e65a90..94e21bcd976 100644 --- a/ethcore/src/client/evm_test_client.rs +++ b/ethcore/src/client/evm_test_client.rs @@ -1,63 +1,71 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Simple Client used for EVM tests. -use std::fmt; -use std::sync::Arc; -use ethereum_types::{H256, U256, H160}; -use {factory, journaldb, trie, kvdb_memorydb}; +use client; +use db; +use ethereum_types::{H160, H256, U256}; +use ethtrie; +use evm::{FinalizationResult, VMType}; +use executive; +use factory::{self, Factories}; +use journaldb; use kvdb::{self, KeyValueDB}; -use {state, state_db, client, executive, trace, db, spec, pod_state}; +use kvdb_memorydb; +use pod_state; +use spec; +use state; +use state_db; +use std::{fmt, sync::Arc}; +use trace; +use trie; use types::{log_entry, receipt, transaction}; -use factory::Factories; -use evm::{VMType, FinalizationResult}; use vm::{self, ActionParams}; -use ethtrie; /// EVM test Error. #[derive(Debug)] pub enum EvmTestError { - /// Trie integrity error. - Trie(Box), - /// EVM error. - Evm(vm::Error), - /// Initialization error. - ClientError(::error::Error), - /// Post-condition failure, - PostCondition(String), + /// Trie integrity error. + Trie(Box), + /// EVM error. + Evm(vm::Error), + /// Initialization error. + ClientError(::error::Error), + /// Post-condition failure, + PostCondition(String), } impl> From for EvmTestError { - fn from(err: E) -> Self { - EvmTestError::ClientError(err.into()) - } + fn from(err: E) -> Self { + EvmTestError::ClientError(err.into()) + } } impl fmt::Display for EvmTestError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - use self::EvmTestError::*; - - match *self { - Trie(ref err) => write!(fmt, "Trie: {}", err), - Evm(ref err) => write!(fmt, "EVM: {}", err), - ClientError(ref err) => write!(fmt, "{}", err), - PostCondition(ref err) => write!(fmt, "{}", err), - } - } + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + use self::EvmTestError::*; + + match *self { + Trie(ref err) => write!(fmt, "Trie: {}", err), + Evm(ref err) => write!(fmt, "EVM: {}", err), + ClientError(ref err) => write!(fmt, "{}", err), + PostCondition(ref err) => write!(fmt, "{}", err), + } + } } use ethereum; @@ -65,282 +73,335 @@ use ethjson::spec::ForkSpec; /// Simplified, single-block EVM test client. pub struct EvmTestClient<'a> { - state: state::State, - spec: &'a spec::Spec, - dump_state: fn(&state::State) -> Option, + state: state::State, + spec: &'a spec::Spec, + dump_state: fn(&state::State) -> Option, } fn no_dump_state(_: &state::State) -> Option { - None + None } impl<'a> fmt::Debug for EvmTestClient<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("EvmTestClient") - .field("state", &self.state) - .field("spec", &self.spec.name) - .finish() - } + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("EvmTestClient") + .field("state", &self.state) + .field("spec", &self.spec.name) + .finish() + } } impl<'a> EvmTestClient<'a> { - /// Converts a json spec definition into spec. - pub fn spec_from_json(spec: &ForkSpec) -> Option { - match *spec { - ForkSpec::Frontier => Some(ethereum::new_frontier_test()), - ForkSpec::Homestead => Some(ethereum::new_homestead_test()), - ForkSpec::EIP150 => Some(ethereum::new_eip150_test()), - ForkSpec::EIP158 => Some(ethereum::new_eip161_test()), - ForkSpec::Byzantium => Some(ethereum::new_byzantium_test()), - ForkSpec::Constantinople => Some(ethereum::new_constantinople_test()), - ForkSpec::ConstantinopleFix => Some(ethereum::new_constantinople_fix_test()), - ForkSpec::EIP158ToByzantiumAt5 => Some(ethereum::new_transition_test()), - ForkSpec::FrontierToHomesteadAt5 | ForkSpec::HomesteadToDaoAt5 | ForkSpec::HomesteadToEIP150At5 => None, - } - } - - /// Change default function for dump state (default does not dump) - pub fn set_dump_state_fn(&mut self, dump_state: fn(&state::State) -> Option) { - self.dump_state = dump_state; - } - - /// Creates new EVM test client with in-memory DB initialized with genesis of given Spec. - /// Takes a `TrieSpec` to set the type of trie. - pub fn new_with_trie(spec: &'a spec::Spec, trie_spec: trie::TrieSpec) -> Result { - let factories = Self::factories(trie_spec); - let state = Self::state_from_spec(spec, &factories)?; - - Ok(EvmTestClient { - state, - spec, - dump_state: no_dump_state, - }) - } - - /// Creates new EVM test client with an in-memory DB initialized with genesis of given chain Spec. - pub fn new(spec: &'a spec::Spec) -> Result { - Self::new_with_trie(spec, trie::TrieSpec::Secure) - } - - /// Creates new EVM test client with an in-memory DB initialized with given PodState. - /// Takes a `TrieSpec` to set the type of trie. - pub fn from_pod_state_with_trie(spec: &'a spec::Spec, pod_state: pod_state::PodState, trie_spec: trie::TrieSpec) -> Result { - let factories = Self::factories(trie_spec); - let state = Self::state_from_pod(spec, &factories, pod_state)?; - - Ok(EvmTestClient { - state, - spec, - dump_state: no_dump_state, - }) - } - - /// Creates new EVM test client with an in-memory DB initialized with given PodState. - pub fn from_pod_state(spec: &'a spec::Spec, pod_state: pod_state::PodState) -> Result { - Self::from_pod_state_with_trie(spec, pod_state, trie::TrieSpec::Secure) - } - - fn factories(trie_spec: trie::TrieSpec) -> Factories { - Factories { - vm: factory::VmFactory::new(VMType::Interpreter, 5 * 1024), - trie: trie::TrieFactory::new(trie_spec), - accountdb: Default::default(), - } - } - - fn state_from_spec(spec: &'a spec::Spec, factories: &Factories) -> Result, EvmTestError> { - let db = Arc::new(kvdb_memorydb::create(db::NUM_COLUMNS.expect("We use column-based DB; qed"))); - let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE); - let mut state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024); - state_db = spec.ensure_db_good(state_db, factories)?; - - let genesis = spec.genesis_header(); - // Write DB - { - let mut batch = kvdb::DBTransaction::new(); - state_db.journal_under(&mut batch, 0, &genesis.hash())?; - db.write(batch)?; - } - - state::State::from_existing( - state_db, - *genesis.state_root(), - spec.engine.account_start_nonce(0), - factories.clone() - ).map_err(EvmTestError::Trie) - } - - fn state_from_pod(spec: &'a spec::Spec, factories: &Factories, pod_state: pod_state::PodState) -> Result, EvmTestError> { - let db = Arc::new(kvdb_memorydb::create(db::NUM_COLUMNS.expect("We use column-based DB; qed"))); - let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE); - let state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024); - let mut state = state::State::new( - state_db, - spec.engine.account_start_nonce(0), - factories.clone(), - ); - state.populate_from(pod_state); - state.commit()?; - Ok(state) - } - - /// Return current state. - pub fn state(&self) -> &state::State { - &self.state - } - - /// Execute the VM given ActionParams and tracer. - /// Returns amount of gas left and the output. - pub fn call( - &mut self, - params: ActionParams, - tracer: &mut T, - vm_tracer: &mut V, - ) -> Result - { - let genesis = self.spec.genesis_header(); - let info = client::EnvInfo { - number: genesis.number(), - author: *genesis.author(), - timestamp: genesis.timestamp(), - difficulty: *genesis.difficulty(), - last_hashes: Arc::new([H256::default(); 256].to_vec()), - gas_used: 0.into(), - gas_limit: *genesis.gas_limit(), - }; - self.call_envinfo(params, tracer, vm_tracer, info) - } - - /// Execute the VM given envinfo, ActionParams and tracer. - /// Returns amount of gas left and the output. - pub fn call_envinfo( - &mut self, - params: ActionParams, - tracer: &mut T, - vm_tracer: &mut V, - info: client::EnvInfo, - ) -> Result - { - let mut substate = state::Substate::new(); - let machine = self.spec.engine.machine(); - let schedule = machine.schedule(info.number); - let mut executive = executive::Executive::new(&mut self.state, &info, &machine, &schedule); - executive.call( - params, - &mut substate, - tracer, - vm_tracer, - ).map_err(EvmTestError::Evm) - } - - /// Executes a SignedTransaction within context of the provided state and `EnvInfo`. - /// Returns the state root, gas left and the output. - pub fn transact( - &mut self, - env_info: &client::EnvInfo, - transaction: transaction::SignedTransaction, - tracer: T, - vm_tracer: V, - ) -> std::result::Result, TransactErr> { - let initial_gas = transaction.gas; - // Verify transaction - let is_ok = transaction.verify_basic(true, None, false); - if let Err(error) = is_ok { - return Err( - TransactErr{ - state_root: *self.state.root(), - error: error.into(), - end_state: (self.dump_state)(&self.state), - }); - } - - // Apply transaction - let result = self.state.apply_with_tracing(&env_info, self.spec.engine.machine(), &transaction, tracer, vm_tracer); - let scheme = self.spec.engine.machine().create_address_scheme(env_info.number); - - // Touch the coinbase at the end of the test to simulate - // miner reward. - // Details: https://github.com/paritytech/parity-ethereum/issues/9431 - let schedule = self.spec.engine.machine().schedule(env_info.number); - self.state.add_balance(&env_info.author, &0.into(), if schedule.no_empty { - state::CleanupMode::NoEmpty - } else { - state::CleanupMode::ForceCreate - }).ok(); - // Touching also means that we should remove the account if it's within eip161 - // conditions. - self.state.kill_garbage( - &vec![env_info.author].into_iter().collect(), - schedule.kill_empty, - &None, - false - ).ok(); - - self.state.commit().ok(); - - let state_root = *self.state.root(); - - let end_state = (self.dump_state)(&self.state); - - match result { - Ok(result) => { - Ok(TransactSuccess { - state_root, - gas_left: initial_gas - result.receipt.gas_used, - outcome: result.receipt.outcome, - output: result.output, - trace: result.trace, - vm_trace: result.vm_trace, - logs: result.receipt.logs, - contract_address: if let transaction::Action::Create = transaction.action { - Some(executive::contract_address(scheme, &transaction.sender(), &transaction.nonce, &transaction.data).0) - } else { - None - }, - end_state, - } - )}, - Err(error) => Err(TransactErr { - state_root, - error, - end_state, - }), - } - } + /// Converts a json spec definition into spec. + pub fn spec_from_json(spec: &ForkSpec) -> Option { + match *spec { + ForkSpec::Frontier => Some(ethereum::new_frontier_test()), + ForkSpec::Homestead => Some(ethereum::new_homestead_test()), + ForkSpec::EIP150 => Some(ethereum::new_eip150_test()), + ForkSpec::EIP158 => Some(ethereum::new_eip161_test()), + ForkSpec::Byzantium => Some(ethereum::new_byzantium_test()), + ForkSpec::Constantinople => Some(ethereum::new_constantinople_test()), + ForkSpec::ConstantinopleFix => Some(ethereum::new_constantinople_fix_test()), + ForkSpec::Istanbul => Some(ethereum::new_istanbul_test()), + ForkSpec::EIP158ToByzantiumAt5 => Some(ethereum::new_transition_test()), + ForkSpec::ByzantiumToConstantinopleFixAt5 => { + Some(ethereum::new_byzantium_to_constantinoplefixat5_test()) + } + ForkSpec::Berlin => Some(ethereum::new_berlin_test()), + ForkSpec::FrontierToHomesteadAt5 + | ForkSpec::HomesteadToDaoAt5 + | ForkSpec::HomesteadToEIP150At5 + | ForkSpec::ByzantiumToConstantinopleAt5 => None, + } + } + + /// Change default function for dump state (default does not dump) + pub fn set_dump_state_fn( + &mut self, + dump_state: fn(&state::State) -> Option, + ) { + self.dump_state = dump_state; + } + + /// Creates new EVM test client with in-memory DB initialized with genesis of given Spec. + /// Takes a `TrieSpec` to set the type of trie. + pub fn new_with_trie( + spec: &'a spec::Spec, + trie_spec: trie::TrieSpec, + ) -> Result { + let factories = Self::factories(trie_spec); + let state = Self::state_from_spec(spec, &factories)?; + + Ok(EvmTestClient { + state, + spec, + dump_state: no_dump_state, + }) + } + + /// Creates new EVM test client with an in-memory DB initialized with genesis of given chain Spec. + pub fn new(spec: &'a spec::Spec) -> Result { + Self::new_with_trie(spec, trie::TrieSpec::Secure) + } + + /// Creates new EVM test client with an in-memory DB initialized with given PodState. + /// Takes a `TrieSpec` to set the type of trie. + pub fn from_pod_state_with_trie( + spec: &'a spec::Spec, + pod_state: pod_state::PodState, + trie_spec: trie::TrieSpec, + ) -> Result { + let factories = Self::factories(trie_spec); + let state = Self::state_from_pod(spec, &factories, pod_state)?; + + Ok(EvmTestClient { + state, + spec, + dump_state: no_dump_state, + }) + } + + /// Creates new EVM test client with an in-memory DB initialized with given PodState. + pub fn from_pod_state( + spec: &'a spec::Spec, + pod_state: pod_state::PodState, + ) -> Result { + Self::from_pod_state_with_trie(spec, pod_state, trie::TrieSpec::Secure) + } + + fn factories(trie_spec: trie::TrieSpec) -> Factories { + Factories { + vm: factory::VmFactory::new(VMType::Interpreter, 5 * 1024), + trie: trie::TrieFactory::new(trie_spec), + accountdb: Default::default(), + } + } + + fn state_from_spec( + spec: &'a spec::Spec, + factories: &Factories, + ) -> Result, EvmTestError> { + let db = Arc::new(kvdb_memorydb::create( + db::NUM_COLUMNS.expect("We use column-based DB; qed"), + )); + let journal_db = + journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE); + let mut state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024); + state_db = spec.ensure_db_good(state_db, factories)?; + + let genesis = spec.genesis_header(); + // Write DB + { + let mut batch = kvdb::DBTransaction::new(); + state_db.journal_under(&mut batch, 0, &genesis.hash())?; + db.write(batch)?; + } + + state::State::from_existing( + state_db, + *genesis.state_root(), + spec.engine.account_start_nonce(0), + factories.clone(), + ) + .map_err(EvmTestError::Trie) + } + + fn state_from_pod( + spec: &'a spec::Spec, + factories: &Factories, + pod_state: pod_state::PodState, + ) -> Result, EvmTestError> { + let db = Arc::new(kvdb_memorydb::create( + db::NUM_COLUMNS.expect("We use column-based DB; qed"), + )); + let journal_db = + journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE); + let state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024); + let mut state = state::State::new( + state_db, + spec.engine.account_start_nonce(0), + factories.clone(), + ); + state.populate_from(pod_state); + state.commit()?; + Ok(state) + } + + /// Return current state. + pub fn state(&self) -> &state::State { + &self.state + } + + /// Execute the VM given ActionParams and tracer. + /// Returns amount of gas left and the output. + pub fn call( + &mut self, + params: ActionParams, + tracer: &mut T, + vm_tracer: &mut V, + ) -> Result { + let genesis = self.spec.genesis_header(); + let info = client::EnvInfo { + number: genesis.number(), + author: *genesis.author(), + timestamp: genesis.timestamp(), + difficulty: *genesis.difficulty(), + last_hashes: Arc::new([H256::default(); 256].to_vec()), + gas_used: 0.into(), + gas_limit: *genesis.gas_limit(), + }; + self.call_envinfo(params, tracer, vm_tracer, info) + } + + /// Execute the VM given envinfo, ActionParams and tracer. + /// Returns amount of gas left and the output. + pub fn call_envinfo( + &mut self, + params: ActionParams, + tracer: &mut T, + vm_tracer: &mut V, + info: client::EnvInfo, + ) -> Result { + let mut substate = state::Substate::new(); + let machine = self.spec.engine.machine(); + let schedule = machine.schedule(info.number); + let mut executive = executive::Executive::new(&mut self.state, &info, &machine, &schedule); + executive + .call(params, &mut substate, tracer, vm_tracer) + .map_err(EvmTestError::Evm) + } + + /// Executes a SignedTransaction within context of the provided state and `EnvInfo`. + /// Returns the state root, gas left and the output. + pub fn transact( + &mut self, + env_info: &client::EnvInfo, + transaction: transaction::SignedTransaction, + tracer: T, + vm_tracer: V, + ) -> std::result::Result, TransactErr> { + let initial_gas = transaction.gas; + // Verify transaction + let is_ok = transaction.verify_basic(true, None); + if let Err(error) = is_ok { + return Err(TransactErr { + state_root: *self.state.root(), + error: error.into(), + end_state: (self.dump_state)(&self.state), + }); + } + + // Apply transaction + let result = self.state.apply_with_tracing( + &env_info, + self.spec.engine.machine(), + &transaction, + tracer, + vm_tracer, + ); + let scheme = self + .spec + .engine + .machine() + .create_address_scheme(env_info.number); + + // Touch the coinbase at the end of the test to simulate + // miner reward. + // Details: https://github.com/openethereum/openethereum/issues/9431 + let schedule = self.spec.engine.machine().schedule(env_info.number); + self.state + .add_balance( + &env_info.author, + &0.into(), + if schedule.no_empty { + state::CleanupMode::NoEmpty + } else { + state::CleanupMode::ForceCreate + }, + ) + .ok(); + // Touching also means that we should remove the account if it's within eip161 + // conditions. + self.state + .kill_garbage( + &vec![env_info.author].into_iter().collect(), + schedule.kill_empty, + &None, + false, + ) + .ok(); + + self.state.commit().ok(); + + let state_root = *self.state.root(); + + let end_state = (self.dump_state)(&self.state); + + match result { + Ok(result) => Ok(TransactSuccess { + state_root, + gas_left: initial_gas - result.receipt.gas_used, + outcome: result.receipt.outcome, + output: result.output, + trace: result.trace, + vm_trace: result.vm_trace, + logs: result.receipt.logs, + contract_address: if let transaction::Action::Create = transaction.action { + Some( + executive::contract_address( + scheme, + &transaction.sender(), + &transaction.nonce, + &transaction.data, + ) + .0, + ) + } else { + None + }, + end_state, + }), + Err(error) => Err(TransactErr { + state_root, + error, + end_state, + }), + } + } } /// To be returned inside a std::result::Result::Ok after a successful /// transaction completed. #[allow(dead_code)] pub struct TransactSuccess { - /// State root - pub state_root: H256, - /// Amount of gas left - pub gas_left: U256, - /// Output - pub output: Vec, - /// Traces - pub trace: Vec, - /// VM Traces - pub vm_trace: Option, - /// Created contract address (if any) - pub contract_address: Option, - /// Generated logs - pub logs: Vec, - /// outcome - pub outcome: receipt::TransactionOutcome, - /// end state if needed - pub end_state: Option, + /// State root + pub state_root: H256, + /// Amount of gas left + pub gas_left: U256, + /// Output + pub output: Vec, + /// Traces + pub trace: Vec, + /// VM Traces + pub vm_trace: Option, + /// Created contract address (if any) + pub contract_address: Option, + /// Generated logs + pub logs: Vec, + /// outcome + pub outcome: receipt::TransactionOutcome, + /// end state if needed + pub end_state: Option, } /// To be returned inside a std::result::Result::Err after a failed /// transaction. #[allow(dead_code)] pub struct TransactErr { - /// State root - pub state_root: H256, - /// Execution error - pub error: ::error::Error, - /// end state if needed - pub end_state: Option, + /// State root + pub state_root: H256, + /// Execution error + pub error: ::error::Error, + /// end state if needed + pub end_state: Option, } diff --git a/ethcore/src/client/io_message.rs b/ethcore/src/client/io_message.rs index 92e2d3e258e..cd9c36cf0c6 100644 --- a/ethcore/src/client/io_message.rs +++ b/ethcore/src/client/io_message.rs @@ -1,56 +1,56 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::fmt; use bytes::Bytes; use client::Client; use ethereum_types::H256; use snapshot::ManifestData; +use std::fmt; /// Message type for external and internal events #[derive(Debug)] pub enum ClientIoMessage { - /// Best Block Hash in chain has been changed - NewChainHead, - /// A block is ready - BlockVerified, - /// Begin snapshot restoration - BeginRestoration(ManifestData), - /// Feed a state chunk to the snapshot service - FeedStateChunk(H256, Bytes), - /// Feed a block chunk to the snapshot service - FeedBlockChunk(H256, Bytes), - /// Take a snapshot for the block with given number. - TakeSnapshot(u64), - /// Execute wrapped closure - Execute(Callback), + /// Best Block Hash in chain has been changed + NewChainHead, + /// A block is ready + BlockVerified, + /// Begin snapshot restoration + BeginRestoration(ManifestData), + /// Feed a state chunk to the snapshot service + FeedStateChunk(H256, Bytes), + /// Feed a block chunk to the snapshot service + FeedBlockChunk(H256, Bytes), + /// Take a snapshot for the block with given number. + TakeSnapshot(u64), + /// Execute wrapped closure + Execute(Callback), } impl ClientIoMessage { - /// Create new `ClientIoMessage` that executes given procedure. - pub fn execute(fun: F) -> Self { - ClientIoMessage::Execute(Callback(Box::new(fun))) - } + /// Create new `ClientIoMessage` that executes given procedure. + pub fn execute(fun: F) -> Self { + ClientIoMessage::Execute(Callback(Box::new(fun))) + } } /// A function to invoke in the client thread. -pub struct Callback(pub Box); +pub struct Callback(pub Box); impl fmt::Debug for Callback { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "") - } + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "") + } } diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 51b4f53db84..300dbfb5a4a 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Blockchain database client. @@ -24,37 +24,39 @@ mod config; mod evm_test_client; mod io_message; #[cfg(any(test, feature = "test-helpers"))] -mod test_client; +pub mod test_client; mod trace; -pub use self::client::*; -pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChainConfig, VMType}; #[cfg(any(test, feature = "test-helpers"))] pub use self::evm_test_client::{EvmTestClient, EvmTestError, TransactErr, TransactSuccess}; -pub use self::io_message::ClientIoMessage; #[cfg(any(test, feature = "test-helpers"))] -pub use self::test_client::{TestBlockChainClient, EachBlockWith}; -pub use self::chain_notify::{ChainNotify, NewBlocks, ChainRoute, ChainRouteType, ChainMessageType}; -pub use self::traits::{ - Nonce, Balance, ChainInfo, BlockInfo, ReopenBlock, PrepareOpenBlock, TransactionInfo, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock, - StateOrBlock, StateClient, Call, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter, BadBlocks, - BlockChainReset +pub use self::test_client::{EachBlockWith, TestBlockChainClient}; +pub use self::{ + chain_notify::{ChainMessageType, ChainNotify, ChainRoute, ChainRouteType, NewBlocks}, + client::*, + config::{BlockChainConfig, ClientConfig, DatabaseCompactionProfile, Mode, VMType}, + io_message::ClientIoMessage, + traits::{ + AccountData, BadBlocks, Balance, BlockChain, BlockChainClient, BlockChainReset, BlockInfo, + BlockProducer, BroadcastProposalBlock, Call, ChainInfo, EngineClient, EngineInfo, + ImportBlock, ImportExportBlocks, ImportSealedBlock, IoClient, Nonce, PrepareOpenBlock, + ProvingBlockChainClient, ReopenBlock, ScheduleInfo, SealedBlockImporter, StateClient, + StateOrBlock, TransactionInfo, + }, }; pub use state::StateInfo; -pub use self::traits::{BlockChainClient, EngineClient, ProvingBlockChainClient, IoClient}; -pub use types::ids::*; -pub use types::trace_filter::Filter as TraceFilter; -pub use types::pruning_info::PruningInfo; -pub use types::call_analytics::CallAnalytics; +pub use types::{ + call_analytics::CallAnalytics, ids::*, pruning_info::PruningInfo, + trace_filter::Filter as TraceFilter, +}; pub use executive::{Executed, Executive, TransactOptions}; -pub use vm::{LastHashes, EnvInfo}; +pub use vm::{EnvInfo, LastHashes}; pub use error::TransactionImportError; pub use verification::VerifierType; -mod traits; +pub mod traits; mod chain_notify; -mod private_notify; diff --git a/ethcore/src/client/private_notify.rs b/ethcore/src/client/private_notify.rs deleted file mode 100644 index 4be18387327..00000000000 --- a/ethcore/src/client/private_notify.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use error::TransactionImportError; - -/// Represent private transactions handler inside the client -pub trait PrivateNotify : Send + Sync { - /// fires when private transaction message queued via client io queue - fn private_transaction_queued(&self) -> Result<(), TransactionImportError>; -} diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 502a9ee72b4..f48fc477aea 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -1,31 +1,34 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Test client. -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrder}; -use std::sync::Arc; -use std::collections::{HashMap, BTreeMap}; -use std::mem; +use std::{ + collections::{BTreeMap, HashMap}, + sync::{ + atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrder}, + Arc, + }, +}; -use blockchain::{TreeRoute, BlockReceipts}; +use blockchain::{BlockReceipts, TreeRoute}; use bytes::Bytes; -use db::{NUM_COLUMNS, COL_STATE}; +use db::{COL_STATE, NUM_COLUMNS}; use ethcore_miner::pool::VerifiedTransaction; -use ethereum_types::{H256, U256, Address}; +use ethereum_types::{Address, H256, U256}; use ethkey::{Generator, Random}; use ethtrie; use hash::keccak; @@ -35,28 +38,30 @@ use kvdb_memorydb; use parking_lot::RwLock; use rlp::{Rlp, RlpStream}; use rustc_hex::FromHex; -use types::transaction::{self, Transaction, LocalizedTransaction, SignedTransaction, Action}; -use types::BlockNumber; -use types::basic_account::BasicAccount; -use types::encoded; -use types::filter::Filter; -use types::header::Header; -use types::log_entry::LocalizedLogEntry; -use types::pruning_info::PruningInfo; -use types::receipt::{Receipt, LocalizedReceipt, TransactionOutcome}; -use types::view; -use types::views::BlockView; +use types::{ + basic_account::BasicAccount, + encoded, + filter::Filter, + header::Header, + log_entry::LocalizedLogEntry, + pruning_info::PruningInfo, + receipt::{LocalizedReceipt, Receipt, TransactionOutcome}, + transaction::{self, Action, LocalizedTransaction, SignedTransaction, Transaction}, + view, + views::BlockView, + BlockNumber, +}; use vm::Schedule; -use block::{OpenBlock, SealedBlock, ClosedBlock}; +use block::{ClosedBlock, OpenBlock, SealedBlock}; use call_contract::{CallContract, RegistryInfo}; use client::{ - Nonce, Balance, ChainInfo, BlockInfo, ReopenBlock, TransactionInfo, - PrepareOpenBlock, BlockChainClient, BlockChainInfo, BlockStatus, BlockId, Mode, - TransactionId, UncleId, TraceId, TraceFilter, LastHashes, CallAnalytics, - ProvingBlockChainClient, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock, StateOrBlock, - Call, StateClient, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter, IoClient, - BadBlocks, + traits::ForceUpdateSealing, AccountData, BadBlocks, Balance, BlockChain, BlockChainClient, + BlockChainInfo, BlockId, BlockInfo, BlockProducer, BlockStatus, BroadcastProposalBlock, Call, + CallAnalytics, ChainInfo, EngineInfo, ImportBlock, ImportSealedBlock, IoClient, LastHashes, + Mode, Nonce, PrepareOpenBlock, ProvingBlockChainClient, ReopenBlock, ScheduleInfo, + SealedBlockImporter, StateClient, StateOrBlock, TraceFilter, TraceId, TransactionId, + TransactionInfo, UncleId, }; use engines::EthEngine; use error::{Error, EthcoreResult}; @@ -67,379 +72,401 @@ use miner::{self, Miner, MinerService}; use spec::Spec; use state::StateInfo; use state_db::StateDB; +use stats::{prometheus, PrometheusMetrics}; use trace::LocalizedTrace; -use verification::queue::QueueInfo; -use verification::queue::kind::blocks::Unverified; +use verification::queue::{kind::blocks::Unverified, QueueInfo}; /// Test client. pub struct TestBlockChainClient { - /// Blocks. - pub blocks: RwLock>, - /// Mapping of numbers to hashes. - pub numbers: RwLock>, - /// Genesis block hash. - pub genesis_hash: H256, - /// Last block hash. - pub last_hash: RwLock, - /// Extra data do set for each block - pub extra_data: Bytes, - /// Difficulty. - pub difficulty: RwLock, - /// Balances. - pub balances: RwLock>, - /// Nonces. - pub nonces: RwLock>, - /// Storage. - pub storage: RwLock>, - /// Code. - pub code: RwLock>, - /// Execution result. - pub execution_result: RwLock>>, - /// Transaction receipts. - pub receipts: RwLock>, - /// Logs - pub logs: RwLock>, - /// Should return errors on logs. - pub error_on_logs: RwLock>, - /// Block queue size. - pub queue_size: AtomicUsize, - /// Miner - pub miner: Arc, - /// Spec - pub spec: Spec, - /// Timestamp assigned to latest sealed block - pub latest_block_timestamp: RwLock, - /// Ancient block info. - pub ancient_block: RwLock>, - /// First block info. - pub first_block: RwLock>, - /// Traces to return - pub traces: RwLock>>, - /// Pruning history size to report. - pub history: RwLock>, - /// Is disabled - pub disabled: AtomicBool, + /// Blocks. + pub blocks: RwLock>, + /// Mapping of numbers to hashes. + pub numbers: RwLock>, + /// Genesis block hash. + pub genesis_hash: H256, + /// Last block hash. + pub last_hash: RwLock, + /// Extra data do set for each block + pub extra_data: Bytes, + /// Difficulty. + pub difficulty: RwLock, + /// Balances. + pub balances: RwLock>, + /// Nonces. + pub nonces: RwLock>, + /// Storage. + pub storage: RwLock>, + /// Code. + pub code: RwLock>, + /// Execution result. + pub execution_result: RwLock>>, + /// Transaction receipts. + pub receipts: RwLock>, + /// Logs + pub logs: RwLock>, + /// Should return errors on logs. + pub error_on_logs: RwLock>, + /// Block queue size. + pub queue_size: AtomicUsize, + /// Miner + pub miner: Arc, + /// Spec + pub spec: Spec, + /// Timestamp assigned to latest sealed block + pub latest_block_timestamp: RwLock, + /// Ancient block info. + pub ancient_block: RwLock>, + /// First block info. + pub first_block: RwLock>, + /// Traces to return + pub traces: RwLock>>, + /// Pruning history size to report. + pub history: RwLock>, + /// Is disabled + pub disabled: AtomicBool, } /// Used for generating test client blocks. #[derive(Clone, Copy)] pub enum EachBlockWith { - /// Plain block. - Nothing, - /// Block with an uncle. - Uncle, - /// Block with a transaction. - Transaction, - /// Block with multiple transactions. - Transactions(usize), - /// Block with an uncle and transaction. - UncleAndTransaction + /// Plain block. + Nothing, + /// Block with an uncle. + Uncle, + /// Block with a transaction. + Transaction, + /// Block with multiple transactions. + Transactions(usize), + /// Block with an uncle and transaction. + UncleAndTransaction, } impl Default for TestBlockChainClient { - fn default() -> Self { - TestBlockChainClient::new() - } + fn default() -> Self { + TestBlockChainClient::new() + } } impl TestBlockChainClient { - /// Creates new test client. - pub fn new() -> Self { - Self::new_with_extra_data(Bytes::new()) - } - - /// Creates new test client with specified extra data for each block - pub fn new_with_extra_data(extra_data: Bytes) -> Self { - let spec = Spec::new_test(); - TestBlockChainClient::new_with_spec_and_extra(spec, extra_data) - } - - /// Create test client with custom spec. - pub fn new_with_spec(spec: Spec) -> Self { - TestBlockChainClient::new_with_spec_and_extra(spec, Bytes::new()) - } - - /// Create test client with custom spec and extra data. - pub fn new_with_spec_and_extra(spec: Spec, extra_data: Bytes) -> Self { - let genesis_block = spec.genesis_block(); - let genesis_hash = spec.genesis_header().hash(); - - let mut client = TestBlockChainClient { - blocks: RwLock::new(HashMap::new()), - numbers: RwLock::new(HashMap::new()), - genesis_hash: H256::new(), - extra_data: extra_data, - last_hash: RwLock::new(H256::new()), - difficulty: RwLock::new(spec.genesis_header().difficulty().clone()), - balances: RwLock::new(HashMap::new()), - nonces: RwLock::new(HashMap::new()), - storage: RwLock::new(HashMap::new()), - code: RwLock::new(HashMap::new()), - execution_result: RwLock::new(None), - receipts: RwLock::new(HashMap::new()), - logs: RwLock::new(Vec::new()), - queue_size: AtomicUsize::new(0), - miner: Arc::new(Miner::new_for_tests(&spec, None)), - spec: spec, - latest_block_timestamp: RwLock::new(10_000_000), - ancient_block: RwLock::new(None), - first_block: RwLock::new(None), - traces: RwLock::new(None), - history: RwLock::new(None), - disabled: AtomicBool::new(false), - error_on_logs: RwLock::new(None), - }; - - // insert genesis hash. - client.blocks.get_mut().insert(genesis_hash, genesis_block); - client.numbers.get_mut().insert(0, genesis_hash); - *client.last_hash.get_mut() = genesis_hash; - client.genesis_hash = genesis_hash; - client - } - - /// Set the transaction receipt result - pub fn set_transaction_receipt(&self, id: TransactionId, receipt: LocalizedReceipt) { - self.receipts.write().insert(id, receipt); - } - - /// Set the execution result. - pub fn set_execution_result(&self, result: Result) { - *self.execution_result.write() = Some(result); - } - - /// Set the balance of account `address` to `balance`. - pub fn set_balance(&self, address: Address, balance: U256) { - self.balances.write().insert(address, balance); - } - - /// Set nonce of account `address` to `nonce`. - pub fn set_nonce(&self, address: Address, nonce: U256) { - self.nonces.write().insert(address, nonce); - } - - /// Set `code` at `address`. - pub fn set_code(&self, address: Address, code: Bytes) { - self.code.write().insert(address, code); - } - - /// Set storage `position` to `value` for account `address`. - pub fn set_storage(&self, address: Address, position: H256, value: H256) { - self.storage.write().insert((address, position), value); - } - - /// Set block queue size for testing - pub fn set_queue_size(&self, size: usize) { - self.queue_size.store(size, AtomicOrder::Relaxed); - } - - /// Set timestamp assigned to latest sealed block - pub fn set_latest_block_timestamp(&self, ts: u64) { - *self.latest_block_timestamp.write() = ts; - } - - /// Set logs to return for each logs call. - pub fn set_logs(&self, logs: Vec) { - *self.logs.write() = logs; - } - - /// Set return errors on logs. - pub fn set_error_on_logs(&self, val: Option) { - *self.error_on_logs.write() = val; - } - - /// Add a block to test client. - pub fn add_block(&self, with: EachBlockWith, hook: F) - where F: Fn(Header) -> Header - { - let n = self.numbers.read().len(); - - let mut header = Header::new(); - header.set_difficulty(From::from(n)); - header.set_parent_hash(self.last_hash.read().clone()); - header.set_number(n as BlockNumber); - header.set_gas_limit(U256::from(1_000_000)); - header.set_extra_data(self.extra_data.clone()); - - header = hook(header); - - let uncles = match with { - EachBlockWith::Uncle | EachBlockWith::UncleAndTransaction => { - let mut uncles = RlpStream::new_list(1); - let mut uncle_header = Header::new(); - uncle_header.set_difficulty(From::from(n)); - uncle_header.set_parent_hash(self.last_hash.read().clone()); - uncle_header.set_number(n as BlockNumber); - uncles.append(&uncle_header); - header.set_uncles_hash(keccak(uncles.as_raw())); - uncles - }, - _ => RlpStream::new_list(0) - }; - let txs = match with { - EachBlockWith::Transaction | EachBlockWith::UncleAndTransaction | EachBlockWith::Transactions(_) => { - let num_transactions = match with { - EachBlockWith::Transactions(num) => num, - _ => 1, - }; - let mut txs = RlpStream::new_list(num_transactions); - let keypair = Random.generate().unwrap(); - let mut nonce = U256::zero(); - - for _ in 0..num_transactions { - // Update nonces value - let tx = Transaction { - action: Action::Create, - value: U256::from(100), - data: "3331600055".from_hex().unwrap(), - gas: U256::from(100_000), - gas_price: U256::from(200_000_000_000u64), - nonce: nonce - }; - let signed_tx = tx.sign(keypair.secret(), None); - txs.append(&signed_tx); - nonce += U256::one(); - } - - self.nonces.write().insert(keypair.address(), nonce); - txs.out() - }, - _ => ::rlp::EMPTY_LIST_RLP.to_vec() - }; - - let mut rlp = RlpStream::new_list(3); - rlp.append(&header); - rlp.append_raw(&txs, 1); - rlp.append_raw(uncles.as_raw(), 1); - let unverified = Unverified::from_rlp(rlp.out()).unwrap(); - self.import_block(unverified).unwrap(); - } - - /// Add a sequence of blocks to test client. - pub fn add_blocks(&self, count: usize, with: EachBlockWith) { - for _ in 0..count { - self.add_block(with, |header| header); - } - } - - /// Make a bad block by setting invalid parent hash. - pub fn corrupt_block_parent(&self, n: BlockNumber) { - let hash = self.block_hash(BlockId::Number(n)).unwrap(); - let mut header: Header = self.block_header(BlockId::Number(n)).unwrap().decode().expect("decoding failed"); - header.set_parent_hash(H256::from(42)); - let mut rlp = RlpStream::new_list(3); - rlp.append(&header); - rlp.append_raw(&::rlp::NULL_RLP, 1); - rlp.append_raw(&::rlp::NULL_RLP, 1); - self.blocks.write().insert(hash, rlp.out()); - } - - /// Get block hash with `delta` as offset from the most recent blocks. - pub fn block_hash_delta_minus(&mut self, delta: usize) -> H256 { - let blocks_read = self.numbers.read(); - let index = blocks_read.len() - delta; - blocks_read[&index].clone() - } - - fn block_hash(&self, id: BlockId) -> Option { - match id { - BlockId::Hash(hash) => Some(hash), - BlockId::Number(n) => self.numbers.read().get(&(n as usize)).cloned(), - BlockId::Earliest => self.numbers.read().get(&0).cloned(), - BlockId::Latest => self.numbers.read().get(&(self.numbers.read().len() - 1)).cloned() - } - } - - /// Inserts a transaction with given gas price to miners transactions queue. - pub fn insert_transaction_with_gas_price_to_queue(&self, gas_price: U256) -> H256 { - let keypair = Random.generate().unwrap(); - let tx = Transaction { - action: Action::Create, - value: U256::from(100), - data: "3331600055".from_hex().unwrap(), - gas: U256::from(100_000), - gas_price: gas_price, - nonce: U256::zero() - }; - let signed_tx = tx.sign(keypair.secret(), None); - self.set_balance(signed_tx.sender(), 10_000_000_000_000_000_000u64.into()); - let hash = signed_tx.hash(); - let res = self.miner.import_external_transactions(self, vec![signed_tx.into()]); - let res = res.into_iter().next().unwrap(); - assert!(res.is_ok()); - hash - } - - /// Inserts a transaction to miners transactions queue. - pub fn insert_transaction_to_queue(&self) -> H256 { - self.insert_transaction_with_gas_price_to_queue(U256::from(20_000_000_000u64)) - } - - /// Set reported history size. - pub fn set_history(&self, h: Option) { - *self.history.write() = h; - } - - /// Returns true if the client has been disabled. - pub fn is_disabled(&self) -> bool { - self.disabled.load(AtomicOrder::Relaxed) - } + /// Creates new test client. + pub fn new() -> Self { + Self::new_with_extra_data(Bytes::new()) + } + + /// Creates new test client with specified extra data for each block + pub fn new_with_extra_data(extra_data: Bytes) -> Self { + let spec = Spec::new_test(); + TestBlockChainClient::new_with_spec_and_extra(spec, extra_data) + } + + /// Create test client with custom spec. + pub fn new_with_spec(spec: Spec) -> Self { + TestBlockChainClient::new_with_spec_and_extra(spec, Bytes::new()) + } + + /// Create test client with custom spec and extra data. + pub fn new_with_spec_and_extra(spec: Spec, extra_data: Bytes) -> Self { + let genesis_block = spec.genesis_block(); + let genesis_hash = spec.genesis_header().hash(); + + let mut client = TestBlockChainClient { + blocks: RwLock::new(HashMap::new()), + numbers: RwLock::new(HashMap::new()), + genesis_hash: H256::new(), + extra_data: extra_data, + last_hash: RwLock::new(H256::new()), + difficulty: RwLock::new(spec.genesis_header().difficulty().clone()), + balances: RwLock::new(HashMap::new()), + nonces: RwLock::new(HashMap::new()), + storage: RwLock::new(HashMap::new()), + code: RwLock::new(HashMap::new()), + execution_result: RwLock::new(None), + receipts: RwLock::new(HashMap::new()), + logs: RwLock::new(Vec::new()), + queue_size: AtomicUsize::new(0), + miner: Arc::new(Miner::new_for_tests(&spec, None)), + spec: spec, + latest_block_timestamp: RwLock::new(10_000_000), + ancient_block: RwLock::new(None), + first_block: RwLock::new(None), + traces: RwLock::new(None), + history: RwLock::new(None), + disabled: AtomicBool::new(false), + error_on_logs: RwLock::new(None), + }; + + // insert genesis hash. + client.blocks.get_mut().insert(genesis_hash, genesis_block); + client.numbers.get_mut().insert(0, genesis_hash); + *client.last_hash.get_mut() = genesis_hash; + client.genesis_hash = genesis_hash; + client + } + + /// Set the transaction receipt result + pub fn set_transaction_receipt(&self, id: TransactionId, receipt: LocalizedReceipt) { + self.receipts.write().insert(id, receipt); + } + + /// Set the execution result. + pub fn set_execution_result(&self, result: Result) { + *self.execution_result.write() = Some(result); + } + + /// Set the balance of account `address` to `balance`. + pub fn set_balance(&self, address: Address, balance: U256) { + self.balances.write().insert(address, balance); + } + + /// Set nonce of account `address` to `nonce`. + pub fn set_nonce(&self, address: Address, nonce: U256) { + self.nonces.write().insert(address, nonce); + } + + /// Set `code` at `address`. + pub fn set_code(&self, address: Address, code: Bytes) { + self.code.write().insert(address, code); + } + + /// Set storage `position` to `value` for account `address`. + pub fn set_storage(&self, address: Address, position: H256, value: H256) { + self.storage.write().insert((address, position), value); + } + + /// Set block queue size for testing + pub fn set_queue_size(&self, size: usize) { + self.queue_size.store(size, AtomicOrder::Relaxed); + } + + /// Set timestamp assigned to latest sealed block + pub fn set_latest_block_timestamp(&self, ts: u64) { + *self.latest_block_timestamp.write() = ts; + } + + /// Set logs to return for each logs call. + pub fn set_logs(&self, logs: Vec) { + *self.logs.write() = logs; + } + + /// Set return errors on logs. + pub fn set_error_on_logs(&self, val: Option) { + *self.error_on_logs.write() = val; + } + + /// Add a block to test client. + pub fn add_block(&self, with: EachBlockWith, hook: F) + where + F: Fn(Header) -> Header, + { + let n = self.numbers.read().len(); + + let mut header = Header::new(); + header.set_difficulty(From::from(n)); + header.set_parent_hash(self.last_hash.read().clone()); + header.set_number(n as BlockNumber); + header.set_gas_limit(U256::from(1_000_000)); + header.set_extra_data(self.extra_data.clone()); + + header = hook(header); + + let uncles = match with { + EachBlockWith::Uncle | EachBlockWith::UncleAndTransaction => { + let mut uncles = RlpStream::new_list(1); + let mut uncle_header = Header::new(); + uncle_header.set_difficulty(From::from(n)); + uncle_header.set_parent_hash(self.last_hash.read().clone()); + uncle_header.set_number(n as BlockNumber); + uncles.append(&uncle_header); + header.set_uncles_hash(keccak(uncles.as_raw())); + uncles + } + _ => RlpStream::new_list(0), + }; + let txs = match with { + EachBlockWith::Transaction + | EachBlockWith::UncleAndTransaction + | EachBlockWith::Transactions(_) => { + let num_transactions = match with { + EachBlockWith::Transactions(num) => num, + _ => 1, + }; + let mut txs = RlpStream::new_list(num_transactions); + let keypair = Random.generate().unwrap(); + let mut nonce = U256::zero(); + + for _ in 0..num_transactions { + // Update nonces value + let tx = Transaction { + action: Action::Create, + value: U256::from(100), + data: "3331600055".from_hex().unwrap(), + gas: U256::from(100_000), + gas_price: U256::from(200_000_000_000u64), + nonce: nonce, + }; + let signed_tx = tx.sign(keypair.secret(), None); + txs.append(&signed_tx); + nonce += U256::one(); + } + + self.nonces.write().insert(keypair.address(), nonce); + txs.out() + } + _ => ::rlp::EMPTY_LIST_RLP.to_vec(), + }; + + let mut rlp = RlpStream::new_list(3); + rlp.append(&header); + rlp.append_raw(&txs, 1); + rlp.append_raw(uncles.as_raw(), 1); + let unverified = Unverified::from_rlp(rlp.out()).unwrap(); + self.import_block(unverified).unwrap(); + } + + /// Add a sequence of blocks to test client. + pub fn add_blocks(&self, count: usize, with: EachBlockWith) { + for _ in 0..count { + self.add_block(with, |header| header); + } + } + + /// Make a bad block by setting invalid parent hash. + pub fn corrupt_block_parent(&self, n: BlockNumber) { + let hash = self.block_hash(BlockId::Number(n)).unwrap(); + let mut header: Header = self + .block_header(BlockId::Number(n)) + .unwrap() + .decode() + .expect("decoding failed"); + header.set_parent_hash(H256::from(42)); + let mut rlp = RlpStream::new_list(3); + rlp.append(&header); + rlp.append_raw(&::rlp::NULL_RLP, 1); + rlp.append_raw(&::rlp::NULL_RLP, 1); + self.blocks.write().insert(hash, rlp.out()); + } + + /// Get block hash with `delta` as offset from the most recent blocks. + pub fn block_hash_delta_minus(&mut self, delta: usize) -> H256 { + let blocks_read = self.numbers.read(); + let index = blocks_read.len() - delta; + blocks_read[&index].clone() + } + + fn block_hash(&self, id: BlockId) -> Option { + match id { + BlockId::Hash(hash) => Some(hash), + BlockId::Number(n) => self.numbers.read().get(&(n as usize)).cloned(), + BlockId::Earliest => self.numbers.read().get(&0).cloned(), + BlockId::Latest => self + .numbers + .read() + .get(&(self.numbers.read().len() - 1)) + .cloned(), + } + } + + /// Inserts a transaction with given gas price to miners transactions queue. + pub fn insert_transaction_with_gas_price_to_queue(&self, gas_price: U256) -> H256 { + let keypair = Random.generate().unwrap(); + let tx = Transaction { + action: Action::Create, + value: U256::from(100), + data: "3331600055".from_hex().unwrap(), + gas: U256::from(100_000), + gas_price: gas_price, + nonce: U256::zero(), + }; + let signed_tx = tx.sign(keypair.secret(), None); + self.set_balance(signed_tx.sender(), 10_000_000_000_000_000_000u64.into()); + let hash = signed_tx.hash(); + let res = self + .miner + .import_external_transactions(self, vec![signed_tx.into()]); + let res = res.into_iter().next().unwrap(); + assert!(res.is_ok()); + hash + } + + /// Inserts a transaction to miners transactions queue. + pub fn insert_transaction_to_queue(&self) -> H256 { + self.insert_transaction_with_gas_price_to_queue(U256::from(20_000_000_000u64)) + } + + /// Set reported history size. + pub fn set_history(&self, h: Option) { + *self.history.write() = h; + } + + /// Returns true if the client has been disabled. + pub fn is_disabled(&self) -> bool { + self.disabled.load(AtomicOrder::Relaxed) + } } +/// Get temporary db state1 pub fn get_temp_state_db() -> StateDB { - let db = kvdb_memorydb::create(NUM_COLUMNS.unwrap_or(0)); - let journal_db = journaldb::new(Arc::new(db), journaldb::Algorithm::EarlyMerge, COL_STATE); - StateDB::new(journal_db, 1024 * 1024) + let db = kvdb_memorydb::create(NUM_COLUMNS.unwrap_or(0)); + let journal_db = journaldb::new(Arc::new(db), journaldb::Algorithm::EarlyMerge, COL_STATE); + StateDB::new(journal_db, 1024 * 1024) } impl ReopenBlock for TestBlockChainClient { - fn reopen_block(&self, block: ClosedBlock) -> OpenBlock { - block.reopen(&*self.spec.engine) - } + fn reopen_block(&self, block: ClosedBlock) -> OpenBlock { + block.reopen(&*self.spec.engine) + } } impl PrepareOpenBlock for TestBlockChainClient { - fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> Result { - let engine = &*self.spec.engine; - let genesis_header = self.spec.genesis_header(); - let db = self.spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - - let last_hashes = vec![genesis_header.hash()]; - let mut open_block = OpenBlock::new( - engine, - Default::default(), - false, - db, - &genesis_header, - Arc::new(last_hashes), - author, - gas_range_target, - extra_data, - false, - None, - )?; - // TODO [todr] Override timestamp for predictability - open_block.set_timestamp(*self.latest_block_timestamp.read()); - Ok(open_block) - } + fn prepare_open_block( + &self, + author: Address, + gas_range_target: (U256, U256), + extra_data: Bytes, + ) -> Result { + let engine = &*self.spec.engine; + let genesis_header = self.spec.genesis_header(); + let db = self + .spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + + let last_hashes = vec![genesis_header.hash()]; + let mut open_block = OpenBlock::new( + engine, + Default::default(), + false, + db, + &genesis_header, + Arc::new(last_hashes), + author, + gas_range_target, + extra_data, + false, + None, + )?; + // TODO [todr] Override timestamp for predictability + open_block.set_timestamp(*self.latest_block_timestamp.read()); + Ok(open_block) + } } impl ScheduleInfo for TestBlockChainClient { - fn latest_schedule(&self) -> Schedule { - Schedule::new_post_eip150(24576, true, true, true) - } + fn latest_schedule(&self) -> Schedule { + Schedule::new_post_eip150(24576, true, true, true) + } } impl ImportSealedBlock for TestBlockChainClient { - fn import_sealed_block(&self, _block: SealedBlock) -> EthcoreResult { - Ok(H256::default()) - } + fn import_sealed_block(&self, _block: SealedBlock) -> EthcoreResult { + Ok(H256::default()) + } } impl BlockProducer for TestBlockChainClient {} impl BroadcastProposalBlock for TestBlockChainClient { - fn broadcast_proposal_block(&self, _block: SealedBlock) {} + fn broadcast_proposal_block(&self, _block: SealedBlock) {} } impl SealedBlockImporter for TestBlockChainClient {} @@ -448,510 +475,648 @@ impl ::miner::TransactionVerifierClient for TestBlockChainClient {} impl ::miner::BlockChainClient for TestBlockChainClient {} impl Nonce for TestBlockChainClient { - fn nonce(&self, address: &Address, id: BlockId) -> Option { - match id { - BlockId::Latest => Some(self.nonces.read().get(address).cloned().unwrap_or(self.spec.params().account_start_nonce)), - _ => None, - } - } - - fn latest_nonce(&self, address: &Address) -> U256 { - self.nonce(address, BlockId::Latest).unwrap() - } + fn nonce(&self, address: &Address, id: BlockId) -> Option { + match id { + BlockId::Latest => Some( + self.nonces + .read() + .get(address) + .cloned() + .unwrap_or(self.spec.params().account_start_nonce), + ), + _ => None, + } + } + + fn latest_nonce(&self, address: &Address) -> U256 { + self.nonce(address, BlockId::Latest).unwrap() + } } impl Balance for TestBlockChainClient { - fn balance(&self, address: &Address, state: StateOrBlock) -> Option { - match state { - StateOrBlock::Block(BlockId::Latest) | StateOrBlock::State(_) => Some(self.balances.read().get(address).cloned().unwrap_or_else(U256::zero)), - _ => None, - } - } - - fn latest_balance(&self, address: &Address) -> U256 { - self.balance(address, BlockId::Latest.into()).unwrap() - } + fn balance(&self, address: &Address, state: StateOrBlock) -> Option { + match state { + StateOrBlock::Block(BlockId::Latest) | StateOrBlock::State(_) => Some( + self.balances + .read() + .get(address) + .cloned() + .unwrap_or_else(U256::zero), + ), + _ => None, + } + } + + fn latest_balance(&self, address: &Address) -> U256 { + self.balance(address, BlockId::Latest.into()).unwrap() + } } impl AccountData for TestBlockChainClient {} impl ChainInfo for TestBlockChainClient { - fn chain_info(&self) -> BlockChainInfo { - let number = self.blocks.read().len() as BlockNumber - 1; - BlockChainInfo { - total_difficulty: *self.difficulty.read(), - pending_total_difficulty: *self.difficulty.read(), - genesis_hash: self.genesis_hash.clone(), - best_block_hash: self.last_hash.read().clone(), - best_block_number: number, - best_block_timestamp: number, - first_block_hash: self.first_block.read().as_ref().map(|x| x.0), - first_block_number: self.first_block.read().as_ref().map(|x| x.1), - ancient_block_hash: self.ancient_block.read().as_ref().map(|x| x.0), - ancient_block_number: self.ancient_block.read().as_ref().map(|x| x.1) - } - } + fn chain_info(&self) -> BlockChainInfo { + let number = self.blocks.read().len() as BlockNumber - 1; + BlockChainInfo { + total_difficulty: *self.difficulty.read(), + pending_total_difficulty: *self.difficulty.read(), + genesis_hash: self.genesis_hash.clone(), + best_block_hash: self.last_hash.read().clone(), + best_block_number: number, + best_block_timestamp: number, + first_block_hash: self.first_block.read().as_ref().map(|x| x.0), + first_block_number: self.first_block.read().as_ref().map(|x| x.1), + ancient_block_hash: self.ancient_block.read().as_ref().map(|x| x.0), + ancient_block_number: self.ancient_block.read().as_ref().map(|x| x.1), + } + } } impl BlockInfo for TestBlockChainClient { - fn block_header(&self, id: BlockId) -> Option { - self.block_hash(id) - .and_then(|hash| self.blocks.read().get(&hash).map(|r| view!(BlockView, r).header_rlp().as_raw().to_vec())) - .map(encoded::Header::new) - } - - fn best_block_header(&self) -> Header { - self.block_header(BlockId::Hash(self.chain_info().best_block_hash)) - .expect("Best block always has header.") - .decode() - .expect("decoding failed") - } - - fn block(&self, id: BlockId) -> Option { - self.block_hash(id) - .and_then(|hash| self.blocks.read().get(&hash).cloned()) - .map(encoded::Block::new) - } - - fn code_hash(&self, address: &Address, id: BlockId) -> Option { - match id { - BlockId::Latest => self.code.read().get(address).map(|c| keccak(&c)), - _ => None, - } - } + fn block_header(&self, id: BlockId) -> Option { + self.block_hash(id) + .and_then(|hash| { + self.blocks + .read() + .get(&hash) + .map(|r| view!(BlockView, r).header_rlp().as_raw().to_vec()) + }) + .map(encoded::Header::new) + } + + fn best_block_header(&self) -> Header { + self.block_header(BlockId::Hash(self.chain_info().best_block_hash)) + .expect("Best block always has header.") + .decode() + .expect("decoding failed") + } + + fn block(&self, id: BlockId) -> Option { + self.block_hash(id) + .and_then(|hash| self.blocks.read().get(&hash).cloned()) + .map(encoded::Block::new) + } + + fn code_hash(&self, address: &Address, id: BlockId) -> Option { + match id { + BlockId::Latest => self.code.read().get(address).map(|c| keccak(&c)), + _ => None, + } + } } impl CallContract for TestBlockChainClient { - fn call_contract(&self, _id: BlockId, _address: Address, _data: Bytes) -> Result { Ok(vec![]) } + fn call_contract( + &self, + _id: BlockId, + _address: Address, + _data: Bytes, + ) -> Result { + Ok(vec![]) + } } impl TransactionInfo for TestBlockChainClient { - fn transaction_block(&self, _id: TransactionId) -> Option { - None // Simple default. - } + fn transaction_block(&self, _id: TransactionId) -> Option { + None // Simple default. + } } impl BlockChain for TestBlockChainClient {} impl RegistryInfo for TestBlockChainClient { - fn registry_address(&self, _name: String, _block: BlockId) -> Option
{ None } + fn registry_address(&self, _name: String, _block: BlockId) -> Option
{ + None + } } impl ImportBlock for TestBlockChainClient { - fn import_block(&self, unverified: Unverified) -> EthcoreResult { - let header = unverified.header; - let h = header.hash(); - let number: usize = header.number() as usize; - if number > self.blocks.read().len() { - panic!("Unexpected block number. Expected {}, got {}", self.blocks.read().len(), number); - } - if number > 0 { - match self.blocks.read().get(header.parent_hash()) { - Some(parent) => { - let parent = view!(BlockView, parent).header(); - if parent.number() != (header.number() - 1) { - panic!("Unexpected block parent"); - } - }, - None => { - panic!("Unknown block parent {:?} for block {}", header.parent_hash(), number); - } - } - } - let len = self.numbers.read().len(); - if number == len { - { - let mut difficulty = self.difficulty.write(); - *difficulty = *difficulty + header.difficulty().clone(); - } - mem::replace(&mut *self.last_hash.write(), h.clone()); - self.blocks.write().insert(h.clone(), unverified.bytes); - self.numbers.write().insert(number, h.clone()); - let mut parent_hash = header.parent_hash().clone(); - if number > 0 { - let mut n = number - 1; - while n > 0 && self.numbers.read()[&n] != parent_hash { - *self.numbers.write().get_mut(&n).unwrap() = parent_hash.clone(); - n -= 1; - parent_hash = view!(BlockView, &self.blocks.read()[&parent_hash]).header().parent_hash().clone(); - } - } - } - else { - self.blocks.write().insert(h.clone(), unverified.bytes); - } - Ok(h) - } + fn import_block(&self, unverified: Unverified) -> EthcoreResult { + let header = unverified.header; + let h = header.hash(); + let number: usize = header.number() as usize; + if number > self.blocks.read().len() { + panic!( + "Unexpected block number. Expected {}, got {}", + self.blocks.read().len(), + number + ); + } + if number > 0 { + match self.blocks.read().get(header.parent_hash()) { + Some(parent) => { + let parent = view!(BlockView, parent).header(); + if parent.number() != (header.number() - 1) { + panic!("Unexpected block parent"); + } + } + None => { + panic!( + "Unknown block parent {:?} for block {}", + header.parent_hash(), + number + ); + } + } + } + let len = self.numbers.read().len(); + if number == len { + { + let mut difficulty = self.difficulty.write(); + *difficulty = *difficulty + header.difficulty().clone(); + } + *self.last_hash.write() = h.clone(); + self.blocks.write().insert(h.clone(), unverified.bytes); + self.numbers.write().insert(number, h.clone()); + let mut parent_hash = header.parent_hash().clone(); + if number > 0 { + let mut n = number - 1; + while n > 0 && self.numbers.read()[&n] != parent_hash { + *self.numbers.write().get_mut(&n).unwrap() = parent_hash.clone(); + n -= 1; + parent_hash = view!(BlockView, &self.blocks.read()[&parent_hash]) + .header() + .parent_hash() + .clone(); + } + } + } else { + self.blocks.write().insert(h.clone(), unverified.bytes); + } + Ok(h) + } } impl Call for TestBlockChainClient { - // State will not be used by test client anyway, since all methods that accept state are mocked - type State = (); - - fn call(&self, _t: &SignedTransaction, _analytics: CallAnalytics, _state: &mut Self::State, _header: &Header) -> Result { - self.execution_result.read().clone().unwrap() - } - - fn call_many(&self, txs: &[(SignedTransaction, CallAnalytics)], state: &mut Self::State, header: &Header) -> Result, CallError> { - let mut res = Vec::with_capacity(txs.len()); - for &(ref tx, analytics) in txs { - res.push(self.call(tx, analytics, state, header)?); - } - Ok(res) - } - - fn estimate_gas(&self, _t: &SignedTransaction, _state: &Self::State, _header: &Header) -> Result { - Ok(21000.into()) - } + // State will not be used by test client anyway, since all methods that accept state are mocked + type State = TestState; + + fn call( + &self, + _t: &SignedTransaction, + _analytics: CallAnalytics, + _state: &mut Self::State, + _header: &Header, + ) -> Result { + self.execution_result.read().clone().unwrap() + } + + fn call_many( + &self, + txs: &[(SignedTransaction, CallAnalytics)], + state: &mut Self::State, + header: &Header, + ) -> Result, CallError> { + let mut res = Vec::with_capacity(txs.len()); + for &(ref tx, analytics) in txs { + res.push(self.call(tx, analytics, state, header)?); + } + Ok(res) + } + + fn estimate_gas( + &self, + _t: &SignedTransaction, + _state: &Self::State, + _header: &Header, + ) -> Result { + Ok(21000.into()) + } } -impl StateInfo for () { - fn nonce(&self, _address: &Address) -> ethtrie::Result { unimplemented!() } - fn balance(&self, _address: &Address) -> ethtrie::Result { unimplemented!() } - fn storage_at(&self, _address: &Address, _key: &H256) -> ethtrie::Result { unimplemented!() } - fn code(&self, _address: &Address) -> ethtrie::Result>> { unimplemented!() } +/// NewType wrapper around `()` to impersonate `State` in trait impls. State will not be used by +/// test client, since all methods that accept state are mocked. +pub struct TestState; +impl StateInfo for TestState { + fn nonce(&self, _address: &Address) -> ethtrie::Result { + unimplemented!() + } + fn balance(&self, _address: &Address) -> ethtrie::Result { + unimplemented!() + } + fn storage_at(&self, _address: &Address, _key: &H256) -> ethtrie::Result { + unimplemented!() + } + fn code(&self, _address: &Address) -> ethtrie::Result>> { + unimplemented!() + } } impl StateClient for TestBlockChainClient { - // State will not be used by test client anyway, since all methods that accept state are mocked - type State = (); + // State will not be used by test client anyway, since all methods that accept state are mocked + type State = TestState; - fn latest_state(&self) -> Self::State { - () - } + fn latest_state_and_header(&self) -> (Self::State, Header) { + (TestState, self.best_block_header()) + } - fn state_at(&self, _id: BlockId) -> Option { - Some(()) - } + fn state_at(&self, _id: BlockId) -> Option { + Some(TestState) + } } impl EngineInfo for TestBlockChainClient { - fn engine(&self) -> &EthEngine { - unimplemented!() - } + fn engine(&self) -> &dyn EthEngine { + unimplemented!() + } } impl BadBlocks for TestBlockChainClient { - fn bad_blocks(&self) -> Vec<(Unverified, String)> { - vec![ - (Unverified { - header: Default::default(), - transactions: vec![], - uncles: vec![], - bytes: vec![1, 2, 3], - }, "Invalid block".into()) - ] - } + fn bad_blocks(&self) -> Vec<(Unverified, String)> { + vec![( + Unverified { + header: Default::default(), + transactions: vec![], + uncles: vec![], + bytes: vec![1, 2, 3], + }, + "Invalid block".into(), + )] + } } impl BlockChainClient for TestBlockChainClient { - fn replay(&self, _id: TransactionId, _analytics: CallAnalytics) -> Result { - self.execution_result.read().clone().unwrap() - } - - fn replay_block_transactions(&self, _block: BlockId, _analytics: CallAnalytics) -> Result>, CallError> { - Ok(Box::new(self.traces.read().clone().unwrap().into_iter().map(|t| t.transaction_hash.unwrap_or(H256::new())).zip(self.execution_result.read().clone().unwrap().into_iter()))) - } - - fn block_total_difficulty(&self, _id: BlockId) -> Option { - Some(U256::zero()) - } - - fn block_hash(&self, id: BlockId) -> Option { - Self::block_hash(self, id) - } - - fn storage_root(&self, _address: &Address, _id: BlockId) -> Option { - None - } - - fn code(&self, address: &Address, state: StateOrBlock) -> Option> { - match state { - StateOrBlock::Block(BlockId::Latest) => Some(self.code.read().get(address).cloned()), - _ => None, - } - } - - fn storage_at(&self, address: &Address, position: &H256, state: StateOrBlock) -> Option { - match state { - StateOrBlock::Block(BlockId::Latest) => Some(self.storage.read().get(&(address.clone(), position.clone())).cloned().unwrap_or_else(H256::new)), - _ => None, - } - } - - fn list_accounts(&self, _id: BlockId, _after: Option<&Address>, _count: u64) -> Option> { - None - } - - fn list_storage(&self, _id: BlockId, _account: &Address, _after: Option<&H256>, _count: u64) -> Option> { - None - } - fn transaction(&self, _id: TransactionId) -> Option { - None // Simple default. - } - - fn uncle(&self, _id: UncleId) -> Option { - None // Simple default. - } - - fn uncle_extra_info(&self, _id: UncleId) -> Option> { - None - } - - fn transaction_receipt(&self, id: TransactionId) -> Option { - self.receipts.read().get(&id).cloned() - } - - fn localized_block_receipts(&self, _id: BlockId) -> Option> { - Some(self.receipts.read().values().cloned().collect()) - } - - fn logs(&self, filter: Filter) -> Result, BlockId> { - match self.error_on_logs.read().as_ref() { - Some(id) => return Err(id.clone()), - None => (), - } - - let mut logs = self.logs.read().clone(); - let len = logs.len(); - Ok(match filter.limit { - Some(limit) if limit <= len => logs.split_off(len - limit), - _ => logs, - }) - } - - fn last_hashes(&self) -> LastHashes { - unimplemented!(); - } - - fn block_number(&self, id: BlockId) -> Option { - match id { - BlockId::Number(number) => Some(number), - BlockId::Earliest => Some(0), - BlockId::Latest => Some(self.chain_info().best_block_number), - BlockId::Hash(ref h) => - self.numbers.read().iter().find(|&(_, hash)| hash == h).map(|e| *e.0 as u64) - } - } - - fn block_body(&self, id: BlockId) -> Option { - self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).map(|r| { - let block = view!(BlockView, r); - let mut stream = RlpStream::new_list(2); - stream.append_raw(block.transactions_rlp().as_raw(), 1); - stream.append_raw(block.uncles_rlp().as_raw(), 1); - encoded::Body::new(stream.out()) - })) - } - - fn block_extra_info(&self, id: BlockId) -> Option> { - self.block(id) - .map(|block| block.view().header()) - .map(|header| self.spec.engine.extra_info(&header)) - } - - fn block_status(&self, id: BlockId) -> BlockStatus { - match id { - BlockId::Number(number) if (number as usize) < self.blocks.read().len() => BlockStatus::InChain, - BlockId::Hash(ref hash) if self.blocks.read().get(hash).is_some() => BlockStatus::InChain, - BlockId::Latest | BlockId::Earliest => BlockStatus::InChain, - _ => BlockStatus::Unknown, - } - } - - // works only if blocks are one after another 1 -> 2 -> 3 - fn tree_route(&self, from: &H256, to: &H256) -> Option { - Some(TreeRoute { - ancestor: H256::new(), - index: 0, - blocks: { - let numbers_read = self.numbers.read(); - let mut adding = false; - - let mut blocks = Vec::new(); - for (_, hash) in numbers_read.iter().sorted_by(|tuple1, tuple2| tuple1.0.cmp(tuple2.0)) { - if hash == to { - if adding { - blocks.push(hash.clone()); - } - adding = false; - break; - } - if hash == from { - adding = true; - } - if adding { - blocks.push(hash.clone()); - } - } - if adding { Vec::new() } else { blocks } - }, - is_from_route_finalized: false, - }) - } - - fn find_uncles(&self, _hash: &H256) -> Option> { - None - } - - // TODO: returns just hashes instead of node state rlp(?) - fn state_data(&self, hash: &H256) -> Option { - // starts with 'f' ? - if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") { - let mut rlp = RlpStream::new(); - rlp.append(&hash.clone()); - return Some(rlp.out()); - } - None - } - - fn block_receipts(&self, hash: &H256) -> Option { - // starts with 'f' ? - if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") { - let receipt = BlockReceipts::new(vec![Receipt::new( - TransactionOutcome::StateRoot(H256::zero()), - U256::zero(), - vec![])]); - return Some(receipt); - } - None - } - - fn queue_info(&self) -> QueueInfo { - QueueInfo { - verified_queue_size: self.queue_size.load(AtomicOrder::Relaxed), - unverified_queue_size: 0, - verifying_queue_size: 0, - max_queue_size: 0, - max_mem_use: 0, - mem_used: 0, - } - } - - fn clear_queue(&self) { - } - - fn additional_params(&self) -> BTreeMap { - Default::default() - } - - fn filter_traces(&self, _filter: TraceFilter) -> Option> { - self.traces.read().clone() - } - - fn trace(&self, _trace: TraceId) -> Option { - self.traces.read().clone().and_then(|vec| vec.into_iter().next()) - } - - fn transaction_traces(&self, _trace: TransactionId) -> Option> { - self.traces.read().clone() - } - - fn block_traces(&self, _trace: BlockId) -> Option> { - self.traces.read().clone() - } - - fn transactions_to_propagate(&self) -> Vec> { - self.miner.ready_transactions(self, 4096, miner::PendingOrdering::Priority) - } - - fn signing_chain_id(&self) -> Option { None } - - fn mode(&self) -> Mode { Mode::Active } - - fn set_mode(&self, _: Mode) { unimplemented!(); } - - fn spec_name(&self) -> String { "foundation".into() } - - fn set_spec_name(&self, _: String) -> Result<(), ()> { unimplemented!(); } - - fn disable(&self) { self.disabled.store(true, AtomicOrder::Relaxed); } - - fn pruning_info(&self) -> PruningInfo { - let best_num = self.chain_info().best_block_number; - PruningInfo { - earliest_chain: 1, - earliest_state: self.history.read().as_ref().map(|x| best_num - x).unwrap_or(0), - } - } - - fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error> { - let transaction = Transaction { - nonce: self.latest_nonce(&self.miner.authoring_params().author), - action: Action::Call(address), - gas: self.spec.gas_limit, - gas_price: U256::zero(), - value: U256::default(), - data: data, - }; - let chain_id = Some(self.spec.chain_id()); - let sig = self.spec.engine.sign(transaction.hash(chain_id)).unwrap(); - let signed = SignedTransaction::new(transaction.with_signature(sig, chain_id)).unwrap(); - self.miner.import_own_transaction(self, signed.into()) - } - - fn registrar_address(&self) -> Option
{ None } + fn replay(&self, _id: TransactionId, _analytics: CallAnalytics) -> Result { + self.execution_result.read().clone().unwrap() + } + + fn replay_block_transactions( + &self, + _block: BlockId, + _analytics: CallAnalytics, + ) -> Result>, CallError> { + Ok(Box::new( + self.traces + .read() + .clone() + .unwrap() + .into_iter() + .map(|t| t.transaction_hash.unwrap_or(H256::new())) + .zip(self.execution_result.read().clone().unwrap().into_iter()), + )) + } + + fn block_total_difficulty(&self, _id: BlockId) -> Option { + Some(U256::zero()) + } + + fn block_hash(&self, id: BlockId) -> Option { + Self::block_hash(self, id) + } + + fn storage_root(&self, _address: &Address, _id: BlockId) -> Option { + None + } + + fn code(&self, address: &Address, state: StateOrBlock) -> Option> { + match state { + StateOrBlock::Block(BlockId::Latest) => Some(self.code.read().get(address).cloned()), + _ => None, + } + } + + fn storage_at(&self, address: &Address, position: &H256, state: StateOrBlock) -> Option { + match state { + StateOrBlock::Block(BlockId::Latest) => Some( + self.storage + .read() + .get(&(address.clone(), position.clone())) + .cloned() + .unwrap_or_else(H256::new), + ), + _ => None, + } + } + + fn list_accounts( + &self, + _id: BlockId, + _after: Option<&Address>, + _count: u64, + ) -> Option> { + None + } + + fn list_storage( + &self, + _id: BlockId, + _account: &Address, + _after: Option<&H256>, + _count: u64, + ) -> Option> { + None + } + fn transaction(&self, _id: TransactionId) -> Option { + None // Simple default. + } + + fn uncle(&self, _id: UncleId) -> Option { + None // Simple default. + } + + fn uncle_extra_info(&self, _id: UncleId) -> Option> { + None + } + + fn transaction_receipt(&self, id: TransactionId) -> Option { + self.receipts.read().get(&id).cloned() + } + + fn localized_block_receipts(&self, _id: BlockId) -> Option> { + Some(self.receipts.read().values().cloned().collect()) + } + + fn logs(&self, filter: Filter) -> Result, BlockId> { + match self.error_on_logs.read().as_ref() { + Some(id) => return Err(id.clone()), + None => (), + } + + let mut logs = self.logs.read().clone(); + let len = logs.len(); + Ok(match filter.limit { + Some(limit) if limit <= len => logs.split_off(len - limit), + _ => logs, + }) + } + + fn last_hashes(&self) -> LastHashes { + unimplemented!(); + } + + fn block_number(&self, id: BlockId) -> Option { + match id { + BlockId::Number(number) => Some(number), + BlockId::Earliest => Some(0), + BlockId::Latest => Some(self.chain_info().best_block_number), + BlockId::Hash(ref h) => self + .numbers + .read() + .iter() + .find(|&(_, hash)| hash == h) + .map(|e| *e.0 as u64), + } + } + + fn block_body(&self, id: BlockId) -> Option { + self.block_hash(id).and_then(|hash| { + self.blocks.read().get(&hash).map(|r| { + let block = view!(BlockView, r); + let mut stream = RlpStream::new_list(2); + stream.append_raw(block.transactions_rlp().as_raw(), 1); + stream.append_raw(block.uncles_rlp().as_raw(), 1); + encoded::Body::new(stream.out()) + }) + }) + } + + fn block_extra_info(&self, id: BlockId) -> Option> { + self.block(id) + .map(|block| block.view().header()) + .map(|header| self.spec.engine.extra_info(&header)) + } + + fn block_status(&self, id: BlockId) -> BlockStatus { + match id { + BlockId::Number(number) if (number as usize) < self.blocks.read().len() => { + BlockStatus::InChain + } + BlockId::Hash(ref hash) if self.blocks.read().get(hash).is_some() => { + BlockStatus::InChain + } + BlockId::Latest | BlockId::Earliest => BlockStatus::InChain, + _ => BlockStatus::Unknown, + } + } + + fn is_processing_fork(&self) -> bool { + false + } + + // works only if blocks are one after another 1 -> 2 -> 3 + fn tree_route(&self, from: &H256, to: &H256) -> Option { + Some(TreeRoute { + ancestor: H256::new(), + index: 0, + blocks: { + let numbers_read = self.numbers.read(); + let mut adding = false; + + let mut blocks = Vec::new(); + for (_, hash) in numbers_read + .iter() + .sorted_by(|tuple1, tuple2| tuple1.0.cmp(tuple2.0)) + { + if hash == to { + if adding { + blocks.push(hash.clone()); + } + adding = false; + break; + } + if hash == from { + adding = true; + } + if adding { + blocks.push(hash.clone()); + } + } + if adding { + Vec::new() + } else { + blocks + } + }, + is_from_route_finalized: false, + }) + } + + fn find_uncles(&self, _hash: &H256) -> Option> { + None + } + + fn block_receipts(&self, hash: &H256) -> Option { + // starts with 'f' ? + if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") { + let receipt = BlockReceipts::new(vec![Receipt::new( + TransactionOutcome::StateRoot(H256::zero()), + U256::zero(), + vec![], + )]); + return Some(receipt); + } + None + } + + fn queue_info(&self) -> QueueInfo { + QueueInfo { + verified_queue_size: self.queue_size.load(AtomicOrder::Relaxed), + unverified_queue_size: 0, + verifying_queue_size: 0, + max_queue_size: 0, + max_mem_use: 0, + mem_used: 0, + } + } + + fn clear_queue(&self) {} + + fn additional_params(&self) -> BTreeMap { + Default::default() + } + + fn filter_traces(&self, _filter: TraceFilter) -> Option> { + self.traces.read().clone() + } + + fn trace(&self, _trace: TraceId) -> Option { + self.traces + .read() + .clone() + .and_then(|vec| vec.into_iter().next()) + } + + fn transaction_traces(&self, _trace: TransactionId) -> Option> { + self.traces.read().clone() + } + + fn block_traces(&self, _trace: BlockId) -> Option> { + self.traces.read().clone() + } + + fn transactions_to_propagate(&self) -> Vec> { + self.miner + .ready_transactions(self, 4096, miner::PendingOrdering::Priority) + } + + fn signing_chain_id(&self) -> Option { + None + } + + fn mode(&self) -> Mode { + Mode::Active + } + + fn set_mode(&self, _: Mode) { + unimplemented!(); + } + + fn spec_name(&self) -> String { + "foundation".into() + } + + fn set_spec_name(&self, _: String) -> Result<(), ()> { + unimplemented!(); + } + + fn disable(&self) { + self.disabled.store(true, AtomicOrder::Relaxed); + } + + fn pruning_info(&self) -> PruningInfo { + let best_num = self.chain_info().best_block_number; + PruningInfo { + earliest_chain: 1, + earliest_state: self + .history + .read() + .as_ref() + .map(|x| best_num - x) + .unwrap_or(0), + } + } + + fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error> { + let transaction = Transaction { + nonce: self.latest_nonce(&self.miner.authoring_params().author), + action: Action::Call(address), + gas: self.spec.gas_limit, + gas_price: U256::zero(), + value: U256::default(), + data: data, + }; + let chain_id = Some(self.spec.chain_id()); + let sig = self.spec.engine.sign(transaction.hash(chain_id)).unwrap(); + let signed = SignedTransaction::new(transaction.with_signature(sig, chain_id)).unwrap(); + self.miner.import_own_transaction(self, signed.into()) + } + + fn registrar_address(&self) -> Option
{ + None + } } impl IoClient for TestBlockChainClient { - fn queue_transactions(&self, transactions: Vec, _peer_id: usize) { - // import right here - let txs = transactions.into_iter().filter_map(|bytes| Rlp::new(&bytes).as_val().ok()).collect(); - self.miner.import_external_transactions(self, txs); - } - - fn queue_ancient_block(&self, unverified: Unverified, _r: Bytes) -> EthcoreResult { - self.import_block(unverified) - } - - fn queue_consensus_message(&self, message: Bytes) { - self.spec.engine.handle_message(&message).unwrap(); - } + fn queue_transactions(&self, transactions: Vec, _peer_id: usize) { + // import right here + let txs = transactions + .into_iter() + .filter_map(|bytes| Rlp::new(&bytes).as_val().ok()) + .collect(); + self.miner.import_external_transactions(self, txs); + } + + fn queue_ancient_block(&self, unverified: Unverified, _r: Bytes) -> EthcoreResult { + self.import_block(unverified) + } + + fn queue_consensus_message(&self, message: Bytes) { + self.spec.engine.handle_message(&message).unwrap(); + } } impl ProvingBlockChainClient for TestBlockChainClient { - fn prove_storage(&self, _: H256, _: H256, _: BlockId) -> Option<(Vec, H256)> { - None - } + fn prove_storage(&self, _: H256, _: H256, _: BlockId) -> Option<(Vec, H256)> { + None + } - fn prove_account(&self, _: H256, _: BlockId) -> Option<(Vec, BasicAccount)> { - None - } + fn prove_account(&self, _: H256, _: BlockId) -> Option<(Vec, BasicAccount)> { + None + } - fn prove_transaction(&self, _: SignedTransaction, _: BlockId) -> Option<(Bytes, Vec)> { - None - } + fn prove_transaction(&self, _: SignedTransaction, _: BlockId) -> Option<(Bytes, Vec)> { + None + } - fn epoch_signal(&self, _: H256) -> Option> { - None - } + fn epoch_signal(&self, _: H256) -> Option> { + None + } } impl super::traits::EngineClient for TestBlockChainClient { - fn update_sealing(&self) { - self.miner.update_sealing(self) - } - - fn submit_seal(&self, block_hash: H256, seal: Vec) { - let import = self.miner.submit_seal(block_hash, seal).and_then(|block| self.import_sealed_block(block)); - if let Err(err) = import { - warn!(target: "poa", "Wrong internal seal submission! {:?}", err); - } - } - - fn broadcast_consensus_message(&self, _message: Bytes) {} - - fn epoch_transition_for(&self, _block_hash: H256) -> Option<::engines::EpochTransition> { - None - } - - fn as_full_client(&self) -> Option<&BlockChainClient> { Some(self) } - - fn block_number(&self, id: BlockId) -> Option { - BlockChainClient::block_number(self, id) - } + fn update_sealing(&self, force: ForceUpdateSealing) { + self.miner.update_sealing(self, force) + } + + fn submit_seal(&self, block_hash: H256, seal: Vec) { + let import = self + .miner + .submit_seal(block_hash, seal) + .and_then(|block| self.import_sealed_block(block)); + if let Err(err) = import { + warn!(target: "poa", "Wrong internal seal submission! {:?}", err); + } + } + + fn broadcast_consensus_message(&self, _message: Bytes) {} + + fn epoch_transition_for(&self, _block_hash: H256) -> Option<::engines::EpochTransition> { + None + } + + fn as_full_client(&self) -> Option<&dyn BlockChainClient> { + Some(self) + } + + fn block_number(&self, id: BlockId) -> Option { + BlockChainClient::block_number(self, id) + } + + fn block_header(&self, id: BlockId) -> Option { + BlockChainClient::block_header(self, id) + } +} - fn block_header(&self, id: BlockId) -> Option { - BlockChainClient::block_header(self, id) - } +impl PrometheusMetrics for TestBlockChainClient { + fn prometheus_metrics(&self, _r: &mut prometheus::Registry) {} } diff --git a/ethcore/src/client/trace.rs b/ethcore/src/client/trace.rs index 73563a1d0f6..2fae25f8df2 100644 --- a/ethcore/src/client/trace.rs +++ b/ethcore/src/client/trace.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Bridge between Tracedb and Blockchain. @@ -22,19 +22,20 @@ use trace::DatabaseExtras as TraceDatabaseExtras; use types::BlockNumber; impl TraceDatabaseExtras for BlockChain { - fn block_hash(&self, block_number: BlockNumber) -> Option { - (self as &BlockProvider).block_hash(block_number) - } + fn block_hash(&self, block_number: BlockNumber) -> Option { + (self as &dyn BlockProvider).block_hash(block_number) + } - fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option { - (self as &BlockProvider).block_hash(block_number) - .and_then(|block_hash| { - let tx_address = TransactionAddress { - block_hash: block_hash, - index: tx_position - }; - self.transaction(&tx_address) - }) - .map(|tx| tx.hash()) - } + fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option { + (self as &dyn BlockProvider) + .block_hash(block_number) + .and_then(|block_hash| { + let tx_address = TransactionAddress { + block_hash: block_hash, + index: tx_position, + }; + self.transaction(&tx_address) + }) + .map(|tx| tx.hash()) + } } diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 8e4abc01cde..69eca8c7770 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -1,47 +1,51 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::collections::BTreeMap; -use std::sync::Arc; +//! Traits implemented by client. + +use std::{collections::BTreeMap, sync::Arc}; use blockchain::{BlockReceipts, TreeRoute}; use bytes::Bytes; use call_contract::{CallContract, RegistryInfo}; use ethcore_miner::pool::VerifiedTransaction; -use ethereum_types::{H256, U256, Address}; +use ethereum_types::{Address, H256, U256}; use evm::Schedule; use itertools::Itertools; use kvdb::DBValue; -use types::transaction::{self, LocalizedTransaction, SignedTransaction}; -use types::BlockNumber; -use types::basic_account::BasicAccount; -use types::block_status::BlockStatus; -use types::blockchain_info::BlockChainInfo; -use types::call_analytics::CallAnalytics; -use types::encoded; -use types::filter::Filter; -use types::header::Header; -use types::ids::*; -use types::log_entry::LocalizedLogEntry; -use types::pruning_info::PruningInfo; -use types::receipt::LocalizedReceipt; -use types::trace_filter::Filter as TraceFilter; +use types::{ + basic_account::BasicAccount, + block_status::BlockStatus, + blockchain_info::BlockChainInfo, + call_analytics::CallAnalytics, + data_format::DataFormat, + encoded, + filter::Filter, + header::Header, + ids::*, + log_entry::LocalizedLogEntry, + pruning_info::PruningInfo, + receipt::LocalizedReceipt, + trace_filter::Filter as TraceFilter, + transaction::{self, LocalizedTransaction, SignedTransaction}, + BlockNumber, +}; use vm::LastHashes; -use block::{OpenBlock, SealedBlock, ClosedBlock}; +use block::{ClosedBlock, OpenBlock, SealedBlock}; use client::Mode; use engines::EthEngine; use error::{Error, EthcoreResult}; @@ -49,64 +53,65 @@ use executed::CallError; use executive::Executed; use state::StateInfo; use trace::LocalizedTrace; -use verification::queue::QueueInfo as BlockQueueInfo; -use verification::queue::kind::blocks::Unverified; +use verification::queue::{kind::blocks::Unverified, QueueInfo as BlockQueueInfo}; /// State information to be used during client query pub enum StateOrBlock { - /// State to be used, may be pending - State(Box), + /// State to be used, may be pending + State(Box), - /// Id of an existing block from a chain to get state from - Block(BlockId) + /// Id of an existing block from a chain to get state from + Block(BlockId), } impl From for StateOrBlock { - fn from(info: S) -> StateOrBlock { - StateOrBlock::State(Box::new(info) as Box<_>) - } + fn from(info: S) -> StateOrBlock { + StateOrBlock::State(Box::new(info) as Box<_>) + } } -impl From> for StateOrBlock { - fn from(info: Box) -> StateOrBlock { - StateOrBlock::State(info) - } +impl From> for StateOrBlock { + fn from(info: Box) -> StateOrBlock { + StateOrBlock::State(info) + } } impl From for StateOrBlock { - fn from(id: BlockId) -> StateOrBlock { - StateOrBlock::Block(id) - } + fn from(id: BlockId) -> StateOrBlock { + StateOrBlock::Block(id) + } } /// Provides `nonce` and `latest_nonce` methods pub trait Nonce { - /// Attempt to get address nonce at given block. - /// May not fail on BlockId::Latest. - fn nonce(&self, address: &Address, id: BlockId) -> Option; - - /// Get address nonce at the latest block's state. - fn latest_nonce(&self, address: &Address) -> U256 { - self.nonce(address, BlockId::Latest) - .expect("nonce will return Some when given BlockId::Latest. nonce was given BlockId::Latest. \ - Therefore nonce has returned Some; qed") - } + /// Attempt to get address nonce at given block. + /// May not fail on BlockId::Latest. + fn nonce(&self, address: &Address, id: BlockId) -> Option; + + /// Get address nonce at the latest block's state. + fn latest_nonce(&self, address: &Address) -> U256 { + self.nonce(address, BlockId::Latest).expect( + "nonce will return Some when given BlockId::Latest. nonce was given BlockId::Latest. \ + Therefore nonce has returned Some; qed", + ) + } } /// Provides `balance` and `latest_balance` methods pub trait Balance { - /// Get address balance at the given block's state. - /// - /// May not return None if given BlockId::Latest. - /// Returns None if and only if the block's root hash has been pruned from the DB. - fn balance(&self, address: &Address, state: StateOrBlock) -> Option; - - /// Get address balance at the latest block's state. - fn latest_balance(&self, address: &Address) -> U256 { - self.balance(address, BlockId::Latest.into()) - .expect("balance will return Some if given BlockId::Latest. balance was given BlockId::Latest \ - Therefore balance has returned Some; qed") - } + /// Get address balance at the given block's state. + /// + /// May not return None if given BlockId::Latest. + /// Returns None if and only if the block's root hash has been pruned from the DB. + fn balance(&self, address: &Address, state: StateOrBlock) -> Option; + + /// Get address balance at the latest block's state. + fn latest_balance(&self, address: &Address) -> U256 { + self.balance(address, BlockId::Latest.into()).expect( + "balance will return Some if given BlockId::Latest. balance was given BlockId::Latest \ + Therefore balance has returned Some; qed", + ) + } } /// Provides methods to access account info @@ -114,45 +119,45 @@ pub trait AccountData: Nonce + Balance {} /// Provides `chain_info` method pub trait ChainInfo { - /// Get blockchain information. - fn chain_info(&self) -> BlockChainInfo; + /// Get blockchain information. + fn chain_info(&self) -> BlockChainInfo; } /// Provides various information on a block by it's ID pub trait BlockInfo { - /// Get raw block header data by block id. - fn block_header(&self, id: BlockId) -> Option; + /// Get raw block header data by block id. + fn block_header(&self, id: BlockId) -> Option; - /// Get the best block header. - fn best_block_header(&self) -> Header; + /// Get the best block header. + fn best_block_header(&self) -> Header; - /// Get raw block data by block header hash. - fn block(&self, id: BlockId) -> Option; + /// Get raw block data by block header hash. + fn block(&self, id: BlockId) -> Option; - /// Get address code hash at given block's state. - fn code_hash(&self, address: &Address, id: BlockId) -> Option; + /// Get address code hash at given block's state. + fn code_hash(&self, address: &Address, id: BlockId) -> Option; } /// Provides various information on a transaction by it's ID pub trait TransactionInfo { - /// Get the hash of block that contains the transaction, if any. - fn transaction_block(&self, id: TransactionId) -> Option; + /// Get the hash of block that contains the transaction, if any. + fn transaction_block(&self, id: TransactionId) -> Option; } /// Provides methods to access chain state pub trait StateClient { - /// Type representing chain state - type State: StateInfo; - - /// Get a copy of the best block's state. - fn latest_state(&self) -> Self::State; - - /// Attempt to get a copy of a specific block's final state. - /// - /// This will not fail if given BlockId::Latest. - /// Otherwise, this can fail (but may not) if the DB prunes state or the block - /// is unknown. - fn state_at(&self, id: BlockId) -> Option; + /// Type representing chain state + type State: StateInfo; + + /// Get a copy of the best block's state and header. + fn latest_state_and_header(&self) -> (Self::State, Header); + + /// Attempt to get a copy of a specific block's final state. + /// + /// This will not fail if given BlockId::Latest. + /// Otherwise, this can fail (but may not) if the DB prunes state or the block + /// is unknown. + fn state_at(&self, id: BlockId) -> Option; } /// Provides various blockchain information, like block header, chain state etc. @@ -161,241 +166,289 @@ pub trait BlockChain: ChainInfo + BlockInfo + TransactionInfo {} // FIXME Why these methods belong to BlockChainClient and not MiningBlockChainClient? /// Provides methods to import block into blockchain pub trait ImportBlock { - /// Import a block into the blockchain. - fn import_block(&self, block: Unverified) -> EthcoreResult; + /// Import a block into the blockchain. + fn import_block(&self, block: Unverified) -> EthcoreResult; } /// Provides `call` and `call_many` methods pub trait Call { - /// Type representing chain state - type State: StateInfo; - - /// Makes a non-persistent transaction call. - fn call(&self, tx: &SignedTransaction, analytics: CallAnalytics, state: &mut Self::State, header: &Header) -> Result; - - /// Makes multiple non-persistent but dependent transaction calls. - /// Returns a vector of successes or a failure if any of the transaction fails. - fn call_many(&self, txs: &[(SignedTransaction, CallAnalytics)], state: &mut Self::State, header: &Header) -> Result, CallError>; - - /// Estimates how much gas will be necessary for a call. - fn estimate_gas(&self, t: &SignedTransaction, state: &Self::State, header: &Header) -> Result; + /// Type representing chain state + type State: StateInfo; + + /// Makes a non-persistent transaction call. + fn call( + &self, + tx: &SignedTransaction, + analytics: CallAnalytics, + state: &mut Self::State, + header: &Header, + ) -> Result; + + /// Makes multiple non-persistent but dependent transaction calls. + /// Returns a vector of successes or a failure if any of the transaction fails. + fn call_many( + &self, + txs: &[(SignedTransaction, CallAnalytics)], + state: &mut Self::State, + header: &Header, + ) -> Result, CallError>; + + /// Estimates how much gas will be necessary for a call. + fn estimate_gas( + &self, + t: &SignedTransaction, + state: &Self::State, + header: &Header, + ) -> Result; } /// Provides `engine` method pub trait EngineInfo { - /// Get underlying engine object - fn engine(&self) -> &EthEngine; + /// Get underlying engine object + fn engine(&self) -> &dyn EthEngine; } /// IO operations that should off-load heavy work to another thread. pub trait IoClient: Sync + Send { - /// Queue transactions for importing. - fn queue_transactions(&self, transactions: Vec, peer_id: usize); - - /// Queue block import with transaction receipts. Does no sealing and transaction validation. - fn queue_ancient_block(&self, block_bytes: Unverified, receipts_bytes: Bytes) -> EthcoreResult; - - /// Queue conensus engine message. - fn queue_consensus_message(&self, message: Bytes); + /// Queue transactions for importing. + fn queue_transactions(&self, transactions: Vec, peer_id: usize); + + /// Queue block import with transaction receipts. Does no sealing and transaction validation. + fn queue_ancient_block( + &self, + block_bytes: Unverified, + receipts_bytes: Bytes, + ) -> EthcoreResult; + + /// Queue conensus engine message. + fn queue_consensus_message(&self, message: Bytes); } /// Provides recently seen bad blocks. pub trait BadBlocks { - /// Returns a list of blocks that were recently not imported because they were invalid. - fn bad_blocks(&self) -> Vec<(Unverified, String)>; + /// Returns a list of blocks that were recently not imported because they were invalid. + fn bad_blocks(&self) -> Vec<(Unverified, String)>; } /// Blockchain database client. Owns and manages a blockchain and a block queue. -pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContract + RegistryInfo + ImportBlock -+ IoClient + BadBlocks { - /// Look up the block number for the given block ID. - fn block_number(&self, id: BlockId) -> Option; - - /// Get raw block body data by block id. - /// Block body is an RLP list of two items: uncles and transactions. - fn block_body(&self, id: BlockId) -> Option; - - /// Get block status by block header hash. - fn block_status(&self, id: BlockId) -> BlockStatus; - - /// Get block total difficulty. - fn block_total_difficulty(&self, id: BlockId) -> Option; - - /// Attempt to get address storage root at given block. - /// May not fail on BlockId::Latest. - fn storage_root(&self, address: &Address, id: BlockId) -> Option; - - /// Get block hash. - fn block_hash(&self, id: BlockId) -> Option; - - /// Get address code at given block's state. - fn code(&self, address: &Address, state: StateOrBlock) -> Option>; - - /// Get address code at the latest block's state. - fn latest_code(&self, address: &Address) -> Option { - self.code(address, BlockId::Latest.into()) - .expect("code will return Some if given BlockId::Latest; qed") - } - - /// Get address code hash at given block's state. - - /// Get value of the storage at given position at the given block's state. - /// - /// May not return None if given BlockId::Latest. - /// Returns None if and only if the block's root hash has been pruned from the DB. - fn storage_at(&self, address: &Address, position: &H256, state: StateOrBlock) -> Option; - - /// Get value of the storage at given position at the latest block's state. - fn latest_storage_at(&self, address: &Address, position: &H256) -> H256 { - self.storage_at(address, position, BlockId::Latest.into()) +pub trait BlockChainClient: + Sync + + Send + + AccountData + + BlockChain + + CallContract + + RegistryInfo + + ImportBlock + + IoClient + + BadBlocks +{ + /// Look up the block number for the given block ID. + fn block_number(&self, id: BlockId) -> Option; + + /// Get raw block body data by block id. + /// Block body is an RLP list of two items: uncles and transactions. + fn block_body(&self, id: BlockId) -> Option; + + /// Get block status by block header hash. + fn block_status(&self, id: BlockId) -> BlockStatus; + + /// Get block total difficulty. + fn block_total_difficulty(&self, id: BlockId) -> Option; + + /// Attempt to get address storage root at given block. + /// May not fail on BlockId::Latest. + fn storage_root(&self, address: &Address, id: BlockId) -> Option; + + /// Get block hash. + fn block_hash(&self, id: BlockId) -> Option; + + /// Get address code at given block's state. + fn code(&self, address: &Address, state: StateOrBlock) -> Option>; + + /// Get address code at the latest block's state. + fn latest_code(&self, address: &Address) -> Option { + self.code(address, BlockId::Latest.into()) + .expect("code will return Some if given BlockId::Latest; qed") + } + + /// Get address code hash at given block's state. + + /// Get value of the storage at given position at the given block's state. + /// + /// May not return None if given BlockId::Latest. + /// Returns None if and only if the block's root hash has been pruned from the DB. + fn storage_at(&self, address: &Address, position: &H256, state: StateOrBlock) -> Option; + + /// Get value of the storage at given position at the latest block's state. + fn latest_storage_at(&self, address: &Address, position: &H256) -> H256 { + self.storage_at(address, position, BlockId::Latest.into()) .expect("storage_at will return Some if given BlockId::Latest. storage_at was given BlockId::Latest. \ Therefore storage_at has returned Some; qed") - } - - /// Get a list of all accounts in the block `id`, if fat DB is in operation, otherwise `None`. - /// If `after` is set the list starts with the following item. - fn list_accounts(&self, id: BlockId, after: Option<&Address>, count: u64) -> Option>; - - /// Get a list of all storage keys in the block `id`, if fat DB is in operation, otherwise `None`. - /// If `after` is set the list starts with the following item. - fn list_storage(&self, id: BlockId, account: &Address, after: Option<&H256>, count: u64) -> Option>; - - /// Get transaction with given hash. - fn transaction(&self, id: TransactionId) -> Option; - - /// Get uncle with given id. - fn uncle(&self, id: UncleId) -> Option; - - /// Get transaction receipt with given hash. - fn transaction_receipt(&self, id: TransactionId) -> Option; + } + + /// Get a list of all accounts in the block `id`, if fat DB is in operation, otherwise `None`. + /// If `after` is set the list starts with the following item. + fn list_accounts( + &self, + id: BlockId, + after: Option<&Address>, + count: u64, + ) -> Option>; + + /// Get a list of all storage keys in the block `id`, if fat DB is in operation, otherwise `None`. + /// If `after` is set the list starts with the following item. + fn list_storage( + &self, + id: BlockId, + account: &Address, + after: Option<&H256>, + count: u64, + ) -> Option>; + + /// Get transaction with given hash. + fn transaction(&self, id: TransactionId) -> Option; + + /// Get uncle with given id. + fn uncle(&self, id: UncleId) -> Option; + + /// Get transaction receipt with given hash. + fn transaction_receipt(&self, id: TransactionId) -> Option; + + /// Get localized receipts for all transaction in given block. + fn localized_block_receipts(&self, id: BlockId) -> Option>; + + /// Get a tree route between `from` and `to`. + /// See `BlockChain::tree_route`. + fn tree_route(&self, from: &H256, to: &H256) -> Option; + + /// Get all possible uncle hashes for a block. + fn find_uncles(&self, hash: &H256) -> Option>; + + /// Get block receipts data by block header hash. + fn block_receipts(&self, hash: &H256) -> Option; - /// Get localized receipts for all transaction in given block. - fn localized_block_receipts(&self, id: BlockId) -> Option>; + /// Get block queue information. + fn queue_info(&self) -> BlockQueueInfo; - /// Get a tree route between `from` and `to`. - /// See `BlockChain::tree_route`. - fn tree_route(&self, from: &H256, to: &H256) -> Option; + /// Returns true if block queue is empty. + fn is_queue_empty(&self) -> bool { + self.queue_info().is_empty() + } + + /// Clear block queue and abort all import activity. + fn clear_queue(&self); + + /// Get the registrar address, if it exists. + fn additional_params(&self) -> BTreeMap; + + /// Returns logs matching given filter. If one of the filtering block cannot be found, returns the block id that caused the error. + fn logs(&self, filter: Filter) -> Result, BlockId>; + + /// Replays a given transaction for inspection. + fn replay(&self, t: TransactionId, analytics: CallAnalytics) -> Result; + + /// Replays all the transactions in a given block for inspection. + fn replay_block_transactions( + &self, + block: BlockId, + analytics: CallAnalytics, + ) -> Result>, CallError>; + + /// Returns traces matching given filter. + fn filter_traces(&self, filter: TraceFilter) -> Option>; + + /// Returns trace with given id. + fn trace(&self, trace: TraceId) -> Option; + + /// Returns traces created by transaction. + fn transaction_traces(&self, trace: TransactionId) -> Option>; + + /// Returns traces created by transaction from block. + fn block_traces(&self, trace: BlockId) -> Option>; + + /// Get last hashes starting from best block. + fn last_hashes(&self) -> LastHashes; + + /// List all ready transactions that should be propagated to other peers. + fn transactions_to_propagate(&self) -> Vec>; + + /// Sorted list of transaction gas prices from at least last sample_size blocks. + fn gas_price_corpus(&self, sample_size: usize) -> ::stats::Corpus { + let mut h = self.chain_info().best_block_hash; + let mut corpus = Vec::new(); + while corpus.is_empty() { + for _ in 0..sample_size { + let block = match self.block(BlockId::Hash(h)) { + Some(block) => block, + None => return corpus.into(), + }; + + if block.number() == 0 { + return corpus.into(); + } + block + .transaction_views() + .iter() + .foreach(|t| corpus.push(t.gas_price())); + h = block.parent_hash().clone(); + } + } + corpus.into() + } - /// Get all possible uncle hashes for a block. - fn find_uncles(&self, hash: &H256) -> Option>; + /// Get the preferred chain ID to sign on + fn signing_chain_id(&self) -> Option; - /// Get latest state node - fn state_data(&self, hash: &H256) -> Option; + /// Get the mode. + fn mode(&self) -> Mode; - /// Get block receipts data by block header hash. - fn block_receipts(&self, hash: &H256) -> Option; + /// Set the mode. + fn set_mode(&self, mode: Mode); - /// Get block queue information. - fn queue_info(&self) -> BlockQueueInfo; + /// Get the chain spec name. + fn spec_name(&self) -> String; - /// Returns true if block queue is empty. - fn is_queue_empty(&self) -> bool { - self.queue_info().is_empty() - } + /// Set the chain via a spec name. + fn set_spec_name(&self, spec_name: String) -> Result<(), ()>; - /// Clear block queue and abort all import activity. - fn clear_queue(&self); + /// Disable the client from importing blocks. This cannot be undone in this session and indicates + /// that a subsystem has reason to believe this executable incapable of syncing the chain. + fn disable(&self); - /// Get the registrar address, if it exists. - fn additional_params(&self) -> BTreeMap; + /// Returns engine-related extra info for `BlockId`. + fn block_extra_info(&self, id: BlockId) -> Option>; - /// Returns logs matching given filter. If one of the filtering block cannot be found, returns the block id that caused the error. - fn logs(&self, filter: Filter) -> Result, BlockId>; + /// Returns engine-related extra info for `UncleId`. + fn uncle_extra_info(&self, id: UncleId) -> Option>; - /// Replays a given transaction for inspection. - fn replay(&self, t: TransactionId, analytics: CallAnalytics) -> Result; + /// Returns information about pruning/data availability. + fn pruning_info(&self) -> PruningInfo; - /// Replays all the transactions in a given block for inspection. - fn replay_block_transactions(&self, block: BlockId, analytics: CallAnalytics) -> Result>, CallError>; + /// Schedule state-altering transaction to be executed on the next pending block. + fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error>; - /// Returns traces matching given filter. - fn filter_traces(&self, filter: TraceFilter) -> Option>; + /// Get the address of the registry itself. + fn registrar_address(&self) -> Option
; - /// Returns trace with given id. - fn trace(&self, trace: TraceId) -> Option; - - /// Returns traces created by transaction. - fn transaction_traces(&self, trace: TransactionId) -> Option>; - - /// Returns traces created by transaction from block. - fn block_traces(&self, trace: BlockId) -> Option>; - - /// Get last hashes starting from best block. - fn last_hashes(&self) -> LastHashes; - - /// List all ready transactions that should be propagated to other peers. - fn transactions_to_propagate(&self) -> Vec>; - - /// Sorted list of transaction gas prices from at least last sample_size blocks. - fn gas_price_corpus(&self, sample_size: usize) -> ::stats::Corpus { - let mut h = self.chain_info().best_block_hash; - let mut corpus = Vec::new(); - while corpus.is_empty() { - for _ in 0..sample_size { - let block = match self.block(BlockId::Hash(h)) { - Some(block) => block, - None => return corpus.into(), - }; - - if block.number() == 0 { - return corpus.into(); - } - block.transaction_views().iter().foreach(|t| corpus.push(t.gas_price())); - h = block.parent_hash().clone(); - } - } - corpus.into() - } - - /// Get the preferred chain ID to sign on - fn signing_chain_id(&self) -> Option; - - /// Get the mode. - fn mode(&self) -> Mode; - - /// Set the mode. - fn set_mode(&self, mode: Mode); - - /// Get the chain spec name. - fn spec_name(&self) -> String; - - /// Set the chain via a spec name. - fn set_spec_name(&self, spec_name: String) -> Result<(), ()>; - - /// Disable the client from importing blocks. This cannot be undone in this session and indicates - /// that a subsystem has reason to believe this executable incapable of syncing the chain. - fn disable(&self); - - /// Returns engine-related extra info for `BlockId`. - fn block_extra_info(&self, id: BlockId) -> Option>; - - /// Returns engine-related extra info for `UncleId`. - fn uncle_extra_info(&self, id: UncleId) -> Option>; - - /// Returns information about pruning/data availability. - fn pruning_info(&self) -> PruningInfo; - - /// Schedule state-altering transaction to be executed on the next pending block. - fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error>; - - /// Get the address of the registry itself. - fn registrar_address(&self) -> Option
; + /// Returns true, if underlying import queue is processing possible fork at the moment + fn is_processing_fork(&self) -> bool; } /// Provides `reopen_block` method pub trait ReopenBlock { - /// Reopens an OpenBlock and updates uncles. - fn reopen_block(&self, block: ClosedBlock) -> OpenBlock; + /// Reopens an OpenBlock and updates uncles. + fn reopen_block(&self, block: ClosedBlock) -> OpenBlock; } /// Provides `prepare_open_block` method pub trait PrepareOpenBlock { - /// Returns OpenBlock prepared for closing. - fn prepare_open_block(&self, - author: Address, - gas_range_target: (U256, U256), - extra_data: Bytes - ) -> Result; + /// Returns OpenBlock prepared for closing. + fn prepare_open_block( + &self, + author: Address, + gas_range_target: (U256, U256), + extra_data: Bytes, + ) -> Result; } /// Provides methods used for sealing new state @@ -403,77 +456,116 @@ pub trait BlockProducer: PrepareOpenBlock + ReopenBlock {} /// Provides `latest_schedule` method pub trait ScheduleInfo { - /// Returns latest schedule. - fn latest_schedule(&self) -> Schedule; + /// Returns latest schedule. + fn latest_schedule(&self) -> Schedule; } ///Provides `import_sealed_block` method pub trait ImportSealedBlock { - /// Import sealed block. Skips all verifications. - fn import_sealed_block(&self, block: SealedBlock) -> EthcoreResult; + /// Import sealed block. Skips all verifications. + fn import_sealed_block(&self, block: SealedBlock) -> EthcoreResult; } /// Provides `broadcast_proposal_block` method pub trait BroadcastProposalBlock { - /// Broadcast a block proposal. - fn broadcast_proposal_block(&self, block: SealedBlock); + /// Broadcast a block proposal. + fn broadcast_proposal_block(&self, block: SealedBlock); } /// Provides methods to import sealed block and broadcast a block proposal pub trait SealedBlockImporter: ImportSealedBlock + BroadcastProposalBlock {} +/// Do we want to force update sealing? +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum ForceUpdateSealing { + /// Ideally you want to use `No` at all times as `Yes` skips `reseal_required` checks. + Yes, + /// Don't skip `reseal_required` checks + No, +} + /// Client facilities used by internally sealing Engines. pub trait EngineClient: Sync + Send + ChainInfo { - /// Make a new block and seal it. - fn update_sealing(&self); + /// Make a new block and seal it. + fn update_sealing(&self, force: ForceUpdateSealing); - /// Submit a seal for a block in the mining queue. - fn submit_seal(&self, block_hash: H256, seal: Vec); + /// Submit a seal for a block in the mining queue. + fn submit_seal(&self, block_hash: H256, seal: Vec); - /// Broadcast a consensus message to the network. - fn broadcast_consensus_message(&self, message: Bytes); + /// Broadcast a consensus message to the network. + fn broadcast_consensus_message(&self, message: Bytes); - /// Get the transition to the epoch the given parent hash is part of - /// or transitions to. - /// This will give the epoch that any children of this parent belong to. - /// - /// The block corresponding the the parent hash must be stored already. - fn epoch_transition_for(&self, parent_hash: H256) -> Option<::engines::EpochTransition>; + /// Get the transition to the epoch the given parent hash is part of + /// or transitions to. + /// This will give the epoch that any children of this parent belong to. + /// + /// The block corresponding the the parent hash must be stored already. + fn epoch_transition_for(&self, parent_hash: H256) -> Option<::engines::EpochTransition>; - /// Attempt to cast the engine client to a full client. - fn as_full_client(&self) -> Option<&BlockChainClient>; + /// Attempt to cast the engine client to a full client. + fn as_full_client(&self) -> Option<&dyn BlockChainClient>; - /// Get a block number by ID. - fn block_number(&self, id: BlockId) -> Option; + /// Get a block number by ID. + fn block_number(&self, id: BlockId) -> Option; - /// Get raw block header data by block id. - fn block_header(&self, id: BlockId) -> Option; + /// Get raw block header data by block id. + fn block_header(&self, id: BlockId) -> Option; } /// Extended client interface for providing proofs of the state. pub trait ProvingBlockChainClient: BlockChainClient { - /// Prove account storage at a specific block id. - /// - /// Both provided keys assume a secure trie. - /// Returns a vector of raw trie nodes (in order from the root) proving the storage query. - fn prove_storage(&self, key1: H256, key2: H256, id: BlockId) -> Option<(Vec, H256)>; - - /// Prove account existence at a specific block id. - /// The key is the keccak hash of the account's address. - /// Returns a vector of raw trie nodes (in order from the root) proving the query. - fn prove_account(&self, key1: H256, id: BlockId) -> Option<(Vec, BasicAccount)>; - - /// Prove execution of a transaction at the given block. - /// Returns the output of the call and a vector of database items necessary - /// to reproduce it. - fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option<(Bytes, Vec)>; - - /// Get an epoch change signal by block hash. - fn epoch_signal(&self, hash: H256) -> Option>; + /// Prove account storage at a specific block id. + /// + /// Both provided keys assume a secure trie. + /// Returns a vector of raw trie nodes (in order from the root) proving the storage query. + fn prove_storage(&self, key1: H256, key2: H256, id: BlockId) -> Option<(Vec, H256)>; + + /// Prove account existence at a specific block id. + /// The key is the keccak hash of the account's address. + /// Returns a vector of raw trie nodes (in order from the root) proving the query. + fn prove_account(&self, key1: H256, id: BlockId) -> Option<(Vec, BasicAccount)>; + + /// Prove execution of a transaction at the given block. + /// Returns the output of the call and a vector of database items necessary + /// to reproduce it. + fn prove_transaction( + &self, + transaction: SignedTransaction, + id: BlockId, + ) -> Option<(Bytes, Vec)>; + + /// Get an epoch change signal by block hash. + fn epoch_signal(&self, hash: H256) -> Option>; } /// resets the blockchain pub trait BlockChainReset { - /// reset to best_block - n - fn reset(&self, num: u32) -> Result<(), String>; + /// reset to best_block - n + fn reset(&self, num: u32) -> Result<(), String>; +} + +/// Provides a method for importing/exporting blocks +pub trait ImportExportBlocks { + /// Export blocks to destination, with the given from, to and format argument. + /// destination could be a file or stdout. + /// If the format is hex, each block is written on a new line. + /// For binary exports, all block data is written to the same line. + fn export_blocks<'a>( + &self, + destination: Box, + from: BlockId, + to: BlockId, + format: Option, + ) -> Result<(), String>; + + /// Import blocks from destination, with the given format argument + /// Source could be a file or stdout. + /// For hex format imports, it attempts to read the blocks on a line by line basis. + /// For binary format imports, reads the 8 byte RLP header in order to decode the block + /// length to be read. + fn import_blocks<'a>( + &self, + source: Box, + format: Option, + ) -> Result<(), String>; } diff --git a/ethcore/src/engines/authority_round/finality.rs b/ethcore/src/engines/authority_round/finality.rs index af57278c9f3..b7542525436 100644 --- a/ethcore/src/engines/authority_round/finality.rs +++ b/ethcore/src/engines/authority_round/finality.rs @@ -1,25 +1,27 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Finality proof generation and checking. -use std::collections::{VecDeque}; -use std::collections::hash_map::{HashMap, Entry}; +use std::collections::{ + hash_map::{Entry, HashMap}, + VecDeque, +}; -use ethereum_types::{H256, Address}; +use ethereum_types::{Address, H256}; use engines::validator_set::SimpleList; @@ -30,104 +32,122 @@ pub struct UnknownValidator; /// Rolling finality checker for authority round consensus. /// Stores a chain of unfinalized hashes that can be pushed onto. pub struct RollingFinality { - headers: VecDeque<(H256, Vec
)>, - signers: SimpleList, - sign_count: HashMap, - last_pushed: Option, + headers: VecDeque<(H256, Vec
)>, + signers: SimpleList, + sign_count: HashMap, + last_pushed: Option, } impl RollingFinality { - /// Create a blank finality checker under the given validator set. - pub fn blank(signers: Vec
) -> Self { - RollingFinality { - headers: VecDeque::new(), - signers: SimpleList::new(signers), - sign_count: HashMap::new(), - last_pushed: None, - } - } - - /// Extract unfinalized subchain from ancestry iterator. - /// Clears the current subchain. - /// - /// Fails if any provided signature isn't part of the signers set. - pub fn build_ancestry_subchain(&mut self, iterable: I) -> Result<(), UnknownValidator> - where I: IntoIterator)> - { - self.clear(); - for (hash, signers) in iterable { - if signers.iter().any(|s| !self.signers.contains(s)) { return Err(UnknownValidator) } - if self.last_pushed.is_none() { self.last_pushed = Some(hash) } - - // break when we've got our first finalized block. - { - let current_signed = self.sign_count.len(); - - let new_signers = signers.iter().filter(|s| !self.sign_count.contains_key(s)).count(); - let would_be_finalized = (current_signed + new_signers) * 2 > self.signers.len(); - - if would_be_finalized { - trace!(target: "finality", "Encountered already finalized block {}", hash); - break - } - - for signer in signers.iter() { - *self.sign_count.entry(*signer).or_insert(0) += 1; - } - } - - self.headers.push_front((hash, signers)); - } - - trace!(target: "finality", "Rolling finality state: {:?}", self.headers); - Ok(()) - } - - /// Clear the finality status, but keeps the validator set. - pub fn clear(&mut self) { - self.headers.clear(); - self.sign_count.clear(); - self.last_pushed = None; - } - - /// Returns the last pushed hash. - pub fn subchain_head(&self) -> Option { - self.last_pushed - } - - /// Get an iterator over stored hashes in order. - #[cfg(test)] - pub fn unfinalized_hashes(&self) -> impl Iterator { - self.headers.iter().map(|(h, _)| h) - } - - /// Get the validator set. - pub fn validators(&self) -> &SimpleList { &self.signers } - - /// Push a hash onto the rolling finality checker (implying `subchain_head` == head.parent) - /// - /// Fails if `signer` isn't a member of the active validator set. - /// Returns a list of all newly finalized headers. - // TODO: optimize with smallvec. - pub fn push_hash(&mut self, head: H256, signers: Vec
) -> Result, UnknownValidator> { - if signers.iter().any(|s| !self.signers.contains(s)) { return Err(UnknownValidator) } - - for signer in signers.iter() { - *self.sign_count.entry(*signer).or_insert(0) += 1; - } - - self.headers.push_back((head, signers)); - - let mut newly_finalized = Vec::new(); - - while self.sign_count.len() * 2 > self.signers.len() { - let (hash, signers) = self.headers.pop_front() - .expect("headers length always greater than sign count length; qed"); - - newly_finalized.push(hash); - - for signer in signers { - match self.sign_count.entry(signer) { + /// Create a blank finality checker under the given validator set. + pub fn blank(signers: Vec
) -> Self { + RollingFinality { + headers: VecDeque::new(), + signers: SimpleList::new(signers), + sign_count: HashMap::new(), + last_pushed: None, + } + } + + /// Extract unfinalized subchain from ancestry iterator. + /// Clears the current subchain. + /// + /// Fails if any provided signature isn't part of the signers set. + pub fn build_ancestry_subchain(&mut self, iterable: I) -> Result<(), UnknownValidator> + where + I: IntoIterator)>, + { + self.clear(); + for (hash, signers) in iterable { + if signers.iter().any(|s| !self.signers.contains(s)) { + return Err(UnknownValidator); + } + if self.last_pushed.is_none() { + self.last_pushed = Some(hash) + } + + // break when we've got our first finalized block. + { + let current_signed = self.sign_count.len(); + + let new_signers = signers + .iter() + .filter(|s| !self.sign_count.contains_key(s)) + .count(); + let would_be_finalized = (current_signed + new_signers) * 2 > self.signers.len(); + + if would_be_finalized { + trace!(target: "finality", "Encountered already finalized block {}", hash); + break; + } + + for signer in signers.iter() { + *self.sign_count.entry(*signer).or_insert(0) += 1; + } + } + + self.headers.push_front((hash, signers)); + } + + trace!(target: "finality", "Rolling finality state: {:?}", self.headers); + Ok(()) + } + + /// Clear the finality status, but keeps the validator set. + pub fn clear(&mut self) { + self.headers.clear(); + self.sign_count.clear(); + self.last_pushed = None; + } + + /// Returns the last pushed hash. + pub fn subchain_head(&self) -> Option { + self.last_pushed + } + + /// Get an iterator over stored hashes in order. + #[cfg(test)] + pub fn unfinalized_hashes(&self) -> impl Iterator { + self.headers.iter().map(|(h, _)| h) + } + + /// Get the validator set. + pub fn validators(&self) -> &SimpleList { + &self.signers + } + + /// Push a hash onto the rolling finality checker (implying `subchain_head` == head.parent) + /// + /// Fails if `signer` isn't a member of the active validator set. + /// Returns a list of all newly finalized headers. + // TODO: optimize with smallvec. + pub fn push_hash( + &mut self, + head: H256, + signers: Vec
, + ) -> Result, UnknownValidator> { + if signers.iter().any(|s| !self.signers.contains(s)) { + return Err(UnknownValidator); + } + + for signer in signers.iter() { + *self.sign_count.entry(*signer).or_insert(0) += 1; + } + + self.headers.push_back((head, signers)); + + let mut newly_finalized = Vec::new(); + + while self.sign_count.len() * 2 > self.signers.len() { + let (hash, signers) = self + .headers + .pop_front() + .expect("headers length always greater than sign count length; qed"); + + newly_finalized.push(hash); + + for signer in signers { + match self.sign_count.entry(signer) { Entry::Occupied(mut entry) => { // decrement count for this signer and purge on zero. *entry.get_mut() -= 1; @@ -138,82 +158,100 @@ impl RollingFinality { } Entry::Vacant(_) => panic!("all hashes in `header` should have entries in `sign_count` for their signers; qed"), } - } - } + } + } - trace!(target: "finality", "Blocks finalized by {:?}: {:?}", head, newly_finalized); + trace!(target: "finality", "Blocks finalized by {:?}: {:?}", head, newly_finalized); - self.last_pushed = Some(head); - Ok(newly_finalized) - } + self.last_pushed = Some(head); + Ok(newly_finalized) + } } #[cfg(test)] mod tests { - use ethereum_types::{H256, Address}; - use super::RollingFinality; - - #[test] - fn rejects_unknown_signers() { - let signers = (0..3).map(|_| Address::random()).collect::>(); - let mut finality = RollingFinality::blank(signers.clone()); - assert!(finality.push_hash(H256::random(), vec![signers[0], Address::random()]).is_err()); - } - - #[test] - fn finalize_multiple() { - let signers: Vec<_> = (0..6).map(|_| Address::random()).collect(); - - let mut finality = RollingFinality::blank(signers.clone()); - let hashes: Vec<_> = (0..7).map(|_| H256::random()).collect(); - - // 3 / 6 signers is < 51% so no finality. - for (i, hash) in hashes.iter().take(6).cloned().enumerate() { - let i = i % 3; - assert!(finality.push_hash(hash, vec![signers[i]]).unwrap().len() == 0); - } - - // after pushing a block signed by a fourth validator, the first four - // blocks of the unverified chain become verified. - assert_eq!(finality.push_hash(hashes[6], vec![signers[4]]).unwrap(), - vec![hashes[0], hashes[1], hashes[2], hashes[3]]); - } - - #[test] - fn finalize_multiple_signers() { - let signers: Vec<_> = (0..6).map(|_| Address::random()).collect(); - let mut finality = RollingFinality::blank(signers.clone()); - let hash = H256::random(); - - // after pushing a block signed by four validators, it becomes verified right away. - assert_eq!(finality.push_hash(hash, signers[0..4].to_vec()).unwrap(), vec![hash]); - } - - #[test] - fn from_ancestry() { - let signers: Vec<_> = (0..6).map(|_| Address::random()).collect(); - let hashes: Vec<_> = (0..12).map(|i| (H256::random(), vec![signers[i % 6]])).collect(); - - let mut finality = RollingFinality::blank(signers.clone()); - finality.build_ancestry_subchain(hashes.iter().rev().cloned()).unwrap(); - - assert_eq!(finality.unfinalized_hashes().count(), 3); - assert_eq!(finality.subchain_head(), Some(hashes[11].0)); - } - - #[test] - fn from_ancestry_multiple_signers() { - let signers: Vec<_> = (0..6).map(|_| Address::random()).collect(); - let hashes: Vec<_> = (0..12).map(|i| { - (H256::random(), vec![signers[i % 6], signers[(i + 1) % 6], signers[(i + 2) % 6]]) - }).collect(); - - let mut finality = RollingFinality::blank(signers.clone()); - finality.build_ancestry_subchain(hashes.iter().rev().cloned()).unwrap(); - - // only the last hash has < 51% of authorities' signatures - assert_eq!(finality.unfinalized_hashes().count(), 1); - assert_eq!(finality.unfinalized_hashes().next(), Some(&hashes[11].0)); - assert_eq!(finality.subchain_head(), Some(hashes[11].0)); - } + use super::RollingFinality; + use ethereum_types::{Address, H256}; + + #[test] + fn rejects_unknown_signers() { + let signers = (0..3).map(|_| Address::random()).collect::>(); + let mut finality = RollingFinality::blank(signers.clone()); + assert!(finality + .push_hash(H256::random(), vec![signers[0], Address::random()]) + .is_err()); + } + + #[test] + fn finalize_multiple() { + let signers: Vec<_> = (0..6).map(|_| Address::random()).collect(); + + let mut finality = RollingFinality::blank(signers.clone()); + let hashes: Vec<_> = (0..7).map(|_| H256::random()).collect(); + + // 3 / 6 signers is < 51% so no finality. + for (i, hash) in hashes.iter().take(6).cloned().enumerate() { + let i = i % 3; + assert!(finality.push_hash(hash, vec![signers[i]]).unwrap().len() == 0); + } + + // after pushing a block signed by a fourth validator, the first four + // blocks of the unverified chain become verified. + assert_eq!( + finality.push_hash(hashes[6], vec![signers[4]]).unwrap(), + vec![hashes[0], hashes[1], hashes[2], hashes[3]] + ); + } + + #[test] + fn finalize_multiple_signers() { + let signers: Vec<_> = (0..6).map(|_| Address::random()).collect(); + let mut finality = RollingFinality::blank(signers.clone()); + let hash = H256::random(); + + // after pushing a block signed by four validators, it becomes verified right away. + assert_eq!( + finality.push_hash(hash, signers[0..4].to_vec()).unwrap(), + vec![hash] + ); + } + + #[test] + fn from_ancestry() { + let signers: Vec<_> = (0..6).map(|_| Address::random()).collect(); + let hashes: Vec<_> = (0..12) + .map(|i| (H256::random(), vec![signers[i % 6]])) + .collect(); + + let mut finality = RollingFinality::blank(signers.clone()); + finality + .build_ancestry_subchain(hashes.iter().rev().cloned()) + .unwrap(); + + assert_eq!(finality.unfinalized_hashes().count(), 3); + assert_eq!(finality.subchain_head(), Some(hashes[11].0)); + } + + #[test] + fn from_ancestry_multiple_signers() { + let signers: Vec<_> = (0..6).map(|_| Address::random()).collect(); + let hashes: Vec<_> = (0..12) + .map(|i| { + ( + H256::random(), + vec![signers[i % 6], signers[(i + 1) % 6], signers[(i + 2) % 6]], + ) + }) + .collect(); + + let mut finality = RollingFinality::blank(signers.clone()); + finality + .build_ancestry_subchain(hashes.iter().rev().cloned()) + .unwrap(); + + // only the last hash has < 51% of authorities' signatures + assert_eq!(finality.unfinalized_hashes().count(), 1); + assert_eq!(finality.unfinalized_hashes().next(), Some(&hashes[11].0)); + assert_eq!(finality.subchain_head(), Some(hashes[11].0)); + } } diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index 36f25144f73..3022dc8df31 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -1,290 +1,315 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! A blockchain engine that supports a non-instant BFT proof-of-authority. -use std::collections::{BTreeMap, BTreeSet, HashSet}; -use std::{cmp, fmt}; -use std::iter::FromIterator; -use std::ops::Deref; -use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering}; -use std::sync::{Weak, Arc}; -use std::time::{UNIX_EPOCH, SystemTime, Duration}; +use std::{ + cmp, + collections::{BTreeMap, BTreeSet, HashSet}, + fmt, + iter::FromIterator, + ops::Deref, + sync::{ + atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering}, + Arc, Weak, + }, + time::{Duration, UNIX_EPOCH}, +}; +use self::finality::RollingFinality; +use super::{ + signer::EngineSigner, + validator_set::{new_validator_set, SimpleList, ValidatorSet}, +}; use block::*; -use client::EngineClient; -use engines::{Engine, Seal, EngineError, ConstructedVerifier}; -use engines::block_reward; -use engines::block_reward::{BlockRewardContract, RewardKind}; -use error::{Error, ErrorKind, BlockError}; +use client::{traits::ForceUpdateSealing, EngineClient}; +use engines::{ + block_reward, + block_reward::{BlockRewardContract, RewardKind}, + ConstructedVerifier, Engine, EngineError, Seal, +}; +use error::{BlockError, Error, ErrorKind}; +use ethereum_types::{Address, H256, H520, U128, U256}; use ethjson; -use machine::{AuxiliaryData, Call, EthereumMachine}; -use hash::keccak; -use super::signer::EngineSigner; -use super::validator_set::{ValidatorSet, SimpleList, new_validator_set}; -use self::finality::RollingFinality; use ethkey::{self, Signature}; -use io::{IoContext, IoHandler, TimerToken, IoService}; +use hash::keccak; +use io::{IoContext, IoHandler, IoService, TimerToken}; use itertools::{self, Itertools}; -use rlp::{encode, Decodable, DecoderError, Encodable, RlpStream, Rlp}; -use ethereum_types::{H256, H520, Address, U128, U256}; +use machine::{AuxiliaryData, Call, EthereumMachine}; use parking_lot::{Mutex, RwLock}; -use types::BlockNumber; -use types::header::{Header, ExtendedHeader}; -use types::ancestry_action::AncestryAction; -use unexpected::{Mismatch, OutOfBounds}; - -#[cfg(not(time_checked_add))] +use rlp::{encode, Decodable, DecoderError, Encodable, Rlp, RlpStream}; use time_utils::CheckedSystemTime; +use types::{ + ancestry_action::AncestryAction, + header::{ExtendedHeader, Header}, + BlockNumber, +}; +use unexpected::{Mismatch, OutOfBounds}; mod finality; /// `AuthorityRound` params. pub struct AuthorityRoundParams { - /// Time to wait before next block or authority switching, - /// in seconds. - /// - /// Deliberately typed as u16 as too high of a value leads - /// to slow block issuance. - pub step_duration: u16, - /// Starting step, - pub start_step: Option, - /// Valid validators. - pub validators: Box, - /// Chain score validation transition block. - pub validate_score_transition: u64, - /// Monotonic step validation transition block. - pub validate_step_transition: u64, - /// Immediate transitions. - pub immediate_transitions: bool, - /// Block reward in base units. - pub block_reward: U256, - /// Block reward contract transition block. - pub block_reward_contract_transition: u64, - /// Block reward contract. - pub block_reward_contract: Option, - /// Number of accepted uncles transition block. - pub maximum_uncle_count_transition: u64, - /// Number of accepted uncles. - pub maximum_uncle_count: usize, - /// Empty step messages transition block. - pub empty_steps_transition: u64, - /// Number of accepted empty steps. - pub maximum_empty_steps: usize, - /// Transition block to strict empty steps validation. - pub strict_empty_steps_transition: u64, + /// Time to wait before next block or authority switching, + /// in seconds. + /// + /// Deliberately typed as u16 as too high of a value leads + /// to slow block issuance. + pub step_duration: u16, + /// Starting step, + pub start_step: Option, + /// Valid validators. + pub validators: Box, + /// Chain score validation transition block. + pub validate_score_transition: u64, + /// Monotonic step validation transition block. + pub validate_step_transition: u64, + /// Immediate transitions. + pub immediate_transitions: bool, + /// Block reward in base units. + pub block_reward: U256, + /// Block reward contract transition block. + pub block_reward_contract_transition: u64, + /// Block reward contract. + pub block_reward_contract: Option, + /// Number of accepted uncles transition block. + pub maximum_uncle_count_transition: u64, + /// Number of accepted uncles. + pub maximum_uncle_count: usize, + /// Empty step messages transition block. + pub empty_steps_transition: u64, + /// Number of accepted empty steps. + pub maximum_empty_steps: usize, + /// Transition block to strict empty steps validation. + pub strict_empty_steps_transition: u64, } const U16_MAX: usize = ::std::u16::MAX as usize; impl From for AuthorityRoundParams { - fn from(p: ethjson::spec::AuthorityRoundParams) -> Self { - let mut step_duration_usize: usize = p.step_duration.into(); - if step_duration_usize > U16_MAX { - step_duration_usize = U16_MAX; - warn!(target: "engine", "step_duration is too high ({}), setting it to {}", step_duration_usize, U16_MAX); - } - AuthorityRoundParams { - step_duration: step_duration_usize as u16, - validators: new_validator_set(p.validators), - start_step: p.start_step.map(Into::into), - validate_score_transition: p.validate_score_transition.map_or(0, Into::into), - validate_step_transition: p.validate_step_transition.map_or(0, Into::into), - immediate_transitions: p.immediate_transitions.unwrap_or(false), - block_reward: p.block_reward.map_or_else(Default::default, Into::into), - block_reward_contract_transition: p.block_reward_contract_transition.map_or(0, Into::into), - block_reward_contract: match (p.block_reward_contract_code, p.block_reward_contract_address) { - (Some(code), _) => Some(BlockRewardContract::new_from_code(Arc::new(code.into()))), - (_, Some(address)) => Some(BlockRewardContract::new_from_address(address.into())), - (None, None) => None, - }, - maximum_uncle_count_transition: p.maximum_uncle_count_transition.map_or(0, Into::into), - maximum_uncle_count: p.maximum_uncle_count.map_or(0, Into::into), - empty_steps_transition: p.empty_steps_transition.map_or(u64::max_value(), |n| ::std::cmp::max(n.into(), 1)), - maximum_empty_steps: p.maximum_empty_steps.map_or(0, Into::into), - strict_empty_steps_transition: p.strict_empty_steps_transition.map_or(0, Into::into), - } - } + fn from(p: ethjson::spec::AuthorityRoundParams) -> Self { + let mut step_duration_usize: usize = p.step_duration.into(); + if step_duration_usize > U16_MAX { + step_duration_usize = U16_MAX; + warn!(target: "engine", "step_duration is too high ({}), setting it to {}", step_duration_usize, U16_MAX); + } + AuthorityRoundParams { + step_duration: step_duration_usize as u16, + validators: new_validator_set(p.validators), + start_step: p.start_step.map(Into::into), + validate_score_transition: p.validate_score_transition.map_or(0, Into::into), + validate_step_transition: p.validate_step_transition.map_or(0, Into::into), + immediate_transitions: p.immediate_transitions.unwrap_or(false), + block_reward: p.block_reward.map_or_else(Default::default, Into::into), + block_reward_contract_transition: p + .block_reward_contract_transition + .map_or(0, Into::into), + block_reward_contract: match ( + p.block_reward_contract_code, + p.block_reward_contract_address, + ) { + (Some(code), _) => Some(BlockRewardContract::new_from_code(Arc::new(code.into()))), + (_, Some(address)) => Some(BlockRewardContract::new_from_address(address.into())), + (None, None) => None, + }, + maximum_uncle_count_transition: p.maximum_uncle_count_transition.map_or(0, Into::into), + maximum_uncle_count: p.maximum_uncle_count.map_or(0, Into::into), + empty_steps_transition: p + .empty_steps_transition + .map_or(u64::max_value(), |n| ::std::cmp::max(n.into(), 1)), + maximum_empty_steps: p.maximum_empty_steps.map_or(0, Into::into), + strict_empty_steps_transition: p.strict_empty_steps_transition.map_or(0, Into::into), + } + } } // Helper for managing the step. #[derive(Debug)] struct Step { - calibrate: bool, // whether calibration is enabled. - inner: AtomicUsize, - duration: u16, + calibrate: bool, // whether calibration is enabled. + inner: AtomicUsize, + duration: u16, } impl Step { - fn load(&self) -> u64 { self.inner.load(AtomicOrdering::SeqCst) as u64 } - fn duration_remaining(&self) -> Duration { - let now = unix_now(); - let expected_seconds = self.load() - .checked_add(1) - .and_then(|ctr| ctr.checked_mul(self.duration as u64)) - .map(Duration::from_secs); - - match expected_seconds { - Some(step_end) if step_end > now => step_end - now, - Some(_) => Duration::from_secs(0), - None => { - let ctr = self.load(); - error!(target: "engine", "Step counter is too high: {}, aborting", ctr); - panic!("step counter is too high: {}", ctr) - }, - } - - } - - fn increment(&self) { - use std::usize; - // fetch_add won't panic on overflow but will rather wrap - // around, leading to zero as the step counter, which might - // lead to unexpected situations, so it's better to shut down. - if self.inner.fetch_add(1, AtomicOrdering::SeqCst) == usize::MAX { - error!(target: "engine", "Step counter is too high: {}, aborting", usize::MAX); - panic!("step counter is too high: {}", usize::MAX); - } - - } - - fn calibrate(&self) { - if self.calibrate { - let new_step = unix_now().as_secs() / (self.duration as u64); - self.inner.store(new_step as usize, AtomicOrdering::SeqCst); - } - } - - fn check_future(&self, given: u64) -> Result<(), Option>> { - const REJECTED_STEP_DRIFT: u64 = 4; - - // Verify if the step is correct. - if given <= self.load() { - return Ok(()); - } - - // Make absolutely sure that the given step is incorrect. - self.calibrate(); - let current = self.load(); - - // reject blocks too far in the future - if given > current + REJECTED_STEP_DRIFT { - Err(None) - // wait a bit for blocks in near future - } else if given > current { - let d = self.duration as u64; - Err(Some(OutOfBounds { - min: None, - max: Some(d * current), - found: d * given, - })) - } else { - Ok(()) - } - } + fn load(&self) -> u64 { + self.inner.load(AtomicOrdering::SeqCst) as u64 + } + fn duration_remaining(&self) -> Duration { + let now = unix_now(); + let expected_seconds = self + .load() + .checked_add(1) + .and_then(|ctr| ctr.checked_mul(self.duration as u64)) + .map(Duration::from_secs); + + match expected_seconds { + Some(step_end) if step_end > now => step_end - now, + Some(_) => Duration::from_secs(0), + None => { + let ctr = self.load(); + error!(target: "engine", "Step counter is too high: {}, aborting", ctr); + panic!("step counter is too high: {}", ctr) + } + } + } + + fn increment(&self) { + use std::usize; + // fetch_add won't panic on overflow but will rather wrap + // around, leading to zero as the step counter, which might + // lead to unexpected situations, so it's better to shut down. + if self.inner.fetch_add(1, AtomicOrdering::SeqCst) == usize::MAX { + error!(target: "engine", "Step counter is too high: {}, aborting", usize::MAX); + panic!("step counter is too high: {}", usize::MAX); + } + } + + fn calibrate(&self) { + if self.calibrate { + let new_step = unix_now().as_secs() / (self.duration as u64); + self.inner.store(new_step as usize, AtomicOrdering::SeqCst); + } + } + + fn check_future(&self, given: u64) -> Result<(), Option>> { + const REJECTED_STEP_DRIFT: u64 = 4; + + // Verify if the step is correct. + if given <= self.load() { + return Ok(()); + } + + // Make absolutely sure that the given step is incorrect. + self.calibrate(); + let current = self.load(); + + // reject blocks too far in the future + if given > current + REJECTED_STEP_DRIFT { + Err(None) + // wait a bit for blocks in near future + } else if given > current { + let d = self.duration as u64; + Err(Some(OutOfBounds { + min: None, + max: Some(d * current), + found: d * given, + })) + } else { + Ok(()) + } + } } // Chain scoring: total weight is sqrt(U256::max_value())*height - step fn calculate_score(parent_step: u64, current_step: u64, current_empty_steps: usize) -> U256 { - U256::from(U128::max_value()) + U256::from(parent_step) - U256::from(current_step) + U256::from(current_empty_steps) + U256::from(U128::max_value()) + U256::from(parent_step) - U256::from(current_step) + + U256::from(current_empty_steps) } struct EpochManager { - epoch_transition_hash: H256, - epoch_transition_number: BlockNumber, - finality_checker: RollingFinality, - force: bool, + epoch_transition_hash: H256, + epoch_transition_number: BlockNumber, + finality_checker: RollingFinality, + force: bool, } impl EpochManager { - fn blank() -> Self { - EpochManager { - epoch_transition_hash: H256::default(), - epoch_transition_number: 0, - finality_checker: RollingFinality::blank(Vec::new()), - force: true, - } - } - - // zoom to epoch for given header. returns true if succeeded, false otherwise. - fn zoom_to(&mut self, client: &EngineClient, machine: &EthereumMachine, validators: &ValidatorSet, header: &Header) -> bool { - let last_was_parent = self.finality_checker.subchain_head() == Some(*header.parent_hash()); - - // early exit for current target == chain head, but only if the epochs are - // the same. - if last_was_parent && !self.force { - return true; - } - - self.force = false; - debug!(target: "engine", "Zooming to epoch for block {}", header.hash()); - - // epoch_transition_for can be an expensive call, but in the absence of - // forks it will only need to be called for the block directly after - // epoch transition, in which case it will be O(1) and require a single - // DB lookup. - let last_transition = match client.epoch_transition_for(*header.parent_hash()) { - Some(t) => t, - None => { - // this really should never happen unless the block passed - // hasn't got a parent in the database. - debug!(target: "engine", "No genesis transition found."); - return false; - } - }; - - // extract other epoch set if it's not the same as the last. - if last_transition.block_hash != self.epoch_transition_hash { - let (signal_number, set_proof, _) = destructure_proofs(&last_transition.proof) - .expect("proof produced by this engine; therefore it is valid; qed"); - - trace!(target: "engine", "extracting epoch set for epoch ({}, {}) signalled at #{}", + fn blank() -> Self { + EpochManager { + epoch_transition_hash: H256::default(), + epoch_transition_number: 0, + finality_checker: RollingFinality::blank(Vec::new()), + force: true, + } + } + + // zoom to epoch for given header. returns true if succeeded, false otherwise. + fn zoom_to( + &mut self, + client: &dyn EngineClient, + machine: &EthereumMachine, + validators: &dyn ValidatorSet, + header: &Header, + ) -> bool { + let last_was_parent = self.finality_checker.subchain_head() == Some(*header.parent_hash()); + + // early exit for current target == chain head, but only if the epochs are + // the same. + if last_was_parent && !self.force { + return true; + } + + self.force = false; + debug!(target: "engine", "Zooming to epoch for block {}", header.hash()); + + // epoch_transition_for can be an expensive call, but in the absence of + // forks it will only need to be called for the block directly after + // epoch transition, in which case it will be O(1) and require a single + // DB lookup. + let last_transition = match client.epoch_transition_for(*header.parent_hash()) { + Some(t) => t, + None => { + // this really should never happen unless the block passed + // hasn't got a parent in the database. + debug!(target: "engine", "No genesis transition found."); + return false; + } + }; + + // extract other epoch set if it's not the same as the last. + if last_transition.block_hash != self.epoch_transition_hash { + let (signal_number, set_proof, _) = destructure_proofs(&last_transition.proof) + .expect("proof produced by this engine; therefore it is valid; qed"); + + trace!(target: "engine", "extracting epoch set for epoch ({}, {}) signalled at #{}", last_transition.block_number, last_transition.block_hash, signal_number); - let first = signal_number == 0; - let epoch_set = validators.epoch_set( - first, - machine, - signal_number, // use signal number so multi-set first calculation is correct. - set_proof, - ) - .ok() - .map(|(list, _)| list.into_inner()) - .expect("proof produced by this engine; therefore it is valid; qed"); - - self.finality_checker = RollingFinality::blank(epoch_set); - } - - self.epoch_transition_hash = last_transition.block_hash; - self.epoch_transition_number = last_transition.block_number; - - true - } - - // note new epoch hash. this will force the next block to re-load - // the epoch set - // TODO: optimize and don't require re-loading after epoch change. - fn note_new_epoch(&mut self) { - self.force = true; - } - - /// Get validator set. Zoom to the correct epoch first. - fn validators(&self) -> &SimpleList { - self.finality_checker.validators() - } + let first = signal_number == 0; + let epoch_set = validators + .epoch_set( + first, + machine, + signal_number, // use signal number so multi-set first calculation is correct. + set_proof, + ) + .ok() + .map(|(list, _)| list.into_inner()) + .expect("proof produced by this engine; therefore it is valid; qed"); + + self.finality_checker = RollingFinality::blank(epoch_set); + } + + self.epoch_transition_hash = last_transition.block_hash; + self.epoch_transition_number = last_transition.block_number; + + true + } + + // note new epoch hash. this will force the next block to re-load + // the epoch set + // TODO: optimize and don't require re-loading after epoch change. + fn note_new_epoch(&mut self) { + self.force = true; + } + + /// Get validator set. Zoom to the correct epoch first. + fn validators(&self) -> &SimpleList { + self.finality_checker.validators() + } } /// A message broadcast by authorities when it's their turn to seal a block but there are no @@ -292,90 +317,103 @@ impl EpochManager { /// proof. #[derive(Clone, Debug, PartialEq, Eq)] struct EmptyStep { - signature: H520, - step: u64, - parent_hash: H256, + signature: H520, + step: u64, + parent_hash: H256, } impl PartialOrd for EmptyStep { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } } impl Ord for EmptyStep { - fn cmp(&self, other: &Self) -> cmp::Ordering { - self.step.cmp(&other.step) - .then_with(|| self.parent_hash.cmp(&other.parent_hash)) - .then_with(|| self.signature.cmp(&other.signature)) - } + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.step + .cmp(&other.step) + .then_with(|| self.parent_hash.cmp(&other.parent_hash)) + .then_with(|| self.signature.cmp(&other.signature)) + } } impl EmptyStep { - fn from_sealed(sealed_empty_step: SealedEmptyStep, parent_hash: &H256) -> EmptyStep { - let signature = sealed_empty_step.signature; - let step = sealed_empty_step.step; - let parent_hash = parent_hash.clone(); - EmptyStep { signature, step, parent_hash } - } - - fn verify(&self, validators: &ValidatorSet) -> Result { - let message = keccak(empty_step_rlp(self.step, &self.parent_hash)); - let correct_proposer = step_proposer(validators, &self.parent_hash, self.step); - - ethkey::verify_address(&correct_proposer, &self.signature.into(), &message) - .map_err(|e| e.into()) - } - - fn author(&self) -> Result { - let message = keccak(empty_step_rlp(self.step, &self.parent_hash)); - let public = ethkey::recover(&self.signature.into(), &message)?; - Ok(ethkey::public_to_address(&public)) - } - - fn sealed(&self) -> SealedEmptyStep { - let signature = self.signature; - let step = self.step; - SealedEmptyStep { signature, step } - } + fn from_sealed(sealed_empty_step: SealedEmptyStep, parent_hash: &H256) -> EmptyStep { + let signature = sealed_empty_step.signature; + let step = sealed_empty_step.step; + let parent_hash = parent_hash.clone(); + EmptyStep { + signature, + step, + parent_hash, + } + } + + fn verify(&self, validators: &dyn ValidatorSet) -> Result { + let message = keccak(empty_step_rlp(self.step, &self.parent_hash)); + let correct_proposer = step_proposer(validators, &self.parent_hash, self.step); + + ethkey::verify_address(&correct_proposer, &self.signature.into(), &message) + .map_err(|e| e.into()) + } + + fn author(&self) -> Result { + let message = keccak(empty_step_rlp(self.step, &self.parent_hash)); + let public = ethkey::recover(&self.signature.into(), &message)?; + Ok(ethkey::public_to_address(&public)) + } + + fn sealed(&self) -> SealedEmptyStep { + let signature = self.signature; + let step = self.step; + SealedEmptyStep { signature, step } + } } impl fmt::Display for EmptyStep { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "({:x}, {}, {:x})", self.signature, self.step, self.parent_hash) - } + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!( + f, + "({:x}, {}, {:x})", + self.signature, self.step, self.parent_hash + ) + } } impl Encodable for EmptyStep { - fn rlp_append(&self, s: &mut RlpStream) { - let empty_step_rlp = empty_step_rlp(self.step, &self.parent_hash); - s.begin_list(2) - .append(&self.signature) - .append_raw(&empty_step_rlp, 1); - } + fn rlp_append(&self, s: &mut RlpStream) { + let empty_step_rlp = empty_step_rlp(self.step, &self.parent_hash); + s.begin_list(2) + .append(&self.signature) + .append_raw(&empty_step_rlp, 1); + } } impl Decodable for EmptyStep { - fn decode(rlp: &Rlp) -> Result { - let signature = rlp.val_at(0)?; - let empty_step_rlp = rlp.at(1)?; - - let step = empty_step_rlp.val_at(0)?; - let parent_hash = empty_step_rlp.val_at(1)?; - - Ok(EmptyStep { signature, step, parent_hash }) - } + fn decode(rlp: &Rlp) -> Result { + let signature = rlp.val_at(0)?; + let empty_step_rlp = rlp.at(1)?; + + let step = empty_step_rlp.val_at(0)?; + let parent_hash = empty_step_rlp.val_at(1)?; + + Ok(EmptyStep { + signature, + step, + parent_hash, + }) + } } pub fn empty_step_full_rlp(signature: &H520, empty_step_rlp: &[u8]) -> Vec { - let mut s = RlpStream::new_list(2); - s.append(signature).append_raw(empty_step_rlp, 1); - s.out() + let mut s = RlpStream::new_list(2); + s.append(signature).append_raw(empty_step_rlp, 1); + s.out() } pub fn empty_step_rlp(step: u64, parent_hash: &H256) -> Vec { - let mut s = RlpStream::new_list(2); - s.append(&step).append(parent_hash); - s.out() + let mut s = RlpStream::new_list(2); + s.append(&step).append(parent_hash); + s.out() } /// An empty step message that is included in a seal, the only difference is that it doesn't include @@ -383,475 +421,566 @@ pub fn empty_step_rlp(step: u64, parent_hash: &H256) -> Vec { /// message, which can be reconstructed by using the parent hash of the block in which this sealed /// empty message is included. struct SealedEmptyStep { - signature: H520, - step: u64, + signature: H520, + step: u64, } impl Encodable for SealedEmptyStep { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(2) - .append(&self.signature) - .append(&self.step); - } + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2).append(&self.signature).append(&self.step); + } } impl Decodable for SealedEmptyStep { - fn decode(rlp: &Rlp) -> Result { - let signature = rlp.val_at(0)?; - let step = rlp.val_at(1)?; + fn decode(rlp: &Rlp) -> Result { + let signature = rlp.val_at(0)?; + let step = rlp.val_at(1)?; - Ok(SealedEmptyStep { signature, step }) - } + Ok(SealedEmptyStep { signature, step }) + } } struct PermissionedStep { - inner: Step, - can_propose: AtomicBool, + inner: Step, + can_propose: AtomicBool, } /// Engine using `AuthorityRound` proof-of-authority BFT consensus. pub struct AuthorityRound { - transition_service: IoService<()>, - step: Arc, - client: Arc>>>, - signer: RwLock>>, - validators: Box, - validate_score_transition: u64, - validate_step_transition: u64, - empty_steps: Mutex>, - epoch_manager: Mutex, - immediate_transitions: bool, - block_reward: U256, - block_reward_contract_transition: u64, - block_reward_contract: Option, - maximum_uncle_count_transition: u64, - maximum_uncle_count: usize, - empty_steps_transition: u64, - strict_empty_steps_transition: u64, - maximum_empty_steps: usize, - machine: EthereumMachine, + transition_service: IoService<()>, + step: Arc, + client: Arc>>>, + signer: RwLock>>, + validators: Box, + validate_score_transition: u64, + validate_step_transition: u64, + empty_steps: Mutex>, + epoch_manager: Mutex, + immediate_transitions: bool, + block_reward: U256, + block_reward_contract_transition: u64, + block_reward_contract: Option, + maximum_uncle_count_transition: u64, + maximum_uncle_count: usize, + empty_steps_transition: u64, + strict_empty_steps_transition: u64, + maximum_empty_steps: usize, + machine: EthereumMachine, } // header-chain validator. struct EpochVerifier { - step: Arc, - subchain_validators: SimpleList, - empty_steps_transition: u64, + step: Arc, + subchain_validators: SimpleList, + empty_steps_transition: u64, } impl super::EpochVerifier for EpochVerifier { - fn verify_light(&self, header: &Header) -> Result<(), Error> { - // Validate the timestamp - verify_timestamp(&self.step.inner, header_step(header, self.empty_steps_transition)?)?; - // always check the seal since it's fast. - // nothing heavier to do. - verify_external(header, &self.subchain_validators, self.empty_steps_transition) - } - - fn check_finality_proof(&self, proof: &[u8]) -> Option> { - let mut finality_checker = RollingFinality::blank(self.subchain_validators.clone().into_inner()); - let mut finalized = Vec::new(); - - let headers: Vec
= Rlp::new(proof).as_list().ok()?; - - { - let mut push_header = |parent_header: &Header, header: Option<&Header>| { - // ensure all headers have correct number of seal fields so we can `verify_external` - // and get `empty_steps` without panic. - if parent_header.seal().len() != header_expected_seal_fields(parent_header, self.empty_steps_transition) { - return None - } - if header.iter().any(|h| h.seal().len() != header_expected_seal_fields(h, self.empty_steps_transition)) { - return None - } - - // `verify_external` checks that signature is correct and author == signer. - verify_external(parent_header, &self.subchain_validators, self.empty_steps_transition).ok()?; - - let mut signers = match header { - Some(header) => header_empty_steps_signers(header, self.empty_steps_transition).ok()?, - _ => Vec::new(), - }; - signers.push(*parent_header.author()); - - let newly_finalized = finality_checker.push_hash(parent_header.hash(), signers).ok()?; - finalized.extend(newly_finalized); - - Some(()) - }; - - for window in headers.windows(2) { - push_header(&window[0], Some(&window[1]))?; - } - - if let Some(last) = headers.last() { - push_header(last, None)?; - } - } - - if finalized.is_empty() { None } else { Some(finalized) } - } + fn verify_light(&self, header: &Header) -> Result<(), Error> { + // Validate the timestamp + verify_timestamp( + &self.step.inner, + header_step(header, self.empty_steps_transition)?, + )?; + // always check the seal since it's fast. + // nothing heavier to do. + verify_external( + header, + &self.subchain_validators, + self.empty_steps_transition, + ) + } + + fn check_finality_proof(&self, proof: &[u8]) -> Option> { + let mut finality_checker = + RollingFinality::blank(self.subchain_validators.clone().into_inner()); + let mut finalized = Vec::new(); + + let headers: Vec
= Rlp::new(proof).as_list().ok()?; + + { + let mut push_header = |parent_header: &Header, header: Option<&Header>| { + // ensure all headers have correct number of seal fields so we can `verify_external` + // and get `empty_steps` without panic. + if parent_header.seal().len() + != header_expected_seal_fields(parent_header, self.empty_steps_transition) + { + return None; + } + if header.iter().any(|h| { + h.seal().len() != header_expected_seal_fields(h, self.empty_steps_transition) + }) { + return None; + } + + // `verify_external` checks that signature is correct and author == signer. + verify_external( + parent_header, + &self.subchain_validators, + self.empty_steps_transition, + ) + .ok()?; + + let mut signers = match header { + Some(header) => { + header_empty_steps_signers(header, self.empty_steps_transition).ok()? + } + _ => Vec::new(), + }; + signers.push(*parent_header.author()); + + let newly_finalized = finality_checker + .push_hash(parent_header.hash(), signers) + .ok()?; + finalized.extend(newly_finalized); + + Some(()) + }; + + for window in headers.windows(2) { + push_header(&window[0], Some(&window[1]))?; + } + + if let Some(last) = headers.last() { + push_header(last, None)?; + } + } + + if finalized.is_empty() { + None + } else { + Some(finalized) + } + } } fn header_seal_hash(header: &Header, empty_steps_rlp: Option<&[u8]>) -> H256 { - match empty_steps_rlp { - Some(empty_steps_rlp) => { - let mut message = header.bare_hash().to_vec(); - message.extend_from_slice(empty_steps_rlp); - keccak(message) - }, - None => { - header.bare_hash() - }, - } + match empty_steps_rlp { + Some(empty_steps_rlp) => { + let mut message = header.bare_hash().to_vec(); + message.extend_from_slice(empty_steps_rlp); + keccak(message) + } + None => header.bare_hash(), + } } fn header_expected_seal_fields(header: &Header, empty_steps_transition: u64) -> usize { - if header.number() >= empty_steps_transition { - 3 - } else { - 2 - } + if header.number() >= empty_steps_transition { + 3 + } else { + 2 + } } fn header_step(header: &Header, empty_steps_transition: u64) -> Result { - Rlp::new(&header.seal().get(0).unwrap_or_else(|| + Rlp::new(&header.seal().get(0).unwrap_or_else(|| panic!("was either checked with verify_block_basic or is genesis; has {} fields; qed (Make sure the spec file has a correct genesis seal)", header_expected_seal_fields(header, empty_steps_transition)) )) .as_val() } -fn header_signature(header: &Header, empty_steps_transition: u64) -> Result { - Rlp::new(&header.seal().get(1).unwrap_or_else(|| - panic!("was checked with verify_block_basic; has {} fields; qed", - header_expected_seal_fields(header, empty_steps_transition)) - )) - .as_val::().map(Into::into) +fn header_signature( + header: &Header, + empty_steps_transition: u64, +) -> Result { + Rlp::new(&header.seal().get(1).unwrap_or_else(|| { + panic!( + "was checked with verify_block_basic; has {} fields; qed", + header_expected_seal_fields(header, empty_steps_transition) + ) + })) + .as_val::() + .map(Into::into) } // extracts the raw empty steps vec from the header seal. should only be called when there are 3 fields in the seal // (i.e. header.number() >= self.empty_steps_transition) fn header_empty_steps_raw(header: &Header) -> &[u8] { - header.seal().get(2).expect("was checked with verify_block_basic; has 3 fields; qed") + header + .seal() + .get(2) + .expect("was checked with verify_block_basic; has 3 fields; qed") } // extracts the empty steps from the header seal. should only be called when there are 3 fields in the seal // (i.e. header.number() >= self.empty_steps_transition). fn header_empty_steps(header: &Header) -> Result, ::rlp::DecoderError> { - let empty_steps = Rlp::new(header_empty_steps_raw(header)).as_list::()?; - Ok(empty_steps.into_iter().map(|s| EmptyStep::from_sealed(s, header.parent_hash())).collect()) + let empty_steps = Rlp::new(header_empty_steps_raw(header)).as_list::()?; + Ok(empty_steps + .into_iter() + .map(|s| EmptyStep::from_sealed(s, header.parent_hash())) + .collect()) } // gets the signers of empty step messages for the given header, does not include repeated signers -fn header_empty_steps_signers(header: &Header, empty_steps_transition: u64) -> Result, Error> { - if header.number() >= empty_steps_transition { - let mut signers = HashSet::new(); - for empty_step in header_empty_steps(header)? { - signers.insert(empty_step.author()?); - } - - Ok(Vec::from_iter(signers.into_iter())) - } else { - Ok(Vec::new()) - } +fn header_empty_steps_signers( + header: &Header, + empty_steps_transition: u64, +) -> Result, Error> { + if header.number() >= empty_steps_transition { + let mut signers = HashSet::new(); + for empty_step in header_empty_steps(header)? { + signers.insert(empty_step.author()?); + } + + Ok(Vec::from_iter(signers.into_iter())) + } else { + Ok(Vec::new()) + } } -fn step_proposer(validators: &ValidatorSet, bh: &H256, step: u64) -> Address { - let proposer = validators.get(bh, step as usize); - trace!(target: "engine", "Fetched proposer for step {}: {}", step, proposer); - proposer +fn step_proposer(validators: &dyn ValidatorSet, bh: &H256, step: u64) -> Address { + let proposer = validators.get(bh, step as usize); + trace!(target: "engine", "Fetched proposer for step {}: {}", step, proposer); + proposer } -fn is_step_proposer(validators: &ValidatorSet, bh: &H256, step: u64, address: &Address) -> bool { - step_proposer(validators, bh, step) == *address +fn is_step_proposer( + validators: &dyn ValidatorSet, + bh: &H256, + step: u64, + address: &Address, +) -> bool { + step_proposer(validators, bh, step) == *address } fn verify_timestamp(step: &Step, header_step: u64) -> Result<(), BlockError> { - match step.check_future(header_step) { - Err(None) => { - trace!(target: "engine", "verify_timestamp: block from the future"); - Err(BlockError::InvalidSeal.into()) - }, - Err(Some(oob)) => { - // NOTE This error might be returned only in early stage of verification (Stage 1). - // Returning it further won't recover the sync process. - trace!(target: "engine", "verify_timestamp: block too early"); - - let now = SystemTime::now(); - let found = now.checked_add(Duration::from_secs(oob.found)).ok_or(BlockError::TimestampOverflow)?; - let max = oob.max.and_then(|m| now.checked_add(Duration::from_secs(m))); - let min = oob.min.and_then(|m| now.checked_add(Duration::from_secs(m))); - - let new_oob = OutOfBounds { min, max, found }; - - Err(BlockError::TemporarilyInvalid(new_oob).into()) - }, - Ok(_) => Ok(()), - } + match step.check_future(header_step) { + Err(None) => { + trace!(target: "engine", "verify_timestamp: block from the future"); + Err(BlockError::InvalidSeal.into()) + } + Err(Some(oob)) => { + // NOTE This error might be returned only in early stage of verification (Stage 1). + // Returning it further won't recover the sync process. + trace!(target: "engine", "verify_timestamp: block too early"); + + let found = CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::from_secs(oob.found)) + .ok_or(BlockError::TimestampOverflow)?; + let max = oob + .max + .and_then(|m| CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::from_secs(m))); + let min = oob + .min + .and_then(|m| CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::from_secs(m))); + + let new_oob = OutOfBounds { min, max, found }; + + Err(BlockError::TemporarilyInvalid(new_oob).into()) + } + Ok(_) => Ok(()), + } } -fn verify_external(header: &Header, validators: &ValidatorSet, empty_steps_transition: u64) -> Result<(), Error> { - let header_step = header_step(header, empty_steps_transition)?; - - let proposer_signature = header_signature(header, empty_steps_transition)?; - let correct_proposer = validators.get(header.parent_hash(), header_step as usize); - let is_invalid_proposer = *header.author() != correct_proposer || { - let empty_steps_rlp = if header.number() >= empty_steps_transition { - Some(header_empty_steps_raw(header)) - } else { - None - }; - - let header_seal_hash = header_seal_hash(header, empty_steps_rlp); - !ethkey::verify_address(&correct_proposer, &proposer_signature, &header_seal_hash)? - }; - - if is_invalid_proposer { - trace!(target: "engine", "verify_block_external: bad proposer for step: {}", header_step); - Err(EngineError::NotProposer(Mismatch { expected: correct_proposer, found: *header.author() }))? - } else { - Ok(()) - } +fn verify_external( + header: &Header, + validators: &dyn ValidatorSet, + empty_steps_transition: u64, +) -> Result<(), Error> { + let header_step = header_step(header, empty_steps_transition)?; + + let proposer_signature = header_signature(header, empty_steps_transition)?; + let correct_proposer = validators.get(header.parent_hash(), header_step as usize); + let is_invalid_proposer = *header.author() != correct_proposer || { + let empty_steps_rlp = if header.number() >= empty_steps_transition { + Some(header_empty_steps_raw(header)) + } else { + None + }; + + let header_seal_hash = header_seal_hash(header, empty_steps_rlp); + !ethkey::verify_address(&correct_proposer, &proposer_signature, &header_seal_hash)? + }; + + if is_invalid_proposer { + trace!(target: "engine", "verify_block_external: bad proposer for step: {}", header_step); + Err(EngineError::NotProposer(Mismatch { + expected: correct_proposer, + found: *header.author(), + }))? + } else { + Ok(()) + } } fn combine_proofs(signal_number: BlockNumber, set_proof: &[u8], finality_proof: &[u8]) -> Vec { - let mut stream = ::rlp::RlpStream::new_list(3); - stream.append(&signal_number).append(&set_proof).append(&finality_proof); - stream.out() + let mut stream = ::rlp::RlpStream::new_list(3); + stream + .append(&signal_number) + .append(&set_proof) + .append(&finality_proof); + stream.out() } - fn destructure_proofs(combined: &[u8]) -> Result<(BlockNumber, &[u8], &[u8]), Error> { - let rlp = Rlp::new(combined); - Ok(( - rlp.at(0)?.as_val()?, - rlp.at(1)?.data()?, - rlp.at(2)?.data()?, - )) + let rlp = Rlp::new(combined); + Ok((rlp.at(0)?.as_val()?, rlp.at(1)?.data()?, rlp.at(2)?.data()?)) } trait AsMillis { - fn as_millis(&self) -> u64; + fn as_millis(&self) -> u64; } impl AsMillis for Duration { - fn as_millis(&self) -> u64 { - self.as_secs() * 1_000 + (self.subsec_nanos() / 1_000_000) as u64 - } + fn as_millis(&self) -> u64 { + self.as_secs() * 1_000 + (self.subsec_nanos() / 1_000_000) as u64 + } } // A type for storing owned or borrowed data that has a common type. // Useful for returning either a borrow or owned data from a function. enum CowLike<'a, A: 'a + ?Sized, B> { - Borrowed(&'a A), - Owned(B), + Borrowed(&'a A), + Owned(B), } -impl<'a, A: ?Sized, B> Deref for CowLike<'a, A, B> where B: AsRef { - type Target = A; - fn deref(&self) -> &A { - match self { - CowLike::Borrowed(b) => b, - CowLike::Owned(o) => o.as_ref(), - } - } +impl<'a, A: ?Sized, B> Deref for CowLike<'a, A, B> +where + B: AsRef, +{ + type Target = A; + fn deref(&self) -> &A { + match self { + CowLike::Borrowed(b) => b, + CowLike::Owned(o) => o.as_ref(), + } + } } impl AuthorityRound { - /// Create a new instance of AuthorityRound engine. - pub fn new(our_params: AuthorityRoundParams, machine: EthereumMachine) -> Result, Error> { - if our_params.step_duration == 0 { - error!(target: "engine", "Authority Round step duration can't be zero, aborting"); - panic!("authority_round: step duration can't be zero") - } - let should_timeout = our_params.start_step.is_none(); - let initial_step = our_params.start_step.unwrap_or_else(|| (unix_now().as_secs() / (our_params.step_duration as u64))); - let engine = Arc::new( - AuthorityRound { - transition_service: IoService::<()>::start()?, - step: Arc::new(PermissionedStep { - inner: Step { - inner: AtomicUsize::new(initial_step as usize), - calibrate: our_params.start_step.is_none(), - duration: our_params.step_duration, - }, - can_propose: AtomicBool::new(true), - }), - client: Arc::new(RwLock::new(None)), - signer: RwLock::new(None), - validators: our_params.validators, - validate_score_transition: our_params.validate_score_transition, - validate_step_transition: our_params.validate_step_transition, - empty_steps: Default::default(), - epoch_manager: Mutex::new(EpochManager::blank()), - immediate_transitions: our_params.immediate_transitions, - block_reward: our_params.block_reward, - block_reward_contract_transition: our_params.block_reward_contract_transition, - block_reward_contract: our_params.block_reward_contract, - maximum_uncle_count_transition: our_params.maximum_uncle_count_transition, - maximum_uncle_count: our_params.maximum_uncle_count, - empty_steps_transition: our_params.empty_steps_transition, - maximum_empty_steps: our_params.maximum_empty_steps, - strict_empty_steps_transition: our_params.strict_empty_steps_transition, - machine: machine, - }); - - // Do not initialize timeouts for tests. - if should_timeout { - let handler = TransitionHandler { - step: engine.step.clone(), - client: engine.client.clone(), - }; - engine.transition_service.register_handler(Arc::new(handler))?; - } - Ok(engine) - } - - // fetch correct validator set for epoch at header, taking into account - // finality of previous transitions. - fn epoch_set<'a>(&'a self, header: &Header) -> Result<(CowLike, BlockNumber), Error> { - Ok(if self.immediate_transitions { - (CowLike::Borrowed(&*self.validators), header.number()) - } else { - let mut epoch_manager = self.epoch_manager.lock(); - let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) { - Some(client) => client, - None => { - debug!(target: "engine", "Unable to verify sig: missing client ref."); - return Err(EngineError::RequiresClient.into()) - } - }; - - if !epoch_manager.zoom_to(&*client, &self.machine, &*self.validators, header) { - debug!(target: "engine", "Unable to zoom to epoch."); - return Err(EngineError::RequiresClient.into()) - } - - (CowLike::Owned(epoch_manager.validators().clone()), epoch_manager.epoch_transition_number) - }) - } - - fn empty_steps(&self, from_step: u64, to_step: u64, parent_hash: H256) -> Vec { - let from = EmptyStep { - step: from_step + 1, - parent_hash, - signature: Default::default(), - }; - let to = EmptyStep { - step: to_step, - parent_hash: Default::default(), - signature: Default::default(), - }; - - if from >= to { - return vec![]; - } - - self.empty_steps.lock() - .range(from..to) - .filter(|e| e.parent_hash == parent_hash) - .cloned() - .collect() - } - - fn clear_empty_steps(&self, step: u64) { - // clear old `empty_steps` messages - let mut empty_steps = self.empty_steps.lock(); - *empty_steps = empty_steps.split_off(&EmptyStep { - step: step + 1, - parent_hash: Default::default(), - signature: Default::default(), - }); - } - - fn handle_empty_step_message(&self, empty_step: EmptyStep) { - self.empty_steps.lock().insert(empty_step); - } - - fn generate_empty_step(&self, parent_hash: &H256) { - let step = self.step.inner.load(); - let empty_step_rlp = empty_step_rlp(step, parent_hash); - - if let Ok(signature) = self.sign(keccak(&empty_step_rlp)).map(Into::into) { - let message_rlp = empty_step_full_rlp(&signature, &empty_step_rlp); - - let parent_hash = *parent_hash; - let empty_step = EmptyStep { signature, step, parent_hash }; - - trace!(target: "engine", "broadcasting empty step message: {:?}", empty_step); - self.broadcast_message(message_rlp); - self.handle_empty_step_message(empty_step); - - } else { - warn!(target: "engine", "generate_empty_step: FAIL: accounts secret key unavailable"); - } - } - - fn broadcast_message(&self, message: Vec) { - if let Some(ref weak) = *self.client.read() { - if let Some(c) = weak.upgrade() { - c.broadcast_consensus_message(message); - } - } - } - - fn report_skipped(&self, header: &Header, current_step: u64, parent_step: u64, validators: &ValidatorSet, set_number: u64) { - // we're building on top of the genesis block so don't report any skipped steps - if header.number() == 1 { - return; - } - - if let (true, Some(me)) = (current_step > parent_step + 1, self.signer.read().as_ref().map(|s| s.address())) { - debug!(target: "engine", "Author {} built block with step gap. current step: {}, parent step: {}", + /// Create a new instance of AuthorityRound engine. + pub fn new( + our_params: AuthorityRoundParams, + machine: EthereumMachine, + ) -> Result, Error> { + if our_params.step_duration == 0 { + error!(target: "engine", "Authority Round step duration can't be zero, aborting"); + panic!("authority_round: step duration can't be zero") + } + let should_timeout = our_params.start_step.is_none(); + let initial_step = our_params + .start_step + .unwrap_or_else(|| (unix_now().as_secs() / (our_params.step_duration as u64))); + let engine = Arc::new(AuthorityRound { + transition_service: IoService::<()>::start()?, + step: Arc::new(PermissionedStep { + inner: Step { + inner: AtomicUsize::new(initial_step as usize), + calibrate: our_params.start_step.is_none(), + duration: our_params.step_duration, + }, + can_propose: AtomicBool::new(true), + }), + client: Arc::new(RwLock::new(None)), + signer: RwLock::new(None), + validators: our_params.validators, + validate_score_transition: our_params.validate_score_transition, + validate_step_transition: our_params.validate_step_transition, + empty_steps: Default::default(), + epoch_manager: Mutex::new(EpochManager::blank()), + immediate_transitions: our_params.immediate_transitions, + block_reward: our_params.block_reward, + block_reward_contract_transition: our_params.block_reward_contract_transition, + block_reward_contract: our_params.block_reward_contract, + maximum_uncle_count_transition: our_params.maximum_uncle_count_transition, + maximum_uncle_count: our_params.maximum_uncle_count, + empty_steps_transition: our_params.empty_steps_transition, + maximum_empty_steps: our_params.maximum_empty_steps, + strict_empty_steps_transition: our_params.strict_empty_steps_transition, + machine: machine, + }); + + // Do not initialize timeouts for tests. + if should_timeout { + let handler = TransitionHandler { + step: engine.step.clone(), + client: engine.client.clone(), + }; + engine + .transition_service + .register_handler(Arc::new(handler))?; + } + Ok(engine) + } + + // fetch correct validator set for epoch at header, taking into account + // finality of previous transitions. + fn epoch_set<'a>( + &'a self, + header: &Header, + ) -> Result<(CowLike, BlockNumber), Error> { + Ok(if self.immediate_transitions { + (CowLike::Borrowed(&*self.validators), header.number()) + } else { + let mut epoch_manager = self.epoch_manager.lock(); + let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) { + Some(client) => client, + None => { + debug!(target: "engine", "Unable to verify sig: missing client ref."); + return Err(EngineError::RequiresClient.into()); + } + }; + + if !epoch_manager.zoom_to(&*client, &self.machine, &*self.validators, header) { + debug!(target: "engine", "Unable to zoom to epoch."); + return Err(EngineError::RequiresClient.into()); + } + + ( + CowLike::Owned(epoch_manager.validators().clone()), + epoch_manager.epoch_transition_number, + ) + }) + } + + fn empty_steps(&self, from_step: u64, to_step: u64, parent_hash: H256) -> Vec { + let from = EmptyStep { + step: from_step + 1, + parent_hash, + signature: Default::default(), + }; + let to = EmptyStep { + step: to_step, + parent_hash: Default::default(), + signature: Default::default(), + }; + + if from >= to { + return vec![]; + } + + self.empty_steps + .lock() + .range(from..to) + .filter(|e| e.parent_hash == parent_hash) + .cloned() + .collect() + } + + fn clear_empty_steps(&self, step: u64) { + // clear old `empty_steps` messages + let mut empty_steps = self.empty_steps.lock(); + *empty_steps = empty_steps.split_off(&EmptyStep { + step: step + 1, + parent_hash: Default::default(), + signature: Default::default(), + }); + } + + fn handle_empty_step_message(&self, empty_step: EmptyStep) { + self.empty_steps.lock().insert(empty_step); + } + + fn generate_empty_step(&self, parent_hash: &H256) { + let step = self.step.inner.load(); + let empty_step_rlp = empty_step_rlp(step, parent_hash); + + if let Ok(signature) = self.sign(keccak(&empty_step_rlp)).map(Into::into) { + let message_rlp = empty_step_full_rlp(&signature, &empty_step_rlp); + + let parent_hash = *parent_hash; + let empty_step = EmptyStep { + signature, + step, + parent_hash, + }; + + trace!(target: "engine", "broadcasting empty step message: {:?}", empty_step); + self.broadcast_message(message_rlp); + self.handle_empty_step_message(empty_step); + } else { + warn!(target: "engine", "generate_empty_step: FAIL: accounts secret key unavailable"); + } + } + + fn broadcast_message(&self, message: Vec) { + if let Some(ref weak) = *self.client.read() { + if let Some(c) = weak.upgrade() { + c.broadcast_consensus_message(message); + } + } + } + + fn report_skipped( + &self, + header: &Header, + current_step: u64, + parent_step: u64, + validators: &dyn ValidatorSet, + set_number: u64, + ) { + // we're building on top of the genesis block so don't report any skipped steps + if header.number() == 1 { + return; + } + + if let (true, Some(me)) = ( + current_step > parent_step + 1, + self.signer.read().as_ref().map(|s| s.address()), + ) { + debug!(target: "engine", "Author {} built block with step gap. current step: {}, parent step: {}", header.author(), current_step, parent_step); - let mut reported = HashSet::new(); - for step in parent_step + 1..current_step { - let skipped_primary = step_proposer(validators, header.parent_hash(), step); - // Do not report this signer. - if skipped_primary != me { - // Stop reporting once validators start repeating. - if !reported.insert(skipped_primary) { break; } - self.validators.report_benign(&skipped_primary, set_number, header.number()); - } - } - } - } - - // Returns the hashes of all ancestor blocks that are finalized by the given `chain_head`. - fn build_finality(&self, chain_head: &Header, ancestry: &mut Iterator) -> Vec { - if self.immediate_transitions { return Vec::new() } - - let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) { - Some(client) => client, - None => { - warn!(target: "engine", "Unable to apply ancestry actions: missing client ref."); - return Vec::new(); - } - }; - - let mut epoch_manager = self.epoch_manager.lock(); - if !epoch_manager.zoom_to(&*client, &self.machine, &*self.validators, chain_head) { - return Vec::new(); - } - - if epoch_manager.finality_checker.subchain_head() != Some(*chain_head.parent_hash()) { - // build new finality checker from unfinalized ancestry of chain head, not including chain head itself yet. - trace!(target: "finality", "Building finality up to parent of {} ({})", - chain_head.hash(), chain_head.parent_hash()); + let mut reported = HashSet::new(); + for step in parent_step + 1..current_step { + let skipped_primary = step_proposer(validators, header.parent_hash(), step); + // Do not report this signer. + if skipped_primary != me { + // Stop reporting once validators start repeating. + if !reported.insert(skipped_primary) { + break; + } + self.validators + .report_benign(&skipped_primary, set_number, header.number()); + } + } + } + } + + // Returns the hashes of all ancestor blocks that are finalized by the given `chain_head`. + fn build_finality( + &self, + chain_head: &Header, + ancestry: &mut dyn Iterator, + ) -> Vec { + if self.immediate_transitions { + return Vec::new(); + } + + let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) { + Some(client) => client, + None => { + warn!(target: "engine", "Unable to apply ancestry actions: missing client ref."); + return Vec::new(); + } + }; - // the empty steps messages in a header signal approval of the - // parent header. - let mut parent_empty_steps_signers = match header_empty_steps_signers(&chain_head, self.empty_steps_transition) { - Ok(empty_step_signers) => empty_step_signers, - Err(_) => { - warn!(target: "finality", "Failed to get empty step signatures from block {}", chain_head.hash()); - return Vec::new(); - } - }; + let mut epoch_manager = self.epoch_manager.lock(); + if !epoch_manager.zoom_to(&*client, &self.machine, &*self.validators, chain_head) { + return Vec::new(); + } - let epoch_transition_hash = epoch_manager.epoch_transition_hash; - let ancestry_iter = ancestry.map(|header| { + if epoch_manager.finality_checker.subchain_head() != Some(*chain_head.parent_hash()) { + // build new finality checker from unfinalized ancestry of chain head, not including chain head itself yet. + trace!(target: "finality", "Building finality up to parent of {} ({})", + chain_head.hash(), chain_head.parent_hash()); + + // the empty steps messages in a header signal approval of the + // parent header. + let mut parent_empty_steps_signers = match header_empty_steps_signers( + &chain_head, + self.empty_steps_transition, + ) { + Ok(empty_step_signers) => empty_step_signers, + Err(_) => { + warn!(target: "finality", "Failed to get empty step signatures from block {}", chain_head.hash()); + return Vec::new(); + } + }; + + let epoch_transition_hash = epoch_manager.epoch_transition_hash; + let ancestry_iter = ancestry.map(|header| { let mut signers = vec![*header.author()]; signers.extend(parent_empty_steps_signers.drain(..)); @@ -871,953 +1000,1093 @@ impl AuthorityRound { .while_some() .take_while(|&(h, _)| h != epoch_transition_hash); - if let Err(e) = epoch_manager.finality_checker.build_ancestry_subchain(ancestry_iter) { - debug!(target: "engine", "inconsistent validator set within epoch: {:?}", e); - return Vec::new(); - } - } - - let finalized = epoch_manager.finality_checker.push_hash(chain_head.hash(), vec![*chain_head.author()]); - finalized.unwrap_or_default() - } + if let Err(e) = epoch_manager + .finality_checker + .build_ancestry_subchain(ancestry_iter) + { + debug!(target: "engine", "inconsistent validator set within epoch: {:?}", e); + return Vec::new(); + } + } + + let finalized = epoch_manager + .finality_checker + .push_hash(chain_head.hash(), vec![*chain_head.author()]); + finalized.unwrap_or_default() + } } fn unix_now() -> Duration { - UNIX_EPOCH.elapsed().expect("Valid time has to be set in your system.") + UNIX_EPOCH + .elapsed() + .expect("Valid time has to be set in your system.") } struct TransitionHandler { - step: Arc, - client: Arc>>>, + step: Arc, + client: Arc>>>, } const ENGINE_TIMEOUT_TOKEN: TimerToken = 23; impl IoHandler<()> for TransitionHandler { - fn initialize(&self, io: &IoContext<()>) { - let remaining = AsMillis::as_millis(&self.step.inner.duration_remaining()); - io.register_timer_once(ENGINE_TIMEOUT_TOKEN, Duration::from_millis(remaining)) - .unwrap_or_else(|e| warn!(target: "engine", "Failed to start consensus step timer: {}.", e)) - } - - fn timeout(&self, io: &IoContext<()>, timer: TimerToken) { - if timer == ENGINE_TIMEOUT_TOKEN { - // NOTE we might be lagging by couple of steps in case the timeout - // has not been called fast enough. - // Make sure to advance up to the actual step. - while AsMillis::as_millis(&self.step.inner.duration_remaining()) == 0 { - self.step.inner.increment(); - self.step.can_propose.store(true, AtomicOrdering::SeqCst); - if let Some(ref weak) = *self.client.read() { - if let Some(c) = weak.upgrade() { - c.update_sealing(); - } - } - } - - let next_run_at = AsMillis::as_millis(&self.step.inner.duration_remaining()) >> 2; - io.register_timer_once(ENGINE_TIMEOUT_TOKEN, Duration::from_millis(next_run_at)) - .unwrap_or_else(|e| warn!(target: "engine", "Failed to restart consensus step timer: {}.", e)) - } - } + fn initialize(&self, io: &IoContext<()>) { + let remaining = AsMillis::as_millis(&self.step.inner.duration_remaining()); + io.register_timer_once(ENGINE_TIMEOUT_TOKEN, Duration::from_millis(remaining)) + .unwrap_or_else( + |e| warn!(target: "engine", "Failed to start consensus step timer: {}.", e), + ) + } + + fn timeout(&self, io: &IoContext<()>, timer: TimerToken) { + if timer == ENGINE_TIMEOUT_TOKEN { + // NOTE we might be lagging by couple of steps in case the timeout + // has not been called fast enough. + // Make sure to advance up to the actual step. + while AsMillis::as_millis(&self.step.inner.duration_remaining()) == 0 { + self.step.inner.increment(); + self.step.can_propose.store(true, AtomicOrdering::SeqCst); + if let Some(ref weak) = *self.client.read() { + if let Some(c) = weak.upgrade() { + c.update_sealing(ForceUpdateSealing::No); + } + } + } + + let next_run_at = AsMillis::as_millis(&self.step.inner.duration_remaining()) >> 2; + io.register_timer_once(ENGINE_TIMEOUT_TOKEN, Duration::from_millis(next_run_at)) + .unwrap_or_else( + |e| warn!(target: "engine", "Failed to restart consensus step timer: {}.", e), + ) + } + } } impl Engine for AuthorityRound { - fn name(&self) -> &str { "AuthorityRound" } - - fn machine(&self) -> &EthereumMachine { &self.machine } - - /// Three fields - consensus step and the corresponding proposer signature, and a list of empty - /// step messages (which should be empty if no steps are skipped) - fn seal_fields(&self, header: &Header) -> usize { - header_expected_seal_fields(header, self.empty_steps_transition) - } - - fn step(&self) { - self.step.inner.increment(); - self.step.can_propose.store(true, AtomicOrdering::SeqCst); - if let Some(ref weak) = *self.client.read() { - if let Some(c) = weak.upgrade() { - c.update_sealing(); - } - } - } - - /// Additional engine-specific information for the user/developer concerning `header`. - fn extra_info(&self, header: &Header) -> BTreeMap { - if header.seal().len() < header_expected_seal_fields(header, self.empty_steps_transition) { - return BTreeMap::default(); - } - - let step = header_step(header, self.empty_steps_transition).as_ref() - .map(ToString::to_string) - .unwrap_or_default(); - let signature = header_signature(header, self.empty_steps_transition).as_ref() - .map(ToString::to_string) - .unwrap_or_default(); - - let mut info = map![ - "step".into() => step, - "signature".into() => signature - ]; - - if header.number() >= self.empty_steps_transition { - let empty_steps = - if let Ok(empty_steps) = header_empty_steps(header).as_ref() { - format!("[{}]", - empty_steps.iter().fold( - "".to_string(), - |acc, e| if acc.len() > 0 { acc + ","} else { acc } + &e.to_string())) + fn name(&self) -> &str { + "AuthorityRound" + } + + fn machine(&self) -> &EthereumMachine { + &self.machine + } + + /// Three fields - consensus step and the corresponding proposer signature, and a list of empty + /// step messages (which should be empty if no steps are skipped) + fn seal_fields(&self, header: &Header) -> usize { + header_expected_seal_fields(header, self.empty_steps_transition) + } + + fn step(&self) { + self.step.inner.increment(); + self.step.can_propose.store(true, AtomicOrdering::SeqCst); + if let Some(ref weak) = *self.client.read() { + if let Some(c) = weak.upgrade() { + c.update_sealing(ForceUpdateSealing::No); + } + } + } + + /// Additional engine-specific information for the user/developer concerning `header`. + fn extra_info(&self, header: &Header) -> BTreeMap { + if header.seal().len() < header_expected_seal_fields(header, self.empty_steps_transition) { + return BTreeMap::default(); + } + + let step = header_step(header, self.empty_steps_transition) + .as_ref() + .map(ToString::to_string) + .unwrap_or_default(); + let signature = header_signature(header, self.empty_steps_transition) + .as_ref() + .map(ToString::to_string) + .unwrap_or_default(); + + let mut info = map![ + "step".into() => step, + "signature".into() => signature + ]; + + if header.number() >= self.empty_steps_transition { + let empty_steps = if let Ok(empty_steps) = header_empty_steps(header).as_ref() { + format!( + "[{}]", + empty_steps + .iter() + .fold("".to_string(), |acc, e| if acc.len() > 0 { + acc + "," + } else { + acc + } + &e.to_string()) + ) + } else { + "".into() + }; + + info.insert("emptySteps".into(), empty_steps); + } + + info + } + + fn maximum_uncle_count(&self, block: BlockNumber) -> usize { + if block >= self.maximum_uncle_count_transition { + self.maximum_uncle_count + } else { + // fallback to default value + 2 + } + } + + fn populate_from_parent(&self, header: &mut Header, parent: &Header) { + let parent_step = header_step(parent, self.empty_steps_transition) + .expect("Header has been verified; qed"); + let current_step = self.step.inner.load(); + + let current_empty_steps_len = if header.number() >= self.empty_steps_transition { + self.empty_steps(parent_step, current_step, parent.hash()) + .len() + } else { + 0 + }; - } else { - "".into() - }; - - info.insert("emptySteps".into(), empty_steps); - } - - info - } - - fn maximum_uncle_count(&self, block: BlockNumber) -> usize { - if block >= self.maximum_uncle_count_transition { - self.maximum_uncle_count - } else { - // fallback to default value - 2 - } - } - - fn populate_from_parent(&self, header: &mut Header, parent: &Header) { - let parent_step = header_step(parent, self.empty_steps_transition).expect("Header has been verified; qed"); - let current_step = self.step.inner.load(); - - let current_empty_steps_len = if header.number() >= self.empty_steps_transition { - self.empty_steps(parent_step, current_step, parent.hash()).len() - } else { - 0 - }; - - let score = calculate_score(parent_step, current_step, current_empty_steps_len); - header.set_difficulty(score); - } - - fn seals_internally(&self) -> Option { - // TODO: accept a `&Call` here so we can query the validator set. - Some(self.signer.read().is_some()) - } - - fn handle_message(&self, rlp: &[u8]) -> Result<(), EngineError> { - fn fmt_err(x: T) -> EngineError { - EngineError::MalformedMessage(format!("{:?}", x)) - } - - let rlp = Rlp::new(rlp); - let empty_step: EmptyStep = rlp.as_val().map_err(fmt_err)?;; - - if empty_step.verify(&*self.validators).unwrap_or(false) { - if self.step.inner.check_future(empty_step.step).is_ok() { - trace!(target: "engine", "handle_message: received empty step message {:?}", empty_step); - self.handle_empty_step_message(empty_step); - } else { - trace!(target: "engine", "handle_message: empty step message from the future {:?}", empty_step); - } - } else { - trace!(target: "engine", "handle_message: received invalid step message {:?}", empty_step); - }; - - Ok(()) - } - - /// Attempt to seal the block internally. - /// - /// This operation is synchronous and may (quite reasonably) not be available, in which case - /// `Seal::None` will be returned. - fn generate_seal(&self, block: &ExecutedBlock, parent: &Header) -> Seal { - // first check to avoid generating signature most of the time - // (but there's still a race to the `compare_and_swap`) - if !self.step.can_propose.load(AtomicOrdering::SeqCst) { - trace!(target: "engine", "Aborting seal generation. Can't propose."); - return Seal::None; - } - - let header = &block.header; - let parent_step = header_step(parent, self.empty_steps_transition) - .expect("Header has been verified; qed"); - - let step = self.step.inner.load(); - - // filter messages from old and future steps and different parents - let empty_steps = if header.number() >= self.empty_steps_transition { - self.empty_steps(parent_step.into(), step.into(), *header.parent_hash()) - } else { - Vec::new() - }; - - let expected_diff = calculate_score(parent_step, step.into(), empty_steps.len().into()); - - if header.difficulty() != &expected_diff { - debug!(target: "engine", "Aborting seal generation. The step or empty_steps have changed in the meantime. {:?} != {:?}", - header.difficulty(), expected_diff); - return Seal::None; - } - - if parent_step > step.into() { - warn!(target: "engine", "Aborting seal generation for invalid step: {} > {}", parent_step, step); - return Seal::None; - } - - let (validators, set_number) = match self.epoch_set(header) { - Err(err) => { - warn!(target: "engine", "Unable to generate seal: {}", err); - return Seal::None; - }, - Ok(ok) => ok, - }; - - if is_step_proposer(&*validators, header.parent_hash(), step, header.author()) { - // this is guarded against by `can_propose` unless the block was signed - // on the same step (implies same key) and on a different node. - if parent_step == step { - warn!("Attempted to seal block on the same step as parent. Is this authority sealing with more than one node?"); - return Seal::None; - } - - // if there are no transactions to include in the block, we don't seal and instead broadcast a signed - // `EmptyStep(step, parent_hash)` message. If we exceed the maximum amount of `empty_step` rounds we proceed - // with the seal. - if header.number() >= self.empty_steps_transition && - block.transactions.is_empty() && - empty_steps.len() < self.maximum_empty_steps { - - if self.step.can_propose.compare_and_swap(true, false, AtomicOrdering::SeqCst) { - self.generate_empty_step(header.parent_hash()); - } + let score = calculate_score(parent_step, current_step, current_empty_steps_len); + header.set_difficulty(score); + } + + fn seals_internally(&self) -> Option { + // TODO: accept a `&Call` here so we can query the validator set. + Some(self.signer.read().is_some()) + } + + fn handle_message(&self, rlp: &[u8]) -> Result<(), EngineError> { + fn fmt_err(x: T) -> EngineError { + EngineError::MalformedMessage(format!("{:?}", x)) + } + + let rlp = Rlp::new(rlp); + let empty_step: EmptyStep = rlp.as_val().map_err(fmt_err)?; + + if empty_step.verify(&*self.validators).unwrap_or(false) { + if self.step.inner.check_future(empty_step.step).is_ok() { + trace!(target: "engine", "handle_message: received empty step message {:?}", empty_step); + self.handle_empty_step_message(empty_step); + } else { + trace!(target: "engine", "handle_message: empty step message from the future {:?}", empty_step); + } + } else { + trace!(target: "engine", "handle_message: received invalid step message {:?}", empty_step); + }; - return Seal::None; - } - - let empty_steps_rlp = if header.number() >= self.empty_steps_transition { - let empty_steps: Vec<_> = empty_steps.iter().map(|e| e.sealed()).collect(); - Some(::rlp::encode_list(&empty_steps)) - } else { - None - }; - - if let Ok(signature) = self.sign(header_seal_hash(header, empty_steps_rlp.as_ref().map(|e| &**e))) { - trace!(target: "engine", "generate_seal: Issuing a block for step {}.", step); - - // only issue the seal if we were the first to reach the compare_and_swap. - if self.step.can_propose.compare_and_swap(true, false, AtomicOrdering::SeqCst) { - // we can drop all accumulated empty step messages that are - // older than the parent step since we're including them in - // the seal - self.clear_empty_steps(parent_step); - - // report any skipped primaries between the parent block and - // the block we're sealing, unless we have empty steps enabled - if header.number() < self.empty_steps_transition { - self.report_skipped(header, step, parent_step, &*validators, set_number); - } - - let mut fields = vec![ - encode(&step), - encode(&(&H520::from(signature) as &[u8])), - ]; - - if let Some(empty_steps_rlp) = empty_steps_rlp { - fields.push(empty_steps_rlp); - } - - return Seal::Regular(fields); - } - } else { - warn!(target: "engine", "generate_seal: FAIL: Accounts secret key unavailable."); - } - } else { - trace!(target: "engine", "generate_seal: {} not a proposer for step {}.", - header.author(), step); - } - - Seal::None - } - - fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> { - Ok(()) - } - - fn on_new_block( - &self, - block: &mut ExecutedBlock, - epoch_begin: bool, - _ancestry: &mut Iterator, - ) -> Result<(), Error> { - // with immediate transitions, we don't use the epoch mechanism anyway. - // the genesis is always considered an epoch, but we ignore it intentionally. - if self.immediate_transitions || !epoch_begin { return Ok(()) } - - // genesis is never a new block, but might as well check. - let header = block.header.clone(); - let first = header.number() == 0; - - let mut call = |to, data| { - let result = self.machine.execute_as_system( - block, - to, - U256::max_value(), // unbounded gas? maybe make configurable. - Some(data), - ); - - result.map_err(|e| format!("{}", e)) - }; - - self.validators.on_epoch_begin(first, &header, &mut call) - } - - /// Apply the block reward on finalisation of the block. - fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { - let mut beneficiaries = Vec::new(); - if block.header.number() >= self.empty_steps_transition { - let empty_steps = if block.header.seal().is_empty() { - // this is a new block, calculate rewards based on the empty steps messages we have accumulated - let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) { - Some(client) => client, - None => { - debug!(target: "engine", "Unable to close block: missing client ref."); - return Err(EngineError::RequiresClient.into()) - }, - }; - - let parent = client.block_header(::client::BlockId::Hash(*block.header.parent_hash())) - .expect("hash is from parent; parent header must exist; qed") - .decode()?; - - let parent_step = header_step(&parent, self.empty_steps_transition)?; - let current_step = self.step.inner.load(); - self.empty_steps(parent_step.into(), current_step.into(), parent.hash()) - } else { - // we're verifying a block, extract empty steps from the seal - header_empty_steps(&block.header)? - }; - - for empty_step in empty_steps { - let author = empty_step.author()?; - beneficiaries.push((author, RewardKind::EmptyStep)); - } - } - - let author = *block.header.author(); - beneficiaries.push((author, RewardKind::Author)); - - let rewards: Vec<_> = match self.block_reward_contract { - Some(ref c) if block.header.number() >= self.block_reward_contract_transition => { - let mut call = super::default_system_or_code_call(&self.machine, block); - - let rewards = c.reward(&beneficiaries, &mut call)?; - rewards.into_iter().map(|(author, amount)| (author, RewardKind::External, amount)).collect() - }, - _ => { - beneficiaries.into_iter().map(|(author, reward_kind)| (author, reward_kind, self.block_reward)).collect() - }, - }; - - block_reward::apply_block_rewards(&rewards, block, &self.machine) - } - - /// Check the number of seal fields. - fn verify_block_basic(&self, header: &Header) -> Result<(), Error> { - if header.number() >= self.validate_score_transition && *header.difficulty() >= U256::from(U128::max_value()) { - return Err(From::from(BlockError::DifficultyOutOfBounds( - OutOfBounds { min: None, max: Some(U256::from(U128::max_value())), found: *header.difficulty() } - ))); - } - - match verify_timestamp(&self.step.inner, header_step(header, self.empty_steps_transition)?) { - Err(BlockError::InvalidSeal) => { - // This check runs in Phase 1 where there is no guarantee that the parent block is - // already imported, therefore the call to `epoch_set` may fail. In that case we - // won't report the misbehavior but this is not a concern because: - // - Only authorities can report and it's expected that they'll be up-to-date and - // importing, therefore the parent header will most likely be available - // - Even if you are an authority that is syncing the chain, the contract will most - // likely ignore old reports - // - This specific check is only relevant if you're importing (since it checks - // against wall clock) - if let Ok((_, set_number)) = self.epoch_set(header) { - self.validators.report_benign(header.author(), set_number, header.number()); - } + Ok(()) + } + + /// Attempt to seal the block internally. + /// + /// This operation is synchronous and may (quite reasonably) not be available, in which case + /// `Seal::None` will be returned. + fn generate_seal(&self, block: &ExecutedBlock, parent: &Header) -> Seal { + // first check to avoid generating signature most of the time + // (but there's still a race to the `compare_and_swap`) + if !self.step.can_propose.load(AtomicOrdering::SeqCst) { + trace!(target: "engine", "Aborting seal generation. Can't propose."); + return Seal::None; + } + + let header = &block.header; + let parent_step = header_step(parent, self.empty_steps_transition) + .expect("Header has been verified; qed"); + + let step = self.step.inner.load(); + + // filter messages from old and future steps and different parents + let empty_steps = if header.number() >= self.empty_steps_transition { + self.empty_steps(parent_step.into(), step.into(), *header.parent_hash()) + } else { + Vec::new() + }; - Err(BlockError::InvalidSeal.into()) - } - Err(e) => Err(e.into()), - Ok(()) => Ok(()), - } - } - - /// Do the step and gas limit validation. - fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> { - let step = header_step(header, self.empty_steps_transition)?; - let parent_step = header_step(parent, self.empty_steps_transition)?; - - let (validators, set_number) = self.epoch_set(header)?; - - // Ensure header is from the step after parent. - if step == parent_step - || (header.number() >= self.validate_step_transition && step <= parent_step) { - trace!(target: "engine", "Multiple blocks proposed for step {}.", parent_step); - - self.validators.report_malicious(header.author(), set_number, header.number(), Default::default()); - Err(EngineError::DoubleVote(*header.author()))?; - } - - // If empty step messages are enabled we will validate the messages in the seal, missing messages are not - // reported as there's no way to tell whether the empty step message was never sent or simply not included. - let empty_steps_len = if header.number() >= self.empty_steps_transition { - let validate_empty_steps = || -> Result { - let strict_empty_steps = header.number() >= self.strict_empty_steps_transition; - let empty_steps = header_empty_steps(header)?; - let empty_steps_len = empty_steps.len(); - let mut prev_empty_step = 0; - - for empty_step in empty_steps { - if empty_step.step <= parent_step || empty_step.step >= step { - Err(EngineError::InsufficientProof( - format!("empty step proof for invalid step: {:?}", empty_step.step)))?; - } - - if empty_step.parent_hash != *header.parent_hash() { - Err(EngineError::InsufficientProof( - format!("empty step proof for invalid parent hash: {:?}", empty_step.parent_hash)))?; - } - - if !empty_step.verify(&*validators).unwrap_or(false) { - Err(EngineError::InsufficientProof( - format!("invalid empty step proof: {:?}", empty_step)))?; - } - - if strict_empty_steps { - if empty_step.step <= prev_empty_step { - Err(EngineError::InsufficientProof(format!( - "{} empty step: {:?}", - if empty_step.step == prev_empty_step { "duplicate" } else { "unordered" }, - empty_step - )))?; - } - - prev_empty_step = empty_step.step; - } - } + let expected_diff = calculate_score(parent_step, step.into(), empty_steps.len().into()); - Ok(empty_steps_len) - }; - - match validate_empty_steps() { - Ok(len) => len, - Err(err) => { - self.validators.report_benign(header.author(), set_number, header.number()); - return Err(err); - }, - } - } else { - self.report_skipped(header, step, parent_step, &*validators, set_number); - - 0 - }; - - if header.number() >= self.validate_score_transition { - let expected_difficulty = calculate_score(parent_step.into(), step.into(), empty_steps_len.into()); - if header.difficulty() != &expected_difficulty { - return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty().clone() }))); - } - } - - Ok(()) - } - - // Check the validators. - fn verify_block_external(&self, header: &Header) -> Result<(), Error> { - let (validators, set_number) = self.epoch_set(header)?; - - // verify signature against fixed list, but reports should go to the - // contract itself. - let res = verify_external(header, &*validators, self.empty_steps_transition); - match res { - Err(Error(ErrorKind::Engine(EngineError::NotProposer(_)), _)) => { - self.validators.report_benign(header.author(), set_number, header.number()); - }, - Ok(_) => { - // we can drop all accumulated empty step messages that are older than this header's step - let header_step = header_step(header, self.empty_steps_transition)?; - self.clear_empty_steps(header_step.into()); - }, - _ => {}, - } - res - } - - fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result, String> { - self.validators.genesis_epoch_data(header, call) - .map(|set_proof| combine_proofs(0, &set_proof, &[])) - } - - fn signals_epoch_end(&self, header: &Header, aux: AuxiliaryData) - -> super::EpochChange - { - if self.immediate_transitions { return super::EpochChange::No } - - let first = header.number() == 0; - self.validators.signals_epoch_end(first, header, aux) - } - - fn is_epoch_end_light( - &self, - chain_head: &Header, - chain: &super::Headers
, - transition_store: &super::PendingTransitionStore, - ) -> Option> { - // epochs only matter if we want to support light clients. - if self.immediate_transitions { return None } - - let epoch_transition_hash = { - let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) { - Some(client) => client, - None => { - warn!(target: "engine", "Unable to check for epoch end: missing client ref."); - return None; - } - }; + if header.difficulty() != &expected_diff { + debug!(target: "engine", "Aborting seal generation. The step or empty_steps have changed in the meantime. {:?} != {:?}", + header.difficulty(), expected_diff); + return Seal::None; + } + + if parent_step > step.into() { + warn!(target: "engine", "Aborting seal generation for invalid step: {} > {}", parent_step, step); + return Seal::None; + } + + let (validators, set_number) = match self.epoch_set(header) { + Err(err) => { + warn!(target: "engine", "Unable to generate seal: {}", err); + return Seal::None; + } + Ok(ok) => ok, + }; - let mut epoch_manager = self.epoch_manager.lock(); - if !epoch_manager.zoom_to(&*client, &self.machine, &*self.validators, chain_head) { - return None; - } + if is_step_proposer(&*validators, header.parent_hash(), step, header.author()) { + // this is guarded against by `can_propose` unless the block was signed + // on the same step (implies same key) and on a different node. + if parent_step == step { + warn!("Attempted to seal block on the same step as parent. Is this authority sealing with more than one node?"); + return Seal::None; + } + + // if there are no transactions to include in the block, we don't seal and instead broadcast a signed + // `EmptyStep(step, parent_hash)` message. If we exceed the maximum amount of `empty_step` rounds we proceed + // with the seal. + if header.number() >= self.empty_steps_transition + && block.transactions.is_empty() + && empty_steps.len() < self.maximum_empty_steps + { + if self + .step + .can_propose + .compare_and_swap(true, false, AtomicOrdering::SeqCst) + { + self.generate_empty_step(header.parent_hash()); + } + + return Seal::None; + } + + let empty_steps_rlp = if header.number() >= self.empty_steps_transition { + let empty_steps: Vec<_> = empty_steps.iter().map(|e| e.sealed()).collect(); + Some(::rlp::encode_list(&empty_steps)) + } else { + None + }; + + if let Ok(signature) = self.sign(header_seal_hash( + header, + empty_steps_rlp.as_ref().map(|e| &**e), + )) { + trace!(target: "engine", "generate_seal: Issuing a block for step {}.", step); + + // only issue the seal if we were the first to reach the compare_and_swap. + if self + .step + .can_propose + .compare_and_swap(true, false, AtomicOrdering::SeqCst) + { + // we can drop all accumulated empty step messages that are + // older than the parent step since we're including them in + // the seal + self.clear_empty_steps(parent_step); + + // report any skipped primaries between the parent block and + // the block we're sealing, unless we have empty steps enabled + if header.number() < self.empty_steps_transition { + self.report_skipped(header, step, parent_step, &*validators, set_number); + } + + let mut fields = + vec![encode(&step), encode(&(&H520::from(signature) as &[u8]))]; + + if let Some(empty_steps_rlp) = empty_steps_rlp { + fields.push(empty_steps_rlp); + } + + return Seal::Regular(fields); + } + } else { + warn!(target: "engine", "generate_seal: FAIL: Accounts secret key unavailable."); + } + } else { + trace!(target: "engine", "generate_seal: {} not a proposer for step {}.", + header.author(), step); + } + + Seal::None + } + + fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> { + Ok(()) + } + + fn on_new_block( + &self, + block: &mut ExecutedBlock, + epoch_begin: bool, + _ancestry: &mut dyn Iterator, + ) -> Result<(), Error> { + // with immediate transitions, we don't use the epoch mechanism anyway. + // the genesis is always considered an epoch, but we ignore it intentionally. + if self.immediate_transitions || !epoch_begin { + return Ok(()); + } + + // genesis is never a new block, but might as well check. + let header = block.header.clone(); + let first = header.number() == 0; + + let mut call = |to, data| { + let result = self.machine.execute_as_system( + block, + to, + U256::max_value(), // unbounded gas? maybe make configurable. + Some(data), + ); + + result.map_err(|e| format!("{}", e)) + }; - epoch_manager.epoch_transition_hash - }; + self.validators.on_epoch_begin(first, &header, &mut call) + } + + /// Apply the block reward on finalisation of the block. + fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { + let mut beneficiaries = Vec::new(); + if block.header.number() >= self.empty_steps_transition { + let empty_steps = if block.header.seal().is_empty() { + // this is a new block, calculate rewards based on the empty steps messages we have accumulated + let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) { + Some(client) => client, + None => { + debug!(target: "engine", "Unable to close block: missing client ref."); + return Err(EngineError::RequiresClient.into()); + } + }; + + let parent = client + .block_header(::client::BlockId::Hash(*block.header.parent_hash())) + .expect("hash is from parent; parent header must exist; qed") + .decode()?; + + let parent_step = header_step(&parent, self.empty_steps_transition)?; + let current_step = self.step.inner.load(); + self.empty_steps(parent_step.into(), current_step.into(), parent.hash()) + } else { + // we're verifying a block, extract empty steps from the seal + header_empty_steps(&block.header)? + }; + + for empty_step in empty_steps { + let author = empty_step.author()?; + beneficiaries.push((author, RewardKind::EmptyStep)); + } + } + + let author = *block.header.author(); + beneficiaries.push((author, RewardKind::Author)); + + let rewards: Vec<_> = match self.block_reward_contract { + Some(ref c) if block.header.number() >= self.block_reward_contract_transition => { + let mut call = super::default_system_or_code_call(&self.machine, block); + + let rewards = c.reward(&beneficiaries, &mut call)?; + rewards + .into_iter() + .map(|(author, amount)| (author, RewardKind::External, amount)) + .collect() + } + _ => beneficiaries + .into_iter() + .map(|(author, reward_kind)| (author, reward_kind, self.block_reward)) + .collect(), + }; - let mut hash = *chain_head.parent_hash(); + block_reward::apply_block_rewards(&rewards, block, &self.machine) + } + + /// Check the number of seal fields. + fn verify_block_basic(&self, header: &Header) -> Result<(), Error> { + if header.number() >= self.validate_score_transition + && *header.difficulty() >= U256::from(U128::max_value()) + { + return Err(From::from(BlockError::DifficultyOutOfBounds(OutOfBounds { + min: None, + max: Some(U256::from(U128::max_value())), + found: *header.difficulty(), + }))); + } + + match verify_timestamp( + &self.step.inner, + header_step(header, self.empty_steps_transition)?, + ) { + Err(BlockError::InvalidSeal) => { + // This check runs in Phase 1 where there is no guarantee that the parent block is + // already imported, therefore the call to `epoch_set` may fail. In that case we + // won't report the misbehavior but this is not a concern because: + // - Only authorities can report and it's expected that they'll be up-to-date and + // importing, therefore the parent header will most likely be available + // - Even if you are an authority that is syncing the chain, the contract will most + // likely ignore old reports + // - This specific check is only relevant if you're importing (since it checks + // against wall clock) + if let Ok((_, set_number)) = self.epoch_set(header) { + self.validators + .report_benign(header.author(), set_number, header.number()); + } + + Err(BlockError::InvalidSeal.into()) + } + Err(e) => Err(e.into()), + Ok(()) => Ok(()), + } + } + + /// Do the step and gas limit validation. + fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> { + let step = header_step(header, self.empty_steps_transition)?; + let parent_step = header_step(parent, self.empty_steps_transition)?; + + let (validators, set_number) = self.epoch_set(header)?; + + // Ensure header is from the step after parent. + if step == parent_step + || (header.number() >= self.validate_step_transition && step <= parent_step) + { + trace!(target: "engine", "Multiple blocks proposed for step {}.", parent_step); + + self.validators.report_malicious( + header.author(), + set_number, + header.number(), + Default::default(), + ); + Err(EngineError::DoubleVote(*header.author()))?; + } + + // If empty step messages are enabled we will validate the messages in the seal, missing messages are not + // reported as there's no way to tell whether the empty step message was never sent or simply not included. + let empty_steps_len = if header.number() >= self.empty_steps_transition { + let validate_empty_steps = || -> Result { + let strict_empty_steps = header.number() >= self.strict_empty_steps_transition; + let empty_steps = header_empty_steps(header)?; + let empty_steps_len = empty_steps.len(); + let mut prev_empty_step = 0; + + for empty_step in empty_steps { + if empty_step.step <= parent_step || empty_step.step >= step { + Err(EngineError::InsufficientProof(format!( + "empty step proof for invalid step: {:?}", + empty_step.step + )))?; + } + + if empty_step.parent_hash != *header.parent_hash() { + Err(EngineError::InsufficientProof(format!( + "empty step proof for invalid parent hash: {:?}", + empty_step.parent_hash + )))?; + } + + if !empty_step.verify(&*validators).unwrap_or(false) { + Err(EngineError::InsufficientProof(format!( + "invalid empty step proof: {:?}", + empty_step + )))?; + } + + if strict_empty_steps { + if empty_step.step <= prev_empty_step { + Err(EngineError::InsufficientProof(format!( + "{} empty step: {:?}", + if empty_step.step == prev_empty_step { + "duplicate" + } else { + "unordered" + }, + empty_step + )))?; + } + + prev_empty_step = empty_step.step; + } + } + + Ok(empty_steps_len) + }; + + match validate_empty_steps() { + Ok(len) => len, + Err(err) => { + self.validators + .report_benign(header.author(), set_number, header.number()); + return Err(err); + } + } + } else { + self.report_skipped(header, step, parent_step, &*validators, set_number); + + 0 + }; - let mut ancestry = itertools::repeat_call(move || { - chain(hash).and_then(|header| { - if header.number() == 0 { return None } - hash = *header.parent_hash(); - Some(header) - }) - }) - .while_some() - .take_while(|header| header.hash() != epoch_transition_hash); - - let finalized = self.build_finality(chain_head, &mut ancestry); - - self.is_epoch_end(chain_head, &finalized, chain, transition_store) - } - - fn is_epoch_end( - &self, - chain_head: &Header, - finalized: &[H256], - chain: &super::Headers
, - transition_store: &super::PendingTransitionStore, - ) -> Option> { - // epochs only matter if we want to support light clients. - if self.immediate_transitions { return None } - - let first = chain_head.number() == 0; - - // Apply transitions that don't require finality and should be enacted immediately (e.g from chain spec) - if let Some(change) = self.validators.is_epoch_end(first, chain_head) { - info!(target: "engine", "Immediately applying validator set change signalled at block {}", chain_head.number()); - self.epoch_manager.lock().note_new_epoch(); - let change = combine_proofs(chain_head.number(), &change, &[]); - return Some(change) - } - - // check transition store for pending transitions against recently finalized blocks - for finalized_hash in finalized { - if let Some(pending) = transition_store(*finalized_hash) { - // walk the chain backwards from current head until finalized_hash - // to construct transition proof. author == ec_recover(sig) known - // since the blocks are in the DB. - let mut hash = chain_head.hash(); - let mut finality_proof: Vec<_> = itertools::repeat_call(move || { - chain(hash).and_then(|header| { - hash = *header.parent_hash(); - if header.number() == 0 { None } - else { Some(header) } - }) - }) - .while_some() - .take_while(|h| h.hash() != *finalized_hash) - .collect(); - - let finalized_header = if *finalized_hash == chain_head.hash() { - // chain closure only stores ancestry, but the chain head is also unfinalized. - chain_head.clone() - } else { - chain(*finalized_hash) - .expect("header is finalized; finalized headers must exist in the chain; qed") - }; - - let signal_number = finalized_header.number(); - info!(target: "engine", "Applying validator set change signalled at block {}", signal_number); - - finality_proof.push(finalized_header); - finality_proof.reverse(); - - let finality_proof = ::rlp::encode_list(&finality_proof); - - self.epoch_manager.lock().note_new_epoch(); - - // We turn off can_propose here because upon validator set change there can - // be two valid proposers for a single step: one from the old set and - // one from the new. - // - // This way, upon encountering an epoch change, the proposer from the - // new set will be forced to wait until the next step to avoid sealing a - // block that breaks the invariant that the parent's step < the block's step. - self.step.can_propose.store(false, AtomicOrdering::SeqCst); - return Some(combine_proofs(signal_number, &pending.proof, &*finality_proof)); - } - } - - None - } - - fn epoch_verifier<'a>(&self, _header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a, EthereumMachine> { - let (signal_number, set_proof, finality_proof) = match destructure_proofs(proof) { - Ok(x) => x, - Err(e) => return ConstructedVerifier::Err(e), - }; - - let first = signal_number == 0; - match self.validators.epoch_set(first, &self.machine, signal_number, set_proof) { - Ok((list, finalize)) => { - let verifier = Box::new(EpochVerifier { - step: self.step.clone(), - subchain_validators: list, - empty_steps_transition: self.empty_steps_transition, - }); - - match finalize { - Some(finalize) => ConstructedVerifier::Unconfirmed(verifier, finality_proof, finalize), - None => ConstructedVerifier::Trusted(verifier), - } - } - Err(e) => ConstructedVerifier::Err(e), - } - } - - fn register_client(&self, client: Weak) { - *self.client.write() = Some(client.clone()); - self.validators.register_client(client); - } - - fn set_signer(&self, signer: Box) { - *self.signer.write() = Some(signer); - } - - fn sign(&self, hash: H256) -> Result { - Ok(self.signer.read() - .as_ref() - .ok_or(ethkey::Error::InvalidAddress)? - .sign(hash)? - ) - } - - fn snapshot_components(&self) -> Option> { - if self.immediate_transitions { - None - } else { - Some(Box::new(::snapshot::PoaSnapshot)) - } - } - - fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice { - super::total_difficulty_fork_choice(new, current) - } - - fn ancestry_actions(&self, header: &Header, ancestry: &mut Iterator) -> Vec { - let finalized = self.build_finality( - header, - &mut ancestry.take_while(|e| !e.is_finalized).map(|e| e.header), - ); - - if !finalized.is_empty() { - debug!(target: "finality", "Finalizing blocks: {:?}", finalized); - } - - finalized.into_iter().map(AncestryAction::MarkFinalized).collect() - } + if header.number() >= self.validate_score_transition { + let expected_difficulty = + calculate_score(parent_step.into(), step.into(), empty_steps_len.into()); + if header.difficulty() != &expected_difficulty { + return Err(From::from(BlockError::InvalidDifficulty(Mismatch { + expected: expected_difficulty, + found: header.difficulty().clone(), + }))); + } + } + + Ok(()) + } + + // Check the validators. + fn verify_block_external(&self, header: &Header) -> Result<(), Error> { + let (validators, set_number) = self.epoch_set(header)?; + + // verify signature against fixed list, but reports should go to the + // contract itself. + let res = verify_external(header, &*validators, self.empty_steps_transition); + match res { + Err(Error(ErrorKind::Engine(EngineError::NotProposer(_)), _)) => { + self.validators + .report_benign(header.author(), set_number, header.number()); + } + Ok(_) => { + // we can drop all accumulated empty step messages that are older than this header's step + let header_step = header_step(header, self.empty_steps_transition)?; + self.clear_empty_steps(header_step.into()); + } + _ => {} + } + res + } + + fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result, String> { + self.validators + .genesis_epoch_data(header, call) + .map(|set_proof| combine_proofs(0, &set_proof, &[])) + } + + fn signals_epoch_end( + &self, + header: &Header, + aux: AuxiliaryData, + ) -> super::EpochChange { + if self.immediate_transitions { + return super::EpochChange::No; + } + + let first = header.number() == 0; + self.validators.signals_epoch_end(first, header, aux) + } + + fn is_epoch_end( + &self, + chain_head: &Header, + finalized: &[H256], + chain: &super::Headers
, + transition_store: &super::PendingTransitionStore, + ) -> Option> { + // epochs only matter if we want to support light clients. + if self.immediate_transitions { + return None; + } + + let first = chain_head.number() == 0; + + // Apply transitions that don't require finality and should be enacted immediately (e.g from chain spec) + if let Some(change) = self.validators.is_epoch_end(first, chain_head) { + info!(target: "engine", "Immediately applying validator set change signalled at block {}", chain_head.number()); + self.epoch_manager.lock().note_new_epoch(); + let change = combine_proofs(chain_head.number(), &change, &[]); + return Some(change); + } + + // check transition store for pending transitions against recently finalized blocks + for finalized_hash in finalized { + if let Some(pending) = transition_store(*finalized_hash) { + // walk the chain backwards from current head until finalized_hash + // to construct transition proof. author == ec_recover(sig) known + // since the blocks are in the DB. + let mut hash = chain_head.hash(); + let mut finality_proof: Vec<_> = itertools::repeat_call(move || { + chain(hash).and_then(|header| { + hash = *header.parent_hash(); + if header.number() == 0 { + None + } else { + Some(header) + } + }) + }) + .while_some() + .take_while(|h| h.hash() != *finalized_hash) + .collect(); + + let finalized_header = if *finalized_hash == chain_head.hash() { + // chain closure only stores ancestry, but the chain head is also unfinalized. + chain_head.clone() + } else { + chain(*finalized_hash).expect( + "header is finalized; finalized headers must exist in the chain; qed", + ) + }; + + let signal_number = finalized_header.number(); + info!(target: "engine", "Applying validator set change signalled at block {}", signal_number); + + finality_proof.push(finalized_header); + finality_proof.reverse(); + + let finality_proof = ::rlp::encode_list(&finality_proof); + + self.epoch_manager.lock().note_new_epoch(); + + // We turn off can_propose here because upon validator set change there can + // be two valid proposers for a single step: one from the old set and + // one from the new. + // + // This way, upon encountering an epoch change, the proposer from the + // new set will be forced to wait until the next step to avoid sealing a + // block that breaks the invariant that the parent's step < the block's step. + self.step.can_propose.store(false, AtomicOrdering::SeqCst); + return Some(combine_proofs( + signal_number, + &pending.proof, + &*finality_proof, + )); + } + } + + None + } + + fn epoch_verifier<'a>( + &self, + _header: &Header, + proof: &'a [u8], + ) -> ConstructedVerifier<'a, EthereumMachine> { + let (signal_number, set_proof, finality_proof) = match destructure_proofs(proof) { + Ok(x) => x, + Err(e) => return ConstructedVerifier::Err(e), + }; + + let first = signal_number == 0; + match self + .validators + .epoch_set(first, &self.machine, signal_number, set_proof) + { + Ok((list, finalize)) => { + let verifier = Box::new(EpochVerifier { + step: self.step.clone(), + subchain_validators: list, + empty_steps_transition: self.empty_steps_transition, + }); + + match finalize { + Some(finalize) => { + ConstructedVerifier::Unconfirmed(verifier, finality_proof, finalize) + } + None => ConstructedVerifier::Trusted(verifier), + } + } + Err(e) => ConstructedVerifier::Err(e), + } + } + + fn register_client(&self, client: Weak) { + *self.client.write() = Some(client.clone()); + self.validators.register_client(client); + } + + fn set_signer(&self, signer: Box) { + *self.signer.write() = Some(signer); + } + + fn sign(&self, hash: H256) -> Result { + Ok(self + .signer + .read() + .as_ref() + .ok_or(ethkey::Error::InvalidAddress)? + .sign(hash)?) + } + + fn snapshot_components(&self) -> Option> { + if self.immediate_transitions { + None + } else { + Some(Box::new(::snapshot::PoaSnapshot)) + } + } + + fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice { + super::total_difficulty_fork_choice(new, current) + } + + fn ancestry_actions( + &self, + header: &Header, + ancestry: &mut dyn Iterator, + ) -> Vec { + let finalized = self.build_finality( + header, + &mut ancestry.take_while(|e| !e.is_finalized).map(|e| e.header), + ); + + if !finalized.is_empty() { + debug!(target: "finality", "Finalizing blocks: {:?}", finalized); + } + + finalized + .into_iter() + .map(AncestryAction::MarkFinalized) + .collect() + } } #[cfg(test)] mod tests { - use std::collections::BTreeMap; - use std::sync::Arc; - use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; - use hash::keccak; - use accounts::AccountProvider; - use ethereum_types::{Address, H520, H256, U256}; - use ethkey::Signature; - use types::header::Header; - use rlp::encode; - use block::*; - use test_helpers::{ - generate_dummy_client_with_spec, get_temp_state_db, - TestNotify - }; - use spec::Spec; - use types::transaction::{Action, Transaction}; - use engines::{Seal, Engine, EngineError, EthEngine}; - use engines::validator_set::{TestSet, SimpleList}; - use error::{Error, ErrorKind}; - use super::{AuthorityRoundParams, AuthorityRound, EmptyStep, SealedEmptyStep, calculate_score}; - - fn aura(f: F) -> Arc where - F: FnOnce(&mut AuthorityRoundParams), - { - let mut params = AuthorityRoundParams { - step_duration: 1, - start_step: Some(1), - validators: Box::new(TestSet::default()), - validate_score_transition: 0, - validate_step_transition: 0, - immediate_transitions: true, - maximum_uncle_count_transition: 0, - maximum_uncle_count: 0, - empty_steps_transition: u64::max_value(), - maximum_empty_steps: 0, - block_reward: Default::default(), - block_reward_contract_transition: 0, - block_reward_contract: Default::default(), - strict_empty_steps_transition: 0, - }; - - // mutate aura params - f(&mut params); - - // create engine - let mut c_params = ::spec::CommonParams::default(); - c_params.gas_limit_bound_divisor = 5.into(); - let machine = ::machine::EthereumMachine::regular(c_params, Default::default()); - AuthorityRound::new(params, machine).unwrap() - } - - #[test] - fn has_valid_metadata() { - let engine = Spec::new_test_round().engine; - assert!(!engine.name().is_empty()); - } - - #[test] - fn can_return_schedule() { - let engine = Spec::new_test_round().engine; - let schedule = engine.schedule(10000000); - - assert!(schedule.stack_limit > 0); - } - - #[test] - fn can_do_signature_verification_fail() { - let engine = Spec::new_test_round().engine; - let mut header: Header = Header::default(); - header.set_seal(vec![encode(&H520::default())]); - - let verify_result = engine.verify_block_external(&header); - assert!(verify_result.is_err()); - } - - #[test] - fn generates_seal_and_does_not_double_propose() { - let tap = Arc::new(AccountProvider::transient_provider()); - let addr1 = tap.insert_account(keccak("1").into(), &"1".into()).unwrap(); - let addr2 = tap.insert_account(keccak("2").into(), &"2".into()).unwrap(); - - let spec = Spec::new_test_round(); - let engine = &*spec.engine; - let genesis_header = spec.genesis_header(); - let db1 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let db2 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); - let b1 = b1.close_and_lock().unwrap(); - let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); - let b2 = b2.close_and_lock().unwrap(); - - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); - if let Seal::Regular(seal) = engine.generate_seal(&b1, &genesis_header) { - assert!(b1.clone().try_seal(engine, seal).is_ok()); - // Second proposal is forbidden. - assert!(engine.generate_seal(&b1, &genesis_header) == Seal::None); - } - - engine.set_signer(Box::new((tap, addr2, "2".into()))); - if let Seal::Regular(seal) = engine.generate_seal(&b2, &genesis_header) { - assert!(b2.clone().try_seal(engine, seal).is_ok()); - // Second proposal is forbidden. - assert!(engine.generate_seal(&b2, &genesis_header) == Seal::None); - } - } - - #[test] - fn checks_difficulty_in_generate_seal() { - let tap = Arc::new(AccountProvider::transient_provider()); - let addr1 = tap.insert_account(keccak("1").into(), &"1".into()).unwrap(); - let addr2 = tap.insert_account(keccak("0").into(), &"0".into()).unwrap(); - - let spec = Spec::new_test_round(); - let engine = &*spec.engine; - - let genesis_header = spec.genesis_header(); - let db1 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let db2 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let last_hashes = Arc::new(vec![genesis_header.hash()]); - - let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); - let b1 = b1.close_and_lock().unwrap(); - let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); - let b2 = b2.close_and_lock().unwrap(); - - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); - match engine.generate_seal(&b1, &genesis_header) { - Seal::None | Seal::Proposal(_) => panic!("wrong seal"), - Seal::Regular(_) => { - engine.step(); - - engine.set_signer(Box::new((tap.clone(), addr2, "0".into()))); - match engine.generate_seal(&b2, &genesis_header) { - Seal::Regular(_) | Seal::Proposal(_) => panic!("sealed despite wrong difficulty"), - Seal::None => {} - } - } - } - } - - #[test] - fn proposer_switching() { - let tap = AccountProvider::transient_provider(); - let addr = tap.insert_account(keccak("0").into(), &"0".into()).unwrap(); - let mut parent_header: Header = Header::default(); - parent_header.set_seal(vec![encode(&0usize)]); - parent_header.set_gas_limit("222222".parse::().unwrap()); - let mut header: Header = Header::default(); - header.set_number(1); - header.set_gas_limit("222222".parse::().unwrap()); - header.set_author(addr); - - let engine = Spec::new_test_round().engine; - - // Two validators. - // Spec starts with step 2. - header.set_difficulty(calculate_score(0, 2, 0)); - let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap(); - header.set_seal(vec![encode(&2usize), encode(&(&*signature as &[u8]))]); - assert!(engine.verify_block_family(&header, &parent_header).is_ok()); - assert!(engine.verify_block_external(&header).is_err()); - header.set_difficulty(calculate_score(0, 1, 0)); - let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap(); - header.set_seal(vec![encode(&1usize), encode(&(&*signature as &[u8]))]); - assert!(engine.verify_block_family(&header, &parent_header).is_ok()); - assert!(engine.verify_block_external(&header).is_ok()); - } - - #[test] - fn rejects_future_block() { - let tap = AccountProvider::transient_provider(); - let addr = tap.insert_account(keccak("0").into(), &"0".into()).unwrap(); - - let mut parent_header: Header = Header::default(); - parent_header.set_seal(vec![encode(&0usize)]); - parent_header.set_gas_limit("222222".parse::().unwrap()); - let mut header: Header = Header::default(); - header.set_number(1); - header.set_gas_limit("222222".parse::().unwrap()); - header.set_author(addr); - - let engine = Spec::new_test_round().engine; - - // Two validators. - // Spec starts with step 2. - header.set_difficulty(calculate_score(0, 1, 0)); - let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap(); - header.set_seal(vec![encode(&1usize), encode(&(&*signature as &[u8]))]); - assert!(engine.verify_block_family(&header, &parent_header).is_ok()); - assert!(engine.verify_block_external(&header).is_ok()); - header.set_seal(vec![encode(&5usize), encode(&(&*signature as &[u8]))]); - assert!(engine.verify_block_basic(&header).is_err()); - } - - #[test] - fn rejects_step_backwards() { - let tap = AccountProvider::transient_provider(); - let addr = tap.insert_account(keccak("0").into(), &"0".into()).unwrap(); - - let mut parent_header: Header = Header::default(); - parent_header.set_seal(vec![encode(&4usize)]); - parent_header.set_gas_limit("222222".parse::().unwrap()); - let mut header: Header = Header::default(); - header.set_number(1); - header.set_gas_limit("222222".parse::().unwrap()); - header.set_author(addr); - - let engine = Spec::new_test_round().engine; - - let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap(); - // Two validators. - // Spec starts with step 2. - header.set_seal(vec![encode(&5usize), encode(&(&*signature as &[u8]))]); - header.set_difficulty(calculate_score(4, 5, 0)); - assert!(engine.verify_block_family(&header, &parent_header).is_ok()); - header.set_seal(vec![encode(&3usize), encode(&(&*signature as &[u8]))]); - header.set_difficulty(calculate_score(4, 3, 0)); - assert!(engine.verify_block_family(&header, &parent_header).is_err()); - } - - #[test] - fn reports_skipped() { - let last_benign = Arc::new(AtomicUsize::new(0)); - let aura = aura(|p| { - p.validators = Box::new(TestSet::new(Default::default(), last_benign.clone())); - }); - - let mut parent_header: Header = Header::default(); - parent_header.set_seal(vec![encode(&1usize)]); - parent_header.set_gas_limit("222222".parse::().unwrap()); - let mut header: Header = Header::default(); - header.set_difficulty(calculate_score(1, 3, 0)); - header.set_gas_limit("222222".parse::().unwrap()); - header.set_seal(vec![encode(&3usize)]); - - // Do not report when signer not present. - assert!(aura.verify_block_family(&header, &parent_header).is_ok()); - assert_eq!(last_benign.load(AtomicOrdering::SeqCst), 0); - - aura.set_signer(Box::new((Arc::new(AccountProvider::transient_provider()), Default::default(), "".into()))); - - // Do not report on steps skipped between genesis and first block. - header.set_number(1); - assert!(aura.verify_block_family(&header, &parent_header).is_ok()); - assert_eq!(last_benign.load(AtomicOrdering::SeqCst), 0); - - // Report on skipped steps otherwise. - header.set_number(2); - assert!(aura.verify_block_family(&header, &parent_header).is_ok()); - assert_eq!(last_benign.load(AtomicOrdering::SeqCst), 2); - } - - #[test] - fn test_uncles_transition() { - let aura = aura(|params| { - params.maximum_uncle_count_transition = 1; - }); - - assert_eq!(aura.maximum_uncle_count(0), 2); - assert_eq!(aura.maximum_uncle_count(1), 0); - assert_eq!(aura.maximum_uncle_count(100), 0); - } + use super::{ + calculate_score, AuthorityRound, AuthorityRoundParams, EmptyStep, SealedEmptyStep, + }; + use accounts::AccountProvider; + use block::*; + use engines::{ + validator_set::{SimpleList, TestSet}, + Engine, EngineError, EthEngine, Seal, + }; + use error::{Error, ErrorKind}; + use ethereum_types::{Address, H256, H520, U256}; + use ethkey::Signature; + use hash::keccak; + use rlp::encode; + use spec::Spec; + use std::{ + collections::BTreeMap, + sync::{ + atomic::{AtomicUsize, Ordering as AtomicOrdering}, + Arc, + }, + }; + use test_helpers::{generate_dummy_client_with_spec, get_temp_state_db, TestNotify}; + use types::{ + header::Header, + transaction::{Action, Transaction}, + }; + + fn aura(f: F) -> Arc + where + F: FnOnce(&mut AuthorityRoundParams), + { + let mut params = AuthorityRoundParams { + step_duration: 1, + start_step: Some(1), + validators: Box::new(TestSet::default()), + validate_score_transition: 0, + validate_step_transition: 0, + immediate_transitions: true, + maximum_uncle_count_transition: 0, + maximum_uncle_count: 0, + empty_steps_transition: u64::max_value(), + maximum_empty_steps: 0, + block_reward: Default::default(), + block_reward_contract_transition: 0, + block_reward_contract: Default::default(), + strict_empty_steps_transition: 0, + }; + + // mutate aura params + f(&mut params); + + // create engine + let mut c_params = ::spec::CommonParams::default(); + c_params.gas_limit_bound_divisor = 5.into(); + let machine = ::machine::EthereumMachine::regular(c_params, Default::default()); + AuthorityRound::new(params, machine).unwrap() + } #[test] - #[should_panic(expected="counter is too high")] + fn has_valid_metadata() { + let engine = Spec::new_test_round().engine; + assert!(!engine.name().is_empty()); + } + + #[test] + fn can_return_schedule() { + let engine = Spec::new_test_round().engine; + let schedule = engine.schedule(10000000); + + assert!(schedule.stack_limit > 0); + } + + #[test] + fn can_do_signature_verification_fail() { + let engine = Spec::new_test_round().engine; + let mut header: Header = Header::default(); + header.set_seal(vec![encode(&H520::default())]); + + let verify_result = engine.verify_block_external(&header); + assert!(verify_result.is_err()); + } + + #[test] + fn generates_seal_and_does_not_double_propose() { + let tap = Arc::new(AccountProvider::transient_provider()); + let addr1 = tap.insert_account(keccak("1").into(), &"1".into()).unwrap(); + let addr2 = tap.insert_account(keccak("2").into(), &"2".into()).unwrap(); + + let spec = Spec::new_test_round(); + let engine = &*spec.engine; + let genesis_header = spec.genesis_header(); + let db1 = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let db2 = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let last_hashes = Arc::new(vec![genesis_header.hash()]); + let b1 = OpenBlock::new( + engine, + Default::default(), + false, + db1, + &genesis_header, + last_hashes.clone(), + addr1, + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + let b1 = b1.close_and_lock().unwrap(); + let b2 = OpenBlock::new( + engine, + Default::default(), + false, + db2, + &genesis_header, + last_hashes, + addr2, + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + let b2 = b2.close_and_lock().unwrap(); + + engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + if let Seal::Regular(seal) = engine.generate_seal(&b1, &genesis_header) { + assert!(b1.clone().try_seal(engine, seal).is_ok()); + // Second proposal is forbidden. + assert!(engine.generate_seal(&b1, &genesis_header) == Seal::None); + } + + engine.set_signer(Box::new((tap, addr2, "2".into()))); + if let Seal::Regular(seal) = engine.generate_seal(&b2, &genesis_header) { + assert!(b2.clone().try_seal(engine, seal).is_ok()); + // Second proposal is forbidden. + assert!(engine.generate_seal(&b2, &genesis_header) == Seal::None); + } + } + + #[test] + fn checks_difficulty_in_generate_seal() { + let tap = Arc::new(AccountProvider::transient_provider()); + let addr1 = tap.insert_account(keccak("1").into(), &"1".into()).unwrap(); + let addr2 = tap.insert_account(keccak("0").into(), &"0".into()).unwrap(); + + let spec = Spec::new_test_round(); + let engine = &*spec.engine; + + let genesis_header = spec.genesis_header(); + let db1 = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let db2 = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let last_hashes = Arc::new(vec![genesis_header.hash()]); + + let b1 = OpenBlock::new( + engine, + Default::default(), + false, + db1, + &genesis_header, + last_hashes.clone(), + addr1, + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + let b1 = b1.close_and_lock().unwrap(); + let b2 = OpenBlock::new( + engine, + Default::default(), + false, + db2, + &genesis_header, + last_hashes, + addr2, + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + let b2 = b2.close_and_lock().unwrap(); + + engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + match engine.generate_seal(&b1, &genesis_header) { + Seal::None | Seal::Proposal(_) => panic!("wrong seal"), + Seal::Regular(_) => { + engine.step(); + + engine.set_signer(Box::new((tap.clone(), addr2, "0".into()))); + match engine.generate_seal(&b2, &genesis_header) { + Seal::Regular(_) | Seal::Proposal(_) => { + panic!("sealed despite wrong difficulty") + } + Seal::None => {} + } + } + } + } + + #[test] + fn proposer_switching() { + let tap = AccountProvider::transient_provider(); + let addr = tap.insert_account(keccak("0").into(), &"0".into()).unwrap(); + let mut parent_header: Header = Header::default(); + parent_header.set_seal(vec![encode(&0usize)]); + parent_header.set_gas_limit("222222".parse::().unwrap()); + let mut header: Header = Header::default(); + header.set_number(1); + header.set_gas_limit("222222".parse::().unwrap()); + header.set_author(addr); + + let engine = Spec::new_test_round().engine; + + // Two validators. + // Spec starts with step 2. + header.set_difficulty(calculate_score(0, 2, 0)); + let signature = tap + .sign(addr, Some("0".into()), header.bare_hash()) + .unwrap(); + header.set_seal(vec![encode(&2usize), encode(&(&*signature as &[u8]))]); + assert!(engine.verify_block_family(&header, &parent_header).is_ok()); + assert!(engine.verify_block_external(&header).is_err()); + header.set_difficulty(calculate_score(0, 1, 0)); + let signature = tap + .sign(addr, Some("0".into()), header.bare_hash()) + .unwrap(); + header.set_seal(vec![encode(&1usize), encode(&(&*signature as &[u8]))]); + assert!(engine.verify_block_family(&header, &parent_header).is_ok()); + assert!(engine.verify_block_external(&header).is_ok()); + } + + #[test] + fn rejects_future_block() { + let tap = AccountProvider::transient_provider(); + let addr = tap.insert_account(keccak("0").into(), &"0".into()).unwrap(); + + let mut parent_header: Header = Header::default(); + parent_header.set_seal(vec![encode(&0usize)]); + parent_header.set_gas_limit("222222".parse::().unwrap()); + let mut header: Header = Header::default(); + header.set_number(1); + header.set_gas_limit("222222".parse::().unwrap()); + header.set_author(addr); + + let engine = Spec::new_test_round().engine; + + // Two validators. + // Spec starts with step 2. + header.set_difficulty(calculate_score(0, 1, 0)); + let signature = tap + .sign(addr, Some("0".into()), header.bare_hash()) + .unwrap(); + header.set_seal(vec![encode(&1usize), encode(&(&*signature as &[u8]))]); + assert!(engine.verify_block_family(&header, &parent_header).is_ok()); + assert!(engine.verify_block_external(&header).is_ok()); + header.set_seal(vec![encode(&5usize), encode(&(&*signature as &[u8]))]); + assert!(engine.verify_block_basic(&header).is_err()); + } + + #[test] + fn rejects_step_backwards() { + let tap = AccountProvider::transient_provider(); + let addr = tap.insert_account(keccak("0").into(), &"0".into()).unwrap(); + + let mut parent_header: Header = Header::default(); + parent_header.set_seal(vec![encode(&4usize)]); + parent_header.set_gas_limit("222222".parse::().unwrap()); + let mut header: Header = Header::default(); + header.set_number(1); + header.set_gas_limit("222222".parse::().unwrap()); + header.set_author(addr); + + let engine = Spec::new_test_round().engine; + + let signature = tap + .sign(addr, Some("0".into()), header.bare_hash()) + .unwrap(); + // Two validators. + // Spec starts with step 2. + header.set_seal(vec![encode(&5usize), encode(&(&*signature as &[u8]))]); + header.set_difficulty(calculate_score(4, 5, 0)); + assert!(engine.verify_block_family(&header, &parent_header).is_ok()); + header.set_seal(vec![encode(&3usize), encode(&(&*signature as &[u8]))]); + header.set_difficulty(calculate_score(4, 3, 0)); + assert!(engine.verify_block_family(&header, &parent_header).is_err()); + } + + #[test] + fn reports_skipped() { + let last_benign = Arc::new(AtomicUsize::new(0)); + let aura = aura(|p| { + p.validators = Box::new(TestSet::new(Default::default(), last_benign.clone())); + }); + + let mut parent_header: Header = Header::default(); + parent_header.set_seal(vec![encode(&1usize)]); + parent_header.set_gas_limit("222222".parse::().unwrap()); + let mut header: Header = Header::default(); + header.set_difficulty(calculate_score(1, 3, 0)); + header.set_gas_limit("222222".parse::().unwrap()); + header.set_seal(vec![encode(&3usize)]); + + // Do not report when signer not present. + assert!(aura.verify_block_family(&header, &parent_header).is_ok()); + assert_eq!(last_benign.load(AtomicOrdering::SeqCst), 0); + + aura.set_signer(Box::new(( + Arc::new(AccountProvider::transient_provider()), + Default::default(), + "".into(), + ))); + + // Do not report on steps skipped between genesis and first block. + header.set_number(1); + assert!(aura.verify_block_family(&header, &parent_header).is_ok()); + assert_eq!(last_benign.load(AtomicOrdering::SeqCst), 0); + + // Report on skipped steps otherwise. + header.set_number(2); + assert!(aura.verify_block_family(&header, &parent_header).is_ok()); + assert_eq!(last_benign.load(AtomicOrdering::SeqCst), 2); + } + + #[test] + fn test_uncles_transition() { + let aura = aura(|params| { + params.maximum_uncle_count_transition = 1; + }); + + assert_eq!(aura.maximum_uncle_count(0), 2); + assert_eq!(aura.maximum_uncle_count(1), 0); + assert_eq!(aura.maximum_uncle_count(100), 0); + } + + #[test] + #[should_panic(expected = "counter is too high")] fn test_counter_increment_too_high() { use super::Step; let step = Step { @@ -1826,522 +2095,684 @@ mod tests { duration: 1, }; step.increment(); - } - - #[test] - #[should_panic(expected="counter is too high")] - fn test_counter_duration_remaining_too_high() { - use super::Step; - let step = Step { - calibrate: false, - inner: AtomicUsize::new(::std::usize::MAX), - duration: 1, - }; - step.duration_remaining(); - } - - #[test] - #[should_panic(expected="authority_round: step duration can't be zero")] - fn test_step_duration_zero() { - aura(|params| { - params.step_duration = 0; - }); - } - - fn setup_empty_steps() -> (Spec, Arc, Vec
) { - let spec = Spec::new_test_round_empty_steps(); - let tap = Arc::new(AccountProvider::transient_provider()); - - let addr1 = tap.insert_account(keccak("1").into(), &"1".into()).unwrap(); - let addr2 = tap.insert_account(keccak("0").into(), &"0".into()).unwrap(); - - let accounts = vec![addr1, addr2]; - - (spec, tap, accounts) - } - - fn empty_step(engine: &EthEngine, step: u64, parent_hash: &H256) -> EmptyStep { - let empty_step_rlp = super::empty_step_rlp(step, parent_hash); - let signature = engine.sign(keccak(&empty_step_rlp)).unwrap().into(); - let parent_hash = parent_hash.clone(); - EmptyStep { step, signature, parent_hash } - } - - fn sealed_empty_step(engine: &EthEngine, step: u64, parent_hash: &H256) -> SealedEmptyStep { - let empty_step_rlp = super::empty_step_rlp(step, parent_hash); - let signature = engine.sign(keccak(&empty_step_rlp)).unwrap().into(); - SealedEmptyStep { signature, step } - } - - fn set_empty_steps_seal(header: &mut Header, step: u64, block_signature: ðkey::Signature, empty_steps: &[SealedEmptyStep]) { - header.set_seal(vec![ - encode(&(step as usize)), - encode(&(&**block_signature as &[u8])), - ::rlp::encode_list(&empty_steps), - ]); - } - - fn assert_insufficient_proof(result: Result, contains: &str) { - match result { - Err(Error(ErrorKind::Engine(EngineError::InsufficientProof(ref s)), _)) =>{ - assert!(s.contains(contains), "Expected {:?} to contain {:?}", s, contains); - }, - e => assert!(false, "Unexpected result: {:?}", e), - } - } - - #[test] - fn broadcast_empty_step_message() { - let (spec, tap, accounts) = setup_empty_steps(); - - let addr1 = accounts[0]; - - let engine = &*spec.engine; - let genesis_header = spec.genesis_header(); - let db1 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - - let last_hashes = Arc::new(vec![genesis_header.hash()]); - - let client = generate_dummy_client_with_spec(Spec::new_test_round_empty_steps); - let notify = Arc::new(TestNotify::default()); - client.add_notify(notify.clone()); - engine.register_client(Arc::downgrade(&client) as _); - - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); - - let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); - let b1 = b1.close_and_lock().unwrap(); - - // the block is empty so we don't seal and instead broadcast an empty step message - assert_eq!(engine.generate_seal(&b1, &genesis_header), Seal::None); - - // spec starts with step 2 - let empty_step_rlp = encode(&empty_step(engine, 2, &genesis_header.hash())); - - // we've received the message - assert!(notify.messages.read().contains(&empty_step_rlp)); - let len = notify.messages.read().len(); - - // make sure that we don't generate empty step for the second time - assert_eq!(engine.generate_seal(&b1, &genesis_header), Seal::None); - assert_eq!(len, notify.messages.read().len()); - } - - #[test] - fn seal_with_empty_steps() { - let (spec, tap, accounts) = setup_empty_steps(); - - let addr1 = accounts[0]; - let addr2 = accounts[1]; - - let engine = &*spec.engine; - let genesis_header = spec.genesis_header(); - let db1 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let db2 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - - let last_hashes = Arc::new(vec![genesis_header.hash()]); - - let client = generate_dummy_client_with_spec(Spec::new_test_round_empty_steps); - let notify = Arc::new(TestNotify::default()); - client.add_notify(notify.clone()); - engine.register_client(Arc::downgrade(&client) as _); - - // step 2 - let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); - let b1 = b1.close_and_lock().unwrap(); - - // since the block is empty it isn't sealed and we generate empty steps - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); - assert_eq!(engine.generate_seal(&b1, &genesis_header), Seal::None); - engine.step(); - - // step 3 - let mut b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes.clone(), addr2, (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); - b2.push_transaction(Transaction { - action: Action::Create, - nonce: U256::from(0), - gas_price: U256::from(3000), - gas: U256::from(53_000), - value: U256::from(1), - data: vec![], - }.fake_sign(addr2), None).unwrap(); - let b2 = b2.close_and_lock().unwrap(); - - // we will now seal a block with 1tx and include the accumulated empty step message - engine.set_signer(Box::new((tap.clone(), addr2, "0".into()))); - if let Seal::Regular(seal) = engine.generate_seal(&b2, &genesis_header) { - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); - let empty_step2 = sealed_empty_step(engine, 2, &genesis_header.hash()); - let empty_steps = ::rlp::encode_list(&vec![empty_step2]); - - assert_eq!(seal[0], encode(&3usize)); - assert_eq!(seal[2], empty_steps); - } - } - - #[test] - fn seal_empty_block_with_empty_steps() { - let (spec, tap, accounts) = setup_empty_steps(); - - let addr1 = accounts[0]; - let addr2 = accounts[1]; - - let engine = &*spec.engine; - let genesis_header = spec.genesis_header(); - let db1 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let db2 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let db3 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - - let last_hashes = Arc::new(vec![genesis_header.hash()]); - - let client = generate_dummy_client_with_spec(Spec::new_test_round_empty_steps); - let notify = Arc::new(TestNotify::default()); - client.add_notify(notify.clone()); - engine.register_client(Arc::downgrade(&client) as _); - - // step 2 - let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); - let b1 = b1.close_and_lock().unwrap(); - - // since the block is empty it isn't sealed and we generate empty steps - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); - assert_eq!(engine.generate_seal(&b1, &genesis_header), Seal::None); - engine.step(); - - // step 3 - let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes.clone(), addr2, (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); - let b2 = b2.close_and_lock().unwrap(); - engine.set_signer(Box::new((tap.clone(), addr2, "0".into()))); - assert_eq!(engine.generate_seal(&b2, &genesis_header), Seal::None); - engine.step(); - - // step 4 - // the spec sets the maximum_empty_steps to 2 so we will now seal an empty block and include the empty step messages - let b3 = OpenBlock::new(engine, Default::default(), false, db3, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); - let b3 = b3.close_and_lock().unwrap(); - - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); - if let Seal::Regular(seal) = engine.generate_seal(&b3, &genesis_header) { - let empty_step2 = sealed_empty_step(engine, 2, &genesis_header.hash()); - engine.set_signer(Box::new((tap.clone(), addr2, "0".into()))); - let empty_step3 = sealed_empty_step(engine, 3, &genesis_header.hash()); - - let empty_steps = ::rlp::encode_list(&vec![empty_step2, empty_step3]); - - assert_eq!(seal[0], encode(&4usize)); - assert_eq!(seal[2], empty_steps); - } - } - - #[test] - fn reward_empty_steps() { - let (spec, tap, accounts) = setup_empty_steps(); - - let addr1 = accounts[0]; - - let engine = &*spec.engine; - let genesis_header = spec.genesis_header(); - let db1 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let db2 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - - let last_hashes = Arc::new(vec![genesis_header.hash()]); - - let client = generate_dummy_client_with_spec(Spec::new_test_round_empty_steps); - engine.register_client(Arc::downgrade(&client) as _); - - // step 2 - let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); - let b1 = b1.close_and_lock().unwrap(); - - // since the block is empty it isn't sealed and we generate empty steps - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); - assert_eq!(engine.generate_seal(&b1, &genesis_header), Seal::None); - engine.step(); - - // step 3 - // the signer of the accumulated empty step message should be rewarded - let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); - let addr1_balance = b2.state.balance(&addr1).unwrap(); - - // after closing the block `addr1` should be reward twice, one for the included empty step message and another for block creation - let b2 = b2.close_and_lock().unwrap(); - - // the spec sets the block reward to 10 - assert_eq!(b2.state.balance(&addr1).unwrap(), addr1_balance + (10 * 2)) - } - - #[test] - fn verify_seal_empty_steps() { - let (spec, tap, accounts) = setup_empty_steps(); - let addr1 = accounts[0]; - let addr2 = accounts[1]; - let engine = &*spec.engine; - - let mut parent_header: Header = Header::default(); - parent_header.set_seal(vec![encode(&0usize)]); - parent_header.set_gas_limit("222222".parse::().unwrap()); - - let mut header: Header = Header::default(); - header.set_parent_hash(parent_header.hash()); - header.set_number(1); - header.set_gas_limit("222222".parse::().unwrap()); - header.set_author(addr1); - - let signature = tap.sign(addr1, Some("1".into()), header.bare_hash()).unwrap(); - - // empty step with invalid step - let empty_steps = vec![SealedEmptyStep { signature: 0.into(), step: 2 }]; - set_empty_steps_seal(&mut header, 2, &signature, &empty_steps); - - assert_insufficient_proof( - engine.verify_block_family(&header, &parent_header), - "invalid step" - ); - - // empty step with invalid signature - let empty_steps = vec![SealedEmptyStep { signature: 0.into(), step: 1 }]; - set_empty_steps_seal(&mut header, 2, &signature, &empty_steps); - - assert_insufficient_proof( - engine.verify_block_family(&header, &parent_header), - "invalid empty step proof" - ); - - // empty step with valid signature from incorrect proposer for step - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); - let empty_steps = vec![sealed_empty_step(engine, 1, &parent_header.hash())]; - set_empty_steps_seal(&mut header, 2, &signature, &empty_steps); - - assert_insufficient_proof( - engine.verify_block_family(&header, &parent_header), - "invalid empty step proof" - ); - - // valid empty steps - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); - let empty_step2 = sealed_empty_step(engine, 2, &parent_header.hash()); - engine.set_signer(Box::new((tap.clone(), addr2, "0".into()))); - let empty_step3 = sealed_empty_step(engine, 3, &parent_header.hash()); - - let empty_steps = vec![empty_step2, empty_step3]; - header.set_difficulty(calculate_score(0, 4, 2)); - let signature = tap.sign(addr1, Some("1".into()), header.bare_hash()).unwrap(); - set_empty_steps_seal(&mut header, 4, &signature, &empty_steps); - - assert!(engine.verify_block_family(&header, &parent_header).is_ok()); - } - - #[test] - fn block_reward_contract() { - let spec = Spec::new_test_round_block_reward_contract(); - let tap = Arc::new(AccountProvider::transient_provider()); - - let addr1 = tap.insert_account(keccak("1").into(), &"1".into()).unwrap(); - - let engine = &*spec.engine; - let genesis_header = spec.genesis_header(); - let db1 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let db2 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - - let last_hashes = Arc::new(vec![genesis_header.hash()]); - - let client = generate_dummy_client_with_spec(Spec::new_test_round_block_reward_contract); - engine.register_client(Arc::downgrade(&client) as _); - - // step 2 - let b1 = OpenBlock::new( - engine, - Default::default(), - false, - db1, - &genesis_header, - last_hashes.clone(), - addr1, - (3141562.into(), 31415620.into()), - vec![], - false, - None, - ).unwrap(); - let b1 = b1.close_and_lock().unwrap(); - - // since the block is empty it isn't sealed and we generate empty steps - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); - assert_eq!(engine.generate_seal(&b1, &genesis_header), Seal::None); - engine.step(); - - // step 3 - // the signer of the accumulated empty step message should be rewarded - let b2 = OpenBlock::new( - engine, - Default::default(), - false, - db2, - &genesis_header, - last_hashes.clone(), - addr1, - (3141562.into(), 31415620.into()), - vec![], - false, - None, - ).unwrap(); - let addr1_balance = b2.state.balance(&addr1).unwrap(); - - // after closing the block `addr1` should be reward twice, one for the included empty step - // message and another for block creation - let b2 = b2.close_and_lock().unwrap(); - - // the contract rewards (1000 + kind) for each benefactor/reward kind - assert_eq!( - b2.state.balance(&addr1).unwrap(), - addr1_balance + (1000 + 0) + (1000 + 2), - ) - } - - #[test] - fn extra_info_from_seal() { - let (spec, tap, accounts) = setup_empty_steps(); - let engine = &*spec.engine; - - let addr1 = accounts[0]; - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); - - let mut header: Header = Header::default(); - let empty_step = empty_step(engine, 1, &header.parent_hash()); - let sealed_empty_step = empty_step.sealed(); - - header.set_number(2); - header.set_seal(vec![ - encode(&2usize), - encode(&H520::default()), - ::rlp::encode_list(&vec![sealed_empty_step]), - ]); - - let info = engine.extra_info(&header); - - let mut expected = BTreeMap::default(); - expected.insert("step".into(), "2".into()); - expected.insert("signature".into(), Signature::from(H520::default()).to_string()); - expected.insert("emptySteps".into(), format!("[{}]", empty_step)); - - assert_eq!(info, expected); - - header.set_seal(vec![]); - - assert_eq!( - engine.extra_info(&header), - BTreeMap::default(), - ); - } - - #[test] - fn test_empty_steps() { - let engine = aura(|p| { - p.step_duration = 4; - p.empty_steps_transition = 0; - p.maximum_empty_steps = 0; - }); - - let parent_hash: H256 = 1.into(); - let signature = H520::default(); - let step = |step: u64| EmptyStep { - step, - parent_hash, - signature, - }; - - engine.handle_empty_step_message(step(1)); - engine.handle_empty_step_message(step(3)); - engine.handle_empty_step_message(step(2)); - engine.handle_empty_step_message(step(1)); - - assert_eq!(engine.empty_steps(0, 4, parent_hash), vec![step(1), step(2), step(3)]); - assert_eq!(engine.empty_steps(2, 3, parent_hash), vec![]); - assert_eq!(engine.empty_steps(2, 4, parent_hash), vec![step(3)]); - - engine.clear_empty_steps(2); - - assert_eq!(engine.empty_steps(0, 3, parent_hash), vec![]); - assert_eq!(engine.empty_steps(0, 4, parent_hash), vec![step(3)]); - } - - #[test] - fn should_reject_duplicate_empty_steps() { - // given - let (_spec, tap, accounts) = setup_empty_steps(); - let engine = aura(|p| { - p.validators = Box::new(SimpleList::new(accounts.clone())); - p.step_duration = 4; - p.empty_steps_transition = 0; - p.maximum_empty_steps = 0; - }); - - let mut parent = Header::default(); - parent.set_seal(vec![encode(&0usize)]); - - let mut header = Header::default(); - header.set_number(parent.number() + 1); - header.set_parent_hash(parent.hash()); - header.set_author(accounts[0]); - - // when - engine.set_signer(Box::new((tap.clone(), accounts[1], "0".into()))); - let empty_steps = vec![ - sealed_empty_step(&*engine, 1, &parent.hash()), - sealed_empty_step(&*engine, 1, &parent.hash()), - ]; - let step = 2; - let signature = tap.sign(accounts[0], Some("1".into()), header.bare_hash()).unwrap(); - set_empty_steps_seal(&mut header, step, &signature, &empty_steps); - header.set_difficulty(calculate_score(0, step, empty_steps.len())); - - // then - assert_insufficient_proof( - engine.verify_block_family(&header, &parent), - "duplicate empty step" - ); - } - - #[test] - fn should_reject_empty_steps_out_of_order() { - // given - let (_spec, tap, accounts) = setup_empty_steps(); - let engine = aura(|p| { - p.validators = Box::new(SimpleList::new(accounts.clone())); - p.step_duration = 4; - p.empty_steps_transition = 0; - p.maximum_empty_steps = 0; - }); - - let mut parent = Header::default(); - parent.set_seal(vec![encode(&0usize)]); - - let mut header = Header::default(); - header.set_number(parent.number() + 1); - header.set_parent_hash(parent.hash()); - header.set_author(accounts[0]); - - // when - engine.set_signer(Box::new((tap.clone(), accounts[1], "0".into()))); - let es1 = sealed_empty_step(&*engine, 1, &parent.hash()); - engine.set_signer(Box::new((tap.clone(), accounts[0], "1".into()))); - let es2 = sealed_empty_step(&*engine, 2, &parent.hash()); - - let mut empty_steps = vec![es2, es1]; - - let step = 3; - let signature = tap.sign(accounts[1], Some("0".into()), header.bare_hash()).unwrap(); - set_empty_steps_seal(&mut header, step, &signature, &empty_steps); - header.set_difficulty(calculate_score(0, step, empty_steps.len())); - - // then make sure it's rejected because of the order - assert_insufficient_proof( - engine.verify_block_family(&header, &parent), - "unordered empty step" - ); - - // now try to fix the order - empty_steps.reverse(); - set_empty_steps_seal(&mut header, step, &signature, &empty_steps); - assert_eq!(engine.verify_block_family(&header, &parent).unwrap(), ()); - } + } + + #[test] + #[should_panic(expected = "counter is too high")] + fn test_counter_duration_remaining_too_high() { + use super::Step; + let step = Step { + calibrate: false, + inner: AtomicUsize::new(::std::usize::MAX), + duration: 1, + }; + step.duration_remaining(); + } + + #[test] + #[should_panic(expected = "authority_round: step duration can't be zero")] + fn test_step_duration_zero() { + aura(|params| { + params.step_duration = 0; + }); + } + + fn setup_empty_steps() -> (Spec, Arc, Vec
) { + let spec = Spec::new_test_round_empty_steps(); + let tap = Arc::new(AccountProvider::transient_provider()); + + let addr1 = tap.insert_account(keccak("1").into(), &"1".into()).unwrap(); + let addr2 = tap.insert_account(keccak("0").into(), &"0".into()).unwrap(); + + let accounts = vec![addr1, addr2]; + + (spec, tap, accounts) + } + + fn empty_step(engine: &dyn EthEngine, step: u64, parent_hash: &H256) -> EmptyStep { + let empty_step_rlp = super::empty_step_rlp(step, parent_hash); + let signature = engine.sign(keccak(&empty_step_rlp)).unwrap().into(); + let parent_hash = parent_hash.clone(); + EmptyStep { + step, + signature, + parent_hash, + } + } + + fn sealed_empty_step(engine: &dyn EthEngine, step: u64, parent_hash: &H256) -> SealedEmptyStep { + let empty_step_rlp = super::empty_step_rlp(step, parent_hash); + let signature = engine.sign(keccak(&empty_step_rlp)).unwrap().into(); + SealedEmptyStep { signature, step } + } + + fn set_empty_steps_seal( + header: &mut Header, + step: u64, + block_signature: ðkey::Signature, + empty_steps: &[SealedEmptyStep], + ) { + header.set_seal(vec![ + encode(&(step as usize)), + encode(&(&**block_signature as &[u8])), + ::rlp::encode_list(&empty_steps), + ]); + } + + fn assert_insufficient_proof(result: Result, contains: &str) { + match result { + Err(Error(ErrorKind::Engine(EngineError::InsufficientProof(ref s)), _)) => { + assert!( + s.contains(contains), + "Expected {:?} to contain {:?}", + s, + contains + ); + } + e => assert!(false, "Unexpected result: {:?}", e), + } + } + + #[test] + fn broadcast_empty_step_message() { + let (spec, tap, accounts) = setup_empty_steps(); + + let addr1 = accounts[0]; + + let engine = &*spec.engine; + let genesis_header = spec.genesis_header(); + let db1 = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + + let last_hashes = Arc::new(vec![genesis_header.hash()]); + + let client = generate_dummy_client_with_spec(Spec::new_test_round_empty_steps); + let notify = Arc::new(TestNotify::default()); + client.add_notify(notify.clone()); + engine.register_client(Arc::downgrade(&client) as _); + + engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + + let b1 = OpenBlock::new( + engine, + Default::default(), + false, + db1, + &genesis_header, + last_hashes.clone(), + addr1, + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + let b1 = b1.close_and_lock().unwrap(); + + // the block is empty so we don't seal and instead broadcast an empty step message + assert_eq!(engine.generate_seal(&b1, &genesis_header), Seal::None); + + // spec starts with step 2 + let empty_step_rlp = encode(&empty_step(engine, 2, &genesis_header.hash())); + + // we've received the message + assert!(notify.messages.read().contains(&empty_step_rlp)); + let len = notify.messages.read().len(); + + // make sure that we don't generate empty step for the second time + assert_eq!(engine.generate_seal(&b1, &genesis_header), Seal::None); + assert_eq!(len, notify.messages.read().len()); + } + + #[test] + fn seal_with_empty_steps() { + let (spec, tap, accounts) = setup_empty_steps(); + + let addr1 = accounts[0]; + let addr2 = accounts[1]; + + let engine = &*spec.engine; + let genesis_header = spec.genesis_header(); + let db1 = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let db2 = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + + let last_hashes = Arc::new(vec![genesis_header.hash()]); + + let client = generate_dummy_client_with_spec(Spec::new_test_round_empty_steps); + let notify = Arc::new(TestNotify::default()); + client.add_notify(notify.clone()); + engine.register_client(Arc::downgrade(&client) as _); + + // step 2 + let b1 = OpenBlock::new( + engine, + Default::default(), + false, + db1, + &genesis_header, + last_hashes.clone(), + addr1, + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + let b1 = b1.close_and_lock().unwrap(); + + // since the block is empty it isn't sealed and we generate empty steps + engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + assert_eq!(engine.generate_seal(&b1, &genesis_header), Seal::None); + engine.step(); + + // step 3 + let mut b2 = OpenBlock::new( + engine, + Default::default(), + false, + db2, + &genesis_header, + last_hashes.clone(), + addr2, + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + b2.push_transaction( + Transaction { + action: Action::Create, + nonce: U256::from(0), + gas_price: U256::from(3000), + gas: U256::from(53_000), + value: U256::from(1), + data: vec![], + } + .fake_sign(addr2), + None, + ) + .unwrap(); + let b2 = b2.close_and_lock().unwrap(); + + // we will now seal a block with 1tx and include the accumulated empty step message + engine.set_signer(Box::new((tap.clone(), addr2, "0".into()))); + if let Seal::Regular(seal) = engine.generate_seal(&b2, &genesis_header) { + engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + let empty_step2 = sealed_empty_step(engine, 2, &genesis_header.hash()); + let empty_steps = ::rlp::encode_list(&vec![empty_step2]); + + assert_eq!(seal[0], encode(&3usize)); + assert_eq!(seal[2], empty_steps); + } + } + + #[test] + fn seal_empty_block_with_empty_steps() { + let (spec, tap, accounts) = setup_empty_steps(); + + let addr1 = accounts[0]; + let addr2 = accounts[1]; + + let engine = &*spec.engine; + let genesis_header = spec.genesis_header(); + let db1 = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let db2 = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let db3 = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + + let last_hashes = Arc::new(vec![genesis_header.hash()]); + + let client = generate_dummy_client_with_spec(Spec::new_test_round_empty_steps); + let notify = Arc::new(TestNotify::default()); + client.add_notify(notify.clone()); + engine.register_client(Arc::downgrade(&client) as _); + + // step 2 + let b1 = OpenBlock::new( + engine, + Default::default(), + false, + db1, + &genesis_header, + last_hashes.clone(), + addr1, + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + let b1 = b1.close_and_lock().unwrap(); + + // since the block is empty it isn't sealed and we generate empty steps + engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + assert_eq!(engine.generate_seal(&b1, &genesis_header), Seal::None); + engine.step(); + + // step 3 + let b2 = OpenBlock::new( + engine, + Default::default(), + false, + db2, + &genesis_header, + last_hashes.clone(), + addr2, + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + let b2 = b2.close_and_lock().unwrap(); + engine.set_signer(Box::new((tap.clone(), addr2, "0".into()))); + assert_eq!(engine.generate_seal(&b2, &genesis_header), Seal::None); + engine.step(); + + // step 4 + // the spec sets the maximum_empty_steps to 2 so we will now seal an empty block and include the empty step messages + let b3 = OpenBlock::new( + engine, + Default::default(), + false, + db3, + &genesis_header, + last_hashes.clone(), + addr1, + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + let b3 = b3.close_and_lock().unwrap(); + + engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + if let Seal::Regular(seal) = engine.generate_seal(&b3, &genesis_header) { + let empty_step2 = sealed_empty_step(engine, 2, &genesis_header.hash()); + engine.set_signer(Box::new((tap.clone(), addr2, "0".into()))); + let empty_step3 = sealed_empty_step(engine, 3, &genesis_header.hash()); + + let empty_steps = ::rlp::encode_list(&vec![empty_step2, empty_step3]); + + assert_eq!(seal[0], encode(&4usize)); + assert_eq!(seal[2], empty_steps); + } + } + + #[test] + fn reward_empty_steps() { + let (spec, tap, accounts) = setup_empty_steps(); + + let addr1 = accounts[0]; + + let engine = &*spec.engine; + let genesis_header = spec.genesis_header(); + let db1 = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let db2 = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + + let last_hashes = Arc::new(vec![genesis_header.hash()]); + + let client = generate_dummy_client_with_spec(Spec::new_test_round_empty_steps); + engine.register_client(Arc::downgrade(&client) as _); + + // step 2 + let b1 = OpenBlock::new( + engine, + Default::default(), + false, + db1, + &genesis_header, + last_hashes.clone(), + addr1, + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + let b1 = b1.close_and_lock().unwrap(); + + // since the block is empty it isn't sealed and we generate empty steps + engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + assert_eq!(engine.generate_seal(&b1, &genesis_header), Seal::None); + engine.step(); + + // step 3 + // the signer of the accumulated empty step message should be rewarded + let b2 = OpenBlock::new( + engine, + Default::default(), + false, + db2, + &genesis_header, + last_hashes.clone(), + addr1, + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + let addr1_balance = b2.state.balance(&addr1).unwrap(); + + // after closing the block `addr1` should be reward twice, one for the included empty step message and another for block creation + let b2 = b2.close_and_lock().unwrap(); + + // the spec sets the block reward to 10 + assert_eq!(b2.state.balance(&addr1).unwrap(), addr1_balance + (10 * 2)) + } + + #[test] + fn verify_seal_empty_steps() { + let (spec, tap, accounts) = setup_empty_steps(); + let addr1 = accounts[0]; + let addr2 = accounts[1]; + let engine = &*spec.engine; + + let mut parent_header: Header = Header::default(); + parent_header.set_seal(vec![encode(&0usize)]); + parent_header.set_gas_limit("222222".parse::().unwrap()); + + let mut header: Header = Header::default(); + header.set_parent_hash(parent_header.hash()); + header.set_number(1); + header.set_gas_limit("222222".parse::().unwrap()); + header.set_author(addr1); + + let signature = tap + .sign(addr1, Some("1".into()), header.bare_hash()) + .unwrap(); + + // empty step with invalid step + let empty_steps = vec![SealedEmptyStep { + signature: 0.into(), + step: 2, + }]; + set_empty_steps_seal(&mut header, 2, &signature, &empty_steps); + + assert_insufficient_proof( + engine.verify_block_family(&header, &parent_header), + "invalid step", + ); + + // empty step with invalid signature + let empty_steps = vec![SealedEmptyStep { + signature: 0.into(), + step: 1, + }]; + set_empty_steps_seal(&mut header, 2, &signature, &empty_steps); + + assert_insufficient_proof( + engine.verify_block_family(&header, &parent_header), + "invalid empty step proof", + ); + + // empty step with valid signature from incorrect proposer for step + engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + let empty_steps = vec![sealed_empty_step(engine, 1, &parent_header.hash())]; + set_empty_steps_seal(&mut header, 2, &signature, &empty_steps); + + assert_insufficient_proof( + engine.verify_block_family(&header, &parent_header), + "invalid empty step proof", + ); + + // valid empty steps + engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + let empty_step2 = sealed_empty_step(engine, 2, &parent_header.hash()); + engine.set_signer(Box::new((tap.clone(), addr2, "0".into()))); + let empty_step3 = sealed_empty_step(engine, 3, &parent_header.hash()); + + let empty_steps = vec![empty_step2, empty_step3]; + header.set_difficulty(calculate_score(0, 4, 2)); + let signature = tap + .sign(addr1, Some("1".into()), header.bare_hash()) + .unwrap(); + set_empty_steps_seal(&mut header, 4, &signature, &empty_steps); + + assert!(engine.verify_block_family(&header, &parent_header).is_ok()); + } + + #[test] + fn block_reward_contract() { + let spec = Spec::new_test_round_block_reward_contract(); + let tap = Arc::new(AccountProvider::transient_provider()); + + let addr1 = tap.insert_account(keccak("1").into(), &"1".into()).unwrap(); + + let engine = &*spec.engine; + let genesis_header = spec.genesis_header(); + let db1 = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let db2 = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + + let last_hashes = Arc::new(vec![genesis_header.hash()]); + + let client = generate_dummy_client_with_spec(Spec::new_test_round_block_reward_contract); + engine.register_client(Arc::downgrade(&client) as _); + + // step 2 + let b1 = OpenBlock::new( + engine, + Default::default(), + false, + db1, + &genesis_header, + last_hashes.clone(), + addr1, + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + let b1 = b1.close_and_lock().unwrap(); + + // since the block is empty it isn't sealed and we generate empty steps + engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + assert_eq!(engine.generate_seal(&b1, &genesis_header), Seal::None); + engine.step(); + + // step 3 + // the signer of the accumulated empty step message should be rewarded + let b2 = OpenBlock::new( + engine, + Default::default(), + false, + db2, + &genesis_header, + last_hashes.clone(), + addr1, + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + let addr1_balance = b2.state.balance(&addr1).unwrap(); + + // after closing the block `addr1` should be reward twice, one for the included empty step + // message and another for block creation + let b2 = b2.close_and_lock().unwrap(); + + // the contract rewards (1000 + kind) for each benefactor/reward kind + assert_eq!( + b2.state.balance(&addr1).unwrap(), + addr1_balance + (1000 + 0) + (1000 + 2), + ) + } + + #[test] + fn extra_info_from_seal() { + let (spec, tap, accounts) = setup_empty_steps(); + let engine = &*spec.engine; + + let addr1 = accounts[0]; + engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + + let mut header: Header = Header::default(); + let empty_step = empty_step(engine, 1, &header.parent_hash()); + let sealed_empty_step = empty_step.sealed(); + + header.set_number(2); + header.set_seal(vec![ + encode(&2usize), + encode(&H520::default()), + ::rlp::encode_list(&vec![sealed_empty_step]), + ]); + + let info = engine.extra_info(&header); + + let mut expected = BTreeMap::default(); + expected.insert("step".into(), "2".into()); + expected.insert( + "signature".into(), + Signature::from(H520::default()).to_string(), + ); + expected.insert("emptySteps".into(), format!("[{}]", empty_step)); + + assert_eq!(info, expected); + + header.set_seal(vec![]); + + assert_eq!(engine.extra_info(&header), BTreeMap::default(),); + } + + #[test] + fn test_empty_steps() { + let engine = aura(|p| { + p.step_duration = 4; + p.empty_steps_transition = 0; + p.maximum_empty_steps = 0; + }); + + let parent_hash: H256 = 1.into(); + let signature = H520::default(); + let step = |step: u64| EmptyStep { + step, + parent_hash, + signature, + }; + + engine.handle_empty_step_message(step(1)); + engine.handle_empty_step_message(step(3)); + engine.handle_empty_step_message(step(2)); + engine.handle_empty_step_message(step(1)); + + assert_eq!( + engine.empty_steps(0, 4, parent_hash), + vec![step(1), step(2), step(3)] + ); + assert_eq!(engine.empty_steps(2, 3, parent_hash), vec![]); + assert_eq!(engine.empty_steps(2, 4, parent_hash), vec![step(3)]); + + engine.clear_empty_steps(2); + + assert_eq!(engine.empty_steps(0, 3, parent_hash), vec![]); + assert_eq!(engine.empty_steps(0, 4, parent_hash), vec![step(3)]); + } + + #[test] + fn should_reject_duplicate_empty_steps() { + // given + let (_spec, tap, accounts) = setup_empty_steps(); + let engine = aura(|p| { + p.validators = Box::new(SimpleList::new(accounts.clone())); + p.step_duration = 4; + p.empty_steps_transition = 0; + p.maximum_empty_steps = 0; + }); + + let mut parent = Header::default(); + parent.set_seal(vec![encode(&0usize)]); + + let mut header = Header::default(); + header.set_number(parent.number() + 1); + header.set_parent_hash(parent.hash()); + header.set_author(accounts[0]); + + // when + engine.set_signer(Box::new((tap.clone(), accounts[1], "0".into()))); + let empty_steps = vec![ + sealed_empty_step(&*engine, 1, &parent.hash()), + sealed_empty_step(&*engine, 1, &parent.hash()), + ]; + let step = 2; + let signature = tap + .sign(accounts[0], Some("1".into()), header.bare_hash()) + .unwrap(); + set_empty_steps_seal(&mut header, step, &signature, &empty_steps); + header.set_difficulty(calculate_score(0, step, empty_steps.len())); + + // then + assert_insufficient_proof( + engine.verify_block_family(&header, &parent), + "duplicate empty step", + ); + } + + #[test] + fn should_reject_empty_steps_out_of_order() { + // given + let (_spec, tap, accounts) = setup_empty_steps(); + let engine = aura(|p| { + p.validators = Box::new(SimpleList::new(accounts.clone())); + p.step_duration = 4; + p.empty_steps_transition = 0; + p.maximum_empty_steps = 0; + }); + + let mut parent = Header::default(); + parent.set_seal(vec![encode(&0usize)]); + + let mut header = Header::default(); + header.set_number(parent.number() + 1); + header.set_parent_hash(parent.hash()); + header.set_author(accounts[0]); + + // when + engine.set_signer(Box::new((tap.clone(), accounts[1], "0".into()))); + let es1 = sealed_empty_step(&*engine, 1, &parent.hash()); + engine.set_signer(Box::new((tap.clone(), accounts[0], "1".into()))); + let es2 = sealed_empty_step(&*engine, 2, &parent.hash()); + + let mut empty_steps = vec![es2, es1]; + + let step = 3; + let signature = tap + .sign(accounts[1], Some("0".into()), header.bare_hash()) + .unwrap(); + set_empty_steps_seal(&mut header, step, &signature, &empty_steps); + header.set_difficulty(calculate_score(0, step, empty_steps.len())); + + // then make sure it's rejected because of the order + assert_insufficient_proof( + engine.verify_block_family(&header, &parent), + "unordered empty step", + ); + + // now try to fix the order + empty_steps.reverse(); + set_empty_steps_seal(&mut header, step, &signature, &empty_steps); + assert_eq!(engine.verify_block_family(&header, &parent).unwrap(), ()); + } } diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index 69b8d07c526..51f1a2bbe37 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -1,284 +1,307 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! A blockchain engine that supports a basic, non-BFT proof-of-authority. -use std::sync::Weak; -use ethereum_types::{H256, H520}; -use parking_lot::RwLock; -use ethkey::{self, Signature}; +use super::validator_set::{new_validator_set, SimpleList, ValidatorSet}; use block::*; -use engines::{Engine, Seal, ConstructedVerifier, EngineError}; -use engines::signer::EngineSigner; +use client::EngineClient; +use engines::{signer::EngineSigner, ConstructedVerifier, Engine, EngineError, Seal}; use error::{BlockError, Error}; +use ethereum_types::{H256, H520}; use ethjson; -use client::EngineClient; +use ethkey::{self, Signature}; use machine::{AuxiliaryData, Call, EthereumMachine}; -use types::header::{Header, ExtendedHeader}; -use super::validator_set::{ValidatorSet, SimpleList, new_validator_set}; +use parking_lot::RwLock; +use std::sync::Weak; +use types::header::{ExtendedHeader, Header}; /// `BasicAuthority` params. #[derive(Debug, PartialEq)] pub struct BasicAuthorityParams { - /// Valid signatories. - pub validators: ethjson::spec::ValidatorSet, + /// Valid signatories. + pub validators: ethjson::spec::ValidatorSet, } impl From for BasicAuthorityParams { - fn from(p: ethjson::spec::BasicAuthorityParams) -> Self { - BasicAuthorityParams { - validators: p.validators, - } - } + fn from(p: ethjson::spec::BasicAuthorityParams) -> Self { + BasicAuthorityParams { + validators: p.validators, + } + } } struct EpochVerifier { - list: SimpleList, + list: SimpleList, } impl super::EpochVerifier for EpochVerifier { - fn verify_light(&self, header: &Header) -> Result<(), Error> { - verify_external(header, &self.list) - } + fn verify_light(&self, header: &Header) -> Result<(), Error> { + verify_external(header, &self.list) + } } -fn verify_external(header: &Header, validators: &ValidatorSet) -> Result<(), Error> { - use rlp::Rlp; +fn verify_external(header: &Header, validators: &dyn ValidatorSet) -> Result<(), Error> { + use rlp::Rlp; - // Check if the signature belongs to a validator, can depend on parent state. - let sig = Rlp::new(&header.seal()[0]).as_val::()?; - let signer = ethkey::public_to_address(ðkey::recover(&sig.into(), &header.bare_hash())?); + // Check if the signature belongs to a validator, can depend on parent state. + let sig = Rlp::new(&header.seal()[0]).as_val::()?; + let signer = ethkey::public_to_address(ðkey::recover(&sig.into(), &header.bare_hash())?); - if *header.author() != signer { - return Err(EngineError::NotAuthorized(*header.author()).into()) - } + if *header.author() != signer { + return Err(EngineError::NotAuthorized(*header.author()).into()); + } - match validators.contains(header.parent_hash(), &signer) { - false => Err(BlockError::InvalidSeal.into()), - true => Ok(()) - } + match validators.contains(header.parent_hash(), &signer) { + false => Err(BlockError::InvalidSeal.into()), + true => Ok(()), + } } /// Engine using `BasicAuthority`, trivial proof-of-authority consensus. pub struct BasicAuthority { - machine: EthereumMachine, - signer: RwLock>>, - validators: Box, + machine: EthereumMachine, + signer: RwLock>>, + validators: Box, } impl BasicAuthority { - /// Create a new instance of BasicAuthority engine - pub fn new(our_params: BasicAuthorityParams, machine: EthereumMachine) -> Self { - BasicAuthority { - machine: machine, - signer: RwLock::new(None), - validators: new_validator_set(our_params.validators), - } - } + /// Create a new instance of BasicAuthority engine + pub fn new(our_params: BasicAuthorityParams, machine: EthereumMachine) -> Self { + BasicAuthority { + machine: machine, + signer: RwLock::new(None), + validators: new_validator_set(our_params.validators), + } + } } impl Engine for BasicAuthority { - fn name(&self) -> &str { "BasicAuthority" } - - fn machine(&self) -> &EthereumMachine { &self.machine } - - // One field - the signature - fn seal_fields(&self, _header: &Header) -> usize { 1 } - - fn seals_internally(&self) -> Option { - Some(self.signer.read().is_some()) - } - - /// Attempt to seal the block internally. - fn generate_seal(&self, block: &ExecutedBlock, _parent: &Header) -> Seal { - let header = &block.header; - let author = header.author(); - if self.validators.contains(header.parent_hash(), author) { - // account should be pernamently unlocked, otherwise sealing will fail - if let Ok(signature) = self.sign(header.bare_hash()) { - return Seal::Regular(vec![::rlp::encode(&(&H520::from(signature) as &[u8]))]); - } else { - trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable"); - } - } - Seal::None - } - - fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> { - Ok(()) - } - - fn verify_block_external(&self, header: &Header) -> Result<(), Error> { - verify_external(header, &*self.validators) - } - - fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result, String> { - self.validators.genesis_epoch_data(header, call) - } - - #[cfg(not(test))] - fn signals_epoch_end(&self, _header: &Header, _auxiliary: AuxiliaryData) - -> super::EpochChange - { - // don't bother signalling even though a contract might try. - super::EpochChange::No - } - - #[cfg(test)] - fn signals_epoch_end(&self, header: &Header, auxiliary: AuxiliaryData) - -> super::EpochChange - { - // in test mode, always signal even though they don't be finalized. - let first = header.number() == 0; - self.validators.signals_epoch_end(first, header, auxiliary) - } - - fn is_epoch_end( - &self, - chain_head: &Header, - _finalized: &[H256], - _chain: &super::Headers
, - _transition_store: &super::PendingTransitionStore, - ) -> Option> { - let first = chain_head.number() == 0; - - // finality never occurs so only apply immediate transitions. - self.validators.is_epoch_end(first, chain_head) - } - - fn is_epoch_end_light( - &self, - chain_head: &Header, - chain: &super::Headers
, - transition_store: &super::PendingTransitionStore, - ) -> Option> { - self.is_epoch_end(chain_head, &[], chain, transition_store) - } - - fn epoch_verifier<'a>(&self, header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a, EthereumMachine> { - let first = header.number() == 0; - - match self.validators.epoch_set(first, &self.machine, header.number(), proof) { - Ok((list, finalize)) => { - let verifier = Box::new(EpochVerifier { list: list }); - - // our epoch verifier will ensure no unverified verifier is ever verified. - match finalize { - Some(finalize) => ConstructedVerifier::Unconfirmed(verifier, proof, finalize), - None => ConstructedVerifier::Trusted(verifier), - } - } - Err(e) => ConstructedVerifier::Err(e), - } - } - - fn register_client(&self, client: Weak) { - self.validators.register_client(client); - } - - fn set_signer(&self, signer: Box) { - *self.signer.write() = Some(signer); - } - - fn sign(&self, hash: H256) -> Result { - Ok(self.signer.read() - .as_ref() - .ok_or_else(|| ethkey::Error::InvalidAddress)? - .sign(hash)? - ) - } - - fn snapshot_components(&self) -> Option> { - None - } - - fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice { - super::total_difficulty_fork_choice(new, current) - } + fn name(&self) -> &str { + "BasicAuthority" + } + + fn machine(&self) -> &EthereumMachine { + &self.machine + } + + // One field - the signature + fn seal_fields(&self, _header: &Header) -> usize { + 1 + } + + fn seals_internally(&self) -> Option { + Some(self.signer.read().is_some()) + } + + /// Attempt to seal the block internally. + fn generate_seal(&self, block: &ExecutedBlock, _parent: &Header) -> Seal { + let header = &block.header; + let author = header.author(); + if self.validators.contains(header.parent_hash(), author) { + // account should be pernamently unlocked, otherwise sealing will fail + if let Ok(signature) = self.sign(header.bare_hash()) { + return Seal::Regular(vec![::rlp::encode(&(&H520::from(signature) as &[u8]))]); + } else { + trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable"); + } + } + Seal::None + } + + fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> { + Ok(()) + } + + fn verify_block_external(&self, header: &Header) -> Result<(), Error> { + verify_external(header, &*self.validators) + } + + fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result, String> { + self.validators.genesis_epoch_data(header, call) + } + + #[cfg(not(test))] + fn signals_epoch_end( + &self, + _header: &Header, + _auxiliary: AuxiliaryData, + ) -> super::EpochChange { + // don't bother signalling even though a contract might try. + super::EpochChange::No + } + + #[cfg(test)] + fn signals_epoch_end( + &self, + header: &Header, + auxiliary: AuxiliaryData, + ) -> super::EpochChange { + // in test mode, always signal even though they don't be finalized. + let first = header.number() == 0; + self.validators.signals_epoch_end(first, header, auxiliary) + } + + fn is_epoch_end( + &self, + chain_head: &Header, + _finalized: &[H256], + _chain: &super::Headers
, + _transition_store: &super::PendingTransitionStore, + ) -> Option> { + let first = chain_head.number() == 0; + + // finality never occurs so only apply immediate transitions. + self.validators.is_epoch_end(first, chain_head) + } + + fn epoch_verifier<'a>( + &self, + header: &Header, + proof: &'a [u8], + ) -> ConstructedVerifier<'a, EthereumMachine> { + let first = header.number() == 0; + + match self + .validators + .epoch_set(first, &self.machine, header.number(), proof) + { + Ok((list, finalize)) => { + let verifier = Box::new(EpochVerifier { list: list }); + + // our epoch verifier will ensure no unverified verifier is ever verified. + match finalize { + Some(finalize) => ConstructedVerifier::Unconfirmed(verifier, proof, finalize), + None => ConstructedVerifier::Trusted(verifier), + } + } + Err(e) => ConstructedVerifier::Err(e), + } + } + + fn register_client(&self, client: Weak) { + self.validators.register_client(client); + } + + fn set_signer(&self, signer: Box) { + *self.signer.write() = Some(signer); + } + + fn sign(&self, hash: H256) -> Result { + Ok(self + .signer + .read() + .as_ref() + .ok_or_else(|| ethkey::Error::InvalidAddress)? + .sign(hash)?) + } + + fn snapshot_components(&self) -> Option> { + None + } + + fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice { + super::total_difficulty_fork_choice(new, current) + } } #[cfg(test)] mod tests { - use std::sync::Arc; - use hash::keccak; - use ethereum_types::H520; - use block::*; - use test_helpers::get_temp_state_db; - use accounts::AccountProvider; - use types::header::Header; - use spec::Spec; - use engines::Seal; - use tempdir::TempDir; - - /// Create a new test chain spec with `BasicAuthority` consensus engine. - fn new_test_authority() -> Spec { - let bytes: &[u8] = include_bytes!("../../res/basic_authority.json"); - let tempdir = TempDir::new("").unwrap(); - Spec::load(&tempdir.path(), bytes).expect("invalid chain spec") - } - - #[test] - fn has_valid_metadata() { - let engine = new_test_authority().engine; - assert!(!engine.name().is_empty()); - } - - #[test] - fn can_return_schedule() { - let engine = new_test_authority().engine; - let schedule = engine.schedule(10000000); - assert!(schedule.stack_limit > 0); - } - - #[test] - fn can_do_signature_verification_fail() { - let engine = new_test_authority().engine; - let mut header: Header = Header::default(); - header.set_seal(vec![::rlp::encode(&H520::default())]); - - let verify_result = engine.verify_block_external(&header); - assert!(verify_result.is_err()); - } - - #[test] - fn can_generate_seal() { - let tap = AccountProvider::transient_provider(); - let addr = tap.insert_account(keccak("").into(), &"".into()).unwrap(); - - let spec = new_test_authority(); - let engine = &*spec.engine; - engine.set_signer(Box::new((Arc::new(tap), addr, "".into()))); - let genesis_header = spec.genesis_header(); - let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); - let b = b.close_and_lock().unwrap(); - if let Seal::Regular(seal) = engine.generate_seal(&b, &genesis_header) { - assert!(b.try_seal(engine, seal).is_ok()); - } - } - - #[test] - fn seals_internally() { - let tap = AccountProvider::transient_provider(); - let authority = tap.insert_account(keccak("").into(), &"".into()).unwrap(); - - let engine = new_test_authority().engine; - assert!(!engine.seals_internally().unwrap()); - engine.set_signer(Box::new((Arc::new(tap), authority, "".into()))); - assert!(engine.seals_internally().unwrap()); - } + use accounts::AccountProvider; + use block::*; + use engines::Seal; + use ethereum_types::H520; + use hash::keccak; + use spec::Spec; + use std::sync::Arc; + use tempdir::TempDir; + use test_helpers::get_temp_state_db; + use types::header::Header; + + /// Create a new test chain spec with `BasicAuthority` consensus engine. + fn new_test_authority() -> Spec { + let bytes: &[u8] = include_bytes!("../../res/basic_authority.json"); + let tempdir = TempDir::new("").unwrap(); + Spec::load(&tempdir.path(), bytes).expect("invalid chain spec") + } + + #[test] + fn has_valid_metadata() { + let engine = new_test_authority().engine; + assert!(!engine.name().is_empty()); + } + + #[test] + fn can_return_schedule() { + let engine = new_test_authority().engine; + let schedule = engine.schedule(10000000); + assert!(schedule.stack_limit > 0); + } + + #[test] + fn can_do_signature_verification_fail() { + let engine = new_test_authority().engine; + let mut header: Header = Header::default(); + header.set_seal(vec![::rlp::encode(&H520::default())]); + + let verify_result = engine.verify_block_external(&header); + assert!(verify_result.is_err()); + } + + #[test] + fn can_generate_seal() { + let tap = AccountProvider::transient_provider(); + let addr = tap.insert_account(keccak("").into(), &"".into()).unwrap(); + + let spec = new_test_authority(); + let engine = &*spec.engine; + engine.set_signer(Box::new((Arc::new(tap), addr, "".into()))); + let genesis_header = spec.genesis_header(); + let db = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let last_hashes = Arc::new(vec![genesis_header.hash()]); + let b = OpenBlock::new( + engine, + Default::default(), + false, + db, + &genesis_header, + last_hashes, + addr, + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + let b = b.close_and_lock().unwrap(); + if let Seal::Regular(seal) = engine.generate_seal(&b, &genesis_header) { + assert!(b.try_seal(engine, seal).is_ok()); + } + } + + #[test] + fn seals_internally() { + let tap = AccountProvider::transient_provider(); + let authority = tap.insert_account(keccak("").into(), &"".into()).unwrap(); + + let engine = new_test_authority().engine; + assert!(!engine.seals_internally().unwrap()); + engine.set_signer(Box::new((Arc::new(tap), authority, "".into()))); + assert!(engine.seals_internally().unwrap()); + } } diff --git a/ethcore/src/engines/block_reward.rs b/ethcore/src/engines/block_reward.rs index 58b55408ebe..1cdd2cc9592 100644 --- a/ethcore/src/engines/block_reward.rs +++ b/ethcore/src/engines/block_reward.rs @@ -1,35 +1,33 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! A module with types for declaring block rewards and a client interface for interacting with a //! block reward contract. -use ethabi; -use ethabi::ParamType; -use ethereum_types::{H160, Address, U256}; +use ethabi::{self, ParamType}; +use ethereum_types::{Address, H160, U256}; -use std::sync::Arc; -use hash::keccak; +use super::{SystemOrCodeCall, SystemOrCodeCallKind}; +use block::ExecutedBlock; use error::Error; +use hash::keccak; use machine::Machine; -use trace; +use std::sync::Arc; +use trace::{self, ExecutiveTracer, Tracer, Tracing}; use types::BlockNumber; -use super::{SystemOrCodeCall, SystemOrCodeCallKind}; -use trace::{Tracer, ExecutiveTracer, Tracing}; -use block::ExecutedBlock; use_contract!(block_reward_contract, "res/contracts/block_reward.json"); @@ -38,203 +36,245 @@ use_contract!(block_reward_contract, "res/contracts/block_reward.json"); /// different semantics which could lead e.g. to different reward values. #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub enum RewardKind { - /// Reward attributed to the block author. - Author, - /// Reward attributed to the author(s) of empty step(s) included in the block (AuthorityRound engine). - EmptyStep, - /// Reward attributed by an external protocol (e.g. block reward contract). - External, - /// Reward attributed to the block uncle(s) with given difference. - Uncle(u8), + /// Reward attributed to the block author. + Author, + /// Reward attributed to the author(s) of empty step(s) included in the block (AuthorityRound engine). + EmptyStep, + /// Reward attributed by an external protocol (e.g. block reward contract). + External, + /// Reward attributed to the block uncle(s) with given difference. + Uncle(u8), } impl RewardKind { - /// Create `RewardKind::Uncle` from given current block number and uncle block number. - pub fn uncle(number: BlockNumber, uncle: BlockNumber) -> Self { - RewardKind::Uncle(if number > uncle && number - uncle <= u8::max_value().into() { (number - uncle) as u8 } else { 0 }) - } + /// Create `RewardKind::Uncle` from given current block number and uncle block number. + pub fn uncle(number: BlockNumber, uncle: BlockNumber) -> Self { + RewardKind::Uncle( + if number > uncle && number - uncle <= u8::max_value().into() { + (number - uncle) as u8 + } else { + 0 + }, + ) + } } impl From for u16 { - fn from(reward_kind: RewardKind) -> Self { - match reward_kind { - RewardKind::Author => 0, - RewardKind::EmptyStep => 2, - RewardKind::External => 3, - - RewardKind::Uncle(depth) => 100 + depth as u16, - } - } + fn from(reward_kind: RewardKind) -> Self { + match reward_kind { + RewardKind::Author => 0, + RewardKind::EmptyStep => 2, + RewardKind::External => 3, + + RewardKind::Uncle(depth) => 100 + depth as u16, + } + } } impl Into for RewardKind { - fn into(self) -> trace::RewardType { - match self { - RewardKind::Author => trace::RewardType::Block, - RewardKind::Uncle(_) => trace::RewardType::Uncle, - RewardKind::EmptyStep => trace::RewardType::EmptyStep, - RewardKind::External => trace::RewardType::External, - } - } + fn into(self) -> trace::RewardType { + match self { + RewardKind::Author => trace::RewardType::Block, + RewardKind::Uncle(_) => trace::RewardType::Uncle, + RewardKind::EmptyStep => trace::RewardType::EmptyStep, + RewardKind::External => trace::RewardType::External, + } + } } /// A client for the block reward contract. #[derive(PartialEq, Debug)] pub struct BlockRewardContract { - kind: SystemOrCodeCallKind, + kind: SystemOrCodeCallKind, } impl BlockRewardContract { - /// Create a new block reward contract client targeting the system call kind. - pub fn new(kind: SystemOrCodeCallKind) -> BlockRewardContract { - BlockRewardContract { - kind, - } - } - - /// Create a new block reward contract client targeting the contract address. - pub fn new_from_address(address: Address) -> BlockRewardContract { - Self::new(SystemOrCodeCallKind::Address(address)) - } - - /// Create a new block reward contract client targeting the given code. - pub fn new_from_code(code: Arc>) -> BlockRewardContract { - let code_hash = keccak(&code[..]); - - Self::new(SystemOrCodeCallKind::Code(code, code_hash)) - } - - /// Calls the block reward contract with the given beneficiaries list (and associated reward kind) - /// and returns the reward allocation (address - value). The block reward contract *must* be - /// called by the system address so the `caller` must ensure that (e.g. using - /// `machine.execute_as_system`). - pub fn reward( - &self, - beneficiaries: &[(Address, RewardKind)], - caller: &mut SystemOrCodeCall, - ) -> Result, Error> { - let input = block_reward_contract::functions::reward::encode_input( - beneficiaries.iter().map(|&(address, _)| H160::from(address)), - beneficiaries.iter().map(|&(_, ref reward_kind)| u16::from(*reward_kind)), - ); - - let output = caller(self.kind.clone(), input) - .map_err(Into::into) - .map_err(::engines::EngineError::FailedSystemCall)?; - - // since this is a non-constant call we can't use ethabi's function output - // deserialization, sadness ensues. - let types = &[ - ParamType::Array(Box::new(ParamType::Address)), - ParamType::Array(Box::new(ParamType::Uint(256))), - ]; - - let tokens = ethabi::decode(types, &output) - .map_err(|err| err.to_string()) - .map_err(::engines::EngineError::FailedSystemCall)?; - - assert!(tokens.len() == 2); - - let addresses = tokens[0].clone().to_array().expect("type checked by ethabi::decode; qed"); - let rewards = tokens[1].clone().to_array().expect("type checked by ethabi::decode; qed"); - - if addresses.len() != rewards.len() { - return Err(::engines::EngineError::FailedSystemCall( - "invalid data returned by reward contract: both arrays must have the same size".into() - ).into()); - } - - let addresses = addresses.into_iter().map(|t| t.to_address().expect("type checked by ethabi::decode; qed")); - let rewards = rewards.into_iter().map(|t| t.to_uint().expect("type checked by ethabi::decode; qed")); - - Ok(addresses.zip(rewards).collect()) - } + /// Create a new block reward contract client targeting the system call kind. + pub fn new(kind: SystemOrCodeCallKind) -> BlockRewardContract { + BlockRewardContract { kind } + } + + /// Create a new block reward contract client targeting the contract address. + pub fn new_from_address(address: Address) -> BlockRewardContract { + Self::new(SystemOrCodeCallKind::Address(address)) + } + + /// Create a new block reward contract client targeting the given code. + pub fn new_from_code(code: Arc>) -> BlockRewardContract { + let code_hash = keccak(&code[..]); + + Self::new(SystemOrCodeCallKind::Code(code, code_hash)) + } + + /// Calls the block reward contract with the given beneficiaries list (and associated reward kind) + /// and returns the reward allocation (address - value). The block reward contract *must* be + /// called by the system address so the `caller` must ensure that (e.g. using + /// `machine.execute_as_system`). + pub fn reward( + &self, + beneficiaries: &[(Address, RewardKind)], + caller: &mut SystemOrCodeCall, + ) -> Result, Error> { + let input = block_reward_contract::functions::reward::encode_input( + beneficiaries + .iter() + .map(|&(address, _)| H160::from(address)), + beneficiaries + .iter() + .map(|&(_, ref reward_kind)| u16::from(*reward_kind)), + ); + + let output = caller(self.kind.clone(), input) + .map_err(Into::into) + .map_err(::engines::EngineError::FailedSystemCall)?; + + // since this is a non-constant call we can't use ethabi's function output + // deserialization, sadness ensues. + let types = &[ + ParamType::Array(Box::new(ParamType::Address)), + ParamType::Array(Box::new(ParamType::Uint(256))), + ]; + + let tokens = ethabi::decode(types, &output) + .map_err(|err| err.to_string()) + .map_err(::engines::EngineError::FailedSystemCall)?; + + assert!(tokens.len() == 2); + + let addresses = tokens[0] + .clone() + .to_array() + .expect("type checked by ethabi::decode; qed"); + let rewards = tokens[1] + .clone() + .to_array() + .expect("type checked by ethabi::decode; qed"); + + if addresses.len() != rewards.len() { + return Err(::engines::EngineError::FailedSystemCall( + "invalid data returned by reward contract: both arrays must have the same size" + .into(), + ) + .into()); + } + + let addresses = addresses + .into_iter() + .map(|t| t.to_address().expect("type checked by ethabi::decode; qed")); + let rewards = rewards + .into_iter() + .map(|t| t.to_uint().expect("type checked by ethabi::decode; qed")); + + Ok(addresses.zip(rewards).collect()) + } } /// Applies the given block rewards, i.e. adds the given balance to each beneficiary' address. /// If tracing is enabled the operations are recorded. pub fn apply_block_rewards( - rewards: &[(Address, RewardKind, U256)], - block: &mut ExecutedBlock, - machine: &M, + rewards: &[(Address, RewardKind, U256)], + block: &mut ExecutedBlock, + machine: &M, ) -> Result<(), M::Error> { - for &(ref author, _, ref block_reward) in rewards { - machine.add_balance(block, author, block_reward)?; - } + for &(ref author, _, ref block_reward) in rewards { + machine.add_balance(block, author, block_reward)?; + } - if let Tracing::Enabled(ref mut traces) = *block.traces_mut() { - let mut tracer = ExecutiveTracer::default(); + if let Tracing::Enabled(ref mut traces) = *block.traces_mut() { + let mut tracer = ExecutiveTracer::default(); - for &(address, reward_kind, amount) in rewards { - tracer.trace_reward(address, amount, reward_kind.into()); - } + for &(address, reward_kind, amount) in rewards { + tracer.trace_reward(address, amount, reward_kind.into()); + } - traces.push(tracer.drain().into()); - } + traces.push(tracer.drain().into()); + } - Ok(()) + Ok(()) } #[cfg(test)] mod test { - use client::PrepareOpenBlock; - use ethereum_types::U256; - use spec::Spec; - use test_helpers::generate_dummy_client_with_spec; - - use engines::SystemOrCodeCallKind; - use super::{BlockRewardContract, RewardKind}; - - #[test] - fn block_reward_contract() { - let client = generate_dummy_client_with_spec(Spec::new_test_round_block_reward_contract); - - let machine = Spec::new_test_machine(); - - // the spec has a block reward contract defined at the given address - let block_reward_contract = BlockRewardContract::new_from_address( - "0000000000000000000000000000000000000042".into(), - ); - - let mut call = |to, data| { - let mut block = client.prepare_open_block( - "0000000000000000000000000000000000000001".into(), - (3141562.into(), 31415620.into()), - vec![], - ).unwrap(); - - let result = match to { - SystemOrCodeCallKind::Address(to) => { - machine.execute_as_system( - block.block_mut(), - to, - U256::max_value(), - Some(data), - ) - }, - _ => panic!("Test reward contract is created by an address, we never reach this branch."), - }; - - result.map_err(|e| format!("{}", e)) - }; - - // if no beneficiaries are given no rewards are attributed - assert!(block_reward_contract.reward(&vec![], &mut call).unwrap().is_empty()); - - // the contract rewards (1000 + kind) for each benefactor - let beneficiaries = vec![ - ("0000000000000000000000000000000000000033".into(), RewardKind::Author), - ("0000000000000000000000000000000000000034".into(), RewardKind::Uncle(1)), - ("0000000000000000000000000000000000000035".into(), RewardKind::EmptyStep), - ]; - - let rewards = block_reward_contract.reward(&beneficiaries, &mut call).unwrap(); - let expected = vec![ - ("0000000000000000000000000000000000000033".into(), U256::from(1000)), - ("0000000000000000000000000000000000000034".into(), U256::from(1000 + 101)), - ("0000000000000000000000000000000000000035".into(), U256::from(1000 + 2)), - ]; - - assert_eq!(expected, rewards); - } + use client::PrepareOpenBlock; + use ethereum_types::U256; + use spec::Spec; + use test_helpers::generate_dummy_client_with_spec; + + use super::{BlockRewardContract, RewardKind}; + use engines::SystemOrCodeCallKind; + + #[test] + fn block_reward_contract() { + let client = generate_dummy_client_with_spec(Spec::new_test_round_block_reward_contract); + + let machine = Spec::new_test_machine(); + + // the spec has a block reward contract defined at the given address + let block_reward_contract = BlockRewardContract::new_from_address( + "0000000000000000000000000000000000000042".into(), + ); + + let mut call = |to, data| { + let mut block = client + .prepare_open_block( + "0000000000000000000000000000000000000001".into(), + (3141562.into(), 31415620.into()), + vec![], + ) + .unwrap(); + + let result = match to { + SystemOrCodeCallKind::Address(to) => { + machine.execute_as_system(block.block_mut(), to, U256::max_value(), Some(data)) + } + _ => panic!( + "Test reward contract is created by an address, we never reach this branch." + ), + }; + + result.map_err(|e| format!("{}", e)) + }; + + // if no beneficiaries are given no rewards are attributed + assert!(block_reward_contract + .reward(&vec![], &mut call) + .unwrap() + .is_empty()); + + // the contract rewards (1000 + kind) for each benefactor + let beneficiaries = vec![ + ( + "0000000000000000000000000000000000000033".into(), + RewardKind::Author, + ), + ( + "0000000000000000000000000000000000000034".into(), + RewardKind::Uncle(1), + ), + ( + "0000000000000000000000000000000000000035".into(), + RewardKind::EmptyStep, + ), + ]; + + let rewards = block_reward_contract + .reward(&beneficiaries, &mut call) + .unwrap(); + let expected = vec![ + ( + "0000000000000000000000000000000000000033".into(), + U256::from(1000), + ), + ( + "0000000000000000000000000000000000000034".into(), + U256::from(1000 + 101), + ), + ( + "0000000000000000000000000000000000000035".into(), + U256::from(1000 + 2), + ), + ]; + + assert_eq!(expected, rewards); + } } diff --git a/ethcore/src/engines/clique/block_state.rs b/ethcore/src/engines/clique/block_state.rs index 4257076c064..424911cf9c0 100644 --- a/ethcore/src/engines/clique/block_state.rs +++ b/ethcore/src/engines/clique/block_state.rs @@ -1,113 +1,136 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{HashMap, BTreeSet, VecDeque}; -use std::fmt; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; - -use engines::EngineError; -use engines::clique::util::{extract_signers, recover_creator}; -use engines::clique::{VoteType, DIFF_INTURN, DIFF_NOTURN, NULL_AUTHOR, SIGNING_DELAY_NOTURN_MS}; -use error::{Error, BlockError}; +// along with OpenEthereum. If not, see . + +use std::{ + collections::{BTreeSet, HashMap, VecDeque}, + fmt, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; + +use engines::{ + clique::{ + util::{extract_signers, recover_creator}, + VoteType, DIFF_INTURN, DIFF_NOTURN, NULL_AUTHOR, SIGNING_DELAY_NOTURN_MS, + }, + EngineError, +}; +use error::{BlockError, Error}; use ethereum_types::{Address, H64}; use rand::Rng; -use types::BlockNumber; -use types::header::Header; -use unexpected::Mismatch; - -#[cfg(not(feature = "time_checked_add"))] use time_utils::CheckedSystemTime; +use types::{header::Header, BlockNumber}; +use unexpected::Mismatch; /// Type that keeps track of the state for a given vote // Votes that go against the proposal aren't counted since it's equivalent to not voting #[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] pub struct VoteState { - kind: VoteType, - votes: u64, + kind: VoteType, + votes: u64, } /// Type that represent a vote #[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] pub struct Vote { - block_number: BlockNumber, - beneficiary: Address, - kind: VoteType, - signer: Address, - reverted: bool, + block_number: BlockNumber, + beneficiary: Address, + kind: VoteType, + signer: Address, + reverted: bool, } /// Type that represent a pending vote #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd)] pub struct PendingVote { - signer: Address, - beneficiary: Address, + signer: Address, + beneficiary: Address, } /// Clique state for each block. #[cfg(not(test))] #[derive(Clone, Debug, Default)] pub struct CliqueBlockState { - /// Current votes for a beneficiary - votes: HashMap, - /// A list of all votes for the given epoch - votes_history: Vec, - /// a list of all valid signer, sorted by ascending order. - signers: BTreeSet
, - /// a deque of recent signer, new entry should be pushed front, apply() modifies this. - recent_signers: VecDeque
, - /// inturn signing should wait until this time - pub next_timestamp_inturn: Option, - /// noturn signing should wait until this time - pub next_timestamp_noturn: Option, + /// Current votes for a beneficiary + votes: HashMap, + /// A list of all votes for the given epoch + votes_history: Vec, + /// a list of all valid signer, sorted by ascending order. + signers: BTreeSet
, + /// a deque of recent signer, new entry should be pushed front, apply() modifies this. + recent_signers: VecDeque
, + /// inturn signing should wait until this time + pub next_timestamp_inturn: Option, + /// noturn signing should wait until this time + pub next_timestamp_noturn: Option, } #[cfg(test)] #[derive(Clone, Debug, Default)] pub struct CliqueBlockState { - /// All recorded votes for a given signer, `Vec` is a stack of votes - pub votes: HashMap, - /// A list of all votes for the given epoch - pub votes_history: Vec, - /// a list of all valid signer, sorted by ascending order. - pub signers: BTreeSet
, - /// a deque of recent signer, new entry should be pushed front, apply() modifies this. - pub recent_signers: VecDeque
, - /// inturn signing should wait until this time - pub next_timestamp_inturn: Option, - /// noturn signing should wait until this time - pub next_timestamp_noturn: Option, + /// All recorded votes for a given signer, `Vec` is a stack of votes + pub votes: HashMap, + /// A list of all votes for the given epoch + pub votes_history: Vec, + /// a list of all valid signer, sorted by ascending order. + pub signers: BTreeSet
, + /// a deque of recent signer, new entry should be pushed front, apply() modifies this. + pub recent_signers: VecDeque
, + /// inturn signing should wait until this time + pub next_timestamp_inturn: Option, + /// noturn signing should wait until this time + pub next_timestamp_noturn: Option, } impl fmt::Display for CliqueBlockState { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let signers: Vec = self.signers.iter() - .map(|s| - format!("{} {:?}", - s, - self.votes.iter().map(|(v, s)| format!("[beneficiary {}, votes: {}]", v.beneficiary, s.votes)) - .collect::>() - ) - ) - .collect(); - - let recent_signers: Vec = self.recent_signers.iter().map(|s| format!("{}", s)).collect(); - let num_votes = self.votes_history.len(); - let add_votes = self.votes_history.iter().filter(|v| v.kind == VoteType::Add).count(); - let rm_votes = self.votes_history.iter().filter(|v| v.kind == VoteType::Remove).count(); - let reverted_votes = self.votes_history.iter().filter(|v| v.reverted).count(); + let signers: Vec = self + .signers + .iter() + .map(|s| { + format!( + "{} {:?}", + s, + self.votes + .iter() + .map(|(v, s)| format!( + "[beneficiary {}, votes: {}]", + v.beneficiary, s.votes + )) + .collect::>() + ) + }) + .collect(); + + let recent_signers: Vec = self + .recent_signers + .iter() + .map(|s| format!("{}", s)) + .collect(); + let num_votes = self.votes_history.len(); + let add_votes = self + .votes_history + .iter() + .filter(|v| v.kind == VoteType::Add) + .count(); + let rm_votes = self + .votes_history + .iter() + .filter(|v| v.kind == VoteType::Remove) + .count(); + let reverted_votes = self.votes_history.iter().filter(|v| v.reverted).count(); write!(f, "Votes {{ \n signers: {:?} \n recent_signers: {:?} \n number of votes: {} \n number of add votes {} @@ -117,253 +140,270 @@ impl fmt::Display for CliqueBlockState { } impl CliqueBlockState { - /// Create new state with given information, this is used creating new state from Checkpoint block. - pub fn new(signers: BTreeSet
) -> Self { - CliqueBlockState { - signers, - ..Default::default() - } - } - - // see https://github.com/ethereum/go-ethereum/blob/master/consensus/clique/clique.go#L474 - fn verify(&self, header: &Header) -> Result { - let creator = recover_creator(header)?.clone(); - - // The signer is not authorized - if !self.signers.contains(&creator) { - trace!(target: "engine", "current state: {}", self); - Err(EngineError::NotAuthorized(creator))? - } - - // The signer has signed a block too recently - if self.recent_signers.contains(&creator) { - trace!(target: "engine", "current state: {}", self); - Err(EngineError::CliqueTooRecentlySigned(creator))? - } - - // Wrong difficulty - let inturn = self.is_inturn(header.number(), &creator); - - if inturn && *header.difficulty() != DIFF_INTURN { - Err(BlockError::InvalidDifficulty(Mismatch { - expected: DIFF_INTURN, - found: *header.difficulty(), - }))? - } - - if !inturn && *header.difficulty() != DIFF_NOTURN { - Err(BlockError::InvalidDifficulty(Mismatch { - expected: DIFF_NOTURN, - found: *header.difficulty(), - }))? - } - - Ok(creator) - } - - /// Verify and apply a new header to current state - pub fn apply(&mut self, header: &Header, is_checkpoint: bool) -> Result { - let creator = self.verify(header)?; - self.recent_signers.push_front(creator); - self.rotate_recent_signers(); - - if is_checkpoint { - // checkpoint block should not affect previous tallying, so we check that. - let signers = extract_signers(header)?; - if self.signers != signers { - let invalid_signers: Vec = signers.into_iter() - .filter(|s| !self.signers.contains(s)) - .map(|s| format!("{}", s)) - .collect(); - Err(EngineError::CliqueFaultyRecoveredSigners(invalid_signers))? - }; - - // TODO(niklasad1): I'm not sure if we should shrink here because it is likely that next epoch - // will need some memory and might be better for allocation algorithm to decide whether to shrink or not - // (typically doubles or halves the allocted memory when necessary) - self.votes.clear(); - self.votes_history.clear(); - self.votes.shrink_to_fit(); - self.votes_history.shrink_to_fit(); - } - - // Contains vote - if *header.author() != NULL_AUTHOR { - let decoded_seal = header.decode_seal::>()?; - if decoded_seal.len() != 2 { - Err(BlockError::InvalidSealArity(Mismatch { expected: 2, found: decoded_seal.len() }))? - } - - let nonce: H64 = decoded_seal[1].into(); - self.update_signers_on_vote(VoteType::from_nonce(nonce)?, creator, *header.author(), header.number())?; - } - - Ok(creator) - } - - fn update_signers_on_vote( - &mut self, - kind: VoteType, - signer: Address, - beneficiary: Address, - block_number: u64 - ) -> Result<(), Error> { - - trace!(target: "engine", "Attempt vote {:?} {:?}", kind, beneficiary); - - let pending_vote = PendingVote { signer, beneficiary }; - - let reverted = if self.is_valid_vote(&beneficiary, kind) { - self.add_vote(pending_vote, kind) - } else { - // This case only happens if a `signer` wants to revert their previous vote - // (does nothing if no previous vote was found) - self.revert_vote(pending_vote) - }; - - // Add all votes to the history - self.votes_history.push( - Vote { - block_number, - beneficiary, - kind, - signer, - reverted, - }); - - // If no vote was found for the beneficiary return `early` but don't propogate an error - let (votes, vote_kind) = match self.get_current_votes_and_kind(beneficiary) { - Some((v, k)) => (v, k), - None => return Ok(()), - }; - let threshold = self.signers.len() / 2; - - debug!(target: "engine", "{}/{} votes to have consensus", votes, threshold + 1); - trace!(target: "engine", "votes: {:?}", votes); - - if votes > threshold { - match vote_kind { - VoteType::Add => { - if self.signers.insert(beneficiary) { - debug!(target: "engine", "added new signer: {}", beneficiary); - } - } - VoteType::Remove => { - if self.signers.remove(&beneficiary) { - debug!(target: "engine", "removed signer: {}", beneficiary); - } - } - } - - self.rotate_recent_signers(); - self.remove_all_votes_from(beneficiary); - } - - Ok(()) - } - - /// Calculate the next timestamp for `inturn` and `noturn` fails if any of them can't be represented as - /// `SystemTime` - // TODO(niklasad1): refactor this method to be in constructor of `CliqueBlockState` instead. - // This is a quite bad API because we must mutate both variables even when already `inturn` fails - // That's why we can't return early and must have the `if-else` in the end - pub fn calc_next_timestamp(&mut self, timestamp: u64, period: u64) -> Result<(), Error> { - let inturn = UNIX_EPOCH.checked_add(Duration::from_secs(timestamp.saturating_add(period))); - - self.next_timestamp_inturn = inturn; - - let delay = Duration::from_millis( - rand::thread_rng().gen_range(0u64, (self.signers.len() as u64 / 2 + 1) * SIGNING_DELAY_NOTURN_MS)); - self.next_timestamp_noturn = inturn.map(|inturn| { - inturn + delay - }); - - if self.next_timestamp_inturn.is_some() && self.next_timestamp_noturn.is_some() { - Ok(()) - } else { - Err(BlockError::TimestampOverflow)? - } - } - - /// Returns true if the block difficulty should be `inturn` - pub fn is_inturn(&self, current_block_number: u64, author: &Address) -> bool { - if let Some(pos) = self.signers.iter().position(|x| *author == *x) { - return current_block_number % self.signers.len() as u64 == pos as u64; - } - false - } - - /// Returns whether the signer is authorized to sign a block - pub fn is_authorized(&self, author: &Address) -> bool { - self.signers.contains(author) && !self.recent_signers.contains(author) - } - - /// Returns whether it makes sense to cast the specified vote in the - /// current state (e.g. don't try to add an already authorized signer). - pub fn is_valid_vote(&self, address: &Address, vote_type: VoteType) -> bool { - let in_signer = self.signers.contains(address); - match vote_type { - VoteType::Add => !in_signer, - VoteType::Remove => in_signer, - } - } - - /// Returns the list of current signers - pub fn signers(&self) -> &BTreeSet
{ - &self.signers - } - - // Note this method will always return `true` but it is intended for a uniform `API` - fn add_vote(&mut self, pending_vote: PendingVote, kind: VoteType) -> bool { - - self.votes.entry(pending_vote) - .and_modify(|state| { - state.votes = state.votes.saturating_add(1); - }) - .or_insert_with(|| VoteState { kind, votes: 1 }); - true - } - - fn revert_vote(&mut self, pending_vote: PendingVote) -> bool { - let mut revert = false; - let mut remove = false; - - self.votes.entry(pending_vote).and_modify(|state| { - if state.votes.saturating_sub(1) == 0 { - remove = true; - } - revert = true; - }); - - if remove { - self.votes.remove(&pending_vote); - } - - revert - } - - fn get_current_votes_and_kind(&self, beneficiary: Address) -> Option<(usize, VoteType)> { - let kind = self.votes.iter() - .find(|(v, _t)| v.beneficiary == beneficiary) - .map(|(_v, t)| t.kind)?; - - let votes = self.votes.keys() - .filter(|vote| vote.beneficiary == beneficiary) - .count(); - - Some((votes, kind)) - } - - fn rotate_recent_signers(&mut self) { - if self.recent_signers.len() >= ( self.signers.len() / 2 ) + 1 { - self.recent_signers.pop_back(); - } - } - - fn remove_all_votes_from(&mut self, beneficiary: Address) { - self.votes = std::mem::replace(&mut self.votes, HashMap::new()) - .into_iter() - .filter(|(v, _t)| v.signer != beneficiary && v.beneficiary != beneficiary) - .collect(); - } + /// Create new state with given information, this is used creating new state from Checkpoint block. + pub fn new(signers: BTreeSet
) -> Self { + CliqueBlockState { + signers, + ..Default::default() + } + } + + // see https://github.com/ethereum/go-ethereum/blob/master/consensus/clique/clique.go#L474 + fn verify(&self, header: &Header) -> Result { + let creator = recover_creator(header)?.clone(); + + // The signer is not authorized + if !self.signers.contains(&creator) { + trace!(target: "engine", "current state: {}", self); + Err(EngineError::NotAuthorized(creator))? + } + + // The signer has signed a block too recently + if self.recent_signers.contains(&creator) { + trace!(target: "engine", "current state: {}", self); + Err(EngineError::CliqueTooRecentlySigned(creator))? + } + + // Wrong difficulty + let inturn = self.is_inturn(header.number(), &creator); + + if inturn && *header.difficulty() != DIFF_INTURN { + Err(BlockError::InvalidDifficulty(Mismatch { + expected: DIFF_INTURN, + found: *header.difficulty(), + }))? + } + + if !inturn && *header.difficulty() != DIFF_NOTURN { + Err(BlockError::InvalidDifficulty(Mismatch { + expected: DIFF_NOTURN, + found: *header.difficulty(), + }))? + } + + Ok(creator) + } + + /// Verify and apply a new header to current state + pub fn apply(&mut self, header: &Header, is_checkpoint: bool) -> Result { + let creator = self.verify(header)?; + self.recent_signers.push_front(creator); + self.rotate_recent_signers(); + + if is_checkpoint { + // checkpoint block should not affect previous tallying, so we check that. + let signers = extract_signers(header)?; + if self.signers != signers { + let invalid_signers: Vec = signers + .into_iter() + .filter(|s| !self.signers.contains(s)) + .map(|s| format!("{}", s)) + .collect(); + Err(EngineError::CliqueFaultyRecoveredSigners(invalid_signers))? + }; + + // TODO(niklasad1): I'm not sure if we should shrink here because it is likely that next epoch + // will need some memory and might be better for allocation algorithm to decide whether to shrink or not + // (typically doubles or halves the allocted memory when necessary) + self.votes.clear(); + self.votes_history.clear(); + self.votes.shrink_to_fit(); + self.votes_history.shrink_to_fit(); + } + + // Contains vote + if *header.author() != NULL_AUTHOR { + let decoded_seal = header.decode_seal::>()?; + if decoded_seal.len() != 2 { + Err(BlockError::InvalidSealArity(Mismatch { + expected: 2, + found: decoded_seal.len(), + }))? + } + + let nonce: H64 = decoded_seal[1].into(); + self.update_signers_on_vote( + VoteType::from_nonce(nonce)?, + creator, + *header.author(), + header.number(), + )?; + } + + Ok(creator) + } + + fn update_signers_on_vote( + &mut self, + kind: VoteType, + signer: Address, + beneficiary: Address, + block_number: u64, + ) -> Result<(), Error> { + trace!(target: "engine", "Attempt vote {:?} {:?}", kind, beneficiary); + + let pending_vote = PendingVote { + signer, + beneficiary, + }; + + let reverted = if self.is_valid_vote(&beneficiary, kind) { + self.add_vote(pending_vote, kind) + } else { + // This case only happens if a `signer` wants to revert their previous vote + // (does nothing if no previous vote was found) + self.revert_vote(pending_vote) + }; + + // Add all votes to the history + self.votes_history.push(Vote { + block_number, + beneficiary, + kind, + signer, + reverted, + }); + + // If no vote was found for the beneficiary return `early` but don't propogate an error + let (votes, vote_kind) = match self.get_current_votes_and_kind(beneficiary) { + Some((v, k)) => (v, k), + None => return Ok(()), + }; + let threshold = self.signers.len() / 2; + + debug!(target: "engine", "{}/{} votes to have consensus", votes, threshold + 1); + trace!(target: "engine", "votes: {:?}", votes); + + if votes > threshold { + match vote_kind { + VoteType::Add => { + if self.signers.insert(beneficiary) { + debug!(target: "engine", "added new signer: {}", beneficiary); + } + } + VoteType::Remove => { + if self.signers.remove(&beneficiary) { + debug!(target: "engine", "removed signer: {}", beneficiary); + } + } + } + + self.rotate_recent_signers(); + self.remove_all_votes_from(beneficiary); + } + + Ok(()) + } + + /// Calculate the next timestamp for `inturn` and `noturn` fails if any of them can't be represented as + /// `SystemTime` + // TODO(niklasad1): refactor this method to be in constructor of `CliqueBlockState` instead. + // This is a quite bad API because we must mutate both variables even when already `inturn` fails + // That's why we can't return early and must have the `if-else` in the end + pub fn calc_next_timestamp(&mut self, timestamp: u64, period: u64) -> Result<(), Error> { + let inturn = CheckedSystemTime::checked_add( + UNIX_EPOCH, + Duration::from_secs(timestamp.saturating_add(period)), + ); + + self.next_timestamp_inturn = inturn; + + let delay = Duration::from_millis(rand::thread_rng().gen_range( + 0u64, + (self.signers.len() as u64 / 2 + 1) * SIGNING_DELAY_NOTURN_MS, + )); + self.next_timestamp_noturn = inturn.map(|inturn| inturn + delay); + + if self.next_timestamp_inturn.is_some() && self.next_timestamp_noturn.is_some() { + Ok(()) + } else { + Err(BlockError::TimestampOverflow)? + } + } + + /// Returns true if the block difficulty should be `inturn` + pub fn is_inturn(&self, current_block_number: u64, author: &Address) -> bool { + if let Some(pos) = self.signers.iter().position(|x| *author == *x) { + return current_block_number % self.signers.len() as u64 == pos as u64; + } + false + } + + /// Returns whether the signer is authorized to sign a block + pub fn is_authorized(&self, author: &Address) -> bool { + self.signers.contains(author) && !self.recent_signers.contains(author) + } + + /// Returns whether it makes sense to cast the specified vote in the + /// current state (e.g. don't try to add an already authorized signer). + pub fn is_valid_vote(&self, address: &Address, vote_type: VoteType) -> bool { + let in_signer = self.signers.contains(address); + match vote_type { + VoteType::Add => !in_signer, + VoteType::Remove => in_signer, + } + } + + /// Returns the list of current signers + pub fn signers(&self) -> &BTreeSet
{ + &self.signers + } + + // Note this method will always return `true` but it is intended for a uniform `API` + fn add_vote(&mut self, pending_vote: PendingVote, kind: VoteType) -> bool { + self.votes + .entry(pending_vote) + .and_modify(|state| { + state.votes = state.votes.saturating_add(1); + }) + .or_insert_with(|| VoteState { kind, votes: 1 }); + true + } + + fn revert_vote(&mut self, pending_vote: PendingVote) -> bool { + let mut revert = false; + let mut remove = false; + + self.votes.entry(pending_vote).and_modify(|state| { + if state.votes.saturating_sub(1) == 0 { + remove = true; + } + revert = true; + }); + + if remove { + self.votes.remove(&pending_vote); + } + + revert + } + + fn get_current_votes_and_kind(&self, beneficiary: Address) -> Option<(usize, VoteType)> { + let kind = self + .votes + .iter() + .find(|(v, _t)| v.beneficiary == beneficiary) + .map(|(_v, t)| t.kind)?; + + let votes = self + .votes + .keys() + .filter(|vote| vote.beneficiary == beneficiary) + .count(); + + Some((votes, kind)) + } + + fn rotate_recent_signers(&mut self) { + if self.recent_signers.len() >= (self.signers.len() / 2) + 1 { + self.recent_signers.pop_back(); + } + } + + fn remove_all_votes_from(&mut self, beneficiary: Address) { + self.votes = std::mem::replace(&mut self.votes, HashMap::new()) + .into_iter() + .filter(|(v, _t)| v.signer != beneficiary && v.beneficiary != beneficiary) + .collect(); + } } diff --git a/ethcore/src/engines/clique/mod.rs b/ethcore/src/engines/clique/mod.rs index c0fad8e78f8..1839f1f8279 100644 --- a/ethcore/src/engines/clique/mod.rs +++ b/ethcore/src/engines/clique/mod.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Implementation of the Clique PoA Engine. //! @@ -57,21 +57,23 @@ /// 6. We call `Clique::on_seal_block()` which will allow us to modify the block header during seal generation. /// 7. Finally, `Clique::verify_local_seal()` is called. After this, the syncing code path will be followed /// in order to import the new block. - use std::cmp; -use std::collections::HashMap; -use std::collections::VecDeque; -use std::sync::{Arc, Weak}; -use std::thread; -use std::time; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use std::{ + collections::{HashMap, VecDeque}, + sync::{Arc, Weak}, + thread, time, + time::{Duration, Instant, SystemTime, UNIX_EPOCH}, +}; +use super::signer::EngineSigner; use block::ExecutedBlock; -use client::{BlockId, EngineClient}; -use engines::clique::util::{extract_signers, recover_creator}; -use engines::{Engine, EngineError, Seal}; +use client::{traits::ForceUpdateSealing, BlockId, EngineClient}; +use engines::{ + clique::util::{extract_signers, recover_creator}, + Engine, EngineError, Seal, +}; use error::{BlockError, Error}; -use ethereum_types::{Address, H64, H160, H256, U256}; +use ethereum_types::{Address, H160, H256, H64, U256}; use ethkey::Signature; use hash::KECCAK_EMPTY_LIST_RLP; use itertools::Itertools; @@ -79,21 +81,17 @@ use lru_cache::LruCache; use machine::{Call, EthereumMachine}; use parking_lot::RwLock; use rand::Rng; -use super::signer::EngineSigner; -use unexpected::{Mismatch, OutOfBounds}; -use types::BlockNumber; -use types::header::{ExtendedHeader, Header}; - -#[cfg(not(feature = "time_checked_add"))] use time_utils::CheckedSystemTime; +use types::{ + header::{ExtendedHeader, Header}, + BlockNumber, +}; +use unexpected::{Mismatch, OutOfBounds}; -use self::block_state::CliqueBlockState; -use self::params::CliqueParams; -use self::step_service::StepService; +use self::{block_state::CliqueBlockState, params::CliqueParams}; -mod params; mod block_state; -mod step_service; +mod params; mod util; // TODO(niklasad1): extract tester types into a separate mod to be shared in the code base @@ -132,637 +130,683 @@ pub const STATE_CACHE_NUM: usize = 128; /// Vote to add or remove the beneficiary #[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] pub enum VoteType { - Add, - Remove, + Add, + Remove, } impl VoteType { - /// Try to construct a `Vote` from a nonce - pub fn from_nonce(nonce: H64) -> Result { - if nonce == NONCE_AUTH_VOTE { - Ok(VoteType::Add) - } else if nonce == NONCE_DROP_VOTE { - Ok(VoteType::Remove) - } else { - Err(EngineError::CliqueInvalidNonce(nonce))? - } - } - - /// Get the rlp encoding of the vote - pub fn as_rlp(&self) -> Vec> { - match self { - VoteType::Add => vec![rlp::encode(&NULL_MIXHASH), rlp::encode(&NONCE_AUTH_VOTE)], - VoteType::Remove => vec![rlp::encode(&NULL_MIXHASH), rlp::encode(&NONCE_DROP_VOTE)], - } - } + /// Try to construct a `Vote` from a nonce + pub fn from_nonce(nonce: H64) -> Result { + if nonce == NONCE_AUTH_VOTE { + Ok(VoteType::Add) + } else if nonce == NONCE_DROP_VOTE { + Ok(VoteType::Remove) + } else { + Err(EngineError::CliqueInvalidNonce(nonce))? + } + } + + /// Get the rlp encoding of the vote + pub fn as_rlp(&self) -> Vec> { + match self { + VoteType::Add => vec![rlp::encode(&NULL_MIXHASH), rlp::encode(&NONCE_AUTH_VOTE)], + VoteType::Remove => vec![rlp::encode(&NULL_MIXHASH), rlp::encode(&NONCE_DROP_VOTE)], + } + } } /// Clique Engine implementation // block_state_by_hash -> block state indexed by header hash. #[cfg(not(test))] pub struct Clique { - epoch_length: u64, - period: u64, - machine: EthereumMachine, - client: RwLock>>, - block_state_by_hash: RwLock>, - proposals: RwLock>, - signer: RwLock>>, - step_service: Option>, + epoch_length: u64, + period: u64, + machine: EthereumMachine, + client: RwLock>>, + block_state_by_hash: RwLock>, + proposals: RwLock>, + signer: RwLock>>, } #[cfg(test)] /// Test version of `CliqueEngine` to make all fields public pub struct Clique { - pub epoch_length: u64, - pub period: u64, - pub machine: EthereumMachine, - pub client: RwLock>>, - pub block_state_by_hash: RwLock>, - pub proposals: RwLock>, - pub signer: RwLock>>, - pub step_service: Option>, + pub epoch_length: u64, + pub period: u64, + pub machine: EthereumMachine, + pub client: RwLock>>, + pub block_state_by_hash: RwLock>, + pub proposals: RwLock>, + pub signer: RwLock>>, } impl Clique { - /// Initialize Clique engine from empty state. - pub fn new(our_params: CliqueParams, machine: EthereumMachine) -> Result, Error> { - let mut engine = Clique { - epoch_length: our_params.epoch, - period: our_params.period, - client: Default::default(), - block_state_by_hash: RwLock::new(LruCache::new(STATE_CACHE_NUM)), - proposals: Default::default(), - signer: Default::default(), - machine, - step_service: None, - }; - - let res = Arc::new(engine); - - if our_params.period > 0 { - engine.step_service = Some(StepService::start(Arc::downgrade(&res) as Weak>)); - } - - Ok(res) - } - - #[cfg(test)] - /// Initialize test variant of `CliqueEngine`, - /// Note we need to `mock` the miner and it is introduced to test block verification to trigger new blocks - /// to mainly test consensus edge cases - pub fn with_test(epoch_length: u64, period: u64) -> Self { - use spec::Spec; - - Self { - epoch_length, - period, - client: Default::default(), - block_state_by_hash: RwLock::new(LruCache::new(STATE_CACHE_NUM)), - proposals: Default::default(), - signer: Default::default(), - machine: Spec::new_test_machine(), - step_service: None, - } - } - - fn sign_header(&self, header: &Header) -> Result<(Signature, H256), Error> { - - match self.signer.read().as_ref() { - None => { - Err(EngineError::RequiresSigner)? - } - Some(signer) => { - let digest = header.hash(); - match signer.sign(digest) { - Ok(sig) => Ok((sig, digest)), - Err(e) => Err(EngineError::Custom(e.into()))?, - } - } - } - } - - /// Construct an new state from given checkpoint header. - fn new_checkpoint_state(&self, header: &Header) -> Result { - debug_assert_eq!(header.number() % self.epoch_length, 0); - - let mut state = CliqueBlockState::new( - extract_signers(header)?); - - // TODO(niklasad1): refactor to perform this check in the `CliqueBlockState` constructor instead - state.calc_next_timestamp(header.timestamp(), self.period)?; - - Ok(state) - } - - fn state_no_backfill(&self, hash: &H256) -> Option { - self.block_state_by_hash.write().get_mut(hash).cloned() - } - - /// Get `CliqueBlockState` for given header, backfill from last checkpoint if needed. - fn state(&self, header: &Header) -> Result { - let mut block_state_by_hash = self.block_state_by_hash.write(); - if let Some(state) = block_state_by_hash.get_mut(&header.hash()) { - return Ok(state.clone()); - } - // If we are looking for an checkpoint block state, we can directly reconstruct it. - if header.number() % self.epoch_length == 0 { - let state = self.new_checkpoint_state(header)?; - block_state_by_hash.insert(header.hash(), state.clone()); - return Ok(state); - } - // BlockState is not found in memory, which means we need to reconstruct state from last checkpoint. - match self.client.read().as_ref().and_then(|w| w.upgrade()) { - None => { - return Err(EngineError::RequiresClient)?; - } - Some(c) => { - let last_checkpoint_number = header.number() - header.number() % self.epoch_length as u64; - debug_assert_ne!(last_checkpoint_number, header.number()); - - // Catching up state, note that we don't really store block state for intermediary blocks, - // for speed. - let backfill_start = time::Instant::now(); - trace!(target: "engine", + /// Initialize Clique engine from empty state. + pub fn new(params: CliqueParams, machine: EthereumMachine) -> Result, Error> { + /// Step Clique at most every 2 seconds + const SEALING_FREQ: Duration = Duration::from_secs(2); + + let engine = Clique { + epoch_length: params.epoch, + period: params.period, + client: Default::default(), + block_state_by_hash: RwLock::new(LruCache::new(STATE_CACHE_NUM)), + proposals: Default::default(), + signer: Default::default(), + machine, + }; + let engine = Arc::new(engine); + let weak_eng = Arc::downgrade(&engine); + + thread::Builder::new() + .name("StepService".into()) + .spawn(move || loop { + let next_step_at = Instant::now() + SEALING_FREQ; + trace!(target: "miner", "StepService: triggering sealing"); + if let Some(eng) = weak_eng.upgrade() { + eng.step() + } else { + warn!(target: "shutdown", "StepService: engine is dropped; exiting."); + break; + } + + let now = Instant::now(); + if now < next_step_at { + thread::sleep(next_step_at - now); + } + })?; + Ok(engine) + } + + #[cfg(test)] + /// Initialize test variant of `CliqueEngine`, + /// Note we need to `mock` the miner and it is introduced to test block verification to trigger new blocks + /// to mainly test consensus edge cases + pub fn with_test(epoch_length: u64, period: u64) -> Self { + use spec::Spec; + + Self { + epoch_length, + period, + client: Default::default(), + block_state_by_hash: RwLock::new(LruCache::new(STATE_CACHE_NUM)), + proposals: Default::default(), + signer: Default::default(), + machine: Spec::new_test_machine(), + } + } + + fn sign_header(&self, header: &Header) -> Result<(Signature, H256), Error> { + match self.signer.read().as_ref() { + None => Err(EngineError::RequiresSigner)?, + Some(signer) => { + let digest = header.hash(); + match signer.sign(digest) { + Ok(sig) => Ok((sig, digest)), + Err(e) => Err(EngineError::Custom(e.into()))?, + } + } + } + } + + /// Construct an new state from given checkpoint header. + fn new_checkpoint_state(&self, header: &Header) -> Result { + debug_assert_eq!(header.number() % self.epoch_length, 0); + + let mut state = CliqueBlockState::new(extract_signers(header)?); + + // TODO(niklasad1): refactor to perform this check in the `CliqueBlockState` constructor instead + state.calc_next_timestamp(header.timestamp(), self.period)?; + + Ok(state) + } + + fn state_no_backfill(&self, hash: &H256) -> Option { + self.block_state_by_hash.write().get_mut(hash).cloned() + } + + /// Get `CliqueBlockState` for given header, backfill from last checkpoint if needed. + fn state(&self, header: &Header) -> Result { + let mut block_state_by_hash = self.block_state_by_hash.write(); + if let Some(state) = block_state_by_hash.get_mut(&header.hash()) { + return Ok(state.clone()); + } + // If we are looking for an checkpoint block state, we can directly reconstruct it. + if header.number() % self.epoch_length == 0 { + let state = self.new_checkpoint_state(header)?; + block_state_by_hash.insert(header.hash(), state.clone()); + return Ok(state); + } + // BlockState is not found in memory, which means we need to reconstruct state from last checkpoint. + match self.client.read().as_ref().and_then(|w| w.upgrade()) { + None => { + return Err(EngineError::RequiresClient)?; + } + Some(c) => { + let last_checkpoint_number = + header.number() - header.number() % self.epoch_length as u64; + debug_assert_ne!(last_checkpoint_number, header.number()); + + // Catching up state, note that we don't really store block state for intermediary blocks, + // for speed. + let backfill_start = time::Instant::now(); + trace!(target: "engine", "Back-filling block state. last_checkpoint_number: {}, target: {}({}).", last_checkpoint_number, header.number(), header.hash()); - let mut chain: &mut VecDeque
= &mut VecDeque::with_capacity( - (header.number() - last_checkpoint_number + 1) as usize); - - // Put ourselves in. - chain.push_front(header.clone()); - - // populate chain to last checkpoint - loop { - let (last_parent_hash, last_num) = { - let l = chain.front().expect("chain has at least one element; qed"); - (*l.parent_hash(), l.number()) - }; - - if last_num == last_checkpoint_number + 1 { - break; - } - match c.block_header(BlockId::Hash(last_parent_hash)) { - None => { - return Err(BlockError::UnknownParent(last_parent_hash))?; - } - Some(next) => { - chain.push_front(next.decode()?); - } - } - } - - // Get the state for last checkpoint. - let last_checkpoint_hash = *chain.front() - .expect("chain has at least one element; qed") - .parent_hash(); - - let last_checkpoint_header = match c.block_header(BlockId::Hash(last_checkpoint_hash)) { - None => return Err(EngineError::CliqueMissingCheckpoint(last_checkpoint_hash))?, - Some(header) => header.decode()?, - }; - - let last_checkpoint_state = match block_state_by_hash.get_mut(&last_checkpoint_hash) { - Some(state) => state.clone(), - None => self.new_checkpoint_state(&last_checkpoint_header)?, - }; - - block_state_by_hash.insert(last_checkpoint_header.hash(), last_checkpoint_state.clone()); - - // Backfill! - let mut new_state = last_checkpoint_state.clone(); - for item in chain { - new_state.apply(item, false)?; - } - new_state.calc_next_timestamp(header.timestamp(), self.period)?; - block_state_by_hash.insert(header.hash(), new_state.clone()); - - let elapsed = backfill_start.elapsed(); - trace!(target: "engine", "Back-filling succeed, took {} ms.", elapsed.as_millis()); - Ok(new_state) - } - } - } + let chain: &mut VecDeque
= &mut VecDeque::with_capacity( + (header.number() - last_checkpoint_number + 1) as usize, + ); + + // Put ourselves in. + chain.push_front(header.clone()); + + // populate chain to last checkpoint + loop { + let (last_parent_hash, last_num) = { + let l = chain.front().expect("chain has at least one element; qed"); + (*l.parent_hash(), l.number()) + }; + + if last_num == last_checkpoint_number + 1 { + break; + } + match c.block_header(BlockId::Hash(last_parent_hash)) { + None => { + return Err(BlockError::UnknownParent(last_parent_hash))?; + } + Some(next) => { + chain.push_front(next.decode()?); + } + } + } + + // Get the state for last checkpoint. + let last_checkpoint_hash = *chain + .front() + .expect("chain has at least one element; qed") + .parent_hash(); + + let last_checkpoint_header = + match c.block_header(BlockId::Hash(last_checkpoint_hash)) { + None => { + return Err(EngineError::CliqueMissingCheckpoint(last_checkpoint_hash))? + } + Some(header) => header.decode()?, + }; + + let last_checkpoint_state = match block_state_by_hash.get_mut(&last_checkpoint_hash) + { + Some(state) => state.clone(), + None => self.new_checkpoint_state(&last_checkpoint_header)?, + }; + + block_state_by_hash + .insert(last_checkpoint_header.hash(), last_checkpoint_state.clone()); + + // Backfill! + let mut new_state = last_checkpoint_state.clone(); + for item in chain { + new_state.apply(item, false)?; + } + new_state.calc_next_timestamp(header.timestamp(), self.period)?; + block_state_by_hash.insert(header.hash(), new_state.clone()); + + let elapsed = backfill_start.elapsed(); + trace!(target: "engine", "Back-filling succeed, took {} ms.", elapsed.as_millis()); + Ok(new_state) + } + } + } } impl Engine for Clique { - fn name(&self) -> &str { "Clique" } - - fn machine(&self) -> &EthereumMachine { &self.machine } - - // Clique use same fields, nonce + mixHash - fn seal_fields(&self, _header: &Header) -> usize { 2 } - - fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 0 } - - fn on_new_block( - &self, - _block: &mut ExecutedBlock, - _epoch_begin: bool, - _ancestry: &mut Iterator, - ) -> Result<(), Error> { - Ok(()) - } - - // Clique has no block reward. - fn on_close_block(&self, _block: &mut ExecutedBlock) -> Result<(), Error> { - Ok(()) - } - - fn on_seal_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { - trace!(target: "engine", "on_seal_block"); - - let header = &mut block.header; - - let state = self.state_no_backfill(header.parent_hash()) - .ok_or_else(|| BlockError::UnknownParent(*header.parent_hash()))?; - - let is_checkpoint = header.number() % self.epoch_length == 0; - - header.set_author(NULL_AUTHOR); - - // Cast a random Vote if not checkpoint - if !is_checkpoint { - // TODO(niklasad1): this will always be false because `proposals` is never written to - let votes = self.proposals.read().iter() - .filter(|(address, vote_type)| state.is_valid_vote(*address, **vote_type)) - .map(|(address, vote_type)| (*address, *vote_type)) - .collect_vec(); - - if !votes.is_empty() { - // Pick a random vote. - let random_vote = rand::thread_rng().gen_range(0 as usize, votes.len()); - let (beneficiary, vote_type) = votes[random_vote]; - - trace!(target: "engine", "Casting vote: beneficiary {}, type {:?} ", beneficiary, vote_type); - - header.set_author(beneficiary); - header.set_seal(vote_type.as_rlp()); - } - } - - // Work on clique seal. - - let mut seal: Vec = Vec::with_capacity(VANITY_LENGTH + SIGNATURE_LENGTH); - - // At this point, extra_data should only contain miner vanity. - if header.extra_data().len() != VANITY_LENGTH { - Err(BlockError::ExtraDataOutOfBounds(OutOfBounds { - min: Some(VANITY_LENGTH), - max: Some(VANITY_LENGTH), - found: header.extra_data().len() - }))?; - } - // vanity - { - seal.extend_from_slice(&header.extra_data()[0..VANITY_LENGTH]); - } - - // If we are building an checkpoint block, add all signers now. - if is_checkpoint { - seal.reserve(state.signers().len() * 20); - state.signers().iter().foreach(|addr| { - seal.extend_from_slice(&addr[..]); - }); - } - - header.set_extra_data(seal.clone()); - - // append signature onto extra_data - let (sig, _msg) = self.sign_header(&header)?; - seal.extend_from_slice(&sig[..]); - header.set_extra_data(seal.clone()); - - header.compute_hash(); - - // locally sealed block don't go through valid_block_family(), so we have to record state here. - let mut new_state = state.clone(); - new_state.apply(&header, is_checkpoint)?; - new_state.calc_next_timestamp(header.timestamp(), self.period)?; - self.block_state_by_hash.write().insert(header.hash(), new_state); - - trace!(target: "engine", "on_seal_block: finished, final header: {:?}", header); - - Ok(()) - } - - /// Clique doesn't require external work to seal, so we always return true here. - fn seals_internally(&self) -> Option { - Some(true) - } - - /// Returns if we are ready to seal, the real sealing (signing extra_data) is actually done in `on_seal_block()`. - fn generate_seal(&self, block: &ExecutedBlock, parent: &Header) -> Seal { - trace!(target: "engine", "tried to generate_seal"); - let null_seal = util::null_seal(); - - if block.header.number() == 0 { - trace!(target: "engine", "attempted to seal genesis block"); - return Seal::None; - } - - // if sealing period is 0, and not an checkpoint block, refuse to seal - if self.period == 0 { - if block.transactions.is_empty() && block.header.number() % self.epoch_length != 0 { - return Seal::None; - } - return Seal::Regular(null_seal); - } - - // Check we actually have authority to seal. - if let Some(author) = self.signer.read().as_ref().map(|x| x.address()) { - - // ensure the voting state exists - match self.state(&parent) { - Err(e) => { - warn!(target: "engine", "generate_seal: can't get parent state(number: {}, hash: {}): {} ", + fn name(&self) -> &str { + "Clique" + } + + fn machine(&self) -> &EthereumMachine { + &self.machine + } + + // Clique use same fields, nonce + mixHash + fn seal_fields(&self, _header: &Header) -> usize { + 2 + } + + fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { + 0 + } + + fn on_new_block( + &self, + _block: &mut ExecutedBlock, + _epoch_begin: bool, + _ancestry: &mut dyn Iterator, + ) -> Result<(), Error> { + Ok(()) + } + + // Clique has no block reward. + fn on_close_block(&self, _block: &mut ExecutedBlock) -> Result<(), Error> { + Ok(()) + } + + fn on_seal_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { + trace!(target: "engine", "on_seal_block"); + + let header = &mut block.header; + + let state = self + .state_no_backfill(header.parent_hash()) + .ok_or_else(|| BlockError::UnknownParent(*header.parent_hash()))?; + + let is_checkpoint = header.number() % self.epoch_length == 0; + + header.set_author(NULL_AUTHOR); + + // Cast a random Vote if not checkpoint + if !is_checkpoint { + // TODO(niklasad1): this will always be false because `proposals` is never written to + let votes = self + .proposals + .read() + .iter() + .filter(|(address, vote_type)| state.is_valid_vote(*address, **vote_type)) + .map(|(address, vote_type)| (*address, *vote_type)) + .collect_vec(); + + if !votes.is_empty() { + // Pick a random vote. + let random_vote = rand::thread_rng().gen_range(0 as usize, votes.len()); + let (beneficiary, vote_type) = votes[random_vote]; + + trace!(target: "engine", "Casting vote: beneficiary {}, type {:?} ", beneficiary, vote_type); + + header.set_author(beneficiary); + header.set_seal(vote_type.as_rlp()); + } + } + + // Work on clique seal. + + let mut seal: Vec = Vec::with_capacity(VANITY_LENGTH + SIGNATURE_LENGTH); + + // At this point, extra_data should only contain miner vanity. + if header.extra_data().len() != VANITY_LENGTH { + Err(BlockError::ExtraDataOutOfBounds(OutOfBounds { + min: Some(VANITY_LENGTH), + max: Some(VANITY_LENGTH), + found: header.extra_data().len(), + }))?; + } + // vanity + { + seal.extend_from_slice(&header.extra_data()[0..VANITY_LENGTH]); + } + + // If we are building an checkpoint block, add all signers now. + if is_checkpoint { + seal.reserve(state.signers().len() * 20); + state.signers().iter().foreach(|addr| { + seal.extend_from_slice(&addr[..]); + }); + } + + header.set_extra_data(seal.clone()); + + // append signature onto extra_data + let (sig, _msg) = self.sign_header(&header)?; + seal.extend_from_slice(&sig[..]); + header.set_extra_data(seal.clone()); + + header.compute_hash(); + + // locally sealed block don't go through valid_block_family(), so we have to record state here. + let mut new_state = state.clone(); + new_state.apply(&header, is_checkpoint)?; + new_state.calc_next_timestamp(header.timestamp(), self.period)?; + self.block_state_by_hash + .write() + .insert(header.hash(), new_state); + + trace!(target: "engine", "on_seal_block: finished, final header: {:?}", header); + + Ok(()) + } + + /// Clique doesn't require external work to seal, so we always return true here. + fn seals_internally(&self) -> Option { + Some(true) + } + + /// Returns if we are ready to seal, the real sealing (signing extra_data) is actually done in `on_seal_block()`. + fn generate_seal(&self, block: &ExecutedBlock, parent: &Header) -> Seal { + trace!(target: "engine", "tried to generate_seal"); + let null_seal = util::null_seal(); + + if block.header.number() == 0 { + trace!(target: "engine", "attempted to seal genesis block"); + return Seal::None; + } + + // if sealing period is 0, and not an checkpoint block, refuse to seal + if self.period == 0 { + if block.transactions.is_empty() && block.header.number() % self.epoch_length != 0 { + return Seal::None; + } + return Seal::Regular(null_seal); + } + + // Check we actually have authority to seal. + if let Some(author) = self.signer.read().as_ref().map(|x| x.address()) { + // ensure the voting state exists + match self.state(&parent) { + Err(e) => { + warn!(target: "engine", "generate_seal: can't get parent state(number: {}, hash: {}): {} ", parent.number(), parent.hash(), e); - return Seal::None; - } - Ok(state) => { - // Are we authorized to seal? - if !state.is_authorized(&author) { - trace!(target: "engine", "generate_seal: Not authorized to sign right now."); - // wait for one third of period to try again. - thread::sleep(Duration::from_secs(self.period / 3 + 1)); - return Seal::None; - } - - let inturn = state.is_inturn(block.header.number(), &author); - - let now = SystemTime::now(); - - let limit = match inturn { - true => state.next_timestamp_inturn.unwrap_or(now), - false => state.next_timestamp_noturn.unwrap_or(now), - }; - - // Wait for the right moment. - if now < limit { - trace!(target: "engine", + return Seal::None; + } + Ok(state) => { + // Are we authorized to seal? + if !state.is_authorized(&author) { + trace!(target: "engine", "generate_seal: Not authorized to sign right now."); + // wait for one third of period to try again. + thread::sleep(Duration::from_secs(self.period / 3 + 1)); + return Seal::None; + } + + let inturn = state.is_inturn(block.header.number(), &author); + + let now = SystemTime::now(); + + let limit = match inturn { + true => state.next_timestamp_inturn.unwrap_or(now), + false => state.next_timestamp_noturn.unwrap_or(now), + }; + + // Wait for the right moment. + if now < limit { + trace!(target: "engine", "generate_seal: sleeping to sign: inturn: {}, now: {:?}, to: {:?}.", inturn, now, limit); - match limit.duration_since(SystemTime::now()) { - Ok(duration) => { - thread::sleep(duration); - }, - Err(e) => { - warn!(target:"engine", "generate_seal: unable to sleep, err: {}", e); - return Seal::None; - } - } - } - - trace!(target: "engine", "generate_seal: seal ready for block {}, txs: {}.", + match limit.duration_since(SystemTime::now()) { + Ok(duration) => { + thread::sleep(duration); + } + Err(e) => { + warn!(target:"engine", "generate_seal: unable to sleep, err: {}", e); + return Seal::None; + } + } + } + + trace!(target: "engine", "generate_seal: seal ready for block {}, txs: {}.", block.header.number(), block.transactions.len()); - return Seal::Regular(null_seal); - } - } - } - Seal::None - } - - fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> { Ok(()) } - - fn verify_block_basic(&self, header: &Header) -> Result<(), Error> { - // Largely same as https://github.com/ethereum/go-ethereum/blob/master/consensus/clique/clique.go#L275 - - // Ignore genesis block. - if header.number() == 0 { - return Ok(()); - } - - // Don't waste time checking blocks from the future - { - let limit = SystemTime::now().checked_add(Duration::from_secs(self.period)) - .ok_or(BlockError::TimestampOverflow)?; - - // This should succeed under the contraints that the system clock works - let limit_as_dur = limit.duration_since(UNIX_EPOCH).map_err(|e| { - Box::new(format!("Converting SystemTime to Duration failed: {}", e)) - })?; - - let hdr = Duration::from_secs(header.timestamp()); - if hdr > limit_as_dur { - let found = UNIX_EPOCH.checked_add(hdr).ok_or(BlockError::TimestampOverflow)?; - - Err(BlockError::TemporarilyInvalid(OutOfBounds { - min: None, - max: Some(limit), - found, - }))? - } - } - - let is_checkpoint = header.number() % self.epoch_length == 0; - - if is_checkpoint && *header.author() != NULL_AUTHOR { - return Err(EngineError::CliqueWrongAuthorCheckpoint(Mismatch { - expected: 0.into(), - found: *header.author(), - }))?; - } - - let seal_fields = header.decode_seal::>()?; - if seal_fields.len() != 2 { - Err(BlockError::InvalidSealArity(Mismatch { - expected: 2, - found: seal_fields.len(), - }))? - } - - let mixhash: H256 = seal_fields[0].into(); - let nonce: H64 = seal_fields[1].into(); - - // Nonce must be 0x00..0 or 0xff..f - if nonce != NONCE_DROP_VOTE && nonce != NONCE_AUTH_VOTE { - Err(EngineError::CliqueInvalidNonce(nonce))?; - } - - if is_checkpoint && nonce != NULL_NONCE { - Err(EngineError::CliqueInvalidNonce(nonce))?; - } - - // Ensure that the mix digest is zero as Clique don't have fork protection currently - if mixhash != NULL_MIXHASH { - Err(BlockError::MismatchedH256SealElement(Mismatch { - expected: NULL_MIXHASH, - found: mixhash, - }))? - } - - let extra_data_len = header.extra_data().len(); - - if extra_data_len < VANITY_LENGTH { - Err(EngineError::CliqueMissingVanity)? - } - - if extra_data_len < VANITY_LENGTH + SIGNATURE_LENGTH { - Err(EngineError::CliqueMissingSignature)? - } - - let signers = extra_data_len - (VANITY_LENGTH + SIGNATURE_LENGTH); - - // Checkpoint blocks must at least contain one signer - if is_checkpoint && signers == 0 { - Err(EngineError::CliqueCheckpointNoSigner)? - } - - // Addresses must be be divisable by 20 - if is_checkpoint && signers % ADDRESS_LENGTH != 0 { - Err(EngineError::CliqueCheckpointInvalidSigners(signers))? - } - - // Ensure that the block doesn't contain any uncles which are meaningless in PoA - if *header.uncles_hash() != NULL_UNCLES_HASH { - Err(BlockError::InvalidUnclesHash(Mismatch { - expected: NULL_UNCLES_HASH, - found: *header.uncles_hash(), - }))? - } - - // Ensure that the block's difficulty is meaningful (may not be correct at this point) - if *header.difficulty() != DIFF_INTURN && *header.difficulty() != DIFF_NOTURN { - Err(BlockError::DifficultyOutOfBounds(OutOfBounds { - min: Some(DIFF_NOTURN), - max: Some(DIFF_INTURN), - found: *header.difficulty(), - }))? - } - - // All basic checks passed, continue to next phase - Ok(()) - } - - fn verify_block_unordered(&self, _header: &Header) -> Result<(), Error> { - // Nothing to check here. - Ok(()) - } - - /// Verify block family by looking up parent state (backfill if needed), then try to apply current header. - /// see https://github.com/ethereum/go-ethereum/blob/master/consensus/clique/clique.go#L338 - fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> { - // Ignore genesis block. - if header.number() == 0 { - return Ok(()); - } - - // parent sanity check - if parent.hash() != *header.parent_hash() || header.number() != parent.number() + 1 { - Err(BlockError::UnknownParent(parent.hash()))? - } - - // Ensure that the block's timestamp isn't too close to it's parent - let limit = parent.timestamp().saturating_add(self.period); - if limit > header.timestamp() { - let max = UNIX_EPOCH.checked_add(Duration::from_secs(header.timestamp())); - let found = UNIX_EPOCH.checked_add(Duration::from_secs(limit)) - .ok_or(BlockError::TimestampOverflow)?; - - Err(BlockError::InvalidTimestamp(OutOfBounds { - min: None, - max, - found, - }))? - } - - // Retrieve the parent state - let parent_state = self.state(&parent)?; - // Try to apply current state, apply() will further check signer and recent signer. - let mut new_state = parent_state.clone(); - new_state.apply(header, header.number() % self.epoch_length == 0)?; - new_state.calc_next_timestamp(header.timestamp(), self.period)?; - self.block_state_by_hash.write().insert(header.hash(), new_state); - - Ok(()) - } - - fn genesis_epoch_data(&self, header: &Header, _call: &Call) -> Result, String> { - let mut state = self.new_checkpoint_state(header).expect("Unable to parse genesis data."); - state.calc_next_timestamp(header.timestamp(), self.period).map_err(|e| format!("{}", e))?; - self.block_state_by_hash.write().insert(header.hash(), state); - - // no proof. - Ok(Vec::new()) - } - - // Our task here is to set difficulty - fn populate_from_parent(&self, header: &mut Header, parent: &Header) { - // TODO(https://github.com/paritytech/parity-ethereum/issues/10410): this is a horrible hack, - // it is due to the fact that enact and miner both use OpenBlock::new() which will both call - // this function. more refactoring is definitely needed. - if header.extra_data().len() < VANITY_LENGTH + SIGNATURE_LENGTH { - trace!(target: "engine", "populate_from_parent in sealing"); - - // It's unclear how to prevent creating new blocks unless we are authorized, the best way (and geth does this too) - // it's just to ignore setting an correct difficulty here, we will check authorization in next step in generate_seal anyway. - if let Some(signer) = self.signer.read().as_ref() { - let state = match self.state(&parent) { - Err(e) => { - trace!(target: "engine", "populate_from_parent: Unable to find parent state: {}, ignored.", e); - return; - } - Ok(state) => state, - }; - - if state.is_authorized(&signer.address()) { - if state.is_inturn(header.number(), &signer.address()) { - header.set_difficulty(DIFF_INTURN); - } else { - header.set_difficulty(DIFF_NOTURN); - } - } - } else { - trace!(target: "engine", "populate_from_parent: no signer registered"); - } - } - } - - fn set_signer(&self, signer: Box) { - trace!(target: "engine", "set_signer: {}", signer.address()); - *self.signer.write() = Some(signer); - } - - fn register_client(&self, client: Weak) { - *self.client.write() = Some(client.clone()); - } - - fn step(&self) { - if self.signer.read().is_some() { - if let Some(ref weak) = *self.client.read() { - if let Some(c) = weak.upgrade() { - c.update_sealing(); - } - } - } - } - - fn stop(&mut self) { - if let Some(mut s) = self.step_service.as_mut() { - Arc::get_mut(&mut s).map(|x| x.stop()); - } else { - warn!(target: "engine", "Stopping `CliqueStepService` failed requires mutable access"); - } - } - - /// Clique timestamp is set to parent + period , or current time which ever is higher. - fn open_block_header_timestamp(&self, parent_timestamp: u64) -> u64 { - let now = time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap_or_default(); - cmp::max(now.as_secs() as u64, parent_timestamp.saturating_add(self.period)) - } - - fn is_timestamp_valid(&self, header_timestamp: u64, parent_timestamp: u64) -> bool { - header_timestamp >= parent_timestamp.saturating_add(self.period) - } - - fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice { - super::total_difficulty_fork_choice(new, current) - } - - // Clique uses the author field for voting, the real author is hidden in the `extra_data` field. - // So when executing tx's (like in `enact()`) we want to use the executive author - fn executive_author(&self, header: &Header) -> Result { - recover_creator(header) - } + return Seal::Regular(null_seal); + } + } + } + Seal::None + } + + fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> { + Ok(()) + } + + fn verify_block_basic(&self, header: &Header) -> Result<(), Error> { + // Largely same as https://github.com/ethereum/go-ethereum/blob/master/consensus/clique/clique.go#L275 + + // Ignore genesis block. + if header.number() == 0 { + return Ok(()); + } + + // Don't waste time checking blocks from the future + { + let limit = + CheckedSystemTime::checked_add(SystemTime::now(), Duration::from_secs(self.period)) + .ok_or(BlockError::TimestampOverflow)?; + + // This should succeed under the contraints that the system clock works + let limit_as_dur = limit.duration_since(UNIX_EPOCH).map_err(|e| { + Box::new(format!("Converting SystemTime to Duration failed: {}", e)) + })?; + + let hdr = Duration::from_secs(header.timestamp()); + if hdr > limit_as_dur { + let found = CheckedSystemTime::checked_add(UNIX_EPOCH, hdr) + .ok_or(BlockError::TimestampOverflow)?; + + Err(BlockError::TemporarilyInvalid(OutOfBounds { + min: None, + max: Some(limit), + found, + }))? + } + } + + let is_checkpoint = header.number() % self.epoch_length == 0; + + if is_checkpoint && *header.author() != NULL_AUTHOR { + return Err(EngineError::CliqueWrongAuthorCheckpoint(Mismatch { + expected: 0.into(), + found: *header.author(), + }))?; + } + + let seal_fields = header.decode_seal::>()?; + if seal_fields.len() != 2 { + Err(BlockError::InvalidSealArity(Mismatch { + expected: 2, + found: seal_fields.len(), + }))? + } + + let mixhash: H256 = seal_fields[0].into(); + let nonce: H64 = seal_fields[1].into(); + + // Nonce must be 0x00..0 or 0xff..f + if nonce != NONCE_DROP_VOTE && nonce != NONCE_AUTH_VOTE { + Err(EngineError::CliqueInvalidNonce(nonce))?; + } + + if is_checkpoint && nonce != NULL_NONCE { + Err(EngineError::CliqueInvalidNonce(nonce))?; + } + + // Ensure that the mix digest is zero as Clique don't have fork protection currently + if mixhash != NULL_MIXHASH { + Err(BlockError::MismatchedH256SealElement(Mismatch { + expected: NULL_MIXHASH, + found: mixhash, + }))? + } + + let extra_data_len = header.extra_data().len(); + + if extra_data_len < VANITY_LENGTH { + Err(EngineError::CliqueMissingVanity)? + } + + if extra_data_len < VANITY_LENGTH + SIGNATURE_LENGTH { + Err(EngineError::CliqueMissingSignature)? + } + + let signers = extra_data_len - (VANITY_LENGTH + SIGNATURE_LENGTH); + + // Checkpoint blocks must at least contain one signer + if is_checkpoint && signers == 0 { + Err(EngineError::CliqueCheckpointNoSigner)? + } + + // Addresses must be be divisable by 20 + if is_checkpoint && signers % ADDRESS_LENGTH != 0 { + Err(EngineError::CliqueCheckpointInvalidSigners(signers))? + } + + // Ensure that the block doesn't contain any uncles which are meaningless in PoA + if *header.uncles_hash() != NULL_UNCLES_HASH { + Err(BlockError::InvalidUnclesHash(Mismatch { + expected: NULL_UNCLES_HASH, + found: *header.uncles_hash(), + }))? + } + + // Ensure that the block's difficulty is meaningful (may not be correct at this point) + if *header.difficulty() != DIFF_INTURN && *header.difficulty() != DIFF_NOTURN { + Err(BlockError::DifficultyOutOfBounds(OutOfBounds { + min: Some(DIFF_NOTURN), + max: Some(DIFF_INTURN), + found: *header.difficulty(), + }))? + } + + // All basic checks passed, continue to next phase + Ok(()) + } + + fn verify_block_unordered(&self, _header: &Header) -> Result<(), Error> { + // Nothing to check here. + Ok(()) + } + + /// Verify block family by looking up parent state (backfill if needed), then try to apply current header. + /// see https://github.com/ethereum/go-ethereum/blob/master/consensus/clique/clique.go#L338 + fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> { + // Ignore genesis block. + if header.number() == 0 { + return Ok(()); + } + + // parent sanity check + if parent.hash() != *header.parent_hash() || header.number() != parent.number() + 1 { + Err(BlockError::UnknownParent(parent.hash()))? + } + + // Ensure that the block's timestamp isn't too close to it's parent + let limit = parent.timestamp().saturating_add(self.period); + if limit > header.timestamp() { + let max = + CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::from_secs(header.timestamp())); + let found = CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::from_secs(limit)) + .ok_or(BlockError::TimestampOverflow)?; + + Err(BlockError::InvalidTimestamp(OutOfBounds { + min: None, + max, + found, + }))? + } + + // Retrieve the parent state + let parent_state = self.state(&parent)?; + // Try to apply current state, apply() will further check signer and recent signer. + let mut new_state = parent_state.clone(); + new_state.apply(header, header.number() % self.epoch_length == 0)?; + new_state.calc_next_timestamp(header.timestamp(), self.period)?; + self.block_state_by_hash + .write() + .insert(header.hash(), new_state); + + Ok(()) + } + + fn genesis_epoch_data(&self, header: &Header, _call: &Call) -> Result, String> { + let mut state = self + .new_checkpoint_state(header) + .expect("Unable to parse genesis data."); + state + .calc_next_timestamp(header.timestamp(), self.period) + .map_err(|e| format!("{}", e))?; + self.block_state_by_hash + .write() + .insert(header.hash(), state); + + // no proof. + Ok(Vec::new()) + } + + // Our task here is to set difficulty + fn populate_from_parent(&self, header: &mut Header, parent: &Header) { + // TODO(https://github.com/openethereum/openethereum/issues/10410): this is a horrible hack, + // it is due to the fact that enact and miner both use OpenBlock::new() which will both call + // this function. more refactoring is definitely needed. + if header.extra_data().len() < VANITY_LENGTH + SIGNATURE_LENGTH { + trace!(target: "engine", "populate_from_parent in sealing"); + + // It's unclear how to prevent creating new blocks unless we are authorized, the best way (and geth does this too) + // it's just to ignore setting a correct difficulty here, we will check authorization in next step in generate_seal anyway. + if let Some(signer) = self.signer.read().as_ref() { + let state = match self.state(&parent) { + Err(e) => { + trace!(target: "engine", "populate_from_parent: Unable to find parent state: {}, ignored.", e); + return; + } + Ok(state) => state, + }; + + if state.is_authorized(&signer.address()) { + if state.is_inturn(header.number(), &signer.address()) { + header.set_difficulty(DIFF_INTURN); + } else { + header.set_difficulty(DIFF_NOTURN); + } + } + + let zero_padding_len = VANITY_LENGTH - header.extra_data().len(); + if zero_padding_len > 0 { + let mut resized_extra_data = header.extra_data().clone(); + resized_extra_data.resize(VANITY_LENGTH, 0); + header.set_extra_data(resized_extra_data); + } + } else { + trace!(target: "engine", "populate_from_parent: no signer registered"); + } + } + } + + fn set_signer(&self, signer: Box) { + trace!(target: "engine", "set_signer: {}", signer.address()); + *self.signer.write() = Some(signer); + } + + fn register_client(&self, client: Weak) { + *self.client.write() = Some(client.clone()); + } + + fn step(&self) { + if self.signer.read().is_some() { + if let Some(ref weak) = *self.client.read() { + if let Some(c) = weak.upgrade() { + c.update_sealing(ForceUpdateSealing::No); + } + } + } + } + + /// Clique timestamp is set to parent + period , or current time which ever is higher. + fn open_block_header_timestamp(&self, parent_timestamp: u64) -> u64 { + let now = time::SystemTime::now() + .duration_since(time::UNIX_EPOCH) + .unwrap_or_default(); + cmp::max( + now.as_secs() as u64, + parent_timestamp.saturating_add(self.period), + ) + } + + fn is_timestamp_valid(&self, header_timestamp: u64, parent_timestamp: u64) -> bool { + header_timestamp >= parent_timestamp.saturating_add(self.period) + } + + fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice { + super::total_difficulty_fork_choice(new, current) + } + + // Clique uses the author field for voting, the real author is hidden in the `extra_data` field. + // So when executing tx's (like in `enact()`) we want to use the executive author + fn executive_author(&self, header: &Header) -> Result { + recover_creator(header) + } } diff --git a/ethcore/src/engines/clique/params.rs b/ethcore/src/engines/clique/params.rs index e24edfcbac7..1ab5746f391 100644 --- a/ethcore/src/engines/clique/params.rs +++ b/ethcore/src/engines/clique/params.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Clique specific parameters. @@ -20,22 +20,19 @@ use ethjson; /// `Clique` params. pub struct CliqueParams { - /// Period as defined in EIP - pub period: u64, - /// Epoch length as defined in EIP - pub epoch: u64, + /// Period as defined in EIP + pub period: u64, + /// Epoch length as defined in EIP + pub epoch: u64, } impl From for CliqueParams { - fn from(p: ethjson::spec::CliqueParams) -> Self { - let period = p.period.map_or_else(|| 30000 as u64, Into::into); - let epoch = p.epoch.map_or_else(|| 15 as u64, Into::into); + fn from(p: ethjson::spec::CliqueParams) -> Self { + let period = p.period.map_or_else(|| 30000 as u64, Into::into); + let epoch = p.epoch.map_or_else(|| 15 as u64, Into::into); - assert!(epoch > 0); + assert!(epoch > 0); - CliqueParams { - period, - epoch, - } - } + CliqueParams { period, epoch } + } } diff --git a/ethcore/src/engines/clique/step_service.rs b/ethcore/src/engines/clique/step_service.rs deleted file mode 100644 index 7a4b5269d2b..00000000000 --- a/ethcore/src/engines/clique/step_service.rs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - - -use std::sync::Weak; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::time::Duration; -use std::thread; -use std::sync::Arc; - -use engines::Engine; -use machine::Machine; - -/// Service that is managing the engine -pub struct StepService { - shutdown: Arc, - thread: Option>, -} - -impl StepService { - /// Start the `StepService` - pub fn start(engine: Weak>) -> Arc { - let shutdown = Arc::new(AtomicBool::new(false)); - let s = shutdown.clone(); - - let thread = thread::Builder::new() - .name("CliqueStepService".into()) - .spawn(move || { - // startup delay. - thread::sleep(Duration::from_secs(5)); - - loop { - // see if we are in shutdown. - if shutdown.load(Ordering::Acquire) { - trace!(target: "miner", "CliqueStepService: received shutdown signal!"); - break; - } - - trace!(target: "miner", "CliqueStepService: triggering sealing"); - - // Try sealing - engine.upgrade().map(|x| x.step()); - - // Yield - thread::sleep(Duration::from_millis(2000)); - } - trace!(target: "miner", "CliqueStepService: shutdown."); - }).expect("CliqueStepService thread failed"); - - Arc::new(StepService { - shutdown: s, - thread: Some(thread), - }) - } - - /// Stop the `StepService` - pub fn stop(&mut self) { - trace!(target: "miner", "CliqueStepService: shutting down."); - self.shutdown.store(true, Ordering::Release); - if let Some(t) = self.thread.take() { - t.join().expect("CliqueStepService thread panicked!"); - } - } -} diff --git a/ethcore/src/engines/clique/tests.rs b/ethcore/src/engines/clique/tests.rs index c7916192dd8..a243a38931e 100644 --- a/ethcore/src/engines/clique/tests.rs +++ b/ethcore/src/engines/clique/tests.rs @@ -1,804 +1,1336 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Consensus tests for `PoA Clique Engine`, see http://eips.ethereum.org/EIPS/eip-225 for more information +use super::*; use block::*; use engines::Engine; use error::{Error, ErrorKind}; use ethereum_types::{Address, H256}; -use ethkey::{Secret, KeyPair}; +use ethkey::{KeyPair, Secret}; use state_db::StateDB; -use super::*; use test_helpers::get_temp_state_db; -use std::sync::Arc; -use std::collections::HashMap; +use std::{collections::HashMap, sync::Arc}; /// Possible signers pub const SIGNER_TAGS: [char; 6] = ['A', 'B', 'C', 'D', 'E', 'F']; /// Clique block types pub enum CliqueBlockType { - /// Epoch transition block must contain list of signers - Checkpoint, - /// Block with no votes - Empty, - /// Vote - Vote(VoteType), + /// Epoch transition block must contain list of signers + Checkpoint, + /// Block with no votes + Empty, + /// Vote + Vote(VoteType), } /// Clique tester pub struct CliqueTester { - /// Mocked Clique - pub clique: Clique, - /// Mocked genesis state - pub genesis: Header, - /// StateDB - pub db: StateDB, - /// List of signers - pub signers: HashMap, + /// Mocked Clique + pub clique: Clique, + /// Mocked genesis state + pub genesis: Header, + /// StateDB + pub db: StateDB, + /// List of signers + pub signers: HashMap, } impl CliqueTester { - /// Create a `Clique` tester with settings - pub fn with(epoch: u64, period: u64, initial_signers: Vec) -> Self { - assert_eq!(initial_signers.iter().all(|s| SIGNER_TAGS.contains(s)), true, - "Not all the initial signers is in SIGNER_TAGS, possible keys are 'A' ..= 'F'"); - - let clique = Clique::with_test(epoch, period); - let mut genesis = Header::default(); - let mut signers = HashMap::new(); - - let call = |_a, _b| { - unimplemented!("Clique doesn't use Engine::Call"); - }; - - let mut extra_data = vec![0; VANITY_LENGTH]; - - for &signer in SIGNER_TAGS.iter() { - let secret = Secret::from(H256::from(signer as u64)); - let keypair = KeyPair::from_secret(secret).unwrap(); - if initial_signers.contains(&signer) { - extra_data.extend(&*keypair.address()); - } - signers.insert(signer, keypair); - } - - // append dummy signature - extra_data.extend(std::iter::repeat(0).take(SIGNATURE_LENGTH)); - - genesis.set_extra_data(extra_data); - genesis.set_gas_limit(U256::from(0xa00000)); - genesis.set_difficulty(U256::from(1)); - genesis.set_seal(util::null_seal()); - - clique.genesis_epoch_data(&genesis, &call).expect("Create genesis failed"); - Self {clique, genesis, db: get_temp_state_db(), signers} - } - - /// Get difficulty for a given block - pub fn get_difficulty(&self, block_num: BlockNumber, header: &Header, signer: &Address) -> U256 { - let state = self.clique.state(header).unwrap(); - if state.is_inturn(block_num, signer) { - DIFF_INTURN - } else { - DIFF_NOTURN - } - } - - /// Get the state of a given block - // Note, this will read the cache and `will` not work with more than 128 blocks - pub fn get_state_at_block(&self, hash: &H256) -> CliqueBlockState { - self.clique.block_state_by_hash.write() - .get_mut(hash) - .expect("CliqueBlockState not found tested failed") - .clone() - } - - /// Get signers after a certain state - // This is generally used to fetch the state after a test has been executed and checked against - // the intial list of signers provided in the test - pub fn clique_signers(&self, hash: &H256) -> impl Iterator { - self.get_state_at_block(hash).signers().clone().into_iter() - } - - /// Fetches all addresses at current `block` and converts them back to `tags (char)` and sorts them - /// Addresses are supposed sorted based on address but these tests are using `tags` just for simplicity - /// and the order is not important! - pub fn into_tags>(&self, addr: T) -> Vec { - let mut tags: Vec = addr.filter_map(|addr| { - for (t, kp) in self.signers.iter() { - if addr == kp.address() { - return Some(*t) - } - } - None - }) - .collect(); - - tags.sort(); - tags - } - - /// Create a new `Clique` block and import - pub fn new_block_and_import( - &self, - block_type: CliqueBlockType, - last_header: &Header, - beneficary: Option
, - signer: char, - ) -> Result { - - let mut extra_data = vec![0; VANITY_LENGTH]; - let mut seal = util::null_seal(); - let last_hash = last_header.hash(); - - match block_type { - CliqueBlockType::Checkpoint => { - let signers = self.clique.state(&last_header).unwrap().signers().clone(); - for signer in signers { - extra_data.extend(&*signer); - } - } - CliqueBlockType::Vote(v) => seal = v.as_rlp(), - CliqueBlockType::Empty => (), - }; - - let db = self.db.boxed_clone(); - - let mut block = OpenBlock::new( - &self.clique, - Default::default(), - false, - db, - &last_header.clone(), - Arc::new(vec![last_hash]), - beneficary.unwrap_or_default(), - (3141562.into(), 31415620.into()), - extra_data, - false, - None, - ).unwrap(); - - { - let difficulty = self.get_difficulty(block.header.number(), last_header, &self.signers[&signer].address()); - let b = block.block_mut(); - b.header.set_timestamp(last_header.timestamp() + self.clique.period); - b.header.set_difficulty(difficulty); - b.header.set_seal(seal); - - let sign = ethkey::sign(self.signers[&signer].secret(), &b.header.hash()).unwrap(); - let mut extra_data = b.header.extra_data().clone(); - extra_data.extend_from_slice(&*sign); - b.header.set_extra_data(extra_data); - } - - let current_header = &block.header; - self.clique.verify_block_basic(current_header)?; - self.clique.verify_block_family(current_header, &last_header)?; - - Ok(current_header.clone()) - } + /// Create a `Clique` tester with settings + pub fn with(epoch: u64, period: u64, initial_signers: Vec) -> Self { + assert_eq!( + initial_signers.iter().all(|s| SIGNER_TAGS.contains(s)), + true, + "Not all the initial signers is in SIGNER_TAGS, possible keys are 'A' ..= 'F'" + ); + + let clique = Clique::with_test(epoch, period); + let mut genesis = Header::default(); + let mut signers = HashMap::new(); + + let call = |_a, _b| { + unimplemented!("Clique doesn't use Engine::Call"); + }; + + let mut extra_data = vec![0; VANITY_LENGTH]; + + for &signer in SIGNER_TAGS.iter() { + let secret = Secret::from(H256::from(signer as u64)); + let keypair = KeyPair::from_secret(secret).unwrap(); + if initial_signers.contains(&signer) { + extra_data.extend(&*keypair.address()); + } + signers.insert(signer, keypair); + } + + // append dummy signature + extra_data.extend(std::iter::repeat(0).take(SIGNATURE_LENGTH)); + + genesis.set_extra_data(extra_data); + genesis.set_gas_limit(U256::from(0xa00000)); + genesis.set_difficulty(U256::from(1)); + genesis.set_seal(util::null_seal()); + + clique + .genesis_epoch_data(&genesis, &call) + .expect("Create genesis failed"); + Self { + clique, + genesis, + db: get_temp_state_db(), + signers, + } + } + + /// Get difficulty for a given block + pub fn get_difficulty( + &self, + block_num: BlockNumber, + header: &Header, + signer: &Address, + ) -> U256 { + let state = self.clique.state(header).unwrap(); + if state.is_inturn(block_num, signer) { + DIFF_INTURN + } else { + DIFF_NOTURN + } + } + + /// Get the state of a given block + // Note, this will read the cache and `will` not work with more than 128 blocks + pub fn get_state_at_block(&self, hash: &H256) -> CliqueBlockState { + self.clique + .block_state_by_hash + .write() + .get_mut(hash) + .expect("CliqueBlockState not found tested failed") + .clone() + } + + /// Get signers after a certain state + // This is generally used to fetch the state after a test has been executed and checked against + // the intial list of signers provided in the test + pub fn clique_signers(&self, hash: &H256) -> impl Iterator { + self.get_state_at_block(hash).signers().clone().into_iter() + } + + /// Fetches all addresses at current `block` and converts them back to `tags (char)` and sorts them + /// Addresses are supposed sorted based on address but these tests are using `tags` just for simplicity + /// and the order is not important! + pub fn into_tags>(&self, addr: T) -> Vec { + let mut tags: Vec = addr + .filter_map(|addr| { + for (t, kp) in self.signers.iter() { + if addr == kp.address() { + return Some(*t); + } + } + None + }) + .collect(); + + tags.sort(); + tags + } + + /// Create a new `Clique` block and import + pub fn new_block_and_import( + &self, + block_type: CliqueBlockType, + last_header: &Header, + beneficary: Option
, + signer: char, + ) -> Result { + let mut extra_data = vec![0; VANITY_LENGTH]; + let mut seal = util::null_seal(); + let last_hash = last_header.hash(); + + match block_type { + CliqueBlockType::Checkpoint => { + let signers = self.clique.state(&last_header).unwrap().signers().clone(); + for signer in signers { + extra_data.extend(&*signer); + } + } + CliqueBlockType::Vote(v) => seal = v.as_rlp(), + CliqueBlockType::Empty => (), + }; + + let db = self.db.boxed_clone(); + + let mut block = OpenBlock::new( + &self.clique, + Default::default(), + false, + db, + &last_header.clone(), + Arc::new(vec![last_hash]), + beneficary.unwrap_or_default(), + (3141562.into(), 31415620.into()), + extra_data, + false, + None, + ) + .unwrap(); + + { + let difficulty = self.get_difficulty( + block.header.number(), + last_header, + &self.signers[&signer].address(), + ); + let b = block.block_mut(); + b.header + .set_timestamp(last_header.timestamp() + self.clique.period); + b.header.set_difficulty(difficulty); + b.header.set_seal(seal); + + let sign = ethkey::sign(self.signers[&signer].secret(), &b.header.hash()).unwrap(); + let mut extra_data = b.header.extra_data().clone(); + extra_data.extend_from_slice(&*sign); + b.header.set_extra_data(extra_data); + } + + let current_header = &block.header; + self.clique.verify_block_basic(current_header)?; + self.clique + .verify_block_family(current_header, &last_header)?; + + Ok(current_header.clone()) + } } #[test] fn one_signer_with_no_votes() { - let tester = CliqueTester::with(10, 1, vec!['A']); + let tester = CliqueTester::with(10, 1, vec!['A']); - let empty_block = tester.new_block_and_import(CliqueBlockType::Empty, &tester.genesis, None, 'A').unwrap(); + let empty_block = tester + .new_block_and_import(CliqueBlockType::Empty, &tester.genesis, None, 'A') + .unwrap(); - let tags = tester.into_tags(tester.clique_signers(&empty_block.hash())); - assert_eq!(&tags, &['A']); + let tags = tester.into_tags(tester.clique_signers(&empty_block.hash())); + assert_eq!(&tags, &['A']); } #[test] fn one_signer_two_votes() { - let tester = CliqueTester::with(10, 1, vec!['A']); - - // Add a vote for `B` signed by `A` - let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &tester.genesis, - Some(tester.signers[&'B'].address()), 'A').unwrap(); - let tags = tester.into_tags(tester.clique_signers(&vote.hash())); - assert_eq!(&tags, &['A', 'B']); - - // Add a empty block signed by `B` - let empty = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'B').unwrap(); - - // Add vote for `C` signed by A but should not be accepted - let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &empty, - Some(tester.signers[&'C'].address()), 'A').unwrap(); - - let tags = tester.into_tags(tester.clique_signers(&vote.hash())); - assert_eq!(&tags, &['A', 'B']); + let tester = CliqueTester::with(10, 1, vec!['A']); + + // Add a vote for `B` signed by `A` + let vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &tester.genesis, + Some(tester.signers[&'B'].address()), + 'A', + ) + .unwrap(); + let tags = tester.into_tags(tester.clique_signers(&vote.hash())); + assert_eq!(&tags, &['A', 'B']); + + // Add a empty block signed by `B` + let empty = tester + .new_block_and_import(CliqueBlockType::Empty, &vote, None, 'B') + .unwrap(); + + // Add vote for `C` signed by A but should not be accepted + let vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &empty, + Some(tester.signers[&'C'].address()), + 'A', + ) + .unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&vote.hash())); + assert_eq!(&tags, &['A', 'B']); } #[test] fn two_signers_six_votes_deny_last() { - let tester = CliqueTester::with(10, 1, vec!['A', 'B']); - - let mut prev_header = tester.genesis.clone(); - - // Add two votes for `C` signed by `A` and `B` - for &signer in SIGNER_TAGS.iter().take(2) { - let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &prev_header, - Some(tester.signers[&'C'].address()), signer).unwrap(); - prev_header = vote.clone(); - } - - // Add two votes for `D` signed by `A` and `B` - for &signer in SIGNER_TAGS.iter().take(2) { - let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &prev_header, - Some(tester.signers[&'D'].address()), signer).unwrap(); - prev_header = vote.clone(); - } - - // Add a empty block signed by `C` - let empty = tester.new_block_and_import(CliqueBlockType::Empty, &prev_header, None, 'C').unwrap(); - prev_header = empty.clone(); - - // Add two votes for `E` signed by `A` and `B` - for &signer in SIGNER_TAGS.iter().take(2) { - let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &prev_header, - Some(tester.signers[&'E'].address()), signer).unwrap(); - prev_header = vote.clone(); - } - - let tags = tester.into_tags(tester.clique_signers(&prev_header.hash())); - assert_eq!(&tags, &['A', 'B', 'C', 'D']); + let tester = CliqueTester::with(10, 1, vec!['A', 'B']); + + let mut prev_header = tester.genesis.clone(); + + // Add two votes for `C` signed by `A` and `B` + for &signer in SIGNER_TAGS.iter().take(2) { + let vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &prev_header, + Some(tester.signers[&'C'].address()), + signer, + ) + .unwrap(); + prev_header = vote.clone(); + } + + // Add two votes for `D` signed by `A` and `B` + for &signer in SIGNER_TAGS.iter().take(2) { + let vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &prev_header, + Some(tester.signers[&'D'].address()), + signer, + ) + .unwrap(); + prev_header = vote.clone(); + } + + // Add a empty block signed by `C` + let empty = tester + .new_block_and_import(CliqueBlockType::Empty, &prev_header, None, 'C') + .unwrap(); + prev_header = empty.clone(); + + // Add two votes for `E` signed by `A` and `B` + for &signer in SIGNER_TAGS.iter().take(2) { + let vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &prev_header, + Some(tester.signers[&'E'].address()), + signer, + ) + .unwrap(); + prev_header = vote.clone(); + } + + let tags = tester.into_tags(tester.clique_signers(&prev_header.hash())); + assert_eq!(&tags, &['A', 'B', 'C', 'D']); } #[test] fn one_signer_dropping_itself() { - let tester = CliqueTester::with(10, 1, vec!['A']); - let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis, - Some(tester.signers[&'A'].address()), 'A').unwrap(); - let signers = tester.clique_signers(&vote.hash()); - assert!(signers.count() == 0); + let tester = CliqueTester::with(10, 1, vec!['A']); + let vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &tester.genesis, + Some(tester.signers[&'A'].address()), + 'A', + ) + .unwrap(); + let signers = tester.clique_signers(&vote.hash()); + assert!(signers.count() == 0); } #[test] fn two_signers_one_remove_vote_no_consensus() { - let tester = CliqueTester::with(10, 1, vec!['A', 'B']); - let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis, - Some(tester.signers[&'B'].address()), 'A').unwrap(); - - let tags = tester.into_tags(tester.clique_signers(&vote.hash())); - assert_eq!(&tags, &['A', 'B']); + let tester = CliqueTester::with(10, 1, vec!['A', 'B']); + let vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &tester.genesis, + Some(tester.signers[&'B'].address()), + 'A', + ) + .unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&vote.hash())); + assert_eq!(&tags, &['A', 'B']); } #[test] fn two_signers_consensus_remove_b() { - let tester = CliqueTester::with(10, 1, vec!['A', 'B']); - let first_vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis, - Some(tester.signers[&'B'].address()), 'A').unwrap(); - let second_vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &first_vote, - Some(tester.signers[&'B'].address()), 'B').unwrap(); - - let tags = tester.into_tags(tester.clique_signers(&second_vote.hash())); - assert_eq!(&tags, &['A']); + let tester = CliqueTester::with(10, 1, vec!['A', 'B']); + let first_vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &tester.genesis, + Some(tester.signers[&'B'].address()), + 'A', + ) + .unwrap(); + let second_vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &first_vote, + Some(tester.signers[&'B'].address()), + 'B', + ) + .unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&second_vote.hash())); + assert_eq!(&tags, &['A']); } #[test] fn three_signers_consensus_remove_c() { - let tester = CliqueTester::with(10, 1, vec!['A', 'B', 'C']); - let first_vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis, - Some(tester.signers[&'C'].address()), 'A').unwrap(); - let second_vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &first_vote, - Some(tester.signers[&'C'].address()), 'B').unwrap(); - - let tags = tester.into_tags(tester.clique_signers(&second_vote.hash())); - assert_eq!(&tags, &['A', 'B']); + let tester = CliqueTester::with(10, 1, vec!['A', 'B', 'C']); + let first_vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &tester.genesis, + Some(tester.signers[&'C'].address()), + 'A', + ) + .unwrap(); + let second_vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &first_vote, + Some(tester.signers[&'C'].address()), + 'B', + ) + .unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&second_vote.hash())); + assert_eq!(&tags, &['A', 'B']); } #[test] fn four_signers_half_no_consensus() { - let tester = CliqueTester::with(10, 1, vec!['A', 'B', 'C', 'D']); - let first_vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis, - Some(tester.signers[&'C'].address()), 'A').unwrap(); - - let second_vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &first_vote, - Some(tester.signers[&'C'].address()), 'B').unwrap(); - - let tags = tester.into_tags(tester.clique_signers(&second_vote.hash())); - assert_eq!(&tags, &['A', 'B', 'C', 'D']); + let tester = CliqueTester::with(10, 1, vec!['A', 'B', 'C', 'D']); + let first_vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &tester.genesis, + Some(tester.signers[&'C'].address()), + 'A', + ) + .unwrap(); + + let second_vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &first_vote, + Some(tester.signers[&'C'].address()), + 'B', + ) + .unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&second_vote.hash())); + assert_eq!(&tags, &['A', 'B', 'C', 'D']); } #[test] fn four_signers_three_consensus_rm() { - let tester = CliqueTester::with(10, 1, vec!['A', 'B', 'C', 'D']); - - let mut prev_header = tester.genesis.clone(); - - // Three votes to remove `D` signed by ['A', 'B', 'C'] - for signer in SIGNER_TAGS.iter().take(3) { - let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &prev_header, - Some(tester.signers[&'D'].address()), *signer).unwrap(); - prev_header = vote.clone(); - } - - let tags = tester.into_tags(tester.clique_signers(&prev_header.hash())); - assert_eq!(&tags, &['A', 'B', 'C']); + let tester = CliqueTester::with(10, 1, vec!['A', 'B', 'C', 'D']); + + let mut prev_header = tester.genesis.clone(); + + // Three votes to remove `D` signed by ['A', 'B', 'C'] + for signer in SIGNER_TAGS.iter().take(3) { + let vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &prev_header, + Some(tester.signers[&'D'].address()), + *signer, + ) + .unwrap(); + prev_header = vote.clone(); + } + + let tags = tester.into_tags(tester.clique_signers(&prev_header.hash())); + assert_eq!(&tags, &['A', 'B', 'C']); } #[test] fn vote_add_only_counted_once_per_signer() { - let tester = CliqueTester::with(10, 1, vec!['A', 'B']); - - // Add a vote for `C` signed by `A` - let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &tester.genesis, - Some(tester.signers[&'C'].address()), 'A').unwrap(); - // Empty block signed by B` - let empty = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'B').unwrap(); - - // Add a vote for `C` signed by `A` - let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &empty, - Some(tester.signers[&'C'].address()), 'A').unwrap(); - // Empty block signed by `B` - let empty = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'B').unwrap(); - - // Add a vote for `C` signed by `A` - let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &empty, - Some(tester.signers[&'C'].address()), 'A').unwrap(); - - let tags = tester.into_tags(tester.clique_signers(&vote.hash())); - assert_eq!(&tags, &['A', 'B']); + let tester = CliqueTester::with(10, 1, vec!['A', 'B']); + + // Add a vote for `C` signed by `A` + let vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &tester.genesis, + Some(tester.signers[&'C'].address()), + 'A', + ) + .unwrap(); + // Empty block signed by B` + let empty = tester + .new_block_and_import(CliqueBlockType::Empty, &vote, None, 'B') + .unwrap(); + + // Add a vote for `C` signed by `A` + let vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &empty, + Some(tester.signers[&'C'].address()), + 'A', + ) + .unwrap(); + // Empty block signed by `B` + let empty = tester + .new_block_and_import(CliqueBlockType::Empty, &vote, None, 'B') + .unwrap(); + + // Add a vote for `C` signed by `A` + let vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &empty, + Some(tester.signers[&'C'].address()), + 'A', + ) + .unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&vote.hash())); + assert_eq!(&tags, &['A', 'B']); } #[test] fn vote_add_concurrently_is_permitted() { - let tester = CliqueTester::with(10, 1, vec!['A', 'B']); - - // Add a vote for `C` signed by `A` - let b = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &tester.genesis, - Some(tester.signers[&'C'].address()), 'A').unwrap(); - - // Empty block signed by `B` - let b = tester.new_block_and_import(CliqueBlockType::Empty, &b, None, 'B').unwrap(); - - // Add a vote for `D` signed by `A` - let b = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &b, - Some(tester.signers[&'D'].address()), 'A').unwrap(); - - // Empty block signed by `B` - let b = tester.new_block_and_import(CliqueBlockType::Empty, &b, None, 'B').unwrap(); - - // Empty block signed by `A` - let b = tester.new_block_and_import(CliqueBlockType::Empty, &b, None, 'A').unwrap(); - - // Add a vote for `D` signed by `B` - let b = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &b, - Some(tester.signers[&'D'].address()), 'B').unwrap(); - - // Empty block signed by `A` - let b = tester.new_block_and_import(CliqueBlockType::Empty, &b, None, 'A').unwrap(); - - // Add a vote for `C` signed by `B` - let b = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &b, - Some(tester.signers[&'C'].address()), 'B').unwrap(); - - let tags = tester.into_tags(tester.clique_signers(&b.hash())); - assert_eq!(&tags, &['A', 'B', 'C', 'D']); + let tester = CliqueTester::with(10, 1, vec!['A', 'B']); + + // Add a vote for `C` signed by `A` + let b = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &tester.genesis, + Some(tester.signers[&'C'].address()), + 'A', + ) + .unwrap(); + + // Empty block signed by `B` + let b = tester + .new_block_and_import(CliqueBlockType::Empty, &b, None, 'B') + .unwrap(); + + // Add a vote for `D` signed by `A` + let b = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &b, + Some(tester.signers[&'D'].address()), + 'A', + ) + .unwrap(); + + // Empty block signed by `B` + let b = tester + .new_block_and_import(CliqueBlockType::Empty, &b, None, 'B') + .unwrap(); + + // Empty block signed by `A` + let b = tester + .new_block_and_import(CliqueBlockType::Empty, &b, None, 'A') + .unwrap(); + + // Add a vote for `D` signed by `B` + let b = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &b, + Some(tester.signers[&'D'].address()), + 'B', + ) + .unwrap(); + + // Empty block signed by `A` + let b = tester + .new_block_and_import(CliqueBlockType::Empty, &b, None, 'A') + .unwrap(); + + // Add a vote for `C` signed by `B` + let b = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &b, + Some(tester.signers[&'C'].address()), + 'B', + ) + .unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&b.hash())); + assert_eq!(&tags, &['A', 'B', 'C', 'D']); } #[test] fn vote_rm_only_counted_once_per_signer() { - let tester = CliqueTester::with(10, 1, vec!['A', 'B']); - - let mut prev_header = tester.genesis.clone(); - - for _ in 0..2 { - // Vote to remove `B` signed by `A` - let b = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &prev_header, - Some(tester.signers[&'B'].address()), 'A').unwrap(); - // Empty block signed by `B` - let b = tester.new_block_and_import(CliqueBlockType::Empty, &b, None, 'B').unwrap(); - - prev_header = b.clone(); - } - - // Add a vote for `B` signed by `A` - let b = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &prev_header, - Some(tester.signers[&'B'].address()), 'A').unwrap(); - - let tags = tester.into_tags(tester.clique_signers(&b.hash())); - assert_eq!(&tags, &['A', 'B']); + let tester = CliqueTester::with(10, 1, vec!['A', 'B']); + + let mut prev_header = tester.genesis.clone(); + + for _ in 0..2 { + // Vote to remove `B` signed by `A` + let b = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &prev_header, + Some(tester.signers[&'B'].address()), + 'A', + ) + .unwrap(); + // Empty block signed by `B` + let b = tester + .new_block_and_import(CliqueBlockType::Empty, &b, None, 'B') + .unwrap(); + + prev_header = b.clone(); + } + + // Add a vote for `B` signed by `A` + let b = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &prev_header, + Some(tester.signers[&'B'].address()), + 'A', + ) + .unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&b.hash())); + assert_eq!(&tags, &['A', 'B']); } #[test] fn vote_rm_concurrently_is_permitted() { - let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D']); - - // Add a vote for `C` signed by `A` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis, - Some(tester.signers[&'C'].address()), 'A').unwrap(); - - // Empty block signed by `B` - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'B').unwrap(); - // Empty block signed by `C` - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap(); - - // Add a vote for `D` signed by `A` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, - Some(tester.signers[&'D'].address()), 'A').unwrap(); - - // Empty block signed by `B` - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'B').unwrap(); - // Empty block signed by `C` - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap(); - // Empty block signed by `A` - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap(); - - // Add a vote for `D` signed by `B` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, - Some(tester.signers[&'D'].address()), 'B').unwrap(); - // Add a vote for `D` signed by `C` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, - Some(tester.signers[&'D'].address()), 'C').unwrap(); - - // Empty block signed by `A` - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap(); - // Add a vote for `C` signed by `B` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, - Some(tester.signers[&'C'].address()), 'B').unwrap(); - - let tags = tester.into_tags(tester.clique_signers(&block.hash())); - assert_eq!(&tags, &['A', 'B']); + let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D']); + + // Add a vote for `C` signed by `A` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &tester.genesis, + Some(tester.signers[&'C'].address()), + 'A', + ) + .unwrap(); + + // Empty block signed by `B` + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'B') + .unwrap(); + // Empty block signed by `C` + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'C') + .unwrap(); + + // Add a vote for `D` signed by `A` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &block, + Some(tester.signers[&'D'].address()), + 'A', + ) + .unwrap(); + + // Empty block signed by `B` + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'B') + .unwrap(); + // Empty block signed by `C` + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'C') + .unwrap(); + // Empty block signed by `A` + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'A') + .unwrap(); + + // Add a vote for `D` signed by `B` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &block, + Some(tester.signers[&'D'].address()), + 'B', + ) + .unwrap(); + // Add a vote for `D` signed by `C` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &block, + Some(tester.signers[&'D'].address()), + 'C', + ) + .unwrap(); + + // Empty block signed by `A` + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'A') + .unwrap(); + // Add a vote for `C` signed by `B` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &block, + Some(tester.signers[&'C'].address()), + 'B', + ) + .unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&block.hash())); + assert_eq!(&tags, &['A', 'B']); } #[test] fn vote_to_rm_are_immediate_and_ensure_votes_are_rm() { - let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C']); - - // Vote to remove `B` signed by `C` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis, - Some(tester.signers[&'B'].address()), 'C').unwrap(); - // Vote to remove `C` signed by `A` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, - Some(tester.signers[&'C'].address()), 'A').unwrap(); - // Vote to remove `C` signed by `B` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, - Some(tester.signers[&'C'].address()), 'B').unwrap(); - // Vote to remove `B` signed by `A` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, - Some(tester.signers[&'B'].address()), 'A').unwrap(); - - let tags = tester.into_tags(tester.clique_signers(&block.hash())); - assert_eq!(&tags, &['A', 'B']); + let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C']); + + // Vote to remove `B` signed by `C` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &tester.genesis, + Some(tester.signers[&'B'].address()), + 'C', + ) + .unwrap(); + // Vote to remove `C` signed by `A` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &block, + Some(tester.signers[&'C'].address()), + 'A', + ) + .unwrap(); + // Vote to remove `C` signed by `B` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &block, + Some(tester.signers[&'C'].address()), + 'B', + ) + .unwrap(); + // Vote to remove `B` signed by `A` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &block, + Some(tester.signers[&'B'].address()), + 'A', + ) + .unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&block.hash())); + assert_eq!(&tags, &['A', 'B']); } #[test] fn vote_to_rm_are_immediate_and_votes_should_be_dropped_from_kicked_signer() { - let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C']); - - // Vote to add `D` signed by `C` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &tester.genesis, - Some(tester.signers[&'D'].address()), 'C').unwrap(); - // Vote to remove `C` signed by `A` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, - Some(tester.signers[&'C'].address()), 'A').unwrap(); - - // Vote to remove `C` signed by `B` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, - Some(tester.signers[&'C'].address()), 'B').unwrap(); - - // Vote to add `D` signed by `A` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &block, - Some(tester.signers[&'D'].address()), 'A').unwrap(); - - let tags = tester.into_tags(tester.clique_signers(&block.hash())); - assert_eq!(&tags, &['A', 'B']); + let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C']); + + // Vote to add `D` signed by `C` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &tester.genesis, + Some(tester.signers[&'D'].address()), + 'C', + ) + .unwrap(); + // Vote to remove `C` signed by `A` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &block, + Some(tester.signers[&'C'].address()), + 'A', + ) + .unwrap(); + + // Vote to remove `C` signed by `B` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &block, + Some(tester.signers[&'C'].address()), + 'B', + ) + .unwrap(); + + // Vote to add `D` signed by `A` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &block, + Some(tester.signers[&'D'].address()), + 'A', + ) + .unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&block.hash())); + assert_eq!(&tags, &['A', 'B']); } #[test] fn cascading_not_allowed() { - let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D']); - - // Vote against `C` signed by `A` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis, - Some(tester.signers[&'C'].address()), 'A').unwrap(); - - // Empty block signed by `B` - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'B').unwrap(); - - // Empty block signed by `C` - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap(); - - // Vote against `D` signed by `A` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, - Some(tester.signers[&'D'].address()), 'A').unwrap(); - - // Vote against `C` signed by `B` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, - Some(tester.signers[&'C'].address()), 'B').unwrap(); - - // Empty block signed by `C` - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap(); - - // Empty block signed by `A` - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap(); - - // Vote against `D` signed by `B` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, - Some(tester.signers[&'D'].address()), 'B').unwrap(); - - // Vote against `D` signed by `C` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, - Some(tester.signers[&'D'].address()), 'C').unwrap(); - - let tags = tester.into_tags(tester.clique_signers(&block.hash())); - assert_eq!(&tags, &['A', 'B', 'C']); + let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D']); + + // Vote against `C` signed by `A` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &tester.genesis, + Some(tester.signers[&'C'].address()), + 'A', + ) + .unwrap(); + + // Empty block signed by `B` + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'B') + .unwrap(); + + // Empty block signed by `C` + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'C') + .unwrap(); + + // Vote against `D` signed by `A` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &block, + Some(tester.signers[&'D'].address()), + 'A', + ) + .unwrap(); + + // Vote against `C` signed by `B` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &block, + Some(tester.signers[&'C'].address()), + 'B', + ) + .unwrap(); + + // Empty block signed by `C` + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'C') + .unwrap(); + + // Empty block signed by `A` + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'A') + .unwrap(); + + // Vote against `D` signed by `B` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &block, + Some(tester.signers[&'D'].address()), + 'B', + ) + .unwrap(); + + // Vote against `D` signed by `C` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &block, + Some(tester.signers[&'D'].address()), + 'C', + ) + .unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&block.hash())); + assert_eq!(&tags, &['A', 'B', 'C']); } #[test] fn consensus_out_of_bounds_consensus_execute_on_touch() { - let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D']); - - // Vote against `C` signed by `A` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis, - Some(tester.signers[&'C'].address()), 'A').unwrap(); - - // Empty block signed by `B` - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'B').unwrap(); - - // Empty block signed by `C` - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap(); - - // Vote against `D` signed by `A` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, - Some(tester.signers[&'D'].address()), 'A').unwrap(); - - // Vote against `C` signed by `B` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, - Some(tester.signers[&'C'].address()), 'B').unwrap(); - - // Empty block signed by `C` - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap(); - - // Empty block signed by `A` - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap(); - - // Vote against `D` signed by `B` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, - Some(tester.signers[&'D'].address()), 'B').unwrap(); - - // Vote against `D` signed by `C` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, - Some(tester.signers[&'D'].address()), 'C').unwrap(); - - let tags = tester.into_tags(tester.clique_signers(&block.hash())); - assert_eq!(&tags, &['A', 'B', 'C'], "D should have been removed after 3/4 remove votes"); - - // Empty block signed by `A` - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap(); - - // Vote for `C` signed by `C` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &block, - Some(tester.signers[&'C'].address()), 'C').unwrap(); - - let tags = tester.into_tags(tester.clique_signers(&block.hash())); - assert_eq!(&tags, &['A', 'B']); + let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D']); + + // Vote against `C` signed by `A` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &tester.genesis, + Some(tester.signers[&'C'].address()), + 'A', + ) + .unwrap(); + + // Empty block signed by `B` + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'B') + .unwrap(); + + // Empty block signed by `C` + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'C') + .unwrap(); + + // Vote against `D` signed by `A` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &block, + Some(tester.signers[&'D'].address()), + 'A', + ) + .unwrap(); + + // Vote against `C` signed by `B` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &block, + Some(tester.signers[&'C'].address()), + 'B', + ) + .unwrap(); + + // Empty block signed by `C` + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'C') + .unwrap(); + + // Empty block signed by `A` + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'A') + .unwrap(); + + // Vote against `D` signed by `B` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &block, + Some(tester.signers[&'D'].address()), + 'B', + ) + .unwrap(); + + // Vote against `D` signed by `C` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &block, + Some(tester.signers[&'D'].address()), + 'C', + ) + .unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&block.hash())); + assert_eq!( + &tags, + &['A', 'B', 'C'], + "D should have been removed after 3/4 remove votes" + ); + + // Empty block signed by `A` + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'A') + .unwrap(); + + // Vote for `C` signed by `C` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &block, + Some(tester.signers[&'C'].address()), + 'C', + ) + .unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&block.hash())); + assert_eq!(&tags, &['A', 'B']); } #[test] fn consensus_out_of_bounds_first_touch() { - let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D']); - - // Vote against `C` signed by `A` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis, - Some(tester.signers[&'C'].address()), 'A').unwrap(); - - // Empty block signed by `B` - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'B').unwrap(); - - // Empty block signed by `C` - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap(); - - // Vote against `D` signed by `A` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, - Some(tester.signers[&'D'].address()), 'A').unwrap(); - - // Vote against `C` signed by `B` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, - Some(tester.signers[&'C'].address()), 'B').unwrap(); - - // Empty block signed by `C` - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap(); - - // Empty block signed by `A` - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap(); - - // Vote against `D` signed by `B` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, - Some(tester.signers[&'D'].address()), 'B').unwrap(); - - // Vote against `D` signed by `C` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, - Some(tester.signers[&'D'].address()), 'C').unwrap(); - - let tags = tester.into_tags(tester.clique_signers(&block.hash())); - assert_eq!(&tags, &['A', 'B', 'C']); - - // Empty block signed by `A` - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap(); - - // Vote for `C` signed by `B` - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &block, - Some(tester.signers[&'C'].address()), 'B').unwrap(); - - let tags = tester.into_tags(tester.clique_signers(&block.hash())); - assert_eq!(&tags, &['A', 'B', 'C']); + let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D']); + + // Vote against `C` signed by `A` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &tester.genesis, + Some(tester.signers[&'C'].address()), + 'A', + ) + .unwrap(); + + // Empty block signed by `B` + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'B') + .unwrap(); + + // Empty block signed by `C` + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'C') + .unwrap(); + + // Vote against `D` signed by `A` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &block, + Some(tester.signers[&'D'].address()), + 'A', + ) + .unwrap(); + + // Vote against `C` signed by `B` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &block, + Some(tester.signers[&'C'].address()), + 'B', + ) + .unwrap(); + + // Empty block signed by `C` + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'C') + .unwrap(); + + // Empty block signed by `A` + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'A') + .unwrap(); + + // Vote against `D` signed by `B` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &block, + Some(tester.signers[&'D'].address()), + 'B', + ) + .unwrap(); + + // Vote against `D` signed by `C` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &block, + Some(tester.signers[&'D'].address()), + 'C', + ) + .unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&block.hash())); + assert_eq!(&tags, &['A', 'B', 'C']); + + // Empty block signed by `A` + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'A') + .unwrap(); + + // Vote for `C` signed by `B` + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &block, + Some(tester.signers[&'C'].address()), + 'B', + ) + .unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&block.hash())); + assert_eq!(&tags, &['A', 'B', 'C']); } #[test] fn pending_votes_doesnt_survive_authorization_changes() { - let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D', 'E']); - - let mut prev_header = tester.genesis.clone(); - - // Vote for `F` from [`A`, `B`, `C`] - for sign in SIGNER_TAGS.iter().take(3) { - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &prev_header, - Some(tester.signers[&'F'].address()), *sign).unwrap(); - prev_header = block.clone(); - } - - let tags = tester.into_tags(tester.clique_signers(&prev_header.hash())); - assert_eq!(&tags, &['A', 'B', 'C', 'D', 'E', 'F'], "F should have been added"); - - // Vote against `F` from [`D`, `E`, `B`, `C`] - for sign in SIGNER_TAGS.iter().skip(3).chain(SIGNER_TAGS.iter().skip(1).take(2)) { - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &prev_header, - Some(tester.signers[&'F'].address()), *sign).unwrap(); - prev_header = block.clone(); - } - - let tags = tester.into_tags(tester.clique_signers(&prev_header.hash())); - assert_eq!(&tags, &['A', 'B', 'C', 'D', 'E'], "F should have been removed"); - - // Vote for `F` from [`D`, `E`] - for sign in SIGNER_TAGS.iter().skip(3).take(2) { - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &prev_header, - Some(tester.signers[&'F'].address()), *sign).unwrap(); - prev_header = block.clone(); - } - - // Vote against `A` from [`B`, `C`, `D`] - for sign in SIGNER_TAGS.iter().skip(1).take(3) { - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &prev_header, - Some(tester.signers[&'A'].address()), *sign).unwrap(); - prev_header = block.clone(); - } - - let tags = tester.into_tags(tester.clique_signers(&prev_header.hash())); - assert_eq!(&tags, &['B', 'C', 'D', 'E'], "A should have been removed"); - - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &prev_header, - Some(tester.signers[&'F'].address()), 'B').unwrap(); - - let tags = tester.into_tags(tester.clique_signers(&block.hash())); - assert_eq!(&tags, &['B', 'C', 'D', 'E', 'F'], "F should have been added again"); + let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D', 'E']); + + let mut prev_header = tester.genesis.clone(); + + // Vote for `F` from [`A`, `B`, `C`] + for sign in SIGNER_TAGS.iter().take(3) { + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &prev_header, + Some(tester.signers[&'F'].address()), + *sign, + ) + .unwrap(); + prev_header = block.clone(); + } + + let tags = tester.into_tags(tester.clique_signers(&prev_header.hash())); + assert_eq!( + &tags, + &['A', 'B', 'C', 'D', 'E', 'F'], + "F should have been added" + ); + + // Vote against `F` from [`D`, `E`, `B`, `C`] + for sign in SIGNER_TAGS + .iter() + .skip(3) + .chain(SIGNER_TAGS.iter().skip(1).take(2)) + { + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &prev_header, + Some(tester.signers[&'F'].address()), + *sign, + ) + .unwrap(); + prev_header = block.clone(); + } + + let tags = tester.into_tags(tester.clique_signers(&prev_header.hash())); + assert_eq!( + &tags, + &['A', 'B', 'C', 'D', 'E'], + "F should have been removed" + ); + + // Vote for `F` from [`D`, `E`] + for sign in SIGNER_TAGS.iter().skip(3).take(2) { + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &prev_header, + Some(tester.signers[&'F'].address()), + *sign, + ) + .unwrap(); + prev_header = block.clone(); + } + + // Vote against `A` from [`B`, `C`, `D`] + for sign in SIGNER_TAGS.iter().skip(1).take(3) { + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Remove), + &prev_header, + Some(tester.signers[&'A'].address()), + *sign, + ) + .unwrap(); + prev_header = block.clone(); + } + + let tags = tester.into_tags(tester.clique_signers(&prev_header.hash())); + assert_eq!(&tags, &['B', 'C', 'D', 'E'], "A should have been removed"); + + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &prev_header, + Some(tester.signers[&'F'].address()), + 'B', + ) + .unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&block.hash())); + assert_eq!( + &tags, + &['B', 'C', 'D', 'E', 'F'], + "F should have been added again" + ); } #[test] fn epoch_transition_reset_all_votes() { - let tester = CliqueTester::with(3, 1, vec!['A', 'B']); - - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &tester.genesis, - Some(tester.signers[&'C'].address()), 'A').unwrap(); - - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'B').unwrap(); - let block = tester.new_block_and_import(CliqueBlockType::Checkpoint, &block, None, 'A').unwrap(); - - let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &block, - Some(tester.signers[&'C'].address()), 'B').unwrap(); - - let tags = tester.into_tags(tester.clique_signers(&block.hash())); - assert_eq!(&tags, &['A', 'B'], "Votes should have been reset after checkpoint"); + let tester = CliqueTester::with(3, 1, vec!['A', 'B']); + + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &tester.genesis, + Some(tester.signers[&'C'].address()), + 'A', + ) + .unwrap(); + + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'B') + .unwrap(); + let block = tester + .new_block_and_import(CliqueBlockType::Checkpoint, &block, None, 'A') + .unwrap(); + + let block = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &block, + Some(tester.signers[&'C'].address()), + 'B', + ) + .unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&block.hash())); + assert_eq!( + &tags, + &['A', 'B'], + "Votes should have been reset after checkpoint" + ); } #[test] fn unauthorized_signer_should_not_be_able_to_sign_block() { - let tester = CliqueTester::with(3, 1, vec!['A']); - let err = tester.new_block_and_import(CliqueBlockType::Empty, &tester.genesis, None, 'B').unwrap_err(); - - match err.kind() { - ErrorKind::Engine(EngineError::NotAuthorized(_)) => (), - _ => assert!(true == false, "Wrong error kind"), - } + let tester = CliqueTester::with(3, 1, vec!['A']); + let err = tester + .new_block_and_import(CliqueBlockType::Empty, &tester.genesis, None, 'B') + .unwrap_err(); + + match err.kind() { + ErrorKind::Engine(EngineError::NotAuthorized(_)) => (), + _ => assert!(true == false, "Wrong error kind"), + } } #[test] fn signer_should_not_be_able_to_sign_two_consequtive_blocks() { - let tester = CliqueTester::with(3, 1, vec!['A', 'B']); - let b = tester.new_block_and_import(CliqueBlockType::Empty, &tester.genesis, None, 'A').unwrap(); - let err = tester.new_block_and_import(CliqueBlockType::Empty, &b, None, 'A').unwrap_err(); - - match err.kind() { - ErrorKind::Engine(EngineError::CliqueTooRecentlySigned(_)) => (), - _ => assert!(true == false, "Wrong error kind"), - } + let tester = CliqueTester::with(3, 1, vec!['A', 'B']); + let b = tester + .new_block_and_import(CliqueBlockType::Empty, &tester.genesis, None, 'A') + .unwrap(); + let err = tester + .new_block_and_import(CliqueBlockType::Empty, &b, None, 'A') + .unwrap_err(); + + match err.kind() { + ErrorKind::Engine(EngineError::CliqueTooRecentlySigned(_)) => (), + _ => assert!(true == false, "Wrong error kind"), + } } - #[test] fn recent_signers_should_not_reset_on_checkpoint() { - let tester = CliqueTester::with(3, 1, vec!['A', 'B', 'C']); - - let block = tester.new_block_and_import(CliqueBlockType::Empty, &tester.genesis, None, 'A').unwrap(); - let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'B').unwrap(); - let block = tester.new_block_and_import(CliqueBlockType::Checkpoint, &block, None, 'A').unwrap(); - - let err = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap_err(); - - match err.kind() { - ErrorKind::Engine(EngineError::CliqueTooRecentlySigned(_)) => (), - _ => assert!(true == false, "Wrong error kind"), - } + let tester = CliqueTester::with(3, 1, vec!['A', 'B', 'C']); + + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &tester.genesis, None, 'A') + .unwrap(); + let block = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'B') + .unwrap(); + let block = tester + .new_block_and_import(CliqueBlockType::Checkpoint, &block, None, 'A') + .unwrap(); + + let err = tester + .new_block_and_import(CliqueBlockType::Empty, &block, None, 'A') + .unwrap_err(); + + match err.kind() { + ErrorKind::Engine(EngineError::CliqueTooRecentlySigned(_)) => (), + _ => assert!(true == false, "Wrong error kind"), + } } // Not part of http://eips.ethereum.org/EIPS/eip-225 #[test] fn bonus_consensus_should_keep_track_of_votes_before_latest_per_signer() { - let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D']); - - // Add a vote for `E` signed by `A` - let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &tester.genesis, - Some(tester.signers[&'E'].address()), 'A').unwrap(); - // Empty block signed by `B` - let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'B').unwrap(); - - // Empty block signed by `C` - let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'C').unwrap(); - - // Empty block signed by `D` - let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'D').unwrap(); - - // Add a vote for `F` signed by `A` - let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &vote, - Some(tester.signers[&'F'].address()), 'A').unwrap(); - // Empty block signed by `C` - let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'C').unwrap(); - - // Empty block signed by `D` - let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'D').unwrap(); - - // Add a vote for `E` signed by `B` - let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &vote, - Some(tester.signers[&'E'].address()), 'B').unwrap(); - // Empty block signed by `A` - let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'A').unwrap(); - - // Empty block signed by `C` - let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'C').unwrap(); - - // Empty block signed by `D` - let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'D').unwrap(); - - // Add a vote for `F` signed by `B` - let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &vote, - Some(tester.signers[&'F'].address()), 'B').unwrap(); - - // Empty block signed by A` - let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'A').unwrap(); - - // Add a vote for `E` signed by `C` - let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &vote, - Some(tester.signers[&'E'].address()), 'C').unwrap(); - - let tags = tester.into_tags(tester.clique_signers(&vote.hash())); - assert_eq!(&tags, &['A', 'B', 'C', 'D', 'E']); + let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D']); + + // Add a vote for `E` signed by `A` + let vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &tester.genesis, + Some(tester.signers[&'E'].address()), + 'A', + ) + .unwrap(); + // Empty block signed by `B` + let vote = tester + .new_block_and_import(CliqueBlockType::Empty, &vote, None, 'B') + .unwrap(); + + // Empty block signed by `C` + let vote = tester + .new_block_and_import(CliqueBlockType::Empty, &vote, None, 'C') + .unwrap(); + + // Empty block signed by `D` + let vote = tester + .new_block_and_import(CliqueBlockType::Empty, &vote, None, 'D') + .unwrap(); + + // Add a vote for `F` signed by `A` + let vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &vote, + Some(tester.signers[&'F'].address()), + 'A', + ) + .unwrap(); + // Empty block signed by `C` + let vote = tester + .new_block_and_import(CliqueBlockType::Empty, &vote, None, 'C') + .unwrap(); + + // Empty block signed by `D` + let vote = tester + .new_block_and_import(CliqueBlockType::Empty, &vote, None, 'D') + .unwrap(); + + // Add a vote for `E` signed by `B` + let vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &vote, + Some(tester.signers[&'E'].address()), + 'B', + ) + .unwrap(); + // Empty block signed by `A` + let vote = tester + .new_block_and_import(CliqueBlockType::Empty, &vote, None, 'A') + .unwrap(); + + // Empty block signed by `C` + let vote = tester + .new_block_and_import(CliqueBlockType::Empty, &vote, None, 'C') + .unwrap(); + + // Empty block signed by `D` + let vote = tester + .new_block_and_import(CliqueBlockType::Empty, &vote, None, 'D') + .unwrap(); + + // Add a vote for `F` signed by `B` + let vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &vote, + Some(tester.signers[&'F'].address()), + 'B', + ) + .unwrap(); + + // Empty block signed by A` + let vote = tester + .new_block_and_import(CliqueBlockType::Empty, &vote, None, 'A') + .unwrap(); + + // Add a vote for `E` signed by `C` + let vote = tester + .new_block_and_import( + CliqueBlockType::Vote(VoteType::Add), + &vote, + Some(tester.signers[&'E'].address()), + 'C', + ) + .unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&vote.hash())); + assert_eq!(&tags, &['A', 'B', 'C', 'D', 'E']); } diff --git a/ethcore/src/engines/clique/util.rs b/ethcore/src/engines/clique/util.rs index 3f75289e91e..81751fa0453 100644 --- a/ethcore/src/engines/clique/util.rs +++ b/ethcore/src/engines/clique/util.rs @@ -1,23 +1,25 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use std::collections::BTreeSet; -use engines::EngineError; -use engines::clique::{ADDRESS_LENGTH, SIGNATURE_LENGTH, VANITY_LENGTH, NULL_NONCE, NULL_MIXHASH}; +use engines::{ + clique::{ADDRESS_LENGTH, NULL_MIXHASH, NULL_NONCE, SIGNATURE_LENGTH, VANITY_LENGTH}, + EngineError, +}; use error::Error; use ethereum_types::{Address, H256}; use ethkey::{public_to_address, recover as ec_recover, Signature}; @@ -29,49 +31,49 @@ use types::header::Header; /// How many recovered signature to cache in the memory. pub const CREATOR_CACHE_NUM: usize = 4096; lazy_static! { - /// key: header hash - /// value: creator address - static ref CREATOR_BY_HASH: RwLock> = RwLock::new(LruCache::new(CREATOR_CACHE_NUM)); + /// key: header hash + /// value: creator address + static ref CREATOR_BY_HASH: RwLock> = RwLock::new(LruCache::new(CREATOR_CACHE_NUM)); } /// Recover block creator from signature pub fn recover_creator(header: &Header) -> Result { - // Initialization - let mut cache = CREATOR_BY_HASH.write(); - - if let Some(creator) = cache.get_mut(&header.hash()) { - return Ok(*creator); - } - - let data = header.extra_data(); - if data.len() < VANITY_LENGTH { - Err(EngineError::CliqueMissingVanity)? - } - - if data.len() < VANITY_LENGTH + SIGNATURE_LENGTH { - Err(EngineError::CliqueMissingSignature)? - } - - // Split `signed_extra data` and `signature` - let (signed_data_slice, signature_slice) = data.split_at(data.len() - SIGNATURE_LENGTH); - - // convert `&[u8]` to `[u8; 65]` - let signature = { - let mut s = [0; SIGNATURE_LENGTH]; - s.copy_from_slice(signature_slice); - s - }; - - // modify header and hash it - let unsigned_header = &mut header.clone(); - unsigned_header.set_extra_data(signed_data_slice.to_vec()); - let msg = unsigned_header.hash(); - - let pubkey = ec_recover(&Signature::from(signature), &msg)?; - let creator = public_to_address(&pubkey); - - cache.insert(header.hash(), creator.clone()); - Ok(creator) + // Initialization + let mut cache = CREATOR_BY_HASH.write(); + + if let Some(creator) = cache.get_mut(&header.hash()) { + return Ok(*creator); + } + + let data = header.extra_data(); + if data.len() < VANITY_LENGTH { + Err(EngineError::CliqueMissingVanity)? + } + + if data.len() < VANITY_LENGTH + SIGNATURE_LENGTH { + Err(EngineError::CliqueMissingSignature)? + } + + // Split `signed_extra data` and `signature` + let (signed_data_slice, signature_slice) = data.split_at(data.len() - SIGNATURE_LENGTH); + + // convert `&[u8]` to `[u8; 65]` + let signature = { + let mut s = [0; SIGNATURE_LENGTH]; + s.copy_from_slice(signature_slice); + s + }; + + // modify header and hash it + let unsigned_header = &mut header.clone(); + unsigned_header.set_extra_data(signed_data_slice.to_vec()); + let msg = unsigned_header.hash(); + + let pubkey = ec_recover(&Signature::from(signature), &msg)?; + let creator = public_to_address(&pubkey); + + cache.insert(header.hash(), creator.clone()); + Ok(creator) } /// Extract signer list from extra_data. @@ -83,33 +85,35 @@ pub fn recover_creator(header: &Header) -> Result { /// Signature: 65 bytes /// -- pub fn extract_signers(header: &Header) -> Result, Error> { - let data = header.extra_data(); + let data = header.extra_data(); - if data.len() <= VANITY_LENGTH + SIGNATURE_LENGTH { - Err(EngineError::CliqueCheckpointNoSigner)? - } + if data.len() <= VANITY_LENGTH + SIGNATURE_LENGTH { + Err(EngineError::CliqueCheckpointNoSigner)? + } - // extract only the portion of extra_data which includes the signer list - let signers_raw = &data[(VANITY_LENGTH)..data.len() - (SIGNATURE_LENGTH)]; + // extract only the portion of extra_data which includes the signer list + let signers_raw = &data[(VANITY_LENGTH)..data.len() - (SIGNATURE_LENGTH)]; - if signers_raw.len() % ADDRESS_LENGTH != 0 { - Err(EngineError::CliqueCheckpointInvalidSigners(signers_raw.len()))? - } + if signers_raw.len() % ADDRESS_LENGTH != 0 { + Err(EngineError::CliqueCheckpointInvalidSigners( + signers_raw.len(), + ))? + } - let num_signers = signers_raw.len() / 20; + let num_signers = signers_raw.len() / 20; - let signers: BTreeSet
= (0..num_signers) - .map(|i| { - let start = i * ADDRESS_LENGTH; - let end = start + ADDRESS_LENGTH; - signers_raw[start..end].into() - }) - .collect(); + let signers: BTreeSet
= (0..num_signers) + .map(|i| { + let start = i * ADDRESS_LENGTH; + let end = start + ADDRESS_LENGTH; + signers_raw[start..end].into() + }) + .collect(); - Ok(signers) + Ok(signers) } /// Retrieve `null_seal` pub fn null_seal() -> Vec> { - vec![encode(&NULL_MIXHASH.to_vec()), encode(&NULL_NONCE.to_vec())] + vec![encode(&NULL_MIXHASH.to_vec()), encode(&NULL_NONCE.to_vec())] } diff --git a/ethcore/src/engines/instant_seal.rs b/ethcore/src/engines/instant_seal.rs index a578c990b30..0c7749fa063 100644 --- a/ethcore/src/engines/instant_seal.rs +++ b/ethcore/src/engines/instant_seal.rs @@ -1,129 +1,173 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . +use block::ExecutedBlock; use engines::{Engine, Seal}; use machine::Machine; -use types::header::{Header, ExtendedHeader}; -use block::ExecutedBlock; +use std::sync::atomic::{AtomicU64, Ordering}; +use types::header::{ExtendedHeader, Header}; /// `InstantSeal` params. #[derive(Default, Debug, PartialEq)] pub struct InstantSealParams { - /// Whether to use millisecond timestamp - pub millisecond_timestamp: bool, + /// Whether to use millisecond timestamp + pub millisecond_timestamp: bool, } impl From<::ethjson::spec::InstantSealParams> for InstantSealParams { - fn from(p: ::ethjson::spec::InstantSealParams) -> Self { - InstantSealParams { - millisecond_timestamp: p.millisecond_timestamp, - } - } + fn from(p: ::ethjson::spec::InstantSealParams) -> Self { + InstantSealParams { + millisecond_timestamp: p.millisecond_timestamp, + } + } } /// An engine which does not provide any consensus mechanism, just seals blocks internally. /// Only seals blocks which have transactions. pub struct InstantSeal { - params: InstantSealParams, - machine: M, + params: InstantSealParams, + machine: M, + last_sealed_block: AtomicU64, } impl InstantSeal { - /// Returns new instance of InstantSeal over the given state machine. - pub fn new(params: InstantSealParams, machine: M) -> Self { - InstantSeal { - params, machine, - } - } + /// Returns new instance of InstantSeal over the given state machine. + pub fn new(params: InstantSealParams, machine: M) -> Self { + InstantSeal { + params, + machine, + last_sealed_block: AtomicU64::new(0), + } + } } impl Engine for InstantSeal { - fn name(&self) -> &str { - "InstantSeal" - } - - fn machine(&self) -> &M { &self.machine } - - fn seals_internally(&self) -> Option { Some(true) } - - fn generate_seal(&self, block: &ExecutedBlock, _parent: &Header) -> Seal { - if block.transactions.is_empty() { - Seal::None - } else { - Seal::Regular(Vec::new()) - } - } - - fn verify_local_seal(&self, _header: &Header) -> Result<(), M::Error> { - Ok(()) - } - - fn open_block_header_timestamp(&self, parent_timestamp: u64) -> u64 { - use std::{time, cmp}; - - let dur = time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap_or_default(); - let mut now = dur.as_secs(); - if self.params.millisecond_timestamp { - now = now * 1000 + dur.subsec_millis() as u64; - } - cmp::max(now, parent_timestamp) - } - - fn is_timestamp_valid(&self, header_timestamp: u64, parent_timestamp: u64) -> bool { - header_timestamp >= parent_timestamp - } - - fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice { - super::total_difficulty_fork_choice(new, current) - } + fn name(&self) -> &str { + "InstantSeal" + } + + fn machine(&self) -> &M { + &self.machine + } + + fn seals_internally(&self) -> Option { + Some(true) + } + + fn should_reseal_on_update(&self) -> bool { + // We would like for the miner to `update_sealing` if there are local_pending_transactions + // in the pool to prevent transactions sent in parallel from stalling in the transaction + // pool. (see #9660) + true + } + + fn generate_seal(&self, block: &ExecutedBlock, _parent: &Header) -> Seal { + if !block.transactions.is_empty() { + let block_number = block.header.number(); + let last_sealed_block = self.last_sealed_block.load(Ordering::SeqCst); + // Return a regular seal if the given block is _higher_ than + // the last sealed one + if block_number > last_sealed_block { + let prev_last_sealed_block = self.last_sealed_block.compare_and_swap( + last_sealed_block, + block_number, + Ordering::SeqCst, + ); + if prev_last_sealed_block == last_sealed_block { + return Seal::Regular(Vec::new()); + } + } + } + Seal::None + } + + fn verify_local_seal(&self, _header: &Header) -> Result<(), M::Error> { + Ok(()) + } + + fn open_block_header_timestamp(&self, parent_timestamp: u64) -> u64 { + use std::{cmp, time}; + + let dur = time::SystemTime::now() + .duration_since(time::UNIX_EPOCH) + .unwrap_or_default(); + let mut now = dur.as_secs(); + if self.params.millisecond_timestamp { + now = now * 1000 + dur.subsec_millis() as u64; + } + cmp::max(now, parent_timestamp) + } + + fn is_timestamp_valid(&self, header_timestamp: u64, parent_timestamp: u64) -> bool { + header_timestamp >= parent_timestamp + } + + fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice { + super::total_difficulty_fork_choice(new, current) + } } #[cfg(test)] mod tests { - use std::sync::Arc; - use ethereum_types::{H520, Address}; - use test_helpers::get_temp_state_db; - use spec::Spec; - use types::header::Header; - use block::*; - use engines::Seal; - - #[test] - fn instant_can_seal() { - let spec = Spec::new_instant(); - let engine = &*spec.engine; - let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let genesis_header = spec.genesis_header(); - let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::default(), (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); - let b = b.close_and_lock().unwrap(); - if let Seal::Regular(seal) = engine.generate_seal(&b, &genesis_header) { - assert!(b.try_seal(engine, seal).is_ok()); - } - } - - #[test] - fn instant_cant_verify() { - let engine = Spec::new_instant().engine; - let mut header: Header = Header::default(); - - assert!(engine.verify_block_basic(&header).is_ok()); - - header.set_seal(vec![::rlp::encode(&H520::default())]); - - assert!(engine.verify_block_unordered(&header).is_ok()); - } + use block::*; + use engines::Seal; + use ethereum_types::{Address, H520}; + use spec::Spec; + use std::sync::Arc; + use test_helpers::get_temp_state_db; + use types::header::Header; + + #[test] + fn instant_can_seal() { + let spec = Spec::new_instant(); + let engine = &*spec.engine; + let db = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let genesis_header = spec.genesis_header(); + let last_hashes = Arc::new(vec![genesis_header.hash()]); + let b = OpenBlock::new( + engine, + Default::default(), + false, + db, + &genesis_header, + last_hashes, + Address::default(), + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + let b = b.close_and_lock().unwrap(); + if let Seal::Regular(seal) = engine.generate_seal(&b, &genesis_header) { + assert!(b.try_seal(engine, seal).is_ok()); + } + } + + #[test] + fn instant_cant_verify() { + let engine = Spec::new_instant().engine; + let mut header: Header = Header::default(); + + assert!(engine.verify_block_basic(&header).is_ok()); + + header.set_seal(vec![::rlp::encode(&H520::default())]); + + assert!(engine.verify_block_unordered(&header).is_ok()); + } } diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 5124f079db2..e979f40666c 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Consensus engine specification and basic implementations. @@ -26,37 +26,45 @@ mod validator_set; pub mod block_reward; pub mod signer; -pub use self::authority_round::AuthorityRound; -pub use self::basic_authority::BasicAuthority; -pub use self::instant_seal::{InstantSeal, InstantSealParams}; -pub use self::null_engine::NullEngine; -pub use self::signer::EngineSigner; -pub use self::clique::Clique; +pub use self::{ + authority_round::AuthorityRound, + basic_authority::BasicAuthority, + clique::Clique, + instant_seal::{InstantSeal, InstantSealParams}, + null_engine::NullEngine, + signer::EngineSigner, +}; // TODO [ToDr] Remove re-export (#10130) -pub use types::engines::ForkChoice; -pub use types::engines::epoch::{self, Transition as EpochTransition}; +pub use types::engines::{ + epoch::{self, Transition as EpochTransition}, + ForkChoice, +}; -use std::sync::{Weak, Arc}; -use std::collections::{BTreeMap, HashMap}; -use std::{fmt, error}; +use std::{ + collections::{BTreeMap, HashMap}, + error, fmt, + sync::{Arc, Weak}, +}; use builtin::Builtin; -use vm::{EnvInfo, Schedule, CreateContractAddress, CallType, ActionValue}; use error::Error; -use types::BlockNumber; -use types::header::{Header, ExtendedHeader}; use snapshot::SnapshotComponents; use spec::CommonParams; -use types::transaction::{self, UnverifiedTransaction, SignedTransaction}; +use types::{ + header::{ExtendedHeader, Header}, + transaction::{self, SignedTransaction, UnverifiedTransaction}, + BlockNumber, +}; +use vm::{ActionValue, CallType, CreateContractAddress, EnvInfo, Schedule}; -use ethkey::{Signature}; -use machine::{self, Machine, AuxiliaryRequest, AuxiliaryData}; -use ethereum_types::{H64, H256, U256, Address}; -use unexpected::{Mismatch, OutOfBounds}; +use block::ExecutedBlock; use bytes::Bytes; +use ethereum_types::{Address, H256, H64, U256}; +use ethkey::Signature; +use machine::{self, AuxiliaryData, AuxiliaryRequest, Machine}; use types::ancestry_action::AncestryAction; -use block::ExecutedBlock; +use unexpected::{Mismatch, OutOfBounds}; /// Default EIP-210 contract code. /// As defined in https://github.com/ethereum/EIPs/pull/210 @@ -67,413 +75,445 @@ pub const MAX_UNCLE_AGE: usize = 6; /// Voting errors. #[derive(Debug)] pub enum EngineError { - /// Signature or author field does not belong to an authority. - NotAuthorized(Address), - /// The same author issued different votes at the same step. - DoubleVote(Address), - /// The received block is from an incorrect proposer. - NotProposer(Mismatch
), - /// Message was not expected. - UnexpectedMessage, - /// Seal field has an unexpected size. - BadSealFieldSize(OutOfBounds), - /// Validation proof insufficient. - InsufficientProof(String), - /// Failed system call. - FailedSystemCall(String), - /// Malformed consensus message. - MalformedMessage(String), - /// Requires client ref, but none registered. - RequiresClient, - /// Invalid engine specification or implementation. - InvalidEngine, - /// Requires signer ref, but none registered. - RequiresSigner, - /// Checkpoint is missing - CliqueMissingCheckpoint(H256), - /// Missing vanity data - CliqueMissingVanity, - /// Missing signature - CliqueMissingSignature, - /// Missing signers - CliqueCheckpointNoSigner, - /// List of signers is invalid - CliqueCheckpointInvalidSigners(usize), - /// Wrong author on a checkpoint - CliqueWrongAuthorCheckpoint(Mismatch
), - /// Wrong checkpoint authors recovered - CliqueFaultyRecoveredSigners(Vec), - /// Invalid nonce (should contain vote) - CliqueInvalidNonce(H64), - /// The signer signed a block to recently - CliqueTooRecentlySigned(Address), - /// Custom - Custom(String), + /// Signature or author field does not belong to an authority. + NotAuthorized(Address), + /// The same author issued different votes at the same step. + DoubleVote(Address), + /// The received block is from an incorrect proposer. + NotProposer(Mismatch
), + /// Message was not expected. + UnexpectedMessage, + /// Seal field has an unexpected size. + BadSealFieldSize(OutOfBounds), + /// Validation proof insufficient. + InsufficientProof(String), + /// Failed system call. + FailedSystemCall(String), + /// Malformed consensus message. + MalformedMessage(String), + /// Requires client ref, but none registered. + RequiresClient, + /// Invalid engine specification or implementation. + InvalidEngine, + /// Requires signer ref, but none registered. + RequiresSigner, + /// Checkpoint is missing + CliqueMissingCheckpoint(H256), + /// Missing vanity data + CliqueMissingVanity, + /// Missing signature + CliqueMissingSignature, + /// Missing signers + CliqueCheckpointNoSigner, + /// List of signers is invalid + CliqueCheckpointInvalidSigners(usize), + /// Wrong author on a checkpoint + CliqueWrongAuthorCheckpoint(Mismatch
), + /// Wrong checkpoint authors recovered + CliqueFaultyRecoveredSigners(Vec), + /// Invalid nonce (should contain vote) + CliqueInvalidNonce(H64), + /// The signer signed a block to recently + CliqueTooRecentlySigned(Address), + /// Custom + Custom(String), } impl fmt::Display for EngineError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::EngineError::*; - let msg = match *self { - CliqueMissingCheckpoint(ref hash) => format!("Missing checkpoint block: {}", hash), - CliqueMissingVanity => format!("Extra data is missing vanity data"), - CliqueMissingSignature => format!("Extra data is missing signature"), - CliqueCheckpointInvalidSigners(len) => format!("Checkpoint block list was of length: {} of checkpoint but - it needs to be bigger than zero and a divisible by 20", len), - CliqueCheckpointNoSigner => format!("Checkpoint block list of signers was empty"), - CliqueInvalidNonce(ref mis) => format!("Unexpected nonce {} expected {} or {}", mis, 0_u64, u64::max_value()), - CliqueWrongAuthorCheckpoint(ref oob) => format!("Unexpected checkpoint author: {}", oob), - CliqueFaultyRecoveredSigners(ref mis) => format!("Faulty recovered signers {:?}", mis), - CliqueTooRecentlySigned(ref address) => format!("The signer: {} has signed a block too recently", address), - Custom(ref s) => s.clone(), - DoubleVote(ref address) => format!("Author {} issued too many blocks.", address), - NotProposer(ref mis) => format!("Author is not a current proposer: {}", mis), - NotAuthorized(ref address) => format!("Signer {} is not authorized.", address), - UnexpectedMessage => "This Engine should not be fed messages.".into(), - BadSealFieldSize(ref oob) => format!("Seal field has an unexpected length: {}", oob), - InsufficientProof(ref msg) => format!("Insufficient validation proof: {}", msg), - FailedSystemCall(ref msg) => format!("Failed to make system call: {}", msg), - MalformedMessage(ref msg) => format!("Received malformed consensus message: {}", msg), - RequiresClient => format!("Call requires client but none registered"), - RequiresSigner => format!("Call requires signer but none registered"), - InvalidEngine => format!("Invalid engine specification or implementation"), - }; - - f.write_fmt(format_args!("Engine error ({})", msg)) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::EngineError::*; + let msg = match *self { + CliqueMissingCheckpoint(ref hash) => format!("Missing checkpoint block: {}", hash), + CliqueMissingVanity => format!("Extra data is missing vanity data"), + CliqueMissingSignature => format!("Extra data is missing signature"), + CliqueCheckpointInvalidSigners(len) => format!( + "Checkpoint block list was of length: {} of checkpoint but + it needs to be bigger than zero and a divisible by 20", + len + ), + CliqueCheckpointNoSigner => format!("Checkpoint block list of signers was empty"), + CliqueInvalidNonce(ref mis) => format!( + "Unexpected nonce {} expected {} or {}", + mis, + 0_u64, + u64::max_value() + ), + CliqueWrongAuthorCheckpoint(ref oob) => { + format!("Unexpected checkpoint author: {}", oob) + } + CliqueFaultyRecoveredSigners(ref mis) => format!("Faulty recovered signers {:?}", mis), + CliqueTooRecentlySigned(ref address) => { + format!("The signer: {} has signed a block too recently", address) + } + Custom(ref s) => s.clone(), + DoubleVote(ref address) => format!("Author {} issued too many blocks.", address), + NotProposer(ref mis) => format!("Author is not a current proposer: {}", mis), + NotAuthorized(ref address) => format!("Signer {} is not authorized.", address), + UnexpectedMessage => "This Engine should not be fed messages.".into(), + BadSealFieldSize(ref oob) => format!("Seal field has an unexpected length: {}", oob), + InsufficientProof(ref msg) => format!("Insufficient validation proof: {}", msg), + FailedSystemCall(ref msg) => format!("Failed to make system call: {}", msg), + MalformedMessage(ref msg) => format!("Received malformed consensus message: {}", msg), + RequiresClient => format!("Call requires client but none registered"), + RequiresSigner => format!("Call requires signer but none registered"), + InvalidEngine => format!("Invalid engine specification or implementation"), + }; + + f.write_fmt(format_args!("Engine error ({})", msg)) + } } impl error::Error for EngineError { - fn description(&self) -> &str { - "Engine error" - } + fn description(&self) -> &str { + "Engine error" + } } /// Seal type. #[derive(Debug, PartialEq, Eq)] pub enum Seal { - /// Proposal seal; should be broadcasted, but not inserted into blockchain. - Proposal(Vec), - /// Regular block seal; should be part of the blockchain. - Regular(Vec), - /// Engine does not generate seal for this block right now. - None, + /// Proposal seal; should be broadcasted, but not inserted into blockchain. + Proposal(Vec), + /// Regular block seal; should be part of the blockchain. + Regular(Vec), + /// Engine does not generate seal for this block right now. + None, } /// A system-calling closure. Enacts calls on a block's state from the system address. -pub type SystemCall<'a> = FnMut(Address, Vec) -> Result, String> + 'a; +pub type SystemCall<'a> = dyn FnMut(Address, Vec) -> Result, String> + 'a; /// A system-calling closure. Enacts calls on a block's state with code either from an on-chain contract, or hard-coded EVM or WASM (if enabled on-chain) codes. -pub type SystemOrCodeCall<'a> = FnMut(SystemOrCodeCallKind, Vec) -> Result, String> + 'a; +pub type SystemOrCodeCall<'a> = + dyn FnMut(SystemOrCodeCallKind, Vec) -> Result, String> + 'a; /// Kind of SystemOrCodeCall, this is either an on-chain address, or code. #[derive(PartialEq, Debug, Clone)] pub enum SystemOrCodeCallKind { - /// On-chain address. - Address(Address), - /// Hard-coded code. - Code(Arc>, H256), + /// On-chain address. + Address(Address), + /// Hard-coded code. + Code(Arc>, H256), } /// Default SystemOrCodeCall implementation. -pub fn default_system_or_code_call<'a>(machine: &'a ::machine::EthereumMachine, block: &'a mut ::block::ExecutedBlock) -> impl FnMut(SystemOrCodeCallKind, Vec) -> Result, String> + 'a { - move |to, data| { - let result = match to { - SystemOrCodeCallKind::Address(address) => { - machine.execute_as_system( - block, - address, - U256::max_value(), - Some(data), - ) - }, - SystemOrCodeCallKind::Code(code, code_hash) => { - machine.execute_code_as_system( - block, - None, - Some(code), - Some(code_hash), - Some(ActionValue::Apparent(U256::zero())), - U256::max_value(), - Some(data), - Some(CallType::StaticCall), - ) - }, - }; - - result.map_err(|e| format!("{}", e)) - } +pub fn default_system_or_code_call<'a>( + machine: &'a ::machine::EthereumMachine, + block: &'a mut ::block::ExecutedBlock, +) -> impl FnMut(SystemOrCodeCallKind, Vec) -> Result, String> + 'a { + move |to, data| { + let result = match to { + SystemOrCodeCallKind::Address(address) => { + machine.execute_as_system(block, address, U256::max_value(), Some(data)) + } + SystemOrCodeCallKind::Code(code, code_hash) => machine.execute_code_as_system( + block, + None, + Some(code), + Some(code_hash), + Some(ActionValue::Apparent(U256::zero())), + U256::max_value(), + Some(data), + Some(CallType::StaticCall), + ), + }; + + result.map_err(|e| format!("{}", e)) + } } /// Type alias for a function we can get headers by hash through. -pub type Headers<'a, H> = Fn(H256) -> Option + 'a; +pub type Headers<'a, H> = dyn Fn(H256) -> Option + 'a; /// Type alias for a function we can query pending transitions by block hash through. -pub type PendingTransitionStore<'a> = Fn(H256) -> Option + 'a; +pub type PendingTransitionStore<'a> = dyn Fn(H256) -> Option + 'a; /// Proof dependent on state. pub trait StateDependentProof: Send + Sync { - /// Generate a proof, given the state. - fn generate_proof<'a>(&self, state: &machine::Call) -> Result, String>; - /// Check a proof generated elsewhere (potentially by a peer). - // `engine` needed to check state proofs, while really this should - // just be state machine params. - fn check_proof(&self, machine: &M, proof: &[u8]) -> Result<(), String>; + /// Generate a proof, given the state. + fn generate_proof<'a>(&self, state: &machine::Call) -> Result, String>; + /// Check a proof generated elsewhere (potentially by a peer). + // `engine` needed to check state proofs, while really this should + // just be state machine params. + fn check_proof(&self, machine: &M, proof: &[u8]) -> Result<(), String>; } /// Proof generated on epoch change. pub enum Proof { - /// Known proof (extracted from signal) - Known(Vec), - /// State dependent proof. - WithState(Arc>), + /// Known proof (extracted from signal) + Known(Vec), + /// State dependent proof. + WithState(Arc>), } /// Generated epoch verifier. pub enum ConstructedVerifier<'a, M: Machine> { - /// Fully trusted verifier. - Trusted(Box>), - /// Verifier unconfirmed. Check whether given finality proof finalizes given hash - /// under previous epoch. - Unconfirmed(Box>, &'a [u8], H256), - /// Error constructing verifier. - Err(Error), + /// Fully trusted verifier. + Trusted(Box>), + /// Verifier unconfirmed. Check whether given finality proof finalizes given hash + /// under previous epoch. + Unconfirmed(Box>, &'a [u8], H256), + /// Error constructing verifier. + Err(Error), } impl<'a, M: Machine> ConstructedVerifier<'a, M> { - /// Convert to a result, indicating that any necessary confirmation has been done - /// already. - pub fn known_confirmed(self) -> Result>, Error> { - match self { - ConstructedVerifier::Trusted(v) | ConstructedVerifier::Unconfirmed(v, _, _) => Ok(v), - ConstructedVerifier::Err(e) => Err(e), - } - } + /// Convert to a result, indicating that any necessary confirmation has been done + /// already. + pub fn known_confirmed(self) -> Result>, Error> { + match self { + ConstructedVerifier::Trusted(v) | ConstructedVerifier::Unconfirmed(v, _, _) => Ok(v), + ConstructedVerifier::Err(e) => Err(e), + } + } } /// Results of a query of whether an epoch change occurred at the given block. pub enum EpochChange { - /// Cannot determine until more data is passed. - Unsure(AuxiliaryRequest), - /// No epoch change. - No, - /// The epoch will change, with proof. - Yes(Proof), + /// Cannot determine until more data is passed. + Unsure(AuxiliaryRequest), + /// No epoch change. + No, + /// The epoch will change, with proof. + Yes(Proof), } /// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based. /// Provides hooks into each of the major parts of block import. pub trait Engine: Sync + Send { - /// The name of this engine. - fn name(&self) -> &str; - - /// Get access to the underlying state machine. - // TODO: decouple. - fn machine(&self) -> &M; - - /// The number of additional header fields required for this engine. - fn seal_fields(&self, _header: &Header) -> usize { 0 } - - /// Additional engine-specific information for the user/developer concerning `header`. - fn extra_info(&self, _header: &Header) -> BTreeMap { BTreeMap::new() } - - /// Maximum number of uncles a block is allowed to declare. - fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 0 } - - /// Optional maximum gas limit. - fn maximum_gas_limit(&self) -> Option { None } - - /// Block transformation functions, before the transactions. - /// `epoch_begin` set to true if this block kicks off an epoch. - fn on_new_block( - &self, - _block: &mut ExecutedBlock, - _epoch_begin: bool, - _ancestry: &mut Iterator, - ) -> Result<(), M::Error> { - Ok(()) - } - - /// Block transformation functions, after the transactions. - fn on_close_block(&self, _block: &mut ExecutedBlock) -> Result<(), M::Error> { - Ok(()) - } - - /// Allow mutating the header during seal generation. Currently only used by Clique. - fn on_seal_block(&self, _block: &mut ExecutedBlock) -> Result<(), Error> { Ok(()) } - - /// None means that it requires external input (e.g. PoW) to seal a block. - /// Some(true) means the engine is currently prime for seal generation (i.e. node is the current validator). - /// Some(false) means that the node might seal internally but is not qualified now. - fn seals_internally(&self) -> Option { None } - - /// Attempt to seal the block internally. - /// - /// If `Some` is returned, then you get a valid seal. - /// - /// This operation is synchronous and may (quite reasonably) not be available, in which None will - /// be returned. - /// - /// It is fine to require access to state or a full client for this function, since - /// light clients do not generate seals. - fn generate_seal(&self, _block: &ExecutedBlock, _parent: &Header) -> Seal { Seal::None } - - /// Verify a locally-generated seal of a header. - /// - /// If this engine seals internally, - /// no checks have to be done here, since all internally generated seals - /// should be valid. - /// - /// Externally-generated seals (e.g. PoW) will need to be checked for validity. - /// - /// It is fine to require access to state or a full client for this function, since - /// light clients do not generate seals. - fn verify_local_seal(&self, header: &Header) -> Result<(), M::Error>; - - /// Phase 1 quick block verification. Only does checks that are cheap. Returns either a null `Ok` or a general error detailing the problem with import. - /// The verification module can optionally avoid checking the seal (`check_seal`), if seal verification is disabled this method won't be called. - fn verify_block_basic(&self, _header: &Header) -> Result<(), M::Error> { Ok(()) } - - /// Phase 2 verification. Perform costly checks such as transaction signatures. Returns either a null `Ok` or a general error detailing the problem with import. - /// The verification module can optionally avoid checking the seal (`check_seal`), if seal verification is disabled this method won't be called. - fn verify_block_unordered(&self, _header: &Header) -> Result<(), M::Error> { Ok(()) } - - /// Phase 3 verification. Check block information against parent. Returns either a null `Ok` or a general error detailing the problem with import. - fn verify_block_family(&self, _header: &Header, _parent: &Header) -> Result<(), M::Error> { Ok(()) } - - /// Phase 4 verification. Verify block header against potentially external data. - /// Should only be called when `register_client` has been called previously. - fn verify_block_external(&self, _header: &Header) -> Result<(), M::Error> { Ok(()) } - - /// Genesis epoch data. - fn genesis_epoch_data<'a>(&self, _header: &Header, _state: &machine::Call) -> Result, String> { Ok(Vec::new()) } - - /// Whether an epoch change is signalled at the given header but will require finality. - /// If a change can be enacted immediately then return `No` from this function but - /// `Yes` from `is_epoch_end`. - /// - /// If auxiliary data of the block is required, return an auxiliary request and the function will be - /// called again with them. - /// Return `Yes` or `No` when the answer is definitively known. - /// - /// Should not interact with state. - fn signals_epoch_end<'a>(&self, _header: &Header, _aux: AuxiliaryData<'a>) - -> EpochChange - { - EpochChange::No - } - - /// Whether a block is the end of an epoch. - /// - /// This either means that an immediate transition occurs or a block signalling transition - /// has reached finality. The `Headers` given are not guaranteed to return any blocks - /// from any epoch other than the current. The client must keep track of finality and provide - /// the latest finalized headers to check against the transition store. - /// - /// Return optional transition proof. - fn is_epoch_end( - &self, - _chain_head: &Header, - _finalized: &[H256], - _chain: &Headers
, - _transition_store: &PendingTransitionStore, - ) -> Option> { - None - } - - /// Whether a block is the end of an epoch. - /// - /// This either means that an immediate transition occurs or a block signalling transition - /// has reached finality. The `Headers` given are not guaranteed to return any blocks - /// from any epoch other than the current. This is a specialized method to use for light - /// clients since the light client doesn't track finality of all blocks, and therefore finality - /// for blocks in the current epoch is built inside this method by the engine. - /// - /// Return optional transition proof. - fn is_epoch_end_light( - &self, - _chain_head: &Header, - _chain: &Headers
, - _transition_store: &PendingTransitionStore, - ) -> Option> { - None - } - - /// Create an epoch verifier from validation proof and a flag indicating - /// whether finality is required. - fn epoch_verifier<'a>(&self, _header: &Header, _proof: &'a [u8]) -> ConstructedVerifier<'a, M> { - ConstructedVerifier::Trusted(Box::new(NoOp)) - } - - /// Populate a header's fields based on its parent's header. - /// Usually implements the chain scoring rule based on weight. - fn populate_from_parent(&self, _header: &mut Header, _parent: &Header) { } - - /// Handle any potential consensus messages; - /// updating consensus state and potentially issuing a new one. - fn handle_message(&self, _message: &[u8]) -> Result<(), EngineError> { Err(EngineError::UnexpectedMessage) } - - /// Register a component which signs consensus messages. - fn set_signer(&self, _signer: Box) {} - - /// Sign using the EngineSigner, to be used for consensus tx signing. - fn sign(&self, _hash: H256) -> Result { unimplemented!() } - - /// Add Client which can be used for sealing, potentially querying the state and sending messages. - fn register_client(&self, _client: Weak) {} - - /// Trigger next step of the consensus engine. - fn step(&self) {} - - /// Stops any services that the may hold the Engine and makes it safe to drop. - fn stop(&mut self) {} - - /// Create a factory for building snapshot chunks and restoring from them. - /// Returning `None` indicates that this engine doesn't support snapshot creation. - fn snapshot_components(&self) -> Option> { - None - } - - /// Whether this engine supports warp sync. - fn supports_warp(&self) -> bool { - self.snapshot_components().is_some() - } - - /// Return a new open block header timestamp based on the parent timestamp. - fn open_block_header_timestamp(&self, parent_timestamp: u64) -> u64 { - use std::{time, cmp}; - - let now = time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap_or_default(); - cmp::max(now.as_secs() as u64, parent_timestamp + 1) - } - - /// Check whether the parent timestamp is valid. - fn is_timestamp_valid(&self, header_timestamp: u64, parent_timestamp: u64) -> bool { - header_timestamp > parent_timestamp - } - - /// Gather all ancestry actions. Called at the last stage when a block is committed. The Engine must guarantee that - /// the ancestry exists. - fn ancestry_actions(&self, _header: &Header, _ancestry: &mut Iterator) -> Vec { - Vec::new() - } - - /// Check whether the given new block is the best block, after finalization check. - fn fork_choice(&self, new: &ExtendedHeader, best: &ExtendedHeader) -> ForkChoice; - - /// Returns author should used when executing tx's for this block. - fn executive_author(&self, header: &Header) -> Result { - Ok(*header.author()) - } + /// The name of this engine. + fn name(&self) -> &str; + + /// Get access to the underlying state machine. + // TODO: decouple. + fn machine(&self) -> &M; + + /// The number of additional header fields required for this engine. + fn seal_fields(&self, _header: &Header) -> usize { + 0 + } + + /// Additional engine-specific information for the user/developer concerning `header`. + fn extra_info(&self, _header: &Header) -> BTreeMap { + BTreeMap::new() + } + + /// Maximum number of uncles a block is allowed to declare. + fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { + 0 + } + + /// Optional maximum gas limit. + fn maximum_gas_limit(&self) -> Option { + None + } + + /// Block transformation functions, before the transactions. + /// `epoch_begin` set to true if this block kicks off an epoch. + fn on_new_block( + &self, + _block: &mut ExecutedBlock, + _epoch_begin: bool, + _ancestry: &mut dyn Iterator, + ) -> Result<(), M::Error> { + Ok(()) + } + + /// Block transformation functions, after the transactions. + fn on_close_block(&self, _block: &mut ExecutedBlock) -> Result<(), M::Error> { + Ok(()) + } + + /// Allow mutating the header during seal generation. Currently only used by Clique. + fn on_seal_block(&self, _block: &mut ExecutedBlock) -> Result<(), Error> { + Ok(()) + } + + /// None means that it requires external input (e.g. PoW) to seal a block. + /// Some(true) means the engine is currently prime for seal generation (i.e. node is the current validator). + /// Some(false) means that the node might seal internally but is not qualified now. + fn seals_internally(&self) -> Option { + None + } + + /// Called in `miner.chain_new_blocks` if the engine wishes to `update_sealing` + /// after a block was recently sealed. + /// + /// returns false by default + fn should_reseal_on_update(&self) -> bool { + false + } + + /// Attempt to seal the block internally. + /// + /// If `Some` is returned, then you get a valid seal. + /// + /// This operation is synchronous and may (quite reasonably) not be available, in which None will + /// be returned. + /// + /// It is fine to require access to state or a full client for this function, since + /// light clients do not generate seals. + fn generate_seal(&self, _block: &ExecutedBlock, _parent: &Header) -> Seal { + Seal::None + } + + /// Verify a locally-generated seal of a header. + /// + /// If this engine seals internally, + /// no checks have to be done here, since all internally generated seals + /// should be valid. + /// + /// Externally-generated seals (e.g. PoW) will need to be checked for validity. + /// + /// It is fine to require access to state or a full client for this function, since + /// light clients do not generate seals. + fn verify_local_seal(&self, header: &Header) -> Result<(), M::Error>; + + /// Phase 1 quick block verification. Only does checks that are cheap. Returns either a null `Ok` or a general error detailing the problem with import. + /// The verification module can optionally avoid checking the seal (`check_seal`), if seal verification is disabled this method won't be called. + fn verify_block_basic(&self, _header: &Header) -> Result<(), M::Error> { + Ok(()) + } + + /// Phase 2 verification. Perform costly checks such as transaction signatures. Returns either a null `Ok` or a general error detailing the problem with import. + /// The verification module can optionally avoid checking the seal (`check_seal`), if seal verification is disabled this method won't be called. + fn verify_block_unordered(&self, _header: &Header) -> Result<(), M::Error> { + Ok(()) + } + + /// Phase 3 verification. Check block information against parent. Returns either a null `Ok` or a general error detailing the problem with import. + fn verify_block_family(&self, _header: &Header, _parent: &Header) -> Result<(), M::Error> { + Ok(()) + } + + /// Phase 4 verification. Verify block header against potentially external data. + /// Should only be called when `register_client` has been called previously. + fn verify_block_external(&self, _header: &Header) -> Result<(), M::Error> { + Ok(()) + } + + /// Genesis epoch data. + fn genesis_epoch_data<'a>( + &self, + _header: &Header, + _state: &machine::Call, + ) -> Result, String> { + Ok(Vec::new()) + } + + /// Whether an epoch change is signalled at the given header but will require finality. + /// If a change can be enacted immediately then return `No` from this function but + /// `Yes` from `is_epoch_end`. + /// + /// If auxiliary data of the block is required, return an auxiliary request and the function will be + /// called again with them. + /// Return `Yes` or `No` when the answer is definitively known. + /// + /// Should not interact with state. + fn signals_epoch_end<'a>(&self, _header: &Header, _aux: AuxiliaryData<'a>) -> EpochChange { + EpochChange::No + } + + /// Whether a block is the end of an epoch. + /// + /// This either means that an immediate transition occurs or a block signalling transition + /// has reached finality. The `Headers` given are not guaranteed to return any blocks + /// from any epoch other than the current. The client must keep track of finality and provide + /// the latest finalized headers to check against the transition store. + /// + /// Return optional transition proof. + fn is_epoch_end( + &self, + _chain_head: &Header, + _finalized: &[H256], + _chain: &Headers
, + _transition_store: &PendingTransitionStore, + ) -> Option> { + None + } + + /// Create an epoch verifier from validation proof and a flag indicating + /// whether finality is required. + fn epoch_verifier<'a>(&self, _header: &Header, _proof: &'a [u8]) -> ConstructedVerifier<'a, M> { + ConstructedVerifier::Trusted(Box::new(NoOp)) + } + + /// Populate a header's fields based on its parent's header. + /// Usually implements the chain scoring rule based on weight. + fn populate_from_parent(&self, _header: &mut Header, _parent: &Header) {} + + /// Handle any potential consensus messages; + /// updating consensus state and potentially issuing a new one. + fn handle_message(&self, _message: &[u8]) -> Result<(), EngineError> { + Err(EngineError::UnexpectedMessage) + } + + /// Register a component which signs consensus messages. + fn set_signer(&self, _signer: Box) {} + + /// Sign using the EngineSigner, to be used for consensus tx signing. + fn sign(&self, _hash: H256) -> Result { + unimplemented!() + } + + /// Add Client which can be used for sealing, potentially querying the state and sending messages. + fn register_client(&self, _client: Weak) {} + + /// Trigger next step of the consensus engine. + fn step(&self) {} + + /// Create a factory for building snapshot chunks and restoring from them. + /// Returning `None` indicates that this engine doesn't support snapshot creation. + fn snapshot_components(&self) -> Option> { + None + } + + /// Whether this engine supports warp sync. + fn supports_warp(&self) -> bool { + self.snapshot_components().is_some() + } + + /// Return a new open block header timestamp based on the parent timestamp. + fn open_block_header_timestamp(&self, parent_timestamp: u64) -> u64 { + use std::{cmp, time}; + + let now = time::SystemTime::now() + .duration_since(time::UNIX_EPOCH) + .unwrap_or_default(); + cmp::max(now.as_secs() as u64, parent_timestamp + 1) + } + + /// Check whether the parent timestamp is valid. + fn is_timestamp_valid(&self, header_timestamp: u64, parent_timestamp: u64) -> bool { + header_timestamp > parent_timestamp + } + + /// Gather all ancestry actions. Called at the last stage when a block is committed. The Engine must guarantee that + /// the ancestry exists. + fn ancestry_actions( + &self, + _header: &Header, + _ancestry: &mut dyn Iterator, + ) -> Vec { + Vec::new() + } + + /// Check whether the given new block is the best block, after finalization check. + fn fork_choice(&self, new: &ExtendedHeader, best: &ExtendedHeader) -> ForkChoice; + + /// Returns author should used when executing tx's for this block. + fn executive_author(&self, header: &Header) -> Result { + Ok(*header.author()) + } } /// Check whether a given block is the best block based on the default total difficulty rule. pub fn total_difficulty_fork_choice(new: &ExtendedHeader, best: &ExtendedHeader) -> ForkChoice { - if new.total_score() > best.total_score() { - ForkChoice::New - } else { - ForkChoice::Old - } + if new.total_score() > best.total_score() { + ForkChoice::New + } else { + ForkChoice::Old + } } /// Common type alias for an engine coupled with an Ethereum-like state machine. @@ -481,109 +521,122 @@ pub fn total_difficulty_fork_choice(new: &ExtendedHeader, best: &ExtendedHeader) // fortunately the effect is largely the same since engines are mostly used // via trait objects. pub trait EthEngine: Engine<::machine::EthereumMachine> { - /// Get the general parameters of the chain. - fn params(&self) -> &CommonParams { - self.machine().params() - } - - /// Get the EVM schedule for the given block number. - fn schedule(&self, block_number: BlockNumber) -> Schedule { - self.machine().schedule(block_number) - } - - /// Builtin-contracts for the chain.. - fn builtins(&self) -> &BTreeMap { - self.machine().builtins() - } - - /// Attempt to get a handle to a built-in contract. - /// Only returns references to activated built-ins. - fn builtin(&self, a: &Address, block_number: BlockNumber) -> Option<&Builtin> { - self.machine().builtin(a, block_number) - } - - /// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`. - fn maximum_extra_data_size(&self) -> usize { - self.machine().maximum_extra_data_size() - } - - /// The nonce with which accounts begin at given block. - fn account_start_nonce(&self, block: BlockNumber) -> U256 { - self.machine().account_start_nonce(block) - } - - /// The network ID that transactions should be signed with. - fn signing_chain_id(&self, env_info: &EnvInfo) -> Option { - self.machine().signing_chain_id(env_info) - } - - /// Returns new contract address generation scheme at given block number. - fn create_address_scheme(&self, number: BlockNumber) -> CreateContractAddress { - self.machine().create_address_scheme(number) - } - - /// Verify a particular transaction is valid. - /// - /// Unordered verification doesn't rely on the transaction execution order, - /// i.e. it should only verify stuff that doesn't assume any previous transactions - /// has already been verified and executed. - /// - /// NOTE This function consumes an `UnverifiedTransaction` and produces `SignedTransaction` - /// which implies that a heavy check of the signature is performed here. - fn verify_transaction_unordered(&self, t: UnverifiedTransaction, header: &Header) -> Result { - self.machine().verify_transaction_unordered(t, header) - } - - /// Perform basic/cheap transaction verification. - /// - /// This should include all cheap checks that can be done before - /// actually checking the signature, like chain-replay protection. - /// - /// NOTE This is done before the signature is recovered so avoid - /// doing any state-touching checks that might be expensive. - /// - /// TODO: Add flags for which bits of the transaction to check. - /// TODO: consider including State in the params. - fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), transaction::Error> { - self.machine().verify_transaction_basic(t, header) - } - - /// Additional information. - fn additional_params(&self) -> HashMap { - self.machine().additional_params() - } - - /// Performs pre-validation of RLP decoded transaction before other processing - fn decode_transaction(&self, transaction: &[u8]) -> Result { - self.machine().decode_transaction(transaction) - } + /// Get the general parameters of the chain. + fn params(&self) -> &CommonParams { + self.machine().params() + } + + /// Get the EVM schedule for the given block number. + fn schedule(&self, block_number: BlockNumber) -> Schedule { + self.machine().schedule(block_number) + } + + /// Builtin-contracts for the chain.. + fn builtins(&self) -> &BTreeMap { + self.machine().builtins() + } + + /// Attempt to get a handle to a built-in contract. + /// Only returns references to activated built-ins. + fn builtin(&self, a: &Address, block_number: BlockNumber) -> Option<&Builtin> { + self.machine().builtin(a, block_number) + } + + /// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`. + fn maximum_extra_data_size(&self) -> usize { + self.machine().maximum_extra_data_size() + } + + /// The nonce with which accounts begin at given block. + fn account_start_nonce(&self, block: BlockNumber) -> U256 { + self.machine().account_start_nonce(block) + } + + /// The network ID that transactions should be signed with. + fn signing_chain_id(&self, env_info: &EnvInfo) -> Option { + self.machine().signing_chain_id(env_info) + } + + /// Returns new contract address generation scheme at given block number. + fn create_address_scheme(&self, number: BlockNumber) -> CreateContractAddress { + self.machine().create_address_scheme(number) + } + + /// Verify a particular transaction is valid. + /// + /// Unordered verification doesn't rely on the transaction execution order, + /// i.e. it should only verify stuff that doesn't assume any previous transactions + /// has already been verified and executed. + /// + /// NOTE This function consumes an `UnverifiedTransaction` and produces `SignedTransaction` + /// which implies that a heavy check of the signature is performed here. + fn verify_transaction_unordered( + &self, + t: UnverifiedTransaction, + header: &Header, + ) -> Result { + self.machine().verify_transaction_unordered(t, header) + } + + /// Perform basic/cheap transaction verification. + /// + /// This should include all cheap checks that can be done before + /// actually checking the signature, like chain-replay protection. + /// + /// NOTE This is done before the signature is recovered so avoid + /// doing any state-touching checks that might be expensive. + /// + /// TODO: Add flags for which bits of the transaction to check. + /// TODO: consider including State in the params. + fn verify_transaction_basic( + &self, + t: &UnverifiedTransaction, + header: &Header, + ) -> Result<(), transaction::Error> { + self.machine().verify_transaction_basic(t, header) + } + + /// Additional information. + fn additional_params(&self) -> HashMap { + self.machine().additional_params() + } + + /// Performs pre-validation of RLP decoded transaction before other processing + fn decode_transaction( + &self, + transaction: &[u8], + ) -> Result { + self.machine().decode_transaction(transaction) + } } // convenience wrappers for existing functions. -impl EthEngine for T where T: Engine<::machine::EthereumMachine> { } +impl EthEngine for T where T: Engine<::machine::EthereumMachine> {} /// Verifier for all blocks within an epoch with self-contained state. pub trait EpochVerifier: Send + Sync { - /// Lightly verify the next block header. - /// This may not be a header belonging to a different epoch. - fn verify_light(&self, header: &Header) -> Result<(), M::Error>; - - /// Perform potentially heavier checks on the next block header. - fn verify_heavy(&self, header: &Header) -> Result<(), M::Error> { - self.verify_light(header) - } - - /// Check a finality proof against this epoch verifier. - /// Returns `Some(hashes)` if the proof proves finality of these hashes. - /// Returns `None` if the proof doesn't prove anything. - fn check_finality_proof(&self, _proof: &[u8]) -> Option> { - None - } + /// Lightly verify the next block header. + /// This may not be a header belonging to a different epoch. + fn verify_light(&self, header: &Header) -> Result<(), M::Error>; + + /// Perform potentially heavier checks on the next block header. + fn verify_heavy(&self, header: &Header) -> Result<(), M::Error> { + self.verify_light(header) + } + + /// Check a finality proof against this epoch verifier. + /// Returns `Some(hashes)` if the proof proves finality of these hashes. + /// Returns `None` if the proof doesn't prove anything. + fn check_finality_proof(&self, _proof: &[u8]) -> Option> { + None + } } /// Special "no-op" verifier for stateless, epoch-less engines. pub struct NoOp; impl EpochVerifier for NoOp { - fn verify_light(&self, _header: &Header) -> Result<(), M::Error> { Ok(()) } + fn verify_light(&self, _header: &Header) -> Result<(), M::Error> { + Ok(()) + } } diff --git a/ethcore/src/engines/null_engine.rs b/ethcore/src/engines/null_engine.rs index 27138985ad6..f23e020d658 100644 --- a/ethcore/src/engines/null_engine.rs +++ b/ethcore/src/engines/null_engine.rs @@ -1,109 +1,143 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use engines::Engine; -use engines::block_reward::{self, RewardKind}; +use block::ExecutedBlock; +use engines::{ + block_reward::{self, RewardKind}, + Engine, +}; use ethereum_types::U256; use machine::Machine; -use types::BlockNumber; -use types::header::{Header, ExtendedHeader}; -use block::ExecutedBlock; +use types::{ + ancestry_action::AncestryAction, + header::{ExtendedHeader, Header}, + BlockNumber, +}; /// Params for a null engine. #[derive(Clone, Default)] pub struct NullEngineParams { - /// base reward for a block. - pub block_reward: U256, + /// base reward for a block. + pub block_reward: U256, + /// Immediate finalization. + pub immediate_finalization: bool, } impl From<::ethjson::spec::NullEngineParams> for NullEngineParams { - fn from(p: ::ethjson::spec::NullEngineParams) -> Self { - NullEngineParams { - block_reward: p.block_reward.map_or_else(Default::default, Into::into), - } - } + fn from(p: ::ethjson::spec::NullEngineParams) -> Self { + NullEngineParams { + block_reward: p.block_reward.map_or_else(Default::default, Into::into), + immediate_finalization: p.immediate_finalization.unwrap_or(false), + } + } } /// An engine which does not provide any consensus mechanism and does not seal blocks. pub struct NullEngine { - params: NullEngineParams, - machine: M, + params: NullEngineParams, + machine: M, } impl NullEngine { - /// Returns new instance of NullEngine with default VM Factory - pub fn new(params: NullEngineParams, machine: M) -> Self { - NullEngine { - params: params, - machine: machine, - } - } + /// Returns new instance of NullEngine with default VM Factory + pub fn new(params: NullEngineParams, machine: M) -> Self { + NullEngine { + params: params, + machine: machine, + } + } } impl Default for NullEngine { - fn default() -> Self { - Self::new(Default::default(), Default::default()) - } + fn default() -> Self { + Self::new(Default::default(), Default::default()) + } } impl Engine for NullEngine { - fn name(&self) -> &str { - "NullEngine" - } - - fn machine(&self) -> &M { &self.machine } - - fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), M::Error> { - use std::ops::Shr; - - let author = *block.header.author(); - let number = block.header.number(); - - let reward = self.params.block_reward; - if reward == U256::zero() { return Ok(()) } - - let n_uncles = block.uncles.len(); - - let mut rewards = Vec::new(); - - // Bestow block reward - let result_block_reward = reward + reward.shr(5) * U256::from(n_uncles); - rewards.push((author, RewardKind::Author, result_block_reward)); - - // bestow uncle rewards. - for u in &block.uncles { - let uncle_author = u.author(); - let result_uncle_reward = (reward * U256::from(8 + u.number() - number)).shr(3); - rewards.push((*uncle_author, RewardKind::uncle(number, u.number()), result_uncle_reward)); - } - - block_reward::apply_block_rewards(&rewards, block, &self.machine) - } - - fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 2 } - - fn verify_local_seal(&self, _header: &Header) -> Result<(), M::Error> { - Ok(()) - } - - fn snapshot_components(&self) -> Option> { - Some(Box::new(::snapshot::PowSnapshot::new(10000, 10000))) - } - - fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice { - super::total_difficulty_fork_choice(new, current) - } + fn name(&self) -> &str { + "NullEngine" + } + + fn machine(&self) -> &M { + &self.machine + } + + fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), M::Error> { + use std::ops::Shr; + + let author = *block.header.author(); + let number = block.header.number(); + + let reward = self.params.block_reward; + if reward == U256::zero() { + return Ok(()); + } + + let n_uncles = block.uncles.len(); + + let mut rewards = Vec::new(); + + // Bestow block reward + let result_block_reward = reward + reward.shr(5) * U256::from(n_uncles); + rewards.push((author, RewardKind::Author, result_block_reward)); + + // bestow uncle rewards. + for u in &block.uncles { + let uncle_author = u.author(); + let result_uncle_reward = (reward * U256::from(8 + u.number() - number)).shr(3); + rewards.push(( + *uncle_author, + RewardKind::uncle(number, u.number()), + result_uncle_reward, + )); + } + + block_reward::apply_block_rewards(&rewards, block, &self.machine) + } + + fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { + 2 + } + + fn verify_local_seal(&self, _header: &Header) -> Result<(), M::Error> { + Ok(()) + } + + fn snapshot_components(&self) -> Option> { + Some(Box::new(::snapshot::PowSnapshot::new(10000, 10000))) + } + + fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice { + super::total_difficulty_fork_choice(new, current) + } + + fn ancestry_actions( + &self, + _header: &Header, + ancestry: &mut dyn Iterator, + ) -> Vec { + if self.params.immediate_finalization { + // always mark parent finalized + ancestry + .take(1) + .map(|e| AncestryAction::MarkFinalized(e.header.hash())) + .collect() + } else { + Vec::new() + } + } } diff --git a/ethcore/src/engines/signer.rs b/ethcore/src/engines/signer.rs index bccaca19153..d4a31802df2 100644 --- a/ethcore/src/engines/signer.rs +++ b/ethcore/src/engines/signer.rs @@ -1,83 +1,79 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! A signer used by Engines which need to sign messages. -use ethereum_types::{H256, Address}; +use ethereum_types::{Address, H256}; use ethkey::{self, Signature}; /// Everything that an Engine needs to sign messages. pub trait EngineSigner: Send + Sync { - /// Sign a consensus message hash. - fn sign(&self, hash: H256) -> Result; + /// Sign a consensus message hash. + fn sign(&self, hash: H256) -> Result; - /// Signing address - fn address(&self) -> Address; + /// Signing address + fn address(&self) -> Address; } /// Creates a new `EngineSigner` from given key pair. -pub fn from_keypair(keypair: ethkey::KeyPair) -> Box { - Box::new(Signer(keypair)) +pub fn from_keypair(keypair: ethkey::KeyPair) -> Box { + Box::new(Signer(keypair)) } struct Signer(ethkey::KeyPair); impl EngineSigner for Signer { - fn sign(&self, hash: H256) -> Result { - ethkey::sign(self.0.secret(), &hash) - } + fn sign(&self, hash: H256) -> Result { + ethkey::sign(self.0.secret(), &hash) + } - fn address(&self) -> Address { - self.0.address() - } + fn address(&self) -> Address { + self.0.address() + } } #[cfg(test)] mod test_signer { - use std::sync::Arc; - - use ethkey::Password; - use accounts::{self, AccountProvider, SignError}; - - use super::*; - - impl EngineSigner for (Arc, Address, Password) { - fn sign(&self, hash: H256) -> Result { - match self.0.sign(self.1, Some(self.2.clone()), hash) { - Err(SignError::NotUnlocked) => unreachable!(), - Err(SignError::NotFound) => Err(ethkey::Error::InvalidAddress), - Err(SignError::Hardware(err)) => { - warn!("Error using hardware wallet for engine: {:?}", err); - Err(ethkey::Error::InvalidSecret) - }, - Err(SignError::SStore(accounts::Error::EthKey(err))) => Err(err), - Err(SignError::SStore(accounts::Error::EthKeyCrypto(err))) => { - warn!("Low level crypto error: {:?}", err); - Err(ethkey::Error::InvalidSecret) - }, - Err(SignError::SStore(err)) => { - warn!("Error signing for engine: {:?}", err); - Err(ethkey::Error::InvalidSignature) - }, - Ok(ok) => Ok(ok), - } - } - - fn address(&self) -> Address { - self.1 - } - } + use std::sync::Arc; + + use accounts::{self, AccountProvider, SignError}; + use ethkey::Password; + + use super::*; + + impl EngineSigner for (Arc, Address, Password) { + fn sign(&self, hash: H256) -> Result { + match self.0.sign(self.1, Some(self.2.clone()), hash) { + Err(SignError::NotUnlocked) => unreachable!(), + Err(SignError::NotFound) => Err(ethkey::Error::InvalidAddress), + Err(SignError::SStore(accounts::Error::EthKey(err))) => Err(err), + Err(SignError::SStore(accounts::Error::EthKeyCrypto(err))) => { + warn!("Low level crypto error: {:?}", err); + Err(ethkey::Error::InvalidSecret) + } + Err(SignError::SStore(err)) => { + warn!("Error signing for engine: {:?}", err); + Err(ethkey::Error::InvalidSignature) + } + Ok(ok) => Ok(ok), + } + } + + fn address(&self) -> Address { + self.1 + } + } } diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/src/engines/validator_set/contract.rs index f0064a8c206..b742120f2ad 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/src/engines/validator_set/contract.rs @@ -1,218 +1,266 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . /// Validator set maintained in a contract, updated using `getValidators` method. /// It can also report validators for misbehaviour with two levels: `reportMalicious` and `reportBenign`. - use std::sync::Weak; use bytes::Bytes; -use ethereum_types::{H256, Address}; +use ethereum_types::{Address, H256}; use machine::{AuxiliaryData, Call, EthereumMachine}; use parking_lot::RwLock; -use types::BlockNumber; -use types::header::Header; +use types::{header::Header, BlockNumber}; use client::EngineClient; -use super::{ValidatorSet, SimpleList, SystemCall}; -use super::safe_contract::ValidatorSafeContract; +use super::{safe_contract::ValidatorSafeContract, SimpleList, SystemCall, ValidatorSet}; use_contract!(validator_report, "res/contracts/validator_report.json"); /// A validator contract with reporting. pub struct ValidatorContract { - contract_address: Address, - validators: ValidatorSafeContract, - client: RwLock>>, // TODO [keorn]: remove + contract_address: Address, + validators: ValidatorSafeContract, + client: RwLock>>, // TODO [keorn]: remove } impl ValidatorContract { - pub fn new(contract_address: Address) -> Self { - ValidatorContract { - contract_address, - validators: ValidatorSafeContract::new(contract_address), - client: RwLock::new(None), - } - } + pub fn new(contract_address: Address) -> Self { + ValidatorContract { + contract_address, + validators: ValidatorSafeContract::new(contract_address), + client: RwLock::new(None), + } + } } impl ValidatorContract { - fn transact(&self, data: Bytes) -> Result<(), String> { - let client = self.client.read().as_ref() - .and_then(Weak::upgrade) - .ok_or_else(|| "No client!")?; - - match client.as_full_client() { - Some(c) => { - c.transact_contract(self.contract_address, data) - .map_err(|e| format!("Transaction import error: {}", e))?; - Ok(()) - }, - None => Err("No full client!".into()), - } - } + fn transact(&self, data: Bytes) -> Result<(), String> { + let client = self + .client + .read() + .as_ref() + .and_then(Weak::upgrade) + .ok_or_else(|| "No client!")?; + + match client.as_full_client() { + Some(c) => { + c.transact_contract(self.contract_address, data) + .map_err(|e| format!("Transaction import error: {}", e))?; + Ok(()) + } + None => Err("No full client!".into()), + } + } } impl ValidatorSet for ValidatorContract { - fn default_caller(&self, id: ::types::ids::BlockId) -> Box { - self.validators.default_caller(id) - } - - fn on_epoch_begin(&self, first: bool, header: &Header, call: &mut SystemCall) -> Result<(), ::error::Error> { - self.validators.on_epoch_begin(first, header, call) - } - - fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result, String> { - self.validators.genesis_epoch_data(header, call) - } - - fn is_epoch_end(&self, first: bool, chain_head: &Header) -> Option> { - self.validators.is_epoch_end(first, chain_head) - } - - fn signals_epoch_end( - &self, - first: bool, - header: &Header, - aux: AuxiliaryData, - ) -> ::engines::EpochChange { - self.validators.signals_epoch_end(first, header, aux) - } - - fn epoch_set(&self, first: bool, machine: &EthereumMachine, number: BlockNumber, proof: &[u8]) -> Result<(SimpleList, Option), ::error::Error> { - self.validators.epoch_set(first, machine, number, proof) - } - - fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool { - self.validators.contains_with_caller(bh, address, caller) - } - - fn get_with_caller(&self, bh: &H256, nonce: usize, caller: &Call) -> Address { - self.validators.get_with_caller(bh, nonce, caller) - } - - fn count_with_caller(&self, bh: &H256, caller: &Call) -> usize { - self.validators.count_with_caller(bh, caller) - } - - fn report_malicious(&self, address: &Address, _set_block: BlockNumber, block: BlockNumber, proof: Bytes) { - let data = validator_report::functions::report_malicious::encode_input(*address, block, proof); - match self.transact(data) { - Ok(_) => warn!(target: "engine", "Reported malicious validator {}", address), - Err(s) => warn!(target: "engine", "Validator {} could not be reported {}", address, s), - } - } - - fn report_benign(&self, address: &Address, _set_block: BlockNumber, block: BlockNumber) { - let data = validator_report::functions::report_benign::encode_input(*address, block); - match self.transact(data) { - Ok(_) => warn!(target: "engine", "Reported benign validator misbehaviour {}", address), - Err(s) => warn!(target: "engine", "Validator {} could not be reported {}", address, s), - } - } - - fn register_client(&self, client: Weak) { - self.validators.register_client(client.clone()); - *self.client.write() = Some(client); - } + fn default_caller(&self, id: ::types::ids::BlockId) -> Box { + self.validators.default_caller(id) + } + + fn on_epoch_begin( + &self, + first: bool, + header: &Header, + call: &mut SystemCall, + ) -> Result<(), ::error::Error> { + self.validators.on_epoch_begin(first, header, call) + } + + fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result, String> { + self.validators.genesis_epoch_data(header, call) + } + + fn is_epoch_end(&self, first: bool, chain_head: &Header) -> Option> { + self.validators.is_epoch_end(first, chain_head) + } + + fn signals_epoch_end( + &self, + first: bool, + header: &Header, + aux: AuxiliaryData, + ) -> ::engines::EpochChange { + self.validators.signals_epoch_end(first, header, aux) + } + + fn epoch_set( + &self, + first: bool, + machine: &EthereumMachine, + number: BlockNumber, + proof: &[u8], + ) -> Result<(SimpleList, Option), ::error::Error> { + self.validators.epoch_set(first, machine, number, proof) + } + + fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool { + self.validators.contains_with_caller(bh, address, caller) + } + + fn get_with_caller(&self, bh: &H256, nonce: usize, caller: &Call) -> Address { + self.validators.get_with_caller(bh, nonce, caller) + } + + fn count_with_caller(&self, bh: &H256, caller: &Call) -> usize { + self.validators.count_with_caller(bh, caller) + } + + fn report_malicious( + &self, + address: &Address, + _set_block: BlockNumber, + block: BlockNumber, + proof: Bytes, + ) { + let data = + validator_report::functions::report_malicious::encode_input(*address, block, proof); + match self.transact(data) { + Ok(_) => warn!(target: "engine", "Reported malicious validator {}", address), + Err(s) => warn!(target: "engine", "Validator {} could not be reported {}", address, s), + } + } + + fn report_benign(&self, address: &Address, _set_block: BlockNumber, block: BlockNumber) { + let data = validator_report::functions::report_benign::encode_input(*address, block); + match self.transact(data) { + Ok(_) => warn!(target: "engine", "Reported benign validator misbehaviour {}", address), + Err(s) => warn!(target: "engine", "Validator {} could not be reported {}", address, s), + } + } + + fn register_client(&self, client: Weak) { + self.validators.register_client(client.clone()); + *self.client.write() = Some(client); + } } #[cfg(test)] mod tests { - use std::sync::Arc; - use rustc_hex::FromHex; - use hash::keccak; - use ethereum_types::{H520, Address}; - use bytes::ToPretty; - use rlp::encode; - use spec::Spec; - use types::header::Header; - use accounts::AccountProvider; - use miner::{self, MinerService}; - use types::ids::BlockId; - use test_helpers::generate_dummy_client_with_spec; - use call_contract::CallContract; - use client::{BlockChainClient, ChainInfo, BlockInfo}; - use super::super::ValidatorSet; - use super::ValidatorContract; - - #[test] - fn fetches_validators() { - let client = generate_dummy_client_with_spec(Spec::new_validator_contract); - let vc = Arc::new(ValidatorContract::new("0000000000000000000000000000000000000005".parse::
().unwrap())); - vc.register_client(Arc::downgrade(&client) as _); - let last_hash = client.best_block_header().hash(); - assert!(vc.contains(&last_hash, &"7d577a597b2742b498cb5cf0c26cdcd726d39e6e".parse::
().unwrap())); - assert!(vc.contains(&last_hash, &"82a978b3f5962a5b0957d9ee9eef472ee55b42f1".parse::
().unwrap())); - } - - #[test] - fn reports_validators() { - let tap = Arc::new(AccountProvider::transient_provider()); - let v1 = tap.insert_account(keccak("1").into(), &"".into()).unwrap(); - let client = generate_dummy_client_with_spec(Spec::new_validator_contract); - client.engine().register_client(Arc::downgrade(&client) as _); - let validator_contract = "0000000000000000000000000000000000000005".parse::
().unwrap(); - - // Make sure reporting can be done. - client.miner().set_gas_range_target((1_000_000.into(), 1_000_000.into())); - let signer = Box::new((tap.clone(), v1, "".into())); - client.miner().set_author(miner::Author::Sealer(signer)); - - // Check a block that is a bit in future, reject it but don't report the validator. - let mut header = Header::default(); - let seal = vec![encode(&4u8), encode(&(&H520::default() as &[u8]))]; - header.set_seal(seal); - header.set_author(v1); - header.set_number(2); - header.set_parent_hash(client.chain_info().best_block_hash); - assert!(client.engine().verify_block_external(&header).is_err()); - client.engine().step(); - assert_eq!(client.chain_info().best_block_number, 0); - - // Now create one that is more in future. That one should be rejected and validator should be reported. - let mut header = Header::default(); - let seal = vec![encode(&8u8), encode(&(&H520::default() as &[u8]))]; - header.set_seal(seal); - header.set_author(v1); - header.set_number(2); - header.set_parent_hash(client.chain_info().best_block_hash); - // `reportBenign` when the designated proposer releases block from the future (bad clock). - assert!(client.engine().verify_block_basic(&header).is_err()); - // Seal a block. - client.engine().step(); - assert_eq!(client.chain_info().best_block_number, 1); - // Check if the unresponsive validator is `disliked`. - assert_eq!( - client.call_contract(BlockId::Latest, validator_contract, "d8f2e0bf".from_hex().unwrap()).unwrap().to_hex(), - "0000000000000000000000007d577a597b2742b498cb5cf0c26cdcd726d39e6e" - ); - // Simulate a misbehaving validator by handling a double proposal. - let header = client.best_block_header(); - assert!(client.engine().verify_block_family(&header, &header).is_err()); - // Seal a block. - client.engine().step(); - client.engine().step(); - assert_eq!(client.chain_info().best_block_number, 2); - - // Check if misbehaving validator was removed. - client.transact_contract(Default::default(), Default::default()).unwrap(); - client.engine().step(); - client.engine().step(); - assert_eq!(client.chain_info().best_block_number, 2); - } + use super::{super::ValidatorSet, ValidatorContract}; + use accounts::AccountProvider; + use bytes::ToPretty; + use call_contract::CallContract; + use client::{BlockChainClient, BlockInfo, ChainInfo}; + use ethereum_types::{Address, H520}; + use hash::keccak; + use miner::{self, MinerService}; + use rlp::encode; + use rustc_hex::FromHex; + use spec::Spec; + use std::sync::Arc; + use test_helpers::generate_dummy_client_with_spec; + use types::{header::Header, ids::BlockId}; + + #[test] + fn fetches_validators() { + let client = generate_dummy_client_with_spec(Spec::new_validator_contract); + let vc = Arc::new(ValidatorContract::new( + "0000000000000000000000000000000000000005" + .parse::
() + .unwrap(), + )); + vc.register_client(Arc::downgrade(&client) as _); + let last_hash = client.best_block_header().hash(); + assert!(vc.contains( + &last_hash, + &"7d577a597b2742b498cb5cf0c26cdcd726d39e6e" + .parse::
() + .unwrap() + )); + assert!(vc.contains( + &last_hash, + &"82a978b3f5962a5b0957d9ee9eef472ee55b42f1" + .parse::
() + .unwrap() + )); + } + + #[test] + fn reports_validators() { + let tap = Arc::new(AccountProvider::transient_provider()); + let v1 = tap.insert_account(keccak("1").into(), &"".into()).unwrap(); + let client = generate_dummy_client_with_spec(Spec::new_validator_contract); + client + .engine() + .register_client(Arc::downgrade(&client) as _); + let validator_contract = "0000000000000000000000000000000000000005" + .parse::
() + .unwrap(); + + // Make sure reporting can be done. + client + .miner() + .set_gas_range_target((1_000_000.into(), 1_000_000.into())); + let signer = Box::new((tap.clone(), v1, "".into())); + client.miner().set_author(miner::Author::Sealer(signer)); + + // Check a block that is a bit in future, reject it but don't report the validator. + let mut header = Header::default(); + let seal = vec![encode(&4u8), encode(&(&H520::default() as &[u8]))]; + header.set_seal(seal); + header.set_author(v1); + header.set_number(2); + header.set_parent_hash(client.chain_info().best_block_hash); + assert!(client.engine().verify_block_external(&header).is_err()); + client.engine().step(); + assert_eq!(client.chain_info().best_block_number, 0); + + // Now create one that is more in future. That one should be rejected and validator should be reported. + let mut header = Header::default(); + let seal = vec![encode(&8u8), encode(&(&H520::default() as &[u8]))]; + header.set_seal(seal); + header.set_author(v1); + header.set_number(2); + header.set_parent_hash(client.chain_info().best_block_hash); + // `reportBenign` when the designated proposer releases block from the future (bad clock). + assert!(client.engine().verify_block_basic(&header).is_err()); + // Seal a block. + client.engine().step(); + assert_eq!(client.chain_info().best_block_number, 1); + // Check if the unresponsive validator is `disliked`. + assert_eq!( + client + .call_contract( + BlockId::Latest, + validator_contract, + "d8f2e0bf".from_hex().unwrap() + ) + .unwrap() + .to_hex(), + "0000000000000000000000007d577a597b2742b498cb5cf0c26cdcd726d39e6e" + ); + // Simulate a misbehaving validator by handling a double proposal. + let header = client.best_block_header(); + assert!(client + .engine() + .verify_block_family(&header, &header) + .is_err()); + // Seal a block. + client.engine().step(); + client.engine().step(); + assert_eq!(client.chain_info().best_block_number, 2); + + // Check if misbehaving validator was removed. + client + .transact_contract(Default::default(), Default::default()) + .unwrap(); + client.engine().step(); + client.engine().step(); + assert_eq!(client.chain_info().best_block_number, 2); + } } diff --git a/ethcore/src/engines/validator_set/mod.rs b/ethcore/src/engines/validator_set/mod.rs index 915a3f9a15d..e9f2d6b994c 100644 --- a/ethcore/src/engines/validator_set/mod.rs +++ b/ethcore/src/engines/validator_set/mod.rs @@ -1,145 +1,172 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . +mod contract; +mod multi; +mod safe_contract; +mod simple_list; /// Validator lists. #[cfg(test)] mod test; -mod simple_list; -mod safe_contract; -mod contract; -mod multi; use std::sync::Weak; use bytes::Bytes; -use ethereum_types::{H256, Address}; +use ethereum_types::{Address, H256}; use ethjson::spec::ValidatorSet as ValidatorSpec; use machine::{AuxiliaryData, Call, EthereumMachine}; -use types::BlockNumber; -use types::header::Header; -use types::ids::BlockId; +use types::{header::Header, ids::BlockId, BlockNumber}; use client::EngineClient; +pub use self::simple_list::SimpleList; #[cfg(test)] pub use self::test::TestSet; -pub use self::simple_list::SimpleList; -use self::contract::ValidatorContract; -use self::safe_contract::ValidatorSafeContract; -use self::multi::Multi; +use self::{contract::ValidatorContract, multi::Multi, safe_contract::ValidatorSafeContract}; use super::SystemCall; /// Creates a validator set from spec. -pub fn new_validator_set(spec: ValidatorSpec) -> Box { - match spec { - ValidatorSpec::List(list) => Box::new(SimpleList::new(list.into_iter().map(Into::into).collect())), - ValidatorSpec::SafeContract(address) => Box::new(ValidatorSafeContract::new(address.into())), - ValidatorSpec::Contract(address) => Box::new(ValidatorContract::new(address.into())), - ValidatorSpec::Multi(sequence) => Box::new( - Multi::new(sequence.into_iter().map(|(block, set)| (block.into(), new_validator_set(set))).collect()) - ), - } +pub fn new_validator_set(spec: ValidatorSpec) -> Box { + match spec { + ValidatorSpec::List(list) => { + Box::new(SimpleList::new(list.into_iter().map(Into::into).collect())) + } + ValidatorSpec::SafeContract(address) => { + Box::new(ValidatorSafeContract::new(address.into())) + } + ValidatorSpec::Contract(address) => Box::new(ValidatorContract::new(address.into())), + ValidatorSpec::Multi(sequence) => Box::new(Multi::new( + sequence + .into_iter() + .map(|(block, set)| (block.into(), new_validator_set(set))) + .collect(), + )), + } } /// A validator set. pub trait ValidatorSet: Send + Sync + 'static { - /// Get the default "Call" helper, for use in general operation. - // TODO [keorn]: this is a hack intended to migrate off of - // a strict dependency on state always being available. - fn default_caller(&self, block_id: BlockId) -> Box; - - /// Checks if a given address is a validator, - /// using underlying, default call mechanism. - fn contains(&self, parent: &H256, address: &Address) -> bool { - let default = self.default_caller(BlockId::Hash(*parent)); - self.contains_with_caller(parent, address, &*default) - } - - /// Draws an validator nonce modulo number of validators. - fn get(&self, parent: &H256, nonce: usize) -> Address { - let default = self.default_caller(BlockId::Hash(*parent)); - self.get_with_caller(parent, nonce, &*default) - } - - /// Returns the current number of validators. - fn count(&self, parent: &H256) -> usize { - let default = self.default_caller(BlockId::Hash(*parent)); - self.count_with_caller(parent, &*default) - } - - /// Signalling that a new epoch has begun. - /// - /// All calls here will be from the `SYSTEM_ADDRESS`: 2^160 - 2 - /// and will have an effect on the block's state. - /// The caller provided here may not generate proofs. - /// - /// `first` is true if this is the first block in the set. - fn on_epoch_begin(&self, _first: bool, _header: &Header, _call: &mut SystemCall) -> Result<(), ::error::Error> { - Ok(()) - } - - /// Extract genesis epoch data from the genesis state and header. - fn genesis_epoch_data(&self, _header: &Header, _call: &Call) -> Result, String> { Ok(Vec::new()) } - - /// Whether this block is the last one in its epoch. - /// - /// Indicates that the validator set changed at the given block in a manner - /// that doesn't require finality. - /// - /// `first` is true if this is the first block in the set. - fn is_epoch_end(&self, first: bool, chain_head: &Header) -> Option>; - - /// Whether the given block signals the end of an epoch, but change won't take effect - /// until finality. - /// - /// Engine should set `first` only if the header is genesis. Multiplexing validator - /// sets can set `first` to internal changes. - fn signals_epoch_end( - &self, - first: bool, - header: &Header, - aux: AuxiliaryData, - ) -> ::engines::EpochChange; - - /// Recover the validator set from the given proof, the block number, and - /// whether this header is first in its set. - /// - /// May fail if the given header doesn't kick off an epoch or - /// the proof is invalid. - /// - /// Returns the set, along with a flag indicating whether finality of a specific - /// hash should be proven. - fn epoch_set(&self, first: bool, machine: &EthereumMachine, number: BlockNumber, proof: &[u8]) - -> Result<(SimpleList, Option), ::error::Error>; - - /// Checks if a given address is a validator, with the given function - /// for executing synchronous calls to contracts. - fn contains_with_caller(&self, parent_block_hash: &H256, address: &Address, caller: &Call) -> bool; - - /// Draws an validator nonce modulo number of validators. - fn get_with_caller(&self, parent_block_hash: &H256, nonce: usize, caller: &Call) -> Address; - - /// Returns the current number of validators. - fn count_with_caller(&self, parent_block_hash: &H256, caller: &Call) -> usize; - - /// Notifies about malicious behaviour. - fn report_malicious(&self, _validator: &Address, _set_block: BlockNumber, _block: BlockNumber, _proof: Bytes) {} - /// Notifies about benign misbehaviour. - fn report_benign(&self, _validator: &Address, _set_block: BlockNumber, _block: BlockNumber) {} - /// Allows blockchain state access. - fn register_client(&self, _client: Weak) {} + /// Get the default "Call" helper, for use in general operation. + // TODO [keorn]: this is a hack intended to migrate off of + // a strict dependency on state always being available. + fn default_caller(&self, block_id: BlockId) -> Box; + + /// Checks if a given address is a validator, + /// using underlying, default call mechanism. + fn contains(&self, parent: &H256, address: &Address) -> bool { + let default = self.default_caller(BlockId::Hash(*parent)); + self.contains_with_caller(parent, address, &*default) + } + + /// Draws an validator nonce modulo number of validators. + fn get(&self, parent: &H256, nonce: usize) -> Address { + let default = self.default_caller(BlockId::Hash(*parent)); + self.get_with_caller(parent, nonce, &*default) + } + + /// Returns the current number of validators. + fn count(&self, parent: &H256) -> usize { + let default = self.default_caller(BlockId::Hash(*parent)); + self.count_with_caller(parent, &*default) + } + + /// Signalling that a new epoch has begun. + /// + /// All calls here will be from the `SYSTEM_ADDRESS`: 2^160 - 2 + /// and will have an effect on the block's state. + /// The caller provided here may not generate proofs. + /// + /// `first` is true if this is the first block in the set. + fn on_epoch_begin( + &self, + _first: bool, + _header: &Header, + _call: &mut SystemCall, + ) -> Result<(), ::error::Error> { + Ok(()) + } + + /// Extract genesis epoch data from the genesis state and header. + fn genesis_epoch_data(&self, _header: &Header, _call: &Call) -> Result, String> { + Ok(Vec::new()) + } + + /// Whether this block is the last one in its epoch. + /// + /// Indicates that the validator set changed at the given block in a manner + /// that doesn't require finality. + /// + /// `first` is true if this is the first block in the set. + fn is_epoch_end(&self, first: bool, chain_head: &Header) -> Option>; + + /// Whether the given block signals the end of an epoch, but change won't take effect + /// until finality. + /// + /// Engine should set `first` only if the header is genesis. Multiplexing validator + /// sets can set `first` to internal changes. + fn signals_epoch_end( + &self, + first: bool, + header: &Header, + aux: AuxiliaryData, + ) -> ::engines::EpochChange; + + /// Recover the validator set from the given proof, the block number, and + /// whether this header is first in its set. + /// + /// May fail if the given header doesn't kick off an epoch or + /// the proof is invalid. + /// + /// Returns the set, along with a flag indicating whether finality of a specific + /// hash should be proven. + fn epoch_set( + &self, + first: bool, + machine: &EthereumMachine, + number: BlockNumber, + proof: &[u8], + ) -> Result<(SimpleList, Option), ::error::Error>; + + /// Checks if a given address is a validator, with the given function + /// for executing synchronous calls to contracts. + fn contains_with_caller( + &self, + parent_block_hash: &H256, + address: &Address, + caller: &Call, + ) -> bool; + + /// Draws an validator nonce modulo number of validators. + fn get_with_caller(&self, parent_block_hash: &H256, nonce: usize, caller: &Call) -> Address; + + /// Returns the current number of validators. + fn count_with_caller(&self, parent_block_hash: &H256, caller: &Call) -> usize; + + /// Notifies about malicious behaviour. + fn report_malicious( + &self, + _validator: &Address, + _set_block: BlockNumber, + _block: BlockNumber, + _proof: Bytes, + ) { + } + /// Notifies about benign misbehaviour. + fn report_benign(&self, _validator: &Address, _set_block: BlockNumber, _block: BlockNumber) {} + /// Allows blockchain state access. + fn register_client(&self, _client: Weak) {} } diff --git a/ethcore/src/engines/validator_set/multi.rs b/ethcore/src/engines/validator_set/multi.rs index b9ef6774784..b5b29016792 100644 --- a/ethcore/src/engines/validator_set/multi.rs +++ b/ethcore/src/engines/validator_set/multi.rs @@ -1,248 +1,297 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . /// Validator set changing at fork blocks. - use std::collections::BTreeMap; use std::sync::Weak; use bytes::Bytes; -use ethereum_types::{H256, Address}; +use ethereum_types::{Address, H256}; use parking_lot::RwLock; -use types::BlockNumber; -use types::header::Header; -use types::ids::BlockId; +use types::{header::Header, ids::BlockId, BlockNumber}; +use super::{SystemCall, ValidatorSet}; use client::EngineClient; use machine::{AuxiliaryData, Call, EthereumMachine}; -use super::{SystemCall, ValidatorSet}; -type BlockNumberLookup = Box Result + Send + Sync + 'static>; +type BlockNumberLookup = + Box Result + Send + Sync + 'static>; pub struct Multi { - sets: BTreeMap>, - block_number: RwLock, + sets: BTreeMap>, + block_number: RwLock, } impl Multi { - pub fn new(set_map: BTreeMap>) -> Self { - assert!(set_map.get(&0u64).is_some(), "ValidatorSet has to be specified from block 0."); - Multi { - sets: set_map, - block_number: RwLock::new(Box::new(move |_| Err("No client!".into()))), - } - } - - fn correct_set(&self, id: BlockId) -> Option<&ValidatorSet> { - match self.block_number.read()(id).map(|parent_block| self.correct_set_by_number(parent_block)) { - Ok((_, set)) => Some(set), - Err(e) => { - debug!(target: "engine", "ValidatorSet could not be recovered: {}", e); - None - }, - } - } - - // get correct set by block number, along with block number at which - // this set was activated. - fn correct_set_by_number(&self, parent_block: BlockNumber) -> (BlockNumber, &ValidatorSet) { - let (block, set) = self.sets.iter() + pub fn new(set_map: BTreeMap>) -> Self { + assert!( + set_map.get(&0u64).is_some(), + "ValidatorSet has to be specified from block 0." + ); + Multi { + sets: set_map, + block_number: RwLock::new(Box::new(move |_| Err("No client!".into()))), + } + } + + fn correct_set(&self, id: BlockId) -> Option<&dyn ValidatorSet> { + match self.block_number.read()(id) + .map(|parent_block| self.correct_set_by_number(parent_block)) + { + Ok((_, set)) => Some(set), + Err(e) => { + debug!(target: "engine", "ValidatorSet could not be recovered: {}", e); + None + } + } + } + + // get correct set by block number, along with block number at which + // this set was activated. + fn correct_set_by_number(&self, parent_block: BlockNumber) -> (BlockNumber, &dyn ValidatorSet) { + let (block, set) = self.sets.iter() .rev() .find(|&(block, _)| *block <= parent_block + 1) .expect("constructor validation ensures that there is at least one validator set for block 0; block 0 is less than any uint; qed"); - trace!(target: "engine", "Multi ValidatorSet retrieved for block {}.", block); - (*block, &**set) - } + trace!(target: "engine", "Multi ValidatorSet retrieved for block {}.", block); + (*block, &**set) + } } impl ValidatorSet for Multi { - fn default_caller(&self, block_id: BlockId) -> Box { - self.correct_set(block_id).map(|set| set.default_caller(block_id)) - .unwrap_or_else(|| Box::new(|_, _| Err("No validator set for given ID.".into()))) - } - - fn on_epoch_begin(&self, _first: bool, header: &Header, call: &mut SystemCall) -> Result<(), ::error::Error> { - let (set_block, set) = self.correct_set_by_number(header.number()); - let first = set_block == header.number(); - - set.on_epoch_begin(first, header, call) - } - - fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result, String> { - self.correct_set_by_number(0).1.genesis_epoch_data(header, call) - } - - fn is_epoch_end(&self, _first: bool, chain_head: &Header) -> Option> { - let (set_block, set) = self.correct_set_by_number(chain_head.number()); - let first = set_block == chain_head.number(); - - set.is_epoch_end(first, chain_head) - } - - fn signals_epoch_end(&self, _first: bool, header: &Header, aux: AuxiliaryData) - -> ::engines::EpochChange - { - let (set_block, set) = self.correct_set_by_number(header.number()); - let first = set_block == header.number(); - - set.signals_epoch_end(first, header, aux) - } - - fn epoch_set(&self, _first: bool, machine: &EthereumMachine, number: BlockNumber, proof: &[u8]) -> Result<(super::SimpleList, Option), ::error::Error> { - let (set_block, set) = self.correct_set_by_number(number); - let first = set_block == number; - - set.epoch_set(first, machine, number, proof) - } - - fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool { - self.correct_set(BlockId::Hash(*bh)) - .map_or(false, |set| set.contains_with_caller(bh, address, caller)) - } - - fn get_with_caller(&self, bh: &H256, nonce: usize, caller: &Call) -> Address { - self.correct_set(BlockId::Hash(*bh)) - .map_or_else(Default::default, |set| set.get_with_caller(bh, nonce, caller)) - } - - fn count_with_caller(&self, bh: &H256, caller: &Call) -> usize { - self.correct_set(BlockId::Hash(*bh)) - .map_or_else(usize::max_value, |set| set.count_with_caller(bh, caller)) - } - - fn report_malicious(&self, validator: &Address, set_block: BlockNumber, block: BlockNumber, proof: Bytes) { - self.correct_set_by_number(set_block).1.report_malicious(validator, set_block, block, proof); - } - - fn report_benign(&self, validator: &Address, set_block: BlockNumber, block: BlockNumber) { - self.correct_set_by_number(set_block).1.report_benign(validator, set_block, block); - } - - fn register_client(&self, client: Weak) { - for set in self.sets.values() { - set.register_client(client.clone()); - } - *self.block_number.write() = Box::new(move |id| client - .upgrade() - .ok_or_else(|| "No client!".into()) - .and_then(|c| c.block_number(id).ok_or_else(|| "Unknown block".into()))); - } + fn default_caller(&self, block_id: BlockId) -> Box { + self.correct_set(block_id) + .map(|set| set.default_caller(block_id)) + .unwrap_or_else(|| Box::new(|_, _| Err("No validator set for given ID.".into()))) + } + + fn on_epoch_begin( + &self, + _first: bool, + header: &Header, + call: &mut SystemCall, + ) -> Result<(), ::error::Error> { + let (set_block, set) = self.correct_set_by_number(header.number()); + let first = set_block == header.number(); + + set.on_epoch_begin(first, header, call) + } + + fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result, String> { + self.correct_set_by_number(0) + .1 + .genesis_epoch_data(header, call) + } + + fn is_epoch_end(&self, _first: bool, chain_head: &Header) -> Option> { + let (set_block, set) = self.correct_set_by_number(chain_head.number()); + let first = set_block == chain_head.number(); + + set.is_epoch_end(first, chain_head) + } + + fn signals_epoch_end( + &self, + _first: bool, + header: &Header, + aux: AuxiliaryData, + ) -> ::engines::EpochChange { + let (set_block, set) = self.correct_set_by_number(header.number()); + let first = set_block == header.number(); + + set.signals_epoch_end(first, header, aux) + } + + fn epoch_set( + &self, + _first: bool, + machine: &EthereumMachine, + number: BlockNumber, + proof: &[u8], + ) -> Result<(super::SimpleList, Option), ::error::Error> { + let (set_block, set) = self.correct_set_by_number(number); + let first = set_block == number; + + set.epoch_set(first, machine, number, proof) + } + + fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool { + self.correct_set(BlockId::Hash(*bh)) + .map_or(false, |set| set.contains_with_caller(bh, address, caller)) + } + + fn get_with_caller(&self, bh: &H256, nonce: usize, caller: &Call) -> Address { + self.correct_set(BlockId::Hash(*bh)) + .map_or_else(Default::default, |set| { + set.get_with_caller(bh, nonce, caller) + }) + } + + fn count_with_caller(&self, bh: &H256, caller: &Call) -> usize { + self.correct_set(BlockId::Hash(*bh)) + .map_or_else(usize::max_value, |set| set.count_with_caller(bh, caller)) + } + + fn report_malicious( + &self, + validator: &Address, + set_block: BlockNumber, + block: BlockNumber, + proof: Bytes, + ) { + self.correct_set_by_number(set_block) + .1 + .report_malicious(validator, set_block, block, proof); + } + + fn report_benign(&self, validator: &Address, set_block: BlockNumber, block: BlockNumber) { + self.correct_set_by_number(set_block) + .1 + .report_benign(validator, set_block, block); + } + + fn register_client(&self, client: Weak) { + for set in self.sets.values() { + set.register_client(client.clone()); + } + *self.block_number.write() = Box::new(move |id| { + client + .upgrade() + .ok_or_else(|| "No client!".into()) + .and_then(|c| c.block_number(id).ok_or_else(|| "Unknown block".into())) + }); + } } #[cfg(test)] mod tests { - use std::sync::Arc; - use std::collections::BTreeMap; - use hash::keccak; - use accounts::AccountProvider; - use client::{BlockChainClient, ChainInfo, BlockInfo, ImportBlock}; - use engines::EpochChange; - use engines::validator_set::ValidatorSet; - use ethkey::Secret; - use types::header::Header; - use miner::{self, MinerService}; - use spec::Spec; - use test_helpers::{generate_dummy_client_with_spec, generate_dummy_client_with_spec_and_data}; - use types::ids::BlockId; - use ethereum_types::Address; - use verification::queue::kind::blocks::Unverified; - - use super::Multi; - - #[test] - fn uses_current_set() { - let tap = Arc::new(AccountProvider::transient_provider()); - let s0: Secret = keccak("0").into(); - let v0 = tap.insert_account(s0.clone(), &"".into()).unwrap(); - let v1 = tap.insert_account(keccak("1").into(), &"".into()).unwrap(); - let client = generate_dummy_client_with_spec(Spec::new_validator_multi); - client.engine().register_client(Arc::downgrade(&client) as _); - - // Make sure txs go through. - client.miner().set_gas_range_target((1_000_000.into(), 1_000_000.into())); - - // Wrong signer for the first block. - let signer = Box::new((tap.clone(), v1, "".into())); - client.miner().set_author(miner::Author::Sealer(signer)); - client.transact_contract(Default::default(), Default::default()).unwrap(); - ::client::EngineClient::update_sealing(&*client); - assert_eq!(client.chain_info().best_block_number, 0); - // Right signer for the first block. - let signer = Box::new((tap.clone(), v0, "".into())); - client.miner().set_author(miner::Author::Sealer(signer)); - ::client::EngineClient::update_sealing(&*client); - assert_eq!(client.chain_info().best_block_number, 1); - // This time v0 is wrong. - client.transact_contract(Default::default(), Default::default()).unwrap(); - ::client::EngineClient::update_sealing(&*client); - assert_eq!(client.chain_info().best_block_number, 1); - let signer = Box::new((tap.clone(), v1, "".into())); - client.miner().set_author(miner::Author::Sealer(signer)); - ::client::EngineClient::update_sealing(&*client); - assert_eq!(client.chain_info().best_block_number, 2); - // v1 is still good. - client.transact_contract(Default::default(), Default::default()).unwrap(); - ::client::EngineClient::update_sealing(&*client); - assert_eq!(client.chain_info().best_block_number, 3); - - // Check syncing. - let sync_client = generate_dummy_client_with_spec_and_data(Spec::new_validator_multi, 0, 0, &[]); - sync_client.engine().register_client(Arc::downgrade(&sync_client) as _); - for i in 1..4 { - sync_client.import_block(Unverified::from_rlp(client.block(BlockId::Number(i)).unwrap().into_inner()).unwrap()).unwrap(); - } - sync_client.flush_queue(); - assert_eq!(sync_client.chain_info().best_block_number, 3); - } - - #[test] - fn transition_to_fixed_list_instant() { - use super::super::SimpleList; - - let mut map: BTreeMap<_, Box> = BTreeMap::new(); - let list1: Vec<_> = (0..10).map(|_| Address::random()).collect(); - let list2 = { - let mut list = list1.clone(); - list.push(Address::random()); - list - }; - - map.insert(0, Box::new(SimpleList::new(list1))); - map.insert(500, Box::new(SimpleList::new(list2))); - - let multi = Multi::new(map); - - let mut header = Header::new(); - header.set_number(499); - - match multi.signals_epoch_end(false, &header, Default::default()) { - EpochChange::No => {}, - _ => panic!("Expected no epoch signal change."), - } - assert!(multi.is_epoch_end(false, &header).is_none()); - - header.set_number(500); - - match multi.signals_epoch_end(false, &header, Default::default()) { - EpochChange::No => {}, - _ => panic!("Expected no epoch signal change."), - } - assert!(multi.is_epoch_end(false, &header).is_some()); - } + use accounts::AccountProvider; + use client::{traits::ForceUpdateSealing, BlockChainClient, BlockInfo, ChainInfo, ImportBlock}; + use engines::{validator_set::ValidatorSet, EpochChange}; + use ethereum_types::Address; + use ethkey::Secret; + use hash::keccak; + use miner::{self, MinerService}; + use spec::Spec; + use std::{collections::BTreeMap, sync::Arc}; + use test_helpers::{generate_dummy_client_with_spec, generate_dummy_client_with_spec_and_data}; + use types::{header::Header, ids::BlockId}; + use verification::queue::kind::blocks::Unverified; + + use super::Multi; + + #[test] + fn uses_current_set() { + let tap = Arc::new(AccountProvider::transient_provider()); + let s0: Secret = keccak("0").into(); + let v0 = tap.insert_account(s0.clone(), &"".into()).unwrap(); + let v1 = tap.insert_account(keccak("1").into(), &"".into()).unwrap(); + let client = generate_dummy_client_with_spec(Spec::new_validator_multi); + client + .engine() + .register_client(Arc::downgrade(&client) as _); + + // Make sure txs go through. + client + .miner() + .set_gas_range_target((1_000_000.into(), 1_000_000.into())); + + // Wrong signer for the first block. + let signer = Box::new((tap.clone(), v1, "".into())); + client.miner().set_author(miner::Author::Sealer(signer)); + client + .transact_contract(Default::default(), Default::default()) + .unwrap(); + ::client::EngineClient::update_sealing(&*client, ForceUpdateSealing::No); + assert_eq!(client.chain_info().best_block_number, 0); + // Right signer for the first block. + let signer = Box::new((tap.clone(), v0, "".into())); + client.miner().set_author(miner::Author::Sealer(signer)); + ::client::EngineClient::update_sealing(&*client, ForceUpdateSealing::No); + assert_eq!(client.chain_info().best_block_number, 1); + // This time v0 is wrong. + client + .transact_contract(Default::default(), Default::default()) + .unwrap(); + ::client::EngineClient::update_sealing(&*client, ForceUpdateSealing::No); + assert_eq!(client.chain_info().best_block_number, 1); + let signer = Box::new((tap.clone(), v1, "".into())); + client.miner().set_author(miner::Author::Sealer(signer)); + ::client::EngineClient::update_sealing(&*client, ForceUpdateSealing::No); + assert_eq!(client.chain_info().best_block_number, 2); + // v1 is still good. + client + .transact_contract(Default::default(), Default::default()) + .unwrap(); + ::client::EngineClient::update_sealing(&*client, ForceUpdateSealing::No); + assert_eq!(client.chain_info().best_block_number, 3); + + // Check syncing. + let sync_client = + generate_dummy_client_with_spec_and_data(Spec::new_validator_multi, 0, 0, &[]); + sync_client + .engine() + .register_client(Arc::downgrade(&sync_client) as _); + for i in 1..4 { + sync_client + .import_block( + Unverified::from_rlp(client.block(BlockId::Number(i)).unwrap().into_inner()) + .unwrap(), + ) + .unwrap(); + } + sync_client.flush_queue(); + assert_eq!(sync_client.chain_info().best_block_number, 3); + } + + #[test] + fn transition_to_fixed_list_instant() { + use super::super::SimpleList; + + let mut map: BTreeMap<_, Box> = BTreeMap::new(); + let list1: Vec<_> = (0..10).map(|_| Address::random()).collect(); + let list2 = { + let mut list = list1.clone(); + list.push(Address::random()); + list + }; + + map.insert(0, Box::new(SimpleList::new(list1))); + map.insert(500, Box::new(SimpleList::new(list2))); + + let multi = Multi::new(map); + + let mut header = Header::new(); + header.set_number(499); + + match multi.signals_epoch_end(false, &header, Default::default()) { + EpochChange::No => {} + _ => panic!("Expected no epoch signal change."), + } + assert!(multi.is_epoch_end(false, &header).is_none()); + + header.set_number(500); + + match multi.signals_epoch_end(false, &header, Default::default()) { + EpochChange::No => {} + _ => panic!("Expected no epoch signal change."), + } + assert!(multi.is_epoch_end(false, &header).is_some()); + } } diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index 49d539df3f3..569510a8d8c 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -1,41 +1,36 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . /// Validator set maintained in a contract, updated using `getValidators` method. - -use std::sync::{Weak, Arc}; +use std::sync::{Arc, Weak}; use bytes::Bytes; use ethabi::FunctionOutputDecoder; -use ethereum_types::{H256, U256, Address, Bloom}; +use ethereum_types::{Address, Bloom, H256, U256}; use hash::keccak; use kvdb::DBValue; use memory_cache::MemoryLruCache; use parking_lot::RwLock; use rlp::{Rlp, RlpStream}; -use types::header::Header; -use types::ids::BlockId; -use types::log_entry::LogEntry; -use types::receipt::Receipt; +use types::{header::Header, ids::BlockId, log_entry::LogEntry, receipt::Receipt}; use unexpected::Mismatch; +use super::{simple_list::SimpleList, SystemCall, ValidatorSet}; use client::EngineClient; -use machine::{AuxiliaryData, Call, EthereumMachine, AuxiliaryRequest}; -use super::{SystemCall, ValidatorSet}; -use super::simple_list::SimpleList; +use machine::{AuxiliaryData, AuxiliaryRequest, Call, EthereumMachine}; use_contract!(validator_set, "res/contracts/validator_set.json"); @@ -45,549 +40,637 @@ const MEMOIZE_CAPACITY: usize = 500; const EVENT_NAME: &'static [u8] = &*b"InitiateChange(bytes32,address[])"; lazy_static! { - static ref EVENT_NAME_HASH: H256 = keccak(EVENT_NAME); + static ref EVENT_NAME_HASH: H256 = keccak(EVENT_NAME); } // state-dependent proofs for the safe contract: // only "first" proofs are such. struct StateProof { - contract_address: Address, - header: Header, + contract_address: Address, + header: Header, } impl ::engines::StateDependentProof for StateProof { - fn generate_proof(&self, caller: &Call) -> Result, String> { - prove_initial(self.contract_address, &self.header, caller) - } - - fn check_proof(&self, machine: &EthereumMachine, proof: &[u8]) -> Result<(), String> { - let (header, state_items) = decode_first_proof(&Rlp::new(proof)) - .map_err(|e| format!("proof incorrectly encoded: {}", e))?; - if &header != &self.header { - return Err("wrong header in proof".into()); - } - - check_first_proof(machine, self.contract_address, header, &state_items).map(|_| ()) - } + fn generate_proof(&self, caller: &Call) -> Result, String> { + prove_initial(self.contract_address, &self.header, caller) + } + + fn check_proof(&self, machine: &EthereumMachine, proof: &[u8]) -> Result<(), String> { + let (header, state_items) = decode_first_proof(&Rlp::new(proof)) + .map_err(|e| format!("proof incorrectly encoded: {}", e))?; + if &header != &self.header { + return Err("wrong header in proof".into()); + } + + check_first_proof(machine, self.contract_address, header, &state_items).map(|_| ()) + } } /// The validator contract should have the following interface: pub struct ValidatorSafeContract { - contract_address: Address, - validators: RwLock>, - client: RwLock>>, // TODO [keorn]: remove + contract_address: Address, + validators: RwLock>, + client: RwLock>>, // TODO [keorn]: remove } // first proof is just a state proof call of `getValidators` at header's state. fn encode_first_proof(header: &Header, state_items: &[Vec]) -> Bytes { - let mut stream = RlpStream::new_list(2); - stream.append(header).begin_list(state_items.len()); - for item in state_items { - stream.append(item); - } + let mut stream = RlpStream::new_list(2); + stream.append(header).begin_list(state_items.len()); + for item in state_items { + stream.append(item); + } - stream.out() + stream.out() } // check a first proof: fetch the validator set at the given block. -fn check_first_proof(machine: &EthereumMachine, contract_address: Address, old_header: Header, state_items: &[DBValue]) - -> Result, String> -{ - use types::transaction::{Action, Transaction}; - - // TODO: match client contract_call_tx more cleanly without duplication. - const PROVIDED_GAS: u64 = 50_000_000; - - let env_info = ::vm::EnvInfo { - number: old_header.number(), - author: *old_header.author(), - difficulty: *old_header.difficulty(), - gas_limit: PROVIDED_GAS.into(), - timestamp: old_header.timestamp(), - last_hashes: { - // this will break if we don't inclue all 256 last hashes. - let mut last_hashes: Vec<_> = (0..256).map(|_| H256::default()).collect(); - last_hashes[255] = *old_header.parent_hash(); - Arc::new(last_hashes) - }, - gas_used: 0.into(), - }; - - // check state proof using given machine. - let number = old_header.number(); - let (data, decoder) = validator_set::functions::get_validators::call(); - - let from = Address::default(); - let tx = Transaction { - nonce: machine.account_start_nonce(number), - action: Action::Call(contract_address), - gas: PROVIDED_GAS.into(), - gas_price: U256::default(), - value: U256::default(), - data, - }.fake_sign(from); - - let res = ::state::check_proof( - state_items, - *old_header.state_root(), - &tx, - machine, - &env_info, - ); - - match res { - ::state::ProvedExecution::BadProof => Err("Bad proof".into()), - ::state::ProvedExecution::Failed(e) => Err(format!("Failed call: {}", e)), - ::state::ProvedExecution::Complete(e) => decoder.decode(&e.output).map_err(|e| e.to_string()), - } +fn check_first_proof( + machine: &EthereumMachine, + contract_address: Address, + old_header: Header, + state_items: &[DBValue], +) -> Result, String> { + use types::transaction::{Action, Transaction}; + + // TODO: match client contract_call_tx more cleanly without duplication. + const PROVIDED_GAS: u64 = 50_000_000; + + let env_info = ::vm::EnvInfo { + number: old_header.number(), + author: *old_header.author(), + difficulty: *old_header.difficulty(), + gas_limit: PROVIDED_GAS.into(), + timestamp: old_header.timestamp(), + last_hashes: { + // this will break if we don't inclue all 256 last hashes. + let mut last_hashes: Vec<_> = (0..256).map(|_| H256::default()).collect(); + last_hashes[255] = *old_header.parent_hash(); + Arc::new(last_hashes) + }, + gas_used: 0.into(), + }; + + // check state proof using given machine. + let number = old_header.number(); + let (data, decoder) = validator_set::functions::get_validators::call(); + + let from = Address::default(); + let tx = Transaction { + nonce: machine.account_start_nonce(number), + action: Action::Call(contract_address), + gas: PROVIDED_GAS.into(), + gas_price: U256::default(), + value: U256::default(), + data, + } + .fake_sign(from); + + let res = ::state::check_proof( + state_items, + *old_header.state_root(), + &tx, + machine, + &env_info, + ); + + match res { + ::state::ProvedExecution::BadProof => Err("Bad proof".into()), + ::state::ProvedExecution::Failed(e) => Err(format!("Failed call: {}", e)), + ::state::ProvedExecution::Complete(e) => { + decoder.decode(&e.output).map_err(|e| e.to_string()) + } + } } fn decode_first_proof(rlp: &Rlp) -> Result<(Header, Vec), ::error::Error> { - let header = rlp.val_at(0)?; - let state_items = rlp.at(1)?.iter().map(|x| { - let mut val = DBValue::new(); - val.append_slice(x.data()?); - Ok(val) - }).collect::>()?; - - Ok((header, state_items)) + let header = rlp.val_at(0)?; + let state_items = rlp + .at(1)? + .iter() + .map(|x| { + let mut val = DBValue::new(); + val.append_slice(x.data()?); + Ok(val) + }) + .collect::>()?; + + Ok((header, state_items)) } // inter-contract proofs are a header and receipts. // checking will involve ensuring that the receipts match the header and // extracting the validator set from the receipts. fn encode_proof(header: &Header, receipts: &[Receipt]) -> Bytes { - let mut stream = RlpStream::new_list(2); - stream.append(header).append_list(receipts); - stream.drain() + let mut stream = RlpStream::new_list(2); + stream.append(header).append_list(receipts); + stream.drain() } fn decode_proof(rlp: &Rlp) -> Result<(Header, Vec), ::error::Error> { - Ok((rlp.val_at(0)?, rlp.list_at(1)?)) + Ok((rlp.val_at(0)?, rlp.list_at(1)?)) } // given a provider and caller, generate proof. this will just be a state proof // of `getValidators`. -fn prove_initial(contract_address: Address, header: &Header, caller: &Call) -> Result, String> { - use std::cell::RefCell; - - let epoch_proof = RefCell::new(None); - let validators = { - let (data, decoder) = validator_set::functions::get_validators::call(); - let (value, proof) = caller(contract_address, data)?; - *epoch_proof.borrow_mut() = Some(encode_first_proof(header, &proof)); - decoder.decode(&value).map_err(|e| e.to_string())? - }; - - let proof = epoch_proof.into_inner().expect("epoch_proof always set after call; qed"); - - trace!(target: "engine", "obtained proof for initial set: {} validators, {} bytes", +fn prove_initial( + contract_address: Address, + header: &Header, + caller: &Call, +) -> Result, String> { + use std::cell::RefCell; + + let epoch_proof = RefCell::new(None); + let validators = { + let (data, decoder) = validator_set::functions::get_validators::call(); + let (value, proof) = caller(contract_address, data)?; + *epoch_proof.borrow_mut() = Some(encode_first_proof(header, &proof)); + decoder.decode(&value).map_err(|e| e.to_string())? + }; + + let proof = epoch_proof + .into_inner() + .expect("epoch_proof always set after call; qed"); + + trace!(target: "engine", "obtained proof for initial set: {} validators, {} bytes", validators.len(), proof.len()); - info!(target: "engine", "Signal for switch to contract-based validator set."); - info!(target: "engine", "Initial contract validators: {:?}", validators); + info!(target: "engine", "Signal for switch to contract-based validator set."); + info!(target: "engine", "Initial contract validators: {:?}", validators); - Ok(proof) + Ok(proof) } impl ValidatorSafeContract { - pub fn new(contract_address: Address) -> Self { - ValidatorSafeContract { - contract_address, - validators: RwLock::new(MemoryLruCache::new(MEMOIZE_CAPACITY)), - client: RwLock::new(None), - } - } - - /// Queries the state and gets the set of validators. - fn get_list(&self, caller: &Call) -> Option { - let contract_address = self.contract_address; - - let (data, decoder) = validator_set::functions::get_validators::call(); - let value = caller(contract_address, data).and_then(|x| decoder.decode(&x.0).map_err(|e| e.to_string())); - - match value { - Ok(new) => { - debug!(target: "engine", "Set of validators obtained: {:?}", new); - Some(SimpleList::new(new)) - }, - Err(s) => { - debug!(target: "engine", "Set of validators could not be updated: {}", s); - None - }, - } - } - - // Whether the header matches the expected bloom. - // - // The expected log should have 3 topics: - // 1. ETHABI-encoded log name. - // 2. the block's parent hash. - // 3. the "nonce": n for the nth transition in history. - // - // We can only search for the first 2, since we don't have the third - // just yet. - // - // The parent hash is included to prevent - // malicious actors from brute forcing other logs that would - // produce the same bloom. - // - // The log data is an array of all new validator addresses. - fn expected_bloom(&self, header: &Header) -> Bloom { - let topics = vec![*EVENT_NAME_HASH, *header.parent_hash()]; - - debug!(target: "engine", "Expected topics for header {}: {:?}", + pub fn new(contract_address: Address) -> Self { + ValidatorSafeContract { + contract_address, + validators: RwLock::new(MemoryLruCache::new(MEMOIZE_CAPACITY)), + client: RwLock::new(None), + } + } + + /// Queries the state and gets the set of validators. + fn get_list(&self, caller: &Call) -> Option { + let contract_address = self.contract_address; + + let (data, decoder) = validator_set::functions::get_validators::call(); + let value = caller(contract_address, data) + .and_then(|x| decoder.decode(&x.0).map_err(|e| e.to_string())); + + match value { + Ok(new) => { + debug!(target: "engine", "Set of validators obtained: {:?}", new); + Some(SimpleList::new(new)) + } + Err(s) => { + debug!(target: "engine", "Set of validators could not be updated: {}", s); + None + } + } + } + + // Whether the header matches the expected bloom. + // + // The expected log should have 3 topics: + // 1. ETHABI-encoded log name. + // 2. the block's parent hash. + // 3. the "nonce": n for the nth transition in history. + // + // We can only search for the first 2, since we don't have the third + // just yet. + // + // The parent hash is included to prevent + // malicious actors from brute forcing other logs that would + // produce the same bloom. + // + // The log data is an array of all new validator addresses. + fn expected_bloom(&self, header: &Header) -> Bloom { + let topics = vec![*EVENT_NAME_HASH, *header.parent_hash()]; + + debug!(target: "engine", "Expected topics for header {}: {:?}", header.hash(), topics); - LogEntry { - address: self.contract_address, - topics: topics, - data: Vec::new(), // irrelevant for bloom. - }.bloom() - } - - // check receipts for log event. bloom should be `expected_bloom` for the - // header the receipts correspond to. - fn extract_from_event(&self, bloom: Bloom, header: &Header, receipts: &[Receipt]) -> Option { - let check_log = |log: &LogEntry| { - log.address == self.contract_address && - log.topics.len() == 2 && - log.topics[0] == *EVENT_NAME_HASH && - log.topics[1] == *header.parent_hash() - }; - - //// iterate in reverse because only the _last_ change in a given - //// block actually has any effect. - //// the contract should only increment the nonce once. - let mut decoded_events = receipts.iter() - .rev() - .filter(|r| r.log_bloom.contains_bloom(&bloom)) - .flat_map(|r| r.logs.iter()) - .filter(move |l| check_log(l)) - .filter_map(|log| { - validator_set::events::initiate_change::parse_log((log.topics.clone(), log.data.clone()).into()).ok() - }); - - // only last log is taken into account - match decoded_events.next() { - None => None, - Some(matched_event) => Some(SimpleList::new(matched_event.new_set)) - } - } + LogEntry { + address: self.contract_address, + topics: topics, + data: Vec::new(), // irrelevant for bloom. + } + .bloom() + } + + // check receipts for log event. bloom should be `expected_bloom` for the + // header the receipts correspond to. + fn extract_from_event( + &self, + bloom: Bloom, + header: &Header, + receipts: &[Receipt], + ) -> Option { + let check_log = |log: &LogEntry| { + log.address == self.contract_address + && log.topics.len() == 2 + && log.topics[0] == *EVENT_NAME_HASH + && log.topics[1] == *header.parent_hash() + }; + + //// iterate in reverse because only the _last_ change in a given + //// block actually has any effect. + //// the contract should only increment the nonce once. + let mut decoded_events = receipts + .iter() + .rev() + .filter(|r| r.log_bloom.contains_bloom(&bloom)) + .flat_map(|r| r.logs.iter()) + .filter(move |l| check_log(l)) + .filter_map(|log| { + validator_set::events::initiate_change::parse_log( + (log.topics.clone(), log.data.clone()).into(), + ) + .ok() + }); + + // only last log is taken into account + match decoded_events.next() { + None => None, + Some(matched_event) => Some(SimpleList::new(matched_event.new_set)), + } + } } impl ValidatorSet for ValidatorSafeContract { - fn default_caller(&self, id: BlockId) -> Box { - let client = self.client.read().clone(); - Box::new(move |addr, data| client.as_ref() - .and_then(Weak::upgrade) - .ok_or_else(|| "No client!".into()) - .and_then(|c| { - match c.as_full_client() { - Some(c) => c.call_contract(id, addr, data), - None => Err("No full client!".into()), - } - }) - .map(|out| (out, Vec::new()))) // generate no proofs in general - } - - fn on_epoch_begin(&self, _first: bool, _header: &Header, caller: &mut SystemCall) -> Result<(), ::error::Error> { - let data = validator_set::functions::finalize_change::encode_input(); - caller(self.contract_address, data) - .map(|_| ()) - .map_err(::engines::EngineError::FailedSystemCall) - .map_err(Into::into) - } - - fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result, String> { - prove_initial(self.contract_address, header, call) - } - - fn is_epoch_end(&self, _first: bool, _chain_head: &Header) -> Option> { - None // no immediate transitions to contract. - } - - fn signals_epoch_end(&self, first: bool, header: &Header, aux: AuxiliaryData) - -> ::engines::EpochChange - { - let receipts = aux.receipts; - - // transition to the first block of a contract requires finality but has no log event. - if first { - debug!(target: "engine", "signalling transition to fresh contract."); - let state_proof = Arc::new(StateProof { - contract_address: self.contract_address, - header: header.clone(), - }); - return ::engines::EpochChange::Yes(::engines::Proof::WithState(state_proof as Arc<_>)); - } - - // otherwise, we're checking for logs. - let bloom = self.expected_bloom(header); - let header_bloom = header.log_bloom(); - - if &bloom & header_bloom != bloom { return ::engines::EpochChange::No } - - trace!(target: "engine", "detected epoch change event bloom"); - - match receipts { - None => ::engines::EpochChange::Unsure(AuxiliaryRequest::Receipts), - Some(receipts) => match self.extract_from_event(bloom, header, receipts) { - None => ::engines::EpochChange::No, - Some(list) => { - info!(target: "engine", "Signal for transition within contract. New list: {:?}", + fn default_caller(&self, id: BlockId) -> Box { + let client = self.client.read().clone(); + Box::new(move |addr, data| { + client + .as_ref() + .and_then(Weak::upgrade) + .ok_or_else(|| "No client!".into()) + .and_then(|c| match c.as_full_client() { + Some(c) => c.call_contract(id, addr, data), + None => Err("No full client!".into()), + }) + .map(|out| (out, Vec::new())) + }) // generate no proofs in general + } + + fn on_epoch_begin( + &self, + _first: bool, + _header: &Header, + caller: &mut SystemCall, + ) -> Result<(), ::error::Error> { + let data = validator_set::functions::finalize_change::encode_input(); + caller(self.contract_address, data) + .map(|_| ()) + .map_err(::engines::EngineError::FailedSystemCall) + .map_err(Into::into) + } + + fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result, String> { + prove_initial(self.contract_address, header, call) + } + + fn is_epoch_end(&self, _first: bool, _chain_head: &Header) -> Option> { + None // no immediate transitions to contract. + } + + fn signals_epoch_end( + &self, + first: bool, + header: &Header, + aux: AuxiliaryData, + ) -> ::engines::EpochChange { + let receipts = aux.receipts; + + // transition to the first block of a contract requires finality but has no log event. + if first { + debug!(target: "engine", "signalling transition to fresh contract."); + let state_proof = Arc::new(StateProof { + contract_address: self.contract_address, + header: header.clone(), + }); + return ::engines::EpochChange::Yes(::engines::Proof::WithState(state_proof as Arc<_>)); + } + + // otherwise, we're checking for logs. + let bloom = self.expected_bloom(header); + let header_bloom = header.log_bloom(); + + if &bloom & header_bloom != bloom { + return ::engines::EpochChange::No; + } + + trace!(target: "engine", "detected epoch change event bloom"); + + match receipts { + None => ::engines::EpochChange::Unsure(AuxiliaryRequest::Receipts), + Some(receipts) => match self.extract_from_event(bloom, header, receipts) { + None => ::engines::EpochChange::No, + Some(list) => { + info!(target: "engine", "Signal for transition within contract. New list: {:?}", &*list); - let proof = encode_proof(&header, receipts); - ::engines::EpochChange::Yes(::engines::Proof::Known(proof)) - } - }, - } - } - - fn epoch_set(&self, first: bool, machine: &EthereumMachine, _number: ::types::BlockNumber, proof: &[u8]) - -> Result<(SimpleList, Option), ::error::Error> - { - let rlp = Rlp::new(proof); - - if first { - trace!(target: "engine", "Recovering initial epoch set"); - - let (old_header, state_items) = decode_first_proof(&rlp)?; - let number = old_header.number(); - let old_hash = old_header.hash(); - let addresses = check_first_proof(machine, self.contract_address, old_header, &state_items) - .map_err(::engines::EngineError::InsufficientProof)?; - - trace!(target: "engine", "extracted epoch set at #{}: {} addresses", + let proof = encode_proof(&header, receipts); + ::engines::EpochChange::Yes(::engines::Proof::Known(proof)) + } + }, + } + } + + fn epoch_set( + &self, + first: bool, + machine: &EthereumMachine, + _number: ::types::BlockNumber, + proof: &[u8], + ) -> Result<(SimpleList, Option), ::error::Error> { + let rlp = Rlp::new(proof); + + if first { + trace!(target: "engine", "Recovering initial epoch set"); + + let (old_header, state_items) = decode_first_proof(&rlp)?; + let number = old_header.number(); + let old_hash = old_header.hash(); + let addresses = + check_first_proof(machine, self.contract_address, old_header, &state_items) + .map_err(::engines::EngineError::InsufficientProof)?; + + trace!(target: "engine", "extracted epoch set at #{}: {} addresses", number, addresses.len()); - Ok((SimpleList::new(addresses), Some(old_hash))) - } else { - let (old_header, receipts) = decode_proof(&rlp)?; - - // ensure receipts match header. - // TODO: optimize? these were just decoded. - let found_root = ::triehash::ordered_trie_root( - receipts.iter().map(::rlp::encode) - ); - if found_root != *old_header.receipts_root() { - return Err(::error::BlockError::InvalidReceiptsRoot( - Mismatch { expected: *old_header.receipts_root(), found: found_root } - ).into()); - } - - let bloom = self.expected_bloom(&old_header); - - match self.extract_from_event(bloom, &old_header, &receipts) { - Some(list) => Ok((list, Some(old_header.hash()))), - None => Err(::engines::EngineError::InsufficientProof("No log event in proof.".into()).into()), - } - } - } - - fn contains_with_caller(&self, block_hash: &H256, address: &Address, caller: &Call) -> bool { - let mut guard = self.validators.write(); - let maybe_existing = guard - .get_mut(block_hash) - .map(|list| list.contains(block_hash, address)); - maybe_existing - .unwrap_or_else(|| self - .get_list(caller) - .map_or(false, |list| { - let contains = list.contains(block_hash, address); - guard.insert(block_hash.clone(), list); - contains - })) - } - - fn get_with_caller(&self, block_hash: &H256, nonce: usize, caller: &Call) -> Address { - let mut guard = self.validators.write(); - let maybe_existing = guard - .get_mut(block_hash) - .map(|list| list.get(block_hash, nonce)); - maybe_existing - .unwrap_or_else(|| self - .get_list(caller) - .map_or_else(Default::default, |list| { - let address = list.get(block_hash, nonce); - guard.insert(block_hash.clone(), list); - address - })) - } - - fn count_with_caller(&self, block_hash: &H256, caller: &Call) -> usize { - let mut guard = self.validators.write(); - let maybe_existing = guard - .get_mut(block_hash) - .map(|list| list.count(block_hash)); - maybe_existing - .unwrap_or_else(|| self - .get_list(caller) - .map_or_else(usize::max_value, |list| { - let address = list.count(block_hash); - guard.insert(block_hash.clone(), list); - address - })) - } - - fn register_client(&self, client: Weak) { - trace!(target: "engine", "Setting up contract caller."); - *self.client.write() = Some(client); - } + Ok((SimpleList::new(addresses), Some(old_hash))) + } else { + let (old_header, receipts) = decode_proof(&rlp)?; + + // ensure receipts match header. + // TODO: optimize? these were just decoded. + let found_root = ::triehash::ordered_trie_root(receipts.iter().map(::rlp::encode)); + if found_root != *old_header.receipts_root() { + return Err(::error::BlockError::InvalidReceiptsRoot(Mismatch { + expected: *old_header.receipts_root(), + found: found_root, + }) + .into()); + } + + let bloom = self.expected_bloom(&old_header); + + match self.extract_from_event(bloom, &old_header, &receipts) { + Some(list) => Ok((list, Some(old_header.hash()))), + None => Err(::engines::EngineError::InsufficientProof( + "No log event in proof.".into(), + ) + .into()), + } + } + } + + fn contains_with_caller(&self, block_hash: &H256, address: &Address, caller: &Call) -> bool { + let mut guard = self.validators.write(); + let maybe_existing = guard + .get_mut(block_hash) + .map(|list| list.contains(block_hash, address)); + maybe_existing.unwrap_or_else(|| { + self.get_list(caller).map_or(false, |list| { + let contains = list.contains(block_hash, address); + guard.insert(block_hash.clone(), list); + contains + }) + }) + } + + fn get_with_caller(&self, block_hash: &H256, nonce: usize, caller: &Call) -> Address { + let mut guard = self.validators.write(); + let maybe_existing = guard + .get_mut(block_hash) + .map(|list| list.get(block_hash, nonce)); + maybe_existing.unwrap_or_else(|| { + self.get_list(caller).map_or_else(Default::default, |list| { + let address = list.get(block_hash, nonce); + guard.insert(block_hash.clone(), list); + address + }) + }) + } + + fn count_with_caller(&self, block_hash: &H256, caller: &Call) -> usize { + let mut guard = self.validators.write(); + let maybe_existing = guard.get_mut(block_hash).map(|list| list.count(block_hash)); + maybe_existing.unwrap_or_else(|| { + self.get_list(caller).map_or_else(usize::max_value, |list| { + let address = list.count(block_hash); + guard.insert(block_hash.clone(), list); + address + }) + }) + } + + fn register_client(&self, client: Weak) { + trace!(target: "engine", "Setting up contract caller."); + *self.client.write() = Some(client); + } } #[cfg(test)] mod tests { - use std::sync::Arc; - use rustc_hex::FromHex; - use hash::keccak; - use ethereum_types::Address; - use types::ids::BlockId; - use spec::Spec; - use accounts::AccountProvider; - use types::transaction::{Transaction, Action}; - use client::{ChainInfo, BlockInfo, ImportBlock}; - use ethkey::Secret; - use miner::{self, MinerService}; - use test_helpers::{generate_dummy_client_with_spec, generate_dummy_client_with_spec_and_data}; - use super::super::ValidatorSet; - use super::{ValidatorSafeContract, EVENT_NAME_HASH}; - use verification::queue::kind::blocks::Unverified; - - #[test] - fn fetches_validators() { - let client = generate_dummy_client_with_spec(Spec::new_validator_safe_contract); - let vc = Arc::new(ValidatorSafeContract::new("0000000000000000000000000000000000000005".parse::
().unwrap())); - vc.register_client(Arc::downgrade(&client) as _); - let last_hash = client.best_block_header().hash(); - assert!(vc.contains(&last_hash, &"7d577a597b2742b498cb5cf0c26cdcd726d39e6e".parse::
().unwrap())); - assert!(vc.contains(&last_hash, &"82a978b3f5962a5b0957d9ee9eef472ee55b42f1".parse::
().unwrap())); - } - - #[test] - fn knows_validators() { - let tap = Arc::new(AccountProvider::transient_provider()); - let s0: Secret = keccak("1").into(); - let v0 = tap.insert_account(s0.clone(), &"".into()).unwrap(); - let v1 = tap.insert_account(keccak("0").into(), &"".into()).unwrap(); - let chain_id = Spec::new_validator_safe_contract().chain_id(); - let client = generate_dummy_client_with_spec(Spec::new_validator_safe_contract); - client.engine().register_client(Arc::downgrade(&client) as _); - let validator_contract = "0000000000000000000000000000000000000005".parse::
().unwrap(); - let signer = Box::new((tap.clone(), v1, "".into())); - - client.miner().set_author(miner::Author::Sealer(signer)); - // Remove "1" validator. - let tx = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 500_000.into(), - action: Action::Call(validator_contract), - value: 0.into(), - data: "bfc708a000000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".from_hex().unwrap(), - }.sign(&s0, Some(chain_id)); - client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap(); - ::client::EngineClient::update_sealing(&*client); - assert_eq!(client.chain_info().best_block_number, 1); - // Add "1" validator back in. - let tx = Transaction { - nonce: 1.into(), - gas_price: 0.into(), - gas: 500_000.into(), - action: Action::Call(validator_contract), - value: 0.into(), - data: "4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".from_hex().unwrap(), - }.sign(&s0, Some(chain_id)); - client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap(); - ::client::EngineClient::update_sealing(&*client); - // The transaction is not yet included so still unable to seal. - assert_eq!(client.chain_info().best_block_number, 1); - - // Switch to the validator that is still there. - let signer = Box::new((tap.clone(), v0, "".into())); - client.miner().set_author(miner::Author::Sealer(signer)); - ::client::EngineClient::update_sealing(&*client); - assert_eq!(client.chain_info().best_block_number, 2); - // Switch back to the added validator, since the state is updated. - let signer = Box::new((tap.clone(), v1, "".into())); - client.miner().set_author(miner::Author::Sealer(signer)); - let tx = Transaction { - nonce: 2.into(), - gas_price: 0.into(), - gas: 21000.into(), - action: Action::Call(Address::default()), - value: 0.into(), - data: Vec::new(), - }.sign(&s0, Some(chain_id)); - client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap(); - ::client::EngineClient::update_sealing(&*client); - // Able to seal again. - assert_eq!(client.chain_info().best_block_number, 3); - - // Check syncing. - let sync_client = generate_dummy_client_with_spec_and_data(Spec::new_validator_safe_contract, 0, 0, &[]); - sync_client.engine().register_client(Arc::downgrade(&sync_client) as _); - for i in 1..4 { - sync_client.import_block(Unverified::from_rlp(client.block(BlockId::Number(i)).unwrap().into_inner()).unwrap()).unwrap(); - } - sync_client.flush_queue(); - assert_eq!(sync_client.chain_info().best_block_number, 3); - } - - #[test] - fn detects_bloom() { - use engines::EpochChange; - use machine::AuxiliaryRequest; - use types::header::Header; - use types::log_entry::LogEntry; - - let client = generate_dummy_client_with_spec(Spec::new_validator_safe_contract); - let engine = client.engine().clone(); - let validator_contract = "0000000000000000000000000000000000000005".parse::
().unwrap(); - - let last_hash = client.best_block_header().hash(); - let mut new_header = Header::default(); - new_header.set_parent_hash(last_hash); - new_header.set_number(1); // so the validator set looks for a log. - - // first, try without the parent hash. - let mut event = LogEntry { - address: validator_contract, - topics: vec![*EVENT_NAME_HASH], - data: Vec::new(), - }; - - new_header.set_log_bloom(event.bloom()); - match engine.signals_epoch_end(&new_header, Default::default()) { - EpochChange::No => {}, - _ => panic!("Expected bloom to be unrecognized."), - }; - - // with the last hash, it should need the receipts. - event.topics.push(last_hash); - new_header.set_log_bloom(event.bloom()); - - match engine.signals_epoch_end(&new_header, Default::default()) { - EpochChange::Unsure(AuxiliaryRequest::Receipts) => {}, - _ => panic!("Expected bloom to be recognized."), - }; - } - - #[test] - fn initial_contract_is_signal() { - use types::header::Header; - use engines::{EpochChange, Proof}; - - let client = generate_dummy_client_with_spec(Spec::new_validator_safe_contract); - let engine = client.engine().clone(); - - let mut new_header = Header::default(); - new_header.set_number(0); // so the validator set doesn't look for a log - - match engine.signals_epoch_end(&new_header, Default::default()) { - EpochChange::Yes(Proof::WithState(_)) => {}, - _ => panic!("Expected state to be required to prove initial signal"), - }; - } + use super::{super::ValidatorSet, ValidatorSafeContract, EVENT_NAME_HASH}; + use accounts::AccountProvider; + use client::{ + traits::{EngineClient, ForceUpdateSealing}, + BlockInfo, ChainInfo, ImportBlock, + }; + use ethereum_types::Address; + use ethkey::Secret; + use hash::keccak; + use miner::{self, MinerService}; + use rustc_hex::FromHex; + use spec::Spec; + use std::sync::Arc; + use test_helpers::{generate_dummy_client_with_spec, generate_dummy_client_with_spec_and_data}; + use types::{ + ids::BlockId, + transaction::{Action, Transaction}, + }; + use verification::queue::kind::blocks::Unverified; + + #[test] + fn fetches_validators() { + let client = generate_dummy_client_with_spec(Spec::new_validator_safe_contract); + let vc = Arc::new(ValidatorSafeContract::new( + "0000000000000000000000000000000000000005" + .parse::
() + .unwrap(), + )); + vc.register_client(Arc::downgrade(&client) as _); + let last_hash = client.best_block_header().hash(); + assert!(vc.contains( + &last_hash, + &"7d577a597b2742b498cb5cf0c26cdcd726d39e6e" + .parse::
() + .unwrap() + )); + assert!(vc.contains( + &last_hash, + &"82a978b3f5962a5b0957d9ee9eef472ee55b42f1" + .parse::
() + .unwrap() + )); + } + + #[test] + fn knows_validators() { + let tap = Arc::new(AccountProvider::transient_provider()); + let s0: Secret = keccak("1").into(); + let v0 = tap.insert_account(s0.clone(), &"".into()).unwrap(); + let v1 = tap.insert_account(keccak("0").into(), &"".into()).unwrap(); + let chain_id = Spec::new_validator_safe_contract().chain_id(); + let client = generate_dummy_client_with_spec(Spec::new_validator_safe_contract); + client + .engine() + .register_client(Arc::downgrade(&client) as _); + let validator_contract = "0000000000000000000000000000000000000005" + .parse::
() + .unwrap(); + let signer = Box::new((tap.clone(), v1, "".into())); + + client.miner().set_author(miner::Author::Sealer(signer)); + // Remove "1" validator. + let tx = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 500_000.into(), + action: Action::Call(validator_contract), + value: 0.into(), + data: "bfc708a000000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1" + .from_hex() + .unwrap(), + } + .sign(&s0, Some(chain_id)); + client + .miner() + .import_own_transaction(client.as_ref(), tx.into()) + .unwrap(); + EngineClient::update_sealing(&*client, ForceUpdateSealing::No); + assert_eq!(client.chain_info().best_block_number, 1); + // Add "1" validator back in. + let tx = Transaction { + nonce: 1.into(), + gas_price: 0.into(), + gas: 500_000.into(), + action: Action::Call(validator_contract), + value: 0.into(), + data: "4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1" + .from_hex() + .unwrap(), + } + .sign(&s0, Some(chain_id)); + client + .miner() + .import_own_transaction(client.as_ref(), tx.into()) + .unwrap(); + EngineClient::update_sealing(&*client, ForceUpdateSealing::No); + // The transaction is not yet included so still unable to seal. + assert_eq!(client.chain_info().best_block_number, 1); + + // Switch to the validator that is still there. + let signer = Box::new((tap.clone(), v0, "".into())); + client.miner().set_author(miner::Author::Sealer(signer)); + EngineClient::update_sealing(&*client, ForceUpdateSealing::No); + assert_eq!(client.chain_info().best_block_number, 2); + // Switch back to the added validator, since the state is updated. + let signer = Box::new((tap.clone(), v1, "".into())); + client.miner().set_author(miner::Author::Sealer(signer)); + let tx = Transaction { + nonce: 2.into(), + gas_price: 0.into(), + gas: 21000.into(), + action: Action::Call(Address::default()), + value: 0.into(), + data: Vec::new(), + } + .sign(&s0, Some(chain_id)); + client + .miner() + .import_own_transaction(client.as_ref(), tx.into()) + .unwrap(); + EngineClient::update_sealing(&*client, ForceUpdateSealing::No); + // Able to seal again. + assert_eq!(client.chain_info().best_block_number, 3); + + // Check syncing. + let sync_client = + generate_dummy_client_with_spec_and_data(Spec::new_validator_safe_contract, 0, 0, &[]); + sync_client + .engine() + .register_client(Arc::downgrade(&sync_client) as _); + for i in 1..4 { + sync_client + .import_block( + Unverified::from_rlp(client.block(BlockId::Number(i)).unwrap().into_inner()) + .unwrap(), + ) + .unwrap(); + } + sync_client.flush_queue(); + assert_eq!(sync_client.chain_info().best_block_number, 3); + } + + #[test] + fn detects_bloom() { + use engines::EpochChange; + use machine::AuxiliaryRequest; + use types::{header::Header, log_entry::LogEntry}; + + let client = generate_dummy_client_with_spec(Spec::new_validator_safe_contract); + let engine = client.engine().clone(); + let validator_contract = "0000000000000000000000000000000000000005" + .parse::
() + .unwrap(); + + let last_hash = client.best_block_header().hash(); + let mut new_header = Header::default(); + new_header.set_parent_hash(last_hash); + new_header.set_number(1); // so the validator set looks for a log. + + // first, try without the parent hash. + let mut event = LogEntry { + address: validator_contract, + topics: vec![*EVENT_NAME_HASH], + data: Vec::new(), + }; + + new_header.set_log_bloom(event.bloom()); + match engine.signals_epoch_end(&new_header, Default::default()) { + EpochChange::No => {} + _ => panic!("Expected bloom to be unrecognized."), + }; + + // with the last hash, it should need the receipts. + event.topics.push(last_hash); + new_header.set_log_bloom(event.bloom()); + + match engine.signals_epoch_end(&new_header, Default::default()) { + EpochChange::Unsure(AuxiliaryRequest::Receipts) => {} + _ => panic!("Expected bloom to be recognized."), + }; + } + + #[test] + fn initial_contract_is_signal() { + use engines::{EpochChange, Proof}; + use types::header::Header; + + let client = generate_dummy_client_with_spec(Spec::new_validator_safe_contract); + let engine = client.engine().clone(); + + let mut new_header = Header::default(); + new_header.set_number(0); // so the validator set doesn't look for a log + + match engine.signals_epoch_end(&new_header, Default::default()) { + EpochChange::Yes(Proof::WithState(_)) => {} + _ => panic!("Expected state to be required to prove initial signal"), + }; + } } diff --git a/ethcore/src/engines/validator_set/simple_list.rs b/ethcore/src/engines/validator_set/simple_list.rs index 0a0294be966..5911d749c54 100644 --- a/ethcore/src/engines/validator_set/simple_list.rs +++ b/ethcore/src/engines/validator_set/simple_list.rs @@ -1,131 +1,139 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . +use ethereum_types::{Address, H256}; /// Preconfigured validator list. - use heapsize::HeapSizeOf; -use ethereum_types::{H256, Address}; -use machine::{AuxiliaryData, Call, EthereumMachine}; -use types::BlockNumber; -use types::header::Header; use super::ValidatorSet; +use machine::{AuxiliaryData, Call, EthereumMachine}; +use types::{header::Header, BlockNumber}; /// Validator set containing a known set of addresses. #[derive(Clone, Debug, PartialEq, Eq, Default)] pub struct SimpleList { - validators: Vec
, + validators: Vec
, } impl SimpleList { - /// Create a new `SimpleList`. - pub fn new(validators: Vec
) -> Self { - SimpleList { - validators: validators, - } - } - - /// Convert into inner representation. - pub fn into_inner(self) -> Vec
{ - self.validators - } + /// Create a new `SimpleList`. + pub fn new(validators: Vec
) -> Self { + SimpleList { + validators: validators, + } + } + + /// Convert into inner representation. + pub fn into_inner(self) -> Vec
{ + self.validators + } } impl ::std::ops::Deref for SimpleList { - type Target = [Address]; + type Target = [Address]; - fn deref(&self) -> &[Address] { &self.validators } + fn deref(&self) -> &[Address] { + &self.validators + } } impl From> for SimpleList { - fn from(validators: Vec
) -> Self { - SimpleList { - validators: validators, - } - } + fn from(validators: Vec
) -> Self { + SimpleList { + validators: validators, + } + } } impl HeapSizeOf for SimpleList { - fn heap_size_of_children(&self) -> usize { - self.validators.heap_size_of_children() - } + fn heap_size_of_children(&self) -> usize { + self.validators.heap_size_of_children() + } } impl ValidatorSet for SimpleList { - fn default_caller(&self, _block_id: ::types::ids::BlockId) -> Box { - Box::new(|_, _| Err("Simple list doesn't require calls.".into())) - } - - fn is_epoch_end(&self, first: bool, _chain_head: &Header) -> Option> { - match first { - true => Some(Vec::new()), // allow transition to fixed list, and instantly - false => None, - } - } - - fn signals_epoch_end(&self, _: bool, _: &Header, _: AuxiliaryData) - -> ::engines::EpochChange - { - ::engines::EpochChange::No - } - - fn epoch_set(&self, _first: bool, _: &EthereumMachine, _: BlockNumber, _: &[u8]) -> Result<(SimpleList, Option), ::error::Error> { - Ok((self.clone(), None)) - } - - fn contains_with_caller(&self, _bh: &H256, address: &Address, _: &Call) -> bool { - self.validators.contains(address) - } - - fn get_with_caller(&self, _bh: &H256, nonce: usize, _: &Call) -> Address { - let validator_n = self.validators.len(); - - if validator_n == 0 { - panic!("Cannot operate with an empty validator set."); - } - - self.validators.get(nonce % validator_n).expect("There are validator_n authorities; taking number modulo validator_n gives number in validator_n range; qed").clone() - } - - fn count_with_caller(&self, _bh: &H256, _: &Call) -> usize { - self.validators.len() - } + fn default_caller(&self, _block_id: ::types::ids::BlockId) -> Box { + Box::new(|_, _| Err("Simple list doesn't require calls.".into())) + } + + fn is_epoch_end(&self, first: bool, _chain_head: &Header) -> Option> { + match first { + true => Some(Vec::new()), // allow transition to fixed list, and instantly + false => None, + } + } + + fn signals_epoch_end( + &self, + _: bool, + _: &Header, + _: AuxiliaryData, + ) -> ::engines::EpochChange { + ::engines::EpochChange::No + } + + fn epoch_set( + &self, + _first: bool, + _: &EthereumMachine, + _: BlockNumber, + _: &[u8], + ) -> Result<(SimpleList, Option), ::error::Error> { + Ok((self.clone(), None)) + } + + fn contains_with_caller(&self, _bh: &H256, address: &Address, _: &Call) -> bool { + self.validators.contains(address) + } + + fn get_with_caller(&self, _bh: &H256, nonce: usize, _: &Call) -> Address { + let validator_n = self.validators.len(); + + if validator_n == 0 { + panic!("Cannot operate with an empty validator set."); + } + + self.validators.get(nonce % validator_n).expect("There are validator_n authorities; taking number modulo validator_n gives number in validator_n range; qed").clone() + } + + fn count_with_caller(&self, _bh: &H256, _: &Call) -> usize { + self.validators.len() + } } -impl AsRef for SimpleList { - fn as_ref(&self) -> &ValidatorSet { +impl AsRef for SimpleList { + fn as_ref(&self) -> &dyn ValidatorSet { self } } #[cfg(test)] mod tests { - use std::str::FromStr; - use ethereum_types::Address; - use super::super::ValidatorSet; - use super::SimpleList; - - #[test] - fn simple_list() { - let a1 = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let a2 = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let list = SimpleList::new(vec![a1.clone(), a2.clone()]); - assert!(list.contains(&Default::default(), &a1)); - assert_eq!(list.get(&Default::default(), 0), a1); - assert_eq!(list.get(&Default::default(), 1), a2); - assert_eq!(list.get(&Default::default(), 2), a1); - } + use super::{super::ValidatorSet, SimpleList}; + use ethereum_types::Address; + use std::str::FromStr; + + #[test] + fn simple_list() { + let a1 = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); + let a2 = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let list = SimpleList::new(vec![a1.clone(), a2.clone()]); + assert!(list.contains(&Default::default(), &a1)); + assert_eq!(list.get(&Default::default(), 0), a1); + assert_eq!(list.get(&Default::default(), 1), a2); + assert_eq!(list.get(&Default::default(), 2), a1); + } } diff --git a/ethcore/src/engines/validator_set/test.rs b/ethcore/src/engines/validator_set/test.rs index c66ff14ad4e..afc4cb1d05a 100644 --- a/ethcore/src/engines/validator_set/test.rs +++ b/ethcore/src/engines/validator_set/test.rs @@ -1,97 +1,119 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . /// Used for Engine testing. - use std::str::FromStr; -use std::sync::Arc; -use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; +use std::sync::{ + atomic::{AtomicUsize, Ordering as AtomicOrdering}, + Arc, +}; use bytes::Bytes; -use ethereum_types::{H256, Address}; +use ethereum_types::{Address, H256}; use heapsize::HeapSizeOf; -use types::BlockNumber; -use types::header::Header; +use types::{header::Header, BlockNumber}; +use super::{SimpleList, ValidatorSet}; use machine::{AuxiliaryData, Call, EthereumMachine}; -use super::{ValidatorSet, SimpleList}; /// Set used for testing with a single validator. pub struct TestSet { - validator: SimpleList, - last_malicious: Arc, - last_benign: Arc, + validator: SimpleList, + last_malicious: Arc, + last_benign: Arc, } impl Default for TestSet { - fn default() -> Self { - TestSet::new(Default::default(), Default::default()) - } + fn default() -> Self { + TestSet::new(Default::default(), Default::default()) + } } impl TestSet { - pub fn new(last_malicious: Arc, last_benign: Arc) -> Self { - TestSet { - validator: SimpleList::new(vec![Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap()]), - last_malicious, - last_benign, - } - } + pub fn new(last_malicious: Arc, last_benign: Arc) -> Self { + TestSet { + validator: SimpleList::new(vec![Address::from_str( + "7d577a597b2742b498cb5cf0c26cdcd726d39e6e", + ) + .unwrap()]), + last_malicious, + last_benign, + } + } } impl HeapSizeOf for TestSet { - fn heap_size_of_children(&self) -> usize { - self.validator.heap_size_of_children() - } + fn heap_size_of_children(&self) -> usize { + self.validator.heap_size_of_children() + } } impl ValidatorSet for TestSet { - fn default_caller(&self, _block_id: ::types::ids::BlockId) -> Box { - Box::new(|_, _| Err("Test set doesn't require calls.".into())) - } - - fn is_epoch_end(&self, _first: bool, _chain_head: &Header) -> Option> { None } - - fn signals_epoch_end(&self, _: bool, _: &Header, _: AuxiliaryData) - -> ::engines::EpochChange - { - ::engines::EpochChange::No - } - - fn epoch_set(&self, _: bool, _: &EthereumMachine, _: BlockNumber, _: &[u8]) -> Result<(SimpleList, Option), ::error::Error> { - Ok((self.validator.clone(), None)) - } - - fn contains_with_caller(&self, bh: &H256, address: &Address, _: &Call) -> bool { - self.validator.contains(bh, address) - } - - fn get_with_caller(&self, bh: &H256, nonce: usize, _: &Call) -> Address { - self.validator.get(bh, nonce) - } - - fn count_with_caller(&self, _bh: &H256, _: &Call) -> usize { - 1 - } - - fn report_malicious(&self, _validator: &Address, _set_block: BlockNumber, block: BlockNumber, _proof: Bytes) { - self.last_malicious.store(block as usize, AtomicOrdering::SeqCst) - } - - fn report_benign(&self, _validator: &Address, _set_block: BlockNumber, block: BlockNumber) { - self.last_benign.store(block as usize, AtomicOrdering::SeqCst) - } + fn default_caller(&self, _block_id: ::types::ids::BlockId) -> Box { + Box::new(|_, _| Err("Test set doesn't require calls.".into())) + } + + fn is_epoch_end(&self, _first: bool, _chain_head: &Header) -> Option> { + None + } + + fn signals_epoch_end( + &self, + _: bool, + _: &Header, + _: AuxiliaryData, + ) -> ::engines::EpochChange { + ::engines::EpochChange::No + } + + fn epoch_set( + &self, + _: bool, + _: &EthereumMachine, + _: BlockNumber, + _: &[u8], + ) -> Result<(SimpleList, Option), ::error::Error> { + Ok((self.validator.clone(), None)) + } + + fn contains_with_caller(&self, bh: &H256, address: &Address, _: &Call) -> bool { + self.validator.contains(bh, address) + } + + fn get_with_caller(&self, bh: &H256, nonce: usize, _: &Call) -> Address { + self.validator.get(bh, nonce) + } + + fn count_with_caller(&self, _bh: &H256, _: &Call) -> usize { + 1 + } + + fn report_malicious( + &self, + _validator: &Address, + _set_block: BlockNumber, + block: BlockNumber, + _proof: Bytes, + ) { + self.last_malicious + .store(block as usize, AtomicOrdering::SeqCst) + } + + fn report_benign(&self, _validator: &Address, _set_block: BlockNumber, block: BlockNumber) { + self.last_benign + .store(block as usize, AtomicOrdering::SeqCst) + } } diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index d5aa45ba0ef..5081c85bf35 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -1,288 +1,299 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! General error types for use in ethcore. // Silence: `use of deprecated item 'std::error::Error::cause': replaced by Error::source, which can support downcasting` -// https://github.com/paritytech/parity-ethereum/issues/10302 +// https://github.com/openethereum/openethereum/issues/10302 #![allow(deprecated)] -use std::{fmt, error}; -use std::time::SystemTime; +use std::{error, fmt, time::SystemTime}; -use ethereum_types::{H256, U256, Address, Bloom}; +use ethereum_types::{Address, Bloom, H256, U256}; use ethkey::Error as EthkeyError; use ethtrie::TrieError; use rlp; use snappy::InvalidInput; use snapshot::Error as SnapshotError; -use types::BlockNumber; -use types::transaction::Error as TransactionError; +use types::{transaction::Error as TransactionError, BlockNumber}; use unexpected::{Mismatch, OutOfBounds}; use engines::EngineError; -pub use executed::{ExecutionError, CallError}; +pub use executed::{CallError, ExecutionError}; #[derive(Debug, PartialEq, Clone, Eq)] /// Errors concerning block processing. pub enum BlockError { - /// Block has too many uncles. - TooManyUncles(OutOfBounds), - /// Extra data is of an invalid length. - ExtraDataOutOfBounds(OutOfBounds), - /// Seal is incorrect format. - InvalidSealArity(Mismatch), - /// Block has too much gas used. - TooMuchGasUsed(OutOfBounds), - /// Uncles hash in header is invalid. - InvalidUnclesHash(Mismatch), - /// An uncle is from a generation too old. - UncleTooOld(OutOfBounds), - /// An uncle is from the same generation as the block. - UncleIsBrother(OutOfBounds), - /// An uncle is already in the chain. - UncleInChain(H256), - /// An uncle is included twice. - DuplicateUncle(H256), - /// An uncle has a parent not in the chain. - UncleParentNotInChain(H256), - /// State root header field is invalid. - InvalidStateRoot(Mismatch), - /// Gas used header field is invalid. - InvalidGasUsed(Mismatch), - /// Transactions root header field is invalid. - InvalidTransactionsRoot(Mismatch), - /// Difficulty is out of range; this can be used as an looser error prior to getting a definitive - /// value for difficulty. This error needs only provide bounds of which it is out. - DifficultyOutOfBounds(OutOfBounds), - /// Difficulty header field is invalid; this is a strong error used after getting a definitive - /// value for difficulty (which is provided). - InvalidDifficulty(Mismatch), - /// Seal element of type H256 (max_hash for Ethash, but could be something else for - /// other seal engines) is out of bounds. - MismatchedH256SealElement(Mismatch), - /// Proof-of-work aspect of seal, which we assume is a 256-bit value, is invalid. - InvalidProofOfWork(OutOfBounds), - /// Some low-level aspect of the seal is incorrect. - InvalidSeal, - /// Gas limit header field is invalid. - InvalidGasLimit(OutOfBounds), - /// Receipts trie root header field is invalid. - InvalidReceiptsRoot(Mismatch), - /// Timestamp header field is invalid. - InvalidTimestamp(OutOfBounds), - /// Timestamp header field is too far in future. - TemporarilyInvalid(OutOfBounds), - /// Log bloom header field is invalid. - InvalidLogBloom(Box>), - /// Number field of header is invalid. - InvalidNumber(Mismatch), - /// Block number isn't sensible. - RidiculousNumber(OutOfBounds), - /// Timestamp header overflowed - TimestampOverflow, - /// Too many transactions from a particular address. - TooManyTransactions(Address), - /// Parent given is unknown. - UnknownParent(H256), - /// Uncle parent given is unknown. - UnknownUncleParent(H256), - /// No transition to epoch number. - UnknownEpochTransition(u64), + /// Block has too many uncles. + TooManyUncles(OutOfBounds), + /// Extra data is of an invalid length. + ExtraDataOutOfBounds(OutOfBounds), + /// Seal is incorrect format. + InvalidSealArity(Mismatch), + /// Block has too much gas used. + TooMuchGasUsed(OutOfBounds), + /// Uncles hash in header is invalid. + InvalidUnclesHash(Mismatch), + /// An uncle is from a generation too old. + UncleTooOld(OutOfBounds), + /// An uncle is from the same generation as the block. + UncleIsBrother(OutOfBounds), + /// An uncle is already in the chain. + UncleInChain(H256), + /// An uncle is included twice. + DuplicateUncle(H256), + /// An uncle has a parent not in the chain. + UncleParentNotInChain(H256), + /// State root header field is invalid. + InvalidStateRoot(Mismatch), + /// Gas used header field is invalid. + InvalidGasUsed(Mismatch), + /// Transactions root header field is invalid. + InvalidTransactionsRoot(Mismatch), + /// Difficulty is out of range; this can be used as an looser error prior to getting a definitive + /// value for difficulty. This error needs only provide bounds of which it is out. + DifficultyOutOfBounds(OutOfBounds), + /// Difficulty header field is invalid; this is a strong error used after getting a definitive + /// value for difficulty (which is provided). + InvalidDifficulty(Mismatch), + /// Seal element of type H256 (max_hash for Ethash, but could be something else for + /// other seal engines) is out of bounds. + MismatchedH256SealElement(Mismatch), + /// Proof-of-work aspect of seal, which we assume is a 256-bit value, is invalid. + InvalidProofOfWork(OutOfBounds), + /// Some low-level aspect of the seal is incorrect. + InvalidSeal, + /// Gas limit header field is invalid. + InvalidGasLimit(OutOfBounds), + /// Receipts trie root header field is invalid. + InvalidReceiptsRoot(Mismatch), + /// Timestamp header field is invalid. + InvalidTimestamp(OutOfBounds), + /// Timestamp header field is too far in future. + TemporarilyInvalid(OutOfBounds), + /// Log bloom header field is invalid. + InvalidLogBloom(Box>), + /// Number field of header is invalid. + InvalidNumber(Mismatch), + /// Block number isn't sensible. + RidiculousNumber(OutOfBounds), + /// Timestamp header overflowed + TimestampOverflow, + /// Too many transactions from a particular address. + TooManyTransactions(Address), + /// Parent given is unknown. + UnknownParent(H256), + /// Uncle parent given is unknown. + UnknownUncleParent(H256), + /// No transition to epoch number. + UnknownEpochTransition(u64), } impl fmt::Display for BlockError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::BlockError::*; - - let msg = match *self { - TooManyUncles(ref oob) => format!("Block has too many uncles. {}", oob), - ExtraDataOutOfBounds(ref oob) => format!("Extra block data too long. {}", oob), - InvalidSealArity(ref mis) => format!("Block seal in incorrect format: {}", mis), - TooMuchGasUsed(ref oob) => format!("Block has too much gas used. {}", oob), - InvalidUnclesHash(ref mis) => format!("Block has invalid uncles hash: {}", mis), - UncleTooOld(ref oob) => format!("Uncle block is too old. {}", oob), - UncleIsBrother(ref oob) => format!("Uncle from same generation as block. {}", oob), - UncleInChain(ref hash) => format!("Uncle {} already in chain", hash), - DuplicateUncle(ref hash) => format!("Uncle {} already in the header", hash), - UncleParentNotInChain(ref hash) => format!("Uncle {} has a parent not in the chain", hash), - InvalidStateRoot(ref mis) => format!("Invalid state root in header: {}", mis), - InvalidGasUsed(ref mis) => format!("Invalid gas used in header: {}", mis), - InvalidTransactionsRoot(ref mis) => format!("Invalid transactions root in header: {}", mis), - DifficultyOutOfBounds(ref oob) => format!("Invalid block difficulty: {}", oob), - InvalidDifficulty(ref mis) => format!("Invalid block difficulty: {}", mis), - MismatchedH256SealElement(ref mis) => format!("Seal element out of bounds: {}", mis), - InvalidProofOfWork(ref oob) => format!("Block has invalid PoW: {}", oob), - InvalidSeal => "Block has invalid seal.".into(), - InvalidGasLimit(ref oob) => format!("Invalid gas limit: {}", oob), - InvalidReceiptsRoot(ref mis) => format!("Invalid receipts trie root in header: {}", mis), - InvalidTimestamp(ref oob) => { - let oob = oob.map(|st| st.elapsed().unwrap_or_default().as_secs()); - format!("Invalid timestamp in header: {}", oob) - }, - TemporarilyInvalid(ref oob) => { - let oob = oob.map(|st| st.elapsed().unwrap_or_default().as_secs()); - format!("Future timestamp in header: {}", oob) - }, - InvalidLogBloom(ref oob) => format!("Invalid log bloom in header: {}", oob), - InvalidNumber(ref mis) => format!("Invalid number in header: {}", mis), - RidiculousNumber(ref oob) => format!("Implausible block number. {}", oob), - UnknownParent(ref hash) => format!("Unknown parent: {}", hash), - UnknownUncleParent(ref hash) => format!("Unknown uncle parent: {}", hash), - UnknownEpochTransition(ref num) => format!("Unknown transition to epoch number: {}", num), - TimestampOverflow => format!("Timestamp overflow"), - TooManyTransactions(ref address) => format!("Too many transactions from: {}", address), - }; - - f.write_fmt(format_args!("Block error ({})", msg)) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::BlockError::*; + + let msg = match *self { + TooManyUncles(ref oob) => format!("Block has too many uncles. {}", oob), + ExtraDataOutOfBounds(ref oob) => format!("Extra block data too long. {}", oob), + InvalidSealArity(ref mis) => format!("Block seal in incorrect format: {}", mis), + TooMuchGasUsed(ref oob) => format!("Block has too much gas used. {}", oob), + InvalidUnclesHash(ref mis) => format!("Block has invalid uncles hash: {}", mis), + UncleTooOld(ref oob) => format!("Uncle block is too old. {}", oob), + UncleIsBrother(ref oob) => format!("Uncle from same generation as block. {}", oob), + UncleInChain(ref hash) => format!("Uncle {} already in chain", hash), + DuplicateUncle(ref hash) => format!("Uncle {} already in the header", hash), + UncleParentNotInChain(ref hash) => { + format!("Uncle {} has a parent not in the chain", hash) + } + InvalidStateRoot(ref mis) => format!("Invalid state root in header: {}", mis), + InvalidGasUsed(ref mis) => format!("Invalid gas used in header: {}", mis), + InvalidTransactionsRoot(ref mis) => { + format!("Invalid transactions root in header: {}", mis) + } + DifficultyOutOfBounds(ref oob) => format!("Invalid block difficulty: {}", oob), + InvalidDifficulty(ref mis) => format!("Invalid block difficulty: {}", mis), + MismatchedH256SealElement(ref mis) => format!("Seal element out of bounds: {}", mis), + InvalidProofOfWork(ref oob) => format!("Block has invalid PoW: {}", oob), + InvalidSeal => "Block has invalid seal.".into(), + InvalidGasLimit(ref oob) => format!("Invalid gas limit: {}", oob), + InvalidReceiptsRoot(ref mis) => { + format!("Invalid receipts trie root in header: {}", mis) + } + InvalidTimestamp(ref oob) => { + let oob = oob.map(|st| st.elapsed().unwrap_or_default().as_secs()); + format!("Invalid timestamp in header: {}", oob) + } + TemporarilyInvalid(ref oob) => { + let oob = oob.map(|st| st.elapsed().unwrap_or_default().as_secs()); + format!("Future timestamp in header: {}", oob) + } + InvalidLogBloom(ref oob) => format!("Invalid log bloom in header: {}", oob), + InvalidNumber(ref mis) => format!("Invalid number in header: {}", mis), + RidiculousNumber(ref oob) => format!("Implausible block number. {}", oob), + UnknownParent(ref hash) => format!("Unknown parent: {}", hash), + UnknownUncleParent(ref hash) => format!("Unknown uncle parent: {}", hash), + UnknownEpochTransition(ref num) => { + format!("Unknown transition to epoch number: {}", num) + } + TimestampOverflow => format!("Timestamp overflow"), + TooManyTransactions(ref address) => format!("Too many transactions from: {}", address), + }; + + f.write_fmt(format_args!("Block error ({})", msg)) + } } impl error::Error for BlockError { - fn description(&self) -> &str { - "Block error" - } + fn description(&self) -> &str { + "Block error" + } } error_chain! { - types { - QueueError, QueueErrorKind, QueueErrorResultExt, QueueErrorResult; - } - - errors { - #[doc = "Queue is full"] - Full(limit: usize) { - description("Queue is full") - display("The queue is full ({})", limit) - } - } - - foreign_links { - Channel(::io::IoError) #[doc = "Io channel error"]; - } + types { + QueueError, QueueErrorKind, QueueErrorResultExt, QueueErrorResult; + } + + errors { + #[doc = "Queue is full"] + Full(limit: usize) { + description("Queue is full") + display("The queue is full ({})", limit) + } + } + + foreign_links { + Channel(::io::IoError) #[doc = "Io channel error"]; + } } error_chain! { - types { - ImportError, ImportErrorKind, ImportErrorResultExt, ImportErrorResult; - } - - errors { - #[doc = "Already in the block chain."] - AlreadyInChain { - description("Block already in chain") - display("Block already in chain") - } - - #[doc = "Already in the block queue"] - AlreadyQueued { - description("block already in the block queue") - display("block already in the block queue") - } - - #[doc = "Already marked as bad from a previous import (could mean parent is bad)."] - KnownBad { - description("block known to be bad") - display("block known to be bad") - } - } + types { + ImportError, ImportErrorKind, ImportErrorResultExt, ImportErrorResult; + } + + errors { + #[doc = "Already in the block chain."] + AlreadyInChain { + description("Block already in chain") + display("Block already in chain") + } + + #[doc = "Already in the block queue"] + AlreadyQueued { + description("block already in the block queue") + display("block already in the block queue") + } + + #[doc = "Already marked as bad from a previous import (could mean parent is bad)."] + KnownBad { + description("block known to be bad") + display("block known to be bad") + } + } } /// Api-level error for transaction import #[derive(Debug, Clone)] pub enum TransactionImportError { - /// Transaction error - Transaction(TransactionError), - /// Other error - Other(String), + /// Transaction error + Transaction(TransactionError), + /// Other error + Other(String), } impl From for TransactionImportError { - fn from(e: Error) -> Self { - match e { - Error(ErrorKind::Transaction(transaction_error), _) => TransactionImportError::Transaction(transaction_error), - _ => TransactionImportError::Other(format!("other block import error: {:?}", e)), - } - } + fn from(e: Error) -> Self { + match e { + Error(ErrorKind::Transaction(transaction_error), _) => { + TransactionImportError::Transaction(transaction_error) + } + _ => TransactionImportError::Other(format!("other block import error: {:?}", e)), + } + } } error_chain! { - types { - Error, ErrorKind, ErrorResultExt, EthcoreResult; - } - - links { - Import(ImportError, ImportErrorKind) #[doc = "Error concerning block import." ]; - Queue(QueueError, QueueErrorKind) #[doc = "Io channel queue error"]; - } - - foreign_links { - Io(::io::IoError) #[doc = "Io create error"]; - StdIo(::std::io::Error) #[doc = "Error concerning the Rust standard library's IO subsystem."]; - Trie(TrieError) #[doc = "Error concerning TrieDBs."]; - Execution(ExecutionError) #[doc = "Error concerning EVM code execution."]; - Block(BlockError) #[doc = "Error concerning block processing."]; - Transaction(TransactionError) #[doc = "Error concerning transaction processing."]; - Snappy(InvalidInput) #[doc = "Snappy error."]; - Engine(EngineError) #[doc = "Consensus vote error."]; - Ethkey(EthkeyError) #[doc = "Ethkey error."]; - Decoder(rlp::DecoderError) #[doc = "RLP decoding errors"]; - } - - errors { - #[doc = "Snapshot error."] - Snapshot(err: SnapshotError) { - description("Snapshot error.") - display("Snapshot error {}", err) - } - - #[doc = "PoW hash is invalid or out of date."] - PowHashInvalid { - description("PoW hash is invalid or out of date.") - display("PoW hash is invalid or out of date.") - } - - #[doc = "The value of the nonce or mishash is invalid."] - PowInvalid { - description("The value of the nonce or mishash is invalid.") - display("The value of the nonce or mishash is invalid.") - } - - #[doc = "Unknown engine given"] - UnknownEngineName(name: String) { - description("Unknown engine name") - display("Unknown engine name ({})", name) - } - } + types { + Error, ErrorKind, ErrorResultExt, EthcoreResult; + } + + links { + Import(ImportError, ImportErrorKind) #[doc = "Error concerning block import." ]; + Queue(QueueError, QueueErrorKind) #[doc = "Io channel queue error"]; + } + + foreign_links { + Io(::io::IoError) #[doc = "Io create error"]; + StdIo(::std::io::Error) #[doc = "Error concerning the Rust standard library's IO subsystem."]; + Trie(TrieError) #[doc = "Error concerning TrieDBs."]; + Execution(ExecutionError) #[doc = "Error concerning EVM code execution."]; + Block(BlockError) #[doc = "Error concerning block processing."]; + Transaction(TransactionError) #[doc = "Error concerning transaction processing."]; + Snappy(InvalidInput) #[doc = "Snappy error."]; + Engine(EngineError) #[doc = "Consensus vote error."]; + Ethkey(EthkeyError) #[doc = "Ethkey error."]; + Decoder(rlp::DecoderError) #[doc = "RLP decoding errors"]; + } + + errors { + #[doc = "Snapshot error."] + Snapshot(err: SnapshotError) { + description("Snapshot error.") + display("Snapshot error {}", err) + } + + #[doc = "PoW hash is invalid or out of date."] + PowHashInvalid { + description("PoW hash is invalid or out of date.") + display("PoW hash is invalid or out of date.") + } + + #[doc = "The value of the nonce or mishash is invalid."] + PowInvalid { + description("The value of the nonce or mishash is invalid.") + display("The value of the nonce or mishash is invalid.") + } + + #[doc = "Unknown engine given"] + UnknownEngineName(name: String) { + description("Unknown engine name") + display("Unknown engine name ({})", name) + } + } } impl From for Error { - fn from(err: SnapshotError) -> Error { - match err { - SnapshotError::Io(err) => ErrorKind::StdIo(err).into(), - SnapshotError::Trie(err) => ErrorKind::Trie(err).into(), - SnapshotError::Decoder(err) => err.into(), - other => ErrorKind::Snapshot(other).into(), - } - } + fn from(err: SnapshotError) -> Error { + match err { + SnapshotError::Io(err) => ErrorKind::StdIo(err).into(), + SnapshotError::Trie(err) => ErrorKind::Trie(err).into(), + SnapshotError::Decoder(err) => err.into(), + other => ErrorKind::Snapshot(other).into(), + } + } } -impl From> for Error where Error: From { - fn from(err: Box) -> Error { - Error::from(*err) - } +impl From> for Error +where + Error: From, +{ + fn from(err: Box) -> Error { + Error::from(*err) + } } diff --git a/ethcore/src/ethereum/denominations.rs b/ethcore/src/ethereum/denominations.rs index 3bf4878c7c5..294f6c244e5 100644 --- a/ethcore/src/ethereum/denominations.rs +++ b/ethcore/src/ethereum/denominations.rs @@ -1,37 +1,47 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use ethereum_types::U256; #[inline] /// 1 Ether in Wei -pub fn ether() -> U256 { U256::exp10(18) } +pub fn ether() -> U256 { + U256::exp10(18) +} #[inline] /// 1 Finney in Wei -pub fn finney() -> U256 { U256::exp10(15) } +pub fn finney() -> U256 { + U256::exp10(15) +} #[inline] /// 1 Szabo in Wei -pub fn szabo() -> U256 { U256::exp10(12) } +pub fn szabo() -> U256 { + U256::exp10(12) +} #[inline] /// 1 Shannon in Wei -pub fn shannon() -> U256 { U256::exp10(9) } +pub fn shannon() -> U256 { + U256::exp10(9) +} #[inline] /// 1 Wei in Wei -pub fn wei() -> U256 { U256::exp10(0) } +pub fn wei() -> U256 { + U256::exp10(0) +} diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 698e23cf7e6..f1d0853eafe 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -1,35 +1,37 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::cmp; -use std::collections::BTreeMap; -use std::path::Path; -use std::sync::Arc; +use std::{cmp, collections::BTreeMap, path::Path, sync::Arc}; use ethereum_types::{H256, H64, U256}; use ethjson; -use hash::{KECCAK_EMPTY_LIST_RLP}; +use hash::KECCAK_EMPTY_LIST_RLP; use rlp::Rlp; -use types::header::{Header, ExtendedHeader}; -use types::BlockNumber; -use unexpected::{OutOfBounds, Mismatch}; +use types::{ + header::{ExtendedHeader, Header}, + BlockNumber, +}; +use unexpected::{Mismatch, OutOfBounds}; use block::ExecutedBlock; -use engines::block_reward::{self, BlockRewardContract, RewardKind}; -use engines::{self, Engine}; +use engines::{ + self, + block_reward::{self, BlockRewardContract, RewardKind}, + Engine, +}; use error::{BlockError, Error}; use ethash::{self, quick_get_difficulty, slow_hash_block_number, EthashManager, OptimizeFor}; use machine::EthereumMachine; @@ -43,156 +45,160 @@ const MAX_SNAPSHOT_BLOCKS: u64 = 30000; /// Ethash specific seal #[derive(Debug, PartialEq)] pub struct Seal { - /// Ethash seal mix_hash - pub mix_hash: H256, - /// Ethash seal nonce - pub nonce: H64, + /// Ethash seal mix_hash + pub mix_hash: H256, + /// Ethash seal nonce + pub nonce: H64, } impl Seal { - /// Tries to parse rlp as ethash seal. - pub fn parse_seal>(seal: &[T]) -> Result { - if seal.len() != 2 { - return Err(BlockError::InvalidSealArity( - Mismatch { - expected: 2, - found: seal.len() - } - ).into()); - } - - let mix_hash = Rlp::new(seal[0].as_ref()).as_val::()?; - let nonce = Rlp::new(seal[1].as_ref()).as_val::()?; - let seal = Seal { - mix_hash, - nonce, - }; - - Ok(seal) - } + /// Tries to parse rlp as ethash seal. + pub fn parse_seal>(seal: &[T]) -> Result { + if seal.len() != 2 { + return Err(BlockError::InvalidSealArity(Mismatch { + expected: 2, + found: seal.len(), + }) + .into()); + } + + let mix_hash = Rlp::new(seal[0].as_ref()).as_val::()?; + let nonce = Rlp::new(seal[1].as_ref()).as_val::()?; + let seal = Seal { mix_hash, nonce }; + + Ok(seal) + } } /// Ethash params. #[derive(Debug, PartialEq)] pub struct EthashParams { - /// Minimum difficulty. - pub minimum_difficulty: U256, - /// Difficulty bound divisor. - pub difficulty_bound_divisor: U256, - /// Difficulty increment divisor. - pub difficulty_increment_divisor: u64, - /// Metropolis difficulty increment divisor. - pub metropolis_difficulty_increment_divisor: u64, - /// Block duration. - pub duration_limit: u64, - /// Homestead transition block number. - pub homestead_transition: u64, - /// Transition block for a change of difficulty params (currently just bound_divisor). - pub difficulty_hardfork_transition: u64, - /// Difficulty param after the difficulty transition. - pub difficulty_hardfork_bound_divisor: U256, - /// Block on which there is no additional difficulty from the exponential bomb. - pub bomb_defuse_transition: u64, - /// Number of first block where EIP-100 rules begin. - pub eip100b_transition: u64, - /// Number of first block where ECIP-1010 begins. - pub ecip1010_pause_transition: u64, - /// Number of first block where ECIP-1010 ends. - pub ecip1010_continue_transition: u64, - /// Total block number for one ECIP-1017 era. - pub ecip1017_era_rounds: u64, - /// Block reward in base units. - pub block_reward: BTreeMap, - /// EXPIP-2 block height - pub expip2_transition: u64, - /// EXPIP-2 duration limit - pub expip2_duration_limit: u64, - /// Block reward contract transition block. - pub block_reward_contract_transition: u64, - /// Block reward contract. - pub block_reward_contract: Option, - /// Difficulty bomb delays. - pub difficulty_bomb_delays: BTreeMap, - /// Block to transition to progpow - pub progpow_transition: u64, + /// Minimum difficulty. + pub minimum_difficulty: U256, + /// Difficulty bound divisor. + pub difficulty_bound_divisor: U256, + /// Difficulty increment divisor. + pub difficulty_increment_divisor: u64, + /// Metropolis difficulty increment divisor. + pub metropolis_difficulty_increment_divisor: u64, + /// Block duration. + pub duration_limit: u64, + /// Homestead transition block number. + pub homestead_transition: u64, + /// Transition block for a change of difficulty params (currently just bound_divisor). + pub difficulty_hardfork_transition: u64, + /// Difficulty param after the difficulty transition. + pub difficulty_hardfork_bound_divisor: U256, + /// Block on which there is no additional difficulty from the exponential bomb. + pub bomb_defuse_transition: u64, + /// Number of first block where EIP-100 rules begin. + pub eip100b_transition: u64, + /// Total block number for one ECIP-1017 era. + pub ecip1017_era_rounds: u64, + /// Block reward in base units. + pub block_reward: BTreeMap, + /// EXPIP-2 block height + pub expip2_transition: u64, + /// EXPIP-2 duration limit + pub expip2_duration_limit: u64, + /// Block reward contract transition block. + pub block_reward_contract_transition: u64, + /// Block reward contract. + pub block_reward_contract: Option, + /// Difficulty bomb delays. + pub difficulty_bomb_delays: BTreeMap, + /// Block to transition to progpow + pub progpow_transition: u64, } impl From for EthashParams { - fn from(p: ethjson::spec::EthashParams) -> Self { - EthashParams { - minimum_difficulty: p.minimum_difficulty.into(), - difficulty_bound_divisor: p.difficulty_bound_divisor.into(), - difficulty_increment_divisor: p.difficulty_increment_divisor.map_or(10, Into::into), - metropolis_difficulty_increment_divisor: p.metropolis_difficulty_increment_divisor.map_or(9, Into::into), - duration_limit: p.duration_limit.map_or(0, Into::into), - homestead_transition: p.homestead_transition.map_or(0, Into::into), - difficulty_hardfork_transition: p.difficulty_hardfork_transition.map_or(u64::max_value(), Into::into), - difficulty_hardfork_bound_divisor: p.difficulty_hardfork_bound_divisor.map_or(p.difficulty_bound_divisor.into(), Into::into), - bomb_defuse_transition: p.bomb_defuse_transition.map_or(u64::max_value(), Into::into), - eip100b_transition: p.eip100b_transition.map_or(u64::max_value(), Into::into), - ecip1010_pause_transition: p.ecip1010_pause_transition.map_or(u64::max_value(), Into::into), - ecip1010_continue_transition: p.ecip1010_continue_transition.map_or(u64::max_value(), Into::into), - ecip1017_era_rounds: p.ecip1017_era_rounds.map_or(u64::max_value(), Into::into), - block_reward: p.block_reward.map_or_else( - || { - let mut ret = BTreeMap::new(); - ret.insert(0, U256::zero()); - ret - }, - |reward| { - match reward { - ethjson::spec::BlockReward::Single(reward) => { - let mut ret = BTreeMap::new(); - ret.insert(0, reward.into()); - ret - }, - ethjson::spec::BlockReward::Multi(multi) => { - multi.into_iter() - .map(|(block, reward)| (block.into(), reward.into())) - .collect() - }, - } - }), - expip2_transition: p.expip2_transition.map_or(u64::max_value(), Into::into), - expip2_duration_limit: p.expip2_duration_limit.map_or(30, Into::into), - progpow_transition: p.progpow_transition.map_or(u64::max_value(), Into::into), - block_reward_contract_transition: p.block_reward_contract_transition.map_or(0, Into::into), - block_reward_contract: match (p.block_reward_contract_code, p.block_reward_contract_address) { - (Some(code), _) => Some(BlockRewardContract::new_from_code(Arc::new(code.into()))), - (_, Some(address)) => Some(BlockRewardContract::new_from_address(address.into())), - (None, None) => None, - }, - difficulty_bomb_delays: p.difficulty_bomb_delays.unwrap_or_default().into_iter() - .map(|(block, delay)| (block.into(), delay.into())) - .collect() - } - } + fn from(p: ethjson::spec::EthashParams) -> Self { + EthashParams { + minimum_difficulty: p.minimum_difficulty.into(), + difficulty_bound_divisor: p.difficulty_bound_divisor.into(), + difficulty_increment_divisor: p.difficulty_increment_divisor.map_or(10, Into::into), + metropolis_difficulty_increment_divisor: p + .metropolis_difficulty_increment_divisor + .map_or(9, Into::into), + duration_limit: p.duration_limit.map_or(0, Into::into), + homestead_transition: p.homestead_transition.map_or(0, Into::into), + difficulty_hardfork_transition: p + .difficulty_hardfork_transition + .map_or(u64::max_value(), Into::into), + difficulty_hardfork_bound_divisor: p + .difficulty_hardfork_bound_divisor + .map_or(p.difficulty_bound_divisor.into(), Into::into), + bomb_defuse_transition: p + .bomb_defuse_transition + .map_or(u64::max_value(), Into::into), + eip100b_transition: p.eip100b_transition.map_or(u64::max_value(), Into::into), + ecip1017_era_rounds: p.ecip1017_era_rounds.map_or(u64::max_value(), Into::into), + block_reward: p.block_reward.map_or_else( + || { + let mut ret = BTreeMap::new(); + ret.insert(0, U256::zero()); + ret + }, + |reward| match reward { + ethjson::spec::BlockReward::Single(reward) => { + let mut ret = BTreeMap::new(); + ret.insert(0, reward.into()); + ret + } + ethjson::spec::BlockReward::Multi(multi) => multi + .into_iter() + .map(|(block, reward)| (block.into(), reward.into())) + .collect(), + }, + ), + expip2_transition: p.expip2_transition.map_or(u64::max_value(), Into::into), + expip2_duration_limit: p.expip2_duration_limit.map_or(30, Into::into), + progpow_transition: p.progpow_transition.map_or(u64::max_value(), Into::into), + block_reward_contract_transition: p + .block_reward_contract_transition + .map_or(0, Into::into), + block_reward_contract: match ( + p.block_reward_contract_code, + p.block_reward_contract_address, + ) { + (Some(code), _) => Some(BlockRewardContract::new_from_code(Arc::new(code.into()))), + (_, Some(address)) => Some(BlockRewardContract::new_from_address(address.into())), + (None, None) => None, + }, + difficulty_bomb_delays: p + .difficulty_bomb_delays + .unwrap_or_default() + .into_iter() + .map(|(block, delay)| (block.into(), delay.into())) + .collect(), + } + } } /// Engine using Ethash proof-of-work consensus algorithm, suitable for Ethereum /// mainnet chains in the Olympic, Frontier and Homestead eras. pub struct Ethash { - ethash_params: EthashParams, - pow: EthashManager, - machine: EthereumMachine, + ethash_params: EthashParams, + pow: EthashManager, + machine: EthereumMachine, } impl Ethash { - /// Create a new instance of Ethash engine - pub fn new>>( - cache_dir: &Path, - ethash_params: EthashParams, - machine: EthereumMachine, - optimize_for: T, - ) -> Arc { - let progpow_transition = ethash_params.progpow_transition; - - Arc::new(Ethash { - ethash_params, - machine, - pow: EthashManager::new(cache_dir.as_ref(), optimize_for.into(), progpow_transition), - }) - } + /// Create a new instance of Ethash engine + pub fn new>>( + cache_dir: &Path, + ethash_params: EthashParams, + machine: EthereumMachine, + optimize_for: T, + ) -> Arc { + let progpow_transition = ethash_params.progpow_transition; + + Arc::new(Ethash { + ethash_params, + machine, + pow: EthashManager::new(cache_dir.as_ref(), optimize_for.into(), progpow_transition), + }) + } } // TODO [rphmeier] @@ -204,719 +210,822 @@ impl Ethash { // in the future, we might move the Ethash epoch // caching onto this mechanism as well. impl engines::EpochVerifier for Arc { - fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) } - fn verify_heavy(&self, header: &Header) -> Result<(), Error> { - self.verify_block_unordered(header) - } + fn verify_light(&self, _header: &Header) -> Result<(), Error> { + Ok(()) + } + fn verify_heavy(&self, header: &Header) -> Result<(), Error> { + self.verify_block_unordered(header) + } } impl Engine for Arc { - fn name(&self) -> &str { "Ethash" } - fn machine(&self) -> &EthereumMachine { &self.machine } - - // Two fields - nonce and mix. - fn seal_fields(&self, _header: &Header) -> usize { 2 } - - /// Additional engine-specific information for the user/developer concerning `header`. - fn extra_info(&self, header: &Header) -> BTreeMap { - match Seal::parse_seal(header.seal()) { - Ok(seal) => map![ - "nonce".to_owned() => format!("0x{:x}", seal.nonce), - "mixHash".to_owned() => format!("0x{:x}", seal.mix_hash) - ], - _ => BTreeMap::default() - } - } - - fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 2 } - - fn maximum_gas_limit(&self) -> Option { Some(0x7fff_ffff_ffff_ffffu64.into()) } - - fn populate_from_parent(&self, header: &mut Header, parent: &Header) { - let difficulty = self.calculate_difficulty(header, parent); - header.set_difficulty(difficulty); - } - - /// Apply the block reward on finalisation of the block. - /// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current). - fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { - use std::ops::Shr; - - let author = *block.header.author(); - let number = block.header.number(); - - let rewards = match self.ethash_params.block_reward_contract { - Some(ref c) if number >= self.ethash_params.block_reward_contract_transition => { - let mut beneficiaries = Vec::new(); - - beneficiaries.push((author, RewardKind::Author)); - for u in &block.uncles { - let uncle_author = u.author(); - beneficiaries.push((*uncle_author, RewardKind::uncle(number, u.number()))); - } - - let mut call = engines::default_system_or_code_call(&self.machine, block); - - let rewards = c.reward(&beneficiaries, &mut call)?; - rewards.into_iter().map(|(author, amount)| (author, RewardKind::External, amount)).collect() - }, - _ => { - let mut rewards = Vec::new(); - - let (_, reward) = self.ethash_params.block_reward.iter() + fn name(&self) -> &str { + "Ethash" + } + fn machine(&self) -> &EthereumMachine { + &self.machine + } + + // Two fields - nonce and mix. + fn seal_fields(&self, _header: &Header) -> usize { + 2 + } + + /// Additional engine-specific information for the user/developer concerning `header`. + fn extra_info(&self, header: &Header) -> BTreeMap { + match Seal::parse_seal(header.seal()) { + Ok(seal) => map![ + "nonce".to_owned() => format!("0x{:x}", seal.nonce), + "mixHash".to_owned() => format!("0x{:x}", seal.mix_hash) + ], + _ => BTreeMap::default(), + } + } + + fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { + 2 + } + + fn maximum_gas_limit(&self) -> Option { + Some(0x7fff_ffff_ffff_ffffu64.into()) + } + + fn populate_from_parent(&self, header: &mut Header, parent: &Header) { + let difficulty = self.calculate_difficulty(header, parent); + header.set_difficulty(difficulty); + } + + /// Apply the block reward on finalisation of the block. + /// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current). + fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { + use std::ops::Shr; + + let author = *block.header.author(); + let number = block.header.number(); + + let rewards = match self.ethash_params.block_reward_contract { + Some(ref c) if number >= self.ethash_params.block_reward_contract_transition => { + let mut beneficiaries = Vec::new(); + + beneficiaries.push((author, RewardKind::Author)); + for u in &block.uncles { + let uncle_author = u.author(); + beneficiaries.push((*uncle_author, RewardKind::uncle(number, u.number()))); + } + + let mut call = engines::default_system_or_code_call(&self.machine, block); + + let rewards = c.reward(&beneficiaries, &mut call)?; + rewards + .into_iter() + .map(|(author, amount)| (author, RewardKind::External, amount)) + .collect() + } + _ => { + let mut rewards = Vec::new(); + + let (_, reward) = self.ethash_params.block_reward.iter() .rev() .find(|&(block, _)| *block <= number) .expect("Current block's reward is not found; this indicates a chain config error; qed"); - let reward = *reward; - - // Applies ECIP-1017 eras. - let eras_rounds = self.ethash_params.ecip1017_era_rounds; - let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, reward, number); - - //let n_uncles = LiveBlock::uncles(&*block).len(); - let n_uncles = block.uncles.len(); - - // Bestow block rewards. - let mut result_block_reward = reward + reward.shr(5) * U256::from(n_uncles); - - rewards.push((author, RewardKind::Author, result_block_reward)); - - // Bestow uncle rewards. - for u in &block.uncles { - let uncle_author = u.author(); - let result_uncle_reward = if eras == 0 { - (reward * U256::from(8 + u.number() - number)).shr(3) - } else { - reward.shr(5) - }; - - rewards.push((*uncle_author, RewardKind::uncle(number, u.number()), result_uncle_reward)); - } - - rewards - }, - }; - - block_reward::apply_block_rewards(&rewards, block, &self.machine) - } - - #[cfg(not(feature = "miner-debug"))] - fn verify_local_seal(&self, header: &Header) -> Result<(), Error> { - self.verify_block_basic(header) - .and_then(|_| self.verify_block_unordered(header)) - } - - #[cfg(feature = "miner-debug")] - fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> { - warn!("Skipping seal verification, running in miner testing mode."); - Ok(()) - } - - fn verify_block_basic(&self, header: &Header) -> Result<(), Error> { - // check the seal fields. - let seal = Seal::parse_seal(header.seal())?; - - // TODO: consider removing these lines. - let min_difficulty = self.ethash_params.minimum_difficulty; - if header.difficulty() < &min_difficulty { - return Err(From::from(BlockError::DifficultyOutOfBounds(OutOfBounds { min: Some(min_difficulty), max: None, found: header.difficulty().clone() }))) - } - - let difficulty = ethash::boundary_to_difficulty(&H256(quick_get_difficulty( - &header.bare_hash().0, - seal.nonce.low_u64(), - &seal.mix_hash.0, - header.number() >= self.ethash_params.progpow_transition - ))); - - if &difficulty < header.difficulty() { - return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty }))); - } - - Ok(()) - } - - fn verify_block_unordered(&self, header: &Header) -> Result<(), Error> { - let seal = Seal::parse_seal(header.seal())?; - - let result = self.pow.compute_light(header.number() as u64, &header.bare_hash().0, seal.nonce.low_u64()); - let mix = H256(result.mix_hash); - let difficulty = ethash::boundary_to_difficulty(&H256(result.value)); - trace!(target: "miner", "num: {num}, seed: {seed}, h: {h}, non: {non}, mix: {mix}, res: {res}", + let reward = *reward; + + // Applies ECIP-1017 eras. + let eras_rounds = self.ethash_params.ecip1017_era_rounds; + let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, reward, number); + + //let n_uncles = LiveBlock::uncles(&*block).len(); + let n_uncles = block.uncles.len(); + + // Bestow block rewards. + let result_block_reward = reward + reward.shr(5) * U256::from(n_uncles); + + rewards.push((author, RewardKind::Author, result_block_reward)); + + // Bestow uncle rewards. + for u in &block.uncles { + let uncle_author = u.author(); + let result_uncle_reward = if eras == 0 { + (reward * U256::from(8 + u.number() - number)).shr(3) + } else { + reward.shr(5) + }; + + rewards.push(( + *uncle_author, + RewardKind::uncle(number, u.number()), + result_uncle_reward, + )); + } + + rewards + } + }; + + block_reward::apply_block_rewards(&rewards, block, &self.machine) + } + + #[cfg(not(feature = "miner-debug"))] + fn verify_local_seal(&self, header: &Header) -> Result<(), Error> { + self.verify_block_basic(header) + .and_then(|_| self.verify_block_unordered(header)) + } + + #[cfg(feature = "miner-debug")] + fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> { + warn!("Skipping seal verification, running in miner testing mode."); + Ok(()) + } + + fn verify_block_basic(&self, header: &Header) -> Result<(), Error> { + // check the seal fields. + let seal = Seal::parse_seal(header.seal())?; + + // TODO: consider removing these lines. + let min_difficulty = self.ethash_params.minimum_difficulty; + if header.difficulty() < &min_difficulty { + return Err(From::from(BlockError::DifficultyOutOfBounds(OutOfBounds { + min: Some(min_difficulty), + max: None, + found: header.difficulty().clone(), + }))); + } + + let difficulty = ethash::boundary_to_difficulty(&H256(quick_get_difficulty( + &header.bare_hash().0, + seal.nonce.low_u64(), + &seal.mix_hash.0, + header.number() >= self.ethash_params.progpow_transition, + ))); + + if &difficulty < header.difficulty() { + return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { + min: Some(header.difficulty().clone()), + max: None, + found: difficulty, + }))); + } + + Ok(()) + } + + fn verify_block_unordered(&self, header: &Header) -> Result<(), Error> { + let seal = Seal::parse_seal(header.seal())?; + + let result = self.pow.compute_light( + header.number() as u64, + &header.bare_hash().0, + seal.nonce.low_u64(), + ); + let mix = H256(result.mix_hash); + let difficulty = ethash::boundary_to_difficulty(&H256(result.value)); + trace!(target: "miner", "num: {num}, seed: {seed}, h: {h}, non: {non}, mix: {mix}, res: {res}", num = header.number() as u64, seed = H256(slow_hash_block_number(header.number() as u64)), h = header.bare_hash(), non = seal.nonce.low_u64(), mix = H256(result.mix_hash), res = H256(result.value)); - if mix != seal.mix_hash { - return Err(From::from(BlockError::MismatchedH256SealElement(Mismatch { expected: mix, found: seal.mix_hash }))); - } - if &difficulty < header.difficulty() { - return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty }))); - } - Ok(()) - } - - fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> { - // we should not calculate difficulty for genesis blocks - if header.number() == 0 { - return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))); - } - - // Check difficulty is correct given the two timestamps. - let expected_difficulty = self.calculate_difficulty(header, parent); - if header.difficulty() != &expected_difficulty { - return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty().clone() }))) - } - - Ok(()) - } - - fn epoch_verifier<'a>(&self, _header: &Header, _proof: &'a [u8]) -> engines::ConstructedVerifier<'a, EthereumMachine> { - engines::ConstructedVerifier::Trusted(Box::new(self.clone())) - } - - fn snapshot_components(&self) -> Option> { - Some(Box::new(::snapshot::PowSnapshot::new(SNAPSHOT_BLOCKS, MAX_SNAPSHOT_BLOCKS))) - } - - fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> engines::ForkChoice { - engines::total_difficulty_fork_choice(new, current) - } + if mix != seal.mix_hash { + return Err(From::from(BlockError::MismatchedH256SealElement( + Mismatch { + expected: mix, + found: seal.mix_hash, + }, + ))); + } + if &difficulty < header.difficulty() { + return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { + min: Some(header.difficulty().clone()), + max: None, + found: difficulty, + }))); + } + Ok(()) + } + + fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> { + // we should not calculate difficulty for genesis blocks + if header.number() == 0 { + return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { + min: Some(1), + max: None, + found: header.number(), + }))); + } + + // Check difficulty is correct given the two timestamps. + let expected_difficulty = self.calculate_difficulty(header, parent); + if header.difficulty() != &expected_difficulty { + return Err(From::from(BlockError::InvalidDifficulty(Mismatch { + expected: expected_difficulty, + found: header.difficulty().clone(), + }))); + } + + Ok(()) + } + + fn epoch_verifier<'a>( + &self, + _header: &Header, + _proof: &'a [u8], + ) -> engines::ConstructedVerifier<'a, EthereumMachine> { + engines::ConstructedVerifier::Trusted(Box::new(self.clone())) + } + + fn snapshot_components(&self) -> Option> { + Some(Box::new(::snapshot::PowSnapshot::new( + SNAPSHOT_BLOCKS, + MAX_SNAPSHOT_BLOCKS, + ))) + } + + fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> engines::ForkChoice { + engines::total_difficulty_fork_choice(new, current) + } } impl Ethash { - fn calculate_difficulty(&self, header: &Header, parent: &Header) -> U256 { - const EXP_DIFF_PERIOD: u64 = 100_000; - if header.number() == 0 { - panic!("Can't calculate genesis block difficulty"); - } - - let parent_has_uncles = parent.uncles_hash() != &KECCAK_EMPTY_LIST_RLP; - - let min_difficulty = self.ethash_params.minimum_difficulty; - - let difficulty_hardfork = header.number() >= self.ethash_params.difficulty_hardfork_transition; - let difficulty_bound_divisor = if difficulty_hardfork { - self.ethash_params.difficulty_hardfork_bound_divisor - } else { - self.ethash_params.difficulty_bound_divisor - }; - - let expip2_hardfork = header.number() >= self.ethash_params.expip2_transition; - let duration_limit = if expip2_hardfork { - self.ethash_params.expip2_duration_limit - } else { - self.ethash_params.duration_limit - }; - - let frontier_limit = self.ethash_params.homestead_transition; - - let mut target = if header.number() < frontier_limit { - if header.timestamp() >= parent.timestamp() + duration_limit { - *parent.difficulty() - (*parent.difficulty() / difficulty_bound_divisor) - } else { - *parent.difficulty() + (*parent.difficulty() / difficulty_bound_divisor) - } - } else { - trace!(target: "ethash", "Calculating difficulty parent.difficulty={}, header.timestamp={}, parent.timestamp={}", parent.difficulty(), header.timestamp(), parent.timestamp()); - //block_diff = parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99) - let (increment_divisor, threshold) = if header.number() < self.ethash_params.eip100b_transition { - (self.ethash_params.difficulty_increment_divisor, 1) - } else if parent_has_uncles { - (self.ethash_params.metropolis_difficulty_increment_divisor, 2) - } else { - (self.ethash_params.metropolis_difficulty_increment_divisor, 1) - }; - - let diff_inc = (header.timestamp() - parent.timestamp()) / increment_divisor; - if diff_inc <= threshold { - *parent.difficulty() + *parent.difficulty() / difficulty_bound_divisor * U256::from(threshold - diff_inc) - } else { - let multiplier: U256 = cmp::min(diff_inc - threshold, 99).into(); - parent.difficulty().saturating_sub( - *parent.difficulty() / difficulty_bound_divisor * multiplier - ) - } - }; - target = cmp::max(min_difficulty, target); - if header.number() < self.ethash_params.bomb_defuse_transition { - if header.number() < self.ethash_params.ecip1010_pause_transition { - let mut number = header.number(); - let original_number = number; - for (block, delay) in &self.ethash_params.difficulty_bomb_delays { - if original_number >= *block { - number = number.saturating_sub(*delay); - } - } - let period = (number / EXP_DIFF_PERIOD) as usize; - if period > 1 { - target = cmp::max(min_difficulty, target + (U256::from(1) << (period - 2))); - } - } else if header.number() < self.ethash_params.ecip1010_continue_transition { - let fixed_difficulty = ((self.ethash_params.ecip1010_pause_transition / EXP_DIFF_PERIOD) - 2) as usize; - target = cmp::max(min_difficulty, target + (U256::from(1) << fixed_difficulty)); - } else { - let period = ((parent.number() + 1) / EXP_DIFF_PERIOD) as usize; - let delay = ((self.ethash_params.ecip1010_continue_transition - self.ethash_params.ecip1010_pause_transition) / EXP_DIFF_PERIOD) as usize; - target = cmp::max(min_difficulty, target + (U256::from(1) << (period - delay - 2))); - } - } - target - } + fn calculate_difficulty(&self, header: &Header, parent: &Header) -> U256 { + const EXP_DIFF_PERIOD: u64 = 100_000; + if header.number() == 0 { + panic!("Can't calculate genesis block difficulty"); + } + + let parent_has_uncles = parent.uncles_hash() != &KECCAK_EMPTY_LIST_RLP; + + let min_difficulty = self.ethash_params.minimum_difficulty; + + let difficulty_hardfork = + header.number() >= self.ethash_params.difficulty_hardfork_transition; + let difficulty_bound_divisor = if difficulty_hardfork { + self.ethash_params.difficulty_hardfork_bound_divisor + } else { + self.ethash_params.difficulty_bound_divisor + }; + + let expip2_hardfork = header.number() >= self.ethash_params.expip2_transition; + let duration_limit = if expip2_hardfork { + self.ethash_params.expip2_duration_limit + } else { + self.ethash_params.duration_limit + }; + + let frontier_limit = self.ethash_params.homestead_transition; + + let mut target = if header.number() < frontier_limit { + if header.timestamp() >= parent.timestamp() + duration_limit { + *parent.difficulty() - (*parent.difficulty() / difficulty_bound_divisor) + } else { + *parent.difficulty() + (*parent.difficulty() / difficulty_bound_divisor) + } + } else { + trace!(target: "ethash", "Calculating difficulty parent.difficulty={}, header.timestamp={}, parent.timestamp={}", parent.difficulty(), header.timestamp(), parent.timestamp()); + //block_diff = parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99) + let (increment_divisor, threshold) = + if header.number() < self.ethash_params.eip100b_transition { + (self.ethash_params.difficulty_increment_divisor, 1) + } else if parent_has_uncles { + ( + self.ethash_params.metropolis_difficulty_increment_divisor, + 2, + ) + } else { + ( + self.ethash_params.metropolis_difficulty_increment_divisor, + 1, + ) + }; + + let diff_inc = (header.timestamp() - parent.timestamp()) / increment_divisor; + if diff_inc <= threshold { + *parent.difficulty() + + *parent.difficulty() / difficulty_bound_divisor + * U256::from(threshold - diff_inc) + } else { + let multiplier: U256 = cmp::min(diff_inc - threshold, 99).into(); + parent + .difficulty() + .saturating_sub(*parent.difficulty() / difficulty_bound_divisor * multiplier) + } + }; + target = cmp::max(min_difficulty, target); + if header.number() < self.ethash_params.bomb_defuse_transition { + let mut number = header.number(); + let original_number = number; + for (block, delay) in &self.ethash_params.difficulty_bomb_delays { + if original_number >= *block { + number = number.saturating_sub(*delay); + } + } + let period = (number / EXP_DIFF_PERIOD) as usize; + if period > 1 { + target = cmp::max(min_difficulty, target + (U256::from(1) << (period - 2))); + } + } + target + } } -fn ecip1017_eras_block_reward(era_rounds: u64, mut reward: U256, block_number:u64) -> (u64, U256) { - let eras = if block_number != 0 && block_number % era_rounds == 0 { - block_number / era_rounds - 1 - } else { - block_number / era_rounds - }; - let mut divi = U256::from(1); - for _ in 0..eras { - reward = reward * U256::from(4); - divi = divi * U256::from(5); - } - reward = reward / divi; - (eras, reward) +fn ecip1017_eras_block_reward(era_rounds: u64, mut reward: U256, block_number: u64) -> (u64, U256) { + let eras = if block_number != 0 && block_number % era_rounds == 0 { + block_number / era_rounds - 1 + } else { + block_number / era_rounds + }; + let mut divi = U256::from(1); + for _ in 0..eras { + reward = reward * U256::from(4); + divi = divi * U256::from(5); + } + reward = reward / divi; + (eras, reward) } #[cfg(test)] mod tests { - use std::str::FromStr; - use std::sync::Arc; - use std::collections::BTreeMap; - use ethereum_types::{H64, H256, U256, Address}; - use block::*; - use test_helpers::get_temp_state_db; - use error::{BlockError, Error, ErrorKind}; - use types::header::Header; - use spec::Spec; - use engines::Engine; - use super::super::{new_morden, new_mcip3_test, new_homestead_test_machine}; - use super::{Ethash, EthashParams, ecip1017_eras_block_reward}; - use rlp; - use tempdir::TempDir; - - fn test_spec() -> Spec { - let tempdir = TempDir::new("").unwrap(); - new_morden(&tempdir.path()) - } - - fn get_default_ethash_params() -> EthashParams { - EthashParams { - minimum_difficulty: U256::from(131072), - difficulty_bound_divisor: U256::from(2048), - difficulty_increment_divisor: 10, - metropolis_difficulty_increment_divisor: 9, - homestead_transition: 1150000, - duration_limit: 13, - block_reward: { - let mut ret = BTreeMap::new(); - ret.insert(0, 0.into()); - ret - }, - difficulty_hardfork_transition: u64::max_value(), - difficulty_hardfork_bound_divisor: U256::from(0), - bomb_defuse_transition: u64::max_value(), - eip100b_transition: u64::max_value(), - ecip1010_pause_transition: u64::max_value(), - ecip1010_continue_transition: u64::max_value(), - ecip1017_era_rounds: u64::max_value(), - expip2_transition: u64::max_value(), - expip2_duration_limit: 30, - block_reward_contract: None, - block_reward_contract_transition: 0, - difficulty_bomb_delays: BTreeMap::new(), - progpow_transition: u64::max_value(), - } - } - - #[test] - fn on_close_block() { - let spec = test_spec(); - let engine = &*spec.engine; - let genesis_header = spec.genesis_header(); - let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); - let b = b.close().unwrap(); - assert_eq!(b.state.balance(&Address::zero()).unwrap(), U256::from_str("4563918244f40000").unwrap()); - } - - #[test] - fn has_valid_ecip1017_eras_block_reward() { - let eras_rounds = 5000000; - - let start_reward: U256 = "4563918244F40000".parse().unwrap(); - - let block_number = 0; - let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); - assert_eq!(0, eras); - assert_eq!(U256::from_str("4563918244F40000").unwrap(), reward); - - let block_number = 5000000; - let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); - assert_eq!(0, eras); - assert_eq!(U256::from_str("4563918244F40000").unwrap(), reward); - - let block_number = 10000000; - let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); - assert_eq!(1, eras); - assert_eq!(U256::from_str("3782DACE9D900000").unwrap(), reward); - - let block_number = 20000000; - let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); - assert_eq!(3, eras); - assert_eq!(U256::from_str("2386F26FC1000000").unwrap(), reward); - - let block_number = 80000000; - let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); - assert_eq!(15, eras); - assert_eq!(U256::from_str("271000000000000").unwrap(), reward); - - let block_number = 250000000; - let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); - assert_eq!(49, eras); - assert_eq!(U256::from_str("51212FFBAF0A").unwrap(), reward); - } - - #[test] - fn on_close_block_with_uncle() { - let spec = test_spec(); - let engine = &*spec.engine; - let genesis_header = spec.genesis_header(); - let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let last_hashes = Arc::new(vec![genesis_header.hash()]); - let mut b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); - let mut uncle = Header::new(); - let uncle_author: Address = "ef2d6d194084c2de36e0dabfce45d046b37d1106".into(); - uncle.set_author(uncle_author); - b.push_uncle(uncle).unwrap(); - - let b = b.close().unwrap(); - assert_eq!(b.state.balance(&Address::zero()).unwrap(), "478eae0e571ba000".into()); - assert_eq!(b.state.balance(&uncle_author).unwrap(), "3cb71f51fc558000".into()); - } - - #[test] - fn has_valid_mcip3_era_block_rewards() { - let spec = new_mcip3_test(); - let engine = &*spec.engine; - let genesis_header = spec.genesis_header(); - let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); - let b = b.close().unwrap(); - - let ubi_contract: Address = "00efdd5883ec628983e9063c7d969fe268bbf310".into(); - let dev_contract: Address = "00756cf8159095948496617f5fb17ed95059f536".into(); - assert_eq!(b.state.balance(&Address::zero()).unwrap(), U256::from_str("d8d726b7177a80000").unwrap()); - assert_eq!(b.state.balance(&ubi_contract).unwrap(), U256::from_str("2b5e3af16b1880000").unwrap()); - assert_eq!(b.state.balance(&dev_contract).unwrap(), U256::from_str("c249fdd327780000").unwrap()); - } - - #[test] - fn has_valid_metadata() { - let engine = test_spec().engine; - assert!(!engine.name().is_empty()); - } - - #[test] - fn can_return_schedule() { - let engine = test_spec().engine; - let schedule = engine.schedule(10000000); - assert!(schedule.stack_limit > 0); - - let schedule = engine.schedule(100); - assert!(!schedule.have_delegate_call); - } - - #[test] - fn can_do_seal_verification_fail() { - let engine = test_spec().engine; - let header: Header = Header::default(); - - let verify_result = engine.verify_block_basic(&header); - - match verify_result { - Err(Error(ErrorKind::Block(BlockError::InvalidSealArity(_)), _)) => {}, - Err(_) => { panic!("should be block seal-arity mismatch error (got {:?})", verify_result); }, - _ => { panic!("Should be error, got Ok"); }, - } - } - - #[test] - fn can_do_difficulty_verification_fail() { - let engine = test_spec().engine; - let mut header: Header = Header::default(); - header.set_seal(vec![rlp::encode(&H256::zero()), rlp::encode(&H64::zero())]); - - let verify_result = engine.verify_block_basic(&header); - - match verify_result { - Err(Error(ErrorKind::Block(BlockError::DifficultyOutOfBounds(_)), _)) => {}, - Err(_) => { panic!("should be block difficulty error (got {:?})", verify_result); }, - _ => { panic!("Should be error, got Ok"); }, - } - } - - #[test] - fn can_do_proof_of_work_verification_fail() { - let engine = test_spec().engine; - let mut header: Header = Header::default(); - header.set_seal(vec![rlp::encode(&H256::zero()), rlp::encode(&H64::zero())]); - header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap()); - - let verify_result = engine.verify_block_basic(&header); - - match verify_result { - Err(Error(ErrorKind::Block(BlockError::InvalidProofOfWork(_)), _)) => {}, - Err(_) => { panic!("should be invalid proof of work error (got {:?})", verify_result); }, - _ => { panic!("Should be error, got Ok"); }, - } - } - - #[test] - fn can_do_seal_unordered_verification_fail() { - let engine = test_spec().engine; - let header = Header::default(); - - let verify_result = engine.verify_block_unordered(&header); - - match verify_result { - Err(Error(ErrorKind::Block(BlockError::InvalidSealArity(_)), _)) => {}, - Err(_) => { panic!("should be block seal-arity mismatch error (got {:?})", verify_result); }, - _ => { panic!("Should be error, got Ok"); }, - } - } - - #[test] - fn can_do_seal_unordered_verification_fail2() { - let engine = test_spec().engine; - let mut header = Header::default(); - header.set_seal(vec![vec![], vec![]]); - - let verify_result = engine.verify_block_unordered(&header); - // rlp error, shouldn't panic - assert!(verify_result.is_err()); - } - - #[test] - fn can_do_seal256_verification_fail() { - let engine = test_spec().engine; - let mut header: Header = Header::default(); - header.set_seal(vec![rlp::encode(&H256::zero()), rlp::encode(&H64::zero())]); - let verify_result = engine.verify_block_unordered(&header); - - match verify_result { - Err(Error(ErrorKind::Block(BlockError::MismatchedH256SealElement(_)), _)) => {}, - Err(_) => { panic!("should be invalid 256-bit seal fail (got {:?})", verify_result); }, - _ => { panic!("Should be error, got Ok"); }, - } - } - - #[test] - fn can_do_proof_of_work_unordered_verification_fail() { - let engine = test_spec().engine; - let mut header: Header = Header::default(); - header.set_seal(vec![rlp::encode(&H256::from("b251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d")), rlp::encode(&H64::zero())]); - header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap()); - - let verify_result = engine.verify_block_unordered(&header); - - match verify_result { - Err(Error(ErrorKind::Block(BlockError::InvalidProofOfWork(_)), _)) => {}, - Err(_) => { panic!("should be invalid proof-of-work fail (got {:?})", verify_result); }, - _ => { panic!("Should be error, got Ok"); }, - } - } - - #[test] - fn can_verify_block_family_genesis_fail() { - let engine = test_spec().engine; - let header: Header = Header::default(); - let parent_header: Header = Header::default(); - - let verify_result = engine.verify_block_family(&header, &parent_header); - - match verify_result { - Err(Error(ErrorKind::Block(BlockError::RidiculousNumber(_)), _)) => {}, - Err(_) => { panic!("should be invalid block number fail (got {:?})", verify_result); }, - _ => { panic!("Should be error, got Ok"); }, - } - } - - #[test] - fn can_verify_block_family_difficulty_fail() { - let engine = test_spec().engine; - let mut header: Header = Header::default(); - header.set_number(2); - let mut parent_header: Header = Header::default(); - parent_header.set_number(1); - - let verify_result = engine.verify_block_family(&header, &parent_header); - - match verify_result { - Err(Error(ErrorKind::Block(BlockError::InvalidDifficulty(_)), _)) => {}, - Err(_) => { panic!("should be invalid difficulty fail (got {:?})", verify_result); }, - _ => { panic!("Should be error, got Ok"); }, - } - } - - #[test] - fn difficulty_frontier() { - let machine = new_homestead_test_machine(); - let ethparams = get_default_ethash_params(); - let tempdir = TempDir::new("").unwrap(); - let ethash = Ethash::new(tempdir.path(), ethparams, machine, None); - - let mut parent_header = Header::default(); - parent_header.set_number(1000000); - parent_header.set_difficulty(U256::from_str("b69de81a22b").unwrap()); - parent_header.set_timestamp(1455404053); - let mut header = Header::default(); - header.set_number(parent_header.number() + 1); - header.set_timestamp(1455404058); - - let difficulty = ethash.calculate_difficulty(&header, &parent_header); - assert_eq!(U256::from_str("b6b4bbd735f").unwrap(), difficulty); - } - - #[test] - fn difficulty_homestead() { - let machine = new_homestead_test_machine(); - let ethparams = get_default_ethash_params(); - let tempdir = TempDir::new("").unwrap(); - let ethash = Ethash::new(tempdir.path(), ethparams, machine, None); - - let mut parent_header = Header::default(); - parent_header.set_number(1500000); - parent_header.set_difficulty(U256::from_str("1fd0fd70792b").unwrap()); - parent_header.set_timestamp(1463003133); - let mut header = Header::default(); - header.set_number(parent_header.number() + 1); - header.set_timestamp(1463003177); - - let difficulty = ethash.calculate_difficulty(&header, &parent_header); - assert_eq!(U256::from_str("1fc50f118efe").unwrap(), difficulty); - } - - #[test] - fn difficulty_classic_bomb_delay() { - let machine = new_homestead_test_machine(); - let ethparams = EthashParams { - ecip1010_pause_transition: 3000000, - ..get_default_ethash_params() - }; - let tempdir = TempDir::new("").unwrap(); - let ethash = Ethash::new(tempdir.path(), ethparams, machine, None); - - let mut parent_header = Header::default(); - parent_header.set_number(3500000); - parent_header.set_difficulty(U256::from_str("6F62EAF8D3C").unwrap()); - parent_header.set_timestamp(1452838500); - let mut header = Header::default(); - header.set_number(parent_header.number() + 1); - - header.set_timestamp(parent_header.timestamp() + 20); - assert_eq!( - U256::from_str("6F55FE9B74B").unwrap(), - ethash.calculate_difficulty(&header, &parent_header) - ); - header.set_timestamp(parent_header.timestamp() + 5); - assert_eq!( - U256::from_str("6F71D75632D").unwrap(), - ethash.calculate_difficulty(&header, &parent_header) - ); - header.set_timestamp(parent_header.timestamp() + 80); - assert_eq!( - U256::from_str("6F02746B3A5").unwrap(), - ethash.calculate_difficulty(&header, &parent_header) - ); - } - - #[test] - fn test_difficulty_bomb_continue() { - let machine = new_homestead_test_machine(); - let ethparams = EthashParams { - ecip1010_pause_transition: 3000000, - ecip1010_continue_transition: 5000000, - ..get_default_ethash_params() - }; - let tempdir = TempDir::new("").unwrap(); - let ethash = Ethash::new(tempdir.path(), ethparams, machine, None); - - let mut parent_header = Header::default(); - parent_header.set_number(5000102); - parent_header.set_difficulty(U256::from_str("14944397EE8B").unwrap()); - parent_header.set_timestamp(1513175023); - let mut header = Header::default(); - header.set_number(parent_header.number() + 1); - header.set_timestamp(parent_header.timestamp() + 6); - assert_eq!( - U256::from_str("1496E6206188").unwrap(), - ethash.calculate_difficulty(&header, &parent_header) - ); - parent_header.set_number(5100123); - parent_header.set_difficulty(U256::from_str("14D24B39C7CF").unwrap()); - parent_header.set_timestamp(1514609324); - header.set_number(parent_header.number() + 1); - header.set_timestamp(parent_header.timestamp() + 41); - assert_eq!( - U256::from_str("14CA9C5D9227").unwrap(), - ethash.calculate_difficulty(&header, &parent_header) - ); - parent_header.set_number(6150001); - parent_header.set_difficulty(U256::from_str("305367B57227").unwrap()); - parent_header.set_timestamp(1529664575); - header.set_number(parent_header.number() + 1); - header.set_timestamp(parent_header.timestamp() + 105); - assert_eq!( - U256::from_str("309D09E0C609").unwrap(), - ethash.calculate_difficulty(&header, &parent_header) - ); - parent_header.set_number(8000000); - parent_header.set_difficulty(U256::from_str("1180B36D4CE5B6A").unwrap()); - parent_header.set_timestamp(1535431724); - header.set_number(parent_header.number() + 1); - header.set_timestamp(parent_header.timestamp() + 420); - assert_eq!( - U256::from_str("5126FFD5BCBB9E7").unwrap(), - ethash.calculate_difficulty(&header, &parent_header) - ); - } - - #[test] - fn difficulty_max_timestamp() { - let machine = new_homestead_test_machine(); - let ethparams = get_default_ethash_params(); - let tempdir = TempDir::new("").unwrap(); - let ethash = Ethash::new(tempdir.path(), ethparams, machine, None); - - let mut parent_header = Header::default(); - parent_header.set_number(1000000); - parent_header.set_difficulty(U256::from_str("b69de81a22b").unwrap()); - parent_header.set_timestamp(1455404053); - let mut header = Header::default(); - header.set_number(parent_header.number() + 1); - header.set_timestamp(u64::max_value()); - - let difficulty = ethash.calculate_difficulty(&header, &parent_header); - assert_eq!(U256::from(12543204905719u64), difficulty); - } - - #[test] - fn test_extra_info() { - let machine = new_homestead_test_machine(); - let ethparams = get_default_ethash_params(); - let tempdir = TempDir::new("").unwrap(); - let ethash = Ethash::new(tempdir.path(), ethparams, machine, None); - let mut header = Header::default(); - header.set_seal(vec![rlp::encode(&H256::from("b251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d")), rlp::encode(&H64::zero())]); - let info = ethash.extra_info(&header); - assert_eq!(info["nonce"], "0x0000000000000000"); - assert_eq!(info["mixHash"], "0xb251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d"); - } + use super::{ + super::{new_homestead_test_machine, new_mcip3_test, new_morden}, + ecip1017_eras_block_reward, Ethash, EthashParams, + }; + use block::*; + use engines::Engine; + use error::{BlockError, Error, ErrorKind}; + use ethereum_types::{Address, H256, H64, U256}; + use rlp; + use spec::Spec; + use std::{collections::BTreeMap, str::FromStr, sync::Arc}; + use tempdir::TempDir; + use test_helpers::get_temp_state_db; + use types::header::Header; + + fn test_spec() -> Spec { + let tempdir = TempDir::new("").unwrap(); + new_morden(&tempdir.path()) + } + + fn get_default_ethash_params() -> EthashParams { + EthashParams { + minimum_difficulty: U256::from(131072), + difficulty_bound_divisor: U256::from(2048), + difficulty_increment_divisor: 10, + metropolis_difficulty_increment_divisor: 9, + homestead_transition: 1150000, + duration_limit: 13, + block_reward: { + let mut ret = BTreeMap::new(); + ret.insert(0, 0.into()); + ret + }, + difficulty_hardfork_transition: u64::max_value(), + difficulty_hardfork_bound_divisor: U256::from(0), + bomb_defuse_transition: u64::max_value(), + eip100b_transition: u64::max_value(), + ecip1017_era_rounds: u64::max_value(), + expip2_transition: u64::max_value(), + expip2_duration_limit: 30, + block_reward_contract: None, + block_reward_contract_transition: 0, + difficulty_bomb_delays: BTreeMap::new(), + progpow_transition: u64::max_value(), + } + } + + #[test] + fn on_close_block() { + let spec = test_spec(); + let engine = &*spec.engine; + let genesis_header = spec.genesis_header(); + let db = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let last_hashes = Arc::new(vec![genesis_header.hash()]); + let b = OpenBlock::new( + engine, + Default::default(), + false, + db, + &genesis_header, + last_hashes, + Address::zero(), + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + let b = b.close().unwrap(); + assert_eq!( + b.state.balance(&Address::zero()).unwrap(), + U256::from_str("4563918244f40000").unwrap() + ); + } + + #[test] + fn has_valid_ecip1017_eras_block_reward() { + let eras_rounds = 5000000; + + let start_reward: U256 = "4563918244F40000".parse().unwrap(); + + let block_number = 0; + let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); + assert_eq!(0, eras); + assert_eq!(U256::from_str("4563918244F40000").unwrap(), reward); + + let block_number = 5000000; + let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); + assert_eq!(0, eras); + assert_eq!(U256::from_str("4563918244F40000").unwrap(), reward); + + let block_number = 10000000; + let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); + assert_eq!(1, eras); + assert_eq!(U256::from_str("3782DACE9D900000").unwrap(), reward); + + let block_number = 20000000; + let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); + assert_eq!(3, eras); + assert_eq!(U256::from_str("2386F26FC1000000").unwrap(), reward); + + let block_number = 80000000; + let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); + assert_eq!(15, eras); + assert_eq!(U256::from_str("271000000000000").unwrap(), reward); + + let block_number = 250000000; + let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); + assert_eq!(49, eras); + assert_eq!(U256::from_str("51212FFBAF0A").unwrap(), reward); + } + + #[test] + fn on_close_block_with_uncle() { + let spec = test_spec(); + let engine = &*spec.engine; + let genesis_header = spec.genesis_header(); + let db = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let last_hashes = Arc::new(vec![genesis_header.hash()]); + let mut b = OpenBlock::new( + engine, + Default::default(), + false, + db, + &genesis_header, + last_hashes, + Address::zero(), + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + let mut uncle = Header::new(); + let uncle_author: Address = "ef2d6d194084c2de36e0dabfce45d046b37d1106".into(); + uncle.set_author(uncle_author); + b.push_uncle(uncle).unwrap(); + + let b = b.close().unwrap(); + assert_eq!( + b.state.balance(&Address::zero()).unwrap(), + "478eae0e571ba000".into() + ); + assert_eq!( + b.state.balance(&uncle_author).unwrap(), + "3cb71f51fc558000".into() + ); + } + + #[test] + fn has_valid_mcip3_era_block_rewards() { + let spec = new_mcip3_test(); + let engine = &*spec.engine; + let genesis_header = spec.genesis_header(); + let db = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let last_hashes = Arc::new(vec![genesis_header.hash()]); + let b = OpenBlock::new( + engine, + Default::default(), + false, + db, + &genesis_header, + last_hashes, + Address::zero(), + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + let b = b.close().unwrap(); + + let ubi_contract: Address = "00efdd5883ec628983e9063c7d969fe268bbf310".into(); + let dev_contract: Address = "00756cf8159095948496617f5fb17ed95059f536".into(); + assert_eq!( + b.state.balance(&Address::zero()).unwrap(), + U256::from_str("d8d726b7177a80000").unwrap() + ); + assert_eq!( + b.state.balance(&ubi_contract).unwrap(), + U256::from_str("2b5e3af16b1880000").unwrap() + ); + assert_eq!( + b.state.balance(&dev_contract).unwrap(), + U256::from_str("c249fdd327780000").unwrap() + ); + } + + #[test] + fn has_valid_metadata() { + let engine = test_spec().engine; + assert!(!engine.name().is_empty()); + } + + #[test] + fn can_return_schedule() { + let engine = test_spec().engine; + let schedule = engine.schedule(10000000); + assert!(schedule.stack_limit > 0); + + let schedule = engine.schedule(100); + assert!(!schedule.have_delegate_call); + } + + #[test] + fn can_do_seal_verification_fail() { + let engine = test_spec().engine; + let header: Header = Header::default(); + + let verify_result = engine.verify_block_basic(&header); + + match verify_result { + Err(Error(ErrorKind::Block(BlockError::InvalidSealArity(_)), _)) => {} + Err(_) => { + panic!( + "should be block seal-arity mismatch error (got {:?})", + verify_result + ); + } + _ => { + panic!("Should be error, got Ok"); + } + } + } + + #[test] + fn can_do_difficulty_verification_fail() { + let engine = test_spec().engine; + let mut header: Header = Header::default(); + header.set_seal(vec![rlp::encode(&H256::zero()), rlp::encode(&H64::zero())]); + + let verify_result = engine.verify_block_basic(&header); + + match verify_result { + Err(Error(ErrorKind::Block(BlockError::DifficultyOutOfBounds(_)), _)) => {} + Err(_) => { + panic!("should be block difficulty error (got {:?})", verify_result); + } + _ => { + panic!("Should be error, got Ok"); + } + } + } + + #[test] + fn can_do_proof_of_work_verification_fail() { + let engine = test_spec().engine; + let mut header: Header = Header::default(); + header.set_seal(vec![rlp::encode(&H256::zero()), rlp::encode(&H64::zero())]); + header.set_difficulty( + U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa") + .unwrap(), + ); + + let verify_result = engine.verify_block_basic(&header); + + match verify_result { + Err(Error(ErrorKind::Block(BlockError::InvalidProofOfWork(_)), _)) => {} + Err(_) => { + panic!( + "should be invalid proof of work error (got {:?})", + verify_result + ); + } + _ => { + panic!("Should be error, got Ok"); + } + } + } + + #[test] + fn can_do_seal_unordered_verification_fail() { + let engine = test_spec().engine; + let header = Header::default(); + + let verify_result = engine.verify_block_unordered(&header); + + match verify_result { + Err(Error(ErrorKind::Block(BlockError::InvalidSealArity(_)), _)) => {} + Err(_) => { + panic!( + "should be block seal-arity mismatch error (got {:?})", + verify_result + ); + } + _ => { + panic!("Should be error, got Ok"); + } + } + } + + #[test] + fn can_do_seal_unordered_verification_fail2() { + let engine = test_spec().engine; + let mut header = Header::default(); + header.set_seal(vec![vec![], vec![]]); + + let verify_result = engine.verify_block_unordered(&header); + // rlp error, shouldn't panic + assert!(verify_result.is_err()); + } + + #[test] + fn can_do_seal256_verification_fail() { + let engine = test_spec().engine; + let mut header: Header = Header::default(); + header.set_seal(vec![rlp::encode(&H256::zero()), rlp::encode(&H64::zero())]); + let verify_result = engine.verify_block_unordered(&header); + + match verify_result { + Err(Error(ErrorKind::Block(BlockError::MismatchedH256SealElement(_)), _)) => {} + Err(_) => { + panic!( + "should be invalid 256-bit seal fail (got {:?})", + verify_result + ); + } + _ => { + panic!("Should be error, got Ok"); + } + } + } + + #[test] + fn can_do_proof_of_work_unordered_verification_fail() { + let engine = test_spec().engine; + let mut header: Header = Header::default(); + header.set_seal(vec![ + rlp::encode(&H256::from( + "b251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d", + )), + rlp::encode(&H64::zero()), + ]); + header.set_difficulty( + U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa") + .unwrap(), + ); + + let verify_result = engine.verify_block_unordered(&header); + + match verify_result { + Err(Error(ErrorKind::Block(BlockError::InvalidProofOfWork(_)), _)) => {} + Err(_) => { + panic!( + "should be invalid proof-of-work fail (got {:?})", + verify_result + ); + } + _ => { + panic!("Should be error, got Ok"); + } + } + } + + #[test] + fn can_verify_block_family_genesis_fail() { + let engine = test_spec().engine; + let header: Header = Header::default(); + let parent_header: Header = Header::default(); + + let verify_result = engine.verify_block_family(&header, &parent_header); + + match verify_result { + Err(Error(ErrorKind::Block(BlockError::RidiculousNumber(_)), _)) => {} + Err(_) => { + panic!( + "should be invalid block number fail (got {:?})", + verify_result + ); + } + _ => { + panic!("Should be error, got Ok"); + } + } + } + + #[test] + fn can_verify_block_family_difficulty_fail() { + let engine = test_spec().engine; + let mut header: Header = Header::default(); + header.set_number(2); + let mut parent_header: Header = Header::default(); + parent_header.set_number(1); + + let verify_result = engine.verify_block_family(&header, &parent_header); + + match verify_result { + Err(Error(ErrorKind::Block(BlockError::InvalidDifficulty(_)), _)) => {} + Err(_) => { + panic!( + "should be invalid difficulty fail (got {:?})", + verify_result + ); + } + _ => { + panic!("Should be error, got Ok"); + } + } + } + + #[test] + fn difficulty_frontier() { + let machine = new_homestead_test_machine(); + let ethparams = get_default_ethash_params(); + let tempdir = TempDir::new("").unwrap(); + let ethash = Ethash::new(tempdir.path(), ethparams, machine, None); + + let mut parent_header = Header::default(); + parent_header.set_number(1000000); + parent_header.set_difficulty(U256::from_str("b69de81a22b").unwrap()); + parent_header.set_timestamp(1455404053); + let mut header = Header::default(); + header.set_number(parent_header.number() + 1); + header.set_timestamp(1455404058); + + let difficulty = ethash.calculate_difficulty(&header, &parent_header); + assert_eq!(U256::from_str("b6b4bbd735f").unwrap(), difficulty); + } + + #[test] + fn difficulty_homestead() { + let machine = new_homestead_test_machine(); + let ethparams = get_default_ethash_params(); + let tempdir = TempDir::new("").unwrap(); + let ethash = Ethash::new(tempdir.path(), ethparams, machine, None); + + let mut parent_header = Header::default(); + parent_header.set_number(1500000); + parent_header.set_difficulty(U256::from_str("1fd0fd70792b").unwrap()); + parent_header.set_timestamp(1463003133); + let mut header = Header::default(); + header.set_number(parent_header.number() + 1); + header.set_timestamp(1463003177); + + let difficulty = ethash.calculate_difficulty(&header, &parent_header); + assert_eq!(U256::from_str("1fc50f118efe").unwrap(), difficulty); + } + + #[test] + fn difficulty_max_timestamp() { + let machine = new_homestead_test_machine(); + let ethparams = get_default_ethash_params(); + let tempdir = TempDir::new("").unwrap(); + let ethash = Ethash::new(tempdir.path(), ethparams, machine, None); + + let mut parent_header = Header::default(); + parent_header.set_number(1000000); + parent_header.set_difficulty(U256::from_str("b69de81a22b").unwrap()); + parent_header.set_timestamp(1455404053); + let mut header = Header::default(); + header.set_number(parent_header.number() + 1); + header.set_timestamp(u64::max_value()); + + let difficulty = ethash.calculate_difficulty(&header, &parent_header); + assert_eq!(U256::from(12543204905719u64), difficulty); + } + + #[test] + fn test_extra_info() { + let machine = new_homestead_test_machine(); + let ethparams = get_default_ethash_params(); + let tempdir = TempDir::new("").unwrap(); + let ethash = Ethash::new(tempdir.path(), ethparams, machine, None); + let mut header = Header::default(); + header.set_seal(vec![ + rlp::encode(&H256::from( + "b251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d", + )), + rlp::encode(&H64::zero()), + ]); + let info = ethash.extra_info(&header); + assert_eq!(info["nonce"], "0x0000000000000000"); + assert_eq!( + info["mixHash"], + "0xb251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d" + ); + } } diff --git a/ethcore/src/ethereum/mod.rs b/ethcore/src/ethereum/mod.rs index d85fbf9b7b2..7a38fecd9f7 100644 --- a/ethcore/src/ethereum/mod.rs +++ b/ethcore/src/ethereum/mod.rs @@ -1,233 +1,397 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Ethereum protocol module. //! //! Contains all Ethereum network specific stuff, such as denominations and //! consensus specifications. -/// Export the ethash module. -pub mod ethash; /// Export the denominations module. pub mod denominations; +/// Export the ethash module. +pub mod ethash; -pub use self::ethash::{Ethash}; -pub use self::denominations::*; +pub use self::{denominations::*, ethash::Ethash}; -use machine::EthereumMachine; use super::spec::*; +use machine::EthereumMachine; /// Load chain spec from `SpecParams` and JSON. pub fn load<'a, T: Into>>>(params: T, b: &[u8]) -> Spec { - match params.into() { - Some(params) => Spec::load(params, b), - None => Spec::load(&::std::env::temp_dir(), b) - }.expect("chain spec is invalid") + match params.into() { + Some(params) => Spec::load(params, b), + None => Spec::load(&::std::env::temp_dir(), b), + } + .expect("chain spec is invalid") } fn load_machine(b: &[u8]) -> EthereumMachine { - Spec::load_machine(b).expect("chain spec is invalid") + Spec::load_machine(b).expect("chain spec is invalid") } /// Create a new Foundation mainnet chain spec. pub fn new_foundation<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/foundation.json")) -} - -/// Create a new Classic mainnet chain spec without the DAO hardfork. -pub fn new_classic<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/classic.json")) + load( + params.into(), + include_bytes!("../../res/ethereum/foundation.json"), + ) } /// Create a new POA Network mainnet chain spec. pub fn new_poanet<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/poacore.json")) + load( + params.into(), + include_bytes!("../../res/ethereum/poacore.json"), + ) +} + +/// Create a new xDai chain spec. +pub fn new_xdai<'a, T: Into>>(params: T) -> Spec { + load( + params.into(), + include_bytes!("../../res/ethereum/xdai.json"), + ) } -/// Create a new Tobalaba mainnet chain spec. -pub fn new_tobalaba<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/tobalaba.json")) +/// Create a new Volta mainnet chain spec. +pub fn new_volta<'a, T: Into>>(params: T) -> Spec { + load( + params.into(), + include_bytes!("../../res/ethereum/volta.json"), + ) } -/// Create a new Expanse mainnet chain spec. -pub fn new_expanse<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/expanse.json")) +/// Create a new EWC mainnet chain spec. +pub fn new_ewc<'a, T: Into>>(params: T) -> Spec { + load(params.into(), include_bytes!("../../res/ethereum/ewc.json")) } /// Create a new Musicoin mainnet chain spec. pub fn new_musicoin<'a, T: Into>>(params: T) -> Spec { - // The musicoin chain spec uses a block reward contract which can be found at - // https://gist.github.com/andresilva/6f2afaf9486732a0797f4bdeae018ee9 - load(params.into(), include_bytes!("../../res/ethereum/musicoin.json")) + // The musicoin chain spec uses a block reward contract which can be found at + // https://gist.github.com/andresilva/6f2afaf9486732a0797f4bdeae018ee9 + load( + params.into(), + include_bytes!("../../res/ethereum/musicoin.json"), + ) } /// Create a new Ellaism mainnet chain spec. pub fn new_ellaism<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/ellaism.json")) + load( + params.into(), + include_bytes!("../../res/ethereum/ellaism.json"), + ) } /// Create a new MIX mainnet chain spec. pub fn new_mix<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/mix.json")) + load(params.into(), include_bytes!("../../res/ethereum/mix.json")) } /// Create a new Callisto chain spec pub fn new_callisto<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/callisto.json")) + load( + params.into(), + include_bytes!("../../res/ethereum/callisto.json"), + ) } /// Create a new Morden testnet chain spec. pub fn new_morden<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/morden.json")) + load( + params.into(), + include_bytes!("../../res/ethereum/morden.json"), + ) } /// Create a new Ropsten testnet chain spec. pub fn new_ropsten<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/ropsten.json")) + load( + params.into(), + include_bytes!("../../res/ethereum/ropsten.json"), + ) } /// Create a new Kovan testnet chain spec. pub fn new_kovan<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/kovan.json")) + load( + params.into(), + include_bytes!("../../res/ethereum/kovan.json"), + ) } /// Create a new Rinkeby testnet chain spec. pub fn new_rinkeby<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/rinkeby.json")) + load( + params.into(), + include_bytes!("../../res/ethereum/rinkeby.json"), + ) } /// Create a new Görli testnet chain spec. pub fn new_goerli<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/goerli.json")) -} - -/// Create a new Kotti testnet chain spec. -pub fn new_kotti<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/kotti.json")) + load( + params.into(), + include_bytes!("../../res/ethereum/goerli.json"), + ) } /// Create a new POA Sokol testnet chain spec. pub fn new_sokol<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/poasokol.json")) + load( + params.into(), + include_bytes!("../../res/ethereum/poasokol.json"), + ) } // For tests /// Create a new Foundation Frontier-era chain spec as though it never changes to Homestead. -pub fn new_frontier_test() -> Spec { load(None, include_bytes!("../../res/ethereum/frontier_test.json")) } +pub fn new_frontier_test() -> Spec { + load( + None, + include_bytes!("../../res/ethereum/frontier_test.json"), + ) +} /// Create a new Ropsten chain spec. -pub fn new_ropsten_test() -> Spec { load(None, include_bytes!("../../res/ethereum/ropsten.json")) } +pub fn new_ropsten_test() -> Spec { + load(None, include_bytes!("../../res/ethereum/ropsten.json")) +} /// Create a new Foundation Homestead-era chain spec as though it never changed from Frontier. -pub fn new_homestead_test() -> Spec { load(None, include_bytes!("../../res/ethereum/homestead_test.json")) } +pub fn new_homestead_test() -> Spec { + load( + None, + include_bytes!("../../res/ethereum/homestead_test.json"), + ) +} /// Create a new Foundation Homestead-EIP150-era chain spec as though it never changed from Homestead/Frontier. -pub fn new_eip150_test() -> Spec { load(None, include_bytes!("../../res/ethereum/eip150_test.json")) } +pub fn new_eip150_test() -> Spec { + load(None, include_bytes!("../../res/ethereum/eip150_test.json")) +} /// Create a new Foundation Homestead-EIP161-era chain spec as though it never changed from Homestead/Frontier. -pub fn new_eip161_test() -> Spec { load(None, include_bytes!("../../res/ethereum/eip161_test.json")) } +pub fn new_eip161_test() -> Spec { + load(None, include_bytes!("../../res/ethereum/eip161_test.json")) +} /// Create a new Foundation Frontier/Homestead/DAO chain spec with transition points at #5 and #8. -pub fn new_transition_test() -> Spec { load(None, include_bytes!("../../res/ethereum/transition_test.json")) } +pub fn new_transition_test() -> Spec { + load( + None, + include_bytes!("../../res/ethereum/transition_test.json"), + ) +} /// Create a new Foundation Mainnet chain spec without genesis accounts. -pub fn new_mainnet_like() -> Spec { load(None, include_bytes!("../../res/ethereum/frontier_like_test.json")) } +pub fn new_mainnet_like() -> Spec { + load( + None, + include_bytes!("../../res/ethereum/frontier_like_test.json"), + ) +} /// Create a new Foundation Byzantium era spec. -pub fn new_byzantium_test() -> Spec { load(None, include_bytes!("../../res/ethereum/byzantium_test.json")) } +pub fn new_byzantium_test() -> Spec { + load( + None, + include_bytes!("../../res/ethereum/byzantium_test.json"), + ) +} /// Create a new Foundation Constantinople era spec. -pub fn new_constantinople_test() -> Spec { load(None, include_bytes!("../../res/ethereum/constantinople_test.json")) } +pub fn new_constantinople_test() -> Spec { + load( + None, + include_bytes!("../../res/ethereum/constantinople_test.json"), + ) +} /// Create a new Foundation St. Peter's (Contantinople Fix) era spec. -pub fn new_constantinople_fix_test() -> Spec { load(None, include_bytes!("../../res/ethereum/st_peters_test.json")) } +pub fn new_constantinople_fix_test() -> Spec { + load( + None, + include_bytes!("../../res/ethereum/st_peters_test.json"), + ) +} + +/// Create a new Foundation Istanbul era spec. +pub fn new_istanbul_test() -> Spec { + load( + None, + include_bytes!("../../res/ethereum/istanbul_test.json"), + ) +} + +/// Create a new BizantiumToConstaninopleFixAt5 era spec. +pub fn new_byzantium_to_constantinoplefixat5_test() -> Spec { + load( + None, + include_bytes!("../../res/ethereum/byzantium_to_constantinoplefixat5_test.json"), + ) +} + +/// Create a new Foundation Berlin era spec. +pub fn new_berlin_test() -> Spec { + load(None, include_bytes!("../../res/ethereum/berlin_test.json")) +} /// Create a new Musicoin-MCIP3-era spec. -pub fn new_mcip3_test() -> Spec { load(None, include_bytes!("../../res/ethereum/mcip3_test.json")) } +pub fn new_mcip3_test() -> Spec { + load(None, include_bytes!("../../res/ethereum/mcip3_test.json")) +} // For tests /// Create a new Foundation Frontier-era chain spec as though it never changes to Homestead. -pub fn new_frontier_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/frontier_test.json")) } +pub fn new_frontier_test_machine() -> EthereumMachine { + load_machine(include_bytes!("../../res/ethereum/frontier_test.json")) +} /// Create a new Foundation Homestead-era chain spec as though it never changed from Frontier. -pub fn new_homestead_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/homestead_test.json")) } +pub fn new_homestead_test_machine() -> EthereumMachine { + load_machine(include_bytes!("../../res/ethereum/homestead_test.json")) +} /// Create a new Foundation Homestead-EIP210-era chain spec as though it never changed from Homestead/Frontier. -pub fn new_eip210_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/eip210_test.json")) } +pub fn new_eip210_test_machine() -> EthereumMachine { + load_machine(include_bytes!("../../res/ethereum/eip210_test.json")) +} /// Create a new Foundation Byzantium era spec. -pub fn new_byzantium_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/byzantium_test.json")) } +pub fn new_byzantium_test_machine() -> EthereumMachine { + load_machine(include_bytes!("../../res/ethereum/byzantium_test.json")) +} /// Create a new Foundation Constantinople era spec. -pub fn new_constantinople_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/constantinople_test.json")) } +pub fn new_constantinople_test_machine() -> EthereumMachine { + load_machine(include_bytes!( + "../../res/ethereum/constantinople_test.json" + )) +} /// Create a new Foundation St. Peter's (Contantinople Fix) era spec. -pub fn new_constantinople_fix_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/st_peters_test.json")) } +pub fn new_constantinople_fix_test_machine() -> EthereumMachine { + load_machine(include_bytes!("../../res/ethereum/st_peters_test.json")) +} + +/// Create a new Foundation Istanbul era spec. +pub fn new_istanbul_test_machine() -> EthereumMachine { + load_machine(include_bytes!("../../res/ethereum/istanbul_test.json")) +} /// Create a new Musicoin-MCIP3-era spec. -pub fn new_mcip3_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/mcip3_test.json")) } +pub fn new_mcip3_test_machine() -> EthereumMachine { + load_machine(include_bytes!("../../res/ethereum/mcip3_test.json")) +} /// Create new Kovan spec with wasm activated at certain block -pub fn new_kovan_wasm_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/kovan_wasm_test.json")) } +pub fn new_kovan_wasm_test_machine() -> EthereumMachine { + load_machine(include_bytes!("../../res/ethereum/kovan_wasm_test.json")) +} #[cfg(test)] mod tests { - use ethereum_types::U256; - use state::*; - use super::*; - use test_helpers::get_temp_state_db; - use types::view; - use types::views::BlockView; - - #[test] - fn ensure_db_good() { - let spec = new_morden(&::std::env::temp_dir()); - let engine = &spec.engine; - let genesis_header = spec.genesis_header(); - let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let s = State::from_existing(db, genesis_header.state_root().clone(), engine.account_start_nonce(0), Default::default()).unwrap(); - assert_eq!(s.balance(&"0000000000000000000000000000000000000001".into()).unwrap(), 1u64.into()); - assert_eq!(s.balance(&"0000000000000000000000000000000000000002".into()).unwrap(), 1u64.into()); - assert_eq!(s.balance(&"0000000000000000000000000000000000000003".into()).unwrap(), 1u64.into()); - assert_eq!(s.balance(&"0000000000000000000000000000000000000004".into()).unwrap(), 1u64.into()); - assert_eq!(s.balance(&"102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c".into()).unwrap(), U256::from(1u64) << 200); - assert_eq!(s.balance(&"0000000000000000000000000000000000000000".into()).unwrap(), 0u64.into()); - } - - #[test] - fn morden() { - let morden = new_morden(&::std::env::temp_dir()); - - assert_eq!(morden.state_root(), "f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9".into()); - let genesis = morden.genesis_block(); - assert_eq!(view!(BlockView, &genesis).header_view().hash(), "0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303".into()); - - let _ = morden.engine; - } - - #[test] - fn frontier() { - let frontier = new_foundation(&::std::env::temp_dir()); - - assert_eq!(frontier.state_root(), "d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544".into()); - let genesis = frontier.genesis_block(); - assert_eq!(view!(BlockView, &genesis).header_view().hash(), "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3".into()); - - let _ = frontier.engine; - } + use super::*; + use ethereum_types::U256; + use state::*; + use test_helpers::get_temp_state_db; + use types::{view, views::BlockView}; + + #[test] + fn ensure_db_good() { + let spec = new_morden(&::std::env::temp_dir()); + let engine = &spec.engine; + let genesis_header = spec.genesis_header(); + let db = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let s = State::from_existing( + db, + genesis_header.state_root().clone(), + engine.account_start_nonce(0), + Default::default(), + ) + .unwrap(); + assert_eq!( + s.balance(&"0000000000000000000000000000000000000001".into()) + .unwrap(), + 1u64.into() + ); + assert_eq!( + s.balance(&"0000000000000000000000000000000000000002".into()) + .unwrap(), + 1u64.into() + ); + assert_eq!( + s.balance(&"0000000000000000000000000000000000000003".into()) + .unwrap(), + 1u64.into() + ); + assert_eq!( + s.balance(&"0000000000000000000000000000000000000004".into()) + .unwrap(), + 1u64.into() + ); + assert_eq!( + s.balance(&"102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c".into()) + .unwrap(), + U256::from(1u64) << 200 + ); + assert_eq!( + s.balance(&"0000000000000000000000000000000000000000".into()) + .unwrap(), + 0u64.into() + ); + } + + #[test] + fn morden() { + let morden = new_morden(&::std::env::temp_dir()); + + assert_eq!( + morden.state_root(), + "f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9".into() + ); + let genesis = morden.genesis_block(); + assert_eq!( + view!(BlockView, &genesis).header_view().hash(), + "0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303".into() + ); + + let _ = morden.engine; + } + + #[test] + fn frontier() { + let frontier = new_foundation(&::std::env::temp_dir()); + + assert_eq!( + frontier.state_root(), + "d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544".into() + ); + let genesis = frontier.genesis_block(); + assert_eq!( + view!(BlockView, &genesis).header_view().hash(), + "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3".into() + ); + + let _ = frontier.engine; + } } diff --git a/ethcore/src/executed.rs b/ethcore/src/executed.rs index 10e06fd05e2..fc403699ba1 100644 --- a/ethcore/src/executed.rs +++ b/ethcore/src/executed.rs @@ -1,199 +1,219 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Transaction execution format module. -use ethereum_types::{U256, U512, Address}; use bytes::Bytes; +use ethereum_types::{Address, U256, U512}; use ethtrie; +use trace::{FlatTrace, VMTrace}; +use types::{log_entry::LogEntry, state_diff::StateDiff}; use vm; -use trace::{VMTrace, FlatTrace}; -use types::state_diff::StateDiff; -use types::log_entry::LogEntry; -use std::{fmt, error}; +use std::{error, fmt}; /// Transaction execution receipt. #[derive(Debug, PartialEq, Clone)] pub struct Executed { - /// True if the outer call/create resulted in an exceptional exit. - pub exception: Option, - - /// Gas paid up front for execution of transaction. - pub gas: U256, - - /// Gas used during execution of transaction. - pub gas_used: U256, - - /// Gas refunded after the execution of transaction. - /// To get gas that was required up front, add `refunded` and `gas_used`. - pub refunded: U256, - - /// Cumulative gas used in current block so far. - /// - /// `cumulative_gas_used = gas_used(t0) + gas_used(t1) + ... gas_used(tn)` - /// - /// where `tn` is current transaction. - pub cumulative_gas_used: U256, - - /// Vector of logs generated by transaction. - pub logs: Vec, - - /// Addresses of contracts created during execution of transaction. - /// Ordered from earliest creation. - /// - /// eg. sender creates contract A and A in constructor creates contract B - /// - /// B creation ends first, and it will be the first element of the vector. - pub contracts_created: Vec
, - /// Transaction output. - pub output: Bytes, - /// The trace of this transaction. - pub trace: Vec, - /// The VM trace of this transaction. - pub vm_trace: Option, - /// The state diff, if we traced it. - pub state_diff: Option, + /// True if the outer call/create resulted in an exceptional exit. + pub exception: Option, + + /// Gas paid up front for execution of transaction. + pub gas: U256, + + /// Gas used during execution of transaction. + pub gas_used: U256, + + /// Gas refunded after the execution of transaction. + /// To get gas that was required up front, add `refunded` and `gas_used`. + pub refunded: U256, + + /// Cumulative gas used in current block so far. + /// + /// `cumulative_gas_used = gas_used(t0) + gas_used(t1) + ... gas_used(tn)` + /// + /// where `tn` is current transaction. + pub cumulative_gas_used: U256, + + /// Vector of logs generated by transaction. + pub logs: Vec, + + /// Addresses of contracts created during execution of transaction. + /// Ordered from earliest creation. + /// + /// eg. sender creates contract A and A in constructor creates contract B + /// + /// B creation ends first, and it will be the first element of the vector. + pub contracts_created: Vec
, + /// Transaction output. + pub output: Bytes, + /// The trace of this transaction. + pub trace: Vec, + /// The VM trace of this transaction. + pub vm_trace: Option, + /// The state diff, if we traced it. + pub state_diff: Option, } /// Result of executing the transaction. #[derive(PartialEq, Debug, Clone)] pub enum ExecutionError { - /// Returned when there gas paid for transaction execution is - /// lower than base gas required. - NotEnoughBaseGas { - /// Absolute minimum gas required. - required: U256, - /// Gas provided. - got: U256 - }, - /// Returned when block (gas_used + gas) > gas_limit. - /// - /// If gas =< gas_limit, upstream may try to execute the transaction - /// in next block. - BlockGasLimitReached { - /// Gas limit of block for transaction. - gas_limit: U256, - /// Gas used in block prior to transaction. - gas_used: U256, - /// Amount of gas in block. - gas: U256 - }, - /// Returned when transaction nonce does not match state nonce. - InvalidNonce { - /// Nonce expected. - expected: U256, - /// Nonce found. - got: U256 - }, - /// Returned when cost of transaction (value + gas_price * gas) exceeds - /// current sender balance. - NotEnoughCash { - /// Minimum required balance. - required: U512, - /// Actual balance. - got: U512 - }, - /// When execution tries to modify the state in static context - MutableCallInStaticContext, - /// Returned when transacting from a non-existing account with dust protection enabled. - SenderMustExist, - /// Returned when internal evm error occurs. - Internal(String), - /// Returned when generic transaction occurs - TransactionMalformed(String), + /// Returned when there gas paid for transaction execution is + /// lower than base gas required. + NotEnoughBaseGas { + /// Absolute minimum gas required. + required: U256, + /// Gas provided. + got: U256, + }, + /// Returned when block (gas_used + gas) > gas_limit. + /// + /// If gas =< gas_limit, upstream may try to execute the transaction + /// in next block. + BlockGasLimitReached { + /// Gas limit of block for transaction. + gas_limit: U256, + /// Gas used in block prior to transaction. + gas_used: U256, + /// Amount of gas in block. + gas: U256, + }, + /// Returned when transaction nonce does not match state nonce. + InvalidNonce { + /// Nonce expected. + expected: U256, + /// Nonce found. + got: U256, + }, + /// Returned when cost of transaction (value + gas_price * gas) exceeds + /// current sender balance. + NotEnoughCash { + /// Minimum required balance. + required: U512, + /// Actual balance. + got: U512, + }, + /// When execution tries to modify the state in static context + MutableCallInStaticContext, + /// Returned when transacting from a non-existing account with dust protection enabled. + SenderMustExist, + /// Returned when internal evm error occurs. + Internal(String), + /// Returned when generic transaction occurs + TransactionMalformed(String), } impl From> for ExecutionError { - fn from(err: Box) -> Self { - ExecutionError::Internal(format!("{:?}", err)) - } + fn from(err: Box) -> Self { + ExecutionError::Internal(format!("{:?}", err)) + } } impl From for ExecutionError { - fn from(err: ethtrie::TrieError) -> Self { - ExecutionError::Internal(format!("{:?}", err)) - } + fn from(err: ethtrie::TrieError) -> Self { + ExecutionError::Internal(format!("{:?}", err)) + } } impl fmt::Display for ExecutionError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::ExecutionError::*; - - let msg = match *self { - NotEnoughBaseGas { ref required, ref got } => - format!("Not enough base gas. {} is required, but only {} paid", required, got), - BlockGasLimitReached { ref gas_limit, ref gas_used, ref gas } => - format!("Block gas limit reached. The limit is {}, {} has \ - already been used, and {} more is required", gas_limit, gas_used, gas), - InvalidNonce { ref expected, ref got } => - format!("Invalid transaction nonce: expected {}, found {}", expected, got), - NotEnoughCash { ref required, ref got } => - format!("Cost of transaction exceeds sender balance. {} is required \ - but the sender only has {}", required, got), - MutableCallInStaticContext => "Mutable Call in static context".to_owned(), - SenderMustExist => "Transacting from an empty account".to_owned(), - Internal(ref msg) => msg.clone(), - TransactionMalformed(ref err) => format!("Malformed transaction: {}", err), - }; - - f.write_fmt(format_args!("Transaction execution error ({}).", msg)) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::ExecutionError::*; + + let msg = match *self { + NotEnoughBaseGas { + ref required, + ref got, + } => format!( + "Not enough base gas. {} is required, but only {} paid", + required, got + ), + BlockGasLimitReached { + ref gas_limit, + ref gas_used, + ref gas, + } => format!( + "Block gas limit reached. The limit is {}, {} has \ + already been used, and {} more is required", + gas_limit, gas_used, gas + ), + InvalidNonce { + ref expected, + ref got, + } => format!( + "Invalid transaction nonce: expected {}, found {}", + expected, got + ), + NotEnoughCash { + ref required, + ref got, + } => format!( + "Cost of transaction exceeds sender balance. {} is required \ + but the sender only has {}", + required, got + ), + MutableCallInStaticContext => "Mutable Call in static context".to_owned(), + SenderMustExist => "Transacting from an empty account".to_owned(), + Internal(ref msg) => msg.clone(), + TransactionMalformed(ref err) => format!("Malformed transaction: {}", err), + }; + + f.write_fmt(format_args!("Transaction execution error ({}).", msg)) + } } impl error::Error for ExecutionError { - fn description(&self) -> &str { - "Transaction execution error" - } + fn description(&self) -> &str { + "Transaction execution error" + } } /// Result of executing the transaction. #[derive(PartialEq, Debug, Clone)] pub enum CallError { - /// Couldn't find the transaction in the chain. - TransactionNotFound, - /// Couldn't find requested block's state in the chain. - StatePruned, - /// Couldn't find an amount of gas that didn't result in an exception. - Exceptional(vm::Error), - /// Corrupt state. - StateCorrupt, - /// Error executing. - Execution(ExecutionError), + /// Couldn't find the transaction in the chain. + TransactionNotFound, + /// Couldn't find requested block's state in the chain. + StatePruned, + /// Couldn't find an amount of gas that didn't result in an exception. + Exceptional(vm::Error), + /// Corrupt state. + StateCorrupt, + /// Error executing. + Execution(ExecutionError), } impl From for CallError { - fn from(error: ExecutionError) -> Self { - CallError::Execution(error) - } + fn from(error: ExecutionError) -> Self { + CallError::Execution(error) + } } impl fmt::Display for CallError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::CallError::*; - - let msg = match *self { - TransactionNotFound => "Transaction couldn't be found in the chain".into(), - StatePruned => "Couldn't find the transaction block's state in the chain".into(), - Exceptional(ref e) => format!("An exception ({}) happened in the execution", e), - StateCorrupt => "Stored state found to be corrupted.".into(), - Execution(ref e) => format!("{}", e), - }; - - f.write_fmt(format_args!("Transaction execution error ({}).", msg)) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::CallError::*; + + let msg = match *self { + TransactionNotFound => "Transaction couldn't be found in the chain".into(), + StatePruned => "Couldn't find the transaction block's state in the chain".into(), + Exceptional(ref e) => format!("An exception ({}) happened in the execution", e), + StateCorrupt => "Stored state found to be corrupted.".into(), + Execution(ref e) => format!("{}", e), + }; + + f.write_fmt(format_args!("Transaction execution error ({}).", msg)) + } } /// Transaction execution result. diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 68d4edc7948..d39303b8720 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -1,40 +1,39 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Transaction Execution environment. -use std::cmp; -use std::sync::Arc; -use hash::keccak; -use ethereum_types::{H256, U256, U512, Address}; use bytes::{Bytes, BytesRef}; -use state::{Backend as StateBackend, State, Substate, CleanupMode}; +use crossbeam_utils::thread; +use ethereum_types::{Address, H256, U256, U512}; +use evm::{CallType, FinalizationResult, Finalize}; use executed::ExecutionError; -use machine::EthereumMachine as Machine; -use evm::{CallType, Finalize, FinalizationResult}; -use vm::{ - self, EnvInfo, CreateContractAddress, ReturnData, CleanDustMode, ActionParams, - ActionValue, Schedule, TrapError, ResumeCall, ResumeCreate -}; -use factory::VmFactory; +pub use executed::{Executed, ExecutionResult}; use externalities::*; +use factory::VmFactory; +use hash::keccak; +use machine::EthereumMachine as Machine; +use state::{Backend as StateBackend, CleanupMode, State, Substate}; +use std::{cmp, sync::Arc}; use trace::{self, Tracer, VMTracer}; -use types::transaction::{Action, SignedTransaction}; use transaction_ext::Transaction; -use crossbeam; -pub use executed::{Executed, ExecutionResult}; +use types::transaction::{Action, SignedTransaction}; +use vm::{ + self, ActionParams, ActionValue, CleanDustMode, CreateContractAddress, EnvInfo, ResumeCall, + ResumeCreate, ReturnData, Schedule, TrapError, +}; #[cfg(debug_assertions)] /// Roughly estimate what stack size each level of evm depth will use. (Debug build) @@ -52,564 +51,853 @@ const STACK_SIZE_ENTRY_OVERHEAD: usize = 100 * 1024; /// Entry stack overhead prior to execution. const STACK_SIZE_ENTRY_OVERHEAD: usize = 20 * 1024; +#[cfg(any(test, feature = "test-helpers"))] +/// Precompile that can never be prunned from state trie (0x3, only in tests) +const UNPRUNABLE_PRECOMPILE_ADDRESS: Option
= Some(ethereum_types::H160([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, +])); + +#[cfg(not(any(test, feature = "test-helpers")))] +/// Precompile that can never be prunned from state trie (none) +const UNPRUNABLE_PRECOMPILE_ADDRESS: Option
= None; + /// Returns new address created from address, nonce, and code hash -pub fn contract_address(address_scheme: CreateContractAddress, sender: &Address, nonce: &U256, code: &[u8]) -> (Address, Option) { - use rlp::RlpStream; - - match address_scheme { - CreateContractAddress::FromSenderAndNonce => { - let mut stream = RlpStream::new_list(2); - stream.append(sender); - stream.append(nonce); - (From::from(keccak(stream.as_raw())), None) - }, - CreateContractAddress::FromSenderSaltAndCodeHash(salt) => { - let code_hash = keccak(code); - let mut buffer = [0u8; 1 + 20 + 32 + 32]; - buffer[0] = 0xff; - &mut buffer[1..(1+20)].copy_from_slice(&sender[..]); - &mut buffer[(1+20)..(1+20+32)].copy_from_slice(&salt[..]); - &mut buffer[(1+20+32)..].copy_from_slice(&code_hash[..]); - (From::from(keccak(&buffer[..])), Some(code_hash)) - }, - CreateContractAddress::FromSenderAndCodeHash => { - let code_hash = keccak(code); - let mut buffer = [0u8; 20 + 32]; - &mut buffer[..20].copy_from_slice(&sender[..]); - &mut buffer[20..].copy_from_slice(&code_hash[..]); - (From::from(keccak(&buffer[..])), Some(code_hash)) - }, - } +pub fn contract_address( + address_scheme: CreateContractAddress, + sender: &Address, + nonce: &U256, + code: &[u8], +) -> (Address, Option) { + use rlp::RlpStream; + + match address_scheme { + CreateContractAddress::FromSenderAndNonce => { + let mut stream = RlpStream::new_list(2); + stream.append(sender); + stream.append(nonce); + (From::from(keccak(stream.as_raw())), None) + } + CreateContractAddress::FromSenderSaltAndCodeHash(salt) => { + let code_hash = keccak(code); + let mut buffer = [0u8; 1 + 20 + 32 + 32]; + buffer[0] = 0xff; + &mut buffer[1..(1 + 20)].copy_from_slice(&sender[..]); + &mut buffer[(1 + 20)..(1 + 20 + 32)].copy_from_slice(&salt[..]); + &mut buffer[(1 + 20 + 32)..].copy_from_slice(&code_hash[..]); + (From::from(keccak(&buffer[..])), Some(code_hash)) + } + CreateContractAddress::FromSenderAndCodeHash => { + let code_hash = keccak(code); + let mut buffer = [0u8; 20 + 32]; + &mut buffer[..20].copy_from_slice(&sender[..]); + &mut buffer[20..].copy_from_slice(&code_hash[..]); + (From::from(keccak(&buffer[..])), Some(code_hash)) + } + } } /// Convert a finalization result into a VM message call result. pub fn into_message_call_result(result: vm::Result) -> vm::MessageCallResult { - match result { - Ok(FinalizationResult { gas_left, return_data, apply_state: true }) => vm::MessageCallResult::Success(gas_left, return_data), - Ok(FinalizationResult { gas_left, return_data, apply_state: false }) => vm::MessageCallResult::Reverted(gas_left, return_data), - _ => vm::MessageCallResult::Failed - } + match result { + Ok(FinalizationResult { + gas_left, + return_data, + apply_state: true, + }) => vm::MessageCallResult::Success(gas_left, return_data), + Ok(FinalizationResult { + gas_left, + return_data, + apply_state: false, + }) => vm::MessageCallResult::Reverted(gas_left, return_data), + _ => vm::MessageCallResult::Failed, + } } /// Convert a finalization result into a VM contract create result. -pub fn into_contract_create_result(result: vm::Result, address: &Address, substate: &mut Substate) -> vm::ContractCreateResult { - match result { - Ok(FinalizationResult { gas_left, apply_state: true, .. }) => { - substate.contracts_created.push(address.clone()); - vm::ContractCreateResult::Created(address.clone(), gas_left) - }, - Ok(FinalizationResult { gas_left, apply_state: false, return_data }) => { - vm::ContractCreateResult::Reverted(gas_left, return_data) - }, - _ => vm::ContractCreateResult::Failed, - } +pub fn into_contract_create_result( + result: vm::Result, + address: &Address, + substate: &mut Substate, +) -> vm::ContractCreateResult { + match result { + Ok(FinalizationResult { + gas_left, + apply_state: true, + .. + }) => { + substate.contracts_created.push(address.clone()); + vm::ContractCreateResult::Created(address.clone(), gas_left) + } + Ok(FinalizationResult { + gas_left, + apply_state: false, + return_data, + }) => vm::ContractCreateResult::Reverted(gas_left, return_data), + _ => vm::ContractCreateResult::Failed, + } } /// Transaction execution options. #[derive(Copy, Clone, PartialEq)] pub struct TransactOptions { - /// Enable call tracing. - pub tracer: T, - /// Enable VM tracing. - pub vm_tracer: V, - /// Check transaction nonce before execution. - pub check_nonce: bool, - /// Records the output from init contract calls. - pub output_from_init_contract: bool, + /// Enable call tracing. + pub tracer: T, + /// Enable VM tracing. + pub vm_tracer: V, + /// Check transaction nonce before execution. + pub check_nonce: bool, + /// Records the output from init contract calls. + pub output_from_init_contract: bool, } impl TransactOptions { - /// Create new `TransactOptions` with given tracer and VM tracer. - pub fn new(tracer: T, vm_tracer: V) -> Self { - TransactOptions { - tracer, - vm_tracer, - check_nonce: true, - output_from_init_contract: false, - } - } - - /// Disables the nonce check - pub fn dont_check_nonce(mut self) -> Self { - self.check_nonce = false; - self - } - - /// Saves the output from contract creation. - pub fn save_output_from_contract(mut self) -> Self { - self.output_from_init_contract = true; - self - } + /// Create new `TransactOptions` with given tracer and VM tracer. + pub fn new(tracer: T, vm_tracer: V) -> Self { + TransactOptions { + tracer, + vm_tracer, + check_nonce: true, + output_from_init_contract: false, + } + } + + /// Disables the nonce check + pub fn dont_check_nonce(mut self) -> Self { + self.check_nonce = false; + self + } + + /// Saves the output from contract creation. + pub fn save_output_from_contract(mut self) -> Self { + self.output_from_init_contract = true; + self + } } impl TransactOptions { - /// Creates new `TransactOptions` with default tracing and VM tracing. - pub fn with_tracing_and_vm_tracing() -> Self { - TransactOptions { - tracer: trace::ExecutiveTracer::default(), - vm_tracer: trace::ExecutiveVMTracer::toplevel(), - check_nonce: true, - output_from_init_contract: false, - } - } + /// Creates new `TransactOptions` with default tracing and VM tracing. + pub fn with_tracing_and_vm_tracing() -> Self { + TransactOptions { + tracer: trace::ExecutiveTracer::default(), + vm_tracer: trace::ExecutiveVMTracer::toplevel(), + check_nonce: true, + output_from_init_contract: false, + } + } } impl TransactOptions { - /// Creates new `TransactOptions` with default tracing and no VM tracing. - pub fn with_tracing() -> Self { - TransactOptions { - tracer: trace::ExecutiveTracer::default(), - vm_tracer: trace::NoopVMTracer, - check_nonce: true, - output_from_init_contract: false, - } - } + /// Creates new `TransactOptions` with default tracing and no VM tracing. + pub fn with_tracing() -> Self { + TransactOptions { + tracer: trace::ExecutiveTracer::default(), + vm_tracer: trace::NoopVMTracer, + check_nonce: true, + output_from_init_contract: false, + } + } } impl TransactOptions { - /// Creates new `TransactOptions` with no tracing and default VM tracing. - pub fn with_vm_tracing() -> Self { - TransactOptions { - tracer: trace::NoopTracer, - vm_tracer: trace::ExecutiveVMTracer::toplevel(), - check_nonce: true, - output_from_init_contract: false, - } - } + /// Creates new `TransactOptions` with no tracing and default VM tracing. + pub fn with_vm_tracing() -> Self { + TransactOptions { + tracer: trace::NoopTracer, + vm_tracer: trace::ExecutiveVMTracer::toplevel(), + check_nonce: true, + output_from_init_contract: false, + } + } } impl TransactOptions { - /// Creates new `TransactOptions` without any tracing. - pub fn with_no_tracing() -> Self { - TransactOptions { - tracer: trace::NoopTracer, - vm_tracer: trace::NoopVMTracer, - check_nonce: true, - output_from_init_contract: false, - } - } + /// Creates new `TransactOptions` without any tracing. + pub fn with_no_tracing() -> Self { + TransactOptions { + tracer: trace::NoopTracer, + vm_tracer: trace::NoopVMTracer, + check_nonce: true, + output_from_init_contract: false, + } + } } /// Trap result returned by executive. -pub type ExecutiveTrapResult<'a, T> = vm::TrapResult, CallCreateExecutive<'a>>; +pub type ExecutiveTrapResult<'a, T> = + vm::TrapResult, CallCreateExecutive<'a>>; /// Trap error for executive. pub type ExecutiveTrapError<'a> = vm::TrapError, CallCreateExecutive<'a>>; enum CallCreateExecutiveKind { - Transfer(ActionParams), - CallBuiltin(ActionParams), - ExecCall(ActionParams, Substate), - ExecCreate(ActionParams, Substate), - ResumeCall(OriginInfo, Box, Substate), - ResumeCreate(OriginInfo, Box, Substate), + Transfer(ActionParams), + CallBuiltin(ActionParams), + ExecCall(ActionParams, Substate), + ExecCreate(ActionParams, Substate), + ResumeCall(OriginInfo, Box, Substate), + ResumeCreate(OriginInfo, Box, Substate), } /// Executive for a raw call/create action. pub struct CallCreateExecutive<'a> { - info: &'a EnvInfo, - machine: &'a Machine, - schedule: &'a Schedule, - factory: &'a VmFactory, - depth: usize, - stack_depth: usize, - static_flag: bool, - is_create: bool, - gas: U256, - kind: CallCreateExecutiveKind, + info: &'a EnvInfo, + machine: &'a Machine, + schedule: &'a Schedule, + factory: &'a VmFactory, + depth: usize, + stack_depth: usize, + static_flag: bool, + is_create: bool, + gas: U256, + kind: CallCreateExecutiveKind, } impl<'a> CallCreateExecutive<'a> { - /// Create a new call executive using raw data. - pub fn new_call_raw(params: ActionParams, info: &'a EnvInfo, machine: &'a Machine, schedule: &'a Schedule, factory: &'a VmFactory, depth: usize, stack_depth: usize, parent_static_flag: bool) -> Self { - trace!("Executive::call(params={:?}) self.env_info={:?}, parent_static={}", params, info, parent_static_flag); - - let gas = params.gas; - let static_flag = parent_static_flag || params.call_type == CallType::StaticCall; - - // if destination is builtin, try to execute it - let kind = if let Some(builtin) = machine.builtin(¶ms.code_address, info.number) { - // Engines aren't supposed to return builtins until activation, but - // prefer to fail rather than silently break consensus. - if !builtin.is_active(info.number) { - panic!("Consensus failure: engine implementation prematurely enabled built-in at {}", params.code_address); - } - - CallCreateExecutiveKind::CallBuiltin(params) - } else { - if params.code.is_some() { - CallCreateExecutiveKind::ExecCall(params, Substate::new()) - } else { - CallCreateExecutiveKind::Transfer(params) - } - }; - - Self { - info, machine, schedule, factory, depth, stack_depth, static_flag, kind, gas, - is_create: false, - } - } - - /// Create a new create executive using raw data. - pub fn new_create_raw(params: ActionParams, info: &'a EnvInfo, machine: &'a Machine, schedule: &'a Schedule, factory: &'a VmFactory, depth: usize, stack_depth: usize, static_flag: bool) -> Self { - trace!("Executive::create(params={:?}) self.env_info={:?}, static={}", params, info, static_flag); - - let gas = params.gas; - - let kind = CallCreateExecutiveKind::ExecCreate(params, Substate::new()); - - Self { - info, machine, schedule, factory, depth, stack_depth, static_flag, kind, gas, - is_create: true, - } - } - - /// If this executive contains an unconfirmed substate, returns a mutable reference to it. - pub fn unconfirmed_substate(&mut self) -> Option<&mut Substate> { - match self.kind { - CallCreateExecutiveKind::ExecCall(_, ref mut unsub) => Some(unsub), - CallCreateExecutiveKind::ExecCreate(_, ref mut unsub) => Some(unsub), - CallCreateExecutiveKind::ResumeCreate(_, _, ref mut unsub) => Some(unsub), - CallCreateExecutiveKind::ResumeCall(_, _, ref mut unsub) => Some(unsub), - CallCreateExecutiveKind::Transfer(..) | CallCreateExecutiveKind::CallBuiltin(..) => None, - } - } - - fn check_static_flag(params: &ActionParams, static_flag: bool, is_create: bool) -> vm::Result<()> { - if is_create { - if static_flag { - return Err(vm::Error::MutableCallInStaticContext); - } - } else { - if (static_flag && - (params.call_type == CallType::StaticCall || params.call_type == CallType::Call)) && - params.value.value() > U256::zero() - { - return Err(vm::Error::MutableCallInStaticContext); - } - } - - Ok(()) - } - - fn check_eip684(params: &ActionParams, state: &State) -> vm::Result<()> { - if state.exists_and_has_code_or_nonce(¶ms.address)? { - return Err(vm::Error::OutOfGas); - } - - Ok(()) - } - - fn transfer_exec_balance(params: &ActionParams, schedule: &Schedule, state: &mut State, substate: &mut Substate) -> vm::Result<()> { - if let ActionValue::Transfer(val) = params.value { - state.transfer_balance(¶ms.sender, ¶ms.address, &val, substate.to_cleanup_mode(&schedule))?; - } - - Ok(()) - } - - fn transfer_exec_balance_and_init_contract(params: &ActionParams, schedule: &Schedule, state: &mut State, substate: &mut Substate) -> vm::Result<()> { - let nonce_offset = if schedule.no_empty {1} else {0}.into(); - let prev_bal = state.balance(¶ms.address)?; - if let ActionValue::Transfer(val) = params.value { - state.sub_balance(¶ms.sender, &val, &mut substate.to_cleanup_mode(&schedule))?; - state.new_contract(¶ms.address, val.saturating_add(prev_bal), nonce_offset)?; - } else { - state.new_contract(¶ms.address, prev_bal, nonce_offset)?; - } - - Ok(()) - } - - fn enact_result(result: &vm::Result, state: &mut State, substate: &mut Substate, un_substate: Substate) { - match *result { - Err(vm::Error::OutOfGas) - | Err(vm::Error::BadJumpDestination {..}) - | Err(vm::Error::BadInstruction {.. }) - | Err(vm::Error::StackUnderflow {..}) - | Err(vm::Error::BuiltIn {..}) - | Err(vm::Error::Wasm {..}) - | Err(vm::Error::OutOfStack {..}) - | Err(vm::Error::MutableCallInStaticContext) - | Err(vm::Error::OutOfBounds) - | Err(vm::Error::Reverted) - | Ok(FinalizationResult { apply_state: false, .. }) => { - state.revert_to_checkpoint(); - }, - Ok(_) | Err(vm::Error::Internal(_)) => { - state.discard_checkpoint(); - substate.accrue(un_substate); - } - } - } - - /// Creates `Externalities` from `Executive`. - fn as_externalities<'any, B: 'any + StateBackend, T, V>( - state: &'any mut State, - info: &'any EnvInfo, - machine: &'any Machine, - schedule: &'any Schedule, - depth: usize, - stack_depth: usize, - static_flag: bool, - origin_info: &'any OriginInfo, - substate: &'any mut Substate, - output: OutputPolicy, - tracer: &'any mut T, - vm_tracer: &'any mut V, - ) -> Externalities<'any, T, V, B> where T: Tracer, V: VMTracer { - Externalities::new(state, info, machine, schedule, depth, stack_depth, origin_info, substate, output, tracer, vm_tracer, static_flag) - } - - /// Execute the executive. If a sub-call/create action is required, a resume trap error is returned. The caller is - /// then expected to call `resume_call` or `resume_create` to continue the execution. - /// - /// Current-level tracing is expected to be handled by caller. - pub fn exec(mut self, state: &mut State, substate: &mut Substate, tracer: &mut T, vm_tracer: &mut V) -> ExecutiveTrapResult<'a, FinalizationResult> { - match self.kind { - CallCreateExecutiveKind::Transfer(ref params) => { - assert!(!self.is_create); - - let mut inner = || { - Self::check_static_flag(params, self.static_flag, self.is_create)?; - Self::transfer_exec_balance(params, self.schedule, state, substate)?; - - Ok(FinalizationResult { - gas_left: params.gas, - return_data: ReturnData::empty(), - apply_state: true, - }) - }; - - Ok(inner()) - }, - CallCreateExecutiveKind::CallBuiltin(ref params) => { - assert!(!self.is_create); - - let mut inner = || { - let builtin = self.machine.builtin(¶ms.code_address, self.info.number).expect("Builtin is_some is checked when creating this kind in new_call_raw; qed"); - - Self::check_static_flag(¶ms, self.static_flag, self.is_create)?; - state.checkpoint(); - Self::transfer_exec_balance(¶ms, self.schedule, state, substate)?; - - let default = []; - let data = if let Some(ref d) = params.data { d as &[u8] } else { &default as &[u8] }; - - let cost = builtin.cost(data); - if cost <= params.gas { - let mut builtin_out_buffer = Vec::new(); - let result = { - let mut builtin_output = BytesRef::Flexible(&mut builtin_out_buffer); - builtin.execute(data, &mut builtin_output) - }; - if let Err(e) = result { - state.revert_to_checkpoint(); - - Err(e.into()) - } else { - state.discard_checkpoint(); - - let out_len = builtin_out_buffer.len(); - Ok(FinalizationResult { - gas_left: params.gas - cost, - return_data: ReturnData::new(builtin_out_buffer, 0, out_len), - apply_state: true, - }) - } - } else { - // just drain the whole gas - state.revert_to_checkpoint(); - - Err(vm::Error::OutOfGas) - } - }; - - Ok(inner()) - }, - CallCreateExecutiveKind::ExecCall(params, mut unconfirmed_substate) => { - assert!(!self.is_create); - - { - let static_flag = self.static_flag; - let is_create = self.is_create; - let schedule = self.schedule; - - let mut pre_inner = || { - Self::check_static_flag(¶ms, static_flag, is_create)?; - state.checkpoint(); - Self::transfer_exec_balance(¶ms, schedule, state, substate)?; - Ok(()) - }; - - match pre_inner() { - Ok(()) => (), - Err(err) => return Ok(Err(err)), - } - } - - let origin_info = OriginInfo::from(¶ms); - let exec = self.factory.create(params, self.schedule, self.depth); - - let out = { - let mut ext = Self::as_externalities(state, self.info, self.machine, self.schedule, self.depth, self.stack_depth, self.static_flag, &origin_info, &mut unconfirmed_substate, OutputPolicy::Return, tracer, vm_tracer); - match exec.exec(&mut ext) { - Ok(val) => Ok(val.finalize(ext)), - Err(err) => Err(err), - } - }; - - let res = match out { - Ok(val) => val, - Err(TrapError::Call(subparams, resume)) => { - self.kind = CallCreateExecutiveKind::ResumeCall(origin_info, resume, unconfirmed_substate); - return Err(TrapError::Call(subparams, self)); - }, - Err(TrapError::Create(subparams, address, resume)) => { - self.kind = CallCreateExecutiveKind::ResumeCreate(origin_info, resume, unconfirmed_substate); - return Err(TrapError::Create(subparams, address, self)); - }, - }; - - Self::enact_result(&res, state, substate, unconfirmed_substate); - Ok(res) - }, - CallCreateExecutiveKind::ExecCreate(params, mut unconfirmed_substate) => { - assert!(self.is_create); - - { - let static_flag = self.static_flag; - let is_create = self.is_create; - let schedule = self.schedule; - - let mut pre_inner = || { - Self::check_eip684(¶ms, state)?; - Self::check_static_flag(¶ms, static_flag, is_create)?; - state.checkpoint(); - Self::transfer_exec_balance_and_init_contract(¶ms, schedule, state, substate)?; - Ok(()) - }; - - match pre_inner() { - Ok(()) => (), - Err(err) => return Ok(Err(err)), - } - } - - let origin_info = OriginInfo::from(¶ms); - let exec = self.factory.create(params, self.schedule, self.depth); - - let out = { - let mut ext = Self::as_externalities(state, self.info, self.machine, self.schedule, self.depth, self.stack_depth, self.static_flag, &origin_info, &mut unconfirmed_substate, OutputPolicy::InitContract, tracer, vm_tracer); - match exec.exec(&mut ext) { - Ok(val) => Ok(val.finalize(ext)), - Err(err) => Err(err), - } - }; - - let res = match out { - Ok(val) => val, - Err(TrapError::Call(subparams, resume)) => { - self.kind = CallCreateExecutiveKind::ResumeCall(origin_info, resume, unconfirmed_substate); - return Err(TrapError::Call(subparams, self)); - }, - Err(TrapError::Create(subparams, address, resume)) => { - self.kind = CallCreateExecutiveKind::ResumeCreate(origin_info, resume, unconfirmed_substate); - return Err(TrapError::Create(subparams, address, self)); - }, - }; - - Self::enact_result(&res, state, substate, unconfirmed_substate); - Ok(res) - }, - CallCreateExecutiveKind::ResumeCall(..) | CallCreateExecutiveKind::ResumeCreate(..) => panic!("This executive has already been executed once."), - } - } - - /// Resume execution from a call trap previsouly trapped by `exec`. - /// - /// Current-level tracing is expected to be handled by caller. - pub fn resume_call(mut self, result: vm::MessageCallResult, state: &mut State, substate: &mut Substate, tracer: &mut T, vm_tracer: &mut V) -> ExecutiveTrapResult<'a, FinalizationResult> { - match self.kind { - CallCreateExecutiveKind::ResumeCall(origin_info, resume, mut unconfirmed_substate) => { - let out = { - let exec = resume.resume_call(result); - - let mut ext = Self::as_externalities(state, self.info, self.machine, self.schedule, self.depth, self.stack_depth, self.static_flag, &origin_info, &mut unconfirmed_substate, if self.is_create { OutputPolicy::InitContract } else { OutputPolicy::Return }, tracer, vm_tracer); - match exec.exec(&mut ext) { - Ok(val) => Ok(val.finalize(ext)), - Err(err) => Err(err), - } - }; - - let res = match out { - Ok(val) => val, - Err(TrapError::Call(subparams, resume)) => { - self.kind = CallCreateExecutiveKind::ResumeCall(origin_info, resume, unconfirmed_substate); - return Err(TrapError::Call(subparams, self)); - }, - Err(TrapError::Create(subparams, address, resume)) => { - self.kind = CallCreateExecutiveKind::ResumeCreate(origin_info, resume, unconfirmed_substate); - return Err(TrapError::Create(subparams, address, self)); - }, - }; - - Self::enact_result(&res, state, substate, unconfirmed_substate); - Ok(res) - }, - CallCreateExecutiveKind::ResumeCreate(..) => - panic!("Resumable as create, but called resume_call"), - CallCreateExecutiveKind::Transfer(..) | CallCreateExecutiveKind::CallBuiltin(..) | - CallCreateExecutiveKind::ExecCall(..) | CallCreateExecutiveKind::ExecCreate(..) => - panic!("Not resumable"), - } - } - - /// Resume execution from a create trap previsouly trapped by `exec`. - /// - /// Current-level tracing is expected to be handled by caller. - pub fn resume_create(mut self, result: vm::ContractCreateResult, state: &mut State, substate: &mut Substate, tracer: &mut T, vm_tracer: &mut V) -> ExecutiveTrapResult<'a, FinalizationResult> { - match self.kind { - CallCreateExecutiveKind::ResumeCreate(origin_info, resume, mut unconfirmed_substate) => { - let out = { - let exec = resume.resume_create(result); - - let mut ext = Self::as_externalities(state, self.info, self.machine, self.schedule, self.depth, self.stack_depth, self.static_flag, &origin_info, &mut unconfirmed_substate, if self.is_create { OutputPolicy::InitContract } else { OutputPolicy::Return }, tracer, vm_tracer); - match exec.exec(&mut ext) { - Ok(val) => Ok(val.finalize(ext)), - Err(err) => Err(err), - } - }; - - let res = match out { - Ok(val) => val, - Err(TrapError::Call(subparams, resume)) => { - self.kind = CallCreateExecutiveKind::ResumeCall(origin_info, resume, unconfirmed_substate); - return Err(TrapError::Call(subparams, self)); - }, - Err(TrapError::Create(subparams, address, resume)) => { - self.kind = CallCreateExecutiveKind::ResumeCreate(origin_info, resume, unconfirmed_substate); - return Err(TrapError::Create(subparams, address, self)); - }, - }; - - Self::enact_result(&res, state, substate, unconfirmed_substate); - Ok(res) - }, - CallCreateExecutiveKind::ResumeCall(..) => - panic!("Resumable as call, but called resume_create"), - CallCreateExecutiveKind::Transfer(..) | CallCreateExecutiveKind::CallBuiltin(..) | - CallCreateExecutiveKind::ExecCall(..) | CallCreateExecutiveKind::ExecCreate(..) => - panic!("Not resumable"), - } - } - - /// Execute and consume the current executive. This function handles resume traps and sub-level tracing. The caller is expected to handle current-level tracing. - pub fn consume(self, state: &mut State, top_substate: &mut Substate, tracer: &mut T, vm_tracer: &mut V) -> vm::Result { - let mut last_res = Some((false, self.gas, self.exec(state, top_substate, tracer, vm_tracer))); - - let mut callstack: Vec<(Option
, CallCreateExecutive<'a>)> = Vec::new(); - loop { - match last_res { + /// Create a new call executive using raw data. + pub fn new_call_raw( + params: ActionParams, + info: &'a EnvInfo, + machine: &'a Machine, + schedule: &'a Schedule, + factory: &'a VmFactory, + depth: usize, + stack_depth: usize, + parent_static_flag: bool, + ) -> Self { + trace!( + "Executive::call(params={:?}) self.env_info={:?}, parent_static={}", + params, + info, + parent_static_flag + ); + + let gas = params.gas; + let static_flag = parent_static_flag || params.call_type == CallType::StaticCall; + + // if destination is builtin, try to execute it + let kind = if let Some(builtin) = machine.builtin(¶ms.code_address, info.number) { + // Engines aren't supposed to return builtins until activation, but + // prefer to fail rather than silently break consensus. + if !builtin.is_active(info.number) { + panic!( + "Consensus failure: engine implementation prematurely enabled built-in at {}", + params.code_address + ); + } + + CallCreateExecutiveKind::CallBuiltin(params) + } else { + if params.code.is_some() { + CallCreateExecutiveKind::ExecCall(params, Substate::new()) + } else { + CallCreateExecutiveKind::Transfer(params) + } + }; + + Self { + info, + machine, + schedule, + factory, + depth, + stack_depth, + static_flag, + kind, + gas, + is_create: false, + } + } + + /// Create a new create executive using raw data. + pub fn new_create_raw( + params: ActionParams, + info: &'a EnvInfo, + machine: &'a Machine, + schedule: &'a Schedule, + factory: &'a VmFactory, + depth: usize, + stack_depth: usize, + static_flag: bool, + ) -> Self { + trace!( + "Executive::create(params={:?}) self.env_info={:?}, static={}", + params, + info, + static_flag + ); + + let gas = params.gas; + + let kind = CallCreateExecutiveKind::ExecCreate(params, Substate::new()); + + Self { + info, + machine, + schedule, + factory, + depth, + stack_depth, + static_flag, + kind, + gas, + is_create: true, + } + } + + /// If this executive contains an unconfirmed substate, returns a mutable reference to it. + pub fn unconfirmed_substate(&mut self) -> Option<&mut Substate> { + match self.kind { + CallCreateExecutiveKind::ExecCall(_, ref mut unsub) => Some(unsub), + CallCreateExecutiveKind::ExecCreate(_, ref mut unsub) => Some(unsub), + CallCreateExecutiveKind::ResumeCreate(_, _, ref mut unsub) => Some(unsub), + CallCreateExecutiveKind::ResumeCall(_, _, ref mut unsub) => Some(unsub), + CallCreateExecutiveKind::Transfer(..) | CallCreateExecutiveKind::CallBuiltin(..) => { + None + } + } + } + + fn check_static_flag( + params: &ActionParams, + static_flag: bool, + is_create: bool, + ) -> vm::Result<()> { + if is_create { + if static_flag { + return Err(vm::Error::MutableCallInStaticContext); + } + } else { + if (static_flag + && (params.call_type == CallType::StaticCall || params.call_type == CallType::Call)) + && params.value.value() > U256::zero() + { + return Err(vm::Error::MutableCallInStaticContext); + } + } + + Ok(()) + } + + fn check_eip684( + params: &ActionParams, + state: &State, + ) -> vm::Result<()> { + if state.exists_and_has_code_or_nonce(¶ms.address)? { + return Err(vm::Error::OutOfGas); + } + + Ok(()) + } + + fn transfer_exec_balance( + params: &ActionParams, + schedule: &Schedule, + state: &mut State, + substate: &mut Substate, + ) -> vm::Result<()> { + if let ActionValue::Transfer(val) = params.value { + state.transfer_balance( + ¶ms.sender, + ¶ms.address, + &val, + substate.to_cleanup_mode(&schedule), + )?; + } + + Ok(()) + } + + fn transfer_exec_balance_and_init_contract( + params: &ActionParams, + schedule: &Schedule, + state: &mut State, + substate: &mut Substate, + ) -> vm::Result<()> { + let nonce_offset = if schedule.no_empty { 1 } else { 0 }.into(); + let prev_bal = state.balance(¶ms.address)?; + if let ActionValue::Transfer(val) = params.value { + state.sub_balance( + ¶ms.sender, + &val, + &mut substate.to_cleanup_mode(&schedule), + )?; + state.new_contract(¶ms.address, val.saturating_add(prev_bal), nonce_offset)?; + } else { + state.new_contract(¶ms.address, prev_bal, nonce_offset)?; + } + + Ok(()) + } + + fn enact_result( + result: &vm::Result, + state: &mut State, + substate: &mut Substate, + un_substate: Substate, + ) { + match *result { + Err(vm::Error::OutOfGas) + | Err(vm::Error::BadJumpDestination { .. }) + | Err(vm::Error::BadInstruction { .. }) + | Err(vm::Error::StackUnderflow { .. }) + | Err(vm::Error::BuiltIn { .. }) + | Err(vm::Error::Wasm { .. }) + | Err(vm::Error::OutOfStack { .. }) + | Err(vm::Error::MutableCallInStaticContext) + | Err(vm::Error::OutOfBounds) + | Err(vm::Error::Reverted) + | Err(vm::Error::SubStackUnderflow { .. }) + | Err(vm::Error::OutOfSubStack { .. }) + | Err(vm::Error::InvalidSubEntry) + | Ok(FinalizationResult { + apply_state: false, .. + }) => { + if let Some(addr) = UNPRUNABLE_PRECOMPILE_ADDRESS { + if un_substate.touched.contains(&addr) { + substate.touched.insert(addr); + } + } + state.revert_to_checkpoint(); + } + Ok(_) | Err(vm::Error::Internal(_)) => { + state.discard_checkpoint(); + substate.accrue(un_substate); + } + } + } + + /// Creates `Externalities` from `Executive`. + fn as_externalities<'any, B: 'any + StateBackend, T, V>( + state: &'any mut State, + info: &'any EnvInfo, + machine: &'any Machine, + schedule: &'any Schedule, + depth: usize, + stack_depth: usize, + static_flag: bool, + origin_info: &'any OriginInfo, + substate: &'any mut Substate, + output: OutputPolicy, + tracer: &'any mut T, + vm_tracer: &'any mut V, + ) -> Externalities<'any, T, V, B> + where + T: Tracer, + V: VMTracer, + { + Externalities::new( + state, + info, + machine, + schedule, + depth, + stack_depth, + origin_info, + substate, + output, + tracer, + vm_tracer, + static_flag, + ) + } + + /// Execute the executive. If a sub-call/create action is required, a resume trap error is returned. The caller is + /// then expected to call `resume_call` or `resume_create` to continue the execution. + /// + /// Current-level tracing is expected to be handled by caller. + pub fn exec( + mut self, + state: &mut State, + substate: &mut Substate, + tracer: &mut T, + vm_tracer: &mut V, + ) -> ExecutiveTrapResult<'a, FinalizationResult> { + match self.kind { + CallCreateExecutiveKind::Transfer(ref params) => { + assert!(!self.is_create); + + let mut inner = || { + Self::check_static_flag(params, self.static_flag, self.is_create)?; + Self::transfer_exec_balance(params, self.schedule, state, substate)?; + + Ok(FinalizationResult { + gas_left: params.gas, + return_data: ReturnData::empty(), + apply_state: true, + }) + }; + + Ok(inner()) + } + CallCreateExecutiveKind::CallBuiltin(ref params) => { + assert!(!self.is_create); + + let mut inner = || { + let builtin = self.machine.builtin(¶ms.code_address, self.info.number).expect("Builtin is_some is checked when creating this kind in new_call_raw; qed"); + + Self::check_static_flag(¶ms, self.static_flag, self.is_create)?; + state.checkpoint(); + Self::transfer_exec_balance(¶ms, self.schedule, state, substate)?; + + let default = []; + let data = if let Some(ref d) = params.data { + d as &[u8] + } else { + &default as &[u8] + }; + + // NOTE(niklasad1): block number is used by `builtin alt_bn128 ops` to enable eip1108 + let cost = builtin.cost(data, self.info.number); + if cost <= params.gas { + let mut builtin_out_buffer = Vec::new(); + let result = { + let mut builtin_output = BytesRef::Flexible(&mut builtin_out_buffer); + builtin.execute(data, &mut builtin_output) + }; + if let Err(e) = result { + state.revert_to_checkpoint(); + + Err(vm::Error::BuiltIn(e)) + } else { + state.discard_checkpoint(); + + let out_len = builtin_out_buffer.len(); + Ok(FinalizationResult { + gas_left: params.gas - cost, + return_data: ReturnData::new(builtin_out_buffer, 0, out_len), + apply_state: true, + }) + } + } else { + // We need balance > 0 in precompiles to be EIP161 compliant, see PR#11597. + // Since RIPEMD160 was removed in mainnet block #2686351, this is activated only in + // tests to check this specific irregular state transition. + if let Some(unprunable_addr) = UNPRUNABLE_PRECOMPILE_ADDRESS { + if unprunable_addr != params.code_address + && state.balance(¶ms.code_address)?.is_zero() + { + substate.touched.remove(¶ms.code_address); + } + } + // just drain the whole gas + state.revert_to_checkpoint(); + Err(vm::Error::OutOfGas) + } + }; + + Ok(inner()) + } + CallCreateExecutiveKind::ExecCall(params, mut unconfirmed_substate) => { + assert!(!self.is_create); + + { + let static_flag = self.static_flag; + let is_create = self.is_create; + let schedule = self.schedule; + + let mut pre_inner = || { + Self::check_static_flag(¶ms, static_flag, is_create)?; + state.checkpoint(); + Self::transfer_exec_balance(¶ms, schedule, state, substate)?; + Ok(()) + }; + + match pre_inner() { + Ok(()) => (), + Err(err) => return Ok(Err(err)), + } + } + + let origin_info = OriginInfo::from(¶ms); + let exec = self.factory.create(params, self.schedule, self.depth); + + let out = { + let mut ext = Self::as_externalities( + state, + self.info, + self.machine, + self.schedule, + self.depth, + self.stack_depth, + self.static_flag, + &origin_info, + &mut unconfirmed_substate, + OutputPolicy::Return, + tracer, + vm_tracer, + ); + match exec.exec(&mut ext) { + Ok(val) => Ok(val.finalize(ext)), + Err(err) => Err(err), + } + }; + + let res = match out { + Ok(val) => val, + Err(TrapError::Call(subparams, resume)) => { + self.kind = CallCreateExecutiveKind::ResumeCall( + origin_info, + resume, + unconfirmed_substate, + ); + return Err(TrapError::Call(subparams, self)); + } + Err(TrapError::Create(subparams, address, resume)) => { + self.kind = CallCreateExecutiveKind::ResumeCreate( + origin_info, + resume, + unconfirmed_substate, + ); + return Err(TrapError::Create(subparams, address, self)); + } + }; + + Self::enact_result(&res, state, substate, unconfirmed_substate); + Ok(res) + } + CallCreateExecutiveKind::ExecCreate(params, mut unconfirmed_substate) => { + assert!(self.is_create); + + { + let static_flag = self.static_flag; + let is_create = self.is_create; + let schedule = self.schedule; + + let mut pre_inner = || { + Self::check_eip684(¶ms, state)?; + Self::check_static_flag(¶ms, static_flag, is_create)?; + state.checkpoint(); + Self::transfer_exec_balance_and_init_contract( + ¶ms, schedule, state, substate, + )?; + Ok(()) + }; + + match pre_inner() { + Ok(()) => (), + Err(err) => return Ok(Err(err)), + } + } + + let origin_info = OriginInfo::from(¶ms); + let exec = self.factory.create(params, self.schedule, self.depth); + + let out = { + let mut ext = Self::as_externalities( + state, + self.info, + self.machine, + self.schedule, + self.depth, + self.stack_depth, + self.static_flag, + &origin_info, + &mut unconfirmed_substate, + OutputPolicy::InitContract, + tracer, + vm_tracer, + ); + match exec.exec(&mut ext) { + Ok(val) => Ok(val.finalize(ext)), + Err(err) => Err(err), + } + }; + + let res = match out { + Ok(val) => val, + Err(TrapError::Call(subparams, resume)) => { + self.kind = CallCreateExecutiveKind::ResumeCall( + origin_info, + resume, + unconfirmed_substate, + ); + return Err(TrapError::Call(subparams, self)); + } + Err(TrapError::Create(subparams, address, resume)) => { + self.kind = CallCreateExecutiveKind::ResumeCreate( + origin_info, + resume, + unconfirmed_substate, + ); + return Err(TrapError::Create(subparams, address, self)); + } + }; + + Self::enact_result(&res, state, substate, unconfirmed_substate); + Ok(res) + } + CallCreateExecutiveKind::ResumeCall(..) | CallCreateExecutiveKind::ResumeCreate(..) => { + panic!("This executive has already been executed once.") + } + } + } + + /// Resume execution from a call trap previsouly trapped by `exec`. + /// + /// Current-level tracing is expected to be handled by caller. + pub fn resume_call( + mut self, + result: vm::MessageCallResult, + state: &mut State, + substate: &mut Substate, + tracer: &mut T, + vm_tracer: &mut V, + ) -> ExecutiveTrapResult<'a, FinalizationResult> { + match self.kind { + CallCreateExecutiveKind::ResumeCall(origin_info, resume, mut unconfirmed_substate) => { + let out = { + let exec = resume.resume_call(result); + + let mut ext = Self::as_externalities( + state, + self.info, + self.machine, + self.schedule, + self.depth, + self.stack_depth, + self.static_flag, + &origin_info, + &mut unconfirmed_substate, + if self.is_create { + OutputPolicy::InitContract + } else { + OutputPolicy::Return + }, + tracer, + vm_tracer, + ); + match exec.exec(&mut ext) { + Ok(val) => Ok(val.finalize(ext)), + Err(err) => Err(err), + } + }; + + let res = match out { + Ok(val) => val, + Err(TrapError::Call(subparams, resume)) => { + self.kind = CallCreateExecutiveKind::ResumeCall( + origin_info, + resume, + unconfirmed_substate, + ); + return Err(TrapError::Call(subparams, self)); + } + Err(TrapError::Create(subparams, address, resume)) => { + self.kind = CallCreateExecutiveKind::ResumeCreate( + origin_info, + resume, + unconfirmed_substate, + ); + return Err(TrapError::Create(subparams, address, self)); + } + }; + + Self::enact_result(&res, state, substate, unconfirmed_substate); + Ok(res) + } + CallCreateExecutiveKind::ResumeCreate(..) => { + panic!("Resumable as create, but called resume_call") + } + CallCreateExecutiveKind::Transfer(..) + | CallCreateExecutiveKind::CallBuiltin(..) + | CallCreateExecutiveKind::ExecCall(..) + | CallCreateExecutiveKind::ExecCreate(..) => panic!("Not resumable"), + } + } + + /// Resume execution from a create trap previsouly trapped by `exec`. + /// + /// Current-level tracing is expected to be handled by caller. + pub fn resume_create( + mut self, + result: vm::ContractCreateResult, + state: &mut State, + substate: &mut Substate, + tracer: &mut T, + vm_tracer: &mut V, + ) -> ExecutiveTrapResult<'a, FinalizationResult> { + match self.kind { + CallCreateExecutiveKind::ResumeCreate( + origin_info, + resume, + mut unconfirmed_substate, + ) => { + let out = { + let exec = resume.resume_create(result); + + let mut ext = Self::as_externalities( + state, + self.info, + self.machine, + self.schedule, + self.depth, + self.stack_depth, + self.static_flag, + &origin_info, + &mut unconfirmed_substate, + if self.is_create { + OutputPolicy::InitContract + } else { + OutputPolicy::Return + }, + tracer, + vm_tracer, + ); + match exec.exec(&mut ext) { + Ok(val) => Ok(val.finalize(ext)), + Err(err) => Err(err), + } + }; + + let res = match out { + Ok(val) => val, + Err(TrapError::Call(subparams, resume)) => { + self.kind = CallCreateExecutiveKind::ResumeCall( + origin_info, + resume, + unconfirmed_substate, + ); + return Err(TrapError::Call(subparams, self)); + } + Err(TrapError::Create(subparams, address, resume)) => { + self.kind = CallCreateExecutiveKind::ResumeCreate( + origin_info, + resume, + unconfirmed_substate, + ); + return Err(TrapError::Create(subparams, address, self)); + } + }; + + Self::enact_result(&res, state, substate, unconfirmed_substate); + Ok(res) + } + CallCreateExecutiveKind::ResumeCall(..) => { + panic!("Resumable as call, but called resume_create") + } + CallCreateExecutiveKind::Transfer(..) + | CallCreateExecutiveKind::CallBuiltin(..) + | CallCreateExecutiveKind::ExecCall(..) + | CallCreateExecutiveKind::ExecCreate(..) => panic!("Not resumable"), + } + } + + /// Execute and consume the current executive. This function handles resume traps and sub-level tracing. The caller is expected to handle current-level tracing. + pub fn consume( + self, + state: &mut State, + top_substate: &mut Substate, + tracer: &mut T, + vm_tracer: &mut V, + ) -> vm::Result { + let mut last_res = Some(( + false, + self.gas, + self.exec(state, top_substate, tracer, vm_tracer), + )); + + let mut callstack: Vec<(Option
, CallCreateExecutive<'a>)> = Vec::new(); + loop { + match last_res { None => { match callstack.pop() { Some((_, exec)) => { @@ -739,718 +1027,940 @@ impl<'a> CallCreateExecutive<'a> { last_res = None; }, } - } - } + } + } } /// Transaction executor. pub struct Executive<'a, B: 'a> { - state: &'a mut State, - info: &'a EnvInfo, - machine: &'a Machine, - schedule: &'a Schedule, - depth: usize, - static_flag: bool, + state: &'a mut State, + info: &'a EnvInfo, + machine: &'a Machine, + schedule: &'a Schedule, + depth: usize, + static_flag: bool, } impl<'a, B: 'a + StateBackend> Executive<'a, B> { - /// Basic constructor. - pub fn new(state: &'a mut State, info: &'a EnvInfo, machine: &'a Machine, schedule: &'a Schedule) -> Self { - Executive { - state: state, - info: info, - machine: machine, - schedule: schedule, - depth: 0, - static_flag: false, - } - } - - /// Populates executive from parent properties. Increments executive depth. - pub fn from_parent(state: &'a mut State, info: &'a EnvInfo, machine: &'a Machine, schedule: &'a Schedule, parent_depth: usize, static_flag: bool) -> Self { - Executive { - state: state, - info: info, - machine: machine, - schedule: schedule, - depth: parent_depth + 1, - static_flag: static_flag, - } - } - - /// This function should be used to execute transaction. - pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) - -> Result, ExecutionError> where T: Tracer, V: VMTracer, - { - self.transact_with_tracer(t, options.check_nonce, options.output_from_init_contract, options.tracer, options.vm_tracer) - } - - /// Execute a transaction in a "virtual" context. - /// This will ensure the caller has enough balance to execute the desired transaction. - /// Used for extra-block executions for things like consensus contracts and RPCs - pub fn transact_virtual(&'a mut self, t: &SignedTransaction, options: TransactOptions) - -> Result, ExecutionError> where T: Tracer, V: VMTracer, - { - let sender = t.sender(); - let balance = self.state.balance(&sender)?; - let needed_balance = t.value.saturating_add(t.gas.saturating_mul(t.gas_price)); - if balance < needed_balance { - // give the sender a sufficient balance - self.state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty)?; - } - - self.transact(t, options) - } - - /// Execute transaction/call with tracing enabled - fn transact_with_tracer( - &'a mut self, - t: &SignedTransaction, - check_nonce: bool, - output_from_create: bool, - mut tracer: T, - mut vm_tracer: V - ) -> Result, ExecutionError> where T: Tracer, V: VMTracer { - let sender = t.sender(); - let nonce = self.state.nonce(&sender)?; - - let schedule = self.schedule; - let base_gas_required = U256::from(t.gas_required(&schedule)); - - if t.gas < base_gas_required { - return Err(ExecutionError::NotEnoughBaseGas { required: base_gas_required, got: t.gas }); - } - - if !t.is_unsigned() && check_nonce && schedule.kill_dust != CleanDustMode::Off && !self.state.exists(&sender)? { - return Err(ExecutionError::SenderMustExist); - } - - let init_gas = t.gas - base_gas_required; - - // validate transaction nonce - if check_nonce && t.nonce != nonce { - return Err(ExecutionError::InvalidNonce { expected: nonce, got: t.nonce }); - } - - // validate if transaction fits into given block - if self.info.gas_used + t.gas > self.info.gas_limit { - return Err(ExecutionError::BlockGasLimitReached { - gas_limit: self.info.gas_limit, - gas_used: self.info.gas_used, - gas: t.gas - }); - } - - // TODO: we might need bigints here, or at least check overflows. - let balance = self.state.balance(&sender)?; - let gas_cost = t.gas.full_mul(t.gas_price); - let total_cost = U512::from(t.value) + gas_cost; - - // avoid unaffordable transactions - let balance512 = U512::from(balance); - if balance512 < total_cost { - return Err(ExecutionError::NotEnoughCash { required: total_cost, got: balance512 }); - } - - let mut substate = Substate::new(); - - // NOTE: there can be no invalid transactions from this point. - if !schedule.keep_unsigned_nonce || !t.is_unsigned() { - self.state.inc_nonce(&sender)?; - } - self.state.sub_balance(&sender, &U256::from(gas_cost), &mut substate.to_cleanup_mode(&schedule))?; - - let (result, output) = match t.action { - Action::Create => { - let (new_address, code_hash) = contract_address(self.machine.create_address_scheme(self.info.number), &sender, &nonce, &t.data); - let params = ActionParams { - code_address: new_address.clone(), - code_hash: code_hash, - address: new_address, - sender: sender.clone(), - origin: sender.clone(), - gas: init_gas, - gas_price: t.gas_price, - value: ActionValue::Transfer(t.value), - code: Some(Arc::new(t.data.clone())), - data: None, - call_type: CallType::None, - params_type: vm::ParamsType::Embedded, - }; - let res = self.create(params, &mut substate, &mut tracer, &mut vm_tracer); - let out = match &res { - Ok(res) if output_from_create => res.return_data.to_vec(), - _ => Vec::new(), - }; - (res, out) - }, - Action::Call(ref address) => { - let params = ActionParams { - code_address: address.clone(), - address: address.clone(), - sender: sender.clone(), - origin: sender.clone(), - gas: init_gas, - gas_price: t.gas_price, - value: ActionValue::Transfer(t.value), - code: self.state.code(address)?, - code_hash: self.state.code_hash(address)?, - data: Some(t.data.clone()), - call_type: CallType::Call, - params_type: vm::ParamsType::Separate, - }; - let res = self.call(params, &mut substate, &mut tracer, &mut vm_tracer); - let out = match &res { - Ok(res) => res.return_data.to_vec(), - _ => Vec::new(), - }; - (res, out) - } - }; - - // finalize here! - Ok(self.finalize(t, substate, result, output, tracer.drain(), vm_tracer.drain())?) - } - - /// Calls contract function with given contract params and stack depth. - /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). - /// Modifies the substate and the output. - /// Returns either gas_left or `vm::Error`. - pub fn call_with_stack_depth( - &mut self, - params: ActionParams, - substate: &mut Substate, - stack_depth: usize, - tracer: &mut T, - vm_tracer: &mut V - ) -> vm::Result where T: Tracer, V: VMTracer { - tracer.prepare_trace_call(¶ms, self.depth, self.machine.builtin(¶ms.address, self.info.number).is_some()); - vm_tracer.prepare_subtrace(params.code.as_ref().map_or_else(|| &[] as &[u8], |d| &*d as &[u8])); - - let gas = params.gas; - - let vm_factory = self.state.vm_factory(); - let result = CallCreateExecutive::new_call_raw( - params, - self.info, - self.machine, - self.schedule, - &vm_factory, - self.depth, - stack_depth, - self.static_flag - ).consume(self.state, substate, tracer, vm_tracer); - - match result { - Ok(ref val) if val.apply_state => { - tracer.done_trace_call( - gas - val.gas_left, - &val.return_data, - ); - }, - Ok(_) => { - tracer.done_trace_failed(&vm::Error::Reverted); - }, - Err(ref err) => { - tracer.done_trace_failed(err); - }, - } - vm_tracer.done_subtrace(); - - result - } - - /// Calls contract function with given contract params, if the stack depth is above a threshold, create a new thread - /// to execute it. - pub fn call_with_crossbeam( - &mut self, - params: ActionParams, - substate: &mut Substate, - stack_depth: usize, - tracer: &mut T, - vm_tracer: &mut V - ) -> vm::Result where T: Tracer, V: VMTracer { - let local_stack_size = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get()); - let depth_threshold = local_stack_size.saturating_sub(STACK_SIZE_ENTRY_OVERHEAD) / STACK_SIZE_PER_DEPTH; - - if stack_depth != depth_threshold { - self.call_with_stack_depth(params, substate, stack_depth, tracer, vm_tracer) - } else { - crossbeam::scope(|scope| { - scope.builder().stack_size(::std::cmp::max(self.schedule.max_depth.saturating_sub(depth_threshold) * STACK_SIZE_PER_DEPTH, local_stack_size)).spawn(move || { - self.call_with_stack_depth(params, substate, stack_depth, tracer, vm_tracer) - }).expect("Sub-thread creation cannot fail; the host might run out of resources; qed") - }).join().expect("Sub-thread never panics; qed") - } - } - - /// Calls contract function with given contract params. - pub fn call( - &mut self, - params: ActionParams, - substate: &mut Substate, - tracer: &mut T, - vm_tracer: &mut V - ) -> vm::Result where T: Tracer, V: VMTracer { - self.call_with_stack_depth(params, substate, 0, tracer, vm_tracer) - } - - /// Creates contract with given contract params and stack depth. - /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). - /// Modifies the substate. - pub fn create_with_stack_depth( - &mut self, - params: ActionParams, - substate: &mut Substate, - stack_depth: usize, - tracer: &mut T, - vm_tracer: &mut V, - ) -> vm::Result where T: Tracer, V: VMTracer { - tracer.prepare_trace_create(¶ms); - vm_tracer.prepare_subtrace(params.code.as_ref().map_or_else(|| &[] as &[u8], |d| &*d as &[u8])); - - let address = params.address; - let gas = params.gas; - - let vm_factory = self.state.vm_factory(); - let result = CallCreateExecutive::new_create_raw( - params, - self.info, - self.machine, - self.schedule, - &vm_factory, - self.depth, - stack_depth, - self.static_flag - ).consume(self.state, substate, tracer, vm_tracer); - - match result { - Ok(ref val) if val.apply_state => { - tracer.done_trace_create( - gas - val.gas_left, - &val.return_data, - address, - ); - }, - Ok(_) => { - tracer.done_trace_failed(&vm::Error::Reverted); - }, - Err(ref err) => { - tracer.done_trace_failed(err); - }, - } - vm_tracer.done_subtrace(); - - result - } - - /// Creates contract with given contract params, if the stack depth is above a threshold, create a new thread to - /// execute it. - pub fn create_with_crossbeam( - &mut self, - params: ActionParams, - substate: &mut Substate, - stack_depth: usize, - tracer: &mut T, - vm_tracer: &mut V, - ) -> vm::Result where T: Tracer, V: VMTracer { - let local_stack_size = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get()); - let depth_threshold = local_stack_size.saturating_sub(STACK_SIZE_ENTRY_OVERHEAD) / STACK_SIZE_PER_DEPTH; - - if stack_depth != depth_threshold { - self.create_with_stack_depth(params, substate, stack_depth, tracer, vm_tracer) - } else { - crossbeam::scope(|scope| { - scope.builder().stack_size(::std::cmp::max(self.schedule.max_depth.saturating_sub(depth_threshold) * STACK_SIZE_PER_DEPTH, local_stack_size)).spawn(move || { - self.create_with_stack_depth(params, substate, stack_depth, tracer, vm_tracer) - }).expect("Sub-thread creation cannot fail; the host might run out of resources; qed") - }).join().expect("Sub-thread never panics; qed") - } - } - - /// Creates contract with given contract params. - pub fn create( - &mut self, - params: ActionParams, - substate: &mut Substate, - tracer: &mut T, - vm_tracer: &mut V, - ) -> vm::Result where T: Tracer, V: VMTracer { - self.create_with_stack_depth(params, substate, 0, tracer, vm_tracer) - } - - /// Finalizes the transaction (does refunds and suicides). - fn finalize( - &mut self, - t: &SignedTransaction, - mut substate: Substate, - result: vm::Result, - output: Bytes, - trace: Vec, - vm_trace: Option - ) -> Result, ExecutionError> { - let schedule = self.schedule; - - // refunds from SSTORE nonzero -> zero - assert!(substate.sstore_clears_refund >= 0, "On transaction level, sstore clears refund cannot go below zero."); - let sstore_refunds = U256::from(substate.sstore_clears_refund as u64); - // refunds from contract suicides - let suicide_refunds = U256::from(schedule.suicide_refund_gas) * U256::from(substate.suicides.len()); - let refunds_bound = sstore_refunds + suicide_refunds; - - // real ammount to refund - let gas_left_prerefund = match result { Ok(FinalizationResult{ gas_left, .. }) => gas_left, _ => 0.into() }; - let refunded = cmp::min(refunds_bound, (t.gas - gas_left_prerefund) >> 1); - let gas_left = gas_left_prerefund + refunded; - - let gas_used = t.gas.saturating_sub(gas_left); - let (refund_value, overflow_1) = gas_left.overflowing_mul(t.gas_price); - let (fees_value, overflow_2) = gas_used.overflowing_mul(t.gas_price); - if overflow_1 || overflow_2 { - return Err(ExecutionError::TransactionMalformed("U256 Overflow".to_string())); - } - - - trace!("exec::finalize: t.gas={}, sstore_refunds={}, suicide_refunds={}, refunds_bound={}, gas_left_prerefund={}, refunded={}, gas_left={}, gas_used={}, refund_value={}, fees_value={}\n", + /// Basic constructor. + pub fn new( + state: &'a mut State, + info: &'a EnvInfo, + machine: &'a Machine, + schedule: &'a Schedule, + ) -> Self { + Executive { + state: state, + info: info, + machine: machine, + schedule: schedule, + depth: 0, + static_flag: false, + } + } + + /// Populates executive from parent properties. Increments executive depth. + pub fn from_parent( + state: &'a mut State, + info: &'a EnvInfo, + machine: &'a Machine, + schedule: &'a Schedule, + parent_depth: usize, + static_flag: bool, + ) -> Self { + Executive { + state: state, + info: info, + machine: machine, + schedule: schedule, + depth: parent_depth + 1, + static_flag: static_flag, + } + } + + /// This function should be used to execute transaction. + pub fn transact( + &'a mut self, + t: &SignedTransaction, + options: TransactOptions, + ) -> Result, ExecutionError> + where + T: Tracer, + V: VMTracer, + { + self.transact_with_tracer( + t, + options.check_nonce, + options.output_from_init_contract, + options.tracer, + options.vm_tracer, + ) + } + + /// Execute a transaction in a "virtual" context. + /// This will ensure the caller has enough balance to execute the desired transaction. + /// Used for extra-block executions for things like consensus contracts and RPCs + pub fn transact_virtual( + &'a mut self, + t: &SignedTransaction, + options: TransactOptions, + ) -> Result, ExecutionError> + where + T: Tracer, + V: VMTracer, + { + let sender = t.sender(); + let balance = self.state.balance(&sender)?; + let needed_balance = t.value.saturating_add(t.gas.saturating_mul(t.gas_price)); + if balance < needed_balance { + // give the sender a sufficient balance + self.state + .add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty)?; + } + + self.transact(t, options) + } + + /// Execute transaction/call with tracing enabled + fn transact_with_tracer( + &'a mut self, + t: &SignedTransaction, + check_nonce: bool, + output_from_create: bool, + mut tracer: T, + mut vm_tracer: V, + ) -> Result, ExecutionError> + where + T: Tracer, + V: VMTracer, + { + let sender = t.sender(); + let nonce = self.state.nonce(&sender)?; + + let schedule = self.schedule; + let base_gas_required = U256::from(t.gas_required(&schedule)); + + if t.gas < base_gas_required { + return Err(ExecutionError::NotEnoughBaseGas { + required: base_gas_required, + got: t.gas, + }); + } + + if !t.is_unsigned() + && check_nonce + && schedule.kill_dust != CleanDustMode::Off + && !self.state.exists(&sender)? + { + return Err(ExecutionError::SenderMustExist); + } + + let init_gas = t.gas - base_gas_required; + + // validate transaction nonce + if check_nonce && t.nonce != nonce { + return Err(ExecutionError::InvalidNonce { + expected: nonce, + got: t.nonce, + }); + } + + // validate if transaction fits into given block + if self.info.gas_used + t.gas > self.info.gas_limit { + return Err(ExecutionError::BlockGasLimitReached { + gas_limit: self.info.gas_limit, + gas_used: self.info.gas_used, + gas: t.gas, + }); + } + + // TODO: we might need bigints here, or at least check overflows. + let balance = self.state.balance(&sender)?; + let gas_cost = t.gas.full_mul(t.gas_price); + let total_cost = U512::from(t.value) + gas_cost; + + // avoid unaffordable transactions + let balance512 = U512::from(balance); + if balance512 < total_cost { + return Err(ExecutionError::NotEnoughCash { + required: total_cost, + got: balance512, + }); + } + + let mut substate = Substate::new(); + + // NOTE: there can be no invalid transactions from this point. + if !schedule.keep_unsigned_nonce || !t.is_unsigned() { + self.state.inc_nonce(&sender)?; + } + self.state.sub_balance( + &sender, + &U256::from(gas_cost), + &mut substate.to_cleanup_mode(&schedule), + )?; + + let (result, output) = match t.action { + Action::Create => { + let (new_address, code_hash) = contract_address( + self.machine.create_address_scheme(self.info.number), + &sender, + &nonce, + &t.data, + ); + let params = ActionParams { + code_address: new_address.clone(), + code_hash: code_hash, + address: new_address, + sender: sender.clone(), + origin: sender.clone(), + gas: init_gas, + gas_price: t.gas_price, + value: ActionValue::Transfer(t.value), + code: Some(Arc::new(t.data.clone())), + data: None, + call_type: CallType::None, + params_type: vm::ParamsType::Embedded, + }; + let res = self.create(params, &mut substate, &mut tracer, &mut vm_tracer); + let out = match &res { + Ok(res) if output_from_create => res.return_data.to_vec(), + _ => Vec::new(), + }; + (res, out) + } + Action::Call(ref address) => { + let params = ActionParams { + code_address: address.clone(), + address: address.clone(), + sender: sender.clone(), + origin: sender.clone(), + gas: init_gas, + gas_price: t.gas_price, + value: ActionValue::Transfer(t.value), + code: self.state.code(address)?, + code_hash: self.state.code_hash(address)?, + data: Some(t.data.clone()), + call_type: CallType::Call, + params_type: vm::ParamsType::Separate, + }; + let res = self.call(params, &mut substate, &mut tracer, &mut vm_tracer); + let out = match &res { + Ok(res) => res.return_data.to_vec(), + _ => Vec::new(), + }; + (res, out) + } + }; + + // finalize here! + Ok(self.finalize( + t, + substate, + result, + output, + tracer.drain(), + vm_tracer.drain(), + )?) + } + + /// Calls contract function with given contract params and stack depth. + /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). + /// Modifies the substate and the output. + /// Returns either gas_left or `vm::Error`. + pub fn call_with_stack_depth( + &mut self, + params: ActionParams, + substate: &mut Substate, + stack_depth: usize, + tracer: &mut T, + vm_tracer: &mut V, + ) -> vm::Result + where + T: Tracer, + V: VMTracer, + { + tracer.prepare_trace_call( + ¶ms, + self.depth, + self.machine + .builtin(¶ms.address, self.info.number) + .is_some(), + ); + vm_tracer.prepare_subtrace( + params + .code + .as_ref() + .map_or_else(|| &[] as &[u8], |d| &*d as &[u8]), + ); + + let gas = params.gas; + + let vm_factory = self.state.vm_factory(); + let result = CallCreateExecutive::new_call_raw( + params, + self.info, + self.machine, + self.schedule, + &vm_factory, + self.depth, + stack_depth, + self.static_flag, + ) + .consume(self.state, substate, tracer, vm_tracer); + + match result { + Ok(ref val) if val.apply_state => { + tracer.done_trace_call(gas - val.gas_left, &val.return_data); + } + Ok(_) => { + tracer.done_trace_failed(&vm::Error::Reverted); + } + Err(ref err) => { + tracer.done_trace_failed(err); + } + } + vm_tracer.done_subtrace(); + + result + } + + /// Calls contract function with given contract params, if the stack depth is above a threshold, create a new thread + /// to execute it. + pub fn call_with_crossbeam( + &mut self, + params: ActionParams, + substate: &mut Substate, + stack_depth: usize, + tracer: &mut T, + vm_tracer: &mut V, + ) -> vm::Result + where + T: Tracer, + V: VMTracer, + { + let local_stack_size = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get()); + let depth_threshold = + local_stack_size.saturating_sub(STACK_SIZE_ENTRY_OVERHEAD) / STACK_SIZE_PER_DEPTH; + + if stack_depth != depth_threshold { + self.call_with_stack_depth(params, substate, stack_depth, tracer, vm_tracer) + } else { + thread::scope(|scope| { + let stack_size = cmp::max( + self.schedule.max_depth.saturating_sub(depth_threshold) * STACK_SIZE_PER_DEPTH, + local_stack_size, + ); + scope + .builder() + .stack_size(stack_size) + .spawn(|_| { + self.call_with_stack_depth(params, substate, stack_depth, tracer, vm_tracer) + }) + .expect( + "Sub-thread creation cannot fail; the host might run out of resources; qed", + ) + .join() + }) + .expect("Sub-thread never panics; qed") + .expect("Sub-thread never panics; qed") + } + } + + /// Calls contract function with given contract params. + pub fn call( + &mut self, + params: ActionParams, + substate: &mut Substate, + tracer: &mut T, + vm_tracer: &mut V, + ) -> vm::Result + where + T: Tracer, + V: VMTracer, + { + self.call_with_stack_depth(params, substate, 0, tracer, vm_tracer) + } + + /// Creates contract with given contract params and stack depth. + /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). + /// Modifies the substate. + pub fn create_with_stack_depth( + &mut self, + params: ActionParams, + substate: &mut Substate, + stack_depth: usize, + tracer: &mut T, + vm_tracer: &mut V, + ) -> vm::Result + where + T: Tracer, + V: VMTracer, + { + tracer.prepare_trace_create(¶ms); + vm_tracer.prepare_subtrace( + params + .code + .as_ref() + .map_or_else(|| &[] as &[u8], |d| &*d as &[u8]), + ); + + let address = params.address; + let gas = params.gas; + + let vm_factory = self.state.vm_factory(); + let result = CallCreateExecutive::new_create_raw( + params, + self.info, + self.machine, + self.schedule, + &vm_factory, + self.depth, + stack_depth, + self.static_flag, + ) + .consume(self.state, substate, tracer, vm_tracer); + + match result { + Ok(ref val) if val.apply_state => { + tracer.done_trace_create(gas - val.gas_left, &val.return_data, address); + } + Ok(_) => { + tracer.done_trace_failed(&vm::Error::Reverted); + } + Err(ref err) => { + tracer.done_trace_failed(err); + } + } + vm_tracer.done_subtrace(); + + result + } + + /// Creates contract with given contract params, if the stack depth is above a threshold, create a new thread to + /// execute it. + pub fn create_with_crossbeam( + &mut self, + params: ActionParams, + substate: &mut Substate, + stack_depth: usize, + tracer: &mut T, + vm_tracer: &mut V, + ) -> vm::Result + where + T: Tracer, + V: VMTracer, + { + let local_stack_size = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get()); + let depth_threshold = + local_stack_size.saturating_sub(STACK_SIZE_ENTRY_OVERHEAD) / STACK_SIZE_PER_DEPTH; + + if stack_depth != depth_threshold { + self.create_with_stack_depth(params, substate, stack_depth, tracer, vm_tracer) + } else { + thread::scope(|scope| { + let stack_size = cmp::max( + self.schedule.max_depth.saturating_sub(depth_threshold) * STACK_SIZE_PER_DEPTH, + local_stack_size, + ); + scope + .builder() + .stack_size(stack_size) + .spawn(|_| { + self.create_with_stack_depth( + params, + substate, + stack_depth, + tracer, + vm_tracer, + ) + }) + .expect( + "Sub-thread creation cannot fail; the host might run out of resources; qed", + ) + .join() + }) + .expect("Sub-thread never panics; qed") + .expect("Sub-thread never panics; qed") + } + } + + /// Creates contract with given contract params. + pub fn create( + &mut self, + params: ActionParams, + substate: &mut Substate, + tracer: &mut T, + vm_tracer: &mut V, + ) -> vm::Result + where + T: Tracer, + V: VMTracer, + { + self.create_with_stack_depth(params, substate, 0, tracer, vm_tracer) + } + + /// Finalizes the transaction (does refunds and suicides). + fn finalize( + &mut self, + t: &SignedTransaction, + mut substate: Substate, + result: vm::Result, + output: Bytes, + trace: Vec, + vm_trace: Option, + ) -> Result, ExecutionError> { + let schedule = self.schedule; + + // refunds from SSTORE nonzero -> zero + assert!( + substate.sstore_clears_refund >= 0, + "On transaction level, sstore clears refund cannot go below zero." + ); + let sstore_refunds = U256::from(substate.sstore_clears_refund as u64); + // refunds from contract suicides + let suicide_refunds = + U256::from(schedule.suicide_refund_gas) * U256::from(substate.suicides.len()); + let refunds_bound = sstore_refunds + suicide_refunds; + + // real ammount to refund + let gas_left_prerefund = match result { + Ok(FinalizationResult { gas_left, .. }) => gas_left, + _ => 0.into(), + }; + let refunded = cmp::min(refunds_bound, (t.gas - gas_left_prerefund) >> 1); + let gas_left = gas_left_prerefund + refunded; + + let gas_used = t.gas.saturating_sub(gas_left); + let (refund_value, overflow_1) = gas_left.overflowing_mul(t.gas_price); + let (fees_value, overflow_2) = gas_used.overflowing_mul(t.gas_price); + if overflow_1 || overflow_2 { + return Err(ExecutionError::TransactionMalformed( + "U256 Overflow".to_string(), + )); + } + + trace!("exec::finalize: t.gas={}, sstore_refunds={}, suicide_refunds={}, refunds_bound={}, gas_left_prerefund={}, refunded={}, gas_left={}, gas_used={}, refund_value={}, fees_value={}\n", t.gas, sstore_refunds, suicide_refunds, refunds_bound, gas_left_prerefund, refunded, gas_left, gas_used, refund_value, fees_value); - let sender = t.sender(); - trace!("exec::finalize: Refunding refund_value={}, sender={}\n", refund_value, sender); - // Below: NoEmpty is safe since the sender must already be non-null to have sent this transaction - self.state.add_balance(&sender, &refund_value, CleanupMode::NoEmpty)?; - trace!("exec::finalize: Compensating author: fees_value={}, author={}\n", fees_value, &self.info.author); - self.state.add_balance(&self.info.author, &fees_value, substate.to_cleanup_mode(&schedule))?; - - // perform suicides - for address in &substate.suicides { - self.state.kill_account(address); - } - - // perform garbage-collection - let min_balance = if schedule.kill_dust != CleanDustMode::Off { Some(U256::from(schedule.tx_gas).overflowing_mul(t.gas_price).0) } else { None }; - self.state.kill_garbage(&substate.touched, schedule.kill_empty, &min_balance, schedule.kill_dust == CleanDustMode::WithCodeAndStorage)?; - - match result { - Err(vm::Error::Internal(msg)) => Err(ExecutionError::Internal(msg)), - Err(exception) => { - Ok(Executed { - exception: Some(exception), - gas: t.gas, - gas_used: t.gas, - refunded: U256::zero(), - cumulative_gas_used: self.info.gas_used + t.gas, - logs: vec![], - contracts_created: vec![], - output: output, - trace: trace, - vm_trace: vm_trace, - state_diff: None, - }) - }, - Ok(r) => { - Ok(Executed { - exception: if r.apply_state { None } else { Some(vm::Error::Reverted) }, - gas: t.gas, - gas_used: gas_used, - refunded: refunded, - cumulative_gas_used: self.info.gas_used + gas_used, - logs: substate.logs, - contracts_created: substate.contracts_created, - output: output, - trace: trace, - vm_trace: vm_trace, - state_diff: None, - }) - }, - } - } + let sender = t.sender(); + trace!( + "exec::finalize: Refunding refund_value={}, sender={}\n", + refund_value, + sender + ); + // Below: NoEmpty is safe since the sender must already be non-null to have sent this transaction + self.state + .add_balance(&sender, &refund_value, CleanupMode::NoEmpty)?; + trace!( + "exec::finalize: Compensating author: fees_value={}, author={}\n", + fees_value, + &self.info.author + ); + self.state.add_balance( + &self.info.author, + &fees_value, + substate.to_cleanup_mode(&schedule), + )?; + + // perform suicides + for address in &substate.suicides { + self.state.kill_account(address); + } + + // perform garbage-collection + let min_balance = if schedule.kill_dust != CleanDustMode::Off { + Some(U256::from(schedule.tx_gas).overflowing_mul(t.gas_price).0) + } else { + None + }; + self.state.kill_garbage( + &substate.touched, + schedule.kill_empty, + &min_balance, + schedule.kill_dust == CleanDustMode::WithCodeAndStorage, + )?; + + match result { + Err(vm::Error::Internal(msg)) => Err(ExecutionError::Internal(msg)), + Err(exception) => Ok(Executed { + exception: Some(exception), + gas: t.gas, + gas_used: t.gas, + refunded: U256::zero(), + cumulative_gas_used: self.info.gas_used + t.gas, + logs: vec![], + contracts_created: vec![], + output: output, + trace: trace, + vm_trace: vm_trace, + state_diff: None, + }), + Ok(r) => Ok(Executed { + exception: if r.apply_state { + None + } else { + Some(vm::Error::Reverted) + }, + gas: t.gas, + gas_used: gas_used, + refunded: refunded, + cumulative_gas_used: self.info.gas_used + gas_used, + logs: substate.logs, + contracts_created: substate.contracts_created, + output: output, + trace: trace, + vm_trace: vm_trace, + state_diff: None, + }), + } + } } #[cfg(test)] #[allow(dead_code)] mod tests { - use std::sync::Arc; - use std::str::FromStr; - use rustc_hex::FromHex; - use ethkey::{Generator, Random}; - use super::*; - use ethereum_types::{H256, U256, U512, Address}; - use vm::{ActionParams, ActionValue, CallType, EnvInfo, CreateContractAddress}; - use evm::{Factory, VMType}; - use error::ExecutionError; - use machine::EthereumMachine; - use state::{Substate, CleanupMode}; - use test_helpers::{get_temp_state_with_factory, get_temp_state}; - use trace::trace; - use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer}; - use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer}; - use types::transaction::{Action, Transaction}; - - fn make_frontier_machine(max_depth: usize) -> EthereumMachine { - let mut machine = ::ethereum::new_frontier_test_machine(); - machine.set_schedule_creation_rules(Box::new(move |s, _| s.max_depth = max_depth)); - machine - } - - fn make_byzantium_machine(max_depth: usize) -> EthereumMachine { - let mut machine = ::ethereum::new_byzantium_test_machine(); - machine.set_schedule_creation_rules(Box::new(move |s, _| s.max_depth = max_depth)); - machine - } - - #[test] - fn test_contract_address() { - let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let expected_address = Address::from_str("3f09c73a5ed19289fb9bdc72f1742566df146f56").unwrap(); - assert_eq!(expected_address, contract_address(CreateContractAddress::FromSenderAndNonce, &address, &U256::from(88), &[]).0); - } - - // TODO: replace params with transactions! - evm_test!{test_sender_balance: test_sender_balance_int} - fn test_sender_balance(factory: Factory) { - let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &[]).0; - let mut params = ActionParams::default(); - params.address = address.clone(); - params.sender = sender.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new("3331600055".from_hex().unwrap())); - params.value = ActionValue::Transfer(U256::from(0x7)); - let mut state = get_temp_state_with_factory(factory); - state.add_balance(&sender, &U256::from(0x100u64), CleanupMode::NoEmpty).unwrap(); - let info = EnvInfo::default(); - let machine = make_frontier_machine(0); - let schedule = machine.schedule(info.number); - let mut substate = Substate::new(); - - let FinalizationResult { gas_left, .. } = { - let mut ex = Executive::new(&mut state, &info, &machine, &schedule); - ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap() - }; - - assert_eq!(gas_left, U256::from(79_975)); - assert_eq!(state.storage_at(&address, &H256::new()).unwrap(), H256::from(&U256::from(0xf9u64))); - assert_eq!(state.balance(&sender).unwrap(), U256::from(0xf9)); - assert_eq!(state.balance(&address).unwrap(), U256::from(0x7)); - assert_eq!(substate.contracts_created.len(), 0); - - // TODO: just test state root. - } - - evm_test!{test_create_contract_out_of_depth: test_create_contract_out_of_depth_int} - fn test_create_contract_out_of_depth(factory: Factory) { - // code: - // - // 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes? - // 60 00 - push 0 - // 52 - // 60 1d - push 29 - // 60 03 - push 3 - // 60 17 - push 17 - // f0 - create - // 60 00 - push 0 - // 55 sstore - // - // other code: - // - // 60 10 - push 16 - // 80 - duplicate first stack item - // 60 0c - push 12 - // 60 00 - push 0 - // 39 - copy current code to memory - // 60 00 - push 0 - // f3 - return - - let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0600055".from_hex().unwrap(); - - let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &[]).0; - // TODO: add tests for 'callcreate' - //let next_address = contract_address(&address, &U256::zero()); - let mut params = ActionParams::default(); - params.address = address.clone(); - params.sender = sender.clone(); - params.origin = sender.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - params.value = ActionValue::Transfer(U256::from(100)); - let mut state = get_temp_state_with_factory(factory); - state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty).unwrap(); - let info = EnvInfo::default(); - let machine = make_frontier_machine(0); - let schedule = machine.schedule(info.number); - let mut substate = Substate::new(); - - let FinalizationResult { gas_left, .. } = { - let mut ex = Executive::new(&mut state, &info, &machine, &schedule); - ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap() - }; - - assert_eq!(gas_left, U256::from(62_976)); - // ended with max depth - assert_eq!(substate.contracts_created.len(), 0); - } - - #[test] - fn test_call_to_precompiled_tracing() { - // code: - // - // 60 00 - push 00 out size - // 60 00 - push 00 out offset - // 60 00 - push 00 in size - // 60 00 - push 00 in offset - // 60 01 - push 01 value - // 60 03 - push 03 to - // 61 ffff - push fff gas - // f1 - CALL - - let code = "60006000600060006001600361fffff1".from_hex().unwrap(); - let sender = Address::from_str("4444444444444444444444444444444444444444").unwrap(); - let address = Address::from_str("5555555555555555555555555555555555555555").unwrap(); - - let mut params = ActionParams::default(); - params.address = address.clone(); - params.code_address = address.clone(); - params.sender = sender.clone(); - params.origin = sender.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - params.value = ActionValue::Transfer(U256::from(100)); - params.call_type = CallType::Call; - let mut state = get_temp_state(); - state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty).unwrap(); - let info = EnvInfo::default(); - let machine = make_byzantium_machine(5); - let schedule = machine.schedule(info.number); - let mut substate = Substate::new(); - let mut tracer = ExecutiveTracer::default(); - let mut vm_tracer = ExecutiveVMTracer::toplevel(); - - let mut ex = Executive::new(&mut state, &info, &machine, &schedule); - ex.call(params, &mut substate, &mut tracer, &mut vm_tracer).unwrap(); - - assert_eq!(tracer.drain(), vec![FlatTrace { - action: trace::Action::Call(trace::Call { - from: "4444444444444444444444444444444444444444".into(), - to: "5555555555555555555555555555555555555555".into(), - value: 100.into(), - gas: 100_000.into(), - input: vec![], - call_type: CallType::Call - }), - result: trace::Res::Call(trace::CallResult { - gas_used: 33021.into(), - output: vec![] - }), - subtraces: 1, - trace_address: Default::default() - }, FlatTrace { - action: trace::Action::Call(trace::Call { - from: "5555555555555555555555555555555555555555".into(), - to: "0000000000000000000000000000000000000003".into(), - value: 1.into(), - gas: 66560.into(), - input: vec![], - call_type: CallType::Call - }), result: trace::Res::Call(trace::CallResult { - gas_used: 600.into(), - output: vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 17, 133, 165, 197, 233, 252, 84, 97, 40, 8, 151, 126, 232, 245, 72, 178, 37, 141, 49] - }), - subtraces: 0, - trace_address: vec![0].into_iter().collect(), - }]); - } - - #[test] - // Tracing is not suported in JIT - fn test_call_to_create() { - // code: - // - // 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes? - // 60 00 - push 0 - // 52 - // 60 1d - push 29 - // 60 03 - push 3 - // 60 17 - push 23 - // f0 - create - // 60 00 - push 0 - // 55 sstore - // - // other code: - // - // 60 10 - push 16 - // 80 - duplicate first stack item - // 60 0c - push 12 - // 60 00 - push 0 - // 39 - copy current code to memory - // 60 00 - push 0 - // f3 - return - - let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0600055".from_hex().unwrap(); - - let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &[]).0; - // TODO: add tests for 'callcreate' - //let next_address = contract_address(&address, &U256::zero()); - let mut params = ActionParams::default(); - params.address = address.clone(); - params.code_address = address.clone(); - params.sender = sender.clone(); - params.origin = sender.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - params.value = ActionValue::Transfer(U256::from(100)); - params.call_type = CallType::Call; - let mut state = get_temp_state(); - state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty).unwrap(); - let info = EnvInfo::default(); - let machine = make_frontier_machine(5); - let schedule = machine.schedule(info.number); - let mut substate = Substate::new(); - let mut tracer = ExecutiveTracer::default(); - let mut vm_tracer = ExecutiveVMTracer::toplevel(); - - let FinalizationResult { gas_left, .. } = { - let mut ex = Executive::new(&mut state, &info, &machine, &schedule); - ex.call(params, &mut substate, &mut tracer, &mut vm_tracer).unwrap() - }; - - assert_eq!(gas_left, U256::from(44_752)); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "cd1722f3947def4cf144679da39c4c32bdc35681".into(), - to: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(), - value: 100.into(), - gas: 100000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(55_248), - output: vec![], - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Create(trace::Create { - from: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(), - value: 23.into(), - gas: 67979.into(), - init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85] - }), - result: trace::Res::Create(trace::CreateResult { - gas_used: U256::from(3224), - address: Address::from_str("c6d80f262ae5e0f164e5fde365044d7ada2bfa34").unwrap(), - code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] - }), - }]; - - assert_eq!(tracer.drain(), expected_trace); - - let expected_vm_trace = VMTrace { + use super::*; + use error::ExecutionError; + use ethereum_types::{Address, H256, U256, U512}; + use ethkey::{Generator, Random}; + use evm::{Factory, VMType}; + use machine::EthereumMachine; + use rustc_hex::FromHex; + use state::{CleanupMode, Substate}; + use std::{str::FromStr, sync::Arc}; + use test_helpers::{get_temp_state, get_temp_state_with_factory}; + use trace::{ + trace, ExecutiveTracer, ExecutiveVMTracer, FlatTrace, MemoryDiff, NoopTracer, NoopVMTracer, + StorageDiff, Tracer, VMExecutedOperation, VMOperation, VMTrace, VMTracer, + }; + use types::transaction::{Action, Transaction}; + use vm::{ActionParams, ActionValue, CallType, CreateContractAddress, EnvInfo}; + + fn make_frontier_machine(max_depth: usize) -> EthereumMachine { + let mut machine = ::ethereum::new_frontier_test_machine(); + machine.set_schedule_creation_rules(Box::new(move |s, _| s.max_depth = max_depth)); + machine + } + + fn make_byzantium_machine(max_depth: usize) -> EthereumMachine { + let mut machine = ::ethereum::new_byzantium_test_machine(); + machine.set_schedule_creation_rules(Box::new(move |s, _| s.max_depth = max_depth)); + machine + } + + #[test] + fn test_contract_address() { + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let expected_address = + Address::from_str("3f09c73a5ed19289fb9bdc72f1742566df146f56").unwrap(); + assert_eq!( + expected_address, + contract_address( + CreateContractAddress::FromSenderAndNonce, + &address, + &U256::from(88), + &[] + ) + .0 + ); + } + + // TODO: replace params with transactions! + evm_test! {test_sender_balance: test_sender_balance_int} + fn test_sender_balance(factory: Factory) { + let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let address = contract_address( + CreateContractAddress::FromSenderAndNonce, + &sender, + &U256::zero(), + &[], + ) + .0; + let mut params = ActionParams::default(); + params.address = address.clone(); + params.sender = sender.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new("3331600055".from_hex().unwrap())); + params.value = ActionValue::Transfer(U256::from(0x7)); + let mut state = get_temp_state_with_factory(factory); + state + .add_balance(&sender, &U256::from(0x100u64), CleanupMode::NoEmpty) + .unwrap(); + let info = EnvInfo::default(); + let machine = make_frontier_machine(0); + let schedule = machine.schedule(info.number); + let mut substate = Substate::new(); + + let FinalizationResult { gas_left, .. } = { + let mut ex = Executive::new(&mut state, &info, &machine, &schedule); + ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) + .unwrap() + }; + + assert_eq!(gas_left, U256::from(79_975)); + assert_eq!( + state.storage_at(&address, &H256::new()).unwrap(), + H256::from(&U256::from(0xf9u64)) + ); + assert_eq!(state.balance(&sender).unwrap(), U256::from(0xf9)); + assert_eq!(state.balance(&address).unwrap(), U256::from(0x7)); + assert_eq!(substate.contracts_created.len(), 0); + + // TODO: just test state root. + } + + evm_test! {test_create_contract_out_of_depth: test_create_contract_out_of_depth_int} + fn test_create_contract_out_of_depth(factory: Factory) { + // code: + // + // 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes? + // 60 00 - push 0 + // 52 + // 60 1d - push 29 + // 60 03 - push 3 + // 60 17 - push 17 + // f0 - create + // 60 00 - push 0 + // 55 sstore + // + // other code: + // + // 60 10 - push 16 + // 80 - duplicate first stack item + // 60 0c - push 12 + // 60 00 - push 0 + // 39 - copy current code to memory + // 60 00 - push 0 + // f3 - return + + let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0600055".from_hex().unwrap(); + + let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); + let address = contract_address( + CreateContractAddress::FromSenderAndNonce, + &sender, + &U256::zero(), + &[], + ) + .0; + // TODO: add tests for 'callcreate' + //let next_address = contract_address(&address, &U256::zero()); + let mut params = ActionParams::default(); + params.address = address.clone(); + params.sender = sender.clone(); + params.origin = sender.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + params.value = ActionValue::Transfer(U256::from(100)); + let mut state = get_temp_state_with_factory(factory); + state + .add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty) + .unwrap(); + let info = EnvInfo::default(); + let machine = make_frontier_machine(0); + let schedule = machine.schedule(info.number); + let mut substate = Substate::new(); + + let FinalizationResult { gas_left, .. } = { + let mut ex = Executive::new(&mut state, &info, &machine, &schedule); + ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) + .unwrap() + }; + + assert_eq!(gas_left, U256::from(62_976)); + // ended with max depth + assert_eq!(substate.contracts_created.len(), 0); + } + + #[test] + fn test_call_to_precompiled_tracing() { + // code: + // + // 60 00 - push 00 out size + // 60 00 - push 00 out offset + // 60 00 - push 00 in size + // 60 00 - push 00 in offset + // 60 01 - push 01 value + // 60 03 - push 03 to + // 61 ffff - push fff gas + // f1 - CALL + + let code = "60006000600060006001600361fffff1".from_hex().unwrap(); + let sender = Address::from_str("4444444444444444444444444444444444444444").unwrap(); + let address = Address::from_str("5555555555555555555555555555555555555555").unwrap(); + + let mut params = ActionParams::default(); + params.address = address.clone(); + params.code_address = address.clone(); + params.sender = sender.clone(); + params.origin = sender.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + params.value = ActionValue::Transfer(U256::from(100)); + params.call_type = CallType::Call; + let mut state = get_temp_state(); + state + .add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty) + .unwrap(); + let info = EnvInfo::default(); + let machine = make_byzantium_machine(5); + let schedule = machine.schedule(info.number); + let mut substate = Substate::new(); + let mut tracer = ExecutiveTracer::default(); + let mut vm_tracer = ExecutiveVMTracer::toplevel(); + + let mut ex = Executive::new(&mut state, &info, &machine, &schedule); + ex.call(params, &mut substate, &mut tracer, &mut vm_tracer) + .unwrap(); + + assert_eq!( + tracer.drain(), + vec![ + FlatTrace { + action: trace::Action::Call(trace::Call { + from: "4444444444444444444444444444444444444444".into(), + to: "5555555555555555555555555555555555555555".into(), + value: 100.into(), + gas: 100_000.into(), + input: vec![], + call_type: CallType::Call + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 33021.into(), + output: vec![] + }), + subtraces: 1, + trace_address: Default::default() + }, + FlatTrace { + action: trace::Action::Call(trace::Call { + from: "5555555555555555555555555555555555555555".into(), + to: "0000000000000000000000000000000000000003".into(), + value: 1.into(), + gas: 66560.into(), + input: vec![], + call_type: CallType::Call + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 600.into(), + output: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 17, 133, 165, 197, 233, 252, + 84, 97, 40, 8, 151, 126, 232, 245, 72, 178, 37, 141, 49 + ] + }), + subtraces: 0, + trace_address: vec![0].into_iter().collect(), + } + ] + ); + } + + #[test] + // Tracing is not suported in JIT + fn test_call_to_create() { + // code: + // + // 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes? + // 60 00 - push 0 + // 52 + // 60 1d - push 29 + // 60 03 - push 3 + // 60 17 - push 23 + // f0 - create + // 60 00 - push 0 + // 55 sstore + // + // other code: + // + // 60 10 - push 16 + // 80 - duplicate first stack item + // 60 0c - push 12 + // 60 00 - push 0 + // 39 - copy current code to memory + // 60 00 - push 0 + // f3 - return + + let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0600055".from_hex().unwrap(); + + let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); + let address = contract_address( + CreateContractAddress::FromSenderAndNonce, + &sender, + &U256::zero(), + &[], + ) + .0; + // TODO: add tests for 'callcreate' + //let next_address = contract_address(&address, &U256::zero()); + let mut params = ActionParams::default(); + params.address = address.clone(); + params.code_address = address.clone(); + params.sender = sender.clone(); + params.origin = sender.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + params.value = ActionValue::Transfer(U256::from(100)); + params.call_type = CallType::Call; + let mut state = get_temp_state(); + state + .add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty) + .unwrap(); + let info = EnvInfo::default(); + let machine = make_frontier_machine(5); + let schedule = machine.schedule(info.number); + let mut substate = Substate::new(); + let mut tracer = ExecutiveTracer::default(); + let mut vm_tracer = ExecutiveVMTracer::toplevel(); + + let FinalizationResult { gas_left, .. } = { + let mut ex = Executive::new(&mut state, &info, &machine, &schedule); + ex.call(params, &mut substate, &mut tracer, &mut vm_tracer) + .unwrap() + }; + + assert_eq!(gas_left, U256::from(44_752)); + + let expected_trace = vec![ + FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "cd1722f3947def4cf144679da39c4c32bdc35681".into(), + to: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(), + value: 100.into(), + gas: 100000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(55_248), + output: vec![], + }), + }, + FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Create(trace::Create { + from: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(), + value: 23.into(), + gas: 67979.into(), + init: vec![ + 96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, + 87, 0, 91, 96, 32, 53, 96, 0, 53, 85, + ], + }), + result: trace::Res::Create(trace::CreateResult { + gas_used: U256::from(3224), + address: Address::from_str("c6d80f262ae5e0f164e5fde365044d7ada2bfa34").unwrap(), + code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53], + }), + }, + ]; + + assert_eq!(tracer.drain(), expected_trace); + + let expected_vm_trace = VMTrace { parent_step: 0, code: vec![124, 96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85, 96, 0, 82, 96, 29, 96, 3, 96, 23, 240, 96, 0, 85], operations: vec![ @@ -1481,704 +1991,986 @@ mod tests { } ] }; - assert_eq!(vm_tracer.drain().unwrap(), expected_vm_trace); - } - - #[test] - fn test_trace_reverted_create() { - // code: - // - // 65 60016000fd - push 5 bytes - // 60 00 - push 0 - // 52 mstore - // 60 05 - push 5 - // 60 1b - push 27 - // 60 17 - push 23 - // f0 - create - // 60 00 - push 0 - // 55 sstore - // - // other code: - // - // 60 01 - // 60 00 - // fd - revert - - let code = "6460016000fd6000526005601b6017f0600055".from_hex().unwrap(); - - let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &[]).0; - let mut params = ActionParams::default(); - params.address = address.clone(); - params.code_address = address.clone(); - params.sender = sender.clone(); - params.origin = sender.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - params.value = ActionValue::Transfer(U256::from(100)); - params.call_type = CallType::Call; - let mut state = get_temp_state(); - state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty).unwrap(); - let info = EnvInfo::default(); - let machine = ::ethereum::new_byzantium_test_machine(); - let schedule = machine.schedule(info.number); - let mut substate = Substate::new(); - let mut tracer = ExecutiveTracer::default(); - let mut vm_tracer = ExecutiveVMTracer::toplevel(); - - let FinalizationResult { gas_left, .. } = { - let mut ex = Executive::new(&mut state, &info, &machine, &schedule); - ex.call(params, &mut substate, &mut tracer, &mut vm_tracer).unwrap() - }; - - assert_eq!(gas_left, U256::from(62967)); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "cd1722f3947def4cf144679da39c4c32bdc35681".into(), - to: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(), - value: 100.into(), - gas: 100_000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(37_033), - output: vec![], - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Create(trace::Create { - from: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(), - value: 23.into(), - gas: 66_917.into(), - init: vec![0x60, 0x01, 0x60, 0x00, 0xfd] - }), - result: trace::Res::FailedCreate(vm::Error::Reverted.into()), - }]; - - assert_eq!(tracer.drain(), expected_trace); - } - - #[test] - fn test_create_contract() { - // Tracing is not supported in JIT - // code: - // - // 60 10 - push 16 - // 80 - duplicate first stack item - // 60 0c - push 12 - // 60 00 - push 0 - // 39 - copy current code to memory - // 60 00 - push 0 - // f3 - return - - let code = "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(); - - let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &[]).0; - // TODO: add tests for 'callcreate' - //let next_address = contract_address(&address, &U256::zero()); - let mut params = ActionParams::default(); - params.address = address.clone(); - params.sender = sender.clone(); - params.origin = sender.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - params.value = ActionValue::Transfer(100.into()); - let mut state = get_temp_state(); - state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty).unwrap(); - let info = EnvInfo::default(); - let machine = make_frontier_machine(5); - let schedule = machine.schedule(info.number); - let mut substate = Substate::new(); - let mut tracer = ExecutiveTracer::default(); - let mut vm_tracer = ExecutiveVMTracer::toplevel(); - - let FinalizationResult { gas_left, .. } = { - let mut ex = Executive::new(&mut state, &info, &machine, &schedule); - ex.create(params.clone(), &mut substate, &mut tracer, &mut vm_tracer).unwrap() - }; - - assert_eq!(gas_left, U256::from(96_776)); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 0, - action: trace::Action::Create(trace::Create { - from: params.sender, - value: 100.into(), - gas: params.gas, - init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85], - }), - result: trace::Res::Create(trace::CreateResult { - gas_used: U256::from(3224), - address: params.address, - code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] - }), - }]; - - assert_eq!(tracer.drain(), expected_trace); - - let expected_vm_trace = VMTrace { - parent_step: 0, - code: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85], - operations: vec![ - VMOperation { pc: 0, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99997.into(), stack_push: vec_into![16], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 2, instruction: 128, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99994.into(), stack_push: vec_into![16, 16], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 3, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99991.into(), stack_push: vec_into![12], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 5, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99988.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 7, instruction: 57, gas_cost: 9.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vec_into![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] }), store_diff: None }) }, - VMOperation { pc: 8, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99976.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 10, instruction: 243, gas_cost: 0.into(), executed: Some(VMExecutedOperation { gas_used: 99976.into(), stack_push: vec_into![], mem_diff: None, store_diff: None }) } - ], - subs: vec![] - }; - assert_eq!(vm_tracer.drain().unwrap(), expected_vm_trace); - } - - evm_test!{test_create_contract_value_too_high: test_create_contract_value_too_high_int} - fn test_create_contract_value_too_high(factory: Factory) { - // code: - // - // 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes? - // 60 00 - push 0 - // 52 - // 60 1d - push 29 - // 60 03 - push 3 - // 60 e6 - push 230 - // f0 - create a contract trying to send 230. - // 60 00 - push 0 - // 55 sstore - // - // other code: - // - // 60 10 - push 16 - // 80 - duplicate first stack item - // 60 0c - push 12 - // 60 00 - push 0 - // 39 - copy current code to memory - // 60 00 - push 0 - // f3 - return - - let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d600360e6f0600055".from_hex().unwrap(); - - let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &[]).0; - // TODO: add tests for 'callcreate' - //let next_address = contract_address(&address, &U256::zero()); - let mut params = ActionParams::default(); - params.address = address.clone(); - params.sender = sender.clone(); - params.origin = sender.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - params.value = ActionValue::Transfer(U256::from(100)); - let mut state = get_temp_state_with_factory(factory); - state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty).unwrap(); - let info = EnvInfo::default(); - let machine = make_frontier_machine(0); - let schedule = machine.schedule(info.number); - let mut substate = Substate::new(); - - let FinalizationResult { gas_left, .. } = { - let mut ex = Executive::new(&mut state, &info, &machine, &schedule); - ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap() - }; - - assert_eq!(gas_left, U256::from(62_976)); - assert_eq!(substate.contracts_created.len(), 0); - } - - evm_test!{test_create_contract_without_max_depth: test_create_contract_without_max_depth_int} - fn test_create_contract_without_max_depth(factory: Factory) { - // code: - // - // 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes? - // 60 00 - push 0 - // 52 - // 60 1d - push 29 - // 60 03 - push 3 - // 60 17 - push 17 - // f0 - create - // 60 00 - push 0 - // 55 sstore - // - // other code: - // - // 60 10 - push 16 - // 80 - duplicate first stack item - // 60 0c - push 12 - // 60 00 - push 0 - // 39 - copy current code to memory - // 60 00 - push 0 - // f3 - return - - let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0".from_hex().unwrap(); - - let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &[]).0; - let next_address = contract_address(CreateContractAddress::FromSenderAndNonce, &address, &U256::zero(), &[]).0; - let mut params = ActionParams::default(); - params.address = address.clone(); - params.sender = sender.clone(); - params.origin = sender.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - params.value = ActionValue::Transfer(U256::from(100)); - let mut state = get_temp_state_with_factory(factory); - state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty).unwrap(); - let info = EnvInfo::default(); - let machine = make_frontier_machine(1024); - let schedule = machine.schedule(info.number); - let mut substate = Substate::new(); - - { - let mut ex = Executive::new(&mut state, &info, &machine, &schedule); - ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap(); - } - - assert_eq!(substate.contracts_created.len(), 1); - assert_eq!(substate.contracts_created[0], next_address); - } - - // test is incorrect, mk - // TODO: fix (preferred) or remove - evm_test_ignore!{test_aba_calls: test_aba_calls_int} - fn test_aba_calls(factory: Factory) { - // 60 00 - push 0 - // 60 00 - push 0 - // 60 00 - push 0 - // 60 00 - push 0 - // 60 18 - push 18 - // 73 945304eb96065b2a98b57a48a06ae28d285a71b5 - push this address - // 61 03e8 - push 1000 - // f1 - message call - // 58 - get PC - // 55 - sstore - - let code_a = "6000600060006000601873945304eb96065b2a98b57a48a06ae28d285a71b56103e8f15855".from_hex().unwrap(); - - // 60 00 - push 0 - // 60 00 - push 0 - // 60 00 - push 0 - // 60 00 - push 0 - // 60 17 - push 17 - // 73 0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 - push this address - // 61 0x01f4 - push 500 - // f1 - message call - // 60 01 - push 1 - // 01 - add - // 58 - get PC - // 55 - sstore - let code_b = "60006000600060006017730f572e5295c57f15886f9b263e2f6d2d6c7b5ec66101f4f16001015855".from_hex().unwrap(); - - let address_a = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let address_b = Address::from_str("945304eb96065b2a98b57a48a06ae28d285a71b5" ).unwrap(); - let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - - let mut params = ActionParams::default(); - params.address = address_a.clone(); - params.sender = sender.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code_a.clone())); - params.value = ActionValue::Transfer(U256::from(100_000)); - - let mut state = get_temp_state_with_factory(factory); - state.init_code(&address_a, code_a.clone()).unwrap(); - state.init_code(&address_b, code_b.clone()).unwrap(); - state.add_balance(&sender, &U256::from(100_000), CleanupMode::NoEmpty).unwrap(); - - let info = EnvInfo::default(); - let machine = make_frontier_machine(0); - let schedule = machine.schedule(info.number); - let mut substate = Substate::new(); - - let FinalizationResult { gas_left, .. } = { - let mut ex = Executive::new(&mut state, &info, &machine, &schedule); - ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap() - }; - - assert_eq!(gas_left, U256::from(73_237)); - assert_eq!(state.storage_at(&address_a, &H256::from(&U256::from(0x23))).unwrap(), H256::from(&U256::from(1))); - } - - // test is incorrect, mk - // TODO: fix (preferred) or remove - evm_test_ignore!{test_recursive_bomb1: test_recursive_bomb1_int} - fn test_recursive_bomb1(factory: Factory) { - // 60 01 - push 1 - // 60 00 - push 0 - // 54 - sload - // 01 - add - // 60 00 - push 0 - // 55 - sstore - // 60 00 - push 0 - // 60 00 - push 0 - // 60 00 - push 0 - // 60 00 - push 0 - // 60 00 - push 0 - // 30 - load address - // 60 e0 - push e0 - // 5a - get gas - // 03 - sub - // f1 - message call (self in this case) - // 60 01 - push 1 - // 55 - sstore - let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let code = "600160005401600055600060006000600060003060e05a03f1600155".from_hex().unwrap(); - let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &[]).0; - let mut params = ActionParams::default(); - params.address = address.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code.clone())); - let mut state = get_temp_state_with_factory(factory); - state.init_code(&address, code).unwrap(); - let info = EnvInfo::default(); - let machine = make_frontier_machine(0); - let schedule = machine.schedule(info.number); - let mut substate = Substate::new(); - - let FinalizationResult { gas_left, .. } = { - let mut ex = Executive::new(&mut state, &info, &machine, &schedule); - ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap() - }; - - assert_eq!(gas_left, U256::from(59_870)); - assert_eq!(state.storage_at(&address, &H256::from(&U256::zero())).unwrap(), H256::from(&U256::from(1))); - assert_eq!(state.storage_at(&address, &H256::from(&U256::one())).unwrap(), H256::from(&U256::from(1))); - } - - // test is incorrect, mk - // TODO: fix (preferred) or remove - evm_test_ignore!{test_transact_simple: test_transact_simple_int} - fn test_transact_simple(factory: Factory) { - let keypair = Random.generate().unwrap(); - let t = Transaction { - action: Action::Create, - value: U256::from(17), - data: "3331600055".from_hex().unwrap(), - gas: U256::from(100_000), - gas_price: U256::zero(), - nonce: U256::zero() - }.sign(keypair.secret(), None); - let sender = t.sender(); - let contract = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &[]).0; - - let mut state = get_temp_state_with_factory(factory); - state.add_balance(&sender, &U256::from(18), CleanupMode::NoEmpty).unwrap(); - let mut info = EnvInfo::default(); - info.gas_limit = U256::from(100_000); - let machine = make_frontier_machine(0); - let schedule = machine.schedule(info.number); - - let executed = { - let mut ex = Executive::new(&mut state, &info, &machine, &schedule); - let opts = TransactOptions::with_no_tracing(); - ex.transact(&t, opts).unwrap() - }; - - assert_eq!(executed.gas, U256::from(100_000)); - assert_eq!(executed.gas_used, U256::from(41_301)); - assert_eq!(executed.refunded, U256::from(58_699)); - assert_eq!(executed.cumulative_gas_used, U256::from(41_301)); - assert_eq!(executed.logs.len(), 0); - assert_eq!(executed.contracts_created.len(), 0); - assert_eq!(state.balance(&sender).unwrap(), U256::from(1)); - assert_eq!(state.balance(&contract).unwrap(), U256::from(17)); - assert_eq!(state.nonce(&sender).unwrap(), U256::from(1)); - assert_eq!(state.storage_at(&contract, &H256::new()).unwrap(), H256::from(&U256::from(1))); - } - - evm_test!{test_transact_invalid_nonce: test_transact_invalid_nonce_int} - fn test_transact_invalid_nonce(factory: Factory) { - let keypair = Random.generate().unwrap(); - let t = Transaction { - action: Action::Create, - value: U256::from(17), - data: "3331600055".from_hex().unwrap(), - gas: U256::from(100_000), - gas_price: U256::zero(), - nonce: U256::one() - }.sign(keypair.secret(), None); - let sender = t.sender(); - - let mut state = get_temp_state_with_factory(factory); - state.add_balance(&sender, &U256::from(17), CleanupMode::NoEmpty).unwrap(); - let mut info = EnvInfo::default(); - info.gas_limit = U256::from(100_000); - let machine = make_frontier_machine(0); - let schedule = machine.schedule(info.number); - - let res = { - let mut ex = Executive::new(&mut state, &info, &machine, &schedule); - let opts = TransactOptions::with_no_tracing(); - ex.transact(&t, opts) - }; - - match res { - Err(ExecutionError::InvalidNonce { expected, got }) - if expected == U256::zero() && got == U256::one() => (), - _ => assert!(false, "Expected invalid nonce error.") - } - } - - evm_test!{test_transact_gas_limit_reached: test_transact_gas_limit_reached_int} - fn test_transact_gas_limit_reached(factory: Factory) { - let keypair = Random.generate().unwrap(); - let t = Transaction { - action: Action::Create, - value: U256::from(17), - data: "3331600055".from_hex().unwrap(), - gas: U256::from(80_001), - gas_price: U256::zero(), - nonce: U256::zero() - }.sign(keypair.secret(), None); - let sender = t.sender(); - - let mut state = get_temp_state_with_factory(factory); - state.add_balance(&sender, &U256::from(17), CleanupMode::NoEmpty).unwrap(); - let mut info = EnvInfo::default(); - info.gas_used = U256::from(20_000); - info.gas_limit = U256::from(100_000); - let machine = make_frontier_machine(0); - let schedule = machine.schedule(info.number); - - let res = { - let mut ex = Executive::new(&mut state, &info, &machine, &schedule); - let opts = TransactOptions::with_no_tracing(); - ex.transact(&t, opts) - }; - - match res { - Err(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, gas }) - if gas_limit == U256::from(100_000) && gas_used == U256::from(20_000) && gas == U256::from(80_001) => (), - _ => assert!(false, "Expected block gas limit error.") - } - } - - evm_test!{test_not_enough_cash: test_not_enough_cash_int} - fn test_not_enough_cash(factory: Factory) { - - let keypair = Random.generate().unwrap(); - let t = Transaction { - action: Action::Create, - value: U256::from(18), - data: "3331600055".from_hex().unwrap(), - gas: U256::from(100_000), - gas_price: U256::one(), - nonce: U256::zero() - }.sign(keypair.secret(), None); - let sender = t.sender(); - - let mut state = get_temp_state_with_factory(factory); - state.add_balance(&sender, &U256::from(100_017), CleanupMode::NoEmpty).unwrap(); - let mut info = EnvInfo::default(); - info.gas_limit = U256::from(100_000); - let machine = make_frontier_machine(0); - let schedule = machine.schedule(info.number); - - let res = { - let mut ex = Executive::new(&mut state, &info, &machine, &schedule); - let opts = TransactOptions::with_no_tracing(); - ex.transact(&t, opts) - }; - - match res { - Err(ExecutionError::NotEnoughCash { required , got }) - if required == U512::from(100_018) && got == U512::from(100_017) => (), - _ => assert!(false, "Expected not enough cash error. {:?}", res) - } - } - - evm_test!{test_keccak: test_keccak_int} - fn test_keccak(factory: Factory) { - let code = "6064640fffffffff20600055".from_hex().unwrap(); - - let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &[]).0; - // TODO: add tests for 'callcreate' - //let next_address = contract_address(&address, &U256::zero()); - let mut params = ActionParams::default(); - params.address = address.clone(); - params.sender = sender.clone(); - params.origin = sender.clone(); - params.gas = U256::from(0x0186a0); - params.code = Some(Arc::new(code)); - params.value = ActionValue::Transfer(U256::from_str("0de0b6b3a7640000").unwrap()); - let mut state = get_temp_state_with_factory(factory); - state.add_balance(&sender, &U256::from_str("152d02c7e14af6800000").unwrap(), CleanupMode::NoEmpty).unwrap(); - let info = EnvInfo::default(); - let machine = make_frontier_machine(0); - let schedule = machine.schedule(info.number); - let mut substate = Substate::new(); - - let result = { - let mut ex = Executive::new(&mut state, &info, &machine, &schedule); - ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) - }; - - match result { - Err(_) => {}, - _ => panic!("Expected OutOfGas"), - } - } - - evm_test!{test_revert: test_revert_int} - fn test_revert(factory: Factory) { - let contract_address = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - // EIP-140 test case - let code = "6c726576657274656420646174616000557f726576657274206d657373616765000000000000000000000000000000000000600052600e6000fd".from_hex().unwrap(); - let returns = "726576657274206d657373616765".from_hex().unwrap(); - let mut state = get_temp_state_with_factory(factory.clone()); - state.add_balance(&sender, &U256::from_str("152d02c7e14af68000000").unwrap(), CleanupMode::NoEmpty).unwrap(); - state.commit().unwrap(); - - let mut params = ActionParams::default(); - params.address = contract_address.clone(); - params.sender = sender.clone(); - params.origin = sender.clone(); - params.gas = U256::from(20025); - params.code = Some(Arc::new(code)); - params.value = ActionValue::Transfer(U256::zero()); - let info = EnvInfo::default(); - let machine = ::ethereum::new_byzantium_test_machine(); - let schedule = machine.schedule(info.number); - let mut substate = Substate::new(); - - let mut output = [0u8; 14]; - let FinalizationResult { gas_left: result, return_data, .. } = { - let mut ex = Executive::new(&mut state, &info, &machine, &schedule); - ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap() - }; - (&mut output).copy_from_slice(&return_data[..(cmp::min(14, return_data.len()))]); - - assert_eq!(result, U256::from(1)); - assert_eq!(output[..], returns[..]); - assert_eq!(state.storage_at(&contract_address, &H256::from(&U256::zero())).unwrap(), H256::from(&U256::from(0))); - } - - evm_test!{test_eip1283: test_eip1283_int} - fn test_eip1283(factory: Factory) { - let x1 = Address::from(0x1000); - let x2 = Address::from(0x1001); - let y1 = Address::from(0x2001); - let y2 = Address::from(0x2002); - let operating_address = Address::from(0); - let k = H256::new(); - - let mut state = get_temp_state_with_factory(factory.clone()); - state.new_contract(&x1, U256::zero(), U256::from(1)).unwrap(); - state.init_code(&x1, "600160005560006000556001600055".from_hex().unwrap()).unwrap(); - state.new_contract(&x2, U256::zero(), U256::from(1)).unwrap(); - state.init_code(&x2, "600060005560016000556000600055".from_hex().unwrap()).unwrap(); - state.new_contract(&y1, U256::zero(), U256::from(1)).unwrap(); - state.init_code(&y1, "600060006000600061100062fffffff4".from_hex().unwrap()).unwrap(); - state.new_contract(&y2, U256::zero(), U256::from(1)).unwrap(); - state.init_code(&y2, "600060006000600061100162fffffff4".from_hex().unwrap()).unwrap(); - - let info = EnvInfo::default(); - let machine = ::ethereum::new_constantinople_test_machine(); - let schedule = machine.schedule(info.number); - - assert_eq!(state.storage_at(&operating_address, &k).unwrap(), H256::from(U256::from(0))); - // Test a call via top-level -> y1 -> x1 - let (FinalizationResult { gas_left, .. }, refund, gas) = { - let gas = U256::from(0xffffffffffu64); - let mut params = ActionParams::default(); - params.code = Some(Arc::new("6001600055600060006000600061200163fffffffff4".from_hex().unwrap())); - params.gas = gas; - let mut substate = Substate::new(); - let mut ex = Executive::new(&mut state, &info, &machine, &schedule); - let res = ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap(); - - (res, substate.sstore_clears_refund, gas) - }; - let gas_used = gas - gas_left; - // sstore: 0 -> (1) -> () -> (1 -> 0 -> 1) - assert_eq!(gas_used, U256::from(41860)); - assert_eq!(refund, 19800); - - assert_eq!(state.storage_at(&operating_address, &k).unwrap(), H256::from(U256::from(1))); - // Test a call via top-level -> y2 -> x2 - let (FinalizationResult { gas_left, .. }, refund, gas) = { - let gas = U256::from(0xffffffffffu64); - let mut params = ActionParams::default(); - params.code = Some(Arc::new("6001600055600060006000600061200263fffffffff4".from_hex().unwrap())); - params.gas = gas; - let mut substate = Substate::new(); - let mut ex = Executive::new(&mut state, &info, &machine, &schedule); - let res = ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap(); - - (res, substate.sstore_clears_refund, gas) - }; - let gas_used = gas - gas_left; - // sstore: 1 -> (1) -> () -> (0 -> 1 -> 0) - assert_eq!(gas_used, U256::from(11860)); - assert_eq!(refund, 19800); - } - - fn wasm_sample_code() -> Arc> { - Arc::new( + assert_eq!(vm_tracer.drain().unwrap(), expected_vm_trace); + } + + #[test] + fn test_trace_reverted_create() { + // code: + // + // 65 60016000fd - push 5 bytes + // 60 00 - push 0 + // 52 mstore + // 60 05 - push 5 + // 60 1b - push 27 + // 60 17 - push 23 + // f0 - create + // 60 00 - push 0 + // 55 sstore + // + // other code: + // + // 60 01 + // 60 00 + // fd - revert + + let code = "6460016000fd6000526005601b6017f0600055".from_hex().unwrap(); + + let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); + let address = contract_address( + CreateContractAddress::FromSenderAndNonce, + &sender, + &U256::zero(), + &[], + ) + .0; + let mut params = ActionParams::default(); + params.address = address.clone(); + params.code_address = address.clone(); + params.sender = sender.clone(); + params.origin = sender.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + params.value = ActionValue::Transfer(U256::from(100)); + params.call_type = CallType::Call; + let mut state = get_temp_state(); + state + .add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty) + .unwrap(); + let info = EnvInfo::default(); + let machine = ::ethereum::new_byzantium_test_machine(); + let schedule = machine.schedule(info.number); + let mut substate = Substate::new(); + let mut tracer = ExecutiveTracer::default(); + let mut vm_tracer = ExecutiveVMTracer::toplevel(); + + let FinalizationResult { gas_left, .. } = { + let mut ex = Executive::new(&mut state, &info, &machine, &schedule); + ex.call(params, &mut substate, &mut tracer, &mut vm_tracer) + .unwrap() + }; + + assert_eq!(gas_left, U256::from(62967)); + + let expected_trace = vec![ + FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "cd1722f3947def4cf144679da39c4c32bdc35681".into(), + to: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(), + value: 100.into(), + gas: 100_000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(37_033), + output: vec![], + }), + }, + FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Create(trace::Create { + from: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(), + value: 23.into(), + gas: 66_917.into(), + init: vec![0x60, 0x01, 0x60, 0x00, 0xfd], + }), + result: trace::Res::FailedCreate(vm::Error::Reverted.into()), + }, + ]; + + assert_eq!(tracer.drain(), expected_trace); + } + + #[test] + fn test_create_contract() { + // Tracing is not supported in JIT + // code: + // + // 60 10 - push 16 + // 80 - duplicate first stack item + // 60 0c - push 12 + // 60 00 - push 0 + // 39 - copy current code to memory + // 60 00 - push 0 + // f3 - return + + let code = "601080600c6000396000f3006000355415600957005b60203560003555" + .from_hex() + .unwrap(); + + let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); + let address = contract_address( + CreateContractAddress::FromSenderAndNonce, + &sender, + &U256::zero(), + &[], + ) + .0; + // TODO: add tests for 'callcreate' + //let next_address = contract_address(&address, &U256::zero()); + let mut params = ActionParams::default(); + params.address = address.clone(); + params.sender = sender.clone(); + params.origin = sender.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + params.value = ActionValue::Transfer(100.into()); + let mut state = get_temp_state(); + state + .add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty) + .unwrap(); + let info = EnvInfo::default(); + let machine = make_frontier_machine(5); + let schedule = machine.schedule(info.number); + let mut substate = Substate::new(); + let mut tracer = ExecutiveTracer::default(); + let mut vm_tracer = ExecutiveVMTracer::toplevel(); + + let FinalizationResult { gas_left, .. } = { + let mut ex = Executive::new(&mut state, &info, &machine, &schedule); + ex.create(params.clone(), &mut substate, &mut tracer, &mut vm_tracer) + .unwrap() + }; + + assert_eq!(gas_left, U256::from(96_776)); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 0, + action: trace::Action::Create(trace::Create { + from: params.sender, + value: 100.into(), + gas: params.gas, + init: vec![ + 96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, + 91, 96, 32, 53, 96, 0, 53, 85, + ], + }), + result: trace::Res::Create(trace::CreateResult { + gas_used: U256::from(3224), + address: params.address, + code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53], + }), + }]; + + assert_eq!(tracer.drain(), expected_trace); + + let expected_vm_trace = VMTrace { + parent_step: 0, + code: vec![ + 96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, + 96, 32, 53, 96, 0, 53, 85, + ], + operations: vec![ + VMOperation { + pc: 0, + instruction: 96, + gas_cost: 3.into(), + executed: Some(VMExecutedOperation { + gas_used: 99997.into(), + stack_push: vec_into![16], + mem_diff: None, + store_diff: None, + }), + }, + VMOperation { + pc: 2, + instruction: 128, + gas_cost: 3.into(), + executed: Some(VMExecutedOperation { + gas_used: 99994.into(), + stack_push: vec_into![16, 16], + mem_diff: None, + store_diff: None, + }), + }, + VMOperation { + pc: 3, + instruction: 96, + gas_cost: 3.into(), + executed: Some(VMExecutedOperation { + gas_used: 99991.into(), + stack_push: vec_into![12], + mem_diff: None, + store_diff: None, + }), + }, + VMOperation { + pc: 5, + instruction: 96, + gas_cost: 3.into(), + executed: Some(VMExecutedOperation { + gas_used: 99988.into(), + stack_push: vec_into![0], + mem_diff: None, + store_diff: None, + }), + }, + VMOperation { + pc: 7, + instruction: 57, + gas_cost: 9.into(), + executed: Some(VMExecutedOperation { + gas_used: 99979.into(), + stack_push: vec_into![], + mem_diff: Some(MemoryDiff { + offset: 0, + data: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53], + }), + store_diff: None, + }), + }, + VMOperation { + pc: 8, + instruction: 96, + gas_cost: 3.into(), + executed: Some(VMExecutedOperation { + gas_used: 99976.into(), + stack_push: vec_into![0], + mem_diff: None, + store_diff: None, + }), + }, + VMOperation { + pc: 10, + instruction: 243, + gas_cost: 0.into(), + executed: Some(VMExecutedOperation { + gas_used: 99976.into(), + stack_push: vec_into![], + mem_diff: None, + store_diff: None, + }), + }, + ], + subs: vec![], + }; + assert_eq!(vm_tracer.drain().unwrap(), expected_vm_trace); + } + + evm_test! {test_create_contract_value_too_high: test_create_contract_value_too_high_int} + fn test_create_contract_value_too_high(factory: Factory) { + // code: + // + // 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes? + // 60 00 - push 0 + // 52 + // 60 1d - push 29 + // 60 03 - push 3 + // 60 e6 - push 230 + // f0 - create a contract trying to send 230. + // 60 00 - push 0 + // 55 sstore + // + // other code: + // + // 60 10 - push 16 + // 80 - duplicate first stack item + // 60 0c - push 12 + // 60 00 - push 0 + // 39 - copy current code to memory + // 60 00 - push 0 + // f3 - return + + let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d600360e6f0600055".from_hex().unwrap(); + + let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); + let address = contract_address( + CreateContractAddress::FromSenderAndNonce, + &sender, + &U256::zero(), + &[], + ) + .0; + // TODO: add tests for 'callcreate' + //let next_address = contract_address(&address, &U256::zero()); + let mut params = ActionParams::default(); + params.address = address.clone(); + params.sender = sender.clone(); + params.origin = sender.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + params.value = ActionValue::Transfer(U256::from(100)); + let mut state = get_temp_state_with_factory(factory); + state + .add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty) + .unwrap(); + let info = EnvInfo::default(); + let machine = make_frontier_machine(0); + let schedule = machine.schedule(info.number); + let mut substate = Substate::new(); + + let FinalizationResult { gas_left, .. } = { + let mut ex = Executive::new(&mut state, &info, &machine, &schedule); + ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) + .unwrap() + }; + + assert_eq!(gas_left, U256::from(62_976)); + assert_eq!(substate.contracts_created.len(), 0); + } + + evm_test! {test_create_contract_without_max_depth: test_create_contract_without_max_depth_int} + fn test_create_contract_without_max_depth(factory: Factory) { + // code: + // + // 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes? + // 60 00 - push 0 + // 52 + // 60 1d - push 29 + // 60 03 - push 3 + // 60 17 - push 17 + // f0 - create + // 60 00 - push 0 + // 55 sstore + // + // other code: + // + // 60 10 - push 16 + // 80 - duplicate first stack item + // 60 0c - push 12 + // 60 00 - push 0 + // 39 - copy current code to memory + // 60 00 - push 0 + // f3 - return + + let code = + "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0" + .from_hex() + .unwrap(); + + let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); + let address = contract_address( + CreateContractAddress::FromSenderAndNonce, + &sender, + &U256::zero(), + &[], + ) + .0; + let next_address = contract_address( + CreateContractAddress::FromSenderAndNonce, + &address, + &U256::zero(), + &[], + ) + .0; + let mut params = ActionParams::default(); + params.address = address.clone(); + params.sender = sender.clone(); + params.origin = sender.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + params.value = ActionValue::Transfer(U256::from(100)); + let mut state = get_temp_state_with_factory(factory); + state + .add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty) + .unwrap(); + let info = EnvInfo::default(); + let machine = make_frontier_machine(1024); + let schedule = machine.schedule(info.number); + let mut substate = Substate::new(); + + { + let mut ex = Executive::new(&mut state, &info, &machine, &schedule); + ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) + .unwrap(); + } + + assert_eq!(substate.contracts_created.len(), 1); + assert_eq!(substate.contracts_created[0], next_address); + } + + // test is incorrect, mk + // TODO: fix (preferred) or remove + evm_test_ignore! {test_aba_calls: test_aba_calls_int} + fn test_aba_calls(factory: Factory) { + // 60 00 - push 0 + // 60 00 - push 0 + // 60 00 - push 0 + // 60 00 - push 0 + // 60 18 - push 18 + // 73 945304eb96065b2a98b57a48a06ae28d285a71b5 - push this address + // 61 03e8 - push 1000 + // f1 - message call + // 58 - get PC + // 55 - sstore + + let code_a = "6000600060006000601873945304eb96065b2a98b57a48a06ae28d285a71b56103e8f15855" + .from_hex() + .unwrap(); + + // 60 00 - push 0 + // 60 00 - push 0 + // 60 00 - push 0 + // 60 00 - push 0 + // 60 17 - push 17 + // 73 0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 - push this address + // 61 0x01f4 - push 500 + // f1 - message call + // 60 01 - push 1 + // 01 - add + // 58 - get PC + // 55 - sstore + let code_b = + "60006000600060006017730f572e5295c57f15886f9b263e2f6d2d6c7b5ec66101f4f16001015855" + .from_hex() + .unwrap(); + + let address_a = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let address_b = Address::from_str("945304eb96065b2a98b57a48a06ae28d285a71b5").unwrap(); + let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); + + let mut params = ActionParams::default(); + params.address = address_a.clone(); + params.sender = sender.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code_a.clone())); + params.value = ActionValue::Transfer(U256::from(100_000)); + + let mut state = get_temp_state_with_factory(factory); + state.init_code(&address_a, code_a.clone()).unwrap(); + state.init_code(&address_b, code_b.clone()).unwrap(); + state + .add_balance(&sender, &U256::from(100_000), CleanupMode::NoEmpty) + .unwrap(); + + let info = EnvInfo::default(); + let machine = make_frontier_machine(0); + let schedule = machine.schedule(info.number); + let mut substate = Substate::new(); + + let FinalizationResult { gas_left, .. } = { + let mut ex = Executive::new(&mut state, &info, &machine, &schedule); + ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) + .unwrap() + }; + + assert_eq!(gas_left, U256::from(73_237)); + assert_eq!( + state + .storage_at(&address_a, &H256::from(&U256::from(0x23))) + .unwrap(), + H256::from(&U256::from(1)) + ); + } + + // test is incorrect, mk + // TODO: fix (preferred) or remove + evm_test_ignore! {test_recursive_bomb1: test_recursive_bomb1_int} + fn test_recursive_bomb1(factory: Factory) { + // 60 01 - push 1 + // 60 00 - push 0 + // 54 - sload + // 01 - add + // 60 00 - push 0 + // 55 - sstore + // 60 00 - push 0 + // 60 00 - push 0 + // 60 00 - push 0 + // 60 00 - push 0 + // 60 00 - push 0 + // 30 - load address + // 60 e0 - push e0 + // 5a - get gas + // 03 - sub + // f1 - message call (self in this case) + // 60 01 - push 1 + // 55 - sstore + let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); + let code = "600160005401600055600060006000600060003060e05a03f1600155" + .from_hex() + .unwrap(); + let address = contract_address( + CreateContractAddress::FromSenderAndNonce, + &sender, + &U256::zero(), + &[], + ) + .0; + let mut params = ActionParams::default(); + params.address = address.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code.clone())); + let mut state = get_temp_state_with_factory(factory); + state.init_code(&address, code).unwrap(); + let info = EnvInfo::default(); + let machine = make_frontier_machine(0); + let schedule = machine.schedule(info.number); + let mut substate = Substate::new(); + + let FinalizationResult { gas_left, .. } = { + let mut ex = Executive::new(&mut state, &info, &machine, &schedule); + ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) + .unwrap() + }; + + assert_eq!(gas_left, U256::from(59_870)); + assert_eq!( + state + .storage_at(&address, &H256::from(&U256::zero())) + .unwrap(), + H256::from(&U256::from(1)) + ); + assert_eq!( + state + .storage_at(&address, &H256::from(&U256::one())) + .unwrap(), + H256::from(&U256::from(1)) + ); + } + + // test is incorrect, mk + // TODO: fix (preferred) or remove + evm_test_ignore! {test_transact_simple: test_transact_simple_int} + fn test_transact_simple(factory: Factory) { + let keypair = Random.generate().unwrap(); + let t = Transaction { + action: Action::Create, + value: U256::from(17), + data: "3331600055".from_hex().unwrap(), + gas: U256::from(100_000), + gas_price: U256::zero(), + nonce: U256::zero(), + } + .sign(keypair.secret(), None); + let sender = t.sender(); + let contract = contract_address( + CreateContractAddress::FromSenderAndNonce, + &sender, + &U256::zero(), + &[], + ) + .0; + + let mut state = get_temp_state_with_factory(factory); + state + .add_balance(&sender, &U256::from(18), CleanupMode::NoEmpty) + .unwrap(); + let mut info = EnvInfo::default(); + info.gas_limit = U256::from(100_000); + let machine = make_frontier_machine(0); + let schedule = machine.schedule(info.number); + + let executed = { + let mut ex = Executive::new(&mut state, &info, &machine, &schedule); + let opts = TransactOptions::with_no_tracing(); + ex.transact(&t, opts).unwrap() + }; + + assert_eq!(executed.gas, U256::from(100_000)); + assert_eq!(executed.gas_used, U256::from(41_301)); + assert_eq!(executed.refunded, U256::from(58_699)); + assert_eq!(executed.cumulative_gas_used, U256::from(41_301)); + assert_eq!(executed.logs.len(), 0); + assert_eq!(executed.contracts_created.len(), 0); + assert_eq!(state.balance(&sender).unwrap(), U256::from(1)); + assert_eq!(state.balance(&contract).unwrap(), U256::from(17)); + assert_eq!(state.nonce(&sender).unwrap(), U256::from(1)); + assert_eq!( + state.storage_at(&contract, &H256::new()).unwrap(), + H256::from(&U256::from(1)) + ); + } + + evm_test! {test_transact_invalid_nonce: test_transact_invalid_nonce_int} + fn test_transact_invalid_nonce(factory: Factory) { + let keypair = Random.generate().unwrap(); + let t = Transaction { + action: Action::Create, + value: U256::from(17), + data: "3331600055".from_hex().unwrap(), + gas: U256::from(100_000), + gas_price: U256::zero(), + nonce: U256::one(), + } + .sign(keypair.secret(), None); + let sender = t.sender(); + + let mut state = get_temp_state_with_factory(factory); + state + .add_balance(&sender, &U256::from(17), CleanupMode::NoEmpty) + .unwrap(); + let mut info = EnvInfo::default(); + info.gas_limit = U256::from(100_000); + let machine = make_frontier_machine(0); + let schedule = machine.schedule(info.number); + + let res = { + let mut ex = Executive::new(&mut state, &info, &machine, &schedule); + let opts = TransactOptions::with_no_tracing(); + ex.transact(&t, opts) + }; + + match res { + Err(ExecutionError::InvalidNonce { expected, got }) + if expected == U256::zero() && got == U256::one() => + { + () + } + _ => assert!(false, "Expected invalid nonce error."), + } + } + + evm_test! {test_transact_gas_limit_reached: test_transact_gas_limit_reached_int} + fn test_transact_gas_limit_reached(factory: Factory) { + let keypair = Random.generate().unwrap(); + let t = Transaction { + action: Action::Create, + value: U256::from(17), + data: "3331600055".from_hex().unwrap(), + gas: U256::from(80_001), + gas_price: U256::zero(), + nonce: U256::zero(), + } + .sign(keypair.secret(), None); + let sender = t.sender(); + + let mut state = get_temp_state_with_factory(factory); + state + .add_balance(&sender, &U256::from(17), CleanupMode::NoEmpty) + .unwrap(); + let mut info = EnvInfo::default(); + info.gas_used = U256::from(20_000); + info.gas_limit = U256::from(100_000); + let machine = make_frontier_machine(0); + let schedule = machine.schedule(info.number); + + let res = { + let mut ex = Executive::new(&mut state, &info, &machine, &schedule); + let opts = TransactOptions::with_no_tracing(); + ex.transact(&t, opts) + }; + + match res { + Err(ExecutionError::BlockGasLimitReached { + gas_limit, + gas_used, + gas, + }) if gas_limit == U256::from(100_000) + && gas_used == U256::from(20_000) + && gas == U256::from(80_001) => + { + () + } + _ => assert!(false, "Expected block gas limit error."), + } + } + + evm_test! {test_not_enough_cash: test_not_enough_cash_int} + fn test_not_enough_cash(factory: Factory) { + let keypair = Random.generate().unwrap(); + let t = Transaction { + action: Action::Create, + value: U256::from(18), + data: "3331600055".from_hex().unwrap(), + gas: U256::from(100_000), + gas_price: U256::one(), + nonce: U256::zero(), + } + .sign(keypair.secret(), None); + let sender = t.sender(); + + let mut state = get_temp_state_with_factory(factory); + state + .add_balance(&sender, &U256::from(100_017), CleanupMode::NoEmpty) + .unwrap(); + let mut info = EnvInfo::default(); + info.gas_limit = U256::from(100_000); + let machine = make_frontier_machine(0); + let schedule = machine.schedule(info.number); + + let res = { + let mut ex = Executive::new(&mut state, &info, &machine, &schedule); + let opts = TransactOptions::with_no_tracing(); + ex.transact(&t, opts) + }; + + match res { + Err(ExecutionError::NotEnoughCash { required, got }) + if required == U512::from(100_018) && got == U512::from(100_017) => + { + () + } + _ => assert!(false, "Expected not enough cash error. {:?}", res), + } + } + + evm_test! {test_keccak: test_keccak_int} + fn test_keccak(factory: Factory) { + let code = "6064640fffffffff20600055".from_hex().unwrap(); + + let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let address = contract_address( + CreateContractAddress::FromSenderAndNonce, + &sender, + &U256::zero(), + &[], + ) + .0; + // TODO: add tests for 'callcreate' + //let next_address = contract_address(&address, &U256::zero()); + let mut params = ActionParams::default(); + params.address = address.clone(); + params.sender = sender.clone(); + params.origin = sender.clone(); + params.gas = U256::from(0x0186a0); + params.code = Some(Arc::new(code)); + params.value = ActionValue::Transfer(U256::from_str("0de0b6b3a7640000").unwrap()); + let mut state = get_temp_state_with_factory(factory); + state + .add_balance( + &sender, + &U256::from_str("152d02c7e14af6800000").unwrap(), + CleanupMode::NoEmpty, + ) + .unwrap(); + let info = EnvInfo::default(); + let machine = make_frontier_machine(0); + let schedule = machine.schedule(info.number); + let mut substate = Substate::new(); + + let result = { + let mut ex = Executive::new(&mut state, &info, &machine, &schedule); + ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) + }; + + match result { + Err(_) => {} + _ => panic!("Expected OutOfGas"), + } + } + + evm_test! {test_revert: test_revert_int} + fn test_revert(factory: Factory) { + let contract_address = + Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); + let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + // EIP-140 test case + let code = "6c726576657274656420646174616000557f726576657274206d657373616765000000000000000000000000000000000000600052600e6000fd".from_hex().unwrap(); + let returns = "726576657274206d657373616765".from_hex().unwrap(); + let mut state = get_temp_state_with_factory(factory.clone()); + state + .add_balance( + &sender, + &U256::from_str("152d02c7e14af68000000").unwrap(), + CleanupMode::NoEmpty, + ) + .unwrap(); + state.commit().unwrap(); + + let mut params = ActionParams::default(); + params.address = contract_address.clone(); + params.sender = sender.clone(); + params.origin = sender.clone(); + params.gas = U256::from(20025); + params.code = Some(Arc::new(code)); + params.value = ActionValue::Transfer(U256::zero()); + let info = EnvInfo::default(); + let machine = ::ethereum::new_byzantium_test_machine(); + let schedule = machine.schedule(info.number); + let mut substate = Substate::new(); + + let mut output = [0u8; 14]; + let FinalizationResult { + gas_left: result, + return_data, + .. + } = { + let mut ex = Executive::new(&mut state, &info, &machine, &schedule); + ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) + .unwrap() + }; + (&mut output).copy_from_slice(&return_data[..(cmp::min(14, return_data.len()))]); + + assert_eq!(result, U256::from(1)); + assert_eq!(output[..], returns[..]); + assert_eq!( + state + .storage_at(&contract_address, &H256::from(&U256::zero())) + .unwrap(), + H256::from(&U256::from(0)) + ); + } + + evm_test! {test_eip1283: test_eip1283_int} + fn test_eip1283(factory: Factory) { + let x1 = Address::from(0x1000); + let x2 = Address::from(0x1001); + let y1 = Address::from(0x2001); + let y2 = Address::from(0x2002); + let operating_address = Address::from(0); + let k = H256::new(); + + let mut state = get_temp_state_with_factory(factory.clone()); + state + .new_contract(&x1, U256::zero(), U256::from(1)) + .unwrap(); + state + .init_code(&x1, "600160005560006000556001600055".from_hex().unwrap()) + .unwrap(); + state + .new_contract(&x2, U256::zero(), U256::from(1)) + .unwrap(); + state + .init_code(&x2, "600060005560016000556000600055".from_hex().unwrap()) + .unwrap(); + state + .new_contract(&y1, U256::zero(), U256::from(1)) + .unwrap(); + state + .init_code(&y1, "600060006000600061100062fffffff4".from_hex().unwrap()) + .unwrap(); + state + .new_contract(&y2, U256::zero(), U256::from(1)) + .unwrap(); + state + .init_code(&y2, "600060006000600061100162fffffff4".from_hex().unwrap()) + .unwrap(); + + let info = EnvInfo::default(); + let machine = ::ethereum::new_constantinople_test_machine(); + let schedule = machine.schedule(info.number); + + assert_eq!( + state.storage_at(&operating_address, &k).unwrap(), + H256::from(U256::from(0)) + ); + // Test a call via top-level -> y1 -> x1 + let (FinalizationResult { gas_left, .. }, refund, gas) = { + let gas = U256::from(0xffffffffffu64); + let mut params = ActionParams::default(); + params.code = Some(Arc::new( + "6001600055600060006000600061200163fffffffff4" + .from_hex() + .unwrap(), + )); + params.gas = gas; + let mut substate = Substate::new(); + let mut ex = Executive::new(&mut state, &info, &machine, &schedule); + let res = ex + .call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) + .unwrap(); + + (res, substate.sstore_clears_refund, gas) + }; + let gas_used = gas - gas_left; + // sstore: 0 -> (1) -> () -> (1 -> 0 -> 1) + assert_eq!(gas_used, U256::from(41860)); + assert_eq!(refund, 19800); + + assert_eq!( + state.storage_at(&operating_address, &k).unwrap(), + H256::from(U256::from(1)) + ); + // Test a call via top-level -> y2 -> x2 + let (FinalizationResult { gas_left, .. }, refund, gas) = { + let gas = U256::from(0xffffffffffu64); + let mut params = ActionParams::default(); + params.code = Some(Arc::new( + "6001600055600060006000600061200263fffffffff4" + .from_hex() + .unwrap(), + )); + params.gas = gas; + let mut substate = Substate::new(); + let mut ex = Executive::new(&mut state, &info, &machine, &schedule); + let res = ex + .call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) + .unwrap(); + + (res, substate.sstore_clears_refund, gas) + }; + let gas_used = gas - gas_left; + // sstore: 1 -> (1) -> () -> (0 -> 1 -> 0) + assert_eq!(gas_used, U256::from(11860)); + assert_eq!(refund, 19800); + } + + fn wasm_sample_code() -> Arc> { + Arc::new( "0061736d01000000010d0360027f7f0060017f0060000002270303656e7603726574000003656e760673656e646572000103656e76066d656d6f727902010110030201020404017000000501000708010463616c6c00020901000ac10101be0102057f017e4100410028020441c0006b22043602042004412c6a41106a220041003602002004412c6a41086a22014200370200200441186a41106a22024100360200200441186a41086a220342003703002004420037022c2004410036021c20044100360218200441186a1001200020022802002202360200200120032903002205370200200441106a2002360200200441086a200537030020042004290318220537022c200420053703002004411410004100200441c0006a3602040b0b0a010041040b0410c00000" .from_hex() .unwrap() ) - } - - #[test] - fn wasm_activated_test() { - let contract_address = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - - let mut state = get_temp_state(); - state.add_balance(&sender, &U256::from(10000000000u64), CleanupMode::NoEmpty).unwrap(); - state.commit().unwrap(); - - let mut params = ActionParams::default(); - params.origin = sender.clone(); - params.sender = sender.clone(); - params.address = contract_address.clone(); - params.gas = U256::from(20025); - params.code = Some(wasm_sample_code()); - - let mut info = EnvInfo::default(); - - // 100 > 10 - info.number = 100; - - // Network with wasm activated at block 10 - let machine = ::ethereum::new_kovan_wasm_test_machine(); - - let mut output = [0u8; 20]; - let FinalizationResult { gas_left: result, return_data, .. } = { - let schedule = machine.schedule(info.number); - let mut ex = Executive::new(&mut state, &info, &machine, &schedule); - ex.call(params.clone(), &mut Substate::new(), &mut NoopTracer, &mut NoopVMTracer).unwrap() - }; - (&mut output).copy_from_slice(&return_data[..(cmp::min(20, return_data.len()))]); - - assert_eq!(result, U256::from(18433)); - // Transaction successfully returned sender - assert_eq!(output[..], sender[..]); - - // 1 < 10 - info.number = 1; - - let mut output = [0u8; 20]; - let FinalizationResult { gas_left: result, return_data, .. } = { - let schedule = machine.schedule(info.number); - let mut ex = Executive::new(&mut state, &info, &machine, &schedule); - ex.call(params, &mut Substate::new(), &mut NoopTracer, &mut NoopVMTracer).unwrap() - }; - (&mut output[..((cmp::min(20, return_data.len())))]).copy_from_slice(&return_data[..(cmp::min(20, return_data.len()))]); - - assert_eq!(result, U256::from(20025)); - // Since transaction errored due to wasm was not activated, result is just empty - assert_eq!(output[..], [0u8; 20][..]); - } + } + + #[test] + fn wasm_activated_test() { + let contract_address = + Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); + let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + + let mut state = get_temp_state(); + state + .add_balance(&sender, &U256::from(10000000000u64), CleanupMode::NoEmpty) + .unwrap(); + state.commit().unwrap(); + + let mut params = ActionParams::default(); + params.origin = sender.clone(); + params.sender = sender.clone(); + params.address = contract_address.clone(); + params.gas = U256::from(20025); + params.code = Some(wasm_sample_code()); + + let mut info = EnvInfo::default(); + + // 100 > 10 + info.number = 100; + + // Network with wasm activated at block 10 + let machine = ::ethereum::new_kovan_wasm_test_machine(); + + let mut output = [0u8; 20]; + let FinalizationResult { + gas_left: result, + return_data, + .. + } = { + let schedule = machine.schedule(info.number); + let mut ex = Executive::new(&mut state, &info, &machine, &schedule); + ex.call( + params.clone(), + &mut Substate::new(), + &mut NoopTracer, + &mut NoopVMTracer, + ) + .unwrap() + }; + (&mut output).copy_from_slice(&return_data[..(cmp::min(20, return_data.len()))]); + + assert_eq!(result, U256::from(18433)); + // Transaction successfully returned sender + assert_eq!(output[..], sender[..]); + + // 1 < 10 + info.number = 1; + + let mut output = [0u8; 20]; + let FinalizationResult { + gas_left: result, + return_data, + .. + } = { + let schedule = machine.schedule(info.number); + let mut ex = Executive::new(&mut state, &info, &machine, &schedule); + ex.call( + params, + &mut Substate::new(), + &mut NoopTracer, + &mut NoopVMTracer, + ) + .unwrap() + }; + (&mut output[..(cmp::min(20, return_data.len()))]) + .copy_from_slice(&return_data[..(cmp::min(20, return_data.len()))]); + + assert_eq!(result, U256::from(20025)); + // Since transaction errored due to wasm was not activated, result is just empty + assert_eq!(output[..], [0u8; 20][..]); + } } diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index 23a4a83c3d1..46e77c83bfd 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -1,638 +1,879 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Transaction Execution environment. -use std::cmp; -use std::sync::Arc; -use ethereum_types::{H256, U256, Address}; use bytes::Bytes; -use state::{Backend as StateBackend, State, Substate, CleanupMode}; -use machine::EthereumMachine as Machine; +use ethereum_types::{Address, H256, U256}; use executive::*; +use machine::EthereumMachine as Machine; +use state::{Backend as StateBackend, CleanupMode, State, Substate}; +use std::{cmp, sync::Arc}; +use trace::{Tracer, VMTracer}; +use types::transaction::UNSIGNED_SENDER; use vm::{ - self, ActionParams, ActionValue, EnvInfo, CallType, Schedule, - Ext, ContractCreateResult, MessageCallResult, CreateContractAddress, - ReturnData, TrapKind + self, ActionParams, ActionValue, CallType, ContractCreateResult, CreateContractAddress, + EnvInfo, Ext, MessageCallResult, ReturnData, Schedule, TrapKind, }; -use types::transaction::UNSIGNED_SENDER; -use trace::{Tracer, VMTracer}; /// Policy for handling output data on `RETURN` opcode. pub enum OutputPolicy { - /// Return reference to fixed sized output. - /// Used for message calls. - Return, - /// Init new contract as soon as `RETURN` is called. - InitContract, + /// Return reference to fixed sized output. + /// Used for message calls. + Return, + /// Init new contract as soon as `RETURN` is called. + InitContract, } /// Transaction properties that externalities need to know about. pub struct OriginInfo { - address: Address, - origin: Address, - gas_price: U256, - value: U256, + address: Address, + origin: Address, + gas_price: U256, + value: U256, } impl OriginInfo { - /// Populates origin info from action params. - pub fn from(params: &ActionParams) -> Self { - OriginInfo { - address: params.address.clone(), - origin: params.origin.clone(), - gas_price: params.gas_price, - value: match params.value { - ActionValue::Transfer(val) | ActionValue::Apparent(val) => val - }, - } - } + /// Populates origin info from action params. + pub fn from(params: &ActionParams) -> Self { + OriginInfo { + address: params.address.clone(), + origin: params.origin.clone(), + gas_price: params.gas_price, + value: match params.value { + ActionValue::Transfer(val) | ActionValue::Apparent(val) => val, + }, + } + } } /// Implementation of evm Externalities. pub struct Externalities<'a, T: 'a, V: 'a, B: 'a> { - state: &'a mut State, - env_info: &'a EnvInfo, - depth: usize, - stack_depth: usize, - origin_info: &'a OriginInfo, - substate: &'a mut Substate, - machine: &'a Machine, - schedule: &'a Schedule, - output: OutputPolicy, - tracer: &'a mut T, - vm_tracer: &'a mut V, - static_flag: bool, + state: &'a mut State, + env_info: &'a EnvInfo, + depth: usize, + stack_depth: usize, + origin_info: &'a OriginInfo, + substate: &'a mut Substate, + machine: &'a Machine, + schedule: &'a Schedule, + output: OutputPolicy, + tracer: &'a mut T, + vm_tracer: &'a mut V, + static_flag: bool, } impl<'a, T: 'a, V: 'a, B: 'a> Externalities<'a, T, V, B> - where T: Tracer, V: VMTracer, B: StateBackend +where + T: Tracer, + V: VMTracer, + B: StateBackend, { - /// Basic `Externalities` constructor. - pub fn new( - state: &'a mut State, - env_info: &'a EnvInfo, - machine: &'a Machine, - schedule: &'a Schedule, - depth: usize, - stack_depth: usize, - origin_info: &'a OriginInfo, - substate: &'a mut Substate, - output: OutputPolicy, - tracer: &'a mut T, - vm_tracer: &'a mut V, - static_flag: bool, - ) -> Self { - Externalities { - state: state, - env_info: env_info, - depth: depth, - stack_depth: stack_depth, - origin_info: origin_info, - substate: substate, - machine: machine, - schedule: schedule, - output: output, - tracer: tracer, - vm_tracer: vm_tracer, - static_flag: static_flag, - } - } + /// Basic `Externalities` constructor. + pub fn new( + state: &'a mut State, + env_info: &'a EnvInfo, + machine: &'a Machine, + schedule: &'a Schedule, + depth: usize, + stack_depth: usize, + origin_info: &'a OriginInfo, + substate: &'a mut Substate, + output: OutputPolicy, + tracer: &'a mut T, + vm_tracer: &'a mut V, + static_flag: bool, + ) -> Self { + Externalities { + state: state, + env_info: env_info, + depth: depth, + stack_depth: stack_depth, + origin_info: origin_info, + substate: substate, + machine: machine, + schedule: schedule, + output: output, + tracer: tracer, + vm_tracer: vm_tracer, + static_flag: static_flag, + } + } } impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> - where T: Tracer, V: VMTracer, B: StateBackend +where + T: Tracer, + V: VMTracer, + B: StateBackend, { - fn initial_storage_at(&self, key: &H256) -> vm::Result { - if self.state.is_base_storage_root_unchanged(&self.origin_info.address)? { - self.state.checkpoint_storage_at(0, &self.origin_info.address, key).map(|v| v.unwrap_or_default()).map_err(Into::into) - } else { - warn!(target: "externalities", "Detected existing account {:#x} where a forced contract creation happened.", self.origin_info.address); - Ok(H256::zero()) - } - } - - fn storage_at(&self, key: &H256) -> vm::Result { - self.state.storage_at(&self.origin_info.address, key).map_err(Into::into) - } - - fn set_storage(&mut self, key: H256, value: H256) -> vm::Result<()> { - if self.static_flag { - Err(vm::Error::MutableCallInStaticContext) - } else { - self.state.set_storage(&self.origin_info.address, key, value).map_err(Into::into) - } - } - - fn is_static(&self) -> bool { - return self.static_flag - } - - fn exists(&self, address: &Address) -> vm::Result { - self.state.exists(address).map_err(Into::into) - } - - fn exists_and_not_null(&self, address: &Address) -> vm::Result { - self.state.exists_and_not_null(address).map_err(Into::into) - } - - fn origin_balance(&self) -> vm::Result { - self.balance(&self.origin_info.address).map_err(Into::into) - } - - fn balance(&self, address: &Address) -> vm::Result { - self.state.balance(address).map_err(Into::into) - } - - fn blockhash(&mut self, number: &U256) -> H256 { - if self.env_info.number + 256 >= self.machine.params().eip210_transition { - let blockhash_contract_address = self.machine.params().eip210_contract_address; - let code_res = self.state.code(&blockhash_contract_address) - .and_then(|code| self.state.code_hash(&blockhash_contract_address).map(|hash| (code, hash))); - - let (code, code_hash) = match code_res { - Ok((code, hash)) => (code, hash), - Err(_) => return H256::zero(), - }; - - let params = ActionParams { - sender: self.origin_info.address.clone(), - address: blockhash_contract_address.clone(), - value: ActionValue::Apparent(self.origin_info.value), - code_address: blockhash_contract_address.clone(), - origin: self.origin_info.origin.clone(), - gas: self.machine.params().eip210_contract_gas, - gas_price: 0.into(), - code: code, - code_hash: code_hash, - data: Some(H256::from(number).to_vec()), - call_type: CallType::Call, - params_type: vm::ParamsType::Separate, - }; - - let mut ex = Executive::new(self.state, self.env_info, self.machine, self.schedule); - let r = ex.call_with_crossbeam(params, self.substate, self.stack_depth + 1, self.tracer, self.vm_tracer); - let output = match &r { - Ok(ref r) => H256::from(&r.return_data[..32]), - _ => H256::new(), - }; - trace!("ext: blockhash contract({}) -> {:?}({}) self.env_info.number={}\n", number, r, output, self.env_info.number); - output - } else { - // TODO: comment out what this function expects from env_info, since it will produce panics if the latter is inconsistent - match *number < U256::from(self.env_info.number) && number.low_u64() >= cmp::max(256, self.env_info.number) - 256 { - true => { - let index = self.env_info.number - number.low_u64() - 1; - assert!(index < self.env_info.last_hashes.len() as u64, format!("Inconsistent env_info, should contain at least {:?} last hashes", index+1)); - let r = self.env_info.last_hashes[index as usize].clone(); - trace!("ext: blockhash({}) -> {} self.env_info.number={}\n", number, r, self.env_info.number); - r - }, - false => { - trace!("ext: blockhash({}) -> null self.env_info.number={}\n", number, self.env_info.number); - H256::zero() - }, - } - } - } - - fn create( - &mut self, - gas: &U256, - value: &U256, - code: &[u8], - address_scheme: CreateContractAddress, - trap: bool, - ) -> ::std::result::Result { - // create new contract address - let (address, code_hash) = match self.state.nonce(&self.origin_info.address) { - Ok(nonce) => contract_address(address_scheme, &self.origin_info.address, &nonce, &code), - Err(e) => { - debug!(target: "ext", "Database corruption encountered: {:?}", e); - return Ok(ContractCreateResult::Failed) - } - }; - - // prepare the params - let params = ActionParams { - code_address: address.clone(), - address: address.clone(), - sender: self.origin_info.address.clone(), - origin: self.origin_info.origin.clone(), - gas: *gas, - gas_price: self.origin_info.gas_price, - value: ActionValue::Transfer(*value), - code: Some(Arc::new(code.to_vec())), - code_hash: code_hash, - data: None, - call_type: CallType::None, - params_type: vm::ParamsType::Embedded, - }; - - if !self.static_flag { - if !self.schedule.keep_unsigned_nonce || params.sender != UNSIGNED_SENDER { - if let Err(e) = self.state.inc_nonce(&self.origin_info.address) { - debug!(target: "ext", "Database corruption encountered: {:?}", e); - return Ok(ContractCreateResult::Failed) - } - } - } - - if trap { - return Err(TrapKind::Create(params, address)); - } - - // TODO: handle internal error separately - let mut ex = Executive::from_parent(self.state, self.env_info, self.machine, self.schedule, self.depth, self.static_flag); - let out = ex.create_with_crossbeam(params, self.substate, self.stack_depth + 1, self.tracer, self.vm_tracer); - Ok(into_contract_create_result(out, &address, self.substate)) - } - - fn call( - &mut self, - gas: &U256, - sender_address: &Address, - receive_address: &Address, - value: Option, - data: &[u8], - code_address: &Address, - call_type: CallType, - trap: bool, - ) -> ::std::result::Result { - trace!(target: "externalities", "call"); - - let code_res = self.state.code(code_address) - .and_then(|code| self.state.code_hash(code_address).map(|hash| (code, hash))); - - let (code, code_hash) = match code_res { - Ok((code, hash)) => (code, hash), - Err(_) => return Ok(MessageCallResult::Failed), - }; - - let mut params = ActionParams { - sender: sender_address.clone(), - address: receive_address.clone(), - value: ActionValue::Apparent(self.origin_info.value), - code_address: code_address.clone(), - origin: self.origin_info.origin.clone(), - gas: *gas, - gas_price: self.origin_info.gas_price, - code: code, - code_hash: code_hash, - data: Some(data.to_vec()), - call_type: call_type, - params_type: vm::ParamsType::Separate, - }; - - if let Some(value) = value { - params.value = ActionValue::Transfer(value); - } - - if trap { - return Err(TrapKind::Call(params)); - } - - let mut ex = Executive::from_parent(self.state, self.env_info, self.machine, self.schedule, self.depth, self.static_flag); - let out = ex.call_with_crossbeam(params, self.substate, self.stack_depth + 1, self.tracer, self.vm_tracer); - Ok(into_message_call_result(out)) - } - - fn extcode(&self, address: &Address) -> vm::Result>> { - Ok(self.state.code(address)?) - } - - fn extcodehash(&self, address: &Address) -> vm::Result> { - Ok(self.state.code_hash(address)?) - } - - fn extcodesize(&self, address: &Address) -> vm::Result> { - Ok(self.state.code_size(address)?) - } - - fn ret(self, gas: &U256, data: &ReturnData, apply_state: bool) -> vm::Result - where Self: Sized { - match self.output { - OutputPolicy::Return => { - Ok(*gas) - }, - OutputPolicy::InitContract if apply_state => { - let return_cost = U256::from(data.len()) * U256::from(self.schedule.create_data_gas); - if return_cost > *gas || data.len() > self.schedule.create_data_limit { - return match self.schedule.exceptional_failed_code_deposit { - true => Err(vm::Error::OutOfGas), - false => Ok(*gas) - } - } - self.state.init_code(&self.origin_info.address, data.to_vec())?; - Ok(*gas - return_cost) - }, - OutputPolicy::InitContract => { - Ok(*gas) - }, - } - } - - fn log(&mut self, topics: Vec, data: &[u8]) -> vm::Result<()> { - use types::log_entry::LogEntry; - - if self.static_flag { - return Err(vm::Error::MutableCallInStaticContext); - } - - let address = self.origin_info.address.clone(); - self.substate.logs.push(LogEntry { - address: address, - topics: topics, - data: data.to_vec() - }); - - Ok(()) - } - - fn suicide(&mut self, refund_address: &Address) -> vm::Result<()> { - if self.static_flag { - return Err(vm::Error::MutableCallInStaticContext); - } - - let address = self.origin_info.address.clone(); - let balance = self.balance(&address)?; - if &address == refund_address { - // TODO [todr] To be consistent with CPP client we set balance to 0 in that case. - self.state.sub_balance(&address, &balance, &mut CleanupMode::NoEmpty)?; - } else { - trace!(target: "ext", "Suiciding {} -> {} (xfer: {})", address, refund_address, balance); - self.state.transfer_balance( - &address, - refund_address, - &balance, - self.substate.to_cleanup_mode(&self.schedule) - )?; - } - - self.tracer.trace_suicide(address, balance, refund_address.clone()); - self.substate.suicides.insert(address); - - Ok(()) - } - - fn schedule(&self) -> &Schedule { - &self.schedule - } - - fn env_info(&self) -> &EnvInfo { - self.env_info - } - - fn depth(&self) -> usize { - self.depth - } - - fn add_sstore_refund(&mut self, value: usize) { - self.substate.sstore_clears_refund += value as i128; - } - - fn sub_sstore_refund(&mut self, value: usize) { - self.substate.sstore_clears_refund -= value as i128; - } - - fn trace_next_instruction(&mut self, pc: usize, instruction: u8, current_gas: U256) -> bool { - self.vm_tracer.trace_next_instruction(pc, instruction, current_gas) - } - - fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: U256, mem_written: Option<(usize, usize)>, store_written: Option<(U256, U256)>) { - self.vm_tracer.trace_prepare_execute(pc, instruction, gas_cost, mem_written, store_written) - } - - fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) { - self.vm_tracer.trace_executed(gas_used, stack_push, mem) - } + fn initial_storage_at(&self, key: &H256) -> vm::Result { + if self + .state + .is_base_storage_root_unchanged(&self.origin_info.address)? + { + self.state + .checkpoint_storage_at(0, &self.origin_info.address, key) + .map(|v| v.unwrap_or_default()) + .map_err(Into::into) + } else { + warn!(target: "externalities", "Detected existing account {:#x} where a forced contract creation happened.", self.origin_info.address); + Ok(H256::zero()) + } + } + + fn storage_at(&self, key: &H256) -> vm::Result { + self.state + .storage_at(&self.origin_info.address, key) + .map_err(Into::into) + } + + fn set_storage(&mut self, key: H256, value: H256) -> vm::Result<()> { + if self.static_flag { + Err(vm::Error::MutableCallInStaticContext) + } else { + self.state + .set_storage(&self.origin_info.address, key, value) + .map_err(Into::into) + } + } + + fn is_static(&self) -> bool { + return self.static_flag; + } + + fn exists(&self, address: &Address) -> vm::Result { + self.state.exists(address).map_err(Into::into) + } + + fn exists_and_not_null(&self, address: &Address) -> vm::Result { + self.state.exists_and_not_null(address).map_err(Into::into) + } + + fn origin_balance(&self) -> vm::Result { + self.balance(&self.origin_info.address).map_err(Into::into) + } + + fn balance(&self, address: &Address) -> vm::Result { + self.state.balance(address).map_err(Into::into) + } + + fn blockhash(&mut self, number: &U256) -> H256 { + if self.env_info.number + 256 >= self.machine.params().eip210_transition { + let blockhash_contract_address = self.machine.params().eip210_contract_address; + let code_res = self + .state + .code(&blockhash_contract_address) + .and_then(|code| { + self.state + .code_hash(&blockhash_contract_address) + .map(|hash| (code, hash)) + }); + + let (code, code_hash) = match code_res { + Ok((code, hash)) => (code, hash), + Err(_) => return H256::zero(), + }; + + let params = ActionParams { + sender: self.origin_info.address.clone(), + address: blockhash_contract_address.clone(), + value: ActionValue::Apparent(self.origin_info.value), + code_address: blockhash_contract_address.clone(), + origin: self.origin_info.origin.clone(), + gas: self.machine.params().eip210_contract_gas, + gas_price: 0.into(), + code: code, + code_hash: code_hash, + data: Some(H256::from(number).to_vec()), + call_type: CallType::Call, + params_type: vm::ParamsType::Separate, + }; + + let mut ex = Executive::new(self.state, self.env_info, self.machine, self.schedule); + let r = ex.call_with_crossbeam( + params, + self.substate, + self.stack_depth + 1, + self.tracer, + self.vm_tracer, + ); + let output = match &r { + Ok(ref r) => H256::from(&r.return_data[..32]), + _ => H256::new(), + }; + trace!( + "ext: blockhash contract({}) -> {:?}({}) self.env_info.number={}\n", + number, + r, + output, + self.env_info.number + ); + output + } else { + // TODO: comment out what this function expects from env_info, since it will produce panics if the latter is inconsistent + match *number < U256::from(self.env_info.number) + && number.low_u64() >= cmp::max(256, self.env_info.number) - 256 + { + true => { + let index = self.env_info.number - number.low_u64() - 1; + assert!( + index < self.env_info.last_hashes.len() as u64, + format!( + "Inconsistent env_info, should contain at least {:?} last hashes", + index + 1 + ) + ); + let r = self.env_info.last_hashes[index as usize].clone(); + trace!( + "ext: blockhash({}) -> {} self.env_info.number={}\n", + number, + r, + self.env_info.number + ); + r + } + false => { + trace!( + "ext: blockhash({}) -> null self.env_info.number={}\n", + number, + self.env_info.number + ); + H256::zero() + } + } + } + } + + fn create( + &mut self, + gas: &U256, + value: &U256, + code: &[u8], + address_scheme: CreateContractAddress, + trap: bool, + ) -> ::std::result::Result { + // create new contract address + let (address, code_hash) = match self.state.nonce(&self.origin_info.address) { + Ok(nonce) => contract_address(address_scheme, &self.origin_info.address, &nonce, &code), + Err(e) => { + debug!(target: "ext", "Database corruption encountered: {:?}", e); + return Ok(ContractCreateResult::Failed); + } + }; + + // prepare the params + let params = ActionParams { + code_address: address.clone(), + address: address.clone(), + sender: self.origin_info.address.clone(), + origin: self.origin_info.origin.clone(), + gas: *gas, + gas_price: self.origin_info.gas_price, + value: ActionValue::Transfer(*value), + code: Some(Arc::new(code.to_vec())), + code_hash: code_hash, + data: None, + call_type: CallType::None, + params_type: vm::ParamsType::Embedded, + }; + + if !self.static_flag { + if !self.schedule.keep_unsigned_nonce || params.sender != UNSIGNED_SENDER { + if let Err(e) = self.state.inc_nonce(&self.origin_info.address) { + debug!(target: "ext", "Database corruption encountered: {:?}", e); + return Ok(ContractCreateResult::Failed); + } + } + } + + if trap { + return Err(TrapKind::Create(params, address)); + } + + // TODO: handle internal error separately + let mut ex = Executive::from_parent( + self.state, + self.env_info, + self.machine, + self.schedule, + self.depth, + self.static_flag, + ); + let out = ex.create_with_crossbeam( + params, + self.substate, + self.stack_depth + 1, + self.tracer, + self.vm_tracer, + ); + Ok(into_contract_create_result(out, &address, self.substate)) + } + + fn call( + &mut self, + gas: &U256, + sender_address: &Address, + receive_address: &Address, + value: Option, + data: &[u8], + code_address: &Address, + call_type: CallType, + trap: bool, + ) -> ::std::result::Result { + trace!(target: "externalities", "call"); + + let code_res = self + .state + .code(code_address) + .and_then(|code| self.state.code_hash(code_address).map(|hash| (code, hash))); + + let (code, code_hash) = match code_res { + Ok((code, hash)) => (code, hash), + Err(_) => return Ok(MessageCallResult::Failed), + }; + + let mut params = ActionParams { + sender: sender_address.clone(), + address: receive_address.clone(), + value: ActionValue::Apparent(self.origin_info.value), + code_address: code_address.clone(), + origin: self.origin_info.origin.clone(), + gas: *gas, + gas_price: self.origin_info.gas_price, + code: code, + code_hash: code_hash, + data: Some(data.to_vec()), + call_type: call_type, + params_type: vm::ParamsType::Separate, + }; + + if let Some(value) = value { + params.value = ActionValue::Transfer(value); + } + + if trap { + return Err(TrapKind::Call(params)); + } + + let mut ex = Executive::from_parent( + self.state, + self.env_info, + self.machine, + self.schedule, + self.depth, + self.static_flag, + ); + let out = ex.call_with_crossbeam( + params, + self.substate, + self.stack_depth + 1, + self.tracer, + self.vm_tracer, + ); + Ok(into_message_call_result(out)) + } + + fn extcode(&self, address: &Address) -> vm::Result>> { + Ok(self.state.code(address)?) + } + + fn extcodehash(&self, address: &Address) -> vm::Result> { + if self.state.exists_and_not_null(address)? { + Ok(self.state.code_hash(address)?) + } else { + Ok(None) + } + } + + fn extcodesize(&self, address: &Address) -> vm::Result> { + Ok(self.state.code_size(address)?) + } + + fn ret(self, gas: &U256, data: &ReturnData, apply_state: bool) -> vm::Result + where + Self: Sized, + { + match self.output { + OutputPolicy::Return => Ok(*gas), + OutputPolicy::InitContract if apply_state => { + let return_cost = + U256::from(data.len()) * U256::from(self.schedule.create_data_gas); + if return_cost > *gas || data.len() > self.schedule.create_data_limit { + return match self.schedule.exceptional_failed_code_deposit { + true => Err(vm::Error::OutOfGas), + false => Ok(*gas), + }; + } + self.state + .init_code(&self.origin_info.address, data.to_vec())?; + Ok(*gas - return_cost) + } + OutputPolicy::InitContract => Ok(*gas), + } + } + + fn log(&mut self, topics: Vec, data: &[u8]) -> vm::Result<()> { + use types::log_entry::LogEntry; + + if self.static_flag { + return Err(vm::Error::MutableCallInStaticContext); + } + + let address = self.origin_info.address.clone(); + self.substate.logs.push(LogEntry { + address: address, + topics: topics, + data: data.to_vec(), + }); + + Ok(()) + } + + fn suicide(&mut self, refund_address: &Address) -> vm::Result<()> { + if self.static_flag { + return Err(vm::Error::MutableCallInStaticContext); + } + + let address = self.origin_info.address.clone(); + let balance = self.balance(&address)?; + if &address == refund_address { + // TODO [todr] To be consistent with CPP client we set balance to 0 in that case. + self.state + .sub_balance(&address, &balance, &mut CleanupMode::NoEmpty)?; + } else { + trace!(target: "ext", "Suiciding {} -> {} (xfer: {})", address, refund_address, balance); + self.state.transfer_balance( + &address, + refund_address, + &balance, + self.substate.to_cleanup_mode(&self.schedule), + )?; + } + + self.tracer + .trace_suicide(address, balance, refund_address.clone()); + self.substate.suicides.insert(address); + + Ok(()) + } + + fn schedule(&self) -> &Schedule { + &self.schedule + } + + fn env_info(&self) -> &EnvInfo { + self.env_info + } + + fn chain_id(&self) -> u64 { + self.machine.params().chain_id + } + + fn depth(&self) -> usize { + self.depth + } + + fn add_sstore_refund(&mut self, value: usize) { + self.substate.sstore_clears_refund += value as i128; + } + + fn sub_sstore_refund(&mut self, value: usize) { + self.substate.sstore_clears_refund -= value as i128; + } + + fn trace_next_instruction(&mut self, pc: usize, instruction: u8, current_gas: U256) -> bool { + self.vm_tracer + .trace_next_instruction(pc, instruction, current_gas) + } + + fn trace_prepare_execute( + &mut self, + pc: usize, + instruction: u8, + gas_cost: U256, + mem_written: Option<(usize, usize)>, + store_written: Option<(U256, U256)>, + ) { + self.vm_tracer + .trace_prepare_execute(pc, instruction, gas_cost, mem_written, store_written) + } + + fn trace_failed(&mut self) { + self.vm_tracer.trace_failed(); + } + + fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) { + self.vm_tracer.trace_executed(gas_used, stack_push, mem) + } } #[cfg(test)] mod tests { - use ethereum_types::{U256, Address}; - use evm::{EnvInfo, Ext, CallType}; - use state::{State, Substate}; - use test_helpers::get_temp_state; - use super::*; - use trace::{NoopTracer, NoopVMTracer}; - - fn get_test_origin() -> OriginInfo { - OriginInfo { - address: Address::zero(), - origin: Address::zero(), - gas_price: U256::zero(), - value: U256::zero() - } - } - - fn get_test_env_info() -> EnvInfo { - EnvInfo { - number: 100, - author: 0.into(), - timestamp: 0, - difficulty: 0.into(), - last_hashes: Arc::new(vec![]), - gas_used: 0.into(), - gas_limit: 0.into(), - } - } - - struct TestSetup { - state: State<::state_db::StateDB>, - machine: ::machine::EthereumMachine, - schedule: Schedule, - sub_state: Substate, - env_info: EnvInfo - } - - impl Default for TestSetup { - fn default() -> Self { - TestSetup::new() - } - } - - impl TestSetup { - fn new() -> Self { - let machine = ::spec::Spec::new_test_machine(); - let env_info = get_test_env_info(); - let schedule = machine.schedule(env_info.number); - TestSetup { - state: get_temp_state(), - schedule: schedule, - machine: machine, - sub_state: Substate::new(), - env_info: env_info, - } - } - } - - #[test] - fn can_be_created() { - let mut setup = TestSetup::new(); - let state = &mut setup.state; - let mut tracer = NoopTracer; - let mut vm_tracer = NoopVMTracer; - let origin_info = get_test_origin(); - - let ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); - - assert_eq!(ext.env_info().number, 100); - } - - #[test] - fn can_return_block_hash_no_env() { - let mut setup = TestSetup::new(); - let state = &mut setup.state; - let mut tracer = NoopTracer; - let mut vm_tracer = NoopVMTracer; - let origin_info = get_test_origin(); - - let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); - - let hash = ext.blockhash(&"0000000000000000000000000000000000000000000000000000000000120000".parse::().unwrap()); - - assert_eq!(hash, H256::zero()); - } - - #[test] - fn can_return_block_hash() { - let test_hash = H256::from("afafafafafafafafafafafbcbcbcbcbcbcbcbcbcbeeeeeeeeeeeeedddddddddd"); - let test_env_number = 0x120001; - - let mut setup = TestSetup::new(); - { - let env_info = &mut setup.env_info; - env_info.number = test_env_number; - let mut last_hashes = (*env_info.last_hashes).clone(); - last_hashes.push(test_hash.clone()); - env_info.last_hashes = Arc::new(last_hashes); - } - let state = &mut setup.state; - let mut tracer = NoopTracer; - let mut vm_tracer = NoopVMTracer; - let origin_info = get_test_origin(); - - let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); - - let hash = ext.blockhash(&"0000000000000000000000000000000000000000000000000000000000120000".parse::().unwrap()); - - assert_eq!(test_hash, hash); - } - - #[test] - #[should_panic] - fn can_call_fail_empty() { - let mut setup = TestSetup::new(); - let state = &mut setup.state; - let mut tracer = NoopTracer; - let mut vm_tracer = NoopVMTracer; - let origin_info = get_test_origin(); - - let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); - - // this should panic because we have no balance on any account - ext.call( - &"0000000000000000000000000000000000000000000000000000000000120000".parse::().unwrap(), - &Address::new(), - &Address::new(), - Some("0000000000000000000000000000000000000000000000000000000000150000".parse::().unwrap()), - &[], - &Address::new(), - CallType::Call, - false, - ).ok().unwrap(); - } - - #[test] - fn can_log() { - let log_data = vec![120u8, 110u8]; - let log_topics = vec![H256::from("af0fa234a6af46afa23faf23bcbc1c1cb4bcb7bcbe7e7e7ee3ee2edddddddddd")]; - - let mut setup = TestSetup::new(); - let state = &mut setup.state; - let mut tracer = NoopTracer; - let mut vm_tracer = NoopVMTracer; - let origin_info = get_test_origin(); - - { - let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); - ext.log(log_topics, &log_data).unwrap(); - } - - assert_eq!(setup.sub_state.logs.len(), 1); - } - - #[test] - fn can_suicide() { - let refund_account = &Address::new(); - - let mut setup = TestSetup::new(); - let state = &mut setup.state; - let mut tracer = NoopTracer; - let mut vm_tracer = NoopVMTracer; - let origin_info = get_test_origin(); - - { - let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); - ext.suicide(refund_account).unwrap(); - } - - assert_eq!(setup.sub_state.suicides.len(), 1); - } - - #[test] - fn can_create() { - use std::str::FromStr; - - let mut setup = TestSetup::new(); - let state = &mut setup.state; - let mut tracer = NoopTracer; - let mut vm_tracer = NoopVMTracer; - let origin_info = get_test_origin(); - - let address = { - let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); - match ext.create(&U256::max_value(), &U256::zero(), &[], CreateContractAddress::FromSenderAndNonce, false) { - Ok(ContractCreateResult::Created(address, _)) => address, - _ => panic!("Test create failed; expected Created, got Failed/Reverted."), - } - }; - - assert_eq!(address, Address::from_str("bd770416a3345f91e4b34576cb804a576fa48eb1").unwrap()); - } - - #[test] - fn can_create2() { - use std::str::FromStr; - - let mut setup = TestSetup::new(); - let state = &mut setup.state; - let mut tracer = NoopTracer; - let mut vm_tracer = NoopVMTracer; - let origin_info = get_test_origin(); - - let address = { - let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); - - match ext.create(&U256::max_value(), &U256::zero(), &[], CreateContractAddress::FromSenderSaltAndCodeHash(H256::default()), false) { - Ok(ContractCreateResult::Created(address, _)) => address, - _ => panic!("Test create failed; expected Created, got Failed/Reverted."), - } - }; - - assert_eq!(address, Address::from_str("e33c0c7f7df4809055c3eba6c09cfe4baf1bd9e0").unwrap()); - } + use super::*; + use ethereum_types::{Address, U256}; + use evm::{CallType, EnvInfo, Ext}; + use state::{State, Substate}; + use test_helpers::get_temp_state; + use trace::{NoopTracer, NoopVMTracer}; + + fn get_test_origin() -> OriginInfo { + OriginInfo { + address: Address::zero(), + origin: Address::zero(), + gas_price: U256::zero(), + value: U256::zero(), + } + } + + fn get_test_env_info() -> EnvInfo { + EnvInfo { + number: 100, + author: 0.into(), + timestamp: 0, + difficulty: 0.into(), + last_hashes: Arc::new(vec![]), + gas_used: 0.into(), + gas_limit: 0.into(), + } + } + + struct TestSetup { + state: State<::state_db::StateDB>, + machine: ::machine::EthereumMachine, + schedule: Schedule, + sub_state: Substate, + env_info: EnvInfo, + } + + impl Default for TestSetup { + fn default() -> Self { + TestSetup::new() + } + } + + impl TestSetup { + fn new() -> Self { + let machine = ::spec::Spec::new_test_machine(); + let env_info = get_test_env_info(); + let schedule = machine.schedule(env_info.number); + TestSetup { + state: get_temp_state(), + schedule: schedule, + machine: machine, + sub_state: Substate::new(), + env_info: env_info, + } + } + } + + #[test] + fn can_be_created() { + let mut setup = TestSetup::new(); + let state = &mut setup.state; + let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; + let origin_info = get_test_origin(); + + let ext = Externalities::new( + state, + &setup.env_info, + &setup.machine, + &setup.schedule, + 0, + 0, + &origin_info, + &mut setup.sub_state, + OutputPolicy::InitContract, + &mut tracer, + &mut vm_tracer, + false, + ); + + assert_eq!(ext.env_info().number, 100); + } + + #[test] + fn can_return_block_hash_no_env() { + let mut setup = TestSetup::new(); + let state = &mut setup.state; + let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; + let origin_info = get_test_origin(); + + let mut ext = Externalities::new( + state, + &setup.env_info, + &setup.machine, + &setup.schedule, + 0, + 0, + &origin_info, + &mut setup.sub_state, + OutputPolicy::InitContract, + &mut tracer, + &mut vm_tracer, + false, + ); + + let hash = ext.blockhash( + &"0000000000000000000000000000000000000000000000000000000000120000" + .parse::() + .unwrap(), + ); + + assert_eq!(hash, H256::zero()); + } + + #[test] + fn can_return_block_hash() { + let test_hash = + H256::from("afafafafafafafafafafafbcbcbcbcbcbcbcbcbcbeeeeeeeeeeeeedddddddddd"); + let test_env_number = 0x120001; + + let mut setup = TestSetup::new(); + { + let env_info = &mut setup.env_info; + env_info.number = test_env_number; + let mut last_hashes = (*env_info.last_hashes).clone(); + last_hashes.push(test_hash.clone()); + env_info.last_hashes = Arc::new(last_hashes); + } + let state = &mut setup.state; + let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; + let origin_info = get_test_origin(); + + let mut ext = Externalities::new( + state, + &setup.env_info, + &setup.machine, + &setup.schedule, + 0, + 0, + &origin_info, + &mut setup.sub_state, + OutputPolicy::InitContract, + &mut tracer, + &mut vm_tracer, + false, + ); + + let hash = ext.blockhash( + &"0000000000000000000000000000000000000000000000000000000000120000" + .parse::() + .unwrap(), + ); + + assert_eq!(test_hash, hash); + } + + #[test] + #[should_panic] + fn can_call_fail_empty() { + let mut setup = TestSetup::new(); + let state = &mut setup.state; + let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; + let origin_info = get_test_origin(); + + let mut ext = Externalities::new( + state, + &setup.env_info, + &setup.machine, + &setup.schedule, + 0, + 0, + &origin_info, + &mut setup.sub_state, + OutputPolicy::InitContract, + &mut tracer, + &mut vm_tracer, + false, + ); + + // this should panic because we have no balance on any account + ext.call( + &"0000000000000000000000000000000000000000000000000000000000120000" + .parse::() + .unwrap(), + &Address::new(), + &Address::new(), + Some( + "0000000000000000000000000000000000000000000000000000000000150000" + .parse::() + .unwrap(), + ), + &[], + &Address::new(), + CallType::Call, + false, + ) + .ok() + .unwrap(); + } + + #[test] + fn can_log() { + let log_data = vec![120u8, 110u8]; + let log_topics = vec![H256::from( + "af0fa234a6af46afa23faf23bcbc1c1cb4bcb7bcbe7e7e7ee3ee2edddddddddd", + )]; + + let mut setup = TestSetup::new(); + let state = &mut setup.state; + let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; + let origin_info = get_test_origin(); + + { + let mut ext = Externalities::new( + state, + &setup.env_info, + &setup.machine, + &setup.schedule, + 0, + 0, + &origin_info, + &mut setup.sub_state, + OutputPolicy::InitContract, + &mut tracer, + &mut vm_tracer, + false, + ); + ext.log(log_topics, &log_data).unwrap(); + } + + assert_eq!(setup.sub_state.logs.len(), 1); + } + + #[test] + fn can_suicide() { + let refund_account = &Address::new(); + + let mut setup = TestSetup::new(); + let state = &mut setup.state; + let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; + let origin_info = get_test_origin(); + + { + let mut ext = Externalities::new( + state, + &setup.env_info, + &setup.machine, + &setup.schedule, + 0, + 0, + &origin_info, + &mut setup.sub_state, + OutputPolicy::InitContract, + &mut tracer, + &mut vm_tracer, + false, + ); + ext.suicide(refund_account).unwrap(); + } + + assert_eq!(setup.sub_state.suicides.len(), 1); + } + + #[test] + fn can_create() { + use std::str::FromStr; + + let mut setup = TestSetup::new(); + let state = &mut setup.state; + let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; + let origin_info = get_test_origin(); + + let address = { + let mut ext = Externalities::new( + state, + &setup.env_info, + &setup.machine, + &setup.schedule, + 0, + 0, + &origin_info, + &mut setup.sub_state, + OutputPolicy::InitContract, + &mut tracer, + &mut vm_tracer, + false, + ); + match ext.create( + &U256::max_value(), + &U256::zero(), + &[], + CreateContractAddress::FromSenderAndNonce, + false, + ) { + Ok(ContractCreateResult::Created(address, _)) => address, + _ => panic!("Test create failed; expected Created, got Failed/Reverted."), + } + }; + + assert_eq!( + address, + Address::from_str("bd770416a3345f91e4b34576cb804a576fa48eb1").unwrap() + ); + } + + #[test] + fn can_create2() { + use std::str::FromStr; + + let mut setup = TestSetup::new(); + let state = &mut setup.state; + let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; + let origin_info = get_test_origin(); + + let address = { + let mut ext = Externalities::new( + state, + &setup.env_info, + &setup.machine, + &setup.schedule, + 0, + 0, + &origin_info, + &mut setup.sub_state, + OutputPolicy::InitContract, + &mut tracer, + &mut vm_tracer, + false, + ); + + match ext.create( + &U256::max_value(), + &U256::zero(), + &[], + CreateContractAddress::FromSenderSaltAndCodeHash(H256::default()), + false, + ) { + Ok(ContractCreateResult::Created(address, _)) => address, + _ => panic!("Test create failed; expected Created, got Failed/Reverted."), + } + }; + + assert_eq!( + address, + Address::from_str("e33c0c7f7df4809055c3eba6c09cfe4baf1bd9e0").unwrap() + ); + } } diff --git a/ethcore/src/factory.rs b/ethcore/src/factory.rs index 06b77da9aa1..f89bce9eeb5 100644 --- a/ethcore/src/factory.rs +++ b/ethcore/src/factory.rs @@ -1,62 +1,68 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use trie::TrieFactory; -use ethtrie::RlpCodec; use account_db::Factory as AccountFactory; +use ethtrie::RlpCodec; use evm::{Factory as EvmFactory, VMType}; -use vm::{Exec, ActionParams, Schedule}; -use wasm::WasmInterpreter; use keccak_hasher::KeccakHasher; +use trie::TrieFactory; +use vm::{ActionParams, Exec, Schedule}; +use wasm::WasmInterpreter; const WASM_MAGIC_NUMBER: &'static [u8; 4] = b"\0asm"; /// Virtual machine factory #[derive(Default, Clone)] pub struct VmFactory { - evm: EvmFactory, + evm: EvmFactory, } impl VmFactory { - pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box { - if schedule.wasm.is_some() && params.code.as_ref().map_or(false, |code| code.len() > 4 && &code[0..4] == WASM_MAGIC_NUMBER) { - Box::new(WasmInterpreter::new(params)) - } else { - self.evm.create(params, schedule, depth) - } - } - - pub fn new(evm: VMType, cache_size: usize) -> Self { - VmFactory { evm: EvmFactory::new(evm, cache_size) } - } + pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box { + if schedule.wasm.is_some() + && params.code.as_ref().map_or(false, |code| { + code.len() > 4 && &code[0..4] == WASM_MAGIC_NUMBER + }) + { + Box::new(WasmInterpreter::new(params)) + } else { + self.evm.create(params, schedule, depth) + } + } + + pub fn new(evm: VMType, cache_size: usize) -> Self { + VmFactory { + evm: EvmFactory::new(evm, cache_size), + } + } } impl From for VmFactory { - fn from(evm: EvmFactory) -> Self { - VmFactory { evm: evm } - } + fn from(evm: EvmFactory) -> Self { + VmFactory { evm: evm } + } } /// Collection of factories. #[derive(Default, Clone)] pub struct Factories { - /// factory for evm. - pub vm: VmFactory, - /// factory for tries. - pub trie: TrieFactory, - /// factory for account databases. - pub accountdb: AccountFactory, + /// factory for evm. + pub vm: VmFactory, + /// factory for tries. + pub trie: TrieFactory, + /// factory for account databases. + pub accountdb: AccountFactory, } diff --git a/ethcore/src/json_tests/chain.rs b/ethcore/src/json_tests/chain.rs index 4488d0f326e..0b489f90d34 100644 --- a/ethcore/src/json_tests/chain.rs +++ b/ethcore/src/json_tests/chain.rs @@ -1,197 +1,248 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::path::Path; -use std::sync::Arc; -use client::{EvmTestClient, Client, ClientConfig, ChainInfo, ImportBlock}; -use spec::Genesis; +use super::HookType; +use client::{ + Balance, BlockChainClient, BlockId, ChainInfo, Client, ClientConfig, EvmTestClient, + ImportBlock, Nonce, StateOrBlock, +}; +use ethereum_types::{H256, U256}; use ethjson; -use miner::Miner; use io::IoChannel; +use log::warn; +use miner::Miner; +use rustc_hex::ToHex; +use spec::Genesis; +use std::{path::Path, sync::Arc}; use test_helpers; -use verification::queue::kind::blocks::Unverified; -use verification::VerifierType; -use super::SKIP_TEST_STATE; -use super::HookType; - -/// Run chain jsontests on a given folder. -pub fn run_test_path(p: &Path, skip: &[&'static str], h: &mut H) { - ::json_tests::test_common::run_test_path(p, skip, json_chain_test, h) -} - -/// Run chain jsontests on a given file. -pub fn run_test_file(p: &Path, h: &mut H) { - ::json_tests::test_common::run_test_file(p, json_chain_test, h) -} - -fn skip_test(name: &String) -> bool { - SKIP_TEST_STATE.block.iter().any(|block_test|block_test.subtests.contains(name)) -} - -pub fn json_chain_test(json_data: &[u8], start_stop_hook: &mut H) -> Vec { - let _ = ::env_logger::try_init(); - let tests = ethjson::blockchain::Test::load(json_data).unwrap(); - let mut failed = Vec::new(); - - for (name, blockchain) in tests.into_iter() { - if skip_test(&name) { - println!(" - {} | {:?} Ignoring tests because in skip list", name, blockchain.network); - continue; - } - start_stop_hook(&name, HookType::OnStart); - - let mut fail = false; - { - let mut fail_unless = |cond: bool| if !cond && !fail { - failed.push(name.clone()); - flushln!("FAIL"); - fail = true; - true - } else {false}; - - flush!(" - {}...", name); - - let spec = { - let mut spec = match EvmTestClient::spec_from_json(&blockchain.network) { - Some(spec) => spec, - None => { - println!(" - {} | {:?} Ignoring tests because of missing spec", name, blockchain.network); - continue; - } - }; - - let genesis = Genesis::from(blockchain.genesis()); - let state = From::from(blockchain.pre_state.clone()); - spec.set_genesis_state(state).expect("Failed to overwrite genesis state"); - spec.overwrite_genesis_params(genesis); - assert!(spec.is_state_root_valid()); - spec - }; - - { - let db = test_helpers::new_db(); - let mut config = ClientConfig::default(); - if ethjson::blockchain::Engine::NoProof == blockchain.engine { - config.verifier_type = VerifierType::CanonNoSeal; - config.check_seal = false; - } - config.history = 8; - let client = Client::new( - config, - &spec, - db, - Arc::new(Miner::new_for_tests(&spec, None)), - IoChannel::disconnected(), - ).unwrap(); - for b in blockchain.blocks_rlp() { - if let Ok(block) = Unverified::from_rlp(b) { - let _ = client.import_block(block); - client.flush_queue(); - client.import_verified_blocks(); - } - } - fail_unless(client.chain_info().best_block_hash == blockchain.best_block.into()); - } - } - - if !fail { - flushln!("ok"); - } - - start_stop_hook(&name, HookType::OnStop); - } - - println!("!!! {:?} tests from failed.", failed.len()); - failed +use verification::{queue::kind::blocks::Unverified, VerifierType}; + +fn check_poststate( + client: &Arc, + test_name: &str, + post_state: ethjson::blockchain::State, +) -> bool { + let mut success = true; + + for (address, expected) in post_state { + if let Some(expected_balance) = expected.balance { + let expected_balance: U256 = expected_balance.into(); + let current_balance = client + .balance( + &address.clone().into(), + StateOrBlock::Block(BlockId::Latest), + ) + .unwrap(); + if expected_balance != current_balance { + warn!(target: "json-tests", "{} – Poststate {:?} balance mismatch current={} expected={}", + test_name, address, current_balance, expected_balance); + success = false; + } + } + + if let Some(expected_nonce) = expected.nonce { + let expected_nonce: U256 = expected_nonce.into(); + let current_nonce = client + .nonce(&address.clone().into(), BlockId::Latest) + .unwrap(); + if expected_nonce != current_nonce { + warn!(target: "json-tests", "{} – Poststate {:?} nonce mismatch current={} expected={}", + test_name, address, current_nonce, expected_nonce); + success = false; + } + } + + if let Some(expected_code) = expected.code { + let expected_code: String = expected_code.to_hex(); + let current_code = match client.code( + &address.clone().into(), + StateOrBlock::Block(BlockId::Latest), + ) { + Some(Some(code)) => code.to_hex(), + _ => "".to_string(), + }; + if current_code != expected_code { + warn!(target: "json-tests", "{} – Poststate {:?} code mismatch current={} expected={}", + test_name, address, current_code, expected_code); + success = false; + } + } + + if let Some(expected_storage) = expected.storage { + for (uint_position, uint_expected_value) in expected_storage.iter() { + let mut position = H256::default(); + uint_position.0.to_big_endian(position.as_mut()); + + let mut expected_value = H256::default(); + uint_expected_value.0.to_big_endian(expected_value.as_mut()); + + let current_value = client + .storage_at( + &address.clone().into(), + &position, + StateOrBlock::Block(BlockId::Latest), + ) + .unwrap(); + + if current_value != expected_value { + let position: &[u8] = position.as_ref(); + let current_value: &[u8] = current_value.as_ref(); + let expected_value: &[u8] = expected_value.as_ref(); + warn!(target: "json-tests", "{} – Poststate {:?} state {} mismatch actual={} expected={}", + test_name, address, position.to_hex(), current_value.to_hex(), + expected_value.to_hex()); + success = false; + } + } + } + + if expected.builtin.is_some() { + warn!(target: "json-tests", "{} – Poststate {:?} builtin not supported", test_name, address); + success = false; + } + if expected.constructor.is_some() { + warn!(target: "json-tests", "{} – Poststate {:?} constructor not supported", test_name, address); + success = false; + } + } + success } -#[cfg(test)] -mod block_tests { - use super::json_chain_test; - use json_tests::HookType; - - fn do_json_test(json_data: &[u8], h: &mut H) -> Vec { - json_chain_test(json_data, h) - } - - declare_test!{BlockchainTests_bcBlockGasLimitTest, "BlockchainTests/bcBlockGasLimitTest"} - declare_test!{BlockchainTests_bcExploitTest, "BlockchainTests/bcExploitTest"} - declare_test!{BlockchainTests_bcForgedTest, "BlockchainTests/bcForgedTest"} - declare_test!{BlockchainTests_bcForkStressTest, "BlockchainTests/bcForkStressTest"} - declare_test!{BlockchainTests_bcGasPricerTest, "BlockchainTests/bcGasPricerTest"} - declare_test!{BlockchainTests_bcInvalidHeaderTest, "BlockchainTests/bcInvalidHeaderTest"} - declare_test!{BlockchainTests_bcMultiChainTest, "BlockchainTests/bcMultiChainTest"} - declare_test!{BlockchainTests_bcRandomBlockhashTest, "BlockchainTests/bcRandomBlockhashTest"} - declare_test!{BlockchainTests_bcStateTest, "BlockchainTests/bcStateTests"} - declare_test!{BlockchainTests_bcTotalDifficultyTest, "BlockchainTests/bcTotalDifficultyTest"} - declare_test!{BlockchainTests_bcUncleHeaderValidity, "BlockchainTests/bcUncleHeaderValidity"} - declare_test!{BlockchainTests_bcUncleTest, "BlockchainTests/bcUncleTest"} - declare_test!{BlockchainTests_bcValidBlockTest, "BlockchainTests/bcValidBlockTest"} - declare_test!{BlockchainTests_bcWalletTest, "BlockchainTests/bcWalletTest"} - - declare_test!{BlockchainTests_GeneralStateTest_stArgsZeroOneBalance, "BlockchainTests/GeneralStateTests/stArgsZeroOneBalance/"} - declare_test!{BlockchainTests_GeneralStateTest_stAttackTest, "BlockchainTests/GeneralStateTests/stAttackTest/"} - declare_test!{BlockchainTests_GeneralStateTest_stBadOpcodeTest, "BlockchainTests/GeneralStateTests/stBadOpcode/"} - declare_test!{BlockchainTests_GeneralStateTest_stBugsTest, "BlockchainTests/GeneralStateTests/stBugs/"} - declare_test!{BlockchainTests_GeneralStateTest_stCallCodes, "BlockchainTests/GeneralStateTests/stCallCodes/"} - declare_test!{BlockchainTests_GeneralStateTest_stCallCreateCallCodeTest, "BlockchainTests/GeneralStateTests/stCallCreateCallCodeTest/"} - declare_test!{BlockchainTests_GeneralStateTest_stCallDelegateCodesCallCodeHomestead, "BlockchainTests/GeneralStateTests/stCallDelegateCodesCallCodeHomestead/"} - declare_test!{BlockchainTests_GeneralStateTest_stCallDelegateCodesHomestead, "BlockchainTests/GeneralStateTests/stCallDelegateCodesHomestead/"} - declare_test!{BlockchainTests_GeneralStateTest_stChangedEIP150, "BlockchainTests/GeneralStateTests/stChangedEIP150/"} - declare_test!{BlockchainTests_GeneralStateTest_stCodeSizeLimit, "BlockchainTests/GeneralStateTests/stCodeSizeLimit/"} - declare_test!{BlockchainTests_GeneralStateTest_stCreate2, "BlockchainTests/GeneralStateTests/stCreate2/"} - declare_test!{BlockchainTests_GeneralStateTest_stCreateTest, "BlockchainTests/GeneralStateTests/stCreateTest/"} - declare_test!{BlockchainTests_GeneralStateTest_stDelegatecallTestHomestead, "BlockchainTests/GeneralStateTests/stDelegatecallTestHomestead/"} - declare_test!{BlockchainTests_GeneralStateTest_stEIP150singleCodeGasPrices, "BlockchainTests/GeneralStateTests/stEIP150singleCodeGasPrices/"} - declare_test!{BlockchainTests_GeneralStateTest_stEIP150Specific, "BlockchainTests/GeneralStateTests/stEIP150Specific/"} - declare_test!{BlockchainTests_GeneralStateTest_stEIP158Specific, "BlockchainTests/GeneralStateTests/stEIP158Specific/"} - declare_test!{BlockchainTests_GeneralStateTest_stExample, "BlockchainTests/GeneralStateTests/stExample/"} - declare_test!{BlockchainTests_GeneralStateTest_stHomesteadSpecific, "BlockchainTests/GeneralStateTests/stHomesteadSpecific/"} - declare_test!{BlockchainTests_GeneralStateTest_stInitCodeTest, "BlockchainTests/GeneralStateTests/stInitCodeTest/"} - declare_test!{BlockchainTests_GeneralStateTest_stLogTests, "BlockchainTests/GeneralStateTests/stLogTests/"} - declare_test!{BlockchainTests_GeneralStateTest_stMemExpandingEIP150Calls, "BlockchainTests/GeneralStateTests/stMemExpandingEIP150Calls/"} - declare_test!{heavy => BlockchainTests_GeneralStateTest_stMemoryStressTest, "BlockchainTests/GeneralStateTests/stMemoryStressTest/"} - declare_test!{BlockchainTests_GeneralStateTest_stMemoryTest, "BlockchainTests/GeneralStateTests/stMemoryTest/"} - declare_test!{BlockchainTests_GeneralStateTest_stNonZeroCallsTest, "BlockchainTests/GeneralStateTests/stNonZeroCallsTest/"} - declare_test!{BlockchainTests_GeneralStateTest_stPreCompiledContracts, "BlockchainTests/GeneralStateTests/stPreCompiledContracts/"} - declare_test!{BlockchainTests_GeneralStateTest_stPreCompiledContracts2, "BlockchainTests/GeneralStateTests/stPreCompiledContracts2/"} - declare_test!{heavy => BlockchainTests_GeneralStateTest_stQuadraticComplexityTest, "BlockchainTests/GeneralStateTests/stQuadraticComplexityTest/"} - declare_test!{BlockchainTests_GeneralStateTest_stRandom, "BlockchainTests/GeneralStateTests/stRandom/"} - declare_test!{BlockchainTests_GeneralStateTest_stRandom2, "BlockchainTests/GeneralStateTests/stRandom2/"} - declare_test!{BlockchainTests_GeneralStateTest_stRecursiveCreate, "BlockchainTests/GeneralStateTests/stRecursiveCreate/"} - declare_test!{BlockchainTests_GeneralStateTest_stRefundTest, "BlockchainTests/GeneralStateTests/stRefundTest/"} - declare_test!{BlockchainTests_GeneralStateTest_stReturnDataTest, "BlockchainTests/GeneralStateTests/stReturnDataTest/"} - declare_test!{BlockchainTests_GeneralStateTest_stRevertTest, "BlockchainTests/GeneralStateTests/stRevertTest/"} - declare_test!{BlockchainTests_GeneralStateTest_stShift, "BlockchainTests/GeneralStateTests/stShift/"} - declare_test!{BlockchainTests_GeneralStateTest_stSolidityTest, "BlockchainTests/GeneralStateTests/stSolidityTest/"} - declare_test!{BlockchainTests_GeneralStateTest_stSpecialTest, "BlockchainTests/GeneralStateTests/stSpecialTest/"} - declare_test!{BlockchainTests_GeneralStateTest_stStackTests, "BlockchainTests/GeneralStateTests/stStackTests/"} - declare_test!{BlockchainTests_GeneralStateTest_stStaticCall, "BlockchainTests/GeneralStateTests/stStaticCall/"} - declare_test!{BlockchainTests_GeneralStateTest_stSystemOperationsTest, "BlockchainTests/GeneralStateTests/stSystemOperationsTest/"} - declare_test!{BlockchainTests_GeneralStateTest_stTransactionTest, "BlockchainTests/GeneralStateTests/stTransactionTest/"} - declare_test!{BlockchainTests_GeneralStateTest_stTransitionTest, "BlockchainTests/GeneralStateTests/stTransitionTest/"} - declare_test!{BlockchainTests_GeneralStateTest_stWalletTest, "BlockchainTests/GeneralStateTests/stWalletTest/"} - declare_test!{BlockchainTests_GeneralStateTest_stZeroCallsRevert, "BlockchainTests/GeneralStateTests/stZeroCallsRevert/"} - declare_test!{BlockchainTests_GeneralStateTest_stZeroCallsTest, "BlockchainTests/GeneralStateTests/stZeroCallsTest/"} - declare_test!{BlockchainTests_GeneralStateTest_stZeroKnowledge, "BlockchainTests/GeneralStateTests/stZeroKnowledge/"} - declare_test!{BlockchainTests_GeneralStateTest_stZeroKnowledge2, "BlockchainTests/GeneralStateTests/stZeroKnowledge2/"} - declare_test!{BlockchainTests_GeneralStateTest_stSStoreTest, "BlockchainTests/GeneralStateTests/stSStoreTest/"} - - declare_test!{BlockchainTests_TransitionTests_bcEIP158ToByzantium, "BlockchainTests/TransitionTests/bcEIP158ToByzantium/"} - declare_test!{BlockchainTests_TransitionTests_bcFrontierToHomestead, "BlockchainTests/TransitionTests/bcFrontierToHomestead/"} - declare_test!{BlockchainTests_TransitionTests_bcHomesteadToDao, "BlockchainTests/TransitionTests/bcHomesteadToDao/"} - declare_test!{BlockchainTests_TransitionTests_bcHomesteadToEIP150, "BlockchainTests/TransitionTests/bcHomesteadToEIP150/"} +pub fn json_chain_test( + test: ðjson::test::ChainTests, + path: &Path, + json_data: &[u8], + start_stop_hook: &mut H, +) -> Vec { + let _ = ::env_logger::try_init(); + let tests = ethjson::blockchain::Test::load(json_data).expect(&format!( + "Could not parse JSON chain test data from {}", + path.display() + )); + let mut failed = Vec::new(); + + for (name, blockchain) in tests.into_iter() { + if !super::debug_include_test(&name) { + continue; + } + + let skip_test = test + .skip + .iter() + .any(|block_test| block_test.names.contains(&name)); + + if skip_test { + info!(" SKIPPED {:?} {:?}", name, blockchain.network); + continue; + } + + let mut fail = false; + { + let mut fail_unless = |cond: bool| { + if !cond && !fail { + failed.push(name.clone()); + flushln!("FAIL"); + fail = true; + true + } else { + false + } + }; + + let spec = { + let mut spec = match EvmTestClient::spec_from_json(&blockchain.network) { + Some(spec) => spec, + None => { + info!( + " SKIPPED {:?} {:?} - Unimplemented chainspec ", + name, blockchain.network + ); + continue; + } + }; + + let genesis = Genesis::from(blockchain.genesis()); + let state = From::from(blockchain.pre_state.clone()); + spec.set_genesis_state(state) + .expect("Failed to overwrite genesis state"); + spec.overwrite_genesis_params(genesis); + spec + }; + + start_stop_hook(&name, HookType::OnStart); + + { + let db = test_helpers::new_db(); + let mut config = ClientConfig::default(); + if ethjson::blockchain::Engine::NoProof == blockchain.engine { + config.verifier_type = VerifierType::CanonNoSeal; + config.check_seal = false; + } + config.history = 8; + config.queue.verifier_settings.num_verifiers = 1; + let client = Client::new( + config, + &spec, + db, + Arc::new(Miner::new_for_tests(&spec, None)), + IoChannel::disconnected(), + ) + .expect("Failed to instantiate a new Client"); + + for b in blockchain.blocks_rlp() { + let bytes_len = b.len(); + let block = Unverified::from_rlp(b); + match block { + Ok(block) => { + let num = block.header.number(); + trace!(target: "json-tests", "{} – Importing {} bytes. Block #{}", name, bytes_len, num); + let res = client.import_block(block); + if let Err(e) = res { + warn!(target: "json-tests", "{} – Error importing block #{}: {:?}", name, num, e); + } + client.flush_queue(); + client.import_verified_blocks(); + } + Err(decoder_err) => { + warn!(target: "json-tests", "Error decoding test block: {:?} ({} bytes)", decoder_err, bytes_len); + } + } + } + + let post_state_success = if let Some(post_state) = blockchain.post_state.clone() { + check_poststate(&client, &name, post_state) + } else { + true + }; + + fail_unless( + client.chain_info().best_block_hash == blockchain.best_block.into() + && post_state_success, + ); + } + } + + if fail { + flushln!(" - chain: {}...FAILED", name); + } else { + flushln!(" - chain: {}...OK", name); + } + + start_stop_hook(&name, HookType::OnStop); + } + + failed } diff --git a/ethcore/src/json_tests/difficulty.rs b/ethcore/src/json_tests/difficulty.rs index bf3a48fff19..60296e707ed 100644 --- a/ethcore/src/json_tests/difficulty.rs +++ b/ethcore/src/json_tests/difficulty.rs @@ -1,112 +1,68 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use ethjson; -use types::header::Header; use ethereum_types::U256; +use ethjson; use spec::Spec; +use std::path::Path; +use types::header::Header; use super::HookType; -pub fn json_difficulty_test(json_data: &[u8], spec: Spec, start_stop_hook: &mut H) -> Vec { - let _ = ::env_logger::try_init(); - let tests = ethjson::test::DifficultyTest::load(json_data).unwrap(); - let engine = &spec.engine; - - for (name, test) in tests.into_iter() { - start_stop_hook(&name, HookType::OnStart); - - flush!(" - {}...", name); - println!(" - {}...", name); - - let mut parent_header = Header::new(); - let block_number: u64 = test.current_block_number.into(); - parent_header.set_number(block_number - 1); - parent_header.set_gas_limit(0x20000.into()); - parent_header.set_timestamp(test.parent_timestamp.into()); - parent_header.set_difficulty(test.parent_difficulty.into()); - parent_header.set_uncles_hash(test.parent_uncles.into()); - let mut header = Header::new(); - header.set_number(block_number); - header.set_timestamp(test.current_timestamp.into()); - engine.populate_from_parent(&mut header, &parent_header); - let expected_difficulty: U256 = test.current_difficulty.into(); - assert_eq!(header.difficulty(), &expected_difficulty); - flushln!("ok"); - - start_stop_hook(&name, HookType::OnStop); - } - vec![] -} - -macro_rules! difficulty_json_test { - ( $spec:ident ) => { - - use super::json_difficulty_test; - use tempdir::TempDir; - use json_tests::HookType; - - fn do_json_test(json_data: &[u8], h: &mut H) -> Vec { - let tempdir = TempDir::new("").unwrap(); - json_difficulty_test(json_data, ::ethereum::$spec(&tempdir.path()), h) - } - - } -} - -macro_rules! difficulty_json_test_nopath { - ( $spec:ident ) => { - - use super::json_difficulty_test; - use json_tests::HookType; - - fn do_json_test(json_data: &[u8], h: &mut H) -> Vec { - json_difficulty_test(json_data, ::ethereum::$spec(), h) - } - - } -} - -mod difficulty_test { - difficulty_json_test!(new_foundation); - declare_test!{DifficultyTests_difficulty, "BasicTests/difficulty.json"} -} - -mod difficulty_test_byzantium { - difficulty_json_test_nopath!(new_byzantium_test); - declare_test!{DifficultyTests_difficultyByzantium, "BasicTests/difficultyByzantium.json"} -} - -mod difficulty_test_foundation { - difficulty_json_test!(new_foundation); - declare_test!{DifficultyTests_difficultyMainNetwork, "BasicTests/difficultyMainNetwork.json"} -} - -// Disabling Ropsten diff tests; waiting for upstream ethereum/tests Constantinople update -//mod difficulty_test_ropsten { -// difficulty_json_test_nopath!(new_ropsten_test); -// declare_test!{DifficultyTests_difficultyRopsten, "BasicTests/difficultyRopsten.json"} -//} - -mod difficulty_test_frontier { - difficulty_json_test_nopath!(new_frontier_test); - declare_test!{DifficultyTests_difficultyFrontier, "BasicTests/difficultyFrontier.json"} -} - -mod difficulty_test_homestead { - difficulty_json_test_nopath!(new_homestead_test); - declare_test!{DifficultyTests_difficultyHomestead, "BasicTests/difficultyHomestead.json"} +pub fn json_difficulty_test( + path: &Path, + json_data: &[u8], + spec: Spec, + start_stop_hook: &mut H, +) -> Vec { + let mut ret = Vec::new(); + let _ = env_logger::try_init(); + let tests = ethjson::test::DifficultyTest::load(json_data).expect(&format!( + "Could not parse JSON difficulty test data from {}", + path.display() + )); + let engine = &spec.engine; + + for (name, test) in tests.into_iter() { + if !super::debug_include_test(&name) { + continue; + } + + start_stop_hook(&name, HookType::OnStart); + + let mut parent_header = Header::new(); + let block_number: u64 = test.current_block_number.into(); + parent_header.set_number(block_number - 1); + parent_header.set_gas_limit(0x20000.into()); + parent_header.set_timestamp(test.parent_timestamp.into()); + parent_header.set_difficulty(test.parent_difficulty.into()); + parent_header.set_uncles_hash(test.parent_uncles.into()); + let mut header = Header::new(); + header.set_number(block_number); + header.set_timestamp(test.current_timestamp.into()); + engine.populate_from_parent(&mut header, &parent_header); + let expected_difficulty: U256 = test.current_difficulty.into(); + if header.difficulty() == &expected_difficulty { + flushln!(" - difficulty: {}...OK", name); + } else { + flushln!(" - difficulty: {}...FAILED", name); + ret.push(format!("{}:{}", path.to_string_lossy(), name)); + } + + start_stop_hook(&name, HookType::OnStop); + } + ret } diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index cd9e8f2aece..11288ae2dce 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -1,374 +1,403 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::path::Path; -use std::sync::Arc; use super::test_common::*; -use state::{Backend as StateBackend, State, Substate}; -use executive::*; -use evm::{VMType, Finalize}; -use vm::{ - self, ActionParams, CallType, Schedule, Ext, - ContractCreateResult, EnvInfo, MessageCallResult, - CreateContractAddress, ReturnData, -}; -use externalities::*; -use test_helpers::get_temp_state; -use ethjson; -use trace::{Tracer, NoopTracer}; -use trace::{VMTracer, NoopVMTracer}; use bytes::Bytes; +use ethjson; use ethtrie; -use rlp::RlpStream; +use evm::Finalize; +use executive::*; +use externalities::*; use hash::keccak; use machine::EthereumMachine as Machine; +use rlp::RlpStream; +use state::{Backend as StateBackend, State, Substate}; +use std::{path::Path, sync::Arc}; +use test_helpers::get_temp_state; +use trace::{NoopTracer, NoopVMTracer, Tracer, VMTracer}; +use vm::{ + self, ActionParams, CallType, ContractCreateResult, CreateContractAddress, EnvInfo, Ext, + MessageCallResult, ReturnData, Schedule, +}; use super::HookType; -/// Run executive jsontests on a given folder. -pub fn run_test_path(p: &Path, skip: &[&'static str], h: &mut H) { - ::json_tests::test_common::run_test_path(p, skip, do_json_test, h) -} - -/// Run executive jsontests on a given file. -pub fn run_test_file(p: &Path, h: &mut H) { - ::json_tests::test_common::run_test_file(p, do_json_test, h) -} - #[derive(Debug, PartialEq, Clone)] struct CallCreate { - data: Bytes, - destination: Option
, - gas_limit: U256, - value: U256 + data: Bytes, + destination: Option
, + gas_limit: U256, + value: U256, } impl From for CallCreate { - fn from(c: ethjson::vm::Call) -> Self { - let dst: Option = c.destination.into(); - CallCreate { - data: c.data.into(), - destination: dst.map(Into::into), - gas_limit: c.gas_limit.into(), - value: c.value.into() - } - } + fn from(c: ethjson::vm::Call) -> Self { + let dst: Option = c.destination.into(); + CallCreate { + data: c.data.into(), + destination: dst.map(Into::into), + gas_limit: c.gas_limit.into(), + value: c.value.into(), + } + } } /// Tiny wrapper around executive externalities. /// Stores callcreates. struct TestExt<'a, T: 'a, V: 'a, B: 'a> - where T: Tracer, V: VMTracer, B: StateBackend +where + T: Tracer, + V: VMTracer, + B: StateBackend, { - ext: Externalities<'a, T, V, B>, - callcreates: Vec, - nonce: U256, - sender: Address, + ext: Externalities<'a, T, V, B>, + callcreates: Vec, + nonce: U256, + sender: Address, } impl<'a, T: 'a, V: 'a, B: 'a> TestExt<'a, T, V, B> - where T: Tracer, V: VMTracer, B: StateBackend, +where + T: Tracer, + V: VMTracer, + B: StateBackend, { - fn new( - state: &'a mut State, - info: &'a EnvInfo, - machine: &'a Machine, - schedule: &'a Schedule, - depth: usize, - origin_info: &'a OriginInfo, - substate: &'a mut Substate, - output: OutputPolicy, - address: Address, - tracer: &'a mut T, - vm_tracer: &'a mut V, - ) -> ethtrie::Result { - let static_call = false; - Ok(TestExt { - nonce: state.nonce(&address)?, - ext: Externalities::new(state, info, machine, schedule, depth, 0, origin_info, substate, output, tracer, vm_tracer, static_call), - callcreates: vec![], - sender: address, - }) - } + fn new( + state: &'a mut State, + info: &'a EnvInfo, + machine: &'a Machine, + schedule: &'a Schedule, + depth: usize, + origin_info: &'a OriginInfo, + substate: &'a mut Substate, + output: OutputPolicy, + address: Address, + tracer: &'a mut T, + vm_tracer: &'a mut V, + ) -> ethtrie::Result { + let static_call = false; + Ok(TestExt { + nonce: state.nonce(&address)?, + ext: Externalities::new( + state, + info, + machine, + schedule, + depth, + 0, + origin_info, + substate, + output, + tracer, + vm_tracer, + static_call, + ), + callcreates: vec![], + sender: address, + }) + } } impl<'a, T: 'a, V: 'a, B: 'a> Ext for TestExt<'a, T, V, B> - where T: Tracer, V: VMTracer, B: StateBackend +where + T: Tracer, + V: VMTracer, + B: StateBackend, { - fn storage_at(&self, key: &H256) -> vm::Result { - self.ext.storage_at(key) - } - - fn initial_storage_at(&self, key: &H256) -> vm::Result { - self.ext.initial_storage_at(key) - } - - fn set_storage(&mut self, key: H256, value: H256) -> vm::Result<()> { - self.ext.set_storage(key, value) - } - - fn exists(&self, address: &Address) -> vm::Result { - self.ext.exists(address) - } - - fn exists_and_not_null(&self, address: &Address) -> vm::Result { - self.ext.exists_and_not_null(address) - } - - fn balance(&self, address: &Address) -> vm::Result { - self.ext.balance(address) - } - - fn origin_balance(&self) -> vm::Result { - self.ext.origin_balance() - } - - fn blockhash(&mut self, number: &U256) -> H256 { - self.ext.blockhash(number) - } - - fn create( - &mut self, - gas: &U256, - value: &U256, - code: &[u8], - address: CreateContractAddress, - _trap: bool - ) -> Result { - self.callcreates.push(CallCreate { - data: code.to_vec(), - destination: None, - gas_limit: *gas, - value: *value - }); - let contract_address = contract_address(address, &self.sender, &self.nonce, &code).0; - Ok(ContractCreateResult::Created(contract_address, *gas)) - } - - fn call( - &mut self, - gas: &U256, - _sender_address: &Address, - receive_address: &Address, - value: Option, - data: &[u8], - _code_address: &Address, - _call_type: CallType, - _trap: bool - ) -> Result { - self.callcreates.push(CallCreate { - data: data.to_vec(), - destination: Some(receive_address.clone()), - gas_limit: *gas, - value: value.unwrap() - }); - Ok(MessageCallResult::Success(*gas, ReturnData::empty())) - } - - fn extcode(&self, address: &Address) -> vm::Result>> { - self.ext.extcode(address) - } - - fn extcodesize(&self, address: &Address) -> vm::Result> { - self.ext.extcodesize(address) - } - - fn extcodehash(&self, address: &Address) -> vm::Result> { - self.ext.extcodehash(address) - } - - fn log(&mut self, topics: Vec, data: &[u8]) -> vm::Result<()> { - self.ext.log(topics, data) - } - - fn ret(self, gas: &U256, data: &ReturnData, apply_state: bool) -> Result { - self.ext.ret(gas, data, apply_state) - } - - fn suicide(&mut self, refund_address: &Address) -> vm::Result<()> { - self.ext.suicide(refund_address) - } - - fn schedule(&self) -> &Schedule { - self.ext.schedule() - } - - fn env_info(&self) -> &EnvInfo { - self.ext.env_info() - } - - fn depth(&self) -> usize { - 0 - } - - fn is_static(&self) -> bool { - false - } - - fn add_sstore_refund(&mut self, value: usize) { - self.ext.add_sstore_refund(value) - } - - fn sub_sstore_refund(&mut self, value: usize) { - self.ext.sub_sstore_refund(value) - } + fn storage_at(&self, key: &H256) -> vm::Result { + self.ext.storage_at(key) + } + + fn initial_storage_at(&self, key: &H256) -> vm::Result { + self.ext.initial_storage_at(key) + } + + fn set_storage(&mut self, key: H256, value: H256) -> vm::Result<()> { + self.ext.set_storage(key, value) + } + + fn exists(&self, address: &Address) -> vm::Result { + self.ext.exists(address) + } + + fn exists_and_not_null(&self, address: &Address) -> vm::Result { + self.ext.exists_and_not_null(address) + } + + fn balance(&self, address: &Address) -> vm::Result { + self.ext.balance(address) + } + + fn origin_balance(&self) -> vm::Result { + self.ext.origin_balance() + } + + fn blockhash(&mut self, number: &U256) -> H256 { + self.ext.blockhash(number) + } + + fn create( + &mut self, + gas: &U256, + value: &U256, + code: &[u8], + address: CreateContractAddress, + _trap: bool, + ) -> Result { + self.callcreates.push(CallCreate { + data: code.to_vec(), + destination: None, + gas_limit: *gas, + value: *value, + }); + let contract_address = contract_address(address, &self.sender, &self.nonce, &code).0; + Ok(ContractCreateResult::Created(contract_address, *gas)) + } + + fn call( + &mut self, + gas: &U256, + _sender_address: &Address, + receive_address: &Address, + value: Option, + data: &[u8], + _code_address: &Address, + _call_type: CallType, + _trap: bool, + ) -> Result { + self.callcreates.push(CallCreate { + data: data.to_vec(), + destination: Some(receive_address.clone()), + gas_limit: *gas, + value: value.unwrap(), + }); + Ok(MessageCallResult::Success(*gas, ReturnData::empty())) + } + + fn extcode(&self, address: &Address) -> vm::Result>> { + self.ext.extcode(address) + } + + fn extcodesize(&self, address: &Address) -> vm::Result> { + self.ext.extcodesize(address) + } + + fn extcodehash(&self, address: &Address) -> vm::Result> { + self.ext.extcodehash(address) + } + + fn log(&mut self, topics: Vec, data: &[u8]) -> vm::Result<()> { + self.ext.log(topics, data) + } + + fn ret(self, gas: &U256, data: &ReturnData, apply_state: bool) -> Result { + self.ext.ret(gas, data, apply_state) + } + + fn suicide(&mut self, refund_address: &Address) -> vm::Result<()> { + self.ext.suicide(refund_address) + } + + fn schedule(&self) -> &Schedule { + self.ext.schedule() + } + + fn env_info(&self) -> &EnvInfo { + self.ext.env_info() + } + + fn chain_id(&self) -> u64 { + 0 + } + + fn depth(&self) -> usize { + 0 + } + + fn is_static(&self) -> bool { + false + } + + fn add_sstore_refund(&mut self, value: usize) { + self.ext.add_sstore_refund(value) + } + + fn sub_sstore_refund(&mut self, value: usize) { + self.ext.sub_sstore_refund(value) + } } -fn do_json_test(json_data: &[u8], h: &mut H) -> Vec { - let vms = VMType::all(); - vms - .iter() - .flat_map(|vm| do_json_test_for(vm, json_data, h)) - .collect() +/// run an json executive test +pub fn json_executive_test( + path: &Path, + json_data: &[u8], + start_stop_hook: &mut H, +) -> Vec { + let tests = ethjson::vm::Test::load(json_data).expect(&format!( + "Could not parse JSON executive test data from {}", + path.display() + )); + let mut failed = Vec::new(); + + for (name, vm) in tests.into_iter() { + if !super::debug_include_test(&name) { + continue; + } + + start_stop_hook(&format!("{}", name), HookType::OnStart); + + let mut fail = false; + + let mut fail_unless = |cond: bool, s: &str| { + if !cond && !fail { + failed.push(format!("{}: {}", name, s)); + fail = true + } + }; + + macro_rules! try_fail { + ($e: expr) => { + match $e { + Ok(x) => x, + Err(e) => { + let msg = format!("Internal error: {}", e); + fail_unless(false, &msg); + continue; + } + } + }; + } + + let out_of_gas = vm.out_of_gas(); + let mut state = get_temp_state(); + state.populate_from(From::from(vm.pre_state.clone())); + let info: EnvInfo = From::from(vm.env); + let machine = { + let mut machine = ::ethereum::new_frontier_test_machine(); + machine.set_schedule_creation_rules(Box::new(move |s, _| s.max_depth = 1)); + machine + }; + + let params = ActionParams::from(vm.transaction); + + let mut substate = Substate::new(); + let mut tracer = NoopTracer; + let mut vm_tracer = NoopVMTracer; + let vm_factory = state.vm_factory(); + let origin_info = OriginInfo::from(¶ms); + + // execute + let (res, callcreates) = { + let schedule = machine.schedule(info.number); + let mut ex = try_fail!(TestExt::new( + &mut state, + &info, + &machine, + &schedule, + 0, + &origin_info, + &mut substate, + OutputPolicy::Return, + params.address.clone(), + &mut tracer, + &mut vm_tracer, + )); + let evm = vm_factory.create(params, &schedule, 0); + let res = evm + .exec(&mut ex) + .ok() + .expect("TestExt never trap; resume error never happens; qed"); + // a return in finalize will not alter callcreates + let callcreates = ex.callcreates.clone(); + (res.finalize(ex), callcreates) + }; + + let output = match &res { + Ok(res) => res.return_data.to_vec(), + Err(_) => Vec::new(), + }; + + let log_hash = { + let mut rlp = RlpStream::new_list(substate.logs.len()); + for l in &substate.logs { + rlp.append(l); + } + keccak(&rlp.drain()) + }; + + match res { + Err(_) => fail_unless(out_of_gas, "didn't expect to run out of gas."), + Ok(res) => { + fail_unless(!out_of_gas, "expected to run out of gas."); + fail_unless( + Some(res.gas_left) == vm.gas_left.map(Into::into), + "gas_left is incorrect", + ); + let vm_output: Option> = vm.output.map(Into::into); + fail_unless(Some(output) == vm_output, "output is incorrect"); + fail_unless(Some(log_hash) == vm.logs.map(|h| h.0), "logs are incorrect"); + + for (address, account) in vm.post_state.unwrap().into_iter() { + let address = address.into(); + if let Some(code) = account.code { + let code: Vec = code.into(); + let found_code = try_fail!(state.code(&address)); + fail_unless( + found_code + .as_ref() + .map_or_else(|| code.is_empty(), |c| &**c == &code), + "code is incorrect", + ); + } + let found_balance = try_fail!(state.balance(&address)); + let found_nonce = try_fail!(state.nonce(&address)); + if let Some(balance) = account.balance { + fail_unless(found_balance == balance.into(), "balance is incorrect"); + } + if let Some(nonce) = account.nonce { + fail_unless(found_nonce == nonce.into(), "nonce is incorrect"); + } + if let Some(storage) = account.storage { + for (k, v) in storage { + let key: U256 = k.into(); + let value: U256 = v.into(); + let found_storage = + try_fail!(state.storage_at(&address, &From::from(&key))); + fail_unless( + found_storage == From::from(&value), + "storage is incorrect", + ); + } + } + } + + let calls: Option> = + vm.calls.map(|c| c.into_iter().map(From::from).collect()); + fail_unless(Some(callcreates) == calls, "callcreates does not match"); + } + }; + + if fail { + println!(" - vm: {:?}...FAILED", name); + } else { + println!(" - vm: {:?}...OK", name); + } + + start_stop_hook(&format!("{}", name), HookType::OnStop); + } + + failed } - -fn do_json_test_for(vm_type: &VMType, json_data: &[u8], start_stop_hook: &mut H) -> Vec { - let tests = ethjson::vm::Test::load(json_data).unwrap(); - let mut failed = Vec::new(); - - for (name, vm) in tests.into_iter() { - start_stop_hook(&format!("{}-{}", name, vm_type), HookType::OnStart); - - info!(target: "jsontests", "name: {:?}", name); - let mut fail = false; - - let mut fail_unless = |cond: bool, s: &str | if !cond && !fail { - failed.push(format!("[{}] {}: {}", vm_type, name, s)); - fail = true - }; - - macro_rules! try_fail { - ($e: expr) => { - match $e { - Ok(x) => x, - Err(e) => { - let msg = format!("Internal error: {}", e); - fail_unless(false, &msg); - continue - } - } - } - } - - let out_of_gas = vm.out_of_gas(); - let mut state = get_temp_state(); - state.populate_from(From::from(vm.pre_state.clone())); - let info: EnvInfo = From::from(vm.env); - let machine = { - let mut machine = ::ethereum::new_frontier_test_machine(); - machine.set_schedule_creation_rules(Box::new(move |s, _| s.max_depth = 1)); - machine - }; - - let params = ActionParams::from(vm.transaction); - - let mut substate = Substate::new(); - let mut tracer = NoopTracer; - let mut vm_tracer = NoopVMTracer; - let vm_factory = state.vm_factory(); - let origin_info = OriginInfo::from(¶ms); - - // execute - let (res, callcreates) = { - let schedule = machine.schedule(info.number); - let mut ex = try_fail!(TestExt::new( - &mut state, - &info, - &machine, - &schedule, - 0, - &origin_info, - &mut substate, - OutputPolicy::Return, - params.address.clone(), - &mut tracer, - &mut vm_tracer, - )); - let mut evm = vm_factory.create(params, &schedule, 0); - let res = evm.exec(&mut ex).ok().expect("TestExt never trap; resume error never happens; qed"); - // a return in finalize will not alter callcreates - let callcreates = ex.callcreates.clone(); - (res.finalize(ex), callcreates) - }; - - let output = match &res { - Ok(res) => res.return_data.to_vec(), - Err(_) => Vec::new(), - }; - - let log_hash = { - let mut rlp = RlpStream::new_list(substate.logs.len()); - for l in &substate.logs { - rlp.append(l); - } - keccak(&rlp.drain()) - }; - - match res { - Err(_) => fail_unless(out_of_gas, "didn't expect to run out of gas."), - Ok(res) => { - fail_unless(!out_of_gas, "expected to run out of gas."); - fail_unless(Some(res.gas_left) == vm.gas_left.map(Into::into), "gas_left is incorrect"); - let vm_output: Option> = vm.output.map(Into::into); - fail_unless(Some(output) == vm_output, "output is incorrect"); - fail_unless(Some(log_hash) == vm.logs.map(|h| h.0), "logs are incorrect"); - - for (address, account) in vm.post_state.unwrap().into_iter() { - let address = address.into(); - let code: Vec = account.code.into(); - let found_code = try_fail!(state.code(&address)); - let found_balance = try_fail!(state.balance(&address)); - let found_nonce = try_fail!(state.nonce(&address)); - - fail_unless(found_code.as_ref().map_or_else(|| code.is_empty(), |c| &**c == &code), "code is incorrect"); - fail_unless(found_balance == account.balance.into(), "balance is incorrect"); - fail_unless(found_nonce == account.nonce.into(), "nonce is incorrect"); - for (k, v) in account.storage { - let key: U256 = k.into(); - let value: U256 = v.into(); - let found_storage = try_fail!(state.storage_at(&address, &From::from(key))); - fail_unless(found_storage == From::from(value), "storage is incorrect"); - } - } - - let calls: Option> = vm.calls.map(|c| c.into_iter().map(From::from).collect()); - fail_unless(Some(callcreates) == calls, "callcreates does not match"); - } - }; - - start_stop_hook(&format!("{}-{}", name, vm_type), HookType::OnStop); - } - - for f in &failed { - error!("FAILED: {:?}", f); - } - - failed -} - -declare_test!{ExecutiveTests_vmArithmeticTest, "VMTests/vmArithmeticTest"} -declare_test!{ExecutiveTests_vmBitwiseLogicOperationTest, "VMTests/vmBitwiseLogicOperation"} -declare_test!{ExecutiveTests_vmBlockInfoTest, "VMTests/vmBlockInfoTest"} - // TODO [todr] Fails with Signal 11 when using JIT -declare_test!{ExecutiveTests_vmEnvironmentalInfoTest, "VMTests/vmEnvironmentalInfo"} -declare_test!{ExecutiveTests_vmIOandFlowOperationsTest, "VMTests/vmIOandFlowOperations"} -declare_test!{ExecutiveTests_vmLogTest, "VMTests/vmLogTest"} -declare_test!{heavy => ExecutiveTests_vmPerformance, "VMTests/vmPerformance"} -declare_test!{ExecutiveTests_vmPushDupSwapTest, "VMTests/vmPushDupSwapTest"} -declare_test!{ExecutiveTests_vmRandomTest, "VMTests/vmRandomTest"} -declare_test!{ExecutiveTests_vmSha3Test, "VMTests/vmSha3Test"} -declare_test!{ExecutiveTests_vmSystemOperationsTest, "VMTests/vmSystemOperations"} -declare_test!{ExecutiveTests_vmTests, "VMTests/vmTests"} diff --git a/ethcore/src/json_tests/mod.rs b/ethcore/src/json_tests/mod.rs index 99cbdb21eec..004d86d69be 100644 --- a/ethcore/src/json_tests/mod.rs +++ b/ethcore/src/json_tests/mod.rs @@ -1,46 +1,33 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Helpers and tests for operating on jsontests. -#[macro_use] -mod test_common; - -mod transaction; +mod chain; +mod difficulty; mod executive; mod state; -mod chain; +mod test_common; +mod transaction; mod trie; -mod skip; - -#[cfg(test)] -mod difficulty; -pub use self::test_common::HookType; +/// executor of ethereum/json tests +pub mod runner; -pub use self::transaction::run_test_path as run_transaction_test_path; -pub use self::transaction::run_test_file as run_transaction_test_file; -pub use self::executive::run_test_path as run_executive_test_path; -pub use self::executive::run_test_file as run_executive_test_file; -pub use self::state::run_test_path as run_state_test_path; -pub use self::state::run_test_file as run_state_test_file; -pub use self::chain::run_test_path as run_chain_test_path; -pub use self::chain::run_test_file as run_chain_test_file; -pub use self::trie::run_generic_test_path as run_generic_trie_test_path; -pub use self::trie::run_generic_test_file as run_generic_trie_test_file; -pub use self::trie::run_secure_test_path as run_secure_trie_test_path; -pub use self::trie::run_secure_test_file as run_secure_trie_test_file; -use self::skip::SKIP_TEST_STATE; +pub use self::{ + executive::json_executive_test, + test_common::{debug_include_test, find_json_files_recursive, HookType}, +}; diff --git a/ethcore/src/json_tests/runner.rs b/ethcore/src/json_tests/runner.rs new file mode 100644 index 00000000000..4b22146faa9 --- /dev/null +++ b/ethcore/src/json_tests/runner.rs @@ -0,0 +1,252 @@ +use ethjson::test::{ + ChainTests, DifficultyTests, EthereumTestSuite, ExecutiveTests, StateTests, TestChainSpec, + TestTrieSpec, TransactionTests, TrieTests, +}; +use globset::Glob; +use log::info; +use rayon::prelude::*; +use std::path::{Path, PathBuf}; +use tempfile::tempdir; +use trie::TrieSpec; + +/// Result of tests execution +pub struct TestResult { + /// Number of success execution + pub success: usize, + /// Number of success execution + pub failed: Vec, +} + +impl TestResult { + /// Creates a new TestResult without results + pub fn zero() -> Self { + TestResult { + success: 0, + failed: Vec::new(), + } + } + /// Creates a new success TestResult + pub fn success() -> Self { + TestResult { + success: 1, + failed: Vec::new(), + } + } + /// Creates a new failed TestResult + pub fn failed(name: &str) -> Self { + TestResult { + success: 0, + failed: vec![name.to_string()], + } + } +} + +impl std::ops::Add for TestResult { + type Output = Self; + + fn add(self, other: Self) -> Self { + let mut mself = self; + mself.success += other.success; + mself.failed.extend_from_slice(&other.failed); + mself + } +} + +impl std::ops::AddAssign for TestResult { + fn add_assign(&mut self, other: Self) { + self.success += other.success; + self.failed.extend_from_slice(&other.failed); + } +} + +/// An executor of ethereum/json tests +pub struct TestRunner(EthereumTestSuite); + +impl TestRunner { + /// Loads a new JSON Test suite + pub fn load(reader: R) -> Result + where + R: std::io::Read, + { + Ok(TestRunner(serde_json::from_reader(reader)?)) + } + + /// Run the tests with one thread + pub fn run_without_par(&self) -> TestResult { + let pool = rayon::ThreadPoolBuilder::new() + .num_threads(1) + .build() + .unwrap(); + pool.install(|| self.run()) + } + + /// Run the tests + pub fn run(&self) -> TestResult { + let mut res = TestResult::zero(); + for t in &self.0.chain { + res += Self::run_chain_tests(&t); + } + for t in &self.0.state { + res += Self::run_state_tests(&t); + } + for t in &self.0.difficulty { + res += Self::run_difficuly_tests(&t); + } + for t in &self.0.executive { + res += Self::run_executive_tests(&t); + } + for t in &self.0.transaction { + res += Self::run_transaction_tests(&t); + } + for t in &self.0.trie { + res += Self::run_trie_tests(&t); + } + res + } + + fn run1(test: &T, base_path: &PathBuf, f: F) -> TestResult + where + T: Send + Sync, + F: Fn(&T, &Path, &[u8]) -> Vec + Send + Sync, + { + let result = super::find_json_files_recursive(&base_path) + .into_par_iter() + .map(|path| { + info!("{:?}", path); + let json = std::fs::read(&path).unwrap(); + let faileds = f(test, &path, &json); + if faileds.len() > 0 { + TestResult::failed(&faileds.join(",")) + } else { + TestResult::success() + } + }) + .reduce(TestResult::zero, |a, b| a + b); + + if result.success + result.failed.len() == 0 { + panic!("There is no tests in the specified path {:?}", base_path); + } + result + } + + fn in_set(path: &Path, exprs: &[String]) -> bool { + for pathexp in exprs { + let glob = Glob::new(&pathexp) + .expect(&format!("cannot parse expression {}", pathexp)) + .compile_matcher(); + if glob.is_match(path) { + return true; + } + } + false + } + + fn run_chain_tests(test: &ChainTests) -> TestResult { + Self::run1( + test, + &test.path, + |test: &ChainTests, path: &Path, json: &[u8]| { + for skip in &test.skip { + if Self::in_set(&path, &skip.paths) { + println!(" - {} ..SKIPPED", path.to_string_lossy()); + return Vec::new(); + } + } + super::chain::json_chain_test(&test, &path, &json, &mut |_, _| {}) + }, + ) + } + + fn run_state_tests(test: &StateTests) -> TestResult { + Self::run1( + test, + &test.path, + |test: &StateTests, path: &Path, json: &[u8]| { + for skip in &test.skip { + if Self::in_set(&path, &skip.paths) { + println!(" - {} ..SKIPPED", path.to_string_lossy()); + return Vec::new(); + } + } + super::state::json_chain_test(&test, &path, &json, &mut |_, _| {}) + }, + ) + } + + fn run_difficuly_tests(test: &DifficultyTests) -> TestResult { + let mut acc = TestResult::zero(); + for path in &test.path { + acc += Self::run1( + test, + &path, + |test: &DifficultyTests, path: &Path, json: &[u8]| { + let spec = match &test.chainspec { + TestChainSpec::Foundation => { + crate::ethereum::new_foundation(&tempdir().unwrap().path()) + } + TestChainSpec::ByzantiumTest => crate::ethereum::new_byzantium_test(), + TestChainSpec::FrontierTest => crate::ethereum::new_frontier_test(), + TestChainSpec::HomesteadTest => crate::ethereum::new_homestead_test(), + }; + super::difficulty::json_difficulty_test(&path, &json, spec, &mut |_, _| {}) + }, + ) + } + acc + } + + fn run_executive_tests(test: &ExecutiveTests) -> TestResult { + Self::run1( + test, + &test.path, + |_: &ExecutiveTests, path: &Path, json: &[u8]| { + super::executive::json_executive_test(&path, &json, &mut |_, _| {}) + }, + ) + } + + fn run_transaction_tests(test: &TransactionTests) -> TestResult { + Self::run1( + test, + &test.path, + |_: &TransactionTests, path: &Path, json: &[u8]| { + super::transaction::json_transaction_test(&path, &json, &mut |_, _| {}) + }, + ) + } + + fn run_trie_tests(test: &TrieTests) -> TestResult { + let mut acc = TestResult::zero(); + for path in &test.path { + acc += Self::run1(test, &path, |test: &TrieTests, path: &Path, json: &[u8]| { + let spec = match &test.triespec { + TestTrieSpec::Generic => TrieSpec::Generic, + TestTrieSpec::Secure => TrieSpec::Secure, + }; + super::trie::json_trie_test(&path, &json, spec, &mut |_, _| {}) + }); + } + acc + } +} + +#[test] +fn ethereum_json_tests() { + let content = std::fs::read("res/ethereum/runner/full.json") + .expect("cannot open ethereum tests spec file"); + let runner = + TestRunner::load(content.as_slice()).expect("cannot load ethereum tests spec file"); + println!("----------------------------------------------------"); + let result = match std::env::var_os("TEST_DEBUG") { + Some(_) => runner.run_without_par(), + _ => runner.run(), + }; + println!("----------------------------------------------------"); + flushln!( + "SUCCESS: {} FAILED: {} {:?}", + result.success, + result.failed.len(), + result.failed + ); + assert!(result.failed.len() == 0); +} diff --git a/ethcore/src/json_tests/skip.rs b/ethcore/src/json_tests/skip.rs index b6ef9795f69..c9daade7175 100644 --- a/ethcore/src/json_tests/skip.rs +++ b/ethcore/src/json_tests/skip.rs @@ -1,34 +1,32 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! State tests to skip. use ethjson; -#[cfg(feature="ci-skip-tests")] -lazy_static!{ - pub static ref SKIP_TEST_STATE: ethjson::test::SkipStates = { - let skip_data = include_bytes!("../../res/ethereum/tests-issues/currents.json"); - ethjson::test::SkipStates::load(&skip_data[..]).expect("No invalid json allowed") - }; +#[cfg(feature = "ci-skip-tests")] +lazy_static! { + pub static ref SKIP_TEST_STATE: ethjson::test::SkipStates = { + let skip_data = include_bytes!("../../res/ethereum/tests-issues/currents.json"); + ethjson::test::SkipStates::load(&skip_data[..]).expect("No invalid json allowed") + }; } -#[cfg(not(feature="ci-skip-tests"))] -lazy_static!{ - pub static ref SKIP_TEST_STATE: ethjson::test::SkipStates = { - ethjson::test::SkipStates::empty() - }; +#[cfg(not(feature = "ci-skip-tests"))] +lazy_static! { + pub static ref SKIP_TEST_STATE: ethjson::test::SkipStates = ethjson::test::SkipStates::empty(); } diff --git a/ethcore/src/json_tests/state.rs b/ethcore/src/json_tests/state.rs index c51a2c361c2..000088ee722 100644 --- a/ethcore/src/json_tests/state.rs +++ b/ethcore/src/json_tests/state.rs @@ -1,188 +1,149 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::path::Path; -use super::test_common::*; -use pod_state::PodState; -use trace; +use super::{test_common::*, HookType}; use client::{EvmTestClient, EvmTestError, TransactErr, TransactSuccess}; use ethjson; +use pod_state::PodState; +use std::path::Path; +use trace; use types::transaction::SignedTransaction; use vm::EnvInfo; -use super::SKIP_TEST_STATE; -use super::HookType; -/// Run state jsontests on a given folder. -pub fn run_test_path(p: &Path, skip: &[&'static str], h: &mut H) { - ::json_tests::test_common::run_test_path(p, skip, json_chain_test, h) +fn skip_test( + test: ðjson::test::StateTests, + subname: &str, + chain: &String, + number: usize, +) -> bool { + trace!(target: "json-tests", "[state, skip_test] subname: '{}', chain: '{}', number: {}", subname, chain, number); + test.skip.iter().any(|state_test| { + if let Some(subtest) = state_test.names.get(subname) { + trace!(target: "json-tests", "[state, skip_test] Maybe skipping {:?}", subtest); + chain == &subtest.chain + && (subtest.subnumbers[0] == "*" + || subtest.subnumbers.contains(&number.to_string())) + } else { + false + } + }) } -/// Run state jsontests on a given file. -pub fn run_test_file(p: &Path, h: &mut H) { - ::json_tests::test_common::run_test_file(p, json_chain_test, h) -} - -fn skip_test(subname: &str, chain: &String, number: usize) -> bool { - SKIP_TEST_STATE.state.iter().any(|state_test|{ - if let Some(subtest) = state_test.subtests.get(subname) { - chain == &subtest.chain && - (subtest.subnumbers[0] == "*" - || subtest.subnumbers.contains(&number.to_string())) - } else { - false - } - }) -} - -pub fn json_chain_test(json_data: &[u8], start_stop_hook: &mut H) -> Vec { - let _ = ::env_logger::try_init(); - let tests = ethjson::state::test::Test::load(json_data).unwrap(); - let mut failed = Vec::new(); - - for (name, test) in tests.into_iter() { - start_stop_hook(&name, HookType::OnStart); - - { - let multitransaction = test.transaction; - let env: EnvInfo = test.env.into(); - let pre: PodState = test.pre_state.into(); - - for (spec_name, states) in test.post_states { - let total = states.len(); - let spec = match EvmTestClient::spec_from_json(&spec_name) { - Some(spec) => spec, - None => { - println!(" - {} | {:?} Ignoring tests because of missing spec", name, spec_name); - continue; - } - }; - - for (i, state) in states.into_iter().enumerate() { - let info = format!(" - {} | {:?} ({}/{}) ...", name, spec_name, i + 1, total); - if skip_test(&name, &spec.name, i + 1) { - println!("{} in skip list : SKIPPED", info); - continue; - } - - let post_root: H256 = state.hash.into(); - let transaction: SignedTransaction = multitransaction.select(&state.indexes).into(); - - let result = || -> Result<_, EvmTestError> { - Ok(EvmTestClient::from_pod_state(&spec, pre.clone())? - .transact(&env, transaction, trace::NoopTracer, trace::NoopVMTracer)) - }; - match result() { - Err(err) => { - println!("{} !!! Unexpected internal error: {:?}", info, err); - flushln!("{} fail", info); - failed.push(name.clone()); - }, - Ok(Ok(TransactSuccess { state_root, .. })) if state_root != post_root => { - println!("{} !!! State mismatch (got: {}, expect: {}", info, state_root, post_root); - flushln!("{} fail", info); - failed.push(name.clone()); - }, - Ok(Err(TransactErr { state_root, ref error, .. })) if state_root != post_root => { - println!("{} !!! State mismatch (got: {}, expect: {}", info, state_root, post_root); - println!("{} !!! Execution error: {:?}", info, error); - flushln!("{} fail", info); - failed.push(name.clone()); - }, - Ok(Err(TransactErr { error, .. })) => { - flushln!("{} ok ({:?})", info, error); - }, - Ok(_) => { - flushln!("{} ok", info); - }, - } - } - } - } - - start_stop_hook(&name, HookType::OnStop); - } - - if !failed.is_empty() { - println!("!!! {:?} tests failed.", failed.len()); - } - failed -} - -#[cfg(test)] -mod state_tests { - use super::json_chain_test; - use json_tests::HookType; - - fn do_json_test(json_data: &[u8], h: &mut H) -> Vec { - json_chain_test(json_data, h) - } - - declare_test!{GeneralStateTest_stArgsZeroOneBalance, "GeneralStateTests/stArgsZeroOneBalance/"} - declare_test!{GeneralStateTest_stAttackTest, "GeneralStateTests/stAttackTest/"} - declare_test!{GeneralStateTest_stBadOpcodeTest, "GeneralStateTests/stBadOpcode/"} - declare_test!{GeneralStateTest_stBugs, "GeneralStateTests/stBugs/"} - declare_test!{GeneralStateTest_stCallCodes, "GeneralStateTests/stCallCodes/"} - declare_test!{GeneralStateTest_stCallCreateCallCodeTest, "GeneralStateTests/stCallCreateCallCodeTest/"} - declare_test!{GeneralStateTest_stCallDelegateCodesCallCodeHomestead, "GeneralStateTests/stCallDelegateCodesCallCodeHomestead/"} - declare_test!{GeneralStateTest_stCallDelegateCodesHomestead, "GeneralStateTests/stCallDelegateCodesHomestead/"} - declare_test!{GeneralStateTest_stChangedEIP150, "GeneralStateTests/stChangedEIP150/"} - declare_test!{GeneralStateTest_stCodeCopyTest, "GeneralStateTests/stCodeCopyTest/"} - declare_test!{GeneralStateTest_stCodeSizeLimit, "GeneralStateTests/stCodeSizeLimit/"} - declare_test!{GeneralStateTest_stCreate2Test, "GeneralStateTests/stCreate2/"} - declare_test!{GeneralStateTest_stCreateTest, "GeneralStateTests/stCreateTest/"} - declare_test!{GeneralStateTest_stDelegatecallTestHomestead, "GeneralStateTests/stDelegatecallTestHomestead/"} - declare_test!{GeneralStateTest_stEIP150singleCodeGasPrices, "GeneralStateTests/stEIP150singleCodeGasPrices/"} - declare_test!{GeneralStateTest_stEIP150Specific, "GeneralStateTests/stEIP150Specific/"} - declare_test!{GeneralStateTest_stEIP158Specific, "GeneralStateTests/stEIP158Specific/"} - declare_test!{GeneralStateTest_stEWASMTests, "GeneralStateTests/stEWASMTests/"} - declare_test!{GeneralStateTest_stExample, "GeneralStateTests/stExample/"} - declare_test!{GeneralStateTest_stHomesteadSpecific, "GeneralStateTests/stHomesteadSpecific/"} - declare_test!{GeneralStateTest_stInitCodeTest, "GeneralStateTests/stInitCodeTest/"} - declare_test!{GeneralStateTest_stLogTests, "GeneralStateTests/stLogTests/"} - declare_test!{GeneralStateTest_stMemExpandingEIP150Calls, "GeneralStateTests/stMemExpandingEIP150Calls/"} - declare_test!{heavy => GeneralStateTest_stMemoryStressTest, "GeneralStateTests/stMemoryStressTest/"} - declare_test!{GeneralStateTest_stMemoryTest, "GeneralStateTests/stMemoryTest/"} - declare_test!{GeneralStateTest_stNonZeroCallsTest, "GeneralStateTests/stNonZeroCallsTest/"} - declare_test!{GeneralStateTest_stPreCompiledContracts, "GeneralStateTests/stPreCompiledContracts/"} - declare_test!{GeneralStateTest_stPreCompiledContracts2, "GeneralStateTests/stPreCompiledContracts2/"} - declare_test!{heavy => GeneralStateTest_stQuadraticComplexityTest, "GeneralStateTests/stQuadraticComplexityTest/"} - declare_test!{GeneralStateTest_stRandom, "GeneralStateTests/stRandom/"} - declare_test!{GeneralStateTest_stRandom2, "GeneralStateTests/stRandom2/"} - declare_test!{GeneralStateTest_stRecursiveCreate, "GeneralStateTests/stRecursiveCreate/"} - declare_test!{GeneralStateTest_stRefundTest, "GeneralStateTests/stRefundTest/"} - declare_test!{GeneralStateTest_stReturnDataTest, "GeneralStateTests/stReturnDataTest/"} - declare_test!{GeneralStateTest_stRevertTest, "GeneralStateTests/stRevertTest/"} - declare_test!{GeneralStateTest_stSStoreTest, "GeneralStateTests/stSStoreTest/"} - declare_test!{GeneralStateTest_stShift, "GeneralStateTests/stShift/"} - declare_test!{GeneralStateTest_stSolidityTest, "GeneralStateTests/stSolidityTest/"} - declare_test!{GeneralStateTest_stSpecialTest, "GeneralStateTests/stSpecialTest/"} - declare_test!{GeneralStateTest_stStackTests, "GeneralStateTests/stStackTests/"} - declare_test!{GeneralStateTest_stStaticCall, "GeneralStateTests/stStaticCall/"} - declare_test!{GeneralStateTest_stSystemOperationsTest, "GeneralStateTests/stSystemOperationsTest/"} - declare_test!{GeneralStateTest_stTransactionTest, "GeneralStateTests/stTransactionTest/"} - declare_test!{GeneralStateTest_stTransitionTest, "GeneralStateTests/stTransitionTest/"} - declare_test!{GeneralStateTest_stWalletTest, "GeneralStateTests/stWalletTest/"} - declare_test!{GeneralStateTest_stZeroCallsRevert, "GeneralStateTests/stZeroCallsRevert/"} - declare_test!{GeneralStateTest_stZeroCallsTest, "GeneralStateTests/stZeroCallsTest/"} - declare_test!{GeneralStateTest_stZeroKnowledge, "GeneralStateTests/stZeroKnowledge/"} - - // Attempts to send a transaction that requires more than current balance: - // Tx: - // https://github.com/ethereum/tests/blob/726b161ba8a739691006cc1ba080672bb50a9d49/GeneralStateTests/stZeroKnowledge2/ecmul_0-3_5616_28000_96.json#L170 - // Balance: - // https://github.com/ethereum/tests/blob/726b161ba8a739691006cc1ba080672bb50a9d49/GeneralStateTests/stZeroKnowledge2/ecmul_0-3_5616_28000_96.json#L126 - declare_test!{GeneralStateTest_stZeroKnowledge2, "GeneralStateTests/stZeroKnowledge2/"} +pub fn json_chain_test( + state_test: ðjson::test::StateTests, + path: &Path, + json_data: &[u8], + start_stop_hook: &mut H, +) -> Vec { + let _ = ::env_logger::try_init(); + let tests = ethjson::state::test::Test::load(json_data).expect(&format!( + "Could not parse JSON state test data from {}", + path.display() + )); + let mut failed = Vec::new(); + + for (name, test) in tests.into_iter() { + if !super::debug_include_test(&name) { + continue; + } + + start_stop_hook(&name, HookType::OnStart); + + { + let multitransaction = test.transaction; + let env: EnvInfo = test.env.into(); + let pre: PodState = test.pre_state.into(); + + for (spec_name, states) in test.post_states { + let total = states.len(); + let spec = match EvmTestClient::spec_from_json(&spec_name) { + Some(spec) => spec, + None => { + panic!( + "Unimplemented chainspec '{:?}' in test '{}'", + spec_name, name + ); + } + }; + + for (i, state) in states.into_iter().enumerate() { + let info = format!( + " - state: {} | {:?} ({}/{}) ...", + name, + spec_name, + i + 1, + total + ); + if skip_test(&state_test, &name, &spec.name, i + 1) { + println!("{}: SKIPPED", info); + continue; + } + + let post_root: H256 = state.hash.into(); + let transaction: SignedTransaction = + multitransaction.select(&state.indexes).into(); + + let result = || -> Result<_, EvmTestError> { + Ok(EvmTestClient::from_pod_state(&spec, pre.clone())?.transact( + &env, + transaction, + trace::NoopTracer, + trace::NoopVMTracer, + )) + }; + match result() { + Err(err) => { + println!("{} !!! Unexpected internal error: {:?}", info, err); + flushln!("{} fail", info); + failed.push(name.clone()); + } + Ok(Ok(TransactSuccess { state_root, .. })) if state_root != post_root => { + println!( + "{} !!! State mismatch (got: {}, expect: {}", + info, state_root, post_root + ); + flushln!("{} fail", info); + failed.push(name.clone()); + } + Ok(Err(TransactErr { + state_root, + ref error, + .. + })) if state_root != post_root => { + println!( + "{} !!! State mismatch (got: {}, expect: {}", + info, state_root, post_root + ); + println!("{} !!! Execution error: {:?}", info, error); + flushln!("{} fail", info); + failed.push(name.clone()); + } + Ok(Err(TransactErr { error, .. })) => { + flushln!("{} ok ({:?})", info, error); + } + Ok(_) => { + flushln!("{} ok", info); + } + } + } + } + } + + start_stop_hook(&name, HookType::OnStop); + } + + failed } diff --git a/ethcore/src/json_tests/test_common.rs b/ethcore/src/json_tests/test_common.rs index 7e3842ecb75..73a6a500c92 100644 --- a/ethcore/src/json_tests/test_common.rs +++ b/ethcore/src/json_tests/test_common.rs @@ -1,150 +1,50 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::collections::HashSet; -use std::io::Read; -use std::fs::{File, read_dir}; -use std::path::Path; -use std::ffi::OsString; -pub use ethereum_types::{H256, U256, Address}; +pub use ethereum_types::{Address, H256, U256}; +use std::path::PathBuf; +use walkdir::{DirEntry, WalkDir}; /// Indicate when to run the hook passed to test functions. #[derive(Copy, Clone, Eq, PartialEq)] pub enum HookType { - /// Hook to code to run on test start. - OnStart, - /// Hook to code to run on test end. - OnStop + /// Hook to code to run on test start. + OnStart, + /// Hook to code to run on test end. + OnStop, } -pub fn run_test_path( - p: &Path, skip: &[&'static str], - runner: fn(json_data: &[u8], start_stop_hook: &mut H) -> Vec, - start_stop_hook: &mut H -) { - let mut errors = Vec::new(); - run_test_path_inner(p, skip, runner, start_stop_hook, &mut errors); - let empty: [String; 0] = []; - assert_eq!(errors, empty); +/// find all json files recursively from a path +pub fn find_json_files_recursive(path: &PathBuf) -> 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 run_test_path_inner( - p: &Path, skip: &[&'static str], - runner: fn(json_data: &[u8], start_stop_hook: &mut H) -> Vec, - start_stop_hook: &mut H, - errors: &mut Vec -) { - let path = Path::new(p); - let s: HashSet = skip.iter().map(|s| { - let mut os: OsString = s.into(); - os.push(".json"); - os - }).collect(); - let extension = path.extension().and_then(|s| s.to_str()); - if path.is_dir() { - for p in read_dir(path).unwrap().filter_map(|e| { - let e = e.unwrap(); - if s.contains(&e.file_name()) { - None - } else { - Some(e.path()) - }}) { - run_test_path_inner(&p, skip, runner, start_stop_hook, errors); - } - } else if extension == Some("swp") || extension == None { - // Ignore junk - } else { - let mut path = p.to_path_buf(); - path.set_extension("json"); - run_test_file_append(&path, runner, start_stop_hook, errors) - } -} - -fn run_test_file_append( - path: &Path, - runner: fn(json_data: &[u8], start_stop_hook: &mut H) -> Vec, - start_stop_hook: &mut H, - errors: &mut Vec -) { - let mut data = Vec::new(); - let mut file = match File::open(&path) { - Ok(file) => file, - Err(_) => panic!("Error opening test file at: {:?}", path), - }; - file.read_to_end(&mut data).expect("Error reading test file"); - errors.append(&mut runner(&data, start_stop_hook)); -} - -pub fn run_test_file( - path: &Path, - runner: fn(json_data: &[u8], start_stop_hook: &mut H) -> Vec, - start_stop_hook: &mut H -) { - let mut data = Vec::new(); - let mut file = match File::open(&path) { - Ok(file) => file, - Err(_) => panic!("Error opening test file at: {:?}", path), - }; - file.read_to_end(&mut data).expect("Error reading test file"); - let results = runner(&data, start_stop_hook); - let empty: [String; 0] = []; - assert_eq!(results, empty); -} - -#[cfg(test)] -macro_rules! test { - ($name: expr, $skip: expr) => { - ::json_tests::test_common::run_test_path(::std::path::Path::new(concat!("res/ethereum/tests/", $name)), &$skip, do_json_test, &mut |_, _| ()); - } -} - -#[macro_export] -macro_rules! declare_test { - (skip => $arr: expr, $id: ident, $name: expr) => { - #[cfg(test)] - #[test] - #[allow(non_snake_case)] - fn $id() { - test!($name, $arr); - } - }; - (ignore => $id: ident, $name: expr) => { - #[cfg(test)] - #[ignore] - #[test] - #[allow(non_snake_case)] - fn $id() { - test!($name, []); - } - }; - (heavy => $id: ident, $name: expr) => { - #[cfg(test)] - #[cfg(feature = "test-heavy")] - #[test] - #[allow(non_snake_case)] - fn $id() { - test!($name, []); - } - }; - ($id: ident, $name: expr) => { - #[cfg(test)] - #[test] - #[allow(non_snake_case)] - fn $id() { - test!($name, []); - } - } +/// check if the test is selected to execute via TEST_DEBUG environment variable +pub fn debug_include_test(name: &str) -> bool { + match std::env::var_os("TEST_DEBUG") { + Some(s) => s.to_string_lossy().split_terminator(",").any(|expr| { + regex::Regex::new(expr) + .expect("invalid regex expression in TEST_DEBUG") + .is_match(name) + }), + _ => true, + } } diff --git a/ethcore/src/json_tests/transaction.rs b/ethcore/src/json_tests/transaction.rs index febc61404da..1146e24e3aa 100644 --- a/ethcore/src/json_tests/transaction.rs +++ b/ethcore/src/json_tests/transaction.rs @@ -1,112 +1,109 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::path::Path; use super::test_common::*; use client::EvmTestClient; use ethjson; use rlp::Rlp; -use types::header::Header; -use types::transaction::UnverifiedTransaction; +use std::path::Path; use transaction_ext::Transaction; - -/// Run transaction jsontests on a given folder. -pub fn run_test_path(p: &Path, skip: &[&'static str], h: &mut H) { - ::json_tests::test_common::run_test_path(p, skip, do_json_test, h) -} - -/// Run transaction jsontests on a given file. -pub fn run_test_file(p: &Path, h: &mut H) { - ::json_tests::test_common::run_test_file(p, do_json_test, h) -} - -// Block number used to run the tests. -// Make sure that all the specified features are activated. -const BLOCK_NUMBER: u64 = 0x6ffffffffffffe; - -fn do_json_test(json_data: &[u8], start_stop_hook: &mut H) -> Vec { - let tests = ethjson::transaction::Test::load(json_data).unwrap(); - let mut failed = Vec::new(); - for (name, test) in tests.into_iter() { - start_stop_hook(&name, HookType::OnStart); - - for (spec_name, result) in test.post_state { - let spec = match EvmTestClient::spec_from_json(&spec_name) { - Some(spec) => spec, - None => { - println!(" - {} | {:?} Ignoring tests because of missing spec", name, spec_name); - continue; - } - }; - - let mut fail_unless = |cond: bool, title: &str| if !cond { - failed.push(format!("{}-{:?}", name, spec_name)); - println!("Transaction failed: {:?}-{:?}: {:?}", name, spec_name, title); - }; - - let rlp: Vec = test.rlp.clone().into(); - let res = Rlp::new(&rlp) - .as_val() - .map_err(::error::Error::from) - .and_then(|t: UnverifiedTransaction| { - let mut header: Header = Default::default(); - // Use high enough number to activate all required features. - header.set_number(BLOCK_NUMBER); - - let minimal = t.gas_required(&spec.engine.schedule(header.number())).into(); - if t.gas < minimal { - return Err(::types::transaction::Error::InsufficientGas { - minimal, got: t.gas, - }.into()); - } - spec.engine.verify_transaction_basic(&t, &header)?; - Ok(spec.engine.verify_transaction_unordered(t, &header)?) - }); - - match (res, result.hash, result.sender) { - (Ok(t), Some(hash), Some(sender)) => { - fail_unless(t.sender() == sender.into(), "sender mismatch"); - fail_unless(t.hash() == hash.into(), "hash mismatch"); - }, - (Err(_), None, None) => {}, - data => { - fail_unless( - false, - &format!("Validity different: {:?}", data) - ); - } - } - } - - start_stop_hook(&name, HookType::OnStop); - } - - for f in &failed { - println!("FAILED: {:?}", f); - } - failed +use types::{header::Header, transaction::UnverifiedTransaction}; + +pub fn json_transaction_test( + path: &Path, + json_data: &[u8], + start_stop_hook: &mut H, +) -> Vec { + // Block number used to run the tests. + // Make sure that all the specified features are activated. + const BLOCK_NUMBER: u64 = 0x6ffffffffffffe; + + let tests = ethjson::transaction::Test::load(json_data).expect(&format!( + "Could not parse JSON transaction test data from {}", + path.display() + )); + let mut failed = Vec::new(); + for (name, test) in tests.into_iter() { + if !super::debug_include_test(&name) { + continue; + } + + start_stop_hook(&name, HookType::OnStart); + + println!(" - tx: {} ", name); + + for (spec_name, result) in test.post_state { + let spec = match EvmTestClient::spec_from_json(&spec_name) { + Some(spec) => spec, + None => { + failed.push(format!("{}-{:?} (missing spec)", name, spec_name)); + continue; + } + }; + + let mut fail_unless = |cond: bool, title: &str| { + if !cond { + failed.push(format!("{}-{:?}", name, spec_name)); + println!( + "Transaction failed: {:?}-{:?}: {:?}", + name, spec_name, title + ); + } + }; + + let rlp: Vec = test.rlp.clone().into(); + let res = Rlp::new(&rlp) + .as_val() + .map_err(::error::Error::from) + .and_then(|t: UnverifiedTransaction| { + let mut header: Header = Default::default(); + // Use high enough number to activate all required features. + header.set_number(BLOCK_NUMBER); + + let minimal = t + .gas_required(&spec.engine.schedule(header.number())) + .into(); + if t.gas < minimal { + return Err(::types::transaction::Error::InsufficientGas { + minimal, + got: t.gas, + } + .into()); + } + spec.engine.verify_transaction_basic(&t, &header)?; + Ok(spec.engine.verify_transaction_unordered(t, &header)?) + }); + + match (res, result.hash, result.sender) { + (Ok(t), Some(hash), Some(sender)) => { + fail_unless(t.sender() == sender.into(), "sender mismatch"); + fail_unless(t.hash() == hash.into(), "hash mismatch"); + } + (Err(_), None, None) => {} + data => { + fail_unless(false, &format!("Validity different: {:?}", data)); + } + } + } + + start_stop_hook(&name, HookType::OnStop); + } + + for f in &failed { + println!("FAILED: {:?}", f); + } + failed } - -declare_test!{TransactionTests_ttAddress, "TransactionTests/ttAddress"} -declare_test!{TransactionTests_ttData, "TransactionTests/ttData"} -declare_test!{TransactionTests_ttGasLimit, "TransactionTests/ttGasLimit"} -declare_test!{TransactionTests_ttGasPrice, "TransactionTests/ttGasPrice"} -declare_test!{TransactionTests_ttNonce, "TransactionTests/ttNonce"} -declare_test!{TransactionTests_ttRSValue, "TransactionTests/ttRSValue"} -declare_test!{TransactionTests_ttSignature, "TransactionTests/ttSignature"} -declare_test!{TransactionTests_ttValue, "TransactionTests/ttValue"} -declare_test!{TransactionTests_ttVValue, "TransactionTests/ttVValue"} -declare_test!{TransactionTests_ttWrongRLP, "TransactionTests/ttWrongRLP"} diff --git a/ethcore/src/json_tests/trie.rs b/ethcore/src/json_tests/trie.rs index d56490ec7e8..7f26318fb15 100644 --- a/ethcore/src/json_tests/trie.rs +++ b/ethcore/src/json_tests/trie.rs @@ -1,109 +1,68 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . +use ethereum_types::H256; use ethjson; -use trie::{TrieFactory, TrieSpec}; use ethtrie::RlpCodec; -use ethereum_types::H256; +use std::path::Path; +use trie::{TrieFactory, TrieSpec}; use super::HookType; -pub use self::generic::run_test_path as run_generic_test_path; -pub use self::generic::run_test_file as run_generic_test_file; -pub use self::secure::run_test_path as run_secure_test_path; -pub use self::secure::run_test_file as run_secure_test_file; - -fn test_trie(json: &[u8], trie: TrieSpec, start_stop_hook: &mut H) -> Vec { - let tests = ethjson::trie::Test::load(json).unwrap(); - let factory = TrieFactory::<_, RlpCodec>::new(trie); - let mut result = vec![]; - - for (name, test) in tests.into_iter() { - start_stop_hook(&name, HookType::OnStart); - - let mut memdb = journaldb::new_memory_db(); - let mut root = H256::default(); - let mut t = factory.create(&mut memdb, &mut root); - - for (key, value) in test.input.data.into_iter() { - let key: Vec = key.into(); - let value: Vec = value.map_or_else(Vec::new, Into::into); - t.insert(&key, &value) - .expect(&format!("Trie test '{:?}' failed due to internal error", name)); - } - - if *t.root() != test.root.into() { - result.push(format!("Trie test '{:?}' failed.", name)); - } - - start_stop_hook(&name, HookType::OnStop); - } - - for i in &result { - println!("FAILED: {}", i); - } - - result -} - -mod generic { - use std::path::Path; - use trie::TrieSpec; - - use super::HookType; - - /// Run generic trie jsontests on a given folder. - pub fn run_test_path(p: &Path, skip: &[&'static str], h: &mut H) { - ::json_tests::test_common::run_test_path(p, skip, do_json_test, h) - } - - /// Run generic trie jsontests on a given file. - pub fn run_test_file(p: &Path, h: &mut H) { - ::json_tests::test_common::run_test_file(p, do_json_test, h) - } - - fn do_json_test(json: &[u8], h: &mut H) -> Vec { - super::test_trie(json, TrieSpec::Generic, h) - } - - declare_test!{TrieTests_trietest, "TrieTests/trietest"} - declare_test!{TrieTests_trieanyorder, "TrieTests/trieanyorder"} -} - -mod secure { - use std::path::Path; - use trie::TrieSpec; - - use super::HookType; - - /// Run secure trie jsontests on a given folder. - pub fn run_test_path(p: &Path, skip: &[&'static str], h: &mut H) { - ::json_tests::test_common::run_test_path(p, skip, do_json_test, h) - } - - /// Run secure trie jsontests on a given file. - pub fn run_test_file(p: &Path, h: &mut H) { - ::json_tests::test_common::run_test_file(p, do_json_test, h) - } - - fn do_json_test(json: &[u8], h: &mut H) -> Vec { - super::test_trie(json, TrieSpec::Secure, h) - } - - declare_test!{TrieTests_hex_encoded_secure, "TrieTests/hex_encoded_securetrie_test"} - declare_test!{TrieTests_trietest_secure, "TrieTests/trietest_secureTrie"} - declare_test!{TrieTests_trieanyorder_secure, "TrieTests/trieanyorder_secureTrie"} +pub fn json_trie_test( + path: &Path, + json: &[u8], + trie: TrieSpec, + start_stop_hook: &mut H, +) -> Vec { + let tests = ethjson::trie::Test::load(json).expect(&format!( + "Could not parse JSON trie test data from {}", + path.display() + )); + let factory = TrieFactory::<_, RlpCodec>::new(trie); + let mut failed = vec![]; + + for (name, test) in tests.into_iter() { + if !super::debug_include_test(&name) { + continue; + } + + start_stop_hook(&name, HookType::OnStart); + + let mut memdb = journaldb::new_memory_db(); + let mut root = H256::zero(); + let mut t = factory.create(&mut memdb, &mut root); + + for (key, value) in test.input.data.into_iter() { + let key: Vec = key.into(); + let value: Vec = value.map_or_else(Vec::new, Into::into); + t.insert(&key, &value).expect(&format!( + "Trie test '{:?}' failed due to internal error", + name + )); + } + + if *t.root() == test.root.into() { + println!(" - trie: {}...OK", name); + } else { + println!(" - trie: {}...FAILED ({:?})", name, path); + failed.push(format!("{}", name)); + } + start_stop_hook(&name, HookType::OnStop); + } + + failed } diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index eee076b32ea..8a95f221393 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -1,72 +1,30 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . #![warn(missing_docs, unused_extern_crates)] -#![cfg_attr(feature = "time_checked_add", feature(time_checked_add))] //! Ethcore library -//! -//! ### Rust version: -//! - nightly -//! -//! ### Supported platforms: -//! - OSX -//! - Linux -//! -//! ### Building: -//! -//! - Ubuntu 14.04 and later: -//! -//! ```bash -//! -//! # install rustup -//! curl https://sh.rustup.rs -sSf | sh -//! -//! # download and build parity -//! git clone https://github.com/paritytech/parity-ethereum -//! cd parity -//! cargo build --release -//! ``` -//! -//! - OSX: -//! -//! ```bash -//! # install rocksdb && rustup -//! brew update -//! curl https://sh.rustup.rs -sSf | sh -//! -//! # download and build parity -//! git clone https://github.com/paritytech/parity-ethereum -//! cd parity -//! cargo build --release -//! ``` - -// Recursion limit required because of -// error_chain foreign_links. -#![recursion_limit="128"] extern crate ansi_term; -extern crate bn; -extern crate byteorder; extern crate common_types as types; -extern crate crossbeam; +extern crate crossbeam_utils; extern crate ethabi; extern crate ethash; extern crate ethcore_blockchain as blockchain; -extern crate ethcore_bloom_journal as bloom_journal; +extern crate ethcore_builtin as builtin; extern crate ethcore_call_contract as call_contract; extern crate ethcore_db as db; extern crate ethcore_io as io; @@ -81,18 +39,15 @@ extern crate journaldb; extern crate keccak_hash as hash; extern crate keccak_hasher; extern crate kvdb; -extern crate kvdb_memorydb; extern crate len_caching_lock; extern crate lru_cache; +extern crate maplit; extern crate memory_cache; extern crate memory_db; -extern crate num; extern crate num_cpus; extern crate parity_bytes as bytes; -extern crate parity_crypto; extern crate parity_snappy as snappy; extern crate parking_lot; -extern crate trie_db as trie; extern crate patricia_trie_ethereum as ethtrie; extern crate rand; extern crate rayon; @@ -100,26 +55,36 @@ extern crate rlp; extern crate rustc_hex; extern crate serde; extern crate stats; +extern crate time_utils; +extern crate trie_db as trie; extern crate triehash_ethereum as triehash; extern crate unexpected; extern crate using_queue; extern crate vm; extern crate wasm; +#[cfg(any(test, feature = "blooms-db"))] +extern crate blooms_db; +#[cfg(any(test, feature = "env_logger"))] +extern crate env_logger; #[cfg(test)] extern crate ethcore_accounts as accounts; #[cfg(feature = "stratum")] extern crate ethcore_stratum; -#[cfg(any(test, feature = "tempdir"))] -extern crate tempdir; +#[cfg(feature = "json-tests")] +extern crate globset; +#[cfg(any(test, feature = "test-helpers"))] +extern crate kvdb_memorydb; #[cfg(any(test, feature = "kvdb-rocksdb"))] extern crate kvdb_rocksdb; -#[cfg(any(test, feature = "blooms-db"))] -extern crate blooms_db; -#[cfg(any(test, feature = "env_logger"))] -extern crate env_logger; #[cfg(test)] extern crate rlp_compress; +#[cfg(any(test, feature = "tempdir"))] +extern crate tempdir; +#[cfg(feature = "json-tests")] +extern crate tempfile; +#[cfg(feature = "json-tests")] +extern crate walkdir; #[macro_use] extern crate ethabi_derive; @@ -149,11 +114,7 @@ extern crate fetch; #[cfg(all(test, feature = "price-info"))] extern crate parity_runtime; -#[cfg(not(time_checked_add))] -extern crate time_utils; - pub mod block; -pub mod builtin; pub mod client; pub mod engines; pub mod error; @@ -162,8 +123,8 @@ pub mod executed; pub mod executive; pub mod machine; pub mod miner; -pub mod pod_state; pub mod pod_account; +pub mod pod_state; pub mod snapshot; pub mod spec; pub mod state; @@ -177,13 +138,13 @@ mod externalities; mod factory; mod tx_filter; -#[cfg(test)] -mod tests; #[cfg(feature = "json-tests")] pub mod json_tests; #[cfg(any(test, feature = "test-helpers"))] pub mod test_helpers; +#[cfg(test)] +mod tests; -pub use executive::contract_address; pub use evm::CreateContractAddress; +pub use executive::contract_address; pub use trie::TrieSpec; diff --git a/ethcore/src/machine/impls.rs b/ethcore/src/machine/impls.rs index 72f20ecdf7d..f848a320d40 100644 --- a/ethcore/src/machine/impls.rs +++ b/ethcore/src/machine/impls.rs @@ -1,32 +1,39 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Ethereum-like state machine definition. -use std::collections::{BTreeMap, HashMap}; -use std::cmp; -use std::sync::Arc; +use std::{ + cmp, + collections::{BTreeMap, HashMap}, + sync::Arc, +}; -use ethereum_types::{U256, H256, Address}; +use ethereum_types::{Address, H256, U256}; use rlp::Rlp; -use types::transaction::{self, SYSTEM_ADDRESS, UNSIGNED_SENDER, UnverifiedTransaction, SignedTransaction}; -use types::BlockNumber; -use types::header::Header; -use vm::{CallType, ActionParams, ActionValue, ParamsType}; -use vm::{EnvInfo, Schedule, CreateContractAddress}; +use types::{ + header::Header, + transaction::{ + self, SignedTransaction, UnverifiedTransaction, SYSTEM_ADDRESS, UNSIGNED_SENDER, + }, + BlockNumber, +}; +use vm::{ + ActionParams, ActionValue, CallType, CreateContractAddress, EnvInfo, ParamsType, Schedule, +}; use block::ExecutedBlock; use builtin::Builtin; @@ -39,507 +46,603 @@ use state::{CleanupMode, Substate}; use trace::{NoopTracer, NoopVMTracer}; use tx_filter::TransactionFilter; -/// Parity tries to round block.gas_limit to multiple of this constant -pub const PARITY_GAS_LIMIT_DETERMINANT: U256 = U256([37, 0, 0, 0]); +/// Open tries to round block.gas_limit to multiple of this constant +pub const GAS_LIMIT_DETERMINANT: U256 = U256([37, 0, 0, 0]); /// Ethash-specific extensions. #[derive(Debug, Clone)] pub struct EthashExtensions { - /// Homestead transition block number. - pub homestead_transition: BlockNumber, - /// DAO hard-fork transition block (X). - pub dao_hardfork_transition: u64, - /// DAO hard-fork refund contract address (C). - pub dao_hardfork_beneficiary: Address, - /// DAO hard-fork DAO accounts list (L) - pub dao_hardfork_accounts: Vec
, + /// Homestead transition block number. + pub homestead_transition: BlockNumber, + /// DAO hard-fork transition block (X). + pub dao_hardfork_transition: u64, + /// DAO hard-fork refund contract address (C). + pub dao_hardfork_beneficiary: Address, + /// DAO hard-fork DAO accounts list (L) + pub dao_hardfork_accounts: Vec
, } impl From<::ethjson::spec::EthashParams> for EthashExtensions { - fn from(p: ::ethjson::spec::EthashParams) -> Self { - EthashExtensions { - homestead_transition: p.homestead_transition.map_or(0, Into::into), - dao_hardfork_transition: p.dao_hardfork_transition.map_or(u64::max_value(), Into::into), - dao_hardfork_beneficiary: p.dao_hardfork_beneficiary.map_or_else(Address::new, Into::into), - dao_hardfork_accounts: p.dao_hardfork_accounts.unwrap_or_else(Vec::new).into_iter().map(Into::into).collect(), - } - } + fn from(p: ::ethjson::spec::EthashParams) -> Self { + EthashExtensions { + homestead_transition: p.homestead_transition.map_or(0, Into::into), + dao_hardfork_transition: p + .dao_hardfork_transition + .map_or(u64::max_value(), Into::into), + dao_hardfork_beneficiary: p + .dao_hardfork_beneficiary + .map_or_else(Address::new, Into::into), + dao_hardfork_accounts: p + .dao_hardfork_accounts + .unwrap_or_else(Vec::new) + .into_iter() + .map(Into::into) + .collect(), + } + } } /// Special rules to be applied to the schedule. -pub type ScheduleCreationRules = Fn(&mut Schedule, BlockNumber) + Sync + Send; +pub type ScheduleCreationRules = dyn Fn(&mut Schedule, BlockNumber) + Sync + Send; /// An ethereum-like state machine. pub struct EthereumMachine { - params: CommonParams, - builtins: Arc>, - tx_filter: Option>, - ethash_extensions: Option, - schedule_rules: Option>, + params: CommonParams, + builtins: Arc>, + tx_filter: Option>, + ethash_extensions: Option, + schedule_rules: Option>, } impl EthereumMachine { - /// Regular ethereum machine. - pub fn regular(params: CommonParams, builtins: BTreeMap) -> EthereumMachine { - let tx_filter = TransactionFilter::from_params(¶ms).map(Arc::new); - EthereumMachine { - params: params, - builtins: Arc::new(builtins), - tx_filter: tx_filter, - ethash_extensions: None, - schedule_rules: None, - } - } - - /// Ethereum machine with ethash extensions. - // TODO: either unify or specify to mainnet specifically and include other specific-chain HFs? - pub fn with_ethash_extensions(params: CommonParams, builtins: BTreeMap, extensions: EthashExtensions) -> EthereumMachine { - let mut machine = EthereumMachine::regular(params, builtins); - machine.ethash_extensions = Some(extensions); - machine - } - - /// Attach special rules to the creation of schedule. - pub fn set_schedule_creation_rules(&mut self, rules: Box) { - self.schedule_rules = Some(rules); - } - - /// Get a reference to the ethash-specific extensions. - pub fn ethash_extensions(&self) -> Option<&EthashExtensions> { - self.ethash_extensions.as_ref() - } + /// Regular ethereum machine. + pub fn regular(params: CommonParams, builtins: BTreeMap) -> EthereumMachine { + let tx_filter = TransactionFilter::from_params(¶ms).map(Arc::new); + EthereumMachine { + params: params, + builtins: Arc::new(builtins), + tx_filter: tx_filter, + ethash_extensions: None, + schedule_rules: None, + } + } + + /// Ethereum machine with ethash extensions. + // TODO: either unify or specify to mainnet specifically and include other specific-chain HFs? + pub fn with_ethash_extensions( + params: CommonParams, + builtins: BTreeMap, + extensions: EthashExtensions, + ) -> EthereumMachine { + let mut machine = EthereumMachine::regular(params, builtins); + machine.ethash_extensions = Some(extensions); + machine + } + + /// Attach special rules to the creation of schedule. + pub fn set_schedule_creation_rules(&mut self, rules: Box) { + self.schedule_rules = Some(rules); + } + + /// Get a reference to the ethash-specific extensions. + pub fn ethash_extensions(&self) -> Option<&EthashExtensions> { + self.ethash_extensions.as_ref() + } } impl EthereumMachine { - /// Execute a call as the system address. Block environment information passed to the - /// VM is modified to have its gas limit bounded at the upper limit of possible used - /// gases including this system call, capped at the maximum value able to be - /// represented by U256. This system call modifies the block state, but discards other - /// information. If suicides, logs or refunds happen within the system call, they - /// will not be executed or recorded. Gas used by this system call will not be counted - /// on the block. - pub fn execute_as_system( - &self, - block: &mut ExecutedBlock, - contract_address: Address, - gas: U256, - data: Option>, - ) -> Result, Error> { - let (code, code_hash) = { - let state = &block.state; - - (state.code(&contract_address)?, - state.code_hash(&contract_address)?) - }; - - self.execute_code_as_system( - block, - Some(contract_address), - code, - code_hash, - None, - gas, - data, - None, - ) - } - - /// Same as execute_as_system, but execute code directly. If contract address is None, use the null sender - /// address. If code is None, then this function has no effect. The call is executed without finalization, and does - /// not form a transaction. - pub fn execute_code_as_system( - &self, - block: &mut ExecutedBlock, - contract_address: Option
, - code: Option>>, - code_hash: Option, - value: Option, - gas: U256, - data: Option>, - call_type: Option, - ) -> Result, Error> { - let env_info = { - let mut env_info = block.env_info(); - env_info.gas_limit = env_info.gas_used.saturating_add(gas); - env_info - }; - - let mut state = block.state_mut(); - - let params = ActionParams { - code_address: contract_address.unwrap_or(UNSIGNED_SENDER), - address: contract_address.unwrap_or(UNSIGNED_SENDER), - sender: SYSTEM_ADDRESS, - origin: SYSTEM_ADDRESS, - gas, - gas_price: 0.into(), - value: value.unwrap_or_else(|| ActionValue::Transfer(0.into())), - code, - code_hash, - data, - call_type: call_type.unwrap_or(CallType::Call), - params_type: ParamsType::Separate, - }; - let schedule = self.schedule(env_info.number); - let mut ex = Executive::new(&mut state, &env_info, self, &schedule); - let mut substate = Substate::new(); - - let res = ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).map_err(|e| ::engines::EngineError::FailedSystemCall(format!("{}", e)))?; - let output = res.return_data.to_vec(); - - Ok(output) - } - - /// Push last known block hash to the state. - fn push_last_hash(&self, block: &mut ExecutedBlock) -> Result<(), Error> { - let params = self.params(); - if block.header.number() == params.eip210_transition { - let state = block.state_mut(); - state.init_code(¶ms.eip210_contract_address, params.eip210_contract_code.clone())?; - } - if block.header.number() >= params.eip210_transition { - let parent_hash = *block.header.parent_hash(); - let _ = self.execute_as_system( - block, - params.eip210_contract_address, - params.eip210_contract_gas, - Some(parent_hash.to_vec()), - )?; - } - Ok(()) - } - - /// Logic to perform on a new block: updating last hashes and the DAO - /// fork, for ethash. - pub fn on_new_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { - self.push_last_hash(block)?; - - if let Some(ref ethash_params) = self.ethash_extensions { - if block.header.number() == ethash_params.dao_hardfork_transition { - let state = block.state_mut(); - for child in ðash_params.dao_hardfork_accounts { - let beneficiary = ðash_params.dao_hardfork_beneficiary; - state.balance(child) - .and_then(|b| state.transfer_balance(child, beneficiary, &b, CleanupMode::NoEmpty))?; - } - } - } - - Ok(()) - } - - /// Populate a header's fields based on its parent's header. - /// Usually implements the chain scoring rule based on weight. - /// The gas floor target must not be lower than the engine's minimum gas limit. - pub fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, gas_ceil_target: U256) { - header.set_difficulty(parent.difficulty().clone()); - let gas_limit = parent.gas_limit().clone(); - assert!(!gas_limit.is_zero(), "Gas limit should be > 0"); - - if let Some(ref ethash_params) = self.ethash_extensions { - let gas_limit = { - let bound_divisor = self.params().gas_limit_bound_divisor; - let lower_limit = gas_limit - gas_limit / bound_divisor + 1; - let upper_limit = gas_limit + gas_limit / bound_divisor - 1; - let gas_limit = if gas_limit < gas_floor_target { - let gas_limit = cmp::min(gas_floor_target, upper_limit); - round_block_gas_limit(gas_limit, lower_limit, upper_limit) - } else if gas_limit > gas_ceil_target { - let gas_limit = cmp::max(gas_ceil_target, lower_limit); - round_block_gas_limit(gas_limit, lower_limit, upper_limit) - } else { - let total_lower_limit = cmp::max(lower_limit, gas_floor_target); - let total_upper_limit = cmp::min(upper_limit, gas_ceil_target); - let gas_limit = cmp::max(gas_floor_target, cmp::min(total_upper_limit, - lower_limit + (header.gas_used().clone() * 6u32 / 5) / bound_divisor)); - round_block_gas_limit(gas_limit, total_lower_limit, total_upper_limit) - }; - // ensure that we are not violating protocol limits - debug_assert!(gas_limit >= lower_limit); - debug_assert!(gas_limit <= upper_limit); - gas_limit - }; - - header.set_gas_limit(gas_limit); - if header.number() >= ethash_params.dao_hardfork_transition && - header.number() <= ethash_params.dao_hardfork_transition + 9 { - header.set_extra_data(b"dao-hard-fork"[..].to_owned()); - } - return - } - - header.set_gas_limit({ - let bound_divisor = self.params().gas_limit_bound_divisor; - if gas_limit < gas_floor_target { - cmp::min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1) - } else { - cmp::max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1) - } - }); - } - - /// Get the general parameters of the chain. - pub fn params(&self) -> &CommonParams { - &self.params - } - - /// Get the EVM schedule for the given block number. - pub fn schedule(&self, block_number: BlockNumber) -> Schedule { - let mut schedule = match self.ethash_extensions { - None => self.params.schedule(block_number), - Some(ref ext) => { - if block_number < ext.homestead_transition { - Schedule::new_frontier() - } else { - self.params.schedule(block_number) - } - } - }; - - if let Some(ref rules) = self.schedule_rules { - (rules)(&mut schedule, block_number) - } - - schedule - } - - /// Builtin-contracts for the chain.. - pub fn builtins(&self) -> &BTreeMap { - &*self.builtins - } - - /// Attempt to get a handle to a built-in contract. - /// Only returns references to activated built-ins. - // TODO: builtin contract routing - to do this properly, it will require removing the built-in configuration-reading logic - // from Spec into here and removing the Spec::builtins field. - pub fn builtin(&self, a: &Address, block_number: BlockNumber) -> Option<&Builtin> { - self.builtins() - .get(a) - .and_then(|b| if b.is_active(block_number) { Some(b) } else { None }) - } - - /// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`. - pub fn maximum_extra_data_size(&self) -> usize { self.params().maximum_extra_data_size } - - /// The nonce with which accounts begin at given block. - pub fn account_start_nonce(&self, block: u64) -> U256 { - let params = self.params(); - - if block >= params.dust_protection_transition { - U256::from(params.nonce_cap_increment) * U256::from(block) - } else { - params.account_start_nonce - } - } - - /// The network ID that transactions should be signed with. - pub fn signing_chain_id(&self, env_info: &EnvInfo) -> Option { - let params = self.params(); - - if env_info.number >= params.eip155_transition { - Some(params.chain_id) - } else { - None - } - } - - /// Returns new contract address generation scheme at given block number. - pub fn create_address_scheme(&self, _number: BlockNumber) -> CreateContractAddress { - CreateContractAddress::FromSenderAndNonce - } - - /// Verify a particular transaction is valid, regardless of order. - pub fn verify_transaction_unordered(&self, t: UnverifiedTransaction, _header: &Header) -> Result { - Ok(SignedTransaction::new(t)?) - } - - /// Does basic verification of the transaction. - pub fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), transaction::Error> { - let check_low_s = match self.ethash_extensions { - Some(ref ext) => header.number() >= ext.homestead_transition, - None => true, - }; - - let chain_id = if header.number() < self.params().validate_chain_id_transition { - t.chain_id() - } else if header.number() >= self.params().eip155_transition { - Some(self.params().chain_id) - } else { - None - }; - t.verify_basic(check_low_s, chain_id, false)?; - - Ok(()) - } - - /// Does verification of the transaction against the parent state. - pub fn verify_transaction(&self, t: &SignedTransaction, parent: &Header, client: &C) - -> Result<(), transaction::Error> - { - if let Some(ref filter) = self.tx_filter.as_ref() { - if !filter.transaction_allowed(&parent.hash(), parent.number() + 1, t, client) { - return Err(transaction::Error::NotAllowed.into()) - } - } - - Ok(()) - } - - /// Additional params. - pub fn additional_params(&self) -> HashMap { - hash_map![ - "registrar".to_owned() => format!("{:x}", self.params.registrar) - ] - } - - /// Performs pre-validation of RLP decoded transaction before other processing - pub fn decode_transaction(&self, transaction: &[u8]) -> Result { - let rlp = Rlp::new(&transaction); - if rlp.as_raw().len() > self.params().max_transaction_size { - debug!("Rejected oversized transaction of {} bytes", rlp.as_raw().len()); - return Err(transaction::Error::TooBig) - } - rlp.as_val().map_err(|e| transaction::Error::InvalidRlp(e.to_string())) - } + /// Execute a call as the system address. Block environment information passed to the + /// VM is modified to have its gas limit bounded at the upper limit of possible used + /// gases including this system call, capped at the maximum value able to be + /// represented by U256. This system call modifies the block state, but discards other + /// information. If suicides, logs or refunds happen within the system call, they + /// will not be executed or recorded. Gas used by this system call will not be counted + /// on the block. + pub fn execute_as_system( + &self, + block: &mut ExecutedBlock, + contract_address: Address, + gas: U256, + data: Option>, + ) -> Result, Error> { + let (code, code_hash) = { + let state = &block.state; + + ( + state.code(&contract_address)?, + state.code_hash(&contract_address)?, + ) + }; + + self.execute_code_as_system( + block, + Some(contract_address), + code, + code_hash, + None, + gas, + data, + None, + ) + } + + /// Same as execute_as_system, but execute code directly. If contract address is None, use the null sender + /// address. If code is None, then this function has no effect. The call is executed without finalization, and does + /// not form a transaction. + pub fn execute_code_as_system( + &self, + block: &mut ExecutedBlock, + contract_address: Option
, + code: Option>>, + code_hash: Option, + value: Option, + gas: U256, + data: Option>, + call_type: Option, + ) -> Result, Error> { + let env_info = { + let mut env_info = block.env_info(); + env_info.gas_limit = env_info.gas_used.saturating_add(gas); + env_info + }; + + let mut state = block.state_mut(); + + let params = ActionParams { + code_address: contract_address.unwrap_or(UNSIGNED_SENDER), + address: contract_address.unwrap_or(UNSIGNED_SENDER), + sender: SYSTEM_ADDRESS, + origin: SYSTEM_ADDRESS, + gas, + gas_price: 0.into(), + value: value.unwrap_or_else(|| ActionValue::Transfer(0.into())), + code, + code_hash, + data, + call_type: call_type.unwrap_or(CallType::Call), + params_type: ParamsType::Separate, + }; + let schedule = self.schedule(env_info.number); + let mut ex = Executive::new(&mut state, &env_info, self, &schedule); + let mut substate = Substate::new(); + + let res = ex + .call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) + .map_err(|e| ::engines::EngineError::FailedSystemCall(format!("{}", e)))?; + let output = res.return_data.to_vec(); + + Ok(output) + } + + /// Push last known block hash to the state. + fn push_last_hash(&self, block: &mut ExecutedBlock) -> Result<(), Error> { + let params = self.params(); + if block.header.number() == params.eip210_transition { + let state = block.state_mut(); + state.init_code( + ¶ms.eip210_contract_address, + params.eip210_contract_code.clone(), + )?; + } + if block.header.number() >= params.eip210_transition { + let parent_hash = *block.header.parent_hash(); + let _ = self.execute_as_system( + block, + params.eip210_contract_address, + params.eip210_contract_gas, + Some(parent_hash.to_vec()), + )?; + } + Ok(()) + } + + /// Logic to perform on a new block: updating last hashes and the DAO + /// fork, for ethash. + pub fn on_new_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { + self.push_last_hash(block)?; + + if let Some(ref ethash_params) = self.ethash_extensions { + if block.header.number() == ethash_params.dao_hardfork_transition { + let state = block.state_mut(); + for child in ðash_params.dao_hardfork_accounts { + let beneficiary = ðash_params.dao_hardfork_beneficiary; + state.balance(child).and_then(|b| { + state.transfer_balance(child, beneficiary, &b, CleanupMode::NoEmpty) + })?; + } + } + } + + Ok(()) + } + + /// Populate a header's fields based on its parent's header. + /// Usually implements the chain scoring rule based on weight. + /// The gas floor target must not be lower than the engine's minimum gas limit. + pub fn populate_from_parent( + &self, + header: &mut Header, + parent: &Header, + gas_floor_target: U256, + gas_ceil_target: U256, + ) { + header.set_difficulty(parent.difficulty().clone()); + let gas_limit = parent.gas_limit().clone(); + assert!(!gas_limit.is_zero(), "Gas limit should be > 0"); + + if let Some(ref ethash_params) = self.ethash_extensions { + let gas_limit = { + let bound_divisor = self.params().gas_limit_bound_divisor; + let lower_limit = gas_limit - gas_limit / bound_divisor + 1; + let upper_limit = gas_limit + gas_limit / bound_divisor - 1; + let gas_limit = if gas_limit < gas_floor_target { + let gas_limit = cmp::min(gas_floor_target, upper_limit); + round_block_gas_limit(gas_limit, lower_limit, upper_limit) + } else if gas_limit > gas_ceil_target { + let gas_limit = cmp::max(gas_ceil_target, lower_limit); + round_block_gas_limit(gas_limit, lower_limit, upper_limit) + } else { + let total_lower_limit = cmp::max(lower_limit, gas_floor_target); + let total_upper_limit = cmp::min(upper_limit, gas_ceil_target); + let gas_limit = cmp::max( + gas_floor_target, + cmp::min( + total_upper_limit, + lower_limit + (header.gas_used().clone() * 6u32 / 5) / bound_divisor, + ), + ); + round_block_gas_limit(gas_limit, total_lower_limit, total_upper_limit) + }; + // ensure that we are not violating protocol limits + debug_assert!(gas_limit >= lower_limit); + debug_assert!(gas_limit <= upper_limit); + gas_limit + }; + + header.set_gas_limit(gas_limit); + if header.number() >= ethash_params.dao_hardfork_transition + && header.number() <= ethash_params.dao_hardfork_transition + 9 + { + header.set_extra_data(b"dao-hard-fork"[..].to_owned()); + } + return; + } + + header.set_gas_limit({ + let bound_divisor = self.params().gas_limit_bound_divisor; + if gas_limit < gas_floor_target { + cmp::min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1) + } else { + cmp::max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1) + } + }); + } + + /// Get the general parameters of the chain. + pub fn params(&self) -> &CommonParams { + &self.params + } + + /// Get the EVM schedule for the given block number. + pub fn schedule(&self, block_number: BlockNumber) -> Schedule { + let mut schedule = match self.ethash_extensions { + None => self.params.schedule(block_number), + Some(ref ext) => { + if block_number < ext.homestead_transition { + Schedule::new_frontier() + } else { + self.params.schedule(block_number) + } + } + }; + + if let Some(ref rules) = self.schedule_rules { + (rules)(&mut schedule, block_number) + } + + schedule + } + + /// Builtin-contracts for the chain.. + pub fn builtins(&self) -> &BTreeMap { + &*self.builtins + } + + /// Attempt to get a handle to a built-in contract. + /// Only returns references to activated built-ins. + // TODO: builtin contract routing - to do this properly, it will require removing the built-in configuration-reading logic + // from Spec into here and removing the Spec::builtins field. + pub fn builtin(&self, a: &Address, block_number: BlockNumber) -> Option<&Builtin> { + self.builtins().get(a).and_then(|b| { + if b.is_active(block_number) { + Some(b) + } else { + None + } + }) + } + + /// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`. + pub fn maximum_extra_data_size(&self) -> usize { + self.params().maximum_extra_data_size + } + + /// The nonce with which accounts begin at given block. + pub fn account_start_nonce(&self, block: u64) -> U256 { + let params = self.params(); + + if block >= params.dust_protection_transition { + U256::from(params.nonce_cap_increment) * U256::from(block) + } else { + params.account_start_nonce + } + } + + /// The network ID that transactions should be signed with. + pub fn signing_chain_id(&self, env_info: &EnvInfo) -> Option { + let params = self.params(); + + if env_info.number >= params.eip155_transition { + Some(params.chain_id) + } else { + None + } + } + + /// Returns new contract address generation scheme at given block number. + pub fn create_address_scheme(&self, _number: BlockNumber) -> CreateContractAddress { + CreateContractAddress::FromSenderAndNonce + } + + /// Verify a particular transaction is valid, regardless of order. + pub fn verify_transaction_unordered( + &self, + t: UnverifiedTransaction, + _header: &Header, + ) -> Result { + Ok(SignedTransaction::new(t)?) + } + + /// Does basic verification of the transaction. + pub fn verify_transaction_basic( + &self, + t: &UnverifiedTransaction, + header: &Header, + ) -> Result<(), transaction::Error> { + let check_low_s = match self.ethash_extensions { + Some(ref ext) => header.number() >= ext.homestead_transition, + None => true, + }; + + let chain_id = if header.number() < self.params().validate_chain_id_transition { + t.chain_id() + } else if header.number() >= self.params().eip155_transition { + Some(self.params().chain_id) + } else { + None + }; + t.verify_basic(check_low_s, chain_id)?; + + Ok(()) + } + + /// Does verification of the transaction against the parent state. + pub fn verify_transaction( + &self, + t: &SignedTransaction, + parent: &Header, + client: &C, + ) -> Result<(), transaction::Error> { + if let Some(ref filter) = self.tx_filter.as_ref() { + if !filter.transaction_allowed(&parent.hash(), parent.number() + 1, t, client) { + return Err(transaction::Error::NotAllowed.into()); + } + } + + Ok(()) + } + + /// Additional params. + pub fn additional_params(&self) -> HashMap { + hash_map![ + "registrar".to_owned() => format!("{:x}", self.params.registrar) + ] + } + + /// Performs pre-validation of RLP decoded transaction before other processing + pub fn decode_transaction( + &self, + transaction: &[u8], + ) -> Result { + let rlp = Rlp::new(&transaction); + if rlp.as_raw().len() > self.params().max_transaction_size { + debug!( + "Rejected oversized transaction of {} bytes", + rlp.as_raw().len() + ); + return Err(transaction::Error::TooBig); + } + rlp.as_val() + .map_err(|e| transaction::Error::InvalidRlp(e.to_string())) + } } /// Auxiliary data fetcher for an Ethereum machine. In Ethereum-like machines /// there are two kinds of auxiliary data: bodies and receipts. #[derive(Default, Clone)] pub struct AuxiliaryData<'a> { - /// The full block bytes, including the header. - pub bytes: Option<&'a [u8]>, - /// The block receipts. - pub receipts: Option<&'a [::types::receipt::Receipt]>, + /// The full block bytes, including the header. + pub bytes: Option<&'a [u8]>, + /// The block receipts. + pub receipts: Option<&'a [::types::receipt::Receipt]>, } /// Type alias for a function we can make calls through synchronously. /// Returns the call result and state proof for each call. -pub type Call<'a> = Fn(Address, Vec) -> Result<(Vec, Vec>), String> + 'a; +pub type Call<'a> = dyn Fn(Address, Vec) -> Result<(Vec, Vec>), String> + 'a; /// Request for auxiliary data of a block. #[derive(Debug, Clone, Copy, PartialEq)] pub enum AuxiliaryRequest { - /// Needs the body. - Body, - /// Needs the receipts. - Receipts, - /// Needs both body and receipts. - Both, + /// Needs the body. + Body, + /// Needs the receipts. + Receipts, + /// Needs both body and receipts. + Both, } impl super::Machine for EthereumMachine { - type EngineClient = ::client::EngineClient; - - type Error = Error; - - fn balance(&self, live: &ExecutedBlock, address: &Address) -> Result { - live.state.balance(address).map_err(Into::into) - } - - fn add_balance(&self, live: &mut ExecutedBlock, address: &Address, amount: &U256) -> Result<(), Error> { - live.state_mut().add_balance(address, amount, CleanupMode::NoEmpty).map_err(Into::into) - } + type EngineClient = dyn crate::client::EngineClient; + + type Error = Error; + + fn balance(&self, live: &ExecutedBlock, address: &Address) -> Result { + live.state.balance(address).map_err(Into::into) + } + + fn add_balance( + &self, + live: &mut ExecutedBlock, + address: &Address, + amount: &U256, + ) -> Result<(), Error> { + live.state_mut() + .add_balance(address, amount, CleanupMode::NoEmpty) + .map_err(Into::into) + } } // Try to round gas_limit a bit so that: // 1) it will still be in desired range -// 2) it will be a nearest (with tendency to increase) multiple of PARITY_GAS_LIMIT_DETERMINANT +// 2) it will be a nearest (with tendency to increase) multiple of GAS_LIMIT_DETERMINANT fn round_block_gas_limit(gas_limit: U256, lower_limit: U256, upper_limit: U256) -> U256 { - let increased_gas_limit = gas_limit + (PARITY_GAS_LIMIT_DETERMINANT - gas_limit % PARITY_GAS_LIMIT_DETERMINANT); - if increased_gas_limit > upper_limit { - let decreased_gas_limit = increased_gas_limit - PARITY_GAS_LIMIT_DETERMINANT; - if decreased_gas_limit < lower_limit { - gas_limit - } else { - decreased_gas_limit - } - } else { - increased_gas_limit - } + let increased_gas_limit = + gas_limit + (GAS_LIMIT_DETERMINANT - gas_limit % GAS_LIMIT_DETERMINANT); + if increased_gas_limit > upper_limit { + let decreased_gas_limit = increased_gas_limit - GAS_LIMIT_DETERMINANT; + if decreased_gas_limit < lower_limit { + gas_limit + } else { + decreased_gas_limit + } + } else { + increased_gas_limit + } } #[cfg(test)] mod tests { - use super::*; - - fn get_default_ethash_extensions() -> EthashExtensions { - EthashExtensions { - homestead_transition: 1150000, - dao_hardfork_transition: u64::max_value(), - dao_hardfork_beneficiary: "0000000000000000000000000000000000000001".into(), - dao_hardfork_accounts: Vec::new(), - } - } - - #[test] - fn should_disallow_unsigned_transactions() { - let rlp = "ea80843b9aca0083015f90948921ebb5f79e9e3920abe571004d0b1d5119c154865af3107a400080038080".into(); - let transaction: UnverifiedTransaction = ::rlp::decode(&::rustc_hex::FromHex::from_hex(rlp).unwrap()).unwrap(); - let spec = ::ethereum::new_ropsten_test(); - let ethparams = get_default_ethash_extensions(); - - let machine = EthereumMachine::with_ethash_extensions( - spec.params().clone(), - Default::default(), - ethparams, - ); - let mut header = ::types::header::Header::new(); - header.set_number(15); - - let res = machine.verify_transaction_basic(&transaction, &header); - assert_eq!(res, Err(transaction::Error::InvalidSignature("Crypto error (Invalid EC signature)".into()))); - } - - #[test] - fn ethash_gas_limit_is_multiple_of_determinant() { - use ethereum_types::U256; - - let spec = ::ethereum::new_homestead_test(); - let ethparams = get_default_ethash_extensions(); - - let machine = EthereumMachine::with_ethash_extensions( - spec.params().clone(), - Default::default(), - ethparams, - ); - - let mut parent = ::types::header::Header::new(); - let mut header = ::types::header::Header::new(); - header.set_number(1); - - // this test will work for this constant only - assert_eq!(PARITY_GAS_LIMIT_DETERMINANT, U256::from(37)); - - // when parent.gas_limit < gas_floor_target: - parent.set_gas_limit(U256::from(50_000)); - machine.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(200_000)); - assert_eq!(*header.gas_limit(), U256::from(50_024)); - - // when parent.gas_limit > gas_ceil_target: - parent.set_gas_limit(U256::from(250_000)); - machine.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(200_000)); - assert_eq!(*header.gas_limit(), U256::from(249_787)); - - // when parent.gas_limit is in miner's range - header.set_gas_used(U256::from(150_000)); - parent.set_gas_limit(U256::from(150_000)); - machine.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(200_000)); - assert_eq!(*header.gas_limit(), U256::from(150_035)); - - // when parent.gas_limit is in miner's range - // && we can NOT increase it to be multiple of constant - header.set_gas_used(U256::from(150_000)); - parent.set_gas_limit(U256::from(150_000)); - machine.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(150_002)); - assert_eq!(*header.gas_limit(), U256::from(149_998)); - - // when parent.gas_limit is in miner's range - // && we can NOT increase it to be multiple of constant - // && we can NOT decrease it to be multiple of constant - header.set_gas_used(U256::from(150_000)); - parent.set_gas_limit(U256::from(150_000)); - machine.populate_from_parent(&mut header, &parent, U256::from(150_000), U256::from(150_002)); - assert_eq!(*header.gas_limit(), U256::from(150_002)); - } + use super::*; + + fn get_default_ethash_extensions() -> EthashExtensions { + EthashExtensions { + homestead_transition: 1150000, + dao_hardfork_transition: u64::max_value(), + dao_hardfork_beneficiary: "0000000000000000000000000000000000000001".into(), + dao_hardfork_accounts: Vec::new(), + } + } + + #[test] + fn should_disallow_unsigned_transactions() { + let rlp = "ea80843b9aca0083015f90948921ebb5f79e9e3920abe571004d0b1d5119c154865af3107a400080038080"; + let transaction: UnverifiedTransaction = + ::rlp::decode(&::rustc_hex::FromHex::from_hex(rlp).unwrap()).unwrap(); + let spec = ::ethereum::new_ropsten_test(); + let ethparams = get_default_ethash_extensions(); + + let machine = EthereumMachine::with_ethash_extensions( + spec.params().clone(), + Default::default(), + ethparams, + ); + let mut header = ::types::header::Header::new(); + header.set_number(15); + + let res = machine.verify_transaction_basic(&transaction, &header); + assert_eq!( + res, + Err(transaction::Error::InvalidSignature( + "Crypto error (Invalid EC signature)".into() + )) + ); + } + + #[test] + fn ethash_gas_limit_is_multiple_of_determinant() { + use ethereum_types::U256; + + let spec = ::ethereum::new_homestead_test(); + let ethparams = get_default_ethash_extensions(); + + let machine = EthereumMachine::with_ethash_extensions( + spec.params().clone(), + Default::default(), + ethparams, + ); + + let mut parent = ::types::header::Header::new(); + let mut header = ::types::header::Header::new(); + header.set_number(1); + + // this test will work for this constant only + assert_eq!(GAS_LIMIT_DETERMINANT, U256::from(37)); + + // when parent.gas_limit < gas_floor_target: + parent.set_gas_limit(U256::from(50_000)); + machine.populate_from_parent( + &mut header, + &parent, + U256::from(100_000), + U256::from(200_000), + ); + assert_eq!(*header.gas_limit(), U256::from(50_024)); + + // when parent.gas_limit > gas_ceil_target: + parent.set_gas_limit(U256::from(250_000)); + machine.populate_from_parent( + &mut header, + &parent, + U256::from(100_000), + U256::from(200_000), + ); + assert_eq!(*header.gas_limit(), U256::from(249_787)); + + // when parent.gas_limit is in miner's range + header.set_gas_used(U256::from(150_000)); + parent.set_gas_limit(U256::from(150_000)); + machine.populate_from_parent( + &mut header, + &parent, + U256::from(100_000), + U256::from(200_000), + ); + assert_eq!(*header.gas_limit(), U256::from(150_035)); + + // when parent.gas_limit is in miner's range + // && we can NOT increase it to be multiple of constant + header.set_gas_used(U256::from(150_000)); + parent.set_gas_limit(U256::from(150_000)); + machine.populate_from_parent( + &mut header, + &parent, + U256::from(100_000), + U256::from(150_002), + ); + assert_eq!(*header.gas_limit(), U256::from(149_998)); + + // when parent.gas_limit is in miner's range + // && we can NOT increase it to be multiple of constant + // && we can NOT decrease it to be multiple of constant + header.set_gas_used(U256::from(150_000)); + parent.set_gas_limit(U256::from(150_000)); + machine.populate_from_parent( + &mut header, + &parent, + U256::from(150_000), + U256::from(150_002), + ); + assert_eq!(*header.gas_limit(), U256::from(150_002)); + } } diff --git a/ethcore/src/machine/mod.rs b/ethcore/src/machine/mod.rs index 882dc011a20..e702294ee4f 100644 --- a/ethcore/src/machine/mod.rs +++ b/ethcore/src/machine/mod.rs @@ -3,5 +3,4 @@ mod impls; mod traits; -pub use self::impls::*; -pub use self::traits::*; +pub use self::{impls::*, traits::*}; diff --git a/ethcore/src/machine/traits.rs b/ethcore/src/machine/traits.rs index 1523885e0e9..3f5f22b2ee6 100644 --- a/ethcore/src/machine/traits.rs +++ b/ethcore/src/machine/traits.rs @@ -1,37 +1,42 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Generalization of a state machine for a consensus engine. //! This will define traits for the header, block, and state of a blockchain. -use ethereum_types::{U256, Address}; use block::ExecutedBlock; +use ethereum_types::{Address, U256}; /// Generalization of types surrounding blockchain-suitable state machines. pub trait Machine: Send + Sync { - /// A handle to a blockchain client for this machine. - type EngineClient: ?Sized; - - /// Errors which can occur when querying or interacting with the machine. - type Error; - - /// Get the balance, in base units, associated with an account. - /// Extracts data from the live block. - fn balance(&self, live: &ExecutedBlock, address: &Address) -> Result; - - /// Increment the balance of an account in the state of the live block. - fn add_balance(&self, live: &mut ExecutedBlock, address: &Address, amount: &U256) -> Result<(), Self::Error>; + /// A handle to a blockchain client for this machine. + type EngineClient: ?Sized; + + /// Errors which can occur when querying or interacting with the machine. + type Error; + + /// Get the balance, in base units, associated with an account. + /// Extracts data from the live block. + fn balance(&self, live: &ExecutedBlock, address: &Address) -> Result; + + /// Increment the balance of an account in the state of the live block. + fn add_balance( + &self, + live: &mut ExecutedBlock, + address: &Address, + amount: &U256, + ) -> Result<(), Self::Error>; } diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index d9d4570a72a..96141f1d5bc 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -1,58 +1,62 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::cmp; -use std::time::{Instant, Duration}; -use std::collections::{BTreeMap, BTreeSet, HashSet}; -use std::sync::Arc; +use std::{ + cmp, + collections::{BTreeMap, BTreeSet, HashSet}, + sync::Arc, + time::{Duration, Instant}, +}; use ansi_term::Colour; use bytes::Bytes; use call_contract::CallContract; -use ethcore_miner::gas_pricer::GasPricer; -use ethcore_miner::local_accounts::LocalAccounts; -use ethcore_miner::pool::{self, TransactionQueue, VerifiedTransaction, QueueStatus, PrioritizationStrategy}; -use ethcore_miner::service_transaction_checker::ServiceTransactionChecker; #[cfg(feature = "work-notify")] use ethcore_miner::work_notify::NotifyWork; -use ethereum_types::{H256, U256, Address}; +use ethcore_miner::{ + gas_pricer::GasPricer, + local_accounts::LocalAccounts, + pool::{self, PrioritizationStrategy, QueueStatus, TransactionQueue, VerifiedTransaction}, + service_transaction_checker::ServiceTransactionChecker, +}; +use ethereum_types::{Address, H256, U256}; use io::IoChannel; -use miner::pool_client::{PoolClient, CachedNonceClient, NonceCache}; -use miner; +use miner::{ + self, + pool_client::{CachedNonceClient, NonceCache, PoolClient}, + MinerService, +}; use parking_lot::{Mutex, RwLock}; use rayon::prelude::*; -use types::transaction::{ - self, - Action, - UnverifiedTransaction, - SignedTransaction, - PendingTransaction, +use types::{ + block::Block, + header::Header, + receipt::RichReceipt, + transaction::{self, Action, PendingTransaction, SignedTransaction, UnverifiedTransaction}, + BlockNumber, }; -use types::BlockNumber; -use types::block::Block; -use types::header::Header; -use types::receipt::RichReceipt; -use using_queue::{UsingQueue, GetAction}; +use using_queue::{GetAction, UsingQueue}; use block::{ClosedBlock, SealedBlock}; use client::{ - BlockChain, ChainInfo, BlockProducer, SealedBlockImporter, Nonce, TransactionInfo, TransactionId + traits::{EngineClient, ForceUpdateSealing}, + BlockChain, BlockId, BlockProducer, ChainInfo, ClientIoMessage, Nonce, SealedBlockImporter, + TransactionId, TransactionInfo, }; -use client::{BlockId, ClientIoMessage}; -use engines::{EthEngine, Seal, EngineSigner}; +use engines::{EngineSigner, EthEngine, Seal}; use error::{Error, ErrorKind}; use executed::ExecutionError; use executive::contract_address; @@ -62,13 +66,13 @@ use state::State; /// Different possible definitions for pending transaction set. #[derive(Debug, PartialEq)] pub enum PendingSet { - /// Always just the transactions in the queue. These have had only cheap checks. - AlwaysQueue, - /// Always just the transactions in the sealing block. These have had full checks but - /// may be empty if the node is not actively mining or has no force_sealing enabled. - AlwaysSealing, - /// Takes from sealing if mining, from queue otherwise. - SealingOrElseQueue, + /// Always just the transactions in the queue. These have had only cheap checks. + AlwaysQueue, + /// Always just the transactions in the sealing block. These have had full checks but + /// may be empty if the node is not actively mining or has no force_sealing enabled. + AlwaysSealing, + /// Takes from sealing if mining, from queue otherwise. + SealingOrElseQueue, } /// Transaction queue penalization settings. @@ -77,24 +81,24 @@ pub enum PendingSet { /// will get lower priority. #[derive(Debug, PartialEq, Clone)] pub enum Penalization { - /// Penalization in transaction queue is disabled - Disabled, - /// Penalization in transaction queue is enabled - Enabled { - /// Upper limit of transaction processing time before penalizing. - offend_threshold: Duration, - }, + /// Penalization in transaction queue is disabled + Disabled, + /// Penalization in transaction queue is enabled + Enabled { + /// Upper limit of transaction processing time before penalizing. + offend_threshold: Duration, + }, } /// Pending block preparation status. #[derive(Debug, PartialEq)] pub enum BlockPreparationStatus { - /// We had to prepare new pending block and the preparation succeeded. - Succeeded, - /// We had to prepare new pending block but the preparation failed. - Failed, - /// We didn't have to prepare a new block. - NotPrepared, + /// We had to prepare new pending block and the preparation succeeded. + Succeeded, + /// We had to prepare new pending block but the preparation failed. + Failed, + /// We didn't have to prepare a new block. + NotPrepared, } /// Initial minimal gas price. @@ -117,1142 +121,1256 @@ const MAX_SKIPPED_TRANSACTIONS: usize = 128; /// Configures the behaviour of the miner. #[derive(Debug, PartialEq)] pub struct MinerOptions { - /// Force the miner to reseal, even when nobody has asked for work. - pub force_sealing: bool, - /// Reseal on receipt of new external transactions. - pub reseal_on_external_tx: bool, - /// Reseal on receipt of new local transactions. - pub reseal_on_own_tx: bool, - /// Reseal when new uncle block has been imported. - pub reseal_on_uncle: bool, - /// Minimum period between transaction-inspired reseals. - pub reseal_min_period: Duration, - /// Maximum period between blocks (enables force sealing after that). - pub reseal_max_period: Duration, - /// Whether we should fallback to providing all the queue's transactions or just pending. - pub pending_set: PendingSet, - /// How many historical work packages can we store before running out? - pub work_queue_size: usize, - /// Can we submit two different solutions for the same block and expect both to result in an import? - pub enable_resubmission: bool, - /// Create a pending block with maximal possible gas limit. - /// NOTE: Such block will contain all pending transactions but - /// will be invalid if mined. - pub infinite_pending_block: bool, - - /// Strategy to use for prioritizing transactions in the queue. - pub tx_queue_strategy: PrioritizationStrategy, - /// Simple senders penalization. - pub tx_queue_penalization: Penalization, - /// Do we want to mark transactions recieved locally (e.g. RPC) as local if we don't have the sending account? - pub tx_queue_no_unfamiliar_locals: bool, - /// Do we refuse to accept service transactions even if sender is certified. - pub refuse_service_transactions: bool, - /// Transaction pool limits. - pub pool_limits: pool::Options, - /// Initial transaction verification options. - pub pool_verification_options: pool::verifier::Options, + /// Force the miner to reseal, even when nobody has asked for work. + pub force_sealing: bool, + /// Reseal on receipt of new external transactions. + pub reseal_on_external_tx: bool, + /// Reseal on receipt of new local transactions. + pub reseal_on_own_tx: bool, + /// Reseal when new uncle block has been imported. + pub reseal_on_uncle: bool, + /// Minimum period between transaction-inspired reseals. + pub reseal_min_period: Duration, + /// Maximum period between blocks (enables force sealing after that). + pub reseal_max_period: Duration, + /// Whether we should fallback to providing all the queue's transactions or just pending. + pub pending_set: PendingSet, + /// How many historical work packages can we store before running out? + pub work_queue_size: usize, + /// Can we submit two different solutions for the same block and expect both to result in an import? + pub enable_resubmission: bool, + /// Create a pending block with maximal possible gas limit. + /// NOTE: Such block will contain all pending transactions but + /// will be invalid if mined. + pub infinite_pending_block: bool, + + /// Strategy to use for prioritizing transactions in the queue. + pub tx_queue_strategy: PrioritizationStrategy, + /// Simple senders penalization. + pub tx_queue_penalization: Penalization, + /// Do we want to mark transactions recieved locally (e.g. RPC) as local if we don't have the sending account? + pub tx_queue_no_unfamiliar_locals: bool, + /// Do we refuse to accept service transactions even if sender is certified. + pub refuse_service_transactions: bool, + /// Transaction pool limits. + pub pool_limits: pool::Options, + /// Initial transaction verification options. + pub pool_verification_options: pool::verifier::Options, } impl Default for MinerOptions { - fn default() -> Self { - MinerOptions { - force_sealing: false, - reseal_on_external_tx: false, - reseal_on_own_tx: true, - reseal_on_uncle: false, - reseal_min_period: Duration::from_secs(2), - reseal_max_period: Duration::from_secs(120), - pending_set: PendingSet::AlwaysQueue, - work_queue_size: 20, - enable_resubmission: true, - infinite_pending_block: false, - tx_queue_strategy: PrioritizationStrategy::GasPriceOnly, - tx_queue_penalization: Penalization::Disabled, - tx_queue_no_unfamiliar_locals: false, - refuse_service_transactions: false, - pool_limits: pool::Options { - max_count: 8_192, - max_per_sender: 81, - max_mem_usage: 4 * 1024 * 1024, - }, - pool_verification_options: pool::verifier::Options { - minimal_gas_price: DEFAULT_MINIMAL_GAS_PRICE.into(), - block_gas_limit: U256::max_value(), - tx_gas_limit: U256::max_value(), - no_early_reject: false, - }, - } - } + fn default() -> Self { + MinerOptions { + force_sealing: false, + reseal_on_external_tx: false, + reseal_on_own_tx: true, + reseal_on_uncle: false, + reseal_min_period: Duration::from_secs(2), + reseal_max_period: Duration::from_secs(120), + pending_set: PendingSet::AlwaysQueue, + work_queue_size: 20, + enable_resubmission: true, + infinite_pending_block: false, + tx_queue_strategy: PrioritizationStrategy::GasPriceOnly, + tx_queue_penalization: Penalization::Disabled, + tx_queue_no_unfamiliar_locals: false, + refuse_service_transactions: false, + pool_limits: pool::Options { + max_count: 8_192, + max_per_sender: 81, + max_mem_usage: 4 * 1024 * 1024, + }, + pool_verification_options: pool::verifier::Options { + minimal_gas_price: DEFAULT_MINIMAL_GAS_PRICE.into(), + block_gas_limit: U256::max_value(), + tx_gas_limit: U256::max_value(), + no_early_reject: false, + }, + } + } } /// Configurable parameters of block authoring. #[derive(Debug, Default, Clone)] pub struct AuthoringParams { - /// Lower and upper bound of block gas limit that we are targeting - pub gas_range_target: (U256, U256), - /// Block author - pub author: Address, - /// Block extra data - pub extra_data: Bytes, + /// Lower and upper bound of block gas limit that we are targeting + pub gas_range_target: (U256, U256), + /// Block author + pub author: Address, + /// Block extra data + pub extra_data: Bytes, } /// Block sealing mechanism pub enum Author { - /// Sealing block is external and we only need a reward beneficiary (i.e. PoW) - External(Address), - /// Sealing is done internally, we need a way to create signatures to seal block (i.e. PoA) - Sealer(Box), + /// Sealing block is external and we only need a reward beneficiary (i.e. PoW) + External(Address), + /// Sealing is done internally, we need a way to create signatures to seal block (i.e. PoA) + Sealer(Box), } impl Author { - /// Get author's address. - pub fn address(&self) -> Address { - match *self { - Author::External(address) => address, - Author::Sealer(ref sealer) => sealer.address(), - } - } + /// Get author's address. + pub fn address(&self) -> Address { + match *self { + Author::External(address) => address, + Author::Sealer(ref sealer) => sealer.address(), + } + } } struct SealingWork { - queue: UsingQueue, - enabled: bool, - next_allowed_reseal: Instant, - next_mandatory_reseal: Instant, - // block number when sealing work was last requested - last_request: Option, + queue: UsingQueue, + enabled: bool, + next_allowed_reseal: Instant, + next_mandatory_reseal: Instant, + // block number when sealing work was last requested + last_request: Option, } impl SealingWork { - /// Are we allowed to do a non-mandatory reseal? - fn reseal_allowed(&self) -> bool { - Instant::now() > self.next_allowed_reseal - } + /// Are we allowed to do a non-mandatory reseal? + fn reseal_allowed(&self) -> bool { + Instant::now() > self.next_allowed_reseal + } } /// Keeps track of transactions using priority queue and holds currently mined block. /// Handles preparing work for "work sealing" or seals "internally" if Engine does not require work. pub struct Miner { - // NOTE [ToDr] When locking always lock in this order! - sealing: Mutex, - params: RwLock, - #[cfg(feature = "work-notify")] - listeners: RwLock>>, - nonce_cache: NonceCache, - gas_pricer: Mutex, - options: MinerOptions, - // TODO [ToDr] Arc is only required because of price updater - transaction_queue: Arc, - engine: Arc, - accounts: Arc, - io_channel: RwLock>>, - service_transaction_checker: Option, + // NOTE [ToDr] When locking always lock in this order! + sealing: Mutex, + params: RwLock, + #[cfg(feature = "work-notify")] + listeners: RwLock>>, + nonce_cache: NonceCache, + gas_pricer: Mutex, + options: MinerOptions, + // TODO [ToDr] Arc is only required because of price updater + transaction_queue: Arc, + engine: Arc, + accounts: Arc, + io_channel: RwLock>>, + service_transaction_checker: Option, } impl Miner { - /// Push listener that will handle new jobs - #[cfg(feature = "work-notify")] - pub fn add_work_listener(&self, notifier: Box) { - self.listeners.write().push(notifier); - self.sealing.lock().enabled = true; - } - - /// Set a callback to be notified about imported transactions' hashes. - pub fn add_transactions_listener(&self, f: Box) { - self.transaction_queue.add_listener(f); - } - - /// Creates new instance of miner Arc. - pub fn new( - options: MinerOptions, - gas_pricer: GasPricer, - spec: &Spec, - accounts: A, - ) -> Self { - let limits = options.pool_limits.clone(); - let verifier_options = options.pool_verification_options.clone(); - let tx_queue_strategy = options.tx_queue_strategy; - let nonce_cache_size = cmp::max(4096, limits.max_count / 4); - let refuse_service_transactions = options.refuse_service_transactions; - - Miner { - sealing: Mutex::new(SealingWork { - queue: UsingQueue::new(options.work_queue_size), - enabled: options.force_sealing - || spec.engine.seals_internally().is_some(), - next_allowed_reseal: Instant::now(), - next_mandatory_reseal: Instant::now() + options.reseal_max_period, - last_request: None, - }), - params: RwLock::new(AuthoringParams::default()), - #[cfg(feature = "work-notify")] - listeners: RwLock::new(vec![]), - gas_pricer: Mutex::new(gas_pricer), - nonce_cache: NonceCache::new(nonce_cache_size), - options, - transaction_queue: Arc::new(TransactionQueue::new(limits, verifier_options, tx_queue_strategy)), - accounts: Arc::new(accounts), - engine: spec.engine.clone(), - io_channel: RwLock::new(None), - service_transaction_checker: if refuse_service_transactions { - None - } else { - Some(ServiceTransactionChecker::default()) - }, - } - } - - /// Creates new instance of miner with given spec and accounts. - /// - /// NOTE This should be only used for tests. - pub fn new_for_tests(spec: &Spec, accounts: Option>) -> Miner { - let minimal_gas_price = 0.into(); - Miner::new(MinerOptions { - pool_verification_options: pool::verifier::Options { - minimal_gas_price, - block_gas_limit: U256::max_value(), - tx_gas_limit: U256::max_value(), - no_early_reject: false, - }, - reseal_min_period: Duration::from_secs(0), - ..Default::default() - }, GasPricer::new_fixed(minimal_gas_price), spec, accounts.unwrap_or_default()) - } - - /// Sets `IoChannel` - pub fn set_io_channel(&self, io_channel: IoChannel) { - *self.io_channel.write() = Some(io_channel); - } - - /// Sets in-blockchain checker for transactions. - pub fn set_in_chain_checker(&self, chain: &Arc) where - C: TransactionInfo + Send + Sync + 'static, - { - let client = Arc::downgrade(chain); - self.transaction_queue.set_in_chain_checker(move |hash| { - match client.upgrade() { - Some(info) => info.transaction_block(TransactionId::Hash(*hash)).is_some(), - None => false, - } - }); - } - - /// Clear all pending block states - pub fn clear(&self) { - self.sealing.lock().queue.reset(); - } - - /// Updates transaction queue verification limits. - /// - /// Limits consist of current block gas limit and minimal gas price. - pub fn update_transaction_queue_limits(&self, block_gas_limit: U256) { - trace!(target: "miner", "minimal_gas_price: recalibrating..."); - let txq = self.transaction_queue.clone(); - let mut options = self.options.pool_verification_options.clone(); - self.gas_pricer.lock().recalibrate(move |gas_price| { - debug!(target: "miner", "minimal_gas_price: Got gas price! {}", gas_price); - options.minimal_gas_price = gas_price; - options.block_gas_limit = block_gas_limit; - txq.set_verifier_options(options); - }); - } - - /// Returns ServiceTransactionChecker - pub fn service_transaction_checker(&self) -> Option { - self.service_transaction_checker.clone() - } - - /// Retrieves an existing pending block iff it's not older than given block number. - /// - /// NOTE: This will not prepare a new pending block if it's not existing. - fn map_existing_pending_block(&self, f: F, latest_block_number: BlockNumber) -> Option where - F: FnOnce(&ClosedBlock) -> T, - { - self.sealing.lock().queue - .peek_last_ref() - .and_then(|b| { - // to prevent a data race between block import and updating pending block - // we allow the number to be equal. - if b.header.number() >= latest_block_number { - Some(f(b)) - } else { - None - } - }) - } - - fn pool_client<'a, C: 'a>(&'a self, chain: &'a C) -> PoolClient<'a, C> where - C: BlockChain + CallContract, - { - PoolClient::new( - chain, - &self.nonce_cache, - &*self.engine, - &*self.accounts, - self.service_transaction_checker.as_ref(), - ) - } - - /// Prepares new block for sealing including top transactions from queue. - fn prepare_block(&self, chain: &C) -> Option<(ClosedBlock, Option)> where - C: BlockChain + CallContract + BlockProducer + Nonce + Sync, - { - trace_time!("prepare_block"); - let chain_info = chain.chain_info(); - - // Open block - let (mut open_block, original_work_hash) = { - let mut sealing = self.sealing.lock(); - let last_work_hash = sealing.queue.peek_last_ref().map(|pb| pb.header.hash()); - let best_hash = chain_info.best_block_hash; - - // check to see if last ClosedBlock in would_seals is actually same parent block. - // if so - // duplicate, re-open and push any new transactions. - // if at least one was pushed successfully, close and enqueue new ClosedBlock; - // otherwise, leave everything alone. - // otherwise, author a fresh block. - let mut open_block = match sealing.queue.get_pending_if(|b| b.header.parent_hash() == &best_hash) { - Some(old_block) => { - trace!(target: "miner", "prepare_block: Already have previous work; updating and returning"); - // add transactions to old_block - chain.reopen_block(old_block) - } - None => { - // block not found - create it. - trace!(target: "miner", "prepare_block: No existing work - making new block"); - let params = self.params.read().clone(); - - match chain.prepare_open_block( - params.author, - params.gas_range_target, - params.extra_data, - ) { - Ok(block) => block, - Err(err) => { - warn!(target: "miner", "Open new block failed with error {:?}. This is likely an error in chain specificiations or on-chain consensus smart contracts.", err); - return None; - } - } - } - }; - - if self.options.infinite_pending_block { - open_block.remove_gas_limit(); - } - - (open_block, last_work_hash) - }; - - let mut invalid_transactions = HashSet::new(); - let mut not_allowed_transactions = HashSet::new(); - let mut senders_to_penalize = HashSet::new(); - let block_number = open_block.header.number(); - - let mut tx_count = 0usize; - let mut skipped_transactions = 0usize; - - let client = self.pool_client(chain); - let engine_params = self.engine.params(); - let min_tx_gas: U256 = self.engine.schedule(chain_info.best_block_number).tx_gas.into(); - let nonce_cap: Option = if chain_info.best_block_number + 1 >= engine_params.dust_protection_transition { - Some((engine_params.nonce_cap_increment * (chain_info.best_block_number + 1)).into()) - } else { - None - }; - // we will never need more transactions than limit divided by min gas - let max_transactions = if min_tx_gas.is_zero() { - usize::max_value() - } else { - MAX_SKIPPED_TRANSACTIONS.saturating_add(cmp::min(*open_block.header.gas_limit() / min_tx_gas, u64::max_value().into()).as_u64() as usize) - }; - - let pending: Vec> = self.transaction_queue.pending( - client.clone(), - pool::PendingSettings { - block_number: chain_info.best_block_number, - current_timestamp: chain_info.best_block_timestamp, - nonce_cap, - max_len: max_transactions, - ordering: miner::PendingOrdering::Priority, - } - ); - - let took_ms = |elapsed: &Duration| { - elapsed.as_secs() * 1000 + elapsed.subsec_nanos() as u64 / 1_000_000 - }; - - let block_start = Instant::now(); - debug!(target: "miner", "Attempting to push {} transactions.", pending.len()); - - for tx in pending { - let start = Instant::now(); - - let transaction = tx.signed().clone(); - let hash = transaction.hash(); - let sender = transaction.sender(); - - // Re-verify transaction again vs current state. - let result = client.verify_signed(&transaction) - .map_err(|e| e.into()) - .and_then(|_| { - open_block.push_transaction(transaction, None) - }); - - let took = start.elapsed(); - - // Check for heavy transactions - match self.options.tx_queue_penalization { - Penalization::Enabled { ref offend_threshold } if &took > offend_threshold => { - senders_to_penalize.insert(sender); - debug!(target: "miner", "Detected heavy transaction ({} ms). Penalizing sender.", took_ms(&took)); - }, - _ => {}, - } - - debug!(target: "miner", "Adding tx {:?} took {} ms", hash, took_ms(&took)); - match result { - Err(Error(ErrorKind::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, gas }), _)) => { - debug!(target: "miner", "Skipping adding transaction to block because of gas limit: {:?} (limit: {:?}, used: {:?}, gas: {:?})", hash, gas_limit, gas_used, gas); - - // Penalize transaction if it's above current gas limit - if gas > gas_limit { - debug!(target: "txqueue", "[{:?}] Transaction above block gas limit.", hash); - invalid_transactions.insert(hash); - } - - // Exit early if gas left is smaller then min_tx_gas - let gas_left = gas_limit - gas_used; - if gas_left < min_tx_gas { - debug!(target: "miner", "Remaining gas is lower than minimal gas for a transaction. Block is full."); - break; - } - - // Avoid iterating over the entire queue in case block is almost full. - skipped_transactions += 1; - if skipped_transactions > MAX_SKIPPED_TRANSACTIONS { - debug!(target: "miner", "Reached skipped transactions threshold. Assuming block is full."); - break; - } - }, - // Invalid nonce error can happen only if previous transaction is skipped because of gas limit. - // If there is errornous state of transaction queue it will be fixed when next block is imported. - Err(Error(ErrorKind::Execution(ExecutionError::InvalidNonce { expected, got }), _)) => { - debug!(target: "miner", "Skipping adding transaction to block because of invalid nonce: {:?} (expected: {:?}, got: {:?})", hash, expected, got); - }, - // already have transaction - ignore - Err(Error(ErrorKind::Transaction(transaction::Error::AlreadyImported), _)) => {}, - Err(Error(ErrorKind::Transaction(transaction::Error::NotAllowed), _)) => { - not_allowed_transactions.insert(hash); - debug!(target: "miner", "Skipping non-allowed transaction for sender {:?}", hash); - }, - Err(e) => { - debug!(target: "txqueue", "[{:?}] Marking as invalid: {:?}.", hash, e); - debug!( - target: "miner", "Error adding transaction to block: number={}. transaction_hash={:?}, Error: {:?}", block_number, hash, e - ); - invalid_transactions.insert(hash); - }, - // imported ok - _ => tx_count += 1, - } - } - let elapsed = block_start.elapsed(); - debug!(target: "miner", "Pushed {} transactions in {} ms", tx_count, took_ms(&elapsed)); - - let block = match open_block.close() { - Ok(block) => block, - Err(err) => { - warn!(target: "miner", "Closing the block failed with error {:?}. This is likely an error in chain specificiations or on-chain consensus smart contracts.", err); - return None; - } - }; - - { - self.transaction_queue.remove(invalid_transactions.iter(), true); - self.transaction_queue.remove(not_allowed_transactions.iter(), false); - self.transaction_queue.penalize(senders_to_penalize.iter()); - } - - Some((block, original_work_hash)) - } - - /// Returns `true` if we should create pending block even if some other conditions are not met. - /// - /// In general we always seal iff: - /// 1. --force-sealing CLI parameter is provided - /// 2. There are listeners awaiting new work packages (e.g. remote work notifications or stratum). - fn forced_sealing(&self) -> bool { - let listeners_empty = { - #[cfg(feature = "work-notify")] - { self.listeners.read().is_empty() } - #[cfg(not(feature = "work-notify"))] - { true } - }; - - self.options.force_sealing || !listeners_empty - } - - /// Check is reseal is allowed and necessary. - fn requires_reseal(&self, best_block: BlockNumber) -> bool { - let mut sealing = self.sealing.lock(); - if !sealing.enabled { - trace!(target: "miner", "requires_reseal: sealing is disabled"); - return false - } - - if !sealing.reseal_allowed() { - trace!(target: "miner", "requires_reseal: reseal too early"); - return false - } - - trace!(target: "miner", "requires_reseal: sealing enabled"); - - // Disable sealing if there were no requests for SEALING_TIMEOUT_IN_BLOCKS - let had_requests = sealing.last_request.map(|last_request| - best_block.saturating_sub(last_request) <= SEALING_TIMEOUT_IN_BLOCKS - ).unwrap_or(false); - - // keep sealing enabled if any of the conditions is met - let sealing_enabled = self.forced_sealing() - || self.transaction_queue.has_local_pending_transactions() - || self.engine.seals_internally() == Some(true) - || had_requests; - - let should_disable_sealing = !sealing_enabled; - - trace!(target: "miner", "requires_reseal: should_disable_sealing={}; forced={:?}, has_local={:?}, internal={:?}, had_requests={:?}", - should_disable_sealing, - self.forced_sealing(), - self.transaction_queue.has_local_pending_transactions(), - self.engine.seals_internally(), - had_requests, - ); - - if should_disable_sealing { - trace!(target: "miner", "Miner sleeping (current {}, last {})", best_block, sealing.last_request.unwrap_or(0)); - sealing.enabled = false; - sealing.queue.reset(); - false - } else { - // sealing enabled and we don't want to sleep. - sealing.next_allowed_reseal = Instant::now() + self.options.reseal_min_period; - true - } - } - - // TODO: (https://github.com/paritytech/parity-ethereum/issues/10407) - // This is only used in authority_round path, and should be refactored to merge with the other seal() path. - // Attempts to perform internal sealing (one that does not require work) and handles the result depending on the - // type of Seal. - fn seal_and_import_block_internally(&self, chain: &C, block: ClosedBlock) -> bool - where C: BlockChain + SealedBlockImporter, - { - { - let sealing = self.sealing.lock(); - if block.transactions.is_empty() - && !self.forced_sealing() - && Instant::now() <= sealing.next_mandatory_reseal - { - return false - } - } - - trace!(target: "miner", "seal_block_internally: attempting internal seal."); - - let parent_header = match chain.block_header(BlockId::Hash(*block.header.parent_hash())) { - Some(h) => { - match h.decode() { - Ok(decoded_hdr) => decoded_hdr, - Err(_) => return false - } - } - None => return false, - }; - - match self.engine.generate_seal(&block, &parent_header) { - // Save proposal for later seal submission and broadcast it. - Seal::Proposal(seal) => { - trace!(target: "miner", "Received a Proposal seal."); - { - let mut sealing = self.sealing.lock(); - sealing.next_mandatory_reseal = Instant::now() + self.options.reseal_max_period; - sealing.queue.set_pending(block.clone()); - sealing.queue.use_last_ref(); - } - - block - .lock() - .seal(&*self.engine, seal) - .map(|sealed| { - chain.broadcast_proposal_block(sealed); - true - }) - .unwrap_or_else(|e| { - warn!("ERROR: seal failed when given internally generated seal: {}", e); - false - }) - }, - // Directly import a regular sealed block. - Seal::Regular(seal) => { - trace!(target: "miner", "Received a Regular seal."); - { - let mut sealing = self.sealing.lock(); - sealing.next_mandatory_reseal = Instant::now() + self.options.reseal_max_period; - } - - block - .lock() - .seal(&*self.engine, seal) - .map(|sealed| { - chain.import_sealed_block(sealed).is_ok() - }) - .unwrap_or_else(|e| { - warn!("ERROR: seal failed when given internally generated seal: {}", e); - false - }) - }, - Seal::None => false, - } - } - - /// Prepares work which has to be done to seal. - fn prepare_work(&self, block: ClosedBlock, original_work_hash: Option) { - let (work, is_new) = { - let block_header = block.header.clone(); - let block_hash = block_header.hash(); - - let mut sealing = self.sealing.lock(); - let last_work_hash = sealing.queue.peek_last_ref().map(|pb| pb.header.hash()); - - trace!( - target: "miner", - "prepare_work: Checking whether we need to reseal: orig={:?} last={:?}, this={:?}", - original_work_hash, last_work_hash, block_hash - ); - - let (work, is_new) = if last_work_hash.map_or(true, |h| h != block_hash) { - trace!( - target: "miner", - "prepare_work: Pushing a new, refreshed or borrowed pending {}...", - block_hash - ); - let is_new = original_work_hash.map_or(true, |h| h != block_hash); - - sealing.queue.set_pending(block); - - #[cfg(feature = "work-notify")] - { - // If push notifications are enabled we assume all work items are used. - if is_new && !self.listeners.read().is_empty() { - sealing.queue.use_last_ref(); - } - } - - (Some((block_hash, *block_header.difficulty(), block_header.number())), is_new) - } else { - (None, false) - }; - trace!( - target: "miner", - "prepare_work: leaving (last={:?})", - sealing.queue.peek_last_ref().map(|b| b.header.hash()) - ); - (work, is_new) - }; - - #[cfg(feature = "work-notify")] - { - if is_new { - work.map(|(pow_hash, difficulty, number)| { - for notifier in self.listeners.read().iter() { - notifier.notify(pow_hash, difficulty, number) - } - }); - } - } - - // NB: hack to use variables to avoid warning. - #[cfg(not(feature = "work-notify"))] - { - let _work = work; - let _is_new = is_new; - } - } - - /// Prepare a pending block. Returns the preparation status. - fn prepare_pending_block(&self, client: &C) -> BlockPreparationStatus where - C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync, - { - trace!(target: "miner", "prepare_pending_block: entering"); - let prepare_new = { - let mut sealing = self.sealing.lock(); - let have_work = sealing.queue.peek_last_ref().is_some(); - trace!(target: "miner", "prepare_pending_block: have_work={}", have_work); - if !have_work { - sealing.enabled = true; - true - } else { - false - } - }; - - let preparation_status = if prepare_new { - // -------------------------------------------------------------------------- - // | NOTE Code below requires sealing locks. | - // | Make sure to release the locks before calling that method. | - // -------------------------------------------------------------------------- - match self.prepare_block(client) { - Some((block, original_work_hash)) => { - self.prepare_work(block, original_work_hash); - BlockPreparationStatus::Succeeded - }, - None => BlockPreparationStatus::Failed, - } - } else { - BlockPreparationStatus::NotPrepared - }; - - let best_number = client.chain_info().best_block_number; - let mut sealing = self.sealing.lock(); - if sealing.last_request != Some(best_number) { - trace!( - target: "miner", - "prepare_pending_block: Miner received request (was {}, now {}) - waking up.", - sealing.last_request.unwrap_or(0), best_number - ); - sealing.last_request = Some(best_number); - } - - preparation_status - } - - /// Prepare pending block, check whether sealing is needed, and then update sealing. - fn prepare_and_update_sealing(&self, chain: &C) { - use miner::MinerService; - - // Make sure to do it after transaction is imported and lock is dropped. - // We need to create pending block and enable sealing. - if self.engine.seals_internally().unwrap_or(false) || self.prepare_pending_block(chain) == BlockPreparationStatus::NotPrepared { - // If new block has not been prepared (means we already had one) - // or Engine might be able to seal internally, - // we need to update sealing. - self.update_sealing(chain); - } - } + /// Push listener that will handle new jobs + #[cfg(feature = "work-notify")] + pub fn add_work_listener(&self, notifier: Box) { + self.listeners.write().push(notifier); + self.sealing.lock().enabled = true; + } + + /// Set a callback to be notified about imported transactions' hashes. + pub fn add_transactions_listener(&self, f: Box) { + self.transaction_queue.add_listener(f); + } + + /// Creates new instance of miner Arc. + pub fn new( + options: MinerOptions, + gas_pricer: GasPricer, + spec: &Spec, + accounts: A, + ) -> Self { + let limits = options.pool_limits.clone(); + let verifier_options = options.pool_verification_options.clone(); + let tx_queue_strategy = options.tx_queue_strategy; + let nonce_cache_size = cmp::max(4096, limits.max_count / 4); + let refuse_service_transactions = options.refuse_service_transactions; + let engine = spec.engine.clone(); + + Miner { + sealing: Mutex::new(SealingWork { + queue: UsingQueue::new(options.work_queue_size), + enabled: options.force_sealing || spec.engine.seals_internally().is_some(), + next_allowed_reseal: Instant::now(), + next_mandatory_reseal: Instant::now() + options.reseal_max_period, + last_request: None, + }), + params: RwLock::new(AuthoringParams::default()), + #[cfg(feature = "work-notify")] + listeners: RwLock::new(vec![]), + gas_pricer: Mutex::new(gas_pricer), + nonce_cache: NonceCache::new(nonce_cache_size), + options, + transaction_queue: Arc::new(TransactionQueue::new( + limits, + verifier_options, + tx_queue_strategy, + )), + accounts: Arc::new(accounts), + engine, + io_channel: RwLock::new(None), + service_transaction_checker: if refuse_service_transactions { + None + } else { + Some(ServiceTransactionChecker::default()) + }, + } + } + + /// Creates new instance of miner with given spec and accounts. + /// + /// NOTE This should be only used for tests. + pub fn new_for_tests(spec: &Spec, accounts: Option>) -> Miner { + let minimal_gas_price = 0.into(); + Miner::new( + MinerOptions { + pool_verification_options: pool::verifier::Options { + minimal_gas_price, + block_gas_limit: U256::max_value(), + tx_gas_limit: U256::max_value(), + no_early_reject: false, + }, + reseal_min_period: Duration::from_secs(0), + ..Default::default() + }, + GasPricer::new_fixed(minimal_gas_price), + spec, + accounts.unwrap_or_default(), + ) + } + + /// Sets `IoChannel` + pub fn set_io_channel(&self, io_channel: IoChannel) { + *self.io_channel.write() = Some(io_channel); + } + + /// Sets in-blockchain checker for transactions. + pub fn set_in_chain_checker(&self, chain: &Arc) + where + C: TransactionInfo + Send + Sync + 'static, + { + let client = Arc::downgrade(chain); + self.transaction_queue + .set_in_chain_checker(move |hash| match client.upgrade() { + Some(info) => info.transaction_block(TransactionId::Hash(*hash)).is_some(), + None => false, + }); + } + + /// Clear all pending block states + pub fn clear(&self) { + self.sealing.lock().queue.reset(); + } + + /// Updates transaction queue verification limits. + /// + /// Limits consist of current block gas limit and minimal gas price. + pub fn update_transaction_queue_limits(&self, block_gas_limit: U256) { + trace!(target: "miner", "minimal_gas_price: recalibrating..."); + let txq = self.transaction_queue.clone(); + let mut options = self.options.pool_verification_options.clone(); + self.gas_pricer.lock().recalibrate(move |gas_price| { + debug!(target: "miner", "minimal_gas_price: Got gas price! {}", gas_price); + options.minimal_gas_price = gas_price; + options.block_gas_limit = block_gas_limit; + txq.set_verifier_options(options); + }); + } + + /// Returns ServiceTransactionChecker + pub fn service_transaction_checker(&self) -> Option { + self.service_transaction_checker.clone() + } + + /// Retrieves an existing pending block iff it's not older than given block number. + /// + /// NOTE: This will not prepare a new pending block if it's not existing. + fn map_existing_pending_block(&self, f: F, latest_block_number: BlockNumber) -> Option + where + F: FnOnce(&ClosedBlock) -> T, + { + self.sealing.lock().queue.peek_last_ref().and_then(|b| { + // to prevent a data race between block import and updating pending block + // we allow the number to be equal. + if b.header.number() >= latest_block_number { + Some(f(b)) + } else { + None + } + }) + } + + fn pool_client<'a, C: 'a>(&'a self, chain: &'a C) -> PoolClient<'a, C> + where + C: BlockChain + CallContract, + { + PoolClient::new( + chain, + &self.nonce_cache, + &*self.engine, + &*self.accounts, + self.service_transaction_checker.as_ref(), + ) + } + + /// Prepares new block for sealing including top transactions from queue. + fn prepare_block(&self, chain: &C) -> Option<(ClosedBlock, Option)> + where + C: BlockChain + CallContract + BlockProducer + Nonce + Sync, + { + trace_time!("prepare_block"); + let chain_info = chain.chain_info(); + + // Open block + let (mut open_block, original_work_hash) = { + let mut sealing = self.sealing.lock(); + let last_work_hash = sealing.queue.peek_last_ref().map(|pb| pb.header.hash()); + let best_hash = chain_info.best_block_hash; + + // check to see if last ClosedBlock in would_seals is actually same parent block. + // if so + // duplicate, re-open and push any new transactions. + // if at least one was pushed successfully, close and enqueue new ClosedBlock; + // otherwise, leave everything alone. + // otherwise, author a fresh block. + let mut open_block = match sealing + .queue + .get_pending_if(|b| b.header.parent_hash() == &best_hash) + { + Some(old_block) => { + trace!(target: "miner", "prepare_block: Already have previous work; updating and returning"); + // add transactions to old_block + chain.reopen_block(old_block) + } + None => { + // block not found - create it. + trace!(target: "miner", "prepare_block: No existing work - making new block"); + let params = self.params.read().clone(); + + match chain.prepare_open_block( + params.author, + params.gas_range_target, + params.extra_data, + ) { + Ok(block) => block, + Err(err) => { + warn!(target: "miner", "Open new block failed with error {:?}. This is likely an error in chain specificiations or on-chain consensus smart contracts.", err); + return None; + } + } + } + }; + + if self.options.infinite_pending_block { + open_block.remove_gas_limit(); + } + + (open_block, last_work_hash) + }; + + let mut invalid_transactions = HashSet::new(); + let mut not_allowed_transactions = HashSet::new(); + let mut senders_to_penalize = HashSet::new(); + let block_number = open_block.header.number(); + + let mut tx_count = 0usize; + let mut skipped_transactions = 0usize; + + let client = self.pool_client(chain); + let engine_params = self.engine.params(); + let min_tx_gas: U256 = self + .engine + .schedule(chain_info.best_block_number) + .tx_gas + .into(); + let nonce_cap: Option = if chain_info.best_block_number + 1 + >= engine_params.dust_protection_transition + { + Some((engine_params.nonce_cap_increment * (chain_info.best_block_number + 1)).into()) + } else { + None + }; + // we will never need more transactions than limit divided by min gas + let max_transactions = if min_tx_gas.is_zero() { + usize::max_value() + } else { + MAX_SKIPPED_TRANSACTIONS.saturating_add( + cmp::min( + *open_block.header.gas_limit() / min_tx_gas, + u64::max_value().into(), + ) + .as_u64() as usize, + ) + }; + + let pending: Vec> = self.transaction_queue.pending( + client.clone(), + pool::PendingSettings { + block_number: chain_info.best_block_number, + current_timestamp: chain_info.best_block_timestamp, + nonce_cap, + max_len: max_transactions, + ordering: miner::PendingOrdering::Priority, + }, + ); + + let took_ms = |elapsed: &Duration| { + elapsed.as_secs() * 1000 + elapsed.subsec_nanos() as u64 / 1_000_000 + }; + + let block_start = Instant::now(); + debug!(target: "miner", "Attempting to push {} transactions.", pending.len()); + + for tx in pending { + let start = Instant::now(); + + let transaction = tx.signed().clone(); + let hash = transaction.hash(); + let sender = transaction.sender(); + + // Re-verify transaction again vs current state. + let result = client + .verify_for_pending_block(&transaction, &open_block.header) + .map_err(|e| e.into()) + .and_then(|_| open_block.push_transaction(transaction, None)); + + let took = start.elapsed(); + + // Check for heavy transactions + match self.options.tx_queue_penalization { + Penalization::Enabled { + ref offend_threshold, + } if &took > offend_threshold => { + senders_to_penalize.insert(sender); + debug!(target: "miner", "Detected heavy transaction ({} ms). Penalizing sender.", took_ms(&took)); + } + _ => {} + } + + debug!(target: "miner", "Adding tx {:?} took {} ms", hash, took_ms(&took)); + match result { + Err(Error( + ErrorKind::Execution(ExecutionError::BlockGasLimitReached { + gas_limit, + gas_used, + gas, + }), + _, + )) => { + debug!(target: "miner", "Skipping adding transaction to block because of gas limit: {:?} (limit: {:?}, used: {:?}, gas: {:?})", hash, gas_limit, gas_used, gas); + + // Penalize transaction if it's above current gas limit + if gas > gas_limit { + debug!(target: "txqueue", "[{:?}] Transaction above block gas limit.", hash); + invalid_transactions.insert(hash); + } + + // Exit early if gas left is smaller then min_tx_gas + let gas_left = gas_limit - gas_used; + if gas_left < min_tx_gas { + debug!(target: "miner", "Remaining gas is lower than minimal gas for a transaction. Block is full."); + break; + } + + // Avoid iterating over the entire queue in case block is almost full. + skipped_transactions += 1; + if skipped_transactions > MAX_SKIPPED_TRANSACTIONS { + debug!(target: "miner", "Reached skipped transactions threshold. Assuming block is full."); + break; + } + } + // Invalid nonce error can happen only if previous transaction is skipped because of gas limit. + // If there is errornous state of transaction queue it will be fixed when next block is imported. + Err(Error( + ErrorKind::Execution(ExecutionError::InvalidNonce { expected, got }), + _, + )) => { + debug!(target: "miner", "Skipping adding transaction to block because of invalid nonce: {:?} (expected: {:?}, got: {:?})", hash, expected, got); + } + // already have transaction - ignore + Err(Error(ErrorKind::Transaction(transaction::Error::AlreadyImported), _)) => {} + Err(Error(ErrorKind::Transaction(transaction::Error::NotAllowed), _)) => { + not_allowed_transactions.insert(hash); + debug!(target: "miner", "Skipping non-allowed transaction for sender {:?}", hash); + } + Err(e) => { + debug!(target: "txqueue", "[{:?}] Marking as invalid: {:?}.", hash, e); + debug!( + target: "miner", "Error adding transaction to block: number={}. transaction_hash={:?}, Error: {:?}", block_number, hash, e + ); + invalid_transactions.insert(hash); + } + // imported ok + _ => tx_count += 1, + } + } + let elapsed = block_start.elapsed(); + debug!(target: "miner", "Pushed {} transactions in {} ms", tx_count, took_ms(&elapsed)); + + let block = match open_block.close() { + Ok(block) => block, + Err(err) => { + warn!(target: "miner", "Closing the block failed with error {:?}. This is likely an error in chain specificiations or on-chain consensus smart contracts.", err); + return None; + } + }; + + { + self.transaction_queue + .remove(invalid_transactions.iter(), true); + self.transaction_queue + .remove(not_allowed_transactions.iter(), false); + self.transaction_queue.penalize(senders_to_penalize.iter()); + } + + Some((block, original_work_hash)) + } + + /// Returns `true` if we should create pending block even if some other conditions are not met. + /// + /// In general we always seal iff: + /// 1. --force-sealing CLI parameter is provided + /// 2. There are listeners awaiting new work packages (e.g. remote work notifications or stratum). + fn forced_sealing(&self) -> bool { + let listeners_empty = { + #[cfg(feature = "work-notify")] + { + self.listeners.read().is_empty() + } + #[cfg(not(feature = "work-notify"))] + { + true + } + }; + + self.options.force_sealing || !listeners_empty + } + + /// Check is reseal is allowed and necessary. + fn requires_reseal(&self, best_block: BlockNumber) -> bool { + let mut sealing = self.sealing.lock(); + if !sealing.enabled { + trace!(target: "miner", "requires_reseal: sealing is disabled"); + return false; + } + + if !sealing.reseal_allowed() { + trace!(target: "miner", "requires_reseal: reseal too early"); + return false; + } + + trace!(target: "miner", "requires_reseal: sealing enabled"); + + // Disable sealing if there were no requests for SEALING_TIMEOUT_IN_BLOCKS + let had_requests = sealing + .last_request + .map(|last_request| { + best_block.saturating_sub(last_request) <= SEALING_TIMEOUT_IN_BLOCKS + }) + .unwrap_or(false); + + // keep sealing enabled if any of the conditions is met + let sealing_enabled = self.forced_sealing() + || self.transaction_queue.has_local_pending_transactions() + || self.engine.seals_internally() == Some(true) + || had_requests; + + let should_disable_sealing = !sealing_enabled; + + trace!(target: "miner", "requires_reseal: should_disable_sealing={}; forced={:?}, has_local={:?}, internal={:?}, had_requests={:?}", + should_disable_sealing, + self.forced_sealing(), + self.transaction_queue.has_local_pending_transactions(), + self.engine.seals_internally(), + had_requests, + ); + + if should_disable_sealing { + trace!(target: "miner", "Miner sleeping (current {}, last {})", best_block, sealing.last_request.unwrap_or(0)); + sealing.enabled = false; + sealing.queue.reset(); + false + } else { + // sealing enabled and we don't want to sleep. + sealing.next_allowed_reseal = Instant::now() + self.options.reseal_min_period; + true + } + } + + // TODO: (https://github.com/openethereum/openethereum/issues/10407) + // This is only used in authority_round path, and should be refactored to merge with the other seal() path. + // Attempts to perform internal sealing (one that does not require work) and handles the result depending on the + // type of Seal. + fn seal_and_import_block_internally(&self, chain: &C, block: ClosedBlock) -> bool + where + C: BlockChain + SealedBlockImporter, + { + { + let sealing = self.sealing.lock(); + if block.transactions.is_empty() + && !self.forced_sealing() + && Instant::now() <= sealing.next_mandatory_reseal + { + return false; + } + } + + trace!(target: "miner", "seal_block_internally: attempting internal seal."); + + let parent_header = match chain.block_header(BlockId::Hash(*block.header.parent_hash())) { + Some(h) => match h.decode() { + Ok(decoded_hdr) => decoded_hdr, + Err(_) => return false, + }, + None => return false, + }; + + match self.engine.generate_seal(&block, &parent_header) { + // Save proposal for later seal submission and broadcast it. + Seal::Proposal(seal) => { + trace!(target: "miner", "Received a Proposal seal."); + { + let mut sealing = self.sealing.lock(); + sealing.next_mandatory_reseal = Instant::now() + self.options.reseal_max_period; + sealing.queue.set_pending(block.clone()); + sealing.queue.use_last_ref(); + } + + block + .lock() + .seal(&*self.engine, seal) + .map(|sealed| { + chain.broadcast_proposal_block(sealed); + true + }) + .unwrap_or_else(|e| { + warn!( + "ERROR: seal failed when given internally generated seal: {}", + e + ); + false + }) + } + // Directly import a regular sealed block. + Seal::Regular(seal) => { + trace!(target: "miner", "Received a Regular seal."); + { + let mut sealing = self.sealing.lock(); + sealing.next_mandatory_reseal = Instant::now() + self.options.reseal_max_period; + } + + block + .lock() + .seal(&*self.engine, seal) + .map(|sealed| chain.import_sealed_block(sealed).is_ok()) + .unwrap_or_else(|e| { + warn!( + "ERROR: seal failed when given internally generated seal: {}", + e + ); + false + }) + } + Seal::None => false, + } + } + + /// Prepares work which has to be done to seal. + fn prepare_work(&self, block: ClosedBlock, original_work_hash: Option) { + let (work, is_new) = { + let block_header = block.header.clone(); + let block_hash = block_header.hash(); + + let mut sealing = self.sealing.lock(); + let last_work_hash = sealing.queue.peek_last_ref().map(|pb| pb.header.hash()); + + trace!( + target: "miner", + "prepare_work: Checking whether we need to reseal: orig={:?} last={:?}, this={:?}", + original_work_hash, last_work_hash, block_hash + ); + + let (work, is_new) = if last_work_hash.map_or(true, |h| h != block_hash) { + trace!( + target: "miner", + "prepare_work: Pushing a new, refreshed or borrowed pending {}...", + block_hash + ); + let is_new = original_work_hash.map_or(true, |h| h != block_hash); + + sealing.queue.set_pending(block); + + #[cfg(feature = "work-notify")] + { + // If push notifications are enabled we assume all work items are used. + if is_new && !self.listeners.read().is_empty() { + sealing.queue.use_last_ref(); + } + } + + ( + Some(( + block_hash, + *block_header.difficulty(), + block_header.number(), + )), + is_new, + ) + } else { + (None, false) + }; + trace!( + target: "miner", + "prepare_work: leaving (last={:?})", + sealing.queue.peek_last_ref().map(|b| b.header.hash()) + ); + (work, is_new) + }; + + #[cfg(feature = "work-notify")] + { + if is_new { + work.map(|(pow_hash, difficulty, number)| { + for notifier in self.listeners.read().iter() { + notifier.notify(pow_hash, difficulty, number) + } + }); + } + } + + // NB: hack to use variables to avoid warning. + #[cfg(not(feature = "work-notify"))] + { + let _work = work; + let _is_new = is_new; + } + } + + /// Prepare a pending block. Returns the preparation status. + fn prepare_pending_block(&self, client: &C) -> BlockPreparationStatus + where + C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync, + { + trace!(target: "miner", "prepare_pending_block: entering"); + let prepare_new = { + let mut sealing = self.sealing.lock(); + let have_work = sealing.queue.peek_last_ref().is_some(); + trace!(target: "miner", "prepare_pending_block: have_work={}", have_work); + if !have_work { + sealing.enabled = true; + true + } else { + false + } + }; + + let preparation_status = if prepare_new { + // -------------------------------------------------------------------------- + // | NOTE Code below requires sealing locks. | + // | Make sure to release the locks before calling that method. | + // -------------------------------------------------------------------------- + match self.prepare_block(client) { + Some((block, original_work_hash)) => { + self.prepare_work(block, original_work_hash); + BlockPreparationStatus::Succeeded + } + None => BlockPreparationStatus::Failed, + } + } else { + BlockPreparationStatus::NotPrepared + }; + + let best_number = client.chain_info().best_block_number; + let mut sealing = self.sealing.lock(); + if sealing.last_request != Some(best_number) { + trace!( + target: "miner", + "prepare_pending_block: Miner received request (was {}, now {}) - waking up.", + sealing.last_request.unwrap_or(0), best_number + ); + sealing.last_request = Some(best_number); + } + + preparation_status + } + + /// Prepare pending block, check whether sealing is needed, and then update sealing. + fn prepare_and_update_sealing(&self, chain: &C) { + // Make sure to do it after transaction is imported and lock is dropped. + // We need to create pending block and enable sealing. + if self.engine.seals_internally().unwrap_or(false) + || self.prepare_pending_block(chain) == BlockPreparationStatus::NotPrepared + { + // If new block has not been prepared (means we already had one) + // or Engine might be able to seal internally, + // we need to update sealing. + self.update_sealing(chain, ForceUpdateSealing::No); + } + } } -const SEALING_TIMEOUT_IN_BLOCKS : u64 = 5; +const SEALING_TIMEOUT_IN_BLOCKS: u64 = 5; impl miner::MinerService for Miner { - type State = State<::state_db::StateDB>; - - fn authoring_params(&self) -> AuthoringParams { - self.params.read().clone() - } - - fn set_gas_range_target(&self, gas_range_target: (U256, U256)) { - self.params.write().gas_range_target = gas_range_target; - } - - fn set_extra_data(&self, extra_data: Bytes) { - self.params.write().extra_data = extra_data; - } - - fn set_author(&self, author: Author) { - self.params.write().author = author.address(); - - if let Author::Sealer(signer) = author { - if self.engine.seals_internally().is_some() { - // Enable sealing - self.sealing.lock().enabled = true; - // -------------------------------------------------------------------------- - // | NOTE Code below may require author and sealing locks | - // | (some `Engine`s call `EngineClient.update_sealing()`) | - // | Make sure to release the locks before calling that method. | - // -------------------------------------------------------------------------- - self.engine.set_signer(signer); - } else { - warn!("Setting an EngineSigner while Engine does not require one."); - } - } - } - - fn sensible_gas_price(&self) -> U256 { - // 10% above our minimum. - self.transaction_queue.current_worst_gas_price() * 110u32 / 100 - } - - fn sensible_gas_limit(&self) -> U256 { - self.params.read().gas_range_target.0 / 5 - } - - fn set_minimal_gas_price(&self, new_price: U256) -> Result { - match *self.gas_pricer.lock() { - // Binding the gas pricer to `gp` here to prevent - // a deadlock when calling recalibrate() - ref mut gp @ GasPricer::Fixed(_) => { - trace!(target: "miner", "minimal_gas_price: recalibrating fixed..."); - *gp = GasPricer::new_fixed(new_price); - - let txq = self.transaction_queue.clone(); - let mut options = self.options.pool_verification_options.clone(); - gp.recalibrate(move |gas_price| { - debug!(target: "miner", "minimal_gas_price: Got gas price! {}", gas_price); - options.minimal_gas_price = gas_price; - txq.set_verifier_options(options); - }); - - Ok(true) - }, - #[cfg(feature = "price-info")] - GasPricer::Calibrated(_) => { - let error_msg = "Can't update fixed gas price while automatic gas calibration is enabled."; - return Err(error_msg); - }, - } - } - - fn import_external_transactions( - &self, - chain: &C, - transactions: Vec - ) -> Vec> { - trace!(target: "external_tx", "Importing external transactions"); - let client = self.pool_client(chain); - let results = self.transaction_queue.import( - client, - transactions.into_iter().map(pool::verifier::Transaction::Unverified).collect(), - ); - - // -------------------------------------------------------------------------- - // | NOTE Code below requires sealing locks. | - // | Make sure to release the locks before calling that method. | - // -------------------------------------------------------------------------- - if !results.is_empty() && self.options.reseal_on_external_tx && self.sealing.lock().reseal_allowed() { - self.prepare_and_update_sealing(chain); - } - - results - } - - fn import_own_transaction( - &self, - chain: &C, - pending: PendingTransaction - ) -> Result<(), transaction::Error> { - // note: you may want to use `import_claimed_local_transaction` instead of this one. - - trace!(target: "own_tx", "Importing transaction: {:?}", pending); - - let client = self.pool_client(chain); - let imported = self.transaction_queue.import( - client, - vec![pool::verifier::Transaction::Local(pending)] - ).pop().expect("one result returned per added transaction; one added => one result; qed"); - - // -------------------------------------------------------------------------- - // | NOTE Code below requires sealing locks. | - // | Make sure to release the locks before calling that method. | - // -------------------------------------------------------------------------- - if imported.is_ok() && self.options.reseal_on_own_tx && self.sealing.lock().reseal_allowed() { - self.prepare_and_update_sealing(chain); - } - - imported - } - - fn import_claimed_local_transaction( - &self, - chain: &C, - pending: PendingTransaction, - trusted: bool - ) -> Result<(), transaction::Error> { - // treat the tx as local if the option is enabled, if we have the account, or if - // the account is specified as a Prioritized Local Addresses - let sender = pending.sender(); - let treat_as_local = trusted - || !self.options.tx_queue_no_unfamiliar_locals - || self.accounts.is_local(&sender); - - if treat_as_local { - self.import_own_transaction(chain, pending) - } else { - // We want to replicate behaviour for external transactions if we're not going to treat - // this as local. This is important with regards to sealing blocks - self.import_external_transactions(chain, vec![pending.transaction.into()]) - .pop().expect("one result per tx, as in `import_own_transaction`") - } - } - - fn local_transactions(&self) -> BTreeMap { - self.transaction_queue.local_transactions() - } - - fn queued_transactions(&self) -> Vec> { - self.transaction_queue.all_transactions() - } - - fn queued_transaction_hashes(&self) -> Vec { - self.transaction_queue.all_transaction_hashes() - } - - fn pending_transaction_hashes(&self, chain: &C) -> BTreeSet where - C: ChainInfo + Sync, - { - let chain_info = chain.chain_info(); - - let from_queue = || self.transaction_queue.pending_hashes( - |sender| self.nonce_cache.get(sender), - ); - - let from_pending = || { - self.map_existing_pending_block(|sealing| { - sealing.transactions - .iter() - .map(|signed| signed.hash()) - .collect() - }, chain_info.best_block_number) - }; - - match self.options.pending_set { - PendingSet::AlwaysQueue => { - from_queue() - }, - PendingSet::AlwaysSealing => { - from_pending().unwrap_or_default() - }, - PendingSet::SealingOrElseQueue => { - from_pending().unwrap_or_else(from_queue) - }, - } - } - - fn ready_transactions(&self, chain: &C, max_len: usize, ordering: miner::PendingOrdering) - -> Vec> - where - C: ChainInfo + Nonce + Sync, - { - let chain_info = chain.chain_info(); - - let from_queue = || { - // We propagate transactions over the nonce cap. - // The mechanism is only to limit number of transactions in pending block - // those transactions are valid and will just be ready to be included in next block. - let nonce_cap = None; - - self.transaction_queue.pending( - CachedNonceClient::new(chain, &self.nonce_cache), - pool::PendingSettings { - block_number: chain_info.best_block_number, - current_timestamp: chain_info.best_block_timestamp, - nonce_cap, - max_len, - ordering, - }, - ) - }; - - let from_pending = || { - self.map_existing_pending_block(|sealing| { - sealing.transactions - .iter() - .map(|signed| pool::VerifiedTransaction::from_pending_block_transaction(signed.clone())) - .map(Arc::new) - .take(max_len) - .collect() - }, chain_info.best_block_number) - }; - - match self.options.pending_set { - PendingSet::AlwaysQueue => { - from_queue() - }, - PendingSet::AlwaysSealing => { - from_pending().unwrap_or_default() - }, - PendingSet::SealingOrElseQueue => { - from_pending().unwrap_or_else(from_queue) - }, - } - } - - fn next_nonce(&self, chain: &C, address: &Address) -> U256 where - C: Nonce + Sync, - { - self.transaction_queue.next_nonce(CachedNonceClient::new(chain, &self.nonce_cache), address) - .unwrap_or_else(|| chain.latest_nonce(address)) - } - - fn transaction(&self, hash: &H256) -> Option> { - self.transaction_queue.find(hash) - } - - fn remove_transaction(&self, hash: &H256) -> Option> { - self.transaction_queue.remove(::std::iter::once(hash), false) - .pop() - .expect("remove() returns one result per hash; one hash passed; qed") - } - - fn queue_status(&self) -> QueueStatus { - self.transaction_queue.status() - } - - fn pending_receipts(&self, best_block: BlockNumber) -> Option> { - self.map_existing_pending_block(|pending| { - let receipts = &pending.receipts; - pending.transactions - .iter() - .enumerate() - .map(|(index, tx)| { - let prev_gas = if index == 0 { Default::default() } else { receipts[index - 1].gas_used }; - let receipt = &receipts[index]; - RichReceipt { - transaction_hash: tx.hash(), - transaction_index: index, - cumulative_gas_used: receipt.gas_used, - gas_used: receipt.gas_used - prev_gas, - contract_address: match tx.action { - Action::Call(_) => None, - Action::Create => { - let sender = tx.sender(); - Some(contract_address(self.engine.create_address_scheme(pending.header.number()), &sender, &tx.nonce, &tx.data).0) - } - }, - logs: receipt.logs.clone(), - log_bloom: receipt.log_bloom, - outcome: receipt.outcome.clone(), - } - }) - .collect() - }, best_block) - } - - /// Update sealing if required. - /// Prepare the block and work if the Engine does not seal internally. - fn update_sealing(&self, chain: &C) where - C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync, - { - trace!(target: "miner", "update_sealing"); - - // Do nothing if reseal is not required, - // but note that `requires_reseal` updates internal state. - if !self.requires_reseal(chain.chain_info().best_block_number) { - return; - } - - // -------------------------------------------------------------------------- - // | NOTE Code below requires sealing locks. | - // | Make sure to release the locks before calling that method. | - // -------------------------------------------------------------------------- - trace!(target: "miner", "update_sealing: preparing a block"); - let (block, original_work_hash) = match self.prepare_block(chain) { - Some((block, original_work_hash)) => (block, original_work_hash), - None => return, - }; - - // refuse to seal the first block of the chain if it contains hard forks - // which should be on by default. - if block.header.number() == 1 { - if let Some(name) = self.engine.params().nonzero_bugfix_hard_fork() { - warn!("Your chain specification contains one or more hard forks which are required to be \ + type State = State<::state_db::StateDB>; + + fn authoring_params(&self) -> AuthoringParams { + self.params.read().clone() + } + + fn set_gas_range_target(&self, gas_range_target: (U256, U256)) { + self.params.write().gas_range_target = gas_range_target; + } + + fn set_extra_data(&self, extra_data: Bytes) { + self.params.write().extra_data = extra_data; + } + + fn set_author(&self, author: Author) { + self.params.write().author = author.address(); + + if let Author::Sealer(signer) = author { + if self.engine.seals_internally().is_some() { + // Enable sealing + self.sealing.lock().enabled = true; + // -------------------------------------------------------------------------- + // | NOTE Code below may require author and sealing locks | + // | (some `Engine`s call `EngineClient.update_sealing()`) | + // | Make sure to release the locks before calling that method. | + // -------------------------------------------------------------------------- + self.engine.set_signer(signer); + } else { + warn!("Setting an EngineSigner while Engine does not require one."); + } + } + } + + fn sensible_gas_price(&self) -> U256 { + // 10% above our minimum. + self.transaction_queue.current_worst_gas_price() * 110u32 / 100 + } + + fn sensible_gas_limit(&self) -> U256 { + self.params.read().gas_range_target.0 / 5 + } + + fn set_minimal_gas_price(&self, new_price: U256) -> Result { + match *self.gas_pricer.lock() { + // Binding the gas pricer to `gp` here to prevent + // a deadlock when calling recalibrate() + ref mut gp @ GasPricer::Fixed(_) => { + trace!(target: "miner", "minimal_gas_price: recalibrating fixed..."); + *gp = GasPricer::new_fixed(new_price); + + let txq = self.transaction_queue.clone(); + let mut options = self.options.pool_verification_options.clone(); + gp.recalibrate(move |gas_price| { + debug!(target: "miner", "minimal_gas_price: Got gas price! {}", gas_price); + options.minimal_gas_price = gas_price; + txq.set_verifier_options(options); + }); + + Ok(true) + } + #[cfg(feature = "price-info")] + GasPricer::Calibrated(_) => { + let error_msg = + "Can't update fixed gas price while automatic gas calibration is enabled."; + return Err(error_msg); + } + } + } + + fn import_external_transactions( + &self, + chain: &C, + transactions: Vec, + ) -> Vec> { + trace!(target: "external_tx", "Importing external transactions"); + let client = self.pool_client(chain); + let results = self.transaction_queue.import( + client, + transactions + .into_iter() + .map(pool::verifier::Transaction::Unverified) + .collect(), + ); + + // -------------------------------------------------------------------------- + // | NOTE Code below requires sealing locks. | + // | Make sure to release the locks before calling that method. | + // -------------------------------------------------------------------------- + if !results.is_empty() + && self.options.reseal_on_external_tx + && self.sealing.lock().reseal_allowed() + { + self.prepare_and_update_sealing(chain); + } + + results + } + + fn import_own_transaction( + &self, + chain: &C, + pending: PendingTransaction, + ) -> Result<(), transaction::Error> { + // note: you may want to use `import_claimed_local_transaction` instead of this one. + + trace!(target: "own_tx", "Importing transaction: {:?}", pending); + + let client = self.pool_client(chain); + let imported = self + .transaction_queue + .import(client, vec![pool::verifier::Transaction::Local(pending)]) + .pop() + .expect("one result returned per added transaction; one added => one result; qed"); + + // -------------------------------------------------------------------------- + // | NOTE Code below requires sealing locks. | + // | Make sure to release the locks before calling that method. | + // -------------------------------------------------------------------------- + if imported.is_ok() && self.options.reseal_on_own_tx && self.sealing.lock().reseal_allowed() + { + self.prepare_and_update_sealing(chain); + } + + imported + } + + fn import_claimed_local_transaction( + &self, + chain: &C, + pending: PendingTransaction, + trusted: bool, + ) -> Result<(), transaction::Error> { + // treat the tx as local if the option is enabled, if we have the account, or if + // the account is specified as a Prioritized Local Addresses + let sender = pending.sender(); + let treat_as_local = trusted + || !self.options.tx_queue_no_unfamiliar_locals + || self.accounts.is_local(&sender); + + if treat_as_local { + self.import_own_transaction(chain, pending) + } else { + // We want to replicate behaviour for external transactions if we're not going to treat + // this as local. This is important with regards to sealing blocks + self.import_external_transactions(chain, vec![pending.transaction.into()]) + .pop() + .expect("one result per tx, as in `import_own_transaction`") + } + } + + fn local_transactions(&self) -> BTreeMap { + self.transaction_queue.local_transactions() + } + + fn queued_transactions(&self) -> Vec> { + self.transaction_queue.all_transactions() + } + + fn queued_transaction_hashes(&self) -> Vec { + self.transaction_queue.all_transaction_hashes() + } + + fn pending_transaction_hashes(&self, chain: &C) -> BTreeSet + where + C: ChainInfo + Sync, + { + let chain_info = chain.chain_info(); + + let from_queue = || { + self.transaction_queue + .pending_hashes(|sender| self.nonce_cache.get(sender)) + }; + + let from_pending = || { + self.map_existing_pending_block( + |sealing| { + sealing + .transactions + .iter() + .map(|signed| signed.hash()) + .collect() + }, + chain_info.best_block_number, + ) + }; + + match self.options.pending_set { + PendingSet::AlwaysQueue => from_queue(), + PendingSet::AlwaysSealing => from_pending().unwrap_or_default(), + PendingSet::SealingOrElseQueue => from_pending().unwrap_or_else(from_queue), + } + } + + fn ready_transactions( + &self, + chain: &C, + max_len: usize, + ordering: miner::PendingOrdering, + ) -> Vec> + where + C: ChainInfo + Nonce + Sync, + { + let chain_info = chain.chain_info(); + + let from_queue = || { + // We propagate transactions over the nonce cap. + // The mechanism is only to limit number of transactions in pending block + // those transactions are valid and will just be ready to be included in next block. + let nonce_cap = None; + + self.transaction_queue.pending( + CachedNonceClient::new(chain, &self.nonce_cache), + pool::PendingSettings { + block_number: chain_info.best_block_number, + current_timestamp: chain_info.best_block_timestamp, + nonce_cap, + max_len, + ordering, + }, + ) + }; + + let from_pending = || { + self.map_existing_pending_block( + |sealing| { + sealing + .transactions + .iter() + .map(|signed| { + pool::VerifiedTransaction::from_pending_block_transaction( + signed.clone(), + ) + }) + .map(Arc::new) + .take(max_len) + .collect() + }, + chain_info.best_block_number, + ) + }; + + match self.options.pending_set { + PendingSet::AlwaysQueue => from_queue(), + PendingSet::AlwaysSealing => from_pending().unwrap_or_default(), + PendingSet::SealingOrElseQueue => from_pending().unwrap_or_else(from_queue), + } + } + + fn next_nonce(&self, chain: &C, address: &Address) -> U256 + where + C: Nonce + Sync, + { + self.transaction_queue + .next_nonce(CachedNonceClient::new(chain, &self.nonce_cache), address) + .unwrap_or_else(|| chain.latest_nonce(address)) + } + + fn transaction(&self, hash: &H256) -> Option> { + self.transaction_queue.find(hash) + } + + fn remove_transaction(&self, hash: &H256) -> Option> { + self.transaction_queue + .remove(::std::iter::once(hash), false) + .pop() + .expect("remove() returns one result per hash; one hash passed; qed") + } + + fn queue_status(&self) -> QueueStatus { + self.transaction_queue.status() + } + + fn pending_receipts(&self, best_block: BlockNumber) -> Option> { + self.map_existing_pending_block( + |pending| { + let receipts = &pending.receipts; + pending + .transactions + .iter() + .enumerate() + .map(|(index, tx)| { + let prev_gas = if index == 0 { + Default::default() + } else { + receipts[index - 1].gas_used + }; + let receipt = &receipts[index]; + RichReceipt { + from: tx.sender(), + to: match tx.action { + Action::Create => None, + Action::Call(ref address) => Some(*address), + }, + transaction_hash: tx.hash(), + transaction_index: index, + cumulative_gas_used: receipt.gas_used, + gas_used: receipt.gas_used - prev_gas, + contract_address: match tx.action { + Action::Call(_) => None, + Action::Create => { + let sender = tx.sender(); + Some( + contract_address( + self.engine + .create_address_scheme(pending.header.number()), + &sender, + &tx.nonce, + &tx.data, + ) + .0, + ) + } + }, + logs: receipt.logs.clone(), + log_bloom: receipt.log_bloom, + outcome: receipt.outcome.clone(), + } + }) + .collect() + }, + best_block, + ) + } + + /// Update sealing if required. + /// Prepare the block and work if the Engine does not seal internally. + fn update_sealing(&self, chain: &C, force: ForceUpdateSealing) + where + C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync, + { + trace!(target: "miner", "update_sealing"); + + // Do nothing if we don't want to force update_sealing and reseal is not required. + // but note that `requires_reseal` updates internal state. + if force == ForceUpdateSealing::No + && !self.requires_reseal(chain.chain_info().best_block_number) + { + return; + } + + // -------------------------------------------------------------------------- + // | NOTE Code below requires sealing locks. | + // | Make sure to release the locks before calling that method. | + // -------------------------------------------------------------------------- + trace!(target: "miner", "update_sealing: preparing a block"); + let (block, original_work_hash) = match self.prepare_block(chain) { + Some((block, original_work_hash)) => (block, original_work_hash), + None => return, + }; + + // refuse to seal the first block of the chain if it contains hard forks + // which should be on by default. + if block.header.number() == 1 { + if let Some(name) = self.engine.params().nonzero_bugfix_hard_fork() { + warn!("Your chain specification contains one or more hard forks which are required to be \ on by default. Please remove these forks and start your chain again: {}.", name); - return; - } - } - - match self.engine.seals_internally() { - Some(true) => { - trace!(target: "miner", "update_sealing: engine indicates internal sealing"); - if self.seal_and_import_block_internally(chain, block) { - trace!(target: "miner", "update_sealing: imported internally sealed block"); - } - }, - Some(false) => { - trace!(target: "miner", "update_sealing: engine is not keen to seal internally right now"); - // anyway, save the block for later use - self.sealing.lock().queue.set_pending(block); - }, - None => { - trace!(target: "miner", "update_sealing: engine does not seal internally, preparing work"); - self.prepare_work(block, original_work_hash) - }, - } - } - - fn is_currently_sealing(&self) -> bool { - self.sealing.lock().enabled - } - - fn work_package(&self, chain: &C) -> Option<(H256, BlockNumber, u64, U256)> where - C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync, - { - if self.engine.seals_internally().is_some() { - return None; - } - - self.prepare_pending_block(chain); - - self.sealing.lock().queue.use_last_ref().map(|b| { - let header = &b.header; - (header.hash(), header.number(), header.timestamp(), *header.difficulty()) - }) - } - - // Note used for external submission (PoW) and internally by sealing engines. - fn submit_seal(&self, block_hash: H256, seal: Vec) -> Result { - let result = - if let Some(b) = self.sealing.lock().queue.get_used_if( - if self.options.enable_resubmission { - GetAction::Clone - } else { - GetAction::Take - }, - |b| &b.header.bare_hash() == &block_hash - ) { - trace!(target: "miner", "Submitted block {}={} with seal {:?}", block_hash, b.header.bare_hash(), seal); - b.lock().try_seal(&*self.engine, seal).or_else(|e| { - warn!(target: "miner", "Mined solution rejected: {}", e); - Err(ErrorKind::PowInvalid.into()) - }) - } else { - warn!(target: "miner", "Submitted solution rejected: Block unknown or out of date."); - Err(ErrorKind::PowHashInvalid.into()) - }; - - result.and_then(|sealed| { + return; + } + } + + match self.engine.seals_internally() { + Some(true) => { + trace!(target: "miner", "update_sealing: engine indicates internal sealing"); + if self.seal_and_import_block_internally(chain, block) { + trace!(target: "miner", "update_sealing: imported internally sealed block"); + } + return; + } + Some(false) => { + trace!(target: "miner", "update_sealing: engine is not keen to seal internally right now"); + // anyway, save the block for later use + self.sealing.lock().queue.set_pending(block); + } + None => { + trace!(target: "miner", "update_sealing: engine does not seal internally, preparing work"); + self.prepare_work(block, original_work_hash); + } + }; + } + + fn is_currently_sealing(&self) -> bool { + self.sealing.lock().enabled + } + + fn work_package(&self, chain: &C) -> Option<(H256, BlockNumber, u64, U256)> + where + C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync, + { + if self.engine.seals_internally().is_some() { + return None; + } + + self.prepare_pending_block(chain); + + self.sealing.lock().queue.use_last_ref().map(|b| { + let header = &b.header; + ( + header.hash(), + header.number(), + header.timestamp(), + *header.difficulty(), + ) + }) + } + + // Note used for external submission (PoW) and internally by sealing engines. + fn submit_seal(&self, block_hash: H256, seal: Vec) -> Result { + let result = if let Some(b) = self.sealing.lock().queue.get_used_if( + if self.options.enable_resubmission { + GetAction::Clone + } else { + GetAction::Take + }, + |b| &b.header.bare_hash() == &block_hash, + ) { + trace!(target: "miner", "Submitted block {}={} with seal {:?}", block_hash, b.header.bare_hash(), seal); + b.lock().try_seal(&*self.engine, seal).or_else(|e| { + warn!(target: "miner", "Mined solution rejected: {}", e); + Err(ErrorKind::PowInvalid.into()) + }) + } else { + warn!(target: "miner", "Submitted solution rejected: Block unknown or out of date."); + Err(ErrorKind::PowHashInvalid.into()) + }; + + result.and_then(|sealed| { let n = sealed.header.number(); let h = sealed.header.hash(); info!(target: "miner", "Submitted block imported OK. #{}: {}", Colour::White.bold().paint(format!("{}", n)), Colour::White.bold().paint(format!("{:x}", h))); Ok(sealed) }) - } - - fn chain_new_blocks(&self, chain: &C, imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256], is_internal_import: bool) - where C: miner::BlockChainClient, - { - trace!(target: "miner", "chain_new_blocks"); - - // 1. We ignore blocks that were `imported` unless resealing on new uncles is enabled. - // 2. We ignore blocks that are `invalid` because it doesn't have any meaning in terms of the transactions that - // are in those blocks - - let has_new_best_block = enacted.len() > 0; - - if has_new_best_block { - // Clear nonce cache - self.nonce_cache.clear(); - } - - // First update gas limit in transaction queue and minimal gas price. - let gas_limit = *chain.best_block_header().gas_limit(); - self.update_transaction_queue_limits(gas_limit); - - // Then import all transactions from retracted blocks. - let client = self.pool_client(chain); - { - retracted + } + + fn chain_new_blocks( + &self, + chain: &C, + imported: &[H256], + _invalid: &[H256], + enacted: &[H256], + retracted: &[H256], + is_internal_import: bool, + ) where + C: miner::BlockChainClient, + { + trace!(target: "miner", "chain_new_blocks"); + + // 1. We ignore blocks that were `imported` unless resealing on new uncles is enabled. + // 2. We ignore blocks that are `invalid` because it doesn't have any meaning in terms of the transactions that + // are in those blocks + + let has_new_best_block = enacted.len() > 0; + + if has_new_best_block { + // Clear nonce cache + self.nonce_cache.clear(); + } + + // First update gas limit in transaction queue and minimal gas price. + let gas_limit = *chain.best_block_header().gas_limit(); + self.update_transaction_queue_limits(gas_limit); + + // Then import all transactions from retracted blocks. + let client = self.pool_client(chain); + { + retracted .par_iter() .for_each(|hash| { let block = chain.block(BlockId::Hash(*hash)) @@ -1266,506 +1384,645 @@ impl miner::MinerService for Miner { txs, ); }); - } - - if has_new_best_block || (imported.len() > 0 && self.options.reseal_on_uncle) { - // Reset `next_allowed_reseal` in case a block is imported. - // Even if min_period is high, we will always attempt to create - // new pending block. - self.sealing.lock().next_allowed_reseal = Instant::now(); - - if !is_internal_import { - // -------------------------------------------------------------------------- - // | NOTE Code below requires sealing locks. | - // | Make sure to release the locks before calling that method. | - // -------------------------------------------------------------------------- - self.update_sealing(chain); - } - } - - if has_new_best_block { - // Make sure to cull transactions after we update sealing. - // Not culling won't lead to old transactions being added to the block - // (thanks to Ready), but culling can take significant amount of time, - // so best to leave it after we create some work for miners to prevent increased - // uncle rate. - // If the io_channel is available attempt to offload culling to a separate task - // to avoid blocking chain_new_blocks - if let Some(ref channel) = *self.io_channel.read() { - let queue = self.transaction_queue.clone(); - let nonce_cache = self.nonce_cache.clone(); - let engine = self.engine.clone(); - let accounts = self.accounts.clone(); - let service_transaction_checker = self.service_transaction_checker.clone(); - - let cull = move |chain: &::client::Client| { - let client = PoolClient::new( - chain, - &nonce_cache, - &*engine, - &*accounts, - service_transaction_checker.as_ref(), - ); - queue.cull(client); - }; - - if let Err(e) = channel.send(ClientIoMessage::execute(cull)) { - warn!(target: "miner", "Error queueing cull: {:?}", e); - } - } else { - self.transaction_queue.cull(client); - } - } - if let Some(ref service_transaction_checker) = self.service_transaction_checker { - match service_transaction_checker.refresh_cache(chain) { - Ok(true) => { - trace!(target: "client", "Service transaction cache was refreshed successfully"); - }, - Ok(false) => { - trace!(target: "client", "Registrar or/and service transactions contract does not exist"); - }, - Err(e) => error!(target: "client", "Error occurred while refreshing service transaction cache: {}", e) - }; - }; - } - - fn pending_state(&self, latest_block_number: BlockNumber) -> Option { - self.map_existing_pending_block(|b| b.state.clone(), latest_block_number) - } - - fn pending_block_header(&self, latest_block_number: BlockNumber) -> Option
{ - self.map_existing_pending_block(|b| b.header.clone(), latest_block_number) - } - - fn pending_block(&self, latest_block_number: BlockNumber) -> Option { - self.map_existing_pending_block(|b| { - Block { - header: b.header.clone(), - transactions: b.transactions.iter().cloned().map(Into::into).collect(), - uncles: b.uncles.to_vec(), - } - }, latest_block_number) - } - - fn pending_transactions(&self, latest_block_number: BlockNumber) -> Option> { - self.map_existing_pending_block(|b| b.transactions.iter().cloned().collect(), latest_block_number) - } + } + + if has_new_best_block || (imported.len() > 0 && self.options.reseal_on_uncle) { + // Reset `next_allowed_reseal` in case a block is imported. + // Even if min_period is high, we will always attempt to create + // new pending block. + self.sealing.lock().next_allowed_reseal = Instant::now(); + + if !is_internal_import { + // -------------------------------------------------------------------------- + // | NOTE Code below requires sealing locks. | + // | Make sure to release the locks before calling that method. | + // -------------------------------------------------------------------------- + self.update_sealing(chain, ForceUpdateSealing::No); + } + } + + if has_new_best_block { + // Make sure to cull transactions after we update sealing. + // Not culling won't lead to old transactions being added to the block + // (thanks to Ready), but culling can take significant amount of time, + // so best to leave it after we create some work for miners to prevent increased + // uncle rate. + // If the io_channel is available attempt to offload culling to a separate task + // to avoid blocking chain_new_blocks + if let Some(ref channel) = *self.io_channel.read() { + let queue = self.transaction_queue.clone(); + let nonce_cache = self.nonce_cache.clone(); + let engine = self.engine.clone(); + let accounts = self.accounts.clone(); + let service_transaction_checker = self.service_transaction_checker.clone(); + + let cull = move |chain: &::client::Client| { + let client = PoolClient::new( + chain, + &nonce_cache, + &*engine, + &*accounts, + service_transaction_checker.as_ref(), + ); + queue.cull(client); + if engine.should_reseal_on_update() { + // force update_sealing here to skip `reseal_required` checks + chain.update_sealing(ForceUpdateSealing::Yes); + } + }; + + if let Err(e) = channel.send(ClientIoMessage::execute(cull)) { + warn!(target: "miner", "Error queueing cull: {:?}", e); + } + } else { + self.transaction_queue.cull(client); + if self.engine.should_reseal_on_update() { + // force update_sealing here to skip `reseal_required` checks + self.update_sealing(chain, ForceUpdateSealing::Yes); + } + } + } + if let Some(ref service_transaction_checker) = self.service_transaction_checker { + match service_transaction_checker.refresh_cache(chain) { + Ok(true) => { + trace!(target: "client", "Service transaction cache was refreshed successfully"); + } + Ok(false) => { + trace!(target: "client", "Registrar or/and service transactions contract does not exist"); + } + Err(e) => { + error!(target: "client", "Error occurred while refreshing service transaction cache: {}", e) + } + }; + }; + } + + fn pending_state(&self, latest_block_number: BlockNumber) -> Option { + self.map_existing_pending_block(|b| b.state.clone(), latest_block_number) + } + + fn pending_block_header(&self, latest_block_number: BlockNumber) -> Option
{ + self.map_existing_pending_block(|b| b.header.clone(), latest_block_number) + } + + fn pending_block(&self, latest_block_number: BlockNumber) -> Option { + self.map_existing_pending_block( + |b| Block { + header: b.header.clone(), + transactions: b.transactions.iter().cloned().map(Into::into).collect(), + uncles: b.uncles.to_vec(), + }, + latest_block_number, + ) + } + + fn pending_transactions( + &self, + latest_block_number: BlockNumber, + ) -> Option> { + self.map_existing_pending_block( + |b| b.transactions.iter().cloned().collect(), + latest_block_number, + ) + } } #[cfg(test)] mod tests { - use std::iter::FromIterator; - - use super::*; - use accounts::AccountProvider; - use ethkey::{Generator, Random}; - use hash::keccak; - use rustc_hex::FromHex; - use types::BlockNumber; - - use client::{TestBlockChainClient, EachBlockWith, ChainInfo, ImportSealedBlock}; - use miner::{MinerService, PendingOrdering}; - use test_helpers::{generate_dummy_client, generate_dummy_client_with_spec}; - use types::transaction::{Transaction}; - - #[test] - fn should_prepare_block_to_seal() { - // given - let client = TestBlockChainClient::default(); - let miner = Miner::new_for_tests(&Spec::new_test(), None); - - // when - let sealing_work = miner.work_package(&client); - assert!(sealing_work.is_some(), "Expected closed block"); - } - - #[test] - fn should_still_work_after_a_couple_of_blocks() { - // given - let client = TestBlockChainClient::default(); - let miner = Miner::new_for_tests(&Spec::new_test(), None); - - let res = miner.work_package(&client); - let hash = res.unwrap().0; - let block = miner.submit_seal(hash, vec![]).unwrap(); - client.import_sealed_block(block).unwrap(); - - // two more blocks mined, work requested. - client.add_blocks(1, EachBlockWith::Uncle); - miner.work_package(&client); - - client.add_blocks(1, EachBlockWith::Uncle); - miner.work_package(&client); - - // solution to original work submitted. - assert!(miner.submit_seal(hash, vec![]).is_ok()); - } - - fn miner() -> Miner { - Miner::new( - MinerOptions { - force_sealing: false, - reseal_on_external_tx: false, - reseal_on_own_tx: true, - reseal_on_uncle: false, - reseal_min_period: Duration::from_secs(5), - reseal_max_period: Duration::from_secs(120), - pending_set: PendingSet::AlwaysSealing, - work_queue_size: 5, - enable_resubmission: true, - infinite_pending_block: false, - tx_queue_penalization: Penalization::Disabled, - tx_queue_strategy: PrioritizationStrategy::GasPriceOnly, - tx_queue_no_unfamiliar_locals: false, - refuse_service_transactions: false, - pool_limits: Default::default(), - pool_verification_options: pool::verifier::Options { - minimal_gas_price: 0.into(), - block_gas_limit: U256::max_value(), - tx_gas_limit: U256::max_value(), - no_early_reject: false, - }, - }, - GasPricer::new_fixed(0u64.into()), - &Spec::new_test(), - ::std::collections::HashSet::new(), // local accounts - ) - } - - const TEST_CHAIN_ID: u64 = 2; - - fn transaction() -> SignedTransaction { - transaction_with_chain_id(TEST_CHAIN_ID) - } - - fn transaction_with_chain_id(chain_id: u64) -> SignedTransaction { - let keypair = Random.generate().unwrap(); - Transaction { - action: Action::Create, - value: U256::zero(), - data: "3331600055".from_hex().unwrap(), - gas: U256::from(100_000), - gas_price: U256::zero(), - nonce: U256::zero(), - }.sign(keypair.secret(), Some(chain_id)) - } - - #[test] - fn should_make_pending_block_when_importing_own_transaction() { - // given - let client = TestBlockChainClient::default(); - let miner = miner(); - let transaction = transaction(); - let best_block = 0; - // when - let res = miner.import_own_transaction(&client, PendingTransaction::new(transaction, None)); - - // then - assert_eq!(res.unwrap(), ()); - assert_eq!(miner.pending_transactions(best_block).unwrap().len(), 1); - assert_eq!(miner.pending_receipts(best_block).unwrap().len(), 1); - assert_eq!(miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 1); - // This method will let us know if pending block was created (before calling that method) - assert_eq!(miner.prepare_pending_block(&client), BlockPreparationStatus::NotPrepared); - } - - #[test] - fn should_not_return_stale_work_packages() { - // given - let client = TestBlockChainClient::default(); - let miner = miner(); - - // initial work package should create the pending block - let res = miner.work_package(&client); - assert_eq!(res.unwrap().1, 1); - // This should be true, since there were some requests. - assert_eq!(miner.requires_reseal(0), true); - - // when new block is imported - let client = generate_dummy_client(2); - let imported = [0.into()]; - let empty = &[]; - miner.chain_new_blocks(&*client, &imported, empty, &imported, empty, false); - - // then - // This should be false, because it's too early. - assert_eq!(miner.requires_reseal(2), false); - // but still work package should be ready - let res = miner.work_package(&*client); - assert_eq!(res.unwrap().1, 3); - assert_eq!(miner.prepare_pending_block(&*client), BlockPreparationStatus::NotPrepared); - } - - #[test] - fn should_not_use_pending_block_if_best_block_is_higher() { - // given - let client = TestBlockChainClient::default(); - let miner = miner(); - let transaction = transaction(); - let best_block = 10; - // when - let res = miner.import_own_transaction(&client, PendingTransaction::new(transaction, None)); - - // then - assert_eq!(res.unwrap(), ()); - assert_eq!(miner.pending_transactions(best_block), None); - assert_eq!(miner.pending_receipts(best_block), None); - assert_eq!(miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 1); - } - - #[test] - fn should_import_external_transaction() { - // given - let client = TestBlockChainClient::default(); - let miner = miner(); - let transaction = transaction().into(); - let best_block = 0; - // when - let res = miner.import_external_transactions(&client, vec![transaction]).pop().unwrap(); - - // then - assert_eq!(res.unwrap(), ()); - // By default we don't reseal on external transactions - assert_eq!(miner.pending_transactions(best_block), None); - assert_eq!(miner.pending_receipts(best_block), None); - // By default we use PendingSet::AlwaysSealing, so no transactions yet. - assert_eq!(miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 0); - // This method will let us know if pending block was created (before calling that method) - assert_eq!(miner.prepare_pending_block(&client), BlockPreparationStatus::Succeeded); - // After pending block is created we should see a transaction. - assert_eq!(miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 1); - } - - #[test] - fn should_treat_unfamiliar_locals_selectively() { - // given - let keypair = Random.generate().unwrap(); - let client = TestBlockChainClient::default(); - let mut local_accounts = ::std::collections::HashSet::new(); - local_accounts.insert(keypair.address()); - - let miner = Miner::new( - MinerOptions { - tx_queue_no_unfamiliar_locals: true, - ..miner().options - }, - GasPricer::new_fixed(0u64.into()), - &Spec::new_test(), - local_accounts, - ); - let transaction = transaction(); - let best_block = 0; - // when - // This transaction should not be marked as local because our account_provider doesn't have the sender - let res = miner.import_claimed_local_transaction(&client, PendingTransaction::new(transaction.clone(), None), false); - - // then - // Check the same conditions as `should_import_external_transaction` first. Behaviour should be identical. - // That is: it's treated as though we added it through `import_external_transactions` - assert_eq!(res.unwrap(), ()); - assert_eq!(miner.pending_transactions(best_block), None); - assert_eq!(miner.pending_receipts(best_block), None); - assert_eq!(miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 0); - assert_eq!(miner.prepare_pending_block(&client), BlockPreparationStatus::Succeeded); - assert_eq!(miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 1); - - // when - 2nd part: create a local transaction from account_provider. - // Borrow the transaction used before & sign with our generated keypair. - let local_transaction = transaction.deconstruct().0.as_unsigned().clone().sign(keypair.secret(), Some(TEST_CHAIN_ID)); - let res2 = miner.import_claimed_local_transaction(&client, PendingTransaction::new(local_transaction, None), false); - - // then - 2nd part: we add on the results from the last pending block. - // This is borrowed from `should_make_pending_block_when_importing_own_transaction` and slightly modified. - assert_eq!(res2.unwrap(), ()); - assert_eq!(miner.pending_transactions(best_block).unwrap().len(), 2); - assert_eq!(miner.pending_receipts(best_block).unwrap().len(), 2); - assert_eq!(miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 2); - assert_eq!(miner.prepare_pending_block(&client), BlockPreparationStatus::NotPrepared); - } - - #[test] - fn should_prioritize_locals() { - let client = TestBlockChainClient::default(); - let transaction = transaction(); - let miner = Miner::new( - MinerOptions { - tx_queue_no_unfamiliar_locals: true, // should work even with this enabled - ..miner().options - }, - GasPricer::new_fixed(0u64.into()), - &Spec::new_test(), - HashSet::from_iter(vec![transaction.sender()].into_iter()), - ); - let best_block = 0; - - // Miner with sender as a known local address should prioritize transactions from that address - let res2 = miner.import_claimed_local_transaction(&client, PendingTransaction::new(transaction, None), false); - - // check to make sure the prioritized transaction is pending - assert_eq!(res2.unwrap(), ()); - assert_eq!(miner.pending_transactions(best_block).unwrap().len(), 1); - assert_eq!(miner.pending_receipts(best_block).unwrap().len(), 1); - assert_eq!(miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 1); - assert_eq!(miner.prepare_pending_block(&client), BlockPreparationStatus::NotPrepared); - } - - #[test] - fn should_not_seal_unless_enabled() { - let miner = miner(); - let client = TestBlockChainClient::default(); - // By default resealing is not required. - assert!(!miner.requires_reseal(1u8.into())); - - miner.import_external_transactions(&client, vec![transaction().into()]).pop().unwrap().unwrap(); - assert_eq!(miner.prepare_pending_block(&client), BlockPreparationStatus::Succeeded); - // Unless asked to prepare work. - assert!(miner.requires_reseal(1u8.into())); - } - - #[test] - fn internal_seals_without_work() { - let spec = Spec::new_instant(); - let miner = Miner::new_for_tests(&spec, None); - - let client = generate_dummy_client(2); - - let import = miner.import_external_transactions(&*client, vec![transaction_with_chain_id(spec.chain_id()).into()]).pop().unwrap(); - assert_eq!(import.unwrap(), ()); - - miner.update_sealing(&*client); - client.flush_queue(); - assert!(miner.pending_block(0).is_none()); - assert_eq!(client.chain_info().best_block_number, 3 as BlockNumber); - - assert!(miner.import_own_transaction(&*client, PendingTransaction::new(transaction_with_chain_id(spec.chain_id()).into(), None)).is_ok()); - - miner.update_sealing(&*client); - client.flush_queue(); - assert!(miner.pending_block(0).is_none()); - assert_eq!(client.chain_info().best_block_number, 4 as BlockNumber); - } - - #[test] - fn should_not_fail_setting_engine_signer_without_account_provider() { - let spec = Spec::new_test_round; - let tap = Arc::new(AccountProvider::transient_provider()); - let addr = tap.insert_account(keccak("1").into(), &"".into()).unwrap(); - let client = generate_dummy_client_with_spec(spec); - let engine_signer = Box::new((tap.clone(), addr, "".into())); - let msg = Default::default(); - assert!(client.engine().sign(msg).is_err()); - - // should set engine signer and miner author - client.miner().set_author(Author::Sealer(engine_signer)); - assert_eq!(client.miner().authoring_params().author, addr); - assert!(client.engine().sign(msg).is_ok()); - } - - #[test] - fn should_mine_if_internal_sealing_is_enabled() { - let spec = Spec::new_instant(); - let miner = Miner::new_for_tests(&spec, None); - - let client = generate_dummy_client(2); - miner.update_sealing(&*client); - - assert!(miner.is_currently_sealing()); - } - - #[test] - fn should_not_mine_if_internal_sealing_is_disabled() { - let spec = Spec::new_test_round(); - let miner = Miner::new_for_tests(&spec, None); - - let client = generate_dummy_client(2); - miner.update_sealing(&*client); - - assert!(!miner.is_currently_sealing()); - } - - #[test] - fn should_not_mine_if_no_fetch_work_request() { - let spec = Spec::new_test(); - let miner = Miner::new_for_tests(&spec, None); - - let client = generate_dummy_client(2); - miner.update_sealing(&*client); - - assert!(!miner.is_currently_sealing()); - } - - #[cfg(feature = "work-notify")] - #[test] - fn should_mine_if_fetch_work_request() { - struct DummyNotifyWork; - - impl NotifyWork for DummyNotifyWork { - fn notify(&self, _pow_hash: H256, _difficulty: U256, _number: u64) { } - } - - let spec = Spec::new_test(); - let miner = Miner::new_for_tests(&spec, None); - miner.add_work_listener(Box::new(DummyNotifyWork)); - - let client = generate_dummy_client(2); - miner.update_sealing(&*client); - - assert!(miner.is_currently_sealing()); - } - - #[test] - fn should_set_new_minimum_gas_price() { - // Creates a new GasPricer::Fixed behind the scenes - let miner = Miner::new_for_tests(&Spec::new_test(), None); - - let expected_minimum_gas_price: U256 = 0x1337.into(); - miner.set_minimal_gas_price(expected_minimum_gas_price).unwrap(); - - let txq_options = miner.transaction_queue.status().options; - let current_minimum_gas_price = txq_options.minimal_gas_price; - - assert!(current_minimum_gas_price == expected_minimum_gas_price); - } - - #[cfg(feature = "price-info")] - fn dynamic_gas_pricer() -> GasPricer { - use std::time::Duration; - use parity_runtime::Executor; - use fetch::Client as FetchClient; - use ethcore_miner::gas_price_calibrator::{GasPriceCalibrator, GasPriceCalibratorOptions}; - - // Don't really care about any of these settings since - // the gas pricer is never actually going to be used - let fetch = FetchClient::new(1).unwrap(); - let p = Executor::new_sync(); - - GasPricer::new_calibrated( - GasPriceCalibrator::new( - GasPriceCalibratorOptions { - usd_per_tx: 0.0, - recalibration_period: Duration::from_secs(0), - }, - fetch, - p, - ) - ) - } - - #[test] - #[cfg(feature = "price-info")] - fn should_fail_to_set_new_minimum_gas_price() { - // We get a fixed gas pricer by default, need to change that - let miner = Miner::new_for_tests(&Spec::new_test(), None); - let calibrated_gas_pricer = dynamic_gas_pricer(); - *miner.gas_pricer.lock() = calibrated_gas_pricer; - - let expected_minimum_gas_price: U256 = 0x1337.into(); - let result = miner.set_minimal_gas_price(expected_minimum_gas_price); - assert!(result.is_err()); - - let received_error_msg = result.unwrap_err(); - let expected_error_msg = "Can't update fixed gas price while automatic gas calibration is enabled."; - - assert!(received_error_msg == expected_error_msg); - } + use std::iter::FromIterator; + + use super::*; + use accounts::AccountProvider; + use ethkey::{Generator, Random}; + use hash::keccak; + use rustc_hex::FromHex; + use types::BlockNumber; + + use client::{ChainInfo, EachBlockWith, ImportSealedBlock, TestBlockChainClient}; + use miner::{MinerService, PendingOrdering}; + use test_helpers::{generate_dummy_client, generate_dummy_client_with_spec}; + use types::transaction::Transaction; + + #[test] + fn should_prepare_block_to_seal() { + // given + let client = TestBlockChainClient::default(); + let miner = Miner::new_for_tests(&Spec::new_test(), None); + + // when + let sealing_work = miner.work_package(&client); + assert!(sealing_work.is_some(), "Expected closed block"); + } + + #[test] + fn should_still_work_after_a_couple_of_blocks() { + // given + let client = TestBlockChainClient::default(); + let miner = Miner::new_for_tests(&Spec::new_test(), None); + + let res = miner.work_package(&client); + let hash = res.unwrap().0; + let block = miner.submit_seal(hash, vec![]).unwrap(); + client.import_sealed_block(block).unwrap(); + + // two more blocks mined, work requested. + client.add_blocks(1, EachBlockWith::Uncle); + miner.work_package(&client); + + client.add_blocks(1, EachBlockWith::Uncle); + miner.work_package(&client); + + // solution to original work submitted. + assert!(miner.submit_seal(hash, vec![]).is_ok()); + } + + fn miner() -> Miner { + Miner::new( + MinerOptions { + force_sealing: false, + reseal_on_external_tx: false, + reseal_on_own_tx: true, + reseal_on_uncle: false, + reseal_min_period: Duration::from_secs(5), + reseal_max_period: Duration::from_secs(120), + pending_set: PendingSet::AlwaysSealing, + work_queue_size: 5, + enable_resubmission: true, + infinite_pending_block: false, + tx_queue_penalization: Penalization::Disabled, + tx_queue_strategy: PrioritizationStrategy::GasPriceOnly, + tx_queue_no_unfamiliar_locals: false, + refuse_service_transactions: false, + pool_limits: Default::default(), + pool_verification_options: pool::verifier::Options { + minimal_gas_price: 0.into(), + block_gas_limit: U256::max_value(), + tx_gas_limit: U256::max_value(), + no_early_reject: false, + }, + }, + GasPricer::new_fixed(0u64.into()), + &Spec::new_test(), + ::std::collections::HashSet::new(), // local accounts + ) + } + + const TEST_CHAIN_ID: u64 = 2; + + fn transaction() -> SignedTransaction { + transaction_with_chain_id(TEST_CHAIN_ID) + } + + fn transaction_with_chain_id(chain_id: u64) -> SignedTransaction { + let keypair = Random.generate().unwrap(); + Transaction { + action: Action::Create, + value: U256::zero(), + data: "3331600055".from_hex().unwrap(), + gas: U256::from(100_000), + gas_price: U256::zero(), + nonce: U256::zero(), + } + .sign(keypair.secret(), Some(chain_id)) + } + + #[test] + fn should_make_pending_block_when_importing_own_transaction() { + // given + let client = TestBlockChainClient::default(); + let miner = miner(); + let transaction = transaction(); + let best_block = 0; + // when + let res = miner.import_own_transaction(&client, PendingTransaction::new(transaction, None)); + + // then + assert_eq!(res.unwrap(), ()); + assert_eq!(miner.pending_transactions(best_block).unwrap().len(), 1); + assert_eq!(miner.pending_receipts(best_block).unwrap().len(), 1); + assert_eq!( + miner + .ready_transactions(&client, 10, PendingOrdering::Priority) + .len(), + 1 + ); + // This method will let us know if pending block was created (before calling that method) + assert_eq!( + miner.prepare_pending_block(&client), + BlockPreparationStatus::NotPrepared + ); + } + + #[test] + fn should_not_return_stale_work_packages() { + // given + let client = TestBlockChainClient::default(); + let miner = miner(); + + // initial work package should create the pending block + let res = miner.work_package(&client); + assert_eq!(res.unwrap().1, 1); + // This should be true, since there were some requests. + assert_eq!(miner.requires_reseal(0), true); + + // when new block is imported + let client = generate_dummy_client(2); + let imported = [0.into()]; + let empty = &[]; + miner.chain_new_blocks(&*client, &imported, empty, &imported, empty, false); + + // then + // This should be false, because it's too early. + assert_eq!(miner.requires_reseal(2), false); + // but still work package should be ready + let res = miner.work_package(&*client); + assert_eq!(res.unwrap().1, 3); + assert_eq!( + miner.prepare_pending_block(&*client), + BlockPreparationStatus::NotPrepared + ); + } + + #[test] + fn should_not_use_pending_block_if_best_block_is_higher() { + // given + let client = TestBlockChainClient::default(); + let miner = miner(); + let transaction = transaction(); + let best_block = 10; + // when + let res = miner.import_own_transaction(&client, PendingTransaction::new(transaction, None)); + + // then + assert_eq!(res.unwrap(), ()); + assert_eq!(miner.pending_transactions(best_block), None); + assert_eq!(miner.pending_receipts(best_block), None); + assert_eq!( + miner + .ready_transactions(&client, 10, PendingOrdering::Priority) + .len(), + 1 + ); + } + + #[test] + fn should_import_external_transaction() { + // given + let client = TestBlockChainClient::default(); + let miner = miner(); + let transaction = transaction().into(); + let best_block = 0; + // when + let res = miner + .import_external_transactions(&client, vec![transaction]) + .pop() + .unwrap(); + + // then + assert_eq!(res.unwrap(), ()); + // By default we don't reseal on external transactions + assert_eq!(miner.pending_transactions(best_block), None); + assert_eq!(miner.pending_receipts(best_block), None); + // By default we use PendingSet::AlwaysSealing, so no transactions yet. + assert_eq!( + miner + .ready_transactions(&client, 10, PendingOrdering::Priority) + .len(), + 0 + ); + // This method will let us know if pending block was created (before calling that method) + assert_eq!( + miner.prepare_pending_block(&client), + BlockPreparationStatus::Succeeded + ); + // After pending block is created we should see a transaction. + assert_eq!( + miner + .ready_transactions(&client, 10, PendingOrdering::Priority) + .len(), + 1 + ); + } + + #[test] + fn should_treat_unfamiliar_locals_selectively() { + // given + let keypair = Random.generate().unwrap(); + let client = TestBlockChainClient::default(); + let mut local_accounts = ::std::collections::HashSet::new(); + local_accounts.insert(keypair.address()); + + let miner = Miner::new( + MinerOptions { + tx_queue_no_unfamiliar_locals: true, + ..miner().options + }, + GasPricer::new_fixed(0u64.into()), + &Spec::new_test(), + local_accounts, + ); + let transaction = transaction(); + let best_block = 0; + // when + // This transaction should not be marked as local because our account_provider doesn't have the sender + let res = miner.import_claimed_local_transaction( + &client, + PendingTransaction::new(transaction.clone(), None), + false, + ); + + // then + // Check the same conditions as `should_import_external_transaction` first. Behaviour should be identical. + // That is: it's treated as though we added it through `import_external_transactions` + assert_eq!(res.unwrap(), ()); + assert_eq!(miner.pending_transactions(best_block), None); + assert_eq!(miner.pending_receipts(best_block), None); + assert_eq!( + miner + .ready_transactions(&client, 10, PendingOrdering::Priority) + .len(), + 0 + ); + assert_eq!( + miner.prepare_pending_block(&client), + BlockPreparationStatus::Succeeded + ); + assert_eq!( + miner + .ready_transactions(&client, 10, PendingOrdering::Priority) + .len(), + 1 + ); + + // when - 2nd part: create a local transaction from account_provider. + // Borrow the transaction used before & sign with our generated keypair. + let local_transaction = transaction + .deconstruct() + .0 + .as_unsigned() + .clone() + .sign(keypair.secret(), Some(TEST_CHAIN_ID)); + let res2 = miner.import_claimed_local_transaction( + &client, + PendingTransaction::new(local_transaction, None), + false, + ); + + // then - 2nd part: we add on the results from the last pending block. + // This is borrowed from `should_make_pending_block_when_importing_own_transaction` and slightly modified. + assert_eq!(res2.unwrap(), ()); + assert_eq!(miner.pending_transactions(best_block).unwrap().len(), 2); + assert_eq!(miner.pending_receipts(best_block).unwrap().len(), 2); + assert_eq!( + miner + .ready_transactions(&client, 10, PendingOrdering::Priority) + .len(), + 2 + ); + assert_eq!( + miner.prepare_pending_block(&client), + BlockPreparationStatus::NotPrepared + ); + } + + #[test] + fn should_reject_local_transaction_with_invalid_chain_id() { + let spec = Spec::new_test(); + let miner = Miner::new_for_tests(&spec, None); + let client = TestBlockChainClient::default(); + let chain_id = spec.chain_id(); + + // chain_id + 100500 is invalid + let import = miner.import_claimed_local_transaction( + &client, + PendingTransaction::new(transaction_with_chain_id(chain_id + 10500), None), + false, + ); + assert_eq!(import, Err(transaction::Error::InvalidChainId)); + + // chain_id is valid + let import = miner.import_claimed_local_transaction( + &client, + PendingTransaction::new(transaction_with_chain_id(chain_id), None), + false, + ); + assert_eq!(import, Ok(())); + } + + #[test] + fn should_prioritize_locals() { + let client = TestBlockChainClient::default(); + let transaction = transaction(); + let miner = Miner::new( + MinerOptions { + tx_queue_no_unfamiliar_locals: true, // should work even with this enabled + ..miner().options + }, + GasPricer::new_fixed(0u64.into()), + &Spec::new_test(), + HashSet::from_iter(vec![transaction.sender()].into_iter()), + ); + let best_block = 0; + + // Miner with sender as a known local address should prioritize transactions from that address + let res2 = miner.import_claimed_local_transaction( + &client, + PendingTransaction::new(transaction, None), + false, + ); + + // check to make sure the prioritized transaction is pending + assert_eq!(res2.unwrap(), ()); + assert_eq!(miner.pending_transactions(best_block).unwrap().len(), 1); + assert_eq!(miner.pending_receipts(best_block).unwrap().len(), 1); + assert_eq!( + miner + .ready_transactions(&client, 10, PendingOrdering::Priority) + .len(), + 1 + ); + assert_eq!( + miner.prepare_pending_block(&client), + BlockPreparationStatus::NotPrepared + ); + } + + #[test] + fn should_not_seal_unless_enabled() { + let miner = miner(); + let client = TestBlockChainClient::default(); + // By default resealing is not required. + assert!(!miner.requires_reseal(1u8.into())); + + miner + .import_external_transactions(&client, vec![transaction().into()]) + .pop() + .unwrap() + .unwrap(); + assert_eq!( + miner.prepare_pending_block(&client), + BlockPreparationStatus::Succeeded + ); + // Unless asked to prepare work. + assert!(miner.requires_reseal(1u8.into())); + } + + #[test] + fn internal_seals_without_work() { + let spec = Spec::new_instant(); + let miner = Miner::new_for_tests(&spec, None); + + let client = generate_dummy_client(2); + + let import = miner + .import_external_transactions( + &*client, + vec![transaction_with_chain_id(spec.chain_id()).into()], + ) + .pop() + .unwrap(); + assert_eq!(import.unwrap(), ()); + + miner.update_sealing(&*client, ForceUpdateSealing::No); + client.flush_queue(); + assert!(miner.pending_block(0).is_none()); + assert_eq!(client.chain_info().best_block_number, 3 as BlockNumber); + + assert!(miner + .import_own_transaction( + &*client, + PendingTransaction::new(transaction_with_chain_id(spec.chain_id()).into(), None) + ) + .is_ok()); + + miner.update_sealing(&*client, ForceUpdateSealing::No); + client.flush_queue(); + assert!(miner.pending_block(0).is_none()); + assert_eq!(client.chain_info().best_block_number, 4 as BlockNumber); + } + + #[test] + fn should_not_fail_setting_engine_signer_without_account_provider() { + let spec = Spec::new_test_round; + let tap = Arc::new(AccountProvider::transient_provider()); + let addr = tap.insert_account(keccak("1").into(), &"".into()).unwrap(); + let client = generate_dummy_client_with_spec(spec); + let engine_signer = Box::new((tap.clone(), addr, "".into())); + let msg = Default::default(); + assert!(client.engine().sign(msg).is_err()); + + // should set engine signer and miner author + client.miner().set_author(Author::Sealer(engine_signer)); + assert_eq!(client.miner().authoring_params().author, addr); + assert!(client.engine().sign(msg).is_ok()); + } + + #[test] + fn should_mine_if_internal_sealing_is_enabled() { + let spec = Spec::new_instant(); + let miner = Miner::new_for_tests(&spec, None); + + let client = generate_dummy_client(2); + miner.update_sealing(&*client, ForceUpdateSealing::No); + + assert!(miner.is_currently_sealing()); + } + + #[test] + fn should_not_mine_if_internal_sealing_is_disabled() { + let spec = Spec::new_test_round(); + let miner = Miner::new_for_tests(&spec, None); + + let client = generate_dummy_client(2); + miner.update_sealing(&*client, ForceUpdateSealing::No); + + assert!(!miner.is_currently_sealing()); + } + + #[test] + fn should_not_mine_if_no_fetch_work_request() { + let spec = Spec::new_test(); + let miner = Miner::new_for_tests(&spec, None); + + let client = generate_dummy_client(2); + miner.update_sealing(&*client, ForceUpdateSealing::No); + + assert!(!miner.is_currently_sealing()); + } + + #[cfg(feature = "work-notify")] + #[test] + fn should_mine_if_fetch_work_request() { + struct DummyNotifyWork; + + impl NotifyWork for DummyNotifyWork { + fn notify(&self, _pow_hash: H256, _difficulty: U256, _number: u64) {} + } + + let spec = Spec::new_test(); + let miner = Miner::new_for_tests(&spec, None); + miner.add_work_listener(Box::new(DummyNotifyWork)); + + let client = generate_dummy_client(2); + miner.update_sealing(&*client, ForceUpdateSealing::No); + + assert!(miner.is_currently_sealing()); + } + + #[test] + fn should_set_new_minimum_gas_price() { + // Creates a new GasPricer::Fixed behind the scenes + let miner = Miner::new_for_tests(&Spec::new_test(), None); + + let expected_minimum_gas_price: U256 = 0x1337.into(); + miner + .set_minimal_gas_price(expected_minimum_gas_price) + .unwrap(); + + let txq_options = miner.transaction_queue.status().options; + let current_minimum_gas_price = txq_options.minimal_gas_price; + + assert!(current_minimum_gas_price == expected_minimum_gas_price); + } + + #[cfg(feature = "price-info")] + fn dynamic_gas_pricer() -> GasPricer { + use ethcore_miner::gas_price_calibrator::{GasPriceCalibrator, GasPriceCalibratorOptions}; + use fetch::Client as FetchClient; + use parity_runtime::Executor; + + // Don't really care about any of these settings since + // the gas pricer is never actually going to be used + let fetch = FetchClient::new(1).unwrap(); + let p = Executor::new_sync(); + + GasPricer::new_calibrated(GasPriceCalibrator::new( + GasPriceCalibratorOptions { + usd_per_tx: 0.0, + recalibration_period: Duration::from_secs(0), + }, + fetch, + p, + "fake_endpoint".to_owned(), + )) + } + + #[test] + #[cfg(feature = "price-info")] + fn should_fail_to_set_new_minimum_gas_price() { + // We get a fixed gas pricer by default, need to change that + let miner = Miner::new_for_tests(&Spec::new_test(), None); + let calibrated_gas_pricer = dynamic_gas_pricer(); + *miner.gas_pricer.lock() = calibrated_gas_pricer; + + let expected_minimum_gas_price: U256 = 0x1337.into(); + let result = miner.set_minimal_gas_price(expected_minimum_gas_price); + assert!(result.is_err()); + + let received_error_msg = result.unwrap_err(); + let expected_error_msg = + "Can't update fixed gas price while automatic gas calibration is enabled."; + + assert!(received_error_msg == expected_error_msg); + } } diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index fd7ab96513c..c7eb0d056fe 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . #![warn(missing_docs)] @@ -25,28 +25,30 @@ pub mod pool_client; #[cfg(feature = "stratum")] pub mod stratum; -pub use self::miner::{Miner, MinerOptions, Penalization, PendingSet, AuthoringParams, Author}; -pub use ethcore_miner::local_accounts::LocalAccounts; -pub use ethcore_miner::pool::PendingOrdering; +pub use self::miner::{Author, AuthoringParams, Miner, MinerOptions, Penalization, PendingSet}; +pub use ethcore_miner::{local_accounts::LocalAccounts, pool::PendingOrdering}; -use std::sync::Arc; -use std::collections::{BTreeSet, BTreeMap}; +use std::{ + collections::{BTreeMap, BTreeSet}, + sync::Arc, +}; use bytes::Bytes; -use ethcore_miner::pool::{VerifiedTransaction, QueueStatus, local_transactions}; -use ethereum_types::{H256, U256, Address}; -use types::transaction::{self, UnverifiedTransaction, SignedTransaction, PendingTransaction}; -use types::BlockNumber; -use types::block::Block; -use types::header::Header; -use types::receipt::RichReceipt; +use ethcore_miner::pool::{local_transactions, QueueStatus, VerifiedTransaction}; +use ethereum_types::{Address, H256, U256}; +use types::{ + block::Block, + header::Header, + receipt::RichReceipt, + transaction::{self, PendingTransaction, SignedTransaction, UnverifiedTransaction}, + BlockNumber, +}; use block::SealedBlock; use call_contract::{CallContract, RegistryInfo}; use client::{ - ScheduleInfo, - BlockChain, BlockProducer, SealedBlockImporter, ChainInfo, - AccountData, Nonce, + traits::ForceUpdateSealing, AccountData, BlockChain, BlockProducer, ChainInfo, Nonce, + ScheduleInfo, SealedBlockImporter, }; use error::Error; use state::StateInfo; @@ -60,153 +62,190 @@ pub trait TransactionVerifierClient: Send + Sync {} /// Extended client interface used for mining -pub trait BlockChainClient: TransactionVerifierClient + BlockProducer + SealedBlockImporter {} +pub trait BlockChainClient: + TransactionVerifierClient + BlockProducer + SealedBlockImporter +{ +} /// Miner client API -pub trait MinerService : Send + Sync { - /// Type representing chain state - type State: StateInfo + 'static; - - // Sealing - - /// Submit `seal` as a valid solution for the header of `pow_hash`. - /// Will check the seal, but not actually insert the block into the chain. - fn submit_seal(&self, pow_hash: H256, seal: Vec) -> Result; - - /// Is it currently sealing? - fn is_currently_sealing(&self) -> bool; - - /// Get the sealing work package preparing it if doesn't exist yet. - /// - /// Returns `None` if engine seals internally. - fn work_package(&self, chain: &C) -> Option<(H256, BlockNumber, u64, U256)> - where C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync; - - /// Update current pending block - fn update_sealing(&self, chain: &C) - where C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync; - - // Notifications - - /// Called when blocks are imported to chain, updates transactions queue. - /// `is_internal_import` indicates that the block has just been created in miner and internally sealed by the engine, - /// so we shouldn't attempt creating new block again. - fn chain_new_blocks(&self, chain: &C, imported: &[H256], invalid: &[H256], enacted: &[H256], retracted: &[H256], is_internal_import: bool) - where C: BlockChainClient; - - // Pending block - - /// Get a list of all pending receipts from pending block. - fn pending_receipts(&self, best_block: BlockNumber) -> Option>; - - /// Get a particular receipt from pending block. - fn pending_receipt(&self, best_block: BlockNumber, hash: &H256) -> Option { - let receipts = self.pending_receipts(best_block)?; - receipts.into_iter().find(|r| &r.transaction_hash == hash) - } - - /// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing. - fn pending_state(&self, latest_block_number: BlockNumber) -> Option; - - /// Get `Some` `clone()` of the current pending block header or `None` if we're not sealing. - fn pending_block_header(&self, latest_block_number: BlockNumber) -> Option
; - - /// Get `Some` `clone()` of the current pending block or `None` if we're not sealing. - fn pending_block(&self, latest_block_number: BlockNumber) -> Option; - - /// Get `Some` `clone()` of the current pending block transactions or `None` if we're not sealing. - fn pending_transactions(&self, latest_block_number: BlockNumber) -> Option>; - - // Block authoring - - /// Get current authoring parameters. - fn authoring_params(&self) -> AuthoringParams; - - /// Set the lower and upper bound of gas limit we wish to target when sealing a new block. - fn set_gas_range_target(&self, gas_range_target: (U256, U256)); - - /// Set the extra_data that we will seal blocks with. - fn set_extra_data(&self, extra_data: Bytes); - - /// Set info necessary to sign consensus messages and block authoring. - /// - /// On chains where sealing is done externally (e.g. PoW) we provide only reward beneficiary. - fn set_author(&self, author: Author); - - // Transaction Pool - - /// Imports transactions to transaction queue. - fn import_external_transactions(&self, client: &C, transactions: Vec) - -> Vec> - where C: BlockChainClient; - - /// Imports own (node owner) transaction to queue. - fn import_own_transaction(&self, chain: &C, transaction: PendingTransaction) - -> Result<(), transaction::Error> - where C: BlockChainClient; - - /// Imports transactions from potentially external sources, with behaviour determined - /// by the config flag `tx_queue_allow_unfamiliar_locals` - fn import_claimed_local_transaction(&self, chain: &C, transaction: PendingTransaction, trusted: bool) - -> Result<(), transaction::Error> - where C: BlockChainClient; - - /// Removes transaction from the pool. - /// - /// Attempts to "cancel" a transaction. If it was not propagated yet (or not accepted by other peers) - /// there is a good chance that the transaction will actually be removed. - /// NOTE: The transaction is not removed from pending block if there is one. - fn remove_transaction(&self, hash: &H256) -> Option>; - - /// Query transaction from the pool given it's hash. - fn transaction(&self, hash: &H256) -> Option>; - - /// Returns next valid nonce for given address. - /// - /// This includes nonces of all transactions from this address in the pending queue - /// if they are consecutive. - /// NOTE: pool may contain some future transactions that will become pending after - /// transaction with nonce returned from this function is signed on. - fn next_nonce(&self, chain: &C, address: &Address) -> U256 - where C: Nonce + Sync; - - /// Get a set of all pending transaction hashes. - /// - /// Depending on the settings may look in transaction pool or only in pending block. - fn pending_transaction_hashes(&self, chain: &C) -> BTreeSet where - C: ChainInfo + Sync; - - /// Get a list of all ready transactions either ordered by priority or unordered (cheaper). - /// - /// Depending on the settings may look in transaction pool or only in pending block. - /// If you don't need a full set of transactions, you can add `max_len` and create only a limited set of - /// transactions. - fn ready_transactions(&self, chain: &C, max_len: usize, ordering: PendingOrdering) -> Vec> - where C: ChainInfo + Nonce + Sync; - - /// Get a list of all transactions in the pool (some of them might not be ready for inclusion yet). - fn queued_transactions(&self) -> Vec>; - - /// Get a list of all transaction hashes in the pool (some of them might not be ready for inclusion yet). - fn queued_transaction_hashes(&self) -> Vec; - - /// Get a list of local transactions with statuses. - fn local_transactions(&self) -> BTreeMap; - - /// Get current queue status. - /// - /// Status includes verification thresholds and current pool utilization and limits. - fn queue_status(&self) -> QueueStatus; - - // Misc - - /// Suggested gas price. - fn sensible_gas_price(&self) -> U256; - - /// Suggested gas limit. - fn sensible_gas_limit(&self) -> U256; - - /// Set a new minimum gas limit. - /// Will not work if dynamic gas calibration is set. - fn set_minimal_gas_price(&self, gas_price: U256) -> Result; +pub trait MinerService: Send + Sync { + /// Type representing chain state + type State: StateInfo + 'static; + + // Sealing + + /// Submit `seal` as a valid solution for the header of `pow_hash`. + /// Will check the seal, but not actually insert the block into the chain. + fn submit_seal(&self, pow_hash: H256, seal: Vec) -> Result; + + /// Is it currently sealing? + fn is_currently_sealing(&self) -> bool; + + /// Get the sealing work package preparing it if doesn't exist yet. + /// + /// Returns `None` if engine seals internally. + fn work_package(&self, chain: &C) -> Option<(H256, BlockNumber, u64, U256)> + where + C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync; + + /// Update current pending block + fn update_sealing(&self, chain: &C, force: ForceUpdateSealing) + where + C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync; + + // Notifications + + /// Called when blocks are imported to chain, updates transactions queue. + /// `is_internal_import` indicates that the block has just been created in miner and internally sealed by the engine, + /// so we shouldn't attempt creating new block again. + fn chain_new_blocks( + &self, + chain: &C, + imported: &[H256], + invalid: &[H256], + enacted: &[H256], + retracted: &[H256], + is_internal_import: bool, + ) where + C: BlockChainClient; + + // Pending block + + /// Get a list of all pending receipts from pending block. + fn pending_receipts(&self, best_block: BlockNumber) -> Option>; + + /// Get a particular receipt from pending block. + fn pending_receipt(&self, best_block: BlockNumber, hash: &H256) -> Option { + let receipts = self.pending_receipts(best_block)?; + receipts.into_iter().find(|r| &r.transaction_hash == hash) + } + + /// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing. + fn pending_state(&self, latest_block_number: BlockNumber) -> Option; + + /// Get `Some` `clone()` of the current pending block header or `None` if we're not sealing. + fn pending_block_header(&self, latest_block_number: BlockNumber) -> Option
; + + /// Get `Some` `clone()` of the current pending block or `None` if we're not sealing. + fn pending_block(&self, latest_block_number: BlockNumber) -> Option; + + /// Get `Some` `clone()` of the current pending block transactions or `None` if we're not sealing. + fn pending_transactions( + &self, + latest_block_number: BlockNumber, + ) -> Option>; + + // Block authoring + + /// Get current authoring parameters. + fn authoring_params(&self) -> AuthoringParams; + + /// Set the lower and upper bound of gas limit we wish to target when sealing a new block. + fn set_gas_range_target(&self, gas_range_target: (U256, U256)); + + /// Set the extra_data that we will seal blocks with. + fn set_extra_data(&self, extra_data: Bytes); + + /// Set info necessary to sign consensus messages and block authoring. + /// + /// On chains where sealing is done externally (e.g. PoW) we provide only reward beneficiary. + fn set_author(&self, author: Author); + + // Transaction Pool + + /// Imports transactions to transaction queue. + fn import_external_transactions( + &self, + client: &C, + transactions: Vec, + ) -> Vec> + where + C: BlockChainClient; + + /// Imports own (node owner) transaction to queue. + fn import_own_transaction( + &self, + chain: &C, + transaction: PendingTransaction, + ) -> Result<(), transaction::Error> + where + C: BlockChainClient; + + /// Imports transactions from potentially external sources, with behaviour determined + /// by the config flag `tx_queue_allow_unfamiliar_locals` + fn import_claimed_local_transaction( + &self, + chain: &C, + transaction: PendingTransaction, + trusted: bool, + ) -> Result<(), transaction::Error> + where + C: BlockChainClient; + + /// Removes transaction from the pool. + /// + /// Attempts to "cancel" a transaction. If it was not propagated yet (or not accepted by other peers) + /// there is a good chance that the transaction will actually be removed. + /// NOTE: The transaction is not removed from pending block if there is one. + fn remove_transaction(&self, hash: &H256) -> Option>; + + /// Query transaction from the pool given it's hash. + fn transaction(&self, hash: &H256) -> Option>; + + /// Returns next valid nonce for given address. + /// + /// This includes nonces of all transactions from this address in the pending queue + /// if they are consecutive. + /// NOTE: pool may contain some future transactions that will become pending after + /// transaction with nonce returned from this function is signed on. + fn next_nonce(&self, chain: &C, address: &Address) -> U256 + where + C: Nonce + Sync; + + /// Get a set of all pending transaction hashes. + /// + /// Depending on the settings may look in transaction pool or only in pending block. + fn pending_transaction_hashes(&self, chain: &C) -> BTreeSet + where + C: ChainInfo + Sync; + + /// Get a list of all ready transactions either ordered by priority or unordered (cheaper). + /// + /// Depending on the settings may look in transaction pool or only in pending block. + /// If you don't need a full set of transactions, you can add `max_len` and create only a limited set of + /// transactions. + fn ready_transactions( + &self, + chain: &C, + max_len: usize, + ordering: PendingOrdering, + ) -> Vec> + where + C: ChainInfo + Nonce + Sync; + + /// Get a list of all transactions in the pool (some of them might not be ready for inclusion yet). + fn queued_transactions(&self) -> Vec>; + + /// Get a list of all transaction hashes in the pool (some of them might not be ready for inclusion yet). + fn queued_transaction_hashes(&self) -> Vec; + + /// Get a list of local transactions with statuses. + fn local_transactions(&self) -> BTreeMap; + + /// Get current queue status. + /// + /// Status includes verification thresholds and current pool utilization and limits. + fn queue_status(&self) -> QueueStatus; + + // Misc + + /// Suggested gas price. + fn sensible_gas_price(&self) -> U256; + + /// Suggested gas limit. + fn sensible_gas_limit(&self) -> U256; + + /// Set a new minimum gas limit. + /// Will not work if dynamic gas calibration is set. + fn set_minimal_gas_price(&self, gas_price: U256) -> Result; } diff --git a/ethcore/src/miner/pool_client.rs b/ethcore/src/miner/pool_client.rs index 60e93dee8ac..b3eb6d229f7 100644 --- a/ethcore/src/miner/pool_client.rs +++ b/ethcore/src/miner/pool_client.rs @@ -1,42 +1,36 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Blockchain access for transaction pool. -use std::{ - collections::HashMap, - fmt, - sync::Arc, -}; +use std::{collections::HashMap, fmt, sync::Arc}; -use ethereum_types::{H256, U256, Address}; -use ethcore_miner::local_accounts::LocalAccounts; -use ethcore_miner::pool; -use ethcore_miner::pool::client::NonceClient; -use ethcore_miner::service_transaction_checker::ServiceTransactionChecker; -use types::transaction::{ - self, - UnverifiedTransaction, - SignedTransaction, +use ethcore_miner::{ + local_accounts::LocalAccounts, pool, pool::client::NonceClient, + service_transaction_checker::ServiceTransactionChecker, }; -use types::header::Header; +use ethereum_types::{Address, H256, U256}; use parking_lot::RwLock; +use types::{ + header::Header, + transaction::{self, SignedTransaction, UnverifiedTransaction}, +}; use call_contract::CallContract; -use client::{TransactionId, BlockInfo, Nonce}; +use client::{BlockInfo, Nonce, TransactionId}; use engines::EthEngine; use miner; use transaction_ext::Transaction; @@ -44,203 +38,233 @@ use transaction_ext::Transaction; /// Cache for state nonces. #[derive(Debug, Clone)] pub struct NonceCache { - nonces: Arc>>, - limit: usize + nonces: Arc>>, + limit: usize, } impl NonceCache { - /// Create new cache with a limit of `limit` entries. - pub fn new(limit: usize) -> Self { - NonceCache { - nonces: Arc::new(RwLock::new(HashMap::with_capacity(limit / 2))), - limit, - } - } - - /// Retrieve a cached nonce for given sender. - pub fn get(&self, sender: &Address) -> Option { - self.nonces.read().get(sender).cloned() - } - - /// Clear all entries from the cache. - pub fn clear(&self) { - self.nonces.write().clear(); - } + /// Create new cache with a limit of `limit` entries. + pub fn new(limit: usize) -> Self { + NonceCache { + nonces: Arc::new(RwLock::new(HashMap::with_capacity(limit / 2))), + limit, + } + } + + /// Retrieve a cached nonce for given sender. + pub fn get(&self, sender: &Address) -> Option { + self.nonces.read().get(sender).cloned() + } + + /// Clear all entries from the cache. + pub fn clear(&self) { + self.nonces.write().clear(); + } } /// Blockchain accesss for transaction pool. pub struct PoolClient<'a, C: 'a> { - chain: &'a C, - cached_nonces: CachedNonceClient<'a, C>, - engine: &'a EthEngine, - accounts: &'a LocalAccounts, - best_block_header: Header, - service_transaction_checker: Option<&'a ServiceTransactionChecker>, + chain: &'a C, + cached_nonces: CachedNonceClient<'a, C>, + engine: &'a dyn EthEngine, + accounts: &'a dyn LocalAccounts, + best_block_header: Header, + service_transaction_checker: Option<&'a ServiceTransactionChecker>, } impl<'a, C: 'a> Clone for PoolClient<'a, C> { - fn clone(&self) -> Self { - PoolClient { - chain: self.chain, - cached_nonces: self.cached_nonces.clone(), - engine: self.engine, - accounts: self.accounts.clone(), - best_block_header: self.best_block_header.clone(), - service_transaction_checker: self.service_transaction_checker.clone(), - } - } + fn clone(&self) -> Self { + PoolClient { + chain: self.chain, + cached_nonces: self.cached_nonces.clone(), + engine: self.engine, + accounts: self.accounts.clone(), + best_block_header: self.best_block_header.clone(), + service_transaction_checker: self.service_transaction_checker.clone(), + } + } } -impl<'a, C: 'a> PoolClient<'a, C> where - C: BlockInfo + CallContract, +impl<'a, C: 'a> PoolClient<'a, C> +where + C: BlockInfo + CallContract, { - /// Creates new client given chain, nonce cache, accounts and service transaction verifier. - pub fn new( - chain: &'a C, - cache: &'a NonceCache, - engine: &'a EthEngine, - accounts: &'a LocalAccounts, - service_transaction_checker: Option<&'a ServiceTransactionChecker>, - ) -> Self { - let best_block_header = chain.best_block_header(); - PoolClient { - chain, - cached_nonces: CachedNonceClient::new(chain, cache), - engine, - accounts, - best_block_header, - service_transaction_checker, - } - } - - /// Verifies if signed transaction is executable. - /// - /// This should perform any verifications that rely on chain status. - pub fn verify_signed(&self, tx: &SignedTransaction) -> Result<(), transaction::Error> { - self.engine.machine().verify_transaction(&tx, &self.best_block_header, self.chain) - } + /// Creates new client given chain, nonce cache, accounts and service transaction verifier. + pub fn new( + chain: &'a C, + cache: &'a NonceCache, + engine: &'a dyn EthEngine, + accounts: &'a dyn LocalAccounts, + service_transaction_checker: Option<&'a ServiceTransactionChecker>, + ) -> Self { + let best_block_header = chain.best_block_header(); + PoolClient { + chain, + cached_nonces: CachedNonceClient::new(chain, cache), + engine, + accounts, + best_block_header, + service_transaction_checker, + } + } + + /// Verifies transaction against its block (before its import into this block) + /// Also Verifies if signed transaction is executable. + /// + /// This should perform any verifications that rely on chain status. + pub fn verify_for_pending_block( + &self, + tx: &SignedTransaction, + header: &Header, + ) -> Result<(), transaction::Error> { + self.engine.machine().verify_transaction_basic(tx, header)?; + self.engine + .machine() + .verify_transaction(tx, &self.best_block_header, self.chain) + } } impl<'a, C: 'a> fmt::Debug for PoolClient<'a, C> { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "PoolClient") - } + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "PoolClient") + } } -impl<'a, C: 'a> pool::client::Client for PoolClient<'a, C> where - C: miner::TransactionVerifierClient + Sync, +impl<'a, C: 'a> pool::client::Client for PoolClient<'a, C> +where + C: miner::TransactionVerifierClient + Sync, { - fn transaction_already_included(&self, hash: &H256) -> bool { - self.chain.transaction_block(TransactionId::Hash(*hash)).is_some() - } - - fn verify_transaction(&self, tx: UnverifiedTransaction)-> Result { - self.engine.verify_transaction_basic(&tx, &self.best_block_header)?; - let tx = self.engine.verify_transaction_unordered(tx, &self.best_block_header)?; - - self.verify_signed(&tx)?; - - Ok(tx) - } - - fn account_details(&self, address: &Address) -> pool::client::AccountDetails { - pool::client::AccountDetails { - nonce: self.cached_nonces.account_nonce(address), - balance: self.chain.latest_balance(address), - is_local: self.accounts.is_local(address), - } - } - - fn required_gas(&self, tx: &transaction::Transaction) -> U256 { - tx.gas_required(&self.chain.latest_schedule()).into() - } - - fn transaction_type(&self, tx: &SignedTransaction) -> pool::client::TransactionType { - match self.service_transaction_checker { - None => pool::client::TransactionType::Regular, - Some(ref checker) => match checker.check(self.chain, &tx) { - Ok(true) => pool::client::TransactionType::Service, - Ok(false) => pool::client::TransactionType::Regular, - Err(e) => { - debug!(target: "txqueue", "Unable to verify service transaction: {:?}", e); - pool::client::TransactionType::Regular - }, - } - } - } - - fn decode_transaction(&self, transaction: &[u8]) -> Result { - self.engine.decode_transaction(transaction) - } + fn transaction_already_included(&self, hash: &H256) -> bool { + self.chain + .transaction_block(TransactionId::Hash(*hash)) + .is_some() + } + + fn verify_transaction_basic( + &self, + tx: &UnverifiedTransaction, + ) -> Result<(), transaction::Error> { + self.engine + .verify_transaction_basic(tx, &self.best_block_header)?; + Ok(()) + } + + fn verify_transaction( + &self, + tx: UnverifiedTransaction, + ) -> Result { + self.engine + .verify_transaction_basic(&tx, &self.best_block_header)?; + let tx = self + .engine + .verify_transaction_unordered(tx, &self.best_block_header)?; + + self.engine + .machine() + .verify_transaction(&tx, &self.best_block_header, self.chain)?; + Ok(tx) + } + + fn account_details(&self, address: &Address) -> pool::client::AccountDetails { + pool::client::AccountDetails { + nonce: self.cached_nonces.account_nonce(address), + balance: self.chain.latest_balance(address), + is_local: self.accounts.is_local(address), + } + } + + fn required_gas(&self, tx: &transaction::Transaction) -> U256 { + tx.gas_required(&self.chain.latest_schedule()).into() + } + + fn transaction_type(&self, tx: &SignedTransaction) -> pool::client::TransactionType { + match self.service_transaction_checker { + None => pool::client::TransactionType::Regular, + Some(ref checker) => match checker.check(self.chain, &tx) { + Ok(true) => pool::client::TransactionType::Service, + Ok(false) => pool::client::TransactionType::Regular, + Err(e) => { + debug!(target: "txqueue", "Unable to verify service transaction: {:?}", e); + pool::client::TransactionType::Regular + } + }, + } + } + + fn decode_transaction( + &self, + transaction: &[u8], + ) -> Result { + self.engine.decode_transaction(transaction) + } } -impl<'a, C: 'a> NonceClient for PoolClient<'a, C> where - C: Nonce + Sync, +impl<'a, C: 'a> NonceClient for PoolClient<'a, C> +where + C: Nonce + Sync, { - fn account_nonce(&self, address: &Address) -> U256 { - self.cached_nonces.account_nonce(address) - } + fn account_nonce(&self, address: &Address) -> U256 { + self.cached_nonces.account_nonce(address) + } } pub(crate) struct CachedNonceClient<'a, C: 'a> { - client: &'a C, - cache: &'a NonceCache, + client: &'a C, + cache: &'a NonceCache, } impl<'a, C: 'a> Clone for CachedNonceClient<'a, C> { - fn clone(&self) -> Self { - CachedNonceClient { - client: self.client, - cache: self.cache, - } - } + fn clone(&self) -> Self { + CachedNonceClient { + client: self.client, + cache: self.cache, + } + } } impl<'a, C: 'a> fmt::Debug for CachedNonceClient<'a, C> { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("CachedNonceClient") - .field("cache", &self.cache.nonces.read().len()) - .field("limit", &self.cache.limit) - .finish() - } + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("CachedNonceClient") + .field("cache", &self.cache.nonces.read().len()) + .field("limit", &self.cache.limit) + .finish() + } } impl<'a, C: 'a> CachedNonceClient<'a, C> { - pub fn new(client: &'a C, cache: &'a NonceCache) -> Self { - CachedNonceClient { - client, - cache, - } - } + pub fn new(client: &'a C, cache: &'a NonceCache) -> Self { + CachedNonceClient { client, cache } + } } -impl<'a, C: 'a> NonceClient for CachedNonceClient<'a, C> where - C: Nonce + Sync, +impl<'a, C: 'a> NonceClient for CachedNonceClient<'a, C> +where + C: Nonce + Sync, { - fn account_nonce(&self, address: &Address) -> U256 { - if let Some(nonce) = self.cache.nonces.read().get(address) { - return *nonce; - } - - // We don't check again if cache has been populated. - // It's not THAT expensive to fetch the nonce from state. - let mut cache = self.cache.nonces.write(); - let nonce = self.client.latest_nonce(address); - cache.insert(*address, nonce); - - if cache.len() < self.cache.limit { - return nonce - } - - debug!(target: "txpool", "NonceCache: reached limit."); - trace_time!("nonce_cache:clear"); - - // Remove excessive amount of entries from the cache - let to_remove: Vec<_> = cache.keys().take(self.cache.limit / 2).cloned().collect(); - for x in to_remove { - cache.remove(&x); - } - - nonce - } + fn account_nonce(&self, address: &Address) -> U256 { + if let Some(nonce) = self.cache.nonces.read().get(address) { + return *nonce; + } + + // We don't check again if cache has been populated. + // It's not THAT expensive to fetch the nonce from state. + let mut cache = self.cache.nonces.write(); + let nonce = self.client.latest_nonce(address); + cache.insert(*address, nonce); + + if cache.len() < self.cache.limit { + return nonce; + } + + debug!(target: "txpool", "NonceCache: reached limit."); + trace_time!("nonce_cache:clear"); + + // Remove excessive amount of entries from the cache + let to_remove: Vec<_> = cache.keys().take(self.cache.limit / 2).cloned().collect(); + for x in to_remove { + cache.remove(&x); + } + + nonce + } } diff --git a/ethcore/src/miner/stratum.rs b/ethcore/src/miner/stratum.rs index 71862225ab5..c08c02ca1be 100644 --- a/ethcore/src/miner/stratum.rs +++ b/ethcore/src/miner/stratum.rs @@ -1,35 +1,35 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Client-side stratum job dispatcher and mining notifier handler -use std::sync::{Arc, Weak}; -use std::net::{SocketAddr, AddrParseError}; -use std::fmt; +use std::{ + fmt, + net::{AddrParseError, SocketAddr}, + sync::{Arc, Weak}, +}; use client::{Client, ImportSealedBlock}; -use ethereum_types::{H64, H256, clean_0x, U256}; use ethash::{self, SeedHashCompute}; #[cfg(feature = "work-notify")] use ethcore_miner::work_notify::NotifyWork; #[cfg(feature = "work-notify")] use ethcore_stratum::PushWorkHandler; -use ethcore_stratum::{ - JobDispatcher, Stratum as StratumService, Error as StratumServiceError, -}; +use ethcore_stratum::{Error as StratumServiceError, JobDispatcher, Stratum as StratumService}; +use ethereum_types::{clean_0x, H256, H64, U256}; use miner::{Miner, MinerService}; use parking_lot::Mutex; use rlp::encode; @@ -37,217 +37,233 @@ use rlp::encode; /// Configures stratum server options. #[derive(Debug, PartialEq, Clone)] pub struct Options { - /// Working directory - pub io_path: String, - /// Network address - pub listen_addr: String, - /// Port - pub port: u16, - /// Secret for peers - pub secret: Option, + /// Working directory + pub io_path: String, + /// Network address + pub listen_addr: String, + /// Port + pub port: u16, + /// Secret for peers + pub secret: Option, } struct SubmitPayload { - nonce: H64, - pow_hash: H256, - mix_hash: H256, + nonce: H64, + pow_hash: H256, + mix_hash: H256, } impl SubmitPayload { - fn from_args(payload: Vec) -> Result { - if payload.len() != 3 { - return Err(PayloadError::ArgumentsAmountUnexpected(payload.len())); - } - - let nonce = match clean_0x(&payload[0]).parse::() { - Ok(nonce) => nonce, - Err(e) => { - warn!(target: "stratum", "submit_work ({}): invalid nonce ({:?})", &payload[0], e); - return Err(PayloadError::InvalidNonce(payload[0].clone())) - } - }; - - let pow_hash = match clean_0x(&payload[1]).parse::() { - Ok(pow_hash) => pow_hash, - Err(e) => { - warn!(target: "stratum", "submit_work ({}): invalid hash ({:?})", &payload[1], e); - return Err(PayloadError::InvalidPowHash(payload[1].clone())); - } - }; - - let mix_hash = match clean_0x(&payload[2]).parse::() { - Ok(mix_hash) => mix_hash, - Err(e) => { - warn!(target: "stratum", "submit_work ({}): invalid mix-hash ({:?})", &payload[2], e); - return Err(PayloadError::InvalidMixHash(payload[2].clone())); - } - }; - - Ok(SubmitPayload { - nonce: nonce, - pow_hash: pow_hash, - mix_hash: mix_hash, - }) - } + fn from_args(payload: Vec) -> Result { + if payload.len() != 3 { + return Err(PayloadError::ArgumentsAmountUnexpected(payload.len())); + } + + let nonce = match clean_0x(&payload[0]).parse::() { + Ok(nonce) => nonce, + Err(e) => { + warn!(target: "stratum", "submit_work ({}): invalid nonce ({:?})", &payload[0], e); + return Err(PayloadError::InvalidNonce(payload[0].clone())); + } + }; + + let pow_hash = match clean_0x(&payload[1]).parse::() { + Ok(pow_hash) => pow_hash, + Err(e) => { + warn!(target: "stratum", "submit_work ({}): invalid hash ({:?})", &payload[1], e); + return Err(PayloadError::InvalidPowHash(payload[1].clone())); + } + }; + + let mix_hash = match clean_0x(&payload[2]).parse::() { + Ok(mix_hash) => mix_hash, + Err(e) => { + warn!(target: "stratum", "submit_work ({}): invalid mix-hash ({:?})", &payload[2], e); + return Err(PayloadError::InvalidMixHash(payload[2].clone())); + } + }; + + Ok(SubmitPayload { + nonce: nonce, + pow_hash: pow_hash, + mix_hash: mix_hash, + }) + } } #[derive(Debug)] enum PayloadError { - ArgumentsAmountUnexpected(usize), - InvalidNonce(String), - InvalidPowHash(String), - InvalidMixHash(String), + ArgumentsAmountUnexpected(usize), + InvalidNonce(String), + InvalidPowHash(String), + InvalidMixHash(String), } impl fmt::Display for PayloadError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&self, f) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self, f) + } } /// Job dispatcher for stratum service pub struct StratumJobDispatcher { - seed_compute: Mutex, - client: Weak, - miner: Weak, + seed_compute: Mutex, + client: Weak, + miner: Weak, } impl JobDispatcher for StratumJobDispatcher { - fn initial(&self) -> Option { - // initial payload may contain additional data, not in this case - self.job() - } - - fn job(&self) -> Option { - self.with_core(|client, miner| miner.work_package(&*client).map(|(pow_hash, number, _timestamp, difficulty)| { - self.payload(pow_hash, difficulty, number) - })) - } - - fn submit(&self, payload: Vec) -> Result<(), StratumServiceError> { - let payload = SubmitPayload::from_args(payload).map_err(|e| - StratumServiceError::Dispatch(e.to_string()) - )?; - - trace!( - target: "stratum", - "submit_work: Decoded: nonce={}, pow_hash={}, mix_hash={}", - payload.nonce, - payload.pow_hash, - payload.mix_hash, - ); - - self.with_core_result(|client, miner| { - let seal = vec![encode(&payload.mix_hash), encode(&payload.nonce)]; - - let import = miner.submit_seal(payload.pow_hash, seal) - .and_then(|block| client.import_sealed_block(block)); - match import { - Ok(_) => Ok(()), - Err(e) => { - warn!(target: "stratum", "submit_seal error: {:?}", e); - Err(StratumServiceError::Dispatch(e.to_string())) - } - } - }) - } + fn initial(&self) -> Option { + // initial payload may contain additional data, not in this case + self.job() + } + + fn job(&self) -> Option { + self.with_core(|client, miner| { + miner + .work_package(&*client) + .map(|(pow_hash, number, _timestamp, difficulty)| { + self.payload(pow_hash, difficulty, number) + }) + }) + } + + fn submit(&self, payload: Vec) -> Result<(), StratumServiceError> { + let payload = SubmitPayload::from_args(payload) + .map_err(|e| StratumServiceError::Dispatch(e.to_string()))?; + + trace!( + target: "stratum", + "submit_work: Decoded: nonce={}, pow_hash={}, mix_hash={}", + payload.nonce, + payload.pow_hash, + payload.mix_hash, + ); + + self.with_core_result(|client, miner| { + let seal = vec![encode(&payload.mix_hash), encode(&payload.nonce)]; + + let import = miner + .submit_seal(payload.pow_hash, seal) + .and_then(|block| client.import_sealed_block(block)); + match import { + Ok(_) => Ok(()), + Err(e) => { + warn!(target: "stratum", "submit_seal error: {:?}", e); + Err(StratumServiceError::Dispatch(e.to_string())) + } + } + }) + } } impl StratumJobDispatcher { - /// New stratum job dispatcher given the miner and client - fn new(miner: Weak, client: Weak) -> StratumJobDispatcher { - StratumJobDispatcher { - seed_compute: Mutex::new(SeedHashCompute::default()), - client: client, - miner: miner, - } - } - - /// Serializes payload for stratum service - fn payload(&self, pow_hash: H256, difficulty: U256, number: u64) -> String { - // TODO: move this to engine - let target = ethash::difficulty_to_boundary(&difficulty); - let seed_hash = &self.seed_compute.lock().hash_block_number(number); - let seed_hash = H256::from_slice(&seed_hash[..]); - format!( - r#"["0x", "0x{:x}","0x{:x}","0x{:x}","0x{:x}"]"#, - pow_hash, seed_hash, target, number - ) - } - - fn with_core(&self, f: F) -> Option where F: Fn(Arc, Arc) -> Option { - self.client.upgrade().and_then(|client| self.miner.upgrade().and_then(|miner| (f)(client, miner))) - } - - fn with_core_result(&self, f: F) -> Result<(), StratumServiceError> where F: Fn(Arc, Arc) -> Result<(), StratumServiceError> { - match (self.client.upgrade(), self.miner.upgrade()) { - (Some(client), Some(miner)) => f(client, miner), - _ => Ok(()), - } - } + /// New stratum job dispatcher given the miner and client + fn new(miner: Weak, client: Weak) -> StratumJobDispatcher { + StratumJobDispatcher { + seed_compute: Mutex::new(SeedHashCompute::default()), + client: client, + miner: miner, + } + } + + /// Serializes payload for stratum service + fn payload(&self, pow_hash: H256, difficulty: U256, number: u64) -> String { + // TODO: move this to engine + let target = ethash::difficulty_to_boundary(&difficulty); + let seed_hash = &self.seed_compute.lock().hash_block_number(number); + let seed_hash = H256::from_slice(&seed_hash[..]); + format!( + r#"["0x", "0x{:x}","0x{:x}","0x{:x}","0x{:x}"]"#, + pow_hash, seed_hash, target, number + ) + } + + fn with_core(&self, f: F) -> Option + where + F: Fn(Arc, Arc) -> Option, + { + self.client + .upgrade() + .and_then(|client| self.miner.upgrade().and_then(|miner| (f)(client, miner))) + } + + fn with_core_result(&self, f: F) -> Result<(), StratumServiceError> + where + F: Fn(Arc, Arc) -> Result<(), StratumServiceError>, + { + match (self.client.upgrade(), self.miner.upgrade()) { + (Some(client), Some(miner)) => f(client, miner), + _ => Ok(()), + } + } } /// Wrapper for dedicated stratum service pub struct Stratum { - dispatcher: Arc, - service: Arc, + dispatcher: Arc, + service: Arc, } #[derive(Debug)] /// Stratum error pub enum Error { - /// IPC sockets error - Service(StratumServiceError), - /// Invalid network address - Address(AddrParseError), + /// IPC sockets error + Service(StratumServiceError), + /// Invalid network address + Address(AddrParseError), } impl From for Error { - fn from(service_err: StratumServiceError) -> Error { Error::Service(service_err) } + fn from(service_err: StratumServiceError) -> Error { + Error::Service(service_err) + } } impl From for Error { - fn from(err: AddrParseError) -> Error { Error::Address(err) } + fn from(err: AddrParseError) -> Error { + Error::Address(err) + } } #[cfg(feature = "work-notify")] impl NotifyWork for Stratum { - fn notify(&self, pow_hash: H256, difficulty: U256, number: u64) { - trace!(target: "stratum", "Notify work"); - - self.service.push_work_all( - self.dispatcher.payload(pow_hash, difficulty, number) - ).unwrap_or_else( - |e| warn!(target: "stratum", "Error while pushing work: {:?}", e) - ); - } + fn notify(&self, pow_hash: H256, difficulty: U256, number: u64) { + trace!(target: "stratum", "Notify work"); + + self.service + .push_work_all(self.dispatcher.payload(pow_hash, difficulty, number)); + } } impl Stratum { - - /// New stratum job dispatcher, given the miner, client and dedicated stratum service - pub fn start(options: &Options, miner: Weak, client: Weak) -> Result { - use std::net::IpAddr; - - let dispatcher = Arc::new(StratumJobDispatcher::new(miner, client)); - - let stratum_svc = StratumService::start( - &SocketAddr::new(options.listen_addr.parse::()?, options.port), - dispatcher.clone(), - options.secret.clone(), - )?; - - Ok(Stratum { - dispatcher: dispatcher, - service: stratum_svc, - }) - } - - /// Start STRATUM job dispatcher and register it in the miner - #[cfg(feature = "work-notify")] - pub fn register(cfg: &Options, miner: Arc, client: Weak) -> Result<(), Error> { - let stratum = Stratum::start(cfg, Arc::downgrade(&miner.clone()), client)?; - miner.add_work_listener(Box::new(stratum) as Box); - Ok(()) - } + /// New stratum job dispatcher, given the miner, client and dedicated stratum service + pub fn start( + options: &Options, + miner: Weak, + client: Weak, + ) -> Result { + use std::net::IpAddr; + + let dispatcher = Arc::new(StratumJobDispatcher::new(miner, client)); + + let service = StratumService::start( + &SocketAddr::new(options.listen_addr.parse::()?, options.port), + dispatcher.clone(), + options.secret.clone(), + )?; + + Ok(Stratum { + dispatcher, + service, + }) + } + + /// Start STRATUM job dispatcher and register it in the miner + #[cfg(feature = "work-notify")] + pub fn register(cfg: &Options, miner: Arc, client: Weak) -> Result<(), Error> { + let stratum = Stratum::start(cfg, Arc::downgrade(&miner.clone()), client)?; + miner.add_work_listener(Box::new(stratum) as Box); + Ok(()) + } } diff --git a/ethcore/src/pod_account.rs b/ethcore/src/pod_account.rs index 39dce6e36da..ed7311e5a24 100644 --- a/ethcore/src/pod_account.rs +++ b/ethcore/src/pod_account.rs @@ -1,145 +1,174 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Account system expressed in Plain Old Data. -use std::fmt; -use std::collections::BTreeMap; -use itertools::Itertools; -use hash::{keccak}; +use bytes::Bytes; use ethereum_types::{H256, U256}; +use ethjson; +use ethtrie::RlpCodec; +use hash::keccak; use hash_db::HashDB; -use kvdb::DBValue; +use itertools::Itertools; use keccak_hasher::KeccakHasher; -use triehash::sec_trie_root; -use bytes::Bytes; -use trie::TrieFactory; -use ethtrie::RlpCodec; -use state::Account; -use ethjson; -use types::account_diff::*; +use kvdb::DBValue; use rlp::{self, RlpStream}; -use serde::Serializer; use rustc_hex::ToHex; +use serde::Serializer; +use state::Account; +use std::{collections::BTreeMap, fmt}; +use trie::TrieFactory; +use triehash::sec_trie_root; +use types::account_diff::*; #[derive(Debug, Clone, PartialEq, Eq, Serialize)] /// An account, expressed as Plain-Old-Data (hence the name). /// Does not have a DB overlay cache, code hash or anything like that. pub struct PodAccount { - /// The balance of the account. - pub balance: U256, - /// The nonce of the account. - pub nonce: U256, - #[serde(serialize_with="opt_bytes_to_hex")] - /// The code of the account or `None` in the special case that it is unknown. - pub code: Option, - /// The storage of the account. - pub storage: BTreeMap, + /// The balance of the account. + pub balance: U256, + /// The nonce of the account. + pub nonce: U256, + #[serde(serialize_with = "opt_bytes_to_hex")] + /// The code of the account or `None` in the special case that it is unknown. + pub code: Option, + /// The storage of the account. + pub storage: BTreeMap, } fn opt_bytes_to_hex(opt_bytes: &Option, serializer: S) -> Result - where S: Serializer +where + S: Serializer, { - serializer.collect_str(&format_args!("0x{}",opt_bytes.as_ref().map_or("".to_string(), |b|b.to_hex()))) + serializer.collect_str(&format_args!( + "0x{}", + opt_bytes.as_ref().map_or("".to_string(), |b| b.to_hex()) + )) } impl PodAccount { - /// Convert Account to a PodAccount. - /// NOTE: This will silently fail unless the account is fully cached. - pub fn from_account(acc: &Account) -> PodAccount { - PodAccount { - balance: *acc.balance(), - nonce: *acc.nonce(), - storage: acc.storage_changes().iter().fold(BTreeMap::new(), |mut m, (k, v)| {m.insert(k.clone(), v.clone()); m}), - code: acc.code().map(|x| x.to_vec()), - } - } + /// Convert Account to a PodAccount. + /// NOTE: This will silently fail unless the account is fully cached. + pub fn from_account(acc: &Account) -> PodAccount { + PodAccount { + balance: *acc.balance(), + nonce: *acc.nonce(), + storage: acc + .storage_changes() + .iter() + .fold(BTreeMap::new(), |mut m, (k, v)| { + m.insert(k.clone(), v.clone()); + m + }), + code: acc.code().map(|x| x.to_vec()), + } + } - /// Returns the RLP for this account. - pub fn rlp(&self) -> Bytes { - let mut stream = RlpStream::new_list(4); - stream.append(&self.nonce); - stream.append(&self.balance); - stream.append(&sec_trie_root(self.storage.iter().map(|(k, v)| (k, rlp::encode(&U256::from(&**v)))))); - stream.append(&keccak(&self.code.as_ref().unwrap_or(&vec![]))); - stream.out() - } + /// Returns the RLP for this account. + pub fn rlp(&self) -> Bytes { + let mut stream = RlpStream::new_list(4); + stream.append(&self.nonce); + stream.append(&self.balance); + stream.append(&sec_trie_root( + self.storage + .iter() + .map(|(k, v)| (k, rlp::encode(&U256::from(&**v)))), + )); + stream.append(&keccak(&self.code.as_ref().unwrap_or(&vec![]))); + stream.out() + } - /// Place additional data into given hash DB. - pub fn insert_additional(&self, db: &mut HashDB, factory: &TrieFactory) { - match self.code { - Some(ref c) if !c.is_empty() => { db.insert(c); } - _ => {} - } - let mut r = H256::new(); - let mut t = factory.create(db, &mut r); - for (k, v) in &self.storage { - if let Err(e) = t.insert(k, &rlp::encode(&U256::from(&**v))) { - warn!("Encountered potential DB corruption: {}", e); - } - } - } + /// Place additional data into given hash DB. + pub fn insert_additional( + &self, + db: &mut dyn HashDB, + factory: &TrieFactory, + ) { + match self.code { + Some(ref c) if !c.is_empty() => { + db.insert(c); + } + _ => {} + } + let mut r = H256::new(); + let mut t = factory.create(db, &mut r); + for (k, v) in &self.storage { + if let Err(e) = t.insert(k, &rlp::encode(&U256::from(&**v))) { + warn!("Encountered potential DB corruption: {}", e); + } + } + } } impl From for PodAccount { - fn from(a: ethjson::blockchain::Account) -> Self { - PodAccount { - balance: a.balance.into(), - nonce: a.nonce.into(), - code: Some(a.code.into()), - storage: a.storage.into_iter().map(|(key, value)| { - let key: U256 = key.into(); - let value: U256 = value.into(); - (H256::from(key), H256::from(value)) - }).collect(), - } - } + fn from(a: ethjson::blockchain::Account) -> Self { + PodAccount { + balance: a.balance.into(), + nonce: a.nonce.into(), + code: Some(a.code.into()), + storage: a + .storage + .into_iter() + .map(|(key, value)| { + let key: U256 = key.into(); + let value: U256 = value.into(); + (H256::from(key), H256::from(value)) + }) + .collect(), + } + } } impl From for PodAccount { - fn from(a: ethjson::spec::Account) -> Self { - PodAccount { - balance: a.balance.map_or_else(U256::zero, Into::into), - nonce: a.nonce.map_or_else(U256::zero, Into::into), - code: Some(a.code.map_or_else(Vec::new, Into::into)), - storage: a.storage.map_or_else(BTreeMap::new, |s| s.into_iter().map(|(key, value)| { - let key: U256 = key.into(); - let value: U256 = value.into(); - (H256::from(key), H256::from(value)) - }).collect()), - } - } + fn from(a: ethjson::spec::Account) -> Self { + PodAccount { + balance: a.balance.map_or_else(U256::zero, Into::into), + nonce: a.nonce.map_or_else(U256::zero, Into::into), + code: Some(a.code.map_or_else(Vec::new, Into::into)), + storage: a.storage.map_or_else(BTreeMap::new, |s| { + s.into_iter() + .map(|(key, value)| { + let key: U256 = key.into(); + let value: U256 = value.into(); + (H256::from(key), H256::from(value)) + }) + .collect() + }), + } + } } impl fmt::Display for PodAccount { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "(bal={}; nonce={}; code={} bytes, #{}; storage={} items)", - self.balance, - self.nonce, - self.code.as_ref().map_or(0, |c| c.len()), - self.code.as_ref().map_or_else(H256::new, |c| keccak(c)), - self.storage.len(), - ) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "(bal={}; nonce={}; code={} bytes, #{}; storage={} items)", + self.balance, + self.nonce, + self.code.as_ref().map_or(0, |c| c.len()), + self.code.as_ref().map_or_else(H256::new, |c| keccak(c)), + self.storage.len(), + ) + } } /// Determine difference between two optionally existant `Account`s. Returns None /// if they are the same. pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option { - match (pre, post) { + match (pre, post) { (None, Some(x)) => Some(AccountDiff { balance: Diff::Born(x.balance), nonce: Diff::Born(x.nonce), @@ -181,71 +210,108 @@ pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option 1, 2 => 2, 3 => 3, 4 => 4, 5 => 0, 6 => 0, 7 => 0] - }; - let b = PodAccount { - balance: 0.into(), - nonce: 0.into(), - code: Some(vec![]), - storage: map_into![1 => 1, 2 => 3, 3 => 0, 5 => 0, 7 => 7, 8 => 0, 9 => 9] - }; - assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff { - balance: Diff::Same, - nonce: Diff::Same, - code: Diff::Same, - storage: map![ - 2.into() => Diff::new(2.into(), 3.into()), - 3.into() => Diff::new(3.into(), 0.into()), - 4.into() => Diff::new(4.into(), 0.into()), - 7.into() => Diff::new(0.into(), 7.into()), - 9.into() => Diff::new(0.into(), 9.into()) - ], - })); - } + #[test] + fn code() { + let a = PodAccount { + balance: 0.into(), + nonce: 0.into(), + code: Some(vec![]), + storage: map![], + }; + let b = PodAccount { + balance: 0.into(), + nonce: 1.into(), + code: Some(vec![0]), + storage: map![], + }; + assert_eq!( + diff_pod(Some(&a), Some(&b)), + Some(AccountDiff { + balance: Diff::Same, + nonce: Diff::Changed(0.into(), 1.into()), + code: Diff::Changed(vec![], vec![0]), + storage: map![], + }) + ); + } + + #[test] + fn storage() { + let a = PodAccount { + balance: 0.into(), + nonce: 0.into(), + code: Some(vec![]), + storage: map_into![1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 0, 6 => 0, 7 => 0], + }; + let b = PodAccount { + balance: 0.into(), + nonce: 0.into(), + code: Some(vec![]), + storage: map_into![1 => 1, 2 => 3, 3 => 0, 5 => 0, 7 => 7, 8 => 0, 9 => 9], + }; + assert_eq!( + diff_pod(Some(&a), Some(&b)), + Some(AccountDiff { + balance: Diff::Same, + nonce: Diff::Same, + code: Diff::Same, + storage: map![ + 2.into() => Diff::new(2.into(), 3.into()), + 3.into() => Diff::new(3.into(), 0.into()), + 4.into() => Diff::new(4.into(), 0.into()), + 7.into() => Diff::new(0.into(), 7.into()), + 9.into() => Diff::new(0.into(), 9.into()) + ], + }) + ); + } } diff --git a/ethcore/src/pod_state.rs b/ethcore/src/pod_state.rs index c1130faa726..5eafca56805 100644 --- a/ethcore/src/pod_state.rs +++ b/ethcore/src/pod_state.rs @@ -1,205 +1,244 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! State of all accounts in the system expressed in Plain Old Data. -use std::fmt; -use std::collections::BTreeMap; +use ethereum_types::{Address, H256}; +use ethjson; use itertools::Itertools; -use ethereum_types::{H256, Address}; -use triehash::sec_trie_root; use pod_account::{self, PodAccount}; +use std::{collections::BTreeMap, fmt}; +use triehash::sec_trie_root; use types::state_diff::StateDiff; -use ethjson; /// State of all accounts in the system expressed in Plain Old Data. #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)] pub struct PodState(BTreeMap); impl PodState { - /// Contruct a new object from the `m`. - pub fn new() -> PodState { Default::default() } - - /// Contruct a new object from the `m`. - pub fn from(m: BTreeMap) -> PodState { PodState(m) } - - /// Get the underlying map. - pub fn get(&self) -> &BTreeMap { &self.0 } - - /// Get the root hash of the trie of the RLP of this. - pub fn root(&self) -> H256 { - sec_trie_root(self.0.iter().map(|(k, v)| (k, v.rlp()))) - } - - /// Drain object to get the underlying map. - pub fn drain(self) -> BTreeMap { self.0 } + /// Contruct a new object from the `m`. + pub fn new() -> PodState { + Default::default() + } + + /// Contruct a new object from the `m`. + pub fn from(m: BTreeMap) -> PodState { + PodState(m) + } + + /// Get the underlying map. + pub fn get(&self) -> &BTreeMap { + &self.0 + } + + /// Get the root hash of the trie of the RLP of this. + pub fn root(&self) -> H256 { + sec_trie_root(self.0.iter().map(|(k, v)| (k, v.rlp()))) + } + + /// Drain object to get the underlying map. + pub fn drain(self) -> BTreeMap { + self.0 + } } impl From for PodState { - fn from(s: ethjson::blockchain::State) -> PodState { - let state = s.into_iter().map(|(addr, acc)| (addr.into(), PodAccount::from(acc))).collect(); - PodState(state) - } + fn from(s: ethjson::blockchain::State) -> PodState { + let state = s + .into_iter() + .map(|(addr, acc)| (addr.into(), PodAccount::from(acc))) + .collect(); + PodState(state) + } } impl From for PodState { - fn from(s: ethjson::spec::State) -> PodState { - let state: BTreeMap<_,_> = s.into_iter() - .filter(|pair| !pair.1.is_empty()) - .map(|(addr, acc)| (addr.into(), PodAccount::from(acc))) - .collect(); - PodState(state) - } + fn from(s: ethjson::spec::State) -> PodState { + let state: BTreeMap<_, _> = s + .into_iter() + .filter(|pair| !pair.1.is_empty()) + .map(|(addr, acc)| (addr.into(), PodAccount::from(acc))) + .collect(); + PodState(state) + } } impl fmt::Display for PodState { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for (add, acc) in &self.0 { - writeln!(f, "{} => {}", add, acc)?; - } - Ok(()) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for (add, acc) in &self.0 { + writeln!(f, "{} => {}", add, acc)?; + } + Ok(()) + } } /// Calculate and return diff between `pre` state and `post` state. pub fn diff_pod(pre: &PodState, post: &PodState) -> StateDiff { - StateDiff { - raw: pre.get().keys() - .merge(post.get().keys()) - .filter_map(|acc| pod_account::diff_pod(pre.get().get(acc), post.get().get(acc)).map(|d| (acc.clone(), d))) - .collect() - } + StateDiff { + raw: pre + .get() + .keys() + .merge(post.get().keys()) + .filter_map(|acc| { + pod_account::diff_pod(pre.get().get(acc), post.get().get(acc)) + .map(|d| (acc.clone(), d)) + }) + .collect(), + } } #[cfg(test)] mod test { - use std::collections::BTreeMap; - use types::state_diff::*; - use types::account_diff::*; - use pod_account::PodAccount; - use super::PodState; - - #[test] - fn create_delete() { - let a = PodState::from(map![ - 1.into() => PodAccount { - balance: 69.into(), - nonce: 0.into(), - code: Some(Vec::new()), - storage: map![], - } - ]); - assert_eq!(super::diff_pod(&a, &PodState::new()), StateDiff { raw: map![ - 1.into() => AccountDiff{ - balance: Diff::Died(69.into()), - nonce: Diff::Died(0.into()), - code: Diff::Died(vec![]), - storage: map![], - } - ]}); - assert_eq!(super::diff_pod(&PodState::new(), &a), StateDiff{ raw: map![ - 1.into() => AccountDiff{ - balance: Diff::Born(69.into()), - nonce: Diff::Born(0.into()), - code: Diff::Born(vec![]), - storage: map![], - } - ]}); - } - - #[test] - fn create_delete_with_unchanged() { - let a = PodState::from(map![ - 1.into() => PodAccount { - balance: 69.into(), - nonce: 0.into(), - code: Some(Vec::new()), - storage: map![], - } - ]); - let b = PodState::from(map![ - 1.into() => PodAccount { - balance: 69.into(), - nonce: 0.into(), - code: Some(Vec::new()), - storage: map![], - }, - 2.into() => PodAccount { - balance: 69.into(), - nonce: 0.into(), - code: Some(Vec::new()), - storage: map![], - } - ]); - assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: map![ - 2.into() => AccountDiff{ - balance: Diff::Born(69.into()), - nonce: Diff::Born(0.into()), - code: Diff::Born(vec![]), - storage: map![], - } - ]}); - assert_eq!(super::diff_pod(&b, &a), StateDiff { raw: map![ - 2.into() => AccountDiff{ - balance: Diff::Died(69.into()), - nonce: Diff::Died(0.into()), - code: Diff::Died(vec![]), - storage: map![], - } - ]}); - } - - #[test] - fn change_with_unchanged() { - let a = PodState::from(map![ - 1.into() => PodAccount { - balance: 69.into(), - nonce: 0.into(), - code: Some(Vec::new()), - storage: map![], - }, - 2.into() => PodAccount { - balance: 69.into(), - nonce: 0.into(), - code: Some(Vec::new()), - storage: map![], - } - ]); - let b = PodState::from(map![ - 1.into() => PodAccount { - balance: 69.into(), - nonce: 1.into(), - code: Some(Vec::new()), - storage: map![], - }, - 2.into() => PodAccount { - balance: 69.into(), - nonce: 0.into(), - code: Some(Vec::new()), - storage: map![], - } - ]); - assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: map![ - 1.into() => AccountDiff{ - balance: Diff::Same, - nonce: Diff::Changed(0.into(), 1.into()), - code: Diff::Same, - storage: map![], - } - ]}); - } - + use super::PodState; + use pod_account::PodAccount; + use std::collections::BTreeMap; + use types::{account_diff::*, state_diff::*}; + + #[test] + fn create_delete() { + let a = PodState::from(map![ + 1.into() => PodAccount { + balance: 69.into(), + nonce: 0.into(), + code: Some(Vec::new()), + storage: map![], + } + ]); + assert_eq!( + super::diff_pod(&a, &PodState::new()), + StateDiff { + raw: map![ + 1.into() => AccountDiff{ + balance: Diff::Died(69.into()), + nonce: Diff::Died(0.into()), + code: Diff::Died(vec![]), + storage: map![], + } + ] + } + ); + assert_eq!( + super::diff_pod(&PodState::new(), &a), + StateDiff { + raw: map![ + 1.into() => AccountDiff{ + balance: Diff::Born(69.into()), + nonce: Diff::Born(0.into()), + code: Diff::Born(vec![]), + storage: map![], + } + ] + } + ); + } + + #[test] + fn create_delete_with_unchanged() { + let a = PodState::from(map![ + 1.into() => PodAccount { + balance: 69.into(), + nonce: 0.into(), + code: Some(Vec::new()), + storage: map![], + } + ]); + let b = PodState::from(map![ + 1.into() => PodAccount { + balance: 69.into(), + nonce: 0.into(), + code: Some(Vec::new()), + storage: map![], + }, + 2.into() => PodAccount { + balance: 69.into(), + nonce: 0.into(), + code: Some(Vec::new()), + storage: map![], + } + ]); + assert_eq!( + super::diff_pod(&a, &b), + StateDiff { + raw: map![ + 2.into() => AccountDiff{ + balance: Diff::Born(69.into()), + nonce: Diff::Born(0.into()), + code: Diff::Born(vec![]), + storage: map![], + } + ] + } + ); + assert_eq!( + super::diff_pod(&b, &a), + StateDiff { + raw: map![ + 2.into() => AccountDiff{ + balance: Diff::Died(69.into()), + nonce: Diff::Died(0.into()), + code: Diff::Died(vec![]), + storage: map![], + } + ] + } + ); + } + + #[test] + fn change_with_unchanged() { + let a = PodState::from(map![ + 1.into() => PodAccount { + balance: 69.into(), + nonce: 0.into(), + code: Some(Vec::new()), + storage: map![], + }, + 2.into() => PodAccount { + balance: 69.into(), + nonce: 0.into(), + code: Some(Vec::new()), + storage: map![], + } + ]); + let b = PodState::from(map![ + 1.into() => PodAccount { + balance: 69.into(), + nonce: 1.into(), + code: Some(Vec::new()), + storage: map![], + }, + 2.into() => PodAccount { + balance: 69.into(), + nonce: 0.into(), + code: Some(Vec::new()), + storage: map![], + } + ]); + assert_eq!( + super::diff_pod(&a, &b), + StateDiff { + raw: map![ + 1.into() => AccountDiff{ + balance: Diff::Same, + nonce: Diff::Changed(0.into(), 1.into()), + code: Diff::Same, + storage: map![], + } + ] + } + ); + } } diff --git a/ethcore/src/snapshot/account.rs b/ethcore/src/snapshot/account.rs index ed56e2435b8..cb3f5e154e3 100644 --- a/ethcore/src/snapshot/account.rs +++ b/ethcore/src/snapshot/account.rs @@ -1,355 +1,465 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Account state encoding and decoding use account_db::{AccountDB, AccountDBMut}; -use types::basic_account::BasicAccount; use bytes::Bytes; use ethereum_types::{H256, U256}; use ethtrie::{TrieDB, TrieDBMut}; use hash::{KECCAK_EMPTY, KECCAK_NULL_RLP}; use hash_db::HashDB; -use rlp::{RlpStream, Rlp}; -use snapshot::Error; -use std::collections::HashSet; +use rlp::{Rlp, RlpStream}; +use snapshot::{Error, Progress}; +use std::{collections::HashSet, sync::atomic::Ordering}; use trie::{Trie, TrieMut}; +use types::basic_account::BasicAccount; // An empty account -- these were replaced with RLP null data for a space optimization in v1. const ACC_EMPTY: BasicAccount = BasicAccount { - nonce: U256([0, 0, 0, 0]), - balance: U256([0, 0, 0, 0]), - storage_root: KECCAK_NULL_RLP, - code_hash: KECCAK_EMPTY, + nonce: U256([0, 0, 0, 0]), + balance: U256([0, 0, 0, 0]), + storage_root: KECCAK_NULL_RLP, + code_hash: KECCAK_EMPTY, }; // whether an encoded account has code and how it is referred to. #[repr(u8)] enum CodeState { - // the account has no code. - Empty = 0, - // raw code is encoded. - Inline = 1, - // the code is referred to by hash. - Hash = 2, + // the account has no code. + Empty = 0, + // raw code is encoded. + Inline = 1, + // the code is referred to by hash. + Hash = 2, } impl CodeState { - fn from(x: u8) -> Result { - match x { - 0 => Ok(CodeState::Empty), - 1 => Ok(CodeState::Inline), - 2 => Ok(CodeState::Hash), - _ => Err(Error::UnrecognizedCodeState(x)) - } - } - - fn raw(self) -> u8 { - self as u8 - } + fn from(x: u8) -> Result { + match x { + 0 => Ok(CodeState::Empty), + 1 => Ok(CodeState::Inline), + 2 => Ok(CodeState::Hash), + _ => Err(Error::UnrecognizedCodeState(x)), + } + } + + fn raw(self) -> u8 { + self as u8 + } } // walk the account's storage trie, returning a vector of RLP items containing the // account address hash, account properties and the storage. Each item contains at most `max_storage_items` // storage records split according to snapshot format definition. -pub fn to_fat_rlps(account_hash: &H256, acc: &BasicAccount, acct_db: &AccountDB, used_code: &mut HashSet, first_chunk_size: usize, max_chunk_size: usize) -> Result, Error> { - let db = &(acct_db as &HashDB<_,_>); - let db = TrieDB::new(db, &acc.storage_root)?; - let mut chunks = Vec::new(); - let mut db_iter = db.iter()?; - let mut target_chunk_size = first_chunk_size; - let mut account_stream = RlpStream::new_list(2); - let mut leftover: Option> = None; - loop { - account_stream.append(account_hash); - account_stream.begin_list(5); - - account_stream.append(&acc.nonce) - .append(&acc.balance); - - // [has_code, code_hash]. - if acc.code_hash == KECCAK_EMPTY { - account_stream.append(&CodeState::Empty.raw()).append_empty_data(); - } else if used_code.contains(&acc.code_hash) { - account_stream.append(&CodeState::Hash.raw()).append(&acc.code_hash); - } else { - match acct_db.get(&acc.code_hash) { - Some(c) => { - used_code.insert(acc.code_hash.clone()); - account_stream.append(&CodeState::Inline.raw()).append(&&*c); - } - None => { - warn!("code lookup failed during snapshot"); - account_stream.append(&false).append_empty_data(); - } - } - } - - account_stream.begin_unbounded_list(); - if account_stream.len() > target_chunk_size { - // account does not fit, push an empty record to mark a new chunk - target_chunk_size = max_chunk_size; - chunks.push(Vec::new()); - } - - if let Some(pair) = leftover.take() { - if !account_stream.append_raw_checked(&pair, 1, target_chunk_size) { - return Err(Error::ChunkTooSmall); - } - } - - loop { - match db_iter.next() { - Some(Ok((k, v))) => { - let pair = { - let mut stream = RlpStream::new_list(2); - stream.append(&k).append(&&*v); - stream.drain() - }; - if !account_stream.append_raw_checked(&pair, 1, target_chunk_size) { - account_stream.complete_unbounded_list(); - let stream = ::std::mem::replace(&mut account_stream, RlpStream::new_list(2)); - chunks.push(stream.out()); - target_chunk_size = max_chunk_size; - leftover = Some(pair); - break; - } - }, - Some(Err(e)) => { - return Err(e.into()); - }, - None => { - account_stream.complete_unbounded_list(); - let stream = ::std::mem::replace(&mut account_stream, RlpStream::new_list(2)); - chunks.push(stream.out()); - return Ok(chunks); - } - } - - } - } +pub fn to_fat_rlps( + account_hash: &H256, + acc: &BasicAccount, + acct_db: &AccountDB, + used_code: &mut HashSet, + first_chunk_size: usize, + max_chunk_size: usize, + p: &Progress, +) -> Result, Error> { + let db = &(acct_db as &dyn HashDB<_, _>); + let db = TrieDB::new(db, &acc.storage_root)?; + let mut chunks = Vec::new(); + let mut db_iter = db.iter()?; + let mut target_chunk_size = first_chunk_size; + let mut account_stream = RlpStream::new_list(2); + let mut leftover: Option> = None; + loop { + account_stream.append(account_hash); + account_stream.begin_list(5); + + account_stream.append(&acc.nonce).append(&acc.balance); + + // [has_code, code_hash]. + if acc.code_hash == KECCAK_EMPTY { + account_stream + .append(&CodeState::Empty.raw()) + .append_empty_data(); + } else if used_code.contains(&acc.code_hash) { + account_stream + .append(&CodeState::Hash.raw()) + .append(&acc.code_hash); + } else { + match acct_db.get(&acc.code_hash) { + Some(c) => { + used_code.insert(acc.code_hash.clone()); + account_stream.append(&CodeState::Inline.raw()).append(&&*c); + } + None => { + warn!("code lookup failed during snapshot"); + account_stream.append(&false).append_empty_data(); + } + } + } + + account_stream.begin_unbounded_list(); + if account_stream.len() > target_chunk_size { + // account does not fit, push an empty record to mark a new chunk + target_chunk_size = max_chunk_size; + chunks.push(Vec::new()); + } + + if let Some(pair) = leftover.take() { + if !account_stream.append_raw_checked(&pair, 1, target_chunk_size) { + return Err(Error::ChunkTooSmall); + } + } + + loop { + if p.abort.load(Ordering::SeqCst) { + trace!(target: "snapshot", "to_fat_rlps: aborting snapshot"); + return Err(Error::SnapshotAborted); + } + match db_iter.next() { + Some(Ok((k, v))) => { + let pair = { + let mut stream = RlpStream::new_list(2); + stream.append(&k).append(&&*v); + stream.drain() + }; + if !account_stream.append_raw_checked(&pair, 1, target_chunk_size) { + account_stream.complete_unbounded_list(); + let stream = + ::std::mem::replace(&mut account_stream, RlpStream::new_list(2)); + chunks.push(stream.out()); + target_chunk_size = max_chunk_size; + leftover = Some(pair); + break; + } + } + Some(Err(e)) => { + return Err(e.into()); + } + None => { + account_stream.complete_unbounded_list(); + let stream = ::std::mem::replace(&mut account_stream, RlpStream::new_list(2)); + chunks.push(stream.out()); + return Ok(chunks); + } + } + } + } } // decode a fat rlp, and rebuild the storage trie as we go. // returns the account structure along with its newly recovered code, // if it exists. pub fn from_fat_rlp( - acct_db: &mut AccountDBMut, - rlp: Rlp, - mut storage_root: H256, + acct_db: &mut AccountDBMut, + rlp: Rlp, + mut storage_root: H256, ) -> Result<(BasicAccount, Option), Error> { - - // check for special case of empty account. - if rlp.is_empty() { - return Ok((ACC_EMPTY, None)); - } - - let nonce = rlp.val_at(0)?; - let balance = rlp.val_at(1)?; - let code_state: CodeState = { - let raw: u8 = rlp.val_at(2)?; - CodeState::from(raw)? - }; - - // load the code if it exists. - let (code_hash, new_code) = match code_state { - CodeState::Empty => (KECCAK_EMPTY, None), - CodeState::Inline => { - let code: Bytes = rlp.val_at(3)?; - let code_hash = acct_db.insert(&code); - - (code_hash, Some(code)) - } - CodeState::Hash => { - let code_hash = rlp.val_at(3)?; - - (code_hash, None) - } - }; - - { - let mut storage_trie = if storage_root.is_zero() { - TrieDBMut::new(acct_db, &mut storage_root) - } else { - TrieDBMut::from_existing(acct_db, &mut storage_root)? - }; - let pairs = rlp.at(4)?; - for pair_rlp in pairs.iter() { - let k: Bytes = pair_rlp.val_at(0)?; - let v: Bytes = pair_rlp.val_at(1)?; - - storage_trie.insert(&k, &v)?; - } - } - - let acc = BasicAccount { - nonce: nonce, - balance: balance, - storage_root: storage_root, - code_hash: code_hash, - }; - - Ok((acc, new_code)) + // check for special case of empty account. + if rlp.is_empty() { + return Ok((ACC_EMPTY, None)); + } + + let nonce = rlp.val_at(0)?; + let balance = rlp.val_at(1)?; + let code_state: CodeState = { + let raw: u8 = rlp.val_at(2)?; + CodeState::from(raw)? + }; + + // load the code if it exists. + let (code_hash, new_code) = match code_state { + CodeState::Empty => (KECCAK_EMPTY, None), + CodeState::Inline => { + let code: Bytes = rlp.val_at(3)?; + let code_hash = acct_db.insert(&code); + + (code_hash, Some(code)) + } + CodeState::Hash => { + let code_hash = rlp.val_at(3)?; + + (code_hash, None) + } + }; + + { + let mut storage_trie = if storage_root.is_zero() { + TrieDBMut::new(acct_db, &mut storage_root) + } else { + TrieDBMut::from_existing(acct_db, &mut storage_root)? + }; + let pairs = rlp.at(4)?; + for pair_rlp in pairs.iter() { + let k: Bytes = pair_rlp.val_at(0)?; + let v: Bytes = pair_rlp.val_at(1)?; + + storage_trie.insert(&k, &v)?; + } + } + + let acc = BasicAccount { + nonce: nonce, + balance: balance, + storage_root: storage_root, + code_hash: code_hash, + }; + + Ok((acc, new_code)) } #[cfg(test)] mod tests { - use account_db::{AccountDB, AccountDBMut}; - use types::basic_account::BasicAccount; - use test_helpers::get_temp_state_db; - use snapshot::tests::helpers::fill_storage; - - use hash::{KECCAK_EMPTY, KECCAK_NULL_RLP, keccak}; - use ethereum_types::{H256, Address}; - use hash_db::HashDB; - use kvdb::DBValue; - use rlp::Rlp; - - use std::collections::HashSet; - - use super::{ACC_EMPTY, to_fat_rlps, from_fat_rlp}; - - #[test] - fn encoding_basic() { - let mut db = get_temp_state_db(); - let addr = Address::random(); - - let account = BasicAccount { - nonce: 50.into(), - balance: 123456789.into(), - storage_root: KECCAK_NULL_RLP, - code_hash: KECCAK_EMPTY, - }; - - let thin_rlp = ::rlp::encode(&account); - assert_eq!(::rlp::decode::(&thin_rlp).unwrap(), account); - - let fat_rlps = to_fat_rlps(&keccak(&addr), &account, &AccountDB::new(db.as_hash_db(), &addr), &mut Default::default(), usize::max_value(), usize::max_value()).unwrap(); - let fat_rlp = Rlp::new(&fat_rlps[0]).at(1).unwrap(); - assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hash_db_mut(), &addr), fat_rlp, H256::zero()).unwrap().0, account); - } - - #[test] - fn encoding_storage() { - let mut db = get_temp_state_db(); - let addr = Address::random(); - - let account = { - let acct_db = AccountDBMut::new(db.as_hash_db_mut(), &addr); - let mut root = KECCAK_NULL_RLP; - fill_storage(acct_db, &mut root, &mut H256::zero()); - BasicAccount { - nonce: 25.into(), - balance: 987654321.into(), - storage_root: root, - code_hash: KECCAK_EMPTY, - } - }; - - let thin_rlp = ::rlp::encode(&account); - assert_eq!(::rlp::decode::(&thin_rlp).unwrap(), account); - - let fat_rlp = to_fat_rlps(&keccak(&addr), &account, &AccountDB::new(db.as_hash_db(), &addr), &mut Default::default(), usize::max_value(), usize::max_value()).unwrap(); - let fat_rlp = Rlp::new(&fat_rlp[0]).at(1).unwrap(); - assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hash_db_mut(), &addr), fat_rlp, H256::zero()).unwrap().0, account); - } - - #[test] - fn encoding_storage_split() { - let mut db = get_temp_state_db(); - let addr = Address::random(); - - let account = { - let acct_db = AccountDBMut::new(db.as_hash_db_mut(), &addr); - let mut root = KECCAK_NULL_RLP; - fill_storage(acct_db, &mut root, &mut H256::zero()); - BasicAccount { - nonce: 25.into(), - balance: 987654321.into(), - storage_root: root, - code_hash: KECCAK_EMPTY, - } - }; - - let thin_rlp = ::rlp::encode(&account); - assert_eq!(::rlp::decode::(&thin_rlp).unwrap(), account); - - let fat_rlps = to_fat_rlps(&keccak(addr), &account, &AccountDB::new(db.as_hash_db(), &addr), &mut Default::default(), 500, 1000).unwrap(); - let mut root = KECCAK_NULL_RLP; - let mut restored_account = None; - for rlp in fat_rlps { - let fat_rlp = Rlp::new(&rlp).at(1).unwrap(); - restored_account = Some(from_fat_rlp(&mut AccountDBMut::new(db.as_hash_db_mut(), &addr), fat_rlp, root).unwrap().0); - root = restored_account.as_ref().unwrap().storage_root.clone(); - } - assert_eq!(restored_account, Some(account)); - } - - #[test] - fn encoding_code() { - let mut db = get_temp_state_db(); - - let addr1 = Address::random(); - let addr2 = Address::random(); - - let code_hash = { - let mut acct_db = AccountDBMut::new(db.as_hash_db_mut(), &addr1); - acct_db.insert(b"this is definitely code") - }; - - { - let mut acct_db = AccountDBMut::new(db.as_hash_db_mut(), &addr2); - acct_db.emplace(code_hash.clone(), DBValue::from_slice(b"this is definitely code")); - } - - let account1 = BasicAccount { - nonce: 50.into(), - balance: 123456789.into(), - storage_root: KECCAK_NULL_RLP, - code_hash: code_hash, - }; - - let account2 = BasicAccount { - nonce: 400.into(), - balance: 98765432123456789usize.into(), - storage_root: KECCAK_NULL_RLP, - code_hash: code_hash, - }; - - let mut used_code = HashSet::new(); - - let fat_rlp1 = to_fat_rlps(&keccak(&addr1), &account1, &AccountDB::new(db.as_hash_db(), &addr1), &mut used_code, usize::max_value(), usize::max_value()).unwrap(); - let fat_rlp2 = to_fat_rlps(&keccak(&addr2), &account2, &AccountDB::new(db.as_hash_db(), &addr2), &mut used_code, usize::max_value(), usize::max_value()).unwrap(); - assert_eq!(used_code.len(), 1); - - let fat_rlp1 = Rlp::new(&fat_rlp1[0]).at(1).unwrap(); - let fat_rlp2 = Rlp::new(&fat_rlp2[0]).at(1).unwrap(); - - let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hash_db_mut(), &addr2), fat_rlp2, H256::zero()).unwrap(); - assert!(maybe_code.is_none()); - assert_eq!(acc, account2); - - let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hash_db_mut(), &addr1), fat_rlp1, H256::zero()).unwrap(); - assert_eq!(maybe_code, Some(b"this is definitely code".to_vec())); - assert_eq!(acc, account1); - } - - #[test] - fn encoding_empty_acc() { - let mut db = get_temp_state_db(); - assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hash_db_mut(), &Address::default()), Rlp::new(&::rlp::NULL_RLP), H256::zero()).unwrap(), (ACC_EMPTY, None)); - } + use account_db::{AccountDB, AccountDBMut}; + use snapshot::{tests::helpers::fill_storage, Progress}; + use test_helpers::get_temp_state_db; + use types::basic_account::BasicAccount; + + use ethereum_types::{Address, H256}; + use hash::{keccak, KECCAK_EMPTY, KECCAK_NULL_RLP}; + use hash_db::HashDB; + use kvdb::DBValue; + use rlp::Rlp; + + use std::collections::HashSet; + + use super::{from_fat_rlp, to_fat_rlps, ACC_EMPTY}; + + #[test] + fn encoding_basic() { + let mut db = get_temp_state_db(); + let addr = Address::random(); + + let account = BasicAccount { + nonce: 50.into(), + balance: 123456789.into(), + storage_root: KECCAK_NULL_RLP, + code_hash: KECCAK_EMPTY, + }; + + let thin_rlp = ::rlp::encode(&account); + assert_eq!(::rlp::decode::(&thin_rlp).unwrap(), account); + let p = Progress::default(); + let fat_rlps = to_fat_rlps( + &keccak(&addr), + &account, + &AccountDB::new(db.as_hash_db(), &addr), + &mut Default::default(), + usize::max_value(), + usize::max_value(), + &p, + ) + .unwrap(); + let fat_rlp = Rlp::new(&fat_rlps[0]).at(1).unwrap(); + assert_eq!( + from_fat_rlp( + &mut AccountDBMut::new(db.as_hash_db_mut(), &addr), + fat_rlp, + H256::zero() + ) + .unwrap() + .0, + account + ); + } + + #[test] + fn encoding_storage() { + let mut db = get_temp_state_db(); + let addr = Address::random(); + + let account = { + let acct_db = AccountDBMut::new(db.as_hash_db_mut(), &addr); + let mut root = KECCAK_NULL_RLP; + fill_storage(acct_db, &mut root, &mut H256::zero()); + BasicAccount { + nonce: 25.into(), + balance: 987654321.into(), + storage_root: root, + code_hash: KECCAK_EMPTY, + } + }; + + let thin_rlp = ::rlp::encode(&account); + assert_eq!(::rlp::decode::(&thin_rlp).unwrap(), account); + + let p = Progress::default(); + + let fat_rlp = to_fat_rlps( + &keccak(&addr), + &account, + &AccountDB::new(db.as_hash_db(), &addr), + &mut Default::default(), + usize::max_value(), + usize::max_value(), + &p, + ) + .unwrap(); + let fat_rlp = Rlp::new(&fat_rlp[0]).at(1).unwrap(); + assert_eq!( + from_fat_rlp( + &mut AccountDBMut::new(db.as_hash_db_mut(), &addr), + fat_rlp, + H256::zero() + ) + .unwrap() + .0, + account + ); + } + + #[test] + fn encoding_storage_split() { + let mut db = get_temp_state_db(); + let addr = Address::random(); + + let account = { + let acct_db = AccountDBMut::new(db.as_hash_db_mut(), &addr); + let mut root = KECCAK_NULL_RLP; + fill_storage(acct_db, &mut root, &mut H256::zero()); + BasicAccount { + nonce: 25.into(), + balance: 987654321.into(), + storage_root: root, + code_hash: KECCAK_EMPTY, + } + }; + + let thin_rlp = ::rlp::encode(&account); + assert_eq!(::rlp::decode::(&thin_rlp).unwrap(), account); + + let p = Progress::default(); + let fat_rlps = to_fat_rlps( + &keccak(addr), + &account, + &AccountDB::new(db.as_hash_db(), &addr), + &mut Default::default(), + 500, + 1000, + &p, + ) + .unwrap(); + let mut root = KECCAK_NULL_RLP; + let mut restored_account = None; + for rlp in fat_rlps { + let fat_rlp = Rlp::new(&rlp).at(1).unwrap(); + restored_account = Some( + from_fat_rlp( + &mut AccountDBMut::new(db.as_hash_db_mut(), &addr), + fat_rlp, + root, + ) + .unwrap() + .0, + ); + root = restored_account.as_ref().unwrap().storage_root.clone(); + } + assert_eq!(restored_account, Some(account)); + } + + #[test] + fn encoding_code() { + let mut db = get_temp_state_db(); + + let addr1 = Address::random(); + let addr2 = Address::random(); + + let code_hash = { + let mut acct_db = AccountDBMut::new(db.as_hash_db_mut(), &addr1); + acct_db.insert(b"this is definitely code") + }; + + { + let mut acct_db = AccountDBMut::new(db.as_hash_db_mut(), &addr2); + acct_db.emplace( + code_hash.clone(), + DBValue::from_slice(b"this is definitely code"), + ); + } + + let account1 = BasicAccount { + nonce: 50.into(), + balance: 123456789.into(), + storage_root: KECCAK_NULL_RLP, + code_hash, + }; + + let account2 = BasicAccount { + nonce: 400.into(), + balance: 98765432123456789usize.into(), + storage_root: KECCAK_NULL_RLP, + code_hash, + }; + + let mut used_code = HashSet::new(); + let p1 = Progress::default(); + let p2 = Progress::default(); + let fat_rlp1 = to_fat_rlps( + &keccak(&addr1), + &account1, + &AccountDB::new(db.as_hash_db(), &addr1), + &mut used_code, + usize::max_value(), + usize::max_value(), + &p1, + ) + .unwrap(); + let fat_rlp2 = to_fat_rlps( + &keccak(&addr2), + &account2, + &AccountDB::new(db.as_hash_db(), &addr2), + &mut used_code, + usize::max_value(), + usize::max_value(), + &p2, + ) + .unwrap(); + assert_eq!(used_code.len(), 1); + + let fat_rlp1 = Rlp::new(&fat_rlp1[0]).at(1).unwrap(); + let fat_rlp2 = Rlp::new(&fat_rlp2[0]).at(1).unwrap(); + + let (acc, maybe_code) = from_fat_rlp( + &mut AccountDBMut::new(db.as_hash_db_mut(), &addr2), + fat_rlp2, + H256::zero(), + ) + .unwrap(); + assert!(maybe_code.is_none()); + assert_eq!(acc, account2); + + let (acc, maybe_code) = from_fat_rlp( + &mut AccountDBMut::new(db.as_hash_db_mut(), &addr1), + fat_rlp1, + H256::zero(), + ) + .unwrap(); + assert_eq!(maybe_code, Some(b"this is definitely code".to_vec())); + assert_eq!(acc, account1); + } + + #[test] + fn encoding_empty_acc() { + let mut db = get_temp_state_db(); + assert_eq!( + from_fat_rlp( + &mut AccountDBMut::new(db.as_hash_db_mut(), &Address::zero()), + Rlp::new(&::rlp::NULL_RLP), + H256::zero() + ) + .unwrap(), + (ACC_EMPTY, None) + ); + } } diff --git a/ethcore/src/snapshot/block.rs b/ethcore/src/snapshot/block.rs index 0fc590763c6..5d29fccd1d6 100644 --- a/ethcore/src/snapshot/block.rs +++ b/ethcore/src/snapshot/block.rs @@ -1,204 +1,202 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Block RLP compression. use bytes::Bytes; use ethereum_types::H256; use hash::keccak; -use rlp::{DecoderError, RlpStream, Rlp}; +use rlp::{DecoderError, Rlp, RlpStream}; use triehash::ordered_trie_root; -use types::block::Block; -use types::header::Header; -use types::views::BlockView; +use types::{block::Block, header::Header, views::BlockView}; const HEADER_FIELDS: usize = 8; const BLOCK_FIELDS: usize = 2; pub struct AbridgedBlock { - rlp: Bytes, + rlp: Bytes, } impl AbridgedBlock { - /// Create from rlp-compressed bytes. Does no verification. - pub fn from_raw(compressed: Bytes) -> Self { - AbridgedBlock { - rlp: compressed, - } - } - - /// Return the inner bytes. - pub fn into_inner(self) -> Bytes { - self.rlp - } - - /// Given a full block view, trim out the parent hash and block number, - /// producing new rlp. - pub fn from_block_view(block_view: &BlockView) -> Self { - let header = block_view.header_view(); - let seal_fields = header.seal(); - - // 10 header fields, unknown number of seal fields, and 2 block fields. - let mut stream = RlpStream::new_list( - HEADER_FIELDS + - seal_fields.len() + - BLOCK_FIELDS - ); - - // write header values. - stream - .append(&header.author()) - .append(&header.state_root()) - .append(&header.log_bloom()) - .append(&header.difficulty()) - .append(&header.gas_limit()) - .append(&header.gas_used()) - .append(&header.timestamp()) - .append(&header.extra_data()); - - // write block values. - stream - .append_list(&block_view.transactions()) - .append_list(&block_view.uncles()); - - // write seal fields. - for field in seal_fields { - stream.append_raw(&field, 1); - } - - AbridgedBlock { - rlp: stream.out(), - } - } - - /// Flesh out an abridged block view with the provided parent hash and block number. - /// - /// Will fail if contains invalid rlp. - pub fn to_block(&self, parent_hash: H256, number: u64, receipts_root: H256) -> Result { - let rlp = Rlp::new(&self.rlp); - - let mut header: Header = Default::default(); - header.set_parent_hash(parent_hash); - header.set_author(rlp.val_at(0)?); - header.set_state_root(rlp.val_at(1)?); - header.set_log_bloom(rlp.val_at(2)?); - header.set_difficulty(rlp.val_at(3)?); - header.set_number(number); - header.set_gas_limit(rlp.val_at(4)?); - header.set_gas_used(rlp.val_at(5)?); - header.set_timestamp(rlp.val_at(6)?); - header.set_extra_data(rlp.val_at(7)?); - - let transactions = rlp.list_at(8)?; - let uncles: Vec
= rlp.list_at(9)?; - - header.set_transactions_root(ordered_trie_root( - rlp.at(8)?.iter().map(|r| r.as_raw()) - )); - header.set_receipts_root(receipts_root); - - let mut uncles_rlp = RlpStream::new(); - uncles_rlp.append_list(&uncles); - header.set_uncles_hash(keccak(uncles_rlp.as_raw())); - - let mut seal_fields = Vec::new(); - for i in (HEADER_FIELDS + BLOCK_FIELDS)..rlp.item_count()? { - let seal_rlp = rlp.at(i)?; - seal_fields.push(seal_rlp.as_raw().to_owned()); - } - - header.set_seal(seal_fields); - - Ok(Block { - header: header, - transactions: transactions, - uncles: uncles, - }) - } + /// Create from rlp-compressed bytes. Does no verification. + pub fn from_raw(compressed: Bytes) -> Self { + AbridgedBlock { rlp: compressed } + } + + /// Return the inner bytes. + pub fn into_inner(self) -> Bytes { + self.rlp + } + + /// Given a full block view, trim out the parent hash and block number, + /// producing new rlp. + pub fn from_block_view(block_view: &BlockView) -> Self { + let header = block_view.header_view(); + let seal_fields = header.seal(); + + // 10 header fields, unknown number of seal fields, and 2 block fields. + let mut stream = RlpStream::new_list(HEADER_FIELDS + seal_fields.len() + BLOCK_FIELDS); + + // write header values. + stream + .append(&header.author()) + .append(&header.state_root()) + .append(&header.log_bloom()) + .append(&header.difficulty()) + .append(&header.gas_limit()) + .append(&header.gas_used()) + .append(&header.timestamp()) + .append(&header.extra_data()); + + // write block values. + stream + .append_list(&block_view.transactions()) + .append_list(&block_view.uncles()); + + // write seal fields. + for field in seal_fields { + stream.append_raw(&field, 1); + } + + AbridgedBlock { rlp: stream.out() } + } + + /// Flesh out an abridged block view with the provided parent hash and block number. + /// + /// Will fail if contains invalid rlp. + pub fn to_block( + &self, + parent_hash: H256, + number: u64, + receipts_root: H256, + ) -> Result { + let rlp = Rlp::new(&self.rlp); + + let mut header: Header = Default::default(); + header.set_parent_hash(parent_hash); + header.set_author(rlp.val_at(0)?); + header.set_state_root(rlp.val_at(1)?); + header.set_log_bloom(rlp.val_at(2)?); + header.set_difficulty(rlp.val_at(3)?); + header.set_number(number); + header.set_gas_limit(rlp.val_at(4)?); + header.set_gas_used(rlp.val_at(5)?); + header.set_timestamp(rlp.val_at(6)?); + header.set_extra_data(rlp.val_at(7)?); + + let transactions = rlp.list_at(8)?; + let uncles: Vec
= rlp.list_at(9)?; + + header.set_transactions_root(ordered_trie_root(rlp.at(8)?.iter().map(|r| r.as_raw()))); + header.set_receipts_root(receipts_root); + + let mut uncles_rlp = RlpStream::new(); + uncles_rlp.append_list(&uncles); + header.set_uncles_hash(keccak(uncles_rlp.as_raw())); + + let mut seal_fields = Vec::new(); + for i in (HEADER_FIELDS + BLOCK_FIELDS)..rlp.item_count()? { + let seal_rlp = rlp.at(i)?; + seal_fields.push(seal_rlp.as_raw().to_owned()); + } + + header.set_seal(seal_fields); + + Ok(Block { + header: header, + transactions: transactions, + uncles: uncles, + }) + } } #[cfg(test)] mod tests { - use super::AbridgedBlock; - - use bytes::Bytes; - use ethereum_types::{H256, U256, Address}; - use types::transaction::{Action, Transaction}; - use types::block::Block; - use types::view; - use types::views::BlockView; - - fn encode_block(b: &Block) -> Bytes { - b.rlp_bytes() - } - - #[test] - fn empty_block_abridging() { - let b = Block::default(); - let receipts_root = b.header.receipts_root().clone(); - let encoded = encode_block(&b); - - let abridged = AbridgedBlock::from_block_view(&view!(BlockView, &encoded)); - assert_eq!(abridged.to_block(H256::new(), 0, receipts_root).unwrap(), b); - } - - #[test] - #[should_panic] - fn wrong_number() { - let b = Block::default(); - let receipts_root = b.header.receipts_root().clone(); - let encoded = encode_block(&b); - - let abridged = AbridgedBlock::from_block_view(&view!(BlockView, &encoded)); - assert_eq!(abridged.to_block(H256::new(), 2, receipts_root).unwrap(), b); - } - - #[test] - fn with_transactions() { - let mut b = Block::default(); - - let t1 = Transaction { - action: Action::Create, - nonce: U256::from(42), - gas_price: U256::from(3000), - gas: U256::from(50_000), - value: U256::from(1), - data: b"Hello!".to_vec() - }.fake_sign(Address::from(0x69)); - - let t2 = Transaction { - action: Action::Create, - nonce: U256::from(88), - gas_price: U256::from(12345), - gas: U256::from(300000), - value: U256::from(1000000000), - data: "Eep!".into(), - }.fake_sign(Address::from(0x55)); - - b.transactions.push(t1.into()); - b.transactions.push(t2.into()); - - let receipts_root = b.header.receipts_root().clone(); - b.header.set_transactions_root(::triehash::ordered_trie_root( - b.transactions.iter().map(::rlp::encode) - )); - - let encoded = encode_block(&b); - - let abridged = AbridgedBlock::from_block_view(&view!(BlockView, &encoded[..])); - assert_eq!(abridged.to_block(H256::new(), 0, receipts_root).unwrap(), b); - } + use super::AbridgedBlock; + + use bytes::Bytes; + use ethereum_types::{Address, H256, U256}; + use types::{ + block::Block, + transaction::{Action, Transaction}, + view, + views::BlockView, + }; + + fn encode_block(b: &Block) -> Bytes { + b.rlp_bytes() + } + + #[test] + fn empty_block_abridging() { + let b = Block::default(); + let receipts_root = b.header.receipts_root().clone(); + let encoded = encode_block(&b); + + let abridged = AbridgedBlock::from_block_view(&view!(BlockView, &encoded)); + assert_eq!(abridged.to_block(H256::new(), 0, receipts_root).unwrap(), b); + } + + #[test] + #[should_panic] + fn wrong_number() { + let b = Block::default(); + let receipts_root = b.header.receipts_root().clone(); + let encoded = encode_block(&b); + + let abridged = AbridgedBlock::from_block_view(&view!(BlockView, &encoded)); + assert_eq!(abridged.to_block(H256::new(), 2, receipts_root).unwrap(), b); + } + + #[test] + fn with_transactions() { + let mut b = Block::default(); + + let t1 = Transaction { + action: Action::Create, + nonce: U256::from(42), + gas_price: U256::from(3000), + gas: U256::from(50_000), + value: U256::from(1), + data: b"Hello!".to_vec(), + } + .fake_sign(Address::from(0x69)); + + let t2 = Transaction { + action: Action::Create, + nonce: U256::from(88), + gas_price: U256::from(12345), + gas: U256::from(300000), + value: U256::from(1000000000), + data: "Eep!".into(), + } + .fake_sign(Address::from(0x55)); + + b.transactions.push(t1.into()); + b.transactions.push(t2.into()); + + let receipts_root = b.header.receipts_root().clone(); + b.header + .set_transactions_root(::triehash::ordered_trie_root( + b.transactions.iter().map(::rlp::encode), + )); + + let encoded = encode_block(&b); + + let abridged = AbridgedBlock::from_block_view(&view!(BlockView, &encoded[..])); + assert_eq!(abridged.to_block(H256::new(), 0, receipts_root).unwrap(), b); + } } diff --git a/ethcore/src/snapshot/consensus/authority.rs b/ethcore/src/snapshot/consensus/authority.rs index 4423e074019..6d4c1daf05d 100644 --- a/ethcore/src/snapshot/consensus/authority.rs +++ b/ethcore/src/snapshot/consensus/authority.rs @@ -1,43 +1,42 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Secondary chunk creation and restoration, implementation for proof-of-authority //! based engines. //! //! The chunks here contain state proofs of transitions, along with validator proofs. -use super::{SnapshotComponents, Rebuilder, ChunkSink}; +use super::{ChunkSink, Rebuilder, SnapshotComponents}; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; -use engines::{EthEngine, EpochVerifier, EpochTransition}; +use engines::{EpochTransition, EpochVerifier, EthEngine}; use machine::EthereumMachine; use snapshot::{Error, ManifestData, Progress}; use blockchain::{BlockChain, BlockChainDB, BlockProvider}; use bytes::Bytes; use ethereum_types::{H256, U256}; -use itertools::{Position, Itertools}; +use itertools::{Itertools, Position}; use kvdb::KeyValueDB; -use rlp::{RlpStream, Rlp}; -use types::encoded; -use types::header::Header; -use types::ids::BlockId; -use types::receipt::Receipt; +use rlp::{Rlp, RlpStream}; +use types::{encoded, header::Header, ids::BlockId, receipt::Receipt}; /// Snapshot creation and restoration for PoA chains. /// Chunk format: @@ -53,343 +52,388 @@ use types::receipt::Receipt; pub struct PoaSnapshot; impl SnapshotComponents for PoaSnapshot { - fn chunk_all( - &mut self, - chain: &BlockChain, - block_at: H256, - sink: &mut ChunkSink, - _progress: &Progress, - preferred_size: usize, - ) -> Result<(), Error> { - let number = chain.block_number(&block_at) - .ok_or_else(|| Error::InvalidStartingBlock(BlockId::Hash(block_at)))?; - - let mut pending_size = 0; - let mut rlps = Vec::new(); - - for (_, transition) in chain.epoch_transitions() - .take_while(|&(_, ref t)| t.block_number <= number) - { - // this can happen when our starting block is non-canonical. - if transition.block_number == number && transition.block_hash != block_at { - break - } - - let header = chain.block_header_data(&transition.block_hash) - .ok_or_else(|| Error::BlockNotFound(transition.block_hash))?; - - let entry = { - let mut entry_stream = RlpStream::new_list(2); - entry_stream - .append_raw(&header.into_inner(), 1) - .append(&transition.proof); - - entry_stream.out() - }; - - // cut of the chunk if too large. - let new_loaded_size = pending_size + entry.len(); - pending_size = if new_loaded_size > preferred_size && !rlps.is_empty() { - write_chunk(false, &mut rlps, sink)?; - entry.len() - } else { - new_loaded_size - }; - - rlps.push(entry); - } - - let (block, receipts) = chain.block(&block_at) - .and_then(|b| chain.block_receipts(&block_at).map(|r| (b, r))) - .ok_or_else(|| Error::BlockNotFound(block_at))?; - let block = block.decode()?; - - let parent_td = chain.block_details(block.header.parent_hash()) - .map(|d| d.total_difficulty) - .ok_or_else(|| Error::BlockNotFound(block_at))?; - - rlps.push({ - let mut stream = RlpStream::new_list(5); - stream - .append(&block.header) - .append_list(&block.transactions) - .append_list(&block.uncles) - .append(&receipts) - .append(&parent_td); - stream.out() - }); - - write_chunk(true, &mut rlps, sink)?; - - Ok(()) - } - - fn rebuilder( - &self, - chain: BlockChain, - db: Arc, - manifest: &ManifestData, - ) -> Result, ::error::Error> { - Ok(Box::new(ChunkRebuilder { - manifest: manifest.clone(), - warp_target: None, - chain: chain, - db: db.key_value().clone(), - had_genesis: false, - unverified_firsts: Vec::new(), - last_epochs: Vec::new(), - })) - } - - fn min_supported_version(&self) -> u64 { 3 } - fn current_version(&self) -> u64 { 3 } + fn chunk_all( + &mut self, + chain: &BlockChain, + block_at: H256, + sink: &mut ChunkSink, + _progress: &Progress, + preferred_size: usize, + ) -> Result<(), Error> { + let number = chain + .block_number(&block_at) + .ok_or_else(|| Error::InvalidStartingBlock(BlockId::Hash(block_at)))?; + + let mut pending_size = 0; + let mut rlps = Vec::new(); + + for (_, transition) in chain + .epoch_transitions() + .take_while(|&(_, ref t)| t.block_number <= number) + { + // this can happen when our starting block is non-canonical. + if transition.block_number == number && transition.block_hash != block_at { + break; + } + + let header = chain + .block_header_data(&transition.block_hash) + .ok_or_else(|| Error::BlockNotFound(transition.block_hash))?; + + let entry = { + let mut entry_stream = RlpStream::new_list(2); + entry_stream + .append_raw(&header.into_inner(), 1) + .append(&transition.proof); + + entry_stream.out() + }; + + // cut of the chunk if too large. + let new_loaded_size = pending_size + entry.len(); + pending_size = if new_loaded_size > preferred_size && !rlps.is_empty() { + write_chunk(false, &mut rlps, sink)?; + entry.len() + } else { + new_loaded_size + }; + + rlps.push(entry); + } + + let (block, receipts) = chain + .block(&block_at) + .and_then(|b| chain.block_receipts(&block_at).map(|r| (b, r))) + .ok_or_else(|| Error::BlockNotFound(block_at))?; + let block = block.decode()?; + + let parent_td = chain + .block_details(block.header.parent_hash()) + .map(|d| d.total_difficulty) + .ok_or_else(|| Error::BlockNotFound(block_at))?; + + rlps.push({ + let mut stream = RlpStream::new_list(5); + stream + .append(&block.header) + .append_list(&block.transactions) + .append_list(&block.uncles) + .append(&receipts) + .append(&parent_td); + stream.out() + }); + + write_chunk(true, &mut rlps, sink)?; + + Ok(()) + } + + fn rebuilder( + &self, + chain: BlockChain, + db: Arc, + manifest: &ManifestData, + ) -> Result, ::error::Error> { + Ok(Box::new(ChunkRebuilder { + manifest: manifest.clone(), + warp_target: None, + chain: chain, + db: db.key_value().clone(), + had_genesis: false, + unverified_firsts: Vec::new(), + last_epochs: Vec::new(), + })) + } + + fn min_supported_version(&self) -> u64 { + 3 + } + fn current_version(&self) -> u64 { + 3 + } } // writes a chunk composed of the inner RLPs here. // flag indicates whether the chunk is the last chunk. fn write_chunk(last: bool, chunk_data: &mut Vec, sink: &mut ChunkSink) -> Result<(), Error> { - let mut stream = RlpStream::new_list(1 + chunk_data.len()); + let mut stream = RlpStream::new_list(1 + chunk_data.len()); - stream.append(&last); - for item in chunk_data.drain(..) { - stream.append_raw(&item, 1); - } + stream.append(&last); + for item in chunk_data.drain(..) { + stream.append_raw(&item, 1); + } - (sink)(stream.out().as_slice()).map_err(Into::into) + (sink)(stream.out().as_slice()).map_err(Into::into) } // rebuilder checks state proofs for all transitions, and checks that each // transition header is verifiable from the epoch data of the one prior. struct ChunkRebuilder { - manifest: ManifestData, - warp_target: Option
, - chain: BlockChain, - db: Arc, - had_genesis: bool, - - // sorted vectors of unverified first blocks in a chunk - // and epoch data from last blocks in chunks. - // verification for these will be done at the end. - unverified_firsts: Vec<(Header, Bytes, H256)>, - last_epochs: Vec<(Header, Box>)>, + manifest: ManifestData, + warp_target: Option
, + chain: BlockChain, + db: Arc, + had_genesis: bool, + + // sorted vectors of unverified first blocks in a chunk + // and epoch data from last blocks in chunks. + // verification for these will be done at the end. + unverified_firsts: Vec<(Header, Bytes, H256)>, + last_epochs: Vec<(Header, Box>)>, } // verified data. struct Verified { - epoch_transition: EpochTransition, - header: Header, + epoch_transition: EpochTransition, + header: Header, } impl ChunkRebuilder { - fn verify_transition( - &mut self, - last_verifier: &mut Option>>, - transition_rlp: Rlp, - engine: &EthEngine, - ) -> Result { - use engines::ConstructedVerifier; - - // decode. - let header: Header = transition_rlp.val_at(0)?; - let epoch_data: Bytes = transition_rlp.val_at(1)?; - - trace!(target: "snapshot", "verifying transition to epoch at block {}", header.number()); - - // check current transition against validators of last epoch. - let new_verifier = match engine.epoch_verifier(&header, &epoch_data) { - ConstructedVerifier::Trusted(v) => v, - ConstructedVerifier::Unconfirmed(v, finality_proof, hash) => { - match *last_verifier { - Some(ref last) => - if last.check_finality_proof(finality_proof).map_or(true, |hashes| !hashes.contains(&hash)) - { - return Err(Error::BadEpochProof(header.number()).into()); - }, - None if header.number() != 0 => { - // genesis never requires additional validation. - - let idx = self.unverified_firsts - .binary_search_by_key(&header.number(), |&(ref h, _, _)| h.number()) - .unwrap_or_else(|x| x); - - let entry = (header.clone(), finality_proof.to_owned(), hash); - self.unverified_firsts.insert(idx, entry); - } - None => {} - } - - v - } - ConstructedVerifier::Err(e) => return Err(e), - }; - - // create new epoch verifier. - *last_verifier = Some(new_verifier); - - Ok(Verified { - epoch_transition: EpochTransition { - block_hash: header.hash(), - block_number: header.number(), - proof: epoch_data, - }, - header: header, - }) - } + fn verify_transition( + &mut self, + last_verifier: &mut Option>>, + transition_rlp: Rlp, + engine: &dyn EthEngine, + ) -> Result { + use engines::ConstructedVerifier; + + // decode. + let header: Header = transition_rlp.val_at(0)?; + let epoch_data: Bytes = transition_rlp.val_at(1)?; + + trace!(target: "snapshot", "verifying transition to epoch at block {}", header.number()); + + // check current transition against validators of last epoch. + let new_verifier = match engine.epoch_verifier(&header, &epoch_data) { + ConstructedVerifier::Trusted(v) => v, + ConstructedVerifier::Unconfirmed(v, finality_proof, hash) => { + match *last_verifier { + Some(ref last) => { + if last + .check_finality_proof(finality_proof) + .map_or(true, |hashes| !hashes.contains(&hash)) + { + return Err(Error::BadEpochProof(header.number()).into()); + } + } + None if header.number() != 0 => { + // genesis never requires additional validation. + + let idx = self + .unverified_firsts + .binary_search_by_key(&header.number(), |&(ref h, _, _)| h.number()) + .unwrap_or_else(|x| x); + + let entry = (header.clone(), finality_proof.to_owned(), hash); + self.unverified_firsts.insert(idx, entry); + } + None => {} + } + + v + } + ConstructedVerifier::Err(e) => return Err(e), + }; + + // create new epoch verifier. + *last_verifier = Some(new_verifier); + + Ok(Verified { + epoch_transition: EpochTransition { + block_hash: header.hash(), + block_number: header.number(), + proof: epoch_data, + }, + header: header, + }) + } } impl Rebuilder for ChunkRebuilder { - fn feed( - &mut self, - chunk: &[u8], - engine: &EthEngine, - abort_flag: &AtomicBool, - ) -> Result<(), ::error::Error> { - let rlp = Rlp::new(chunk); - let is_last_chunk: bool = rlp.val_at(0)?; - let num_items = rlp.item_count()?; - - // number of transitions in the chunk. - let num_transitions = if is_last_chunk { - num_items - 2 - } else { - num_items - 1 - }; - - if num_transitions == 0 && !is_last_chunk { - return Err(Error::WrongChunkFormat("Found non-last chunk without any data.".into()).into()); - } - - let mut last_verifier = None; - let mut last_number = None; - for transition_rlp in rlp.iter().skip(1).take(num_transitions).with_position() { - if !abort_flag.load(Ordering::SeqCst) { return Err(Error::RestorationAborted.into()) } - - let (is_first, is_last) = match transition_rlp { - Position::First(_) => (true, false), - Position::Middle(_) => (false, false), - Position::Last(_) => (false, true), - Position::Only(_) => (true, true), - }; - - let transition_rlp = transition_rlp.into_inner(); - let verified = self.verify_transition( - &mut last_verifier, - transition_rlp, - engine, - )?; - - if last_number.map_or(false, |num| verified.header.number() <= num) { - return Err(Error::WrongChunkFormat("Later epoch transition in earlier or same block.".into()).into()); - } - - last_number = Some(verified.header.number()); - - // book-keep borders for verification later. - if is_first { - // make sure the genesis transition was included, - // but it doesn't need verification later. - if verified.header.number() == 0 { - if verified.header.hash() != self.chain.genesis_hash() { - return Err(Error::WrongBlockHash(0, verified.header.hash(), self.chain.genesis_hash()).into()); - } - - self.had_genesis = true; - } - } - if is_last { - let idx = self.last_epochs - .binary_search_by_key(&verified.header.number(), |&(ref h, _)| h.number()) - .unwrap_or_else(|x| x); - - let entry = ( - verified.header.clone(), - last_verifier.take().expect("last_verifier always set after verify_transition; qed"), - ); - self.last_epochs.insert(idx, entry); - } - - // write epoch transition into database. - let mut batch = self.db.transaction(); - self.chain.insert_epoch_transition(&mut batch, verified.header.number(), - verified.epoch_transition); - self.db.write_buffered(batch); - - trace!(target: "snapshot", "Verified epoch transition for epoch at block {}", verified.header.number()); - } - - if is_last_chunk { - use types::block::Block; - - let last_rlp = rlp.at(num_items - 1)?; - let block = Block { - header: last_rlp.val_at(0)?, - transactions: last_rlp.list_at(1)?, - uncles: last_rlp.list_at(2)?, - }; - let block_data = block.rlp_bytes(); - let receipts: Vec = last_rlp.list_at(3)?; - - { - let hash = block.header.hash(); - let best_hash = self.manifest.block_hash; - if hash != best_hash { - return Err(Error::WrongBlockHash(block.header.number(), best_hash, hash).into()) - } - } - - let parent_td: U256 = last_rlp.val_at(4)?; - - let mut batch = self.db.transaction(); - self.chain.insert_unordered_block(&mut batch, encoded::Block::new(block_data), receipts, Some(parent_td), true, false); - self.db.write_buffered(batch); - - self.warp_target = Some(block.header); - } - - Ok(()) - } - - fn finalize(&mut self, _engine: &EthEngine) -> Result<(), ::error::Error> { - if !self.had_genesis { - return Err(Error::WrongChunkFormat("No genesis transition included.".into()).into()); - } - - let target_header = match self.warp_target.take() { - Some(x) => x, - None => return Err(Error::WrongChunkFormat("Warp target block not included.".into()).into()), - }; - - // verify the first entries of chunks we couldn't before. - // we store all last verifiers, but not all firsts. - // match each unverified first epoch with a last epoch verifier. - let mut lasts_reversed = self.last_epochs.iter().rev(); - for &(ref header, ref finality_proof, hash) in self.unverified_firsts.iter().rev() { - let mut found = false; - while let Some(&(ref last_header, ref last_verifier)) = lasts_reversed.next() { - if last_header.number() < header.number() { - if last_verifier.check_finality_proof(&finality_proof).map_or(true, |hashes| !hashes.contains(&hash)) { - return Err(Error::BadEpochProof(header.number()).into()); - } - found = true; - break; - } - } - - if !found { - return Err(Error::WrongChunkFormat("Inconsistent chunk ordering.".into()).into()); - } - } - - // verify that the warp target verifies correctly the - // most recent epoch. if the warp target was a transition itself, - // it's already verified and doesn't need any more verification. - let &(ref header, ref last_epoch) = self.last_epochs.last() - .expect("last_epochs known to have at least one element by the check above; qed"); - - if header != &target_header { - last_epoch.verify_heavy(&target_header)?; - } - - Ok(()) - } + fn feed( + &mut self, + chunk: &[u8], + engine: &dyn EthEngine, + abort_flag: &AtomicBool, + ) -> Result<(), ::error::Error> { + let rlp = Rlp::new(chunk); + let is_last_chunk: bool = rlp.val_at(0)?; + let num_items = rlp.item_count()?; + + // number of transitions in the chunk. + let num_transitions = if is_last_chunk { + num_items - 2 + } else { + num_items - 1 + }; + + if num_transitions == 0 && !is_last_chunk { + return Err( + Error::WrongChunkFormat("Found non-last chunk without any data.".into()).into(), + ); + } + + let mut last_verifier = None; + let mut last_number = None; + for transition_rlp in rlp.iter().skip(1).take(num_transitions).with_position() { + if !abort_flag.load(Ordering::SeqCst) { + return Err(Error::RestorationAborted.into()); + } + + let (is_first, is_last) = match transition_rlp { + Position::First(_) => (true, false), + Position::Middle(_) => (false, false), + Position::Last(_) => (false, true), + Position::Only(_) => (true, true), + }; + + let transition_rlp = transition_rlp.into_inner(); + let verified = self.verify_transition(&mut last_verifier, transition_rlp, engine)?; + + if last_number.map_or(false, |num| verified.header.number() <= num) { + return Err(Error::WrongChunkFormat( + "Later epoch transition in earlier or same block.".into(), + ) + .into()); + } + + last_number = Some(verified.header.number()); + + // book-keep borders for verification later. + if is_first { + // make sure the genesis transition was included, + // but it doesn't need verification later. + if verified.header.number() == 0 { + if verified.header.hash() != self.chain.genesis_hash() { + return Err(Error::WrongBlockHash( + 0, + verified.header.hash(), + self.chain.genesis_hash(), + ) + .into()); + } + + self.had_genesis = true; + } + } + if is_last { + let idx = self + .last_epochs + .binary_search_by_key(&verified.header.number(), |&(ref h, _)| h.number()) + .unwrap_or_else(|x| x); + + let entry = ( + verified.header.clone(), + last_verifier + .take() + .expect("last_verifier always set after verify_transition; qed"), + ); + self.last_epochs.insert(idx, entry); + } + + // write epoch transition into database. + let mut batch = self.db.transaction(); + self.chain.insert_epoch_transition( + &mut batch, + verified.header.number(), + verified.epoch_transition, + ); + self.db.write_buffered(batch); + + trace!(target: "snapshot", "Verified epoch transition for epoch at block {}", verified.header.number()); + } + + if is_last_chunk { + use types::block::Block; + + let last_rlp = rlp.at(num_items - 1)?; + let block = Block { + header: last_rlp.val_at(0)?, + transactions: last_rlp.list_at(1)?, + uncles: last_rlp.list_at(2)?, + }; + let block_data = block.rlp_bytes(); + let receipts: Vec = last_rlp.list_at(3)?; + + { + let hash = block.header.hash(); + let best_hash = self.manifest.block_hash; + if hash != best_hash { + return Err( + Error::WrongBlockHash(block.header.number(), best_hash, hash).into(), + ); + } + } + + let parent_td: U256 = last_rlp.val_at(4)?; + + let mut batch = self.db.transaction(); + self.chain.insert_unordered_block( + &mut batch, + encoded::Block::new(block_data), + receipts, + Some(parent_td), + true, + false, + ); + self.db.write_buffered(batch); + + self.warp_target = Some(block.header); + } + + Ok(()) + } + + fn finalize(&mut self, _engine: &dyn EthEngine) -> Result<(), ::error::Error> { + if !self.had_genesis { + return Err(Error::WrongChunkFormat("No genesis transition included.".into()).into()); + } + + let target_header = match self.warp_target.take() { + Some(x) => x, + None => { + return Err( + Error::WrongChunkFormat("Warp target block not included.".into()).into(), + ) + } + }; + + // verify the first entries of chunks we couldn't before. + // we store all last verifiers, but not all firsts. + // match each unverified first epoch with a last epoch verifier. + let mut lasts_reversed = self.last_epochs.iter().rev(); + for &(ref header, ref finality_proof, hash) in self.unverified_firsts.iter().rev() { + let mut found = false; + while let Some(&(ref last_header, ref last_verifier)) = lasts_reversed.next() { + if last_header.number() < header.number() { + if last_verifier + .check_finality_proof(&finality_proof) + .map_or(true, |hashes| !hashes.contains(&hash)) + { + return Err(Error::BadEpochProof(header.number()).into()); + } + found = true; + break; + } + } + + if !found { + return Err(Error::WrongChunkFormat("Inconsistent chunk ordering.".into()).into()); + } + } + + // verify that the warp target verifies correctly the + // most recent epoch. if the warp target was a transition itself, + // it's already verified and doesn't need any more verification. + let &(ref header, ref last_epoch) = self + .last_epochs + .last() + .expect("last_epochs known to have at least one element by the check above; qed"); + + if header != &target_header { + last_epoch.verify_heavy(&target_header)?; + } + + Ok(()) + } } diff --git a/ethcore/src/snapshot/consensus/mod.rs b/ethcore/src/snapshot/consensus/mod.rs index 907e9c520bb..0a0865a7097 100644 --- a/ethcore/src/snapshot/consensus/mod.rs +++ b/ethcore/src/snapshot/consensus/mod.rs @@ -1,24 +1,23 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Secondary chunk creation and restoration, implementations for different consensus //! engines. -use std::sync::atomic::AtomicBool; -use std::sync::Arc; +use std::sync::{atomic::AtomicBool, Arc}; use blockchain::{BlockChain, BlockChainDB}; use engines::EthEngine; @@ -29,68 +28,67 @@ use ethereum_types::H256; mod authority; mod work; -pub use self::authority::*; -pub use self::work::*; +pub use self::{authority::*, work::*}; /// A sink for produced chunks. -pub type ChunkSink<'a> = FnMut(&[u8]) -> ::std::io::Result<()> + 'a; +pub type ChunkSink<'a> = dyn FnMut(&[u8]) -> ::std::io::Result<()> + 'a; /// Components necessary for snapshot creation and restoration. pub trait SnapshotComponents: Send { - /// Create secondary snapshot chunks; these corroborate the state data - /// in the state chunks. - /// - /// Chunks shouldn't exceed the given preferred size, and should be fed - /// uncompressed into the sink. - /// - /// This will vary by consensus engine, so it's exposed as a trait. - fn chunk_all( - &mut self, - chain: &BlockChain, - block_at: H256, - chunk_sink: &mut ChunkSink, - progress: &Progress, - preferred_size: usize, - ) -> Result<(), Error>; - - /// Create a rebuilder, which will have chunks fed into it in aribtrary - /// order and then be finalized. - /// - /// The manifest, a database, and fresh `BlockChain` are supplied. - /// - /// The engine passed to the `Rebuilder` methods will be the same instance - /// that created the `SnapshotComponents`. - fn rebuilder( - &self, - chain: BlockChain, - db: Arc, - manifest: &ManifestData, - ) -> Result, ::error::Error>; - - /// Minimum supported snapshot version number. - fn min_supported_version(&self) -> u64; - - /// Current version number - fn current_version(&self) -> u64; + /// Create secondary snapshot chunks; these corroborate the state data + /// in the state chunks. + /// + /// Chunks shouldn't exceed the given preferred size, and should be fed + /// uncompressed into the sink. + /// + /// This will vary by consensus engine, so it's exposed as a trait. + fn chunk_all( + &mut self, + chain: &BlockChain, + block_at: H256, + chunk_sink: &mut ChunkSink, + progress: &Progress, + preferred_size: usize, + ) -> Result<(), Error>; + + /// Create a rebuilder, which will have chunks fed into it in aribtrary + /// order and then be finalized. + /// + /// The manifest, a database, and fresh `BlockChain` are supplied. + /// + /// The engine passed to the `Rebuilder` methods will be the same instance + /// that created the `SnapshotComponents`. + fn rebuilder( + &self, + chain: BlockChain, + db: Arc, + manifest: &ManifestData, + ) -> Result, ::error::Error>; + + /// Minimum supported snapshot version number. + fn min_supported_version(&self) -> u64; + + /// Current version number + fn current_version(&self) -> u64; } /// Restore from secondary snapshot chunks. pub trait Rebuilder: Send { - /// Feed a chunk, potentially out of order. - /// - /// Check `abort_flag` periodically while doing heavy work. If set to `false`, should bail with - /// `Error::RestorationAborted`. - fn feed( - &mut self, - chunk: &[u8], - engine: &EthEngine, - abort_flag: &AtomicBool, - ) -> Result<(), ::error::Error>; - - /// Finalize the restoration. Will be done after all chunks have been - /// fed successfully. - /// - /// This should apply the necessary "glue" between chunks, - /// and verify against the restored state. - fn finalize(&mut self, engine: &EthEngine) -> Result<(), ::error::Error>; + /// Feed a chunk, potentially out of order. + /// + /// Check `abort_flag` periodically while doing heavy work. If set to `false`, should bail with + /// `Error::RestorationAborted`. + fn feed( + &mut self, + chunk: &[u8], + engine: &dyn EthEngine, + abort_flag: &AtomicBool, + ) -> Result<(), ::error::Error>; + + /// Finalize the restoration. Will be done after all chunks have been + /// fed successfully. + /// + /// This should apply the necessary "glue" between chunks, + /// and verify against the restored state. + fn finalize(&mut self, engine: &dyn EthEngine) -> Result<(), ::error::Error>; } diff --git a/ethcore/src/snapshot/consensus/work.rs b/ethcore/src/snapshot/consensus/work.rs index 106fe4474c0..8bbb06fca7b 100644 --- a/ethcore/src/snapshot/consensus/work.rs +++ b/ethcore/src/snapshot/consensus/work.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Secondary chunk creation and restoration, implementation for proof-of-work //! chains. @@ -20,21 +20,24 @@ //! The secondary chunks in this instance are 30,000 "abridged blocks" from the head //! of the chain, which serve as an indication of valid chain. -use super::{SnapshotComponents, Rebuilder, ChunkSink}; +use super::{ChunkSink, Rebuilder, SnapshotComponents}; -use std::collections::VecDeque; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; +use std::{ + collections::VecDeque, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, +}; use blockchain::{BlockChain, BlockChainDB, BlockProvider}; +use bytes::Bytes; use engines::EthEngine; -use snapshot::{Error, ManifestData, Progress}; -use snapshot::block::AbridgedBlock; use ethereum_types::H256; use kvdb::KeyValueDB; -use bytes::Bytes; -use rlp::{RlpStream, Rlp}; use rand::OsRng; +use rlp::{Rlp, RlpStream}; +use snapshot::{block::AbridgedBlock, Error, ManifestData, Progress}; use types::encoded; /// Snapshot creation and restoration for PoW chains. @@ -42,146 +45,170 @@ use types::encoded; /// loose assurance that the chain is valid. #[derive(Clone, Copy, PartialEq)] pub struct PowSnapshot { - /// Number of blocks from the head of the chain - /// to include in the snapshot. - pub blocks: u64, - /// Number of to allow in the snapshot when restoring. - pub max_restore_blocks: u64, + /// Number of blocks from the head of the chain + /// to include in the snapshot. + pub blocks: u64, + /// Number of to allow in the snapshot when restoring. + pub max_restore_blocks: u64, } impl PowSnapshot { - /// Create a new instance. - pub fn new(blocks: u64, max_restore_blocks: u64) -> PowSnapshot { - PowSnapshot { - blocks: blocks, - max_restore_blocks: max_restore_blocks, - } - } + /// Create a new instance. + pub fn new(blocks: u64, max_restore_blocks: u64) -> PowSnapshot { + PowSnapshot { + blocks: blocks, + max_restore_blocks: max_restore_blocks, + } + } } impl SnapshotComponents for PowSnapshot { - fn chunk_all( - &mut self, - chain: &BlockChain, - block_at: H256, - chunk_sink: &mut ChunkSink, - progress: &Progress, - preferred_size: usize, - ) -> Result<(), Error> { - PowWorker { - chain: chain, - rlps: VecDeque::new(), - current_hash: block_at, - writer: chunk_sink, - progress: progress, - preferred_size: preferred_size, - }.chunk_all(self.blocks) - } - - fn rebuilder( - &self, - chain: BlockChain, - db: Arc, - manifest: &ManifestData, - ) -> Result, ::error::Error> { - PowRebuilder::new(chain, db.key_value().clone(), manifest, self.max_restore_blocks).map(|r| Box::new(r) as Box<_>) - } - - fn min_supported_version(&self) -> u64 { ::snapshot::MIN_SUPPORTED_STATE_CHUNK_VERSION } - fn current_version(&self) -> u64 { ::snapshot::STATE_CHUNK_VERSION } + fn chunk_all( + &mut self, + chain: &BlockChain, + block_at: H256, + chunk_sink: &mut ChunkSink, + progress: &Progress, + preferred_size: usize, + ) -> Result<(), Error> { + PowWorker { + chain: chain, + rlps: VecDeque::new(), + current_hash: block_at, + writer: chunk_sink, + progress: progress, + preferred_size: preferred_size, + } + .chunk_all(self.blocks) + } + + fn rebuilder( + &self, + chain: BlockChain, + db: Arc, + manifest: &ManifestData, + ) -> Result, ::error::Error> { + PowRebuilder::new( + chain, + db.key_value().clone(), + manifest, + self.max_restore_blocks, + ) + .map(|r| Box::new(r) as Box<_>) + } + + fn min_supported_version(&self) -> u64 { + ::snapshot::MIN_SUPPORTED_STATE_CHUNK_VERSION + } + fn current_version(&self) -> u64 { + ::snapshot::STATE_CHUNK_VERSION + } } /// Used to build block chunks. struct PowWorker<'a> { - chain: &'a BlockChain, - // block, receipt rlp pairs. - rlps: VecDeque, - current_hash: H256, - writer: &'a mut ChunkSink<'a>, - progress: &'a Progress, - preferred_size: usize, + chain: &'a BlockChain, + // block, receipt rlp pairs. + rlps: VecDeque, + current_hash: H256, + writer: &'a mut ChunkSink<'a>, + progress: &'a Progress, + preferred_size: usize, } impl<'a> PowWorker<'a> { - // Repeatedly fill the buffers and writes out chunks, moving backwards from starting block hash. - // Loops until we reach the first desired block, and writes out the remainder. - fn chunk_all(&mut self, snapshot_blocks: u64) -> Result<(), Error> { - let mut loaded_size = 0; - let mut last = self.current_hash; - - let genesis_hash = self.chain.genesis_hash(); - - for _ in 0..snapshot_blocks { - if self.current_hash == genesis_hash { break } - - let (block, receipts) = self.chain.block(&self.current_hash) - .and_then(|b| self.chain.block_receipts(&self.current_hash).map(|r| (b, r))) - .ok_or_else(|| Error::BlockNotFound(self.current_hash))?; - - let abridged_rlp = AbridgedBlock::from_block_view(&block.view()).into_inner(); - - let pair = { - let mut pair_stream = RlpStream::new_list(2); - pair_stream.append_raw(&abridged_rlp, 1).append(&receipts); - pair_stream.out() - }; - - let new_loaded_size = loaded_size + pair.len(); - - // cut off the chunk if too large. - - if new_loaded_size > self.preferred_size && !self.rlps.is_empty() { - self.write_chunk(last)?; - loaded_size = pair.len(); - } else { - loaded_size = new_loaded_size; - } - - self.rlps.push_front(pair); - - last = self.current_hash; - self.current_hash = block.header_view().parent_hash(); - self.progress.blocks.fetch_add(1, Ordering::SeqCst); - } - - if loaded_size != 0 { - self.write_chunk(last)?; - } - - Ok(()) - } - - // write out the data in the buffers to a chunk on disk - // - // we preface each chunk with the parent of the first block's details, - // obtained from the details of the last block written. - fn write_chunk(&mut self, last: H256) -> Result<(), Error> { - trace!(target: "snapshot", "prepared block chunk with {} blocks", self.rlps.len()); - - let (last_header, last_details) = self.chain.block_header_data(&last) - .and_then(|n| self.chain.block_details(&last).map(|d| (n, d))) - .ok_or_else(|| Error::BlockNotFound(last))?; - - let parent_number = last_header.number() - 1; - let parent_hash = last_header.parent_hash(); - let parent_total_difficulty = last_details.total_difficulty - last_header.difficulty(); - - trace!(target: "snapshot", "parent last written block: {}", parent_hash); - - let num_entries = self.rlps.len(); - let mut rlp_stream = RlpStream::new_list(3 + num_entries); - rlp_stream.append(&parent_number).append(&parent_hash).append(&parent_total_difficulty); - - for pair in self.rlps.drain(..) { - rlp_stream.append_raw(&pair, 1); - } - - let raw_data = rlp_stream.out(); - - (self.writer)(&raw_data)?; - - Ok(()) - } + // Repeatedly fill the buffers and writes out chunks, moving backwards from starting block hash. + // Loops until we reach the first desired block, and writes out the remainder. + fn chunk_all(&mut self, snapshot_blocks: u64) -> Result<(), Error> { + let mut loaded_size = 0; + let mut last = self.current_hash; + + let genesis_hash = self.chain.genesis_hash(); + + for _ in 0..snapshot_blocks { + if self.current_hash == genesis_hash { + break; + } + + let (block, receipts) = self + .chain + .block(&self.current_hash) + .and_then(|b| { + self.chain + .block_receipts(&self.current_hash) + .map(|r| (b, r)) + }) + .ok_or_else(|| Error::BlockNotFound(self.current_hash))?; + + let abridged_rlp = AbridgedBlock::from_block_view(&block.view()).into_inner(); + + let pair = { + let mut pair_stream = RlpStream::new_list(2); + pair_stream.append_raw(&abridged_rlp, 1).append(&receipts); + pair_stream.out() + }; + + let new_loaded_size = loaded_size + pair.len(); + + // cut off the chunk if too large. + + if new_loaded_size > self.preferred_size && !self.rlps.is_empty() { + self.write_chunk(last)?; + loaded_size = pair.len(); + } else { + loaded_size = new_loaded_size; + } + + self.rlps.push_front(pair); + + last = self.current_hash; + self.current_hash = block.header_view().parent_hash(); + self.progress.blocks.fetch_add(1, Ordering::SeqCst); + } + + if loaded_size != 0 { + self.write_chunk(last)?; + } + + Ok(()) + } + + // write out the data in the buffers to a chunk on disk + // + // we preface each chunk with the parent of the first block's details, + // obtained from the details of the last block written. + fn write_chunk(&mut self, last: H256) -> Result<(), Error> { + trace!(target: "snapshot", "prepared block chunk with {} blocks", self.rlps.len()); + + let (last_header, last_details) = self + .chain + .block_header_data(&last) + .and_then(|n| self.chain.block_details(&last).map(|d| (n, d))) + .ok_or_else(|| Error::BlockNotFound(last))?; + + let parent_number = last_header.number() - 1; + let parent_hash = last_header.parent_hash(); + let parent_total_difficulty = last_details.total_difficulty - last_header.difficulty(); + + trace!(target: "snapshot", "parent last written block: {}", parent_hash); + + let num_entries = self.rlps.len(); + let mut rlp_stream = RlpStream::new_list(3 + num_entries); + rlp_stream + .append(&parent_number) + .append(&parent_hash) + .append(&parent_total_difficulty); + + for pair in self.rlps.drain(..) { + rlp_stream.append_raw(&pair, 1); + } + + let raw_data = rlp_stream.out(); + + (self.writer)(&raw_data)?; + + Ok(()) + } } /// Rebuilder for proof-of-work chains. @@ -193,134 +220,167 @@ impl<'a> PowWorker<'a> { /// /// After all chunks have been submitted, we "glue" the chunks together. pub struct PowRebuilder { - chain: BlockChain, - db: Arc, - rng: OsRng, - disconnected: Vec<(u64, H256)>, - best_number: u64, - best_hash: H256, - best_root: H256, - fed_blocks: u64, - snapshot_blocks: u64, + chain: BlockChain, + db: Arc, + rng: OsRng, + disconnected: Vec<(u64, H256)>, + best_number: u64, + best_hash: H256, + best_root: H256, + fed_blocks: u64, + snapshot_blocks: u64, } impl PowRebuilder { - /// Create a new PowRebuilder. - fn new(chain: BlockChain, db: Arc, manifest: &ManifestData, snapshot_blocks: u64) -> Result { - Ok(PowRebuilder { - chain: chain, - db: db, - rng: OsRng::new()?, - disconnected: Vec::new(), - best_number: manifest.block_number, - best_hash: manifest.block_hash, - best_root: manifest.state_root, - fed_blocks: 0, - snapshot_blocks: snapshot_blocks, - }) - } + /// Create a new PowRebuilder. + fn new( + chain: BlockChain, + db: Arc, + manifest: &ManifestData, + snapshot_blocks: u64, + ) -> Result { + Ok(PowRebuilder { + chain: chain, + db: db, + rng: OsRng::new()?, + disconnected: Vec::new(), + best_number: manifest.block_number, + best_hash: manifest.block_hash, + best_root: manifest.state_root, + fed_blocks: 0, + snapshot_blocks: snapshot_blocks, + }) + } } impl Rebuilder for PowRebuilder { - /// Feed the rebuilder an uncompressed block chunk. - /// Returns the number of blocks fed or any errors. - fn feed(&mut self, chunk: &[u8], engine: &EthEngine, abort_flag: &AtomicBool) -> Result<(), ::error::Error> { - use snapshot::verify_old_block; - use ethereum_types::U256; - use triehash::ordered_trie_root; - - let rlp = Rlp::new(chunk); - let item_count = rlp.item_count()?; - let num_blocks = (item_count - 3) as u64; - - trace!(target: "snapshot", "restoring block chunk with {} blocks.", num_blocks); - - if self.fed_blocks + num_blocks > self.snapshot_blocks { - return Err(Error::TooManyBlocks(self.snapshot_blocks, self.fed_blocks + num_blocks).into()) - } - - // todo: assert here that these values are consistent with chunks being in order. - let mut cur_number = rlp.val_at::(0)? + 1; - let mut parent_hash = rlp.val_at::(1)?; - let parent_total_difficulty = rlp.val_at::(2)?; - - for idx in 3..item_count { - if !abort_flag.load(Ordering::SeqCst) { return Err(Error::RestorationAborted.into()) } - - let pair = rlp.at(idx)?; - let abridged_rlp = pair.at(0)?.as_raw().to_owned(); - let abridged_block = AbridgedBlock::from_raw(abridged_rlp); - let receipts: Vec<::types::receipt::Receipt> = pair.list_at(1)?; - let receipts_root = ordered_trie_root(pair.at(1)?.iter().map(|r| r.as_raw())); - - let block = abridged_block.to_block(parent_hash, cur_number, receipts_root)?; - let block_bytes = encoded::Block::new(block.rlp_bytes()); - let is_best = cur_number == self.best_number; - - if is_best { - if block.header.hash() != self.best_hash { - return Err(Error::WrongBlockHash(cur_number, self.best_hash, block.header.hash()).into()) - } - - if block.header.state_root() != &self.best_root { - return Err(Error::WrongStateRoot(self.best_root, *block.header.state_root()).into()) - } - } - - verify_old_block( - &mut self.rng, - &block.header, - engine, - &self.chain, - is_best - )?; - - let mut batch = self.db.transaction(); - - // special-case the first block in each chunk. - if idx == 3 { - if self.chain.insert_unordered_block(&mut batch, block_bytes, receipts, Some(parent_total_difficulty), is_best, false) { - self.disconnected.push((cur_number, block.header.hash())); - } - } else { - self.chain.insert_unordered_block(&mut batch, block_bytes, receipts, None, is_best, false); - } - self.db.write_buffered(batch); - self.chain.commit(); - - parent_hash = block.header.hash(); - cur_number += 1; - } - - self.fed_blocks += num_blocks; - - Ok(()) - } - - /// Glue together any disconnected chunks and check that the chain is complete. - fn finalize(&mut self, _: &EthEngine) -> Result<(), ::error::Error> { - let mut batch = self.db.transaction(); - - for (first_num, first_hash) in self.disconnected.drain(..) { - let parent_num = first_num - 1; - - // check if the parent is even in the chain. - // since we don't restore every single block in the chain, - // the first block of the first chunks has nothing to connect to. - if let Some(parent_hash) = self.chain.block_hash(parent_num) { - // if so, add the child to it. - self.chain.add_child(&mut batch, parent_hash, first_hash); - } - } - - let genesis_hash = self.chain.genesis_hash(); - self.chain.insert_epoch_transition(&mut batch, 0, ::engines::EpochTransition { - block_number: 0, - block_hash: genesis_hash, - proof: vec![], - }); - - self.db.write_buffered(batch); - Ok(()) - } + /// Feed the rebuilder an uncompressed block chunk. + /// Returns the number of blocks fed or any errors. + fn feed( + &mut self, + chunk: &[u8], + engine: &dyn EthEngine, + abort_flag: &AtomicBool, + ) -> Result<(), ::error::Error> { + use ethereum_types::U256; + use snapshot::verify_old_block; + use triehash::ordered_trie_root; + + let rlp = Rlp::new(chunk); + let item_count = rlp.item_count()?; + let num_blocks = (item_count - 3) as u64; + + trace!(target: "snapshot", "restoring block chunk with {} blocks.", num_blocks); + + if self.fed_blocks + num_blocks > self.snapshot_blocks { + return Err( + Error::TooManyBlocks(self.snapshot_blocks, self.fed_blocks + num_blocks).into(), + ); + } + + // todo: assert here that these values are consistent with chunks being in order. + let mut cur_number = rlp.val_at::(0)? + 1; + let mut parent_hash = rlp.val_at::(1)?; + let parent_total_difficulty = rlp.val_at::(2)?; + + for idx in 3..item_count { + if !abort_flag.load(Ordering::SeqCst) { + return Err(Error::RestorationAborted.into()); + } + + let pair = rlp.at(idx)?; + let abridged_rlp = pair.at(0)?.as_raw().to_owned(); + let abridged_block = AbridgedBlock::from_raw(abridged_rlp); + let receipts: Vec<::types::receipt::Receipt> = pair.list_at(1)?; + let receipts_root = ordered_trie_root(pair.at(1)?.iter().map(|r| r.as_raw())); + + let block = abridged_block.to_block(parent_hash, cur_number, receipts_root)?; + let block_bytes = encoded::Block::new(block.rlp_bytes()); + let is_best = cur_number == self.best_number; + + if is_best { + if block.header.hash() != self.best_hash { + return Err(Error::WrongBlockHash( + cur_number, + self.best_hash, + block.header.hash(), + ) + .into()); + } + + if block.header.state_root() != &self.best_root { + return Err( + Error::WrongStateRoot(self.best_root, *block.header.state_root()).into(), + ); + } + } + + verify_old_block(&mut self.rng, &block.header, engine, &self.chain, is_best)?; + + let mut batch = self.db.transaction(); + + // special-case the first block in each chunk. + if idx == 3 { + if self.chain.insert_unordered_block( + &mut batch, + block_bytes, + receipts, + Some(parent_total_difficulty), + is_best, + false, + ) { + self.disconnected.push((cur_number, block.header.hash())); + } + } else { + self.chain.insert_unordered_block( + &mut batch, + block_bytes, + receipts, + None, + is_best, + false, + ); + } + self.db.write_buffered(batch); + self.chain.commit(); + + parent_hash = block.header.hash(); + cur_number += 1; + } + + self.fed_blocks += num_blocks; + + Ok(()) + } + + /// Glue together any disconnected chunks and check that the chain is complete. + fn finalize(&mut self, _: &dyn EthEngine) -> Result<(), ::error::Error> { + let mut batch = self.db.transaction(); + + for (first_num, first_hash) in self.disconnected.drain(..) { + let parent_num = first_num - 1; + + // check if the parent is even in the chain. + // since we don't restore every single block in the chain, + // the first block of the first chunks has nothing to connect to. + if let Some(parent_hash) = self.chain.block_hash(parent_num) { + // if so, add the child to it. + self.chain.add_child(&mut batch, parent_hash, first_hash); + } + } + + let genesis_hash = self.chain.genesis_hash(); + self.chain.insert_epoch_transition( + &mut batch, + 0, + ::engines::EpochTransition { + block_number: 0, + block_hash: genesis_hash, + proof: vec![], + }, + ); + + self.db.write_buffered(batch); + Ok(()) + } } diff --git a/ethcore/src/snapshot/error.rs b/ethcore/src/snapshot/error.rs index b71f79f8054..914f01d32b8 100644 --- a/ethcore/src/snapshot/error.rs +++ b/ethcore/src/snapshot/error.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Snapshot-related errors. @@ -27,97 +27,125 @@ use rlp::DecoderError; /// Snapshot-related errors. #[derive(Debug)] pub enum Error { - /// Invalid starting block for snapshot. - InvalidStartingBlock(BlockId), - /// Block not found. - BlockNotFound(H256), - /// Incomplete chain. - IncompleteChain, - /// Best block has wrong state root. - WrongStateRoot(H256, H256), - /// Wrong block hash. - WrongBlockHash(u64, H256, H256), - /// Too many blocks contained within the snapshot. - TooManyBlocks(u64, u64), - /// Old starting block in a pruned database. - OldBlockPrunedDB, - /// Missing code. - MissingCode(Vec), - /// Unrecognized code encoding. - UnrecognizedCodeState(u8), - /// Restoration aborted. - RestorationAborted, - /// Trie error. - Trie(TrieError), - /// Decoder error. - Decoder(DecoderError), - /// Io error. - Io(::std::io::Error), - /// Snapshot version is not supported. - VersionNotSupported(u64), - /// Max chunk size is to small to fit basic account data. - ChunkTooSmall, - /// Oversized chunk - ChunkTooLarge, - /// Snapshots not supported by the consensus engine. - SnapshotsUnsupported, - /// Bad epoch transition. - BadEpochProof(u64), - /// Wrong chunk format. - WrongChunkFormat(String), - /// Unlinked ancient block chain - UnlinkedAncientBlockChain, + /// Invalid starting block for snapshot. + InvalidStartingBlock(BlockId), + /// Block not found. + BlockNotFound(H256), + /// Incomplete chain. + IncompleteChain, + /// Best block has wrong state root. + WrongStateRoot(H256, H256), + /// Wrong block hash. + WrongBlockHash(u64, H256, H256), + /// Too many blocks contained within the snapshot. + TooManyBlocks(u64, u64), + /// Old starting block in a pruned database. + OldBlockPrunedDB, + /// Missing code. + MissingCode(Vec), + /// Unrecognized code encoding. + UnrecognizedCodeState(u8), + /// Restoration aborted. + RestorationAborted, + /// Trie error. + Trie(TrieError), + /// Decoder error. + Decoder(DecoderError), + /// Io error. + Io(::std::io::Error), + /// Snapshot version is not supported. + VersionNotSupported(u64), + /// Max chunk size is to small to fit basic account data. + ChunkTooSmall, + /// Oversized chunk + ChunkTooLarge, + /// Snapshots not supported by the consensus engine. + SnapshotsUnsupported, + /// Aborted snapshot + SnapshotAborted, + /// Bad epoch transition. + BadEpochProof(u64), + /// Wrong chunk format. + WrongChunkFormat(String), + /// Unlinked ancient block chain + UnlinkedAncientBlockChain, } impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::InvalidStartingBlock(ref id) => write!(f, "Invalid starting block: {:?}", id), - Error::BlockNotFound(ref hash) => write!(f, "Block not found in chain: {}", hash), - Error::IncompleteChain => write!(f, "Incomplete blockchain."), - Error::WrongStateRoot(ref expected, ref found) => write!(f, "Final block has wrong state root. Expected {:?}, got {:?}", expected, found), - Error::WrongBlockHash(ref num, ref expected, ref found) => - write!(f, "Block {} had wrong hash. expected {:?}, got {:?}", num, expected, found), - Error::TooManyBlocks(ref expected, ref found) => write!(f, "Snapshot contained too many blocks. Expected {}, got {}", expected, found), - Error::OldBlockPrunedDB => write!(f, "Attempted to create a snapshot at an old block while using \ - a pruned database. Please re-run with the --pruning archive flag."), - Error::MissingCode(ref missing) => write!(f, "Incomplete snapshot: {} contract codes not found.", missing.len()), - Error::UnrecognizedCodeState(state) => write!(f, "Unrecognized code encoding ({})", state), - Error::RestorationAborted => write!(f, "Snapshot restoration aborted."), - Error::Io(ref err) => err.fmt(f), - Error::Decoder(ref err) => err.fmt(f), - Error::Trie(ref err) => err.fmt(f), - Error::VersionNotSupported(ref ver) => write!(f, "Snapshot version {} is not supprted.", ver), - Error::ChunkTooSmall => write!(f, "Chunk size is too small."), - Error::ChunkTooLarge => write!(f, "Chunk size is too large."), - Error::SnapshotsUnsupported => write!(f, "Snapshots unsupported by consensus engine."), - Error::BadEpochProof(i) => write!(f, "Bad epoch proof for transition to epoch {}", i), - Error::WrongChunkFormat(ref msg) => write!(f, "Wrong chunk format: {}", msg), - Error::UnlinkedAncientBlockChain => write!(f, "Unlinked ancient blocks chain"), - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::InvalidStartingBlock(ref id) => write!(f, "Invalid starting block: {:?}", id), + Error::BlockNotFound(ref hash) => write!(f, "Block not found in chain: {}", hash), + Error::IncompleteChain => write!(f, "Incomplete blockchain."), + Error::WrongStateRoot(ref expected, ref found) => write!( + f, + "Final block has wrong state root. Expected {:?}, got {:?}", + expected, found + ), + Error::WrongBlockHash(ref num, ref expected, ref found) => write!( + f, + "Block {} had wrong hash. expected {:?}, got {:?}", + num, expected, found + ), + Error::TooManyBlocks(ref expected, ref found) => write!( + f, + "Snapshot contained too many blocks. Expected {}, got {}", + expected, found + ), + Error::OldBlockPrunedDB => write!( + f, + "Attempted to create a snapshot at an old block while using \ + a pruned database. Please re-run with the --pruning archive flag." + ), + Error::MissingCode(ref missing) => write!( + f, + "Incomplete snapshot: {} contract codes not found.", + missing.len() + ), + Error::UnrecognizedCodeState(state) => { + write!(f, "Unrecognized code encoding ({})", state) + } + Error::RestorationAborted => write!(f, "Snapshot restoration aborted."), + Error::Io(ref err) => err.fmt(f), + Error::Decoder(ref err) => err.fmt(f), + Error::Trie(ref err) => err.fmt(f), + Error::VersionNotSupported(ref ver) => { + write!(f, "Snapshot version {} is not supprted.", ver) + } + Error::ChunkTooSmall => write!(f, "Chunk size is too small."), + Error::ChunkTooLarge => write!(f, "Chunk size is too large."), + Error::SnapshotsUnsupported => write!(f, "Snapshots unsupported by consensus engine."), + Error::SnapshotAborted => write!(f, "Snapshot was aborted."), + Error::BadEpochProof(i) => write!(f, "Bad epoch proof for transition to epoch {}", i), + Error::WrongChunkFormat(ref msg) => write!(f, "Wrong chunk format: {}", msg), + Error::UnlinkedAncientBlockChain => write!(f, "Unlinked ancient blocks chain"), + } + } } impl From<::std::io::Error> for Error { - fn from(err: ::std::io::Error) -> Self { - Error::Io(err) - } + fn from(err: ::std::io::Error) -> Self { + Error::Io(err) + } } impl From for Error { - fn from(err: TrieError) -> Self { - Error::Trie(err) - } + fn from(err: TrieError) -> Self { + Error::Trie(err) + } } impl From for Error { - fn from(err: DecoderError) -> Self { - Error::Decoder(err) - } + fn from(err: DecoderError) -> Self { + Error::Decoder(err) + } } -impl From> for Error where Error: From { - fn from(err: Box) -> Self { - Error::from(*err) - } +impl From> for Error +where + Error: From, +{ + fn from(err: Box) -> Self { + Error::from(*err) + } } diff --git a/ethcore/src/snapshot/io.rs b/ethcore/src/snapshot/io.rs index c5f178cd329..f1abb60be8b 100644 --- a/ethcore/src/snapshot/io.rs +++ b/ethcore/src/snapshot/io.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Snapshot i/o. //! Ways of writing and reading snapshots. This module supports writing and reading @@ -20,14 +20,16 @@ //! Packed snapshots are written to a single file, and loose snapshots are //! written to multiple files in one directory. -use std::collections::HashMap; -use std::io::{self, Read, Seek, SeekFrom, Write}; -use std::fs::{self, File}; -use std::path::{Path, PathBuf}; +use std::{ + collections::HashMap, + fs::{self, File}, + io::{self, Read, Seek, SeekFrom, Write}, + path::{Path, PathBuf}, +}; use bytes::Bytes; use ethereum_types::H256; -use rlp::{RlpStream, Rlp}; +use rlp::{Rlp, RlpStream}; use super::ManifestData; @@ -37,15 +39,17 @@ const SNAPSHOT_VERSION: u64 = 2; /// Writing the same chunk multiple times will lead to implementation-defined /// behavior, and is not advised. pub trait SnapshotWriter { - /// Write a compressed state chunk. - fn write_state_chunk(&mut self, hash: H256, chunk: &[u8]) -> io::Result<()>; + /// Write a compressed state chunk. + fn write_state_chunk(&mut self, hash: H256, chunk: &[u8]) -> io::Result<()>; - /// Write a compressed block chunk. - fn write_block_chunk(&mut self, hash: H256, chunk: &[u8]) -> io::Result<()>; + /// Write a compressed block chunk. + fn write_block_chunk(&mut self, hash: H256, chunk: &[u8]) -> io::Result<()>; - /// Complete writing. The manifest's chunk lists must be consistent - /// with the chunks written. - fn finish(self, manifest: ManifestData) -> io::Result<()> where Self: Sized; + /// Complete writing. The manifest's chunk lists must be consistent + /// with the chunks written. + fn finish(self, manifest: ManifestData) -> io::Result<()> + where + Self: Sized; } // (hash, len, offset) @@ -63,361 +67,369 @@ struct ChunkInfo(H256, u64, u64); /// but also maps chunk hashes to their lengths and offsets in the file /// for easy reading. pub struct PackedWriter { - file: File, - state_hashes: Vec, - block_hashes: Vec, - cur_len: u64, + file: File, + state_hashes: Vec, + block_hashes: Vec, + cur_len: u64, } impl PackedWriter { - /// Create a new "PackedWriter", to write into the file at the given path. - pub fn new(path: &Path) -> io::Result { - Ok(PackedWriter { - file: File::create(path)?, - state_hashes: Vec::new(), - block_hashes: Vec::new(), - cur_len: 0, - }) - } + /// Create a new "PackedWriter", to write into the file at the given path. + pub fn new(path: &Path) -> io::Result { + Ok(PackedWriter { + file: File::create(path)?, + state_hashes: Vec::new(), + block_hashes: Vec::new(), + cur_len: 0, + }) + } } impl SnapshotWriter for PackedWriter { - fn write_state_chunk(&mut self, hash: H256, chunk: &[u8]) -> io::Result<()> { - self.file.write_all(chunk)?; - - let len = chunk.len() as u64; - self.state_hashes.push(ChunkInfo(hash, len, self.cur_len)); - - self.cur_len += len; - Ok(()) - } - - fn write_block_chunk(&mut self, hash: H256, chunk: &[u8]) -> io::Result<()> { - self.file.write_all(chunk)?; - - let len = chunk.len() as u64; - self.block_hashes.push(ChunkInfo(hash, len, self.cur_len)); - - self.cur_len += len; - Ok(()) - } - - fn finish(mut self, manifest: ManifestData) -> io::Result<()> { - // we ignore the hashes fields of the manifest under the assumption that - // they are consistent with ours. - let mut stream = RlpStream::new_list(6); - stream - .append(&SNAPSHOT_VERSION) - .append_list(&self.state_hashes) - .append_list(&self.block_hashes) - .append(&manifest.state_root) - .append(&manifest.block_number) - .append(&manifest.block_hash); - - let manifest_rlp = stream.out(); - - self.file.write_all(&manifest_rlp)?; - let off = self.cur_len; - trace!(target: "snapshot_io", "writing manifest of len {} to offset {}", manifest_rlp.len(), off); - - let off_bytes: [u8; 8] = - [ - off as u8, - (off >> 8) as u8, - (off >> 16) as u8, - (off >> 24) as u8, - (off >> 32) as u8, - (off >> 40) as u8, - (off >> 48) as u8, - (off >> 56) as u8, - ]; - - self.file.write_all(&off_bytes[..])?; - - Ok(()) - } + fn write_state_chunk(&mut self, hash: H256, chunk: &[u8]) -> io::Result<()> { + self.file.write_all(chunk)?; + + let len = chunk.len() as u64; + self.state_hashes.push(ChunkInfo(hash, len, self.cur_len)); + + self.cur_len += len; + Ok(()) + } + + fn write_block_chunk(&mut self, hash: H256, chunk: &[u8]) -> io::Result<()> { + self.file.write_all(chunk)?; + + let len = chunk.len() as u64; + self.block_hashes.push(ChunkInfo(hash, len, self.cur_len)); + + self.cur_len += len; + Ok(()) + } + + fn finish(mut self, manifest: ManifestData) -> io::Result<()> { + // we ignore the hashes fields of the manifest under the assumption that + // they are consistent with ours. + let mut stream = RlpStream::new_list(6); + stream + .append(&SNAPSHOT_VERSION) + .append_list(&self.state_hashes) + .append_list(&self.block_hashes) + .append(&manifest.state_root) + .append(&manifest.block_number) + .append(&manifest.block_hash); + + let manifest_rlp = stream.out(); + + self.file.write_all(&manifest_rlp)?; + let off = self.cur_len; + trace!(target: "snapshot_io", "writing manifest of len {} to offset {}", manifest_rlp.len(), off); + + let off_bytes: [u8; 8] = [ + off as u8, + (off >> 8) as u8, + (off >> 16) as u8, + (off >> 24) as u8, + (off >> 32) as u8, + (off >> 40) as u8, + (off >> 48) as u8, + (off >> 56) as u8, + ]; + + self.file.write_all(&off_bytes[..])?; + + Ok(()) + } } /// A "loose" writer writes chunk files into a directory. pub struct LooseWriter { - dir: PathBuf, + dir: PathBuf, } impl LooseWriter { - /// Create a new LooseWriter which will write into the given directory, - /// creating it if it doesn't exist. - pub fn new(path: PathBuf) -> io::Result { - fs::create_dir_all(&path)?; - - Ok(LooseWriter { - dir: path, - }) - } - - // writing logic is the same for both kinds of chunks. - fn write_chunk(&mut self, hash: H256, chunk: &[u8]) -> io::Result<()> { - let file_path = self.dir.join(format!("{:x}", hash)); - let mut file = File::create(file_path)?; - file.write_all(chunk)?; - Ok(()) - } + /// Create a new LooseWriter which will write into the given directory, + /// creating it if it doesn't exist. + pub fn new(path: PathBuf) -> io::Result { + fs::create_dir_all(&path)?; + + Ok(LooseWriter { dir: path }) + } + + // writing logic is the same for both kinds of chunks. + fn write_chunk(&mut self, hash: H256, chunk: &[u8]) -> io::Result<()> { + let file_path = self.dir.join(format!("{:x}", hash)); + let mut file = File::create(file_path)?; + file.write_all(chunk)?; + Ok(()) + } } impl SnapshotWriter for LooseWriter { - fn write_state_chunk(&mut self, hash: H256, chunk: &[u8]) -> io::Result<()> { - self.write_chunk(hash, chunk) - } + fn write_state_chunk(&mut self, hash: H256, chunk: &[u8]) -> io::Result<()> { + self.write_chunk(hash, chunk) + } - fn write_block_chunk(&mut self, hash: H256, chunk: &[u8]) -> io::Result<()> { - self.write_chunk(hash, chunk) - } + fn write_block_chunk(&mut self, hash: H256, chunk: &[u8]) -> io::Result<()> { + self.write_chunk(hash, chunk) + } - fn finish(self, manifest: ManifestData) -> io::Result<()> { - let rlp = manifest.into_rlp(); - let mut path = self.dir.clone(); - path.push("MANIFEST"); + fn finish(self, manifest: ManifestData) -> io::Result<()> { + let rlp = manifest.into_rlp(); + let mut path = self.dir.clone(); + path.push("MANIFEST"); - let mut file = File::create(path)?; - file.write_all(&rlp[..])?; + let mut file = File::create(path)?; + file.write_all(&rlp[..])?; - Ok(()) - } + Ok(()) + } } /// Something which can read compressed snapshots. pub trait SnapshotReader { - /// Get the manifest data for this snapshot. - fn manifest(&self) -> &ManifestData; + /// Get the manifest data for this snapshot. + fn manifest(&self) -> &ManifestData; - /// Get raw chunk data by hash. implementation defined behavior - /// if a chunk not in the manifest is requested. - fn chunk(&self, hash: H256) -> io::Result; + /// Get raw chunk data by hash. implementation defined behavior + /// if a chunk not in the manifest is requested. + fn chunk(&self, hash: H256) -> io::Result; } /// Packed snapshot reader. pub struct PackedReader { - file: File, - state_hashes: HashMap, // len, offset - block_hashes: HashMap, // len, offset - manifest: ManifestData, + file: File, + state_hashes: HashMap, // len, offset + block_hashes: HashMap, // len, offset + manifest: ManifestData, } impl PackedReader { - /// Create a new `PackedReader` for the file at the given path. - /// This will fail if any io errors are encountered or the file - /// is not a valid packed snapshot. - pub fn new(path: &Path) -> Result, ::snapshot::error::Error> { - let mut file = File::open(path)?; - let file_len = file.metadata()?.len(); - if file_len < 8 { - // ensure we don't seek before beginning. - return Ok(None); - } - - file.seek(SeekFrom::End(-8))?; - let mut off_bytes = [0u8; 8]; - - file.read_exact(&mut off_bytes[..])?; - - let manifest_off: u64 = - ((off_bytes[7] as u64) << 56) + - ((off_bytes[6] as u64) << 48) + - ((off_bytes[5] as u64) << 40) + - ((off_bytes[4] as u64) << 32) + - ((off_bytes[3] as u64) << 24) + - ((off_bytes[2] as u64) << 16) + - ((off_bytes[1] as u64) << 8) + - (off_bytes[0] as u64); - - let manifest_len = file_len - manifest_off - 8; - trace!(target: "snapshot", "loading manifest of length {} from offset {}", manifest_len, manifest_off); - - let mut manifest_buf = vec![0; manifest_len as usize]; - - file.seek(SeekFrom::Start(manifest_off))?; - file.read_exact(&mut manifest_buf)?; - - let rlp = Rlp::new(&manifest_buf); - - let (start, version) = if rlp.item_count()? == 5 { - (0, 1) - } else { - (1, rlp.val_at(0)?) - }; - - if version > SNAPSHOT_VERSION { - return Err(::snapshot::error::Error::VersionNotSupported(version)); - } - - let state: Vec = rlp.list_at(0 + start)?; - let blocks: Vec = rlp.list_at(1 + start)?; - - let manifest = ManifestData { - version: version, - state_hashes: state.iter().map(|c| c.0).collect(), - block_hashes: blocks.iter().map(|c| c.0).collect(), - state_root: rlp.val_at(2 + start)?, - block_number: rlp.val_at(3 + start)?, - block_hash: rlp.val_at(4 + start)?, - }; - - Ok(Some(PackedReader { - file: file, - state_hashes: state.into_iter().map(|c| (c.0, (c.1, c.2))).collect(), - block_hashes: blocks.into_iter().map(|c| (c.0, (c.1, c.2))).collect(), - manifest: manifest - })) - } + /// Create a new `PackedReader` for the file at the given path. + /// This will fail if any io errors are encountered or the file + /// is not a valid packed snapshot. + pub fn new(path: &Path) -> Result, ::snapshot::error::Error> { + let mut file = File::open(path)?; + let file_len = file.metadata()?.len(); + if file_len < 8 { + // ensure we don't seek before beginning. + return Ok(None); + } + + file.seek(SeekFrom::End(-8))?; + let mut off_bytes = [0u8; 8]; + + file.read_exact(&mut off_bytes[..])?; + + let manifest_off: u64 = ((off_bytes[7] as u64) << 56) + + ((off_bytes[6] as u64) << 48) + + ((off_bytes[5] as u64) << 40) + + ((off_bytes[4] as u64) << 32) + + ((off_bytes[3] as u64) << 24) + + ((off_bytes[2] as u64) << 16) + + ((off_bytes[1] as u64) << 8) + + (off_bytes[0] as u64); + + let manifest_len = file_len - manifest_off - 8; + trace!(target: "snapshot", "loading manifest of length {} from offset {}", manifest_len, manifest_off); + + let mut manifest_buf = vec![0; manifest_len as usize]; + + file.seek(SeekFrom::Start(manifest_off))?; + file.read_exact(&mut manifest_buf)?; + + let rlp = Rlp::new(&manifest_buf); + + let (start, version) = if rlp.item_count()? == 5 { + (0, 1) + } else { + (1, rlp.val_at(0)?) + }; + + if version > SNAPSHOT_VERSION { + return Err(::snapshot::error::Error::VersionNotSupported(version)); + } + + let state: Vec = rlp.list_at(0 + start)?; + let blocks: Vec = rlp.list_at(1 + start)?; + + let manifest = ManifestData { + version: version, + state_hashes: state.iter().map(|c| c.0).collect(), + block_hashes: blocks.iter().map(|c| c.0).collect(), + state_root: rlp.val_at(2 + start)?, + block_number: rlp.val_at(3 + start)?, + block_hash: rlp.val_at(4 + start)?, + }; + + Ok(Some(PackedReader { + file: file, + state_hashes: state.into_iter().map(|c| (c.0, (c.1, c.2))).collect(), + block_hashes: blocks.into_iter().map(|c| (c.0, (c.1, c.2))).collect(), + manifest: manifest, + })) + } } impl SnapshotReader for PackedReader { - fn manifest(&self) -> &ManifestData { - &self.manifest - } + fn manifest(&self) -> &ManifestData { + &self.manifest + } - fn chunk(&self, hash: H256) -> io::Result { - let &(len, off) = self.state_hashes.get(&hash).or_else(|| self.block_hashes.get(&hash)) - .expect("only chunks in the manifest can be requested; qed"); + fn chunk(&self, hash: H256) -> io::Result { + let &(len, off) = self + .state_hashes + .get(&hash) + .or_else(|| self.block_hashes.get(&hash)) + .expect("only chunks in the manifest can be requested; qed"); - let mut file = &self.file; + let mut file = &self.file; - file.seek(SeekFrom::Start(off))?; - let mut buf = vec![0; len as usize]; + file.seek(SeekFrom::Start(off))?; + let mut buf = vec![0; len as usize]; - file.read_exact(&mut buf[..])?; + file.read_exact(&mut buf[..])?; - Ok(buf) - } + Ok(buf) + } } /// reader for "loose" snapshots pub struct LooseReader { - dir: PathBuf, - manifest: ManifestData, + dir: PathBuf, + manifest: ManifestData, } impl LooseReader { - /// Create a new `LooseReader` which will read the manifest and chunk data from - /// the given directory. - pub fn new(mut dir: PathBuf) -> Result { - let mut manifest_buf = Vec::new(); + /// Create a new `LooseReader` which will read the manifest and chunk data from + /// the given directory. + pub fn new(mut dir: PathBuf) -> Result { + let mut manifest_buf = Vec::new(); - dir.push("MANIFEST"); - let mut manifest_file = File::open(&dir)?; - manifest_file.read_to_end(&mut manifest_buf)?; + dir.push("MANIFEST"); + let mut manifest_file = File::open(&dir)?; + manifest_file.read_to_end(&mut manifest_buf)?; - let manifest = ManifestData::from_rlp(&manifest_buf[..])?; + let manifest = ManifestData::from_rlp(&manifest_buf[..])?; - dir.pop(); + dir.pop(); - Ok(LooseReader { - dir: dir, - manifest: manifest, - }) - } + Ok(LooseReader { dir, manifest }) + } } impl SnapshotReader for LooseReader { - fn manifest(&self) -> &ManifestData { - &self.manifest - } - - fn chunk(&self, hash: H256) -> io::Result { - let path = self.dir.join(format!("{:x}", hash)); - let mut buf = Vec::new(); - let mut file = File::open(&path)?; - file.read_to_end(&mut buf)?; - Ok(buf) - } + fn manifest(&self) -> &ManifestData { + &self.manifest + } + + fn chunk(&self, hash: H256) -> io::Result { + let path = self.dir.join(format!("{:x}", hash)); + let mut buf = Vec::new(); + let mut file = File::open(&path)?; + file.read_to_end(&mut buf)?; + Ok(buf) + } } #[cfg(test)] mod tests { - use tempdir::TempDir; - use hash::keccak; - - use snapshot::ManifestData; - use super::{SnapshotWriter, SnapshotReader, PackedWriter, PackedReader, LooseWriter, LooseReader, SNAPSHOT_VERSION}; - - const STATE_CHUNKS: &'static [&'static [u8]] = &[b"dog", b"cat", b"hello world", b"hi", b"notarealchunk"]; - const BLOCK_CHUNKS: &'static [&'static [u8]] = &[b"hello!", b"goodbye!", b"abcdefg", b"hijklmnop", b"qrstuvwxy", b"and", b"z"]; - - #[test] - fn packed_write_and_read() { - let tempdir = TempDir::new("").unwrap(); - let path = tempdir.path().join("packed"); - let mut writer = PackedWriter::new(&path).unwrap(); - - let mut state_hashes = Vec::new(); - let mut block_hashes = Vec::new(); - - for chunk in STATE_CHUNKS { - let hash = keccak(&chunk); - state_hashes.push(hash.clone()); - writer.write_state_chunk(hash, chunk).unwrap(); - } - - for chunk in BLOCK_CHUNKS { - let hash = keccak(&chunk); - block_hashes.push(hash.clone()); - writer.write_block_chunk(keccak(&chunk), chunk).unwrap(); - } - - let manifest = ManifestData { - version: SNAPSHOT_VERSION, - state_hashes: state_hashes, - block_hashes: block_hashes, - state_root: keccak(b"notarealroot"), - block_number: 12345678987654321, - block_hash: keccak(b"notarealblock"), - }; - - writer.finish(manifest.clone()).unwrap(); - - let reader = PackedReader::new(&path).unwrap().unwrap(); - assert_eq!(reader.manifest(), &manifest); - - for hash in manifest.state_hashes.iter().chain(&manifest.block_hashes) { - reader.chunk(hash.clone()).unwrap(); - } - } - - #[test] - fn loose_write_and_read() { - let tempdir = TempDir::new("").unwrap(); - let mut writer = LooseWriter::new(tempdir.path().into()).unwrap(); - - let mut state_hashes = Vec::new(); - let mut block_hashes = Vec::new(); - - for chunk in STATE_CHUNKS { - let hash = keccak(&chunk); - state_hashes.push(hash.clone()); - writer.write_state_chunk(hash, chunk).unwrap(); - } - - for chunk in BLOCK_CHUNKS { - let hash = keccak(&chunk); - block_hashes.push(hash.clone()); - writer.write_block_chunk(keccak(&chunk), chunk).unwrap(); - } - - let manifest = ManifestData { - version: SNAPSHOT_VERSION, - state_hashes: state_hashes, - block_hashes: block_hashes, - state_root: keccak(b"notarealroot"), - block_number: 12345678987654321, - block_hash: keccak(b"notarealblock)"), - }; - - writer.finish(manifest.clone()).unwrap(); - - let reader = LooseReader::new(tempdir.path().into()).unwrap(); - assert_eq!(reader.manifest(), &manifest); - - for hash in manifest.state_hashes.iter().chain(&manifest.block_hashes) { - reader.chunk(hash.clone()).unwrap(); - } - } + use hash::keccak; + use tempdir::TempDir; + + use super::{ + LooseReader, LooseWriter, PackedReader, PackedWriter, SnapshotReader, SnapshotWriter, + SNAPSHOT_VERSION, + }; + use snapshot::ManifestData; + + const STATE_CHUNKS: &'static [&'static [u8]] = + &[b"dog", b"cat", b"hello world", b"hi", b"notarealchunk"]; + const BLOCK_CHUNKS: &'static [&'static [u8]] = &[ + b"hello!", + b"goodbye!", + b"abcdefg", + b"hijklmnop", + b"qrstuvwxy", + b"and", + b"z", + ]; + + #[test] + fn packed_write_and_read() { + let tempdir = TempDir::new("").unwrap(); + let path = tempdir.path().join("packed"); + let mut writer = PackedWriter::new(&path).unwrap(); + + let mut state_hashes = Vec::new(); + let mut block_hashes = Vec::new(); + + for chunk in STATE_CHUNKS { + let hash = keccak(&chunk); + state_hashes.push(hash.clone()); + writer.write_state_chunk(hash, chunk).unwrap(); + } + + for chunk in BLOCK_CHUNKS { + let hash = keccak(&chunk); + block_hashes.push(hash.clone()); + writer.write_block_chunk(keccak(&chunk), chunk).unwrap(); + } + + let manifest = ManifestData { + version: SNAPSHOT_VERSION, + state_hashes: state_hashes, + block_hashes: block_hashes, + state_root: keccak(b"notarealroot"), + block_number: 12345678987654321, + block_hash: keccak(b"notarealblock"), + }; + + writer.finish(manifest.clone()).unwrap(); + + let reader = PackedReader::new(&path).unwrap().unwrap(); + assert_eq!(reader.manifest(), &manifest); + + for hash in manifest.state_hashes.iter().chain(&manifest.block_hashes) { + reader.chunk(hash.clone()).unwrap(); + } + } + + #[test] + fn loose_write_and_read() { + let tempdir = TempDir::new("").unwrap(); + let mut writer = LooseWriter::new(tempdir.path().into()).unwrap(); + + let mut state_hashes = Vec::new(); + let mut block_hashes = Vec::new(); + + for chunk in STATE_CHUNKS { + let hash = keccak(&chunk); + state_hashes.push(hash.clone()); + writer.write_state_chunk(hash, chunk).unwrap(); + } + + for chunk in BLOCK_CHUNKS { + let hash = keccak(&chunk); + block_hashes.push(hash.clone()); + writer.write_block_chunk(keccak(&chunk), chunk).unwrap(); + } + + let manifest = ManifestData { + version: SNAPSHOT_VERSION, + state_hashes: state_hashes, + block_hashes: block_hashes, + state_root: keccak(b"notarealroot"), + block_number: 12345678987654321, + block_hash: keccak(b"notarealblock)"), + }; + + writer.finish(manifest.clone()).unwrap(); + + let reader = LooseReader::new(tempdir.path().into()).unwrap(); + assert_eq!(reader.manifest(), &manifest); + + for hash in manifest.state_hashes.iter().chain(&manifest.block_hashes) { + reader.chunk(hash.clone()).unwrap(); + } + } } diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index 19a5f8ce6e0..5ffd3cff2de 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -1,67 +1,69 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Snapshot creation, restoration, and network service. //! //! Documentation of the format can be found at -//! https://wiki.parity.io/Warp-Sync-Snapshot-Format - -use std::collections::{HashMap, HashSet}; -use std::cmp; -use std::sync::Arc; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use hash::{keccak, KECCAK_NULL_RLP, KECCAK_EMPTY}; +//! https://openethereum.github.io/Warp-Sync-Snapshot-Format + +use hash::{keccak, KECCAK_EMPTY, KECCAK_NULL_RLP}; +use std::{ + cmp, + collections::{HashMap, HashSet}, + sync::{ + atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering}, + Arc, + }, +}; use account_db::{AccountDB, AccountDBMut}; use blockchain::{BlockChain, BlockProvider}; use engines::EthEngine; -use types::header::Header; -use types::ids::BlockId; +use types::{header::Header, ids::BlockId}; -use ethereum_types::{H256, U256}; +use bytes::Bytes; +use ethereum_types::H256; +use ethtrie::{TrieDB, TrieDBMut}; use hash_db::HashDB; +use journaldb::{self, Algorithm, JournalDB}; use keccak_hasher::KeccakHasher; -use snappy; -use bytes::Bytes; +use kvdb::{DBValue, KeyValueDB}; +use num_cpus; use parking_lot::Mutex; -use journaldb::{self, Algorithm, JournalDB}; -use kvdb::{KeyValueDB, DBValue}; +use rlp::{Rlp, RlpStream}; +use snappy; use trie::{Trie, TrieMut}; -use ethtrie::{TrieDB, TrieDBMut}; -use rlp::{RlpStream, Rlp}; -use bloom_journal::Bloom; -use num_cpus; use self::io::SnapshotWriter; -use super::state_db::StateDB; -use super::state::Account as StateAccount; - -use crossbeam::scope; -use rand::{Rng, OsRng}; +use crossbeam_utils::thread; +use rand::{OsRng, Rng}; pub use self::error::Error; -pub use self::consensus::*; -pub use self::service::{SnapshotClient, Service, DatabaseRestore}; -pub use self::traits::SnapshotService; -pub use self::watcher::Watcher; -pub use types::snapshot_manifest::ManifestData; -pub use types::restoration_status::RestorationStatus; -pub use types::basic_account::BasicAccount; +pub use self::{ + consensus::*, + service::{DatabaseRestore, Service, SnapshotClient}, + traits::SnapshotService, + watcher::Watcher, +}; +pub use types::{ + basic_account::BasicAccount, creation_status::CreationStatus, + restoration_status::RestorationStatus, snapshot_manifest::ManifestData, +}; pub mod io; pub mod service; @@ -97,78 +99,89 @@ const MAX_SNAPSHOT_SUBPARTS: usize = 256; /// Configuration for the Snapshot service #[derive(Debug, Clone, PartialEq)] pub struct SnapshotConfiguration { - /// If `true`, no periodic snapshots will be created - pub no_periodic: bool, - /// Number of threads for creating snapshots - pub processing_threads: usize, + /// Enable creation of periodic snapshots + pub enable: bool, + /// Number of threads for creating snapshots + pub processing_threads: usize, } impl Default for SnapshotConfiguration { - fn default() -> Self { - SnapshotConfiguration { - no_periodic: false, - processing_threads: ::std::cmp::max(1, num_cpus::get() / 2), - } - } + fn default() -> Self { + SnapshotConfiguration { + enable: false, + processing_threads: ::std::cmp::max(1, num_cpus::get_physical() / 2), + } + } } /// A progress indicator for snapshots. #[derive(Debug, Default)] pub struct Progress { - accounts: AtomicUsize, - blocks: AtomicUsize, - size: AtomicUsize, // Todo [rob] use Atomicu64 when it stabilizes. - done: AtomicBool, + accounts: AtomicUsize, + blocks: AtomicUsize, + size: AtomicU64, + done: AtomicBool, + abort: AtomicBool, } impl Progress { - /// Reset the progress. - pub fn reset(&self) { - self.accounts.store(0, Ordering::Release); - self.blocks.store(0, Ordering::Release); - self.size.store(0, Ordering::Release); - - // atomic fence here to ensure the others are written first? - // logs might very rarely get polluted if not. - self.done.store(false, Ordering::Release); - } - - /// Get the number of accounts snapshotted thus far. - pub fn accounts(&self) -> usize { self.accounts.load(Ordering::Acquire) } - - /// Get the number of blocks snapshotted thus far. - pub fn blocks(&self) -> usize { self.blocks.load(Ordering::Acquire) } - - /// Get the written size of the snapshot in bytes. - pub fn size(&self) -> usize { self.size.load(Ordering::Acquire) } - - /// Whether the snapshot is complete. - pub fn done(&self) -> bool { self.done.load(Ordering::Acquire) } - + /// Reset the progress. + pub fn reset(&self) { + self.accounts.store(0, Ordering::Release); + self.blocks.store(0, Ordering::Release); + self.size.store(0, Ordering::Release); + self.abort.store(false, Ordering::Release); + + // atomic fence here to ensure the others are written first? + // logs might very rarely get polluted if not. + self.done.store(false, Ordering::Release); + } + + /// Get the number of accounts snapshotted thus far. + pub fn accounts(&self) -> usize { + self.accounts.load(Ordering::Acquire) + } + + /// Get the number of blocks snapshotted thus far. + pub fn blocks(&self) -> usize { + self.blocks.load(Ordering::Acquire) + } + + /// Get the written size of the snapshot in bytes. + pub fn size(&self) -> u64 { + self.size.load(Ordering::Acquire) + } + + /// Whether the snapshot is complete. + pub fn done(&self) -> bool { + self.done.load(Ordering::Acquire) + } } /// Take a snapshot using the given blockchain, starting block hash, and database, writing into the given writer. pub fn take_snapshot( - engine: &EthEngine, - chain: &BlockChain, - block_at: H256, - state_db: &HashDB, - writer: W, - p: &Progress, - processing_threads: usize, + chunker: Box, + chain: &BlockChain, + block_hash: H256, + state_db: &dyn HashDB, + writer: W, + p: &Progress, + processing_threads: usize, ) -> Result<(), Error> { - let start_header = chain.block_header_data(&block_at) - .ok_or_else(|| Error::InvalidStartingBlock(BlockId::Hash(block_at)))?; - let state_root = start_header.state_root(); - let number = start_header.number(); + let start_header = chain + .block_header_data(&block_hash) + .ok_or_else(|| Error::InvalidStartingBlock(BlockId::Hash(block_hash)))?; + let state_root = start_header.state_root(); + let block_number = start_header.number(); - info!("Taking snapshot starting at block {}", number); + info!("Taking snapshot starting at block {}", block_number); - let writer = Mutex::new(writer); - let chunker = engine.snapshot_components().ok_or(Error::SnapshotsUnsupported)?; - let snapshot_version = chunker.current_version(); - let (state_hashes, block_hashes) = scope(|scope| -> Result<(Vec, Vec), Error> { + let version = chunker.current_version(); + let writer = Mutex::new(writer); + let (state_hashes, block_hashes) = thread::scope(|scope| -> Result<(Vec, Vec), Error> { let writer = &writer; - let block_guard = scope.spawn(move || chunk_secondary(chunker, chain, block_at, writer, p)); + let block_guard = scope.spawn(move |_| { + chunk_secondary(chunker, chain, block_hash, writer, p) + }); // The number of threads must be between 1 and SNAPSHOT_SUBPARTS assert!(processing_threads >= 1, "Cannot use less than 1 threads for creating snapshots"); @@ -178,12 +191,12 @@ pub fn take_snapshot( let mut state_guards = Vec::with_capacity(num_threads as usize); for thread_idx in 0..num_threads { - let state_guard = scope.spawn(move || -> Result, Error> { + let state_guard = scope.spawn(move |_| -> Result, Error> { let mut chunk_hashes = Vec::new(); for part in (thread_idx..SNAPSHOT_SUBPARTS).step_by(num_threads) { debug!(target: "snapshot", "Chunking part {} in thread {}", part, thread_idx); - let mut hashes = chunk_state(state_db, &state_root, writer, p, Some(part))?; + let mut hashes = chunk_state(state_db, &state_root, writer, p, Some(part), thread_idx)?; chunk_hashes.append(&mut hashes); } @@ -202,24 +215,24 @@ pub fn take_snapshot( debug!(target: "snapshot", "Took a snapshot of {} accounts", p.accounts.load(Ordering::SeqCst)); Ok((state_hashes, block_hashes)) - })?; + }).expect("Sub-thread never panics; qed")?; - info!(target: "snapshot", "produced {} state chunks and {} block chunks.", state_hashes.len(), block_hashes.len()); + info!(target: "snapshot", "produced {} state chunks and {} block chunks.", state_hashes.len(), block_hashes.len()); - let manifest_data = ManifestData { - version: snapshot_version, - state_hashes: state_hashes, - block_hashes: block_hashes, - state_root: state_root, - block_number: number, - block_hash: block_at, - }; + let manifest_data = ManifestData { + version, + state_hashes, + block_hashes, + state_root, + block_number, + block_hash, + }; - writer.into_inner().finish(manifest_data)?; + writer.into_inner().finish(manifest_data)?; - p.done.store(true, Ordering::SeqCst); + p.done.store(true, Ordering::SeqCst); - Ok(()) + Ok(()) } /// Create and write out all secondary chunks to disk, returning a vector of all @@ -228,90 +241,101 @@ pub fn take_snapshot( /// Secondary chunks are engine-specific, but they intend to corroborate the state data /// in the state chunks. /// Returns a list of chunk hashes, with the first having the blocks furthest from the genesis. -pub fn chunk_secondary<'a>(mut chunker: Box, chain: &'a BlockChain, start_hash: H256, writer: &Mutex, progress: &'a Progress) -> Result, Error> { - let mut chunk_hashes = Vec::new(); - let mut snappy_buffer = vec![0; snappy::max_compressed_len(PREFERRED_CHUNK_SIZE)]; - - { - let mut chunk_sink = |raw_data: &[u8]| { - let compressed_size = snappy::compress_into(raw_data, &mut snappy_buffer); - let compressed = &snappy_buffer[..compressed_size]; - let hash = keccak(&compressed); - let size = compressed.len(); - - writer.lock().write_block_chunk(hash, compressed)?; - trace!(target: "snapshot", "wrote secondary chunk. hash: {:x}, size: {}, uncompressed size: {}", +pub fn chunk_secondary<'a>( + mut chunker: Box, + chain: &'a BlockChain, + start_hash: H256, + writer: &Mutex, + progress: &'a Progress, +) -> Result, Error> { + let mut chunk_hashes = Vec::new(); + let mut snappy_buffer = vec![0; snappy::max_compressed_len(PREFERRED_CHUNK_SIZE)]; + + { + let mut chunk_sink = |raw_data: &[u8]| { + let compressed_size = snappy::compress_into(raw_data, &mut snappy_buffer); + let compressed = &snappy_buffer[..compressed_size]; + let hash = keccak(&compressed); + let size = compressed.len(); + + writer.lock().write_block_chunk(hash, compressed)?; + trace!(target: "snapshot", "wrote secondary chunk. hash: {:x}, size: {}, uncompressed size: {}", hash, size, raw_data.len()); - progress.size.fetch_add(size, Ordering::SeqCst); - chunk_hashes.push(hash); - Ok(()) - }; - - chunker.chunk_all( - chain, - start_hash, - &mut chunk_sink, - progress, - PREFERRED_CHUNK_SIZE, - )?; - } - - Ok(chunk_hashes) + progress.size.fetch_add(size as u64, Ordering::SeqCst); + chunk_hashes.push(hash); + Ok(()) + }; + + chunker.chunk_all( + chain, + start_hash, + &mut chunk_sink, + progress, + PREFERRED_CHUNK_SIZE, + )?; + } + + Ok(chunk_hashes) } /// State trie chunker. struct StateChunker<'a> { - hashes: Vec, - rlps: Vec, - cur_size: usize, - snappy_buffer: Vec, - writer: &'a Mutex, - progress: &'a Progress, + hashes: Vec, + rlps: Vec, + cur_size: usize, + snappy_buffer: Vec, + writer: &'a Mutex, + progress: &'a Progress, + thread_idx: usize, } impl<'a> StateChunker<'a> { - // Push a key, value pair to be encoded. - // - // If the buffer is greater than the desired chunk size, - // this will write out the data to disk. - fn push(&mut self, data: Bytes) -> Result<(), Error> { - self.cur_size += data.len(); - self.rlps.push(data); - Ok(()) - } - - // Write out the buffer to disk, pushing the created chunk's hash to - // the list. - fn write_chunk(&mut self) -> Result<(), Error> { - let num_entries = self.rlps.len(); - let mut stream = RlpStream::new_list(num_entries); - for rlp in self.rlps.drain(..) { - stream.append_raw(&rlp, 1); - } - - let raw_data = stream.out(); - - let compressed_size = snappy::compress_into(&raw_data, &mut self.snappy_buffer); - let compressed = &self.snappy_buffer[..compressed_size]; - let hash = keccak(&compressed); - - self.writer.lock().write_state_chunk(hash, compressed)?; - trace!(target: "snapshot", "wrote state chunk. size: {}, uncompressed size: {}", compressed_size, raw_data.len()); - - self.progress.accounts.fetch_add(num_entries, Ordering::SeqCst); - self.progress.size.fetch_add(compressed_size, Ordering::SeqCst); - - self.hashes.push(hash); - self.cur_size = 0; - - Ok(()) - } - - // Get current chunk size. - fn chunk_size(&self) -> usize { - self.cur_size - } + // Push a key, value pair to be encoded. + // + // If the buffer is greater than the desired chunk size, + // this will write out the data to disk. + fn push(&mut self, data: Bytes) -> Result<(), Error> { + self.cur_size += data.len(); + self.rlps.push(data); + Ok(()) + } + + // Write out the buffer to disk, pushing the created chunk's hash to + // the list. + fn write_chunk(&mut self) -> Result<(), Error> { + let num_entries = self.rlps.len(); + let mut stream = RlpStream::new_list(num_entries); + for rlp in self.rlps.drain(..) { + stream.append_raw(&rlp, 1); + } + + let raw_data = stream.out(); + + let compressed_size = snappy::compress_into(&raw_data, &mut self.snappy_buffer); + let compressed = &self.snappy_buffer[..compressed_size]; + let hash = keccak(&compressed); + + self.writer.lock().write_state_chunk(hash, compressed)?; + trace!(target: "snapshot", "Thread {} wrote state chunk. size: {}, uncompressed size: {}", self.thread_idx, compressed_size, raw_data.len()); + + self.progress + .accounts + .fetch_add(num_entries, Ordering::SeqCst); + self.progress + .size + .fetch_add(compressed_size as u64, Ordering::SeqCst); + + self.hashes.push(hash); + self.cur_size = 0; + + Ok(()) + } + + // Get current chunk size. + fn chunk_size(&self) -> usize { + self.cur_size + } } /// Walk the given state database starting from the given root, @@ -321,237 +345,262 @@ impl<'a> StateChunker<'a> { /// /// Returns a list of hashes of chunks created, or any error it may /// have encountered. -pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex, progress: &'a Progress, part: Option) -> Result, Error> { - let account_trie = TrieDB::new(&db, &root)?; - - let mut chunker = StateChunker { - hashes: Vec::new(), - rlps: Vec::new(), - cur_size: 0, - snappy_buffer: vec![0; snappy::max_compressed_len(PREFERRED_CHUNK_SIZE)], - writer: writer, - progress: progress, - }; - - let mut used_code = HashSet::new(); - - // account_key here is the address' hash. - let mut account_iter = account_trie.iter()?; - - let mut seek_to = None; - - if let Some(part) = part { - assert!(part < 16, "Wrong chunk state part number (must be <16) in snapshot creation."); - - let part_offset = MAX_SNAPSHOT_SUBPARTS / SNAPSHOT_SUBPARTS; - let mut seek_from = vec![0; 32]; - seek_from[0] = (part * part_offset) as u8; - account_iter.seek(&seek_from)?; - - // Set the upper-bound, except for the last part - if part < SNAPSHOT_SUBPARTS - 1 { - seek_to = Some(((part + 1) * part_offset) as u8) - } - } - - for item in account_iter { - let (account_key, account_data) = item?; - let account_key_hash = H256::from_slice(&account_key); - - if seek_to.map_or(false, |seek_to| account_key[0] >= seek_to) { - break; - } - - let account = ::rlp::decode(&*account_data)?; - let account_db = AccountDB::from_hash(db, account_key_hash); - - let fat_rlps = account::to_fat_rlps(&account_key_hash, &account, &account_db, &mut used_code, PREFERRED_CHUNK_SIZE - chunker.chunk_size(), PREFERRED_CHUNK_SIZE)?; - for (i, fat_rlp) in fat_rlps.into_iter().enumerate() { - if i > 0 { - chunker.write_chunk()?; - } - chunker.push(fat_rlp)?; - } - } - - if chunker.cur_size != 0 { - chunker.write_chunk()?; - } - - Ok(chunker.hashes) +pub fn chunk_state<'a>( + db: &dyn HashDB, + root: &H256, + writer: &Mutex, + progress: &'a Progress, + part: Option, + thread_idx: usize, +) -> Result, Error> { + let account_trie = TrieDB::new(&db, &root)?; + + let mut chunker = StateChunker { + hashes: Vec::new(), + rlps: Vec::new(), + cur_size: 0, + snappy_buffer: vec![0; snappy::max_compressed_len(PREFERRED_CHUNK_SIZE)], + writer, + progress, + thread_idx, + }; + + let mut used_code = HashSet::new(); + + // account_key here is the address' hash. + let mut account_iter = account_trie.iter()?; + + let mut seek_to = None; + + if let Some(part) = part { + assert!( + part < 16, + "Wrong chunk state part number (must be <16) in snapshot creation." + ); + + let part_offset = MAX_SNAPSHOT_SUBPARTS / SNAPSHOT_SUBPARTS; + let mut seek_from = vec![0; 32]; + seek_from[0] = (part * part_offset) as u8; + account_iter.seek(&seek_from)?; + + // Set the upper-bound, except for the last part + if part < SNAPSHOT_SUBPARTS - 1 { + seek_to = Some(((part + 1) * part_offset) as u8) + } + } + + for item in account_iter { + let (account_key, account_data) = item?; + let account_key_hash = H256::from_slice(&account_key); + + if seek_to.map_or(false, |seek_to| account_key[0] >= seek_to) { + break; + } + + let account = ::rlp::decode(&*account_data)?; + let account_db = AccountDB::from_hash(db, account_key_hash); + + let fat_rlps = account::to_fat_rlps( + &account_key_hash, + &account, + &account_db, + &mut used_code, + PREFERRED_CHUNK_SIZE - chunker.chunk_size(), + PREFERRED_CHUNK_SIZE, + progress, + )?; + for (i, fat_rlp) in fat_rlps.into_iter().enumerate() { + if i > 0 { + chunker.write_chunk()?; + } + chunker.push(fat_rlp)?; + } + } + + if chunker.cur_size != 0 { + chunker.write_chunk()?; + } + + Ok(chunker.hashes) } /// Used to rebuild the state trie piece by piece. pub struct StateRebuilder { - db: Box, - state_root: H256, - known_code: HashMap, // code hashes mapped to first account with this code. - missing_code: HashMap>, // maps code hashes to lists of accounts missing that code. - bloom: Bloom, - known_storage_roots: HashMap, // maps account hashes to last known storage root. Only filled for last account per chunk. + db: Box, + state_root: H256, + known_code: HashMap, // code hashes mapped to first account with this code. + missing_code: HashMap>, // maps code hashes to lists of accounts missing that code. + known_storage_roots: HashMap, // maps account hashes to last known storage root. Only filled for last account per chunk. } impl StateRebuilder { - /// Create a new state rebuilder to write into the given backing DB. - pub fn new(db: Arc, pruning: Algorithm) -> Self { - StateRebuilder { - db: journaldb::new(db.clone(), pruning, ::db::COL_STATE), - state_root: KECCAK_NULL_RLP, - known_code: HashMap::new(), - missing_code: HashMap::new(), - bloom: StateDB::load_bloom(&*db), - known_storage_roots: HashMap::new(), - } - } - - /// Feed an uncompressed state chunk into the rebuilder. - pub fn feed(&mut self, chunk: &[u8], flag: &AtomicBool) -> Result<(), ::error::Error> { - let rlp = Rlp::new(chunk); - let empty_rlp = StateAccount::new_basic(U256::zero(), U256::zero()).rlp(); - let mut pairs = Vec::with_capacity(rlp.item_count()?); - - // initialize the pairs vector with empty values so we have slots to write into. - pairs.resize(rlp.item_count()?, (H256::new(), Vec::new())); - - let status = rebuild_accounts( - self.db.as_hash_db_mut(), - rlp, - &mut pairs, - &self.known_code, - &mut self.known_storage_roots, - flag - )?; - - for (addr_hash, code_hash) in status.missing_code { - self.missing_code.entry(code_hash).or_insert_with(Vec::new).push(addr_hash); - } - - // patch up all missing code. must be done after collecting all new missing code entries. - for (code_hash, code, first_with) in status.new_code { - for addr_hash in self.missing_code.remove(&code_hash).unwrap_or_else(Vec::new) { - let mut db = AccountDBMut::from_hash(self.db.as_hash_db_mut(), addr_hash); - db.emplace(code_hash, DBValue::from_slice(&code)); - } - - self.known_code.insert(code_hash, first_with); - } - - let backing = self.db.backing().clone(); - - // batch trie writes - { - let mut account_trie = if self.state_root != KECCAK_NULL_RLP { - TrieDBMut::from_existing(self.db.as_hash_db_mut(), &mut self.state_root)? - } else { - TrieDBMut::new(self.db.as_hash_db_mut(), &mut self.state_root) - }; - - for (hash, thin_rlp) in pairs { - if !flag.load(Ordering::SeqCst) { return Err(Error::RestorationAborted.into()) } - - if &thin_rlp[..] != &empty_rlp[..] { - self.bloom.set(&*hash); - } - account_trie.insert(&hash, &thin_rlp)?; - } - } - - let bloom_journal = self.bloom.drain_journal(); - let mut batch = backing.transaction(); - StateDB::commit_bloom(&mut batch, bloom_journal)?; - self.db.inject(&mut batch)?; - backing.write_buffered(batch); - trace!(target: "snapshot", "current state root: {:?}", self.state_root); - Ok(()) - } - - /// Finalize the restoration. Check for accounts missing code and make a dummy - /// journal entry. - /// Once all chunks have been fed, there should be nothing missing. - pub fn finalize(mut self, era: u64, id: H256) -> Result, ::error::Error> { - let missing = self.missing_code.keys().cloned().collect::>(); - if !missing.is_empty() { return Err(Error::MissingCode(missing).into()) } - - let mut batch = self.db.backing().transaction(); - self.db.journal_under(&mut batch, era, &id)?; - self.db.backing().write_buffered(batch); - - Ok(self.db) - } - - /// Get the state root of the rebuilder. - pub fn state_root(&self) -> H256 { self.state_root } + /// Create a new state rebuilder to write into the given backing DB. + pub fn new(db: Arc, pruning: Algorithm) -> Self { + StateRebuilder { + db: journaldb::new(db.clone(), pruning, ::db::COL_STATE), + state_root: KECCAK_NULL_RLP, + known_code: HashMap::new(), + missing_code: HashMap::new(), + known_storage_roots: HashMap::new(), + } + } + + /// Feed an uncompressed state chunk into the rebuilder. + pub fn feed(&mut self, chunk: &[u8], flag: &AtomicBool) -> Result<(), ::error::Error> { + let rlp = Rlp::new(chunk); + let mut pairs = Vec::with_capacity(rlp.item_count()?); + + // initialize the pairs vector with empty values so we have slots to write into. + pairs.resize(rlp.item_count()?, (H256::zero(), Vec::new())); + + let status = rebuild_accounts( + self.db.as_hash_db_mut(), + rlp, + &mut pairs, + &self.known_code, + &mut self.known_storage_roots, + flag, + )?; + + for (addr_hash, code_hash) in status.missing_code { + self.missing_code + .entry(code_hash) + .or_insert_with(Vec::new) + .push(addr_hash); + } + + // patch up all missing code. must be done after collecting all new missing code entries. + for (code_hash, code, first_with) in status.new_code { + for addr_hash in self + .missing_code + .remove(&code_hash) + .unwrap_or_else(Vec::new) + { + let mut db = AccountDBMut::from_hash(self.db.as_hash_db_mut(), addr_hash); + db.emplace(code_hash, DBValue::from_slice(&code)); + } + + self.known_code.insert(code_hash, first_with); + } + + // batch trie writes + { + let mut account_trie = if self.state_root != KECCAK_NULL_RLP { + TrieDBMut::from_existing(self.db.as_hash_db_mut(), &mut self.state_root)? + } else { + TrieDBMut::new(self.db.as_hash_db_mut(), &mut self.state_root) + }; + + for (hash, thin_rlp) in pairs { + if !flag.load(Ordering::SeqCst) { + return Err(Error::RestorationAborted.into()); + } + + account_trie.insert(&hash, &thin_rlp)?; + } + } + trace!(target: "snapshot", "current state root: {:?}", self.state_root); + + let backing = self.db.backing().clone(); + let mut batch = backing.transaction(); + // Drain the transaction overlay and put the data into the batch. + self.db.inject(&mut batch)?; + backing.write_buffered(batch); + Ok(()) + } + + /// Finalize the restoration. Check for accounts missing code and make a dummy + /// journal entry. + /// Once all chunks have been fed, there should be nothing missing. + pub fn finalize(mut self, era: u64, id: H256) -> Result, ::error::Error> { + let missing = self.missing_code.keys().cloned().collect::>(); + if !missing.is_empty() { + return Err(Error::MissingCode(missing).into()); + } + + let mut batch = self.db.backing().transaction(); + self.db.journal_under(&mut batch, era, &id)?; + self.db.backing().write_buffered(batch); + + Ok(self.db) + } + + /// Get the state root of the rebuilder. + pub fn state_root(&self) -> H256 { + self.state_root + } } #[derive(Default)] struct RebuiltStatus { - // new code that's become available. (code_hash, code, addr_hash) - new_code: Vec<(H256, Bytes, H256)>, - missing_code: Vec<(H256, H256)>, // accounts that are missing code. + // new code that's become available. (code_hash, code, addr_hash) + new_code: Vec<(H256, Bytes, H256)>, + missing_code: Vec<(H256, H256)>, // accounts that are missing code. } // rebuild a set of accounts and their storage. // returns a status detailing newly-loaded code and accounts missing code. fn rebuild_accounts( - db: &mut HashDB, - account_fat_rlps: Rlp, - out_chunk: &mut [(H256, Bytes)], - known_code: &HashMap, - known_storage_roots: &mut HashMap, - abort_flag: &AtomicBool, + db: &mut dyn HashDB, + account_fat_rlps: Rlp, + out_chunk: &mut [(H256, Bytes)], + known_code: &HashMap, + known_storage_roots: &mut HashMap, + abort_flag: &AtomicBool, ) -> Result { - let mut status = RebuiltStatus::default(); - for (account_rlp, out) in account_fat_rlps.into_iter().zip(out_chunk.iter_mut()) { - if !abort_flag.load(Ordering::SeqCst) { return Err(Error::RestorationAborted.into()) } - - let hash: H256 = account_rlp.val_at(0)?; - let fat_rlp = account_rlp.at(1)?; - - let thin_rlp = { - - // fill out the storage trie and code while decoding. - let (acc, maybe_code) = { - let mut acct_db = AccountDBMut::from_hash(db, hash); - let storage_root = known_storage_roots.get(&hash).cloned().unwrap_or_default(); - account::from_fat_rlp(&mut acct_db, fat_rlp, storage_root)? - }; - - let code_hash = acc.code_hash.clone(); - match maybe_code { - // new inline code - Some(code) => status.new_code.push((code_hash, code, hash)), - None => { - if code_hash != KECCAK_EMPTY { - // see if this code has already been included inline - match known_code.get(&code_hash) { - Some(&first_with) => { - // if so, load it from the database. - let code = AccountDB::from_hash(db, first_with) - .get(&code_hash) - .ok_or_else(|| Error::MissingCode(vec![first_with]))?; - - // and write it again under a different mangled key - AccountDBMut::from_hash(db, hash).emplace(code_hash, code); - } - // if not, queue it up to be filled later - None => status.missing_code.push((hash, code_hash)), - } - } - } - } - - ::rlp::encode(&acc) - }; - - *out = (hash, thin_rlp); - } - if let Some(&(ref hash, ref rlp)) = out_chunk.iter().last() { - known_storage_roots.insert(*hash, ::rlp::decode::(rlp)?.storage_root); - } - if let Some(&(ref hash, ref rlp)) = out_chunk.iter().next() { - known_storage_roots.insert(*hash, ::rlp::decode::(rlp)?.storage_root); - } - Ok(status) + let mut status = RebuiltStatus::default(); + for (account_rlp, out) in account_fat_rlps.into_iter().zip(out_chunk.iter_mut()) { + if !abort_flag.load(Ordering::SeqCst) { + return Err(Error::RestorationAborted.into()); + } + + let hash: H256 = account_rlp.val_at(0)?; + let fat_rlp = account_rlp.at(1)?; + + let thin_rlp = { + // fill out the storage trie and code while decoding. + let (acc, maybe_code) = { + let mut acct_db = AccountDBMut::from_hash(db, hash); + let storage_root = known_storage_roots.get(&hash).cloned().unwrap_or_default(); + account::from_fat_rlp(&mut acct_db, fat_rlp, storage_root)? + }; + + let code_hash = acc.code_hash.clone(); + match maybe_code { + // new inline code + Some(code) => status.new_code.push((code_hash, code, hash)), + None => { + if code_hash != KECCAK_EMPTY { + // see if this code has already been included inline + match known_code.get(&code_hash) { + Some(&first_with) => { + // if so, load it from the database. + let code = AccountDB::from_hash(db, first_with) + .get(&code_hash) + .ok_or_else(|| Error::MissingCode(vec![first_with]))?; + + // and write it again under a different mangled key + AccountDBMut::from_hash(db, hash).emplace(code_hash, code); + } + // if not, queue it up to be filled later + None => status.missing_code.push((hash, code_hash)), + } + } + } + } + + ::rlp::encode(&acc) + }; + + *out = (hash, thin_rlp); + } + if let Some(&(ref hash, ref rlp)) = out_chunk.iter().last() { + known_storage_roots.insert(*hash, ::rlp::decode::(rlp)?.storage_root); + } + if let Some(&(ref hash, ref rlp)) = out_chunk.iter().next() { + known_storage_roots.insert(*hash, ::rlp::decode::(rlp)?.storage_root); + } + Ok(status) } /// Proportion of blocks which we will verify `PoW` for. @@ -560,16 +609,22 @@ const POW_VERIFY_RATE: f32 = 0.02; /// Verify an old block with the given header, engine, blockchain, body. If `always` is set, it will perform /// the fullest verification possible. If not, it will take a random sample to determine whether it will /// do heavy or light verification. -pub fn verify_old_block(rng: &mut OsRng, header: &Header, engine: &EthEngine, chain: &BlockChain, always: bool) -> Result<(), ::error::Error> { - engine.verify_block_basic(header)?; - - if always || rng.gen::() <= POW_VERIFY_RATE { - engine.verify_block_unordered(header)?; - match chain.block_header_data(header.parent_hash()) { - Some(parent) => engine.verify_block_family(header, &parent.decode()?), - None => Ok(()), - } - } else { - Ok(()) - } +pub fn verify_old_block( + rng: &mut OsRng, + header: &Header, + engine: &dyn EthEngine, + chain: &BlockChain, + always: bool, +) -> Result<(), ::error::Error> { + engine.verify_block_basic(header)?; + + if always || rng.gen::() <= POW_VERIFY_RATE { + engine.verify_block_unordered(header)?; + match chain.block_header_data(header.parent_hash()) { + Some(parent) => engine.verify_block_family(header, &parent.decode()?), + None => Ok(()), + } + } else { + Ok(()) + } } diff --git a/ethcore/src/snapshot/service.rs b/ethcore/src/snapshot/service.rs index 4b3f196cb12..09b254bbb35 100644 --- a/ethcore/src/snapshot/service.rs +++ b/ethcore/src/snapshot/service.rs @@ -1,205 +1,233 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Snapshot network service implementation. -use std::collections::HashSet; -use std::io::{self, Read, ErrorKind}; -use std::fs::{self, File}; -use std::path::PathBuf; -use std::sync::Arc; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use std::cmp; - -use super::{ManifestData, StateRebuilder, Rebuilder, RestorationStatus, SnapshotService, MAX_CHUNK_SIZE}; -use super::io::{SnapshotReader, LooseReader, SnapshotWriter, LooseWriter}; +use std::{ + cmp, + collections::HashSet, + fs::{self, File}, + io::{self, ErrorKind, Read}, + path::PathBuf, + sync::{ + atomic::{AtomicBool, AtomicUsize, Ordering}, + Arc, + }, +}; + +use super::{ + io::{LooseReader, LooseWriter, SnapshotReader, SnapshotWriter}, + CreationStatus, ManifestData, Rebuilder, RestorationStatus, SnapshotService, StateRebuilder, + MAX_CHUNK_SIZE, +}; use blockchain::{BlockChain, BlockChainDB, BlockChainDBHandler}; -use client::{BlockInfo, BlockChainClient, Client, ChainInfo, ClientIoMessage}; +use client::{BlockChainClient, BlockInfo, ChainInfo, Client, ClientIoMessage}; use engines::EthEngine; use error::{Error, ErrorKind as SnapshotErrorKind}; -use snapshot::{Error as SnapshotError}; use hash::keccak; +use snapshot::Error as SnapshotError; use types::ids::BlockId; use io::IoChannel; -use ethereum_types::H256; -use parking_lot::{Mutex, RwLock, RwLockReadGuard}; use bytes::Bytes; +use ethereum_types::H256; use journaldb::Algorithm; use kvdb::DBTransaction; +use parking_lot::{Mutex, RwLock, RwLockReadGuard}; use snappy; /// Helper for removing directories in case of error. struct Guard(bool, PathBuf); impl Guard { - fn new(path: PathBuf) -> Self { Guard(true, path) } - - #[cfg(test)] - fn benign() -> Self { Guard(false, PathBuf::default()) } - - fn disarm(mut self) { self.0 = false } + fn new(path: PathBuf) -> Self { + Guard(true, path) + } + + #[cfg(test)] + fn benign() -> Self { + Guard(false, PathBuf::default()) + } + + fn disarm(mut self) { + self.0 = false + } } impl Drop for Guard { - fn drop(&mut self) { - if self.0 { - let _ = fs::remove_dir_all(&self.1); - } - } + fn drop(&mut self) { + if self.0 { + let _ = fs::remove_dir_all(&self.1); + } + } } /// External database restoration handler pub trait DatabaseRestore: Send + Sync { - /// Restart with a new backend. Takes ownership of passed database and moves it to a new location. - fn restore_db(&self, new_db: &str) -> Result<(), Error>; + /// Restart with a new backend. Takes ownership of passed database and moves it to a new location. + fn restore_db(&self, new_db: &str) -> Result<(), Error>; } /// State restoration manager. struct Restoration { - manifest: ManifestData, - state_chunks_left: HashSet, - block_chunks_left: HashSet, - state: StateRebuilder, - secondary: Box, - writer: Option, - snappy_buffer: Bytes, - final_state_root: H256, - guard: Guard, - db: Arc, + manifest: ManifestData, + state_chunks_left: HashSet, + block_chunks_left: HashSet, + state: StateRebuilder, + secondary: Box, + writer: Option, + snappy_buffer: Bytes, + final_state_root: H256, + guard: Guard, + db: Arc, } struct RestorationParams<'a> { - manifest: ManifestData, // manifest to base restoration on. - pruning: Algorithm, // pruning algorithm for the database. - db: Arc, // database - writer: Option, // writer for recovered snapshot. - genesis: &'a [u8], // genesis block of the chain. - guard: Guard, // guard for the restoration directory. - engine: &'a EthEngine, + manifest: ManifestData, // manifest to base restoration on. + pruning: Algorithm, // pruning algorithm for the database. + db: Arc, // database + writer: Option, // writer for recovered snapshot. + genesis: &'a [u8], // genesis block of the chain. + guard: Guard, // guard for the restoration directory. + engine: &'a dyn EthEngine, } impl Restoration { - // make a new restoration using the given parameters. - fn new(params: RestorationParams) -> Result { - let manifest = params.manifest; - - let state_chunks = manifest.state_hashes.iter().cloned().collect(); - let block_chunks = manifest.block_hashes.iter().cloned().collect(); - - let raw_db = params.db; - - let chain = BlockChain::new(Default::default(), params.genesis, raw_db.clone()); - let components = params.engine.snapshot_components() - .ok_or_else(|| ::snapshot::Error::SnapshotsUnsupported)?; - - let secondary = components.rebuilder(chain, raw_db.clone(), &manifest)?; - - let root = manifest.state_root.clone(); - - Ok(Restoration { - manifest: manifest, - state_chunks_left: state_chunks, - block_chunks_left: block_chunks, - state: StateRebuilder::new(raw_db.key_value().clone(), params.pruning), - secondary: secondary, - writer: params.writer, - snappy_buffer: Vec::new(), - final_state_root: root, - guard: params.guard, - db: raw_db, - }) - } - - // feeds a state chunk, aborts early if `flag` becomes false. - fn feed_state(&mut self, hash: H256, chunk: &[u8], flag: &AtomicBool) -> Result<(), Error> { - if self.state_chunks_left.contains(&hash) { - let expected_len = snappy::decompressed_len(chunk)?; - if expected_len > MAX_CHUNK_SIZE { - trace!(target: "snapshot", "Discarding large chunk: {} vs {}", expected_len, MAX_CHUNK_SIZE); - return Err(::snapshot::Error::ChunkTooLarge.into()); - } - let len = snappy::decompress_into(chunk, &mut self.snappy_buffer)?; - - self.state.feed(&self.snappy_buffer[..len], flag)?; - - if let Some(ref mut writer) = self.writer.as_mut() { - writer.write_state_chunk(hash, chunk)?; - } - - self.state_chunks_left.remove(&hash); - } - - Ok(()) - } - - // feeds a block chunk - fn feed_blocks(&mut self, hash: H256, chunk: &[u8], engine: &EthEngine, flag: &AtomicBool) -> Result<(), Error> { - if self.block_chunks_left.contains(&hash) { - let expected_len = snappy::decompressed_len(chunk)?; - if expected_len > MAX_CHUNK_SIZE { - trace!(target: "snapshot", "Discarding large chunk: {} vs {}", expected_len, MAX_CHUNK_SIZE); - return Err(::snapshot::Error::ChunkTooLarge.into()); - } - let len = snappy::decompress_into(chunk, &mut self.snappy_buffer)?; - - self.secondary.feed(&self.snappy_buffer[..len], engine, flag)?; - if let Some(ref mut writer) = self.writer.as_mut() { - writer.write_block_chunk(hash, chunk)?; - } - - self.block_chunks_left.remove(&hash); - } - - Ok(()) - } - - // finish up restoration. - fn finalize(mut self, engine: &EthEngine) -> Result<(), Error> { - use trie::TrieError; - - if !self.is_done() { return Ok(()) } - - // verify final state root. - let root = self.state.state_root(); - if root != self.final_state_root { - warn!("Final restored state has wrong state root: expected {:?}, got {:?}", self.final_state_root, root); - return Err(TrieError::InvalidStateRoot(root).into()); - } - - // check for missing code. - self.state.finalize(self.manifest.block_number, self.manifest.block_hash)?; - - // connect out-of-order chunks and verify chain integrity. - self.secondary.finalize(engine)?; - - if let Some(writer) = self.writer { - writer.finish(self.manifest)?; - } - - self.guard.disarm(); - Ok(()) - } - - // is everything done? - fn is_done(&self) -> bool { - self.block_chunks_left.is_empty() && self.state_chunks_left.is_empty() - } + // make a new restoration using the given parameters. + fn new(params: RestorationParams) -> Result { + let manifest = params.manifest; + + let state_chunks = manifest.state_hashes.iter().cloned().collect(); + let block_chunks = manifest.block_hashes.iter().cloned().collect(); + + let raw_db = params.db; + + let chain = BlockChain::new(Default::default(), params.genesis, raw_db.clone()); + let components = params + .engine + .snapshot_components() + .ok_or_else(|| ::snapshot::Error::SnapshotsUnsupported)?; + + let secondary = components.rebuilder(chain, raw_db.clone(), &manifest)?; + + let root = manifest.state_root.clone(); + + Ok(Restoration { + manifest: manifest, + state_chunks_left: state_chunks, + block_chunks_left: block_chunks, + state: StateRebuilder::new(raw_db.key_value().clone(), params.pruning), + secondary: secondary, + writer: params.writer, + snappy_buffer: Vec::new(), + final_state_root: root, + guard: params.guard, + db: raw_db, + }) + } + + // feeds a state chunk, aborts early if `flag` becomes false. + fn feed_state(&mut self, hash: H256, chunk: &[u8], flag: &AtomicBool) -> Result<(), Error> { + if self.state_chunks_left.contains(&hash) { + let expected_len = snappy::decompressed_len(chunk)?; + if expected_len > MAX_CHUNK_SIZE { + trace!(target: "snapshot", "Discarding large chunk: {} vs {}", expected_len, MAX_CHUNK_SIZE); + return Err(::snapshot::Error::ChunkTooLarge.into()); + } + let len = snappy::decompress_into(chunk, &mut self.snappy_buffer)?; + + self.state.feed(&self.snappy_buffer[..len], flag)?; + + if let Some(ref mut writer) = self.writer.as_mut() { + writer.write_state_chunk(hash, chunk)?; + } + + self.state_chunks_left.remove(&hash); + } + + Ok(()) + } + + // feeds a block chunk + fn feed_blocks( + &mut self, + hash: H256, + chunk: &[u8], + engine: &dyn EthEngine, + flag: &AtomicBool, + ) -> Result<(), Error> { + if self.block_chunks_left.contains(&hash) { + let expected_len = snappy::decompressed_len(chunk)?; + if expected_len > MAX_CHUNK_SIZE { + trace!(target: "snapshot", "Discarding large chunk: {} vs {}", expected_len, MAX_CHUNK_SIZE); + return Err(::snapshot::Error::ChunkTooLarge.into()); + } + let len = snappy::decompress_into(chunk, &mut self.snappy_buffer)?; + + self.secondary + .feed(&self.snappy_buffer[..len], engine, flag)?; + if let Some(ref mut writer) = self.writer.as_mut() { + writer.write_block_chunk(hash, chunk)?; + } + + self.block_chunks_left.remove(&hash); + } + + Ok(()) + } + + // finish up restoration. + fn finalize(mut self, engine: &dyn EthEngine) -> Result<(), Error> { + use trie::TrieError; + + if !self.is_done() { + return Ok(()); + } + + // verify final state root. + let root = self.state.state_root(); + if root != self.final_state_root { + warn!( + "Final restored state has wrong state root: expected {:?}, got {:?}", + self.final_state_root, root + ); + return Err(TrieError::InvalidStateRoot(root).into()); + } + + // check for missing code. + self.state + .finalize(self.manifest.block_number, self.manifest.block_hash)?; + + // connect out-of-order chunks and verify chain integrity. + self.secondary.finalize(engine)?; + + if let Some(writer) = self.writer { + writer.finish(self.manifest)?; + } + + self.guard.disarm(); + Ok(()) + } + + // is everything done? + fn is_done(&self) -> bool { + self.block_chunks_left.is_empty() && self.state_chunks_left.is_empty() + } } /// Type alias for client io channel. @@ -210,747 +238,854 @@ pub trait SnapshotClient: BlockChainClient + BlockInfo + DatabaseRestore {} /// Snapshot service parameters. pub struct ServiceParams { - /// The consensus engine this is built on. - pub engine: Arc, - /// The chain's genesis block. - pub genesis_block: Bytes, - /// State pruning algorithm. - pub pruning: Algorithm, - /// Handler for opening a restoration DB. - pub restoration_db_handler: Box, - /// Async IO channel for sending messages. - pub channel: Channel, - /// The directory to put snapshots in. - /// Usually "/snapshot" - pub snapshot_root: PathBuf, - /// A handle for database restoration. - pub client: Arc, + /// The consensus engine this is built on. + pub engine: Arc, + /// The chain's genesis block. + pub genesis_block: Bytes, + /// State pruning algorithm. + pub pruning: Algorithm, + /// Handler for opening a restoration DB. + pub restoration_db_handler: Box, + /// Async IO channel for sending messages. + pub channel: Channel, + /// The directory to put snapshots in. + /// Usually "/snapshot" + pub snapshot_root: PathBuf, + /// A handle for database restoration. + pub client: Arc, } /// `SnapshotService` implementation. /// This controls taking snapshots and restoring from them. pub struct Service { - restoration: Mutex>, - restoration_db_handler: Box, - snapshot_root: PathBuf, - io_channel: Mutex, - pruning: Algorithm, - status: Mutex, - reader: RwLock>, - engine: Arc, - genesis_block: Bytes, - state_chunks: AtomicUsize, - block_chunks: AtomicUsize, - client: Arc, - progress: super::Progress, - taking_snapshot: AtomicBool, - restoring_snapshot: AtomicBool, + restoration: Mutex>, + restoration_db_handler: Box, + snapshot_root: PathBuf, + io_channel: Mutex, + pruning: Algorithm, + status: Mutex, + reader: RwLock>, + engine: Arc, + genesis_block: Bytes, + state_chunks: AtomicUsize, + block_chunks: AtomicUsize, + client: Arc, + progress: super::Progress, + taking_snapshot: AtomicBool, + taking_snapshot_at: AtomicUsize, + restoring_snapshot: AtomicBool, } impl Service { - /// Create a new snapshot service from the given parameters. - pub fn new(params: ServiceParams) -> Result { - let mut service = Service { - restoration: Mutex::new(None), - restoration_db_handler: params.restoration_db_handler, - snapshot_root: params.snapshot_root, - io_channel: Mutex::new(params.channel), - pruning: params.pruning, - status: Mutex::new(RestorationStatus::Inactive), - reader: RwLock::new(None), - engine: params.engine, - genesis_block: params.genesis_block, - state_chunks: AtomicUsize::new(0), - block_chunks: AtomicUsize::new(0), - client: params.client, - progress: Default::default(), - taking_snapshot: AtomicBool::new(false), - restoring_snapshot: AtomicBool::new(false), - }; - - // create the root snapshot dir if it doesn't exist. - if let Err(e) = fs::create_dir_all(&service.snapshot_root) { - if e.kind() != ErrorKind::AlreadyExists { - return Err(e.into()) - } - } - - // delete the temporary restoration DB dir if it does exist. - if let Err(e) = fs::remove_dir_all(service.restoration_db()) { - if e.kind() != ErrorKind::NotFound { - return Err(e.into()) - } - } - - // delete the temporary snapshot dir if it does exist. - if let Err(e) = fs::remove_dir_all(service.temp_snapshot_dir()) { - if e.kind() != ErrorKind::NotFound { - return Err(e.into()) - } - } - - let reader = LooseReader::new(service.snapshot_dir()).ok(); - *service.reader.get_mut() = reader; - - Ok(service) - } - - // get the current snapshot dir. - fn snapshot_dir(&self) -> PathBuf { - let mut dir = self.snapshot_root.clone(); - dir.push("current"); - dir - } - - // get the temporary snapshot dir. - fn temp_snapshot_dir(&self) -> PathBuf { - let mut dir = self.snapshot_root.clone(); - dir.push("in_progress"); - dir - } - - // get the restoration directory. - fn restoration_dir(&self) -> PathBuf { - let mut dir = self.snapshot_root.clone(); - dir.push("restoration"); - dir - } - - // restoration db path. - fn restoration_db(&self) -> PathBuf { - let mut dir = self.restoration_dir(); - dir.push("db"); - dir - } - - // temporary snapshot recovery path. - fn temp_recovery_dir(&self) -> PathBuf { - let mut dir = self.restoration_dir(); - dir.push("temp"); - dir - } - - // previous snapshot chunks path. - fn prev_chunks_dir(&self) -> PathBuf { - let mut dir = self.snapshot_root.clone(); - dir.push("prev_chunks"); - dir - } - - // replace one the client's database with our own. - fn replace_client_db(&self) -> Result<(), Error> { - let migrated_blocks = self.migrate_blocks()?; - info!(target: "snapshot", "Migrated {} ancient blocks", migrated_blocks); - - let rest_db = self.restoration_db(); - self.client.restore_db(&*rest_db.to_string_lossy())?; - Ok(()) - } - - // Migrate the blocks in the current DB into the new chain - fn migrate_blocks(&self) -> Result { - // Count the number of migrated blocks - let mut count = 0; - let rest_db = self.restoration_db(); - - let cur_chain_info = self.client.chain_info(); - - let next_db = self.restoration_db_handler.open(&rest_db)?; - let next_chain = BlockChain::new(Default::default(), &[], next_db.clone()); - let next_chain_info = next_chain.chain_info(); - - // The old database looks like this: - // [genesis, best_ancient_block] ... [first_block, best_block] - // If we are fully synced neither `best_ancient_block` nor `first_block` is set, and we can assume that the whole range from [genesis, best_block] is imported. - // The new database only contains the tip of the chain ([first_block, best_block]), - // so the useful set of blocks is defined as: - // [0 ... min(new.first_block, best_ancient_block or best_block)] - let find_range = || -> Option<(H256, H256)> { - let next_available_from = next_chain_info.first_block_number?; - let cur_available_to = cur_chain_info.ancient_block_number.unwrap_or(cur_chain_info.best_block_number); - - let highest_block_num = cmp::min(next_available_from.saturating_sub(1), cur_available_to); - - if highest_block_num == 0 { - return None; - } - - trace!(target: "snapshot", "Trying to import ancient blocks until {}", highest_block_num); - - // Here we start from the highest block number and go backward to 0, - // thus starting at `highest_block_num` and targetting `0`. - let target_hash = self.client.block_hash(BlockId::Number(0))?; - let start_hash = self.client.block_hash(BlockId::Number(highest_block_num))?; - - Some((start_hash, target_hash)) - }; - - let (start_hash, target_hash) = match find_range() { - Some(x) => x, - None => return Ok(0), - }; - - let mut batch = DBTransaction::new(); - let mut parent_hash = start_hash; - while parent_hash != target_hash { - // Early return if restoration is aborted - if !self.restoring_snapshot.load(Ordering::SeqCst) { - return Ok(count); - } - - let block = self.client.block(BlockId::Hash(parent_hash)).ok_or(::snapshot::error::Error::UnlinkedAncientBlockChain)?; - parent_hash = block.parent_hash(); - - let block_number = block.number(); - let block_receipts = self.client.block_receipts(&block.hash()); - let parent_total_difficulty = self.client.block_total_difficulty(BlockId::Hash(parent_hash)); - - match (block_receipts, parent_total_difficulty) { - (Some(block_receipts), Some(parent_total_difficulty)) => { - let block_receipts = block_receipts.receipts; - - next_chain.insert_unordered_block(&mut batch, block, block_receipts, Some(parent_total_difficulty), false, true); - count += 1; - }, - _ => break, - } - - // Writting changes to DB and logging every now and then - if block_number % 1_000 == 0 { - next_db.key_value().write_buffered(batch); - next_chain.commit(); - next_db.key_value().flush().expect("DB flush failed."); - batch = DBTransaction::new(); - } - - if block_number % 10_000 == 0 { - info!(target: "snapshot", "Block restoration at #{}", block_number); - } - } - - // Final commit to the DB - next_db.key_value().write_buffered(batch); - next_chain.commit(); - next_db.key_value().flush().expect("DB flush failed."); - - // We couldn't reach the targeted hash - if parent_hash != target_hash { - return Err(::snapshot::error::Error::UnlinkedAncientBlockChain.into()); - } - - // Update best ancient block in the Next Chain - next_chain.update_best_ancient_block(&start_hash); - Ok(count) - } - - /// Get a reference to the snapshot reader. - pub fn reader(&self) -> RwLockReadGuard> { - self.reader.read() - } - - /// Tick the snapshot service. This will log any active snapshot - /// being taken. - pub fn tick(&self) { - if self.progress.done() || !self.taking_snapshot.load(Ordering::SeqCst) { return } - - let p = &self.progress; - info!("Snapshot: {} accounts {} blocks {} bytes", p.accounts(), p.blocks(), p.size()); - } - - /// Take a snapshot at the block with the given number. - /// calling this while a restoration is in progress or vice versa - /// will lead to a race condition where the first one to finish will - /// have their produced snapshot overwritten. - pub fn take_snapshot(&self, client: &Client, num: u64) -> Result<(), Error> { - if self.taking_snapshot.compare_and_swap(false, true, Ordering::SeqCst) { - info!("Skipping snapshot at #{} as another one is currently in-progress.", num); - return Ok(()); - } - - info!("Taking snapshot at #{}", num); - self.progress.reset(); - - let temp_dir = self.temp_snapshot_dir(); - let snapshot_dir = self.snapshot_dir(); - - let _ = fs::remove_dir_all(&temp_dir); - - let writer = LooseWriter::new(temp_dir.clone())?; - - let guard = Guard::new(temp_dir.clone()); - let res = client.take_snapshot(writer, BlockId::Number(num), &self.progress); - - self.taking_snapshot.store(false, Ordering::SeqCst); - if let Err(e) = res { - if client.chain_info().best_block_number >= num + client.pruning_history() { - // "Cancelled" is mincing words a bit -- what really happened - // is that the state we were snapshotting got pruned out - // before we could finish. - info!("Periodic snapshot failed: block state pruned.\ - Run with a longer `--pruning-history` or with `--no-periodic-snapshot`"); - return Ok(()) - } else { - return Err(e); - } - } - - info!("Finished taking snapshot at #{}", num); - - let mut reader = self.reader.write(); - - // destroy the old snapshot reader. - *reader = None; - - if snapshot_dir.exists() { - fs::remove_dir_all(&snapshot_dir)?; - } - - fs::rename(temp_dir, &snapshot_dir)?; - - *reader = Some(LooseReader::new(snapshot_dir)?); - - guard.disarm(); - Ok(()) - } - - /// Initialize the restoration synchronously. - /// The recover flag indicates whether to recover the restored snapshot. - pub fn init_restore(&self, manifest: ManifestData, recover: bool) -> Result<(), Error> { - let mut res = self.restoration.lock(); - - let rest_dir = self.restoration_dir(); - let rest_db = self.restoration_db(); - let recovery_temp = self.temp_recovery_dir(); - let prev_chunks = self.prev_chunks_dir(); - - // delete and restore the restoration dir. - if let Err(e) = fs::remove_dir_all(&prev_chunks) { - match e.kind() { - ErrorKind::NotFound => {}, - _ => return Err(e.into()), - } - } - - // Move the previous recovery temp directory - // to `prev_chunks` to be able to restart restoring - // with previously downloaded blocks - // This step is optional, so don't fail on error - fs::rename(&recovery_temp, &prev_chunks).ok(); - - self.state_chunks.store(0, Ordering::SeqCst); - self.block_chunks.store(0, Ordering::SeqCst); - - // tear down existing restoration. - *res = None; - - // delete and restore the restoration dir. - if let Err(e) = fs::remove_dir_all(&rest_dir) { - match e.kind() { - ErrorKind::NotFound => {}, - _ => return Err(e.into()), - } - } - - *self.status.lock() = RestorationStatus::Initializing { - chunks_done: 0, - }; - - fs::create_dir_all(&rest_dir)?; - - // make new restoration. - let writer = match recover { - true => Some(LooseWriter::new(recovery_temp)?), - false => None - }; - - let params = RestorationParams { - manifest: manifest.clone(), - pruning: self.pruning, - db: self.restoration_db_handler.open(&rest_db)?, - writer: writer, - genesis: &self.genesis_block, - guard: Guard::new(rest_db), - engine: &*self.engine, - }; - - let state_chunks = manifest.state_hashes.len(); - let block_chunks = manifest.block_hashes.len(); - - *res = Some(Restoration::new(params)?); - - self.restoring_snapshot.store(true, Ordering::SeqCst); - - // Import previous chunks, continue if it fails - self.import_prev_chunks(&mut res, manifest).ok(); - - // It could be that the restoration failed or completed in the meanwhile - let mut restoration_status = self.status.lock(); - if let RestorationStatus::Initializing { .. } = *restoration_status { - *restoration_status = RestorationStatus::Ongoing { - state_chunks: state_chunks as u32, - block_chunks: block_chunks as u32, - state_chunks_done: self.state_chunks.load(Ordering::SeqCst) as u32, - block_chunks_done: self.block_chunks.load(Ordering::SeqCst) as u32, - }; - } - - Ok(()) - } - - /// Import the previous chunks into the current restoration - fn import_prev_chunks(&self, restoration: &mut Option, manifest: ManifestData) -> Result<(), Error> { - let prev_chunks = self.prev_chunks_dir(); - - // Restore previous snapshot chunks - let files = fs::read_dir(prev_chunks.as_path())?; - let mut num_temp_chunks = 0; - - for prev_chunk_file in files { - // Don't go over all the files if the restoration has been aborted - if !self.restoring_snapshot.load(Ordering::SeqCst) { - trace!(target:"snapshot", "Aborting importing previous chunks"); - return Ok(()); - } - // Import the chunk, don't fail and continue if one fails - match self.import_prev_chunk(restoration, &manifest, prev_chunk_file) { - Ok(true) => num_temp_chunks += 1, - Err(e) => trace!(target: "snapshot", "Error importing chunk: {:?}", e), - _ => (), - } - } - - trace!(target:"snapshot", "Imported {} previous chunks", num_temp_chunks); - - // Remove the prev temp directory - fs::remove_dir_all(&prev_chunks)?; - - Ok(()) - } - - /// Import a previous chunk at the given path. Returns whether the block was imported or not - fn import_prev_chunk(&self, restoration: &mut Option, manifest: &ManifestData, file: io::Result) -> Result { - let file = file?; - let path = file.path(); - - let mut file = File::open(path.clone())?; - let mut buffer = Vec::new(); - file.read_to_end(&mut buffer)?; - - let hash = keccak(&buffer); - - let is_state = if manifest.block_hashes.contains(&hash) { - false - } else if manifest.state_hashes.contains(&hash) { - true - } else { - return Ok(false); - }; - - self.feed_chunk_with_restoration(restoration, hash, &buffer, is_state)?; - - trace!(target: "snapshot", "Fed chunk {:?}", hash); - - Ok(true) - } - - // finalize the restoration. this accepts an already-locked - // restoration as an argument -- so acquiring it again _will_ - // lead to deadlock. - fn finalize_restoration(&self, rest: &mut Option) -> Result<(), Error> { - trace!(target: "snapshot", "finalizing restoration"); - - let recover = rest.as_ref().map_or(false, |rest| rest.writer.is_some()); - - // destroy the restoration before replacing databases and snapshot. - rest.take() - .map(|r| r.finalize(&*self.engine)) - .unwrap_or(Ok(()))?; - - self.replace_client_db()?; - - if recover { - let mut reader = self.reader.write(); - *reader = None; // destroy the old reader if it existed. - - let snapshot_dir = self.snapshot_dir(); - - if snapshot_dir.exists() { - trace!(target: "snapshot", "removing old snapshot dir at {}", snapshot_dir.to_string_lossy()); - fs::remove_dir_all(&snapshot_dir)?; - } - - trace!(target: "snapshot", "copying restored snapshot files over"); - fs::rename(self.temp_recovery_dir(), &snapshot_dir)?; - - *reader = Some(LooseReader::new(snapshot_dir)?); - } - - let _ = fs::remove_dir_all(self.restoration_dir()); - *self.status.lock() = RestorationStatus::Inactive; - - Ok(()) - } - - /// Feed a chunk of either kind (block or state). no-op if no restoration or status is wrong. - fn feed_chunk(&self, hash: H256, chunk: &[u8], is_state: bool) { - // TODO: be able to process block chunks and state chunks at same time? - let mut restoration = self.restoration.lock(); - match self.feed_chunk_with_restoration(&mut restoration, hash, chunk, is_state) { - Ok(()) | - Err(Error(SnapshotErrorKind::Snapshot(SnapshotError::RestorationAborted), _)) => (), - Err(e) => { - warn!("Encountered error during snapshot restoration: {}", e); - *self.restoration.lock() = None; - *self.status.lock() = RestorationStatus::Failed; - let _ = fs::remove_dir_all(self.restoration_dir()); - } - } - } - - /// Feed a chunk with the Restoration - fn feed_chunk_with_restoration(&self, restoration: &mut Option, hash: H256, chunk: &[u8], is_state: bool) -> Result<(), Error> { - let (result, db) = { - match self.status() { - RestorationStatus::Inactive | RestorationStatus::Failed => { - trace!(target: "snapshot", "Tried to restore chunk {:x} while inactive or failed", hash); - return Ok(()); - }, - RestorationStatus::Ongoing { .. } | RestorationStatus::Initializing { .. } => { - let (res, db) = { - let rest = match *restoration { - Some(ref mut r) => r, - None => return Ok(()), - }; - - (match is_state { - true => rest.feed_state(hash, chunk, &self.restoring_snapshot), - false => rest.feed_blocks(hash, chunk, &*self.engine, &self.restoring_snapshot), - }.map(|_| rest.is_done()), rest.db.clone()) - }; - - let res = match res { - Ok(is_done) => { - match is_state { - true => self.state_chunks.fetch_add(1, Ordering::SeqCst), - false => self.block_chunks.fetch_add(1, Ordering::SeqCst), - }; - - match is_done { - true => { - db.key_value().flush()?; - drop(db); - return self.finalize_restoration(&mut *restoration); - }, - false => Ok(()) - } - } - other => other.map(drop), - }; - (res, db) - } - } - }; - - result?; - db.key_value().flush()?; - Ok(()) - } - - /// Feed a state chunk to be processed synchronously. - pub fn feed_state_chunk(&self, hash: H256, chunk: &[u8]) { - self.feed_chunk(hash, chunk, true); - } - - /// Feed a block chunk to be processed synchronously. - pub fn feed_block_chunk(&self, hash: H256, chunk: &[u8]) { - self.feed_chunk(hash, chunk, false); - } + /// Create a new snapshot service from the given parameters. + pub fn new(params: ServiceParams) -> Result { + let mut service = Service { + restoration: Mutex::new(None), + restoration_db_handler: params.restoration_db_handler, + snapshot_root: params.snapshot_root, + io_channel: Mutex::new(params.channel), + pruning: params.pruning, + status: Mutex::new(RestorationStatus::Inactive), + reader: RwLock::new(None), + engine: params.engine, + genesis_block: params.genesis_block, + state_chunks: AtomicUsize::new(0), + block_chunks: AtomicUsize::new(0), + client: params.client, + progress: Default::default(), + taking_snapshot: AtomicBool::new(false), + taking_snapshot_at: AtomicUsize::new(0), + restoring_snapshot: AtomicBool::new(false), + }; + + // create the root snapshot dir if it doesn't exist. + if let Err(e) = fs::create_dir_all(&service.snapshot_root) { + if e.kind() != ErrorKind::AlreadyExists { + return Err(e.into()); + } + } + + // delete the temporary restoration DB dir if it does exist. + if let Err(e) = fs::remove_dir_all(service.restoration_db()) { + if e.kind() != ErrorKind::NotFound { + return Err(e.into()); + } + } + + // delete the temporary snapshot dir if it does exist. + if let Err(e) = fs::remove_dir_all(service.temp_snapshot_dir()) { + if e.kind() != ErrorKind::NotFound { + return Err(e.into()); + } + } + + let reader = LooseReader::new(service.snapshot_dir()).ok(); + *service.reader.get_mut() = reader; + + Ok(service) + } + + // get the current snapshot dir. + fn snapshot_dir(&self) -> PathBuf { + let mut dir = self.snapshot_root.clone(); + dir.push("current"); + dir + } + + // get the temporary snapshot dir. + fn temp_snapshot_dir(&self) -> PathBuf { + let mut dir = self.snapshot_root.clone(); + dir.push("in_progress"); + dir + } + + // get the restoration directory. + fn restoration_dir(&self) -> PathBuf { + let mut dir = self.snapshot_root.clone(); + dir.push("restoration"); + dir + } + + // restoration db path. + fn restoration_db(&self) -> PathBuf { + let mut dir = self.restoration_dir(); + dir.push("db"); + dir + } + + // temporary snapshot recovery path. + fn temp_recovery_dir(&self) -> PathBuf { + let mut dir = self.restoration_dir(); + dir.push("temp"); + dir + } + + // previous snapshot chunks path. + fn prev_chunks_dir(&self) -> PathBuf { + let mut dir = self.snapshot_root.clone(); + dir.push("prev_chunks"); + dir + } + + // replace one the client's database with our own. + fn replace_client_db(&self) -> Result<(), Error> { + let migrated_blocks = self.migrate_blocks()?; + info!(target: "snapshot", "Migrated {} ancient blocks", migrated_blocks); + + let rest_db = self.restoration_db(); + self.client.restore_db(&*rest_db.to_string_lossy())?; + Ok(()) + } + + // Migrate the blocks in the current DB into the new chain + fn migrate_blocks(&self) -> Result { + // Count the number of migrated blocks + let mut count = 0; + let rest_db = self.restoration_db(); + + let cur_chain_info = self.client.chain_info(); + + let next_db = self.restoration_db_handler.open(&rest_db)?; + let next_chain = BlockChain::new(Default::default(), &[], next_db.clone()); + let next_chain_info = next_chain.chain_info(); + + // The old database looks like this: + // [genesis, best_ancient_block] ... [first_block, best_block] + // If we are fully synced neither `best_ancient_block` nor `first_block` is set, and we can assume that the whole range from [genesis, best_block] is imported. + // The new database only contains the tip of the chain ([first_block, best_block]), + // so the useful set of blocks is defined as: + // [0 ... min(new.first_block, best_ancient_block or best_block)] + let find_range = || -> Option<(H256, H256)> { + let next_available_from = next_chain_info.first_block_number?; + let cur_available_to = cur_chain_info + .ancient_block_number + .unwrap_or(cur_chain_info.best_block_number); + + let highest_block_num = + cmp::min(next_available_from.saturating_sub(1), cur_available_to); + + if highest_block_num == 0 { + return None; + } + + trace!(target: "snapshot", "Trying to import ancient blocks until {}", highest_block_num); + + // Here we start from the highest block number and go backward to 0, + // thus starting at `highest_block_num` and targetting `0`. + let target_hash = self.client.block_hash(BlockId::Number(0))?; + let start_hash = self.client.block_hash(BlockId::Number(highest_block_num))?; + + Some((start_hash, target_hash)) + }; + + let (start_hash, target_hash) = match find_range() { + Some(x) => x, + None => return Ok(0), + }; + + let mut batch = DBTransaction::new(); + let mut parent_hash = start_hash; + while parent_hash != target_hash { + // Early return if restoration is aborted + if !self.restoring_snapshot.load(Ordering::SeqCst) { + return Ok(count); + } + + let block = self + .client + .block(BlockId::Hash(parent_hash)) + .ok_or(::snapshot::error::Error::UnlinkedAncientBlockChain)?; + parent_hash = block.parent_hash(); + + let block_number = block.number(); + let block_receipts = self.client.block_receipts(&block.hash()); + let parent_total_difficulty = self + .client + .block_total_difficulty(BlockId::Hash(parent_hash)); + + match (block_receipts, parent_total_difficulty) { + (Some(block_receipts), Some(parent_total_difficulty)) => { + let block_receipts = block_receipts.receipts; + + next_chain.insert_unordered_block( + &mut batch, + block, + block_receipts, + Some(parent_total_difficulty), + false, + true, + ); + count += 1; + } + _ => break, + } + + // Writing changes to DB and logging every now and then + if block_number % 1_000 == 0 { + next_db.key_value().write_buffered(batch); + next_chain.commit(); + next_db.key_value().flush().expect("DB flush failed."); + batch = DBTransaction::new(); + } + + if block_number % 10_000 == 0 { + info!(target: "snapshot", "Block restoration at #{}", block_number); + } + } + + // Final commit to the DB + next_db.key_value().write_buffered(batch); + next_chain.commit(); + next_db.key_value().flush().expect("DB flush failed."); + + // We couldn't reach the targeted hash + if parent_hash != target_hash { + return Err(::snapshot::error::Error::UnlinkedAncientBlockChain.into()); + } + + // Update best ancient block in the Next Chain + next_chain.update_best_ancient_block(&start_hash); + Ok(count) + } + + /// Get a reference to the snapshot reader. + pub fn reader(&self) -> RwLockReadGuard> { + self.reader.read() + } + + /// Tick the snapshot service. This will log any active snapshot + /// being taken. + pub fn tick(&self) { + if self.progress.done() || !self.taking_snapshot.load(Ordering::SeqCst) { + return; + } + + let p = &self.progress; + info!( + "Snapshot: {} accounts {} blocks {} bytes", + p.accounts(), + p.blocks(), + p.size() + ); + } + + /// Take a snapshot at the block with the given number. + /// calling this while a restoration is in progress or vice versa + /// will lead to a race condition where the first one to finish will + /// have their produced snapshot overwritten. + pub fn take_snapshot(&self, client: &Client, num: u64) -> Result<(), Error> { + if self + .taking_snapshot + .compare_and_swap(false, true, Ordering::SeqCst) + { + info!( + "Skipping snapshot at #{} as another one is currently in-progress.", + num + ); + return Ok(()); + } + + self.taking_snapshot_at + .store(num as usize, Ordering::SeqCst); + + info!("Taking snapshot at #{}", num); + self.progress.reset(); + + let temp_dir = self.temp_snapshot_dir(); + let snapshot_dir = self.snapshot_dir(); + + let _ = fs::remove_dir_all(&temp_dir); + + let writer = LooseWriter::new(temp_dir.clone())?; + + let guard = Guard::new(temp_dir.clone()); + let res = client.take_snapshot(writer, BlockId::Number(num), &self.progress); + self.taking_snapshot.store(false, Ordering::SeqCst); + if let Err(e) = res { + if client.chain_info().best_block_number >= num + client.pruning_history() { + // The state we were snapshotting was pruned before we could finish. + info!("Periodic snapshot failed: block state pruned. Run with a longer `--pruning-history` or with `--no-periodic-snapshot`"); + return Err(e); + } else { + return Err(e); + } + } + + info!("Finished taking snapshot at #{}", num); + + let mut reader = self.reader.write(); + + // destroy the old snapshot reader. + *reader = None; + + if snapshot_dir.exists() { + fs::remove_dir_all(&snapshot_dir)?; + } + + fs::rename(temp_dir, &snapshot_dir)?; + + *reader = Some(LooseReader::new(snapshot_dir)?); + + guard.disarm(); + Ok(()) + } + + /// Initialize the restoration synchronously. + /// The recover flag indicates whether to recover the restored snapshot. + pub fn init_restore(&self, manifest: ManifestData, recover: bool) -> Result<(), Error> { + let mut res = self.restoration.lock(); + + let rest_dir = self.restoration_dir(); + let rest_db = self.restoration_db(); + let recovery_temp = self.temp_recovery_dir(); + let prev_chunks = self.prev_chunks_dir(); + + // delete and restore the restoration dir. + if let Err(e) = fs::remove_dir_all(&prev_chunks) { + match e.kind() { + ErrorKind::NotFound => {} + _ => return Err(e.into()), + } + } + + // Move the previous recovery temp directory + // to `prev_chunks` to be able to restart restoring + // with previously downloaded blocks + // This step is optional, so don't fail on error + fs::rename(&recovery_temp, &prev_chunks).ok(); + + self.state_chunks.store(0, Ordering::SeqCst); + self.block_chunks.store(0, Ordering::SeqCst); + + // tear down existing restoration. + *res = None; + + // delete and restore the restoration dir. + if let Err(e) = fs::remove_dir_all(&rest_dir) { + match e.kind() { + ErrorKind::NotFound => {} + _ => return Err(e.into()), + } + } + + *self.status.lock() = RestorationStatus::Initializing { chunks_done: 0 }; + + fs::create_dir_all(&rest_dir)?; + + // make new restoration. + let writer = match recover { + true => Some(LooseWriter::new(recovery_temp)?), + false => None, + }; + + let params = RestorationParams { + manifest: manifest.clone(), + pruning: self.pruning, + db: self.restoration_db_handler.open(&rest_db)?, + writer: writer, + genesis: &self.genesis_block, + guard: Guard::new(rest_db), + engine: &*self.engine, + }; + + let state_chunks = manifest.state_hashes.len(); + let block_chunks = manifest.block_hashes.len(); + + *res = Some(Restoration::new(params)?); + + self.restoring_snapshot.store(true, Ordering::SeqCst); + + let block_number = manifest.block_number; + // Import previous chunks, continue if it fails + self.import_prev_chunks(&mut res, manifest).ok(); + + // It could be that the restoration failed or completed in the meanwhile + let mut restoration_status = self.status.lock(); + if let RestorationStatus::Initializing { .. } = *restoration_status { + *restoration_status = RestorationStatus::Ongoing { + block_number, + state_chunks: state_chunks as u32, + block_chunks: block_chunks as u32, + state_chunks_done: self.state_chunks.load(Ordering::SeqCst) as u32, + block_chunks_done: self.block_chunks.load(Ordering::SeqCst) as u32, + }; + } + + Ok(()) + } + + /// Import the previous chunks into the current restoration + fn import_prev_chunks( + &self, + restoration: &mut Option, + manifest: ManifestData, + ) -> Result<(), Error> { + let prev_chunks = self.prev_chunks_dir(); + + // Restore previous snapshot chunks + let files = fs::read_dir(prev_chunks.as_path())?; + let mut num_temp_chunks = 0; + + for prev_chunk_file in files { + // Don't go over all the files if the restoration has been aborted + if !self.restoring_snapshot.load(Ordering::SeqCst) { + trace!(target:"snapshot", "Aborting importing previous chunks"); + return Ok(()); + } + // Import the chunk, don't fail and continue if one fails + match self.import_prev_chunk(restoration, &manifest, prev_chunk_file) { + Ok(true) => num_temp_chunks += 1, + Err(e) => trace!(target: "snapshot", "Error importing chunk: {:?}", e), + _ => (), + } + } + + trace!(target:"snapshot", "Imported {} previous chunks", num_temp_chunks); + + // Remove the prev temp directory + fs::remove_dir_all(&prev_chunks)?; + + Ok(()) + } + + /// Import a previous chunk at the given path. Returns whether the block was imported or not + fn import_prev_chunk( + &self, + restoration: &mut Option, + manifest: &ManifestData, + file: io::Result, + ) -> Result { + let file = file?; + let path = file.path(); + + let mut file = File::open(path.clone())?; + let mut buffer = Vec::new(); + file.read_to_end(&mut buffer)?; + + let hash = keccak(&buffer); + + let is_state = if manifest.block_hashes.contains(&hash) { + false + } else if manifest.state_hashes.contains(&hash) { + true + } else { + return Ok(false); + }; + + self.feed_chunk_with_restoration(restoration, hash, &buffer, is_state)?; + + trace!(target: "snapshot", "Fed chunk {:?}", hash); + + Ok(true) + } + + // finalize the restoration. this accepts an already-locked + // restoration as an argument -- so acquiring it again _will_ + // lead to deadlock. + fn finalize_restoration(&self, rest: &mut Option) -> Result<(), Error> { + trace!(target: "snapshot", "finalizing restoration"); + + let recover = rest.as_ref().map_or(false, |rest| rest.writer.is_some()); + + // destroy the restoration before replacing databases and snapshot. + rest.take() + .map(|r| r.finalize(&*self.engine)) + .unwrap_or(Ok(()))?; + + self.replace_client_db()?; + + if recover { + let mut reader = self.reader.write(); + *reader = None; // destroy the old reader if it existed. + + let snapshot_dir = self.snapshot_dir(); + + if snapshot_dir.exists() { + trace!(target: "snapshot", "removing old snapshot dir at {}", snapshot_dir.to_string_lossy()); + fs::remove_dir_all(&snapshot_dir)?; + } + + trace!(target: "snapshot", "copying restored snapshot files over"); + fs::rename(self.temp_recovery_dir(), &snapshot_dir)?; + + *reader = Some(LooseReader::new(snapshot_dir)?); + } + + let _ = fs::remove_dir_all(self.restoration_dir()); + *self.status.lock() = RestorationStatus::Inactive; + + Ok(()) + } + + /// Feed a chunk of either kind (block or state). no-op if no restoration or status is wrong. + fn feed_chunk(&self, hash: H256, chunk: &[u8], is_state: bool) { + // TODO: be able to process block chunks and state chunks at same time? + let mut restoration = self.restoration.lock(); + match self.feed_chunk_with_restoration(&mut restoration, hash, chunk, is_state) { + Ok(()) + | Err(Error(SnapshotErrorKind::Snapshot(SnapshotError::RestorationAborted), _)) => (), + Err(e) => { + warn!("Encountered error during snapshot restoration: {}", e); + *self.restoration.lock() = None; + *self.status.lock() = RestorationStatus::Failed; + let _ = fs::remove_dir_all(self.restoration_dir()); + } + } + } + + /// Feed a chunk with the Restoration + fn feed_chunk_with_restoration( + &self, + restoration: &mut Option, + hash: H256, + chunk: &[u8], + is_state: bool, + ) -> Result<(), Error> { + let (result, db) = { + match self.restoration_status() { + RestorationStatus::Inactive | RestorationStatus::Failed => { + trace!(target: "snapshot", "Tried to restore chunk {:x} while inactive or failed", hash); + return Ok(()); + } + RestorationStatus::Ongoing { .. } | RestorationStatus::Initializing { .. } => { + let (res, db) = { + let rest = match *restoration { + Some(ref mut r) => r, + None => return Ok(()), + }; + + ( + match is_state { + true => rest.feed_state(hash, chunk, &self.restoring_snapshot), + false => rest.feed_blocks( + hash, + chunk, + &*self.engine, + &self.restoring_snapshot, + ), + } + .map(|_| rest.is_done()), + rest.db.clone(), + ) + }; + + let res = match res { + Ok(is_done) => { + match is_state { + true => self.state_chunks.fetch_add(1, Ordering::SeqCst), + false => self.block_chunks.fetch_add(1, Ordering::SeqCst), + }; + + match is_done { + true => { + db.key_value().flush()?; + drop(db); + return self.finalize_restoration(&mut *restoration); + } + false => Ok(()), + } + } + other => other.map(drop), + }; + (res, db) + } + } + }; + + result?; + db.key_value().flush()?; + Ok(()) + } + + /// Feed a state chunk to be processed synchronously. + pub fn feed_state_chunk(&self, hash: H256, chunk: &[u8]) { + self.feed_chunk(hash, chunk, true); + } + + /// Feed a block chunk to be processed synchronously. + pub fn feed_block_chunk(&self, hash: H256, chunk: &[u8]) { + self.feed_chunk(hash, chunk, false); + } } impl SnapshotService for Service { - fn manifest(&self) -> Option { - self.reader.read().as_ref().map(|r| r.manifest().clone()) - } - - fn supported_versions(&self) -> Option<(u64, u64)> { - self.engine.snapshot_components() - .map(|c| (c.min_supported_version(), c.current_version())) - } - - fn chunk(&self, hash: H256) -> Option { - self.reader.read().as_ref().and_then(|r| r.chunk(hash).ok()) - } - - fn completed_chunks(&self) -> Option> { - let restoration = self.restoration.lock(); - - match *restoration { - Some(ref restoration) => { - let completed_chunks = restoration.manifest.block_hashes - .iter() - .filter(|h| !restoration.block_chunks_left.contains(h)) - .chain( - restoration.manifest.state_hashes - .iter() - .filter(|h| !restoration.state_chunks_left.contains(h)) - ) - .map(|h| *h) - .collect(); - - Some(completed_chunks) - }, - None => None, - } - } - - fn status(&self) -> RestorationStatus { - let mut cur_status = self.status.lock(); - - match *cur_status { - RestorationStatus::Initializing { ref mut chunks_done } => { - *chunks_done = self.state_chunks.load(Ordering::SeqCst) as u32 + - self.block_chunks.load(Ordering::SeqCst) as u32; - } - RestorationStatus::Ongoing { ref mut state_chunks_done, ref mut block_chunks_done, .. } => { - *state_chunks_done = self.state_chunks.load(Ordering::SeqCst) as u32; - *block_chunks_done = self.block_chunks.load(Ordering::SeqCst) as u32; - }, - _ => (), - } - - cur_status.clone() - } - - fn begin_restore(&self, manifest: ManifestData) { - if let Err(e) = self.io_channel.lock().send(ClientIoMessage::BeginRestoration(manifest)) { - trace!("Error sending snapshot service message: {:?}", e); - } - } - - fn abort_restore(&self) { - trace!(target: "snapshot", "Aborting restore"); - self.restoring_snapshot.store(false, Ordering::SeqCst); - *self.restoration.lock() = None; - *self.status.lock() = RestorationStatus::Inactive; - } - - fn restore_state_chunk(&self, hash: H256, chunk: Bytes) { - if let Err(e) = self.io_channel.lock().send(ClientIoMessage::FeedStateChunk(hash, chunk)) { - trace!("Error sending snapshot service message: {:?}", e); - } - } - - fn restore_block_chunk(&self, hash: H256, chunk: Bytes) { - if let Err(e) = self.io_channel.lock().send(ClientIoMessage::FeedBlockChunk(hash, chunk)) { - trace!("Error sending snapshot service message: {:?}", e); - } - } - - fn shutdown(&self) { - self.abort_restore(); - } + fn manifest(&self) -> Option { + self.reader.read().as_ref().map(|r| r.manifest().clone()) + } + + fn supported_versions(&self) -> Option<(u64, u64)> { + self.engine + .snapshot_components() + .map(|c| (c.min_supported_version(), c.current_version())) + } + + fn chunk(&self, hash: H256) -> Option { + self.reader.read().as_ref().and_then(|r| r.chunk(hash).ok()) + } + + fn completed_chunks(&self) -> Option> { + let restoration = self.restoration.lock(); + + match *restoration { + Some(ref restoration) => { + let completed_chunks = restoration + .manifest + .block_hashes + .iter() + .filter(|h| !restoration.block_chunks_left.contains(h)) + .chain( + restoration + .manifest + .state_hashes + .iter() + .filter(|h| !restoration.state_chunks_left.contains(h)), + ) + .map(|h| *h) + .collect(); + + Some(completed_chunks) + } + None => None, + } + } + + fn creation_status(&self) -> CreationStatus { + if self.taking_snapshot.load(Ordering::SeqCst) { + CreationStatus::Ongoing { + block_number: self.taking_snapshot_at.load(Ordering::SeqCst) as u32, + } + } else { + CreationStatus::Inactive + } + } + + fn restoration_status(&self) -> RestorationStatus { + let mut cur_status = self.status.lock(); + + match *cur_status { + RestorationStatus::Initializing { + ref mut chunks_done, + } => { + *chunks_done = self.state_chunks.load(Ordering::SeqCst) as u32 + + self.block_chunks.load(Ordering::SeqCst) as u32; + } + RestorationStatus::Ongoing { + ref mut state_chunks_done, + ref mut block_chunks_done, + .. + } => { + *state_chunks_done = self.state_chunks.load(Ordering::SeqCst) as u32; + *block_chunks_done = self.block_chunks.load(Ordering::SeqCst) as u32; + } + _ => (), + } + + cur_status.clone() + } + + fn begin_restore(&self, manifest: ManifestData) { + if let Err(e) = self + .io_channel + .lock() + .send(ClientIoMessage::BeginRestoration(manifest)) + { + trace!("Error sending snapshot service message: {:?}", e); + } + } + + fn abort_restore(&self) { + trace!(target: "snapshot", "Aborting restore"); + self.restoring_snapshot.store(false, Ordering::SeqCst); + *self.restoration.lock() = None; + *self.status.lock() = RestorationStatus::Inactive; + } + + fn restore_state_chunk(&self, hash: H256, chunk: Bytes) { + if let Err(e) = self + .io_channel + .lock() + .send(ClientIoMessage::FeedStateChunk(hash, chunk)) + { + trace!("Error sending snapshot service message: {:?}", e); + } + } + + fn restore_block_chunk(&self, hash: H256, chunk: Bytes) { + if let Err(e) = self + .io_channel + .lock() + .send(ClientIoMessage::FeedBlockChunk(hash, chunk)) + { + trace!("Error sending snapshot service message: {:?}", e); + } + } + + fn abort_snapshot(&self) { + if self.taking_snapshot.load(Ordering::SeqCst) { + trace!(target: "snapshot", "Aborting snapshot – Snapshot under way"); + self.progress.abort.store(true, Ordering::SeqCst); + } + } + + fn shutdown(&self) { + trace!(target: "snapshot", "Shut down SnapshotService"); + self.abort_restore(); + trace!(target: "snapshot", "Shut down SnapshotService - restore aborted"); + self.abort_snapshot(); + trace!(target: "snapshot", "Shut down SnapshotService - snapshot aborted"); + } } impl Drop for Service { - fn drop(&mut self) { - self.abort_restore(); - } + fn drop(&mut self) { + trace!(target: "shutdown", "Dropping Service"); + self.abort_restore(); + trace!(target: "shutdown", "Dropping Service - restore aborted"); + self.abort_snapshot(); + trace!(target: "shutdown", "Dropping Service - snapshot aborted"); + } } #[cfg(test)] mod tests { - use client::ClientIoMessage; - use io::{IoService}; - use spec::Spec; - use journaldb::Algorithm; - use snapshot::{ManifestData, RestorationStatus, SnapshotService}; - use super::*; - use tempdir::TempDir; - use test_helpers::{generate_dummy_client_with_spec_and_data, restoration_db_handler}; - - #[test] - fn sends_async_messages() { - let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()]; - let client = generate_dummy_client_with_spec_and_data(Spec::new_null, 400, 5, &gas_prices); - let service = IoService::::start().unwrap(); - let spec = Spec::new_test(); - - let tempdir = TempDir::new("").unwrap(); - let dir = tempdir.path().join("snapshot"); - - let snapshot_params = ServiceParams { - engine: spec.engine.clone(), - genesis_block: spec.genesis_block(), - restoration_db_handler: restoration_db_handler(Default::default()), - pruning: Algorithm::Archive, - channel: service.channel(), - snapshot_root: dir, - client: client, - }; - - let service = Service::new(snapshot_params).unwrap(); - - assert!(service.manifest().is_none()); - assert!(service.chunk(Default::default()).is_none()); - assert_eq!(service.status(), RestorationStatus::Inactive); - - let manifest = ManifestData { - version: 2, - state_hashes: vec![], - block_hashes: vec![], - state_root: Default::default(), - block_number: 0, - block_hash: Default::default(), - }; - - service.begin_restore(manifest); - service.abort_restore(); - service.restore_state_chunk(Default::default(), vec![]); - service.restore_block_chunk(Default::default(), vec![]); - } - - #[test] - fn cannot_finish_with_invalid_chunks() { - use ethereum_types::H256; - use kvdb_rocksdb::DatabaseConfig; - - let spec = Spec::new_test(); - let tempdir = TempDir::new("").unwrap(); - - let state_hashes: Vec<_> = (0..5).map(|_| H256::random()).collect(); - let block_hashes: Vec<_> = (0..5).map(|_| H256::random()).collect(); - let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS); - let gb = spec.genesis_block(); - let flag = ::std::sync::atomic::AtomicBool::new(true); - - let params = RestorationParams { - manifest: ManifestData { - version: 2, - state_hashes: state_hashes.clone(), - block_hashes: block_hashes.clone(), - state_root: H256::default(), - block_number: 100000, - block_hash: H256::default(), - }, - pruning: Algorithm::Archive, - db: restoration_db_handler(db_config).open(&tempdir.path().to_owned()).unwrap(), - writer: None, - genesis: &gb, - guard: Guard::benign(), - engine: &*spec.engine.clone(), - }; - - let mut restoration = Restoration::new(params).unwrap(); - let definitely_bad_chunk = [1, 2, 3, 4, 5]; - - for hash in state_hashes { - assert!(restoration.feed_state(hash, &definitely_bad_chunk, &flag).is_err()); - assert!(!restoration.is_done()); - } - - for hash in block_hashes { - assert!(restoration.feed_blocks(hash, &definitely_bad_chunk, &*spec.engine, &flag).is_err()); - assert!(!restoration.is_done()); - } - } + use super::*; + use client::ClientIoMessage; + use io::IoService; + use journaldb::Algorithm; + use snapshot::{ManifestData, RestorationStatus, SnapshotService}; + use spec::Spec; + use tempdir::TempDir; + use test_helpers::{generate_dummy_client_with_spec_and_data, restoration_db_handler}; + + #[test] + fn sends_async_messages() { + let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()]; + let client = generate_dummy_client_with_spec_and_data(Spec::new_null, 400, 5, &gas_prices); + let service = IoService::::start().unwrap(); + let spec = Spec::new_test(); + + let tempdir = TempDir::new("").unwrap(); + let dir = tempdir.path().join("snapshot"); + + let snapshot_params = ServiceParams { + engine: spec.engine.clone(), + genesis_block: spec.genesis_block(), + restoration_db_handler: restoration_db_handler(Default::default()), + pruning: Algorithm::Archive, + channel: service.channel(), + snapshot_root: dir, + client: client, + }; + + let service = Service::new(snapshot_params).unwrap(); + + assert!(service.manifest().is_none()); + assert!(service.chunk(Default::default()).is_none()); + assert_eq!(service.restoration_status(), RestorationStatus::Inactive); + + let manifest = ManifestData { + version: 2, + state_hashes: vec![], + block_hashes: vec![], + state_root: Default::default(), + block_number: 0, + block_hash: Default::default(), + }; + + service.begin_restore(manifest); + service.abort_restore(); + service.restore_state_chunk(Default::default(), vec![]); + service.restore_block_chunk(Default::default(), vec![]); + } + + #[test] + fn cannot_finish_with_invalid_chunks() { + use ethereum_types::H256; + use kvdb_rocksdb::DatabaseConfig; + + let spec = Spec::new_test(); + let tempdir = TempDir::new("").unwrap(); + + let state_hashes: Vec<_> = (0..5).map(|_| H256::random()).collect(); + let block_hashes: Vec<_> = (0..5).map(|_| H256::random()).collect(); + let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS); + let gb = spec.genesis_block(); + let flag = ::std::sync::atomic::AtomicBool::new(true); + + let params = RestorationParams { + manifest: ManifestData { + version: 2, + state_hashes: state_hashes.clone(), + block_hashes: block_hashes.clone(), + state_root: H256::default(), + block_number: 100000, + block_hash: H256::default(), + }, + pruning: Algorithm::Archive, + db: restoration_db_handler(db_config) + .open(&tempdir.path().to_owned()) + .unwrap(), + writer: None, + genesis: &gb, + guard: Guard::benign(), + engine: &*spec.engine.clone(), + }; + + let mut restoration = Restoration::new(params).unwrap(); + let definitely_bad_chunk = [1, 2, 3, 4, 5]; + + for hash in state_hashes { + assert!(restoration + .feed_state(hash, &definitely_bad_chunk, &flag) + .is_err()); + assert!(!restoration.is_done()); + } + + for hash in block_hashes { + assert!(restoration + .feed_blocks(hash, &definitely_bad_chunk, &*spec.engine, &flag) + .is_err()); + assert!(!restoration.is_done()); + } + } } diff --git a/ethcore/src/snapshot/tests/helpers.rs b/ethcore/src/snapshot/tests/helpers.rs index 817e0249986..7203a51ffa7 100644 --- a/ethcore/src/snapshot/tests/helpers.rs +++ b/ethcore/src/snapshot/tests/helpers.rs @@ -1,192 +1,198 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Snapshot test helpers. These are used to build blockchains and state tries //! which can be queried before and after a full snapshot/restore cycle. extern crate trie_standardmap; +use hash::KECCAK_NULL_RLP; use std::sync::Arc; -use hash::{KECCAK_NULL_RLP}; use account_db::AccountDBMut; -use types::basic_account::BasicAccount; use blockchain::{BlockChain, BlockChainDB}; -use client::{Client, ChainInfo}; +use client::{ChainInfo, Client}; use engines::EthEngine; -use snapshot::{StateRebuilder}; -use snapshot::io::{SnapshotReader, PackedWriter, PackedReader}; +use snapshot::{ + io::{PackedReader, PackedWriter, SnapshotReader}, + StateRebuilder, +}; +use types::basic_account::BasicAccount; -use tempdir::TempDir; use rand::Rng; +use tempdir::TempDir; -use kvdb::DBValue; +use self::trie_standardmap::{Alphabet, StandardMap, ValueMode}; use ethereum_types::H256; +use ethtrie::{SecTrieDBMut, TrieDB, TrieDBMut}; use hash_db::HashDB; -use keccak_hasher::KeccakHasher; use journaldb; -use trie::{TrieMut, Trie}; -use ethtrie::{SecTrieDBMut, TrieDB, TrieDBMut}; -use self::trie_standardmap::{Alphabet, StandardMap, ValueMode}; +use keccak_hasher::KeccakHasher; +use kvdb::DBValue; +use trie::{Trie, TrieMut}; // the proportion of accounts we will alter each tick. const ACCOUNT_CHURN: f32 = 0.01; /// This structure will incrementally alter a state given an rng. pub struct StateProducer { - state_root: H256, - storage_seed: H256, + state_root: H256, + storage_seed: H256, } impl StateProducer { - /// Create a new `StateProducer`. - pub fn new() -> Self { - StateProducer { - state_root: KECCAK_NULL_RLP, - storage_seed: H256::zero(), - } - } - - /// Tick the state producer. This alters the state, writing new data into - /// the database. - pub fn tick(&mut self, rng: &mut R, db: &mut HashDB) { - // modify existing accounts. - let mut accounts_to_modify: Vec<_> = { - let trie = TrieDB::new(&db, &self.state_root).unwrap(); - let temp = trie.iter().unwrap() // binding required due to complicated lifetime stuff - .filter(|_| rng.gen::() < ACCOUNT_CHURN) - .map(Result::unwrap) - .map(|(k, v)| (H256::from_slice(&k), v.to_owned())) - .collect(); - - temp - }; - - // sweep once to alter storage tries. - for &mut (ref mut address_hash, ref mut account_data) in &mut accounts_to_modify { - let mut account: BasicAccount = ::rlp::decode(&*account_data).expect("error decoding basic account"); - let acct_db = AccountDBMut::from_hash(db, *address_hash); - fill_storage(acct_db, &mut account.storage_root, &mut self.storage_seed); - *account_data = DBValue::from_vec(::rlp::encode(&account)); - } - - // sweep again to alter account trie. - let mut trie = TrieDBMut::from_existing(db, &mut self.state_root).unwrap(); - - for (address_hash, account_data) in accounts_to_modify { - trie.insert(&address_hash[..], &account_data).unwrap(); - } - - // add between 0 and 5 new accounts each tick. - let new_accs = rng.gen::() % 5; - - for _ in 0..new_accs { - let address_hash = H256(rng.gen()); - let balance: usize = rng.gen(); - let nonce: usize = rng.gen(); - let acc = ::state::Account::new_basic(balance.into(), nonce.into()).rlp(); - trie.insert(&address_hash[..], &acc).unwrap(); - } - } - - /// Get the current state root. - pub fn state_root(&self) -> H256 { - self.state_root - } + /// Create a new `StateProducer`. + pub fn new() -> Self { + StateProducer { + state_root: KECCAK_NULL_RLP, + storage_seed: H256::zero(), + } + } + + /// Tick the state producer. This alters the state, writing new data into + /// the database. + pub fn tick(&mut self, rng: &mut R, db: &mut dyn HashDB) { + // modify existing accounts. + let mut accounts_to_modify: Vec<_> = { + let trie = TrieDB::new(&db, &self.state_root).unwrap(); + let temp = trie + .iter() + .unwrap() // binding required due to complicated lifetime stuff + .filter(|_| rng.gen::() < ACCOUNT_CHURN) + .map(Result::unwrap) + .map(|(k, v)| (H256::from_slice(&k), v.to_owned())) + .collect(); + + temp + }; + + // sweep once to alter storage tries. + for &mut (ref mut address_hash, ref mut account_data) in &mut accounts_to_modify { + let mut account: BasicAccount = + ::rlp::decode(&*account_data).expect("error decoding basic account"); + let acct_db = AccountDBMut::from_hash(db, *address_hash); + fill_storage(acct_db, &mut account.storage_root, &mut self.storage_seed); + *account_data = DBValue::from_vec(::rlp::encode(&account)); + } + + // sweep again to alter account trie. + let mut trie = TrieDBMut::from_existing(db, &mut self.state_root).unwrap(); + + for (address_hash, account_data) in accounts_to_modify { + trie.insert(&address_hash[..], &account_data).unwrap(); + } + + // add between 0 and 5 new accounts each tick. + let new_accs = rng.gen::() % 5; + + for _ in 0..new_accs { + let address_hash = H256(rng.gen()); + let balance: usize = rng.gen(); + let nonce: usize = rng.gen(); + let acc = ::state::Account::new_basic(balance.into(), nonce.into()).rlp(); + trie.insert(&address_hash[..], &acc).unwrap(); + } + } + + /// Get the current state root. + pub fn state_root(&self) -> H256 { + self.state_root + } } /// Fill the storage of an account. pub fn fill_storage(mut db: AccountDBMut, root: &mut H256, seed: &mut H256) { - let map = StandardMap { - alphabet: Alphabet::All, - min_key: 6, - journal_key: 6, - value_mode: ValueMode::Random, - count: 100, - }; - { - let mut trie = if *root == KECCAK_NULL_RLP { - SecTrieDBMut::new(&mut db, root) - } else { - SecTrieDBMut::from_existing(&mut db, root).unwrap() - }; - - for (k, v) in map.make_with(seed) { - trie.insert(&k, &v).unwrap(); - } - } + let map = StandardMap { + alphabet: Alphabet::All, + min_key: 6, + journal_key: 6, + value_mode: ValueMode::Random, + count: 100, + }; + { + let mut trie = if *root == KECCAK_NULL_RLP { + SecTrieDBMut::new(&mut db, root) + } else { + SecTrieDBMut::from_existing(&mut db, root).unwrap() + }; + + for (k, v) in map.make_with(seed) { + trie.insert(&k, &v).unwrap(); + } + } } /// Take a snapshot from the given client into a temporary file. /// Return a snapshot reader for it. -pub fn snap(client: &Client) -> (Box, TempDir) { - use types::ids::BlockId; +pub fn snap(client: &Client) -> (Box, TempDir) { + use types::ids::BlockId; - let tempdir = TempDir::new("").unwrap(); - let path = tempdir.path().join("file"); - let writer = PackedWriter::new(&path).unwrap(); - let progress = Default::default(); + let tempdir = TempDir::new("").unwrap(); + let path = tempdir.path().join("file"); + let writer = PackedWriter::new(&path).unwrap(); + let progress = Default::default(); - let hash = client.chain_info().best_block_hash; - client.take_snapshot(writer, BlockId::Hash(hash), &progress).unwrap(); + let hash = client.chain_info().best_block_hash; + client + .take_snapshot(writer, BlockId::Hash(hash), &progress) + .unwrap(); - let reader = PackedReader::new(&path).unwrap().unwrap(); + let reader = PackedReader::new(&path).unwrap().unwrap(); - (Box::new(reader), tempdir) + (Box::new(reader), tempdir) } /// Restore a snapshot into a given database. This will read chunks from the given reader /// write into the given database. pub fn restore( - db: Arc, - engine: &EthEngine, - reader: &SnapshotReader, - genesis: &[u8], + db: Arc, + engine: &dyn EthEngine, + reader: &dyn SnapshotReader, + genesis: &[u8], ) -> Result<(), ::error::Error> { - use std::sync::atomic::AtomicBool; - use snappy; - - let flag = AtomicBool::new(true); - let components = engine.snapshot_components().unwrap(); - let manifest = reader.manifest(); - - let mut state = StateRebuilder::new(db.key_value().clone(), journaldb::Algorithm::Archive); - let mut secondary = { - let chain = BlockChain::new(Default::default(), genesis, db.clone()); - components.rebuilder(chain, db, manifest).unwrap() - }; - - let mut snappy_buffer = Vec::new(); - - trace!(target: "snapshot", "restoring state"); - for state_chunk_hash in manifest.state_hashes.iter() { - trace!(target: "snapshot", "state chunk hash: {}", state_chunk_hash); - let chunk = reader.chunk(*state_chunk_hash).unwrap(); - let len = snappy::decompress_into(&chunk, &mut snappy_buffer).unwrap(); - state.feed(&snappy_buffer[..len], &flag)?; - } - - trace!(target: "snapshot", "restoring secondary"); - for chunk_hash in manifest.block_hashes.iter() { - let chunk = reader.chunk(*chunk_hash).unwrap(); - let len = snappy::decompress_into(&chunk, &mut snappy_buffer).unwrap(); - secondary.feed(&snappy_buffer[..len], engine, &flag)?; - } - - trace!(target: "snapshot", "finalizing"); - state.finalize(manifest.block_number, manifest.block_hash)?; - secondary.finalize(engine) + use std::sync::atomic::AtomicBool; + + let flag = AtomicBool::new(true); + let components = engine.snapshot_components().unwrap(); + let manifest = reader.manifest(); + + let mut state = StateRebuilder::new(db.key_value().clone(), journaldb::Algorithm::Archive); + let mut secondary = { + let chain = BlockChain::new(Default::default(), genesis, db.clone()); + components.rebuilder(chain, db, manifest).unwrap() + }; + + let mut snappy_buffer = Vec::new(); + + trace!(target: "snapshot", "restoring state"); + for state_chunk_hash in manifest.state_hashes.iter() { + trace!(target: "snapshot", "state chunk hash: {}", state_chunk_hash); + let chunk = reader.chunk(*state_chunk_hash).unwrap(); + let len = snappy::decompress_into(&chunk, &mut snappy_buffer).unwrap(); + state.feed(&snappy_buffer[..len], &flag)?; + } + + trace!(target: "snapshot", "restoring secondary"); + for chunk_hash in manifest.block_hashes.iter() { + let chunk = reader.chunk(*chunk_hash).unwrap(); + let len = snappy::decompress_into(&chunk, &mut snappy_buffer).unwrap(); + secondary.feed(&snappy_buffer[..len], engine, &flag)?; + } + + trace!(target: "snapshot", "finalizing"); + state.finalize(manifest.block_number, manifest.block_hash)?; + secondary.finalize(engine) } diff --git a/ethcore/src/snapshot/tests/mod.rs b/ethcore/src/snapshot/tests/mod.rs index f25fd03b258..96e95bd815c 100644 --- a/ethcore/src/snapshot/tests/mod.rs +++ b/ethcore/src/snapshot/tests/mod.rs @@ -1,25 +1,25 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Snapshot tests. -mod proof_of_work; mod proof_of_authority; -mod state; +mod proof_of_work; mod service; +mod state; pub mod helpers; @@ -27,14 +27,14 @@ use super::ManifestData; #[test] fn manifest_rlp() { - let manifest = ManifestData { - version: 2, - block_hashes: Vec::new(), - state_hashes: Vec::new(), - block_number: 1234567, - state_root: Default::default(), - block_hash: Default::default(), - }; - let raw = manifest.clone().into_rlp(); - assert_eq!(ManifestData::from_rlp(&raw).unwrap(), manifest); + let manifest = ManifestData { + version: 2, + block_hashes: Vec::new(), + state_hashes: Vec::new(), + block_number: 1234567, + state_root: Default::default(), + block_hash: Default::default(), + }; + let raw = manifest.clone().into_rlp(); + assert_eq!(ManifestData::from_rlp(&raw).unwrap(), manifest); } diff --git a/ethcore/src/snapshot/tests/proof_of_authority.rs b/ethcore/src/snapshot/tests/proof_of_authority.rs index f1610e6ccda..229a9f4341e 100644 --- a/ethcore/src/snapshot/tests/proof_of_authority.rs +++ b/ethcore/src/snapshot/tests/proof_of_authority.rs @@ -1,33 +1,31 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! PoA block chunker and rebuilder tests. -use std::cell::RefCell; -use std::sync::Arc; -use std::str::FromStr; +use std::{cell::RefCell, str::FromStr, sync::Arc}; use accounts::AccountProvider; -use client::{Client, BlockChainClient, ChainInfo}; +use client::{BlockChainClient, ChainInfo, Client}; use ethkey::Secret; use snapshot::tests::helpers as snapshot_helpers; use spec::Spec; -use test_helpers::generate_dummy_client_with_spec; -use types::transaction::{Transaction, Action, SignedTransaction}; use tempdir::TempDir; +use test_helpers::generate_dummy_client_with_spec; +use types::transaction::{Action, SignedTransaction, Transaction}; use ethereum_types::Address; use test_helpers; @@ -39,17 +37,19 @@ const TRANSITION_BLOCK_1: usize = 2; // block at which the contract becomes acti const TRANSITION_BLOCK_2: usize = 10; // block at which the second contract activates. macro_rules! secret { - ($e: expr) => { Secret::from($crate::hash::keccak($e).0) } + ($e: expr) => { + Secret::from($crate::hash::keccak($e).0) + }; } lazy_static! { - // contract addresses. - static ref CONTRACT_ADDR_1: Address = Address::from_str("0000000000000000000000000000000000000005").unwrap(); - static ref CONTRACT_ADDR_2: Address = Address::from_str("0000000000000000000000000000000000000006").unwrap(); - // secret: `keccak(1)`, and initial validator. - static ref RICH_ADDR: Address = Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap(); - // rich address' secret. - static ref RICH_SECRET: Secret = secret!("1"); + // contract addresses. + static ref CONTRACT_ADDR_1: Address = Address::from_str("0000000000000000000000000000000000000005").unwrap(); + static ref CONTRACT_ADDR_2: Address = Address::from_str("0000000000000000000000000000000000000006").unwrap(); + // secret: `keccak(1)`, and initial validator. + static ref RICH_ADDR: Address = Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap(); + // rich address' secret. + static ref RICH_SECRET: Secret = secret!("1"); } /// Contract code used here: https://gist.github.com/anonymous/2a43783647e0f0dfcc359bd6fd81d6d9 @@ -58,207 +58,234 @@ lazy_static! { /// Create a new Spec with AuthorityRound which uses a contract at address 5 to determine the current validators using `getValidators`. /// `test_validator_set::ValidatorSet` provides a native wrapper for the ABi. fn spec_fixed_to_contract() -> Spec { - let data = include_bytes!("test_validator_contract.json"); - let tempdir = TempDir::new("").unwrap(); - Spec::load(&tempdir.path(), &data[..]).unwrap() + let data = include_bytes!("test_validator_contract.json"); + let tempdir = TempDir::new("").unwrap(); + Spec::load(&tempdir.path(), &data[..]).unwrap() } // creates an account provider, filling it with accounts from all the given // secrets and password `PASS`. // returns addresses corresponding to secrets. fn make_accounts(secrets: &[Secret]) -> (Arc, Vec
) { - let provider = AccountProvider::transient_provider(); + let provider = AccountProvider::transient_provider(); - let addrs = secrets.iter() - .cloned() - .map(|s| provider.insert_account(s, &PASS.into()).unwrap()) - .collect(); + let addrs = secrets + .iter() + .cloned() + .map(|s| provider.insert_account(s, &PASS.into()).unwrap()) + .collect(); - (Arc::new(provider), addrs) + (Arc::new(provider), addrs) } // validator transition. block number and new validators. must be after `TRANSITION_BLOCK`. // all addresses in the set must be in the account provider. enum Transition { - // manual transition via transaction - Manual(usize, Vec
), - // implicit transition via multi-set - Implicit(usize, Vec
), + // manual transition via transaction + Manual(usize, Vec
), + // implicit transition via multi-set + Implicit(usize, Vec
), } // create a chain with the given transitions and some blocks beyond that transition. -fn make_chain(accounts: Arc, blocks_beyond: usize, transitions: Vec) -> Arc { - let client = generate_dummy_client_with_spec(spec_fixed_to_contract); - - let mut cur_signers = vec![*RICH_ADDR]; - { - let engine = client.engine(); - engine.register_client(Arc::downgrade(&client) as _); - } - - { - // push a block with given number, signed by one of the signers, with given transactions. - let push_block = |signers: &[Address], n, txs: Vec| { - use miner::{self, MinerService}; - - let idx = n as usize % signers.len(); - trace!(target: "snapshot", "Pushing block #{}, {} txs, author={}", +fn make_chain( + accounts: Arc, + blocks_beyond: usize, + transitions: Vec, +) -> Arc { + let client = generate_dummy_client_with_spec(spec_fixed_to_contract); + + let mut cur_signers = vec![*RICH_ADDR]; + { + let engine = client.engine(); + engine.register_client(Arc::downgrade(&client) as _); + } + + { + // push a block with given number, signed by one of the signers, with given transactions. + let push_block = |signers: &[Address], n, txs: Vec| { + use miner::{self, MinerService}; + + let idx = n as usize % signers.len(); + trace!(target: "snapshot", "Pushing block #{}, {} txs, author={}", n, txs.len(), signers[idx]); - let signer = Box::new((accounts.clone(), signers[idx], PASS.into())); - client.miner().set_author(miner::Author::Sealer(signer)); - client.miner().import_external_transactions(&*client, - txs.into_iter().map(Into::into).collect()); - - client.engine().step(); - - assert_eq!(client.chain_info().best_block_number, n); - }; - - // execution callback for native contract: push transaction to be sealed. - let nonce = RefCell::new(client.engine().account_start_nonce(0)); - - // create useless transactions vector so we don't have to dig in - // and force sealing. - let make_useless_transactions = || { - let mut nonce = nonce.borrow_mut(); - let transaction = Transaction { - nonce: *nonce, - gas_price: 1.into(), - gas: 21_000.into(), - action: Action::Call(Address::new()), - value: 1.into(), - data: Vec::new(), - }.sign(&*RICH_SECRET, client.signing_chain_id()); - - *nonce = *nonce + 1; - vec![transaction] - }; - - // apply all transitions. - for transition in transitions { - let (num, manual, new_set) = match transition { - Transition::Manual(num, new_set) => (num, true, new_set), - Transition::Implicit(num, new_set) => (num, false, new_set), - }; - - if num < TRANSITION_BLOCK_1 { - panic!("Bad test: issued epoch change before transition to contract."); - } - - if (num as u64) < client.chain_info().best_block_number { - panic!("Bad test: issued epoch change before previous transition finalized."); - } - - for number in client.chain_info().best_block_number + 1 .. num as u64 { - push_block(&cur_signers, number, make_useless_transactions()); - } - - let pending = if manual { - trace!(target: "snapshot", "applying set transition at block #{}", num); - let address = match num >= TRANSITION_BLOCK_2 { - true => &CONTRACT_ADDR_2 as &Address, - false => &CONTRACT_ADDR_1 as &Address, - }; - - let data = test_validator_set::functions::set_validators::encode_input(new_set.clone()); - let mut nonce = nonce.borrow_mut(); - let transaction = Transaction { - nonce: *nonce, - gas_price: 0.into(), - gas: 1_000_000.into(), - action: Action::Call(*address), - value: 0.into(), - data, - }.sign(&*RICH_SECRET, client.signing_chain_id()); - - *nonce = *nonce + 1; - vec![transaction] - } else { - make_useless_transactions() - }; - - // push transition block. - push_block(&cur_signers, num as u64, pending); - - // push blocks to finalize transition - for finalization_count in 1.. { - if finalization_count * 2 > cur_signers.len() { break } - push_block(&cur_signers, (num + finalization_count) as u64, make_useless_transactions()); - } - - cur_signers = new_set; - } - - // make blocks beyond. - for number in (client.chain_info().best_block_number..).take(blocks_beyond) { - push_block(&cur_signers, number + 1, make_useless_transactions()); - } - } - - client + let signer = Box::new((accounts.clone(), signers[idx], PASS.into())); + client.miner().set_author(miner::Author::Sealer(signer)); + client + .miner() + .import_external_transactions(&*client, txs.into_iter().map(Into::into).collect()); + + client.engine().step(); + + assert_eq!(client.chain_info().best_block_number, n); + }; + + // execution callback for native contract: push transaction to be sealed. + let nonce = RefCell::new(client.engine().account_start_nonce(0)); + + // create useless transactions vector so we don't have to dig in + // and force sealing. + let make_useless_transactions = || { + let mut nonce = nonce.borrow_mut(); + let transaction = Transaction { + nonce: *nonce, + gas_price: 1.into(), + gas: 21_000.into(), + action: Action::Call(Address::new()), + value: 1.into(), + data: Vec::new(), + } + .sign(&*RICH_SECRET, client.signing_chain_id()); + + *nonce = *nonce + 1; + vec![transaction] + }; + + // apply all transitions. + for transition in transitions { + let (num, manual, new_set) = match transition { + Transition::Manual(num, new_set) => (num, true, new_set), + Transition::Implicit(num, new_set) => (num, false, new_set), + }; + + if num < TRANSITION_BLOCK_1 { + panic!("Bad test: issued epoch change before transition to contract."); + } + + if (num as u64) < client.chain_info().best_block_number { + panic!("Bad test: issued epoch change before previous transition finalized."); + } + + for number in client.chain_info().best_block_number + 1..num as u64 { + push_block(&cur_signers, number, make_useless_transactions()); + } + + let pending = if manual { + trace!(target: "snapshot", "applying set transition at block #{}", num); + let address = match num >= TRANSITION_BLOCK_2 { + true => &CONTRACT_ADDR_2 as &Address, + false => &CONTRACT_ADDR_1 as &Address, + }; + + let data = + test_validator_set::functions::set_validators::encode_input(new_set.clone()); + let mut nonce = nonce.borrow_mut(); + let transaction = Transaction { + nonce: *nonce, + gas_price: 0.into(), + gas: 1_000_000.into(), + action: Action::Call(*address), + value: 0.into(), + data, + } + .sign(&*RICH_SECRET, client.signing_chain_id()); + + *nonce = *nonce + 1; + vec![transaction] + } else { + make_useless_transactions() + }; + + // push transition block. + push_block(&cur_signers, num as u64, pending); + + // push blocks to finalize transition + for finalization_count in 1.. { + if finalization_count * 2 > cur_signers.len() { + break; + } + push_block( + &cur_signers, + (num + finalization_count) as u64, + make_useless_transactions(), + ); + } + + cur_signers = new_set; + } + + // make blocks beyond. + for number in (client.chain_info().best_block_number..).take(blocks_beyond) { + push_block(&cur_signers, number + 1, make_useless_transactions()); + } + } + + client } #[test] fn fixed_to_contract_only() { - let (provider, addrs) = make_accounts(&[ - RICH_SECRET.clone(), - secret!("foo"), - secret!("bar"), - secret!("test"), - secret!("signer"), - secret!("crypto"), - secret!("wizard"), - secret!("dog42"), - ]); - - assert!(provider.has_account(*RICH_ADDR)); - - let client = make_chain(provider, 3, vec![ - Transition::Manual(3, vec![addrs[2], addrs[3], addrs[5], addrs[7]]), - Transition::Manual(6, vec![addrs[0], addrs[1], addrs[4], addrs[6]]), - ]); - - // 6, 7, 8 prove finality for transition at 6. - // 3 beyond gets us to 11. - assert_eq!(client.chain_info().best_block_number, 11); - let (reader, _tempdir) = snapshot_helpers::snap(&*client); - - let new_db = test_helpers::new_db(); - let spec = spec_fixed_to_contract(); - - // ensure fresh engine's step matches. - for _ in 0..11 { spec.engine.step() } - snapshot_helpers::restore(new_db, &*spec.engine, &*reader, &spec.genesis_block()).unwrap(); + let (provider, addrs) = make_accounts(&[ + RICH_SECRET.clone(), + secret!("foo"), + secret!("bar"), + secret!("test"), + secret!("signer"), + secret!("crypto"), + secret!("wizard"), + secret!("dog42"), + ]); + + assert!(provider.has_account(*RICH_ADDR)); + + let client = make_chain( + provider, + 3, + vec![ + Transition::Manual(3, vec![addrs[2], addrs[3], addrs[5], addrs[7]]), + Transition::Manual(6, vec![addrs[0], addrs[1], addrs[4], addrs[6]]), + ], + ); + + // 6, 7, 8 prove finality for transition at 6. + // 3 beyond gets us to 11. + assert_eq!(client.chain_info().best_block_number, 11); + let (reader, _tempdir) = snapshot_helpers::snap(&*client); + + let new_db = test_helpers::new_db(); + let spec = spec_fixed_to_contract(); + + // ensure fresh engine's step matches. + for _ in 0..11 { + spec.engine.step() + } + snapshot_helpers::restore(new_db, &*spec.engine, &*reader, &spec.genesis_block()).unwrap(); } #[test] fn fixed_to_contract_to_contract() { - let (provider, addrs) = make_accounts(&[ - RICH_SECRET.clone(), - secret!("foo"), - secret!("bar"), - secret!("test"), - secret!("signer"), - secret!("crypto"), - secret!("wizard"), - secret!("dog42"), - ]); - - assert!(provider.has_account(*RICH_ADDR)); - - let client = make_chain(provider, 3, vec![ - Transition::Manual(3, vec![addrs[2], addrs[3], addrs[5], addrs[7]]), - Transition::Manual(6, vec![addrs[0], addrs[1], addrs[4], addrs[6]]), - Transition::Implicit(10, vec![addrs[0]]), - Transition::Manual(13, vec![addrs[2], addrs[4], addrs[6], addrs[7]]), - ]); - - assert_eq!(client.chain_info().best_block_number, 16); - let (reader, _tempdir) = snapshot_helpers::snap(&*client); - let new_db = test_helpers::new_db(); - let spec = spec_fixed_to_contract(); - - for _ in 0..16 { spec.engine.step() } - snapshot_helpers::restore(new_db, &*spec.engine, &*reader, &spec.genesis_block()).unwrap(); + let (provider, addrs) = make_accounts(&[ + RICH_SECRET.clone(), + secret!("foo"), + secret!("bar"), + secret!("test"), + secret!("signer"), + secret!("crypto"), + secret!("wizard"), + secret!("dog42"), + ]); + + assert!(provider.has_account(*RICH_ADDR)); + + let client = make_chain( + provider, + 3, + vec![ + Transition::Manual(3, vec![addrs[2], addrs[3], addrs[5], addrs[7]]), + Transition::Manual(6, vec![addrs[0], addrs[1], addrs[4], addrs[6]]), + Transition::Implicit(10, vec![addrs[0]]), + Transition::Manual(13, vec![addrs[2], addrs[4], addrs[6], addrs[7]]), + ], + ); + + assert_eq!(client.chain_info().best_block_number, 16); + let (reader, _tempdir) = snapshot_helpers::snap(&*client); + let new_db = test_helpers::new_db(); + let spec = spec_fixed_to_contract(); + + for _ in 0..16 { + spec.engine.step() + } + snapshot_helpers::restore(new_db, &*spec.engine, &*reader, &spec.genesis_block()).unwrap(); } diff --git a/ethcore/src/snapshot/tests/proof_of_work.rs b/ethcore/src/snapshot/tests/proof_of_work.rs index d970da406cb..44506f23ea6 100644 --- a/ethcore/src/snapshot/tests/proof_of_work.rs +++ b/ethcore/src/snapshot/tests/proof_of_work.rs @@ -1,149 +1,172 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! PoW block chunker and rebuilder tests. +use error::{Error, ErrorKind}; use std::sync::atomic::AtomicBool; use tempdir::TempDir; -use error::{Error, ErrorKind}; -use blockchain::generator::{BlockGenerator, BlockBuilder}; -use blockchain::{BlockChain, ExtrasInsert}; -use snapshot::{chunk_secondary, Error as SnapshotError, Progress, SnapshotComponents}; -use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter}; +use blockchain::{ + generator::{BlockBuilder, BlockGenerator}, + BlockChain, ExtrasInsert, +}; +use snapshot::{ + chunk_secondary, + io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter}, + Error as SnapshotError, Progress, SnapshotComponents, +}; +use kvdb::DBTransaction; use parking_lot::Mutex; use snappy; -use kvdb::DBTransaction; use test_helpers; -const SNAPSHOT_MODE: ::snapshot::PowSnapshot = ::snapshot::PowSnapshot { blocks: 30000, max_restore_blocks: 30000 }; +const SNAPSHOT_MODE: ::snapshot::PowSnapshot = ::snapshot::PowSnapshot { + blocks: 30000, + max_restore_blocks: 30000, +}; fn chunk_and_restore(amount: u64) { - let genesis = BlockBuilder::genesis(); - let rest = genesis.add_blocks(amount as usize); - let generator = BlockGenerator::new(vec![rest]); - let genesis = genesis.last(); - - let engine = ::spec::Spec::new_test().engine; - let tempdir = TempDir::new("").unwrap(); - let snapshot_path = tempdir.path().join("SNAP"); - - let old_db = test_helpers::new_db(); - let bc = BlockChain::new(Default::default(), genesis.encoded().raw(), old_db.clone()); - - // build the blockchain. - let mut batch = DBTransaction::new(); - for block in generator { - bc.insert_block(&mut batch, block.encoded(), vec![], ExtrasInsert { - fork_choice: ::engines::ForkChoice::New, - is_finalized: false, - }); - bc.commit(); - } - - old_db.key_value().write(batch).unwrap(); - - let best_hash = bc.best_block_hash(); - - // snapshot it. - let writer = Mutex::new(PackedWriter::new(&snapshot_path).unwrap()); - let block_hashes = chunk_secondary( - Box::new(SNAPSHOT_MODE), - &bc, - best_hash, - &writer, - &Progress::default() - ).unwrap(); - - let manifest = ::snapshot::ManifestData { - version: 2, - state_hashes: Vec::new(), - block_hashes: block_hashes, - state_root: ::hash::KECCAK_NULL_RLP, - block_number: amount, - block_hash: best_hash, - }; - - writer.into_inner().finish(manifest.clone()).unwrap(); - - // restore it. - let new_db = test_helpers::new_db(); - let new_chain = BlockChain::new(Default::default(), genesis.encoded().raw(), new_db.clone()); - let mut rebuilder = SNAPSHOT_MODE.rebuilder(new_chain, new_db.clone(), &manifest).unwrap(); - - let reader = PackedReader::new(&snapshot_path).unwrap().unwrap(); - let flag = AtomicBool::new(true); - for chunk_hash in &reader.manifest().block_hashes { - let compressed = reader.chunk(*chunk_hash).unwrap(); - let chunk = snappy::decompress(&compressed).unwrap(); - rebuilder.feed(&chunk, engine.as_ref(), &flag).unwrap(); - } - - rebuilder.finalize(engine.as_ref()).unwrap(); - drop(rebuilder); - - // and test it. - let new_chain = BlockChain::new(Default::default(), genesis.encoded().raw(), new_db); - assert_eq!(new_chain.best_block_hash(), best_hash); + let genesis = BlockBuilder::genesis(); + let rest = genesis.add_blocks(amount as usize); + let generator = BlockGenerator::new(vec![rest]); + let genesis = genesis.last(); + + let engine = ::spec::Spec::new_test().engine; + let tempdir = TempDir::new("").unwrap(); + let snapshot_path = tempdir.path().join("SNAP"); + + let old_db = test_helpers::new_db(); + let bc = BlockChain::new(Default::default(), genesis.encoded().raw(), old_db.clone()); + + // build the blockchain. + let mut batch = DBTransaction::new(); + for block in generator { + bc.insert_block( + &mut batch, + block.encoded(), + vec![], + ExtrasInsert { + fork_choice: ::engines::ForkChoice::New, + is_finalized: false, + }, + ); + bc.commit(); + } + + old_db.key_value().write(batch).unwrap(); + + let best_hash = bc.best_block_hash(); + + // snapshot it. + let writer = Mutex::new(PackedWriter::new(&snapshot_path).unwrap()); + let block_hashes = chunk_secondary( + Box::new(SNAPSHOT_MODE), + &bc, + best_hash, + &writer, + &Progress::default(), + ) + .unwrap(); + + let manifest = ::snapshot::ManifestData { + version: 2, + state_hashes: Vec::new(), + block_hashes: block_hashes, + state_root: ::hash::KECCAK_NULL_RLP, + block_number: amount, + block_hash: best_hash, + }; + + writer.into_inner().finish(manifest.clone()).unwrap(); + + // restore it. + let new_db = test_helpers::new_db(); + let new_chain = BlockChain::new(Default::default(), genesis.encoded().raw(), new_db.clone()); + let mut rebuilder = SNAPSHOT_MODE + .rebuilder(new_chain, new_db.clone(), &manifest) + .unwrap(); + + let reader = PackedReader::new(&snapshot_path).unwrap().unwrap(); + let flag = AtomicBool::new(true); + for chunk_hash in &reader.manifest().block_hashes { + let compressed = reader.chunk(*chunk_hash).unwrap(); + let chunk = snappy::decompress(&compressed).unwrap(); + rebuilder.feed(&chunk, engine.as_ref(), &flag).unwrap(); + } + + rebuilder.finalize(engine.as_ref()).unwrap(); + drop(rebuilder); + + // and test it. + let new_chain = BlockChain::new(Default::default(), genesis.encoded().raw(), new_db); + assert_eq!(new_chain.best_block_hash(), best_hash); } #[test] fn chunk_and_restore_500() { - chunk_and_restore(500) + chunk_and_restore(500) } #[test] fn chunk_and_restore_4k() { - chunk_and_restore(4000) + chunk_and_restore(4000) } #[test] fn checks_flag() { - use rlp::RlpStream; - use ethereum_types::H256; - - let mut stream = RlpStream::new_list(5); - - stream.append(&100u64) - .append(&H256::default()) - .append(&(!0u64)); - - stream.append_empty_data().append_empty_data(); - - let genesis = BlockBuilder::genesis(); - let chunk = stream.out(); - - let db = test_helpers::new_db(); - let engine = ::spec::Spec::new_test().engine; - let chain = BlockChain::new(Default::default(), genesis.last().encoded().raw(), db.clone()); - - let manifest = ::snapshot::ManifestData { - version: 2, - state_hashes: Vec::new(), - block_hashes: Vec::new(), - state_root: ::hash::KECCAK_NULL_RLP, - block_number: 102, - block_hash: H256::default(), - }; - - let mut rebuilder = SNAPSHOT_MODE.rebuilder(chain, db.clone(), &manifest).unwrap(); - - match rebuilder.feed(&chunk, engine.as_ref(), &AtomicBool::new(false)) { - Err(Error(ErrorKind::Snapshot(SnapshotError::RestorationAborted), _)) => {} - _ => panic!("Wrong result on abort flag set") - } + use ethereum_types::H256; + use rlp::RlpStream; + + let mut stream = RlpStream::new_list(5); + + stream + .append(&100u64) + .append(&H256::default()) + .append(&(!0u64)); + + stream.append_empty_data().append_empty_data(); + + let genesis = BlockBuilder::genesis(); + let chunk = stream.out(); + + let db = test_helpers::new_db(); + let engine = ::spec::Spec::new_test().engine; + let chain = BlockChain::new( + Default::default(), + genesis.last().encoded().raw(), + db.clone(), + ); + + let manifest = ::snapshot::ManifestData { + version: 2, + state_hashes: Vec::new(), + block_hashes: Vec::new(), + state_root: ::hash::KECCAK_NULL_RLP, + block_number: 102, + block_hash: H256::default(), + }; + + let mut rebuilder = SNAPSHOT_MODE + .rebuilder(chain, db.clone(), &manifest) + .unwrap(); + + match rebuilder.feed(&chunk, engine.as_ref(), &AtomicBool::new(false)) { + Err(Error(ErrorKind::Snapshot(SnapshotError::RestorationAborted), _)) => {} + _ => panic!("Wrong result on abort flag set"), + } } diff --git a/ethcore/src/snapshot/tests/service.rs b/ethcore/src/snapshot/tests/service.rs index 37a10048abf..7ca2cd1baf2 100644 --- a/ethcore/src/snapshot/tests/service.rs +++ b/ethcore/src/snapshot/tests/service.rs @@ -1,102 +1,108 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Tests for the snapshot service. -use std::fs; -use std::sync::Arc; +use std::{fs, sync::Arc}; -use tempdir::TempDir; use blockchain::BlockProvider; -use client::{Client, ClientConfig, ImportBlock, BlockInfo}; -use types::ids::BlockId; -use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter}; -use snapshot::service::{Service, ServiceParams}; -use snapshot::{chunk_state, chunk_secondary, ManifestData, Progress, SnapshotService, RestorationStatus}; +use client::{BlockInfo, Client, ClientConfig, ImportBlock}; +use snapshot::{ + chunk_secondary, chunk_state, + io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter}, + service::{Service, ServiceParams}, + ManifestData, Progress, RestorationStatus, SnapshotService, +}; use spec::Spec; -use test_helpers::{new_db, new_temp_db, generate_dummy_client_with_spec_and_data, restoration_db_handler}; +use tempdir::TempDir; +use test_helpers::{ + generate_dummy_client_with_spec_and_data, new_db, new_temp_db, restoration_db_handler, +}; +use types::ids::BlockId; -use parking_lot::Mutex; use io::IoChannel; use kvdb_rocksdb::DatabaseConfig; +use parking_lot::Mutex; use verification::queue::kind::blocks::Unverified; #[test] fn restored_is_equivalent() { - let _ = ::env_logger::try_init(); - - const NUM_BLOCKS: u32 = 400; - const TX_PER: usize = 5; - - let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()]; - let client = generate_dummy_client_with_spec_and_data(Spec::new_null, NUM_BLOCKS, TX_PER, &gas_prices); - - let tempdir = TempDir::new("").unwrap(); - let client_db = tempdir.path().join("client_db"); - let path = tempdir.path().join("snapshot"); - - let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS); - let restoration = restoration_db_handler(db_config); - let blockchain_db = restoration.open(&client_db).unwrap(); - - let spec = Spec::new_null(); - let client2 = Client::new( - Default::default(), - &spec, - blockchain_db, - Arc::new(::miner::Miner::new_for_tests(&spec, None)), - IoChannel::disconnected(), - ).unwrap(); - - let service_params = ServiceParams { - engine: spec.engine.clone(), - genesis_block: spec.genesis_block(), - restoration_db_handler: restoration, - pruning: ::journaldb::Algorithm::Archive, - channel: IoChannel::disconnected(), - snapshot_root: path, - client: client2.clone(), - }; - - let service = Service::new(service_params).unwrap(); - service.take_snapshot(&client, NUM_BLOCKS as u64).unwrap(); - - let manifest = service.manifest().unwrap(); - - service.init_restore(manifest.clone(), true).unwrap(); - assert!(service.init_restore(manifest.clone(), true).is_ok()); - - for hash in manifest.state_hashes { - let chunk = service.chunk(hash).unwrap(); - service.feed_state_chunk(hash, &chunk); - } - - for hash in manifest.block_hashes { - let chunk = service.chunk(hash).unwrap(); - service.feed_block_chunk(hash, &chunk); - } - - assert_eq!(service.status(), RestorationStatus::Inactive); - - for x in 0..NUM_BLOCKS { - let block1 = client.block(BlockId::Number(x as u64)).unwrap(); - let block2 = client2.block(BlockId::Number(x as u64)).unwrap(); - - assert_eq!(block1, block2); - } + let _ = ::env_logger::try_init(); + + const NUM_BLOCKS: u32 = 400; + const TX_PER: usize = 5; + + let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()]; + let client = + generate_dummy_client_with_spec_and_data(Spec::new_null, NUM_BLOCKS, TX_PER, &gas_prices); + + let tempdir = TempDir::new("").unwrap(); + let client_db = tempdir.path().join("client_db"); + let path = tempdir.path().join("snapshot"); + + let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS); + let restoration = restoration_db_handler(db_config); + let blockchain_db = restoration.open(&client_db).unwrap(); + + let spec = Spec::new_null(); + let client2 = Client::new( + Default::default(), + &spec, + blockchain_db, + Arc::new(::miner::Miner::new_for_tests(&spec, None)), + IoChannel::disconnected(), + ) + .unwrap(); + + let service_params = ServiceParams { + engine: spec.engine.clone(), + genesis_block: spec.genesis_block(), + restoration_db_handler: restoration, + pruning: ::journaldb::Algorithm::Archive, + channel: IoChannel::disconnected(), + snapshot_root: path, + client: client2.clone(), + }; + + let service = Service::new(service_params).unwrap(); + service.take_snapshot(&client, NUM_BLOCKS as u64).unwrap(); + + let manifest = service.manifest().unwrap(); + + service.init_restore(manifest.clone(), true).unwrap(); + assert!(service.init_restore(manifest.clone(), true).is_ok()); + + for hash in manifest.state_hashes { + let chunk = service.chunk(hash).unwrap(); + service.feed_state_chunk(hash, &chunk); + } + + for hash in manifest.block_hashes { + let chunk = service.chunk(hash).unwrap(); + service.feed_block_chunk(hash, &chunk); + } + + assert_eq!(service.restoration_status(), RestorationStatus::Inactive); + + for x in 0..NUM_BLOCKS { + let block1 = client.block(BlockId::Number(x as u64)).unwrap(); + let block2 = client2.block(BlockId::Number(x as u64)).unwrap(); + + assert_eq!(block1, block2); + } } // on windows the guards deletion (remove_dir_all) @@ -105,244 +111,275 @@ fn restored_is_equivalent() { #[cfg(not(target_os = "windows"))] #[test] fn guards_delete_folders() { - let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()]; - let client = generate_dummy_client_with_spec_and_data(Spec::new_null, 400, 5, &gas_prices); - - let spec = Spec::new_null(); - let tempdir = TempDir::new("").unwrap(); - let service_params = ServiceParams { - engine: spec.engine.clone(), - genesis_block: spec.genesis_block(), - restoration_db_handler: restoration_db_handler(DatabaseConfig::with_columns(::db::NUM_COLUMNS)), - pruning: ::journaldb::Algorithm::Archive, - channel: IoChannel::disconnected(), - snapshot_root: tempdir.path().to_owned(), - client: client, - }; - - let service = Service::new(service_params).unwrap(); - let path = tempdir.path().join("restoration"); - - let manifest = ManifestData { - version: 2, - state_hashes: vec![], - block_hashes: vec![], - block_number: 0, - block_hash: Default::default(), - state_root: Default::default(), - }; - - service.init_restore(manifest.clone(), true).unwrap(); - assert!(path.exists()); - - // The `db` folder should have been deleted, - // while the `temp` one kept - service.abort_restore(); - assert!(!path.join("db").exists()); - assert!(path.join("temp").exists()); - - service.init_restore(manifest.clone(), true).unwrap(); - assert!(path.exists()); - - drop(service); - assert!(!path.join("db").exists()); - assert!(path.join("temp").exists()); + let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()]; + let client = generate_dummy_client_with_spec_and_data(Spec::new_null, 400, 5, &gas_prices); + + let spec = Spec::new_null(); + let tempdir = TempDir::new("").unwrap(); + let service_params = ServiceParams { + engine: spec.engine.clone(), + genesis_block: spec.genesis_block(), + restoration_db_handler: restoration_db_handler(DatabaseConfig::with_columns( + ::db::NUM_COLUMNS, + )), + pruning: ::journaldb::Algorithm::Archive, + channel: IoChannel::disconnected(), + snapshot_root: tempdir.path().to_owned(), + client: client, + }; + + let service = Service::new(service_params).unwrap(); + let path = tempdir.path().join("restoration"); + + let manifest = ManifestData { + version: 2, + state_hashes: vec![], + block_hashes: vec![], + block_number: 0, + block_hash: Default::default(), + state_root: Default::default(), + }; + + service.init_restore(manifest.clone(), true).unwrap(); + assert!(path.exists()); + + // The `db` folder should have been deleted, + // while the `temp` one kept + service.abort_restore(); + assert!(!path.join("db").exists()); + assert!(path.join("temp").exists()); + + service.init_restore(manifest.clone(), true).unwrap(); + assert!(path.exists()); + + drop(service); + assert!(!path.join("db").exists()); + assert!(path.join("temp").exists()); } #[test] fn keep_ancient_blocks() { - let _ = ::env_logger::try_init(); - - // Test variables - const NUM_BLOCKS: u64 = 500; - const NUM_SNAPSHOT_BLOCKS: u64 = 300; - const SNAPSHOT_MODE: ::snapshot::PowSnapshot = ::snapshot::PowSnapshot { blocks: NUM_SNAPSHOT_BLOCKS, max_restore_blocks: NUM_SNAPSHOT_BLOCKS }; - - // Temporary folders - let tempdir = TempDir::new("").unwrap(); - let snapshot_path = tempdir.path().join("SNAP"); - - // Generate blocks - let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()]; - let spec_f = Spec::new_null; - let spec = spec_f(); - let client = generate_dummy_client_with_spec_and_data(spec_f, NUM_BLOCKS as u32, 5, &gas_prices); - - let bc = client.chain(); - - // Create the Snapshot - let best_hash = bc.best_block_hash(); - let writer = Mutex::new(PackedWriter::new(&snapshot_path).unwrap()); - let block_hashes = chunk_secondary( - Box::new(SNAPSHOT_MODE), - &bc, - best_hash, - &writer, - &Progress::default() - ).unwrap(); - let state_db = client.state_db().journal_db().boxed_clone(); - let start_header = bc.block_header_data(&best_hash).unwrap(); - let state_root = start_header.state_root(); - let state_hashes = chunk_state( - state_db.as_hash_db(), - &state_root, - &writer, - &Progress::default(), - None - ).unwrap(); - - let manifest = ::snapshot::ManifestData { - version: 2, - state_hashes: state_hashes, - state_root: state_root, - block_hashes: block_hashes, - block_number: NUM_BLOCKS, - block_hash: best_hash, - }; - - writer.into_inner().finish(manifest.clone()).unwrap(); - - // Initialize the Client - let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS); - let client_db = new_temp_db(&tempdir.path()); - let client2 = Client::new( - ClientConfig::default(), - &spec, - client_db, - Arc::new(::miner::Miner::new_for_tests(&spec, None)), - IoChannel::disconnected(), - ).unwrap(); - - // Add some ancient blocks - for block_number in 1..50 { - let block_hash = bc.block_hash(block_number).unwrap(); - let block = bc.block(&block_hash).unwrap(); - client2.import_block(Unverified::from_rlp(block.into_inner()).unwrap()).unwrap(); - } - - client2.import_verified_blocks(); - client2.flush_queue(); - - // Restore the Snapshot - let reader = PackedReader::new(&snapshot_path).unwrap().unwrap(); - let service_params = ServiceParams { - engine: spec.engine.clone(), - genesis_block: spec.genesis_block(), - restoration_db_handler: restoration_db_handler(db_config), - pruning: ::journaldb::Algorithm::Archive, - channel: IoChannel::disconnected(), - snapshot_root: tempdir.path().to_owned(), - client: client2.clone(), - }; - let service = Service::new(service_params).unwrap(); - service.init_restore(manifest.clone(), false).unwrap(); - - for hash in &manifest.block_hashes { - let chunk = reader.chunk(*hash).unwrap(); - service.feed_block_chunk(*hash, &chunk); - } - - for hash in &manifest.state_hashes { - let chunk = reader.chunk(*hash).unwrap(); - service.feed_state_chunk(*hash, &chunk); - } - - match service.status() { - RestorationStatus::Inactive => (), - RestorationStatus::Failed => panic!("Snapshot Restoration has failed."), - RestorationStatus::Ongoing { .. } => panic!("Snapshot Restoration should be done."), - _ => panic!("Invalid Snapshot Service status."), - } - - // Check that the latest block number is the right one - assert_eq!(client2.block(BlockId::Latest).unwrap().number(), NUM_BLOCKS as u64); - - // Check that we have blocks in [NUM_BLOCKS - NUM_SNAPSHOT_BLOCKS + 1 ; NUM_BLOCKS] - // but none before - assert!(client2.block(BlockId::Number(NUM_BLOCKS - NUM_SNAPSHOT_BLOCKS + 1)).is_some()); - assert!(client2.block(BlockId::Number(100)).is_none()); - - // Check that the first 50 blocks have been migrated - for block_number in 1..49 { - assert!(client2.block(BlockId::Number(block_number)).is_some()); - } + let _ = ::env_logger::try_init(); + + // Test variables + const NUM_BLOCKS: u64 = 500; + const NUM_SNAPSHOT_BLOCKS: u64 = 300; + const SNAPSHOT_MODE: ::snapshot::PowSnapshot = ::snapshot::PowSnapshot { + blocks: NUM_SNAPSHOT_BLOCKS, + max_restore_blocks: NUM_SNAPSHOT_BLOCKS, + }; + + // Temporary folders + let tempdir = TempDir::new("").unwrap(); + let snapshot_path = tempdir.path().join("SNAP"); + + // Generate blocks + let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()]; + let spec_f = Spec::new_null; + let spec = spec_f(); + let client = + generate_dummy_client_with_spec_and_data(spec_f, NUM_BLOCKS as u32, 5, &gas_prices); + + let bc = client.chain(); + + // Create the Snapshot + let best_hash = bc.best_block_hash(); + let writer = Mutex::new(PackedWriter::new(&snapshot_path).unwrap()); + let block_hashes = chunk_secondary( + Box::new(SNAPSHOT_MODE), + &bc, + best_hash, + &writer, + &Progress::default(), + ) + .unwrap(); + let state_db = client.state_db().journal_db().boxed_clone(); + let start_header = bc.block_header_data(&best_hash).unwrap(); + let state_root = start_header.state_root(); + let state_hashes = chunk_state( + state_db.as_hash_db(), + &state_root, + &writer, + &Progress::default(), + None, + 0, + ) + .unwrap(); + + let manifest = ManifestData { + version: 2, + state_hashes, + state_root, + block_hashes, + block_number: NUM_BLOCKS, + block_hash: best_hash, + }; + + writer.into_inner().finish(manifest.clone()).unwrap(); + + // Initialize the Client + let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS); + let client_db = new_temp_db(&tempdir.path()); + let client2 = Client::new( + ClientConfig::default(), + &spec, + client_db, + Arc::new(::miner::Miner::new_for_tests(&spec, None)), + IoChannel::disconnected(), + ) + .unwrap(); + + // Add some ancient blocks + for block_number in 1..50 { + let block_hash = bc.block_hash(block_number).unwrap(); + let block = bc.block(&block_hash).unwrap(); + client2 + .import_block(Unverified::from_rlp(block.into_inner()).unwrap()) + .unwrap(); + } + + client2.import_verified_blocks(); + client2.flush_queue(); + + // Restore the Snapshot + let reader = PackedReader::new(&snapshot_path).unwrap().unwrap(); + let service_params = ServiceParams { + engine: spec.engine.clone(), + genesis_block: spec.genesis_block(), + restoration_db_handler: restoration_db_handler(db_config), + pruning: ::journaldb::Algorithm::Archive, + channel: IoChannel::disconnected(), + snapshot_root: tempdir.path().to_owned(), + client: client2.clone(), + }; + let service = Service::new(service_params).unwrap(); + service.init_restore(manifest.clone(), false).unwrap(); + + for hash in &manifest.block_hashes { + let chunk = reader.chunk(*hash).unwrap(); + service.feed_block_chunk(*hash, &chunk); + } + + for hash in &manifest.state_hashes { + let chunk = reader.chunk(*hash).unwrap(); + service.feed_state_chunk(*hash, &chunk); + } + + match service.restoration_status() { + RestorationStatus::Inactive => (), + RestorationStatus::Failed => panic!("Snapshot Restoration has failed."), + RestorationStatus::Ongoing { .. } => panic!("Snapshot Restoration should be done."), + _ => panic!("Invalid Snapshot Service status."), + } + + // Check that the latest block number is the right one + assert_eq!( + client2.block(BlockId::Latest).unwrap().number(), + NUM_BLOCKS as u64 + ); + + // Check that we have blocks in [NUM_BLOCKS - NUM_SNAPSHOT_BLOCKS + 1 ; NUM_BLOCKS] + // but none before + assert!(client2 + .block(BlockId::Number(NUM_BLOCKS - NUM_SNAPSHOT_BLOCKS + 1)) + .is_some()); + assert!(client2.block(BlockId::Number(100)).is_none()); + + // Check that the first 50 blocks have been migrated + for block_number in 1..49 { + assert!(client2.block(BlockId::Number(block_number)).is_some()); + } } #[test] fn recover_aborted_recovery() { - let _ = ::env_logger::try_init(); - - const NUM_BLOCKS: u32 = 400; - let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()]; - let client = generate_dummy_client_with_spec_and_data(Spec::new_null, NUM_BLOCKS, 5, &gas_prices); - - let spec = Spec::new_null(); - let tempdir = TempDir::new("").unwrap(); - let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS); - let client_db = new_db(); - let client2 = Client::new( - Default::default(), - &spec, - client_db, - Arc::new(::miner::Miner::new_for_tests(&spec, None)), - IoChannel::disconnected(), - ).unwrap(); - let service_params = ServiceParams { - engine: spec.engine.clone(), - genesis_block: spec.genesis_block(), - restoration_db_handler: restoration_db_handler(db_config), - pruning: ::journaldb::Algorithm::Archive, - channel: IoChannel::disconnected(), - snapshot_root: tempdir.path().to_owned(), - client: client2.clone(), - }; - - let service = Service::new(service_params).unwrap(); - service.take_snapshot(&client, NUM_BLOCKS as u64).unwrap(); - - let manifest = service.manifest().unwrap(); - service.init_restore(manifest.clone(), true).unwrap(); - - // Restore only the state chunks - for hash in &manifest.state_hashes { - let chunk = service.chunk(*hash).unwrap(); - service.feed_state_chunk(*hash, &chunk); - } - - match service.status() { - RestorationStatus::Ongoing { block_chunks_done, state_chunks_done, .. } => { - assert_eq!(state_chunks_done, manifest.state_hashes.len() as u32); - assert_eq!(block_chunks_done, 0); - }, - e => panic!("Snapshot restoration must be ongoing ; {:?}", e), - } - - // Abort the restore... - service.abort_restore(); - - // And try again! - service.init_restore(manifest.clone(), true).unwrap(); - - match service.status() { - RestorationStatus::Ongoing { block_chunks_done, state_chunks_done, .. } => { - assert_eq!(state_chunks_done, manifest.state_hashes.len() as u32); - assert_eq!(block_chunks_done, 0); - }, - e => panic!("Snapshot restoration must be ongoing ; {:?}", e), - } - - // Remove the snapshot directory, and restart the restoration - // It shouldn't have restored any previous blocks - fs::remove_dir_all(tempdir.path()).unwrap(); - - // And try again! - service.init_restore(manifest.clone(), true).unwrap(); - - match service.status() { - RestorationStatus::Ongoing { block_chunks_done, state_chunks_done, .. } => { - assert_eq!(block_chunks_done, 0); - assert_eq!(state_chunks_done, 0); - }, - _ => panic!("Snapshot restoration must be ongoing"), - } + let _ = ::env_logger::try_init(); + + const NUM_BLOCKS: u32 = 400; + let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()]; + let client = + generate_dummy_client_with_spec_and_data(Spec::new_null, NUM_BLOCKS, 5, &gas_prices); + + let spec = Spec::new_null(); + let tempdir = TempDir::new("").unwrap(); + let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS); + let client_db = new_db(); + let client2 = Client::new( + Default::default(), + &spec, + client_db, + Arc::new(::miner::Miner::new_for_tests(&spec, None)), + IoChannel::disconnected(), + ) + .unwrap(); + let service_params = ServiceParams { + engine: spec.engine.clone(), + genesis_block: spec.genesis_block(), + restoration_db_handler: restoration_db_handler(db_config), + pruning: ::journaldb::Algorithm::Archive, + channel: IoChannel::disconnected(), + snapshot_root: tempdir.path().to_owned(), + client: client2.clone(), + }; + + let service = Service::new(service_params).unwrap(); + service.take_snapshot(&client, NUM_BLOCKS as u64).unwrap(); + + let manifest = service.manifest().unwrap(); + service.init_restore(manifest.clone(), true).unwrap(); + + // Restore only the state chunks + for hash in &manifest.state_hashes { + let chunk = service.chunk(*hash).unwrap(); + service.feed_state_chunk(*hash, &chunk); + } + + match service.restoration_status() { + RestorationStatus::Ongoing { + block_chunks_done, + state_chunks_done, + .. + } => { + assert_eq!(state_chunks_done, manifest.state_hashes.len() as u32); + assert_eq!(block_chunks_done, 0); + } + e => panic!("Snapshot restoration must be ongoing ; {:?}", e), + } + + // Abort the restore... + service.abort_restore(); + + // And try again! + service.init_restore(manifest.clone(), true).unwrap(); + + match service.restoration_status() { + RestorationStatus::Ongoing { + block_chunks_done, + state_chunks_done, + .. + } => { + assert_eq!(state_chunks_done, manifest.state_hashes.len() as u32); + assert_eq!(block_chunks_done, 0); + } + e => panic!("Snapshot restoration must be ongoing ; {:?}", e), + } + + // Remove the snapshot directory, and restart the restoration + // It shouldn't have restored any previous blocks + fs::remove_dir_all(tempdir.path()).unwrap(); + + // And try again! + service.init_restore(manifest.clone(), true).unwrap(); + + match service.restoration_status() { + RestorationStatus::Ongoing { + block_chunks_done, + state_chunks_done, + .. + } => { + assert_eq!(block_chunks_done, 0); + assert_eq!(state_chunks_done, 0); + } + _ => panic!("Snapshot restoration must be ongoing"), + } } diff --git a/ethcore/src/snapshot/tests/state.rs b/ethcore/src/snapshot/tests/state.rs index fa7df6b6195..06a7e4afec9 100644 --- a/ethcore/src/snapshot/tests/state.rs +++ b/ethcore/src/snapshot/tests/state.rs @@ -1,204 +1,233 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! State snapshotting tests. -use std::sync::Arc; -use std::sync::atomic::AtomicBool; -use hash::{KECCAK_NULL_RLP, keccak}; +use hash::{keccak, KECCAK_NULL_RLP}; +use std::sync::{atomic::AtomicBool, Arc}; -use types::basic_account::BasicAccount; -use snapshot::account; -use snapshot::{chunk_state, Error as SnapshotError, Progress, StateRebuilder, SNAPSHOT_SUBPARTS}; -use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter}; use super::helpers::StateProducer; +use snapshot::{ + account, chunk_state, + io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter}, + Error as SnapshotError, Progress, StateRebuilder, SNAPSHOT_SUBPARTS, +}; +use types::basic_account::BasicAccount; use error::{Error, ErrorKind}; -use rand::{XorShiftRng, SeedableRng}; use ethereum_types::H256; use journaldb::{self, Algorithm}; use kvdb_rocksdb::{Database, DatabaseConfig}; use parking_lot::Mutex; +use rand::{SeedableRng, XorShiftRng}; use tempdir::TempDir; #[test] fn snap_and_restore() { - use hash_db::HashDB; - let mut producer = StateProducer::new(); - let mut rng = XorShiftRng::from_seed([1, 2, 3, 4]); - let mut old_db = journaldb::new_memory_db(); - let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS); - - for _ in 0..150 { - producer.tick(&mut rng, &mut old_db); - } - - let tempdir = TempDir::new("").unwrap(); - let snap_file = tempdir.path().join("SNAP"); - - let state_root = producer.state_root(); - let writer = Mutex::new(PackedWriter::new(&snap_file).unwrap()); - - let mut state_hashes = Vec::new(); - for part in 0..SNAPSHOT_SUBPARTS { - let mut hashes = chunk_state(&old_db, &state_root, &writer, &Progress::default(), Some(part)).unwrap(); - state_hashes.append(&mut hashes); - } - - writer.into_inner().finish(::snapshot::ManifestData { - version: 2, - state_hashes: state_hashes, - block_hashes: Vec::new(), - state_root: state_root, - block_number: 1000, - block_hash: H256::default(), - }).unwrap(); - - let db_path = tempdir.path().join("db"); - let db = { - let new_db = Arc::new(Database::open(&db_cfg, &db_path.to_string_lossy()).unwrap()); - let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::OverlayRecent); - let reader = PackedReader::new(&snap_file).unwrap().unwrap(); - - let flag = AtomicBool::new(true); - - for chunk_hash in &reader.manifest().state_hashes { - let raw = reader.chunk(*chunk_hash).unwrap(); - let chunk = ::snappy::decompress(&raw).unwrap(); - - rebuilder.feed(&chunk, &flag).unwrap(); - } - - assert_eq!(rebuilder.state_root(), state_root); - rebuilder.finalize(1000, H256::default()).unwrap(); - - new_db - }; - - let new_db = journaldb::new(db, Algorithm::OverlayRecent, ::db::COL_STATE); - assert_eq!(new_db.earliest_era(), Some(1000)); - let keys = old_db.keys(); - - for key in keys.keys() { - assert_eq!(old_db.get(&key).unwrap(), new_db.as_hash_db().get(&key).unwrap()); - } + use hash_db::HashDB; + let mut producer = StateProducer::new(); + let mut rng = XorShiftRng::from_seed([1, 2, 3, 4]); + let mut old_db = journaldb::new_memory_db(); + let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS); + + for _ in 0..150 { + producer.tick(&mut rng, &mut old_db); + } + + let tempdir = TempDir::new("").unwrap(); + let snap_file = tempdir.path().join("SNAP"); + + let state_root = producer.state_root(); + let writer = Mutex::new(PackedWriter::new(&snap_file).unwrap()); + + let mut state_hashes = Vec::new(); + for part in 0..SNAPSHOT_SUBPARTS { + let mut hashes = chunk_state( + &old_db, + &state_root, + &writer, + &Progress::default(), + Some(part), + 0, + ) + .unwrap(); + state_hashes.append(&mut hashes); + } + + writer + .into_inner() + .finish(::snapshot::ManifestData { + version: 2, + state_hashes: state_hashes, + block_hashes: Vec::new(), + state_root: state_root, + block_number: 1000, + block_hash: H256::default(), + }) + .unwrap(); + + let db_path = tempdir.path().join("db"); + let db = { + let new_db = Arc::new(Database::open(&db_cfg, &db_path.to_string_lossy()).unwrap()); + let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::OverlayRecent); + let reader = PackedReader::new(&snap_file).unwrap().unwrap(); + + let flag = AtomicBool::new(true); + + for chunk_hash in &reader.manifest().state_hashes { + let raw = reader.chunk(*chunk_hash).unwrap(); + let chunk = ::snappy::decompress(&raw).unwrap(); + + rebuilder.feed(&chunk, &flag).unwrap(); + } + + assert_eq!(rebuilder.state_root(), state_root); + rebuilder.finalize(1000, H256::default()).unwrap(); + + new_db + }; + + let new_db = journaldb::new(db, Algorithm::OverlayRecent, ::db::COL_STATE); + assert_eq!(new_db.earliest_era(), Some(1000)); + let keys = old_db.keys(); + + for key in keys.keys() { + assert_eq!( + old_db.get(&key).unwrap(), + new_db.as_hash_db().get(&key).unwrap() + ); + } } #[test] fn get_code_from_prev_chunk() { - use std::collections::HashSet; - use rlp::RlpStream; - use ethereum_types::{H256, U256}; - use hash_db::HashDB; - - use account_db::{AccountDBMut, AccountDB}; - - let code = b"this is definitely code"; - let mut used_code = HashSet::new(); - let mut acc_stream = RlpStream::new_list(4); - acc_stream.append(&U256::default()) - .append(&U256::default()) - .append(&KECCAK_NULL_RLP) - .append(&keccak(code)); - - let (h1, h2) = (H256::random(), H256::random()); - - // two accounts with the same code, one per chunk. - // first one will have code inlined, - // second will just have its hash. - let thin_rlp = acc_stream.out(); - let acc: BasicAccount = ::rlp::decode(&thin_rlp).expect("error decoding basic account"); - - let mut make_chunk = |acc, hash| { - let mut db = journaldb::new_memory_db(); - AccountDBMut::from_hash(&mut db, hash).insert(&code[..]); - - let fat_rlp = account::to_fat_rlps(&hash, &acc, &AccountDB::from_hash(&db, hash), &mut used_code, usize::max_value(), usize::max_value()).unwrap(); - let mut stream = RlpStream::new_list(1); - stream.append_raw(&fat_rlp[0], 1); - stream.out() - }; - - let chunk1 = make_chunk(acc.clone(), h1); - let chunk2 = make_chunk(acc, h2); - - let tempdir = TempDir::new("").unwrap(); - let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS); - let new_db = Arc::new(Database::open(&db_cfg, tempdir.path().to_str().unwrap()).unwrap()); - - { - let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::OverlayRecent); - let flag = AtomicBool::new(true); - - rebuilder.feed(&chunk1, &flag).unwrap(); - rebuilder.feed(&chunk2, &flag).unwrap(); - - rebuilder.finalize(1000, H256::random()).unwrap(); - } - - let state_db = journaldb::new(new_db, Algorithm::OverlayRecent, ::db::COL_STATE); - assert_eq!(state_db.earliest_era(), Some(1000)); + use ethereum_types::{H256, U256}; + use hash_db::HashDB; + use rlp::RlpStream; + use std::collections::HashSet; + + use account_db::{AccountDB, AccountDBMut}; + + let code = b"this is definitely code"; + let mut used_code = HashSet::new(); + let mut acc_stream = RlpStream::new_list(4); + acc_stream + .append(&U256::default()) + .append(&U256::default()) + .append(&KECCAK_NULL_RLP) + .append(&keccak(code)); + + let (h1, h2) = (H256::random(), H256::random()); + + // two accounts with the same code, one per chunk. + // first one will have code inlined, + // second will just have its hash. + let thin_rlp = acc_stream.out(); + let acc: BasicAccount = ::rlp::decode(&thin_rlp).expect("error decoding basic account"); + + let mut make_chunk = |acc, hash| { + let mut db = journaldb::new_memory_db(); + AccountDBMut::from_hash(&mut db, hash).insert(&code[..]); + let p = Progress::default(); + let fat_rlp = account::to_fat_rlps( + &hash, + &acc, + &AccountDB::from_hash(&db, hash), + &mut used_code, + usize::max_value(), + usize::max_value(), + &p, + ) + .unwrap(); + let mut stream = RlpStream::new_list(1); + stream.append_raw(&fat_rlp[0], 1); + stream.out() + }; + + let chunk1 = make_chunk(acc.clone(), h1); + let chunk2 = make_chunk(acc, h2); + + let tempdir = TempDir::new("").unwrap(); + let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS); + let new_db = Arc::new(Database::open(&db_cfg, tempdir.path().to_str().unwrap()).unwrap()); + + { + let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::OverlayRecent); + let flag = AtomicBool::new(true); + + rebuilder.feed(&chunk1, &flag).unwrap(); + rebuilder.feed(&chunk2, &flag).unwrap(); + + rebuilder.finalize(1000, H256::random()).unwrap(); + } + + let state_db = journaldb::new(new_db, Algorithm::OverlayRecent, ::db::COL_STATE); + assert_eq!(state_db.earliest_era(), Some(1000)); } #[test] fn checks_flag() { - let mut producer = StateProducer::new(); - let mut rng = XorShiftRng::from_seed([5, 6, 7, 8]); - let mut old_db = journaldb::new_memory_db(); - let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS); - - for _ in 0..10 { - producer.tick(&mut rng, &mut old_db); - } - - let tempdir = TempDir::new("").unwrap(); - let snap_file = tempdir.path().join("SNAP"); - - let state_root = producer.state_root(); - let writer = Mutex::new(PackedWriter::new(&snap_file).unwrap()); - - let state_hashes = chunk_state(&old_db, &state_root, &writer, &Progress::default(), None).unwrap(); - - writer.into_inner().finish(::snapshot::ManifestData { - version: 2, - state_hashes: state_hashes, - block_hashes: Vec::new(), - state_root: state_root, - block_number: 0, - block_hash: H256::default(), - }).unwrap(); - - let tempdir = TempDir::new("").unwrap(); - let db_path = tempdir.path().join("db"); - { - let new_db = Arc::new(Database::open(&db_cfg, &db_path.to_string_lossy()).unwrap()); - let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::OverlayRecent); - let reader = PackedReader::new(&snap_file).unwrap().unwrap(); - - let flag = AtomicBool::new(false); - - for chunk_hash in &reader.manifest().state_hashes { - let raw = reader.chunk(*chunk_hash).unwrap(); - let chunk = ::snappy::decompress(&raw).unwrap(); - - match rebuilder.feed(&chunk, &flag) { - Err(Error(ErrorKind::Snapshot(SnapshotError::RestorationAborted), _)) => {}, - _ => panic!("unexpected result when feeding with flag off"), - } - } - } + let mut producer = StateProducer::new(); + let mut rng = XorShiftRng::from_seed([5, 6, 7, 8]); + let mut old_db = journaldb::new_memory_db(); + let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS); + + for _ in 0..10 { + producer.tick(&mut rng, &mut old_db); + } + + let tempdir = TempDir::new("").unwrap(); + let snap_file = tempdir.path().join("SNAP"); + + let state_root = producer.state_root(); + let writer = Mutex::new(PackedWriter::new(&snap_file).unwrap()); + + let state_hashes = + chunk_state(&old_db, &state_root, &writer, &Progress::default(), None, 0).unwrap(); + + writer + .into_inner() + .finish(::snapshot::ManifestData { + version: 2, + state_hashes, + block_hashes: Vec::new(), + state_root, + block_number: 0, + block_hash: H256::default(), + }) + .unwrap(); + + let tempdir = TempDir::new("").unwrap(); + let db_path = tempdir.path().join("db"); + { + let new_db = Arc::new(Database::open(&db_cfg, &db_path.to_string_lossy()).unwrap()); + let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::OverlayRecent); + let reader = PackedReader::new(&snap_file).unwrap().unwrap(); + + let flag = AtomicBool::new(false); + + for chunk_hash in &reader.manifest().state_hashes { + let raw = reader.chunk(*chunk_hash).unwrap(); + let chunk = ::snappy::decompress(&raw).unwrap(); + + match rebuilder.feed(&chunk, &flag) { + Err(Error(ErrorKind::Snapshot(SnapshotError::RestorationAborted), _)) => {} + _ => panic!("unexpected result when feeding with flag off"), + } + } + } } diff --git a/ethcore/src/snapshot/traits.rs b/ethcore/src/snapshot/traits.rs index bb4ab3b3964..6b17d2f03c8 100644 --- a/ethcore/src/snapshot/traits.rs +++ b/ethcore/src/snapshot/traits.rs @@ -1,60 +1,66 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use super::{ManifestData, RestorationStatus}; -use ethereum_types::H256; +use super::{CreationStatus, ManifestData, RestorationStatus}; use bytes::Bytes; +use ethereum_types::H256; /// The interface for a snapshot network service. /// This handles: /// - restoration of snapshots to temporary databases. /// - responding to queries for snapshot manifests and chunks -pub trait SnapshotService : Sync + Send { - /// Query the most recent manifest data. - fn manifest(&self) -> Option; +pub trait SnapshotService: Sync + Send { + /// Query the most recent manifest data. + fn manifest(&self) -> Option; + + /// Get the supported range of snapshot version numbers. + /// `None` indicates warp sync isn't supported by the consensus engine. + fn supported_versions(&self) -> Option<(u64, u64)>; + + /// Returns a list of the completed chunks + fn completed_chunks(&self) -> Option>; - /// Get the supported range of snapshot version numbers. - /// `None` indicates warp sync isn't supported by the consensus engine. - fn supported_versions(&self) -> Option<(u64, u64)>; + /// Get raw chunk for a given hash. + fn chunk(&self, hash: H256) -> Option; - /// Returns a list of the completed chunks - fn completed_chunks(&self) -> Option>; + /// Ask the snapshot service for the restoration status. + fn restoration_status(&self) -> RestorationStatus; - /// Get raw chunk for a given hash. - fn chunk(&self, hash: H256) -> Option; + /// Ask the snapshot service for the creation status. + fn creation_status(&self) -> CreationStatus; - /// Ask the snapshot service for the restoration status. - fn status(&self) -> RestorationStatus; + /// Begin snapshot restoration. + /// If restoration in-progress, this will reset it. + /// From this point on, any previous snapshot may become unavailable. + fn begin_restore(&self, manifest: ManifestData); - /// Begin snapshot restoration. - /// If restoration in-progress, this will reset it. - /// From this point on, any previous snapshot may become unavailable. - fn begin_restore(&self, manifest: ManifestData); + /// Abort an in-progress restoration if there is one. + fn abort_restore(&self); - /// Abort an in-progress restoration if there is one. - fn abort_restore(&self); + /// Feed a raw state chunk to the service to be processed asynchronously. + /// no-op if not currently restoring. + fn restore_state_chunk(&self, hash: H256, chunk: Bytes); - /// Feed a raw state chunk to the service to be processed asynchronously. - /// no-op if not currently restoring. - fn restore_state_chunk(&self, hash: H256, chunk: Bytes); + /// Feed a raw block chunk to the service to be processed asynchronously. + /// no-op if currently restoring. + fn restore_block_chunk(&self, hash: H256, chunk: Bytes); - /// Feed a raw block chunk to the service to be processed asynchronously. - /// no-op if currently restoring. - fn restore_block_chunk(&self, hash: H256, chunk: Bytes); + /// Abort in-progress snapshotting if there is one. + fn abort_snapshot(&self); - /// Shutdown the Snapshot Service by aborting any ongoing restore - fn shutdown(&self); + /// Shutdown the Snapshot Service by aborting any ongoing restore + fn shutdown(&self); } diff --git a/ethcore/src/snapshot/watcher.rs b/ethcore/src/snapshot/watcher.rs index 5c4712cff9c..8f1bced3735 100644 --- a/ethcore/src/snapshot/watcher.rs +++ b/ethcore/src/snapshot/watcher.rs @@ -1,196 +1,221 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Watcher for snapshot-related chain events. +use client::{BlockInfo, ChainNotify, Client, ClientIoMessage, NewBlocks}; use parking_lot::Mutex; -use client::{BlockInfo, Client, ChainNotify, NewBlocks, ClientIoMessage}; use types::ids::BlockId; -use io::IoChannel; use ethereum_types::H256; +use io::IoChannel; use std::sync::Arc; // helper trait for transforming hashes to numbers and checking if syncing. trait Oracle: Send + Sync { - fn to_number(&self, hash: H256) -> Option; + fn to_number(&self, hash: H256) -> Option; - fn is_major_importing(&self) -> bool; + fn is_major_importing(&self) -> bool; } -struct StandardOracle where F: 'static + Send + Sync + Fn() -> bool { - client: Arc, - sync_status: F, +struct StandardOracle +where + F: 'static + Send + Sync + Fn() -> bool, +{ + client: Arc, + sync_status: F, } impl Oracle for StandardOracle - where F: Send + Sync + Fn() -> bool +where + F: Send + Sync + Fn() -> bool, { - fn to_number(&self, hash: H256) -> Option { - self.client.block_header(BlockId::Hash(hash)).map(|h| h.number()) - } - - fn is_major_importing(&self) -> bool { - (self.sync_status)() - } + fn to_number(&self, hash: H256) -> Option { + self.client + .block_header(BlockId::Hash(hash)) + .map(|h| h.number()) + } + + fn is_major_importing(&self) -> bool { + (self.sync_status)() + } } // helper trait for broadcasting a block to take a snapshot at. trait Broadcast: Send + Sync { - fn take_at(&self, num: Option); + fn take_at(&self, num: Option); } impl Broadcast for Mutex> { - fn take_at(&self, num: Option) { - let num = match num { - Some(n) => n, - None => return, - }; - - trace!(target: "snapshot_watcher", "broadcast: {}", num); - - if let Err(e) = self.lock().send(ClientIoMessage::TakeSnapshot(num)) { - warn!("Snapshot watcher disconnected from IoService: {}", e); - } - } + fn take_at(&self, num: Option) { + let num = match num { + Some(n) => n, + None => return, + }; + + trace!(target: "snapshot_watcher", "broadcast: {}", num); + + if let Err(e) = self.lock().send(ClientIoMessage::TakeSnapshot(num)) { + warn!("Snapshot watcher disconnected from IoService: {}", e); + } + } } /// A `ChainNotify` implementation which will trigger a snapshot event /// at certain block numbers. pub struct Watcher { - oracle: Box, - broadcast: Box, - period: u64, - history: u64, + oracle: Box, + broadcast: Box, + period: u64, + history: u64, } impl Watcher { - /// Create a new `Watcher` which will trigger a snapshot event - /// once every `period` blocks, but only after that block is - /// `history` blocks old. - pub fn new(client: Arc, sync_status: F, channel: IoChannel, period: u64, history: u64) -> Self - where F: 'static + Send + Sync + Fn() -> bool - { - Watcher { - oracle: Box::new(StandardOracle { - client: client, - sync_status: sync_status, - }), - broadcast: Box::new(Mutex::new(channel)), - period: period, - history: history, - } - } + /// Create a new `Watcher` which will trigger a snapshot event + /// once every `period` blocks, but only after that block is + /// `history` blocks old. + pub fn new( + client: Arc, + sync_status: F, + channel: IoChannel, + period: u64, + history: u64, + ) -> Self + where + F: 'static + Send + Sync + Fn() -> bool, + { + Watcher { + oracle: Box::new(StandardOracle { + client: client, + sync_status: sync_status, + }), + broadcast: Box::new(Mutex::new(channel)), + period: period, + history: history, + } + } } impl ChainNotify for Watcher { - fn new_blocks(&self, new_blocks: NewBlocks) { - if self.oracle.is_major_importing() || new_blocks.has_more_blocks_to_import { return } - - trace!(target: "snapshot_watcher", "{} imported", new_blocks.imported.len()); - - let highest = new_blocks.imported.into_iter() - .filter_map(|h| self.oracle.to_number(h)) - .filter(|&num| num >= self.period + self.history) - .map(|num| num - self.history) - .filter(|num| num % self.period == 0) - .fold(0, ::std::cmp::max); - - match highest { - 0 => self.broadcast.take_at(None), - _ => self.broadcast.take_at(Some(highest)), - } - } + fn new_blocks(&self, new_blocks: NewBlocks) { + if self.oracle.is_major_importing() || new_blocks.has_more_blocks_to_import { + return; + } + + trace!(target: "snapshot_watcher", "{} imported", new_blocks.imported.len()); + + let highest = new_blocks + .imported + .into_iter() + .filter_map(|h| self.oracle.to_number(h)) + .filter(|&num| num >= self.period + self.history) + .map(|num| num - self.history) + .filter(|num| num % self.period == 0) + .fold(0, ::std::cmp::max); + + match highest { + 0 => self.broadcast.take_at(None), + _ => self.broadcast.take_at(Some(highest)), + } + } } #[cfg(test)] mod tests { - use super::{Broadcast, Oracle, Watcher}; - - use client::{ChainNotify, NewBlocks, ChainRoute}; - - use ethereum_types::{H256, U256}; - - use std::collections::HashMap; - use std::time::Duration; - - struct TestOracle(HashMap); - - impl Oracle for TestOracle { - fn to_number(&self, hash: H256) -> Option { - self.0.get(&hash).cloned() - } - - fn is_major_importing(&self) -> bool { false } - } - - struct TestBroadcast(Option); - impl Broadcast for TestBroadcast { - fn take_at(&self, num: Option) { - if num != self.0 { - panic!("Watcher broadcast wrong number. Expected {:?}, found {:?}", self.0, num); - } - } - } - - // helper harness for tests which expect a notification. - fn harness(numbers: Vec, period: u64, history: u64, expected: Option) { - const DURATION_ZERO: Duration = Duration::from_millis(0); - - let hashes: Vec<_> = numbers.clone().into_iter().map(|x| H256::from(U256::from(x))).collect(); - let map = hashes.clone().into_iter().zip(numbers).collect(); - - let watcher = Watcher { - oracle: Box::new(TestOracle(map)), - broadcast: Box::new(TestBroadcast(expected)), - period: period, - history: history, - }; - - watcher.new_blocks(NewBlocks::new( - hashes, - vec![], - ChainRoute::default(), - vec![], - vec![], - DURATION_ZERO, - false - )); - } - - // helper - - #[test] - fn should_not_fire() { - harness(vec![0], 5, 0, None); - } - - #[test] - fn fires_once_for_two() { - harness(vec![14, 15], 10, 5, Some(10)); - } - - #[test] - fn finds_highest() { - harness(vec![15, 25], 10, 5, Some(20)); - } - - #[test] - fn doesnt_fire_before_history() { - harness(vec![10, 11], 10, 5, None); - } + use super::{Broadcast, Oracle, Watcher}; + + use client::{ChainNotify, ChainRoute, NewBlocks}; + + use ethereum_types::{H256, U256}; + + use std::{collections::HashMap, time::Duration}; + + struct TestOracle(HashMap); + + impl Oracle for TestOracle { + fn to_number(&self, hash: H256) -> Option { + self.0.get(&hash).cloned() + } + + fn is_major_importing(&self) -> bool { + false + } + } + + struct TestBroadcast(Option); + impl Broadcast for TestBroadcast { + fn take_at(&self, num: Option) { + if num != self.0 { + panic!( + "Watcher broadcast wrong number. Expected {:?}, found {:?}", + self.0, num + ); + } + } + } + + // helper harness for tests which expect a notification. + fn harness(numbers: Vec, period: u64, history: u64, expected: Option) { + const DURATION_ZERO: Duration = Duration::from_millis(0); + + let hashes: Vec<_> = numbers + .clone() + .into_iter() + .map(|x| H256::from(U256::from(x))) + .collect(); + let map = hashes.clone().into_iter().zip(numbers).collect(); + + let watcher = Watcher { + oracle: Box::new(TestOracle(map)), + broadcast: Box::new(TestBroadcast(expected)), + period: period, + history: history, + }; + + watcher.new_blocks(NewBlocks::new( + hashes, + vec![], + ChainRoute::default(), + vec![], + vec![], + DURATION_ZERO, + false, + )); + } + + // helper + + #[test] + fn should_not_fire() { + harness(vec![0], 5, 0, None); + } + + #[test] + fn fires_once_for_two() { + harness(vec![14, 15], 10, 5, Some(10)); + } + + #[test] + fn finds_highest() { + harness(vec![15, 25], 10, 5, Some(20)); + } + + #[test] + fn doesnt_fire_before_history() { + harness(vec![10, 11], 10, 5, None); + } } diff --git a/ethcore/src/spec/genesis.rs b/ethcore/src/spec/genesis.rs index 96a42178dd4..88f3f785947 100644 --- a/ethcore/src/spec/genesis.rs +++ b/ethcore/src/spec/genesis.rs @@ -1,64 +1,68 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use ethereum_types::{H256, U256, Address}; +use ethereum_types::{Address, H256, U256}; use ethjson; use hash::KECCAK_NULL_RLP; use spec::seal::Seal; /// Genesis components. pub struct Genesis { - /// Seal. - pub seal: Seal, - /// Difficulty. - pub difficulty: U256, - /// Author. - pub author: Address, - /// Timestamp. - pub timestamp: u64, - /// Parent hash. - pub parent_hash: H256, - /// Gas limit. - pub gas_limit: U256, - /// Transactions root. - pub transactions_root: H256, - /// Receipts root. - pub receipts_root: H256, - /// State root. - pub state_root: Option, - /// Gas used. - pub gas_used: U256, - /// Extra data. - pub extra_data: Vec, + /// Seal. + pub seal: Seal, + /// Difficulty. + pub difficulty: U256, + /// Author. + pub author: Address, + /// Timestamp. + pub timestamp: u64, + /// Parent hash. + pub parent_hash: H256, + /// Gas limit. + pub gas_limit: U256, + /// Transactions root. + pub transactions_root: H256, + /// Receipts root. + pub receipts_root: H256, + /// State root. + pub state_root: Option, + /// Gas used. + pub gas_used: U256, + /// Extra data. + pub extra_data: Vec, } impl From for Genesis { - fn from(g: ethjson::spec::Genesis) -> Self { - Genesis { - seal: From::from(g.seal), - difficulty: g.difficulty.into(), - author: g.author.map_or_else(Address::zero, Into::into), - timestamp: g.timestamp.map_or(0, Into::into), - parent_hash: g.parent_hash.map_or_else(H256::zero, Into::into), - gas_limit: g.gas_limit.into(), - transactions_root: g.transactions_root.map_or_else(|| KECCAK_NULL_RLP.clone(), Into::into), - receipts_root: g.receipts_root.map_or_else(|| KECCAK_NULL_RLP.clone(), Into::into), - state_root: g.state_root.map(Into::into), - gas_used: g.gas_used.map_or_else(U256::zero, Into::into), - extra_data: g.extra_data.map_or_else(Vec::new, Into::into), - } - } + fn from(g: ethjson::spec::Genesis) -> Self { + Genesis { + seal: From::from(g.seal), + difficulty: g.difficulty.into(), + author: g.author.map_or_else(Address::zero, Into::into), + timestamp: g.timestamp.map_or(0, Into::into), + parent_hash: g.parent_hash.map_or_else(H256::zero, Into::into), + gas_limit: g.gas_limit.into(), + transactions_root: g + .transactions_root + .map_or_else(|| KECCAK_NULL_RLP.clone(), Into::into), + receipts_root: g + .receipts_root + .map_or_else(|| KECCAK_NULL_RLP.clone(), Into::into), + state_root: g.state_root.map(Into::into), + gas_used: g.gas_used.map_or_else(U256::zero, Into::into), + extra_data: g.extra_data.map_or_else(Vec::new, Into::into), + } + } } diff --git a/ethcore/src/spec/mod.rs b/ethcore/src/spec/mod.rs index 5d90b5fbf89..de9cd6411c9 100644 --- a/ethcore/src/spec/mod.rs +++ b/ethcore/src/spec/mod.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Blockchain params. @@ -20,5 +20,7 @@ mod genesis; mod seal; mod spec; -pub use self::genesis::Genesis; -pub use self::spec::{Spec, SpecHardcodedSync, SpecParams, CommonParams, OptimizeFor}; +pub use self::{ + genesis::Genesis, + spec::{CommonParams, OptimizeFor, Spec, SpecParams}, +}; diff --git a/ethcore/src/spec/seal.rs b/ethcore/src/spec/seal.rs index ed70ac8b545..33d69b12123 100644 --- a/ethcore/src/spec/seal.rs +++ b/ethcore/src/spec/seal.rs @@ -1,120 +1,120 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Spec seal. -use rlp::RlpStream; -use ethereum_types::{H64, H256, H520}; +use ethereum_types::{H256, H520, H64}; use ethjson; +use rlp::RlpStream; /// Classic ethereum seal. pub struct Ethereum { - /// Seal nonce. - pub nonce: H64, - /// Seal mix hash. - pub mix_hash: H256, + /// Seal nonce. + pub nonce: H64, + /// Seal mix hash. + pub mix_hash: H256, } impl Into for Ethereum { - fn into(self) -> Generic { - let mut s = RlpStream::new_list(2); - s.append(&self.mix_hash).append(&self.nonce); - Generic(s.out()) - } + fn into(self) -> Generic { + let mut s = RlpStream::new_list(2); + s.append(&self.mix_hash).append(&self.nonce); + Generic(s.out()) + } } /// AuthorityRound seal. pub struct AuthorityRound { - /// Seal step. - pub step: usize, - /// Seal signature. - pub signature: H520, + /// Seal step. + pub step: usize, + /// Seal signature. + pub signature: H520, } /// Tendermint seal. pub struct Tendermint { - /// Seal round. - pub round: usize, - /// Proposal seal signature. - pub proposal: H520, - /// Precommit seal signatures. - pub precommits: Vec, + /// Seal round. + pub round: usize, + /// Proposal seal signature. + pub proposal: H520, + /// Precommit seal signatures. + pub precommits: Vec, } impl Into for AuthorityRound { - fn into(self) -> Generic { - let mut s = RlpStream::new_list(2); - s.append(&self.step).append(&self.signature); - Generic(s.out()) - } + fn into(self) -> Generic { + let mut s = RlpStream::new_list(2); + s.append(&self.step).append(&self.signature); + Generic(s.out()) + } } impl Into for Tendermint { - fn into(self) -> Generic { - let mut stream = RlpStream::new_list(3); - stream - .append(&self.round) - .append(&self.proposal) - .append_list(&self.precommits); - Generic(stream.out()) - } + fn into(self) -> Generic { + let mut stream = RlpStream::new_list(3); + stream + .append(&self.round) + .append(&self.proposal) + .append_list(&self.precommits); + Generic(stream.out()) + } } pub struct Generic(pub Vec); /// Genesis seal type. pub enum Seal { - /// Classic ethereum seal. - Ethereum(Ethereum), - /// AuthorityRound seal. - AuthorityRound(AuthorityRound), - /// Tendermint seal. - Tendermint(Tendermint), - /// Generic RLP seal. - Generic(Generic), + /// Classic ethereum seal. + Ethereum(Ethereum), + /// AuthorityRound seal. + AuthorityRound(AuthorityRound), + /// Tendermint seal. + Tendermint(Tendermint), + /// Generic RLP seal. + Generic(Generic), } impl From for Seal { - fn from(s: ethjson::spec::Seal) -> Self { - match s { - ethjson::spec::Seal::Ethereum(eth) => Seal::Ethereum(Ethereum { - nonce: eth.nonce.into(), - mix_hash: eth.mix_hash.into() - }), - ethjson::spec::Seal::AuthorityRound(ar) => Seal::AuthorityRound(AuthorityRound { - step: ar.step.into(), - signature: ar.signature.into() - }), - ethjson::spec::Seal::Tendermint(tender) => Seal::Tendermint(Tendermint { - round: tender.round.into(), - proposal: tender.proposal.into(), - precommits: tender.precommits.into_iter().map(Into::into).collect() - }), - ethjson::spec::Seal::Generic(g) => Seal::Generic(Generic(g.into())), - } - } + fn from(s: ethjson::spec::Seal) -> Self { + match s { + ethjson::spec::Seal::Ethereum(eth) => Seal::Ethereum(Ethereum { + nonce: eth.nonce.into(), + mix_hash: eth.mix_hash.into(), + }), + ethjson::spec::Seal::AuthorityRound(ar) => Seal::AuthorityRound(AuthorityRound { + step: ar.step.into(), + signature: ar.signature.into(), + }), + ethjson::spec::Seal::Tendermint(tender) => Seal::Tendermint(Tendermint { + round: tender.round.into(), + proposal: tender.proposal.into(), + precommits: tender.precommits.into_iter().map(Into::into).collect(), + }), + ethjson::spec::Seal::Generic(g) => Seal::Generic(Generic(g.into())), + } + } } impl Into for Seal { - fn into(self) -> Generic { - match self { - Seal::Generic(generic) => generic, - Seal::Ethereum(eth) => eth.into(), - Seal::AuthorityRound(ar) => ar.into(), - Seal::Tendermint(tender) => tender.into(), - } - } + fn into(self) -> Generic { + match self { + Seal::Generic(generic) => generic, + Seal::Ethereum(eth) => eth.into(), + Seal::AuthorityRound(ar) => ar.into(), + Seal::Tendermint(tender) => tender.into(), + } + } } diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index fe32c0d87e1..e8f968a7101 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -1,52 +1,52 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Parameters for a block chain. -use std::collections::BTreeMap; -use std::io::Read; -use std::path::Path; -use std::sync::Arc; +use std::{ + collections::{BTreeMap, BTreeSet}, + convert::TryFrom, + io::Read, + path::Path, + sync::Arc, +}; use bytes::Bytes; -use ethereum_types::{H256, Bloom, U256, Address}; +use ethereum_types::{Address, Bloom, H256, U256}; use ethjson; -use hash::{KECCAK_NULL_RLP, keccak}; +use hash::{keccak, KECCAK_NULL_RLP}; use parking_lot::RwLock; use rlp::{Rlp, RlpStream}; -use rustc_hex::{FromHex, ToHex}; -use types::BlockNumber; -use types::encoded; -use types::header::Header; -use vm::{EnvInfo, CallType, ActionValue, ActionParams, ParamsType}; +use rustc_hex::FromHex; +use types::{header::Header, BlockNumber}; +use vm::{ActionParams, ActionValue, CallType, EnvInfo, ParamsType}; use builtin::Builtin; use engines::{ - EthEngine, NullEngine, InstantSeal, InstantSealParams, BasicAuthority, Clique, - AuthorityRound, DEFAULT_BLOCKHASH_CONTRACT + AuthorityRound, BasicAuthority, Clique, EthEngine, InstantSeal, InstantSealParams, NullEngine, + DEFAULT_BLOCKHASH_CONTRACT, }; use error::Error; use executive::Executive; use factory::Factories; use machine::EthereumMachine; +use maplit::btreeset; use pod_state::PodState; -use spec::Genesis; -use spec::seal::Generic as GenericSeal; -use state::backend::Basic as BasicBackend; -use state::{Backend, State, Substate}; +use spec::{seal::Generic as GenericSeal, Genesis}; +use state::{backend::Basic as BasicBackend, Backend, State, Substate}; use trace::{NoopTracer, NoopVMTracer}; pub use ethash::OptimizeFor; @@ -55,7 +55,7 @@ const MAX_TRANSACTION_SIZE: usize = 300 * 1024; // helper for formatting errors. fn fmt_err(f: F) -> String { - format!("Spec json is invalid: {}", f) + format!("Spec json is invalid: {}", f) } /// Parameters common to ethereum-like blockchains. @@ -65,977 +65,1063 @@ fn fmt_err(f: F) -> String { /// we define a "bugfix" hard fork as any hard fork which /// you would put on-by-default in a new chain. #[derive(Debug, PartialEq, Default)] -#[cfg_attr(test, derive(Clone))] +#[cfg_attr(any(test, feature = "test-helpers"), derive(Clone))] pub struct CommonParams { - /// Account start nonce. - pub account_start_nonce: U256, - /// Maximum size of extra data. - pub maximum_extra_data_size: usize, - /// Network id. - pub network_id: u64, - /// Chain id. - pub chain_id: u64, - /// Main subprotocol name. - pub subprotocol_name: String, - /// Minimum gas limit. - pub min_gas_limit: U256, - /// Fork block to check. - pub fork_block: Option<(BlockNumber, H256)>, - /// EIP150 transition block number. - pub eip150_transition: BlockNumber, - /// Number of first block where EIP-160 rules begin. - pub eip160_transition: BlockNumber, - /// Number of first block where EIP-161.abc begin. - pub eip161abc_transition: BlockNumber, - /// Number of first block where EIP-161.d begins. - pub eip161d_transition: BlockNumber, - /// Number of first block where EIP-98 rules begin. - pub eip98_transition: BlockNumber, - /// Number of first block where EIP-658 rules begin. - pub eip658_transition: BlockNumber, - /// Number of first block where EIP-155 rules begin. - pub eip155_transition: BlockNumber, - /// Validate block receipts root. - pub validate_receipts_transition: BlockNumber, - /// Validate transaction chain id. - pub validate_chain_id_transition: BlockNumber, - /// Number of first block where EIP-140 rules begin. - pub eip140_transition: BlockNumber, - /// Number of first block where EIP-210 rules begin. - pub eip210_transition: BlockNumber, - /// EIP-210 Blockhash contract address. - pub eip210_contract_address: Address, - /// EIP-210 Blockhash contract code. - pub eip210_contract_code: Bytes, - /// Gas allocated for EIP-210 blockhash update. - pub eip210_contract_gas: U256, - /// Number of first block where EIP-211 rules begin. - pub eip211_transition: BlockNumber, - /// Number of first block where EIP-214 rules begin. - pub eip214_transition: BlockNumber, - /// Number of first block where EIP-145 rules begin. - pub eip145_transition: BlockNumber, - /// Number of first block where EIP-1052 rules begin. - pub eip1052_transition: BlockNumber, - /// Number of first block where EIP-1283 rules begin. - pub eip1283_transition: BlockNumber, - /// Number of first block where EIP-1283 rules end. - pub eip1283_disable_transition: BlockNumber, - /// Number of first block where EIP-1014 rules begin. - pub eip1014_transition: BlockNumber, - /// Number of first block where dust cleanup rules (EIP-168 and EIP169) begin. - pub dust_protection_transition: BlockNumber, - /// Nonce cap increase per block. Nonce cap is only checked if dust protection is enabled. - pub nonce_cap_increment: u64, - /// Enable dust cleanup for contracts. - pub remove_dust_contracts: bool, - /// Wasm activation blocknumber, if any disabled initially. - pub wasm_activation_transition: BlockNumber, - /// Number of first block where KIP-4 rules begin. Only has effect if Wasm is activated. - pub kip4_transition: BlockNumber, - /// Number of first block where KIP-6 rules begin. Only has effect if Wasm is activated. - pub kip6_transition: BlockNumber, - /// Gas limit bound divisor (how much gas limit can change per block) - pub gas_limit_bound_divisor: U256, - /// Registrar contract address. - pub registrar: Address, - /// Node permission managing contract address. - pub node_permission_contract: Option
, - /// Maximum contract code size that can be deployed. - pub max_code_size: u64, - /// Number of first block where max code size limit is active. - pub max_code_size_transition: BlockNumber, - /// Transaction permission managing contract address. - pub transaction_permission_contract: Option
, - /// Block at which the transaction permission contract should start being used. - pub transaction_permission_contract_transition: BlockNumber, - /// Maximum size of transaction's RLP payload - pub max_transaction_size: usize, + /// Account start nonce. + pub account_start_nonce: U256, + /// Maximum size of extra data. + pub maximum_extra_data_size: usize, + /// Network id. + pub network_id: u64, + /// Chain id. + pub chain_id: u64, + /// Main subprotocol name. + pub subprotocol_name: String, + /// Minimum gas limit. + pub min_gas_limit: U256, + /// Fork block to check. + pub fork_block: Option<(BlockNumber, H256)>, + /// EIP150 transition block number. + pub eip150_transition: BlockNumber, + /// Number of first block where EIP-160 rules begin. + pub eip160_transition: BlockNumber, + /// Number of first block where EIP-161.abc begin. + pub eip161abc_transition: BlockNumber, + /// Number of first block where EIP-161.d begins. + pub eip161d_transition: BlockNumber, + /// Number of first block where EIP-98 rules begin. + pub eip98_transition: BlockNumber, + /// Number of first block where EIP-658 rules begin. + pub eip658_transition: BlockNumber, + /// Number of first block where EIP-155 rules begin. + pub eip155_transition: BlockNumber, + /// Validate block receipts root. + pub validate_receipts_transition: BlockNumber, + /// Validate transaction chain id. + pub validate_chain_id_transition: BlockNumber, + /// Number of first block where EIP-140 rules begin. + pub eip140_transition: BlockNumber, + /// Number of first block where EIP-210 rules begin. + pub eip210_transition: BlockNumber, + /// EIP-210 Blockhash contract address. + pub eip210_contract_address: Address, + /// EIP-210 Blockhash contract code. + pub eip210_contract_code: Bytes, + /// Gas allocated for EIP-210 blockhash update. + pub eip210_contract_gas: U256, + /// Number of first block where EIP-211 rules begin. + pub eip211_transition: BlockNumber, + /// Number of first block where EIP-214 rules begin. + pub eip214_transition: BlockNumber, + /// Number of first block where EIP-145 rules begin. + pub eip145_transition: BlockNumber, + /// Number of first block where EIP-1052 rules begin. + pub eip1052_transition: BlockNumber, + /// Number of first block where EIP-1283 rules begin. + pub eip1283_transition: BlockNumber, + /// Number of first block where EIP-1283 rules end. + pub eip1283_disable_transition: BlockNumber, + /// Number of first block where EIP-1283 rules re-enabled. + pub eip1283_reenable_transition: BlockNumber, + /// Number of first block where EIP-1014 rules begin. + pub eip1014_transition: BlockNumber, + /// Number of first block where EIP-1706 rules begin. + pub eip1706_transition: BlockNumber, + /// Number of first block where EIP-1344 rules begin: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1344.md + pub eip1344_transition: BlockNumber, + /// Number of first block where EIP-1884 rules begin:https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1884.md + pub eip1884_transition: BlockNumber, + /// Number of first block where EIP-2028 rules begin. + pub eip2028_transition: BlockNumber, + /// Number of first block where EIP-2315 rules begin. + pub eip2315_transition: BlockNumber, + /// Number of first block where dust cleanup rules (EIP-168 and EIP169) begin. + pub dust_protection_transition: BlockNumber, + /// Nonce cap increase per block. Nonce cap is only checked if dust protection is enabled. + pub nonce_cap_increment: u64, + /// Enable dust cleanup for contracts. + pub remove_dust_contracts: bool, + /// Wasm activation blocknumber, if any disabled initially. + pub wasm_activation_transition: BlockNumber, + /// Number of first block where KIP-4 rules begin. Only has effect if Wasm is activated. + pub kip4_transition: BlockNumber, + /// Number of first block where KIP-6 rules begin. Only has effect if Wasm is activated. + pub kip6_transition: BlockNumber, + /// Gas limit bound divisor (how much gas limit can change per block) + pub gas_limit_bound_divisor: U256, + /// Registrar contract address. + pub registrar: Address, + /// Node permission managing contract address. + pub node_permission_contract: Option
, + /// Maximum contract code size that can be deployed. + pub max_code_size: u64, + /// Number of first block where max code size limit is active. + pub max_code_size_transition: BlockNumber, + /// Transaction permission managing contract address. + pub transaction_permission_contract: Option
, + /// Block at which the transaction permission contract should start being used. + pub transaction_permission_contract_transition: BlockNumber, + /// Maximum size of transaction's RLP payload + pub max_transaction_size: usize, } impl CommonParams { - /// Schedule for an EVM in the post-EIP-150-era of the Ethereum main net. - pub fn schedule(&self, block_number: u64) -> ::vm::Schedule { - if block_number < self.eip150_transition { - ::vm::Schedule::new_homestead() - } else { - let max_code_size = self.max_code_size(block_number); - let mut schedule = ::vm::Schedule::new_post_eip150( - max_code_size as _, - block_number >= self.eip160_transition, - block_number >= self.eip161abc_transition, - block_number >= self.eip161d_transition - ); - - self.update_schedule(block_number, &mut schedule); - schedule - } - } - - /// Returns max code size at given block. - pub fn max_code_size(&self, block_number: u64) -> u64 { - if block_number >= self.max_code_size_transition { - self.max_code_size - } else { - u64::max_value() - } - } - - /// Apply common spec config parameters to the schedule. - pub fn update_schedule(&self, block_number: u64, schedule: &mut ::vm::Schedule) { - schedule.have_create2 = block_number >= self.eip1014_transition; - schedule.have_revert = block_number >= self.eip140_transition; - schedule.have_static_call = block_number >= self.eip214_transition; - schedule.have_return_data = block_number >= self.eip211_transition; - schedule.have_bitwise_shifting = block_number >= self.eip145_transition; - schedule.have_extcodehash = block_number >= self.eip1052_transition; - schedule.eip1283 = block_number >= self.eip1283_transition && !(block_number >= self.eip1283_disable_transition); - if block_number >= self.eip210_transition { - schedule.blockhash_gas = 800; - } - if block_number >= self.dust_protection_transition { - schedule.kill_dust = match self.remove_dust_contracts { - true => ::vm::CleanDustMode::WithCodeAndStorage, - false => ::vm::CleanDustMode::BasicOnly, - }; - } - if block_number >= self.wasm_activation_transition { - let mut wasm = ::vm::WasmCosts::default(); - if block_number >= self.kip4_transition { - wasm.have_create2 = true; - } - if block_number >= self.kip6_transition { - wasm.have_gasleft = true; - } - schedule.wasm = Some(wasm); - } - } - - /// Return Some if the current parameters contain a bugfix hard fork not on block 0. - pub fn nonzero_bugfix_hard_fork(&self) -> Option<&str> { - if self.eip155_transition != 0 { - return Some("eip155Transition"); - } - - if self.validate_receipts_transition != 0 { - return Some("validateReceiptsTransition"); - } - - if self.validate_chain_id_transition != 0 { - return Some("validateChainIdTransition"); - } - - None - } + /// Schedule for an EVM in the post-EIP-150-era of the Ethereum main net. + pub fn schedule(&self, block_number: u64) -> ::vm::Schedule { + if block_number < self.eip150_transition { + ::vm::Schedule::new_homestead() + } else { + let max_code_size = self.max_code_size(block_number); + let mut schedule = ::vm::Schedule::new_post_eip150( + max_code_size as _, + block_number >= self.eip160_transition, + block_number >= self.eip161abc_transition, + block_number >= self.eip161d_transition, + ); + + self.update_schedule(block_number, &mut schedule); + schedule + } + } + + /// Returns max code size at given block. + pub fn max_code_size(&self, block_number: u64) -> u64 { + if block_number >= self.max_code_size_transition { + self.max_code_size + } else { + u64::max_value() + } + } + + /// Apply common spec config parameters to the schedule. + pub fn update_schedule(&self, block_number: u64, schedule: &mut ::vm::Schedule) { + schedule.have_create2 = block_number >= self.eip1014_transition; + schedule.have_revert = block_number >= self.eip140_transition; + schedule.have_static_call = block_number >= self.eip214_transition; + schedule.have_return_data = block_number >= self.eip211_transition; + schedule.have_bitwise_shifting = block_number >= self.eip145_transition; + schedule.have_extcodehash = block_number >= self.eip1052_transition; + schedule.have_chain_id = block_number >= self.eip1344_transition; + schedule.eip1283 = (block_number >= self.eip1283_transition + && !(block_number >= self.eip1283_disable_transition)) + || block_number >= self.eip1283_reenable_transition; + schedule.eip1706 = block_number >= self.eip1706_transition; + schedule.have_subs = block_number >= self.eip2315_transition; + + if block_number >= self.eip1884_transition { + schedule.have_selfbalance = true; + schedule.sload_gas = 800; + schedule.balance_gas = 700; + schedule.extcodehash_gas = 700; + } + if block_number >= self.eip2028_transition { + schedule.tx_data_non_zero_gas = 16; + } + if block_number >= self.eip210_transition { + schedule.blockhash_gas = 800; + } + if block_number >= self.dust_protection_transition { + schedule.kill_dust = match self.remove_dust_contracts { + true => ::vm::CleanDustMode::WithCodeAndStorage, + false => ::vm::CleanDustMode::BasicOnly, + }; + } + if block_number >= self.wasm_activation_transition { + let mut wasm = ::vm::WasmCosts::default(); + if block_number >= self.kip4_transition { + wasm.have_create2 = true; + } + if block_number >= self.kip6_transition { + wasm.have_gasleft = true; + } + schedule.wasm = Some(wasm); + } + } + + /// Return Some if the current parameters contain a bugfix hard fork not on block 0. + pub fn nonzero_bugfix_hard_fork(&self) -> Option<&str> { + if self.eip155_transition != 0 { + return Some("eip155Transition"); + } + + if self.validate_receipts_transition != 0 { + return Some("validateReceiptsTransition"); + } + + if self.validate_chain_id_transition != 0 { + return Some("validateChainIdTransition"); + } + + None + } } impl From for CommonParams { - fn from(p: ethjson::spec::Params) -> Self { - CommonParams { - account_start_nonce: p.account_start_nonce.map_or_else(U256::zero, Into::into), - maximum_extra_data_size: p.maximum_extra_data_size.into(), - network_id: p.network_id.into(), - chain_id: if let Some(n) = p.chain_id { - n.into() - } else { - p.network_id.into() - }, - subprotocol_name: p.subprotocol_name.unwrap_or_else(|| "eth".to_owned()), - min_gas_limit: p.min_gas_limit.into(), - fork_block: if let (Some(n), Some(h)) = (p.fork_block, p.fork_hash) { - Some((n.into(), h.into())) - } else { - None - }, - eip150_transition: p.eip150_transition.map_or(0, Into::into), - eip160_transition: p.eip160_transition.map_or(0, Into::into), - eip161abc_transition: p.eip161abc_transition.map_or(0, Into::into), - eip161d_transition: p.eip161d_transition.map_or(0, Into::into), - eip98_transition: p.eip98_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip155_transition: p.eip155_transition.map_or(0, Into::into), - validate_receipts_transition: p.validate_receipts_transition.map_or(0, Into::into), - validate_chain_id_transition: p.validate_chain_id_transition.map_or(0, Into::into), - eip140_transition: p.eip140_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip210_transition: p.eip210_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip210_contract_address: p.eip210_contract_address.map_or(0xf0.into(), Into::into), - eip210_contract_code: p.eip210_contract_code.map_or_else( - || { - DEFAULT_BLOCKHASH_CONTRACT.from_hex().expect( - "Default BLOCKHASH contract is valid", - ) - }, - Into::into, - ), - eip210_contract_gas: p.eip210_contract_gas.map_or(1000000.into(), Into::into), - eip211_transition: p.eip211_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip145_transition: p.eip145_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip214_transition: p.eip214_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip658_transition: p.eip658_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip1052_transition: p.eip1052_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip1283_transition: p.eip1283_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip1283_disable_transition: p.eip1283_disable_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip1014_transition: p.eip1014_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - dust_protection_transition: p.dust_protection_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - nonce_cap_increment: p.nonce_cap_increment.map_or(64, Into::into), - remove_dust_contracts: p.remove_dust_contracts.unwrap_or(false), - gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(), - registrar: p.registrar.map_or_else(Address::new, Into::into), - node_permission_contract: p.node_permission_contract.map(Into::into), - max_code_size: p.max_code_size.map_or(u64::max_value(), Into::into), - max_transaction_size: p.max_transaction_size.map_or(MAX_TRANSACTION_SIZE, Into::into), - max_code_size_transition: p.max_code_size_transition.map_or(0, Into::into), - transaction_permission_contract: p.transaction_permission_contract.map(Into::into), - transaction_permission_contract_transition: - p.transaction_permission_contract_transition.map_or(0, Into::into), - wasm_activation_transition: p.wasm_activation_transition.map_or_else( - BlockNumber::max_value, - Into::into - ), - kip4_transition: p.kip4_transition.map_or_else( - BlockNumber::max_value, - Into::into - ), - kip6_transition: p.kip6_transition.map_or_else( - BlockNumber::max_value, - Into::into - ), - } - } + fn from(p: ethjson::spec::Params) -> Self { + CommonParams { + account_start_nonce: p.account_start_nonce.map_or_else(U256::zero, Into::into), + maximum_extra_data_size: p.maximum_extra_data_size.into(), + network_id: p.network_id.into(), + chain_id: if let Some(n) = p.chain_id { + n.into() + } else { + p.network_id.into() + }, + subprotocol_name: p.subprotocol_name.unwrap_or_else(|| "eth".to_owned()), + min_gas_limit: p.min_gas_limit.into(), + fork_block: if let (Some(n), Some(h)) = (p.fork_block, p.fork_hash) { + Some((n.into(), h.into())) + } else { + None + }, + eip150_transition: p.eip150_transition.map_or(0, Into::into), + eip160_transition: p.eip160_transition.map_or(0, Into::into), + eip161abc_transition: p.eip161abc_transition.map_or(0, Into::into), + eip161d_transition: p.eip161d_transition.map_or(0, Into::into), + eip98_transition: p + .eip98_transition + .map_or_else(BlockNumber::max_value, Into::into), + eip155_transition: p.eip155_transition.map_or(0, Into::into), + validate_receipts_transition: p.validate_receipts_transition.map_or(0, Into::into), + validate_chain_id_transition: p.validate_chain_id_transition.map_or(0, Into::into), + eip140_transition: p + .eip140_transition + .map_or_else(BlockNumber::max_value, Into::into), + eip210_transition: p + .eip210_transition + .map_or_else(BlockNumber::max_value, Into::into), + eip210_contract_address: p.eip210_contract_address.map_or(0xf0.into(), Into::into), + eip210_contract_code: p.eip210_contract_code.map_or_else( + || { + DEFAULT_BLOCKHASH_CONTRACT + .from_hex() + .expect("Default BLOCKHASH contract is valid") + }, + Into::into, + ), + eip210_contract_gas: p.eip210_contract_gas.map_or(1000000.into(), Into::into), + eip211_transition: p + .eip211_transition + .map_or_else(BlockNumber::max_value, Into::into), + eip145_transition: p + .eip145_transition + .map_or_else(BlockNumber::max_value, Into::into), + eip214_transition: p + .eip214_transition + .map_or_else(BlockNumber::max_value, Into::into), + eip658_transition: p + .eip658_transition + .map_or_else(BlockNumber::max_value, Into::into), + eip1052_transition: p + .eip1052_transition + .map_or_else(BlockNumber::max_value, Into::into), + eip1283_transition: p + .eip1283_transition + .map_or_else(BlockNumber::max_value, Into::into), + eip1283_disable_transition: p + .eip1283_disable_transition + .map_or_else(BlockNumber::max_value, Into::into), + eip1283_reenable_transition: p + .eip1283_reenable_transition + .map_or_else(BlockNumber::max_value, Into::into), + eip1706_transition: p + .eip1706_transition + .map_or_else(BlockNumber::max_value, Into::into), + eip1014_transition: p + .eip1014_transition + .map_or_else(BlockNumber::max_value, Into::into), + eip1344_transition: p + .eip1344_transition + .map_or_else(BlockNumber::max_value, Into::into), + eip1884_transition: p + .eip1884_transition + .map_or_else(BlockNumber::max_value, Into::into), + eip2028_transition: p + .eip2028_transition + .map_or_else(BlockNumber::max_value, Into::into), + eip2315_transition: p + .eip2315_transition + .map_or_else(BlockNumber::max_value, Into::into), + dust_protection_transition: p + .dust_protection_transition + .map_or_else(BlockNumber::max_value, Into::into), + nonce_cap_increment: p.nonce_cap_increment.map_or(64, Into::into), + remove_dust_contracts: p.remove_dust_contracts.unwrap_or(false), + gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(), + registrar: p.registrar.map_or_else(Address::new, Into::into), + node_permission_contract: p.node_permission_contract.map(Into::into), + max_code_size: p.max_code_size.map_or(u64::max_value(), Into::into), + max_transaction_size: p + .max_transaction_size + .map_or(MAX_TRANSACTION_SIZE, Into::into), + max_code_size_transition: p.max_code_size_transition.map_or(0, Into::into), + transaction_permission_contract: p.transaction_permission_contract.map(Into::into), + transaction_permission_contract_transition: p + .transaction_permission_contract_transition + .map_or(0, Into::into), + wasm_activation_transition: p + .wasm_activation_transition + .map_or_else(BlockNumber::max_value, Into::into), + kip4_transition: p + .kip4_transition + .map_or_else(BlockNumber::max_value, Into::into), + kip6_transition: p + .kip6_transition + .map_or_else(BlockNumber::max_value, Into::into), + } + } } /// Runtime parameters for the spec that are related to how the software should run the chain, /// rather than integral properties of the chain itself. #[derive(Debug, Clone, Copy)] pub struct SpecParams<'a> { - /// The path to the folder used to cache nodes. This is typically /tmp/ on Unix-like systems - pub cache_dir: &'a Path, - /// Whether to run slower at the expense of better memory usage, or run faster while using - /// more - /// memory. This may get more fine-grained in the future but for now is simply a binary - /// option. - pub optimization_setting: Option, + /// The path to the folder used to cache nodes. This is typically /tmp/ on Unix-like systems + pub cache_dir: &'a Path, + /// Whether to run slower at the expense of better memory usage, or run faster while using + /// more + /// memory. This may get more fine-grained in the future but for now is simply a binary + /// option. + pub optimization_setting: Option, } impl<'a> SpecParams<'a> { - /// Create from a cache path, with null values for the other fields - pub fn from_path(path: &'a Path) -> Self { - SpecParams { - cache_dir: path, - optimization_setting: None, - } - } - - /// Create from a cache path and an optimization setting - pub fn new(path: &'a Path, optimization: OptimizeFor) -> Self { - SpecParams { - cache_dir: path, - optimization_setting: Some(optimization), - } - } + /// Create from a cache path, with null values for the other fields + pub fn from_path(path: &'a Path) -> Self { + SpecParams { + cache_dir: path, + optimization_setting: None, + } + } + + /// Create from a cache path and an optimization setting + pub fn new(path: &'a Path, optimization: OptimizeFor) -> Self { + SpecParams { + cache_dir: path, + optimization_setting: Some(optimization), + } + } } impl<'a, T: AsRef> From<&'a T> for SpecParams<'a> { - fn from(path: &'a T) -> Self { - Self::from_path(path.as_ref()) - } + fn from(path: &'a T) -> Self { + Self::from_path(path.as_ref()) + } } /// Parameters for a block chain; includes both those intrinsic to the design of the /// chain and those to be interpreted by the active chain engine. pub struct Spec { - /// User friendly spec name - pub name: String, - /// What engine are we using for this? - pub engine: Arc, - /// Name of the subdir inside the main data dir to use for chain data and settings. - pub data_dir: String, - - /// Known nodes on the network in enode format. - pub nodes: Vec, - - /// The genesis block's parent hash field. - pub parent_hash: H256, - /// The genesis block's author field. - pub author: Address, - /// The genesis block's difficulty field. - pub difficulty: U256, - /// The genesis block's gas limit field. - pub gas_limit: U256, - /// The genesis block's gas used field. - pub gas_used: U256, - /// The genesis block's timestamp field. - pub timestamp: u64, - /// Transactions root of the genesis block. Should be KECCAK_NULL_RLP. - pub transactions_root: H256, - /// Receipts root of the genesis block. Should be KECCAK_NULL_RLP. - pub receipts_root: H256, - /// The genesis block's extra data field. - pub extra_data: Bytes, - /// Each seal field, expressed as RLP, concatenated. - pub seal_rlp: Bytes, - - /// Hardcoded synchronization. Allows the light client to immediately jump to a specific block. - pub hardcoded_sync: Option, - - /// Contract constructors to be executed on genesis. - constructors: Vec<(Address, Bytes)>, - - /// May be prepopulated if we know this in advance. - state_root_memo: RwLock, - - /// Genesis state as plain old data. - genesis_state: PodState, + /// User friendly spec name + pub name: String, + /// What engine are we using for this? + pub engine: Arc, + /// Name of the subdir inside the main data dir to use for chain data and settings. + pub data_dir: String, + + /// Known nodes on the network in enode format. + pub nodes: Vec, + + /// The genesis block's parent hash field. + pub parent_hash: H256, + /// The genesis block's author field. + pub author: Address, + /// The genesis block's difficulty field. + pub difficulty: U256, + /// The genesis block's gas limit field. + pub gas_limit: U256, + /// The genesis block's gas used field. + pub gas_used: U256, + /// The genesis block's timestamp field. + pub timestamp: u64, + /// Transactions root of the genesis block. Should be KECCAK_NULL_RLP. + pub transactions_root: H256, + /// Receipts root of the genesis block. Should be KECCAK_NULL_RLP. + pub receipts_root: H256, + /// The genesis block's extra data field. + pub extra_data: Bytes, + /// Each seal field, expressed as RLP, concatenated. + pub seal_rlp: Bytes, + + /// List of hard forks in the network. + pub hard_forks: BTreeSet, + + /// Contract constructors to be executed on genesis. + constructors: Vec<(Address, Bytes)>, + + /// May be prepopulated if we know this in advance. + state_root_memo: RwLock, + + /// Genesis state as plain old data. + genesis_state: PodState, } #[cfg(test)] impl Clone for Spec { - fn clone(&self) -> Spec { - Spec { - name: self.name.clone(), - engine: self.engine.clone(), - data_dir: self.data_dir.clone(), - nodes: self.nodes.clone(), - parent_hash: self.parent_hash.clone(), - transactions_root: self.transactions_root.clone(), - receipts_root: self.receipts_root.clone(), - author: self.author.clone(), - difficulty: self.difficulty.clone(), - gas_limit: self.gas_limit.clone(), - gas_used: self.gas_used.clone(), - timestamp: self.timestamp.clone(), - extra_data: self.extra_data.clone(), - seal_rlp: self.seal_rlp.clone(), - hardcoded_sync: self.hardcoded_sync.clone(), - constructors: self.constructors.clone(), - state_root_memo: RwLock::new(*self.state_root_memo.read()), - genesis_state: self.genesis_state.clone(), - } - } -} - -/// Part of `Spec`. Describes the hardcoded synchronization parameters. -pub struct SpecHardcodedSync { - /// Header of the block to jump to for hardcoded sync, and total difficulty. - pub header: encoded::Header, - /// Total difficulty of the block to jump to. - pub total_difficulty: U256, - /// List of hardcoded CHTs, in order. If `hardcoded_sync` is set, the CHTs should include the - /// header of `hardcoded_sync`. - pub chts: Vec, -} - -impl SpecHardcodedSync { - /// Turns this specifications back into JSON. Useful for pretty printing. - pub fn to_json(self) -> ethjson::spec::HardcodedSync { - self.into() - } -} - -#[cfg(test)] -impl Clone for SpecHardcodedSync { - fn clone(&self) -> SpecHardcodedSync { - SpecHardcodedSync { - header: self.header.clone(), - total_difficulty: self.total_difficulty.clone(), - chts: self.chts.clone(), - } - } -} - -impl From for ethjson::spec::HardcodedSync { - fn from(sync: SpecHardcodedSync) -> ethjson::spec::HardcodedSync { - ethjson::spec::HardcodedSync { - header: sync.header.into_inner().to_hex(), - total_difficulty: ethjson::uint::Uint(sync.total_difficulty), - chts: sync.chts.into_iter().map(Into::into).collect(), - } - } + fn clone(&self) -> Spec { + Spec { + name: self.name.clone(), + engine: self.engine.clone(), + data_dir: self.data_dir.clone(), + nodes: self.nodes.clone(), + parent_hash: self.parent_hash.clone(), + transactions_root: self.transactions_root.clone(), + receipts_root: self.receipts_root.clone(), + author: self.author.clone(), + difficulty: self.difficulty.clone(), + gas_limit: self.gas_limit.clone(), + gas_used: self.gas_used.clone(), + timestamp: self.timestamp.clone(), + extra_data: self.extra_data.clone(), + seal_rlp: self.seal_rlp.clone(), + hard_forks: self.hard_forks.clone(), + constructors: self.constructors.clone(), + state_root_memo: RwLock::new(*self.state_root_memo.read()), + genesis_state: self.genesis_state.clone(), + } + } } fn load_machine_from(s: ethjson::spec::Spec) -> EthereumMachine { - let builtins = s.accounts.builtins().into_iter().map(|p| (p.0.into(), From::from(p.1))).collect(); - let params = CommonParams::from(s.params); + let builtins = s + .accounts + .builtins() + .into_iter() + .map(|p| { + ( + p.0.into(), + Builtin::try_from(p.1).expect("chain spec is invalid"), + ) + }) + .collect(); + let params = CommonParams::from(s.params); + + Spec::machine(&s.engine, params, builtins) +} - Spec::machine(&s.engine, params, builtins) +fn convert_json_to_spec( + (address, builtin): (ethjson::hash::Address, ethjson::spec::builtin::Builtin), +) -> Result<(Address, Builtin), Error> { + let builtin = Builtin::try_from(builtin)?; + Ok((address.into(), builtin)) } /// Load from JSON object. fn load_from(spec_params: SpecParams, s: ethjson::spec::Spec) -> Result { - let builtins = s.accounts - .builtins() - .into_iter() - .map(|p| (p.0.into(), From::from(p.1))) - .collect(); - let g = Genesis::from(s.genesis); - let GenericSeal(seal_rlp) = g.seal.into(); - let params = CommonParams::from(s.params); - - let hardcoded_sync = if let Some(ref hs) = s.hardcoded_sync { - if let Ok(header) = hs.header.from_hex() { - Some(SpecHardcodedSync { - header: encoded::Header::new(header), - total_difficulty: hs.total_difficulty.into(), - chts: s.hardcoded_sync - .as_ref() - .map(|s| s.chts.iter().map(|c| c.clone().into()).collect()) - .unwrap_or_default() - }) - } else { - None - } - } else { - None - }; - - let mut s = Spec { - name: s.name.clone().into(), - engine: Spec::engine(spec_params, s.engine, params, builtins), - data_dir: s.data_dir.unwrap_or(s.name).into(), - nodes: s.nodes.unwrap_or_else(Vec::new), - parent_hash: g.parent_hash, - transactions_root: g.transactions_root, - receipts_root: g.receipts_root, - author: g.author, - difficulty: g.difficulty, - gas_limit: g.gas_limit, - gas_used: g.gas_used, - timestamp: g.timestamp, - extra_data: g.extra_data, - seal_rlp: seal_rlp, - hardcoded_sync: hardcoded_sync, - constructors: s.accounts - .constructors() - .into_iter() - .map(|(a, c)| (a.into(), c.into())) - .collect(), - state_root_memo: RwLock::new(Default::default()), // will be overwritten right after. - genesis_state: s.accounts.into(), - }; - - // use memoized state root if provided. - match g.state_root { - Some(root) => *s.state_root_memo.get_mut() = root, - None => { - let _ = s.run_constructors( - &Default::default(), - BasicBackend(journaldb::new_memory_db()), - )?; - } - } - - Ok(s) + let builtins: Result, _> = s + .accounts + .builtins() + .into_iter() + .map(convert_json_to_spec) + .collect(); + let builtins = builtins?; + let g = Genesis::from(s.genesis); + let GenericSeal(seal_rlp) = g.seal.into(); + let params = CommonParams::from(s.params); + + let (engine, hard_forks) = Spec::engine(spec_params, s.engine, params, builtins); + + let mut s = Spec { + name: s.name.clone().into(), + engine, + data_dir: s.data_dir.unwrap_or(s.name).into(), + nodes: s.nodes.unwrap_or_else(Vec::new), + parent_hash: g.parent_hash, + transactions_root: g.transactions_root, + receipts_root: g.receipts_root, + author: g.author, + difficulty: g.difficulty, + gas_limit: g.gas_limit, + gas_used: g.gas_used, + timestamp: g.timestamp, + extra_data: g.extra_data, + seal_rlp: seal_rlp, + hard_forks, + constructors: s + .accounts + .constructors() + .into_iter() + .map(|(a, c)| (a.into(), c.into())) + .collect(), + state_root_memo: RwLock::new(Default::default()), // will be overwritten right after. + genesis_state: s.accounts.into(), + }; + + // use memoized state root if provided. + match g.state_root { + Some(root) => *s.state_root_memo.get_mut() = root, + None => { + let _ = s.run_constructors( + &Default::default(), + BasicBackend(journaldb::new_memory_db()), + )?; + } + } + + Ok(s) } macro_rules! load_bundled { - ($e:expr) => { - Spec::load( - &::std::env::temp_dir(), - include_bytes!(concat!("../../res/", $e, ".json")) as &[u8] - ).expect(concat!("Chain spec ", $e, " is invalid.")) - }; + ($e:expr) => { + Spec::load( + &::std::env::temp_dir(), + include_bytes!(concat!("../../res/", $e, ".json")) as &[u8], + ) + .expect(concat!("Chain spec ", $e, " is invalid.")) + }; } #[cfg(any(test, feature = "test-helpers"))] macro_rules! load_machine_bundled { - ($e:expr) => { - Spec::load_machine( - include_bytes!(concat!("../../res/", $e, ".json")) as &[u8] - ).expect(concat!("Chain spec ", $e, " is invalid.")) - }; + ($e:expr) => { + Spec::load_machine(include_bytes!(concat!("../../res/", $e, ".json")) as &[u8]) + .expect(concat!("Chain spec ", $e, " is invalid.")) + }; } impl Spec { - // create an instance of an Ethereum state machine, minus consensus logic. - fn machine( - engine_spec: ðjson::spec::Engine, - params: CommonParams, - builtins: BTreeMap, - ) -> EthereumMachine { - if let ethjson::spec::Engine::Ethash(ref ethash) = *engine_spec { - EthereumMachine::with_ethash_extensions(params, builtins, ethash.params.clone().into()) - } else { - EthereumMachine::regular(params, builtins) - } - } - - /// Convert engine spec into a arc'd Engine of the right underlying type. - /// TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead. - fn engine( - spec_params: SpecParams, - engine_spec: ethjson::spec::Engine, - params: CommonParams, - builtins: BTreeMap, - ) -> Arc { - let machine = Self::machine(&engine_spec, params, builtins); - - match engine_spec { - ethjson::spec::Engine::Null(null) => Arc::new(NullEngine::new(null.params.into(), machine)), - ethjson::spec::Engine::Ethash(ethash) => Arc::new(::ethereum::Ethash::new(spec_params.cache_dir, ethash.params.into(), machine, spec_params.optimization_setting)), - ethjson::spec::Engine::InstantSeal(Some(instant_seal)) => Arc::new(InstantSeal::new(instant_seal.params.into(), machine)), - ethjson::spec::Engine::InstantSeal(None) => Arc::new(InstantSeal::new(InstantSealParams::default(), machine)), - ethjson::spec::Engine::BasicAuthority(basic_authority) => Arc::new(BasicAuthority::new(basic_authority.params.into(), machine)), - ethjson::spec::Engine::Clique(clique) => Clique::new(clique.params.into(), machine) - .expect("Failed to start Clique consensus engine."), - ethjson::spec::Engine::AuthorityRound(authority_round) => AuthorityRound::new(authority_round.params.into(), machine) - .expect("Failed to start AuthorityRound consensus engine."), - } - } - - // given a pre-constructor state, run all the given constructors and produce a new state and - // state root. - fn run_constructors(&self, factories: &Factories, mut db: T) -> Result { - let mut root = KECCAK_NULL_RLP; - - // basic accounts in spec. - { - let mut t = factories.trie.create(db.as_hash_db_mut(), &mut root); - - for (address, account) in self.genesis_state.get().iter() { - t.insert(&**address, &account.rlp())?; - } - } - - for (address, account) in self.genesis_state.get().iter() { - db.note_non_null_account(address); - account.insert_additional( - &mut *factories.accountdb.create( - db.as_hash_db_mut(), - keccak(address), - ), - &factories.trie, - ); - } - - let start_nonce = self.engine.account_start_nonce(0); - - let (root, db) = { - let mut state = State::from_existing(db, root, start_nonce, factories.clone())?; - - // Execute contract constructors. - let env_info = EnvInfo { - number: 0, - author: self.author, - timestamp: self.timestamp, - difficulty: self.difficulty, - last_hashes: Default::default(), - gas_used: U256::zero(), - gas_limit: U256::max_value(), - }; - - let from = Address::default(); - for &(ref address, ref constructor) in self.constructors.iter() { - trace!(target: "spec", "run_constructors: Creating a contract at {}.", address); - trace!(target: "spec", " .. root before = {}", state.root()); - let params = ActionParams { - code_address: address.clone(), - code_hash: Some(keccak(constructor)), - address: address.clone(), - sender: from.clone(), - origin: from.clone(), - gas: U256::max_value(), - gas_price: Default::default(), - value: ActionValue::Transfer(Default::default()), - code: Some(Arc::new(constructor.clone())), - data: None, - call_type: CallType::None, - params_type: ParamsType::Embedded, - }; - - let mut substate = Substate::new(); - - { - let machine = self.engine.machine(); - let schedule = machine.schedule(env_info.number); - let mut exec = Executive::new(&mut state, &env_info, &machine, &schedule); - if let Err(e) = exec.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) { - warn!(target: "spec", "Genesis constructor execution at {} failed: {}.", address, e); - } - } - - if let Err(e) = state.commit() { - warn!(target: "spec", "Genesis constructor trie commit at {} failed: {}.", address, e); - } - - trace!(target: "spec", " .. root after = {}", state.root()); - } - - state.drop() - }; - - *self.state_root_memo.write() = root; - Ok(db) - } - - /// Return the state root for the genesis state, memoising accordingly. - pub fn state_root(&self) -> H256 { - self.state_root_memo.read().clone() - } - - /// Get common blockchain parameters. - pub fn params(&self) -> &CommonParams { - &self.engine.params() - } - - /// Get the known knodes of the network in enode format. - pub fn nodes(&self) -> &[String] { - &self.nodes - } - - /// Get the configured Network ID. - pub fn network_id(&self) -> u64 { - self.params().network_id - } - - /// Get the chain ID used for signing. - pub fn chain_id(&self) -> u64 { - self.params().chain_id - } - - /// Get the configured subprotocol name. - pub fn subprotocol_name(&self) -> String { - self.params().subprotocol_name.clone() - } - - /// Get the configured network fork block. - pub fn fork_block(&self) -> Option<(BlockNumber, H256)> { - self.params().fork_block - } - - /// Get the header of the genesis block. - pub fn genesis_header(&self) -> Header { - let mut header: Header = Default::default(); - header.set_parent_hash(self.parent_hash.clone()); - header.set_timestamp(self.timestamp); - header.set_number(0); - header.set_author(self.author.clone()); - header.set_transactions_root(self.transactions_root.clone()); - header.set_uncles_hash(keccak(RlpStream::new_list(0).out())); - header.set_extra_data(self.extra_data.clone()); - header.set_state_root(self.state_root()); - header.set_receipts_root(self.receipts_root.clone()); - header.set_log_bloom(Bloom::default()); - header.set_gas_used(self.gas_used.clone()); - header.set_gas_limit(self.gas_limit.clone()); - header.set_difficulty(self.difficulty.clone()); - header.set_seal({ - let r = Rlp::new(&self.seal_rlp); - r.iter().map(|f| f.as_raw().to_vec()).collect() - }); - trace!(target: "spec", "Header hash is {}", header.hash()); - header - } - - /// Compose the genesis block for this chain. - pub fn genesis_block(&self) -> Bytes { - let empty_list = RlpStream::new_list(0).out(); - let header = self.genesis_header(); - let mut ret = RlpStream::new_list(3); - ret.append(&header); - ret.append_raw(&empty_list, 1); - ret.append_raw(&empty_list, 1); - ret.out() - } - - /// Overwrite the genesis components. - pub fn overwrite_genesis_params(&mut self, g: Genesis) { - let GenericSeal(seal_rlp) = g.seal.into(); - self.parent_hash = g.parent_hash; - self.transactions_root = g.transactions_root; - self.receipts_root = g.receipts_root; - self.author = g.author; - self.difficulty = g.difficulty; - self.gas_limit = g.gas_limit; - self.gas_used = g.gas_used; - self.timestamp = g.timestamp; - self.extra_data = g.extra_data; - self.seal_rlp = seal_rlp; - } - - /// Alter the value of the genesis state. - pub fn set_genesis_state(&mut self, s: PodState) -> Result<(), Error> { - self.genesis_state = s; - let _ = self.run_constructors( - &Default::default(), - BasicBackend(journaldb::new_memory_db()), - )?; - - Ok(()) - } - - /// Return genesis state as Plain old data. - pub fn genesis_state(&self) -> &PodState { - &self.genesis_state - } - - /// Returns `false` if the memoized state root is invalid. `true` otherwise. - pub fn is_state_root_valid(&self) -> bool { - // TODO: get rid of this function and ensure state root always is valid. - // we're mostly there, but `self.genesis_state.root()` doesn't encompass - // post-constructor state. - *self.state_root_memo.read() == self.genesis_state.root() - } - - /// Ensure that the given state DB has the trie nodes in for the genesis state. - pub fn ensure_db_good(&self, db: T, factories: &Factories) -> Result { - if db.as_hash_db().contains(&self.state_root()) { - return Ok(db); - } - - // TODO: could optimize so we don't re-run, but `ensure_db_good` is barely ever - // called anyway. - let db = self.run_constructors(factories, db)?; - Ok(db) - } - - /// Loads just the state machine from a json file. - pub fn load_machine(reader: R) -> Result { - ethjson::spec::Spec::load(reader) - .map_err(fmt_err) - .map(load_machine_from) - } - - /// Loads spec from json file. Provide factories for executing contracts and ensuring - /// storage goes to the right place. - pub fn load<'a, T: Into>, R>(params: T, reader: R) -> Result - where - R: Read, - { - ethjson::spec::Spec::load(reader).map_err(fmt_err).and_then( - |x| { - load_from(params.into(), x).map_err(fmt_err) - }, - ) - } - - /// initialize genesis epoch data, using in-memory database for - /// constructor. - pub fn genesis_epoch_data(&self) -> Result, String> { - use types::transaction::{Action, Transaction}; - use journaldb; - use kvdb_memorydb; - - let genesis = self.genesis_header(); - - let factories = Default::default(); - let mut db = journaldb::new( - Arc::new(kvdb_memorydb::create(0)), - journaldb::Algorithm::Archive, - None, - ); - - self.ensure_db_good(BasicBackend(db.as_hash_db_mut()), &factories) - .map_err(|e| format!("Unable to initialize genesis state: {}", e))?; - - let call = |a, d| { - let mut db = db.boxed_clone(); - let env_info = ::evm::EnvInfo { - number: 0, - author: *genesis.author(), - timestamp: genesis.timestamp(), - difficulty: *genesis.difficulty(), - gas_limit: U256::max_value(), - last_hashes: Arc::new(Vec::new()), - gas_used: 0.into(), - }; - - let from = Address::default(); - let tx = Transaction { - nonce: self.engine.account_start_nonce(0), - action: Action::Call(a), - gas: U256::max_value(), - gas_price: U256::default(), - value: U256::default(), - data: d, - }.fake_sign(from); - - let res = ::state::prove_transaction_virtual( - db.as_hash_db_mut(), - *genesis.state_root(), - &tx, - self.engine.machine(), - &env_info, - factories.clone(), - ); - - res.map(|(out, proof)| { - (out, proof.into_iter().map(|x| x.into_vec()).collect()) - }).ok_or_else(|| "Failed to prove call: insufficient state".into()) - }; - - self.engine.genesis_epoch_data(&genesis, &call) - } - - /// Create a new Spec with InstantSeal consensus which does internal sealing (not requiring - /// work). - pub fn new_instant() -> Spec { - load_bundled!("instant_seal") - } - - /// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a - /// NullEngine consensus. - #[cfg(any(test, feature = "test-helpers"))] - pub fn new_test() -> Spec { - load_bundled!("null_morden") - } - - /// Create the EthereumMachine corresponding to Spec::new_test. - #[cfg(any(test, feature = "test-helpers"))] - pub fn new_test_machine() -> EthereumMachine { load_machine_bundled!("null_morden") } - - /// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a NullEngine consensus with applying reward on block close. - #[cfg(any(test, feature = "test-helpers"))] - pub fn new_test_with_reward() -> Spec { load_bundled!("null_morden_with_reward") } - - /// Create a new Spec which is a NullEngine consensus with a premine of address whose - /// secret is keccak(''). - #[cfg(any(test, feature = "test-helpers"))] - pub fn new_null() -> Spec { - load_bundled!("null") - } - - /// Create a new Spec which constructs a contract at address 5 with storage at 0 equal to 1. - #[cfg(any(test, feature = "test-helpers"))] - pub fn new_test_constructor() -> Spec { - load_bundled!("constructor") - } - - /// Create a new Spec with AuthorityRound consensus which does internal sealing (not - /// requiring work). - /// Accounts with secrets keccak("0") and keccak("1") are the validators. - #[cfg(any(test, feature = "test-helpers"))] - pub fn new_test_round() -> Self { - load_bundled!("authority_round") - } - - /// Create a new Spec with AuthorityRound consensus which does internal sealing (not - /// requiring work) with empty step messages enabled. - /// Accounts with secrets keccak("0") and keccak("1") are the validators. - #[cfg(any(test, feature = "test-helpers"))] - pub fn new_test_round_empty_steps() -> Self { - load_bundled!("authority_round_empty_steps") - } - - /// Create a new Spec with AuthorityRound consensus (with empty steps) using a block reward - /// contract. The contract source code can be found at: - /// https://github.com/parity-contracts/block-reward/blob/daf7d44383b6cdb11cb6b953b018648e2b027cfb/contracts/ExampleBlockReward.sol - #[cfg(any(test, feature = "test-helpers"))] - pub fn new_test_round_block_reward_contract() -> Self { - load_bundled!("authority_round_block_reward_contract") - } - - /// TestList.sol used in both specs: https://github.com/paritytech/contracts/pull/30/files - /// Accounts with secrets keccak("0") and keccak("1") are initially the validators. - /// Create a new Spec with BasicAuthority which uses a contract at address 5 to determine - /// the current validators using `getValidators`. - /// Second validator can be removed with - /// "0xbfc708a000000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1" and added - /// back in using - /// "0x4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1". - #[cfg(any(test, feature = "test-helpers"))] - pub fn new_validator_safe_contract() -> Self { - load_bundled!("validator_safe_contract") - } - - /// The same as the `safeContract`, but allows reporting and uses AuthorityRound. - /// Account is marked with `reportBenign` it can be checked as disliked with "0xd8f2e0bf". - /// Validator can be removed with `reportMalicious`. - #[cfg(any(test, feature = "test-helpers"))] - pub fn new_validator_contract() -> Self { - load_bundled!("validator_contract") - } - - /// Create a new Spec with BasicAuthority which uses multiple validator sets changing with - /// height. - /// Account with secrets keccak("0") is the validator for block 1 and with keccak("1") - /// onwards. - #[cfg(any(test, feature = "test-helpers"))] - pub fn new_validator_multi() -> Self { - load_bundled!("validator_multi") - } + // create an instance of an Ethereum state machine, minus consensus logic. + fn machine( + engine_spec: ðjson::spec::Engine, + params: CommonParams, + builtins: BTreeMap, + ) -> EthereumMachine { + if let ethjson::spec::Engine::Ethash(ref ethash) = *engine_spec { + EthereumMachine::with_ethash_extensions(params, builtins, ethash.params.clone().into()) + } else { + EthereumMachine::regular(params, builtins) + } + } + + /// Convert engine spec into a arc'd Engine of the right underlying type. + /// TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead. + fn engine( + spec_params: SpecParams, + engine_spec: ethjson::spec::Engine, + params: CommonParams, + builtins: BTreeMap, + ) -> (Arc, BTreeSet) { + let mut hard_forks = btreeset![ + params.eip150_transition, + params.eip160_transition, + params.eip161abc_transition, + params.eip161d_transition, + params.eip98_transition, + params.eip658_transition, + params.eip155_transition, + params.validate_receipts_transition, + params.validate_chain_id_transition, + params.eip140_transition, + params.eip210_transition, + params.eip211_transition, + params.eip214_transition, + params.eip145_transition, + params.eip1052_transition, + params.eip1283_transition, + params.eip1283_disable_transition, + params.eip1283_reenable_transition, + params.eip1014_transition, + params.eip1706_transition, + params.eip1344_transition, + params.eip1884_transition, + params.eip2028_transition, + params.eip2315_transition, + params.dust_protection_transition, + params.wasm_activation_transition, + params.kip4_transition, + params.kip6_transition, + params.max_code_size_transition, + params.transaction_permission_contract_transition, + ]; + // BUG: Rinkeby has homestead transition at block 1 but we can't reflect that in specs for non-Ethash networks + if params.network_id == 0x4 { + hard_forks.insert(1); + } + + let machine = Self::machine(&engine_spec, params, builtins); + + let engine: Arc = match engine_spec { + ethjson::spec::Engine::Null(null) => { + Arc::new(NullEngine::new(null.params.into(), machine)) + } + ethjson::spec::Engine::Ethash(ethash) => { + // Specific transitions for Ethash-based networks + for block in &[ + ethash.params.homestead_transition, + ethash.params.dao_hardfork_transition, + ] { + if let Some(block) = *block { + hard_forks.insert(block.into()); + } + } + + // Ethereum's difficulty bomb delay is a fork too + if let Some(delays) = ðash.params.difficulty_bomb_delays { + for delay in delays.keys().copied() { + hard_forks.insert(delay.into()); + } + } + + Arc::new(::ethereum::Ethash::new( + spec_params.cache_dir, + ethash.params.into(), + machine, + spec_params.optimization_setting, + )) + } + ethjson::spec::Engine::InstantSeal(Some(instant_seal)) => { + Arc::new(InstantSeal::new(instant_seal.params.into(), machine)) + } + ethjson::spec::Engine::InstantSeal(None) => { + Arc::new(InstantSeal::new(InstantSealParams::default(), machine)) + } + ethjson::spec::Engine::BasicAuthority(basic_authority) => { + Arc::new(BasicAuthority::new(basic_authority.params.into(), machine)) + } + ethjson::spec::Engine::Clique(clique) => Clique::new(clique.params.into(), machine) + .expect("Failed to start Clique consensus engine."), + ethjson::spec::Engine::AuthorityRound(authority_round) => { + AuthorityRound::new(authority_round.params.into(), machine) + .expect("Failed to start AuthorityRound consensus engine.") + } + }; + + // Dummy value is a filler for non-existent transitions + hard_forks.remove(&BlockNumber::max_value()); + + (engine, hard_forks) + } + + // given a pre-constructor state, run all the given constructors and produce a new state and + // state root. + fn run_constructors(&self, factories: &Factories, mut db: T) -> Result { + let mut root = KECCAK_NULL_RLP; + + // basic accounts in spec. + { + let mut t = factories.trie.create(db.as_hash_db_mut(), &mut root); + + for (address, account) in self.genesis_state.get().iter() { + t.insert(&**address, &account.rlp())?; + } + } + + for (address, account) in self.genesis_state.get().iter() { + account.insert_additional( + &mut *factories + .accountdb + .create(db.as_hash_db_mut(), keccak(address)), + &factories.trie, + ); + } + + let start_nonce = self.engine.account_start_nonce(0); + + let (root, db) = { + let mut state = State::from_existing(db, root, start_nonce, factories.clone())?; + + // Execute contract constructors. + let env_info = EnvInfo { + number: 0, + author: self.author, + timestamp: self.timestamp, + difficulty: self.difficulty, + last_hashes: Default::default(), + gas_used: U256::zero(), + gas_limit: U256::max_value(), + }; + + if !self.constructors.is_empty() { + let from = Address::default(); + for &(ref address, ref constructor) in self.constructors.iter() { + trace!(target: "spec", "run_constructors: Creating a contract at {}.", address); + trace!(target: "spec", " .. root before = {}", state.root()); + let params = ActionParams { + code_address: address.clone(), + code_hash: Some(keccak(constructor)), + address: address.clone(), + sender: from.clone(), + origin: from.clone(), + gas: U256::max_value(), + gas_price: Default::default(), + value: ActionValue::Transfer(Default::default()), + code: Some(Arc::new(constructor.clone())), + data: None, + call_type: CallType::None, + params_type: ParamsType::Embedded, + }; + + let mut substate = Substate::new(); + + { + let machine = self.engine.machine(); + let schedule = machine.schedule(env_info.number); + let mut exec = Executive::new(&mut state, &env_info, &machine, &schedule); + if let Err(e) = + exec.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) + { + warn!(target: "spec", "Genesis constructor execution at {} failed: {}.", address, e); + } + } + + if let Err(e) = state.commit() { + warn!(target: "spec", "Genesis constructor trie commit at {} failed: {}.", address, e); + } + + trace!(target: "spec", " .. root after = {}", state.root()); + } + } else { + state.populate_from(self.genesis_state().to_owned()); + state.commit()?; + } + state.drop() + }; + + *self.state_root_memo.write() = root; + Ok(db) + } + + /// Return the state root for the genesis state, memoising accordingly. + pub fn state_root(&self) -> H256 { + self.state_root_memo.read().clone() + } + + /// Get common blockchain parameters. + pub fn params(&self) -> &CommonParams { + &self.engine.params() + } + + /// Get the known knodes of the network in enode format. + pub fn nodes(&self) -> &[String] { + &self.nodes + } + + /// Get the configured Network ID. + pub fn network_id(&self) -> u64 { + self.params().network_id + } + + /// Get the chain ID used for signing. + pub fn chain_id(&self) -> u64 { + self.params().chain_id + } + + /// Get the configured subprotocol name. + pub fn subprotocol_name(&self) -> String { + self.params().subprotocol_name.clone() + } + + /// Get the configured network fork block. + pub fn fork_block(&self) -> Option<(BlockNumber, H256)> { + self.params().fork_block + } + + /// Get the header of the genesis block. + pub fn genesis_header(&self) -> Header { + let mut header: Header = Default::default(); + header.set_parent_hash(self.parent_hash.clone()); + header.set_timestamp(self.timestamp); + header.set_number(0); + header.set_author(self.author.clone()); + header.set_transactions_root(self.transactions_root.clone()); + header.set_uncles_hash(keccak(RlpStream::new_list(0).out())); + header.set_extra_data(self.extra_data.clone()); + header.set_state_root(self.state_root()); + header.set_receipts_root(self.receipts_root.clone()); + header.set_log_bloom(Bloom::default()); + header.set_gas_used(self.gas_used.clone()); + header.set_gas_limit(self.gas_limit.clone()); + header.set_difficulty(self.difficulty.clone()); + header.set_seal({ + let r = Rlp::new(&self.seal_rlp); + r.iter().map(|f| f.as_raw().to_vec()).collect() + }); + trace!(target: "spec", "Header hash is {}", header.hash()); + header + } + + /// Compose the genesis block for this chain. + pub fn genesis_block(&self) -> Bytes { + let empty_list = RlpStream::new_list(0).out(); + let header = self.genesis_header(); + let mut ret = RlpStream::new_list(3); + ret.append(&header); + ret.append_raw(&empty_list, 1); + ret.append_raw(&empty_list, 1); + ret.out() + } + + /// Overwrite the genesis components. + pub fn overwrite_genesis_params(&mut self, g: Genesis) { + let GenericSeal(seal_rlp) = g.seal.into(); + self.parent_hash = g.parent_hash; + self.transactions_root = g.transactions_root; + self.receipts_root = g.receipts_root; + self.author = g.author; + self.difficulty = g.difficulty; + self.gas_limit = g.gas_limit; + self.gas_used = g.gas_used; + self.timestamp = g.timestamp; + self.extra_data = g.extra_data; + self.seal_rlp = seal_rlp; + } + + /// Alter the value of the genesis state. + pub fn set_genesis_state(&mut self, s: PodState) -> Result<(), Error> { + self.genesis_state = s; + let _ = self.run_constructors( + &Default::default(), + BasicBackend(journaldb::new_memory_db()), + )?; + + Ok(()) + } + + /// Return genesis state as Plain old data. + pub fn genesis_state(&self) -> &PodState { + &self.genesis_state + } + + /// Returns `false` if the memoized state root is invalid. `true` otherwise. + pub fn is_state_root_valid(&self) -> bool { + // TODO: get rid of this function and ensure state root always is valid. + // we're mostly there, but `self.genesis_state.root()` doesn't encompass + // post-constructor state. + *self.state_root_memo.read() == self.genesis_state.root() + } + + /// Ensure that the given state DB has the trie nodes in for the genesis state. + pub fn ensure_db_good(&self, db: T, factories: &Factories) -> Result { + if db.as_hash_db().contains(&self.state_root()) { + return Ok(db); + } + + // TODO: could optimize so we don't re-run, but `ensure_db_good` is barely ever + // called anyway. + let db = self.run_constructors(factories, db)?; + Ok(db) + } + + /// Loads just the state machine from a json file. + pub fn load_machine(reader: R) -> Result { + ethjson::spec::Spec::load(reader) + .map_err(fmt_err) + .map(load_machine_from) + } + + /// Loads spec from json file. Provide factories for executing contracts and ensuring + /// storage goes to the right place. + pub fn load<'a, T: Into>, R>(params: T, reader: R) -> Result + where + R: Read, + { + ethjson::spec::Spec::load(reader) + .map_err(fmt_err) + .and_then(|x| load_from(params.into(), x).map_err(fmt_err)) + } + + /// initialize genesis epoch data, using in-memory database for + /// constructor. + pub fn genesis_epoch_data(&self) -> Result, String> { + use types::transaction::{Action, Transaction}; + + let genesis = self.genesis_header(); + + let factories = Default::default(); + let mut db = journaldb::new( + Arc::new(kvdb_memorydb::create(0)), + journaldb::Algorithm::Archive, + None, + ); + + self.ensure_db_good(BasicBackend(db.as_hash_db_mut()), &factories) + .map_err(|e| format!("Unable to initialize genesis state: {}", e))?; + + let call = |a, d| { + let mut db = db.boxed_clone(); + let env_info = ::evm::EnvInfo { + number: 0, + author: *genesis.author(), + timestamp: genesis.timestamp(), + difficulty: *genesis.difficulty(), + gas_limit: U256::max_value(), + last_hashes: Arc::new(Vec::new()), + gas_used: 0.into(), + }; + + let from = Address::default(); + let tx = Transaction { + nonce: self.engine.account_start_nonce(0), + action: Action::Call(a), + gas: U256::max_value(), + gas_price: U256::default(), + value: U256::default(), + data: d, + } + .fake_sign(from); + + let res = ::state::prove_transaction_virtual( + db.as_hash_db_mut(), + *genesis.state_root(), + &tx, + self.engine.machine(), + &env_info, + factories.clone(), + ); + + res.map(|(out, proof)| (out, proof.into_iter().map(|x| x.into_vec()).collect())) + .ok_or_else(|| "Failed to prove call: insufficient state".into()) + }; + + self.engine.genesis_epoch_data(&genesis, &call) + } + + /// Create a new Spec with InstantSeal consensus which does internal sealing (not requiring + /// work). + pub fn new_instant() -> Spec { + load_bundled!("instant_seal") + } + + /// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a + /// NullEngine consensus. + #[cfg(any(test, feature = "test-helpers"))] + pub fn new_test() -> Spec { + load_bundled!("null_morden") + } + + /// Create the EthereumMachine corresponding to Spec::new_test. + #[cfg(any(test, feature = "test-helpers"))] + pub fn new_test_machine() -> EthereumMachine { + load_machine_bundled!("null_morden") + } + + /// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a NullEngine consensus with applying reward on block close. + #[cfg(any(test, feature = "test-helpers"))] + pub fn new_test_with_reward() -> Spec { + load_bundled!("null_morden_with_reward") + } + + /// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a NullEngine consensus with finality. + #[cfg(any(test, feature = "test-helpers"))] + pub fn new_test_with_finality() -> Spec { + load_bundled!("null_morden_with_finality") + } + + /// Create a new Spec which is a NullEngine consensus with a premine of address whose + /// secret is keccak(''). + #[cfg(any(test, feature = "test-helpers"))] + pub fn new_null() -> Spec { + load_bundled!("null") + } + + /// Create a new Spec which constructs a contract at address 5 with storage at 0 equal to 1. + #[cfg(any(test, feature = "test-helpers"))] + pub fn new_test_constructor() -> Spec { + load_bundled!("constructor") + } + + /// Create a new Spec with AuthorityRound consensus which does internal sealing (not + /// requiring work). + /// Accounts with secrets keccak("0") and keccak("1") are the validators. + #[cfg(any(test, feature = "test-helpers"))] + pub fn new_test_round() -> Self { + load_bundled!("authority_round") + } + + /// Create a new Spec with AuthorityRound consensus which does internal sealing (not + /// requiring work) with empty step messages enabled. + /// Accounts with secrets keccak("0") and keccak("1") are the validators. + #[cfg(any(test, feature = "test-helpers"))] + pub fn new_test_round_empty_steps() -> Self { + load_bundled!("authority_round_empty_steps") + } + + /// Create a new Spec with AuthorityRound consensus (with empty steps) using a block reward + /// contract. The contract source code can be found at: + /// https://github.com/openethereum/block-reward/blob/daf7d44383b6cdb11cb6b953b018648e2b027cfb/contracts/ExampleBlockReward.sol + #[cfg(any(test, feature = "test-helpers"))] + pub fn new_test_round_block_reward_contract() -> Self { + load_bundled!("authority_round_block_reward_contract") + } + + /// TestList.sol used in both specs: https://github.com/paritytech/contracts/pull/30/files (link not valid) + /// Accounts with secrets keccak("0") and keccak("1") are initially the validators. + /// Create a new Spec with BasicAuthority which uses a contract at address 5 to determine + /// the current validators using `getValidators`. + /// Second validator can be removed with + /// "0xbfc708a000000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1" and added + /// back in using + /// "0x4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1". + #[cfg(any(test, feature = "test-helpers"))] + pub fn new_validator_safe_contract() -> Self { + load_bundled!("validator_safe_contract") + } + + /// The same as the `safeContract`, but allows reporting and uses AuthorityRound. + /// Account is marked with `reportBenign` it can be checked as disliked with "0xd8f2e0bf". + /// Validator can be removed with `reportMalicious`. + #[cfg(any(test, feature = "test-helpers"))] + pub fn new_validator_contract() -> Self { + load_bundled!("validator_contract") + } + + /// Create a new Spec with BasicAuthority which uses multiple validator sets changing with + /// height. + /// Account with secrets keccak("0") is the validator for block 1 and with keccak("1") + /// onwards. + #[cfg(any(test, feature = "test-helpers"))] + pub fn new_validator_multi() -> Self { + load_bundled!("validator_multi") + } } #[cfg(test)] mod tests { - use super::*; - use state::State; - use test_helpers::get_temp_state_db; - use tempdir::TempDir; - use types::view; - use types::views::BlockView; - - #[test] - fn test_load_empty() { - let tempdir = TempDir::new("").unwrap(); - assert!(Spec::load(&tempdir.path(), &[] as &[u8]).is_err()); - } - - #[test] - fn test_chain() { - let test_spec = Spec::new_test(); - - assert_eq!( - test_spec.state_root(), - "f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9".into() - ); - let genesis = test_spec.genesis_block(); - assert_eq!( - view!(BlockView, &genesis).header_view().hash(), - "0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303".into() - ); - } - - #[test] - fn genesis_constructor() { - let _ = ::env_logger::try_init(); - let spec = Spec::new_test_constructor(); - let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()) - .unwrap(); - let state = State::from_existing( - db.boxed_clone(), - spec.state_root(), - spec.engine.account_start_nonce(0), - Default::default(), - ).unwrap(); - let expected = "0000000000000000000000000000000000000000000000000000000000000001".into(); - let address = "0000000000000000000000000000000000001337".into(); - - assert_eq!(state.storage_at(&address, &H256::zero()).unwrap(), expected); - assert_eq!(state.balance(&address).unwrap(), 1.into()); - } + use super::*; + use state::State; + use tempdir::TempDir; + use test_helpers::get_temp_state_db; + use types::{view, views::BlockView}; + + #[test] + fn test_load_empty() { + let tempdir = TempDir::new("").unwrap(); + assert!(Spec::load(&tempdir.path(), &[] as &[u8]).is_err()); + } + + #[test] + fn test_chain() { + let test_spec = Spec::new_test(); + + assert_eq!( + test_spec.state_root(), + "f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9".into() + ); + let genesis = test_spec.genesis_block(); + assert_eq!( + view!(BlockView, &genesis).header_view().hash(), + "0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303".into() + ); + } + + #[test] + fn genesis_constructor() { + let _ = ::env_logger::try_init(); + let spec = Spec::new_test_constructor(); + let db = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let state = State::from_existing( + db.boxed_clone(), + spec.state_root(), + spec.engine.account_start_nonce(0), + Default::default(), + ) + .unwrap(); + let expected = "0000000000000000000000000000000000000000000000000000000000000001".into(); + let address = "0000000000000000000000000000000000001337".into(); + + assert_eq!(state.storage_at(&address, &H256::zero()).unwrap(), expected); + assert_eq!(state.balance(&address).unwrap(), 1.into()); + } } diff --git a/ethcore/src/state/account.rs b/ethcore/src/state/account.rs index fea9444b1cc..b6504c1765a 100644 --- a/ethcore/src/state/account.rs +++ b/ethcore/src/state/account.rs @@ -1,744 +1,845 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Single account in the system. -use std::fmt; -use std::sync::Arc; -use std::collections::{HashMap, BTreeMap}; -use hash::{KECCAK_EMPTY, KECCAK_NULL_RLP, keccak}; -use ethereum_types::{H256, U256, Address}; +use bytes::{Bytes, ToPretty}; use error::Error; +use ethereum_types::{Address, H256, U256}; +use ethtrie::{Result as TrieResult, SecTrieDB, TrieDB, TrieFactory}; +use hash::{keccak, KECCAK_EMPTY, KECCAK_NULL_RLP}; use hash_db::HashDB; use keccak_hasher::KeccakHasher; use kvdb::DBValue; -use bytes::{Bytes, ToPretty}; -use trie::{Trie, Recorder}; -use ethtrie::{TrieFactory, TrieDB, SecTrieDB, Result as TrieResult}; -use pod_account::*; -use rlp::{RlpStream, encode}; use lru_cache::LruCache; +use pod_account::*; +use rlp::{encode, RlpStream}; +use std::{ + collections::{BTreeMap, HashMap}, + fmt, + sync::Arc, +}; +use trie::{Recorder, Trie}; use types::basic_account::BasicAccount; -use std::cell::{RefCell, Cell}; +use std::cell::{Cell, RefCell}; const STORAGE_CACHE_ITEMS: usize = 8192; /// Boolean type for clean/dirty status. #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub enum Filth { - /// Data has not been changed. - Clean, - /// Data has been changed. - Dirty, + /// Data has not been changed. + Clean, + /// Data has been changed. + Dirty, } /// Single account in the system. /// Keeps track of changes to the code and storage. /// The changes are applied in `commit_storage` and `commit_code` pub struct Account { - // Balance of the account. - balance: U256, - // Nonce of the account. - nonce: U256, - // Trie-backed storage. - storage_root: H256, - // LRU Cache of the trie-backed storage. - // This is limited to `STORAGE_CACHE_ITEMS` recent queries - storage_cache: RefCell>, - // LRU Cache of the trie-backed storage for original value. - // This is only used when the initial storage root is different compared to - // what is in the database. That is, it is only used for new contracts. - original_storage_cache: Option<(H256, RefCell>)>, - // Modified storage. Accumulates changes to storage made in `set_storage` - // Takes precedence over `storage_cache`. - storage_changes: HashMap, - // Code hash of the account. - code_hash: H256, - // Size of the account code. - code_size: Option, - // Code cache of the account. - code_cache: Arc, - // Account code new or has been modified. - code_filth: Filth, - // Cached address hash. - address_hash: Cell>, + // Balance of the account. + balance: U256, + // Nonce of the account. + nonce: U256, + // Trie-backed storage. + storage_root: H256, + // LRU Cache of the trie-backed storage. + // This is limited to `STORAGE_CACHE_ITEMS` recent queries + storage_cache: RefCell>, + // LRU Cache of the trie-backed storage for original value. + // This is only used when the initial storage root is different compared to + // what is in the database. That is, it is only used for new contracts. + original_storage_cache: Option<(H256, RefCell>)>, + // Modified storage. Accumulates changes to storage made in `set_storage` + // Takes precedence over `storage_cache`. + storage_changes: HashMap, + // Code hash of the account. + code_hash: H256, + // Size of the account code. + code_size: Option, + // Code cache of the account. + code_cache: Arc, + // Account code new or has been modified. + code_filth: Filth, + // Cached address hash. + address_hash: Cell>, } impl From for Account { - fn from(basic: BasicAccount) -> Self { - Account { - balance: basic.balance, - nonce: basic.nonce, - storage_root: basic.storage_root, - storage_cache: Self::empty_storage_cache(), - original_storage_cache: None, - storage_changes: HashMap::new(), - code_hash: basic.code_hash, - code_size: None, - code_cache: Arc::new(vec![]), - code_filth: Filth::Clean, - address_hash: Cell::new(None), - } - } + fn from(basic: BasicAccount) -> Self { + Account { + balance: basic.balance, + nonce: basic.nonce, + storage_root: basic.storage_root, + storage_cache: Self::empty_storage_cache(), + original_storage_cache: None, + storage_changes: HashMap::new(), + code_hash: basic.code_hash, + code_size: None, + code_cache: Arc::new(vec![]), + code_filth: Filth::Clean, + address_hash: Cell::new(None), + } + } } impl Account { - #[cfg(test)] - /// General constructor. - pub fn new(balance: U256, nonce: U256, storage: HashMap, code: Bytes) -> Account { - Account { - balance: balance, - nonce: nonce, - storage_root: KECCAK_NULL_RLP, - storage_cache: Self::empty_storage_cache(), - original_storage_cache: None, - storage_changes: storage, - code_hash: keccak(&code), - code_size: Some(code.len()), - code_cache: Arc::new(code), - code_filth: Filth::Dirty, - address_hash: Cell::new(None), - } - } - - fn empty_storage_cache() -> RefCell> { - RefCell::new(LruCache::new(STORAGE_CACHE_ITEMS)) - } - - /// General constructor. - pub fn from_pod(pod: PodAccount) -> Account { - Account { - balance: pod.balance, - nonce: pod.nonce, - storage_root: KECCAK_NULL_RLP, - storage_cache: Self::empty_storage_cache(), - original_storage_cache: None, - storage_changes: pod.storage.into_iter().collect(), - code_hash: pod.code.as_ref().map_or(KECCAK_EMPTY, |c| keccak(c)), - code_filth: Filth::Dirty, - code_size: Some(pod.code.as_ref().map_or(0, |c| c.len())), - code_cache: Arc::new(pod.code.map_or_else(|| { warn!("POD account with unknown code is being created! Assuming no code."); vec![] }, |c| c)), - address_hash: Cell::new(None), - } - } - - /// Create a new account with the given balance. - pub fn new_basic(balance: U256, nonce: U256) -> Account { - Account { - balance: balance, - nonce: nonce, - storage_root: KECCAK_NULL_RLP, - storage_cache: Self::empty_storage_cache(), - original_storage_cache: None, - storage_changes: HashMap::new(), - code_hash: KECCAK_EMPTY, - code_cache: Arc::new(vec![]), - code_size: Some(0), - code_filth: Filth::Clean, - address_hash: Cell::new(None), - } - } - - /// Create a new account from RLP. - pub fn from_rlp(rlp: &[u8]) -> Result { - ::rlp::decode::(rlp) - .map(|ba| ba.into()) - .map_err(|e| e.into()) - } - - /// Create a new contract account. - /// NOTE: make sure you use `init_code` on this before `commit`ing. - pub fn new_contract(balance: U256, nonce: U256, original_storage_root: H256) -> Account { - Account { - balance: balance, - nonce: nonce, - storage_root: KECCAK_NULL_RLP, - storage_cache: Self::empty_storage_cache(), - original_storage_cache: if original_storage_root == KECCAK_NULL_RLP { - None - } else { - Some((original_storage_root, Self::empty_storage_cache())) - }, - storage_changes: HashMap::new(), - code_hash: KECCAK_EMPTY, - code_cache: Arc::new(vec![]), - code_size: None, - code_filth: Filth::Clean, - address_hash: Cell::new(None), - } - } - - /// Set this account's code to the given code. - /// NOTE: Account should have been created with `new_contract()` - pub fn init_code(&mut self, code: Bytes) { - self.code_hash = keccak(&code); - self.code_cache = Arc::new(code); - self.code_size = Some(self.code_cache.len()); - self.code_filth = Filth::Dirty; - } - - /// Reset this account's code to the given code. - pub fn reset_code(&mut self, code: Bytes) { - self.init_code(code); - } - - /// Reset this account's code and storage to given values. - pub fn reset_code_and_storage(&mut self, code: Arc, storage: HashMap) { - self.code_hash = keccak(&*code); - self.code_cache = code; - self.code_size = Some(self.code_cache.len()); - self.code_filth = Filth::Dirty; - self.storage_cache = Self::empty_storage_cache(); - self.storage_changes = storage; - if self.storage_root != KECCAK_NULL_RLP { - self.original_storage_cache = Some((self.storage_root, Self::empty_storage_cache())); - } - self.storage_root = KECCAK_NULL_RLP; - } - - /// Set (and cache) the contents of the trie's storage at `key` to `value`. - pub fn set_storage(&mut self, key: H256, value: H256) { - self.storage_changes.insert(key, value); - } - - /// Get (and cache) the contents of the trie's storage at `key`. - /// Takes modified storage into account. - pub fn storage_at(&self, db: &HashDB, key: &H256) -> TrieResult { - if let Some(value) = self.cached_storage_at(key) { - return Ok(value); - } - Self::get_and_cache_storage( - &self.storage_root, - &mut self.storage_cache.borrow_mut(), - db, - key) - } - - /// Get (and cache) the contents of the trie's storage at `key`. - /// Does not take modified storage into account. - pub fn original_storage_at(&self, db: &HashDB, key: &H256) -> TrieResult { - if let Some(value) = self.cached_original_storage_at(key) { - return Ok(value); - } - match &self.original_storage_cache { - Some((ref original_storage_root, ref original_storage_cache)) => - Self::get_and_cache_storage( - original_storage_root, - &mut original_storage_cache.borrow_mut(), - db, - key - ), - None => - Self::get_and_cache_storage( - &self.storage_root, - &mut self.storage_cache.borrow_mut(), - db, - key - ), - } - } - - fn get_and_cache_storage(storage_root: &H256, storage_cache: &mut LruCache, db: &HashDB, key: &H256) -> TrieResult { - let db = SecTrieDB::new(&db, storage_root)?; - let panicky_decoder = |bytes:&[u8]| ::rlp::decode(&bytes).expect("decoding db value failed"); - let item: U256 = db.get_with(key, panicky_decoder)?.unwrap_or_else(U256::zero); - let value: H256 = item.into(); - storage_cache.insert(key.clone(), value.clone()); - Ok(value) - } - - /// Get cached storage value if any. Returns `None` if the - /// key is not in the cache. - pub fn cached_storage_at(&self, key: &H256) -> Option { - if let Some(value) = self.storage_changes.get(key) { - return Some(value.clone()) - } - self.cached_moved_original_storage_at(key) - } - - /// Get cached original storage value after last state commitment. Returns `None` if the key is not in the cache. - pub fn cached_original_storage_at(&self, key: &H256) -> Option { - match &self.original_storage_cache { - Some((_, ref original_storage_cache)) => { - if let Some(value) = original_storage_cache.borrow_mut().get_mut(key) { - Some(value.clone()) - } else { - None - } - }, - None => { - self.cached_moved_original_storage_at(key) - }, - } - } - - /// Get cached original storage value since last contract creation on this address. Returns `None` if the key is not in the cache. - fn cached_moved_original_storage_at(&self, key: &H256) -> Option { - // If storage root is empty RLP, then early return zero value. Practically, this makes it so that if - // `original_storage_cache` is used, then `storage_cache` will always remain empty. - if self.storage_root == KECCAK_NULL_RLP { - return Some(H256::new()); - } - - if let Some(value) = self.storage_cache.borrow_mut().get_mut(key) { - Some(value.clone()) - } else { - None - } - } - - /// return the balance associated with this account. - pub fn balance(&self) -> &U256 { &self.balance } - - /// return the nonce associated with this account. - pub fn nonce(&self) -> &U256 { &self.nonce } - - /// return the code hash associated with this account. - pub fn code_hash(&self) -> H256 { - self.code_hash.clone() - } - - /// return and cache `keccak(address)`, `address` must be the address of this - /// account. - pub fn address_hash(&self, address: &Address) -> H256 { - let hash = self.address_hash.get(); - hash.unwrap_or_else(|| { - let hash = keccak(address); - self.address_hash.set(Some(hash.clone())); - hash - }) - } - - /// returns the account's code. If `None` then the code cache isn't available - - /// get someone who knows to call `note_code`. - pub fn code(&self) -> Option> { - if self.code_hash != KECCAK_EMPTY && self.code_cache.is_empty() { - return None; - } - Some(self.code_cache.clone()) - } - - /// returns the account's code size. If `None` then the code cache or code size cache isn't available - - /// get someone who knows to call `note_code`. - pub fn code_size(&self) -> Option { - self.code_size.clone() - } - - #[cfg(test)] - /// Provide a byte array which hashes to the `code_hash`. returns the hash as a result. - pub fn note_code(&mut self, code: Bytes) -> Result<(), H256> { - let h = keccak(&code); - if self.code_hash == h { - self.code_cache = Arc::new(code); - self.code_size = Some(self.code_cache.len()); - Ok(()) - } else { - Err(h) - } - } - - /// Is `code_cache` valid; such that code is going to return Some? - pub fn is_cached(&self) -> bool { - !self.code_cache.is_empty() || (self.code_cache.is_empty() && self.code_hash == KECCAK_EMPTY) - } - - /// Provide a database to get `code_hash`. Should not be called if it is a contract without code. Returns the cached code, if successful. - #[must_use] - pub fn cache_code(&mut self, db: &HashDB) -> Option> { - // TODO: fill out self.code_cache; - trace!("Account::cache_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty()); - - if self.is_cached() { return Some(self.code_cache.clone()); } - - match db.get(&self.code_hash) { - Some(x) => { - self.code_size = Some(x.len()); - self.code_cache = Arc::new(x.into_vec()); - Some(self.code_cache.clone()) - }, - _ => { - warn!("Failed reverse get of {}", self.code_hash); - None - }, - } - } - - /// Provide code to cache. For correctness, should be the correct code for the account. - pub fn cache_given_code(&mut self, code: Arc) { - trace!("Account::cache_given_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty()); - - self.code_size = Some(code.len()); - self.code_cache = code; - } - - /// Provide a database to get `code_size`. Should not be called if it is a contract without code. Returns whether - /// the cache succeeds. - #[must_use] - pub fn cache_code_size(&mut self, db: &HashDB) -> bool { - // TODO: fill out self.code_cache; - trace!("Account::cache_code_size: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty()); - self.code_size.is_some() || - if self.code_hash != KECCAK_EMPTY { - match db.get(&self.code_hash) { - Some(x) => { - self.code_size = Some(x.len()); - true - }, - _ => { - warn!("Failed reverse get of {}", self.code_hash); - false - }, - } - } else { - // If the code hash is empty hash, then the code size is zero. - self.code_size = Some(0); - true - } - } - - /// Determine whether there are any un-`commit()`-ed storage-setting operations. - pub fn storage_is_clean(&self) -> bool { self.storage_changes.is_empty() } - - /// Check if account has zero nonce, balance, no code and no storage. - /// - /// NOTE: Will panic if `!self.storage_is_clean()` - pub fn is_empty(&self) -> bool { - assert!(self.storage_is_clean(), "Account::is_empty() may only legally be called when storage is clean."); - self.is_null() && self.storage_root == KECCAK_NULL_RLP - } - - /// Check if account has zero nonce, balance, no code. - pub fn is_null(&self) -> bool { - self.balance.is_zero() && - self.nonce.is_zero() && - self.code_hash == KECCAK_EMPTY - } - - /// Check if account is basic (Has no code). - pub fn is_basic(&self) -> bool { - self.code_hash == KECCAK_EMPTY - } - - /// Return the storage root associated with this account or None if it has been altered via the overlay. - pub fn storage_root(&self) -> Option { - if self.storage_is_clean() { - Some(self.storage_root) - } else { - None - } - } - - /// Return the original storage root of this account. - pub fn original_storage_root(&self) -> H256 { - if let Some((original_storage_root, _)) = self.original_storage_cache { - original_storage_root - } else { - self.storage_root - } - } - - /// Whether the base storage root of this account is unchanged. - pub fn is_base_storage_root_unchanged(&self) -> bool { - self.original_storage_cache.is_none() - } - - /// Storage root where the account changes are based upon. - pub fn base_storage_root(&self) -> H256 { - self.storage_root - } - - /// Return the storage overlay. - pub fn storage_changes(&self) -> &HashMap { &self.storage_changes } - - /// Increment the nonce of the account by one. - pub fn inc_nonce(&mut self) { - self.nonce = self.nonce.saturating_add(U256::from(1u8)); - } - - /// Increase account balance. - pub fn add_balance(&mut self, x: &U256) { - self.balance = self.balance.saturating_add(*x); - } - - /// Decrease account balance. - /// Panics if balance is less than `x` - pub fn sub_balance(&mut self, x: &U256) { - assert!(self.balance >= *x); - self.balance = self.balance - *x; - } - - /// Commit the `storage_changes` to the backing DB and update `storage_root`. - pub fn commit_storage(&mut self, trie_factory: &TrieFactory, db: &mut HashDB) -> TrieResult<()> { - let mut t = trie_factory.from_existing(db, &mut self.storage_root)?; - for (k, v) in self.storage_changes.drain() { - // cast key and value to trait type, - // so we can call overloaded `to_bytes` method - match v.is_zero() { - true => t.remove(&k)?, - false => t.insert(&k, &encode(&U256::from(&*v)))?, - }; - - self.storage_cache.borrow_mut().insert(k, v); - } - self.original_storage_cache = None; - Ok(()) - } - - /// Commit any unsaved code. `code_hash` will always return the hash of the `code_cache` after this. - pub fn commit_code(&mut self, db: &mut HashDB) { - trace!("Commiting code of {:?} - {:?}, {:?}", self, self.code_filth == Filth::Dirty, self.code_cache.is_empty()); - match (self.code_filth == Filth::Dirty, self.code_cache.is_empty()) { - (true, true) => { - self.code_size = Some(0); - self.code_filth = Filth::Clean; - }, - (true, false) => { - db.emplace(self.code_hash.clone(), DBValue::from_slice(&*self.code_cache)); - self.code_size = Some(self.code_cache.len()); - self.code_filth = Filth::Clean; - }, - (false, _) => {}, - } - } - - /// Export to RLP. - pub fn rlp(&self) -> Bytes { - let mut stream = RlpStream::new_list(4); - stream.append(&self.nonce); - stream.append(&self.balance); - stream.append(&self.storage_root); - stream.append(&self.code_hash); - stream.out() - } - - /// Clone basic account data - pub fn clone_basic(&self) -> Account { - Account { - balance: self.balance.clone(), - nonce: self.nonce.clone(), - storage_root: self.storage_root.clone(), - storage_cache: Self::empty_storage_cache(), - original_storage_cache: self.original_storage_cache.as_ref().map(|(r, _)| (*r, Self::empty_storage_cache())), - storage_changes: HashMap::new(), - code_hash: self.code_hash.clone(), - code_size: self.code_size.clone(), - code_cache: self.code_cache.clone(), - code_filth: self.code_filth, - address_hash: self.address_hash.clone(), - } - } - - /// Clone account data and dirty storage keys - pub fn clone_dirty(&self) -> Account { - let mut account = self.clone_basic(); - account.storage_changes = self.storage_changes.clone(); - account - } - - /// Clone account data, dirty storage keys and cached storage keys. - pub fn clone_all(&self) -> Account { - let mut account = self.clone_dirty(); - account.storage_cache = self.storage_cache.clone(); - account.original_storage_cache = self.original_storage_cache.clone(); - account - } - - /// Replace self with the data from other account merging storage cache. - /// Basic account data and all modifications are overwritten - /// with new values. - pub fn overwrite_with(&mut self, other: Account) { - self.balance = other.balance; - self.nonce = other.nonce; - self.code_hash = other.code_hash; - self.code_filth = other.code_filth; - self.code_cache = other.code_cache; - self.code_size = other.code_size; - self.address_hash = other.address_hash; - if self.storage_root == other.storage_root { - let mut cache = self.storage_cache.borrow_mut(); - for (k, v) in other.storage_cache.into_inner() { - cache.insert(k, v); - } - } else { - self.storage_cache = other.storage_cache; - } - self.original_storage_cache = other.original_storage_cache; - self.storage_root = other.storage_root; - self.storage_changes = other.storage_changes; - } + #[cfg(test)] + /// General constructor. + pub fn new(balance: U256, nonce: U256, storage: HashMap, code: Bytes) -> Account { + Account { + balance: balance, + nonce: nonce, + storage_root: KECCAK_NULL_RLP, + storage_cache: Self::empty_storage_cache(), + original_storage_cache: None, + storage_changes: storage, + code_hash: keccak(&code), + code_size: Some(code.len()), + code_cache: Arc::new(code), + code_filth: Filth::Dirty, + address_hash: Cell::new(None), + } + } + + fn empty_storage_cache() -> RefCell> { + RefCell::new(LruCache::new(STORAGE_CACHE_ITEMS)) + } + + /// General constructor. + pub fn from_pod(pod: PodAccount) -> Account { + Account { + balance: pod.balance, + nonce: pod.nonce, + storage_root: KECCAK_NULL_RLP, + storage_cache: Self::empty_storage_cache(), + original_storage_cache: None, + storage_changes: pod.storage.into_iter().collect(), + code_hash: pod.code.as_ref().map_or(KECCAK_EMPTY, |c| keccak(c)), + code_filth: Filth::Dirty, + code_size: Some(pod.code.as_ref().map_or(0, |c| c.len())), + code_cache: Arc::new(pod.code.map_or_else( + || { + warn!("POD account with unknown code is being created! Assuming no code."); + vec![] + }, + |c| c, + )), + address_hash: Cell::new(None), + } + } + + /// Create a new account with the given balance. + pub fn new_basic(balance: U256, nonce: U256) -> Account { + Account { + balance: balance, + nonce: nonce, + storage_root: KECCAK_NULL_RLP, + storage_cache: Self::empty_storage_cache(), + original_storage_cache: None, + storage_changes: HashMap::new(), + code_hash: KECCAK_EMPTY, + code_cache: Arc::new(vec![]), + code_size: Some(0), + code_filth: Filth::Clean, + address_hash: Cell::new(None), + } + } + + /// Create a new account from RLP. + pub fn from_rlp(rlp: &[u8]) -> Result { + ::rlp::decode::(rlp) + .map(|ba| ba.into()) + .map_err(|e| e.into()) + } + + /// Create a new contract account. + /// NOTE: make sure you use `init_code` on this before `commit`ing. + pub fn new_contract(balance: U256, nonce: U256, original_storage_root: H256) -> Account { + Account { + balance: balance, + nonce: nonce, + storage_root: KECCAK_NULL_RLP, + storage_cache: Self::empty_storage_cache(), + original_storage_cache: if original_storage_root == KECCAK_NULL_RLP { + None + } else { + Some((original_storage_root, Self::empty_storage_cache())) + }, + storage_changes: HashMap::new(), + code_hash: KECCAK_EMPTY, + code_cache: Arc::new(vec![]), + code_size: None, + code_filth: Filth::Clean, + address_hash: Cell::new(None), + } + } + + /// Set this account's code to the given code. + /// NOTE: Account should have been created with `new_contract()` + pub fn init_code(&mut self, code: Bytes) { + self.code_hash = keccak(&code); + self.code_cache = Arc::new(code); + self.code_size = Some(self.code_cache.len()); + self.code_filth = Filth::Dirty; + } + + /// Reset this account's code to the given code. + pub fn reset_code(&mut self, code: Bytes) { + self.init_code(code); + } + + /// Reset this account's code and storage to given values. + pub fn reset_code_and_storage(&mut self, code: Arc, storage: HashMap) { + self.code_hash = keccak(&*code); + self.code_cache = code; + self.code_size = Some(self.code_cache.len()); + self.code_filth = Filth::Dirty; + self.storage_cache = Self::empty_storage_cache(); + self.storage_changes = storage; + if self.storage_root != KECCAK_NULL_RLP { + self.original_storage_cache = Some((self.storage_root, Self::empty_storage_cache())); + } + self.storage_root = KECCAK_NULL_RLP; + } + + /// Set (and cache) the contents of the trie's storage at `key` to `value`. + pub fn set_storage(&mut self, key: H256, value: H256) { + self.storage_changes.insert(key, value); + } + + /// Get (and cache) the contents of the trie's storage at `key`. + /// Takes modified storage into account. + pub fn storage_at( + &self, + db: &dyn HashDB, + key: &H256, + ) -> TrieResult { + if let Some(value) = self.cached_storage_at(key) { + return Ok(value); + } + Self::get_and_cache_storage( + &self.storage_root, + &mut self.storage_cache.borrow_mut(), + db, + key, + ) + } + + /// Get (and cache) the contents of the trie's storage at `key`. + /// Does not take modified storage into account. + pub fn original_storage_at( + &self, + db: &dyn HashDB, + key: &H256, + ) -> TrieResult { + if let Some(value) = self.cached_original_storage_at(key) { + return Ok(value); + } + match &self.original_storage_cache { + Some((ref original_storage_root, ref original_storage_cache)) => { + Self::get_and_cache_storage( + original_storage_root, + &mut original_storage_cache.borrow_mut(), + db, + key, + ) + } + None => Self::get_and_cache_storage( + &self.storage_root, + &mut self.storage_cache.borrow_mut(), + db, + key, + ), + } + } + + fn get_and_cache_storage( + storage_root: &H256, + storage_cache: &mut LruCache, + db: &dyn HashDB, + key: &H256, + ) -> TrieResult { + let db = SecTrieDB::new(&db, storage_root)?; + let panicky_decoder = + |bytes: &[u8]| ::rlp::decode(&bytes).expect("decoding db value failed"); + let item: U256 = db + .get_with(key, panicky_decoder)? + .unwrap_or_else(U256::zero); + let value: H256 = item.into(); + storage_cache.insert(key.clone(), value.clone()); + Ok(value) + } + + /// Get cached storage value if any. Returns `None` if the + /// key is not in the cache. + pub fn cached_storage_at(&self, key: &H256) -> Option { + if let Some(value) = self.storage_changes.get(key) { + return Some(value.clone()); + } + self.cached_moved_original_storage_at(key) + } + + /// Get cached original storage value after last state commitment. Returns `None` if the key is not in the cache. + pub fn cached_original_storage_at(&self, key: &H256) -> Option { + match &self.original_storage_cache { + Some((_, ref original_storage_cache)) => { + if let Some(value) = original_storage_cache.borrow_mut().get_mut(key) { + Some(value.clone()) + } else { + None + } + } + None => self.cached_moved_original_storage_at(key), + } + } + + /// Get cached original storage value since last contract creation on this address. Returns `None` if the key is not in the cache. + fn cached_moved_original_storage_at(&self, key: &H256) -> Option { + // If storage root is empty RLP, then early return zero value. Practically, this makes it so that if + // `original_storage_cache` is used, then `storage_cache` will always remain empty. + if self.storage_root == KECCAK_NULL_RLP { + return Some(H256::new()); + } + + if let Some(value) = self.storage_cache.borrow_mut().get_mut(key) { + Some(value.clone()) + } else { + None + } + } + + /// return the balance associated with this account. + pub fn balance(&self) -> &U256 { + &self.balance + } + + /// return the nonce associated with this account. + pub fn nonce(&self) -> &U256 { + &self.nonce + } + + /// return the code hash associated with this account. + pub fn code_hash(&self) -> H256 { + self.code_hash.clone() + } + + /// return and cache `keccak(address)`, `address` must be the address of this + /// account. + pub fn address_hash(&self, address: &Address) -> H256 { + let hash = self.address_hash.get(); + hash.unwrap_or_else(|| { + let hash = keccak(address); + self.address_hash.set(Some(hash.clone())); + hash + }) + } + + /// returns the account's code. If `None` then the code cache isn't available - + /// get someone who knows to call `note_code`. + pub fn code(&self) -> Option> { + if self.code_hash != KECCAK_EMPTY && self.code_cache.is_empty() { + return None; + } + Some(self.code_cache.clone()) + } + + /// returns the account's code size. If `None` then the code cache or code size cache isn't available - + /// get someone who knows to call `note_code`. + pub fn code_size(&self) -> Option { + self.code_size.clone() + } + + #[cfg(test)] + /// Provide a byte array which hashes to the `code_hash`. returns the hash as a result. + pub fn note_code(&mut self, code: Bytes) -> Result<(), H256> { + let h = keccak(&code); + if self.code_hash == h { + self.code_cache = Arc::new(code); + self.code_size = Some(self.code_cache.len()); + Ok(()) + } else { + Err(h) + } + } + + /// Is `code_cache` valid; such that code is going to return Some? + pub fn is_cached(&self) -> bool { + !self.code_cache.is_empty() + || (self.code_cache.is_empty() && self.code_hash == KECCAK_EMPTY) + } + + /// Provide a database to get `code_hash`. Should not be called if it is a contract without code. Returns the cached code, if successful. + #[must_use] + pub fn cache_code(&mut self, db: &dyn HashDB) -> Option> { + // TODO: fill out self.code_cache; + trace!( + "Account::cache_code: ic={}; self.code_hash={:?}, self.code_cache={}", + self.is_cached(), + self.code_hash, + self.code_cache.pretty() + ); + + if self.is_cached() { + return Some(self.code_cache.clone()); + } + + match db.get(&self.code_hash) { + Some(x) => { + self.code_size = Some(x.len()); + self.code_cache = Arc::new(x.into_vec()); + Some(self.code_cache.clone()) + } + _ => { + warn!("Failed reverse get of {}", self.code_hash); + None + } + } + } + + /// Provide code to cache. For correctness, should be the correct code for the account. + pub fn cache_given_code(&mut self, code: Arc) { + trace!( + "Account::cache_given_code: ic={}; self.code_hash={:?}, self.code_cache={}", + self.is_cached(), + self.code_hash, + self.code_cache.pretty() + ); + + self.code_size = Some(code.len()); + self.code_cache = code; + } + + /// Provide a database to get `code_size`. Should not be called if it is a contract without code. Returns whether + /// the cache succeeds. + #[must_use] + pub fn cache_code_size(&mut self, db: &dyn HashDB) -> bool { + // TODO: fill out self.code_cache; + trace!( + "Account::cache_code_size: ic={}; self.code_hash={:?}, self.code_cache={}", + self.is_cached(), + self.code_hash, + self.code_cache.pretty() + ); + self.code_size.is_some() + || if self.code_hash != KECCAK_EMPTY { + match db.get(&self.code_hash) { + Some(x) => { + self.code_size = Some(x.len()); + true + } + _ => { + warn!("Failed reverse get of {}", self.code_hash); + false + } + } + } else { + // If the code hash is empty hash, then the code size is zero. + self.code_size = Some(0); + true + } + } + + /// Determine whether there are any un-`commit()`-ed storage-setting operations. + pub fn storage_is_clean(&self) -> bool { + self.storage_changes.is_empty() + } + + /// Check if account has zero nonce, balance, no code and no storage. + /// + /// NOTE: Will panic if `!self.storage_is_clean()` + pub fn is_empty(&self) -> bool { + assert!( + self.storage_is_clean(), + "Account::is_empty() may only legally be called when storage is clean." + ); + self.is_null() && self.storage_root == KECCAK_NULL_RLP + } + + /// Check if account has zero nonce, balance, no code. + pub fn is_null(&self) -> bool { + self.balance.is_zero() && self.nonce.is_zero() && self.code_hash == KECCAK_EMPTY + } + + /// Check if account is basic (Has no code). + pub fn is_basic(&self) -> bool { + self.code_hash == KECCAK_EMPTY + } + + /// Return the storage root associated with this account or None if it has been altered via the overlay. + pub fn storage_root(&self) -> Option { + if self.storage_is_clean() { + Some(self.storage_root) + } else { + None + } + } + + /// Return the original storage root of this account. + pub fn original_storage_root(&self) -> H256 { + if let Some((original_storage_root, _)) = self.original_storage_cache { + original_storage_root + } else { + self.storage_root + } + } + + /// Whether the base storage root of this account is unchanged. + pub fn is_base_storage_root_unchanged(&self) -> bool { + self.original_storage_cache.is_none() + } + + /// Storage root where the account changes are based upon. + pub fn base_storage_root(&self) -> H256 { + self.storage_root + } + + /// Return the storage overlay. + pub fn storage_changes(&self) -> &HashMap { + &self.storage_changes + } + + /// Increment the nonce of the account by one. + pub fn inc_nonce(&mut self) { + self.nonce = self.nonce.saturating_add(U256::from(1u8)); + } + + /// Increase account balance. + pub fn add_balance(&mut self, x: &U256) { + self.balance = self.balance.saturating_add(*x); + } + + /// Decrease account balance. + /// Panics if balance is less than `x` + pub fn sub_balance(&mut self, x: &U256) { + assert!(self.balance >= *x); + self.balance = self.balance - *x; + } + + /// Commit the `storage_changes` to the backing DB and update `storage_root`. + pub fn commit_storage( + &mut self, + trie_factory: &TrieFactory, + db: &mut dyn HashDB, + ) -> TrieResult<()> { + let mut t = trie_factory.from_existing(db, &mut self.storage_root)?; + for (k, v) in self.storage_changes.drain() { + // cast key and value to trait type, + // so we can call overloaded `to_bytes` method + match v.is_zero() { + true => t.remove(&k)?, + false => t.insert(&k, &encode(&U256::from(&*v)))?, + }; + + self.storage_cache.borrow_mut().insert(k, v); + } + self.original_storage_cache = None; + Ok(()) + } + + /// Commit any unsaved code. `code_hash` will always return the hash of the `code_cache` after this. + pub fn commit_code(&mut self, db: &mut dyn HashDB) { + trace!( + "Commiting code of {:?} - {:?}, {:?}", + self, + self.code_filth == Filth::Dirty, + self.code_cache.is_empty() + ); + match (self.code_filth == Filth::Dirty, self.code_cache.is_empty()) { + (true, true) => { + self.code_size = Some(0); + self.code_filth = Filth::Clean; + } + (true, false) => { + db.emplace( + self.code_hash.clone(), + DBValue::from_slice(&*self.code_cache), + ); + self.code_size = Some(self.code_cache.len()); + self.code_filth = Filth::Clean; + } + (false, _) => {} + } + } + + /// Export to RLP. + pub fn rlp(&self) -> Bytes { + let mut stream = RlpStream::new_list(4); + stream.append(&self.nonce); + stream.append(&self.balance); + stream.append(&self.storage_root); + stream.append(&self.code_hash); + stream.out() + } + + /// Clone basic account data + pub fn clone_basic(&self) -> Account { + Account { + balance: self.balance.clone(), + nonce: self.nonce.clone(), + storage_root: self.storage_root.clone(), + storage_cache: Self::empty_storage_cache(), + original_storage_cache: self + .original_storage_cache + .as_ref() + .map(|(r, _)| (*r, Self::empty_storage_cache())), + storage_changes: HashMap::new(), + code_hash: self.code_hash.clone(), + code_size: self.code_size.clone(), + code_cache: self.code_cache.clone(), + code_filth: self.code_filth, + address_hash: self.address_hash.clone(), + } + } + + /// Clone account data and dirty storage keys + pub fn clone_dirty(&self) -> Account { + let mut account = self.clone_basic(); + account.storage_changes = self.storage_changes.clone(); + account + } + + /// Clone account data, dirty storage keys and cached storage keys. + pub fn clone_all(&self) -> Account { + let mut account = self.clone_dirty(); + account.storage_cache = self.storage_cache.clone(); + account.original_storage_cache = self.original_storage_cache.clone(); + account + } + + /// Replace self with the data from other account merging storage cache. + /// Basic account data and all modifications are overwritten + /// with new values. + pub fn overwrite_with(&mut self, other: Account) { + self.balance = other.balance; + self.nonce = other.nonce; + self.code_hash = other.code_hash; + self.code_filth = other.code_filth; + self.code_cache = other.code_cache; + self.code_size = other.code_size; + self.address_hash = other.address_hash; + if self.storage_root == other.storage_root { + let mut cache = self.storage_cache.borrow_mut(); + for (k, v) in other.storage_cache.into_inner() { + cache.insert(k, v); + } + } else { + self.storage_cache = other.storage_cache; + } + self.original_storage_cache = other.original_storage_cache; + self.storage_root = other.storage_root; + self.storage_changes = other.storage_changes; + } } // light client storage proof. impl Account { - /// Prove a storage key's existence or nonexistence in the account's storage - /// trie. - /// `storage_key` is the hash of the desired storage key, meaning - /// this will only work correctly under a secure trie. - pub fn prove_storage(&self, db: &HashDB, storage_key: H256) -> TrieResult<(Vec, H256)> { - let mut recorder = Recorder::new(); - - let trie = TrieDB::new(&db, &self.storage_root)?; - let item: U256 = { - let panicky_decoder = |bytes:&[u8]| ::rlp::decode(bytes).expect("decoding db value failed"); - let query = (&mut recorder, panicky_decoder); - trie.get_with(&storage_key, query)?.unwrap_or_else(U256::zero) - }; - - Ok((recorder.drain().into_iter().map(|r| r.data).collect(), item.into())) - } + /// Prove a storage key's existence or nonexistence in the account's storage + /// trie. + /// `storage_key` is the hash of the desired storage key, meaning + /// this will only work correctly under a secure trie. + pub fn prove_storage( + &self, + db: &dyn HashDB, + storage_key: H256, + ) -> TrieResult<(Vec, H256)> { + let mut recorder = Recorder::new(); + + let trie = TrieDB::new(&db, &self.storage_root)?; + let item: U256 = { + let panicky_decoder = + |bytes: &[u8]| ::rlp::decode(bytes).expect("decoding db value failed"); + let query = (&mut recorder, panicky_decoder); + trie.get_with(&storage_key, query)? + .unwrap_or_else(U256::zero) + }; + + Ok(( + recorder.drain().into_iter().map(|r| r.data).collect(), + item.into(), + )) + } } impl fmt::Debug for Account { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Account") - .field("balance", &self.balance) - .field("nonce", &self.nonce) - .field("code", &self.code()) - .field("storage", &self.storage_changes.iter().collect::>()) - .finish() - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Account") + .field("balance", &self.balance) + .field("nonce", &self.nonce) + .field("code", &self.code()) + .field( + "storage", + &self.storage_changes.iter().collect::>(), + ) + .finish() + } } #[cfg(test)] mod tests { - use rlp_compress::{compress, decompress, snapshot_swapper}; - use ethereum_types::{H256, Address}; - use journaldb::new_memory_db; - use bytes::Bytes; - use super::*; - use account_db::*; - - #[test] - fn account_compress() { - let raw = Account::new_basic(2.into(), 4.into()).rlp(); - let compact_vec = compress(&raw, snapshot_swapper()); - assert!(raw.len() > compact_vec.len()); - let again_raw = decompress(&compact_vec, snapshot_swapper()); - assert_eq!(raw, again_raw.into_vec()); - } - - #[test] - fn storage_at() { - let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::new()); - let rlp = { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); - a.set_storage(0x00u64.into(), 0x1234u64.into()); - a.commit_storage(&Default::default(), &mut db).unwrap(); - a.init_code(vec![]); - a.commit_code(&mut db); - a.rlp() - }; - - let a = Account::from_rlp(&rlp).expect("decoding db value failed"); - assert_eq!(a.storage_root().unwrap(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2".into()); - assert_eq!(a.storage_at(&db.immutable(), &0x00u64.into()).unwrap(), 0x1234u64.into()); - assert_eq!(a.storage_at(&db.immutable(), &0x01u64.into()).unwrap(), H256::default()); - } - - #[test] - fn note_code() { - let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::new()); - - let rlp = { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); - a.init_code(vec![0x55, 0x44, 0xffu8]); - a.commit_code(&mut db); - a.rlp() - }; - - let mut a = Account::from_rlp(&rlp).expect("decoding db value failed"); - assert!(a.cache_code(&db.immutable()).is_some()); - - let mut a = Account::from_rlp(&rlp).expect("decoding db value failed"); - assert_eq!(a.note_code(vec![0x55, 0x44, 0xffu8]), Ok(())); - } - - #[test] - fn commit_storage() { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); - let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::new()); - a.set_storage(0.into(), 0x1234.into()); - assert_eq!(a.storage_root(), None); - a.commit_storage(&Default::default(), &mut db).unwrap(); - assert_eq!(a.storage_root().unwrap(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2".into()); - } - - #[test] - fn commit_remove_commit_storage() { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); - let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::new()); - a.set_storage(0.into(), 0x1234.into()); - a.commit_storage(&Default::default(), &mut db).unwrap(); - a.set_storage(1.into(), 0x1234.into()); - a.commit_storage(&Default::default(), &mut db).unwrap(); - a.set_storage(1.into(), 0.into()); - a.commit_storage(&Default::default(), &mut db).unwrap(); - assert_eq!(a.storage_root().unwrap(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2".into()); - } - - #[test] - fn commit_code() { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); - let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::new()); - a.init_code(vec![0x55, 0x44, 0xffu8]); - assert_eq!(a.code_filth, Filth::Dirty); - assert_eq!(a.code_size(), Some(3)); - a.commit_code(&mut db); - assert_eq!(a.code_hash(), "af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb".into()); - } - - #[test] - fn reset_code() { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); - let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::new()); - a.init_code(vec![0x55, 0x44, 0xffu8]); - assert_eq!(a.code_filth, Filth::Dirty); - a.commit_code(&mut db); - assert_eq!(a.code_filth, Filth::Clean); - assert_eq!(a.code_hash(), "af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb".into()); - a.reset_code(vec![0x55]); - assert_eq!(a.code_filth, Filth::Dirty); - a.commit_code(&mut db); - assert_eq!(a.code_hash(), "37bf2238b11b68cdc8382cece82651b59d3c3988873b6e0f33d79694aa45f1be".into()); - } - - #[test] - fn rlpio() { - let a = Account::new(69u8.into(), 0u8.into(), HashMap::new(), Bytes::new()); - let b = Account::from_rlp(&a.rlp()).unwrap(); - assert_eq!(a.balance(), b.balance()); - assert_eq!(a.nonce(), b.nonce()); - assert_eq!(a.code_hash(), b.code_hash()); - assert_eq!(a.storage_root(), b.storage_root()); - } - - #[test] - fn new_account() { - let a = Account::new(69u8.into(), 0u8.into(), HashMap::new(), Bytes::new()); - assert_eq!(a.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); - assert_eq!(*a.balance(), 69u8.into()); - assert_eq!(*a.nonce(), 0u8.into()); - assert_eq!(a.code_hash(), KECCAK_EMPTY); - assert_eq!(a.storage_root().unwrap(), KECCAK_NULL_RLP); - } + use super::*; + use account_db::*; + use bytes::Bytes; + use ethereum_types::{Address, H256}; + use journaldb::new_memory_db; + use rlp_compress::{compress, decompress, snapshot_swapper}; + + #[test] + fn account_compress() { + let raw = Account::new_basic(2.into(), 4.into()).rlp(); + let compact_vec = compress(&raw, snapshot_swapper()); + assert!(raw.len() > compact_vec.len()); + let again_raw = decompress(&compact_vec, snapshot_swapper()); + assert_eq!(raw, again_raw.into_vec()); + } + + #[test] + fn storage_at() { + let mut db = new_memory_db(); + let mut db = AccountDBMut::new(&mut db, &Address::new()); + let rlp = { + let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + a.set_storage(0x00u64.into(), 0x1234u64.into()); + a.commit_storage(&Default::default(), &mut db).unwrap(); + a.init_code(vec![]); + a.commit_code(&mut db); + a.rlp() + }; + + let a = Account::from_rlp(&rlp).expect("decoding db value failed"); + assert_eq!( + a.storage_root().unwrap(), + "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2".into() + ); + assert_eq!( + a.storage_at(&db.immutable(), &0x00u64.into()).unwrap(), + 0x1234u64.into() + ); + assert_eq!( + a.storage_at(&db.immutable(), &0x01u64.into()).unwrap(), + H256::default() + ); + } + + #[test] + fn note_code() { + let mut db = new_memory_db(); + let mut db = AccountDBMut::new(&mut db, &Address::new()); + + let rlp = { + let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + a.init_code(vec![0x55, 0x44, 0xffu8]); + a.commit_code(&mut db); + a.rlp() + }; + + let mut a = Account::from_rlp(&rlp).expect("decoding db value failed"); + assert!(a.cache_code(&db.immutable()).is_some()); + + let mut a = Account::from_rlp(&rlp).expect("decoding db value failed"); + assert_eq!(a.note_code(vec![0x55, 0x44, 0xffu8]), Ok(())); + } + + #[test] + fn commit_storage() { + let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + let mut db = new_memory_db(); + let mut db = AccountDBMut::new(&mut db, &Address::new()); + a.set_storage(0.into(), 0x1234.into()); + assert_eq!(a.storage_root(), None); + a.commit_storage(&Default::default(), &mut db).unwrap(); + assert_eq!( + a.storage_root().unwrap(), + "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2".into() + ); + } + + #[test] + fn commit_remove_commit_storage() { + let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + let mut db = new_memory_db(); + let mut db = AccountDBMut::new(&mut db, &Address::new()); + a.set_storage(0.into(), 0x1234.into()); + a.commit_storage(&Default::default(), &mut db).unwrap(); + a.set_storage(1.into(), 0x1234.into()); + a.commit_storage(&Default::default(), &mut db).unwrap(); + a.set_storage(1.into(), 0.into()); + a.commit_storage(&Default::default(), &mut db).unwrap(); + assert_eq!( + a.storage_root().unwrap(), + "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2".into() + ); + } + + #[test] + fn commit_code() { + let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + let mut db = new_memory_db(); + let mut db = AccountDBMut::new(&mut db, &Address::new()); + a.init_code(vec![0x55, 0x44, 0xffu8]); + assert_eq!(a.code_filth, Filth::Dirty); + assert_eq!(a.code_size(), Some(3)); + a.commit_code(&mut db); + assert_eq!( + a.code_hash(), + "af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb".into() + ); + } + + #[test] + fn reset_code() { + let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + let mut db = new_memory_db(); + let mut db = AccountDBMut::new(&mut db, &Address::new()); + a.init_code(vec![0x55, 0x44, 0xffu8]); + assert_eq!(a.code_filth, Filth::Dirty); + a.commit_code(&mut db); + assert_eq!(a.code_filth, Filth::Clean); + assert_eq!( + a.code_hash(), + "af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb".into() + ); + a.reset_code(vec![0x55]); + assert_eq!(a.code_filth, Filth::Dirty); + a.commit_code(&mut db); + assert_eq!( + a.code_hash(), + "37bf2238b11b68cdc8382cece82651b59d3c3988873b6e0f33d79694aa45f1be".into() + ); + } + + #[test] + fn rlpio() { + let a = Account::new(69u8.into(), 0u8.into(), HashMap::new(), Bytes::new()); + let b = Account::from_rlp(&a.rlp()).unwrap(); + assert_eq!(a.balance(), b.balance()); + assert_eq!(a.nonce(), b.nonce()); + assert_eq!(a.code_hash(), b.code_hash()); + assert_eq!(a.storage_root(), b.storage_root()); + } + + #[test] + fn new_account() { + let a = Account::new(69u8.into(), 0u8.into(), HashMap::new(), Bytes::new()); + assert_eq!(a.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); + assert_eq!(*a.balance(), 69u8.into()); + assert_eq!(*a.nonce(), 0u8.into()); + assert_eq!(a.code_hash(), KECCAK_EMPTY); + assert_eq!(a.storage_root().unwrap(), KECCAK_NULL_RLP); + } } diff --git a/ethcore/src/state/backend.rs b/ethcore/src/state/backend.rs index 11e73edb3ab..167884cd018 100644 --- a/ethcore/src/state/backend.rs +++ b/ethcore/src/state/backend.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! A minimal "state backend" trait: an abstraction over the sources of data //! a blockchain state may draw upon. @@ -21,54 +21,50 @@ //! should become general over time to the point where not even a //! merkle trie is strictly necessary. -use std::collections::{HashSet, HashMap}; -use std::sync::Arc; +use std::{ + collections::{HashMap, HashSet}, + sync::Arc, +}; -use state::Account; -use parking_lot::Mutex; use ethereum_types::{Address, H256}; -use memory_db::MemoryDB; use hash_db::{AsHashDB, HashDB}; -use kvdb::DBValue; -use keccak_hasher::KeccakHasher; use journaldb::AsKeyedHashDB; +use keccak_hasher::KeccakHasher; +use kvdb::DBValue; +use memory_db::MemoryDB; +use parking_lot::Mutex; +use state::Account; /// State backend. See module docs for more details. pub trait Backend: Send { - /// Treat the backend as a read-only hashdb. - fn as_hash_db(&self) -> &HashDB; - - /// Treat the backend as a writeable hashdb. - fn as_hash_db_mut(&mut self) -> &mut HashDB; - - /// Add an account entry to the cache. - fn add_to_account_cache(&mut self, addr: Address, data: Option, modified: bool); - - /// Add a global code cache entry. This doesn't need to worry about canonicality because - /// it simply maps hashes to raw code and will always be correct in the absence of - /// hash collisions. - fn cache_code(&self, hash: H256, code: Arc>); - - /// Get basic copy of the cached account. Not required to include storage. - /// Returns 'None' if cache is disabled or if the account is not cached. - fn get_cached_account(&self, addr: &Address) -> Option>; - - /// Get value from a cached account. - /// `None` is passed to the closure if the account entry cached - /// is known not to exist. - /// `None` is returned if the entry is not cached. - fn get_cached(&self, a: &Address, f: F) -> Option - where F: FnOnce(Option<&mut Account>) -> U; - - /// Get cached code based on hash. - fn get_cached_code(&self, hash: &H256) -> Option>>; - - /// Note that an account with the given address is non-null. - fn note_non_null_account(&self, address: &Address); - - /// Check whether an account is known to be empty. Returns true if known to be - /// empty, false otherwise. - fn is_known_null(&self, address: &Address) -> bool; + /// Treat the backend as a read-only hashdb. + fn as_hash_db(&self) -> &dyn HashDB; + + /// Treat the backend as a writeable hashdb. + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB; + + /// Add an account entry to the cache. + fn add_to_account_cache(&mut self, addr: Address, data: Option, modified: bool); + + /// Add a global code cache entry. This doesn't need to worry about canonicality because + /// it simply maps hashes to raw code and will always be correct in the absence of + /// hash collisions. + fn cache_code(&self, hash: H256, code: Arc>); + + /// Get basic copy of the cached account. Not required to include storage. + /// Returns 'None' if cache is disabled or if the account is not cached. + fn get_cached_account(&self, addr: &Address) -> Option>; + + /// Get value from a cached account. + /// `None` is passed to the closure if the account entry cached + /// is known not to exist. + /// `None` is returned if the entry is not cached. + fn get_cached(&self, a: &Address, f: F) -> Option + where + F: FnOnce(Option<&mut Account>) -> U; + + /// Get cached code based on hash. + fn get_cached_code(&self, hash: &H256) -> Option>>; } /// A raw backend used to check proofs of execution. @@ -81,57 +77,72 @@ pub trait Backend: Send { pub struct ProofCheck(MemoryDB); impl ProofCheck { - /// Create a new `ProofCheck` backend from the given state items. - pub fn new(proof: &[DBValue]) -> Self { - let mut db = journaldb::new_memory_db(); - for item in proof { db.insert(item); } - ProofCheck(db) - } + /// Create a new `ProofCheck` backend from the given state items. + pub fn new(proof: &[DBValue]) -> Self { + let mut db = journaldb::new_memory_db(); + for item in proof { + db.insert(item); + } + ProofCheck(db) + } } impl journaldb::KeyedHashDB for ProofCheck { - fn keys(&self) -> HashMap { self.0.keys() } + fn keys(&self) -> HashMap { + self.0.keys() + } } impl HashDB for ProofCheck { - fn get(&self, key: &H256) -> Option { - self.0.get(key) - } + fn get(&self, key: &H256) -> Option { + self.0.get(key) + } - fn contains(&self, key: &H256) -> bool { - self.0.contains(key) - } + fn contains(&self, key: &H256) -> bool { + self.0.contains(key) + } - fn insert(&mut self, value: &[u8]) -> H256 { - self.0.insert(value) - } + fn insert(&mut self, value: &[u8]) -> H256 { + self.0.insert(value) + } - fn emplace(&mut self, key: H256, value: DBValue) { - self.0.emplace(key, value) - } + fn emplace(&mut self, key: H256, value: DBValue) { + self.0.emplace(key, value) + } - fn remove(&mut self, _key: &H256) { } + fn remove(&mut self, _key: &H256) {} } impl AsHashDB for ProofCheck { - fn as_hash_db(&self) -> &HashDB { self } - fn as_hash_db_mut(&mut self) -> &mut HashDB { self } + fn as_hash_db(&self) -> &dyn HashDB { + self + } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { + self + } } impl Backend for ProofCheck { - fn as_hash_db(&self) -> &HashDB { self } - fn as_hash_db_mut(&mut self) -> &mut HashDB { self } - fn add_to_account_cache(&mut self, _addr: Address, _data: Option, _modified: bool) {} - fn cache_code(&self, _hash: H256, _code: Arc>) {} - fn get_cached_account(&self, _addr: &Address) -> Option> { None } - fn get_cached(&self, _a: &Address, _f: F) -> Option - where F: FnOnce(Option<&mut Account>) -> U - { - None - } - fn get_cached_code(&self, _hash: &H256) -> Option>> { None } - fn note_non_null_account(&self, _address: &Address) {} - fn is_known_null(&self, _address: &Address) -> bool { false } + fn as_hash_db(&self) -> &dyn HashDB { + self + } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { + self + } + fn add_to_account_cache(&mut self, _addr: Address, _data: Option, _modified: bool) {} + fn cache_code(&self, _hash: H256, _code: Arc>) {} + fn get_cached_account(&self, _addr: &Address) -> Option> { + None + } + fn get_cached(&self, _a: &Address, _f: F) -> Option + where + F: FnOnce(Option<&mut Account>) -> U, + { + None + } + fn get_cached_code(&self, _hash: &H256) -> Option>> { + None + } } /// Proving state backend. @@ -140,107 +151,124 @@ impl Backend for ProofCheck { /// /// This doesn't cache anything or rely on the canonical state caches. pub struct Proving { - base: H, // state we're proving values from. - changed: MemoryDB, // changed state via insertions. - proof: Mutex>, + base: H, // state we're proving values from. + changed: MemoryDB, // changed state via insertions. + proof: Mutex>, } impl AsKeyedHashDB for Proving { - fn as_keyed_hash_db(&self) -> &journaldb::KeyedHashDB { self } + fn as_keyed_hash_db(&self) -> &dyn journaldb::KeyedHashDB { + self + } } -impl + Send + Sync> AsHashDB for Proving { - fn as_hash_db(&self) -> &HashDB { self } - fn as_hash_db_mut(&mut self) -> &mut HashDB { self } +impl + Send + Sync> AsHashDB + for Proving +{ + fn as_hash_db(&self) -> &dyn HashDB { + self + } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { + self + } } impl journaldb::KeyedHashDB for Proving { - fn keys(&self) -> HashMap { - let mut keys = self.base.as_keyed_hash_db().keys(); - keys.extend(self.changed.keys()); - keys - } + fn keys(&self) -> HashMap { + let mut keys = self.base.as_keyed_hash_db().keys(); + keys.extend(self.changed.keys()); + keys + } } -impl + Send + Sync> HashDB for Proving { - fn get(&self, key: &H256) -> Option { - match self.base.as_hash_db().get(key) { - Some(val) => { - self.proof.lock().insert(val.clone()); - Some(val) - } - None => self.changed.get(key) - } - } - - fn contains(&self, key: &H256) -> bool { - self.get(key).is_some() - } - - fn insert(&mut self, value: &[u8]) -> H256 { - self.changed.insert(value) - } - - fn emplace(&mut self, key: H256, value: DBValue) { - self.changed.emplace(key, value) - } - - fn remove(&mut self, key: &H256) { - // only remove from `changed` - if self.changed.contains(key) { - self.changed.remove(key) - } - } +impl + Send + Sync> HashDB + for Proving +{ + fn get(&self, key: &H256) -> Option { + match self.base.as_hash_db().get(key) { + Some(val) => { + self.proof.lock().insert(val.clone()); + Some(val) + } + None => self.changed.get(key), + } + } + + fn contains(&self, key: &H256) -> bool { + self.get(key).is_some() + } + + fn insert(&mut self, value: &[u8]) -> H256 { + self.changed.insert(value) + } + + fn emplace(&mut self, key: H256, value: DBValue) { + self.changed.emplace(key, value) + } + + fn remove(&mut self, key: &H256) { + // only remove from `changed` + if self.changed.contains(key) { + self.changed.remove(key) + } + } } impl + Send + Sync> Backend for Proving { - fn as_hash_db(&self) -> &HashDB { self } + fn as_hash_db(&self) -> &dyn HashDB { + self + } - fn as_hash_db_mut(&mut self) -> &mut HashDB { self } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { + self + } - fn add_to_account_cache(&mut self, _: Address, _: Option, _: bool) { } + fn add_to_account_cache(&mut self, _: Address, _: Option, _: bool) {} - fn cache_code(&self, _: H256, _: Arc>) { } + fn cache_code(&self, _: H256, _: Arc>) {} - fn get_cached_account(&self, _: &Address) -> Option> { None } + fn get_cached_account(&self, _: &Address) -> Option> { + None + } - fn get_cached(&self, _: &Address, _: F) -> Option - where F: FnOnce(Option<&mut Account>) -> U - { - None - } + fn get_cached(&self, _: &Address, _: F) -> Option + where + F: FnOnce(Option<&mut Account>) -> U, + { + None + } - fn get_cached_code(&self, _: &H256) -> Option>> { None } - fn note_non_null_account(&self, _: &Address) { } - fn is_known_null(&self, _: &Address) -> bool { false } + fn get_cached_code(&self, _: &H256) -> Option>> { + None + } } impl> Proving { - /// Create a new `Proving` over a base database. - /// This will store all values ever fetched from that base. - pub fn new(base: H) -> Self { - Proving { - base: base, - changed: journaldb::new_memory_db(), - proof: Mutex::new(HashSet::new()), - } - } - - /// Consume the backend, extracting the gathered proof in lexicographical order - /// by value. - pub fn extract_proof(self) -> Vec { - self.proof.into_inner().into_iter().collect() - } + /// Create a new `Proving` over a base database. + /// This will store all values ever fetched from that base. + pub fn new(base: H) -> Self { + Proving { + base: base, + changed: journaldb::new_memory_db(), + proof: Mutex::new(HashSet::new()), + } + } + + /// Consume the backend, extracting the gathered proof in lexicographical order + /// by value. + pub fn extract_proof(self) -> Vec { + self.proof.into_inner().into_iter().collect() + } } impl + Clone> Clone for Proving { - fn clone(&self) -> Self { - Proving { - base: self.base.clone(), - changed: self.changed.clone(), - proof: Mutex::new(self.proof.lock().clone()), - } - } + fn clone(&self) -> Self { + Proving { + base: self.base.clone(), + changed: self.changed.clone(), + proof: Mutex::new(self.proof.lock().clone()), + } + } } /// A basic backend. Just wraps the given database, directly inserting into and deleting from @@ -248,27 +276,30 @@ impl + Clone> Clone for Proving { pub struct Basic(pub H); impl + Send + Sync> Backend for Basic { - fn as_hash_db(&self) -> &HashDB { - self.0.as_hash_db() - } + fn as_hash_db(&self) -> &dyn HashDB { + self.0.as_hash_db() + } - fn as_hash_db_mut(&mut self) -> &mut HashDB { - self.0.as_hash_db_mut() - } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { + self.0.as_hash_db_mut() + } - fn add_to_account_cache(&mut self, _: Address, _: Option, _: bool) { } + fn add_to_account_cache(&mut self, _: Address, _: Option, _: bool) {} - fn cache_code(&self, _: H256, _: Arc>) { } + fn cache_code(&self, _: H256, _: Arc>) {} - fn get_cached_account(&self, _: &Address) -> Option> { None } + fn get_cached_account(&self, _: &Address) -> Option> { + None + } - fn get_cached(&self, _: &Address, _: F) -> Option - where F: FnOnce(Option<&mut Account>) -> U - { - None - } + fn get_cached(&self, _: &Address, _: F) -> Option + where + F: FnOnce(Option<&mut Account>) -> U, + { + None + } - fn get_cached_code(&self, _: &H256) -> Option>> { None } - fn note_non_null_account(&self, _: &Address) { } - fn is_known_null(&self, _: &Address) -> bool { false } + fn get_cached_code(&self, _: &H256) -> Option>> { + None + } } diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index ddad10c40dd..7a63687570e 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -1,75 +1,75 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! A mutable state representation suitable to execute transactions. //! Generic over a `Backend`. Deals with `Account`s. //! Unconfirmed sub-states are managed with `checkpoint`s which may be canonicalized //! or rolled back. -use std::cell::{RefCell, RefMut}; -use std::collections::hash_map::Entry; -use std::collections::{HashMap, BTreeMap, BTreeSet, HashSet}; -use std::fmt; -use std::sync::Arc; -use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY}; +use hash::{KECCAK_EMPTY, KECCAK_NULL_RLP}; +use std::{ + cell::{RefCell, RefMut}, + collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet}, + fmt, + sync::Arc, +}; -use types::receipt::{Receipt, TransactionOutcome}; -use machine::EthereumMachine as Machine; -use vm::EnvInfo; use error::Error; +use executed::{Executed, ExecutionError}; use executive::{Executive, TransactOptions}; -use factory::Factories; -use trace::{self, FlatTrace, VMTrace}; +use factory::{Factories, VmFactory}; +use machine::EthereumMachine as Machine; use pod_account::*; use pod_state::{self, PodState}; -use types::basic_account::BasicAccount; -use executed::{Executed, ExecutionError}; -use types::state_diff::StateDiff; -use types::transaction::SignedTransaction; use state_db::StateDB; -use factory::VmFactory; +use trace::{self, FlatTrace, VMTrace}; +use types::{ + basic_account::BasicAccount, + receipt::{Receipt, TransactionOutcome}, + state_diff::StateDiff, + transaction::SignedTransaction, +}; +use vm::EnvInfo; -use ethereum_types::{H256, U256, Address}; -use hash_db::{HashDB, AsHashDB}; +use bytes::Bytes; +use ethereum_types::{Address, H256, U256}; +use hash_db::{AsHashDB, HashDB}; use keccak_hasher::KeccakHasher; use kvdb::DBValue; -use bytes::Bytes; -use trie::{Trie, TrieError, Recorder}; -use ethtrie::{TrieDB, Result as TrieResult}; +use ethtrie::{Result as TrieResult, TrieDB}; +use trie::{Recorder, Trie, TrieError}; mod account; mod substate; pub mod backend; -pub use self::account::Account; -pub use self::backend::Backend; -pub use self::substate::Substate; +pub use self::{account::Account, backend::Backend, substate::Substate}; /// Used to return information about an `State::apply` operation. pub struct ApplyOutcome { - /// The receipt for the applied transaction. - pub receipt: Receipt, - /// The output of the applied transaction. - pub output: Bytes, - /// The trace for the applied transaction, empty if tracing was not produced. - pub trace: Vec, - /// The VM trace for the applied transaction, None if tracing was not produced. - pub vm_trace: Option + /// The receipt for the applied transaction. + pub receipt: Receipt, + /// The output of the applied transaction. + pub output: Bytes, + /// The trace for the applied transaction, empty if tracing was not produced. + pub trace: Vec, + /// The VM trace for the applied transaction, None if tracing was not produced. + pub vm_trace: Option, } /// Result type for the execution ("application") of a transaction. @@ -78,28 +78,28 @@ pub type ApplyResult = Result, Error>; /// Return type of proof validity check. #[derive(Debug, Clone)] pub enum ProvedExecution { - /// Proof wasn't enough to complete execution. - BadProof, - /// The transaction failed, but not due to a bad proof. - Failed(ExecutionError), - /// The transaction successfully completed with the given proof. - Complete(Box), + /// Proof wasn't enough to complete execution. + BadProof, + /// The transaction failed, but not due to a bad proof. + Failed(ExecutionError), + /// The transaction successfully completed with the given proof. + Complete(Box), } #[derive(Eq, PartialEq, Clone, Copy, Debug)] /// Account modification state. Used to check if the account was /// Modified in between commits and overall. enum AccountState { - /// Account was loaded from disk and never modified in this state object. - CleanFresh, - /// Account was loaded from the global cache and never modified. - CleanCached, - /// Account has been modified and is not committed to the trie yet. - /// This is set if any of the account data is changed, including - /// storage and code. - Dirty, - /// Account was modified and committed to the trie. - Committed, + /// Account was loaded from disk and never modified in this state object. + CleanFresh, + /// Account was loaded from the global cache and never modified. + CleanCached, + /// Account has been modified and is not committed to the trie yet. + /// This is set if any of the account data is changed, including + /// storage and code. + Dirty, + /// Account was modified and committed to the trie. + Committed, } #[derive(Debug)] @@ -108,157 +108,159 @@ enum AccountState { /// Account entry can contain existing (`Some`) or non-existing /// account (`None`) struct AccountEntry { - /// Account entry. `None` if account known to be non-existant. - account: Option, - /// Unmodified account balance. - old_balance: Option, - /// Entry state. - state: AccountState, + /// Account entry. `None` if account known to be non-existant. + account: Option, + /// Unmodified account balance. + old_balance: Option, + /// Entry state. + state: AccountState, } // Account cache item. Contains account data and // modification state impl AccountEntry { - fn is_dirty(&self) -> bool { - self.state == AccountState::Dirty - } - - fn exists_and_is_null(&self) -> bool { - self.account.as_ref().map_or(false, |a| a.is_null()) - } - - /// Clone dirty data into new `AccountEntry`. This includes - /// basic account data and modified storage keys. - /// Returns None if clean. - fn clone_if_dirty(&self) -> Option { - match self.is_dirty() { - true => Some(self.clone_dirty()), - false => None, - } - } - - /// Clone dirty data into new `AccountEntry`. This includes - /// basic account data and modified storage keys. - fn clone_dirty(&self) -> AccountEntry { - AccountEntry { - old_balance: self.old_balance, - account: self.account.as_ref().map(Account::clone_dirty), - state: self.state, - } - } - - // Create a new account entry and mark it as dirty. - fn new_dirty(account: Option) -> AccountEntry { - AccountEntry { - old_balance: account.as_ref().map(|a| a.balance().clone()), - account: account, - state: AccountState::Dirty, - } - } - - // Create a new account entry and mark it as clean. - fn new_clean(account: Option) -> AccountEntry { - AccountEntry { - old_balance: account.as_ref().map(|a| a.balance().clone()), - account: account, - state: AccountState::CleanFresh, - } - } - - // Create a new account entry and mark it as clean and cached. - fn new_clean_cached(account: Option) -> AccountEntry { - AccountEntry { - old_balance: account.as_ref().map(|a| a.balance().clone()), - account: account, - state: AccountState::CleanCached, - } - } - - // Replace data with another entry but preserve storage cache. - fn overwrite_with(&mut self, other: AccountEntry) { - self.state = other.state; - match other.account { - Some(acc) => { - if let Some(ref mut ours) = self.account { - ours.overwrite_with(acc); - } else { - self.account = Some(acc); - } - }, - None => self.account = None, - } - } + fn is_dirty(&self) -> bool { + self.state == AccountState::Dirty + } + + fn exists_and_is_null(&self) -> bool { + self.account.as_ref().map_or(false, |a| a.is_null()) + } + + /// Clone dirty data into new `AccountEntry`. This includes + /// basic account data and modified storage keys. + /// Returns None if clean. + fn clone_if_dirty(&self) -> Option { + match self.is_dirty() { + true => Some(self.clone_dirty()), + false => None, + } + } + + /// Clone dirty data into new `AccountEntry`. This includes + /// basic account data and modified storage keys. + fn clone_dirty(&self) -> AccountEntry { + AccountEntry { + old_balance: self.old_balance, + account: self.account.as_ref().map(Account::clone_dirty), + state: self.state, + } + } + + // Create a new account entry and mark it as dirty. + fn new_dirty(account: Option) -> AccountEntry { + AccountEntry { + old_balance: account.as_ref().map(|a| a.balance().clone()), + account: account, + state: AccountState::Dirty, + } + } + + // Create a new account entry and mark it as clean. + fn new_clean(account: Option) -> AccountEntry { + AccountEntry { + old_balance: account.as_ref().map(|a| a.balance().clone()), + account: account, + state: AccountState::CleanFresh, + } + } + + // Create a new account entry and mark it as clean and cached. + fn new_clean_cached(account: Option) -> AccountEntry { + AccountEntry { + old_balance: account.as_ref().map(|a| a.balance().clone()), + account: account, + state: AccountState::CleanCached, + } + } + + // Replace data with another entry but preserve storage cache. + fn overwrite_with(&mut self, other: AccountEntry) { + self.state = other.state; + match other.account { + Some(acc) => { + if let Some(ref mut ours) = self.account { + ours.overwrite_with(acc); + } else { + self.account = Some(acc); + } + } + None => self.account = None, + } + } } /// Check the given proof of execution. /// `Err(ExecutionError::Internal)` indicates failure, everything else indicates /// a successful proof (as the transaction itself may be poorly chosen). pub fn check_proof( - proof: &[DBValue], - root: H256, - transaction: &SignedTransaction, - machine: &Machine, - env_info: &EnvInfo, + proof: &[DBValue], + root: H256, + transaction: &SignedTransaction, + machine: &Machine, + env_info: &EnvInfo, ) -> ProvedExecution { - let backend = self::backend::ProofCheck::new(proof); - let mut factories = Factories::default(); - factories.accountdb = ::account_db::Factory::Plain; - - let res = State::from_existing( - backend, - root, - machine.account_start_nonce(env_info.number), - factories - ); - - let mut state = match res { - Ok(state) => state, - Err(_) => return ProvedExecution::BadProof, - }; - - let options = TransactOptions::with_no_tracing().save_output_from_contract(); - match state.execute(env_info, machine, transaction, options, true) { - Ok(executed) => ProvedExecution::Complete(Box::new(executed)), - Err(ExecutionError::Internal(_)) => ProvedExecution::BadProof, - Err(e) => ProvedExecution::Failed(e), - } + let backend = self::backend::ProofCheck::new(proof); + let mut factories = Factories::default(); + factories.accountdb = ::account_db::Factory::Plain; + + let res = State::from_existing( + backend, + root, + machine.account_start_nonce(env_info.number), + factories, + ); + + let mut state = match res { + Ok(state) => state, + Err(_) => return ProvedExecution::BadProof, + }; + + let options = TransactOptions::with_no_tracing().save_output_from_contract(); + match state.execute(env_info, machine, transaction, options, true) { + Ok(executed) => ProvedExecution::Complete(Box::new(executed)), + Err(ExecutionError::Internal(_)) => ProvedExecution::BadProof, + Err(e) => ProvedExecution::Failed(e), + } } /// Prove a `virtual` transaction on the given state. /// Returns `None` when the transacion could not be proved, /// and a proof otherwise. pub fn prove_transaction_virtual + Send + Sync>( - db: H, - root: H256, - transaction: &SignedTransaction, - machine: &Machine, - env_info: &EnvInfo, - factories: Factories, + db: H, + root: H256, + transaction: &SignedTransaction, + machine: &Machine, + env_info: &EnvInfo, + factories: Factories, ) -> Option<(Bytes, Vec)> { - use self::backend::Proving; - - let backend = Proving::new(db); - let res = State::from_existing( - backend, - root, - machine.account_start_nonce(env_info.number), - factories, - ); - - let mut state = match res { - Ok(state) => state, - Err(_) => return None, - }; - - let options = TransactOptions::with_no_tracing().dont_check_nonce().save_output_from_contract(); - match state.execute(env_info, machine, transaction, options, true) { - Err(ExecutionError::Internal(_)) => None, - Err(e) => { - trace!(target: "state", "Proved call failed: {}", e); - Some((Vec::new(), state.drop().1.extract_proof())) - } - Ok(res) => Some((res.output, state.drop().1.extract_proof())), - } + use self::backend::Proving; + + let backend = Proving::new(db); + let res = State::from_existing( + backend, + root, + machine.account_start_nonce(env_info.number), + factories, + ); + + let mut state = match res { + Ok(state) => state, + Err(_) => return None, + }; + + let options = TransactOptions::with_no_tracing() + .dont_check_nonce() + .save_output_from_contract(); + match state.execute(env_info, machine, transaction, options, true) { + Err(ExecutionError::Internal(_)) => None, + Err(e) => { + trace!(target: "state", "Proved call failed: {}", e); + Some((Vec::new(), state.drop().1.extract_proof())) + } + Ok(res) => Some((res.output, state.drop().1.extract_proof())), + } } /// Representation of the entire state of all accounts in the system. @@ -307,634 +309,761 @@ pub fn prove_transaction_virtual + Send + Syn /// backed-up values are moved into a parent checkpoint (if any). /// pub struct State { - db: B, - root: H256, - cache: RefCell>, - // The original account is preserved in - checkpoints: RefCell>>>, - account_start_nonce: U256, - factories: Factories, + db: B, + root: H256, + cache: RefCell>, + // The original account is preserved in + checkpoints: RefCell>>>, + account_start_nonce: U256, + factories: Factories, } #[derive(Copy, Clone)] enum RequireCache { - None, - CodeSize, - Code, + None, + CodeSize, + Code, } /// Mode of dealing with null accounts. #[derive(PartialEq)] pub enum CleanupMode<'a> { - /// Create accounts which would be null. - ForceCreate, - /// Don't delete null accounts upon touching, but also don't create them. - NoEmpty, - /// Mark all touched accounts. - TrackTouched(&'a mut HashSet
), + /// Create accounts which would be null. + ForceCreate, + /// Don't delete null accounts upon touching, but also don't create them. + NoEmpty, + /// Mark all touched accounts. + TrackTouched(&'a mut HashSet
), } /// Provides subset of `State` methods to query state information pub trait StateInfo { - /// Get the nonce of account `a`. - fn nonce(&self, a: &Address) -> TrieResult; + /// Get the nonce of account `a`. + fn nonce(&self, a: &Address) -> TrieResult; - /// Get the balance of account `a`. - fn balance(&self, a: &Address) -> TrieResult; + /// Get the balance of account `a`. + fn balance(&self, a: &Address) -> TrieResult; - /// Mutate storage of account `address` so that it is `value` for `key`. - fn storage_at(&self, address: &Address, key: &H256) -> TrieResult; + /// Mutate storage of account `address` so that it is `value` for `key`. + fn storage_at(&self, address: &Address, key: &H256) -> TrieResult; - /// Get accounts' code. - fn code(&self, a: &Address) -> TrieResult>>; + /// Get accounts' code. + fn code(&self, a: &Address) -> TrieResult>>; } impl StateInfo for State { - fn nonce(&self, a: &Address) -> TrieResult { State::nonce(self, a) } - fn balance(&self, a: &Address) -> TrieResult { State::balance(self, a) } - fn storage_at(&self, address: &Address, key: &H256) -> TrieResult { State::storage_at(self, address, key) } - fn code(&self, address: &Address) -> TrieResult>> { State::code(self, address) } + fn nonce(&self, a: &Address) -> TrieResult { + State::nonce(self, a) + } + fn balance(&self, a: &Address) -> TrieResult { + State::balance(self, a) + } + fn storage_at(&self, address: &Address, key: &H256) -> TrieResult { + State::storage_at(self, address, key) + } + fn code(&self, address: &Address) -> TrieResult>> { + State::code(self, address) + } } const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \ Therefore creating a SecTrieDB with this state's root will not fail."; impl State { - /// Creates new state with empty state root - /// Used for tests. - pub fn new(mut db: B, account_start_nonce: U256, factories: Factories) -> State { - let mut root = H256::new(); - { - // init trie and reset root to null - let _ = factories.trie.create(db.as_hash_db_mut(), &mut root); - } - - State { - db: db, - root: root, - cache: RefCell::new(HashMap::new()), - checkpoints: RefCell::new(Vec::new()), - account_start_nonce: account_start_nonce, - factories: factories, - } - } - - /// Creates new state with existing state root - pub fn from_existing(db: B, root: H256, account_start_nonce: U256, factories: Factories) -> TrieResult> { - if !db.as_hash_db().contains(&root) { - return Err(Box::new(TrieError::InvalidStateRoot(root))); - } - - let state = State { - db: db, - root: root, - cache: RefCell::new(HashMap::new()), - checkpoints: RefCell::new(Vec::new()), - account_start_nonce: account_start_nonce, - factories: factories - }; - - Ok(state) - } - - /// Get a VM factory that can execute on this state. - pub fn vm_factory(&self) -> VmFactory { - self.factories.vm.clone() - } - - /// Create a recoverable checkpoint of this state. Return the checkpoint index. - pub fn checkpoint(&mut self) -> usize { - let checkpoints = self.checkpoints.get_mut(); - let index = checkpoints.len(); - checkpoints.push(HashMap::new()); - index - } - - /// Merge last checkpoint with previous. - pub fn discard_checkpoint(&mut self) { - // merge with previous checkpoint - let last = self.checkpoints.get_mut().pop(); - if let Some(mut checkpoint) = last { - if let Some(ref mut prev) = self.checkpoints.get_mut().last_mut() { - if prev.is_empty() { - **prev = checkpoint; - } else { - for (k, v) in checkpoint.drain() { - prev.entry(k).or_insert(v); - } - } - } - } - } - - /// Revert to the last checkpoint and discard it. - pub fn revert_to_checkpoint(&mut self) { - if let Some(mut checkpoint) = self.checkpoints.get_mut().pop() { - for (k, v) in checkpoint.drain() { - match v { - Some(v) => { - match self.cache.get_mut().entry(k) { - Entry::Occupied(mut e) => { - // Merge checkpointed changes back into the main account - // storage preserving the cache. - e.get_mut().overwrite_with(v); - }, - Entry::Vacant(e) => { - e.insert(v); - } - } - }, - None => { - if let Entry::Occupied(e) = self.cache.get_mut().entry(k) { - if e.get().is_dirty() { - e.remove(); - } - } - } - } - } - } - } - - fn insert_cache(&self, address: &Address, account: AccountEntry) { - // Dirty account which is not in the cache means this is a new account. - // It goes directly into the checkpoint as there's nothing to rever to. - // - // In all other cases account is read as clean first, and after that made - // dirty in and added to the checkpoint with `note_cache`. - let is_dirty = account.is_dirty(); - let old_value = self.cache.borrow_mut().insert(*address, account); - if is_dirty { - if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() { - checkpoint.entry(*address).or_insert(old_value); - } - } - } - - fn note_cache(&self, address: &Address) { - if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() { - checkpoint.entry(*address) - .or_insert_with(|| self.cache.borrow().get(address).map(AccountEntry::clone_dirty)); - } - } - - /// Destroy the current object and return root and database. - pub fn drop(mut self) -> (H256, B) { - self.propagate_to_global_cache(); - (self.root, self.db) - } - - /// Destroy the current object and return single account data. - pub fn into_account(self, account: &Address) -> TrieResult<(Option>, HashMap)> { - // TODO: deconstruct without cloning. - let account = self.require(account, true)?; - Ok((account.code().clone(), account.storage_changes().clone())) - } - - /// Return reference to root - pub fn root(&self) -> &H256 { - &self.root - } - - /// Create a new contract at address `contract`. If there is already an account at the address - /// it will have its code reset, ready for `init_code()`. - pub fn new_contract(&mut self, contract: &Address, balance: U256, nonce_offset: U256) -> TrieResult<()> { - let original_storage_root = self.original_storage_root(contract)?; - let (nonce, overflow) = self.account_start_nonce.overflowing_add(nonce_offset); - if overflow { - return Err(Box::new(TrieError::DecoderError(H256::from(contract), - rlp::DecoderError::Custom("Nonce overflow".into())))); - } - self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, nonce, original_storage_root)))); - Ok(()) - } - - /// Remove an existing account. - pub fn kill_account(&mut self, account: &Address) { - self.insert_cache(account, AccountEntry::new_dirty(None)); - } - - /// Determine whether an account exists. - pub fn exists(&self, a: &Address) -> TrieResult { - // Bloom filter does not contain empty accounts, so it is important here to - // check if account exists in the database directly before EIP-161 is in effect. - self.ensure_cached(a, RequireCache::None, false, |a| a.is_some()) - } - - /// Determine whether an account exists and if not empty. - pub fn exists_and_not_null(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::None, false, |a| a.map_or(false, |a| !a.is_null())) - } - - /// Determine whether an account exists and has code or non-zero nonce. - pub fn exists_and_has_code_or_nonce(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::CodeSize, false, - |a| a.map_or(false, |a| a.code_hash() != KECCAK_EMPTY || *a.nonce() != self.account_start_nonce)) - } - - /// Get the balance of account `a`. - pub fn balance(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map_or(U256::zero(), |account| *account.balance())) - } - - /// Get the nonce of account `a`. - pub fn nonce(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce())) - } - - /// Whether the base storage root of an account remains unchanged. - pub fn is_base_storage_root_unchanged(&self, a: &Address) -> TrieResult { - Ok(self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map(|account| account.is_base_storage_root_unchanged()))? - .unwrap_or(true)) - } - - /// Get the storage root of account `a`. - pub fn storage_root(&self, a: &Address) -> TrieResult> { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().and_then(|account| account.storage_root())) - } - - /// Get the original storage root since last commit of account `a`. - pub fn original_storage_root(&self, a: &Address) -> TrieResult { - Ok(self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map(|account| account.original_storage_root()))? - .unwrap_or(KECCAK_NULL_RLP)) - } - - /// Get the value of storage at a specific checkpoint. - pub fn checkpoint_storage_at(&self, start_checkpoint_index: usize, address: &Address, key: &H256) -> TrieResult> { - #[must_use] - enum ReturnKind { - /// Use original storage at value at this address. - OriginalAt, - /// The checkpoint storage value is the same as the checkpoint storage value at the next checkpoint. - SameAsNext, - } - - let kind = { - let checkpoints = self.checkpoints.borrow(); - - if start_checkpoint_index >= checkpoints.len() { - // The checkpoint was not found. Return None. - return Ok(None); - } - - let mut kind = None; - - for checkpoint in checkpoints.iter().skip(start_checkpoint_index) { - match checkpoint.get(address) { - // The account exists at this checkpoint. - Some(Some(AccountEntry { account: Some(ref account), .. })) => { - if let Some(value) = account.cached_storage_at(key) { - return Ok(Some(value)); - } else { - // This account has checkpoint entry, but the key is not in the entry's cache. We can use - // original_storage_at if current account's original storage root is the same as checkpoint - // account's original storage root. Otherwise, the account must be a newly created contract. - if account.base_storage_root() == self.original_storage_root(address)? { - kind = Some(ReturnKind::OriginalAt); - break - } else { - // If account base storage root is different from the original storage root since last - // commit, then it can only be created from a new contract, where the base storage root - // would always be empty. Note that this branch is actually never called, because - // `cached_storage_at` handled this case. - warn!(target: "state", "Trying to get an account's cached storage value, but base storage root does not equal to original storage root! Assuming the value is empty."); - return Ok(Some(H256::new())); - } - } - }, - // The account didn't exist at that point. Return empty value. - Some(Some(AccountEntry { account: None, .. })) => return Ok(Some(H256::new())), - // The value was not cached at that checkpoint, meaning it was not modified at all. - Some(None) => { - kind = Some(ReturnKind::OriginalAt); - break - }, - // This key does not have a checkpoint entry. - None => { - kind = Some(ReturnKind::SameAsNext); - }, - } - } - - kind.expect("start_checkpoint_index is checked to be below checkpoints_len; for loop above must have been executed at least once; it will either early return, or set the kind value to Some; qed") - }; - - match kind { - ReturnKind::SameAsNext => { - // If we reached here, all previous SameAsNext failed to early return. It means that the value we want - // to fetch is the same as current. - Ok(Some(self.storage_at(address, key)?)) - }, - ReturnKind::OriginalAt => Ok(Some(self.original_storage_at(address, key)?)), - } - } - - fn storage_at_inner( - &self, address: &Address, key: &H256, f_cached_at: FCachedStorageAt, f_at: FStorageAt, - ) -> TrieResult where - FCachedStorageAt: Fn(&Account, &H256) -> Option, - FStorageAt: Fn(&Account, &HashDB, &H256) -> TrieResult - { - // Storage key search and update works like this: - // 1. If there's an entry for the account in the local cache check for the key and return it if found. - // 2. If there's an entry for the account in the global cache check for the key or load it into that account. - // 3. If account is missing in the global cache load it into the local cache and cache the key there. - - { - // check local cache first without updating - let local_cache = self.cache.borrow_mut(); - let mut local_account = None; - if let Some(maybe_acc) = local_cache.get(address) { - match maybe_acc.account { - Some(ref account) => { - if let Some(value) = f_cached_at(account, key) { - return Ok(value); - } else { - local_account = Some(maybe_acc); - } - }, - _ => return Ok(H256::new()), - } - } - // check the global cache and and cache storage key there if found, - let trie_res = self.db.get_cached(address, |acc| match acc { - None => Ok(H256::new()), - Some(a) => { - let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), a.address_hash(address)); - f_at(a, account_db.as_hash_db(), key) - } - }); - - if let Some(res) = trie_res { - return res; - } - - // otherwise cache the account localy and cache storage key there. - if let Some(ref mut acc) = local_account { - if let Some(ref account) = acc.account { - let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(address)); - return f_at(account, account_db.as_hash_db(), key) - } else { - return Ok(H256::new()) - } - } - } - - // check if the account could exist before any requests to trie - if self.db.is_known_null(address) { return Ok(H256::zero()) } - - // account is not found in the global cache, get from the DB and insert into local - let db = &self.db.as_hash_db(); - let db = self.factories.trie.readonly(db, &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); - let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); - let maybe_acc = db.get_with(address, from_rlp)?; - let r = maybe_acc.as_ref().map_or(Ok(H256::new()), |a| { - let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), a.address_hash(address)); - f_at(a, account_db.as_hash_db(), key) - }); - self.insert_cache(address, AccountEntry::new_clean(maybe_acc)); - r - } - - /// Mutate storage of account `address` so that it is `value` for `key`. - pub fn storage_at(&self, address: &Address, key: &H256) -> TrieResult { - self.storage_at_inner( - address, - key, - |account, key| { account.cached_storage_at(key) }, - |account, db, key| { account.storage_at(db, key) }, - ) - } - - /// Get the value of storage after last state commitment. - pub fn original_storage_at(&self, address: &Address, key: &H256) -> TrieResult { - self.storage_at_inner( - address, - key, - |account, key| { account.cached_original_storage_at(key) }, - |account, db, key| { account.original_storage_at(db, key) }, - ) - } - - /// Get accounts' code. - pub fn code(&self, a: &Address) -> TrieResult>> { - self.ensure_cached(a, RequireCache::Code, true, - |a| a.as_ref().map_or(None, |a| a.code().clone())) - } - - /// Get an account's code hash. - pub fn code_hash(&self, a: &Address) -> TrieResult> { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map(|a| a.code_hash())) - } - - /// Get accounts' code size. - pub fn code_size(&self, a: &Address) -> TrieResult> { - self.ensure_cached(a, RequireCache::CodeSize, true, - |a| a.as_ref().and_then(|a| a.code_size())) - } - - /// Add `incr` to the balance of account `a`. - pub fn add_balance(&mut self, a: &Address, incr: &U256, cleanup_mode: CleanupMode) -> TrieResult<()> { - trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a)?); - let is_value_transfer = !incr.is_zero(); - if is_value_transfer || (cleanup_mode == CleanupMode::ForceCreate && !self.exists(a)?) { - self.require(a, false)?.add_balance(incr); - } else if let CleanupMode::TrackTouched(set) = cleanup_mode { - if self.exists(a)? { - set.insert(*a); - self.touch(a)?; - } - } - Ok(()) - } - - /// Subtract `decr` from the balance of account `a`. - pub fn sub_balance(&mut self, a: &Address, decr: &U256, cleanup_mode: &mut CleanupMode) -> TrieResult<()> { - trace!(target: "state", "sub_balance({}, {}): {}", a, decr, self.balance(a)?); - if !decr.is_zero() || !self.exists(a)? { - self.require(a, false)?.sub_balance(decr); - } - if let CleanupMode::TrackTouched(ref mut set) = *cleanup_mode { - set.insert(*a); - } - Ok(()) - } - - /// Subtracts `by` from the balance of `from` and adds it to that of `to`. - pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256, mut cleanup_mode: CleanupMode) -> TrieResult<()> { - self.sub_balance(from, by, &mut cleanup_mode)?; - self.add_balance(to, by, cleanup_mode)?; - Ok(()) - } - - /// Increment the nonce of account `a` by 1. - pub fn inc_nonce(&mut self, a: &Address) -> TrieResult<()> { - self.require(a, false).map(|mut x| x.inc_nonce()) - } - - /// Mutate storage of account `a` so that it is `value` for `key`. - pub fn set_storage(&mut self, a: &Address, key: H256, value: H256) -> TrieResult<()> { - trace!(target: "state", "set_storage({}:{:x} to {:x})", a, key, value); - if self.storage_at(a, &key)? != value { - self.require(a, false)?.set_storage(key, value) - } - - Ok(()) - } - - /// Initialise the code of account `a` so that it is `code`. - /// NOTE: Account should have been created with `new_contract`. - pub fn init_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { - self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, KECCAK_NULL_RLP), |_| {})?.init_code(code); - Ok(()) - } - - /// Reset the code of account `a` so that it is `code`. - pub fn reset_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { - self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, KECCAK_NULL_RLP), |_| {})?.reset_code(code); - Ok(()) - } - - /// Execute a given transaction, producing a receipt and an optional trace. - /// This will change the state accordingly. - pub fn apply(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, tracing: bool) -> ApplyResult { - if tracing { - let options = TransactOptions::with_tracing(); - self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) - } else { - let options = TransactOptions::with_no_tracing(); - self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) - } - } - - /// Execute a given transaction with given tracer and VM tracer producing a receipt and an optional trace. - /// This will change the state accordingly. - pub fn apply_with_tracing( - &mut self, - env_info: &EnvInfo, - machine: &Machine, - t: &SignedTransaction, - tracer: T, - vm_tracer: V, - ) -> ApplyResult where - T: trace::Tracer, - V: trace::VMTracer, - { - let options = TransactOptions::new(tracer, vm_tracer); - let e = self.execute(env_info, machine, t, options, false)?; - let params = machine.params(); - - let eip658 = env_info.number >= params.eip658_transition; - let no_intermediate_commits = - eip658 || - (env_info.number >= params.eip98_transition && env_info.number >= params.validate_receipts_transition); - - let outcome = if no_intermediate_commits { - if eip658 { - TransactionOutcome::StatusCode(if e.exception.is_some() { 0 } else { 1 }) - } else { - TransactionOutcome::Unknown - } - } else { - self.commit()?; - TransactionOutcome::StateRoot(self.root().clone()) - }; - - let output = e.output; - let receipt = Receipt::new(outcome, e.cumulative_gas_used, e.logs); - trace!(target: "state", "Transaction receipt: {:?}", receipt); - - Ok(ApplyOutcome { - receipt, - output, - trace: e.trace, - vm_trace: e.vm_trace, - }) - } - - // Execute a given transaction without committing changes. - // - // `virt` signals that we are executing outside of a block set and restrictions like - // gas limits and gas costs should be lifted. - fn execute(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, options: TransactOptions, virt: bool) - -> Result, ExecutionError> where T: trace::Tracer, V: trace::VMTracer, - { - let schedule = machine.schedule(env_info.number); - let mut e = Executive::new(self, env_info, machine, &schedule); - - match virt { - true => e.transact_virtual(t, options), - false => e.transact(t, options), - } - } - - fn touch(&mut self, a: &Address) -> TrieResult<()> { - self.require(a, false)?; - Ok(()) - } - - /// Commits our cached account changes into the trie. - pub fn commit(&mut self) -> Result<(), Error> { - assert!(self.checkpoints.borrow().is_empty()); - // first, commit the sub trees. - let mut accounts = self.cache.borrow_mut(); - for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) { - if let Some(ref mut account) = a.account { - let addr_hash = account.address_hash(address); - { - let mut account_db = self.factories.accountdb.create(self.db.as_hash_db_mut(), addr_hash); - account.commit_storage(&self.factories.trie, account_db.as_hash_db_mut())?; - account.commit_code(account_db.as_hash_db_mut()); - } - if !account.is_empty() { - self.db.note_non_null_account(address); - } - } - } - - { - let mut trie = self.factories.trie.from_existing(self.db.as_hash_db_mut(), &mut self.root)?; - for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) { - a.state = AccountState::Committed; - match a.account { - Some(ref mut account) => { - trie.insert(address, &account.rlp())?; - }, - None => { - trie.remove(address)?; - }, - }; - } - } - - Ok(()) - } - - /// Propagate local cache into shared canonical state cache. - fn propagate_to_global_cache(&mut self) { - let mut addresses = self.cache.borrow_mut(); - trace!("Committing cache {:?} entries", addresses.len()); - for (address, a) in addresses.drain().filter(|&(_, ref a)| a.state == AccountState::Committed || a.state == AccountState::CleanFresh) { - self.db.add_to_account_cache(address, a.account, a.state == AccountState::Committed); - } - } - - /// Clear state cache - pub fn clear(&mut self) { - assert!(self.checkpoints.borrow().is_empty()); - self.cache.borrow_mut().clear(); - } - - /// Remove any touched empty or dust accounts. - pub fn kill_garbage(&mut self, touched: &HashSet
, remove_empty_touched: bool, min_balance: &Option, kill_contracts: bool) -> TrieResult<()> { - let to_kill: HashSet<_> = { - self.cache.borrow().iter().filter_map(|(address, ref entry)| + /// Creates new state with empty state root + /// Used for tests. + pub fn new(mut db: B, account_start_nonce: U256, factories: Factories) -> State { + let mut root = H256::new(); + { + // init trie and reset root to null + let _ = factories.trie.create(db.as_hash_db_mut(), &mut root); + } + + State { + db: db, + root: root, + cache: RefCell::new(HashMap::new()), + checkpoints: RefCell::new(Vec::new()), + account_start_nonce: account_start_nonce, + factories: factories, + } + } + + /// Creates new state with existing state root + pub fn from_existing( + db: B, + root: H256, + account_start_nonce: U256, + factories: Factories, + ) -> TrieResult> { + if !db.as_hash_db().contains(&root) { + return Err(Box::new(TrieError::InvalidStateRoot(root))); + } + + let state = State { + db: db, + root: root, + cache: RefCell::new(HashMap::new()), + checkpoints: RefCell::new(Vec::new()), + account_start_nonce: account_start_nonce, + factories: factories, + }; + + Ok(state) + } + + /// Get a VM factory that can execute on this state. + pub fn vm_factory(&self) -> VmFactory { + self.factories.vm.clone() + } + + /// Create a recoverable checkpoint of this state. Return the checkpoint index. + pub fn checkpoint(&mut self) -> usize { + let checkpoints = self.checkpoints.get_mut(); + let index = checkpoints.len(); + checkpoints.push(HashMap::new()); + index + } + + /// Merge last checkpoint with previous. + pub fn discard_checkpoint(&mut self) { + // merge with previous checkpoint + let last = self.checkpoints.get_mut().pop(); + if let Some(mut checkpoint) = last { + if let Some(ref mut prev) = self.checkpoints.get_mut().last_mut() { + if prev.is_empty() { + **prev = checkpoint; + } else { + for (k, v) in checkpoint.drain() { + prev.entry(k).or_insert(v); + } + } + } + } + } + + /// Revert to the last checkpoint and discard it. + pub fn revert_to_checkpoint(&mut self) { + if let Some(mut checkpoint) = self.checkpoints.get_mut().pop() { + for (k, v) in checkpoint.drain() { + match v { + Some(v) => { + match self.cache.get_mut().entry(k) { + Entry::Occupied(mut e) => { + // Merge checkpointed changes back into the main account + // storage preserving the cache. + e.get_mut().overwrite_with(v); + } + Entry::Vacant(e) => { + e.insert(v); + } + } + } + None => { + if let Entry::Occupied(e) = self.cache.get_mut().entry(k) { + if e.get().is_dirty() { + e.remove(); + } + } + } + } + } + } + } + + fn insert_cache(&self, address: &Address, account: AccountEntry) { + // Dirty account which is not in the cache means this is a new account. + // It goes directly into the checkpoint as there's nothing to rever to. + // + // In all other cases account is read as clean first, and after that made + // dirty in and added to the checkpoint with `note_cache`. + let is_dirty = account.is_dirty(); + let old_value = self.cache.borrow_mut().insert(*address, account); + if is_dirty { + if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() { + checkpoint.entry(*address).or_insert(old_value); + } + } + } + + fn note_cache(&self, address: &Address) { + if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() { + checkpoint.entry(*address).or_insert_with(|| { + self.cache + .borrow() + .get(address) + .map(AccountEntry::clone_dirty) + }); + } + } + + /// Destroy the current object and return root and database. + pub fn drop(mut self) -> (H256, B) { + self.propagate_to_global_cache(); + (self.root, self.db) + } + + /// Destroy the current object and return single account data. + pub fn into_account( + self, + account: &Address, + ) -> TrieResult<(Option>, HashMap)> { + // TODO: deconstruct without cloning. + let account = self.require(account, true)?; + Ok((account.code().clone(), account.storage_changes().clone())) + } + + /// Return reference to root + pub fn root(&self) -> &H256 { + &self.root + } + + /// Create a new contract at address `contract`. If there is already an account at the address + /// it will have its code reset, ready for `init_code()`. + pub fn new_contract( + &mut self, + contract: &Address, + balance: U256, + nonce_offset: U256, + ) -> TrieResult<()> { + let original_storage_root = self.original_storage_root(contract)?; + let (nonce, overflow) = self.account_start_nonce.overflowing_add(nonce_offset); + if overflow { + return Err(Box::new(TrieError::DecoderError( + H256::from(contract), + rlp::DecoderError::Custom("Nonce overflow".into()), + ))); + } + self.insert_cache( + contract, + AccountEntry::new_dirty(Some(Account::new_contract( + balance, + nonce, + original_storage_root, + ))), + ); + Ok(()) + } + + /// Remove an existing account. + pub fn kill_account(&mut self, account: &Address) { + self.insert_cache(account, AccountEntry::new_dirty(None)); + } + + /// Determine whether an account exists. + pub fn exists(&self, a: &Address) -> TrieResult { + // Bloom filter does not contain empty accounts, so it is important here to + // check if account exists in the database directly before EIP-161 is in effect. + self.ensure_cached(a, RequireCache::None, |a| a.is_some()) + } + + /// Determine whether an account exists and if not empty. + pub fn exists_and_not_null(&self, a: &Address) -> TrieResult { + self.ensure_cached(a, RequireCache::None, |a| a.map_or(false, |a| !a.is_null())) + } + + /// Determine whether an account exists and has code or non-zero nonce. + pub fn exists_and_has_code_or_nonce(&self, a: &Address) -> TrieResult { + self.ensure_cached(a, RequireCache::CodeSize, |a| { + a.map_or(false, |a| { + a.code_hash() != KECCAK_EMPTY || *a.nonce() != self.account_start_nonce + }) + }) + } + + /// Get the balance of account `a`. + pub fn balance(&self, a: &Address) -> TrieResult { + self.ensure_cached(a, RequireCache::None, |a| { + a.as_ref() + .map_or(U256::zero(), |account| *account.balance()) + }) + } + + /// Get the nonce of account `a`. + pub fn nonce(&self, a: &Address) -> TrieResult { + self.ensure_cached(a, RequireCache::None, |a| { + a.as_ref() + .map_or(self.account_start_nonce, |account| *account.nonce()) + }) + } + + /// Whether the base storage root of an account remains unchanged. + pub fn is_base_storage_root_unchanged(&self, a: &Address) -> TrieResult { + Ok(self + .ensure_cached(a, RequireCache::None, |a| { + a.as_ref() + .map(|account| account.is_base_storage_root_unchanged()) + })? + .unwrap_or(true)) + } + + /// Get the storage root of account `a`. + pub fn storage_root(&self, a: &Address) -> TrieResult> { + self.ensure_cached(a, RequireCache::None, |a| { + a.as_ref().and_then(|account| account.storage_root()) + }) + } + + /// Get the original storage root since last commit of account `a`. + pub fn original_storage_root(&self, a: &Address) -> TrieResult { + Ok(self + .ensure_cached(a, RequireCache::None, |a| { + a.as_ref().map(|account| account.original_storage_root()) + })? + .unwrap_or(KECCAK_NULL_RLP)) + } + + /// Get the value of storage at a specific checkpoint. + pub fn checkpoint_storage_at( + &self, + start_checkpoint_index: usize, + address: &Address, + key: &H256, + ) -> TrieResult> { + #[must_use] + enum ReturnKind { + /// Use original storage at value at this address. + OriginalAt, + /// The checkpoint storage value is the same as the checkpoint storage value at the next checkpoint. + SameAsNext, + } + + let kind = { + let checkpoints = self.checkpoints.borrow(); + + if start_checkpoint_index >= checkpoints.len() { + // The checkpoint was not found. Return None. + return Ok(None); + } + + let mut kind = None; + + for checkpoint in checkpoints.iter().skip(start_checkpoint_index) { + match checkpoint.get(address) { + // The account exists at this checkpoint. + Some(Some(AccountEntry { + account: Some(ref account), + .. + })) => { + if let Some(value) = account.cached_storage_at(key) { + return Ok(Some(value)); + } else { + // This account has checkpoint entry, but the key is not in the entry's cache. We can use + // original_storage_at if current account's original storage root is the same as checkpoint + // account's original storage root. Otherwise, the account must be a newly created contract. + if account.base_storage_root() == self.original_storage_root(address)? { + kind = Some(ReturnKind::OriginalAt); + break; + } else { + // If account base storage root is different from the original storage root since last + // commit, then it can only be created from a new contract, where the base storage root + // would always be empty. Note that this branch is actually never called, because + // `cached_storage_at` handled this case. + warn!(target: "state", "Trying to get an account's cached storage value, but base storage root does not equal to original storage root! Assuming the value is empty."); + return Ok(Some(H256::new())); + } + } + } + // The account didn't exist at that point. Return empty value. + Some(Some(AccountEntry { account: None, .. })) => return Ok(Some(H256::new())), + // The value was not cached at that checkpoint, meaning it was not modified at all. + Some(None) => { + kind = Some(ReturnKind::OriginalAt); + break; + } + // This key does not have a checkpoint entry. + None => { + kind = Some(ReturnKind::SameAsNext); + } + } + } + + kind.expect("start_checkpoint_index is checked to be below checkpoints_len; for loop above must have been executed at least once; it will either early return, or set the kind value to Some; qed") + }; + + match kind { + ReturnKind::SameAsNext => { + // If we reached here, all previous SameAsNext failed to early return. It means that the value we want + // to fetch is the same as current. + Ok(Some(self.storage_at(address, key)?)) + } + ReturnKind::OriginalAt => Ok(Some(self.original_storage_at(address, key)?)), + } + } + + fn storage_at_inner( + &self, + address: &Address, + key: &H256, + f_cached_at: FCachedStorageAt, + f_at: FStorageAt, + ) -> TrieResult + where + FCachedStorageAt: Fn(&Account, &H256) -> Option, + FStorageAt: Fn(&Account, &dyn HashDB, &H256) -> TrieResult, + { + // Storage key search and update works like this: + // 1. If there's an entry for the account in the local cache check for the key and return it if found. + // 2. If there's an entry for the account in the global cache check for the key or load it into that account. + // 3. If account is missing in the global cache load it into the local cache and cache the key there. + + { + // check local cache first without updating + let local_cache = self.cache.borrow_mut(); + let mut local_account = None; + if let Some(maybe_acc) = local_cache.get(address) { + match maybe_acc.account { + Some(ref account) => { + if let Some(value) = f_cached_at(account, key) { + return Ok(value); + } else { + local_account = Some(maybe_acc); + } + } + _ => return Ok(H256::new()), + } + } + // check the global cache and and cache storage key there if found, + let trie_res = self.db.get_cached(address, |acc| match acc { + None => Ok(H256::new()), + Some(a) => { + let account_db = self + .factories + .accountdb + .readonly(self.db.as_hash_db(), a.address_hash(address)); + f_at(a, account_db.as_hash_db(), key) + } + }); + + if let Some(res) = trie_res { + return res; + } + + // otherwise cache the account localy and cache storage key there. + if let Some(ref mut acc) = local_account { + if let Some(ref account) = acc.account { + let account_db = self + .factories + .accountdb + .readonly(self.db.as_hash_db(), account.address_hash(address)); + return f_at(account, account_db.as_hash_db(), key); + } else { + return Ok(H256::new()); + } + } + } + + // account is not found in the global cache, get from the DB and insert into local + let db = &self.db.as_hash_db(); + let db = self + .factories + .trie + .readonly(db, &self.root) + .expect(SEC_TRIE_DB_UNWRAP_STR); + let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); + let maybe_acc = db.get_with(address, from_rlp)?; + let r = maybe_acc.as_ref().map_or(Ok(H256::new()), |a| { + let account_db = self + .factories + .accountdb + .readonly(self.db.as_hash_db(), a.address_hash(address)); + f_at(a, account_db.as_hash_db(), key) + }); + self.insert_cache(address, AccountEntry::new_clean(maybe_acc)); + r + } + + /// Mutate storage of account `address` so that it is `value` for `key`. + pub fn storage_at(&self, address: &Address, key: &H256) -> TrieResult { + self.storage_at_inner( + address, + key, + |account, key| account.cached_storage_at(key), + |account, db, key| account.storage_at(db, key), + ) + } + + /// Get the value of storage after last state commitment. + pub fn original_storage_at(&self, address: &Address, key: &H256) -> TrieResult { + self.storage_at_inner( + address, + key, + |account, key| account.cached_original_storage_at(key), + |account, db, key| account.original_storage_at(db, key), + ) + } + + /// Get accounts' code. + pub fn code(&self, a: &Address) -> TrieResult>> { + self.ensure_cached(a, RequireCache::Code, |a| { + a.as_ref().map_or(None, |a| a.code().clone()) + }) + } + + /// Get an account's code hash. + pub fn code_hash(&self, a: &Address) -> TrieResult> { + self.ensure_cached(a, RequireCache::None, |a| a.as_ref().map(|a| a.code_hash())) + } + + /// Get accounts' code size. + pub fn code_size(&self, a: &Address) -> TrieResult> { + self.ensure_cached(a, RequireCache::CodeSize, |a| { + a.as_ref().and_then(|a| a.code_size()) + }) + } + + /// Add `incr` to the balance of account `a`. + pub fn add_balance( + &mut self, + a: &Address, + incr: &U256, + cleanup_mode: CleanupMode, + ) -> TrieResult<()> { + trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a)?); + let is_value_transfer = !incr.is_zero(); + if is_value_transfer || (cleanup_mode == CleanupMode::ForceCreate && !self.exists(a)?) { + self.require(a, false)?.add_balance(incr); + } else if let CleanupMode::TrackTouched(set) = cleanup_mode { + if self.exists(a)? { + set.insert(*a); + self.touch(a)?; + } + } + Ok(()) + } + + /// Subtract `decr` from the balance of account `a`. + pub fn sub_balance( + &mut self, + a: &Address, + decr: &U256, + cleanup_mode: &mut CleanupMode, + ) -> TrieResult<()> { + trace!(target: "state", "sub_balance({}, {}): {}", a, decr, self.balance(a)?); + if !decr.is_zero() || !self.exists(a)? { + self.require(a, false)?.sub_balance(decr); + } + if let CleanupMode::TrackTouched(ref mut set) = *cleanup_mode { + set.insert(*a); + } + Ok(()) + } + + /// Subtracts `by` from the balance of `from` and adds it to that of `to`. + pub fn transfer_balance( + &mut self, + from: &Address, + to: &Address, + by: &U256, + mut cleanup_mode: CleanupMode, + ) -> TrieResult<()> { + self.sub_balance(from, by, &mut cleanup_mode)?; + self.add_balance(to, by, cleanup_mode)?; + Ok(()) + } + + /// Increment the nonce of account `a` by 1. + pub fn inc_nonce(&mut self, a: &Address) -> TrieResult<()> { + self.require(a, false).map(|mut x| x.inc_nonce()) + } + + /// Mutate storage of account `a` so that it is `value` for `key`. + pub fn set_storage(&mut self, a: &Address, key: H256, value: H256) -> TrieResult<()> { + trace!(target: "state", "set_storage({}:{:x} to {:x})", a, key, value); + if self.storage_at(a, &key)? != value { + self.require(a, false)?.set_storage(key, value) + } + + Ok(()) + } + + /// Initialise the code of account `a` so that it is `code`. + /// NOTE: Account should have been created with `new_contract`. + pub fn init_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { + self.require_or_from( + a, + true, + || Account::new_contract(0.into(), self.account_start_nonce, KECCAK_NULL_RLP), + |_| {}, + )? + .init_code(code); + Ok(()) + } + + /// Reset the code of account `a` so that it is `code`. + pub fn reset_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { + self.require_or_from( + a, + true, + || Account::new_contract(0.into(), self.account_start_nonce, KECCAK_NULL_RLP), + |_| {}, + )? + .reset_code(code); + Ok(()) + } + + /// Execute a given transaction, producing a receipt and an optional trace. + /// This will change the state accordingly. + pub fn apply( + &mut self, + env_info: &EnvInfo, + machine: &Machine, + t: &SignedTransaction, + tracing: bool, + ) -> ApplyResult { + if tracing { + let options = TransactOptions::with_tracing(); + self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) + } else { + let options = TransactOptions::with_no_tracing(); + self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) + } + } + + /// Execute a given transaction with given tracer and VM tracer producing a receipt and an optional trace. + /// This will change the state accordingly. + pub fn apply_with_tracing( + &mut self, + env_info: &EnvInfo, + machine: &Machine, + t: &SignedTransaction, + tracer: T, + vm_tracer: V, + ) -> ApplyResult + where + T: trace::Tracer, + V: trace::VMTracer, + { + let options = TransactOptions::new(tracer, vm_tracer); + let e = self.execute(env_info, machine, t, options, false)?; + let params = machine.params(); + + let eip658 = env_info.number >= params.eip658_transition; + let no_intermediate_commits = eip658 + || (env_info.number >= params.eip98_transition + && env_info.number >= params.validate_receipts_transition); + + let outcome = if no_intermediate_commits { + if eip658 { + TransactionOutcome::StatusCode(if e.exception.is_some() { 0 } else { 1 }) + } else { + TransactionOutcome::Unknown + } + } else { + self.commit()?; + TransactionOutcome::StateRoot(self.root().clone()) + }; + + let output = e.output; + let receipt = Receipt::new(outcome, e.cumulative_gas_used, e.logs); + trace!(target: "state", "Transaction receipt: {:?}", receipt); + + Ok(ApplyOutcome { + receipt, + output, + trace: e.trace, + vm_trace: e.vm_trace, + }) + } + + // Execute a given transaction without committing changes. + // + // `virt` signals that we are executing outside of a block set and restrictions like + // gas limits and gas costs should be lifted. + fn execute( + &mut self, + env_info: &EnvInfo, + machine: &Machine, + t: &SignedTransaction, + options: TransactOptions, + virt: bool, + ) -> Result, ExecutionError> + where + T: trace::Tracer, + V: trace::VMTracer, + { + let schedule = machine.schedule(env_info.number); + let mut e = Executive::new(self, env_info, machine, &schedule); + + match virt { + true => e.transact_virtual(t, options), + false => e.transact(t, options), + } + } + + fn touch(&mut self, a: &Address) -> TrieResult<()> { + self.require(a, false)?; + Ok(()) + } + + /// Commits our cached account changes into the trie. + pub fn commit(&mut self) -> Result<(), Error> { + assert!(self.checkpoints.borrow().is_empty()); + // first, commit the sub trees. + let mut accounts = self.cache.borrow_mut(); + for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) { + if let Some(ref mut account) = a.account { + let addr_hash = account.address_hash(address); + { + let mut account_db = self + .factories + .accountdb + .create(self.db.as_hash_db_mut(), addr_hash); + account.commit_storage(&self.factories.trie, account_db.as_hash_db_mut())?; + account.commit_code(account_db.as_hash_db_mut()); + } + } + } + + { + let mut trie = self + .factories + .trie + .from_existing(self.db.as_hash_db_mut(), &mut self.root)?; + for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) { + a.state = AccountState::Committed; + match a.account { + Some(ref mut account) => { + trie.insert(address, &account.rlp())?; + } + None => { + trie.remove(address)?; + } + }; + } + } + + Ok(()) + } + + /// Propagate local cache into shared canonical state cache. + fn propagate_to_global_cache(&mut self) { + let mut addresses = self.cache.borrow_mut(); + trace!("Committing cache {:?} entries", addresses.len()); + for (address, a) in addresses.drain().filter(|&(_, ref a)| { + a.state == AccountState::Committed || a.state == AccountState::CleanFresh + }) { + self.db + .add_to_account_cache(address, a.account, a.state == AccountState::Committed); + } + } + + /// Clear state cache + pub fn clear(&mut self) { + assert!(self.checkpoints.borrow().is_empty()); + self.cache.borrow_mut().clear(); + } + + /// Remove any touched empty or dust accounts. + pub fn kill_garbage( + &mut self, + touched: &HashSet
, + remove_empty_touched: bool, + min_balance: &Option, + kill_contracts: bool, + ) -> TrieResult<()> { + let to_kill: HashSet<_> = { + self.cache.borrow().iter().filter_map(|(address, ref entry)| if touched.contains(address) && // Check all touched accounts ((remove_empty_touched && entry.exists_and_is_null()) // Remove all empty touched accounts. || min_balance.map_or(false, |ref balance| entry.account.as_ref().map_or(false, |account| @@ -943,1825 +1072,2388 @@ impl State { Some(address.clone()) } else { None }).collect() - }; - for address in to_kill { - self.kill_account(&address); - } - Ok(()) - } - - /// Populate the state from `accounts`. - /// Used for tests. - pub fn populate_from(&mut self, accounts: PodState) { - assert!(self.checkpoints.borrow().is_empty()); - for (add, acc) in accounts.drain().into_iter() { - self.cache.borrow_mut().insert(add, AccountEntry::new_dirty(Some(Account::from_pod(acc)))); - } - } - - /// Populate a PodAccount map from this state. - fn to_pod_cache(&self) -> PodState { - assert!(self.checkpoints.borrow().is_empty()); - PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| { - if let Some(ref acc) = opt.account { - m.insert(*add, PodAccount::from_account(acc)); - } - m - })) - } - - #[cfg(feature="to-pod-full")] - /// Populate a PodAccount map from this state. - /// Warning this is not for real time use. - /// Use of this method requires FatDB mode to be able - /// to iterate on accounts. - pub fn to_pod_full(&self) -> Result { - - assert!(self.checkpoints.borrow().is_empty()); - assert!(self.factories.trie.is_fat()); - - let mut result = BTreeMap::new(); - - let db = &self.db.as_hash_db(); - let trie = self.factories.trie.readonly(db, &self.root)?; - - // put trie in cache - for item in trie.iter()? { - if let Ok((addr, _dbval)) = item { - let address = Address::from_slice(&addr); - let _ = self.require(&address, true); - } - } - - // Resolve missing part - for (add, opt) in self.cache.borrow().iter() { - if let Some(ref acc) = opt.account { - let pod_account = self.account_to_pod_account(acc, add)?; - result.insert(add.clone(), pod_account); - } - } - - Ok(PodState::from(result)) - } - - /// Create a PodAccount from an account. - /// Differs from existing method by including all storage - /// values of the account to the PodAccount. - /// This function is only intended for use in small tests or with fresh accounts. - /// It requires FatDB. - #[cfg(feature="to-pod-full")] - fn account_to_pod_account(&self, account: &Account, address: &Address) -> Result { - let mut pod_storage = BTreeMap::new(); - let addr_hash = account.address_hash(address); - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), addr_hash); - let root = account.base_storage_root(); - - let accountdb = &accountdb.as_hash_db(); - let trie = self.factories.trie.readonly(accountdb, &root)?; - for o_kv in trie.iter()? { - if let Ok((key, val)) = o_kv { - pod_storage.insert(key[..].into(), rlp::decode::(&val[..]).expect("Decoded from trie which was encoded from the same type; qed").into()); - } - } - - let mut pod_account = PodAccount::from_account(&account); - // cached one first - pod_storage.append(&mut pod_account.storage); - pod_account.storage = pod_storage; - Ok(pod_account) - } - - /// Populate a PodAccount map from this state, with another state as the account and storage query. - fn to_pod_diff(&mut self, query: &State) -> TrieResult { - assert!(self.checkpoints.borrow().is_empty()); - - // Merge PodAccount::to_pod for cache of self and `query`. - let all_addresses = self.cache.borrow().keys().cloned() - .chain(query.cache.borrow().keys().cloned()) - .collect::>(); - - Ok(PodState::from(all_addresses.into_iter().fold(Ok(BTreeMap::new()), |m: TrieResult<_>, address| { - let mut m = m?; - - let account = self.ensure_cached(&address, RequireCache::Code, true, |acc| { - acc.map(|acc| { - // Merge all modified storage keys. - let all_keys = { - let self_keys = acc.storage_changes().keys().cloned() - .collect::>(); - - if let Some(ref query_storage) = query.cache.borrow().get(&address) - .and_then(|opt| { - Some(opt.account.as_ref()?.storage_changes().keys().cloned() - .collect::>()) - }) - { - self_keys.union(&query_storage).cloned().collect::>() - } else { - self_keys.into_iter().collect::>() - } - }; - - // Storage must be fetched after ensure_cached to avoid borrow problem. - (*acc.balance(), *acc.nonce(), all_keys, acc.code().map(|x| x.to_vec())) - }) - })?; - - if let Some((balance, nonce, storage_keys, code)) = account { - let storage = storage_keys.into_iter().fold(Ok(BTreeMap::new()), |s: TrieResult<_>, key| { - let mut s = s?; - - s.insert(key, self.storage_at(&address, &key)?); - Ok(s) - })?; - - m.insert(address, PodAccount { - balance, nonce, storage, code - }); - } - - Ok(m) - })?)) - } - - /// Returns a `StateDiff` describing the difference from `orig` to `self`. - /// Consumes self. - pub fn diff_from(&self, mut orig: State) -> TrieResult { - let pod_state_post = self.to_pod_cache(); - let pod_state_pre = orig.to_pod_diff(self)?; - Ok(pod_state::diff_pod(&pod_state_pre, &pod_state_post)) - } - - /// Load required account data from the databases. Returns whether the cache succeeds. - #[must_use] - fn update_account_cache(require: RequireCache, account: &mut Account, state_db: &B, db: &HashDB) -> bool { - if let RequireCache::None = require { - return true; - } - - if account.is_cached() { - return true; - } - - // if there's already code in the global cache, always cache it localy - let hash = account.code_hash(); - match state_db.get_cached_code(&hash) { - Some(code) => { - account.cache_given_code(code); - true - }, - None => match require { - RequireCache::None => true, - RequireCache::Code => { - if let Some(code) = account.cache_code(db) { - // propagate code loaded from the database to - // the global code cache. - state_db.cache_code(hash, code); - true - } else { - false - } - }, - RequireCache::CodeSize => { - account.cache_code_size(db) - } - } - } - } - - /// Check caches for required data - /// First searches for account in the local, then the shared cache. - /// Populates local cache if nothing found. - fn ensure_cached(&self, a: &Address, require: RequireCache, check_null: bool, f: F) -> TrieResult - where F: Fn(Option<&Account>) -> U { - // check local cache first - if let Some(ref mut maybe_acc) = self.cache.borrow_mut().get_mut(a) { - if let Some(ref mut account) = maybe_acc.account { - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); - if Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { - return Ok(f(Some(account))); - } else { - return Err(Box::new(TrieError::IncompleteDatabase(H256::from(a)))); - } - } - return Ok(f(None)); - } - // check global cache - let result = self.db.get_cached(a, |mut acc| { - if let Some(ref mut account) = acc { - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); - if !Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { - return Err(Box::new(TrieError::IncompleteDatabase(H256::from(a)))); - } - } - Ok(f(acc.map(|a| &*a))) - }); - match result { - Some(r) => Ok(r?), - None => { - // first check if it is not in database for sure - if check_null && self.db.is_known_null(a) { return Ok(f(None)); } - - // not found in the global cache, get from the DB and insert into local - let db = &self.db.as_hash_db(); - let db = self.factories.trie.readonly(db, &self.root)?; - let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); - let mut maybe_acc = db.get_with(a, from_rlp)?; - if let Some(ref mut account) = maybe_acc.as_mut() { - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); - if !Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { - return Err(Box::new(TrieError::IncompleteDatabase(H256::from(a)))); - } - } - let r = f(maybe_acc.as_ref()); - self.insert_cache(a, AccountEntry::new_clean(maybe_acc)); - Ok(r) - } - } - } - - /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. - fn require<'a>(&'a self, a: &Address, require_code: bool) -> TrieResult> { - self.require_or_from(a, require_code, || Account::new_basic(0u8.into(), self.account_start_nonce), |_| {}) - } - - /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. - /// If it doesn't exist, make account equal the evaluation of `default`. - fn require_or_from<'a, F, G>(&'a self, a: &Address, require_code: bool, default: F, not_default: G) -> TrieResult> - where F: FnOnce() -> Account, G: FnOnce(&mut Account), - { - let contains_key = self.cache.borrow().contains_key(a); - if !contains_key { - match self.db.get_cached_account(a) { - Some(acc) => self.insert_cache(a, AccountEntry::new_clean_cached(acc)), - None => { - let maybe_acc = if !self.db.is_known_null(a) { - let db = &self.db.as_hash_db(); - let db = self.factories.trie.readonly(db, &self.root)?; - let from_rlp = |b:&[u8]| { Account::from_rlp(b).expect("decoding db value failed") }; - AccountEntry::new_clean(db.get_with(a, from_rlp)?) - } else { - AccountEntry::new_clean(None) - }; - self.insert_cache(a, maybe_acc); - } - } - } - self.note_cache(a); - - // at this point the entry is guaranteed to be in the cache. - let mut account = RefMut::map(self.cache.borrow_mut(), |c| { - let entry = c.get_mut(a).expect("entry known to exist in the cache; qed"); - - match &mut entry.account { - &mut Some(ref mut acc) => not_default(acc), - slot => *slot = Some(default()), - } - - // set the dirty flag after changing account data. - entry.state = AccountState::Dirty; - entry.account.as_mut().expect("Required account must always exist; qed") - }); - - if require_code { - let addr_hash = account.address_hash(a); - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), addr_hash); - - if !Self::update_account_cache(RequireCache::Code, &mut account, &self.db, accountdb.as_hash_db()) { - return Err(Box::new(TrieError::IncompleteDatabase(H256::from(a)))) - } - } - - Ok(account) - } - - /// Replace account code and storage. Creates account if it does not exist. - pub fn patch_account(&self, a: &Address, code: Arc, storage: HashMap) -> TrieResult<()> { - Ok(self.require(a, false)?.reset_code_and_storage(code, storage)) - } + }; + for address in to_kill { + self.kill_account(&address); + } + Ok(()) + } + + /// Populate the state from `accounts`. + /// Used for tests. + pub fn populate_from(&mut self, accounts: PodState) { + assert!(self.checkpoints.borrow().is_empty()); + for (add, acc) in accounts.drain().into_iter() { + self.cache + .borrow_mut() + .insert(add, AccountEntry::new_dirty(Some(Account::from_pod(acc)))); + } + } + + /// Populate a PodAccount map from this state. + fn to_pod_cache(&self) -> PodState { + assert!(self.checkpoints.borrow().is_empty()); + PodState::from( + self.cache + .borrow() + .iter() + .fold(BTreeMap::new(), |mut m, (add, opt)| { + if let Some(ref acc) = opt.account { + m.insert(*add, PodAccount::from_account(acc)); + } + m + }), + ) + } + + #[cfg(feature = "to-pod-full")] + /// Populate a PodAccount map from this state. + /// Warning this is not for real time use. + /// Use of this method requires FatDB mode to be able + /// to iterate on accounts. + pub fn to_pod_full(&self) -> Result { + assert!(self.checkpoints.borrow().is_empty()); + assert!(self.factories.trie.is_fat()); + + let mut result = BTreeMap::new(); + + let db = &self.db.as_hash_db(); + let trie = self.factories.trie.readonly(db, &self.root)?; + + // put trie in cache + for item in trie.iter()? { + if let Ok((addr, _dbval)) = item { + let address = Address::from_slice(&addr); + let _ = self.require(&address, true); + } + } + + // Resolve missing part + for (add, opt) in self.cache.borrow().iter() { + if let Some(ref acc) = opt.account { + let pod_account = self.account_to_pod_account(acc, add)?; + result.insert(add.clone(), pod_account); + } + } + + Ok(PodState::from(result)) + } + + /// Create a PodAccount from an account. + /// Differs from existing method by including all storage + /// values of the account to the PodAccount. + /// This function is only intended for use in small tests or with fresh accounts. + /// It requires FatDB. + #[cfg(feature = "to-pod-full")] + fn account_to_pod_account( + &self, + account: &Account, + address: &Address, + ) -> Result { + let mut pod_storage = BTreeMap::new(); + let addr_hash = account.address_hash(address); + let accountdb = self + .factories + .accountdb + .readonly(self.db.as_hash_db(), addr_hash); + let root = account.base_storage_root(); + + let accountdb = &accountdb.as_hash_db(); + let trie = self.factories.trie.readonly(accountdb, &root)?; + for o_kv in trie.iter()? { + if let Ok((key, val)) = o_kv { + pod_storage.insert( + key[..].into(), + rlp::decode::(&val[..]) + .expect("Decoded from trie which was encoded from the same type; qed") + .into(), + ); + } + } + + let mut pod_account = PodAccount::from_account(&account); + // cached one first + pod_storage.append(&mut pod_account.storage); + pod_account.storage = pod_storage; + Ok(pod_account) + } + + /// Populate a PodAccount map from this state, with another state as the account and storage query. + fn to_pod_diff(&mut self, query: &State) -> TrieResult { + assert!(self.checkpoints.borrow().is_empty()); + + // Merge PodAccount::to_pod for cache of self and `query`. + let all_addresses = self + .cache + .borrow() + .keys() + .cloned() + .chain(query.cache.borrow().keys().cloned()) + .collect::>(); + + Ok(PodState::from(all_addresses.into_iter().fold( + Ok(BTreeMap::new()), + |m: TrieResult<_>, address| { + let mut m = m?; + + let account = self.ensure_cached(&address, RequireCache::Code, |acc| { + acc.map(|acc| { + // Merge all modified storage keys. + let all_keys = { + let self_keys = acc + .storage_changes() + .keys() + .cloned() + .collect::>(); + + if let Some(ref query_storage) = + query.cache.borrow().get(&address).and_then(|opt| { + Some( + opt.account + .as_ref()? + .storage_changes() + .keys() + .cloned() + .collect::>(), + ) + }) + { + self_keys.union(&query_storage).cloned().collect::>() + } else { + self_keys.into_iter().collect::>() + } + }; + + // Storage must be fetched after ensure_cached to avoid borrow problem. + ( + *acc.balance(), + *acc.nonce(), + all_keys, + acc.code().map(|x| x.to_vec()), + ) + }) + })?; + + if let Some((balance, nonce, storage_keys, code)) = account { + let storage = storage_keys.into_iter().fold( + Ok(BTreeMap::new()), + |s: TrieResult<_>, key| { + let mut s = s?; + + s.insert(key, self.storage_at(&address, &key)?); + Ok(s) + }, + )?; + + m.insert( + address, + PodAccount { + balance, + nonce, + storage, + code, + }, + ); + } + + Ok(m) + }, + )?)) + } + + /// Returns a `StateDiff` describing the difference from `orig` to `self`. + /// Consumes self. + pub fn diff_from(&self, mut orig: State) -> TrieResult { + let pod_state_post = self.to_pod_cache(); + let pod_state_pre = orig.to_pod_diff(self)?; + Ok(pod_state::diff_pod(&pod_state_pre, &pod_state_post)) + } + + /// Load required account data from the databases. Returns whether the cache succeeds. + #[must_use] + fn update_account_cache( + require: RequireCache, + account: &mut Account, + state_db: &B, + db: &dyn HashDB, + ) -> bool { + if let RequireCache::None = require { + return true; + } + + if account.is_cached() { + return true; + } + + // if there's already code in the global cache, always cache it localy + let hash = account.code_hash(); + match state_db.get_cached_code(&hash) { + Some(code) => { + account.cache_given_code(code); + true + } + None => match require { + RequireCache::None => true, + RequireCache::Code => { + if let Some(code) = account.cache_code(db) { + // propagate code loaded from the database to + // the global code cache. + state_db.cache_code(hash, code); + true + } else { + false + } + } + RequireCache::CodeSize => account.cache_code_size(db), + }, + } + } + + /// Check caches for required data + /// First searches for account in the local, then the shared cache. + /// Populates local cache if nothing found. + fn ensure_cached(&self, a: &Address, require: RequireCache, f: F) -> TrieResult + where + F: Fn(Option<&Account>) -> U, + { + // check local cache first + if let Some(ref mut maybe_acc) = self.cache.borrow_mut().get_mut(a) { + if let Some(ref mut account) = maybe_acc.account { + let accountdb = self + .factories + .accountdb + .readonly(self.db.as_hash_db(), account.address_hash(a)); + if Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { + return Ok(f(Some(account))); + } else { + return Err(Box::new(TrieError::IncompleteDatabase(H256::from(a)))); + } + } + return Ok(f(None)); + } + // check global cache + let result = self.db.get_cached(a, |mut acc| { + if let Some(ref mut account) = acc { + let accountdb = self + .factories + .accountdb + .readonly(self.db.as_hash_db(), account.address_hash(a)); + if !Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { + return Err(Box::new(TrieError::IncompleteDatabase(H256::from(a)))); + } + } + Ok(f(acc.map(|a| &*a))) + }); + match result { + Some(r) => Ok(r?), + None => { + // not found in the global cache, get from the DB and insert into local + let db = &self.db.as_hash_db(); + let db = self.factories.trie.readonly(db, &self.root)?; + let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); + let mut maybe_acc = db.get_with(a, from_rlp)?; + if let Some(ref mut account) = maybe_acc.as_mut() { + let accountdb = self + .factories + .accountdb + .readonly(self.db.as_hash_db(), account.address_hash(a)); + if !Self::update_account_cache( + require, + account, + &self.db, + accountdb.as_hash_db(), + ) { + return Err(Box::new(TrieError::IncompleteDatabase(H256::from(a)))); + } + } + let r = f(maybe_acc.as_ref()); + self.insert_cache(a, AccountEntry::new_clean(maybe_acc)); + Ok(r) + } + } + } + + /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. + fn require<'a>(&'a self, a: &Address, require_code: bool) -> TrieResult> { + self.require_or_from( + a, + require_code, + || Account::new_basic(0u8.into(), self.account_start_nonce), + |_| {}, + ) + } + + /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. + /// If it doesn't exist, make account equal the evaluation of `default`. + fn require_or_from<'a, F, G>( + &'a self, + a: &Address, + require_code: bool, + default: F, + not_default: G, + ) -> TrieResult> + where + F: FnOnce() -> Account, + G: FnOnce(&mut Account), + { + let contains_key = self.cache.borrow().contains_key(a); + if !contains_key { + match self.db.get_cached_account(a) { + Some(acc) => self.insert_cache(a, AccountEntry::new_clean_cached(acc)), + None => { + let db = &self.db.as_hash_db(); + let db = self.factories.trie.readonly(db, &self.root)?; + let from_rlp = + |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); + let maybe_acc = AccountEntry::new_clean(db.get_with(a, from_rlp)?); + self.insert_cache(a, maybe_acc); + } + } + } + self.note_cache(a); + + // at this point the entry is guaranteed to be in the cache. + let mut account = RefMut::map(self.cache.borrow_mut(), |c| { + let entry = c + .get_mut(a) + .expect("entry known to exist in the cache; qed"); + + match &mut entry.account { + &mut Some(ref mut acc) => not_default(acc), + slot => *slot = Some(default()), + } + + // set the dirty flag after changing account data. + entry.state = AccountState::Dirty; + entry + .account + .as_mut() + .expect("Required account must always exist; qed") + }); + + if require_code { + let addr_hash = account.address_hash(a); + let accountdb = self + .factories + .accountdb + .readonly(self.db.as_hash_db(), addr_hash); + + if !Self::update_account_cache( + RequireCache::Code, + &mut account, + &self.db, + accountdb.as_hash_db(), + ) { + return Err(Box::new(TrieError::IncompleteDatabase(H256::from(a)))); + } + } + + Ok(account) + } + + /// Replace account code and storage. Creates account if it does not exist. + pub fn patch_account( + &self, + a: &Address, + code: Arc, + storage: HashMap, + ) -> TrieResult<()> { + Ok(self + .require(a, false)? + .reset_code_and_storage(code, storage)) + } } // State proof implementations; useful for light client protocols. impl State { - /// Prove an account's existence or nonexistence in the state trie. - /// Returns a merkle proof of the account's trie node omitted or an encountered trie error. - /// If the account doesn't exist in the trie, prove that and return defaults. - /// Requires a secure trie to be used for accurate results. - /// `account_key` == keccak(address) - pub fn prove_account(&self, account_key: H256) -> TrieResult<(Vec, BasicAccount)> { - let mut recorder = Recorder::new(); - let db = &self.db.as_hash_db(); - let trie = TrieDB::new(db, &self.root)?; - let maybe_account: Option = { - let panicky_decoder = |bytes: &[u8]| { - ::rlp::decode(bytes).unwrap_or_else(|_| panic!("prove_account, could not query trie for account key={}", &account_key)) - }; - let query = (&mut recorder, panicky_decoder); - trie.get_with(&account_key, query)? - }; - let account = maybe_account.unwrap_or_else(|| BasicAccount { - balance: 0.into(), - nonce: self.account_start_nonce, - code_hash: KECCAK_EMPTY, - storage_root: KECCAK_NULL_RLP, - }); - - Ok((recorder.drain().into_iter().map(|r| r.data).collect(), account)) - } - - /// Prove an account's storage key's existence or nonexistence in the state. - /// Returns a merkle proof of the account's storage trie. - /// Requires a secure trie to be used for correctness. - /// `account_key` == keccak(address) - /// `storage_key` == keccak(key) - pub fn prove_storage(&self, account_key: H256, storage_key: H256) -> TrieResult<(Vec, H256)> { - // TODO: probably could look into cache somehow but it's keyed by - // address, not keccak(address). - let db = &self.db.as_hash_db(); - let trie = TrieDB::new(db, &self.root)?; - let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); - let acc = match trie.get_with(&account_key, from_rlp)? { - Some(acc) => acc, - None => return Ok((Vec::new(), H256::new())), - }; - - let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), account_key); - acc.prove_storage(account_db.as_hash_db(), storage_key) - } + /// Prove an account's existence or nonexistence in the state trie. + /// Returns a merkle proof of the account's trie node omitted or an encountered trie error. + /// If the account doesn't exist in the trie, prove that and return defaults. + /// Requires a secure trie to be used for accurate results. + /// `account_key` == keccak(address) + pub fn prove_account(&self, account_key: H256) -> TrieResult<(Vec, BasicAccount)> { + let mut recorder = Recorder::new(); + let db = &self.db.as_hash_db(); + let trie = TrieDB::new(db, &self.root)?; + let maybe_account: Option = { + let panicky_decoder = |bytes: &[u8]| { + ::rlp::decode(bytes).unwrap_or_else(|_| { + panic!( + "prove_account, could not query trie for account key={}", + &account_key + ) + }) + }; + let query = (&mut recorder, panicky_decoder); + trie.get_with(&account_key, query)? + }; + let account = maybe_account.unwrap_or_else(|| BasicAccount { + balance: 0.into(), + nonce: self.account_start_nonce, + code_hash: KECCAK_EMPTY, + storage_root: KECCAK_NULL_RLP, + }); + + Ok(( + recorder.drain().into_iter().map(|r| r.data).collect(), + account, + )) + } + + /// Prove an account's storage key's existence or nonexistence in the state. + /// Returns a merkle proof of the account's storage trie. + /// Requires a secure trie to be used for correctness. + /// `account_key` == keccak(address) + /// `storage_key` == keccak(key) + pub fn prove_storage( + &self, + account_key: H256, + storage_key: H256, + ) -> TrieResult<(Vec, H256)> { + // TODO: probably could look into cache somehow but it's keyed by + // address, not keccak(address). + let db = &self.db.as_hash_db(); + let trie = TrieDB::new(db, &self.root)?; + let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); + let acc = match trie.get_with(&account_key, from_rlp)? { + Some(acc) => acc, + None => return Ok((Vec::new(), H256::new())), + }; + + let account_db = self + .factories + .accountdb + .readonly(self.db.as_hash_db(), account_key); + acc.prove_storage(account_db.as_hash_db(), storage_key) + } } impl fmt::Debug for State { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self.cache.borrow()) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.cache.borrow()) + } } impl State { - /// Get a reference to the underlying state DB. - pub fn db(&self) -> &StateDB { - &self.db - } + /// Get a reference to the underlying state DB. + pub fn db(&self) -> &StateDB { + &self.db + } } // TODO: cloning for `State` shouldn't be possible in general; Remove this and use // checkpoints where possible. impl Clone for State { - fn clone(&self) -> State { - let cache = { - let mut cache: HashMap = HashMap::new(); - for (key, val) in self.cache.borrow().iter() { - if let Some(entry) = val.clone_if_dirty() { - cache.insert(key.clone(), entry); - } - } - cache - }; - - State { - db: self.db.boxed_clone(), - root: self.root.clone(), - cache: RefCell::new(cache), - checkpoints: RefCell::new(Vec::new()), - account_start_nonce: self.account_start_nonce.clone(), - factories: self.factories.clone(), - } - } + fn clone(&self) -> State { + let cache = { + let mut cache: HashMap = HashMap::new(); + for (key, val) in self.cache.borrow().iter() { + if let Some(entry) = val.clone_if_dirty() { + cache.insert(key.clone(), entry); + } + } + cache + }; + + State { + db: self.db.boxed_clone(), + root: self.root.clone(), + cache: RefCell::new(cache), + checkpoints: RefCell::new(Vec::new()), + account_start_nonce: self.account_start_nonce.clone(), + factories: self.factories.clone(), + } + } } #[cfg(test)] mod tests { - use std::sync::Arc; - use std::str::FromStr; - use rustc_hex::FromHex; - use hash::{keccak, KECCAK_NULL_RLP}; - use super::*; - use ethkey::Secret; - use ethereum_types::{H256, U256, Address}; - use test_helpers::{get_temp_state, get_temp_state_db}; - use machine::EthereumMachine; - use vm::EnvInfo; - use spec::*; - use types::transaction::*; - use trace::{FlatTrace, TraceError, trace}; - use evm::CallType; - - fn secret() -> Secret { - keccak("").into() - } - - fn make_frontier_machine(max_depth: usize) -> EthereumMachine { - let mut machine = ::ethereum::new_frontier_test_machine(); - machine.set_schedule_creation_rules(Box::new(move |s, _| s.max_depth = max_depth)); - machine - } - - #[test] - fn should_apply_create_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Create, - value: 100.into(), - data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555").unwrap(), - }.sign(&secret(), None); - - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 0, - action: trace::Action::Create(trace::Create { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - value: 100.into(), - gas: 77412.into(), - init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85], - }), - result: trace::Res::Create(trace::CreateResult { - gas_used: U256::from(3224), - address: Address::from_str("8988167e088c87cd314df6d3c2b83da5acb93ace").unwrap(), - code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] - }), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_work_when_cloned() { - let _ = env_logger::try_init(); - - let a = Address::zero(); - - let mut state = { - let mut state = get_temp_state(); - assert_eq!(state.exists(&a).unwrap(), false); - state.inc_nonce(&a).unwrap(); - state.commit().unwrap(); - state.clone() - }; - - state.inc_nonce(&a).unwrap(); - state.commit().unwrap(); - } - - #[test] - fn should_trace_failed_create_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Create, - value: 100.into(), - data: FromHex::from_hex("5b600056").unwrap(), - }.sign(&secret(), None); - - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Create(trace::Create { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - value: 100.into(), - gas: 78792.into(), - init: vec![91, 96, 0, 86], - }), - result: trace::Res::FailedCreate(TraceError::OutOfGas), - subtraces: 0 - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_call_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("6000").unwrap()).unwrap(); - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_basic_call_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(0), - output: vec![] - }), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_call_transaction_to_builtin() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = Spec::new_test_machine(); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0x1.into()), - value: 0.into(), - data: vec![], - }.sign(&secret(), None); - - let result = state.apply(&info, &machine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: "0000000000000000000000000000000000000001".into(), - value: 0.into(), - gas: 79_000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3000), - output: vec![] - }), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_not_trace_subcall_transaction_to_builtin() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = Spec::new_test_machine(); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 0.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap()).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 0.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3_721), // in post-eip150 - output: vec![] - }), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_callcode_properly() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = Spec::new_test_machine(); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 0.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b611000f2").unwrap()).unwrap(); - state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 0.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: 724.into(), // in post-eip150 - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 4096.into(), - input: vec![], - call_type: CallType::CallCode, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: 3.into(), - output: vec![], - }), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_delegatecall_properly() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - info.number = 0x789b0; - let machine = Spec::new_test_machine(); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 0.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("6000600060006000600b618000f4").unwrap()).unwrap(); - state.init_code(&0xb.into(), FromHex::from_hex("60056000526001601ff3").unwrap()).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 0.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(736), // in post-eip150 - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 32768.into(), - input: vec![], - call_type: CallType::DelegateCall, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: 18.into(), - output: vec![5], - }), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_failed_call_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("5b600056").unwrap()).unwrap(); - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::FailedCall(TraceError::OutOfGas), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_call_with_subcall_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); - state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()).unwrap(); - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(69), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_call_with_basic_subcall_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006045600b6000f1").unwrap()).unwrap(); - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(31761), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 69.into(), - gas: 2300.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult::default()), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_not_trace_call_with_invalid_basic_subcall_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()).unwrap(); // not enough funds. - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(31761), - output: vec![] - }), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_failed_subcall_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![],//600480600b6000396000f35b600056 - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); - state.init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap()).unwrap(); - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(79_000), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::FailedCall(TraceError::OutOfGas), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_call_with_subcall_with_subcall_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); - state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap()).unwrap(); - state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()).unwrap(); - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(135), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(69), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0, 0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xb.into(), - to: 0xc.into(), - value: 0.into(), - gas: 78868.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_failed_subcall_with_subcall_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![],//600480600b6000396000f35b600056 - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); - state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap()).unwrap(); - state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()).unwrap(); - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(79_000), - output: vec![] - }) - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::FailedCall(TraceError::OutOfGas), - }, FlatTrace { - trace_address: vec![0, 0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xb.into(), - to: 0xc.into(), - value: 0.into(), - gas: 78868.into(), - call_type: CallType::Call, - input: vec![], - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_suicide() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap()).unwrap(); - state.add_balance(&0xa.into(), &50.into(), CleanupMode::NoEmpty).unwrap(); - state.add_balance(&t.sender(), &100.into(), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: 3.into(), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Suicide(trace::Suicide { - address: 0xa.into(), - refund_address: 0xb.into(), - balance: 150.into(), - }), - result: trace::Res::None, - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn code_from_database() { - let a = Address::zero(); - let (root, db) = { - let mut state = get_temp_state(); - state.require_or_from(&a, false, || Account::new_contract(42.into(), 0.into(), KECCAK_NULL_RLP), |_|{}).unwrap(); - state.init_code(&a, vec![1, 2, 3]).unwrap(); - assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3]))); - state.commit().unwrap(); - assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3]))); - state.drop() - }; - - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3]))); - } - - #[test] - fn storage_at_from_database() { - let a = Address::zero(); - let (root, db) = { - let mut state = get_temp_state(); - state.set_storage(&a, H256::from(&U256::from(1u64)), H256::from(&U256::from(69u64))).unwrap(); - state.commit().unwrap(); - state.drop() - }; - - let s = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(s.storage_at(&a, &H256::from(&U256::from(1u64))).unwrap(), H256::from(&U256::from(69u64))); - } - - #[test] - fn get_from_database() { - let a = Address::zero(); - let (root, db) = { - let mut state = get_temp_state(); - state.inc_nonce(&a).unwrap(); - state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); - state.commit().unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - state.drop() - }; - - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); - } - - #[test] - fn remove() { - let a = Address::zero(); - let mut state = get_temp_state(); - assert_eq!(state.exists(&a).unwrap(), false); - assert_eq!(state.exists_and_not_null(&a).unwrap(), false); - state.inc_nonce(&a).unwrap(); - assert_eq!(state.exists(&a).unwrap(), true); - assert_eq!(state.exists_and_not_null(&a).unwrap(), true); - assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); - state.kill_account(&a); - assert_eq!(state.exists(&a).unwrap(), false); - assert_eq!(state.exists_and_not_null(&a).unwrap(), false); - assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); - } - - #[test] - fn empty_account_is_not_created() { - let a = Address::zero(); - let db = get_temp_state_db(); - let (root, db) = { - let mut state = State::new(db, U256::from(0), Default::default()); - state.add_balance(&a, &U256::default(), CleanupMode::NoEmpty).unwrap(); // create an empty account - state.commit().unwrap(); - state.drop() - }; - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert!(!state.exists(&a).unwrap()); - assert!(!state.exists_and_not_null(&a).unwrap()); - } - - #[test] - fn empty_account_exists_when_creation_forced() { - let a = Address::zero(); - let db = get_temp_state_db(); - let (root, db) = { - let mut state = State::new(db, U256::from(0), Default::default()); - state.add_balance(&a, &U256::default(), CleanupMode::ForceCreate).unwrap(); // create an empty account - state.commit().unwrap(); - state.drop() - }; - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert!(state.exists(&a).unwrap()); - assert!(!state.exists_and_not_null(&a).unwrap()); - } - - #[test] - fn remove_from_database() { - let a = Address::zero(); - let (root, db) = { - let mut state = get_temp_state(); - state.inc_nonce(&a).unwrap(); - state.commit().unwrap(); - assert_eq!(state.exists(&a).unwrap(), true); - assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); - state.drop() - }; - - let (root, db) = { - let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(state.exists(&a).unwrap(), true); - assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); - state.kill_account(&a); - state.commit().unwrap(); - assert_eq!(state.exists(&a).unwrap(), false); - assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); - state.drop() - }; - - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(state.exists(&a).unwrap(), false); - assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); - } - - #[test] - fn alter_balance() { - let mut state = get_temp_state(); - let a = Address::zero(); - let b = 1u64.into(); - state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - state.commit().unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - state.sub_balance(&a, &U256::from(42u64), &mut CleanupMode::NoEmpty).unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(27u64)); - state.commit().unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(27u64)); - state.transfer_balance(&a, &b, &U256::from(18u64), CleanupMode::NoEmpty).unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(9u64)); - assert_eq!(state.balance(&b).unwrap(), U256::from(18u64)); - state.commit().unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(9u64)); - assert_eq!(state.balance(&b).unwrap(), U256::from(18u64)); - } - - #[test] - fn alter_nonce() { - let mut state = get_temp_state(); - let a = Address::zero(); - state.inc_nonce(&a).unwrap(); - assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); - state.inc_nonce(&a).unwrap(); - assert_eq!(state.nonce(&a).unwrap(), U256::from(2u64)); - state.commit().unwrap(); - assert_eq!(state.nonce(&a).unwrap(), U256::from(2u64)); - state.inc_nonce(&a).unwrap(); - assert_eq!(state.nonce(&a).unwrap(), U256::from(3u64)); - state.commit().unwrap(); - assert_eq!(state.nonce(&a).unwrap(), U256::from(3u64)); - } - - #[test] - fn balance_nonce() { - let mut state = get_temp_state(); - let a = Address::zero(); - assert_eq!(state.balance(&a).unwrap(), U256::from(0u64)); - assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); - state.commit().unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(0u64)); - assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); - } - - #[test] - fn ensure_cached() { - let mut state = get_temp_state(); - let a = Address::zero(); - state.require(&a, false).unwrap(); - state.commit().unwrap(); - assert_eq!(*state.root(), "0ce23f3c809de377b008a4a3ee94a0834aac8bec1f86e28ffe4fdb5a15b0c785".into()); - } - - #[test] - fn checkpoint_basic() { - let mut state = get_temp_state(); - let a = Address::zero(); - state.checkpoint(); - state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - state.discard_checkpoint(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - state.checkpoint(); - state.add_balance(&a, &U256::from(1u64), CleanupMode::NoEmpty).unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(70u64)); - state.revert_to_checkpoint(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - } - - #[test] - fn checkpoint_nested() { - let mut state = get_temp_state(); - let a = Address::zero(); - state.checkpoint(); - state.checkpoint(); - state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - state.discard_checkpoint(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - state.revert_to_checkpoint(); - assert_eq!(state.balance(&a).unwrap(), U256::from(0)); - } - - #[test] - fn checkpoint_revert_to_get_storage_at() { - let mut state = get_temp_state(); - let a = Address::zero(); - let k = H256::from(U256::from(0)); - - let c0 = state.checkpoint(); - let c1 = state.checkpoint(); - state.set_storage(&a, k, H256::from(U256::from(1))).unwrap(); - - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(1))); - - state.revert_to_checkpoint(); // Revert to c1. - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(0))); - } - - #[test] - fn checkpoint_from_empty_get_storage_at() { - let mut state = get_temp_state(); - let a = Address::zero(); - let k = H256::from(U256::from(0)); - let k2 = H256::from(U256::from(1)); - - assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(0))); - state.clear(); - - let c0 = state.checkpoint(); - state.new_contract(&a, U256::zero(), U256::zero()).unwrap(); - let c1 = state.checkpoint(); - state.set_storage(&a, k, H256::from(U256::from(1))).unwrap(); - let c2 = state.checkpoint(); - let c3 = state.checkpoint(); - state.set_storage(&a, k2, H256::from(U256::from(3))).unwrap(); - state.set_storage(&a, k, H256::from(U256::from(3))).unwrap(); - let c4 = state.checkpoint(); - state.set_storage(&a, k, H256::from(U256::from(4))).unwrap(); - let c5 = state.checkpoint(); - - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c4, &a, &k).unwrap(), Some(H256::from(U256::from(3)))); - assert_eq!(state.checkpoint_storage_at(c5, &a, &k).unwrap(), Some(H256::from(U256::from(4)))); - - state.discard_checkpoint(); // Commit/discard c5. - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c4, &a, &k).unwrap(), Some(H256::from(U256::from(3)))); - - state.revert_to_checkpoint(); // Revert to c4. - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - - state.discard_checkpoint(); // Commit/discard c3. - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - - state.revert_to_checkpoint(); // Revert to c2. - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - - state.discard_checkpoint(); // Commit/discard c1. - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - } - - #[test] - fn checkpoint_get_storage_at() { - let mut state = get_temp_state(); - let a = Address::zero(); - let k = H256::from(U256::from(0)); - let k2 = H256::from(U256::from(1)); - - state.set_storage(&a, k, H256::from(U256::from(0xffff))).unwrap(); - state.commit().unwrap(); - state.clear(); - - assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(0xffff))); - state.clear(); - - let cm1 = state.checkpoint(); - let c0 = state.checkpoint(); - state.new_contract(&a, U256::zero(), U256::zero()).unwrap(); - let c1 = state.checkpoint(); - state.set_storage(&a, k, H256::from(U256::from(1))).unwrap(); - let c2 = state.checkpoint(); - let c3 = state.checkpoint(); - state.set_storage(&a, k2, H256::from(U256::from(3))).unwrap(); - state.set_storage(&a, k, H256::from(U256::from(3))).unwrap(); - let c4 = state.checkpoint(); - state.set_storage(&a, k, H256::from(U256::from(4))).unwrap(); - let c5 = state.checkpoint(); - - assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c4, &a, &k).unwrap(), Some(H256::from(U256::from(3)))); - assert_eq!(state.checkpoint_storage_at(c5, &a, &k).unwrap(), Some(H256::from(U256::from(4)))); - - state.discard_checkpoint(); // Commit/discard c5. - assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c4, &a, &k).unwrap(), Some(H256::from(U256::from(3)))); - - state.revert_to_checkpoint(); // Revert to c4. - assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - - state.discard_checkpoint(); // Commit/discard c3. - assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - - state.revert_to_checkpoint(); // Revert to c2. - assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - - state.discard_checkpoint(); // Commit/discard c1. - assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - } - - #[test] - fn kill_account_with_checkpoints() { - let mut state = get_temp_state(); - let a = Address::zero(); - let k = H256::from(U256::from(0)); - state.checkpoint(); - state.set_storage(&a, k, H256::from(U256::from(1))).unwrap(); - state.checkpoint(); - state.kill_account(&a); - - assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(0))); - state.revert_to_checkpoint(); - assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(1))); - } - - #[test] - fn create_contract_fail() { - let mut state = get_temp_state(); - let orig_root = state.root().clone(); - let a: Address = 1000.into(); - - state.checkpoint(); // c1 - state.new_contract(&a, U256::zero(), U256::zero()).unwrap(); - state.add_balance(&a, &U256::from(1), CleanupMode::ForceCreate).unwrap(); - state.checkpoint(); // c2 - state.add_balance(&a, &U256::from(1), CleanupMode::ForceCreate).unwrap(); - state.discard_checkpoint(); // discard c2 - state.revert_to_checkpoint(); // revert to c1 - assert_eq!(state.exists(&a).unwrap(), false); - - state.commit().unwrap(); - assert_eq!(orig_root, state.root().clone()); - } - - #[test] - fn create_contract_fail_previous_storage() { - let mut state = get_temp_state(); - let a: Address = 1000.into(); - let k = H256::from(U256::from(0)); - - state.set_storage(&a, k, H256::from(U256::from(0xffff))).unwrap(); - state.commit().unwrap(); - state.clear(); - - let orig_root = state.root().clone(); - assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(0xffff))); - state.clear(); - - state.checkpoint(); // c1 - state.new_contract(&a, U256::zero(), U256::zero()).unwrap(); - state.checkpoint(); // c2 - state.set_storage(&a, k, H256::from(U256::from(2))).unwrap(); - state.revert_to_checkpoint(); // revert to c2 - assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(0))); - state.revert_to_checkpoint(); // revert to c1 - assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(0xffff))); - - state.commit().unwrap(); - assert_eq!(orig_root, state.root().clone()); - } - - #[test] - fn create_empty() { - let mut state = get_temp_state(); - state.commit().unwrap(); - assert_eq!(*state.root(), "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".into()); - } - - #[test] - fn should_not_panic_on_state_diff_with_storage() { - let mut state = get_temp_state(); - - let a: Address = 0xa.into(); - state.init_code(&a, b"abcdefg".to_vec()).unwrap();; - state.add_balance(&a, &256.into(), CleanupMode::NoEmpty).unwrap(); - state.set_storage(&a, 0xb.into(), 0xc.into()).unwrap(); - - let mut new_state = state.clone(); - new_state.set_storage(&a, 0xb.into(), 0xd.into()).unwrap(); - - new_state.diff_from(state).unwrap(); - } - - #[test] - fn should_kill_garbage() { - let a = 10.into(); - let b = 20.into(); - let c = 30.into(); - let d = 40.into(); - let e = 50.into(); - let x = 0.into(); - let db = get_temp_state_db(); - let (root, db) = { - let mut state = State::new(db, U256::from(0), Default::default()); - state.add_balance(&a, &U256::default(), CleanupMode::ForceCreate).unwrap(); // create an empty account - state.add_balance(&b, &100.into(), CleanupMode::ForceCreate).unwrap(); // create a dust account - state.add_balance(&c, &101.into(), CleanupMode::ForceCreate).unwrap(); // create a normal account - state.add_balance(&d, &99.into(), CleanupMode::ForceCreate).unwrap(); // create another dust account - state.new_contract(&e, 100.into(), 1.into()).unwrap(); // create a contract account - state.init_code(&e, vec![0x00]).unwrap(); - state.commit().unwrap(); - state.drop() - }; - - let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - let mut touched = HashSet::new(); - state.add_balance(&a, &U256::default(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account - state.transfer_balance(&b, &x, &1.into(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account decreasing its balance - state.transfer_balance(&c, &x, &1.into(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account decreasing its balance - state.transfer_balance(&e, &x, &1.into(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account decreasing its balance - state.kill_garbage(&touched, true, &None, false).unwrap(); - assert!(!state.exists(&a).unwrap()); - assert!(state.exists(&b).unwrap()); - state.kill_garbage(&touched, true, &Some(100.into()), false).unwrap(); - assert!(!state.exists(&b).unwrap()); - assert!(state.exists(&c).unwrap()); - assert!(state.exists(&d).unwrap()); - assert!(state.exists(&e).unwrap()); - state.kill_garbage(&touched, true, &Some(100.into()), true).unwrap(); - assert!(state.exists(&c).unwrap()); - assert!(state.exists(&d).unwrap()); - assert!(!state.exists(&e).unwrap()); - } - - #[test] - fn should_trace_diff_suicided_accounts() { - use pod_account; - - let a = 10.into(); - let db = get_temp_state_db(); - let (root, db) = { - let mut state = State::new(db, U256::from(0), Default::default()); - state.add_balance(&a, &100.into(), CleanupMode::ForceCreate).unwrap(); - state.commit().unwrap(); - state.drop() - }; - - let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - let original = state.clone(); - state.kill_account(&a); - - let diff = state.diff_from(original).unwrap(); - let diff_map = diff.get(); - assert_eq!(diff_map.len(), 1); - assert!(diff_map.get(&a).is_some()); - assert_eq!(diff_map.get(&a), - pod_account::diff_pod(Some(&PodAccount { - balance: U256::from(100), - nonce: U256::zero(), - code: Some(Default::default()), - storage: Default::default() - }), None).as_ref()); - } - - #[test] - fn should_trace_diff_unmodified_storage() { - use pod_account; - - let a = 10.into(); - let db = get_temp_state_db(); - - let (root, db) = { - let mut state = State::new(db, U256::from(0), Default::default()); - state.set_storage(&a, H256::from(&U256::from(1u64)), H256::from(&U256::from(20u64))).unwrap(); - state.commit().unwrap(); - state.drop() - }; - - let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - let original = state.clone(); - state.set_storage(&a, H256::from(&U256::from(1u64)), H256::from(&U256::from(100u64))).unwrap(); - - let diff = state.diff_from(original).unwrap(); - let diff_map = diff.get(); - assert_eq!(diff_map.len(), 1); - assert!(diff_map.get(&a).is_some()); - assert_eq!(diff_map.get(&a), - pod_account::diff_pod(Some(&PodAccount { - balance: U256::zero(), - nonce: U256::zero(), - code: Some(Default::default()), - storage: vec![(H256::from(&U256::from(1u64)), H256::from(&U256::from(20u64)))] - .into_iter().collect(), - }), Some(&PodAccount { - balance: U256::zero(), - nonce: U256::zero(), - code: Some(Default::default()), - storage: vec![(H256::from(&U256::from(1u64)), H256::from(&U256::from(100u64)))] - .into_iter().collect(), - })).as_ref()); - } - - #[cfg(feature="to-pod-full")] - #[test] - fn should_get_full_pod_storage_values() { - use trie::{TrieFactory, TrieSpec}; - - let a = 10.into(); - let db = get_temp_state_db(); - - let factories = Factories { - vm: Default::default(), - trie: TrieFactory::new(TrieSpec::Fat), - accountdb: Default::default(), - }; - - let get_pod_state_val = |pod_state : &PodState, ak, k| { - pod_state.get().get(ak).unwrap().storage.get(&k).unwrap().clone() - }; - - let storage_address = H256::from(&U256::from(1u64)); - - let (root, db) = { - let mut state = State::new(db, U256::from(0), factories.clone()); - state.set_storage(&a, storage_address.clone(), H256::from(&U256::from(20u64))).unwrap(); - let dump = state.to_pod_full().unwrap(); - assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), H256::from(&U256::from(20u64))); - state.commit().unwrap(); - let dump = state.to_pod_full().unwrap(); - assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), H256::from(&U256::from(20u64))); - state.drop() - }; - - let mut state = State::from_existing(db, root, U256::from(0u8), factories).unwrap(); - let dump = state.to_pod_full().unwrap(); - assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), H256::from(&U256::from(20u64))); - state.set_storage(&a, storage_address.clone(), H256::from(&U256::from(21u64))).unwrap(); - let dump = state.to_pod_full().unwrap(); - assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), H256::from(&U256::from(21u64))); - state.commit().unwrap(); - state.set_storage(&a, storage_address.clone(), H256::from(&U256::from(0u64))).unwrap(); - let dump = state.to_pod_full().unwrap(); - assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), H256::from(&U256::from(0u64))); - - } - + use super::*; + use ethereum_types::{Address, H256, U256}; + use ethkey::Secret; + use evm::CallType; + use hash::{keccak, KECCAK_NULL_RLP}; + use machine::EthereumMachine; + use rustc_hex::FromHex; + use spec::*; + use std::{str::FromStr, sync::Arc}; + use test_helpers::{get_temp_state, get_temp_state_db}; + use trace::{trace, FlatTrace, TraceError}; + use types::transaction::*; + use vm::EnvInfo; + + fn secret() -> Secret { + keccak("").into() + } + + fn make_frontier_machine(max_depth: usize) -> EthereumMachine { + let mut machine = ::ethereum::new_frontier_test_machine(); + machine.set_schedule_creation_rules(Box::new(move |s, _| s.max_depth = max_depth)); + machine + } + + #[test] + fn should_apply_create_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 100.into(), + data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555") + .unwrap(), + } + .sign(&secret(), None); + + state + .add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty) + .unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 0, + action: trace::Action::Create(trace::Create { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + value: 100.into(), + gas: 77412.into(), + init: vec![ + 96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, + 91, 96, 32, 53, 96, 0, 53, 85, + ], + }), + result: trace::Res::Create(trace::CreateResult { + gas_used: U256::from(3224), + address: Address::from_str("8988167e088c87cd314df6d3c2b83da5acb93ace").unwrap(), + code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53], + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_work_when_cloned() { + let _ = env_logger::try_init(); + + let a = Address::zero(); + + let mut state = { + let mut state = get_temp_state(); + assert_eq!(state.exists(&a).unwrap(), false); + state.inc_nonce(&a).unwrap(); + state.commit().unwrap(); + state.clone() + }; + + state.inc_nonce(&a).unwrap(); + state.commit().unwrap(); + } + + #[test] + fn should_trace_failed_create_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 100.into(), + data: FromHex::from_hex("5b600056").unwrap(), + } + .sign(&secret(), None); + + state + .add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty) + .unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Create(trace::Create { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + value: 100.into(), + gas: 78792.into(), + init: vec![91, 96, 0, 86], + }), + result: trace::Res::FailedCreate(TraceError::OutOfGas), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + } + .sign(&secret(), None); + + state + .init_code(&0xa.into(), FromHex::from_hex("6000").unwrap()) + .unwrap(); + state + .add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty) + .unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![], + }), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_basic_call_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + } + .sign(&secret(), None); + + state + .add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty) + .unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(0), + output: vec![], + }), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_transaction_to_builtin() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = Spec::new_test_machine(); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0x1.into()), + value: 0.into(), + data: vec![], + } + .sign(&secret(), None); + + let result = state.apply(&info, &machine, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: "0000000000000000000000000000000000000001".into(), + value: 0.into(), + gas: 79_000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3000), + output: vec![], + }), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_not_trace_subcall_transaction_to_builtin() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = Spec::new_test_machine(); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 0.into(), + data: vec![], + } + .sign(&secret(), None); + + state + .init_code( + &0xa.into(), + FromHex::from_hex("600060006000600060006001610be0f1").unwrap(), + ) + .unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 0.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3_721), // in post-eip150 + output: vec![], + }), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_callcode_properly() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = Spec::new_test_machine(); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 0.into(), + data: vec![], + } + .sign(&secret(), None); + + state + .init_code( + &0xa.into(), + FromHex::from_hex("60006000600060006000600b611000f2").unwrap(), + ) + .unwrap(); + state + .init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()) + .unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + + let expected_trace = vec![ + FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 0.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 724.into(), // in post-eip150 + output: vec![], + }), + }, + FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 4096.into(), + input: vec![], + call_type: CallType::CallCode, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 3.into(), + output: vec![], + }), + }, + ]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_delegatecall_properly() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + info.number = 0x789b0; + let machine = Spec::new_test_machine(); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 0.into(), + data: vec![], + } + .sign(&secret(), None); + + state + .init_code( + &0xa.into(), + FromHex::from_hex("6000600060006000600b618000f4").unwrap(), + ) + .unwrap(); + state + .init_code( + &0xb.into(), + FromHex::from_hex("60056000526001601ff3").unwrap(), + ) + .unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + + let expected_trace = vec![ + FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 0.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(736), // in post-eip150 + output: vec![], + }), + }, + FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 32768.into(), + input: vec![], + call_type: CallType::DelegateCall, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 18.into(), + output: vec![5], + }), + }, + ]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_failed_call_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + } + .sign(&secret(), None); + + state + .init_code(&0xa.into(), FromHex::from_hex("5b600056").unwrap()) + .unwrap(); + state + .add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty) + .unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::FailedCall(TraceError::OutOfGas), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_with_subcall_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + } + .sign(&secret(), None); + + state + .init_code( + &0xa.into(), + FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap(), + ) + .unwrap(); + state + .init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()) + .unwrap(); + state + .add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty) + .unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + + let expected_trace = vec![ + FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(69), + output: vec![], + }), + }, + FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![], + }), + }, + ]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_with_basic_subcall_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + } + .sign(&secret(), None); + + state + .init_code( + &0xa.into(), + FromHex::from_hex("60006000600060006045600b6000f1").unwrap(), + ) + .unwrap(); + state + .add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty) + .unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![ + FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(31761), + output: vec![], + }), + }, + FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xb.into(), + value: 69.into(), + gas: 2300.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult::default()), + }, + ]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_not_trace_call_with_invalid_basic_subcall_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + } + .sign(&secret(), None); + + state + .init_code( + &0xa.into(), + FromHex::from_hex("600060006000600060ff600b6000f1").unwrap(), + ) + .unwrap(); // not enough funds. + state + .add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty) + .unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(31761), + output: vec![], + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_failed_subcall_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], //600480600b6000396000f35b600056 + } + .sign(&secret(), None); + + state + .init_code( + &0xa.into(), + FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap(), + ) + .unwrap(); + state + .init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap()) + .unwrap(); + state + .add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty) + .unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![ + FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(79_000), + output: vec![], + }), + }, + FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::FailedCall(TraceError::OutOfGas), + }, + ]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_with_subcall_with_subcall_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + } + .sign(&secret(), None); + + state + .init_code( + &0xa.into(), + FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap(), + ) + .unwrap(); + state + .init_code( + &0xb.into(), + FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap(), + ) + .unwrap(); + state + .init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()) + .unwrap(); + state + .add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty) + .unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![ + FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(135), + output: vec![], + }), + }, + FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(69), + output: vec![], + }), + }, + FlatTrace { + trace_address: vec![0, 0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xb.into(), + to: 0xc.into(), + value: 0.into(), + gas: 78868.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![], + }), + }, + ]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_failed_subcall_with_subcall_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], //600480600b6000396000f35b600056 + } + .sign(&secret(), None); + + state + .init_code( + &0xa.into(), + FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap(), + ) + .unwrap(); + state + .init_code( + &0xb.into(), + FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap(), + ) + .unwrap(); + state + .init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()) + .unwrap(); + state + .add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty) + .unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + + let expected_trace = vec![ + FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(79_000), + output: vec![], + }), + }, + FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::FailedCall(TraceError::OutOfGas), + }, + FlatTrace { + trace_address: vec![0, 0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xb.into(), + to: 0xc.into(), + value: 0.into(), + gas: 78868.into(), + call_type: CallType::Call, + input: vec![], + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![], + }), + }, + ]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_suicide() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + } + .sign(&secret(), None); + + state + .init_code( + &0xa.into(), + FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap(), + ) + .unwrap(); + state + .add_balance(&0xa.into(), &50.into(), CleanupMode::NoEmpty) + .unwrap(); + state + .add_balance(&t.sender(), &100.into(), CleanupMode::NoEmpty) + .unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![ + FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 3.into(), + output: vec![], + }), + }, + FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Suicide(trace::Suicide { + address: 0xa.into(), + refund_address: 0xb.into(), + balance: 150.into(), + }), + result: trace::Res::None, + }, + ]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn code_from_database() { + let a = Address::zero(); + let (root, db) = { + let mut state = get_temp_state(); + state + .require_or_from( + &a, + false, + || Account::new_contract(42.into(), 0.into(), KECCAK_NULL_RLP), + |_| {}, + ) + .unwrap(); + state.init_code(&a, vec![1, 2, 3]).unwrap(); + assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3]))); + state.commit().unwrap(); + assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3]))); + state.drop() + }; + + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3]))); + } + + #[test] + fn storage_at_from_database() { + let a = Address::zero(); + let (root, db) = { + let mut state = get_temp_state(); + state + .set_storage( + &a, + H256::from(&U256::from(1u64)), + H256::from(&U256::from(69u64)), + ) + .unwrap(); + state.commit().unwrap(); + state.drop() + }; + + let s = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert_eq!( + s.storage_at(&a, &H256::from(&U256::from(1u64))).unwrap(), + H256::from(&U256::from(69u64)) + ); + } + + #[test] + fn get_from_database() { + let a = Address::zero(); + let (root, db) = { + let mut state = get_temp_state(); + state.inc_nonce(&a).unwrap(); + state + .add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty) + .unwrap(); + state.commit().unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + state.drop() + }; + + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); + } + + #[test] + fn remove() { + let a = Address::zero(); + let mut state = get_temp_state(); + assert_eq!(state.exists(&a).unwrap(), false); + assert_eq!(state.exists_and_not_null(&a).unwrap(), false); + state.inc_nonce(&a).unwrap(); + assert_eq!(state.exists(&a).unwrap(), true); + assert_eq!(state.exists_and_not_null(&a).unwrap(), true); + assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); + state.kill_account(&a); + assert_eq!(state.exists(&a).unwrap(), false); + assert_eq!(state.exists_and_not_null(&a).unwrap(), false); + assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); + } + + #[test] + fn empty_account_is_not_created() { + let a = Address::zero(); + let db = get_temp_state_db(); + let (root, db) = { + let mut state = State::new(db, U256::from(0), Default::default()); + state + .add_balance(&a, &U256::default(), CleanupMode::NoEmpty) + .unwrap(); // create an empty account + state.commit().unwrap(); + state.drop() + }; + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert!(!state.exists(&a).unwrap()); + assert!(!state.exists_and_not_null(&a).unwrap()); + } + + #[test] + fn empty_account_exists_when_creation_forced() { + let a = Address::zero(); + let db = get_temp_state_db(); + let (root, db) = { + let mut state = State::new(db, U256::from(0), Default::default()); + state + .add_balance(&a, &U256::default(), CleanupMode::ForceCreate) + .unwrap(); // create an empty account + state.commit().unwrap(); + state.drop() + }; + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert!(state.exists(&a).unwrap()); + assert!(!state.exists_and_not_null(&a).unwrap()); + } + + #[test] + fn remove_from_database() { + let a = Address::zero(); + let (root, db) = { + let mut state = get_temp_state(); + state.inc_nonce(&a).unwrap(); + state.commit().unwrap(); + assert_eq!(state.exists(&a).unwrap(), true); + assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); + state.drop() + }; + + let (root, db) = { + let mut state = + State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert_eq!(state.exists(&a).unwrap(), true); + assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); + state.kill_account(&a); + state.commit().unwrap(); + assert_eq!(state.exists(&a).unwrap(), false); + assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); + state.drop() + }; + + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert_eq!(state.exists(&a).unwrap(), false); + assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); + } + + #[test] + fn alter_balance() { + let mut state = get_temp_state(); + let a = Address::zero(); + let b = 1u64.into(); + state + .add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty) + .unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + state.commit().unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + state + .sub_balance(&a, &U256::from(42u64), &mut CleanupMode::NoEmpty) + .unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(27u64)); + state.commit().unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(27u64)); + state + .transfer_balance(&a, &b, &U256::from(18u64), CleanupMode::NoEmpty) + .unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(9u64)); + assert_eq!(state.balance(&b).unwrap(), U256::from(18u64)); + state.commit().unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(9u64)); + assert_eq!(state.balance(&b).unwrap(), U256::from(18u64)); + } + + #[test] + fn alter_nonce() { + let mut state = get_temp_state(); + let a = Address::zero(); + state.inc_nonce(&a).unwrap(); + assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); + state.inc_nonce(&a).unwrap(); + assert_eq!(state.nonce(&a).unwrap(), U256::from(2u64)); + state.commit().unwrap(); + assert_eq!(state.nonce(&a).unwrap(), U256::from(2u64)); + state.inc_nonce(&a).unwrap(); + assert_eq!(state.nonce(&a).unwrap(), U256::from(3u64)); + state.commit().unwrap(); + assert_eq!(state.nonce(&a).unwrap(), U256::from(3u64)); + } + + #[test] + fn balance_nonce() { + let mut state = get_temp_state(); + let a = Address::zero(); + assert_eq!(state.balance(&a).unwrap(), U256::from(0u64)); + assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); + state.commit().unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(0u64)); + assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); + } + + #[test] + fn ensure_cached() { + let mut state = get_temp_state(); + let a = Address::zero(); + state.require(&a, false).unwrap(); + state.commit().unwrap(); + assert_eq!( + *state.root(), + "0ce23f3c809de377b008a4a3ee94a0834aac8bec1f86e28ffe4fdb5a15b0c785".into() + ); + } + + #[test] + fn checkpoint_basic() { + let mut state = get_temp_state(); + let a = Address::zero(); + state.checkpoint(); + state + .add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty) + .unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + state.discard_checkpoint(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + state.checkpoint(); + state + .add_balance(&a, &U256::from(1u64), CleanupMode::NoEmpty) + .unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(70u64)); + state.revert_to_checkpoint(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + } + + #[test] + fn checkpoint_nested() { + let mut state = get_temp_state(); + let a = Address::zero(); + state.checkpoint(); + state.checkpoint(); + state + .add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty) + .unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + state.discard_checkpoint(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + state.revert_to_checkpoint(); + assert_eq!(state.balance(&a).unwrap(), U256::from(0)); + } + + #[test] + fn checkpoint_revert_to_get_storage_at() { + let mut state = get_temp_state(); + let a = Address::zero(); + let k = H256::from(U256::from(0)); + + let c0 = state.checkpoint(); + let c1 = state.checkpoint(); + state.set_storage(&a, k, H256::from(U256::from(1))).unwrap(); + + assert_eq!( + state.checkpoint_storage_at(c0, &a, &k).unwrap(), + Some(H256::from(U256::from(0))) + ); + assert_eq!( + state.checkpoint_storage_at(c1, &a, &k).unwrap(), + Some(H256::from(U256::from(0))) + ); + assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(1))); + + state.revert_to_checkpoint(); // Revert to c1. + assert_eq!( + state.checkpoint_storage_at(c0, &a, &k).unwrap(), + Some(H256::from(U256::from(0))) + ); + assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(0))); + } + + #[test] + fn checkpoint_from_empty_get_storage_at() { + let mut state = get_temp_state(); + let a = Address::zero(); + let k = H256::from(U256::from(0)); + let k2 = H256::from(U256::from(1)); + + assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(0))); + state.clear(); + + let c0 = state.checkpoint(); + state.new_contract(&a, U256::zero(), U256::zero()).unwrap(); + let c1 = state.checkpoint(); + state.set_storage(&a, k, H256::from(U256::from(1))).unwrap(); + let c2 = state.checkpoint(); + let c3 = state.checkpoint(); + state + .set_storage(&a, k2, H256::from(U256::from(3))) + .unwrap(); + state.set_storage(&a, k, H256::from(U256::from(3))).unwrap(); + let c4 = state.checkpoint(); + state.set_storage(&a, k, H256::from(U256::from(4))).unwrap(); + let c5 = state.checkpoint(); + + assert_eq!( + state.checkpoint_storage_at(c0, &a, &k).unwrap(), + Some(H256::from(U256::from(0))) + ); + assert_eq!( + state.checkpoint_storage_at(c1, &a, &k).unwrap(), + Some(H256::from(U256::from(0))) + ); + assert_eq!( + state.checkpoint_storage_at(c2, &a, &k).unwrap(), + Some(H256::from(U256::from(1))) + ); + assert_eq!( + state.checkpoint_storage_at(c3, &a, &k).unwrap(), + Some(H256::from(U256::from(1))) + ); + assert_eq!( + state.checkpoint_storage_at(c4, &a, &k).unwrap(), + Some(H256::from(U256::from(3))) + ); + assert_eq!( + state.checkpoint_storage_at(c5, &a, &k).unwrap(), + Some(H256::from(U256::from(4))) + ); + + state.discard_checkpoint(); // Commit/discard c5. + assert_eq!( + state.checkpoint_storage_at(c0, &a, &k).unwrap(), + Some(H256::from(U256::from(0))) + ); + assert_eq!( + state.checkpoint_storage_at(c1, &a, &k).unwrap(), + Some(H256::from(U256::from(0))) + ); + assert_eq!( + state.checkpoint_storage_at(c2, &a, &k).unwrap(), + Some(H256::from(U256::from(1))) + ); + assert_eq!( + state.checkpoint_storage_at(c3, &a, &k).unwrap(), + Some(H256::from(U256::from(1))) + ); + assert_eq!( + state.checkpoint_storage_at(c4, &a, &k).unwrap(), + Some(H256::from(U256::from(3))) + ); + + state.revert_to_checkpoint(); // Revert to c4. + assert_eq!( + state.checkpoint_storage_at(c0, &a, &k).unwrap(), + Some(H256::from(U256::from(0))) + ); + assert_eq!( + state.checkpoint_storage_at(c1, &a, &k).unwrap(), + Some(H256::from(U256::from(0))) + ); + assert_eq!( + state.checkpoint_storage_at(c2, &a, &k).unwrap(), + Some(H256::from(U256::from(1))) + ); + assert_eq!( + state.checkpoint_storage_at(c3, &a, &k).unwrap(), + Some(H256::from(U256::from(1))) + ); + + state.discard_checkpoint(); // Commit/discard c3. + assert_eq!( + state.checkpoint_storage_at(c0, &a, &k).unwrap(), + Some(H256::from(U256::from(0))) + ); + assert_eq!( + state.checkpoint_storage_at(c1, &a, &k).unwrap(), + Some(H256::from(U256::from(0))) + ); + assert_eq!( + state.checkpoint_storage_at(c2, &a, &k).unwrap(), + Some(H256::from(U256::from(1))) + ); + + state.revert_to_checkpoint(); // Revert to c2. + assert_eq!( + state.checkpoint_storage_at(c0, &a, &k).unwrap(), + Some(H256::from(U256::from(0))) + ); + assert_eq!( + state.checkpoint_storage_at(c1, &a, &k).unwrap(), + Some(H256::from(U256::from(0))) + ); + + state.discard_checkpoint(); // Commit/discard c1. + assert_eq!( + state.checkpoint_storage_at(c0, &a, &k).unwrap(), + Some(H256::from(U256::from(0))) + ); + } + + #[test] + fn checkpoint_get_storage_at() { + let mut state = get_temp_state(); + let a = Address::zero(); + let k = H256::from(U256::from(0)); + let k2 = H256::from(U256::from(1)); + + state + .set_storage(&a, k, H256::from(U256::from(0xffff))) + .unwrap(); + state.commit().unwrap(); + state.clear(); + + assert_eq!( + state.storage_at(&a, &k).unwrap(), + H256::from(U256::from(0xffff)) + ); + state.clear(); + + let cm1 = state.checkpoint(); + let c0 = state.checkpoint(); + state.new_contract(&a, U256::zero(), U256::zero()).unwrap(); + let c1 = state.checkpoint(); + state.set_storage(&a, k, H256::from(U256::from(1))).unwrap(); + let c2 = state.checkpoint(); + let c3 = state.checkpoint(); + state + .set_storage(&a, k2, H256::from(U256::from(3))) + .unwrap(); + state.set_storage(&a, k, H256::from(U256::from(3))).unwrap(); + let c4 = state.checkpoint(); + state.set_storage(&a, k, H256::from(U256::from(4))).unwrap(); + let c5 = state.checkpoint(); + + assert_eq!( + state.checkpoint_storage_at(cm1, &a, &k).unwrap(), + Some(H256::from(U256::from(0xffff))) + ); + assert_eq!( + state.checkpoint_storage_at(c0, &a, &k).unwrap(), + Some(H256::from(U256::from(0xffff))) + ); + assert_eq!( + state.checkpoint_storage_at(c1, &a, &k).unwrap(), + Some(H256::from(U256::from(0))) + ); + assert_eq!( + state.checkpoint_storage_at(c2, &a, &k).unwrap(), + Some(H256::from(U256::from(1))) + ); + assert_eq!( + state.checkpoint_storage_at(c3, &a, &k).unwrap(), + Some(H256::from(U256::from(1))) + ); + assert_eq!( + state.checkpoint_storage_at(c4, &a, &k).unwrap(), + Some(H256::from(U256::from(3))) + ); + assert_eq!( + state.checkpoint_storage_at(c5, &a, &k).unwrap(), + Some(H256::from(U256::from(4))) + ); + + state.discard_checkpoint(); // Commit/discard c5. + assert_eq!( + state.checkpoint_storage_at(cm1, &a, &k).unwrap(), + Some(H256::from(U256::from(0xffff))) + ); + assert_eq!( + state.checkpoint_storage_at(c0, &a, &k).unwrap(), + Some(H256::from(U256::from(0xffff))) + ); + assert_eq!( + state.checkpoint_storage_at(c1, &a, &k).unwrap(), + Some(H256::from(U256::from(0))) + ); + assert_eq!( + state.checkpoint_storage_at(c2, &a, &k).unwrap(), + Some(H256::from(U256::from(1))) + ); + assert_eq!( + state.checkpoint_storage_at(c3, &a, &k).unwrap(), + Some(H256::from(U256::from(1))) + ); + assert_eq!( + state.checkpoint_storage_at(c4, &a, &k).unwrap(), + Some(H256::from(U256::from(3))) + ); + + state.revert_to_checkpoint(); // Revert to c4. + assert_eq!( + state.checkpoint_storage_at(cm1, &a, &k).unwrap(), + Some(H256::from(U256::from(0xffff))) + ); + assert_eq!( + state.checkpoint_storage_at(c0, &a, &k).unwrap(), + Some(H256::from(U256::from(0xffff))) + ); + assert_eq!( + state.checkpoint_storage_at(c1, &a, &k).unwrap(), + Some(H256::from(U256::from(0))) + ); + assert_eq!( + state.checkpoint_storage_at(c2, &a, &k).unwrap(), + Some(H256::from(U256::from(1))) + ); + assert_eq!( + state.checkpoint_storage_at(c3, &a, &k).unwrap(), + Some(H256::from(U256::from(1))) + ); + + state.discard_checkpoint(); // Commit/discard c3. + assert_eq!( + state.checkpoint_storage_at(cm1, &a, &k).unwrap(), + Some(H256::from(U256::from(0xffff))) + ); + assert_eq!( + state.checkpoint_storage_at(c0, &a, &k).unwrap(), + Some(H256::from(U256::from(0xffff))) + ); + assert_eq!( + state.checkpoint_storage_at(c1, &a, &k).unwrap(), + Some(H256::from(U256::from(0))) + ); + assert_eq!( + state.checkpoint_storage_at(c2, &a, &k).unwrap(), + Some(H256::from(U256::from(1))) + ); + + state.revert_to_checkpoint(); // Revert to c2. + assert_eq!( + state.checkpoint_storage_at(cm1, &a, &k).unwrap(), + Some(H256::from(U256::from(0xffff))) + ); + assert_eq!( + state.checkpoint_storage_at(c0, &a, &k).unwrap(), + Some(H256::from(U256::from(0xffff))) + ); + assert_eq!( + state.checkpoint_storage_at(c1, &a, &k).unwrap(), + Some(H256::from(U256::from(0))) + ); + + state.discard_checkpoint(); // Commit/discard c1. + assert_eq!( + state.checkpoint_storage_at(cm1, &a, &k).unwrap(), + Some(H256::from(U256::from(0xffff))) + ); + assert_eq!( + state.checkpoint_storage_at(c0, &a, &k).unwrap(), + Some(H256::from(U256::from(0xffff))) + ); + } + + #[test] + fn kill_account_with_checkpoints() { + let mut state = get_temp_state(); + let a = Address::zero(); + let k = H256::from(U256::from(0)); + state.checkpoint(); + state.set_storage(&a, k, H256::from(U256::from(1))).unwrap(); + state.checkpoint(); + state.kill_account(&a); + + assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(0))); + state.revert_to_checkpoint(); + assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(1))); + } + + #[test] + fn create_contract_fail() { + let mut state = get_temp_state(); + let orig_root = state.root().clone(); + let a: Address = 1000.into(); + + state.checkpoint(); // c1 + state.new_contract(&a, U256::zero(), U256::zero()).unwrap(); + state + .add_balance(&a, &U256::from(1), CleanupMode::ForceCreate) + .unwrap(); + state.checkpoint(); // c2 + state + .add_balance(&a, &U256::from(1), CleanupMode::ForceCreate) + .unwrap(); + state.discard_checkpoint(); // discard c2 + state.revert_to_checkpoint(); // revert to c1 + assert_eq!(state.exists(&a).unwrap(), false); + + state.commit().unwrap(); + assert_eq!(orig_root, state.root().clone()); + } + + #[test] + fn create_contract_fail_previous_storage() { + let mut state = get_temp_state(); + let a: Address = 1000.into(); + let k = H256::from(U256::from(0)); + + state + .set_storage(&a, k, H256::from(U256::from(0xffff))) + .unwrap(); + state.commit().unwrap(); + state.clear(); + + let orig_root = state.root().clone(); + assert_eq!( + state.storage_at(&a, &k).unwrap(), + H256::from(U256::from(0xffff)) + ); + state.clear(); + + state.checkpoint(); // c1 + state.new_contract(&a, U256::zero(), U256::zero()).unwrap(); + state.checkpoint(); // c2 + state.set_storage(&a, k, H256::from(U256::from(2))).unwrap(); + state.revert_to_checkpoint(); // revert to c2 + assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(0))); + state.revert_to_checkpoint(); // revert to c1 + assert_eq!( + state.storage_at(&a, &k).unwrap(), + H256::from(U256::from(0xffff)) + ); + + state.commit().unwrap(); + assert_eq!(orig_root, state.root().clone()); + } + + #[test] + fn create_empty() { + let mut state = get_temp_state(); + state.commit().unwrap(); + assert_eq!( + *state.root(), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".into() + ); + } + + #[test] + fn should_not_panic_on_state_diff_with_storage() { + let mut state = get_temp_state(); + + let a: Address = 0xa.into(); + state.init_code(&a, b"abcdefg".to_vec()).unwrap(); + state + .add_balance(&a, &256.into(), CleanupMode::NoEmpty) + .unwrap(); + state.set_storage(&a, 0xb.into(), 0xc.into()).unwrap(); + + let mut new_state = state.clone(); + new_state.set_storage(&a, 0xb.into(), 0xd.into()).unwrap(); + + new_state.diff_from(state).unwrap(); + } + + #[test] + fn should_kill_garbage() { + let a = 10.into(); + let b = 20.into(); + let c = 30.into(); + let d = 40.into(); + let e = 50.into(); + let x = 0.into(); + let db = get_temp_state_db(); + let (root, db) = { + let mut state = State::new(db, U256::from(0), Default::default()); + state + .add_balance(&a, &U256::default(), CleanupMode::ForceCreate) + .unwrap(); // create an empty account + state + .add_balance(&b, &100.into(), CleanupMode::ForceCreate) + .unwrap(); // create a dust account + state + .add_balance(&c, &101.into(), CleanupMode::ForceCreate) + .unwrap(); // create a normal account + state + .add_balance(&d, &99.into(), CleanupMode::ForceCreate) + .unwrap(); // create another dust account + state.new_contract(&e, 100.into(), 1.into()).unwrap(); // create a contract account + state.init_code(&e, vec![0x00]).unwrap(); + state.commit().unwrap(); + state.drop() + }; + + let mut state = + State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + let mut touched = HashSet::new(); + state + .add_balance( + &a, + &U256::default(), + CleanupMode::TrackTouched(&mut touched), + ) + .unwrap(); // touch an account + state + .transfer_balance(&b, &x, &1.into(), CleanupMode::TrackTouched(&mut touched)) + .unwrap(); // touch an account decreasing its balance + state + .transfer_balance(&c, &x, &1.into(), CleanupMode::TrackTouched(&mut touched)) + .unwrap(); // touch an account decreasing its balance + state + .transfer_balance(&e, &x, &1.into(), CleanupMode::TrackTouched(&mut touched)) + .unwrap(); // touch an account decreasing its balance + state.kill_garbage(&touched, true, &None, false).unwrap(); + assert!(!state.exists(&a).unwrap()); + assert!(state.exists(&b).unwrap()); + state + .kill_garbage(&touched, true, &Some(100.into()), false) + .unwrap(); + assert!(!state.exists(&b).unwrap()); + assert!(state.exists(&c).unwrap()); + assert!(state.exists(&d).unwrap()); + assert!(state.exists(&e).unwrap()); + state + .kill_garbage(&touched, true, &Some(100.into()), true) + .unwrap(); + assert!(state.exists(&c).unwrap()); + assert!(state.exists(&d).unwrap()); + assert!(!state.exists(&e).unwrap()); + } + + #[test] + fn should_trace_diff_suicided_accounts() { + use pod_account; + + let a = 10.into(); + let db = get_temp_state_db(); + let (root, db) = { + let mut state = State::new(db, U256::from(0), Default::default()); + state + .add_balance(&a, &100.into(), CleanupMode::ForceCreate) + .unwrap(); + state.commit().unwrap(); + state.drop() + }; + + let mut state = + State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + let original = state.clone(); + state.kill_account(&a); + + let diff = state.diff_from(original).unwrap(); + let diff_map = diff.get(); + assert_eq!(diff_map.len(), 1); + assert!(diff_map.get(&a).is_some()); + assert_eq!( + diff_map.get(&a), + pod_account::diff_pod( + Some(&PodAccount { + balance: U256::from(100), + nonce: U256::zero(), + code: Some(Default::default()), + storage: Default::default() + }), + None + ) + .as_ref() + ); + } + + #[test] + fn should_trace_diff_unmodified_storage() { + use pod_account; + + let a = 10.into(); + let db = get_temp_state_db(); + + let (root, db) = { + let mut state = State::new(db, U256::from(0), Default::default()); + state + .set_storage( + &a, + H256::from(&U256::from(1u64)), + H256::from(&U256::from(20u64)), + ) + .unwrap(); + state.commit().unwrap(); + state.drop() + }; + + let mut state = + State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + let original = state.clone(); + state + .set_storage( + &a, + H256::from(&U256::from(1u64)), + H256::from(&U256::from(100u64)), + ) + .unwrap(); + + let diff = state.diff_from(original).unwrap(); + let diff_map = diff.get(); + assert_eq!(diff_map.len(), 1); + assert!(diff_map.get(&a).is_some()); + assert_eq!( + diff_map.get(&a), + pod_account::diff_pod( + Some(&PodAccount { + balance: U256::zero(), + nonce: U256::zero(), + code: Some(Default::default()), + storage: vec![( + H256::from(&U256::from(1u64)), + H256::from(&U256::from(20u64)) + )] + .into_iter() + .collect(), + }), + Some(&PodAccount { + balance: U256::zero(), + nonce: U256::zero(), + code: Some(Default::default()), + storage: vec![( + H256::from(&U256::from(1u64)), + H256::from(&U256::from(100u64)) + )] + .into_iter() + .collect(), + }) + ) + .as_ref() + ); + } + + #[cfg(feature = "to-pod-full")] + #[test] + fn should_get_full_pod_storage_values() { + use trie::{TrieFactory, TrieSpec}; + + let a = 10.into(); + let db = get_temp_state_db(); + + let factories = Factories { + vm: Default::default(), + trie: TrieFactory::new(TrieSpec::Fat), + accountdb: Default::default(), + }; + + let get_pod_state_val = |pod_state: &PodState, ak, k| { + pod_state + .get() + .get(ak) + .unwrap() + .storage + .get(&k) + .unwrap() + .clone() + }; + + let storage_address = H256::from(&U256::from(1u64)); + + let (root, db) = { + let mut state = State::new(db, U256::from(0), factories.clone()); + state + .set_storage(&a, storage_address.clone(), H256::from(&U256::from(20u64))) + .unwrap(); + let dump = state.to_pod_full().unwrap(); + assert_eq!( + get_pod_state_val(&dump, &a, storage_address.clone()), + H256::from(&U256::from(20u64)) + ); + state.commit().unwrap(); + let dump = state.to_pod_full().unwrap(); + assert_eq!( + get_pod_state_val(&dump, &a, storage_address.clone()), + H256::from(&U256::from(20u64)) + ); + state.drop() + }; + + let mut state = State::from_existing(db, root, U256::from(0u8), factories).unwrap(); + let dump = state.to_pod_full().unwrap(); + assert_eq!( + get_pod_state_val(&dump, &a, storage_address.clone()), + H256::from(&U256::from(20u64)) + ); + state + .set_storage(&a, storage_address.clone(), H256::from(&U256::from(21u64))) + .unwrap(); + let dump = state.to_pod_full().unwrap(); + assert_eq!( + get_pod_state_val(&dump, &a, storage_address.clone()), + H256::from(&U256::from(21u64)) + ); + state.commit().unwrap(); + state + .set_storage(&a, storage_address.clone(), H256::from(&U256::from(0u64))) + .unwrap(); + let dump = state.to_pod_full().unwrap(); + assert_eq!( + get_pod_state_val(&dump, &a, storage_address.clone()), + H256::from(&U256::from(0u64)) + ); + } } diff --git a/ethcore/src/state/substate.rs b/ethcore/src/state/substate.rs index 86f6e37f8d5..ec23e85e6cb 100644 --- a/ethcore/src/state/substate.rs +++ b/ethcore/src/state/substate.rs @@ -1,106 +1,110 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Execution environment substate. -use std::collections::HashSet; +use super::CleanupMode; use ethereum_types::Address; +use evm::{CleanDustMode, Schedule}; +use std::collections::HashSet; use types::log_entry::LogEntry; -use evm::{Schedule, CleanDustMode}; -use super::CleanupMode; /// State changes which should be applied in finalize, /// after transaction is fully executed. #[derive(Debug, Default)] pub struct Substate { - /// Any accounts that have suicided. - pub suicides: HashSet
, + /// Any accounts that have suicided. + pub suicides: HashSet
, - /// Any accounts that are touched. - pub touched: HashSet
, + /// Any accounts that are touched. + pub touched: HashSet
, - /// Any logs. - pub logs: Vec, + /// Any logs. + pub logs: Vec, - /// Refund counter of SSTORE. - pub sstore_clears_refund: i128, + /// Refund counter of SSTORE. + pub sstore_clears_refund: i128, - /// Created contracts. - pub contracts_created: Vec
, + /// Created contracts. + pub contracts_created: Vec
, } impl Substate { - /// Creates new substate. - pub fn new() -> Self { - Substate::default() - } - - /// Merge secondary substate `s` into self, accruing each element correspondingly. - pub fn accrue(&mut self, s: Substate) { - self.suicides.extend(s.suicides); - self.touched.extend(s.touched); - self.logs.extend(s.logs); - self.sstore_clears_refund += s.sstore_clears_refund; - self.contracts_created.extend(s.contracts_created); - } - - /// Get the cleanup mode object from this. - pub fn to_cleanup_mode(&mut self, schedule: &Schedule) -> CleanupMode { - match (schedule.kill_dust != CleanDustMode::Off, schedule.no_empty, schedule.kill_empty) { - (false, false, _) => CleanupMode::ForceCreate, - (false, true, false) => CleanupMode::NoEmpty, - (false, true, true) | (true, _, _,) => CleanupMode::TrackTouched(&mut self.touched), - } - } + /// Creates new substate. + pub fn new() -> Self { + Substate::default() + } + + /// Merge secondary substate `s` into self, accruing each element correspondingly. + pub fn accrue(&mut self, s: Substate) { + self.suicides.extend(s.suicides); + self.touched.extend(s.touched); + self.logs.extend(s.logs); + self.sstore_clears_refund += s.sstore_clears_refund; + self.contracts_created.extend(s.contracts_created); + } + + /// Get the cleanup mode object from this. + pub fn to_cleanup_mode(&mut self, schedule: &Schedule) -> CleanupMode { + match ( + schedule.kill_dust != CleanDustMode::Off, + schedule.no_empty, + schedule.kill_empty, + ) { + (false, false, _) => CleanupMode::ForceCreate, + (false, true, false) => CleanupMode::NoEmpty, + (false, true, true) | (true, _, _) => CleanupMode::TrackTouched(&mut self.touched), + } + } } #[cfg(test)] mod tests { - use super::Substate; - use types::log_entry::LogEntry; - - #[test] - fn created() { - let sub_state = Substate::new(); - assert_eq!(sub_state.suicides.len(), 0); - } - - #[test] - fn accrue() { - let mut sub_state = Substate::new(); - sub_state.contracts_created.push(1u64.into()); - sub_state.logs.push(LogEntry { - address: 1u64.into(), - topics: vec![], - data: vec![] - }); - sub_state.sstore_clears_refund = (15000 * 5).into(); - sub_state.suicides.insert(10u64.into()); - - let mut sub_state_2 = Substate::new(); - sub_state_2.contracts_created.push(2u64.into()); - sub_state_2.logs.push(LogEntry { - address: 1u64.into(), - topics: vec![], - data: vec![] - }); - sub_state_2.sstore_clears_refund = (15000 * 7).into(); - - sub_state.accrue(sub_state_2); - assert_eq!(sub_state.contracts_created.len(), 2); - assert_eq!(sub_state.sstore_clears_refund, (15000 * 12).into()); - assert_eq!(sub_state.suicides.len(), 1); - } + use super::Substate; + use types::log_entry::LogEntry; + + #[test] + fn created() { + let sub_state = Substate::new(); + assert_eq!(sub_state.suicides.len(), 0); + } + + #[test] + fn accrue() { + let mut sub_state = Substate::new(); + sub_state.contracts_created.push(1u64.into()); + sub_state.logs.push(LogEntry { + address: 1u64.into(), + topics: vec![], + data: vec![], + }); + sub_state.sstore_clears_refund = (15000 * 5).into(); + sub_state.suicides.insert(10u64.into()); + + let mut sub_state_2 = Substate::new(); + sub_state_2.contracts_created.push(2u64.into()); + sub_state_2.logs.push(LogEntry { + address: 1u64.into(), + topics: vec![], + data: vec![], + }); + sub_state_2.sstore_clears_refund = (15000 * 7).into(); + + sub_state.accrue(sub_state_2); + assert_eq!(sub_state.contracts_created.len(), 2); + assert_eq!(sub_state.sstore_clears_refund, (15000 * 12).into()); + assert_eq!(sub_state.suicides.len(), 1); + } } diff --git a/ethcore/src/state_db.rs b/ethcore/src/state_db.rs index 066a4f6162e..306851b9f9f 100644 --- a/ethcore/src/state_db.rs +++ b/ethcore/src/state_db.rs @@ -1,34 +1,32 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! State database abstraction. For more info, see the doc for `StateDB` -use std::collections::{VecDeque, HashSet}; -use std::io; -use std::sync::Arc; +use std::{ + collections::{BTreeMap, HashSet, VecDeque}, + io, + sync::Arc, +}; -use bloom_journal::{Bloom, BloomJournal}; -use byteorder::{LittleEndian, ByteOrder}; -use db::COL_ACCOUNT_BLOOM; -use ethereum_types::{H256, Address}; -use hash::keccak; +use ethereum_types::{Address, H256}; use hash_db::HashDB; use journaldb::JournalDB; use keccak_hasher::KeccakHasher; -use kvdb::{KeyValueDB, DBTransaction, DBValue}; +use kvdb::{DBTransaction, DBValue}; use lru_cache::LruCache; use memory_cache::MemoryLruCache; use parking_lot::Mutex; @@ -36,19 +34,6 @@ use types::BlockNumber; use state::{self, Account}; -/// Value used to initialize bloom bitmap size. -/// -/// Bitmap size is the size in bytes (not bits) that will be allocated in memory. -pub const ACCOUNT_BLOOM_SPACE: usize = 1048576; - -/// Value used to initialize bloom items count. -/// -/// Items count is an estimation of the maximum number of items to store. -pub const DEFAULT_ACCOUNT_PRESET: usize = 1000000; - -/// Key for a value storing amount of hashes -pub const ACCOUNT_BLOOM_HASHCOUNT_KEY: &'static [u8] = b"account_hash_count"; - const STATE_CACHE_BLOCKS: usize = 12; // The percentage of supplied cache size to go to accounts. @@ -56,39 +41,39 @@ const ACCOUNT_CACHE_RATIO: usize = 90; /// Shared canonical state cache. struct AccountCache { - /// DB Account cache. `None` indicates that account is known to be missing. - // When changing the type of the values here, be sure to update `mem_used` and - // `new`. - accounts: LruCache>, - /// Information on the modifications in recently committed blocks; specifically which addresses - /// changed in which block. Ordered by block number. - modifications: VecDeque, + /// DB Account cache. `None` indicates that account is known to be missing. + // When changing the type of the values here, be sure to update `mem_used` and + // `new`. + accounts: LruCache>, + /// Information on the modifications in recently committed blocks; specifically which addresses + /// changed in which block. Ordered by block number. + modifications: VecDeque, } /// Buffered account cache item. struct CacheQueueItem { - /// Account address. - address: Address, - /// Acccount data or `None` if account does not exist. - account: SyncAccount, - /// Indicates that the account was modified before being - /// added to the cache. - modified: bool, + /// Account address. + address: Address, + /// Acccount data or `None` if account does not exist. + account: SyncAccount, + /// Indicates that the account was modified before being + /// added to the cache. + modified: bool, } #[derive(Debug)] /// Accumulates a list of accounts changed in a block. struct BlockChanges { - /// Block number. - number: BlockNumber, - /// Block hash. - hash: H256, - /// Parent block hash. - parent: H256, - /// A set of modified account addresses. - accounts: HashSet
, - /// Block is part of the canonical chain. - is_canon: bool, + /// Block number. + number: BlockNumber, + /// Block hash. + hash: H256, + /// Parent block hash. + parent: H256, + /// A set of modified account addresses. + accounts: HashSet
, + /// Block is part of the canonical chain. + is_canon: bool, } /// State database abstraction. @@ -106,367 +91,348 @@ struct BlockChanges { /// Then, after the block has been added to the chain the local cache in the /// `StateDB` is propagated into the global cache. pub struct StateDB { - /// Backing database. - db: Box, - /// Shared canonical state cache. - account_cache: Arc>, - /// DB Code cache. Maps code hashes to shared bytes. - code_cache: Arc>>>>, - /// Local dirty cache. - local_cache: Vec, - /// Shared account bloom. Does not handle chain reorganizations. - account_bloom: Arc>, - cache_size: usize, - /// Hash of the block on top of which this instance was created or - /// `None` if cache is disabled - parent_hash: Option, - /// Hash of the committing block or `None` if not committed yet. - commit_hash: Option, - /// Number of the committing block or `None` if not committed yet. - commit_number: Option, + /// Backing database. + db: Box, + /// Shared canonical state cache. + account_cache: Arc>, + /// DB Code cache. Maps code hashes to shared bytes. + code_cache: Arc>>>>, + /// Local dirty cache. + local_cache: Vec, + cache_size: usize, + /// Hash of the block on top of which this instance was created or + /// `None` if cache is disabled + parent_hash: Option, + /// Hash of the committing block or `None` if not committed yet. + commit_hash: Option, + /// Number of the committing block or `None` if not committed yet. + commit_number: Option, } impl StateDB { - - /// Create a new instance wrapping `JournalDB` and the maximum allowed size - /// of the LRU cache in bytes. Actual used memory may (read: will) be higher due to bookkeeping. - // TODO: make the cache size actually accurate by moving the account storage cache - // into the `AccountCache` structure as its own `LruCache<(Address, H256), H256>`. - pub fn new(db: Box, cache_size: usize) -> StateDB { - let bloom = Self::load_bloom(&**db.backing()); - let acc_cache_size = cache_size * ACCOUNT_CACHE_RATIO / 100; - let code_cache_size = cache_size - acc_cache_size; - let cache_items = acc_cache_size / ::std::mem::size_of::>(); - - StateDB { - db: db, - account_cache: Arc::new(Mutex::new(AccountCache { - accounts: LruCache::new(cache_items), - modifications: VecDeque::new(), - })), - code_cache: Arc::new(Mutex::new(MemoryLruCache::new(code_cache_size))), - local_cache: Vec::new(), - account_bloom: Arc::new(Mutex::new(bloom)), - cache_size: cache_size, - parent_hash: None, - commit_hash: None, - commit_number: None, - } - } - - /// Loads accounts bloom from the database - /// This bloom is used to handle request for the non-existant account fast - pub fn load_bloom(db: &KeyValueDB) -> Bloom { - let hash_count_entry = db.get(COL_ACCOUNT_BLOOM, ACCOUNT_BLOOM_HASHCOUNT_KEY) - .expect("Low-level database error"); - - let hash_count_bytes = match hash_count_entry { - Some(bytes) => bytes, - None => return Bloom::new(ACCOUNT_BLOOM_SPACE, DEFAULT_ACCOUNT_PRESET), - }; - - assert_eq!(hash_count_bytes.len(), 1); - let hash_count = hash_count_bytes[0]; - - let mut bloom_parts = vec![0u64; ACCOUNT_BLOOM_SPACE / 8]; - let mut key = [0u8; 8]; - for i in 0..ACCOUNT_BLOOM_SPACE / 8 { - LittleEndian::write_u64(&mut key, i as u64); - bloom_parts[i] = db.get(COL_ACCOUNT_BLOOM, &key).expect("low-level database error") - .and_then(|val| Some(LittleEndian::read_u64(&val[..]))) - .unwrap_or(0u64); - } - - let bloom = Bloom::from_parts(&bloom_parts, hash_count as u32); - trace!(target: "account_bloom", "Bloom is {:?} full, hash functions count = {:?}", bloom.saturation(), hash_count); - bloom - } - - /// Commit blooms journal to the database transaction - pub fn commit_bloom(batch: &mut DBTransaction, journal: BloomJournal) -> io::Result<()> { - assert!(journal.hash_functions <= 255); - batch.put(COL_ACCOUNT_BLOOM, ACCOUNT_BLOOM_HASHCOUNT_KEY, &[journal.hash_functions as u8]); - let mut key = [0u8; 8]; - let mut val = [0u8; 8]; - - for (bloom_part_index, bloom_part_value) in journal.entries { - LittleEndian::write_u64(&mut key, bloom_part_index as u64); - LittleEndian::write_u64(&mut val, bloom_part_value); - batch.put(COL_ACCOUNT_BLOOM, &key, &val); - } - Ok(()) - } - - /// Journal all recent operations under the given era and ID. - pub fn journal_under(&mut self, batch: &mut DBTransaction, now: u64, id: &H256) -> io::Result { - { - let mut bloom_lock = self.account_bloom.lock(); - Self::commit_bloom(batch, bloom_lock.drain_journal())?; - } - let records = self.db.journal_under(batch, now, id)?; - self.commit_hash = Some(id.clone()); - self.commit_number = Some(now); - Ok(records) - } - - /// Mark a given candidate from an ancient era as canonical, enacting its removals from the - /// backing database and reverting any non-canonical historical commit's insertions. - pub fn mark_canonical(&mut self, batch: &mut DBTransaction, end_era: u64, canon_id: &H256) -> io::Result { - self.db.mark_canonical(batch, end_era, canon_id) - } - - /// Propagate local cache into the global cache and synchonize - /// the global cache with the best block state. - /// This function updates the global cache by removing entries - /// that are invalidated by chain reorganization. `sync_cache` - /// should be called after the block has been committed and the - /// blockchain route has ben calculated. - pub fn sync_cache(&mut self, enacted: &[H256], retracted: &[H256], is_best: bool) { - trace!("sync_cache id = (#{:?}, {:?}), parent={:?}, best={}", self.commit_number, self.commit_hash, self.parent_hash, is_best); - let mut cache = self.account_cache.lock(); - let cache = &mut *cache; - - // Purge changes from re-enacted and retracted blocks. - // Filter out commiting block if any. - let mut clear = false; - for block in enacted.iter().filter(|h| self.commit_hash.as_ref().map_or(true, |p| *h != p)) { - clear = clear || { - if let Some(ref mut m) = cache.modifications.iter_mut().find(|m| &m.hash == block) { - trace!("Reverting enacted block {:?}", block); - m.is_canon = true; - for a in &m.accounts { - trace!("Reverting enacted address {:?}", a); - cache.accounts.remove(a); - } - false - } else { - true - } - }; - } - - for block in retracted { - clear = clear || { - if let Some(ref mut m) = cache.modifications.iter_mut().find(|m| &m.hash == block) { - trace!("Retracting block {:?}", block); - m.is_canon = false; - for a in &m.accounts { - trace!("Retracted address {:?}", a); - cache.accounts.remove(a); - } - false - } else { - true - } - }; - } - if clear { - // We don't know anything about the block; clear everything - trace!("Wiping cache"); - cache.accounts.clear(); - cache.modifications.clear(); - } - - // Propagate cache only if committing on top of the latest canonical state - // blocks are ordered by number and only one block with a given number is marked as canonical - // (contributed to canonical state cache) - if let (Some(ref number), Some(ref hash), Some(ref parent)) = (self.commit_number, self.commit_hash, self.parent_hash) { - if cache.modifications.len() == STATE_CACHE_BLOCKS { - cache.modifications.pop_back(); - } - let mut modifications = HashSet::new(); - trace!("committing {} cache entries", self.local_cache.len()); - for account in self.local_cache.drain(..) { - if account.modified { - modifications.insert(account.address.clone()); - } - if is_best { - let acc = account.account.0; - if let Some(&mut Some(ref mut existing)) = cache.accounts.get_mut(&account.address) { - if let Some(new) = acc { - if account.modified { - existing.overwrite_with(new); - } - continue; - } - } - cache.accounts.insert(account.address, acc); - } - } - - // Save modified accounts. These are ordered by the block number. - let block_changes = BlockChanges { - accounts: modifications, - number: *number, - hash: hash.clone(), - is_canon: is_best, - parent: parent.clone(), - }; - let insert_at = cache.modifications.iter().enumerate().find(|&(_, m)| m.number < *number).map(|(i, _)| i); - trace!("inserting modifications at {:?}", insert_at); - if let Some(insert_at) = insert_at { - cache.modifications.insert(insert_at, block_changes); - } else { - cache.modifications.push_back(block_changes); - } - } - } - - /// Conversion method to interpret self as `HashDB` reference - pub fn as_hash_db(&self) -> &HashDB { - self.db.as_hash_db() - } - - /// Conversion method to interpret self as mutable `HashDB` reference - pub fn as_hash_db_mut(&mut self) -> &mut HashDB { - self.db.as_hash_db_mut() - } - - /// Clone the database. - pub fn boxed_clone(&self) -> StateDB { - StateDB { - db: self.db.boxed_clone(), - account_cache: self.account_cache.clone(), - code_cache: self.code_cache.clone(), - local_cache: Vec::new(), - account_bloom: self.account_bloom.clone(), - cache_size: self.cache_size, - parent_hash: None, - commit_hash: None, - commit_number: None, - } - } - - /// Clone the database for a canonical state. - pub fn boxed_clone_canon(&self, parent: &H256) -> StateDB { - StateDB { - db: self.db.boxed_clone(), - account_cache: self.account_cache.clone(), - code_cache: self.code_cache.clone(), - local_cache: Vec::new(), - account_bloom: self.account_bloom.clone(), - cache_size: self.cache_size, - parent_hash: Some(parent.clone()), - commit_hash: None, - commit_number: None, - } - } - - /// Check if pruning is enabled on the database. - pub fn is_pruned(&self) -> bool { - self.db.is_pruned() - } - - /// Heap size used. - pub fn mem_used(&self) -> usize { - // TODO: account for LRU-cache overhead; this is a close approximation. - self.db.mem_used() + { - let accounts = self.account_cache.lock().accounts.len(); - let code_size = self.code_cache.lock().current_size(); - code_size + accounts * ::std::mem::size_of::>() - } - } - - /// Returns underlying `JournalDB`. - pub fn journal_db(&self) -> &JournalDB { - &*self.db - } - - /// Query how much memory is set aside for the accounts cache (in bytes). - pub fn cache_size(&self) -> usize { - self.cache_size - } - - /// Check if the account can be returned from cache by matching current block parent hash against canonical - /// state and filtering out account modified in later blocks. - fn is_allowed(addr: &Address, parent_hash: &H256, modifications: &VecDeque) -> bool { - if modifications.is_empty() { - return true; - } - // Ignore all accounts modified in later blocks - // Modifications contains block ordered by the number - // We search for our parent in that list first and then for - // all its parent until we hit the canonical block, - // checking against all the intermediate modifications. - let mut parent = parent_hash; - for m in modifications { - if &m.hash == parent { - if m.is_canon { - return true; - } - parent = &m.parent; - } - if m.accounts.contains(addr) { - trace!("Cache lookup skipped for {:?}: modified in a later block", addr); - return false; - } - } - trace!("Cache lookup skipped for {:?}: parent hash is unknown", addr); - false - } + /// Create a new instance wrapping `JournalDB` and the maximum allowed size + /// of the LRU cache in bytes. Actual used memory may (read: will) be higher due to bookkeeping. + // TODO: make the cache size actually accurate by moving the account storage cache + // into the `AccountCache` structure as its own `LruCache<(Address, H256), H256>`. + pub fn new(db: Box, cache_size: usize) -> StateDB { + let acc_cache_size = cache_size * ACCOUNT_CACHE_RATIO / 100; + let code_cache_size = cache_size - acc_cache_size; + let cache_items = acc_cache_size / ::std::mem::size_of::>(); + + StateDB { + db: db, + account_cache: Arc::new(Mutex::new(AccountCache { + accounts: LruCache::new(cache_items), + modifications: VecDeque::new(), + })), + code_cache: Arc::new(Mutex::new(MemoryLruCache::new(code_cache_size))), + local_cache: Vec::new(), + cache_size: cache_size, + parent_hash: None, + commit_hash: None, + commit_number: None, + } + } + + /// Journal all recent operations under the given era and ID. + pub fn journal_under( + &mut self, + batch: &mut DBTransaction, + now: u64, + id: &H256, + ) -> io::Result { + let records = self.db.journal_under(batch, now, id)?; + self.commit_hash = Some(id.clone()); + self.commit_number = Some(now); + Ok(records) + } + + /// Mark a given candidate from an ancient era as canonical, enacting its removals from the + /// backing database and reverting any non-canonical historical commit's insertions. + pub fn mark_canonical( + &mut self, + batch: &mut DBTransaction, + end_era: u64, + canon_id: &H256, + ) -> io::Result { + self.db.mark_canonical(batch, end_era, canon_id) + } + + /// Propagate local cache into the global cache and synchonize + /// the global cache with the best block state. + /// This function updates the global cache by removing entries + /// that are invalidated by chain reorganization. `sync_cache` + /// should be called after the block has been committed and the + /// blockchain route has ben calculated. + pub fn sync_cache(&mut self, enacted: &[H256], retracted: &[H256], is_best: bool) { + trace!( + "sync_cache id = (#{:?}, {:?}), parent={:?}, best={}", + self.commit_number, + self.commit_hash, + self.parent_hash, + is_best + ); + let mut cache = self.account_cache.lock(); + let cache = &mut *cache; + + // Purge changes from re-enacted and retracted blocks. + // Filter out commiting block if any. + let mut clear = false; + for block in enacted + .iter() + .filter(|h| self.commit_hash.as_ref().map_or(true, |p| *h != p)) + { + clear = clear || { + if let Some(ref mut m) = cache.modifications.iter_mut().find(|m| &m.hash == block) { + trace!("Reverting enacted block {:?}", block); + m.is_canon = true; + for a in &m.accounts { + trace!("Reverting enacted address {:?}", a); + cache.accounts.remove(a); + } + false + } else { + true + } + }; + } + + for block in retracted { + clear = clear || { + if let Some(ref mut m) = cache.modifications.iter_mut().find(|m| &m.hash == block) { + trace!("Retracting block {:?}", block); + m.is_canon = false; + for a in &m.accounts { + trace!("Retracted address {:?}", a); + cache.accounts.remove(a); + } + false + } else { + true + } + }; + } + if clear { + // We don't know anything about the block; clear everything + trace!("Wiping cache"); + cache.accounts.clear(); + cache.modifications.clear(); + } + + // Propagate cache only if committing on top of the latest canonical state + // blocks are ordered by number and only one block with a given number is marked as canonical + // (contributed to canonical state cache) + if let (Some(ref number), Some(ref hash), Some(ref parent)) = + (self.commit_number, self.commit_hash, self.parent_hash) + { + if cache.modifications.len() == STATE_CACHE_BLOCKS { + cache.modifications.pop_back(); + } + let mut modifications = HashSet::new(); + trace!("committing {} cache entries", self.local_cache.len()); + for account in self.local_cache.drain(..) { + if account.modified { + modifications.insert(account.address.clone()); + } + if is_best { + let acc = account.account.0; + if let Some(&mut Some(ref mut existing)) = + cache.accounts.get_mut(&account.address) + { + if let Some(new) = acc { + if account.modified { + existing.overwrite_with(new); + } + continue; + } + } + cache.accounts.insert(account.address, acc); + } + } + + // Save modified accounts. These are ordered by the block number. + let block_changes = BlockChanges { + accounts: modifications, + number: *number, + hash: hash.clone(), + is_canon: is_best, + parent: parent.clone(), + }; + let insert_at = cache + .modifications + .iter() + .enumerate() + .find(|&(_, m)| m.number < *number) + .map(|(i, _)| i); + trace!("inserting modifications at {:?}", insert_at); + if let Some(insert_at) = insert_at { + cache.modifications.insert(insert_at, block_changes); + } else { + cache.modifications.push_back(block_changes); + } + } + } + + /// Conversion method to interpret self as `HashDB` reference + pub fn as_hash_db(&self) -> &dyn HashDB { + self.db.as_hash_db() + } + + /// Conversion method to interpret self as mutable `HashDB` reference + pub fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { + self.db.as_hash_db_mut() + } + + /// Clone the database. + pub fn boxed_clone(&self) -> StateDB { + StateDB { + db: self.db.boxed_clone(), + account_cache: self.account_cache.clone(), + code_cache: self.code_cache.clone(), + local_cache: Vec::new(), + cache_size: self.cache_size, + parent_hash: None, + commit_hash: None, + commit_number: None, + } + } + + /// Clone the database for a canonical state. + pub fn boxed_clone_canon(&self, parent: &H256) -> StateDB { + StateDB { + db: self.db.boxed_clone(), + account_cache: self.account_cache.clone(), + code_cache: self.code_cache.clone(), + local_cache: Vec::new(), + cache_size: self.cache_size, + parent_hash: Some(parent.clone()), + commit_hash: None, + commit_number: None, + } + } + + /// Check if pruning is enabled on the database. + pub fn is_pruned(&self) -> bool { + self.db.is_pruned() + } + + /// Heap size used. + pub fn get_sizes(&self, sizes: &mut BTreeMap) { + self.db.get_sizes(sizes); + + sizes.insert( + String::from("account_cache_len"), + self.account_cache.lock().accounts.len(), + ); + sizes.insert( + String::from("code_cache_size"), + self.code_cache.lock().current_size(), + ); + } + + /// Returns underlying `JournalDB`. + pub fn journal_db(&self) -> &dyn JournalDB { + &*self.db + } + + /// Query how much memory is set aside for the accounts cache (in bytes). + pub fn cache_size(&self) -> usize { + self.cache_size + } + + /// Check if the account can be returned from cache by matching current block parent hash against canonical + /// state and filtering out account modified in later blocks. + fn is_allowed( + addr: &Address, + parent_hash: &H256, + modifications: &VecDeque, + ) -> bool { + if modifications.is_empty() { + return true; + } + // Ignore all accounts modified in later blocks + // Modifications contains block ordered by the number + // We search for our parent in that list first and then for + // all its parent until we hit the canonical block, + // checking against all the intermediate modifications. + let mut parent = parent_hash; + for m in modifications { + if &m.hash == parent { + if m.is_canon { + return true; + } + parent = &m.parent; + } + if m.accounts.contains(addr) { + trace!( + "Cache lookup skipped for {:?}: modified in a later block", + addr + ); + return false; + } + } + trace!( + "Cache lookup skipped for {:?}: parent hash is unknown", + addr + ); + false + } } impl state::Backend for StateDB { - fn as_hash_db(&self) -> &HashDB { self.db.as_hash_db() } - - fn as_hash_db_mut(&mut self) -> &mut HashDB { - self.db.as_hash_db_mut() - } - - fn add_to_account_cache(&mut self, addr: Address, data: Option, modified: bool) { - self.local_cache.push(CacheQueueItem { - address: addr, - account: SyncAccount(data), - modified: modified, - }) - } - - fn cache_code(&self, hash: H256, code: Arc>) { - let mut cache = self.code_cache.lock(); - - cache.insert(hash, code); - } - - fn get_cached_account(&self, addr: &Address) -> Option> { - self.parent_hash.as_ref().and_then(|parent_hash| { - let mut cache = self.account_cache.lock(); - if !Self::is_allowed(addr, parent_hash, &cache.modifications) { - return None; - } - cache.accounts.get_mut(addr).map(|a| a.as_ref().map(|a| a.clone_basic())) - }) - } - - fn get_cached(&self, a: &Address, f: F) -> Option - where F: FnOnce(Option<&mut Account>) -> U - { - self.parent_hash.as_ref().and_then(|parent_hash| { - let mut cache = self.account_cache.lock(); - if !Self::is_allowed(a, parent_hash, &cache.modifications) { - return None; - } - cache.accounts.get_mut(a).map(|c| f(c.as_mut())) - }) - } - - fn get_cached_code(&self, hash: &H256) -> Option>> { - let mut cache = self.code_cache.lock(); - - cache.get_mut(hash).map(|code| code.clone()) - } - - fn note_non_null_account(&self, address: &Address) { - trace!(target: "account_bloom", "Note account bloom: {:?}", address); - let mut bloom = self.account_bloom.lock(); - bloom.set(&*keccak(address)); - } - - fn is_known_null(&self, address: &Address) -> bool { - trace!(target: "account_bloom", "Check account bloom: {:?}", address); - let bloom = self.account_bloom.lock(); - let is_null = !bloom.check(&*keccak(address)); - is_null - } + fn as_hash_db(&self) -> &dyn HashDB { + self.db.as_hash_db() + } + + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { + self.db.as_hash_db_mut() + } + + fn add_to_account_cache(&mut self, addr: Address, data: Option, modified: bool) { + self.local_cache.push(CacheQueueItem { + address: addr, + account: SyncAccount(data), + modified: modified, + }) + } + + fn cache_code(&self, hash: H256, code: Arc>) { + let mut cache = self.code_cache.lock(); + + cache.insert(hash, code); + } + + fn get_cached_account(&self, addr: &Address) -> Option> { + self.parent_hash.as_ref().and_then(|parent_hash| { + let mut cache = self.account_cache.lock(); + if !Self::is_allowed(addr, parent_hash, &cache.modifications) { + return None; + } + cache + .accounts + .get_mut(addr) + .map(|a| a.as_ref().map(|a| a.clone_basic())) + }) + } + + fn get_cached(&self, a: &Address, f: F) -> Option + where + F: FnOnce(Option<&mut Account>) -> U, + { + self.parent_hash.as_ref().and_then(|parent_hash| { + let mut cache = self.account_cache.lock(); + if !Self::is_allowed(a, parent_hash, &cache.modifications) { + return None; + } + cache.accounts.get_mut(a).map(|c| f(c.as_mut())) + }) + } + + fn get_cached_code(&self, hash: &H256) -> Option>> { + let mut cache = self.code_cache.lock(); + + cache.get_mut(hash).map(|code| code.clone()) + } } /// Sync wrapper for the account. @@ -478,75 +444,82 @@ unsafe impl Sync for SyncAccount {} #[cfg(test)] mod tests { - use ethereum_types::{H256, U256, Address}; - use kvdb::DBTransaction; - use test_helpers::get_temp_state_db; - use state::{Account, Backend}; - - #[test] - fn state_db_smoke() { - let _ = ::env_logger::try_init(); - - let state_db = get_temp_state_db(); - let root_parent = H256::random(); - let address = Address::random(); - let h0 = H256::random(); - let h1a = H256::random(); - let h1b = H256::random(); - let h2a = H256::random(); - let h2b = H256::random(); - let h3a = H256::random(); - let h3b = H256::random(); - let mut batch = DBTransaction::new(); - - // blocks [ 3a(c) 2a(c) 2b 1b 1a(c) 0 ] - // balance [ 5 5 4 3 2 2 ] - let mut s = state_db.boxed_clone_canon(&root_parent); - s.add_to_account_cache(address, Some(Account::new_basic(2.into(), 0.into())), false); - s.journal_under(&mut batch, 0, &h0).unwrap(); - s.sync_cache(&[], &[], true); - - let mut s = state_db.boxed_clone_canon(&h0); - s.journal_under(&mut batch, 1, &h1a).unwrap(); - s.sync_cache(&[], &[], true); - - let mut s = state_db.boxed_clone_canon(&h0); - s.add_to_account_cache(address, Some(Account::new_basic(3.into(), 0.into())), true); - s.journal_under(&mut batch, 1, &h1b).unwrap(); - s.sync_cache(&[], &[], false); - - let mut s = state_db.boxed_clone_canon(&h1b); - s.add_to_account_cache(address, Some(Account::new_basic(4.into(), 0.into())), true); - s.journal_under(&mut batch, 2, &h2b).unwrap(); - s.sync_cache(&[], &[], false); - - let mut s = state_db.boxed_clone_canon(&h1a); - s.add_to_account_cache(address, Some(Account::new_basic(5.into(), 0.into())), true); - s.journal_under(&mut batch, 2, &h2a).unwrap(); - s.sync_cache(&[], &[], true); - - let mut s = state_db.boxed_clone_canon(&h2a); - s.journal_under(&mut batch, 3, &h3a).unwrap(); - s.sync_cache(&[], &[], true); - - let s = state_db.boxed_clone_canon(&h3a); - assert_eq!(s.get_cached_account(&address).unwrap().unwrap().balance(), &U256::from(5)); - - let s = state_db.boxed_clone_canon(&h1a); - assert!(s.get_cached_account(&address).is_none()); - - let s = state_db.boxed_clone_canon(&h2b); - assert!(s.get_cached_account(&address).is_none()); - - let s = state_db.boxed_clone_canon(&h1b); - assert!(s.get_cached_account(&address).is_none()); - - // reorg to 3b - // blocks [ 3b(c) 3a 2a 2b(c) 1b 1a 0 ] - let mut s = state_db.boxed_clone_canon(&h2b); - s.journal_under(&mut batch, 3, &h3b).unwrap(); - s.sync_cache(&[h1b.clone(), h2b.clone(), h3b.clone()], &[h1a.clone(), h2a.clone(), h3a.clone()], true); - let s = state_db.boxed_clone_canon(&h3a); - assert!(s.get_cached_account(&address).is_none()); - } + use ethereum_types::{Address, H256, U256}; + use kvdb::DBTransaction; + use state::{Account, Backend}; + use test_helpers::get_temp_state_db; + + #[test] + fn state_db_smoke() { + let _ = ::env_logger::try_init(); + + let state_db = get_temp_state_db(); + let root_parent = H256::random(); + let address = Address::random(); + let h0 = H256::random(); + let h1a = H256::random(); + let h1b = H256::random(); + let h2a = H256::random(); + let h2b = H256::random(); + let h3a = H256::random(); + let h3b = H256::random(); + let mut batch = DBTransaction::new(); + + // blocks [ 3a(c) 2a(c) 2b 1b 1a(c) 0 ] + // balance [ 5 5 4 3 2 2 ] + let mut s = state_db.boxed_clone_canon(&root_parent); + s.add_to_account_cache(address, Some(Account::new_basic(2.into(), 0.into())), false); + s.journal_under(&mut batch, 0, &h0).unwrap(); + s.sync_cache(&[], &[], true); + + let mut s = state_db.boxed_clone_canon(&h0); + s.journal_under(&mut batch, 1, &h1a).unwrap(); + s.sync_cache(&[], &[], true); + + let mut s = state_db.boxed_clone_canon(&h0); + s.add_to_account_cache(address, Some(Account::new_basic(3.into(), 0.into())), true); + s.journal_under(&mut batch, 1, &h1b).unwrap(); + s.sync_cache(&[], &[], false); + + let mut s = state_db.boxed_clone_canon(&h1b); + s.add_to_account_cache(address, Some(Account::new_basic(4.into(), 0.into())), true); + s.journal_under(&mut batch, 2, &h2b).unwrap(); + s.sync_cache(&[], &[], false); + + let mut s = state_db.boxed_clone_canon(&h1a); + s.add_to_account_cache(address, Some(Account::new_basic(5.into(), 0.into())), true); + s.journal_under(&mut batch, 2, &h2a).unwrap(); + s.sync_cache(&[], &[], true); + + let mut s = state_db.boxed_clone_canon(&h2a); + s.journal_under(&mut batch, 3, &h3a).unwrap(); + s.sync_cache(&[], &[], true); + + let s = state_db.boxed_clone_canon(&h3a); + assert_eq!( + s.get_cached_account(&address).unwrap().unwrap().balance(), + &U256::from(5) + ); + + let s = state_db.boxed_clone_canon(&h1a); + assert!(s.get_cached_account(&address).is_none()); + + let s = state_db.boxed_clone_canon(&h2b); + assert!(s.get_cached_account(&address).is_none()); + + let s = state_db.boxed_clone_canon(&h1b); + assert!(s.get_cached_account(&address).is_none()); + + // reorg to 3b + // blocks [ 3b(c) 3a 2a 2b(c) 1b 1a 0 ] + let mut s = state_db.boxed_clone_canon(&h2b); + s.journal_under(&mut batch, 3, &h3b).unwrap(); + s.sync_cache( + &[h1b.clone(), h2b.clone(), h3b.clone()], + &[h1a.clone(), h2a.clone(), h3a.clone()], + true, + ); + let s = state_db.boxed_clone_canon(&h3a); + assert!(s.get_cached_account(&address).is_none()); + } } diff --git a/ethcore/src/test_helpers.rs b/ethcore/src/test_helpers.rs index b5575f36cd9..2bf3a8410bb 100644 --- a/ethcore/src/test_helpers.rs +++ b/ethcore/src/test_helpers.rs @@ -1,29 +1,29 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Set of different helpers for client tests -use std::path::Path; -use std::sync::Arc; -use std::{fs, io}; +use std::{fs, io, path::Path, sync::Arc}; -use blockchain::{BlockChain, BlockChainDB, BlockChainDBHandler, Config as BlockChainConfig, ExtrasInsert}; +use blockchain::{ + BlockChain, BlockChainDB, BlockChainDBHandler, Config as BlockChainConfig, ExtrasInsert, +}; use blooms_db; use bytes::Bytes; -use ethereum_types::{H256, U256, Address}; +use ethereum_types::{Address, H256, U256}; use ethkey::KeyPair; use evm::Factory as EvmFactory; use hash::keccak; @@ -33,14 +33,18 @@ use kvdb_rocksdb::{self, Database, DatabaseConfig}; use parking_lot::RwLock; use rlp::{self, RlpStream}; use tempdir::TempDir; -use types::transaction::{Action, Transaction, SignedTransaction}; -use types::encoded; -use types::header::Header; -use types::view; -use types::views::BlockView; - -use block::{OpenBlock, Drain}; -use client::{Client, ClientConfig, ChainInfo, ImportBlock, ChainNotify, ChainMessageType, PrepareOpenBlock}; +use types::{ + encoded, + header::Header, + transaction::{Action, SignedTransaction, Transaction}, + view, + views::BlockView, +}; + +use block::{Drain, OpenBlock}; +use client::{ + ChainInfo, ChainMessageType, ChainNotify, Client, ClientConfig, ImportBlock, PrepareOpenBlock, +}; use factory::Factories; use miner::Miner; use spec::Spec; @@ -50,468 +54,567 @@ use verification::queue::kind::blocks::Unverified; /// Creates test block with corresponding header pub fn create_test_block(header: &Header) -> Bytes { - let mut rlp = RlpStream::new_list(3); - rlp.append(header); - rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1); - rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1); - rlp.out() + let mut rlp = RlpStream::new_list(3); + rlp.append(header); + rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1); + rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1); + rlp.out() } fn create_unverifiable_block_header(order: u32, parent_hash: H256) -> Header { - let mut header = Header::new(); - header.set_gas_limit(0.into()); - header.set_difficulty((order * 100).into()); - header.set_timestamp((order * 10) as u64); - header.set_number(order as u64); - header.set_parent_hash(parent_hash); - header.set_state_root(H256::zero()); - - header + let mut header = Header::new(); + header.set_gas_limit(0.into()); + header.set_difficulty((order * 100).into()); + header.set_timestamp((order * 10) as u64); + header.set_number(order as u64); + header.set_parent_hash(parent_hash); + header.set_state_root(H256::zero()); + + header } -fn create_unverifiable_block_with_extra(order: u32, parent_hash: H256, extra: Option) -> Bytes { - let mut header = create_unverifiable_block_header(order, parent_hash); - header.set_extra_data(match extra { - Some(extra_data) => extra_data, - None => { - let base = (order & 0x000000ff) as u8; - let generated: Vec = vec![base + 1, base + 2, base + 3]; - generated - } - }); - create_test_block(&header) +fn create_unverifiable_block_with_extra( + order: u32, + parent_hash: H256, + extra: Option, +) -> Bytes { + let mut header = create_unverifiable_block_header(order, parent_hash); + header.set_extra_data(match extra { + Some(extra_data) => extra_data, + None => { + let base = (order & 0x000000ff) as u8; + let generated: Vec = vec![base + 1, base + 2, base + 3]; + generated + } + }); + create_test_block(&header) } fn create_unverifiable_block(order: u32, parent_hash: H256) -> Bytes { - create_test_block(&create_unverifiable_block_header(order, parent_hash)) + create_test_block(&create_unverifiable_block_header(order, parent_hash)) } /// Creates test block with corresponding header and data -pub fn create_test_block_with_data(header: &Header, transactions: &[SignedTransaction], uncles: &[Header]) -> Bytes { - let mut rlp = RlpStream::new_list(3); - rlp.append(header); - rlp.begin_list(transactions.len()); - for t in transactions { - rlp.append_raw(&rlp::encode(t), 1); - } - rlp.append_list(&uncles); - rlp.out() +pub fn create_test_block_with_data( + header: &Header, + transactions: &[SignedTransaction], + uncles: &[Header], +) -> Bytes { + let mut rlp = RlpStream::new_list(3); + rlp.append(header); + rlp.begin_list(transactions.len()); + for t in transactions { + rlp.append_raw(&rlp::encode(t), 1); + } + rlp.append_list(&uncles); + rlp.out() } /// Generates dummy client (not test client) with corresponding amount of blocks pub fn generate_dummy_client(block_number: u32) -> Arc { - generate_dummy_client_with_spec_and_data(Spec::new_test, block_number, 0, &[]) + generate_dummy_client_with_spec_and_data(Spec::new_test, block_number, 0, &[]) } /// Generates dummy client (not test client) with corresponding amount of blocks and txs per every block -pub fn generate_dummy_client_with_data(block_number: u32, txs_per_block: usize, tx_gas_prices: &[U256]) -> Arc { - generate_dummy_client_with_spec_and_data(Spec::new_null, block_number, txs_per_block, tx_gas_prices) +pub fn generate_dummy_client_with_data( + block_number: u32, + txs_per_block: usize, + tx_gas_prices: &[U256], +) -> Arc { + generate_dummy_client_with_spec_and_data( + Spec::new_null, + block_number, + txs_per_block, + tx_gas_prices, + ) } /// Generates dummy client (not test client) with corresponding spec and accounts -pub fn generate_dummy_client_with_spec(test_spec: F) -> Arc where F: Fn()->Spec { - generate_dummy_client_with_spec_and_data(test_spec, 0, 0, &[]) +pub fn generate_dummy_client_with_spec(test_spec: F) -> Arc +where + F: Fn() -> Spec, +{ + generate_dummy_client_with_spec_and_data(test_spec, 0, 0, &[]) } /// Generates dummy client (not test client) with corresponding amount of blocks, txs per block and spec -pub fn generate_dummy_client_with_spec_and_data(test_spec: F, block_number: u32, txs_per_block: usize, tx_gas_prices: &[U256]) -> Arc where - F: Fn() -> Spec +pub fn generate_dummy_client_with_spec_and_data( + test_spec: F, + block_number: u32, + txs_per_block: usize, + tx_gas_prices: &[U256], +) -> Arc +where + F: Fn() -> Spec, { - let test_spec = test_spec(); - let client_db = new_db(); - - let client = Client::new( - ClientConfig::default(), - &test_spec, - client_db, - Arc::new(Miner::new_for_tests(&test_spec, None)), - IoChannel::disconnected(), - ).unwrap(); - let test_engine = &*test_spec.engine; - - let mut db = test_spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let genesis_header = test_spec.genesis_header(); - - let mut rolling_timestamp = 40; - let mut last_hashes = vec![]; - let mut last_header = genesis_header.clone(); - - let kp = KeyPair::from_secret_slice(&keccak("")).unwrap(); - let author = kp.address(); - - let mut n = 0; - for _ in 0..block_number { - last_hashes.push(last_header.hash()); - - // forge block. - let mut b = OpenBlock::new( - test_engine, - Default::default(), - false, - db, - &last_header, - Arc::new(last_hashes.clone()), - author.clone(), - (3141562.into(), 31415620.into()), - vec![], - false, - None, - ).unwrap(); - rolling_timestamp += 10; - b.set_timestamp(rolling_timestamp); - - // first block we don't have any balance, so can't send any transactions. - for _ in 0..txs_per_block { - b.push_transaction(Transaction { - nonce: n.into(), - gas_price: tx_gas_prices[n % tx_gas_prices.len()], - gas: 100000.into(), - action: Action::Create, - data: vec![], - value: U256::zero(), - }.sign(kp.secret(), Some(test_spec.chain_id())), None).unwrap(); - n += 1; - } - - let b = b.close_and_lock().unwrap().seal(test_engine, vec![]).unwrap(); - - if let Err(e) = client.import_block(Unverified::from_rlp(b.rlp_bytes()).unwrap()) { - panic!("error importing block which is valid by definition: {:?}", e); - } - - last_header = view!(BlockView, &b.rlp_bytes()).header(); - db = b.drain().state.drop().1; - } - client.flush_queue(); - client.import_verified_blocks(); - client + let test_spec = test_spec(); + let client_db = new_db(); + + let client = Client::new( + ClientConfig::default(), + &test_spec, + client_db, + Arc::new(Miner::new_for_tests(&test_spec, None)), + IoChannel::disconnected(), + ) + .unwrap(); + let test_engine = &*test_spec.engine; + + let mut db = test_spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let genesis_header = test_spec.genesis_header(); + + let mut rolling_timestamp = 40; + let mut last_hashes = vec![]; + let mut last_header = genesis_header.clone(); + + let kp = KeyPair::from_secret_slice(&keccak("")).unwrap(); + let author = kp.address(); + + let mut n = 0; + for _ in 0..block_number { + last_hashes.push(last_header.hash()); + + // forge block. + let mut b = OpenBlock::new( + test_engine, + Default::default(), + false, + db, + &last_header, + Arc::new(last_hashes.clone()), + author.clone(), + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + rolling_timestamp += 10; + b.set_timestamp(rolling_timestamp); + + // first block we don't have any balance, so can't send any transactions. + for _ in 0..txs_per_block { + b.push_transaction( + Transaction { + nonce: n.into(), + gas_price: tx_gas_prices[n % tx_gas_prices.len()], + gas: 100000.into(), + action: Action::Create, + data: vec![], + value: U256::zero(), + } + .sign(kp.secret(), Some(test_spec.chain_id())), + None, + ) + .unwrap(); + n += 1; + } + + let b = b + .close_and_lock() + .unwrap() + .seal(test_engine, vec![]) + .unwrap(); + + if let Err(e) = client.import_block(Unverified::from_rlp(b.rlp_bytes()).unwrap()) { + panic!( + "error importing block which is valid by definition: {:?}", + e + ); + } + + last_header = view!(BlockView, &b.rlp_bytes()).header(); + db = b.drain().state.drop().1; + } + client.flush_queue(); + client.import_verified_blocks(); + client } /// Adds blocks to the client -pub fn push_blocks_to_client(client: &Arc, timestamp_salt: u64, starting_number: usize, block_number: usize) { - let test_spec = Spec::new_test(); - let state_root = test_spec.genesis_header().state_root().clone(); - let genesis_gas = test_spec.genesis_header().gas_limit().clone(); - - let mut rolling_hash = client.chain_info().best_block_hash; - let mut rolling_block_number = starting_number as u64; - let mut rolling_timestamp = timestamp_salt + starting_number as u64 * 10; - - for _ in 0..block_number { - let mut header = Header::new(); - - header.set_gas_limit(genesis_gas); - header.set_difficulty(U256::from(0x20000)); - header.set_timestamp(rolling_timestamp); - header.set_number(rolling_block_number); - header.set_parent_hash(rolling_hash); - header.set_state_root(state_root); - - rolling_hash = header.hash(); - rolling_block_number = rolling_block_number + 1; - rolling_timestamp = rolling_timestamp + 10; - - if let Err(e) = client.import_block(Unverified::from_rlp(create_test_block(&header)).unwrap()) { - panic!("error importing block which is valid by definition: {:?}", e); - } - } +pub fn push_blocks_to_client( + client: &Arc, + timestamp_salt: u64, + starting_number: usize, + block_number: usize, +) { + let test_spec = Spec::new_test(); + let state_root = test_spec.genesis_header().state_root().clone(); + let genesis_gas = test_spec.genesis_header().gas_limit().clone(); + + let mut rolling_hash = client.chain_info().best_block_hash; + let mut rolling_block_number = starting_number as u64; + let mut rolling_timestamp = timestamp_salt + starting_number as u64 * 10; + + for _ in 0..block_number { + let mut header = Header::new(); + + header.set_gas_limit(genesis_gas); + header.set_difficulty(U256::from(0x20000)); + header.set_timestamp(rolling_timestamp); + header.set_number(rolling_block_number); + header.set_parent_hash(rolling_hash); + header.set_state_root(state_root); + + rolling_hash = header.hash(); + rolling_block_number = rolling_block_number + 1; + rolling_timestamp = rolling_timestamp + 10; + + if let Err(e) = + client.import_block(Unverified::from_rlp(create_test_block(&header)).unwrap()) + { + panic!( + "error importing block which is valid by definition: {:?}", + e + ); + } + } } /// Adds one block with transactions pub fn push_block_with_transactions(client: &Arc, transactions: &[SignedTransaction]) { - let test_spec = Spec::new_test(); - let test_engine = &*test_spec.engine; - let block_number = client.chain_info().best_block_number as u64 + 1; - - let mut b = client.prepare_open_block(Address::default(), (0.into(), 5000000.into()), Bytes::new()).unwrap(); - b.set_timestamp(block_number * 10); - - for t in transactions { - b.push_transaction(t.clone(), None).unwrap(); - } - let b = b.close_and_lock().unwrap().seal(test_engine, vec![]).unwrap(); - - if let Err(e) = client.import_block(Unverified::from_rlp(b.rlp_bytes()).unwrap()) { - panic!("error importing block which is valid by definition: {:?}", e); - } - - client.flush_queue(); - client.import_verified_blocks(); + let test_spec = Spec::new_test(); + let test_engine = &*test_spec.engine; + let block_number = client.chain_info().best_block_number as u64 + 1; + + let mut b = client + .prepare_open_block(Address::default(), (0.into(), 5000000.into()), Bytes::new()) + .unwrap(); + b.set_timestamp(block_number * 10); + + for t in transactions { + b.push_transaction(t.clone(), None).unwrap(); + } + let b = b + .close_and_lock() + .unwrap() + .seal(test_engine, vec![]) + .unwrap(); + + if let Err(e) = client.import_block(Unverified::from_rlp(b.rlp_bytes()).unwrap()) { + panic!( + "error importing block which is valid by definition: {:?}", + e + ); + } + + client.flush_queue(); + client.import_verified_blocks(); } /// Creates dummy client (not test client) with corresponding blocks pub fn get_test_client_with_blocks(blocks: Vec) -> Arc { - let test_spec = Spec::new_test(); - let client_db = new_db(); - - let client = Client::new( - ClientConfig::default(), - &test_spec, - client_db, - Arc::new(Miner::new_for_tests(&test_spec, None)), - IoChannel::disconnected(), - ).unwrap(); - - for block in blocks { - if let Err(e) = client.import_block(Unverified::from_rlp(block).unwrap()) { - panic!("error importing block which is well-formed: {:?}", e); - } - } - client.flush_queue(); - client.import_verified_blocks(); - client + let test_spec = Spec::new_test(); + let client_db = new_db(); + + let client = Client::new( + ClientConfig::default(), + &test_spec, + client_db, + Arc::new(Miner::new_for_tests(&test_spec, None)), + IoChannel::disconnected(), + ) + .unwrap(); + + for block in blocks { + if let Err(e) = client.import_block(Unverified::from_rlp(block).unwrap()) { + panic!("error importing block which is well-formed: {:?}", e); + } + } + client.flush_queue(); + client.import_verified_blocks(); + client } struct TestBlockChainDB { - _blooms_dir: TempDir, - _trace_blooms_dir: TempDir, - blooms: blooms_db::Database, - trace_blooms: blooms_db::Database, - key_value: Arc, + _blooms_dir: TempDir, + _trace_blooms_dir: TempDir, + blooms: blooms_db::Database, + trace_blooms: blooms_db::Database, + key_value: Arc, } impl BlockChainDB for TestBlockChainDB { - fn key_value(&self) -> &Arc { - &self.key_value - } + fn key_value(&self) -> &Arc { + &self.key_value + } - fn blooms(&self) -> &blooms_db::Database { - &self.blooms - } + fn blooms(&self) -> &blooms_db::Database { + &self.blooms + } - fn trace_blooms(&self) -> &blooms_db::Database { - &self.trace_blooms - } + fn trace_blooms(&self) -> &blooms_db::Database { + &self.trace_blooms + } } /// Creates new test instance of `BlockChainDB` -pub fn new_db() -> Arc { - let blooms_dir = TempDir::new("").unwrap(); - let trace_blooms_dir = TempDir::new("").unwrap(); - - let db = TestBlockChainDB { - blooms: blooms_db::Database::open(blooms_dir.path()).unwrap(), - trace_blooms: blooms_db::Database::open(trace_blooms_dir.path()).unwrap(), - _blooms_dir: blooms_dir, - _trace_blooms_dir: trace_blooms_dir, - key_value: Arc::new(::kvdb_memorydb::create(::db::NUM_COLUMNS.unwrap())) - }; - - Arc::new(db) +pub fn new_db() -> Arc { + let blooms_dir = TempDir::new("").unwrap(); + let trace_blooms_dir = TempDir::new("").unwrap(); + + let db = TestBlockChainDB { + blooms: blooms_db::Database::open(blooms_dir.path()).unwrap(), + trace_blooms: blooms_db::Database::open(trace_blooms_dir.path()).unwrap(), + _blooms_dir: blooms_dir, + _trace_blooms_dir: trace_blooms_dir, + key_value: Arc::new(::kvdb_memorydb::create(::db::NUM_COLUMNS.unwrap())), + }; + + Arc::new(db) } /// Creates a new temporary `BlockChainDB` on FS -pub fn new_temp_db(tempdir: &Path) -> Arc { - let blooms_dir = TempDir::new("").unwrap(); - let trace_blooms_dir = TempDir::new("").unwrap(); - let key_value_dir = tempdir.join("key_value"); - - let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS); - let key_value_db = Database::open(&db_config, key_value_dir.to_str().unwrap()).unwrap(); - - let db = TestBlockChainDB { - blooms: blooms_db::Database::open(blooms_dir.path()).unwrap(), - trace_blooms: blooms_db::Database::open(trace_blooms_dir.path()).unwrap(), - _blooms_dir: blooms_dir, - _trace_blooms_dir: trace_blooms_dir, - key_value: Arc::new(key_value_db) - }; - - Arc::new(db) +pub fn new_temp_db(tempdir: &Path) -> Arc { + let blooms_dir = TempDir::new("").unwrap(); + let trace_blooms_dir = TempDir::new("").unwrap(); + let key_value_dir = tempdir.join("key_value"); + + let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS); + let key_value_db = Database::open(&db_config, key_value_dir.to_str().unwrap()).unwrap(); + + let db = TestBlockChainDB { + blooms: blooms_db::Database::open(blooms_dir.path()).unwrap(), + trace_blooms: blooms_db::Database::open(trace_blooms_dir.path()).unwrap(), + _blooms_dir: blooms_dir, + _trace_blooms_dir: trace_blooms_dir, + key_value: Arc::new(key_value_db), + }; + + Arc::new(db) } /// Creates new instance of KeyValueDBHandler -pub fn restoration_db_handler(config: kvdb_rocksdb::DatabaseConfig) -> Box { - struct RestorationDBHandler { - config: kvdb_rocksdb::DatabaseConfig, - } - - struct RestorationDB { - blooms: blooms_db::Database, - trace_blooms: blooms_db::Database, - key_value: Arc, - } - - impl BlockChainDB for RestorationDB { - fn key_value(&self) -> &Arc { - &self.key_value - } - - fn blooms(&self) -> &blooms_db::Database { - &self.blooms - } - - fn trace_blooms(&self) -> &blooms_db::Database { - &self.trace_blooms - } - } - - impl BlockChainDBHandler for RestorationDBHandler { - fn open(&self, db_path: &Path) -> io::Result> { - let key_value = Arc::new(kvdb_rocksdb::Database::open(&self.config, &db_path.to_string_lossy())?); - let blooms_path = db_path.join("blooms"); - let trace_blooms_path = db_path.join("trace_blooms"); - fs::create_dir_all(&blooms_path)?; - fs::create_dir_all(&trace_blooms_path)?; - let blooms = blooms_db::Database::open(blooms_path).unwrap(); - let trace_blooms = blooms_db::Database::open(trace_blooms_path).unwrap(); - let db = RestorationDB { - blooms, - trace_blooms, - key_value, - }; - Ok(Arc::new(db)) - } - } - - Box::new(RestorationDBHandler { config }) +pub fn restoration_db_handler( + config: kvdb_rocksdb::DatabaseConfig, +) -> Box { + struct RestorationDBHandler { + config: kvdb_rocksdb::DatabaseConfig, + } + + struct RestorationDB { + blooms: blooms_db::Database, + trace_blooms: blooms_db::Database, + key_value: Arc, + } + + impl BlockChainDB for RestorationDB { + fn key_value(&self) -> &Arc { + &self.key_value + } + + fn blooms(&self) -> &blooms_db::Database { + &self.blooms + } + + fn trace_blooms(&self) -> &blooms_db::Database { + &self.trace_blooms + } + } + + impl BlockChainDBHandler for RestorationDBHandler { + fn open(&self, db_path: &Path) -> io::Result> { + let key_value = Arc::new(kvdb_rocksdb::Database::open( + &self.config, + &db_path.to_string_lossy(), + )?); + let blooms_path = db_path.join("blooms"); + let trace_blooms_path = db_path.join("trace_blooms"); + fs::create_dir_all(&blooms_path)?; + fs::create_dir_all(&trace_blooms_path)?; + let blooms = blooms_db::Database::open(blooms_path).unwrap(); + let trace_blooms = blooms_db::Database::open(trace_blooms_path).unwrap(); + let db = RestorationDB { + blooms, + trace_blooms, + key_value, + }; + Ok(Arc::new(db)) + } + } + + Box::new(RestorationDBHandler { config }) } /// Generates dummy blockchain with corresponding amount of blocks pub fn generate_dummy_blockchain(block_number: u32) -> BlockChain { - let db = new_db(); - let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone()); - - let mut batch = db.key_value().transaction(); - for block_order in 1..block_number { - // Total difficulty is always 0 here. - bc.insert_block(&mut batch, encoded::Block::new(create_unverifiable_block(block_order, bc.best_block_hash())), vec![], ExtrasInsert { - fork_choice: ::engines::ForkChoice::New, - is_finalized: false, - }); - bc.commit(); - } - db.key_value().write(batch).unwrap(); - bc + let db = new_db(); + let bc = BlockChain::new( + BlockChainConfig::default(), + &create_unverifiable_block(0, H256::zero()), + db.clone(), + ); + + let mut batch = db.key_value().transaction(); + for block_order in 1..block_number { + // Total difficulty is always 0 here. + bc.insert_block( + &mut batch, + encoded::Block::new(create_unverifiable_block(block_order, bc.best_block_hash())), + vec![], + ExtrasInsert { + fork_choice: ::engines::ForkChoice::New, + is_finalized: false, + }, + ); + bc.commit(); + } + db.key_value().write(batch).unwrap(); + bc } /// Generates dummy blockchain with corresponding amount of blocks (using creation with extra method for blocks creation) pub fn generate_dummy_blockchain_with_extra(block_number: u32) -> BlockChain { - let db = new_db(); - let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone()); - - let mut batch = db.key_value().transaction(); - for block_order in 1..block_number { - // Total difficulty is always 0 here. - bc.insert_block(&mut batch, encoded::Block::new(create_unverifiable_block_with_extra(block_order, bc.best_block_hash(), None)), vec![], ExtrasInsert { - fork_choice: ::engines::ForkChoice::New, - is_finalized: false, - }); - bc.commit(); - } - db.key_value().write(batch).unwrap(); - bc + let db = new_db(); + let bc = BlockChain::new( + BlockChainConfig::default(), + &create_unverifiable_block(0, H256::zero()), + db.clone(), + ); + + let mut batch = db.key_value().transaction(); + for block_order in 1..block_number { + // Total difficulty is always 0 here. + bc.insert_block( + &mut batch, + encoded::Block::new(create_unverifiable_block_with_extra( + block_order, + bc.best_block_hash(), + None, + )), + vec![], + ExtrasInsert { + fork_choice: ::engines::ForkChoice::New, + is_finalized: false, + }, + ); + bc.commit(); + } + db.key_value().write(batch).unwrap(); + bc } /// Returns empty dummy blockchain pub fn generate_dummy_empty_blockchain() -> BlockChain { - let db = new_db(); - let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone()); - bc + let db = new_db(); + let bc = BlockChain::new( + BlockChainConfig::default(), + &create_unverifiable_block(0, H256::zero()), + db.clone(), + ); + bc } /// Returns temp state pub fn get_temp_state() -> State<::state_db::StateDB> { - let journal_db = get_temp_state_db(); - State::new(journal_db, U256::from(0), Default::default()) + let journal_db = get_temp_state_db(); + State::new(journal_db, U256::from(0), Default::default()) } /// Returns temp state using coresponding factory pub fn get_temp_state_with_factory(factory: EvmFactory) -> State<::state_db::StateDB> { - let journal_db = get_temp_state_db(); - let mut factories = Factories::default(); - factories.vm = factory.into(); - State::new(journal_db, U256::from(0), factories) + let journal_db = get_temp_state_db(); + let mut factories = Factories::default(); + factories.vm = factory.into(); + State::new(journal_db, U256::from(0), factories) } /// Returns temp state db pub fn get_temp_state_db() -> StateDB { - let db = new_db(); - let journal_db = ::journaldb::new(db.key_value().clone(), ::journaldb::Algorithm::EarlyMerge, ::db::COL_STATE); - StateDB::new(journal_db, 5 * 1024 * 1024) + let db = new_db(); + let journal_db = ::journaldb::new( + db.key_value().clone(), + ::journaldb::Algorithm::EarlyMerge, + ::db::COL_STATE, + ); + StateDB::new(journal_db, 5 * 1024 * 1024) } /// Returns sequence of hashes of the dummy blocks pub fn get_good_dummy_block_seq(count: usize) -> Vec { - let test_spec = Spec::new_test(); - get_good_dummy_block_fork_seq(1, count, &test_spec.genesis_header().hash()) + let test_spec = Spec::new_test(); + get_good_dummy_block_fork_seq(1, count, &test_spec.genesis_header().hash()) } /// Returns sequence of hashes of the dummy blocks beginning from corresponding parent -pub fn get_good_dummy_block_fork_seq(start_number: usize, count: usize, parent_hash: &H256) -> Vec { - let test_spec = Spec::new_test(); - let genesis_gas = test_spec.genesis_header().gas_limit().clone(); - let mut rolling_timestamp = start_number as u64 * 10; - let mut parent = *parent_hash; - let mut r = Vec::new(); - for i in start_number .. start_number + count + 1 { - let mut block_header = Header::new(); - block_header.set_gas_limit(genesis_gas); - block_header.set_difficulty(U256::from(i) * U256([0, 1, 0, 0])); - block_header.set_timestamp(rolling_timestamp); - block_header.set_number(i as u64); - block_header.set_parent_hash(parent); - block_header.set_state_root(test_spec.genesis_header().state_root().clone()); - - parent = block_header.hash(); - rolling_timestamp = rolling_timestamp + 10; - - r.push(create_test_block(&block_header)); - } - r +pub fn get_good_dummy_block_fork_seq( + start_number: usize, + count: usize, + parent_hash: &H256, +) -> Vec { + let test_spec = Spec::new_test(); + let genesis_gas = test_spec.genesis_header().gas_limit().clone(); + let mut rolling_timestamp = start_number as u64 * 10; + let mut parent = *parent_hash; + let mut r = Vec::new(); + for i in start_number..start_number + count + 1 { + let mut block_header = Header::new(); + block_header.set_gas_limit(genesis_gas); + block_header.set_difficulty(U256::from(i) * U256([0, 1, 0, 0])); + block_header.set_timestamp(rolling_timestamp); + block_header.set_number(i as u64); + block_header.set_parent_hash(parent); + block_header.set_state_root(test_spec.genesis_header().state_root().clone()); + + parent = block_header.hash(); + rolling_timestamp = rolling_timestamp + 10; + + r.push(create_test_block(&block_header)); + } + r } /// Returns hash and header of the correct dummy block pub fn get_good_dummy_block_hash() -> (H256, Bytes) { - let mut block_header = Header::new(); - let test_spec = Spec::new_test(); - let genesis_gas = test_spec.genesis_header().gas_limit().clone(); - block_header.set_gas_limit(genesis_gas); - block_header.set_difficulty(U256::from(0x20000)); - block_header.set_timestamp(40); - block_header.set_number(1); - block_header.set_parent_hash(test_spec.genesis_header().hash()); - block_header.set_state_root(test_spec.genesis_header().state_root().clone()); - - (block_header.hash(), create_test_block(&block_header)) + let mut block_header = Header::new(); + let test_spec = Spec::new_test(); + let genesis_gas = test_spec.genesis_header().gas_limit().clone(); + block_header.set_gas_limit(genesis_gas); + block_header.set_difficulty(U256::from(0x20000)); + block_header.set_timestamp(40); + block_header.set_number(1); + block_header.set_parent_hash(test_spec.genesis_header().hash()); + block_header.set_state_root(test_spec.genesis_header().state_root().clone()); + + (block_header.hash(), create_test_block(&block_header)) } /// Returns hash of the correct dummy block pub fn get_good_dummy_block() -> Bytes { - let (_, bytes) = get_good_dummy_block_hash(); - bytes + let (_, bytes) = get_good_dummy_block_hash(); + bytes } /// Returns hash of the dummy block with incorrect state root pub fn get_bad_state_dummy_block() -> Bytes { - let mut block_header = Header::new(); - let test_spec = Spec::new_test(); - let genesis_gas = test_spec.genesis_header().gas_limit().clone(); - - block_header.set_gas_limit(genesis_gas); - block_header.set_difficulty(U256::from(0x20000)); - block_header.set_timestamp(40); - block_header.set_number(1); - block_header.set_parent_hash(test_spec.genesis_header().hash()); - block_header.set_state_root(0xbad.into()); - - create_test_block(&block_header) + let mut block_header = Header::new(); + let test_spec = Spec::new_test(); + let genesis_gas = test_spec.genesis_header().gas_limit().clone(); + + block_header.set_gas_limit(genesis_gas); + block_header.set_difficulty(U256::from(0x20000)); + block_header.set_timestamp(40); + block_header.set_number(1); + block_header.set_parent_hash(test_spec.genesis_header().hash()); + block_header.set_state_root(0xbad.into()); + + create_test_block(&block_header) } /// Test actor for chain events #[derive(Default)] pub struct TestNotify { - /// Messages store - pub messages: RwLock>, + /// Messages store + pub messages: RwLock>, } impl ChainNotify for TestNotify { - fn broadcast(&self, message: ChainMessageType) { - let data = match message { - ChainMessageType::Consensus(data) => data, - ChainMessageType::SignedPrivateTransaction(_, data) => data, - ChainMessageType::PrivateTransaction(_, data) => data, - }; - self.messages.write().push(data); - } + fn broadcast(&self, message: ChainMessageType) { + let data = match message { + ChainMessageType::Consensus(data) => data, + }; + self.messages.write().push(data); + } } diff --git a/ethcore/src/tests/blockchain.rs b/ethcore/src/tests/blockchain.rs index 9f2cdbdb9cb..86a1475b157 100644 --- a/ethcore/src/tests/blockchain.rs +++ b/ethcore/src/tests/blockchain.rs @@ -1,60 +1,59 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use blockchain::BlockProvider; use test_helpers::{ - generate_dummy_blockchain, - generate_dummy_blockchain_with_extra, - generate_dummy_empty_blockchain, + generate_dummy_blockchain, generate_dummy_blockchain_with_extra, + generate_dummy_empty_blockchain, }; #[test] fn can_contain_arbitrary_block_sequence() { - let bc = generate_dummy_blockchain(50); - assert_eq!(bc.best_block_number(), 49); + let bc = generate_dummy_blockchain(50); + assert_eq!(bc.best_block_number(), 49); } #[test] fn can_collect_garbage() { - let bc = generate_dummy_blockchain(3000); + let bc = generate_dummy_blockchain(3000); - assert_eq!(bc.best_block_number(), 2999); - let best_hash = bc.best_block_hash(); - let mut block_header = bc.block_header_data(&best_hash); + assert_eq!(bc.best_block_number(), 2999); + let best_hash = bc.best_block_hash(); + let mut block_header = bc.block_header_data(&best_hash); - while !block_header.is_none() { - block_header = bc.block_header_data(&block_header.unwrap().parent_hash()); - } - assert!(bc.cache_size().blocks > 1024 * 1024); + while !block_header.is_none() { + block_header = bc.block_header_data(&block_header.unwrap().parent_hash()); + } + assert!(bc.cache_size().blocks > 1024 * 1024); - for _ in 0..2 { - bc.collect_garbage(); - } - assert!(bc.cache_size().blocks < 1024 * 1024); + for _ in 0..2 { + bc.collect_garbage(); + } + assert!(bc.cache_size().blocks < 1024 * 1024); } #[test] fn can_contain_arbitrary_block_sequence_with_extra() { - let bc = generate_dummy_blockchain_with_extra(25); - assert_eq!(bc.best_block_number(), 24); + let bc = generate_dummy_blockchain_with_extra(25); + assert_eq!(bc.best_block_number(), 24); } #[test] fn can_contain_only_genesis_block() { - let bc = generate_dummy_empty_blockchain(); - assert_eq!(bc.best_block_number(), 0); + let bc = generate_dummy_empty_blockchain(); + assert_eq!(bc.best_block_number(), 0); } diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 4d12bb1385e..954d2a044dc 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -1,368 +1,584 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::str::FromStr; -use std::sync::Arc; +use std::{ + str::{from_utf8, FromStr}, + sync::Arc, +}; -use ethereum_types::{U256, Address}; +use client::{ + traits::{ + BlockChainClient, BlockChainReset, BlockInfo, ChainInfo, ImportBlock, ImportExportBlocks, + }, + Client, ClientConfig, ImportSealedBlock, PrepareOpenBlock, +}; +use ethereum; +use ethereum_types::{Address, U256}; use ethkey::KeyPair; +use executive::{Executive, TransactOptions}; use hash::keccak; use io::IoChannel; -use tempdir::TempDir; -use types::transaction::{PendingTransaction, Transaction, Action, Condition}; -use types::filter::Filter; -use types::view; -use types::views::BlockView; - -use client::{BlockChainClient, Client, ClientConfig, BlockId, ChainInfo, BlockInfo, PrepareOpenBlock, ImportSealedBlock, ImportBlock}; -use ethereum; -use executive::{Executive, TransactOptions}; -use miner::{Miner, PendingOrdering, MinerService}; +use miner::{Miner, MinerService, PendingOrdering}; +use rustc_hex::ToHex; use spec::Spec; -use state::{self, State, CleanupMode}; +use state::{self, CleanupMode, State, StateInfo}; +use tempdir::TempDir; use test_helpers::{ - self, - generate_dummy_client, push_blocks_to_client, get_test_client_with_blocks, get_good_dummy_block_seq, - generate_dummy_client_with_data, get_good_dummy_block, get_bad_state_dummy_block + self, generate_dummy_client, generate_dummy_client_with_data, get_bad_state_dummy_block, + get_good_dummy_block, get_good_dummy_block_seq, get_test_client_with_blocks, + push_blocks_to_client, +}; +use types::{ + data_format::DataFormat, + filter::Filter, + ids::BlockId, + transaction::{Action, Condition, PendingTransaction, Transaction}, + view, + views::BlockView, }; use verification::queue::kind::blocks::Unverified; #[test] fn imports_from_empty() { - let db = test_helpers::new_db(); - let spec = Spec::new_test(); - - let client = Client::new( - ClientConfig::default(), - &spec, - db, - Arc::new(Miner::new_for_tests(&spec, None)), - IoChannel::disconnected(), - ).unwrap(); - client.import_verified_blocks(); - client.flush_queue(); + let db = test_helpers::new_db(); + let spec = Spec::new_test(); + + let client = Client::new( + ClientConfig::default(), + &spec, + db, + Arc::new(Miner::new_for_tests(&spec, None)), + IoChannel::disconnected(), + ) + .unwrap(); + client.import_verified_blocks(); + client.flush_queue(); } #[test] fn should_return_registrar() { - let db = test_helpers::new_db(); - let tempdir = TempDir::new("").unwrap(); - let spec = ethereum::new_morden(&tempdir.path().to_owned()); - - let client = Client::new( - ClientConfig::default(), - &spec, - db, - Arc::new(Miner::new_for_tests(&spec, None)), - IoChannel::disconnected(), - ).unwrap(); - let params = client.additional_params(); - let address = ¶ms["registrar"]; - - assert_eq!(address.len(), 40); - assert!(U256::from_str(address).is_ok()); -} - -#[test] -fn returns_state_root_basic() { - let client = generate_dummy_client(6); - let test_spec = Spec::new_test(); - let genesis_header = test_spec.genesis_header(); - - assert!(client.state_data(genesis_header.state_root()).is_some()); + let db = test_helpers::new_db(); + let tempdir = TempDir::new("").unwrap(); + let spec = ethereum::new_morden(&tempdir.path().to_owned()); + + let client = Client::new( + ClientConfig::default(), + &spec, + db, + Arc::new(Miner::new_for_tests(&spec, None)), + IoChannel::disconnected(), + ) + .unwrap(); + let params = client.additional_params(); + let address = ¶ms["registrar"]; + + assert_eq!(address.len(), 40); + assert!(U256::from_str(address).is_ok()); } #[test] fn imports_good_block() { - let db = test_helpers::new_db(); - let spec = Spec::new_test(); - - let client = Client::new( - ClientConfig::default(), - &spec, - db, - Arc::new(Miner::new_for_tests(&spec, None)), - IoChannel::disconnected(), - ).unwrap(); - let good_block = get_good_dummy_block(); - if client.import_block(Unverified::from_rlp(good_block).unwrap()).is_err() { - panic!("error importing block being good by definition"); - } - client.flush_queue(); - client.import_verified_blocks(); - - let block = client.block_header(BlockId::Number(1)).unwrap(); - assert!(!block.into_inner().is_empty()); + let db = test_helpers::new_db(); + let spec = Spec::new_test(); + + let client = Client::new( + ClientConfig::default(), + &spec, + db, + Arc::new(Miner::new_for_tests(&spec, None)), + IoChannel::disconnected(), + ) + .unwrap(); + let good_block = get_good_dummy_block(); + if client + .import_block(Unverified::from_rlp(good_block).unwrap()) + .is_err() + { + panic!("error importing block being good by definition"); + } + client.flush_queue(); + client.import_verified_blocks(); + + let block = client.block_header(BlockId::Number(1)).unwrap(); + assert!(!block.into_inner().is_empty()); } #[test] fn query_none_block() { - let db = test_helpers::new_db(); - let spec = Spec::new_test(); - - let client = Client::new( - ClientConfig::default(), - &spec, - db, - Arc::new(Miner::new_for_tests(&spec, None)), - IoChannel::disconnected(), - ).unwrap(); + let db = test_helpers::new_db(); + let spec = Spec::new_test(); + + let client = Client::new( + ClientConfig::default(), + &spec, + db, + Arc::new(Miner::new_for_tests(&spec, None)), + IoChannel::disconnected(), + ) + .unwrap(); let non_existant = client.block_header(BlockId::Number(188)); - assert!(non_existant.is_none()); + assert!(non_existant.is_none()); } #[test] fn query_bad_block() { - let client = get_test_client_with_blocks(vec![get_bad_state_dummy_block()]); - let bad_block: Option<_> = client.block_header(BlockId::Number(1)); + let client = get_test_client_with_blocks(vec![get_bad_state_dummy_block()]); + let bad_block: Option<_> = client.block_header(BlockId::Number(1)); - assert!(bad_block.is_none()); + assert!(bad_block.is_none()); } #[test] fn returns_chain_info() { - let dummy_block = get_good_dummy_block(); - let client = get_test_client_with_blocks(vec![dummy_block.clone()]); - let block = view!(BlockView, &dummy_block); - let info = client.chain_info(); - assert_eq!(info.best_block_hash, block.header().hash()); + let dummy_block = get_good_dummy_block(); + let client = get_test_client_with_blocks(vec![dummy_block.clone()]); + let block = view!(BlockView, &dummy_block); + let info = client.chain_info(); + assert_eq!(info.best_block_hash, block.header().hash()); } #[test] fn returns_logs() { - let dummy_block = get_good_dummy_block(); - let client = get_test_client_with_blocks(vec![dummy_block.clone()]); - let logs = client.logs(Filter { - from_block: BlockId::Earliest, - to_block: BlockId::Latest, - address: None, - topics: vec![], - limit: None, - }).unwrap(); - assert_eq!(logs.len(), 0); + let dummy_block = get_good_dummy_block(); + let client = get_test_client_with_blocks(vec![dummy_block.clone()]); + let logs = client + .logs(Filter { + from_block: BlockId::Earliest, + to_block: BlockId::Latest, + address: None, + topics: vec![], + limit: None, + }) + .unwrap(); + assert_eq!(logs.len(), 0); } #[test] fn returns_logs_with_limit() { - let dummy_block = get_good_dummy_block(); - let client = get_test_client_with_blocks(vec![dummy_block.clone()]); - let logs = client.logs(Filter { - from_block: BlockId::Earliest, - to_block: BlockId::Latest, - address: None, - topics: vec![], - limit: None, - }).unwrap(); - assert_eq!(logs.len(), 0); + let dummy_block = get_good_dummy_block(); + let client = get_test_client_with_blocks(vec![dummy_block.clone()]); + let logs = client + .logs(Filter { + from_block: BlockId::Earliest, + to_block: BlockId::Latest, + address: None, + topics: vec![], + limit: None, + }) + .unwrap(); + assert_eq!(logs.len(), 0); } #[test] fn returns_block_body() { - let dummy_block = get_good_dummy_block(); - let client = get_test_client_with_blocks(vec![dummy_block.clone()]); - let block = view!(BlockView, &dummy_block); - let body = client.block_body(BlockId::Hash(block.header().hash())).unwrap(); - let body = body.rlp(); - assert_eq!(body.item_count().unwrap(), 2); - assert_eq!(body.at(0).unwrap().as_raw()[..], block.rlp().at(1).as_raw()[..]); - assert_eq!(body.at(1).unwrap().as_raw()[..], block.rlp().at(2).as_raw()[..]); + let dummy_block = get_good_dummy_block(); + let client = get_test_client_with_blocks(vec![dummy_block.clone()]); + let block = view!(BlockView, &dummy_block); + let body = client + .block_body(BlockId::Hash(block.header().hash())) + .unwrap(); + let body = body.rlp(); + assert_eq!(body.item_count().unwrap(), 2); + assert_eq!( + body.at(0).unwrap().as_raw()[..], + block.rlp().at(1).as_raw()[..] + ); + assert_eq!( + body.at(1).unwrap().as_raw()[..], + block.rlp().at(2).as_raw()[..] + ); } #[test] fn imports_block_sequence() { - let client = generate_dummy_client(6); - let block = client.block_header(BlockId::Number(5)).unwrap(); + let client = generate_dummy_client(6); + let block = client.block_header(BlockId::Number(5)).unwrap(); - assert!(!block.into_inner().is_empty()); + assert!(!block.into_inner().is_empty()); } #[test] fn can_collect_garbage() { - let client = generate_dummy_client(100); - client.tick(true); - assert!(client.blockchain_cache_info().blocks < 100 * 1024); + let client = generate_dummy_client(100); + client.tick(true); + assert!(client.blockchain_cache_info().blocks < 100 * 1024); } #[test] fn can_generate_gas_price_median() { - let client = generate_dummy_client_with_data(3, 1, slice_into![1, 2, 3]); - assert_eq!(Some(&U256::from(2)), client.gas_price_corpus(3).median()); + let client = generate_dummy_client_with_data(3, 1, slice_into![1, 2, 3]); + assert_eq!(Some(&U256::from(2)), client.gas_price_corpus(3).median()); - let client = generate_dummy_client_with_data(4, 1, slice_into![1, 4, 3, 2]); - assert_eq!(Some(&U256::from(3)), client.gas_price_corpus(3).median()); + let client = generate_dummy_client_with_data(4, 1, slice_into![1, 4, 3, 2]); + assert_eq!(Some(&U256::from(3)), client.gas_price_corpus(3).median()); } #[test] fn can_generate_gas_price_histogram() { - let client = generate_dummy_client_with_data(20, 1, slice_into![6354,8593,6065,4842,7845,7002,689,4958,4250,6098,5804,4320,643,8895,2296,8589,7145,2000,2512,1408]); - - let hist = client.gas_price_corpus(20).histogram(5).unwrap(); - let correct_hist = ::stats::Histogram { bucket_bounds: vec_into![643, 2294, 3945, 5596, 7247, 8898], counts: vec![4,2,4,6,4] }; - assert_eq!(hist, correct_hist); + let client = generate_dummy_client_with_data( + 20, + 1, + slice_into![ + 6354, 8593, 6065, 4842, 7845, 7002, 689, 4958, 4250, 6098, 5804, 4320, 643, 8895, 2296, + 8589, 7145, 2000, 2512, 1408 + ], + ); + + let hist = client.gas_price_corpus(20).histogram(5).unwrap(); + let correct_hist = ::stats::Histogram { + bucket_bounds: vec_into![643, 2294, 3945, 5596, 7247, 8898], + counts: vec![4, 2, 4, 6, 4], + }; + assert_eq!(hist, correct_hist); } #[test] fn empty_gas_price_histogram() { - let client = generate_dummy_client_with_data(20, 0, slice_into![]); + let client = generate_dummy_client_with_data(20, 0, slice_into![]); - assert!(client.gas_price_corpus(20).histogram(5).is_none()); + assert!(client.gas_price_corpus(20).histogram(5).is_none()); } #[test] fn corpus_is_sorted() { - let client = generate_dummy_client_with_data(2, 1, slice_into![U256::from_str("11426908979").unwrap(), U256::from_str("50426908979").unwrap()]); - let corpus = client.gas_price_corpus(20); - assert!(corpus[0] < corpus[1]); + let client = generate_dummy_client_with_data( + 2, + 1, + slice_into![ + U256::from_str("11426908979").unwrap(), + U256::from_str("50426908979").unwrap() + ], + ); + let corpus = client.gas_price_corpus(20); + assert!(corpus[0] < corpus[1]); } #[test] fn can_handle_long_fork() { - let client = generate_dummy_client(1200); - for _ in 0..20 { - client.import_verified_blocks(); - } - assert_eq!(1200, client.chain_info().best_block_number); - - push_blocks_to_client(&client, 45, 1201, 800); - push_blocks_to_client(&client, 49, 1201, 800); - push_blocks_to_client(&client, 53, 1201, 600); - - for _ in 0..400 { - client.import_verified_blocks(); - } - assert_eq!(2000, client.chain_info().best_block_number); + let client = generate_dummy_client(1200); + for _ in 0..20 { + client.import_verified_blocks(); + } + assert_eq!(1200, client.chain_info().best_block_number); + + push_blocks_to_client(&client, 45, 1201, 800); + push_blocks_to_client(&client, 49, 1201, 800); + push_blocks_to_client(&client, 53, 1201, 600); + + for _ in 0..400 { + client.import_verified_blocks(); + } + assert_eq!(2000, client.chain_info().best_block_number); } #[test] fn can_mine() { - let dummy_blocks = get_good_dummy_block_seq(2); - let client = get_test_client_with_blocks(vec![dummy_blocks[0].clone()]); - - let b = client.prepare_open_block(Address::default(), (3141562.into(), 31415620.into()), vec![]).unwrap().close().unwrap(); - - assert_eq!(*b.header.parent_hash(), view!(BlockView, &dummy_blocks[0]).header_view().hash()); + let dummy_blocks = get_good_dummy_block_seq(2); + let client = get_test_client_with_blocks(vec![dummy_blocks[0].clone()]); + + let b = client + .prepare_open_block( + Address::default(), + (3141562.into(), 31415620.into()), + vec![], + ) + .unwrap() + .close() + .unwrap(); + + assert_eq!( + *b.header.parent_hash(), + view!(BlockView, &dummy_blocks[0]).header_view().hash() + ); } #[test] fn change_history_size() { - let db = test_helpers::new_db(); - let test_spec = Spec::new_null(); - let mut config = ClientConfig::default(); - - config.history = 2; - let address = Address::random(); - { - let client = Client::new( - ClientConfig::default(), - &test_spec, - db.clone(), - Arc::new(Miner::new_for_tests(&test_spec, None)), - IoChannel::disconnected() - ).unwrap(); - - for _ in 0..20 { - let mut b = client.prepare_open_block(Address::default(), (3141562.into(), 31415620.into()), vec![]).unwrap(); - b.block_mut().state_mut().add_balance(&address, &5.into(), CleanupMode::NoEmpty).unwrap(); - b.block_mut().state_mut().commit().unwrap(); - let b = b.close_and_lock().unwrap().seal(&*test_spec.engine, vec![]).unwrap(); - client.import_sealed_block(b).unwrap(); // account change is in the journal overlay - } - } - let mut config = ClientConfig::default(); - config.history = 10; - let client = Client::new( - config, - &test_spec, - db, - Arc::new(Miner::new_for_tests(&test_spec, None)), - IoChannel::disconnected(), - ).unwrap(); - assert_eq!(client.state().balance(&address).unwrap(), 100.into()); + let db = test_helpers::new_db(); + let test_spec = Spec::new_null(); + let mut config = ClientConfig::default(); + + config.history = 2; + let address = Address::random(); + { + let client = Client::new( + ClientConfig::default(), + &test_spec, + db.clone(), + Arc::new(Miner::new_for_tests(&test_spec, None)), + IoChannel::disconnected(), + ) + .unwrap(); + + for _ in 0..20 { + let mut b = client + .prepare_open_block( + Address::default(), + (3141562.into(), 31415620.into()), + vec![], + ) + .unwrap(); + b.block_mut() + .state_mut() + .add_balance(&address, &5.into(), CleanupMode::NoEmpty) + .unwrap(); + b.block_mut().state_mut().commit().unwrap(); + let b = b + .close_and_lock() + .unwrap() + .seal(&*test_spec.engine, vec![]) + .unwrap(); + client.import_sealed_block(b).unwrap(); // account change is in the journal overlay + } + } + let mut config = ClientConfig::default(); + config.history = 10; + let client = Client::new( + config, + &test_spec, + db, + Arc::new(Miner::new_for_tests(&test_spec, None)), + IoChannel::disconnected(), + ) + .unwrap(); + assert_eq!(client.state().balance(&address).unwrap(), 100.into()); } #[test] fn does_not_propagate_delayed_transactions() { - let key = KeyPair::from_secret(keccak("test").into()).unwrap(); - let secret = key.secret(); - let tx0 = PendingTransaction::new(Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 21000.into(), - action: Action::Call(Address::default()), - value: 0.into(), - data: Vec::new(), - }.sign(secret, None), Some(Condition::Number(2))); - let tx1 = PendingTransaction::new(Transaction { - nonce: 1.into(), - gas_price: 0.into(), - gas: 21000.into(), - action: Action::Call(Address::default()), - value: 0.into(), - data: Vec::new(), - }.sign(secret, None), None); - let client = generate_dummy_client(1); - - client.miner().import_own_transaction(&*client, tx0).unwrap(); - client.miner().import_own_transaction(&*client, tx1).unwrap(); - assert_eq!(0, client.transactions_to_propagate().len()); - assert_eq!(0, client.miner().ready_transactions(&*client, 10, PendingOrdering::Priority).len()); - push_blocks_to_client(&client, 53, 2, 2); - client.flush_queue(); - assert_eq!(2, client.transactions_to_propagate().len()); - assert_eq!(2, client.miner().ready_transactions(&*client, 10, PendingOrdering::Priority).len()); + let key = KeyPair::from_secret(keccak("test").into()).unwrap(); + let secret = key.secret(); + let tx0 = PendingTransaction::new( + Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 21000.into(), + action: Action::Call(Address::default()), + value: 0.into(), + data: Vec::new(), + } + .sign(secret, None), + Some(Condition::Number(2)), + ); + let tx1 = PendingTransaction::new( + Transaction { + nonce: 1.into(), + gas_price: 0.into(), + gas: 21000.into(), + action: Action::Call(Address::default()), + value: 0.into(), + data: Vec::new(), + } + .sign(secret, None), + None, + ); + let client = generate_dummy_client(1); + + client + .miner() + .import_own_transaction(&*client, tx0) + .unwrap(); + client + .miner() + .import_own_transaction(&*client, tx1) + .unwrap(); + assert_eq!(0, client.transactions_to_propagate().len()); + assert_eq!( + 0, + client + .miner() + .ready_transactions(&*client, 10, PendingOrdering::Priority) + .len() + ); + push_blocks_to_client(&client, 53, 2, 2); + client.flush_queue(); + assert_eq!(2, client.transactions_to_propagate().len()); + assert_eq!( + 2, + client + .miner() + .ready_transactions(&*client, 10, PendingOrdering::Priority) + .len() + ); } #[test] fn transaction_proof() { - use ::client::ProvingBlockChainClient; - - let client = generate_dummy_client(0); - let address = Address::random(); - let test_spec = Spec::new_test(); - for _ in 0..20 { - let mut b = client.prepare_open_block(Address::default(), (3141562.into(), 31415620.into()), vec![]).unwrap(); - b.block_mut().state_mut().add_balance(&address, &5.into(), CleanupMode::NoEmpty).unwrap(); - b.block_mut().state_mut().commit().unwrap(); - let b = b.close_and_lock().unwrap().seal(&*test_spec.engine, vec![]).unwrap(); - client.import_sealed_block(b).unwrap(); // account change is in the journal overlay - } - - let transaction = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 21000.into(), - action: Action::Call(Address::default()), - value: 5.into(), - data: Vec::new(), - }.fake_sign(address); - - let proof = client.prove_transaction(transaction.clone(), BlockId::Latest).unwrap().1; - let backend = state::backend::ProofCheck::new(&proof); - - let mut factories = ::factory::Factories::default(); - factories.accountdb = ::account_db::Factory::Plain; // raw state values, no mangled keys. - let root = *client.best_block_header().state_root(); - - let machine = test_spec.engine.machine(); - let env_info = client.latest_env_info(); - let schedule = machine.schedule(env_info.number); - let mut state = State::from_existing(backend, root, 0.into(), factories.clone()).unwrap(); - Executive::new(&mut state, &env_info, &machine, &schedule) - .transact(&transaction, TransactOptions::with_no_tracing().dont_check_nonce()).unwrap(); - - assert_eq!(state.balance(&Address::default()).unwrap(), 5.into()); - assert_eq!(state.balance(&address).unwrap(), 95.into()); + use client::ProvingBlockChainClient; + + let client = generate_dummy_client(0); + let address = Address::random(); + let test_spec = Spec::new_test(); + for _ in 0..20 { + let mut b = client + .prepare_open_block( + Address::default(), + (3141562.into(), 31415620.into()), + vec![], + ) + .unwrap(); + b.block_mut() + .state_mut() + .add_balance(&address, &5.into(), CleanupMode::NoEmpty) + .unwrap(); + b.block_mut().state_mut().commit().unwrap(); + let b = b + .close_and_lock() + .unwrap() + .seal(&*test_spec.engine, vec![]) + .unwrap(); + client.import_sealed_block(b).unwrap(); // account change is in the journal overlay + } + + let transaction = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 21000.into(), + action: Action::Call(Address::default()), + value: 5.into(), + data: Vec::new(), + } + .fake_sign(address); + + let proof = client + .prove_transaction(transaction.clone(), BlockId::Latest) + .unwrap() + .1; + let backend = state::backend::ProofCheck::new(&proof); + + let mut factories = ::factory::Factories::default(); + factories.accountdb = ::account_db::Factory::Plain; // raw state values, no mangled keys. + let root = *client.best_block_header().state_root(); + + let machine = test_spec.engine.machine(); + let env_info = client.latest_env_info(); + let schedule = machine.schedule(env_info.number); + let mut state = State::from_existing(backend, root, 0.into(), factories.clone()).unwrap(); + Executive::new(&mut state, &env_info, &machine, &schedule) + .transact( + &transaction, + TransactOptions::with_no_tracing().dont_check_nonce(), + ) + .unwrap(); + + assert_eq!(state.balance(&Address::default()).unwrap(), 5.into()); + assert_eq!(state.balance(&address).unwrap(), 95.into()); +} + +#[test] +fn reset_blockchain() { + let client = get_test_client_with_blocks(get_good_dummy_block_seq(19)); + // 19 + genesis block + assert!(client.block_header(BlockId::Number(20)).is_some()); + assert_eq!( + client.block_header(BlockId::Number(20)).unwrap().hash(), + client.best_block_header().hash() + ); + + assert!(client.reset(5).is_ok()); + + client.chain().clear_cache(); + + assert!(client.block_header(BlockId::Number(20)).is_none()); + assert!(client.block_header(BlockId::Number(19)).is_none()); + assert!(client.block_header(BlockId::Number(18)).is_none()); + assert!(client.block_header(BlockId::Number(17)).is_none()); + assert!(client.block_header(BlockId::Number(16)).is_none()); + + assert!(client.block_header(BlockId::Number(15)).is_some()); +} + +#[test] +fn import_export_hex() { + let client = get_test_client_with_blocks(get_good_dummy_block_seq(19)); + let block_rlps = (15..20) + .filter_map(|num| client.block(BlockId::Number(num))) + .map(|header| header.raw().to_hex()) + .collect::>(); + + let mut out = Vec::new(); + + client + .export_blocks( + Box::new(&mut out), + BlockId::Number(15), + BlockId::Number(20), + Some(DataFormat::Hex), + ) + .unwrap(); + + let written = from_utf8(&out) + .unwrap() + .split("\n") + // last line is empty, ignore it. + .take(5) + .collect::>(); + assert_eq!(block_rlps, written); + + assert!(client.reset(5).is_ok()); + client.chain().clear_cache(); + + assert!(client.block_header(BlockId::Number(20)).is_none()); + assert!(client.block_header(BlockId::Number(19)).is_none()); + assert!(client.block_header(BlockId::Number(18)).is_none()); + assert!(client.block_header(BlockId::Number(17)).is_none()); + assert!(client.block_header(BlockId::Number(16)).is_none()); + + client + .import_blocks(Box::new(&*out), Some(DataFormat::Hex)) + .unwrap(); + + assert!(client.block_header(BlockId::Number(20)).is_some()); + assert!(client.block_header(BlockId::Number(19)).is_some()); + assert!(client.block_header(BlockId::Number(18)).is_some()); + assert!(client.block_header(BlockId::Number(17)).is_some()); + assert!(client.block_header(BlockId::Number(16)).is_some()); +} + +#[test] +fn import_export_binary() { + let client = get_test_client_with_blocks(get_good_dummy_block_seq(19)); + + let mut out = Vec::new(); + + client + .export_blocks( + Box::new(&mut out), + BlockId::Number(15), + BlockId::Number(20), + Some(DataFormat::Binary), + ) + .unwrap(); + + assert!(client.reset(5).is_ok()); + client.chain().clear_cache(); + + assert!(client.block_header(BlockId::Number(20)).is_none()); + assert!(client.block_header(BlockId::Number(19)).is_none()); + assert!(client.block_header(BlockId::Number(18)).is_none()); + assert!(client.block_header(BlockId::Number(17)).is_none()); + assert!(client.block_header(BlockId::Number(16)).is_none()); + + client + .import_blocks(Box::new(&*out), Some(DataFormat::Binary)) + .unwrap(); + + assert!(client.block_header(BlockId::Number(19)).is_some()); + assert!(client.block_header(BlockId::Number(18)).is_some()); + assert!(client.block_header(BlockId::Number(20)).is_some()); + assert!(client.block_header(BlockId::Number(17)).is_some()); + assert!(client.block_header(BlockId::Number(16)).is_some()); } diff --git a/ethcore/src/tests/evm.rs b/ethcore/src/tests/evm.rs index ec0b1dd8e6a..555207d0a38 100644 --- a/ethcore/src/tests/evm.rs +++ b/ethcore/src/tests/evm.rs @@ -1,98 +1,100 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Tests of EVM integration with transaction execution. -use std::sync::Arc; -use hash::keccak; -use vm::{EnvInfo, ActionParams, ActionValue, CallType, ParamsType}; use evm::{Factory, VMType}; use executive::Executive; +use hash::keccak; use state::Substate; +use std::sync::Arc; use test_helpers::get_temp_state_with_factory; -use trace::{NoopVMTracer, NoopTracer}; +use trace::{NoopTracer, NoopVMTracer}; use types::transaction::SYSTEM_ADDRESS; +use vm::{ActionParams, ActionValue, CallType, EnvInfo, ParamsType}; use rustc_hex::FromHex; -use ethereum_types::{H256, Address}; +use ethereum_types::{Address, H256}; -evm_test!{test_blockhash_eip210: test_blockhash_eip210_int} +evm_test! {test_blockhash_eip210: test_blockhash_eip210_int} fn test_blockhash_eip210(factory: Factory) { - let get_prev_hash_code = Arc::new("600143034060205260206020f3".from_hex().unwrap()); // this returns previous block hash - let get_prev_hash_code_hash = keccak(get_prev_hash_code.as_ref()); - // This is same as DEFAULT_BLOCKHASH_CONTRACT except for metropolis transition block check removed. - let test_blockhash_contract = "73fffffffffffffffffffffffffffffffffffffffe33141561007a57600143036020526000356101006020510755600061010060205107141561005057600035610100610100602051050761010001555b6000620100006020510714156100755760003561010062010000602051050761020001555b61014a565b4360003512151561009057600060405260206040f35b610100600035430312156100b357610100600035075460605260206060f3610149565b62010000600035430312156100d157600061010060003507146100d4565b60005b156100f6576101006101006000350507610100015460805260206080f3610148565b630100000060003543031215610116576000620100006000350714610119565b60005b1561013c57610100620100006000350507610200015460a052602060a0f3610147565b600060c052602060c0f35b5b5b5b5b"; - let blockhash_contract_code = Arc::new(test_blockhash_contract.from_hex().unwrap()); - let blockhash_contract_code_hash = keccak(blockhash_contract_code.as_ref()); - let machine = ::ethereum::new_eip210_test_machine(); - let mut env_info = EnvInfo::default(); + let get_prev_hash_code = Arc::new("600143034060205260206020f3".from_hex().unwrap()); // this returns previous block hash + let get_prev_hash_code_hash = keccak(get_prev_hash_code.as_ref()); + // This is same as DEFAULT_BLOCKHASH_CONTRACT except for metropolis transition block check removed. + let test_blockhash_contract = "73fffffffffffffffffffffffffffffffffffffffe33141561007a57600143036020526000356101006020510755600061010060205107141561005057600035610100610100602051050761010001555b6000620100006020510714156100755760003561010062010000602051050761020001555b61014a565b4360003512151561009057600060405260206040f35b610100600035430312156100b357610100600035075460605260206060f3610149565b62010000600035430312156100d157600061010060003507146100d4565b60005b156100f6576101006101006000350507610100015460805260206080f3610148565b630100000060003543031215610116576000620100006000350714610119565b60005b1561013c57610100620100006000350507610200015460a052602060a0f3610147565b600060c052602060c0f35b5b5b5b5b"; + let blockhash_contract_code = Arc::new(test_blockhash_contract.from_hex().unwrap()); + let blockhash_contract_code_hash = keccak(blockhash_contract_code.as_ref()); + let machine = ::ethereum::new_eip210_test_machine(); + let mut env_info = EnvInfo::default(); - // populate state with 256 last hashes - let mut state = get_temp_state_with_factory(factory); - let contract_address: Address = 0xf0.into(); - state.init_code(&contract_address, (*blockhash_contract_code).clone()).unwrap(); - for i in 1 .. 257 { - env_info.number = i.into(); - let params = ActionParams { - code_address: contract_address.clone(), - address: contract_address, - sender: SYSTEM_ADDRESS.clone(), - origin: SYSTEM_ADDRESS.clone(), - gas: 100000.into(), - gas_price: 0.into(), - value: ActionValue::Transfer(0.into()), - code: Some(blockhash_contract_code.clone()), - code_hash: Some(blockhash_contract_code_hash), - data: Some(H256::from(i - 1).to_vec()), - call_type: CallType::Call, - params_type: ParamsType::Separate, - }; - let schedule = machine.schedule(env_info.number); - let mut ex = Executive::new(&mut state, &env_info, &machine, &schedule); - let mut substate = Substate::new(); - if let Err(e) = ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) { - panic!("Encountered error on updating last hashes: {}", e); - } - } + // populate state with 256 last hashes + let mut state = get_temp_state_with_factory(factory); + let contract_address: Address = 0xf0.into(); + state + .init_code(&contract_address, (*blockhash_contract_code).clone()) + .unwrap(); + for i in 1..257 { + env_info.number = i.into(); + let params = ActionParams { + code_address: contract_address.clone(), + address: contract_address, + sender: SYSTEM_ADDRESS.clone(), + origin: SYSTEM_ADDRESS.clone(), + gas: 100000.into(), + gas_price: 0.into(), + value: ActionValue::Transfer(0.into()), + code: Some(blockhash_contract_code.clone()), + code_hash: Some(blockhash_contract_code_hash), + data: Some(H256::from(i - 1).to_vec()), + call_type: CallType::Call, + params_type: ParamsType::Separate, + }; + let schedule = machine.schedule(env_info.number); + let mut ex = Executive::new(&mut state, &env_info, &machine, &schedule); + let mut substate = Substate::new(); + if let Err(e) = ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) { + panic!("Encountered error on updating last hashes: {}", e); + } + } - env_info.number = 256; - let params = ActionParams { - code_address: Address::new(), - address: Address::new(), - sender: Address::new(), - origin: Address::new(), - gas: 100000.into(), - gas_price: 0.into(), - value: ActionValue::Transfer(0.into()), - code: Some(get_prev_hash_code), - code_hash: Some(get_prev_hash_code_hash), - data: None, - call_type: CallType::Call, - params_type: ParamsType::Separate, - }; - let schedule = machine.schedule(env_info.number); - let mut ex = Executive::new(&mut state, &env_info, &machine, &schedule); - let mut substate = Substate::new(); - let res = ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer); - let output = match res { - Ok(res) => H256::from(&res.return_data[..32]), - Err(e) => { - panic!("Encountered error on getting last hash: {}", e); - }, - }; - assert_eq!(output, 255.into()); + env_info.number = 256; + let params = ActionParams { + code_address: Address::new(), + address: Address::new(), + sender: Address::new(), + origin: Address::new(), + gas: 100000.into(), + gas_price: 0.into(), + value: ActionValue::Transfer(0.into()), + code: Some(get_prev_hash_code), + code_hash: Some(get_prev_hash_code_hash), + data: None, + call_type: CallType::Call, + params_type: ParamsType::Separate, + }; + let schedule = machine.schedule(env_info.number); + let mut ex = Executive::new(&mut state, &env_info, &machine, &schedule); + let mut substate = Substate::new(); + let res = ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer); + let output = match res { + Ok(res) => H256::from(&res.return_data[..32]), + Err(e) => { + panic!("Encountered error on getting last hash: {}", e); + } + }; + assert_eq!(output, 255.into()); } diff --git a/ethcore/src/tests/mod.rs b/ethcore/src/tests/mod.rs index ee45c73856f..9492bc316d8 100644 --- a/ethcore/src/tests/mod.rs +++ b/ethcore/src/tests/mod.rs @@ -1,20 +1,20 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -mod client; mod blockchain; +mod client; mod evm; mod trace; diff --git a/ethcore/src/tests/trace.rs b/ethcore/src/tests/trace.rs index c14f13cf59e..4b21dd06f8e 100644 --- a/ethcore/src/tests/trace.rs +++ b/ethcore/src/tests/trace.rs @@ -1,210 +1,247 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Client tests of tracing +use block::*; +use client::{BlockChainClient, Client, ClientConfig, *}; +use ethereum_types::{Address, U256}; use ethkey::KeyPair; use hash::keccak; -use block::*; -use ethereum_types::{U256, Address}; use io::*; +use miner::Miner; use spec::*; -use client::*; -use test_helpers::get_temp_state_db; -use client::{BlockChainClient, Client, ClientConfig}; use std::sync::Arc; -use miner::Miner; -use types::transaction::{Action, Transaction}; -use trace::{RewardType, LocalizedTrace}; -use trace::trace::Action::Reward; -use test_helpers; +use test_helpers::{self, get_temp_state_db}; +use trace::{trace::Action::Reward, LocalizedTrace, RewardType}; +use types::{ + header::Header, + transaction::{Action, Transaction}, + view, + views::BlockView, +}; use verification::queue::kind::blocks::Unverified; -use types::header::Header; -use types::view; -use types::views::BlockView; #[test] fn can_trace_block_and_uncle_reward() { - let db = test_helpers::new_db(); - let spec = Spec::new_test_with_reward(); - let engine = &*spec.engine; - - // Create client - let mut client_config = ClientConfig::default(); - client_config.tracing.enabled = true; - let client = Client::new( - client_config, - &spec, - db, - Arc::new(Miner::new_for_tests(&spec, None)), - IoChannel::disconnected(), - ).unwrap(); - - // Create test data: - // genesis - // | - // root_block - // | - // parent_block - // | - // block with transaction and uncle - - let genesis_header = spec.genesis_header(); - let genesis_gas = genesis_header.gas_limit().clone(); - - let mut db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let mut rolling_timestamp = 40; - let mut last_hashes = vec![]; - let mut last_header = genesis_header.clone(); - last_hashes.push(last_header.hash()); - - let kp = KeyPair::from_secret_slice(&keccak("")).unwrap(); - let author = kp.address(); - - // Add root block first - let mut root_block = OpenBlock::new( - engine, - Default::default(), - false, - db, - &last_header, - Arc::new(last_hashes.clone()), - author.clone(), - (3141562.into(), 31415620.into()), - vec![], - false, - None, - ).unwrap(); - rolling_timestamp += 10; - root_block.set_timestamp(rolling_timestamp); - - let root_block = root_block.close_and_lock().unwrap().seal(engine, vec![]).unwrap(); - - if let Err(e) = client.import_block(Unverified::from_rlp(root_block.rlp_bytes()).unwrap()) { - panic!("error importing block which is valid by definition: {:?}", e); - } - - last_header = view!(BlockView, &root_block.rlp_bytes()).header(); - let root_header = last_header.clone(); - db = root_block.drain().state.drop().1; - - last_hashes.push(last_header.hash()); - - // Add parent block - let mut parent_block = OpenBlock::new( - engine, - Default::default(), - false, - db, - &last_header, - Arc::new(last_hashes.clone()), - author.clone(), - (3141562.into(), 31415620.into()), - vec![], - false, - None, - ).unwrap(); - rolling_timestamp += 10; - parent_block.set_timestamp(rolling_timestamp); - - let parent_block = parent_block.close_and_lock().unwrap().seal(engine, vec![]).unwrap(); - - if let Err(e) = client.import_block(Unverified::from_rlp(parent_block.rlp_bytes()).unwrap()) { - panic!("error importing block which is valid by definition: {:?}", e); - } - - last_header = view!(BlockView,&parent_block.rlp_bytes()).header(); - db = parent_block.drain().state.drop().1; - - last_hashes.push(last_header.hash()); - - // Add testing block with transaction and uncle - let mut block = OpenBlock::new( - engine, - Default::default(), - true, - db, - &last_header, - Arc::new(last_hashes.clone()), - author.clone(), - (3141562.into(), 31415620.into()), - vec![], - false, - None, - ).unwrap(); - rolling_timestamp += 10; - block.set_timestamp(rolling_timestamp); - - let mut n = 0; - for _ in 0..1 { - block.push_transaction(Transaction { - nonce: n.into(), - gas_price: 10000.into(), - gas: 100000.into(), - action: Action::Create, - data: vec![], - value: U256::zero(), - }.sign(kp.secret(), Some(spec.network_id())), None).unwrap(); - n += 1; - } - - let mut uncle = Header::new(); - let uncle_author: Address = "ef2d6d194084c2de36e0dabfce45d046b37d1106".into(); - uncle.set_author(uncle_author); - uncle.set_parent_hash(root_header.hash()); - uncle.set_gas_limit(genesis_gas); - uncle.set_number(root_header.number() + 1); - uncle.set_timestamp(rolling_timestamp); - block.push_uncle(uncle).unwrap(); - - let block = block.close_and_lock().unwrap().seal(engine, vec![]).unwrap(); - - let res = client.import_block(Unverified::from_rlp(block.rlp_bytes()).unwrap()); - if res.is_err() { - panic!("error importing block: {:#?}", res.err().unwrap()); - } - - block.drain(); - client.flush_queue(); - client.import_verified_blocks(); - - // Test0. Check overall filter - let filter = TraceFilter { - range: (BlockId::Number(1)..BlockId::Number(3)), - from_address: vec![], - to_address: vec![], - after: None, - count: None, - }; - - let traces = client.filter_traces(filter); - assert!(traces.is_some(), "Filtered traces should be present"); - let traces_vec = traces.unwrap(); - let block_reward_traces: Vec = traces_vec.clone().into_iter().filter(|trace| match (trace).action { - Reward(ref a) => a.reward_type == RewardType::Block, - _ => false, - }).collect(); - assert_eq!(block_reward_traces.len(), 3); - let uncle_reward_traces: Vec = traces_vec.clone().into_iter().filter(|trace| match (trace).action { - Reward(ref a) => a.reward_type == RewardType::Uncle, - _ => false, - }).collect(); - assert_eq!(uncle_reward_traces.len(), 1); - - // Test1. Check block filter - let traces = client.block_traces(BlockId::Number(3)); - assert_eq!(traces.unwrap().len(), 3); + let db = test_helpers::new_db(); + let spec = Spec::new_test_with_reward(); + let engine = &*spec.engine; + + // Create client + let mut client_config = ClientConfig::default(); + client_config.tracing.enabled = true; + let client = Client::new( + client_config, + &spec, + db, + Arc::new(Miner::new_for_tests(&spec, None)), + IoChannel::disconnected(), + ) + .unwrap(); + + // Create test data: + // genesis + // | + // root_block + // | + // parent_block + // | + // block with transaction and uncle + + let genesis_header = spec.genesis_header(); + let genesis_gas = genesis_header.gas_limit().clone(); + + let mut db = spec + .ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let mut rolling_timestamp = 40; + let mut last_hashes = vec![]; + let mut last_header = genesis_header.clone(); + last_hashes.push(last_header.hash()); + + let kp = KeyPair::from_secret_slice(&keccak("")).unwrap(); + let author = kp.address(); + + // Add root block first + let mut root_block = OpenBlock::new( + engine, + Default::default(), + false, + db, + &last_header, + Arc::new(last_hashes.clone()), + author.clone(), + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + rolling_timestamp += 10; + root_block.set_timestamp(rolling_timestamp); + + let root_block = root_block + .close_and_lock() + .unwrap() + .seal(engine, vec![]) + .unwrap(); + + if let Err(e) = client.import_block(Unverified::from_rlp(root_block.rlp_bytes()).unwrap()) { + panic!( + "error importing block which is valid by definition: {:?}", + e + ); + } + + last_header = view!(BlockView, &root_block.rlp_bytes()).header(); + let root_header = last_header.clone(); + db = root_block.drain().state.drop().1; + + last_hashes.push(last_header.hash()); + + // Add parent block + let mut parent_block = OpenBlock::new( + engine, + Default::default(), + false, + db, + &last_header, + Arc::new(last_hashes.clone()), + author.clone(), + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + rolling_timestamp += 10; + parent_block.set_timestamp(rolling_timestamp); + + let parent_block = parent_block + .close_and_lock() + .unwrap() + .seal(engine, vec![]) + .unwrap(); + + if let Err(e) = client.import_block(Unverified::from_rlp(parent_block.rlp_bytes()).unwrap()) { + panic!( + "error importing block which is valid by definition: {:?}", + e + ); + } + + last_header = view!(BlockView, &parent_block.rlp_bytes()).header(); + db = parent_block.drain().state.drop().1; + + last_hashes.push(last_header.hash()); + + // Add testing block with transaction and uncle + let mut block = OpenBlock::new( + engine, + Default::default(), + true, + db, + &last_header, + Arc::new(last_hashes.clone()), + author.clone(), + (3141562.into(), 31415620.into()), + vec![], + false, + None, + ) + .unwrap(); + rolling_timestamp += 10; + block.set_timestamp(rolling_timestamp); + + let mut n = 0; + for _ in 0..1 { + block + .push_transaction( + Transaction { + nonce: n.into(), + gas_price: 10000.into(), + gas: 100000.into(), + action: Action::Create, + data: vec![], + value: U256::zero(), + } + .sign(kp.secret(), Some(spec.network_id())), + None, + ) + .unwrap(); + n += 1; + } + + let mut uncle = Header::new(); + let uncle_author: Address = "ef2d6d194084c2de36e0dabfce45d046b37d1106".into(); + uncle.set_author(uncle_author); + uncle.set_parent_hash(root_header.hash()); + uncle.set_gas_limit(genesis_gas); + uncle.set_number(root_header.number() + 1); + uncle.set_timestamp(rolling_timestamp); + block.push_uncle(uncle).unwrap(); + + let block = block + .close_and_lock() + .unwrap() + .seal(engine, vec![]) + .unwrap(); + + let res = client.import_block(Unverified::from_rlp(block.rlp_bytes()).unwrap()); + if res.is_err() { + panic!("error importing block: {:#?}", res.err().unwrap()); + } + + block.drain(); + client.flush_queue(); + client.import_verified_blocks(); + + // Test0. Check overall filter + let filter = TraceFilter { + range: (BlockId::Number(1)..BlockId::Number(3)), + from_address: vec![], + to_address: vec![], + after: None, + count: None, + }; + + let traces = client.filter_traces(filter); + assert!(traces.is_some(), "Filtered traces should be present"); + let traces_vec = traces.unwrap(); + let block_reward_traces: Vec = traces_vec + .clone() + .into_iter() + .filter(|trace| match (trace).action { + Reward(ref a) => a.reward_type == RewardType::Block, + _ => false, + }) + .collect(); + assert_eq!(block_reward_traces.len(), 3); + let uncle_reward_traces: Vec = traces_vec + .clone() + .into_iter() + .filter(|trace| match (trace).action { + Reward(ref a) => a.reward_type == RewardType::Uncle, + _ => false, + }) + .collect(); + assert_eq!(uncle_reward_traces.len(), 1); + + // Test1. Check block filter + let traces = client.block_traces(BlockId::Number(3)); + assert_eq!(traces.unwrap().len(), 3); } diff --git a/ethcore/src/trace/config.rs b/ethcore/src/trace/config.rs index 72fc1765511..157bc01f422 100644 --- a/ethcore/src/trace/config.rs +++ b/ethcore/src/trace/config.rs @@ -1,39 +1,39 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Traces config. /// Traces config. #[derive(Debug, PartialEq, Clone)] pub struct Config { - /// Indicates if tracing should be enabled or not. - /// If it's None, it will be automatically configured. - pub enabled: bool, - /// Preferef cache-size. - pub pref_cache_size: usize, - /// Max cache-size. - pub max_cache_size: usize, + /// Indicates if tracing should be enabled or not. + /// If it's None, it will be automatically configured. + pub enabled: bool, + /// Preferef cache-size. + pub pref_cache_size: usize, + /// Max cache-size. + pub max_cache_size: usize, } impl Default for Config { - fn default() -> Self { - Config { - enabled: false, - pref_cache_size: 15 * 1024 * 1024, - max_cache_size: 20 * 1024 * 1024, - } - } + fn default() -> Self { + Config { + enabled: false, + pref_cache_size: 15 * 1024 * 1024, + max_cache_size: 20 * 1024 * 1024, + } + } } diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index 8dbd38449f7..f0943649ead 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -1,52 +1,52 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Trace database. -use std::collections::HashMap; -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; use blockchain::BlockChainDB; -use db::cache_manager::CacheManager; -use db::{self, Key, Writable, Readable, CacheUpdatePolicy}; +use db::{self, cache_manager::CacheManager, CacheUpdatePolicy, Key, Readable, Writable}; use ethereum_types::{H256, H264}; use heapsize::HeapSizeOf; -use kvdb::{DBTransaction}; +use kvdb::DBTransaction; use parking_lot::RwLock; use types::BlockNumber; -use trace::{LocalizedTrace, Config, Filter, Database as TraceDatabase, ImportRequest, DatabaseExtras}; -use trace::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces}; +use trace::{ + flat::{FlatBlockTraces, FlatTrace, FlatTransactionTraces}, + Config, Database as TraceDatabase, DatabaseExtras, Filter, ImportRequest, LocalizedTrace, +}; const TRACE_DB_VER: &'static [u8] = b"1.0"; #[derive(Debug, Copy, Clone)] enum TraceDBIndex { - /// Block traces index. - BlockTraces = 0, + /// Block traces index. + BlockTraces = 0, } impl Key for H256 { - type Target = H264; - - fn key(&self) -> H264 { - let mut result = H264::default(); - result[0] = TraceDBIndex::BlockTraces as u8; - result[1..33].copy_from_slice(self); - result - } + type Target = H264; + + fn key(&self) -> H264 { + let mut result = H264::default(); + result[0] = TraceDBIndex::BlockTraces as u8; + result[1..33].copy_from_slice(self); + result + } } /// Database to store transaction execution trace. @@ -55,583 +55,704 @@ impl Key for H256 { /// in trace database. Each trace has information, which contracts have been /// touched, which have been created during the execution of transaction, and /// which calls failed. -pub struct TraceDB where T: DatabaseExtras { - /// cache - traces: RwLock>, - /// hashes of cached traces - cache_manager: RwLock>, - /// db - db: Arc, - /// tracing enabled - enabled: bool, - /// extras - extras: Arc, +pub struct TraceDB +where + T: DatabaseExtras, +{ + /// cache + traces: RwLock>, + /// hashes of cached traces + cache_manager: RwLock>, + /// db + db: Arc, + /// tracing enabled + enabled: bool, + /// extras + extras: Arc, } -impl TraceDB where T: DatabaseExtras { - /// Creates new instance of `TraceDB`. - pub fn new(config: Config, db: Arc, extras: Arc) -> Self { - let mut batch = DBTransaction::new(); - let genesis = extras.block_hash(0) - .expect("Genesis block is always inserted upon extras db creation qed"); - batch.write(db::COL_TRACE, &genesis, &FlatBlockTraces::default()); - batch.put(db::COL_TRACE, b"version", TRACE_DB_VER); - db.key_value().write(batch).expect("failed to update version"); - - TraceDB { - traces: RwLock::new(HashMap::new()), - cache_manager: RwLock::new(CacheManager::new(config.pref_cache_size, config.max_cache_size, 10 * 1024)), - db, - enabled: config.enabled, - extras: extras, - } - } - - fn cache_size(&self) -> usize { - self.traces.read().heap_size_of_children() - } - - /// Let the cache system know that a cacheable item has been used. - fn note_trace_used(&self, trace_id: H256) { - let mut cache_manager = self.cache_manager.write(); - cache_manager.note_used(trace_id); - } - - /// Ticks our cache system and throws out any old data. - pub fn collect_garbage(&self) { - let current_size = self.cache_size(); - - let mut traces = self.traces.write(); - let mut cache_manager = self.cache_manager.write(); - - cache_manager.collect_garbage(current_size, | ids | { - for id in &ids { - traces.remove(id); - } - traces.shrink_to_fit(); - - traces.heap_size_of_children() - }); - } - - /// Returns traces for block with hash. - fn traces(&self, block_hash: &H256) -> Option { - let result = self.db.key_value().read_with_cache(db::COL_TRACE, &self.traces, block_hash); - self.note_trace_used(*block_hash); - result - } - - /// Returns vector of transaction traces for given block. - fn transactions_traces(&self, block_hash: &H256) -> Option> { - self.traces(block_hash).map(Into::into) - } - - fn matching_block_traces( - &self, - filter: &Filter, - traces: FlatBlockTraces, - block_hash: H256, - block_number: BlockNumber - ) -> Vec { - let tx_traces: Vec = traces.into(); - tx_traces.into_iter() - .enumerate() - .flat_map(|(tx_number, tx_trace)| { - self.matching_transaction_traces(filter, tx_trace, block_hash.clone(), block_number, tx_number) - }) - .collect() - } - - fn matching_transaction_traces( - &self, - filter: &Filter, - traces: FlatTransactionTraces, - block_hash: H256, - block_number: BlockNumber, - tx_number: usize - ) -> Vec { - let (trace_tx_number, trace_tx_hash) = match self.extras.transaction_hash(block_number, tx_number) { - Some(hash) => (Some(tx_number), Some(hash.clone())), - //None means trace without transaction (reward) - None => (None, None), - }; - - let flat_traces: Vec = traces.into(); - flat_traces.into_iter() - .filter_map(|trace| { - match filter.matches(&trace) { - true => Some(LocalizedTrace { - action: trace.action, - result: trace.result, - subtraces: trace.subtraces, - trace_address: trace.trace_address.into_iter().collect(), - transaction_number: trace_tx_number, - transaction_hash: trace_tx_hash, - block_number: block_number, - block_hash: block_hash - }), - false => None - } - }) - .collect() - } +impl TraceDB +where + T: DatabaseExtras, +{ + /// Creates new instance of `TraceDB`. + pub fn new(config: Config, db: Arc, extras: Arc) -> Self { + let mut batch = DBTransaction::new(); + let genesis = extras + .block_hash(0) + .expect("Genesis block is always inserted upon extras db creation qed"); + batch.write(db::COL_TRACE, &genesis, &FlatBlockTraces::default()); + batch.put(db::COL_TRACE, b"version", TRACE_DB_VER); + db.key_value() + .write(batch) + .expect("failed to update version"); + + TraceDB { + traces: RwLock::new(HashMap::new()), + cache_manager: RwLock::new(CacheManager::new( + config.pref_cache_size, + config.max_cache_size, + 10 * 1024, + )), + db, + enabled: config.enabled, + extras: extras, + } + } + + fn cache_size(&self) -> usize { + self.traces.read().heap_size_of_children() + } + + /// Let the cache system know that a cacheable item has been used. + fn note_trace_used(&self, trace_id: H256) { + let mut cache_manager = self.cache_manager.write(); + cache_manager.note_used(trace_id); + } + + /// Ticks our cache system and throws out any old data. + pub fn collect_garbage(&self) { + let current_size = self.cache_size(); + + let mut traces = self.traces.write(); + let mut cache_manager = self.cache_manager.write(); + + cache_manager.collect_garbage(current_size, |ids| { + for id in &ids { + traces.remove(id); + } + traces.shrink_to_fit(); + + traces.heap_size_of_children() + }); + } + + /// Returns traces for block with hash. + fn traces(&self, block_hash: &H256) -> Option { + let result = self + .db + .key_value() + .read_with_cache(db::COL_TRACE, &self.traces, block_hash); + self.note_trace_used(*block_hash); + result + } + + /// Returns vector of transaction traces for given block. + fn transactions_traces(&self, block_hash: &H256) -> Option> { + self.traces(block_hash).map(Into::into) + } + + fn matching_block_traces( + &self, + filter: &Filter, + traces: FlatBlockTraces, + block_hash: H256, + block_number: BlockNumber, + ) -> Vec { + let tx_traces: Vec = traces.into(); + tx_traces + .into_iter() + .enumerate() + .flat_map(|(tx_number, tx_trace)| { + self.matching_transaction_traces( + filter, + tx_trace, + block_hash.clone(), + block_number, + tx_number, + ) + }) + .collect() + } + + fn matching_transaction_traces( + &self, + filter: &Filter, + traces: FlatTransactionTraces, + block_hash: H256, + block_number: BlockNumber, + tx_number: usize, + ) -> Vec { + let (trace_tx_number, trace_tx_hash) = + match self.extras.transaction_hash(block_number, tx_number) { + Some(hash) => (Some(tx_number), Some(hash.clone())), + //None means trace without transaction (reward) + None => (None, None), + }; + + let flat_traces: Vec = traces.into(); + flat_traces + .into_iter() + .filter_map(|trace| match filter.matches(&trace) { + true => Some(LocalizedTrace { + action: trace.action, + result: trace.result, + subtraces: trace.subtraces, + trace_address: trace.trace_address.into_iter().collect(), + transaction_number: trace_tx_number, + transaction_hash: trace_tx_hash, + block_number: block_number, + block_hash: block_hash, + }), + false => None, + }) + .collect() + } } -impl TraceDatabase for TraceDB where T: DatabaseExtras { - fn tracing_enabled(&self) -> bool { - self.enabled - } - - /// Traces of import request's enacted blocks are expected to be already in database - /// or to be the currently inserted trace. - fn import(&self, batch: &mut DBTransaction, request: ImportRequest) { - // valid (canon): retracted 0, enacted 1 => false, true, - // valid (branch): retracted 0, enacted 0 => false, false, - // valid (bbcc): retracted 1, enacted 1 => true, true, - // invalid: retracted 1, enacted 0 => true, false, - let ret = request.retracted != 0; - let ena = !request.enacted.is_empty(); - assert!(!(ret && !ena)); - // fast return if tracing is disabled - if !self.tracing_enabled() { - return; - } - - // now let's rebuild the blooms - if !request.enacted.is_empty() { - let range_start = request.block_number + 1 - request.enacted.len() as u64; - let enacted_blooms: Vec<_> = request.enacted - .iter() - // all traces are expected to be found here. That's why `expect` has been used - // instead of `filter_map`. If some traces haven't been found, it meens that - // traces database is corrupted or incomplete. - .map(|block_hash| if block_hash == &request.block_hash { - request.traces.bloom() - } else { - self.traces(block_hash).expect("Traces database is incomplete.").bloom() - }) - .collect(); - - self.db.trace_blooms() - .insert_blooms(range_start, enacted_blooms.iter()) - .expect("Low level database error. Some issue with disk?"); - } - - // insert new block traces into the cache and the database - { - let mut traces = self.traces.write(); - // it's important to use overwrite here, - // cause this value might be queried by hash later - batch.write_with_cache(db::COL_TRACE, &mut *traces, request.block_hash, request.traces, CacheUpdatePolicy::Overwrite); - // note_used must be called after locking traces to avoid cache/traces deadlock on garbage collection - self.note_trace_used(request.block_hash); - } - } - - fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec) -> Option { - self.extras.block_hash(block_number) - .and_then(|block_hash| self.transactions_traces(&block_hash) - .and_then(|traces| traces.into_iter().nth(tx_position)) - .map(Into::>::into) - // this may and should be optimized - .and_then(|traces| traces.into_iter().find(|trace| trace.trace_address == trace_position)) - .map(|trace| { - let tx_hash = self.extras.transaction_hash(block_number, tx_position) - .expect("Expected to find transaction hash. Database is probably corrupted"); - - LocalizedTrace { - action: trace.action, - result: trace.result, - subtraces: trace.subtraces, - trace_address: trace.trace_address.into_iter().collect(), - transaction_number: Some(tx_position), - transaction_hash: Some(tx_hash), - block_number: block_number, - block_hash: block_hash, - } - }) - ) - } - - fn transaction_traces(&self, block_number: BlockNumber, tx_position: usize) -> Option> { - self.extras.block_hash(block_number) - .and_then(|block_hash| self.transactions_traces(&block_hash) - .and_then(|traces| traces.into_iter().nth(tx_position)) - .map(Into::>::into) - .map(|traces| { - let tx_hash = self.extras.transaction_hash(block_number, tx_position) - .expect("Expected to find transaction hash. Database is probably corrupted"); - - traces.into_iter() - .map(|trace| LocalizedTrace { - action: trace.action, - result: trace.result, - subtraces: trace.subtraces, - trace_address: trace.trace_address.into_iter().collect(), - transaction_number: Some(tx_position), - transaction_hash: Some(tx_hash.clone()), - block_number: block_number, - block_hash: block_hash - }) - .collect() - }) - ) - } - - fn block_traces(&self, block_number: BlockNumber) -> Option> { - self.extras.block_hash(block_number) - .and_then(|block_hash| self.transactions_traces(&block_hash) - .map(|traces| { - traces.into_iter() - .map(Into::>::into) - .enumerate() - .flat_map(|(tx_position, traces)| { - let (trace_tx_number, trace_tx_hash) = match self.extras.transaction_hash(block_number, tx_position) { - Some(hash) => (Some(tx_position), Some(hash.clone())), - //None means trace without transaction (reward) - None => (None, None), - }; - - traces.into_iter() - .map(|trace| LocalizedTrace { - action: trace.action, - result: trace.result, - subtraces: trace.subtraces, - trace_address: trace.trace_address.into_iter().collect(), - transaction_number: trace_tx_number, - transaction_hash: trace_tx_hash, - block_number: block_number, - block_hash: block_hash, - }) - .collect::>() - }) - .collect::>() - }) - ) - } - - fn filter(&self, filter: &Filter) -> Vec { - let possibilities = filter.bloom_possibilities(); - let numbers = self.db.trace_blooms() - .filter(filter.range.start as u64, filter.range.end as u64, &possibilities) - .expect("Low level database error. Some issue with disk?"); - - numbers.into_iter() - .flat_map(|n| { - let number = n as BlockNumber; - let hash = self.extras.block_hash(number) - .expect("Expected to find block hash. Extras db is probably corrupted"); - let traces = self.traces(&hash) - .expect("Expected to find a trace. Db is probably corrupted."); - self.matching_block_traces(filter, traces, hash, number) - }) - .collect() - } +impl TraceDatabase for TraceDB +where + T: DatabaseExtras, +{ + fn tracing_enabled(&self) -> bool { + self.enabled + } + + /// Traces of import request's enacted blocks are expected to be already in database + /// or to be the currently inserted trace. + fn import(&self, batch: &mut DBTransaction, request: ImportRequest) { + // valid (canon): retracted 0, enacted 1 => false, true, + // valid (branch): retracted 0, enacted 0 => false, false, + // valid (bbcc): retracted 1, enacted 1 => true, true, + // invalid: retracted 1, enacted 0 => true, false, + let ret = request.retracted != 0; + let ena = !request.enacted.is_empty(); + assert!(!(ret && !ena)); + // fast return if tracing is disabled + if !self.tracing_enabled() { + return; + } + + // now let's rebuild the blooms + if !request.enacted.is_empty() { + let range_start = request.block_number + 1 - request.enacted.len() as u64; + let enacted_blooms: Vec<_> = request + .enacted + .iter() + // all traces are expected to be found here. That's why `expect` has been used + // instead of `filter_map`. If some traces haven't been found, it meens that + // traces database is corrupted or incomplete. + .map(|block_hash| { + if block_hash == &request.block_hash { + request.traces.bloom() + } else { + self.traces(block_hash) + .expect("Traces database is incomplete.") + .bloom() + } + }) + .collect(); + + self.db + .trace_blooms() + .insert_blooms(range_start, enacted_blooms.iter()) + .expect("Low level database error. Some issue with disk?"); + } + + // insert new block traces into the cache and the database + { + let mut traces = self.traces.write(); + // it's important to use overwrite here, + // cause this value might be queried by hash later + batch.write_with_cache( + db::COL_TRACE, + &mut *traces, + request.block_hash, + request.traces, + CacheUpdatePolicy::Overwrite, + ); + // note_used must be called after locking traces to avoid cache/traces deadlock on garbage collection + self.note_trace_used(request.block_hash); + } + } + + fn trace( + &self, + block_number: BlockNumber, + tx_position: usize, + trace_position: Vec, + ) -> Option { + self.extras.block_hash(block_number).and_then(|block_hash| { + self.transactions_traces(&block_hash) + .and_then(|traces| traces.into_iter().nth(tx_position)) + .map(Into::>::into) + // this may and should be optimized + .and_then(|traces| { + traces + .into_iter() + .find(|trace| trace.trace_address == trace_position) + }) + .map(|trace| { + let tx_hash = self + .extras + .transaction_hash(block_number, tx_position) + .expect( + "Expected to find transaction hash. Database is probably corrupted", + ); + + LocalizedTrace { + action: trace.action, + result: trace.result, + subtraces: trace.subtraces, + trace_address: trace.trace_address.into_iter().collect(), + transaction_number: Some(tx_position), + transaction_hash: Some(tx_hash), + block_number: block_number, + block_hash: block_hash, + } + }) + }) + } + + fn transaction_traces( + &self, + block_number: BlockNumber, + tx_position: usize, + ) -> Option> { + self.extras.block_hash(block_number).and_then(|block_hash| { + self.transactions_traces(&block_hash) + .and_then(|traces| traces.into_iter().nth(tx_position)) + .map(Into::>::into) + .map(|traces| { + let tx_hash = self + .extras + .transaction_hash(block_number, tx_position) + .expect( + "Expected to find transaction hash. Database is probably corrupted", + ); + + traces + .into_iter() + .map(|trace| LocalizedTrace { + action: trace.action, + result: trace.result, + subtraces: trace.subtraces, + trace_address: trace.trace_address.into_iter().collect(), + transaction_number: Some(tx_position), + transaction_hash: Some(tx_hash.clone()), + block_number: block_number, + block_hash: block_hash, + }) + .collect() + }) + }) + } + + fn block_traces(&self, block_number: BlockNumber) -> Option> { + self.extras.block_hash(block_number).and_then(|block_hash| { + self.transactions_traces(&block_hash).map(|traces| { + traces + .into_iter() + .map(Into::>::into) + .enumerate() + .flat_map(|(tx_position, traces)| { + let (trace_tx_number, trace_tx_hash) = + match self.extras.transaction_hash(block_number, tx_position) { + Some(hash) => (Some(tx_position), Some(hash.clone())), + //None means trace without transaction (reward) + None => (None, None), + }; + + traces + .into_iter() + .map(|trace| LocalizedTrace { + action: trace.action, + result: trace.result, + subtraces: trace.subtraces, + trace_address: trace.trace_address.into_iter().collect(), + transaction_number: trace_tx_number, + transaction_hash: trace_tx_hash, + block_number: block_number, + block_hash: block_hash, + }) + .collect::>() + }) + .collect::>() + }) + }) + } + + fn filter(&self, filter: &Filter) -> Vec { + let possibilities = filter.bloom_possibilities(); + let numbers = self + .db + .trace_blooms() + .filter( + filter.range.start as u64, + filter.range.end as u64, + &possibilities, + ) + .expect("Low level database error. Some issue with disk?"); + + numbers + .into_iter() + .flat_map(|n| { + let number = n as BlockNumber; + let hash = self + .extras + .block_hash(number) + .expect("Expected to find block hash. Extras db is probably corrupted"); + let traces = self + .traces(&hash) + .expect("Expected to find a trace. Db is probably corrupted."); + self.matching_block_traces(filter, traces, hash, number) + }) + .collect() + } } #[cfg(test)] mod tests { - use std::collections::HashMap; - use std::sync::Arc; - use ethereum_types::{H256, U256, Address}; - use kvdb::{DBTransaction}; - use types::BlockNumber; - use trace::{Config, TraceDB, Database as TraceDatabase, DatabaseExtras, ImportRequest}; - use trace::{Filter, LocalizedTrace, AddressesFilter, TraceError}; - use trace::trace::{Call, Action, Res}; - use trace::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces}; - use evm::CallType; - use test_helpers::new_db; - - struct NoopExtras; - - impl DatabaseExtras for NoopExtras { - fn block_hash(&self, block_number: BlockNumber) -> Option { - if block_number == 0 { - Some(H256::default()) - } else { - unimplemented!() - } - } - - fn transaction_hash(&self, _block_number: BlockNumber, _tx_position: usize) -> Option { - unimplemented!(); - } - } - - #[derive(Clone)] - struct Extras { - block_hashes: HashMap, - transaction_hashes: HashMap>, - } - - impl Default for Extras { - fn default() -> Self { - Extras { - block_hashes: HashMap::new(), - transaction_hashes: HashMap::new(), - } - } - } - - impl DatabaseExtras for Extras { - fn block_hash(&self, block_number: BlockNumber) -> Option { - self.block_hashes.get(&block_number).cloned() - } - - fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option { - self.transaction_hashes.get(&block_number) - .and_then(|hashes| hashes.iter().cloned().nth(tx_position)) - } - } - - #[test] - fn test_reopening_db_with_tracing_off() { - let db = new_db(); - let mut config = Config::default(); - - // set autotracing - config.enabled = false; - - { - let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(NoopExtras)); - assert_eq!(tracedb.tracing_enabled(), false); - } - } - - #[test] - fn test_reopening_db_with_tracing_on() { - let db = new_db(); - let mut config = Config::default(); - - // set tracing on - config.enabled = true; - - { - let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(NoopExtras)); - assert_eq!(tracedb.tracing_enabled(), true); - } - } - - fn create_simple_import_request(block_number: BlockNumber, block_hash: H256) -> ImportRequest { - ImportRequest { - traces: FlatBlockTraces::from(vec![FlatTransactionTraces::from(vec![FlatTrace { - trace_address: Default::default(), - subtraces: 0, - action: Action::Call(Call { - from: 1.into(), - to: 2.into(), - value: 3.into(), - gas: 4.into(), - input: vec![], - call_type: CallType::Call, - }), - result: Res::FailedCall(TraceError::OutOfGas), - }])]), - block_hash: block_hash.clone(), - block_number: block_number, - enacted: vec![block_hash], - retracted: 0, - } - } - - fn create_noncanon_import_request(block_number: BlockNumber, block_hash: H256) -> ImportRequest { - ImportRequest { - traces: FlatBlockTraces::from(vec![FlatTransactionTraces::from(vec![FlatTrace { - trace_address: Default::default(), - subtraces: 0, - action: Action::Call(Call { - from: 1.into(), - to: 2.into(), - value: 3.into(), - gas: 4.into(), - input: vec![], - call_type: CallType::Call, - }), - result: Res::FailedCall(TraceError::OutOfGas), - }])]), - block_hash: block_hash.clone(), - block_number: block_number, - enacted: vec![], - retracted: 0, - } - } - - fn create_simple_localized_trace(block_number: BlockNumber, block_hash: H256, tx_hash: H256) -> LocalizedTrace { - LocalizedTrace { - action: Action::Call(Call { - from: Address::from(1), - to: Address::from(2), - value: U256::from(3), - gas: U256::from(4), - input: vec![], - call_type: CallType::Call, - }), - result: Res::FailedCall(TraceError::OutOfGas), - trace_address: vec![], - subtraces: 0, - transaction_number: Some(0), - transaction_hash: Some(tx_hash), - block_number: block_number, - block_hash: block_hash, - } - } - - #[test] - fn test_import_non_canon_traces() { - let db = new_db(); - let mut config = Config::default(); - config.enabled = true; - let block_0 = H256::from(0xa1); - let block_1 = H256::from(0xa2); - let tx_0 = H256::from(0xff); - let tx_1 = H256::from(0xaf); - - let mut extras = Extras::default(); - extras.block_hashes.insert(0, block_0.clone()); - extras.block_hashes.insert(1, block_1.clone()); - extras.transaction_hashes.insert(0, vec![tx_0.clone()]); - extras.transaction_hashes.insert(1, vec![tx_1.clone()]); - - let tracedb = TraceDB::new(config, db.clone(), Arc::new(extras)); - - // import block 0 - let request = create_noncanon_import_request(0, block_0.clone()); - let mut batch = DBTransaction::new(); - tracedb.import(&mut batch, request); - db.key_value().write(batch).unwrap(); - - assert!(tracedb.traces(&block_0).is_some(), "Traces should be available even if block is non-canon."); - } - - #[test] - fn test_import() { - let db = new_db(); - let mut config = Config::default(); - config.enabled = true; - let block_1 = H256::from(0xa1); - let block_2 = H256::from(0xa2); - let tx_1 = H256::from(0xff); - let tx_2 = H256::from(0xaf); - - let mut extras = Extras::default(); - extras.block_hashes.insert(0, H256::default()); - - extras.block_hashes.insert(1, block_1.clone()); - extras.block_hashes.insert(2, block_2.clone()); - extras.transaction_hashes.insert(1, vec![tx_1.clone()]); - extras.transaction_hashes.insert(2, vec![tx_2.clone()]); - - let tracedb = TraceDB::new(config, db.clone(), Arc::new(extras)); - - // import block 1 - let request = create_simple_import_request(1, block_1.clone()); - let mut batch = DBTransaction::new(); - tracedb.import(&mut batch, request); - db.key_value().write(batch).unwrap(); - - let filter = Filter { - range: (1..1), - from_address: AddressesFilter::from(vec![Address::from(1)]), - to_address: AddressesFilter::from(vec![]), - }; - - let traces = tracedb.filter(&filter); - assert_eq!(traces.len(), 1); - assert_eq!(traces[0], create_simple_localized_trace(1, block_1.clone(), tx_1.clone())); - - // import block 2 - let request = create_simple_import_request(2, block_2.clone()); - let mut batch = DBTransaction::new(); - tracedb.import(&mut batch, request); - db.key_value().write(batch).unwrap(); - - let filter = Filter { - range: (1..2), - from_address: AddressesFilter::from(vec![Address::from(1)]), - to_address: AddressesFilter::from(vec![]), - }; - - let traces = tracedb.filter(&filter); - assert_eq!(traces.len(), 2); - assert_eq!(traces[0], create_simple_localized_trace(1, block_1.clone(), tx_1.clone())); - assert_eq!(traces[1], create_simple_localized_trace(2, block_2.clone(), tx_2.clone())); - - assert!(tracedb.block_traces(0).is_some(), "Genesis trace should be always present."); - - let traces = tracedb.block_traces(1).unwrap(); - assert_eq!(traces.len(), 1); - assert_eq!(traces[0], create_simple_localized_trace(1, block_1.clone(), tx_1.clone())); - - let traces = tracedb.block_traces(2).unwrap(); - assert_eq!(traces.len(), 1); - assert_eq!(traces[0], create_simple_localized_trace(2, block_2.clone(), tx_2.clone())); - - assert_eq!(None, tracedb.block_traces(3)); - - let traces = tracedb.transaction_traces(1, 0).unwrap(); - assert_eq!(traces.len(), 1); - assert_eq!(traces[0], create_simple_localized_trace(1, block_1.clone(), tx_1.clone())); - - let traces = tracedb.transaction_traces(2, 0).unwrap(); - assert_eq!(traces.len(), 1); - assert_eq!(traces[0], create_simple_localized_trace(2, block_2.clone(), tx_2.clone())); - - assert_eq!(None, tracedb.transaction_traces(2, 1)); - - assert_eq!(tracedb.trace(1, 0, vec![]).unwrap(), create_simple_localized_trace(1, block_1.clone(), tx_1.clone())); - assert_eq!(tracedb.trace(2, 0, vec![]).unwrap(), create_simple_localized_trace(2, block_2.clone(), tx_2.clone())); - } - - #[test] - fn query_trace_after_reopen() { - let db = new_db(); - let mut config = Config::default(); - let mut extras = Extras::default(); - let block_0 = H256::from(0xa1); - let tx_0 = H256::from(0xff); - - extras.block_hashes.insert(0, H256::default()); - extras.transaction_hashes.insert(0, vec![]); - extras.block_hashes.insert(1, block_0.clone()); - extras.transaction_hashes.insert(1, vec![tx_0.clone()]); - - // set tracing on - config.enabled = true; - - { - let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(extras.clone())); - - // import block 1 - let request = create_simple_import_request(1, block_0.clone()); - let mut batch = DBTransaction::new(); - tracedb.import(&mut batch, request); - db.key_value().write(batch).unwrap(); - } - - { - let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(extras)); - let traces = tracedb.transaction_traces(1, 0); - assert_eq!(traces.unwrap(), vec![create_simple_localized_trace(1, block_0, tx_0)]); - } - } - - #[test] - fn query_genesis() { - let db = new_db(); - let mut config = Config::default(); - let mut extras = Extras::default(); - let block_0 = H256::from(0xa1); - - extras.block_hashes.insert(0, block_0.clone()); - extras.transaction_hashes.insert(0, vec![]); - - // set tracing on - config.enabled = true; - - let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(extras.clone())); - let traces = tracedb.block_traces(0).unwrap(); - - assert_eq!(traces.len(), 0); - } + use ethereum_types::{Address, H256, U256}; + use evm::CallType; + use kvdb::DBTransaction; + use std::{collections::HashMap, sync::Arc}; + use test_helpers::new_db; + use trace::{ + flat::{FlatBlockTraces, FlatTrace, FlatTransactionTraces}, + trace::{Action, Call, Res}, + AddressesFilter, Config, Database as TraceDatabase, DatabaseExtras, Filter, ImportRequest, + LocalizedTrace, TraceDB, TraceError, + }; + use types::BlockNumber; + + struct NoopExtras; + + impl DatabaseExtras for NoopExtras { + fn block_hash(&self, block_number: BlockNumber) -> Option { + if block_number == 0 { + Some(H256::default()) + } else { + unimplemented!() + } + } + + fn transaction_hash( + &self, + _block_number: BlockNumber, + _tx_position: usize, + ) -> Option { + unimplemented!(); + } + } + + #[derive(Clone)] + struct Extras { + block_hashes: HashMap, + transaction_hashes: HashMap>, + } + + impl Default for Extras { + fn default() -> Self { + Extras { + block_hashes: HashMap::new(), + transaction_hashes: HashMap::new(), + } + } + } + + impl DatabaseExtras for Extras { + fn block_hash(&self, block_number: BlockNumber) -> Option { + self.block_hashes.get(&block_number).cloned() + } + + fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option { + self.transaction_hashes + .get(&block_number) + .and_then(|hashes| hashes.iter().cloned().nth(tx_position)) + } + } + + #[test] + fn test_reopening_db_with_tracing_off() { + let db = new_db(); + let mut config = Config::default(); + + // set autotracing + config.enabled = false; + + { + let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(NoopExtras)); + assert_eq!(tracedb.tracing_enabled(), false); + } + } + + #[test] + fn test_reopening_db_with_tracing_on() { + let db = new_db(); + let mut config = Config::default(); + + // set tracing on + config.enabled = true; + + { + let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(NoopExtras)); + assert_eq!(tracedb.tracing_enabled(), true); + } + } + + fn create_simple_import_request(block_number: BlockNumber, block_hash: H256) -> ImportRequest { + ImportRequest { + traces: FlatBlockTraces::from(vec![FlatTransactionTraces::from(vec![FlatTrace { + trace_address: Default::default(), + subtraces: 0, + action: Action::Call(Call { + from: 1.into(), + to: 2.into(), + value: 3.into(), + gas: 4.into(), + input: vec![], + call_type: CallType::Call, + }), + result: Res::FailedCall(TraceError::OutOfGas), + }])]), + block_hash: block_hash.clone(), + block_number: block_number, + enacted: vec![block_hash], + retracted: 0, + } + } + + fn create_noncanon_import_request( + block_number: BlockNumber, + block_hash: H256, + ) -> ImportRequest { + ImportRequest { + traces: FlatBlockTraces::from(vec![FlatTransactionTraces::from(vec![FlatTrace { + trace_address: Default::default(), + subtraces: 0, + action: Action::Call(Call { + from: 1.into(), + to: 2.into(), + value: 3.into(), + gas: 4.into(), + input: vec![], + call_type: CallType::Call, + }), + result: Res::FailedCall(TraceError::OutOfGas), + }])]), + block_hash: block_hash.clone(), + block_number: block_number, + enacted: vec![], + retracted: 0, + } + } + + fn create_simple_localized_trace( + block_number: BlockNumber, + block_hash: H256, + tx_hash: H256, + ) -> LocalizedTrace { + LocalizedTrace { + action: Action::Call(Call { + from: Address::from(1), + to: Address::from(2), + value: U256::from(3), + gas: U256::from(4), + input: vec![], + call_type: CallType::Call, + }), + result: Res::FailedCall(TraceError::OutOfGas), + trace_address: vec![], + subtraces: 0, + transaction_number: Some(0), + transaction_hash: Some(tx_hash), + block_number: block_number, + block_hash: block_hash, + } + } + + #[test] + fn test_import_non_canon_traces() { + let db = new_db(); + let mut config = Config::default(); + config.enabled = true; + let block_0 = H256::from(0xa1); + let block_1 = H256::from(0xa2); + let tx_0 = H256::from(0xff); + let tx_1 = H256::from(0xaf); + + let mut extras = Extras::default(); + extras.block_hashes.insert(0, block_0.clone()); + extras.block_hashes.insert(1, block_1.clone()); + extras.transaction_hashes.insert(0, vec![tx_0.clone()]); + extras.transaction_hashes.insert(1, vec![tx_1.clone()]); + + let tracedb = TraceDB::new(config, db.clone(), Arc::new(extras)); + + // import block 0 + let request = create_noncanon_import_request(0, block_0.clone()); + let mut batch = DBTransaction::new(); + tracedb.import(&mut batch, request); + db.key_value().write(batch).unwrap(); + + assert!( + tracedb.traces(&block_0).is_some(), + "Traces should be available even if block is non-canon." + ); + } + + #[test] + fn test_import() { + let db = new_db(); + let mut config = Config::default(); + config.enabled = true; + let block_1 = H256::from(0xa1); + let block_2 = H256::from(0xa2); + let tx_1 = H256::from(0xff); + let tx_2 = H256::from(0xaf); + + let mut extras = Extras::default(); + extras.block_hashes.insert(0, H256::default()); + + extras.block_hashes.insert(1, block_1.clone()); + extras.block_hashes.insert(2, block_2.clone()); + extras.transaction_hashes.insert(1, vec![tx_1.clone()]); + extras.transaction_hashes.insert(2, vec![tx_2.clone()]); + + let tracedb = TraceDB::new(config, db.clone(), Arc::new(extras)); + + // import block 1 + let request = create_simple_import_request(1, block_1.clone()); + let mut batch = DBTransaction::new(); + tracedb.import(&mut batch, request); + db.key_value().write(batch).unwrap(); + + let filter = Filter { + range: (1..1), + from_address: AddressesFilter::from(vec![Address::from(1)]), + to_address: AddressesFilter::from(vec![]), + }; + + let traces = tracedb.filter(&filter); + assert_eq!(traces.len(), 1); + assert_eq!( + traces[0], + create_simple_localized_trace(1, block_1.clone(), tx_1.clone()) + ); + + // import block 2 + let request = create_simple_import_request(2, block_2.clone()); + let mut batch = DBTransaction::new(); + tracedb.import(&mut batch, request); + db.key_value().write(batch).unwrap(); + + let filter = Filter { + range: (1..2), + from_address: AddressesFilter::from(vec![Address::from(1)]), + to_address: AddressesFilter::from(vec![]), + }; + + let traces = tracedb.filter(&filter); + assert_eq!(traces.len(), 2); + assert_eq!( + traces[0], + create_simple_localized_trace(1, block_1.clone(), tx_1.clone()) + ); + assert_eq!( + traces[1], + create_simple_localized_trace(2, block_2.clone(), tx_2.clone()) + ); + + assert!( + tracedb.block_traces(0).is_some(), + "Genesis trace should be always present." + ); + + let traces = tracedb.block_traces(1).unwrap(); + assert_eq!(traces.len(), 1); + assert_eq!( + traces[0], + create_simple_localized_trace(1, block_1.clone(), tx_1.clone()) + ); + + let traces = tracedb.block_traces(2).unwrap(); + assert_eq!(traces.len(), 1); + assert_eq!( + traces[0], + create_simple_localized_trace(2, block_2.clone(), tx_2.clone()) + ); + + assert_eq!(None, tracedb.block_traces(3)); + + let traces = tracedb.transaction_traces(1, 0).unwrap(); + assert_eq!(traces.len(), 1); + assert_eq!( + traces[0], + create_simple_localized_trace(1, block_1.clone(), tx_1.clone()) + ); + + let traces = tracedb.transaction_traces(2, 0).unwrap(); + assert_eq!(traces.len(), 1); + assert_eq!( + traces[0], + create_simple_localized_trace(2, block_2.clone(), tx_2.clone()) + ); + + assert_eq!(None, tracedb.transaction_traces(2, 1)); + + assert_eq!( + tracedb.trace(1, 0, vec![]).unwrap(), + create_simple_localized_trace(1, block_1.clone(), tx_1.clone()) + ); + assert_eq!( + tracedb.trace(2, 0, vec![]).unwrap(), + create_simple_localized_trace(2, block_2.clone(), tx_2.clone()) + ); + } + + #[test] + fn query_trace_after_reopen() { + let db = new_db(); + let mut config = Config::default(); + let mut extras = Extras::default(); + let block_0 = H256::from(0xa1); + let tx_0 = H256::from(0xff); + + extras.block_hashes.insert(0, H256::default()); + extras.transaction_hashes.insert(0, vec![]); + extras.block_hashes.insert(1, block_0.clone()); + extras.transaction_hashes.insert(1, vec![tx_0.clone()]); + + // set tracing on + config.enabled = true; + + { + let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(extras.clone())); + + // import block 1 + let request = create_simple_import_request(1, block_0.clone()); + let mut batch = DBTransaction::new(); + tracedb.import(&mut batch, request); + db.key_value().write(batch).unwrap(); + } + + { + let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(extras)); + let traces = tracedb.transaction_traces(1, 0); + assert_eq!( + traces.unwrap(), + vec![create_simple_localized_trace(1, block_0, tx_0)] + ); + } + } + + #[test] + fn query_genesis() { + let db = new_db(); + let mut config = Config::default(); + let mut extras = Extras::default(); + let block_0 = H256::from(0xa1); + + extras.block_hashes.insert(0, block_0.clone()); + extras.transaction_hashes.insert(0, vec![]); + + // set tracing on + config.enabled = true; + + let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(extras.clone())); + let traces = tracedb.block_traces(0).unwrap(); + + assert_eq!(traces.len(), 0); + } } diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs index 015deaea038..3cb7f2cd4f0 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/src/trace/executive_tracer.rs @@ -1,307 +1,364 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Simple executive tracer. -use ethereum_types::{U256, Address}; -use vm::{Error as VmError, ActionParams}; -use trace::trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide, Reward, RewardType}; -use trace::{Tracer, VMTracer, FlatTrace}; +use ethereum_types::{Address, U256}; +use log::{debug, warn}; +use std::cmp::min; +use trace::{ + trace::{ + Action, Call, CallResult, Create, CreateResult, MemoryDiff, Res, Reward, RewardType, + StorageDiff, Suicide, VMExecutedOperation, VMOperation, VMTrace, + }, + FlatTrace, Tracer, VMTracer, +}; +use vm::{ActionParams, Error as VmError}; /// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls. #[derive(Default)] pub struct ExecutiveTracer { - traces: Vec, - index_stack: Vec, - vecindex_stack: Vec, - sublen_stack: Vec, - skip_one: bool, + traces: Vec, + index_stack: Vec, + vecindex_stack: Vec, + sublen_stack: Vec, + skip_one: bool, } impl Tracer for ExecutiveTracer { - type Output = FlatTrace; - - fn prepare_trace_call(&mut self, params: &ActionParams, depth: usize, is_builtin: bool) { - assert!(!self.skip_one, "skip_one is used only for builtin contracts that do not have subsequent calls; in prepare_trace_call it cannot be true; qed"); - - if depth != 0 && is_builtin && params.value.value() == U256::zero() { - self.skip_one = true; - return; - } - - if let Some(parentlen) = self.sublen_stack.last_mut() { - *parentlen += 1; - } - - let trace = FlatTrace { - trace_address: self.index_stack.clone(), - subtraces: self.sublen_stack.last().cloned().unwrap_or(0), - action: Action::Call(Call::from(params.clone())), - result: Res::Call(CallResult { - gas_used: U256::zero(), - output: Vec::new() - }), - }; - self.vecindex_stack.push(self.traces.len()); - self.traces.push(trace); - self.index_stack.push(0); - self.sublen_stack.push(0); - } - - fn prepare_trace_create(&mut self, params: &ActionParams) { - assert!(!self.skip_one, "skip_one is used only for builtin contracts that do not have subsequent calls; in prepare_trace_create it cannot be true; qed"); - - if let Some(parentlen) = self.sublen_stack.last_mut() { - *parentlen += 1; - } - - let trace = FlatTrace { - trace_address: self.index_stack.clone(), - subtraces: self.sublen_stack.last().cloned().unwrap_or(0), - action: Action::Create(Create::from(params.clone())), - result: Res::Create(CreateResult { - gas_used: U256::zero(), - code: Vec::new(), - address: Address::default(), - }), - }; - self.vecindex_stack.push(self.traces.len()); - self.traces.push(trace); - self.index_stack.push(0); - self.sublen_stack.push(0); - } - - fn done_trace_call(&mut self, gas_used: U256, output: &[u8]) { - if self.skip_one { - self.skip_one = false; - return; - } - - let vecindex = self.vecindex_stack.pop().expect("Executive invoked prepare_trace_call before this function; vecindex_stack is never empty; qed"); - let sublen = self.sublen_stack.pop().expect("Executive invoked prepare_trace_call before this function; sublen_stack is never empty; qed"); - self.index_stack.pop(); - - self.traces[vecindex].result = Res::Call(CallResult { - gas_used, - output: output.into(), - }); - self.traces[vecindex].subtraces = sublen; - - if let Some(index) = self.index_stack.last_mut() { - *index += 1; - } - } - - fn done_trace_create(&mut self, gas_used: U256, code: &[u8], address: Address) { - assert!(!self.skip_one, "skip_one is only set with prepare_trace_call for builtin contracts with no subsequent calls; skip_one cannot be true after the same level prepare_trace_create; qed"); - - let vecindex = self.vecindex_stack.pop().expect("Executive invoked prepare_trace_create before this function; vecindex_stack is never empty; qed"); - let sublen = self.sublen_stack.pop().expect("Executive invoked prepare_trace_create before this function; sublen_stack is never empty; qed"); - self.index_stack.pop(); - - self.traces[vecindex].result = Res::Create(CreateResult { - gas_used, address, - code: code.into(), - }); - self.traces[vecindex].subtraces = sublen; - - if let Some(index) = self.index_stack.last_mut() { - *index += 1; - } - } - - fn done_trace_failed(&mut self, error: &VmError) { - if self.skip_one { - self.skip_one = false; - return; - } - - let vecindex = self.vecindex_stack.pop().expect("Executive invoked prepare_trace_create/call before this function; vecindex_stack is never empty; qed"); - let sublen = self.sublen_stack.pop().expect("Executive invoked prepare_trace_create/call before this function; vecindex_stack is never empty; qed"); - self.index_stack.pop(); - - let is_create = match self.traces[vecindex].action { - Action::Create(_) => true, - _ => false, - }; - - if is_create { - self.traces[vecindex].result = Res::FailedCreate(error.into()); - } else { - self.traces[vecindex].result = Res::FailedCall(error.into()); - } - self.traces[vecindex].subtraces = sublen; - - if let Some(index) = self.index_stack.last_mut() { - *index += 1; - } - } - - fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address) { - if let Some(parentlen) = self.sublen_stack.last_mut() { - *parentlen += 1; - } - - let trace = FlatTrace { - subtraces: 0, - action: Action::Suicide(Suicide { address, refund_address, balance } ), - result: Res::None, - trace_address: self.index_stack.clone(), - }; - debug!(target: "trace", "Traced suicide {:?}", trace); - self.traces.push(trace); - - if let Some(index) = self.index_stack.last_mut() { - *index += 1; - } - } - - fn trace_reward(&mut self, author: Address, value: U256, reward_type: RewardType) { - if let Some(parentlen) = self.sublen_stack.last_mut() { - *parentlen += 1; - } - - let trace = FlatTrace { - subtraces: 0, - action: Action::Reward(Reward { author, value, reward_type } ), - result: Res::None, - trace_address: self.index_stack.clone(), - }; - debug!(target: "trace", "Traced reward {:?}", trace); - self.traces.push(trace); - - if let Some(index) = self.index_stack.last_mut() { - *index += 1; - } - } - - fn drain(self) -> Vec { - self.traces - } + type Output = FlatTrace; + + fn prepare_trace_call(&mut self, params: &ActionParams, depth: usize, is_builtin: bool) { + assert!(!self.skip_one, "skip_one is used only for builtin contracts that do not have subsequent calls; in prepare_trace_call it cannot be true; qed"); + + if depth != 0 && is_builtin && params.value.value() == U256::zero() { + self.skip_one = true; + return; + } + + if let Some(parentlen) = self.sublen_stack.last_mut() { + *parentlen += 1; + } + + let trace = FlatTrace { + trace_address: self.index_stack.clone(), + subtraces: self.sublen_stack.last().cloned().unwrap_or(0), + action: Action::Call(Call::from(params.clone())), + result: Res::Call(CallResult { + gas_used: U256::zero(), + output: Vec::new(), + }), + }; + self.vecindex_stack.push(self.traces.len()); + self.traces.push(trace); + self.index_stack.push(0); + self.sublen_stack.push(0); + } + + fn prepare_trace_create(&mut self, params: &ActionParams) { + assert!(!self.skip_one, "skip_one is used only for builtin contracts that do not have subsequent calls; in prepare_trace_create it cannot be true; qed"); + + if let Some(parentlen) = self.sublen_stack.last_mut() { + *parentlen += 1; + } + + let trace = FlatTrace { + trace_address: self.index_stack.clone(), + subtraces: self.sublen_stack.last().cloned().unwrap_or(0), + action: Action::Create(Create::from(params.clone())), + result: Res::Create(CreateResult { + gas_used: U256::zero(), + code: Vec::new(), + address: Address::default(), + }), + }; + self.vecindex_stack.push(self.traces.len()); + self.traces.push(trace); + self.index_stack.push(0); + self.sublen_stack.push(0); + } + + fn done_trace_call(&mut self, gas_used: U256, output: &[u8]) { + if self.skip_one { + self.skip_one = false; + return; + } + + let vecindex = self.vecindex_stack.pop().expect("Executive invoked prepare_trace_call before this function; vecindex_stack is never empty; qed"); + let sublen = self.sublen_stack.pop().expect("Executive invoked prepare_trace_call before this function; sublen_stack is never empty; qed"); + self.index_stack.pop(); + + self.traces[vecindex].result = Res::Call(CallResult { + gas_used, + output: output.into(), + }); + self.traces[vecindex].subtraces = sublen; + + if let Some(index) = self.index_stack.last_mut() { + *index += 1; + } + } + + fn done_trace_create(&mut self, gas_used: U256, code: &[u8], address: Address) { + assert!(!self.skip_one, "skip_one is only set with prepare_trace_call for builtin contracts with no subsequent calls; skip_one cannot be true after the same level prepare_trace_create; qed"); + + let vecindex = self.vecindex_stack.pop().expect("Executive invoked prepare_trace_create before this function; vecindex_stack is never empty; qed"); + let sublen = self.sublen_stack.pop().expect("Executive invoked prepare_trace_create before this function; sublen_stack is never empty; qed"); + self.index_stack.pop(); + + self.traces[vecindex].result = Res::Create(CreateResult { + gas_used, + address, + code: code.into(), + }); + self.traces[vecindex].subtraces = sublen; + + if let Some(index) = self.index_stack.last_mut() { + *index += 1; + } + } + + fn done_trace_failed(&mut self, error: &VmError) { + if self.skip_one { + self.skip_one = false; + return; + } + + let vecindex = self.vecindex_stack.pop().expect("Executive invoked prepare_trace_create/call before this function; vecindex_stack is never empty; qed"); + let sublen = self.sublen_stack.pop().expect("Executive invoked prepare_trace_create/call before this function; vecindex_stack is never empty; qed"); + self.index_stack.pop(); + + let is_create = match self.traces[vecindex].action { + Action::Create(_) => true, + _ => false, + }; + + if is_create { + self.traces[vecindex].result = Res::FailedCreate(error.into()); + } else { + self.traces[vecindex].result = Res::FailedCall(error.into()); + } + self.traces[vecindex].subtraces = sublen; + + if let Some(index) = self.index_stack.last_mut() { + *index += 1; + } + } + + fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address) { + if let Some(parentlen) = self.sublen_stack.last_mut() { + *parentlen += 1; + } + + let trace = FlatTrace { + subtraces: 0, + action: Action::Suicide(Suicide { + address, + refund_address, + balance, + }), + result: Res::None, + trace_address: self.index_stack.clone(), + }; + debug!(target: "trace", "Traced suicide {:?}", trace); + self.traces.push(trace); + + if let Some(index) = self.index_stack.last_mut() { + *index += 1; + } + } + + fn trace_reward(&mut self, author: Address, value: U256, reward_type: RewardType) { + if let Some(parentlen) = self.sublen_stack.last_mut() { + *parentlen += 1; + } + + let trace = FlatTrace { + subtraces: 0, + action: Action::Reward(Reward { + author, + value, + reward_type, + }), + result: Res::None, + trace_address: self.index_stack.clone(), + }; + debug!(target: "trace", "Traced reward {:?}", trace); + self.traces.push(trace); + + if let Some(index) = self.index_stack.last_mut() { + *index += 1; + } + } + + fn drain(self) -> Vec { + self.traces + } +} + +struct TraceData { + mem_written: Option<(usize, usize)>, + store_written: Option<(U256, U256)>, } /// Simple VM tracer. Traces all operations. pub struct ExecutiveVMTracer { - data: VMTrace, - depth: usize, - last_mem_written: Option<(usize, usize)>, - last_store_written: Option<(U256, U256)>, + data: VMTrace, + depth: usize, + trace_stack: Vec, } impl ExecutiveVMTracer { - /// Create a new top-level instance. - pub fn toplevel() -> Self { - ExecutiveVMTracer { - data: VMTrace { - parent_step: 0, - code: vec![], - operations: vec![Default::default()], // prefill with a single entry so that prepare_subtrace can get the parent_step - subs: vec![], - }, - depth: 0, - last_mem_written: None, - last_store_written: None, - } - } - - fn with_trace_in_depth(trace: &mut VMTrace, depth: usize, f: F) { - if depth == 0 { - f(trace); - } else { - Self::with_trace_in_depth(trace.subs.last_mut().expect("self.depth is incremented with prepare_subtrace; a subtrace is always pushed; self.depth cannot be greater than subtrace stack; qed"), depth - 1, f); - } - } + /// Create a new top-level instance. + pub fn toplevel() -> Self { + ExecutiveVMTracer { + data: VMTrace { + parent_step: 0, + code: vec![], + operations: vec![Default::default()], // prefill with a single entry so that prepare_subtrace can get the parent_step + subs: vec![], + }, + depth: 0, + trace_stack: vec![], + } + } + + fn with_trace_in_depth(trace: &mut VMTrace, depth: usize, f: F) { + if depth == 0 { + f(trace); + } else { + Self::with_trace_in_depth(trace.subs.last_mut().expect("self.depth is incremented with prepare_subtrace; a subtrace is always pushed; self.depth cannot be greater than subtrace stack; qed"), depth - 1, f); + } + } } impl VMTracer for ExecutiveVMTracer { - type Output = VMTrace; - - fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool { true } - - fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: U256, mem_written: Option<(usize, usize)>, store_written: Option<(U256, U256)>) { - Self::with_trace_in_depth(&mut self.data, self.depth, move |trace| { - trace.operations.push(VMOperation { - pc: pc, - instruction: instruction, - gas_cost: gas_cost, - executed: None, - }); - }); - self.last_mem_written = mem_written; - self.last_store_written = store_written; - } - - fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) { - let mem_diff = self.last_mem_written.take().map(|(o, s)| (o, &(mem[o..o+s]))); - let store_diff = self.last_store_written.take(); - Self::with_trace_in_depth(&mut self.data, self.depth, move |trace| { - let ex = VMExecutedOperation { - gas_used: gas_used, - stack_push: stack_push.iter().cloned().collect(), - mem_diff: mem_diff.map(|(s, r)| MemoryDiff { offset: s, data: r.iter().cloned().collect() }), - store_diff: store_diff.map(|(l, v)| StorageDiff { location: l, value: v }), - }; - trace.operations.last_mut().expect("trace_executed is always called after a trace_prepare_execute; trace.operations cannot be empty; qed").executed = Some(ex); - }); - } - - fn prepare_subtrace(&mut self, code: &[u8]) { - Self::with_trace_in_depth(&mut self.data, self.depth, move |trace| { - let parent_step = trace.operations.len() - 1; // won't overflow since we must already have pushed an operation in trace_prepare_execute. - trace.subs.push(VMTrace { - parent_step, - code: code.to_vec(), - operations: vec![], - subs: vec![], - }); - }); - self.depth += 1; - } - - fn done_subtrace(&mut self) { - self.depth -= 1; - } - - fn drain(mut self) -> Option { self.data.subs.pop() } + type Output = VMTrace; + + fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool { + true + } + + fn trace_prepare_execute( + &mut self, + pc: usize, + instruction: u8, + gas_cost: U256, + mem_written: Option<(usize, usize)>, + store_written: Option<(U256, U256)>, + ) { + Self::with_trace_in_depth(&mut self.data, self.depth, move |trace| { + trace.operations.push(VMOperation { + pc: pc, + instruction: instruction, + gas_cost: gas_cost, + executed: None, + }); + }); + self.trace_stack.push(TraceData { + mem_written, + store_written, + }); + } + + fn trace_failed(&mut self) { + let _ = self + .trace_stack + .pop() + .expect("pushed in trace_prepare_execute; qed"); + } + + fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) { + let TraceData { + mem_written, + store_written, + } = self + .trace_stack + .pop() + .expect("pushed in trace_prepare_execute; qed"); + let mem_diff = mem_written.map(|(o, s)| { + if o + s > mem.len() { + warn!(target: "trace", "mem_written is out of bounds"); + } + (o, &mem[min(mem.len(), o)..min(o + s, mem.len())]) + }); + let store_diff = store_written; + Self::with_trace_in_depth(&mut self.data, self.depth, move |trace| { + let ex = VMExecutedOperation { + gas_used: gas_used, + stack_push: stack_push.to_vec(), + mem_diff: mem_diff.map(|(s, r)| MemoryDiff { + offset: s, + data: r.to_vec(), + }), + store_diff: store_diff.map(|(l, v)| StorageDiff { + location: l, + value: v, + }), + }; + trace.operations.last_mut().expect("trace_executed is always called after a trace_prepare_execute; trace.operations cannot be empty; qed").executed = Some(ex); + }); + } + + fn prepare_subtrace(&mut self, code: &[u8]) { + Self::with_trace_in_depth(&mut self.data, self.depth, move |trace| { + let parent_step = trace.operations.len() - 1; // won't overflow since we must already have pushed an operation in trace_prepare_execute. + trace.subs.push(VMTrace { + parent_step, + code: code.to_vec(), + operations: vec![], + subs: vec![], + }); + }); + self.depth += 1; + } + + fn done_subtrace(&mut self) { + self.depth -= 1; + } + + fn drain(mut self) -> Option { + self.data.subs.pop() + } } #[cfg(test)] mod tests { - use super::*; - - #[test] - fn should_prefix_address_properly() { - let mut tracer = ExecutiveTracer::default(); - - tracer.prepare_trace_call(&ActionParams::default(), 0, false); - tracer.prepare_trace_call(&ActionParams::default(), 1, false); - tracer.prepare_trace_call(&ActionParams::default(), 2, false); - tracer.done_trace_call(U256::zero(), &[]); - tracer.prepare_trace_call(&ActionParams::default(), 2, false); - tracer.done_trace_call(U256::zero(), &[]); - tracer.prepare_trace_call(&ActionParams::default(), 2, false); - tracer.done_trace_call(U256::zero(), &[]); - tracer.done_trace_call(U256::zero(), &[]); - tracer.done_trace_call(U256::zero(), &[]); - - let drained = tracer.drain(); - assert!(drained[0].trace_address.len() == 0); - assert_eq!(&drained[1].trace_address, &[0]); - assert_eq!(&drained[2].trace_address, &[0, 0]); - assert_eq!(&drained[3].trace_address, &[0, 1]); - assert_eq!(&drained[4].trace_address, &[0, 2]); - } + use super::*; + + #[test] + fn should_prefix_address_properly() { + let mut tracer = ExecutiveTracer::default(); + + tracer.prepare_trace_call(&ActionParams::default(), 0, false); + tracer.prepare_trace_call(&ActionParams::default(), 1, false); + tracer.prepare_trace_call(&ActionParams::default(), 2, false); + tracer.done_trace_call(U256::zero(), &[]); + tracer.prepare_trace_call(&ActionParams::default(), 2, false); + tracer.done_trace_call(U256::zero(), &[]); + tracer.prepare_trace_call(&ActionParams::default(), 2, false); + tracer.done_trace_call(U256::zero(), &[]); + tracer.done_trace_call(U256::zero(), &[]); + tracer.done_trace_call(U256::zero(), &[]); + + let drained = tracer.drain(); + assert!(drained[0].trace_address.len() == 0); + assert_eq!(&drained[1].trace_address, &[0]); + assert_eq!(&drained[2].trace_address, &[0, 0]); + assert_eq!(&drained[3].trace_address, &[0, 1]); + assert_eq!(&drained[4].trace_address, &[0, 2]); + } } diff --git a/ethcore/src/trace/import.rs b/ethcore/src/trace/import.rs index e9ec9c77bad..aba9b2b0910 100644 --- a/ethcore/src/trace/import.rs +++ b/ethcore/src/trace/import.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Traces import request. use ethereum_types::H256; @@ -22,16 +22,16 @@ use trace::FlatBlockTraces; /// Traces import request. pub struct ImportRequest { - /// Traces to import. - pub traces: FlatBlockTraces, - /// Hash of traces block. - pub block_hash: H256, - /// Number of traces block. - pub block_number: BlockNumber, - /// Blocks enacted by this import. - /// - /// They should be ordered from oldest to newest. - pub enacted: Vec, - /// Number of blocks retracted by this import. - pub retracted: usize, + /// Traces to import. + pub traces: FlatBlockTraces, + /// Hash of traces block. + pub block_hash: H256, + /// Number of traces block. + pub block_number: BlockNumber, + /// Blocks enacted by this import. + /// + /// They should be ordered from oldest to newest. + pub enacted: Vec, + /// Number of blocks retracted by this import. + pub retracted: usize, } diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index d3586614754..036a5245f11 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Tracing @@ -23,109 +23,136 @@ mod import; mod noop_tracer; mod types; -pub use self::config::Config; -pub use self::db::TraceDB; -pub use self::noop_tracer::{NoopTracer, NoopVMTracer}; -pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer}; -pub use self::import::ImportRequest; -pub use self::localized::LocalizedTrace; - -pub use self::types::{filter, flat, localized, trace, Tracing}; -pub use self::types::error::Error as TraceError; -pub use self::types::trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, RewardType}; -pub use self::types::flat::{FlatTrace, FlatTransactionTraces, FlatBlockTraces}; -pub use self::types::filter::{Filter, AddressesFilter}; - -use ethereum_types::{H256, U256, Address}; +pub use self::{ + config::Config, + db::TraceDB, + executive_tracer::{ExecutiveTracer, ExecutiveVMTracer}, + import::ImportRequest, + localized::LocalizedTrace, + noop_tracer::{NoopTracer, NoopVMTracer}, +}; + +pub use self::types::{ + error::Error as TraceError, + filter, + filter::{AddressesFilter, Filter}, + flat, + flat::{FlatBlockTraces, FlatTrace, FlatTransactionTraces}, + localized, trace, + trace::{MemoryDiff, RewardType, StorageDiff, VMExecutedOperation, VMOperation, VMTrace}, + Tracing, +}; + +use ethereum_types::{Address, H256, U256}; use kvdb::DBTransaction; -use vm::{Error as VmError, ActionParams}; use types::BlockNumber; +use vm::{ActionParams, Error as VmError}; /// This trait is used by executive to build traces. pub trait Tracer: Send { - /// Data returned when draining the Tracer. - type Output; + /// Data returned when draining the Tracer. + type Output; - /// Prepares call trace for given params. Would panic if prepare/done_trace are not balanced. - fn prepare_trace_call(&mut self, params: &ActionParams, depth: usize, is_builtin: bool); + /// Prepares call trace for given params. Would panic if prepare/done_trace are not balanced. + fn prepare_trace_call(&mut self, params: &ActionParams, depth: usize, is_builtin: bool); - /// Prepares create trace for given params. Would panic if prepare/done_trace are not balanced. - fn prepare_trace_create(&mut self, params: &ActionParams); + /// Prepares create trace for given params. Would panic if prepare/done_trace are not balanced. + fn prepare_trace_create(&mut self, params: &ActionParams); - /// Finishes a successful call trace. Would panic if prepare/done_trace are not balanced. - fn done_trace_call(&mut self, gas_used: U256, output: &[u8]); + /// Finishes a successful call trace. Would panic if prepare/done_trace are not balanced. + fn done_trace_call(&mut self, gas_used: U256, output: &[u8]); - /// Finishes a successful create trace. Would panic if prepare/done_trace are not balanced. - fn done_trace_create(&mut self, gas_used: U256, code: &[u8], address: Address); + /// Finishes a successful create trace. Would panic if prepare/done_trace are not balanced. + fn done_trace_create(&mut self, gas_used: U256, code: &[u8], address: Address); - /// Finishes a failed trace. Would panic if prepare/done_trace are not balanced. - fn done_trace_failed(&mut self, error: &VmError); + /// Finishes a failed trace. Would panic if prepare/done_trace are not balanced. + fn done_trace_failed(&mut self, error: &VmError); - /// Stores suicide info. - fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address); + /// Stores suicide info. + fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address); - /// Stores reward info. - fn trace_reward(&mut self, author: Address, value: U256, reward_type: RewardType); + /// Stores reward info. + fn trace_reward(&mut self, author: Address, value: U256, reward_type: RewardType); - /// Consumes self and returns all traces. - fn drain(self) -> Vec; + /// Consumes self and returns all traces. + fn drain(self) -> Vec; } /// Used by executive to build VM traces. pub trait VMTracer: Send { - - /// Data returned when draining the VMTracer. - type Output; - - /// Trace the progression of interpreter to next instruction. - /// If tracer returns `false` it won't be called again. - /// @returns true if `trace_prepare_execute` and `trace_executed` should be called. - fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool { false } - - /// Trace the preparation to execute a single valid instruction. - fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256, _mem_written: Option<(usize, usize)>, _store_written: Option<(U256, U256)>) {} - - /// Trace the finalised execution of a single valid instruction. - fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem: &[u8]) {} - - /// Spawn subtracer which will be used to trace deeper levels of execution. - fn prepare_subtrace(&mut self, _code: &[u8]) {} - - /// Finalize subtracer. - fn done_subtrace(&mut self) {} - - /// Consumes self and returns the VM trace. - fn drain(self) -> Option; - + /// Data returned when draining the VMTracer. + type Output; + + /// Trace the progression of interpreter to next instruction. + /// If tracer returns `false` it won't be called again. + /// @returns true if `trace_prepare_execute` and `trace_executed` should be called. + fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool { + false + } + + /// Trace the preparation to execute a single valid instruction. + fn trace_prepare_execute( + &mut self, + _pc: usize, + _instruction: u8, + _gas_cost: U256, + _mem_written: Option<(usize, usize)>, + _store_written: Option<(U256, U256)>, + ) { + } + + /// Trace the execution failure of a single instruction. + fn trace_failed(&mut self) {} + + /// Trace the finalised execution of a single valid instruction. + fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem: &[u8]) {} + + /// Spawn subtracer which will be used to trace deeper levels of execution. + fn prepare_subtrace(&mut self, _code: &[u8]) {} + + /// Finalize subtracer. + fn done_subtrace(&mut self) {} + + /// Consumes self and returns the VM trace. + fn drain(self) -> Option; } /// `DbExtras` provides an interface to query extra data which is not stored in tracesdb, /// but necessary to work correctly. pub trait DatabaseExtras { - /// Returns hash of given block number. - fn block_hash(&self, block_number: BlockNumber) -> Option; + /// Returns hash of given block number. + fn block_hash(&self, block_number: BlockNumber) -> Option; - /// Returns hash of transaction at given position. - fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option; + /// Returns hash of transaction at given position. + fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option; } /// Db provides an interface to query tracesdb. pub trait Database { - /// Returns true if tracing is enabled. Otherwise false. - fn tracing_enabled(&self) -> bool; - - /// Imports new block traces. - fn import(&self, batch: &mut DBTransaction, request: ImportRequest); - - /// Returns localized trace at given position. - fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec) -> Option; - - /// Returns localized traces created by a single transaction. - fn transaction_traces(&self, block_number: BlockNumber, tx_position: usize) -> Option>; - - /// Returns localized traces created in given block. - fn block_traces(&self, block_number: BlockNumber) -> Option>; - - /// Filter traces matching given filter. - fn filter(&self, filter: &Filter) -> Vec; + /// Returns true if tracing is enabled. Otherwise false. + fn tracing_enabled(&self) -> bool; + + /// Imports new block traces. + fn import(&self, batch: &mut DBTransaction, request: ImportRequest); + + /// Returns localized trace at given position. + fn trace( + &self, + block_number: BlockNumber, + tx_position: usize, + trace_position: Vec, + ) -> Option; + + /// Returns localized traces created by a single transaction. + fn transaction_traces( + &self, + block_number: BlockNumber, + tx_position: usize, + ) -> Option>; + + /// Returns localized traces created in given block. + fn block_traces(&self, block_number: BlockNumber) -> Option>; + + /// Filter traces matching given filter. + fn filter(&self, filter: &Filter) -> Vec; } diff --git a/ethcore/src/trace/noop_tracer.rs b/ethcore/src/trace/noop_tracer.rs index 62cce8e0111..3944ecef827 100644 --- a/ethcore/src/trace/noop_tracer.rs +++ b/ethcore/src/trace/noop_tracer.rs @@ -1,47 +1,53 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Nonoperative tracer. -use ethereum_types::{U256, Address}; -use vm::{Error as VmError, ActionParams}; -use trace::{Tracer, VMTracer, FlatTrace}; -use trace::trace::{VMTrace, RewardType}; +use ethereum_types::{Address, U256}; +use trace::{ + trace::{RewardType, VMTrace}, + FlatTrace, Tracer, VMTracer, +}; +use vm::{ActionParams, Error as VmError}; /// Nonoperative tracer. Does not trace anything. pub struct NoopTracer; impl Tracer for NoopTracer { - type Output = FlatTrace; - - fn prepare_trace_call(&mut self, _: &ActionParams, _: usize, _: bool) { } - fn prepare_trace_create(&mut self, _: &ActionParams) { } - fn done_trace_call(&mut self, _: U256, _: &[u8]) { } - fn done_trace_create(&mut self, _: U256, _: &[u8], _: Address) { } - fn done_trace_failed(&mut self, _: &VmError) { } - fn trace_suicide(&mut self, _: Address, _: U256, _: Address) { } - fn trace_reward(&mut self, _: Address, _: U256, _: RewardType) { } - fn drain(self) -> Vec { vec![] } + type Output = FlatTrace; + + fn prepare_trace_call(&mut self, _: &ActionParams, _: usize, _: bool) {} + fn prepare_trace_create(&mut self, _: &ActionParams) {} + fn done_trace_call(&mut self, _: U256, _: &[u8]) {} + fn done_trace_create(&mut self, _: U256, _: &[u8], _: Address) {} + fn done_trace_failed(&mut self, _: &VmError) {} + fn trace_suicide(&mut self, _: Address, _: U256, _: Address) {} + fn trace_reward(&mut self, _: Address, _: U256, _: RewardType) {} + fn drain(self) -> Vec { + vec![] + } } /// Nonoperative VM tracer. Does not trace anything. pub struct NoopVMTracer; impl VMTracer for NoopVMTracer { - type Output = VMTrace; + type Output = VMTrace; - fn drain(self) -> Option { None } + fn drain(self) -> Option { + None + } } diff --git a/ethcore/src/trace/types/error.rs b/ethcore/src/trace/types/error.rs index 5c775dcb6ec..691e6cc397f 100644 --- a/ethcore/src/trace/types/error.rs +++ b/ethcore/src/trace/types/error.rs @@ -1,154 +1,172 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Trace errors. +use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; use std::fmt; -use rlp::{Encodable, RlpStream, Decodable, DecoderError, Rlp}; use vm::Error as VmError; /// Trace evm errors. #[derive(Debug, PartialEq, Clone)] pub enum Error { - /// `OutOfGas` is returned when transaction execution runs out of gas. - OutOfGas, - /// `BadJumpDestination` is returned when execution tried to move - /// to position that wasn't marked with JUMPDEST instruction - BadJumpDestination, - /// `BadInstructions` is returned when given instruction is not supported - BadInstruction, - /// `StackUnderflow` when there is not enough stack elements to execute instruction - StackUnderflow, - /// When execution would exceed defined Stack Limit - OutOfStack, - /// When builtin contract failed on input data - BuiltIn, - /// Returned on evm internal error. Should never be ignored during development. - /// Likely to cause consensus issues. - Internal, - /// When execution tries to modify the state in static context - MutableCallInStaticContext, - /// Wasm error - Wasm, - /// Contract tried to access past the return data buffer. - OutOfBounds, - /// Execution has been reverted with REVERT instruction. - Reverted, + /// `OutOfGas` is returned when transaction execution runs out of gas. + OutOfGas, + /// `BadJumpDestination` is returned when execution tried to move + /// to position that wasn't marked with JUMPDEST instruction + BadJumpDestination, + /// `BadInstructions` is returned when given instruction is not supported + BadInstruction, + /// `StackUnderflow` when there is not enough stack elements to execute instruction + StackUnderflow, + /// When execution would exceed defined Stack Limit + OutOfStack, + /// When there is not enough subroutine stack elements to return from + SubStackUnderflow, + /// When execution would exceed defined subroutine Stack Limit + OutOfSubStack, + /// When the code walks into a subroutine, that is not allowed + InvalidSubEntry, + /// When builtin contract failed on input data + BuiltIn, + /// Returned on evm internal error. Should never be ignored during development. + /// Likely to cause consensus issues. + Internal, + /// When execution tries to modify the state in static context + MutableCallInStaticContext, + /// Wasm error + Wasm, + /// Contract tried to access past the return data buffer. + OutOfBounds, + /// Execution has been reverted with REVERT instruction. + Reverted, } impl<'a> From<&'a VmError> for Error { - fn from(e: &'a VmError) -> Self { - match *e { - VmError::OutOfGas => Error::OutOfGas, - VmError::BadJumpDestination { .. } => Error::BadJumpDestination, - VmError::BadInstruction { .. } => Error::BadInstruction, - VmError::StackUnderflow { .. } => Error::StackUnderflow, - VmError::OutOfStack { .. } => Error::OutOfStack, - VmError::BuiltIn { .. } => Error::BuiltIn, - VmError::Wasm { .. } => Error::Wasm, - VmError::Internal(_) => Error::Internal, - VmError::MutableCallInStaticContext => Error::MutableCallInStaticContext, - VmError::OutOfBounds => Error::OutOfBounds, - VmError::Reverted => Error::Reverted, - } - } + fn from(e: &'a VmError) -> Self { + match *e { + VmError::OutOfGas => Error::OutOfGas, + VmError::BadJumpDestination { .. } => Error::BadJumpDestination, + VmError::BadInstruction { .. } => Error::BadInstruction, + VmError::StackUnderflow { .. } => Error::StackUnderflow, + VmError::OutOfStack { .. } => Error::OutOfStack, + VmError::SubStackUnderflow { .. } => Error::SubStackUnderflow, + VmError::OutOfSubStack { .. } => Error::OutOfSubStack, + VmError::InvalidSubEntry { .. } => Error::InvalidSubEntry, + VmError::BuiltIn { .. } => Error::BuiltIn, + VmError::Wasm { .. } => Error::Wasm, + VmError::Internal(_) => Error::Internal, + VmError::MutableCallInStaticContext => Error::MutableCallInStaticContext, + VmError::OutOfBounds => Error::OutOfBounds, + VmError::Reverted => Error::Reverted, + } + } } impl From for Error { - fn from(e: VmError) -> Self { - Error::from(&e) - } + fn from(e: VmError) -> Self { + Error::from(&e) + } } impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::Error::*; - let message = match *self { - OutOfGas => "Out of gas", - BadJumpDestination => "Bad jump destination", - BadInstruction => "Bad instruction", - StackUnderflow => "Stack underflow", - OutOfStack => "Out of stack", - BuiltIn => "Built-in failed", - Wasm => "Wasm runtime error", - Internal => "Internal error", - MutableCallInStaticContext => "Mutable Call In Static Context", - OutOfBounds => "Out of bounds", - Reverted => "Reverted", - }; - message.fmt(f) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::Error::*; + let message = match *self { + OutOfGas => "Out of gas", + BadJumpDestination => "Bad jump destination", + BadInstruction => "Bad instruction", + StackUnderflow => "Stack underflow", + OutOfStack => "Out of stack", + SubStackUnderflow => "Subroutine stack underflow", + OutOfSubStack => "Subroutine stack overflow", + BuiltIn => "Built-in failed", + InvalidSubEntry => "Invalid subroutine entry", + Wasm => "Wasm runtime error", + Internal => "Internal error", + MutableCallInStaticContext => "Mutable Call In Static Context", + OutOfBounds => "Out of bounds", + Reverted => "Reverted", + }; + message.fmt(f) + } } impl Encodable for Error { - fn rlp_append(&self, s: &mut RlpStream) { - use self::Error::*; - let value = match *self { - OutOfGas => 0u8, - BadJumpDestination => 1, - BadInstruction => 2, - StackUnderflow => 3, - OutOfStack => 4, - Internal => 5, - BuiltIn => 6, - MutableCallInStaticContext => 7, - Wasm => 8, - OutOfBounds => 9, - Reverted => 10, - }; + fn rlp_append(&self, s: &mut RlpStream) { + use self::Error::*; + let value = match *self { + OutOfGas => 0u8, + BadJumpDestination => 1, + BadInstruction => 2, + StackUnderflow => 3, + OutOfStack => 4, + Internal => 5, + BuiltIn => 6, + MutableCallInStaticContext => 7, + Wasm => 8, + OutOfBounds => 9, + Reverted => 10, + SubStackUnderflow => 11, + OutOfSubStack => 12, + InvalidSubEntry => 13, + }; - s.append_internal(&value); - } + s.append_internal(&value); + } } impl Decodable for Error { - fn decode(rlp: &Rlp) -> Result { - use self::Error::*; - let value: u8 = rlp.as_val()?; - match value { - 0 => Ok(OutOfGas), - 1 => Ok(BadJumpDestination), - 2 => Ok(BadInstruction), - 3 => Ok(StackUnderflow), - 4 => Ok(OutOfStack), - 5 => Ok(Internal), - 6 => Ok(BuiltIn), - 7 => Ok(MutableCallInStaticContext), - 8 => Ok(Wasm), - 9 => Ok(OutOfBounds), - 10 => Ok(Reverted), - _ => Err(DecoderError::Custom("Invalid error type")), - } - } + fn decode(rlp: &Rlp) -> Result { + use self::Error::*; + let value: u8 = rlp.as_val()?; + match value { + 0 => Ok(OutOfGas), + 1 => Ok(BadJumpDestination), + 2 => Ok(BadInstruction), + 3 => Ok(StackUnderflow), + 4 => Ok(OutOfStack), + 5 => Ok(Internal), + 6 => Ok(BuiltIn), + 7 => Ok(MutableCallInStaticContext), + 8 => Ok(Wasm), + 9 => Ok(OutOfBounds), + 10 => Ok(Reverted), + 11 => Ok(SubStackUnderflow), + 12 => Ok(OutOfSubStack), + 13 => Ok(InvalidSubEntry), + _ => Err(DecoderError::Custom("Invalid error type")), + } + } } #[cfg(test)] mod tests { - use rlp::*; - use super::Error; + use super::Error; + use rlp::*; - #[test] - fn encode_error() { - let err = Error::BadJumpDestination; + #[test] + fn encode_error() { + let err = Error::BadJumpDestination; - let mut s = RlpStream::new_list(2); - s.append(&err); - assert!(!s.is_finished(), "List shouldn't finished yet"); - s.append(&err); - assert!(s.is_finished(), "List should be finished now"); - s.out(); - } + let mut s = RlpStream::new_list(2); + s.append(&err); + assert!(!s.is_finished(), "List shouldn't finished yet"); + s.append(&err); + assert!(s.is_finished(), "List should be finished now"); + s.out(); + } } diff --git a/ethcore/src/trace/types/filter.rs b/ethcore/src/trace/types/filter.rs index d7b8fcc1808..604981e4ed3 100644 --- a/ethcore/src/trace/types/filter.rs +++ b/ethcore/src/trace/types/filter.rs @@ -1,428 +1,434 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Trace filters type definitions -use std::ops::Range; +use super::trace::{Action, Res}; use ethereum_types::{Address, Bloom, BloomInput}; +use std::ops::Range; use trace::flat::FlatTrace; -use super::trace::{Action, Res}; /// Addresses filter. /// /// Used to create bloom possibilities and match filters. #[derive(Debug)] pub struct AddressesFilter { - list: Vec
+ list: Vec
, } impl From> for AddressesFilter { - fn from(addresses: Vec
) -> Self { - AddressesFilter { list: addresses } - } + fn from(addresses: Vec
) -> Self { + AddressesFilter { list: addresses } + } } impl AddressesFilter { - /// Returns true if address matches one of the searched addresses. - pub fn matches(&self, address: &Address) -> bool { - self.matches_all() || self.list.contains(address) - } - - /// Returns true if this address filter matches everything. - pub fn matches_all(&self) -> bool { - self.list.is_empty() - } - - /// Returns blooms of this addresses filter. - pub fn blooms(&self) -> Vec { - match self.list.is_empty() { - true => vec![Bloom::default()], - false => self.list.iter() - .map(|address| Bloom::from(BloomInput::Raw(address))) - .collect(), - } - } - - /// Returns vector of blooms zipped with blooms of this addresses filter. - pub fn with_blooms(&self, blooms: Vec) -> Vec { - match self.list.is_empty() { - true => blooms, - false => blooms - .into_iter() - .flat_map(|bloom| self.list.iter() - .map(|address| { - let mut bloom = bloom.clone(); - bloom.accrue(BloomInput::Raw(address)); - bloom - }) - .collect::>()) - .collect(), - } - } + /// Returns true if address matches one of the searched addresses. + pub fn matches(&self, address: &Address) -> bool { + self.matches_all() || self.list.contains(address) + } + + /// Returns true if this address filter matches everything. + pub fn matches_all(&self) -> bool { + self.list.is_empty() + } + + /// Returns blooms of this addresses filter. + pub fn blooms(&self) -> Vec { + match self.list.is_empty() { + true => vec![Bloom::default()], + false => self + .list + .iter() + .map(|address| Bloom::from(BloomInput::Raw(address))) + .collect(), + } + } + + /// Returns vector of blooms zipped with blooms of this addresses filter. + pub fn with_blooms(&self, blooms: Vec) -> Vec { + match self.list.is_empty() { + true => blooms, + false => blooms + .into_iter() + .flat_map(|bloom| { + self.list + .iter() + .map(|address| { + let mut bloom = bloom.clone(); + bloom.accrue(BloomInput::Raw(address)); + bloom + }) + .collect::>() + }) + .collect(), + } + } } #[derive(Debug)] /// Traces filter. pub struct Filter { - /// Block range. - pub range: Range, + /// Block range. + pub range: Range, - /// From address filter. - pub from_address: AddressesFilter, + /// From address filter. + pub from_address: AddressesFilter, - /// To address filter. - pub to_address: AddressesFilter, + /// To address filter. + pub to_address: AddressesFilter, } impl Filter { - /// Returns combinations of each address. - pub fn bloom_possibilities(&self) -> Vec { - self.to_address.with_blooms(self.from_address.blooms()) - } - - /// Returns true if given trace matches the filter. - pub fn matches(&self, trace: &FlatTrace) -> bool { - match trace.action { - Action::Call(ref call) => { - let from_matches = self.from_address.matches(&call.from); - let to_matches = self.to_address.matches(&call.to); - from_matches && to_matches - }, - Action::Create(ref create) => { - let from_matches = self.from_address.matches(&create.from); - - let to_matches = match trace.result { - Res::Create(ref create_result) => self.to_address.matches(&create_result.address), - _ => self.to_address.matches_all(), - }; - - from_matches && to_matches - }, - Action::Suicide(ref suicide) => { - let from_matches = self.from_address.matches(&suicide.address); - let to_matches = self.to_address.matches(&suicide.refund_address); - from_matches && to_matches - }, - Action::Reward(ref reward) => { - self.from_address.matches_all() && self.to_address.matches(&reward.author) - }, - } - } + /// Returns combinations of each address. + pub fn bloom_possibilities(&self) -> Vec { + self.to_address.with_blooms(self.from_address.blooms()) + } + + /// Returns true if given trace matches the filter. + pub fn matches(&self, trace: &FlatTrace) -> bool { + match trace.action { + Action::Call(ref call) => { + let from_matches = self.from_address.matches(&call.from); + let to_matches = self.to_address.matches(&call.to); + from_matches && to_matches + } + Action::Create(ref create) => { + let from_matches = self.from_address.matches(&create.from); + + let to_matches = match trace.result { + Res::Create(ref create_result) => { + self.to_address.matches(&create_result.address) + } + _ => self.to_address.matches_all(), + }; + + from_matches && to_matches + } + Action::Suicide(ref suicide) => { + let from_matches = self.from_address.matches(&suicide.address); + let to_matches = self.to_address.matches(&suicide.refund_address); + from_matches && to_matches + } + Action::Reward(ref reward) => { + self.from_address.matches_all() && self.to_address.matches(&reward.author) + } + } + } } #[cfg(test)] mod tests { - use ethereum_types::{Address, Bloom, BloomInput}; - use trace::trace::{Action, Call, Res, Create, CreateResult, Suicide, Reward}; - use trace::flat::FlatTrace; - use trace::{Filter, AddressesFilter, TraceError, RewardType}; - use evm::CallType; - - #[test] - fn empty_trace_filter_bloom_possibilities() { - let filter = Filter { - range: (0..0), - from_address: AddressesFilter::from(vec![]), - to_address: AddressesFilter::from(vec![]), - }; - - let blooms = filter.bloom_possibilities(); - assert_eq!(blooms, vec![Bloom::default()]); - } - - #[test] - fn single_trace_filter_bloom_possibility() { - let filter = Filter { - range: (0..0), - from_address: AddressesFilter::from(vec![Address::from(1)]), - to_address: AddressesFilter::from(vec![Address::from(2)]), - }; - - let blooms = filter.bloom_possibilities(); - assert_eq!(blooms.len(), 1); - - assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(1)))); - assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(2)))); - assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(3)))); - } - - #[test] - fn only_from_trace_filter_bloom_possibility() { - let filter = Filter { - range: (0..0), - from_address: AddressesFilter::from(vec![Address::from(1)]), - to_address: AddressesFilter::from(vec![]), - }; - - let blooms = filter.bloom_possibilities(); - assert_eq!(blooms.len(), 1); - - assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(1)))); - assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(2)))); - } - - #[test] - fn only_to_trace_filter_bloom_possibility() { - let filter = Filter { - range: (0..0), - from_address: AddressesFilter::from(vec![]), - to_address: AddressesFilter::from(vec![Address::from(1)]), - }; - - let blooms = filter.bloom_possibilities(); - assert_eq!(blooms.len(), 1); - - assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(1)))); - assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(2)))); - } - - #[test] - fn multiple_trace_filter_bloom_possibility() { - let filter = Filter { - range: (0..0), - from_address: AddressesFilter::from(vec![Address::from(1), Address::from(3)]), - to_address: AddressesFilter::from(vec![Address::from(2), Address::from(4)]), - }; - - let blooms = filter.bloom_possibilities(); - assert_eq!(blooms.len(), 4); - - assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(1)))); - assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(2)))); - assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(3)))); - assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(4)))); - - assert!(blooms[1].contains_input(BloomInput::Raw(&Address::from(1)))); - assert!(blooms[1].contains_input(BloomInput::Raw(&Address::from(4)))); - assert!(!blooms[1].contains_input(BloomInput::Raw(&Address::from(2)))); - assert!(!blooms[1].contains_input(BloomInput::Raw(&Address::from(3)))); - - assert!(blooms[2].contains_input(BloomInput::Raw(&Address::from(2)))); - assert!(blooms[2].contains_input(BloomInput::Raw(&Address::from(3)))); - assert!(!blooms[2].contains_input(BloomInput::Raw(&Address::from(1)))); - assert!(!blooms[2].contains_input(BloomInput::Raw(&Address::from(4)))); - - assert!(blooms[3].contains_input(BloomInput::Raw(&Address::from(3)))); - assert!(blooms[3].contains_input(BloomInput::Raw(&Address::from(4)))); - assert!(!blooms[3].contains_input(BloomInput::Raw(&Address::from(1)))); - assert!(!blooms[3].contains_input(BloomInput::Raw(&Address::from(2)))); - } - - #[test] - fn filter_matches() { - let f0 = Filter { - range: (0..0), - from_address: AddressesFilter::from(vec![Address::from(1)]), - to_address: AddressesFilter::from(vec![]), - }; - - let f1 = Filter { - range: (0..0), - from_address: AddressesFilter::from(vec![Address::from(3), Address::from(1)]), - to_address: AddressesFilter::from(vec![]), - }; - - let f2 = Filter { - range: (0..0), - from_address: AddressesFilter::from(vec![]), - to_address: AddressesFilter::from(vec![]), - }; - - let f3 = Filter { - range: (0..0), - from_address: AddressesFilter::from(vec![]), - to_address: AddressesFilter::from(vec![Address::from(2)]), - }; - - let f4 = Filter { - range: (0..0), - from_address: AddressesFilter::from(vec![]), - to_address: AddressesFilter::from(vec![Address::from(2), Address::from(3)]), - }; - - let f5 = Filter { - range: (0..0), - from_address: AddressesFilter::from(vec![Address::from(1)]), - to_address: AddressesFilter::from(vec![Address::from(2), Address::from(3)]), - }; - - let f6 = Filter { - range: (0..0), - from_address: AddressesFilter::from(vec![Address::from(1)]), - to_address: AddressesFilter::from(vec![Address::from(4)]), - }; - - let trace = FlatTrace { - action: Action::Call(Call { - from: 1.into(), - to: 2.into(), - value: 3.into(), - gas: 4.into(), - input: vec![0x5], - call_type: CallType::Call, - }), - result: Res::FailedCall(TraceError::OutOfGas), - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - }; - - assert!(f0.matches(&trace)); - assert!(f1.matches(&trace)); - assert!(f2.matches(&trace)); - assert!(f3.matches(&trace)); - assert!(f4.matches(&trace)); - assert!(f5.matches(&trace)); - assert!(!f6.matches(&trace)); - - let trace = FlatTrace { - action: Action::Create(Create { - from: 1.into(), - value: 3.into(), - gas: 4.into(), - init: vec![0x5], - }), - result: Res::Create(CreateResult { - gas_used: 10.into(), - code: vec![], - address: 2.into(), - }), - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - }; - - assert!(f0.matches(&trace)); - assert!(f1.matches(&trace)); - assert!(f2.matches(&trace)); - assert!(f3.matches(&trace)); - assert!(f4.matches(&trace)); - assert!(f5.matches(&trace)); - assert!(!f6.matches(&trace)); - - let trace = FlatTrace { - action: Action::Suicide(Suicide { - address: 1.into(), - refund_address: 2.into(), - balance: 3.into(), - }), - result: Res::None, - trace_address: vec![].into_iter().collect(), - subtraces: 0 - }; - - assert!(f0.matches(&trace)); - assert!(f1.matches(&trace)); - assert!(f2.matches(&trace)); - assert!(f3.matches(&trace)); - assert!(f4.matches(&trace)); - assert!(f5.matches(&trace)); - assert!(!f6.matches(&trace)); - - let trace = FlatTrace { - action: Action::Reward(Reward { - author: 2.into(), - value: 100.into(), - reward_type: RewardType::Block, - }), - result: Res::None, - trace_address: vec![].into_iter().collect(), - subtraces: 0 - }; - - assert!(!f0.matches(&trace)); - assert!(!f1.matches(&trace)); - assert!(f2.matches(&trace)); - assert!(f3.matches(&trace)); - assert!(f4.matches(&trace)); - assert!(!f5.matches(&trace)); - assert!(!f6.matches(&trace)); - } - - #[test] - fn filter_match_block_reward_fix_8070() { - let f0 = Filter { - range: (0..0), - from_address: vec![1.into()].into(), - to_address: vec![].into(), - }; - - let f1 = Filter { - range: (0..0), - from_address: vec![].into(), - to_address: vec![].into(), - }; - - let f2 = Filter { - range: (0..0), - from_address: vec![].into(), - to_address: vec![2.into()].into(), - }; - - let trace = FlatTrace { - action: Action::Reward(Reward { - author: 2.into(), - value: 10.into(), - reward_type: RewardType::Block, - }), - result: Res::None, - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - }; - - assert!(!f0.matches(&trace)); - assert!(f1.matches(&trace)); - assert!(f2.matches(&trace)); - } - - #[test] - fn filter_match_failed_contract_creation_fix_9822() { - - let f0 = Filter { - range: (0..0), - from_address: vec![1.into()].into(), - to_address: vec![].into(), - }; - - let f1 = Filter { - range: (0..0), - from_address: vec![].into(), - to_address: vec![].into(), - }; - - let f2 = Filter { - range: (0..0), - from_address: vec![].into(), - to_address: vec![2.into()].into(), - }; - - let trace = FlatTrace { - action: Action::Create(Create { - from: 1.into(), - gas: 4.into(), - init: vec![0x5], - value: 3.into(), - }), - result: Res::FailedCall(TraceError::BadInstruction), - trace_address: vec![].into_iter().collect(), - subtraces: 0 - }; - - assert!(f0.matches(&trace)); - assert!(f1.matches(&trace)); - assert!(!f2.matches(&trace)); - } - + use ethereum_types::{Address, Bloom, BloomInput}; + use evm::CallType; + use trace::{ + flat::FlatTrace, + trace::{Action, Call, Create, CreateResult, Res, Reward, Suicide}, + AddressesFilter, Filter, RewardType, TraceError, + }; + + #[test] + fn empty_trace_filter_bloom_possibilities() { + let filter = Filter { + range: (0..0), + from_address: AddressesFilter::from(vec![]), + to_address: AddressesFilter::from(vec![]), + }; + + let blooms = filter.bloom_possibilities(); + assert_eq!(blooms, vec![Bloom::default()]); + } + + #[test] + fn single_trace_filter_bloom_possibility() { + let filter = Filter { + range: (0..0), + from_address: AddressesFilter::from(vec![Address::from(1)]), + to_address: AddressesFilter::from(vec![Address::from(2)]), + }; + + let blooms = filter.bloom_possibilities(); + assert_eq!(blooms.len(), 1); + + assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(1)))); + assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(2)))); + assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(3)))); + } + + #[test] + fn only_from_trace_filter_bloom_possibility() { + let filter = Filter { + range: (0..0), + from_address: AddressesFilter::from(vec![Address::from(1)]), + to_address: AddressesFilter::from(vec![]), + }; + + let blooms = filter.bloom_possibilities(); + assert_eq!(blooms.len(), 1); + + assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(1)))); + assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(2)))); + } + + #[test] + fn only_to_trace_filter_bloom_possibility() { + let filter = Filter { + range: (0..0), + from_address: AddressesFilter::from(vec![]), + to_address: AddressesFilter::from(vec![Address::from(1)]), + }; + + let blooms = filter.bloom_possibilities(); + assert_eq!(blooms.len(), 1); + + assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(1)))); + assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(2)))); + } + + #[test] + fn multiple_trace_filter_bloom_possibility() { + let filter = Filter { + range: (0..0), + from_address: AddressesFilter::from(vec![Address::from(1), Address::from(3)]), + to_address: AddressesFilter::from(vec![Address::from(2), Address::from(4)]), + }; + + let blooms = filter.bloom_possibilities(); + assert_eq!(blooms.len(), 4); + + assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(1)))); + assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(2)))); + assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(3)))); + assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(4)))); + + assert!(blooms[1].contains_input(BloomInput::Raw(&Address::from(1)))); + assert!(blooms[1].contains_input(BloomInput::Raw(&Address::from(4)))); + assert!(!blooms[1].contains_input(BloomInput::Raw(&Address::from(2)))); + assert!(!blooms[1].contains_input(BloomInput::Raw(&Address::from(3)))); + + assert!(blooms[2].contains_input(BloomInput::Raw(&Address::from(2)))); + assert!(blooms[2].contains_input(BloomInput::Raw(&Address::from(3)))); + assert!(!blooms[2].contains_input(BloomInput::Raw(&Address::from(1)))); + assert!(!blooms[2].contains_input(BloomInput::Raw(&Address::from(4)))); + + assert!(blooms[3].contains_input(BloomInput::Raw(&Address::from(3)))); + assert!(blooms[3].contains_input(BloomInput::Raw(&Address::from(4)))); + assert!(!blooms[3].contains_input(BloomInput::Raw(&Address::from(1)))); + assert!(!blooms[3].contains_input(BloomInput::Raw(&Address::from(2)))); + } + + #[test] + fn filter_matches() { + let f0 = Filter { + range: (0..0), + from_address: AddressesFilter::from(vec![Address::from(1)]), + to_address: AddressesFilter::from(vec![]), + }; + + let f1 = Filter { + range: (0..0), + from_address: AddressesFilter::from(vec![Address::from(3), Address::from(1)]), + to_address: AddressesFilter::from(vec![]), + }; + + let f2 = Filter { + range: (0..0), + from_address: AddressesFilter::from(vec![]), + to_address: AddressesFilter::from(vec![]), + }; + + let f3 = Filter { + range: (0..0), + from_address: AddressesFilter::from(vec![]), + to_address: AddressesFilter::from(vec![Address::from(2)]), + }; + + let f4 = Filter { + range: (0..0), + from_address: AddressesFilter::from(vec![]), + to_address: AddressesFilter::from(vec![Address::from(2), Address::from(3)]), + }; + + let f5 = Filter { + range: (0..0), + from_address: AddressesFilter::from(vec![Address::from(1)]), + to_address: AddressesFilter::from(vec![Address::from(2), Address::from(3)]), + }; + + let f6 = Filter { + range: (0..0), + from_address: AddressesFilter::from(vec![Address::from(1)]), + to_address: AddressesFilter::from(vec![Address::from(4)]), + }; + + let trace = FlatTrace { + action: Action::Call(Call { + from: 1.into(), + to: 2.into(), + value: 3.into(), + gas: 4.into(), + input: vec![0x5], + call_type: CallType::Call, + }), + result: Res::FailedCall(TraceError::OutOfGas), + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + }; + + assert!(f0.matches(&trace)); + assert!(f1.matches(&trace)); + assert!(f2.matches(&trace)); + assert!(f3.matches(&trace)); + assert!(f4.matches(&trace)); + assert!(f5.matches(&trace)); + assert!(!f6.matches(&trace)); + + let trace = FlatTrace { + action: Action::Create(Create { + from: 1.into(), + value: 3.into(), + gas: 4.into(), + init: vec![0x5], + }), + result: Res::Create(CreateResult { + gas_used: 10.into(), + code: vec![], + address: 2.into(), + }), + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + }; + + assert!(f0.matches(&trace)); + assert!(f1.matches(&trace)); + assert!(f2.matches(&trace)); + assert!(f3.matches(&trace)); + assert!(f4.matches(&trace)); + assert!(f5.matches(&trace)); + assert!(!f6.matches(&trace)); + + let trace = FlatTrace { + action: Action::Suicide(Suicide { + address: 1.into(), + refund_address: 2.into(), + balance: 3.into(), + }), + result: Res::None, + trace_address: vec![].into_iter().collect(), + subtraces: 0, + }; + + assert!(f0.matches(&trace)); + assert!(f1.matches(&trace)); + assert!(f2.matches(&trace)); + assert!(f3.matches(&trace)); + assert!(f4.matches(&trace)); + assert!(f5.matches(&trace)); + assert!(!f6.matches(&trace)); + + let trace = FlatTrace { + action: Action::Reward(Reward { + author: 2.into(), + value: 100.into(), + reward_type: RewardType::Block, + }), + result: Res::None, + trace_address: vec![].into_iter().collect(), + subtraces: 0, + }; + + assert!(!f0.matches(&trace)); + assert!(!f1.matches(&trace)); + assert!(f2.matches(&trace)); + assert!(f3.matches(&trace)); + assert!(f4.matches(&trace)); + assert!(!f5.matches(&trace)); + assert!(!f6.matches(&trace)); + } + + #[test] + fn filter_match_block_reward_fix_8070() { + let f0 = Filter { + range: (0..0), + from_address: vec![1.into()].into(), + to_address: vec![].into(), + }; + + let f1 = Filter { + range: (0..0), + from_address: vec![].into(), + to_address: vec![].into(), + }; + + let f2 = Filter { + range: (0..0), + from_address: vec![].into(), + to_address: vec![2.into()].into(), + }; + + let trace = FlatTrace { + action: Action::Reward(Reward { + author: 2.into(), + value: 10.into(), + reward_type: RewardType::Block, + }), + result: Res::None, + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + }; + + assert!(!f0.matches(&trace)); + assert!(f1.matches(&trace)); + assert!(f2.matches(&trace)); + } + + #[test] + fn filter_match_failed_contract_creation_fix_9822() { + let f0 = Filter { + range: (0..0), + from_address: vec![1.into()].into(), + to_address: vec![].into(), + }; + + let f1 = Filter { + range: (0..0), + from_address: vec![].into(), + to_address: vec![].into(), + }; + + let f2 = Filter { + range: (0..0), + from_address: vec![].into(), + to_address: vec![2.into()].into(), + }; + + let trace = FlatTrace { + action: Action::Create(Create { + from: 1.into(), + gas: 4.into(), + init: vec![0x5], + value: 3.into(), + }), + result: Res::FailedCall(TraceError::BadInstruction), + trace_address: vec![].into_iter().collect(), + subtraces: 0, + }; + + assert!(f0.matches(&trace)); + assert!(f1.matches(&trace)); + assert!(!f2.matches(&trace)); + } } - diff --git a/ethcore/src/trace/types/flat.rs b/ethcore/src/trace/types/flat.rs index cb3e1229b71..caf5b6be6b1 100644 --- a/ethcore/src/trace/types/flat.rs +++ b/ethcore/src/trace/types/flat.rs @@ -1,78 +1,78 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Flat trace module -use rlp::{Rlp, RlpStream, Decodable, Encodable, DecoderError}; -use heapsize::HeapSizeOf; -use ethereum_types::Bloom; use super::trace::{Action, Res}; +use ethereum_types::Bloom; +use heapsize::HeapSizeOf; +use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; /// Trace localized in vector of traces produced by a single transaction. /// /// Parent and children indexes refer to positions in this vector. #[derive(Debug, PartialEq, Clone)] pub struct FlatTrace { - /// Type of action performed by a transaction. - pub action: Action, - /// Result of this action. - pub result: Res, - /// Number of subtraces. - pub subtraces: usize, - /// Exact location of trace. - /// - /// [index in root, index in first CALL, index in second CALL, ...] - pub trace_address: Vec, + /// Type of action performed by a transaction. + pub action: Action, + /// Result of this action. + pub result: Res, + /// Number of subtraces. + pub subtraces: usize, + /// Exact location of trace. + /// + /// [index in root, index in first CALL, index in second CALL, ...] + pub trace_address: Vec, } impl FlatTrace { - /// Returns bloom of the trace. - pub fn bloom(&self) -> Bloom { - self.action.bloom() | self.result.bloom() - } + /// Returns bloom of the trace. + pub fn bloom(&self) -> Bloom { + self.action.bloom() | self.result.bloom() + } } impl HeapSizeOf for FlatTrace { - fn heap_size_of_children(&self) -> usize { - self.trace_address.heap_size_of_children() - } + fn heap_size_of_children(&self) -> usize { + self.trace_address.heap_size_of_children() + } } impl Encodable for FlatTrace { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(4); - s.append(&self.action); - s.append(&self.result); - s.append(&self.subtraces); - s.append_list::(&self.trace_address.iter().collect::>()); - } + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(4); + s.append(&self.action); + s.append(&self.result); + s.append(&self.subtraces); + s.append_list::(&self.trace_address.iter().collect::>()); + } } impl Decodable for FlatTrace { - fn decode(d: &Rlp) -> Result { - let v: Vec = d.list_at(3)?; - let res = FlatTrace { - action: d.val_at(0)?, - result: d.val_at(1)?, - subtraces: d.val_at(2)?, - trace_address: v.into_iter().collect(), - }; - - Ok(res) - } + fn decode(d: &Rlp) -> Result { + let v: Vec = d.list_at(3)?; + let res = FlatTrace { + action: d.val_at(0)?, + result: d.val_at(1)?, + subtraces: d.val_at(2)?, + trace_address: v.into_iter().collect(), + }; + + Ok(res) + } } /// Represents all traces produced by a single transaction. @@ -80,28 +80,30 @@ impl Decodable for FlatTrace { pub struct FlatTransactionTraces(Vec); impl From> for FlatTransactionTraces { - fn from(v: Vec) -> Self { - FlatTransactionTraces(v) - } + fn from(v: Vec) -> Self { + FlatTransactionTraces(v) + } } impl HeapSizeOf for FlatTransactionTraces { - fn heap_size_of_children(&self) -> usize { - self.0.heap_size_of_children() - } + fn heap_size_of_children(&self) -> usize { + self.0.heap_size_of_children() + } } impl FlatTransactionTraces { - /// Returns bloom of all traces in the collection. - pub fn bloom(&self) -> Bloom { - self.0.iter().fold(Default::default(), | bloom, trace | bloom | trace.bloom()) - } + /// Returns bloom of all traces in the collection. + pub fn bloom(&self) -> Bloom { + self.0 + .iter() + .fold(Default::default(), |bloom, trace| bloom | trace.bloom()) + } } impl Into> for FlatTransactionTraces { - fn into(self) -> Vec { - self.0 - } + fn into(self) -> Vec { + self.0 + } } /// Represents all traces produced by transactions in a single block. @@ -109,141 +111,145 @@ impl Into> for FlatTransactionTraces { pub struct FlatBlockTraces(Vec); impl HeapSizeOf for FlatBlockTraces { - fn heap_size_of_children(&self) -> usize { - self.0.heap_size_of_children() - } + fn heap_size_of_children(&self) -> usize { + self.0.heap_size_of_children() + } } impl From> for FlatBlockTraces { - fn from(v: Vec) -> Self { - FlatBlockTraces(v) - } + fn from(v: Vec) -> Self { + FlatBlockTraces(v) + } } impl FlatBlockTraces { - /// Returns bloom of all traces in the block. - pub fn bloom(&self) -> Bloom { - self.0.iter().fold(Default::default(), | bloom, tx_traces | bloom | tx_traces.bloom()) - } + /// Returns bloom of all traces in the block. + pub fn bloom(&self) -> Bloom { + self.0.iter().fold(Default::default(), |bloom, tx_traces| { + bloom | tx_traces.bloom() + }) + } } impl Into> for FlatBlockTraces { - fn into(self) -> Vec { - self.0 - } + fn into(self) -> Vec { + self.0 + } } #[cfg(test)] mod tests { - use rlp::*; - use super::{FlatBlockTraces, FlatTransactionTraces, FlatTrace}; - use trace::trace::{Action, Res, CallResult, Call, Suicide, Reward}; - use evm::CallType; - use trace::RewardType; - - #[test] - fn encode_flat_transaction_traces() { - let ftt = FlatTransactionTraces::from(Vec::new()); - - let mut s = RlpStream::new_list(2); - s.append(&ftt); - assert!(!s.is_finished(), "List shouldn't finished yet"); - s.append(&ftt); - assert!(s.is_finished(), "List should be finished now"); - s.out(); - } - - #[test] - fn encode_flat_block_traces() { - let fbt = FlatBlockTraces::from(Vec::new()); - - let mut s = RlpStream::new_list(2); - s.append(&fbt); - assert!(!s.is_finished(), "List shouldn't finished yet"); - s.append(&fbt); - assert!(s.is_finished(), "List should be finished now"); - s.out(); - } - - #[test] - fn test_trace_serialization() { - // block #51921 - - let flat_trace = FlatTrace { - action: Action::Call(Call { - from: "8dda5e016e674683241bf671cced51e7239ea2bc".parse().unwrap(), - to: "37a5e19cc2d49f244805d5c268c0e6f321965ab9".parse().unwrap(), - value: "3627e8f712373c0000".parse().unwrap(), - gas: 0x03e8.into(), - input: vec![], - call_type: CallType::Call, - }), - result: Res::Call(CallResult { - gas_used: 0.into(), - output: vec![], - }), - trace_address: Default::default(), - subtraces: 0, - }; - - let flat_trace1 = FlatTrace { - action: Action::Call(Call { - from: "3d0768da09ce77d25e2d998e6a7b6ed4b9116c2d".parse().unwrap(), - to: "412fda7643b37d436cb40628f6dbbb80a07267ed".parse().unwrap(), - value: 0.into(), - gas: 0x010c78.into(), - input: vec![0x41, 0xc0, 0xe1, 0xb5], - call_type: CallType::Call, - }), - result: Res::Call(CallResult { - gas_used: 0x0127.into(), - output: vec![], - }), - trace_address: Default::default(), - subtraces: 1, - }; - - let flat_trace2 = FlatTrace { - action: Action::Suicide(Suicide { - address: "412fda7643b37d436cb40628f6dbbb80a07267ed".parse().unwrap(), - balance: 0.into(), - refund_address: "3d0768da09ce77d25e2d998e6a7b6ed4b9116c2d".parse().unwrap(), - }), - result: Res::None, - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - }; - - let flat_trace3 = FlatTrace { - action: Action::Reward(Reward { - author: "412fda7643b37d436cb40628f6dbbb80a07267ed".parse().unwrap(), - value: 10.into(), - reward_type: RewardType::Uncle, - }), - result: Res::None, - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - }; - - let flat_trace4 = FlatTrace { - action: Action::Reward(Reward { - author: "412fda7643b37d436cb40628f6dbbb80a07267ed".parse().unwrap(), - value: 10.into(), - reward_type: RewardType::Block, - }), - result: Res::None, - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - }; - - let block_traces = FlatBlockTraces(vec![ - FlatTransactionTraces(vec![flat_trace]), - FlatTransactionTraces(vec![flat_trace1, flat_trace2]), - FlatTransactionTraces(vec![flat_trace3, flat_trace4]) - ]); - - let encoded = ::rlp::encode(&block_traces); - let decoded = ::rlp::decode(&encoded).expect("error decoding block traces"); - assert_eq!(block_traces, decoded); - } + use super::{FlatBlockTraces, FlatTrace, FlatTransactionTraces}; + use evm::CallType; + use rlp::*; + use trace::{ + trace::{Action, Call, CallResult, Res, Reward, Suicide}, + RewardType, + }; + + #[test] + fn encode_flat_transaction_traces() { + let ftt = FlatTransactionTraces::from(Vec::new()); + + let mut s = RlpStream::new_list(2); + s.append(&ftt); + assert!(!s.is_finished(), "List shouldn't finished yet"); + s.append(&ftt); + assert!(s.is_finished(), "List should be finished now"); + s.out(); + } + + #[test] + fn encode_flat_block_traces() { + let fbt = FlatBlockTraces::from(Vec::new()); + + let mut s = RlpStream::new_list(2); + s.append(&fbt); + assert!(!s.is_finished(), "List shouldn't finished yet"); + s.append(&fbt); + assert!(s.is_finished(), "List should be finished now"); + s.out(); + } + + #[test] + fn test_trace_serialization() { + // block #51921 + + let flat_trace = FlatTrace { + action: Action::Call(Call { + from: "8dda5e016e674683241bf671cced51e7239ea2bc".parse().unwrap(), + to: "37a5e19cc2d49f244805d5c268c0e6f321965ab9".parse().unwrap(), + value: "3627e8f712373c0000".parse().unwrap(), + gas: 0x03e8.into(), + input: vec![], + call_type: CallType::Call, + }), + result: Res::Call(CallResult { + gas_used: 0.into(), + output: vec![], + }), + trace_address: Default::default(), + subtraces: 0, + }; + + let flat_trace1 = FlatTrace { + action: Action::Call(Call { + from: "3d0768da09ce77d25e2d998e6a7b6ed4b9116c2d".parse().unwrap(), + to: "412fda7643b37d436cb40628f6dbbb80a07267ed".parse().unwrap(), + value: 0.into(), + gas: 0x010c78.into(), + input: vec![0x41, 0xc0, 0xe1, 0xb5], + call_type: CallType::Call, + }), + result: Res::Call(CallResult { + gas_used: 0x0127.into(), + output: vec![], + }), + trace_address: Default::default(), + subtraces: 1, + }; + + let flat_trace2 = FlatTrace { + action: Action::Suicide(Suicide { + address: "412fda7643b37d436cb40628f6dbbb80a07267ed".parse().unwrap(), + balance: 0.into(), + refund_address: "3d0768da09ce77d25e2d998e6a7b6ed4b9116c2d".parse().unwrap(), + }), + result: Res::None, + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + }; + + let flat_trace3 = FlatTrace { + action: Action::Reward(Reward { + author: "412fda7643b37d436cb40628f6dbbb80a07267ed".parse().unwrap(), + value: 10.into(), + reward_type: RewardType::Uncle, + }), + result: Res::None, + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + }; + + let flat_trace4 = FlatTrace { + action: Action::Reward(Reward { + author: "412fda7643b37d436cb40628f6dbbb80a07267ed".parse().unwrap(), + value: 10.into(), + reward_type: RewardType::Block, + }), + result: Res::None, + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + }; + + let block_traces = FlatBlockTraces(vec![ + FlatTransactionTraces(vec![flat_trace]), + FlatTransactionTraces(vec![flat_trace1, flat_trace2]), + FlatTransactionTraces(vec![flat_trace3, flat_trace4]), + ]); + + let encoded = ::rlp::encode(&block_traces); + let decoded = ::rlp::decode(&encoded).expect("error decoding block traces"); + assert_eq!(block_traces, decoded); + } } diff --git a/ethcore/src/trace/types/localized.rs b/ethcore/src/trace/types/localized.rs index 330d23a7289..8392a33d7c3 100644 --- a/ethcore/src/trace/types/localized.rs +++ b/ethcore/src/trace/types/localized.rs @@ -1,44 +1,44 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Localized traces type definitions -use ethereum_types::H256; use super::trace::{Action, Res}; +use ethereum_types::H256; use types::BlockNumber; /// Localized trace. #[derive(Debug, PartialEq, Clone)] pub struct LocalizedTrace { - /// Type of action performed by a transaction. - pub action: Action, - /// Result of this action. - pub result: Res, - /// Number of subtraces. - pub subtraces: usize, - /// Exact location of trace. - /// - /// [index in root, index in first CALL, index in second CALL, ...] - pub trace_address: Vec, - /// Transaction number within the block. - pub transaction_number: Option, - /// Signed transaction hash. - pub transaction_hash: Option, - /// Block number. - pub block_number: BlockNumber, - /// Block hash. - pub block_hash: H256, + /// Type of action performed by a transaction. + pub action: Action, + /// Result of this action. + pub result: Res, + /// Number of subtraces. + pub subtraces: usize, + /// Exact location of trace. + /// + /// [index in root, index in first CALL, index in second CALL, ...] + pub trace_address: Vec, + /// Transaction number within the block. + pub transaction_number: Option, + /// Signed transaction hash. + pub transaction_hash: Option, + /// Block number. + pub block_number: BlockNumber, + /// Block hash. + pub block_hash: H256, } diff --git a/ethcore/src/trace/types/mod.rs b/ethcore/src/trace/types/mod.rs index c1ef3ac1a55..a28cf89c5d6 100644 --- a/ethcore/src/trace/types/mod.rs +++ b/ethcore/src/trace/types/mod.rs @@ -1,57 +1,57 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Types used in the public api pub mod error; pub mod filter; pub mod flat; -pub mod trace; pub mod localized; +pub mod trace; use self::flat::FlatTransactionTraces; /// Container for block traces. #[derive(Clone)] pub enum Tracing { - /// This variant should be used when tracing is enabled. - Enabled(Vec), - /// Tracing is disabled. - Disabled, + /// This variant should be used when tracing is enabled. + Enabled(Vec), + /// Tracing is disabled. + Disabled, } impl Tracing { - /// Creates new instance of enabled tracing object. - pub fn enabled() -> Self { - Tracing::Enabled(Default::default()) - } - - /// Returns true if tracing is enabled. - pub fn is_enabled(&self) -> bool { - match *self { - Tracing::Enabled(_) => true, - Tracing::Disabled => false, - } - } - - /// Drain all traces. - pub fn drain(self) -> Vec { - match self { - Tracing::Enabled(traces) => traces, - Tracing::Disabled => vec![], - } - } + /// Creates new instance of enabled tracing object. + pub fn enabled() -> Self { + Tracing::Enabled(Default::default()) + } + + /// Returns true if tracing is enabled. + pub fn is_enabled(&self) -> bool { + match *self { + Tracing::Enabled(_) => true, + Tracing::Disabled => false, + } + } + + /// Drain all traces. + pub fn drain(self) -> Vec { + match self { + Tracing::Enabled(traces) => traces, + Tracing::Disabled => vec![], + } + } } diff --git a/ethcore/src/trace/types/trace.rs b/ethcore/src/trace/types/trace.rs index 16084e94cbd..28f7c3f7269 100644 --- a/ethcore/src/trace/types/trace.rs +++ b/ethcore/src/trace/types/trace.rs @@ -1,431 +1,434 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Tracing datatypes. -use ethereum_types::{U256, Address, Bloom, BloomInput}; use bytes::Bytes; -use rlp::{Rlp, RlpStream, Encodable, DecoderError, Decodable}; +use ethereum_types::{Address, Bloom, BloomInput, U256}; +use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; -use vm::ActionParams; -use evm::CallType; use super::error::Error; +use evm::CallType; +use vm::ActionParams; /// `Call` result. #[derive(Debug, Clone, PartialEq, Default, RlpEncodable, RlpDecodable)] pub struct CallResult { - /// Gas used by call. - pub gas_used: U256, - /// Call Output. - pub output: Bytes, + /// Gas used by call. + pub gas_used: U256, + /// Call Output. + pub output: Bytes, } /// `Create` result. #[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)] pub struct CreateResult { - /// Gas used by create. - pub gas_used: U256, - /// Code of the newly created contract. - pub code: Bytes, - /// Address of the newly created contract. - pub address: Address, + /// Gas used by create. + pub gas_used: U256, + /// Code of the newly created contract. + pub code: Bytes, + /// Address of the newly created contract. + pub address: Address, } impl CreateResult { - /// Returns bloom. - pub fn bloom(&self) -> Bloom { - BloomInput::Raw(&self.address).into() - } + /// Returns bloom. + pub fn bloom(&self) -> Bloom { + BloomInput::Raw(&self.address).into() + } } /// Description of a _call_ action, either a `CALL` operation or a message transction. #[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)] pub struct Call { - /// The sending account. - pub from: Address, - /// The destination account. - pub to: Address, - /// The value transferred to the destination account. - pub value: U256, - /// The gas available for executing the call. - pub gas: U256, - /// The input data provided to the call. - pub input: Bytes, - /// The type of the call. - pub call_type: CallType, + /// The sending account. + pub from: Address, + /// The destination account. + pub to: Address, + /// The value transferred to the destination account. + pub value: U256, + /// The gas available for executing the call. + pub gas: U256, + /// The input data provided to the call. + pub input: Bytes, + /// The type of the call. + pub call_type: CallType, } impl From for Call { - fn from(p: ActionParams) -> Self { - match p.call_type { - CallType::DelegateCall | CallType::CallCode => Call { - from: p.address, - to: p.code_address, - value: p.value.value(), - gas: p.gas, - input: p.data.unwrap_or_else(Vec::new), - call_type: p.call_type, - }, - _ => Call { - from: p.sender, - to: p.address, - value: p.value.value(), - gas: p.gas, - input: p.data.unwrap_or_else(Vec::new), - call_type: p.call_type, - }, - } - } + fn from(p: ActionParams) -> Self { + match p.call_type { + CallType::DelegateCall | CallType::CallCode => Call { + from: p.address, + to: p.code_address, + value: p.value.value(), + gas: p.gas, + input: p.data.unwrap_or_else(Vec::new), + call_type: p.call_type, + }, + _ => Call { + from: p.sender, + to: p.address, + value: p.value.value(), + gas: p.gas, + input: p.data.unwrap_or_else(Vec::new), + call_type: p.call_type, + }, + } + } } impl Call { - /// Returns call action bloom. - /// The bloom contains from and to addresses. - pub fn bloom(&self) -> Bloom { - let mut bloom = Bloom::default(); - bloom.accrue(BloomInput::Raw(&self.from)); - bloom.accrue(BloomInput::Raw(&self.to)); - bloom - } + /// Returns call action bloom. + /// The bloom contains from and to addresses. + pub fn bloom(&self) -> Bloom { + let mut bloom = Bloom::default(); + bloom.accrue(BloomInput::Raw(&self.from)); + bloom.accrue(BloomInput::Raw(&self.to)); + bloom + } } /// Description of a _create_ action, either a `CREATE` operation or a create transction. #[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)] pub struct Create { - /// The address of the creator. - pub from: Address, - /// The value with which the new account is endowed. - pub value: U256, - /// The gas available for the creation init code. - pub gas: U256, - /// The init code. - pub init: Bytes, + /// The address of the creator. + pub from: Address, + /// The value with which the new account is endowed. + pub value: U256, + /// The gas available for the creation init code. + pub gas: U256, + /// The init code. + pub init: Bytes, } impl From for Create { - fn from(p: ActionParams) -> Self { - Create { - from: p.sender, - value: p.value.value(), - gas: p.gas, - init: p.code.map_or_else(Vec::new, |c| (*c).clone()), - } - } + fn from(p: ActionParams) -> Self { + Create { + from: p.sender, + value: p.value.value(), + gas: p.gas, + init: p.code.map_or_else(Vec::new, |c| (*c).clone()), + } + } } impl Create { - /// Returns bloom create action bloom. - /// The bloom contains only from address. - pub fn bloom(&self) -> Bloom { - BloomInput::Raw(&self.from).into() - } + /// Returns bloom create action bloom. + /// The bloom contains only from address. + pub fn bloom(&self) -> Bloom { + BloomInput::Raw(&self.from).into() + } } /// Reward type. #[derive(Debug, PartialEq, Clone, Copy)] pub enum RewardType { - /// Block - Block, - /// Uncle - Uncle, - /// Empty step (AuthorityRound) - EmptyStep, - /// A reward directly attributed by an external protocol (e.g. block reward contract) - External, + /// Block + Block, + /// Uncle + Uncle, + /// Empty step (AuthorityRound) + EmptyStep, + /// A reward directly attributed by an external protocol (e.g. block reward contract) + External, } impl Encodable for RewardType { - fn rlp_append(&self, s: &mut RlpStream) { - let v = match *self { - RewardType::Block => 0u32, - RewardType::Uncle => 1, - RewardType::EmptyStep => 2, - RewardType::External => 3, - }; - Encodable::rlp_append(&v, s); - } + fn rlp_append(&self, s: &mut RlpStream) { + let v = match *self { + RewardType::Block => 0u32, + RewardType::Uncle => 1, + RewardType::EmptyStep => 2, + RewardType::External => 3, + }; + Encodable::rlp_append(&v, s); + } } impl Decodable for RewardType { - fn decode(rlp: &Rlp) -> Result { - rlp.as_val().and_then(|v| Ok(match v { - 0u32 => RewardType::Block, - 1 => RewardType::Uncle, - 2 => RewardType::EmptyStep, - 3 => RewardType::External, - _ => return Err(DecoderError::Custom("Invalid value of RewardType item")), - })) - } + fn decode(rlp: &Rlp) -> Result { + rlp.as_val().and_then(|v| { + Ok(match v { + 0u32 => RewardType::Block, + 1 => RewardType::Uncle, + 2 => RewardType::EmptyStep, + 3 => RewardType::External, + _ => return Err(DecoderError::Custom("Invalid value of RewardType item")), + }) + }) + } } /// Reward action #[derive(Debug, Clone, PartialEq)] pub struct Reward { - /// Author's address. - pub author: Address, - /// Reward amount. - pub value: U256, - /// Reward type. - pub reward_type: RewardType, + /// Author's address. + pub author: Address, + /// Reward amount. + pub value: U256, + /// Reward type. + pub reward_type: RewardType, } impl Reward { - /// Return reward action bloom. - pub fn bloom(&self) -> Bloom { - BloomInput::Raw(&self.author).into() - } + /// Return reward action bloom. + pub fn bloom(&self) -> Bloom { + BloomInput::Raw(&self.author).into() + } } impl Encodable for Reward { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(3); - s.append(&self.author); - s.append(&self.value); - s.append(&self.reward_type); - } + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(3); + s.append(&self.author); + s.append(&self.value); + s.append(&self.reward_type); + } } impl Decodable for Reward { - fn decode(rlp: &Rlp) -> Result { - let res = Reward { - author: rlp.val_at(0)?, - value: rlp.val_at(1)?, - reward_type: rlp.val_at(2)?, - }; - - Ok(res) - } + fn decode(rlp: &Rlp) -> Result { + let res = Reward { + author: rlp.val_at(0)?, + value: rlp.val_at(1)?, + reward_type: rlp.val_at(2)?, + }; + + Ok(res) + } } /// Suicide action. #[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)] pub struct Suicide { - /// Suicided address. - pub address: Address, - /// Suicided contract heir. - pub refund_address: Address, - /// Balance of the contract just before suicide. - pub balance: U256, + /// Suicided address. + pub address: Address, + /// Suicided contract heir. + pub refund_address: Address, + /// Balance of the contract just before suicide. + pub balance: U256, } impl Suicide { - /// Return suicide action bloom. - pub fn bloom(&self) -> Bloom { - let mut bloom = Bloom::default(); - bloom.accrue(BloomInput::Raw(&self.address)); - bloom.accrue(BloomInput::Raw(&self.refund_address)); - bloom - } + /// Return suicide action bloom. + pub fn bloom(&self) -> Bloom { + let mut bloom = Bloom::default(); + bloom.accrue(BloomInput::Raw(&self.address)); + bloom.accrue(BloomInput::Raw(&self.refund_address)); + bloom + } } /// Description of an action that we trace; will be either a call or a create. #[derive(Debug, Clone, PartialEq)] pub enum Action { - /// It's a call action. - Call(Call), - /// It's a create action. - Create(Create), - /// Suicide. - Suicide(Suicide), - /// Reward - Reward(Reward), + /// It's a call action. + Call(Call), + /// It's a create action. + Create(Create), + /// Suicide. + Suicide(Suicide), + /// Reward + Reward(Reward), } impl Encodable for Action { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(2); - match *self { - Action::Call(ref call) => { - s.append(&0u8); - s.append(call); - }, - Action::Create(ref create) => { - s.append(&1u8); - s.append(create); - }, - Action::Suicide(ref suicide) => { - s.append(&2u8); - s.append(suicide); - }, - Action::Reward(ref reward) => { - s.append(&3u8); - s.append(reward); - } - - } - } + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2); + match *self { + Action::Call(ref call) => { + s.append(&0u8); + s.append(call); + } + Action::Create(ref create) => { + s.append(&1u8); + s.append(create); + } + Action::Suicide(ref suicide) => { + s.append(&2u8); + s.append(suicide); + } + Action::Reward(ref reward) => { + s.append(&3u8); + s.append(reward); + } + } + } } impl Decodable for Action { - fn decode(rlp: &Rlp) -> Result { - let action_type: u8 = rlp.val_at(0)?; - match action_type { - 0 => rlp.val_at(1).map(Action::Call), - 1 => rlp.val_at(1).map(Action::Create), - 2 => rlp.val_at(1).map(Action::Suicide), - 3 => rlp.val_at(1).map(Action::Reward), - _ => Err(DecoderError::Custom("Invalid action type.")), - } - } + fn decode(rlp: &Rlp) -> Result { + let action_type: u8 = rlp.val_at(0)?; + match action_type { + 0 => rlp.val_at(1).map(Action::Call), + 1 => rlp.val_at(1).map(Action::Create), + 2 => rlp.val_at(1).map(Action::Suicide), + 3 => rlp.val_at(1).map(Action::Reward), + _ => Err(DecoderError::Custom("Invalid action type.")), + } + } } impl Action { - /// Returns action bloom. - pub fn bloom(&self) -> Bloom { - match *self { - Action::Call(ref call) => call.bloom(), - Action::Create(ref create) => create.bloom(), - Action::Suicide(ref suicide) => suicide.bloom(), - Action::Reward(ref reward) => reward.bloom(), - } - } + /// Returns action bloom. + pub fn bloom(&self) -> Bloom { + match *self { + Action::Call(ref call) => call.bloom(), + Action::Create(ref create) => create.bloom(), + Action::Suicide(ref suicide) => suicide.bloom(), + Action::Reward(ref reward) => reward.bloom(), + } + } } /// The result of the performed action. #[derive(Debug, Clone, PartialEq)] pub enum Res { - /// Successful call action result. - Call(CallResult), - /// Successful create action result. - Create(CreateResult), - /// Failed call. - FailedCall(Error), - /// Failed create. - FailedCreate(Error), - /// None - None, + /// Successful call action result. + Call(CallResult), + /// Successful create action result. + Create(CreateResult), + /// Failed call. + FailedCall(Error), + /// Failed create. + FailedCreate(Error), + /// None + None, } impl Encodable for Res { - fn rlp_append(&self, s: &mut RlpStream) { - match *self { - Res::Call(ref call) => { - s.begin_list(2); - s.append(&0u8); - s.append(call); - }, - Res::Create(ref create) => { - s.begin_list(2); - s.append(&1u8); - s.append(create); - }, - Res::FailedCall(ref err) => { - s.begin_list(2); - s.append(&2u8); - s.append(err); - }, - Res::FailedCreate(ref err) => { - s.begin_list(2); - s.append(&3u8); - s.append(err); - }, - Res::None => { - s.begin_list(1); - s.append(&4u8); - } - } - } + fn rlp_append(&self, s: &mut RlpStream) { + match *self { + Res::Call(ref call) => { + s.begin_list(2); + s.append(&0u8); + s.append(call); + } + Res::Create(ref create) => { + s.begin_list(2); + s.append(&1u8); + s.append(create); + } + Res::FailedCall(ref err) => { + s.begin_list(2); + s.append(&2u8); + s.append(err); + } + Res::FailedCreate(ref err) => { + s.begin_list(2); + s.append(&3u8); + s.append(err); + } + Res::None => { + s.begin_list(1); + s.append(&4u8); + } + } + } } impl Decodable for Res { - fn decode(rlp: &Rlp) -> Result { - let action_type: u8 = rlp.val_at(0)?; - match action_type { - 0 => rlp.val_at(1).map(Res::Call), - 1 => rlp.val_at(1).map(Res::Create), - 2 => rlp.val_at(1).map(Res::FailedCall), - 3 => rlp.val_at(1).map(Res::FailedCreate), - 4 => Ok(Res::None), - _ => Err(DecoderError::Custom("Invalid result type.")), - } - } + fn decode(rlp: &Rlp) -> Result { + let action_type: u8 = rlp.val_at(0)?; + match action_type { + 0 => rlp.val_at(1).map(Res::Call), + 1 => rlp.val_at(1).map(Res::Create), + 2 => rlp.val_at(1).map(Res::FailedCall), + 3 => rlp.val_at(1).map(Res::FailedCreate), + 4 => Ok(Res::None), + _ => Err(DecoderError::Custom("Invalid result type.")), + } + } } impl Res { - /// Returns result bloom. - pub fn bloom(&self) -> Bloom { - match *self { - Res::Create(ref create) => create.bloom(), - Res::Call(_) | Res::FailedCall(_) | Res::FailedCreate(_) | Res::None => Default::default(), - } - } - - /// Did this call fail? - pub fn succeeded(&self) -> bool { - match *self { - Res::Call(_) | Res::Create(_) => true, - _ => false, - } - } + /// Returns result bloom. + pub fn bloom(&self) -> Bloom { + match *self { + Res::Create(ref create) => create.bloom(), + Res::Call(_) | Res::FailedCall(_) | Res::FailedCreate(_) | Res::None => { + Default::default() + } + } + } + + /// Did this call fail? + pub fn succeeded(&self) -> bool { + match *self { + Res::Call(_) | Res::Create(_) => true, + _ => false, + } + } } #[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)] /// A diff of some chunk of memory. pub struct MemoryDiff { - /// Offset into memory the change begins. - pub offset: usize, - /// The changed data. - pub data: Bytes, + /// Offset into memory the change begins. + pub offset: usize, + /// The changed data. + pub data: Bytes, } #[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)] /// A diff of some storage value. pub struct StorageDiff { - /// Which key in storage is changed. - pub location: U256, - /// What the value has been changed to. - pub value: U256, + /// Which key in storage is changed. + pub location: U256, + /// What the value has been changed to. + pub value: U256, } #[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)] /// A record of an executed VM operation. pub struct VMExecutedOperation { - /// The total gas used. - pub gas_used: U256, - /// The stack item placed, if any. - pub stack_push: Vec, - /// If altered, the memory delta. - pub mem_diff: Option, - /// The altered storage value, if any. - pub store_diff: Option, + /// The total gas used. + pub gas_used: U256, + /// The stack item placed, if any. + pub stack_push: Vec, + /// If altered, the memory delta. + pub mem_diff: Option, + /// The altered storage value, if any. + pub store_diff: Option, } #[derive(Debug, Clone, PartialEq, Default, RlpEncodable, RlpDecodable)] /// A record of the execution of a single VM operation. pub struct VMOperation { - /// The program counter. - pub pc: usize, - /// The instruction executed. - pub instruction: u8, - /// The gas cost for this instruction. - pub gas_cost: U256, - /// Information concerning the execution of the operation. - pub executed: Option, + /// The program counter. + pub pc: usize, + /// The instruction executed. + pub instruction: u8, + /// The gas cost for this instruction. + pub gas_cost: U256, + /// Information concerning the execution of the operation. + pub executed: Option, } #[derive(Debug, Clone, PartialEq, Default, RlpEncodable, RlpDecodable)] /// A record of a full VM trace for a CALL/CREATE. pub struct VMTrace { - /// The step (i.e. index into operations) at which this trace corresponds. - pub parent_step: usize, - /// The code to be executed. - pub code: Bytes, - /// The operations executed. - pub operations: Vec, - /// The sub traces for each interior action performed as part of this call/create. - /// Thre is a 1:1 correspondance between these and a CALL/CREATE/CALLCODE/DELEGATECALL instruction. - pub subs: Vec, + /// The step (i.e. index into operations) at which this trace corresponds. + pub parent_step: usize, + /// The code to be executed. + pub code: Bytes, + /// The operations executed. + pub operations: Vec, + /// The sub traces for each interior action performed as part of this call/create. + /// Thre is a 1:1 correspondance between these and a CALL/CREATE/CALLCODE/DELEGATECALL instruction. + pub subs: Vec, } diff --git a/ethcore/src/transaction_ext.rs b/ethcore/src/transaction_ext.rs index fefcd91a341..d86f095823d 100644 --- a/ethcore/src/transaction_ext.rs +++ b/ethcore/src/transaction_ext.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Ethereum transaction @@ -21,23 +21,36 @@ use types::transaction::{self, Action}; /// Extends transaction with gas verification method. pub trait Transaction { - /// Get the transaction cost in gas for this transaction. - fn gas_required(&self, schedule: &Schedule) -> u64; + /// Get the transaction cost in gas for this transaction. + fn gas_required(&self, schedule: &Schedule) -> u64; } impl Transaction for transaction::Transaction { - fn gas_required(&self, schedule: &Schedule) -> u64 { - gas_required_for(match self.action { - Action::Create => true, - Action::Call(_) => false - }, &self.data, schedule) - } + fn gas_required(&self, schedule: &Schedule) -> u64 { + gas_required_for( + match self.action { + Action::Create => true, + Action::Call(_) => false, + }, + &self.data, + schedule, + ) + } } /// Get the transaction cost in gas for the given params. fn gas_required_for(is_create: bool, data: &[u8], schedule: &Schedule) -> u64 { - data.iter().fold( - (if is_create {schedule.tx_create_gas} else {schedule.tx_gas}) as u64, - |g, b| g + (match *b { 0 => schedule.tx_data_zero_gas, _ => schedule.tx_data_non_zero_gas }) as u64 - ) + data.iter().fold( + (if is_create { + schedule.tx_create_gas + } else { + schedule.tx_gas + }) as u64, + |g, b| { + g + (match *b { + 0 => schedule.tx_data_zero_gas, + _ => schedule.tx_data_non_zero_gas, + }) as u64 + }, + ) } diff --git a/ethcore/src/tx_filter.rs b/ethcore/src/tx_filter.rs index 3f32ab365a8..d4e7abb93b6 100644 --- a/ethcore/src/tx_filter.rs +++ b/ethcore/src/tx_filter.rs @@ -1,287 +1,546 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Smart contract based transaction filter. -use ethereum_types::{H256, U256, Address}; -use lru_cache::LruCache; use ethabi::FunctionOutputDecoder; +use ethereum_types::{Address, H256, U256}; +use lru_cache::LruCache; use call_contract::CallContract; -use client::{BlockInfo, BlockId}; +use client::{BlockId, BlockInfo}; +use hash::KECCAK_EMPTY; use parking_lot::Mutex; use spec::CommonParams; -use types::transaction::{Action, SignedTransaction}; -use types::BlockNumber; -use hash::KECCAK_EMPTY; - -use_contract!(transact_acl_deprecated, "res/contracts/tx_acl_deprecated.json"); +use types::{ + transaction::{Action, SignedTransaction}, + BlockNumber, +}; + +use_contract!( + transact_acl_deprecated, + "res/contracts/tx_acl_deprecated.json" +); use_contract!(transact_acl, "res/contracts/tx_acl.json"); const MAX_CACHE_SIZE: usize = 4096; mod tx_permissions { - pub const _ALL: u32 = 0xffffffff; - pub const NONE: u32 = 0x0; - pub const BASIC: u32 = 0b00000001; - pub const CALL: u32 = 0b00000010; - pub const CREATE: u32 = 0b00000100; - pub const _PRIVATE: u32 = 0b00001000; + pub const _ALL: u32 = 0xffffffff; + pub const NONE: u32 = 0x0; + pub const BASIC: u32 = 0b00000001; + pub const CALL: u32 = 0b00000010; + pub const CREATE: u32 = 0b00000100; + pub const _PRIVATE: u32 = 0b00001000; } /// Connection filter that uses a contract to manage permissions. pub struct TransactionFilter { - contract_address: Address, - transition_block: BlockNumber, - permission_cache: Mutex>, - contract_version_cache: Mutex>> + contract_address: Address, + transition_block: BlockNumber, + permission_cache: Mutex>, + contract_version_cache: Mutex>>, } impl TransactionFilter { - /// Create a new instance if address is specified in params. - pub fn from_params(params: &CommonParams) -> Option { - params.transaction_permission_contract.map(|address| - TransactionFilter { - contract_address: address, - transition_block: params.transaction_permission_contract_transition, - permission_cache: Mutex::new(LruCache::new(MAX_CACHE_SIZE)), - contract_version_cache: Mutex::new(LruCache::new(MAX_CACHE_SIZE)), - } - ) - } - - /// Check if transaction is allowed at given block. - pub fn transaction_allowed(&self, parent_hash: &H256, block_number: BlockNumber, transaction: &SignedTransaction, client: &C) -> bool { - if block_number < self.transition_block { return true; } - - let mut permission_cache = self.permission_cache.lock(); - let mut contract_version_cache = self.contract_version_cache.lock(); - - let (tx_type, to) = match transaction.action { - Action::Create => (tx_permissions::CREATE, Address::new()), - Action::Call(address) => if client.code_hash(&address, BlockId::Hash(*parent_hash)).map_or(false, |c| c != KECCAK_EMPTY) { - (tx_permissions::CALL, address) - } else { - (tx_permissions::BASIC, address) - } - }; - - let sender = transaction.sender(); - let value = transaction.value; - let key = (*parent_hash, sender); - - if let Some(permissions) = permission_cache.get_mut(&key) { - return *permissions & tx_type != 0; - } - - let contract_address = self.contract_address; - let contract_version = contract_version_cache.get_mut(parent_hash).and_then(|v| *v).or_else(|| { - let (data, decoder) = transact_acl::functions::contract_version::call(); - decoder.decode(&client.call_contract(BlockId::Hash(*parent_hash), contract_address, data).ok()?).ok() - }); - contract_version_cache.insert(*parent_hash, contract_version); - - // Check permissions in smart contract based on its version - let (permissions, filter_only_sender) = match contract_version { - Some(version) => { - let version_u64 = version.low_u64(); - trace!(target: "tx_filter", "Version of tx permission contract: {}", version); - match version_u64 { - 2 => { - let (data, decoder) = transact_acl::functions::allowed_tx_types::call(sender, to, value); - client.call_contract(BlockId::Hash(*parent_hash), contract_address, data) + /// Create a new instance if address is specified in params. + pub fn from_params(params: &CommonParams) -> Option { + params + .transaction_permission_contract + .map(|address| TransactionFilter { + contract_address: address, + transition_block: params.transaction_permission_contract_transition, + permission_cache: Mutex::new(LruCache::new(MAX_CACHE_SIZE)), + contract_version_cache: Mutex::new(LruCache::new(MAX_CACHE_SIZE)), + }) + } + + /// Check if transaction is allowed at given block. + pub fn transaction_allowed( + &self, + parent_hash: &H256, + block_number: BlockNumber, + transaction: &SignedTransaction, + client: &C, + ) -> bool { + if block_number < self.transition_block { + return true; + } + + let mut permission_cache = self.permission_cache.lock(); + let mut contract_version_cache = self.contract_version_cache.lock(); + + let (tx_type, to) = match transaction.action { + Action::Create => (tx_permissions::CREATE, Address::new()), + Action::Call(address) => { + if client + .code_hash(&address, BlockId::Hash(*parent_hash)) + .map_or(false, |c| c != KECCAK_EMPTY) + { + (tx_permissions::CALL, address) + } else { + (tx_permissions::BASIC, address) + } + } + }; + + let sender = transaction.sender(); + let value = transaction.value; + let key = (*parent_hash, sender); + + if let Some(permissions) = permission_cache.get_mut(&key) { + return *permissions & tx_type != 0; + } + + let contract_address = self.contract_address; + let contract_version = contract_version_cache + .get_mut(parent_hash) + .and_then(|v| *v) + .or_else(|| { + let (data, decoder) = transact_acl::functions::contract_version::call(); + decoder + .decode( + &client + .call_contract(BlockId::Hash(*parent_hash), contract_address, data) + .ok()?, + ) + .ok() + }); + contract_version_cache.insert(*parent_hash, contract_version); + + // Check permissions in smart contract based on its version + let (permissions, filter_only_sender) = match contract_version { + Some(version) => { + let version_u64 = version.low_u64(); + trace!(target: "tx_filter", "Version of tx permission contract: {}", version); + match version_u64 { + 2 => { + let (data, decoder) = + transact_acl::functions::allowed_tx_types::call(sender, to, value); + client.call_contract(BlockId::Hash(*parent_hash), contract_address, data) .and_then(|value| decoder.decode(&value).map_err(|e| e.to_string())) .map(|(p, f)| (p.low_u32(), f)) .unwrap_or_else(|e| { error!(target: "tx_filter", "Error calling tx permissions contract: {:?}", e); (tx_permissions::NONE, true) }) - }, - _ => { - error!(target: "tx_filter", "Unknown version of tx permissions contract is used"); - (tx_permissions::NONE, true) - } - } - }, - None => { - trace!(target: "tx_filter", "Fallback to the deprecated version of tx permission contract"); - let (data, decoder) = transact_acl_deprecated::functions::allowed_tx_types::call(sender); - (client.call_contract(BlockId::Hash(*parent_hash), contract_address, data) + } + _ => { + error!(target: "tx_filter", "Unknown version of tx permissions contract is used"); + (tx_permissions::NONE, true) + } + } + } + None => { + trace!(target: "tx_filter", "Fallback to the deprecated version of tx permission contract"); + let (data, decoder) = + transact_acl_deprecated::functions::allowed_tx_types::call(sender); + (client.call_contract(BlockId::Hash(*parent_hash), contract_address, data) .and_then(|value| decoder.decode(&value).map_err(|e| e.to_string())) .map(|p| p.low_u32()) .unwrap_or_else(|e| { error!(target: "tx_filter", "Error calling tx permissions contract: {:?}", e); tx_permissions::NONE }), true) - } - }; - - if filter_only_sender { - permission_cache.insert((*parent_hash, sender), permissions); - } - trace!(target: "tx_filter", - "Given transaction data: sender: {:?} to: {:?} value: {}. Permissions required: {:X}, got: {:X}", - sender, to, value, tx_type, permissions - ); - permissions & tx_type != 0 - } + } + }; + + if filter_only_sender { + permission_cache.insert((*parent_hash, sender), permissions); + } + trace!(target: "tx_filter", + "Given transaction data: sender: {:?} to: {:?} value: {}. Permissions required: {:X}, got: {:X}", + sender, to, value, tx_type, permissions + ); + permissions & tx_type != 0 + } } #[cfg(test)] mod test { - use std::sync::Arc; - use spec::Spec; - use client::{BlockChainClient, Client, ClientConfig, BlockId}; - use miner::Miner; - use ethereum_types::{U256, Address}; - use io::IoChannel; - use ethkey::{Secret, KeyPair}; - use super::TransactionFilter; - use types::transaction::{Transaction, Action}; - use tempdir::TempDir; - use test_helpers; - - /// Contract code: https://gist.github.com/VladLupashevskyi/84f18eabb1e4afadf572cf92af3e7e7f - #[test] - fn transaction_filter() { - let spec_data = include_str!("../res/tx_permission_tests/contract_ver_2_genesis.json"); - - let db = test_helpers::new_db(); - let tempdir = TempDir::new("").unwrap(); - let spec = Spec::load(&tempdir.path(), spec_data.as_bytes()).unwrap(); - - let client = Client::new( - ClientConfig::default(), - &spec, - db, - Arc::new(Miner::new_for_tests(&spec, None)), - IoChannel::disconnected(), - ).unwrap(); - let key1 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000001")).unwrap(); - let key2 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000002")).unwrap(); - let key3 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000003")).unwrap(); - let key4 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000004")).unwrap(); - let key5 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000005")).unwrap(); - let key6 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000006")).unwrap(); - let key7 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000007")).unwrap(); - - let filter = TransactionFilter::from_params(spec.params()).unwrap(); - let mut basic_tx = Transaction::default(); - basic_tx.action = Action::Call(Address::from("d41c057fd1c78805aac12b0a94a405c0461a6fbb")); - let create_tx = Transaction::default(); - let mut call_tx = Transaction::default(); - call_tx.action = Action::Call(Address::from("0000000000000000000000000000000000000005")); - - let mut basic_tx_with_ether_and_to_key7 = Transaction::default(); - basic_tx_with_ether_and_to_key7.action = Action::Call(Address::from("d41c057fd1c78805aac12b0a94a405c0461a6fbb")); - basic_tx_with_ether_and_to_key7.value = U256::from(123123); - let mut call_tx_with_ether = Transaction::default(); - call_tx_with_ether.action = Action::Call(Address::from("0000000000000000000000000000000000000005")); - call_tx_with_ether.value = U256::from(123123); - - let mut basic_tx_to_key6 = Transaction::default(); - basic_tx_to_key6.action = Action::Call(Address::from("e57bfe9f44b819898f47bf37e5af72a0783e1141")); - let mut basic_tx_with_ether_and_to_key6 = Transaction::default(); - basic_tx_with_ether_and_to_key6.action = Action::Call(Address::from("e57bfe9f44b819898f47bf37e5af72a0783e1141")); - basic_tx_with_ether_and_to_key6.value = U256::from(123123); - - let genesis = client.block_hash(BlockId::Latest).unwrap(); - let block_number = 1; - - assert!(!filter.transaction_allowed(&genesis, block_number, &create_tx.clone().sign(key2.secret(), None), &*client)); - // same tx but request is allowed because the contract only enables at block #1 - assert!(filter.transaction_allowed(&genesis, 0, &create_tx.clone().sign(key2.secret(), None), &*client)); - - assert!(filter.transaction_allowed(&genesis, block_number, &basic_tx.clone().sign(key1.secret(), None), &*client)); - assert!(filter.transaction_allowed(&genesis, block_number, &create_tx.clone().sign(key1.secret(), None), &*client)); - assert!(filter.transaction_allowed(&genesis, block_number, &call_tx.clone().sign(key1.secret(), None), &*client)); - - assert!(filter.transaction_allowed(&genesis, block_number, &basic_tx.clone().sign(key2.secret(), None), &*client)); - assert!(!filter.transaction_allowed(&genesis, block_number, &create_tx.clone().sign(key2.secret(), None), &*client)); - assert!(filter.transaction_allowed(&genesis, block_number, &call_tx.clone().sign(key2.secret(), None), &*client)); - - assert!(filter.transaction_allowed(&genesis, block_number, &basic_tx.clone().sign(key3.secret(), None), &*client)); - assert!(!filter.transaction_allowed(&genesis, block_number, &create_tx.clone().sign(key3.secret(), None), &*client)); - assert!(!filter.transaction_allowed(&genesis, block_number, &call_tx.clone().sign(key3.secret(), None), &*client)); - - assert!(!filter.transaction_allowed(&genesis, block_number, &basic_tx.clone().sign(key4.secret(), None), &*client)); - assert!(!filter.transaction_allowed(&genesis, block_number, &create_tx.clone().sign(key4.secret(), None), &*client)); - assert!(!filter.transaction_allowed(&genesis, block_number, &call_tx.clone().sign(key4.secret(), None), &*client)); - - assert!(filter.transaction_allowed(&genesis, block_number, &basic_tx.clone().sign(key1.secret(), None), &*client)); - assert!(filter.transaction_allowed(&genesis, block_number, &create_tx.clone().sign(key1.secret(), None), &*client)); - assert!(filter.transaction_allowed(&genesis, block_number, &call_tx.clone().sign(key1.secret(), None), &*client)); - - assert!(!filter.transaction_allowed(&genesis, block_number, &basic_tx_with_ether_and_to_key7.clone().sign(key5.secret(), None), &*client)); - assert!(!filter.transaction_allowed(&genesis, block_number, &call_tx_with_ether.clone().sign(key5.secret(), None), &*client)); - assert!(filter.transaction_allowed(&genesis, block_number, &basic_tx.clone().sign(key6.secret(), None), &*client)); - assert!(filter.transaction_allowed(&genesis, block_number, &basic_tx_with_ether_and_to_key7.clone().sign(key6.secret(), None), &*client)); - assert!(filter.transaction_allowed(&genesis, block_number, &basic_tx_to_key6.clone().sign(key7.secret(), None), &*client)); - assert!(!filter.transaction_allowed(&genesis, block_number, &basic_tx_with_ether_and_to_key6.clone().sign(key7.secret(), None), &*client)); - } - - /// Contract code: https://gist.github.com/arkpar/38a87cb50165b7e683585eec71acb05a - #[test] - fn transaction_filter_deprecated() { - let spec_data = include_str!("../res/tx_permission_tests/deprecated_contract_genesis.json"); - - let db = test_helpers::new_db(); - let tempdir = TempDir::new("").unwrap(); - let spec = Spec::load(&tempdir.path(), spec_data.as_bytes()).unwrap(); - - let client = Client::new( - ClientConfig::default(), - &spec, - db, - Arc::new(Miner::new_for_tests(&spec, None)), - IoChannel::disconnected(), - ).unwrap(); - let key1 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000001")).unwrap(); - let key2 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000002")).unwrap(); - let key3 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000003")).unwrap(); - let key4 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000004")).unwrap(); - - let filter = TransactionFilter::from_params(spec.params()).unwrap(); - let mut basic_tx = Transaction::default(); - basic_tx.action = Action::Call(Address::from("000000000000000000000000000000000000032")); - let create_tx = Transaction::default(); - let mut call_tx = Transaction::default(); - call_tx.action = Action::Call(Address::from("0000000000000000000000000000000000000005")); - - let genesis = client.block_hash(BlockId::Latest).unwrap(); - let block_number = 1; - - assert!(!filter.transaction_allowed(&genesis, block_number, &create_tx.clone().sign(key2.secret(), None), &*client)); - // same tx but request is allowed because the contract only enables at block #1 - assert!(filter.transaction_allowed(&genesis, 0, &create_tx.clone().sign(key2.secret(), None), &*client)); - - assert!(filter.transaction_allowed(&genesis, block_number, &basic_tx.clone().sign(key1.secret(), None), &*client)); - assert!(filter.transaction_allowed(&genesis, block_number, &create_tx.clone().sign(key1.secret(), None), &*client)); - assert!(filter.transaction_allowed(&genesis, block_number, &call_tx.clone().sign(key1.secret(), None), &*client)); - - assert!(filter.transaction_allowed(&genesis, block_number, &basic_tx.clone().sign(key2.secret(), None), &*client)); - assert!(!filter.transaction_allowed(&genesis, block_number, &create_tx.clone().sign(key2.secret(), None), &*client)); - assert!(filter.transaction_allowed(&genesis, block_number, &call_tx.clone().sign(key2.secret(), None), &*client)); - - assert!(filter.transaction_allowed(&genesis, block_number, &basic_tx.clone().sign(key3.secret(), None), &*client)); - assert!(!filter.transaction_allowed(&genesis, block_number, &create_tx.clone().sign(key3.secret(), None), &*client)); - assert!(!filter.transaction_allowed(&genesis, block_number, &call_tx.clone().sign(key3.secret(), None), &*client)); - - assert!(!filter.transaction_allowed(&genesis, block_number, &basic_tx.clone().sign(key4.secret(), None), &*client)); - assert!(!filter.transaction_allowed(&genesis, block_number, &create_tx.clone().sign(key4.secret(), None), &*client)); - assert!(!filter.transaction_allowed(&genesis, block_number, &call_tx.clone().sign(key4.secret(), None), &*client)); - } + use super::TransactionFilter; + use client::{BlockChainClient, BlockId, Client, ClientConfig}; + use ethereum_types::{Address, U256}; + use ethkey::{KeyPair, Secret}; + use io::IoChannel; + use miner::Miner; + use spec::Spec; + use std::sync::Arc; + use tempdir::TempDir; + use test_helpers; + use types::transaction::{Action, Transaction}; + + /// Contract code: https://gist.github.com/VladLupashevskyi/84f18eabb1e4afadf572cf92af3e7e7f + #[test] + fn transaction_filter() { + let spec_data = include_str!("../res/tx_permission_tests/contract_ver_2_genesis.json"); + + let db = test_helpers::new_db(); + let tempdir = TempDir::new("").unwrap(); + let spec = Spec::load(&tempdir.path(), spec_data.as_bytes()).unwrap(); + + let client = Client::new( + ClientConfig::default(), + &spec, + db, + Arc::new(Miner::new_for_tests(&spec, None)), + IoChannel::disconnected(), + ) + .unwrap(); + let key1 = KeyPair::from_secret(Secret::from( + "0000000000000000000000000000000000000000000000000000000000000001", + )) + .unwrap(); + let key2 = KeyPair::from_secret(Secret::from( + "0000000000000000000000000000000000000000000000000000000000000002", + )) + .unwrap(); + let key3 = KeyPair::from_secret(Secret::from( + "0000000000000000000000000000000000000000000000000000000000000003", + )) + .unwrap(); + let key4 = KeyPair::from_secret(Secret::from( + "0000000000000000000000000000000000000000000000000000000000000004", + )) + .unwrap(); + let key5 = KeyPair::from_secret(Secret::from( + "0000000000000000000000000000000000000000000000000000000000000005", + )) + .unwrap(); + let key6 = KeyPair::from_secret(Secret::from( + "0000000000000000000000000000000000000000000000000000000000000006", + )) + .unwrap(); + let key7 = KeyPair::from_secret(Secret::from( + "0000000000000000000000000000000000000000000000000000000000000007", + )) + .unwrap(); + + let filter = TransactionFilter::from_params(spec.params()).unwrap(); + let mut basic_tx = Transaction::default(); + basic_tx.action = Action::Call(Address::from("d41c057fd1c78805aac12b0a94a405c0461a6fbb")); + let create_tx = Transaction::default(); + let mut call_tx = Transaction::default(); + call_tx.action = Action::Call(Address::from("0000000000000000000000000000000000000005")); + + let mut basic_tx_with_ether_and_to_key7 = Transaction::default(); + basic_tx_with_ether_and_to_key7.action = + Action::Call(Address::from("d41c057fd1c78805aac12b0a94a405c0461a6fbb")); + basic_tx_with_ether_and_to_key7.value = U256::from(123123); + let mut call_tx_with_ether = Transaction::default(); + call_tx_with_ether.action = + Action::Call(Address::from("0000000000000000000000000000000000000005")); + call_tx_with_ether.value = U256::from(123123); + + let mut basic_tx_to_key6 = Transaction::default(); + basic_tx_to_key6.action = + Action::Call(Address::from("e57bfe9f44b819898f47bf37e5af72a0783e1141")); + let mut basic_tx_with_ether_and_to_key6 = Transaction::default(); + basic_tx_with_ether_and_to_key6.action = + Action::Call(Address::from("e57bfe9f44b819898f47bf37e5af72a0783e1141")); + basic_tx_with_ether_and_to_key6.value = U256::from(123123); + + let genesis = client.block_hash(BlockId::Latest).unwrap(); + let block_number = 1; + + assert!(!filter.transaction_allowed( + &genesis, + block_number, + &create_tx.clone().sign(key2.secret(), None), + &*client + )); + // same tx but request is allowed because the contract only enables at block #1 + assert!(filter.transaction_allowed( + &genesis, + 0, + &create_tx.clone().sign(key2.secret(), None), + &*client + )); + + assert!(filter.transaction_allowed( + &genesis, + block_number, + &basic_tx.clone().sign(key1.secret(), None), + &*client + )); + assert!(filter.transaction_allowed( + &genesis, + block_number, + &create_tx.clone().sign(key1.secret(), None), + &*client + )); + assert!(filter.transaction_allowed( + &genesis, + block_number, + &call_tx.clone().sign(key1.secret(), None), + &*client + )); + + assert!(filter.transaction_allowed( + &genesis, + block_number, + &basic_tx.clone().sign(key2.secret(), None), + &*client + )); + assert!(!filter.transaction_allowed( + &genesis, + block_number, + &create_tx.clone().sign(key2.secret(), None), + &*client + )); + assert!(filter.transaction_allowed( + &genesis, + block_number, + &call_tx.clone().sign(key2.secret(), None), + &*client + )); + + assert!(filter.transaction_allowed( + &genesis, + block_number, + &basic_tx.clone().sign(key3.secret(), None), + &*client + )); + assert!(!filter.transaction_allowed( + &genesis, + block_number, + &create_tx.clone().sign(key3.secret(), None), + &*client + )); + assert!(!filter.transaction_allowed( + &genesis, + block_number, + &call_tx.clone().sign(key3.secret(), None), + &*client + )); + + assert!(!filter.transaction_allowed( + &genesis, + block_number, + &basic_tx.clone().sign(key4.secret(), None), + &*client + )); + assert!(!filter.transaction_allowed( + &genesis, + block_number, + &create_tx.clone().sign(key4.secret(), None), + &*client + )); + assert!(!filter.transaction_allowed( + &genesis, + block_number, + &call_tx.clone().sign(key4.secret(), None), + &*client + )); + + assert!(filter.transaction_allowed( + &genesis, + block_number, + &basic_tx.clone().sign(key1.secret(), None), + &*client + )); + assert!(filter.transaction_allowed( + &genesis, + block_number, + &create_tx.clone().sign(key1.secret(), None), + &*client + )); + assert!(filter.transaction_allowed( + &genesis, + block_number, + &call_tx.clone().sign(key1.secret(), None), + &*client + )); + + assert!(!filter.transaction_allowed( + &genesis, + block_number, + &basic_tx_with_ether_and_to_key7 + .clone() + .sign(key5.secret(), None), + &*client + )); + assert!(!filter.transaction_allowed( + &genesis, + block_number, + &call_tx_with_ether.clone().sign(key5.secret(), None), + &*client + )); + assert!(filter.transaction_allowed( + &genesis, + block_number, + &basic_tx.clone().sign(key6.secret(), None), + &*client + )); + assert!(filter.transaction_allowed( + &genesis, + block_number, + &basic_tx_with_ether_and_to_key7 + .clone() + .sign(key6.secret(), None), + &*client + )); + assert!(filter.transaction_allowed( + &genesis, + block_number, + &basic_tx_to_key6.clone().sign(key7.secret(), None), + &*client + )); + assert!(!filter.transaction_allowed( + &genesis, + block_number, + &basic_tx_with_ether_and_to_key6 + .clone() + .sign(key7.secret(), None), + &*client + )); + } + + /// Contract code: https://gist.github.com/arkpar/38a87cb50165b7e683585eec71acb05a + #[test] + fn transaction_filter_deprecated() { + let spec_data = include_str!("../res/tx_permission_tests/deprecated_contract_genesis.json"); + + let db = test_helpers::new_db(); + let tempdir = TempDir::new("").unwrap(); + let spec = Spec::load(&tempdir.path(), spec_data.as_bytes()).unwrap(); + + let client = Client::new( + ClientConfig::default(), + &spec, + db, + Arc::new(Miner::new_for_tests(&spec, None)), + IoChannel::disconnected(), + ) + .unwrap(); + let key1 = KeyPair::from_secret(Secret::from( + "0000000000000000000000000000000000000000000000000000000000000001", + )) + .unwrap(); + let key2 = KeyPair::from_secret(Secret::from( + "0000000000000000000000000000000000000000000000000000000000000002", + )) + .unwrap(); + let key3 = KeyPair::from_secret(Secret::from( + "0000000000000000000000000000000000000000000000000000000000000003", + )) + .unwrap(); + let key4 = KeyPair::from_secret(Secret::from( + "0000000000000000000000000000000000000000000000000000000000000004", + )) + .unwrap(); + + let filter = TransactionFilter::from_params(spec.params()).unwrap(); + let mut basic_tx = Transaction::default(); + basic_tx.action = Action::Call(Address::from("000000000000000000000000000000000000032")); + let create_tx = Transaction::default(); + let mut call_tx = Transaction::default(); + call_tx.action = Action::Call(Address::from("0000000000000000000000000000000000000005")); + + let genesis = client.block_hash(BlockId::Latest).unwrap(); + let block_number = 1; + + assert!(!filter.transaction_allowed( + &genesis, + block_number, + &create_tx.clone().sign(key2.secret(), None), + &*client + )); + // same tx but request is allowed because the contract only enables at block #1 + assert!(filter.transaction_allowed( + &genesis, + 0, + &create_tx.clone().sign(key2.secret(), None), + &*client + )); + + assert!(filter.transaction_allowed( + &genesis, + block_number, + &basic_tx.clone().sign(key1.secret(), None), + &*client + )); + assert!(filter.transaction_allowed( + &genesis, + block_number, + &create_tx.clone().sign(key1.secret(), None), + &*client + )); + assert!(filter.transaction_allowed( + &genesis, + block_number, + &call_tx.clone().sign(key1.secret(), None), + &*client + )); + + assert!(filter.transaction_allowed( + &genesis, + block_number, + &basic_tx.clone().sign(key2.secret(), None), + &*client + )); + assert!(!filter.transaction_allowed( + &genesis, + block_number, + &create_tx.clone().sign(key2.secret(), None), + &*client + )); + assert!(filter.transaction_allowed( + &genesis, + block_number, + &call_tx.clone().sign(key2.secret(), None), + &*client + )); + + assert!(filter.transaction_allowed( + &genesis, + block_number, + &basic_tx.clone().sign(key3.secret(), None), + &*client + )); + assert!(!filter.transaction_allowed( + &genesis, + block_number, + &create_tx.clone().sign(key3.secret(), None), + &*client + )); + assert!(!filter.transaction_allowed( + &genesis, + block_number, + &call_tx.clone().sign(key3.secret(), None), + &*client + )); + + assert!(!filter.transaction_allowed( + &genesis, + block_number, + &basic_tx.clone().sign(key4.secret(), None), + &*client + )); + assert!(!filter.transaction_allowed( + &genesis, + block_number, + &create_tx.clone().sign(key4.secret(), None), + &*client + )); + assert!(!filter.transaction_allowed( + &genesis, + block_number, + &call_tx.clone().sign(key4.secret(), None), + &*client + )); + } } diff --git a/ethcore/src/verification/canon_verifier.rs b/ethcore/src/verification/canon_verifier.rs index 03a1c7155f8..a5089356ee5 100644 --- a/ethcore/src/verification/canon_verifier.rs +++ b/ethcore/src/verification/canon_verifier.rs @@ -1,48 +1,47 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Canonical verifier. +use super::{verification, Verifier}; use call_contract::CallContract; use client::BlockInfo; use engines::EthEngine; use error::Error; use types::header::Header; -use super::Verifier; -use super::verification; /// A canonial verifier -- this does full verification. pub struct CanonVerifier; impl Verifier for CanonVerifier { - fn verify_block_family( - &self, - header: &Header, - parent: &Header, - engine: &EthEngine, - do_full: Option>, - ) -> Result<(), Error> { - verification::verify_block_family(header, parent, engine, do_full) - } - - fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error> { - verification::verify_block_final(expected, got) - } - - fn verify_block_external(&self, header: &Header, engine: &EthEngine) -> Result<(), Error> { - engine.verify_block_external(header) - } + fn verify_block_family( + &self, + header: &Header, + parent: &Header, + engine: &dyn EthEngine, + do_full: Option>, + ) -> Result<(), Error> { + verification::verify_block_family(header, parent, engine, do_full) + } + + fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error> { + verification::verify_block_final(expected, got) + } + + fn verify_block_external(&self, header: &Header, engine: &dyn EthEngine) -> Result<(), Error> { + engine.verify_block_external(header) + } } diff --git a/ethcore/src/verification/mod.rs b/ethcore/src/verification/mod.rs index 5546bd60c91..3ee8f8503ad 100644 --- a/ethcore/src/verification/mod.rs +++ b/ethcore/src/verification/mod.rs @@ -1,32 +1,34 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Block verification utilities. -mod verification; -mod verifier; -pub mod queue; mod canon_verifier; mod noop_verifier; +pub mod queue; +mod verification; +mod verifier; -pub use self::verification::*; -pub use self::verifier::Verifier; -pub use self::canon_verifier::CanonVerifier; -pub use self::noop_verifier::NoopVerifier; -pub use self::queue::{BlockQueue, Config as QueueConfig, VerificationQueue, QueueInfo}; +pub use self::{ + canon_verifier::CanonVerifier, + noop_verifier::NoopVerifier, + queue::{BlockQueue, Config as QueueConfig, QueueInfo, VerificationQueue}, + verification::*, + verifier::Verifier, +}; use call_contract::CallContract; use client::BlockInfo; @@ -34,29 +36,29 @@ use client::BlockInfo; /// Verifier type. #[derive(Debug, PartialEq, Clone)] pub enum VerifierType { - /// Verifies block normally. - Canon, - /// Verifies block normallly, but skips seal verification. - CanonNoSeal, - /// Does not verify block at all. - /// Used in tests. - Noop, + /// Verifies block normally. + Canon, + /// Verifies block normallly, but skips seal verification. + CanonNoSeal, + /// Does not verify block at all. + /// Used in tests. + Noop, } /// Create a new verifier based on type. -pub fn new(v: VerifierType) -> Box> { - match v { - VerifierType::Canon | VerifierType::CanonNoSeal => Box::new(CanonVerifier), - VerifierType::Noop => Box::new(NoopVerifier), - } +pub fn new(v: VerifierType) -> Box> { + match v { + VerifierType::Canon | VerifierType::CanonNoSeal => Box::new(CanonVerifier), + VerifierType::Noop => Box::new(NoopVerifier), + } } impl VerifierType { - /// Check if seal verification is enabled for this verifier type. - pub fn verifying_seal(&self) -> bool { - match *self { - VerifierType::Canon => true, - VerifierType::Noop | VerifierType::CanonNoSeal => false, - } - } + /// Check if seal verification is enabled for this verifier type. + pub fn verifying_seal(&self) -> bool { + match *self { + VerifierType::Canon => true, + VerifierType::Noop | VerifierType::CanonNoSeal => false, + } + } } diff --git a/ethcore/src/verification/noop_verifier.rs b/ethcore/src/verification/noop_verifier.rs index d68f1eb8856..b4e1562cadf 100644 --- a/ethcore/src/verification/noop_verifier.rs +++ b/ethcore/src/verification/noop_verifier.rs @@ -1,48 +1,52 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! No-op verifier. +use super::{verification, Verifier}; use call_contract::CallContract; use client::BlockInfo; use engines::EthEngine; use error::Error; use types::header::Header; -use super::{verification, Verifier}; /// A no-op verifier -- this will verify everything it's given immediately. #[allow(dead_code)] pub struct NoopVerifier; impl Verifier for NoopVerifier { - fn verify_block_family( - &self, - _: &Header, - _t: &Header, - _: &EthEngine, - _: Option> - ) -> Result<(), Error> { - Ok(()) - } - - fn verify_block_final(&self, _expected: &Header, _got: &Header) -> Result<(), Error> { - Ok(()) - } - - fn verify_block_external(&self, _header: &Header, _engine: &EthEngine) -> Result<(), Error> { - Ok(()) - } + fn verify_block_family( + &self, + _: &Header, + _t: &Header, + _: &dyn EthEngine, + _: Option>, + ) -> Result<(), Error> { + Ok(()) + } + + fn verify_block_final(&self, _expected: &Header, _got: &Header) -> Result<(), Error> { + Ok(()) + } + + fn verify_block_external( + &self, + _header: &Header, + _engine: &dyn EthEngine, + ) -> Result<(), Error> { + Ok(()) + } } diff --git a/ethcore/src/verification/queue/kind.rs b/ethcore/src/verification/queue/kind.rs index dd9798c092b..0c5b42afec6 100644 --- a/ethcore/src/verification/queue/kind.rs +++ b/ethcore/src/verification/queue/kind.rs @@ -1,40 +1,42 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Definition of valid items for the verification queue. use engines::EthEngine; use error::Error; -use heapsize::HeapSizeOf; use ethereum_types::{H256, U256}; +use heapsize::HeapSizeOf; -pub use self::blocks::Blocks; -pub use self::headers::Headers; +pub use self::{blocks::Blocks, headers::Headers}; /// Something which can produce a hash and a parent hash. pub trait BlockLike { - /// Get the hash of this item. - fn hash(&self) -> H256; + /// Get the hash of this item - i.e. the header hash. + fn hash(&self) -> H256; - /// Get the hash of this item's parent. - fn parent_hash(&self) -> H256; + /// Get a raw hash of this item - i.e. the hash of the RLP representation. + fn raw_hash(&self) -> H256; - /// Get the difficulty of this item. - fn difficulty(&self) -> U256; + /// Get the hash of this item's parent. + fn parent_hash(&self) -> H256; + + /// Get the difficulty of this item. + fn difficulty(&self) -> U256; } /// Defines transitions between stages of verification. @@ -48,179 +50,221 @@ pub trait BlockLike { /// For correctness, the hashes produced by each stage of the pipeline should be /// consistent. pub trait Kind: 'static + Sized + Send + Sync { - /// The first stage: completely unverified. - type Input: Sized + Send + BlockLike + HeapSizeOf; - - /// The second stage: partially verified. - type Unverified: Sized + Send + BlockLike + HeapSizeOf; - - /// The third stage: completely verified. - type Verified: Sized + Send + BlockLike + HeapSizeOf; - - /// Attempt to create the `Unverified` item from the input. - fn create(input: Self::Input, engine: &EthEngine, check_seal: bool) -> Result; - - /// Attempt to verify the `Unverified` item using the given engine. - fn verify(unverified: Self::Unverified, engine: &EthEngine, check_seal: bool) -> Result; + /// The first stage: completely unverified. + type Input: Sized + Send + BlockLike + HeapSizeOf; + + /// The second stage: partially verified. + type Unverified: Sized + Send + BlockLike + HeapSizeOf; + + /// The third stage: completely verified. + type Verified: Sized + Send + BlockLike + HeapSizeOf; + + /// Attempt to create the `Unverified` item from the input. + fn create( + input: Self::Input, + engine: &dyn EthEngine, + check_seal: bool, + ) -> Result; + + /// Attempt to verify the `Unverified` item using the given engine. + fn verify( + unverified: Self::Unverified, + engine: &dyn EthEngine, + check_seal: bool, + ) -> Result; } /// The blocks verification module. pub mod blocks { - use super::{Kind, BlockLike}; - - use engines::EthEngine; - use error::{Error, ErrorKind, BlockError}; - use types::header::Header; - use verification::{PreverifiedBlock, verify_block_basic, verify_block_unordered}; - use types::transaction::UnverifiedTransaction; - - use heapsize::HeapSizeOf; - use ethereum_types::{H256, U256}; - use bytes::Bytes; - - /// A mode for verifying blocks. - pub struct Blocks; - - impl Kind for Blocks { - type Input = Unverified; - type Unverified = Unverified; - type Verified = PreverifiedBlock; - - fn create(input: Self::Input, engine: &EthEngine, check_seal: bool) -> Result { - match verify_block_basic(&input, engine, check_seal) { - Ok(()) => Ok(input), - Err(Error(ErrorKind::Block(BlockError::TemporarilyInvalid(oob)), _)) => { - debug!(target: "client", "Block received too early {}: {:?}", input.hash(), oob); - Err((input, BlockError::TemporarilyInvalid(oob).into())) - }, - Err(e) => { - warn!(target: "client", "Stage 1 block verification failed for {}: {:?}", input.hash(), e); - Err((input, e)) - } - } - } - - fn verify(un: Self::Unverified, engine: &EthEngine, check_seal: bool) -> Result { - let hash = un.hash(); - match verify_block_unordered(un, engine, check_seal) { - Ok(verified) => Ok(verified), - Err(e) => { - warn!(target: "client", "Stage 2 block verification failed for {}: {:?}", hash, e); - Err(e) - } - } - } - } - - /// An unverified block. - #[derive(PartialEq, Debug)] - pub struct Unverified { - /// Unverified block header. - pub header: Header, - /// Unverified block transactions. - pub transactions: Vec, - /// Unverified block uncles. - pub uncles: Vec
, - /// Raw block bytes. - pub bytes: Bytes, - } - - impl Unverified { - /// Create an `Unverified` from raw bytes. - pub fn from_rlp(bytes: Bytes) -> Result { - use rlp::Rlp; - let (header, transactions, uncles) = { - let rlp = Rlp::new(&bytes); - let header = rlp.val_at(0)?; - let transactions = rlp.list_at(1)?; - let uncles = rlp.list_at(2)?; - (header, transactions, uncles) - }; - - Ok(Unverified { - header, - transactions, - uncles, - bytes, - }) - } - } - - impl HeapSizeOf for Unverified { - fn heap_size_of_children(&self) -> usize { - self.header.heap_size_of_children() - + self.transactions.heap_size_of_children() - + self.uncles.heap_size_of_children() - + self.bytes.heap_size_of_children() - } - } - - impl BlockLike for Unverified { - fn hash(&self) -> H256 { - self.header.hash() - } - - fn parent_hash(&self) -> H256 { - self.header.parent_hash().clone() - } - - fn difficulty(&self) -> U256 { - self.header.difficulty().clone() - } - } - - impl BlockLike for PreverifiedBlock { - fn hash(&self) -> H256 { - self.header.hash() - } - - fn parent_hash(&self) -> H256 { - self.header.parent_hash().clone() - } - - fn difficulty(&self) -> U256 { - self.header.difficulty().clone() - } - } + use super::{BlockLike, Kind}; + + use engines::EthEngine; + use error::{BlockError, Error, ErrorKind}; + use types::{header::Header, transaction::UnverifiedTransaction}; + use verification::{verify_block_basic, verify_block_unordered, PreverifiedBlock}; + + use bytes::Bytes; + use ethereum_types::{H256, U256}; + use heapsize::HeapSizeOf; + + /// A mode for verifying blocks. + pub struct Blocks; + + impl Kind for Blocks { + type Input = Unverified; + type Unverified = Unverified; + type Verified = PreverifiedBlock; + + fn create( + input: Self::Input, + engine: &dyn EthEngine, + check_seal: bool, + ) -> Result { + match verify_block_basic(&input, engine, check_seal) { + Ok(()) => Ok(input), + Err(Error(ErrorKind::Block(BlockError::TemporarilyInvalid(oob)), _)) => { + debug!(target: "client", "Block received too early {}: {:?}", input.hash(), oob); + Err((input, BlockError::TemporarilyInvalid(oob).into())) + } + Err(e) => { + warn!(target: "client", "Stage 1 block verification failed for {}: {:?}", input.hash(), e); + Err((input, e)) + } + } + } + + fn verify( + un: Self::Unverified, + engine: &dyn EthEngine, + check_seal: bool, + ) -> Result { + let hash = un.hash(); + match verify_block_unordered(un, engine, check_seal) { + Ok(verified) => Ok(verified), + Err(e) => { + warn!(target: "client", "Stage 2 block verification failed for {}: {:?}", hash, e); + Err(e) + } + } + } + } + + /// An unverified block. + #[derive(PartialEq, Debug)] + pub struct Unverified { + /// Unverified block header. + pub header: Header, + /// Unverified block transactions. + pub transactions: Vec, + /// Unverified block uncles. + pub uncles: Vec
, + /// Raw block bytes. + pub bytes: Bytes, + } + + impl Unverified { + /// Create an `Unverified` from raw bytes. + pub fn from_rlp(bytes: Bytes) -> Result { + use rlp::Rlp; + let (header, transactions, uncles) = { + let rlp = Rlp::new(&bytes); + let header = rlp.val_at(0)?; + let transactions = rlp.list_at(1)?; + let uncles = rlp.list_at(2)?; + (header, transactions, uncles) + }; + + Ok(Unverified { + header, + transactions, + uncles, + bytes, + }) + } + } + + impl HeapSizeOf for Unverified { + fn heap_size_of_children(&self) -> usize { + self.header.heap_size_of_children() + + self.transactions.heap_size_of_children() + + self.uncles.heap_size_of_children() + + self.bytes.heap_size_of_children() + } + } + + impl BlockLike for Unverified { + fn hash(&self) -> H256 { + self.header.hash() + } + + fn raw_hash(&self) -> H256 { + hash::keccak(&self.bytes) + } + + fn parent_hash(&self) -> H256 { + self.header.parent_hash().clone() + } + + fn difficulty(&self) -> U256 { + self.header.difficulty().clone() + } + } + + impl BlockLike for PreverifiedBlock { + fn hash(&self) -> H256 { + self.header.hash() + } + + fn raw_hash(&self) -> H256 { + hash::keccak(&self.bytes) + } + + fn parent_hash(&self) -> H256 { + self.header.parent_hash().clone() + } + + fn difficulty(&self) -> U256 { + self.header.difficulty().clone() + } + } } /// Verification for headers. pub mod headers { - use super::{Kind, BlockLike}; - - use engines::EthEngine; - use error::Error; - use types::header::Header; - use verification::verify_header_params; - - use ethereum_types::{H256, U256}; - - impl BlockLike for Header { - fn hash(&self) -> H256 { self.hash() } - fn parent_hash(&self) -> H256 { self.parent_hash().clone() } - fn difficulty(&self) -> U256 { self.difficulty().clone() } - } - - /// A mode for verifying headers. - pub struct Headers; - - impl Kind for Headers { - type Input = Header; - type Unverified = Header; - type Verified = Header; - - fn create(input: Self::Input, engine: &EthEngine, check_seal: bool) -> Result { - match verify_header_params(&input, engine, true, check_seal) { - Ok(_) => Ok(input), - Err(err) => Err((input, err)) - } - } - - fn verify(unverified: Self::Unverified, engine: &EthEngine, check_seal: bool) -> Result { - match check_seal { - true => engine.verify_block_unordered(&unverified,).map(|_| unverified), - false => Ok(unverified), - } - } - } + use super::{BlockLike, Kind}; + + use engines::EthEngine; + use error::Error; + use types::header::Header; + use verification::verify_header_params; + + use ethereum_types::{H256, U256}; + + impl BlockLike for Header { + fn hash(&self) -> H256 { + self.hash() + } + fn raw_hash(&self) -> H256 { + self.hash() + } + fn parent_hash(&self) -> H256 { + self.parent_hash().clone() + } + fn difficulty(&self) -> U256 { + self.difficulty().clone() + } + } + + /// A mode for verifying headers. + pub struct Headers; + + impl Kind for Headers { + type Input = Header; + type Unverified = Header; + type Verified = Header; + + fn create( + input: Self::Input, + engine: &dyn EthEngine, + check_seal: bool, + ) -> Result { + match verify_header_params(&input, engine, true, check_seal) { + Ok(_) => Ok(input), + Err(err) => Err((input, err)), + } + } + + fn verify( + unverified: Self::Unverified, + engine: &dyn EthEngine, + check_seal: bool, + ) -> Result { + match check_seal { + true => engine + .verify_block_unordered(&unverified) + .map(|_| unverified), + false => Ok(unverified), + } + } + } } diff --git a/ethcore/src/verification/queue/mod.rs b/ethcore/src/verification/queue/mod.rs index 423121eefa9..1d39db9c27b 100644 --- a/ethcore/src/verification/queue/mod.rs +++ b/ethcore/src/verification/queue/mod.rs @@ -1,35 +1,41 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! A queue of blocks. Sits between network or other I/O and the `BlockChain`. //! Sorts them ready for blockchain insertion. -use std::thread::{self, JoinHandle}; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering}; -use std::sync::Arc; -use std::cmp; -use std::collections::{VecDeque, HashSet, HashMap}; -use heapsize::HeapSizeOf; +use blockchain::BlockChain; +use client::ClientIoMessage; +use engines::EthEngine; +use error::{BlockError, Error, ErrorKind, ImportErrorKind}; use ethereum_types::{H256, U256}; -use parking_lot::{Condvar, Mutex, RwLock}; +use heapsize::HeapSizeOf; use io::*; -use error::{BlockError, ImportErrorKind, ErrorKind, Error}; -use engines::EthEngine; -use client::ClientIoMessage; use len_caching_lock::LenCachingMutex; +use parking_lot::{Condvar, Mutex, RwLock}; +use std::{ + cmp, + collections::{HashMap, HashSet, VecDeque}, + iter::FromIterator, + sync::{ + atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering}, + Arc, + }, + thread::{self, JoinHandle}, +}; use self::kind::{BlockLike, Kind}; @@ -39,6 +45,9 @@ pub mod kind; const MIN_MEM_LIMIT: usize = 16384; const MIN_QUEUE_LIMIT: usize = 512; +/// Empiric estimation of the minimal length of the processing queue, +/// That definitely doesn't contain forks inside. +const MAX_QUEUE_WITH_FORK: usize = 8; /// Type alias for block queue convenience. pub type BlockQueue = VerificationQueue; @@ -49,900 +58,1018 @@ pub type HeaderQueue = VerificationQueue; /// Verification queue configuration #[derive(Debug, PartialEq, Clone)] pub struct Config { - /// Maximum number of items to keep in unverified queue. - /// When the limit is reached, is_full returns true. - pub max_queue_size: usize, - /// Maximum heap memory to use. - /// When the limit is reached, is_full returns true. - pub max_mem_use: usize, - /// Settings for the number of verifiers and adaptation strategy. - pub verifier_settings: VerifierSettings, + /// Maximum number of items to keep in unverified queue. + /// When the limit is reached, is_full returns true. + pub max_queue_size: usize, + /// Maximum heap memory to use. + /// When the limit is reached, is_full returns true. + pub max_mem_use: usize, + /// Settings for the number of verifiers and adaptation strategy. + pub verifier_settings: VerifierSettings, } impl Default for Config { - fn default() -> Self { - Config { - max_queue_size: 30000, - max_mem_use: 50 * 1024 * 1024, - verifier_settings: VerifierSettings::default(), - } - } + fn default() -> Self { + Config { + max_queue_size: 30000, + max_mem_use: 50 * 1024 * 1024, + verifier_settings: VerifierSettings::default(), + } + } } /// Verifier settings. #[derive(Debug, PartialEq, Clone)] pub struct VerifierSettings { - /// Whether to scale amount of verifiers according to load. - // Todo: replace w/ strategy enum? - pub scale_verifiers: bool, - /// Beginning amount of verifiers. - pub num_verifiers: usize, + /// Whether to scale amount of verifiers according to load. + // Todo: replace w/ strategy enum? + pub scale_verifiers: bool, + /// Beginning amount of verifiers. + pub num_verifiers: usize, + /// list of block and header hashes that will marked as bad and not included into chain. + pub bad_hashes: Vec, } impl Default for VerifierSettings { - fn default() -> Self { - VerifierSettings { - scale_verifiers: false, - num_verifiers: ::num_cpus::get(), - } - } + fn default() -> Self { + VerifierSettings { + scale_verifiers: false, + num_verifiers: ::num_cpus::get(), + bad_hashes: Vec::new(), + } + } } // pool states enum State { - // all threads with id < inner value are to work. - Work(usize), - Exit, + // all threads with id < inner value are to work. + Work(usize), + Exit, } /// An item which is in the process of being verified. pub struct Verifying { - hash: H256, - output: Option, + hash: H256, + output: Option, } impl HeapSizeOf for Verifying { - fn heap_size_of_children(&self) -> usize { - self.output.heap_size_of_children() - } + fn heap_size_of_children(&self) -> usize { + self.output.heap_size_of_children() + } } /// Status of items in the queue. pub enum Status { - /// Currently queued. - Queued, - /// Known to be bad. - Bad, - /// Unknown. - Unknown, + /// Currently queued. + Queued, + /// Known to be bad. + Bad, + /// Unknown. + Unknown, } impl Into<::types::block_status::BlockStatus> for Status { - fn into(self) -> ::types::block_status::BlockStatus { - use ::types::block_status::BlockStatus; - match self { - Status::Queued => BlockStatus::Queued, - Status::Bad => BlockStatus::Bad, - Status::Unknown => BlockStatus::Unknown, - } - } + fn into(self) -> ::types::block_status::BlockStatus { + use types::block_status::BlockStatus; + match self { + Status::Queued => BlockStatus::Queued, + Status::Bad => BlockStatus::Bad, + Status::Unknown => BlockStatus::Unknown, + } + } } // the internal queue sizes. struct Sizes { - unverified: AtomicUsize, - verifying: AtomicUsize, - verified: AtomicUsize, + unverified: AtomicUsize, + verifying: AtomicUsize, + verified: AtomicUsize, } /// A queue of items to be verified. Sits between network or other I/O and the `BlockChain`. /// Keeps them in the same order as inserted, minus invalid items. pub struct VerificationQueue { - engine: Arc, - more_to_verify: Arc, - verification: Arc>, - deleting: Arc, - ready_signal: Arc, - empty: Arc, - processing: RwLock>, // hash to difficulty - ticks_since_adjustment: AtomicUsize, - max_queue_size: usize, - max_mem_use: usize, - scale_verifiers: bool, - verifier_handles: Vec>, - state: Arc<(Mutex, Condvar)>, - total_difficulty: RwLock, + engine: Arc, + more_to_verify: Arc, + verification: Arc>, + deleting: Arc, + ready_signal: Arc, + empty: Arc, + processing: RwLock>, // item's hash to difficulty and parent item hash + ticks_since_adjustment: AtomicUsize, + max_queue_size: usize, + max_mem_use: usize, + scale_verifiers: bool, + verifier_handles: Vec>, + state: Arc<(Mutex, Condvar)>, + total_difficulty: RwLock, } struct QueueSignal { - deleting: Arc, - signalled: AtomicBool, - message_channel: Mutex>, + deleting: Arc, + signalled: AtomicBool, + message_channel: Mutex>, } impl QueueSignal { - fn set_sync(&self) { - // Do not signal when we are about to close - if self.deleting.load(AtomicOrdering::Relaxed) { - return; - } - - if self.signalled.compare_and_swap(false, true, AtomicOrdering::Relaxed) == false { - let channel = self.message_channel.lock().clone(); - if let Err(e) = channel.send_sync(ClientIoMessage::BlockVerified) { - debug!("Error sending BlockVerified message: {:?}", e); - } - } - } - - fn set_async(&self) { - // Do not signal when we are about to close - if self.deleting.load(AtomicOrdering::Relaxed) { - return; - } - - if self.signalled.compare_and_swap(false, true, AtomicOrdering::Relaxed) == false { - let channel = self.message_channel.lock().clone(); - if let Err(e) = channel.send(ClientIoMessage::BlockVerified) { - debug!("Error sending BlockVerified message: {:?}", e); - } - } - } - - fn reset(&self) { - self.signalled.store(false, AtomicOrdering::Relaxed); - } + fn set_sync(&self) { + // Do not signal when we are about to close + if self.deleting.load(AtomicOrdering::Relaxed) { + return; + } + + if self + .signalled + .compare_and_swap(false, true, AtomicOrdering::Relaxed) + == false + { + let channel = self.message_channel.lock().clone(); + if let Err(e) = channel.send_sync(ClientIoMessage::BlockVerified) { + debug!("Error sending BlockVerified message: {:?}", e); + } + } + } + + fn set_async(&self) { + // Do not signal when we are about to close + if self.deleting.load(AtomicOrdering::Relaxed) { + return; + } + + if self + .signalled + .compare_and_swap(false, true, AtomicOrdering::Relaxed) + == false + { + let channel = self.message_channel.lock().clone(); + if let Err(e) = channel.send(ClientIoMessage::BlockVerified) { + debug!("Error sending BlockVerified message: {:?}", e); + } + } + } + + fn reset(&self) { + self.signalled.store(false, AtomicOrdering::Relaxed); + } } struct Verification { - // All locks must be captured in the order declared here. - unverified: LenCachingMutex>, - verifying: LenCachingMutex>>, - verified: LenCachingMutex>, - bad: Mutex>, - sizes: Sizes, - check_seal: bool, + // All locks must be captured in the order declared here. + unverified: LenCachingMutex>, + verifying: LenCachingMutex>>, + verified: LenCachingMutex>, + bad: Mutex>, + sizes: Sizes, + check_seal: bool, } impl VerificationQueue { - /// Creates a new queue instance. - pub fn new(config: Config, engine: Arc, message_channel: IoChannel, check_seal: bool) -> Self { - let verification = Arc::new(Verification { - unverified: LenCachingMutex::new(VecDeque::new()), - verifying: LenCachingMutex::new(VecDeque::new()), - verified: LenCachingMutex::new(VecDeque::new()), - bad: Mutex::new(HashSet::new()), - sizes: Sizes { - unverified: AtomicUsize::new(0), - verifying: AtomicUsize::new(0), - verified: AtomicUsize::new(0), - }, - check_seal: check_seal, - }); - let more_to_verify = Arc::new(Condvar::new()); - let deleting = Arc::new(AtomicBool::new(false)); - let ready_signal = Arc::new(QueueSignal { - deleting: deleting.clone(), - signalled: AtomicBool::new(false), - message_channel: Mutex::new(message_channel), - }); - let empty = Arc::new(Condvar::new()); - let scale_verifiers = config.verifier_settings.scale_verifiers; - - let max_verifiers = ::num_cpus::get(); - let default_amount = cmp::max(1, cmp::min(max_verifiers, config.verifier_settings.num_verifiers)); - - // if `auto-scaling` is enabled spawn up extra threads as they might be needed - // otherwise just spawn the number of threads specified by the config - let number_of_threads = if scale_verifiers { - max_verifiers - } else { - cmp::min(default_amount, max_verifiers) - }; - - let state = Arc::new((Mutex::new(State::Work(default_amount)), Condvar::new())); - let mut verifier_handles = Vec::with_capacity(number_of_threads); - - debug!(target: "verification", "Allocating {} verifiers, {} initially active", number_of_threads, default_amount); - debug!(target: "verification", "Verifier auto-scaling {}", if scale_verifiers { "enabled" } else { "disabled" }); - - for i in 0..number_of_threads { - debug!(target: "verification", "Adding verification thread #{}", i); - - let verification = verification.clone(); - let engine = engine.clone(); - let wait = more_to_verify.clone(); - let ready = ready_signal.clone(); - let empty = empty.clone(); - let state = state.clone(); - - let handle = thread::Builder::new() - .name(format!("Verifier #{}", i)) - .spawn(move || { - VerificationQueue::verify( - verification, - engine, - wait, - ready, - empty, - state, - i, - ) - }) - .expect("Failed to create verifier thread."); - verifier_handles.push(handle); - } - - VerificationQueue { - engine: engine, - ready_signal: ready_signal, - more_to_verify: more_to_verify, - verification: verification, - deleting: deleting, - processing: RwLock::new(HashMap::new()), - empty: empty, - ticks_since_adjustment: AtomicUsize::new(0), - max_queue_size: cmp::max(config.max_queue_size, MIN_QUEUE_LIMIT), - max_mem_use: cmp::max(config.max_mem_use, MIN_MEM_LIMIT), - scale_verifiers: scale_verifiers, - verifier_handles: verifier_handles, - state: state, - total_difficulty: RwLock::new(0.into()), - } - } - - fn verify( - verification: Arc>, - engine: Arc, - wait: Arc, - ready: Arc, - empty: Arc, - state: Arc<(Mutex, Condvar)>, - id: usize, - ) { - loop { - // check current state. - { - let mut cur_state = state.0.lock(); - while let State::Work(x) = *cur_state { - // sleep until this thread is required. - if id < x { break } - - debug!(target: "verification", "verifier {} sleeping", id); - state.1.wait(&mut cur_state); - debug!(target: "verification", "verifier {} waking up", id); - } - - if let State::Exit = *cur_state { - debug!(target: "verification", "verifier {} exiting", id); - break; - } - } - - // wait for work if empty. - { - let mut unverified = verification.unverified.lock(); - - if unverified.is_empty() && verification.verifying.lock().is_empty() { - empty.notify_all(); - } - - while unverified.is_empty() { - if let State::Exit = *state.0.lock() { - debug!(target: "verification", "verifier {} exiting", id); - return; - } - - wait.wait(unverified.inner_mut()); - } - - if let State::Exit = *state.0.lock() { - debug!(target: "verification", "verifier {} exiting", id); - return; - } - } - - // do work. - let item = { - // acquire these locks before getting the item to verify. - let mut unverified = verification.unverified.lock(); - let mut verifying = verification.verifying.lock(); - - let item = match unverified.pop_front() { - Some(item) => item, - None => continue, - }; - - verification.sizes.unverified.fetch_sub(item.heap_size_of_children(), AtomicOrdering::SeqCst); - verifying.push_back(Verifying { hash: item.hash(), output: None }); - item - }; - - let hash = item.hash(); - let is_ready = match K::verify(item, &*engine, verification.check_seal) { - Ok(verified) => { - let mut verifying = verification.verifying.lock(); - let mut idx = None; - for (i, e) in verifying.iter_mut().enumerate() { - if e.hash == hash { - idx = Some(i); - - verification.sizes.verifying.fetch_add(verified.heap_size_of_children(), AtomicOrdering::SeqCst); - e.output = Some(verified); - break; - } - } - - if idx == Some(0) { - // we're next! - let mut verified = verification.verified.lock(); - let mut bad = verification.bad.lock(); - VerificationQueue::drain_verifying(&mut verifying, &mut verified, &mut bad, &verification.sizes); - true - } else { - false - } - }, - Err(_) => { - let mut verifying = verification.verifying.lock(); - let mut verified = verification.verified.lock(); - let mut bad = verification.bad.lock(); - - bad.insert(hash.clone()); - verifying.retain(|e| e.hash != hash); - - if verifying.front().map_or(false, |x| x.output.is_some()) { - VerificationQueue::drain_verifying(&mut verifying, &mut verified, &mut bad, &verification.sizes); - true - } else { - false - } - } - }; - if is_ready { - // Import the block immediately - ready.set_sync(); - } - } - } - - fn drain_verifying( - verifying: &mut VecDeque>, - verified: &mut VecDeque, - bad: &mut HashSet, - sizes: &Sizes, - ) { - let mut removed_size = 0; - let mut inserted_size = 0; - - while let Some(output) = verifying.front_mut().and_then(|x| x.output.take()) { - assert!(verifying.pop_front().is_some()); - let size = output.heap_size_of_children(); - removed_size += size; - - if bad.contains(&output.parent_hash()) { - bad.insert(output.hash()); - } else { - inserted_size += size; - verified.push_back(output); - } - } - - sizes.verifying.fetch_sub(removed_size, AtomicOrdering::SeqCst); - sizes.verified.fetch_add(inserted_size, AtomicOrdering::SeqCst); - } - - /// Clear the queue and stop verification activity. - pub fn clear(&self) { - let mut unverified = self.verification.unverified.lock(); - let mut verifying = self.verification.verifying.lock(); - let mut verified = self.verification.verified.lock(); - unverified.clear(); - verifying.clear(); - verified.clear(); - - let sizes = &self.verification.sizes; - sizes.unverified.store(0, AtomicOrdering::Release); - sizes.verifying.store(0, AtomicOrdering::Release); - sizes.verified.store(0, AtomicOrdering::Release); - *self.total_difficulty.write() = 0.into(); - - self.processing.write().clear(); - } - - /// Wait for unverified queue to be empty - pub fn flush(&self) { - let mut unverified = self.verification.unverified.lock(); - while !unverified.is_empty() || !self.verification.verifying.lock().is_empty() { - self.empty.wait(unverified.inner_mut()); - } - } - - /// Check if the item is currently in the queue - pub fn status(&self, hash: &H256) -> Status { - if self.processing.read().contains_key(hash) { - return Status::Queued; - } - if self.verification.bad.lock().contains(hash) { - return Status::Bad; - } - Status::Unknown - } - - /// Add a block to the queue. - pub fn import(&self, input: K::Input) -> Result { - let hash = input.hash(); - { - if self.processing.read().contains_key(&hash) { - bail!((input, ErrorKind::Import(ImportErrorKind::AlreadyQueued).into())); - } - - let mut bad = self.verification.bad.lock(); - if bad.contains(&hash) { - bail!((input, ErrorKind::Import(ImportErrorKind::KnownBad).into())); - } - - if bad.contains(&input.parent_hash()) { - bad.insert(hash); - bail!((input, ErrorKind::Import(ImportErrorKind::KnownBad).into())); - } - } - - match K::create(input, &*self.engine, self.verification.check_seal) { - Ok(item) => { - self.verification.sizes.unverified.fetch_add(item.heap_size_of_children(), AtomicOrdering::SeqCst); - - self.processing.write().insert(hash, item.difficulty()); - { - let mut td = self.total_difficulty.write(); - *td = *td + item.difficulty(); - } - self.verification.unverified.lock().push_back(item); - self.more_to_verify.notify_all(); - Ok(hash) - }, - Err((input, err)) => { - match err { - // Don't mark future blocks as bad. - Error(ErrorKind::Block(BlockError::TemporarilyInvalid(_)), _) => {}, - _ => { - self.verification.bad.lock().insert(hash); - } - } - Err((input, err)) - } - } - } - - /// Mark given item and all its children as bad. pauses verification - /// until complete. - pub fn mark_as_bad(&self, hashes: &[H256]) { - if hashes.is_empty() { - return; - } - let mut verified_lock = self.verification.verified.lock(); - let verified = &mut *verified_lock; - let mut bad = self.verification.bad.lock(); - let mut processing = self.processing.write(); - bad.reserve(hashes.len()); - for hash in hashes { - bad.insert(hash.clone()); - if let Some(difficulty) = processing.remove(hash) { - let mut td = self.total_difficulty.write(); - *td = *td - difficulty; - } - } - - let mut new_verified = VecDeque::new(); - let mut removed_size = 0; - for output in verified.drain(..) { - if bad.contains(&output.parent_hash()) { - removed_size += output.heap_size_of_children(); - bad.insert(output.hash()); - if let Some(difficulty) = processing.remove(&output.hash()) { - let mut td = self.total_difficulty.write(); - *td = *td - difficulty; - } - } else { - new_verified.push_back(output); - } - } - - self.verification.sizes.verified.fetch_sub(removed_size, AtomicOrdering::SeqCst); - *verified = new_verified; - } - - /// Mark given item as processed. - /// Returns true if the queue becomes empty. - pub fn mark_as_good(&self, hashes: &[H256]) -> bool { - if hashes.is_empty() { - return self.processing.read().is_empty(); - } - let mut processing = self.processing.write(); - for hash in hashes { - if let Some(difficulty) = processing.remove(hash) { - let mut td = self.total_difficulty.write(); - *td = *td - difficulty; - } - } - processing.is_empty() - } - - /// Removes up to `max` verified items from the queue - pub fn drain(&self, max: usize) -> Vec { - let mut verified = self.verification.verified.lock(); - let count = cmp::min(max, verified.len()); - let result = verified.drain(..count).collect::>(); - - let drained_size = result.iter().map(HeapSizeOf::heap_size_of_children).fold(0, |a, c| a + c); - self.verification.sizes.verified.fetch_sub(drained_size, AtomicOrdering::SeqCst); - - self.ready_signal.reset(); - if !verified.is_empty() { - self.ready_signal.set_async(); - } - result - } - - /// Returns true if there is nothing currently in the queue. - pub fn is_empty(&self) -> bool { - let v = &self.verification; - - v.unverified.load_len() == 0 - && v.verifying.load_len() == 0 - && v.verified.load_len() == 0 - } - - /// Get queue status. - pub fn queue_info(&self) -> QueueInfo { - use std::mem::size_of; - - let (unverified_len, unverified_bytes) = { - let len = self.verification.unverified.load_len(); - let size = self.verification.sizes.unverified.load(AtomicOrdering::Acquire); - - (len, size + len * size_of::()) - }; - let (verifying_len, verifying_bytes) = { - let len = self.verification.verifying.load_len(); - let size = self.verification.sizes.verifying.load(AtomicOrdering::Acquire); - (len, size + len * size_of::>()) - }; - let (verified_len, verified_bytes) = { - let len = self.verification.verified.load_len(); - let size = self.verification.sizes.verified.load(AtomicOrdering::Acquire); - (len, size + len * size_of::()) - }; - - QueueInfo { - unverified_queue_size: unverified_len, - verifying_queue_size: verifying_len, - verified_queue_size: verified_len, - max_queue_size: self.max_queue_size, - max_mem_use: self.max_mem_use, - mem_used: unverified_bytes - + verifying_bytes - + verified_bytes - } - } - - /// Get the total difficulty of all the blocks in the queue. - pub fn total_difficulty(&self) -> U256 { - self.total_difficulty.read().clone() - } - - /// Get the current number of working verifiers. - pub fn num_verifiers(&self) -> usize { - match *self.state.0.lock() { - State::Work(x) => x, - State::Exit => panic!("state only set to exit on drop; queue live now; qed"), - } - } - - /// Optimise memory footprint of the heap fields, and adjust the number of threads - /// to better suit the workload. - pub fn collect_garbage(&self) { - // number of ticks to average queue stats over - // when deciding whether to change the number of verifiers. - #[cfg(not(test))] - const READJUSTMENT_PERIOD: usize = 12; - - #[cfg(test)] - const READJUSTMENT_PERIOD: usize = 1; - - let (u_len, v_len) = { - let u_len = { - let mut q = self.verification.unverified.lock(); - q.shrink_to_fit(); - q.len() - }; - self.verification.verifying.lock().shrink_to_fit(); - - let v_len = { - let mut q = self.verification.verified.lock(); - q.shrink_to_fit(); - q.len() - }; - - (u_len as isize, v_len as isize) - }; - - self.processing.write().shrink_to_fit(); - - if !self.scale_verifiers { return } - - if self.ticks_since_adjustment.fetch_add(1, AtomicOrdering::SeqCst) + 1 >= READJUSTMENT_PERIOD { - self.ticks_since_adjustment.store(0, AtomicOrdering::SeqCst); - } else { - return; - } - - let current = self.num_verifiers(); - - let diff = (v_len - u_len).abs(); - let total = v_len + u_len; - - self.scale_verifiers( - if u_len < 20 { - 1 - } else if diff <= total / 10 { - current - } else if v_len > u_len { - current - 1 - } else { - current + 1 - } - ); - } - - // wake up or sleep verifiers to get as close to the target as - // possible, never going over the amount of initially allocated threads - // or below 1. - fn scale_verifiers(&self, target: usize) { - let current = self.num_verifiers(); - let target = cmp::min(self.verifier_handles.len(), target); - let target = cmp::max(1, target); - - debug!(target: "verification", "Scaling from {} to {} verifiers", current, target); - - *self.state.0.lock() = State::Work(target); - self.state.1.notify_all(); - } + /// Creates a new queue instance. + pub fn new( + config: Config, + engine: Arc, + message_channel: IoChannel, + check_seal: bool, + ) -> Self { + let verification = Arc::new(Verification { + unverified: LenCachingMutex::new(VecDeque::new()), + verifying: LenCachingMutex::new(VecDeque::new()), + verified: LenCachingMutex::new(VecDeque::new()), + bad: Mutex::new(HashSet::from_iter(config.verifier_settings.bad_hashes)), + sizes: Sizes { + unverified: AtomicUsize::new(0), + verifying: AtomicUsize::new(0), + verified: AtomicUsize::new(0), + }, + check_seal: check_seal, + }); + let more_to_verify = Arc::new(Condvar::new()); + let deleting = Arc::new(AtomicBool::new(false)); + let ready_signal = Arc::new(QueueSignal { + deleting: deleting.clone(), + signalled: AtomicBool::new(false), + message_channel: Mutex::new(message_channel), + }); + let empty = Arc::new(Condvar::new()); + let scale_verifiers = config.verifier_settings.scale_verifiers; + + let max_verifiers = ::num_cpus::get(); + let default_amount = cmp::max( + 1, + cmp::min(max_verifiers, config.verifier_settings.num_verifiers), + ); + + // if `auto-scaling` is enabled spawn up extra threads as they might be needed + // otherwise just spawn the number of threads specified by the config + let number_of_threads = if scale_verifiers { + max_verifiers + } else { + cmp::min(default_amount, max_verifiers) + }; + + let state = Arc::new((Mutex::new(State::Work(default_amount)), Condvar::new())); + let mut verifier_handles = Vec::with_capacity(number_of_threads); + + debug!(target: "verification", "Allocating {} verifiers, {} initially active", number_of_threads, default_amount); + debug!(target: "verification", "Verifier auto-scaling {}", if scale_verifiers { "enabled" } else { "disabled" }); + + for i in 0..number_of_threads { + debug!(target: "verification", "Adding verification thread #{}", i); + + let verification = verification.clone(); + let engine = engine.clone(); + let wait = more_to_verify.clone(); + let ready = ready_signal.clone(); + let empty = empty.clone(); + let state = state.clone(); + + let handle = thread::Builder::new() + .name(format!("Verifier #{}", i)) + .spawn(move || { + VerificationQueue::verify(verification, engine, wait, ready, empty, state, i) + }) + .expect("Failed to create verifier thread."); + verifier_handles.push(handle); + } + + VerificationQueue { + engine: engine, + ready_signal: ready_signal, + more_to_verify: more_to_verify, + verification: verification, + deleting: deleting, + processing: RwLock::new(HashMap::new()), + empty: empty, + ticks_since_adjustment: AtomicUsize::new(0), + max_queue_size: cmp::max(config.max_queue_size, MIN_QUEUE_LIMIT), + max_mem_use: cmp::max(config.max_mem_use, MIN_MEM_LIMIT), + scale_verifiers: scale_verifiers, + verifier_handles: verifier_handles, + state: state, + total_difficulty: RwLock::new(0.into()), + } + } + + fn verify( + verification: Arc>, + engine: Arc, + wait: Arc, + ready: Arc, + empty: Arc, + state: Arc<(Mutex, Condvar)>, + id: usize, + ) { + loop { + // check current state. + { + let mut cur_state = state.0.lock(); + while let State::Work(x) = *cur_state { + // sleep until this thread is required. + if id < x { + break; + } + + debug!(target: "verification", "verifier {} sleeping", id); + state.1.wait(&mut cur_state); + debug!(target: "verification", "verifier {} waking up", id); + } + + if let State::Exit = *cur_state { + debug!(target: "verification", "verifier {} exiting", id); + break; + } + } + + // wait for work if empty. + { + let mut unverified = verification.unverified.lock(); + + if unverified.is_empty() && verification.verifying.lock().is_empty() { + empty.notify_all(); + } + + while unverified.is_empty() { + if let State::Exit = *state.0.lock() { + debug!(target: "verification", "verifier {} exiting", id); + return; + } + + wait.wait(unverified.inner_mut()); + } + + if let State::Exit = *state.0.lock() { + debug!(target: "verification", "verifier {} exiting", id); + return; + } + } + + // do work. + let item = { + // acquire these locks before getting the item to verify. + let mut unverified = verification.unverified.lock(); + let mut verifying = verification.verifying.lock(); + + let item = match unverified.pop_front() { + Some(item) => item, + None => continue, + }; + + verification + .sizes + .unverified + .fetch_sub(item.heap_size_of_children(), AtomicOrdering::SeqCst); + verifying.push_back(Verifying { + hash: item.hash(), + output: None, + }); + item + }; + + let hash = item.hash(); + let is_ready = match K::verify(item, &*engine, verification.check_seal) { + Ok(verified) => { + let mut verifying = verification.verifying.lock(); + let mut idx = None; + for (i, e) in verifying.iter_mut().enumerate() { + if e.hash == hash { + idx = Some(i); + + verification.sizes.verifying.fetch_add( + verified.heap_size_of_children(), + AtomicOrdering::SeqCst, + ); + e.output = Some(verified); + break; + } + } + + if idx == Some(0) { + // we're next! + let mut verified = verification.verified.lock(); + let mut bad = verification.bad.lock(); + VerificationQueue::drain_verifying( + &mut verifying, + &mut verified, + &mut bad, + &verification.sizes, + ); + true + } else { + false + } + } + Err(_) => { + let mut verifying = verification.verifying.lock(); + let mut verified = verification.verified.lock(); + let mut bad = verification.bad.lock(); + + bad.insert(hash.clone()); + verifying.retain(|e| e.hash != hash); + + if verifying.front().map_or(false, |x| x.output.is_some()) { + VerificationQueue::drain_verifying( + &mut verifying, + &mut verified, + &mut bad, + &verification.sizes, + ); + true + } else { + false + } + } + }; + if is_ready { + // Import the block immediately + ready.set_sync(); + } + } + } + + fn drain_verifying( + verifying: &mut VecDeque>, + verified: &mut VecDeque, + bad: &mut HashSet, + sizes: &Sizes, + ) { + let mut removed_size = 0; + let mut inserted_size = 0; + + while let Some(output) = verifying.front_mut().and_then(|x| x.output.take()) { + assert!(verifying.pop_front().is_some()); + let size = output.heap_size_of_children(); + removed_size += size; + + if bad.contains(&output.parent_hash()) { + bad.insert(output.hash()); + } else { + inserted_size += size; + verified.push_back(output); + } + } + + sizes + .verifying + .fetch_sub(removed_size, AtomicOrdering::SeqCst); + sizes + .verified + .fetch_add(inserted_size, AtomicOrdering::SeqCst); + } + + /// Clear the queue and stop verification activity. + pub fn clear(&self) { + let mut unverified = self.verification.unverified.lock(); + let mut verifying = self.verification.verifying.lock(); + let mut verified = self.verification.verified.lock(); + unverified.clear(); + verifying.clear(); + verified.clear(); + + let sizes = &self.verification.sizes; + sizes.unverified.store(0, AtomicOrdering::Release); + sizes.verifying.store(0, AtomicOrdering::Release); + sizes.verified.store(0, AtomicOrdering::Release); + *self.total_difficulty.write() = 0.into(); + + self.processing.write().clear(); + } + + /// Wait for unverified queue to be empty + pub fn flush(&self) { + let mut unverified = self.verification.unverified.lock(); + while !unverified.is_empty() || !self.verification.verifying.lock().is_empty() { + self.empty.wait(unverified.inner_mut()); + } + } + + /// Check if the item is currently in the queue + pub fn status(&self, hash: &H256) -> Status { + if self.processing.read().contains_key(hash) { + return Status::Queued; + } + if self.verification.bad.lock().contains(hash) { + return Status::Bad; + } + Status::Unknown + } + + /// Add a block to the queue. + pub fn import(&self, input: K::Input) -> Result, Error)> { + let hash = input.hash(); + let raw_hash = input.raw_hash(); + { + if self.processing.read().contains_key(&hash) { + bail!(( + Some(input), + ErrorKind::Import(ImportErrorKind::AlreadyQueued).into() + )); + } + + let mut bad = self.verification.bad.lock(); + if bad.contains(&hash) || bad.contains(&raw_hash) { + bail!(( + Some(input), + ErrorKind::Import(ImportErrorKind::KnownBad).into() + )); + } + + if bad.contains(&input.parent_hash()) { + bad.insert(hash); + bail!(( + Some(input), + ErrorKind::Import(ImportErrorKind::KnownBad).into() + )); + } + } + + match K::create(input, &*self.engine, self.verification.check_seal) { + Ok(item) => { + if self + .processing + .write() + .insert(hash, (item.difficulty(), item.parent_hash())) + .is_some() + { + bail!(( + None, + ErrorKind::Import(ImportErrorKind::AlreadyQueued).into() + )); + } + self.verification + .sizes + .unverified + .fetch_add(item.heap_size_of_children(), AtomicOrdering::SeqCst); + + //self.processing.write().insert(hash, item.difficulty()); + { + let mut td = self.total_difficulty.write(); + *td = *td + item.difficulty(); + } + self.verification.unverified.lock().push_back(item); + self.more_to_verify.notify_all(); + Ok(hash) + } + Err((input, err)) => { + match err { + // Don't mark future blocks as bad. + Error(ErrorKind::Block(BlockError::TemporarilyInvalid(_)), _) => {} + // If the transaction root or uncles hash is invalid, it doesn't necessarily mean + // that the header is invalid. We might have just received a malformed block body, + // so we shouldn't put the header hash to `bad`. + // + // We still put the entire `Item` hash to bad, so that we can early reject + // the items that are malformed. + Error(ErrorKind::Block(BlockError::InvalidTransactionsRoot(_)), _) + | Error(ErrorKind::Block(BlockError::InvalidUnclesHash(_)), _) => { + self.verification.bad.lock().insert(raw_hash); + } + _ => { + self.verification.bad.lock().insert(hash); + } + } + Err((Some(input), err)) + } + } + } + + /// Mark given item and all its children as bad. pauses verification + /// until complete. + pub fn mark_as_bad(&self, hashes: &[H256]) { + if hashes.is_empty() { + return; + } + let mut verified_lock = self.verification.verified.lock(); + let verified = &mut *verified_lock; + let mut bad = self.verification.bad.lock(); + let mut processing = self.processing.write(); + bad.reserve(hashes.len()); + for hash in hashes { + bad.insert(hash.clone()); + if let Some((difficulty, _)) = processing.remove(hash) { + let mut td = self.total_difficulty.write(); + *td = *td - difficulty; + } + } + + let mut new_verified = VecDeque::new(); + let mut removed_size = 0; + for output in verified.drain(..) { + if bad.contains(&output.parent_hash()) { + removed_size += output.heap_size_of_children(); + bad.insert(output.hash()); + if let Some((difficulty, _)) = processing.remove(&output.hash()) { + let mut td = self.total_difficulty.write(); + *td = *td - difficulty; + } + } else { + new_verified.push_back(output); + } + } + + self.verification + .sizes + .verified + .fetch_sub(removed_size, AtomicOrdering::SeqCst); + *verified = new_verified; + } + + /// Mark given item as processed. + /// Returns true if the queue becomes empty. + pub fn mark_as_good(&self, hashes: &[H256]) -> bool { + if hashes.is_empty() { + return self.processing.read().is_empty(); + } + let mut processing = self.processing.write(); + for hash in hashes { + if let Some((difficulty, _)) = processing.remove(hash) { + let mut td = self.total_difficulty.write(); + *td = *td - difficulty; + } + } + processing.is_empty() + } + + /// Removes up to `max` verified items from the queue + pub fn drain(&self, max: usize) -> Vec { + let mut verified = self.verification.verified.lock(); + let count = cmp::min(max, verified.len()); + let result = verified.drain(..count).collect::>(); + + let drained_size = result + .iter() + .map(HeapSizeOf::heap_size_of_children) + .fold(0, |a, c| a + c); + self.verification + .sizes + .verified + .fetch_sub(drained_size, AtomicOrdering::SeqCst); + + self.ready_signal.reset(); + if !verified.is_empty() { + self.ready_signal.set_async(); + } + result + } + + /// Returns true if there is nothing currently in the queue. + pub fn is_empty(&self) -> bool { + let v = &self.verification; + + v.unverified.load_len() == 0 && v.verifying.load_len() == 0 && v.verified.load_len() == 0 + } + + /// Returns true if there are descendants of the current best block in the processing queue + pub fn is_processing_fork(&self, best_block_hash: &H256, chain: &BlockChain) -> bool { + let processing = self.processing.read(); + if processing.is_empty() || processing.len() > MAX_QUEUE_WITH_FORK { + // Assume, that long enough processing queue doesn't have fork blocks + return false; + } + for (_, item_parent_hash) in processing.values() { + if chain + .tree_route(*best_block_hash, *item_parent_hash) + .map_or(true, |route| route.ancestor != *best_block_hash) + { + return true; + } + } + false + } + + /// Get queue status. + pub fn queue_info(&self) -> QueueInfo { + use std::mem::size_of; + + let (unverified_len, unverified_bytes) = { + let len = self.verification.unverified.load_len(); + let size = self + .verification + .sizes + .unverified + .load(AtomicOrdering::Acquire); + + (len, size + len * size_of::()) + }; + let (verifying_len, verifying_bytes) = { + let len = self.verification.verifying.load_len(); + let size = self + .verification + .sizes + .verifying + .load(AtomicOrdering::Acquire); + (len, size + len * size_of::>()) + }; + let (verified_len, verified_bytes) = { + let len = self.verification.verified.load_len(); + let size = self + .verification + .sizes + .verified + .load(AtomicOrdering::Acquire); + (len, size + len * size_of::()) + }; + + QueueInfo { + unverified_queue_size: unverified_len, + verifying_queue_size: verifying_len, + verified_queue_size: verified_len, + max_queue_size: self.max_queue_size, + max_mem_use: self.max_mem_use, + mem_used: unverified_bytes + verifying_bytes + verified_bytes, + } + } + + /// Get the total difficulty of all the blocks in the queue. + pub fn total_difficulty(&self) -> U256 { + self.total_difficulty.read().clone() + } + + /// Get the current number of working verifiers. + pub fn num_verifiers(&self) -> usize { + match *self.state.0.lock() { + State::Work(x) => x, + State::Exit => panic!("state only set to exit on drop; queue live now; qed"), + } + } + + /// Optimise memory footprint of the heap fields, and adjust the number of threads + /// to better suit the workload. + pub fn collect_garbage(&self) { + // number of ticks to average queue stats over + // when deciding whether to change the number of verifiers. + #[cfg(not(test))] + const READJUSTMENT_PERIOD: usize = 12; + + #[cfg(test)] + const READJUSTMENT_PERIOD: usize = 1; + + let (u_len, v_len) = { + let u_len = { + let mut q = self.verification.unverified.lock(); + q.shrink_to_fit(); + q.len() + }; + self.verification.verifying.lock().shrink_to_fit(); + + let v_len = { + let mut q = self.verification.verified.lock(); + q.shrink_to_fit(); + q.len() + }; + + (u_len as isize, v_len as isize) + }; + + self.processing.write().shrink_to_fit(); + + if !self.scale_verifiers { + return; + } + + if self + .ticks_since_adjustment + .fetch_add(1, AtomicOrdering::SeqCst) + + 1 + >= READJUSTMENT_PERIOD + { + self.ticks_since_adjustment.store(0, AtomicOrdering::SeqCst); + } else { + return; + } + + let current = self.num_verifiers(); + + let diff = (v_len - u_len).abs(); + let total = v_len + u_len; + + self.scale_verifiers(if u_len < 20 { + 1 + } else if diff <= total / 10 { + current + } else if v_len > u_len { + current - 1 + } else { + current + 1 + }); + } + + // wake up or sleep verifiers to get as close to the target as + // possible, never going over the amount of initially allocated threads + // or below 1. + fn scale_verifiers(&self, target: usize) { + let current = self.num_verifiers(); + let target = cmp::min(self.verifier_handles.len(), target); + let target = cmp::max(1, target); + + debug!(target: "verification", "Scaling from {} to {} verifiers", current, target); + + *self.state.0.lock() = State::Work(target); + self.state.1.notify_all(); + } } impl Drop for VerificationQueue { - fn drop(&mut self) { - trace!(target: "shutdown", "[VerificationQueue] Closing..."); - self.clear(); - self.deleting.store(true, AtomicOrdering::SeqCst); - - // set exit state; should be done before `more_to_verify` notification. - *self.state.0.lock() = State::Exit; - self.state.1.notify_all(); - - // acquire this lock to force threads to reach the waiting point - // if they're in-between the exit check and the more_to_verify wait. - { - let _unverified = self.verification.unverified.lock(); - self.more_to_verify.notify_all(); - } - - // wait for all verifier threads to join. - for thread in self.verifier_handles.drain(..) { - thread.join().expect("Propagating verifier thread panic on shutdown"); - } - - trace!(target: "shutdown", "[VerificationQueue] Closed."); - } + fn drop(&mut self) { + trace!(target: "shutdown", "[VerificationQueue] Closing..."); + self.clear(); + self.deleting.store(true, AtomicOrdering::SeqCst); + + // set exit state; should be done before `more_to_verify` notification. + *self.state.0.lock() = State::Exit; + self.state.1.notify_all(); + + // acquire this lock to force threads to reach the waiting point + // if they're in-between the exit check and the more_to_verify wait. + { + let _unverified = self.verification.unverified.lock(); + self.more_to_verify.notify_all(); + } + + // wait for all verifier threads to join. + for thread in self.verifier_handles.drain(..) { + thread + .join() + .expect("Propagating verifier thread panic on shutdown"); + } + + trace!(target: "shutdown", "[VerificationQueue] Closed."); + } } #[cfg(test)] mod tests { - use io::*; - use spec::Spec; - use super::{BlockQueue, Config, State}; - use super::kind::blocks::Unverified; - use test_helpers::{get_good_dummy_block_seq, get_good_dummy_block}; - use error::*; - use bytes::Bytes; - use types::view; - use types::views::BlockView; - - // create a test block queue. - // auto_scaling enables verifier adjustment. - fn get_test_queue(auto_scale: bool) -> BlockQueue { - let spec = Spec::new_test(); - let engine = spec.engine; - - let mut config = Config::default(); - config.verifier_settings.scale_verifiers = auto_scale; - BlockQueue::new(config, engine, IoChannel::disconnected(), true) - } - - fn get_test_config(num_verifiers: usize, is_auto_scale: bool) -> Config { - let mut config = Config::default(); - config.verifier_settings.num_verifiers = num_verifiers; - config.verifier_settings.scale_verifiers = is_auto_scale; - config - } - - fn new_unverified(bytes: Bytes) -> Unverified { - Unverified::from_rlp(bytes).expect("Should be valid rlp") - } - - #[test] - fn can_be_created() { - // TODO better test - let spec = Spec::new_test(); - let engine = spec.engine; - let _ = BlockQueue::new(Config::default(), engine, IoChannel::disconnected(), true); - } - - #[test] - fn can_import_blocks() { - let queue = get_test_queue(false); - if let Err(e) = queue.import(new_unverified(get_good_dummy_block())) { - panic!("error importing block that is valid by definition({:?})", e); - } - } - - #[test] - fn returns_error_for_duplicates() { - let queue = get_test_queue(false); - if let Err(e) = queue.import(new_unverified(get_good_dummy_block())) { - panic!("error importing block that is valid by definition({:?})", e); - } - - let duplicate_import = queue.import(new_unverified(get_good_dummy_block())); - match duplicate_import { - Err((_, e)) => { - match e { - Error(ErrorKind::Import(ImportErrorKind::AlreadyQueued), _) => {}, - _ => { panic!("must return AlreadyQueued error"); } - } - } - Ok(_) => { panic!("must produce error"); } - } - } - - #[test] - fn returns_total_difficulty() { - let queue = get_test_queue(false); - let block = get_good_dummy_block(); - let hash = view!(BlockView, &block).header().hash().clone(); - if let Err(e) = queue.import(new_unverified(block)) { - panic!("error importing block that is valid by definition({:?})", e); - } - queue.flush(); - assert_eq!(queue.total_difficulty(), 131072.into()); - queue.drain(10); - assert_eq!(queue.total_difficulty(), 131072.into()); - queue.mark_as_good(&[ hash ]); - assert_eq!(queue.total_difficulty(), 0.into()); - } - - #[test] - fn returns_ok_for_drained_duplicates() { - let queue = get_test_queue(false); - let block = get_good_dummy_block(); - let hash = view!(BlockView, &block).header().hash().clone(); - if let Err(e) = queue.import(new_unverified(block)) { - panic!("error importing block that is valid by definition({:?})", e); - } - queue.flush(); - queue.drain(10); - queue.mark_as_good(&[ hash ]); - - if let Err(e) = queue.import(new_unverified(get_good_dummy_block())) { - panic!("error importing block that has already been drained ({:?})", e); - } - } - - #[test] - fn returns_empty_once_finished() { - let queue = get_test_queue(false); - queue.import(new_unverified(get_good_dummy_block())) - .expect("error importing block that is valid by definition"); - queue.flush(); - queue.drain(1); - - assert!(queue.queue_info().is_empty()); - } - - #[test] - fn test_mem_limit() { - let spec = Spec::new_test(); - let engine = spec.engine; - let mut config = Config::default(); - config.max_mem_use = super::MIN_MEM_LIMIT; // empty queue uses about 15000 - let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true); - assert!(!queue.queue_info().is_full()); - let mut blocks = get_good_dummy_block_seq(50); - for b in blocks.drain(..) { - queue.import(new_unverified(b)).unwrap(); - } - assert!(queue.queue_info().is_full()); - } - - #[test] - fn scaling_limits() { - let max_verifiers = ::num_cpus::get(); - let queue = get_test_queue(true); - queue.scale_verifiers(max_verifiers + 1); - - assert!(queue.num_verifiers() < max_verifiers + 1); - - queue.scale_verifiers(0); - - assert!(queue.num_verifiers() == 1); - } - - #[test] - fn readjust_verifiers() { - let queue = get_test_queue(true); - - // put all the verifiers to sleep to ensure - // the test isn't timing sensitive. - *queue.state.0.lock() = State::Work(0); - - for block in get_good_dummy_block_seq(5000) { - queue.import(new_unverified(block)).expect("Block good by definition; qed"); - } - - // almost all unverified == bump verifier count. - queue.collect_garbage(); - assert_eq!(queue.num_verifiers(), 1); - - queue.flush(); - - // nothing to verify == use minimum number of verifiers. - queue.collect_garbage(); - assert_eq!(queue.num_verifiers(), 1); - } - - #[test] - fn worker_threads_honor_specified_number_without_scaling() { - let spec = Spec::new_test(); - let engine = spec.engine; - let config = get_test_config(1, false); - let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true); - - assert_eq!(queue.num_verifiers(), 1); - } - - #[test] - fn worker_threads_specified_to_zero_should_set_to_one() { - let spec = Spec::new_test(); - let engine = spec.engine; - let config = get_test_config(0, false); - let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true); - - assert_eq!(queue.num_verifiers(), 1); - } - - #[test] - fn worker_threads_should_only_accept_max_number_cpus() { - let spec = Spec::new_test(); - let engine = spec.engine; - let config = get_test_config(10_000, false); - let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true); - let num_cpus = ::num_cpus::get(); - - assert_eq!(queue.num_verifiers(), num_cpus); - } - - #[test] - fn worker_threads_scaling_with_specifed_num_of_workers() { - let num_cpus = ::num_cpus::get(); - // only run the test with at least 2 CPUs - if num_cpus > 1 { - let spec = Spec::new_test(); - let engine = spec.engine; - let config = get_test_config(num_cpus - 1, true); - let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true); - queue.scale_verifiers(num_cpus); - - assert_eq!(queue.num_verifiers(), num_cpus); - } - } + use super::{kind::blocks::Unverified, BlockQueue, Config, State}; + use bytes::Bytes; + use error::*; + use io::*; + use spec::Spec; + use test_helpers::{get_good_dummy_block, get_good_dummy_block_seq}; + use types::{view, views::BlockView}; + + // create a test block queue. + // auto_scaling enables verifier adjustment. + fn get_test_queue(auto_scale: bool) -> BlockQueue { + let spec = Spec::new_test(); + let engine = spec.engine; + + let mut config = Config::default(); + config.verifier_settings.scale_verifiers = auto_scale; + BlockQueue::new(config, engine, IoChannel::disconnected(), true) + } + + fn get_test_config(num_verifiers: usize, is_auto_scale: bool) -> Config { + let mut config = Config::default(); + config.verifier_settings.num_verifiers = num_verifiers; + config.verifier_settings.scale_verifiers = is_auto_scale; + config + } + + fn new_unverified(bytes: Bytes) -> Unverified { + Unverified::from_rlp(bytes).expect("Should be valid rlp") + } + + #[test] + fn can_be_created() { + // TODO better test + let spec = Spec::new_test(); + let engine = spec.engine; + let _ = BlockQueue::new(Config::default(), engine, IoChannel::disconnected(), true); + } + + #[test] + fn can_import_blocks() { + let queue = get_test_queue(false); + if let Err(e) = queue.import(new_unverified(get_good_dummy_block())) { + panic!("error importing block that is valid by definition({:?})", e); + } + } + + #[test] + fn returns_error_for_duplicates() { + let queue = get_test_queue(false); + if let Err(e) = queue.import(new_unverified(get_good_dummy_block())) { + panic!("error importing block that is valid by definition({:?})", e); + } + + let duplicate_import = queue.import(new_unverified(get_good_dummy_block())); + match duplicate_import { + Err((_, e)) => match e { + Error(ErrorKind::Import(ImportErrorKind::AlreadyQueued), _) => {} + _ => { + panic!("must return AlreadyQueued error"); + } + }, + Ok(_) => { + panic!("must produce error"); + } + } + } + + #[test] + fn returns_total_difficulty() { + let queue = get_test_queue(false); + let block = get_good_dummy_block(); + let hash = view!(BlockView, &block).header().hash().clone(); + if let Err(e) = queue.import(new_unverified(block)) { + panic!("error importing block that is valid by definition({:?})", e); + } + queue.flush(); + assert_eq!(queue.total_difficulty(), 131072.into()); + queue.drain(10); + assert_eq!(queue.total_difficulty(), 131072.into()); + queue.mark_as_good(&[hash]); + assert_eq!(queue.total_difficulty(), 0.into()); + } + + #[test] + fn returns_ok_for_drained_duplicates() { + let queue = get_test_queue(false); + let block = get_good_dummy_block(); + let hash = view!(BlockView, &block).header().hash().clone(); + if let Err(e) = queue.import(new_unverified(block)) { + panic!("error importing block that is valid by definition({:?})", e); + } + queue.flush(); + queue.drain(10); + queue.mark_as_good(&[hash]); + + if let Err(e) = queue.import(new_unverified(get_good_dummy_block())) { + panic!( + "error importing block that has already been drained ({:?})", + e + ); + } + } + + #[test] + fn returns_empty_once_finished() { + let queue = get_test_queue(false); + queue + .import(new_unverified(get_good_dummy_block())) + .expect("error importing block that is valid by definition"); + queue.flush(); + queue.drain(1); + + assert!(queue.queue_info().is_empty()); + } + + #[test] + fn test_mem_limit() { + let spec = Spec::new_test(); + let engine = spec.engine; + let mut config = Config::default(); + config.max_mem_use = super::MIN_MEM_LIMIT; // empty queue uses about 15000 + let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true); + assert!(!queue.queue_info().is_full()); + let mut blocks = get_good_dummy_block_seq(50); + for b in blocks.drain(..) { + queue.import(new_unverified(b)).unwrap(); + } + assert!(queue.queue_info().is_full()); + } + + #[test] + fn scaling_limits() { + let max_verifiers = ::num_cpus::get(); + let queue = get_test_queue(true); + queue.scale_verifiers(max_verifiers + 1); + + assert!(queue.num_verifiers() < max_verifiers + 1); + + queue.scale_verifiers(0); + + assert!(queue.num_verifiers() == 1); + } + + #[test] + fn readjust_verifiers() { + let queue = get_test_queue(true); + + // put all the verifiers to sleep to ensure + // the test isn't timing sensitive. + *queue.state.0.lock() = State::Work(0); + + for block in get_good_dummy_block_seq(5000) { + queue + .import(new_unverified(block)) + .expect("Block good by definition; qed"); + } + + // almost all unverified == bump verifier count. + queue.collect_garbage(); + assert_eq!(queue.num_verifiers(), 1); + + queue.flush(); + + // nothing to verify == use minimum number of verifiers. + queue.collect_garbage(); + assert_eq!(queue.num_verifiers(), 1); + } + + #[test] + fn worker_threads_honor_specified_number_without_scaling() { + let spec = Spec::new_test(); + let engine = spec.engine; + let config = get_test_config(1, false); + let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true); + + assert_eq!(queue.num_verifiers(), 1); + } + + #[test] + fn worker_threads_specified_to_zero_should_set_to_one() { + let spec = Spec::new_test(); + let engine = spec.engine; + let config = get_test_config(0, false); + let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true); + + assert_eq!(queue.num_verifiers(), 1); + } + + #[test] + fn worker_threads_should_only_accept_max_number_cpus() { + let spec = Spec::new_test(); + let engine = spec.engine; + let config = get_test_config(10_000, false); + let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true); + let num_cpus = ::num_cpus::get(); + + assert_eq!(queue.num_verifiers(), num_cpus); + } + + #[test] + fn worker_threads_scaling_with_specifed_num_of_workers() { + let num_cpus = ::num_cpus::get(); + // only run the test with at least 2 CPUs + if num_cpus > 1 { + let spec = Spec::new_test(); + let engine = spec.engine; + let config = get_test_config(num_cpus - 1, true); + let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true); + queue.scale_verifiers(num_cpus); + + assert_eq!(queue.num_verifiers(), num_cpus); + } + } } diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index 36a847f64af..a4437c719d5 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Block and transaction verification functions //! @@ -21,8 +21,10 @@ //! 2. Signatures verification done in the queue. //! 3. Final verification against the blockchain done before enactment. -use std::collections::HashSet; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use std::{ + collections::HashSet, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; use bytes::Bytes; use hash::keccak; @@ -36,792 +38,1091 @@ use call_contract::CallContract; use client::BlockInfo; use engines::{EthEngine, MAX_UNCLE_AGE}; use error::{BlockError, Error}; -use types::{BlockNumber, header::Header}; -use types::transaction::SignedTransaction; +use types::{header::Header, transaction::SignedTransaction, BlockNumber}; use verification::queue::kind::blocks::Unverified; -#[cfg(not(time_checked_add))] use time_utils::CheckedSystemTime; /// Preprocessed block data gathered in `verify_block_unordered` call pub struct PreverifiedBlock { - /// Populated block header - pub header: Header, - /// Populated block transactions - pub transactions: Vec, - /// Populated block uncles - pub uncles: Vec
, - /// Block bytes - pub bytes: Bytes, + /// Populated block header + pub header: Header, + /// Populated block transactions + pub transactions: Vec, + /// Populated block uncles + pub uncles: Vec
, + /// Block bytes + pub bytes: Bytes, } impl HeapSizeOf for PreverifiedBlock { - fn heap_size_of_children(&self) -> usize { - self.header.heap_size_of_children() - + self.transactions.heap_size_of_children() - + self.bytes.heap_size_of_children() - } + fn heap_size_of_children(&self) -> usize { + self.header.heap_size_of_children() + + self.transactions.heap_size_of_children() + + self.bytes.heap_size_of_children() + } } /// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block -pub fn verify_block_basic(block: &Unverified, engine: &EthEngine, check_seal: bool) -> Result<(), Error> { - verify_header_params(&block.header, engine, true, check_seal)?; - verify_block_integrity(block)?; - - if check_seal { - engine.verify_block_basic(&block.header)?; - } - - for uncle in &block.uncles { - verify_header_params(uncle, engine, false, check_seal)?; - if check_seal { - engine.verify_block_basic(uncle)?; - } - } - - for t in &block.transactions { - engine.verify_transaction_basic(t, &block.header)?; - } - - Ok(()) +pub fn verify_block_basic( + block: &Unverified, + engine: &dyn EthEngine, + check_seal: bool, +) -> Result<(), Error> { + verify_header_params(&block.header, engine, true, check_seal)?; + verify_block_integrity(block)?; + + if check_seal { + engine.verify_block_basic(&block.header)?; + } + + for uncle in &block.uncles { + verify_header_params(uncle, engine, false, check_seal)?; + if check_seal { + engine.verify_block_basic(uncle)?; + } + } + + for t in &block.transactions { + engine.verify_transaction_basic(t, &block.header)?; + } + + Ok(()) } /// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash. /// Still operates on a individual block /// Returns a `PreverifiedBlock` structure populated with transactions -pub fn verify_block_unordered(block: Unverified, engine: &EthEngine, check_seal: bool) -> Result { - let header = block.header; - if check_seal { - engine.verify_block_unordered(&header)?; - for uncle in &block.uncles { - engine.verify_block_unordered(uncle)?; - } - } - // Verify transactions. - let nonce_cap = if header.number() >= engine.params().dust_protection_transition { - Some((engine.params().nonce_cap_increment * header.number()).into()) - } else { - None - }; - - let transactions = block.transactions - .into_iter() - .map(|t| { - let t = engine.verify_transaction_unordered(t, &header)?; - if let Some(max_nonce) = nonce_cap { - if t.nonce >= max_nonce { - return Err(BlockError::TooManyTransactions(t.sender()).into()); - } - } - Ok(t) - }) - .collect::, Error>>()?; - - Ok(PreverifiedBlock { - header, - transactions, - uncles: block.uncles, - bytes: block.bytes, - }) +pub fn verify_block_unordered( + block: Unverified, + engine: &dyn EthEngine, + check_seal: bool, +) -> Result { + let header = block.header; + if check_seal { + engine.verify_block_unordered(&header)?; + for uncle in &block.uncles { + engine.verify_block_unordered(uncle)?; + } + } + // Verify transactions. + let nonce_cap = if header.number() >= engine.params().dust_protection_transition { + Some((engine.params().nonce_cap_increment * header.number()).into()) + } else { + None + }; + + let transactions = block + .transactions + .into_iter() + .map(|t| { + let t = engine.verify_transaction_unordered(t, &header)?; + if let Some(max_nonce) = nonce_cap { + if t.nonce >= max_nonce { + return Err(BlockError::TooManyTransactions(t.sender()).into()); + } + } + Ok(t) + }) + .collect::, Error>>()?; + + Ok(PreverifiedBlock { + header, + transactions, + uncles: block.uncles, + bytes: block.bytes, + }) } /// Parameters for full verification of block family pub struct FullFamilyParams<'a, C: BlockInfo + CallContract + 'a> { - /// Preverified block - pub block: &'a PreverifiedBlock, + /// Preverified block + pub block: &'a PreverifiedBlock, - /// Block provider to use during verification - pub block_provider: &'a BlockProvider, + /// Block provider to use during verification + pub block_provider: &'a dyn BlockProvider, - /// Engine client to use during verification - pub client: &'a C, + /// Engine client to use during verification + pub client: &'a C, } /// Phase 3 verification. Check block information against parent and uncles. -pub fn verify_block_family(header: &Header, parent: &Header, engine: &EthEngine, do_full: Option>) -> Result<(), Error> { - // TODO: verify timestamp - verify_parent(&header, &parent, engine)?; - engine.verify_block_family(&header, &parent)?; - - let params = match do_full { - Some(x) => x, - None => return Ok(()), - }; - - verify_uncles(params.block, params.block_provider, engine)?; - - for tx in ¶ms.block.transactions { - // transactions are verified against the parent header since the current - // state wasn't available when the tx was created - engine.machine().verify_transaction(tx, parent, params.client)?; - } - - Ok(()) +pub fn verify_block_family( + header: &Header, + parent: &Header, + engine: &dyn EthEngine, + do_full: Option>, +) -> Result<(), Error> { + // TODO: verify timestamp + verify_parent(&header, &parent, engine)?; + engine.verify_block_family(&header, &parent)?; + + let params = match do_full { + Some(x) => x, + None => return Ok(()), + }; + + verify_uncles(params.block, params.block_provider, engine)?; + + for tx in ¶ms.block.transactions { + // transactions are verified against the parent header since the current + // state wasn't available when the tx was created + engine + .machine() + .verify_transaction(tx, parent, params.client)?; + } + + Ok(()) } -fn verify_uncles(block: &PreverifiedBlock, bc: &BlockProvider, engine: &EthEngine) -> Result<(), Error> { - let header = &block.header; - let num_uncles = block.uncles.len(); - let max_uncles = engine.maximum_uncle_count(header.number()); - if num_uncles != 0 { - if num_uncles > max_uncles { - return Err(From::from(BlockError::TooManyUncles(OutOfBounds { - min: None, - max: Some(max_uncles), - found: num_uncles, - }))); - } - - let mut excluded = HashSet::new(); - excluded.insert(header.hash()); - let mut hash = header.parent_hash().clone(); - excluded.insert(hash.clone()); - for _ in 0..MAX_UNCLE_AGE { - match bc.block_details(&hash) { - Some(details) => { - excluded.insert(details.parent); - let b = bc.block(&hash) - .expect("parent already known to be stored; qed"); - excluded.extend(b.uncle_hashes()); - hash = details.parent; - } - None => break - } - } - - let mut verified = HashSet::new(); - for uncle in &block.uncles { - if excluded.contains(&uncle.hash()) { - return Err(From::from(BlockError::UncleInChain(uncle.hash()))) - } - - if verified.contains(&uncle.hash()) { - return Err(From::from(BlockError::DuplicateUncle(uncle.hash()))) - } - - // m_currentBlock.number() - uncle.number() m_cB.n - uP.n() - // 1 2 - // 2 - // 3 - // 4 - // 5 - // 6 7 - // (8 Invalid) - - let depth = if header.number() > uncle.number() { header.number() - uncle.number() } else { 0 }; - if depth > MAX_UNCLE_AGE as u64 { - return Err(From::from(BlockError::UncleTooOld(OutOfBounds { min: Some(header.number() - depth), max: Some(header.number() - 1), found: uncle.number() }))); - } - else if depth < 1 { - return Err(From::from(BlockError::UncleIsBrother(OutOfBounds { min: Some(header.number() - depth), max: Some(header.number() - 1), found: uncle.number() }))); - } - - // cB - // cB.p^1 1 depth, valid uncle - // cB.p^2 ---/ 2 - // cB.p^3 -----/ 3 - // cB.p^4 -------/ 4 - // cB.p^5 ---------/ 5 - // cB.p^6 -----------/ 6 - // cB.p^7 -------------/ - // cB.p^8 - let mut expected_uncle_parent = header.parent_hash().clone(); - let uncle_parent = bc.block_header_data(&uncle.parent_hash()).ok_or_else(|| Error::from(BlockError::UnknownUncleParent(uncle.parent_hash().clone())))?; - for _ in 0..depth { - match bc.block_details(&expected_uncle_parent) { - Some(details) => { - expected_uncle_parent = details.parent; - }, - None => break - } - } - if expected_uncle_parent != uncle_parent.hash() { - return Err(From::from(BlockError::UncleParentNotInChain(uncle_parent.hash()))); - } - - let uncle_parent = uncle_parent.decode()?; - verify_parent(&uncle, &uncle_parent, engine)?; - engine.verify_block_family(&uncle, &uncle_parent)?; - verified.insert(uncle.hash()); - } - } - - Ok(()) +fn verify_uncles( + block: &PreverifiedBlock, + bc: &dyn BlockProvider, + engine: &dyn EthEngine, +) -> Result<(), Error> { + let header = &block.header; + let num_uncles = block.uncles.len(); + let max_uncles = engine.maximum_uncle_count(header.number()); + if num_uncles != 0 { + if num_uncles > max_uncles { + return Err(From::from(BlockError::TooManyUncles(OutOfBounds { + min: None, + max: Some(max_uncles), + found: num_uncles, + }))); + } + + let mut excluded = HashSet::new(); + excluded.insert(header.hash()); + let mut hash = header.parent_hash().clone(); + excluded.insert(hash.clone()); + for _ in 0..MAX_UNCLE_AGE { + match bc.block_details(&hash) { + Some(details) => { + excluded.insert(details.parent); + let b = bc + .block(&hash) + .expect("parent already known to be stored; qed"); + excluded.extend(b.uncle_hashes()); + hash = details.parent; + } + None => break, + } + } + + let mut verified = HashSet::new(); + for uncle in &block.uncles { + if excluded.contains(&uncle.hash()) { + return Err(From::from(BlockError::UncleInChain(uncle.hash()))); + } + + if verified.contains(&uncle.hash()) { + return Err(From::from(BlockError::DuplicateUncle(uncle.hash()))); + } + + // m_currentBlock.number() - uncle.number() m_cB.n - uP.n() + // 1 2 + // 2 + // 3 + // 4 + // 5 + // 6 7 + // (8 Invalid) + + let depth = if header.number() > uncle.number() { + header.number() - uncle.number() + } else { + 0 + }; + if depth > MAX_UNCLE_AGE as u64 { + return Err(From::from(BlockError::UncleTooOld(OutOfBounds { + min: Some(header.number() - depth), + max: Some(header.number() - 1), + found: uncle.number(), + }))); + } else if depth < 1 { + return Err(From::from(BlockError::UncleIsBrother(OutOfBounds { + min: Some(header.number() - depth), + max: Some(header.number() - 1), + found: uncle.number(), + }))); + } + + // cB + // cB.p^1 1 depth, valid uncle + // cB.p^2 ---/ 2 + // cB.p^3 -----/ 3 + // cB.p^4 -------/ 4 + // cB.p^5 ---------/ 5 + // cB.p^6 -----------/ 6 + // cB.p^7 -------------/ + // cB.p^8 + let mut expected_uncle_parent = header.parent_hash().clone(); + let uncle_parent = bc.block_header_data(&uncle.parent_hash()).ok_or_else(|| { + Error::from(BlockError::UnknownUncleParent(uncle.parent_hash().clone())) + })?; + for _ in 0..depth { + match bc.block_details(&expected_uncle_parent) { + Some(details) => { + expected_uncle_parent = details.parent; + } + None => break, + } + } + if expected_uncle_parent != uncle_parent.hash() { + return Err(From::from(BlockError::UncleParentNotInChain( + uncle_parent.hash(), + ))); + } + + let uncle_parent = uncle_parent.decode()?; + verify_parent(&uncle, &uncle_parent, engine)?; + engine.verify_block_family(&uncle, &uncle_parent)?; + verified.insert(uncle.hash()); + } + } + + Ok(()) } /// Phase 4 verification. Check block information against transaction enactment results, pub fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error> { - if expected.state_root() != got.state_root() { - return Err(From::from(BlockError::InvalidStateRoot(Mismatch { expected: *expected.state_root(), found: *got.state_root() }))) - } - if expected.gas_used() != got.gas_used() { - return Err(From::from(BlockError::InvalidGasUsed(Mismatch { expected: *expected.gas_used(), found: *got.gas_used() }))) - } - if expected.log_bloom() != got.log_bloom() { - return Err(From::from(BlockError::InvalidLogBloom(Box::new(Mismatch { expected: *expected.log_bloom(), found: *got.log_bloom() })))) - } - if expected.receipts_root() != got.receipts_root() { - return Err(From::from(BlockError::InvalidReceiptsRoot(Mismatch { expected: *expected.receipts_root(), found: *got.receipts_root() }))) - } - Ok(()) + if expected.state_root() != got.state_root() { + return Err(From::from(BlockError::InvalidStateRoot(Mismatch { + expected: *expected.state_root(), + found: *got.state_root(), + }))); + } + if expected.gas_used() != got.gas_used() { + return Err(From::from(BlockError::InvalidGasUsed(Mismatch { + expected: *expected.gas_used(), + found: *got.gas_used(), + }))); + } + if expected.log_bloom() != got.log_bloom() { + return Err(From::from(BlockError::InvalidLogBloom(Box::new( + Mismatch { + expected: *expected.log_bloom(), + found: *got.log_bloom(), + }, + )))); + } + if expected.receipts_root() != got.receipts_root() { + return Err(From::from(BlockError::InvalidReceiptsRoot(Mismatch { + expected: *expected.receipts_root(), + found: *got.receipts_root(), + }))); + } + Ok(()) } /// Check basic header parameters. -pub fn verify_header_params(header: &Header, engine: &EthEngine, is_full: bool, check_seal: bool) -> Result<(), Error> { - if check_seal { - let expected_seal_fields = engine.seal_fields(header); - if header.seal().len() != expected_seal_fields { - return Err(From::from(BlockError::InvalidSealArity( - Mismatch { expected: expected_seal_fields, found: header.seal().len() } - ))); - } - } - - if header.number() >= From::from(BlockNumber::max_value()) { - return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { max: Some(From::from(BlockNumber::max_value())), min: None, found: header.number() }))) - } - if header.gas_used() > header.gas_limit() { - return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { max: Some(*header.gas_limit()), min: None, found: *header.gas_used() }))); - } - let min_gas_limit = engine.params().min_gas_limit; - if header.gas_limit() < &min_gas_limit { - return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: *header.gas_limit() }))); - } - if let Some(limit) = engine.maximum_gas_limit() { - if header.gas_limit() > &limit { - return Err(From::from(::error::BlockError::InvalidGasLimit(OutOfBounds { min: None, max: Some(limit), found: *header.gas_limit() }))); - } - } - let maximum_extra_data_size = engine.maximum_extra_data_size(); - if header.number() != 0 && header.extra_data().len() > maximum_extra_data_size { - return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: Some(maximum_extra_data_size), found: header.extra_data().len() }))); - } - - if let Some(ref ext) = engine.machine().ethash_extensions() { - if header.number() >= ext.dao_hardfork_transition && - header.number() <= ext.dao_hardfork_transition + 9 && - header.extra_data()[..] != b"dao-hard-fork"[..] { - return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: None, found: 0 }))); - } - } - - if is_full { - const ACCEPTABLE_DRIFT: Duration = Duration::from_secs(15); - // this will resist overflow until `year 2037` - let max_time = SystemTime::now() + ACCEPTABLE_DRIFT; - let invalid_threshold = max_time + ACCEPTABLE_DRIFT * 9; - let timestamp = UNIX_EPOCH.checked_add(Duration::from_secs(header.timestamp())) - .ok_or(BlockError::TimestampOverflow)?; - - if timestamp > invalid_threshold { - return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: Some(max_time), min: None, found: timestamp }))) - } - - if timestamp > max_time { - return Err(From::from(BlockError::TemporarilyInvalid(OutOfBounds { max: Some(max_time), min: None, found: timestamp }))) - } - } - - Ok(()) +pub fn verify_header_params( + header: &Header, + engine: &dyn EthEngine, + is_full: bool, + check_seal: bool, +) -> Result<(), Error> { + if check_seal { + let expected_seal_fields = engine.seal_fields(header); + if header.seal().len() != expected_seal_fields { + return Err(From::from(BlockError::InvalidSealArity(Mismatch { + expected: expected_seal_fields, + found: header.seal().len(), + }))); + } + } + + if header.number() >= From::from(BlockNumber::max_value()) { + return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { + max: Some(From::from(BlockNumber::max_value())), + min: None, + found: header.number(), + }))); + } + if header.gas_used() > header.gas_limit() { + return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { + max: Some(*header.gas_limit()), + min: None, + found: *header.gas_used(), + }))); + } + let min_gas_limit = engine.params().min_gas_limit; + if header.gas_limit() < &min_gas_limit { + return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { + min: Some(min_gas_limit), + max: None, + found: *header.gas_limit(), + }))); + } + if let Some(limit) = engine.maximum_gas_limit() { + if header.gas_limit() > &limit { + return Err(From::from(::error::BlockError::InvalidGasLimit( + OutOfBounds { + min: None, + max: Some(limit), + found: *header.gas_limit(), + }, + ))); + } + } + let maximum_extra_data_size = engine.maximum_extra_data_size(); + if header.number() != 0 && header.extra_data().len() > maximum_extra_data_size { + return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { + min: None, + max: Some(maximum_extra_data_size), + found: header.extra_data().len(), + }))); + } + + if let Some(ref ext) = engine.machine().ethash_extensions() { + if header.number() >= ext.dao_hardfork_transition + && header.number() <= ext.dao_hardfork_transition + 9 + && header.extra_data()[..] != b"dao-hard-fork"[..] + { + return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { + min: None, + max: None, + found: 0, + }))); + } + } + + if is_full { + const ACCEPTABLE_DRIFT: Duration = Duration::from_secs(15); + // this will resist overflow until `year 2037` + let max_time = SystemTime::now() + ACCEPTABLE_DRIFT; + let invalid_threshold = max_time + ACCEPTABLE_DRIFT * 9; + let timestamp = + CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::from_secs(header.timestamp())) + .ok_or(BlockError::TimestampOverflow)?; + + if timestamp > invalid_threshold { + return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { + max: Some(max_time), + min: None, + found: timestamp, + }))); + } + + if timestamp > max_time { + return Err(From::from(BlockError::TemporarilyInvalid(OutOfBounds { + max: Some(max_time), + min: None, + found: timestamp, + }))); + } + } + + Ok(()) } /// Check header parameters agains parent header. -fn verify_parent(header: &Header, parent: &Header, engine: &EthEngine) -> Result<(), Error> { - assert!(header.parent_hash().is_zero() || &parent.hash() == header.parent_hash(), - "Parent hash should already have been verified; qed"); - - let gas_limit_divisor = engine.params().gas_limit_bound_divisor; - - if !engine.is_timestamp_valid(header.timestamp(), parent.timestamp()) { - let now = SystemTime::now(); - let min = now.checked_add(Duration::from_secs(parent.timestamp().saturating_add(1))) - .ok_or(BlockError::TimestampOverflow)?; - let found = now.checked_add(Duration::from_secs(header.timestamp())) - .ok_or(BlockError::TimestampOverflow)?; - return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: None, min: Some(min), found }))) - } - if header.number() != parent.number() + 1 { - return Err(From::from(BlockError::InvalidNumber(Mismatch { expected: parent.number() + 1, found: header.number() }))); - } - - if header.number() == 0 { - return Err(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }).into()); - } - - let parent_gas_limit = *parent.gas_limit(); - let min_gas = parent_gas_limit - parent_gas_limit / gas_limit_divisor; - let max_gas = parent_gas_limit + parent_gas_limit / gas_limit_divisor; - if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas { - return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: *header.gas_limit() }))); - } - - Ok(()) +fn verify_parent(header: &Header, parent: &Header, engine: &dyn EthEngine) -> Result<(), Error> { + assert!( + header.parent_hash().is_zero() || &parent.hash() == header.parent_hash(), + "Parent hash should already have been verified; qed" + ); + + let gas_limit_divisor = engine.params().gas_limit_bound_divisor; + + if !engine.is_timestamp_valid(header.timestamp(), parent.timestamp()) { + let now = SystemTime::now(); + let min = CheckedSystemTime::checked_add( + now, + Duration::from_secs(parent.timestamp().saturating_add(1)), + ) + .ok_or(BlockError::TimestampOverflow)?; + let found = CheckedSystemTime::checked_add(now, Duration::from_secs(header.timestamp())) + .ok_or(BlockError::TimestampOverflow)?; + return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { + max: None, + min: Some(min), + found, + }))); + } + if header.number() != parent.number() + 1 { + return Err(From::from(BlockError::InvalidNumber(Mismatch { + expected: parent.number() + 1, + found: header.number(), + }))); + } + + if header.number() == 0 { + return Err(BlockError::RidiculousNumber(OutOfBounds { + min: Some(1), + max: None, + found: header.number(), + }) + .into()); + } + + let parent_gas_limit = *parent.gas_limit(); + let min_gas = parent_gas_limit - parent_gas_limit / gas_limit_divisor; + let max_gas = parent_gas_limit + parent_gas_limit / gas_limit_divisor; + if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas { + return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { + min: Some(min_gas), + max: Some(max_gas), + found: *header.gas_limit(), + }))); + } + + Ok(()) } /// Verify block data against header: transactions root and uncles hash. fn verify_block_integrity(block: &Unverified) -> Result<(), Error> { - let block_rlp = Rlp::new(&block.bytes); - let tx = block_rlp.at(1)?; - let expected_root = ordered_trie_root(tx.iter().map(|r| r.as_raw())); - if &expected_root != block.header.transactions_root() { - bail!(BlockError::InvalidTransactionsRoot(Mismatch { - expected: expected_root, - found: *block.header.transactions_root(), - })); - } - let expected_uncles = keccak(block_rlp.at(2)?.as_raw()); - if &expected_uncles != block.header.uncles_hash(){ - bail!(BlockError::InvalidUnclesHash(Mismatch { - expected: expected_uncles, - found: *block.header.uncles_hash(), - })); - } - Ok(()) + let block_rlp = Rlp::new(&block.bytes); + let tx = block_rlp.at(1)?; + let expected_root = ordered_trie_root(tx.iter().map(|r| r.as_raw())); + if &expected_root != block.header.transactions_root() { + bail!(BlockError::InvalidTransactionsRoot(Mismatch { + expected: expected_root, + found: *block.header.transactions_root(), + })); + } + let expected_uncles = keccak(block_rlp.at(2)?.as_raw()); + if &expected_uncles != block.header.uncles_hash() { + bail!(BlockError::InvalidUnclesHash(Mismatch { + expected: expected_uncles, + found: *block.header.uncles_hash(), + })); + } + Ok(()) } #[cfg(test)] mod tests { - use super::*; - - use std::collections::{BTreeMap, HashMap}; - use std::time::{SystemTime, UNIX_EPOCH}; - use ethereum_types::{H256, BloomRef, U256}; - use blockchain::{BlockDetails, TransactionAddress, BlockReceipts}; - use types::encoded; - use hash::keccak; - use engines::EthEngine; - use error::BlockError::*; - use error::ErrorKind; - use ethkey::{Random, Generator}; - use spec::{CommonParams, Spec}; - use test_helpers::{create_test_block_with_data, create_test_block}; - use types::transaction::{SignedTransaction, Transaction, UnverifiedTransaction, Action}; - use types::log_entry::{LogEntry, LocalizedLogEntry}; - use rlp; - use triehash::ordered_trie_root; - - fn check_ok(result: Result<(), Error>) { - result.unwrap_or_else(|e| panic!("Block verification failed: {:?}", e)); - } - - fn check_fail(result: Result<(), Error>, e: BlockError) { - match result { - Err(Error(ErrorKind::Block(ref error), _)) if *error == e => (), - Err(other) => panic!("Block verification failed.\nExpected: {:?}\nGot: {:?}", e, other), - Ok(_) => panic!("Block verification failed.\nExpected: {:?}\nGot: Ok", e), - } - } - - fn check_fail_timestamp(result: Result<(), Error>, temp: bool) { - let name = if temp { "TemporarilyInvalid" } else { "InvalidTimestamp" }; - match result { - Err(Error(ErrorKind::Block(BlockError::InvalidTimestamp(_)), _)) if !temp => (), - Err(Error(ErrorKind::Block(BlockError::TemporarilyInvalid(_)), _)) if temp => (), - Err(other) => panic!("Block verification failed.\nExpected: {}\nGot: {:?}", name, other), - Ok(_) => panic!("Block verification failed.\nExpected: {}\nGot: Ok", name), - } - } - - struct TestBlockChain { - blocks: HashMap, - numbers: HashMap, - } - - impl Default for TestBlockChain { - fn default() -> Self { - TestBlockChain::new() - } - } - - impl TestBlockChain { - pub fn new() -> Self { - TestBlockChain { - blocks: HashMap::new(), - numbers: HashMap::new(), - } - } - - pub fn insert(&mut self, bytes: Bytes) { - let header = Unverified::from_rlp(bytes.clone()).unwrap().header; - let hash = header.hash(); - self.blocks.insert(hash, bytes); - self.numbers.insert(header.number(), hash); - } - } - - impl BlockProvider for TestBlockChain { - fn is_known(&self, hash: &H256) -> bool { - self.blocks.contains_key(hash) - } - - fn first_block(&self) -> Option { - unimplemented!() - } - - /// Get raw block data - fn block(&self, hash: &H256) -> Option { - self.blocks.get(hash).cloned().map(encoded::Block::new) - } - - fn block_header_data(&self, hash: &H256) -> Option { - self.block(hash) - .map(|b| b.header_view().rlp().as_raw().to_vec()) - .map(encoded::Header::new) - } - - fn block_body(&self, hash: &H256) -> Option { - self.block(hash) - .map(|b| BlockChain::block_to_body(&b.into_inner())) - .map(encoded::Body::new) - } - - fn best_ancient_block(&self) -> Option { - None - } - - /// Get the familial details concerning a block. - fn block_details(&self, hash: &H256) -> Option { - self.blocks.get(hash).map(|bytes| { - let header = Unverified::from_rlp(bytes.to_vec()).unwrap().header; - BlockDetails { - number: header.number(), - total_difficulty: *header.difficulty(), - parent: *header.parent_hash(), - children: Vec::new(), - is_finalized: false, - } - }) - } - - fn transaction_address(&self, _hash: &H256) -> Option { - unimplemented!() - } - - /// Get the hash of given block's number. - fn block_hash(&self, index: BlockNumber) -> Option { - self.numbers.get(&index).cloned() - } - - fn block_receipts(&self, _hash: &H256) -> Option { - unimplemented!() - } - - fn blocks_with_bloom<'a, B, I, II>(&self, _blooms: II, _from_block: BlockNumber, _to_block: BlockNumber) -> Vec - where BloomRef<'a>: From, II: IntoIterator + Copy, I: Iterator, Self: Sized { - unimplemented!() - } - - fn logs(&self, _blocks: Vec, _matches: F, _limit: Option) -> Vec - where F: Fn(&LogEntry) -> bool, Self: Sized { - unimplemented!() - } - } - - fn basic_test(bytes: &[u8], engine: &EthEngine) -> Result<(), Error> { - let unverified = Unverified::from_rlp(bytes.to_vec())?; - verify_block_basic(&unverified, engine, true) - } - - fn family_test(bytes: &[u8], engine: &EthEngine, bc: &BC) -> Result<(), Error> where BC: BlockProvider { - let block = Unverified::from_rlp(bytes.to_vec()).unwrap(); - let header = block.header; - let transactions: Vec<_> = block.transactions - .into_iter() - .map(SignedTransaction::new) - .collect::>()?; - - // TODO: client is really meant to be used for state query here by machine - // additions that need access to state (tx filter in specific) - // no existing tests need access to test, so having this not function - // is fine. - let client = ::client::TestBlockChainClient::default(); - let parent = bc.block_header_data(header.parent_hash()) - .ok_or(BlockError::UnknownParent(*header.parent_hash()))? - .decode()?; - - let block = PreverifiedBlock { - header, - transactions, - uncles: block.uncles, - bytes: bytes.to_vec(), - }; - - let full_params = FullFamilyParams { - block: &block, - block_provider: bc as &BlockProvider, - client: &client, - }; - verify_block_family(&block.header, &parent, engine, Some(full_params)) - } - - fn unordered_test(bytes: &[u8], engine: &EthEngine) -> Result<(), Error> { - let un = Unverified::from_rlp(bytes.to_vec())?; - verify_block_unordered(un, engine, false)?; - Ok(()) - } - - #[test] - fn test_verify_block_basic_with_invalid_transactions() { - let spec = Spec::new_test(); - let engine = &*spec.engine; - - let block = { - let mut rlp = rlp::RlpStream::new_list(3); - let mut header = Header::default(); - // that's an invalid transaction list rlp - let invalid_transactions = vec![vec![0u8]]; - header.set_transactions_root(ordered_trie_root(&invalid_transactions)); - header.set_gas_limit(engine.params().min_gas_limit); - rlp.append(&header); - rlp.append_list::, _>(&invalid_transactions); - rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1); - rlp.out() - }; - - assert!(basic_test(&block, engine).is_err()); - } - - #[test] - fn test_verify_block() { - use rlp::RlpStream; - - // Test against morden - let mut good = Header::new(); - let spec = Spec::new_test(); - let engine = &*spec.engine; - - let min_gas_limit = engine.params().min_gas_limit; - good.set_gas_limit(min_gas_limit); - good.set_timestamp(40); - good.set_number(10); - - let keypair = Random.generate().unwrap(); - - let tr1 = Transaction { - action: Action::Create, - value: U256::from(0), - data: Bytes::new(), - gas: U256::from(30_000), - gas_price: U256::from(40_000), - nonce: U256::one() - }.sign(keypair.secret(), None); - - let tr2 = Transaction { - action: Action::Create, - value: U256::from(0), - data: Bytes::new(), - gas: U256::from(30_000), - gas_price: U256::from(40_000), - nonce: U256::from(2) - }.sign(keypair.secret(), None); - - let tr3 = Transaction { - action: Action::Call(0x0.into()), - value: U256::from(0), - data: Bytes::new(), - gas: U256::from(30_000), - gas_price: U256::from(0), - nonce: U256::zero(), - }.null_sign(0); - - let good_transactions = [ tr1.clone(), tr2.clone() ]; - let eip86_transactions = [ tr3.clone() ]; - - let diff_inc = U256::from(0x40); - - let mut parent6 = good.clone(); - parent6.set_number(6); - let mut parent7 = good.clone(); - parent7.set_number(7); - parent7.set_parent_hash(parent6.hash()); - parent7.set_difficulty(parent6.difficulty().clone() + diff_inc); - parent7.set_timestamp(parent6.timestamp() + 10); - let mut parent8 = good.clone(); - parent8.set_number(8); - parent8.set_parent_hash(parent7.hash()); - parent8.set_difficulty(parent7.difficulty().clone() + diff_inc); - parent8.set_timestamp(parent7.timestamp() + 10); - - let mut good_uncle1 = good.clone(); - good_uncle1.set_number(9); - good_uncle1.set_parent_hash(parent8.hash()); - good_uncle1.set_difficulty(parent8.difficulty().clone() + diff_inc); - good_uncle1.set_timestamp(parent8.timestamp() + 10); - let mut ex = good_uncle1.extra_data().to_vec(); - ex.push(1u8); - good_uncle1.set_extra_data(ex); - - let mut good_uncle2 = good.clone(); - good_uncle2.set_number(8); - good_uncle2.set_parent_hash(parent7.hash()); - good_uncle2.set_difficulty(parent7.difficulty().clone() + diff_inc); - good_uncle2.set_timestamp(parent7.timestamp() + 10); - let mut ex = good_uncle2.extra_data().to_vec(); - ex.push(2u8); - good_uncle2.set_extra_data(ex); - - let good_uncles = vec![ good_uncle1.clone(), good_uncle2.clone() ]; - let mut uncles_rlp = RlpStream::new(); - uncles_rlp.append_list(&good_uncles); - let good_uncles_hash = keccak(uncles_rlp.as_raw()); - let good_transactions_root = ordered_trie_root(good_transactions.iter().map(|t| ::rlp::encode::(t))); - let eip86_transactions_root = ordered_trie_root(eip86_transactions.iter().map(|t| ::rlp::encode::(t))); - - let mut parent = good.clone(); - parent.set_number(9); - parent.set_timestamp(parent8.timestamp() + 10); - parent.set_parent_hash(parent8.hash()); - parent.set_difficulty(parent8.difficulty().clone() + diff_inc); - - good.set_parent_hash(parent.hash()); - good.set_difficulty(parent.difficulty().clone() + diff_inc); - good.set_timestamp(parent.timestamp() + 10); - - let mut bc = TestBlockChain::new(); - bc.insert(create_test_block(&good)); - bc.insert(create_test_block(&parent)); - bc.insert(create_test_block(&parent6)); - bc.insert(create_test_block(&parent7)); - bc.insert(create_test_block(&parent8)); - - check_ok(basic_test(&create_test_block(&good), engine)); - - let mut bad_header = good.clone(); - bad_header.set_transactions_root(eip86_transactions_root.clone()); - bad_header.set_uncles_hash(good_uncles_hash.clone()); - match basic_test(&create_test_block_with_data(&bad_header, &eip86_transactions, &good_uncles), engine) { + use super::*; + + use blockchain::{BlockDetails, BlockReceipts, TransactionAddress}; + use engines::EthEngine; + use error::{BlockError::*, ErrorKind}; + use ethereum_types::{BloomRef, H256, U256}; + use ethkey::{Generator, Random}; + use hash::keccak; + use rlp; + use spec::{CommonParams, Spec}; + use std::{ + collections::{BTreeMap, HashMap}, + time::{SystemTime, UNIX_EPOCH}, + }; + use test_helpers::{create_test_block, create_test_block_with_data}; + use triehash::ordered_trie_root; + use types::{ + encoded, + log_entry::{LocalizedLogEntry, LogEntry}, + transaction::{Action, SignedTransaction, Transaction, UnverifiedTransaction}, + }; + + fn check_ok(result: Result<(), Error>) { + result.unwrap_or_else(|e| panic!("Block verification failed: {:?}", e)); + } + + fn check_fail(result: Result<(), Error>, e: BlockError) { + match result { + Err(Error(ErrorKind::Block(ref error), _)) if *error == e => (), + Err(other) => panic!( + "Block verification failed.\nExpected: {:?}\nGot: {:?}", + e, other + ), + Ok(_) => panic!("Block verification failed.\nExpected: {:?}\nGot: Ok", e), + } + } + + fn check_fail_timestamp(result: Result<(), Error>, temp: bool) { + let name = if temp { + "TemporarilyInvalid" + } else { + "InvalidTimestamp" + }; + match result { + Err(Error(ErrorKind::Block(BlockError::InvalidTimestamp(_)), _)) if !temp => (), + Err(Error(ErrorKind::Block(BlockError::TemporarilyInvalid(_)), _)) if temp => (), + Err(other) => panic!( + "Block verification failed.\nExpected: {}\nGot: {:?}", + name, other + ), + Ok(_) => panic!("Block verification failed.\nExpected: {}\nGot: Ok", name), + } + } + + struct TestBlockChain { + blocks: HashMap, + numbers: HashMap, + } + + impl Default for TestBlockChain { + fn default() -> Self { + TestBlockChain::new() + } + } + + impl TestBlockChain { + pub fn new() -> Self { + TestBlockChain { + blocks: HashMap::new(), + numbers: HashMap::new(), + } + } + + pub fn insert(&mut self, bytes: Bytes) { + let header = Unverified::from_rlp(bytes.clone()).unwrap().header; + let hash = header.hash(); + self.blocks.insert(hash, bytes); + self.numbers.insert(header.number(), hash); + } + } + + impl BlockProvider for TestBlockChain { + fn is_known(&self, hash: &H256) -> bool { + self.blocks.contains_key(hash) + } + + fn first_block(&self) -> Option { + unimplemented!() + } + + /// Get raw block data + fn block(&self, hash: &H256) -> Option { + self.blocks.get(hash).cloned().map(encoded::Block::new) + } + + fn block_header_data(&self, hash: &H256) -> Option { + self.block(hash) + .map(|b| b.header_view().rlp().as_raw().to_vec()) + .map(encoded::Header::new) + } + + fn block_body(&self, hash: &H256) -> Option { + self.block(hash) + .map(|b| BlockChain::block_to_body(&b.into_inner())) + .map(encoded::Body::new) + } + + fn best_ancient_block(&self) -> Option { + None + } + + /// Get the familial details concerning a block. + fn block_details(&self, hash: &H256) -> Option { + self.blocks.get(hash).map(|bytes| { + let header = Unverified::from_rlp(bytes.to_vec()).unwrap().header; + BlockDetails { + number: header.number(), + total_difficulty: *header.difficulty(), + parent: *header.parent_hash(), + children: Vec::new(), + is_finalized: false, + } + }) + } + + fn transaction_address(&self, _hash: &H256) -> Option { + unimplemented!() + } + + /// Get the hash of given block's number. + fn block_hash(&self, index: BlockNumber) -> Option { + self.numbers.get(&index).cloned() + } + + fn block_receipts(&self, _hash: &H256) -> Option { + unimplemented!() + } + + fn blocks_with_bloom<'a, B, I, II>( + &self, + _blooms: II, + _from_block: BlockNumber, + _to_block: BlockNumber, + ) -> Vec + where + BloomRef<'a>: From, + II: IntoIterator + Copy, + I: Iterator, + Self: Sized, + { + unimplemented!() + } + + fn logs( + &self, + _blocks: Vec, + _matches: F, + _limit: Option, + ) -> Vec + where + F: Fn(&LogEntry) -> bool, + Self: Sized, + { + unimplemented!() + } + } + + fn basic_test(bytes: &[u8], engine: &dyn EthEngine) -> Result<(), Error> { + let unverified = Unverified::from_rlp(bytes.to_vec())?; + verify_block_basic(&unverified, engine, true) + } + + fn family_test(bytes: &[u8], engine: &dyn EthEngine, bc: &BC) -> Result<(), Error> + where + BC: BlockProvider, + { + let block = Unverified::from_rlp(bytes.to_vec()).unwrap(); + let header = block.header; + let transactions: Vec<_> = block + .transactions + .into_iter() + .map(SignedTransaction::new) + .collect::>()?; + + // TODO: client is really meant to be used for state query here by machine + // additions that need access to state (tx filter in specific) + // no existing tests need access to test, so having this not function + // is fine. + let client = ::client::TestBlockChainClient::default(); + let parent = bc + .block_header_data(header.parent_hash()) + .ok_or(BlockError::UnknownParent(*header.parent_hash()))? + .decode()?; + + let block = PreverifiedBlock { + header, + transactions, + uncles: block.uncles, + bytes: bytes.to_vec(), + }; + + let full_params = FullFamilyParams { + block: &block, + block_provider: bc as &dyn BlockProvider, + client: &client, + }; + verify_block_family(&block.header, &parent, engine, Some(full_params)) + } + + fn unordered_test(bytes: &[u8], engine: &dyn EthEngine) -> Result<(), Error> { + let un = Unverified::from_rlp(bytes.to_vec())?; + verify_block_unordered(un, engine, false)?; + Ok(()) + } + + #[test] + fn test_verify_block_basic_with_invalid_transactions() { + let spec = Spec::new_test(); + let engine = &*spec.engine; + + let block = { + let mut rlp = rlp::RlpStream::new_list(3); + let mut header = Header::default(); + // that's an invalid transaction list rlp + let invalid_transactions = vec![vec![0u8]]; + header.set_transactions_root(ordered_trie_root(&invalid_transactions)); + header.set_gas_limit(engine.params().min_gas_limit); + rlp.append(&header); + rlp.append_list::, _>(&invalid_transactions); + rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1); + rlp.out() + }; + + assert!(basic_test(&block, engine).is_err()); + } + + #[test] + fn test_verify_block() { + use rlp::RlpStream; + + // Test against morden + let mut good = Header::new(); + let spec = Spec::new_test(); + let engine = &*spec.engine; + + let min_gas_limit = engine.params().min_gas_limit; + good.set_gas_limit(min_gas_limit); + good.set_timestamp(40); + good.set_number(10); + + let keypair = Random.generate().unwrap(); + + let tr1 = Transaction { + action: Action::Create, + value: U256::from(0), + data: Bytes::new(), + gas: U256::from(30_000), + gas_price: U256::from(40_000), + nonce: U256::one(), + } + .sign(keypair.secret(), None); + + let tr2 = Transaction { + action: Action::Create, + value: U256::from(0), + data: Bytes::new(), + gas: U256::from(30_000), + gas_price: U256::from(40_000), + nonce: U256::from(2), + } + .sign(keypair.secret(), None); + + let tr3 = Transaction { + action: Action::Call(0x0.into()), + value: U256::from(0), + data: Bytes::new(), + gas: U256::from(30_000), + gas_price: U256::from(0), + nonce: U256::zero(), + } + .null_sign(0); + + let good_transactions = [tr1.clone(), tr2.clone()]; + let eip86_transactions = [tr3.clone()]; + + let diff_inc = U256::from(0x40); + + let mut parent6 = good.clone(); + parent6.set_number(6); + let mut parent7 = good.clone(); + parent7.set_number(7); + parent7.set_parent_hash(parent6.hash()); + parent7.set_difficulty(parent6.difficulty().clone() + diff_inc); + parent7.set_timestamp(parent6.timestamp() + 10); + let mut parent8 = good.clone(); + parent8.set_number(8); + parent8.set_parent_hash(parent7.hash()); + parent8.set_difficulty(parent7.difficulty().clone() + diff_inc); + parent8.set_timestamp(parent7.timestamp() + 10); + + let mut good_uncle1 = good.clone(); + good_uncle1.set_number(9); + good_uncle1.set_parent_hash(parent8.hash()); + good_uncle1.set_difficulty(parent8.difficulty().clone() + diff_inc); + good_uncle1.set_timestamp(parent8.timestamp() + 10); + let mut ex = good_uncle1.extra_data().to_vec(); + ex.push(1u8); + good_uncle1.set_extra_data(ex); + + let mut good_uncle2 = good.clone(); + good_uncle2.set_number(8); + good_uncle2.set_parent_hash(parent7.hash()); + good_uncle2.set_difficulty(parent7.difficulty().clone() + diff_inc); + good_uncle2.set_timestamp(parent7.timestamp() + 10); + let mut ex = good_uncle2.extra_data().to_vec(); + ex.push(2u8); + good_uncle2.set_extra_data(ex); + + let good_uncles = vec![good_uncle1.clone(), good_uncle2.clone()]; + let mut uncles_rlp = RlpStream::new(); + uncles_rlp.append_list(&good_uncles); + let good_uncles_hash = keccak(uncles_rlp.as_raw()); + let good_transactions_root = ordered_trie_root( + good_transactions + .iter() + .map(|t| ::rlp::encode::(t)), + ); + let eip86_transactions_root = ordered_trie_root( + eip86_transactions + .iter() + .map(|t| ::rlp::encode::(t)), + ); + + let mut parent = good.clone(); + parent.set_number(9); + parent.set_timestamp(parent8.timestamp() + 10); + parent.set_parent_hash(parent8.hash()); + parent.set_difficulty(parent8.difficulty().clone() + diff_inc); + + good.set_parent_hash(parent.hash()); + good.set_difficulty(parent.difficulty().clone() + diff_inc); + good.set_timestamp(parent.timestamp() + 10); + + let mut bc = TestBlockChain::new(); + bc.insert(create_test_block(&good)); + bc.insert(create_test_block(&parent)); + bc.insert(create_test_block(&parent6)); + bc.insert(create_test_block(&parent7)); + bc.insert(create_test_block(&parent8)); + + check_ok(basic_test(&create_test_block(&good), engine)); + + let mut bad_header = good.clone(); + bad_header.set_transactions_root(eip86_transactions_root.clone()); + bad_header.set_uncles_hash(good_uncles_hash.clone()); + match basic_test(&create_test_block_with_data(&bad_header, &eip86_transactions, &good_uncles), engine) { Err(Error(ErrorKind::Transaction(ref e), _)) if e == &::ethkey::Error::InvalidSignature.into() => (), e => panic!("Block verification failed.\nExpected: Transaction Error (Invalid Signature)\nGot: {:?}", e), } - let mut header = good.clone(); - header.set_transactions_root(good_transactions_root.clone()); - header.set_uncles_hash(good_uncles_hash.clone()); - check_ok(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine)); - - header.set_gas_limit(min_gas_limit - 1); - check_fail(basic_test(&create_test_block(&header), engine), - InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: header.gas_limit().clone() })); - - header = good.clone(); - header.set_number(BlockNumber::max_value()); - check_fail(basic_test(&create_test_block(&header), engine), - RidiculousNumber(OutOfBounds { max: Some(BlockNumber::max_value()), min: None, found: header.number() })); - - header = good.clone(); - let gas_used = header.gas_limit().clone() + 1; - header.set_gas_used(gas_used); - check_fail(basic_test(&create_test_block(&header), engine), - TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit().clone()), min: None, found: header.gas_used().clone() })); - - header = good.clone(); - let mut ex = header.extra_data().to_vec(); - ex.resize(engine.maximum_extra_data_size() + 1, 0u8); - header.set_extra_data(ex); - check_fail(basic_test(&create_test_block(&header), engine), - ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data().len() })); - - header = good.clone(); - let mut ex = header.extra_data().to_vec(); - ex.resize(engine.maximum_extra_data_size() + 1, 0u8); - header.set_extra_data(ex); - check_fail(basic_test(&create_test_block(&header), engine), - ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data().len() })); - - header = good.clone(); - header.set_uncles_hash(good_uncles_hash.clone()); - check_fail(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine), - InvalidTransactionsRoot(Mismatch { expected: good_transactions_root.clone(), found: header.transactions_root().clone() })); - - header = good.clone(); - header.set_transactions_root(good_transactions_root.clone()); - check_fail(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine), - InvalidUnclesHash(Mismatch { expected: good_uncles_hash.clone(), found: header.uncles_hash().clone() })); - - check_ok(family_test(&create_test_block(&good), engine, &bc)); - check_ok(family_test(&create_test_block_with_data(&good, &good_transactions, &good_uncles), engine, &bc)); - - header = good.clone(); - header.set_parent_hash(H256::random()); - check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine, &bc), - UnknownParent(header.parent_hash().clone())); - - header = good.clone(); - header.set_timestamp(10); - check_fail_timestamp(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine, &bc), false); - - header = good.clone(); - // will return `BlockError::TimestampOverflow` when timestamp > `i32::max_value()` - header.set_timestamp(i32::max_value() as u64); - check_fail_timestamp(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine), false); - - header = good.clone(); - header.set_timestamp(SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 20); - check_fail_timestamp(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine), true); - - header = good.clone(); - header.set_timestamp(SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 10); - header.set_uncles_hash(good_uncles_hash.clone()); - header.set_transactions_root(good_transactions_root.clone()); - check_ok(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine)); - - header = good.clone(); - header.set_number(9); - check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine, &bc), - InvalidNumber(Mismatch { expected: parent.number() + 1, found: header.number() })); - - header = good.clone(); - let mut bad_uncles = good_uncles.clone(); - bad_uncles.push(good_uncle1.clone()); - check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &bad_uncles), engine, &bc), - TooManyUncles(OutOfBounds { max: Some(engine.maximum_uncle_count(header.number())), min: None, found: bad_uncles.len() })); - - header = good.clone(); - bad_uncles = vec![ good_uncle1.clone(), good_uncle1.clone() ]; - check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &bad_uncles), engine, &bc), - DuplicateUncle(good_uncle1.hash())); - - header = good.clone(); - header.set_gas_limit(0.into()); - header.set_difficulty("0000000000000000000000000000000000000000000000000000000000020000".parse::().unwrap()); - match family_test(&create_test_block(&header), engine, &bc) { - Err(Error(ErrorKind::Block(InvalidGasLimit(_)), _)) => {}, - Err(_) => { panic!("should be invalid difficulty fail"); }, - _ => { panic!("Should be error, got Ok"); }, - } - - // TODO: some additional uncle checks - } - - #[test] - fn dust_protection() { - use ethkey::{Generator, Random}; - use types::transaction::{Transaction, Action}; - use machine::EthereumMachine; - use engines::NullEngine; - - let mut params = CommonParams::default(); - params.dust_protection_transition = 0; - params.nonce_cap_increment = 2; - - let mut header = Header::default(); - header.set_number(1); - - let keypair = Random.generate().unwrap(); - let bad_transactions: Vec<_> = (0..3).map(|i| Transaction { - action: Action::Create, - value: U256::zero(), - data: Vec::new(), - gas: 0.into(), - gas_price: U256::zero(), - nonce: i.into(), - }.sign(keypair.secret(), None)).collect(); - - let good_transactions = [bad_transactions[0].clone(), bad_transactions[1].clone()]; - - let machine = EthereumMachine::regular(params, BTreeMap::new()); - let engine = NullEngine::new(Default::default(), machine); - check_fail(unordered_test(&create_test_block_with_data(&header, &bad_transactions, &[]), &engine), TooManyTransactions(keypair.address())); - unordered_test(&create_test_block_with_data(&header, &good_transactions, &[]), &engine).unwrap(); - } + let mut header = good.clone(); + header.set_transactions_root(good_transactions_root.clone()); + header.set_uncles_hash(good_uncles_hash.clone()); + check_ok(basic_test( + &create_test_block_with_data(&header, &good_transactions, &good_uncles), + engine, + )); + + header.set_gas_limit(min_gas_limit - 1); + check_fail( + basic_test(&create_test_block(&header), engine), + InvalidGasLimit(OutOfBounds { + min: Some(min_gas_limit), + max: None, + found: header.gas_limit().clone(), + }), + ); + + header = good.clone(); + header.set_number(BlockNumber::max_value()); + check_fail( + basic_test(&create_test_block(&header), engine), + RidiculousNumber(OutOfBounds { + max: Some(BlockNumber::max_value()), + min: None, + found: header.number(), + }), + ); + + header = good.clone(); + let gas_used = header.gas_limit().clone() + 1; + header.set_gas_used(gas_used); + check_fail( + basic_test(&create_test_block(&header), engine), + TooMuchGasUsed(OutOfBounds { + max: Some(header.gas_limit().clone()), + min: None, + found: header.gas_used().clone(), + }), + ); + + header = good.clone(); + let mut ex = header.extra_data().to_vec(); + ex.resize(engine.maximum_extra_data_size() + 1, 0u8); + header.set_extra_data(ex); + check_fail( + basic_test(&create_test_block(&header), engine), + ExtraDataOutOfBounds(OutOfBounds { + max: Some(engine.maximum_extra_data_size()), + min: None, + found: header.extra_data().len(), + }), + ); + + header = good.clone(); + let mut ex = header.extra_data().to_vec(); + ex.resize(engine.maximum_extra_data_size() + 1, 0u8); + header.set_extra_data(ex); + check_fail( + basic_test(&create_test_block(&header), engine), + ExtraDataOutOfBounds(OutOfBounds { + max: Some(engine.maximum_extra_data_size()), + min: None, + found: header.extra_data().len(), + }), + ); + + header = good.clone(); + header.set_uncles_hash(good_uncles_hash.clone()); + check_fail( + basic_test( + &create_test_block_with_data(&header, &good_transactions, &good_uncles), + engine, + ), + InvalidTransactionsRoot(Mismatch { + expected: good_transactions_root.clone(), + found: header.transactions_root().clone(), + }), + ); + + header = good.clone(); + header.set_transactions_root(good_transactions_root.clone()); + check_fail( + basic_test( + &create_test_block_with_data(&header, &good_transactions, &good_uncles), + engine, + ), + InvalidUnclesHash(Mismatch { + expected: good_uncles_hash.clone(), + found: header.uncles_hash().clone(), + }), + ); + + check_ok(family_test(&create_test_block(&good), engine, &bc)); + check_ok(family_test( + &create_test_block_with_data(&good, &good_transactions, &good_uncles), + engine, + &bc, + )); + + header = good.clone(); + header.set_parent_hash(H256::random()); + check_fail( + family_test( + &create_test_block_with_data(&header, &good_transactions, &good_uncles), + engine, + &bc, + ), + UnknownParent(header.parent_hash().clone()), + ); + + header = good.clone(); + header.set_timestamp(10); + check_fail_timestamp( + family_test( + &create_test_block_with_data(&header, &good_transactions, &good_uncles), + engine, + &bc, + ), + false, + ); + + header = good.clone(); + // will return `BlockError::TimestampOverflow` when timestamp > `i32::max_value()` + header.set_timestamp(i32::max_value() as u64); + check_fail_timestamp( + basic_test( + &create_test_block_with_data(&header, &good_transactions, &good_uncles), + engine, + ), + false, + ); + + header = good.clone(); + header.set_timestamp( + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() + + 20, + ); + check_fail_timestamp( + basic_test( + &create_test_block_with_data(&header, &good_transactions, &good_uncles), + engine, + ), + true, + ); + + header = good.clone(); + header.set_timestamp( + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() + + 10, + ); + header.set_uncles_hash(good_uncles_hash.clone()); + header.set_transactions_root(good_transactions_root.clone()); + check_ok(basic_test( + &create_test_block_with_data(&header, &good_transactions, &good_uncles), + engine, + )); + + header = good.clone(); + header.set_number(9); + check_fail( + family_test( + &create_test_block_with_data(&header, &good_transactions, &good_uncles), + engine, + &bc, + ), + InvalidNumber(Mismatch { + expected: parent.number() + 1, + found: header.number(), + }), + ); + + header = good.clone(); + let mut bad_uncles = good_uncles.clone(); + bad_uncles.push(good_uncle1.clone()); + check_fail( + family_test( + &create_test_block_with_data(&header, &good_transactions, &bad_uncles), + engine, + &bc, + ), + TooManyUncles(OutOfBounds { + max: Some(engine.maximum_uncle_count(header.number())), + min: None, + found: bad_uncles.len(), + }), + ); + + header = good.clone(); + bad_uncles = vec![good_uncle1.clone(), good_uncle1.clone()]; + check_fail( + family_test( + &create_test_block_with_data(&header, &good_transactions, &bad_uncles), + engine, + &bc, + ), + DuplicateUncle(good_uncle1.hash()), + ); + + header = good.clone(); + header.set_gas_limit(0.into()); + header.set_difficulty( + "0000000000000000000000000000000000000000000000000000000000020000" + .parse::() + .unwrap(), + ); + match family_test(&create_test_block(&header), engine, &bc) { + Err(Error(ErrorKind::Block(InvalidGasLimit(_)), _)) => {} + Err(_) => { + panic!("should be invalid difficulty fail"); + } + _ => { + panic!("Should be error, got Ok"); + } + } + + // TODO: some additional uncle checks + } + + #[test] + fn dust_protection() { + use engines::NullEngine; + use ethkey::{Generator, Random}; + use machine::EthereumMachine; + use types::transaction::{Action, Transaction}; + + let mut params = CommonParams::default(); + params.dust_protection_transition = 0; + params.nonce_cap_increment = 2; + + let mut header = Header::default(); + header.set_number(1); + + let keypair = Random.generate().unwrap(); + let bad_transactions: Vec<_> = (0..3) + .map(|i| { + Transaction { + action: Action::Create, + value: U256::zero(), + data: Vec::new(), + gas: 0.into(), + gas_price: U256::zero(), + nonce: i.into(), + } + .sign(keypair.secret(), None) + }) + .collect(); + + let good_transactions = [bad_transactions[0].clone(), bad_transactions[1].clone()]; + + let machine = EthereumMachine::regular(params, BTreeMap::new()); + let engine = NullEngine::new(Default::default(), machine); + check_fail( + unordered_test( + &create_test_block_with_data(&header, &bad_transactions, &[]), + &engine, + ), + TooManyTransactions(keypair.address()), + ); + unordered_test( + &create_test_block_with_data(&header, &good_transactions, &[]), + &engine, + ) + .unwrap(); + } } diff --git a/ethcore/src/verification/verifier.rs b/ethcore/src/verification/verifier.rs index 76eb60b9a18..c74af43e7c3 100644 --- a/ethcore/src/verification/verifier.rs +++ b/ethcore/src/verification/verifier.rs @@ -1,43 +1,44 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! A generic verifier trait. +use super::verification; use call_contract::CallContract; use client::BlockInfo; use engines::EthEngine; use error::Error; use types::header::Header; -use super::verification; /// Should be used to verify blocks. pub trait Verifier: Send + Sync - where C: BlockInfo + CallContract +where + C: BlockInfo + CallContract, { - /// Verify a block relative to its parent and uncles. - fn verify_block_family( - &self, - header: &Header, - parent: &Header, - engine: &EthEngine, - do_full: Option> - ) -> Result<(), Error>; + /// Verify a block relative to its parent and uncles. + fn verify_block_family( + &self, + header: &Header, + parent: &Header, + engine: &dyn EthEngine, + do_full: Option>, + ) -> Result<(), Error>; - /// Do a final verification check for an enacted header vs its expected counterpart. - fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error>; - /// Verify a block, inspecing external state. - fn verify_block_external(&self, header: &Header, engine: &EthEngine) -> Result<(), Error>; + /// Do a final verification check for an enacted header vs its expected counterpart. + fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error>; + /// Verify a block, inspecing external state. + fn verify_block_external(&self, header: &Header, engine: &dyn EthEngine) -> Result<(), Error>; } diff --git a/ethcore/sync/Cargo.toml b/ethcore/sync/Cargo.toml index bc7a80e6700..4ea7ba1aef1 100644 --- a/ethcore/sync/Cargo.toml +++ b/ethcore/sync/Cargo.toml @@ -1,5 +1,5 @@ [package] -description = "Ethcore blockchain sync" +description = "Parity Ethereum (EthCore) Blockchain Synchronization (Strategy, Blockchain Downloader, Blockchain Synchronization Implementation of Ethereum Protocol, Propagating Data to Peers, Requesting Data from Peers, Supplying Data in Response to Peer Requests, Handling Peer Responses, Matching Packet IDs and Protocol, Light Client Synchronization of Header Chain, Header Download State Machine, Light Decoding & Verifying Header Responses, Private Transaction Handling, Synchronization Snapshot Service to Download & Verify Block Chunks, Peer Connection Management and Blockchain Client I/O Interface for Synchronization Handler, Transaction Statistics)" name = "ethcore-sync" version = "1.12.0" license = "GPL-3.0" @@ -10,11 +10,13 @@ authors = ["Parity Technologies "] [dependencies] common-types = { path = "../types" } enum_primitive = "0.1.1" +derive_more = "0.99" ethcore = { path = ".." } ethcore-io = { path = "../../util/io" } -ethcore-light = { path = "../light" } ethcore-network = { path = "../../util/network" } ethcore-network-devp2p = { path = "../../util/network-devp2p" } +ethereum-forkid = "0.2" +primitive_types07 = { package = "primitive-types", version = "0.7"} ethereum-types = "0.4" ethkey = { path = "../../accounts/ethkey" } ethstore = { path = "../../accounts/ethstore" } @@ -30,13 +32,15 @@ parity-bytes = "0.1" parking_lot = "0.7" rand = "0.4" rlp = { version = "0.3.0", features = ["ethereum"] } +rlp04 = { package = "rlp", version = "0.4" } trace-time = "0.1" triehash-ethereum = {version = "0.2", path = "../../util/triehash-ethereum" } +stats = { path = "../../util/stats" } + [dev-dependencies] env_logger = "0.5" ethcore = { path = "..", features = ["test-helpers"] } ethcore-io = { path = "../../util/io", features = ["mio"] } -ethcore-private-tx = { path = "../private-tx" } kvdb-memorydb = "0.1" rustc-hex = "1.0" diff --git a/ethcore/sync/src/api.rs b/ethcore/sync/src/api.rs index 4a66f468d59..a9e8a2470f8 100644 --- a/ethcore/sync/src/api.rs +++ b/ethcore/sync/src/api.rs @@ -1,234 +1,180 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::sync::{Arc, mpsc, atomic}; -use std::collections::{HashMap, BTreeMap}; -use std::io; -use std::ops::RangeInclusive; -use std::time::Duration; use bytes::Bytes; use devp2p::NetworkService; -use network::{NetworkProtocolHandler, NetworkContext, PeerId, ProtocolId, - NetworkConfiguration as BasicNetworkConfiguration, NonReservedPeerMode, Error, ErrorKind, - ConnectionFilter}; -use network::client_version::ClientVersion; +use network::{ + client_version::ClientVersion, ConnectionFilter, Error, ErrorKind, + NetworkConfiguration as BasicNetworkConfiguration, NetworkContext, NetworkProtocolHandler, + NonReservedPeerMode, PeerId, ProtocolId, +}; +use std::{ + collections::{BTreeMap, BTreeSet, HashMap}, + io, + ops::RangeInclusive, + sync::{atomic, mpsc, Arc}, + time::Duration, +}; -use types::pruning_info::PruningInfo; +use chain::{ + fork_filter::ForkFilterApi, ChainSyncApi, SyncState, SyncStatus as EthSyncStatus, + ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_64, PAR_PROTOCOL_VERSION_1, + PAR_PROTOCOL_VERSION_2, +}; +use ethcore::{ + client::{BlockChainClient, ChainMessageType, ChainNotify, NewBlocks}, + snapshot::SnapshotService, +}; use ethereum_types::{H256, H512, U256}; -use io::{TimerToken}; use ethkey::Secret; -use ethcore::client::{BlockChainClient, ChainNotify, NewBlocks, ChainMessageType}; -use ethcore::snapshot::SnapshotService; -use types::BlockNumber; -use sync_io::NetSyncIo; -use chain::{ChainSyncApi, SyncStatus as EthSyncStatus}; -use std::net::{SocketAddr, AddrParseError}; -use std::str::FromStr; -use parking_lot::{RwLock, Mutex}; -use chain::{ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_62, - PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, PAR_PROTOCOL_VERSION_3}; -use chain::sync_packet::SyncPacket::{PrivateTransactionPacket, SignedPrivateTransactionPacket}; -use light::client::AsLightClient; -use light::Provider; -use light::net::{ - self as light_net, LightProtocol, Params as LightParams, - Capabilities, Handler as LightHandler, EventContext, SampleStore, -}; +use io::TimerToken; use network::IpFilter; -use private_tx::PrivateTxHandler; -use types::transaction::UnverifiedTransaction; +use parking_lot::{Mutex, RwLock}; +use stats::{prometheus, prometheus_counter, prometheus_gauge, PrometheusMetrics}; -use super::light_sync::SyncInfo; +use std::{ + net::{AddrParseError, SocketAddr}, + str::FromStr, +}; +use sync_io::NetSyncIo; +use types::{ + creation_status::CreationStatus, restoration_status::RestorationStatus, + transaction::UnverifiedTransaction, BlockNumber, +}; -/// Parity sync protocol -pub const WARP_SYNC_PROTOCOL_ID: ProtocolId = *b"par"; +/// OpenEthereum sync protocol +pub const PAR_PROTOCOL: ProtocolId = *b"par"; /// Ethereum sync protocol pub const ETH_PROTOCOL: ProtocolId = *b"eth"; -/// Ethereum light protocol -pub const LIGHT_PROTOCOL: ProtocolId = *b"pip"; /// Determine warp sync status. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum WarpSync { - /// Warp sync is enabled. - Enabled, - /// Warp sync is disabled. - Disabled, - /// Only warp sync is allowed (no regular sync) and only after given block number. - OnlyAndAfter(BlockNumber), + /// Warp sync is enabled. + Enabled, + /// Warp sync is disabled. + Disabled, + /// Only warp sync is allowed (no regular sync) and only after given block number. + OnlyAndAfter(BlockNumber), } impl WarpSync { - /// Returns true if warp sync is enabled. - pub fn is_enabled(&self) -> bool { - match *self { - WarpSync::Enabled => true, - WarpSync::OnlyAndAfter(_) => true, - WarpSync::Disabled => false, - } - } - - /// Returns `true` if we are in warp-only mode. - /// - /// i.e. we will never fall back to regular sync - /// until given block number is reached by - /// successfuly finding and restoring from a snapshot. - pub fn is_warp_only(&self) -> bool { - if let WarpSync::OnlyAndAfter(_) = *self { - true - } else { - false - } - } + /// Returns true if warp sync is enabled. + pub fn is_enabled(&self) -> bool { + match *self { + WarpSync::Enabled => true, + WarpSync::OnlyAndAfter(_) => true, + WarpSync::Disabled => false, + } + } + + /// Returns `true` if we are in warp-only mode. + /// + /// i.e. we will never fall back to regular sync + /// until given block number is reached by + /// successfuly finding and restoring from a snapshot. + pub fn is_warp_only(&self) -> bool { + if let WarpSync::OnlyAndAfter(_) = *self { + true + } else { + false + } + } } /// Sync configuration #[derive(Debug, Clone, Copy)] pub struct SyncConfig { - /// Max blocks to download ahead - pub max_download_ahead_blocks: usize, - /// Enable ancient block download. - pub download_old_blocks: bool, - /// Network ID - pub network_id: u64, - /// Main "eth" subprotocol name. - pub subprotocol_name: [u8; 3], - /// Light subprotocol name. - pub light_subprotocol_name: [u8; 3], - /// Fork block to check - pub fork_block: Option<(BlockNumber, H256)>, - /// Enable snapshot sync - pub warp_sync: WarpSync, - /// Enable light client server. - pub serve_light: bool, + /// Max blocks to download ahead + pub max_download_ahead_blocks: usize, + /// Enable ancient block download. + pub download_old_blocks: bool, + /// Network ID + pub network_id: u64, + /// Main "eth" subprotocol name. + pub subprotocol_name: [u8; 3], + /// Fork block to check + pub fork_block: Option<(BlockNumber, H256)>, + /// Enable snapshot sync + pub warp_sync: WarpSync, } impl Default for SyncConfig { - fn default() -> SyncConfig { - SyncConfig { - max_download_ahead_blocks: 20000, - download_old_blocks: true, - network_id: 1, - subprotocol_name: ETH_PROTOCOL, - light_subprotocol_name: LIGHT_PROTOCOL, - fork_block: None, - warp_sync: WarpSync::Disabled, - serve_light: false, - } - } + fn default() -> SyncConfig { + SyncConfig { + max_download_ahead_blocks: 20000, + download_old_blocks: true, + network_id: 1, + subprotocol_name: ETH_PROTOCOL, + fork_block: None, + warp_sync: WarpSync::Disabled, + } + } } /// Current sync status -pub trait SyncProvider: Send + Sync { - /// Get sync status - fn status(&self) -> EthSyncStatus; +pub trait SyncProvider: Send + Sync + PrometheusMetrics { + /// Get sync status + fn status(&self) -> EthSyncStatus; - /// Get peers information - fn peers(&self) -> Vec; + /// Get peers information + fn peers(&self) -> Vec; - /// Get the enode if available. - fn enode(&self) -> Option; + /// Get the enode if available. + fn enode(&self) -> Option; - /// Returns propagation count for pending transactions. - fn transactions_stats(&self) -> BTreeMap; + /// Returns propagation count for pending transactions. + fn transactions_stats(&self) -> BTreeMap; } /// Transaction stats #[derive(Debug)] pub struct TransactionStats { - /// Block number where this TX was first seen. - pub first_seen: u64, - /// Peers it was propagated to. - pub propagated_to: BTreeMap, + /// Block number where this TX was first seen. + pub first_seen: u64, + /// Peers it was propagated to. + pub propagated_to: BTreeMap, } /// Peer connection information #[derive(Debug)] pub struct PeerInfo { - /// Public node id - pub id: Option, - /// Node client ID - pub client_version: ClientVersion, - /// Capabilities - pub capabilities: Vec, - /// Remote endpoint address - pub remote_address: String, - /// Local endpoint address - pub local_address: String, - /// Eth protocol info. - pub eth_info: Option, - /// Light protocol info. - pub pip_info: Option, + /// Public node id + pub id: Option, + /// Node client ID + pub client_version: ClientVersion, + /// Capabilities + pub capabilities: Vec, + /// Remote endpoint address + pub remote_address: String, + /// Local endpoint address + pub local_address: String, + /// Eth protocol info. + pub eth_info: Option, } /// Ethereum protocol info. #[derive(Debug)] pub struct EthProtocolInfo { - /// Protocol version - pub version: u32, - /// SHA3 of peer best block hash - pub head: H256, - /// Peer total difficulty if known - pub difficulty: Option, -} - -/// PIP protocol info. -#[derive(Debug)] -pub struct PipProtocolInfo { - /// Protocol version - pub version: u32, - /// SHA3 of peer best block hash - pub head: H256, - /// Peer total difficulty if known - pub difficulty: U256, -} - -impl From for PipProtocolInfo { - fn from(status: light_net::Status) -> Self { - PipProtocolInfo { - version: status.protocol_version, - head: status.head_hash, - difficulty: status.head_td, - } - } -} - -/// Configuration to attach alternate protocol handlers. -/// Only works when IPC is disabled. -pub struct AttachedProtocol { - /// The protocol handler in question. - pub handler: Arc, - /// 3-character ID for the protocol. - pub protocol_id: ProtocolId, - /// Supported versions and their packet counts. - pub versions: &'static [(u8, u8)], -} - -impl AttachedProtocol { - fn register(&self, network: &NetworkService) { - let res = network.register_protocol( - self.handler.clone(), - self.protocol_id, - self.versions - ); - - if let Err(e) = res { - warn!(target: "sync", "Error attaching protocol {:?}: {:?}", self.protocol_id, e); - } - } + /// Protocol version + pub version: u32, + /// SHA3 of peer best block hash + pub head: H256, + /// Peer total difficulty if known + pub difficulty: Option, } /// A prioritized tasks run in a specialised timer. @@ -238,188 +184,250 @@ impl AttachedProtocol { /// that happens here should work even if the task is cancelled. #[derive(Debug)] pub enum PriorityTask { - /// Propagate given block - PropagateBlock { - /// When the task was initiated - started: ::std::time::Instant, - /// Raw block RLP to propagate - block: Bytes, - /// Block hash - hash: H256, - /// Blocks difficulty - difficulty: U256, - }, - /// Propagate a list of transactions - PropagateTransactions(::std::time::Instant, Arc), + /// Propagate given block + PropagateBlock { + /// When the task was initiated + started: ::std::time::Instant, + /// Raw block RLP to propagate + block: Bytes, + /// Block hash + hash: H256, + /// Blocks difficulty + difficulty: U256, + }, + /// Propagate a list of transactions + PropagateTransactions(::std::time::Instant, Arc), } impl PriorityTask { - /// Mark the task as being processed, right after it's retrieved from the queue. - pub fn starting(&self) { - match *self { - PriorityTask::PropagateTransactions(_, ref is_ready) => is_ready.store(true, atomic::Ordering::SeqCst), - _ => {}, - } - } + /// Mark the task as being processed, right after it's retrieved from the queue. + pub fn starting(&self) { + match *self { + PriorityTask::PropagateTransactions(_, ref is_ready) => { + is_ready.store(true, atomic::Ordering::SeqCst) + } + _ => {} + } + } } /// EthSync initialization parameters. pub struct Params { - /// Configuration. - pub config: SyncConfig, - /// Blockchain client. - pub chain: Arc, - /// Snapshot service. - pub snapshot_service: Arc, - /// Private tx service. - pub private_tx_handler: Option>, - /// Light data provider. - pub provider: Arc<::light::Provider>, - /// Network layer configuration. - pub network_config: NetworkConfiguration, - /// Other protocols to attach. - pub attached_protos: Vec, + /// Configuration. + pub config: SyncConfig, + /// Blockchain client. + pub chain: Arc, + /// Forks. + pub forks: BTreeSet, + /// Snapshot service. + pub snapshot_service: Arc, + /// Network layer configuration. + pub network_config: NetworkConfiguration, } /// Ethereum network protocol handler pub struct EthSync { - /// Network service - network: NetworkService, - /// Main (eth/par) protocol handler - eth_handler: Arc, - /// Light (pip) protocol handler - light_proto: Option>, - /// Other protocols to attach. - attached_protos: Vec, - /// The main subprotocol name - subprotocol_name: [u8; 3], - /// Light subprotocol name. - light_subprotocol_name: [u8; 3], - /// Priority tasks notification channel - priority_tasks: Mutex>, -} - -fn light_params( - network_id: u64, - median_peers: f64, - pruning_info: PruningInfo, - sample_store: Option>, -) -> LightParams { - let mut light_params = LightParams { - network_id: network_id, - config: Default::default(), - capabilities: Capabilities { - serve_headers: true, - serve_chain_since: Some(pruning_info.earliest_chain), - serve_state_since: Some(pruning_info.earliest_state), - tx_relay: true, - }, - sample_store: sample_store, - }; - - light_params.config.median_peers = median_peers; - light_params + /// Network service + network: NetworkService, + /// Main (eth/par) protocol handler + eth_handler: Arc, + /// The main subprotocol name + subprotocol_name: [u8; 3], + /// Priority tasks notification channel + priority_tasks: Mutex>, } impl EthSync { - /// Creates and register protocol with the network service - pub fn new(params: Params, connection_filter: Option>) -> Result, Error> { - let pruning_info = params.chain.pruning_info(); - let light_proto = match params.config.serve_light { - false => None, - true => Some({ - let sample_store = params.network_config.net_config_path - .clone() - .map(::std::path::PathBuf::from) - .map(|mut p| { p.push("request_timings"); light_net::FileStore(p) }) - .map(|store| Box::new(store) as Box<_>); - - let median_peers = (params.network_config.min_peers + params.network_config.max_peers) as f64 / 2.0; - let light_params = light_params( - params.config.network_id, - median_peers, - pruning_info, - sample_store, - ); - - let mut light_proto = LightProtocol::new(params.provider, light_params); - light_proto.add_handler(Arc::new(TxRelay(params.chain.clone()))); - - Arc::new(light_proto) - }) - }; - - let (priority_tasks_tx, priority_tasks_rx) = mpsc::channel(); - let sync = ChainSyncApi::new( - params.config, - &*params.chain, - params.private_tx_handler.as_ref().cloned(), - priority_tasks_rx, - ); - let service = NetworkService::new(params.network_config.clone().into_basic()?, connection_filter)?; - - let sync = Arc::new(EthSync { - network: service, - eth_handler: Arc::new(SyncProtocolHandler { - sync, - chain: params.chain, - snapshot_service: params.snapshot_service, - overlay: RwLock::new(HashMap::new()), - }), - light_proto: light_proto, - subprotocol_name: params.config.subprotocol_name, - light_subprotocol_name: params.config.light_subprotocol_name, - attached_protos: params.attached_protos, - priority_tasks: Mutex::new(priority_tasks_tx), - }); - - Ok(sync) - } - - /// Priority tasks producer - pub fn priority_tasks(&self) -> mpsc::Sender { - self.priority_tasks.lock().clone() - } + /// Creates and register protocol with the network service + pub fn new( + params: Params, + connection_filter: Option>, + ) -> Result, Error> { + let (priority_tasks_tx, priority_tasks_rx) = mpsc::channel(); + let fork_filter = ForkFilterApi::new(&*params.chain, params.forks); + + let sync = ChainSyncApi::new( + params.config, + &*params.chain, + fork_filter, + priority_tasks_rx, + ); + let service = NetworkService::new( + params.network_config.clone().into_basic()?, + connection_filter, + )?; + + let sync = Arc::new(EthSync { + network: service, + eth_handler: Arc::new(SyncProtocolHandler { + sync, + chain: params.chain, + snapshot_service: params.snapshot_service, + overlay: RwLock::new(HashMap::new()), + }), + subprotocol_name: params.config.subprotocol_name, + priority_tasks: Mutex::new(priority_tasks_tx), + }); + + Ok(sync) + } + + /// Priority tasks producer + pub fn priority_tasks(&self) -> mpsc::Sender { + self.priority_tasks.lock().clone() + } } impl SyncProvider for EthSync { - /// Get sync status - fn status(&self) -> EthSyncStatus { - self.eth_handler.sync.status() - } - - /// Get sync peers - fn peers(&self) -> Vec { - self.network.with_context_eval(self.subprotocol_name, |ctx| { - let peer_ids = self.network.connected_peers(); - let light_proto = self.light_proto.as_ref(); - - let peer_info = self.eth_handler.sync.peer_info(&peer_ids); - peer_ids.into_iter().zip(peer_info).filter_map(|(peer_id, peer_info)| { - let session_info = match ctx.session_info(peer_id) { - None => return None, - Some(info) => info, - }; - - Some(PeerInfo { - id: session_info.id.map(|id| format!("{:x}", id)), - client_version: session_info.client_version, - capabilities: session_info.peer_capabilities.into_iter().map(|c| c.to_string()).collect(), - remote_address: session_info.remote_address, - local_address: session_info.local_address, - eth_info: peer_info, - pip_info: light_proto.as_ref().and_then(|lp| lp.peer_status(peer_id)).map(Into::into), - }) - }).collect() - }).unwrap_or_else(Vec::new) - } - - fn enode(&self) -> Option { - self.network.external_url() - } - - fn transactions_stats(&self) -> BTreeMap { - self.eth_handler.sync.transactions_stats() - } + /// Get sync status + fn status(&self) -> EthSyncStatus { + self.eth_handler.sync.status() + } + + /// Get sync peers + fn peers(&self) -> Vec { + self.network + .with_context_eval(self.subprotocol_name, |ctx| { + let peer_ids = self.network.connected_peers(); + + let peer_info = self.eth_handler.sync.peer_info(&peer_ids); + peer_ids + .into_iter() + .zip(peer_info) + .filter_map(|(peer_id, peer_info)| { + let session_info = match ctx.session_info(peer_id) { + None => return None, + Some(info) => info, + }; + + Some(PeerInfo { + id: session_info.id.map(|id| format!("{:x}", id)), + client_version: session_info.client_version, + capabilities: session_info + .peer_capabilities + .into_iter() + .map(|c| c.to_string()) + .collect(), + remote_address: session_info.remote_address, + local_address: session_info.local_address, + eth_info: peer_info, + }) + }) + .collect() + }) + .unwrap_or_else(Vec::new) + } + + fn enode(&self) -> Option { + self.network.external_url() + } + + fn transactions_stats(&self) -> BTreeMap { + self.eth_handler.sync.transactions_stats() + } +} + +impl PrometheusMetrics for EthSync { + fn prometheus_metrics(&self, r: &mut prometheus::Registry) { + let scalar = |b| if b { 1i64 } else { 0i64 }; + let sync_status = self.status(); + + prometheus_gauge(r, + "sync_status", + "WaitingPeers(0), SnapshotManifest(1), SnapshotData(2), SnapshotWaiting(3), Blocks(4), Idle(5), Waiting(6), NewBlocks(7)", + match self.eth_handler.sync.status().state { + SyncState::WaitingPeers => 0, + SyncState::SnapshotManifest => 1, + SyncState::SnapshotData => 2, + SyncState::SnapshotWaiting => 3, + SyncState::Blocks => 4, + SyncState::Idle => 5, + SyncState::Waiting => 6, + SyncState::NewBlocks => 7, + }); + + for (key, value) in sync_status.item_sizes.iter() { + prometheus_gauge( + r, + &key, + format!("Total item number of {}", key).as_str(), + *value as i64, + ); + } + + prometheus_gauge( + r, + "net_peers", + "Total number of connected peers", + sync_status.num_peers as i64, + ); + prometheus_gauge( + r, + "net_active_peers", + "Total number of active peers", + sync_status.num_active_peers as i64, + ); + prometheus_counter( + r, + "sync_blocks_recieved", + "Number of blocks downloaded so far", + sync_status.blocks_received as i64, + ); + prometheus_counter( + r, + "sync_blocks_total", + "Total number of blocks for the sync process", + sync_status.blocks_total as i64, + ); + prometheus_gauge( + r, + "sync_blocks_highest", + "Highest block number in the download queue", + sync_status.highest_block_number.unwrap_or(0) as i64, + ); + + prometheus_gauge( + r, + "snapshot_download_active", + "1 if downloading snapshots", + scalar(sync_status.is_snapshot_syncing()), + ); + prometheus_gauge( + r, + "snapshot_download_chunks", + "Snapshot chunks", + sync_status.num_snapshot_chunks as i64, + ); + prometheus_gauge( + r, + "snapshot_download_chunks_done", + "Snapshot chunks downloaded", + sync_status.snapshot_chunks_done as i64, + ); + + let restoration = self.eth_handler.snapshot_service.restoration_status(); + let creation = self.eth_handler.snapshot_service.creation_status(); + + prometheus_gauge( + r, + "snapshot_create_block", + "First block of the current snapshot creation", + if let CreationStatus::Ongoing { block_number } = creation { + block_number as i64 + } else { + 0 + }, + ); + prometheus_gauge( + r, + "snapshot_restore_block", + "First block of the current snapshot restoration", + if let RestorationStatus::Ongoing { block_number, .. } = restoration { + block_number as i64 + } else { + 0 + }, + ); + } } const PEERS_TIMER: TimerToken = 0; @@ -427,120 +435,125 @@ const MAINTAIN_SYNC_TIMER: TimerToken = 1; const CONTINUE_SYNC_TIMER: TimerToken = 2; const TX_TIMER: TimerToken = 3; const PRIORITY_TIMER: TimerToken = 4; +const DELAYED_PROCESSING_TIMER: TimerToken = 5; pub(crate) const PRIORITY_TIMER_INTERVAL: Duration = Duration::from_millis(250); struct SyncProtocolHandler { - /// Shared blockchain client. - chain: Arc, - /// Shared snapshot service. - snapshot_service: Arc, - /// Sync strategy - sync: ChainSyncApi, - /// Chain overlay used to cache data such as fork block. - overlay: RwLock>, + /// Shared blockchain client. + chain: Arc, + /// Shared snapshot service. + snapshot_service: Arc, + /// Sync strategy + sync: ChainSyncApi, + /// Chain overlay used to cache data such as fork block. + overlay: RwLock>, } impl NetworkProtocolHandler for SyncProtocolHandler { - fn initialize(&self, io: &NetworkContext) { - if io.subprotocol_name() != WARP_SYNC_PROTOCOL_ID { - io.register_timer(PEERS_TIMER, Duration::from_millis(700)).expect("Error registering peers timer"); - io.register_timer(MAINTAIN_SYNC_TIMER, Duration::from_millis(1100)).expect("Error registering sync timer"); - io.register_timer(CONTINUE_SYNC_TIMER, Duration::from_millis(2500)).expect("Error registering sync timer"); - io.register_timer(TX_TIMER, Duration::from_millis(1300)).expect("Error registering transactions timer"); - - io.register_timer(PRIORITY_TIMER, PRIORITY_TIMER_INTERVAL).expect("Error registering peers timer"); - } - } - - fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) { - self.sync.dispatch_packet(&mut NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay), *peer, packet_id, data); - } - - fn connected(&self, io: &NetworkContext, peer: &PeerId) { - trace_time!("sync::connected"); - // If warp protocol is supported only allow warp handshake - let warp_protocol = io.protocol_version(WARP_SYNC_PROTOCOL_ID, *peer).unwrap_or(0) != 0; - let warp_context = io.subprotocol_name() == WARP_SYNC_PROTOCOL_ID; - if warp_protocol == warp_context { - self.sync.write().on_peer_connected(&mut NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay), *peer); - } - } - - fn disconnected(&self, io: &NetworkContext, peer: &PeerId) { - trace_time!("sync::disconnected"); - if io.subprotocol_name() != WARP_SYNC_PROTOCOL_ID { - self.sync.write().on_peer_aborting(&mut NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay), *peer); - } - } - - fn timeout(&self, io: &NetworkContext, timer: TimerToken) { - trace_time!("sync::timeout"); - let mut io = NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay); - match timer { - PEERS_TIMER => self.sync.write().maintain_peers(&mut io), - MAINTAIN_SYNC_TIMER => self.sync.write().maintain_sync(&mut io), - CONTINUE_SYNC_TIMER => self.sync.write().continue_sync(&mut io), - TX_TIMER => self.sync.write().propagate_new_transactions(&mut io), - PRIORITY_TIMER => self.sync.process_priority_queue(&mut io), - _ => warn!("Unknown timer {} triggered.", timer), - } - } + fn initialize(&self, io: &dyn NetworkContext) { + if io.subprotocol_name() != PAR_PROTOCOL { + io.register_timer(PEERS_TIMER, Duration::from_millis(700)) + .expect("Error registering peers timer"); + io.register_timer(MAINTAIN_SYNC_TIMER, Duration::from_millis(1100)) + .expect("Error registering sync timer"); + io.register_timer(CONTINUE_SYNC_TIMER, Duration::from_millis(2500)) + .expect("Error registering sync timer"); + io.register_timer(TX_TIMER, Duration::from_millis(1300)) + .expect("Error registering transactions timer"); + io.register_timer(DELAYED_PROCESSING_TIMER, Duration::from_millis(2100)) + .expect("Error registering delayed processing timer"); + + io.register_timer(PRIORITY_TIMER, PRIORITY_TIMER_INTERVAL) + .expect("Error registering peers timer"); + } + } + + fn read(&self, io: &dyn NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) { + self.sync.dispatch_packet( + &mut NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay), + *peer, + packet_id, + data, + ); + } + + fn connected(&self, io: &dyn NetworkContext, peer: &PeerId) { + trace_time!("sync::connected"); + // If warp protocol is supported only allow warp handshake + let warp_protocol = io.protocol_version(PAR_PROTOCOL, *peer).unwrap_or(0) != 0; + let warp_context = io.subprotocol_name() == PAR_PROTOCOL; + if warp_protocol == warp_context { + self.sync.write().on_peer_connected( + &mut NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay), + *peer, + ); + } + } + + fn disconnected(&self, io: &dyn NetworkContext, peer: &PeerId) { + trace_time!("sync::disconnected"); + if io.subprotocol_name() != PAR_PROTOCOL { + self.sync.write().on_peer_aborting( + &mut NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay), + *peer, + ); + } + } + + fn timeout(&self, io: &dyn NetworkContext, timer: TimerToken) { + trace_time!("sync::timeout"); + let mut io = NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay); + match timer { + PEERS_TIMER => self.sync.write().maintain_peers(&mut io), + MAINTAIN_SYNC_TIMER => self.sync.write().maintain_sync(&mut io), + CONTINUE_SYNC_TIMER => self.sync.write().continue_sync(&mut io), + TX_TIMER => self.sync.write().propagate_new_transactions(&mut io), + PRIORITY_TIMER => self.sync.process_priority_queue(&mut io), + DELAYED_PROCESSING_TIMER => self.sync.process_delayed_requests(&mut io), + _ => warn!("Unknown timer {} triggered.", timer), + } + } } impl ChainNotify for EthSync { - fn block_pre_import(&self, bytes: &Bytes, hash: &H256, difficulty: &U256) { - let task = PriorityTask::PropagateBlock { - started: ::std::time::Instant::now(), - block: bytes.clone(), - hash: *hash, - difficulty: *difficulty, - }; - if let Err(e) = self.priority_tasks.lock().send(task) { - warn!(target: "sync", "Unexpected error during priority block propagation: {:?}", e); - } - } - - fn new_blocks(&self, new_blocks: NewBlocks) - { - if new_blocks.has_more_blocks_to_import { return } - use light::net::Announcement; - - self.network.with_context(self.subprotocol_name, |context| { - let mut sync_io = NetSyncIo::new(context, &*self.eth_handler.chain, &*self.eth_handler.snapshot_service, - &self.eth_handler.overlay); - self.eth_handler.sync.write().chain_new_blocks( - &mut sync_io, - &new_blocks.imported, - &new_blocks.invalid, - new_blocks.route.enacted(), - new_blocks.route.retracted(), - &new_blocks.sealed, - &new_blocks.proposed); - }); - - self.network.with_context(self.light_subprotocol_name, |context| { - let light_proto = match self.light_proto.as_ref() { - Some(lp) => lp, - None => return, - }; - - let chain_info = self.eth_handler.chain.chain_info(); - light_proto.make_announcement(&context, Announcement { - head_hash: chain_info.best_block_hash, - head_num: chain_info.best_block_number, - head_td: chain_info.total_difficulty, - reorg_depth: 0, // recalculated on a per-peer basis. - serve_headers: false, // these fields consist of _changes_ in capability. - serve_state_since: None, - serve_chain_since: None, - tx_relay: false, - }) - }) - } - - fn start(&self) { - match self.network.start() { + fn block_pre_import(&self, bytes: &Bytes, hash: &H256, difficulty: &U256) { + let task = PriorityTask::PropagateBlock { + started: ::std::time::Instant::now(), + block: bytes.clone(), + hash: *hash, + difficulty: *difficulty, + }; + if let Err(e) = self.priority_tasks.lock().send(task) { + warn!(target: "sync", "Unexpected error during priority block propagation: {:?}", e); + } + } + + fn new_blocks(&self, new_blocks: NewBlocks) { + if new_blocks.has_more_blocks_to_import { + return; + } + self.network.with_context(self.subprotocol_name, |context| { + let mut sync_io = NetSyncIo::new( + context, + &*self.eth_handler.chain, + &*self.eth_handler.snapshot_service, + &self.eth_handler.overlay, + ); + self.eth_handler.sync.write().chain_new_blocks( + &mut sync_io, + &new_blocks.imported, + &new_blocks.invalid, + new_blocks.route.enacted(), + new_blocks.route.retracted(), + &new_blocks.sealed, + &new_blocks.proposed, + ); + }); + } + + fn start(&self) { + match self.network.start() { Err((err, listen_address)) => { match err.into() { ErrorKind::Io(ref e) if e.kind() == io::ErrorKind::AddrInUse => { @@ -552,477 +565,257 @@ impl ChainNotify for EthSync { _ => {}, } - self.network.register_protocol(self.eth_handler.clone(), self.subprotocol_name, &[ETH_PROTOCOL_VERSION_62, ETH_PROTOCOL_VERSION_63]) - .unwrap_or_else(|e| warn!("Error registering ethereum protocol: {:?}", e)); - // register the warp sync subprotocol - self.network.register_protocol(self.eth_handler.clone(), WARP_SYNC_PROTOCOL_ID, &[PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, PAR_PROTOCOL_VERSION_3]) - .unwrap_or_else(|e| warn!("Error registering snapshot sync protocol: {:?}", e)); - - // register the light protocol. - if let Some(light_proto) = self.light_proto.as_ref().map(|x| x.clone()) { - self.network.register_protocol(light_proto, self.light_subprotocol_name, ::light::net::PROTOCOL_VERSIONS) - .unwrap_or_else(|e| warn!("Error registering light client protocol: {:?}", e)); - } - - // register any attached protocols. - for proto in &self.attached_protos { proto.register(&self.network) } - } - - fn stop(&self) { - self.eth_handler.snapshot_service.abort_restore(); - self.network.stop(); - } - - fn broadcast(&self, message_type: ChainMessageType) { - self.network.with_context(WARP_SYNC_PROTOCOL_ID, |context| { - let mut sync_io = NetSyncIo::new(context, &*self.eth_handler.chain, &*self.eth_handler.snapshot_service, &self.eth_handler.overlay); - match message_type { - ChainMessageType::Consensus(message) => self.eth_handler.sync.write().propagate_consensus_packet(&mut sync_io, message), - ChainMessageType::PrivateTransaction(transaction_hash, message) => - self.eth_handler.sync.write().propagate_private_transaction(&mut sync_io, transaction_hash, PrivateTransactionPacket, message), - ChainMessageType::SignedPrivateTransaction(transaction_hash, message) => - self.eth_handler.sync.write().propagate_private_transaction(&mut sync_io, transaction_hash, SignedPrivateTransactionPacket, message), - } - }); - } - - fn transactions_received(&self, txs: &[UnverifiedTransaction], peer_id: PeerId) { - let mut sync = self.eth_handler.sync.write(); - sync.transactions_received(txs, peer_id); - } -} - -/// PIP event handler. -/// Simply queues transactions from light client peers. -struct TxRelay(Arc); - -impl LightHandler for TxRelay { - fn on_transactions(&self, ctx: &EventContext, relay: &[::types::transaction::UnverifiedTransaction]) { - trace!(target: "pip", "Relaying {} transactions from peer {}", relay.len(), ctx.peer()); - self.0.queue_transactions(relay.iter().map(|tx| ::rlp::encode(tx)).collect(), ctx.peer()) - } + self.network + .register_protocol( + self.eth_handler.clone(), + self.subprotocol_name, + &[ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_64], + ) + .unwrap_or_else(|e| warn!("Error registering ethereum protocol: {:?}", e)); + // register the warp sync subprotocol + self.network + .register_protocol( + self.eth_handler.clone(), + PAR_PROTOCOL, + &[PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2], + ) + .unwrap_or_else(|e| warn!("Error registering snapshot sync protocol: {:?}", e)); + } + + fn stop(&self) { + self.eth_handler.snapshot_service.abort_restore(); + self.network.stop(); + } + + fn broadcast(&self, message_type: ChainMessageType) { + self.network.with_context(PAR_PROTOCOL, |context| { + let mut sync_io = NetSyncIo::new( + context, + &*self.eth_handler.chain, + &*self.eth_handler.snapshot_service, + &self.eth_handler.overlay, + ); + match message_type { + ChainMessageType::Consensus(message) => self + .eth_handler + .sync + .write() + .propagate_consensus_packet(&mut sync_io, message), + } + }); + } + + fn transactions_received(&self, txs: &[UnverifiedTransaction], peer_id: PeerId) { + let mut sync = self.eth_handler.sync.write(); + sync.transactions_received(txs, peer_id); + } } /// Trait for managing network -pub trait ManageNetwork : Send + Sync { - /// Set to allow unreserved peers to connect - fn accept_unreserved_peers(&self); - /// Set to deny unreserved peers to connect - fn deny_unreserved_peers(&self); - /// Remove reservation for the peer - fn remove_reserved_peer(&self, peer: String) -> Result<(), String>; - /// Add reserved peer - fn add_reserved_peer(&self, peer: String) -> Result<(), String>; - /// Start network - fn start_network(&self); - /// Stop network - fn stop_network(&self); - /// Returns the minimum and maximum peers. - fn num_peers_range(&self) -> RangeInclusive; - /// Get network context for protocol. - fn with_proto_context(&self, proto: ProtocolId, f: &mut FnMut(&NetworkContext)); +pub trait ManageNetwork: Send + Sync { + /// Set to allow unreserved peers to connect + fn accept_unreserved_peers(&self); + /// Set to deny unreserved peers to connect + fn deny_unreserved_peers(&self); + /// Remove reservation for the peer + fn remove_reserved_peer(&self, peer: String) -> Result<(), String>; + /// Add reserved peer + fn add_reserved_peer(&self, peer: String) -> Result<(), String>; + /// Start network + fn start_network(&self); + /// Stop network + fn stop_network(&self); + /// Returns the minimum and maximum peers. + fn num_peers_range(&self) -> RangeInclusive; + /// Get network context for protocol. + fn with_proto_context(&self, proto: ProtocolId, f: &mut dyn FnMut(&dyn NetworkContext)); } impl ManageNetwork for EthSync { - fn accept_unreserved_peers(&self) { - self.network.set_non_reserved_mode(NonReservedPeerMode::Accept); - } - - fn deny_unreserved_peers(&self) { - self.network.set_non_reserved_mode(NonReservedPeerMode::Deny); - } - - fn remove_reserved_peer(&self, peer: String) -> Result<(), String> { - self.network.remove_reserved_peer(&peer).map_err(|e| format!("{:?}", e)) - } - - fn add_reserved_peer(&self, peer: String) -> Result<(), String> { - self.network.add_reserved_peer(&peer).map_err(|e| format!("{:?}", e)) - } - - fn start_network(&self) { - self.start(); - } - - fn stop_network(&self) { - self.network.with_context(self.subprotocol_name, |context| { - let mut sync_io = NetSyncIo::new(context, &*self.eth_handler.chain, &*self.eth_handler.snapshot_service, &self.eth_handler.overlay); - self.eth_handler.sync.write().abort(&mut sync_io); - }); - - if let Some(light_proto) = self.light_proto.as_ref() { - light_proto.abort(); - } - - self.stop(); - } - - fn num_peers_range(&self) -> RangeInclusive { - self.network.num_peers_range() - } - - fn with_proto_context(&self, proto: ProtocolId, f: &mut FnMut(&NetworkContext)) { - self.network.with_context_eval(proto, f); - } + fn accept_unreserved_peers(&self) { + self.network + .set_non_reserved_mode(NonReservedPeerMode::Accept); + } + + fn deny_unreserved_peers(&self) { + self.network + .set_non_reserved_mode(NonReservedPeerMode::Deny); + } + + fn remove_reserved_peer(&self, peer: String) -> Result<(), String> { + self.network + .remove_reserved_peer(&peer) + .map_err(|e| format!("{:?}", e)) + } + + fn add_reserved_peer(&self, peer: String) -> Result<(), String> { + self.network + .add_reserved_peer(&peer) + .map_err(|e| format!("{:?}", e)) + } + + fn start_network(&self) { + self.start(); + } + + fn stop_network(&self) { + self.network.with_context(self.subprotocol_name, |context| { + let mut sync_io = NetSyncIo::new( + context, + &*self.eth_handler.chain, + &*self.eth_handler.snapshot_service, + &self.eth_handler.overlay, + ); + self.eth_handler.sync.write().abort(&mut sync_io); + }); + + self.stop(); + } + + fn num_peers_range(&self) -> RangeInclusive { + self.network.num_peers_range() + } + + fn with_proto_context(&self, proto: ProtocolId, f: &mut dyn FnMut(&dyn NetworkContext)) { + self.network.with_context_eval(proto, f); + } } #[derive(Debug, Clone, PartialEq, Eq)] /// Network service configuration pub struct NetworkConfiguration { - /// Directory path to store general network configuration. None means nothing will be saved - pub config_path: Option, - /// Directory path to store network-specific configuration. None means nothing will be saved - pub net_config_path: Option, - /// IP address to listen for incoming connections. Listen to all connections by default - pub listen_address: Option, - /// IP address to advertise. Detected automatically if none. - pub public_address: Option, - /// Port for UDP connections, same as TCP by default - pub udp_port: Option, - /// Enable NAT configuration - pub nat_enabled: bool, - /// Enable discovery - pub discovery_enabled: bool, - /// List of initial node addresses - pub boot_nodes: Vec, - /// Use provided node key instead of default - pub use_secret: Option, - /// Max number of connected peers to maintain - pub max_peers: u32, - /// Min number of connected peers to maintain - pub min_peers: u32, - /// Max pending peers. - pub max_pending_peers: u32, - /// Reserved snapshot sync peers. - pub snapshot_peers: u32, - /// List of reserved node addresses. - pub reserved_nodes: Vec, - /// The non-reserved peer mode. - pub allow_non_reserved: bool, - /// IP Filtering - pub ip_filter: IpFilter, - /// Client version string - pub client_version: String, + /// Directory path to store general network configuration. None means nothing will be saved + pub config_path: Option, + /// Directory path to store network-specific configuration. None means nothing will be saved + pub net_config_path: Option, + /// IP address to listen for incoming connections. Listen to all connections by default + pub listen_address: Option, + /// IP address to advertise. Detected automatically if none. + pub public_address: Option, + /// Port for UDP connections, same as TCP by default + pub udp_port: Option, + /// Enable NAT configuration + pub nat_enabled: bool, + /// Enable discovery + pub discovery_enabled: bool, + /// List of initial node addresses + pub boot_nodes: Vec, + /// Use provided node key instead of default + pub use_secret: Option, + /// Max number of connected peers to maintain + pub max_peers: u32, + /// Min number of connected peers to maintain + pub min_peers: u32, + /// Max pending peers. + pub max_pending_peers: u32, + /// Reserved snapshot sync peers. + pub snapshot_peers: u32, + /// List of reserved node addresses. + pub reserved_nodes: Vec, + /// The non-reserved peer mode. + pub allow_non_reserved: bool, + /// IP Filtering + pub ip_filter: IpFilter, + /// Client version string + pub client_version: String, } impl NetworkConfiguration { - /// Create a new default config. - pub fn new() -> Self { - From::from(BasicNetworkConfiguration::new()) - } - - /// Create a new local config. - pub fn new_local() -> Self { - From::from(BasicNetworkConfiguration::new_local()) - } - - /// Attempt to convert this config into a BasicNetworkConfiguration. - pub fn into_basic(self) -> Result { - Ok(BasicNetworkConfiguration { - config_path: self.config_path, - net_config_path: self.net_config_path, - listen_address: match self.listen_address { None => None, Some(addr) => Some(SocketAddr::from_str(&addr)?) }, - public_address: match self.public_address { None => None, Some(addr) => Some(SocketAddr::from_str(&addr)?) }, - udp_port: self.udp_port, - nat_enabled: self.nat_enabled, - discovery_enabled: self.discovery_enabled, - boot_nodes: self.boot_nodes, - use_secret: self.use_secret, - max_peers: self.max_peers, - min_peers: self.min_peers, - max_handshakes: self.max_pending_peers, - reserved_protocols: hash_map![WARP_SYNC_PROTOCOL_ID => self.snapshot_peers], - reserved_nodes: self.reserved_nodes, - ip_filter: self.ip_filter, - non_reserved_mode: if self.allow_non_reserved { NonReservedPeerMode::Accept } else { NonReservedPeerMode::Deny }, - client_version: self.client_version, - }) - } + /// Create a new default config. + pub fn new() -> Self { + From::from(BasicNetworkConfiguration::new()) + } + + /// Create a new local config. + pub fn new_local() -> Self { + From::from(BasicNetworkConfiguration::new_local()) + } + + /// Attempt to convert this config into a BasicNetworkConfiguration. + pub fn into_basic(self) -> Result { + Ok(BasicNetworkConfiguration { + config_path: self.config_path, + net_config_path: self.net_config_path, + listen_address: match self.listen_address { + None => None, + Some(addr) => Some(SocketAddr::from_str(&addr)?), + }, + public_address: match self.public_address { + None => None, + Some(addr) => Some(SocketAddr::from_str(&addr)?), + }, + udp_port: self.udp_port, + nat_enabled: self.nat_enabled, + discovery_enabled: self.discovery_enabled, + boot_nodes: self.boot_nodes, + use_secret: self.use_secret, + max_peers: self.max_peers, + min_peers: self.min_peers, + max_handshakes: self.max_pending_peers, + reserved_protocols: hash_map![PAR_PROTOCOL => self.snapshot_peers], + reserved_nodes: self.reserved_nodes, + ip_filter: self.ip_filter, + non_reserved_mode: if self.allow_non_reserved { + NonReservedPeerMode::Accept + } else { + NonReservedPeerMode::Deny + }, + client_version: self.client_version, + }) + } } impl From for NetworkConfiguration { - fn from(other: BasicNetworkConfiguration) -> Self { - NetworkConfiguration { - config_path: other.config_path, - net_config_path: other.net_config_path, - listen_address: other.listen_address.and_then(|addr| Some(format!("{}", addr))), - public_address: other.public_address.and_then(|addr| Some(format!("{}", addr))), - udp_port: other.udp_port, - nat_enabled: other.nat_enabled, - discovery_enabled: other.discovery_enabled, - boot_nodes: other.boot_nodes, - use_secret: other.use_secret, - max_peers: other.max_peers, - min_peers: other.min_peers, - max_pending_peers: other.max_handshakes, - snapshot_peers: *other.reserved_protocols.get(&WARP_SYNC_PROTOCOL_ID).unwrap_or(&0), - reserved_nodes: other.reserved_nodes, - ip_filter: other.ip_filter, - allow_non_reserved: match other.non_reserved_mode { NonReservedPeerMode::Accept => true, _ => false } , - client_version: other.client_version, - } - } + fn from(other: BasicNetworkConfiguration) -> Self { + NetworkConfiguration { + config_path: other.config_path, + net_config_path: other.net_config_path, + listen_address: other + .listen_address + .and_then(|addr| Some(format!("{}", addr))), + public_address: other + .public_address + .and_then(|addr| Some(format!("{}", addr))), + udp_port: other.udp_port, + nat_enabled: other.nat_enabled, + discovery_enabled: other.discovery_enabled, + boot_nodes: other.boot_nodes, + use_secret: other.use_secret, + max_peers: other.max_peers, + min_peers: other.min_peers, + max_pending_peers: other.max_handshakes, + snapshot_peers: *other.reserved_protocols.get(&PAR_PROTOCOL).unwrap_or(&0), + reserved_nodes: other.reserved_nodes, + ip_filter: other.ip_filter, + allow_non_reserved: match other.non_reserved_mode { + NonReservedPeerMode::Accept => true, + _ => false, + }, + client_version: other.client_version, + } + } } /// Configuration for IPC service. #[derive(Debug, Clone)] pub struct ServiceConfiguration { - /// Sync config. - pub sync: SyncConfig, - /// Network configuration. - pub net: NetworkConfiguration, - /// IPC path. - pub io_path: String, + /// Sync config. + pub sync: SyncConfig, + /// Network configuration. + pub net: NetworkConfiguration, + /// IPC path. + pub io_path: String, } /// Numbers of peers (max, min, active). #[derive(Debug, Clone)] pub struct PeerNumbers { - /// Number of connected peers. - pub connected: usize, - /// Number of active peers. - pub active: usize, - /// Max peers. - pub max: usize, - /// Min peers. - pub min: usize, -} - -/// Light synchronization. -pub trait LightSyncProvider { - /// Get peer numbers. - fn peer_numbers(&self) -> PeerNumbers; - - /// Get peers information - fn peers(&self) -> Vec; - - /// Get network id. - fn network_id(&self) -> u64; - - /// Get the enode if available. - fn enode(&self) -> Option; - - /// Returns propagation count for pending transactions. - fn transactions_stats(&self) -> BTreeMap; -} - -/// Wrapper around `light_sync::SyncInfo` to expose those methods without the concrete type `LightSync` -pub trait LightSyncInfo: Send + Sync { - /// Get the highest block advertised on the network. - fn highest_block(&self) -> Option; - - /// Get the block number at the time of sync start. - fn start_block(&self) -> u64; - - /// Whether major sync is underway. - fn is_major_importing(&self) -> bool; -} - -/// Execute a closure with a protocol context. -pub trait LightNetworkDispatcher { - /// Execute a closure with a protocol context. - fn with_context(&self, f: F) -> Option where F: FnOnce(&::light::net::BasicContext) -> T; -} - -/// Configuration for the light sync. -pub struct LightSyncParams { - /// Network configuration. - pub network_config: BasicNetworkConfiguration, - /// Light client to sync to. - pub client: Arc, - /// Network ID. - pub network_id: u64, - /// Subprotocol name. - pub subprotocol_name: [u8; 3], - /// Other handlers to attach. - pub handlers: Vec>, - /// Other subprotocols to run. - pub attached_protos: Vec, -} - -/// Service for light synchronization. -pub struct LightSync { - proto: Arc, - sync: Arc, - attached_protos: Vec, - network: NetworkService, - subprotocol_name: [u8; 3], - network_id: u64, -} - -impl LightSync { - /// Create a new light sync service. - pub fn new(params: LightSyncParams) -> Result - where L: AsLightClient + Provider + Sync + Send + 'static - { - use light_sync::LightSync as SyncHandler; - - // initialize light protocol handler and attach sync module. - let (sync, light_proto) = { - let light_params = LightParams { - network_id: params.network_id, - config: Default::default(), - capabilities: Capabilities { - serve_headers: false, - serve_chain_since: None, - serve_state_since: None, - tx_relay: false, - }, - sample_store: None, - }; - - let mut light_proto = LightProtocol::new(params.client.clone(), light_params); - let sync_handler = Arc::new(SyncHandler::new(params.client.clone())?); - light_proto.add_handler(sync_handler.clone()); - - for handler in params.handlers { - light_proto.add_handler(handler); - } - - (sync_handler, Arc::new(light_proto)) - }; - - let service = NetworkService::new(params.network_config, None)?; - - Ok(LightSync { - proto: light_proto, - sync: sync, - attached_protos: params.attached_protos, - network: service, - subprotocol_name: params.subprotocol_name, - network_id: params.network_id, - }) - } - -} - -impl ::std::ops::Deref for LightSync { - type Target = ::light_sync::SyncInfo; - - fn deref(&self) -> &Self::Target { &*self.sync } -} - - -impl LightNetworkDispatcher for LightSync { - fn with_context(&self, f: F) -> Option where F: FnOnce(&::light::net::BasicContext) -> T { - self.network.with_context_eval( - self.subprotocol_name, - move |ctx| self.proto.with_context(&ctx, f), - ) - } -} - -impl ManageNetwork for LightSync { - fn accept_unreserved_peers(&self) { - self.network.set_non_reserved_mode(NonReservedPeerMode::Accept); - } - - fn deny_unreserved_peers(&self) { - self.network.set_non_reserved_mode(NonReservedPeerMode::Deny); - } - - fn remove_reserved_peer(&self, peer: String) -> Result<(), String> { - self.network.remove_reserved_peer(&peer).map_err(|e| format!("{:?}", e)) - } - - fn add_reserved_peer(&self, peer: String) -> Result<(), String> { - self.network.add_reserved_peer(&peer).map_err(|e| format!("{:?}", e)) - } - - fn start_network(&self) { - match self.network.start() { - Err((err, listen_address)) => { - match err.into() { - ErrorKind::Io(ref e) if e.kind() == io::ErrorKind::AddrInUse => { - warn!("Network port {:?} is already in use, make sure that another instance of an Ethereum client is not running or change the port using the --port option.", listen_address.expect("Listen address is not set.")) - }, - err => warn!("Error starting network: {}", err), - } - }, - _ => {}, - } - - let light_proto = self.proto.clone(); - - self.network.register_protocol(light_proto, self.subprotocol_name, ::light::net::PROTOCOL_VERSIONS) - .unwrap_or_else(|e| warn!("Error registering light client protocol: {:?}", e)); - - for proto in &self.attached_protos { proto.register(&self.network) } - } - - fn stop_network(&self) { - self.proto.abort(); - self.network.stop(); - } - - fn num_peers_range(&self) -> RangeInclusive { - self.network.num_peers_range() - } - - fn with_proto_context(&self, proto: ProtocolId, f: &mut FnMut(&NetworkContext)) { - self.network.with_context_eval(proto, f); - } -} - -impl LightSyncProvider for LightSync { - fn peer_numbers(&self) -> PeerNumbers { - let (connected, active) = self.proto.peer_count(); - let peers_range = self.num_peers_range(); - debug_assert!(peers_range.end() >= peers_range.start()); - PeerNumbers { - connected: connected, - active: active, - max: *peers_range.end() as usize, - min: *peers_range.start() as usize, - } - } - - fn peers(&self) -> Vec { - self.network.with_context_eval(self.subprotocol_name, |ctx| { - let peer_ids = self.network.connected_peers(); - - peer_ids.into_iter().filter_map(|peer_id| { - let session_info = match ctx.session_info(peer_id) { - None => return None, - Some(info) => info, - }; - - Some(PeerInfo { - id: session_info.id.map(|id| format!("{:x}", id)), - client_version: session_info.client_version, - capabilities: session_info.peer_capabilities.into_iter().map(|c| c.to_string()).collect(), - remote_address: session_info.remote_address, - local_address: session_info.local_address, - eth_info: None, - pip_info: self.proto.peer_status(peer_id).map(Into::into), - }) - }).collect() - }).unwrap_or_else(Vec::new) - } - - fn enode(&self) -> Option { - self.network.external_url() - } - - fn network_id(&self) -> u64 { - self.network_id - } - - fn transactions_stats(&self) -> BTreeMap { - Default::default() // TODO - } -} - -impl LightSyncInfo for LightSync { - fn highest_block(&self) -> Option { - (*self.sync).highest_block() - } - - fn start_block(&self) -> u64 { - (*self.sync).start_block() - } - - fn is_major_importing(&self) -> bool { - (*self.sync).is_major_importing() - } + /// Number of connected peers. + pub connected: usize, + /// Number of active peers. + pub active: usize, + /// Max peers. + pub max: usize, + /// Min peers. + pub min: usize, } diff --git a/ethcore/sync/src/block_sync.rs b/ethcore/sync/src/block_sync.rs index 04ffa5f18dd..1e7f00a338b 100644 --- a/ethcore/sync/src/block_sync.rs +++ b/ethcore/sync/src/block_sync.rs @@ -1,36 +1,38 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . +use blocks::{BlockCollection, SyncBody, SyncHeader}; +use chain::BlockSet; +use ethcore::{ + client::{BlockId, BlockStatus}, + error::{ + BlockError, Error as EthcoreError, ErrorKind as EthcoreErrorKind, ImportErrorKind, + QueueErrorKind, + }, +}; +use ethereum_types::H256; +use network::{client_version::ClientCapabilities, PeerId}; +use rlp::{self, Rlp}; +use std::cmp; /// /// Blockchain downloader /// - -use std::collections::{HashSet, VecDeque}; -use std::cmp; -use heapsize::HeapSizeOf; -use ethereum_types::H256; -use rlp::{self, Rlp}; -use types::BlockNumber; -use ethcore::client::{BlockStatus, BlockId}; -use ethcore::error::{ImportErrorKind, QueueErrorKind, BlockError, Error as EthcoreError, ErrorKind as EthcoreErrorKind}; +use std::collections::{BTreeMap, HashSet, VecDeque}; use sync_io::SyncIo; -use blocks::{BlockCollection, SyncBody, SyncHeader}; -use chain::BlockSet; -use network::PeerId; -use network::client_version::ClientCapabilities; +use types::BlockNumber; const MAX_HEADERS_TO_REQUEST: usize = 128; const MAX_BODIES_TO_REQUEST_LARGE: usize = 128; @@ -63,942 +65,1082 @@ macro_rules! debug_sync { #[derive(Copy, Clone, Eq, PartialEq, Debug)] /// Downloader state pub enum State { - /// No active downloads. - Idle, - /// Downloading subchain heads - ChainHead, - /// Downloading blocks - Blocks, - /// Download is complete - Complete, + /// No active downloads. + Idle, + /// Downloading subchain heads + ChainHead, + /// Downloading blocks + Blocks, + /// Download is complete + Complete, } /// Data that needs to be requested from a peer. pub enum BlockRequest { - Headers { - start: H256, - count: u64, - skip: u64, - }, - Bodies { - hashes: Vec, - }, - Receipts { - hashes: Vec, - }, + Headers { start: H256, count: u64, skip: u64 }, + Bodies { hashes: Vec }, + Receipts { hashes: Vec }, } /// Indicates sync action #[derive(Eq, PartialEq, Debug)] pub enum DownloadAction { - /// Do nothing - None, - /// Reset downloads for all peers - Reset + /// Do nothing + None, + /// Reset downloads for all peers + Reset, } #[derive(Eq, PartialEq, Debug)] pub enum BlockDownloaderImportError { - /// Imported data is rejected as invalid. Peer should be dropped. - Invalid, - /// Imported data is valid but rejected cause the downloader does not need it. - Useless, + /// Imported data is rejected as invalid. Peer should be dropped. + Invalid, + /// Imported data is valid but rejected cause the downloader does not need it. + Useless, +} + +impl From for BlockDownloaderImportError { + fn from(_: rlp04::DecoderError) -> BlockDownloaderImportError { + BlockDownloaderImportError::Invalid + } } impl From for BlockDownloaderImportError { - fn from(_: rlp::DecoderError) -> BlockDownloaderImportError { - BlockDownloaderImportError::Invalid - } + fn from(_: rlp::DecoderError) -> BlockDownloaderImportError { + BlockDownloaderImportError::Invalid + } } /// Block downloader strategy. /// Manages state and block data for a block download process. pub struct BlockDownloader { - /// Which set of blocks to download - block_set: BlockSet, - /// Downloader state - state: State, - /// Highest block number seen - highest_block: Option, - /// Downloaded blocks, holds `H`, `B` and `S` - blocks: BlockCollection, - /// Last imported block number - last_imported_block: BlockNumber, - /// Last imported block hash - last_imported_hash: H256, - /// Number of blocks imported this round - imported_this_round: Option, - /// Block number the last round started with. - last_round_start: BlockNumber, - last_round_start_hash: H256, - /// Block parents imported this round (hash, parent) - round_parents: VecDeque<(H256, H256)>, - /// Do we need to download block recetips. - download_receipts: bool, - /// Sync up to the block with this hash. - target_hash: Option, - /// Probing range for seeking common best block. - retract_step: u64, - /// consecutive useless headers this round - useless_headers_count: usize, + /// Which set of blocks to download + block_set: BlockSet, + /// Downloader state + state: State, + /// Highest block number seen + highest_block: Option, + /// Downloaded blocks, holds `H`, `B` and `S` + blocks: BlockCollection, + /// Last imported block number + last_imported_block: BlockNumber, + /// Last imported block hash + last_imported_hash: H256, + /// Number of blocks imported this round + imported_this_round: Option, + /// Block number the last round started with. + last_round_start: BlockNumber, + last_round_start_hash: H256, + /// Block parents imported this round (hash, parent) + round_parents: VecDeque<(H256, H256)>, + /// Do we need to download block recetips. + download_receipts: bool, + /// Sync up to the block with this hash. + target_hash: Option, + /// Probing range for seeking common best block. + retract_step: u64, + /// consecutive useless headers this round + useless_headers_count: usize, } impl BlockDownloader { - /// Create a new instance of syncing strategy. - /// For BlockSet::NewBlocks this won't reorganize to before the last kept state. - pub fn new(block_set: BlockSet, start_hash: &H256, start_number: BlockNumber) -> Self { - let sync_receipts = match block_set { - BlockSet::NewBlocks => false, - BlockSet::OldBlocks => true - }; - BlockDownloader { - block_set, - state: State::Idle, - highest_block: None, - last_imported_block: start_number, - last_imported_hash: start_hash.clone(), - last_round_start: start_number, - last_round_start_hash: start_hash.clone(), - blocks: BlockCollection::new(sync_receipts), - imported_this_round: None, - round_parents: VecDeque::new(), - download_receipts: sync_receipts, - target_hash: None, - retract_step: 1, - useless_headers_count: 0, - } - } - - /// Reset sync. Clear all local downloaded data. - pub fn reset(&mut self) { - self.blocks.clear(); - self.useless_headers_count = 0; - self.state = State::Idle; - } - - /// Mark a block as known in the chain - pub fn mark_as_known(&mut self, hash: &H256, number: BlockNumber) { - if number >= self.last_imported_block + 1 { - self.last_imported_block = number; - self.last_imported_hash = hash.clone(); - self.imported_this_round = Some(self.imported_this_round.unwrap_or(0) + 1); - self.last_round_start = number; - self.last_round_start_hash = hash.clone(); - } - } - - /// Check if download is complete - pub fn is_complete(&self) -> bool { - self.state == State::Complete - } - - /// Check if particular block hash is being downloaded - pub fn is_downloading(&self, hash: &H256) -> bool { - self.blocks.is_downloading(hash) - } - - /// Set starting sync block - pub fn set_target(&mut self, hash: &H256) { - self.target_hash = Some(hash.clone()); - } - - /// Unmark header as being downloaded. - pub fn clear_header_download(&mut self, hash: &H256) { - self.blocks.clear_header_download(hash) - } - - /// Unmark block body as being downloaded. - pub fn clear_body_download(&mut self, hashes: &[H256]) { - self.blocks.clear_body_download(hashes) - } - - /// Unmark block receipt as being downloaded. - pub fn clear_receipt_download(&mut self, hashes: &[H256]) { - self.blocks.clear_receipt_download(hashes) - } - /// Reset collection for a new sync round with given subchain block hashes. - pub fn reset_to(&mut self, hashes: Vec) { - self.reset(); - self.blocks.reset_to(hashes); - self.state = State::Blocks; - } - - /// Returns used heap memory size. - pub fn heap_size(&self) -> usize { - self.blocks.heap_size() + self.round_parents.heap_size_of_children() - } - - /// Returns best imported block number. - pub fn last_imported_block_number(&self) -> BlockNumber { - self.last_imported_block - } - - /// Add new block headers. - pub fn import_headers(&mut self, io: &mut SyncIo, r: &Rlp, expected_hash: H256) -> Result { - let item_count = r.item_count().unwrap_or(0); - if self.state == State::Idle { - trace_sync!(self, "Ignored unexpected block headers"); - return Ok(DownloadAction::None) - } - if item_count == 0 && (self.state == State::Blocks) { - return Err(BlockDownloaderImportError::Invalid); - } - - // The request is generated in ::request_blocks. - let (max_count, skip) = if self.state == State::ChainHead { - (SUBCHAIN_SIZE as usize, (MAX_HEADERS_TO_REQUEST - 2) as u64) - } else { - (MAX_HEADERS_TO_REQUEST, 0) - }; - - if item_count > max_count { - debug!(target: "sync", "Headers response is larger than expected"); - return Err(BlockDownloaderImportError::Invalid); - } - - let mut headers = Vec::new(); - let mut hashes = Vec::new(); - let mut last_header = None; - for i in 0..item_count { - let info = SyncHeader::from_rlp(r.at(i)?.as_raw().to_vec())?; - let number = BlockNumber::from(info.header.number()); - let hash = info.header.hash(); - - let valid_response = match last_header { - // First header must match expected hash. - None => expected_hash == hash, - Some((last_number, last_hash)) => { - // Subsequent headers must be spaced by skip interval. - let skip_valid = number == last_number + skip + 1; - // Consecutive headers must be linked by parent hash. - let parent_valid = (number != last_number + 1) || *info.header.parent_hash() == last_hash; - skip_valid && parent_valid - } - }; - - // Disable the peer for this syncing round if it gives invalid chain - if !valid_response { - debug!(target: "sync", "Invalid headers response"); - return Err(BlockDownloaderImportError::Invalid); - } - - last_header = Some((number, hash)); - if self.blocks.contains(&hash) { - trace_sync!(self, "Skipping existing block header {} ({:?})", number, hash); - continue; - } - - match io.chain().block_status(BlockId::Hash(hash.clone())) { - BlockStatus::InChain | BlockStatus::Queued => { - match self.state { - State::Blocks => trace_sync!(self, "Header already in chain {} ({})", number, hash), - _ => trace_sync!(self, "Header already in chain {} ({}), state = {:?}", number, hash, self.state), - } - headers.push(info); - hashes.push(hash); - }, - BlockStatus::Bad => { - return Err(BlockDownloaderImportError::Invalid); - }, - BlockStatus::Unknown => { - headers.push(info); - hashes.push(hash); - } - } - } - - if let Some((number, _)) = last_header { - if self.highest_block.as_ref().map_or(true, |n| number > *n) { - self.highest_block = Some(number); - } - } - - match self.state { - State::ChainHead => { - if !headers.is_empty() { - trace_sync!(self, "Received {} subchain heads, proceeding to download", headers.len()); - self.blocks.reset_to(hashes); - self.state = State::Blocks; - return Ok(DownloadAction::Reset); - } else { - trace_sync!(self, "No useful subchain heads received, expected hash {:?}", expected_hash); - let best = io.chain().chain_info().best_block_number; - let oldest_reorg = io.chain().pruning_info().earliest_state; - let last = self.last_imported_block; - match self.block_set { - BlockSet::NewBlocks if best > last && (last == 0 || last < oldest_reorg) => { - trace_sync!(self, "No common block, disabling peer"); - return Err(BlockDownloaderImportError::Invalid) - }, - BlockSet::OldBlocks => { - trace_sync!(self, "Expected some useful headers for downloading OldBlocks. Try a different peer"); - return Err(BlockDownloaderImportError::Useless) - }, - _ => (), - } - } - }, - State::Blocks => { - let count = headers.len(); - // At least one of the headers must advance the subchain. Otherwise they are all useless. - if count == 0 { - self.useless_headers_count += 1; - trace_sync!(self, "No useful headers ({:?} this round), expected hash {:?}", self.useless_headers_count, expected_hash); - // only reset download if we have multiple subchain heads, to avoid unnecessary resets - // when we are at the head of the chain when we may legitimately receive no useful headers - if self.blocks.heads_len() > 1 && self.useless_headers_count >= MAX_USELESS_HEADERS_PER_ROUND { - trace_sync!(self, "Received {:?} useless responses this round. Resetting sync", MAX_USELESS_HEADERS_PER_ROUND); - self.reset(); - } - return Err(BlockDownloaderImportError::Useless); - } - self.blocks.insert_headers(headers); - trace_sync!(self, "Inserted {} headers", count); - }, - _ => trace_sync!(self, "Unexpected headers({})", headers.len()), - } - - Ok(DownloadAction::None) - } - - /// Called by peer once it has new block bodies - pub fn import_bodies(&mut self, r: &Rlp, expected_hashes: &[H256]) -> Result<(), BlockDownloaderImportError> { - let item_count = r.item_count().unwrap_or(0); - if item_count == 0 { - return Err(BlockDownloaderImportError::Useless); - } else if self.state != State::Blocks { - trace_sync!(self, "Ignored unexpected block bodies"); - } else { - let mut bodies = Vec::with_capacity(item_count); - for i in 0..item_count { - let body = SyncBody::from_rlp(r.at(i)?.as_raw())?; - bodies.push(body); - } - - let hashes = self.blocks.insert_bodies(bodies); - if hashes.len() != item_count { - trace_sync!(self, "Deactivating peer for giving invalid block bodies"); - return Err(BlockDownloaderImportError::Invalid); - } - if !all_expected(hashes.as_slice(), expected_hashes, |&a, &b| a == b) { - trace_sync!(self, "Deactivating peer for giving unexpected block bodies"); - return Err(BlockDownloaderImportError::Invalid); - } - } - Ok(()) - } - - /// Called by peer once it has new block bodies - pub fn import_receipts(&mut self, r: &Rlp, expected_hashes: &[H256]) -> Result<(), BlockDownloaderImportError> { - let item_count = r.item_count().unwrap_or(0); - if item_count == 0 { - return Err(BlockDownloaderImportError::Useless); - } - else if self.state != State::Blocks { - trace_sync!(self, "Ignored unexpected block receipts"); - } - else { - let mut receipts = Vec::with_capacity(item_count); - for i in 0..item_count { - let receipt = r.at(i).map_err(|e| { - trace_sync!(self, "Error decoding block receipts RLP: {:?}", e); - BlockDownloaderImportError::Invalid - })?; - receipts.push(receipt.as_raw().to_vec()); - } - let hashes = self.blocks.insert_receipts(receipts); - if hashes.len() != item_count { - trace_sync!(self, "Deactivating peer for giving invalid block receipts"); - return Err(BlockDownloaderImportError::Invalid); - } - if !all_expected(hashes.as_slice(), expected_hashes, |a, b| a.contains(b)) { - trace_sync!(self, "Deactivating peer for giving unexpected block receipts"); - return Err(BlockDownloaderImportError::Invalid); - } - } - Ok(()) - } - - fn start_sync_round(&mut self, io: &mut SyncIo) { - self.state = State::ChainHead; - trace_sync!(self, "Starting round (last imported count = {:?}, last started = {}, block = {:?}", self.imported_this_round, self.last_round_start, self.last_imported_block); - // Check if need to retract to find the common block. The problem is that the peers still return headers by hash even - // from the non-canonical part of the tree. So we also retract if nothing has been imported last round. - let start = self.last_round_start; - let start_hash = self.last_round_start_hash; - match self.imported_this_round { - Some(n) if n == 0 && start > 0 => { - // nothing was imported last round, step back to a previous block - // search parent in last round known parents first - if let Some(&(_, p)) = self.round_parents.iter().find(|&&(h, _)| h == start_hash) { - self.last_imported_block = start - 1; - self.last_imported_hash = p.clone(); - trace_sync!(self, "Searching common header from the last round {} ({})", self.last_imported_block, self.last_imported_hash); - } else { - let best = io.chain().chain_info().best_block_number; - let oldest_reorg = io.chain().pruning_info().earliest_state; - if self.block_set == BlockSet::NewBlocks && best > start && start < oldest_reorg { - debug_sync!(self, "Could not revert to previous ancient block, last: {} ({})", start, start_hash); - self.reset(); - } else { - let n = start - cmp::min(self.retract_step, start); - self.retract_step *= 2; - match io.chain().block_hash(BlockId::Number(n)) { - Some(h) => { - self.last_imported_block = n; - self.last_imported_hash = h; - trace_sync!(self, "Searching common header in the blockchain {} ({})", start, self.last_imported_hash); - } - None => { - debug_sync!(self, "Could not revert to previous block, last: {} ({})", start, self.last_imported_hash); - self.reset(); - } - } - } - } - }, - _ => { - self.retract_step = 1; - }, - } - self.last_round_start = self.last_imported_block; - self.last_round_start_hash = self.last_imported_hash; - self.imported_this_round = None; - } - - /// Find some headers or blocks to download for a peer. - pub fn request_blocks(&mut self, peer_id: PeerId, io: &mut SyncIo, num_active_peers: usize) -> Option { - match self.state { - State::Idle => { - self.start_sync_round(io); - if self.state == State::ChainHead { - return self.request_blocks(peer_id, io, num_active_peers); - } - }, - State::ChainHead => { - if num_active_peers < MAX_PARALLEL_SUBCHAIN_DOWNLOAD { - // Request subchain headers - trace_sync!(self, "Starting sync with better chain"); - // Request MAX_HEADERS_TO_REQUEST - 2 headers apart so that - // MAX_HEADERS_TO_REQUEST would include headers for neighbouring subchains - return Some(BlockRequest::Headers { - start: self.last_imported_hash.clone(), - count: SUBCHAIN_SIZE, - skip: (MAX_HEADERS_TO_REQUEST - 2) as u64, - }); - } - }, - State::Blocks => { - // check to see if we need to download any block bodies first - let client_version = io.peer_version(peer_id); - - let number_of_bodies_to_request = if client_version.can_handle_large_requests() { - MAX_BODIES_TO_REQUEST_LARGE - } else { - MAX_BODIES_TO_REQUEST_SMALL - }; - - let needed_bodies = self.blocks.needed_bodies(number_of_bodies_to_request, false); - if !needed_bodies.is_empty() { - return Some(BlockRequest::Bodies { - hashes: needed_bodies, - }); - } - - if self.download_receipts { - let needed_receipts = self.blocks.needed_receipts(MAX_RECEPITS_TO_REQUEST, false); - if !needed_receipts.is_empty() { - return Some(BlockRequest::Receipts { - hashes: needed_receipts, - }); - } - } - - // find subchain to download - if let Some((h, count)) = self.blocks.needed_headers(MAX_HEADERS_TO_REQUEST, false) { - return Some(BlockRequest::Headers { - start: h, - count: count as u64, - skip: 0, - }); - } - }, - State::Complete => (), - } - None - } - - /// Checks if there are blocks fully downloaded that can be imported into the blockchain and does the import. - /// Returns DownloadAction::Reset if it is imported all the the blocks it can and all downloading peers should be reset - pub fn collect_blocks(&mut self, io: &mut SyncIo, allow_out_of_order: bool) -> DownloadAction { - let mut download_action = DownloadAction::None; - let mut imported = HashSet::new(); - let blocks = self.blocks.drain(); - let count = blocks.len(); - for block_and_receipts in blocks { - let block = block_and_receipts.block; - let receipts = block_and_receipts.receipts; - - let h = block.header.hash(); - let number = block.header.number(); - let parent = *block.header.parent_hash(); - - if self.target_hash.as_ref().map_or(false, |t| t == &h) { - self.state = State::Complete; - trace_sync!(self, "Sync target reached"); - return download_action; - } - - let result = if let Some(receipts) = receipts { - io.chain().queue_ancient_block(block, receipts) - } else { - io.chain().import_block(block) - }; - - match result { - Err(EthcoreError(EthcoreErrorKind::Import(ImportErrorKind::AlreadyInChain), _)) => { - trace_sync!(self, "Block already in chain {:?}", h); - self.block_imported(&h, number, &parent); - }, - Err(EthcoreError(EthcoreErrorKind::Import(ImportErrorKind::AlreadyQueued), _)) => { - trace_sync!(self, "Block already queued {:?}", h); - self.block_imported(&h, number, &parent); - }, - Ok(_) => { - trace_sync!(self, "Block queued {:?}", h); - imported.insert(h.clone()); - self.block_imported(&h, number, &parent); - }, - Err(EthcoreError(EthcoreErrorKind::Block(BlockError::UnknownParent(_)), _)) if allow_out_of_order => { - break; - }, - Err(EthcoreError(EthcoreErrorKind::Block(BlockError::UnknownParent(_)), _)) => { - trace_sync!(self, "Unknown new block parent, restarting sync"); - break; - }, - Err(EthcoreError(EthcoreErrorKind::Block(BlockError::TemporarilyInvalid(_)), _)) => { - debug_sync!(self, "Block temporarily invalid: {:?}, restarting sync", h); - break; - }, - Err(EthcoreError(EthcoreErrorKind::Queue(QueueErrorKind::Full(limit)), _)) => { - debug_sync!(self, "Block import queue full ({}), restarting sync", limit); - download_action = DownloadAction::Reset; - break; - }, - Err(e) => { - debug_sync!(self, "Bad block {:?} : {:?}", h, e); - download_action = DownloadAction::Reset; - break; - } - } - } - trace_sync!(self, "Imported {} of {}", imported.len(), count); - self.imported_this_round = Some(self.imported_this_round.unwrap_or(0) + imported.len()); - - if self.blocks.is_empty() { - // complete sync round - trace_sync!(self, "Sync round complete"); - download_action = DownloadAction::Reset; - } - download_action - } - - fn block_imported(&mut self, hash: &H256, number: BlockNumber, parent: &H256) { - self.last_imported_block = number; - self.last_imported_hash = hash.clone(); - self.round_parents.push_back((hash.clone(), parent.clone())); - if self.round_parents.len() > MAX_ROUND_PARENTS { - self.round_parents.pop_front(); - } - } + /// Create a new instance of syncing strategy. + /// For BlockSet::NewBlocks this won't reorganize to before the last kept state. + pub fn new(block_set: BlockSet, start_hash: &H256, start_number: BlockNumber) -> Self { + let sync_receipts = match block_set { + BlockSet::NewBlocks => false, + BlockSet::OldBlocks => true, + }; + BlockDownloader { + block_set, + state: State::Idle, + highest_block: None, + last_imported_block: start_number, + last_imported_hash: start_hash.clone(), + last_round_start: start_number, + last_round_start_hash: start_hash.clone(), + blocks: BlockCollection::new(sync_receipts), + imported_this_round: None, + round_parents: VecDeque::new(), + download_receipts: sync_receipts, + target_hash: None, + retract_step: 1, + useless_headers_count: 0, + } + } + + /// Reset sync. Clear all local downloaded data. + pub fn reset(&mut self) { + self.blocks.clear(); + self.useless_headers_count = 0; + self.state = State::Idle; + } + + /// Mark a block as known in the chain + pub fn mark_as_known(&mut self, hash: &H256, number: BlockNumber) { + if number >= self.last_imported_block + 1 { + self.last_imported_block = number; + self.last_imported_hash = hash.clone(); + self.imported_this_round = Some(self.imported_this_round.unwrap_or(0) + 1); + self.last_round_start = number; + self.last_round_start_hash = hash.clone(); + } + } + + /// Check if download is complete + pub fn is_complete(&self) -> bool { + self.state == State::Complete + } + + /// Check if particular block hash is being downloaded + pub fn is_downloading(&self, hash: &H256) -> bool { + self.blocks.is_downloading(hash) + } + + /// Set starting sync block + pub fn set_target(&mut self, hash: &H256) { + self.target_hash = Some(hash.clone()); + } + + /// Unmark header as being downloaded. + pub fn clear_header_download(&mut self, hash: &H256) { + self.blocks.clear_header_download(hash) + } + + /// Unmark block body as being downloaded. + pub fn clear_body_download(&mut self, hashes: &[H256]) { + self.blocks.clear_body_download(hashes) + } + + /// Unmark block receipt as being downloaded. + pub fn clear_receipt_download(&mut self, hashes: &[H256]) { + self.blocks.clear_receipt_download(hashes) + } + /// Reset collection for a new sync round with given subchain block hashes. + pub fn reset_to(&mut self, hashes: Vec) { + self.reset(); + self.blocks.reset_to(hashes); + self.state = State::Blocks; + } + + /// Returns number if items in structures + pub fn get_sizes(&self, sizes: &mut BTreeMap) { + let prefix = format!("{}_", self.block_set.to_string()); + self.blocks.get_sizes(sizes, &prefix); + sizes.insert( + format!("{}{}", prefix, "round_parents"), + self.round_parents.len(), + ); + } + + fn reset_to_block(&mut self, start_hash: &H256, start_number: BlockNumber) { + self.reset(); + self.last_imported_block = start_number; + self.last_imported_hash = start_hash.clone(); + self.last_round_start = start_number; + self.last_round_start_hash = start_hash.clone(); + self.imported_this_round = None; + self.round_parents = VecDeque::new(); + self.target_hash = None; + self.retract_step = 1; + } + + /// Returns best imported block number. + pub fn last_imported_block_number(&self) -> BlockNumber { + self.last_imported_block + } + + /// Add new block headers. + pub fn import_headers( + &mut self, + io: &mut dyn SyncIo, + r: &Rlp, + expected_hash: H256, + ) -> Result { + let item_count = r.item_count().unwrap_or(0); + if self.state == State::Idle { + trace_sync!(self, "Ignored unexpected block headers"); + return Ok(DownloadAction::None); + } + if item_count == 0 && (self.state == State::Blocks) { + return Err(BlockDownloaderImportError::Invalid); + } + + // The request is generated in ::request_blocks. + let (max_count, skip) = if self.state == State::ChainHead { + (SUBCHAIN_SIZE as usize, (MAX_HEADERS_TO_REQUEST - 2) as u64) + } else { + (MAX_HEADERS_TO_REQUEST, 0) + }; + + if item_count > max_count { + debug!(target: "sync", "Headers response is larger than expected"); + return Err(BlockDownloaderImportError::Invalid); + } + + let mut headers = Vec::new(); + let mut hashes = Vec::new(); + let mut last_header = None; + for i in 0..item_count { + let info = SyncHeader::from_rlp(r.at(i)?.as_raw().to_vec())?; + let number = BlockNumber::from(info.header.number()); + let hash = info.header.hash(); + + // This part checks if first header is what we expect and that all other header are chained correctly. + let valid_response = match last_header { + // First header must match expected hash. + None => expected_hash == hash, + Some((last_number, last_hash)) => { + // Subsequent headers must be spaced by skip interval. + let skip_valid = number == last_number + skip + 1; + // Consecutive headers must be linked by parent hash. + let parent_valid = + (number != last_number + 1) || *info.header.parent_hash() == last_hash; + skip_valid && parent_valid + } + }; + + // Disable the peer for this syncing round if it gives invalid chain + if !valid_response { + debug!(target: "sync", "Invalid headers response"); + return Err(BlockDownloaderImportError::Invalid); + } + + // If header is already included skip and go to next one in chain. + last_header = Some((number, hash)); + if self.blocks.contains(&hash) { + trace_sync!( + self, + "Skipping existing block header {} ({:?})", + number, + hash + ); + continue; + } + + // Check if received header is present in chain. If it is in chain include header into list that will be inserted + match io.chain().block_status(BlockId::Hash(hash.clone())) { + BlockStatus::InChain | BlockStatus::Queued => { + match self.state { + State::Blocks => { + trace_sync!(self, "Header already in chain {} ({})", number, hash) + } + _ => trace_sync!( + self, + "Header already in chain {} ({}), state = {:?}", + number, + hash, + self.state + ), + } + headers.push(info); + hashes.push(hash); + } + BlockStatus::Bad => { + return Err(BlockDownloaderImportError::Invalid); + } + BlockStatus::Unknown => { + headers.push(info); + hashes.push(hash); + } + } + } + + // Set highest block that we receive from network. This is only used as stat and nothing more. + if let Some((number, _)) = last_header { + if self.highest_block.as_ref().map_or(true, |n| number > *n) { + self.highest_block = Some(number); + } + } + + match self.state { + State::ChainHead => { + if !headers.is_empty() { + trace_sync!( + self, + "Received {} subchain heads, proceeding to download", + headers.len() + ); + self.blocks.reset_to(hashes); + self.state = State::Blocks; + return Ok(DownloadAction::Reset); + } else { + trace_sync!( + self, + "No useful subchain heads received, expected hash {:?}", + expected_hash + ); + let best = io.chain().chain_info().best_block_number; + let oldest_reorg = io.chain().pruning_info().earliest_state; + let last = self.last_imported_block; + match self.block_set { + BlockSet::NewBlocks + if best > last && (last == 0 || last < oldest_reorg) => + { + trace_sync!(self, "No common block, disabling peer"); + return Err(BlockDownloaderImportError::Invalid); + } + BlockSet::OldBlocks => { + trace_sync!(self, "Expected some useful headers for downloading OldBlocks. Try a different peer"); + return Err(BlockDownloaderImportError::Useless); + } + _ => (), + } + } + } + State::Blocks => { + let count = headers.len(); + // At least one of the headers must advance the subchain. Otherwise they are all useless. + if count == 0 { + self.useless_headers_count += 1; + trace_sync!( + self, + "No useful headers ({:?} this round), expected hash {:?}", + self.useless_headers_count, + expected_hash + ); + // only reset download if we have multiple subchain heads, to avoid unnecessary resets + // when we are at the head of the chain when we may legitimately receive no useful headers + if self.blocks.heads_len() > 1 + && self.useless_headers_count >= MAX_USELESS_HEADERS_PER_ROUND + { + trace_sync!( + self, + "Received {:?} useless responses this round. Resetting sync", + MAX_USELESS_HEADERS_PER_ROUND + ); + self.reset(); + } + return Err(BlockDownloaderImportError::Useless); + } + self.blocks.insert_headers(headers); + trace_sync!(self, "Inserted {} headers", count); + } + _ => trace_sync!(self, "Unexpected headers({})", headers.len()), + } + + Ok(DownloadAction::None) + } + + /// Called by peer once it has new block bodies + pub fn import_bodies( + &mut self, + r: &Rlp, + expected_hashes: &[H256], + ) -> Result<(), BlockDownloaderImportError> { + let item_count = r.item_count().unwrap_or(0); + if item_count == 0 { + return Err(BlockDownloaderImportError::Useless); + } else if self.state != State::Blocks { + trace_sync!(self, "Ignored unexpected block bodies"); + } else { + let mut bodies = Vec::with_capacity(item_count); + for i in 0..item_count { + let body = SyncBody::from_rlp(r.at(i)?.as_raw())?; + bodies.push(body); + } + + let hashes = self.blocks.insert_bodies(bodies); + if hashes.len() != item_count { + trace_sync!(self, "Deactivating peer for giving invalid block bodies"); + return Err(BlockDownloaderImportError::Invalid); + } + if !all_expected(hashes.as_slice(), expected_hashes, |&a, &b| a == b) { + trace_sync!(self, "Deactivating peer for giving unexpected block bodies"); + return Err(BlockDownloaderImportError::Invalid); + } + } + Ok(()) + } + + /// Called by peer once it has new block bodies + pub fn import_receipts( + &mut self, + r: &Rlp, + expected_hashes: &[H256], + ) -> Result<(), BlockDownloaderImportError> { + let item_count = r.item_count().unwrap_or(0); + if item_count == 0 { + return Err(BlockDownloaderImportError::Useless); + } else if self.state != State::Blocks { + trace_sync!(self, "Ignored unexpected block receipts"); + } else { + let mut receipts = Vec::with_capacity(item_count); + for i in 0..item_count { + let receipt = r.at(i).map_err(|e| { + trace_sync!(self, "Error decoding block receipts RLP: {:?}", e); + BlockDownloaderImportError::Invalid + })?; + receipts.push(receipt.as_raw().to_vec()); + } + let hashes = self.blocks.insert_receipts(receipts); + if hashes.len() != item_count { + trace_sync!(self, "Deactivating peer for giving invalid block receipts"); + return Err(BlockDownloaderImportError::Invalid); + } + if !all_expected(hashes.as_slice(), expected_hashes, |a, b| a.contains(b)) { + trace_sync!( + self, + "Deactivating peer for giving unexpected block receipts" + ); + return Err(BlockDownloaderImportError::Invalid); + } + } + Ok(()) + } + + fn start_sync_round(&mut self, io: &mut dyn SyncIo) { + self.state = State::ChainHead; + trace_sync!( + self, + "Starting round (last imported count = {:?}, last started = {}, block = {:?}", + self.imported_this_round, + self.last_round_start, + self.last_imported_block + ); + // Check if need to retract to find the common block. The problem is that the peers still return headers by hash even + // from the non-canonical part of the tree. So we also retract if nothing has been imported last round. + let start = self.last_round_start; + let start_hash = self.last_round_start_hash; + match self.imported_this_round { + Some(n) if n == 0 && start > 0 => { + // nothing was imported last round, step back to a previous block + // search parent in last round known parents first + if let Some(&(_, p)) = self.round_parents.iter().find(|&&(h, _)| h == start_hash) { + self.last_imported_block = start - 1; + self.last_imported_hash = p.clone(); + trace_sync!( + self, + "Searching common header from the last round {} ({})", + self.last_imported_block, + self.last_imported_hash + ); + } else { + let best = io.chain().chain_info().best_block_number; + let best_hash = io.chain().chain_info().best_block_hash; + let oldest_reorg = io.chain().pruning_info().earliest_state; + if self.block_set == BlockSet::NewBlocks && best > start && start < oldest_reorg + { + debug_sync!( + self, + "Could not revert to previous ancient block, last: {} ({})", + start, + start_hash + ); + self.reset_to_block(&best_hash, best); + } else { + let n = start - cmp::min(self.retract_step, start); + if n == 0 { + debug_sync!(self, "Header not found, bottom line reached, resetting, last imported: {}", self.last_imported_hash); + self.reset_to_block(&best_hash, best); + } else { + self.retract_step *= 2; + match io.chain().block_hash(BlockId::Number(n)) { + Some(h) => { + self.last_imported_block = n; + self.last_imported_hash = h; + trace_sync!( + self, + "Searching common header in the blockchain {} ({})", + start, + self.last_imported_hash + ); + } + None => { + debug_sync!( + self, + "Could not revert to previous block, last: {} ({})", + start, + self.last_imported_hash + ); + self.reset_to_block(&best_hash, best); + } + } + } + } + } + } + _ => { + self.retract_step = 1; + } + } + self.last_round_start = self.last_imported_block; + self.last_round_start_hash = self.last_imported_hash; + self.imported_this_round = None; + } + + /// Find some headers or blocks to download for a peer. + pub fn request_blocks( + &mut self, + peer_id: PeerId, + io: &mut dyn SyncIo, + num_active_peers: usize, + ) -> Option { + match self.state { + State::Idle => { + self.start_sync_round(io); + if self.state == State::ChainHead { + return self.request_blocks(peer_id, io, num_active_peers); + } + } + State::ChainHead => { + if num_active_peers < MAX_PARALLEL_SUBCHAIN_DOWNLOAD { + // Request subchain headers + trace_sync!(self, "Starting sync with better chain"); + // Request MAX_HEADERS_TO_REQUEST - 2 headers apart so that + // MAX_HEADERS_TO_REQUEST would include headers for neighbouring subchains + return Some(BlockRequest::Headers { + start: self.last_imported_hash.clone(), + count: SUBCHAIN_SIZE, + skip: (MAX_HEADERS_TO_REQUEST - 2) as u64, + }); + } + } + State::Blocks => { + // check to see if we need to download any block bodies first + let client_version = io.peer_version(peer_id); + + let number_of_bodies_to_request = if client_version.can_handle_large_requests() { + MAX_BODIES_TO_REQUEST_LARGE + } else { + MAX_BODIES_TO_REQUEST_SMALL + }; + + let needed_bodies = self + .blocks + .needed_bodies(number_of_bodies_to_request, false); + if !needed_bodies.is_empty() { + return Some(BlockRequest::Bodies { + hashes: needed_bodies, + }); + } + + if self.download_receipts { + let needed_receipts = + self.blocks.needed_receipts(MAX_RECEPITS_TO_REQUEST, false); + if !needed_receipts.is_empty() { + return Some(BlockRequest::Receipts { + hashes: needed_receipts, + }); + } + } + + // find subchain to download + if let Some((h, count)) = self.blocks.needed_headers(MAX_HEADERS_TO_REQUEST, false) + { + return Some(BlockRequest::Headers { + start: h, + count: count as u64, + skip: 0, + }); + } + } + State::Complete => (), + } + None + } + + /// Checks if there are blocks fully downloaded that can be imported into the blockchain and does the import. + /// Returns DownloadAction::Reset if it is imported all the the blocks it can and all downloading peers should be reset + pub fn collect_blocks( + &mut self, + io: &mut dyn SyncIo, + allow_out_of_order: bool, + ) -> DownloadAction { + let mut download_action = DownloadAction::None; + let mut imported = HashSet::new(); + let blocks = self.blocks.drain(); + let count = blocks.len(); + for block_and_receipts in blocks { + let block = block_and_receipts.block; + let receipts = block_and_receipts.receipts; + + let h = block.header.hash(); + let number = block.header.number(); + let parent = *block.header.parent_hash(); + + if self.target_hash.as_ref().map_or(false, |t| t == &h) { + self.state = State::Complete; + trace_sync!(self, "Sync target reached"); + return download_action; + } + + let result = if let Some(receipts) = receipts { + io.chain().queue_ancient_block(block, receipts) + } else { + io.chain().import_block(block) + }; + + match result { + Err(EthcoreError(EthcoreErrorKind::Import(ImportErrorKind::AlreadyInChain), _)) => { + trace_sync!(self, "Block already in chain {:?}", h); + self.block_imported(&h, number, &parent); + } + Err(EthcoreError(EthcoreErrorKind::Import(ImportErrorKind::AlreadyQueued), _)) => { + trace_sync!(self, "Block already queued {:?}", h); + self.block_imported(&h, number, &parent); + } + Ok(_) => { + trace_sync!(self, "Block queued {:?}", h); + imported.insert(h.clone()); + self.block_imported(&h, number, &parent); + } + Err(EthcoreError(EthcoreErrorKind::Block(BlockError::UnknownParent(_)), _)) + if allow_out_of_order => + { + break; + } + Err(EthcoreError(EthcoreErrorKind::Block(BlockError::UnknownParent(_)), _)) => { + trace_sync!(self, "Unknown new block parent, restarting sync"); + break; + } + Err(EthcoreError( + EthcoreErrorKind::Block(BlockError::TemporarilyInvalid(_)), + _, + )) => { + debug_sync!(self, "Block temporarily invalid: {:?}, restarting sync", h); + break; + } + Err(EthcoreError(EthcoreErrorKind::Queue(QueueErrorKind::Full(limit)), _)) => { + debug_sync!(self, "Block import queue full ({}), restarting sync", limit); + download_action = DownloadAction::Reset; + break; + } + Err(e) => { + debug_sync!(self, "Bad block {:?} : {:?}", h, e); + download_action = DownloadAction::Reset; + break; + } + } + } + trace_sync!(self, "Imported {} of {}", imported.len(), count); + self.imported_this_round = Some(self.imported_this_round.unwrap_or(0) + imported.len()); + + if self.blocks.is_empty() { + // complete sync round + trace_sync!(self, "Sync round complete"); + download_action = DownloadAction::Reset; + } + download_action + } + + fn block_imported(&mut self, hash: &H256, number: BlockNumber, parent: &H256) { + self.last_imported_block = number; + self.last_imported_hash = hash.clone(); + self.round_parents.push_back((hash.clone(), parent.clone())); + if self.round_parents.len() > MAX_ROUND_PARENTS { + self.round_parents.pop_front(); + } + } } // Determines if the first argument matches an ordered subset of the second, according to some predicate. fn all_expected(values: &[A], expected_values: &[B], is_expected: F) -> bool - where F: Fn(&A, &B) -> bool +where + F: Fn(&A, &B) -> bool, { - let mut expected_iter = expected_values.iter(); - values.iter().all(|val1| { - while let Some(val2) = expected_iter.next() { - if is_expected(val1, val2) { - return true; - } - } - false - }) + let mut expected_iter = expected_values.iter(); + values.iter().all(|val1| { + while let Some(val2) = expected_iter.next() { + if is_expected(val1, val2) { + return true; + } + } + false + }) } #[cfg(test)] mod tests { - use super::*; - use ethcore::client::TestBlockChainClient; - use ethcore::spec::Spec; - use ethkey::{Generator,Random}; - use hash::keccak; - use parking_lot::RwLock; - use rlp::{encode_list,RlpStream}; - use tests::helpers::TestIo; - use tests::snapshot::TestSnapshotService; - use types::transaction::{Transaction,SignedTransaction}; - use triehash_ethereum::ordered_trie_root; - use types::header::Header as BlockHeader; - - fn dummy_header(number: u64, parent_hash: H256) -> BlockHeader { - let mut header = BlockHeader::new(); - header.set_gas_limit(0.into()); - header.set_difficulty((number * 100).into()); - header.set_timestamp(number * 10); - header.set_number(number); - header.set_parent_hash(parent_hash); - header.set_state_root(H256::zero()); - header - } - - fn dummy_signed_tx() -> SignedTransaction { - let keypair = Random.generate().unwrap(); - Transaction::default().sign(keypair.secret(), None) - } - - fn import_headers(headers: &[BlockHeader], downloader: &mut BlockDownloader, io: &mut SyncIo) -> Result { - let mut stream = RlpStream::new(); - stream.append_list(headers); - let bytes = stream.out(); - let rlp = Rlp::new(&bytes); - let expected_hash = headers.first().unwrap().hash(); - downloader.import_headers(io, &rlp, expected_hash) - } - - fn import_headers_ok(headers: &[BlockHeader], downloader: &mut BlockDownloader, io: &mut SyncIo) { - let res = import_headers(headers, downloader, io); - assert!(res.is_ok()); - } - - #[test] - fn import_headers_in_chain_head_state() { - ::env_logger::try_init().ok(); - - let spec = Spec::new_test(); - let genesis_hash = spec.genesis_header().hash(); - - let mut downloader = BlockDownloader::new(BlockSet::NewBlocks, &genesis_hash, 0); - downloader.state = State::ChainHead; - - let mut chain = TestBlockChainClient::new(); - let snapshot_service = TestSnapshotService::new(); - let queue = RwLock::new(VecDeque::new()); - let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); - - // Valid headers sequence. - let valid_headers = [ - spec.genesis_header(), - dummy_header(127, H256::random()), - dummy_header(254, H256::random()), - ]; - let rlp_data = encode_list(&valid_headers); - let valid_rlp = Rlp::new(&rlp_data); - - match downloader.import_headers(&mut io, &valid_rlp, genesis_hash) { - Ok(DownloadAction::Reset) => assert_eq!(downloader.state, State::Blocks), - _ => panic!("expected transition to Blocks state"), - }; - - // Headers are rejected because the expected hash does not match. - let invalid_start_block_headers = [ - dummy_header(0, H256::random()), - dummy_header(127, H256::random()), - dummy_header(254, H256::random()), - ]; - let rlp_data = encode_list(&invalid_start_block_headers); - let invalid_start_block_rlp = Rlp::new(&rlp_data); - - match downloader.import_headers(&mut io, &invalid_start_block_rlp, genesis_hash) { - Err(BlockDownloaderImportError::Invalid) => (), - _ => panic!("expected BlockDownloaderImportError"), - }; - - // Headers are rejected because they are not spaced as expected. - let invalid_skip_headers = [ - spec.genesis_header(), - dummy_header(128, H256::random()), - dummy_header(256, H256::random()), - ]; - let rlp_data = encode_list(&invalid_skip_headers); - let invalid_skip_rlp = Rlp::new(&rlp_data); - - match downloader.import_headers(&mut io, &invalid_skip_rlp, genesis_hash) { - Err(BlockDownloaderImportError::Invalid) => (), - _ => panic!("expected BlockDownloaderImportError"), - }; - - // Invalid because the packet size is too large. - let mut too_many_headers = Vec::with_capacity((SUBCHAIN_SIZE + 1) as usize); - too_many_headers.push(spec.genesis_header()); - for i in 1..(SUBCHAIN_SIZE + 1) { - too_many_headers.push(dummy_header((MAX_HEADERS_TO_REQUEST as u64 - 1) * i, H256::random())); - } - let rlp_data = encode_list(&too_many_headers); - - let too_many_rlp = Rlp::new(&rlp_data); - match downloader.import_headers(&mut io, &too_many_rlp, genesis_hash) { - Err(BlockDownloaderImportError::Invalid) => (), - _ => panic!("expected BlockDownloaderImportError"), - }; - } - - #[test] - fn import_headers_in_blocks_state() { - ::env_logger::try_init().ok(); - - let mut chain = TestBlockChainClient::new(); - let snapshot_service = TestSnapshotService::new(); - let queue = RwLock::new(VecDeque::new()); - let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); - - let mut headers = Vec::with_capacity(3); - let parent_hash = H256::random(); - headers.push(dummy_header(127, parent_hash)); - let parent_hash = headers[0].hash(); - headers.push(dummy_header(128, parent_hash)); - let parent_hash = headers[1].hash(); - headers.push(dummy_header(129, parent_hash)); - - let mut downloader = BlockDownloader::new(BlockSet::NewBlocks, &H256::random(), 0); - downloader.state = State::Blocks; - downloader.blocks.reset_to(vec![headers[0].hash()]); - - let rlp_data = encode_list(&headers); - let headers_rlp = Rlp::new(&rlp_data); - - match downloader.import_headers(&mut io, &headers_rlp, headers[0].hash()) { - Ok(DownloadAction::None) => (), - _ => panic!("expected successful import"), - }; - - // Invalidate parent_hash link. - headers[2] = dummy_header(129, H256::random()); - let rlp_data = encode_list(&headers); - let headers_rlp = Rlp::new(&rlp_data); - - match downloader.import_headers(&mut io, &headers_rlp, headers[0].hash()) { - Err(BlockDownloaderImportError::Invalid) => (), - _ => panic!("expected BlockDownloaderImportError"), - }; - - // Invalidate header sequence by skipping a header. - headers[2] = dummy_header(130, headers[1].hash()); - let rlp_data = encode_list(&headers); - let headers_rlp = Rlp::new(&rlp_data); - - match downloader.import_headers(&mut io, &headers_rlp, headers[0].hash()) { - Err(BlockDownloaderImportError::Invalid) => (), - _ => panic!("expected BlockDownloaderImportError"), - }; - } - - #[test] - fn import_bodies() { - ::env_logger::try_init().ok(); - - let mut chain = TestBlockChainClient::new(); - let snapshot_service = TestSnapshotService::new(); - let queue = RwLock::new(VecDeque::new()); - let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); - - // Import block headers. - let mut headers = Vec::with_capacity(4); - let mut bodies = Vec::with_capacity(4); - let mut parent_hash = H256::zero(); - for i in 0..4 { - // Construct the block body - let mut uncles = if i > 0 { - encode_list(&[dummy_header(i - 1, H256::random())]) - } else { - ::rlp::EMPTY_LIST_RLP.to_vec() - }; - - let mut txs = encode_list(&[dummy_signed_tx()]); - let tx_root = ordered_trie_root(Rlp::new(&txs).iter().map(|r| r.as_raw())); - - let mut rlp = RlpStream::new_list(2); - rlp.append_raw(&txs, 1); - rlp.append_raw(&uncles, 1); - bodies.push(rlp.out()); - - // Construct the block header - let mut header = dummy_header(i, parent_hash); - header.set_transactions_root(tx_root); - header.set_uncles_hash(keccak(&uncles)); - parent_hash = header.hash(); - headers.push(header); - } - - let mut downloader = BlockDownloader::new(BlockSet::NewBlocks, &headers[0].hash(), 0); - downloader.state = State::Blocks; - downloader.blocks.reset_to(vec![headers[0].hash()]); - - // Only import the first three block headers. - let rlp_data = encode_list(&headers[0..3]); - let headers_rlp = Rlp::new(&rlp_data); - assert!(downloader.import_headers(&mut io, &headers_rlp, headers[0].hash()).is_ok()); - - // Import first body successfully. - let mut rlp_data = RlpStream::new_list(1); - rlp_data.append_raw(&bodies[0], 1); - let bodies_rlp = Rlp::new(rlp_data.as_raw()); - assert!(downloader.import_bodies(&bodies_rlp, &[headers[0].hash(), headers[1].hash()]).is_ok()); - - // Import second body successfully. - let mut rlp_data = RlpStream::new_list(1); - rlp_data.append_raw(&bodies[1], 1); - let bodies_rlp = Rlp::new(rlp_data.as_raw()); - assert!(downloader.import_bodies(&bodies_rlp, &[headers[0].hash(), headers[1].hash()]).is_ok()); - - // Import unexpected third body. - let mut rlp_data = RlpStream::new_list(1); - rlp_data.append_raw(&bodies[2], 1); - let bodies_rlp = Rlp::new(rlp_data.as_raw()); - match downloader.import_bodies(&bodies_rlp, &[headers[0].hash(), headers[1].hash()]) { - Err(BlockDownloaderImportError::Invalid) => (), - _ => panic!("expected BlockDownloaderImportError"), - }; - } - - #[test] - fn import_receipts() { - ::env_logger::try_init().ok(); - - let mut chain = TestBlockChainClient::new(); - let snapshot_service = TestSnapshotService::new(); - let queue = RwLock::new(VecDeque::new()); - let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); - - // Import block headers. - let mut headers = Vec::with_capacity(4); - let mut receipts = Vec::with_capacity(4); - let mut parent_hash = H256::zero(); - for i in 0..4 { - // Construct the receipts. Receipt root for the first two blocks is the same. - // - // The RLP-encoded integers are clearly not receipts, but the BlockDownloader treats - // all receipts as byte blobs, so it does not matter. - let mut receipts_rlp = if i < 2 { - encode_list(&[0u32]) - } else { - encode_list(&[i as u32]) - }; - let receipts_root = ordered_trie_root(Rlp::new(&receipts_rlp).iter().map(|r| r.as_raw())); - receipts.push(receipts_rlp); - - // Construct the block header. - let mut header = dummy_header(i, parent_hash); - header.set_receipts_root(receipts_root); - parent_hash = header.hash(); - headers.push(header); - } - - let mut downloader = BlockDownloader::new(BlockSet::OldBlocks, &headers[0].hash(), 0); - downloader.state = State::Blocks; - downloader.blocks.reset_to(vec![headers[0].hash()]); - - // Only import the first three block headers. - let rlp_data = encode_list(&headers[0..3]); - let headers_rlp = Rlp::new(&rlp_data); - assert!(downloader.import_headers(&mut io, &headers_rlp, headers[0].hash()).is_ok()); - - // Import second and third receipts successfully. - let mut rlp_data = RlpStream::new_list(2); - rlp_data.append_raw(&receipts[1], 1); - rlp_data.append_raw(&receipts[2], 1); - let receipts_rlp = Rlp::new(rlp_data.as_raw()); - assert!(downloader.import_receipts(&receipts_rlp, &[headers[1].hash(), headers[2].hash()]).is_ok()); - - // Import unexpected fourth receipt. - let mut rlp_data = RlpStream::new_list(1); - rlp_data.append_raw(&receipts[3], 1); - let bodies_rlp = Rlp::new(rlp_data.as_raw()); - match downloader.import_bodies(&bodies_rlp, &[headers[1].hash(), headers[2].hash()]) { - Err(BlockDownloaderImportError::Invalid) => (), - _ => panic!("expected BlockDownloaderImportError"), - }; - } - - #[test] - fn reset_after_multiple_sets_of_useless_headers() { - ::env_logger::try_init().ok(); - - let spec = Spec::new_test(); - let genesis_hash = spec.genesis_header().hash(); - - let mut downloader = BlockDownloader::new(BlockSet::NewBlocks, &genesis_hash, 0); - downloader.state = State::ChainHead; - - let mut chain = TestBlockChainClient::new(); - let snapshot_service = TestSnapshotService::new(); - let queue = RwLock::new(VecDeque::new()); - let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); - - let heads = [ - spec.genesis_header(), - dummy_header(127, H256::random()), - dummy_header(254, H256::random()), - ]; - - let short_subchain = [dummy_header(1, genesis_hash)]; - - import_headers_ok(&heads, &mut downloader, &mut io); - import_headers_ok(&short_subchain, &mut downloader, &mut io); - - assert_eq!(downloader.state, State::Blocks); - assert!(!downloader.blocks.is_empty()); - - // simulate receiving useless headers - let head = vec![short_subchain.last().unwrap().clone()]; - for _ in 0..MAX_USELESS_HEADERS_PER_ROUND { - let res = import_headers(&head, &mut downloader, &mut io); - assert!(res.is_err()); - } - - assert_eq!(downloader.state, State::Idle); - assert!(downloader.blocks.is_empty()); - } - - #[test] - fn dont_reset_after_multiple_sets_of_useless_headers_for_chain_head() { - ::env_logger::try_init().ok(); - - let spec = Spec::new_test(); - let genesis_hash = spec.genesis_header().hash(); - - let mut downloader = BlockDownloader::new(BlockSet::NewBlocks, &genesis_hash, 0); - downloader.state = State::ChainHead; - - let mut chain = TestBlockChainClient::new(); - let snapshot_service = TestSnapshotService::new(); - let queue = RwLock::new(VecDeque::new()); - let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); - - let heads = [ - spec.genesis_header() - ]; - - let short_subchain = [dummy_header(1, genesis_hash)]; - - import_headers_ok(&heads, &mut downloader, &mut io); - import_headers_ok(&short_subchain, &mut downloader, &mut io); - - assert_eq!(downloader.state, State::Blocks); - assert!(!downloader.blocks.is_empty()); - - // simulate receiving useless headers - let head = vec![short_subchain.last().unwrap().clone()]; - for _ in 0..MAX_USELESS_HEADERS_PER_ROUND { - let res = import_headers(&head, &mut downloader, &mut io); - assert!(res.is_err()); - } - - // download shouldn't be reset since this is the chain head for a single subchain. - // this state usually occurs for NewBlocks when it has reached the chain head. - assert_eq!(downloader.state, State::Blocks); - assert!(!downloader.blocks.is_empty()); - } + use super::*; + use ethcore::{client::TestBlockChainClient, spec::Spec}; + use ethkey::{Generator, Random}; + use hash::keccak; + use parking_lot::RwLock; + use rlp::{encode_list, RlpStream}; + use tests::{helpers::TestIo, snapshot::TestSnapshotService}; + use triehash_ethereum::ordered_trie_root; + use types::{ + header::Header as BlockHeader, + transaction::{SignedTransaction, Transaction}, + }; + + fn dummy_header(number: u64, parent_hash: H256) -> BlockHeader { + let mut header = BlockHeader::new(); + header.set_gas_limit(0.into()); + header.set_difficulty((number * 100).into()); + header.set_timestamp(number * 10); + header.set_number(number); + header.set_parent_hash(parent_hash); + header.set_state_root(H256::zero()); + header + } + + fn dummy_signed_tx() -> SignedTransaction { + let keypair = Random.generate().unwrap(); + Transaction::default().sign(keypair.secret(), None) + } + + fn import_headers( + headers: &[BlockHeader], + downloader: &mut BlockDownloader, + io: &mut dyn SyncIo, + ) -> Result { + let mut stream = RlpStream::new(); + stream.append_list(headers); + let bytes = stream.out(); + let rlp = Rlp::new(&bytes); + let expected_hash = headers.first().unwrap().hash(); + downloader.import_headers(io, &rlp, expected_hash) + } + + fn import_headers_ok( + headers: &[BlockHeader], + downloader: &mut BlockDownloader, + io: &mut dyn SyncIo, + ) { + let res = import_headers(headers, downloader, io); + assert!(res.is_ok()); + } + + #[test] + fn import_headers_in_chain_head_state() { + ::env_logger::try_init().ok(); + + let spec = Spec::new_test(); + let genesis_hash = spec.genesis_header().hash(); + + let mut downloader = BlockDownloader::new(BlockSet::NewBlocks, &genesis_hash, 0); + downloader.state = State::ChainHead; + + let mut chain = TestBlockChainClient::new(); + let snapshot_service = TestSnapshotService::new(); + let queue = RwLock::new(VecDeque::new()); + let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); + + // Valid headers sequence. + let valid_headers = [ + spec.genesis_header(), + dummy_header(127, H256::random()), + dummy_header(254, H256::random()), + ]; + let rlp_data = encode_list(&valid_headers); + let valid_rlp = Rlp::new(&rlp_data); + + match downloader.import_headers(&mut io, &valid_rlp, genesis_hash) { + Ok(DownloadAction::Reset) => assert_eq!(downloader.state, State::Blocks), + _ => panic!("expected transition to Blocks state"), + }; + + // Headers are rejected because the expected hash does not match. + let invalid_start_block_headers = [ + dummy_header(0, H256::random()), + dummy_header(127, H256::random()), + dummy_header(254, H256::random()), + ]; + let rlp_data = encode_list(&invalid_start_block_headers); + let invalid_start_block_rlp = Rlp::new(&rlp_data); + + match downloader.import_headers(&mut io, &invalid_start_block_rlp, genesis_hash) { + Err(BlockDownloaderImportError::Invalid) => (), + _ => panic!("expected BlockDownloaderImportError"), + }; + + // Headers are rejected because they are not spaced as expected. + let invalid_skip_headers = [ + spec.genesis_header(), + dummy_header(128, H256::random()), + dummy_header(256, H256::random()), + ]; + let rlp_data = encode_list(&invalid_skip_headers); + let invalid_skip_rlp = Rlp::new(&rlp_data); + + match downloader.import_headers(&mut io, &invalid_skip_rlp, genesis_hash) { + Err(BlockDownloaderImportError::Invalid) => (), + _ => panic!("expected BlockDownloaderImportError"), + }; + + // Invalid because the packet size is too large. + let mut too_many_headers = Vec::with_capacity((SUBCHAIN_SIZE + 1) as usize); + too_many_headers.push(spec.genesis_header()); + for i in 1..(SUBCHAIN_SIZE + 1) { + too_many_headers.push(dummy_header( + (MAX_HEADERS_TO_REQUEST as u64 - 1) * i, + H256::random(), + )); + } + let rlp_data = encode_list(&too_many_headers); + + let too_many_rlp = Rlp::new(&rlp_data); + match downloader.import_headers(&mut io, &too_many_rlp, genesis_hash) { + Err(BlockDownloaderImportError::Invalid) => (), + _ => panic!("expected BlockDownloaderImportError"), + }; + } + + #[test] + fn import_headers_in_blocks_state() { + ::env_logger::try_init().ok(); + + let mut chain = TestBlockChainClient::new(); + let snapshot_service = TestSnapshotService::new(); + let queue = RwLock::new(VecDeque::new()); + let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); + + let mut headers = Vec::with_capacity(3); + let parent_hash = H256::random(); + headers.push(dummy_header(127, parent_hash)); + let parent_hash = headers[0].hash(); + headers.push(dummy_header(128, parent_hash)); + let parent_hash = headers[1].hash(); + headers.push(dummy_header(129, parent_hash)); + + let mut downloader = BlockDownloader::new(BlockSet::NewBlocks, &H256::random(), 0); + downloader.state = State::Blocks; + downloader.blocks.reset_to(vec![headers[0].hash()]); + + let rlp_data = encode_list(&headers); + let headers_rlp = Rlp::new(&rlp_data); + + match downloader.import_headers(&mut io, &headers_rlp, headers[0].hash()) { + Ok(DownloadAction::None) => (), + _ => panic!("expected successful import"), + }; + + // Invalidate parent_hash link. + headers[2] = dummy_header(129, H256::random()); + let rlp_data = encode_list(&headers); + let headers_rlp = Rlp::new(&rlp_data); + + match downloader.import_headers(&mut io, &headers_rlp, headers[0].hash()) { + Err(BlockDownloaderImportError::Invalid) => (), + _ => panic!("expected BlockDownloaderImportError"), + }; + + // Invalidate header sequence by skipping a header. + headers[2] = dummy_header(130, headers[1].hash()); + let rlp_data = encode_list(&headers); + let headers_rlp = Rlp::new(&rlp_data); + + match downloader.import_headers(&mut io, &headers_rlp, headers[0].hash()) { + Err(BlockDownloaderImportError::Invalid) => (), + _ => panic!("expected BlockDownloaderImportError"), + }; + } + + #[test] + fn import_bodies() { + ::env_logger::try_init().ok(); + + let mut chain = TestBlockChainClient::new(); + let snapshot_service = TestSnapshotService::new(); + let queue = RwLock::new(VecDeque::new()); + let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); + + // Import block headers. + let mut headers = Vec::with_capacity(4); + let mut bodies = Vec::with_capacity(4); + let mut parent_hash = H256::zero(); + for i in 0..4 { + // Construct the block body + let uncles = if i > 0 { + encode_list(&[dummy_header(i - 1, H256::random())]) + } else { + ::rlp::EMPTY_LIST_RLP.to_vec() + }; + + let txs = encode_list(&[dummy_signed_tx()]); + let tx_root = ordered_trie_root(Rlp::new(&txs).iter().map(|r| r.as_raw())); + + let mut rlp = RlpStream::new_list(2); + rlp.append_raw(&txs, 1); + rlp.append_raw(&uncles, 1); + bodies.push(rlp.out()); + + // Construct the block header + let mut header = dummy_header(i, parent_hash); + header.set_transactions_root(tx_root); + header.set_uncles_hash(keccak(&uncles)); + parent_hash = header.hash(); + headers.push(header); + } + + let mut downloader = BlockDownloader::new(BlockSet::NewBlocks, &headers[0].hash(), 0); + downloader.state = State::Blocks; + downloader.blocks.reset_to(vec![headers[0].hash()]); + + // Only import the first three block headers. + let rlp_data = encode_list(&headers[0..3]); + let headers_rlp = Rlp::new(&rlp_data); + assert!(downloader + .import_headers(&mut io, &headers_rlp, headers[0].hash()) + .is_ok()); + + // Import first body successfully. + let mut rlp_data = RlpStream::new_list(1); + rlp_data.append_raw(&bodies[0], 1); + let bodies_rlp = Rlp::new(rlp_data.as_raw()); + assert!(downloader + .import_bodies(&bodies_rlp, &[headers[0].hash(), headers[1].hash()]) + .is_ok()); + + // Import second body successfully. + let mut rlp_data = RlpStream::new_list(1); + rlp_data.append_raw(&bodies[1], 1); + let bodies_rlp = Rlp::new(rlp_data.as_raw()); + assert!(downloader + .import_bodies(&bodies_rlp, &[headers[0].hash(), headers[1].hash()]) + .is_ok()); + + // Import unexpected third body. + let mut rlp_data = RlpStream::new_list(1); + rlp_data.append_raw(&bodies[2], 1); + let bodies_rlp = Rlp::new(rlp_data.as_raw()); + match downloader.import_bodies(&bodies_rlp, &[headers[0].hash(), headers[1].hash()]) { + Err(BlockDownloaderImportError::Invalid) => (), + _ => panic!("expected BlockDownloaderImportError"), + }; + } + + #[test] + fn import_receipts() { + ::env_logger::try_init().ok(); + + let mut chain = TestBlockChainClient::new(); + let snapshot_service = TestSnapshotService::new(); + let queue = RwLock::new(VecDeque::new()); + let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); + + // Import block headers. + let mut headers = Vec::with_capacity(4); + let mut receipts = Vec::with_capacity(4); + let mut parent_hash = H256::zero(); + for i in 0..4 { + // Construct the receipts. Receipt root for the first two blocks is the same. + // + // The RLP-encoded integers are clearly not receipts, but the BlockDownloader treats + // all receipts as byte blobs, so it does not matter. + let receipts_rlp = if i < 2 { + encode_list(&[0u32]) + } else { + encode_list(&[i as u32]) + }; + let receipts_root = + ordered_trie_root(Rlp::new(&receipts_rlp).iter().map(|r| r.as_raw())); + receipts.push(receipts_rlp); + + // Construct the block header. + let mut header = dummy_header(i, parent_hash); + header.set_receipts_root(receipts_root); + parent_hash = header.hash(); + headers.push(header); + } + + let mut downloader = BlockDownloader::new(BlockSet::OldBlocks, &headers[0].hash(), 0); + downloader.state = State::Blocks; + downloader.blocks.reset_to(vec![headers[0].hash()]); + + // Only import the first three block headers. + let rlp_data = encode_list(&headers[0..3]); + let headers_rlp = Rlp::new(&rlp_data); + assert!(downloader + .import_headers(&mut io, &headers_rlp, headers[0].hash()) + .is_ok()); + + // Import second and third receipts successfully. + let mut rlp_data = RlpStream::new_list(2); + rlp_data.append_raw(&receipts[1], 1); + rlp_data.append_raw(&receipts[2], 1); + let receipts_rlp = Rlp::new(rlp_data.as_raw()); + assert!(downloader + .import_receipts(&receipts_rlp, &[headers[1].hash(), headers[2].hash()]) + .is_ok()); + + // Import unexpected fourth receipt. + let mut rlp_data = RlpStream::new_list(1); + rlp_data.append_raw(&receipts[3], 1); + let bodies_rlp = Rlp::new(rlp_data.as_raw()); + match downloader.import_bodies(&bodies_rlp, &[headers[1].hash(), headers[2].hash()]) { + Err(BlockDownloaderImportError::Invalid) => (), + _ => panic!("expected BlockDownloaderImportError"), + }; + } + + #[test] + fn reset_after_multiple_sets_of_useless_headers() { + ::env_logger::try_init().ok(); + + let spec = Spec::new_test(); + let genesis_hash = spec.genesis_header().hash(); + + let mut downloader = BlockDownloader::new(BlockSet::NewBlocks, &genesis_hash, 0); + downloader.state = State::ChainHead; + + let mut chain = TestBlockChainClient::new(); + let snapshot_service = TestSnapshotService::new(); + let queue = RwLock::new(VecDeque::new()); + let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); + + let heads = [ + spec.genesis_header(), + dummy_header(127, H256::random()), + dummy_header(254, H256::random()), + ]; + + let short_subchain = [dummy_header(1, genesis_hash)]; + + import_headers_ok(&heads, &mut downloader, &mut io); + import_headers_ok(&short_subchain, &mut downloader, &mut io); + + assert_eq!(downloader.state, State::Blocks); + assert!(!downloader.blocks.is_empty()); + + // simulate receiving useless headers + let head = vec![short_subchain.last().unwrap().clone()]; + for _ in 0..MAX_USELESS_HEADERS_PER_ROUND { + let res = import_headers(&head, &mut downloader, &mut io); + assert!(res.is_err()); + } + + assert_eq!(downloader.state, State::Idle); + assert!(downloader.blocks.is_empty()); + } + + #[test] + fn dont_reset_after_multiple_sets_of_useless_headers_for_chain_head() { + ::env_logger::try_init().ok(); + + let spec = Spec::new_test(); + let genesis_hash = spec.genesis_header().hash(); + + let mut downloader = BlockDownloader::new(BlockSet::NewBlocks, &genesis_hash, 0); + downloader.state = State::ChainHead; + + let mut chain = TestBlockChainClient::new(); + let snapshot_service = TestSnapshotService::new(); + let queue = RwLock::new(VecDeque::new()); + let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); + + let heads = [spec.genesis_header()]; + + let short_subchain = [dummy_header(1, genesis_hash)]; + + import_headers_ok(&heads, &mut downloader, &mut io); + import_headers_ok(&short_subchain, &mut downloader, &mut io); + + assert_eq!(downloader.state, State::Blocks); + assert!(!downloader.blocks.is_empty()); + + // simulate receiving useless headers + let head = vec![short_subchain.last().unwrap().clone()]; + for _ in 0..MAX_USELESS_HEADERS_PER_ROUND { + let res = import_headers(&head, &mut downloader, &mut io); + assert!(res.is_err()); + } + + // download shouldn't be reset since this is the chain head for a single subchain. + // this state usually occurs for NewBlocks when it has reached the chain head. + assert_eq!(downloader.state, State::Blocks); + assert!(!downloader.blocks.is_empty()); + } } diff --git a/ethcore/sync/src/blocks.rs b/ethcore/sync/src/blocks.rs index 125c8d0b9c5..9ae323cbe6d 100644 --- a/ethcore/sync/src/blocks.rs +++ b/ethcore/sync/src/blocks.rs @@ -1,141 +1,139 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::collections::{HashSet, HashMap, hash_map}; -use hash::{keccak, KECCAK_NULL_RLP, KECCAK_EMPTY_LIST_RLP}; -use heapsize::HeapSizeOf; -use ethereum_types::H256; -use triehash_ethereum::ordered_trie_root; use bytes::Bytes; -use rlp::{Rlp, RlpStream, DecoderError}; -use network; use ethcore::verification::queue::kind::blocks::Unverified; -use types::transaction::UnverifiedTransaction; -use types::header::Header as BlockHeader; +use ethereum_types::H256; +use hash::{keccak, KECCAK_EMPTY_LIST_RLP, KECCAK_NULL_RLP}; +use heapsize::HeapSizeOf; +use network; +use rlp::{DecoderError, Rlp, RlpStream}; +use std::collections::{hash_map, BTreeMap, HashMap, HashSet}; +use triehash_ethereum::ordered_trie_root; +use types::{header::Header as BlockHeader, transaction::UnverifiedTransaction}; known_heap_size!(0, HeaderId); #[derive(PartialEq, Debug, Clone)] pub struct SyncHeader { - pub bytes: Bytes, - pub header: BlockHeader, + pub bytes: Bytes, + pub header: BlockHeader, } impl HeapSizeOf for SyncHeader { - fn heap_size_of_children(&self) -> usize { - self.bytes.heap_size_of_children() - + self.header.heap_size_of_children() - } + fn heap_size_of_children(&self) -> usize { + self.bytes.heap_size_of_children() + self.header.heap_size_of_children() + } } impl SyncHeader { - pub fn from_rlp(bytes: Bytes) -> Result { - let result = SyncHeader { - header: ::rlp::decode(&bytes)?, - bytes, - }; - - Ok(result) - } + pub fn from_rlp(bytes: Bytes) -> Result { + let result = SyncHeader { + header: ::rlp::decode(&bytes)?, + bytes, + }; + + Ok(result) + } } pub struct SyncBody { - pub transactions_bytes: Bytes, - pub transactions: Vec, - pub uncles_bytes: Bytes, - pub uncles: Vec, + pub transactions_bytes: Bytes, + pub transactions: Vec, + pub uncles_bytes: Bytes, + pub uncles: Vec, } impl SyncBody { - pub fn from_rlp(bytes: &[u8]) -> Result { - let rlp = Rlp::new(bytes); - let transactions_rlp = rlp.at(0)?; - let uncles_rlp = rlp.at(1)?; - - let result = SyncBody { - transactions_bytes: transactions_rlp.as_raw().to_vec(), - transactions: transactions_rlp.as_list()?, - uncles_bytes: uncles_rlp.as_raw().to_vec(), - uncles: uncles_rlp.as_list()?, - }; - - Ok(result) - } - - fn empty_body() -> Self { - SyncBody { - transactions_bytes: ::rlp::EMPTY_LIST_RLP.to_vec(), - transactions: Vec::with_capacity(0), - uncles_bytes: ::rlp::EMPTY_LIST_RLP.to_vec(), - uncles: Vec::with_capacity(0), - } - } + pub fn from_rlp(bytes: &[u8]) -> Result { + let rlp = Rlp::new(bytes); + let transactions_rlp = rlp.at(0)?; + let uncles_rlp = rlp.at(1)?; + + let result = SyncBody { + transactions_bytes: transactions_rlp.as_raw().to_vec(), + transactions: transactions_rlp.as_list()?, + uncles_bytes: uncles_rlp.as_raw().to_vec(), + uncles: uncles_rlp.as_list()?, + }; + + Ok(result) + } + + fn empty_body() -> Self { + SyncBody { + transactions_bytes: ::rlp::EMPTY_LIST_RLP.to_vec(), + transactions: Vec::with_capacity(0), + uncles_bytes: ::rlp::EMPTY_LIST_RLP.to_vec(), + uncles: Vec::with_capacity(0), + } + } } impl HeapSizeOf for SyncBody { - fn heap_size_of_children(&self) -> usize { - self.transactions_bytes.heap_size_of_children() - + self.transactions.heap_size_of_children() - + self.uncles_bytes.heap_size_of_children() - + self.uncles.heap_size_of_children() - } + fn heap_size_of_children(&self) -> usize { + self.transactions_bytes.heap_size_of_children() + + self.transactions.heap_size_of_children() + + self.uncles_bytes.heap_size_of_children() + + self.uncles.heap_size_of_children() + } } /// Block data with optional body. struct SyncBlock { - header: SyncHeader, - body: Option, - receipts: Option, - receipts_root: H256, + header: SyncHeader, + body: Option, + receipts: Option, + receipts_root: H256, } impl HeapSizeOf for SyncBlock { - fn heap_size_of_children(&self) -> usize { - self.header.heap_size_of_children() + self.body.heap_size_of_children() - } + fn heap_size_of_children(&self) -> usize { + self.header.heap_size_of_children() + self.body.heap_size_of_children() + } } fn unverified_from_sync(header: SyncHeader, body: Option) -> Unverified { - let mut stream = RlpStream::new_list(3); - stream.append_raw(&header.bytes, 1); - let body = body.unwrap_or_else(SyncBody::empty_body); - stream.append_raw(&body.transactions_bytes, 1); - stream.append_raw(&body.uncles_bytes, 1); - - Unverified { - header: header.header, - transactions: body.transactions, - uncles: body.uncles, - bytes: stream.out().to_vec(), - } + let mut stream = RlpStream::new_list(3); + stream.append_raw(&header.bytes, 1); + let body = body.unwrap_or_else(SyncBody::empty_body); + stream.append_raw(&body.transactions_bytes, 1); + stream.append_raw(&body.uncles_bytes, 1); + + Unverified { + header: header.header, + transactions: body.transactions, + uncles: body.uncles, + bytes: stream.out().to_vec(), + } } /// Block with optional receipt pub struct BlockAndReceipts { - /// Block data. - pub block: Unverified, - /// Block receipts RLP list. - pub receipts: Option, + /// Block data. + pub block: Unverified, + /// Block receipts RLP list. + pub receipts: Option, } /// Used to identify header by transactions and uncles hashes #[derive(Eq, PartialEq, Hash)] struct HeaderId { - transactions_root: H256, - uncles: H256 + transactions_root: H256, + uncles: H256, } /// A collection of blocks and subchain pointers being downloaded. This keeps track of @@ -143,565 +141,664 @@ struct HeaderId { /// the downloaded blocks. #[derive(Default)] pub struct BlockCollection { - /// Does this collection need block receipts. - need_receipts: bool, - /// Heads of subchains to download - heads: Vec, - /// Downloaded blocks. - blocks: HashMap, - /// Downloaded blocks by parent. - parents: HashMap, - /// Used to map body to header. - header_ids: HashMap, - /// Used to map receipts root to headers. - receipt_ids: HashMap>, - /// First block in `blocks`. - head: Option, - /// Set of block header hashes being downloaded - downloading_headers: HashSet, - /// Set of block bodies being downloaded identified by block hash. - downloading_bodies: HashSet, - /// Set of block receipts being downloaded identified by receipt root. - downloading_receipts: HashSet, + /// Does this collection need block receipts. + need_receipts: bool, + /// Heads of subchains to download + heads: Vec, + /// Downloaded blocks. + blocks: HashMap, + /// Downloaded blocks by parent. + parents: HashMap, + /// Used to map body to header. + header_ids: HashMap, + /// Used to map receipts root to headers. + receipt_ids: HashMap>, + /// First block in `blocks`. + head: Option, + /// Set of block header hashes being downloaded + downloading_headers: HashSet, + /// Set of block bodies being downloaded identified by block hash. + downloading_bodies: HashSet, + /// Set of block receipts being downloaded identified by receipt root. + downloading_receipts: HashSet, } impl BlockCollection { - /// Create a new instance. - pub fn new(download_receipts: bool) -> BlockCollection { - BlockCollection { - need_receipts: download_receipts, - blocks: HashMap::new(), - header_ids: HashMap::new(), - receipt_ids: HashMap::new(), - heads: Vec::new(), - parents: HashMap::new(), - head: None, - downloading_headers: HashSet::new(), - downloading_bodies: HashSet::new(), - downloading_receipts: HashSet::new(), - } - } - - /// Clear everything. - pub fn clear(&mut self) { - self.blocks.clear(); - self.parents.clear(); - self.header_ids.clear(); - self.receipt_ids.clear(); - self.heads.clear(); - self.head = None; - self.downloading_headers.clear(); - self.downloading_bodies.clear(); - self.downloading_receipts.clear(); - } - - /// Reset collection for a new sync round with given subchain block hashes. - pub fn reset_to(&mut self, hashes: Vec) { - self.clear(); - self.heads = hashes; - } - - /// Insert a set of headers into collection and advance subchain head pointers. - pub fn insert_headers(&mut self, headers: Vec) { - for h in headers { - if let Err(e) = self.insert_header(h) { - trace!(target: "sync", "Ignored invalid header: {:?}", e); - } - } - self.update_heads(); - } - - /// Insert a collection of block bodies for previously downloaded headers. - pub fn insert_bodies(&mut self, bodies: Vec) -> Vec { - bodies.into_iter() - .filter_map(|b| { - self.insert_body(b) - .map_err(|e| trace!(target: "sync", "Ignored invalid body: {:?}", e)) - .ok() - }) - .collect() - } - - /// Insert a collection of block receipts for previously downloaded headers. - pub fn insert_receipts(&mut self, receipts: Vec) -> Vec> { - if !self.need_receipts { - return Vec::new(); - } - receipts.into_iter() - .filter_map(|r| { - self.insert_receipt(r) - .map_err(|e| trace!(target: "sync", "Ignored invalid receipt: {:?}", e)) - .ok() - }) - .collect() - } - - /// Returns a set of block hashes that require a body download. The returned set is marked as being downloaded. - pub fn needed_bodies(&mut self, count: usize, _ignore_downloading: bool) -> Vec { - if self.head.is_none() { - return Vec::new(); - } - let mut needed_bodies: Vec = Vec::new(); - let mut head = self.head; - while head.is_some() && needed_bodies.len() < count { - head = self.parents.get(&head.unwrap()).cloned(); - if let Some(head) = head { - match self.blocks.get(&head) { - Some(block) if block.body.is_none() && !self.downloading_bodies.contains(&head) => { - self.downloading_bodies.insert(head.clone()); - needed_bodies.push(head.clone()); - } - _ => (), - } - } - } - for h in self.header_ids.values() { - if needed_bodies.len() >= count { - break; - } - if !self.downloading_bodies.contains(h) { - needed_bodies.push(h.clone()); - self.downloading_bodies.insert(h.clone()); - } - } - needed_bodies - } - - /// Returns a set of block hashes that require a receipt download. The returned set is marked as being downloaded. - pub fn needed_receipts(&mut self, count: usize, _ignore_downloading: bool) -> Vec { - if self.head.is_none() || !self.need_receipts { - return Vec::new(); - } - let mut needed_receipts: Vec = Vec::new(); - let mut head = self.head; - while head.is_some() && needed_receipts.len() < count { - head = self.parents.get(&head.unwrap()).cloned(); - if let Some(head) = head { - match self.blocks.get(&head) { - Some(block) => { - if block.receipts.is_none() && !self.downloading_receipts.contains(&block.receipts_root) { - self.downloading_receipts.insert(block.receipts_root); - needed_receipts.push(head.clone()); - } - } - _ => (), - } - } - } - // If there are multiple blocks per receipt, only request one of them. - for (root, h) in self.receipt_ids.iter().map(|(root, hashes)| (root, hashes[0])) { - if needed_receipts.len() >= count { - break; - } - if !self.downloading_receipts.contains(root) { - needed_receipts.push(h.clone()); - self.downloading_receipts.insert(*root); - } - } - needed_receipts - } - - /// Returns a set of block hashes that require a header download. The returned set is marked as being downloaded. - pub fn needed_headers(&mut self, count: usize, ignore_downloading: bool) -> Option<(H256, usize)> { - // find subchain to download - let mut download = None; - { - for h in &self.heads { - if ignore_downloading || !self.downloading_headers.contains(h) { - self.downloading_headers.insert(h.clone()); - download = Some(h.clone()); - break; - } - } - } - download.map(|h| (h, count)) - } - - /// Unmark header as being downloaded. - pub fn clear_header_download(&mut self, hash: &H256) { - self.downloading_headers.remove(hash); - } - - /// Unmark block body as being downloaded. - pub fn clear_body_download(&mut self, hashes: &[H256]) { - for h in hashes { - self.downloading_bodies.remove(h); - } - } - - /// Unmark block receipt as being downloaded. - pub fn clear_receipt_download(&mut self, hashes: &[H256]) { - for h in hashes { - if let Some(ref block) = self.blocks.get(h) { - self.downloading_receipts.remove(&block.receipts_root); - } - } - } - - /// Get a valid chain of blocks ordered in ascending order and ready for importing into blockchain. - pub fn drain(&mut self) -> Vec { - if self.blocks.is_empty() || self.head.is_none() { - return Vec::new(); - } - - let mut drained = Vec::new(); - let mut hashes = Vec::new(); - { - let mut blocks = Vec::new(); - let mut head = self.head; - while let Some(h) = head { - head = self.parents.get(&h).cloned(); - if let Some(head) = head { - match self.blocks.remove(&head) { - Some(block) => { - if block.body.is_some() && (!self.need_receipts || block.receipts.is_some()) { - blocks.push(block); - hashes.push(head); - self.head = Some(head); - } else { - self.blocks.insert(head, block); - break; - } - }, - _ => { - break; - }, - } - } - } - - for block in blocks.into_iter() { - let unverified = unverified_from_sync(block.header, block.body); - drained.push(BlockAndReceipts { - block: unverified, - receipts: block.receipts.clone(), - }); - } - } - - trace!(target: "sync", "Drained {} blocks, new head :{:?}", drained.len(), self.head); - drained - } - - /// Check if the collection is empty. We consider the syncing round complete once - /// there is no block data left and only a single or none head pointer remains. - pub fn is_empty(&self) -> bool { - self.heads.len() == 0 || (self.heads.len() == 1 && self.head.map_or(false, |h| h == self.heads[0])) - } - - /// Check if collection contains a block header. - pub fn contains(&self, hash: &H256) -> bool { - self.blocks.contains_key(hash) - } - - /// Check the number of heads - pub fn heads_len(&self) -> usize { - self.heads.len() - } - - /// Return used heap size. - pub fn heap_size(&self) -> usize { - self.heads.heap_size_of_children() - + self.blocks.heap_size_of_children() - + self.parents.heap_size_of_children() - + self.header_ids.heap_size_of_children() - + self.downloading_headers.heap_size_of_children() - + self.downloading_bodies.heap_size_of_children() - } - - /// Check if given block hash is marked as being downloaded. - pub fn is_downloading(&self, hash: &H256) -> bool { - self.downloading_headers.contains(hash) || self.downloading_bodies.contains(hash) - } - - fn insert_body(&mut self, body: SyncBody) -> Result { - let header_id = { - let tx_root = ordered_trie_root(Rlp::new(&body.transactions_bytes).iter().map(|r| r.as_raw())); - let uncles = keccak(&body.uncles_bytes); - HeaderId { - transactions_root: tx_root, - uncles: uncles - } - }; - - match self.header_ids.remove(&header_id) { - Some(h) => { - self.downloading_bodies.remove(&h); - match self.blocks.get_mut(&h) { - Some(ref mut block) => { - trace!(target: "sync", "Got body {}", h); - block.body = Some(body); - Ok(h) - }, - None => { - warn!("Got body with no header {}", h); - Err(network::ErrorKind::BadProtocol.into()) - } - } - } - None => { - trace!(target: "sync", "Ignored unknown/stale block body. tx_root = {:?}, uncles = {:?}", header_id.transactions_root, header_id.uncles); - Err(network::ErrorKind::BadProtocol.into()) - } - } - } - - fn insert_receipt(&mut self, r: Bytes) -> Result, network::Error> { - let receipt_root = { - let receipts = Rlp::new(&r); - ordered_trie_root(receipts.iter().map(|r| r.as_raw())) - }; - self.downloading_receipts.remove(&receipt_root); - match self.receipt_ids.entry(receipt_root) { - hash_map::Entry::Occupied(entry) => { - let block_hashes = entry.remove(); - for h in block_hashes.iter() { - match self.blocks.get_mut(&h) { - Some(ref mut block) => { - trace!(target: "sync", "Got receipt {}", h); - block.receipts = Some(r.clone()); - }, - None => { - warn!("Got receipt with no header {}", h); - return Err(network::ErrorKind::BadProtocol.into()) - } - } - } - Ok(block_hashes) - }, - hash_map::Entry::Vacant(_) => { - trace!(target: "sync", "Ignored unknown/stale block receipt {:?}", receipt_root); - Err(network::ErrorKind::BadProtocol.into()) - } - } - } - - fn insert_header(&mut self, info: SyncHeader) -> Result { - let hash = info.header.hash(); - if self.blocks.contains_key(&hash) { - return Ok(hash); - } - - match self.head { - None if hash == self.heads[0] => { - trace!(target: "sync", "New head {}", hash); - self.head = Some(info.header.parent_hash().clone()); - }, - _ => () - } - - let header_id = HeaderId { - transactions_root: *info.header.transactions_root(), - uncles: *info.header.uncles_hash(), - }; - - let body = if header_id.transactions_root == KECCAK_NULL_RLP && header_id.uncles == KECCAK_EMPTY_LIST_RLP { - // empty body, just mark as downloaded - Some(SyncBody::empty_body()) - } else { - trace!( - "Queueing body tx_root = {:?}, uncles = {:?}, block = {:?}, number = {}", - header_id.transactions_root, - header_id.uncles, - hash, - info.header.number() - ); - self.header_ids.insert(header_id, hash); - None - }; - - let (receipts, receipts_root) = if self.need_receipts { - let receipt_root = *info.header.receipts_root(); - if receipt_root == KECCAK_NULL_RLP { - let receipts_stream = RlpStream::new_list(0); - (Some(receipts_stream.out()), receipt_root) - } else { - self.receipt_ids.entry(receipt_root).or_insert_with(Vec::new).push(hash); - (None, receipt_root) - } - } else { - (None, H256::new()) - }; - - self.parents.insert(*info.header.parent_hash(), hash); - - let block = SyncBlock { - header: info, - body, - receipts, - receipts_root, - }; - - self.blocks.insert(hash, block); - trace!(target: "sync", "New header: {:x}", hash); - Ok(hash) - } - - // update subchain headers - fn update_heads(&mut self) { - let mut new_heads = Vec::new(); - let old_subchains: HashSet<_> = { self.heads.iter().cloned().collect() }; - for s in self.heads.drain(..) { - let mut h = s.clone(); - if !self.blocks.contains_key(&h) { - new_heads.push(h); - continue; - } - loop { - match self.parents.get(&h) { - Some(next) => { - h = next.clone(); - if old_subchains.contains(&h) { - trace!(target: "sync", "Completed subchain {:?}", s); - break; // reached head of the other subchain, merge by not adding - } - }, - _ => { - new_heads.push(h); - break; - } - } - } - } - self.heads = new_heads; - } + /// Create a new instance. + pub fn new(download_receipts: bool) -> BlockCollection { + BlockCollection { + need_receipts: download_receipts, + blocks: HashMap::new(), + header_ids: HashMap::new(), + receipt_ids: HashMap::new(), + heads: Vec::new(), + parents: HashMap::new(), + head: None, + downloading_headers: HashSet::new(), + downloading_bodies: HashSet::new(), + downloading_receipts: HashSet::new(), + } + } + + /// Clear everything. + pub fn clear(&mut self) { + self.blocks.clear(); + self.parents.clear(); + self.header_ids.clear(); + self.receipt_ids.clear(); + self.heads.clear(); + self.head = None; + self.downloading_headers.clear(); + self.downloading_bodies.clear(); + self.downloading_receipts.clear(); + } + + /// Reset collection for a new sync round with given subchain block hashes. + pub fn reset_to(&mut self, hashes: Vec) { + self.clear(); + self.heads = hashes; + } + + /// Insert a set of headers into collection and advance subchain head pointers. + pub fn insert_headers(&mut self, headers: Vec) { + for h in headers { + if let Err(e) = self.insert_header(h) { + trace!(target: "sync", "Ignored invalid header: {:?}", e); + } + } + self.update_heads(); + } + + /// Insert a collection of block bodies for previously downloaded headers. + pub fn insert_bodies(&mut self, bodies: Vec) -> Vec { + bodies + .into_iter() + .filter_map(|b| { + self.insert_body(b) + .map_err(|e| trace!(target: "sync", "Ignored invalid body: {:?}", e)) + .ok() + }) + .collect() + } + + /// Insert a collection of block receipts for previously downloaded headers. + pub fn insert_receipts(&mut self, receipts: Vec) -> Vec> { + if !self.need_receipts { + return Vec::new(); + } + receipts + .into_iter() + .filter_map(|r| { + self.insert_receipt(r) + .map_err(|e| trace!(target: "sync", "Ignored invalid receipt: {:?}", e)) + .ok() + }) + .collect() + } + + /// Returns a set of block hashes that require a body download. The returned set is marked as being downloaded. + pub fn needed_bodies(&mut self, count: usize, _ignore_downloading: bool) -> Vec { + if self.head.is_none() { + return Vec::new(); + } + let mut needed_bodies: Vec = Vec::new(); + let mut head = self.head; + while head.is_some() && needed_bodies.len() < count { + head = self.parents.get(&head.unwrap()).cloned(); + if let Some(head) = head { + match self.blocks.get(&head) { + Some(block) + if block.body.is_none() && !self.downloading_bodies.contains(&head) => + { + self.downloading_bodies.insert(head.clone()); + needed_bodies.push(head.clone()); + } + _ => (), + } + } + } + for h in self.header_ids.values() { + if needed_bodies.len() >= count { + break; + } + if !self.downloading_bodies.contains(h) { + needed_bodies.push(h.clone()); + self.downloading_bodies.insert(h.clone()); + } + } + needed_bodies + } + + /// Returns a set of block hashes that require a receipt download. The returned set is marked as being downloaded. + pub fn needed_receipts(&mut self, count: usize, _ignore_downloading: bool) -> Vec { + if self.head.is_none() || !self.need_receipts { + return Vec::new(); + } + let mut needed_receipts: Vec = Vec::new(); + let mut head = self.head; + while head.is_some() && needed_receipts.len() < count { + head = self.parents.get(&head.unwrap()).cloned(); + if let Some(head) = head { + match self.blocks.get(&head) { + Some(block) => { + if block.receipts.is_none() + && !self.downloading_receipts.contains(&block.receipts_root) + { + self.downloading_receipts.insert(block.receipts_root); + needed_receipts.push(head.clone()); + } + } + _ => (), + } + } + } + // If there are multiple blocks per receipt, only request one of them. + for (root, h) in self + .receipt_ids + .iter() + .map(|(root, hashes)| (root, hashes[0])) + { + if needed_receipts.len() >= count { + break; + } + if !self.downloading_receipts.contains(root) { + needed_receipts.push(h.clone()); + self.downloading_receipts.insert(*root); + } + } + needed_receipts + } + + /// Returns a set of block hashes that require a header download. The returned set is marked as being downloaded. + pub fn needed_headers( + &mut self, + count: usize, + ignore_downloading: bool, + ) -> Option<(H256, usize)> { + // find subchain to download + let mut download = None; + { + for h in &self.heads { + if ignore_downloading || !self.downloading_headers.contains(h) { + self.downloading_headers.insert(h.clone()); + download = Some(h.clone()); + break; + } + } + } + download.map(|h| (h, count)) + } + + /// Unmark header as being downloaded. + pub fn clear_header_download(&mut self, hash: &H256) { + self.downloading_headers.remove(hash); + } + + /// Unmark block body as being downloaded. + pub fn clear_body_download(&mut self, hashes: &[H256]) { + for h in hashes { + self.downloading_bodies.remove(h); + } + } + + /// Unmark block receipt as being downloaded. + pub fn clear_receipt_download(&mut self, hashes: &[H256]) { + for h in hashes { + if let Some(ref block) = self.blocks.get(h) { + self.downloading_receipts.remove(&block.receipts_root); + } + } + } + + /// Get a valid chain of blocks ordered in ascending order and ready for importing into blockchain. + pub fn drain(&mut self) -> Vec { + if self.blocks.is_empty() || self.head.is_none() { + return Vec::new(); + } + + let mut drained = Vec::new(); + let mut hashes = Vec::new(); + { + let mut blocks = Vec::new(); + let mut head = self.head; + while let Some(h) = head { + head = self.parents.get(&h).cloned(); + if let Some(head) = head { + match self.blocks.remove(&head) { + Some(block) => { + if block.body.is_some() + && (!self.need_receipts || block.receipts.is_some()) + { + blocks.push(block); + hashes.push(head); + self.head = Some(head); + } else { + self.blocks.insert(head, block); + break; + } + } + _ => { + break; + } + } + } + } + + for block in blocks.into_iter() { + let unverified = unverified_from_sync(block.header, block.body); + drained.push(BlockAndReceipts { + block: unverified, + receipts: block.receipts.clone(), + }); + } + } + + trace!(target: "sync", "Drained {} blocks, new head :{:?}", drained.len(), self.head); + drained + } + + /// Check if the collection is empty. We consider the syncing round complete once + /// there is no block data left and only a single or none head pointer remains. + pub fn is_empty(&self) -> bool { + self.heads.len() == 0 + || (self.heads.len() == 1 && self.head.map_or(false, |h| h == self.heads[0])) + } + + /// Check if collection contains a block header. + pub fn contains(&self, hash: &H256) -> bool { + self.blocks.contains_key(hash) + } + + /// Check the number of heads + pub fn heads_len(&self) -> usize { + self.heads.len() + } + + /// Return number of items size. + pub fn get_sizes(&self, sizes: &mut BTreeMap, insert_prefix: &str) { + sizes.insert(format!("{}{}", insert_prefix, "heads"), self.heads.len()); + sizes.insert(format!("{}{}", insert_prefix, "blocks"), self.blocks.len()); + sizes.insert( + format!("{}{}", insert_prefix, "parents_len"), + self.parents.len(), + ); + sizes.insert( + format!("{}{}", insert_prefix, "header_ids_len"), + self.header_ids.len(), + ); + sizes.insert( + format!("{}{}", insert_prefix, "downloading_headers_len"), + self.downloading_headers.len(), + ); + sizes.insert( + format!("{}{}", insert_prefix, "downloading_bodies_len"), + self.downloading_bodies.len(), + ); + + if self.need_receipts { + sizes.insert( + format!("{}{}", insert_prefix, "downloading_receipts_len"), + self.downloading_receipts.len(), + ); + sizes.insert( + format!("{}{}", insert_prefix, "receipt_ids_len"), + self.receipt_ids.len(), + ); + } + } + + /// Check if given block hash is marked as being downloaded. + pub fn is_downloading(&self, hash: &H256) -> bool { + self.downloading_headers.contains(hash) || self.downloading_bodies.contains(hash) + } + + fn insert_body(&mut self, body: SyncBody) -> Result { + let header_id = { + let tx_root = ordered_trie_root( + Rlp::new(&body.transactions_bytes) + .iter() + .map(|r| r.as_raw()), + ); + let uncles = keccak(&body.uncles_bytes); + HeaderId { + transactions_root: tx_root, + uncles: uncles, + } + }; + + match self.header_ids.remove(&header_id) { + Some(h) => { + self.downloading_bodies.remove(&h); + match self.blocks.get_mut(&h) { + Some(ref mut block) => { + trace!(target: "sync", "Got body {}", h); + block.body = Some(body); + Ok(h) + } + None => { + warn!("Got body with no header {}", h); + Err(network::ErrorKind::BadProtocol.into()) + } + } + } + None => { + trace!(target: "sync", "Ignored unknown/stale block body. tx_root = {:?}, uncles = {:?}", header_id.transactions_root, header_id.uncles); + Err(network::ErrorKind::BadProtocol.into()) + } + } + } + + fn insert_receipt(&mut self, r: Bytes) -> Result, network::Error> { + let receipt_root = { + let receipts = Rlp::new(&r); + ordered_trie_root(receipts.iter().map(|r| r.as_raw())) + }; + self.downloading_receipts.remove(&receipt_root); + match self.receipt_ids.entry(receipt_root) { + hash_map::Entry::Occupied(entry) => { + let block_hashes = entry.remove(); + for h in block_hashes.iter() { + match self.blocks.get_mut(&h) { + Some(ref mut block) => { + trace!(target: "sync", "Got receipt {}", h); + block.receipts = Some(r.clone()); + } + None => { + warn!("Got receipt with no header {}", h); + return Err(network::ErrorKind::BadProtocol.into()); + } + } + } + Ok(block_hashes) + } + hash_map::Entry::Vacant(_) => { + trace!(target: "sync", "Ignored unknown/stale block receipt {:?}", receipt_root); + Err(network::ErrorKind::BadProtocol.into()) + } + } + } + + fn insert_header(&mut self, info: SyncHeader) -> Result { + let hash = info.header.hash(); + if self.blocks.contains_key(&hash) { + return Ok(hash); + } + + match self.head { + None if hash == self.heads[0] => { + trace!(target: "sync", "New head {}", hash); + self.head = Some(info.header.parent_hash().clone()); + } + _ => (), + } + + let header_id = HeaderId { + transactions_root: *info.header.transactions_root(), + uncles: *info.header.uncles_hash(), + }; + + let body = if header_id.transactions_root == KECCAK_NULL_RLP + && header_id.uncles == KECCAK_EMPTY_LIST_RLP + { + // empty body, just mark as downloaded + Some(SyncBody::empty_body()) + } else { + trace!( + "Queueing body tx_root = {:?}, uncles = {:?}, block = {:?}, number = {}", + header_id.transactions_root, + header_id.uncles, + hash, + info.header.number() + ); + self.header_ids.insert(header_id, hash); + None + }; + + let (receipts, receipts_root) = if self.need_receipts { + let receipt_root = *info.header.receipts_root(); + if receipt_root == KECCAK_NULL_RLP { + let receipts_stream = RlpStream::new_list(0); + (Some(receipts_stream.out()), receipt_root) + } else { + self.receipt_ids + .entry(receipt_root) + .or_insert_with(Vec::new) + .push(hash); + (None, receipt_root) + } + } else { + (None, H256::new()) + }; + + self.parents.insert(*info.header.parent_hash(), hash); + + let block = SyncBlock { + header: info, + body, + receipts, + receipts_root, + }; + + self.blocks.insert(hash, block); + trace!(target: "sync", "New header: {:x}", hash); + Ok(hash) + } + + // update subchain headers + fn update_heads(&mut self) { + let mut new_heads = Vec::new(); + let old_subchains: HashSet<_> = { self.heads.iter().cloned().collect() }; + for s in self.heads.drain(..) { + let mut h = s.clone(); + if !self.blocks.contains_key(&h) { + new_heads.push(h); + continue; + } + loop { + match self.parents.get(&h) { + Some(next) => { + h = next.clone(); + if old_subchains.contains(&h) { + trace!(target: "sync", "Completed subchain {:?}", s); + break; // reached head of the other subchain, merge by not adding + } + } + _ => { + new_heads.push(h); + break; + } + } + } + } + self.heads = new_heads; + } } #[cfg(test)] mod test { - use super::{BlockCollection, SyncHeader}; - use ethcore::client::{TestBlockChainClient, EachBlockWith, BlockId, BlockChainClient}; - use types::BlockNumber; - use ethcore::verification::queue::kind::blocks::Unverified; - use rlp::*; - - fn is_empty(bc: &BlockCollection) -> bool { - bc.heads.is_empty() && - bc.blocks.is_empty() && - bc.parents.is_empty() && - bc.header_ids.is_empty() && - bc.head.is_none() && - bc.downloading_headers.is_empty() && - bc.downloading_bodies.is_empty() - } - - #[test] - fn create_clear() { - let mut bc = BlockCollection::new(false); - assert!(is_empty(&bc)); - let client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Nothing); - let hashes = (0 .. 100).map(|i| (&client as &BlockChainClient).block_hash(BlockId::Number(i)).unwrap()).collect(); - bc.reset_to(hashes); - assert!(!is_empty(&bc)); - bc.clear(); - assert!(is_empty(&bc)); - } - - #[test] - fn insert_headers() { - let mut bc = BlockCollection::new(false); - assert!(is_empty(&bc)); - let client = TestBlockChainClient::new(); - let nblocks = 200; - client.add_blocks(nblocks, EachBlockWith::Nothing); - let blocks: Vec<_> = (0..nblocks) - .map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap().into_inner()) - .collect(); - let headers: Vec<_> = blocks.iter().map(|b| SyncHeader::from_rlp(Rlp::new(b).at(0).unwrap().as_raw().to_vec()).unwrap()).collect(); - let hashes: Vec<_> = headers.iter().map(|h| h.header.hash()).collect(); - let heads: Vec<_> = hashes.iter().enumerate().filter_map(|(i, h)| if i % 20 == 0 { Some(*h) } else { None }).collect(); - bc.reset_to(heads); - assert!(!bc.is_empty()); - assert_eq!(hashes[0], bc.heads[0]); - assert!(bc.needed_bodies(1, false).is_empty()); - assert!(!bc.contains(&hashes[0])); - assert!(!bc.is_downloading(&hashes[0])); - - let (h, n) = bc.needed_headers(6, false).unwrap(); - assert!(bc.is_downloading(&hashes[0])); - assert_eq!(hashes[0], h); - assert_eq!(n, 6); - assert_eq!(bc.downloading_headers.len(), 1); - assert!(bc.drain().is_empty()); - - bc.insert_headers(headers[0..6].into_iter().map(Clone::clone).collect()); - assert_eq!(hashes[5], bc.heads[0]); - for h in &hashes[0..6] { - bc.clear_header_download(h) - } - assert_eq!(bc.downloading_headers.len(), 0); - assert!(!bc.is_downloading(&hashes[0])); - assert!(bc.contains(&hashes[0])); - - assert_eq!( - bc.drain().into_iter().map(|b| b.block).collect::>(), - blocks[0..6].iter().map(|b| Unverified::from_rlp(b.to_vec()).unwrap()).collect::>() - ); - assert!(!bc.contains(&hashes[0])); - assert_eq!(hashes[5], bc.head.unwrap()); - - let (h, _) = bc.needed_headers(6, false).unwrap(); - assert_eq!(hashes[5], h); - let (h, _) = bc.needed_headers(6, false).unwrap(); - assert_eq!(hashes[20], h); - bc.insert_headers(headers[10..16].into_iter().map(Clone::clone).collect()); - assert!(bc.drain().is_empty()); - bc.insert_headers(headers[5..10].into_iter().map(Clone::clone).collect()); - assert_eq!( - bc.drain().into_iter().map(|b| b.block).collect::>(), - blocks[6..16].iter().map(|b| Unverified::from_rlp(b.to_vec()).unwrap()).collect::>() - ); - - assert_eq!(hashes[15], bc.heads[0]); - - bc.insert_headers(headers[15..].into_iter().map(Clone::clone).collect()); - bc.drain(); - assert!(bc.is_empty()); - } - - #[test] - fn insert_headers_with_gap() { - let mut bc = BlockCollection::new(false); - assert!(is_empty(&bc)); - let client = TestBlockChainClient::new(); - let nblocks = 200; - client.add_blocks(nblocks, EachBlockWith::Nothing); - let blocks: Vec<_> = (0..nblocks) - .map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap().into_inner()) - .collect(); - let headers: Vec<_> = blocks.iter().map(|b| SyncHeader::from_rlp(Rlp::new(b).at(0).unwrap().as_raw().to_vec()).unwrap()).collect(); - let hashes: Vec<_> = headers.iter().map(|h| h.header.hash()).collect(); - let heads: Vec<_> = hashes.iter().enumerate().filter_map(|(i, h)| if i % 20 == 0 { Some(*h) } else { None }).collect(); - bc.reset_to(heads); - - bc.insert_headers(headers[2..22].into_iter().map(Clone::clone).collect()); - assert_eq!(hashes[0], bc.heads[0]); - assert_eq!(hashes[21], bc.heads[1]); - assert!(bc.head.is_none()); - bc.insert_headers(headers[0..2].into_iter().map(Clone::clone).collect()); - assert!(bc.head.is_some()); - assert_eq!(hashes[21], bc.heads[0]); - } - - #[test] - fn insert_headers_no_gap() { - let mut bc = BlockCollection::new(false); - assert!(is_empty(&bc)); - let client = TestBlockChainClient::new(); - let nblocks = 200; - client.add_blocks(nblocks, EachBlockWith::Nothing); - let blocks: Vec<_> = (0..nblocks) - .map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap().into_inner()) - .collect(); - let headers: Vec<_> = blocks.iter().map(|b| SyncHeader::from_rlp(Rlp::new(b).at(0).unwrap().as_raw().to_vec()).unwrap()).collect(); - let hashes: Vec<_> = headers.iter().map(|h| h.header.hash()).collect(); - let heads: Vec<_> = hashes.iter().enumerate().filter_map(|(i, h)| if i % 20 == 0 { Some(*h) } else { None }).collect(); - bc.reset_to(heads); - - bc.insert_headers(headers[1..2].into_iter().map(Clone::clone).collect()); - assert!(bc.drain().is_empty()); - bc.insert_headers(headers[0..1].into_iter().map(Clone::clone).collect()); - assert_eq!(bc.drain().len(), 2); - } + use super::{BlockCollection, SyncHeader}; + use ethcore::{ + client::{BlockChainClient, BlockId, EachBlockWith, TestBlockChainClient}, + verification::queue::kind::blocks::Unverified, + }; + use rlp::*; + use types::BlockNumber; + + fn is_empty(bc: &BlockCollection) -> bool { + bc.heads.is_empty() + && bc.blocks.is_empty() + && bc.parents.is_empty() + && bc.header_ids.is_empty() + && bc.head.is_none() + && bc.downloading_headers.is_empty() + && bc.downloading_bodies.is_empty() + } + + #[test] + fn create_clear() { + let mut bc = BlockCollection::new(false); + assert!(is_empty(&bc)); + let client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Nothing); + let hashes = (0..100) + .map(|i| { + (&client as &dyn BlockChainClient) + .block_hash(BlockId::Number(i)) + .unwrap() + }) + .collect(); + bc.reset_to(hashes); + assert!(!is_empty(&bc)); + bc.clear(); + assert!(is_empty(&bc)); + } + + #[test] + fn insert_headers() { + let mut bc = BlockCollection::new(false); + assert!(is_empty(&bc)); + let client = TestBlockChainClient::new(); + let nblocks = 200; + client.add_blocks(nblocks, EachBlockWith::Nothing); + let blocks: Vec<_> = (0..nblocks) + .map(|i| { + (&client as &dyn BlockChainClient) + .block(BlockId::Number(i as BlockNumber)) + .unwrap() + .into_inner() + }) + .collect(); + let headers: Vec<_> = blocks + .iter() + .map(|b| SyncHeader::from_rlp(Rlp::new(b).at(0).unwrap().as_raw().to_vec()).unwrap()) + .collect(); + let hashes: Vec<_> = headers.iter().map(|h| h.header.hash()).collect(); + let heads: Vec<_> = hashes + .iter() + .enumerate() + .filter_map(|(i, h)| if i % 20 == 0 { Some(*h) } else { None }) + .collect(); + bc.reset_to(heads); + assert!(!bc.is_empty()); + assert_eq!(hashes[0], bc.heads[0]); + assert!(bc.needed_bodies(1, false).is_empty()); + assert!(!bc.contains(&hashes[0])); + assert!(!bc.is_downloading(&hashes[0])); + + let (h, n) = bc.needed_headers(6, false).unwrap(); + assert!(bc.is_downloading(&hashes[0])); + assert_eq!(hashes[0], h); + assert_eq!(n, 6); + assert_eq!(bc.downloading_headers.len(), 1); + assert!(bc.drain().is_empty()); + + bc.insert_headers(headers[0..6].into_iter().map(Clone::clone).collect()); + assert_eq!(hashes[5], bc.heads[0]); + for h in &hashes[0..6] { + bc.clear_header_download(h) + } + assert_eq!(bc.downloading_headers.len(), 0); + assert!(!bc.is_downloading(&hashes[0])); + assert!(bc.contains(&hashes[0])); + + assert_eq!( + bc.drain().into_iter().map(|b| b.block).collect::>(), + blocks[0..6] + .iter() + .map(|b| Unverified::from_rlp(b.to_vec()).unwrap()) + .collect::>() + ); + assert!(!bc.contains(&hashes[0])); + assert_eq!(hashes[5], bc.head.unwrap()); + + let (h, _) = bc.needed_headers(6, false).unwrap(); + assert_eq!(hashes[5], h); + let (h, _) = bc.needed_headers(6, false).unwrap(); + assert_eq!(hashes[20], h); + bc.insert_headers(headers[10..16].into_iter().map(Clone::clone).collect()); + assert!(bc.drain().is_empty()); + bc.insert_headers(headers[5..10].into_iter().map(Clone::clone).collect()); + assert_eq!( + bc.drain().into_iter().map(|b| b.block).collect::>(), + blocks[6..16] + .iter() + .map(|b| Unverified::from_rlp(b.to_vec()).unwrap()) + .collect::>() + ); + + assert_eq!(hashes[15], bc.heads[0]); + + bc.insert_headers(headers[15..].into_iter().map(Clone::clone).collect()); + bc.drain(); + assert!(bc.is_empty()); + } + + #[test] + fn insert_headers_with_gap() { + let mut bc = BlockCollection::new(false); + assert!(is_empty(&bc)); + let client = TestBlockChainClient::new(); + let nblocks = 200; + client.add_blocks(nblocks, EachBlockWith::Nothing); + let blocks: Vec<_> = (0..nblocks) + .map(|i| { + (&client as &dyn BlockChainClient) + .block(BlockId::Number(i as BlockNumber)) + .unwrap() + .into_inner() + }) + .collect(); + let headers: Vec<_> = blocks + .iter() + .map(|b| SyncHeader::from_rlp(Rlp::new(b).at(0).unwrap().as_raw().to_vec()).unwrap()) + .collect(); + let hashes: Vec<_> = headers.iter().map(|h| h.header.hash()).collect(); + let heads: Vec<_> = hashes + .iter() + .enumerate() + .filter_map(|(i, h)| if i % 20 == 0 { Some(*h) } else { None }) + .collect(); + bc.reset_to(heads); + + bc.insert_headers(headers[2..22].into_iter().map(Clone::clone).collect()); + assert_eq!(hashes[0], bc.heads[0]); + assert_eq!(hashes[21], bc.heads[1]); + assert!(bc.head.is_none()); + bc.insert_headers(headers[0..2].into_iter().map(Clone::clone).collect()); + assert!(bc.head.is_some()); + assert_eq!(hashes[21], bc.heads[0]); + } + + #[test] + fn insert_headers_no_gap() { + let mut bc = BlockCollection::new(false); + assert!(is_empty(&bc)); + let client = TestBlockChainClient::new(); + let nblocks = 200; + client.add_blocks(nblocks, EachBlockWith::Nothing); + let blocks: Vec<_> = (0..nblocks) + .map(|i| { + (&client as &dyn BlockChainClient) + .block(BlockId::Number(i as BlockNumber)) + .unwrap() + .into_inner() + }) + .collect(); + let headers: Vec<_> = blocks + .iter() + .map(|b| SyncHeader::from_rlp(Rlp::new(b).at(0).unwrap().as_raw().to_vec()).unwrap()) + .collect(); + let hashes: Vec<_> = headers.iter().map(|h| h.header.hash()).collect(); + let heads: Vec<_> = hashes + .iter() + .enumerate() + .filter_map(|(i, h)| if i % 20 == 0 { Some(*h) } else { None }) + .collect(); + bc.reset_to(heads); + + bc.insert_headers(headers[1..2].into_iter().map(Clone::clone).collect()); + assert!(bc.drain().is_empty()); + bc.insert_headers(headers[0..1].into_iter().map(Clone::clone).collect()); + assert_eq!(bc.drain().len(), 2); + } } diff --git a/ethcore/sync/src/chain/fork_filter.rs b/ethcore/sync/src/chain/fork_filter.rs new file mode 100644 index 00000000000..b813cbff5fe --- /dev/null +++ b/ethcore/sync/src/chain/fork_filter.rs @@ -0,0 +1,114 @@ +//! This module contains a wrapper that connects this codebase with `ethereum-forkid` crate which provides `FORK_ID` +//! to support Ethereum network protocol, version 64 and above. + +// Re-export ethereum-forkid crate contents here. +pub use ethereum_forkid::{BlockNumber, ForkId, RejectReason}; + +use ethcore::client::ChainInfo; +use ethereum_forkid::ForkFilter; + +/// Wrapper around fork filter that provides integration with `ForkFilter`. +pub struct ForkFilterApi { + inner: ForkFilter, +} + +impl ForkFilterApi { + /// Create `ForkFilterApi` from `ChainInfo` and an `Iterator` over the hard forks. + pub fn new>( + client: &C, + forks: I, + ) -> Self { + let chain_info = client.chain_info(); + let genesis_hash = primitive_types07::H256::from_slice(&chain_info.genesis_hash.0); + Self { + inner: ForkFilter::new(chain_info.best_block_number, genesis_hash, forks), + } + } + + #[cfg(test)] + /// Dummy version of ForkFilterApi with no forks. + pub fn new_dummy(client: &C) -> Self { + let chain_info = client.chain_info(); + Self { + inner: ForkFilter::new( + chain_info.best_block_number, + primitive_types07::H256::from_slice(&chain_info.genesis_hash.0), + vec![], + ), + } + } + + fn update_head(&mut self, client: &C) { + self.inner.set_head(client.chain_info().best_block_number); + } + + /// Wrapper for `ForkFilter::current` + pub fn current(&mut self, client: &C) -> ForkId { + self.update_head(client); + self.inner.current() + } + + /// Wrapper for `ForkFilter::is_compatible` + pub fn is_compatible( + &mut self, + client: &C, + fork_id: ForkId, + ) -> Result<(), RejectReason> { + self.update_head(client); + self.inner.is_compatible(fork_id) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ethcore::{client::TestBlockChainClient, ethereum, spec::Spec}; + + fn test_spec Spec>(spec_builder: F, forks: Vec) { + let spec = (spec_builder)(); + let genesis_hash = spec.genesis_header().hash(); + let spec_forks = spec.hard_forks.clone(); + let client = TestBlockChainClient::new_with_spec(spec); + + assert_eq!( + ForkFilterApi::new(&client, spec_forks).inner, + ForkFilter::new( + 0, + primitive_types07::H256::from_slice(&genesis_hash.0), + forks + ) + ); + } + + #[test] + fn ethereum_spec() { + test_spec( + || ethereum::new_foundation(&String::new()), + vec![ + 1_150_000, 1_920_000, 2_463_000, 2_675_000, 4_370_000, 7_280_000, 9_069_000, + 9_200_000, + ], + ) + } + + #[test] + fn ropsten_spec() { + test_spec( + || ethereum::new_ropsten(&String::new()), + vec![10, 1_700_000, 4_230_000, 4_939_394, 6_485_846, 7_117_117], + ) + } + + #[test] + fn rinkeby_spec() { + test_spec( + || ethereum::new_rinkeby(&String::new()), + vec![1, 2, 3, 1_035_301, 3_660_663, 4_321_234, 5_435_345], + ) + } + + #[test] + fn goerli_spec() { + test_spec(|| ethereum::new_goerli(&String::new()), vec![1_561_651]) + } +} diff --git a/ethcore/sync/src/chain/handler.rs b/ethcore/sync/src/chain/handler.rs index 63ab8916139..12e0db97396 100644 --- a/ethcore/sync/src/chain/handler.rs +++ b/ethcore/sync/src/chain/handler.rs @@ -1,841 +1,946 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use api::WARP_SYNC_PROTOCOL_ID; +use api::{ETH_PROTOCOL, PAR_PROTOCOL}; use block_sync::{BlockDownloaderImportError as DownloaderImportError, DownloadAction}; use bytes::Bytes; use enum_primitive::FromPrimitive; -use ethcore::error::{Error as EthcoreError, ErrorKind as EthcoreErrorKind, ImportErrorKind, BlockError}; -use ethcore::snapshot::{ManifestData, RestorationStatus}; -use ethcore::verification::queue::kind::blocks::Unverified; +use ethcore::{ + error::{BlockError, Error as EthcoreError, ErrorKind as EthcoreErrorKind, ImportErrorKind}, + snapshot::{ManifestData, RestorationStatus}, + verification::queue::kind::blocks::Unverified, +}; use ethereum_types::{H256, U256}; use hash::keccak; -use network::PeerId; -use network::client_version::ClientVersion; +use network::{client_version::ClientVersion, PeerId}; use rlp::Rlp; use snapshot::ChunkType; -use std::time::Instant; -use std::{mem, cmp}; +use std::{cmp, mem, time::Instant}; use sync_io::SyncIo; -use types::BlockNumber; -use types::block_status::BlockStatus; -use types::ids::BlockId; - -use super::sync_packet::{PacketInfo, SyncPacket}; -use super::sync_packet::SyncPacket::{ - StatusPacket, - NewBlockHashesPacket, - BlockHeadersPacket, - BlockBodiesPacket, - NewBlockPacket, - ReceiptsPacket, - SnapshotManifestPacket, - SnapshotDataPacket, - PrivateTransactionPacket, - SignedPrivateTransactionPacket, +use types::{block_status::BlockStatus, ids::BlockId, BlockNumber}; + +use super::sync_packet::{ + PacketInfo, SyncPacket, + SyncPacket::{ + BlockBodiesPacket, BlockHeadersPacket, NewBlockHashesPacket, NewBlockPacket, + ReceiptsPacket, SnapshotDataPacket, SnapshotManifestPacket, StatusPacket, + }, }; use super::{ - BlockSet, - ChainSync, - ForkConfirmation, - PacketDecodeError, - PeerAsking, - PeerInfo, - SyncRequester, - SyncState, - ETH_PROTOCOL_VERSION_62, - ETH_PROTOCOL_VERSION_63, - MAX_NEW_BLOCK_AGE, - MAX_NEW_HASHES, - PAR_PROTOCOL_VERSION_1, - PAR_PROTOCOL_VERSION_3, + BlockSet, ChainSync, ForkConfirmation, PacketProcessError, PeerAsking, PeerInfo, SyncRequester, + SyncState, ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_64, MAX_NEW_BLOCK_AGE, MAX_NEW_HASHES, + PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, }; /// The Chain Sync Handler: handles responses from peers pub struct SyncHandler; impl SyncHandler { - /// Handle incoming packet from peer - pub fn on_packet(sync: &mut ChainSync, io: &mut SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) { - let rlp = Rlp::new(data); - if let Some(packet_id) = SyncPacket::from_u8(packet_id) { - let result = match packet_id { - StatusPacket => SyncHandler::on_peer_status(sync, io, peer, &rlp), - BlockHeadersPacket => SyncHandler::on_peer_block_headers(sync, io, peer, &rlp), - BlockBodiesPacket => SyncHandler::on_peer_block_bodies(sync, io, peer, &rlp), - ReceiptsPacket => SyncHandler::on_peer_block_receipts(sync, io, peer, &rlp), - NewBlockPacket => SyncHandler::on_peer_new_block(sync, io, peer, &rlp), - NewBlockHashesPacket => SyncHandler::on_peer_new_hashes(sync, io, peer, &rlp), - SnapshotManifestPacket => SyncHandler::on_snapshot_manifest(sync, io, peer, &rlp), - SnapshotDataPacket => SyncHandler::on_snapshot_data(sync, io, peer, &rlp), - PrivateTransactionPacket => SyncHandler::on_private_transaction(sync, io, peer, &rlp), - SignedPrivateTransactionPacket => SyncHandler::on_signed_private_transaction(sync, io, peer, &rlp), - _ => { - debug!(target: "sync", "{}: Unknown packet {}", peer, packet_id.id()); - Ok(()) - } - }; - - match result { - Err(DownloaderImportError::Invalid) => { - debug!(target:"sync", "{} -> Invalid packet {}", peer, packet_id.id()); - io.disable_peer(peer); - sync.deactivate_peer(io, peer); - }, - Err(DownloaderImportError::Useless) => { - sync.deactivate_peer(io, peer); - }, - Ok(()) => { - // give a task to the same peer first - sync.sync_peer(io, peer, false); - }, - } - } else { - debug!(target: "sync", "{}: Unknown packet {}", peer, packet_id); - } - } - - /// Called when peer sends us new consensus packet - pub fn on_consensus_packet(io: &mut SyncIo, peer_id: PeerId, r: &Rlp) { - trace!(target: "sync", "Received consensus packet from {:?}", peer_id); - io.chain().queue_consensus_message(r.as_raw().to_vec()); - } - - /// Called by peer when it is disconnecting - pub fn on_peer_aborting(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId) { - trace!(target: "sync", "== Disconnecting {}: {}", peer_id, io.peer_version(peer_id)); - sync.handshaking_peers.remove(&peer_id); - if sync.peers.contains_key(&peer_id) { - debug!(target: "sync", "Disconnected {}", peer_id); - sync.clear_peer_download(peer_id); - sync.peers.remove(&peer_id); - sync.active_peers.remove(&peer_id); - - if sync.state == SyncState::SnapshotManifest { - // Check if we are asking other peers for - // the snapshot manifest as well. - // If not, return to initial state - let still_asking_manifest = sync.peers.iter() - .filter(|&(id, p)| sync.active_peers.contains(id) && p.asking == PeerAsking::SnapshotManifest) - .next().is_none(); - - if still_asking_manifest { - sync.state = ChainSync::get_init_state(sync.warp_sync, io.chain()); - } - } - sync.continue_sync(io); - } - } - - /// Called when a new peer is connected - pub fn on_peer_connected(sync: &mut ChainSync, io: &mut SyncIo, peer: PeerId) { - trace!(target: "sync", "== Connected {}: {}", peer, io.peer_version(peer)); - if let Err(e) = sync.send_status(io, peer) { - debug!(target:"sync", "Error sending status request: {:?}", e); - io.disconnect_peer(peer); - } else { - sync.handshaking_peers.insert(peer, Instant::now()); - } - } - - /// Called by peer once it has new block bodies - pub fn on_peer_new_block(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { - if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { - trace!(target: "sync", "Ignoring new block from unconfirmed peer {}", peer_id); - return Ok(()); - } - let difficulty: U256 = r.val_at(1)?; - if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) { - if peer.difficulty.map_or(true, |pd| difficulty > pd) { - peer.difficulty = Some(difficulty); - } - } - let block = Unverified::from_rlp(r.at(0)?.as_raw().to_vec())?; - let hash = block.header.hash(); - let number = block.header.number(); - trace!(target: "sync", "{} -> NewBlock ({})", peer_id, hash); - if number > sync.highest_block.unwrap_or(0) { - sync.highest_block = Some(number); - } - let mut unknown = false; - - if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) { - peer.latest_hash = hash; - } - - let last_imported_number = sync.new_blocks.last_imported_block_number(); - if last_imported_number > number && last_imported_number - number > MAX_NEW_BLOCK_AGE { - trace!(target: "sync", "Ignored ancient new block {:?}", hash); - return Err(DownloaderImportError::Invalid); - } - match io.chain().import_block(block) { - Err(EthcoreError(EthcoreErrorKind::Import(ImportErrorKind::AlreadyInChain), _)) => { - trace!(target: "sync", "New block already in chain {:?}", hash); - }, - Err(EthcoreError(EthcoreErrorKind::Import(ImportErrorKind::AlreadyQueued), _)) => { - trace!(target: "sync", "New block already queued {:?}", hash); - }, - Ok(_) => { - // abort current download of the same block - sync.complete_sync(io); - sync.new_blocks.mark_as_known(&hash, number); - trace!(target: "sync", "New block queued {:?} ({})", hash, number); - }, - Err(EthcoreError(EthcoreErrorKind::Block(BlockError::UnknownParent(p)), _)) => { - unknown = true; - trace!(target: "sync", "New block with unknown parent ({:?}) {:?}", p, hash); - }, - Err(e) => { - debug!(target: "sync", "Bad new block {:?} : {:?}", hash, e); - return Err(DownloaderImportError::Invalid); - } - }; - if unknown { - if sync.state != SyncState::Idle { - trace!(target: "sync", "NewBlock ignored while seeking"); - } else { - trace!(target: "sync", "New unknown block {:?}", hash); - //TODO: handle too many unknown blocks - sync.sync_peer(io, peer_id, true); - } - } - Ok(()) - } - - /// Handles `NewHashes` packet. Initiates headers download for any unknown hashes. - pub fn on_peer_new_hashes(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { - if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { - trace!(target: "sync", "Ignoring new hashes from unconfirmed peer {}", peer_id); - return Ok(()); - } - let hashes: Vec<_> = r.iter().take(MAX_NEW_HASHES).map(|item| (item.val_at::(0), item.val_at::(1))).collect(); - if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) { - // Peer has new blocks with unknown difficulty - peer.difficulty = None; - if let Some(&(Ok(ref h), _)) = hashes.last() { - peer.latest_hash = h.clone(); - } - } - if sync.state != SyncState::Idle { - trace!(target: "sync", "Ignoring new hashes since we're already downloading."); - let max = r.iter().take(MAX_NEW_HASHES).map(|item| item.val_at::(1).unwrap_or(0)).fold(0u64, cmp::max); - if max > sync.highest_block.unwrap_or(0) { - sync.highest_block = Some(max); - } - return Ok(()); - } - trace!(target: "sync", "{} -> NewHashes ({} entries)", peer_id, r.item_count()?); - let mut max_height: BlockNumber = 0; - let mut new_hashes = Vec::new(); - let last_imported_number = sync.new_blocks.last_imported_block_number(); - for (rh, rn) in hashes { - let hash = rh?; - let number = rn?; - if number > sync.highest_block.unwrap_or(0) { - sync.highest_block = Some(number); - } - if sync.new_blocks.is_downloading(&hash) { - continue; - } - if last_imported_number > number && last_imported_number - number > MAX_NEW_BLOCK_AGE { - trace!(target: "sync", "Ignored ancient new block hash {:?}", hash); - return Err(DownloaderImportError::Invalid); - } - match io.chain().block_status(BlockId::Hash(hash.clone())) { - BlockStatus::InChain => { - trace!(target: "sync", "New block hash already in chain {:?}", hash); - }, - BlockStatus::Queued => { - trace!(target: "sync", "New hash block already queued {:?}", hash); - }, - BlockStatus::Unknown => { - new_hashes.push(hash.clone()); - if number > max_height { - trace!(target: "sync", "New unknown block hash {:?}", hash); - if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) { - peer.latest_hash = hash.clone(); - } - max_height = number; - } - }, - BlockStatus::Bad => { - debug!(target: "sync", "Bad new block hash {:?}", hash); - return Err(DownloaderImportError::Invalid); - } - } - }; - if max_height != 0 { - trace!(target: "sync", "Downloading blocks for new hashes"); - sync.new_blocks.reset_to(new_hashes); - sync.state = SyncState::NewBlocks; - sync.sync_peer(io, peer_id, true); - } - Ok(()) - } - - /// Called by peer once it has new block bodies - fn on_peer_block_bodies(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { - sync.clear_peer_download(peer_id); - let block_set = sync.peers.get(&peer_id) - .and_then(|p| p.block_set) - .unwrap_or(BlockSet::NewBlocks); - let allowed = sync.peers.get(&peer_id).map(|p| p.is_allowed()).unwrap_or(false); - - if !sync.reset_peer_asking(peer_id, PeerAsking::BlockBodies) || !allowed { - trace!(target: "sync", "{}: Ignored unexpected bodies", peer_id); - return Ok(()); - } - let expected_blocks = match sync.peers.get_mut(&peer_id) { - Some(peer) => mem::replace(&mut peer.asking_blocks, Vec::new()), - None => { - trace!(target: "sync", "{}: Ignored unexpected bodies (peer not found)", peer_id); - return Ok(()); - } - }; - let item_count = r.item_count()?; - trace!(target: "sync", "{} -> BlockBodies ({} entries), set = {:?}", peer_id, item_count, block_set); - if item_count == 0 { - Err(DownloaderImportError::Useless) - } else if sync.state == SyncState::Waiting { - trace!(target: "sync", "Ignored block bodies while waiting"); - Ok(()) - } else { - { - let downloader = match block_set { - BlockSet::NewBlocks => &mut sync.new_blocks, - BlockSet::OldBlocks => match sync.old_blocks { - None => { - trace!(target: "sync", "Ignored block headers while block download is inactive"); - return Ok(()); - }, - Some(ref mut blocks) => blocks, - } - }; - downloader.import_bodies(r, expected_blocks.as_slice())?; - } - sync.collect_blocks(io, block_set); - Ok(()) - } - } - - fn on_peer_fork_header(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { - { - let peer = sync.peers.get_mut(&peer_id).expect("Is only called when peer is present in peers"); - peer.asking = PeerAsking::Nothing; - let item_count = r.item_count()?; - let (fork_number, fork_hash) = sync.fork_block.expect("ForkHeader request is sent only fork block is Some; qed").clone(); - - if item_count == 0 || item_count != 1 { - trace!(target: "sync", "{}: Chain is too short to confirm the block", peer_id); - peer.confirmation = ForkConfirmation::TooShort; - - } else { - let header = r.at(0)?.as_raw(); - if keccak(&header) != fork_hash { - trace!(target: "sync", "{}: Fork mismatch", peer_id); - return Err(DownloaderImportError::Invalid); - } - - trace!(target: "sync", "{}: Confirmed peer", peer_id); - peer.confirmation = ForkConfirmation::Confirmed; - - if !io.chain_overlay().read().contains_key(&fork_number) { - trace!(target: "sync", "Inserting (fork) block {} header", fork_number); - io.chain_overlay().write().insert(fork_number, header.to_vec()); - } - } - } - - return Ok(()); - } - - /// Called by peer once it has new block headers during sync - fn on_peer_block_headers(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { - let is_fork_header_request = match sync.peers.get(&peer_id) { - Some(peer) if peer.asking == PeerAsking::ForkHeader => true, - _ => false, - }; - - if is_fork_header_request { - return SyncHandler::on_peer_fork_header(sync, io, peer_id, r); - } - - sync.clear_peer_download(peer_id); - let expected_hash = sync.peers.get(&peer_id).and_then(|p| p.asking_hash); - let allowed = sync.peers.get(&peer_id).map(|p| p.is_allowed()).unwrap_or(false); - let block_set = sync.peers.get(&peer_id).and_then(|p| p.block_set).unwrap_or(BlockSet::NewBlocks); - - if !sync.reset_peer_asking(peer_id, PeerAsking::BlockHeaders) { - debug!(target: "sync", "{}: Ignored unexpected headers", peer_id); - return Ok(()); - } - let expected_hash = match expected_hash { - Some(hash) => hash, - None => { - debug!(target: "sync", "{}: Ignored unexpected headers (expected_hash is None)", peer_id); - return Ok(()); - } - }; - if !allowed { - debug!(target: "sync", "{}: Ignored unexpected headers (peer not allowed)", peer_id); - return Ok(()); - } - - let item_count = r.item_count()?; - trace!(target: "sync", "{} -> BlockHeaders ({} entries), state = {:?}, set = {:?}", peer_id, item_count, sync.state, block_set); - if (sync.state == SyncState::Idle || sync.state == SyncState::WaitingPeers) && sync.old_blocks.is_none() { - trace!(target: "sync", "Ignored unexpected block headers"); - return Ok(()); - } - if sync.state == SyncState::Waiting { - trace!(target: "sync", "Ignored block headers while waiting"); - return Ok(()); - } - - let result = { - let downloader = match block_set { - BlockSet::NewBlocks => &mut sync.new_blocks, - BlockSet::OldBlocks => { - match sync.old_blocks { - None => { - trace!(target: "sync", "Ignored block headers while block download is inactive"); - return Ok(()); - }, - Some(ref mut blocks) => blocks, - } - } - }; - downloader.import_headers(io, r, expected_hash)? - }; - - if result == DownloadAction::Reset { - sync.reset_downloads(block_set); - } - - sync.collect_blocks(io, block_set); - Ok(()) - } - - /// Called by peer once it has new block receipts - fn on_peer_block_receipts(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { - sync.clear_peer_download(peer_id); - let block_set = sync.peers.get(&peer_id).and_then(|p| p.block_set).unwrap_or(BlockSet::NewBlocks); - let allowed = sync.peers.get(&peer_id).map(|p| p.is_allowed()).unwrap_or(false); - if !sync.reset_peer_asking(peer_id, PeerAsking::BlockReceipts) || !allowed { - trace!(target: "sync", "{}: Ignored unexpected receipts", peer_id); - return Ok(()); - } - let expected_blocks = match sync.peers.get_mut(&peer_id) { - Some(peer) => mem::replace(&mut peer.asking_blocks, Vec::new()), - None => { - trace!(target: "sync", "{}: Ignored unexpected bodies (peer not found)", peer_id); - return Ok(()); - } - }; - let item_count = r.item_count()?; - trace!(target: "sync", "{} -> BlockReceipts ({} entries)", peer_id, item_count); - if item_count == 0 { - Err(DownloaderImportError::Useless) - } else if sync.state == SyncState::Waiting { - trace!(target: "sync", "Ignored block receipts while waiting"); - Ok(()) - } else { - { - let downloader = match block_set { - BlockSet::NewBlocks => &mut sync.new_blocks, - BlockSet::OldBlocks => match sync.old_blocks { - None => { - trace!(target: "sync", "Ignored block headers while block download is inactive"); - return Ok(()); - }, - Some(ref mut blocks) => blocks, - } - }; - downloader.import_receipts(r, expected_blocks.as_slice())?; - } - sync.collect_blocks(io, block_set); - Ok(()) - } - } - - /// Called when snapshot manifest is downloaded from a peer. - fn on_snapshot_manifest(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { - if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { - trace!(target: "sync", "Ignoring snapshot manifest from unconfirmed peer {}", peer_id); - return Ok(()); - } - sync.clear_peer_download(peer_id); - if !sync.reset_peer_asking(peer_id, PeerAsking::SnapshotManifest) || sync.state != SyncState::SnapshotManifest { - trace!(target: "sync", "{}: Ignored unexpected/expired manifest", peer_id); - return Ok(()); - } - - let manifest_rlp = r.at(0)?; - let manifest = ManifestData::from_rlp(manifest_rlp.as_raw())?; - - let is_supported_version = io.snapshot_service().supported_versions() - .map_or(false, |(l, h)| manifest.version >= l && manifest.version <= h); - - if !is_supported_version { - trace!(target: "sync", "{}: Snapshot manifest version not supported: {}", peer_id, manifest.version); - return Err(DownloaderImportError::Invalid); - } - sync.snapshot.reset_to(&manifest, &keccak(manifest_rlp.as_raw())); - io.snapshot_service().begin_restore(manifest); - sync.state = SyncState::SnapshotData; - - Ok(()) - } - - /// Called when snapshot data is downloaded from a peer. - fn on_snapshot_data(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { - if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { - trace!(target: "sync", "Ignoring snapshot data from unconfirmed peer {}", peer_id); - return Ok(()); - } - sync.clear_peer_download(peer_id); - if !sync.reset_peer_asking(peer_id, PeerAsking::SnapshotData) || (sync.state != SyncState::SnapshotData && sync.state != SyncState::SnapshotWaiting) { - trace!(target: "sync", "{}: Ignored unexpected snapshot data", peer_id); - return Ok(()); - } - - // check service status - let status = io.snapshot_service().status(); - match status { - RestorationStatus::Inactive | RestorationStatus::Failed => { - trace!(target: "sync", "{}: Snapshot restoration aborted", peer_id); - sync.state = SyncState::WaitingPeers; - - // only note bad if restoration failed. - if let (Some(hash), RestorationStatus::Failed) = (sync.snapshot.snapshot_hash(), status) { - trace!(target: "sync", "Noting snapshot hash {} as bad", hash); - sync.snapshot.note_bad(hash); - } - - sync.snapshot.clear(); - return Ok(()); - }, - RestorationStatus::Initializing { .. } => { - trace!(target: "warp", "{}: Snapshot restoration is initializing", peer_id); - return Ok(()); - } - RestorationStatus::Ongoing { .. } => { - trace!(target: "sync", "{}: Snapshot restoration is ongoing", peer_id); - }, - } - - let snapshot_data: Bytes = r.val_at(0)?; - match sync.snapshot.validate_chunk(&snapshot_data) { - Ok(ChunkType::Block(hash)) => { - trace!(target: "sync", "{}: Processing block chunk", peer_id); - io.snapshot_service().restore_block_chunk(hash, snapshot_data); - } - Ok(ChunkType::State(hash)) => { - trace!(target: "sync", "{}: Processing state chunk", peer_id); - io.snapshot_service().restore_state_chunk(hash, snapshot_data); - } - Err(()) => { - trace!(target: "sync", "{}: Got bad snapshot chunk", peer_id); - io.disconnect_peer(peer_id); - return Ok(()); - } - } - - if sync.snapshot.is_complete() { - // wait for snapshot restoration process to complete - sync.state = SyncState::SnapshotWaiting; - } - - Ok(()) - } - - /// Called by peer to report status - fn on_peer_status(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { - sync.handshaking_peers.remove(&peer_id); - let protocol_version: u8 = r.val_at(0)?; - let warp_protocol_version = io.protocol_version(&WARP_SYNC_PROTOCOL_ID, peer_id); - let warp_protocol = warp_protocol_version != 0; - let private_tx_protocol = warp_protocol_version >= PAR_PROTOCOL_VERSION_3.0; - let peer = PeerInfo { - protocol_version: protocol_version, - network_id: r.val_at(1)?, - difficulty: Some(r.val_at(2)?), - latest_hash: r.val_at(3)?, - genesis: r.val_at(4)?, - asking: PeerAsking::Nothing, - asking_blocks: Vec::new(), - asking_hash: None, - ask_time: Instant::now(), - last_sent_transactions: Default::default(), - last_sent_private_transactions: Default::default(), - expired: false, - confirmation: if sync.fork_block.is_none() { ForkConfirmation::Confirmed } else { ForkConfirmation::Unconfirmed }, - asking_snapshot_data: None, - snapshot_hash: if warp_protocol { Some(r.val_at(5)?) } else { None }, - snapshot_number: if warp_protocol { Some(r.val_at(6)?) } else { None }, - block_set: None, - private_tx_enabled: if private_tx_protocol { r.val_at(7).unwrap_or(false) } else { false }, - client_version: ClientVersion::from(io.peer_version(peer_id)), - }; - - trace!(target: "sync", "New peer {} (\ - protocol: {}, \ - network: {:?}, \ - difficulty: {:?}, \ - latest:{}, \ - genesis:{}, \ - snapshot:{:?}, \ - private_tx_enabled:{})", - peer_id, - peer.protocol_version, - peer.network_id, - peer.difficulty, - peer.latest_hash, - peer.genesis, - peer.snapshot_number, - peer.private_tx_enabled - ); - if io.is_expired() { - trace!(target: "sync", "Status packet from expired session {}:{}", peer_id, io.peer_version(peer_id)); - return Ok(()); - } - - if sync.peers.contains_key(&peer_id) { - debug!(target: "sync", "Unexpected status packet from {}:{}", peer_id, io.peer_version(peer_id)); - return Ok(()); - } - let chain_info = io.chain().chain_info(); - if peer.genesis != chain_info.genesis_hash { - trace!(target: "sync", "Peer {} genesis hash mismatch (ours: {}, theirs: {})", peer_id, chain_info.genesis_hash, peer.genesis); - return Err(DownloaderImportError::Invalid); - } - if peer.network_id != sync.network_id { - trace!(target: "sync", "Peer {} network id mismatch (ours: {}, theirs: {})", peer_id, sync.network_id, peer.network_id); - return Err(DownloaderImportError::Invalid); - } - - if false - || (warp_protocol && (peer.protocol_version < PAR_PROTOCOL_VERSION_1.0 || peer.protocol_version > PAR_PROTOCOL_VERSION_3.0)) - || (!warp_protocol && (peer.protocol_version < ETH_PROTOCOL_VERSION_62.0 || peer.protocol_version > ETH_PROTOCOL_VERSION_63.0)) - { - trace!(target: "sync", "Peer {} unsupported eth protocol ({})", peer_id, peer.protocol_version); - return Err(DownloaderImportError::Invalid); - } - - if sync.sync_start_time.is_none() { - sync.sync_start_time = Some(Instant::now()); - } - - sync.peers.insert(peer_id.clone(), peer); - // Don't activate peer immediatelly when searching for common block. - // Let the current sync round complete first. - sync.active_peers.insert(peer_id.clone()); - debug!(target: "sync", "Connected {}:{}", peer_id, io.peer_version(peer_id)); - - if let Some((fork_block, _)) = sync.fork_block { - SyncRequester::request_fork_header(sync, io, peer_id, fork_block); - } - - Ok(()) - } - - /// Called when peer sends us new transactions - pub fn on_peer_transactions(sync: &ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), PacketDecodeError> { - // Accept transactions only when fully synced - if !io.is_chain_queue_empty() || (sync.state != SyncState::Idle && sync.state != SyncState::NewBlocks) { - trace!(target: "sync", "{} Ignoring transactions while syncing", peer_id); - return Ok(()); - } - if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { - trace!(target: "sync", "{} Ignoring transactions from unconfirmed/unknown peer", peer_id); - return Ok(()); - } - - let item_count = r.item_count()?; - trace!(target: "sync", "{:02} -> Transactions ({} entries)", peer_id, item_count); - let mut transactions = Vec::with_capacity(item_count); - for i in 0 .. item_count { - let rlp = r.at(i)?; - let tx = rlp.as_raw().to_vec(); - transactions.push(tx); - } - io.chain().queue_transactions(transactions, peer_id); - Ok(()) - } - - /// Called when peer sends us signed private transaction packet - fn on_signed_private_transaction(sync: &mut ChainSync, _io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { - if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { - trace!(target: "sync", "{} Ignoring packet from unconfirmed/unknown peer", peer_id); - return Ok(()); - } - let private_handler = match sync.private_tx_handler { - Some(ref handler) => handler, - None => { - trace!(target: "sync", "{} Ignoring private tx packet from peer", peer_id); - return Ok(()); - } - }; - trace!(target: "sync", "Received signed private transaction packet from {:?}", peer_id); - match private_handler.import_signed_private_transaction(r.as_raw()) { - Ok(transaction_hash) => { - //don't send the packet back - if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) { - peer.last_sent_private_transactions.insert(transaction_hash); - } - }, - Err(e) => { - trace!(target: "sync", "Ignoring the message, error queueing: {}", e); - } - } - Ok(()) - } - - /// Called when peer sends us new private transaction packet - fn on_private_transaction(sync: &mut ChainSync, _io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { - if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { - trace!(target: "sync", "{} Ignoring packet from unconfirmed/unknown peer", peer_id); - return Ok(()); - } - let private_handler = match sync.private_tx_handler { - Some(ref handler) => handler, - None => { - trace!(target: "sync", "{} Ignoring private tx packet from peer", peer_id); - return Ok(()); - } - }; - trace!(target: "sync", "Received private transaction packet from {:?}", peer_id); - match private_handler.import_private_transaction(r.as_raw()) { - Ok(transaction_hash) => { - //don't send the packet back - if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) { - peer.last_sent_private_transactions.insert(transaction_hash); - } - }, - Err(e) => { - trace!(target: "sync", "Ignoring the message, error queueing: {}", e); - } - } - Ok(()) - } + /// Handle incoming packet from peer + pub fn on_packet( + sync: &mut ChainSync, + io: &mut dyn SyncIo, + peer: PeerId, + packet_id: u8, + data: &[u8], + ) { + let rlp = Rlp::new(data); + if let Some(packet_id) = SyncPacket::from_u8(packet_id) { + let result = match packet_id { + StatusPacket => SyncHandler::on_peer_status(sync, io, peer, &rlp), + BlockHeadersPacket => SyncHandler::on_peer_block_headers(sync, io, peer, &rlp), + BlockBodiesPacket => SyncHandler::on_peer_block_bodies(sync, io, peer, &rlp), + ReceiptsPacket => SyncHandler::on_peer_block_receipts(sync, io, peer, &rlp), + NewBlockPacket => SyncHandler::on_peer_new_block(sync, io, peer, &rlp), + NewBlockHashesPacket => SyncHandler::on_peer_new_hashes(sync, io, peer, &rlp), + SnapshotManifestPacket => SyncHandler::on_snapshot_manifest(sync, io, peer, &rlp), + SnapshotDataPacket => SyncHandler::on_snapshot_data(sync, io, peer, &rlp), + _ => { + debug!(target: "sync", "{}: Unknown packet {}", peer, packet_id.id()); + Ok(()) + } + }; + + match result { + Err(DownloaderImportError::Invalid) => { + debug!(target:"sync", "{} -> Invalid packet {}", peer, packet_id.id()); + io.disable_peer(peer); + sync.deactivate_peer(io, peer); + } + Err(DownloaderImportError::Useless) => { + sync.deactivate_peer(io, peer); + } + Ok(()) => { + // give a task to the same peer first + sync.sync_peer(io, peer, false); + } + } + } else { + debug!(target: "sync", "{}: Unknown packet {}", peer, packet_id); + } + } + + /// Called when peer sends us new consensus packet + pub fn on_consensus_packet(io: &mut dyn SyncIo, peer_id: PeerId, r: &Rlp) { + trace!(target: "sync", "Received consensus packet from {:?}", peer_id); + io.chain().queue_consensus_message(r.as_raw().to_vec()); + } + + /// Called by peer when it is disconnecting + pub fn on_peer_aborting(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId) { + trace!(target: "sync", "== Disconnecting {}: {}", peer_id, io.peer_version(peer_id)); + sync.handshaking_peers.remove(&peer_id); + if sync.peers.contains_key(&peer_id) { + debug!(target: "sync", "Disconnected {}", peer_id); + sync.clear_peer_download(peer_id); + sync.peers.remove(&peer_id); + sync.delayed_requests + .retain(|(request_peer_id, _, _)| *request_peer_id != peer_id); + sync.active_peers.remove(&peer_id); + + if sync.state == SyncState::SnapshotManifest { + // Check if we are asking other peers for + // the snapshot manifest as well. + // If not, return to initial state + let still_asking_manifest = sync + .peers + .iter() + .filter(|&(id, p)| { + sync.active_peers.contains(id) && p.asking == PeerAsking::SnapshotManifest + }) + .next() + .is_none(); + + if still_asking_manifest { + sync.state = ChainSync::get_init_state(sync.warp_sync, io.chain()); + } + } + sync.continue_sync(io); + } + } + + /// Called when a new peer is connected + pub fn on_peer_connected(sync: &mut ChainSync, io: &mut dyn SyncIo, peer: PeerId) { + trace!(target: "sync", "== Connected {}: {}", peer, io.peer_version(peer)); + if let Err(e) = sync.send_status(io, peer) { + debug!(target:"sync", "Error sending status request: {:?}", e); + io.disconnect_peer(peer); + } else { + sync.handshaking_peers.insert(peer, Instant::now()); + } + } + + /// Called by peer once it has new block bodies + pub fn on_peer_new_block( + sync: &mut ChainSync, + io: &mut dyn SyncIo, + peer_id: PeerId, + r: &Rlp, + ) -> Result<(), DownloaderImportError> { + if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { + trace!(target: "sync", "Ignoring new block from unconfirmed peer {}", peer_id); + return Ok(()); + } + let block = Unverified::from_rlp(r.at(0)?.as_raw().to_vec())?; + let hash = block.header.hash(); + let number = block.header.number(); + trace!(target: "sync", "{} -> NewBlock ({})", peer_id, hash); + if number > sync.highest_block.unwrap_or(0) { + sync.highest_block = Some(number); + } + let parent_hash = block.header.parent_hash(); + let difficulty: U256 = r.val_at(1)?; + // Most probably the sent block is being imported by peer right now + // Use td and hash, that peer must have for now + let parent_td = difficulty.checked_sub(*block.header.difficulty()); + if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) { + if peer + .difficulty + .map_or(true, |pd| parent_td.map_or(false, |td| td > pd)) + { + peer.difficulty = parent_td; + } + } + let mut unknown = false; + + if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) { + peer.latest_hash = *parent_hash; + } + + let last_imported_number = sync.new_blocks.last_imported_block_number(); + if last_imported_number > number && last_imported_number - number > MAX_NEW_BLOCK_AGE { + trace!(target: "sync", "Ignored ancient new block {:?}", hash); + return Err(DownloaderImportError::Invalid); + } + match io.chain().import_block(block) { + Err(EthcoreError(EthcoreErrorKind::Import(ImportErrorKind::AlreadyInChain), _)) => { + trace!(target: "sync", "New block already in chain {:?}", hash); + } + Err(EthcoreError(EthcoreErrorKind::Import(ImportErrorKind::AlreadyQueued), _)) => { + trace!(target: "sync", "New block already queued {:?}", hash); + } + Ok(_) => { + // abort current download of the same block + sync.complete_sync(io); + sync.new_blocks.mark_as_known(&hash, number); + trace!(target: "sync", "New block queued {:?} ({})", hash, number); + } + Err(EthcoreError(EthcoreErrorKind::Block(BlockError::UnknownParent(p)), _)) => { + unknown = true; + trace!(target: "sync", "New block with unknown parent ({:?}) {:?}", p, hash); + } + Err(e) => { + debug!(target: "sync", "Bad new block {:?} : {:?}", hash, e); + return Err(DownloaderImportError::Invalid); + } + }; + if unknown { + if sync.state != SyncState::Idle { + trace!(target: "sync", "NewBlock ignored while seeking"); + } else { + trace!(target: "sync", "New unknown block {:?}", hash); + //TODO: handle too many unknown blocks + sync.sync_peer(io, peer_id, true); + } + } + Ok(()) + } + + /// Handles `NewHashes` packet. Initiates headers download for any unknown hashes. + pub fn on_peer_new_hashes( + sync: &mut ChainSync, + io: &mut dyn SyncIo, + peer_id: PeerId, + r: &Rlp, + ) -> Result<(), DownloaderImportError> { + if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { + trace!(target: "sync", "Ignoring new hashes from unconfirmed peer {}", peer_id); + return Ok(()); + } + let hashes: Vec<_> = r + .iter() + .take(MAX_NEW_HASHES) + .map(|item| (item.val_at::(0), item.val_at::(1))) + .collect(); + if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) { + // Peer has new blocks with unknown difficulty + peer.difficulty = None; + if let Some(&(Ok(ref h), _)) = hashes.last() { + peer.latest_hash = h.clone(); + } + } + if sync.state != SyncState::Idle { + trace!(target: "sync", "Ignoring new hashes since we're already downloading."); + let max = r + .iter() + .take(MAX_NEW_HASHES) + .map(|item| item.val_at::(1).unwrap_or(0)) + .fold(0u64, cmp::max); + if max > sync.highest_block.unwrap_or(0) { + sync.highest_block = Some(max); + } + return Ok(()); + } + trace!(target: "sync", "{} -> NewHashes ({} entries)", peer_id, r.item_count()?); + let mut max_height: BlockNumber = 0; + let mut new_hashes = Vec::new(); + let last_imported_number = sync.new_blocks.last_imported_block_number(); + for (rh, rn) in hashes { + let hash = rh?; + let number = rn?; + if number > sync.highest_block.unwrap_or(0) { + sync.highest_block = Some(number); + } + if sync.new_blocks.is_downloading(&hash) { + continue; + } + if last_imported_number > number && last_imported_number - number > MAX_NEW_BLOCK_AGE { + trace!(target: "sync", "Ignored ancient new block hash {:?}", hash); + return Err(DownloaderImportError::Invalid); + } + match io.chain().block_status(BlockId::Hash(hash.clone())) { + BlockStatus::InChain => { + trace!(target: "sync", "New block hash already in chain {:?}", hash); + } + BlockStatus::Queued => { + trace!(target: "sync", "New hash block already queued {:?}", hash); + } + BlockStatus::Unknown => { + new_hashes.push(hash.clone()); + if number > max_height { + trace!(target: "sync", "New unknown block hash {:?}", hash); + if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) { + peer.latest_hash = hash.clone(); + } + max_height = number; + } + } + BlockStatus::Bad => { + debug!(target: "sync", "Bad new block hash {:?}", hash); + return Err(DownloaderImportError::Invalid); + } + } + } + if max_height != 0 { + trace!(target: "sync", "Downloading blocks for new hashes"); + sync.new_blocks.reset_to(new_hashes); + sync.state = SyncState::NewBlocks; + sync.sync_peer(io, peer_id, true); + } + Ok(()) + } + + /// Called by peer once it has new block bodies + fn on_peer_block_bodies( + sync: &mut ChainSync, + io: &mut dyn SyncIo, + peer_id: PeerId, + r: &Rlp, + ) -> Result<(), DownloaderImportError> { + sync.clear_peer_download(peer_id); + let block_set = sync + .peers + .get(&peer_id) + .and_then(|p| p.block_set) + .unwrap_or(BlockSet::NewBlocks); + let allowed = sync + .peers + .get(&peer_id) + .map(|p| p.is_allowed()) + .unwrap_or(false); + + if !sync.reset_peer_asking(peer_id, PeerAsking::BlockBodies) || !allowed { + trace!(target: "sync", "{}: Ignored unexpected bodies", peer_id); + return Ok(()); + } + let expected_blocks = match sync.peers.get_mut(&peer_id) { + Some(peer) => mem::replace(&mut peer.asking_blocks, Vec::new()), + None => { + trace!(target: "sync", "{}: Ignored unexpected bodies (peer not found)", peer_id); + return Ok(()); + } + }; + let item_count = r.item_count()?; + trace!(target: "sync", "{} -> BlockBodies ({} entries), set = {:?}", peer_id, item_count, block_set); + if item_count == 0 { + Err(DownloaderImportError::Useless) + } else if sync.state == SyncState::Waiting { + trace!(target: "sync", "Ignored block bodies while waiting"); + Ok(()) + } else { + { + let downloader = match block_set { + BlockSet::NewBlocks => &mut sync.new_blocks, + BlockSet::OldBlocks => match sync.old_blocks { + None => { + trace!(target: "sync", "Ignored block headers while block download is inactive"); + return Ok(()); + } + Some(ref mut blocks) => blocks, + }, + }; + downloader.import_bodies(r, expected_blocks.as_slice())?; + } + sync.collect_blocks(io, block_set); + Ok(()) + } + } + + fn on_peer_fork_header( + sync: &mut ChainSync, + io: &mut dyn SyncIo, + peer_id: PeerId, + r: &Rlp, + ) -> Result<(), DownloaderImportError> { + { + let peer = sync + .peers + .get_mut(&peer_id) + .expect("Is only called when peer is present in peers"); + peer.asking = PeerAsking::Nothing; + let item_count = r.item_count()?; + let (fork_number, fork_hash) = sync + .fork_block + .expect("ForkHeader request is sent only fork block is Some; qed") + .clone(); + + if item_count == 0 || item_count != 1 { + trace!(target: "sync", "{}: Chain is too short to confirm the block", peer_id); + peer.confirmation = ForkConfirmation::TooShort; + } else { + let header = r.at(0)?.as_raw(); + if keccak(&header) != fork_hash { + trace!(target: "sync", "{}: Fork mismatch", peer_id); + return Err(DownloaderImportError::Invalid); + } + + trace!(target: "sync", "{}: Confirmed peer", peer_id); + peer.confirmation = ForkConfirmation::Confirmed; + + if !io.chain_overlay().read().contains_key(&fork_number) { + trace!(target: "sync", "Inserting (fork) block {} header", fork_number); + io.chain_overlay() + .write() + .insert(fork_number, header.to_vec()); + } + } + } + + return Ok(()); + } + + /// Called by peer once it has new block headers during sync + fn on_peer_block_headers( + sync: &mut ChainSync, + io: &mut dyn SyncIo, + peer_id: PeerId, + r: &Rlp, + ) -> Result<(), DownloaderImportError> { + let is_fork_header_request = match sync.peers.get(&peer_id) { + Some(peer) if peer.asking == PeerAsking::ForkHeader => true, + _ => false, + }; + + if is_fork_header_request { + return SyncHandler::on_peer_fork_header(sync, io, peer_id, r); + } + + sync.clear_peer_download(peer_id); + let expected_hash = sync.peers.get(&peer_id).and_then(|p| p.asking_hash); + let allowed = sync + .peers + .get(&peer_id) + .map(|p| p.is_allowed()) + .unwrap_or(false); + let block_set = sync + .peers + .get(&peer_id) + .and_then(|p| p.block_set) + .unwrap_or(BlockSet::NewBlocks); + + if !sync.reset_peer_asking(peer_id, PeerAsking::BlockHeaders) { + debug!(target: "sync", "{}: Ignored unexpected headers", peer_id); + return Ok(()); + } + let expected_hash = match expected_hash { + Some(hash) => hash, + None => { + debug!(target: "sync", "{}: Ignored unexpected headers (expected_hash is None)", peer_id); + return Ok(()); + } + }; + if !allowed { + debug!(target: "sync", "{}: Ignored unexpected headers (peer not allowed)", peer_id); + return Ok(()); + } + + let item_count = r.item_count()?; + trace!(target: "sync", "{} -> BlockHeaders ({} entries), state = {:?}, set = {:?}", peer_id, item_count, sync.state, block_set); + if (sync.state == SyncState::Idle || sync.state == SyncState::WaitingPeers) + && sync.old_blocks.is_none() + { + trace!(target: "sync", "Ignored unexpected block headers"); + return Ok(()); + } + if sync.state == SyncState::Waiting { + trace!(target: "sync", "Ignored block headers while waiting"); + return Ok(()); + } + + let result = { + let downloader = match block_set { + BlockSet::NewBlocks => &mut sync.new_blocks, + BlockSet::OldBlocks => match sync.old_blocks { + None => { + trace!(target: "sync", "Ignored block headers while block download is inactive"); + return Ok(()); + } + Some(ref mut blocks) => blocks, + }, + }; + downloader.import_headers(io, r, expected_hash)? + }; + + if result == DownloadAction::Reset { + sync.reset_downloads(block_set); + } + + sync.collect_blocks(io, block_set); + Ok(()) + } + + /// Called by peer once it has new block receipts + fn on_peer_block_receipts( + sync: &mut ChainSync, + io: &mut dyn SyncIo, + peer_id: PeerId, + r: &Rlp, + ) -> Result<(), DownloaderImportError> { + sync.clear_peer_download(peer_id); + let block_set = sync + .peers + .get(&peer_id) + .and_then(|p| p.block_set) + .unwrap_or(BlockSet::NewBlocks); + let allowed = sync + .peers + .get(&peer_id) + .map(|p| p.is_allowed()) + .unwrap_or(false); + if !sync.reset_peer_asking(peer_id, PeerAsking::BlockReceipts) || !allowed { + trace!(target: "sync", "{}: Ignored unexpected receipts", peer_id); + return Ok(()); + } + let expected_blocks = match sync.peers.get_mut(&peer_id) { + Some(peer) => mem::replace(&mut peer.asking_blocks, Vec::new()), + None => { + trace!(target: "sync", "{}: Ignored unexpected bodies (peer not found)", peer_id); + return Ok(()); + } + }; + let item_count = r.item_count()?; + trace!(target: "sync", "{} -> BlockReceipts ({} entries)", peer_id, item_count); + if item_count == 0 { + Err(DownloaderImportError::Useless) + } else if sync.state == SyncState::Waiting { + trace!(target: "sync", "Ignored block receipts while waiting"); + Ok(()) + } else { + { + let downloader = match block_set { + BlockSet::NewBlocks => &mut sync.new_blocks, + BlockSet::OldBlocks => match sync.old_blocks { + None => { + trace!(target: "sync", "Ignored block headers while block download is inactive"); + return Ok(()); + } + Some(ref mut blocks) => blocks, + }, + }; + downloader.import_receipts(r, expected_blocks.as_slice())?; + } + sync.collect_blocks(io, block_set); + Ok(()) + } + } + + /// Called when snapshot manifest is downloaded from a peer. + fn on_snapshot_manifest( + sync: &mut ChainSync, + io: &mut dyn SyncIo, + peer_id: PeerId, + r: &Rlp, + ) -> Result<(), DownloaderImportError> { + if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { + trace!(target: "sync", "Ignoring snapshot manifest from unconfirmed peer {}", peer_id); + return Ok(()); + } + sync.clear_peer_download(peer_id); + if !sync.reset_peer_asking(peer_id, PeerAsking::SnapshotManifest) + || sync.state != SyncState::SnapshotManifest + { + trace!(target: "sync", "{}: Ignored unexpected/expired manifest", peer_id); + return Ok(()); + } + + let manifest_rlp = r.at(0)?; + let manifest = ManifestData::from_rlp(manifest_rlp.as_raw())?; + + let is_supported_version = io + .snapshot_service() + .supported_versions() + .map_or(false, |(l, h)| { + manifest.version >= l && manifest.version <= h + }); + + if !is_supported_version { + trace!(target: "sync", "{}: Snapshot manifest version not supported: {}", peer_id, manifest.version); + return Err(DownloaderImportError::Invalid); + } + sync.snapshot + .reset_to(&manifest, &keccak(manifest_rlp.as_raw())); + io.snapshot_service().begin_restore(manifest); + sync.state = SyncState::SnapshotData; + + Ok(()) + } + + /// Called when snapshot data is downloaded from a peer. + fn on_snapshot_data( + sync: &mut ChainSync, + io: &mut dyn SyncIo, + peer_id: PeerId, + r: &Rlp, + ) -> Result<(), DownloaderImportError> { + if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { + trace!(target: "sync", "Ignoring snapshot data from unconfirmed peer {}", peer_id); + return Ok(()); + } + sync.clear_peer_download(peer_id); + if !sync.reset_peer_asking(peer_id, PeerAsking::SnapshotData) + || (sync.state != SyncState::SnapshotData && sync.state != SyncState::SnapshotWaiting) + { + trace!(target: "sync", "{}: Ignored unexpected snapshot data", peer_id); + return Ok(()); + } + + // check service status + let status = io.snapshot_service().restoration_status(); + match status { + RestorationStatus::Inactive | RestorationStatus::Failed => { + trace!(target: "sync", "{}: Snapshot restoration aborted", peer_id); + sync.state = SyncState::WaitingPeers; + + // only note bad if restoration failed. + if let (Some(hash), RestorationStatus::Failed) = + (sync.snapshot.snapshot_hash(), status) + { + trace!(target: "sync", "Noting snapshot hash {} as bad", hash); + sync.snapshot.note_bad(hash); + } + + sync.snapshot.clear(); + return Ok(()); + } + RestorationStatus::Initializing { .. } => { + trace!(target: "warp", "{}: Snapshot restoration is initializing", peer_id); + return Ok(()); + } + RestorationStatus::Ongoing { .. } => { + trace!(target: "sync", "{}: Snapshot restoration is ongoing", peer_id); + } + } + + let snapshot_data: Bytes = r.val_at(0)?; + match sync.snapshot.validate_chunk(&snapshot_data) { + Ok(ChunkType::Block(hash)) => { + trace!(target: "sync", "{}: Processing block chunk", peer_id); + io.snapshot_service() + .restore_block_chunk(hash, snapshot_data); + } + Ok(ChunkType::State(hash)) => { + trace!(target: "sync", "{}: Processing state chunk", peer_id); + io.snapshot_service() + .restore_state_chunk(hash, snapshot_data); + } + Err(()) => { + trace!(target: "sync", "{}: Got bad snapshot chunk", peer_id); + io.disconnect_peer(peer_id); + return Ok(()); + } + } + + if sync.snapshot.is_complete() { + // wait for snapshot restoration process to complete + sync.state = SyncState::SnapshotWaiting; + } + + Ok(()) + } + + /// Called by peer to report status + fn on_peer_status( + sync: &mut ChainSync, + io: &mut dyn SyncIo, + peer_id: PeerId, + r: &Rlp, + ) -> Result<(), DownloaderImportError> { + let mut r_iter = r.iter(); + sync.handshaking_peers.remove(&peer_id); + let protocol_version: u8 = r_iter + .next() + .ok_or(rlp::DecoderError::RlpIsTooShort)? + .as_val()?; + let eth_protocol_version = io.protocol_version(Ð_PROTOCOL, peer_id); + let warp_protocol_version = io.protocol_version(&PAR_PROTOCOL, peer_id); + let warp_protocol = warp_protocol_version != 0; + + let network_id = r_iter + .next() + .ok_or(rlp::DecoderError::RlpIsTooShort)? + .as_val()?; + let difficulty = Some( + r_iter + .next() + .ok_or(rlp::DecoderError::RlpIsTooShort)? + .as_val()?, + ); + let latest_hash = r_iter + .next() + .ok_or(rlp::DecoderError::RlpIsTooShort)? + .as_val()?; + let genesis = r_iter + .next() + .ok_or(rlp::DecoderError::RlpIsTooShort)? + .as_val()?; + let forkid_validation_error = { + if eth_protocol_version >= ETH_PROTOCOL_VERSION_64.0 { + let fork_id = rlp04::Rlp::new(r.as_raw()).val_at(5)?; + r_iter.next().ok_or(rlp::DecoderError::RlpIsTooShort)?; + sync.fork_filter + .is_compatible(io.chain(), fork_id) + .err() + .map(|e| (fork_id, e)) + } else { + None + } + }; + let snapshot_hash = if warp_protocol { + Some( + r_iter + .next() + .ok_or(rlp::DecoderError::RlpIsTooShort)? + .as_val()?, + ) + } else { + None + }; + let snapshot_number = if warp_protocol { + Some( + r_iter + .next() + .ok_or(rlp::DecoderError::RlpIsTooShort)? + .as_val()?, + ) + } else { + None + }; + let peer = PeerInfo { + protocol_version, + network_id, + difficulty, + latest_hash, + genesis, + asking: PeerAsking::Nothing, + asking_blocks: Vec::new(), + asking_hash: None, + ask_time: Instant::now(), + last_sent_transactions: Default::default(), + expired: false, + confirmation: if sync.fork_block.is_none() { + ForkConfirmation::Confirmed + } else { + ForkConfirmation::Unconfirmed + }, + asking_snapshot_data: None, + snapshot_hash, + snapshot_number, + block_set: None, + client_version: ClientVersion::from(io.peer_version(peer_id)), + }; + + trace!(target: "sync", "New peer {} (\ + protocol: {}, \ + network: {:?}, \ + difficulty: {:?}, \ + latest:{}, \ + genesis:{}, \ + snapshot:{:?})", + peer_id, + peer.protocol_version, + peer.network_id, + peer.difficulty, + peer.latest_hash, + peer.genesis, + peer.snapshot_number + ); + if io.is_expired() { + trace!(target: "sync", "Status packet from expired session {}:{}", peer_id, io.peer_version(peer_id)); + return Ok(()); + } + + if sync.peers.contains_key(&peer_id) { + debug!(target: "sync", "Unexpected status packet from {}:{}", peer_id, io.peer_version(peer_id)); + return Ok(()); + } + let chain_info = io.chain().chain_info(); + if peer.genesis != chain_info.genesis_hash { + trace!(target: "sync", "Peer {} genesis hash mismatch (ours: {}, theirs: {})", peer_id, chain_info.genesis_hash, peer.genesis); + return Err(DownloaderImportError::Invalid); + } + if peer.network_id != sync.network_id { + trace!(target: "sync", "Peer {} network id mismatch (ours: {}, theirs: {})", peer_id, sync.network_id, peer.network_id); + return Err(DownloaderImportError::Invalid); + } + + if let Some((fork_id, reason)) = forkid_validation_error { + trace!(target: "sync", "Peer {} incompatible fork id (fork id: {:#x}/{}, error: {:?})", peer_id, fork_id.hash.0, fork_id.next, reason); + return Err(DownloaderImportError::Invalid); + } + + if false + || (warp_protocol + && (peer.protocol_version < PAR_PROTOCOL_VERSION_1.0 + || peer.protocol_version > PAR_PROTOCOL_VERSION_2.0)) + || (!warp_protocol + && (peer.protocol_version < ETH_PROTOCOL_VERSION_63.0 + || peer.protocol_version > ETH_PROTOCOL_VERSION_64.0)) + { + trace!(target: "sync", "Peer {} unsupported eth protocol ({})", peer_id, peer.protocol_version); + return Err(DownloaderImportError::Invalid); + } + + if sync.sync_start_time.is_none() { + sync.sync_start_time = Some(Instant::now()); + } + + sync.peers.insert(peer_id.clone(), peer); + // Don't activate peer immediatelly when searching for common block. + // Let the current sync round complete first. + sync.active_peers.insert(peer_id.clone()); + debug!(target: "sync", "Connected {}:{}", peer_id, io.peer_version(peer_id)); + + if let Some((fork_block, _)) = sync.fork_block { + SyncRequester::request_fork_header(sync, io, peer_id, fork_block); + } + + Ok(()) + } + + /// Called when peer sends us new transactions + pub fn on_peer_transactions( + sync: &ChainSync, + io: &mut dyn SyncIo, + peer_id: PeerId, + r: &Rlp, + ) -> Result<(), PacketProcessError> { + // Accept transactions only when fully synced + if !io.is_chain_queue_empty() + || (sync.state != SyncState::Idle && sync.state != SyncState::NewBlocks) + { + trace!(target: "sync", "{} Ignoring transactions while syncing", peer_id); + return Ok(()); + } + if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { + trace!(target: "sync", "{} Ignoring transactions from unconfirmed/unknown peer", peer_id); + return Ok(()); + } + + let item_count = r.item_count()?; + trace!(target: "sync", "{:02} -> Transactions ({} entries)", peer_id, item_count); + let mut transactions = Vec::with_capacity(item_count); + for i in 0..item_count { + let rlp = r.at(i)?; + let tx = rlp.as_raw().to_vec(); + transactions.push(tx); + } + io.chain().queue_transactions(transactions, peer_id); + Ok(()) + } } #[cfg(test)] mod tests { - use ethcore::client::{ChainInfo, EachBlockWith, TestBlockChainClient}; - use parking_lot::RwLock; - use rlp::{Rlp}; - use std::collections::{VecDeque}; - use tests::helpers::{TestIo}; - use tests::snapshot::TestSnapshotService; - - use super::*; - use super::super::tests::{ - dummy_sync_with_peer, - get_dummy_block, - get_dummy_blocks, - get_dummy_hashes, - }; - - #[test] - fn handles_peer_new_hashes() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(10, EachBlockWith::Uncle); - let queue = RwLock::new(VecDeque::new()); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - let hashes_data = get_dummy_hashes(); - let hashes_rlp = Rlp::new(&hashes_data); - - let result = SyncHandler::on_peer_new_hashes(&mut sync, &mut io, 0, &hashes_rlp); - - assert!(result.is_ok()); - } - - #[test] - fn handles_peer_new_block_malformed() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(10, EachBlockWith::Uncle); - - let block_data = get_dummy_block(11, client.chain_info().best_block_hash); - - let queue = RwLock::new(VecDeque::new()); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - //sync.have_common_block = true; - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - let block = Rlp::new(&block_data); - - let result = SyncHandler::on_peer_new_block(&mut sync, &mut io, 0, &block); - - assert!(result.is_err()); - } - - #[test] - fn handles_peer_new_block() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(10, EachBlockWith::Uncle); - - let block_data = get_dummy_blocks(11, client.chain_info().best_block_hash); - - let queue = RwLock::new(VecDeque::new()); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - let block = Rlp::new(&block_data); - - SyncHandler::on_peer_new_block(&mut sync, &mut io, 0, &block).expect("result to be ok"); - } - - #[test] - fn handles_peer_new_block_empty() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(10, EachBlockWith::Uncle); - let queue = RwLock::new(VecDeque::new()); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - let empty_data = vec![]; - let block = Rlp::new(&empty_data); - - let result = SyncHandler::on_peer_new_block(&mut sync, &mut io, 0, &block); - - assert!(result.is_err()); - } - - #[test] - fn handles_peer_new_hashes_empty() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(10, EachBlockWith::Uncle); - let queue = RwLock::new(VecDeque::new()); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - let empty_hashes_data = vec![]; - let hashes_rlp = Rlp::new(&empty_hashes_data); - - let result = SyncHandler::on_peer_new_hashes(&mut sync, &mut io, 0, &hashes_rlp); - - assert!(result.is_ok()); - } + use ethcore::client::{ChainInfo, EachBlockWith, TestBlockChainClient}; + use parking_lot::RwLock; + use rlp::Rlp; + use std::collections::VecDeque; + use tests::{helpers::TestIo, snapshot::TestSnapshotService}; + + use super::{ + super::tests::{dummy_sync_with_peer, get_dummy_block, get_dummy_blocks, get_dummy_hashes}, + *, + }; + + #[test] + fn handles_peer_new_hashes() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(10, EachBlockWith::Uncle); + let queue = RwLock::new(VecDeque::new()); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + let hashes_data = get_dummy_hashes(); + let hashes_rlp = Rlp::new(&hashes_data); + + let result = SyncHandler::on_peer_new_hashes(&mut sync, &mut io, 0, &hashes_rlp); + + assert!(result.is_ok()); + } + + #[test] + fn handles_peer_new_block_malformed() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(10, EachBlockWith::Uncle); + + let block_data = get_dummy_block(11, client.chain_info().best_block_hash); + + let queue = RwLock::new(VecDeque::new()); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + //sync.have_common_block = true; + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + let block = Rlp::new(&block_data); + + let result = SyncHandler::on_peer_new_block(&mut sync, &mut io, 0, &block); + + assert!(result.is_err()); + } + + #[test] + fn handles_peer_new_block() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(10, EachBlockWith::Uncle); + + let block_data = get_dummy_blocks(11, client.chain_info().best_block_hash); + + let queue = RwLock::new(VecDeque::new()); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + let block = Rlp::new(&block_data); + + SyncHandler::on_peer_new_block(&mut sync, &mut io, 0, &block).expect("result to be ok"); + } + + #[test] + fn handles_peer_new_block_empty() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(10, EachBlockWith::Uncle); + let queue = RwLock::new(VecDeque::new()); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + let empty_data = vec![]; + let block = Rlp::new(&empty_data); + + let result = SyncHandler::on_peer_new_block(&mut sync, &mut io, 0, &block); + + assert!(result.is_err()); + } + + #[test] + fn handles_peer_new_hashes_empty() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(10, EachBlockWith::Uncle); + let queue = RwLock::new(VecDeque::new()); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + let empty_hashes_data = vec![]; + let hashes_rlp = Rlp::new(&empty_hashes_data); + + let result = SyncHandler::on_peer_new_hashes(&mut sync, &mut io, 0, &hashes_rlp); + + assert!(result.is_ok()); + } } diff --git a/ethcore/sync/src/chain/mod.rs b/ethcore/sync/src/chain/mod.rs index 81f1ccffe94..9a5b1f5fe88 100644 --- a/ethcore/sync/src/chain/mod.rs +++ b/ethcore/sync/src/chain/mod.rs @@ -1,22 +1,22 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! `BlockChain` synchronization strategy. //! Syncs to peers and keeps up to date. -//! This implementation uses ethereum protocol v63 +//! This implementation uses ethereum protocol v63/v64 //! //! Syncing strategy summary. //! Split the chain into ranges of N blocks each. Download ranges sequentially. Split each range into subchains of M blocks. Download subchains in parallel. @@ -87,67 +87,83 @@ //! //! All other messages are ignored. +pub mod fork_filter; mod handler; -pub mod sync_packet; mod propagator; mod requester; mod supplier; +pub mod sync_packet; -use std::sync::{Arc, mpsc}; -use std::collections::{HashSet, HashMap, BTreeMap}; -use std::cmp; -use std::time::{Duration, Instant}; -use hash::keccak; -use heapsize::HeapSizeOf; +pub use self::fork_filter::ForkFilterApi; +use super::{SyncConfig, WarpSync}; +use api::{EthProtocolInfo as PeerInfoDigest, PriorityTask, ETH_PROTOCOL, PAR_PROTOCOL}; +use block_sync::{BlockDownloader, DownloadAction}; +use bytes::Bytes; +use derive_more::Display; +use ethcore::{ + client::{BlockChainClient, BlockChainInfo, BlockId, BlockQueueInfo, BlockStatus}, + snapshot::RestorationStatus, +}; use ethereum_types::{H256, U256}; use fastmap::{H256FastMap, H256FastSet}; +use hash::keccak; +use network::{self, client_version::ClientVersion, PeerId}; use parking_lot::{Mutex, RwLock, RwLockWriteGuard}; -use bytes::Bytes; -use rlp::{RlpStream, DecoderError}; -use network::{self, PeerId, PacketId}; -use network::client_version::ClientVersion; -use ethcore::client::{BlockChainClient, BlockStatus, BlockId, BlockChainInfo, BlockQueueInfo}; -use ethcore::snapshot::{RestorationStatus}; -use sync_io::SyncIo; -use super::{WarpSync, SyncConfig}; -use block_sync::{BlockDownloader, DownloadAction}; use rand::Rng; -use snapshot::{Snapshot}; -use api::{EthProtocolInfo as PeerInfoDigest, WARP_SYNC_PROTOCOL_ID, PriorityTask}; -use private_tx::PrivateTxHandler; -use transactions_stats::{TransactionsStats, Stats as TransactionStats}; -use types::transaction::UnverifiedTransaction; -use types::BlockNumber; - -use self::handler::SyncHandler; -use self::sync_packet::{PacketInfo, SyncPacket}; -use self::sync_packet::SyncPacket::{ - NewBlockPacket, - StatusPacket, +use rlp::{DecoderError, RlpStream}; +use snapshot::Snapshot; +use std::{ + cmp, + collections::{BTreeMap, HashMap, HashSet}, + sync::mpsc, + time::{Duration, Instant}, +}; +use sync_io::SyncIo; +use transactions_stats::{Stats as TransactionStats, TransactionsStats}; +use types::{transaction::UnverifiedTransaction, BlockNumber}; + +use self::{ + handler::SyncHandler, + sync_packet::{ + PacketInfo, + SyncPacket::{self, NewBlockPacket, StatusPacket}, + }, }; -use self::propagator::SyncPropagator; -use self::requester::SyncRequester; pub(crate) use self::supplier::SyncSupplier; +use self::{propagator::SyncPropagator, requester::SyncRequester}; known_heap_size!(0, PeerInfo); -pub type PacketDecodeError = DecoderError; +/// Possible errors during packet's processing +#[derive(Debug, Display)] +pub enum PacketProcessError { + /// Error of RLP decoder + #[display(fmt = "Decoder Error: {}", _0)] + Decoder(DecoderError), + /// Underlying client is busy and cannot process the packet + /// The packet should be postponed for later response + #[display(fmt = "Underlying client is busy")] + ClientBusy, +} +impl From for PacketProcessError { + fn from(err: DecoderError) -> Self { + PacketProcessError::Decoder(err).into() + } +} + +/// 64 version of Ethereum protocol. +pub const ETH_PROTOCOL_VERSION_64: (u8, u8) = (64, 0x11); /// 63 version of Ethereum protocol. pub const ETH_PROTOCOL_VERSION_63: (u8, u8) = (63, 0x11); -/// 62 version of Ethereum protocol. -pub const ETH_PROTOCOL_VERSION_62: (u8, u8) = (62, 0x11); -/// 1 version of Parity protocol and the packet count. +/// 1 version of OpenEthereum protocol and the packet count. pub const PAR_PROTOCOL_VERSION_1: (u8, u8) = (1, 0x15); -/// 2 version of Parity protocol (consensus messages added). +/// 2 version of OpenEthereum protocol (consensus messages added). pub const PAR_PROTOCOL_VERSION_2: (u8, u8) = (2, 0x16); -/// 3 version of Parity protocol (private transactions messages added). -pub const PAR_PROTOCOL_VERSION_3: (u8, u8) = (3, 0x18); pub const MAX_BODIES_TO_SEND: usize = 256; pub const MAX_HEADERS_TO_SEND: usize = 512; -pub const MAX_NODE_DATA_TO_SEND: usize = 1024; pub const MAX_RECEIPTS_HEADERS_TO_SEND: usize = 256; const MIN_PEERS_PROPAGATION: usize = 4; const MAX_PEERS_PROPAGATION: usize = 128; @@ -180,594 +196,689 @@ const PRIORITY_TASK_DEADLINE: Duration = Duration::from_millis(100); #[derive(Copy, Clone, Eq, PartialEq, Debug)] /// Sync state pub enum SyncState { - /// Collecting enough peers to start syncing. - WaitingPeers, - /// Waiting for snapshot manifest download - SnapshotManifest, - /// Downloading snapshot data - SnapshotData, - /// Waiting for snapshot restoration progress. - SnapshotWaiting, - /// Downloading new blocks - Blocks, - /// Initial chain sync complete. Waiting for new packets - Idle, - /// Block downloading paused. Waiting for block queue to process blocks and free some space - Waiting, - /// Downloading blocks learned from `NewHashes` packet - NewBlocks, + /// Collecting enough peers to start syncing. + WaitingPeers, + /// Waiting for snapshot manifest download + SnapshotManifest, + /// Downloading snapshot data + SnapshotData, + /// Waiting for snapshot restoration progress. + SnapshotWaiting, + /// Downloading new blocks + Blocks, + /// Initial chain sync complete. Waiting for new packets + Idle, + /// Block downloading paused. Waiting for block queue to process blocks and free some space + Waiting, + /// Downloading blocks learned from `NewHashes` packet + NewBlocks, } /// Syncing status and statistics -#[derive(Clone, Copy)] +#[derive(Clone)] pub struct SyncStatus { - /// State - pub state: SyncState, - /// Syncing protocol version. That's the maximum protocol version we connect to. - pub protocol_version: u8, - /// The underlying p2p network version. - pub network_id: u64, - /// `BlockChain` height for the moment the sync started. - pub start_block_number: BlockNumber, - /// Last fully downloaded and imported block number (if any). - pub last_imported_block_number: Option, - /// Highest block number in the download queue (if any). - pub highest_block_number: Option, - /// Total number of blocks for the sync process. - pub blocks_total: BlockNumber, - /// Number of blocks downloaded so far. - pub blocks_received: BlockNumber, - /// Total number of connected peers - pub num_peers: usize, - /// Total number of active peers. - pub num_active_peers: usize, - /// Heap memory used in bytes. - pub mem_used: usize, - /// Snapshot chunks - pub num_snapshot_chunks: usize, - /// Snapshot chunks downloaded - pub snapshot_chunks_done: usize, - /// Last fully downloaded and imported ancient block number (if any). - pub last_imported_old_block_number: Option, + /// State + pub state: SyncState, + /// Syncing protocol version. That's the maximum protocol version we connect to. + pub protocol_version: u8, + /// The underlying p2p network version. + pub network_id: u64, + /// `BlockChain` height for the moment the sync started. + pub start_block_number: BlockNumber, + /// Last fully downloaded and imported block number (if any). + pub last_imported_block_number: Option, + /// Highest block number in the download queue (if any). + pub highest_block_number: Option, + /// Total number of blocks for the sync process. + pub blocks_total: BlockNumber, + /// Number of blocks downloaded so far. + pub blocks_received: BlockNumber, + /// Total number of connected peers + pub num_peers: usize, + /// Total number of active peers. + pub num_active_peers: usize, + /// Snapshot chunks + pub num_snapshot_chunks: usize, + /// Snapshot chunks downloaded + pub snapshot_chunks_done: usize, + /// Last fully downloaded and imported ancient block number (if any). + pub last_imported_old_block_number: Option, + /// Internal structure item numbers + pub item_sizes: BTreeMap, } impl SyncStatus { - /// Indicates if snapshot download is in progress - pub fn is_snapshot_syncing(&self) -> bool { - match self.state { - SyncState::SnapshotManifest | - SyncState::SnapshotData | - SyncState::SnapshotWaiting => true, - _ => false, - } - } - - /// Returns max no of peers to display in informants - pub fn current_max_peers(&self, min_peers: u32, max_peers: u32) -> u32 { - if self.num_peers as u32 > min_peers { - max_peers - } else { - min_peers - } - } - - /// Is it doing a major sync? - pub fn is_syncing(&self, queue_info: BlockQueueInfo) -> bool { - let is_syncing_state = match self.state { SyncState::Idle | SyncState::NewBlocks => false, _ => true }; - let is_verifying = queue_info.unverified_queue_size + queue_info.verified_queue_size > 3; - is_verifying || is_syncing_state - } + /// Indicates if snapshot download is in progress + pub fn is_snapshot_syncing(&self) -> bool { + match self.state { + SyncState::SnapshotManifest | SyncState::SnapshotData | SyncState::SnapshotWaiting => { + true + } + _ => false, + } + } + + /// Returns max no of peers to display in informants + pub fn current_max_peers(&self, min_peers: u32, max_peers: u32) -> u32 { + if self.num_peers as u32 > min_peers { + max_peers + } else { + min_peers + } + } + + /// Is it doing a major sync? + pub fn is_syncing(&self, queue_info: BlockQueueInfo) -> bool { + let is_syncing_state = match self.state { + SyncState::Idle | SyncState::NewBlocks => false, + _ => true, + }; + let is_verifying = queue_info.unverified_queue_size + queue_info.verified_queue_size > 3; + is_verifying || is_syncing_state + } } #[derive(PartialEq, Eq, Debug, Clone)] /// Peer data type requested pub enum PeerAsking { - Nothing, - ForkHeader, - BlockHeaders, - BlockBodies, - BlockReceipts, - SnapshotManifest, - SnapshotData, + Nothing, + ForkHeader, + BlockHeaders, + BlockBodies, + BlockReceipts, + SnapshotManifest, + SnapshotData, } #[derive(PartialEq, Eq, Debug, Clone, Copy)] /// Block downloader channel. pub enum BlockSet { - /// New blocks better than out best blocks - NewBlocks, - /// Missing old blocks - OldBlocks, + /// New blocks better than out best blocks + NewBlocks, + /// Missing old blocks + OldBlocks, } + +impl BlockSet { + pub fn to_string(&self) -> &'static str { + match *self { + Self::NewBlocks => "new_blocks", + Self::OldBlocks => "old_blocks", + } + } +} + #[derive(Clone, Eq, PartialEq)] pub enum ForkConfirmation { - /// Fork block confirmation pending. - Unconfirmed, - /// Peer's chain is too short to confirm the fork. - TooShort, - /// Fork is confirmed. - Confirmed, + /// Fork block confirmation pending. + Unconfirmed, + /// Peer's chain is too short to confirm the fork. + TooShort, + /// Fork is confirmed. + Confirmed, } #[derive(Clone)] /// Syncing peer information pub struct PeerInfo { - /// eth protocol version - protocol_version: u8, - /// Peer chain genesis hash - genesis: H256, - /// Peer network id - network_id: u64, - /// Peer best block hash - latest_hash: H256, - /// Peer total difficulty if known - difficulty: Option, - /// Type of data currenty being requested from peer. - asking: PeerAsking, - /// A set of block numbers being requested - asking_blocks: Vec, - /// Holds requested header hash if currently requesting block header by hash - asking_hash: Option, - /// Holds requested snapshot chunk hash if any. - asking_snapshot_data: Option, - /// Request timestamp - ask_time: Instant, - /// Holds a set of transactions recently sent to this peer to avoid spamming. - last_sent_transactions: H256FastSet, - /// Holds a set of private transactions and their signatures recently sent to this peer to avoid spamming. - last_sent_private_transactions: H256FastSet, - /// Pending request is expired and result should be ignored - expired: bool, - /// Private transactions enabled - private_tx_enabled: bool, - /// Peer fork confirmation status - confirmation: ForkConfirmation, - /// Best snapshot hash - snapshot_hash: Option, - /// Best snapshot block number - snapshot_number: Option, - /// Block set requested - block_set: Option, - /// Version of the software the peer is running - client_version: ClientVersion, + /// eth protocol version + protocol_version: u8, + /// Peer chain genesis hash + genesis: H256, + /// Peer network id + network_id: u64, + /// Peer best block hash + latest_hash: H256, + /// Peer total difficulty if known + difficulty: Option, + /// Type of data currenty being requested from peer. + asking: PeerAsking, + /// A set of block numbers being requested + asking_blocks: Vec, + /// Holds requested header hash if currently requesting block header by hash + asking_hash: Option, + /// Holds requested snapshot chunk hash if any. + asking_snapshot_data: Option, + /// Request timestamp + ask_time: Instant, + /// Holds a set of transactions recently sent to this peer to avoid spamming. + last_sent_transactions: H256FastSet, + /// Pending request is expired and result should be ignored + expired: bool, + /// Peer fork confirmation status + confirmation: ForkConfirmation, + /// Best snapshot hash + snapshot_hash: Option, + /// Best snapshot block number + snapshot_number: Option, + /// Block set requested + block_set: Option, + /// Version of the software the peer is running + client_version: ClientVersion, } impl PeerInfo { - fn can_sync(&self) -> bool { - self.confirmation == ForkConfirmation::Confirmed && !self.expired - } - - fn is_allowed(&self) -> bool { - self.confirmation != ForkConfirmation::Unconfirmed && !self.expired - } - - fn reset_asking(&mut self) { - self.asking_blocks.clear(); - self.asking_hash = None; - // mark any pending requests as expired - if self.asking != PeerAsking::Nothing && self.is_allowed() { - self.expired = true; - } - } - - fn reset_private_stats(&mut self) { - self.last_sent_private_transactions.clear(); - } + fn can_sync(&self) -> bool { + self.confirmation == ForkConfirmation::Confirmed && !self.expired + } + + fn is_allowed(&self) -> bool { + self.confirmation != ForkConfirmation::Unconfirmed && !self.expired + } + + fn reset_asking(&mut self) { + self.asking_blocks.clear(); + self.asking_hash = None; + // mark any pending requests as expired + if self.asking != PeerAsking::Nothing && self.is_allowed() { + self.expired = true; + } + } } #[cfg(not(test))] pub mod random { - use rand; - pub fn new() -> rand::ThreadRng { rand::thread_rng() } + use rand; + pub fn new() -> rand::ThreadRng { + rand::thread_rng() + } } #[cfg(test)] pub mod random { - use rand::{self, SeedableRng}; - pub fn new() -> rand::XorShiftRng { rand::XorShiftRng::from_seed([0, 1, 2, 3]) } + use rand::{self, SeedableRng}; + pub fn new() -> rand::XorShiftRng { + rand::XorShiftRng::from_seed([0, 1, 2, 3]) + } } -pub type RlpResponseResult = Result, PacketDecodeError>; +pub type RlpResponseResult = Result, PacketProcessError>; pub type Peers = HashMap; /// Thread-safe wrapper for `ChainSync`. /// /// NOTE always lock in order of fields declaration pub struct ChainSyncApi { - /// Priority tasks queue - priority_tasks: Mutex>, - /// The rest of sync data - sync: RwLock, + /// Priority tasks queue + priority_tasks: Mutex>, + /// The rest of sync data + sync: RwLock, } impl ChainSyncApi { - /// Creates new `ChainSyncApi` - pub fn new( - config: SyncConfig, - chain: &BlockChainClient, - private_tx_handler: Option>, - priority_tasks: mpsc::Receiver, - ) -> Self { - ChainSyncApi { - sync: RwLock::new(ChainSync::new(config, chain, private_tx_handler)), - priority_tasks: Mutex::new(priority_tasks), - } - } - - /// Gives `write` access to underlying `ChainSync` - pub fn write(&self) -> RwLockWriteGuard { - self.sync.write() - } - - /// Returns info about given list of peers - pub fn peer_info(&self, ids: &[PeerId]) -> Vec> { - let sync = self.sync.read(); - ids.iter().map(|id| sync.peer_info(id)).collect() - } - - /// Returns synchonization status - pub fn status(&self) -> SyncStatus { - self.sync.read().status() - } - - /// Returns transactions propagation statistics - pub fn transactions_stats(&self) -> BTreeMap { - self.sync.read().transactions_stats() - .iter() - .map(|(hash, stats)| (*hash, stats.into())) - .collect() - } - - /// Dispatch incoming requests and responses - pub fn dispatch_packet(&self, io: &mut SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) { - SyncSupplier::dispatch_packet(&self.sync, io, peer, packet_id, data) - } - - /// Process a priority propagation queue. - /// This task is run from a timer and should be time constrained. - /// Hence we set up a deadline for the execution and cancel the task if the deadline is exceeded. - /// - /// NOTE This method should only handle stuff that can be canceled and would reach other peers - /// by other means. - pub fn process_priority_queue(&self, io: &mut SyncIo) { - fn check_deadline(deadline: Instant) -> Option { - let now = Instant::now(); - if now > deadline { - None - } else { - Some(deadline - now) - } - } - - // deadline to get the task from the queue - let deadline = Instant::now() + ::api::PRIORITY_TIMER_INTERVAL; - let mut work = || { - let task = { - let tasks = self.priority_tasks.try_lock_until(deadline)?; - let left = check_deadline(deadline)?; - tasks.recv_timeout(left).ok()? - }; - task.starting(); - // wait for the sync lock until deadline, - // note we might drop the task here if we won't manage to acquire the lock. - let mut sync = self.sync.try_write_until(deadline)?; - // since we already have everything let's use a different deadline - // to do the rest of the job now, so that previous work is not wasted. - let deadline = Instant::now() + PRIORITY_TASK_DEADLINE; - let as_ms = move |prev| { - let dur: Duration = Instant::now() - prev; - dur.as_secs() * 1_000 + dur.subsec_millis() as u64 - }; - match task { - // NOTE We can't simply use existing methods, - // cause the block is not in the DB yet. - PriorityTask::PropagateBlock { started, block, hash, difficulty } => { - // try to send to peers that are on the same block as us - // (they will most likely accept the new block). - let chain_info = io.chain().chain_info(); - let total_difficulty = chain_info.total_difficulty + difficulty; - let rlp = ChainSync::create_block_rlp(&block, total_difficulty); - for peers in sync.get_peers(&chain_info, PeerState::SameBlock).chunks(10) { - check_deadline(deadline)?; - for peer in peers { - SyncPropagator::send_packet(io, *peer, NewBlockPacket, rlp.clone()); - if let Some(ref mut peer) = sync.peers.get_mut(peer) { - peer.latest_hash = hash; - } - } - } - debug!(target: "sync", "Finished block propagation, took {}ms", as_ms(started)); - }, - PriorityTask::PropagateTransactions(time, _) => { - SyncPropagator::propagate_new_transactions(&mut sync, io, || { - check_deadline(deadline).is_some() - }); - debug!(target: "sync", "Finished transaction propagation, took {}ms", as_ms(time)); - }, - } - - Some(()) - }; - - // Process as many items as we can until the deadline is reached. - loop { - if work().is_none() { - return; - } - } - } + /// Creates new `ChainSyncApi` + pub fn new( + config: SyncConfig, + chain: &dyn BlockChainClient, + fork_filter: ForkFilterApi, + priority_tasks: mpsc::Receiver, + ) -> Self { + ChainSyncApi { + sync: RwLock::new(ChainSync::new(config, chain, fork_filter)), + priority_tasks: Mutex::new(priority_tasks), + } + } + + /// Gives `write` access to underlying `ChainSync` + pub fn write(&self) -> RwLockWriteGuard { + self.sync.write() + } + + /// Returns info about given list of peers + pub fn peer_info(&self, ids: &[PeerId]) -> Vec> { + let sync = self.sync.read(); + ids.iter().map(|id| sync.peer_info(id)).collect() + } + + /// Returns synchonization status + pub fn status(&self) -> SyncStatus { + self.sync.read().status() + } + + /// Returns transactions propagation statistics + pub fn transactions_stats(&self) -> BTreeMap { + self.sync + .read() + .transactions_stats() + .iter() + .map(|(hash, stats)| (*hash, stats.into())) + .collect() + } + + /// Dispatch incoming requests and responses + pub fn dispatch_packet(&self, io: &mut dyn SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) { + SyncSupplier::dispatch_packet(&self.sync, io, peer, packet_id, data) + } + + /// Process the queue with requests, that were delayed with response. + pub fn process_delayed_requests(&self, io: &mut dyn SyncIo) { + let requests = self.sync.write().retrieve_delayed_requests(); + if !requests.is_empty() { + debug!(target: "sync", "Processing {} delayed requests", requests.len()); + for (peer_id, packet_id, packet_data) in requests { + SyncSupplier::dispatch_delayed_request( + &self.sync, + io, + peer_id, + packet_id, + &packet_data, + ); + } + } + } + + /// Process a priority propagation queue. + /// This task is run from a timer and should be time constrained. + /// Hence we set up a deadline for the execution and cancel the task if the deadline is exceeded. + /// + /// NOTE This method should only handle stuff that can be canceled and would reach other peers + /// by other means. + pub fn process_priority_queue(&self, io: &mut dyn SyncIo) { + fn check_deadline(deadline: Instant) -> Option { + let now = Instant::now(); + if now > deadline { + None + } else { + Some(deadline - now) + } + } + + // deadline to get the task from the queue + let deadline = Instant::now() + ::api::PRIORITY_TIMER_INTERVAL; + let mut work = || { + let task = { + let tasks = self.priority_tasks.try_lock_until(deadline)?; + let left = check_deadline(deadline)?; + tasks.recv_timeout(left).ok()? + }; + task.starting(); + // wait for the sync lock until deadline, + // note we might drop the task here if we won't manage to acquire the lock. + let mut sync = self.sync.try_write_until(deadline)?; + // since we already have everything let's use a different deadline + // to do the rest of the job now, so that previous work is not wasted. + let deadline = Instant::now() + PRIORITY_TASK_DEADLINE; + let as_ms = move |prev| { + let dur: Duration = Instant::now() - prev; + dur.as_secs() * 1_000 + dur.subsec_millis() as u64 + }; + match task { + // NOTE We can't simply use existing methods, + // cause the block is not in the DB yet. + PriorityTask::PropagateBlock { + started, + block, + hash, + difficulty, + } => { + // try to send to peers that are on the same block as us + // (they will most likely accept the new block). + let chain_info = io.chain().chain_info(); + let total_difficulty = chain_info.total_difficulty + difficulty; + let rlp = ChainSync::create_block_rlp(&block, total_difficulty); + for peers in sync.get_peers(&chain_info, PeerState::SameBlock).chunks(10) { + check_deadline(deadline)?; + for peer in peers { + SyncPropagator::send_packet(io, *peer, NewBlockPacket, rlp.clone()); + if let Some(ref mut peer) = sync.peers.get_mut(peer) { + peer.latest_hash = hash; + } + } + } + debug!(target: "sync", "Finished block propagation, took {}ms", as_ms(started)); + } + PriorityTask::PropagateTransactions(time, _) => { + SyncPropagator::propagate_new_transactions(&mut sync, io, || { + check_deadline(deadline).is_some() + }); + debug!(target: "sync", "Finished transaction propagation, took {}ms", as_ms(time)); + } + } + + Some(()) + }; + + // Process as many items as we can until the deadline is reached. + loop { + if work().is_none() { + return; + } + } + } } // Static methods impl ChainSync { - /// creates rlp to send for the tree defined by 'from' and 'to' hashes - fn create_new_hashes_rlp(chain: &BlockChainClient, from: &H256, to: &H256) -> Option { - match chain.tree_route(from, to) { - Some(route) => { - let uncles = chain.find_uncles(from).unwrap_or_else(Vec::new); - match route.blocks.len() { - 0 => None, - _ => { - let mut blocks = route.blocks; - blocks.extend(uncles); - let mut rlp_stream = RlpStream::new_list(blocks.len()); - for block_hash in blocks { - let mut hash_rlp = RlpStream::new_list(2); - let number = chain.block_header(BlockId::Hash(block_hash.clone())) + /// creates rlp to send for the tree defined by 'from' and 'to' hashes + fn create_new_hashes_rlp( + chain: &dyn BlockChainClient, + from: &H256, + to: &H256, + ) -> Option { + match chain.tree_route(from, to) { + Some(route) => { + let uncles = chain.find_uncles(from).unwrap_or_else(Vec::new); + match route.blocks.len() { + 0 => None, + _ => { + let mut blocks = route.blocks; + blocks.extend(uncles); + let mut rlp_stream = RlpStream::new_list(blocks.len()); + for block_hash in blocks { + let mut hash_rlp = RlpStream::new_list(2); + let number = chain.block_header(BlockId::Hash(block_hash.clone())) .expect("chain.tree_route and chain.find_uncles only return hahses of blocks that are in the blockchain. qed.").number(); - hash_rlp.append(&block_hash); - hash_rlp.append(&number); - rlp_stream.append_raw(hash_rlp.as_raw(), 1); - } - Some(rlp_stream.out()) - } - } - }, - None => None - } - } - - /// creates rlp from block bytes and total difficulty - fn create_block_rlp(bytes: &Bytes, total_difficulty: U256) -> Bytes { - let mut rlp_stream = RlpStream::new_list(2); - rlp_stream.append_raw(bytes, 1); - rlp_stream.append(&total_difficulty); - rlp_stream.out() - } - - /// creates latest block rlp for the given client - fn create_latest_block_rlp(chain: &BlockChainClient) -> Bytes { - Self::create_block_rlp( - &chain.block(BlockId::Hash(chain.chain_info().best_block_hash)) - .expect("Best block always exists").into_inner(), - chain.chain_info().total_difficulty - ) - } - - /// creates given hash block rlp for the given client - fn create_new_block_rlp(chain: &BlockChainClient, hash: &H256) -> Bytes { - Self::create_block_rlp( - &chain.block(BlockId::Hash(hash.clone())).expect("Block has just been sealed; qed").into_inner(), - chain.block_total_difficulty(BlockId::Hash(hash.clone())).expect("Block has just been sealed; qed.") - ) - } - - fn select_random_peers(peers: &[PeerId]) -> Vec { - // take sqrt(x) peers - let mut peers = peers.to_vec(); - let mut count = (peers.len() as f64).powf(0.5).round() as usize; - count = cmp::min(count, MAX_PEERS_PROPAGATION); - count = cmp::max(count, MIN_PEERS_PROPAGATION); - random::new().shuffle(&mut peers); - peers.truncate(count); - peers - } - - fn get_init_state(warp_sync: WarpSync, chain: &BlockChainClient) -> SyncState { - let best_block = chain.chain_info().best_block_number; - match warp_sync { - WarpSync::Enabled => SyncState::WaitingPeers, - WarpSync::OnlyAndAfter(block) if block > best_block => SyncState::WaitingPeers, - _ => SyncState::Idle, - } - } + hash_rlp.append(&block_hash); + hash_rlp.append(&number); + rlp_stream.append_raw(hash_rlp.as_raw(), 1); + } + Some(rlp_stream.out()) + } + } + } + None => None, + } + } + + /// creates rlp from block bytes and total difficulty + fn create_block_rlp(bytes: &Bytes, total_difficulty: U256) -> Bytes { + let mut rlp_stream = RlpStream::new_list(2); + rlp_stream.append_raw(bytes, 1); + rlp_stream.append(&total_difficulty); + rlp_stream.out() + } + + /// creates latest block rlp for the given client + fn create_latest_block_rlp(chain: &dyn BlockChainClient) -> Bytes { + Self::create_block_rlp( + &chain + .block(BlockId::Hash(chain.chain_info().best_block_hash)) + .expect("Best block always exists") + .into_inner(), + chain.chain_info().total_difficulty, + ) + } + + /// creates given hash block rlp for the given client + fn create_new_block_rlp(chain: &dyn BlockChainClient, hash: &H256) -> Bytes { + Self::create_block_rlp( + &chain + .block(BlockId::Hash(hash.clone())) + .expect("Block has just been sealed; qed") + .into_inner(), + chain + .block_total_difficulty(BlockId::Hash(hash.clone())) + .expect("Block has just been sealed; qed."), + ) + } + + fn select_random_peers(peers: &[PeerId]) -> Vec { + // take sqrt(x) peers + let mut peers = peers.to_vec(); + let mut count = (peers.len() as f64).powf(0.5).round() as usize; + count = cmp::min(count, MAX_PEERS_PROPAGATION); + count = cmp::max(count, MIN_PEERS_PROPAGATION); + random::new().shuffle(&mut peers); + peers.truncate(count); + peers + } + + fn get_init_state(warp_sync: WarpSync, chain: &dyn BlockChainClient) -> SyncState { + let best_block = chain.chain_info().best_block_number; + match warp_sync { + WarpSync::Enabled => SyncState::WaitingPeers, + WarpSync::OnlyAndAfter(block) if block > best_block => SyncState::WaitingPeers, + _ => SyncState::Idle, + } + } } /// A peer query method for getting a list of peers enum PeerState { - /// Peer is on different hash than us - Lagging, - /// Peer is on the same block as us - SameBlock + /// Peer is on different hash than us + Lagging, + /// Peer is on the same block as us + SameBlock, } /// Blockchain sync handler. /// See module documentation for more details. pub struct ChainSync { - /// Sync state - state: SyncState, - /// Last block number for the start of sync - starting_block: BlockNumber, - /// Highest block number seen - highest_block: Option, - /// All connected peers - peers: Peers, - /// Peers active for current sync round - active_peers: HashSet, - /// Block download process for new blocks - new_blocks: BlockDownloader, - /// Block download process for ancient blocks - old_blocks: Option, - /// Last propagated block number - last_sent_block_number: BlockNumber, - /// Network ID - network_id: u64, - /// Optional fork block to check - fork_block: Option<(BlockNumber, H256)>, - /// Snapshot downloader. - snapshot: Snapshot, - /// Connected peers pending Status message. - /// Value is request timestamp. - handshaking_peers: HashMap, - /// Sync start timestamp. Measured when first peer is connected - sync_start_time: Option, - /// Transactions propagation statistics - transactions_stats: TransactionsStats, - /// Enable ancient block downloading - download_old_blocks: bool, - /// Shared private tx service. - private_tx_handler: Option>, - /// Enable warp sync. - warp_sync: WarpSync, + /// Sync state + state: SyncState, + /// Last block number for the start of sync + starting_block: BlockNumber, + /// Highest block number seen + highest_block: Option, + /// All connected peers + peers: Peers, + /// Peers active for current sync round + active_peers: HashSet, + /// Block download process for new blocks + new_blocks: BlockDownloader, + /// Block download process for ancient blocks + old_blocks: Option, + /// Last propagated block number + last_sent_block_number: BlockNumber, + /// Network ID + network_id: u64, + /// Optional fork block to check + fork_block: Option<(BlockNumber, H256)>, + /// Fork filter + fork_filter: ForkFilterApi, + /// Snapshot downloader. + snapshot: Snapshot, + /// Connected peers pending Status message. + /// Value is request timestamp. + handshaking_peers: HashMap, + /// Requests, that can not be processed at the moment + delayed_requests: Vec<(PeerId, u8, Vec)>, + /// Ids of delayed requests, used for lookup, id is composed from peer id and packet id + delayed_requests_ids: HashSet<(PeerId, u8)>, + /// Sync start timestamp. Measured when first peer is connected + sync_start_time: Option, + /// Transactions propagation statistics + transactions_stats: TransactionsStats, + /// Enable ancient block downloading + download_old_blocks: bool, + /// Enable warp sync. + warp_sync: WarpSync, } impl ChainSync { - /// Create a new instance of syncing strategy. - pub fn new( - config: SyncConfig, - chain: &BlockChainClient, - private_tx_handler: Option>, - ) -> Self { - let chain_info = chain.chain_info(); - let best_block = chain.chain_info().best_block_number; - let state = Self::get_init_state(config.warp_sync, chain); - - let mut sync = ChainSync { - state, - starting_block: best_block, - highest_block: None, - peers: HashMap::new(), - handshaking_peers: HashMap::new(), - active_peers: HashSet::new(), - new_blocks: BlockDownloader::new(BlockSet::NewBlocks, &chain_info.best_block_hash, chain_info.best_block_number), - old_blocks: None, - last_sent_block_number: 0, - network_id: config.network_id, - fork_block: config.fork_block, - download_old_blocks: config.download_old_blocks, - snapshot: Snapshot::new(), - sync_start_time: None, - transactions_stats: TransactionsStats::default(), - private_tx_handler, - warp_sync: config.warp_sync, - }; - sync.update_targets(chain); - sync - } - - /// Returns synchonization status - pub fn status(&self) -> SyncStatus { - let last_imported_number = self.new_blocks.last_imported_block_number(); - SyncStatus { - state: self.state.clone(), - protocol_version: ETH_PROTOCOL_VERSION_63.0, - network_id: self.network_id, - start_block_number: self.starting_block, - last_imported_block_number: Some(last_imported_number), - last_imported_old_block_number: self.old_blocks.as_ref().map(|d| d.last_imported_block_number()), - highest_block_number: self.highest_block.map(|n| cmp::max(n, last_imported_number)), - blocks_received: if last_imported_number > self.starting_block { last_imported_number - self.starting_block } else { 0 }, - blocks_total: match self.highest_block { Some(x) if x > self.starting_block => x - self.starting_block, _ => 0 }, - num_peers: self.peers.values().filter(|p| p.is_allowed()).count(), - num_active_peers: self.peers.values().filter(|p| p.is_allowed() && p.asking != PeerAsking::Nothing).count(), - num_snapshot_chunks: self.snapshot.total_chunks(), - snapshot_chunks_done: self.snapshot.done_chunks(), - mem_used: - self.new_blocks.heap_size() - + self.old_blocks.as_ref().map_or(0, |d| d.heap_size()) - + self.peers.heap_size_of_children(), - } - } - - /// Returns information on peers connections - pub fn peer_info(&self, peer_id: &PeerId) -> Option { - self.peers.get(peer_id).map(|peer_data| { - PeerInfoDigest { - version: peer_data.protocol_version as u32, - difficulty: peer_data.difficulty, - head: peer_data.latest_hash, - } - }) - } - - /// Returns transactions propagation statistics - pub fn transactions_stats(&self) -> &H256FastMap { - self.transactions_stats.stats() - } - - /// Updates transactions were received by a peer - pub fn transactions_received(&mut self, txs: &[UnverifiedTransaction], peer_id: PeerId) { - if let Some(peer_info) = self.peers.get_mut(&peer_id) { - peer_info.last_sent_transactions.extend(txs.iter().map(|tx| tx.hash())); - } - } - - /// Abort all sync activity - pub fn abort(&mut self, io: &mut SyncIo) { - self.reset_and_continue(io); - self.peers.clear(); - } - - /// Reset sync. Clear all downloaded data but keep the queue. - /// Set sync state to the given state or to the initial state if `None` is provided. - fn reset(&mut self, io: &mut SyncIo, state: Option) { - self.new_blocks.reset(); - let chain_info = io.chain().chain_info(); - for (_, ref mut p) in &mut self.peers { - if p.block_set != Some(BlockSet::OldBlocks) { - p.reset_asking(); - if p.difficulty.is_none() { - // assume peer has up to date difficulty - p.difficulty = Some(chain_info.pending_total_difficulty); - } - } - } - self.state = state.unwrap_or_else(|| Self::get_init_state(self.warp_sync, io.chain())); - // Reactivate peers only if some progress has been made - // since the last sync round of if starting fresh. - self.active_peers = self.peers.keys().cloned().collect(); - } - - /// Restart sync - pub fn reset_and_continue(&mut self, io: &mut SyncIo) { - trace!(target: "sync", "Restarting"); - if self.state == SyncState::SnapshotData { - debug!(target:"sync", "Aborting snapshot restore"); - io.snapshot_service().abort_restore(); - } - self.snapshot.clear(); - self.reset(io, None); - self.continue_sync(io); - } - - /// Remove peer from active peer set. Peer will be reactivated on the next sync - /// round. - fn deactivate_peer(&mut self, _io: &mut SyncIo, peer_id: PeerId) { - trace!(target: "sync", "Deactivating peer {}", peer_id); - self.active_peers.remove(&peer_id); - } - - fn maybe_start_snapshot_sync(&mut self, io: &mut SyncIo) { - if !self.warp_sync.is_enabled() || io.snapshot_service().supported_versions().is_none() { - trace!(target: "sync", "Skipping warp sync. Disabled or not supported."); - return; - } - if self.state != SyncState::WaitingPeers && self.state != SyncState::Blocks && self.state != SyncState::Waiting { - trace!(target: "sync", "Skipping warp sync. State: {:?}", self.state); - return; - } - // Make sure the snapshot block is not too far away from best block and network best block and - // that it is higher than fork detection block - let our_best_block = io.chain().chain_info().best_block_number; - let fork_block = self.fork_block.map_or(0, |(n, _)| n); - - let (best_hash, max_peers, snapshot_peers) = { - let expected_warp_block = match self.warp_sync { - WarpSync::OnlyAndAfter(block) => block, - _ => 0, - }; - //collect snapshot infos from peers - let snapshots = self.peers.iter() - .filter(|&(_, p)| p.is_allowed() && p.snapshot_number.map_or(false, |sn| + pub fn new( + config: SyncConfig, + chain: &dyn BlockChainClient, + fork_filter: ForkFilterApi, + ) -> Self { + let chain_info = chain.chain_info(); + let best_block = chain.chain_info().best_block_number; + let state = Self::get_init_state(config.warp_sync, chain); + + let mut sync = ChainSync { + state, + starting_block: best_block, + highest_block: None, + peers: HashMap::new(), + handshaking_peers: HashMap::new(), + active_peers: HashSet::new(), + delayed_requests: Vec::new(), + delayed_requests_ids: HashSet::new(), + new_blocks: BlockDownloader::new( + BlockSet::NewBlocks, + &chain_info.best_block_hash, + chain_info.best_block_number, + ), + old_blocks: None, + last_sent_block_number: 0, + network_id: config.network_id, + fork_block: config.fork_block, + fork_filter, + download_old_blocks: config.download_old_blocks, + snapshot: Snapshot::new(), + sync_start_time: None, + transactions_stats: TransactionsStats::default(), + warp_sync: config.warp_sync, + }; + sync.update_targets(chain); + sync + } + + /// Returns synchonization status + pub fn status(&self) -> SyncStatus { + let last_imported_number = self.new_blocks.last_imported_block_number(); + let mut item_sizes = BTreeMap::::new(); + self.old_blocks + .as_ref() + .map_or((), |d| d.get_sizes(&mut item_sizes)); + self.new_blocks.get_sizes(&mut item_sizes); + + SyncStatus { + state: self.state.clone(), + protocol_version: ETH_PROTOCOL_VERSION_64.0, + network_id: self.network_id, + start_block_number: self.starting_block, + last_imported_block_number: Some(last_imported_number), + last_imported_old_block_number: self + .old_blocks + .as_ref() + .map(|d| d.last_imported_block_number()), + highest_block_number: self + .highest_block + .map(|n| cmp::max(n, last_imported_number)), + blocks_received: if last_imported_number > self.starting_block { + last_imported_number - self.starting_block + } else { + 0 + }, + blocks_total: match self.highest_block { + Some(x) if x > self.starting_block => x - self.starting_block, + _ => 0, + }, + num_peers: self.peers.values().filter(|p| p.is_allowed()).count(), + num_active_peers: self + .peers + .values() + .filter(|p| p.is_allowed() && p.asking != PeerAsking::Nothing) + .count(), + num_snapshot_chunks: self.snapshot.total_chunks(), + snapshot_chunks_done: self.snapshot.done_chunks(), + item_sizes: item_sizes, + } + } + + /// Returns information on peers connections + pub fn peer_info(&self, peer_id: &PeerId) -> Option { + self.peers.get(peer_id).map(|peer_data| PeerInfoDigest { + version: peer_data.protocol_version as u32, + difficulty: peer_data.difficulty, + head: peer_data.latest_hash, + }) + } + + /// Returns transactions propagation statistics + pub fn transactions_stats(&self) -> &H256FastMap { + self.transactions_stats.stats() + } + + /// Updates transactions were received by a peer + pub fn transactions_received(&mut self, txs: &[UnverifiedTransaction], peer_id: PeerId) { + if let Some(peer_info) = self.peers.get_mut(&peer_id) { + peer_info + .last_sent_transactions + .extend(txs.iter().map(|tx| tx.hash())); + } + } + + /// Abort all sync activity + pub fn abort(&mut self, io: &mut dyn SyncIo) { + self.reset_and_continue(io); + self.peers.clear(); + } + + /// Reset sync. Clear all downloaded data but keep the queue. + /// Set sync state to the given state or to the initial state if `None` is provided. + fn reset(&mut self, io: &mut dyn SyncIo, state: Option) { + self.new_blocks.reset(); + let chain_info = io.chain().chain_info(); + for (_, ref mut p) in &mut self.peers { + if p.block_set != Some(BlockSet::OldBlocks) { + p.reset_asking(); + if p.difficulty.is_none() { + // assume peer has up to date difficulty + p.difficulty = Some(chain_info.pending_total_difficulty); + } + } + } + self.state = state.unwrap_or_else(|| Self::get_init_state(self.warp_sync, io.chain())); + // Reactivate peers only if some progress has been made + // since the last sync round of if starting fresh. + self.active_peers = self.peers.keys().cloned().collect(); + } + + /// Add a request for later processing + pub fn add_delayed_request(&mut self, peer: PeerId, packet_id: u8, data: &[u8]) { + // Ignore the request, if there is a request already in queue with the same id + if !self.delayed_requests_ids.contains(&(peer, packet_id)) { + self.delayed_requests_ids.insert((peer, packet_id)); + self.delayed_requests.push((peer, packet_id, data.to_vec())); + debug!(target: "sync", "Delayed request with packet id {} from peer {} added", packet_id, peer); + } + } + + /// Drain and return all delayed requests + pub fn retrieve_delayed_requests(&mut self) -> Vec<(PeerId, u8, Vec)> { + self.delayed_requests_ids.clear(); + self.delayed_requests.drain(..).collect() + } + + /// Restart sync + pub fn reset_and_continue(&mut self, io: &mut dyn SyncIo) { + trace!(target: "sync", "Restarting"); + if self.state == SyncState::SnapshotData { + debug!(target:"sync", "Aborting snapshot restore"); + io.snapshot_service().abort_restore(); + } + self.snapshot.clear(); + self.reset(io, None); + self.continue_sync(io); + } + + /// Remove peer from active peer set. Peer will be reactivated on the next sync + /// round. + fn deactivate_peer(&mut self, _io: &mut dyn SyncIo, peer_id: PeerId) { + trace!(target: "sync", "Deactivating peer {}", peer_id); + self.active_peers.remove(&peer_id); + } + + fn maybe_start_snapshot_sync(&mut self, io: &mut dyn SyncIo) { + if !self.warp_sync.is_enabled() || io.snapshot_service().supported_versions().is_none() { + trace!(target: "sync", "Skipping warp sync. Disabled or not supported."); + return; + } + if self.state != SyncState::WaitingPeers + && self.state != SyncState::Blocks + && self.state != SyncState::Waiting + { + trace!(target: "sync", "Skipping warp sync. State: {:?}", self.state); + return; + } + // Make sure the snapshot block is not too far away from best block and network best block and + // that it is higher than fork detection block + let our_best_block = io.chain().chain_info().best_block_number; + let fork_block = self.fork_block.map_or(0, |(n, _)| n); + + let (best_hash, max_peers, snapshot_peers) = { + let expected_warp_block = match self.warp_sync { + WarpSync::OnlyAndAfter(block) => block, + _ => 0, + }; + //collect snapshot infos from peers + let snapshots = self + .peers + .iter() + .filter(|&(_, p)| { + p.is_allowed() + && p.snapshot_number.map_or(false, |sn| // Snapshot must be old enough that it's usefull to sync with it our_best_block < sn && (sn - our_best_block) > SNAPSHOT_RESTORE_THRESHOLD && // Snapshot must have been taken after the Fork @@ -777,160 +888,198 @@ impl ChainSync { // If we know a highest block, snapshot must be recent enough self.highest_block.map_or(true, |highest| { highest < sn || (highest - sn) <= SNAPSHOT_RESTORE_THRESHOLD - }) - )) - .filter_map(|(p, peer)| peer.snapshot_hash.map(|hash| (p, hash.clone()))) - .filter(|&(_, ref hash)| !self.snapshot.is_known_bad(hash)); - - let mut snapshot_peers = HashMap::new(); - let mut max_peers: usize = 0; - let mut best_hash = None; - for (p, hash) in snapshots { - let peers = snapshot_peers.entry(hash).or_insert_with(Vec::new); - peers.push(*p); - if peers.len() > max_peers { - max_peers = peers.len(); - best_hash = Some(hash); - } - } - (best_hash, max_peers, snapshot_peers) - }; - - let timeout = (self.state == SyncState::WaitingPeers) && self.sync_start_time.map_or(false, |t| t.elapsed() > WAIT_PEERS_TIMEOUT); - - if let (Some(hash), Some(peers)) = (best_hash, best_hash.map_or(None, |h| snapshot_peers.get(&h))) { - if max_peers >= SNAPSHOT_MIN_PEERS { - trace!(target: "sync", "Starting confirmed snapshot sync {:?} with {:?}", hash, peers); - self.start_snapshot_sync(io, peers); - } else if timeout { - trace!(target: "sync", "Starting unconfirmed snapshot sync {:?} with {:?}", hash, peers); - self.start_snapshot_sync(io, peers); - } - } else if timeout && !self.warp_sync.is_warp_only() { - trace!(target: "sync", "No snapshots found, starting full sync"); - self.state = SyncState::Idle; - self.continue_sync(io); - } - } - - fn start_snapshot_sync(&mut self, io: &mut SyncIo, peers: &[PeerId]) { - if !self.snapshot.have_manifest() { - for p in peers { - if self.peers.get(p).map_or(false, |p| p.asking == PeerAsking::Nothing) { - SyncRequester::request_snapshot_manifest(self, io, *p); - } - } - self.state = SyncState::SnapshotManifest; - trace!(target: "sync", "New snapshot sync with {:?}", peers); - } else { - self.state = SyncState::SnapshotData; - trace!(target: "sync", "Resumed snapshot sync with {:?}", peers); - } - } - - /// Restart sync disregarding the block queue status. May end up re-downloading up to QUEUE_SIZE blocks - pub fn restart(&mut self, io: &mut SyncIo) { - self.update_targets(io.chain()); - self.reset_and_continue(io); - } - - /// Update sync after the blockchain has been changed externally. - pub fn update_targets(&mut self, chain: &BlockChainClient) { - // Do not assume that the block queue/chain still has our last_imported_block - let chain = chain.chain_info(); - self.new_blocks = BlockDownloader::new(BlockSet::NewBlocks, &chain.best_block_hash, chain.best_block_number); - self.old_blocks = None; - if self.download_old_blocks { - if let (Some(ancient_block_hash), Some(ancient_block_number)) = (chain.ancient_block_hash, chain.ancient_block_number) { - - trace!(target: "sync", "Downloading old blocks from {:?} (#{}) till {:?} (#{:?})", ancient_block_hash, ancient_block_number, chain.first_block_hash, chain.first_block_number); - let mut downloader = BlockDownloader::new(BlockSet::OldBlocks, &ancient_block_hash, ancient_block_number); - if let Some(hash) = chain.first_block_hash { - trace!(target: "sync", "Downloader target set to {:?}", hash); - downloader.set_target(&hash); - } - self.old_blocks = Some(downloader); - } - } - } - - /// Resume downloading - pub fn continue_sync(&mut self, io: &mut SyncIo) { - if self.state == SyncState::Waiting { - trace!(target: "sync", "Waiting for the block queue"); - } else if self.state == SyncState::SnapshotWaiting { - trace!(target: "sync", "Waiting for the snapshot restoration"); - } else { - // Collect active peers that can sync - let mut peers: Vec<(PeerId, u8)> = self.peers.iter().filter_map(|(peer_id, peer)| - if peer.can_sync() && peer.asking == PeerAsking::Nothing && self.active_peers.contains(&peer_id) { - Some((*peer_id, peer.protocol_version)) - } else { - None - } - ).collect(); - - if peers.len() > 0 { - trace!( - target: "sync", - "Syncing with peers: {} active, {} available, {} total", - self.active_peers.len(), peers.len(), self.peers.len() - ); - - random::new().shuffle(&mut peers); // TODO (#646): sort by rating - // prefer peers with higher protocol version - peers.sort_by(|&(_, ref v1), &(_, ref v2)| v1.cmp(v2)); - - for (peer_id, _) in peers { - self.sync_peer(io, peer_id, false); - } - } - } - - if - (self.state == SyncState::Blocks || self.state == SyncState::NewBlocks) && - !self.peers.values().any(|p| p.asking != PeerAsking::Nothing && p.block_set != Some(BlockSet::OldBlocks) && p.can_sync()) - { - self.complete_sync(io); - } - } - - /// Called after all blocks have been downloaded - fn complete_sync(&mut self, io: &mut SyncIo) { - trace!(target: "sync", "Sync complete"); - self.reset(io, Some(SyncState::Idle)); - } - - /// Enter waiting state - fn pause_sync(&mut self) { - trace!(target: "sync", "Block queue full, pausing sync"); - self.state = SyncState::Waiting; - } - - /// Find something to do for a peer. Called for a new peer or when a peer is done with its task. - fn sync_peer(&mut self, io: &mut SyncIo, peer_id: PeerId, force: bool) { - if !self.active_peers.contains(&peer_id) { - trace!(target: "sync", "Skipping deactivated peer {}", peer_id); - return; - } - let (peer_latest, peer_difficulty, peer_snapshot_number, peer_snapshot_hash) = { - if let Some(peer) = self.peers.get_mut(&peer_id) { - if peer.asking != PeerAsking::Nothing || !peer.can_sync() { - trace!(target: "sync", "Skipping busy peer {}", peer_id); - return; - } - (peer.latest_hash.clone(), peer.difficulty.clone(), peer.snapshot_number.as_ref().cloned().unwrap_or(0), peer.snapshot_hash.as_ref().cloned()) - } else { - return; - } - }; - let chain_info = io.chain().chain_info(); - let syncing_difficulty = chain_info.pending_total_difficulty; - let num_active_peers = self.peers.values().filter(|p| p.asking != PeerAsking::Nothing).count(); - - let higher_difficulty = peer_difficulty.map_or(true, |pd| pd > syncing_difficulty); - if force || higher_difficulty || self.old_blocks.is_some() { - match self.state { + })) + }) + .filter_map(|(p, peer)| peer.snapshot_hash.map(|hash| (p, hash.clone()))) + .filter(|&(_, ref hash)| !self.snapshot.is_known_bad(hash)); + + let mut snapshot_peers = HashMap::new(); + let mut max_peers: usize = 0; + let mut best_hash = None; + for (p, hash) in snapshots { + let peers = snapshot_peers.entry(hash).or_insert_with(Vec::new); + peers.push(*p); + if peers.len() > max_peers { + max_peers = peers.len(); + best_hash = Some(hash); + } + } + (best_hash, max_peers, snapshot_peers) + }; + + let timeout = (self.state == SyncState::WaitingPeers) + && self + .sync_start_time + .map_or(false, |t| t.elapsed() > WAIT_PEERS_TIMEOUT); + + if let (Some(hash), Some(peers)) = ( + best_hash, + best_hash.map_or(None, |h| snapshot_peers.get(&h)), + ) { + if max_peers >= SNAPSHOT_MIN_PEERS { + trace!(target: "sync", "Starting confirmed snapshot sync {:?} with {:?}", hash, peers); + self.start_snapshot_sync(io, peers); + } else if timeout { + trace!(target: "sync", "Starting unconfirmed snapshot sync {:?} with {:?}", hash, peers); + self.start_snapshot_sync(io, peers); + } + } else if timeout && !self.warp_sync.is_warp_only() { + trace!(target: "sync", "No snapshots found, starting full sync"); + self.state = SyncState::Idle; + self.continue_sync(io); + } + } + + fn start_snapshot_sync(&mut self, io: &mut dyn SyncIo, peers: &[PeerId]) { + if !self.snapshot.have_manifest() { + for p in peers { + if self + .peers + .get(p) + .map_or(false, |p| p.asking == PeerAsking::Nothing) + { + SyncRequester::request_snapshot_manifest(self, io, *p); + } + } + self.state = SyncState::SnapshotManifest; + trace!(target: "sync", "New snapshot sync with {:?}", peers); + } else { + self.state = SyncState::SnapshotData; + trace!(target: "sync", "Resumed snapshot sync with {:?}", peers); + } + } + + /// Restart sync disregarding the block queue status. May end up re-downloading up to QUEUE_SIZE blocks + pub fn restart(&mut self, io: &mut dyn SyncIo) { + self.update_targets(io.chain()); + self.reset_and_continue(io); + } + + /// Update sync after the blockchain has been changed externally. + pub fn update_targets(&mut self, chain: &dyn BlockChainClient) { + // Do not assume that the block queue/chain still has our last_imported_block + let chain = chain.chain_info(); + self.new_blocks = BlockDownloader::new( + BlockSet::NewBlocks, + &chain.best_block_hash, + chain.best_block_number, + ); + self.old_blocks = None; + if self.download_old_blocks { + if let (Some(ancient_block_hash), Some(ancient_block_number)) = + (chain.ancient_block_hash, chain.ancient_block_number) + { + trace!(target: "sync", "Downloading old blocks from {:?} (#{}) till {:?} (#{:?})", ancient_block_hash, ancient_block_number, chain.first_block_hash, chain.first_block_number); + let mut downloader = BlockDownloader::new( + BlockSet::OldBlocks, + &ancient_block_hash, + ancient_block_number, + ); + if let Some(hash) = chain.first_block_hash { + trace!(target: "sync", "Downloader target set to {:?}", hash); + downloader.set_target(&hash); + } + self.old_blocks = Some(downloader); + } + } + } + + /// Resume downloading + pub fn continue_sync(&mut self, io: &mut dyn SyncIo) { + if self.state == SyncState::Waiting { + trace!(target: "sync", "Waiting for the block queue"); + } else if self.state == SyncState::SnapshotWaiting { + trace!(target: "sync", "Waiting for the snapshot restoration"); + } else { + // Collect active peers that can sync + let mut peers: Vec<(PeerId, u8)> = self + .peers + .iter() + .filter_map(|(peer_id, peer)| { + if peer.can_sync() + && peer.asking == PeerAsking::Nothing + && self.active_peers.contains(&peer_id) + { + Some((*peer_id, peer.protocol_version)) + } else { + None + } + }) + .collect(); + + if peers.len() > 0 { + trace!( + target: "sync", + "Syncing with peers: {} active, {} available, {} total", + self.active_peers.len(), peers.len(), self.peers.len() + ); + + random::new().shuffle(&mut peers); // TODO (#646): sort by rating + // prefer peers with higher protocol version + peers.sort_by(|&(_, ref v1), &(_, ref v2)| v1.cmp(v2)); + + for (peer_id, _) in peers { + self.sync_peer(io, peer_id, false); + } + } + } + + if (self.state == SyncState::Blocks || self.state == SyncState::NewBlocks) + && !self.peers.values().any(|p| { + p.asking != PeerAsking::Nothing + && p.block_set != Some(BlockSet::OldBlocks) + && p.can_sync() + }) + { + self.complete_sync(io); + } + } + + /// Called after all blocks have been downloaded + fn complete_sync(&mut self, io: &mut dyn SyncIo) { + trace!(target: "sync", "Sync complete"); + self.reset(io, Some(SyncState::Idle)); + } + + /// Enter waiting state + fn pause_sync(&mut self) { + trace!(target: "sync", "Block queue full, pausing sync"); + self.state = SyncState::Waiting; + } + + /// Find something to do for a peer. Called for a new peer or when a peer is done with its task. + fn sync_peer(&mut self, io: &mut dyn SyncIo, peer_id: PeerId, force: bool) { + if !self.active_peers.contains(&peer_id) { + trace!(target: "sync", "Skipping deactivated peer {}", peer_id); + return; + } + let (peer_latest, peer_difficulty, peer_snapshot_number, peer_snapshot_hash) = { + if let Some(peer) = self.peers.get_mut(&peer_id) { + if peer.asking != PeerAsking::Nothing || !peer.can_sync() { + trace!(target: "sync", "Skipping busy peer {}", peer_id); + return; + } + ( + peer.latest_hash.clone(), + peer.difficulty.clone(), + peer.snapshot_number.as_ref().cloned().unwrap_or(0), + peer.snapshot_hash.as_ref().cloned(), + ) + } else { + return; + } + }; + let chain_info = io.chain().chain_info(); + let syncing_difficulty = chain_info.pending_total_difficulty; + let num_active_peers = self + .peers + .values() + .filter(|p| p.asking != PeerAsking::Nothing) + .count(); + + let higher_difficulty = peer_difficulty.map_or(true, |pd| pd > syncing_difficulty); + if force || higher_difficulty || self.old_blocks.is_some() { + match self.state { SyncState::WaitingPeers => { trace!( target: "sync", @@ -962,7 +1111,7 @@ impl ChainSync { } // Only ask for old blocks if the peer has an equal or higher difficulty - let equal_or_higher_difficulty = peer_difficulty.map_or(false, |pd| pd >= syncing_difficulty); + let equal_or_higher_difficulty = peer_difficulty.map_or(true, |pd| pd >= syncing_difficulty); if force || equal_or_higher_difficulty { if let Some(request) = self.old_blocks.as_mut().and_then(|d| d.request_blocks(peer_id, io, num_active_peers)) { @@ -981,7 +1130,7 @@ impl ChainSync { } }, SyncState::SnapshotData => { - match io.snapshot_service().status() { + match io.snapshot_service().restoration_status() { RestorationStatus::Ongoing { state_chunks_done, block_chunks_done, .. } => { // Initialize the snapshot if not already done self.snapshot.initialize(io.snapshot_service()); @@ -1009,214 +1158,233 @@ impl ChainSync { SyncState::Waiting | SyncState::SnapshotWaiting => () } - } else { - trace!(target: "sync", "Skipping peer {}, force={}, td={:?}, our td={}, state={:?}", peer_id, force, peer_difficulty, syncing_difficulty, self.state); - } - } - - /// Clear all blocks/headers marked as being downloaded by a peer. - fn clear_peer_download(&mut self, peer_id: PeerId) { - if let Some(ref peer) = self.peers.get(&peer_id) { - match peer.asking { - PeerAsking::BlockHeaders => { - if let Some(ref hash) = peer.asking_hash { - self.new_blocks.clear_header_download(hash); - if let Some(ref mut old) = self.old_blocks { - old.clear_header_download(hash); - } - } - }, - PeerAsking::BlockBodies => { - self.new_blocks.clear_body_download(&peer.asking_blocks); - if let Some(ref mut old) = self.old_blocks { - old.clear_body_download(&peer.asking_blocks); - } - }, - PeerAsking::BlockReceipts => { - self.new_blocks.clear_receipt_download(&peer.asking_blocks); - if let Some(ref mut old) = self.old_blocks { - old.clear_receipt_download(&peer.asking_blocks); - } - }, - PeerAsking::SnapshotData => { - if let Some(hash) = peer.asking_snapshot_data { - self.snapshot.clear_chunk_download(&hash); - } - }, - _ => (), - } - } - } - - /// Checks if there are blocks fully downloaded that can be imported into the blockchain and does the import. - fn collect_blocks(&mut self, io: &mut SyncIo, block_set: BlockSet) { - match block_set { - BlockSet::NewBlocks => { - if self.new_blocks.collect_blocks(io, self.state == SyncState::NewBlocks) == DownloadAction::Reset { - self.reset_downloads(block_set); - self.new_blocks.reset(); - } - }, - BlockSet::OldBlocks => { - let mut is_complete = false; - let mut download_action = DownloadAction::None; - if let Some(downloader) = self.old_blocks.as_mut() { - download_action = downloader.collect_blocks(io, false); - is_complete = downloader.is_complete(); - } - - if download_action == DownloadAction::Reset { - self.reset_downloads(block_set); - if let Some(downloader) = self.old_blocks.as_mut() { - downloader.reset(); - } - } - - if is_complete { - trace!(target: "sync", "Background block download is complete"); - self.old_blocks = None; - } - } - }; - } - - /// Mark all outstanding requests as expired - fn reset_downloads(&mut self, block_set: BlockSet) { - trace!(target: "sync", "Resetting downloads for {:?}", block_set); - for (_, ref mut p) in self.peers.iter_mut().filter(|&(_, ref p)| p.block_set == Some(block_set)) { - p.reset_asking(); - } - } - - /// Reset peer status after request is complete. - fn reset_peer_asking(&mut self, peer_id: PeerId, asking: PeerAsking) -> bool { - if let Some(ref mut peer) = self.peers.get_mut(&peer_id) { - peer.expired = false; - peer.block_set = None; - if peer.asking != asking { - trace!(target:"sync", "Asking {:?} while expected {:?}", peer.asking, asking); - peer.asking = PeerAsking::Nothing; - return false; - } else { - peer.asking = PeerAsking::Nothing; - return true; - } - } - false - } - - /// Send Status message - fn send_status(&mut self, io: &mut SyncIo, peer: PeerId) -> Result<(), network::Error> { - let warp_protocol_version = io.protocol_version(&WARP_SYNC_PROTOCOL_ID, peer); - let warp_protocol = warp_protocol_version != 0; - let private_tx_protocol = warp_protocol_version >= PAR_PROTOCOL_VERSION_3.0; - let protocol = if warp_protocol { warp_protocol_version } else { ETH_PROTOCOL_VERSION_63.0 }; - trace!(target: "sync", "Sending status to {}, protocol version {}", peer, protocol); - let mut packet = RlpStream::new(); - packet.begin_unbounded_list(); - let chain = io.chain().chain_info(); - packet.append(&(protocol as u32)); - packet.append(&self.network_id); - packet.append(&chain.total_difficulty); - packet.append(&chain.best_block_hash); - packet.append(&chain.genesis_hash); - if warp_protocol { - let manifest = io.snapshot_service().manifest(); - let block_number = manifest.as_ref().map_or(0, |m| m.block_number); - let manifest_hash = manifest.map_or(H256::new(), |m| keccak(m.into_rlp())); - packet.append(&manifest_hash); - packet.append(&block_number); - if private_tx_protocol { - packet.append(&self.private_tx_handler.is_some()); - } - } - packet.complete_unbounded_list(); - io.respond(StatusPacket.id(), packet.out()) - } - - pub fn maintain_peers(&mut self, io: &mut SyncIo) { - let tick = Instant::now(); - let mut aborting = Vec::new(); - for (peer_id, peer) in &self.peers { - let elapsed = tick - peer.ask_time; - let timeout = match peer.asking { - PeerAsking::BlockHeaders => elapsed > HEADERS_TIMEOUT, - PeerAsking::BlockBodies => elapsed > BODIES_TIMEOUT, - PeerAsking::BlockReceipts => elapsed > RECEIPTS_TIMEOUT, - PeerAsking::Nothing => false, - PeerAsking::ForkHeader => elapsed > FORK_HEADER_TIMEOUT, - PeerAsking::SnapshotManifest => elapsed > SNAPSHOT_MANIFEST_TIMEOUT, - PeerAsking::SnapshotData => elapsed > SNAPSHOT_DATA_TIMEOUT, - }; - if timeout { - debug!(target:"sync", "Timeout {}", peer_id); - io.disconnect_peer(*peer_id); - aborting.push(*peer_id); - } - } - for p in aborting { - SyncHandler::on_peer_aborting(self, io, p); - } - - // Check for handshake timeouts - for (peer, &ask_time) in &self.handshaking_peers { - let elapsed = (tick - ask_time) / 1_000_000_000; - if elapsed > STATUS_TIMEOUT { - trace!(target:"sync", "Status timeout {}", peer); - io.disconnect_peer(*peer); - } - } - } - - fn check_resume(&mut self, io: &mut SyncIo) { - match self.state { - SyncState::Waiting if !io.chain().queue_info().is_full() => { - self.state = SyncState::Blocks; - self.continue_sync(io); - }, - SyncState::SnapshotData => match io.snapshot_service().status() { - RestorationStatus::Inactive | RestorationStatus::Failed => { - self.state = SyncState::SnapshotWaiting; - }, - RestorationStatus::Initializing { .. } | RestorationStatus::Ongoing { .. } => (), - }, - SyncState::SnapshotWaiting => { - match io.snapshot_service().status() { - RestorationStatus::Inactive => { - trace!(target:"sync", "Snapshot restoration is complete"); - self.restart(io); - }, - RestorationStatus::Initializing { .. } => { - trace!(target:"sync", "Snapshot restoration is initializing"); - }, - RestorationStatus::Ongoing { state_chunks_done, block_chunks_done, .. } => { - if !self.snapshot.is_complete() && self.snapshot.done_chunks() - (state_chunks_done + block_chunks_done) as usize <= MAX_SNAPSHOT_CHUNKS_DOWNLOAD_AHEAD { - trace!(target:"sync", "Resuming snapshot sync"); - self.state = SyncState::SnapshotData; - self.continue_sync(io); - } - }, - RestorationStatus::Failed => { - trace!(target: "sync", "Snapshot restoration aborted"); - self.state = SyncState::WaitingPeers; - self.snapshot.clear(); - self.continue_sync(io); - }, - } - }, - _ => (), - } - } - - /// returns peer ids that have different block than our chain - fn get_lagging_peers(&self, chain_info: &BlockChainInfo) -> Vec { - self.get_peers(chain_info, PeerState::Lagging) - } - - /// returns peer ids that have different or the same blocks than our chain - fn get_peers(&self, chain_info: &BlockChainInfo, peers: PeerState) -> Vec { - let latest_hash = chain_info.best_block_hash; - self + } else { + trace!(target: "sync", "Skipping peer {}, force={}, td={:?}, our td={}, state={:?}", peer_id, force, peer_difficulty, syncing_difficulty, self.state); + } + } + + /// Clear all blocks/headers marked as being downloaded by a peer. + fn clear_peer_download(&mut self, peer_id: PeerId) { + if let Some(ref peer) = self.peers.get(&peer_id) { + match peer.asking { + PeerAsking::BlockHeaders => { + if let Some(ref hash) = peer.asking_hash { + self.new_blocks.clear_header_download(hash); + if let Some(ref mut old) = self.old_blocks { + old.clear_header_download(hash); + } + } + } + PeerAsking::BlockBodies => { + self.new_blocks.clear_body_download(&peer.asking_blocks); + if let Some(ref mut old) = self.old_blocks { + old.clear_body_download(&peer.asking_blocks); + } + } + PeerAsking::BlockReceipts => { + self.new_blocks.clear_receipt_download(&peer.asking_blocks); + if let Some(ref mut old) = self.old_blocks { + old.clear_receipt_download(&peer.asking_blocks); + } + } + PeerAsking::SnapshotData => { + if let Some(hash) = peer.asking_snapshot_data { + self.snapshot.clear_chunk_download(&hash); + } + } + _ => (), + } + } + } + + /// Checks if there are blocks fully downloaded that can be imported into the blockchain and does the import. + fn collect_blocks(&mut self, io: &mut dyn SyncIo, block_set: BlockSet) { + match block_set { + BlockSet::NewBlocks => { + if self + .new_blocks + .collect_blocks(io, self.state == SyncState::NewBlocks) + == DownloadAction::Reset + { + self.reset_downloads(block_set); + self.new_blocks.reset(); + } + } + BlockSet::OldBlocks => { + let mut is_complete = false; + let mut download_action = DownloadAction::None; + if let Some(downloader) = self.old_blocks.as_mut() { + download_action = downloader.collect_blocks(io, false); + is_complete = downloader.is_complete(); + } + + if download_action == DownloadAction::Reset { + self.reset_downloads(block_set); + if let Some(downloader) = self.old_blocks.as_mut() { + downloader.reset(); + } + } + + if is_complete { + trace!(target: "sync", "Background block download is complete"); + self.old_blocks = None; + } + } + }; + } + + /// Mark all outstanding requests as expired + fn reset_downloads(&mut self, block_set: BlockSet) { + trace!(target: "sync", "Resetting downloads for {:?}", block_set); + for (_, ref mut p) in self + .peers + .iter_mut() + .filter(|&(_, ref p)| p.block_set == Some(block_set)) + { + p.reset_asking(); + } + } + + /// Reset peer status after request is complete. + fn reset_peer_asking(&mut self, peer_id: PeerId, asking: PeerAsking) -> bool { + if let Some(ref mut peer) = self.peers.get_mut(&peer_id) { + peer.expired = false; + peer.block_set = None; + if peer.asking != asking { + trace!(target:"sync", "Asking {:?} while expected {:?}", peer.asking, asking); + peer.asking = PeerAsking::Nothing; + return false; + } else { + peer.asking = PeerAsking::Nothing; + return true; + } + } + false + } + + /// Send Status message + fn send_status(&mut self, io: &mut dyn SyncIo, peer: PeerId) -> Result<(), network::Error> { + let eth_protocol_version = io.protocol_version(Ð_PROTOCOL, peer); + let warp_protocol_version = io.protocol_version(&PAR_PROTOCOL, peer); + let warp_protocol = warp_protocol_version != 0; + let protocol = if warp_protocol { + warp_protocol_version + } else { + eth_protocol_version + }; + trace!(target: "sync", "Sending status to {}, protocol version {}", peer, protocol); + + let mut packet = rlp04::RlpStream::new(); + packet.begin_unbounded_list(); + let chain = io.chain().chain_info(); + packet.append(&(protocol as u32)); + packet.append(&self.network_id); + packet.append(&primitive_types07::U256(chain.total_difficulty.0)); + packet.append(&primitive_types07::H256(chain.best_block_hash.0)); + packet.append(&primitive_types07::H256(chain.genesis_hash.0)); + if eth_protocol_version >= ETH_PROTOCOL_VERSION_64.0 { + packet.append(&self.fork_filter.current(io.chain())); + } + if warp_protocol { + let manifest = io.snapshot_service().manifest(); + let block_number = manifest.as_ref().map_or(0, |m| m.block_number); + let manifest_hash = manifest.map_or(H256::new(), |m| keccak(m.into_rlp())); + packet.append(&primitive_types07::H256(manifest_hash.0)); + packet.append(&block_number); + } + packet.finalize_unbounded_list(); + io.respond(StatusPacket.id(), packet.out()) + } + + pub fn maintain_peers(&mut self, io: &mut dyn SyncIo) { + let tick = Instant::now(); + let mut aborting = Vec::new(); + for (peer_id, peer) in &self.peers { + let elapsed = tick - peer.ask_time; + let timeout = match peer.asking { + PeerAsking::BlockHeaders => elapsed > HEADERS_TIMEOUT, + PeerAsking::BlockBodies => elapsed > BODIES_TIMEOUT, + PeerAsking::BlockReceipts => elapsed > RECEIPTS_TIMEOUT, + PeerAsking::Nothing => false, + PeerAsking::ForkHeader => elapsed > FORK_HEADER_TIMEOUT, + PeerAsking::SnapshotManifest => elapsed > SNAPSHOT_MANIFEST_TIMEOUT, + PeerAsking::SnapshotData => elapsed > SNAPSHOT_DATA_TIMEOUT, + }; + if timeout { + debug!(target:"sync", "Timeout {}", peer_id); + io.disconnect_peer(*peer_id); + aborting.push(*peer_id); + } + } + for p in aborting { + SyncHandler::on_peer_aborting(self, io, p); + } + + // Check for handshake timeouts + for (peer, &ask_time) in &self.handshaking_peers { + let elapsed = (tick - ask_time) / 1_000_000_000; + if elapsed > STATUS_TIMEOUT { + trace!(target:"sync", "Status timeout {}", peer); + io.disconnect_peer(*peer); + } + } + } + + fn check_resume(&mut self, io: &mut dyn SyncIo) { + match self.state { + SyncState::Waiting if !io.chain().queue_info().is_full() => { + self.state = SyncState::Blocks; + self.continue_sync(io); + } + SyncState::SnapshotData => match io.snapshot_service().restoration_status() { + RestorationStatus::Inactive | RestorationStatus::Failed => { + self.state = SyncState::SnapshotWaiting; + } + RestorationStatus::Initializing { .. } | RestorationStatus::Ongoing { .. } => (), + }, + SyncState::SnapshotWaiting => match io.snapshot_service().restoration_status() { + RestorationStatus::Inactive => { + trace!(target:"sync", "Snapshot restoration is complete"); + self.restart(io); + } + RestorationStatus::Initializing { .. } => { + trace!(target:"sync", "Snapshot restoration is initializing"); + } + RestorationStatus::Ongoing { + state_chunks_done, + block_chunks_done, + .. + } => { + if !self.snapshot.is_complete() + && self.snapshot.done_chunks() + - (state_chunks_done + block_chunks_done) as usize + <= MAX_SNAPSHOT_CHUNKS_DOWNLOAD_AHEAD + { + trace!(target:"sync", "Resuming snapshot sync"); + self.state = SyncState::SnapshotData; + self.continue_sync(io); + } + } + RestorationStatus::Failed => { + trace!(target: "sync", "Snapshot restoration aborted"); + self.state = SyncState::WaitingPeers; + self.snapshot.clear(); + self.continue_sync(io); + } + }, + _ => (), + } + } + + /// returns peer ids that have different block than our chain + fn get_lagging_peers(&self, chain_info: &BlockChainInfo) -> Vec { + self.get_peers(chain_info, PeerState::Lagging) + } + + /// returns peer ids that have different or the same blocks than our chain + fn get_peers(&self, chain_info: &BlockChainInfo, peers: PeerState) -> Vec { + let latest_hash = chain_info.best_block_hash; + self .peers .iter() .filter_map(|(&id, ref mut peer_info)| { @@ -1232,367 +1400,393 @@ impl ChainSync { } }) .collect::>() - } - - fn get_consensus_peers(&self) -> Vec { - self.peers.iter().filter_map(|(id, p)| if p.protocol_version >= PAR_PROTOCOL_VERSION_2.0 { Some(*id) } else { None }).collect() - } - - fn get_private_transaction_peers(&self, transaction_hash: &H256) -> Vec { - self.peers.iter().filter_map( - |(id, p)| if p.protocol_version >= PAR_PROTOCOL_VERSION_3.0 - && !p.last_sent_private_transactions.contains(transaction_hash) - && p.private_tx_enabled { - Some(*id) - } else { - None - } - ).collect() - } - - /// Maintain other peers. Send out any new blocks and transactions - pub fn maintain_sync(&mut self, io: &mut SyncIo) { - self.maybe_start_snapshot_sync(io); - self.check_resume(io); - } - - /// called when block is imported to chain - propagates the blocks and updates transactions sent to peers - pub fn chain_new_blocks(&mut self, io: &mut SyncIo, _imported: &[H256], invalid: &[H256], enacted: &[H256], _retracted: &[H256], sealed: &[H256], proposed: &[Bytes]) { - let queue_info = io.chain().queue_info(); - let is_syncing = self.status().is_syncing(queue_info); - - if !is_syncing || !sealed.is_empty() || !proposed.is_empty() { - trace!(target: "sync", "Propagating blocks, state={:?}", self.state); - SyncPropagator::propagate_latest_blocks(self, io, sealed); - SyncPropagator::propagate_proposed_blocks(self, io, proposed); - } - if !invalid.is_empty() { - trace!(target: "sync", "Bad blocks in the queue, restarting"); - self.restart(io); - } - - if !is_syncing && !enacted.is_empty() && !self.peers.is_empty() { - // Select random peer to re-broadcast transactions to. - let peer = random::new().gen_range(0, self.peers.len()); - trace!(target: "sync", "Re-broadcasting transactions to a random peer."); - self.peers.values_mut().nth(peer).map(|peer_info| { - peer_info.last_sent_transactions.clear(); - peer_info.reset_private_stats() - } - ); - } - } - - pub fn on_packet(&mut self, io: &mut SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) { - SyncHandler::on_packet(self, io, peer, packet_id, data); - } - - /// Called by peer when it is disconnecting - pub fn on_peer_aborting(&mut self, io: &mut SyncIo, peer: PeerId) { - SyncHandler::on_peer_aborting(self, io, peer); - } - - /// Called when a new peer is connected - pub fn on_peer_connected(&mut self, io: &mut SyncIo, peer: PeerId) { - SyncHandler::on_peer_connected(self, io, peer); - } - - /// propagates new transactions to all peers - pub fn propagate_new_transactions(&mut self, io: &mut SyncIo) { - let deadline = Instant::now() + Duration::from_millis(500); - SyncPropagator::propagate_new_transactions(self, io, || { - if deadline > Instant::now() { - true - } else { - debug!(target: "sync", "Wasn't able to finish transaction propagation within a deadline."); - false - } - }); - } - - /// Broadcast consensus message to peers. - pub fn propagate_consensus_packet(&mut self, io: &mut SyncIo, packet: Bytes) { - SyncPropagator::propagate_consensus_packet(self, io, packet); - } - - /// Broadcast private transaction message to peers. - pub fn propagate_private_transaction(&mut self, io: &mut SyncIo, transaction_hash: H256, packet_id: SyncPacket, packet: Bytes) { - SyncPropagator::propagate_private_transaction(self, io, transaction_hash, packet_id, packet); - } + } + + fn get_consensus_peers(&self) -> Vec { + self.peers + .iter() + .filter_map(|(id, p)| { + if p.protocol_version >= PAR_PROTOCOL_VERSION_2.0 { + Some(*id) + } else { + None + } + }) + .collect() + } + + /// Maintain other peers. Send out any new blocks and transactions + pub fn maintain_sync(&mut self, io: &mut dyn SyncIo) { + self.maybe_start_snapshot_sync(io); + self.check_resume(io); + } + + /// called when block is imported to chain - propagates the blocks and updates transactions sent to peers + pub fn chain_new_blocks( + &mut self, + io: &mut dyn SyncIo, + _imported: &[H256], + invalid: &[H256], + enacted: &[H256], + _retracted: &[H256], + sealed: &[H256], + proposed: &[Bytes], + ) { + let queue_info = io.chain().queue_info(); + let is_syncing = self.status().is_syncing(queue_info); + + if !is_syncing || !sealed.is_empty() || !proposed.is_empty() { + trace!(target: "sync", "Propagating blocks, state={:?}", self.state); + SyncPropagator::propagate_latest_blocks(self, io, sealed); + SyncPropagator::propagate_proposed_blocks(self, io, proposed); + } + if !invalid.is_empty() { + trace!(target: "sync", "Bad blocks in the queue, restarting"); + self.restart(io); + } + + if !is_syncing && !enacted.is_empty() && !self.peers.is_empty() { + // Select random peer to re-broadcast transactions to. + let peer = random::new().gen_range(0, self.peers.len()); + trace!(target: "sync", "Re-broadcasting transactions to a random peer."); + self.peers.values_mut().nth(peer).map(|peer_info| { + peer_info.last_sent_transactions.clear(); + }); + } + } + + pub fn on_packet(&mut self, io: &mut dyn SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) { + SyncHandler::on_packet(self, io, peer, packet_id, data); + } + + /// Called by peer when it is disconnecting + pub fn on_peer_aborting(&mut self, io: &mut dyn SyncIo, peer: PeerId) { + SyncHandler::on_peer_aborting(self, io, peer); + } + + /// Called when a new peer is connected + pub fn on_peer_connected(&mut self, io: &mut dyn SyncIo, peer: PeerId) { + SyncHandler::on_peer_connected(self, io, peer); + } + + /// propagates new transactions to all peers + pub fn propagate_new_transactions(&mut self, io: &mut dyn SyncIo) { + let deadline = Instant::now() + Duration::from_millis(500); + SyncPropagator::propagate_new_transactions(self, io, || { + if deadline > Instant::now() { + true + } else { + debug!(target: "sync", "Wasn't able to finish transaction propagation within a deadline."); + false + } + }); + } + + /// Broadcast consensus message to peers. + pub fn propagate_consensus_packet(&mut self, io: &mut dyn SyncIo, packet: Bytes) { + SyncPropagator::propagate_consensus_packet(self, io, packet); + } } #[cfg(test)] pub mod tests { - use std::collections::{VecDeque}; - use ethkey; - use network::PeerId; - use tests::helpers::{TestIo}; - use tests::snapshot::TestSnapshotService; - use ethereum_types::{H256, U256, Address}; - use parking_lot::RwLock; - use bytes::Bytes; - use rlp::{Rlp, RlpStream}; - use super::*; - use ::SyncConfig; - use super::{PeerInfo, PeerAsking}; - use ethcore::client::{BlockChainClient, EachBlockWith, TestBlockChainClient, ChainInfo, BlockInfo}; - use ethcore::miner::{MinerService, PendingOrdering}; - use types::header::Header; - - pub fn get_dummy_block(order: u32, parent_hash: H256) -> Bytes { - let mut header = Header::new(); - header.set_gas_limit(0.into()); - header.set_difficulty((order * 100).into()); - header.set_timestamp((order * 10) as u64); - header.set_number(order as u64); - header.set_parent_hash(parent_hash); - header.set_state_root(H256::zero()); - - let mut rlp = RlpStream::new_list(3); - rlp.append(&header); - rlp.append_raw(&::rlp::EMPTY_LIST_RLP, 1); - rlp.append_raw(&::rlp::EMPTY_LIST_RLP, 1); - rlp.out() - } - - pub fn get_dummy_blocks(order: u32, parent_hash: H256) -> Bytes { - let mut rlp = RlpStream::new_list(2); - rlp.append_raw(&get_dummy_block(order, parent_hash), 1); - let difficulty: U256 = (100 * order).into(); - rlp.append(&difficulty); - rlp.out() - } - - pub fn get_dummy_hashes() -> Bytes { - let mut rlp = RlpStream::new_list(5); - for _ in 0..5 { - let mut hash_d_rlp = RlpStream::new_list(2); - let hash: H256 = H256::from(0u64); - let diff: U256 = U256::from(1u64); - hash_d_rlp.append(&hash); - hash_d_rlp.append(&diff); - - rlp.append_raw(&hash_d_rlp.out(), 1); - } - - rlp.out() - } - - fn queue_info(unverified: usize, verified: usize) -> BlockQueueInfo { - BlockQueueInfo { - unverified_queue_size: unverified, - verified_queue_size: verified, - verifying_queue_size: 0, - max_queue_size: 1000, - max_mem_use: 1000, - mem_used: 500 - } - } - - fn sync_status(state: SyncState) -> SyncStatus { - SyncStatus { - state: state, - protocol_version: 0, - network_id: 0, - start_block_number: 0, - last_imported_block_number: None, - highest_block_number: None, - blocks_total: 0, - blocks_received: 0, - num_peers: 0, - num_active_peers: 0, - mem_used: 0, - num_snapshot_chunks: 0, - snapshot_chunks_done: 0, - last_imported_old_block_number: None, - } - } - - #[test] - fn is_still_verifying() { - assert!(!sync_status(SyncState::Idle).is_syncing(queue_info(2, 1))); - assert!(sync_status(SyncState::Idle).is_syncing(queue_info(2, 2))); - } - - #[test] - fn is_synced_state() { - assert!(sync_status(SyncState::Blocks).is_syncing(queue_info(0, 0))); - assert!(!sync_status(SyncState::Idle).is_syncing(queue_info(0, 0))); - } - - pub fn dummy_sync_with_peer(peer_latest_hash: H256, client: &BlockChainClient) -> ChainSync { - let mut sync = ChainSync::new(SyncConfig::default(), client, None); - insert_dummy_peer(&mut sync, 0, peer_latest_hash); - sync - } - - pub fn insert_dummy_peer(sync: &mut ChainSync, peer_id: PeerId, peer_latest_hash: H256) { - sync.peers.insert(peer_id, - PeerInfo { - protocol_version: 0, - genesis: H256::zero(), - network_id: 0, - latest_hash: peer_latest_hash, - difficulty: None, - asking: PeerAsking::Nothing, - asking_blocks: Vec::new(), - asking_hash: None, - ask_time: Instant::now(), - last_sent_transactions: Default::default(), - last_sent_private_transactions: Default::default(), - expired: false, - private_tx_enabled: false, - confirmation: super::ForkConfirmation::Confirmed, - snapshot_number: None, - snapshot_hash: None, - asking_snapshot_data: None, - block_set: None, - client_version: ClientVersion::from(""), - }); - - } - - #[test] - fn finds_lagging_peers() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - let sync = dummy_sync_with_peer(client.block_hash_delta_minus(10), &client); - let chain_info = client.chain_info(); - - let lagging_peers = sync.get_lagging_peers(&chain_info); - - assert_eq!(1, lagging_peers.len()); - } - - #[test] - fn calculates_tree_for_lagging_peer() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(15, EachBlockWith::Uncle); - - let start = client.block_hash_delta_minus(4); - let end = client.block_hash_delta_minus(2); - - // wrong way end -> start, should be None - let rlp = ChainSync::create_new_hashes_rlp(&client, &end, &start); - assert!(rlp.is_none()); - - let rlp = ChainSync::create_new_hashes_rlp(&client, &start, &end).unwrap(); - // size of three rlp encoded hash-difficulty - assert_eq!(107, rlp.len()); - } - // idea is that what we produce when propagading latest hashes should be accepted in - // on_peer_new_hashes in our code as well - #[test] - fn hashes_rlp_mutually_acceptable() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - let queue = RwLock::new(VecDeque::new()); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - let chain_info = client.chain_info(); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - let peers = sync.get_lagging_peers(&chain_info); - SyncPropagator::propagate_new_hashes(&mut sync, &chain_info, &mut io, &peers); - - let data = &io.packets[0].data.clone(); - let result = SyncHandler::on_peer_new_hashes(&mut sync, &mut io, 0, &Rlp::new(data)); - assert!(result.is_ok()); - } - - // idea is that what we produce when propagading latest block should be accepted in - // on_peer_new_block in our code as well - #[test] - fn block_rlp_mutually_acceptable() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - let queue = RwLock::new(VecDeque::new()); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - let chain_info = client.chain_info(); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - let peers = sync.get_lagging_peers(&chain_info); - SyncPropagator::propagate_blocks(&mut sync, &chain_info, &mut io, &[], &peers); - - let data = &io.packets[0].data.clone(); - let result = SyncHandler::on_peer_new_block(&mut sync, &mut io, 0, &Rlp::new(data)); - assert!(result.is_ok()); - } - - #[test] - fn should_add_transactions_to_queue() { - fn sender(tx: &UnverifiedTransaction) -> Address { - ethkey::public_to_address(&tx.recover_public().unwrap()) - } - - // given - let mut client = TestBlockChainClient::new(); - client.add_blocks(98, EachBlockWith::Uncle); - client.add_blocks(1, EachBlockWith::UncleAndTransaction); - client.add_blocks(1, EachBlockWith::Transaction); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - - let good_blocks = vec![client.block_hash_delta_minus(2)]; - let retracted_blocks = vec![client.block_hash_delta_minus(1)]; - - // Add some balance to clients and reset nonces - for h in &[good_blocks[0], retracted_blocks[0]] { - let block = client.block(BlockId::Hash(*h)).unwrap(); - let sender = sender(&block.transactions()[0]);; - client.set_balance(sender, U256::from(10_000_000_000_000_000_000u64)); - client.set_nonce(sender, U256::from(0)); - } - - // when - { - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - io.chain.miner.chain_new_blocks(io.chain, &[], &[], &[], &good_blocks, false); - sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks, &[], &[]); - assert_eq!(io.chain.miner.ready_transactions(io.chain, 10, PendingOrdering::Priority).len(), 1); - } - // We need to update nonce status (because we say that the block has been imported) - for h in &[good_blocks[0]] { - let block = client.block(BlockId::Hash(*h)).unwrap(); - client.set_nonce(sender(&block.transactions()[0]), U256::from(1)); - } - { - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&client, &ss, &queue, None); - io.chain.miner.chain_new_blocks(io.chain, &[], &[], &good_blocks, &retracted_blocks, false); - sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks, &[], &[]); - } - - // then - assert_eq!(client.miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 1); - } - - #[test] - fn should_not_add_transactions_to_queue_if_not_synced() { - // given - let mut client = TestBlockChainClient::new(); - client.add_blocks(98, EachBlockWith::Uncle); - client.add_blocks(1, EachBlockWith::UncleAndTransaction); - client.add_blocks(1, EachBlockWith::Transaction); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - - let good_blocks = vec![client.block_hash_delta_minus(2)]; - let retracted_blocks = vec![client.block_hash_delta_minus(1)]; - - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - // when - sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks, &[], &[]); - assert_eq!(io.chain.miner.queue_status().status.transaction_count, 0); - sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks, &[], &[]); - - // then - let status = io.chain.miner.queue_status(); - assert_eq!(status.status.transaction_count, 0); - } + use super::{PeerAsking, PeerInfo, *}; + use bytes::Bytes; + use ethcore::{ + client::{BlockChainClient, BlockInfo, ChainInfo, EachBlockWith, TestBlockChainClient}, + miner::{MinerService, PendingOrdering}, + }; + use ethereum_types::{Address, H256, U256}; + use ethkey; + use network::PeerId; + use parking_lot::RwLock; + use rlp::{Rlp, RlpStream}; + use std::collections::VecDeque; + use tests::{helpers::TestIo, snapshot::TestSnapshotService}; + use types::header::Header; + use SyncConfig; + + pub fn get_dummy_block(order: u32, parent_hash: H256) -> Bytes { + let mut header = Header::new(); + header.set_gas_limit(0.into()); + header.set_difficulty((order * 100).into()); + header.set_timestamp((order * 10) as u64); + header.set_number(order as u64); + header.set_parent_hash(parent_hash); + header.set_state_root(H256::zero()); + + let mut rlp = RlpStream::new_list(3); + rlp.append(&header); + rlp.append_raw(&::rlp::EMPTY_LIST_RLP, 1); + rlp.append_raw(&::rlp::EMPTY_LIST_RLP, 1); + rlp.out() + } + + pub fn get_dummy_blocks(order: u32, parent_hash: H256) -> Bytes { + let mut rlp = RlpStream::new_list(2); + rlp.append_raw(&get_dummy_block(order, parent_hash), 1); + let difficulty: U256 = (100 * order).into(); + rlp.append(&difficulty); + rlp.out() + } + + pub fn get_dummy_hashes() -> Bytes { + let mut rlp = RlpStream::new_list(5); + for _ in 0..5 { + let mut hash_d_rlp = RlpStream::new_list(2); + let hash: H256 = H256::from(0u64); + let diff: U256 = U256::from(1u64); + hash_d_rlp.append(&hash); + hash_d_rlp.append(&diff); + + rlp.append_raw(&hash_d_rlp.out(), 1); + } + + rlp.out() + } + + fn queue_info(unverified: usize, verified: usize) -> BlockQueueInfo { + BlockQueueInfo { + unverified_queue_size: unverified, + verified_queue_size: verified, + verifying_queue_size: 0, + max_queue_size: 1000, + max_mem_use: 1000, + mem_used: 500, + } + } + + fn sync_status(state: SyncState) -> SyncStatus { + SyncStatus { + state: state, + protocol_version: 0, + network_id: 0, + start_block_number: 0, + last_imported_block_number: None, + highest_block_number: None, + blocks_total: 0, + blocks_received: 0, + num_peers: 0, + num_active_peers: 0, + item_sizes: BTreeMap::new(), + num_snapshot_chunks: 0, + snapshot_chunks_done: 0, + last_imported_old_block_number: None, + } + } + + #[test] + fn is_still_verifying() { + assert!(!sync_status(SyncState::Idle).is_syncing(queue_info(2, 1))); + assert!(sync_status(SyncState::Idle).is_syncing(queue_info(2, 2))); + } + + #[test] + fn is_synced_state() { + assert!(sync_status(SyncState::Blocks).is_syncing(queue_info(0, 0))); + assert!(!sync_status(SyncState::Idle).is_syncing(queue_info(0, 0))); + } + + pub fn dummy_sync_with_peer( + peer_latest_hash: H256, + client: &dyn BlockChainClient, + ) -> ChainSync { + let mut sync = ChainSync::new( + SyncConfig::default(), + client, + ForkFilterApi::new_dummy(client), + ); + insert_dummy_peer(&mut sync, 0, peer_latest_hash); + sync + } + + pub fn insert_dummy_peer(sync: &mut ChainSync, peer_id: PeerId, peer_latest_hash: H256) { + sync.peers.insert( + peer_id, + PeerInfo { + protocol_version: 0, + genesis: H256::zero(), + network_id: 0, + latest_hash: peer_latest_hash, + difficulty: None, + asking: PeerAsking::Nothing, + asking_blocks: Vec::new(), + asking_hash: None, + ask_time: Instant::now(), + last_sent_transactions: Default::default(), + expired: false, + confirmation: super::ForkConfirmation::Confirmed, + snapshot_number: None, + snapshot_hash: None, + asking_snapshot_data: None, + block_set: None, + client_version: ClientVersion::from(""), + }, + ); + } + + #[test] + fn finds_lagging_peers() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + let sync = dummy_sync_with_peer(client.block_hash_delta_minus(10), &client); + let chain_info = client.chain_info(); + + let lagging_peers = sync.get_lagging_peers(&chain_info); + + assert_eq!(1, lagging_peers.len()); + } + + #[test] + fn calculates_tree_for_lagging_peer() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(15, EachBlockWith::Uncle); + + let start = client.block_hash_delta_minus(4); + let end = client.block_hash_delta_minus(2); + + // wrong way end -> start, should be None + let rlp = ChainSync::create_new_hashes_rlp(&client, &end, &start); + assert!(rlp.is_none()); + + let rlp = ChainSync::create_new_hashes_rlp(&client, &start, &end).unwrap(); + // size of three rlp encoded hash-difficulty + assert_eq!(107, rlp.len()); + } + // idea is that what we produce when propagading latest hashes should be accepted in + // on_peer_new_hashes in our code as well + #[test] + fn hashes_rlp_mutually_acceptable() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + let queue = RwLock::new(VecDeque::new()); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + let chain_info = client.chain_info(); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + let peers = sync.get_lagging_peers(&chain_info); + SyncPropagator::propagate_new_hashes(&mut sync, &chain_info, &mut io, &peers); + + let data = &io.packets[0].data.clone(); + let result = SyncHandler::on_peer_new_hashes(&mut sync, &mut io, 0, &Rlp::new(data)); + assert!(result.is_ok()); + } + + // idea is that what we produce when propagading latest block should be accepted in + // on_peer_new_block in our code as well + #[test] + fn block_rlp_mutually_acceptable() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + let queue = RwLock::new(VecDeque::new()); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + let chain_info = client.chain_info(); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + let peers = sync.get_lagging_peers(&chain_info); + SyncPropagator::propagate_blocks(&mut sync, &chain_info, &mut io, &[], &peers); + + let data = &io.packets[0].data.clone(); + let result = SyncHandler::on_peer_new_block(&mut sync, &mut io, 0, &Rlp::new(data)); + assert!(result.is_ok()); + } + + #[test] + fn should_add_transactions_to_queue() { + fn sender(tx: &UnverifiedTransaction) -> Address { + ethkey::public_to_address(&tx.recover_public().unwrap()) + } + + // given + let mut client = TestBlockChainClient::new(); + client.add_blocks(98, EachBlockWith::Uncle); + client.add_blocks(1, EachBlockWith::UncleAndTransaction); + client.add_blocks(1, EachBlockWith::Transaction); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + + let good_blocks = vec![client.block_hash_delta_minus(2)]; + let retracted_blocks = vec![client.block_hash_delta_minus(1)]; + + // Add some balance to clients and reset nonces + for h in &[good_blocks[0], retracted_blocks[0]] { + let block = client.block(BlockId::Hash(*h)).unwrap(); + let sender = sender(&block.transactions()[0]); + client.set_balance(sender, U256::from(10_000_000_000_000_000_000u64)); + client.set_nonce(sender, U256::from(0)); + } + + // when + { + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + io.chain + .miner + .chain_new_blocks(io.chain, &[], &[], &[], &good_blocks, false); + sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks, &[], &[]); + assert_eq!( + io.chain + .miner + .ready_transactions(io.chain, 10, PendingOrdering::Priority) + .len(), + 1 + ); + } + // We need to update nonce status (because we say that the block has been imported) + for h in &[good_blocks[0]] { + let block = client.block(BlockId::Hash(*h)).unwrap(); + client.set_nonce(sender(&block.transactions()[0]), U256::from(1)); + } + { + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&client, &ss, &queue, None); + io.chain.miner.chain_new_blocks( + io.chain, + &[], + &[], + &good_blocks, + &retracted_blocks, + false, + ); + sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks, &[], &[]); + } + + // then + assert_eq!( + client + .miner + .ready_transactions(&client, 10, PendingOrdering::Priority) + .len(), + 1 + ); + } + + #[test] + fn should_not_add_transactions_to_queue_if_not_synced() { + // given + let mut client = TestBlockChainClient::new(); + client.add_blocks(98, EachBlockWith::Uncle); + client.add_blocks(1, EachBlockWith::UncleAndTransaction); + client.add_blocks(1, EachBlockWith::Transaction); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + + let good_blocks = vec![client.block_hash_delta_minus(2)]; + let retracted_blocks = vec![client.block_hash_delta_minus(1)]; + + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + // when + sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks, &[], &[]); + assert_eq!(io.chain.miner.queue_status().status.transaction_count, 0); + sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks, &[], &[]); + + // then + let status = io.chain.miner.queue_status(); + assert_eq!(status.status.transaction_count, 0); + } } diff --git a/ethcore/sync/src/chain/propagator.rs b/ethcore/sync/src/chain/propagator.rs index c3654553ffd..cd28f7fe85f 100644 --- a/ethcore/sync/src/chain/propagator.rs +++ b/ethcore/sync/src/chain/propagator.rs @@ -1,639 +1,712 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::cmp; -use std::collections::HashSet; +use std::{cmp, collections::HashSet}; use bytes::Bytes; use ethereum_types::H256; use fastmap::H256FastSet; -use network::client_version::ClientCapabilities; -use network::PeerId; +use network::{client_version::ClientCapabilities, PeerId}; use rand::Rng; use rlp::{Encodable, RlpStream}; use sync_io::SyncIo; -use types::transaction::SignedTransaction; -use types::BlockNumber; -use types::blockchain_info::BlockChainInfo; - -use super::sync_packet::SyncPacket; -use super::sync_packet::SyncPacket::{ - NewBlockHashesPacket, - TransactionsPacket, - NewBlockPacket, - ConsensusDataPacket, +use types::{blockchain_info::BlockChainInfo, transaction::SignedTransaction, BlockNumber}; + +use super::sync_packet::{ + SyncPacket, + SyncPacket::{ConsensusDataPacket, NewBlockHashesPacket, NewBlockPacket, TransactionsPacket}, }; use super::{ - random, - ChainSync, - MAX_TRANSACTION_PACKET_SIZE, - MAX_PEER_LAG_PROPAGATION, - MAX_PEERS_PROPAGATION, - MIN_PEERS_PROPAGATION, + random, ChainSync, MAX_PEERS_PROPAGATION, MAX_PEER_LAG_PROPAGATION, + MAX_TRANSACTION_PACKET_SIZE, MIN_PEERS_PROPAGATION, }; /// The Chain Sync Propagator: propagates data to peers pub struct SyncPropagator; impl SyncPropagator { - /// propagates latest block to a set of peers - pub fn propagate_blocks(sync: &mut ChainSync, chain_info: &BlockChainInfo, io: &mut SyncIo, blocks: &[H256], peers: &[PeerId]) -> usize { - trace!(target: "sync", "Sending NewBlocks to {:?}", peers); - let sent = peers.len(); - let mut send_packet = |io: &mut SyncIo, rlp: Bytes| { - for peer_id in peers { - SyncPropagator::send_packet(io, *peer_id, NewBlockPacket, rlp.clone()); - - if let Some(ref mut peer) = sync.peers.get_mut(peer_id) { - peer.latest_hash = chain_info.best_block_hash.clone(); - } - } - }; - - if blocks.is_empty() { - let rlp = ChainSync::create_latest_block_rlp(io.chain()); - send_packet(io, rlp); - } else { - for h in blocks { - let rlp = ChainSync::create_new_block_rlp(io.chain(), h); - send_packet(io, rlp); - } - } - - sent - } - - /// propagates new known hashes to all peers - pub fn propagate_new_hashes(sync: &mut ChainSync, chain_info: &BlockChainInfo, io: &mut SyncIo, peers: &[PeerId]) -> usize { - trace!(target: "sync", "Sending NewHashes to {:?}", peers); - let last_parent = *io.chain().best_block_header().parent_hash(); - let best_block_hash = chain_info.best_block_hash; - let rlp = match ChainSync::create_new_hashes_rlp(io.chain(), &last_parent, &best_block_hash) { - Some(rlp) => rlp, - None => return 0 - }; - - let sent = peers.len(); - for peer_id in peers { - if let Some(ref mut peer) = sync.peers.get_mut(peer_id) { - peer.latest_hash = best_block_hash; - } - SyncPropagator::send_packet(io, *peer_id, NewBlockHashesPacket, rlp.clone()); - } - sent - } - - /// propagates new transactions to all peers - pub fn propagate_new_transactions bool>(sync: &mut ChainSync, io: &mut SyncIo, mut should_continue: F) -> usize { - // Early out if nobody to send to. - if sync.peers.is_empty() { - return 0; - } - - let transactions = io.chain().transactions_to_propagate(); - if transactions.is_empty() { - return 0; - } - - if !should_continue() { - return 0; - } - - let (transactions, service_transactions): (Vec<_>, Vec<_>) = transactions.iter() - .map(|tx| tx.signed()) - .partition(|tx| !tx.gas_price.is_zero()); - - // usual transactions could be propagated to all peers - let mut affected_peers = HashSet::new(); - if !transactions.is_empty() { - let peers = SyncPropagator::select_peers_for_transactions(sync, |_| true); - affected_peers = SyncPropagator::propagate_transactions_to_peers( - sync, io, peers, transactions, &mut should_continue, - ); - } - - // most of times service_transactions will be empty - // => there's no need to merge packets - if !service_transactions.is_empty() { - let service_transactions_peers = SyncPropagator::select_peers_for_transactions(sync, |peer_id| io.peer_version(*peer_id).accepts_service_transaction()); - let service_transactions_affected_peers = SyncPropagator::propagate_transactions_to_peers( - sync, io, service_transactions_peers, service_transactions, &mut should_continue - ); - affected_peers.extend(&service_transactions_affected_peers); - } - - affected_peers.len() - } - - fn propagate_transactions_to_peers bool>( - sync: &mut ChainSync, - io: &mut SyncIo, - peers: Vec, - transactions: Vec<&SignedTransaction>, - mut should_continue: F, - ) -> HashSet { - let all_transactions_hashes = transactions.iter() - .map(|tx| tx.hash()) - .collect::(); - let all_transactions_rlp = { - let mut packet = RlpStream::new_list(transactions.len()); - for tx in &transactions { packet.append(&**tx); } - packet.out() - }; - - // Clear old transactions from stats - sync.transactions_stats.retain(&all_transactions_hashes); - - let send_packet = |io: &mut SyncIo, peer_id: PeerId, sent: usize, rlp: Bytes| { - let size = rlp.len(); - SyncPropagator::send_packet(io, peer_id, TransactionsPacket, rlp); - trace!(target: "sync", "{:02} <- Transactions ({} entries; {} bytes)", peer_id, sent, size); - }; - - let block_number = io.chain().chain_info().best_block_number; - let mut sent_to_peers = HashSet::new(); - let mut max_sent = 0; - - // for every peer construct and send transactions packet - for peer_id in peers { - if !should_continue() { - debug!(target: "sync", "Sent up to {} transactions to {} peers.", max_sent, sent_to_peers.len()); - return sent_to_peers; - } - - let stats = &mut sync.transactions_stats; - let peer_info = sync.peers.get_mut(&peer_id) + /// propagates latest block to a set of peers + pub fn propagate_blocks( + sync: &mut ChainSync, + chain_info: &BlockChainInfo, + io: &mut dyn SyncIo, + blocks: &[H256], + peers: &[PeerId], + ) -> usize { + trace!(target: "sync", "Sending NewBlocks to {:?}", peers); + let sent = peers.len(); + let mut send_packet = |io: &mut dyn SyncIo, rlp: Bytes| { + for peer_id in peers { + SyncPropagator::send_packet(io, *peer_id, NewBlockPacket, rlp.clone()); + + if let Some(ref mut peer) = sync.peers.get_mut(peer_id) { + peer.latest_hash = chain_info.best_block_hash.clone(); + } + } + }; + + if blocks.is_empty() { + let rlp = ChainSync::create_latest_block_rlp(io.chain()); + send_packet(io, rlp); + } else { + for h in blocks { + let rlp = ChainSync::create_new_block_rlp(io.chain(), h); + send_packet(io, rlp); + } + } + + sent + } + + /// propagates new known hashes to all peers + pub fn propagate_new_hashes( + sync: &mut ChainSync, + chain_info: &BlockChainInfo, + io: &mut dyn SyncIo, + peers: &[PeerId], + ) -> usize { + trace!(target: "sync", "Sending NewHashes to {:?}", peers); + let last_parent = *io.chain().best_block_header().parent_hash(); + let best_block_hash = chain_info.best_block_hash; + let rlp = match ChainSync::create_new_hashes_rlp(io.chain(), &last_parent, &best_block_hash) + { + Some(rlp) => rlp, + None => return 0, + }; + + let sent = peers.len(); + for peer_id in peers { + if let Some(ref mut peer) = sync.peers.get_mut(peer_id) { + peer.latest_hash = best_block_hash; + } + SyncPropagator::send_packet(io, *peer_id, NewBlockHashesPacket, rlp.clone()); + } + sent + } + + /// propagates new transactions to all peers + pub fn propagate_new_transactions bool>( + sync: &mut ChainSync, + io: &mut dyn SyncIo, + mut should_continue: F, + ) -> usize { + // Early out if nobody to send to. + if sync.peers.is_empty() { + return 0; + } + + let transactions = io.chain().transactions_to_propagate(); + if transactions.is_empty() { + return 0; + } + + if !should_continue() { + return 0; + } + + let (transactions, service_transactions): (Vec<_>, Vec<_>) = transactions + .iter() + .map(|tx| tx.signed()) + .partition(|tx| !tx.gas_price.is_zero()); + + // usual transactions could be propagated to all peers + let mut affected_peers = HashSet::new(); + if !transactions.is_empty() { + let peers = SyncPropagator::select_peers_for_transactions(sync, |_| true); + affected_peers = SyncPropagator::propagate_transactions_to_peers( + sync, + io, + peers, + transactions, + &mut should_continue, + ); + } + + // most of times service_transactions will be empty + // => there's no need to merge packets + if !service_transactions.is_empty() { + let service_transactions_peers = + SyncPropagator::select_peers_for_transactions(sync, |peer_id| { + io.peer_version(*peer_id).accepts_service_transaction() + }); + let service_transactions_affected_peers = + SyncPropagator::propagate_transactions_to_peers( + sync, + io, + service_transactions_peers, + service_transactions, + &mut should_continue, + ); + affected_peers.extend(&service_transactions_affected_peers); + } + + affected_peers.len() + } + + fn propagate_transactions_to_peers bool>( + sync: &mut ChainSync, + io: &mut dyn SyncIo, + peers: Vec, + transactions: Vec<&SignedTransaction>, + mut should_continue: F, + ) -> HashSet { + let all_transactions_hashes = transactions + .iter() + .map(|tx| tx.hash()) + .collect::(); + let all_transactions_rlp = { + let mut packet = RlpStream::new_list(transactions.len()); + for tx in &transactions { + packet.append(&**tx); + } + packet.out() + }; + + // Clear old transactions from stats + sync.transactions_stats.retain(&all_transactions_hashes); + + let send_packet = |io: &mut dyn SyncIo, peer_id: PeerId, sent: usize, rlp: Bytes| { + let size = rlp.len(); + SyncPropagator::send_packet(io, peer_id, TransactionsPacket, rlp); + trace!(target: "sync", "{:02} <- Transactions ({} entries; {} bytes)", peer_id, sent, size); + }; + + let block_number = io.chain().chain_info().best_block_number; + let mut sent_to_peers = HashSet::new(); + let mut max_sent = 0; + + // for every peer construct and send transactions packet + for peer_id in peers { + if !should_continue() { + debug!(target: "sync", "Sent up to {} transactions to {} peers.", max_sent, sent_to_peers.len()); + return sent_to_peers; + } + + let stats = &mut sync.transactions_stats; + let peer_info = sync.peers.get_mut(&peer_id) .expect("peer_id is form peers; peers is result of select_peers_for_transactions; select_peers_for_transactions selects peers from self.peers; qed"); - // Send all transactions, if the peer doesn't know about anything - if peer_info.last_sent_transactions.is_empty() { - // update stats - for hash in &all_transactions_hashes { - let id = io.peer_session_info(peer_id).and_then(|info| info.id); - stats.propagated(hash, id, block_number); - } - peer_info.last_sent_transactions = all_transactions_hashes.clone(); - - send_packet(io, peer_id, all_transactions_hashes.len(), all_transactions_rlp.clone()); - sent_to_peers.insert(peer_id); - max_sent = cmp::max(max_sent, all_transactions_hashes.len()); - continue; - } - - // Get hashes of all transactions to send to this peer - let to_send = all_transactions_hashes.difference(&peer_info.last_sent_transactions) - .cloned() - .collect::>(); - if to_send.is_empty() { - continue; - } - - // Construct RLP - let (packet, to_send) = { - let mut to_send = to_send; - let mut packet = RlpStream::new(); - packet.begin_unbounded_list(); - let mut pushed = 0; - for tx in &transactions { - let hash = tx.hash(); - if to_send.contains(&hash) { - let mut transaction = RlpStream::new(); - tx.rlp_append(&mut transaction); - let appended = packet.append_raw_checked(&transaction.drain(), 1, MAX_TRANSACTION_PACKET_SIZE); - if !appended { - // Maximal packet size reached just proceed with sending - debug!(target: "sync", "Transaction packet size limit reached. Sending incomplete set of {}/{} transactions.", pushed, to_send.len()); - to_send = to_send.into_iter().take(pushed).collect(); - break; - } - pushed += 1; - } - } - packet.complete_unbounded_list(); - (packet, to_send) - }; - - // Update stats - let id = io.peer_session_info(peer_id).and_then(|info| info.id); - for hash in &to_send { - // update stats - stats.propagated(hash, id, block_number); - } - - peer_info.last_sent_transactions = all_transactions_hashes - .intersection(&peer_info.last_sent_transactions) - .chain(&to_send) - .cloned() - .collect(); - send_packet(io, peer_id, to_send.len(), packet.out()); - sent_to_peers.insert(peer_id); - max_sent = cmp::max(max_sent, to_send.len()); - - } - - debug!(target: "sync", "Sent up to {} transactions to {} peers.", max_sent, sent_to_peers.len()); - sent_to_peers - } - - pub fn propagate_latest_blocks(sync: &mut ChainSync, io: &mut SyncIo, sealed: &[H256]) { - let chain_info = io.chain().chain_info(); - if (((chain_info.best_block_number as i64) - (sync.last_sent_block_number as i64)).abs() as BlockNumber) < MAX_PEER_LAG_PROPAGATION { - let peers = sync.get_lagging_peers(&chain_info); - if sealed.is_empty() { - let hashes = SyncPropagator::propagate_new_hashes(sync, &chain_info, io, &peers); - let peers = ChainSync::select_random_peers(&peers); - let blocks = SyncPropagator::propagate_blocks(sync, &chain_info, io, sealed, &peers); - if blocks != 0 || hashes != 0 { - trace!(target: "sync", "Sent latest {} blocks and {} hashes to peers.", blocks, hashes); - } - } else { - SyncPropagator::propagate_blocks(sync, &chain_info, io, sealed, &peers); - SyncPropagator::propagate_new_hashes(sync, &chain_info, io, &peers); - trace!(target: "sync", "Sent sealed block to all peers"); - }; - } - sync.last_sent_block_number = chain_info.best_block_number; - } - - /// Distribute valid proposed blocks to subset of current peers. - pub fn propagate_proposed_blocks(sync: &mut ChainSync, io: &mut SyncIo, proposed: &[Bytes]) { - let peers = sync.get_consensus_peers(); - trace!(target: "sync", "Sending proposed blocks to {:?}", peers); - for block in proposed { - let rlp = ChainSync::create_block_rlp( - block, - io.chain().chain_info().total_difficulty - ); - for peer_id in &peers { - SyncPropagator::send_packet(io, *peer_id, NewBlockPacket, rlp.clone()); - } - } - } - - /// Broadcast consensus message to peers. - pub fn propagate_consensus_packet(sync: &mut ChainSync, io: &mut SyncIo, packet: Bytes) { - let lucky_peers = ChainSync::select_random_peers(&sync.get_consensus_peers()); - trace!(target: "sync", "Sending consensus packet to {:?}", lucky_peers); - for peer_id in lucky_peers { - SyncPropagator::send_packet(io, peer_id, ConsensusDataPacket, packet.clone()); - } - } - - /// Broadcast private transaction message to peers. - pub fn propagate_private_transaction(sync: &mut ChainSync, io: &mut SyncIo, transaction_hash: H256, packet_id: SyncPacket, packet: Bytes) { - let lucky_peers = ChainSync::select_random_peers(&sync.get_private_transaction_peers(&transaction_hash)); - if lucky_peers.is_empty() { - error!(target: "privatetx", "Cannot propagate the packet, no peers with private tx enabled connected"); - } else { - trace!(target: "privatetx", "Sending private transaction packet to {:?}", lucky_peers); - for peer_id in lucky_peers { - if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) { - peer.last_sent_private_transactions.insert(transaction_hash); - } - SyncPropagator::send_packet(io, peer_id, packet_id, packet.clone()); - } - } - } - - fn select_peers_for_transactions(sync: &ChainSync, filter: F) -> Vec - where F: Fn(&PeerId) -> bool { - // sqrt(x)/x scaled to max u32 - let fraction = ((sync.peers.len() as f64).powf(-0.5) * (u32::max_value() as f64).round()) as u32; - let small = sync.peers.len() < MIN_PEERS_PROPAGATION; - - let mut random = random::new(); - sync.peers.keys() - .cloned() - .filter(filter) - .filter(|_| small || random.next_u32() < fraction) - .take(MAX_PEERS_PROPAGATION) - .collect() - } - - /// Generic packet sender - pub fn send_packet(sync: &mut SyncIo, peer_id: PeerId, packet_id: SyncPacket, packet: Bytes) { - if let Err(e) = sync.send(peer_id, packet_id, packet) { - debug!(target:"sync", "Error sending packet: {:?}", e); - sync.disconnect_peer(peer_id); - } - } + // Send all transactions, if the peer doesn't know about anything + if peer_info.last_sent_transactions.is_empty() { + // update stats + for hash in &all_transactions_hashes { + let id = io.peer_session_info(peer_id).and_then(|info| info.id); + stats.propagated(hash, id, block_number); + } + peer_info.last_sent_transactions = all_transactions_hashes.clone(); + + send_packet( + io, + peer_id, + all_transactions_hashes.len(), + all_transactions_rlp.clone(), + ); + sent_to_peers.insert(peer_id); + max_sent = cmp::max(max_sent, all_transactions_hashes.len()); + continue; + } + + // Get hashes of all transactions to send to this peer + let to_send = all_transactions_hashes + .difference(&peer_info.last_sent_transactions) + .cloned() + .collect::>(); + if to_send.is_empty() { + continue; + } + + // Construct RLP + let (packet, to_send) = { + let mut to_send = to_send; + let mut packet = RlpStream::new(); + packet.begin_unbounded_list(); + let mut pushed = 0; + for tx in &transactions { + let hash = tx.hash(); + if to_send.contains(&hash) { + let mut transaction = RlpStream::new(); + tx.rlp_append(&mut transaction); + let appended = packet.append_raw_checked( + &transaction.drain(), + 1, + MAX_TRANSACTION_PACKET_SIZE, + ); + if !appended { + // Maximal packet size reached just proceed with sending + debug!(target: "sync", "Transaction packet size limit reached. Sending incomplete set of {}/{} transactions.", pushed, to_send.len()); + to_send = to_send.into_iter().take(pushed).collect(); + break; + } + pushed += 1; + } + } + packet.complete_unbounded_list(); + (packet, to_send) + }; + + // Update stats + let id = io.peer_session_info(peer_id).and_then(|info| info.id); + for hash in &to_send { + // update stats + stats.propagated(hash, id, block_number); + } + + peer_info.last_sent_transactions = all_transactions_hashes + .intersection(&peer_info.last_sent_transactions) + .chain(&to_send) + .cloned() + .collect(); + send_packet(io, peer_id, to_send.len(), packet.out()); + sent_to_peers.insert(peer_id); + max_sent = cmp::max(max_sent, to_send.len()); + } + + debug!(target: "sync", "Sent up to {} transactions to {} peers.", max_sent, sent_to_peers.len()); + sent_to_peers + } + + pub fn propagate_latest_blocks(sync: &mut ChainSync, io: &mut dyn SyncIo, sealed: &[H256]) { + let chain_info = io.chain().chain_info(); + if (((chain_info.best_block_number as i64) - (sync.last_sent_block_number as i64)).abs() + as BlockNumber) + < MAX_PEER_LAG_PROPAGATION + { + let peers = sync.get_lagging_peers(&chain_info); + if sealed.is_empty() { + let hashes = SyncPropagator::propagate_new_hashes(sync, &chain_info, io, &peers); + let peers = ChainSync::select_random_peers(&peers); + let blocks = + SyncPropagator::propagate_blocks(sync, &chain_info, io, sealed, &peers); + if blocks != 0 || hashes != 0 { + trace!(target: "sync", "Sent latest {} blocks and {} hashes to peers.", blocks, hashes); + } + } else { + SyncPropagator::propagate_blocks(sync, &chain_info, io, sealed, &peers); + SyncPropagator::propagate_new_hashes(sync, &chain_info, io, &peers); + trace!(target: "sync", "Sent sealed block to all peers"); + }; + } + sync.last_sent_block_number = chain_info.best_block_number; + } + + /// Distribute valid proposed blocks to subset of current peers. + pub fn propagate_proposed_blocks( + sync: &mut ChainSync, + io: &mut dyn SyncIo, + proposed: &[Bytes], + ) { + let peers = sync.get_consensus_peers(); + trace!(target: "sync", "Sending proposed blocks to {:?}", peers); + for block in proposed { + let rlp = ChainSync::create_block_rlp(block, io.chain().chain_info().total_difficulty); + for peer_id in &peers { + SyncPropagator::send_packet(io, *peer_id, NewBlockPacket, rlp.clone()); + } + } + } + + /// Broadcast consensus message to peers. + pub fn propagate_consensus_packet(sync: &mut ChainSync, io: &mut dyn SyncIo, packet: Bytes) { + let lucky_peers = ChainSync::select_random_peers(&sync.get_consensus_peers()); + trace!(target: "sync", "Sending consensus packet to {:?}", lucky_peers); + for peer_id in lucky_peers { + SyncPropagator::send_packet(io, peer_id, ConsensusDataPacket, packet.clone()); + } + } + + fn select_peers_for_transactions(sync: &ChainSync, filter: F) -> Vec + where + F: Fn(&PeerId) -> bool, + { + // sqrt(x)/x scaled to max u32 + let fraction = + ((sync.peers.len() as f64).powf(-0.5) * (u32::max_value() as f64).round()) as u32; + let small = sync.peers.len() < MIN_PEERS_PROPAGATION; + + let mut random = random::new(); + sync.peers + .keys() + .cloned() + .filter(filter) + .filter(|_| small || random.next_u32() < fraction) + .take(MAX_PEERS_PROPAGATION) + .collect() + } + + /// Generic packet sender + pub fn send_packet( + sync: &mut dyn SyncIo, + peer_id: PeerId, + packet_id: SyncPacket, + packet: Bytes, + ) { + if let Err(e) = sync.send(peer_id, packet_id, packet) { + debug!(target:"sync", "Error sending packet: {:?}", e); + sync.disconnect_peer(peer_id); + } + } } #[cfg(test)] mod tests { - use ethcore::client::{BlockInfo, ChainInfo, EachBlockWith, TestBlockChainClient}; - use parking_lot::RwLock; - use rlp::{Rlp}; - use std::collections::{VecDeque}; - use tests::helpers::{TestIo}; - use tests::snapshot::TestSnapshotService; - - use super::{*, super::{*, tests::*}}; - - #[test] - fn sends_new_hashes_to_lagging_peer() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - let queue = RwLock::new(VecDeque::new()); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - let chain_info = client.chain_info(); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - let peers = sync.get_lagging_peers(&chain_info); - let peer_count = SyncPropagator::propagate_new_hashes(&mut sync, &chain_info, &mut io, &peers); - - // 1 message should be send - assert_eq!(1, io.packets.len()); - // 1 peer should be updated - assert_eq!(1, peer_count); - // NEW_BLOCK_HASHES_PACKET - assert_eq!(0x01, io.packets[0].packet_id); - } - - #[test] - fn sends_latest_block_to_lagging_peer() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - let queue = RwLock::new(VecDeque::new()); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - let chain_info = client.chain_info(); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - let peers = sync.get_lagging_peers(&chain_info); - let peer_count = SyncPropagator::propagate_blocks(&mut sync, &chain_info, &mut io, &[], &peers); - - // 1 message should be send - assert_eq!(1, io.packets.len()); - // 1 peer should be updated - assert_eq!(1, peer_count); - // NEW_BLOCK_PACKET - assert_eq!(0x07, io.packets[0].packet_id); - } - - #[test] - fn sends_sealed_block() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - let queue = RwLock::new(VecDeque::new()); - let hash = client.block_hash(BlockId::Number(99)).unwrap(); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); - let chain_info = client.chain_info(); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - let peers = sync.get_lagging_peers(&chain_info); - let peer_count = SyncPropagator::propagate_blocks(&mut sync ,&chain_info, &mut io, &[hash.clone()], &peers); - - // 1 message should be send - assert_eq!(1, io.packets.len()); - // 1 peer should be updated - assert_eq!(1, peer_count); - // NEW_BLOCK_PACKET - assert_eq!(0x07, io.packets[0].packet_id); - } - - #[test] - fn sends_proposed_block() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(2, EachBlockWith::Uncle); - let queue = RwLock::new(VecDeque::new()); - let block = client.block(BlockId::Latest).unwrap().into_inner(); - let mut sync = ChainSync::new(SyncConfig::default(), &client, None); - sync.peers.insert(0, - PeerInfo { - // Messaging protocol - protocol_version: 2, - genesis: H256::zero(), - network_id: 0, - latest_hash: client.block_hash_delta_minus(1), - difficulty: None, - asking: PeerAsking::Nothing, - asking_blocks: Vec::new(), - asking_hash: None, - ask_time: Instant::now(), - last_sent_transactions: Default::default(), - last_sent_private_transactions: Default::default(), - expired: false, - private_tx_enabled: false, - confirmation: ForkConfirmation::Confirmed, - snapshot_number: None, - snapshot_hash: None, - asking_snapshot_data: None, - block_set: None, - client_version: ClientVersion::from(""), - }); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - SyncPropagator::propagate_proposed_blocks(&mut sync, &mut io, &[block]); - - // 1 message should be sent - assert_eq!(1, io.packets.len()); - // NEW_BLOCK_PACKET - assert_eq!(0x07, io.packets[0].packet_id); - } - - #[test] - fn propagates_transactions() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - client.insert_transaction_to_queue(); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(1), &client); - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); - // Try to propagate same transactions for the second time - let peer_count2 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); - // Even after new block transactions should not be propagated twice - sync.chain_new_blocks(&mut io, &[], &[], &[], &[], &[], &[]); - // Try to propagate same transactions for the third time - let peer_count3 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); - - // 1 message should be send - assert_eq!(1, io.packets.len()); - // 1 peer should be updated but only once - assert_eq!(1, peer_count); - assert_eq!(0, peer_count2); - assert_eq!(0, peer_count3); - // TRANSACTIONS_PACKET - assert_eq!(0x02, io.packets[0].packet_id); - } - - #[test] - fn does_not_propagate_new_transactions_after_new_block() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - client.insert_transaction_to_queue(); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(1), &client); - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); - io.chain.insert_transaction_to_queue(); - // New block import should not trigger propagation. - // (we only propagate on timeout) - sync.chain_new_blocks(&mut io, &[], &[], &[], &[], &[], &[]); - - // 2 message should be send - assert_eq!(1, io.packets.len()); - // 1 peer should receive the message - assert_eq!(1, peer_count); - // TRANSACTIONS_PACKET - assert_eq!(0x02, io.packets[0].packet_id); - } - - #[test] - fn does_not_fail_for_no_peers() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - client.insert_transaction_to_queue(); - // Sync with no peers - let mut sync = ChainSync::new(SyncConfig::default(), &client, None); - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); - sync.chain_new_blocks(&mut io, &[], &[], &[], &[], &[], &[]); - // Try to propagate same transactions for the second time - let peer_count2 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); - - assert_eq!(0, io.packets.len()); - assert_eq!(0, peer_count); - assert_eq!(0, peer_count2); - } - - #[test] - fn propagates_transactions_without_alternating() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - client.insert_transaction_to_queue(); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(1), &client); - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - // should sent some - { - let mut io = TestIo::new(&mut client, &ss, &queue, None); - let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); - assert_eq!(1, io.packets.len()); - assert_eq!(1, peer_count); - } - // Insert some more - client.insert_transaction_to_queue(); - let (peer_count2, peer_count3) = { - let mut io = TestIo::new(&mut client, &ss, &queue, None); - // Propagate new transactions - let peer_count2 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); - // And now the peer should have all transactions - let peer_count3 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); - (peer_count2, peer_count3) - }; - - // 2 message should be send (in total) - assert_eq!(2, queue.read().len()); - // 1 peer should be updated but only once after inserting new transaction - assert_eq!(1, peer_count2); - assert_eq!(0, peer_count3); - // TRANSACTIONS_PACKET - assert_eq!(0x02, queue.read()[0].packet_id); - assert_eq!(0x02, queue.read()[1].packet_id); - } - - #[test] - fn should_maintain_transations_propagation_stats() { - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Uncle); - client.insert_transaction_to_queue(); - let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(1), &client); - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); - - let stats = sync.transactions_stats(); - assert_eq!(stats.len(), 1, "Should maintain stats for single transaction.") - } - - #[test] - fn should_propagate_service_transaction_to_selected_peers_only() { - let mut client = TestBlockChainClient::new(); - client.insert_transaction_with_gas_price_to_queue(U256::zero()); - let block_hash = client.block_hash_delta_minus(1); - let mut sync = ChainSync::new(SyncConfig::default(), &client, None); - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - // when peer#1 is Geth - insert_dummy_peer(&mut sync, 1, block_hash); - io.peers_info.insert(1, "Geth".to_owned()); - // and peer#2 is Parity, accepting service transactions - insert_dummy_peer(&mut sync, 2, block_hash); - io.peers_info.insert(2, "Parity-Ethereum/v2.6.0/linux/rustc".to_owned()); - // and peer#3 is Parity, accepting service transactions - insert_dummy_peer(&mut sync, 3, block_hash); - io.peers_info.insert(3, "Parity-Ethereum/ABCDEFGH/v2.7.3/linux/rustc".to_owned()); - - // and new service transaction is propagated to peers - SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); - - // peer#2 && peer#3 are receiving service transaction - assert!(io.packets.iter().any(|p| p.packet_id == 0x02 && p.recipient == 2)); // TRANSACTIONS_PACKET - assert!(io.packets.iter().any(|p| p.packet_id == 0x02 && p.recipient == 3)); // TRANSACTIONS_PACKET - assert_eq!(io.packets.len(), 2); - } - - #[test] - fn should_propagate_service_transaction_is_sent_as_separate_message() { - let mut client = TestBlockChainClient::new(); - let tx1_hash = client.insert_transaction_to_queue(); - let tx2_hash = client.insert_transaction_with_gas_price_to_queue(U256::zero()); - let block_hash = client.block_hash_delta_minus(1); - let mut sync = ChainSync::new(SyncConfig::default(), &client, None); - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - // when peer#1 is Parity, accepting service transactions - insert_dummy_peer(&mut sync, 1, block_hash); - io.peers_info.insert(1, "Parity-Ethereum/v2.6.0/linux/rustc".to_owned()); - - // and service + non-service transactions are propagated to peers - SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); - - // two separate packets for peer are queued: - // 1) with non-service-transaction - // 2) with service transaction - let sent_transactions: Vec = io.packets.iter() - .filter_map(|p| { - if p.packet_id != 0x02 || p.recipient != 1 { // TRANSACTIONS_PACKET - return None; - } - - let rlp = Rlp::new(&*p.data); - let item_count = rlp.item_count().unwrap_or(0); - if item_count != 1 { - return None; - } - - rlp.at(0).ok().and_then(|r| r.as_val().ok()) - }) - .collect(); - assert_eq!(sent_transactions.len(), 2); - assert!(sent_transactions.iter().any(|tx| tx.hash() == tx1_hash)); - assert!(sent_transactions.iter().any(|tx| tx.hash() == tx2_hash)); - } + use ethcore::client::{BlockInfo, ChainInfo, EachBlockWith, TestBlockChainClient}; + use parking_lot::RwLock; + use rlp::Rlp; + use std::collections::VecDeque; + use tests::{helpers::TestIo, snapshot::TestSnapshotService}; + + use super::{ + super::{tests::*, *}, + *, + }; + + #[test] + fn sends_new_hashes_to_lagging_peer() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + let queue = RwLock::new(VecDeque::new()); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + let chain_info = client.chain_info(); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + let peers = sync.get_lagging_peers(&chain_info); + let peer_count = + SyncPropagator::propagate_new_hashes(&mut sync, &chain_info, &mut io, &peers); + + // 1 message should be send + assert_eq!(1, io.packets.len()); + // 1 peer should be updated + assert_eq!(1, peer_count); + // NEW_BLOCK_HASHES_PACKET + assert_eq!(0x01, io.packets[0].packet_id); + } + + #[test] + fn sends_latest_block_to_lagging_peer() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + let queue = RwLock::new(VecDeque::new()); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + let chain_info = client.chain_info(); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + let peers = sync.get_lagging_peers(&chain_info); + let peer_count = + SyncPropagator::propagate_blocks(&mut sync, &chain_info, &mut io, &[], &peers); + + // 1 message should be send + assert_eq!(1, io.packets.len()); + // 1 peer should be updated + assert_eq!(1, peer_count); + // NEW_BLOCK_PACKET + assert_eq!(0x07, io.packets[0].packet_id); + } + + #[test] + fn sends_sealed_block() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + let queue = RwLock::new(VecDeque::new()); + let hash = client.block_hash(BlockId::Number(99)).unwrap(); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + let chain_info = client.chain_info(); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + let peers = sync.get_lagging_peers(&chain_info); + let peer_count = SyncPropagator::propagate_blocks( + &mut sync, + &chain_info, + &mut io, + &[hash.clone()], + &peers, + ); + + // 1 message should be send + assert_eq!(1, io.packets.len()); + // 1 peer should be updated + assert_eq!(1, peer_count); + // NEW_BLOCK_PACKET + assert_eq!(0x07, io.packets[0].packet_id); + } + + #[test] + fn sends_proposed_block() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(2, EachBlockWith::Uncle); + let queue = RwLock::new(VecDeque::new()); + let block = client.block(BlockId::Latest).unwrap().into_inner(); + let mut sync = ChainSync::new( + SyncConfig::default(), + &client, + ForkFilterApi::new_dummy(&client), + ); + sync.peers.insert( + 0, + PeerInfo { + // Messaging protocol + protocol_version: 2, + genesis: H256::zero(), + network_id: 0, + latest_hash: client.block_hash_delta_minus(1), + difficulty: None, + asking: PeerAsking::Nothing, + asking_blocks: Vec::new(), + asking_hash: None, + ask_time: Instant::now(), + last_sent_transactions: Default::default(), + expired: false, + confirmation: ForkConfirmation::Confirmed, + snapshot_number: None, + snapshot_hash: None, + asking_snapshot_data: None, + block_set: None, + client_version: ClientVersion::from(""), + }, + ); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + SyncPropagator::propagate_proposed_blocks(&mut sync, &mut io, &[block]); + + // 1 message should be sent + assert_eq!(1, io.packets.len()); + // NEW_BLOCK_PACKET + assert_eq!(0x07, io.packets[0].packet_id); + } + + #[test] + fn propagates_transactions() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + client.insert_transaction_to_queue(); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(1), &client); + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); + // Try to propagate same transactions for the second time + let peer_count2 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); + // Even after new block transactions should not be propagated twice + sync.chain_new_blocks(&mut io, &[], &[], &[], &[], &[], &[]); + // Try to propagate same transactions for the third time + let peer_count3 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); + + // 1 message should be send + assert_eq!(1, io.packets.len()); + // 1 peer should be updated but only once + assert_eq!(1, peer_count); + assert_eq!(0, peer_count2); + assert_eq!(0, peer_count3); + // TRANSACTIONS_PACKET + assert_eq!(0x02, io.packets[0].packet_id); + } + + #[test] + fn does_not_propagate_new_transactions_after_new_block() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + client.insert_transaction_to_queue(); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(1), &client); + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); + io.chain.insert_transaction_to_queue(); + // New block import should not trigger propagation. + // (we only propagate on timeout) + sync.chain_new_blocks(&mut io, &[], &[], &[], &[], &[], &[]); + + // 2 message should be send + assert_eq!(1, io.packets.len()); + // 1 peer should receive the message + assert_eq!(1, peer_count); + // TRANSACTIONS_PACKET + assert_eq!(0x02, io.packets[0].packet_id); + } + + #[test] + fn does_not_fail_for_no_peers() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + client.insert_transaction_to_queue(); + // Sync with no peers + let mut sync = ChainSync::new( + SyncConfig::default(), + &client, + ForkFilterApi::new_dummy(&client), + ); + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); + sync.chain_new_blocks(&mut io, &[], &[], &[], &[], &[], &[]); + // Try to propagate same transactions for the second time + let peer_count2 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); + + assert_eq!(0, io.packets.len()); + assert_eq!(0, peer_count); + assert_eq!(0, peer_count2); + } + + #[test] + fn propagates_transactions_without_alternating() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + client.insert_transaction_to_queue(); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(1), &client); + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + // should sent some + { + let mut io = TestIo::new(&mut client, &ss, &queue, None); + let peer_count = + SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); + assert_eq!(1, io.packets.len()); + assert_eq!(1, peer_count); + } + // Insert some more + client.insert_transaction_to_queue(); + let (peer_count2, peer_count3) = { + let mut io = TestIo::new(&mut client, &ss, &queue, None); + // Propagate new transactions + let peer_count2 = + SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); + // And now the peer should have all transactions + let peer_count3 = + SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); + (peer_count2, peer_count3) + }; + + // 2 message should be send (in total) + assert_eq!(2, queue.read().len()); + // 1 peer should be updated but only once after inserting new transaction + assert_eq!(1, peer_count2); + assert_eq!(0, peer_count3); + // TRANSACTIONS_PACKET + assert_eq!(0x02, queue.read()[0].packet_id); + assert_eq!(0x02, queue.read()[1].packet_id); + } + + #[test] + fn should_maintain_transations_propagation_stats() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + client.insert_transaction_to_queue(); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(1), &client); + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); + + let stats = sync.transactions_stats(); + assert_eq!( + stats.len(), + 1, + "Should maintain stats for single transaction." + ) + } + + #[test] + fn should_propagate_service_transaction_to_selected_peers_only() { + let mut client = TestBlockChainClient::new(); + client.insert_transaction_with_gas_price_to_queue(U256::zero()); + let block_hash = client.block_hash_delta_minus(1); + let mut sync = ChainSync::new( + SyncConfig::default(), + &client, + ForkFilterApi::new_dummy(&client), + ); + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + // when peer#1 is Geth + insert_dummy_peer(&mut sync, 1, block_hash); + io.peers_info.insert(1, "Geth".to_owned()); + // and peer#2 is OpenEthereum, accepting service transactions + insert_dummy_peer(&mut sync, 2, block_hash); + io.peers_info + .insert(2, "OpenEthereum/v2.6.0/linux/rustc".to_owned()); + // and peer#3 is OpenEthereum, accepting service transactions + insert_dummy_peer(&mut sync, 3, block_hash); + io.peers_info + .insert(3, "OpenEthereum/ABCDEFGH/v2.7.3/linux/rustc".to_owned()); + + // and new service transaction is propagated to peers + SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); + + // peer#2 && peer#3 are receiving service transaction + assert!(io + .packets + .iter() + .any(|p| p.packet_id == 0x02 && p.recipient == 2)); // TRANSACTIONS_PACKET + assert!(io + .packets + .iter() + .any(|p| p.packet_id == 0x02 && p.recipient == 3)); // TRANSACTIONS_PACKET + assert_eq!(io.packets.len(), 2); + } + + #[test] + fn should_propagate_service_transaction_is_sent_as_separate_message() { + let mut client = TestBlockChainClient::new(); + let tx1_hash = client.insert_transaction_to_queue(); + let tx2_hash = client.insert_transaction_with_gas_price_to_queue(U256::zero()); + let block_hash = client.block_hash_delta_minus(1); + let mut sync = ChainSync::new( + SyncConfig::default(), + &client, + ForkFilterApi::new_dummy(&client), + ); + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + // when peer#1 is OpenEthereum, accepting service transactions + insert_dummy_peer(&mut sync, 1, block_hash); + io.peers_info + .insert(1, "OpenEthereum/v2.6.0/linux/rustc".to_owned()); + + // and service + non-service transactions are propagated to peers + SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); + + // two separate packets for peer are queued: + // 1) with non-service-transaction + // 2) with service transaction + let sent_transactions: Vec = io + .packets + .iter() + .filter_map(|p| { + if p.packet_id != 0x02 || p.recipient != 1 { + // TRANSACTIONS_PACKET + return None; + } + + let rlp = Rlp::new(&*p.data); + let item_count = rlp.item_count().unwrap_or(0); + if item_count != 1 { + return None; + } + + rlp.at(0).ok().and_then(|r| r.as_val().ok()) + }) + .collect(); + assert_eq!(sent_transactions.len(), 2); + assert!(sent_transactions.iter().any(|tx| tx.hash() == tx1_hash)); + assert!(sent_transactions.iter().any(|tx| tx.hash() == tx2_hash)); + } } diff --git a/ethcore/sync/src/chain/requester.rs b/ethcore/sync/src/chain/requester.rs index 31d3ce59006..1aba74f6b3a 100644 --- a/ethcore/sync/src/chain/requester.rs +++ b/ethcore/sync/src/chain/requester.rs @@ -1,154 +1,237 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use block_sync::BlockRequest; use bytes::Bytes; use ethereum_types::H256; -use network::{PeerId}; +use network::PeerId; use rlp::RlpStream; use std::time::Instant; use sync_io::SyncIo; use types::BlockNumber; -use super::sync_packet::SyncPacket; -use super::sync_packet::SyncPacket::{ - GetBlockHeadersPacket, - GetBlockBodiesPacket, - GetReceiptsPacket, - GetSnapshotManifestPacket, - GetSnapshotDataPacket, +use super::sync_packet::{ + SyncPacket, + SyncPacket::{ + GetBlockBodiesPacket, GetBlockHeadersPacket, GetReceiptsPacket, GetSnapshotDataPacket, + GetSnapshotManifestPacket, + }, }; -use super::{ - BlockSet, - ChainSync, - PeerAsking, -}; +use super::{BlockSet, ChainSync, PeerAsking}; /// The Chain Sync Requester: requesting data to other peers pub struct SyncRequester; impl SyncRequester { - /// Perform block download request` - pub fn request_blocks(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, request: BlockRequest, block_set: BlockSet) { - match request { - BlockRequest::Headers { start, count, skip } => { - SyncRequester::request_headers_by_hash(sync, io, peer_id, &start, count, skip, false, block_set); - }, - BlockRequest::Bodies { hashes } => { - SyncRequester::request_bodies(sync, io, peer_id, hashes, block_set); - }, - BlockRequest::Receipts { hashes } => { - SyncRequester::request_receipts(sync, io, peer_id, hashes, block_set); - }, - } - } - - /// Request block bodies from a peer - fn request_bodies(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, hashes: Vec, set: BlockSet) { - let mut rlp = RlpStream::new_list(hashes.len()); - trace!(target: "sync", "{} <- GetBlockBodies: {} entries starting from {:?}, set = {:?}", peer_id, hashes.len(), hashes.first(), set); - for h in &hashes { - rlp.append(&h.clone()); - } - SyncRequester::send_request(sync, io, peer_id, PeerAsking::BlockBodies, GetBlockBodiesPacket, rlp.out()); - let peer = sync.peers.get_mut(&peer_id).expect("peer_id may originate either from on_packet, where it is already validated or from enumerating self.peers. qed"); - peer.asking_blocks = hashes; - peer.block_set = Some(set); - } - - /// Request headers from a peer by block number - pub fn request_fork_header(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, n: BlockNumber) { - trace!(target: "sync", "{} <- GetForkHeader: at {}", peer_id, n); - let mut rlp = RlpStream::new_list(4); - rlp.append(&n); - rlp.append(&1u32); - rlp.append(&0u32); - rlp.append(&0u32); - SyncRequester::send_request(sync, io, peer_id, PeerAsking::ForkHeader, GetBlockHeadersPacket, rlp.out()); - } - - /// Find some headers or blocks to download for a peer. - pub fn request_snapshot_data(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId) { - // find chunk data to download - if let Some(hash) = sync.snapshot.needed_chunk() { - if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) { - peer.asking_snapshot_data = Some(hash.clone()); - } - SyncRequester::request_snapshot_chunk(sync, io, peer_id, &hash); - } - } - - /// Request snapshot manifest from a peer. - pub fn request_snapshot_manifest(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId) { - trace!(target: "sync", "{} <- GetSnapshotManifest", peer_id); - let rlp = RlpStream::new_list(0); - SyncRequester::send_request(sync, io, peer_id, PeerAsking::SnapshotManifest, GetSnapshotManifestPacket, rlp.out()); - } - - /// Request headers from a peer by block hash - fn request_headers_by_hash(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, h: &H256, count: u64, skip: u64, reverse: bool, set: BlockSet) { - trace!(target: "sync", "{} <- GetBlockHeaders: {} entries starting from {}, set = {:?}", peer_id, count, h, set); - let mut rlp = RlpStream::new_list(4); - rlp.append(h); - rlp.append(&count); - rlp.append(&skip); - rlp.append(&if reverse {1u32} else {0u32}); - SyncRequester::send_request(sync, io, peer_id, PeerAsking::BlockHeaders, GetBlockHeadersPacket, rlp.out()); - let peer = sync.peers.get_mut(&peer_id).expect("peer_id may originate either from on_packet, where it is already validated or from enumerating self.peers. qed"); - peer.asking_hash = Some(h.clone()); - peer.block_set = Some(set); - } - - /// Request block receipts from a peer - fn request_receipts(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, hashes: Vec, set: BlockSet) { - let mut rlp = RlpStream::new_list(hashes.len()); - trace!(target: "sync", "{} <- GetBlockReceipts: {} entries starting from {:?}, set = {:?}", peer_id, hashes.len(), hashes.first(), set); - for h in &hashes { - rlp.append(&h.clone()); - } - SyncRequester::send_request(sync, io, peer_id, PeerAsking::BlockReceipts, GetReceiptsPacket, rlp.out()); - let peer = sync.peers.get_mut(&peer_id).expect("peer_id may originate either from on_packet, where it is already validated or from enumerating self.peers. qed"); - peer.asking_blocks = hashes; - peer.block_set = Some(set); - } - - /// Request snapshot chunk from a peer. - fn request_snapshot_chunk(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, chunk: &H256) { - trace!(target: "sync", "{} <- GetSnapshotData {:?}", peer_id, chunk); - let mut rlp = RlpStream::new_list(1); - rlp.append(chunk); - SyncRequester::send_request(sync, io, peer_id, PeerAsking::SnapshotData, GetSnapshotDataPacket, rlp.out()); - } - - /// Generic request sender - fn send_request(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, asking: PeerAsking, packet_id: SyncPacket, packet: Bytes) { - if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) { - if peer.asking != PeerAsking::Nothing { - warn!(target:"sync", "Asking {:?} while requesting {:?}", peer.asking, asking); - } - peer.asking = asking; - peer.ask_time = Instant::now(); - - let result = io.send(peer_id, packet_id, packet); - - if let Err(e) = result { - debug!(target:"sync", "Error sending request: {:?}", e); - io.disconnect_peer(peer_id); - } - } - } + /// Perform block download request` + pub fn request_blocks( + sync: &mut ChainSync, + io: &mut dyn SyncIo, + peer_id: PeerId, + request: BlockRequest, + block_set: BlockSet, + ) { + match request { + BlockRequest::Headers { start, count, skip } => { + SyncRequester::request_headers_by_hash( + sync, io, peer_id, &start, count, skip, false, block_set, + ); + } + BlockRequest::Bodies { hashes } => { + SyncRequester::request_bodies(sync, io, peer_id, hashes, block_set); + } + BlockRequest::Receipts { hashes } => { + SyncRequester::request_receipts(sync, io, peer_id, hashes, block_set); + } + } + } + + /// Request block bodies from a peer + fn request_bodies( + sync: &mut ChainSync, + io: &mut dyn SyncIo, + peer_id: PeerId, + hashes: Vec, + set: BlockSet, + ) { + let mut rlp = RlpStream::new_list(hashes.len()); + trace!(target: "sync", "{} <- GetBlockBodies: {} entries starting from {:?}, set = {:?}", peer_id, hashes.len(), hashes.first(), set); + for h in &hashes { + rlp.append(&h.clone()); + } + SyncRequester::send_request( + sync, + io, + peer_id, + PeerAsking::BlockBodies, + GetBlockBodiesPacket, + rlp.out(), + ); + let peer = sync.peers.get_mut(&peer_id).expect("peer_id may originate either from on_packet, where it is already validated or from enumerating self.peers. qed"); + peer.asking_blocks = hashes; + peer.block_set = Some(set); + } + + /// Request headers from a peer by block number + pub fn request_fork_header( + sync: &mut ChainSync, + io: &mut dyn SyncIo, + peer_id: PeerId, + n: BlockNumber, + ) { + trace!(target: "sync", "{} <- GetForkHeader: at {}", peer_id, n); + let mut rlp = RlpStream::new_list(4); + rlp.append(&n); + rlp.append(&1u32); + rlp.append(&0u32); + rlp.append(&0u32); + SyncRequester::send_request( + sync, + io, + peer_id, + PeerAsking::ForkHeader, + GetBlockHeadersPacket, + rlp.out(), + ); + } + + /// Find some headers or blocks to download for a peer. + pub fn request_snapshot_data(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId) { + // find chunk data to download + if let Some(hash) = sync.snapshot.needed_chunk() { + if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) { + peer.asking_snapshot_data = Some(hash.clone()); + } + SyncRequester::request_snapshot_chunk(sync, io, peer_id, &hash); + } + } + + /// Request snapshot manifest from a peer. + pub fn request_snapshot_manifest(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId) { + trace!(target: "sync", "{} <- GetSnapshotManifest", peer_id); + let rlp = RlpStream::new_list(0); + SyncRequester::send_request( + sync, + io, + peer_id, + PeerAsking::SnapshotManifest, + GetSnapshotManifestPacket, + rlp.out(), + ); + } + + /// Request headers from a peer by block hash + fn request_headers_by_hash( + sync: &mut ChainSync, + io: &mut dyn SyncIo, + peer_id: PeerId, + h: &H256, + count: u64, + skip: u64, + reverse: bool, + set: BlockSet, + ) { + trace!(target: "sync", "{} <- GetBlockHeaders: {} entries starting from {}, set = {:?}", peer_id, count, h, set); + let mut rlp = RlpStream::new_list(4); + rlp.append(h); + rlp.append(&count); + rlp.append(&skip); + rlp.append(&if reverse { 1u32 } else { 0u32 }); + SyncRequester::send_request( + sync, + io, + peer_id, + PeerAsking::BlockHeaders, + GetBlockHeadersPacket, + rlp.out(), + ); + let peer = sync.peers.get_mut(&peer_id).expect("peer_id may originate either from on_packet, where it is already validated or from enumerating self.peers. qed"); + peer.asking_hash = Some(h.clone()); + peer.block_set = Some(set); + } + + /// Request block receipts from a peer + fn request_receipts( + sync: &mut ChainSync, + io: &mut dyn SyncIo, + peer_id: PeerId, + hashes: Vec, + set: BlockSet, + ) { + let mut rlp = RlpStream::new_list(hashes.len()); + trace!(target: "sync", "{} <- GetBlockReceipts: {} entries starting from {:?}, set = {:?}", peer_id, hashes.len(), hashes.first(), set); + for h in &hashes { + rlp.append(&h.clone()); + } + SyncRequester::send_request( + sync, + io, + peer_id, + PeerAsking::BlockReceipts, + GetReceiptsPacket, + rlp.out(), + ); + let peer = sync.peers.get_mut(&peer_id).expect("peer_id may originate either from on_packet, where it is already validated or from enumerating self.peers. qed"); + peer.asking_blocks = hashes; + peer.block_set = Some(set); + } + + /// Request snapshot chunk from a peer. + fn request_snapshot_chunk( + sync: &mut ChainSync, + io: &mut dyn SyncIo, + peer_id: PeerId, + chunk: &H256, + ) { + trace!(target: "sync", "{} <- GetSnapshotData {:?}", peer_id, chunk); + let mut rlp = RlpStream::new_list(1); + rlp.append(chunk); + SyncRequester::send_request( + sync, + io, + peer_id, + PeerAsking::SnapshotData, + GetSnapshotDataPacket, + rlp.out(), + ); + } + + /// Generic request sender + fn send_request( + sync: &mut ChainSync, + io: &mut dyn SyncIo, + peer_id: PeerId, + asking: PeerAsking, + packet_id: SyncPacket, + packet: Bytes, + ) { + if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) { + if peer.asking != PeerAsking::Nothing { + warn!(target:"sync", "Asking {:?} while requesting {:?}", peer.asking, asking); + } + peer.asking = asking; + peer.ask_time = Instant::now(); + + let result = io.send(peer_id, packet_id, packet); + + if let Err(e) = result { + debug!(target:"sync", "Error sending request: {:?}", e); + io.disconnect_peer(peer_id); + } + } + } } diff --git a/ethcore/sync/src/chain/supplier.rs b/ethcore/sync/src/chain/supplier.rs index 7e71e6aeec7..315adaa99d2 100644 --- a/ethcore/sync/src/chain/supplier.rs +++ b/ethcore/sync/src/chain/supplier.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use bytes::Bytes; use enum_primitive::FromPrimitive; @@ -21,535 +21,632 @@ use network::{self, PeerId}; use parking_lot::RwLock; use rlp::{Rlp, RlpStream}; use std::cmp; -use types::BlockNumber; -use types::ids::BlockId; +use types::{ids::BlockId, BlockNumber}; use sync_io::SyncIo; -use super::sync_packet::{PacketInfo, SyncPacket}; -use super::sync_packet::SyncPacket::{ - StatusPacket, - TransactionsPacket, - GetBlockHeadersPacket, - BlockHeadersPacket, - GetBlockBodiesPacket, - BlockBodiesPacket, - GetNodeDataPacket, - NodeDataPacket, - GetReceiptsPacket, - ReceiptsPacket, - GetSnapshotManifestPacket, - SnapshotManifestPacket, - GetSnapshotDataPacket, - SnapshotDataPacket, - ConsensusDataPacket, +use super::sync_packet::{ + PacketInfo, SyncPacket, + SyncPacket::{ + BlockBodiesPacket, BlockHeadersPacket, ConsensusDataPacket, GetBlockBodiesPacket, + GetBlockHeadersPacket, GetReceiptsPacket, GetSnapshotDataPacket, GetSnapshotManifestPacket, + ReceiptsPacket, SnapshotDataPacket, SnapshotManifestPacket, StatusPacket, + TransactionsPacket, + }, }; use super::{ - ChainSync, - SyncHandler, - RlpResponseResult, - PacketDecodeError, - MAX_BODIES_TO_SEND, - MAX_HEADERS_TO_SEND, - MAX_NODE_DATA_TO_SEND, - MAX_RECEIPTS_HEADERS_TO_SEND, + ChainSync, PacketProcessError, RlpResponseResult, SyncHandler, MAX_BODIES_TO_SEND, + MAX_HEADERS_TO_SEND, MAX_RECEIPTS_HEADERS_TO_SEND, }; /// The Chain Sync Supplier: answers requests from peers with available data pub struct SyncSupplier; impl SyncSupplier { - /// Dispatch incoming requests and responses - // Take a u8 and not a SyncPacketId because this is the entry point - // to chain sync from the outside world. - pub fn dispatch_packet(sync: &RwLock, io: &mut SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) { - let rlp = Rlp::new(data); - - if let Some(id) = SyncPacket::from_u8(packet_id) { - let result = match id { - GetBlockBodiesPacket => SyncSupplier::return_rlp( - io, &rlp, peer, - SyncSupplier::return_block_bodies, - |e| format!("Error sending block bodies: {:?}", e)), - - GetBlockHeadersPacket => SyncSupplier::return_rlp( - io, &rlp, peer, - SyncSupplier::return_block_headers, - |e| format!("Error sending block headers: {:?}", e)), - - GetReceiptsPacket => SyncSupplier::return_rlp( - io, &rlp, peer, - SyncSupplier::return_receipts, - |e| format!("Error sending receipts: {:?}", e)), - - GetNodeDataPacket => SyncSupplier::return_rlp( - io, &rlp, peer, - SyncSupplier::return_node_data, - |e| format!("Error sending nodes: {:?}", e)), - - GetSnapshotManifestPacket => SyncSupplier::return_rlp( - io, &rlp, peer, - SyncSupplier::return_snapshot_manifest, - |e| format!("Error sending snapshot manifest: {:?}", e)), - - GetSnapshotDataPacket => SyncSupplier::return_rlp( - io, &rlp, peer, - SyncSupplier::return_snapshot_data, - |e| format!("Error sending snapshot data: {:?}", e)), - - StatusPacket => { - sync.write().on_packet(io, peer, packet_id, data); - Ok(()) - }, - // Packets that require the peer to be confirmed - _ => { - if !sync.read().peers.contains_key(&peer) { - debug!(target:"sync", "Unexpected packet {} from unregistered peer: {}:{}", packet_id, peer, io.peer_version(peer)); - return; - } - debug!(target: "sync", "{} -> Dispatching packet: {}", peer, packet_id); - - match id { - ConsensusDataPacket => { - SyncHandler::on_consensus_packet(io, peer, &rlp) - }, - TransactionsPacket => { - let res = { - let sync_ro = sync.read(); - SyncHandler::on_peer_transactions(&*sync_ro, io, peer, &rlp) - }; - if res.is_err() { - // peer sent invalid data, disconnect. - io.disable_peer(peer); - sync.write().deactivate_peer(io, peer); - } - }, - _ => { - sync.write().on_packet(io, peer, packet_id, data); - } - } - - Ok(()) - } - }; - - result.unwrap_or_else(|e| { - debug!(target:"sync", "{} -> Malformed packet {} : {}", peer, packet_id, e); - }) - } - } - - /// Respond to GetBlockHeaders request - fn return_block_headers(io: &SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { - let payload_soft_limit = io.payload_soft_limit(); - // Packet layout: - // [ block: { P , B_32 }, maxHeaders: P, skip: P, reverse: P in { 0 , 1 } ] - let max_headers: usize = r.val_at(1)?; - let skip: usize = r.val_at(2)?; - let reverse: bool = r.val_at(3)?; - let last = io.chain().chain_info().best_block_number; - let number = if r.at(0)?.size() == 32 { - // id is a hash - let hash: H256 = r.val_at(0)?; - trace!(target: "sync", "{} -> GetBlockHeaders (hash: {}, max: {}, skip: {}, reverse:{})", peer_id, hash, max_headers, skip, reverse); - match io.chain().block_header(BlockId::Hash(hash)) { - Some(hdr) => { - let number = hdr.number().into(); - debug_assert_eq!(hdr.hash(), hash); - - if max_headers == 1 || io.chain().block_hash(BlockId::Number(number)) != Some(hash) { - // Non canonical header or single header requested - // TODO: handle single-step reverse hashchains of non-canon hashes - trace!(target:"sync", "Returning single header: {:?}", hash); - let mut rlp = RlpStream::new_list(1); - rlp.append_raw(&hdr.into_inner(), 1); - return Ok(Some((BlockHeadersPacket.id(), rlp))); - } - number - } - None => return Ok(Some((BlockHeadersPacket.id(), RlpStream::new_list(0)))) //no such header, return nothing - } - } else { - let number = r.val_at::(0)?; - trace!(target: "sync", "{} -> GetBlockHeaders (number: {}, max: {}, skip: {}, reverse:{})", peer_id, number, max_headers, skip, reverse); - number - }; - - let mut number = if reverse { - cmp::min(last, number) - } else { - cmp::max(0, number) - }; - let max_count = cmp::min(MAX_HEADERS_TO_SEND, max_headers); - let mut count = 0; - let mut data = Bytes::new(); - let inc = skip.saturating_add(1) as BlockNumber; - let overlay = io.chain_overlay().read(); - - // We are checking the `overlay` as well since it's where the ForkBlock - // header is cached : so peers can confirm we are on the right fork, - // even if we are not synced until the fork block - while (number <= last || overlay.contains_key(&number)) && count < max_count { - if let Some(hdr) = overlay.get(&number) { - trace!(target: "sync", "{}: Returning cached fork header", peer_id); - data.extend_from_slice(hdr); - count += 1; - } else if let Some(hdr) = io.chain().block_header(BlockId::Number(number)) { - data.append(&mut hdr.into_inner()); - count += 1; - // Check that the packet won't be oversized - if data.len() > payload_soft_limit { - break; - } - } else { - // No required block. - break; - } - if reverse { - if number <= inc || number == 0 { - break; - } - number = number.saturating_sub(inc); - } else { - number = number.saturating_add(inc); - } - } - let mut rlp = RlpStream::new_list(count as usize); - rlp.append_raw(&data, count as usize); - trace!(target: "sync", "{} -> GetBlockHeaders: returned {} entries", peer_id, count); - Ok(Some((BlockHeadersPacket.id(), rlp))) - } - - /// Respond to GetBlockBodies request - fn return_block_bodies(io: &SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { - let payload_soft_limit = io.payload_soft_limit(); - let mut count = r.item_count().unwrap_or(0); - if count == 0 { - debug!(target: "sync", "Empty GetBlockBodies request, ignoring."); - return Ok(None); - } - count = cmp::min(count, MAX_BODIES_TO_SEND); - let mut added = 0usize; - let mut data = Bytes::new(); - for i in 0..count { - if let Some(body) = io.chain().block_body(BlockId::Hash(r.val_at::(i)?)) { - data.append(&mut body.into_inner()); - added += 1; - // Check that the packet won't be oversized - if data.len() > payload_soft_limit { - break; - } - } - } - let mut rlp = RlpStream::new_list(added); - rlp.append_raw(&data, added); - trace!(target: "sync", "{} -> GetBlockBodies: returned {} entries", peer_id, added); - Ok(Some((BlockBodiesPacket.id(), rlp))) - } - - /// Respond to GetNodeData request - fn return_node_data(io: &SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { - let payload_soft_limit = io.payload_soft_limit(); - let mut count = r.item_count().unwrap_or(0); - trace!(target: "sync", "{} -> GetNodeData: {} entries", peer_id, count); - if count == 0 { - debug!(target: "sync", "Empty GetNodeData request, ignoring."); - return Ok(None); - } - count = cmp::min(count, MAX_NODE_DATA_TO_SEND); - let mut added = 0usize; - let mut data = Vec::new(); - let mut total_bytes = 0; - for i in 0..count { - if let Some(node) = io.chain().state_data(&r.val_at::(i)?) { - total_bytes += node.len(); - // Check that the packet won't be oversized - if total_bytes > payload_soft_limit { - break; - } - data.push(node); - added += 1; - } - } - trace!(target: "sync", "{} -> GetNodeData: return {} entries", peer_id, added); - let mut rlp = RlpStream::new_list(added); - for d in data { - rlp.append(&d); - } - Ok(Some((NodeDataPacket.id(), rlp))) - } - - fn return_receipts(io: &SyncIo, rlp: &Rlp, peer_id: PeerId) -> RlpResponseResult { - let payload_soft_limit = io.payload_soft_limit(); - let mut count = rlp.item_count().unwrap_or(0); - trace!(target: "sync", "{} -> GetReceipts: {} entries", peer_id, count); - if count == 0 { - debug!(target: "sync", "Empty GetReceipts request, ignoring."); - return Ok(None); - } - count = cmp::min(count, MAX_RECEIPTS_HEADERS_TO_SEND); - let mut added_headers = 0usize; - let mut data = Bytes::new(); - let mut total_bytes = 0; - for i in 0..count { - if let Some(receipts) = io.chain().block_receipts(&rlp.val_at::(i)?) { - let mut receipts_bytes = ::rlp::encode(&receipts); - total_bytes += receipts_bytes.len(); - if total_bytes > payload_soft_limit { break; } - data.append(&mut receipts_bytes); - added_headers += 1; - } - } - let mut rlp_result = RlpStream::new_list(added_headers); - rlp_result.append_raw(&data, added_headers); - Ok(Some((ReceiptsPacket.id(), rlp_result))) - } - - /// Respond to GetSnapshotManifest request - fn return_snapshot_manifest(io: &SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { - let count = r.item_count().unwrap_or(0); - trace!(target: "warp", "{} -> GetSnapshotManifest", peer_id); - if count != 0 { - debug!(target: "warp", "Invalid GetSnapshotManifest request, ignoring."); - return Ok(None); - } - let rlp = match io.snapshot_service().manifest() { - Some(manifest) => { - trace!(target: "warp", "{} <- SnapshotManifest", peer_id); - let mut rlp = RlpStream::new_list(1); - rlp.append_raw(&manifest.into_rlp(), 1); - rlp - }, - None => { - trace!(target: "warp", "{}: No snapshot manifest to return", peer_id); - RlpStream::new_list(0) - } - }; - Ok(Some((SnapshotManifestPacket.id(), rlp))) - } - - /// Respond to GetSnapshotData request - fn return_snapshot_data(io: &SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { - let hash: H256 = r.val_at(0)?; - trace!(target: "warp", "{} -> GetSnapshotData {:?}", peer_id, hash); - let rlp = match io.snapshot_service().chunk(hash) { - Some(data) => { - let mut rlp = RlpStream::new_list(1); - trace!(target: "warp", "{} <- SnapshotData", peer_id); - rlp.append(&data); - rlp - }, - None => { - trace!(target: "warp", "{}: No snapshot data to return", peer_id); - RlpStream::new_list(0) - } - }; - Ok(Some((SnapshotDataPacket.id(), rlp))) - } - - fn return_rlp(io: &mut SyncIo, rlp: &Rlp, peer: PeerId, rlp_func: FRlp, error_func: FError) -> Result<(), PacketDecodeError> - where FRlp : Fn(&SyncIo, &Rlp, PeerId) -> RlpResponseResult, - FError : FnOnce(network::Error) -> String - { - let response = rlp_func(io, rlp, peer); - match response { - Err(e) => Err(e), - Ok(Some((packet_id, rlp_stream))) => { - io.respond(packet_id, rlp_stream.out()).unwrap_or_else( - |e| debug!(target: "sync", "{:?}", error_func(e))); - Ok(()) - } - _ => Ok(()) - } - } + /// Dispatch incoming requests and responses + // Take a u8 and not a SyncPacketId because this is the entry point + // to chain sync from the outside world. + pub fn dispatch_packet( + sync: &RwLock, + io: &mut dyn SyncIo, + peer: PeerId, + packet_id: u8, + data: &[u8], + ) { + let rlp = Rlp::new(data); + + if let Some(id) = SyncPacket::from_u8(packet_id) { + let result = match id { + GetBlockBodiesPacket => SyncSupplier::return_rlp( + io, + &rlp, + peer, + SyncSupplier::return_block_bodies, + |e| format!("Error sending block bodies: {:?}", e), + ), + + GetBlockHeadersPacket => SyncSupplier::return_rlp( + io, + &rlp, + peer, + SyncSupplier::return_block_headers, + |e| format!("Error sending block headers: {:?}", e), + ), + + GetReceiptsPacket => { + SyncSupplier::return_rlp(io, &rlp, peer, SyncSupplier::return_receipts, |e| { + format!("Error sending receipts: {:?}", e) + }) + } + GetSnapshotManifestPacket => SyncSupplier::return_rlp( + io, + &rlp, + peer, + SyncSupplier::return_snapshot_manifest, + |e| format!("Error sending snapshot manifest: {:?}", e), + ), + + GetSnapshotDataPacket => SyncSupplier::return_rlp( + io, + &rlp, + peer, + SyncSupplier::return_snapshot_data, + |e| format!("Error sending snapshot data: {:?}", e), + ), + + StatusPacket => { + sync.write().on_packet(io, peer, packet_id, data); + Ok(()) + } + // Packets that require the peer to be confirmed + _ => { + if !sync.read().peers.contains_key(&peer) { + debug!(target:"sync", "Unexpected packet {} from unregistered peer: {}:{}", packet_id, peer, io.peer_version(peer)); + return; + } + debug!(target: "sync", "{} -> Dispatching packet: {}", peer, packet_id); + + match id { + ConsensusDataPacket => SyncHandler::on_consensus_packet(io, peer, &rlp), + TransactionsPacket => { + let res = { + let sync_ro = sync.read(); + SyncHandler::on_peer_transactions(&*sync_ro, io, peer, &rlp) + }; + if res.is_err() { + // peer sent invalid data, disconnect. + io.disable_peer(peer); + sync.write().deactivate_peer(io, peer); + } + } + _ => { + sync.write().on_packet(io, peer, packet_id, data); + } + } + + Ok(()) + } + }; + + match result { + Err(PacketProcessError::Decoder(e)) => { + debug!(target:"sync", "{} -> Malformed packet {} : {}", peer, packet_id, e) + } + Err(PacketProcessError::ClientBusy) => { + sync.write().add_delayed_request(peer, packet_id, data) + } + Ok(()) => {} + } + } + } + + /// Dispatch delayed request + /// The main difference with dispatch packet is the direct send of the responses to the peer + pub fn dispatch_delayed_request( + sync: &RwLock, + io: &mut dyn SyncIo, + peer: PeerId, + packet_id: u8, + data: &[u8], + ) { + let rlp = Rlp::new(data); + + if let Some(id) = SyncPacket::from_u8(packet_id) { + let result = match id { + GetBlockHeadersPacket => SyncSupplier::send_rlp( + io, + &rlp, + peer, + SyncSupplier::return_block_headers, + |e| format!("Error sending block headers: {:?}", e), + ), + + _ => { + debug!(target:"sync", "Unexpected packet {} was dispatched for delayed processing", packet_id); + Ok(()) + } + }; + + match result { + Err(PacketProcessError::Decoder(e)) => { + debug!(target:"sync", "{} -> Malformed packet {} : {}", peer, packet_id, e) + } + Err(PacketProcessError::ClientBusy) => { + sync.write().add_delayed_request(peer, packet_id, data) + } + Ok(()) => {} + } + } + } + + /// Respond to GetBlockHeaders request + fn return_block_headers(io: &dyn SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { + // Cannot return blocks, if forks processing is in progress, + // The request should be postponed for later processing + if io.chain().is_processing_fork() { + return Err(PacketProcessError::ClientBusy); + } + let payload_soft_limit = io.payload_soft_limit(); + // Packet layout: + // [ block: { P , B_32 }, maxHeaders: P, skip: P, reverse: P in { 0 , 1 } ] + let max_headers: usize = r.val_at(1)?; + let skip: usize = r.val_at(2)?; + let reverse: bool = r.val_at(3)?; + let last = io.chain().chain_info().best_block_number; + let number = if r.at(0)?.size() == 32 { + // id is a hash + let hash: H256 = r.val_at(0)?; + trace!(target: "sync", "{} -> GetBlockHeaders (hash: {}, max: {}, skip: {}, reverse:{})", peer_id, hash, max_headers, skip, reverse); + match io.chain().block_header(BlockId::Hash(hash)) { + Some(hdr) => { + let number = hdr.number().into(); + debug_assert_eq!(hdr.hash(), hash); + + if max_headers == 1 + || io.chain().block_hash(BlockId::Number(number)) != Some(hash) + { + // Non canonical header or single header requested + // TODO: handle single-step reverse hashchains of non-canon hashes + trace!(target:"sync", "Returning single header: {:?}", hash); + let mut rlp = RlpStream::new_list(1); + rlp.append_raw(&hdr.into_inner(), 1); + return Ok(Some((BlockHeadersPacket, rlp))); + } + number + } + None => return Ok(Some((BlockHeadersPacket, RlpStream::new_list(0)))), //no such header, return nothing + } + } else { + let number = r.val_at::(0)?; + trace!(target: "sync", "{} -> GetBlockHeaders (number: {}, max: {}, skip: {}, reverse:{})", peer_id, number, max_headers, skip, reverse); + number + }; + + let mut number = if reverse { + cmp::min(last, number) + } else { + cmp::max(0, number) + }; + let max_count = cmp::min(MAX_HEADERS_TO_SEND, max_headers); + let mut count = 0; + let mut data = Bytes::new(); + let inc = skip.saturating_add(1) as BlockNumber; + let overlay = io.chain_overlay().read(); + + // We are checking the `overlay` as well since it's where the ForkBlock + // header is cached : so peers can confirm we are on the right fork, + // even if we are not synced until the fork block + while (number <= last || overlay.contains_key(&number)) && count < max_count { + if let Some(hdr) = overlay.get(&number) { + trace!(target: "sync", "{}: Returning cached fork header", peer_id); + data.extend_from_slice(hdr); + count += 1; + } else if let Some(hdr) = io.chain().block_header(BlockId::Number(number)) { + data.append(&mut hdr.into_inner()); + count += 1; + // Check that the packet won't be oversized + if data.len() > payload_soft_limit { + break; + } + } else { + // No required block. + break; + } + if reverse { + if number <= inc || number == 0 { + break; + } + number = number.saturating_sub(inc); + } else { + number = number.saturating_add(inc); + } + } + let mut rlp = RlpStream::new_list(count as usize); + rlp.append_raw(&data, count as usize); + trace!(target: "sync", "{} -> GetBlockHeaders: returned {} entries", peer_id, count); + Ok(Some((BlockHeadersPacket, rlp))) + } + + /// Respond to GetBlockBodies request + fn return_block_bodies(io: &dyn SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { + let payload_soft_limit = io.payload_soft_limit(); + let mut count = r.item_count().unwrap_or(0); + if count == 0 { + debug!(target: "sync", "Empty GetBlockBodies request, ignoring."); + return Ok(None); + } + count = cmp::min(count, MAX_BODIES_TO_SEND); + let mut added = 0usize; + let mut data = Bytes::new(); + for i in 0..count { + if let Some(body) = io.chain().block_body(BlockId::Hash(r.val_at::(i)?)) { + data.append(&mut body.into_inner()); + added += 1; + // Check that the packet won't be oversized + if data.len() > payload_soft_limit { + break; + } + } + } + let mut rlp = RlpStream::new_list(added); + rlp.append_raw(&data, added); + trace!(target: "sync", "{} -> GetBlockBodies: returned {} entries", peer_id, added); + Ok(Some((BlockBodiesPacket, rlp))) + } + + fn return_receipts(io: &dyn SyncIo, rlp: &Rlp, peer_id: PeerId) -> RlpResponseResult { + let payload_soft_limit = io.payload_soft_limit(); + let mut count = rlp.item_count().unwrap_or(0); + trace!(target: "sync", "{} -> GetReceipts: {} entries", peer_id, count); + if count == 0 { + debug!(target: "sync", "Empty GetReceipts request, ignoring."); + return Ok(None); + } + count = cmp::min(count, MAX_RECEIPTS_HEADERS_TO_SEND); + let mut added_headers = 0usize; + let mut data = Bytes::new(); + let mut total_bytes = 0; + for i in 0..count { + if let Some(receipts) = io.chain().block_receipts(&rlp.val_at::(i)?) { + let mut receipts_bytes = ::rlp::encode(&receipts); + total_bytes += receipts_bytes.len(); + if total_bytes > payload_soft_limit { + break; + } + data.append(&mut receipts_bytes); + added_headers += 1; + } + } + let mut rlp_result = RlpStream::new_list(added_headers); + rlp_result.append_raw(&data, added_headers); + Ok(Some((ReceiptsPacket, rlp_result))) + } + + /// Respond to GetSnapshotManifest request + fn return_snapshot_manifest(io: &dyn SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { + let count = r.item_count().unwrap_or(0); + trace!(target: "warp", "{} -> GetSnapshotManifest", peer_id); + if count != 0 { + debug!(target: "warp", "Invalid GetSnapshotManifest request, ignoring."); + return Ok(None); + } + let rlp = match io.snapshot_service().manifest() { + Some(manifest) => { + trace!(target: "warp", "{} <- SnapshotManifest", peer_id); + let mut rlp = RlpStream::new_list(1); + rlp.append_raw(&manifest.into_rlp(), 1); + rlp + } + None => { + trace!(target: "warp", "{}: No snapshot manifest to return", peer_id); + RlpStream::new_list(0) + } + }; + Ok(Some((SnapshotManifestPacket, rlp))) + } + + /// Respond to GetSnapshotData request + fn return_snapshot_data(io: &dyn SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { + let hash: H256 = r.val_at(0)?; + trace!(target: "warp", "{} -> GetSnapshotData {:?}", peer_id, hash); + let rlp = match io.snapshot_service().chunk(hash) { + Some(data) => { + let mut rlp = RlpStream::new_list(1); + trace!(target: "warp", "{} <- SnapshotData", peer_id); + rlp.append(&data); + rlp + } + None => { + trace!(target: "warp", "{}: No snapshot data to return", peer_id); + RlpStream::new_list(0) + } + }; + Ok(Some((SnapshotDataPacket, rlp))) + } + + fn return_rlp( + io: &mut dyn SyncIo, + rlp: &Rlp, + peer: PeerId, + rlp_func: FRlp, + error_func: FError, + ) -> Result<(), PacketProcessError> + where + FRlp: Fn(&dyn SyncIo, &Rlp, PeerId) -> RlpResponseResult, + FError: FnOnce(network::Error) -> String, + { + let response = rlp_func(io, rlp, peer); + if let Some((packet_id, rlp_stream)) = response? { + io.respond(packet_id.id(), rlp_stream.out()) + .unwrap_or_else(|e| debug!(target: "sync", "{:?}", error_func(e))); + } + Ok(()) + } + + fn send_rlp( + io: &mut dyn SyncIo, + rlp: &Rlp, + peer: PeerId, + rlp_func: FRlp, + error_func: FError, + ) -> Result<(), PacketProcessError> + where + FRlp: Fn(&dyn SyncIo, &Rlp, PeerId) -> RlpResponseResult, + FError: FnOnce(network::Error) -> String, + { + let response = rlp_func(io, rlp, peer); + match response { + Err(e) => Err(e), + Ok(Some((packet_id, rlp_stream))) => { + io.send(peer, packet_id, rlp_stream.out()) + .unwrap_or_else(|e| debug!(target: "sync", "{:?}", error_func(e))); + Ok(()) + } + _ => Ok(()), + } + } } #[cfg(test)] mod test { - use std::collections::{VecDeque}; - use tests::helpers::{TestIo}; - use tests::snapshot::TestSnapshotService; - use ethereum_types::{H256}; - use parking_lot::RwLock; - use bytes::Bytes; - use rlp::{Rlp, RlpStream}; - use super::{*, super::tests::*}; - use blocks::SyncHeader; - use ethcore::client::{BlockChainClient, EachBlockWith, TestBlockChainClient}; - - #[test] - fn return_block_headers() { - fn make_hash_req(h: &H256, count: usize, skip: usize, reverse: bool) -> Bytes { - let mut rlp = RlpStream::new_list(4); - rlp.append(h); - rlp.append(&count); - rlp.append(&skip); - rlp.append(&if reverse {1u32} else {0u32}); - rlp.out() - } - - fn make_num_req(n: usize, count: usize, skip: usize, reverse: bool) -> Bytes { - let mut rlp = RlpStream::new_list(4); - rlp.append(&n); - rlp.append(&count); - rlp.append(&skip); - rlp.append(&if reverse {1u32} else {0u32}); - rlp.out() - } - fn to_header_vec(rlp: ::chain::RlpResponseResult) -> Vec { - Rlp::new(&rlp.unwrap().unwrap().1.out()).iter().map(|r| SyncHeader::from_rlp(r.as_raw().to_vec()).unwrap()).collect() - } - - let mut client = TestBlockChainClient::new(); - client.add_blocks(100, EachBlockWith::Nothing); - let blocks: Vec<_> = (0 .. 100) - .map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).map(|b| b.into_inner()).unwrap()).collect(); - let headers: Vec<_> = blocks.iter().map(|b| SyncHeader::from_rlp(Rlp::new(b).at(0).unwrap().as_raw().to_vec()).unwrap()).collect(); - let hashes: Vec<_> = headers.iter().map(|h| h.header.hash()).collect(); - - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let io = TestIo::new(&mut client, &ss, &queue, None); - - let unknown: H256 = H256::new(); - let result = SyncSupplier::return_block_headers(&io, &Rlp::new(&make_hash_req(&unknown, 1, 0, false)), 0); - assert!(to_header_vec(result).is_empty()); - let result = SyncSupplier::return_block_headers(&io, &Rlp::new(&make_hash_req(&unknown, 1, 0, true)), 0); - assert!(to_header_vec(result).is_empty()); - - let result = SyncSupplier::return_block_headers(&io, &Rlp::new(&make_hash_req(&hashes[2], 1, 0, true)), 0); - assert_eq!(to_header_vec(result), vec![headers[2].clone()]); - - let result = SyncSupplier::return_block_headers(&io, &Rlp::new(&make_hash_req(&hashes[2], 1, 0, false)), 0); - assert_eq!(to_header_vec(result), vec![headers[2].clone()]); - - let result = SyncSupplier::return_block_headers(&io, &Rlp::new(&make_hash_req(&hashes[50], 3, 5, false)), 0); - assert_eq!(to_header_vec(result), vec![headers[50].clone(), headers[56].clone(), headers[62].clone()]); - - let result = SyncSupplier::return_block_headers(&io, &Rlp::new(&make_hash_req(&hashes[50], 3, 5, true)), 0); - assert_eq!(to_header_vec(result), vec![headers[50].clone(), headers[44].clone(), headers[38].clone()]); - - let result = SyncSupplier::return_block_headers(&io, &Rlp::new(&make_num_req(2, 1, 0, true)), 0); - assert_eq!(to_header_vec(result), vec![headers[2].clone()]); - - let result = SyncSupplier::return_block_headers(&io, &Rlp::new(&make_num_req(2, 1, 0, false)), 0); - assert_eq!(to_header_vec(result), vec![headers[2].clone()]); - - let result = SyncSupplier::return_block_headers(&io, &Rlp::new(&make_num_req(50, 3, 5, false)), 0); - assert_eq!(to_header_vec(result), vec![headers[50].clone(), headers[56].clone(), headers[62].clone()]); - - let result = SyncSupplier::return_block_headers(&io, &Rlp::new(&make_num_req(50, 3, 5, true)), 0); - assert_eq!(to_header_vec(result), vec![headers[50].clone(), headers[44].clone(), headers[38].clone()]); - } - - #[test] - fn respect_packet_limit() { - let small_num_blocks = 10; - let large_num_blocks = 50; - let tx_per_block = 100; - - let mut client = TestBlockChainClient::new(); - client.add_blocks(large_num_blocks, EachBlockWith::Transactions(tx_per_block)); - - let mut small_rlp_request = RlpStream::new_list(small_num_blocks); - let mut large_rlp_request = RlpStream::new_list(large_num_blocks); - - for i in 0..small_num_blocks { - let hash: H256 = client.block_hash(BlockId::Number(i as u64)).unwrap(); - small_rlp_request.append(&hash); - large_rlp_request.append(&hash); - } - - for i in small_num_blocks..large_num_blocks { - let hash: H256 = client.block_hash(BlockId::Number(i as u64)).unwrap(); - large_rlp_request.append(&hash); - } - - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let io = TestIo::new(&mut client, &ss, &queue, None); - - let small_result = SyncSupplier::return_block_bodies(&io, &Rlp::new(&small_rlp_request.out()), 0); - let small_result = small_result.unwrap().unwrap().1; - assert_eq!(Rlp::new(&small_result.out()).item_count().unwrap(), small_num_blocks); - - let large_result = SyncSupplier::return_block_bodies(&io, &Rlp::new(&large_rlp_request.out()), 0); - let large_result = large_result.unwrap().unwrap().1; - assert!(Rlp::new(&large_result.out()).item_count().unwrap() < large_num_blocks); - } - - #[test] - fn return_nodes() { - let mut client = TestBlockChainClient::new(); - let queue = RwLock::new(VecDeque::new()); - let sync = dummy_sync_with_peer(H256::new(), &client); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - let mut node_list = RlpStream::new_list(3); - node_list.append(&H256::from("0000000000000000000000000000000000000000000000005555555555555555")); - node_list.append(&H256::from("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa")); - node_list.append(&H256::from("aff0000000000000000000000000000000000000000000000000000000000000")); - - let node_request = node_list.out(); - // it returns rlp ONLY for hashes started with "f" - let result = SyncSupplier::return_node_data(&io, &Rlp::new(&node_request.clone()), 0); - - assert!(result.is_ok()); - let rlp_result = result.unwrap(); - assert!(rlp_result.is_some()); - - // the length of one rlp-encoded hashe - let rlp = rlp_result.unwrap().1.out(); - let rlp = Rlp::new(&rlp); - assert_eq!(Ok(1), rlp.item_count()); - - io.sender = Some(2usize); - - SyncSupplier::dispatch_packet(&RwLock::new(sync), &mut io, 0usize, GetNodeDataPacket.id(), &node_request); - assert_eq!(1, io.packets.len()); - } - - #[test] - fn return_receipts_empty() { - let mut client = TestBlockChainClient::new(); - let queue = RwLock::new(VecDeque::new()); - let ss = TestSnapshotService::new(); - let io = TestIo::new(&mut client, &ss, &queue, None); - - let result = SyncSupplier::return_receipts(&io, &Rlp::new(&[0xc0]), 0); - - assert!(result.is_ok()); - } - - #[test] - fn return_receipts() { - let mut client = TestBlockChainClient::new(); - let queue = RwLock::new(VecDeque::new()); - let sync = dummy_sync_with_peer(H256::new(), &client); - let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); - - let mut receipt_list = RlpStream::new_list(4); - receipt_list.append(&H256::from("0000000000000000000000000000000000000000000000005555555555555555")); - receipt_list.append(&H256::from("ff00000000000000000000000000000000000000000000000000000000000000")); - receipt_list.append(&H256::from("fff0000000000000000000000000000000000000000000000000000000000000")); - receipt_list.append(&H256::from("aff0000000000000000000000000000000000000000000000000000000000000")); - - let receipts_request = receipt_list.out(); - // it returns rlp ONLY for hashes started with "f" - let result = SyncSupplier::return_receipts(&io, &Rlp::new(&receipts_request.clone()), 0); - - assert!(result.is_ok()); - let rlp_result = result.unwrap(); - assert!(rlp_result.is_some()); - - // the length of two rlp-encoded receipts - assert_eq!(603, rlp_result.unwrap().1.out().len()); - - io.sender = Some(2usize); - SyncSupplier::dispatch_packet(&RwLock::new(sync), &mut io, 0usize, GetReceiptsPacket.id(), &receipts_request); - assert_eq!(1, io.packets.len()); - } + use super::{super::tests::*, *}; + use blocks::SyncHeader; + use bytes::Bytes; + use ethcore::client::{BlockChainClient, EachBlockWith, TestBlockChainClient}; + use ethereum_types::H256; + use parking_lot::RwLock; + use rlp::{Rlp, RlpStream}; + use std::collections::VecDeque; + use tests::{helpers::TestIo, snapshot::TestSnapshotService}; + + #[test] + fn return_block_headers() { + fn make_hash_req(h: &H256, count: usize, skip: usize, reverse: bool) -> Bytes { + let mut rlp = RlpStream::new_list(4); + rlp.append(h); + rlp.append(&count); + rlp.append(&skip); + rlp.append(&if reverse { 1u32 } else { 0u32 }); + rlp.out() + } + + fn make_num_req(n: usize, count: usize, skip: usize, reverse: bool) -> Bytes { + let mut rlp = RlpStream::new_list(4); + rlp.append(&n); + rlp.append(&count); + rlp.append(&skip); + rlp.append(&if reverse { 1u32 } else { 0u32 }); + rlp.out() + } + fn to_header_vec(rlp: ::chain::RlpResponseResult) -> Vec { + Rlp::new(&rlp.unwrap().unwrap().1.out()) + .iter() + .map(|r| SyncHeader::from_rlp(r.as_raw().to_vec()).unwrap()) + .collect() + } + + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Nothing); + let blocks: Vec<_> = (0..100) + .map(|i| { + (&client as &dyn BlockChainClient) + .block(BlockId::Number(i as BlockNumber)) + .map(|b| b.into_inner()) + .unwrap() + }) + .collect(); + let headers: Vec<_> = blocks + .iter() + .map(|b| SyncHeader::from_rlp(Rlp::new(b).at(0).unwrap().as_raw().to_vec()).unwrap()) + .collect(); + let hashes: Vec<_> = headers.iter().map(|h| h.header.hash()).collect(); + + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let io = TestIo::new(&mut client, &ss, &queue, None); + + let unknown: H256 = H256::new(); + let result = SyncSupplier::return_block_headers( + &io, + &Rlp::new(&make_hash_req(&unknown, 1, 0, false)), + 0, + ); + assert!(to_header_vec(result).is_empty()); + let result = SyncSupplier::return_block_headers( + &io, + &Rlp::new(&make_hash_req(&unknown, 1, 0, true)), + 0, + ); + assert!(to_header_vec(result).is_empty()); + + let result = SyncSupplier::return_block_headers( + &io, + &Rlp::new(&make_hash_req(&hashes[2], 1, 0, true)), + 0, + ); + assert_eq!(to_header_vec(result), vec![headers[2].clone()]); + + let result = SyncSupplier::return_block_headers( + &io, + &Rlp::new(&make_hash_req(&hashes[2], 1, 0, false)), + 0, + ); + assert_eq!(to_header_vec(result), vec![headers[2].clone()]); + + let result = SyncSupplier::return_block_headers( + &io, + &Rlp::new(&make_hash_req(&hashes[50], 3, 5, false)), + 0, + ); + assert_eq!( + to_header_vec(result), + vec![ + headers[50].clone(), + headers[56].clone(), + headers[62].clone() + ] + ); + + let result = SyncSupplier::return_block_headers( + &io, + &Rlp::new(&make_hash_req(&hashes[50], 3, 5, true)), + 0, + ); + assert_eq!( + to_header_vec(result), + vec![ + headers[50].clone(), + headers[44].clone(), + headers[38].clone() + ] + ); + + let result = + SyncSupplier::return_block_headers(&io, &Rlp::new(&make_num_req(2, 1, 0, true)), 0); + assert_eq!(to_header_vec(result), vec![headers[2].clone()]); + + let result = + SyncSupplier::return_block_headers(&io, &Rlp::new(&make_num_req(2, 1, 0, false)), 0); + assert_eq!(to_header_vec(result), vec![headers[2].clone()]); + + let result = + SyncSupplier::return_block_headers(&io, &Rlp::new(&make_num_req(50, 3, 5, false)), 0); + assert_eq!( + to_header_vec(result), + vec![ + headers[50].clone(), + headers[56].clone(), + headers[62].clone() + ] + ); + + let result = + SyncSupplier::return_block_headers(&io, &Rlp::new(&make_num_req(50, 3, 5, true)), 0); + assert_eq!( + to_header_vec(result), + vec![ + headers[50].clone(), + headers[44].clone(), + headers[38].clone() + ] + ); + } + + #[test] + fn respect_packet_limit() { + let small_num_blocks = 10; + let large_num_blocks = 50; + let tx_per_block = 100; + + let mut client = TestBlockChainClient::new(); + client.add_blocks(large_num_blocks, EachBlockWith::Transactions(tx_per_block)); + + let mut small_rlp_request = RlpStream::new_list(small_num_blocks); + let mut large_rlp_request = RlpStream::new_list(large_num_blocks); + + for i in 0..small_num_blocks { + let hash: H256 = client.block_hash(BlockId::Number(i as u64)).unwrap(); + small_rlp_request.append(&hash); + large_rlp_request.append(&hash); + } + + for i in small_num_blocks..large_num_blocks { + let hash: H256 = client.block_hash(BlockId::Number(i as u64)).unwrap(); + large_rlp_request.append(&hash); + } + + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let io = TestIo::new(&mut client, &ss, &queue, None); + + let small_result = + SyncSupplier::return_block_bodies(&io, &Rlp::new(&small_rlp_request.out()), 0); + let small_result = small_result.unwrap().unwrap().1; + assert_eq!( + Rlp::new(&small_result.out()).item_count().unwrap(), + small_num_blocks + ); + + let large_result = + SyncSupplier::return_block_bodies(&io, &Rlp::new(&large_rlp_request.out()), 0); + let large_result = large_result.unwrap().unwrap().1; + assert!(Rlp::new(&large_result.out()).item_count().unwrap() < large_num_blocks); + } + + #[test] + fn return_receipts_empty() { + let mut client = TestBlockChainClient::new(); + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let io = TestIo::new(&mut client, &ss, &queue, None); + + let result = SyncSupplier::return_receipts(&io, &Rlp::new(&[0xc0]), 0); + + assert!(result.is_ok()); + } + + #[test] + fn return_receipts() { + let mut client = TestBlockChainClient::new(); + let queue = RwLock::new(VecDeque::new()); + let sync = dummy_sync_with_peer(H256::new(), &client); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + let mut receipt_list = RlpStream::new_list(4); + receipt_list.append(&H256::from( + "0000000000000000000000000000000000000000000000005555555555555555", + )); + receipt_list.append(&H256::from( + "ff00000000000000000000000000000000000000000000000000000000000000", + )); + receipt_list.append(&H256::from( + "fff0000000000000000000000000000000000000000000000000000000000000", + )); + receipt_list.append(&H256::from( + "aff0000000000000000000000000000000000000000000000000000000000000", + )); + + let receipts_request = receipt_list.out(); + // it returns rlp ONLY for hashes started with "f" + let result = SyncSupplier::return_receipts(&io, &Rlp::new(&receipts_request.clone()), 0); + + assert!(result.is_ok()); + let rlp_result = result.unwrap(); + assert!(rlp_result.is_some()); + + // the length of two rlp-encoded receipts + assert_eq!(603, rlp_result.unwrap().1.out().len()); + + io.sender = Some(2usize); + SyncSupplier::dispatch_packet( + &RwLock::new(sync), + &mut io, + 0usize, + GetReceiptsPacket.id(), + &receipts_request, + ); + assert_eq!(1, io.packets.len()); + } } diff --git a/ethcore/sync/src/chain/sync_packet.rs b/ethcore/sync/src/chain/sync_packet.rs index 3891090f65e..a868e1c35f7 100644 --- a/ethcore/sync/src/chain/sync_packet.rs +++ b/ethcore/sync/src/chain/sync_packet.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! When sending packets over p2p we specify both which subprotocol //! to use and what kind of packet we are sending (through a packet id). @@ -22,39 +22,39 @@ //! to convert to/from the packet id values transmitted over the //! wire. -use api::{ETH_PROTOCOL, WARP_SYNC_PROTOCOL_ID}; +#![allow(unused_doc_comments)] + +use api::{ETH_PROTOCOL, PAR_PROTOCOL}; use network::{PacketId, ProtocolId}; -/// An enum that defines all known packet ids in the context of -/// synchronization and provides a mechanism to convert from -/// packet ids (of type PacketId or u8) directly read from the network -/// to enum variants. This implicitly provides a mechanism to -/// check whether a given packet id is known, and to prevent -/// packet id clashes when defining new ids. +// An enum that defines all known packet ids in the context of +// synchronization and provides a mechanism to convert from +// packet ids (of type PacketId or u8) directly read from the network +// to enum variants. This implicitly provides a mechanism to +// check whether a given packet id is known, and to prevent +// packet id clashes when defining new ids. enum_from_primitive! { #[derive(Clone, Copy, Debug, PartialEq)] pub enum SyncPacket { - StatusPacket = 0x00, - NewBlockHashesPacket = 0x01, - TransactionsPacket = 0x02, - GetBlockHeadersPacket = 0x03, - BlockHeadersPacket = 0x04, - GetBlockBodiesPacket = 0x05, - BlockBodiesPacket = 0x06, - NewBlockPacket = 0x07, - - GetNodeDataPacket = 0x0d, - NodeDataPacket = 0x0e, - GetReceiptsPacket = 0x0f, - ReceiptsPacket = 0x10, - - GetSnapshotManifestPacket = 0x11, - SnapshotManifestPacket = 0x12, - GetSnapshotDataPacket = 0x13, - SnapshotDataPacket = 0x14, - ConsensusDataPacket = 0x15, - PrivateTransactionPacket = 0x16, - SignedPrivateTransactionPacket = 0x17, + StatusPacket = 0x00, + NewBlockHashesPacket = 0x01, + TransactionsPacket = 0x02, + GetBlockHeadersPacket = 0x03, + BlockHeadersPacket = 0x04, + GetBlockBodiesPacket = 0x05, + BlockBodiesPacket = 0x06, + NewBlockPacket = 0x07, + + //GetNodeDataPacket = 0x0d, + //NodeDataPacket = 0x0e, + GetReceiptsPacket = 0x0f, + ReceiptsPacket = 0x10, + + GetSnapshotManifestPacket = 0x11, + SnapshotManifestPacket = 0x12, + GetSnapshotDataPacket = 0x13, + SnapshotDataPacket = 0x14, + ConsensusDataPacket = 0x15, } } @@ -63,79 +63,71 @@ use self::SyncPacket::*; /// Provide both subprotocol and packet id information within the /// same object. pub trait PacketInfo { - fn id(&self) -> PacketId; - fn protocol(&self) -> ProtocolId; + fn id(&self) -> PacketId; + fn protocol(&self) -> ProtocolId; } // The mechanism to match packet ids and protocol may be improved // through some macro magic, but for now this works. impl PacketInfo for SyncPacket { - fn protocol(&self) -> ProtocolId { - match self { - StatusPacket | - NewBlockHashesPacket | - TransactionsPacket | - GetBlockHeadersPacket | - BlockHeadersPacket | - GetBlockBodiesPacket | - BlockBodiesPacket | - NewBlockPacket | - - GetNodeDataPacket| - NodeDataPacket | - GetReceiptsPacket | - ReceiptsPacket - - => ETH_PROTOCOL, - - GetSnapshotManifestPacket| - SnapshotManifestPacket | - GetSnapshotDataPacket | - SnapshotDataPacket | - ConsensusDataPacket | - PrivateTransactionPacket | - SignedPrivateTransactionPacket - - => WARP_SYNC_PROTOCOL_ID, - } - } - - fn id(&self) -> PacketId { - (*self) as PacketId - } + fn protocol(&self) -> ProtocolId { + match self { + StatusPacket + | NewBlockHashesPacket + | TransactionsPacket + | GetBlockHeadersPacket + | BlockHeadersPacket + | GetBlockBodiesPacket + | BlockBodiesPacket + | NewBlockPacket + //| GetNodeDataPacket + //| NodeDataPacket + | GetReceiptsPacket + | ReceiptsPacket => ETH_PROTOCOL, + + GetSnapshotManifestPacket + | SnapshotManifestPacket + | GetSnapshotDataPacket + | SnapshotDataPacket + | ConsensusDataPacket => PAR_PROTOCOL, + } + } + + fn id(&self) -> PacketId { + (*self) as PacketId + } } - #[cfg(test)] mod tests { - use super::*; - - use enum_primitive::FromPrimitive; - - #[test] - fn packet_ids_from_u8_when_from_primitive_zero_then_equals_status_packet() { - assert_eq!(SyncPacket::from_u8(0x00), Some(StatusPacket)); - } - - #[test] - fn packet_ids_from_u8_when_from_primitive_eleven_then_equals_get_snapshot_manifest_packet() { - assert_eq!(SyncPacket::from_u8(0x11), Some(GetSnapshotManifestPacket)); - } - - #[test] - fn packet_ids_from_u8_when_invalid_packet_id_then_none() { - assert!(SyncPacket::from_u8(0x99).is_none()); - } - - #[test] - fn when_status_packet_then_id_and_protocol_match() { - assert_eq!(StatusPacket.id(), StatusPacket as PacketId); - assert_eq!(StatusPacket.protocol(), ETH_PROTOCOL); - } - - #[test] - fn when_consensus_data_packet_then_id_and_protocol_match() { - assert_eq!(ConsensusDataPacket.id(), ConsensusDataPacket as PacketId); - assert_eq!(ConsensusDataPacket.protocol(), WARP_SYNC_PROTOCOL_ID); - } + use super::*; + + use enum_primitive::FromPrimitive; + + #[test] + fn packet_ids_from_u8_when_from_primitive_zero_then_equals_status_packet() { + assert_eq!(SyncPacket::from_u8(0x00), Some(StatusPacket)); + } + + #[test] + fn packet_ids_from_u8_when_from_primitive_eleven_then_equals_get_snapshot_manifest_packet() { + assert_eq!(SyncPacket::from_u8(0x11), Some(GetSnapshotManifestPacket)); + } + + #[test] + fn packet_ids_from_u8_when_invalid_packet_id_then_none() { + assert!(SyncPacket::from_u8(0x99).is_none()); + } + + #[test] + fn when_status_packet_then_id_and_protocol_match() { + assert_eq!(StatusPacket.id(), StatusPacket as PacketId); + assert_eq!(StatusPacket.protocol(), ETH_PROTOCOL); + } + + #[test] + fn when_consensus_data_packet_then_id_and_protocol_match() { + assert_eq!(ConsensusDataPacket.id(), ConsensusDataPacket as PacketId); + assert_eq!(ConsensusDataPacket.protocol(), PAR_PROTOCOL); + } } diff --git a/ethcore/sync/src/lib.rs b/ethcore/sync/src/lib.rs index 8a1e19569a4..ff2bf8bb3cf 100644 --- a/ethcore/sync/src/lib.rs +++ b/ethcore/sync/src/lib.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . #![warn(missing_docs)] @@ -22,10 +22,12 @@ //! extern crate common_types as types; +extern crate derive_more; extern crate ethcore; extern crate ethcore_io as io; extern crate ethcore_network as network; extern crate ethcore_network_devp2p as devp2p; +extern crate ethereum_forkid; extern crate ethereum_types; extern crate ethkey; extern crate ethstore; @@ -33,16 +35,19 @@ extern crate fastmap; extern crate keccak_hash as hash; extern crate parity_bytes as bytes; extern crate parking_lot; +extern crate primitive_types07; extern crate rand; extern crate rlp; +extern crate rlp04; +extern crate stats; extern crate triehash_ethereum; -extern crate ethcore_light as light; - -#[cfg(test)] extern crate env_logger; -#[cfg(test)] extern crate ethcore_private_tx; -#[cfg(test)] extern crate kvdb_memorydb; -#[cfg(test)] extern crate rustc_hex; +#[cfg(test)] +extern crate env_logger; +#[cfg(test)] +extern crate kvdb_memorydb; +#[cfg(test)] +extern crate rustc_hex; #[macro_use] extern crate enum_primitive; @@ -55,23 +60,19 @@ extern crate heapsize; #[macro_use] extern crate trace_time; -mod chain; -mod blocks; mod block_sync; -mod sync_io; -mod private_tx; +mod blocks; +mod chain; mod snapshot; +mod sync_io; mod transactions_stats; -pub mod light_sync; - #[cfg(test)] mod tests; mod api; pub use api::*; -pub use chain::{SyncStatus, SyncState}; +pub use chain::{SyncState, SyncStatus}; pub use devp2p::validate_node_url; -pub use network::{NonReservedPeerMode, Error, ErrorKind, ConnectionFilter, ConnectionDirection}; -pub use private_tx::{PrivateTxHandler, NoopPrivateTxHandler, SimplePrivateTxHandler}; +pub use network::{ConnectionDirection, ConnectionFilter, Error, ErrorKind, NonReservedPeerMode}; diff --git a/ethcore/sync/src/light_sync/mod.rs b/ethcore/sync/src/light_sync/mod.rs deleted file mode 100644 index dae05c3188a..00000000000 --- a/ethcore/sync/src/light_sync/mod.rs +++ /dev/null @@ -1,723 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Light client synchronization. -//! -//! This will synchronize the header chain using PIP messages. -//! Dataflow is largely one-directional as headers are pushed into -//! the light client queue for import. Where possible, they are batched -//! in groups. -//! -//! This is written assuming that the client and sync service are running -//! in the same binary; unlike a full node which might communicate via IPC. -//! -//! -//! Sync strategy: -//! - Find a common ancestor with peers. -//! - Split the chain up into subchains, which are downloaded in parallel from various peers in rounds. -//! - When within a certain distance of the head of the chain, aggressively download all -//! announced blocks. -//! - On bad block/response, punish peer and reset. - -use std::collections::{HashMap, HashSet}; -use std::mem; -use std::ops::Deref; -use std::sync::Arc; -use std::time::{Instant, Duration}; - -use types::encoded; -use light::client::{AsLightClient, LightChainClient}; -use light::net::{ - PeerStatus, Announcement, Handler, BasicContext, - EventContext, Capabilities, ReqId, Status, - Error as NetError, -}; -use light::request::{self, CompleteHeadersRequest as HeadersRequest}; -use network::PeerId; -use ethereum_types::{H256, U256}; -use parking_lot::{Mutex, RwLock}; -use rand::{Rng, OsRng}; - -use self::sync_round::{AbortReason, SyncRound, ResponseContext}; - -mod response; -mod sync_round; - -#[cfg(test)] -mod tests; - -// Base value for the header request timeout. -const REQ_TIMEOUT_BASE: Duration = Duration::from_secs(7); -// Additional value for each requested header. -// If we request N headers, then the timeout will be: -// REQ_TIMEOUT_BASE + N * REQ_TIMEOUT_PER_HEADER -const REQ_TIMEOUT_PER_HEADER: Duration = Duration::from_millis(10); - -/// Peer chain info. -#[derive(Debug, Clone, PartialEq, Eq)] -struct ChainInfo { - head_td: U256, - head_hash: H256, - head_num: u64, -} - -impl PartialOrd for ChainInfo { - fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> { - self.head_td.partial_cmp(&other.head_td) - } -} - -impl Ord for ChainInfo { - fn cmp(&self, other: &Self) -> ::std::cmp::Ordering { - self.head_td.cmp(&other.head_td) - } -} - -struct Peer { - status: ChainInfo, -} - -impl Peer { - // Create a new peer. - fn new(chain_info: ChainInfo) -> Self { - Peer { - status: chain_info, - } - } -} - -// search for a common ancestor with the best chain. -#[derive(Debug)] -enum AncestorSearch { - Queued(u64), // queued to search for blocks starting from here. - Awaiting(ReqId, u64, HeadersRequest), // awaiting response for this request. - Prehistoric, // prehistoric block found. TODO: start to roll back CHTs. - FoundCommon(u64, H256), // common block found. - Genesis, // common ancestor is the genesis. -} - -impl AncestorSearch { - fn begin(best_num: u64) -> Self { - match best_num { - 0 => AncestorSearch::Genesis, - _ => AncestorSearch::Queued(best_num), - } - } - - fn process_response(self, ctx: &ResponseContext, client: &L) -> AncestorSearch - where L: AsLightClient - { - let client = client.as_light_client(); - let first_num = client.chain_info().first_block_number.unwrap_or(0); - match self { - AncestorSearch::Awaiting(id, start, req) => { - if &id == ctx.req_id() { - match response::verify(ctx.data(), &req) { - Ok(headers) => { - for header in &headers { - if client.is_known(&header.hash()) { - debug!(target: "sync", "Found common ancestor with best chain"); - return AncestorSearch::FoundCommon(header.number(), header.hash()); - } - - if header.number() < first_num { - debug!(target: "sync", "Prehistoric common ancestor with best chain."); - return AncestorSearch::Prehistoric; - } - } - - let probe = start - headers.len() as u64; - if probe == 0 { - AncestorSearch::Genesis - } else { - AncestorSearch::Queued(probe) - } - } - Err(e) => { - trace!(target: "sync", "Bad headers response from {}: {}", ctx.responder(), e); - - ctx.punish_responder(); - AncestorSearch::Queued(start) - } - } - } else { - AncestorSearch::Awaiting(id, start, req) - } - } - other => other, - } - } - - fn requests_abandoned(self, req_ids: &[ReqId]) -> AncestorSearch { - match self { - AncestorSearch::Awaiting(id, start, req) => { - if req_ids.iter().find(|&x| x == &id).is_some() { - AncestorSearch::Queued(start) - } else { - AncestorSearch::Awaiting(id, start, req) - } - } - other => other, - } - } - - fn dispatch_request(self, mut dispatcher: F) -> AncestorSearch - where F: FnMut(HeadersRequest) -> Option - { - const BATCH_SIZE: u64 = 64; - - match self { - AncestorSearch::Queued(start) => { - let batch_size = ::std::cmp::min(start, BATCH_SIZE); - trace!(target: "sync", "Requesting {} reverse headers from {} to find common ancestor", - batch_size, start); - - let req = HeadersRequest { - start: start.into(), - max: batch_size, - skip: 0, - reverse: true, - }; - - match dispatcher(req.clone()) { - Some(req_id) => AncestorSearch::Awaiting(req_id, start, req), - None => AncestorSearch::Queued(start), - } - } - other => other, - } - } -} - -// synchronization state machine. -#[derive(Debug)] -enum SyncState { - // Idle (waiting for peers) or at chain head. - Idle, - // searching for common ancestor with best chain. - // queue should be cleared at this phase. - AncestorSearch(AncestorSearch), - // Doing sync rounds. - Rounds(SyncRound), -} - -/// A wrapper around the SyncState that makes sure to -/// update the giving reference to `is_idle` -#[derive(Debug)] -struct SyncStateWrapper { - state: SyncState, -} - -impl SyncStateWrapper { - /// Create a new wrapper for SyncState::Idle - pub fn idle() -> Self { - SyncStateWrapper { - state: SyncState::Idle, - } - } - - /// Set the new state's value, making sure `is_idle` gets updated - pub fn set(&mut self, state: SyncState, is_idle_handle: &mut bool) { - *is_idle_handle = match state { - SyncState::Idle => true, - _ => false, - }; - self.state = state; - } - - /// Returns the internal state's value - pub fn into_inner(self) -> SyncState { - self.state - } -} - -impl Deref for SyncStateWrapper { - type Target = SyncState; - - fn deref(&self) -> &SyncState { - &self.state - } -} - -struct ResponseCtx<'a> { - peer: PeerId, - req_id: ReqId, - ctx: &'a BasicContext, - data: &'a [encoded::Header], -} - -impl<'a> ResponseContext for ResponseCtx<'a> { - fn responder(&self) -> PeerId { self.peer } - fn req_id(&self) -> &ReqId { &self.req_id } - fn data(&self) -> &[encoded::Header] { self.data } - fn punish_responder(&self) { self.ctx.disable_peer(self.peer) } -} - -/// Light client synchronization manager. See module docs for more details. -pub struct LightSync { - start_block_number: u64, - best_seen: Mutex>, // best seen block on the network. - peers: RwLock>>, // peers which are relevant to synchronization. - pending_reqs: Mutex>, // requests from this handler - client: Arc, - rng: Mutex, - state: Mutex, - // We duplicate this state tracking to avoid deadlocks in `is_major_importing`. - is_idle: Mutex, -} - -#[derive(Debug, Clone)] -struct PendingReq { - started: Instant, - timeout: Duration, -} - -impl Handler for LightSync { - fn on_connect( - &self, - ctx: &EventContext, - status: &Status, - capabilities: &Capabilities - ) -> PeerStatus { - use std::cmp; - - if capabilities.serve_headers { - let chain_info = ChainInfo { - head_td: status.head_td, - head_hash: status.head_hash, - head_num: status.head_num, - }; - - { - let mut best = self.best_seen.lock(); - *best = cmp::max(best.clone(), Some(chain_info.clone())); - } - - self.peers.write().insert(ctx.peer(), Mutex::new(Peer::new(chain_info))); - self.maintain_sync(ctx.as_basic()); - - PeerStatus::Kept - } else { - PeerStatus::Unkept - } - } - - fn on_disconnect(&self, ctx: &EventContext, unfulfilled: &[ReqId]) { - let peer_id = ctx.peer(); - - let peer = match self.peers.write().remove(&peer_id).map(|p| p.into_inner()) { - Some(peer) => peer, - None => return, - }; - - trace!(target: "sync", "peer {} disconnecting", peer_id); - - let new_best = { - let mut best = self.best_seen.lock(); - - if best.as_ref().map_or(false, |b| b == &peer.status) { - // search for next-best block. - let next_best: Option = self.peers.read().values() - .map(|p| p.lock().status.clone()) - .map(Some) - .fold(None, ::std::cmp::max); - - *best = next_best; - } - - best.clone() - }; - - { - let mut pending_reqs = self.pending_reqs.lock(); - for unfulfilled in unfulfilled { - pending_reqs.remove(&unfulfilled); - } - } - - if new_best.is_none() { - debug!(target: "sync", "No peers remain. Reverting to idle"); - self.set_state(&mut self.state.lock(), SyncState::Idle); - } else { - let mut state = self.state.lock(); - - let next_state = match mem::replace(&mut *state, SyncStateWrapper::idle()).into_inner() { - SyncState::Idle => SyncState::Idle, - SyncState::AncestorSearch(search) => - SyncState::AncestorSearch(search.requests_abandoned(unfulfilled)), - SyncState::Rounds(round) => SyncState::Rounds(round.requests_abandoned(unfulfilled)), - }; - self.set_state(&mut state, next_state); - } - - self.maintain_sync(ctx.as_basic()); - } - - fn on_announcement(&self, ctx: &EventContext, announcement: &Announcement) { - let (last_td, chain_info) = { - let peers = self.peers.read(); - match peers.get(&ctx.peer()) { - None => return, - Some(peer) => { - let mut peer = peer.lock(); - let last_td = peer.status.head_td; - peer.status = ChainInfo { - head_td: announcement.head_td, - head_hash: announcement.head_hash, - head_num: announcement.head_num, - }; - (last_td, peer.status.clone()) - } - } - }; - - trace!(target: "sync", "Announcement from peer {}: new chain head {:?}, reorg depth {}", - ctx.peer(), (announcement.head_hash, announcement.head_num), announcement.reorg_depth); - - if last_td > announcement.head_td { - trace!(target: "sync", "Peer {} moved backwards.", ctx.peer()); - self.peers.write().remove(&ctx.peer()); - ctx.disconnect_peer(ctx.peer()); - return - } - - { - let mut best = self.best_seen.lock(); - *best = ::std::cmp::max(best.clone(), Some(chain_info)); - } - - self.maintain_sync(ctx.as_basic()); - } - - fn on_responses(&self, ctx: &EventContext, req_id: ReqId, responses: &[request::Response]) { - let peer = ctx.peer(); - if !self.peers.read().contains_key(&peer) { - return - } - - if self.pending_reqs.lock().remove(&req_id).is_none() { - return - } - - let headers = match responses.get(0) { - Some(&request::Response::Headers(ref response)) => &response.headers[..], - Some(_) => { - trace!("Disabling peer {} for wrong response type.", peer); - ctx.disable_peer(peer); - &[] - } - None => &[], - }; - - { - let mut state = self.state.lock(); - - let ctx = ResponseCtx { - peer: ctx.peer(), - req_id: req_id, - ctx: ctx.as_basic(), - data: headers, - }; - - let next_state = match mem::replace(&mut *state, SyncStateWrapper::idle()).into_inner() { - SyncState::Idle => SyncState::Idle, - SyncState::AncestorSearch(search) => - SyncState::AncestorSearch(search.process_response(&ctx, &*self.client)), - SyncState::Rounds(round) => SyncState::Rounds(round.process_response(&ctx)), - }; - self.set_state(&mut state, next_state); - } - - self.maintain_sync(ctx.as_basic()); - } - - fn tick(&self, ctx: &BasicContext) { - self.maintain_sync(ctx); - } -} - -// private helpers -impl LightSync { - /// Sets the LightSync's state, and update - /// `is_idle` - fn set_state(&self, state: &mut SyncStateWrapper, next_state: SyncState) { - state.set(next_state, &mut self.is_idle.lock()); - } - - // Begins a search for the common ancestor and our best block. - // does not lock state, instead has a mutable reference to it passed. - fn begin_search(&self, state: &mut SyncStateWrapper) { - if let None = *self.best_seen.lock() { - // no peers. - self.set_state(state, SyncState::Idle); - return; - } - - self.client.as_light_client().flush_queue(); - let chain_info = self.client.as_light_client().chain_info(); - - trace!(target: "sync", "Beginning search for common ancestor from {:?}", - (chain_info.best_block_number, chain_info.best_block_hash)); - let next_state = SyncState::AncestorSearch(AncestorSearch::begin(chain_info.best_block_number)); - self.set_state(state, next_state); - } - - // handles request dispatch, block import, state machine transitions, and timeouts. - fn maintain_sync(&self, ctx: &BasicContext) { - use ethcore::error::{Error as EthcoreError, ErrorKind as EthcoreErrorKind, ImportErrorKind}; - - const DRAIN_AMOUNT: usize = 128; - - let client = self.client.as_light_client(); - let chain_info = client.chain_info(); - - let mut state = self.state.lock(); - debug!(target: "sync", "Maintaining sync ({:?})", **state); - - // drain any pending blocks into the queue. - { - let mut sink = Vec::with_capacity(DRAIN_AMOUNT); - - 'a: - loop { - if client.queue_info().is_full() { break } - - let next_state = match mem::replace(&mut *state, SyncStateWrapper::idle()).into_inner() { - SyncState::Rounds(round) - => SyncState::Rounds(round.drain(&mut sink, Some(DRAIN_AMOUNT))), - other => other, - }; - self.set_state(&mut state, next_state); - - if sink.is_empty() { break } - trace!(target: "sync", "Drained {} headers to import", sink.len()); - - for header in sink.drain(..) { - match client.queue_header(header) { - Ok(_) => {} - Err(EthcoreError(EthcoreErrorKind::Import(ImportErrorKind::AlreadyInChain), _)) => { - trace!(target: "sync", "Block already in chain. Continuing."); - }, - Err(EthcoreError(EthcoreErrorKind::Import(ImportErrorKind::AlreadyQueued), _)) => { - trace!(target: "sync", "Block already queued. Continuing."); - }, - Err(e) => { - debug!(target: "sync", "Found bad header ({:?}). Reset to search state.", e); - - self.begin_search(&mut state); - break 'a; - } - } - } - } - } - - // handle state transitions. - { - let best_td = chain_info.pending_total_difficulty; - let sync_target = match *self.best_seen.lock() { - Some(ref target) if target.head_td > best_td => (target.head_num, target.head_hash), - ref other => { - let network_score = other.as_ref().map(|target| target.head_td); - trace!(target: "sync", "No target to sync to. Network score: {:?}, Local score: {:?}", - network_score, best_td); - self.set_state(&mut state, SyncState::Idle); - return; - } - }; - - match mem::replace(&mut *state, SyncStateWrapper::idle()).into_inner() { - SyncState::Rounds(SyncRound::Abort(reason, remaining)) => { - if remaining.len() > 0 { - self.set_state(&mut state, SyncState::Rounds(SyncRound::Abort(reason, remaining))); - return; - } - - match reason { - AbortReason::BadScaffold(bad_peers) => { - debug!(target: "sync", "Disabling peers responsible for bad scaffold"); - for peer in bad_peers { - ctx.disable_peer(peer); - } - } - AbortReason::NoResponses => {} - AbortReason::TargetReached => { - debug!(target: "sync", "Sync target reached. Going idle"); - self.set_state(&mut state, SyncState::Idle); - return; - } - } - - debug!(target: "sync", "Beginning search after aborted sync round"); - self.begin_search(&mut state); - } - SyncState::AncestorSearch(AncestorSearch::FoundCommon(num, hash)) => { - self.set_state(&mut state, SyncState::Rounds(SyncRound::begin((num, hash), sync_target))); - } - SyncState::AncestorSearch(AncestorSearch::Genesis) => { - // Same here. - let g_hash = chain_info.genesis_hash; - self.set_state(&mut state, SyncState::Rounds(SyncRound::begin((0, g_hash), sync_target))); - } - SyncState::Idle => self.begin_search(&mut state), - other => self.set_state(&mut state, other), // restore displaced state. - } - } - - // handle requests timeouts - { - let mut pending_reqs = self.pending_reqs.lock(); - let mut unfulfilled = Vec::new(); - for (req_id, info) in pending_reqs.iter() { - if info.started.elapsed() >= info.timeout { - debug!(target: "sync", "{} timed out", req_id); - unfulfilled.push(req_id.clone()); - } - } - - if !unfulfilled.is_empty() { - for unfulfilled in unfulfilled.iter() { - pending_reqs.remove(unfulfilled); - } - drop(pending_reqs); - - let next_state = match mem::replace(&mut *state, SyncStateWrapper::idle()).into_inner() { - SyncState::Idle => SyncState::Idle, - SyncState::AncestorSearch(search) => - SyncState::AncestorSearch(search.requests_abandoned(&unfulfilled)), - SyncState::Rounds(round) => SyncState::Rounds(round.requests_abandoned(&unfulfilled)), - }; - self.set_state(&mut state, next_state); - } - } - - // allow dispatching of requests. - { - let peers = self.peers.read(); - let mut peer_ids: Vec<_> = peers.iter().filter_map(|(id, p)| { - if p.lock().status.head_td > chain_info.pending_total_difficulty { - Some(*id) - } else { - None - } - }).collect(); - - let mut rng = self.rng.lock(); - let mut requested_from = HashSet::new(); - - // naive request dispatcher: just give to any peer which says it will - // give us responses. but only one request per peer per state transition. - let dispatcher = move |req: HeadersRequest| { - rng.shuffle(&mut peer_ids); - - let request = { - let mut builder = request::Builder::default(); - builder.push(request::Request::Headers(request::IncompleteHeadersRequest { - start: req.start.into(), - skip: req.skip, - max: req.max, - reverse: req.reverse, - })).expect("request provided fully complete with no unresolved back-references; qed"); - builder.build() - }; - for peer in &peer_ids { - if requested_from.contains(peer) { continue } - match ctx.request_from(*peer, request.clone()) { - Ok(id) => { - assert!(req.max <= u32::max_value() as u64, - "requesting more than 2^32 headers at a time would overflow"); - let timeout = REQ_TIMEOUT_BASE + REQ_TIMEOUT_PER_HEADER * req.max as u32; - self.pending_reqs.lock().insert(id.clone(), PendingReq { - started: Instant::now(), - timeout, - }); - requested_from.insert(peer.clone()); - - return Some(id) - } - Err(NetError::NoCredits) => {} - Err(e) => - trace!(target: "sync", "Error requesting headers from viable peer: {}", e), - } - } - - None - }; - - let next_state = match mem::replace(&mut *state, SyncStateWrapper::idle()).into_inner() { - SyncState::Rounds(round) => - SyncState::Rounds(round.dispatch_requests(dispatcher)), - SyncState::AncestorSearch(search) => - SyncState::AncestorSearch(search.dispatch_request(dispatcher)), - other => other, - }; - self.set_state(&mut state, next_state); - } - } -} - -// public API -impl LightSync { - /// Create a new instance of `LightSync`. - /// - /// This won't do anything until registered as a handler - /// so it can act on events. - pub fn new(client: Arc) -> Result { - Ok(LightSync { - start_block_number: client.as_light_client().chain_info().best_block_number, - best_seen: Mutex::new(None), - peers: RwLock::new(HashMap::new()), - pending_reqs: Mutex::new(HashMap::new()), - client: client, - rng: Mutex::new(OsRng::new()?), - state: Mutex::new(SyncStateWrapper::idle()), - is_idle: Mutex::new(true), - }) - } -} - -/// Trait for erasing the type of a light sync object and exposing read-only methods. -pub trait SyncInfo { - /// Get the highest block advertised on the network. - fn highest_block(&self) -> Option; - - /// Get the block number at the time of sync start. - fn start_block(&self) -> u64; - - /// Whether major sync is underway. - fn is_major_importing(&self) -> bool; -} - -impl SyncInfo for LightSync { - fn highest_block(&self) -> Option { - self.best_seen.lock().as_ref().map(|x| x.head_num) - } - - fn start_block(&self) -> u64 { - self.start_block_number - } - - fn is_major_importing(&self) -> bool { - const EMPTY_QUEUE: usize = 3; - - let queue_info = self.client.as_light_client().queue_info(); - let is_verifying = queue_info.unverified_queue_size + queue_info.verified_queue_size > EMPTY_QUEUE; - let is_syncing = !*self.is_idle.lock(); - - is_verifying || is_syncing - } - -} diff --git a/ethcore/sync/src/light_sync/response.rs b/ethcore/sync/src/light_sync/response.rs deleted file mode 100644 index 96d2a8822e9..00000000000 --- a/ethcore/sync/src/light_sync/response.rs +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Helpers for decoding and verifying responses for headers. - -use types::{encoded, header::Header}; -use ethereum_types::H256; -use light::request::{HashOrNumber, CompleteHeadersRequest as HeadersRequest}; -use rlp::DecoderError; -use std::fmt; - -/// Errors found when decoding headers and verifying with basic constraints. -#[derive(Debug, PartialEq)] -pub enum BasicError { - /// Wrong skip value: expected, found (if any). - WrongSkip(u64, Option), - /// Wrong start number. - WrongStartNumber(u64, u64), - /// Wrong start hash. - WrongStartHash(H256, H256), - /// Too many headers. - TooManyHeaders(usize, usize), - /// Decoder error. - Decoder(DecoderError), -} - -impl From for BasicError { - fn from(err: DecoderError) -> Self { - BasicError::Decoder(err) - } -} - -impl fmt::Display for BasicError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Header response verification error: ")?; - - match *self { - BasicError::WrongSkip(ref exp, ref got) - => write!(f, "wrong skip (expected {}, got {:?})", exp, got), - BasicError::WrongStartNumber(ref exp, ref got) - => write!(f, "wrong start number (expected {}, got {})", exp, got), - BasicError::WrongStartHash(ref exp, ref got) - => write!(f, "wrong start hash (expected {}, got {})", exp, got), - BasicError::TooManyHeaders(ref max, ref got) - => write!(f, "too many headers (max {}, got {})", max, got), - BasicError::Decoder(ref err) - => write!(f, "{}", err), - } - } -} - -/// Request verification constraint. -pub trait Constraint { - type Error; - - /// Verify headers against this. - fn verify(&self, headers: &[Header], reverse: bool) -> Result<(), Self::Error>; -} - -/// Do basic verification of provided headers against a request. -pub fn verify(headers: &[encoded::Header], request: &HeadersRequest) -> Result, BasicError> { - let headers: Result, _> = headers.iter().map(|h| h.decode() ).collect(); - match headers { - Ok(headers) => { - let reverse = request.reverse; - - Max(request.max as usize).verify(&headers, reverse)?; - match request.start { - HashOrNumber::Number(ref num) => StartsAtNumber(*num).verify(&headers, reverse)?, - HashOrNumber::Hash(ref hash) => StartsAtHash(*hash).verify(&headers, reverse)?, - } - - SkipsBetween(request.skip).verify(&headers, reverse)?; - - Ok(headers) - }, - Err(e) => Err(e.into()) - } -} - -struct StartsAtNumber(u64); -struct StartsAtHash(H256); -struct SkipsBetween(u64); -struct Max(usize); - -impl Constraint for StartsAtNumber { - type Error = BasicError; - - fn verify(&self, headers: &[Header], _reverse: bool) -> Result<(), BasicError> { - headers.first().map_or(Ok(()), |h| { - if h.number() == self.0 { - Ok(()) - } else { - Err(BasicError::WrongStartNumber(self.0, h.number())) - } - }) - } -} - -impl Constraint for StartsAtHash { - type Error = BasicError; - - fn verify(&self, headers: &[Header], _reverse: bool) -> Result<(), BasicError> { - headers.first().map_or(Ok(()), |h| { - if h.hash() == self.0 { - Ok(()) - } else { - Err(BasicError::WrongStartHash(self.0, h.hash())) - } - }) - } -} - -impl Constraint for SkipsBetween { - type Error = BasicError; - - fn verify(&self, headers: &[Header], reverse: bool) -> Result<(), BasicError> { - for pair in headers.windows(2) { - let (low, high) = if reverse { (&pair[1], &pair[0]) } else { (&pair[0], &pair[1]) }; - if low.number() >= high.number() { return Err(BasicError::WrongSkip(self.0, None)) } - - let skip = (high.number() - low.number()) - 1; - if skip != self.0 { return Err(BasicError::WrongSkip(self.0, Some(skip))) } - } - - Ok(()) - } -} - -impl Constraint for Max { - type Error = BasicError; - - fn verify(&self, headers: &[Header], _reverse: bool) -> Result<(), BasicError> { - match headers.len() > self.0 { - true => Err(BasicError::TooManyHeaders(self.0, headers.len())), - false => Ok(()) - } - } -} - -#[cfg(test)] -mod tests { - use types::encoded; - use types::header::Header; - use light::request::CompleteHeadersRequest as HeadersRequest; - - use super::*; - - #[test] - fn sequential_forward() { - let request = HeadersRequest { - start: 10.into(), - max: 30, - skip: 0, - reverse: false, - }; - - let mut parent_hash = None; - let headers: Vec<_> = (0..25).map(|x| x + 10).map(|x| { - let mut header = Header::default(); - header.set_number(x); - - if let Some(parent_hash) = parent_hash { - header.set_parent_hash(parent_hash); - } - - parent_hash = Some(header.hash()); - - encoded::Header::new(::rlp::encode(&header)) - }).collect(); - - assert!(verify(&headers, &request).is_ok()); - } - - #[test] - fn sequential_backward() { - let request = HeadersRequest { - start: 34.into(), - max: 30, - skip: 0, - reverse: true, - }; - - let mut parent_hash = None; - let headers: Vec<_> = (0..25).map(|x| x + 10).rev().map(|x| { - let mut header = Header::default(); - header.set_number(x); - - if let Some(parent_hash) = parent_hash { - header.set_parent_hash(parent_hash); - } - - parent_hash = Some(header.hash()); - - encoded::Header::new(::rlp::encode(&header)) - }).collect(); - - assert!(verify(&headers, &request).is_ok()); - } - - #[test] - fn too_many() { - let request = HeadersRequest { - start: 10.into(), - max: 20, - skip: 0, - reverse: false, - }; - - let mut parent_hash = None; - let headers: Vec<_> = (0..25).map(|x| x + 10).map(|x| { - let mut header = Header::default(); - header.set_number(x); - - if let Some(parent_hash) = parent_hash { - header.set_parent_hash(parent_hash); - } - - parent_hash = Some(header.hash()); - - encoded::Header::new(::rlp::encode(&header)) - }).collect(); - - assert_eq!(verify(&headers, &request), Err(BasicError::TooManyHeaders(20, 25))); - } - - #[test] - fn wrong_skip() { - let request = HeadersRequest { - start: 10.into(), - max: 30, - skip: 5, - reverse: false, - }; - - let headers: Vec<_> = (0..25).map(|x| x * 3).map(|x| x + 10).map(|x| { - let mut header = Header::default(); - header.set_number(x); - - encoded::Header::new(::rlp::encode(&header)) - }).collect(); - - assert_eq!(verify(&headers, &request), Err(BasicError::WrongSkip(5, Some(2)))); - } -} diff --git a/ethcore/sync/src/light_sync/sync_round.rs b/ethcore/sync/src/light_sync/sync_round.rs deleted file mode 100644 index 7c2a2bc0169..00000000000 --- a/ethcore/sync/src/light_sync/sync_round.rs +++ /dev/null @@ -1,557 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Header download state machine. - -use std::cmp::Ordering; -use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque}; -use std::fmt; - -use types::encoded; -use types::header::Header; - -use light::net::ReqId; -use light::request::CompleteHeadersRequest as HeadersRequest; - -use network::PeerId; -use ethereum_types::H256; - -use super::response; - -// number of attempts to make to get a full scaffold for a sync round. -const SCAFFOLD_ATTEMPTS: usize = 3; - -/// Context for a headers response. -pub trait ResponseContext { - /// Get the peer who sent this response. - fn responder(&self) -> PeerId; - /// Get the request ID this response corresponds to. - fn req_id(&self) -> &ReqId; - /// Get the (unverified) response data. - fn data(&self) -> &[encoded::Header]; - /// Punish the responder. - fn punish_responder(&self); -} - -/// Reasons for sync round abort. -#[derive(Debug, Clone)] -pub enum AbortReason { - /// Bad sparse header chain along with a list of peers who contributed to it. - BadScaffold(Vec), - /// No incoming data. - NoResponses, - /// Sync rounds completed. - TargetReached, -} - -// A request for headers with a known starting header hash. -// and a known parent hash for the last block. -#[derive(PartialEq, Eq)] -struct SubchainRequest { - subchain_parent: (u64, H256), - headers_request: HeadersRequest, - subchain_end: (u64, H256), - downloaded: VecDeque
, -} - -// ordered by subchain parent number so pending requests towards the -// front of the round are dispatched first. -impl PartialOrd for SubchainRequest { - fn partial_cmp(&self, other: &Self) -> Option { - self.subchain_parent.0 - .partial_cmp(&other.subchain_parent.0) - .map(Ordering::reverse) - } -} - -impl Ord for SubchainRequest { - fn cmp(&self, other: &Self) -> Ordering { - self.subchain_parent.0.cmp(&other.subchain_parent.0).reverse() - } -} - -/// Manages downloading of interior blocks of a sparse header chain. -pub struct Fetcher { - sparse: VecDeque
, // sparse header chain. - requests: BinaryHeap, - complete_requests: HashMap, - pending: HashMap, - scaffold_contributors: Vec, - ready: VecDeque
, - end: (u64, H256), - target: (u64, H256), -} - -impl Fetcher { - // Produce a new fetcher given a sparse headerchain, in ascending order along - // with a list of peers who helped produce the chain. - // The headers must be valid RLP at this point and must have a consistent - // non-zero gap between them. Will abort the round if found wrong. - fn new(sparse_headers: Vec
, contributors: Vec, target: (u64, H256)) -> SyncRound { - let mut requests = BinaryHeap::with_capacity(sparse_headers.len() - 1); - - for pair in sparse_headers.windows(2) { - let low_rung = &pair[0]; - let high_rung = &pair[1]; - - let diff = high_rung.number() - low_rung.number(); - - // should never happen as long as we verify the gaps - // gotten from SyncRound::Start - if diff < 2 { continue } - - let needed_headers = HeadersRequest { - start: high_rung.parent_hash().clone().into(), - max: diff - 1, - skip: 0, - reverse: true, - }; - - requests.push(SubchainRequest { - headers_request: needed_headers, - subchain_end: (high_rung.number() - 1, *high_rung.parent_hash()), - downloaded: VecDeque::new(), - subchain_parent: (low_rung.number(), low_rung.hash()), - }); - } - - let end = match sparse_headers.last().map(|h| (h.number(), h.hash())) { - Some(end) => end, - None => return SyncRound::abort(AbortReason::BadScaffold(contributors), VecDeque::new()), - }; - - SyncRound::Fetch(Fetcher { - sparse: sparse_headers.into(), - requests: requests, - complete_requests: HashMap::new(), - pending: HashMap::new(), - scaffold_contributors: contributors, - ready: VecDeque::new(), - end: end, - target: target, - }) - } - - // collect complete requests and their subchain from the sparse header chain - // into the ready set in order. - fn collect_ready(&mut self) { - loop { - let start_hash = match self.sparse.front() { - Some(first) => first.hash(), - None => break, - }; - - match self.complete_requests.remove(&start_hash) { - None => break, - Some(complete_req) => { - self.ready.push_back(self.sparse.pop_front().expect("first known to exist; qed")); - self.ready.extend(complete_req.downloaded); - } - } - } - - // frames are between two sparse headers and keyed by subchain parent, so the last - // remaining will be the last header. - if self.sparse.len() == 1 { - self.ready.push_back(self.sparse.pop_back().expect("sparse known to have one entry; qed")) - } - - trace!(target: "sync", "{} headers ready to drain", self.ready.len()); - } - - fn process_response(mut self, ctx: &R) -> SyncRound { - let mut request = match self.pending.remove(ctx.req_id()) { - Some(request) => request, - None => return SyncRound::Fetch(self), - }; - - trace!(target: "sync", "Received response for subchain ({} -> {})", - request.subchain_parent.0, request.subchain_end.0); - - let headers = ctx.data(); - - if headers.is_empty() { - trace!(target: "sync", "Punishing peer {} for empty response", ctx.responder()); - ctx.punish_responder(); - - self.requests.push(request); - return SyncRound::Fetch(self); - } - - match response::verify(headers, &request.headers_request) { - Err(e) => { - trace!(target: "sync", "Punishing peer {} for invalid response ({})", ctx.responder(), e); - ctx.punish_responder(); - - // TODO: track number of attempts per request, - // abort if failure rate too high. - self.requests.push(request); - SyncRound::Fetch(self) - } - Ok(headers) => { - let mut parent_hash = None; - for header in headers { - if let Some(hash) = parent_hash.as_ref() { - if *hash != header.hash() { - trace!(target: "sync", "Punishing peer {} for parent mismatch", ctx.responder()); - ctx.punish_responder(); - self.requests.push(request); - return SyncRound::Fetch(self); - } - } - // incrementally update the frame request as we go so we can - // return at any time in the loop. - parent_hash = Some(*header.parent_hash()); - request.headers_request.start = header.parent_hash().clone().into(); - request.headers_request.max -= 1; - request.downloaded.push_front(header); - } - - let subchain_parent = request.subchain_parent.1; - - // check if the subchain portion has been completely filled. - if request.headers_request.max == 0 { - if parent_hash.map_or(true, |hash| hash != subchain_parent) { - let abort = AbortReason::BadScaffold(self.scaffold_contributors); - return SyncRound::abort(abort, self.ready); - } - - self.complete_requests.insert(subchain_parent, request); - self.collect_ready(); - } - - // state transition not triggered until drain is finished. - (SyncRound::Fetch(self)) - } - } - } - - fn requests_abandoned(mut self, abandoned: &[ReqId]) -> SyncRound { - trace!(target: "sync", "Abandonned requests {:?}", abandoned); - - for abandoned in abandoned { - match self.pending.remove(abandoned) { - None => {}, - Some(req) => self.requests.push(req), - } - } - - // TODO: track failure rate and potentially abort. - SyncRound::Fetch(self) - } - - fn dispatch_requests(mut self, mut dispatcher: D) -> SyncRound - where D: FnMut(HeadersRequest) -> Option - { - while let Some(pending_req) = self.requests.pop() { - match dispatcher(pending_req.headers_request.clone()) { - Some(req_id) => { - trace!(target: "sync", "Assigned request {} for subchain ({} -> {})", - req_id, pending_req.subchain_parent.0, pending_req.subchain_end.0); - - self.pending.insert(req_id, pending_req); - } - None => { - trace!(target: "sync", "Failed to assign request for subchain ({} -> {})", - pending_req.subchain_parent.0, pending_req.subchain_end.0); - self.requests.push(pending_req); - break; - } - } - } - - SyncRound::Fetch(self) - } - - fn drain(mut self, headers: &mut Vec
, max: Option) -> SyncRound { - let max = ::std::cmp::min(max.unwrap_or(usize::max_value()), self.ready.len()); - headers.extend(self.ready.drain(0..max)); - - if self.sparse.is_empty() && self.ready.is_empty() { - trace!(target: "sync", "sync round complete. Starting anew from {:?}", self.end); - SyncRound::begin(self.end, self.target) - } else { - SyncRound::Fetch(self) - } - } -} - -// Compute scaffold parameters from non-zero distance between start and target block: (skip, pivots). -fn scaffold_params(diff: u64) -> (u64, u64) { - // default parameters. - // amount of blocks between each scaffold pivot. - const ROUND_SKIP: u64 = 255; - // amount of scaffold pivots: these are the Xs in "X___X___X" - const ROUND_PIVOTS: u64 = 256; - - let rem = diff % (ROUND_SKIP + 1); - if diff <= ROUND_SKIP { - // just request headers from the start to the target. - (0, rem) - } else { - // the number of pivots necessary to exactly hit or overshoot the target. - let pivots_to_target = (diff / (ROUND_SKIP + 1)) + if rem == 0 { 0 } else { 1 }; - let num_pivots = ::std::cmp::min(pivots_to_target, ROUND_PIVOTS); - (ROUND_SKIP, num_pivots) - } -} - -/// Round started: get stepped header chain. -/// from a start block with number X we request ROUND_PIVOTS headers stepped by ROUND_SKIP from -/// block X + 1 to a target >= X + 1. -/// If the sync target is within ROUND_SKIP of the start, we request -/// only those blocks. If the sync target is within (ROUND_SKIP + 1) * (ROUND_PIVOTS - 1) of -/// the start, we reduce the number of pivots so the target is outside it. -pub struct RoundStart { - start_block: (u64, H256), - target: (u64, H256), - pending_req: Option<(ReqId, HeadersRequest)>, - sparse_headers: Vec
, - contributors: HashSet, - attempt: usize, - skip: u64, - pivots: u64, -} - -impl RoundStart { - fn new(start: (u64, H256), target: (u64, H256)) -> Self { - let (skip, pivots) = scaffold_params(target.0 - start.0); - - trace!(target: "sync", "Beginning sync round: {} pivots and {} skip from block {}", - pivots, skip, start.0); - - RoundStart { - start_block: start, - target: target, - pending_req: None, - sparse_headers: Vec::new(), - contributors: HashSet::new(), - attempt: 0, - skip: skip, - pivots: pivots, - } - } - - // called on failed attempt. may trigger a transition after a number of attempts. - // a failed attempt is defined as any time a peer returns invalid or incomplete response - fn failed_attempt(mut self) -> SyncRound { - self.attempt += 1; - - if self.attempt >= SCAFFOLD_ATTEMPTS { - return if self.sparse_headers.len() > 1 { - Fetcher::new(self.sparse_headers, self.contributors.into_iter().collect(), self.target) - } else { - let fetched_headers = if self.skip == 0 { - self.sparse_headers.into() - } else { - VecDeque::new() - }; - - SyncRound::abort(AbortReason::NoResponses, fetched_headers) - } - } else { - SyncRound::Start(self) - } - } - - fn process_response(mut self, ctx: &R) -> SyncRound { - let req = match self.pending_req.take() { - Some((id, ref req)) if ctx.req_id() == &id => { req.clone() } - other => { - self.pending_req = other; - return SyncRound::Start(self); - } - }; - - match response::verify(ctx.data(), &req) { - Ok(headers) => { - if self.sparse_headers.is_empty() - && headers.get(0).map_or(false, |x| x.parent_hash() != &self.start_block.1) { - trace!(target: "sync", "Wrong parent for first header in round"); - ctx.punish_responder(); // or should we reset? - } - - self.contributors.insert(ctx.responder()); - self.sparse_headers.extend(headers); - - if self.sparse_headers.len() as u64 == self.pivots { - return if self.skip == 0 { - SyncRound::abort(AbortReason::TargetReached, self.sparse_headers.into()) - } else { - trace!(target: "sync", "Beginning fetch of blocks between {} sparse headers", - self.sparse_headers.len()); - Fetcher::new( - self.sparse_headers, - self.contributors.into_iter().collect(), - self.target - ) - } - } - } - Err(e) => { - trace!(target: "sync", "Punishing peer {} for malformed response ({})", ctx.responder(), e); - ctx.punish_responder(); - } - }; - - self.failed_attempt() - } - - fn requests_abandoned(mut self, abandoned: &[ReqId]) -> SyncRound { - match self.pending_req.take() { - Some((id, req)) => { - if abandoned.iter().any(|r| r == &id) { - self.pending_req = None; - self.failed_attempt() - } else { - self.pending_req = Some((id, req)); - SyncRound::Start(self) - } - } - None => SyncRound::Start(self), - } - } - - fn dispatch_requests(mut self, mut dispatcher: D) -> SyncRound - where D: FnMut(HeadersRequest) -> Option - { - if self.pending_req.is_none() { - // beginning offset + first block expected after last header we have. - let start = (self.start_block.0 + 1) - + self.sparse_headers.len() as u64 * (self.skip + 1); - - let max = self.pivots - self.sparse_headers.len() as u64; - - let headers_request = HeadersRequest { - start: start.into(), - max: max, - skip: self.skip, - reverse: false, - }; - - if let Some(req_id) = dispatcher(headers_request.clone()) { - trace!(target: "sync", "Requesting scaffold: {} headers forward from {}, skip={}", - max, start, self.skip); - - self.pending_req = Some((req_id, headers_request)); - } - } - - SyncRound::Start(self) - } -} - -/// Sync round state machine. -pub enum SyncRound { - /// Beginning a sync round. - Start(RoundStart), - /// Fetching intermediate blocks during a sync round. - Fetch(Fetcher), - /// Aborted + Sequential headers - Abort(AbortReason, VecDeque
), -} - -impl SyncRound { - fn abort(reason: AbortReason, remaining: VecDeque
) -> Self { - trace!(target: "sync", "Aborting sync round: {:?}. To drain: {}", reason, remaining.len()); - - SyncRound::Abort(reason, remaining) - } - - /// Begin sync rounds from a starting block, but not to go past a given target - pub fn begin(start: (u64, H256), target: (u64, H256)) -> Self { - if target.0 <= start.0 { - SyncRound::abort(AbortReason::TargetReached, VecDeque::new()) - } else { - SyncRound::Start(RoundStart::new(start, target)) - } - } - - /// Process an answer to a request. Unknown requests will be ignored. - pub fn process_response(self, ctx: &R) -> Self { - match self { - SyncRound::Start(round_start) => round_start.process_response(ctx), - SyncRound::Fetch(fetcher) => fetcher.process_response(ctx), - other => other, - } - } - - /// Return unfulfilled requests from disconnected peer. Unknown requests will be ignored. - pub fn requests_abandoned(self, abandoned: &[ReqId]) -> Self { - match self { - SyncRound::Start(round_start) => round_start.requests_abandoned(abandoned), - SyncRound::Fetch(fetcher) => fetcher.requests_abandoned(abandoned), - other => other, - } - } - - /// Dispatch pending requests. The dispatcher provided will attempt to - /// find a suitable peer to serve the request. - // TODO: have dispatcher take capabilities argument? and return an error as - // to why no suitable peer can be found? (no buffer, no chain heads that high, etc) - pub fn dispatch_requests(self, dispatcher: D) -> Self - where D: FnMut(HeadersRequest) -> Option - { - match self { - SyncRound::Start(round_start) => round_start.dispatch_requests(dispatcher), - SyncRound::Fetch(fetcher) => fetcher.dispatch_requests(dispatcher), - other => other, - } - } - - /// Drain up to a maximum number (None -> all) of headers (continuous, starting with a child of - /// the round start block) from the round, starting a new one once finished. - pub fn drain(self, v: &mut Vec
, max: Option) -> Self { - match self { - SyncRound::Fetch(fetcher) => fetcher.drain(v, max), - SyncRound::Abort(reason, mut remaining) => { - let len = ::std::cmp::min(max.unwrap_or(usize::max_value()), remaining.len()); - v.extend(remaining.drain(..len)); - SyncRound::Abort(reason, remaining) - } - other => other, - } - } -} - -impl fmt::Debug for SyncRound { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - SyncRound::Start(ref state) => write!(f, "Scaffolding from {:?}", state.start_block), - SyncRound::Fetch(ref fetcher) => write!(f, "Filling scaffold up to {:?}", fetcher.end), - SyncRound::Abort(ref reason, ref remaining) => - write!(f, "Aborted: {:?}, {} remain", reason, remaining.len()), - } - } -} - -#[cfg(test)] -mod tests { - use super::scaffold_params; - - #[test] - fn scaffold_config() { - // within a certain distance of the head, we download - // sequentially. - assert_eq!(scaffold_params(1), (0, 1)); - assert_eq!(scaffold_params(6), (0, 6)); - - // when scaffolds are useful, download enough frames to get - // within a close distance of the goal. - assert_eq!(scaffold_params(1000), (255, 4)); - assert_eq!(scaffold_params(1024), (255, 4)); - } -} diff --git a/ethcore/sync/src/light_sync/tests/mod.rs b/ethcore/sync/src/light_sync/tests/mod.rs deleted file mode 100644 index 9bfb99ed0d9..00000000000 --- a/ethcore/sync/src/light_sync/tests/mod.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use tests::helpers::TestNet; - -use ethcore::client::{BlockInfo, BlockId, EachBlockWith}; - -mod test_net; - -#[test] -fn basic_sync() { - let mut net = TestNet::light(1, 2); - net.peer(1).chain().add_blocks(5000, EachBlockWith::Nothing); - net.peer(2).chain().add_blocks(6000, EachBlockWith::Nothing); - - net.sync(); - - assert!(net.peer(0).light_chain().block_header(BlockId::Number(6000)).is_some()); -} - -#[test] -fn fork_post_cht() { - const CHAIN_LENGTH: u64 = 50; // shouldn't be longer than ::light::cht::size(); - - let mut net = TestNet::light(1, 2); - - // peer 2 is on a higher TD chain. - net.peer(1).chain().add_blocks(CHAIN_LENGTH as usize, EachBlockWith::Nothing); - net.peer(2).chain().add_blocks(CHAIN_LENGTH as usize + 1, EachBlockWith::Uncle); - - // get the light peer on peer 1's chain. - for id in (0..CHAIN_LENGTH).map(|x| x + 1).map(BlockId::Number) { - let (light_peer, full_peer) = (net.peer(0), net.peer(1)); - let light_chain = light_peer.light_chain(); - let header = full_peer.chain().block_header(id).unwrap().decode().expect("decoding failure"); - let _ = light_chain.import_header(header); - light_chain.flush_queue(); - light_chain.import_verified(); - assert!(light_chain.block_header(id).is_some()); - } - - net.sync(); - - for id in (0..CHAIN_LENGTH).map(|x| x + 1).map(BlockId::Number) { - assert_eq!( - net.peer(0).light_chain().block_header(id).unwrap(), - net.peer(2).chain().block_header(id).unwrap() - ); - } -} diff --git a/ethcore/sync/src/light_sync/tests/test_net.rs b/ethcore/sync/src/light_sync/tests/test_net.rs deleted file mode 100644 index 74567c11925..00000000000 --- a/ethcore/sync/src/light_sync/tests/test_net.rs +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! TestNet peer definition. - -use std::collections::{HashSet, VecDeque}; -use std::sync::Arc; - -use light_sync::*; -use tests::helpers::{TestNet, Peer as PeerLike, TestPacket}; - -use ethcore::client::TestBlockChainClient; -use ethcore::spec::Spec; -use io::IoChannel; -use kvdb_memorydb; -use light::client::fetch::{self, Unavailable}; -use light::net::{LightProtocol, IoContext, Capabilities, Params as LightParams}; -use light::provider::LightProvider; -use network::{NodeId, PeerId}; -use parking_lot::RwLock; - -use std::time::Duration; -use light::cache::Cache; - -const NETWORK_ID: u64 = 0xcafebabe; - -pub type LightClient = ::light::client::Client; - -struct TestIoContext<'a> { - queue: &'a RwLock>, - sender: Option, - to_disconnect: RwLock>, -} - -impl<'a> IoContext for TestIoContext<'a> { - fn send(&self, peer: PeerId, packet_id: u8, packet_body: Vec) { - self.queue.write().push_back(TestPacket { - data: packet_body, - packet_id: packet_id, - recipient: peer, - }) - } - - fn respond(&self, packet_id: u8, packet_body: Vec) { - if let Some(sender) = self.sender { - self.send(sender, packet_id, packet_body); - } - } - - fn disconnect_peer(&self, peer: PeerId) { - self.to_disconnect.write().insert(peer); - } - - fn disable_peer(&self, peer: PeerId) { self.disconnect_peer(peer) } - fn protocol_version(&self, _peer: PeerId) -> Option { Some(::light::net::MAX_PROTOCOL_VERSION) } - - fn persistent_peer_id(&self, _peer: PeerId) -> Option { unimplemented!() } - fn is_reserved_peer(&self, _peer: PeerId) -> bool { false } -} - -// peer-specific data. -enum PeerData { - Light(Arc>, Arc), - Full(Arc) -} - -// test peer type. -// Either a full peer or a light peer. -pub struct Peer { - proto: LightProtocol, - queue: RwLock>, - data: PeerData, -} - -impl Peer { - // create a new full-client peer for light client peers to sync to. - // buffer flow is made negligible. - pub fn new_full(chain: Arc) -> Self { - let params = LightParams { - network_id: NETWORK_ID, - config: Default::default(), - capabilities: Capabilities { - serve_headers: true, - serve_chain_since: None, - serve_state_since: None, - tx_relay: true, - }, - sample_store: None, - }; - - let proto = LightProtocol::new(chain.clone(), params); - Peer { - proto: proto, - queue: RwLock::new(VecDeque::new()), - data: PeerData::Full(chain), - } - } - - // create a new light-client peer to sync to full peers. - pub fn new_light(chain: Arc) -> Self { - let sync = Arc::new(LightSync::new(chain.clone()).unwrap()); - let params = LightParams { - network_id: NETWORK_ID, - config: Default::default(), - capabilities: Capabilities { - serve_headers: false, - serve_chain_since: None, - serve_state_since: None, - tx_relay: false, - }, - sample_store: None, - }; - - let provider = LightProvider::new(chain.clone(), Arc::new(RwLock::new(Default::default()))); - let mut proto = LightProtocol::new(Arc::new(provider), params); - proto.add_handler(sync.clone()); - Peer { - proto: proto, - queue: RwLock::new(VecDeque::new()), - data: PeerData::Light(sync, chain), - } - } - - // get the chain from the client, asserting that it is a full node. - pub fn chain(&self) -> &TestBlockChainClient { - match self.data { - PeerData::Full(ref chain) => &*chain, - _ => panic!("Attempted to access full chain on light peer."), - } - } - - // get the light chain from the peer, asserting that it is a light node. - pub fn light_chain(&self) -> &LightClient { - match self.data { - PeerData::Light(_, ref chain) => &*chain, - _ => panic!("Attempted to access light chain on full peer."), - } - } - - // get a test Io context based on - fn io(&self, sender: Option) -> TestIoContext { - TestIoContext { - queue: &self.queue, - sender: sender, - to_disconnect: RwLock::new(HashSet::new()), - } - } -} - -impl PeerLike for Peer { - type Message = TestPacket; - - fn on_connect(&self, other: PeerId) { - let io = self.io(Some(other)); - self.proto.on_connect(other, &io); - } - - fn on_disconnect(&self, other: PeerId){ - let io = self.io(Some(other)); - self.proto.on_disconnect(other, &io); - } - - fn receive_message(&self, from: PeerId, msg: TestPacket) -> HashSet { - let io = self.io(Some(from)); - self.proto.handle_packet(&io, from, msg.packet_id, &msg.data); - io.to_disconnect.into_inner() - } - - fn pending_message(&self) -> Option { - self.queue.write().pop_front() - } - - fn is_done(&self) -> bool { - self.queue.read().is_empty() && match self.data { - PeerData::Light(_, ref client) => { - // should create a test light client which just imports - // headers directly and doesn't have a queue to drain. - client.import_verified(); - client.queue_info().is_empty() - } - _ => true, - } - } - - fn sync_step(&self) { - if let PeerData::Light(_, ref client) = self.data { - client.flush_queue(); - - while !client.queue_info().is_empty() { - client.import_verified() - } - } - } - - fn restart_sync(&self) { } - - fn process_all_io_messages(&self) { } - - fn process_all_new_block_messages(&self) { } -} - -impl TestNet { - /// Create a new `TestNet` for testing light synchronization. - /// The first parameter is the number of light nodes, - /// the second is the number of full nodes. - pub fn light(n_light: usize, n_full: usize) -> Self { - let mut peers = Vec::with_capacity(n_light + n_full); - for _ in 0..n_light { - let mut config = ::light::client::Config::default(); - - // skip full verification because the blocks are bad. - config.verify_full = false; - let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); - let db = kvdb_memorydb::create(0); - let client = LightClient::new( - config, - Arc::new(db), - None, - &Spec::new_test(), - fetch::unavailable(), // TODO: allow fetch from full nodes. - IoChannel::disconnected(), - cache - ).expect("New DB creation infallible; qed"); - - peers.push(Arc::new(Peer::new_light(Arc::new(client)))) - } - - for _ in 0..n_full { - peers.push(Arc::new(Peer::new_full(Arc::new(TestBlockChainClient::new())))) - } - - TestNet { - peers: peers, - started: false, - disconnect_events: Vec::new(), - } - } -} diff --git a/ethcore/sync/src/private_tx.rs b/ethcore/sync/src/private_tx.rs deleted file mode 100644 index c9396af5b4b..00000000000 --- a/ethcore/sync/src/private_tx.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use parking_lot::Mutex; -use ethereum_types::H256; - -/// Trait which should be implemented by a private transaction handler. -pub trait PrivateTxHandler: Send + Sync + 'static { - /// Function called on new private transaction received. - /// Returns the hash of the imported transaction - fn import_private_transaction(&self, rlp: &[u8]) -> Result; - - /// Function called on new signed private transaction received. - /// Returns the hash of the imported transaction - fn import_signed_private_transaction(&self, rlp: &[u8]) -> Result; -} - -/// Nonoperative private transaction handler. -pub struct NoopPrivateTxHandler; - -impl PrivateTxHandler for NoopPrivateTxHandler { - fn import_private_transaction(&self, _rlp: &[u8]) -> Result { - Ok(H256::default()) - } - - fn import_signed_private_transaction(&self, _rlp: &[u8]) -> Result { - Ok(H256::default()) - } -} - -/// Simple private transaction handler. Used for tests. -#[derive(Default)] -pub struct SimplePrivateTxHandler { - /// imported private transactions - pub txs: Mutex>>, - /// imported signed private transactions - pub signed_txs: Mutex>>, -} - -impl PrivateTxHandler for SimplePrivateTxHandler { - fn import_private_transaction(&self, rlp: &[u8]) -> Result { - self.txs.lock().push(rlp.to_vec()); - Ok(H256::default()) - } - - fn import_signed_private_transaction(&self, rlp: &[u8]) -> Result { - self.signed_txs.lock().push(rlp.to_vec()); - Ok(H256::default()) - } -} diff --git a/ethcore/sync/src/snapshot.rs b/ethcore/sync/src/snapshot.rs index 64e463c7b25..72b90574da7 100644 --- a/ethcore/sync/src/snapshot.rs +++ b/ethcore/sync/src/snapshot.rs @@ -1,272 +1,285 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use ethcore::snapshot::{ManifestData, SnapshotService}; use ethereum_types::H256; use hash::keccak; -use std::collections::HashSet; -use std::iter::FromIterator; +use std::{collections::HashSet, iter::FromIterator}; #[derive(PartialEq, Eq, Debug)] pub enum ChunkType { - State(H256), - Block(H256), + State(H256), + Block(H256), } pub struct Snapshot { - pending_state_chunks: Vec, - pending_block_chunks: Vec, - downloading_chunks: HashSet, - completed_chunks: HashSet, - snapshot_hash: Option, - bad_hashes: HashSet, - initialized: bool, + pending_state_chunks: Vec, + pending_block_chunks: Vec, + downloading_chunks: HashSet, + completed_chunks: HashSet, + snapshot_hash: Option, + bad_hashes: HashSet, + initialized: bool, } impl Snapshot { - /// Create a new instance. - pub fn new() -> Snapshot { - Snapshot { - pending_state_chunks: Vec::new(), - pending_block_chunks: Vec::new(), - downloading_chunks: HashSet::new(), - completed_chunks: HashSet::new(), - snapshot_hash: None, - bad_hashes: HashSet::new(), - initialized: false, - } - } - - /// Sync the Snapshot completed chunks with the Snapshot Service - pub fn initialize(&mut self, snapshot_service: &SnapshotService) { - if self.initialized { - return; - } - - if let Some(completed_chunks) = snapshot_service.completed_chunks() { - self.completed_chunks = HashSet::from_iter(completed_chunks); - } - - trace!( - target: "snapshot", - "Snapshot is now initialized with {} completed chunks.", - self.completed_chunks.len(), - ); - - self.initialized = true; - } - - /// Clear everything. - pub fn clear(&mut self) { - self.pending_state_chunks.clear(); - self.pending_block_chunks.clear(); - self.downloading_chunks.clear(); - self.completed_chunks.clear(); - self.snapshot_hash = None; - self.initialized = false; - } - - /// Check if currently downloading a snapshot. - pub fn have_manifest(&self) -> bool { - self.snapshot_hash.is_some() - } - - /// Reset collection for a manifest RLP - pub fn reset_to(&mut self, manifest: &ManifestData, hash: &H256) { - self.clear(); - self.pending_state_chunks = manifest.state_hashes.clone(); - self.pending_block_chunks = manifest.block_hashes.clone(); - self.snapshot_hash = Some(hash.clone()); - } - - /// Validate chunk and mark it as downloaded - pub fn validate_chunk(&mut self, chunk: &[u8]) -> Result { - let hash = keccak(chunk); - if self.completed_chunks.contains(&hash) { - trace!(target: "sync", "Ignored proccessed chunk: {:x}", hash); - return Err(()); - } - self.downloading_chunks.remove(&hash); - if self.pending_block_chunks.iter().any(|h| h == &hash) { - self.completed_chunks.insert(hash.clone()); - return Ok(ChunkType::Block(hash)); - } - if self.pending_state_chunks.iter().any(|h| h == &hash) { - self.completed_chunks.insert(hash.clone()); - return Ok(ChunkType::State(hash)); - } - trace!(target: "sync", "Ignored unknown chunk: {:x}", hash); - Err(()) - } - - /// Find a chunk to download - pub fn needed_chunk(&mut self) -> Option { - // Find next needed chunk: first block, then state chunks - let chunk = { - let chunk_filter = |h| !self.downloading_chunks.contains(h) && !self.completed_chunks.contains(h); - - let needed_block_chunk = self.pending_block_chunks.iter() - .filter(|&h| chunk_filter(h)) - .map(|h| *h) - .next(); - - // If no block chunks to download, get the state chunks - if needed_block_chunk.is_none() { - self.pending_state_chunks.iter() - .filter(|&h| chunk_filter(h)) - .map(|h| *h) - .next() - } else { - needed_block_chunk - } - }; - - if let Some(hash) = chunk { - self.downloading_chunks.insert(hash.clone()); - } - chunk - } - - pub fn clear_chunk_download(&mut self, hash: &H256) { - self.downloading_chunks.remove(hash); - } - - // note snapshot hash as bad. - pub fn note_bad(&mut self, hash: H256) { - self.bad_hashes.insert(hash); - } - - // whether snapshot hash is known to be bad. - pub fn is_known_bad(&self, hash: &H256) -> bool { - self.bad_hashes.contains(hash) - } - - pub fn snapshot_hash(&self) -> Option { - self.snapshot_hash - } - - pub fn total_chunks(&self) -> usize { - self.pending_block_chunks.len() + self.pending_state_chunks.len() - } - - pub fn done_chunks(&self) -> usize { - self.completed_chunks.len() - } - - pub fn is_complete(&self) -> bool { - self.total_chunks() == self.completed_chunks.len() - } + /// Create a new instance. + pub fn new() -> Snapshot { + Snapshot { + pending_state_chunks: Vec::new(), + pending_block_chunks: Vec::new(), + downloading_chunks: HashSet::new(), + completed_chunks: HashSet::new(), + snapshot_hash: None, + bad_hashes: HashSet::new(), + initialized: false, + } + } + + /// Sync the Snapshot completed chunks with the Snapshot Service + pub fn initialize(&mut self, snapshot_service: &dyn SnapshotService) { + if self.initialized { + return; + } + + if let Some(completed_chunks) = snapshot_service.completed_chunks() { + self.completed_chunks = HashSet::from_iter(completed_chunks); + } + + trace!( + target: "snapshot", + "Snapshot is now initialized with {} completed chunks.", + self.completed_chunks.len(), + ); + + self.initialized = true; + } + + /// Clear everything. + pub fn clear(&mut self) { + self.pending_state_chunks.clear(); + self.pending_block_chunks.clear(); + self.downloading_chunks.clear(); + self.completed_chunks.clear(); + self.snapshot_hash = None; + self.initialized = false; + } + + /// Check if currently downloading a snapshot. + pub fn have_manifest(&self) -> bool { + self.snapshot_hash.is_some() + } + + /// Reset collection for a manifest RLP + pub fn reset_to(&mut self, manifest: &ManifestData, hash: &H256) { + self.clear(); + self.pending_state_chunks = manifest.state_hashes.clone(); + self.pending_block_chunks = manifest.block_hashes.clone(); + self.snapshot_hash = Some(hash.clone()); + } + + /// Validate chunk and mark it as downloaded + pub fn validate_chunk(&mut self, chunk: &[u8]) -> Result { + let hash = keccak(chunk); + if self.completed_chunks.contains(&hash) { + trace!(target: "sync", "Ignored proccessed chunk: {:x}", hash); + return Err(()); + } + self.downloading_chunks.remove(&hash); + if self.pending_block_chunks.iter().any(|h| h == &hash) { + self.completed_chunks.insert(hash.clone()); + return Ok(ChunkType::Block(hash)); + } + if self.pending_state_chunks.iter().any(|h| h == &hash) { + self.completed_chunks.insert(hash.clone()); + return Ok(ChunkType::State(hash)); + } + trace!(target: "sync", "Ignored unknown chunk: {:x}", hash); + Err(()) + } + + /// Find a chunk to download + pub fn needed_chunk(&mut self) -> Option { + // Find next needed chunk: first block, then state chunks + let chunk = { + let chunk_filter = + |h| !self.downloading_chunks.contains(h) && !self.completed_chunks.contains(h); + + let needed_block_chunk = self + .pending_block_chunks + .iter() + .filter(|&h| chunk_filter(h)) + .map(|h| *h) + .next(); + + // If no block chunks to download, get the state chunks + if needed_block_chunk.is_none() { + self.pending_state_chunks + .iter() + .filter(|&h| chunk_filter(h)) + .map(|h| *h) + .next() + } else { + needed_block_chunk + } + }; + + if let Some(hash) = chunk { + self.downloading_chunks.insert(hash.clone()); + } + chunk + } + + pub fn clear_chunk_download(&mut self, hash: &H256) { + self.downloading_chunks.remove(hash); + } + + // note snapshot hash as bad. + pub fn note_bad(&mut self, hash: H256) { + self.bad_hashes.insert(hash); + } + + // whether snapshot hash is known to be bad. + pub fn is_known_bad(&self, hash: &H256) -> bool { + self.bad_hashes.contains(hash) + } + + pub fn snapshot_hash(&self) -> Option { + self.snapshot_hash + } + + pub fn total_chunks(&self) -> usize { + self.pending_block_chunks.len() + self.pending_state_chunks.len() + } + + pub fn done_chunks(&self) -> usize { + self.completed_chunks.len() + } + + pub fn is_complete(&self) -> bool { + self.total_chunks() == self.completed_chunks.len() + } } #[cfg(test)] mod test { - use hash::keccak; - use bytes::Bytes; - use super::*; - use ethcore::snapshot::ManifestData; - - fn is_empty(snapshot: &Snapshot) -> bool { - snapshot.pending_block_chunks.is_empty() && - snapshot.pending_state_chunks.is_empty() && - snapshot.completed_chunks.is_empty() && - snapshot.downloading_chunks.is_empty() && - snapshot.snapshot_hash.is_none() - } - - fn test_manifest() -> (ManifestData, H256, Vec, Vec) { - let state_chunks: Vec = (0..20).map(|_| H256::random().to_vec()).collect(); - let block_chunks: Vec = (0..20).map(|_| H256::random().to_vec()).collect(); - let manifest = ManifestData { - version: 2, - state_hashes: state_chunks.iter().map(|data| keccak(data)).collect(), - block_hashes: block_chunks.iter().map(|data| keccak(data)).collect(), - state_root: H256::new(), - block_number: 42, - block_hash: H256::new(), - }; - let mhash = keccak(manifest.clone().into_rlp()); - (manifest, mhash, state_chunks, block_chunks) - } - - #[test] - fn create_clear() { - let mut snapshot = Snapshot::new(); - assert!(is_empty(&snapshot)); - let (manifest, mhash, _, _,) = test_manifest(); - snapshot.reset_to(&manifest, &mhash); - assert!(!is_empty(&snapshot)); - snapshot.clear(); - assert!(is_empty(&snapshot)); - } - - #[test] - fn validate_chunks() { - let mut snapshot = Snapshot::new(); - let (manifest, mhash, state_chunks, block_chunks) = test_manifest(); - snapshot.reset_to(&manifest, &mhash); - assert_eq!(snapshot.done_chunks(), 0); - assert!(snapshot.validate_chunk(&H256::random().to_vec()).is_err()); - - let requested: Vec = (0..40).map(|_| snapshot.needed_chunk().unwrap()).collect(); - assert!(snapshot.needed_chunk().is_none()); - - let requested_all_block_chunks = manifest.block_hashes.iter() - .all(|h| requested.iter().any(|rh| rh == h)); - assert!(requested_all_block_chunks); - - let requested_all_state_chunks = manifest.state_hashes.iter() - .all(|h| requested.iter().any(|rh| rh == h)); - assert!(requested_all_state_chunks); - - assert_eq!(snapshot.downloading_chunks.len(), 40); - - assert_eq!(snapshot.validate_chunk(&state_chunks[4]), Ok(ChunkType::State(manifest.state_hashes[4].clone()))); - assert_eq!(snapshot.completed_chunks.len(), 1); - assert_eq!(snapshot.downloading_chunks.len(), 39); - - assert_eq!(snapshot.validate_chunk(&block_chunks[10]), Ok(ChunkType::Block(manifest.block_hashes[10].clone()))); - assert_eq!(snapshot.completed_chunks.len(), 2); - assert_eq!(snapshot.downloading_chunks.len(), 38); - - for (i, data) in state_chunks.iter().enumerate() { - if i != 4 { - assert!(snapshot.validate_chunk(data).is_ok()); - } - } - - for (i, data) in block_chunks.iter().enumerate() { - if i != 10 { - assert!(snapshot.validate_chunk(data).is_ok()); - } - } - - assert!(snapshot.is_complete()); - assert_eq!(snapshot.done_chunks(), 40); - assert_eq!(snapshot.done_chunks(), snapshot.total_chunks()); - assert_eq!(snapshot.snapshot_hash(), Some(keccak(manifest.into_rlp()))); - } - - #[test] - fn tracks_known_bad() { - let mut snapshot = Snapshot::new(); - let hash = H256::random(); - - assert_eq!(snapshot.is_known_bad(&hash), false); - snapshot.note_bad(hash); - assert_eq!(snapshot.is_known_bad(&hash), true); - } + use super::*; + use bytes::Bytes; + use ethcore::snapshot::ManifestData; + use hash::keccak; + + fn is_empty(snapshot: &Snapshot) -> bool { + snapshot.pending_block_chunks.is_empty() + && snapshot.pending_state_chunks.is_empty() + && snapshot.completed_chunks.is_empty() + && snapshot.downloading_chunks.is_empty() + && snapshot.snapshot_hash.is_none() + } + + fn test_manifest() -> (ManifestData, H256, Vec, Vec) { + let state_chunks: Vec = (0..20).map(|_| H256::random().to_vec()).collect(); + let block_chunks: Vec = (0..20).map(|_| H256::random().to_vec()).collect(); + let manifest = ManifestData { + version: 2, + state_hashes: state_chunks.iter().map(|data| keccak(data)).collect(), + block_hashes: block_chunks.iter().map(|data| keccak(data)).collect(), + state_root: H256::new(), + block_number: 42, + block_hash: H256::new(), + }; + let mhash = keccak(manifest.clone().into_rlp()); + (manifest, mhash, state_chunks, block_chunks) + } + + #[test] + fn create_clear() { + let mut snapshot = Snapshot::new(); + assert!(is_empty(&snapshot)); + let (manifest, mhash, _, _) = test_manifest(); + snapshot.reset_to(&manifest, &mhash); + assert!(!is_empty(&snapshot)); + snapshot.clear(); + assert!(is_empty(&snapshot)); + } + + #[test] + fn validate_chunks() { + let mut snapshot = Snapshot::new(); + let (manifest, mhash, state_chunks, block_chunks) = test_manifest(); + snapshot.reset_to(&manifest, &mhash); + assert_eq!(snapshot.done_chunks(), 0); + assert!(snapshot.validate_chunk(&H256::random().to_vec()).is_err()); + + let requested: Vec = (0..40).map(|_| snapshot.needed_chunk().unwrap()).collect(); + assert!(snapshot.needed_chunk().is_none()); + + let requested_all_block_chunks = manifest + .block_hashes + .iter() + .all(|h| requested.iter().any(|rh| rh == h)); + assert!(requested_all_block_chunks); + + let requested_all_state_chunks = manifest + .state_hashes + .iter() + .all(|h| requested.iter().any(|rh| rh == h)); + assert!(requested_all_state_chunks); + + assert_eq!(snapshot.downloading_chunks.len(), 40); + + assert_eq!( + snapshot.validate_chunk(&state_chunks[4]), + Ok(ChunkType::State(manifest.state_hashes[4].clone())) + ); + assert_eq!(snapshot.completed_chunks.len(), 1); + assert_eq!(snapshot.downloading_chunks.len(), 39); + + assert_eq!( + snapshot.validate_chunk(&block_chunks[10]), + Ok(ChunkType::Block(manifest.block_hashes[10].clone())) + ); + assert_eq!(snapshot.completed_chunks.len(), 2); + assert_eq!(snapshot.downloading_chunks.len(), 38); + + for (i, data) in state_chunks.iter().enumerate() { + if i != 4 { + assert!(snapshot.validate_chunk(data).is_ok()); + } + } + + for (i, data) in block_chunks.iter().enumerate() { + if i != 10 { + assert!(snapshot.validate_chunk(data).is_ok()); + } + } + + assert!(snapshot.is_complete()); + assert_eq!(snapshot.done_chunks(), 40); + assert_eq!(snapshot.done_chunks(), snapshot.total_chunks()); + assert_eq!(snapshot.snapshot_hash(), Some(keccak(manifest.into_rlp()))); + } + + #[test] + fn tracks_known_bad() { + let mut snapshot = Snapshot::new(); + let hash = H256::random(); + + assert_eq!(snapshot.is_known_bad(&hash), false); + snapshot.note_bad(hash); + assert_eq!(snapshot.is_known_bad(&hash), true); + } } diff --git a/ethcore/sync/src/sync_io.rs b/ethcore/sync/src/sync_io.rs index 56bf98ab2ee..8527b78105c 100644 --- a/ethcore/sync/src/sync_io.rs +++ b/ethcore/sync/src/sync_io.rs @@ -1,140 +1,147 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::collections::HashMap; -use chain::sync_packet::{PacketInfo, SyncPacket}; -use network::{NetworkContext, PeerId, PacketId, Error, SessionInfo, ProtocolId}; -use network::client_version::ClientVersion; use bytes::Bytes; -use ethcore::client::BlockChainClient; -use types::BlockNumber; -use ethcore::snapshot::SnapshotService; +use chain::sync_packet::{PacketInfo, SyncPacket}; +use ethcore::{client::BlockChainClient, snapshot::SnapshotService}; +use network::{ + client_version::ClientVersion, Error, NetworkContext, PacketId, PeerId, ProtocolId, SessionInfo, +}; use parking_lot::RwLock; +use std::collections::HashMap; +use types::BlockNumber; /// IO interface for the syncing handler. /// Provides peer connection management and an interface to the blockchain client. // TODO: ratings pub trait SyncIo { - /// Disable a peer - fn disable_peer(&mut self, peer_id: PeerId); - /// Disconnect peer - fn disconnect_peer(&mut self, peer_id: PeerId); - /// Respond to current request with a packet. Can be called from an IO handler for incoming packet. - fn respond(&mut self, packet_id: PacketId, data: Vec) -> Result<(), Error>; - /// Send a packet to a peer using specified protocol. - fn send(&mut self, peer_id: PeerId, packet_id: SyncPacket, data: Vec) -> Result<(), Error>; - /// Get the blockchain - fn chain(&self) -> &BlockChainClient; - /// Get the snapshot service. - fn snapshot_service(&self) -> &SnapshotService; - /// Returns peer version identifier - fn peer_version(&self, peer_id: PeerId) -> ClientVersion { - ClientVersion::from(peer_id.to_string()) - } - /// Returns information on p2p session - fn peer_session_info(&self, peer_id: PeerId) -> Option; - /// Maximum mutually supported ETH protocol version - fn eth_protocol_version(&self, peer_id: PeerId) -> u8; - /// Maximum mutually supported version of a gien protocol. - fn protocol_version(&self, protocol: &ProtocolId, peer_id: PeerId) -> u8; - /// Returns if the chain block queue empty - fn is_chain_queue_empty(&self) -> bool { - self.chain().is_queue_empty() - } - /// Check if the session is expired - fn is_expired(&self) -> bool; - /// Return sync overlay - fn chain_overlay(&self) -> &RwLock>; - /// Returns the size the payload shouldn't exceed - fn payload_soft_limit(&self) -> usize; + /// Disable a peer + fn disable_peer(&mut self, peer_id: PeerId); + /// Disconnect peer + fn disconnect_peer(&mut self, peer_id: PeerId); + /// Respond to current request with a packet. Can be called from an IO handler for incoming packet. + fn respond(&mut self, packet_id: PacketId, data: Vec) -> Result<(), Error>; + /// Send a packet to a peer using specified protocol. + fn send(&mut self, peer_id: PeerId, packet_id: SyncPacket, data: Vec) -> Result<(), Error>; + /// Get the blockchain + fn chain(&self) -> &dyn BlockChainClient; + /// Get the snapshot service. + fn snapshot_service(&self) -> &dyn SnapshotService; + /// Returns peer version identifier + fn peer_version(&self, peer_id: PeerId) -> ClientVersion { + ClientVersion::from(peer_id.to_string()) + } + /// Returns information on p2p session + fn peer_session_info(&self, peer_id: PeerId) -> Option; + /// Maximum mutually supported ETH protocol version + fn eth_protocol_version(&self, peer_id: PeerId) -> u8; + /// Maximum mutually supported version of a gien protocol. + fn protocol_version(&self, protocol: &ProtocolId, peer_id: PeerId) -> u8; + /// Returns if the chain block queue empty + fn is_chain_queue_empty(&self) -> bool { + self.chain().is_queue_empty() + } + /// Check if the session is expired + fn is_expired(&self) -> bool; + /// Return sync overlay + fn chain_overlay(&self) -> &RwLock>; + /// Returns the size the payload shouldn't exceed + fn payload_soft_limit(&self) -> usize; } /// Wraps `NetworkContext` and the blockchain client pub struct NetSyncIo<'s> { - network: &'s NetworkContext, - chain: &'s BlockChainClient, - snapshot_service: &'s SnapshotService, - chain_overlay: &'s RwLock>, + network: &'s dyn NetworkContext, + chain: &'s dyn BlockChainClient, + snapshot_service: &'s dyn SnapshotService, + chain_overlay: &'s RwLock>, } impl<'s> NetSyncIo<'s> { - /// Creates a new instance from the `NetworkContext` and the blockchain client reference. - pub fn new(network: &'s NetworkContext, - chain: &'s BlockChainClient, - snapshot_service: &'s SnapshotService, - chain_overlay: &'s RwLock>) -> NetSyncIo<'s> { - NetSyncIo { - network: network, - chain: chain, - snapshot_service: snapshot_service, - chain_overlay: chain_overlay, - } - } + /// Creates a new instance from the `NetworkContext` and the blockchain client reference. + pub fn new( + network: &'s dyn NetworkContext, + chain: &'s dyn BlockChainClient, + snapshot_service: &'s dyn SnapshotService, + chain_overlay: &'s RwLock>, + ) -> NetSyncIo<'s> { + NetSyncIo { + network: network, + chain: chain, + snapshot_service: snapshot_service, + chain_overlay: chain_overlay, + } + } } impl<'s> SyncIo for NetSyncIo<'s> { - fn disable_peer(&mut self, peer_id: PeerId) { - self.network.disable_peer(peer_id); - } - - fn disconnect_peer(&mut self, peer_id: PeerId) { - self.network.disconnect_peer(peer_id); - } - - fn respond(&mut self, packet_id: PacketId, data: Vec) -> Result<(), Error>{ - self.network.respond(packet_id, data) - } - - fn send(&mut self, peer_id: PeerId, packet_id: SyncPacket, data: Vec) -> Result<(), Error>{ - self.network.send_protocol(packet_id.protocol(), peer_id, packet_id.id(), data) - } - - fn chain(&self) -> &BlockChainClient { - self.chain - } - - fn chain_overlay(&self) -> &RwLock> { - self.chain_overlay - } - - fn snapshot_service(&self) -> &SnapshotService { - self.snapshot_service - } - - fn peer_session_info(&self, peer_id: PeerId) -> Option { - self.network.session_info(peer_id) - } - - fn is_expired(&self) -> bool { - self.network.is_expired() - } - - fn eth_protocol_version(&self, peer_id: PeerId) -> u8 { - self.network.protocol_version(self.network.subprotocol_name(), peer_id).unwrap_or(0) - } - - fn protocol_version(&self, protocol: &ProtocolId, peer_id: PeerId) -> u8 { - self.network.protocol_version(*protocol, peer_id).unwrap_or(0) - } - - fn peer_version(&self, peer_id: PeerId) -> ClientVersion { - self.network.peer_client_version(peer_id) - } - - fn payload_soft_limit(&self) -> usize { - self.network.payload_soft_limit() - } + fn disable_peer(&mut self, peer_id: PeerId) { + self.network.disable_peer(peer_id); + } + + fn disconnect_peer(&mut self, peer_id: PeerId) { + self.network.disconnect_peer(peer_id); + } + + fn respond(&mut self, packet_id: PacketId, data: Vec) -> Result<(), Error> { + self.network.respond(packet_id, data) + } + + fn send(&mut self, peer_id: PeerId, packet_id: SyncPacket, data: Vec) -> Result<(), Error> { + self.network + .send_protocol(packet_id.protocol(), peer_id, packet_id.id(), data) + } + + fn chain(&self) -> &dyn BlockChainClient { + self.chain + } + + fn chain_overlay(&self) -> &RwLock> { + self.chain_overlay + } + + fn snapshot_service(&self) -> &dyn SnapshotService { + self.snapshot_service + } + + fn peer_session_info(&self, peer_id: PeerId) -> Option { + self.network.session_info(peer_id) + } + + fn is_expired(&self) -> bool { + self.network.is_expired() + } + + fn eth_protocol_version(&self, peer_id: PeerId) -> u8 { + self.network + .protocol_version(self.network.subprotocol_name(), peer_id) + .unwrap_or(0) + } + + fn protocol_version(&self, protocol: &ProtocolId, peer_id: PeerId) -> u8 { + self.network + .protocol_version(*protocol, peer_id) + .unwrap_or(0) + } + + fn peer_version(&self, peer_id: PeerId) -> ClientVersion { + self.network.peer_client_version(peer_id) + } + + fn payload_soft_limit(&self) -> usize { + self.network.payload_soft_limit() + } } diff --git a/ethcore/sync/src/tests/chain.rs b/ethcore/sync/src/tests/chain.rs index d81a876d7a8..3cc12729b01 100644 --- a/ethcore/sync/src/tests/chain.rs +++ b/ethcore/sync/src/tests/chain.rs @@ -1,264 +1,301 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::sync::Arc; -use ethcore::client::{TestBlockChainClient, BlockChainClient, BlockId, EachBlockWith, ChainInfo, BlockInfo}; -use chain::{SyncState}; use super::helpers::*; -use {SyncConfig, WarpSync}; +use chain::SyncState; +use ethcore::client::{ + BlockChainClient, BlockId, BlockInfo, ChainInfo, EachBlockWith, TestBlockChainClient, +}; +use std::sync::Arc; +use SyncConfig; +use WarpSync; #[test] fn two_peers() { - ::env_logger::try_init().ok(); - let mut net = TestNet::new(3); - net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle); - net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle); - net.sync(); - assert!(net.peer(0).chain.block(BlockId::Number(1000)).is_some()); - assert_eq!(*net.peer(0).chain.blocks.read(), *net.peer(1).chain.blocks.read()); + ::env_logger::try_init().ok(); + let mut net = TestNet::new(3); + net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle); + net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle); + net.sync(); + assert!(net.peer(0).chain.block(BlockId::Number(1000)).is_some()); + assert_eq!( + *net.peer(0).chain.blocks.read(), + *net.peer(1).chain.blocks.read() + ); } #[test] fn long_chain() { - ::env_logger::try_init().ok(); - let mut net = TestNet::new(2); - net.peer(1).chain.add_blocks(50000, EachBlockWith::Nothing); - net.sync(); - assert!(net.peer(0).chain.block(BlockId::Number(50000)).is_some()); - assert_eq!(*net.peer(0).chain.blocks.read(), *net.peer(1).chain.blocks.read()); + ::env_logger::try_init().ok(); + let mut net = TestNet::new(2); + net.peer(1).chain.add_blocks(50000, EachBlockWith::Nothing); + net.sync(); + assert!(net.peer(0).chain.block(BlockId::Number(50000)).is_some()); + assert_eq!( + *net.peer(0).chain.blocks.read(), + *net.peer(1).chain.blocks.read() + ); } #[test] fn status_after_sync() { - ::env_logger::try_init().ok(); - let mut net = TestNet::new(3); - net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle); - net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle); - net.sync(); - let status = net.peer(0).sync.read().status(); - assert_eq!(status.state, SyncState::Idle); + ::env_logger::try_init().ok(); + let mut net = TestNet::new(3); + net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle); + net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle); + net.sync(); + let status = net.peer(0).sync.read().status(); + assert_eq!(status.state, SyncState::Idle); } #[test] fn takes_few_steps() { - let mut net = TestNet::new(3); - net.peer(1).chain.add_blocks(100, EachBlockWith::Uncle); - net.peer(2).chain.add_blocks(100, EachBlockWith::Uncle); - let total_steps = net.sync(); - assert!(total_steps < 20); + let mut net = TestNet::new(3); + net.peer(1).chain.add_blocks(100, EachBlockWith::Uncle); + net.peer(2).chain.add_blocks(100, EachBlockWith::Uncle); + let total_steps = net.sync(); + assert!(total_steps < 20); } #[test] fn empty_blocks() { - ::env_logger::try_init().ok(); - let mut net = TestNet::new(3); - for n in 0..200 { - let with = if n % 2 == 0 { EachBlockWith::Nothing } else { EachBlockWith::Uncle }; - net.peer(1).chain.add_blocks(5, with.clone()); - net.peer(2).chain.add_blocks(5, with); - } - net.sync(); - assert!(net.peer(0).chain.block(BlockId::Number(1000)).is_some()); - assert_eq!(*net.peer(0).chain.blocks.read(), *net.peer(1).chain.blocks.read()); + ::env_logger::try_init().ok(); + let mut net = TestNet::new(3); + for n in 0..200 { + let with = if n % 2 == 0 { + EachBlockWith::Nothing + } else { + EachBlockWith::Uncle + }; + net.peer(1).chain.add_blocks(5, with.clone()); + net.peer(2).chain.add_blocks(5, with); + } + net.sync(); + assert!(net.peer(0).chain.block(BlockId::Number(1000)).is_some()); + assert_eq!( + *net.peer(0).chain.blocks.read(), + *net.peer(1).chain.blocks.read() + ); } #[test] fn forked() { - ::env_logger::try_init().ok(); - let mut net = TestNet::new(3); - net.peer(0).chain.add_blocks(30, EachBlockWith::Uncle); - net.peer(1).chain.add_blocks(30, EachBlockWith::Uncle); - net.peer(2).chain.add_blocks(30, EachBlockWith::Uncle); - net.peer(0).chain.add_blocks(10, EachBlockWith::Nothing); //fork - net.peer(1).chain.add_blocks(20, EachBlockWith::Uncle); - net.peer(2).chain.add_blocks(20, EachBlockWith::Uncle); - net.peer(1).chain.add_blocks(10, EachBlockWith::Uncle); //fork between 1 and 2 - net.peer(2).chain.add_blocks(1, EachBlockWith::Nothing); - // peer 1 has the best chain of 601 blocks - let peer1_chain = net.peer(1).chain.numbers.read().clone(); - net.sync(); - assert_eq!(*net.peer(0).chain.difficulty.read(), *net.peer(1).chain.difficulty.read()); - assert_eq!(&*net.peer(0).chain.numbers.read(), &peer1_chain); - assert_eq!(&*net.peer(1).chain.numbers.read(), &peer1_chain); - assert_eq!(&*net.peer(2).chain.numbers.read(), &peer1_chain); + ::env_logger::try_init().ok(); + let mut net = TestNet::new(3); + net.peer(0).chain.add_blocks(30, EachBlockWith::Uncle); + net.peer(1).chain.add_blocks(30, EachBlockWith::Uncle); + net.peer(2).chain.add_blocks(30, EachBlockWith::Uncle); + net.peer(0).chain.add_blocks(10, EachBlockWith::Nothing); //fork + net.peer(1).chain.add_blocks(20, EachBlockWith::Uncle); + net.peer(2).chain.add_blocks(20, EachBlockWith::Uncle); + net.peer(1).chain.add_blocks(10, EachBlockWith::Uncle); //fork between 1 and 2 + net.peer(2).chain.add_blocks(1, EachBlockWith::Nothing); + // peer 1 has the best chain of 601 blocks + let peer1_chain = net.peer(1).chain.numbers.read().clone(); + net.sync(); + assert_eq!( + *net.peer(0).chain.difficulty.read(), + *net.peer(1).chain.difficulty.read() + ); + assert_eq!(&*net.peer(0).chain.numbers.read(), &peer1_chain); + assert_eq!(&*net.peer(1).chain.numbers.read(), &peer1_chain); + assert_eq!(&*net.peer(2).chain.numbers.read(), &peer1_chain); } #[test] fn forked_with_misbehaving_peer() { - ::env_logger::try_init().ok(); - let mut net = TestNet::new(3); - - let mut alt_spec = ::ethcore::spec::Spec::new_test(); - alt_spec.extra_data = b"fork".to_vec(); - // peer 0 is on a totally different chain with higher total difficulty - net.peer_mut(0).chain = Arc::new(TestBlockChainClient::new_with_spec(alt_spec)); - net.peer(0).chain.add_blocks(50, EachBlockWith::Nothing); - net.peer(1).chain.add_blocks(10, EachBlockWith::Nothing); - net.peer(2).chain.add_blocks(10, EachBlockWith::Nothing); - - net.peer(1).chain.add_blocks(10, EachBlockWith::Nothing); - net.peer(2).chain.add_blocks(20, EachBlockWith::Uncle); - // peer 1 should sync to peer 2, others should not change - let peer0_chain = net.peer(0).chain.numbers.read().clone(); - let peer2_chain = net.peer(2).chain.numbers.read().clone(); - net.sync(); - assert_eq!(&*net.peer(0).chain.numbers.read(), &peer0_chain); - assert_eq!(&*net.peer(1).chain.numbers.read(), &peer2_chain); - assert_eq!(&*net.peer(2).chain.numbers.read(), &peer2_chain); + ::env_logger::try_init().ok(); + let mut net = TestNet::new(3); + + let mut alt_spec = ::ethcore::spec::Spec::new_test(); + alt_spec.extra_data = b"fork".to_vec(); + // peer 0 is on a totally different chain with higher total difficulty + net.peer_mut(0).chain = Arc::new(TestBlockChainClient::new_with_spec(alt_spec)); + net.peer(0).chain.add_blocks(50, EachBlockWith::Nothing); + net.peer(1).chain.add_blocks(10, EachBlockWith::Nothing); + net.peer(2).chain.add_blocks(10, EachBlockWith::Nothing); + + net.peer(1).chain.add_blocks(10, EachBlockWith::Nothing); + net.peer(2).chain.add_blocks(20, EachBlockWith::Uncle); + // peer 1 should sync to peer 2, others should not change + let peer0_chain = net.peer(0).chain.numbers.read().clone(); + let peer2_chain = net.peer(2).chain.numbers.read().clone(); + net.sync(); + assert_eq!(&*net.peer(0).chain.numbers.read(), &peer0_chain); + assert_eq!(&*net.peer(1).chain.numbers.read(), &peer2_chain); + assert_eq!(&*net.peer(2).chain.numbers.read(), &peer2_chain); } #[test] fn net_hard_fork() { - ::env_logger::try_init().ok(); - let ref_client = TestBlockChainClient::new(); - ref_client.add_blocks(50, EachBlockWith::Uncle); - { - let mut net = TestNet::new_with_fork(2, Some((50, ref_client.block_hash(BlockId::Number(50)).unwrap()))); - net.peer(0).chain.add_blocks(100, EachBlockWith::Uncle); - net.sync(); - assert_eq!(net.peer(1).chain.chain_info().best_block_number, 100); - } - { - let mut net = TestNet::new_with_fork(2, Some((50, ref_client.block_hash(BlockId::Number(50)).unwrap()))); - net.peer(0).chain.add_blocks(100, EachBlockWith::Nothing); - net.sync(); - assert_eq!(net.peer(1).chain.chain_info().best_block_number, 0); - } + ::env_logger::try_init().ok(); + let ref_client = TestBlockChainClient::new(); + ref_client.add_blocks(50, EachBlockWith::Uncle); + { + let mut net = TestNet::new_with_fork( + 2, + Some((50, ref_client.block_hash(BlockId::Number(50)).unwrap())), + ); + net.peer(0).chain.add_blocks(100, EachBlockWith::Uncle); + net.sync(); + assert_eq!(net.peer(1).chain.chain_info().best_block_number, 100); + } + { + let mut net = TestNet::new_with_fork( + 2, + Some((50, ref_client.block_hash(BlockId::Number(50)).unwrap())), + ); + net.peer(0).chain.add_blocks(100, EachBlockWith::Nothing); + net.sync(); + assert_eq!(net.peer(1).chain.chain_info().best_block_number, 0); + } } #[test] fn restart() { - ::env_logger::try_init().ok(); - let mut net = TestNet::new(3); - net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle); - net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle); + ::env_logger::try_init().ok(); + let mut net = TestNet::new(3); + net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle); + net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle); - net.sync(); + net.sync(); - // make sure that sync has actually happened - assert!(net.peer(0).chain.chain_info().best_block_number > 100); - net.restart_peer(0); + // make sure that sync has actually happened + assert!(net.peer(0).chain.chain_info().best_block_number > 100); + net.restart_peer(0); - let status = net.peer(0).sync.read().status(); - assert_eq!(status.state, SyncState::Idle); + let status = net.peer(0).sync.read().status(); + assert_eq!(status.state, SyncState::Idle); } #[test] fn status_empty() { - let net = TestNet::new(2); - assert_eq!(net.peer(0).sync.read().status().state, SyncState::Idle); - let mut config = SyncConfig::default(); - config.warp_sync = WarpSync::Enabled; - let net = TestNet::new_with_config(2, config); - assert_eq!(net.peer(0).sync.read().status().state, SyncState::WaitingPeers); + let net = TestNet::new(2); + assert_eq!(net.peer(0).sync.read().status().state, SyncState::Idle); + let mut config = SyncConfig::default(); + config.warp_sync = WarpSync::Enabled; + let net = TestNet::new_with_config(2, config); + assert_eq!( + net.peer(0).sync.read().status().state, + SyncState::WaitingPeers + ); } #[test] fn status_packet() { - let mut net = TestNet::new(2); - net.peer(0).chain.add_blocks(100, EachBlockWith::Uncle); - net.peer(1).chain.add_blocks(1, EachBlockWith::Uncle); + let mut net = TestNet::new(2); + net.peer(0).chain.add_blocks(100, EachBlockWith::Uncle); + net.peer(1).chain.add_blocks(1, EachBlockWith::Uncle); - net.start(); + net.start(); - net.sync_step_peer(0); + net.sync_step_peer(0); - assert_eq!(1, net.peer(0).queue.read().len()); - assert_eq!(0x00, net.peer(0).queue.read()[0].packet_id); + assert_eq!(1, net.peer(0).queue.read().len()); + assert_eq!(0x00, net.peer(0).queue.read()[0].packet_id); } #[test] fn propagate_hashes() { - let mut net = TestNet::new(6); - net.peer(1).chain.add_blocks(10, EachBlockWith::Uncle); - net.sync(); - - net.peer(0).chain.add_blocks(10, EachBlockWith::Uncle); - net.sync(); - net.trigger_chain_new_blocks(0); //first event just sets the marker - net.trigger_chain_new_blocks(0); - - // 5 peers with NewHahses, 4 with blocks - assert_eq!(9, net.peer(0).queue.read().len()); - let mut hashes = 0; - let mut blocks = 0; - for i in 0..net.peer(0).queue.read().len() { - if net.peer(0).queue.read()[i].packet_id == 0x1 { - hashes += 1; - } - if net.peer(0).queue.read()[i].packet_id == 0x7 { - blocks += 1; - } - } - assert_eq!(blocks, 4); - assert_eq!(hashes, 5); + let mut net = TestNet::new(6); + net.peer(1).chain.add_blocks(10, EachBlockWith::Uncle); + net.sync(); + + net.peer(0).chain.add_blocks(10, EachBlockWith::Uncle); + net.sync(); + net.trigger_chain_new_blocks(0); //first event just sets the marker + net.trigger_chain_new_blocks(0); + + // 5 peers with NewHahses, 4 with blocks + assert_eq!(9, net.peer(0).queue.read().len()); + let mut hashes = 0; + let mut blocks = 0; + for i in 0..net.peer(0).queue.read().len() { + if net.peer(0).queue.read()[i].packet_id == 0x1 { + hashes += 1; + } + if net.peer(0).queue.read()[i].packet_id == 0x7 { + blocks += 1; + } + } + assert_eq!(blocks, 4); + assert_eq!(hashes, 5); } #[test] fn propagate_blocks() { - let mut net = TestNet::new(20); - net.peer(1).chain.add_blocks(10, EachBlockWith::Uncle); - net.sync(); - - net.peer(0).chain.add_blocks(10, EachBlockWith::Uncle); - net.trigger_chain_new_blocks(0); //first event just sets the marker - net.trigger_chain_new_blocks(0); - - assert!(!net.peer(0).queue.read().is_empty()); - // NEW_BLOCK_PACKET - let blocks = net.peer(0).queue.read().iter().filter(|p| p.packet_id == 0x7).count(); - assert!(blocks > 0); + let mut net = TestNet::new(20); + net.peer(1).chain.add_blocks(10, EachBlockWith::Uncle); + net.sync(); + + net.peer(0).chain.add_blocks(10, EachBlockWith::Uncle); + net.trigger_chain_new_blocks(0); //first event just sets the marker + net.trigger_chain_new_blocks(0); + + assert!(!net.peer(0).queue.read().is_empty()); + // NEW_BLOCK_PACKET + let blocks = net + .peer(0) + .queue + .read() + .iter() + .filter(|p| p.packet_id == 0x7) + .count(); + assert!(blocks > 0); } #[test] fn restart_on_malformed_block() { - ::env_logger::try_init().ok(); - let mut net = TestNet::new(2); - net.peer(1).chain.add_blocks(5, EachBlockWith::Nothing); - net.peer(1).chain.add_block(EachBlockWith::Nothing, |mut header| { - header.set_extra_data(b"This extra data is way too long to be considered valid".to_vec()); - header - }); - net.sync_steps(20); - - // This gets accepted just fine since the TestBlockChainClient performs no validation. - // Probably remove this test? - assert_eq!(net.peer(0).chain.chain_info().best_block_number, 6); + ::env_logger::try_init().ok(); + let mut net = TestNet::new(2); + net.peer(1).chain.add_blocks(5, EachBlockWith::Nothing); + net.peer(1) + .chain + .add_block(EachBlockWith::Nothing, |mut header| { + header + .set_extra_data(b"This extra data is way too long to be considered valid".to_vec()); + header + }); + net.sync_steps(20); + + // This gets accepted just fine since the TestBlockChainClient performs no validation. + // Probably remove this test? + assert_eq!(net.peer(0).chain.chain_info().best_block_number, 6); } #[test] fn reject_on_broken_chain() { - let mut net = TestNet::new(2); - net.peer(1).chain.add_blocks(10, EachBlockWith::Nothing); - net.peer(1).chain.corrupt_block_parent(6); - net.sync_steps(20); + let mut net = TestNet::new(2); + net.peer(1).chain.add_blocks(10, EachBlockWith::Nothing); + net.peer(1).chain.corrupt_block_parent(6); + net.sync_steps(20); - assert_eq!(net.peer(0).chain.chain_info().best_block_number, 0); + assert_eq!(net.peer(0).chain.chain_info().best_block_number, 0); } #[test] fn disconnect_on_unrelated_chain() { - ::env_logger::try_init().ok(); - let mut net = TestNet::new(2); - net.peer(0).chain.set_history(Some(20)); - net.peer(1).chain.set_history(Some(20)); - net.restart_peer(0); - net.restart_peer(1); - net.peer(0).chain.add_blocks(500, EachBlockWith::Uncle); - net.peer(1).chain.add_blocks(300, EachBlockWith::Nothing); - net.sync(); - assert_eq!(net.disconnect_events, vec![(0, 0)]); + ::env_logger::try_init().ok(); + let mut net = TestNet::new(2); + net.peer(0).chain.set_history(Some(20)); + net.peer(1).chain.set_history(Some(20)); + net.restart_peer(0); + net.restart_peer(1); + net.peer(0).chain.add_blocks(500, EachBlockWith::Uncle); + net.peer(1).chain.add_blocks(300, EachBlockWith::Nothing); + net.sync(); + assert_eq!(net.disconnect_events, vec![(0, 0)]); } diff --git a/ethcore/sync/src/tests/consensus.rs b/ethcore/sync/src/tests/consensus.rs index df093663383..089c7c58591 100644 --- a/ethcore/sync/src/tests/consensus.rs +++ b/ethcore/sync/src/tests/consensus.rs @@ -1,124 +1,174 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::sync::Arc; -use hash::keccak; -use ethereum_types::{U256, Address}; -use io::{IoHandler, IoChannel}; -use ethcore::client::{ChainInfo, ClientIoMessage}; -use ethcore::engines; -use ethcore::spec::Spec; -use ethcore::miner::{self, MinerService}; +use super::helpers::*; +use ethcore::{ + client::{ChainInfo, ClientIoMessage}, + engines, + miner::{self, MinerService}, + spec::Spec, +}; +use ethereum_types::{Address, U256}; use ethkey::{KeyPair, Secret}; +use hash::keccak; +use io::{IoChannel, IoHandler}; +use std::sync::Arc; use types::transaction::{Action, PendingTransaction, Transaction}; -use super::helpers::*; use SyncConfig; fn new_tx(secret: &Secret, nonce: U256, chain_id: u64) -> PendingTransaction { - let signed = Transaction { - nonce: nonce.into(), - gas_price: 0.into(), - gas: 21000.into(), - action: Action::Call(Address::default()), - value: 0.into(), - data: Vec::new(), - }.sign(secret, Some(chain_id)); - PendingTransaction::new(signed, None) + let signed = Transaction { + nonce: nonce.into(), + gas_price: 0.into(), + gas: 21000.into(), + action: Action::Call(Address::default()), + value: 0.into(), + data: Vec::new(), + } + .sign(secret, Some(chain_id)); + PendingTransaction::new(signed, None) } #[test] fn authority_round() { - let s0 = KeyPair::from_secret_slice(&keccak("1")).unwrap(); - let s1 = KeyPair::from_secret_slice(&keccak("0")).unwrap(); + let s0 = KeyPair::from_secret_slice(&keccak("1")).unwrap(); + let s1 = KeyPair::from_secret_slice(&keccak("0")).unwrap(); - let chain_id = Spec::new_test_round().chain_id(); - let mut net = TestNet::with_spec(2, SyncConfig::default(), Spec::new_test_round); - let io_handler0: Arc> = Arc::new(TestIoHandler::new(net.peer(0).chain.clone())); - let io_handler1: Arc> = Arc::new(TestIoHandler::new(net.peer(1).chain.clone())); - // Push transaction to both clients. Only one of them gets lucky to produce a block. - net.peer(0).miner.set_author(miner::Author::Sealer(engines::signer::from_keypair(s0.clone()))); - net.peer(1).miner.set_author(miner::Author::Sealer(engines::signer::from_keypair(s1.clone()))); - net.peer(0).chain.engine().register_client(Arc::downgrade(&net.peer(0).chain) as _); - net.peer(1).chain.engine().register_client(Arc::downgrade(&net.peer(1).chain) as _); - net.peer(0).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler1))); - net.peer(1).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler0))); - // exchange statuses - net.sync(); - // Trigger block proposal - net.peer(0).miner.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 0.into(), chain_id)).unwrap(); - net.peer(1).miner.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 0.into(), chain_id)).unwrap(); - // Sync a block - net.sync(); - assert_eq!(net.peer(0).chain.chain_info().best_block_number, 1); - assert_eq!(net.peer(1).chain.chain_info().best_block_number, 1); + let chain_id = Spec::new_test_round().chain_id(); + let mut net = TestNet::with_spec(2, SyncConfig::default(), Spec::new_test_round); + let io_handler0: Arc> = + Arc::new(TestIoHandler::new(net.peer(0).chain.clone())); + let io_handler1: Arc> = + Arc::new(TestIoHandler::new(net.peer(1).chain.clone())); + // Push transaction to both clients. Only one of them gets lucky to produce a block. + net.peer(0) + .miner + .set_author(miner::Author::Sealer(engines::signer::from_keypair( + s0.clone(), + ))); + net.peer(1) + .miner + .set_author(miner::Author::Sealer(engines::signer::from_keypair( + s1.clone(), + ))); + net.peer(0) + .chain + .engine() + .register_client(Arc::downgrade(&net.peer(0).chain) as _); + net.peer(1) + .chain + .engine() + .register_client(Arc::downgrade(&net.peer(1).chain) as _); + net.peer(0) + .chain + .set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler1))); + net.peer(1) + .chain + .set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler0))); + // exchange statuses + net.sync(); + // Trigger block proposal + net.peer(0) + .miner + .import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 0.into(), chain_id)) + .unwrap(); + net.peer(1) + .miner + .import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 0.into(), chain_id)) + .unwrap(); + // Sync a block + net.sync(); + assert_eq!(net.peer(0).chain.chain_info().best_block_number, 1); + assert_eq!(net.peer(1).chain.chain_info().best_block_number, 1); - net.peer(0).miner.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 1.into(), chain_id)).unwrap(); - net.peer(1).miner.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 1.into(), chain_id)).unwrap(); - // Move to next proposer step. - net.peer(0).chain.engine().step(); - net.peer(1).chain.engine().step(); - net.sync(); - assert_eq!(net.peer(0).chain.chain_info().best_block_number, 2); - assert_eq!(net.peer(1).chain.chain_info().best_block_number, 2); + net.peer(0) + .miner + .import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 1.into(), chain_id)) + .unwrap(); + net.peer(1) + .miner + .import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 1.into(), chain_id)) + .unwrap(); + // Move to next proposer step. + net.peer(0).chain.engine().step(); + net.peer(1).chain.engine().step(); + net.sync(); + assert_eq!(net.peer(0).chain.chain_info().best_block_number, 2); + assert_eq!(net.peer(1).chain.chain_info().best_block_number, 2); - // Fork the network with equal height. - net.peer(0).miner.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 2.into(), chain_id)).unwrap(); - net.peer(1).miner.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 2.into(), chain_id)).unwrap(); - // Let both nodes build one block. - net.peer(0).chain.engine().step(); - let early_hash = net.peer(0).chain.chain_info().best_block_hash; - net.peer(1).chain.engine().step(); - net.peer(0).chain.engine().step(); - net.peer(1).chain.engine().step(); - let ci0 = net.peer(0).chain.chain_info(); - let ci1 = net.peer(1).chain.chain_info(); - assert_eq!(ci0.best_block_number, 3); - assert_eq!(ci1.best_block_number, 3); - assert!(ci0.best_block_hash != ci1.best_block_hash); - // Reorg to the chain with earlier view. - net.sync(); - let ci0 = net.peer(0).chain.chain_info(); - let ci1 = net.peer(1).chain.chain_info(); - assert_eq!(ci0.best_block_number, 3); - assert_eq!(ci1.best_block_number, 3); - assert_eq!(ci0.best_block_hash, ci1.best_block_hash); - assert_eq!(ci1.best_block_hash, early_hash); + // Fork the network with equal height. + net.peer(0) + .miner + .import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 2.into(), chain_id)) + .unwrap(); + net.peer(1) + .miner + .import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 2.into(), chain_id)) + .unwrap(); + // Let both nodes build one block. + net.peer(0).chain.engine().step(); + let early_hash = net.peer(0).chain.chain_info().best_block_hash; + net.peer(1).chain.engine().step(); + net.peer(0).chain.engine().step(); + net.peer(1).chain.engine().step(); + let ci0 = net.peer(0).chain.chain_info(); + let ci1 = net.peer(1).chain.chain_info(); + assert_eq!(ci0.best_block_number, 3); + assert_eq!(ci1.best_block_number, 3); + assert!(ci0.best_block_hash != ci1.best_block_hash); + // Reorg to the chain with earlier view. + net.sync(); + let ci0 = net.peer(0).chain.chain_info(); + let ci1 = net.peer(1).chain.chain_info(); + assert_eq!(ci0.best_block_number, 3); + assert_eq!(ci1.best_block_number, 3); + assert_eq!(ci0.best_block_hash, ci1.best_block_hash); + assert_eq!(ci1.best_block_hash, early_hash); - // Selfish miner - net.peer(0).miner.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 3.into(), chain_id)).unwrap(); - net.peer(1).miner.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 3.into(), chain_id)).unwrap(); - // Node 0 is an earlier primary. - net.peer(0).chain.engine().step(); - assert_eq!(net.peer(0).chain.chain_info().best_block_number, 4); - net.peer(0).chain.engine().step(); - net.peer(0).chain.engine().step(); - net.peer(0).chain.engine().step(); - assert_eq!(net.peer(0).chain.chain_info().best_block_number, 4); - // Node 1 makes 2 blocks, but is a later primary on the first one. - net.peer(1).chain.engine().step(); - net.peer(1).chain.engine().step(); - net.peer(1).miner.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 4.into(), chain_id)).unwrap(); - net.peer(1).chain.engine().step(); - net.peer(1).chain.engine().step(); - assert_eq!(net.peer(1).chain.chain_info().best_block_number, 5); - // Reorg to the longest chain one not ealier view one. - net.sync(); - let ci0 = net.peer(0).chain.chain_info(); - let ci1 = net.peer(1).chain.chain_info(); - assert_eq!(ci0.best_block_number, 5); - assert_eq!(ci1.best_block_number, 5); - assert_eq!(ci0.best_block_hash, ci1.best_block_hash); + // Selfish miner + net.peer(0) + .miner + .import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 3.into(), chain_id)) + .unwrap(); + net.peer(1) + .miner + .import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 3.into(), chain_id)) + .unwrap(); + // Node 0 is an earlier primary. + net.peer(0).chain.engine().step(); + assert_eq!(net.peer(0).chain.chain_info().best_block_number, 4); + net.peer(0).chain.engine().step(); + net.peer(0).chain.engine().step(); + net.peer(0).chain.engine().step(); + assert_eq!(net.peer(0).chain.chain_info().best_block_number, 4); + // Node 1 makes 2 blocks, but is a later primary on the first one. + net.peer(1).chain.engine().step(); + net.peer(1).chain.engine().step(); + net.peer(1) + .miner + .import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 4.into(), chain_id)) + .unwrap(); + net.peer(1).chain.engine().step(); + net.peer(1).chain.engine().step(); + assert_eq!(net.peer(1).chain.chain_info().best_block_number, 5); + // Reorg to the longest chain one not ealier view one. + net.sync(); + let ci0 = net.peer(0).chain.chain_info(); + let ci1 = net.peer(1).chain.chain_info(); + assert_eq!(ci0.best_block_number, 5); + assert_eq!(ci1.best_block_number, 5); + assert_eq!(ci0.best_block_hash, ci1.best_block_hash); } diff --git a/ethcore/sync/src/tests/helpers.rs b/ethcore/sync/src/tests/helpers.rs index 8bc4b542e28..264754f1e06 100644 --- a/ethcore/sync/src/tests/helpers.rs +++ b/ethcore/sync/src/tests/helpers.rs @@ -1,563 +1,626 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::collections::{VecDeque, HashSet, HashMap}; -use std::sync::Arc; -use ethereum_types::H256; -use parking_lot::{RwLock, Mutex}; +use api::PAR_PROTOCOL; use bytes::Bytes; -use network::{self, PeerId, ProtocolId, PacketId, SessionInfo}; -use network::client_version::ClientVersion; -use tests::snapshot::*; -use ethcore::client::{TestBlockChainClient, BlockChainClient, Client as EthcoreClient, - ClientConfig, ChainNotify, NewBlocks, ChainMessageType, ClientIoMessage}; -use ethcore::snapshot::SnapshotService; -use ethcore::spec::Spec; -use ethcore::miner::Miner; -use ethcore::test_helpers; -use sync_io::SyncIo; +use chain::{ + sync_packet::{PacketInfo, SyncPacket}, + ChainSync, ForkFilterApi, SyncSupplier, ETH_PROTOCOL_VERSION_64, PAR_PROTOCOL_VERSION_2, +}; +use ethcore::{ + client::{ + BlockChainClient, ChainMessageType, ChainNotify, Client as EthcoreClient, ClientConfig, + ClientIoMessage, NewBlocks, TestBlockChainClient, + }, + miner::Miner, + snapshot::SnapshotService, + spec::Spec, + test_helpers, +}; + +use ethereum_types::H256; use io::{IoChannel, IoContext, IoHandler}; -use api::WARP_SYNC_PROTOCOL_ID; -use chain::{ChainSync, SyncSupplier, ETH_PROTOCOL_VERSION_63, PAR_PROTOCOL_VERSION_3}; -use chain::sync_packet::{PacketInfo, SyncPacket}; -use chain::sync_packet::SyncPacket::{PrivateTransactionPacket, SignedPrivateTransactionPacket}; +use network::{self, client_version::ClientVersion, PacketId, PeerId, ProtocolId, SessionInfo}; +use parking_lot::RwLock; +use std::{ + collections::{HashMap, HashSet, VecDeque}, + sync::Arc, +}; +use sync_io::SyncIo; +use tests::snapshot::*; -use SyncConfig; -use private_tx::SimplePrivateTxHandler; use types::BlockNumber; +use SyncConfig; pub trait FlushingBlockChainClient: BlockChainClient { - fn flush(&self) {} + fn flush(&self) {} } impl FlushingBlockChainClient for EthcoreClient { - fn flush(&self) { - self.flush_queue(); - } + fn flush(&self) { + self.flush_queue(); + } } impl FlushingBlockChainClient for TestBlockChainClient {} -pub struct TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { - pub chain: &'p C, - pub snapshot_service: &'p TestSnapshotService, - pub queue: &'p RwLock>, - pub sender: Option, - pub to_disconnect: HashSet, - pub packets: Vec, - pub peers_info: HashMap, - overlay: RwLock>, +pub struct TestIo<'p, C> +where + C: FlushingBlockChainClient, + C: 'p, +{ + pub chain: &'p C, + pub snapshot_service: &'p TestSnapshotService, + pub queue: &'p RwLock>, + pub sender: Option, + pub to_disconnect: HashSet, + pub packets: Vec, + pub peers_info: HashMap, + overlay: RwLock>, } -impl<'p, C> TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { - pub fn new(chain: &'p C, ss: &'p TestSnapshotService, queue: &'p RwLock>, sender: Option) -> TestIo<'p, C> { - TestIo { - chain: chain, - snapshot_service: ss, - queue: queue, - sender: sender, - to_disconnect: HashSet::new(), - overlay: RwLock::new(HashMap::new()), - packets: Vec::new(), - peers_info: HashMap::new(), - } - } +impl<'p, C> TestIo<'p, C> +where + C: FlushingBlockChainClient, + C: 'p, +{ + pub fn new( + chain: &'p C, + ss: &'p TestSnapshotService, + queue: &'p RwLock>, + sender: Option, + ) -> TestIo<'p, C> { + TestIo { + chain: chain, + snapshot_service: ss, + queue: queue, + sender: sender, + to_disconnect: HashSet::new(), + overlay: RwLock::new(HashMap::new()), + packets: Vec::new(), + peers_info: HashMap::new(), + } + } } -impl<'p, C> Drop for TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { - fn drop(&mut self) { - self.queue.write().extend(self.packets.drain(..)); - } +impl<'p, C> Drop for TestIo<'p, C> +where + C: FlushingBlockChainClient, + C: 'p, +{ + fn drop(&mut self) { + self.queue.write().extend(self.packets.drain(..)); + } } -impl<'p, C> SyncIo for TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { - fn disable_peer(&mut self, peer_id: PeerId) { - self.disconnect_peer(peer_id); - } - - fn disconnect_peer(&mut self, peer_id: PeerId) { - self.to_disconnect.insert(peer_id); - } - - fn is_expired(&self) -> bool { - false - } - - fn respond(&mut self, packet_id: PacketId, data: Vec) -> Result<(), network::Error> { - self.packets.push(TestPacket { - data: data, - packet_id: packet_id, - recipient: self.sender.unwrap() - }); - Ok(()) - } - - fn send(&mut self,peer_id: PeerId, packet_id: SyncPacket, data: Vec) -> Result<(), network::Error> { - self.packets.push(TestPacket { - data: data, - packet_id: packet_id.id(), - recipient: peer_id, - }); - Ok(()) - } - - fn chain(&self) -> &BlockChainClient { - &*self.chain - } - - fn peer_version(&self, peer_id: PeerId) -> ClientVersion { - let client_id = self.peers_info.get(&peer_id) - .cloned() - .unwrap_or_else(|| peer_id.to_string()); - - ClientVersion::from(client_id) - } - - fn snapshot_service(&self) -> &SnapshotService { - self.snapshot_service - } - - fn peer_session_info(&self, _peer_id: PeerId) -> Option { - None - } - - fn eth_protocol_version(&self, _peer: PeerId) -> u8 { - ETH_PROTOCOL_VERSION_63.0 - } - - fn protocol_version(&self, protocol: &ProtocolId, peer_id: PeerId) -> u8 { - if protocol == &WARP_SYNC_PROTOCOL_ID { PAR_PROTOCOL_VERSION_3.0 } else { self.eth_protocol_version(peer_id) } - } - - fn chain_overlay(&self) -> &RwLock> { - &self.overlay - } - - fn payload_soft_limit(&self) -> usize { - 100_000 - } +impl<'p, C> SyncIo for TestIo<'p, C> +where + C: FlushingBlockChainClient, + C: 'p, +{ + fn disable_peer(&mut self, peer_id: PeerId) { + self.disconnect_peer(peer_id); + } + + fn disconnect_peer(&mut self, peer_id: PeerId) { + self.to_disconnect.insert(peer_id); + } + + fn is_expired(&self) -> bool { + false + } + + fn respond(&mut self, packet_id: PacketId, data: Vec) -> Result<(), network::Error> { + self.packets.push(TestPacket { + data: data, + packet_id: packet_id, + recipient: self.sender.unwrap(), + }); + Ok(()) + } + + fn send( + &mut self, + peer_id: PeerId, + packet_id: SyncPacket, + data: Vec, + ) -> Result<(), network::Error> { + self.packets.push(TestPacket { + data: data, + packet_id: packet_id.id(), + recipient: peer_id, + }); + Ok(()) + } + + fn chain(&self) -> &dyn BlockChainClient { + &*self.chain + } + + fn peer_version(&self, peer_id: PeerId) -> ClientVersion { + let client_id = self + .peers_info + .get(&peer_id) + .cloned() + .unwrap_or_else(|| peer_id.to_string()); + + ClientVersion::from(client_id) + } + + fn snapshot_service(&self) -> &dyn SnapshotService { + self.snapshot_service + } + + fn peer_session_info(&self, _peer_id: PeerId) -> Option { + None + } + + fn eth_protocol_version(&self, _peer: PeerId) -> u8 { + ETH_PROTOCOL_VERSION_64.0 + } + + fn protocol_version(&self, protocol: &ProtocolId, peer_id: PeerId) -> u8 { + if protocol == &PAR_PROTOCOL { + PAR_PROTOCOL_VERSION_2.0 + } else { + self.eth_protocol_version(peer_id) + } + } + + fn chain_overlay(&self) -> &RwLock> { + &self.overlay + } + + fn payload_soft_limit(&self) -> usize { + 100_000 + } } /// Mock for emulution of async run of new blocks struct NewBlockMessage { - imported: Vec, - invalid: Vec, - enacted: Vec, - retracted: Vec, - sealed: Vec, - proposed: Vec, + imported: Vec, + invalid: Vec, + enacted: Vec, + retracted: Vec, + sealed: Vec, + proposed: Vec, } /// Abstract messages between peers. pub trait Message { - /// The intended recipient of this message. - fn recipient(&self) -> PeerId; + /// The intended recipient of this message. + fn recipient(&self) -> PeerId; } /// Mock subprotocol packet pub struct TestPacket { - pub data: Bytes, - pub packet_id: PacketId, - pub recipient: PeerId, + pub data: Bytes, + pub packet_id: PacketId, + pub recipient: PeerId, } impl Message for TestPacket { - fn recipient(&self) -> PeerId { self.recipient } + fn recipient(&self) -> PeerId { + self.recipient + } } /// A peer which can be a member of the `TestNet`. pub trait Peer { - type Message: Message; + type Message: Message; - /// Called on connection to other indicated peer. - fn on_connect(&self, other: PeerId); + /// Called on connection to other indicated peer. + fn on_connect(&self, other: PeerId); - /// Called on disconnect from other indicated peer. - fn on_disconnect(&self, other: PeerId); + /// Called on disconnect from other indicated peer. + fn on_disconnect(&self, other: PeerId); - /// Receive a message from another peer. Return a set of peers to disconnect. - fn receive_message(&self, from: PeerId, msg: Self::Message) -> HashSet; + /// Receive a message from another peer. Return a set of peers to disconnect. + fn receive_message(&self, from: PeerId, msg: Self::Message) -> HashSet; - /// Produce the next pending message to send to another peer. - fn pending_message(&self) -> Option; + /// Produce the next pending message to send to another peer. + fn pending_message(&self) -> Option; - /// Whether this peer is done syncing (has no messages to send). - fn is_done(&self) -> bool; + /// Whether this peer is done syncing (has no messages to send). + fn is_done(&self) -> bool; - /// Execute a "sync step". This is called for each peer after it sends a packet. - fn sync_step(&self); + /// Execute a "sync step". This is called for each peer after it sends a packet. + fn sync_step(&self); - /// Restart sync for a peer. - fn restart_sync(&self); + /// Restart sync for a peer. + fn restart_sync(&self); - /// Process the queue of pending io messages - fn process_all_io_messages(&self); + /// Process the queue of pending io messages + fn process_all_io_messages(&self); - /// Process the queue of new block messages - fn process_all_new_block_messages(&self); + /// Process the queue of new block messages + fn process_all_new_block_messages(&self); } -pub struct EthPeer where C: FlushingBlockChainClient { - pub chain: Arc, - pub miner: Arc, - pub snapshot_service: Arc, - pub sync: RwLock, - pub queue: RwLock>, - pub private_tx_handler: Arc, - pub io_queue: RwLock>, - new_blocks_queue: RwLock>, +pub struct EthPeer +where + C: FlushingBlockChainClient, +{ + pub chain: Arc, + pub miner: Arc, + pub snapshot_service: Arc, + pub sync: RwLock, + pub queue: RwLock>, + pub io_queue: RwLock>, + new_blocks_queue: RwLock>, } -impl EthPeer where C: FlushingBlockChainClient { - fn is_io_queue_empty(&self) -> bool { - self.io_queue.read().is_empty() - } - - fn is_new_blocks_queue_empty(&self) -> bool { - self.new_blocks_queue.read().is_empty() - } - - fn process_io_message(&self, message: ChainMessageType) { - let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None); - match message { - ChainMessageType::Consensus(data) => self.sync.write().propagate_consensus_packet(&mut io, data), - ChainMessageType::PrivateTransaction(transaction_hash, data) => - self.sync.write().propagate_private_transaction(&mut io, transaction_hash, PrivateTransactionPacket, data), - ChainMessageType::SignedPrivateTransaction(transaction_hash, data) => - self.sync.write().propagate_private_transaction(&mut io, transaction_hash, SignedPrivateTransactionPacket, data), - } - } - - fn process_new_block_message(&self, message: NewBlockMessage) { - let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None); - self.sync.write().chain_new_blocks( - &mut io, - &message.imported, - &message.invalid, - &message.enacted, - &message.retracted, - &message.sealed, - &message.proposed - ); - } +impl EthPeer +where + C: FlushingBlockChainClient, +{ + fn is_io_queue_empty(&self) -> bool { + self.io_queue.read().is_empty() + } + + fn is_new_blocks_queue_empty(&self) -> bool { + self.new_blocks_queue.read().is_empty() + } + + fn process_io_message(&self, message: ChainMessageType) { + let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None); + match message { + ChainMessageType::Consensus(data) => { + self.sync.write().propagate_consensus_packet(&mut io, data) + } + } + } + + fn process_new_block_message(&self, message: NewBlockMessage) { + let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None); + self.sync.write().chain_new_blocks( + &mut io, + &message.imported, + &message.invalid, + &message.enacted, + &message.retracted, + &message.sealed, + &message.proposed, + ); + } } impl Peer for EthPeer { - type Message = TestPacket; - - fn on_connect(&self, other: PeerId) { - self.sync.write().update_targets(&*self.chain); - self.sync.write().on_peer_connected(&mut TestIo::new( - &*self.chain, - &self.snapshot_service, - &self.queue, - Some(other)), - other); - } - - fn on_disconnect(&self, other: PeerId) { - let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, Some(other)); - self.sync.write().on_peer_aborting(&mut io, other); - } - - fn receive_message(&self, from: PeerId, msg: TestPacket) -> HashSet { - let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, Some(from)); - SyncSupplier::dispatch_packet(&self.sync, &mut io, from, msg.packet_id, &msg.data); - self.chain.flush(); - io.to_disconnect.clone() - } - - fn pending_message(&self) -> Option { - self.chain.flush(); - self.queue.write().pop_front() - } - - fn is_done(&self) -> bool { - self.queue.read().is_empty() && self.is_io_queue_empty() && self.is_new_blocks_queue_empty() - } - - fn sync_step(&self) { - let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None); - self.chain.flush(); - self.sync.write().maintain_peers(&mut io); - self.sync.write().maintain_sync(&mut io); - self.sync.write().continue_sync(&mut io); - self.sync.write().propagate_new_transactions(&mut io); - } - - fn restart_sync(&self) { - self.sync.write().restart(&mut TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None)); - } - - fn process_all_io_messages(&self) { - if !self.is_io_queue_empty() { - while let Some(message) = self.io_queue.write().pop_front() { - self.process_io_message(message); - } - } - } - - fn process_all_new_block_messages(&self) { - if !self.is_new_blocks_queue_empty() { - while let Some(message) = self.new_blocks_queue.write().pop_front() { - self.process_new_block_message(message); - } - } - } + type Message = TestPacket; + + fn on_connect(&self, other: PeerId) { + self.sync.write().update_targets(&*self.chain); + self.sync.write().on_peer_connected( + &mut TestIo::new( + &*self.chain, + &self.snapshot_service, + &self.queue, + Some(other), + ), + other, + ); + } + + fn on_disconnect(&self, other: PeerId) { + let mut io = TestIo::new( + &*self.chain, + &self.snapshot_service, + &self.queue, + Some(other), + ); + self.sync.write().on_peer_aborting(&mut io, other); + } + + fn receive_message(&self, from: PeerId, msg: TestPacket) -> HashSet { + let mut io = TestIo::new( + &*self.chain, + &self.snapshot_service, + &self.queue, + Some(from), + ); + SyncSupplier::dispatch_packet(&self.sync, &mut io, from, msg.packet_id, &msg.data); + self.chain.flush(); + io.to_disconnect.clone() + } + + fn pending_message(&self) -> Option { + self.chain.flush(); + self.queue.write().pop_front() + } + + fn is_done(&self) -> bool { + self.queue.read().is_empty() && self.is_io_queue_empty() && self.is_new_blocks_queue_empty() + } + + fn sync_step(&self) { + let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None); + self.chain.flush(); + self.sync.write().maintain_peers(&mut io); + self.sync.write().maintain_sync(&mut io); + self.sync.write().continue_sync(&mut io); + self.sync.write().propagate_new_transactions(&mut io); + } + + fn restart_sync(&self) { + self.sync.write().restart(&mut TestIo::new( + &*self.chain, + &self.snapshot_service, + &self.queue, + None, + )); + } + + fn process_all_io_messages(&self) { + if !self.is_io_queue_empty() { + while let Some(message) = self.io_queue.write().pop_front() { + self.process_io_message(message); + } + } + } + + fn process_all_new_block_messages(&self) { + if !self.is_new_blocks_queue_empty() { + while let Some(message) = self.new_blocks_queue.write().pop_front() { + self.process_new_block_message(message); + } + } + } } pub struct TestNet

{ - pub peers: Vec>, - pub started: bool, - pub disconnect_events: Vec<(PeerId, PeerId)>, //disconnected (initiated by, to) + pub peers: Vec>, + pub started: bool, + pub disconnect_events: Vec<(PeerId, PeerId)>, //disconnected (initiated by, to) } impl TestNet> { - pub fn new(n: usize) -> Self { - Self::new_with_config(n, SyncConfig::default()) - } - - pub fn new_with_fork(n: usize, fork: Option<(BlockNumber, H256)>) -> Self { - let mut config = SyncConfig::default(); - config.fork_block = fork; - Self::new_with_config(n, config) - } - - pub fn new_with_config(n: usize, config: SyncConfig) -> Self { - let mut net = TestNet { - peers: Vec::new(), - started: false, - disconnect_events: Vec::new(), - }; - for _ in 0..n { - let chain = TestBlockChainClient::new(); - let ss = Arc::new(TestSnapshotService::new()); - let private_tx_handler = Arc::new(SimplePrivateTxHandler::default()); - let sync = ChainSync::new(config.clone(), &chain, Some(private_tx_handler.clone())); - net.peers.push(Arc::new(EthPeer { - sync: RwLock::new(sync), - snapshot_service: ss, - chain: Arc::new(chain), - miner: Arc::new(Miner::new_for_tests(&Spec::new_test(), None)), - queue: RwLock::new(VecDeque::new()), - private_tx_handler, - io_queue: RwLock::new(VecDeque::new()), - new_blocks_queue: RwLock::new(VecDeque::new()), - })); - } - net - } - - // relies on Arc uniqueness, which is only true when we haven't registered a ChainNotify. - pub fn peer_mut(&mut self, i: usize) -> &mut EthPeer { - Arc::get_mut(&mut self.peers[i]).expect("Arc never exposed externally") - } + pub fn new(n: usize) -> Self { + Self::new_with_config(n, SyncConfig::default()) + } + + pub fn new_with_fork(n: usize, fork: Option<(BlockNumber, H256)>) -> Self { + let mut config = SyncConfig::default(); + config.fork_block = fork; + Self::new_with_config(n, config) + } + + pub fn new_with_config(n: usize, config: SyncConfig) -> Self { + let mut net = TestNet { + peers: Vec::new(), + started: false, + disconnect_events: Vec::new(), + }; + for _ in 0..n { + let chain = TestBlockChainClient::new(); + let ss = Arc::new(TestSnapshotService::new()); + let sync = ChainSync::new(config.clone(), &chain, ForkFilterApi::new_dummy(&chain)); + net.peers.push(Arc::new(EthPeer { + sync: RwLock::new(sync), + snapshot_service: ss, + chain: Arc::new(chain), + miner: Arc::new(Miner::new_for_tests(&Spec::new_test(), None)), + queue: RwLock::new(VecDeque::new()), + io_queue: RwLock::new(VecDeque::new()), + new_blocks_queue: RwLock::new(VecDeque::new()), + })); + } + net + } + + // relies on Arc uniqueness, which is only true when we haven't registered a ChainNotify. + pub fn peer_mut(&mut self, i: usize) -> &mut EthPeer { + Arc::get_mut(&mut self.peers[i]).expect("Arc never exposed externally") + } } impl TestNet> { - pub fn with_spec( - n: usize, - config: SyncConfig, - spec_factory: F, - ) -> Self - where F: Fn() -> Spec - { - let mut net = TestNet { - peers: Vec::new(), - started: false, - disconnect_events: Vec::new(), - }; - for _ in 0..n { - net.add_peer_with_private_config(config.clone(), spec_factory()); - } - net - } - - pub fn add_peer_with_private_config(&mut self, config: SyncConfig, spec: Spec) { - let channel = IoChannel::disconnected(); - let miner = Arc::new(Miner::new_for_tests(&spec, None)); - let client = EthcoreClient::new( - ClientConfig::default(), - &spec, - test_helpers::new_db(), - miner.clone(), - channel.clone() - ).unwrap(); - - let private_tx_handler = Arc::new(SimplePrivateTxHandler::default()); - let ss = Arc::new(TestSnapshotService::new()); - let sync = ChainSync::new(config, &*client, Some(private_tx_handler.clone())); - let peer = Arc::new(EthPeer { - sync: RwLock::new(sync), - snapshot_service: ss, - chain: client, - miner, - queue: RwLock::new(VecDeque::new()), - private_tx_handler, - io_queue: RwLock::new(VecDeque::new()), - new_blocks_queue: RwLock::new(VecDeque::new()), - }); - peer.chain.add_notify(peer.clone()); - //private_provider.add_notify(peer.clone()); - self.peers.push(peer); - } + pub fn with_spec(n: usize, config: SyncConfig, spec_factory: F) -> Self + where + F: Fn() -> Spec, + { + let mut net = TestNet { + peers: Vec::new(), + started: false, + disconnect_events: Vec::new(), + }; + for _ in 0..n { + net.add_peer_with_private_config(config.clone(), spec_factory()); + } + net + } + + pub fn add_peer_with_private_config(&mut self, config: SyncConfig, spec: Spec) { + let channel = IoChannel::disconnected(); + let miner = Arc::new(Miner::new_for_tests(&spec, None)); + let client = EthcoreClient::new( + ClientConfig::default(), + &spec, + test_helpers::new_db(), + miner.clone(), + channel.clone(), + ) + .unwrap(); + + let ss = Arc::new(TestSnapshotService::new()); + let sync = ChainSync::new(config, &*client, ForkFilterApi::new_dummy(&*client)); + let peer = Arc::new(EthPeer { + sync: RwLock::new(sync), + snapshot_service: ss, + chain: client, + miner, + queue: RwLock::new(VecDeque::new()), + io_queue: RwLock::new(VecDeque::new()), + new_blocks_queue: RwLock::new(VecDeque::new()), + }); + peer.chain.add_notify(peer.clone()); + //private_provider.add_notify(peer.clone()); + self.peers.push(peer); + } } -impl

TestNet

where P: Peer { - pub fn peer(&self, i: usize) -> &P { - &self.peers[i] - } - - pub fn start(&mut self) { - if self.started { - return; - } - for peer in 0..self.peers.len() { - for client in 0..self.peers.len() { - if peer != client { - self.peers[peer].on_connect(client as PeerId); - } - } - } - self.started = true; - } - - pub fn sync_step(&mut self) { - for peer in 0..self.peers.len() { - let packet = self.peers[peer].pending_message(); - if let Some(packet) = packet { - let disconnecting = { - let recipient = packet.recipient(); - trace!("--- {} -> {} ---", peer, recipient); - let to_disconnect = self.peers[recipient].receive_message(peer as PeerId, packet); - for d in &to_disconnect { - // notify this that disconnecting peers are disconnecting - self.peers[recipient].on_disconnect(*d as PeerId); - self.disconnect_events.push((peer, *d)); - } - to_disconnect - }; - for d in &disconnecting { - // notify other peers that this peer is disconnecting - self.peers[*d].on_disconnect(peer as PeerId); - } - } - - self.sync_step_peer(peer); - } - } - - pub fn sync_step_peer(&mut self, peer_num: usize) { - self.peers[peer_num].sync_step(); - } - - pub fn restart_peer(&mut self, i: usize) { - self.peers[i].restart_sync(); - } - - pub fn sync(&mut self) -> u32 { - self.start(); - let mut total_steps = 0; - while !self.done() { - self.sync_step(); - self.deliver_io_messages(); - self.deliver_new_block_messages(); - total_steps += 1; - } - total_steps - } - - pub fn sync_steps(&mut self, count: usize) { - self.start(); - for _ in 0..count { - self.sync_step(); - } - } - - pub fn deliver_io_messages(&mut self) { - for peer in self.peers.iter() { - peer.process_all_io_messages(); - } - } - - pub fn deliver_new_block_messages(&mut self) { - for peer in self.peers.iter() { - peer.process_all_new_block_messages(); - } - } - - pub fn done(&self) -> bool { - self.peers.iter().all(|p| p.is_done()) - } +impl

TestNet

+where + P: Peer, +{ + pub fn peer(&self, i: usize) -> &P { + &self.peers[i] + } + + pub fn start(&mut self) { + if self.started { + return; + } + for peer in 0..self.peers.len() { + for client in 0..self.peers.len() { + if peer != client { + self.peers[peer].on_connect(client as PeerId); + } + } + } + self.started = true; + } + + pub fn sync_step(&mut self) { + for peer in 0..self.peers.len() { + let packet = self.peers[peer].pending_message(); + if let Some(packet) = packet { + let disconnecting = { + let recipient = packet.recipient(); + trace!("--- {} -> {} ---", peer, recipient); + let to_disconnect = + self.peers[recipient].receive_message(peer as PeerId, packet); + for d in &to_disconnect { + // notify this that disconnecting peers are disconnecting + self.peers[recipient].on_disconnect(*d as PeerId); + self.disconnect_events.push((peer, *d)); + } + to_disconnect + }; + for d in &disconnecting { + // notify other peers that this peer is disconnecting + self.peers[*d].on_disconnect(peer as PeerId); + } + } + + self.sync_step_peer(peer); + } + } + + pub fn sync_step_peer(&mut self, peer_num: usize) { + self.peers[peer_num].sync_step(); + } + + pub fn restart_peer(&mut self, i: usize) { + self.peers[i].restart_sync(); + } + + pub fn sync(&mut self) -> u32 { + self.start(); + let mut total_steps = 0; + while !self.done() { + self.sync_step(); + self.deliver_io_messages(); + self.deliver_new_block_messages(); + total_steps += 1; + } + total_steps + } + + pub fn sync_steps(&mut self, count: usize) { + self.start(); + for _ in 0..count { + self.sync_step(); + } + } + + pub fn deliver_io_messages(&mut self) { + for peer in self.peers.iter() { + peer.process_all_io_messages(); + } + } + + pub fn deliver_new_block_messages(&mut self) { + for peer in self.peers.iter() { + peer.process_all_new_block_messages(); + } + } + + pub fn done(&self) -> bool { + self.peers.iter().all(|p| p.is_done()) + } } impl TestNet> { - pub fn trigger_chain_new_blocks(&mut self, peer_id: usize) { - let peer = &mut self.peers[peer_id]; - peer.sync.write().chain_new_blocks(&mut TestIo::new(&*peer.chain, &peer.snapshot_service, &peer.queue, None), &[], &[], &[], &[], &[], &[]); - } + pub fn trigger_chain_new_blocks(&mut self, peer_id: usize) { + let peer = &mut self.peers[peer_id]; + peer.sync.write().chain_new_blocks( + &mut TestIo::new(&*peer.chain, &peer.snapshot_service, &peer.queue, None), + &[], + &[], + &[], + &[], + &[], + &[], + ); + } } pub struct TestIoHandler { - pub client: Arc, - pub private_tx_queued: Mutex, + pub client: Arc, } impl TestIoHandler { - pub fn new(client: Arc) -> Self { - TestIoHandler { - client, - private_tx_queued: Mutex::default(), - } - } + pub fn new(client: Arc) -> Self { + TestIoHandler { client } + } } impl IoHandler for TestIoHandler { - fn message(&self, _io: &IoContext, net_message: &ClientIoMessage) { - match *net_message { - ClientIoMessage::Execute(ref exec) => { - *self.private_tx_queued.lock() += 1; - (*exec.0)(&self.client); - }, - _ => {} // ignore other messages - } - } + fn message(&self, _io: &IoContext, net_message: &ClientIoMessage) { + match *net_message { + ClientIoMessage::Execute(ref exec) => { + (*exec.0)(&self.client); + } + _ => {} // ignore other messages + } + } } impl ChainNotify for EthPeer { - fn new_blocks(&self, new_blocks: NewBlocks) - { - if new_blocks.has_more_blocks_to_import { return } - let (enacted, retracted) = new_blocks.route.into_enacted_retracted(); - - self.new_blocks_queue.write().push_back(NewBlockMessage { - imported: new_blocks.imported, - invalid: new_blocks.invalid, - enacted, - retracted, - sealed: new_blocks.sealed, - proposed: new_blocks.proposed, - }); - } - - fn start(&self) {} - - fn stop(&self) {} - - fn broadcast(&self, message_type: ChainMessageType) { - self.io_queue.write().push_back(message_type) - } + fn new_blocks(&self, new_blocks: NewBlocks) { + if new_blocks.has_more_blocks_to_import { + return; + } + let (enacted, retracted) = new_blocks.route.into_enacted_retracted(); + + self.new_blocks_queue.write().push_back(NewBlockMessage { + imported: new_blocks.imported, + invalid: new_blocks.invalid, + enacted, + retracted, + sealed: new_blocks.sealed, + proposed: new_blocks.proposed, + }); + } + + fn start(&self) {} + + fn stop(&self) {} + + fn broadcast(&self, message_type: ChainMessageType) { + self.io_queue.write().push_back(message_type) + } } diff --git a/ethcore/sync/src/tests/mod.rs b/ethcore/sync/src/tests/mod.rs index 34e04d19607..cc6d4b357c1 100644 --- a/ethcore/sync/src/tests/mod.rs +++ b/ethcore/sync/src/tests/mod.rs @@ -1,24 +1,23 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -pub mod helpers; -pub mod snapshot; mod chain; mod consensus; -mod private; +pub mod helpers; +pub mod snapshot; #[cfg(feature = "ipc")] mod rpc; diff --git a/ethcore/sync/src/tests/private.rs b/ethcore/sync/src/tests/private.rs deleted file mode 100644 index 24de14d936d..00000000000 --- a/ethcore/sync/src/tests/private.rs +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use hash::keccak; -use io::{IoHandler, IoChannel}; -use types::transaction::{Transaction, Action}; -use types::ids::BlockId; -use ethcore::CreateContractAddress; -use ethcore::client::{ClientIoMessage, BlockChainClient}; -use ethcore::executive::{contract_address}; -use ethcore::engines; -use ethcore::miner::{self, MinerService}; -use ethcore::spec::Spec; -use ethcore::test_helpers::{push_block_with_transactions}; -use ethcore_private_tx::{Provider, ProviderConfig, NoopEncryptor, Importer, SignedPrivateTransaction, StoringKeyProvider}; -use ethkey::KeyPair; -use tests::helpers::{TestNet, TestIoHandler}; -use rustc_hex::FromHex; -use rlp::Rlp; -use SyncConfig; - -fn seal_spec() -> Spec { - let spec_data = include_str!("../res/private_spec.json"); - Spec::load(&::std::env::temp_dir(), spec_data.as_bytes()).unwrap() -} - -#[test] -fn send_private_transaction() { - // Setup two clients - let s0 = KeyPair::from_secret_slice(&keccak("1")).unwrap(); - let s1 = KeyPair::from_secret_slice(&keccak("0")).unwrap(); - - let signer = Arc::new(ethcore_private_tx::KeyPairSigner(vec![s0.clone(), s1.clone()])); - - let mut net = TestNet::with_spec(2, SyncConfig::default(), seal_spec); - let client0 = net.peer(0).chain.clone(); - let client1 = net.peer(1).chain.clone(); - let io_handler0: Arc> = Arc::new(TestIoHandler::new(net.peer(0).chain.clone())); - let io_handler1: Arc> = Arc::new(TestIoHandler::new(net.peer(1).chain.clone())); - - net.peer(0).miner.set_author(miner::Author::Sealer(engines::signer::from_keypair(s0.clone()))); - net.peer(1).miner.set_author(miner::Author::Sealer(engines::signer::from_keypair(s1.clone()))); - net.peer(0).chain.engine().register_client(Arc::downgrade(&net.peer(0).chain) as _); - net.peer(1).chain.engine().register_client(Arc::downgrade(&net.peer(1).chain) as _); - net.peer(0).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler0))); - net.peer(1).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler1))); - - let (address, _) = contract_address(CreateContractAddress::FromSenderAndNonce, &s0.address(), &0.into(), &[]); - let chain_id = client0.signing_chain_id(); - - // Exhange statuses - net.sync(); - - // Setup private providers - let validator_config = ProviderConfig{ - validator_accounts: vec![s1.address()], - signer_account: None, - }; - - let signer_config = ProviderConfig{ - validator_accounts: Vec::new(), - signer_account: Some(s0.address()), - }; - - let private_keys = Arc::new(StoringKeyProvider::default()); - - let pm0 = Arc::new(Provider::new( - client0.clone(), - net.peer(0).miner.clone(), - signer.clone(), - Box::new(NoopEncryptor::default()), - signer_config, - IoChannel::to_handler(Arc::downgrade(&io_handler0)), - private_keys.clone(), - )); - pm0.add_notify(net.peers[0].clone()); - - let pm1 = Arc::new(Provider::new( - client1.clone(), - net.peer(1).miner.clone(), - signer.clone(), - Box::new(NoopEncryptor::default()), - validator_config, - IoChannel::to_handler(Arc::downgrade(&io_handler1)), - private_keys.clone(), - )); - pm1.add_notify(net.peers[1].clone()); - - // Create and deploy contract - let private_contract_test = "6060604052341561000f57600080fd5b60d88061001d6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630c55699c146046578063bc64b76d14607457600080fd5b3415605057600080fd5b60566098565b60405180826000191660001916815260200191505060405180910390f35b3415607e57600080fd5b6096600480803560001916906020019091905050609e565b005b60005481565b8060008160001916905550505600a165627a7a723058206acbdf4b15ca4c2d43e1b1879b830451a34f1e9d02ff1f2f394d8d857e79d2080029".from_hex().unwrap(); - let mut private_create_tx = Transaction::default(); - private_create_tx.action = Action::Create; - private_create_tx.data = private_contract_test; - private_create_tx.gas = 200000.into(); - let private_create_tx_signed = private_create_tx.sign(&s0.secret(), None); - let validators = vec![s1.address()]; - let (public_tx, _) = pm0.public_creation_transaction(BlockId::Latest, &private_create_tx_signed, &validators, 0.into()).unwrap(); - let public_tx = public_tx.sign(&s0.secret(), chain_id); - - let public_tx_copy = public_tx.clone(); - push_block_with_transactions(&client0, &[public_tx]); - push_block_with_transactions(&client1, &[public_tx_copy]); - - net.sync(); - - //Create private transaction for modifying state - let mut private_tx = Transaction::default(); - private_tx.action = Action::Call(address.clone()); - private_tx.data = "bc64b76d2a00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap(); //setX(42) - private_tx.gas = 120000.into(); - private_tx.nonce = 1.into(); - let private_tx = private_tx.sign(&s0.secret(), None); - assert!(pm0.create_private_transaction(private_tx).is_ok()); - - //send private transaction message to validator - net.sync(); - - let validator_handler = net.peer(1).private_tx_handler.clone(); - let received_private_transactions = validator_handler.txs.lock().clone(); - assert_eq!(received_private_transactions.len(), 1); - - //process received private transaction message - let private_transaction = received_private_transactions[0].clone(); - assert!(pm1.import_private_transaction(&private_transaction).is_ok()); - - //send signed response - net.sync(); - - let sender_handler = net.peer(0).private_tx_handler.clone(); - let received_signed_private_transactions = sender_handler.signed_txs.lock().clone(); - assert_eq!(received_signed_private_transactions.len(), 1); - - //process signed response - let signed_private_transaction = received_signed_private_transactions[0].clone(); - assert!(pm0.import_signed_private_transaction(&signed_private_transaction).is_ok()); - let signature: SignedPrivateTransaction = Rlp::new(&signed_private_transaction).as_val().unwrap(); - assert!(pm0.process_signature(&signature).is_ok()); - let local_transactions = net.peer(0).miner.local_transactions(); - assert_eq!(local_transactions.len(), 1); -} diff --git a/ethcore/sync/src/tests/rpc.rs b/ethcore/sync/src/tests/rpc.rs index 3e0523931e1..fd3b1e04402 100644 --- a/ethcore/sync/src/tests/rpc.rs +++ b/ethcore/sync/src/tests/rpc.rs @@ -1,29 +1,29 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use super::super::NetworkConfiguration; +use ipc::binary::{deserialize, serialize}; use network::NetworkConfiguration as BasicNetworkConfiguration; use std::convert::From; -use ipc::binary::{serialize, deserialize}; #[test] fn network_settings_serialize() { - let net_cfg = NetworkConfiguration::from(BasicNetworkConfiguration::new_local()); - let serialized = serialize(&net_cfg).unwrap(); - let deserialized = deserialize::(&serialized).unwrap(); + let net_cfg = NetworkConfiguration::from(BasicNetworkConfiguration::new_local()); + let serialized = serialize(&net_cfg).unwrap(); + let deserialized = deserialize::(&serialized).unwrap(); - assert_eq!(net_cfg.udp_port, deserialized.udp_port); + assert_eq!(net_cfg.udp_port, deserialized.udp_port); } diff --git a/ethcore/sync/src/tests/snapshot.rs b/ethcore/sync/src/tests/snapshot.rs index a3aa77d36d6..03ae2873ee7 100644 --- a/ethcore/sync/src/tests/snapshot.rs +++ b/ethcore/sync/src/tests/snapshot.rs @@ -1,156 +1,221 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::collections::HashMap; -use std::sync::Arc; -use hash::keccak; +use super::helpers::*; +use bytes::Bytes; +use ethcore::{ + client::EachBlockWith, + snapshot::{CreationStatus, ManifestData, RestorationStatus, SnapshotService}, +}; use ethereum_types::H256; +use hash::keccak; use parking_lot::Mutex; -use bytes::Bytes; -use ethcore::snapshot::{SnapshotService, ManifestData, RestorationStatus}; -use ethcore::client::EachBlockWith; +use std::{collections::HashMap, sync::Arc}; use types::BlockNumber; -use super::helpers::*; -use {SyncConfig, WarpSync}; +use SyncConfig; +use WarpSync; pub struct TestSnapshotService { - manifest: Option, - chunks: HashMap, + manifest: Option, + chunks: HashMap, - restoration_manifest: Mutex>, - state_restoration_chunks: Mutex>, - block_restoration_chunks: Mutex>, + restoration_manifest: Mutex>, + state_restoration_chunks: Mutex>, + block_restoration_chunks: Mutex>, } impl TestSnapshotService { - pub fn new() -> TestSnapshotService { - TestSnapshotService { - manifest: None, - chunks: HashMap::new(), - restoration_manifest: Mutex::new(None), - state_restoration_chunks: Mutex::new(HashMap::new()), - block_restoration_chunks: Mutex::new(HashMap::new()), - } - } - - pub fn new_with_snapshot(num_chunks: usize, block_hash: H256, block_number: BlockNumber) -> TestSnapshotService { - let num_state_chunks = num_chunks / 2; - let num_block_chunks = num_chunks - num_state_chunks; - let state_chunks: Vec = (0..num_state_chunks).map(|_| H256::random().to_vec()).collect(); - let block_chunks: Vec = (0..num_block_chunks).map(|_| H256::random().to_vec()).collect(); - let manifest = ManifestData { - version: 2, - state_hashes: state_chunks.iter().map(|data| keccak(data)).collect(), - block_hashes: block_chunks.iter().map(|data| keccak(data)).collect(), - state_root: H256::new(), - block_number: block_number, - block_hash: block_hash, - }; - let mut chunks: HashMap = state_chunks.into_iter().map(|data| (keccak(&data), data)).collect(); - chunks.extend(block_chunks.into_iter().map(|data| (keccak(&data), data))); - TestSnapshotService { - manifest: Some(manifest), - chunks: chunks, - restoration_manifest: Mutex::new(None), - state_restoration_chunks: Mutex::new(HashMap::new()), - block_restoration_chunks: Mutex::new(HashMap::new()), - } - } + pub fn new() -> TestSnapshotService { + TestSnapshotService { + manifest: None, + chunks: HashMap::new(), + restoration_manifest: Mutex::new(None), + state_restoration_chunks: Mutex::new(HashMap::new()), + block_restoration_chunks: Mutex::new(HashMap::new()), + } + } + + pub fn new_with_snapshot( + num_chunks: usize, + block_hash: H256, + block_number: BlockNumber, + ) -> TestSnapshotService { + let num_state_chunks = num_chunks / 2; + let num_block_chunks = num_chunks - num_state_chunks; + let state_chunks: Vec = (0..num_state_chunks) + .map(|_| H256::random().to_vec()) + .collect(); + let block_chunks: Vec = (0..num_block_chunks) + .map(|_| H256::random().to_vec()) + .collect(); + let manifest = ManifestData { + version: 2, + state_hashes: state_chunks.iter().map(|data| keccak(data)).collect(), + block_hashes: block_chunks.iter().map(|data| keccak(data)).collect(), + state_root: H256::new(), + block_number: block_number, + block_hash: block_hash, + }; + let mut chunks: HashMap = state_chunks + .into_iter() + .map(|data| (keccak(&data), data)) + .collect(); + chunks.extend(block_chunks.into_iter().map(|data| (keccak(&data), data))); + TestSnapshotService { + manifest: Some(manifest), + chunks: chunks, + restoration_manifest: Mutex::new(None), + state_restoration_chunks: Mutex::new(HashMap::new()), + block_restoration_chunks: Mutex::new(HashMap::new()), + } + } } impl SnapshotService for TestSnapshotService { - fn manifest(&self) -> Option { - self.manifest.as_ref().cloned() - } - - fn supported_versions(&self) -> Option<(u64, u64)> { - Some((1, 2)) - } - - fn completed_chunks(&self) -> Option> { - Some(vec![]) - } - - fn chunk(&self, hash: H256) -> Option { - self.chunks.get(&hash).cloned() - } - - fn status(&self) -> RestorationStatus { - match *self.restoration_manifest.lock() { - Some(ref manifest) if self.state_restoration_chunks.lock().len() == manifest.state_hashes.len() && - self.block_restoration_chunks.lock().len() == manifest.block_hashes.len() => RestorationStatus::Inactive, - Some(ref manifest) => RestorationStatus::Ongoing { - state_chunks: manifest.state_hashes.len() as u32, - block_chunks: manifest.block_hashes.len() as u32, - state_chunks_done: self.state_restoration_chunks.lock().len() as u32, - block_chunks_done: self.block_restoration_chunks.lock().len() as u32, - }, - None => RestorationStatus::Inactive, - } - } - - fn begin_restore(&self, manifest: ManifestData) { - let mut restoration_manifest = self.restoration_manifest.lock(); - - if let Some(ref c_manifest) = *restoration_manifest { - if c_manifest.state_root == manifest.state_root { - return; - } - } - - *restoration_manifest = Some(manifest); - self.state_restoration_chunks.lock().clear(); - self.block_restoration_chunks.lock().clear(); - } - - fn abort_restore(&self) { - *self.restoration_manifest.lock() = None; - self.state_restoration_chunks.lock().clear(); - self.block_restoration_chunks.lock().clear(); - } - - fn restore_state_chunk(&self, hash: H256, chunk: Bytes) { - if self.restoration_manifest.lock().as_ref().map_or(false, |m| m.state_hashes.iter().any(|h| h == &hash)) { - self.state_restoration_chunks.lock().insert(hash, chunk); - } - } - - fn restore_block_chunk(&self, hash: H256, chunk: Bytes) { - if self.restoration_manifest.lock().as_ref().map_or(false, |m| m.block_hashes.iter().any(|h| h == &hash)) { - self.block_restoration_chunks.lock().insert(hash, chunk); - } - } - - fn shutdown(&self) { - self.abort_restore(); - } + fn manifest(&self) -> Option { + self.manifest.as_ref().cloned() + } + + fn supported_versions(&self) -> Option<(u64, u64)> { + Some((1, 2)) + } + + fn completed_chunks(&self) -> Option> { + Some(vec![]) + } + + fn chunk(&self, hash: H256) -> Option { + self.chunks.get(&hash).cloned() + } + + fn creation_status(&self) -> CreationStatus { + CreationStatus::Inactive + } + + fn restoration_status(&self) -> RestorationStatus { + match *self.restoration_manifest.lock() { + Some(ref manifest) + if self.state_restoration_chunks.lock().len() == manifest.state_hashes.len() + && self.block_restoration_chunks.lock().len() + == manifest.block_hashes.len() => + { + RestorationStatus::Inactive + } + Some(ref manifest) => RestorationStatus::Ongoing { + block_number: 0, + state_chunks: manifest.state_hashes.len() as u32, + block_chunks: manifest.block_hashes.len() as u32, + state_chunks_done: self.state_restoration_chunks.lock().len() as u32, + block_chunks_done: self.block_restoration_chunks.lock().len() as u32, + }, + None => RestorationStatus::Inactive, + } + } + + fn begin_restore(&self, manifest: ManifestData) { + let mut restoration_manifest = self.restoration_manifest.lock(); + + if let Some(ref c_manifest) = *restoration_manifest { + if c_manifest.state_root == manifest.state_root { + return; + } + } + + *restoration_manifest = Some(manifest); + self.state_restoration_chunks.lock().clear(); + self.block_restoration_chunks.lock().clear(); + } + + fn abort_restore(&self) { + *self.restoration_manifest.lock() = None; + self.state_restoration_chunks.lock().clear(); + self.block_restoration_chunks.lock().clear(); + } + + fn abort_snapshot(&self) {} + + fn restore_state_chunk(&self, hash: H256, chunk: Bytes) { + if self + .restoration_manifest + .lock() + .as_ref() + .map_or(false, |m| m.state_hashes.iter().any(|h| h == &hash)) + { + self.state_restoration_chunks.lock().insert(hash, chunk); + } + } + + fn restore_block_chunk(&self, hash: H256, chunk: Bytes) { + if self + .restoration_manifest + .lock() + .as_ref() + .map_or(false, |m| m.block_hashes.iter().any(|h| h == &hash)) + { + self.block_restoration_chunks.lock().insert(hash, chunk); + } + } + + fn shutdown(&self) { + self.abort_restore(); + } } #[test] fn snapshot_sync() { - ::env_logger::try_init().ok(); - let mut config = SyncConfig::default(); - config.warp_sync = WarpSync::Enabled; - let mut net = TestNet::new_with_config(5, config); - let snapshot_service = Arc::new(TestSnapshotService::new_with_snapshot(16, H256::new(), 500000)); - for i in 0..4 { - net.peer_mut(i).snapshot_service = snapshot_service.clone(); - net.peer(i).chain.add_blocks(1, EachBlockWith::Nothing); - } - net.sync_steps(50); - assert_eq!(net.peer(4).snapshot_service.state_restoration_chunks.lock().len(), net.peer(0).snapshot_service.manifest.as_ref().unwrap().state_hashes.len()); - assert_eq!(net.peer(4).snapshot_service.block_restoration_chunks.lock().len(), net.peer(0).snapshot_service.manifest.as_ref().unwrap().block_hashes.len()); + ::env_logger::try_init().ok(); + let mut config = SyncConfig::default(); + config.warp_sync = WarpSync::Enabled; + let mut net = TestNet::new_with_config(5, config); + let snapshot_service = Arc::new(TestSnapshotService::new_with_snapshot( + 16, + H256::new(), + 500000, + )); + for i in 0..4 { + net.peer_mut(i).snapshot_service = snapshot_service.clone(); + net.peer(i).chain.add_blocks(1, EachBlockWith::Nothing); + } + net.sync_steps(50); + assert_eq!( + net.peer(4) + .snapshot_service + .state_restoration_chunks + .lock() + .len(), + net.peer(0) + .snapshot_service + .manifest + .as_ref() + .unwrap() + .state_hashes + .len() + ); + assert_eq!( + net.peer(4) + .snapshot_service + .block_restoration_chunks + .lock() + .len(), + net.peer(0) + .snapshot_service + .manifest + .as_ref() + .unwrap() + .block_hashes + .len() + ); } diff --git a/ethcore/sync/src/transactions_stats.rs b/ethcore/sync/src/transactions_stats.rs index 91094fa5fd2..989900ecbd0 100644 --- a/ethcore/sync/src/transactions_stats.rs +++ b/ethcore/sync/src/transactions_stats.rs @@ -1,135 +1,151 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use api::TransactionStats; -use std::hash::BuildHasher; -use std::collections::{HashSet, HashMap}; use ethereum_types::{H256, H512}; use fastmap::H256FastMap; +use std::{ + collections::{HashMap, HashSet}, + hash::BuildHasher, +}; use types::BlockNumber; type NodeId = H512; #[derive(Debug, PartialEq, Clone)] pub struct Stats { - first_seen: BlockNumber, - propagated_to: HashMap, + first_seen: BlockNumber, + propagated_to: HashMap, } impl Stats { - pub fn new(number: BlockNumber) -> Self { - Stats { - first_seen: number, - propagated_to: Default::default(), - } - } + pub fn new(number: BlockNumber) -> Self { + Stats { + first_seen: number, + propagated_to: Default::default(), + } + } } impl<'a> From<&'a Stats> for TransactionStats { - fn from(other: &'a Stats) -> Self { - TransactionStats { - first_seen: other.first_seen, - propagated_to: other.propagated_to - .iter() - .map(|(hash, size)| (*hash, *size)) - .collect(), - } - } + fn from(other: &'a Stats) -> Self { + TransactionStats { + first_seen: other.first_seen, + propagated_to: other + .propagated_to + .iter() + .map(|(hash, size)| (*hash, *size)) + .collect(), + } + } } #[derive(Debug, Default)] pub struct TransactionsStats { - pending_transactions: H256FastMap, + pending_transactions: H256FastMap, } impl TransactionsStats { - /// Increases number of propagations to given `enodeid`. - pub fn propagated(&mut self, hash: &H256, enode_id: Option, current_block_num: BlockNumber) { - let enode_id = enode_id.unwrap_or_default(); - let stats = self.pending_transactions.entry(*hash).or_insert_with(|| Stats::new(current_block_num)); - let count = stats.propagated_to.entry(enode_id).or_insert(0); - *count = count.saturating_add(1); - } - - /// Returns propagation stats for given hash or `None` if hash is not known. - #[cfg(test)] - pub fn get(&self, hash: &H256) -> Option<&Stats> { - self.pending_transactions.get(hash) - } - - pub fn stats(&self) -> &H256FastMap { - &self.pending_transactions - } - - /// Retains only transactions present in given `HashSet`. - pub fn retain(&mut self, hashes: &HashSet) { - let to_remove = self.pending_transactions.keys() - .filter(|hash| !hashes.contains(hash)) - .cloned() - .collect::>(); - - for hash in to_remove { - self.pending_transactions.remove(&hash); - } - } + /// Increases number of propagations to given `enodeid`. + pub fn propagated( + &mut self, + hash: &H256, + enode_id: Option, + current_block_num: BlockNumber, + ) { + let enode_id = enode_id.unwrap_or_default(); + let stats = self + .pending_transactions + .entry(*hash) + .or_insert_with(|| Stats::new(current_block_num)); + let count = stats.propagated_to.entry(enode_id).or_insert(0); + *count = count.saturating_add(1); + } + + /// Returns propagation stats for given hash or `None` if hash is not known. + #[cfg(test)] + pub fn get(&self, hash: &H256) -> Option<&Stats> { + self.pending_transactions.get(hash) + } + + pub fn stats(&self) -> &H256FastMap { + &self.pending_transactions + } + + /// Retains only transactions present in given `HashSet`. + pub fn retain(&mut self, hashes: &HashSet) { + let to_remove = self + .pending_transactions + .keys() + .filter(|hash| !hashes.contains(hash)) + .cloned() + .collect::>(); + + for hash in to_remove { + self.pending_transactions.remove(&hash); + } + } } #[cfg(test)] mod tests { - use std::collections::{HashMap, HashSet}; - use super::{Stats, TransactionsStats}; - - #[test] - fn should_keep_track_of_propagations() { - // given - let mut stats = TransactionsStats::default(); - let hash = 5.into(); - let enodeid1 = 2.into(); - let enodeid2 = 5.into(); - - // when - stats.propagated(&hash, Some(enodeid1), 5); - stats.propagated(&hash, Some(enodeid1), 10); - stats.propagated(&hash, Some(enodeid2), 15); - - // then - let stats = stats.get(&hash); - assert_eq!(stats, Some(&Stats { - first_seen: 5, - propagated_to: hash_map![ - enodeid1 => 2, - enodeid2 => 1 - ], - })); - } - - #[test] - fn should_remove_hash_from_tracking() { - // given - let mut stats = TransactionsStats::default(); - let hash = 5.into(); - let enodeid1 = 5.into(); - stats.propagated(&hash, Some(enodeid1), 10); - - // when - stats.retain(&HashSet::new()); - - // then - let stats = stats.get(&hash); - assert_eq!(stats, None); - } + use super::{Stats, TransactionsStats}; + use std::collections::{HashMap, HashSet}; + + #[test] + fn should_keep_track_of_propagations() { + // given + let mut stats = TransactionsStats::default(); + let hash = 5.into(); + let enodeid1 = 2.into(); + let enodeid2 = 5.into(); + + // when + stats.propagated(&hash, Some(enodeid1), 5); + stats.propagated(&hash, Some(enodeid1), 10); + stats.propagated(&hash, Some(enodeid2), 15); + + // then + let stats = stats.get(&hash); + assert_eq!( + stats, + Some(&Stats { + first_seen: 5, + propagated_to: hash_map![ + enodeid1 => 2, + enodeid2 => 1 + ], + }) + ); + } + + #[test] + fn should_remove_hash_from_tracking() { + // given + let mut stats = TransactionsStats::default(); + let hash = 5.into(); + let enodeid1 = 5.into(); + stats.propagated(&hash, Some(enodeid1), 10); + + // when + stats.retain(&HashSet::new()); + + // then + let stats = stats.get(&hash); + assert_eq!(stats, None); + } } diff --git a/ethcore/types/Cargo.toml b/ethcore/types/Cargo.toml index e3f9bd1db63..1d831278f83 100644 --- a/ethcore/types/Cargo.toml +++ b/ethcore/types/Cargo.toml @@ -1,6 +1,6 @@ [package] +description = "Parity Ethereum Common Types" name = "common-types" -description = "Common types used throughout the codebase" version = "0.1.0" authors = ["Parity Technologies "] @@ -16,4 +16,7 @@ rlp_derive = { path = "../../util/rlp-derive" } unexpected = { path = "../../util/unexpected" } [dev-dependencies] -rustc-hex= "1.0" +rustc-hex = "1.0" + +[features] +test-helpers = [] diff --git a/ethcore/types/src/account_diff.rs b/ethcore/types/src/account_diff.rs index 09751ba4554..1144712c77c 100644 --- a/ethcore/types/src/account_diff.rs +++ b/ethcore/types/src/account_diff.rs @@ -1,146 +1,195 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Diff between two accounts. -use std::cmp::*; -use std::fmt; -use std::collections::BTreeMap; -use ethereum_types::{H256, U256}; use bytes::Bytes; +use ethereum_types::{H256, U256}; +use std::{cmp::*, collections::BTreeMap, fmt}; #[derive(Debug, PartialEq, Eq, Clone)] /// Diff type for specifying a change (or not). pub enum Diff { - /// Both sides are the same. - Same, - /// Left (pre, source) side doesn't include value, right side (post, destination) does. - Born(T), - /// Both sides include data; it chaged value between them. - Changed(T, T), - /// Left (pre, source) side does include value, right side (post, destination) does not. - Died(T), + /// Both sides are the same. + Same, + /// Left (pre, source) side doesn't include value, right side (post, destination) does. + Born(T), + /// Both sides include data; it chaged value between them. + Changed(T, T), + /// Left (pre, source) side does include value, right side (post, destination) does not. + Died(T), } impl Diff { - /// Construct new object with given `pre` and `post`. - pub fn new(pre: T, post: T) -> Self where T: Eq { - if pre == post { - Diff::Same - } else { - Diff::Changed(pre, post) - } - } - - /// Get the before value, if there is one. - pub fn pre(&self) -> Option<&T> { match *self { Diff::Died(ref x) | Diff::Changed(ref x, _) => Some(x), _ => None } } - - /// Get the after value, if there is one. - pub fn post(&self) -> Option<&T> { match *self { Diff::Born(ref x) | Diff::Changed(_, ref x) => Some(x), _ => None } } - - /// Determine whether there was a change or not. - pub fn is_same(&self) -> bool { match *self { Diff::Same => true, _ => false }} + /// Construct new object with given `pre` and `post`. + pub fn new(pre: T, post: T) -> Self + where + T: Eq, + { + if pre == post { + Diff::Same + } else { + Diff::Changed(pre, post) + } + } + + /// Get the before value, if there is one. + pub fn pre(&self) -> Option<&T> { + match *self { + Diff::Died(ref x) | Diff::Changed(ref x, _) => Some(x), + _ => None, + } + } + + /// Get the after value, if there is one. + pub fn post(&self) -> Option<&T> { + match *self { + Diff::Born(ref x) | Diff::Changed(_, ref x) => Some(x), + _ => None, + } + } + + /// Determine whether there was a change or not. + pub fn is_same(&self) -> bool { + match *self { + Diff::Same => true, + _ => false, + } + } } #[derive(Debug, PartialEq, Eq, Clone)] /// Account diff. pub struct AccountDiff { - /// Change in balance, allowed to be `Diff::Same`. - pub balance: Diff, - /// Change in nonce, allowed to be `Diff::Same`. - pub nonce: Diff, // Allowed to be Same - /// Change in code, allowed to be `Diff::Same`. - pub code: Diff, // Allowed to be Same - /// Change in storage, values are not allowed to be `Diff::Same`. - pub storage: BTreeMap>, + /// Change in balance, allowed to be `Diff::Same`. + pub balance: Diff, + /// Change in nonce, allowed to be `Diff::Same`. + pub nonce: Diff, // Allowed to be Same + /// Change in code, allowed to be `Diff::Same`. + pub code: Diff, // Allowed to be Same + /// Change in storage, values are not allowed to be `Diff::Same`. + pub storage: BTreeMap>, } #[derive(Debug, PartialEq, Eq, Clone)] /// Change in existance type. // TODO: include other types of change. pub enum Existance { - /// Item came into existance. - Born, - /// Item stayed in existance. - Alive, - /// Item went out of existance. - Died, + /// Item came into existance. + Born, + /// Item stayed in existance. + Alive, + /// Item went out of existance. + Died, } impl fmt::Display for Existance { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Existance::Born => write!(f, "+++")?, - Existance::Alive => write!(f, "***")?, - Existance::Died => write!(f, "XXX")?, - } - Ok(()) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Existance::Born => write!(f, "+++")?, + Existance::Alive => write!(f, "***")?, + Existance::Died => write!(f, "XXX")?, + } + Ok(()) + } } impl AccountDiff { - /// Get `Existance` projection. - pub fn existance(&self) -> Existance { - match self.balance { - Diff::Born(_) => Existance::Born, - Diff::Died(_) => Existance::Died, - _ => Existance::Alive, - } - } + /// Get `Existance` projection. + pub fn existance(&self) -> Existance { + match self.balance { + Diff::Born(_) => Existance::Born, + Diff::Died(_) => Existance::Died, + _ => Existance::Alive, + } + } } // TODO: refactor into something nicer. fn interpreted_hash(u: &H256) -> String { - if u <= &H256::from(0xffffffff) { - format!("{} = 0x{:x}", U256::from(&**u).low_u32(), U256::from(&**u).low_u32()) - } else if u <= &H256::from(u64::max_value()) { - format!("{} = 0x{:x}", U256::from(&**u).low_u64(), U256::from(&**u).low_u64()) -// } else if u <= &H256::from("0xffffffffffffffffffffffffffffffffffffffff") { -// format!("@{}", Address::from(u)) - } else { - format!("#{}", u) - } + if u <= &H256::from(0xffffffff) { + format!( + "{} = 0x{:x}", + U256::from(&**u).low_u32(), + U256::from(&**u).low_u32() + ) + } else if u <= &H256::from(u64::max_value()) { + format!( + "{} = 0x{:x}", + U256::from(&**u).low_u64(), + U256::from(&**u).low_u64() + ) + // } else if u <= &H256::from("0xffffffffffffffffffffffffffffffffffffffff") { + // format!("@{}", Address::from(u)) + } else { + format!("#{}", u) + } } impl fmt::Display for AccountDiff { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use bytes::ToPretty; - - match self.nonce { - Diff::Born(ref x) => write!(f, " non {}", x)?, - Diff::Changed(ref pre, ref post) => write!(f, "#{} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - * min(pre, post))?, - _ => {}, - } - match self.balance { - Diff::Born(ref x) => write!(f, " bal {}", x)?, - Diff::Changed(ref pre, ref post) => write!(f, "${} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - *min(pre, post))?, - _ => {}, - } - if let Diff::Born(ref x) = self.code { - write!(f, " code {}", x.pretty())?; - } - write!(f, "\n")?; - for (k, dv) in &self.storage { - match *dv { - Diff::Born(ref v) => write!(f, " + {} => {}\n", interpreted_hash(k), interpreted_hash(v))?, - Diff::Changed(ref pre, ref post) => write!(f, " * {} => {} (was {})\n", interpreted_hash(k), interpreted_hash(post), interpreted_hash(pre))?, - Diff::Died(_) => write!(f, " X {}\n", interpreted_hash(k))?, - _ => {}, - } - } - Ok(()) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use bytes::ToPretty; + + match self.nonce { + Diff::Born(ref x) => write!(f, " non {}", x)?, + Diff::Changed(ref pre, ref post) => write!( + f, + "#{} ({} {} {})", + post, + pre, + if pre > post { "-" } else { "+" }, + *max(pre, post) - *min(pre, post) + )?, + _ => {} + } + match self.balance { + Diff::Born(ref x) => write!(f, " bal {}", x)?, + Diff::Changed(ref pre, ref post) => write!( + f, + "${} ({} {} {})", + post, + pre, + if pre > post { "-" } else { "+" }, + *max(pre, post) - *min(pre, post) + )?, + _ => {} + } + if let Diff::Born(ref x) = self.code { + write!(f, " code {}", x.pretty())?; + } + write!(f, "\n")?; + for (k, dv) in &self.storage { + match *dv { + Diff::Born(ref v) => write!( + f, + " + {} => {}\n", + interpreted_hash(k), + interpreted_hash(v) + )?, + Diff::Changed(ref pre, ref post) => write!( + f, + " * {} => {} (was {})\n", + interpreted_hash(k), + interpreted_hash(post), + interpreted_hash(pre) + )?, + Diff::Died(_) => write!(f, " X {}\n", interpreted_hash(k))?, + _ => {} + } + } + Ok(()) + } } diff --git a/ethcore/types/src/ancestry_action.rs b/ethcore/types/src/ancestry_action.rs index 39b73ef99ea..c2684b40490 100644 --- a/ethcore/types/src/ancestry_action.rs +++ b/ethcore/types/src/ancestry_action.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Actions on ancestry blocks when working on a new block. @@ -22,6 +22,6 @@ use ethereum_types::H256; /// Actions on a live block's parent block. Only committed when the live block is committed. Those actions here must /// respect the normal blockchain reorganization rules. pub enum AncestryAction { - /// Mark an ancestry block as finalized. - MarkFinalized(H256), + /// Mark an ancestry block as finalized. + MarkFinalized(H256), } diff --git a/ethcore/types/src/basic_account.rs b/ethcore/types/src/basic_account.rs index 039dbac9493..d4e776706a0 100644 --- a/ethcore/types/src/basic_account.rs +++ b/ethcore/types/src/basic_account.rs @@ -1,32 +1,32 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Basic account type -- the decoded RLP from the state trie. -use ethereum_types::{U256, H256}; +use ethereum_types::{H256, U256}; /// Basic account type. #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] pub struct BasicAccount { - /// Nonce of the account. - pub nonce: U256, - /// Balance of the account. - pub balance: U256, - /// Storage root of the account. - pub storage_root: H256, - /// Code hash of the account. - pub code_hash: H256, + /// Nonce of the account. + pub nonce: U256, + /// Balance of the account. + pub balance: U256, + /// Storage root of the account. + pub storage_root: H256, + /// Code hash of the account. + pub code_hash: H256, } diff --git a/ethcore/types/src/block.rs b/ethcore/types/src/block.rs index a423fb1ed1b..dea92ad5b28 100644 --- a/ethcore/types/src/block.rs +++ b/ethcore/types/src/block.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Base data structure of this module is `Block`. //! @@ -34,43 +34,43 @@ use bytes::Bytes; use header::Header; -use rlp::{Rlp, RlpStream, Decodable, DecoderError}; +use rlp::{Decodable, DecoderError, Rlp, RlpStream}; use transaction::UnverifiedTransaction; /// A block, encoded as it is on the block chain. #[derive(Default, Debug, Clone, PartialEq)] pub struct Block { - /// The header of this block. - pub header: Header, - /// The transactions in this block. - pub transactions: Vec, - /// The uncles of this block. - pub uncles: Vec

, + /// The header of this block. + pub header: Header, + /// The transactions in this block. + pub transactions: Vec, + /// The uncles of this block. + pub uncles: Vec
, } impl Block { - /// Get the RLP-encoding of the block with the seal. - pub fn rlp_bytes(&self) -> Bytes { - let mut block_rlp = RlpStream::new_list(3); - block_rlp.append(&self.header); - block_rlp.append_list(&self.transactions); - block_rlp.append_list(&self.uncles); - block_rlp.out() - } + /// Get the RLP-encoding of the block with the seal. + pub fn rlp_bytes(&self) -> Bytes { + let mut block_rlp = RlpStream::new_list(3); + block_rlp.append(&self.header); + block_rlp.append_list(&self.transactions); + block_rlp.append_list(&self.uncles); + block_rlp.out() + } } impl Decodable for Block { - fn decode(rlp: &Rlp) -> Result { - if rlp.as_raw().len() != rlp.payload_info()?.total() { - return Err(DecoderError::RlpIsTooBig); - } - if rlp.item_count()? != 3 { - return Err(DecoderError::RlpIncorrectListLen); - } - Ok(Block { - header: rlp.val_at(0)?, - transactions: rlp.list_at(1)?, - uncles: rlp.list_at(2)?, - }) - } + fn decode(rlp: &Rlp) -> Result { + if rlp.as_raw().len() != rlp.payload_info()?.total() { + return Err(DecoderError::RlpIsTooBig); + } + if rlp.item_count()? != 3 { + return Err(DecoderError::RlpIncorrectListLen); + } + Ok(Block { + header: rlp.val_at(0)?, + transactions: rlp.list_at(1)?, + uncles: rlp.list_at(2)?, + }) + } } diff --git a/ethcore/types/src/block_status.rs b/ethcore/types/src/block_status.rs index 0460fcbe6d1..9a9b134aaec 100644 --- a/ethcore/types/src/block_status.rs +++ b/ethcore/types/src/block_status.rs @@ -1,30 +1,30 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! General block status /// General block status #[derive(Debug, Eq, PartialEq)] pub enum BlockStatus { - /// Part of the blockchain. - InChain, - /// Queued for import. - Queued, - /// Known as bad. - Bad, - /// Unknown. - Unknown, + /// Part of the blockchain. + InChain, + /// Queued for import. + Queued, + /// Known as bad. + Bad, + /// Unknown. + Unknown, } diff --git a/ethcore/types/src/blockchain_info.rs b/ethcore/types/src/blockchain_info.rs index 42158638dae..b04a8640d46 100644 --- a/ethcore/types/src/blockchain_info.rs +++ b/ethcore/types/src/blockchain_info.rs @@ -1,66 +1,71 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Blockhain info type definition use std::fmt; -use ethereum_types::{U256, H256}; +use ethereum_types::{H256, U256}; use security_level::SecurityLevel; -use {BlockNumber}; +use BlockNumber; /// Information about the blockchain gathered together. #[derive(Clone, Debug)] pub struct BlockChainInfo { - /// Blockchain difficulty. - pub total_difficulty: U256, - /// Block queue difficulty. - pub pending_total_difficulty: U256, - /// Genesis block hash. - pub genesis_hash: H256, - /// Best blockchain block hash. - pub best_block_hash: H256, - /// Best blockchain block number. - pub best_block_number: BlockNumber, - /// Best blockchain block timestamp. - pub best_block_timestamp: u64, - /// Best ancient block hash. - pub ancient_block_hash: Option, - /// Best ancient block number. - pub ancient_block_number: Option, - /// First block on the best sequence. - pub first_block_hash: Option, - /// Number of the first block on the best sequence. - pub first_block_number: Option, + /// Blockchain difficulty. + pub total_difficulty: U256, + /// Block queue difficulty. + pub pending_total_difficulty: U256, + /// Genesis block hash. + pub genesis_hash: H256, + /// Best blockchain block hash. + pub best_block_hash: H256, + /// Best blockchain block number. + pub best_block_number: BlockNumber, + /// Best blockchain block timestamp. + pub best_block_timestamp: u64, + /// Best ancient block hash. + pub ancient_block_hash: Option, + /// Best ancient block number. + pub ancient_block_number: Option, + /// First block on the best sequence. + pub first_block_hash: Option, + /// Number of the first block on the best sequence. + pub first_block_number: Option, } impl BlockChainInfo { - /// Determine the security model for the current state. - pub fn security_level(&self) -> SecurityLevel { - // TODO: Detect SecurityLevel::FullState : https://github.com/paritytech/parity-ethereum/issues/3834 - if self.ancient_block_number.is_none() || self.first_block_number.is_none() { - SecurityLevel::FullProofOfWork - } else { - SecurityLevel::PartialProofOfWork(self.best_block_number - self.first_block_number.expect("Guard condition means this is not none")) - } - } + /// Determine the security model for the current state. + pub fn security_level(&self) -> SecurityLevel { + // TODO: Detect SecurityLevel::FullState : https://github.com/openethereum/openethereum/issues/3834 + if self.ancient_block_number.is_none() || self.first_block_number.is_none() { + SecurityLevel::FullProofOfWork + } else { + SecurityLevel::PartialProofOfWork( + self.best_block_number + - self + .first_block_number + .expect("Guard condition means this is not none"), + ) + } + } } impl fmt::Display for BlockChainInfo { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "#{}.{}", self.best_block_number, self.best_block_hash) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "#{}.{}", self.best_block_number, self.best_block_hash) + } } diff --git a/ethcore/types/src/call_analytics.rs b/ethcore/types/src/call_analytics.rs index 902c75e56a7..35709296f46 100644 --- a/ethcore/types/src/call_analytics.rs +++ b/ethcore/types/src/call_analytics.rs @@ -1,28 +1,28 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Call analytics related types /// Options concerning what analytics we run on the call. #[derive(Eq, PartialEq, Default, Clone, Copy, Debug)] pub struct CallAnalytics { - /// Make a transaction trace. - pub transaction_tracing: bool, - /// Make a VM trace. - pub vm_tracing: bool, - /// Make a diff. - pub state_diffing: bool, + /// Make a transaction trace. + pub transaction_tracing: bool, + /// Make a VM trace. + pub vm_tracing: bool, + /// Make a diff. + pub state_diffing: bool, } diff --git a/ethcore/types/src/creation_status.rs b/ethcore/types/src/creation_status.rs new file mode 100644 index 00000000000..edd74ce70e3 --- /dev/null +++ b/ethcore/types/src/creation_status.rs @@ -0,0 +1,27 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. + +// OpenEthereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// OpenEthereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with OpenEthereum. If not, see . + +/// Statuses for snapshot creation. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub enum CreationStatus { + /// No creation activity currently. + Inactive, + /// Snapshot creation is in progress. + Ongoing { + /// Current created snapshot. + block_number: u32, + }, +} diff --git a/ethcore/types/src/data_format.rs b/ethcore/types/src/data_format.rs new file mode 100644 index 00000000000..f4d25046c07 --- /dev/null +++ b/ethcore/types/src/data_format.rs @@ -0,0 +1,43 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. + +// OpenEthereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// OpenEthereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with OpenEthereum. If not, see . + +//! Data format for importing/exporting blocks from disk +use std::str::FromStr; + +/// Format for importing/exporting blocks +#[derive(Debug, PartialEq)] +pub enum DataFormat { + Hex, + Binary, +} + +impl Default for DataFormat { + fn default() -> Self { + DataFormat::Binary + } +} + +impl FromStr for DataFormat { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "binary" | "bin" => Ok(DataFormat::Binary), + "hex" => Ok(DataFormat::Hex), + x => Err(format!("Invalid format: {}", x)), + } + } +} diff --git a/ethcore/types/src/encoded.rs b/ethcore/types/src/encoded.rs index 4680f95af50..1a13e2668e2 100644 --- a/ethcore/types/src/encoded.rs +++ b/ethcore/types/src/encoded.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Lazily-decoded owning views of RLP-encoded blockchain objects. //! These views are meant to contain _trusted_ data -- without encoding @@ -24,13 +24,13 @@ //! decoded object where parts like the hash can be saved. use block::Block as FullBlock; -use ethereum_types::{H256, Bloom, U256, Address}; +use ethereum_types::{Address, Bloom, H256, U256}; use hash::keccak; -use header::{Header as FullHeader}; +use header::Header as FullHeader; use heapsize::HeapSizeOf; use rlp::{self, Rlp, RlpStream}; use transaction::UnverifiedTransaction; -use views::{self, BlockView, HeaderView, BodyView}; +use views::{self, BlockView, BodyView, HeaderView}; use BlockNumber; /// Owning header view. @@ -38,78 +38,118 @@ use BlockNumber; pub struct Header(Vec); impl HeapSizeOf for Header { - fn heap_size_of_children(&self) -> usize { self.0.heap_size_of_children() } + fn heap_size_of_children(&self) -> usize { + self.0.heap_size_of_children() + } } impl Header { - /// Create a new owning header view. - /// Expects the data to be an RLP-encoded header -- any other case will likely lead to - /// panics further down the line. - pub fn new(encoded: Vec) -> Self { Header(encoded) } - - /// Upgrade this encoded view to a fully owned `Header` object. - pub fn decode(&self) -> Result { - rlp::decode(&self.0) - } - - /// Get a borrowed header view onto the data. - #[inline] - pub fn view(&self) -> HeaderView { view!(HeaderView, &self.0) } - - /// Get the rlp of the header. - #[inline] - pub fn rlp(&self) -> Rlp { Rlp::new(&self.0) } - - /// Consume the view and return the raw bytes. - pub fn into_inner(self) -> Vec { self.0 } + /// Create a new owning header view. + /// Expects the data to be an RLP-encoded header -- any other case will likely lead to + /// panics further down the line. + pub fn new(encoded: Vec) -> Self { + Header(encoded) + } + + /// Upgrade this encoded view to a fully owned `Header` object. + pub fn decode(&self) -> Result { + rlp::decode(&self.0) + } + + /// Get a borrowed header view onto the data. + #[inline] + pub fn view(&self) -> HeaderView { + view!(HeaderView, &self.0) + } + + /// Get the rlp of the header. + #[inline] + pub fn rlp(&self) -> Rlp { + Rlp::new(&self.0) + } + + /// Consume the view and return the raw bytes. + pub fn into_inner(self) -> Vec { + self.0 + } } // forwarders to borrowed view. impl Header { - /// Returns the header hash. - pub fn hash(&self) -> H256 { keccak(&self.0) } - - /// Returns the parent hash. - pub fn parent_hash(&self) -> H256 { self.view().parent_hash() } - - /// Returns the uncles hash. - pub fn uncles_hash(&self) -> H256 { self.view().uncles_hash() } - - /// Returns the author. - pub fn author(&self) -> Address { self.view().author() } - - /// Returns the state root. - pub fn state_root(&self) -> H256 { self.view().state_root() } - - /// Returns the transaction trie root. - pub fn transactions_root(&self) -> H256 { self.view().transactions_root() } - - /// Returns the receipts trie root - pub fn receipts_root(&self) -> H256 { self.view().receipts_root() } - - /// Returns the block log bloom - pub fn log_bloom(&self) -> Bloom { self.view().log_bloom() } - - /// Difficulty of this block - pub fn difficulty(&self) -> U256 { self.view().difficulty() } - - /// Number of this block. - pub fn number(&self) -> BlockNumber { self.view().number() } - - /// Time this block was produced. - pub fn timestamp(&self) -> u64 { self.view().timestamp() } - - /// Gas limit of this block. - pub fn gas_limit(&self) -> U256 { self.view().gas_limit() } - - /// Total gas used in this block. - pub fn gas_used(&self) -> U256 { self.view().gas_used() } - - /// Block extra data. - pub fn extra_data(&self) -> Vec { self.view().extra_data() } - - /// Engine-specific seal fields. - pub fn seal(&self) -> Vec> { self.view().seal() } + /// Returns the header hash. + pub fn hash(&self) -> H256 { + keccak(&self.0) + } + + /// Returns the parent hash. + pub fn parent_hash(&self) -> H256 { + self.view().parent_hash() + } + + /// Returns the uncles hash. + pub fn uncles_hash(&self) -> H256 { + self.view().uncles_hash() + } + + /// Returns the author. + pub fn author(&self) -> Address { + self.view().author() + } + + /// Returns the state root. + pub fn state_root(&self) -> H256 { + self.view().state_root() + } + + /// Returns the transaction trie root. + pub fn transactions_root(&self) -> H256 { + self.view().transactions_root() + } + + /// Returns the receipts trie root + pub fn receipts_root(&self) -> H256 { + self.view().receipts_root() + } + + /// Returns the block log bloom + pub fn log_bloom(&self) -> Bloom { + self.view().log_bloom() + } + + /// Difficulty of this block + pub fn difficulty(&self) -> U256 { + self.view().difficulty() + } + + /// Number of this block. + pub fn number(&self) -> BlockNumber { + self.view().number() + } + + /// Time this block was produced. + pub fn timestamp(&self) -> u64 { + self.view().timestamp() + } + + /// Gas limit of this block. + pub fn gas_limit(&self) -> U256 { + self.view().gas_limit() + } + + /// Total gas used in this block. + pub fn gas_used(&self) -> U256 { + self.view().gas_used() + } + + /// Block extra data. + pub fn extra_data(&self) -> Vec { + self.view().extra_data() + } + + /// Engine-specific seal fields. + pub fn seal(&self) -> Vec> { + self.view().seal() + } } /// Owning block body view. @@ -117,64 +157,92 @@ impl Header { pub struct Body(Vec); impl HeapSizeOf for Body { - fn heap_size_of_children(&self) -> usize { self.0.heap_size_of_children() } + fn heap_size_of_children(&self) -> usize { + self.0.heap_size_of_children() + } } impl Body { - /// Create a new owning block body view. The raw bytes passed in must be an rlp-encoded block - /// body. - pub fn new(raw: Vec) -> Self { Body(raw) } - - /// Get a borrowed view of the data within. - #[inline] - pub fn view(&self) -> BodyView { view!(BodyView, &self.0) } - - /// Fully decode this block body. - pub fn decode(&self) -> (Vec, Vec) { - (self.view().transactions(), self.view().uncles()) - } - - /// Get the RLP of this block body. - #[inline] - pub fn rlp(&self) -> Rlp { - Rlp::new(&self.0) - } - - /// Consume the view and return the raw bytes. - pub fn into_inner(self) -> Vec { self.0 } + /// Create a new owning block body view. The raw bytes passed in must be an rlp-encoded block + /// body. + pub fn new(raw: Vec) -> Self { + Body(raw) + } + + /// Get a borrowed view of the data within. + #[inline] + pub fn view(&self) -> BodyView { + view!(BodyView, &self.0) + } + + /// Fully decode this block body. + pub fn decode(&self) -> (Vec, Vec) { + (self.view().transactions(), self.view().uncles()) + } + + /// Get the RLP of this block body. + #[inline] + pub fn rlp(&self) -> Rlp { + Rlp::new(&self.0) + } + + /// Consume the view and return the raw bytes. + pub fn into_inner(self) -> Vec { + self.0 + } } // forwarders to borrowed view. impl Body { - /// Get raw rlp of transactions - pub fn transactions_rlp(&self) -> Rlp { self.view().transactions_rlp().rlp } - - /// Get a vector of all transactions. - pub fn transactions(&self) -> Vec { self.view().transactions() } - - /// Number of transactions in the block. - pub fn transactions_count(&self) -> usize { self.view().transactions_count() } - - /// A view over each transaction in the block. - pub fn transaction_views(&self) -> Vec { self.view().transaction_views() } - - /// The hash of each transaction in the block. - pub fn transaction_hashes(&self) -> Vec { self.view().transaction_hashes() } - - /// Get raw rlp of uncle headers - pub fn uncles_rlp(&self) -> Rlp { self.view().uncles_rlp().rlp } - - /// Decode uncle headers. - pub fn uncles(&self) -> Vec { self.view().uncles() } - - /// Number of uncles. - pub fn uncles_count(&self) -> usize { self.view().uncles_count() } - - /// Borrowed view over each uncle. - pub fn uncle_views(&self) -> Vec { self.view().uncle_views() } - - /// Hash of each uncle. - pub fn uncle_hashes(&self) -> Vec { self.view().uncle_hashes() } + /// Get raw rlp of transactions + pub fn transactions_rlp(&self) -> Rlp { + self.view().transactions_rlp().rlp + } + + /// Get a vector of all transactions. + pub fn transactions(&self) -> Vec { + self.view().transactions() + } + + /// Number of transactions in the block. + pub fn transactions_count(&self) -> usize { + self.view().transactions_count() + } + + /// A view over each transaction in the block. + pub fn transaction_views(&self) -> Vec { + self.view().transaction_views() + } + + /// The hash of each transaction in the block. + pub fn transaction_hashes(&self) -> Vec { + self.view().transaction_hashes() + } + + /// Get raw rlp of uncle headers + pub fn uncles_rlp(&self) -> Rlp { + self.view().uncles_rlp().rlp + } + + /// Decode uncle headers. + pub fn uncles(&self) -> Vec { + self.view().uncles() + } + + /// Number of uncles. + pub fn uncles_count(&self) -> usize { + self.view().uncles_count() + } + + /// Borrowed view over each uncle. + pub fn uncle_views(&self) -> Vec { + self.view().uncle_views() + } + + /// Hash of each uncle. + pub fn uncle_hashes(&self) -> Vec { + self.view().uncle_hashes() + } } /// Owning block view. @@ -182,125 +250,187 @@ impl Body { pub struct Block(Vec); impl HeapSizeOf for Block { - fn heap_size_of_children(&self) -> usize { self.0.heap_size_of_children() } + fn heap_size_of_children(&self) -> usize { + self.0.heap_size_of_children() + } } impl Block { - /// Create a new owning block view. The raw bytes passed in must be an rlp-encoded block. - pub fn new(raw: Vec) -> Self { Block(raw) } - - /// Create a new owning block view by concatenating the encoded header and body - pub fn new_from_header_and_body(header: &views::HeaderView, body: &views::BodyView) -> Self { - let mut stream = RlpStream::new_list(3); - stream.append_raw(header.rlp().as_raw(), 1); - stream.append_raw(body.transactions_rlp().as_raw(), 1); - stream.append_raw(body.uncles_rlp().as_raw(), 1); - Block::new(stream.out()) - } - - /// Get a borrowed view of the whole block. - #[inline] - pub fn view(&self) -> BlockView { view!(BlockView, &self.0) } - - /// Get a borrowed view of the block header. - #[inline] - pub fn header_view(&self) -> HeaderView { self.view().header_view() } - - /// Decode to a full block. - pub fn decode(&self) -> Result { rlp::decode(&self.0) } - - /// Decode the header. - pub fn decode_header(&self) -> FullHeader { self.view().rlp().val_at(0) } - - /// Clone the encoded header. - pub fn header(&self) -> Header { Header(self.view().rlp().at(0).as_raw().to_vec()) } - - /// Get the rlp of this block. - #[inline] - pub fn rlp(&self) -> Rlp { - Rlp::new(&self.0) - } - - /// Consume the view and return the raw bytes. - pub fn into_inner(self) -> Vec { self.0 } - - /// Returns the reference to slice of bytes - pub fn raw(&self) -> &[u8] { - &self.0 - } + /// Create a new owning block view. The raw bytes passed in must be an rlp-encoded block. + pub fn new(raw: Vec) -> Self { + Block(raw) + } + + /// Create a new owning block view by concatenating the encoded header and body + pub fn new_from_header_and_body(header: &views::HeaderView, body: &views::BodyView) -> Self { + let mut stream = RlpStream::new_list(3); + stream.append_raw(header.rlp().as_raw(), 1); + stream.append_raw(body.transactions_rlp().as_raw(), 1); + stream.append_raw(body.uncles_rlp().as_raw(), 1); + Block::new(stream.out()) + } + + /// Get a borrowed view of the whole block. + #[inline] + pub fn view(&self) -> BlockView { + view!(BlockView, &self.0) + } + + /// Get a borrowed view of the block header. + #[inline] + pub fn header_view(&self) -> HeaderView { + self.view().header_view() + } + + /// Decode to a full block. + pub fn decode(&self) -> Result { + rlp::decode(&self.0) + } + + /// Decode the header. + pub fn decode_header(&self) -> FullHeader { + self.view().rlp().val_at(0) + } + + /// Clone the encoded header. + pub fn header(&self) -> Header { + Header(self.view().rlp().at(0).as_raw().to_vec()) + } + + /// Get the rlp of this block. + #[inline] + pub fn rlp(&self) -> Rlp { + Rlp::new(&self.0) + } + + /// Consume the view and return the raw bytes. + pub fn into_inner(self) -> Vec { + self.0 + } + + /// Returns the reference to slice of bytes + pub fn raw(&self) -> &[u8] { + &self.0 + } } // forwarders to borrowed header view. impl Block { - /// Returns the header hash. - pub fn hash(&self) -> H256 { self.header_view().hash() } - - /// Returns the parent hash. - pub fn parent_hash(&self) -> H256 { self.header_view().parent_hash() } - - /// Returns the uncles hash. - pub fn uncles_hash(&self) -> H256 { self.header_view().uncles_hash() } - - /// Returns the author. - pub fn author(&self) -> Address { self.header_view().author() } - - /// Returns the state root. - pub fn state_root(&self) -> H256 { self.header_view().state_root() } - - /// Returns the transaction trie root. - pub fn transactions_root(&self) -> H256 { self.header_view().transactions_root() } - - /// Returns the receipts trie root - pub fn receipts_root(&self) -> H256 { self.header_view().receipts_root() } - - /// Returns the block log bloom - pub fn log_bloom(&self) -> Bloom { self.header_view().log_bloom() } - - /// Difficulty of this block - pub fn difficulty(&self) -> U256 { self.header_view().difficulty() } - - /// Number of this block. - pub fn number(&self) -> BlockNumber { self.header_view().number() } - - /// Time this block was produced. - pub fn timestamp(&self) -> u64 { self.header_view().timestamp() } - - /// Gas limit of this block. - pub fn gas_limit(&self) -> U256 { self.header_view().gas_limit() } - - /// Total gas used in this block. - pub fn gas_used(&self) -> U256 { self.header_view().gas_used() } - - /// Block extra data. - pub fn extra_data(&self) -> Vec { self.header_view().extra_data() } - - /// Engine-specific seal fields. - pub fn seal(&self) -> Vec> { self.header_view().seal() } + /// Returns the header hash. + pub fn hash(&self) -> H256 { + self.header_view().hash() + } + + /// Returns the parent hash. + pub fn parent_hash(&self) -> H256 { + self.header_view().parent_hash() + } + + /// Returns the uncles hash. + pub fn uncles_hash(&self) -> H256 { + self.header_view().uncles_hash() + } + + /// Returns the author. + pub fn author(&self) -> Address { + self.header_view().author() + } + + /// Returns the state root. + pub fn state_root(&self) -> H256 { + self.header_view().state_root() + } + + /// Returns the transaction trie root. + pub fn transactions_root(&self) -> H256 { + self.header_view().transactions_root() + } + + /// Returns the receipts trie root + pub fn receipts_root(&self) -> H256 { + self.header_view().receipts_root() + } + + /// Returns the block log bloom + pub fn log_bloom(&self) -> Bloom { + self.header_view().log_bloom() + } + + /// Difficulty of this block + pub fn difficulty(&self) -> U256 { + self.header_view().difficulty() + } + + /// Number of this block. + pub fn number(&self) -> BlockNumber { + self.header_view().number() + } + + /// Time this block was produced. + pub fn timestamp(&self) -> u64 { + self.header_view().timestamp() + } + + /// Gas limit of this block. + pub fn gas_limit(&self) -> U256 { + self.header_view().gas_limit() + } + + /// Total gas used in this block. + pub fn gas_used(&self) -> U256 { + self.header_view().gas_used() + } + + /// Block extra data. + pub fn extra_data(&self) -> Vec { + self.header_view().extra_data() + } + + /// Engine-specific seal fields. + pub fn seal(&self) -> Vec> { + self.header_view().seal() + } } // forwarders to body view. impl Block { - /// Get a vector of all transactions. - pub fn transactions(&self) -> Vec { self.view().transactions() } - - /// Number of transactions in the block. - pub fn transactions_count(&self) -> usize { self.view().transactions_count() } - - /// A view over each transaction in the block. - pub fn transaction_views(&self) -> Vec { self.view().transaction_views() } - - /// The hash of each transaction in the block. - pub fn transaction_hashes(&self) -> Vec { self.view().transaction_hashes() } - - /// Decode uncle headers. - pub fn uncles(&self) -> Vec { self.view().uncles() } - - /// Number of uncles. - pub fn uncles_count(&self) -> usize { self.view().uncles_count() } - - /// Borrowed view over each uncle. - pub fn uncle_views(&self) -> Vec { self.view().uncle_views() } - - /// Hash of each uncle. - pub fn uncle_hashes(&self) -> Vec { self.view().uncle_hashes() } + /// Get a vector of all transactions. + pub fn transactions(&self) -> Vec { + self.view().transactions() + } + + /// Number of transactions in the block. + pub fn transactions_count(&self) -> usize { + self.view().transactions_count() + } + + /// A view over each transaction in the block. + pub fn transaction_views(&self) -> Vec { + self.view().transaction_views() + } + + /// The hash of each transaction in the block. + pub fn transaction_hashes(&self) -> Vec { + self.view().transaction_hashes() + } + + /// Decode uncle headers. + pub fn uncles(&self) -> Vec { + self.view().uncles() + } + + /// Number of uncles. + pub fn uncles_count(&self) -> usize { + self.view().uncles_count() + } + + /// Borrowed view over each uncle. + pub fn uncle_views(&self) -> Vec { + self.view().uncle_views() + } + + /// Hash of each uncle. + pub fn uncle_hashes(&self) -> Vec { + self.view().uncle_hashes() + } } diff --git a/ethcore/types/src/engines/epoch.rs b/ethcore/types/src/engines/epoch.rs index 2a43b47755c..2d21c5f7fc6 100644 --- a/ethcore/types/src/engines/epoch.rs +++ b/ethcore/types/src/engines/epoch.rs @@ -1,73 +1,72 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Epoch verifiers and transitions. use ethereum_types::H256; -use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp}; +use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; /// A full epoch transition. #[derive(Debug, Clone)] pub struct Transition { - /// Block hash at which the transition occurred. - pub block_hash: H256, - /// Block number at which the transition occurred. - pub block_number: u64, - /// "transition/epoch" proof from the engine combined with a finality proof. - pub proof: Vec, + /// Block hash at which the transition occurred. + pub block_hash: H256, + /// Block number at which the transition occurred. + pub block_number: u64, + /// "transition/epoch" proof from the engine combined with a finality proof. + pub proof: Vec, } impl Encodable for Transition { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(3) - .append(&self.block_hash) - .append(&self.block_number) - .append(&self.proof); - } + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(3) + .append(&self.block_hash) + .append(&self.block_number) + .append(&self.proof); + } } impl Decodable for Transition { - fn decode(rlp: &Rlp) -> Result { - Ok(Transition { - block_hash: rlp.val_at(0)?, - block_number: rlp.val_at(1)?, - proof: rlp.val_at(2)?, - }) - } + fn decode(rlp: &Rlp) -> Result { + Ok(Transition { + block_hash: rlp.val_at(0)?, + block_number: rlp.val_at(1)?, + proof: rlp.val_at(2)?, + }) + } } /// An epoch transition pending a finality proof. /// Not all transitions need one. pub struct PendingTransition { - /// "transition/epoch" proof from the engine. - pub proof: Vec, + /// "transition/epoch" proof from the engine. + pub proof: Vec, } impl Encodable for PendingTransition { - fn rlp_append(&self, s: &mut RlpStream) { - s.append(&self.proof); - } + fn rlp_append(&self, s: &mut RlpStream) { + s.append(&self.proof); + } } impl Decodable for PendingTransition { - fn decode(rlp: &Rlp) -> Result { - Ok(PendingTransition { - proof: rlp.as_val()?, - }) - } + fn decode(rlp: &Rlp) -> Result { + Ok(PendingTransition { + proof: rlp.as_val()?, + }) + } } - diff --git a/ethcore/types/src/engines/mod.rs b/ethcore/types/src/engines/mod.rs index cc622bbe65b..aa6011250e5 100644 --- a/ethcore/types/src/engines/mod.rs +++ b/ethcore/types/src/engines/mod.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Engine-specific types. @@ -21,8 +21,8 @@ pub mod epoch; /// Fork choice. #[derive(Debug, PartialEq, Eq)] pub enum ForkChoice { - /// Choose the new block. - New, - /// Choose the current best block. - Old, + /// Choose the new block. + New, + /// Choose the current best block. + Old, } diff --git a/ethcore/types/src/filter.rs b/ethcore/types/src/filter.rs index 71e8d394414..84b8a3b64ad 100644 --- a/ethcore/types/src/filter.rs +++ b/ethcore/types/src/filter.rs @@ -1,248 +1,268 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Blockchain filter -use ethereum_types::{H256, Address, Bloom, BloomInput}; +use ethereum_types::{Address, Bloom, BloomInput, H256}; use ids::BlockId; use log_entry::LogEntry; /// Blockchain Filter. #[derive(Debug, PartialEq)] pub struct Filter { - /// Blockchain will be searched from this block. - pub from_block: BlockId, - - /// Till this block. - pub to_block: BlockId, - - /// Search addresses. - /// - /// If None, match all. - /// If specified, log must be produced by one of these addresses. - pub address: Option>, - - /// Search topics. - /// - /// If None, match all. - /// If specified, log must contain one of these topics. - pub topics: Vec>>, - - /// Logs limit - /// - /// If None, return all logs - /// If specified, should only return *last* `n` logs. - pub limit: Option, + /// Blockchain will be searched from this block. + pub from_block: BlockId, + + /// Till this block. + pub to_block: BlockId, + + /// Search addresses. + /// + /// If None, match all. + /// If specified, log must be produced by one of these addresses. + pub address: Option>, + + /// Search topics. + /// + /// If None, match all. + /// If specified, log must contain one of these topics. + pub topics: Vec>>, + + /// Logs limit + /// + /// If None, return all logs + /// If specified, should only return *last* `n` logs. + pub limit: Option, } impl Clone for Filter { - fn clone(&self) -> Self { - let mut topics = [ - None, - None, - None, - None, - ]; - for i in 0..4 { - topics[i] = self.topics[i].clone(); - } - - Filter { - from_block: self.from_block.clone(), - to_block: self.to_block.clone(), - address: self.address.clone(), - topics: topics[..].to_vec(), - limit: self.limit, - } - } + fn clone(&self) -> Self { + let mut topics = [None, None, None, None]; + for i in 0..4 { + topics[i] = self.topics[i].clone(); + } + + Filter { + from_block: self.from_block.clone(), + to_block: self.to_block.clone(), + address: self.address.clone(), + topics: topics[..].to_vec(), + limit: self.limit, + } + } } impl Filter { - /// Returns combinations of each address and topic. - pub fn bloom_possibilities(&self) -> Vec { - let blooms = match self.address { - Some(ref addresses) if !addresses.is_empty() => - addresses.iter() - .map(|ref address| Bloom::from(BloomInput::Raw(address))) - .collect(), - _ => vec![Bloom::default()] - }; - - self.topics.iter().fold(blooms, |bs, topic| match *topic { - None => bs, - Some(ref topics) => bs.into_iter().flat_map(|bloom| { - topics.into_iter().map(|topic| { - let mut b = bloom.clone(); - b.accrue(BloomInput::Raw(topic)); - b - }).collect::>() - }).collect() - }) - } - - /// Returns true if given log entry matches filter. - pub fn matches(&self, log: &LogEntry) -> bool { - let matches = match self.address { - Some(ref addresses) if !addresses.is_empty() => addresses.iter().any(|address| &log.address == address), - _ => true - }; - - matches && self.topics.iter().enumerate().all(|(i, topic)| match *topic { - Some(ref topics) if !topics.is_empty() => topics.iter().any(|topic| log.topics.get(i) == Some(topic)), - _ => true - }) - } + /// Returns combinations of each address and topic. + pub fn bloom_possibilities(&self) -> Vec { + let blooms = match self.address { + Some(ref addresses) if !addresses.is_empty() => addresses + .iter() + .map(|ref address| Bloom::from(BloomInput::Raw(address))) + .collect(), + _ => vec![Bloom::default()], + }; + + self.topics.iter().fold(blooms, |bs, topic| match *topic { + None => bs, + Some(ref topics) => bs + .into_iter() + .flat_map(|bloom| { + topics + .into_iter() + .map(|topic| { + let mut b = bloom.clone(); + b.accrue(BloomInput::Raw(topic)); + b + }) + .collect::>() + }) + .collect(), + }) + } + + /// Returns true if given log entry matches filter. + pub fn matches(&self, log: &LogEntry) -> bool { + let matches = match self.address { + Some(ref addresses) if !addresses.is_empty() => { + addresses.iter().any(|address| &log.address == address) + } + _ => true, + }; + + matches + && self + .topics + .iter() + .enumerate() + .all(|(i, topic)| match *topic { + Some(ref topics) if !topics.is_empty() => { + topics.iter().any(|topic| log.topics.get(i) == Some(topic)) + } + _ => true, + }) + } } #[cfg(test)] mod tests { - use ethereum_types::Bloom; - use filter::Filter; - use ids::BlockId; - use log_entry::LogEntry; - - #[test] - fn test_bloom_possibilities_none() { - let none_filter = Filter { - from_block: BlockId::Earliest, - to_block: BlockId::Latest, - address: None, - topics: vec![None, None, None, None], - limit: None, - }; - - let possibilities = none_filter.bloom_possibilities(); - assert_eq!(possibilities.len(), 1); - assert!(possibilities[0].is_zero()) - } - - // block 399849 - #[test] - fn test_bloom_possibilities_single_address_and_topic() { - let filter = Filter { - from_block: BlockId::Earliest, - to_block: BlockId::Latest, - address: Some(vec!["b372018f3be9e171df0581136b59d2faf73a7d5d".into()]), - topics: vec![ - Some(vec!["ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into()]), - None, - None, - None, - ], - limit: None, - }; - - let possibilities = filter.bloom_possibilities(); - assert_eq!(possibilities, vec!["00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000004000000004000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000".into()] as Vec); - } - - #[test] - fn test_bloom_possibilities_single_address_and_many_topics() { - let filter = Filter { - from_block: BlockId::Earliest, - to_block: BlockId::Latest, - address: Some(vec!["b372018f3be9e171df0581136b59d2faf73a7d5d".into()]), - topics: vec![ - Some(vec!["ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into()]), - Some(vec!["ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into()]), - None, - None, - ], - limit: None, - }; - - let possibilities = filter.bloom_possibilities(); - assert_eq!(possibilities, vec!["00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000004000000004000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000".into()] as Vec); - } - - #[test] - fn test_bloom_possibilites_multiple_addresses_and_topics() { - let filter = Filter { - from_block: BlockId::Earliest, - to_block: BlockId::Latest, - address: Some(vec![ - "b372018f3be9e171df0581136b59d2faf73a7d5d".into(), - "b372018f3be9e171df0581136b59d2faf73a7d5d".into(), - ]), - topics: vec![ - Some(vec![ - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into() - ]), - Some(vec![ - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into() - ]), - Some(vec!["ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into()]), - None - ], - limit: None, - }; - - // number of possibilites should be equal 2 * 2 * 2 * 1 = 8 - let possibilities = filter.bloom_possibilities(); - assert_eq!(possibilities.len(), 8); - assert_eq!(possibilities[0], Bloom::from("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000004000000004000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000")); - } - - #[test] - fn test_filter_matches() { - let filter = Filter { - from_block: BlockId::Earliest, - to_block: BlockId::Latest, - address: Some(vec!["b372018f3be9e171df0581136b59d2faf73a7d5d".into()]), - topics: vec![ - Some(vec!["ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into()]), - Some(vec!["ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa".into()]), - None, - None, - ], - limit: None, - }; - - let entry0 = LogEntry { - address: "b372018f3be9e171df0581136b59d2faf73a7d5d".into(), - topics: vec![ - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa".into(), - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), - ], - data: vec![] - }; - - let entry1 = LogEntry { - address: "b372018f3be9e171df0581136b59d2faf73a7d5e".into(), - topics: vec![ - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa".into(), - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), - ], - data: vec![] - }; - - let entry2 = LogEntry { - address: "b372018f3be9e171df0581136b59d2faf73a7d5d".into(), - topics: vec![ - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), - ], - data: vec![] - }; - - assert_eq!(filter.matches(&entry0), true); - assert_eq!(filter.matches(&entry1), false); - assert_eq!(filter.matches(&entry2), false); - } + use ethereum_types::Bloom; + use filter::Filter; + use ids::BlockId; + use log_entry::LogEntry; + + #[test] + fn test_bloom_possibilities_none() { + let none_filter = Filter { + from_block: BlockId::Earliest, + to_block: BlockId::Latest, + address: None, + topics: vec![None, None, None, None], + limit: None, + }; + + let possibilities = none_filter.bloom_possibilities(); + assert_eq!(possibilities.len(), 1); + assert!(possibilities[0].is_zero()) + } + + // block 399849 + #[test] + fn test_bloom_possibilities_single_address_and_topic() { + let filter = Filter { + from_block: BlockId::Earliest, + to_block: BlockId::Latest, + address: Some(vec!["b372018f3be9e171df0581136b59d2faf73a7d5d".into()]), + topics: vec![ + Some(vec![ + "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), + ]), + None, + None, + None, + ], + limit: None, + }; + + let possibilities = filter.bloom_possibilities(); + assert_eq!(possibilities, vec!["00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000004000000004000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000".into()] as Vec); + } + + #[test] + fn test_bloom_possibilities_single_address_and_many_topics() { + let filter = Filter { + from_block: BlockId::Earliest, + to_block: BlockId::Latest, + address: Some(vec!["b372018f3be9e171df0581136b59d2faf73a7d5d".into()]), + topics: vec![ + Some(vec![ + "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), + ]), + Some(vec![ + "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), + ]), + None, + None, + ], + limit: None, + }; + + let possibilities = filter.bloom_possibilities(); + assert_eq!(possibilities, vec!["00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000004000000004000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000".into()] as Vec); + } + + #[test] + fn test_bloom_possibilites_multiple_addresses_and_topics() { + let filter = Filter { + from_block: BlockId::Earliest, + to_block: BlockId::Latest, + address: Some(vec![ + "b372018f3be9e171df0581136b59d2faf73a7d5d".into(), + "b372018f3be9e171df0581136b59d2faf73a7d5d".into(), + ]), + topics: vec![ + Some(vec![ + "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), + "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), + ]), + Some(vec![ + "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), + "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), + ]), + Some(vec![ + "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), + ]), + None, + ], + limit: None, + }; + + // number of possibilites should be equal 2 * 2 * 2 * 1 = 8 + let possibilities = filter.bloom_possibilities(); + assert_eq!(possibilities.len(), 8); + assert_eq!(possibilities[0], Bloom::from("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000004000000004000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000")); + } + + #[test] + fn test_filter_matches() { + let filter = Filter { + from_block: BlockId::Earliest, + to_block: BlockId::Latest, + address: Some(vec!["b372018f3be9e171df0581136b59d2faf73a7d5d".into()]), + topics: vec![ + Some(vec![ + "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), + ]), + Some(vec![ + "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa".into(), + ]), + None, + None, + ], + limit: None, + }; + + let entry0 = LogEntry { + address: "b372018f3be9e171df0581136b59d2faf73a7d5d".into(), + topics: vec![ + "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), + "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa".into(), + "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), + ], + data: vec![], + }; + + let entry1 = LogEntry { + address: "b372018f3be9e171df0581136b59d2faf73a7d5e".into(), + topics: vec![ + "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), + "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa".into(), + "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), + ], + data: vec![], + }; + + let entry2 = LogEntry { + address: "b372018f3be9e171df0581136b59d2faf73a7d5d".into(), + topics: vec!["ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into()], + data: vec![], + }; + + assert_eq!(filter.matches(&entry0), true); + assert_eq!(filter.matches(&entry1), false); + assert_eq!(filter.matches(&entry2), false); + } } diff --git a/ethcore/types/src/header.rs b/ethcore/types/src/header.rs index 3dfe6ab83b1..f53412becd0 100644 --- a/ethcore/types/src/header.rs +++ b/ethcore/types/src/header.rs @@ -1,47 +1,46 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Block header. -use std::cmp; -use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY_LIST_RLP, keccak}; -use heapsize::HeapSizeOf; -use ethereum_types::{H256, U256, Address, Bloom}; use bytes::Bytes; -use rlp::{Rlp, RlpStream, Encodable, DecoderError, Decodable}; +use ethereum_types::{Address, Bloom, H256, U256}; +use hash::{keccak, KECCAK_EMPTY_LIST_RLP, KECCAK_NULL_RLP}; +use heapsize::HeapSizeOf; +use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; use BlockNumber; /// Semantic boolean for when a seal/signature is included. #[derive(Debug, Clone, Copy)] enum Seal { - /// The seal/signature is included. - With, - /// The seal/signature is not included. - Without, + /// The seal/signature is included. + With, + /// The seal/signature is not included. + Without, } /// Extended block header, wrapping `Header` with finalized and total difficulty information. #[derive(Debug, Clone, PartialEq, Eq)] pub struct ExtendedHeader { - /// The actual header. - pub header: Header, - /// Whether the block underlying this header is considered finalized. - pub is_finalized: bool, - /// The parent block difficulty. - pub parent_total_difficulty: U256, + /// The actual header. + pub header: Header, + /// Whether the block underlying this header is considered finalized. + pub is_finalized: bool, + /// The parent block difficulty. + pub parent_total_difficulty: U256, } /// A block header. @@ -52,364 +51,411 @@ pub struct ExtendedHeader { /// Doesn't do all that much on its own. #[derive(Debug, Clone, Eq)] pub struct Header { - /// Parent hash. - parent_hash: H256, - /// Block timestamp. - timestamp: u64, - /// Block number. - number: BlockNumber, - /// Block author. - author: Address, - - /// Transactions root. - transactions_root: H256, - /// Block uncles hash. - uncles_hash: H256, - /// Block extra data. - extra_data: Bytes, - - /// State root. - state_root: H256, - /// Block receipts root. - receipts_root: H256, - /// Block bloom. - log_bloom: Bloom, - /// Gas used for contracts execution. - gas_used: U256, - /// Block gas limit. - gas_limit: U256, - - /// Block difficulty. - difficulty: U256, - /// Vector of post-RLP-encoded fields. - seal: Vec, - - /// Memoized hash of that header and the seal. - hash: Option, + /// Parent hash. + parent_hash: H256, + /// Block timestamp. + timestamp: u64, + /// Block number. + number: BlockNumber, + /// Block author. + author: Address, + + /// Transactions root. + transactions_root: H256, + /// Block uncles hash. + uncles_hash: H256, + /// Block extra data. + extra_data: Bytes, + + /// State root. + state_root: H256, + /// Block receipts root. + receipts_root: H256, + /// Block bloom. + log_bloom: Bloom, + /// Gas used for contracts execution. + gas_used: U256, + /// Block gas limit. + gas_limit: U256, + + /// Block difficulty. + difficulty: U256, + /// Vector of post-RLP-encoded fields. + seal: Vec, + + /// Memoized hash of that header and the seal. + hash: Option, } impl PartialEq for Header { - fn eq(&self, c: &Header) -> bool { - if let (&Some(ref h1), &Some(ref h2)) = (&self.hash, &c.hash) { - if h1 == h2 { - return true - } - } - - self.parent_hash == c.parent_hash && - self.timestamp == c.timestamp && - self.number == c.number && - self.author == c.author && - self.transactions_root == c.transactions_root && - self.uncles_hash == c.uncles_hash && - self.extra_data == c.extra_data && - self.state_root == c.state_root && - self.receipts_root == c.receipts_root && - self.log_bloom == c.log_bloom && - self.gas_used == c.gas_used && - self.gas_limit == c.gas_limit && - self.difficulty == c.difficulty && - self.seal == c.seal - } + fn eq(&self, c: &Header) -> bool { + if let (&Some(ref h1), &Some(ref h2)) = (&self.hash, &c.hash) { + if h1 == h2 { + return true; + } + } + + self.parent_hash == c.parent_hash + && self.timestamp == c.timestamp + && self.number == c.number + && self.author == c.author + && self.transactions_root == c.transactions_root + && self.uncles_hash == c.uncles_hash + && self.extra_data == c.extra_data + && self.state_root == c.state_root + && self.receipts_root == c.receipts_root + && self.log_bloom == c.log_bloom + && self.gas_used == c.gas_used + && self.gas_limit == c.gas_limit + && self.difficulty == c.difficulty + && self.seal == c.seal + } } impl Default for Header { - fn default() -> Self { - Header { - parent_hash: H256::default(), - timestamp: 0, - number: 0, - author: Address::default(), - - transactions_root: KECCAK_NULL_RLP, - uncles_hash: KECCAK_EMPTY_LIST_RLP, - extra_data: vec![], - - state_root: KECCAK_NULL_RLP, - receipts_root: KECCAK_NULL_RLP, - log_bloom: Bloom::default(), - gas_used: U256::default(), - gas_limit: U256::default(), - - difficulty: U256::default(), - seal: vec![], - hash: None, - } - } + fn default() -> Self { + Header { + parent_hash: H256::default(), + timestamp: 0, + number: 0, + author: Address::default(), + + transactions_root: KECCAK_NULL_RLP, + uncles_hash: KECCAK_EMPTY_LIST_RLP, + extra_data: vec![], + + state_root: KECCAK_NULL_RLP, + receipts_root: KECCAK_NULL_RLP, + log_bloom: Bloom::default(), + gas_used: U256::default(), + gas_limit: U256::default(), + + difficulty: U256::default(), + seal: vec![], + hash: None, + } + } } impl Header { - /// Create a new, default-valued, header. - pub fn new() -> Self { Self::default() } - - /// Get the parent_hash field of the header. - pub fn parent_hash(&self) -> &H256 { &self.parent_hash } - - /// Get the timestamp field of the header. - pub fn timestamp(&self) -> u64 { self.timestamp } - - /// Get the number field of the header. - pub fn number(&self) -> BlockNumber { self.number } - - /// Get the author field of the header. - pub fn author(&self) -> &Address { &self.author } - - /// Get the extra data field of the header. - pub fn extra_data(&self) -> &Bytes { &self.extra_data } - - /// Get the state root field of the header. - pub fn state_root(&self) -> &H256 { &self.state_root } - - /// Get the receipts root field of the header. - pub fn receipts_root(&self) -> &H256 { &self.receipts_root } - - /// Get the log bloom field of the header. - pub fn log_bloom(&self) -> &Bloom { &self.log_bloom } - - /// Get the transactions root field of the header. - pub fn transactions_root(&self) -> &H256 { &self.transactions_root } - - /// Get the uncles hash field of the header. - pub fn uncles_hash(&self) -> &H256 { &self.uncles_hash } - - /// Get the gas used field of the header. - pub fn gas_used(&self) -> &U256 { &self.gas_used } - - /// Get the gas limit field of the header. - pub fn gas_limit(&self) -> &U256 { &self.gas_limit } - - /// Get the difficulty field of the header. - pub fn difficulty(&self) -> &U256 { &self.difficulty } - - /// Get the seal field of the header. - pub fn seal(&self) -> &[Bytes] { &self.seal } - - /// Get the seal field with RLP-decoded values as bytes. - pub fn decode_seal<'a, T: ::std::iter::FromIterator<&'a [u8]>>(&'a self) -> Result { - self.seal.iter().map(|rlp| { - Rlp::new(rlp).data() - }).collect() - } - - /// Set the number field of the header. - pub fn set_parent_hash(&mut self, a: H256) { - change_field(&mut self.hash, &mut self.parent_hash, a); - } - - /// Set the uncles hash field of the header. - pub fn set_uncles_hash(&mut self, a: H256) { - change_field(&mut self.hash, &mut self.uncles_hash, a); - - } - /// Set the state root field of the header. - pub fn set_state_root(&mut self, a: H256) { - change_field(&mut self.hash, &mut self.state_root, a); - } - - /// Set the transactions root field of the header. - pub fn set_transactions_root(&mut self, a: H256) { - change_field(&mut self.hash, &mut self.transactions_root, a); - } - - /// Set the receipts root field of the header. - pub fn set_receipts_root(&mut self, a: H256) { - change_field(&mut self.hash, &mut self.receipts_root, a); - } - - /// Set the log bloom field of the header. - pub fn set_log_bloom(&mut self, a: Bloom) { - change_field(&mut self.hash, &mut self.log_bloom, a); - } - - /// Set the timestamp field of the header. - pub fn set_timestamp(&mut self, a: u64) { - change_field(&mut self.hash, &mut self.timestamp, a); - } - - /// Set the number field of the header. - pub fn set_number(&mut self, a: BlockNumber) { - change_field(&mut self.hash, &mut self.number, a); - } - - /// Set the author field of the header. - pub fn set_author(&mut self, a: Address) { - change_field(&mut self.hash, &mut self.author, a); - } - - /// Set the extra data field of the header. - pub fn set_extra_data(&mut self, a: Bytes) { - change_field(&mut self.hash, &mut self.extra_data, a); - } - - /// Set the gas used field of the header. - pub fn set_gas_used(&mut self, a: U256) { - change_field(&mut self.hash, &mut self.gas_used, a); - } - - /// Set the gas limit field of the header. - pub fn set_gas_limit(&mut self, a: U256) { - change_field(&mut self.hash, &mut self.gas_limit, a); - } - - /// Set the difficulty field of the header. - pub fn set_difficulty(&mut self, a: U256) { - change_field(&mut self.hash, &mut self.difficulty, a); - } - - /// Set the seal field of the header. - pub fn set_seal(&mut self, a: Vec) { - change_field(&mut self.hash, &mut self.seal, a) - } - - /// Get & memoize the hash of this header (keccak of the RLP with seal). - pub fn compute_hash(&mut self) -> H256 { - let hash = self.hash(); - self.hash = Some(hash); - hash - } - - /// Get the hash of this header (keccak of the RLP with seal). - pub fn hash(&self) -> H256 { - self.hash.unwrap_or_else(|| keccak(self.rlp(Seal::With))) - } - - /// Get the hash of the header excluding the seal - pub fn bare_hash(&self) -> H256 { - keccak(self.rlp(Seal::Without)) - } - - /// Encode the header, getting a type-safe wrapper around the RLP. - pub fn encoded(&self) -> ::encoded::Header { - ::encoded::Header::new(self.rlp(Seal::With)) - } - - /// Get the RLP representation of this Header. - fn rlp(&self, with_seal: Seal) -> Bytes { - let mut s = RlpStream::new(); - self.stream_rlp(&mut s, with_seal); - s.out() - } - - /// Place this header into an RLP stream `s`, optionally `with_seal`. - fn stream_rlp(&self, s: &mut RlpStream, with_seal: Seal) { - if let Seal::With = with_seal { - s.begin_list(13 + self.seal.len()); - } else { - s.begin_list(13); - } - - s.append(&self.parent_hash); - s.append(&self.uncles_hash); - s.append(&self.author); - s.append(&self.state_root); - s.append(&self.transactions_root); - s.append(&self.receipts_root); - s.append(&self.log_bloom); - s.append(&self.difficulty); - s.append(&self.number); - s.append(&self.gas_limit); - s.append(&self.gas_used); - s.append(&self.timestamp); - s.append(&self.extra_data); - - if let Seal::With = with_seal { - for b in &self.seal { - s.append_raw(b, 1); - } - } - } + /// Create a new, default-valued, header. + pub fn new() -> Self { + Self::default() + } + + /// Get the parent_hash field of the header. + pub fn parent_hash(&self) -> &H256 { + &self.parent_hash + } + + /// Get the timestamp field of the header. + pub fn timestamp(&self) -> u64 { + self.timestamp + } + + /// Get the number field of the header. + pub fn number(&self) -> BlockNumber { + self.number + } + + /// Get the author field of the header. + pub fn author(&self) -> &Address { + &self.author + } + + /// Get the extra data field of the header. + pub fn extra_data(&self) -> &Bytes { + &self.extra_data + } + + /// Get the state root field of the header. + pub fn state_root(&self) -> &H256 { + &self.state_root + } + + /// Get the receipts root field of the header. + pub fn receipts_root(&self) -> &H256 { + &self.receipts_root + } + + /// Get the log bloom field of the header. + pub fn log_bloom(&self) -> &Bloom { + &self.log_bloom + } + + /// Get the transactions root field of the header. + pub fn transactions_root(&self) -> &H256 { + &self.transactions_root + } + + /// Get the uncles hash field of the header. + pub fn uncles_hash(&self) -> &H256 { + &self.uncles_hash + } + + /// Get the gas used field of the header. + pub fn gas_used(&self) -> &U256 { + &self.gas_used + } + + /// Get the gas limit field of the header. + pub fn gas_limit(&self) -> &U256 { + &self.gas_limit + } + + /// Get the difficulty field of the header. + pub fn difficulty(&self) -> &U256 { + &self.difficulty + } + + /// Get the seal field of the header. + pub fn seal(&self) -> &[Bytes] { + &self.seal + } + + /// Get the seal field with RLP-decoded values as bytes. + pub fn decode_seal<'a, T: ::std::iter::FromIterator<&'a [u8]>>( + &'a self, + ) -> Result { + self.seal.iter().map(|rlp| Rlp::new(rlp).data()).collect() + } + + /// Set the number field of the header. + pub fn set_parent_hash(&mut self, a: H256) { + change_field(&mut self.hash, &mut self.parent_hash, a); + } + + /// Set the uncles hash field of the header. + pub fn set_uncles_hash(&mut self, a: H256) { + change_field(&mut self.hash, &mut self.uncles_hash, a); + } + /// Set the state root field of the header. + pub fn set_state_root(&mut self, a: H256) { + change_field(&mut self.hash, &mut self.state_root, a); + } + + /// Set the transactions root field of the header. + pub fn set_transactions_root(&mut self, a: H256) { + change_field(&mut self.hash, &mut self.transactions_root, a); + } + + /// Set the receipts root field of the header. + pub fn set_receipts_root(&mut self, a: H256) { + change_field(&mut self.hash, &mut self.receipts_root, a); + } + + /// Set the log bloom field of the header. + pub fn set_log_bloom(&mut self, a: Bloom) { + change_field(&mut self.hash, &mut self.log_bloom, a); + } + + /// Set the timestamp field of the header. + pub fn set_timestamp(&mut self, a: u64) { + change_field(&mut self.hash, &mut self.timestamp, a); + } + + /// Set the number field of the header. + pub fn set_number(&mut self, a: BlockNumber) { + change_field(&mut self.hash, &mut self.number, a); + } + + /// Set the author field of the header. + pub fn set_author(&mut self, a: Address) { + change_field(&mut self.hash, &mut self.author, a); + } + + /// Set the extra data field of the header. + pub fn set_extra_data(&mut self, a: Bytes) { + change_field(&mut self.hash, &mut self.extra_data, a); + } + + /// Set the gas used field of the header. + pub fn set_gas_used(&mut self, a: U256) { + change_field(&mut self.hash, &mut self.gas_used, a); + } + + /// Set the gas limit field of the header. + pub fn set_gas_limit(&mut self, a: U256) { + change_field(&mut self.hash, &mut self.gas_limit, a); + } + + /// Set the difficulty field of the header. + pub fn set_difficulty(&mut self, a: U256) { + change_field(&mut self.hash, &mut self.difficulty, a); + } + + /// Set the seal field of the header. + pub fn set_seal(&mut self, a: Vec) { + change_field(&mut self.hash, &mut self.seal, a) + } + + /// Get & memoize the hash of this header (keccak of the RLP with seal). + pub fn compute_hash(&mut self) -> H256 { + let hash = self.hash(); + self.hash = Some(hash); + hash + } + + /// Get the hash of this header (keccak of the RLP with seal). + pub fn hash(&self) -> H256 { + self.hash.unwrap_or_else(|| keccak(self.rlp(Seal::With))) + } + + /// Get the hash of the header excluding the seal + pub fn bare_hash(&self) -> H256 { + keccak(self.rlp(Seal::Without)) + } + + /// Encode the header, getting a type-safe wrapper around the RLP. + pub fn encoded(&self) -> ::encoded::Header { + ::encoded::Header::new(self.rlp(Seal::With)) + } + + /// Get the RLP representation of this Header. + fn rlp(&self, with_seal: Seal) -> Bytes { + let mut s = RlpStream::new(); + self.stream_rlp(&mut s, with_seal); + s.out() + } + + /// Place this header into an RLP stream `s`, optionally `with_seal`. + fn stream_rlp(&self, s: &mut RlpStream, with_seal: Seal) { + if let Seal::With = with_seal { + s.begin_list(13 + self.seal.len()); + } else { + s.begin_list(13); + } + + s.append(&self.parent_hash); + s.append(&self.uncles_hash); + s.append(&self.author); + s.append(&self.state_root); + s.append(&self.transactions_root); + s.append(&self.receipts_root); + s.append(&self.log_bloom); + s.append(&self.difficulty); + s.append(&self.number); + s.append(&self.gas_limit); + s.append(&self.gas_used); + s.append(&self.timestamp); + s.append(&self.extra_data); + + if let Seal::With = with_seal { + for b in &self.seal { + s.append_raw(b, 1); + } + } + } } /// Alter value of given field, reset memoised hash if changed. -fn change_field(hash: &mut Option, field: &mut T, value: T) where T: PartialEq { - if field != &value { - *field = value; - *hash = None; - } +fn change_field(hash: &mut Option, field: &mut T, value: T) +where + T: PartialEq, +{ + if field != &value { + *field = value; + *hash = None; + } } impl Decodable for Header { - fn decode(r: &Rlp) -> Result { - let mut blockheader = Header { - parent_hash: r.val_at(0)?, - uncles_hash: r.val_at(1)?, - author: r.val_at(2)?, - state_root: r.val_at(3)?, - transactions_root: r.val_at(4)?, - receipts_root: r.val_at(5)?, - log_bloom: r.val_at(6)?, - difficulty: r.val_at(7)?, - number: r.val_at(8)?, - gas_limit: r.val_at(9)?, - gas_used: r.val_at(10)?, - timestamp: cmp::min(r.val_at::(11)?, u64::max_value().into()).as_u64(), - extra_data: r.val_at(12)?, - seal: vec![], - hash: keccak(r.as_raw()).into(), - }; - - for i in 13..r.item_count()? { - blockheader.seal.push(r.at(i)?.as_raw().to_vec()) - } - - Ok(blockheader) - } + fn decode(r: &Rlp) -> Result { + let mut blockheader = Header { + parent_hash: r.val_at(0)?, + uncles_hash: r.val_at(1)?, + author: r.val_at(2)?, + state_root: r.val_at(3)?, + transactions_root: r.val_at(4)?, + receipts_root: r.val_at(5)?, + log_bloom: r.val_at(6)?, + difficulty: r.val_at(7)?, + number: r.val_at(8)?, + gas_limit: r.val_at(9)?, + gas_used: r.val_at(10)?, + timestamp: r.val_at(11)?, + extra_data: r.val_at(12)?, + seal: vec![], + hash: keccak(r.as_raw()).into(), + }; + + for i in 13..r.item_count()? { + blockheader.seal.push(r.at(i)?.as_raw().to_vec()) + } + + Ok(blockheader) + } } impl Encodable for Header { - fn rlp_append(&self, s: &mut RlpStream) { - self.stream_rlp(s, Seal::With); - } + fn rlp_append(&self, s: &mut RlpStream) { + self.stream_rlp(s, Seal::With); + } } impl HeapSizeOf for Header { - fn heap_size_of_children(&self) -> usize { - self.extra_data.heap_size_of_children() + self.seal.heap_size_of_children() - } + fn heap_size_of_children(&self) -> usize { + self.extra_data.heap_size_of_children() + self.seal.heap_size_of_children() + } } impl ExtendedHeader { - /// Returns combined difficulty of all ancestors together with the difficulty of this header. - pub fn total_score(&self) -> U256 { - self.parent_total_difficulty + *self.header.difficulty() - } + /// Returns combined difficulty of all ancestors together with the difficulty of this header. + pub fn total_score(&self) -> U256 { + self.parent_total_difficulty + *self.header.difficulty() + } } #[cfg(test)] mod tests { - use rustc_hex::FromHex; - use rlp; - use super::Header; - - #[test] - fn test_header_seal_fields() { - // that's rlp of block header created with ethash engine. - let header_rlp = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap(); - let mix_hash = "a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd".from_hex().unwrap(); - let mix_hash_decoded = "a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd".from_hex().unwrap(); - let nonce = "88ab4e252a7e8c2a23".from_hex().unwrap(); - let nonce_decoded = "ab4e252a7e8c2a23".from_hex().unwrap(); - - let header: Header = rlp::decode(&header_rlp).expect("error decoding header"); - let seal_fields = header.seal.clone(); - assert_eq!(seal_fields.len(), 2); - assert_eq!(seal_fields[0], mix_hash); - assert_eq!(seal_fields[1], nonce); - - let decoded_seal = header.decode_seal::>().unwrap(); - assert_eq!(decoded_seal.len(), 2); - assert_eq!(decoded_seal[0], &*mix_hash_decoded); - assert_eq!(decoded_seal[1], &*nonce_decoded); - } - - #[test] - fn decode_and_encode_header() { - // that's rlp of block header created with ethash engine. - let header_rlp = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap(); - - let header: Header = rlp::decode(&header_rlp).expect("error decoding header"); - let encoded_header = rlp::encode(&header); - - assert_eq!(header_rlp, encoded_header); - } + use super::Header; + use rlp; + use rustc_hex::FromHex; + + #[test] + fn test_header_seal_fields() { + // that's rlp of block header created with ethash engine. + let header_rlp = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap(); + let mix_hash = "a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd" + .from_hex() + .unwrap(); + let mix_hash_decoded = "a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd" + .from_hex() + .unwrap(); + let nonce = "88ab4e252a7e8c2a23".from_hex().unwrap(); + let nonce_decoded = "ab4e252a7e8c2a23".from_hex().unwrap(); + + let header: Header = rlp::decode(&header_rlp).expect("error decoding header"); + let seal_fields = header.seal.clone(); + assert_eq!(seal_fields.len(), 2); + assert_eq!(seal_fields[0], mix_hash); + assert_eq!(seal_fields[1], nonce); + + let decoded_seal = header.decode_seal::>().unwrap(); + assert_eq!(decoded_seal.len(), 2); + assert_eq!(decoded_seal[0], &*mix_hash_decoded); + assert_eq!(decoded_seal[1], &*nonce_decoded); + } + + #[test] + fn decode_and_encode_header() { + // that's rlp of block header created with ethash engine. + let header_rlp = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap(); + + let header: Header = rlp::decode(&header_rlp).expect("error decoding header"); + let encoded_header = rlp::encode(&header); + + assert_eq!(header_rlp, encoded_header); + } + + #[test] + fn reject_header_with_large_timestamp() { + // that's rlp of block header created with ethash engine. + // The encoding contains a large timestamp (295147905179352825856) + let header_rlp = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d891000000000000000000080a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap(); + + // This should fail decoding timestamp + let header: Result = rlp::decode(&header_rlp); + assert_eq!(header.unwrap_err(), rlp::DecoderError::RlpIsTooBig); + } } diff --git a/ethcore/types/src/ids.rs b/ethcore/types/src/ids.rs index 1f099be57da..c117ed3c143 100644 --- a/ethcore/types/src/ids.rs +++ b/ethcore/types/src/ids.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Unique identifiers. @@ -22,40 +22,40 @@ use BlockNumber; /// Uniquely identifies block. #[derive(Debug, PartialEq, Copy, Clone, Hash, Eq)] pub enum BlockId { - /// Block's sha3. - /// Querying by hash is always faster. - Hash(H256), - /// Block number within canon blockchain. - Number(BlockNumber), - /// Earliest block (genesis). - Earliest, - /// Latest mined block. - Latest, + /// Block's sha3. + /// Querying by hash is always faster. + Hash(H256), + /// Block number within canon blockchain. + Number(BlockNumber), + /// Earliest block (genesis). + Earliest, + /// Latest mined block. + Latest, } /// Uniquely identifies transaction. #[derive(Debug, PartialEq, Clone, Hash, Eq)] pub enum TransactionId { - /// Transaction's sha3. - Hash(H256), - /// Block id and transaction index within this block. - /// Querying by block position is always faster. - Location(BlockId, usize) + /// Transaction's sha3. + Hash(H256), + /// Block id and transaction index within this block. + /// Querying by block position is always faster. + Location(BlockId, usize), } /// Uniquely identifies Trace. pub struct TraceId { - /// Transaction - pub transaction: TransactionId, - /// Trace address within transaction. - pub address: Vec, + /// Transaction + pub transaction: TransactionId, + /// Trace address within transaction. + pub address: Vec, } /// Uniquely identifies Uncle. #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct UncleId { - /// Block id. - pub block: BlockId, - /// Position in block. - pub position: usize + /// Block id. + pub block: BlockId, + /// Position in block. + pub position: usize, } diff --git a/ethcore/types/src/lib.rs b/ethcore/types/src/lib.rs index 3223db72206..cfd301eb722 100644 --- a/ethcore/types/src/lib.rs +++ b/ethcore/types/src/lib.rs @@ -1,22 +1,22 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Types used in the public API //! -//! This crate stores Parity Etherem specific types that are +//! This crate stores Open Etherem specific types that are //! COMMONLY used across different separate modules of the codebase. //! It should only focus on data structures, not any logic that relates to them. //! @@ -25,13 +25,14 @@ //! structures from that crate. //! //! NOTE If you can specify your data type in the same crate as your trait, please do that. -//! Don't treat this crate as a bag for any types that we use in Parity Ethereum. +//! Don't treat this crate as a bag for any types that we use in OpenEthereum. //! This one is reserved for types that are shared heavily (like transactions), //! historically this contains types extracted from `ethcore` crate, if possible //! we should try to dissolve that crate in favour of more fine-grained crates, //! by moving the types closer to where they are actually required. -#![warn(missing_docs, unused_extern_crates)] +#![allow(missing_docs)] +#![warn(unused_extern_crates)] extern crate ethereum_types; extern crate ethjson; @@ -58,6 +59,8 @@ pub mod block; pub mod block_status; pub mod blockchain_info; pub mod call_analytics; +pub mod creation_status; +pub mod data_format; pub mod encoded; pub mod engines; pub mod filter; diff --git a/ethcore/types/src/log_entry.rs b/ethcore/types/src/log_entry.rs index a5087b2a003..89b56161b83 100644 --- a/ethcore/types/src/log_entry.rs +++ b/ethcore/types/src/log_entry.rs @@ -1,107 +1,111 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Log entry type definition. -use std::ops::Deref; -use heapsize::HeapSizeOf; use bytes::Bytes; -use ethereum_types::{H256, Address, Bloom, BloomInput}; +use ethereum_types::{Address, Bloom, BloomInput, H256}; +use heapsize::HeapSizeOf; +use std::ops::Deref; -use {BlockNumber}; use ethjson; +use BlockNumber; /// A record of execution for a `LOG` operation. #[derive(Default, Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] pub struct LogEntry { - /// The address of the contract executing at the point of the `LOG` operation. - pub address: Address, - /// The topics associated with the `LOG` operation. - pub topics: Vec, - /// The data associated with the `LOG` operation. - pub data: Bytes, + /// The address of the contract executing at the point of the `LOG` operation. + pub address: Address, + /// The topics associated with the `LOG` operation. + pub topics: Vec, + /// The data associated with the `LOG` operation. + pub data: Bytes, } impl HeapSizeOf for LogEntry { - fn heap_size_of_children(&self) -> usize { - self.topics.heap_size_of_children() + self.data.heap_size_of_children() - } + fn heap_size_of_children(&self) -> usize { + self.topics.heap_size_of_children() + self.data.heap_size_of_children() + } } impl LogEntry { - /// Calculates the bloom of this log entry. - pub fn bloom(&self) -> Bloom { - self.topics.iter().fold(Bloom::from(BloomInput::Raw(&self.address)), |mut b, t| { - b.accrue(BloomInput::Raw(t)); - b - }) - } + /// Calculates the bloom of this log entry. + pub fn bloom(&self) -> Bloom { + self.topics + .iter() + .fold(Bloom::from(BloomInput::Raw(&self.address)), |mut b, t| { + b.accrue(BloomInput::Raw(t)); + b + }) + } } impl From for LogEntry { - fn from(l: ethjson::state::Log) -> Self { - LogEntry { - address: l.address.into(), - topics: l.topics.into_iter().map(Into::into).collect(), - data: l.data.into(), - } - } + fn from(l: ethjson::state::Log) -> Self { + LogEntry { + address: l.address.into(), + topics: l.topics.into_iter().map(Into::into).collect(), + data: l.data.into(), + } + } } /// Log localized in a blockchain. #[derive(Default, Debug, PartialEq, Clone)] pub struct LocalizedLogEntry { - /// Plain log entry. - pub entry: LogEntry, - /// Block in which this log was created. - pub block_hash: H256, - /// Block number. - pub block_number: BlockNumber, - /// Hash of transaction in which this log was created. - pub transaction_hash: H256, - /// Index of transaction within block. - pub transaction_index: usize, - /// Log position in the block. - pub log_index: usize, - /// Log position in the transaction. - pub transaction_log_index: usize, + /// Plain log entry. + pub entry: LogEntry, + /// Block in which this log was created. + pub block_hash: H256, + /// Block number. + pub block_number: BlockNumber, + /// Hash of transaction in which this log was created. + pub transaction_hash: H256, + /// Index of transaction within block. + pub transaction_index: usize, + /// Log position in the block. + pub log_index: usize, + /// Log position in the transaction. + pub transaction_log_index: usize, } impl Deref for LocalizedLogEntry { - type Target = LogEntry; + type Target = LogEntry; - fn deref(&self) -> &Self::Target { - &self.entry - } + fn deref(&self) -> &Self::Target { + &self.entry + } } #[cfg(test)] mod tests { - use ethereum_types::{Bloom, Address}; - use super::LogEntry; + use super::LogEntry; + use ethereum_types::{Address, Bloom}; - #[test] - fn test_empty_log_bloom() { - let bloom = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".parse::().unwrap(); - let address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse::
().unwrap(); - let log = LogEntry { - address: address, - topics: vec![], - data: vec![] - }; - assert_eq!(log.bloom(), bloom); - } + #[test] + fn test_empty_log_bloom() { + let bloom = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".parse::().unwrap(); + let address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" + .parse::
() + .unwrap(); + let log = LogEntry { + address: address, + topics: vec![], + data: vec![], + }; + assert_eq!(log.bloom(), bloom); + } } diff --git a/ethcore/types/src/pruning_info.rs b/ethcore/types/src/pruning_info.rs index 76f775cb7e4..8eb4210446b 100644 --- a/ethcore/types/src/pruning_info.rs +++ b/ethcore/types/src/pruning_info.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Information about portions of the state and chain which the client may serve. //! @@ -23,8 +23,8 @@ /// Client pruning info. See module-level docs for more details. #[derive(Debug, Clone)] pub struct PruningInfo { - /// The first block which everything can be served after. - pub earliest_chain: u64, - /// The first block where state requests may be served. - pub earliest_state: u64, + /// The first block which everything can be served after. + pub earliest_chain: u64, + /// The first block where state requests may be served. + pub earliest_state: u64, } diff --git a/ethcore/types/src/receipt.rs b/ethcore/types/src/receipt.rs index aedb208a78b..916ab602515 100644 --- a/ethcore/types/src/receipt.rs +++ b/ethcore/types/src/receipt.rs @@ -1,224 +1,234 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Receipt -use ethereum_types::{H160, H256, U256, Address, Bloom}; +use ethereum_types::{Address, Bloom, H160, H256, U256}; use heapsize::HeapSizeOf; -use rlp::{Rlp, RlpStream, Encodable, Decodable, DecoderError}; +use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; +use log_entry::{LocalizedLogEntry, LogEntry}; use BlockNumber; -use log_entry::{LogEntry, LocalizedLogEntry}; /// Transaction outcome store in the receipt. #[derive(Debug, Clone, PartialEq, Eq)] pub enum TransactionOutcome { - /// Status and state root are unknown under EIP-98 rules. - Unknown, - /// State root is known. Pre EIP-98 and EIP-658 rules. - StateRoot(H256), - /// Status code is known. EIP-658 rules. - StatusCode(u8), + /// Status and state root are unknown under EIP-98 rules. + Unknown, + /// State root is known. Pre EIP-98 and EIP-658 rules. + StateRoot(H256), + /// Status code is known. EIP-658 rules. + StatusCode(u8), } /// Information describing execution of a transaction. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Receipt { - /// The total gas used in the block following execution of the transaction. - pub gas_used: U256, - /// The OR-wide combination of all logs' blooms for this transaction. - pub log_bloom: Bloom, - /// The logs stemming from this transaction. - pub logs: Vec, - /// Transaction outcome. - pub outcome: TransactionOutcome, + /// The total gas used in the block following execution of the transaction. + pub gas_used: U256, + /// The OR-wide combination of all logs' blooms for this transaction. + pub log_bloom: Bloom, + /// The logs stemming from this transaction. + pub logs: Vec, + /// Transaction outcome. + pub outcome: TransactionOutcome, } impl Receipt { - /// Create a new receipt. - pub fn new(outcome: TransactionOutcome, gas_used: U256, logs: Vec) -> Self { - Self { - gas_used, - log_bloom: logs.iter().fold(Bloom::default(), |mut b, l| { - b.accrue_bloom(&l.bloom()); - b - }), - logs, - outcome, - } - } + /// Create a new receipt. + pub fn new(outcome: TransactionOutcome, gas_used: U256, logs: Vec) -> Self { + Self { + gas_used, + log_bloom: logs.iter().fold(Bloom::default(), |mut b, l| { + b.accrue_bloom(&l.bloom()); + b + }), + logs, + outcome, + } + } } impl Encodable for Receipt { - fn rlp_append(&self, s: &mut RlpStream) { - match self.outcome { - TransactionOutcome::Unknown => { - s.begin_list(3); - }, - TransactionOutcome::StateRoot(ref root) => { - s.begin_list(4); - s.append(root); - }, - TransactionOutcome::StatusCode(ref status_code) => { - s.begin_list(4); - s.append(status_code); - }, - } - s.append(&self.gas_used); - s.append(&self.log_bloom); - s.append_list(&self.logs); - } + fn rlp_append(&self, s: &mut RlpStream) { + match self.outcome { + TransactionOutcome::Unknown => { + s.begin_list(3); + } + TransactionOutcome::StateRoot(ref root) => { + s.begin_list(4); + s.append(root); + } + TransactionOutcome::StatusCode(ref status_code) => { + s.begin_list(4); + s.append(status_code); + } + } + s.append(&self.gas_used); + s.append(&self.log_bloom); + s.append_list(&self.logs); + } } impl Decodable for Receipt { - fn decode(rlp: &Rlp) -> Result { - if rlp.item_count()? == 3 { - Ok(Receipt { - outcome: TransactionOutcome::Unknown, - gas_used: rlp.val_at(0)?, - log_bloom: rlp.val_at(1)?, - logs: rlp.list_at(2)?, - }) - } else { - Ok(Receipt { - gas_used: rlp.val_at(1)?, - log_bloom: rlp.val_at(2)?, - logs: rlp.list_at(3)?, - outcome: { - let first = rlp.at(0)?; - if first.is_data() && first.data()?.len() <= 1 { - TransactionOutcome::StatusCode(first.as_val()?) - } else { - TransactionOutcome::StateRoot(first.as_val()?) - } - } - }) - } - } + fn decode(rlp: &Rlp) -> Result { + if rlp.item_count()? == 3 { + Ok(Receipt { + outcome: TransactionOutcome::Unknown, + gas_used: rlp.val_at(0)?, + log_bloom: rlp.val_at(1)?, + logs: rlp.list_at(2)?, + }) + } else { + Ok(Receipt { + gas_used: rlp.val_at(1)?, + log_bloom: rlp.val_at(2)?, + logs: rlp.list_at(3)?, + outcome: { + let first = rlp.at(0)?; + if first.is_data() && first.data()?.len() <= 1 { + TransactionOutcome::StatusCode(first.as_val()?) + } else { + TransactionOutcome::StateRoot(first.as_val()?) + } + }, + }) + } + } } impl HeapSizeOf for Receipt { - fn heap_size_of_children(&self) -> usize { - self.logs.heap_size_of_children() - } + fn heap_size_of_children(&self) -> usize { + self.logs.heap_size_of_children() + } } /// Receipt with additional info. #[derive(Debug, Clone, PartialEq)] pub struct RichReceipt { - /// Transaction hash. - pub transaction_hash: H256, - /// Transaction index. - pub transaction_index: usize, - /// The total gas used in the block following execution of the transaction. - pub cumulative_gas_used: U256, - /// The gas used in the execution of the transaction. Note the difference of meaning to `Receipt::gas_used`. - pub gas_used: U256, - /// Contract address. - pub contract_address: Option
, - /// Logs - pub logs: Vec, - /// Logs bloom - pub log_bloom: Bloom, - /// Transaction outcome. - pub outcome: TransactionOutcome, + /// Transaction hash. + pub transaction_hash: H256, + /// Transaction index. + pub transaction_index: usize, + /// The total gas used in the block following execution of the transaction. + pub cumulative_gas_used: U256, + /// The gas used in the execution of the transaction. Note the difference of meaning to `Receipt::gas_used`. + pub gas_used: U256, + /// Contract address. + /// NOTE: It is an Option because only `Action::Create` transactions has a contract address + pub contract_address: Option
, + /// Logs + pub logs: Vec, + /// Logs bloom + pub log_bloom: Bloom, + /// Transaction outcome. + pub outcome: TransactionOutcome, + /// Receiver address + /// NOTE: It is an Option because only `Action::Call` transactions has a receiver address + pub to: Option, + /// Sender + pub from: H160, } /// Receipt with additional info. #[derive(Debug, Clone, PartialEq)] pub struct LocalizedReceipt { - /// Transaction hash. - pub transaction_hash: H256, - /// Transaction index. - pub transaction_index: usize, - /// Block hash. - pub block_hash: H256, - /// Block number. - pub block_number: BlockNumber, - /// The total gas used in the block following execution of the transaction. - pub cumulative_gas_used: U256, - /// The gas used in the execution of the transaction. Note the difference of meaning to `Receipt::gas_used`. - pub gas_used: U256, - /// Contract address. - pub contract_address: Option
, - /// Logs - pub logs: Vec, - /// Logs bloom - pub log_bloom: Bloom, - /// Transaction outcome. - pub outcome: TransactionOutcome, - /// Receiver address - pub to: Option, - /// Sender - pub from: H160 + /// Transaction hash. + pub transaction_hash: H256, + /// Transaction index. + pub transaction_index: usize, + /// Block hash. + pub block_hash: H256, + /// Block number. + pub block_number: BlockNumber, + /// The total gas used in the block following execution of the transaction. + pub cumulative_gas_used: U256, + /// The gas used in the execution of the transaction. Note the difference of meaning to `Receipt::gas_used`. + pub gas_used: U256, + /// Contract address. + /// NOTE: It is an Option because only `Action::Create` transactions has a contract address + pub contract_address: Option
, + /// Logs + pub logs: Vec, + /// Logs bloom + pub log_bloom: Bloom, + /// Transaction outcome. + pub outcome: TransactionOutcome, + /// Receiver address + /// NOTE: It is an Option because only `Action::Call` transactions has a receiver address + pub to: Option, + /// Sender + pub from: H160, } #[cfg(test)] mod tests { - use super::{Receipt, TransactionOutcome}; - use log_entry::LogEntry; - - #[test] - fn test_no_state_root() { - let expected = ::rustc_hex::FromHex::from_hex("f9014183040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); - let r = Receipt::new( - TransactionOutcome::Unknown, - 0x40cae.into(), - vec![LogEntry { - address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(), - topics: vec![], - data: vec![0u8; 32] - }] - ); - assert_eq!(&::rlp::encode(&r)[..], &expected[..]); - } - - #[test] - fn test_basic() { - let expected = ::rustc_hex::FromHex::from_hex("f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); - let r = Receipt::new( - TransactionOutcome::StateRoot("2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into()), - 0x40cae.into(), - vec![LogEntry { - address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(), - topics: vec![], - data: vec![0u8; 32] - }] - ); - let encoded = ::rlp::encode(&r); - assert_eq!(&encoded[..], &expected[..]); - let decoded: Receipt = ::rlp::decode(&encoded).expect("decoding receipt failed"); - assert_eq!(decoded, r); - } - - #[test] - fn test_status_code() { - let expected = ::rustc_hex::FromHex::from_hex("f901428083040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); - let r = Receipt::new( - TransactionOutcome::StatusCode(0), - 0x40cae.into(), - vec![LogEntry { - address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(), - topics: vec![], - data: vec![0u8; 32] - }] - ); - let encoded = ::rlp::encode(&r); - assert_eq!(&encoded[..], &expected[..]); - let decoded: Receipt = ::rlp::decode(&encoded).expect("decoding receipt failed"); - assert_eq!(decoded, r); - } + use super::{Receipt, TransactionOutcome}; + use log_entry::LogEntry; + + #[test] + fn test_no_state_root() { + let expected = ::rustc_hex::FromHex::from_hex("f9014183040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let r = Receipt::new( + TransactionOutcome::Unknown, + 0x40cae.into(), + vec![LogEntry { + address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(), + topics: vec![], + data: vec![0u8; 32], + }], + ); + assert_eq!(&::rlp::encode(&r)[..], &expected[..]); + } + + #[test] + fn test_basic() { + let expected = ::rustc_hex::FromHex::from_hex("f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let r = Receipt::new( + TransactionOutcome::StateRoot( + "2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into(), + ), + 0x40cae.into(), + vec![LogEntry { + address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(), + topics: vec![], + data: vec![0u8; 32], + }], + ); + let encoded = ::rlp::encode(&r); + assert_eq!(&encoded[..], &expected[..]); + let decoded: Receipt = ::rlp::decode(&encoded).expect("decoding receipt failed"); + assert_eq!(decoded, r); + } + + #[test] + fn test_status_code() { + let expected = ::rustc_hex::FromHex::from_hex("f901428083040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let r = Receipt::new( + TransactionOutcome::StatusCode(0), + 0x40cae.into(), + vec![LogEntry { + address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(), + topics: vec![], + data: vec![0u8; 32], + }], + ); + let encoded = ::rlp::encode(&r); + assert_eq!(&encoded[..], &expected[..]); + let decoded: Receipt = ::rlp::decode(&encoded).expect("decoding receipt failed"); + assert_eq!(decoded, r); + } } diff --git a/ethcore/types/src/restoration_status.rs b/ethcore/types/src/restoration_status.rs index dea342a3104..d6ef8831542 100644 --- a/ethcore/types/src/restoration_status.rs +++ b/ethcore/types/src/restoration_status.rs @@ -1,42 +1,44 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Restoration status type definition /// Statuses for restorations. #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub enum RestorationStatus { - /// No restoration. - Inactive, - /// Restoration is initalizing - Initializing { - /// Number of chunks done/imported - chunks_done: u32, - }, - /// Ongoing restoration. - Ongoing { - /// Total number of state chunks. - state_chunks: u32, - /// Total number of block chunks. - block_chunks: u32, - /// Number of state chunks completed. - state_chunks_done: u32, - /// Number of block chunks completed. - block_chunks_done: u32, - }, - /// Failed restoration. - Failed, + /// No restoration. + Inactive, + /// Restoration is initializing + Initializing { + /// Number of chunks done/imported + chunks_done: u32, + }, + /// Ongoing restoration. + Ongoing { + /// Block number specified in the manifest. + block_number: u64, + /// Total number of state chunks. + state_chunks: u32, + /// Total number of block chunks. + block_chunks: u32, + /// Number of state chunks completed. + state_chunks_done: u32, + /// Number of block chunks completed. + block_chunks_done: u32, + }, + /// Failed restoration. + Failed, } diff --git a/ethcore/types/src/security_level.rs b/ethcore/types/src/security_level.rs index eb87317e7ff..10439ff02de 100644 --- a/ethcore/types/src/security_level.rs +++ b/ethcore/types/src/security_level.rs @@ -1,40 +1,40 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Indication of how secure the chain is. -use {BlockNumber}; +use BlockNumber; /// Indication of how secure the chain is. #[derive(Debug, PartialEq, Copy, Clone, Hash, Eq)] pub enum SecurityLevel { - /// All blocks from genesis to chain head are known to have valid state transitions and PoW. - FullState, - /// All blocks from genesis to chain head are known to have a valid PoW. - FullProofOfWork, - /// Some recent headers (the argument) are known to have a valid PoW. - PartialProofOfWork(BlockNumber), + /// All blocks from genesis to chain head are known to have valid state transitions and PoW. + FullState, + /// All blocks from genesis to chain head are known to have a valid PoW. + FullProofOfWork, + /// Some recent headers (the argument) are known to have a valid PoW. + PartialProofOfWork(BlockNumber), } impl SecurityLevel { - /// `true` for `FullPoW`/`FullState`. - pub fn is_full(&self) -> bool { - match *self { - SecurityLevel::FullState | SecurityLevel::FullProofOfWork => true, - _ => false, - } - } + /// `true` for `FullPoW`/`FullState`. + pub fn is_full(&self) -> bool { + match *self { + SecurityLevel::FullState | SecurityLevel::FullProofOfWork => true, + _ => false, + } + } } diff --git a/ethcore/types/src/snapshot_manifest.rs b/ethcore/types/src/snapshot_manifest.rs index 8ed19fcfc20..461e2a5bb4b 100644 --- a/ethcore/types/src/snapshot_manifest.rs +++ b/ethcore/types/src/snapshot_manifest.rs @@ -1,78 +1,78 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Snapshot manifest type definition -use ethereum_types::H256; -use rlp::{Rlp, RlpStream, DecoderError}; use bytes::Bytes; +use ethereum_types::H256; +use rlp::{DecoderError, Rlp, RlpStream}; /// Manifest data. #[derive(Debug, Clone, PartialEq, Eq)] pub struct ManifestData { - /// Snapshot format version. - pub version: u64, - /// List of state chunk hashes. - pub state_hashes: Vec, - /// List of block chunk hashes. - pub block_hashes: Vec, - /// The final, expected state root. - pub state_root: H256, - /// Block number this snapshot was taken at. - pub block_number: u64, - /// Block hash this snapshot was taken at. - pub block_hash: H256, + /// Snapshot format version. + pub version: u64, + /// List of state chunk hashes. + pub state_hashes: Vec, + /// List of block chunk hashes. + pub block_hashes: Vec, + /// The final, expected state root. + pub state_root: H256, + /// Block number this snapshot was taken at. + pub block_number: u64, + /// Block hash this snapshot was taken at. + pub block_hash: H256, } impl ManifestData { - /// Encode the manifest data to rlp. - pub fn into_rlp(self) -> Bytes { - let mut stream = RlpStream::new_list(6); - stream.append(&self.version); - stream.append_list(&self.state_hashes); - stream.append_list(&self.block_hashes); - stream.append(&self.state_root); - stream.append(&self.block_number); - stream.append(&self.block_hash); + /// Encode the manifest data to rlp. + pub fn into_rlp(self) -> Bytes { + let mut stream = RlpStream::new_list(6); + stream.append(&self.version); + stream.append_list(&self.state_hashes); + stream.append_list(&self.block_hashes); + stream.append(&self.state_root); + stream.append(&self.block_number); + stream.append(&self.block_hash); - stream.out() - } + stream.out() + } - /// Try to restore manifest data from raw bytes, interpreted as RLP. - pub fn from_rlp(raw: &[u8]) -> Result { - let decoder = Rlp::new(raw); - let (start, version) = if decoder.item_count()? == 5 { - (0, 1) - } else { - (1, decoder.val_at(0)?) - }; + /// Try to restore manifest data from raw bytes, interpreted as RLP. + pub fn from_rlp(raw: &[u8]) -> Result { + let decoder = Rlp::new(raw); + let (start, version) = if decoder.item_count()? == 5 { + (0, 1) + } else { + (1, decoder.val_at(0)?) + }; - let state_hashes: Vec = decoder.list_at(start + 0)?; - let block_hashes: Vec = decoder.list_at(start + 1)?; - let state_root: H256 = decoder.val_at(start + 2)?; - let block_number: u64 = decoder.val_at(start + 3)?; - let block_hash: H256 = decoder.val_at(start + 4)?; + let state_hashes: Vec = decoder.list_at(start + 0)?; + let block_hashes: Vec = decoder.list_at(start + 1)?; + let state_root: H256 = decoder.val_at(start + 2)?; + let block_number: u64 = decoder.val_at(start + 3)?; + let block_hash: H256 = decoder.val_at(start + 4)?; - Ok(ManifestData { - version: version, - state_hashes: state_hashes, - block_hashes: block_hashes, - state_root: state_root, - block_number: block_number, - block_hash: block_hash, - }) - } + Ok(ManifestData { + version: version, + state_hashes: state_hashes, + block_hashes: block_hashes, + state_root: state_root, + block_number: block_number, + block_hash: block_hash, + }) + } } diff --git a/ethcore/types/src/state_diff.rs b/ethcore/types/src/state_diff.rs index 5605719f015..439133c8341 100644 --- a/ethcore/types/src/state_diff.rs +++ b/ethcore/types/src/state_diff.rs @@ -1,55 +1,53 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! State diff module. -use std::fmt; -use std::ops::*; -use std::collections::BTreeMap; -use ethereum_types::Address; use account_diff::*; +use ethereum_types::Address; +use std::{collections::BTreeMap, fmt, ops::*}; /// Expression for the delta between two system states. Encoded the /// delta of every altered account. #[derive(Debug, PartialEq, Eq, Clone)] pub struct StateDiff { - /// Raw diff key-value - pub raw: BTreeMap + /// Raw diff key-value + pub raw: BTreeMap, } impl StateDiff { - /// Get the actual data. - pub fn get(&self) -> &BTreeMap { - &self.raw - } + /// Get the actual data. + pub fn get(&self) -> &BTreeMap { + &self.raw + } } impl fmt::Display for StateDiff { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for (add, acc) in &self.raw { - write!(f, "{} {}: {}", acc.existance(), add, acc)?; - } - Ok(()) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for (add, acc) in &self.raw { + write!(f, "{} {}: {}", acc.existance(), add, acc)?; + } + Ok(()) + } } impl Deref for StateDiff { - type Target = BTreeMap; + type Target = BTreeMap; - fn deref(&self) -> &Self::Target { - &self.raw - } + fn deref(&self) -> &Self::Target { + &self.raw + } } diff --git a/ethcore/types/src/trace_filter.rs b/ethcore/types/src/trace_filter.rs index 8b1d715b42f..5404bad9501 100644 --- a/ethcore/types/src/trace_filter.rs +++ b/ethcore/types/src/trace_filter.rs @@ -1,35 +1,35 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Trace filter related types -use std::ops::Range; use ethereum_types::Address; use ids::BlockId; +use std::ops::Range; /// Easy to use trace filter. pub struct Filter { - /// Range of filtering. - pub range: Range, - /// From address. - pub from_address: Vec
, - /// To address. - pub to_address: Vec
, - /// Output offset - pub after: Option, - /// Output amount - pub count: Option, + /// Range of filtering. + pub range: Range, + /// From address. + pub from_address: Vec
, + /// To address. + pub to_address: Vec
, + /// Output offset + pub after: Option, + /// Output amount + pub count: Option, } diff --git a/ethcore/types/src/transaction/error.rs b/ethcore/types/src/transaction/error.rs index ab10636643f..4d62868c2e4 100644 --- a/ethcore/types/src/transaction/error.rs +++ b/ethcore/types/src/transaction/error.rs @@ -1,20 +1,20 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::{fmt, error}; +use std::{error, fmt}; use ethereum_types::U256; use ethkey; @@ -24,109 +24,123 @@ use unexpected::OutOfBounds; #[derive(Debug, PartialEq, Clone)] /// Errors concerning transaction processing. pub enum Error { - /// Transaction is already imported to the queue - AlreadyImported, - /// Transaction is not valid anymore (state already has higher nonce) - Old, - /// Transaction has too low fee - /// (there is already a transaction with the same sender-nonce but higher gas price) - TooCheapToReplace, - /// Transaction was not imported to the queue because limit has been reached. - LimitReached, - /// Transaction's gas price is below threshold. - InsufficientGasPrice { - /// Minimal expected gas price - minimal: U256, - /// Transaction gas price - got: U256, - }, - /// Transaction's gas is below currently set minimal gas requirement. - InsufficientGas { - /// Minimal expected gas - minimal: U256, - /// Transaction gas - got: U256, - }, - /// Sender doesn't have enough funds to pay for this transaction - InsufficientBalance { - /// Senders balance - balance: U256, - /// Transaction cost - cost: U256, - }, - /// Transactions gas is higher then current gas limit - GasLimitExceeded { - /// Current gas limit - limit: U256, - /// Declared transaction gas - got: U256, - }, - /// Transaction's gas limit (aka gas) is invalid. - InvalidGasLimit(OutOfBounds), - /// Transaction sender is banned. - SenderBanned, - /// Transaction receipient is banned. - RecipientBanned, - /// Contract creation code is banned. - CodeBanned, - /// Invalid chain ID given. - InvalidChainId, - /// Not enough permissions given by permission contract. - NotAllowed, - /// Signature error - InvalidSignature(String), - /// Transaction too big - TooBig, - /// Invalid RLP encoding - InvalidRlp(String), + /// Transaction is already imported to the queue + AlreadyImported, + /// Transaction is not valid anymore (state already has higher nonce) + Old, + /// Transaction was not imported to the queue because limit has been reached. + LimitReached, + /// Transaction's gas price is below threshold. + InsufficientGasPrice { + /// Minimal expected gas price + minimal: U256, + /// Transaction gas price + got: U256, + }, + /// Transaction has too low fee + /// (there is already a transaction with the same sender-nonce but higher gas price) + TooCheapToReplace { + /// previous transaction's gas price + prev: Option, + /// new transaction's gas price + new: Option, + }, + /// Transaction's gas is below currently set minimal gas requirement. + InsufficientGas { + /// Minimal expected gas + minimal: U256, + /// Transaction gas + got: U256, + }, + /// Sender doesn't have enough funds to pay for this transaction + InsufficientBalance { + /// Senders balance + balance: U256, + /// Transaction cost + cost: U256, + }, + /// Transactions gas is higher then current gas limit + GasLimitExceeded { + /// Current gas limit + limit: U256, + /// Declared transaction gas + got: U256, + }, + /// Transaction's gas limit (aka gas) is invalid. + InvalidGasLimit(OutOfBounds), + /// Transaction sender is banned. + SenderBanned, + /// Transaction receipient is banned. + RecipientBanned, + /// Contract creation code is banned. + CodeBanned, + /// Invalid chain ID given. + InvalidChainId, + /// Not enough permissions given by permission contract. + NotAllowed, + /// Signature error + InvalidSignature(String), + /// Transaction too big + TooBig, + /// Invalid RLP encoding + InvalidRlp(String), } impl From for Error { - fn from(err: ethkey::Error) -> Self { - Error::InvalidSignature(format!("{}", err)) - } + fn from(err: ethkey::Error) -> Self { + Error::InvalidSignature(format!("{}", err)) + } } impl From for Error { - fn from(err: rlp::DecoderError) -> Self { - Error::InvalidRlp(format!("{}", err)) - } + fn from(err: rlp::DecoderError) -> Self { + Error::InvalidRlp(format!("{}", err)) + } } impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::Error::*; - let msg = match *self { - AlreadyImported => "Already imported".into(), - Old => "No longer valid".into(), - TooCheapToReplace => "Gas price too low to replace".into(), - LimitReached => "Transaction limit reached".into(), - InsufficientGasPrice { minimal, got } => - format!("Insufficient gas price. Min={}, Given={}", minimal, got), - InsufficientGas { minimal, got } => - format!("Insufficient gas. Min={}, Given={}", minimal, got), - InsufficientBalance { balance, cost } => - format!("Insufficient balance for transaction. Balance={}, Cost={}", - balance, cost), - GasLimitExceeded { limit, got } => - format!("Gas limit exceeded. Limit={}, Given={}", limit, got), - InvalidGasLimit(ref err) => format!("Invalid gas limit. {}", err), - SenderBanned => "Sender is temporarily banned.".into(), - RecipientBanned => "Recipient is temporarily banned.".into(), - CodeBanned => "Contract code is temporarily banned.".into(), - InvalidChainId => "Transaction of this chain ID is not allowed on this chain.".into(), - InvalidSignature(ref err) => format!("Transaction has invalid signature: {}.", err), - NotAllowed => "Sender does not have permissions to execute this type of transction".into(), - TooBig => "Transaction too big".into(), - InvalidRlp(ref err) => format!("Transaction has invalid RLP structure: {}.", err), - }; + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::Error::*; + let msg = match *self { + AlreadyImported => "Already imported".into(), + Old => "No longer valid".into(), + TooCheapToReplace { prev, new } => format!( + "Gas price too low to replace, previous tx gas: {:?}, new tx gas: {:?}", + prev, new + ), + LimitReached => "Transaction limit reached".into(), + InsufficientGasPrice { minimal, got } => { + format!("Insufficient gas price. Min={}, Given={}", minimal, got) + } + InsufficientGas { minimal, got } => { + format!("Insufficient gas. Min={}, Given={}", minimal, got) + } + InsufficientBalance { balance, cost } => format!( + "Insufficient balance for transaction. Balance={}, Cost={}", + balance, cost + ), + GasLimitExceeded { limit, got } => { + format!("Gas limit exceeded. Limit={}, Given={}", limit, got) + } + InvalidGasLimit(ref err) => format!("Invalid gas limit. {}", err), + SenderBanned => "Sender is temporarily banned.".into(), + RecipientBanned => "Recipient is temporarily banned.".into(), + CodeBanned => "Contract code is temporarily banned.".into(), + InvalidChainId => "Transaction of this chain ID is not allowed on this chain.".into(), + InvalidSignature(ref err) => format!("Transaction has invalid signature: {}.", err), + NotAllowed => { + "Sender does not have permissions to execute this type of transction".into() + } + TooBig => "Transaction too big".into(), + InvalidRlp(ref err) => format!("Transaction has invalid RLP structure: {}.", err), + }; - f.write_fmt(format_args!("Transaction error ({})", msg)) - } + f.write_fmt(format_args!("Transaction error ({})", msg)) + } } impl error::Error for Error { - fn description(&self) -> &str { - "Transaction error" - } + fn description(&self) -> &str { + "Transaction error" + } } diff --git a/ethcore/types/src/transaction/mod.rs b/ethcore/types/src/transaction/mod.rs index 4b26b7dc144..5af69712d22 100644 --- a/ethcore/types/src/transaction/mod.rs +++ b/ethcore/types/src/transaction/mod.rs @@ -1,23 +1,22 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Ethereum Transactions mod error; mod transaction; -pub use self::error::Error; -pub use self::transaction::*; +pub use self::{error::Error, transaction::*}; diff --git a/ethcore/types/src/transaction/transaction.rs b/ethcore/types/src/transaction/transaction.rs index 248bc264629..55320d21d54 100644 --- a/ethcore/types/src/transaction/transaction.rs +++ b/ethcore/types/src/transaction/transaction.rs @@ -1,29 +1,29 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Transaction data structure. use std::ops::Deref; -use ethereum_types::{H256, H160, Address, U256}; +use ethereum_types::{Address, H160, H256, U256}; use ethjson; -use ethkey::{self, Signature, Secret, Public, recover, public_to_address}; +use ethkey::{self, public_to_address, recover, Public, Secret, Signature}; use hash::keccak; use heapsize::HeapSizeOf; -use rlp::{self, RlpStream, Rlp, DecoderError, Encodable}; +use rlp::{self, DecoderError, Encodable, Rlp, RlpStream}; use transaction::error; @@ -34,636 +34,711 @@ type BlockNumber = u64; pub const UNSIGNED_SENDER: Address = H160([0xff; 20]); /// System sender address for internal state updates. -pub const SYSTEM_ADDRESS: Address = H160([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,0xff, 0xff, 0xff, 0xff,0xff, 0xff, 0xff, 0xff,0xff, 0xff, 0xff, 0xfe]); +pub const SYSTEM_ADDRESS: Address = H160([ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfe, +]); /// Transaction action type. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Action { - /// Create creates new contract. - Create, - /// Calls contract at given address. - /// In the case of a transfer, this is the receiver's address.' - Call(Address), + /// Create creates new contract. + Create, + /// Calls contract at given address. + /// In the case of a transfer, this is the receiver's address.' + Call(Address), } impl Default for Action { - fn default() -> Action { Action::Create } + fn default() -> Action { + Action::Create + } } impl rlp::Decodable for Action { - fn decode(rlp: &Rlp) -> Result { - if rlp.is_empty() { - Ok(Action::Create) - } else { - Ok(Action::Call(rlp.as_val()?)) - } - } + fn decode(rlp: &Rlp) -> Result { + if rlp.is_empty() { + if rlp.is_data() { + Ok(Action::Create) + } else { + Err(DecoderError::RlpExpectedToBeData) + } + } else { + Ok(Action::Call(rlp.as_val()?)) + } + } } impl rlp::Encodable for Action { - fn rlp_append(&self, s: &mut RlpStream) { - match *self { - Action::Create => s.append_internal(&""), - Action::Call(ref addr) => s.append_internal(addr), - }; - } + fn rlp_append(&self, s: &mut RlpStream) { + match *self { + Action::Create => s.append_internal(&""), + Action::Call(ref addr) => s.append_internal(addr), + }; + } } /// Transaction activation condition. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Condition { - /// Valid at this block number or later. - Number(BlockNumber), - /// Valid at this unix time or later. - Timestamp(u64), + /// Valid at this block number or later. + Number(BlockNumber), + /// Valid at this unix time or later. + Timestamp(u64), } /// Replay protection logic for v part of transaction's signature pub mod signature { - /// Adds chain id into v - pub fn add_chain_replay_protection(v: u64, chain_id: Option) -> u64 { - v + if let Some(n) = chain_id { 35 + n * 2 } else { 27 } - } - - /// Returns refined v - /// 0 if `v` would have been 27 under "Electrum" notation, 1 if 28 or 4 if invalid. - pub fn check_replay_protection(v: u64) -> u8 { - match v { - v if v == 27 => 0, - v if v == 28 => 1, - v if v >= 35 => ((v - 1) % 2) as u8, - _ => 4 - } - } + /// Adds chain id into v + pub fn add_chain_replay_protection(v: u64, chain_id: Option) -> u64 { + v + if let Some(n) = chain_id { + 35 + n * 2 + } else { + 27 + } + } + + /// Returns refined v + /// 0 if `v` would have been 27 under "Electrum" notation, 1 if 28 or 4 if invalid. + pub fn check_replay_protection(v: u64) -> u8 { + match v { + v if v == 27 => 0, + v if v == 28 => 1, + v if v >= 35 => ((v - 1) % 2) as u8, + _ => 4, + } + } } /// A set of information describing an externally-originating message call /// or contract creation operation. #[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct Transaction { - /// Nonce. - pub nonce: U256, - /// Gas price. - pub gas_price: U256, - /// Gas paid up front for transaction execution. - pub gas: U256, - /// Action, can be either call or contract create. - pub action: Action, - /// Transfered value. - pub value: U256, - /// Transaction data. - pub data: Bytes, + /// Nonce. + pub nonce: U256, + /// Gas price. + pub gas_price: U256, + /// Gas paid up front for transaction execution. + pub gas: U256, + /// Action, can be either call or contract create. + pub action: Action, + /// Transfered value. + pub value: U256, + /// Transaction data. + pub data: Bytes, } impl Transaction { - /// Append object with a without signature into RLP stream - pub fn rlp_append_unsigned_transaction(&self, s: &mut RlpStream, chain_id: Option) { - s.begin_list(if chain_id.is_none() { 6 } else { 9 }); - s.append(&self.nonce); - s.append(&self.gas_price); - s.append(&self.gas); - s.append(&self.action); - s.append(&self.value); - s.append(&self.data); - if let Some(n) = chain_id { - s.append(&n); - s.append(&0u8); - s.append(&0u8); - } - } + /// Append object with a without signature into RLP stream + pub fn rlp_append_unsigned_transaction(&self, s: &mut RlpStream, chain_id: Option) { + s.begin_list(if chain_id.is_none() { 6 } else { 9 }); + s.append(&self.nonce); + s.append(&self.gas_price); + s.append(&self.gas); + s.append(&self.action); + s.append(&self.value); + s.append(&self.data); + if let Some(n) = chain_id { + s.append(&n); + s.append(&0u8); + s.append(&0u8); + } + } } impl HeapSizeOf for Transaction { - fn heap_size_of_children(&self) -> usize { - self.data.heap_size_of_children() - } + fn heap_size_of_children(&self) -> usize { + self.data.heap_size_of_children() + } } +#[cfg(any(test, feature = "test-helpers"))] impl From for SignedTransaction { - fn from(t: ethjson::state::Transaction) -> Self { - let to: Option = t.to.into(); - let secret = t.secret.map(|s| Secret::from(s.0)); - let tx = Transaction { - nonce: t.nonce.into(), - gas_price: t.gas_price.into(), - gas: t.gas_limit.into(), - action: match to { - Some(to) => Action::Call(to.into()), - None => Action::Create - }, - value: t.value.into(), - data: t.data.into(), - }; - match secret { - Some(s) => tx.sign(&s, None), - None => tx.null_sign(1), - } - } + fn from(t: ethjson::state::Transaction) -> Self { + let to: Option = t.to.into(); + let secret = t.secret.map(|s| Secret::from(s.0)); + let tx = Transaction { + nonce: t.nonce.into(), + gas_price: t.gas_price.into(), + gas: t.gas_limit.into(), + action: match to { + Some(to) => Action::Call(to.into()), + None => Action::Create, + }, + value: t.value.into(), + data: t.data.into(), + }; + match secret { + Some(s) => tx.sign(&s, None), + None => tx.null_sign(1), + } + } } impl From for UnverifiedTransaction { - fn from(t: ethjson::transaction::Transaction) -> Self { - let to: Option = t.to.into(); - UnverifiedTransaction { - unsigned: Transaction { - nonce: t.nonce.into(), - gas_price: t.gas_price.into(), - gas: t.gas_limit.into(), - action: match to { - Some(to) => Action::Call(to.into()), - None => Action::Create - }, - value: t.value.into(), - data: t.data.into(), - }, - r: t.r.into(), - s: t.s.into(), - v: t.v.into(), - hash: 0.into(), - }.compute_hash() - } + fn from(t: ethjson::transaction::Transaction) -> Self { + let to: Option = t.to.into(); + UnverifiedTransaction { + unsigned: Transaction { + nonce: t.nonce.into(), + gas_price: t.gas_price.into(), + gas: t.gas_limit.into(), + action: match to { + Some(to) => Action::Call(to.into()), + None => Action::Create, + }, + value: t.value.into(), + data: t.data.into(), + }, + r: t.r.into(), + s: t.s.into(), + v: t.v.into(), + hash: 0.into(), + } + .compute_hash() + } } impl Transaction { - /// The message hash of the transaction. - pub fn hash(&self, chain_id: Option) -> H256 { - let mut stream = RlpStream::new(); - self.rlp_append_unsigned_transaction(&mut stream, chain_id); - keccak(stream.as_raw()) - } - - /// Signs the transaction as coming from `sender`. - pub fn sign(self, secret: &Secret, chain_id: Option) -> SignedTransaction { - let sig = ::ethkey::sign(secret, &self.hash(chain_id)) - .expect("data is valid and context has signing capabilities; qed"); - SignedTransaction::new(self.with_signature(sig, chain_id)) - .expect("secret is valid so it's recoverable") - } - - /// Signs the transaction with signature. - pub fn with_signature(self, sig: Signature, chain_id: Option) -> UnverifiedTransaction { - UnverifiedTransaction { - unsigned: self, - r: sig.r().into(), - s: sig.s().into(), - v: signature::add_chain_replay_protection(sig.v() as u64, chain_id), - hash: 0.into(), - }.compute_hash() - } - - /// Useful for test incorrectly signed transactions. - #[cfg(test)] - pub fn invalid_sign(self) -> UnverifiedTransaction { - UnverifiedTransaction { - unsigned: self, - r: U256::one(), - s: U256::one(), - v: 0, - hash: 0.into(), - }.compute_hash() - } - - /// Specify the sender; this won't survive the serialize/deserialize process, but can be cloned. - pub fn fake_sign(self, from: Address) -> SignedTransaction { - SignedTransaction { - transaction: UnverifiedTransaction { - unsigned: self, - r: U256::one(), - s: U256::one(), - v: 0, - hash: 0.into(), - }.compute_hash(), - sender: from, - public: None, - } - } - - /// Add EIP-86 compatible empty signature. - pub fn null_sign(self, chain_id: u64) -> SignedTransaction { - SignedTransaction { - transaction: UnverifiedTransaction { - unsigned: self, - r: U256::zero(), - s: U256::zero(), - v: chain_id, - hash: 0.into(), - }.compute_hash(), - sender: UNSIGNED_SENDER, - public: None, - } - } + /// The message hash of the transaction. + pub fn hash(&self, chain_id: Option) -> H256 { + let mut stream = RlpStream::new(); + self.rlp_append_unsigned_transaction(&mut stream, chain_id); + keccak(stream.as_raw()) + } + + /// Signs the transaction as coming from `sender`. + pub fn sign(self, secret: &Secret, chain_id: Option) -> SignedTransaction { + let sig = ::ethkey::sign(secret, &self.hash(chain_id)) + .expect("data is valid and context has signing capabilities; qed"); + SignedTransaction::new(self.with_signature(sig, chain_id)) + .expect("secret is valid so it's recoverable") + } + + /// Signs the transaction with signature. + pub fn with_signature(self, sig: Signature, chain_id: Option) -> UnverifiedTransaction { + UnverifiedTransaction { + unsigned: self, + r: sig.r().into(), + s: sig.s().into(), + v: signature::add_chain_replay_protection(sig.v() as u64, chain_id), + hash: 0.into(), + } + .compute_hash() + } + + /// Useful for test incorrectly signed transactions. + #[cfg(test)] + pub fn invalid_sign(self) -> UnverifiedTransaction { + UnverifiedTransaction { + unsigned: self, + r: U256::one(), + s: U256::one(), + v: 0, + hash: 0.into(), + } + .compute_hash() + } + + /// Specify the sender; this won't survive the serialize/deserialize process, but can be cloned. + pub fn fake_sign(self, from: Address) -> SignedTransaction { + SignedTransaction { + transaction: UnverifiedTransaction { + unsigned: self, + r: U256::one(), + s: U256::one(), + v: 0, + hash: 0.into(), + } + .compute_hash(), + sender: from, + public: None, + } + } + + /// Legacy EIP-86 compatible empty signature. + /// This method is used in json tests as well as + /// signature verification tests. + #[cfg(any(test, feature = "test-helpers"))] + pub fn null_sign(self, chain_id: u64) -> SignedTransaction { + SignedTransaction { + transaction: UnverifiedTransaction { + unsigned: self, + r: U256::zero(), + s: U256::zero(), + v: chain_id, + hash: 0.into(), + } + .compute_hash(), + sender: UNSIGNED_SENDER, + public: None, + } + } } /// Signed transaction information without verified signature. #[derive(Debug, Clone, Eq, PartialEq)] pub struct UnverifiedTransaction { - /// Plain Transaction. - unsigned: Transaction, - /// The V field of the signature; the LS bit described which half of the curve our point falls - /// in. The MS bits describe which chain this transaction is for. If 27/28, its for all chains. - v: u64, - /// The R field of the signature; helps describe the point on the curve. - r: U256, - /// The S field of the signature; helps describe the point on the curve. - s: U256, - /// Hash of the transaction - hash: H256, + /// Plain Transaction. + unsigned: Transaction, + /// The V field of the signature; the LS bit described which half of the curve our point falls + /// in. The MS bits describe which chain this transaction is for. If 27/28, its for all chains. + v: u64, + /// The R field of the signature; helps describe the point on the curve. + r: U256, + /// The S field of the signature; helps describe the point on the curve. + s: U256, + /// Hash of the transaction + hash: H256, } impl HeapSizeOf for UnverifiedTransaction { - fn heap_size_of_children(&self) -> usize { - self.unsigned.heap_size_of_children() - } + fn heap_size_of_children(&self) -> usize { + self.unsigned.heap_size_of_children() + } } impl Deref for UnverifiedTransaction { - type Target = Transaction; + type Target = Transaction; - fn deref(&self) -> &Self::Target { - &self.unsigned - } + fn deref(&self) -> &Self::Target { + &self.unsigned + } } impl rlp::Decodable for UnverifiedTransaction { - fn decode(d: &Rlp) -> Result { - if d.item_count()? != 9 { - return Err(DecoderError::RlpIncorrectListLen); - } - let hash = keccak(d.as_raw()); - Ok(UnverifiedTransaction { - unsigned: Transaction { - nonce: d.val_at(0)?, - gas_price: d.val_at(1)?, - gas: d.val_at(2)?, - action: d.val_at(3)?, - value: d.val_at(4)?, - data: d.val_at(5)?, - }, - v: d.val_at(6)?, - r: d.val_at(7)?, - s: d.val_at(8)?, - hash: hash, - }) - } + fn decode(d: &Rlp) -> Result { + if d.item_count()? != 9 { + return Err(DecoderError::RlpIncorrectListLen); + } + let hash = keccak(d.as_raw()); + Ok(UnverifiedTransaction { + unsigned: Transaction { + nonce: d.val_at(0)?, + gas_price: d.val_at(1)?, + gas: d.val_at(2)?, + action: d.val_at(3)?, + value: d.val_at(4)?, + data: d.val_at(5)?, + }, + v: d.val_at(6)?, + r: d.val_at(7)?, + s: d.val_at(8)?, + hash, + }) + } } impl rlp::Encodable for UnverifiedTransaction { - fn rlp_append(&self, s: &mut RlpStream) { self.rlp_append_sealed_transaction(s) } + fn rlp_append(&self, s: &mut RlpStream) { + self.rlp_append_sealed_transaction(s) + } } impl UnverifiedTransaction { - /// Used to compute hash of created transactions - fn compute_hash(mut self) -> UnverifiedTransaction { - let hash = keccak(&*self.rlp_bytes()); - self.hash = hash; - self - } - - /// Checks is signature is empty. - pub fn is_unsigned(&self) -> bool { - self.r.is_zero() && self.s.is_zero() - } - - /// Append object with a signature into RLP stream - fn rlp_append_sealed_transaction(&self, s: &mut RlpStream) { - s.begin_list(9); - s.append(&self.nonce); - s.append(&self.gas_price); - s.append(&self.gas); - s.append(&self.action); - s.append(&self.value); - s.append(&self.data); - s.append(&self.v); - s.append(&self.r); - s.append(&self.s); - } - - /// Reference to unsigned part of this transaction. - pub fn as_unsigned(&self) -> &Transaction { - &self.unsigned - } - - /// Returns standardized `v` value (0, 1 or 4 (invalid)) - pub fn standard_v(&self) -> u8 { signature::check_replay_protection(self.v) } - - /// The `v` value that appears in the RLP. - pub fn original_v(&self) -> u64 { self.v } - - /// The chain ID, or `None` if this is a global transaction. - pub fn chain_id(&self) -> Option { - match self.v { - v if self.is_unsigned() => Some(v), - v if v >= 35 => Some((v - 35) / 2), - _ => None, - } - } - - /// Construct a signature object from the sig. - pub fn signature(&self) -> Signature { - Signature::from_rsv(&self.r.into(), &self.s.into(), self.standard_v()) - } - - /// Checks whether the signature has a low 's' value. - pub fn check_low_s(&self) -> Result<(), ethkey::Error> { - if !self.signature().is_low_s() { - Err(ethkey::Error::InvalidSignature.into()) - } else { - Ok(()) - } - } - - /// Get the hash of this transaction (keccak of the RLP). - pub fn hash(&self) -> H256 { - self.hash - } - - /// Recovers the public key of the sender. - pub fn recover_public(&self) -> Result { - Ok(recover(&self.signature(), &self.unsigned.hash(self.chain_id()))?) - } - - /// Verify basic signature params. Does not attempt sender recovery. - pub fn verify_basic(&self, check_low_s: bool, chain_id: Option, allow_empty_signature: bool) -> Result<(), error::Error> { - if check_low_s && !(allow_empty_signature && self.is_unsigned()) { - self.check_low_s()?; - } - // Disallow unsigned transactions in case EIP-86 is disabled. - if !allow_empty_signature && self.is_unsigned() { - return Err(ethkey::Error::InvalidSignature.into()); - } - // EIP-86: Transactions of this form MUST have gasprice = 0, nonce = 0, value = 0, and do NOT increment the nonce of account 0. - if allow_empty_signature && self.is_unsigned() && !(self.gas_price.is_zero() && self.value.is_zero() && self.nonce.is_zero()) { - return Err(ethkey::Error::InvalidSignature.into()) - } - match (self.chain_id(), chain_id) { - (None, _) => {}, - (Some(n), Some(m)) if n == m => {}, - _ => return Err(error::Error::InvalidChainId), - }; - Ok(()) - } + /// Used to compute hash of created transactions + fn compute_hash(mut self) -> UnverifiedTransaction { + let hash = keccak(&*self.rlp_bytes()); + self.hash = hash; + self + } + + /// Checks if the signature is empty. + pub fn is_unsigned(&self) -> bool { + self.r.is_zero() && self.s.is_zero() + } + + /// Append object with a signature into RLP stream + fn rlp_append_sealed_transaction(&self, s: &mut RlpStream) { + s.begin_list(9); + s.append(&self.nonce); + s.append(&self.gas_price); + s.append(&self.gas); + s.append(&self.action); + s.append(&self.value); + s.append(&self.data); + s.append(&self.v); + s.append(&self.r); + s.append(&self.s); + } + + /// Reference to unsigned part of this transaction. + pub fn as_unsigned(&self) -> &Transaction { + &self.unsigned + } + + /// Returns standardized `v` value (0, 1 or 4 (invalid)) + pub fn standard_v(&self) -> u8 { + signature::check_replay_protection(self.v) + } + + /// The `v` value that appears in the RLP. + pub fn original_v(&self) -> u64 { + self.v + } + + /// The chain ID, or `None` if this is a global transaction. + pub fn chain_id(&self) -> Option { + match self.v { + v if self.is_unsigned() => Some(v), + v if v >= 35 => Some((v - 35) / 2), + _ => None, + } + } + + /// Construct a signature object from the sig. + pub fn signature(&self) -> Signature { + Signature::from_rsv(&self.r.into(), &self.s.into(), self.standard_v()) + } + + /// Checks whether the signature has a low 's' value. + pub fn check_low_s(&self) -> Result<(), ethkey::Error> { + if !self.signature().is_low_s() { + Err(ethkey::Error::InvalidSignature.into()) + } else { + Ok(()) + } + } + + /// Get the hash of this transaction (keccak of the RLP). + pub fn hash(&self) -> H256 { + self.hash + } + + /// Recovers the public key of the sender. + pub fn recover_public(&self) -> Result { + Ok(recover( + &self.signature(), + &self.unsigned.hash(self.chain_id()), + )?) + } + + /// Verify basic signature params. Does not attempt sender recovery. + pub fn verify_basic( + &self, + check_low_s: bool, + chain_id: Option, + ) -> Result<(), error::Error> { + if self.is_unsigned() { + return Err(ethkey::Error::InvalidSignature.into()); + } + if check_low_s { + self.check_low_s()?; + } + match (self.chain_id(), chain_id) { + (None, _) => {} + (Some(n), Some(m)) if n == m => {} + _ => return Err(error::Error::InvalidChainId), + }; + Ok(()) + } } /// A `UnverifiedTransaction` with successfully recovered `sender`. #[derive(Debug, Clone, Eq, PartialEq)] pub struct SignedTransaction { - transaction: UnverifiedTransaction, - sender: Address, - public: Option, + transaction: UnverifiedTransaction, + sender: Address, + public: Option, } impl HeapSizeOf for SignedTransaction { - fn heap_size_of_children(&self) -> usize { - self.transaction.heap_size_of_children() - } + fn heap_size_of_children(&self) -> usize { + self.transaction.heap_size_of_children() + } } impl rlp::Encodable for SignedTransaction { - fn rlp_append(&self, s: &mut RlpStream) { self.transaction.rlp_append_sealed_transaction(s) } + fn rlp_append(&self, s: &mut RlpStream) { + self.transaction.rlp_append_sealed_transaction(s) + } } impl Deref for SignedTransaction { - type Target = UnverifiedTransaction; - fn deref(&self) -> &Self::Target { - &self.transaction - } + type Target = UnverifiedTransaction; + fn deref(&self) -> &Self::Target { + &self.transaction + } } impl From for UnverifiedTransaction { - fn from(tx: SignedTransaction) -> Self { - tx.transaction - } + fn from(tx: SignedTransaction) -> Self { + tx.transaction + } } impl SignedTransaction { - /// Try to verify transaction and recover sender. - pub fn new(transaction: UnverifiedTransaction) -> Result { - if transaction.is_unsigned() { - Ok(SignedTransaction { - transaction: transaction, - sender: UNSIGNED_SENDER, - public: None, - }) - } else { - let public = transaction.recover_public()?; - let sender = public_to_address(&public); - Ok(SignedTransaction { - transaction: transaction, - sender: sender, - public: Some(public), - }) - } - } - - /// Returns transaction sender. - pub fn sender(&self) -> Address { - self.sender - } - - /// Returns a public key of the sender. - pub fn public_key(&self) -> Option { - self.public - } - - /// Checks is signature is empty. - pub fn is_unsigned(&self) -> bool { - self.transaction.is_unsigned() - } - - /// Deconstructs this transaction back into `UnverifiedTransaction` - pub fn deconstruct(self) -> (UnverifiedTransaction, Address, Option) { - (self.transaction, self.sender, self.public) - } + /// Try to verify transaction and recover sender. + pub fn new(transaction: UnverifiedTransaction) -> Result { + if transaction.is_unsigned() { + return Err(ethkey::Error::InvalidSignature); + } + let public = transaction.recover_public()?; + let sender = public_to_address(&public); + Ok(SignedTransaction { + transaction, + sender, + public: Some(public), + }) + } + + /// Returns transaction sender. + pub fn sender(&self) -> Address { + self.sender + } + + /// Returns a public key of the sender. + pub fn public_key(&self) -> Option { + self.public + } + + /// Checks is signature is empty. + pub fn is_unsigned(&self) -> bool { + self.transaction.is_unsigned() + } + + /// Deconstructs this transaction back into `UnverifiedTransaction` + pub fn deconstruct(self) -> (UnverifiedTransaction, Address, Option) { + (self.transaction, self.sender, self.public) + } } /// Signed Transaction that is a part of canon blockchain. #[derive(Debug, Clone, PartialEq, Eq)] pub struct LocalizedTransaction { - /// Signed part. - pub signed: UnverifiedTransaction, - /// Block number. - pub block_number: BlockNumber, - /// Block hash. - pub block_hash: H256, - /// Transaction index within block. - pub transaction_index: usize, - /// Cached sender - pub cached_sender: Option
, + /// Signed part. + pub signed: UnverifiedTransaction, + /// Block number. + pub block_number: BlockNumber, + /// Block hash. + pub block_hash: H256, + /// Transaction index within block. + pub transaction_index: usize, + /// Cached sender + pub cached_sender: Option
, } impl LocalizedTransaction { - /// Returns transaction sender. - /// Panics if `LocalizedTransaction` is constructed using invalid `UnverifiedTransaction`. - pub fn sender(&mut self) -> Address { - if let Some(sender) = self.cached_sender { - return sender; - } - if self.is_unsigned() { - return UNSIGNED_SENDER.clone(); - } - let sender = public_to_address(&self.recover_public() + /// Returns transaction sender. + /// Panics if `LocalizedTransaction` is constructed using invalid `UnverifiedTransaction`. + pub fn sender(&mut self) -> Address { + if let Some(sender) = self.cached_sender { + return sender; + } + if self.is_unsigned() { + return UNSIGNED_SENDER.clone(); + } + let sender = public_to_address(&self.recover_public() .expect("LocalizedTransaction is always constructed from transaction from blockchain; Blockchain only stores verified transactions; qed")); - self.cached_sender = Some(sender); - sender - } + self.cached_sender = Some(sender); + sender + } } impl Deref for LocalizedTransaction { - type Target = UnverifiedTransaction; + type Target = UnverifiedTransaction; - fn deref(&self) -> &Self::Target { - &self.signed - } + fn deref(&self) -> &Self::Target { + &self.signed + } } /// Queued transaction with additional information. #[derive(Debug, Clone, PartialEq, Eq)] pub struct PendingTransaction { - /// Signed transaction data. - pub transaction: SignedTransaction, - /// To be activated at this condition. `None` for immediately. - pub condition: Option, + /// Signed transaction data. + pub transaction: SignedTransaction, + /// To be activated at this condition. `None` for immediately. + pub condition: Option, } impl PendingTransaction { - /// Create a new pending transaction from signed transaction. - pub fn new(signed: SignedTransaction, condition: Option) -> Self { - PendingTransaction { - transaction: signed, - condition: condition, - } - } + /// Create a new pending transaction from signed transaction. + pub fn new(signed: SignedTransaction, condition: Option) -> Self { + PendingTransaction { + transaction: signed, + condition: condition, + } + } } impl Deref for PendingTransaction { - type Target = SignedTransaction; + type Target = SignedTransaction; - fn deref(&self) -> &SignedTransaction { &self.transaction } + fn deref(&self) -> &SignedTransaction { + &self.transaction + } } impl From for PendingTransaction { - fn from(t: SignedTransaction) -> Self { - PendingTransaction { - transaction: t, - condition: None, - } - } + fn from(t: SignedTransaction) -> Self { + PendingTransaction { + transaction: t, + condition: None, + } + } } #[cfg(test)] mod tests { - use super::*; - use ethereum_types::U256; - use hash::keccak; - - #[test] - fn sender_test() { - let bytes = ::rustc_hex::FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap(); - let t: UnverifiedTransaction = rlp::decode(&bytes).expect("decoding UnverifiedTransaction failed"); - assert_eq!(t.data, b""); - assert_eq!(t.gas, U256::from(0x5208u64)); - assert_eq!(t.gas_price, U256::from(0x01u64)); - assert_eq!(t.nonce, U256::from(0x00u64)); - if let Action::Call(ref to) = t.action { - assert_eq!(*to, "095e7baea6a6c7c4c2dfeb977efac326af552d87".into()); - } else { panic!(); } - assert_eq!(t.value, U256::from(0x0au64)); - assert_eq!(public_to_address(&t.recover_public().unwrap()), "0f65fe9276bc9a24ae7083ae28e2660ef72df99e".into()); - assert_eq!(t.chain_id(), None); - } - - #[test] - fn signing_eip155_zero_chainid() { - use ethkey::{Random, Generator}; - - let key = Random.generate().unwrap(); - let t = Transaction { - action: Action::Create, - nonce: U256::from(42), - gas_price: U256::from(3000), - gas: U256::from(50_000), - value: U256::from(1), - data: b"Hello!".to_vec() - }; - - let hash = t.hash(Some(0)); - let sig = ::ethkey::sign(&key.secret(), &hash).unwrap(); - let u = t.with_signature(sig, Some(0)); - - assert!(SignedTransaction::new(u).is_ok()); - } - - #[test] - fn signing() { - use ethkey::{Random, Generator}; - - let key = Random.generate().unwrap(); - let t = Transaction { - action: Action::Create, - nonce: U256::from(42), - gas_price: U256::from(3000), - gas: U256::from(50_000), - value: U256::from(1), - data: b"Hello!".to_vec() - }.sign(&key.secret(), None); - assert_eq!(Address::from(keccak(key.public())), t.sender()); - assert_eq!(t.chain_id(), None); - } - - #[test] - fn fake_signing() { - let t = Transaction { - action: Action::Create, - nonce: U256::from(42), - gas_price: U256::from(3000), - gas: U256::from(50_000), - value: U256::from(1), - data: b"Hello!".to_vec() - }.fake_sign(Address::from(0x69)); - assert_eq!(Address::from(0x69), t.sender()); - assert_eq!(t.chain_id(), None); - - let t = t.clone(); - assert_eq!(Address::from(0x69), t.sender()); - assert_eq!(t.chain_id(), None); - } - - #[test] - fn should_recover_from_chain_specific_signing() { - use ethkey::{Random, Generator}; - let key = Random.generate().unwrap(); - let t = Transaction { - action: Action::Create, - nonce: U256::from(42), - gas_price: U256::from(3000), - gas: U256::from(50_000), - value: U256::from(1), - data: b"Hello!".to_vec() - }.sign(&key.secret(), Some(69)); - assert_eq!(Address::from(keccak(key.public())), t.sender()); - assert_eq!(t.chain_id(), Some(69)); - } - - #[test] - fn should_agree_with_vitalik() { - use rustc_hex::FromHex; - - let test_vector = |tx_data: &str, address: &'static str| { - let signed = rlp::decode(&FromHex::from_hex(tx_data).unwrap()).expect("decoding tx data failed"); - let signed = SignedTransaction::new(signed).unwrap(); - assert_eq!(signed.sender(), address.into()); - println!("chainid: {:?}", signed.chain_id()); - }; - - test_vector("f864808504a817c800825208943535353535353535353535353535353535353535808025a0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116da0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d", "0xf0f6f18bca1b28cd68e4357452947e021241e9ce"); - test_vector("f864018504a817c80182a410943535353535353535353535353535353535353535018025a0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bcaa0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6", "0x23ef145a395ea3fa3deb533b8a9e1b4c6c25d112"); - test_vector("f864028504a817c80282f618943535353535353535353535353535353535353535088025a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5", "0x2e485e0c23b4c3c542628a5f672eeab0ad4888be"); - test_vector("f865038504a817c803830148209435353535353535353535353535353535353535351b8025a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4e0a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de", "0x82a88539669a3fd524d669e858935de5e5410cf0"); - test_vector("f865048504a817c80483019a28943535353535353535353535353535353535353535408025a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c063a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c060", "0xf9358f2538fd5ccfeb848b64a96b743fcc930554"); - test_vector("f865058504a817c8058301ec309435353535353535353535353535353535353535357d8025a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1", "0xa8f7aba377317440bc5b26198a363ad22af1f3a4"); - test_vector("f866068504a817c80683023e3894353535353535353535353535353535353535353581d88025a06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2fa06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2d", "0xf1f571dc362a0e5b2696b8e775f8491d3e50de35"); - test_vector("f867078504a817c807830290409435353535353535353535353535353535353535358201578025a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021", "0xd37922162ab7cea97c97a87551ed02c9a38b7332"); - test_vector("f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10", "0x9bddad43f934d313c2b79ca28a432dd2b7281029"); - test_vector("f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb", "0x3c24d7329e92f84f08556ceb6df1cdb0104ca49f"); - } + use super::*; + use ethereum_types::U256; + use hash::keccak; + + #[test] + fn sender_test() { + let bytes = ::rustc_hex::FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap(); + let t: UnverifiedTransaction = + rlp::decode(&bytes).expect("decoding UnverifiedTransaction failed"); + assert_eq!(t.data, b""); + assert_eq!(t.gas, U256::from(0x5208u64)); + assert_eq!(t.gas_price, U256::from(0x01u64)); + assert_eq!(t.nonce, U256::from(0x00u64)); + if let Action::Call(ref to) = t.action { + assert_eq!(*to, "095e7baea6a6c7c4c2dfeb977efac326af552d87".into()); + } else { + panic!(); + } + assert_eq!(t.value, U256::from(0x0au64)); + assert_eq!( + public_to_address(&t.recover_public().unwrap()), + "0f65fe9276bc9a24ae7083ae28e2660ef72df99e".into() + ); + assert_eq!(t.chain_id(), None); + } + + #[test] + fn empty_atom_as_create_action() { + let empty_atom = [0x80]; + let action: Action = rlp::decode(&empty_atom).unwrap(); + assert_eq!(action, Action::Create); + } + + #[test] + fn empty_list_as_create_action_rejected() { + let empty_list = [0xc0]; + let action: Result = rlp::decode(&empty_list); + assert_eq!(action, Err(DecoderError::RlpExpectedToBeData)); + } + + #[test] + fn signing_eip155_zero_chainid() { + use ethkey::{Generator, Random}; + + let key = Random.generate().unwrap(); + let t = Transaction { + action: Action::Create, + nonce: U256::from(42), + gas_price: U256::from(3000), + gas: U256::from(50_000), + value: U256::from(1), + data: b"Hello!".to_vec(), + }; + + let hash = t.hash(Some(0)); + let sig = ::ethkey::sign(&key.secret(), &hash).unwrap(); + let u = t.with_signature(sig, Some(0)); + + assert!(SignedTransaction::new(u).is_ok()); + } + + #[test] + fn signing() { + use ethkey::{Generator, Random}; + + let key = Random.generate().unwrap(); + let t = Transaction { + action: Action::Create, + nonce: U256::from(42), + gas_price: U256::from(3000), + gas: U256::from(50_000), + value: U256::from(1), + data: b"Hello!".to_vec(), + } + .sign(&key.secret(), None); + assert_eq!(Address::from(keccak(key.public())), t.sender()); + assert_eq!(t.chain_id(), None); + } + + #[test] + fn fake_signing() { + let t = Transaction { + action: Action::Create, + nonce: U256::from(42), + gas_price: U256::from(3000), + gas: U256::from(50_000), + value: U256::from(1), + data: b"Hello!".to_vec(), + } + .fake_sign(Address::from(0x69)); + assert_eq!(Address::from(0x69), t.sender()); + assert_eq!(t.chain_id(), None); + + let t = t.clone(); + assert_eq!(Address::from(0x69), t.sender()); + assert_eq!(t.chain_id(), None); + } + + #[test] + fn should_reject_null_signature() { + use std::str::FromStr; + let t = Transaction { + nonce: U256::zero(), + gas_price: U256::from(10000000000u64), + gas: U256::from(21000), + action: Action::Call( + Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(), + ), + value: U256::from(1), + data: vec![], + } + .null_sign(1); + + let res = SignedTransaction::new(t.transaction); + match res { + Err(ethkey::Error::InvalidSignature) => {} + _ => panic!("null signature should be rejected"), + } + } + + #[test] + fn should_recover_from_chain_specific_signing() { + use ethkey::{Generator, Random}; + let key = Random.generate().unwrap(); + let t = Transaction { + action: Action::Create, + nonce: U256::from(42), + gas_price: U256::from(3000), + gas: U256::from(50_000), + value: U256::from(1), + data: b"Hello!".to_vec(), + } + .sign(&key.secret(), Some(69)); + assert_eq!(Address::from(keccak(key.public())), t.sender()); + assert_eq!(t.chain_id(), Some(69)); + } + + #[test] + fn should_agree_with_vitalik() { + use rustc_hex::FromHex; + + let test_vector = |tx_data: &str, address: &'static str| { + let signed = + rlp::decode(&FromHex::from_hex(tx_data).unwrap()).expect("decoding tx data failed"); + let signed = SignedTransaction::new(signed).unwrap(); + assert_eq!(signed.sender(), address.into()); + println!("chainid: {:?}", signed.chain_id()); + }; + + test_vector("f864808504a817c800825208943535353535353535353535353535353535353535808025a0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116da0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d", "0xf0f6f18bca1b28cd68e4357452947e021241e9ce"); + test_vector("f864018504a817c80182a410943535353535353535353535353535353535353535018025a0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bcaa0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6", "0x23ef145a395ea3fa3deb533b8a9e1b4c6c25d112"); + test_vector("f864028504a817c80282f618943535353535353535353535353535353535353535088025a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5", "0x2e485e0c23b4c3c542628a5f672eeab0ad4888be"); + test_vector("f865038504a817c803830148209435353535353535353535353535353535353535351b8025a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4e0a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de", "0x82a88539669a3fd524d669e858935de5e5410cf0"); + test_vector("f865048504a817c80483019a28943535353535353535353535353535353535353535408025a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c063a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c060", "0xf9358f2538fd5ccfeb848b64a96b743fcc930554"); + test_vector("f865058504a817c8058301ec309435353535353535353535353535353535353535357d8025a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1", "0xa8f7aba377317440bc5b26198a363ad22af1f3a4"); + test_vector("f866068504a817c80683023e3894353535353535353535353535353535353535353581d88025a06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2fa06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2d", "0xf1f571dc362a0e5b2696b8e775f8491d3e50de35"); + test_vector("f867078504a817c807830290409435353535353535353535353535353535353535358201578025a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021", "0xd37922162ab7cea97c97a87551ed02c9a38b7332"); + test_vector("f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10", "0x9bddad43f934d313c2b79ca28a432dd2b7281029"); + test_vector("f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb", "0x3c24d7329e92f84f08556ceb6df1cdb0104ca49f"); + } } diff --git a/ethcore/types/src/tree_route.rs b/ethcore/types/src/tree_route.rs index 0386472b858..c023cd33c75 100644 --- a/ethcore/types/src/tree_route.rs +++ b/ethcore/types/src/tree_route.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Tree route info type definition @@ -21,12 +21,12 @@ use ethereum_types::H256; /// Represents a tree route between `from` block and `to` block: #[derive(Debug)] pub struct TreeRoute { - /// A vector of hashes of all blocks, ordered from `from` to `to`. - pub blocks: Vec, - /// Best common ancestor of these blocks. - pub ancestor: H256, - /// An index where best common ancestor would be. - pub index: usize, - /// Whether it has finalized blocks from `from` (inclusive) to `ancestor` (exclusive). - pub is_from_route_finalized: bool, + /// A vector of hashes of all blocks, ordered from `from` to `to`. + pub blocks: Vec, + /// Best common ancestor of these blocks. + pub ancestor: H256, + /// An index where best common ancestor would be. + pub index: usize, + /// Whether it has finalized blocks from `from` (inclusive) to `ancestor` (exclusive). + pub is_from_route_finalized: bool, } diff --git a/ethcore/types/src/verification_queue_info.rs b/ethcore/types/src/verification_queue_info.rs index a855fee6a17..7baba83c6af 100644 --- a/ethcore/types/src/verification_queue_info.rs +++ b/ethcore/types/src/verification_queue_info.rs @@ -1,53 +1,58 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Verification queue info types /// Verification queue status #[derive(Debug, Clone)] pub struct VerificationQueueInfo { - /// Number of queued items pending verification - pub unverified_queue_size: usize, - /// Number of verified queued items pending import - pub verified_queue_size: usize, - /// Number of items being verified - pub verifying_queue_size: usize, - /// Configured maximum number of items in the queue - pub max_queue_size: usize, - /// Configured maximum number of bytes to use - pub max_mem_use: usize, - /// Heap memory used in bytes - pub mem_used: usize, + /// Number of queued items pending verification + pub unverified_queue_size: usize, + /// Number of verified queued items pending import + pub verified_queue_size: usize, + /// Number of items being verified + pub verifying_queue_size: usize, + /// Configured maximum number of items in the queue + pub max_queue_size: usize, + /// Configured maximum number of bytes to use + pub max_mem_use: usize, + /// Heap memory used in bytes + pub mem_used: usize, } impl VerificationQueueInfo { - /// The total size of the queues. - pub fn total_queue_size(&self) -> usize { self.unverified_queue_size + self.verified_queue_size + self.verifying_queue_size } - - /// The size of the unverified and verifying queues. - pub fn incomplete_queue_size(&self) -> usize { self.unverified_queue_size + self.verifying_queue_size } - - /// Indicates that queue is full - pub fn is_full(&self) -> bool { - self.unverified_queue_size + self.verified_queue_size + self.verifying_queue_size > self.max_queue_size || - self.mem_used > self.max_mem_use - } - - /// Indicates that queue is empty - pub fn is_empty(&self) -> bool { - self.unverified_queue_size + self.verified_queue_size + self.verifying_queue_size == 0 - } + /// The total size of the queues. + pub fn total_queue_size(&self) -> usize { + self.unverified_queue_size + self.verified_queue_size + self.verifying_queue_size + } + + /// The size of the unverified and verifying queues. + pub fn incomplete_queue_size(&self) -> usize { + self.unverified_queue_size + self.verifying_queue_size + } + + /// Indicates that queue is full + pub fn is_full(&self) -> bool { + self.unverified_queue_size + self.verified_queue_size + self.verifying_queue_size + > self.max_queue_size + || self.mem_used > self.max_mem_use + } + + /// Indicates that queue is empty + pub fn is_empty(&self) -> bool { + self.unverified_queue_size + self.verified_queue_size + self.verifying_queue_size == 0 + } } diff --git a/ethcore/types/src/views/block.rs b/ethcore/types/src/views/block.rs index 9ad67ddd66b..8db0a364c5b 100644 --- a/ethcore/types/src/views/block.rs +++ b/ethcore/types/src/views/block.rs @@ -1,192 +1,209 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! View onto block rlp. +use super::ViewRlp; use bytes::Bytes; use ethereum_types::H256; use hash::keccak; use header::Header; -use transaction::{UnverifiedTransaction, LocalizedTransaction}; -use views::{TransactionView, HeaderView}; -use super::ViewRlp; +use transaction::{LocalizedTransaction, UnverifiedTransaction}; +use views::{HeaderView, TransactionView}; /// View onto block rlp. pub struct BlockView<'a> { - rlp: ViewRlp<'a> + rlp: ViewRlp<'a>, } impl<'a> BlockView<'a> { - /// Creates new view onto block from rlp. - /// Use the `view!` macro to create this view in order to capture debugging info. - /// - /// # Example - /// - /// ``` - /// #[macro_use] - /// extern crate common_types as types; - /// - /// use types::views::{BlockView}; - /// - /// fn main() { - /// let bytes : &[u8] = &[]; - /// let block_view = view!(BlockView, bytes); - /// } - /// ``` - pub fn new(rlp: ViewRlp<'a>) -> BlockView<'a> { - BlockView { - rlp: rlp - } - } - - /// Block header hash. - pub fn hash(&self) -> H256 { - self.header_view().hash() - } - - /// Return reference to underlaying rlp. - pub fn rlp(&self) -> &ViewRlp<'a> { - &self.rlp - } - - /// Create new Header object from header rlp. - pub fn header(&self) -> Header { - self.rlp.val_at(0) - } - - /// Return header rlp. - pub fn header_rlp(&self) -> ViewRlp<'a> { - self.rlp.at(0) - } - - /// Create new header view obto block head rlp. - pub fn header_view(&self) -> HeaderView<'a> { - HeaderView::new(self.header_rlp()) - } - - /// Return List of transactions in given block. - pub fn transactions(&self) -> Vec { - self.rlp.list_at(1) - } - - /// Return List of transactions with additional localization info. - pub fn localized_transactions(&self) -> Vec { - let header = self.header_view(); - let block_hash = header.hash(); - let block_number = header.number(); - self.transactions() - .into_iter() - .enumerate() - .map(|(i, t)| LocalizedTransaction { - signed: t, - block_hash: block_hash.clone(), - block_number: block_number, - transaction_index: i, - cached_sender: None, - }).collect() - } - - /// Return the raw rlp for the transactions in the given block. - pub fn transactions_rlp(&self) -> ViewRlp<'a> { - self.rlp.at(1) - } - - /// Return number of transactions in given block, without deserializing them. - pub fn transactions_count(&self) -> usize { - self.transactions_rlp().iter().count() - } - - /// Return List of transactions in given block. - pub fn transaction_views(&self) -> Vec> { - self.transactions_rlp().iter().map(TransactionView::new).collect() - } - - /// Return transaction hashes. - pub fn transaction_hashes(&self) -> Vec { - self.transactions_rlp().iter().map(|rlp| keccak(rlp.as_raw())).collect() - } - - /// Returns transaction at given index without deserializing unnecessary data. - pub fn transaction_at(&self, index: usize) -> Option { - self.transactions_rlp().iter().nth(index).map(|rlp| rlp.as_val()) - } - - /// Returns localized transaction at given index. - pub fn localized_transaction_at(&self, index: usize) -> Option { - let header = self.header_view(); - let block_hash = header.hash(); - let block_number = header.number(); - self.transaction_at(index).map(|t| LocalizedTransaction { - signed: t, - block_hash: block_hash, - block_number: block_number, - transaction_index: index, - cached_sender: None, - }) - } - - /// Returns raw rlp for the uncles in the given block - pub fn uncles_rlp(&self) -> ViewRlp<'a> { - self.rlp.at(2) - } - - /// Return list of uncles of given block. - pub fn uncles(&self) -> Vec
{ - self.rlp.list_at(2) - } - - /// Return number of uncles in given block, without deserializing them. - pub fn uncles_count(&self) -> usize { - self.uncles_rlp().iter().count() - } - - /// Return List of transactions in given block. - pub fn uncle_views(&self) -> Vec> { - self.uncles_rlp().iter().map(HeaderView::new).collect() - } - - /// Return list of uncle hashes of given block. - pub fn uncle_hashes(&self) -> Vec { - self.uncles_rlp().iter().map(|rlp| keccak(rlp.as_raw())).collect() - } - - /// Return nth uncle. - pub fn uncle_at(&self, index: usize) -> Option
{ - self.uncles_rlp().iter().nth(index).map(|rlp| rlp.as_val()) - } - - /// Return nth uncle rlp. - pub fn uncle_rlp_at(&self, index: usize) -> Option { - self.uncles_rlp().iter().nth(index).map(|rlp| rlp.as_raw().to_vec()) - } + /// Creates new view onto block from rlp. + /// Use the `view!` macro to create this view in order to capture debugging info. + /// + /// # Example + /// + /// ``` + /// #[macro_use] + /// extern crate common_types as types; + /// + /// use types::views::{BlockView}; + /// + /// fn main() { + /// let bytes : &[u8] = &[]; + /// let block_view = view!(BlockView, bytes); + /// } + /// ``` + pub fn new(rlp: ViewRlp<'a>) -> BlockView<'a> { + BlockView { rlp: rlp } + } + + /// Block header hash. + pub fn hash(&self) -> H256 { + self.header_view().hash() + } + + /// Return reference to underlaying rlp. + pub fn rlp(&self) -> &ViewRlp<'a> { + &self.rlp + } + + /// Create new Header object from header rlp. + pub fn header(&self) -> Header { + self.rlp.val_at(0) + } + + /// Return header rlp. + pub fn header_rlp(&self) -> ViewRlp<'a> { + self.rlp.at(0) + } + + /// Create new header view obto block head rlp. + pub fn header_view(&self) -> HeaderView<'a> { + HeaderView::new(self.header_rlp()) + } + + /// Return List of transactions in given block. + pub fn transactions(&self) -> Vec { + self.rlp.list_at(1) + } + + /// Return List of transactions with additional localization info. + pub fn localized_transactions(&self) -> Vec { + let header = self.header_view(); + let block_hash = header.hash(); + let block_number = header.number(); + self.transactions() + .into_iter() + .enumerate() + .map(|(i, t)| LocalizedTransaction { + signed: t, + block_hash: block_hash.clone(), + block_number: block_number, + transaction_index: i, + cached_sender: None, + }) + .collect() + } + + /// Return the raw rlp for the transactions in the given block. + pub fn transactions_rlp(&self) -> ViewRlp<'a> { + self.rlp.at(1) + } + + /// Return number of transactions in given block, without deserializing them. + pub fn transactions_count(&self) -> usize { + self.transactions_rlp().iter().count() + } + + /// Return List of transactions in given block. + pub fn transaction_views(&self) -> Vec> { + self.transactions_rlp() + .iter() + .map(TransactionView::new) + .collect() + } + + /// Return transaction hashes. + pub fn transaction_hashes(&self) -> Vec { + self.transactions_rlp() + .iter() + .map(|rlp| keccak(rlp.as_raw())) + .collect() + } + + /// Returns transaction at given index without deserializing unnecessary data. + pub fn transaction_at(&self, index: usize) -> Option { + self.transactions_rlp() + .iter() + .nth(index) + .map(|rlp| rlp.as_val()) + } + + /// Returns localized transaction at given index. + pub fn localized_transaction_at(&self, index: usize) -> Option { + let header = self.header_view(); + let block_hash = header.hash(); + let block_number = header.number(); + self.transaction_at(index).map(|t| LocalizedTransaction { + signed: t, + block_hash: block_hash, + block_number: block_number, + transaction_index: index, + cached_sender: None, + }) + } + + /// Returns raw rlp for the uncles in the given block + pub fn uncles_rlp(&self) -> ViewRlp<'a> { + self.rlp.at(2) + } + + /// Return list of uncles of given block. + pub fn uncles(&self) -> Vec
{ + self.rlp.list_at(2) + } + + /// Return number of uncles in given block, without deserializing them. + pub fn uncles_count(&self) -> usize { + self.uncles_rlp().iter().count() + } + + /// Return List of transactions in given block. + pub fn uncle_views(&self) -> Vec> { + self.uncles_rlp().iter().map(HeaderView::new).collect() + } + + /// Return list of uncle hashes of given block. + pub fn uncle_hashes(&self) -> Vec { + self.uncles_rlp() + .iter() + .map(|rlp| keccak(rlp.as_raw())) + .collect() + } + + /// Return nth uncle. + pub fn uncle_at(&self, index: usize) -> Option
{ + self.uncles_rlp().iter().nth(index).map(|rlp| rlp.as_val()) + } + + /// Return nth uncle rlp. + pub fn uncle_rlp_at(&self, index: usize) -> Option { + self.uncles_rlp() + .iter() + .nth(index) + .map(|rlp| rlp.as_raw().to_vec()) + } } #[cfg(test)] mod tests { - use rustc_hex::FromHex; - use super::BlockView; - - #[test] - fn test_block_view() { - // that's rlp of block created with ethash engine. - let rlp = "f90261f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23f862f86002018304cb2f94ec0e71ad0a90ffe1909d27dac207f7680abba42d01801ba03a347e72953c860f32b1eb2c78a680d8734b2ea08085d949d729479796f218d5a047ea6239d9e31ccac8af3366f5ca37184d26e7646e3191a3aeb81c4cf74de500c0".from_hex().unwrap(); - - let view = view!(BlockView, &rlp); - assert_eq!(view.hash(), "2c9747e804293bd3f1a986484343f23bc88fd5be75dfe9d5c2860aff61e6f259".into()); - assert_eq!(view.transactions_count(), 1); - assert_eq!(view.uncles_count(), 0); - } + use super::BlockView; + use rustc_hex::FromHex; + + #[test] + fn test_block_view() { + // that's rlp of block created with ethash engine. + let rlp = "f90261f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23f862f86002018304cb2f94ec0e71ad0a90ffe1909d27dac207f7680abba42d01801ba03a347e72953c860f32b1eb2c78a680d8734b2ea08085d949d729479796f218d5a047ea6239d9e31ccac8af3366f5ca37184d26e7646e3191a3aeb81c4cf74de500c0".from_hex().unwrap(); + + let view = view!(BlockView, &rlp); + assert_eq!( + view.hash(), + "2c9747e804293bd3f1a986484343f23bc88fd5be75dfe9d5c2860aff61e6f259".into() + ); + assert_eq!(view.transactions_count(), 1); + assert_eq!(view.uncles_count(), 0); + } } diff --git a/ethcore/types/src/views/body.rs b/ethcore/types/src/views/body.rs index 1ea4999b8bc..8805f029ad4 100644 --- a/ethcore/types/src/views/body.rs +++ b/ethcore/types/src/views/body.rs @@ -1,176 +1,199 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! View onto block body rlp. +use super::ViewRlp; use bytes::Bytes; use ethereum_types::H256; use hash::keccak; use header::Header; use transaction::{LocalizedTransaction, UnverifiedTransaction}; -use views::{TransactionView, HeaderView}; -use super::ViewRlp; +use views::{HeaderView, TransactionView}; use BlockNumber; /// View onto block rlp. pub struct BodyView<'a> { - rlp: ViewRlp<'a> + rlp: ViewRlp<'a>, } impl<'a> BodyView<'a> { - /// Creates new view onto block body from rlp. - /// Use the `view!` macro to create this view in order to capture debugging info. - /// - /// # Example - /// - /// ``` - /// #[macro_use] - /// extern crate common_types as types; - /// - /// use types::views::{BodyView}; - /// - /// fn main() { - /// let bytes : &[u8] = &[]; - /// let body_view = view!(BodyView, bytes); - /// } - /// ``` - pub fn new(rlp: ViewRlp<'a>) -> BodyView<'a> { - BodyView { - rlp: rlp - } - } - - /// Return reference to underlaying rlp. - pub fn rlp(&self) -> &ViewRlp<'a> { - &self.rlp - } - - /// Return List of transactions in given block. - pub fn transactions(&self) -> Vec { - self.rlp.list_at(0) - } - - /// Return List of transactions with additional localization info. - pub fn localized_transactions(&self, block_hash: &H256, block_number: BlockNumber) -> Vec { - self.transactions() - .into_iter() - .enumerate() - .map(|(i, t)| LocalizedTransaction { - signed: t, - block_hash: block_hash.clone(), - block_number: block_number, - transaction_index: i, - cached_sender: None, - }).collect() - } - - /// Return the raw rlp for the transactions in the given block. - pub fn transactions_rlp(&self) -> ViewRlp<'a> { - self.rlp.at(0) - } - - /// Return number of transactions in given block, without deserializing them. - pub fn transactions_count(&self) -> usize { - self.transactions_rlp().item_count() - } - /// Return List of transactions in given block. - pub fn transaction_views(&self) -> Vec> { - self.transactions_rlp().iter().map(TransactionView::new).collect() - } - - /// Return transaction hashes. - pub fn transaction_hashes(&self) -> Vec { - self.transactions_rlp().iter().map(|rlp| keccak(rlp.as_raw())).collect() - } - - /// Returns transaction at given index without deserializing unnecessary data. - pub fn transaction_at(&self, index: usize) -> Option { - self.transactions_rlp().iter().nth(index).map(|rlp| rlp.as_val()) - } - - /// Returns localized transaction at given index. - pub fn localized_transaction_at(&self, block_hash: &H256, block_number: BlockNumber, index: usize) -> Option { - self.transaction_at(index).map(|t| LocalizedTransaction { - signed: t, - block_hash: block_hash.clone(), - block_number: block_number, - transaction_index: index, - cached_sender: None, - }) - } - - /// Returns raw rlp for the uncles in the given block - pub fn uncles_rlp(&self) -> ViewRlp<'a> { - self.rlp.at(1) - } - - /// Return list of uncles of given block. - pub fn uncles(&self) -> Vec
{ - self.rlp.list_at(1) - } - - /// Return number of uncles in given block, without deserializing them. - pub fn uncles_count(&self) -> usize { - self.uncles_rlp().item_count() - } - - /// Return List of transactions in given block. - pub fn uncle_views(&self) -> Vec> { - self.uncles_rlp().iter().map(HeaderView::new).collect() - } - - /// Return list of uncle hashes of given block. - pub fn uncle_hashes(&self) -> Vec { - self.uncles_rlp().iter().map(|rlp| keccak(rlp.as_raw())).collect() - } - - /// Return nth uncle. - pub fn uncle_at(&self, index: usize) -> Option
{ - self.uncles_rlp().iter().nth(index).map(|rlp| rlp.as_val()) - } - - /// Return nth uncle rlp. - pub fn uncle_rlp_at(&self, index: usize) -> Option { - self.uncles_rlp().iter().nth(index).map(|rlp| rlp.as_raw().to_vec()) - } + /// Creates new view onto block body from rlp. + /// Use the `view!` macro to create this view in order to capture debugging info. + /// + /// # Example + /// + /// ``` + /// #[macro_use] + /// extern crate common_types as types; + /// + /// use types::views::{BodyView}; + /// + /// fn main() { + /// let bytes : &[u8] = &[]; + /// let body_view = view!(BodyView, bytes); + /// } + /// ``` + pub fn new(rlp: ViewRlp<'a>) -> BodyView<'a> { + BodyView { rlp: rlp } + } + + /// Return reference to underlaying rlp. + pub fn rlp(&self) -> &ViewRlp<'a> { + &self.rlp + } + + /// Return List of transactions in given block. + pub fn transactions(&self) -> Vec { + self.rlp.list_at(0) + } + + /// Return List of transactions with additional localization info. + pub fn localized_transactions( + &self, + block_hash: &H256, + block_number: BlockNumber, + ) -> Vec { + self.transactions() + .into_iter() + .enumerate() + .map(|(i, t)| LocalizedTransaction { + signed: t, + block_hash: block_hash.clone(), + block_number: block_number, + transaction_index: i, + cached_sender: None, + }) + .collect() + } + + /// Return the raw rlp for the transactions in the given block. + pub fn transactions_rlp(&self) -> ViewRlp<'a> { + self.rlp.at(0) + } + + /// Return number of transactions in given block, without deserializing them. + pub fn transactions_count(&self) -> usize { + self.transactions_rlp().item_count() + } + /// Return List of transactions in given block. + pub fn transaction_views(&self) -> Vec> { + self.transactions_rlp() + .iter() + .map(TransactionView::new) + .collect() + } + + /// Return transaction hashes. + pub fn transaction_hashes(&self) -> Vec { + self.transactions_rlp() + .iter() + .map(|rlp| keccak(rlp.as_raw())) + .collect() + } + + /// Returns transaction at given index without deserializing unnecessary data. + pub fn transaction_at(&self, index: usize) -> Option { + self.transactions_rlp() + .iter() + .nth(index) + .map(|rlp| rlp.as_val()) + } + + /// Returns localized transaction at given index. + pub fn localized_transaction_at( + &self, + block_hash: &H256, + block_number: BlockNumber, + index: usize, + ) -> Option { + self.transaction_at(index).map(|t| LocalizedTransaction { + signed: t, + block_hash: block_hash.clone(), + block_number: block_number, + transaction_index: index, + cached_sender: None, + }) + } + + /// Returns raw rlp for the uncles in the given block + pub fn uncles_rlp(&self) -> ViewRlp<'a> { + self.rlp.at(1) + } + + /// Return list of uncles of given block. + pub fn uncles(&self) -> Vec
{ + self.rlp.list_at(1) + } + + /// Return number of uncles in given block, without deserializing them. + pub fn uncles_count(&self) -> usize { + self.uncles_rlp().item_count() + } + + /// Return List of transactions in given block. + pub fn uncle_views(&self) -> Vec> { + self.uncles_rlp().iter().map(HeaderView::new).collect() + } + + /// Return list of uncle hashes of given block. + pub fn uncle_hashes(&self) -> Vec { + self.uncles_rlp() + .iter() + .map(|rlp| keccak(rlp.as_raw())) + .collect() + } + + /// Return nth uncle. + pub fn uncle_at(&self, index: usize) -> Option
{ + self.uncles_rlp().iter().nth(index).map(|rlp| rlp.as_val()) + } + + /// Return nth uncle rlp. + pub fn uncle_rlp_at(&self, index: usize) -> Option { + self.uncles_rlp() + .iter() + .nth(index) + .map(|rlp| rlp.as_raw().to_vec()) + } } #[cfg(test)] mod tests { - use bytes::Bytes; - use rlp::RlpStream; - use rustc_hex::FromHex; - use super::BodyView; - use views::BlockView; - - fn block_to_body(block: &[u8]) -> Bytes { - let mut body = RlpStream::new_list(2); - let block_view = view!(BlockView, block); - body.append_raw(block_view.transactions_rlp().as_raw(), 1); - body.append_raw(block_view.uncles_rlp().as_raw(), 1); - body.out() - } - - #[test] - fn test_block_view() { - // that's rlp of block created with ethash engine. - let rlp = "f90261f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23f862f86002018304cb2f94ec0e71ad0a90ffe1909d27dac207f7680abba42d01801ba03a347e72953c860f32b1eb2c78a680d8734b2ea08085d949d729479796f218d5a047ea6239d9e31ccac8af3366f5ca37184d26e7646e3191a3aeb81c4cf74de500c0".from_hex().unwrap(); - let body = block_to_body(&rlp); - let view = view!(BodyView, &body); - assert_eq!(view.transactions_count(), 1); - assert_eq!(view.uncles_count(), 0); - } + use super::BodyView; + use bytes::Bytes; + use rlp::RlpStream; + use rustc_hex::FromHex; + use views::BlockView; + + fn block_to_body(block: &[u8]) -> Bytes { + let mut body = RlpStream::new_list(2); + let block_view = view!(BlockView, block); + body.append_raw(block_view.transactions_rlp().as_raw(), 1); + body.append_raw(block_view.uncles_rlp().as_raw(), 1); + body.out() + } + + #[test] + fn test_block_view() { + // that's rlp of block created with ethash engine. + let rlp = "f90261f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23f862f86002018304cb2f94ec0e71ad0a90ffe1909d27dac207f7680abba42d01801ba03a347e72953c860f32b1eb2c78a680d8734b2ea08085d949d729479796f218d5a047ea6239d9e31ccac8af3366f5ca37184d26e7646e3191a3aeb81c4cf74de500c0".from_hex().unwrap(); + let body = block_to_body(&rlp); + let view = view!(BodyView, &body); + assert_eq!(view.transactions_count(), 1); + assert_eq!(view.uncles_count(), 0); + } } diff --git a/ethcore/types/src/views/header.rs b/ethcore/types/src/views/header.rs index 4009892910f..e29713e37d0 100644 --- a/ethcore/types/src/views/header.rs +++ b/ethcore/types/src/views/header.rs @@ -1,150 +1,198 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! View onto block header rlp +use super::ViewRlp; use bytes::Bytes; -use ethereum_types::{H256, Bloom, U256, Address}; +use ethereum_types::{Address, Bloom, H256, U256}; use hash::keccak; use rlp::{self}; -use super::ViewRlp; use BlockNumber; /// View onto block header rlp. pub struct HeaderView<'a> { - rlp: ViewRlp<'a> + rlp: ViewRlp<'a>, } impl<'a> HeaderView<'a> { - /// Creates a new Header view from valid ViewRlp - /// Use the `view!` macro to create this view in order to capture debugging info. - /// - /// # Example - /// - /// ``` - /// #[macro_use] - /// extern crate common_types as types; - /// - /// use types::views::{HeaderView}; - /// - /// fn main() { - /// let bytes : &[u8] = &[]; - /// let tx_view = view!(HeaderView, bytes); - /// } - /// ``` - pub fn new(rlp: ViewRlp<'a>) -> HeaderView<'a> { - HeaderView { - rlp - } - } - - /// Returns header hash. - pub fn hash(&self) -> H256 { - keccak(self.rlp.rlp.as_raw()) - } - - /// Returns raw rlp. - pub fn rlp(&self) -> &ViewRlp<'a> { &self.rlp } - - /// Returns parent hash. - pub fn parent_hash(&self) -> H256 { self.rlp.val_at(0) } - - /// Returns uncles hash. - pub fn uncles_hash(&self) -> H256 { self.rlp.val_at(1) } - - /// Returns author. - pub fn author(&self) -> Address { self.rlp.val_at(2) } - - /// Returns state root. - pub fn state_root(&self) -> H256 { self.rlp.val_at(3) } - - /// Returns transactions root. - pub fn transactions_root(&self) -> H256 { self.rlp.val_at(4) } - - /// Returns block receipts root. - pub fn receipts_root(&self) -> H256 { self.rlp.val_at(5) } - - /// Returns block log bloom. - pub fn log_bloom(&self) -> Bloom { self.rlp.val_at(6) } - - /// Returns block difficulty. - pub fn difficulty(&self) -> U256 { self.rlp.val_at(7) } - - /// Returns block number. - pub fn number(&self) -> BlockNumber { self.rlp.val_at(8) } - - /// Returns block gas limit. - pub fn gas_limit(&self) -> U256 { self.rlp.val_at(9) } - - /// Returns block gas used. - pub fn gas_used(&self) -> U256 { self.rlp.val_at(10) } - - /// Returns timestamp. - pub fn timestamp(&self) -> u64 { self.rlp.val_at(11) } - - /// Returns block extra data. - pub fn extra_data(&self) -> Bytes { self.rlp.val_at(12) } - - /// Returns a vector of post-RLP-encoded seal fields. - pub fn seal(&self) -> Vec { - let mut seal = vec![]; - for i in 13..self.rlp.item_count() { - seal.push(self.rlp.at(i).as_raw().to_vec()); - } - seal - } - - /// Returns a vector of seal fields (RLP-decoded). - pub fn decode_seal(&self) -> Result, rlp::DecoderError> { - let seal = self.seal(); - seal.into_iter() - .map(|s| rlp::Rlp::new(&s).data().map(|x| x.to_vec())) - .collect() - } - + /// Creates a new Header view from valid ViewRlp + /// Use the `view!` macro to create this view in order to capture debugging info. + /// + /// # Example + /// + /// ``` + /// #[macro_use] + /// extern crate common_types as types; + /// + /// use types::views::{HeaderView}; + /// + /// fn main() { + /// let bytes : &[u8] = &[]; + /// let tx_view = view!(HeaderView, bytes); + /// } + /// ``` + pub fn new(rlp: ViewRlp<'a>) -> HeaderView<'a> { + HeaderView { rlp } + } + + /// Returns header hash. + pub fn hash(&self) -> H256 { + keccak(self.rlp.rlp.as_raw()) + } + + /// Returns raw rlp. + pub fn rlp(&self) -> &ViewRlp<'a> { + &self.rlp + } + + /// Returns parent hash. + pub fn parent_hash(&self) -> H256 { + self.rlp.val_at(0) + } + + /// Returns uncles hash. + pub fn uncles_hash(&self) -> H256 { + self.rlp.val_at(1) + } + + /// Returns author. + pub fn author(&self) -> Address { + self.rlp.val_at(2) + } + + /// Returns state root. + pub fn state_root(&self) -> H256 { + self.rlp.val_at(3) + } + + /// Returns transactions root. + pub fn transactions_root(&self) -> H256 { + self.rlp.val_at(4) + } + + /// Returns block receipts root. + pub fn receipts_root(&self) -> H256 { + self.rlp.val_at(5) + } + + /// Returns block log bloom. + pub fn log_bloom(&self) -> Bloom { + self.rlp.val_at(6) + } + + /// Returns block difficulty. + pub fn difficulty(&self) -> U256 { + self.rlp.val_at(7) + } + + /// Returns block number. + pub fn number(&self) -> BlockNumber { + self.rlp.val_at(8) + } + + /// Returns block gas limit. + pub fn gas_limit(&self) -> U256 { + self.rlp.val_at(9) + } + + /// Returns block gas used. + pub fn gas_used(&self) -> U256 { + self.rlp.val_at(10) + } + + /// Returns timestamp. + pub fn timestamp(&self) -> u64 { + self.rlp.val_at(11) + } + + /// Returns block extra data. + pub fn extra_data(&self) -> Bytes { + self.rlp.val_at(12) + } + + /// Returns a vector of post-RLP-encoded seal fields. + pub fn seal(&self) -> Vec { + let mut seal = vec![]; + for i in 13..self.rlp.item_count() { + seal.push(self.rlp.at(i).as_raw().to_vec()); + } + seal + } + + /// Returns a vector of seal fields (RLP-decoded). + pub fn decode_seal(&self) -> Result, rlp::DecoderError> { + let seal = self.seal(); + seal.into_iter() + .map(|s| rlp::Rlp::new(&s).data().map(|x| x.to_vec())) + .collect() + } } #[cfg(test)] mod tests { - use rustc_hex::FromHex; - use ethereum_types::Bloom; - use super::HeaderView; - - #[test] - fn test_header_view() { - // that's rlp of block header created with ethash engine. - let rlp = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap(); - let mix_hash = "a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd".from_hex().unwrap(); - let nonce = "88ab4e252a7e8c2a23".from_hex().unwrap(); - - let view = view!(HeaderView, &rlp); - assert_eq!(view.hash(), "2c9747e804293bd3f1a986484343f23bc88fd5be75dfe9d5c2860aff61e6f259".into()); - assert_eq!(view.parent_hash(), "d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7".into()); - assert_eq!(view.uncles_hash(), "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347".into()); - assert_eq!(view.author(), "8888f1f195afa192cfee860698584c030f4c9db1".into()); - assert_eq!(view.state_root(), "5fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25".into()); - assert_eq!(view.transactions_root(), "88d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158".into()); - assert_eq!(view.receipts_root(), "07c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1".into()); - assert_eq!(view.log_bloom(), Bloom::default()); - assert_eq!(view.difficulty(), 0x020080.into()); - assert_eq!(view.number(), 3); - assert_eq!(view.gas_limit(), 0x2fefba.into()); - assert_eq!(view.gas_used(), 0x524d.into()); - assert_eq!(view.timestamp(), 0x56_8e_93_2a); - assert_eq!(view.extra_data(), vec![] as Vec); - assert_eq!(view.seal(), vec![mix_hash, nonce]); - } + use super::HeaderView; + use ethereum_types::Bloom; + use rustc_hex::FromHex; + + #[test] + fn test_header_view() { + // that's rlp of block header created with ethash engine. + let rlp = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap(); + let mix_hash = "a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd" + .from_hex() + .unwrap(); + let nonce = "88ab4e252a7e8c2a23".from_hex().unwrap(); + + let view = view!(HeaderView, &rlp); + assert_eq!( + view.hash(), + "2c9747e804293bd3f1a986484343f23bc88fd5be75dfe9d5c2860aff61e6f259".into() + ); + assert_eq!( + view.parent_hash(), + "d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7".into() + ); + assert_eq!( + view.uncles_hash(), + "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347".into() + ); + assert_eq!( + view.author(), + "8888f1f195afa192cfee860698584c030f4c9db1".into() + ); + assert_eq!( + view.state_root(), + "5fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25".into() + ); + assert_eq!( + view.transactions_root(), + "88d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158".into() + ); + assert_eq!( + view.receipts_root(), + "07c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1".into() + ); + assert_eq!(view.log_bloom(), Bloom::default()); + assert_eq!(view.difficulty(), 0x020080.into()); + assert_eq!(view.number(), 3); + assert_eq!(view.gas_limit(), 0x2fefba.into()); + assert_eq!(view.gas_used(), 0x524d.into()); + assert_eq!(view.timestamp(), 0x56_8e_93_2a); + assert_eq!(view.extra_data(), vec![] as Vec); + assert_eq!(view.seal(), vec![mix_hash, nonce]); + } } diff --git a/ethcore/types/src/views/mod.rs b/ethcore/types/src/views/mod.rs index f5c2eab9411..312e5ecc1f9 100644 --- a/ethcore/types/src/views/mod.rs +++ b/ethcore/types/src/views/mod.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Block oriented views onto rlp. @@ -23,19 +23,18 @@ mod body; mod header; mod transaction; -pub use self::view_rlp::ViewRlp; -pub use self::block::BlockView; -pub use self::body::BodyView; -pub use self::header::HeaderView; -pub use self::transaction::TransactionView; +pub use self::{ + block::BlockView, body::BodyView, header::HeaderView, transaction::TransactionView, + view_rlp::ViewRlp, +}; #[cfg(test)] mod tests { - use super::HeaderView; + use super::HeaderView; - #[test] - #[should_panic] - fn should_include_file_line_number_in_panic_for_invalid_rlp() { - let _ = view!(HeaderView, &[]).parent_hash(); - } + #[test] + #[should_panic] + fn should_include_file_line_number_in_panic_for_invalid_rlp() { + let _ = view!(HeaderView, &[]).parent_hash(); + } } diff --git a/ethcore/types/src/views/transaction.rs b/ethcore/types/src/views/transaction.rs index b7d412f6ce8..d20115cbb2a 100644 --- a/ethcore/types/src/views/transaction.rs +++ b/ethcore/types/src/views/transaction.rs @@ -1,105 +1,131 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! View onto transaction rlp +use super::ViewRlp; use bytes::Bytes; use ethereum_types::{H256, U256}; use hash::keccak; -use super::ViewRlp; /// View onto transaction rlp. pub struct TransactionView<'a> { - rlp: ViewRlp<'a> + rlp: ViewRlp<'a>, } impl<'a> TransactionView<'a> { - /// Creates new view onto valid transaction rlp. - /// Use the `view!` macro to create this view in order to capture debugging info. - /// - /// # Example - /// - /// ``` - /// #[macro_use] - /// extern crate common_types as types; - /// - /// use types::views::{TransactionView}; - /// - /// fn main() { - /// let bytes : &[u8] = &[]; - /// let tx_view = view!(TransactionView, bytes); - /// } - /// ``` - pub fn new(rlp: ViewRlp<'a>) -> TransactionView<'a> { - TransactionView { - rlp: rlp - } - } - - /// Return reference to underlaying rlp. - pub fn rlp(&self) -> &ViewRlp<'a> { - &self.rlp - } - - /// Returns transaction hash. - pub fn hash(&self) -> H256 { - keccak(self.rlp.as_raw()) - } - - /// Get the nonce field of the transaction. - pub fn nonce(&self) -> U256 { self.rlp.val_at(0) } - - /// Get the gas_price field of the transaction. - pub fn gas_price(&self) -> U256 { self.rlp.val_at(1) } - - /// Get the gas field of the transaction. - pub fn gas(&self) -> U256 { self.rlp.val_at(2) } - - /// Get the value field of the transaction. - pub fn value(&self) -> U256 { self.rlp.val_at(4) } - - /// Get the data field of the transaction. - pub fn data(&self) -> Bytes { self.rlp.val_at(5) } - - /// Get the v field of the transaction. - pub fn v(&self) -> u8 { let r: u16 = self.rlp.val_at(6); r as u8 } - - /// Get the r field of the transaction. - pub fn r(&self) -> U256 { self.rlp.val_at(7) } - - /// Get the s field of the transaction. - pub fn s(&self) -> U256 { self.rlp.val_at(8) } + /// Creates new view onto valid transaction rlp. + /// Use the `view!` macro to create this view in order to capture debugging info. + /// + /// # Example + /// + /// ``` + /// #[macro_use] + /// extern crate common_types as types; + /// + /// use types::views::{TransactionView}; + /// + /// fn main() { + /// let bytes : &[u8] = &[]; + /// let tx_view = view!(TransactionView, bytes); + /// } + /// ``` + pub fn new(rlp: ViewRlp<'a>) -> TransactionView<'a> { + TransactionView { rlp: rlp } + } + + /// Return reference to underlaying rlp. + pub fn rlp(&self) -> &ViewRlp<'a> { + &self.rlp + } + + /// Returns transaction hash. + pub fn hash(&self) -> H256 { + keccak(self.rlp.as_raw()) + } + + /// Get the nonce field of the transaction. + pub fn nonce(&self) -> U256 { + self.rlp.val_at(0) + } + + /// Get the gas_price field of the transaction. + pub fn gas_price(&self) -> U256 { + self.rlp.val_at(1) + } + + /// Get the gas field of the transaction. + pub fn gas(&self) -> U256 { + self.rlp.val_at(2) + } + + /// Get the value field of the transaction. + pub fn value(&self) -> U256 { + self.rlp.val_at(4) + } + + /// Get the data field of the transaction. + pub fn data(&self) -> Bytes { + self.rlp.val_at(5) + } + + /// Get the v field of the transaction. + pub fn v(&self) -> u8 { + let r: u16 = self.rlp.val_at(6); + r as u8 + } + + /// Get the r field of the transaction. + pub fn r(&self) -> U256 { + self.rlp.val_at(7) + } + + /// Get the s field of the transaction. + pub fn s(&self) -> U256 { + self.rlp.val_at(8) + } } #[cfg(test)] mod tests { - use rustc_hex::FromHex; - use super::TransactionView; - - #[test] - fn test_transaction_view() { - let rlp = "f87c80018261a894095e7baea6a6c7c4c2dfeb977efac326af552d870a9d00000000000000000000000000000000000000000000000000000000001ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804".from_hex().unwrap(); - - let view = view!(TransactionView, &rlp); - assert_eq!(view.nonce(), 0.into()); - assert_eq!(view.gas_price(), 1.into()); - assert_eq!(view.gas(), 0x61a8.into()); - assert_eq!(view.value(), 0xa.into()); - assert_eq!(view.data(), "0000000000000000000000000000000000000000000000000000000000".from_hex().unwrap()); - assert_eq!(view.r(), "48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353".into()); - assert_eq!(view.s(), "efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804".into()); - assert_eq!(view.v(), 0x1b); - } + use super::TransactionView; + use rustc_hex::FromHex; + + #[test] + fn test_transaction_view() { + let rlp = "f87c80018261a894095e7baea6a6c7c4c2dfeb977efac326af552d870a9d00000000000000000000000000000000000000000000000000000000001ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804".from_hex().unwrap(); + + let view = view!(TransactionView, &rlp); + assert_eq!(view.nonce(), 0.into()); + assert_eq!(view.gas_price(), 1.into()); + assert_eq!(view.gas(), 0x61a8.into()); + assert_eq!(view.value(), 0xa.into()); + assert_eq!( + view.data(), + "0000000000000000000000000000000000000000000000000000000000" + .from_hex() + .unwrap() + ); + assert_eq!( + view.r(), + "48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353".into() + ); + assert_eq!( + view.s(), + "efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804".into() + ); + assert_eq!(view.v(), 0x1b); + } } diff --git a/ethcore/types/src/views/view_rlp.rs b/ethcore/types/src/views/view_rlp.rs index a6c789de989..2d584b08a59 100644 --- a/ethcore/types/src/views/view_rlp.rs +++ b/ethcore/types/src/views/view_rlp.rs @@ -1,135 +1,151 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Wrapper for view rlp expected to be valid with debug info -use rlp::{Rlp, Decodable, DecoderError}; +use rlp::{Decodable, DecoderError, Rlp}; /// Wrapper for trusted rlp, which is expected to be valid, for use in views /// When created with view!, records the file and line where it was created for debugging pub struct ViewRlp<'a> { - /// Wrapped Rlp, expected to be valid - pub rlp: Rlp<'a>, - file: &'a str, - line: u32, + /// Wrapped Rlp, expected to be valid + pub rlp: Rlp<'a>, + file: &'a str, + line: u32, } -impl<'a, 'view> ViewRlp<'a> where 'a : 'view { - #[doc(hidden)] - pub fn new(bytes: &'a [u8], file: &'a str, line: u32) -> Self { - ViewRlp { - rlp: Rlp::new(bytes), - file, - line - } - } - - /// Returns a new instance replacing existing rlp with new rlp, maintaining debug info - fn new_from_rlp(&self, rlp: Rlp<'a>) -> Self { - ViewRlp { - rlp, - file: self.file, - line: self.line - } - } - - fn maybe_at(&self, index: usize) -> Option> { - self.rlp.at(index) - .map(|rlp| self.new_from_rlp(rlp)) - .ok() - } - - fn expect_valid_rlp(&self, r: Result) -> T { - r.unwrap_or_else(|e| panic!( - "View rlp is trusted and should be valid. Constructed in {} on line {}: {}", - self.file, - self.line, - e - )) - } - - /// Returns rlp at the given index, panics if no rlp at that index - pub fn at(&self, index: usize) -> ViewRlp<'a> { - let rlp = self.expect_valid_rlp(self.rlp.at(index)); - self.new_from_rlp(rlp) - } - - /// Returns an iterator over all rlp values - pub fn iter(&'view self) -> ViewRlpIterator<'a, 'view> { - self.into_iter() - } - - /// Returns decoded value of this rlp, panics if rlp not valid - pub fn as_val(&self) -> T where T: Decodable { - self.expect_valid_rlp(self.rlp.as_val()) - } - - /// Returns decoded value at the given index, panics not present or valid at that index - pub fn val_at(&self, index: usize) -> T where T : Decodable { - self.expect_valid_rlp(self.rlp.val_at(index)) - } - - /// Returns decoded list of values, panics if rlp is invalid - pub fn list_at(&self, index: usize) -> Vec where T: Decodable { - self.expect_valid_rlp(self.rlp.list_at(index)) - } - - /// Returns the number of items in the rlp, panics if it is not a list of rlp values - pub fn item_count(&self) -> usize { - self.expect_valid_rlp(self.rlp.item_count()) - } - - /// Returns raw rlp bytes - pub fn as_raw(&'view self) -> &'a [u8] { - self.rlp.as_raw() - } +impl<'a, 'view> ViewRlp<'a> +where + 'a: 'view, +{ + #[doc(hidden)] + pub fn new(bytes: &'a [u8], file: &'a str, line: u32) -> Self { + ViewRlp { + rlp: Rlp::new(bytes), + file, + line, + } + } + + /// Returns a new instance replacing existing rlp with new rlp, maintaining debug info + fn new_from_rlp(&self, rlp: Rlp<'a>) -> Self { + ViewRlp { + rlp, + file: self.file, + line: self.line, + } + } + + fn maybe_at(&self, index: usize) -> Option> { + self.rlp.at(index).map(|rlp| self.new_from_rlp(rlp)).ok() + } + + fn expect_valid_rlp(&self, r: Result) -> T { + r.unwrap_or_else(|e| { + panic!( + "View rlp is trusted and should be valid. Constructed in {} on line {}: {}", + self.file, self.line, e + ) + }) + } + + /// Returns rlp at the given index, panics if no rlp at that index + pub fn at(&self, index: usize) -> ViewRlp<'a> { + let rlp = self.expect_valid_rlp(self.rlp.at(index)); + self.new_from_rlp(rlp) + } + + /// Returns an iterator over all rlp values + pub fn iter(&'view self) -> ViewRlpIterator<'a, 'view> { + self.into_iter() + } + + /// Returns decoded value of this rlp, panics if rlp not valid + pub fn as_val(&self) -> T + where + T: Decodable, + { + self.expect_valid_rlp(self.rlp.as_val()) + } + + /// Returns decoded value at the given index, panics not present or valid at that index + pub fn val_at(&self, index: usize) -> T + where + T: Decodable, + { + self.expect_valid_rlp(self.rlp.val_at(index)) + } + + /// Returns decoded list of values, panics if rlp is invalid + pub fn list_at(&self, index: usize) -> Vec + where + T: Decodable, + { + self.expect_valid_rlp(self.rlp.list_at(index)) + } + + /// Returns the number of items in the rlp, panics if it is not a list of rlp values + pub fn item_count(&self) -> usize { + self.expect_valid_rlp(self.rlp.item_count()) + } + + /// Returns raw rlp bytes + pub fn as_raw(&'view self) -> &'a [u8] { + self.rlp.as_raw() + } } /// Iterator over rlp-slice list elements. -pub struct ViewRlpIterator<'a, 'view> where 'a: 'view { - rlp: &'view ViewRlp<'a>, - index: usize, +pub struct ViewRlpIterator<'a, 'view> +where + 'a: 'view, +{ + rlp: &'view ViewRlp<'a>, + index: usize, } -impl<'a, 'view> IntoIterator for &'view ViewRlp<'a> where 'a: 'view { - type Item = ViewRlp<'a>; - type IntoIter = ViewRlpIterator<'a, 'view>; - - fn into_iter(self) -> Self::IntoIter { - ViewRlpIterator { - rlp: self, - index: 0, - } - } +impl<'a, 'view> IntoIterator for &'view ViewRlp<'a> +where + 'a: 'view, +{ + type Item = ViewRlp<'a>; + type IntoIter = ViewRlpIterator<'a, 'view>; + + fn into_iter(self) -> Self::IntoIter { + ViewRlpIterator { + rlp: self, + index: 0, + } + } } impl<'a, 'view> Iterator for ViewRlpIterator<'a, 'view> { - type Item = ViewRlp<'a>; - - fn next(&mut self) -> Option> { - let index = self.index; - let result = self.rlp.maybe_at(index); - self.index += 1; - result - } + type Item = ViewRlp<'a>; + + fn next(&mut self) -> Option> { + let index = self.index; + let result = self.rlp.maybe_at(index); + self.index += 1; + result + } } #[macro_export] macro_rules! view { - ($view: ident, $bytes: expr) => { - $view::new($crate::views::ViewRlp::new($bytes, file!(), line!())) - }; + ($view: ident, $bytes: expr) => { + $view::new($crate::views::ViewRlp::new($bytes, file!(), line!())) + }; } diff --git a/ethcore/vm/Cargo.toml b/ethcore/vm/Cargo.toml index 19b84a6ecc2..34b086bce0a 100644 --- a/ethcore/vm/Cargo.toml +++ b/ethcore/vm/Cargo.toml @@ -1,15 +1,13 @@ [package] +description = "Virtual Machines (VM) Support Library" name = "vm" version = "0.1.0" authors = ["Parity Technologies "] [dependencies] -byteorder = "1.0" parity-bytes = "0.1" ethereum-types = "0.4" -trie-db = "0.11.0" patricia-trie-ethereum = { path = "../../util/patricia-trie-ethereum" } -log = "0.4" ethjson = { path = "../../json" } rlp = { version = "0.3.0", features = ["ethereum"] } keccak-hash = "0.1" diff --git a/ethcore/vm/src/action_params.rs b/ethcore/vm/src/action_params.rs index 0d4959c1899..b46dc919b1f 100644 --- a/ethcore/vm/src/action_params.rs +++ b/ethcore/vm/src/action_params.rs @@ -1,24 +1,24 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Evm input params. -use ethereum_types::{U256, H256, Address}; use bytes::Bytes; -use hash::{keccak, KECCAK_EMPTY}; +use ethereum_types::{Address, H256, U256}; use ethjson; +use hash::{keccak, KECCAK_EMPTY}; use call_type::CallType; @@ -27,107 +27,110 @@ use std::sync::Arc; /// Transaction value #[derive(Clone, Debug)] pub enum ActionValue { - /// Value that should be transfered - Transfer(U256), - /// Apparent value for transaction (not transfered) - Apparent(U256) + /// Value that should be transfered + Transfer(U256), + /// Apparent value for transaction (not transfered) + Apparent(U256), } /// Type of the way parameters encoded #[derive(Clone, Debug)] pub enum ParamsType { - /// Parameters are included in code - Embedded, - /// Parameters are passed in data section - Separate, + /// Parameters are included in code + Embedded, + /// Parameters are passed in data section + Separate, } impl ActionValue { - /// Returns action value as U256. - pub fn value(&self) -> U256 { - match *self { - ActionValue::Transfer(x) | ActionValue::Apparent(x) => x - } - } + /// Returns action value as U256. + pub fn value(&self) -> U256 { + match *self { + ActionValue::Transfer(x) | ActionValue::Apparent(x) => x, + } + } - /// Returns the transfer action value of the U256-convertable raw value - pub fn transfer>(transfer_value: T) -> ActionValue { - ActionValue::Transfer(transfer_value.into()) - } + /// Returns the transfer action value of the U256-convertable raw value + pub fn transfer>(transfer_value: T) -> ActionValue { + ActionValue::Transfer(transfer_value.into()) + } - /// Returns the apparent action value of the U256-convertable raw value - pub fn apparent>(apparent_value: T) -> ActionValue { - ActionValue::Apparent(apparent_value.into()) - } + /// Returns the apparent action value of the U256-convertable raw value + pub fn apparent>(apparent_value: T) -> ActionValue { + ActionValue::Apparent(apparent_value.into()) + } } // TODO: should be a trait, possible to avoid cloning everything from a Transaction(/View). /// Action (call/create) input params. Everything else should be specified in Externalities. #[derive(Clone, Debug)] pub struct ActionParams { - /// Address of currently executed code. - pub code_address: Address, - /// Hash of currently executed code. - pub code_hash: Option, - /// Receive address. Usually equal to code_address, - /// except when called using CALLCODE. - pub address: Address, - /// Sender of current part of the transaction. - pub sender: Address, - /// Transaction initiator. - pub origin: Address, - /// Gas paid up front for transaction execution - pub gas: U256, - /// Gas price. - pub gas_price: U256, - /// Transaction value. - pub value: ActionValue, - /// Code being executed. - pub code: Option>, - /// Input data. - pub data: Option, - /// Type of call - pub call_type: CallType, - /// Param types encoding - pub params_type: ParamsType, + /// Address of currently executed code. + pub code_address: Address, + /// Hash of currently executed code. + pub code_hash: Option, + /// Receive address. Usually equal to code_address, + /// except when called using CALLCODE. + pub address: Address, + /// Sender of current part of the transaction. + pub sender: Address, + /// Transaction initiator. + pub origin: Address, + /// Gas paid up front for transaction execution + pub gas: U256, + /// Gas price. + pub gas_price: U256, + /// Transaction value. + pub value: ActionValue, + /// Code being executed. + pub code: Option>, + /// Input data. + pub data: Option, + /// Type of call + pub call_type: CallType, + /// Param types encoding + pub params_type: ParamsType, } impl Default for ActionParams { - /// Returns default ActionParams initialized with zeros - fn default() -> ActionParams { - ActionParams { - code_address: Address::new(), - code_hash: Some(KECCAK_EMPTY), - address: Address::new(), - sender: Address::new(), - origin: Address::new(), - gas: U256::zero(), - gas_price: U256::zero(), - value: ActionValue::Transfer(U256::zero()), - code: None, - data: None, - call_type: CallType::None, - params_type: ParamsType::Separate, - } - } + /// Returns default ActionParams initialized with zeros + fn default() -> ActionParams { + ActionParams { + code_address: Address::new(), + code_hash: Some(KECCAK_EMPTY), + address: Address::new(), + sender: Address::new(), + origin: Address::new(), + gas: U256::zero(), + gas_price: U256::zero(), + value: ActionValue::Transfer(U256::zero()), + code: None, + data: None, + call_type: CallType::None, + params_type: ParamsType::Separate, + } + } } impl From for ActionParams { - fn from(t: ethjson::vm::Transaction) -> Self { - let address: Address = t.address.into(); - ActionParams { - code_address: Address::new(), - code_hash: Some(keccak(&*t.code)), - address: address, - sender: t.sender.into(), - origin: t.origin.into(), - code: Some(Arc::new(t.code.into())), - data: Some(t.data.into()), - gas: t.gas.into(), - gas_price: t.gas_price.into(), - value: ActionValue::Transfer(t.value.into()), - call_type: match address.is_zero() { true => CallType::None, false => CallType::Call }, // TODO @debris is this correct? - params_type: ParamsType::Separate, - } - } + fn from(t: ethjson::vm::Transaction) -> Self { + let address: Address = t.address.into(); + ActionParams { + code_address: Address::new(), + code_hash: Some(keccak(&*t.code)), + address: address, + sender: t.sender.into(), + origin: t.origin.into(), + code: Some(Arc::new(t.code.into())), + data: Some(t.data.into()), + gas: t.gas.into(), + gas_price: t.gas_price.into(), + value: ActionValue::Transfer(t.value.into()), + call_type: match address.is_zero() { + true => CallType::None, + false => CallType::Call, + }, // TODO @debris is this correct? + params_type: ParamsType::Separate, + } + } } diff --git a/ethcore/vm/src/call_type.rs b/ethcore/vm/src/call_type.rs index e5245c8c142..b8dcf559f73 100644 --- a/ethcore/vm/src/call_type.rs +++ b/ethcore/vm/src/call_type.rs @@ -1,86 +1,88 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! EVM call types. -use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp}; +use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; /// The type of the call-like instruction. #[derive(Debug, PartialEq, Clone)] pub enum CallType { - /// Not a CALL. - None, - /// CALL. - Call, - /// CALLCODE. - CallCode, - /// DELEGATECALL. - DelegateCall, - /// STATICCALL - StaticCall, + /// Not a CALL. + None, + /// CALL. + Call, + /// CALLCODE. + CallCode, + /// DELEGATECALL. + DelegateCall, + /// STATICCALL + StaticCall, } impl Encodable for CallType { - fn rlp_append(&self, s: &mut RlpStream) { - let v = match *self { - CallType::None => 0u32, - CallType::Call => 1, - CallType::CallCode => 2, - CallType::DelegateCall => 3, - CallType::StaticCall => 4, - }; - Encodable::rlp_append(&v, s); - } + fn rlp_append(&self, s: &mut RlpStream) { + let v = match *self { + CallType::None => 0u32, + CallType::Call => 1, + CallType::CallCode => 2, + CallType::DelegateCall => 3, + CallType::StaticCall => 4, + }; + Encodable::rlp_append(&v, s); + } } impl Decodable for CallType { - fn decode(rlp: &Rlp) -> Result { - rlp.as_val().and_then(|v| Ok(match v { - 0u32 => CallType::None, - 1 => CallType::Call, - 2 => CallType::CallCode, - 3 => CallType::DelegateCall, - 4 => CallType::StaticCall, - _ => return Err(DecoderError::Custom("Invalid value of CallType item")), - })) - } + fn decode(rlp: &Rlp) -> Result { + rlp.as_val().and_then(|v| { + Ok(match v { + 0u32 => CallType::None, + 1 => CallType::Call, + 2 => CallType::CallCode, + 3 => CallType::DelegateCall, + 4 => CallType::StaticCall, + _ => return Err(DecoderError::Custom("Invalid value of CallType item")), + }) + }) + } } #[cfg(test)] mod tests { - use rlp::*; - use super::CallType; + use super::CallType; + use rlp::*; - #[test] - fn encode_call_type() { - let ct = CallType::Call; + #[test] + fn encode_call_type() { + let ct = CallType::Call; - let mut s = RlpStream::new_list(2); - s.append(&ct); - assert!(!s.is_finished(), "List shouldn't finished yet"); - s.append(&ct); - assert!(s.is_finished(), "List should be finished now"); - s.out(); - } + let mut s = RlpStream::new_list(2); + s.append(&ct); + assert!(!s.is_finished(), "List shouldn't finished yet"); + s.append(&ct); + assert!(s.is_finished(), "List should be finished now"); + s.out(); + } - #[test] - fn should_encode_and_decode_call_type() { - let original = CallType::Call; - let encoded = encode(&original); - let decoded = decode(&encoded).expect("failure decoding CallType"); - assert_eq!(original, decoded); - } + #[test] + fn should_encode_and_decode_call_type() { + let original = CallType::Call; + let encoded = encode(&original); + let decoded = decode(&encoded).expect("failure decoding CallType"); + assert_eq!(original, decoded); + } } diff --git a/ethcore/vm/src/env_info.rs b/ethcore/vm/src/env_info.rs index e5bd3f64e34..df9d3c10eb9 100644 --- a/ethcore/vm/src/env_info.rs +++ b/ethcore/vm/src/env_info.rs @@ -1,26 +1,25 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Environment information for transaction execution. -use std::cmp; -use std::sync::Arc; -use hash::keccak; -use ethereum_types::{U256, H256, Address}; +use ethereum_types::{Address, H256, U256}; use ethjson; +use hash::keccak; +use std::{cmp, sync::Arc}; type BlockNumber = u64; @@ -31,79 +30,88 @@ pub type LastHashes = Vec; /// Information concerning the execution environment for a message-call/contract-creation. #[derive(Debug, Clone)] pub struct EnvInfo { - /// The block number. - pub number: BlockNumber, - /// The block author. - pub author: Address, - /// The block timestamp. - pub timestamp: u64, - /// The block difficulty. - pub difficulty: U256, - /// The block gas limit. - pub gas_limit: U256, - /// The last 256 block hashes. - pub last_hashes: Arc, - /// The gas used. - pub gas_used: U256, + /// The block number. + pub number: BlockNumber, + /// The block author. + pub author: Address, + /// The block timestamp. + pub timestamp: u64, + /// The block difficulty. + pub difficulty: U256, + /// The block gas limit. + pub gas_limit: U256, + /// The last 256 block hashes. + pub last_hashes: Arc, + /// The gas used. + pub gas_used: U256, } impl Default for EnvInfo { - fn default() -> Self { - EnvInfo { - number: 0, - author: Address::default(), - timestamp: 0, - difficulty: 0.into(), - gas_limit: 0.into(), - last_hashes: Arc::new(vec![]), - gas_used: 0.into(), - } - } + fn default() -> Self { + EnvInfo { + number: 0, + author: Address::default(), + timestamp: 0, + difficulty: 0.into(), + gas_limit: 0.into(), + last_hashes: Arc::new(vec![]), + gas_used: 0.into(), + } + } } impl From for EnvInfo { - fn from(e: ethjson::vm::Env) -> Self { - let number = e.number.into(); - EnvInfo { - number: number, - author: e.author.into(), - difficulty: e.difficulty.into(), - gas_limit: e.gas_limit.into(), - timestamp: e.timestamp.into(), - last_hashes: Arc::new((1..cmp::min(number + 1, 257)).map(|i| keccak(format!("{}", number - i).as_bytes())).collect()), - gas_used: U256::default(), - } - } + fn from(e: ethjson::vm::Env) -> Self { + let number = e.number.into(); + EnvInfo { + number, + author: e.author.into(), + difficulty: e.difficulty.into(), + gas_limit: e.gas_limit.into(), + timestamp: e.timestamp.into(), + last_hashes: Arc::new( + (1..cmp::min(number + 1, 257)) + .map(|i| keccak(format!("{}", number - i).as_bytes())) + .collect(), + ), + gas_used: U256::default(), + } + } } #[cfg(test)] mod tests { - use std::str::FromStr; - use super::*; - use ethereum_types::{U256, Address}; - use ethjson; + use super::*; + use ethereum_types::{Address, U256}; + use ethjson; + use std::str::FromStr; - #[test] - fn it_serializes_from_json() { - let env_info = EnvInfo::from(ethjson::vm::Env { - author: ethjson::hash::Address(Address::from_str("000000f00000000f000000000000f00000000f00").unwrap()), - number: ethjson::uint::Uint(U256::from(1_112_339)), - difficulty: ethjson::uint::Uint(U256::from(50_000)), - gas_limit: ethjson::uint::Uint(U256::from(40_000)), - timestamp: ethjson::uint::Uint(U256::from(1_100)) - }); + #[test] + fn it_serializes_from_json() { + let env_info = EnvInfo::from(ethjson::vm::Env { + author: ethjson::hash::Address( + Address::from_str("000000f00000000f000000000000f00000000f00").unwrap(), + ), + number: ethjson::uint::Uint(U256::from(1_112_339)), + difficulty: ethjson::uint::Uint(U256::from(50_000)), + gas_limit: ethjson::uint::Uint(U256::from(40_000)), + timestamp: ethjson::uint::Uint(U256::from(1_100)), + }); - assert_eq!(env_info.number, 1112339); - assert_eq!(env_info.author, Address::from_str("000000f00000000f000000000000f00000000f00").unwrap()); - assert_eq!(env_info.gas_limit, 40000.into()); - assert_eq!(env_info.difficulty, 50000.into()); - assert_eq!(env_info.gas_used, 0.into()); - } + assert_eq!(env_info.number, 1112339); + assert_eq!( + env_info.author, + Address::from_str("000000f00000000f000000000000f00000000f00").unwrap() + ); + assert_eq!(env_info.gas_limit, 40000.into()); + assert_eq!(env_info.difficulty, 50000.into()); + assert_eq!(env_info.gas_used, 0.into()); + } - #[test] - fn it_can_be_created_as_default() { - let default_env_info = EnvInfo::default(); + #[test] + fn it_can_be_created_as_default() { + let default_env_info = EnvInfo::default(); - assert_eq!(default_env_info.difficulty, 0.into()); - } + assert_eq!(default_env_info.difficulty, 0.into()); + } } diff --git a/ethcore/vm/src/error.rs b/ethcore/vm/src/error.rs index 33400563359..627b73cfe9a 100644 --- a/ethcore/vm/src/error.rs +++ b/ethcore/vm/src/error.rs @@ -1,123 +1,159 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! VM errors module -use ::{ResumeCall, ResumeCreate}; -use ethereum_types::Address; use action_params::ActionParams; -use std::fmt; +use ethereum_types::Address; use ethtrie; +use std::fmt; +use ResumeCall; +use ResumeCreate; #[derive(Debug)] pub enum TrapKind { - Call(ActionParams), - Create(ActionParams, Address), + Call(ActionParams), + Create(ActionParams, Address), } pub enum TrapError { - Call(ActionParams, Call), - Create(ActionParams, Address, Create), + Call(ActionParams, Call), + Create(ActionParams, Address, Create), } /// VM errors. #[derive(Debug, Clone, PartialEq)] pub enum Error { - /// `OutOfGas` is returned when transaction execution runs out of gas. - /// The state should be reverted to the state from before the - /// transaction execution. But it does not mean that transaction - /// was invalid. Balance still should be transfered and nonce - /// should be increased. - OutOfGas, - /// `BadJumpDestination` is returned when execution tried to move - /// to position that wasn't marked with JUMPDEST instruction - BadJumpDestination { - /// Position the code tried to jump to. - destination: usize - }, - /// `BadInstructions` is returned when given instruction is not supported - BadInstruction { - /// Unrecognized opcode - instruction: u8, - }, - /// `StackUnderflow` when there is not enough stack elements to execute instruction - StackUnderflow { - /// Invoked instruction - instruction: &'static str, - /// How many stack elements was requested by instruction - wanted: usize, - /// How many elements were on stack - on_stack: usize - }, - /// When execution would exceed defined Stack Limit - OutOfStack { - /// Invoked instruction - instruction: &'static str, - /// How many stack elements instruction wanted to push - wanted: usize, - /// What was the stack limit - limit: usize - }, - /// Built-in contract failed on given input - BuiltIn(&'static str), - /// When execution tries to modify the state in static context - MutableCallInStaticContext, - /// Likely to cause consensus issues. - Internal(String), - /// Wasm runtime error - Wasm(String), - /// Out of bounds access in RETURNDATACOPY. - OutOfBounds, - /// Execution has been reverted with REVERT. - Reverted, + /// `OutOfGas` is returned when transaction execution runs out of gas. + /// The state should be reverted to the state from before the + /// transaction execution. But it does not mean that transaction + /// was invalid. Balance still should be transfered and nonce + /// should be increased. + OutOfGas, + /// `BadJumpDestination` is returned when execution tried to move + /// to position that wasn't marked with JUMPDEST instruction + BadJumpDestination { + /// Position the code tried to jump to. + destination: usize, + }, + /// `BadInstructions` is returned when given instruction is not supported + BadInstruction { + /// Unrecognized opcode + instruction: u8, + }, + /// `StackUnderflow` when there is not enough stack elements to execute instruction + StackUnderflow { + /// Invoked instruction + instruction: &'static str, + /// How many stack elements was requested by instruction + wanted: usize, + /// How many elements were on stack + on_stack: usize, + }, + /// When execution would exceed defined Stack Limit + OutOfStack { + /// Invoked instruction + instruction: &'static str, + /// How many stack elements instruction wanted to push + wanted: usize, + /// What was the stack limit + limit: usize, + }, + /// When there is not enough subroutine stack elements to return from + SubStackUnderflow { + /// How many stack elements was requested by instruction + wanted: usize, + /// How many elements were on stack + on_stack: usize, + }, + /// When execution would exceed defined subroutine Stack Limit + OutOfSubStack { + /// How many stack elements instruction wanted to pop + wanted: usize, + /// What was the stack limit + limit: usize, + }, + /// When the code walks into a subroutine, that is not allowed + InvalidSubEntry, + /// Built-in contract failed on given input + BuiltIn(&'static str), + /// When execution tries to modify the state in static context + MutableCallInStaticContext, + /// Likely to cause consensus issues. + Internal(String), + /// Wasm runtime error + Wasm(String), + /// Out of bounds access in RETURNDATACOPY. + OutOfBounds, + /// Execution has been reverted with REVERT. + Reverted, } impl From> for Error { - fn from(err: Box) -> Self { - Error::Internal(format!("Internal error: {}", err)) - } + fn from(err: Box) -> Self { + Error::Internal(format!("Internal error: {}", err)) + } } impl From for Error { - fn from(err: ethtrie::TrieError) -> Self { - Error::Internal(format!("Internal error: {}", err)) - } + fn from(err: ethtrie::TrieError) -> Self { + Error::Internal(format!("Internal error: {}", err)) + } } impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::Error::*; - match *self { - OutOfGas => write!(f, "Out of gas"), - BadJumpDestination { destination } => write!(f, "Bad jump destination {:x}", destination), - BadInstruction { instruction } => write!(f, "Bad instruction {:x}", instruction), - StackUnderflow { instruction, wanted, on_stack } => write!(f, "Stack underflow {} {}/{}", instruction, wanted, on_stack), - OutOfStack { instruction, wanted, limit } => write!(f, "Out of stack {} {}/{}", instruction, wanted, limit), - BuiltIn(name) => write!(f, "Built-in failed: {}", name), - Internal(ref msg) => write!(f, "Internal error: {}", msg), - MutableCallInStaticContext => write!(f, "Mutable call in static context"), - Wasm(ref msg) => write!(f, "Internal error: {}", msg), - OutOfBounds => write!(f, "Out of bounds"), - Reverted => write!(f, "Reverted"), - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::Error::*; + match *self { + OutOfGas => write!(f, "Out of gas"), + BadJumpDestination { destination } => write!( + f, + "Bad jump destination {:x} (trimmed to usize)", + destination + ), + BadInstruction { instruction } => write!(f, "Bad instruction {:x}", instruction), + StackUnderflow { + instruction, + wanted, + on_stack, + } => write!(f, "Stack underflow {} {}/{}", instruction, wanted, on_stack), + OutOfStack { + instruction, + wanted, + limit, + } => write!(f, "Out of stack {} {}/{}", instruction, wanted, limit), + SubStackUnderflow { wanted, on_stack } => { + write!(f, "Subroutine stack underflow {}/{}", wanted, on_stack) + } + OutOfSubStack { wanted, limit } => { + write!(f, "Out of subroutine stack {}/{}", wanted, limit) + } + InvalidSubEntry => write!(f, "Invalid subroutine entry"), + BuiltIn(name) => write!(f, "Built-in failed: {}", name), + Internal(ref msg) => write!(f, "Internal error: {}", msg), + MutableCallInStaticContext => write!(f, "Mutable call in static context"), + Wasm(ref msg) => write!(f, "Internal error: {}", msg), + OutOfBounds => write!(f, "Out of bounds"), + Reverted => write!(f, "Reverted"), + } + } } pub type Result = ::std::result::Result; pub type TrapResult = ::std::result::Result, TrapError>; -pub type ExecTrapResult = TrapResult, Box>; -pub type ExecTrapError = TrapError, Box>; +pub type ExecTrapResult = TrapResult, Box>; +pub type ExecTrapError = TrapError, Box>; diff --git a/ethcore/vm/src/ext.rs b/ethcore/vm/src/ext.rs index 9b499fcab48..752b60a2bf4 100644 --- a/ethcore/vm/src/ext.rs +++ b/ethcore/vm/src/ext.rs @@ -1,170 +1,187 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Interface for Evm externalities. -use std::sync::Arc; -use ethereum_types::{U256, H256, Address}; use bytes::Bytes; use call_type::CallType; use env_info::EnvInfo; -use schedule::Schedule; -use return_data::ReturnData; use error::{Result, TrapKind}; +use ethereum_types::{Address, H256, U256}; +use return_data::ReturnData; +use schedule::Schedule; +use std::sync::Arc; #[derive(Debug)] /// Result of externalities create function. pub enum ContractCreateResult { - /// Returned when creation was successfull. - /// Contains an address of newly created contract and gas left. - Created(Address, U256), - /// Returned when contract creation failed. - /// VM doesn't have to know the reason. - Failed, - /// Reverted with REVERT. - Reverted(U256, ReturnData), + /// Returned when creation was successfull. + /// Contains an address of newly created contract and gas left. + Created(Address, U256), + /// Returned when contract creation failed. + /// VM doesn't have to know the reason. + Failed, + /// Reverted with REVERT. + Reverted(U256, ReturnData), } #[derive(Debug)] /// Result of externalities call function. pub enum MessageCallResult { - /// Returned when message call was successfull. - /// Contains gas left and output data. - Success(U256, ReturnData), - /// Returned when message call failed. - /// VM doesn't have to know the reason. - Failed, - /// Returned when message call was reverted. - /// Contains gas left and output data. - Reverted(U256, ReturnData), + /// Returned when message call was successfull. + /// Contains gas left and output data. + Success(U256, ReturnData), + /// Returned when message call failed. + /// VM doesn't have to know the reason. + Failed, + /// Returned when message call was reverted. + /// Contains gas left and output data. + Reverted(U256, ReturnData), } /// Specifies how an address is calculated for a new contract. #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub enum CreateContractAddress { - /// Address is calculated from sender and nonce. pWASM `create` scheme. - FromSenderAndNonce, - /// Address is calculated from sender, salt and code hash. pWASM `create2` scheme and EIP-1014 CREATE2 scheme. - FromSenderSaltAndCodeHash(H256), - /// Address is calculated from code hash and sender. Used by pwasm create ext. - FromSenderAndCodeHash, + /// Address is calculated from sender and nonce. pWASM `create` scheme. + FromSenderAndNonce, + /// Address is calculated from sender, salt and code hash. pWASM `create2` scheme and EIP-1014 CREATE2 scheme. + FromSenderSaltAndCodeHash(H256), + /// Address is calculated from code hash and sender. Used by pwasm create ext. + FromSenderAndCodeHash, } /// Externalities interface for EVMs pub trait Ext { - /// Returns the storage value for a given key if reversion happens on the current transaction. - fn initial_storage_at(&self, key: &H256) -> Result; - - /// Returns a value for given key. - fn storage_at(&self, key: &H256) -> Result; - - /// Stores a value for given key. - fn set_storage(&mut self, key: H256, value: H256) -> Result<()>; - - /// Determine whether an account exists. - fn exists(&self, address: &Address) -> Result; - - /// Determine whether an account exists and is not null (zero balance/nonce, no code). - fn exists_and_not_null(&self, address: &Address) -> Result; - - /// Balance of the origin account. - fn origin_balance(&self) -> Result; - - /// Returns address balance. - fn balance(&self, address: &Address) -> Result; - - /// Returns the hash of one of the 256 most recent complete blocks. - fn blockhash(&mut self, number: &U256) -> H256; - - /// Creates new contract. - /// - /// Returns gas_left and contract address if contract creation was succesfull. - fn create( - &mut self, - gas: &U256, - value: &U256, - code: &[u8], - address: CreateContractAddress, - trap: bool, - ) -> ::std::result::Result; - - /// Message call. - /// - /// Returns Err, if we run out of gas. - /// Otherwise returns call_result which contains gas left - /// and true if subcall was successfull. - fn call( - &mut self, - gas: &U256, - sender_address: &Address, - receive_address: &Address, - value: Option, - data: &[u8], - code_address: &Address, - call_type: CallType, - trap: bool - ) -> ::std::result::Result; - - /// Returns code at given address - fn extcode(&self, address: &Address) -> Result>>; - - /// Returns code hash at given address - fn extcodehash(&self, address: &Address) -> Result>; - - /// Returns code size at given address - fn extcodesize(&self, address: &Address) -> Result>; - - /// Creates log entry with given topics and data - fn log(&mut self, topics: Vec, data: &[u8]) -> Result<()>; - - /// Should be called when transaction calls `RETURN` opcode. - /// Returns gas_left if cost of returning the data is not too high. - fn ret(self, gas: &U256, data: &ReturnData, apply_state: bool) -> Result; - - /// Should be called when contract commits suicide. - /// Address to which funds should be refunded. - fn suicide(&mut self, refund_address: &Address) -> Result<()> ; - - /// Returns schedule. - fn schedule(&self) -> &Schedule; - - /// Returns environment info. - fn env_info(&self) -> &EnvInfo; - - /// Returns current depth of execution. - /// - /// If contract A calls contract B, and contract B calls C, - /// then A depth is 0, B is 1, C is 2 and so on. - fn depth(&self) -> usize; - - /// Increments sstore refunds counter. - fn add_sstore_refund(&mut self, value: usize); - - /// Decrements sstore refunds counter. - fn sub_sstore_refund(&mut self, value: usize); - - /// Decide if any more operations should be traced. Passthrough for the VM trace. - fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool { false } - - /// Prepare to trace an operation. Passthrough for the VM trace. - fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256, _mem_written: Option<(usize, usize)>, _store_written: Option<(U256, U256)>) {} - - /// Trace the finalised execution of a single instruction. - fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem: &[u8]) {} - - /// Check if running in static context. - fn is_static(&self) -> bool; + /// Returns the storage value for a given key if reversion happens on the current transaction. + fn initial_storage_at(&self, key: &H256) -> Result; + + /// Returns a value for given key. + fn storage_at(&self, key: &H256) -> Result; + + /// Stores a value for given key. + fn set_storage(&mut self, key: H256, value: H256) -> Result<()>; + + /// Determine whether an account exists. + fn exists(&self, address: &Address) -> Result; + + /// Determine whether an account exists and is not null (zero balance/nonce, no code). + fn exists_and_not_null(&self, address: &Address) -> Result; + + /// Balance of the origin account. + fn origin_balance(&self) -> Result; + + /// Returns address balance. + fn balance(&self, address: &Address) -> Result; + + /// Returns the hash of one of the 256 most recent complete blocks. + fn blockhash(&mut self, number: &U256) -> H256; + + /// Creates new contract. + /// + /// Returns gas_left and contract address if contract creation was successful. + fn create( + &mut self, + gas: &U256, + value: &U256, + code: &[u8], + address: CreateContractAddress, + trap: bool, + ) -> ::std::result::Result; + + /// Message call. + /// + /// Returns Err, if we run out of gas. + /// Otherwise returns call_result which contains gas left + /// and true if subcall was successfull. + fn call( + &mut self, + gas: &U256, + sender_address: &Address, + receive_address: &Address, + value: Option, + data: &[u8], + code_address: &Address, + call_type: CallType, + trap: bool, + ) -> ::std::result::Result; + + /// Returns code at given address + fn extcode(&self, address: &Address) -> Result>>; + + /// Returns code hash at given address + fn extcodehash(&self, address: &Address) -> Result>; + + /// Returns code size at given address + fn extcodesize(&self, address: &Address) -> Result>; + + /// Creates log entry with given topics and data + fn log(&mut self, topics: Vec, data: &[u8]) -> Result<()>; + + /// Should be called when transaction calls `RETURN` opcode. + /// Returns gas_left if cost of returning the data is not too high. + fn ret(self, gas: &U256, data: &ReturnData, apply_state: bool) -> Result; + + /// Should be called when contract commits suicide. + /// Address to which funds should be refunded. + fn suicide(&mut self, refund_address: &Address) -> Result<()>; + + /// Returns schedule. + fn schedule(&self) -> &Schedule; + + /// Returns environment info. + fn env_info(&self) -> &EnvInfo; + + /// Returns the chain ID of the blockchain + fn chain_id(&self) -> u64; + + /// Returns current depth of execution. + /// + /// If contract A calls contract B, and contract B calls C, + /// then A depth is 0, B is 1, C is 2 and so on. + fn depth(&self) -> usize; + + /// Increments sstore refunds counter. + fn add_sstore_refund(&mut self, value: usize); + + /// Decrements sstore refunds counter. + fn sub_sstore_refund(&mut self, value: usize); + + /// Decide if any more operations should be traced. Passthrough for the VM trace. + fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool { + false + } + + /// Prepare to trace an operation. Passthrough for the VM trace. + /// For each call of `trace_prepare_execute` either `trace_failed` or `trace_executed` MUST be called. + fn trace_prepare_execute( + &mut self, + _pc: usize, + _instruction: u8, + _gas_cost: U256, + _mem_written: Option<(usize, usize)>, + _store_written: Option<(U256, U256)>, + ) { + } + + /// Trace the execution failure of a single instruction. + fn trace_failed(&mut self) {} + + /// Trace the finalised execution of a single instruction. + fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem: &[u8]) {} + + /// Check if running in static context. + fn is_static(&self) -> bool; } diff --git a/ethcore/vm/src/lib.rs b/ethcore/vm/src/lib.rs index f239e405680..4f9b747efda 100644 --- a/ethcore/vm/src/lib.rs +++ b/ethcore/vm/src/lib.rs @@ -1,63 +1,62 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Virtual machines support library extern crate ethereum_types; -extern crate parity_bytes as bytes; extern crate ethjson; -extern crate rlp; extern crate keccak_hash as hash; +extern crate parity_bytes as bytes; extern crate patricia_trie_ethereum as ethtrie; -extern crate trie_db as trie; +extern crate rlp; mod action_params; mod call_type; mod env_info; -mod schedule; +mod error; mod ext; mod return_data; -mod error; +mod schedule; pub mod tests; pub use action_params::{ActionParams, ActionValue, ParamsType}; pub use call_type::CallType; pub use env_info::{EnvInfo, LastHashes}; -pub use schedule::{Schedule, CleanDustMode, WasmCosts}; -pub use ext::{Ext, MessageCallResult, ContractCreateResult, CreateContractAddress}; -pub use return_data::{ReturnData, GasLeft}; -pub use error::{Error, Result, TrapResult, TrapError, TrapKind, ExecTrapResult, ExecTrapError}; +pub use error::{Error, ExecTrapError, ExecTrapResult, Result, TrapError, TrapKind, TrapResult}; +pub use ext::{ContractCreateResult, CreateContractAddress, Ext, MessageCallResult}; +pub use return_data::{GasLeft, ReturnData}; +pub use schedule::{CleanDustMode, Schedule, WasmCosts}; /// Virtual Machine interface pub trait Exec: Send { - /// This function should be used to execute transaction. - /// It returns either an error, a known amount of gas left, or parameters to be used - /// to compute the final gas left. - fn exec(self: Box, ext: &mut Ext) -> ExecTrapResult; + /// This function should be used to execute transaction. + /// It returns either an error, a known amount of gas left, or parameters to be used + /// to compute the final gas left. + fn exec(self: Box, ext: &mut dyn Ext) -> ExecTrapResult; } /// Resume call interface pub trait ResumeCall: Send { - /// Resume an execution for call, returns back the Vm interface. - fn resume_call(self: Box, result: MessageCallResult) -> Box; + /// Resume an execution for call, returns back the Vm interface. + fn resume_call(self: Box, result: MessageCallResult) -> Box; } /// Resume create interface pub trait ResumeCreate: Send { - /// Resume an execution from create, returns back the Vm interface. - fn resume_create(self: Box, result: ContractCreateResult) -> Box; + /// Resume an execution from create, returns back the Vm interface. + fn resume_create(self: Box, result: ContractCreateResult) -> Box; } diff --git a/ethcore/vm/src/return_data.rs b/ethcore/vm/src/return_data.rs index 85fdb361db5..5c17783faac 100644 --- a/ethcore/vm/src/return_data.rs +++ b/ethcore/vm/src/return_data.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Return data structures @@ -21,50 +21,46 @@ use ethereum_types::U256; /// Return data buffer. Holds memory from a previous call and a slice into that memory. #[derive(Debug)] pub struct ReturnData { - mem: Vec, - offset: usize, - size: usize, + mem: Vec, + offset: usize, + size: usize, } impl ::std::ops::Deref for ReturnData { - type Target = [u8]; - fn deref(&self) -> &[u8] { - &self.mem[self.offset..self.offset + self.size] - } + type Target = [u8]; + fn deref(&self) -> &[u8] { + &self.mem[self.offset..self.offset + self.size] + } } impl ReturnData { - /// Create empty `ReturnData`. - pub fn empty() -> Self { - ReturnData { - mem: Vec::new(), - offset: 0, - size: 0, - } - } - /// Create `ReturnData` from give buffer and slice. - pub fn new(mem: Vec, offset: usize, size: usize) -> Self { - ReturnData { - mem: mem, - offset: offset, - size: size, - } - } + /// Create empty `ReturnData`. + pub fn empty() -> Self { + ReturnData { + mem: Vec::new(), + offset: 0, + size: 0, + } + } + /// Create `ReturnData` from give buffer and slice. + pub fn new(mem: Vec, offset: usize, size: usize) -> Self { + ReturnData { mem, offset, size } + } } /// Gas Left: either it is a known value, or it needs to be computed by processing /// a return instruction. #[derive(Debug)] pub enum GasLeft { - /// Known gas left - Known(U256), - /// Return or Revert instruction must be processed. - NeedsReturn { - /// Amount of gas left. - gas_left: U256, - /// Return data buffer. - data: ReturnData, - /// Apply or revert state changes on revert. - apply_state: bool - }, + /// Known gas left + Known(U256), + /// Return or Revert instruction must be processed. + NeedsReturn { + /// Amount of gas left. + gas_left: U256, + /// Return data buffer. + data: ReturnData, + /// Apply or revert state changes on revert. + apply_state: bool, + }, } diff --git a/ethcore/vm/src/schedule.rs b/ethcore/vm/src/schedule.rs index d1dc3b5aad7..50a5d5e6e93 100644 --- a/ethcore/vm/src/schedule.rs +++ b/ethcore/vm/src/schedule.rs @@ -1,359 +1,403 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Cost schedule and other parameterisations for the EVM. /// Definition of the cost schedule and other parameterisations for the EVM. +#[derive(Debug)] pub struct Schedule { - /// Does it support exceptional failed code deposit - pub exceptional_failed_code_deposit: bool, - /// Does it have a delegate cal - pub have_delegate_call: bool, - /// Does it have a CREATE2 instruction - pub have_create2: bool, - /// Does it have a REVERT instruction - pub have_revert: bool, - /// Does it have a EXTCODEHASH instruction - pub have_extcodehash: bool, - /// VM stack limit - pub stack_limit: usize, - /// Max number of nested calls/creates - pub max_depth: usize, - /// Gas prices for instructions in all tiers - pub tier_step_gas: [usize; 8], - /// Gas price for `EXP` opcode - pub exp_gas: usize, - /// Additional gas for `EXP` opcode for each byte of exponent - pub exp_byte_gas: usize, - /// Gas price for `SHA3` opcode - pub sha3_gas: usize, - /// Additional gas for `SHA3` opcode for each word of hashed memory - pub sha3_word_gas: usize, - /// Gas price for loading from storage - pub sload_gas: usize, - /// Gas price for setting new value to storage (`storage==0`, `new!=0`) - pub sstore_set_gas: usize, - /// Gas price for altering value in storage - pub sstore_reset_gas: usize, - /// Gas refund for `SSTORE` clearing (when `storage!=0`, `new==0`) - pub sstore_refund_gas: usize, - /// Gas price for `JUMPDEST` opcode - pub jumpdest_gas: usize, - /// Gas price for `LOG*` - pub log_gas: usize, - /// Additional gas for data in `LOG*` - pub log_data_gas: usize, - /// Additional gas for each topic in `LOG*` - pub log_topic_gas: usize, - /// Gas price for `CREATE` opcode - pub create_gas: usize, - /// Gas price for `*CALL*` opcodes - pub call_gas: usize, - /// Stipend for transfer for `CALL|CALLCODE` opcode when `value>0` - pub call_stipend: usize, - /// Additional gas required for value transfer (`CALL|CALLCODE`) - pub call_value_transfer_gas: usize, - /// Additional gas for creating new account (`CALL|CALLCODE`) - pub call_new_account_gas: usize, - /// Refund for SUICIDE - pub suicide_refund_gas: usize, - /// Gas for used memory - pub memory_gas: usize, - /// Coefficient used to convert memory size to gas price for memory - pub quad_coeff_div: usize, - /// Cost for contract length when executing `CREATE` - pub create_data_gas: usize, - /// Maximum code size when creating a contract. - pub create_data_limit: usize, - /// Transaction cost - pub tx_gas: usize, - /// `CREATE` transaction cost - pub tx_create_gas: usize, - /// Additional cost for empty data transaction - pub tx_data_zero_gas: usize, - /// Aditional cost for non-empty data transaction - pub tx_data_non_zero_gas: usize, - /// Gas price for copying memory - pub copy_gas: usize, - /// Price of EXTCODESIZE - pub extcodesize_gas: usize, - /// Base price of EXTCODECOPY - pub extcodecopy_base_gas: usize, - /// Price of BALANCE - pub balance_gas: usize, - /// Price of EXTCODEHASH - pub extcodehash_gas: usize, - /// Price of SUICIDE - pub suicide_gas: usize, - /// Amount of additional gas to pay when SUICIDE credits a non-existant account - pub suicide_to_new_account_cost: usize, - /// If Some(x): let limit = GAS * (x - 1) / x; let CALL's gas = min(requested, limit). let CREATE's gas = limit. - /// If None: let CALL's gas = (requested > GAS ? [OOG] : GAS). let CREATE's gas = GAS - pub sub_gas_cap_divisor: Option, - /// Don't ever make empty accounts; contracts start with nonce=1. Also, don't charge 25k when sending/suicide zero-value. - pub no_empty: bool, - /// Kill empty accounts if touched. - pub kill_empty: bool, - /// Blockhash instruction gas cost. - pub blockhash_gas: usize, - /// Static Call opcode enabled. - pub have_static_call: bool, - /// RETURNDATA and RETURNDATASIZE opcodes enabled. - pub have_return_data: bool, - /// SHL, SHR, SAR opcodes enabled. - pub have_bitwise_shifting: bool, - /// Kill basic accounts below this balance if touched. - pub kill_dust: CleanDustMode, - /// Enable EIP-1283 rules - pub eip1283: bool, - /// VM execution does not increase null signed address nonce if this field is true. - pub keep_unsigned_nonce: bool, - /// Wasm extra schedule settings, if wasm activated - pub wasm: Option, + /// Does it support exceptional failed code deposit + pub exceptional_failed_code_deposit: bool, + /// Does it have a delegate cal + pub have_delegate_call: bool, + /// Does it have a CREATE2 instruction + pub have_create2: bool, + /// Does it have a REVERT instruction + pub have_revert: bool, + /// Does it have a EXTCODEHASH instruction + pub have_extcodehash: bool, + /// VM stack limit + pub stack_limit: usize, + /// Max number of nested calls/creates + pub max_depth: usize, + /// Gas prices for instructions in all tiers + pub tier_step_gas: [usize; 8], + /// Gas price for `EXP` opcode + pub exp_gas: usize, + /// Additional gas for `EXP` opcode for each byte of exponent + pub exp_byte_gas: usize, + /// Gas price for `SHA3` opcode + pub sha3_gas: usize, + /// Additional gas for `SHA3` opcode for each word of hashed memory + pub sha3_word_gas: usize, + /// Gas price for loading from storage + pub sload_gas: usize, + /// Gas price for setting new value to storage (`storage==0`, `new!=0`) + pub sstore_set_gas: usize, + /// Gas price for altering value in storage + pub sstore_reset_gas: usize, + /// Gas refund for `SSTORE` clearing (when `storage!=0`, `new==0`) + pub sstore_refund_gas: usize, + /// Gas price for `JUMPDEST` opcode + pub jumpdest_gas: usize, + /// Gas price for `LOG*` + pub log_gas: usize, + /// Additional gas for data in `LOG*` + pub log_data_gas: usize, + /// Additional gas for each topic in `LOG*` + pub log_topic_gas: usize, + /// Gas price for `CREATE` opcode + pub create_gas: usize, + /// Gas price for `*CALL*` opcodes + pub call_gas: usize, + /// Stipend for transfer for `CALL|CALLCODE` opcode when `value>0` + pub call_stipend: usize, + /// Additional gas required for value transfer (`CALL|CALLCODE`) + pub call_value_transfer_gas: usize, + /// Additional gas for creating new account (`CALL|CALLCODE`) + pub call_new_account_gas: usize, + /// Refund for SUICIDE + pub suicide_refund_gas: usize, + /// Gas for used memory + pub memory_gas: usize, + /// Coefficient used to convert memory size to gas price for memory + pub quad_coeff_div: usize, + /// Cost for contract length when executing `CREATE` + pub create_data_gas: usize, + /// Maximum code size when creating a contract. + pub create_data_limit: usize, + /// Transaction cost + pub tx_gas: usize, + /// `CREATE` transaction cost + pub tx_create_gas: usize, + /// Additional cost for empty data transaction + pub tx_data_zero_gas: usize, + /// Additional cost for non-empty data transaction + pub tx_data_non_zero_gas: usize, + /// Gas price for copying memory + pub copy_gas: usize, + /// Price of EXTCODESIZE + pub extcodesize_gas: usize, + /// Base price of EXTCODECOPY + pub extcodecopy_base_gas: usize, + /// Price of BALANCE + pub balance_gas: usize, + /// Price of EXTCODEHASH + pub extcodehash_gas: usize, + /// Price of SUICIDE + pub suicide_gas: usize, + /// Amount of additional gas to pay when SUICIDE credits a non-existant account + pub suicide_to_new_account_cost: usize, + /// If Some(x): let limit = GAS * (x - 1) / x; let CALL's gas = min(requested, limit). let CREATE's gas = limit. + /// If None: let CALL's gas = (requested > GAS ? [OOG] : GAS). let CREATE's gas = GAS + pub sub_gas_cap_divisor: Option, + /// Don't ever make empty accounts; contracts start with nonce=1. Also, don't charge 25k when sending/suicide zero-value. + pub no_empty: bool, + /// Kill empty accounts if touched. + pub kill_empty: bool, + /// Blockhash instruction gas cost. + pub blockhash_gas: usize, + /// Static Call opcode enabled. + pub have_static_call: bool, + /// RETURNDATA and RETURNDATASIZE opcodes enabled. + pub have_return_data: bool, + /// SHL, SHR, SAR opcodes enabled. + pub have_bitwise_shifting: bool, + /// CHAINID opcode enabled. + pub have_chain_id: bool, + /// SELFBALANCE opcode enabled. + pub have_selfbalance: bool, + /// BEGINSUB, JUMPSUB and RETURNSUB opcodes enabled. + pub have_subs: bool, + /// Kill basic accounts below this balance if touched. + pub kill_dust: CleanDustMode, + /// Enable EIP-1283 rules + pub eip1283: bool, + /// Enable EIP-1706 rules + pub eip1706: bool, + /// VM execution does not increase null signed address nonce if this field is true. + pub keep_unsigned_nonce: bool, + /// Wasm extra schedule settings, if wasm activated + pub wasm: Option, } /// Wasm cost table +#[derive(Debug)] pub struct WasmCosts { - /// Default opcode cost - pub regular: u32, - /// Div operations multiplier. - pub div: u32, - /// Div operations multiplier. - pub mul: u32, - /// Memory (load/store) operations multiplier. - pub mem: u32, - /// General static query of U256 value from env-info - pub static_u256: u32, - /// General static query of Address value from env-info - pub static_address: u32, - /// Memory stipend. Amount of free memory (in 64kb pages) each contract can use for stack. - pub initial_mem: u32, - /// Grow memory cost, per page (64kb) - pub grow_mem: u32, - /// Memory copy cost, per byte - pub memcpy: u32, - /// Max stack height (native WebAssembly stack limiter) - pub max_stack_height: u32, - /// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div` - pub opcodes_mul: u32, - /// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div` - pub opcodes_div: u32, - /// Whether create2 extern function is activated. - pub have_create2: bool, - /// Whether gasleft extern function is activated. - pub have_gasleft: bool, + /// Default opcode cost + pub regular: u32, + /// Div operations multiplier. + pub div: u32, + /// Div operations multiplier. + pub mul: u32, + /// Memory (load/store) operations multiplier. + pub mem: u32, + /// General static query of U256 value from env-info + pub static_u256: u32, + /// General static query of Address value from env-info + pub static_address: u32, + /// Memory stipend. Amount of free memory (in 64kb pages) each contract can use for stack. + pub initial_mem: u32, + /// Grow memory cost, per page (64kb) + pub grow_mem: u32, + /// Memory copy cost, per byte + pub memcpy: u32, + /// Max stack height (native WebAssembly stack limiter) + pub max_stack_height: u32, + /// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div` + pub opcodes_mul: u32, + /// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div` + pub opcodes_div: u32, + /// Whether create2 extern function is activated. + pub have_create2: bool, + /// Whether gasleft extern function is activated. + pub have_gasleft: bool, } impl Default for WasmCosts { - fn default() -> Self { - WasmCosts { - regular: 1, - div: 16, - mul: 4, - mem: 2, - static_u256: 64, - static_address: 40, - initial_mem: 4096, - grow_mem: 8192, - memcpy: 1, - max_stack_height: 64*1024, - opcodes_mul: 3, - opcodes_div: 8, - have_create2: false, - have_gasleft: false, - } - } + fn default() -> Self { + WasmCosts { + regular: 1, + div: 16, + mul: 4, + mem: 2, + static_u256: 64, + static_address: 40, + initial_mem: 4096, + grow_mem: 8192, + memcpy: 1, + max_stack_height: 64 * 1024, + opcodes_mul: 3, + opcodes_div: 8, + have_create2: false, + have_gasleft: false, + } + } } /// Dust accounts cleanup mode. -#[derive(PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq)] pub enum CleanDustMode { - /// Dust cleanup is disabled. - Off, - /// Basic dust accounts will be removed. - BasicOnly, - /// Basic and contract dust accounts will be removed. - WithCodeAndStorage, + /// Dust cleanup is disabled. + Off, + /// Basic dust accounts will be removed. + BasicOnly, + /// Basic and contract dust accounts will be removed. + WithCodeAndStorage, } impl Schedule { - /// Schedule for the Frontier-era of the Ethereum main net. - pub fn new_frontier() -> Schedule { - Self::new(false, false, 21000) - } + /// Schedule for the Frontier-era of the Ethereum main net. + pub fn new_frontier() -> Schedule { + Self::new(false, false, 21000) + } - /// Schedule for the Homestead-era of the Ethereum main net. - pub fn new_homestead() -> Schedule { - Self::new(true, true, 53000) - } + /// Schedule for the Homestead-era of the Ethereum main net. + pub fn new_homestead() -> Schedule { + Self::new(true, true, 53000) + } - /// Schedule for the post-EIP-150-era of the Ethereum main net. - pub fn new_post_eip150(max_code_size: usize, fix_exp: bool, no_empty: bool, kill_empty: bool) -> Schedule { - Schedule { - exceptional_failed_code_deposit: true, - have_delegate_call: true, - have_create2: false, - have_revert: false, - have_return_data: false, - have_bitwise_shifting: false, - have_extcodehash: false, - stack_limit: 1024, - max_depth: 1024, - tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0], - exp_gas: 10, - exp_byte_gas: if fix_exp {50} else {10}, - sha3_gas: 30, - sha3_word_gas: 6, - sload_gas: 200, - sstore_set_gas: 20000, - sstore_reset_gas: 5000, - sstore_refund_gas: 15000, - jumpdest_gas: 1, - log_gas: 375, - log_data_gas: 8, - log_topic_gas: 375, - create_gas: 32000, - call_gas: 700, - call_stipend: 2300, - call_value_transfer_gas: 9000, - call_new_account_gas: 25000, - suicide_refund_gas: 24000, - memory_gas: 3, - quad_coeff_div: 512, - create_data_gas: 200, - create_data_limit: max_code_size, - tx_gas: 21000, - tx_create_gas: 53000, - tx_data_zero_gas: 4, - tx_data_non_zero_gas: 68, - copy_gas: 3, - extcodesize_gas: 700, - extcodecopy_base_gas: 700, - extcodehash_gas: 400, - balance_gas: 400, - suicide_gas: 5000, - suicide_to_new_account_cost: 25000, - sub_gas_cap_divisor: Some(64), - no_empty: no_empty, - kill_empty: kill_empty, - blockhash_gas: 20, - have_static_call: false, - kill_dust: CleanDustMode::Off, - eip1283: false, - keep_unsigned_nonce: false, - wasm: None, - } - } + /// Schedule for the post-EIP-150-era of the Ethereum main net. + pub fn new_post_eip150( + max_code_size: usize, + fix_exp: bool, + no_empty: bool, + kill_empty: bool, + ) -> Schedule { + Schedule { + exceptional_failed_code_deposit: true, + have_delegate_call: true, + have_create2: false, + have_revert: false, + have_return_data: false, + have_bitwise_shifting: false, + have_chain_id: false, + have_selfbalance: false, + have_subs: false, + have_extcodehash: false, + stack_limit: 1024, + max_depth: 1024, + tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0], + exp_gas: 10, + exp_byte_gas: if fix_exp { 50 } else { 10 }, + sha3_gas: 30, + sha3_word_gas: 6, + sload_gas: 200, + sstore_set_gas: 20000, + sstore_reset_gas: 5000, + sstore_refund_gas: 15000, + jumpdest_gas: 1, + log_gas: 375, + log_data_gas: 8, + log_topic_gas: 375, + create_gas: 32000, + call_gas: 700, + call_stipend: 2300, + call_value_transfer_gas: 9000, + call_new_account_gas: 25000, + suicide_refund_gas: 24000, + memory_gas: 3, + quad_coeff_div: 512, + create_data_gas: 200, + create_data_limit: max_code_size, + tx_gas: 21000, + tx_create_gas: 53000, + tx_data_zero_gas: 4, + tx_data_non_zero_gas: 68, + copy_gas: 3, + extcodesize_gas: 700, + extcodecopy_base_gas: 700, + extcodehash_gas: 400, + balance_gas: 400, + suicide_gas: 5000, + suicide_to_new_account_cost: 25000, + sub_gas_cap_divisor: Some(64), + no_empty: no_empty, + kill_empty: kill_empty, + blockhash_gas: 20, + have_static_call: false, + kill_dust: CleanDustMode::Off, + eip1283: false, + eip1706: false, + keep_unsigned_nonce: false, + wasm: None, + } + } - /// Schedule for the Byzantium fork of the Ethereum main net. - pub fn new_byzantium() -> Schedule { - let mut schedule = Self::new_post_eip150(24576, true, true, true); - schedule.have_create2 = true; - schedule.have_revert = true; - schedule.have_static_call = true; - schedule.have_return_data = true; - schedule - } + /// Schedule for the Byzantium fork of the Ethereum main net. + pub fn new_byzantium() -> Schedule { + let mut schedule = Self::new_post_eip150(24576, true, true, true); + schedule.have_create2 = true; + schedule.have_revert = true; + schedule.have_static_call = true; + schedule.have_return_data = true; + schedule + } - /// Schedule for the Constantinople fork of the Ethereum main net. - pub fn new_constantinople() -> Schedule { - let mut schedule = Self::new_byzantium(); - schedule.have_bitwise_shifting = true; - schedule - } + /// Schedule for the Constantinople fork of the Ethereum main net. + pub fn new_constantinople() -> Schedule { + let mut schedule = Self::new_byzantium(); + schedule.have_bitwise_shifting = true; + schedule + } - fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule { - Schedule { - exceptional_failed_code_deposit: efcd, - have_delegate_call: hdc, - have_create2: false, - have_revert: false, - have_return_data: false, - have_bitwise_shifting: false, - have_extcodehash: false, - stack_limit: 1024, - max_depth: 1024, - tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0], - exp_gas: 10, - exp_byte_gas: 10, - sha3_gas: 30, - sha3_word_gas: 6, - sload_gas: 50, - sstore_set_gas: 20000, - sstore_reset_gas: 5000, - sstore_refund_gas: 15000, - jumpdest_gas: 1, - log_gas: 375, - log_data_gas: 8, - log_topic_gas: 375, - create_gas: 32000, - call_gas: 40, - call_stipend: 2300, - call_value_transfer_gas: 9000, - call_new_account_gas: 25000, - suicide_refund_gas: 24000, - memory_gas: 3, - quad_coeff_div: 512, - create_data_gas: 200, - create_data_limit: usize::max_value(), - tx_gas: 21000, - tx_create_gas: tcg, - tx_data_zero_gas: 4, - tx_data_non_zero_gas: 68, - copy_gas: 3, - extcodesize_gas: 20, - extcodecopy_base_gas: 20, - extcodehash_gas: 400, - balance_gas: 20, - suicide_gas: 0, - suicide_to_new_account_cost: 0, - sub_gas_cap_divisor: None, - no_empty: false, - kill_empty: false, - blockhash_gas: 20, - have_static_call: false, - kill_dust: CleanDustMode::Off, - eip1283: false, - keep_unsigned_nonce: false, - wasm: None, - } - } + /// Schedule for the Istanbul fork of the Ethereum main net. + pub fn new_istanbul() -> Schedule { + let mut schedule = Self::new_constantinople(); + schedule.have_chain_id = true; // EIP 1344 + schedule.tx_data_non_zero_gas = 16; // EIP 2028 + schedule.sload_gas = 800; // EIP 1884 + schedule.balance_gas = 700; // EIP 1884 + schedule.extcodehash_gas = 700; // EIP 1884 + schedule.have_selfbalance = true; // EIP 1884 + schedule + } - /// Returns wasm schedule - /// - /// May panic if there is no wasm schedule - pub fn wasm(&self) -> &WasmCosts { - // *** Prefer PANIC here instead of silently breaking consensus! *** - self.wasm.as_ref().expect("Wasm schedule expected to exist while checking wasm contract. Misconfigured client?") - } + /// Schedule for the Berlin fork of the Ethereum main net. + pub fn new_berlin() -> Schedule { + let mut schedule = Self::new_istanbul(); + schedule.have_subs = true; // EIP 2315 + schedule + } + + fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule { + Schedule { + exceptional_failed_code_deposit: efcd, + have_delegate_call: hdc, + have_create2: false, + have_revert: false, + have_return_data: false, + have_bitwise_shifting: false, + have_chain_id: false, + have_selfbalance: false, + have_subs: false, + have_extcodehash: false, + stack_limit: 1024, + max_depth: 1024, + tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0], + exp_gas: 10, + exp_byte_gas: 10, + sha3_gas: 30, + sha3_word_gas: 6, + sload_gas: 50, + sstore_set_gas: 20000, + sstore_reset_gas: 5000, + sstore_refund_gas: 15000, + jumpdest_gas: 1, + log_gas: 375, + log_data_gas: 8, + log_topic_gas: 375, + create_gas: 32000, + call_gas: 40, + call_stipend: 2300, + call_value_transfer_gas: 9000, + call_new_account_gas: 25000, + suicide_refund_gas: 24000, + memory_gas: 3, + quad_coeff_div: 512, + create_data_gas: 200, + create_data_limit: usize::max_value(), + tx_gas: 21000, + tx_create_gas: tcg, + tx_data_zero_gas: 4, + tx_data_non_zero_gas: 68, + copy_gas: 3, + extcodesize_gas: 20, + extcodecopy_base_gas: 20, + extcodehash_gas: 400, + balance_gas: 20, + suicide_gas: 0, + suicide_to_new_account_cost: 0, + sub_gas_cap_divisor: None, + no_empty: false, + kill_empty: false, + blockhash_gas: 20, + have_static_call: false, + kill_dust: CleanDustMode::Off, + eip1283: false, + eip1706: false, + keep_unsigned_nonce: false, + wasm: None, + } + } + + /// Returns wasm schedule + /// + /// May panic if there is no wasm schedule + pub fn wasm(&self) -> &WasmCosts { + // *** Prefer PANIC here instead of silently breaking consensus! *** + self.wasm.as_ref().expect( + "Wasm schedule expected to exist while checking wasm contract. Misconfigured client?", + ) + } } impl Default for Schedule { - fn default() -> Self { - Schedule::new_frontier() - } + fn default() -> Self { + Schedule::new_frontier() + } } #[test] #[cfg(test)] fn schedule_evm_assumptions() { - let s1 = Schedule::new_frontier(); - let s2 = Schedule::new_homestead(); + let s1 = Schedule::new_frontier(); + let s2 = Schedule::new_homestead(); - // To optimize division we assume 2**9 for quad_coeff_div - assert_eq!(s1.quad_coeff_div, 512); - assert_eq!(s2.quad_coeff_div, 512); + // To optimize division we assume 2**9 for quad_coeff_div + assert_eq!(s1.quad_coeff_div, 512); + assert_eq!(s2.quad_coeff_div, 512); } diff --git a/ethcore/vm/src/tests.rs b/ethcore/vm/src/tests.rs index 306ad871da9..a5ada1dd2c5 100644 --- a/ethcore/vm/src/tests.rs +++ b/ethcore/vm/src/tests.rs @@ -1,52 +1,60 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::sync::Arc; -use std::collections::{HashMap, HashSet}; +use std::{ + collections::{HashMap, HashSet}, + sync::Arc, +}; -use ethereum_types::{U256, H256, Address}; use bytes::Bytes; -use { - CallType, Schedule, EnvInfo, - ReturnData, Ext, ContractCreateResult, MessageCallResult, - CreateContractAddress, Result, GasLeft, -}; -use hash::keccak; use error::TrapKind; +use ethereum_types::{Address, H256, U256}; +use hash::keccak; +use CallType; +use ContractCreateResult; +use CreateContractAddress; +use EnvInfo; +use Ext; +use GasLeft; +use MessageCallResult; +use Result; +use ReturnData; +use Schedule; pub struct FakeLogEntry { - pub topics: Vec, - pub data: Bytes + pub topics: Vec, + pub data: Bytes, } #[derive(PartialEq, Eq, Hash, Debug)] pub enum FakeCallType { - Call, Create + Call, + Create, } #[derive(PartialEq, Eq, Hash, Debug)] pub struct FakeCall { - pub call_type: FakeCallType, - pub create_scheme: Option, - pub gas: U256, - pub sender_address: Option
, - pub receive_address: Option
, - pub value: Option, - pub data: Bytes, - pub code_address: Option
, + pub call_type: FakeCallType, + pub create_scheme: Option, + pub gas: U256, + pub sender_address: Option
, + pub receive_address: Option
, + pub value: Option, + pub data: Bytes, + pub code_address: Option
, } /// Fake externalities test structure. @@ -54,192 +62,218 @@ pub struct FakeCall { /// Can't do recursive calls. #[derive(Default)] pub struct FakeExt { - pub store: HashMap, - pub suicides: HashSet
, - pub calls: HashSet, - pub sstore_clears: i128, - pub depth: usize, - pub blockhashes: HashMap, - pub codes: HashMap>, - pub logs: Vec, - pub info: EnvInfo, - pub schedule: Schedule, - pub balances: HashMap, - pub tracing: bool, - pub is_static: bool, + pub store: HashMap, + pub suicides: HashSet
, + pub calls: HashSet, + pub sstore_clears: i128, + pub depth: usize, + pub blockhashes: HashMap, + pub codes: HashMap>, + pub logs: Vec, + pub info: EnvInfo, + pub schedule: Schedule, + pub balances: HashMap, + pub tracing: bool, + pub is_static: bool, + + chain_id: u64, } // similar to the normal `finalize` function, but ignoring NeedsReturn. pub fn test_finalize(res: Result) -> Result { - match res { - Ok(GasLeft::Known(gas)) => Ok(gas), - Ok(GasLeft::NeedsReturn{..}) => unimplemented!(), // since ret is unimplemented. - Err(e) => Err(e), - } + match res { + Ok(GasLeft::Known(gas)) => Ok(gas), + Ok(GasLeft::NeedsReturn { .. }) => unimplemented!(), // since ret is unimplemented. + Err(e) => Err(e), + } } impl FakeExt { - /// New fake externalities - pub fn new() -> Self { - FakeExt::default() - } - - /// New fake externalities with byzantium schedule rules - pub fn new_byzantium() -> Self { - let mut ext = FakeExt::default(); - ext.schedule = Schedule::new_byzantium(); - ext - } - - /// New fake externalities with constantinople schedule rules - pub fn new_constantinople() -> Self { - let mut ext = FakeExt::default(); - ext.schedule = Schedule::new_constantinople(); - ext - } - - /// Alter fake externalities to allow wasm - pub fn with_wasm(mut self) -> Self { - self.schedule.wasm = Some(Default::default()); - self - } + /// New fake externalities + pub fn new() -> Self { + FakeExt::default() + } + + /// New fake externalities with byzantium schedule rules + pub fn new_byzantium() -> Self { + let mut ext = FakeExt::default(); + ext.schedule = Schedule::new_byzantium(); + ext + } + + /// New fake externalities with constantinople schedule rules + pub fn new_constantinople() -> Self { + let mut ext = FakeExt::default(); + ext.schedule = Schedule::new_constantinople(); + ext + } + + /// New fake externalities with Istanbul schedule rules + pub fn new_istanbul() -> Self { + let mut ext = FakeExt::default(); + ext.schedule = Schedule::new_istanbul(); + ext + } + + /// New fake externalities with Berlin schedule rules + pub fn new_berlin() -> Self { + let mut ext = FakeExt::default(); + ext.schedule = Schedule::new_berlin(); + ext + } + + /// Alter fake externalities to allow wasm + pub fn with_wasm(mut self) -> Self { + self.schedule.wasm = Some(Default::default()); + self + } + + /// Set chain ID + pub fn with_chain_id(mut self, chain_id: u64) -> Self { + self.chain_id = chain_id; + self + } } impl Ext for FakeExt { - fn initial_storage_at(&self, _key: &H256) -> Result { - Ok(H256::new()) - } - - fn storage_at(&self, key: &H256) -> Result { - Ok(self.store.get(key).unwrap_or(&H256::new()).clone()) - } - - fn set_storage(&mut self, key: H256, value: H256) -> Result<()> { - self.store.insert(key, value); - Ok(()) - } - - fn exists(&self, address: &Address) -> Result { - Ok(self.balances.contains_key(address)) - } - - fn exists_and_not_null(&self, address: &Address) -> Result { - Ok(self.balances.get(address).map_or(false, |b| !b.is_zero())) - } - - fn origin_balance(&self) -> Result { - unimplemented!() - } - - fn balance(&self, address: &Address) -> Result { - Ok(self.balances[address]) - } - - fn blockhash(&mut self, number: &U256) -> H256 { - self.blockhashes.get(number).unwrap_or(&H256::new()).clone() - } - - fn create( - &mut self, - gas: &U256, - value: &U256, - code: &[u8], - address: CreateContractAddress, - _trap: bool, - ) -> ::std::result::Result { - self.calls.insert(FakeCall { - call_type: FakeCallType::Create, - create_scheme: Some(address), - gas: *gas, - sender_address: None, - receive_address: None, - value: Some(*value), - data: code.to_vec(), - code_address: None - }); - // TODO: support traps in testing. - Ok(ContractCreateResult::Failed) - } - - fn call( - &mut self, - gas: &U256, - sender_address: &Address, - receive_address: &Address, - value: Option, - data: &[u8], - code_address: &Address, - _call_type: CallType, - _trap: bool, - ) -> ::std::result::Result { - self.calls.insert(FakeCall { - call_type: FakeCallType::Call, - create_scheme: None, - gas: *gas, - sender_address: Some(sender_address.clone()), - receive_address: Some(receive_address.clone()), - value: value, - data: data.to_vec(), - code_address: Some(code_address.clone()) - }); - // TODO: support traps in testing. - Ok(MessageCallResult::Success(*gas, ReturnData::empty())) - } - - fn extcode(&self, address: &Address) -> Result>> { - Ok(self.codes.get(address).cloned()) - } - - fn extcodesize(&self, address: &Address) -> Result> { - Ok(self.codes.get(address).map(|c| c.len())) - } - - fn extcodehash(&self, address: &Address) -> Result> { - Ok(self.codes.get(address).map(|c| keccak(c.as_ref()))) - } - - fn log(&mut self, topics: Vec, data: &[u8]) -> Result<()> { - self.logs.push(FakeLogEntry { - topics: topics, - data: data.to_vec() - }); - Ok(()) - } - - fn ret(self, _gas: &U256, _data: &ReturnData, _apply_state: bool) -> Result { - unimplemented!(); - } - - fn suicide(&mut self, refund_address: &Address) -> Result<()> { - self.suicides.insert(refund_address.clone()); - Ok(()) - } - - fn schedule(&self) -> &Schedule { - &self.schedule - } - - fn env_info(&self) -> &EnvInfo { - &self.info - } - - fn depth(&self) -> usize { - self.depth - } - - fn is_static(&self) -> bool { - self.is_static - } - - fn add_sstore_refund(&mut self, value: usize) { - self.sstore_clears += value as i128; - } - - fn sub_sstore_refund(&mut self, value: usize) { - self.sstore_clears -= value as i128; - } - - fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _gas: U256) -> bool { - self.tracing - } + fn initial_storage_at(&self, _key: &H256) -> Result { + Ok(H256::new()) + } + + fn storage_at(&self, key: &H256) -> Result { + Ok(self.store.get(key).unwrap_or(&H256::new()).clone()) + } + + fn set_storage(&mut self, key: H256, value: H256) -> Result<()> { + self.store.insert(key, value); + Ok(()) + } + + fn exists(&self, address: &Address) -> Result { + Ok(self.balances.contains_key(address)) + } + + fn exists_and_not_null(&self, address: &Address) -> Result { + Ok(self.balances.get(address).map_or(false, |b| !b.is_zero())) + } + + fn origin_balance(&self) -> Result { + unimplemented!() + } + + fn balance(&self, address: &Address) -> Result { + Ok(self.balances[address]) + } + + fn blockhash(&mut self, number: &U256) -> H256 { + self.blockhashes.get(number).unwrap_or(&H256::new()).clone() + } + + fn create( + &mut self, + gas: &U256, + value: &U256, + code: &[u8], + address: CreateContractAddress, + _trap: bool, + ) -> ::std::result::Result { + self.calls.insert(FakeCall { + call_type: FakeCallType::Create, + create_scheme: Some(address), + gas: *gas, + sender_address: None, + receive_address: None, + value: Some(*value), + data: code.to_vec(), + code_address: None, + }); + // TODO: support traps in testing. + Ok(ContractCreateResult::Failed) + } + + fn call( + &mut self, + gas: &U256, + sender_address: &Address, + receive_address: &Address, + value: Option, + data: &[u8], + code_address: &Address, + _call_type: CallType, + _trap: bool, + ) -> ::std::result::Result { + self.calls.insert(FakeCall { + call_type: FakeCallType::Call, + create_scheme: None, + gas: *gas, + sender_address: Some(sender_address.clone()), + receive_address: Some(receive_address.clone()), + value: value, + data: data.to_vec(), + code_address: Some(code_address.clone()), + }); + // TODO: support traps in testing. + Ok(MessageCallResult::Success(*gas, ReturnData::empty())) + } + + fn extcode(&self, address: &Address) -> Result>> { + Ok(self.codes.get(address).cloned()) + } + + fn extcodesize(&self, address: &Address) -> Result> { + Ok(self.codes.get(address).map(|c| c.len())) + } + + fn extcodehash(&self, address: &Address) -> Result> { + Ok(self.codes.get(address).map(|c| keccak(c.as_ref()))) + } + + fn log(&mut self, topics: Vec, data: &[u8]) -> Result<()> { + self.logs.push(FakeLogEntry { + topics, + data: data.to_vec(), + }); + Ok(()) + } + + fn ret(self, _gas: &U256, _data: &ReturnData, _apply_state: bool) -> Result { + unimplemented!(); + } + + fn suicide(&mut self, refund_address: &Address) -> Result<()> { + self.suicides.insert(refund_address.clone()); + Ok(()) + } + + fn schedule(&self) -> &Schedule { + &self.schedule + } + + fn env_info(&self) -> &EnvInfo { + &self.info + } + + fn chain_id(&self) -> u64 { + self.chain_id + } + + fn depth(&self) -> usize { + self.depth + } + + fn is_static(&self) -> bool { + self.is_static + } + + fn add_sstore_refund(&mut self, value: usize) { + self.sstore_clears += value as i128; + } + + fn sub_sstore_refund(&mut self, value: usize) { + self.sstore_clears -= value as i128; + } + + fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _gas: U256) -> bool { + self.tracing + } } diff --git a/ethcore/wasm/Cargo.toml b/ethcore/wasm/Cargo.toml index 02aa978d7b6..e724a6a4cf9 100644 --- a/ethcore/wasm/Cargo.toml +++ b/ethcore/wasm/Cargo.toml @@ -1,4 +1,5 @@ [package] +description = "WASM Interpreter" name = "wasm" version = "0.1.0" authors = ["Parity Technologies "] diff --git a/ethcore/wasm/run/Cargo.toml b/ethcore/wasm/run/Cargo.toml index 9260d1eee83..eee0d5fa329 100644 --- a/ethcore/wasm/run/Cargo.toml +++ b/ethcore/wasm/run/Cargo.toml @@ -1,4 +1,5 @@ [package] +description = "Parity WASM Test Run" name = "pwasm-run-test" version = "0.1.0" authors = ["Parity Technologies "] diff --git a/ethcore/wasm/run/src/fixture.rs b/ethcore/wasm/run/src/fixture.rs index 42117d6dfd3..d1fe1970afe 100644 --- a/ethcore/wasm/run/src/fixture.rs +++ b/ethcore/wasm/run/src/fixture.rs @@ -1,86 +1,88 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . +use ethjson::{ + bytes::Bytes, + hash::{Address, H256}, + uint::Uint, +}; use std::borrow::Cow; -use ethjson::uint::Uint; -use ethjson::hash::{Address, H256}; -use ethjson::bytes::Bytes; #[derive(Deserialize)] #[serde(untagged)] pub enum Source { - Raw(Cow<'static, String>), - Constructor { - #[serde(rename = "constructor")] - source: Cow<'static, String>, - arguments: Bytes, - sender: Address, - at: Address, - }, + Raw(Cow<'static, String>), + Constructor { + #[serde(rename = "constructor")] + source: Cow<'static, String>, + arguments: Bytes, + sender: Address, + at: Address, + }, } impl Source { - pub fn as_ref(&self) -> &str { - match *self { - Source::Raw(ref r) => r.as_ref(), - Source::Constructor { ref source, .. } => source.as_ref(), - } - } + pub fn as_ref(&self) -> &str { + match *self { + Source::Raw(ref r) => r.as_ref(), + Source::Constructor { ref source, .. } => source.as_ref(), + } + } } #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct Fixture { - pub caption: Cow<'static, String>, - pub source: Source, - pub address: Option
, - pub sender: Option
, - pub value: Option, - pub gas_limit: Option, - pub payload: Option, - pub storage: Option>, - pub asserts: Vec, + pub caption: Cow<'static, String>, + pub source: Source, + pub address: Option
, + pub sender: Option
, + pub value: Option, + pub gas_limit: Option, + pub payload: Option, + pub storage: Option>, + pub asserts: Vec, } #[derive(Deserialize, Debug)] pub struct StorageEntry { - pub key: Uint, - pub value: Uint, + pub key: Uint, + pub value: Uint, } #[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct CallLocator { - pub sender: Option
, - pub receiver: Option
, - pub value: Option, - pub data: Option, - pub code_address: Option
, + pub sender: Option
, + pub receiver: Option
, + pub value: Option, + pub data: Option, + pub code_address: Option
, } #[derive(Deserialize, Debug)] pub struct StorageAssert { - pub key: H256, - pub value: H256, + pub key: H256, + pub value: H256, } #[derive(Deserialize, Debug)] pub enum Assert { - HasCall(CallLocator), - HasStorage(StorageAssert), - UsedGas(u64), - Return(Bytes), + HasCall(CallLocator), + HasStorage(StorageAssert), + UsedGas(u64), + Return(Bytes), } diff --git a/ethcore/wasm/run/src/main.rs b/ethcore/wasm/run/src/main.rs index 0773f9b42af..63e2de00ba5 100644 --- a/ethcore/wasm/run/src/main.rs +++ b/ethcore/wasm/run/src/main.rs @@ -1,62 +1,71 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . extern crate serde; extern crate serde_json; -#[macro_use] extern crate serde_derive; +#[macro_use] +extern crate serde_derive; +extern crate clap; +extern crate env_logger; extern crate ethereum_types; extern crate ethjson; -extern crate wasm; -extern crate vm; -extern crate clap; extern crate rustc_hex; -extern crate env_logger; +extern crate vm; +extern crate wasm; mod fixture; mod runner; -use fixture::Fixture; use clap::{App, Arg}; +use fixture::Fixture; use std::fs; fn main() { - ::env_logger::init(); - - let matches = App::new("pwasm-run-test") - .arg(Arg::with_name("target") - .index(1) - .required(true) - .multiple(true) - .help("JSON fixture")) - .get_matches(); - - let mut exit_code = 0; - - for target in matches.values_of("target").expect("No target parameter") { - let mut f = fs::File::open(target).expect("Failed to open file"); - let fixtures: Vec = serde_json::from_reader(&mut f).expect("Failed to deserialize json"); - - for fixture in fixtures.into_iter() { - let fails = runner::run_fixture(&fixture); - for fail in fails.iter() { - exit_code = 1; - println!("Failed assert in test \"{}\" ('{}'): {}", fixture.caption.as_ref(), target, fail); - } - } - } - - std::process::exit(exit_code); + ::env_logger::init(); + + let matches = App::new("pwasm-run-test") + .arg( + Arg::with_name("target") + .index(1) + .required(true) + .multiple(true) + .help("JSON fixture"), + ) + .get_matches(); + + let mut exit_code = 0; + + for target in matches.values_of("target").expect("No target parameter") { + let mut f = fs::File::open(target).expect("Failed to open file"); + let fixtures: Vec = + serde_json::from_reader(&mut f).expect("Failed to deserialize json"); + + for fixture in fixtures.into_iter() { + let fails = runner::run_fixture(&fixture); + for fail in fails.iter() { + exit_code = 1; + println!( + "Failed assert in test \"{}\" ('{}'): {}", + fixture.caption.as_ref(), + target, + fail + ); + } + } + } + + std::process::exit(exit_code); } diff --git a/ethcore/wasm/run/src/runner.rs b/ethcore/wasm/run/src/runner.rs index a69fe6bf674..a348c44e2d4 100644 --- a/ethcore/wasm/run/src/runner.rs +++ b/ethcore/wasm/run/src/runner.rs @@ -1,277 +1,345 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use fixture::{Fixture, Assert, CallLocator, Source}; -use wasm::WasmInterpreter; -use vm::{self, Exec, GasLeft, ActionParams, ActionValue, ParamsType}; -use vm::tests::FakeExt; -use std::io::{self, Read}; -use std::{fs, path, fmt}; -use std::sync::Arc; -use ethereum_types::{U256, H256, H160}; +use ethereum_types::{H160, H256, U256}; +use fixture::{Assert, CallLocator, Fixture, Source}; use rustc_hex::ToHex; +use std::{ + fmt, fs, + io::{self, Read}, + path, + sync::Arc, +}; +use vm::{self, tests::FakeExt, ActionParams, ActionValue, Exec, GasLeft, ParamsType}; +use wasm::WasmInterpreter; fn load_code>(p: P) -> io::Result> { - let mut result = Vec::new(); - let mut f = fs::File::open(p)?; - f.read_to_end(&mut result)?; - Ok(result) + let mut result = Vec::new(); + let mut f = fs::File::open(p)?; + f.read_to_end(&mut result)?; + Ok(result) } fn wasm_interpreter(params: ActionParams) -> Box { - Box::new(WasmInterpreter::new(params)) + Box::new(WasmInterpreter::new(params)) } #[derive(Debug)] pub enum SpecNonconformity { - Address, + Address, } #[derive(Debug)] pub enum Fail { - Return { expected: Vec, actual: Vec }, - UsedGas { expected: u64, actual: u64 }, - Runtime(String), - Load(io::Error), - NoCall(CallLocator), - StorageMismatch { key: H256, expected: H256, actual: Option }, - Nonconformity(SpecNonconformity) + Return { + expected: Vec, + actual: Vec, + }, + UsedGas { + expected: u64, + actual: u64, + }, + Runtime(String), + Load(io::Error), + NoCall(CallLocator), + StorageMismatch { + key: H256, + expected: H256, + actual: Option, + }, + Nonconformity(SpecNonconformity), } impl Fail { - fn runtime(err: vm::Error) -> Vec { - vec![Fail::Runtime(format!("{}", err))] - } + fn runtime(err: vm::Error) -> Vec { + vec![Fail::Runtime(format!("{}", err))] + } - fn load(err: io::Error) -> Vec { - vec![Fail::Load(err)] - } + fn load(err: io::Error) -> Vec { + vec![Fail::Load(err)] + } - fn nononformity(kind: SpecNonconformity) -> Vec { - vec![Fail::Nonconformity(kind)] - } + fn nononformity(kind: SpecNonconformity) -> Vec { + vec![Fail::Nonconformity(kind)] + } } impl fmt::Display for Fail { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::Fail::*; - match *self { - Return { ref expected, ref actual } => - write!( - f, - "Expected to return result: 0x{} ({} bytes), but got 0x{} ({} bytes)", - expected.to_hex(), - expected.len(), - actual.to_hex(), - actual.len() - ), - - UsedGas { expected, actual } => - write!(f, "Expected to use gas: {}, but got actual gas used: {}", expected, actual), - - Runtime(ref s) => - write!(f, "WASM Runtime error: {}", s), - - Load(ref e) => - write!(f, "Load i/o error: {}", e), - - NoCall(ref call) => - write!(f, "Call not found: {:?}", call), - - StorageMismatch { ref key, ref expected, actual: Some(ref actual)} => - write!( - f, - "Storage key {} value mismatch, expected {}, got: {}", - key.to_vec().to_hex(), - expected.to_vec().to_hex(), - actual.to_vec().to_hex(), - ), - - StorageMismatch { ref key, ref expected, actual: None} => - write!( - f, - "No expected storage value for key {} found, expected {}", - key.to_vec().to_hex(), - expected.to_vec().to_hex(), - ), - - Nonconformity(SpecNonconformity::Address) => - write!(f, "Cannot use address when constructor is specified!"), - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::Fail::*; + match *self { + Return { + ref expected, + ref actual, + } => write!( + f, + "Expected to return result: 0x{} ({} bytes), but got 0x{} ({} bytes)", + expected.to_hex(), + expected.len(), + actual.to_hex(), + actual.len() + ), + + UsedGas { expected, actual } => write!( + f, + "Expected to use gas: {}, but got actual gas used: {}", + expected, actual + ), + + Runtime(ref s) => write!(f, "WASM Runtime error: {}", s), + + Load(ref e) => write!(f, "Load i/o error: {}", e), + + NoCall(ref call) => write!(f, "Call not found: {:?}", call), + + StorageMismatch { + ref key, + ref expected, + actual: Some(ref actual), + } => write!( + f, + "Storage key {} value mismatch, expected {}, got: {}", + key.to_vec().to_hex(), + expected.to_vec().to_hex(), + actual.to_vec().to_hex(), + ), + + StorageMismatch { + ref key, + ref expected, + actual: None, + } => write!( + f, + "No expected storage value for key {} found, expected {}", + key.to_vec().to_hex(), + expected.to_vec().to_hex(), + ), + + Nonconformity(SpecNonconformity::Address) => { + write!(f, "Cannot use address when constructor is specified!") + } + } + } } pub fn construct( - ext: &mut vm::Ext, - source: Vec, - arguments: Vec, - sender: H160, - at: H160, + ext: &mut dyn vm::Ext, + source: Vec, + arguments: Vec, + sender: H160, + at: H160, ) -> Result, vm::Error> { - - let mut params = ActionParams::default(); - params.sender = sender; - params.address = at; - params.gas = U256::from(100_000_000); - params.data = Some(arguments); - params.code = Some(Arc::new(source)); - params.params_type = ParamsType::Separate; - - Ok( - match wasm_interpreter(params).exec(ext).ok().expect("Wasm interpreter always calls with trap=false; trap never happens; qed")? { - GasLeft::Known(_) => Vec::new(), - GasLeft::NeedsReturn { data, .. } => data.to_vec(), - } - ) + let mut params = ActionParams::default(); + params.sender = sender; + params.address = at; + params.gas = U256::from(100_000_000); + params.data = Some(arguments); + params.code = Some(Arc::new(source)); + params.params_type = ParamsType::Separate; + + Ok( + match wasm_interpreter(params) + .exec(ext) + .ok() + .expect("Wasm interpreter always calls with trap=false; trap never happens; qed")? + { + GasLeft::Known(_) => Vec::new(), + GasLeft::NeedsReturn { data, .. } => data.to_vec(), + }, + ) } pub fn run_fixture(fixture: &Fixture) -> Vec { - let mut params = ActionParams::default(); - - let source = match load_code(fixture.source.as_ref()) { - Ok(code) => code, - Err(e) => { return Fail::load(e); }, - }; - - let mut ext = FakeExt::new().with_wasm(); - params.code = Some(Arc::new( - if let Source::Constructor { ref arguments, ref sender, ref at, .. } = fixture.source { - match construct(&mut ext, source, arguments.clone().into(), sender.clone().into(), at.clone().into()) { - Ok(code) => code, - Err(e) => { return Fail::runtime(e); } - } - } else { - source - } - )); - - if let Some(ref sender) = fixture.sender { - params.sender = sender.clone().into(); - } - - if let Some(ref address) = fixture.address { - if let Source::Constructor { .. } = fixture.source { - return Fail::nononformity(SpecNonconformity::Address); - } - - params.address = address.clone().into(); - } else if let Source::Constructor { ref at, .. } = fixture.source { - params.address = at.clone().into(); - } - - if let Some(gas_limit) = fixture.gas_limit { - params.gas = U256::from(gas_limit); - } - - if let Some(ref data) = fixture.payload { - params.data = Some(data.clone().into()) - } - - if let Some(value) = fixture.value { - params.value = ActionValue::Transfer(value.clone().into()) - } - - if let Some(ref storage) = fixture.storage { - for storage_entry in storage.iter() { - let key: U256 = storage_entry.key.into(); - let val: U256 = storage_entry.value.into(); - ext.store.insert(key.into(), val.into()); - } - } - - let interpreter = wasm_interpreter(params); - - let interpreter_return = match interpreter.exec(&mut ext).ok().expect("Wasm interpreter always calls with trap=false; trap never happens; qed") { - Ok(ret) => ret, - Err(e) => { return Fail::runtime(e); } - }; - let (gas_left, result) = match interpreter_return { - GasLeft::Known(gas) => { (gas, Vec::new()) }, - GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), - }; - - let mut fails = Vec::new(); - - for assert in fixture.asserts.iter() { - match *assert { - Assert::Return(ref data) => { - if &data[..] != &result[..] { - fails.push(Fail::Return { expected: (&data[..]).to_vec(), actual: (&result[..]).to_vec() }) - } - }, - Assert::UsedGas(gas) => { - let used_gas = fixture.gas_limit.unwrap_or(0) - gas_left.low_u64(); - if gas != used_gas { - fails.push(Fail::UsedGas { expected: gas, actual: used_gas }); - } - }, - Assert::HasCall(ref locator) => { - let mut found = false; - - for fake_call in ext.calls.iter() { - let mut match_ = true; - if let Some(ref data) = locator.data { - if data.as_ref() != &fake_call.data[..] { match_ = false; } - } - - if let Some(ref code_addr) = locator.code_address { - if fake_call.code_address.unwrap_or(H160::zero()) != code_addr.clone().into() { match_ = false } - } - - if let Some(ref sender) = locator.sender { - if fake_call.sender_address.unwrap_or(H160::zero()) != sender.clone().into() { match_ = false } - } - - if let Some(ref receiver) = locator.receiver { - if fake_call.receive_address.unwrap_or(H160::zero()) != receiver.clone().into() { match_ = false } - } - - if match_ { - found = true; - break; - } - } - - if !found { - fails.push(Fail::NoCall(locator.clone())) - } - }, - Assert::HasStorage(ref storage_entry) => { - let expected_storage_key: H256 = storage_entry.key.clone().into(); - let expected_storage_value: H256 = storage_entry.value.clone().into(); - let val = ext.store.get(&expected_storage_key); - - if let Some(val) = val { - if val != &expected_storage_value { - fails.push(Fail::StorageMismatch { - key: expected_storage_key, - expected: expected_storage_value, - actual: Some(val.clone()) - }) - } - } else { - fails.push(Fail::StorageMismatch { - key: expected_storage_key, - expected: expected_storage_value, - actual: None, - }) - } - - }, - } - } - fails + let mut params = ActionParams::default(); + + let source = match load_code(fixture.source.as_ref()) { + Ok(code) => code, + Err(e) => { + return Fail::load(e); + } + }; + + let mut ext = FakeExt::new().with_wasm(); + params.code = Some(Arc::new( + if let Source::Constructor { + ref arguments, + ref sender, + ref at, + .. + } = fixture.source + { + match construct( + &mut ext, + source, + arguments.clone().into(), + sender.clone().into(), + at.clone().into(), + ) { + Ok(code) => code, + Err(e) => { + return Fail::runtime(e); + } + } + } else { + source + }, + )); + + if let Some(ref sender) = fixture.sender { + params.sender = sender.clone().into(); + } + + if let Some(ref address) = fixture.address { + if let Source::Constructor { .. } = fixture.source { + return Fail::nononformity(SpecNonconformity::Address); + } + + params.address = address.clone().into(); + } else if let Source::Constructor { ref at, .. } = fixture.source { + params.address = at.clone().into(); + } + + if let Some(gas_limit) = fixture.gas_limit { + params.gas = U256::from(gas_limit); + } + + if let Some(ref data) = fixture.payload { + params.data = Some(data.clone().into()) + } + + if let Some(value) = fixture.value { + params.value = ActionValue::Transfer(value.clone().into()) + } + + if let Some(ref storage) = fixture.storage { + for storage_entry in storage.iter() { + let key: U256 = storage_entry.key.into(); + let val: U256 = storage_entry.value.into(); + ext.store.insert(key.into(), val.into()); + } + } + + let interpreter = wasm_interpreter(params); + + let interpreter_return = match interpreter + .exec(&mut ext) + .ok() + .expect("Wasm interpreter always calls with trap=false; trap never happens; qed") + { + Ok(ret) => ret, + Err(e) => { + return Fail::runtime(e); + } + }; + let (gas_left, result) = match interpreter_return { + GasLeft::Known(gas) => (gas, Vec::new()), + GasLeft::NeedsReturn { + gas_left: gas, + data: result, + apply_state: _apply, + } => (gas, result.to_vec()), + }; + + let mut fails = Vec::new(); + + for assert in fixture.asserts.iter() { + match *assert { + Assert::Return(ref data) => { + if &data[..] != &result[..] { + fails.push(Fail::Return { + expected: (&data[..]).to_vec(), + actual: (&result[..]).to_vec(), + }) + } + } + Assert::UsedGas(gas) => { + let used_gas = fixture.gas_limit.unwrap_or(0) - gas_left.low_u64(); + if gas != used_gas { + fails.push(Fail::UsedGas { + expected: gas, + actual: used_gas, + }); + } + } + Assert::HasCall(ref locator) => { + let mut found = false; + + for fake_call in ext.calls.iter() { + let mut match_ = true; + if let Some(ref data) = locator.data { + if data.as_ref() != &fake_call.data[..] { + match_ = false; + } + } + + if let Some(ref code_addr) = locator.code_address { + if fake_call.code_address.unwrap_or(H160::zero()) + != code_addr.clone().into() + { + match_ = false + } + } + + if let Some(ref sender) = locator.sender { + if fake_call.sender_address.unwrap_or(H160::zero()) != sender.clone().into() + { + match_ = false + } + } + + if let Some(ref receiver) = locator.receiver { + if fake_call.receive_address.unwrap_or(H160::zero()) + != receiver.clone().into() + { + match_ = false + } + } + + if match_ { + found = true; + break; + } + } + + if !found { + fails.push(Fail::NoCall(locator.clone())) + } + } + Assert::HasStorage(ref storage_entry) => { + let expected_storage_key: H256 = storage_entry.key.clone().into(); + let expected_storage_value: H256 = storage_entry.value.clone().into(); + let val = ext.store.get(&expected_storage_key); + + if let Some(val) = val { + if val != &expected_storage_value { + fails.push(Fail::StorageMismatch { + key: expected_storage_key, + expected: expected_storage_value, + actual: Some(val.clone()), + }) + } + } else { + fails.push(Fail::StorageMismatch { + key: expected_storage_key, + expected: expected_storage_value, + actual: None, + }) + } + } + } + } + fails } diff --git a/ethcore/wasm/src/env.rs b/ethcore/wasm/src/env.rs index b996ea000a0..ba51305c5dc 100644 --- a/ethcore/wasm/src/env.rs +++ b/ethcore/wasm/src/env.rs @@ -1,207 +1,129 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Env module glue for wasmi interpreter use std::cell::RefCell; use vm::WasmCosts; use wasmi::{ - self, Signature, Error, FuncRef, FuncInstance, MemoryDescriptor, - MemoryRef, MemoryInstance, memory_units, + self, memory_units, Error, FuncInstance, FuncRef, MemoryDescriptor, MemoryInstance, MemoryRef, + Signature, }; /// Internal ids all functions runtime supports. This is just a glue for wasmi interpreter /// that lacks high-level api and later will be factored out pub mod ids { - pub const STORAGE_WRITE_FUNC: usize = 0; - pub const STORAGE_READ_FUNC: usize = 10; - pub const RET_FUNC: usize = 20; - pub const GAS_FUNC: usize = 30; - pub const FETCH_INPUT_FUNC: usize = 40; - pub const INPUT_LENGTH_FUNC: usize = 50; - pub const CCALL_FUNC: usize = 60; - pub const SCALL_FUNC: usize = 70; - pub const DCALL_FUNC: usize = 80; - pub const VALUE_FUNC: usize = 90; - pub const CREATE_FUNC: usize = 100; - pub const SUICIDE_FUNC: usize = 110; - pub const BLOCKHASH_FUNC: usize = 120; - pub const BLOCKNUMBER_FUNC: usize = 130; - pub const COINBASE_FUNC: usize = 140; - pub const DIFFICULTY_FUNC: usize = 150; - pub const GASLIMIT_FUNC: usize = 160; - pub const TIMESTAMP_FUNC: usize = 170; - pub const ADDRESS_FUNC: usize = 180; - pub const SENDER_FUNC: usize = 190; - pub const ORIGIN_FUNC: usize = 200; - pub const ELOG_FUNC: usize = 210; - pub const CREATE2_FUNC: usize = 220; - pub const GASLEFT_FUNC: usize = 230; - - pub const PANIC_FUNC: usize = 1000; - pub const DEBUG_FUNC: usize = 1010; + pub const STORAGE_WRITE_FUNC: usize = 0; + pub const STORAGE_READ_FUNC: usize = 10; + pub const RET_FUNC: usize = 20; + pub const GAS_FUNC: usize = 30; + pub const FETCH_INPUT_FUNC: usize = 40; + pub const INPUT_LENGTH_FUNC: usize = 50; + pub const CCALL_FUNC: usize = 60; + pub const SCALL_FUNC: usize = 70; + pub const DCALL_FUNC: usize = 80; + pub const VALUE_FUNC: usize = 90; + pub const CREATE_FUNC: usize = 100; + pub const SUICIDE_FUNC: usize = 110; + pub const BLOCKHASH_FUNC: usize = 120; + pub const BLOCKNUMBER_FUNC: usize = 130; + pub const COINBASE_FUNC: usize = 140; + pub const DIFFICULTY_FUNC: usize = 150; + pub const GASLIMIT_FUNC: usize = 160; + pub const TIMESTAMP_FUNC: usize = 170; + pub const ADDRESS_FUNC: usize = 180; + pub const SENDER_FUNC: usize = 190; + pub const ORIGIN_FUNC: usize = 200; + pub const ELOG_FUNC: usize = 210; + pub const CREATE2_FUNC: usize = 220; + pub const GASLEFT_FUNC: usize = 230; + + pub const PANIC_FUNC: usize = 1000; + pub const DEBUG_FUNC: usize = 1010; } /// Signatures of all functions runtime supports. The actual dispatch happens at /// impl runtime::Runtime methods. pub mod signatures { - use wasmi::{self, ValueType}; - use wasmi::ValueType::*; - - pub struct StaticSignature(pub &'static [ValueType], pub Option); - - pub const STORAGE_READ: StaticSignature = StaticSignature( - &[I32, I32], - None, - ); - - pub const STORAGE_WRITE: StaticSignature = StaticSignature( - &[I32, I32], - None, - ); - - pub const RET: StaticSignature = StaticSignature( - &[I32, I32], - None, - ); - - pub const GAS: StaticSignature = StaticSignature( - &[I32], - None, - ); - - pub const FETCH_INPUT: StaticSignature = StaticSignature( - &[I32], - None, - ); - - pub const INPUT_LENGTH: StaticSignature = StaticSignature( - &[], - Some(I32), - ); - - pub const CCALL: StaticSignature = StaticSignature( - &[I64, I32, I32, I32, I32, I32, I32], - Some(I32), - ); - - pub const DCALL: StaticSignature = StaticSignature( - &[I64, I32, I32, I32, I32, I32], - Some(I32), - ); - - pub const SCALL: StaticSignature = StaticSignature( - &[I64, I32, I32, I32, I32, I32], - Some(I32), - ); - - pub const PANIC: StaticSignature = StaticSignature( - &[I32, I32], - None, - ); - - pub const DEBUG: StaticSignature = StaticSignature( - &[I32, I32], - None, - ); - - pub const VALUE: StaticSignature = StaticSignature( - &[I32], - None, - ); - - pub const CREATE: StaticSignature = StaticSignature( - &[I32, I32, I32, I32], - Some(I32), - ); - - pub const CREATE2: StaticSignature = StaticSignature( - &[I32, I32, I32, I32, I32], - Some(I32), - ); - - pub const SUICIDE: StaticSignature = StaticSignature( - &[I32], - None, - ); - - pub const BLOCKHASH: StaticSignature = StaticSignature( - &[I64, I32], - None, - ); - - pub const BLOCKNUMBER: StaticSignature = StaticSignature( - &[], - Some(I64), - ); - - pub const COINBASE: StaticSignature = StaticSignature( - &[I32], - None, - ); - - pub const DIFFICULTY: StaticSignature = StaticSignature( - &[I32], - None, - ); - - pub const GASLEFT: StaticSignature = StaticSignature( - &[], - Some(I64), - ); - - pub const GASLIMIT: StaticSignature = StaticSignature( - &[I32], - None, - ); - - pub const TIMESTAMP: StaticSignature = StaticSignature( - &[], - Some(I64), - ); - - pub const ADDRESS: StaticSignature = StaticSignature( - &[I32], - None, - ); - - pub const SENDER: StaticSignature = StaticSignature( - &[I32], - None, - ); - - pub const ORIGIN: StaticSignature = StaticSignature( - &[I32], - None, - ); - - pub const ELOG: StaticSignature = StaticSignature( - &[I32, I32, I32, I32], - None, - ); - - impl Into for StaticSignature { - fn into(self) -> wasmi::Signature { - wasmi::Signature::new(self.0, self.1) - } - } + use wasmi::{self, ValueType, ValueType::*}; + + pub struct StaticSignature(pub &'static [ValueType], pub Option); + + pub const STORAGE_READ: StaticSignature = StaticSignature(&[I32, I32], None); + + pub const STORAGE_WRITE: StaticSignature = StaticSignature(&[I32, I32], None); + + pub const RET: StaticSignature = StaticSignature(&[I32, I32], None); + + pub const GAS: StaticSignature = StaticSignature(&[I32], None); + + pub const FETCH_INPUT: StaticSignature = StaticSignature(&[I32], None); + + pub const INPUT_LENGTH: StaticSignature = StaticSignature(&[], Some(I32)); + + pub const CCALL: StaticSignature = + StaticSignature(&[I64, I32, I32, I32, I32, I32, I32], Some(I32)); + + pub const DCALL: StaticSignature = StaticSignature(&[I64, I32, I32, I32, I32, I32], Some(I32)); + + pub const SCALL: StaticSignature = StaticSignature(&[I64, I32, I32, I32, I32, I32], Some(I32)); + + pub const PANIC: StaticSignature = StaticSignature(&[I32, I32], None); + + pub const DEBUG: StaticSignature = StaticSignature(&[I32, I32], None); + + pub const VALUE: StaticSignature = StaticSignature(&[I32], None); + + pub const CREATE: StaticSignature = StaticSignature(&[I32, I32, I32, I32], Some(I32)); + + pub const CREATE2: StaticSignature = StaticSignature(&[I32, I32, I32, I32, I32], Some(I32)); + + pub const SUICIDE: StaticSignature = StaticSignature(&[I32], None); + + pub const BLOCKHASH: StaticSignature = StaticSignature(&[I64, I32], None); + + pub const BLOCKNUMBER: StaticSignature = StaticSignature(&[], Some(I64)); + + pub const COINBASE: StaticSignature = StaticSignature(&[I32], None); + + pub const DIFFICULTY: StaticSignature = StaticSignature(&[I32], None); + + pub const GASLEFT: StaticSignature = StaticSignature(&[], Some(I64)); + + pub const GASLIMIT: StaticSignature = StaticSignature(&[I32], None); + + pub const TIMESTAMP: StaticSignature = StaticSignature(&[], Some(I64)); + + pub const ADDRESS: StaticSignature = StaticSignature(&[I32], None); + + pub const SENDER: StaticSignature = StaticSignature(&[I32], None); + + pub const ORIGIN: StaticSignature = StaticSignature(&[I32], None); + + pub const ELOG: StaticSignature = StaticSignature(&[I32, I32, I32, I32], None); + + impl Into for StaticSignature { + fn into(self) -> wasmi::Signature { + wasmi::Signature::new(self.0, self.1) + } + } } fn host(signature: signatures::StaticSignature, idx: usize) -> FuncRef { - FuncInstance::alloc_host(signature.into(), idx) + FuncInstance::alloc_host(signature.into(), idx) } /// Import resolver for wasmi @@ -209,110 +131,117 @@ fn host(signature: signatures::StaticSignature, idx: usize) -> FuncRef { /// entries. /// Also manages initial memory request from the runtime. pub struct ImportResolver { - max_memory: u32, - memory: RefCell>, + max_memory: u32, + memory: RefCell>, - have_create2: bool, - have_gasleft: bool, + have_create2: bool, + have_gasleft: bool, } impl ImportResolver { - /// New import resolver with specifed maximum amount of inital memory (in wasm pages = 64kb) - pub fn with_limit(max_memory: u32, schedule: &WasmCosts) -> ImportResolver { - ImportResolver { - max_memory: max_memory, - memory: RefCell::new(None), - - have_create2: schedule.have_create2, - have_gasleft: schedule.have_gasleft, - } - } - - /// Returns memory that was instantiated during the contract module - /// start. If contract does not use memory at all, the dummy memory of length (0, 0) - /// will be created instead. So this method always returns memory instance - /// unless errored. - pub fn memory_ref(&self) -> MemoryRef { - { - let mut mem_ref = self.memory.borrow_mut(); - if mem_ref.is_none() { - *mem_ref = Some( - MemoryInstance::alloc( - memory_units::Pages(0), - Some(memory_units::Pages(0)), - ).expect("Memory allocation (0, 0) should not fail; qed") - ); - } - } - - self.memory.borrow().clone().expect("it is either existed or was created as (0, 0) above; qed") - } - - /// Returns memory size module initially requested - pub fn memory_size(&self) -> Result { - Ok(self.memory_ref().current_size().0 as u32) - } + /// New import resolver with specifed maximum amount of inital memory (in wasm pages = 64kb) + pub fn with_limit(max_memory: u32, schedule: &WasmCosts) -> ImportResolver { + ImportResolver { + max_memory: max_memory, + memory: RefCell::new(None), + + have_create2: schedule.have_create2, + have_gasleft: schedule.have_gasleft, + } + } + + /// Returns memory that was instantiated during the contract module + /// start. If contract does not use memory at all, the dummy memory of length (0, 0) + /// will be created instead. So this method always returns memory instance + /// unless errored. + pub fn memory_ref(&self) -> MemoryRef { + { + let mut mem_ref = self.memory.borrow_mut(); + if mem_ref.is_none() { + *mem_ref = Some( + MemoryInstance::alloc(memory_units::Pages(0), Some(memory_units::Pages(0))) + .expect("Memory allocation (0, 0) should not fail; qed"), + ); + } + } + + self.memory + .borrow() + .clone() + .expect("it is either existed or was created as (0, 0) above; qed") + } + + /// Returns memory size module initially requested + pub fn memory_size(&self) -> Result { + Ok(self.memory_ref().current_size().0 as u32) + } } impl wasmi::ModuleImportResolver for ImportResolver { - fn resolve_func(&self, field_name: &str, _signature: &Signature) -> Result { - let func_ref = match field_name { - "storage_read" => host(signatures::STORAGE_READ, ids::STORAGE_READ_FUNC), - "storage_write" => host(signatures::STORAGE_WRITE, ids::STORAGE_WRITE_FUNC), - "ret" => host(signatures::RET, ids::RET_FUNC), - "gas" => host(signatures::GAS, ids::GAS_FUNC), - "input_length" => host(signatures::INPUT_LENGTH, ids::INPUT_LENGTH_FUNC), - "fetch_input" => host(signatures::FETCH_INPUT, ids::FETCH_INPUT_FUNC), - "panic" => host(signatures::PANIC, ids::PANIC_FUNC), - "debug" => host(signatures::DEBUG, ids::DEBUG_FUNC), - "ccall" => host(signatures::CCALL, ids::CCALL_FUNC), - "dcall" => host(signatures::DCALL, ids::DCALL_FUNC), - "scall" => host(signatures::SCALL, ids::SCALL_FUNC), - "value" => host(signatures::VALUE, ids::VALUE_FUNC), - "create" => host(signatures::CREATE, ids::CREATE_FUNC), - "suicide" => host(signatures::SUICIDE, ids::SUICIDE_FUNC), - "blockhash" => host(signatures::BLOCKHASH, ids::BLOCKHASH_FUNC), - "blocknumber" => host(signatures::BLOCKNUMBER, ids::BLOCKNUMBER_FUNC), - "coinbase" => host(signatures::COINBASE, ids::COINBASE_FUNC), - "difficulty" => host(signatures::DIFFICULTY, ids::DIFFICULTY_FUNC), - "gaslimit" => host(signatures::GASLIMIT, ids::GASLIMIT_FUNC), - "timestamp" => host(signatures::TIMESTAMP, ids::TIMESTAMP_FUNC), - "address" => host(signatures::ADDRESS, ids::ADDRESS_FUNC), - "sender" => host(signatures::SENDER, ids::SENDER_FUNC), - "origin" => host(signatures::ORIGIN, ids::ORIGIN_FUNC), - "elog" => host(signatures::ELOG, ids::ELOG_FUNC), - "create2" if self.have_create2 => host(signatures::CREATE2, ids::CREATE2_FUNC), - "gasleft" if self.have_gasleft => host(signatures::GASLEFT, ids::GASLEFT_FUNC), - _ => { - return Err(wasmi::Error::Instantiation( - format!("Export {} not found", field_name), - )) - } - }; - - Ok(func_ref) - } - - fn resolve_memory( - &self, - field_name: &str, - descriptor: &MemoryDescriptor, - ) -> Result { - if field_name == "memory" { - let effective_max = descriptor.maximum().unwrap_or(self.max_memory + 1); - if descriptor.initial() > self.max_memory || effective_max > self.max_memory - { - Err(Error::Instantiation("Module requested too much memory".to_owned())) - } else { - let mem = MemoryInstance::alloc( - memory_units::Pages(descriptor.initial() as usize), - descriptor.maximum().map(|x| memory_units::Pages(x as usize)), - )?; - *self.memory.borrow_mut() = Some(mem.clone()); - Ok(mem) - } - } else { - Err(Error::Instantiation("Memory imported under unknown name".to_owned())) - } - } + fn resolve_func(&self, field_name: &str, _signature: &Signature) -> Result { + let func_ref = match field_name { + "storage_read" => host(signatures::STORAGE_READ, ids::STORAGE_READ_FUNC), + "storage_write" => host(signatures::STORAGE_WRITE, ids::STORAGE_WRITE_FUNC), + "ret" => host(signatures::RET, ids::RET_FUNC), + "gas" => host(signatures::GAS, ids::GAS_FUNC), + "input_length" => host(signatures::INPUT_LENGTH, ids::INPUT_LENGTH_FUNC), + "fetch_input" => host(signatures::FETCH_INPUT, ids::FETCH_INPUT_FUNC), + "panic" => host(signatures::PANIC, ids::PANIC_FUNC), + "debug" => host(signatures::DEBUG, ids::DEBUG_FUNC), + "ccall" => host(signatures::CCALL, ids::CCALL_FUNC), + "dcall" => host(signatures::DCALL, ids::DCALL_FUNC), + "scall" => host(signatures::SCALL, ids::SCALL_FUNC), + "value" => host(signatures::VALUE, ids::VALUE_FUNC), + "create" => host(signatures::CREATE, ids::CREATE_FUNC), + "suicide" => host(signatures::SUICIDE, ids::SUICIDE_FUNC), + "blockhash" => host(signatures::BLOCKHASH, ids::BLOCKHASH_FUNC), + "blocknumber" => host(signatures::BLOCKNUMBER, ids::BLOCKNUMBER_FUNC), + "coinbase" => host(signatures::COINBASE, ids::COINBASE_FUNC), + "difficulty" => host(signatures::DIFFICULTY, ids::DIFFICULTY_FUNC), + "gaslimit" => host(signatures::GASLIMIT, ids::GASLIMIT_FUNC), + "timestamp" => host(signatures::TIMESTAMP, ids::TIMESTAMP_FUNC), + "address" => host(signatures::ADDRESS, ids::ADDRESS_FUNC), + "sender" => host(signatures::SENDER, ids::SENDER_FUNC), + "origin" => host(signatures::ORIGIN, ids::ORIGIN_FUNC), + "elog" => host(signatures::ELOG, ids::ELOG_FUNC), + "create2" if self.have_create2 => host(signatures::CREATE2, ids::CREATE2_FUNC), + "gasleft" if self.have_gasleft => host(signatures::GASLEFT, ids::GASLEFT_FUNC), + _ => { + return Err(wasmi::Error::Instantiation(format!( + "Export {} not found", + field_name + ))) + } + }; + + Ok(func_ref) + } + + fn resolve_memory( + &self, + field_name: &str, + descriptor: &MemoryDescriptor, + ) -> Result { + if field_name == "memory" { + let effective_max = descriptor.maximum().unwrap_or(self.max_memory + 1); + if descriptor.initial() > self.max_memory || effective_max > self.max_memory { + Err(Error::Instantiation( + "Module requested too much memory".to_owned(), + )) + } else { + let mem = MemoryInstance::alloc( + memory_units::Pages(descriptor.initial() as usize), + descriptor + .maximum() + .map(|x| memory_units::Pages(x as usize)), + )?; + *self.memory.borrow_mut() = Some(mem.clone()); + Ok(mem) + } + } else { + Err(Error::Instantiation( + "Memory imported under unknown name".to_owned(), + )) + } + } } diff --git a/ethcore/wasm/src/lib.rs b/ethcore/wasm/src/lib.rs index 1e6129ba13c..5ebaaadb45a 100644 --- a/ethcore/wasm/src/lib.rs +++ b/ethcore/wasm/src/lib.rs @@ -1,28 +1,29 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Wasm Interpreter extern crate byteorder; extern crate ethereum_types; -#[macro_use] extern crate log; +#[macro_use] +extern crate log; extern crate libc; extern crate parity_wasm; -extern crate vm; extern crate pwasm_utils as wasm_utils; +extern crate vm; extern crate wasmi; #[cfg(test)] @@ -36,8 +37,7 @@ mod runtime; #[cfg(test)] mod tests; - -use vm::{GasLeft, ReturnData, ActionParams}; +use vm::{ActionParams, GasLeft, ReturnData}; use wasmi::{Error as InterpreterError, Trap}; use runtime::{Runtime, RuntimeContext}; @@ -47,155 +47,164 @@ use ethereum_types::U256; /// Wrapped interpreter error #[derive(Debug)] pub enum Error { - Interpreter(InterpreterError), - Trap(Trap), + Interpreter(InterpreterError), + Trap(Trap), } impl From for Error { - fn from(e: InterpreterError) -> Self { - Error::Interpreter(e) - } + fn from(e: InterpreterError) -> Self { + Error::Interpreter(e) + } } impl From for Error { - fn from(e: Trap) -> Self { - Error::Trap(e) - } + fn from(e: Trap) -> Self { + Error::Trap(e) + } } impl From for vm::Error { - fn from(e: Error) -> Self { - match e { - Error::Interpreter(e) => vm::Error::Wasm(format!("Wasm runtime error: {:?}", e)), - Error::Trap(e) => vm::Error::Wasm(format!("Wasm contract trap: {:?}", e)), - } - } + fn from(e: Error) -> Self { + match e { + Error::Interpreter(e) => vm::Error::Wasm(format!("Wasm runtime error: {:?}", e)), + Error::Trap(e) => vm::Error::Wasm(format!("Wasm contract trap: {:?}", e)), + } + } } /// Wasm interpreter instance pub struct WasmInterpreter { - params: ActionParams, + params: ActionParams, } impl WasmInterpreter { - pub fn new(params: ActionParams) -> Self { - WasmInterpreter { params } - } + pub fn new(params: ActionParams) -> Self { + WasmInterpreter { params } + } } impl From for vm::Error { - fn from(e: runtime::Error) -> Self { - vm::Error::Wasm(format!("Wasm runtime error: {:?}", e)) - } + fn from(e: runtime::Error) -> Self { + vm::Error::Wasm(format!("Wasm runtime error: {:?}", e)) + } } enum ExecutionOutcome { - Suicide, - Return, - NotSpecial, + Suicide, + Return, + NotSpecial, } impl WasmInterpreter { - pub fn run(self: Box, ext: &mut vm::Ext) -> vm::Result { - let (module, data) = parser::payload(&self.params, ext.schedule().wasm())?; - - let loaded_module = wasmi::Module::from_parity_wasm_module(module).map_err(Error::Interpreter)?; - - let instantiation_resolver = env::ImportResolver::with_limit(16, ext.schedule().wasm()); - - let module_instance = wasmi::ModuleInstance::new( - &loaded_module, - &wasmi::ImportsBuilder::new().with_resolver("env", &instantiation_resolver) - ).map_err(Error::Interpreter)?; - - let adjusted_gas = self.params.gas * U256::from(ext.schedule().wasm().opcodes_div) / - U256::from(ext.schedule().wasm().opcodes_mul); - - if adjusted_gas > ::std::u64::MAX.into() - { - return Err(vm::Error::Wasm("Wasm interpreter cannot run contracts with gas (wasm adjusted) >= 2^64".to_owned())); - } - - let initial_memory = instantiation_resolver.memory_size().map_err(Error::Interpreter)?; - trace!(target: "wasm", "Contract requested {:?} pages of initial memory", initial_memory); - - let (gas_left, result) = { - let mut runtime = Runtime::with_params( - ext, - instantiation_resolver.memory_ref(), - // cannot overflow, checked above - adjusted_gas.low_u64(), - data.to_vec(), - RuntimeContext { - address: self.params.address, - sender: self.params.sender, - origin: self.params.origin, - code_address: self.params.code_address, - value: self.params.value.value(), - }, - ); - - // cannot overflow if static_region < 2^16, - // initial_memory ∈ [0..2^32) - // total_charge <- static_region * 2^32 * 2^16 - // total_charge ∈ [0..2^64) if static_region ∈ [0..2^16) - // qed - assert!(runtime.schedule().wasm().initial_mem < 1 << 16); - runtime.charge(|s| initial_memory as u64 * s.wasm().initial_mem as u64)?; - - let module_instance = module_instance.run_start(&mut runtime).map_err(Error::Trap)?; - - let invoke_result = module_instance.invoke_export("call", &[], &mut runtime); - - let mut execution_outcome = ExecutionOutcome::NotSpecial; - if let Err(InterpreterError::Trap(ref trap)) = invoke_result { - if let wasmi::TrapKind::Host(ref boxed) = *trap.kind() { - let ref runtime_err = boxed.downcast_ref::() - .expect("Host errors other than runtime::Error never produced; qed"); - - match **runtime_err { - runtime::Error::Suicide => { execution_outcome = ExecutionOutcome::Suicide; }, - runtime::Error::Return => { execution_outcome = ExecutionOutcome::Return; }, - _ => {} - } - } - } - - if let (ExecutionOutcome::NotSpecial, Err(e)) = (execution_outcome, invoke_result) { - trace!(target: "wasm", "Error executing contract: {:?}", e); - return Err(vm::Error::from(Error::from(e))); - } - - ( - runtime.gas_left().expect("Cannot fail since it was not updated since last charge"), - runtime.into_result(), - ) - }; - - let gas_left = - U256::from(gas_left) * U256::from(ext.schedule().wasm().opcodes_mul) - / U256::from(ext.schedule().wasm().opcodes_div); - - if result.is_empty() { - trace!(target: "wasm", "Contract execution result is empty."); - Ok(GasLeft::Known(gas_left)) - } else { - let len = result.len(); - Ok(GasLeft::NeedsReturn { - gas_left: gas_left, - data: ReturnData::new( - result, - 0, - len, - ), - apply_state: true, - }) - } - } + pub fn run(self: Box, ext: &mut dyn vm::Ext) -> vm::Result { + let (module, data) = parser::payload(&self.params, ext.schedule().wasm())?; + + let loaded_module = + wasmi::Module::from_parity_wasm_module(module).map_err(Error::Interpreter)?; + + let instantiation_resolver = env::ImportResolver::with_limit(16, ext.schedule().wasm()); + + let module_instance = wasmi::ModuleInstance::new( + &loaded_module, + &wasmi::ImportsBuilder::new().with_resolver("env", &instantiation_resolver), + ) + .map_err(Error::Interpreter)?; + + let adjusted_gas = self.params.gas * U256::from(ext.schedule().wasm().opcodes_div) + / U256::from(ext.schedule().wasm().opcodes_mul); + + if adjusted_gas > ::std::u64::MAX.into() { + return Err(vm::Error::Wasm( + "Wasm interpreter cannot run contracts with gas (wasm adjusted) >= 2^64".to_owned(), + )); + } + + let initial_memory = instantiation_resolver + .memory_size() + .map_err(Error::Interpreter)?; + trace!(target: "wasm", "Contract requested {:?} pages of initial memory", initial_memory); + + let (gas_left, result) = { + let mut runtime = Runtime::with_params( + ext, + instantiation_resolver.memory_ref(), + // cannot overflow, checked above + adjusted_gas.low_u64(), + data.to_vec(), + RuntimeContext { + address: self.params.address, + sender: self.params.sender, + origin: self.params.origin, + code_address: self.params.code_address, + value: self.params.value.value(), + }, + ); + + // cannot overflow if static_region < 2^16, + // initial_memory ∈ [0..2^32) + // total_charge <- static_region * 2^32 * 2^16 + // total_charge ∈ [0..2^64) if static_region ∈ [0..2^16) + // qed + assert!(runtime.schedule().wasm().initial_mem < 1 << 16); + runtime.charge(|s| initial_memory as u64 * s.wasm().initial_mem as u64)?; + + let module_instance = module_instance + .run_start(&mut runtime) + .map_err(Error::Trap)?; + + let invoke_result = module_instance.invoke_export("call", &[], &mut runtime); + + let mut execution_outcome = ExecutionOutcome::NotSpecial; + if let Err(InterpreterError::Trap(ref trap)) = invoke_result { + if let wasmi::TrapKind::Host(ref boxed) = *trap.kind() { + let ref runtime_err = boxed + .downcast_ref::() + .expect("Host errors other than runtime::Error never produced; qed"); + + match **runtime_err { + runtime::Error::Suicide => { + execution_outcome = ExecutionOutcome::Suicide; + } + runtime::Error::Return => { + execution_outcome = ExecutionOutcome::Return; + } + _ => {} + } + } + } + + if let (ExecutionOutcome::NotSpecial, Err(e)) = (execution_outcome, invoke_result) { + trace!(target: "wasm", "Error executing contract: {:?}", e); + return Err(vm::Error::from(Error::from(e))); + } + + ( + runtime + .gas_left() + .expect("Cannot fail since it was not updated since last charge"), + runtime.into_result(), + ) + }; + + let gas_left = U256::from(gas_left) * U256::from(ext.schedule().wasm().opcodes_mul) + / U256::from(ext.schedule().wasm().opcodes_div); + + if result.is_empty() { + trace!(target: "wasm", "Contract execution result is empty."); + Ok(GasLeft::Known(gas_left)) + } else { + let len = result.len(); + Ok(GasLeft::NeedsReturn { + gas_left: gas_left, + data: ReturnData::new(result, 0, len), + apply_state: true, + }) + } + } } impl vm::Exec for WasmInterpreter { - fn exec(self: Box, ext: &mut vm::Ext) -> vm::ExecTrapResult { - Ok(self.run(ext)) - } + fn exec(self: Box, ext: &mut dyn vm::Ext) -> vm::ExecTrapResult { + Ok(self.run(ext)) + } } diff --git a/ethcore/wasm/src/panic_payload.rs b/ethcore/wasm/src/panic_payload.rs index a484daf7123..55751218124 100644 --- a/ethcore/wasm/src/panic_payload.rs +++ b/ethcore/wasm/src/panic_payload.rs @@ -1,168 +1,168 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use byteorder::{LittleEndian, ReadBytesExt}; use std::io::{self, Read}; #[derive(Debug, PartialEq, Eq)] pub struct PanicPayload { - pub msg: Option, - pub file: Option, - pub line: Option, - pub col: Option, + pub msg: Option, + pub file: Option, + pub line: Option, + pub col: Option, } fn read_string(rdr: &mut io::Cursor<&[u8]>) -> io::Result> { - let string_len = rdr.read_u32::()?; - let string = if string_len == 0 { - None - } else { - let mut content = vec![0; string_len as usize]; - rdr.read_exact(&mut content)?; - Some(String::from_utf8_lossy(&content).into_owned()) - }; - Ok(string) + let string_len = rdr.read_u32::()?; + let string = if string_len == 0 { + None + } else { + let mut content = vec![0; string_len as usize]; + rdr.read_exact(&mut content)?; + Some(String::from_utf8_lossy(&content).into_owned()) + }; + Ok(string) } pub fn decode(raw: &[u8]) -> PanicPayload { - let mut rdr = io::Cursor::new(raw); - let msg = read_string(&mut rdr).ok().and_then(|x| x); - let file = read_string(&mut rdr).ok().and_then(|x| x); - let line = rdr.read_u32::().ok(); - let col = rdr.read_u32::().ok(); - PanicPayload { - msg: msg, - file: file, - line: line, - col: col, - } + let mut rdr = io::Cursor::new(raw); + let msg = read_string(&mut rdr).ok().and_then(|x| x); + let file = read_string(&mut rdr).ok().and_then(|x| x); + let line = rdr.read_u32::().ok(); + let col = rdr.read_u32::().ok(); + PanicPayload { + msg: msg, + file: file, + line: line, + col: col, + } } #[cfg(test)] mod tests { - use super::*; - use byteorder::WriteBytesExt; - - fn write_u32(payload: &mut Vec, val: u32) { - payload.write_u32::(val).unwrap(); - } - - fn write_bytes(payload: &mut Vec, bytes: &[u8]) { - write_u32(payload, bytes.len() as u32); - payload.extend(bytes); - } - - #[test] - fn it_works() { - let mut raw = Vec::new(); - write_bytes(&mut raw, b"msg"); - write_bytes(&mut raw, b"file"); - write_u32(&mut raw, 1); - write_u32(&mut raw, 2); - - let payload = decode(&raw); - - assert_eq!( - payload, - PanicPayload { - msg: Some("msg".to_string()), - file: Some("file".to_string()), - line: Some(1), - col: Some(2), - } - ); - } - - #[test] - fn only_msg() { - let mut raw = Vec::new(); - write_bytes(&mut raw, b"msg"); - - let payload = decode(&raw); - - assert_eq!( - payload, - PanicPayload { - msg: Some("msg".to_string()), - file: None, - line: None, - col: None, - } - ); - } - - #[test] - fn invalid_utf8() { - let mut raw = Vec::new(); - write_bytes(&mut raw, b"\xF0\x90\x80msg"); - write_bytes(&mut raw, b"file"); - write_u32(&mut raw, 1); - write_u32(&mut raw, 2); - - let payload = decode(&raw); - - assert_eq!( - payload, - PanicPayload { - msg: Some("�msg".to_string()), - file: Some("file".to_string()), - line: Some(1), - col: Some(2), - } - ); - } - - #[test] - fn trailing_data() { - let mut raw = Vec::new(); - write_bytes(&mut raw, b"msg"); - write_bytes(&mut raw, b"file"); - write_u32(&mut raw, 1); - write_u32(&mut raw, 2); - write_u32(&mut raw, 0xdeadbeef); - - let payload = decode(&raw); - - assert_eq!( - payload, - PanicPayload { - msg: Some("msg".to_string()), - file: Some("file".to_string()), - line: Some(1), - col: Some(2), - } - ); - } - - #[test] - fn empty_str_is_none() { - let mut raw = Vec::new(); - write_bytes(&mut raw, b"msg"); - write_bytes(&mut raw, b""); - - let payload = decode(&raw); - - assert_eq!( - payload, - PanicPayload { - msg: Some("msg".to_string()), - file: None, - line: None, - col: None, - } - ); - } + use super::*; + use byteorder::WriteBytesExt; + + fn write_u32(payload: &mut Vec, val: u32) { + payload.write_u32::(val).unwrap(); + } + + fn write_bytes(payload: &mut Vec, bytes: &[u8]) { + write_u32(payload, bytes.len() as u32); + payload.extend(bytes); + } + + #[test] + fn it_works() { + let mut raw = Vec::new(); + write_bytes(&mut raw, b"msg"); + write_bytes(&mut raw, b"file"); + write_u32(&mut raw, 1); + write_u32(&mut raw, 2); + + let payload = decode(&raw); + + assert_eq!( + payload, + PanicPayload { + msg: Some("msg".to_string()), + file: Some("file".to_string()), + line: Some(1), + col: Some(2), + } + ); + } + + #[test] + fn only_msg() { + let mut raw = Vec::new(); + write_bytes(&mut raw, b"msg"); + + let payload = decode(&raw); + + assert_eq!( + payload, + PanicPayload { + msg: Some("msg".to_string()), + file: None, + line: None, + col: None, + } + ); + } + + #[test] + fn invalid_utf8() { + let mut raw = Vec::new(); + write_bytes(&mut raw, b"\xF0\x90\x80msg"); + write_bytes(&mut raw, b"file"); + write_u32(&mut raw, 1); + write_u32(&mut raw, 2); + + let payload = decode(&raw); + + assert_eq!( + payload, + PanicPayload { + msg: Some("�msg".to_string()), + file: Some("file".to_string()), + line: Some(1), + col: Some(2), + } + ); + } + + #[test] + fn trailing_data() { + let mut raw = Vec::new(); + write_bytes(&mut raw, b"msg"); + write_bytes(&mut raw, b"file"); + write_u32(&mut raw, 1); + write_u32(&mut raw, 2); + write_u32(&mut raw, 0xdeadbeef); + + let payload = decode(&raw); + + assert_eq!( + payload, + PanicPayload { + msg: Some("msg".to_string()), + file: Some("file".to_string()), + line: Some(1), + col: Some(2), + } + ); + } + + #[test] + fn empty_str_is_none() { + let mut raw = Vec::new(); + write_bytes(&mut raw, b"msg"); + write_bytes(&mut raw, b""); + + let payload = decode(&raw); + + assert_eq!( + payload, + PanicPayload { + msg: Some("msg".to_string()), + file: None, + line: None, + col: None, + } + ); + } } diff --git a/ethcore/wasm/src/parser.rs b/ethcore/wasm/src/parser.rs index ca730e71886..80dbb521da0 100644 --- a/ethcore/wasm/src/parser.rs +++ b/ethcore/wasm/src/parser.rs @@ -1,98 +1,110 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! ActionParams parser for wasm +use parity_wasm::{ + elements::{self, Deserialize}, + peek_size, +}; use vm; use wasm_utils::{self, rules}; -use parity_wasm::elements::{self, Deserialize}; -use parity_wasm::peek_size; fn gas_rules(wasm_costs: &vm::WasmCosts) -> rules::Set { - rules::Set::new( - wasm_costs.regular, - { - let mut vals = ::std::collections::BTreeMap::new(); - vals.insert(rules::InstructionType::Load, rules::Metering::Fixed(wasm_costs.mem as u32)); - vals.insert(rules::InstructionType::Store, rules::Metering::Fixed(wasm_costs.mem as u32)); - vals.insert(rules::InstructionType::Div, rules::Metering::Fixed(wasm_costs.div as u32)); - vals.insert(rules::InstructionType::Mul, rules::Metering::Fixed(wasm_costs.mul as u32)); - vals - }) - .with_grow_cost(wasm_costs.grow_mem) - .with_forbidden_floats() + rules::Set::new(wasm_costs.regular, { + let mut vals = ::std::collections::BTreeMap::new(); + vals.insert( + rules::InstructionType::Load, + rules::Metering::Fixed(wasm_costs.mem as u32), + ); + vals.insert( + rules::InstructionType::Store, + rules::Metering::Fixed(wasm_costs.mem as u32), + ); + vals.insert( + rules::InstructionType::Div, + rules::Metering::Fixed(wasm_costs.div as u32), + ); + vals.insert( + rules::InstructionType::Mul, + rules::Metering::Fixed(wasm_costs.mul as u32), + ); + vals + }) + .with_grow_cost(wasm_costs.grow_mem) + .with_forbidden_floats() } /// Splits payload to code and data according to params.params_type, also /// loads the module instance from payload and injects gas counter according /// to schedule. -pub fn payload<'a>(params: &'a vm::ActionParams, wasm_costs: &vm::WasmCosts) - -> Result<(elements::Module, &'a [u8]), vm::Error> -{ - let code = match params.code { - Some(ref code) => &code[..], - None => { return Err(vm::Error::Wasm("Invalid wasm call".to_owned())); } - }; +pub fn payload<'a>( + params: &'a vm::ActionParams, + wasm_costs: &vm::WasmCosts, +) -> Result<(elements::Module, &'a [u8]), vm::Error> { + let code = match params.code { + Some(ref code) => &code[..], + None => { + return Err(vm::Error::Wasm("Invalid wasm call".to_owned())); + } + }; - let (mut cursor, data_position) = match params.params_type { - vm::ParamsType::Embedded => { - let module_size = peek_size(&*code); - ( - ::std::io::Cursor::new(&code[..module_size]), - module_size - ) - }, - vm::ParamsType::Separate => { - (::std::io::Cursor::new(&code[..]), 0) - }, - }; + let (mut cursor, data_position) = match params.params_type { + vm::ParamsType::Embedded => { + let module_size = peek_size(&*code); + (::std::io::Cursor::new(&code[..module_size]), module_size) + } + vm::ParamsType::Separate => (::std::io::Cursor::new(&code[..]), 0), + }; - let deserialized_module = elements::Module::deserialize( - &mut cursor - ).map_err(|err| { - vm::Error::Wasm(format!("Error deserializing contract code ({:?})", err)) - })?; + let deserialized_module = elements::Module::deserialize(&mut cursor) + .map_err(|err| vm::Error::Wasm(format!("Error deserializing contract code ({:?})", err)))?; - if deserialized_module.memory_section().map_or(false, |ms| ms.entries().len() > 0) { - // According to WebAssembly spec, internal memory is hidden from embedder and should not - // be interacted with. So we disable this kind of modules at decoding level. - return Err(vm::Error::Wasm(format!("Malformed wasm module: internal memory"))); - } + if deserialized_module + .memory_section() + .map_or(false, |ms| ms.entries().len() > 0) + { + // According to WebAssembly spec, internal memory is hidden from embedder and should not + // be interacted with. So we disable this kind of modules at decoding level. + return Err(vm::Error::Wasm(format!( + "Malformed wasm module: internal memory" + ))); + } - let contract_module = wasm_utils::inject_gas_counter( - deserialized_module, - &gas_rules(wasm_costs), - ).map_err(|_| vm::Error::Wasm(format!("Wasm contract error: bytecode invalid")))?; + let contract_module = + wasm_utils::inject_gas_counter(deserialized_module, &gas_rules(wasm_costs)) + .map_err(|_| vm::Error::Wasm(format!("Wasm contract error: bytecode invalid")))?; - let contract_module = wasm_utils::stack_height::inject_limiter( - contract_module, - wasm_costs.max_stack_height, - ).map_err(|_| vm::Error::Wasm(format!("Wasm contract error: stack limiter failure")))?; + let contract_module = + wasm_utils::stack_height::inject_limiter(contract_module, wasm_costs.max_stack_height) + .map_err(|_| vm::Error::Wasm(format!("Wasm contract error: stack limiter failure")))?; - let data = match params.params_type { - vm::ParamsType::Embedded => { - if data_position < code.len() { &code[data_position..] } else { &[] } - }, - vm::ParamsType::Separate => { - match params.data { - Some(ref s) => &s[..], - None => &[] - } - } - }; + let data = match params.params_type { + vm::ParamsType::Embedded => { + if data_position < code.len() { + &code[data_position..] + } else { + &[] + } + } + vm::ParamsType::Separate => match params.data { + Some(ref s) => &s[..], + None => &[], + }, + }; - Ok((contract_module, data)) + Ok((contract_module, data)) } diff --git a/ethcore/wasm/src/runtime.rs b/ethcore/wasm/src/runtime.rs index 8466c3b8d90..386ae577bca 100644 --- a/ethcore/wasm/src/runtime.rs +++ b/ethcore/wasm/src/runtime.rs @@ -1,801 +1,861 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . +use super::panic_payload; +use ethereum_types::{Address, H256, U256}; use std::cmp; -use ethereum_types::{U256, H256, Address}; use vm::{self, CallType}; -use wasmi::{self, MemoryRef, RuntimeArgs, RuntimeValue, Error as InterpreterError, Trap, TrapKind}; -use super::panic_payload; +use wasmi::{ + self, Error as InterpreterError, MemoryRef, RuntimeArgs, RuntimeValue, Trap, TrapKind, +}; pub struct RuntimeContext { - pub address: Address, - pub sender: Address, - pub origin: Address, - pub code_address: Address, - pub value: U256, + pub address: Address, + pub sender: Address, + pub origin: Address, + pub code_address: Address, + pub value: U256, } pub struct Runtime<'a> { - gas_counter: u64, - gas_limit: u64, - ext: &'a mut vm::Ext, - context: RuntimeContext, - memory: MemoryRef, - args: Vec, - result: Vec, + gas_counter: u64, + gas_limit: u64, + ext: &'a mut dyn vm::Ext, + context: RuntimeContext, + memory: MemoryRef, + args: Vec, + result: Vec, } /// User trap in native code #[derive(Debug, Clone, PartialEq)] pub enum Error { - /// Storage read error - StorageReadError, - /// Storage update error - StorageUpdateError, - /// Memory access violation - MemoryAccessViolation, - /// Native code resulted in suicide - Suicide, - /// Native code requested execution to finish - Return, - /// Suicide was requested but coudn't complete - SuicideAbort, - /// Invalid gas state inside interpreter - InvalidGasState, - /// Query of the balance resulted in an error - BalanceQueryError, - /// Failed allocation - AllocationFailed, - /// Gas limit reached - GasLimit, - /// Unknown runtime function - Unknown, - /// Passed string had invalid utf-8 encoding - BadUtf8, - /// Log event error - Log, - /// Other error in native code - Other, - /// Syscall signature mismatch - InvalidSyscall, - /// Unreachable instruction encountered - Unreachable, - /// Invalid virtual call - InvalidVirtualCall, - /// Division by zero - DivisionByZero, - /// Invalid conversion to integer - InvalidConversionToInt, - /// Stack overflow - StackOverflow, - /// Panic with message - Panic(String), + /// Storage read error + StorageReadError, + /// Storage update error + StorageUpdateError, + /// Memory access violation + MemoryAccessViolation, + /// Native code resulted in suicide + Suicide, + /// Native code requested execution to finish + Return, + /// Suicide was requested but coudn't complete + SuicideAbort, + /// Invalid gas state inside interpreter + InvalidGasState, + /// Query of the balance resulted in an error + BalanceQueryError, + /// Failed allocation + AllocationFailed, + /// Gas limit reached + GasLimit, + /// Unknown runtime function + Unknown, + /// Passed string had invalid utf-8 encoding + BadUtf8, + /// Log event error + Log, + /// Other error in native code + Other, + /// Syscall signature mismatch + InvalidSyscall, + /// Unreachable instruction encountered + Unreachable, + /// Invalid virtual call + InvalidVirtualCall, + /// Division by zero + DivisionByZero, + /// Invalid conversion to integer + InvalidConversionToInt, + /// Stack overflow + StackOverflow, + /// Panic with message + Panic(String), } -impl wasmi::HostError for Error { } +impl wasmi::HostError for Error {} impl From for Error { - fn from(trap: Trap) -> Self { - match *trap.kind() { - TrapKind::Unreachable => Error::Unreachable, - TrapKind::MemoryAccessOutOfBounds => Error::MemoryAccessViolation, - TrapKind::TableAccessOutOfBounds | TrapKind::ElemUninitialized => Error::InvalidVirtualCall, - TrapKind::DivisionByZero => Error::DivisionByZero, - TrapKind::InvalidConversionToInt => Error::InvalidConversionToInt, - TrapKind::UnexpectedSignature => Error::InvalidVirtualCall, - TrapKind::StackOverflow => Error::StackOverflow, - TrapKind::Host(_) => Error::Other, - } - } + fn from(trap: Trap) -> Self { + match *trap.kind() { + TrapKind::Unreachable => Error::Unreachable, + TrapKind::MemoryAccessOutOfBounds => Error::MemoryAccessViolation, + TrapKind::TableAccessOutOfBounds | TrapKind::ElemUninitialized => { + Error::InvalidVirtualCall + } + TrapKind::DivisionByZero => Error::DivisionByZero, + TrapKind::InvalidConversionToInt => Error::InvalidConversionToInt, + TrapKind::UnexpectedSignature => Error::InvalidVirtualCall, + TrapKind::StackOverflow => Error::StackOverflow, + TrapKind::Host(_) => Error::Other, + } + } } impl From for Error { - fn from(err: InterpreterError) -> Self { - match err { - InterpreterError::Value(_) => Error::InvalidSyscall, - InterpreterError::Memory(_) => Error::MemoryAccessViolation, - _ => Error::Other, - } - } + fn from(err: InterpreterError) -> Self { + match err { + InterpreterError::Value(_) => Error::InvalidSyscall, + InterpreterError::Memory(_) => Error::MemoryAccessViolation, + _ => Error::Other, + } + } } impl ::std::fmt::Display for Error { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> { - match *self { - Error::StorageReadError => write!(f, "Storage read error"), - Error::StorageUpdateError => write!(f, "Storage update error"), - Error::MemoryAccessViolation => write!(f, "Memory access violation"), - Error::SuicideAbort => write!(f, "Attempt to suicide resulted in an error"), - Error::InvalidGasState => write!(f, "Invalid gas state"), - Error::BalanceQueryError => write!(f, "Balance query resulted in an error"), - Error::Suicide => write!(f, "Suicide result"), - Error::Return => write!(f, "Return result"), - Error::Unknown => write!(f, "Unknown runtime function invoked"), - Error::AllocationFailed => write!(f, "Memory allocation failed (OOM)"), - Error::BadUtf8 => write!(f, "String encoding is bad utf-8 sequence"), - Error::GasLimit => write!(f, "Invocation resulted in gas limit violated"), - Error::Log => write!(f, "Error occured while logging an event"), - Error::InvalidSyscall => write!(f, "Invalid syscall signature encountered at runtime"), - Error::Other => write!(f, "Other unspecified error"), - Error::Unreachable => write!(f, "Unreachable instruction encountered"), - Error::InvalidVirtualCall => write!(f, "Invalid virtual call"), - Error::DivisionByZero => write!(f, "Division by zero"), - Error::StackOverflow => write!(f, "Stack overflow"), - Error::InvalidConversionToInt => write!(f, "Invalid conversion to integer"), - Error::Panic(ref msg) => write!(f, "Panic: {}", msg), - } - } + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> { + match *self { + Error::StorageReadError => write!(f, "Storage read error"), + Error::StorageUpdateError => write!(f, "Storage update error"), + Error::MemoryAccessViolation => write!(f, "Memory access violation"), + Error::SuicideAbort => write!(f, "Attempt to suicide resulted in an error"), + Error::InvalidGasState => write!(f, "Invalid gas state"), + Error::BalanceQueryError => write!(f, "Balance query resulted in an error"), + Error::Suicide => write!(f, "Suicide result"), + Error::Return => write!(f, "Return result"), + Error::Unknown => write!(f, "Unknown runtime function invoked"), + Error::AllocationFailed => write!(f, "Memory allocation failed (OOM)"), + Error::BadUtf8 => write!(f, "String encoding is bad utf-8 sequence"), + Error::GasLimit => write!(f, "Invocation resulted in gas limit violated"), + Error::Log => write!(f, "Error occured while logging an event"), + Error::InvalidSyscall => write!(f, "Invalid syscall signature encountered at runtime"), + Error::Other => write!(f, "Other unspecified error"), + Error::Unreachable => write!(f, "Unreachable instruction encountered"), + Error::InvalidVirtualCall => write!(f, "Invalid virtual call"), + Error::DivisionByZero => write!(f, "Division by zero"), + Error::StackOverflow => write!(f, "Stack overflow"), + Error::InvalidConversionToInt => write!(f, "Invalid conversion to integer"), + Error::Panic(ref msg) => write!(f, "Panic: {}", msg), + } + } } type Result = ::std::result::Result; impl<'a> Runtime<'a> { - - /// New runtime for wasm contract with specified params - pub fn with_params( - ext: &mut vm::Ext, - memory: MemoryRef, - gas_limit: u64, - args: Vec, - context: RuntimeContext, - ) -> Runtime { - Runtime { - gas_counter: 0, - gas_limit: gas_limit, - memory: memory, - ext: ext, - context: context, - args: args, - result: Vec::new(), - } - } - - /// Loads 256-bit hash from the specifed sandboxed memory pointer - fn h256_at(&self, ptr: u32) -> Result { - let mut buf = [0u8; 32]; - self.memory.get_into(ptr, &mut buf[..])?; - - Ok(H256::from(&buf[..])) - } - - /// Loads 160-bit hash (Ethereum address) from the specified sandboxed memory pointer - fn address_at(&self, ptr: u32) -> Result
{ - let mut buf = [0u8; 20]; - self.memory.get_into(ptr, &mut buf[..])?; - - Ok(Address::from(&buf[..])) - } - - /// Loads 256-bit integer represented with bigendian from the specified sandboxed memory pointer - fn u256_at(&self, ptr: u32) -> Result { - let mut buf = [0u8; 32]; - self.memory.get_into(ptr, &mut buf[..])?; - - Ok(U256::from_big_endian(&buf[..])) - } - - /// Charge specified amount of gas - /// - /// Returns false if gas limit exceeded and true if not. - /// Intuition about the return value sense is to aswer the question 'are we allowed to continue?' - fn charge_gas(&mut self, amount: u64) -> bool { - let prev = self.gas_counter; - match prev.checked_add(amount) { - // gas charge overflow protection - None => false, - Some(val) if val > self.gas_limit => false, - Some(_) => { - self.gas_counter = prev + amount; - true - } - } - } - - /// Charge gas according to closure - pub fn charge(&mut self, f: F) -> Result<()> - where F: FnOnce(&vm::Schedule) -> u64 - { - let amount = f(self.ext.schedule()); - if !self.charge_gas(amount as u64) { - Err(Error::GasLimit) - } else { - Ok(()) - } - } - - /// Adjusted charge of gas which scales actual charge according to the wasm opcode counting coefficient - pub fn adjusted_charge(&mut self, f: F) -> Result<()> - where F: FnOnce(&vm::Schedule) -> u64 - { - self.charge(|schedule| f(schedule) * schedule.wasm().opcodes_div as u64 / schedule.wasm().opcodes_mul as u64) - } - - /// Charge gas provided by the closure - /// - /// Closure also can return overflowing flag as None in gas cost. - pub fn overflow_charge(&mut self, f: F) -> Result<()> - where F: FnOnce(&vm::Schedule) -> Option - { - let amount = match f(self.ext.schedule()) { - Some(amount) => amount, - None => { return Err(Error::GasLimit.into()); } - }; - - if !self.charge_gas(amount as u64) { - Err(Error::GasLimit.into()) - } else { - Ok(()) - } - } - - /// Same as overflow_charge, but with amount adjusted by wasm opcodes coeff - pub fn adjusted_overflow_charge(&mut self, f: F) -> Result<()> - where F: FnOnce(&vm::Schedule) -> Option - { - self.overflow_charge(|schedule| - f(schedule) - .and_then(|x| x.checked_mul(schedule.wasm().opcodes_div as u64)) - .map(|x| x / schedule.wasm().opcodes_mul as u64) - ) - } - - /// Read from the storage to wasm memory - pub fn storage_read(&mut self, args: RuntimeArgs) -> Result<()> - { - let key = self.h256_at(args.nth_checked(0)?)?; - let val_ptr: u32 = args.nth_checked(1)?; - - let val = self.ext.storage_at(&key).map_err(|_| Error::StorageReadError)?; - - self.adjusted_charge(|schedule| schedule.sload_gas as u64)?; - - self.memory.set(val_ptr as u32, &*val)?; - - Ok(()) - } - - /// Write to storage from wasm memory - pub fn storage_write(&mut self, args: RuntimeArgs) -> Result<()> - { - let key = self.h256_at(args.nth_checked(0)?)?; - let val_ptr: u32 = args.nth_checked(1)?; - - let val = self.h256_at(val_ptr)?; - let former_val = self.ext.storage_at(&key).map_err(|_| Error::StorageUpdateError)?; - - if former_val == H256::zero() && val != H256::zero() { - self.adjusted_charge(|schedule| schedule.sstore_set_gas as u64)?; - } else { - self.adjusted_charge(|schedule| schedule.sstore_reset_gas as u64)?; - } - - self.ext.set_storage(key, val).map_err(|_| Error::StorageUpdateError)?; - - if former_val != H256::zero() && val == H256::zero() { - let sstore_clears_schedule = self.schedule().sstore_refund_gas; - self.ext.add_sstore_refund(sstore_clears_schedule); - } - - Ok(()) - } - - /// Return currently used schedule - pub fn schedule(&self) -> &vm::Schedule { - self.ext.schedule() - } - - /// Sets a return value for the call - /// - /// Syscall takes 2 arguments: - /// * pointer in sandboxed memory where result is - /// * the length of the result - pub fn ret(&mut self, args: RuntimeArgs) -> Result<()> { - let ptr: u32 = args.nth_checked(0)?; - let len: u32 = args.nth_checked(1)?; - - trace!(target: "wasm", "Contract ret: {} bytes @ {}", len, ptr); - - self.result = self.memory.get(ptr, len as usize)?; - - Err(Error::Return) - } - - /// Destroy the runtime, returning currently recorded result of the execution - pub fn into_result(self) -> Vec { - self.result - } - - /// Query current gas left for execution - pub fn gas_left(&self) -> Result { - if self.gas_counter > self.gas_limit { return Err(Error::InvalidGasState); } - Ok(self.gas_limit - self.gas_counter) - } - - /// General gas charging extern. - fn gas(&mut self, args: RuntimeArgs) -> Result<()> { - let amount: u32 = args.nth_checked(0)?; - if self.charge_gas(amount as u64) { - Ok(()) - } else { - Err(Error::GasLimit.into()) - } - } - - /// Query the length of the input bytes - fn input_legnth(&mut self) -> RuntimeValue { - RuntimeValue::I32(self.args.len() as i32) - } - - /// Write input bytes to the memory location using the passed pointer - fn fetch_input(&mut self, args: RuntimeArgs) -> Result<()> { - let ptr: u32 = args.nth_checked(0)?; - - let args_len = self.args.len() as u64; - self.charge(|s| args_len * s.wasm().memcpy as u64)?; - - self.memory.set(ptr, &self.args[..])?; - Ok(()) - } - - /// User panic - /// - /// Contract can invoke this when he encounters unrecoverable error. - fn panic(&mut self, args: RuntimeArgs) -> Result<()> - { - let payload_ptr: u32 = args.nth_checked(0)?; - let payload_len: u32 = args.nth_checked(1)?; - - let raw_payload = self.memory.get(payload_ptr, payload_len as usize)?; - let payload = panic_payload::decode(&raw_payload); - let msg = format!( - "{msg}, {file}:{line}:{col}", - msg = payload - .msg - .as_ref() - .map(String::as_ref) - .unwrap_or(""), - file = payload - .file - .as_ref() - .map(String::as_ref) - .unwrap_or(""), - line = payload.line.unwrap_or(0), - col = payload.col.unwrap_or(0) - ); - trace!(target: "wasm", "Contract custom panic message: {}", msg); - - Err(Error::Panic(msg).into()) - } - - fn do_call( - &mut self, - use_val: bool, - call_type: CallType, - args: RuntimeArgs, - ) - -> Result - { - trace!(target: "wasm", "runtime: CALL({:?})", call_type); - - let gas: u64 = args.nth_checked(0)?; - trace!(target: "wasm", " gas: {:?}", gas); - - let address = self.address_at(args.nth_checked(1)?)?; - trace!(target: "wasm", " address: {:?}", address); - - let vofs = if use_val { 1 } else { 0 }; - let val = if use_val { Some(self.u256_at(args.nth_checked(2)?)?) } else { None }; - trace!(target: "wasm", " val: {:?}", val); - - let input_ptr: u32 = args.nth_checked(2 + vofs)?; - trace!(target: "wasm", " input_ptr: {:?}", input_ptr); - - let input_len: u32 = args.nth_checked(3 + vofs)?; - trace!(target: "wasm", " input_len: {:?}", input_len); - - let result_ptr: u32 = args.nth_checked(4 + vofs)?; - trace!(target: "wasm", " result_ptr: {:?}", result_ptr); - - let result_alloc_len: u32 = args.nth_checked(5 + vofs)?; - trace!(target: "wasm", " result_len: {:?}", result_alloc_len); - - if let Some(ref val) = val { - let address_balance = self.ext.balance(&self.context.address) - .map_err(|_| Error::BalanceQueryError)?; - - if &address_balance < val { - trace!(target: "wasm", "runtime: call failed due to balance check"); - return Ok((-1i32).into()); - } - } - - self.adjusted_charge(|schedule| schedule.call_gas as u64)?; - - let mut result = Vec::with_capacity(result_alloc_len as usize); - result.resize(result_alloc_len as usize, 0); - - // todo: optimize to use memory views once it's in - let payload = self.memory.get(input_ptr, input_len as usize)?; - - let adjusted_gas = match gas.checked_mul(self.ext.schedule().wasm().opcodes_div as u64) - .map(|x| x / self.ext.schedule().wasm().opcodes_mul as u64) - { - Some(x) => x, - None => { - trace!("CALL overflowed gas, call aborted with error returned"); - return Ok(RuntimeValue::I32(-1)) - }, - }; - - self.charge(|_| adjusted_gas)?; - - let call_result = self.ext.call( - &gas.into(), - match call_type { CallType::DelegateCall => &self.context.sender, _ => &self.context.address }, - match call_type { CallType::Call | CallType::StaticCall => &address, _ => &self.context.address }, - val, - &payload, - &address, - call_type, - false - ).ok().expect("Trap is false; trap error will not happen; qed"); - - match call_result { - vm::MessageCallResult::Success(gas_left, data) => { - let len = cmp::min(result.len(), data.len()); - (&mut result[..len]).copy_from_slice(&data[..len]); - - // cannot overflow, before making call gas_counter was incremented with gas, and gas_left < gas - self.gas_counter = self.gas_counter - - gas_left.low_u64() * self.ext.schedule().wasm().opcodes_div as u64 - / self.ext.schedule().wasm().opcodes_mul as u64; - - self.memory.set(result_ptr, &result)?; - Ok(0i32.into()) - }, - vm::MessageCallResult::Reverted(gas_left, data) => { - let len = cmp::min(result.len(), data.len()); - (&mut result[..len]).copy_from_slice(&data[..len]); - - // cannot overflow, before making call gas_counter was incremented with gas, and gas_left < gas - self.gas_counter = self.gas_counter - - gas_left.low_u64() * self.ext.schedule().wasm().opcodes_div as u64 - / self.ext.schedule().wasm().opcodes_mul as u64; - - self.memory.set(result_ptr, &result)?; - Ok((-1i32).into()) - }, - vm::MessageCallResult::Failed => { - Ok((-1i32).into()) - } - } - } - - /// Message call - fn ccall(&mut self, args: RuntimeArgs) -> Result { - self.do_call(true, CallType::Call, args) - } - - /// Delegate call - fn dcall(&mut self, args: RuntimeArgs) -> Result { - self.do_call(false, CallType::DelegateCall, args) - } - - /// Static call - fn scall(&mut self, args: RuntimeArgs) -> Result { - self.do_call(false, CallType::StaticCall, args) - } - - fn return_address_ptr(&mut self, ptr: u32, val: Address) -> Result<()> - { - self.charge(|schedule| schedule.wasm().static_address as u64)?; - self.memory.set(ptr, &*val)?; - Ok(()) - } - - fn return_u256_ptr(&mut self, ptr: u32, val: U256) -> Result<()> { - let value: H256 = val.into(); - self.charge(|schedule| schedule.wasm().static_u256 as u64)?; - self.memory.set(ptr, &*value)?; - Ok(()) - } - - /// Returns value (in Wei) passed to contract - pub fn value(&mut self, args: RuntimeArgs) -> Result<()> { - let val = self.context.value; - self.return_u256_ptr(args.nth_checked(0)?, val) - } - - fn do_create(&mut self, endowment: U256, code_ptr: u32, code_len: u32, result_ptr: u32, scheme: vm::CreateContractAddress) -> Result { - let code = self.memory.get(code_ptr, code_len as usize)?; - - self.adjusted_charge(|schedule| schedule.create_gas as u64)?; - self.adjusted_charge(|schedule| schedule.create_data_gas as u64 * code.len() as u64)?; - - let gas_left: U256 = U256::from(self.gas_left()?) - * U256::from(self.ext.schedule().wasm().opcodes_mul) - / U256::from(self.ext.schedule().wasm().opcodes_div); - - match self.ext.create(&gas_left, &endowment, &code, scheme, false).ok().expect("Trap is false; trap error will not happen; qed") { - vm::ContractCreateResult::Created(address, gas_left) => { - self.memory.set(result_ptr, &*address)?; - self.gas_counter = self.gas_limit - + /// New runtime for wasm contract with specified params + pub fn with_params( + ext: &mut dyn vm::Ext, + memory: MemoryRef, + gas_limit: u64, + args: Vec, + context: RuntimeContext, + ) -> Runtime { + Runtime { + gas_counter: 0, + gas_limit: gas_limit, + memory: memory, + ext: ext, + context: context, + args: args, + result: Vec::new(), + } + } + + /// Loads 256-bit hash from the specifed sandboxed memory pointer + fn h256_at(&self, ptr: u32) -> Result { + let mut buf = [0u8; 32]; + self.memory.get_into(ptr, &mut buf[..])?; + + Ok(H256::from(&buf[..])) + } + + /// Loads 160-bit hash (Ethereum address) from the specified sandboxed memory pointer + fn address_at(&self, ptr: u32) -> Result
{ + let mut buf = [0u8; 20]; + self.memory.get_into(ptr, &mut buf[..])?; + + Ok(Address::from(&buf[..])) + } + + /// Loads 256-bit integer represented with bigendian from the specified sandboxed memory pointer + fn u256_at(&self, ptr: u32) -> Result { + let mut buf = [0u8; 32]; + self.memory.get_into(ptr, &mut buf[..])?; + + Ok(U256::from_big_endian(&buf[..])) + } + + /// Charge specified amount of gas + /// + /// Returns false if gas limit exceeded and true if not. + /// Intuition about the return value sense is to aswer the question 'are we allowed to continue?' + fn charge_gas(&mut self, amount: u64) -> bool { + let prev = self.gas_counter; + match prev.checked_add(amount) { + // gas charge overflow protection + None => false, + Some(val) if val > self.gas_limit => false, + Some(_) => { + self.gas_counter = prev + amount; + true + } + } + } + + /// Charge gas according to closure + pub fn charge(&mut self, f: F) -> Result<()> + where + F: FnOnce(&vm::Schedule) -> u64, + { + let amount = f(self.ext.schedule()); + if !self.charge_gas(amount as u64) { + Err(Error::GasLimit) + } else { + Ok(()) + } + } + + /// Adjusted charge of gas which scales actual charge according to the wasm opcode counting coefficient + pub fn adjusted_charge(&mut self, f: F) -> Result<()> + where + F: FnOnce(&vm::Schedule) -> u64, + { + self.charge(|schedule| { + f(schedule) * schedule.wasm().opcodes_div as u64 / schedule.wasm().opcodes_mul as u64 + }) + } + + /// Charge gas provided by the closure + /// + /// Closure also can return overflowing flag as None in gas cost. + pub fn overflow_charge(&mut self, f: F) -> Result<()> + where + F: FnOnce(&vm::Schedule) -> Option, + { + let amount = match f(self.ext.schedule()) { + Some(amount) => amount, + None => { + return Err(Error::GasLimit.into()); + } + }; + + if !self.charge_gas(amount as u64) { + Err(Error::GasLimit.into()) + } else { + Ok(()) + } + } + + /// Same as overflow_charge, but with amount adjusted by wasm opcodes coeff + pub fn adjusted_overflow_charge(&mut self, f: F) -> Result<()> + where + F: FnOnce(&vm::Schedule) -> Option, + { + self.overflow_charge(|schedule| { + f(schedule) + .and_then(|x| x.checked_mul(schedule.wasm().opcodes_div as u64)) + .map(|x| x / schedule.wasm().opcodes_mul as u64) + }) + } + + /// Read from the storage to wasm memory + pub fn storage_read(&mut self, args: RuntimeArgs) -> Result<()> { + let key = self.h256_at(args.nth_checked(0)?)?; + let val_ptr: u32 = args.nth_checked(1)?; + + let val = self + .ext + .storage_at(&key) + .map_err(|_| Error::StorageReadError)?; + + self.adjusted_charge(|schedule| schedule.sload_gas as u64)?; + + self.memory.set(val_ptr as u32, &*val)?; + + Ok(()) + } + + /// Write to storage from wasm memory + pub fn storage_write(&mut self, args: RuntimeArgs) -> Result<()> { + let key = self.h256_at(args.nth_checked(0)?)?; + let val_ptr: u32 = args.nth_checked(1)?; + + let val = self.h256_at(val_ptr)?; + let former_val = self + .ext + .storage_at(&key) + .map_err(|_| Error::StorageUpdateError)?; + + if former_val == H256::zero() && val != H256::zero() { + self.adjusted_charge(|schedule| schedule.sstore_set_gas as u64)?; + } else { + self.adjusted_charge(|schedule| schedule.sstore_reset_gas as u64)?; + } + + self.ext + .set_storage(key, val) + .map_err(|_| Error::StorageUpdateError)?; + + if former_val != H256::zero() && val == H256::zero() { + let sstore_clears_schedule = self.schedule().sstore_refund_gas; + self.ext.add_sstore_refund(sstore_clears_schedule); + } + + Ok(()) + } + + /// Return currently used schedule + pub fn schedule(&self) -> &vm::Schedule { + self.ext.schedule() + } + + /// Sets a return value for the call + /// + /// Syscall takes 2 arguments: + /// * pointer in sandboxed memory where result is + /// * the length of the result + pub fn ret(&mut self, args: RuntimeArgs) -> Result<()> { + let ptr: u32 = args.nth_checked(0)?; + let len: u32 = args.nth_checked(1)?; + + trace!(target: "wasm", "Contract ret: {} bytes @ {}", len, ptr); + + self.result = self.memory.get(ptr, len as usize)?; + + Err(Error::Return) + } + + /// Destroy the runtime, returning currently recorded result of the execution + pub fn into_result(self) -> Vec { + self.result + } + + /// Query current gas left for execution + pub fn gas_left(&self) -> Result { + if self.gas_counter > self.gas_limit { + return Err(Error::InvalidGasState); + } + Ok(self.gas_limit - self.gas_counter) + } + + /// General gas charging extern. + fn gas(&mut self, args: RuntimeArgs) -> Result<()> { + let amount: u32 = args.nth_checked(0)?; + if self.charge_gas(amount as u64) { + Ok(()) + } else { + Err(Error::GasLimit.into()) + } + } + + /// Query the length of the input bytes + fn input_legnth(&mut self) -> RuntimeValue { + RuntimeValue::I32(self.args.len() as i32) + } + + /// Write input bytes to the memory location using the passed pointer + fn fetch_input(&mut self, args: RuntimeArgs) -> Result<()> { + let ptr: u32 = args.nth_checked(0)?; + + let args_len = self.args.len() as u64; + self.charge(|s| args_len * s.wasm().memcpy as u64)?; + + self.memory.set(ptr, &self.args[..])?; + Ok(()) + } + + /// User panic + /// + /// Contract can invoke this when he encounters unrecoverable error. + fn panic(&mut self, args: RuntimeArgs) -> Result<()> { + let payload_ptr: u32 = args.nth_checked(0)?; + let payload_len: u32 = args.nth_checked(1)?; + + let raw_payload = self.memory.get(payload_ptr, payload_len as usize)?; + let payload = panic_payload::decode(&raw_payload); + let msg = format!( + "{msg}, {file}:{line}:{col}", + msg = payload + .msg + .as_ref() + .map(String::as_ref) + .unwrap_or(""), + file = payload + .file + .as_ref() + .map(String::as_ref) + .unwrap_or(""), + line = payload.line.unwrap_or(0), + col = payload.col.unwrap_or(0) + ); + trace!(target: "wasm", "Contract custom panic message: {}", msg); + + Err(Error::Panic(msg).into()) + } + + fn do_call( + &mut self, + use_val: bool, + call_type: CallType, + args: RuntimeArgs, + ) -> Result { + trace!(target: "wasm", "runtime: CALL({:?})", call_type); + + let gas: u64 = args.nth_checked(0)?; + trace!(target: "wasm", " gas: {:?}", gas); + + let address = self.address_at(args.nth_checked(1)?)?; + trace!(target: "wasm", " address: {:?}", address); + + let vofs = if use_val { 1 } else { 0 }; + let val = if use_val { + Some(self.u256_at(args.nth_checked(2)?)?) + } else { + None + }; + trace!(target: "wasm", " val: {:?}", val); + + let input_ptr: u32 = args.nth_checked(2 + vofs)?; + trace!(target: "wasm", " input_ptr: {:?}", input_ptr); + + let input_len: u32 = args.nth_checked(3 + vofs)?; + trace!(target: "wasm", " input_len: {:?}", input_len); + + let result_ptr: u32 = args.nth_checked(4 + vofs)?; + trace!(target: "wasm", " result_ptr: {:?}", result_ptr); + + let result_alloc_len: u32 = args.nth_checked(5 + vofs)?; + trace!(target: "wasm", " result_len: {:?}", result_alloc_len); + + if let Some(ref val) = val { + let address_balance = self + .ext + .balance(&self.context.address) + .map_err(|_| Error::BalanceQueryError)?; + + if &address_balance < val { + trace!(target: "wasm", "runtime: call failed due to balance check"); + return Ok((-1i32).into()); + } + } + + self.adjusted_charge(|schedule| schedule.call_gas as u64)?; + + let mut result = Vec::with_capacity(result_alloc_len as usize); + result.resize(result_alloc_len as usize, 0); + + // todo: optimize to use memory views once it's in + let payload = self.memory.get(input_ptr, input_len as usize)?; + + let adjusted_gas = match gas + .checked_mul(self.ext.schedule().wasm().opcodes_div as u64) + .map(|x| x / self.ext.schedule().wasm().opcodes_mul as u64) + { + Some(x) => x, + None => { + trace!("CALL overflowed gas, call aborted with error returned"); + return Ok(RuntimeValue::I32(-1)); + } + }; + + self.charge(|_| adjusted_gas)?; + + let call_result = self + .ext + .call( + &gas.into(), + match call_type { + CallType::DelegateCall => &self.context.sender, + _ => &self.context.address, + }, + match call_type { + CallType::Call | CallType::StaticCall => &address, + _ => &self.context.address, + }, + val, + &payload, + &address, + call_type, + false, + ) + .ok() + .expect("Trap is false; trap error will not happen; qed"); + + match call_result { + vm::MessageCallResult::Success(gas_left, data) => { + let len = cmp::min(result.len(), data.len()); + (&mut result[..len]).copy_from_slice(&data[..len]); + + // cannot overflow, before making call gas_counter was incremented with gas, and gas_left < gas + self.gas_counter = self.gas_counter + - gas_left.low_u64() * self.ext.schedule().wasm().opcodes_div as u64 + / self.ext.schedule().wasm().opcodes_mul as u64; + + self.memory.set(result_ptr, &result)?; + Ok(0i32.into()) + } + vm::MessageCallResult::Reverted(gas_left, data) => { + let len = cmp::min(result.len(), data.len()); + (&mut result[..len]).copy_from_slice(&data[..len]); + + // cannot overflow, before making call gas_counter was incremented with gas, and gas_left < gas + self.gas_counter = self.gas_counter + - gas_left.low_u64() * self.ext.schedule().wasm().opcodes_div as u64 + / self.ext.schedule().wasm().opcodes_mul as u64; + + self.memory.set(result_ptr, &result)?; + Ok((-1i32).into()) + } + vm::MessageCallResult::Failed => Ok((-1i32).into()), + } + } + + /// Message call + fn ccall(&mut self, args: RuntimeArgs) -> Result { + self.do_call(true, CallType::Call, args) + } + + /// Delegate call + fn dcall(&mut self, args: RuntimeArgs) -> Result { + self.do_call(false, CallType::DelegateCall, args) + } + + /// Static call + fn scall(&mut self, args: RuntimeArgs) -> Result { + self.do_call(false, CallType::StaticCall, args) + } + + fn return_address_ptr(&mut self, ptr: u32, val: Address) -> Result<()> { + self.charge(|schedule| schedule.wasm().static_address as u64)?; + self.memory.set(ptr, &*val)?; + Ok(()) + } + + fn return_u256_ptr(&mut self, ptr: u32, val: U256) -> Result<()> { + let value: H256 = val.into(); + self.charge(|schedule| schedule.wasm().static_u256 as u64)?; + self.memory.set(ptr, &*value)?; + Ok(()) + } + + /// Returns value (in Wei) passed to contract + pub fn value(&mut self, args: RuntimeArgs) -> Result<()> { + let val = self.context.value; + self.return_u256_ptr(args.nth_checked(0)?, val) + } + + fn do_create( + &mut self, + endowment: U256, + code_ptr: u32, + code_len: u32, + result_ptr: u32, + scheme: vm::CreateContractAddress, + ) -> Result { + let code = self.memory.get(code_ptr, code_len as usize)?; + + self.adjusted_charge(|schedule| schedule.create_gas as u64)?; + self.adjusted_charge(|schedule| schedule.create_data_gas as u64 * code.len() as u64)?; + + let gas_left: U256 = U256::from(self.gas_left()?) + * U256::from(self.ext.schedule().wasm().opcodes_mul) + / U256::from(self.ext.schedule().wasm().opcodes_div); + + match self + .ext + .create(&gas_left, &endowment, &code, scheme, false) + .ok() + .expect("Trap is false; trap error will not happen; qed") + { + vm::ContractCreateResult::Created(address, gas_left) => { + self.memory.set(result_ptr, &*address)?; + self.gas_counter = self.gas_limit - // this cannot overflow, since initial gas is in [0..u64::max) range, // and gas_left cannot be bigger gas_left.low_u64() * self.ext.schedule().wasm().opcodes_div as u64 / self.ext.schedule().wasm().opcodes_mul as u64; - trace!(target: "wasm", "runtime: create contract success (@{:?})", address); - Ok(0i32.into()) - }, - vm::ContractCreateResult::Failed => { - trace!(target: "wasm", "runtime: create contract fail"); - Ok((-1i32).into()) - }, - vm::ContractCreateResult::Reverted(gas_left, _) => { - trace!(target: "wasm", "runtime: create contract reverted"); - self.gas_counter = self.gas_limit - + trace!(target: "wasm", "runtime: create contract success (@{:?})", address); + Ok(0i32.into()) + } + vm::ContractCreateResult::Failed => { + trace!(target: "wasm", "runtime: create contract fail"); + Ok((-1i32).into()) + } + vm::ContractCreateResult::Reverted(gas_left, _) => { + trace!(target: "wasm", "runtime: create contract reverted"); + self.gas_counter = self.gas_limit - // this cannot overflow, since initial gas is in [0..u64::max) range, // and gas_left cannot be bigger gas_left.low_u64() * self.ext.schedule().wasm().opcodes_div as u64 / self.ext.schedule().wasm().opcodes_mul as u64; - Ok((-1i32).into()) - }, - } - } - - /// Creates a new contract - /// - /// Arguments: - /// * endowment - how much value (in Wei) transfer to the newly created contract - /// * code_ptr - pointer to the code data - /// * code_len - lenght of the code data - /// * result_ptr - pointer to write an address of the newly created contract - pub fn create(&mut self, args: RuntimeArgs) -> Result { - // - // method signature: - // fn create(endowment: *const u8, code_ptr: *const u8, code_len: u32, result_ptr: *mut u8) -> i32; - // - trace!(target: "wasm", "runtime: CREATE"); - let endowment = self.u256_at(args.nth_checked(0)?)?; - trace!(target: "wasm", " val: {:?}", endowment); - let code_ptr: u32 = args.nth_checked(1)?; - trace!(target: "wasm", " code_ptr: {:?}", code_ptr); - let code_len: u32 = args.nth_checked(2)?; - trace!(target: "wasm", " code_len: {:?}", code_len); - let result_ptr: u32 = args.nth_checked(3)?; - trace!(target: "wasm", "result_ptr: {:?}", result_ptr); - - self.do_create(endowment, code_ptr, code_len, result_ptr, vm::CreateContractAddress::FromSenderAndCodeHash) - } - - /// Creates a new contract using FromSenderSaltAndCodeHash scheme - /// - /// Arguments: - /// * endowment - how much value (in Wei) transfer to the newly created contract - /// * salt - salt to be used in contract creation address - /// * code_ptr - pointer to the code data - /// * code_len - lenght of the code data - /// * result_ptr - pointer to write an address of the newly created contract - pub fn create2(&mut self, args: RuntimeArgs) -> Result { - // - // method signature: - // fn create2(endowment: *const u8, salt: *const u8, code_ptr: *const u8, code_len: u32, result_ptr: *mut u8) -> i32; - // - trace!(target: "wasm", "runtime: CREATE2"); - let endowment = self.u256_at(args.nth_checked(0)?)?; - trace!(target: "wasm", " val: {:?}", endowment); - let salt: H256 = self.u256_at(args.nth_checked(1)?)?.into(); - trace!(target: "wasm", " salt: {:?}", salt); - let code_ptr: u32 = args.nth_checked(2)?; - trace!(target: "wasm", " code_ptr: {:?}", code_ptr); - let code_len: u32 = args.nth_checked(3)?; - trace!(target: "wasm", " code_len: {:?}", code_len); - let result_ptr: u32 = args.nth_checked(4)?; - trace!(target: "wasm", "result_ptr: {:?}", result_ptr); - - self.do_create(endowment, code_ptr, code_len, result_ptr, vm::CreateContractAddress::FromSenderSaltAndCodeHash(salt)) - } - - fn debug(&mut self, args: RuntimeArgs) -> Result<()> - { - trace!(target: "wasm", "Contract debug message: {}", { - let msg_ptr: u32 = args.nth_checked(0)?; - let msg_len: u32 = args.nth_checked(1)?; - - String::from_utf8(self.memory.get(msg_ptr, msg_len as usize)?) - .map_err(|_| Error::BadUtf8)? - }); - - Ok(()) - } - - /// Pass suicide to state runtime - pub fn suicide(&mut self, args: RuntimeArgs) -> Result<()> - { - let refund_address = self.address_at(args.nth_checked(0)?)?; - - if self.ext.exists(&refund_address).map_err(|_| Error::SuicideAbort)? { - trace!(target: "wasm", "Suicide: refund to existing address {}", refund_address); - self.adjusted_charge(|schedule| schedule.suicide_gas as u64)?; - } else { - trace!(target: "wasm", "Suicide: refund to new address {}", refund_address); - self.adjusted_charge(|schedule| schedule.suicide_to_new_account_cost as u64)?; - } - - self.ext.suicide(&refund_address).map_err(|_| Error::SuicideAbort)?; - - // We send trap to interpreter so it should abort further execution - Err(Error::Suicide.into()) - } - - /// Signature: `fn blockhash(number: i64, dest: *mut u8)` - pub fn blockhash(&mut self, args: RuntimeArgs) -> Result<()> { - self.adjusted_charge(|schedule| schedule.blockhash_gas as u64)?; - let hash = self.ext.blockhash(&U256::from(args.nth_checked::(0)?)); - self.memory.set(args.nth_checked(1)?, &*hash)?; - - Ok(()) - } - - /// Signature: `fn blocknumber() -> i64` - pub fn blocknumber(&mut self) -> Result { - Ok(RuntimeValue::from(self.ext.env_info().number)) - } - - /// Signature: `fn coinbase(dest: *mut u8)` - pub fn coinbase(&mut self, args: RuntimeArgs) -> Result<()> { - let coinbase = self.ext.env_info().author; - self.return_address_ptr(args.nth_checked(0)?, coinbase) - } - - /// Signature: `fn difficulty(dest: *mut u8)` - pub fn difficulty(&mut self, args: RuntimeArgs) -> Result<()> { - let difficulty = self.ext.env_info().difficulty; - self.return_u256_ptr(args.nth_checked(0)?, difficulty) - } - - /// Signature: `fn gasleft() -> i64` - pub fn gasleft(&mut self) -> Result { - Ok(RuntimeValue::from( - self.gas_left()? * self.ext.schedule().wasm().opcodes_mul as u64 - / self.ext.schedule().wasm().opcodes_div as u64 - ) - ) - } - - /// Signature: `fn gaslimit(dest: *mut u8)` - pub fn gaslimit(&mut self, args: RuntimeArgs) -> Result<()> { - let gas_limit = self.ext.env_info().gas_limit; - self.return_u256_ptr(args.nth_checked(0)?, gas_limit) - } - - /// Signature: `fn address(dest: *mut u8)` - pub fn address(&mut self, args: RuntimeArgs) -> Result<()> { - let address = self.context.address; - self.return_address_ptr(args.nth_checked(0)?, address) - } - - /// Signature: `sender(dest: *mut u8)` - pub fn sender(&mut self, args: RuntimeArgs) -> Result<()> { - let sender = self.context.sender; - self.return_address_ptr(args.nth_checked(0)?, sender) - } - - /// Signature: `origin(dest: *mut u8)` - pub fn origin(&mut self, args: RuntimeArgs) -> Result<()> { - let origin = self.context.origin; - self.return_address_ptr(args.nth_checked(0)?, origin) - } - - /// Signature: `timestamp() -> i64` - pub fn timestamp(&mut self) -> Result { - let timestamp = self.ext.env_info().timestamp; - Ok(RuntimeValue::from(timestamp)) - } - - /// Signature: `fn elog(topic_ptr: *const u8, topic_count: u32, data_ptr: *const u8, data_len: u32)` - pub fn elog(&mut self, args: RuntimeArgs) -> Result<()> - { - let topic_ptr: u32 = args.nth_checked(0)?; - let topic_count: u32 = args.nth_checked(1)?; - let data_ptr: u32 = args.nth_checked(2)?; - let data_len: u32 = args.nth_checked(3)?; - - if topic_count > 4 { - return Err(Error::Log.into()); - } - - self.adjusted_overflow_charge(|schedule| - { - let topics_gas = schedule.log_gas as u64 + schedule.log_topic_gas as u64 * topic_count as u64; - (schedule.log_data_gas as u64) - .checked_mul(schedule.log_data_gas as u64) - .and_then(|data_gas| data_gas.checked_add(topics_gas)) - } - )?; - - let mut topics: Vec = Vec::with_capacity(topic_count as usize); - topics.resize(topic_count as usize, H256::zero()); - for i in 0..topic_count { - let offset = i.checked_mul(32).ok_or(Error::MemoryAccessViolation)? - .checked_add(topic_ptr).ok_or(Error::MemoryAccessViolation)?; - - *topics.get_mut(i as usize) + Ok((-1i32).into()) + } + } + } + + /// Creates a new contract + /// + /// Arguments: + /// * endowment - how much value (in Wei) transfer to the newly created contract + /// * code_ptr - pointer to the code data + /// * code_len - lenght of the code data + /// * result_ptr - pointer to write an address of the newly created contract + pub fn create(&mut self, args: RuntimeArgs) -> Result { + // + // method signature: + // fn create(endowment: *const u8, code_ptr: *const u8, code_len: u32, result_ptr: *mut u8) -> i32; + // + trace!(target: "wasm", "runtime: CREATE"); + let endowment = self.u256_at(args.nth_checked(0)?)?; + trace!(target: "wasm", " val: {:?}", endowment); + let code_ptr: u32 = args.nth_checked(1)?; + trace!(target: "wasm", " code_ptr: {:?}", code_ptr); + let code_len: u32 = args.nth_checked(2)?; + trace!(target: "wasm", " code_len: {:?}", code_len); + let result_ptr: u32 = args.nth_checked(3)?; + trace!(target: "wasm", "result_ptr: {:?}", result_ptr); + + self.do_create( + endowment, + code_ptr, + code_len, + result_ptr, + vm::CreateContractAddress::FromSenderAndCodeHash, + ) + } + + /// Creates a new contract using FromSenderSaltAndCodeHash scheme + /// + /// Arguments: + /// * endowment - how much value (in Wei) transfer to the newly created contract + /// * salt - salt to be used in contract creation address + /// * code_ptr - pointer to the code data + /// * code_len - lenght of the code data + /// * result_ptr - pointer to write an address of the newly created contract + pub fn create2(&mut self, args: RuntimeArgs) -> Result { + // + // method signature: + // fn create2(endowment: *const u8, salt: *const u8, code_ptr: *const u8, code_len: u32, result_ptr: *mut u8) -> i32; + // + trace!(target: "wasm", "runtime: CREATE2"); + let endowment = self.u256_at(args.nth_checked(0)?)?; + trace!(target: "wasm", " val: {:?}", endowment); + let salt: H256 = self.u256_at(args.nth_checked(1)?)?.into(); + trace!(target: "wasm", " salt: {:?}", salt); + let code_ptr: u32 = args.nth_checked(2)?; + trace!(target: "wasm", " code_ptr: {:?}", code_ptr); + let code_len: u32 = args.nth_checked(3)?; + trace!(target: "wasm", " code_len: {:?}", code_len); + let result_ptr: u32 = args.nth_checked(4)?; + trace!(target: "wasm", "result_ptr: {:?}", result_ptr); + + self.do_create( + endowment, + code_ptr, + code_len, + result_ptr, + vm::CreateContractAddress::FromSenderSaltAndCodeHash(salt), + ) + } + + fn debug(&mut self, args: RuntimeArgs) -> Result<()> { + trace!(target: "wasm", "Contract debug message: {}", { + let msg_ptr: u32 = args.nth_checked(0)?; + let msg_len: u32 = args.nth_checked(1)?; + + String::from_utf8(self.memory.get(msg_ptr, msg_len as usize)?) + .map_err(|_| Error::BadUtf8)? + }); + + Ok(()) + } + + /// Pass suicide to state runtime + pub fn suicide(&mut self, args: RuntimeArgs) -> Result<()> { + let refund_address = self.address_at(args.nth_checked(0)?)?; + + if self + .ext + .exists(&refund_address) + .map_err(|_| Error::SuicideAbort)? + { + trace!(target: "wasm", "Suicide: refund to existing address {}", refund_address); + self.adjusted_charge(|schedule| schedule.suicide_gas as u64)?; + } else { + trace!(target: "wasm", "Suicide: refund to new address {}", refund_address); + self.adjusted_charge(|schedule| schedule.suicide_to_new_account_cost as u64)?; + } + + self.ext + .suicide(&refund_address) + .map_err(|_| Error::SuicideAbort)?; + + // We send trap to interpreter so it should abort further execution + Err(Error::Suicide.into()) + } + + /// Signature: `fn blockhash(number: i64, dest: *mut u8)` + pub fn blockhash(&mut self, args: RuntimeArgs) -> Result<()> { + self.adjusted_charge(|schedule| schedule.blockhash_gas as u64)?; + let hash = self.ext.blockhash(&U256::from(args.nth_checked::(0)?)); + self.memory.set(args.nth_checked(1)?, &*hash)?; + + Ok(()) + } + + /// Signature: `fn blocknumber() -> i64` + pub fn blocknumber(&mut self) -> Result { + Ok(RuntimeValue::from(self.ext.env_info().number)) + } + + /// Signature: `fn coinbase(dest: *mut u8)` + pub fn coinbase(&mut self, args: RuntimeArgs) -> Result<()> { + let coinbase = self.ext.env_info().author; + self.return_address_ptr(args.nth_checked(0)?, coinbase) + } + + /// Signature: `fn difficulty(dest: *mut u8)` + pub fn difficulty(&mut self, args: RuntimeArgs) -> Result<()> { + let difficulty = self.ext.env_info().difficulty; + self.return_u256_ptr(args.nth_checked(0)?, difficulty) + } + + /// Signature: `fn gasleft() -> i64` + pub fn gasleft(&mut self) -> Result { + Ok(RuntimeValue::from( + self.gas_left()? * self.ext.schedule().wasm().opcodes_mul as u64 + / self.ext.schedule().wasm().opcodes_div as u64, + )) + } + + /// Signature: `fn gaslimit(dest: *mut u8)` + pub fn gaslimit(&mut self, args: RuntimeArgs) -> Result<()> { + let gas_limit = self.ext.env_info().gas_limit; + self.return_u256_ptr(args.nth_checked(0)?, gas_limit) + } + + /// Signature: `fn address(dest: *mut u8)` + pub fn address(&mut self, args: RuntimeArgs) -> Result<()> { + let address = self.context.address; + self.return_address_ptr(args.nth_checked(0)?, address) + } + + /// Signature: `sender(dest: *mut u8)` + pub fn sender(&mut self, args: RuntimeArgs) -> Result<()> { + let sender = self.context.sender; + self.return_address_ptr(args.nth_checked(0)?, sender) + } + + /// Signature: `origin(dest: *mut u8)` + pub fn origin(&mut self, args: RuntimeArgs) -> Result<()> { + let origin = self.context.origin; + self.return_address_ptr(args.nth_checked(0)?, origin) + } + + /// Signature: `timestamp() -> i64` + pub fn timestamp(&mut self) -> Result { + let timestamp = self.ext.env_info().timestamp; + Ok(RuntimeValue::from(timestamp)) + } + + /// Signature: `fn elog(topic_ptr: *const u8, topic_count: u32, data_ptr: *const u8, data_len: u32)` + pub fn elog(&mut self, args: RuntimeArgs) -> Result<()> { + let topic_ptr: u32 = args.nth_checked(0)?; + let topic_count: u32 = args.nth_checked(1)?; + let data_ptr: u32 = args.nth_checked(2)?; + let data_len: u32 = args.nth_checked(3)?; + + if topic_count > 4 { + return Err(Error::Log.into()); + } + + self.adjusted_overflow_charge(|schedule| { + let topics_gas = + schedule.log_gas as u64 + schedule.log_topic_gas as u64 * topic_count as u64; + (schedule.log_data_gas as u64) + .checked_mul(schedule.log_data_gas as u64) + .and_then(|data_gas| data_gas.checked_add(topics_gas)) + })?; + + let mut topics: Vec = Vec::with_capacity(topic_count as usize); + topics.resize(topic_count as usize, H256::zero()); + for i in 0..topic_count { + let offset = i + .checked_mul(32) + .ok_or(Error::MemoryAccessViolation)? + .checked_add(topic_ptr) + .ok_or(Error::MemoryAccessViolation)?; + + *topics.get_mut(i as usize) .expect("topics is resized to `topic_count`, i is in 0..topic count iterator, get_mut uses i as an indexer, get_mut cannot fail; qed") = H256::from(&self.memory.get(offset, 32)?[..]); - } - self.ext.log(topics, &self.memory.get(data_ptr, data_len as usize)?).map_err(|_| Error::Log)?; + } + self.ext + .log(topics, &self.memory.get(data_ptr, data_len as usize)?) + .map_err(|_| Error::Log)?; - Ok(()) - } + Ok(()) + } } mod ext_impl { - use wasmi::{Externals, RuntimeArgs, RuntimeValue, Trap}; - use env::ids::*; + use env::ids::*; + use wasmi::{Externals, RuntimeArgs, RuntimeValue, Trap}; - macro_rules! void { + macro_rules! void { { $e: expr } => { { $e?; Ok(None) } } } - macro_rules! some { + macro_rules! some { { $e: expr } => { { Ok(Some($e?)) } } } - macro_rules! cast { + macro_rules! cast { { $e: expr } => { { Ok(Some($e)) } } } - impl<'a> Externals for super::Runtime<'a> { - fn invoke_index( - &mut self, - index: usize, - args: RuntimeArgs, - ) -> Result, Trap> { - match index { - STORAGE_WRITE_FUNC => void!(self.storage_write(args)), - STORAGE_READ_FUNC => void!(self.storage_read(args)), - RET_FUNC => void!(self.ret(args)), - GAS_FUNC => void!(self.gas(args)), - INPUT_LENGTH_FUNC => cast!(self.input_legnth()), - FETCH_INPUT_FUNC => void!(self.fetch_input(args)), - PANIC_FUNC => void!(self.panic(args)), - DEBUG_FUNC => void!(self.debug(args)), - CCALL_FUNC => some!(self.ccall(args)), - DCALL_FUNC => some!(self.dcall(args)), - SCALL_FUNC => some!(self.scall(args)), - VALUE_FUNC => void!(self.value(args)), - CREATE_FUNC => some!(self.create(args)), - SUICIDE_FUNC => void!(self.suicide(args)), - BLOCKHASH_FUNC => void!(self.blockhash(args)), - BLOCKNUMBER_FUNC => some!(self.blocknumber()), - COINBASE_FUNC => void!(self.coinbase(args)), - DIFFICULTY_FUNC => void!(self.difficulty(args)), - GASLIMIT_FUNC => void!(self.gaslimit(args)), - TIMESTAMP_FUNC => some!(self.timestamp()), - ADDRESS_FUNC => void!(self.address(args)), - SENDER_FUNC => void!(self.sender(args)), - ORIGIN_FUNC => void!(self.origin(args)), - ELOG_FUNC => void!(self.elog(args)), - CREATE2_FUNC => some!(self.create2(args)), - GASLEFT_FUNC => some!(self.gasleft()), - _ => panic!("env module doesn't provide function at index {}", index), - } - } - } + impl<'a> Externals for super::Runtime<'a> { + fn invoke_index( + &mut self, + index: usize, + args: RuntimeArgs, + ) -> Result, Trap> { + match index { + STORAGE_WRITE_FUNC => void!(self.storage_write(args)), + STORAGE_READ_FUNC => void!(self.storage_read(args)), + RET_FUNC => void!(self.ret(args)), + GAS_FUNC => void!(self.gas(args)), + INPUT_LENGTH_FUNC => cast!(self.input_legnth()), + FETCH_INPUT_FUNC => void!(self.fetch_input(args)), + PANIC_FUNC => void!(self.panic(args)), + DEBUG_FUNC => void!(self.debug(args)), + CCALL_FUNC => some!(self.ccall(args)), + DCALL_FUNC => some!(self.dcall(args)), + SCALL_FUNC => some!(self.scall(args)), + VALUE_FUNC => void!(self.value(args)), + CREATE_FUNC => some!(self.create(args)), + SUICIDE_FUNC => void!(self.suicide(args)), + BLOCKHASH_FUNC => void!(self.blockhash(args)), + BLOCKNUMBER_FUNC => some!(self.blocknumber()), + COINBASE_FUNC => void!(self.coinbase(args)), + DIFFICULTY_FUNC => void!(self.difficulty(args)), + GASLIMIT_FUNC => void!(self.gaslimit(args)), + TIMESTAMP_FUNC => some!(self.timestamp()), + ADDRESS_FUNC => void!(self.address(args)), + SENDER_FUNC => void!(self.sender(args)), + ORIGIN_FUNC => void!(self.origin(args)), + ELOG_FUNC => void!(self.elog(args)), + CREATE2_FUNC => some!(self.create2(args)), + GASLEFT_FUNC => some!(self.gasleft()), + _ => panic!("env module doesn't provide function at index {}", index), + } + } + } } diff --git a/ethcore/wasm/src/tests.rs b/ethcore/wasm/src/tests.rs index 9ed7053da18..00cf534191d 100644 --- a/ethcore/wasm/src/tests.rs +++ b/ethcore/wasm/src/tests.rs @@ -1,92 +1,101 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::sync::Arc; -use std::collections::HashMap; -use byteorder::{LittleEndian, ByteOrder}; -use ethereum_types::{H256, U256, Address}; +use byteorder::{ByteOrder, LittleEndian}; +use ethereum_types::{Address, H256, U256}; +use std::{collections::HashMap, sync::Arc}; use super::WasmInterpreter; -use vm::{self, Exec, GasLeft, ActionParams, ActionValue, CreateContractAddress}; -use vm::tests::{FakeCall, FakeExt, FakeCallType}; +use vm::{ + self, + tests::{FakeCall, FakeCallType, FakeExt}, + ActionParams, ActionValue, CreateContractAddress, Exec, GasLeft, +}; macro_rules! load_sample { - ($name: expr) => { - include_bytes!(concat!("../../res/wasm-tests/compiled/", $name)).to_vec() - } + ($name: expr) => { + include_bytes!(concat!("../../res/wasm-tests/compiled/", $name)).to_vec() + }; } macro_rules! reqrep_test { - ($name: expr, $input: expr) => { - reqrep_test!($name, $input, vm::EnvInfo::default(), HashMap::new()) - }; - ($name: expr, $input: expr, $info: expr, $block_hashes: expr) => { - { - let _ = ::env_logger::try_init(); - let code = load_sample!($name); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - params.data = Some($input); - - let mut fake_ext = FakeExt::new().with_wasm(); - fake_ext.info = $info; - fake_ext.blockhashes = $block_hashes; - - let mut interpreter = wasm_interpreter(params); - interpreter.exec(&mut fake_ext).ok().unwrap() - .map(|result| match result { - GasLeft::Known(_) => { panic!("Test is expected to return payload to check"); }, - GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), - }) - } - }; + ($name: expr, $input: expr) => { + reqrep_test!($name, $input, vm::EnvInfo::default(), HashMap::new()) + }; + ($name: expr, $input: expr, $info: expr, $block_hashes: expr) => {{ + let _ = ::env_logger::try_init(); + let code = load_sample!($name); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + params.data = Some($input); + + let mut fake_ext = FakeExt::new().with_wasm(); + fake_ext.info = $info; + fake_ext.blockhashes = $block_hashes; + + let interpreter = wasm_interpreter(params); + interpreter + .exec(&mut fake_ext) + .ok() + .unwrap() + .map(|result| match result { + GasLeft::Known(_) => { + panic!("Test is expected to return payload to check"); + } + GasLeft::NeedsReturn { + gas_left: gas, + data: result, + apply_state: _apply, + } => (gas, result.to_vec()), + }) + }}; } fn test_finalize(res: Result) -> Result { - match res { - Ok(GasLeft::Known(gas)) => Ok(gas), - Ok(GasLeft::NeedsReturn{..}) => unimplemented!(), // since ret is unimplemented. - Err(e) => Err(e), - } + match res { + Ok(GasLeft::Known(gas)) => Ok(gas), + Ok(GasLeft::NeedsReturn { .. }) => unimplemented!(), // since ret is unimplemented. + Err(e) => Err(e), + } } fn wasm_interpreter(params: ActionParams) -> Box { - Box::new(WasmInterpreter::new(params)) + Box::new(WasmInterpreter::new(params)) } /// Empty contract does almost nothing except producing 1 (one) local node debug log message #[test] fn empty() { - let code = load_sample!("empty.wasm"); - let address: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); + let code = load_sample!("empty.wasm"); + let address: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); - let mut params = ActionParams::default(); - params.address = address.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new().with_wasm(); + let mut params = ActionParams::default(); + params.address = address.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new().with_wasm(); - let gas_left = { - let mut interpreter = wasm_interpreter(params); - test_finalize(interpreter.exec(&mut ext).ok().unwrap()).unwrap() - }; + let gas_left = { + let interpreter = wasm_interpreter(params); + test_finalize(interpreter.exec(&mut ext).ok().unwrap()).unwrap() + }; - assert_eq!(gas_left, U256::from(96_926)); + assert_eq!(gas_left, U256::from(96_926)); } // This test checks if the contract deserializes payload header properly. @@ -94,51 +103,77 @@ fn empty() { // logger.wasm writes all these provided fixed header fields to some arbitrary storage keys. #[test] fn logger() { - let _ = ::env_logger::try_init(); - - let code = load_sample!("logger.wasm"); - let address: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); - let sender: Address = "0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d".parse().unwrap(); - let origin: Address = "0102030405060708090a0b0c0d0e0f1011121314".parse().unwrap(); - - let mut params = ActionParams::default(); - params.address = address.clone(); - params.sender = sender.clone(); - params.origin = origin.clone(); - params.gas = U256::from(100_000); - params.value = ActionValue::transfer(1_000_000_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new().with_wasm(); - - let gas_left = { - let mut interpreter = wasm_interpreter(params); - test_finalize(interpreter.exec(&mut ext).ok().unwrap()).unwrap() - }; - - let address_val: H256 = address.into(); - assert_eq!( - ext.store.get(&"0100000000000000000000000000000000000000000000000000000000000000".parse().unwrap()).expect("storage key to exist"), - &address_val, - "Logger sets 0x01 key to the provided address" - ); - let sender_val: H256 = sender.into(); - assert_eq!( - ext.store.get(&"0200000000000000000000000000000000000000000000000000000000000000".parse().unwrap()).expect("storage key to exist"), - &sender_val, - "Logger sets 0x02 key to the provided sender" - ); - let origin_val: H256 = origin.into(); - assert_eq!( - ext.store.get(&"0300000000000000000000000000000000000000000000000000000000000000".parse().unwrap()).expect("storage key to exist"), - &origin_val, - "Logger sets 0x03 key to the provided origin" - ); - assert_eq!( - U256::from(ext.store.get(&"0400000000000000000000000000000000000000000000000000000000000000".parse().unwrap()).expect("storage key to exist")), - U256::from(1_000_000_000), - "Logger sets 0x04 key to the trasferred value" - ); - assert_eq!(gas_left, U256::from(17_716)); + let _ = ::env_logger::try_init(); + + let code = load_sample!("logger.wasm"); + let address: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); + let sender: Address = "0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d".parse().unwrap(); + let origin: Address = "0102030405060708090a0b0c0d0e0f1011121314".parse().unwrap(); + + let mut params = ActionParams::default(); + params.address = address.clone(); + params.sender = sender.clone(); + params.origin = origin.clone(); + params.gas = U256::from(100_000); + params.value = ActionValue::transfer(1_000_000_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new().with_wasm(); + + let gas_left = { + let interpreter = wasm_interpreter(params); + test_finalize(interpreter.exec(&mut ext).ok().unwrap()).unwrap() + }; + + let address_val: H256 = address.into(); + assert_eq!( + ext.store + .get( + &"0100000000000000000000000000000000000000000000000000000000000000" + .parse() + .unwrap() + ) + .expect("storage key to exist"), + &address_val, + "Logger sets 0x01 key to the provided address" + ); + let sender_val: H256 = sender.into(); + assert_eq!( + ext.store + .get( + &"0200000000000000000000000000000000000000000000000000000000000000" + .parse() + .unwrap() + ) + .expect("storage key to exist"), + &sender_val, + "Logger sets 0x02 key to the provided sender" + ); + let origin_val: H256 = origin.into(); + assert_eq!( + ext.store + .get( + &"0300000000000000000000000000000000000000000000000000000000000000" + .parse() + .unwrap() + ) + .expect("storage key to exist"), + &origin_val, + "Logger sets 0x03 key to the provided origin" + ); + assert_eq!( + U256::from( + ext.store + .get( + &"0400000000000000000000000000000000000000000000000000000000000000" + .parse() + .unwrap() + ) + .expect("storage key to exist") + ), + U256::from(1_000_000_000), + "Logger sets 0x04 key to the trasferred value" + ); + assert_eq!(gas_left, U256::from(17_716)); } // This test checks if the contract can allocate memory and pass pointer to the result stream properly. @@ -148,32 +183,42 @@ fn logger() { // if it has any result. #[test] fn identity() { - let _ = ::env_logger::try_init(); - - let code = load_sample!("identity.wasm"); - let sender: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap(); - - let mut params = ActionParams::default(); - params.sender = sender.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new().with_wasm(); - - let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); - match result { - GasLeft::Known(_) => { panic!("Identity contract should return payload"); }, - GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), - } - }; - - assert_eq!( - Address::from_slice(&result), - sender, - "Idenity test contract does not return the sender passed" - ); - assert_eq!(gas_left, U256::from(98_419)); + let _ = ::env_logger::try_init(); + + let code = load_sample!("identity.wasm"); + let sender: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap(); + + let mut params = ActionParams::default(); + params.sender = sender.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new().with_wasm(); + + let (gas_left, result) = { + let interpreter = wasm_interpreter(params); + let result = interpreter + .exec(&mut ext) + .ok() + .unwrap() + .expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(_) => { + panic!("Identity contract should return payload"); + } + GasLeft::NeedsReturn { + gas_left: gas, + data: result, + apply_state: _apply, + } => (gas, result.to_vec()), + } + }; + + assert_eq!( + Address::from_slice(&result), + sender, + "Idenity test contract does not return the sender passed" + ); + assert_eq!(gas_left, U256::from(98_419)); } // Dispersion test sends byte array and expect the contract to 'disperse' the original elements with @@ -182,435 +227,537 @@ fn identity() { // This also tests byte-perfect memory allocation and in/out ptr lifecycle. #[test] fn dispersion() { - let _ = ::env_logger::try_init(); - - let code = load_sample!("dispersion.wasm"); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - params.data = Some(vec![ - 0u8, 125, 197, 255, 19 - ]); - let mut ext = FakeExt::new().with_wasm(); - - let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); - match result { - GasLeft::Known(_) => { panic!("Dispersion routine should return payload"); }, - GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), - } - }; - - assert_eq!( - result, - vec![0u8, 0, 125, 11, 197, 7, 255, 8, 19, 0] - ); - assert_eq!(gas_left, U256::from(92_377)); + let _ = ::env_logger::try_init(); + + let code = load_sample!("dispersion.wasm"); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + params.data = Some(vec![0u8, 125, 197, 255, 19]); + let mut ext = FakeExt::new().with_wasm(); + + let (gas_left, result) = { + let interpreter = wasm_interpreter(params); + let result = interpreter + .exec(&mut ext) + .ok() + .unwrap() + .expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(_) => { + panic!("Dispersion routine should return payload"); + } + GasLeft::NeedsReturn { + gas_left: gas, + data: result, + apply_state: _apply, + } => (gas, result.to_vec()), + } + }; + + assert_eq!(result, vec![0u8, 0, 125, 11, 197, 7, 255, 8, 19, 0]); + assert_eq!(gas_left, U256::from(92_377)); } #[test] fn suicide_not() { - let code = load_sample!("suicidal.wasm"); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - params.data = Some(vec![ - 0u8 - ]); - let mut ext = FakeExt::new().with_wasm(); - - let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); - match result { - GasLeft::Known(_) => { panic!("Suicidal contract should return payload when had not actualy killed himself"); }, - GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), - } - }; - - assert_eq!( - result, - vec![0u8] - ); - assert_eq!(gas_left, U256::from(93_378)); + let code = load_sample!("suicidal.wasm"); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + params.data = Some(vec![0u8]); + let mut ext = FakeExt::new().with_wasm(); + + let (gas_left, result) = { + let interpreter = wasm_interpreter(params); + let result = interpreter + .exec(&mut ext) + .ok() + .unwrap() + .expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(_) => { + panic!( + "Suicidal contract should return payload when had not actualy killed himself" + ); + } + GasLeft::NeedsReturn { + gas_left: gas, + data: result, + apply_state: _apply, + } => (gas, result.to_vec()), + } + }; + + assert_eq!(result, vec![0u8]); + assert_eq!(gas_left, U256::from(93_378)); } #[test] fn suicide() { - let _ = ::env_logger::try_init(); - - let code = load_sample!("suicidal.wasm"); - - let refund: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap(); - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - - let mut args = vec![127u8]; - args.extend(refund.to_vec()); - params.data = Some(args); - - let mut ext = FakeExt::new().with_wasm(); - - let gas_left = { - let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); - match result { - GasLeft::Known(gas) => gas, - GasLeft::NeedsReturn { .. } => { - panic!("Suicidal contract should not return anything when had killed itself"); - }, - } - }; - - assert!(ext.suicides.contains(&refund)); - assert_eq!(gas_left, U256::from(93_346)); + let _ = ::env_logger::try_init(); + + let code = load_sample!("suicidal.wasm"); + + let refund: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap(); + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + + let mut args = vec![127u8]; + args.extend(refund.to_vec()); + params.data = Some(args); + + let mut ext = FakeExt::new().with_wasm(); + + let gas_left = { + let interpreter = wasm_interpreter(params); + let result = interpreter + .exec(&mut ext) + .ok() + .unwrap() + .expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(gas) => gas, + GasLeft::NeedsReturn { .. } => { + panic!("Suicidal contract should not return anything when had killed itself"); + } + } + }; + + assert!(ext.suicides.contains(&refund)); + assert_eq!(gas_left, U256::from(93_346)); } #[test] fn create() { - let _ = ::env_logger::try_init(); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(load_sample!("creator.wasm"))); - params.data = Some(vec![0u8, 2, 4, 8, 16, 32, 64, 128]); - params.value = ActionValue::transfer(1_000_000_000); - - let mut ext = FakeExt::new().with_wasm(); - ext.schedule.wasm.as_mut().unwrap().have_create2 = true; - - let gas_left = { - let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); - match result { - GasLeft::Known(_) => { - panic!("Create contract always return 40 bytes of the creation address, or in the case where it fails, return 40 bytes of zero."); - }, - GasLeft::NeedsReturn { gas_left, data, apply_state } => { - assert!(apply_state); - assert_eq!(data.as_ref(), [0u8; 40].as_ref()); // FakeExt never succeeds in create. - gas_left - }, - } - }; - - trace!(target: "wasm", "fake_calls: {:?}", &ext.calls); - assert!(ext.calls.contains( - &FakeCall { - call_type: FakeCallType::Create, - create_scheme: Some(CreateContractAddress::FromSenderAndCodeHash), - gas: U256::from(49_674), - sender_address: None, - receive_address: None, - value: Some((1_000_000_000 / 2).into()), - data: vec![0u8, 2, 4, 8, 16, 32, 64, 128], - code_address: None, - } - )); - assert!(ext.calls.contains( - &FakeCall { - call_type: FakeCallType::Create, - create_scheme: Some(CreateContractAddress::FromSenderSaltAndCodeHash(H256::from([5u8].as_ref()))), - gas: U256::from(6039), - sender_address: None, - receive_address: None, - value: Some((1_000_000_000 / 2).into()), - data: vec![0u8, 2, 4, 8, 16, 32, 64, 128], - code_address: None, - } - )); - assert_eq!(gas_left, U256::from(5974)); + let _ = ::env_logger::try_init(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(load_sample!("creator.wasm"))); + params.data = Some(vec![0u8, 2, 4, 8, 16, 32, 64, 128]); + params.value = ActionValue::transfer(1_000_000_000); + + let mut ext = FakeExt::new().with_wasm(); + ext.schedule.wasm.as_mut().unwrap().have_create2 = true; + + let gas_left = { + let interpreter = wasm_interpreter(params); + let result = interpreter + .exec(&mut ext) + .ok() + .unwrap() + .expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(_) => { + panic!("Create contract always return 40 bytes of the creation address, or in the case where it fails, return 40 bytes of zero."); + } + GasLeft::NeedsReturn { + gas_left, + data, + apply_state, + } => { + assert!(apply_state); + assert_eq!(data.as_ref(), [0u8; 40].as_ref()); // FakeExt never succeeds in create. + gas_left + } + } + }; + + trace!(target: "wasm", "fake_calls: {:?}", &ext.calls); + assert!(ext.calls.contains(&FakeCall { + call_type: FakeCallType::Create, + create_scheme: Some(CreateContractAddress::FromSenderAndCodeHash), + gas: U256::from(49_674), + sender_address: None, + receive_address: None, + value: Some((1_000_000_000 / 2).into()), + data: vec![0u8, 2, 4, 8, 16, 32, 64, 128], + code_address: None, + })); + assert!(ext.calls.contains(&FakeCall { + call_type: FakeCallType::Create, + create_scheme: Some(CreateContractAddress::FromSenderSaltAndCodeHash( + H256::from([5u8].as_ref()) + )), + gas: U256::from(6039), + sender_address: None, + receive_address: None, + value: Some((1_000_000_000 / 2).into()), + data: vec![0u8, 2, 4, 8, 16, 32, 64, 128], + code_address: None, + })); + assert_eq!(gas_left, U256::from(5974)); } #[test] fn call_msg() { - let _ = ::env_logger::try_init(); - - let sender: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap(); - let receiver: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); - let contract_address: Address = "0d461d4174b4ae35775c4a342f1e5e1e4e6c4db5".parse().unwrap(); - - let mut params = ActionParams::default(); - params.sender = sender.clone(); - params.address = receiver.clone(); - params.code_address = contract_address.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(load_sample!("call.wasm"))); - params.data = Some(Vec::new()); - - let mut ext = FakeExt::new().with_wasm(); - ext.balances.insert(receiver.clone(), U256::from(10000000000u64)); - - let gas_left = { - let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); - match result { - GasLeft::Known(gas_left) => gas_left, - GasLeft::NeedsReturn { .. } => { panic!("Call test should not return payload"); }, - } - }; - - trace!(target: "wasm", "fake_calls: {:?}", &ext.calls); - assert!(ext.calls.contains( - &FakeCall { - call_type: FakeCallType::Call, - create_scheme: None, - gas: U256::from(33_000), - sender_address: Some(receiver), - receive_address: Some(Address::from([99, 88, 77, 66, 55, 44, 33, 22, 11, 0, 11, 22, 33, 44, 55, 66, 77, 88, 99, 0])), - value: Some(1000000000.into()), - data: vec![129u8, 123, 113, 107, 101, 97], - code_address: Some(Address::from([99, 88, 77, 66, 55, 44, 33, 22, 11, 0, 11, 22, 33, 44, 55, 66, 77, 88, 99, 0])), - } - )); - - assert_eq!(gas_left, U256::from(91_672)); + let _ = ::env_logger::try_init(); + + let sender: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap(); + let receiver: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); + let contract_address: Address = "0d461d4174b4ae35775c4a342f1e5e1e4e6c4db5".parse().unwrap(); + + let mut params = ActionParams::default(); + params.sender = sender.clone(); + params.address = receiver.clone(); + params.code_address = contract_address.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(load_sample!("call.wasm"))); + params.data = Some(Vec::new()); + + let mut ext = FakeExt::new().with_wasm(); + ext.balances + .insert(receiver.clone(), U256::from(10000000000u64)); + + let gas_left = { + let interpreter = wasm_interpreter(params); + let result = interpreter + .exec(&mut ext) + .ok() + .unwrap() + .expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(gas_left) => gas_left, + GasLeft::NeedsReturn { .. } => { + panic!("Call test should not return payload"); + } + } + }; + + trace!(target: "wasm", "fake_calls: {:?}", &ext.calls); + assert!(ext.calls.contains(&FakeCall { + call_type: FakeCallType::Call, + create_scheme: None, + gas: U256::from(33_000), + sender_address: Some(receiver), + receive_address: Some(Address::from([ + 99, 88, 77, 66, 55, 44, 33, 22, 11, 0, 11, 22, 33, 44, 55, 66, 77, 88, 99, 0 + ])), + value: Some(1000000000.into()), + data: vec![129u8, 123, 113, 107, 101, 97], + code_address: Some(Address::from([ + 99, 88, 77, 66, 55, 44, 33, 22, 11, 0, 11, 22, 33, 44, 55, 66, 77, 88, 99, 0 + ])), + })); + + assert_eq!(gas_left, U256::from(91_672)); } // The same as `call_msg`, but send a `pwasm_ethereum::gasleft` // value as `gas` argument to the inner pwasm_ethereum::call #[test] fn call_msg_gasleft() { - let _ = ::env_logger::try_init(); - - let sender: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap(); - let receiver: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); - let contract_address: Address = "0d461d4174b4ae35775c4a342f1e5e1e4e6c4db5".parse().unwrap(); - - let mut params = ActionParams::default(); - params.sender = sender.clone(); - params.address = receiver.clone(); - params.code_address = contract_address.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(load_sample!("call_gasleft.wasm"))); - params.data = Some(Vec::new()); - - let mut ext = FakeExt::new().with_wasm(); - ext.schedule.wasm.as_mut().unwrap().have_gasleft = true; - ext.balances.insert(receiver.clone(), U256::from(10000000000u64)); - - let gas_left = { - let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); - match result { - GasLeft::Known(gas_left) => gas_left, - GasLeft::NeedsReturn { .. } => { panic!("Call test should not return payload"); }, - } - }; - - trace!(target: "wasm", "fake_calls: {:?}", &ext.calls); - assert!(ext.calls.contains( - &FakeCall { - call_type: FakeCallType::Call, - create_scheme: None, - gas: U256::from(91_165), - sender_address: Some(receiver), - receive_address: Some(Address::from([99, 88, 77, 66, 55, 44, 33, 22, 11, 0, 11, 22, 33, 44, 55, 66, 77, 88, 99, 0])), - value: Some(1000000000.into()), - data: vec![129u8, 123, 113, 107, 101, 97], - code_address: Some(Address::from([99, 88, 77, 66, 55, 44, 33, 22, 11, 0, 11, 22, 33, 44, 55, 66, 77, 88, 99, 0])), - } - )); - - assert_eq!(gas_left, U256::from(91_671)); + let _ = ::env_logger::try_init(); + + let sender: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap(); + let receiver: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); + let contract_address: Address = "0d461d4174b4ae35775c4a342f1e5e1e4e6c4db5".parse().unwrap(); + + let mut params = ActionParams::default(); + params.sender = sender.clone(); + params.address = receiver.clone(); + params.code_address = contract_address.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(load_sample!("call_gasleft.wasm"))); + params.data = Some(Vec::new()); + + let mut ext = FakeExt::new().with_wasm(); + ext.schedule.wasm.as_mut().unwrap().have_gasleft = true; + ext.balances + .insert(receiver.clone(), U256::from(10000000000u64)); + + let gas_left = { + let interpreter = wasm_interpreter(params); + let result = interpreter + .exec(&mut ext) + .ok() + .unwrap() + .expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(gas_left) => gas_left, + GasLeft::NeedsReturn { .. } => { + panic!("Call test should not return payload"); + } + } + }; + + trace!(target: "wasm", "fake_calls: {:?}", &ext.calls); + assert!(ext.calls.contains(&FakeCall { + call_type: FakeCallType::Call, + create_scheme: None, + gas: U256::from(91_165), + sender_address: Some(receiver), + receive_address: Some(Address::from([ + 99, 88, 77, 66, 55, 44, 33, 22, 11, 0, 11, 22, 33, 44, 55, 66, 77, 88, 99, 0 + ])), + value: Some(1000000000.into()), + data: vec![129u8, 123, 113, 107, 101, 97], + code_address: Some(Address::from([ + 99, 88, 77, 66, 55, 44, 33, 22, 11, 0, 11, 22, 33, 44, 55, 66, 77, 88, 99, 0 + ])), + })); + + assert_eq!(gas_left, U256::from(91_671)); } #[test] fn call_code() { - let _ = ::env_logger::try_init(); - - let sender: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap(); - let receiver: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); - - let mut params = ActionParams::default(); - params.sender = sender.clone(); - params.address = receiver.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(load_sample!("call_code.wasm"))); - params.data = Some(Vec::new()); - params.value = ActionValue::transfer(1_000_000_000); - - let mut ext = FakeExt::new().with_wasm(); - - let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); - match result { - GasLeft::Known(_) => { panic!("Call test should return payload"); }, - GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), - } - }; - - trace!(target: "wasm", "fake_calls: {:?}", &ext.calls); - assert!(ext.calls.contains( - &FakeCall { - call_type: FakeCallType::Call, - create_scheme: None, - gas: U256::from(20_000), - sender_address: Some(sender), - receive_address: Some(receiver), - value: None, - data: vec![1u8, 2, 3, 5, 7, 11], - code_address: Some("0d13710000000000000000000000000000000000".parse().unwrap()), - } - )); - - // siphash result - let res = LittleEndian::read_u32(&result[..]); - assert_eq!(res, 4198595614); - assert_eq!(gas_left, U256::from(90_037)); + let _ = ::env_logger::try_init(); + + let sender: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap(); + let receiver: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); + + let mut params = ActionParams::default(); + params.sender = sender.clone(); + params.address = receiver.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(load_sample!("call_code.wasm"))); + params.data = Some(Vec::new()); + params.value = ActionValue::transfer(1_000_000_000); + + let mut ext = FakeExt::new().with_wasm(); + + let (gas_left, result) = { + let interpreter = wasm_interpreter(params); + let result = interpreter + .exec(&mut ext) + .ok() + .unwrap() + .expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(_) => { + panic!("Call test should return payload"); + } + GasLeft::NeedsReturn { + gas_left: gas, + data: result, + apply_state: _apply, + } => (gas, result.to_vec()), + } + }; + + trace!(target: "wasm", "fake_calls: {:?}", &ext.calls); + assert!(ext.calls.contains(&FakeCall { + call_type: FakeCallType::Call, + create_scheme: None, + gas: U256::from(20_000), + sender_address: Some(sender), + receive_address: Some(receiver), + value: None, + data: vec![1u8, 2, 3, 5, 7, 11], + code_address: Some("0d13710000000000000000000000000000000000".parse().unwrap()), + })); + + // siphash result + let res = LittleEndian::read_u32(&result[..]); + assert_eq!(res, 4198595614); + assert_eq!(gas_left, U256::from(90_037)); } #[test] fn call_static() { - let _ = ::env_logger::try_init(); - - let sender: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); - let receiver: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap(); - let contract_address: Address = "0d461d4174b4ae35775c4a342f1e5e1e4e6c4db5".parse().unwrap(); - - let mut params = ActionParams::default(); - params.sender = sender.clone(); - params.address = receiver.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(load_sample!("call_static.wasm"))); - params.data = Some(Vec::new()); - params.value = ActionValue::transfer(1_000_000_000); - params.code_address = contract_address.clone(); - - let mut ext = FakeExt::new().with_wasm(); - - let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); - match result { - GasLeft::Known(_) => { panic!("Static call test should return payload"); }, - GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), - } - }; - - trace!(target: "wasm", "fake_calls: {:?}", &ext.calls); - assert!(ext.calls.contains( - &FakeCall { - call_type: FakeCallType::Call, - create_scheme: None, - gas: U256::from(20_000), - sender_address: Some(receiver), - receive_address: Some("13077bfb00000000000000000000000000000000".parse().unwrap()), - value: None, - data: vec![1u8, 2, 3, 5, 7, 11], - code_address: Some("13077bfb00000000000000000000000000000000".parse().unwrap()), - } - )); - - // siphash result - let res = LittleEndian::read_u32(&result[..]); - assert_eq!(res, 317632590); - - assert_eq!(gas_left, U256::from(90_042)); + let _ = ::env_logger::try_init(); + + let sender: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); + let receiver: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap(); + let contract_address: Address = "0d461d4174b4ae35775c4a342f1e5e1e4e6c4db5".parse().unwrap(); + + let mut params = ActionParams::default(); + params.sender = sender.clone(); + params.address = receiver.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(load_sample!("call_static.wasm"))); + params.data = Some(Vec::new()); + params.value = ActionValue::transfer(1_000_000_000); + params.code_address = contract_address.clone(); + + let mut ext = FakeExt::new().with_wasm(); + + let (gas_left, result) = { + let interpreter = wasm_interpreter(params); + let result = interpreter + .exec(&mut ext) + .ok() + .unwrap() + .expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(_) => { + panic!("Static call test should return payload"); + } + GasLeft::NeedsReturn { + gas_left: gas, + data: result, + apply_state: _apply, + } => (gas, result.to_vec()), + } + }; + + trace!(target: "wasm", "fake_calls: {:?}", &ext.calls); + assert!(ext.calls.contains(&FakeCall { + call_type: FakeCallType::Call, + create_scheme: None, + gas: U256::from(20_000), + sender_address: Some(receiver), + receive_address: Some("13077bfb00000000000000000000000000000000".parse().unwrap()), + value: None, + data: vec![1u8, 2, 3, 5, 7, 11], + code_address: Some("13077bfb00000000000000000000000000000000".parse().unwrap()), + })); + + // siphash result + let res = LittleEndian::read_u32(&result[..]); + assert_eq!(res, 317632590); + + assert_eq!(gas_left, U256::from(90_042)); } // Realloc test #[test] fn realloc() { - let code = load_sample!("realloc.wasm"); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - params.data = Some(vec![0u8]); - let mut ext = FakeExt::new().with_wasm(); - - let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); - match result { - GasLeft::Known(_) => { panic!("Realloc should return payload"); }, - GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), - } - }; - assert_eq!(result, vec![0u8; 2]); - assert_eq!(gas_left, U256::from(92_848)); + let code = load_sample!("realloc.wasm"); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + params.data = Some(vec![0u8]); + let mut ext = FakeExt::new().with_wasm(); + + let (gas_left, result) = { + let interpreter = wasm_interpreter(params); + let result = interpreter + .exec(&mut ext) + .ok() + .unwrap() + .expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(_) => { + panic!("Realloc should return payload"); + } + GasLeft::NeedsReturn { + gas_left: gas, + data: result, + apply_state: _apply, + } => (gas, result.to_vec()), + } + }; + assert_eq!(result, vec![0u8; 2]); + assert_eq!(gas_left, U256::from(92_848)); } #[test] fn alloc() { - let code = load_sample!("alloc.wasm"); - - let mut params = ActionParams::default(); - params.gas = U256::from(10_000_000); - params.code = Some(Arc::new(code)); - params.data = Some(vec![0u8]); - let mut ext = FakeExt::new().with_wasm(); - - let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); - match result { - GasLeft::Known(_) => { panic!("alloc test should return payload"); }, - GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), - } - }; - assert_eq!(result, vec![5u8; 1024*400]); - assert_eq!(gas_left, U256::from(6_893_881)); + let code = load_sample!("alloc.wasm"); + + let mut params = ActionParams::default(); + params.gas = U256::from(10_000_000); + params.code = Some(Arc::new(code)); + params.data = Some(vec![0u8]); + let mut ext = FakeExt::new().with_wasm(); + + let (gas_left, result) = { + let interpreter = wasm_interpreter(params); + let result = interpreter + .exec(&mut ext) + .ok() + .unwrap() + .expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(_) => { + panic!("alloc test should return payload"); + } + GasLeft::NeedsReturn { + gas_left: gas, + data: result, + apply_state: _apply, + } => (gas, result.to_vec()), + } + }; + assert_eq!(result, vec![5u8; 1024 * 400]); + assert_eq!(gas_left, U256::from(6_893_881)); } // Tests that contract's ability to read from a storage // Test prepopulates address into storage, than executes a contract which read that address from storage and write this address into result #[test] fn storage_read() { - let _ = ::env_logger::try_init(); - - let code = load_sample!("storage_read.wasm"); - let address: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new().with_wasm(); - ext.store.insert("0100000000000000000000000000000000000000000000000000000000000000".into(), address.into()); - - let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); - match result { - GasLeft::Known(_) => { panic!("storage_read should return payload"); }, - GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), - } - }; - - assert_eq!(Address::from(&result[12..32]), address); - assert_eq!(gas_left, U256::from(98_369)); + let _ = ::env_logger::try_init(); + + let code = load_sample!("storage_read.wasm"); + let address: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new().with_wasm(); + ext.store.insert( + "0100000000000000000000000000000000000000000000000000000000000000".into(), + address.into(), + ); + + let (gas_left, result) = { + let interpreter = wasm_interpreter(params); + let result = interpreter + .exec(&mut ext) + .ok() + .unwrap() + .expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(_) => { + panic!("storage_read should return payload"); + } + GasLeft::NeedsReturn { + gas_left: gas, + data: result, + apply_state: _apply, + } => (gas, result.to_vec()), + } + }; + + assert_eq!(Address::from(&result[12..32]), address); + assert_eq!(gas_left, U256::from(98_369)); } // Tests keccak calculation // keccak.wasm runs wasm-std::keccak function on data param and returns hash #[test] fn keccak() { - let _ = ::env_logger::try_init(); - let code = load_sample!("keccak.wasm"); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - params.data = Some(b"something".to_vec()); - let mut ext = FakeExt::new().with_wasm(); - - let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); - match result { - GasLeft::Known(_) => { panic!("keccak should return payload"); }, - GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), - } - }; - - assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87")); - assert_eq!(gas_left, U256::from(85_949)); + let _ = ::env_logger::try_init(); + let code = load_sample!("keccak.wasm"); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + params.data = Some(b"something".to_vec()); + let mut ext = FakeExt::new().with_wasm(); + + let (gas_left, result) = { + let interpreter = wasm_interpreter(params); + let result = interpreter + .exec(&mut ext) + .ok() + .unwrap() + .expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(_) => { + panic!("keccak should return payload"); + } + GasLeft::NeedsReturn { + gas_left: gas, + data: result, + apply_state: _apply, + } => (gas, result.to_vec()), + } + }; + + assert_eq!( + H256::from_slice(&result), + H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87") + ); + assert_eq!(gas_left, U256::from(85_949)); } // math_* tests check the ability of wasm contract to perform big integer operations @@ -622,312 +769,324 @@ fn keccak() { // addition #[test] fn math_add() { - - let (gas_left, result) = reqrep_test!( - "math.wasm", - { - let mut args = [0u8; 65]; - let arg_a = U256::from_dec_str("999999999999999999999999999999").unwrap(); - let arg_b = U256::from_dec_str("888888888888888888888888888888").unwrap(); - arg_a.to_big_endian(&mut args[1..33]); - arg_b.to_big_endian(&mut args[33..65]); - args.to_vec() - } - ).expect("Interpreter to execute without any errors"); - - assert_eq!( - U256::from_dec_str("1888888888888888888888888888887").unwrap(), - (&result[..]).into() - ); - assert_eq!(gas_left, U256::from(92_072)); + let (gas_left, result) = reqrep_test!("math.wasm", { + let mut args = [0u8; 65]; + let arg_a = U256::from_dec_str("999999999999999999999999999999").unwrap(); + let arg_b = U256::from_dec_str("888888888888888888888888888888").unwrap(); + arg_a.to_big_endian(&mut args[1..33]); + arg_b.to_big_endian(&mut args[33..65]); + args.to_vec() + }) + .expect("Interpreter to execute without any errors"); + + assert_eq!( + U256::from_dec_str("1888888888888888888888888888887").unwrap(), + (&result[..]).into() + ); + assert_eq!(gas_left, U256::from(92_072)); } // multiplication #[test] fn math_mul() { - let (gas_left, result) = reqrep_test!( - "math.wasm", - { - let mut args = [1u8; 65]; - let arg_a = U256::from_dec_str("888888888888888888888888888888").unwrap(); - let arg_b = U256::from_dec_str("999999999999999999999999999999").unwrap(); - arg_a.to_big_endian(&mut args[1..33]); - arg_b.to_big_endian(&mut args[33..65]); - args.to_vec() - } - ).expect("Interpreter to execute without any errors"); - - assert_eq!( - U256::from_dec_str("888888888888888888888888888887111111111111111111111111111112").unwrap(), - (&result[..]).into() - ); - assert_eq!(gas_left, U256::from(91_400)); + let (gas_left, result) = reqrep_test!("math.wasm", { + let mut args = [1u8; 65]; + let arg_a = U256::from_dec_str("888888888888888888888888888888").unwrap(); + let arg_b = U256::from_dec_str("999999999999999999999999999999").unwrap(); + arg_a.to_big_endian(&mut args[1..33]); + arg_b.to_big_endian(&mut args[33..65]); + args.to_vec() + }) + .expect("Interpreter to execute without any errors"); + + assert_eq!( + U256::from_dec_str("888888888888888888888888888887111111111111111111111111111112").unwrap(), + (&result[..]).into() + ); + assert_eq!(gas_left, U256::from(91_400)); } // subtraction #[test] fn math_sub() { - let (gas_left, result) = reqrep_test!( - "math.wasm", - { - let mut args = [2u8; 65]; - let arg_a = U256::from_dec_str("999999999999999999999999999999").unwrap(); - let arg_b = U256::from_dec_str("888888888888888888888888888888").unwrap(); - arg_a.to_big_endian(&mut args[1..33]); - arg_b.to_big_endian(&mut args[33..65]); - args.to_vec() - } - ).expect("Interpreter to execute without any errors"); - - assert_eq!( - U256::from_dec_str("111111111111111111111111111111").unwrap(), - (&result[..]).into() - ); - assert_eq!(gas_left, U256::from(92_072)); + let (gas_left, result) = reqrep_test!("math.wasm", { + let mut args = [2u8; 65]; + let arg_a = U256::from_dec_str("999999999999999999999999999999").unwrap(); + let arg_b = U256::from_dec_str("888888888888888888888888888888").unwrap(); + arg_a.to_big_endian(&mut args[1..33]); + arg_b.to_big_endian(&mut args[33..65]); + args.to_vec() + }) + .expect("Interpreter to execute without any errors"); + + assert_eq!( + U256::from_dec_str("111111111111111111111111111111").unwrap(), + (&result[..]).into() + ); + assert_eq!(gas_left, U256::from(92_072)); } // subtraction with overflow #[test] fn math_sub_with_overflow() { - let result = reqrep_test!( - "math.wasm", - { - let mut args = [2u8; 65]; - let arg_a = U256::from_dec_str("888888888888888888888888888888").unwrap(); - let arg_b = U256::from_dec_str("999999999999999999999999999999").unwrap(); - arg_a.to_big_endian(&mut args[1..33]); - arg_b.to_big_endian(&mut args[33..65]); - args.to_vec() - } - ); - - match result { - Err(vm::Error::Wasm(_)) => {}, - _ => panic!("Unexpected result {:?}", result), - } + let result = reqrep_test!("math.wasm", { + let mut args = [2u8; 65]; + let arg_a = U256::from_dec_str("888888888888888888888888888888").unwrap(); + let arg_b = U256::from_dec_str("999999999999999999999999999999").unwrap(); + arg_a.to_big_endian(&mut args[1..33]); + arg_b.to_big_endian(&mut args[33..65]); + args.to_vec() + }); + + match result { + Err(vm::Error::Wasm(_)) => {} + _ => panic!("Unexpected result {:?}", result), + } } #[test] fn math_div() { - let (gas_left, result) = reqrep_test!( - "math.wasm", - { - let mut args = [3u8; 65]; - let arg_a = U256::from_dec_str("999999999999999999999999999999").unwrap(); - let arg_b = U256::from_dec_str("888888888888888888888888").unwrap(); - arg_a.to_big_endian(&mut args[1..33]); - arg_b.to_big_endian(&mut args[33..65]); - args.to_vec() - } - ).expect("Interpreter to execute without any errors"); - - assert_eq!( - U256::from_dec_str("1125000").unwrap(), - (&result[..]).into() - ); - assert_eq!(gas_left, U256::from(85_700)); + let (gas_left, result) = reqrep_test!("math.wasm", { + let mut args = [3u8; 65]; + let arg_a = U256::from_dec_str("999999999999999999999999999999").unwrap(); + let arg_b = U256::from_dec_str("888888888888888888888888").unwrap(); + arg_a.to_big_endian(&mut args[1..33]); + arg_b.to_big_endian(&mut args[33..65]); + args.to_vec() + }) + .expect("Interpreter to execute without any errors"); + + assert_eq!(U256::from_dec_str("1125000").unwrap(), (&result[..]).into()); + assert_eq!(gas_left, U256::from(85_700)); } #[test] fn storage_metering() { - let _ = ::env_logger::try_init(); - - // #1 - let mut ext = FakeExt::new().with_wasm(); - - let code = Arc::new(load_sample!("setter.wasm")); - let address: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); - - let mut params = ActionParams::default(); - params.address = address.clone(); - params.gas = U256::from(100_000); - params.code = Some(code.clone()); - params.data = Some(vec![ - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, - ]); - - let gas_left = { - let mut interpreter = wasm_interpreter(params); - test_finalize(interpreter.exec(&mut ext).ok().unwrap()).unwrap() - }; - - // 0 -> not 0 - assert_eq!(gas_left, U256::from(72_164)); - - // #2 - - let mut params = ActionParams::default(); - params.address = address.clone(); - params.gas = U256::from(100_000); - params.code = Some(code.clone()); - params.data = Some(vec![ - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x6b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, - ]); - - let gas_left = { - let mut interpreter = wasm_interpreter(params); - test_finalize(interpreter.exec(&mut ext).ok().unwrap()).unwrap() - }; - - // not 0 -> not 0 - assert_eq!(gas_left, U256::from(87_164)); + let _ = ::env_logger::try_init(); + + // #1 + let mut ext = FakeExt::new().with_wasm(); + + let code = Arc::new(load_sample!("setter.wasm")); + let address: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); + + let mut params = ActionParams::default(); + params.address = address.clone(); + params.gas = U256::from(100_000); + params.code = Some(code.clone()); + params.data = Some(vec![ + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, + 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, + 0x7b, 0x7b, 0x7b, 0x7b, + ]); + + let gas_left = { + let interpreter = wasm_interpreter(params); + test_finalize(interpreter.exec(&mut ext).ok().unwrap()).unwrap() + }; + + // 0 -> not 0 + assert_eq!(gas_left, U256::from(72_164)); + + // #2 + + let mut params = ActionParams::default(); + params.address = address.clone(); + params.gas = U256::from(100_000); + params.code = Some(code.clone()); + params.data = Some(vec![ + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x6b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, + 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, + 0x7b, 0x7b, 0x7b, 0x7b, + ]); + + let gas_left = { + let interpreter = wasm_interpreter(params); + test_finalize(interpreter.exec(&mut ext).ok().unwrap()).unwrap() + }; + + // not 0 -> not 0 + assert_eq!(gas_left, U256::from(87_164)); } // This test checks the ability of wasm contract to invoke // varios blockchain runtime methods #[test] fn externs() { - let (gas_left, result) = reqrep_test!( - "externs.wasm", - Vec::new(), - vm::EnvInfo { - number: 0x9999999999u64.into(), - author: "efefefefefefefefefefefefefefefefefefefef".parse().unwrap(), - timestamp: 0x8888888888u64.into(), - difficulty: H256::from("0f1f2f3f4f5f6f7f8f9fafbfcfdfefff0d1d2d3d4d5d6d7d8d9dadbdcdddedfd").into(), - gas_limit: 0x777777777777u64.into(), - last_hashes: Default::default(), - gas_used: 0.into(), - }, - { - let mut hashes = HashMap::new(); - hashes.insert( - U256::from(0), - H256::from("9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d") - ); - hashes.insert( - U256::from(1), - H256::from("7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b") - ); - hashes - } - ).expect("Interpreter to execute without any errors"); - - assert_eq!( - &result[0..64].to_vec(), - &vec![ - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, - ], - "Block hashes requested and returned do not match" - ); - - assert_eq!( - &result[64..84].to_vec(), - &vec![ - 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, - ], - "Coinbase requested and returned does not match" - ); - - assert_eq!( - &result[84..92].to_vec(), - &vec![ - 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00 - ], - "Timestamp requested and returned does not match" - ); - - assert_eq!( - &result[92..100].to_vec(), - &vec![ - 0x99, 0x99, 0x99, 0x99, 0x99, 0x00, 0x00, 0x00 - ], - "Block number requested and returned does not match" - ); - - assert_eq!( - &result[100..132].to_vec(), - &vec![ - 0x0f, 0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f, - 0x8f, 0x9f, 0xaf, 0xbf, 0xcf, 0xdf, 0xef, 0xff, - 0x0d, 0x1d, 0x2d, 0x3d, 0x4d, 0x5d, 0x6d, 0x7d, - 0x8d, 0x9d, 0xad, 0xbd, 0xcd, 0xdd, 0xed, 0xfd, - ], - "Difficulty requested and returned does not match" - ); - - assert_eq!( - &result[132..164].to_vec(), - &vec![ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, - ], - "Gas limit requested and returned does not match" - ); - - assert_eq!(gas_left, U256::from(90_428)); + let (gas_left, result) = reqrep_test!( + "externs.wasm", + Vec::new(), + vm::EnvInfo { + number: 0x9999999999u64.into(), + author: "efefefefefefefefefefefefefefefefefefefef".parse().unwrap(), + timestamp: 0x8888888888u64.into(), + difficulty: H256::from( + "0f1f2f3f4f5f6f7f8f9fafbfcfdfefff0d1d2d3d4d5d6d7d8d9dadbdcdddedfd" + ) + .into(), + gas_limit: 0x777777777777u64.into(), + last_hashes: Default::default(), + gas_used: 0.into(), + }, + { + let mut hashes = HashMap::new(); + hashes.insert( + U256::from(0), + H256::from("9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d"), + ); + hashes.insert( + U256::from(1), + H256::from("7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b"), + ); + hashes + } + ) + .expect("Interpreter to execute without any errors"); + + assert_eq!( + &result[0..64].to_vec(), + &vec![ + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, + 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, + 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, + ], + "Block hashes requested and returned do not match" + ); + + assert_eq!( + &result[64..84].to_vec(), + &vec![ + 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, + 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, + ], + "Coinbase requested and returned does not match" + ); + + assert_eq!( + &result[84..92].to_vec(), + &vec![0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00], + "Timestamp requested and returned does not match" + ); + + assert_eq!( + &result[92..100].to_vec(), + &vec![0x99, 0x99, 0x99, 0x99, 0x99, 0x00, 0x00, 0x00], + "Block number requested and returned does not match" + ); + + assert_eq!( + &result[100..132].to_vec(), + &vec![ + 0x0f, 0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f, 0x8f, 0x9f, 0xaf, 0xbf, 0xcf, 0xdf, + 0xef, 0xff, 0x0d, 0x1d, 0x2d, 0x3d, 0x4d, 0x5d, 0x6d, 0x7d, 0x8d, 0x9d, 0xad, 0xbd, + 0xcd, 0xdd, 0xed, 0xfd, + ], + "Difficulty requested and returned does not match" + ); + + assert_eq!( + &result[132..164].to_vec(), + &vec![ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x77, + 0x77, 0x77, 0x77, 0x77, + ], + "Gas limit requested and returned does not match" + ); + + assert_eq!(gas_left, U256::from(90_428)); } // This test checks the ability of wasm contract to invoke gasleft #[test] fn gasleft() { - let _ = ::env_logger::try_init(); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(load_sample!("gasleft.wasm"))); - - let mut ext = FakeExt::new().with_wasm(); - ext.schedule.wasm.as_mut().unwrap().have_gasleft = true; - - let interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); - match result { - GasLeft::Known(_) => {}, - GasLeft::NeedsReturn { gas_left, data, .. } => { - let gas = LittleEndian::read_u64(data.as_ref()); - assert_eq!(gas, 93_423); - assert_eq!(gas_left, U256::from(93_349)); - }, - } + let _ = ::env_logger::try_init(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(load_sample!("gasleft.wasm"))); + + let mut ext = FakeExt::new().with_wasm(); + ext.schedule.wasm.as_mut().unwrap().have_gasleft = true; + + let interpreter = wasm_interpreter(params); + let result = interpreter + .exec(&mut ext) + .ok() + .unwrap() + .expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(_) => {} + GasLeft::NeedsReturn { gas_left, data, .. } => { + let gas = LittleEndian::read_u64(data.as_ref()); + assert_eq!(gas, 93_423); + assert_eq!(gas_left, U256::from(93_349)); + } + } } // This test should fail because // ext.schedule.wasm.as_mut().unwrap().have_gasleft = false; #[test] fn gasleft_fail() { - let _ = ::env_logger::try_init(); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(load_sample!("gasleft.wasm"))); - let mut ext = FakeExt::new().with_wasm(); - let interpreter = wasm_interpreter(params); - match interpreter.exec(&mut ext).ok().unwrap() { - Err(_) => {}, - Ok(_) => panic!("interpreter.exec should return Err if ext.schedule.wasm.have_gasleft = false") - } + let _ = ::env_logger::try_init(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(load_sample!("gasleft.wasm"))); + let mut ext = FakeExt::new().with_wasm(); + let interpreter = wasm_interpreter(params); + match interpreter.exec(&mut ext).ok().unwrap() { + Err(_) => {} + Ok(_) => { + panic!("interpreter.exec should return Err if ext.schedule.wasm.have_gasleft = false") + } + } } #[test] fn embedded_keccak() { - let _ = ::env_logger::try_init(); - let mut code = load_sample!("keccak.wasm"); - code.extend_from_slice(b"something"); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - params.params_type = vm::ParamsType::Embedded; - - let mut ext = FakeExt::new().with_wasm(); - - let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); - match result { - GasLeft::Known(_) => { panic!("keccak should return payload"); }, - GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), - } - }; - - assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87")); - assert_eq!(gas_left, U256::from(85_949)); + let _ = ::env_logger::try_init(); + let mut code = load_sample!("keccak.wasm"); + code.extend_from_slice(b"something"); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + params.params_type = vm::ParamsType::Embedded; + + let mut ext = FakeExt::new().with_wasm(); + + let (gas_left, result) = { + let interpreter = wasm_interpreter(params); + let result = interpreter + .exec(&mut ext) + .ok() + .unwrap() + .expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(_) => { + panic!("keccak should return payload"); + } + GasLeft::NeedsReturn { + gas_left: gas, + data: result, + apply_state: _apply, + } => (gas, result.to_vec()), + } + }; + + assert_eq!( + H256::from_slice(&result), + H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87") + ); + assert_eq!(gas_left, U256::from(85_949)); } /// This test checks the correctness of log extern @@ -935,72 +1094,88 @@ fn embedded_keccak() { /// and reversed input as a data #[test] fn events() { - let _ = ::env_logger::try_init(); - let code = load_sample!("events.wasm"); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - params.data = Some(b"something".to_vec()); - - let mut ext = FakeExt::new().with_wasm(); - - let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); - match result { - GasLeft::Known(_) => { panic!("events should return payload"); }, - GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), - } - }; - - assert_eq!(ext.logs.len(), 1); - let log_entry = &ext.logs[0]; - assert_eq!(log_entry.topics.len(), 2); - assert_eq!(&log_entry.topics[0], &H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87")); - assert_eq!(&log_entry.topics[1], &H256::from("871d5ea37430753faab7dff7a7187783517d83bd822c02e28a164c887e1d3768")); - assert_eq!(&log_entry.data, b"gnihtemos"); - - assert_eq!(&result, b"gnihtemos"); - assert_eq!(gas_left, U256::from(83_161)); + let _ = ::env_logger::try_init(); + let code = load_sample!("events.wasm"); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + params.data = Some(b"something".to_vec()); + + let mut ext = FakeExt::new().with_wasm(); + + let (gas_left, result) = { + let interpreter = wasm_interpreter(params); + let result = interpreter + .exec(&mut ext) + .ok() + .unwrap() + .expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(_) => { + panic!("events should return payload"); + } + GasLeft::NeedsReturn { + gas_left: gas, + data: result, + apply_state: _apply, + } => (gas, result.to_vec()), + } + }; + + assert_eq!(ext.logs.len(), 1); + let log_entry = &ext.logs[0]; + assert_eq!(log_entry.topics.len(), 2); + assert_eq!( + &log_entry.topics[0], + &H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87") + ); + assert_eq!( + &log_entry.topics[1], + &H256::from("871d5ea37430753faab7dff7a7187783517d83bd822c02e28a164c887e1d3768") + ); + assert_eq!(&log_entry.data, b"gnihtemos"); + + assert_eq!(&result, b"gnihtemos"); + assert_eq!(gas_left, U256::from(83_161)); } #[test] fn recursive() { - let _ = ::env_logger::try_init(); - let code = load_sample!("recursive.wasm"); - - let mut params = ActionParams::default(); - params.gas = U256::from(100_000_000); - params.code = Some(Arc::new(code)); - params.data = Some({ - // `recursive` expects only one 32-bit word in LE that - // represents an iteration count. - // - // We pick a relative big number to definitely hit stack overflow. - use byteorder::WriteBytesExt; - let mut data = vec![]; - data.write_u32::(100000).unwrap(); - data - }); - - let mut ext = FakeExt::new().with_wasm(); - - let interpreter = wasm_interpreter(params); - let result = interpreter.exec(&mut ext).ok().unwrap(); - - // We expect that stack overflow will occur and it should be generated by - // deterministic stack metering. Exceeding deterministic stack height limit - // always ends with a trap generated by `unreachable` instruction. - match result { - Err(trap) => { - let err_description = trap.to_string(); - assert!( - err_description.contains("Unreachable"), - "err_description: {} should contain 'Unreachable'", - err_description - ); - }, - _ => panic!("this test should trap"), - } + let _ = ::env_logger::try_init(); + let code = load_sample!("recursive.wasm"); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000_000); + params.code = Some(Arc::new(code)); + params.data = Some({ + // `recursive` expects only one 32-bit word in LE that + // represents an iteration count. + // + // We pick a relative big number to definitely hit stack overflow. + use byteorder::WriteBytesExt; + let mut data = vec![]; + data.write_u32::(100000).unwrap(); + data + }); + + let mut ext = FakeExt::new().with_wasm(); + + let interpreter = wasm_interpreter(params); + let result = interpreter.exec(&mut ext).ok().unwrap(); + + // We expect that stack overflow will occur and it should be generated by + // deterministic stack metering. Exceeding deterministic stack height limit + // always ends with a trap generated by `unreachable` instruction. + match result { + Err(trap) => { + let err_description = trap.to_string(); + assert!( + err_description.contains("Unreachable"), + "err_description: {} should contain 'Unreachable'", + err_description + ); + } + _ => panic!("this test should trap"), + } } diff --git a/evmbin/Cargo.toml b/evmbin/Cargo.toml index 95b532366cd..18d0b3e8e06 100644 --- a/evmbin/Cargo.toml +++ b/evmbin/Cargo.toml @@ -1,15 +1,15 @@ [package] +description = "Parity EVM Implementation" name = "evmbin" -description = "Parity's EVM implementation" version = "0.1.0" authors = ["Parity Technologies "] [[bin]] -name = "parity-evm" +name = "openethereum-evm" path = "./src/main.rs" [dependencies] -common-types = { path = "../ethcore/types" } +common-types = { path = "../ethcore/types", features = ["test-helpers"] } docopt = "1.0" env_logger = "0.5" ethcore = { path = "../ethcore", features = ["test-helpers", "json-tests", "to-pod-full"] } @@ -25,6 +25,7 @@ serde_json = "1.0" vm = { path = "../ethcore/vm" } [dev-dependencies] +criterion = "0.3.0" pretty_assertions = "0.1" tempdir = "0.3" diff --git a/evmbin/README.md b/evmbin/README.md index e0a8c9d9669..1610187b41f 100644 --- a/evmbin/README.md +++ b/evmbin/README.md @@ -1,19 +1,19 @@ ## evmbin -EVM implementation for Parity. +EVM implementation for OpenEthereum. ### Usage ``` -EVM implementation for Parity. - Copyright 2015-2019 Parity Technologies (UK) Ltd. +EVM implementation for OpenEthereum. + Copyright 2015-2020 Parity Technologies (UK) Ltd. Usage: - parity-evm state-test [--json --std-json --std-dump-json --only NAME --chain CHAIN --std-out-only --std-err-only] - parity-evm stats [options] - parity-evm stats-jsontests-vm - parity-evm [options] - parity-evm [-h | --help] + openethereum-evm state-test [--json --std-json --std-dump-json --only NAME --chain CHAIN --std-out-only --std-err-only] + openethereum-evm stats [options] + openethereum-evm stats-jsontests-vm + openethereum-evm [options] + openethereum-evm [-h | --help] Commands: state-test Run a state test from a json file. @@ -45,11 +45,10 @@ Display result state dump in standardized JSON format. -h, --help Display this message and exit. ``` -## Parity Ethereum toolchain -_This project is a part of the Parity Ethereum toolchain._ +## OpenEthereum toolchain +_This project is a part of the OpenEthereum toolchain._ -- [evmbin](https://github.com/paritytech/parity-ethereum/blob/master/evmbin/) - EVM implementation for Parity Ethereum. -- [ethabi](https://github.com/paritytech/ethabi) - Parity Ethereum function calls encoding. -- [ethstore](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethstore) - Parity Ethereum key management. -- [ethkey](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethkey) - Parity Ethereum keys generator. -- [whisper](https://github.com/paritytech/parity-ethereum/blob/master/whisper/) - Implementation of Whisper-v2 PoC. +- [evmbin](https://github.com/openethereum/openethereum/blob/master/evmbin/) - EVM implementation for OpenEthereum +- [ethabi](https://github.com/openethereum/ethabi) - OpenEthereum function calls encoding. +- [ethstore](https://github.com/openethereum/openethereum/blob/master/accounts/ethstore) - OpenEthereum key management. +- [ethkey](https://github.com/openethereum/openethereum/blob/master/accounts/ethkey) - OpenEthereum keys generator. diff --git a/evmbin/benches/mod.rs b/evmbin/benches/mod.rs index d8872b5100e..e02f1ea9187 100644 --- a/evmbin/benches/mod.rs +++ b/evmbin/benches/mod.rs @@ -1,85 +1,98 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! benchmarking for EVM //! should be started with: //! ```bash -//! multirust run nightly cargo bench +//! cargo bench //! ``` -#![feature(test)] - -extern crate test; +#[macro_use] +extern crate criterion; extern crate ethcore; +extern crate ethereum_types; extern crate evm; -extern crate ethcore_util; -extern crate ethcore_bigint; extern crate rustc_hex; +extern crate vm; -use self::test::{Bencher, black_box}; +use criterion::{black_box, Criterion}; +use std::sync::Arc; -use evm::run_vm; -use ethcore::vm::ActionParams; -use ethcore_bigint::prelude::U256; +use ethereum_types::U256; +use evm::Factory; use rustc_hex::FromHex; - -#[bench] -fn simple_loop_usize(b: &mut Bencher) { - simple_loop(U256::from(::std::usize::MAX), b) +use vm::{tests::FakeExt, ActionParams, Ext}; + +criterion_group!( + evmbin, + bench_simple_loop_usize, + bench_simple_loop_u256, + bench_rng_usize, + bench_rng_u256 +); +criterion_main!(evmbin); + +fn bench_simple_loop_usize(c: &mut Criterion) { + simple_loop(U256::from(::std::usize::MAX), c, "simple_loop_usize") } -#[bench] -fn simple_loop_u256(b: &mut Bencher) { - simple_loop(!U256::zero(), b) +fn bench_simple_loop_u256(c: &mut Criterion) { + simple_loop(!U256::zero(), c, "simple_loop_u256") } -fn simple_loop(gas: U256, b: &mut Bencher) { - let code = black_box( +fn simple_loop(gas: U256, c: &mut Criterion, bench_id: &str) { + let code = black_box( "606060405260005b620042408112156019575b6001016007565b600081905550600680602b6000396000f3606060405200".from_hex().unwrap() ); - b.iter(|| { - let mut params = ActionParams::default(); - params.gas = gas; - params.code = Some(code.clone()); - - run_vm(params) - }); + c.bench_function(bench_id, move |b| { + b.iter(|| { + let mut params = ActionParams::default(); + params.gas = gas; + params.code = Some(Arc::new(code.clone())); + + let mut ext = FakeExt::new(); + let evm = Factory::default().create(params, ext.schedule(), ext.depth()); + let _ = evm.exec(&mut ext); + }) + }); } -#[bench] -fn rng_usize(b: &mut Bencher) { - rng(U256::from(::std::usize::MAX), b) +fn bench_rng_usize(c: &mut Criterion) { + rng(U256::from(::std::usize::MAX), c, "rng_usize") } -#[bench] -fn rng_u256(b: &mut Bencher) { - rng(!U256::zero(), b) +fn bench_rng_u256(c: &mut Criterion) { + rng(!U256::zero(), c, "rng_u256") } -fn rng(gas: U256, b: &mut Bencher) { - let code = black_box( +fn rng(gas: U256, c: &mut Criterion, bench_id: &str) { + let code = black_box( "6060604052600360056007600b60005b62004240811215607f5767ffe7649d5eca84179490940267f47ed85c4b9a6379019367f8e5dd9a5c994bba9390930267f91d87e4b8b74e55019267ff97f6f3b29cda529290920267f393ada8dd75c938019167fe8d437c45bb3735830267f47d9a7b5428ffec019150600101600f565b838518831882186000555050505050600680609a6000396000f3606060405200".from_hex().unwrap() ); - b.iter(|| { - let mut params = ActionParams::default(); - params.gas = gas; - params.code = Some(code.clone()); - - run_vm(params) - }); + c.bench_function(bench_id, move |b| { + b.iter(|| { + let mut params = ActionParams::default(); + params.gas = gas; + params.code = Some(Arc::new(code.clone())); + + let mut ext = FakeExt::new(); + let evm = Factory::default().create(params, ext.schedule(), ext.depth()); + let _ = evm.exec(&mut ext); + }) + }); } diff --git a/evmbin/src/display/json.rs b/evmbin/src/display/json.rs index eec5131c7b0..f8e3ebae0d6 100644 --- a/evmbin/src/display/json.rs +++ b/evmbin/src/display/json.rs @@ -1,27 +1,26 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! JSON VM output. -use std::collections::HashMap; -use std::mem; +use std::{collections::HashMap, mem}; -use ethereum_types::{U256, H256}; use bytes::ToPretty; use ethcore::trace; +use ethereum_types::{H256, U256}; use display; use info as vm; @@ -29,292 +28,321 @@ use info as vm; /// JSON formatting informant. #[derive(Default)] pub struct Informant { - code: Vec, - depth: usize, - pc: usize, - instruction: u8, - gas_cost: U256, - gas_used: U256, - mem_written: Option<(usize, usize)>, - store_written: Option<(U256, U256)>, - stack: Vec, - memory: Vec, - storage: HashMap, - traces: Vec, - subtraces: Vec, - subinfos: Vec, - subdepth: usize, - unmatched: bool, + code: Vec, + depth: usize, + pc: usize, + instruction: u8, + gas_cost: U256, + gas_used: U256, + mem_written: Option<(usize, usize)>, + store_written: Option<(U256, U256)>, + stack: Vec, + memory: Vec, + storage: HashMap, + traces: Vec, + subtraces: Vec, + subinfos: Vec, + subdepth: usize, + unmatched: bool, } impl Informant { - fn with_informant_in_depth(informant: &mut Informant, depth: usize, f: F) { - if depth == 0 { - f(informant); - } else { - Self::with_informant_in_depth(informant.subinfos.last_mut().expect("prepare/done_trace are not balanced"), depth - 1, f); - } - } - - fn informant_trace(informant: &Informant, gas_used: U256) -> String { - let info = ::evm::Instruction::from_u8(informant.instruction).map(|i| i.info()); - - json!({ - "pc": informant.pc, - "op": informant.instruction, - "opName": info.map(|i| i.name).unwrap_or(""), - "gas": format!("{:#x}", gas_used.saturating_add(informant.gas_cost)), - "gasCost": format!("{:#x}", informant.gas_cost), - "memory": format!("0x{}", informant.memory.to_hex()), - "stack": informant.stack, - "storage": informant.storage, - "depth": informant.depth, - }).to_string() - } + fn with_informant_in_depth( + informant: &mut Informant, + depth: usize, + f: F, + ) { + if depth == 0 { + f(informant); + } else { + Self::with_informant_in_depth( + informant + .subinfos + .last_mut() + .expect("prepare/done_trace are not balanced"), + depth - 1, + f, + ); + } + } + + fn informant_trace(informant: &Informant, gas_used: U256) -> String { + let info = ::evm::Instruction::from_u8(informant.instruction).map(|i| i.info()); + + json!({ + "pc": informant.pc, + "op": informant.instruction, + "opName": info.map(|i| i.name).unwrap_or(""), + "gas": format!("{:#x}", gas_used.saturating_add(informant.gas_cost)), + "gasCost": format!("{:#x}", informant.gas_cost), + "memory": format!("0x{}", informant.memory.to_hex()), + "stack": informant.stack, + "storage": informant.storage, + "depth": informant.depth, + }) + .to_string() + } } impl vm::Informant for Informant { - type Sink = (); - - fn before_test(&mut self, name: &str, action: &str) { - println!("{}", json!({"action": action, "test": name})); - } - - fn set_gas(&mut self, gas: U256) { - self.gas_used = gas; - } - - fn clone_sink(&self) -> Self::Sink { () } - - fn finish(result: vm::RunResult, _sink: &mut Self::Sink) { - match result { - Ok(success) => { - for trace in success.traces.unwrap_or_else(Vec::new) { - println!("{}", trace); - } - - let success_msg = json!({ - "output": format!("0x{}", success.output.to_hex()), - "gasUsed": format!("{:#x}", success.gas_used), - "time": display::as_micros(&success.time), - }); - - println!("{}", success_msg) - }, - Err(failure) => { - for trace in failure.traces.unwrap_or_else(Vec::new) { - println!("{}", trace); - } - - let failure_msg = json!({ - "error": &failure.error.to_string(), - "gasUsed": format!("{:#x}", failure.gas_used), - "time": display::as_micros(&failure.time), - }); - - println!("{}", failure_msg) - }, - } - } + type Sink = (); + + fn before_test(&mut self, name: &str, action: &str) { + println!("{}", json!({"action": action, "test": name})); + } + + fn set_gas(&mut self, gas: U256) { + self.gas_used = gas; + } + + fn clone_sink(&self) -> Self::Sink { + () + } + + fn finish(result: vm::RunResult, _sink: &mut Self::Sink) { + match result { + Ok(success) => { + for trace in success.traces.unwrap_or_else(Vec::new) { + println!("{}", trace); + } + + let success_msg = json!({ + "output": format!("0x{}", success.output.to_hex()), + "gasUsed": format!("{:#x}", success.gas_used), + "time": display::as_micros(&success.time), + }); + + println!("{}", success_msg) + } + Err(failure) => { + for trace in failure.traces.unwrap_or_else(Vec::new) { + println!("{}", trace); + } + + let failure_msg = json!({ + "error": &failure.error.to_string(), + "gasUsed": format!("{:#x}", failure.gas_used), + "time": display::as_micros(&failure.time), + }); + + println!("{}", failure_msg) + } + } + } } impl trace::VMTracer for Informant { - type Output = Vec; - - fn trace_next_instruction(&mut self, pc: usize, instruction: u8, _current_gas: U256) -> bool { - let subdepth = self.subdepth; - Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { - informant.pc = pc; - informant.instruction = instruction; - informant.unmatched = true; - }); - true - } - - fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: U256, mem_written: Option<(usize, usize)>, store_written: Option<(U256, U256)>) { - let subdepth = self.subdepth; - Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { - informant.pc = pc; - informant.instruction = instruction; - informant.gas_cost = gas_cost; - informant.mem_written = mem_written; - informant.store_written = store_written; - }); - } - - fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) { - let subdepth = self.subdepth; - Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { - let store_diff = informant.store_written.clone(); - let info = ::evm::Instruction::from_u8(informant.instruction).map(|i| i.info()); - - let trace = Self::informant_trace(informant, gas_used); - informant.traces.push(trace); - - informant.unmatched = false; - informant.gas_used = gas_used; - - let len = informant.stack.len(); - let info_args = info.map(|i| i.args).unwrap_or(0); - informant.stack.truncate(if len > info_args { len - info_args } else { 0 }); - informant.stack.extend_from_slice(stack_push); - - // TODO [ToDr] Align memory? - if let Some((pos, size)) = informant.mem_written.clone() { - if informant.memory.len() < (pos + size) { - informant.memory.resize(pos + size, 0); - } - informant.memory[pos..(pos + size)].copy_from_slice(&mem[pos..(pos + size)]); - } - - if let Some((pos, val)) = store_diff { - informant.storage.insert(pos.into(), val.into()); - } - - if !informant.subtraces.is_empty() { - informant.traces.extend(mem::replace(&mut informant.subtraces, vec![])); - } - }); - } - - fn prepare_subtrace(&mut self, code: &[u8]) { - let subdepth = self.subdepth; - Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { - let mut vm = Informant::default(); - vm.depth = informant.depth + 1; - vm.code = code.to_vec(); - vm.gas_used = informant.gas_used; - informant.subinfos.push(vm); - }); - self.subdepth += 1; - } - - fn done_subtrace(&mut self) { - self.subdepth -= 1; - let subdepth = self.subdepth; - Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { - if let Some(subtraces) = informant.subinfos.pop().expect("prepare/done_subtrace are not balanced").drain() { - informant.subtraces.extend(subtraces); - } - }); - } - - fn drain(mut self) -> Option { - if self.unmatched { - // print last line with final state: - self.gas_cost = 0.into(); - let gas_used = self.gas_used; - let subdepth = self.subdepth; - - Self::with_informant_in_depth(&mut self, subdepth, |informant: &mut Informant| { - let trace = Self::informant_trace(informant, gas_used); - informant.traces.push(trace); - }); - } else if !self.subtraces.is_empty() { - self.traces.extend(mem::replace(&mut self.subtraces, vec![])); - } - Some(self.traces) - } + type Output = Vec; + + fn trace_next_instruction(&mut self, pc: usize, instruction: u8, _current_gas: U256) -> bool { + let subdepth = self.subdepth; + Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { + informant.pc = pc; + informant.instruction = instruction; + informant.unmatched = true; + }); + true + } + + fn trace_prepare_execute( + &mut self, + pc: usize, + instruction: u8, + gas_cost: U256, + mem_written: Option<(usize, usize)>, + store_written: Option<(U256, U256)>, + ) { + let subdepth = self.subdepth; + Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { + informant.pc = pc; + informant.instruction = instruction; + informant.gas_cost = gas_cost; + informant.mem_written = mem_written; + informant.store_written = store_written; + }); + } + + fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) { + let subdepth = self.subdepth; + Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { + let store_diff = informant.store_written.clone(); + let info = ::evm::Instruction::from_u8(informant.instruction).map(|i| i.info()); + + let trace = Self::informant_trace(informant, gas_used); + informant.traces.push(trace); + + informant.unmatched = false; + informant.gas_used = gas_used; + + let len = informant.stack.len(); + let info_args = info.map(|i| i.args).unwrap_or(0); + informant + .stack + .truncate(if len > info_args { len - info_args } else { 0 }); + informant.stack.extend_from_slice(stack_push); + + // TODO [ToDr] Align memory? + if let Some((pos, size)) = informant.mem_written.clone() { + if informant.memory.len() < (pos + size) { + informant.memory.resize(pos + size, 0); + } + informant.memory[pos..(pos + size)].copy_from_slice(&mem[pos..(pos + size)]); + } + + if let Some((pos, val)) = store_diff { + informant.storage.insert(pos.into(), val.into()); + } + + if !informant.subtraces.is_empty() { + informant + .traces + .extend(mem::replace(&mut informant.subtraces, vec![])); + } + }); + } + + fn prepare_subtrace(&mut self, code: &[u8]) { + let subdepth = self.subdepth; + Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { + let mut vm = Informant::default(); + vm.depth = informant.depth + 1; + vm.code = code.to_vec(); + vm.gas_used = informant.gas_used; + informant.subinfos.push(vm); + }); + self.subdepth += 1; + } + + fn done_subtrace(&mut self) { + self.subdepth -= 1; + let subdepth = self.subdepth; + Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { + if let Some(subtraces) = informant + .subinfos + .pop() + .expect("prepare/done_subtrace are not balanced") + .drain() + { + informant.subtraces.extend(subtraces); + } + }); + } + + fn drain(mut self) -> Option { + if self.unmatched { + // print last line with final state: + self.gas_cost = 0.into(); + let gas_used = self.gas_used; + let subdepth = self.subdepth; + + Self::with_informant_in_depth(&mut self, subdepth, |informant: &mut Informant| { + let trace = Self::informant_trace(informant, gas_used); + informant.traces.push(trace); + }); + } else if !self.subtraces.is_empty() { + self.traces + .extend(mem::replace(&mut self.subtraces, vec![])); + } + Some(self.traces) + } } #[cfg(test)] mod tests { - use super::*; - use info::tests::run_test; - use serde_json; - - #[derive(Serialize, Deserialize, Debug, PartialEq)] - #[serde(rename_all = "camelCase")] - struct TestTrace { - pc: usize, - #[serde(rename = "op")] - instruction: u8, - op_name: String, - #[serde(rename = "gas")] - gas_used: U256, - gas_cost: U256, - memory: String, - stack: Vec, - storage: HashMap, - depth: usize, - } - - fn assert_traces_eq( - a: &[String], - b: &[String], - ) { - let mut ita = a.iter(); - let mut itb = b.iter(); - - loop { - match (ita.next(), itb.next()) { - (Some(a), Some(b)) => { - // Compare both without worrying about the order of the fields - let actual: TestTrace = serde_json::from_str(a).unwrap(); - let expected: TestTrace = serde_json::from_str(b).unwrap(); - assert_eq!(actual, expected); - println!("{}", a); - }, - (None, None) => return, - e => { - panic!("Traces mismatch: {:?}", e); - } - } - } - } - - fn compare_json(traces: Option>, expected: &str) { - let expected = expected.split("\n") - .map(|x| x.trim()) - .map(|x| x.to_owned()) - .filter(|x| !x.is_empty()) - .collect::>(); - assert_traces_eq(&traces.unwrap(), &expected); - } - - #[test] - fn should_trace_failure() { - run_test( - Informant::default(), - &compare_json, - "60F8d6", - 0xffff, - r#" + use super::*; + use info::tests::run_test; + use serde_json; + + #[derive(Serialize, Deserialize, Debug, PartialEq)] + #[serde(rename_all = "camelCase")] + struct TestTrace { + pc: usize, + #[serde(rename = "op")] + instruction: u8, + op_name: String, + #[serde(rename = "gas")] + gas_used: U256, + gas_cost: U256, + memory: String, + stack: Vec, + storage: HashMap, + depth: usize, + } + + fn assert_traces_eq(a: &[String], b: &[String]) { + let mut ita = a.iter(); + let mut itb = b.iter(); + + loop { + match (ita.next(), itb.next()) { + (Some(a), Some(b)) => { + // Compare both without worrying about the order of the fields + let actual: TestTrace = serde_json::from_str(a).unwrap(); + let expected: TestTrace = serde_json::from_str(b).unwrap(); + assert_eq!(actual, expected); + println!("{}", a); + } + (None, None) => return, + e => { + panic!("Traces mismatch: {:?}", e); + } + } + } + } + + fn compare_json(traces: Option>, expected: &str) { + let expected = expected + .split("\n") + .map(|x| x.trim()) + .map(|x| x.to_owned()) + .filter(|x| !x.is_empty()) + .collect::>(); + assert_traces_eq(&traces.unwrap(), &expected); + } + + #[test] + fn should_trace_failure() { + run_test( + Informant::default(), + &compare_json, + "60F8d6", + 0xffff, + r#" {"pc":0,"op":96,"opName":"PUSH1","gas":"0xffff","gasCost":"0x3","memory":"0x","stack":[],"storage":{},"depth":1} {"pc":2,"op":214,"opName":"","gas":"0xfffc","gasCost":"0x0","memory":"0x","stack":["0xf8"],"storage":{},"depth":1} "#, - ); - - run_test( - Informant::default(), - &compare_json, - "F8d6", - 0xffff, - r#" + ); + + run_test( + Informant::default(), + &compare_json, + "F8d6", + 0xffff, + r#" {"pc":0,"op":248,"opName":"","gas":"0xffff","gasCost":"0x0","memory":"0x","stack":[],"storage":{},"depth":1} "#, - ); - - run_test( - Informant::default(), - &compare_json, - "5A51", - 0xfffff, - r#" + ); + + run_test( + Informant::default(), + &compare_json, + "5A51", + 0xfffff, + r#" {"depth":1,"gas":"0xfffff","gasCost":"0x2","memory":"0x","op":90,"opName":"GAS","pc":0,"stack":[],"storage":{}} {"depth":1,"gas":"0xffffd","gasCost":"0x0","memory":"0x","op":81,"opName":"MLOAD","pc":1,"stack":["0xffffd"],"storage":{}} "#, - ); - } - - #[test] - fn should_trace_create_correctly() { - run_test( - Informant::default(), - &compare_json, - "32343434345830f138343438323439f0", - 0xffff, - r#" + ); + } + + #[test] + fn should_trace_create_correctly() { + run_test( + Informant::default(), + &compare_json, + "32343434345830f138343438323439f0", + 0xffff, + r#" {"pc":0,"op":50,"opName":"ORIGIN","gas":"0xffff","gasCost":"0x2","memory":"0x","stack":[],"storage":{},"depth":1} {"pc":1,"op":52,"opName":"CALLVALUE","gas":"0xfffd","gasCost":"0x2","memory":"0x","stack":["0x0"],"storage":{},"depth":1} {"pc":2,"op":52,"opName":"CALLVALUE","gas":"0xfffb","gasCost":"0x2","memory":"0x","stack":["0x0","0x0"],"storage":{},"depth":1} @@ -340,19 +368,19 @@ mod tests { {"pc":6,"op":48,"opName":"ADDRESS","gas":"0x2100","gasCost":"0x2","memory":"0x","stack":["0x0","0x0","0x0","0x0","0x0","0x5"],"storage":{},"depth":2} {"pc":7,"op":241,"opName":"CALL","gas":"0x20fe","gasCost":"0x0","memory":"0x","stack":["0x0","0x0","0x0","0x0","0x0","0x5","0xbd770416a3345f91e4b34576cb804a576fa48eb1"],"storage":{},"depth":2} "#, - ); - - run_test( - Informant::default(), - &compare_json, - "3260D85554", - 0xffff, - r#" + ); + + run_test( + Informant::default(), + &compare_json, + "3260D85554", + 0xffff, + r#" {"pc":0,"op":50,"opName":"ORIGIN","gas":"0xffff","gasCost":"0x2","memory":"0x","stack":[],"storage":{},"depth":1} {"pc":1,"op":96,"opName":"PUSH1","gas":"0xfffd","gasCost":"0x3","memory":"0x","stack":["0x0"],"storage":{},"depth":1} {"pc":3,"op":85,"opName":"SSTORE","gas":"0xfffa","gasCost":"0x1388","memory":"0x","stack":["0x0","0xd8"],"storage":{},"depth":1} {"pc":4,"op":84,"opName":"SLOAD","gas":"0xec72","gasCost":"0x0","memory":"0x","stack":[],"storage":{"0x00000000000000000000000000000000000000000000000000000000000000d8":"0x0000000000000000000000000000000000000000000000000000000000000000"},"depth":1} "#, - ) - } + ) + } } diff --git a/evmbin/src/display/mod.rs b/evmbin/src/display/mod.rs index 32b45c56922..9131f436cf7 100644 --- a/evmbin/src/display/mod.rs +++ b/evmbin/src/display/mod.rs @@ -1,33 +1,33 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! VM Output display utils. use std::time::Duration; pub mod json; -pub mod std_json; pub mod simple; +pub mod std_json; /// Formats duration into human readable format. pub fn format_time(time: &Duration) -> String { - format!("{}.{:.9}s", time.as_secs(), time.subsec_nanos()) + format!("{}.{:.9}s", time.as_secs(), time.subsec_nanos()) } /// Formats the time as microseconds. pub fn as_micros(time: &Duration) -> u64 { - time.as_secs() * 1_000_000 + time.subsec_nanos() as u64 / 1_000 + time.as_secs() * 1_000_000 + time.subsec_nanos() as u64 / 1_000 } diff --git a/evmbin/src/display/simple.rs b/evmbin/src/display/simple.rs index 58d4a704585..264205768ad 100644 --- a/evmbin/src/display/simple.rs +++ b/evmbin/src/display/simple.rs @@ -1,23 +1,23 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Simple VM output. -use ethcore::trace; use bytes::ToPretty; +use ethcore::trace; use display; use info as vm; @@ -27,34 +27,39 @@ use info as vm; pub struct Informant; impl vm::Informant for Informant { + type Sink = (); + + fn before_test(&mut self, name: &str, action: &str) { + println!("Test: {} ({})", name, action); + } + + fn clone_sink(&self) -> Self::Sink { + () + } - type Sink = (); - - fn before_test(&mut self, name: &str, action: &str) { - println!("Test: {} ({})", name, action); - } - - fn clone_sink(&self) -> Self::Sink { () } - - fn finish(result: vm::RunResult, _sink: &mut Self::Sink) { - match result { - Ok(success) => { - println!("Output: 0x{}", success.output.to_hex()); - println!("Gas used: {:x}", success.gas_used); - println!("Time: {}", display::format_time(&success.time)); - }, - Err(failure) => { - println!("Error: {}", failure.error); - println!("Time: {}", display::format_time(&failure.time)); - }, - } - } + fn finish(result: vm::RunResult, _sink: &mut Self::Sink) { + match result { + Ok(success) => { + println!("Output: 0x{}", success.output.to_hex()); + println!("Gas used: {:x}", success.gas_used); + println!("Time: {}", display::format_time(&success.time)); + } + Err(failure) => { + println!("Error: {}", failure.error); + println!("Time: {}", display::format_time(&failure.time)); + } + } + } } impl trace::VMTracer for Informant { - type Output = (); + type Output = (); - fn prepare_subtrace(&mut self, _code: &[u8]) { Default::default() } - fn done_subtrace(&mut self) {} - fn drain(self) -> Option<()> { None } + fn prepare_subtrace(&mut self, _code: &[u8]) { + Default::default() + } + fn done_subtrace(&mut self) {} + fn drain(self) -> Option<()> { + None + } } diff --git a/evmbin/src/display/std_json.rs b/evmbin/src/display/std_json.rs index a0071b174fa..33638aa8ed6 100644 --- a/evmbin/src/display/std_json.rs +++ b/evmbin/src/display/std_json.rs @@ -1,315 +1,343 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Standardized JSON VM output. -use std::collections::HashMap; -use std::io; +use std::{collections::HashMap, io}; -use ethereum_types::{H256, U256}; use bytes::ToPretty; -use ethcore::{trace, pod_state}; +use ethcore::{pod_state, trace}; +use ethereum_types::{H256, U256}; use display; use info as vm; pub trait Writer: io::Write + Send + Sized { - fn clone(&self) -> Self; - fn default() -> Self; + fn clone(&self) -> Self; + fn default() -> Self; } impl Writer for io::Stdout { - fn clone(&self) -> Self { - io::stdout() - } + fn clone(&self) -> Self { + io::stdout() + } - fn default() -> Self { - io::stdout() - } + fn default() -> Self { + io::stdout() + } } impl Writer for io::Stderr { - fn clone(&self) -> Self { - io::stderr() - } + fn clone(&self) -> Self { + io::stderr() + } - fn default() -> Self { - io::stderr() - } + fn default() -> Self { + io::stderr() + } } /// JSON formatting informant. pub struct Informant { - code: Vec, - instruction: u8, - depth: usize, - stack: Vec, - storage: HashMap, - subinfos: Vec>, - subdepth: usize, - trace_sink: Trace, - out_sink: Out, + code: Vec, + instruction: u8, + depth: usize, + stack: Vec, + storage: HashMap, + subinfos: Vec>, + subdepth: usize, + trace_sink: Trace, + out_sink: Out, } impl Default for Informant { - fn default() -> Self { - Self::new(io::stderr(), io::stdout()) - } + fn default() -> Self { + Self::new(io::stderr(), io::stdout()) + } } impl Informant { - /// std json informant using out only. - pub fn out_only() -> Self { - Self::new(io::stdout(), io::stdout()) - } + /// std json informant using out only. + pub fn out_only() -> Self { + Self::new(io::stdout(), io::stdout()) + } } impl Informant { - /// std json informant using err only. - pub fn err_only() -> Self { - Self::new(io::stderr(), io::stderr()) - } + /// std json informant using err only. + pub fn err_only() -> Self { + Self::new(io::stderr(), io::stderr()) + } } impl Informant { - - pub fn new(trace_sink: Trace, out_sink: Out) -> Self { - Informant { - code: Default::default(), - instruction: Default::default(), - depth: Default::default(), - stack: Default::default(), - storage: Default::default(), - subinfos: Default::default(), - subdepth: 0, - trace_sink, out_sink - } - } - - fn with_informant_in_depth)>(informant: &mut Informant, depth: usize, f: F) { - if depth == 0 { - f(informant); - } else { - Self::with_informant_in_depth(informant.subinfos.last_mut().expect("prepare/done_trace are not balanced"), depth - 1, f); - } - } - - fn dump_state_into(trace_sink: &mut Trace, root: H256, end_state: &Option) { - if let Some(ref end_state) = end_state { - let dump_data = json!({ - "root": root, - "accounts": end_state, - }); - writeln!(trace_sink, "{}", dump_data).expect("The sink must be writeable."); - } - } - + pub fn new(trace_sink: Trace, out_sink: Out) -> Self { + Informant { + code: Default::default(), + instruction: Default::default(), + depth: Default::default(), + stack: Default::default(), + storage: Default::default(), + subinfos: Default::default(), + subdepth: 0, + trace_sink, + out_sink, + } + } + + fn with_informant_in_depth)>( + informant: &mut Informant, + depth: usize, + f: F, + ) { + if depth == 0 { + f(informant); + } else { + Self::with_informant_in_depth( + informant + .subinfos + .last_mut() + .expect("prepare/done_trace are not balanced"), + depth - 1, + f, + ); + } + } + + fn dump_state_into( + trace_sink: &mut Trace, + root: H256, + end_state: &Option, + ) { + if let Some(ref end_state) = end_state { + let dump_data = json!({ + "root": root, + "accounts": end_state, + }); + writeln!(trace_sink, "{}", dump_data).expect("The sink must be writeable."); + } + } } impl vm::Informant for Informant { - - type Sink = (Trace, Out); - - fn before_test(&mut self, name: &str, action: &str) { - let out_data = json!({ - "action": action, - "test": name, - }); - - writeln!(&mut self.out_sink, "{}", out_data).expect("The sink must be writeable."); - } - - fn set_gas(&mut self, _gas: U256) {} - - fn clone_sink(&self) -> Self::Sink { - (self.trace_sink.clone(), self.out_sink.clone()) - } - fn finish(result: vm::RunResult<::Output>, (ref mut trace_sink, ref mut out_sink): &mut Self::Sink) { - - match result { - Ok(success) => { - let trace_data = json!({"stateRoot": success.state_root}); - writeln!(trace_sink, "{}", trace_data) - .expect("The sink must be writeable."); - - Self::dump_state_into(trace_sink, success.state_root, &success.end_state); - - let out_data = json!({ - "output": format!("0x{}", success.output.to_hex()), - "gasUsed": format!("{:#x}", success.gas_used), - "time": display::as_micros(&success.time), - }); - - writeln!(out_sink, "{}", out_data).expect("The sink must be writeable."); - }, - Err(failure) => { - let out_data = json!({ - "error": &failure.error.to_string(), - "gasUsed": format!("{:#x}", failure.gas_used), - "time": display::as_micros(&failure.time), - }); - - Self::dump_state_into(trace_sink, failure.state_root, &failure.end_state); - - writeln!(out_sink, "{}", out_data).expect("The sink must be writeable."); - }, - } - } + type Sink = (Trace, Out); + + fn before_test(&mut self, name: &str, action: &str) { + let out_data = json!({ + "action": action, + "test": name, + }); + + writeln!(&mut self.out_sink, "{}", out_data).expect("The sink must be writeable."); + } + + fn set_gas(&mut self, _gas: U256) {} + + fn clone_sink(&self) -> Self::Sink { + (self.trace_sink.clone(), self.out_sink.clone()) + } + fn finish( + result: vm::RunResult<::Output>, + (ref mut trace_sink, ref mut out_sink): &mut Self::Sink, + ) { + match result { + Ok(success) => { + let trace_data = json!({"stateRoot": success.state_root}); + writeln!(trace_sink, "{}", trace_data).expect("The sink must be writeable."); + + Self::dump_state_into(trace_sink, success.state_root, &success.end_state); + + let out_data = json!({ + "output": format!("0x{}", success.output.to_hex()), + "gasUsed": format!("{:#x}", success.gas_used), + "time": display::as_micros(&success.time), + }); + + writeln!(out_sink, "{}", out_data).expect("The sink must be writeable."); + } + Err(failure) => { + let out_data = json!({ + "error": &failure.error.to_string(), + "gasUsed": format!("{:#x}", failure.gas_used), + "time": display::as_micros(&failure.time), + }); + + Self::dump_state_into(trace_sink, failure.state_root, &failure.end_state); + + writeln!(out_sink, "{}", out_data).expect("The sink must be writeable."); + } + } + } } impl trace::VMTracer for Informant { - type Output = (); - - fn trace_next_instruction(&mut self, pc: usize, instruction: u8, current_gas: U256) -> bool { - let subdepth = self.subdepth; - Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { - let info = ::evm::Instruction::from_u8(instruction).map(|i| i.info()); - informant.instruction = instruction; - let trace_data = json!({ - "pc": pc, - "op": instruction, - "opName": info.map(|i| i.name).unwrap_or(""), - "gas": format!("{:#x}", current_gas), - "stack": informant.stack, - "storage": informant.storage, - "depth": informant.depth, - }); - - writeln!(&mut informant.trace_sink, "{}", trace_data).expect("The sink must be writeable."); - }); - true - } - - fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256, _mem_written: Option<(usize, usize)>, store_written: Option<(U256, U256)>) { - let subdepth = self.subdepth; - Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { - if let Some((pos, val)) = store_written { - informant.storage.insert(pos.into(), val.into()); - } - }); - } - - fn trace_executed(&mut self, _gas_used: U256, stack_push: &[U256], _mem: &[u8]) { - let subdepth = self.subdepth; - Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { - let info = ::evm::Instruction::from_u8(informant.instruction).map(|i| i.info()); - - let len = informant.stack.len(); - let info_args = info.map(|i| i.args).unwrap_or(0); - informant.stack.truncate(if len > info_args { len - info_args } else { 0 }); - informant.stack.extend_from_slice(stack_push); - }); - } - - fn prepare_subtrace(&mut self, code: &[u8]) { - let subdepth = self.subdepth; - Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { - let mut vm = Informant::new(informant.trace_sink.clone(), informant.out_sink.clone()); - vm.depth = informant.depth + 1; - vm.code = code.to_vec(); - informant.subinfos.push(vm); - }); - self.subdepth += 1; - } - - fn done_subtrace(&mut self) { - self.subdepth -= 1; - let subdepth = self.subdepth; - Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { - informant.subinfos.pop(); - }); - } - - fn drain(self) -> Option { None } - + type Output = (); + + fn trace_next_instruction(&mut self, pc: usize, instruction: u8, current_gas: U256) -> bool { + let subdepth = self.subdepth; + Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { + let info = ::evm::Instruction::from_u8(instruction).map(|i| i.info()); + informant.instruction = instruction; + let trace_data = json!({ + "pc": pc, + "op": instruction, + "opName": info.map(|i| i.name).unwrap_or(""), + "gas": format!("{:#x}", current_gas), + "stack": informant.stack, + "storage": informant.storage, + "depth": informant.depth, + }); + + writeln!(&mut informant.trace_sink, "{}", trace_data) + .expect("The sink must be writeable."); + }); + true + } + + fn trace_prepare_execute( + &mut self, + _pc: usize, + _instruction: u8, + _gas_cost: U256, + _mem_written: Option<(usize, usize)>, + store_written: Option<(U256, U256)>, + ) { + let subdepth = self.subdepth; + Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { + if let Some((pos, val)) = store_written { + informant.storage.insert(pos.into(), val.into()); + } + }); + } + + fn trace_executed(&mut self, _gas_used: U256, stack_push: &[U256], _mem: &[u8]) { + let subdepth = self.subdepth; + Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { + let info = ::evm::Instruction::from_u8(informant.instruction).map(|i| i.info()); + + let len = informant.stack.len(); + let info_args = info.map(|i| i.args).unwrap_or(0); + informant + .stack + .truncate(if len > info_args { len - info_args } else { 0 }); + informant.stack.extend_from_slice(stack_push); + }); + } + + fn prepare_subtrace(&mut self, code: &[u8]) { + let subdepth = self.subdepth; + Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { + let mut vm = Informant::new(informant.trace_sink.clone(), informant.out_sink.clone()); + vm.depth = informant.depth + 1; + vm.code = code.to_vec(); + informant.subinfos.push(vm); + }); + self.subdepth += 1; + } + + fn done_subtrace(&mut self) { + self.subdepth -= 1; + let subdepth = self.subdepth; + Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { + informant.subinfos.pop(); + }); + } + + fn drain(self) -> Option { + None + } } #[cfg(test)] pub mod tests { - use std::sync::{Arc, Mutex}; - use super::*; - use info::tests::run_test; - - #[derive(Debug, Clone, Default)] - pub struct TestWriter(pub Arc>>); - - impl Writer for TestWriter { - fn clone(&self) -> Self { Clone::clone(self) } - fn default() -> Self { Default::default() } - } - - impl io::Write for TestWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.lock().unwrap().write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.0.lock().unwrap().flush() - } - } - - pub fn informant() -> (Informant, Arc>>) { - let trace_writer: TestWriter = Default::default(); - let out_writer: TestWriter = Default::default(); - let res = trace_writer.0.clone(); - (Informant::new(trace_writer, out_writer), res) - } - - #[test] - fn should_trace_failure() { - let (inf, res) = informant(); - run_test( - inf, - move |_, expected| { - let bytes = res.lock().unwrap(); - assert_eq!(expected, &String::from_utf8_lossy(&**bytes)) - }, - "60F8d6", - 0xffff, - r#"{"depth":1,"gas":"0xffff","op":96,"opName":"PUSH1","pc":0,"stack":[],"storage":{}} + use super::*; + use info::tests::run_test; + use std::sync::{Arc, Mutex}; + + #[derive(Debug, Clone, Default)] + pub struct TestWriter(pub Arc>>); + + impl Writer for TestWriter { + fn clone(&self) -> Self { + Clone::clone(self) + } + fn default() -> Self { + Default::default() + } + } + + impl io::Write for TestWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.lock().unwrap().write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.0.lock().unwrap().flush() + } + } + + pub fn informant() -> (Informant, Arc>>) { + let trace_writer: TestWriter = Default::default(); + let out_writer: TestWriter = Default::default(); + let res = trace_writer.0.clone(); + (Informant::new(trace_writer, out_writer), res) + } + + #[test] + fn should_trace_failure() { + let (inf, res) = informant(); + run_test( + inf, + move |_, expected| { + let bytes = res.lock().unwrap(); + assert_eq!(expected, &String::from_utf8_lossy(&**bytes)) + }, + "60F8d6", + 0xffff, + r#"{"depth":1,"gas":"0xffff","op":96,"opName":"PUSH1","pc":0,"stack":[],"storage":{}} {"depth":1,"gas":"0xfffc","op":214,"opName":"","pc":2,"stack":["0xf8"],"storage":{}} "#, - ); - - let (inf, res) = informant(); - run_test( - inf, - move |_, expected| { - let bytes = res.lock().unwrap(); - assert_eq!(expected, &String::from_utf8_lossy(&**bytes)) - }, - "F8d6", - 0xffff, - r#"{"depth":1,"gas":"0xffff","op":248,"opName":"","pc":0,"stack":[],"storage":{}} + ); + + let (inf, res) = informant(); + run_test( + inf, + move |_, expected| { + let bytes = res.lock().unwrap(); + assert_eq!(expected, &String::from_utf8_lossy(&**bytes)) + }, + "F8d6", + 0xffff, + r#"{"depth":1,"gas":"0xffff","op":248,"opName":"","pc":0,"stack":[],"storage":{}} "#, - ); - } - - #[test] - fn should_trace_create_correctly() { - let (informant, res) = informant(); - run_test( - informant, - move |_, expected| { - let bytes = res.lock().unwrap(); - assert_eq!(expected, &String::from_utf8_lossy(&**bytes)) - }, - "32343434345830f138343438323439f0", - 0xffff, - r#"{"depth":1,"gas":"0xffff","op":50,"opName":"ORIGIN","pc":0,"stack":[],"storage":{}} + ); + } + + #[test] + fn should_trace_create_correctly() { + let (informant, res) = informant(); + run_test( + informant, + move |_, expected| { + let bytes = res.lock().unwrap(); + assert_eq!(expected, &String::from_utf8_lossy(&**bytes)) + }, + "32343434345830f138343438323439f0", + 0xffff, + r#"{"depth":1,"gas":"0xffff","op":50,"opName":"ORIGIN","pc":0,"stack":[],"storage":{}} {"depth":1,"gas":"0xfffd","op":52,"opName":"CALLVALUE","pc":1,"stack":["0x0"],"storage":{}} {"depth":1,"gas":"0xfffb","op":52,"opName":"CALLVALUE","pc":2,"stack":["0x0","0x0"],"storage":{}} {"depth":1,"gas":"0xfff9","op":52,"opName":"CALLVALUE","pc":3,"stack":["0x0","0x0","0x0"],"storage":{}} @@ -334,6 +362,6 @@ pub mod tests { {"depth":2,"gas":"0x2100","op":48,"opName":"ADDRESS","pc":6,"stack":["0x0","0x0","0x0","0x0","0x0","0x5"],"storage":{}} {"depth":2,"gas":"0x20fe","op":241,"opName":"CALL","pc":7,"stack":["0x0","0x0","0x0","0x0","0x0","0x5","0xbd770416a3345f91e4b34576cb804a576fa48eb1"],"storage":{}} "#, - ) - } + ) + } } diff --git a/evmbin/src/info.rs b/evmbin/src/info.rs index 74ea3175a68..d2b0b6bc78e 100644 --- a/evmbin/src/info.rs +++ b/evmbin/src/info.rs @@ -1,75 +1,77 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! VM runner. -use std::time::{Instant, Duration}; +use ethcore::{ + client::{self, EvmTestClient, EvmTestError, TransactErr, TransactSuccess}, + pod_state, spec, state, state_db, trace, TrieSpec, +}; use ethereum_types::{H256, U256}; -use ethcore::client::{self, EvmTestClient, EvmTestError, TransactErr, TransactSuccess}; -use ethcore::{state, state_db, trace, spec, pod_state, TrieSpec}; use ethjson; +use std::time::{Duration, Instant}; use types::transaction; use vm::ActionParams; /// VM execution informant pub trait Informant: trace::VMTracer { - /// Sink to use with finish - type Sink; - /// Display a single run init message - fn before_test(&mut self, test: &str, action: &str); - /// Set initial gas. - fn set_gas(&mut self, _gas: U256) {} - /// Clone sink. - fn clone_sink(&self) -> Self::Sink; - /// Display final result. - fn finish(result: RunResult, &mut Self::Sink); + /// Sink to use with finish + type Sink; + /// Display a single run init message + fn before_test(&mut self, test: &str, action: &str); + /// Set initial gas. + fn set_gas(&mut self, _gas: U256) {} + /// Clone sink. + fn clone_sink(&self) -> Self::Sink; + /// Display final result. + fn finish(result: RunResult, &mut Self::Sink); } /// Execution finished correctly #[derive(Debug)] pub struct Success { - /// State root - pub state_root: H256, - /// Used gas - pub gas_used: U256, - /// Output as bytes - pub output: Vec, - /// Time Taken - pub time: Duration, - /// Traces - pub traces: Option, - /// Optional end state dump - pub end_state: Option, + /// State root + pub state_root: H256, + /// Used gas + pub gas_used: U256, + /// Output as bytes + pub output: Vec, + /// Time Taken + pub time: Duration, + /// Traces + pub traces: Option, + /// Optional end state dump + pub end_state: Option, } /// Execution failed #[derive(Debug)] pub struct Failure { - /// State root - pub state_root: H256, - /// Used gas - pub gas_used: U256, - /// Internal error - pub error: EvmTestError, - /// Duration - pub time: Duration, - /// Traces - pub traces: Option, - /// Optional end state dump - pub end_state: Option, + /// State root + pub state_root: H256, + /// Used gas + pub gas_used: U256, + /// Internal error + pub error: EvmTestError, + /// Duration + pub time: Duration, + /// Traces + pub traces: Option, + /// Optional end state dump + pub end_state: Option, } /// EVM Execution result @@ -77,185 +79,227 @@ pub type RunResult = Result, Failure>; /// Execute given `ActionParams` and return the result. pub fn run_action( - spec: &spec::Spec, - mut params: ActionParams, - mut informant: T, - trie_spec: TrieSpec, + spec: &spec::Spec, + mut params: ActionParams, + mut informant: T, + trie_spec: TrieSpec, ) -> RunResult { - informant.set_gas(params.gas); + informant.set_gas(params.gas); - // if the code is not overwritten from CLI, use code from spec file. - if params.code.is_none() { - if let Some(acc) = spec.genesis_state().get().get(¶ms.code_address) { - params.code = acc.code.clone().map(::std::sync::Arc::new); - params.code_hash = None; - } - } - run(spec, trie_spec, params.gas, spec.genesis_state(), |mut client| { - let result = match client.call(params, &mut trace::NoopTracer, &mut informant) { - Ok(r) => (Ok(r.return_data.to_vec()), Some(r.gas_left)), - Err(err) => (Err(err), None), - }; - (result.0, 0.into(), None, result.1, informant.drain()) - }) + // if the code is not overwritten from CLI, use code from spec file. + if params.code.is_none() { + if let Some(acc) = spec.genesis_state().get().get(¶ms.code_address) { + params.code = acc.code.clone().map(::std::sync::Arc::new); + params.code_hash = None; + } + } + run( + spec, + trie_spec, + params.gas, + spec.genesis_state(), + |mut client| { + let result = match client.call(params, &mut trace::NoopTracer, &mut informant) { + Ok(r) => (Ok(r.return_data.to_vec()), Some(r.gas_left)), + Err(err) => (Err(err), None), + }; + (result.0, 0.into(), None, result.1, informant.drain()) + }, + ) } /// Execute given Transaction and verify resulting state root. pub fn run_transaction( - name: &str, - idx: usize, - spec: ðjson::spec::ForkSpec, - pre_state: &pod_state::PodState, - post_root: H256, - env_info: &client::EnvInfo, - transaction: transaction::SignedTransaction, - mut informant: T, - trie_spec: TrieSpec, + name: &str, + idx: usize, + spec: ðjson::spec::ForkSpec, + pre_state: &pod_state::PodState, + post_root: H256, + env_info: &client::EnvInfo, + transaction: transaction::SignedTransaction, + mut informant: T, + trie_spec: TrieSpec, ) { - let spec_name = format!("{:?}", spec).to_lowercase(); - let spec = match EvmTestClient::spec_from_json(spec) { - Some(spec) => { - informant.before_test(&format!("{}:{}:{}", name, spec_name, idx), "starting"); - spec - }, - None => { - informant.before_test(&format!("{}:{}:{}", name, spec_name, idx), "skipping because of missing spec"); - return; - }, - }; + let spec_name = format!("{:?}", spec).to_lowercase(); + let spec = match EvmTestClient::spec_from_json(spec) { + Some(spec) => { + informant.before_test(&format!("{}:{}:{}", name, spec_name, idx), "starting"); + spec + } + None => { + informant.before_test( + &format!("{}:{}:{}", name, spec_name, idx), + "skipping because of missing spec", + ); + return; + } + }; - informant.set_gas(env_info.gas_limit); + informant.set_gas(env_info.gas_limit); - let mut sink = informant.clone_sink(); - let result = run(&spec, trie_spec, transaction.gas, pre_state, |mut client| { - let result = client.transact(env_info, transaction, trace::NoopTracer, informant); - match result { - Ok(TransactSuccess { state_root, gas_left, output, vm_trace, end_state, .. }) => { - if state_root != post_root { - (Err(EvmTestError::PostCondition(format!( - "State root mismatch (got: {:#x}, expected: {:#x})", - state_root, - post_root, - ))), state_root, end_state, Some(gas_left), None) - } else { - (Ok(output), state_root, end_state, Some(gas_left), vm_trace) - } - }, - Err(TransactErr { state_root, error, end_state }) => { - (Err(EvmTestError::PostCondition(format!( - "Unexpected execution error: {:?}", error - ))), state_root, end_state, None, None) - }, - } - }); + let mut sink = informant.clone_sink(); + let result = run( + &spec, + trie_spec, + transaction.gas, + pre_state, + |mut client| { + let result = client.transact(env_info, transaction, trace::NoopTracer, informant); + match result { + Ok(TransactSuccess { + state_root, + gas_left, + output, + vm_trace, + end_state, + .. + }) => { + if state_root != post_root { + ( + Err(EvmTestError::PostCondition(format!( + "State root mismatch (got: {:#x}, expected: {:#x})", + state_root, post_root, + ))), + state_root, + end_state, + Some(gas_left), + None, + ) + } else { + (Ok(output), state_root, end_state, Some(gas_left), vm_trace) + } + } + Err(TransactErr { + state_root, + error, + end_state, + }) => ( + Err(EvmTestError::PostCondition(format!( + "Unexpected execution error: {:?}", + error + ))), + state_root, + end_state, + None, + None, + ), + } + }, + ); - T::finish(result, &mut sink) + T::finish(result, &mut sink) } fn dump_state(state: &state::State) -> Option { - state.to_pod_full().ok() + state.to_pod_full().ok() } /// Execute VM with given `ActionParams` pub fn run<'a, F, X>( - spec: &'a spec::Spec, - trie_spec: TrieSpec, - initial_gas: U256, - pre_state: &'a pod_state::PodState, - run: F, -) -> RunResult where - F: FnOnce(EvmTestClient) -> (Result, EvmTestError>, H256, Option, Option, Option), + spec: &'a spec::Spec, + trie_spec: TrieSpec, + initial_gas: U256, + pre_state: &'a pod_state::PodState, + run: F, +) -> RunResult +where + F: FnOnce( + EvmTestClient, + ) -> ( + Result, EvmTestError>, + H256, + Option, + Option, + Option, + ), { - let do_dump = trie_spec == TrieSpec::Fat; + let do_dump = trie_spec == TrieSpec::Fat; - let mut test_client = EvmTestClient::from_pod_state_with_trie(spec, pre_state.clone(), trie_spec) - .map_err(|error| Failure { - gas_used: 0.into(), - error, - time: Duration::from_secs(0), - traces: None, - state_root: H256::default(), - end_state: None, - })?; + let mut test_client = + EvmTestClient::from_pod_state_with_trie(spec, pre_state.clone(), trie_spec).map_err( + |error| Failure { + gas_used: 0.into(), + error, + time: Duration::from_secs(0), + traces: None, + state_root: H256::default(), + end_state: None, + }, + )?; - if do_dump { - test_client.set_dump_state_fn(dump_state); - } + if do_dump { + test_client.set_dump_state_fn(dump_state); + } - let start = Instant::now(); - let result = run(test_client); - let time = start.elapsed(); + let start = Instant::now(); + let result = run(test_client); + let time = start.elapsed(); - match result { - (Ok(output), state_root, end_state, gas_left, traces) => Ok(Success { - state_root, - gas_used: gas_left.map(|gas_left| initial_gas - gas_left).unwrap_or(initial_gas), - output, - time, - traces, - end_state, - }), - (Err(error), state_root, end_state, gas_left, traces) => Err(Failure { - gas_used: gas_left.map(|gas_left| initial_gas - gas_left).unwrap_or(initial_gas), - error, - time, - traces, - state_root, - end_state, - }), - } + match result { + (Ok(output), state_root, end_state, gas_left, traces) => Ok(Success { + state_root, + gas_used: gas_left + .map(|gas_left| initial_gas - gas_left) + .unwrap_or(initial_gas), + output, + time, + traces, + end_state, + }), + (Err(error), state_root, end_state, gas_left, traces) => Err(Failure { + gas_used: gas_left + .map(|gas_left| initial_gas - gas_left) + .unwrap_or(initial_gas), + error, + time, + traces, + state_root, + end_state, + }), + } } #[cfg(test)] pub mod tests { - use std::sync::Arc; - use rustc_hex::FromHex; - use super::*; - use tempdir::TempDir; + use super::*; + use rustc_hex::FromHex; + use std::sync::Arc; + use tempdir::TempDir; - pub fn run_test( - informant: I, - compare: F, - code: &str, - gas: T, - expected: &str, - ) where - T: Into, - I: Informant, - F: FnOnce(Option, &str), - { - let mut params = ActionParams::default(); - params.code = Some(Arc::new(code.from_hex().unwrap())); - params.gas = gas.into(); + pub fn run_test(informant: I, compare: F, code: &str, gas: T, expected: &str) + where + T: Into, + I: Informant, + F: FnOnce(Option, &str), + { + let mut params = ActionParams::default(); + params.code = Some(Arc::new(code.from_hex().unwrap())); + params.gas = gas.into(); - let tempdir = TempDir::new("").unwrap(); - let spec = ::ethcore::ethereum::new_foundation(&tempdir.path()); - let result = run_action(&spec, params, informant, TrieSpec::Secure); - match result { - Ok(Success { traces, .. }) => { - compare(traces, expected) - }, - Err(Failure { traces, .. }) => { - compare(traces, expected) - }, - } - } + let tempdir = TempDir::new("").unwrap(); + let spec = ::ethcore::ethereum::new_foundation(&tempdir.path()); + let result = run_action(&spec, params, informant, TrieSpec::Secure); + match result { + Ok(Success { traces, .. }) => compare(traces, expected), + Err(Failure { traces, .. }) => compare(traces, expected), + } + } - #[test] - fn should_call_account_from_spec() { - use display::std_json::tests::informant; + #[test] + fn should_call_account_from_spec() { + use display::std_json::tests::informant; - let (inf, res) = informant(); - let mut params = ActionParams::default(); - params.code_address = 0x20.into(); - params.gas = 0xffff.into(); + let (inf, res) = informant(); + let mut params = ActionParams::default(); + params.code_address = 0x20.into(); + params.gas = 0xffff.into(); - let spec = ::ethcore::ethereum::load(None, include_bytes!("../res/testchain.json")); - let _result = run_action(&spec, params, inf, TrieSpec::Secure); + let spec = ::ethcore::ethereum::load(None, include_bytes!("../res/testchain.json")); + let _result = run_action(&spec, params, inf, TrieSpec::Secure); - assert_eq!( - &String::from_utf8_lossy(&**res.lock().unwrap()), -r#"{"depth":1,"gas":"0xffff","op":98,"opName":"PUSH3","pc":0,"stack":[],"storage":{}} + assert_eq!( + &String::from_utf8_lossy(&**res.lock().unwrap()), + r#"{"depth":1,"gas":"0xffff","op":98,"opName":"PUSH3","pc":0,"stack":[],"storage":{}} {"depth":1,"gas":"0xfffc","op":96,"opName":"PUSH1","pc":4,"stack":["0xaaaaaa"],"storage":{}} {"depth":1,"gas":"0xfff9","op":96,"opName":"PUSH1","pc":6,"stack":["0xaaaaaa","0xaa"],"storage":{}} {"depth":1,"gas":"0xfff6","op":80,"opName":"POP","pc":8,"stack":["0xaaaaaa","0xaa","0xaa"],"storage":{}} @@ -265,6 +309,7 @@ r#"{"depth":1,"gas":"0xffff","op":98,"opName":"PUSH3","pc":0,"stack":[],"storage {"depth":1,"gas":"0xffeb","op":96,"opName":"PUSH1","pc":15,"stack":["0xaaaaaa","0xaa","0xaa","0xaa","0xaa"],"storage":{}} {"depth":1,"gas":"0xffe8","op":96,"opName":"PUSH1","pc":17,"stack":["0xaaaaaa","0xaa","0xaa","0xaa","0xaa","0xaa"],"storage":{}} {"depth":1,"gas":"0xffe5","op":96,"opName":"PUSH1","pc":19,"stack":["0xaaaaaa","0xaa","0xaa","0xaa","0xaa","0xaa","0xaa"],"storage":{}} -"#); - } +"# + ); + } } diff --git a/evmbin/src/main.rs b/evmbin/src/main.rs index 6e1260189e1..7853788c71e 100644 --- a/evmbin/src/main.rs +++ b/evmbin/src/main.rs @@ -1,20 +1,20 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -//! Parity EVM interpreter binary. +//! OpenEthereum EVM interpreter binary. #![warn(missing_docs)] @@ -28,12 +28,12 @@ extern crate serde_derive; #[macro_use] extern crate serde_json; extern crate docopt; -extern crate parity_bytes as bytes; +extern crate env_logger; extern crate ethereum_types; -extern crate vm; extern crate evm; extern crate panic_hook; -extern crate env_logger; +extern crate parity_bytes as bytes; +extern crate vm; #[cfg(test)] #[macro_use] @@ -42,18 +42,16 @@ extern crate pretty_assertions; #[cfg(test)] extern crate tempdir; -use std::sync::Arc; -use std::{fmt, fs}; -use std::path::PathBuf; +use bytes::Bytes; use docopt::Docopt; +use ethcore::{json_tests, spec, TrieSpec}; +use ethereum_types::{Address, U256}; use rustc_hex::FromHex; -use ethereum_types::{U256, Address}; -use bytes::Bytes; -use ethcore::{spec, json_tests, TrieSpec}; +use std::{fmt, fs, path::PathBuf, sync::Arc}; use vm::{ActionParams, CallType}; -mod info; mod display; +mod info; use info::Informant; @@ -62,11 +60,11 @@ EVM implementation for Parity. Copyright 2015-2019 Parity Technologies (UK) Ltd. Usage: - parity-evm state-test [--json --std-json --std-dump-json --only NAME --chain CHAIN --std-out-only --std-err-only] - parity-evm stats [options] - parity-evm stats-jsontests-vm - parity-evm [options] - parity-evm [-h | --help] + openethereum-evm state-test [--json --std-json --std-dump-json --only NAME --chain CHAIN --std-out-only --std-err-only] + openethereum-evm stats [options] + openethereum-evm stats-jsontests-vm + openethereum-evm [options] + openethereum-evm [-h | --help] Commands: state-test Run a state test from a json file. @@ -83,8 +81,11 @@ Transaction options: --gas-price WEI Supplied gas price as hex (without 0x). State test options: + --chain CHAIN Run only from specific chain name (i.e. one of EIP150, EIP158, + Frontier, Homestead, Byzantium, Constantinople, + ConstantinopleFix, Istanbul, EIP158ToByzantiumAt5, FrontierToHomesteadAt5, + HomesteadToDaoAt5, HomesteadToEIP150At5). --only NAME Runs only a single test matching the name. - --chain CHAIN Run only tests from specific chain. General options: --json Display verbose results in JSON. @@ -99,301 +100,376 @@ Display result state dump in standardized JSON format. "#; fn main() { - panic_hook::set_abort(); - env_logger::init(); - - let args: Args = Docopt::new(USAGE).and_then(|d| d.deserialize()).unwrap_or_else(|e| e.exit()); - - if args.cmd_state_test { - run_state_test(args) - } else if args.cmd_stats_jsontests_vm { - run_stats_jsontests_vm(args) - } else if args.flag_json { - run_call(args, display::json::Informant::default()) - } else if args.flag_std_dump_json || args.flag_std_json { - if args.flag_std_err_only { - run_call(args, display::std_json::Informant::err_only()) - } else if args.flag_std_out_only { - run_call(args, display::std_json::Informant::out_only()) - } else { - run_call(args, display::std_json::Informant::default()) - }; - } else { - run_call(args, display::simple::Informant::default()) - } + panic_hook::set_abort(); + env_logger::init(); + + let args: Args = Docopt::new(USAGE) + .and_then(|d| d.deserialize()) + .unwrap_or_else(|e| e.exit()); + + if args.cmd_state_test { + run_state_test(args) + } else if args.cmd_stats_jsontests_vm { + run_stats_jsontests_vm(args) + } else if args.flag_json { + run_call(args, display::json::Informant::default()) + } else if args.flag_std_dump_json || args.flag_std_json { + if args.flag_std_err_only { + run_call(args, display::std_json::Informant::err_only()) + } else if args.flag_std_out_only { + run_call(args, display::std_json::Informant::out_only()) + } else { + run_call(args, display::std_json::Informant::default()) + }; + } else { + run_call(args, display::simple::Informant::default()) + } } fn run_stats_jsontests_vm(args: Args) { - use json_tests::HookType; - use std::collections::HashMap; - use std::time::{Instant, Duration}; - - let file = args.arg_file.expect("FILE (or PATH) is required"); - - let mut timings: HashMap)> = HashMap::new(); - - { - let mut record_time = |name: &str, typ: HookType| { - match typ { - HookType::OnStart => { - timings.insert(name.to_string(), (Instant::now(), None)); - }, - HookType::OnStop => { - timings.entry(name.to_string()).and_modify(|v| { - v.1 = Some(v.0.elapsed()); - }); - }, - } - }; - if !file.is_file() { - json_tests::run_executive_test_path(&file, &[], &mut record_time); - } else { - json_tests::run_executive_test_file(&file, &mut record_time); - } - } - - for (name, v) in timings { - println!("{}\t{}", name, display::as_micros(&v.1.expect("All hooks are called with OnStop; qed"))); - } + use json_tests::HookType; + use std::{ + collections::HashMap, + time::{Duration, Instant}, + }; + + let file = args.arg_file.expect("FILE (or PATH) is required"); + + let mut timings: HashMap)> = HashMap::new(); + + { + let mut record_time = |name: &str, typ: HookType| match typ { + HookType::OnStart => { + timings.insert(name.to_string(), (Instant::now(), None)); + } + HookType::OnStop => { + timings.entry(name.to_string()).and_modify(|v| { + v.1 = Some(v.0.elapsed()); + }); + } + }; + for file_path in json_tests::find_json_files_recursive(&file) { + let json_data = std::fs::read(&file_path).unwrap(); + json_tests::json_executive_test(&file_path, &json_data, &mut record_time); + } + } + + for (name, v) in timings { + println!( + "{}\t{}", + name, + display::as_micros(&v.1.expect("All hooks are called with OnStop; qed")) + ); + } } fn run_state_test(args: Args) { - use ethjson::state::test::Test; - - let file = args.arg_file.expect("FILE is required"); - let mut file = match fs::File::open(&file) { - Err(err) => die(format!("Unable to open: {:?}: {}", file, err)), - Ok(file) => file, - }; - let state_test = match Test::load(&mut file) { - Err(err) => die(format!("Unable to load the test file: {}", err)), - Ok(test) => test, - }; - let only_test = args.flag_only.map(|s| s.to_lowercase()); - let only_chain = args.flag_chain.map(|s| s.to_lowercase()); - - for (name, test) in state_test { - if let Some(false) = only_test.as_ref().map(|only_test| &name.to_lowercase() == only_test) { - continue; - } - - let multitransaction = test.transaction; - let env_info = test.env.into(); - let pre = test.pre_state.into(); - - for (spec, states) in test.post_states { - if let Some(false) = only_chain.as_ref().map(|only_chain| &format!("{:?}", spec).to_lowercase() == only_chain) { - continue; - } - - for (idx, state) in states.into_iter().enumerate() { - let post_root = state.hash.into(); - let transaction = multitransaction.select(&state.indexes).into(); - - let trie_spec = if args.flag_std_dump_json { - TrieSpec::Fat - } else { - TrieSpec::Secure - }; - if args.flag_json { - info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, display::json::Informant::default(), trie_spec) - } else if args.flag_std_dump_json || args.flag_std_json { - if args.flag_std_err_only { - info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, display::std_json::Informant::err_only(), trie_spec) - } else if args.flag_std_out_only { - info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, display::std_json::Informant::out_only(), trie_spec) - } else { - info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, display::std_json::Informant::default(), trie_spec) - } - } else { - info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, display::simple::Informant::default(), trie_spec) - } - } - } - } + use ethjson::state::test::Test; + + let file = args.arg_file.expect("FILE is required"); + let mut file = match fs::File::open(&file) { + Err(err) => die(format!("Unable to open: {:?}: {}", file, err)), + Ok(file) => file, + }; + let state_test = match Test::load(&mut file) { + Err(err) => die(format!("Unable to load the test file: {}", err)), + Ok(test) => test, + }; + let only_test = args.flag_only.map(|s| s.to_lowercase()); + let only_chain = args.flag_chain.map(|s| s.to_lowercase()); + + for (name, test) in state_test { + if let Some(false) = only_test + .as_ref() + .map(|only_test| &name.to_lowercase() == only_test) + { + continue; + } + + let multitransaction = test.transaction; + let env_info = test.env.into(); + let pre = test.pre_state.into(); + + for (spec, states) in test.post_states { + if let Some(false) = only_chain + .as_ref() + .map(|only_chain| &format!("{:?}", spec).to_lowercase() == only_chain) + { + continue; + } + + for (idx, state) in states.into_iter().enumerate() { + let post_root = state.hash.into(); + let transaction = multitransaction.select(&state.indexes).into(); + + let trie_spec = if args.flag_std_dump_json { + TrieSpec::Fat + } else { + TrieSpec::Secure + }; + if args.flag_json { + info::run_transaction( + &name, + idx, + &spec, + &pre, + post_root, + &env_info, + transaction, + display::json::Informant::default(), + trie_spec, + ) + } else if args.flag_std_dump_json || args.flag_std_json { + if args.flag_std_err_only { + info::run_transaction( + &name, + idx, + &spec, + &pre, + post_root, + &env_info, + transaction, + display::std_json::Informant::err_only(), + trie_spec, + ) + } else if args.flag_std_out_only { + info::run_transaction( + &name, + idx, + &spec, + &pre, + post_root, + &env_info, + transaction, + display::std_json::Informant::out_only(), + trie_spec, + ) + } else { + info::run_transaction( + &name, + idx, + &spec, + &pre, + post_root, + &env_info, + transaction, + display::std_json::Informant::default(), + trie_spec, + ) + } + } else { + info::run_transaction( + &name, + idx, + &spec, + &pre, + post_root, + &env_info, + transaction, + display::simple::Informant::default(), + trie_spec, + ) + } + } + } + } } fn run_call(args: Args, informant: T) { - let from = arg(args.from(), "--from"); - let to = arg(args.to(), "--to"); - let code = arg(args.code(), "--code"); - let spec = arg(args.spec(), "--chain"); - let gas = arg(args.gas(), "--gas"); - let gas_price = arg(args.gas_price(), "--gas-price"); - let data = arg(args.data(), "--input"); - - if code.is_none() && to == Address::default() { - die("Either --code or --to is required."); - } - - let mut params = ActionParams::default(); - params.call_type = if code.is_none() { CallType::Call } else { CallType::None }; - params.code_address = to; - params.address = to; - params.sender = from; - params.origin = from; - params.gas = gas; - params.gas_price = gas_price; - params.code = code.map(Arc::new); - params.data = data; - - let mut sink = informant.clone_sink(); - let result = if args.flag_std_dump_json { - info::run_action(&spec, params, informant, TrieSpec::Fat) - } else { - info::run_action(&spec, params, informant, TrieSpec::Secure) - }; - T::finish(result, &mut sink); + let from = arg(args.from(), "--from"); + let to = arg(args.to(), "--to"); + let code = arg(args.code(), "--code"); + let spec = arg(args.spec(), "--chain"); + let gas = arg(args.gas(), "--gas"); + let gas_price = arg(args.gas_price(), "--gas-price"); + let data = arg(args.data(), "--input"); + + if code.is_none() && to == Address::default() { + die("Either --code or --to is required."); + } + + let mut params = ActionParams::default(); + params.call_type = if code.is_none() { + CallType::Call + } else { + CallType::None + }; + params.code_address = to; + params.address = to; + params.sender = from; + params.origin = from; + params.gas = gas; + params.gas_price = gas_price; + params.code = code.map(Arc::new); + params.data = data; + + let mut sink = informant.clone_sink(); + let result = if args.flag_std_dump_json { + info::run_action(&spec, params, informant, TrieSpec::Fat) + } else { + info::run_action(&spec, params, informant, TrieSpec::Secure) + }; + T::finish(result, &mut sink); } #[derive(Debug, Deserialize)] struct Args { - cmd_stats: bool, - cmd_state_test: bool, - cmd_stats_jsontests_vm: bool, - arg_file: Option, - flag_only: Option, - flag_from: Option, - flag_to: Option, - flag_code: Option, - flag_gas: Option, - flag_gas_price: Option, - flag_input: Option, - flag_chain: Option, - flag_json: bool, - flag_std_json: bool, - flag_std_dump_json: bool, - flag_std_err_only: bool, - flag_std_out_only: bool, + cmd_stats: bool, + cmd_state_test: bool, + cmd_stats_jsontests_vm: bool, + arg_file: Option, + flag_only: Option, + flag_from: Option, + flag_to: Option, + flag_code: Option, + flag_gas: Option, + flag_gas_price: Option, + flag_input: Option, + flag_chain: Option, + flag_json: bool, + flag_std_json: bool, + flag_std_dump_json: bool, + flag_std_err_only: bool, + flag_std_out_only: bool, } impl Args { - pub fn gas(&self) -> Result { - match self.flag_gas { - Some(ref gas) => gas.parse().map_err(to_string), - None => Ok(U256::from(u64::max_value())), - } - } - - pub fn gas_price(&self) -> Result { - match self.flag_gas_price { - Some(ref gas_price) => gas_price.parse().map_err(to_string), - None => Ok(U256::zero()), - } - } - - pub fn from(&self) -> Result { - match self.flag_from { - Some(ref from) => from.parse().map_err(to_string), - None => Ok(Address::default()), - } - } - - pub fn to(&self) -> Result { - match self.flag_to { - Some(ref to) => to.parse().map_err(to_string), - None => Ok(Address::default()), - } - } - - pub fn code(&self) -> Result, String> { - match self.flag_code { - Some(ref code) => code.from_hex().map(Some).map_err(to_string), - None => Ok(None), - } - } - - pub fn data(&self) -> Result, String> { - match self.flag_input { - Some(ref input) => input.from_hex().map_err(to_string).map(Some), - None => Ok(None), - } - } - - pub fn spec(&self) -> Result { - Ok(match self.flag_chain { - Some(ref filename) => { - let file = fs::File::open(filename).map_err(|e| format!("{}", e))?; - spec::Spec::load(&::std::env::temp_dir(), file)? - }, - None => { - ethcore::ethereum::new_foundation(&::std::env::temp_dir()) - }, - }) - } + pub fn gas(&self) -> Result { + match self.flag_gas { + Some(ref gas) => gas.parse().map_err(to_string), + None => Ok(U256::from(u64::max_value())), + } + } + + pub fn gas_price(&self) -> Result { + match self.flag_gas_price { + Some(ref gas_price) => gas_price.parse().map_err(to_string), + None => Ok(U256::zero()), + } + } + + pub fn from(&self) -> Result { + match self.flag_from { + Some(ref from) => from.parse().map_err(to_string), + None => Ok(Address::default()), + } + } + + pub fn to(&self) -> Result { + match self.flag_to { + Some(ref to) => to.parse().map_err(to_string), + None => Ok(Address::default()), + } + } + + pub fn code(&self) -> Result, String> { + match self.flag_code { + Some(ref code) => code.from_hex().map(Some).map_err(to_string), + None => Ok(None), + } + } + + pub fn data(&self) -> Result, String> { + match self.flag_input { + Some(ref input) => input.from_hex().map_err(to_string).map(Some), + None => Ok(None), + } + } + + pub fn spec(&self) -> Result { + Ok(match self.flag_chain { + Some(ref filename) => { + let file = fs::File::open(filename).map_err(|e| format!("{}", e))?; + spec::Spec::load(&::std::env::temp_dir(), file)? + } + None => ethcore::ethereum::new_foundation(&::std::env::temp_dir()), + }) + } } fn arg(v: Result, param: &str) -> T { - v.unwrap_or_else(|e| die(format!("Invalid {}: {}", param, e))) + v.unwrap_or_else(|e| die(format!("Invalid {}: {}", param, e))) } fn to_string(msg: T) -> String { - format!("{}", msg) + format!("{}", msg) } fn die(msg: T) -> ! { - println!("{}", msg); - ::std::process::exit(-1) + println!("{}", msg); + ::std::process::exit(-1) } #[cfg(test)] mod tests { - use docopt::Docopt; - use super::{Args, USAGE}; - - fn run>(args: &[T]) -> Args { - Docopt::new(USAGE).and_then(|d| d.argv(args.into_iter()).deserialize()).unwrap() - } - - #[test] - fn should_parse_all_the_options() { - let args = run(&[ - "parity-evm", - "--json", - "--std-json", - "--std-dump-json", - "--gas", "1", - "--gas-price", "2", - "--from", "0000000000000000000000000000000000000003", - "--to", "0000000000000000000000000000000000000004", - "--code", "05", - "--input", "06", - "--chain", "./testfile", "--std-err-only", "--std-out-only" - ]); - - assert_eq!(args.flag_json, true); - assert_eq!(args.flag_std_json, true); - assert_eq!(args.flag_std_dump_json, true); - assert_eq!(args.flag_std_err_only, true); - assert_eq!(args.flag_std_out_only, true); - assert_eq!(args.gas(), Ok(1.into())); - assert_eq!(args.gas_price(), Ok(2.into())); - assert_eq!(args.from(), Ok(3.into())); - assert_eq!(args.to(), Ok(4.into())); - assert_eq!(args.code(), Ok(Some(vec![05]))); - assert_eq!(args.data(), Ok(Some(vec![06]))); - assert_eq!(args.flag_chain, Some("./testfile".to_owned())); - } - - #[test] - fn should_parse_state_test_command() { - let args = run(&[ - "parity-evm", - "state-test", - "./file.json", - "--chain", "homestead", - "--only=add11", - "--json", - "--std-json", - "--std-dump-json" - ]); - - assert_eq!(args.cmd_state_test, true); - assert!(args.arg_file.is_some()); - assert_eq!(args.flag_json, true); - assert_eq!(args.flag_std_json, true); - assert_eq!(args.flag_std_dump_json, true); - assert_eq!(args.flag_chain, Some("homestead".to_owned())); - assert_eq!(args.flag_only, Some("add11".to_owned())); - } + use super::{Args, USAGE}; + use docopt::Docopt; + + fn run>(args: &[T]) -> Args { + Docopt::new(USAGE) + .and_then(|d| d.argv(args.into_iter()).deserialize()) + .unwrap() + } + + #[test] + fn should_parse_all_the_options() { + let args = run(&[ + "openethereum-evm", + "--json", + "--std-json", + "--std-dump-json", + "--gas", + "1", + "--gas-price", + "2", + "--from", + "0000000000000000000000000000000000000003", + "--to", + "0000000000000000000000000000000000000004", + "--code", + "05", + "--input", + "06", + "--chain", + "./testfile", + "--std-err-only", + "--std-out-only", + ]); + + assert_eq!(args.flag_json, true); + assert_eq!(args.flag_std_json, true); + assert_eq!(args.flag_std_dump_json, true); + assert_eq!(args.flag_std_err_only, true); + assert_eq!(args.flag_std_out_only, true); + assert_eq!(args.gas(), Ok(1.into())); + assert_eq!(args.gas_price(), Ok(2.into())); + assert_eq!(args.from(), Ok(3.into())); + assert_eq!(args.to(), Ok(4.into())); + assert_eq!(args.code(), Ok(Some(vec![05]))); + assert_eq!(args.data(), Ok(Some(vec![06]))); + assert_eq!(args.flag_chain, Some("./testfile".to_owned())); + } + + #[test] + fn should_parse_state_test_command() { + let args = run(&[ + "openethereum-evm", + "state-test", + "./file.json", + "--chain", + "homestead", + "--only=add11", + "--json", + "--std-json", + "--std-dump-json", + ]); + + assert_eq!(args.cmd_state_test, true); + assert!(args.arg_file.is_some()); + assert_eq!(args.flag_json, true); + assert_eq!(args.flag_std_json, true); + assert_eq!(args.flag_std_dump_json, true); + assert_eq!(args.flag_chain, Some("homestead".to_owned())); + assert_eq!(args.flag_only, Some("add11".to_owned())); + } } diff --git a/ipfs/Cargo.toml b/ipfs/Cargo.toml deleted file mode 100644 index 23dc8d68987..00000000000 --- a/ipfs/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -description = "Parity IPFS-compatible API" -name = "parity-ipfs-api" -version = "1.12.0" -license = "GPL-3.0" -authors = ["Parity Technologies "] - -[dependencies] -ethcore = { path = "../ethcore" } -parity-bytes = "0.1" -ethereum-types = "0.4" -jsonrpc-core = "10.0.1" -jsonrpc-http-server = "10.0.1" -rlp = { version = "0.3.0", features = ["ethereum"] } -cid = "0.3" -multihash = "0.8" -unicase = "2.0" - -[dev-dependencies] -ethcore = { path = "../ethcore", features = ["test-helpers"] } diff --git a/ipfs/src/error.rs b/ipfs/src/error.rs deleted file mode 100644 index a9a584985a7..00000000000 --- a/ipfs/src/error.rs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use {multihash, cid, http}; -use route::Out; - -pub type Result = ::std::result::Result; - -/// IPFS server error -#[derive(Debug)] -pub enum ServerError { - /// Wrapped `std::io::Error` - IoError(::std::io::Error), - /// Other `hyper` error - Other(http::hyper::error::Error), - /// Invalid --ipfs-api-interface - InvalidInterface -} - -/// Handle IO errors (ports taken when starting the server). -impl From<::std::io::Error> for ServerError { - fn from(err: ::std::io::Error) -> ServerError { - ServerError::IoError(err) - } -} - -impl From for ServerError { - fn from(err: http::hyper::error::Error) -> ServerError { - ServerError::Other(err) - } -} - -impl From for String { - fn from(err: ServerError) -> String { - match err { - ServerError::IoError(err) => err.to_string(), - ServerError::Other(err) => err.to_string(), - ServerError::InvalidInterface => "Invalid --ipfs-api-interface parameter".into(), - } - } -} - -impl ::std::fmt::Display for ServerError { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - match self { - ServerError::IoError(err) => write!(f, "Io Error: {}", err), - ServerError::Other(err) => write!(f, "Other error: {}", err), - ServerError::InvalidInterface => write!(f, "Invalid interface"), - } - } -} - -impl ::std::error::Error for ServerError {} - -#[derive(Debug, PartialEq)] -pub enum Error { - CidParsingFailed, - UnsupportedHash, - UnsupportedCid, - BlockNotFound, - TransactionNotFound, - StateRootNotFound, - ContractNotFound, -} - -/// Convert Error into Out, handy when switching from Rust's Result-based -/// error handling to Hyper's request handling. -impl From for Out { - fn from(err: Error) -> Out { - use self::Error::*; - - match err { - UnsupportedHash => Out::Bad("Hash must be Keccak-256"), - UnsupportedCid => Out::Bad("CID codec not supported"), - CidParsingFailed => Out::Bad("CID parsing failed"), - BlockNotFound => Out::NotFound("Block not found"), - TransactionNotFound => Out::NotFound("Transaction not found"), - StateRootNotFound => Out::NotFound("State root not found"), - ContractNotFound => Out::NotFound("Contract not found"), - } - } -} - -/// Convert Content ID errors. -impl From for Error { - fn from(_: cid::Error) -> Error { - Error::CidParsingFailed - } -} - -/// Convert multihash errors (multihash being part of CID). -impl From for Error { - fn from(_: multihash::Error) -> Error { - Error::CidParsingFailed - } -} diff --git a/ipfs/src/lib.rs b/ipfs/src/lib.rs deleted file mode 100644 index 0ba6a86d029..00000000000 --- a/ipfs/src/lib.rs +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -extern crate multihash; -extern crate cid; -extern crate unicase; - -extern crate rlp; -extern crate ethcore; -extern crate parity_bytes as bytes; -extern crate ethereum_types; -extern crate jsonrpc_core as core; -extern crate jsonrpc_http_server as http; - -pub mod error; -mod route; - -use std::thread; -use std::sync::{mpsc, Arc}; -use std::net::{SocketAddr, IpAddr}; - -use core::futures::future::{self, FutureResult}; -use core::futures::{self, Future}; -use ethcore::client::BlockChainClient; -use http::hyper::{self, server, Method, StatusCode, Body, - header::{self, HeaderValue}, -}; - -use error::ServerError; -use route::Out; - -pub use http::{AccessControlAllowOrigin, Host, DomainsValidation}; - -/// Request/response handler -pub struct IpfsHandler { - /// Allowed CORS domains - cors_domains: Option>, - /// Hostnames allowed in the `Host` request header - allowed_hosts: Option>, - /// Reference to the Blockchain Client - client: Arc, -} - -impl IpfsHandler { - pub fn client(&self) -> &BlockChainClient { - &*self.client - } - - pub fn new(cors: DomainsValidation, hosts: DomainsValidation, client: Arc) -> Self { - IpfsHandler { - cors_domains: cors.into(), - allowed_hosts: hosts.into(), - client: client, - } - } - pub fn on_request(&self, req: hyper::Request) -> (Option, Out) { - match *req.method() { - Method::GET | Method::POST => {}, - _ => return (None, Out::Bad("Invalid Request")), - } - - if !http::is_host_allowed(&req, &self.allowed_hosts) { - return (None, Out::Bad("Disallowed Host header")); - } - - let cors_header = http::cors_allow_origin(&req, &self.cors_domains); - if cors_header == http::AllowCors::Invalid { - return (None, Out::Bad("Disallowed Origin header")); - } - - let path = req.uri().path(); - let query = req.uri().query(); - return (cors_header.into(), self.route(path, query)); - } -} - -impl hyper::service::Service for IpfsHandler { - type ReqBody = Body; - type ResBody = Body; - type Error = hyper::Error; - type Future = FutureResult, Self::Error>; - - fn call(&mut self, request: hyper::Request) -> Self::Future { - let (cors_header, out) = self.on_request(request); - - let mut res = match out { - Out::OctetStream(bytes) => { - hyper::Response::builder() - .status(StatusCode::OK) - .header("content-type", HeaderValue::from_static("application/octet-stream")) - .body(bytes.into()) - }, - Out::NotFound(reason) => { - hyper::Response::builder() - .status(StatusCode::NOT_FOUND) - .header("content-type", HeaderValue::from_static("text/plain; charset=utf-8")) - .body(reason.into()) - }, - Out::Bad(reason) => { - hyper::Response::builder() - .status(StatusCode::BAD_REQUEST) - .header("content-type", HeaderValue::from_static("text/plain; charset=utf-8")) - .body(reason.into()) - } - }.expect("Response builder: Parsing 'content-type' header name will not fail; qed"); - - if let Some(cors_header) = cors_header { - res.headers_mut().append(header::ACCESS_CONTROL_ALLOW_ORIGIN, cors_header); - res.headers_mut().append(header::VARY, HeaderValue::from_static("origin")); - } - - future::ok(res) - } -} - -/// Add current interface (default: "127.0.0.1:5001") to list of allowed hosts -fn include_current_interface(mut hosts: Vec, interface: String, port: u16) -> Vec { - hosts.push(match port { - 80 => interface, - _ => format!("{}:{}", interface, port), - }.into()); - - hosts -} - -#[derive(Debug)] -pub struct Listening { - close: Option>, - thread: Option>, -} - -impl Drop for Listening { - fn drop(&mut self) { - self.close.take().unwrap().send(()).unwrap(); - let _ = self.thread.take().unwrap().join(); - } -} - -pub fn start_server( - port: u16, - interface: String, - cors: DomainsValidation, - hosts: DomainsValidation, - client: Arc -) -> Result { - - let ip: IpAddr = interface.parse().map_err(|_| ServerError::InvalidInterface)?; - let addr = SocketAddr::new(ip, port); - let hosts: Option> = hosts.into(); - let hosts: DomainsValidation<_> = hosts.map(move |hosts| include_current_interface(hosts, interface, port)).into(); - - let (close, shutdown_signal) = futures::sync::oneshot::channel::<()>(); - let (tx, rx) = mpsc::sync_channel::>(1); - let thread = thread::spawn(move || { - let send = |res| tx.send(res).expect("rx end is never dropped; qed"); - - let server_bldr = match server::Server::try_bind(&addr) { - Ok(s) => s, - Err(err) => { - send(Err(ServerError::from(err))); - return; - } - }; - - let new_service = move || { - Ok::<_, ServerError>( - IpfsHandler::new(cors.clone(), hosts.clone(), client.clone()) - ) - }; - - let server = server_bldr - .serve(new_service) - .map_err(|_| ()) - .select(shutdown_signal.map_err(|_| ())) - .then(|_| Ok(())); - - hyper::rt::run(server); - send(Ok(())); - }); - - // Wait for server to start successfuly. - rx.recv().expect("tx end is never dropped; qed")?; - - Ok(Listening { - close: close.into(), - thread: thread.into(), - }) -} diff --git a/ipfs/src/route.rs b/ipfs/src/route.rs deleted file mode 100644 index f4a730338bd..00000000000 --- a/ipfs/src/route.rs +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use {rlp, multihash, IpfsHandler}; -use error::{Error, Result}; -use cid::{ToCid, Codec}; - -use multihash::Hash; -use ethereum_types::H256; -use bytes::Bytes; -use ethcore::client::{BlockId, TransactionId}; - -type Reason = &'static str; - -/// Keeps the state of the response to send out -#[derive(Debug, PartialEq)] -pub enum Out { - OctetStream(Bytes), - NotFound(Reason), - Bad(Reason), -} - -impl IpfsHandler { - /// Route path + query string to a specialized method - pub fn route(&self, path: &str, query: Option<&str>) -> Out { - match path { - "/api/v0/block/get" => { - let arg = query.and_then(|q| get_param(q, "arg")).unwrap_or(""); - - self.route_cid(arg).unwrap_or_else(Into::into) - }, - - _ => Out::NotFound("Route not found") - } - } - - /// Attempt to read Content ID from `arg` query parameter, get a hash and - /// route further by the CID's codec. - fn route_cid(&self, cid: &str) -> Result { - let cid = cid.to_cid()?; - - let mh = multihash::decode(&cid.hash)?; - - if mh.alg != Hash::Keccak256 { return Err(Error::UnsupportedHash); } - - let hash: H256 = mh.digest.into(); - - match cid.codec { - Codec::EthereumBlock => self.block(hash), - Codec::EthereumBlockList => self.block_list(hash), - Codec::EthereumTx => self.transaction(hash), - Codec::EthereumStateTrie => self.state_trie(hash), - Codec::Raw => self.contract_code(hash), - _ => return Err(Error::UnsupportedCid), - } - } - - /// Get block header by hash as raw binary. - fn block(&self, hash: H256) -> Result { - let block_id = BlockId::Hash(hash); - let block = self.client().block_header(block_id).ok_or(Error::BlockNotFound)?; - - Ok(Out::OctetStream(block.into_inner())) - } - - /// Get list of block ommers by hash as raw binary. - fn block_list(&self, hash: H256) -> Result { - let uncles = self.client().find_uncles(&hash).ok_or(Error::BlockNotFound)?; - - Ok(Out::OctetStream(rlp::encode_list(&uncles))) - } - - /// Get transaction by hash and return as raw binary. - fn transaction(&self, hash: H256) -> Result { - let tx_id = TransactionId::Hash(hash); - let tx = self.client().transaction(tx_id).ok_or(Error::TransactionNotFound)?; - - Ok(Out::OctetStream(rlp::encode(&*tx))) - } - - /// Get state trie node by hash and return as raw binary. - fn state_trie(&self, hash: H256) -> Result { - let data = self.client().state_data(&hash).ok_or(Error::StateRootNotFound)?; - - Ok(Out::OctetStream(data)) - } - - /// Get state trie node by hash and return as raw binary. - fn contract_code(&self, hash: H256) -> Result { - let data = self.client().state_data(&hash).ok_or(Error::ContractNotFound)?; - - Ok(Out::OctetStream(data)) - } -} - -/// Get a query parameter's value by name. -fn get_param<'a>(query: &'a str, name: &str) -> Option<&'a str> { - query.split('&') - .find(|part| part.starts_with(name) && part[name.len()..].starts_with("=")) - .map(|part| &part[name.len() + 1..]) -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use super::*; - use ethcore::client::TestBlockChainClient; - - fn get_mocked_handler() -> IpfsHandler { - IpfsHandler::new(None.into(), None.into(), Arc::new(TestBlockChainClient::new())) - } - - #[test] - fn test_get_param() { - let query = "foo=100&bar=200&qux=300"; - - assert_eq!(get_param(query, "foo"), Some("100")); - assert_eq!(get_param(query, "bar"), Some("200")); - assert_eq!(get_param(query, "qux"), Some("300")); - assert_eq!(get_param(query, "bar="), None); - assert_eq!(get_param(query, "200"), None); - assert_eq!(get_param("", "foo"), None); - assert_eq!(get_param("foo", "foo"), None); - assert_eq!(get_param("foo&bar", "foo"), None); - assert_eq!(get_param("bar&foo", "foo"), None); - } - - #[test] - fn cid_route_block() { - let handler = get_mocked_handler(); - - // `eth-block` with Keccak-256 - let cid = "z43AaGF5tmkT9SEX6urrhwpEW5ZSaACY73Vw357ZXTsur2fR8BM"; - - assert_eq!(Err(Error::BlockNotFound), handler.route_cid(cid)); - } - - #[test] - fn cid_route_block_list() { - let handler = get_mocked_handler(); - - // `eth-block-list` with Keccak-256 - let cid = "z43c7o7FsNxqdLJW8Ucj19tuCALtnmUb2EkDptj4W6xSkFVTqWs"; - - assert_eq!(Err(Error::BlockNotFound), handler.route_cid(cid)); - } - - #[test] - fn cid_route_tx() { - let handler = get_mocked_handler(); - - // `eth-tx` with Keccak-256 - let cid = "z44VCrqbpbPcb8SUBc8Tba4EaKuoDz2grdEoQXx4TP7WYh9ZGBu"; - - assert_eq!(Err(Error::TransactionNotFound), handler.route_cid(cid)); - } - - #[test] - fn cid_route_state_trie() { - let handler = get_mocked_handler(); - - // `eth-state-trie` with Keccak-256 - let cid = "z45oqTS7kR2n2peRGJQ4VCJEeaG9sorqcCyfmznZPJM7FMdhQCT"; - - assert_eq!(Err(Error::StateRootNotFound), handler.route_cid(&cid)); - } - - #[test] - fn cid_route_contract_code() { - let handler = get_mocked_handler(); - - // `raw` with Keccak-256 - let cid = "zb34WAp1Q5fhtLGZ3w3jhnTWaNbVV5ZZvGq4vuJQzERj6Pu3H"; - - assert_eq!(Err(Error::ContractNotFound), handler.route_cid(&cid)); - } - - #[test] - fn cid_route_invalid_hash() { - let handler = get_mocked_handler(); - - // `eth-block` with SHA3-256 hash - let cid = "z43Aa9gr1MM7TENJh4Em9d9Ttr7p3UcfyMpNei6WLVeCmSEPu8F"; - - assert_eq!(Err(Error::UnsupportedHash), handler.route_cid(cid)); - } - - #[test] - fn cid_route_invalid_codec() { - let handler = get_mocked_handler(); - - // `bitcoin-block` with Keccak-256 - let cid = "z4HFyHvb8CarYARyxz4cCcPaciduXd49TFPCKLhYmvNxf7Auvwu"; - - assert_eq!(Err(Error::UnsupportedCid), handler.route_cid(&cid)); - } - - #[test] - fn route_block() { - let handler = get_mocked_handler(); - - let out = handler.route("/api/v0/block/get", Some("arg=z43AaGF5tmkT9SEX6urrhwpEW5ZSaACY73Vw357ZXTsur2fR8BM")); - - assert_eq!(out, Out::NotFound("Block not found")); - } - - #[test] - fn route_block_missing_query() { - let handler = get_mocked_handler(); - - let out = handler.route("/api/v0/block/get", None); - - assert_eq!(out, Out::Bad("CID parsing failed")); - } - - #[test] - fn route_block_invalid_query() { - let handler = get_mocked_handler(); - - let out = handler.route("/api/v0/block/get", Some("arg=foobarz43AaGF5tmkT9SEX6urrhwpEW5ZSaACY73Vw357ZXTsur2fR8BM")); - - assert_eq!(out, Out::Bad("CID parsing failed")); - } - - #[test] - fn route_invalid_route() { - let handler = get_mocked_handler(); - - let out = handler.route("/foo/bar/baz", Some("arg=z43AaGF5tmkT9SEX6urrhwpEW5ZSaACY73Vw357ZXTsur2fR8BM")); - - assert_eq!(out, Out::NotFound("Route not found")); - } -} diff --git a/json/Cargo.toml b/json/Cargo.toml index 2a6d8c7f36b..584c046370d 100644 --- a/json/Cargo.toml +++ b/json/Cargo.toml @@ -1,4 +1,5 @@ [package] +description = "Parity Ethereum JSON Deserialization" name = "ethjson" version = "0.1.0" authors = ["Parity Technologies "] @@ -10,3 +11,6 @@ serde = "1.0" serde_json = "1.0" serde_derive = "1.0" +[dev-dependencies] +macros = { path = "../util/macros" } +maplit = "1.0.2" diff --git a/json/src/blockchain/account.rs b/json/src/blockchain/account.rs index aa6f6f8bf26..fc39b074e8d 100644 --- a/json/src/blockchain/account.rs +++ b/json/src/blockchain/account.rs @@ -1,46 +1,46 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Blockchain test account deserializer. +use bytes::Bytes; use std::collections::BTreeMap; use uint::Uint; -use bytes::Bytes; /// Blockchain test account deserializer. #[derive(Debug, PartialEq, Deserialize, Clone)] pub struct Account { - /// Balance. - pub balance: Uint, - /// Code. - pub code: Bytes, - /// Nonce. - pub nonce: Uint, - /// Storage. - pub storage: BTreeMap, + /// Balance. + pub balance: Uint, + /// Code. + pub code: Bytes, + /// Nonce. + pub nonce: Uint, + /// Storage. + pub storage: BTreeMap, } #[cfg(test)] mod tests { - use serde_json; - use blockchain::account::Account; + use blockchain::account::Account; + use serde_json; - #[test] - fn account_deserialization() { - let s = r#"{ + #[test] + fn account_deserialization() { + let s = r#"{ "balance" : "0x09184e72a078", "code" : "0x600140600155", "nonce" : "0x00", @@ -48,7 +48,7 @@ mod tests { "0x01" : "0x9a10c2b5bb8f3c602e674006d9b21f09167df57c87a78a5ce96d4159ecb76520" } }"#; - let _deserialized: Account = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } + let _deserialized: Account = serde_json::from_str(s).unwrap(); + // TODO: validate all fields + } } diff --git a/json/src/blockchain/block.rs b/json/src/blockchain/block.rs index 23ba5300dc2..087c17fffb6 100644 --- a/json/src/blockchain/block.rs +++ b/json/src/blockchain/block.rs @@ -1,51 +1,50 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Blockchain test block deserializer. +use blockchain::{header::Header, transaction::Transaction}; use bytes::Bytes; -use blockchain::header::Header; -use blockchain::transaction::Transaction; /// Blockchain test block deserializer. #[derive(Debug, PartialEq, Deserialize)] pub struct Block { - #[serde(rename = "blockHeader")] - header: Option
, - rlp: Bytes, - transactions: Option>, - #[serde(rename = "uncleHeaders")] - uncles: Option>, + #[serde(rename = "blockHeader")] + header: Option
, + rlp: Bytes, + transactions: Option>, + #[serde(rename = "uncleHeaders")] + uncles: Option>, } impl Block { - /// Returns block rlp. - pub fn rlp(&self) -> Vec { - self.rlp.clone().into() - } + /// Returns block rlp. + pub fn rlp(&self) -> Vec { + self.rlp.clone().into() + } } #[cfg(test)] mod tests { - use serde_json; - use blockchain::block::Block; + use blockchain::block::Block; + use serde_json; - #[test] - fn block_deserialization() { - let s = r#"{ + #[test] + fn block_deserialization() { + let s = r#"{ "blockHeader" : { "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1", @@ -69,7 +68,7 @@ mod tests { "transactions" : [], "uncleHeaders" : [] }"#; - let _deserialized: Block = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } + let _deserialized: Block = serde_json::from_str(s).unwrap(); + // TODO: validate all fields + } } diff --git a/json/src/blockchain/blockchain.rs b/json/src/blockchain/blockchain.rs index b92336f793a..7e1406f0a3c 100644 --- a/json/src/blockchain/blockchain.rs +++ b/json/src/blockchain/blockchain.rs @@ -1,106 +1,104 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Blockchain deserialization. +use blockchain::{block::Block, header::Header, state::State}; use bytes::Bytes; use hash::H256; -use blockchain::state::State; -use blockchain::header::Header; -use blockchain::block::Block; -use spec::{ForkSpec, Genesis, Seal, Ethereum}; +use spec::{Ethereum, ForkSpec, Genesis, Seal}; /// Json Block test possible engine kind. #[derive(Debug, PartialEq, Deserialize)] pub enum Engine { - /// Default (old) behaviour. - Ethash, - /// No check of block's difficulty and nonce for tests. - NoProof, + /// Default (old) behaviour. + Ethash, + /// No check of block's difficulty and nonce for tests. + NoProof, } impl Default for Engine { - fn default() -> Self { - Engine::Ethash - } + fn default() -> Self { + Engine::Ethash + } } /// Blockchain deserialization. #[derive(Debug, PartialEq, Deserialize)] #[serde(rename_all = "camelCase")] pub struct BlockChain { - /// Genesis block header. - #[serde(rename = "genesisBlockHeader")] - pub genesis_block: Header, - /// Genesis block rlp. - #[serde(rename = "genesisRLP")] - pub genesis_rlp: Option, - /// Blocks. - pub blocks: Vec, - /// Post state. - pub post_state: State, - /// Pre state. - #[serde(rename = "pre")] - pub pre_state: State, - /// Hash of best block. - #[serde(rename = "lastblockhash")] - pub best_block: H256, - /// Network. - pub network: ForkSpec, - #[serde(default)] - #[serde(rename="sealEngine")] - /// Engine - pub engine: Engine, + /// Genesis block header. + #[serde(rename = "genesisBlockHeader")] + pub genesis_block: Header, + /// Genesis block rlp. + #[serde(rename = "genesisRLP")] + pub genesis_rlp: Option, + /// Blocks. + pub blocks: Vec, + /// Post state. + pub post_state: Option, + /// Pre state. + #[serde(rename = "pre")] + pub pre_state: State, + /// Hash of best block. + #[serde(rename = "lastblockhash")] + pub best_block: H256, + /// Network. + pub network: ForkSpec, + #[serde(default)] + #[serde(rename = "sealEngine")] + /// Engine + pub engine: Engine, } impl BlockChain { - /// Returns blocks rlp. - pub fn blocks_rlp(&self) -> Vec> { - self.blocks.iter().map(|block| block.rlp()).collect() - } + /// Returns blocks rlp. + pub fn blocks_rlp(&self) -> Vec> { + self.blocks.iter().map(|block| block.rlp()).collect() + } - /// Returns spec compatible genesis struct. - pub fn genesis(&self) -> Genesis { - Genesis { - seal: Seal::Ethereum(Ethereum { - nonce: self.genesis_block.nonce.clone(), - mix_hash: self.genesis_block.mix_hash.clone(), - }), - difficulty: self.genesis_block.difficulty, - author: Some(self.genesis_block.author.clone()), - timestamp: Some(self.genesis_block.timestamp), - parent_hash: Some(self.genesis_block.parent_hash.clone()), - gas_limit: self.genesis_block.gas_limit, - transactions_root: Some(self.genesis_block.transactions_root.clone()), - receipts_root: Some(self.genesis_block.receipts_root.clone()), - state_root: Some(self.genesis_block.state_root.clone()), - gas_used: Some(self.genesis_block.gas_used), - extra_data: Some(self.genesis_block.extra_data.clone()), - } - } + /// Returns spec compatible genesis struct. + pub fn genesis(&self) -> Genesis { + Genesis { + seal: Seal::Ethereum(Ethereum { + nonce: self.genesis_block.nonce.clone(), + mix_hash: self.genesis_block.mix_hash.clone(), + }), + difficulty: self.genesis_block.difficulty, + author: Some(self.genesis_block.author.clone()), + timestamp: Some(self.genesis_block.timestamp), + parent_hash: Some(self.genesis_block.parent_hash.clone()), + gas_limit: self.genesis_block.gas_limit, + transactions_root: Some(self.genesis_block.transactions_root.clone()), + receipts_root: Some(self.genesis_block.receipts_root.clone()), + state_root: Some(self.genesis_block.state_root.clone()), + gas_used: Some(self.genesis_block.gas_used), + extra_data: Some(self.genesis_block.extra_data.clone()), + } + } } #[cfg(test)] mod tests { - use serde_json; - use blockchain::blockchain::BlockChain; + use blockchain::blockchain::BlockChain; + use serde_json; - #[test] - fn blockchain_deserialization() { - let s = r#"{ + #[test] + fn blockchain_deserialization() { + let s = r#"{ "blocks" : [{ "blockHeader" : { "bloom" : "00000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000040000000000000000000000000000000000000000000000000000000", @@ -198,7 +196,7 @@ mod tests { } } }"#; - let _deserialized: BlockChain = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } + let _deserialized: BlockChain = serde_json::from_str(s).unwrap(); + // TODO: validate all fields + } } diff --git a/json/src/blockchain/header.rs b/json/src/blockchain/header.rs index 8de5b16edb6..8fcb25caaa3 100644 --- a/json/src/blockchain/header.rs +++ b/json/src/blockchain/header.rs @@ -1,75 +1,75 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Blockchain test header deserializer. -use hash::{H64, Address, H256, Bloom}; -use uint::Uint; use bytes::Bytes; +use hash::{Address, Bloom, H256, H64}; +use uint::Uint; /// Blockchain test header deserializer. #[derive(Debug, PartialEq, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Header { - /// Blocks bloom. - pub bloom: Bloom, - /// Blocks author. - #[serde(rename = "coinbase")] - pub author: Address, - /// Difficulty. - pub difficulty: Uint, - /// Extra data. - pub extra_data: Bytes, - /// Gas limit. - pub gas_limit: Uint, - /// Gas used. - pub gas_used: Uint, - /// Hash. - pub hash: H256, - /// Mix hash. - pub mix_hash: H256, - /// Seal nonce. - pub nonce: H64, - /// Block number. - pub number: Uint, - /// Parent hash. - pub parent_hash: H256, - /// Receipt root. - #[serde(rename = "receiptTrie")] - pub receipts_root: H256, - /// State root. - pub state_root: H256, - /// Timestamp. - pub timestamp: Uint, - /// Transactions root. - #[serde(rename = "transactionsTrie")] - pub transactions_root: H256, - /// Uncles hash. - #[serde(rename = "uncleHash")] - pub uncles_hash: H256, + /// Blocks bloom. + pub bloom: Bloom, + /// Blocks author. + #[serde(rename = "coinbase")] + pub author: Address, + /// Difficulty. + pub difficulty: Uint, + /// Extra data. + pub extra_data: Bytes, + /// Gas limit. + pub gas_limit: Uint, + /// Gas used. + pub gas_used: Uint, + /// Hash. + pub hash: H256, + /// Mix hash. + pub mix_hash: H256, + /// Seal nonce. + pub nonce: H64, + /// Block number. + pub number: Uint, + /// Parent hash. + pub parent_hash: H256, + /// Receipt root. + #[serde(rename = "receiptTrie")] + pub receipts_root: H256, + /// State root. + pub state_root: H256, + /// Timestamp. + pub timestamp: Uint, + /// Transactions root. + #[serde(rename = "transactionsTrie")] + pub transactions_root: H256, + /// Uncles hash. + #[serde(rename = "uncleHash")] + pub uncles_hash: H256, } #[cfg(test)] mod tests { - use serde_json; - use blockchain::header::Header; + use blockchain::header::Header; + use serde_json; - #[test] - fn header_deserialization() { - let s = r#"{ + #[test] + fn header_deserialization() { + let s = r#"{ "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1", "difficulty" : "0x020000", @@ -87,7 +87,7 @@ mod tests { "transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" }"#; - let _deserialized: Header = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } + let _deserialized: Header = serde_json::from_str(s).unwrap(); + // TODO: validate all fields + } } diff --git a/json/src/blockchain/mod.rs b/json/src/blockchain/mod.rs index 0a3b162e95f..50d4f1d3c26 100644 --- a/json/src/blockchain/mod.rs +++ b/json/src/blockchain/mod.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Blockchain test deserialization. @@ -21,14 +21,15 @@ pub mod block; pub mod blockchain; pub mod header; pub mod state; -pub mod transaction; pub mod test; +pub mod transaction; -pub use self::account::Account; -pub use self::block::Block; -pub use self::blockchain::BlockChain; -pub use self::blockchain::Engine; -pub use self::header::Header; -pub use self::state::State; -pub use self::test::Test; -pub use self::transaction::Transaction; +pub use self::{ + account::Account, + block::Block, + blockchain::{BlockChain, Engine}, + header::Header, + state::State, + test::Test, + transaction::Transaction, +}; diff --git a/json/src/blockchain/state.rs b/json/src/blockchain/state.rs index e108c937f8a..f2ebb04b339 100644 --- a/json/src/blockchain/state.rs +++ b/json/src/blockchain/state.rs @@ -1,34 +1,77 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Blockchain test state deserializer. +use crate::{ + bytes::Bytes, + hash::{Address, H256}, + spec::{Account, Builtin}, +}; +use serde::Deserialize; use std::collections::BTreeMap; -use hash::Address; -use blockchain::account::Account; -/// Blockchain test state deserializer. -#[derive(Debug, PartialEq, Deserialize, Clone)] -pub struct State(BTreeMap); +#[derive(Clone, Debug, PartialEq, Deserialize)] +#[serde(untagged)] +pub enum HashOrMap { + /// When the `postState` is large, tests sometimes just include the state root of the last + /// successful block here. + Hash(H256), + /// The expected `postState` of a test + Map(BTreeMap), +} + +/// Blockchain state deserializer. +#[derive(Clone, Debug, PartialEq, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct State(pub HashOrMap); + +impl State { + /// Returns all builtins. + pub fn builtins(&self) -> BTreeMap { + match &self.0 { + HashOrMap::Hash(_) => BTreeMap::default(), + HashOrMap::Map(map) => map + .iter() + .filter_map(|(add, ref acc)| acc.builtin.clone().map(|b| (add.clone(), b.into()))) + .collect(), + } + } + + /// Returns all constructors. + pub fn constructors(&self) -> BTreeMap { + match &self.0 { + HashOrMap::Hash(_) => BTreeMap::default(), + HashOrMap::Map(map) => map + .iter() + .filter_map(|(add, ref acc)| acc.constructor.clone().map(|b| (add.clone(), b))) + .collect(), + } + } +} impl IntoIterator for State { - type Item = as IntoIterator>::Item; - type IntoIter = as IntoIterator>::IntoIter; + type Item = as IntoIterator>::Item; + type IntoIter = as IntoIterator>::IntoIter; - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } + fn into_iter(self) -> Self::IntoIter { + if let HashOrMap::Map(m) = self.0 { + m.into_iter() + } else { + BTreeMap::default().into_iter() + } + } } diff --git a/json/src/blockchain/test.rs b/json/src/blockchain/test.rs index d773aa3b510..01ace8eb6f5 100644 --- a/json/src/blockchain/test.rs +++ b/json/src/blockchain/test.rs @@ -1,43 +1,44 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Blockchain test deserializer. -use std::collections::BTreeMap; -use std::io::Read; -use serde_json; -use serde_json::Error; use blockchain::blockchain::BlockChain; +use serde_json::{self, Error}; +use std::{collections::BTreeMap, io::Read}; /// Blockchain test deserializer. #[derive(Debug, PartialEq, Deserialize)] pub struct Test(BTreeMap); impl IntoIterator for Test { - type Item = as IntoIterator>::Item; - type IntoIter = as IntoIterator>::IntoIter; + type Item = as IntoIterator>::Item; + type IntoIter = as IntoIterator>::IntoIter; - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } } impl Test { - /// Loads test from json. - pub fn load(reader: R) -> Result where R: Read { - serde_json::from_reader(reader) - } + /// Loads test from json. + pub fn load(reader: R) -> Result + where + R: Read, + { + serde_json::from_reader(reader) + } } diff --git a/json/src/blockchain/transaction.rs b/json/src/blockchain/transaction.rs index 4e519f394ee..58fa2709044 100644 --- a/json/src/blockchain/transaction.rs +++ b/json/src/blockchain/transaction.rs @@ -1,34 +1,34 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Blockchain test transaction deserialization. -use uint::Uint; use bytes::Bytes; +use uint::Uint; /// Blockchain test transaction deserialization. #[derive(Debug, PartialEq, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Transaction { - data: Bytes, - gas_limit: Uint, - gas_price: Uint, - nonce: Uint, - r: Uint, - s: Uint, - v: Uint, - value: Uint + data: Bytes, + gas_limit: Uint, + gas_price: Uint, + nonce: Uint, + r: Uint, + s: Uint, + v: Uint, + value: Uint, } diff --git a/json/src/bytes.rs b/json/src/bytes.rs index 3fbdee2380f..848a6359f89 100644 --- a/json/src/bytes.rs +++ b/json/src/bytes.rs @@ -1,119 +1,130 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Lenient bytes json deserialization for test json files. -use std::fmt; -use std::str::FromStr; -use std::ops::Deref; use rustc_hex::FromHex; -use serde::{Deserialize, Deserializer}; -use serde::de::{Error, Visitor}; +use serde::{ + de::{Error, Visitor}, + Deserialize, Deserializer, +}; +use std::{fmt, ops::Deref, str::FromStr}; /// Lenient bytes json deserialization for test json files. #[derive(Default, Debug, PartialEq, Eq, Clone, PartialOrd, Ord)] pub struct Bytes(Vec); impl Bytes { - /// Creates bytes struct. - pub fn new(v: Vec) -> Self { - Bytes(v) - } + /// Creates bytes struct. + pub fn new(v: Vec) -> Self { + Bytes(v) + } } impl Into> for Bytes { - fn into(self) -> Vec { - self.0 - } + fn into(self) -> Vec { + self.0 + } } impl Deref for Bytes { - type Target = [u8]; + type Target = [u8]; - fn deref(&self) -> &[u8] { - &self.0 - } + fn deref(&self) -> &[u8] { + &self.0 + } } impl FromStr for Bytes { - type Err = String; - - fn from_str(value: &str) -> Result { - let v = match value.len() { - 0 => vec![], - 2 if value.starts_with("0x") => vec![], - _ if value.starts_with("0x") && value.len() % 2 == 1 => { - let v = "0".to_owned() + &value[2..]; - FromHex::from_hex(v.as_str()).unwrap_or(vec![]) - }, - _ if value.starts_with("0x") => FromHex::from_hex(&value[2..]).unwrap_or(vec![]), - _ => FromHex::from_hex(value).unwrap_or(vec![]), - }; - - Ok(Bytes(v)) - } + type Err = String; + + fn from_str(value: &str) -> Result { + let v = match value.len() { + 0 => vec![], + 2 if value.starts_with("0x") => vec![], + _ if value.starts_with("0x") && value.len() % 2 == 1 => { + let v = "0".to_owned() + &value[2..]; + FromHex::from_hex(v.as_str()).unwrap_or(vec![]) + } + _ if value.starts_with("0x") => FromHex::from_hex(&value[2..]).unwrap_or(vec![]), + _ => FromHex::from_hex(value).unwrap_or(vec![]), + }; + + Ok(Bytes(v)) + } } impl<'a> Deserialize<'a> for Bytes { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> { - deserializer.deserialize_any(BytesVisitor) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + deserializer.deserialize_any(BytesVisitor) + } } struct BytesVisitor; impl<'a> Visitor<'a> for BytesVisitor { - type Value = Bytes; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a hex encoded string of bytes") - } - - fn visit_str(self, value: &str) -> Result where E: Error { - Bytes::from_str(value).map_err(Error::custom) - } - - fn visit_string(self, value: String) -> Result where E: Error { - self.visit_str(value.as_ref()) - } + type Value = Bytes; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a hex encoded string of bytes") + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + Bytes::from_str(value).map_err(Error::custom) + } + + fn visit_string(self, value: String) -> Result + where + E: Error, + { + self.visit_str(value.as_ref()) + } } #[cfg(test)] mod test { - use serde_json; - use bytes::Bytes; - - #[test] - fn bytes_deserialization() { - let s = r#"["", "0x", "0x12", "1234", "0x001"]"#; - let deserialized: Vec = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, vec![ - Bytes(vec![]), - Bytes(vec![]), - Bytes(vec![0x12]), - Bytes(vec![0x12, 0x34]), - Bytes(vec![0, 1]) - ]); - } - - #[test] - fn bytes_into() { - let bytes = Bytes(vec![0xff, 0x11]); - let v: Vec = bytes.into(); - assert_eq!(vec![0xff, 0x11], v); - } + use bytes::Bytes; + use serde_json; + + #[test] + fn bytes_deserialization() { + let s = r#"["", "0x", "0x12", "1234", "0x001"]"#; + let deserialized: Vec = serde_json::from_str(s).unwrap(); + assert_eq!( + deserialized, + vec![ + Bytes(vec![]), + Bytes(vec![]), + Bytes(vec![0x12]), + Bytes(vec![0x12, 0x34]), + Bytes(vec![0, 1]) + ] + ); + } + + #[test] + fn bytes_into() { + let bytes = Bytes(vec![0xff, 0x11]); + let v: Vec = bytes.into(); + assert_eq!(vec![0xff, 0x11], v); + } } diff --git a/json/src/hash.rs b/json/src/hash.rs index a025dc45414..fc98dc3c624 100644 --- a/json/src/hash.rs +++ b/json/src/hash.rs @@ -1,91 +1,110 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Lenient hash json deserialization for test json files. -use std::str::FromStr; -use std::fmt; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use serde::de::{Error, Visitor}; +use ethereum_types::{ + Bloom as Hash2048, H160 as Hash160, H256 as Hash256, H520 as Hash520, H64 as Hash64, +}; use rustc_hex::ToHex; -use ethereum_types::{H64 as Hash64, H160 as Hash160, H256 as Hash256, H520 as Hash520, Bloom as Hash2048}; +use serde::{ + de::{Error, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; +use std::{fmt, str::FromStr}; macro_rules! impl_hash { - ($name: ident, $inner: ident) => { - /// Lenient hash json deserialization for test json files. - #[derive(Default, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone)] - pub struct $name(pub $inner); - - impl From<$name> for $inner { - fn from(other: $name) -> $inner { - other.0 - } - } - - impl From<$inner> for $name { - fn from(i: $inner) -> Self { - $name(i) - } - } - - impl<'a> Deserialize<'a> for $name { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> { - - struct HashVisitor; - - impl<'b> Visitor<'b> for HashVisitor { - type Value = $name; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a 0x-prefixed hex-encoded hash") - } - - fn visit_str(self, value: &str) -> Result where E: Error { - let value = match value.len() { - 0 => $inner::from(0), - 2 if value == "0x" => $inner::from(0), - _ if value.starts_with("0x") => $inner::from_str(&value[2..]).map_err(|e| { - Error::custom(format!("Invalid hex value {}: {}", value, e).as_str()) - })?, - _ => $inner::from_str(value).map_err(|e| { - Error::custom(format!("Invalid hex value {}: {}", value, e).as_str()) - })?, - }; - - Ok($name(value)) - } - - fn visit_string(self, value: String) -> Result where E: Error { - self.visit_str(value.as_ref()) - } - } - - deserializer.deserialize_any(HashVisitor) - } - } - - impl Serialize for $name { - fn serialize(&self, serializer: S) -> Result where S: Serializer { - let mut hex = "0x".to_owned(); - hex.push_str(&self.0.to_hex()); - serializer.serialize_str(&hex) - } - } - } + ($name: ident, $inner: ident) => { + /// Lenient hash json deserialization for test json files. + #[derive(Default, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone)] + pub struct $name(pub $inner); + + impl From<$name> for $inner { + fn from(other: $name) -> $inner { + other.0 + } + } + + impl From<$inner> for $name { + fn from(i: $inner) -> Self { + $name(i) + } + } + + impl<'a> Deserialize<'a> for $name { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + struct HashVisitor; + + impl<'b> Visitor<'b> for HashVisitor { + type Value = $name; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a 0x-prefixed hex-encoded hash") + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + let value = match value.len() { + 0 => $inner::from(0), + 2 if value == "0x" => $inner::from(0), + _ if value.starts_with("0x") => { + $inner::from_str(&value[2..]).map_err(|e| { + Error::custom( + format!("Invalid hex value {}: {}", value, e).as_str(), + ) + })? + } + _ => $inner::from_str(value).map_err(|e| { + Error::custom( + format!("Invalid hex value {}: {}", value, e).as_str(), + ) + })?, + }; + + Ok($name(value)) + } + + fn visit_string(self, value: String) -> Result + where + E: Error, + { + self.visit_str(value.as_ref()) + } + } + + deserializer.deserialize_any(HashVisitor) + } + } + + impl Serialize for $name { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut hex = "0x".to_owned(); + hex.push_str(&self.0.to_hex()); + serializer.serialize_str(&hex) + } + } + }; } impl_hash!(H64, Hash64); @@ -96,23 +115,34 @@ impl_hash!(Bloom, Hash2048); #[cfg(test)] mod test { - use std::str::FromStr; - use serde_json; - use ethereum_types; - use hash::H256; - - #[test] - fn hash_deserialization() { - let s = r#"["", "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae"]"#; - let deserialized: Vec = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, vec![ - H256(ethereum_types::H256::from(0)), - H256(ethereum_types::H256::from_str("5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae").unwrap()) - ]); - } - - #[test] - fn hash_into() { - assert_eq!(ethereum_types::H256::from(0), H256(ethereum_types::H256::from(0)).into()); - } + use ethereum_types; + use hash::H256; + use serde_json; + use std::str::FromStr; + + #[test] + fn hash_deserialization() { + let s = r#"["", "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae"]"#; + let deserialized: Vec = serde_json::from_str(s).unwrap(); + assert_eq!( + deserialized, + vec![ + H256(ethereum_types::H256::from(0)), + H256( + ethereum_types::H256::from_str( + "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae" + ) + .unwrap() + ) + ] + ); + } + + #[test] + fn hash_into() { + assert_eq!( + ethereum_types::H256::from(0), + H256(ethereum_types::H256::from(0)).into() + ); + } } diff --git a/json/src/lib.rs b/json/src/lib.rs index af5d93edfa6..c0641c4705f 100644 --- a/json/src/lib.rs +++ b/json/src/lib.rs @@ -1,33 +1,41 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . +extern crate ethereum_types; extern crate rustc_hex; extern crate serde; extern crate serde_json; -extern crate ethereum_types; -#[macro_use] extern crate serde_derive; +#[macro_use] +extern crate serde_derive; + +#[cfg(test)] +extern crate macros; + +#[cfg(test)] +#[macro_use] +extern crate maplit; -pub mod hash; -pub mod uint; -pub mod bytes; pub mod blockchain; -pub mod spec; -pub mod trie; -pub mod vm; +pub mod bytes; +pub mod hash; pub mod maybe; +pub mod spec; pub mod state; -pub mod transaction; pub mod test; +pub mod transaction; +pub mod trie; +pub mod uint; +pub mod vm; diff --git a/json/src/maybe.rs b/json/src/maybe.rs index 4fd2ca60e49..4d72a67355c 100644 --- a/json/src/maybe.rs +++ b/json/src/maybe.rs @@ -1,100 +1,120 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Deserializer of empty string values into optionals. -use std::fmt; -use std::marker::PhantomData; -use serde::{Deserialize, Deserializer}; -use serde::de::{Error, Visitor, IntoDeserializer}; +use serde::{ + de::{Error, IntoDeserializer, Visitor}, + Deserialize, Deserializer, +}; +use std::{fmt, marker::PhantomData}; /// Deserializer of empty string values into optionals. #[derive(Debug, PartialEq, Clone)] pub enum MaybeEmpty { - /// Some. - Some(T), - /// None. - None, + /// Some. + Some(T), + /// None. + None, } -impl<'a, T> Deserialize<'a> for MaybeEmpty where T: Deserialize<'a> { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> { - deserializer.deserialize_any(MaybeEmptyVisitor::new()) - } +impl<'a, T> Deserialize<'a> for MaybeEmpty +where + T: Deserialize<'a>, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + deserializer.deserialize_any(MaybeEmptyVisitor::new()) + } } struct MaybeEmptyVisitor { - _phantom: PhantomData + _phantom: PhantomData, } impl MaybeEmptyVisitor { - fn new() -> Self { - MaybeEmptyVisitor { - _phantom: PhantomData - } - } + fn new() -> Self { + MaybeEmptyVisitor { + _phantom: PhantomData, + } + } } -impl<'a, T> Visitor<'a> for MaybeEmptyVisitor where T: Deserialize<'a> { - type Value = MaybeEmpty; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "an empty string or string-encoded type") - } - - fn visit_str(self, value: &str) -> Result where E: Error { - self.visit_string(value.to_owned()) - } - - fn visit_string(self, value: String) -> Result where E: Error { - match value.is_empty() { - true => Ok(MaybeEmpty::None), - false => { - T::deserialize(value.into_deserializer()).map(MaybeEmpty::Some) - } - } - } +impl<'a, T> Visitor<'a> for MaybeEmptyVisitor +where + T: Deserialize<'a>, +{ + type Value = MaybeEmpty; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "an empty string or string-encoded type") + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + self.visit_string(value.to_owned()) + } + + fn visit_string(self, value: String) -> Result + where + E: Error, + { + match value.is_empty() { + true => Ok(MaybeEmpty::None), + false => T::deserialize(value.into_deserializer()).map(MaybeEmpty::Some), + } + } } impl Into> for MaybeEmpty { - fn into(self) -> Option { - match self { - MaybeEmpty::Some(s) => Some(s), - MaybeEmpty::None => None - } - } + fn into(self) -> Option { + match self { + MaybeEmpty::Some(s) => Some(s), + MaybeEmpty::None => None, + } + } } #[cfg(test)] mod tests { - use std::str::FromStr; - use serde_json; - use ethereum_types; - use hash::H256; - use maybe::MaybeEmpty; - - #[test] - fn maybe_deserialization() { - let s = r#"["", "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae"]"#; - let deserialized: Vec> = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, vec![ - MaybeEmpty::None, - MaybeEmpty::Some(H256(ethereum_types::H256::from_str("5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae").unwrap())) - ]); - } - + use ethereum_types; + use hash::H256; + use maybe::MaybeEmpty; + use serde_json; + use std::str::FromStr; + + #[test] + fn maybe_deserialization() { + let s = r#"["", "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae"]"#; + let deserialized: Vec> = serde_json::from_str(s).unwrap(); + assert_eq!( + deserialized, + vec![ + MaybeEmpty::None, + MaybeEmpty::Some(H256( + ethereum_types::H256::from_str( + "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae" + ) + .unwrap() + )) + ] + ); + } } diff --git a/json/src/spec/account.rs b/json/src/spec/account.rs index 3d18ecd6c24..0f920db4c73 100644 --- a/json/src/spec/account.rs +++ b/json/src/spec/account.rs @@ -1,144 +1,168 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Spec account deserialization. use std::collections::BTreeMap; -use uint::Uint; + use bytes::Bytes; -use spec::builtin::Builtin; +use spec::builtin::BuiltinCompat; +use uint::Uint; /// Spec account. -#[derive(Debug, PartialEq, Deserialize)] +#[derive(Clone, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] +#[serde(rename_all = "camelCase")] pub struct Account { - /// Builtin contract. - pub builtin: Option, - /// Balance. - pub balance: Option, - /// Nonce. - pub nonce: Option, - /// Code. - pub code: Option, - /// Storage. - pub storage: Option>, - /// Constructor. - pub constructor: Option, + /// Builtin contract. + pub builtin: Option, + /// Balance. + pub balance: Option, + /// Nonce. + pub nonce: Option, + /// Code. + pub code: Option, + /// Storage. + pub storage: Option>, + /// Constructor. + pub constructor: Option, } impl Account { - /// Returns true if account does not have nonce, balance, code and storage. - pub fn is_empty(&self) -> bool { - self.balance.is_none() && self.nonce.is_none() && self.code.is_none() && self.storage.is_none() - } + /// Returns true if account does not have nonce, balance, code and storage. + pub fn is_empty(&self) -> bool { + self.balance.is_none() + && self.nonce.is_none() + && self.code.is_none() + && self.storage.is_none() + } } #[cfg(test)] mod tests { - use std::collections::BTreeMap; - use serde_json; - use spec::account::Account; - use ethereum_types::U256; - use uint::Uint; - use bytes::Bytes; - - #[test] - fn account_balance_missing_not_empty() { - let s = r#"{ + use bytes::Bytes; + use ethereum_types::U256; + use serde_json; + use spec::account::Account; + use std::collections::BTreeMap; + use uint::Uint; + + #[test] + fn account_balance_missing_not_empty() { + let s = r#"{ "nonce": "0", "code": "1234", "storage": { "0x7fffffffffffffff7fffffffffffffff": "0x1" } }"#; - let deserialized: Account = serde_json::from_str(s).unwrap(); - assert!(!deserialized.is_empty()); - } + let deserialized: Account = serde_json::from_str(s).unwrap(); + assert!(!deserialized.is_empty()); + } - #[test] - fn account_nonce_missing_not_empty() { - let s = r#"{ + #[test] + fn account_nonce_missing_not_empty() { + let s = r#"{ "balance": "1", "code": "1234", "storage": { "0x7fffffffffffffff7fffffffffffffff": "0x1" } }"#; - let deserialized: Account = serde_json::from_str(s).unwrap(); - assert!(!deserialized.is_empty()); - } + let deserialized: Account = serde_json::from_str(s).unwrap(); + assert!(!deserialized.is_empty()); + } - #[test] - fn account_code_missing_not_empty() { - let s = r#"{ + #[test] + fn account_code_missing_not_empty() { + let s = r#"{ "balance": "1", "nonce": "0", "storage": { "0x7fffffffffffffff7fffffffffffffff": "0x1" } }"#; - let deserialized: Account = serde_json::from_str(s).unwrap(); - assert!(!deserialized.is_empty()); - } + let deserialized: Account = serde_json::from_str(s).unwrap(); + assert!(!deserialized.is_empty()); + } - #[test] - fn account_storage_missing_not_empty() { - let s = r#"{ + #[test] + fn account_storage_missing_not_empty() { + let s = r#"{ "balance": "1", "nonce": "0", "code": "1234" }"#; - let deserialized: Account = serde_json::from_str(s).unwrap(); - assert!(!deserialized.is_empty()); - } - - #[test] - fn account_empty() { - let s = r#"{ - "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } + let deserialized: Account = serde_json::from_str(s).unwrap(); + assert!(!deserialized.is_empty()); + } + + #[test] + fn account_empty() { + let s = r#"{ + "builtin": { + "name": "ecrecover", + "pricing": { + "linear": { + "base": 3000, + "word": 0 + } + } + } }"#; - let deserialized: Account = serde_json::from_str(s).unwrap(); - assert!(deserialized.is_empty()); - } + let deserialized: Account = serde_json::from_str(s).unwrap(); + assert!(deserialized.is_empty()); + } - #[test] - fn account_deserialization() { - let s = r#"{ + #[test] + fn account_deserialization() { + let s = r#"{ "balance": "1", "nonce": "0", - "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } }, - "code": "1234" + "code": "1234", + "builtin": { + "name": "ecrecover", + "pricing": { + "linear": { + "base": 3000, + "word": 0 + } + } + } }"#; - let deserialized: Account = serde_json::from_str(s).unwrap(); - assert!(!deserialized.is_empty()); - assert_eq!(deserialized.balance.unwrap(), Uint(U256::from(1))); - assert_eq!(deserialized.nonce.unwrap(), Uint(U256::from(0))); - assert_eq!(deserialized.code.unwrap(), Bytes::new(vec![0x12, 0x34])); - assert!(deserialized.builtin.is_some()); // Further tested in builtin.rs - } - - #[test] - fn account_storage_deserialization() { - let s = r#"{ + let deserialized: Account = serde_json::from_str(s).unwrap(); + assert!(!deserialized.is_empty()); + assert_eq!(deserialized.balance.unwrap(), Uint(U256::from(1))); + assert_eq!(deserialized.nonce.unwrap(), Uint(U256::from(0))); + assert_eq!(deserialized.code.unwrap(), Bytes::new(vec![0x12, 0x34])); + assert!(deserialized.builtin.is_some()); // Further tested in builtin.rs + } + + #[test] + fn account_storage_deserialization() { + let s = r#"{ "balance": "1", "nonce": "0", "code": "1234", "storage": { "0x7fffffffffffffff7fffffffffffffff": "0x1" } }"#; - let deserialized: Account = serde_json::from_str(s).unwrap(); - assert!(!deserialized.is_empty()); - assert_eq!(deserialized.balance.unwrap(), Uint(U256::from(1))); - assert_eq!(deserialized.nonce.unwrap(), Uint(U256::from(0))); - assert_eq!(deserialized.code.unwrap(), Bytes::new(vec![0x12, 0x34])); - let mut storage = BTreeMap::new(); - storage.insert(Uint(U256::from("7fffffffffffffff7fffffffffffffff")), Uint(U256::from(1))); - assert_eq!(deserialized.storage.unwrap(), storage); - } + let deserialized: Account = serde_json::from_str(s).unwrap(); + assert!(!deserialized.is_empty()); + assert_eq!(deserialized.balance.unwrap(), Uint(U256::from(1))); + assert_eq!(deserialized.nonce.unwrap(), Uint(U256::from(0))); + assert_eq!(deserialized.code.unwrap(), Bytes::new(vec![0x12, 0x34])); + let mut storage = BTreeMap::new(); + storage.insert( + Uint(U256::from("7fffffffffffffff7fffffffffffffff")), + Uint(U256::from(1)), + ); + assert_eq!(deserialized.storage.unwrap(), storage); + } } diff --git a/json/src/spec/authority_round.rs b/json/src/spec/authority_round.rs index 61936a5f9a2..15699c33a7b 100644 --- a/json/src/spec/authority_round.rs +++ b/json/src/spec/authority_round.rs @@ -1,85 +1,84 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Authority params deserialization. +use super::ValidatorSet; +use bytes::Bytes; use hash::Address; use uint::Uint; -use bytes::Bytes; -use super::ValidatorSet; /// Authority params deserialization. #[derive(Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub struct AuthorityRoundParams { - /// Block duration, in seconds. - pub step_duration: Uint, - /// Valid authorities - pub validators: ValidatorSet, - /// Starting step. Determined automatically if not specified. - /// To be used for testing only. - pub start_step: Option, - /// Block at which score validation should start. - pub validate_score_transition: Option, - /// Block from which monotonic steps start. - pub validate_step_transition: Option, - /// Whether transitions should be immediate. - pub immediate_transitions: Option, - /// Reward per block in wei. - pub block_reward: Option, - /// Block at which the block reward contract should start being used. - pub block_reward_contract_transition: Option, - /// Block reward contract address (setting the block reward contract - /// overrides the static block reward definition). - pub block_reward_contract_address: Option
, - /// Block reward code. This overrides the block reward contract address. - pub block_reward_contract_code: Option, - /// Block at which maximum uncle count should be considered. - pub maximum_uncle_count_transition: Option, - /// Maximum number of accepted uncles. - pub maximum_uncle_count: Option, - /// Block at which empty step messages should start. - pub empty_steps_transition: Option, - /// Maximum number of accepted empty steps. - pub maximum_empty_steps: Option, - /// Strict validation of empty steps transition block. - pub strict_empty_steps_transition: Option, + /// Block duration, in seconds. + pub step_duration: Uint, + /// Valid authorities + pub validators: ValidatorSet, + /// Starting step. Determined automatically if not specified. + /// To be used for testing only. + pub start_step: Option, + /// Block at which score validation should start. + pub validate_score_transition: Option, + /// Block from which monotonic steps start. + pub validate_step_transition: Option, + /// Whether transitions should be immediate. + pub immediate_transitions: Option, + /// Reward per block in wei. + pub block_reward: Option, + /// Block at which the block reward contract should start being used. + pub block_reward_contract_transition: Option, + /// Block reward contract address (setting the block reward contract + /// overrides the static block reward definition). + pub block_reward_contract_address: Option
, + /// Block reward code. This overrides the block reward contract address. + pub block_reward_contract_code: Option, + /// Block at which maximum uncle count should be considered. + pub maximum_uncle_count_transition: Option, + /// Maximum number of accepted uncles. + pub maximum_uncle_count: Option, + /// Block at which empty step messages should start. + pub empty_steps_transition: Option, + /// Maximum number of accepted empty steps. + pub maximum_empty_steps: Option, + /// Strict validation of empty steps transition block. + pub strict_empty_steps_transition: Option, } /// Authority engine deserialization. #[derive(Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] pub struct AuthorityRound { - /// Ethash params. - pub params: AuthorityRoundParams, + /// Ethash params. + pub params: AuthorityRoundParams, } #[cfg(test)] mod tests { - use ethereum_types::{U256, H160}; - use uint::Uint; - use serde_json; - use hash::Address; - use spec::validator_set::ValidatorSet; - use spec::authority_round::AuthorityRound; + use ethereum_types::{H160, U256}; + use hash::Address; + use serde_json; + use spec::{authority_round::AuthorityRound, validator_set::ValidatorSet}; + use uint::Uint; - #[test] - fn authority_round_deserialization() { - let s = r#"{ + #[test] + fn authority_round_deserialization() { + let s = r#"{ "params": { "stepDuration": "0x02", "validators": { @@ -93,13 +92,23 @@ mod tests { } }"#; - let deserialized: AuthorityRound = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized.params.step_duration, Uint(U256::from(0x02))); - assert_eq!(deserialized.params.validators, ValidatorSet::List(vec![Address(H160::from("0xc6d9d2cd449a754c494264e1809c50e34d64562b"))])); - assert_eq!(deserialized.params.start_step, Some(Uint(U256::from(24)))); - assert_eq!(deserialized.params.immediate_transitions, None); - assert_eq!(deserialized.params.maximum_uncle_count_transition, Some(Uint(10_000_000.into()))); - assert_eq!(deserialized.params.maximum_uncle_count, Some(Uint(5.into()))); - - } + let deserialized: AuthorityRound = serde_json::from_str(s).unwrap(); + assert_eq!(deserialized.params.step_duration, Uint(U256::from(0x02))); + assert_eq!( + deserialized.params.validators, + ValidatorSet::List(vec![Address(H160::from( + "0xc6d9d2cd449a754c494264e1809c50e34d64562b" + ))]) + ); + assert_eq!(deserialized.params.start_step, Some(Uint(U256::from(24)))); + assert_eq!(deserialized.params.immediate_transitions, None); + assert_eq!( + deserialized.params.maximum_uncle_count_transition, + Some(Uint(10_000_000.into())) + ); + assert_eq!( + deserialized.params.maximum_uncle_count, + Some(Uint(5.into())) + ); + } } diff --git a/json/src/spec/basic_authority.rs b/json/src/spec/basic_authority.rs index 195b89bebb6..0f0ebc17948 100644 --- a/json/src/spec/basic_authority.rs +++ b/json/src/spec/basic_authority.rs @@ -1,55 +1,54 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Authority params deserialization. -use uint::Uint; use super::ValidatorSet; +use uint::Uint; /// Authority params deserialization. #[derive(Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub struct BasicAuthorityParams { - /// Block duration. - pub duration_limit: Uint, - /// Valid authorities - pub validators: ValidatorSet, + /// Block duration. + pub duration_limit: Uint, + /// Valid authorities + pub validators: ValidatorSet, } /// Authority engine deserialization. #[derive(Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] pub struct BasicAuthority { - /// Ethash params. - pub params: BasicAuthorityParams, + /// Ethash params. + pub params: BasicAuthorityParams, } #[cfg(test)] mod tests { - use serde_json; - use uint::Uint; - use ethereum_types::{U256, H160}; - use hash::Address; - use spec::basic_authority::BasicAuthority; - use spec::validator_set::ValidatorSet; + use ethereum_types::{H160, U256}; + use hash::Address; + use serde_json; + use spec::{basic_authority::BasicAuthority, validator_set::ValidatorSet}; + use uint::Uint; - #[test] - fn basic_authority_deserialization() { - let s = r#"{ + #[test] + fn basic_authority_deserialization() { + let s = r#"{ "params": { "durationLimit": "0x0d", "validators" : { @@ -58,10 +57,12 @@ mod tests { } }"#; - let deserialized: BasicAuthority = serde_json::from_str(s).unwrap(); + let deserialized: BasicAuthority = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized.params.duration_limit, Uint(U256::from(0x0d))); - let vs = ValidatorSet::List(vec![Address(H160::from("0xc6d9d2cd449a754c494264e1809c50e34d64562b"))]); - assert_eq!(deserialized.params.validators, vs); - } + assert_eq!(deserialized.params.duration_limit, Uint(U256::from(0x0d))); + let vs = ValidatorSet::List(vec![Address(H160::from( + "0xc6d9d2cd449a754c494264e1809c50e34d64562b", + ))]); + assert_eq!(deserialized.params.validators, vs); + } } diff --git a/json/src/spec/builtin.rs b/json/src/spec/builtin.rs index 930e0bc9a1b..c106f691343 100644 --- a/json/src/spec/builtin.rs +++ b/json/src/spec/builtin.rs @@ -1,49 +1,92 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Spec builtin deserialization. +use std::collections::BTreeMap; use uint::Uint; /// Linear pricing. #[derive(Debug, PartialEq, Deserialize, Clone)] #[serde(deny_unknown_fields)] pub struct Linear { - /// Base price. - pub base: usize, - /// Price for word. - pub word: usize, + /// Base price. + pub base: u64, + /// Price for word. + pub word: u64, } /// Pricing for modular exponentiation. #[derive(Debug, PartialEq, Deserialize, Clone)] #[serde(deny_unknown_fields)] pub struct Modexp { - /// Price divisor. - pub divisor: usize, + /// Price divisor. + pub divisor: u64, +} + +/// Pricing for constant alt_bn128 operations (ECADD and ECMUL) +#[derive(Debug, PartialEq, Deserialize, Clone)] +#[serde(deny_unknown_fields)] +pub struct AltBn128ConstOperations { + /// price + pub price: u64, } /// Pricing for alt_bn128_pairing. #[derive(Debug, PartialEq, Deserialize, Clone)] #[serde(deny_unknown_fields)] pub struct AltBn128Pairing { - /// Base price. - pub base: usize, - /// Price per point pair. - pub pair: usize, + /// Base price. + pub base: u64, + /// Price per point pair. + pub pair: u64, +} + +/// Bls12 pairing price +#[derive(Debug, PartialEq, Deserialize, Clone)] +#[serde(deny_unknown_fields)] +pub struct Bls12Pairing { + /// Price per final exp + pub base: u64, + /// Price per pair (Miller loop) + pub pair: u64, +} + +/// Pricing for constant Bls12 operations (ADD and MUL in G1 and G2, as well as mappings) +#[derive(Debug, PartialEq, Deserialize, Clone)] +#[serde(deny_unknown_fields)] +pub struct Bls12ConstOperations { + /// Fixed price. + pub price: u64, +} + +/// Pricing for constant Bls12 operations (ADD and MUL in G1, as well as mappings) +#[derive(Debug, PartialEq, Deserialize, Clone)] +#[serde(deny_unknown_fields)] +pub struct Bls12G1Multiexp { + /// Base const of the operation (G1 or G2 multiplication) + pub base: u64, +} + +/// Pricing for constant Bls12 operations (ADD and MUL in G2, as well as mappings) +#[derive(Debug, PartialEq, Deserialize, Clone)] +#[serde(deny_unknown_fields)] +pub struct Bls12G2Multiexp { + /// Base const of the operation (G1 or G2 multiplication) + pub base: u64, } /// Pricing variants. @@ -51,55 +94,269 @@ pub struct AltBn128Pairing { #[serde(deny_unknown_fields)] #[serde(rename_all = "snake_case")] pub enum Pricing { - /// Linear pricing. - Linear(Linear), - /// Pricing for modular exponentiation. - Modexp(Modexp), - /// Pricing for alt_bn128_pairing exponentiation. - AltBn128Pairing(AltBn128Pairing), + /// Pricing for Blake2 compression function: each call costs the same amount per round. + Blake2F { + /// Price per round of Blake2 compression function. + gas_per_round: u64, + }, + /// Linear pricing. + Linear(Linear), + /// Pricing for modular exponentiation. + Modexp(Modexp), + /// Pricing for alt_bn128_pairing exponentiation. + AltBn128Pairing(AltBn128Pairing), + /// Pricing for constant alt_bn128 operations + AltBn128ConstOperations(AltBn128ConstOperations), + /// Pricing of constant price bls12_381 operations + Bls12ConstOperations(Bls12ConstOperations), + /// Pricing of pairing bls12_381 operation + Bls12Pairing(Bls12Pairing), + /// Pricing of bls12_381 multiexp operations in G1 + Bls12G1Multiexp(Bls12G1Multiexp), + /// Pricing of bls12_381 multiexp operations in G2 + Bls12G2Multiexp(Bls12G2Multiexp), } -/// Spec builtin. +/// Builtin compability layer #[derive(Debug, PartialEq, Deserialize, Clone)] #[serde(deny_unknown_fields)] +pub struct BuiltinCompat { + /// Builtin name. + name: String, + /// Builtin pricing. + pricing: PricingCompat, + /// Activation block. + activate_at: Option, +} + +/// Spec builtin. +#[derive(Debug, PartialEq, Clone)] pub struct Builtin { - /// Builtin name. - pub name: String, - /// Builtin pricing. - pub pricing: Pricing, - /// Activation block. - pub activate_at: Option, + /// Builtin name. + pub name: String, + /// Builtin pricing. + pub pricing: BTreeMap, +} + +impl From for Builtin { + fn from(legacy: BuiltinCompat) -> Self { + let pricing = match legacy.pricing { + PricingCompat::Single(pricing) => { + let mut map = BTreeMap::new(); + let activate_at: u64 = legacy.activate_at.map_or(0, Into::into); + map.insert( + activate_at, + PricingAt { + info: None, + price: pricing, + }, + ); + map + } + PricingCompat::Multi(pricings) => { + pricings.into_iter().map(|(a, p)| (a.into(), p)).collect() + } + }; + Self { + name: legacy.name, + pricing, + } + } +} + +/// Compability layer for different pricings +#[derive(Debug, PartialEq, Deserialize, Clone)] +#[serde(rename_all = "snake_case")] +#[serde(deny_unknown_fields)] +#[serde(untagged)] +enum PricingCompat { + /// Single builtin + Single(Pricing), + /// Multiple builtins + Multi(BTreeMap), +} + +/// Price for a builtin, with the block number to activate it on +#[derive(Debug, PartialEq, Deserialize, Clone)] +#[serde(deny_unknown_fields)] +pub struct PricingAt { + /// Description of the activation, e.g. "PunyPony HF, March 12, 2025". + pub info: Option, + /// Builtin pricing. + pub price: Pricing, } #[cfg(test)] mod tests { - use serde_json; - use spec::builtin::{Builtin, Pricing, Linear, Modexp}; - use uint::Uint; + use super::{ + AltBn128ConstOperations, BTreeMap, Bls12G1Multiexp, Bls12G2Multiexp, Builtin, + BuiltinCompat, Linear, Modexp, Pricing, PricingAt, + }; + use macros::map; + use serde_json; - #[test] - fn builtin_deserialization() { - let s = r#"{ + #[test] + fn builtin_deserialization() { + let s = r#"{ "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } }"#; - let deserialized: Builtin = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized.name, "ecrecover"); - assert_eq!(deserialized.pricing, Pricing::Linear(Linear { base: 3000, word: 0 })); - assert!(deserialized.activate_at.is_none()); - } - - #[test] - fn activate_at() { - let s = r#"{ + let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); + assert_eq!(builtin.name, "ecrecover"); + assert_eq!( + builtin.pricing, + map![ + 0 => PricingAt { + info: None, + price: Pricing::Linear(Linear { base: 3000, word: 0 }) + } + ] + ); + } + + #[test] + fn deserialize_multiple_pricings() { + let s = r#"{ + "name": "ecrecover", + "pricing": { + "0": { + "price": {"linear": { "base": 3000, "word": 0 }} + }, + "500": { + "info": "enable fake EIP at block 500", + "price": {"linear": { "base": 10, "word": 0 }} + } + } + }"#; + let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); + assert_eq!(builtin.name, "ecrecover"); + assert_eq!( + builtin.pricing, + map![ + 0 => PricingAt { + info: None, + price: Pricing::Linear(Linear { base: 3000, word: 0 }) + }, + 500 => PricingAt { + info: Some(String::from("enable fake EIP at block 500")), + price: Pricing::Linear(Linear { base: 10, word: 0 }) + } + ] + ); + } + + #[test] + fn deserialization_blake2_f_builtin() { + let s = r#"{ + "name": "blake2_f", + "activate_at": "0xffffff", + "pricing": { "blake2_f": { "gas_per_round": 123 } } + }"#; + let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); + assert_eq!(builtin.name, "blake2_f"); + assert_eq!( + builtin.pricing, + map![ + 0xffffff => PricingAt { + info: None, + price: Pricing::Blake2F { gas_per_round: 123 } + } + ] + ); + } + + #[test] + fn deserialization_alt_bn128_const_operations() { + let s = r#"{ + "name": "alt_bn128_mul", + "pricing": { + "100500": { + "price": { "alt_bn128_const_operations": { "price": 123 }} + } + } + }"#; + let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); + assert_eq!(builtin.name, "alt_bn128_mul"); + assert_eq!( + builtin.pricing, + map![ + 100500 => PricingAt { + info: None, + price: Pricing::AltBn128ConstOperations(AltBn128ConstOperations { + price: 123, + }), + } + ] + ); + } + + #[test] + fn activate_at() { + let s = r#"{ "name": "late_start", "activate_at": 100000, "pricing": { "modexp": { "divisor": 5 } } }"#; - let deserialized: Builtin = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized.name, "late_start"); - assert_eq!(deserialized.pricing, Pricing::Modexp(Modexp { divisor: 5 })); - assert_eq!(deserialized.activate_at, Some(Uint(100000.into()))); - } + let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); + assert_eq!(builtin.name, "late_start"); + assert_eq!( + builtin.pricing, + map![ + 100_000 => PricingAt { + info: None, + price: Pricing::Modexp(Modexp { divisor: 5 }) + } + ] + ); + } + #[test] + fn deserialization_bls12_381_multiexp_operation() { + let s = r#"{ + "name": "bls12_381_g1_multiexp", + "pricing": { + "10000000": { + "price": { "bls12_g1_multiexp": { "base": 12000}} + } + } + }"#; + let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); + assert_eq!(builtin.name, "bls12_381_g1_multiexp"); + assert_eq!( + builtin.pricing, + btreemap![ + 10000000 => PricingAt { + info: None, + price: Pricing::Bls12G1Multiexp(Bls12G1Multiexp{ + base: 12000 + }), + } + ] + ); + } + + #[test] + fn deserialization_bls12_381_multiexp_operation_in_g2() { + let s = r#"{ + "name": "bls12_381_g2_multiexp", + "pricing": { + "10000000": { + "price": { "bls12_g2_multiexp": { "base": 55000}} + } + } + }"#; + let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); + assert_eq!(builtin.name, "bls12_381_g2_multiexp"); + assert_eq!( + builtin.pricing, + btreemap![ + 10000000 => PricingAt { + info: None, + price: Pricing::Bls12G2Multiexp(Bls12G2Multiexp{ + base: 55000 + }), + } + ] + ); + } } diff --git a/json/src/spec/clique.rs b/json/src/spec/clique.rs index 64be9c569ac..22c2a1112c0 100644 --- a/json/src/spec/clique.rs +++ b/json/src/spec/clique.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Clique params deserialization. @@ -21,37 +21,35 @@ use std::num::NonZeroU64; /// Clique params deserialization. #[derive(Debug, PartialEq, Deserialize)] pub struct CliqueParams { - /// period as defined in EIP - pub period: Option, - /// epoch length as defined in EIP - pub epoch: Option + /// period as defined in EIP + pub period: Option, + /// epoch length as defined in EIP + pub epoch: Option, } /// Clique engine deserialization. #[derive(Debug, PartialEq, Deserialize)] pub struct Clique { - /// CliqueEngine params - pub params: CliqueParams, + /// CliqueEngine params + pub params: CliqueParams, } #[cfg(test)] mod tests { - use serde_json; - use uint::Uint; - use ethereum_types::U256; - use super::*; - - #[test] - fn clique_deserialization() { - let s = r#"{ + use super::*; + use serde_json; + + #[test] + fn clique_deserialization() { + let s = r#"{ "params": { "period": 5, "epoch": 30000 } }"#; - let deserialized: Clique = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized.params.period, Some(5u64)); - assert_eq!(deserialized.params.epoch, NonZeroU64::new(30000)); - } + let deserialized: Clique = serde_json::from_str(s).unwrap(); + assert_eq!(deserialized.params.period, Some(5u64)); + assert_eq!(deserialized.params.epoch, NonZeroU64::new(30000)); + } } diff --git a/json/src/spec/engine.rs b/json/src/spec/engine.rs index cfa1d8cafd1..2a6e9faf6f1 100644 --- a/json/src/spec/engine.rs +++ b/json/src/spec/engine.rs @@ -1,51 +1,51 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Engine deserialization. -use super::{Ethash, BasicAuthority, AuthorityRound, NullEngine, InstantSeal, Clique}; +use super::{AuthorityRound, BasicAuthority, Clique, Ethash, InstantSeal, NullEngine}; /// Engine deserialization. #[derive(Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub enum Engine { - /// Null engine. - Null(NullEngine), - /// Instantly sealing engine. - InstantSeal(Option), - /// Ethash engine. - #[serde(rename = "Ethash")] - Ethash(Ethash), - /// BasicAuthority engine. - BasicAuthority(BasicAuthority), - /// AuthorityRound engine. - AuthorityRound(AuthorityRound), - /// Clique engine. - Clique(Clique) + /// Null engine. + Null(NullEngine), + /// Instantly sealing engine. + InstantSeal(Option), + /// Ethash engine. + #[serde(rename = "Ethash")] + Ethash(Ethash), + /// BasicAuthority engine. + BasicAuthority(BasicAuthority), + /// AuthorityRound engine. + AuthorityRound(AuthorityRound), + /// Clique engine. + Clique(Clique), } #[cfg(test)] mod tests { - use serde_json; - use spec::Engine; + use serde_json; + use spec::Engine; - #[test] - fn engine_deserialization() { - let s = r#"{ + #[test] + fn engine_deserialization() { + let s = r#"{ "null": { "params": { "blockReward": "0x0d" @@ -53,33 +53,33 @@ mod tests { } }"#; - let deserialized: Engine = serde_json::from_str(s).unwrap(); - match deserialized { - Engine::Null(_) => {}, // unit test in its own file. - _ => panic!(), - } + let deserialized: Engine = serde_json::from_str(s).unwrap(); + match deserialized { + Engine::Null(_) => {} // unit test in its own file. + _ => panic!(), + } - let s = r#"{ + let s = r#"{ "instantSeal": {"params": {}} }"#; - let deserialized: Engine = serde_json::from_str(s).unwrap(); - match deserialized { - Engine::InstantSeal(_) => {}, // instant seal is unit tested in its own file. - _ => panic!(), - }; + let deserialized: Engine = serde_json::from_str(s).unwrap(); + match deserialized { + Engine::InstantSeal(_) => {} // instant seal is unit tested in its own file. + _ => panic!(), + }; - let s = r#"{ + let s = r#"{ "instantSeal": null }"#; - let deserialized: Engine = serde_json::from_str(s).unwrap(); - match deserialized { - Engine::InstantSeal(_) => {}, // instant seal is unit tested in its own file. - _ => panic!(), - }; + let deserialized: Engine = serde_json::from_str(s).unwrap(); + match deserialized { + Engine::InstantSeal(_) => {} // instant seal is unit tested in its own file. + _ => panic!(), + }; - let s = r#"{ + let s = r#"{ "Ethash": { "params": { "minimumDifficulty": "0x020000", @@ -93,13 +93,13 @@ mod tests { } }"#; - let deserialized: Engine = serde_json::from_str(s).unwrap(); - match deserialized { - Engine::Ethash(_) => {}, // ethash is unit tested in its own file. - _ => panic!(), - }; + let deserialized: Engine = serde_json::from_str(s).unwrap(); + match deserialized { + Engine::Ethash(_) => {} // ethash is unit tested in its own file. + _ => panic!(), + }; - let s = r#"{ + let s = r#"{ "basicAuthority": { "params": { "durationLimit": "0x0d", @@ -109,13 +109,13 @@ mod tests { } } }"#; - let deserialized: Engine = serde_json::from_str(s).unwrap(); - match deserialized { - Engine::BasicAuthority(_) => {}, // basicAuthority is unit tested in its own file. - _ => panic!(), - }; + let deserialized: Engine = serde_json::from_str(s).unwrap(); + match deserialized { + Engine::BasicAuthority(_) => {} // basicAuthority is unit tested in its own file. + _ => panic!(), + }; - let s = r#"{ + let s = r#"{ "authorityRound": { "params": { "stepDuration": "0x02", @@ -127,13 +127,13 @@ mod tests { } } }"#; - let deserialized: Engine = serde_json::from_str(s).unwrap(); - match deserialized { - Engine::AuthorityRound(_) => {}, // AuthorityRound is unit tested in its own file. - _ => panic!(), - }; + let deserialized: Engine = serde_json::from_str(s).unwrap(); + match deserialized { + Engine::AuthorityRound(_) => {} // AuthorityRound is unit tested in its own file. + _ => panic!(), + }; - let s = r#"{ + let s = r#"{ "clique": { "params": { "period": 15, @@ -141,10 +141,10 @@ mod tests { } } }"#; - let deserialized: Engine = serde_json::from_str(s).unwrap(); - match deserialized { - Engine::Clique(_) => {}, // Clique is unit tested in its own file. - _ => panic!(), - }; - } + let deserialized: Engine = serde_json::from_str(s).unwrap(); + match deserialized { + Engine::Clique(_) => {} // Clique is unit tested in its own file. + _ => panic!(), + }; + } } diff --git a/json/src/spec/ethash.rs b/json/src/spec/ethash.rs index 6051ac90d8a..6d2ec0d40c6 100644 --- a/json/src/spec/ethash.rs +++ b/json/src/spec/ethash.rs @@ -1,33 +1,33 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Ethash params deserialization. -use std::collections::BTreeMap; -use uint::{self, Uint}; use bytes::Bytes; use hash::Address; +use std::collections::BTreeMap; +use uint::{self, Uint}; /// Deserializable doppelganger of block rewards for EthashParams #[derive(Clone, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] #[serde(untagged)] pub enum BlockReward { - Single(Uint), - Multi(BTreeMap), + Single(Uint), + Multi(BTreeMap), } /// Deserializable doppelganger of EthashParams. @@ -35,90 +35,85 @@ pub enum BlockReward { #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub struct EthashParams { - /// See main EthashParams docs. - #[serde(deserialize_with="uint::validate_non_zero")] - pub minimum_difficulty: Uint, - /// See main EthashParams docs. - #[serde(deserialize_with="uint::validate_non_zero")] - pub difficulty_bound_divisor: Uint, - /// See main EthashParams docs. - #[serde(default, deserialize_with="uint::validate_optional_non_zero")] - pub difficulty_increment_divisor: Option, - /// See main EthashParams docs. - #[serde(default, deserialize_with="uint::validate_optional_non_zero")] - pub metropolis_difficulty_increment_divisor: Option, - /// See main EthashParams docs. - pub duration_limit: Option, + /// See main EthashParams docs. + #[serde(deserialize_with = "uint::validate_non_zero")] + pub minimum_difficulty: Uint, + /// See main EthashParams docs. + #[serde(deserialize_with = "uint::validate_non_zero")] + pub difficulty_bound_divisor: Uint, + /// See main EthashParams docs. + #[serde(default, deserialize_with = "uint::validate_optional_non_zero")] + pub difficulty_increment_divisor: Option, + /// See main EthashParams docs. + #[serde(default, deserialize_with = "uint::validate_optional_non_zero")] + pub metropolis_difficulty_increment_divisor: Option, + /// See main EthashParams docs. + pub duration_limit: Option, - /// See main EthashParams docs. - pub homestead_transition: Option, - /// Reward per block in wei. - pub block_reward: Option, - /// Block at which the block reward contract should start being used. - pub block_reward_contract_transition: Option, - /// Block reward contract address (setting the block reward contract - /// overrides all other block reward parameters). - pub block_reward_contract_address: Option
, - /// Block reward code. This overrides the block reward contract address. - pub block_reward_contract_code: Option, + /// See main EthashParams docs. + pub homestead_transition: Option, + /// Reward per block in wei. + pub block_reward: Option, + /// Block at which the block reward contract should start being used. + pub block_reward_contract_transition: Option, + /// Block reward contract address (setting the block reward contract + /// overrides all other block reward parameters). + pub block_reward_contract_address: Option
, + /// Block reward code. This overrides the block reward contract address. + pub block_reward_contract_code: Option, - /// See main EthashParams docs. - pub dao_hardfork_transition: Option, - /// See main EthashParams docs. - pub dao_hardfork_beneficiary: Option
, - /// See main EthashParams docs. - pub dao_hardfork_accounts: Option>, + /// See main EthashParams docs. + pub dao_hardfork_transition: Option, + /// See main EthashParams docs. + pub dao_hardfork_beneficiary: Option
, + /// See main EthashParams docs. + pub dao_hardfork_accounts: Option>, - /// See main EthashParams docs. - pub difficulty_hardfork_transition: Option, - /// See main EthashParams docs. - #[serde(default, deserialize_with="uint::validate_optional_non_zero")] - pub difficulty_hardfork_bound_divisor: Option, - /// See main EthashParams docs. - pub bomb_defuse_transition: Option, + /// See main EthashParams docs. + pub difficulty_hardfork_transition: Option, + /// See main EthashParams docs. + #[serde(default, deserialize_with = "uint::validate_optional_non_zero")] + pub difficulty_hardfork_bound_divisor: Option, + /// See main EthashParams docs. + pub bomb_defuse_transition: Option, - /// See main EthashParams docs. - pub eip100b_transition: Option, + /// See main EthashParams docs. + pub eip100b_transition: Option, - /// See main EthashParams docs. - pub ecip1010_pause_transition: Option, - /// See main EthashParams docs. - pub ecip1010_continue_transition: Option, + /// See main EthashParams docs. + pub ecip1017_era_rounds: Option, - /// See main EthashParams docs. - pub ecip1017_era_rounds: Option, + /// Delays of difficulty bombs. + pub difficulty_bomb_delays: Option>, - /// Delays of difficulty bombs. - pub difficulty_bomb_delays: Option>, - - /// EXPIP-2 block height - pub expip2_transition: Option, - /// EXPIP-2 duration limit - pub expip2_duration_limit: Option, - /// Block to transition to progpow - #[serde(rename="progpowTransition")] - pub progpow_transition: Option, + /// EXPIP-2 block height + pub expip2_transition: Option, + /// EXPIP-2 duration limit + pub expip2_duration_limit: Option, + /// Block to transition to progpow + #[serde(rename = "progpowTransition")] + pub progpow_transition: Option, } /// Ethash engine deserialization. #[derive(Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] pub struct Ethash { - /// Ethash params. - pub params: EthashParams, + /// Ethash params. + pub params: EthashParams, } #[cfg(test)] mod tests { - use serde_json; - use uint::Uint; - use ethereum_types::{H160, U256}; - use hash::Address; - use spec::ethash::{Ethash, EthashParams, BlockReward}; + use ethereum_types::{H160, U256}; + use hash::Address; + use serde_json; + use spec::ethash::{BlockReward, Ethash, EthashParams}; + use uint::Uint; - #[test] - fn ethash_deserialization() { - let s = r#"{ + #[test] + fn ethash_deserialization() { + let s = r#"{ "params": { "minimumDifficulty": "0x020000", "difficultyBoundDivisor": "0x0800", @@ -156,109 +151,113 @@ mod tests { } }"#; - let deserialized: Ethash = serde_json::from_str(s).unwrap(); + let deserialized: Ethash = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, Ethash { - params: EthashParams { - minimum_difficulty: Uint(U256::from(0x020000)), - difficulty_bound_divisor: Uint(U256::from(0x0800)), - difficulty_increment_divisor: None, - metropolis_difficulty_increment_divisor: None, - duration_limit: Some(Uint(U256::from(0x0d))), - homestead_transition: Some(Uint(U256::from(0x42))), - block_reward: Some(BlockReward::Single(Uint(U256::from(0x100)))), - block_reward_contract_address: None, - block_reward_contract_code: None, - block_reward_contract_transition: None, - dao_hardfork_transition: Some(Uint(U256::from(0x08))), - dao_hardfork_beneficiary: Some(Address(H160::from("0xabcabcabcabcabcabcabcabcabcabcabcabcabca"))), - dao_hardfork_accounts: Some(vec![ - Address(H160::from("0x304a554a310c7e546dfe434669c62820b7d83490")), - Address(H160::from("0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79")), - Address(H160::from("0xfe24cdd8648121a43a7c86d289be4dd2951ed49f")), - Address(H160::from("0x17802f43a0137c506ba92291391a8a8f207f487d")), - Address(H160::from("0xb136707642a4ea12fb4bae820f03d2562ebff487")), - Address(H160::from("0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940")), - Address(H160::from("0xf14c14075d6c4ed84b86798af0956deef67365b5")), - Address(H160::from("0xca544e5c4687d109611d0f8f928b53a25af72448")), - Address(H160::from("0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c")), - Address(H160::from("0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7")), - Address(H160::from("0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6")), - Address(H160::from("0x2b3455ec7fedf16e646268bf88846bd7a2319bb2")), - Address(H160::from("0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a")), - Address(H160::from("0xd343b217de44030afaa275f54d31a9317c7f441e")), - Address(H160::from("0x84ef4b2357079cd7a7c69fd7a37cd0609a679106")), - Address(H160::from("0xda2fef9e4a3230988ff17df2165440f37e8b1708")), - Address(H160::from("0xf4c64518ea10f995918a454158c6b61407ea345c")), - Address(H160::from("0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97")), - Address(H160::from("0xbb9bc244d798123fde783fcc1c72d3bb8c189413")), - Address(H160::from("0x807640a13483f8ac783c557fcdf27be11ea4ac7a")), - ]), - difficulty_hardfork_transition: Some(Uint(U256::from(0x59d9))), - difficulty_hardfork_bound_divisor: Some(Uint(U256::from(0x0200))), - bomb_defuse_transition: Some(Uint(U256::from(0x41))), - eip100b_transition: Some(Uint(U256::from(0x42))), - ecip1010_pause_transition: None, - ecip1010_continue_transition: None, - ecip1017_era_rounds: None, - expip2_transition: None, - expip2_duration_limit: None, - progpow_transition: None, - difficulty_bomb_delays: None, - } - }); - } + assert_eq!( + deserialized, + Ethash { + params: EthashParams { + minimum_difficulty: Uint(U256::from(0x020000)), + difficulty_bound_divisor: Uint(U256::from(0x0800)), + difficulty_increment_divisor: None, + metropolis_difficulty_increment_divisor: None, + duration_limit: Some(Uint(U256::from(0x0d))), + homestead_transition: Some(Uint(U256::from(0x42))), + block_reward: Some(BlockReward::Single(Uint(U256::from(0x100)))), + block_reward_contract_address: None, + block_reward_contract_code: None, + block_reward_contract_transition: None, + dao_hardfork_transition: Some(Uint(U256::from(0x08))), + dao_hardfork_beneficiary: Some(Address(H160::from( + "0xabcabcabcabcabcabcabcabcabcabcabcabcabca" + ))), + dao_hardfork_accounts: Some(vec![ + Address(H160::from("0x304a554a310c7e546dfe434669c62820b7d83490")), + Address(H160::from("0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79")), + Address(H160::from("0xfe24cdd8648121a43a7c86d289be4dd2951ed49f")), + Address(H160::from("0x17802f43a0137c506ba92291391a8a8f207f487d")), + Address(H160::from("0xb136707642a4ea12fb4bae820f03d2562ebff487")), + Address(H160::from("0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940")), + Address(H160::from("0xf14c14075d6c4ed84b86798af0956deef67365b5")), + Address(H160::from("0xca544e5c4687d109611d0f8f928b53a25af72448")), + Address(H160::from("0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c")), + Address(H160::from("0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7")), + Address(H160::from("0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6")), + Address(H160::from("0x2b3455ec7fedf16e646268bf88846bd7a2319bb2")), + Address(H160::from("0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a")), + Address(H160::from("0xd343b217de44030afaa275f54d31a9317c7f441e")), + Address(H160::from("0x84ef4b2357079cd7a7c69fd7a37cd0609a679106")), + Address(H160::from("0xda2fef9e4a3230988ff17df2165440f37e8b1708")), + Address(H160::from("0xf4c64518ea10f995918a454158c6b61407ea345c")), + Address(H160::from("0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97")), + Address(H160::from("0xbb9bc244d798123fde783fcc1c72d3bb8c189413")), + Address(H160::from("0x807640a13483f8ac783c557fcdf27be11ea4ac7a")), + ]), + difficulty_hardfork_transition: Some(Uint(U256::from(0x59d9))), + difficulty_hardfork_bound_divisor: Some(Uint(U256::from(0x0200))), + bomb_defuse_transition: Some(Uint(U256::from(0x41))), + eip100b_transition: Some(Uint(U256::from(0x42))), + ecip1017_era_rounds: None, + expip2_transition: None, + expip2_duration_limit: None, + progpow_transition: None, + difficulty_bomb_delays: None, + } + } + ); + } - #[test] - fn ethash_deserialization_missing_optionals() { - let s = r#"{ + #[test] + fn ethash_deserialization_missing_optionals() { + let s = r#"{ "params": { "difficultyBoundDivisor": "0x0800", "minimumDifficulty": "0x020000" } }"#; - let deserialized: Ethash = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, Ethash { - params: EthashParams { - minimum_difficulty: Uint(U256::from(0x020000)), - difficulty_bound_divisor: Uint(U256::from(0x0800)), - difficulty_increment_divisor: None, - metropolis_difficulty_increment_divisor: None, - duration_limit: None, - homestead_transition: None, - block_reward: None, - block_reward_contract_address: None, - block_reward_contract_code: None, - block_reward_contract_transition: None, - dao_hardfork_transition: None, - dao_hardfork_beneficiary: None, - dao_hardfork_accounts: None, - difficulty_hardfork_transition: None, - difficulty_hardfork_bound_divisor: None, - bomb_defuse_transition: None, - eip100b_transition: None, - ecip1010_pause_transition: None, - ecip1010_continue_transition: None, - ecip1017_era_rounds: None, - expip2_transition: None, - expip2_duration_limit: None, - progpow_transition: None, - difficulty_bomb_delays: None, - } - }); - } + let deserialized: Ethash = serde_json::from_str(s).unwrap(); + assert_eq!( + deserialized, + Ethash { + params: EthashParams { + minimum_difficulty: Uint(U256::from(0x020000)), + difficulty_bound_divisor: Uint(U256::from(0x0800)), + difficulty_increment_divisor: None, + metropolis_difficulty_increment_divisor: None, + duration_limit: None, + homestead_transition: None, + block_reward: None, + block_reward_contract_address: None, + block_reward_contract_code: None, + block_reward_contract_transition: None, + dao_hardfork_transition: None, + dao_hardfork_beneficiary: None, + dao_hardfork_accounts: None, + difficulty_hardfork_transition: None, + difficulty_hardfork_bound_divisor: None, + bomb_defuse_transition: None, + eip100b_transition: None, + ecip1017_era_rounds: None, + expip2_transition: None, + expip2_duration_limit: None, + progpow_transition: None, + difficulty_bomb_delays: None, + } + } + ); + } - #[test] - #[should_panic(expected = "a non-zero value")] - fn test_zero_value_divisor() { - let s = r#"{ + #[test] + #[should_panic(expected = "a non-zero value")] + fn test_zero_value_divisor() { + let s = r#"{ "params": { "difficultyBoundDivisor": "0x0", "minimumDifficulty": "0x020000" } }"#; - let _deserialized: Ethash = serde_json::from_str(s).unwrap(); - } + let _deserialized: Ethash = serde_json::from_str(s).unwrap(); + } } diff --git a/json/src/spec/genesis.rs b/json/src/spec/genesis.rs index 1452bea0c17..0cbcd68795b 100644 --- a/json/src/spec/genesis.rs +++ b/json/src/spec/genesis.rs @@ -1,70 +1,69 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Spec genesis deserialization. -use uint::{Uint, self}; -use hash::{Address, H256}; use bytes::Bytes; +use hash::{Address, H256}; use spec::Seal; +use uint::{self, Uint}; /// Spec genesis. #[derive(Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub struct Genesis { - /// Seal. - pub seal: Seal, - /// Difficulty. - pub difficulty: Uint, - /// Block author, defaults to 0. - pub author: Option
, - /// Block timestamp, defaults to 0. - pub timestamp: Option, - /// Parent hash, defaults to 0. - pub parent_hash: Option, - /// Gas limit. - #[serde(deserialize_with="uint::validate_non_zero")] - pub gas_limit: Uint, - /// Transactions root. - pub transactions_root: Option, - /// Receipts root. - pub receipts_root: Option, - /// State root. - pub state_root: Option, - /// Gas used. - pub gas_used: Option, - /// Extra data. - pub extra_data: Option, + /// Seal. + pub seal: Seal, + /// Difficulty. + pub difficulty: Uint, + /// Block author, defaults to 0. + pub author: Option
, + /// Block timestamp, defaults to 0. + pub timestamp: Option, + /// Parent hash, defaults to 0. + pub parent_hash: Option, + /// Gas limit. + #[serde(deserialize_with = "uint::validate_non_zero")] + pub gas_limit: Uint, + /// Transactions root. + pub transactions_root: Option, + /// Receipts root. + pub receipts_root: Option, + /// State root. + pub state_root: Option, + /// Gas used. + pub gas_used: Option, + /// Extra data. + pub extra_data: Option, } #[cfg(test)] mod tests { - use serde_json; - use bytes::Bytes; - use uint::Uint; - use ethereum_types::{U256, H160, H64 as Eth64, H256 as Eth256}; - use hash::{H64, H256, Address}; - use spec::genesis::Genesis; - use spec::{Ethereum, Seal}; - use std::str::FromStr; + use bytes::Bytes; + use ethereum_types::{H160, H256 as Eth256, H64 as Eth64, U256}; + use hash::{Address, H256, H64}; + use serde_json; + use spec::{genesis::Genesis, Ethereum, Seal}; + use std::str::FromStr; + use uint::Uint; - #[test] - fn genesis_deserialization() { - let s = r#"{ + #[test] + fn genesis_deserialization() { + let s = r#"{ "difficulty": "0x400000000", "seal": { "ethereum": { @@ -79,22 +78,38 @@ mod tests { "gasLimit": "0x1388", "stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" }"#; - let deserialized: Genesis = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, Genesis { - seal: Seal::Ethereum(Ethereum { - nonce: H64(Eth64::from("0x00006d6f7264656e")), - mix_hash: H256(Eth256::from("0x0000000000000000000000000000000000000000000000000000000000000000")) - }), - difficulty: Uint(U256::from(0x400000000u64)), - author: Some(Address(H160::from("0x1000000000000000000000000000000000000001"))), - timestamp: Some(Uint(U256::from(0x07))), - parent_hash: Some(H256(Eth256::from("0x9000000000000000000000000000000000000000000000000000000000000000"))), - gas_limit: Uint(U256::from(0x1388)), - transactions_root: None, - receipts_root: None, - state_root: Some(H256(Eth256::from("0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"))), - gas_used: None, - extra_data: Some(Bytes::from_str("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa").unwrap()), - }); - } + let deserialized: Genesis = serde_json::from_str(s).unwrap(); + assert_eq!( + deserialized, + Genesis { + seal: Seal::Ethereum(Ethereum { + nonce: H64(Eth64::from("0x00006d6f7264656e")), + mix_hash: H256(Eth256::from( + "0x0000000000000000000000000000000000000000000000000000000000000000" + )) + }), + difficulty: Uint(U256::from(0x400000000u64)), + author: Some(Address(H160::from( + "0x1000000000000000000000000000000000000001" + ))), + timestamp: Some(Uint(U256::from(0x07))), + parent_hash: Some(H256(Eth256::from( + "0x9000000000000000000000000000000000000000000000000000000000000000" + ))), + gas_limit: Uint(U256::from(0x1388)), + transactions_root: None, + receipts_root: None, + state_root: Some(H256(Eth256::from( + "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" + ))), + gas_used: None, + extra_data: Some( + Bytes::from_str( + "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa" + ) + .unwrap() + ), + } + ); + } } diff --git a/json/src/spec/hardcoded_sync.rs b/json/src/spec/hardcoded_sync.rs deleted file mode 100644 index 381cd1f1acc..00000000000 --- a/json/src/spec/hardcoded_sync.rs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Spec hardcoded synchronization deserialization for the light client. - -use hash::H256; -use uint::Uint; - -/// Spec hardcoded sync. -#[derive(Debug, PartialEq, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -#[serde(rename_all = "camelCase")] -pub struct HardcodedSync { - /// Hexadecimal of the RLP encoding of the header of the block to start synchronization from. - pub header: String, - /// Total difficulty including the block of `header`. - pub total_difficulty: Uint, - /// Ordered trie roots of blocks before and including `header`. - #[serde(rename = "CHTs")] - pub chts: Vec, -} - -#[cfg(test)] -mod tests { - use serde_json; - use uint::Uint; - use ethereum_types::{U256, H256 as Eth256}; - use hash::H256; - use spec::hardcoded_sync::HardcodedSync; - - #[test] - fn hardcoded_sync_deserialization() { - let s = r#"{ - "header": "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23", - "totalDifficulty": "0x400000000", - "CHTs": [ - "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", - "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" - ] - }"#; - let deserialized: HardcodedSync = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, HardcodedSync { - header: String::from("f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23"), - total_difficulty: Uint(U256::from(0x400000000u64)), - chts: vec![ - H256(Eth256::from("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa")), - H256(Eth256::from("0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544")), - ] - }); - } -} diff --git a/json/src/spec/instant_seal.rs b/json/src/spec/instant_seal.rs index 2f1ad33e9bc..9aeebfa7088 100644 --- a/json/src/spec/instant_seal.rs +++ b/json/src/spec/instant_seal.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Instant seal engine params deserialization. @@ -21,15 +21,15 @@ #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub struct InstantSealParams { - /// Whether to enable millisecond timestamp. - #[serde(default)] - pub millisecond_timestamp: bool, + /// Whether to enable millisecond timestamp. + #[serde(default)] + pub millisecond_timestamp: bool, } /// Instant seal engine descriptor. #[derive(Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] pub struct InstantSeal { - /// Instant seal parameters. - pub params: InstantSealParams, + /// Instant seal parameters. + pub params: InstantSealParams, } diff --git a/json/src/spec/mod.rs b/json/src/spec/mod.rs index f1145be2e97..12955a2d9fb 100644 --- a/json/src/spec/mod.rs +++ b/json/src/spec/mod.rs @@ -1,51 +1,51 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Spec deserialization. pub mod account; +pub mod authority_round; +pub mod basic_authority; pub mod builtin; +pub mod clique; +pub mod engine; +pub mod ethash; pub mod genesis; +pub mod instant_seal; +pub mod null_engine; pub mod params; -pub mod spec; pub mod seal; -pub mod engine; +pub mod spec; pub mod state; -pub mod ethash; pub mod validator_set; -pub mod basic_authority; -pub mod authority_round; -pub mod null_engine; -pub mod instant_seal; -pub mod hardcoded_sync; -pub mod clique; -pub use self::account::Account; -pub use self::builtin::{Builtin, Pricing, Linear}; -pub use self::genesis::Genesis; -pub use self::params::Params; -pub use self::spec::{Spec, ForkSpec}; -pub use self::seal::{Seal, Ethereum, AuthorityRoundSeal, TendermintSeal}; -pub use self::engine::Engine; -pub use self::state::State; -pub use self::ethash::{Ethash, EthashParams, BlockReward}; -pub use self::validator_set::ValidatorSet; -pub use self::basic_authority::{BasicAuthority, BasicAuthorityParams}; -pub use self::authority_round::{AuthorityRound, AuthorityRoundParams}; -pub use self::clique::{Clique, CliqueParams}; -pub use self::null_engine::{NullEngine, NullEngineParams}; -pub use self::instant_seal::{InstantSeal, InstantSealParams}; -pub use self::hardcoded_sync::HardcodedSync; +pub use self::{ + account::Account, + authority_round::{AuthorityRound, AuthorityRoundParams}, + basic_authority::{BasicAuthority, BasicAuthorityParams}, + builtin::{Builtin, Linear, Pricing}, + clique::{Clique, CliqueParams}, + engine::Engine, + ethash::{BlockReward, Ethash, EthashParams}, + genesis::Genesis, + instant_seal::{InstantSeal, InstantSealParams}, + null_engine::{NullEngine, NullEngineParams}, + params::Params, + seal::{AuthorityRoundSeal, Ethereum, Seal, TendermintSeal}, + spec::{ForkSpec, Spec}, + state::State, + validator_set::ValidatorSet, +}; diff --git a/json/src/spec/null_engine.rs b/json/src/spec/null_engine.rs index bf75749808c..6c978b4796a 100644 --- a/json/src/spec/null_engine.rs +++ b/json/src/spec/null_engine.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Null engine params deserialization. @@ -23,34 +23,39 @@ use uint::Uint; #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub struct NullEngineParams { - /// Block reward. - pub block_reward: Option, + /// Block reward. + pub block_reward: Option, + /// Immediate finalization. + pub immediate_finalization: Option, } /// Null engine descriptor #[derive(Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] pub struct NullEngine { - /// Ethash params. - pub params: NullEngineParams, + /// Ethash params. + pub params: NullEngineParams, } #[cfg(test)] mod tests { - use serde_json; - use uint::Uint; - use ethereum_types::U256; - use super::*; - - #[test] - fn null_engine_deserialization() { - let s = r#"{ + use super::*; + use ethereum_types::U256; + use serde_json; + use uint::Uint; + + #[test] + fn null_engine_deserialization() { + let s = r#"{ "params": { "blockReward": "0x0d" } }"#; - let deserialized: NullEngine = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized.params.block_reward, Some(Uint(U256::from(0x0d)))); - } + let deserialized: NullEngine = serde_json::from_str(s).unwrap(); + assert_eq!( + deserialized.params.block_reward, + Some(Uint(U256::from(0x0d))) + ); + } } diff --git a/json/src/spec/params.rs b/json/src/spec/params.rs index 765384a6b91..dbaa1f907ce 100644 --- a/json/src/spec/params.rs +++ b/json/src/spec/params.rs @@ -1,141 +1,153 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Spec params deserialization. -use uint::{self, Uint}; -use hash::{H256, Address}; use bytes::Bytes; +use hash::{Address, H256}; +use uint::{self, Uint}; /// Spec params. #[derive(Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub struct Params { - /// Account start nonce, defaults to 0. - pub account_start_nonce: Option, - /// Maximum size of extra data. - pub maximum_extra_data_size: Uint, - /// Minimum gas limit. - pub min_gas_limit: Uint, - - /// Network id. - #[serde(rename = "networkID")] - pub network_id: Uint, - /// Chain id. - #[serde(rename = "chainID")] - pub chain_id: Option, - - /// Name of the main ("eth") subprotocol. - pub subprotocol_name: Option, - - /// Option fork block number to check. - pub fork_block: Option, - /// Expected fork block hash. - #[serde(rename = "forkCanonHash")] - pub fork_hash: Option, - - /// See main EthashParams docs. - pub eip150_transition: Option, - - /// See main EthashParams docs. - pub eip160_transition: Option, - - /// See main EthashParams docs. - pub eip161abc_transition: Option, - /// See main EthashParams docs. - pub eip161d_transition: Option, - - /// See `CommonParams` docs. - pub eip98_transition: Option, - /// See `CommonParams` docs. - pub eip155_transition: Option, - /// See `CommonParams` docs. - pub validate_chain_id_transition: Option, - /// See `CommonParams` docs. - pub validate_receipts_transition: Option, - /// See `CommonParams` docs. - pub eip140_transition: Option, - /// See `CommonParams` docs. - pub eip210_transition: Option, - /// See `CommonParams` docs. - pub eip210_contract_address: Option
, - /// See `CommonParams` docs. - pub eip210_contract_code: Option, - /// See `CommonParams` docs. - pub eip210_contract_gas: Option, - /// See `CommonParams` docs. - pub eip211_transition: Option, - /// See `CommonParams` docs. - pub eip145_transition: Option, - /// See `CommonParams` docs. - pub eip214_transition: Option, - /// See `CommonParams` docs. - pub eip658_transition: Option, - /// See `CommonParams` docs. - pub eip1052_transition: Option, - /// See `CommonParams` docs. - pub eip1283_transition: Option, - /// See `CommonParams` docs. - pub eip1283_disable_transition: Option, - /// See `CommonParams` docs. - pub eip1014_transition: Option, - /// See `CommonParams` docs. - pub dust_protection_transition: Option, - /// See `CommonParams` docs. - pub nonce_cap_increment: Option, - /// See `CommonParams` docs. - pub remove_dust_contracts : Option, - /// See `CommonParams` docs. - #[serde(deserialize_with="uint::validate_non_zero")] - pub gas_limit_bound_divisor: Uint, - /// See `CommonParams` docs. - pub registrar: Option
, - /// Apply reward flag - pub apply_reward: Option, - /// Node permission contract address. - pub node_permission_contract: Option
, - /// See main EthashParams docs. - pub max_code_size: Option, - /// Maximum size of transaction RLP payload. - pub max_transaction_size: Option, - /// See main EthashParams docs. - pub max_code_size_transition: Option, - /// Transaction permission contract address. - pub transaction_permission_contract: Option
, - /// Block at which the transaction permission contract should start being used. - pub transaction_permission_contract_transition: Option, - /// Wasm activation block height, if not activated from start - pub wasm_activation_transition: Option, - /// KIP4 activiation block height. - pub kip4_transition: Option, - /// KIP6 activiation block height. - pub kip6_transition: Option, + /// Account start nonce, defaults to 0. + pub account_start_nonce: Option, + /// Maximum size of extra data. + pub maximum_extra_data_size: Uint, + /// Minimum gas limit. + pub min_gas_limit: Uint, + + /// Network id. + #[serde(rename = "networkID")] + pub network_id: Uint, + /// Chain id. + #[serde(rename = "chainID")] + pub chain_id: Option, + + /// Name of the main ("eth") subprotocol. + pub subprotocol_name: Option, + + /// Option fork block number to check. + pub fork_block: Option, + /// Expected fork block hash. + #[serde(rename = "forkCanonHash")] + pub fork_hash: Option, + + /// See main EthashParams docs. + pub eip150_transition: Option, + + /// See main EthashParams docs. + pub eip160_transition: Option, + + /// See main EthashParams docs. + pub eip161abc_transition: Option, + /// See main EthashParams docs. + pub eip161d_transition: Option, + + /// See `CommonParams` docs. + pub eip98_transition: Option, + /// See `CommonParams` docs. + pub eip155_transition: Option, + /// See `CommonParams` docs. + pub validate_chain_id_transition: Option, + /// See `CommonParams` docs. + pub validate_receipts_transition: Option, + /// See `CommonParams` docs. + pub eip140_transition: Option, + /// See `CommonParams` docs. + pub eip210_transition: Option, + /// See `CommonParams` docs. + pub eip210_contract_address: Option
, + /// See `CommonParams` docs. + pub eip210_contract_code: Option, + /// See `CommonParams` docs. + pub eip210_contract_gas: Option, + /// See `CommonParams` docs. + pub eip211_transition: Option, + /// See `CommonParams` docs. + pub eip145_transition: Option, + /// See `CommonParams` docs. + pub eip214_transition: Option, + /// See `CommonParams` docs. + pub eip658_transition: Option, + /// See `CommonParams` docs. + pub eip1052_transition: Option, + /// See `CommonParams` docs. + pub eip1283_transition: Option, + /// See `CommonParams` docs. + pub eip1283_disable_transition: Option, + /// See `CommonParams` docs. + pub eip1283_reenable_transition: Option, + /// See `CommonParams` docs. + pub eip1014_transition: Option, + /// See `CommonParams` docs. + pub eip1706_transition: Option, + /// See `CommonParams` docs. + pub eip1344_transition: Option, + /// See `CommonParams` docs. + pub eip1884_transition: Option, + /// See `CommonParams` docs. + pub eip2028_transition: Option, + /// See `CommonParams` docs. + pub eip2315_transition: Option, + /// See `CommonParams` docs. + pub dust_protection_transition: Option, + /// See `CommonParams` docs. + pub nonce_cap_increment: Option, + /// See `CommonParams` docs. + pub remove_dust_contracts: Option, + /// See `CommonParams` docs. + #[serde(deserialize_with = "uint::validate_non_zero")] + pub gas_limit_bound_divisor: Uint, + /// See `CommonParams` docs. + pub registrar: Option
, + /// Apply reward flag + pub apply_reward: Option, + /// Node permission contract address. + pub node_permission_contract: Option
, + /// See main EthashParams docs. + pub max_code_size: Option, + /// Maximum size of transaction RLP payload. + pub max_transaction_size: Option, + /// See main EthashParams docs. + pub max_code_size_transition: Option, + /// Transaction permission contract address. + pub transaction_permission_contract: Option
, + /// Block at which the transaction permission contract should start being used. + pub transaction_permission_contract_transition: Option, + /// Wasm activation block height, if not activated from start + pub wasm_activation_transition: Option, + /// KIP4 activiation block height. + pub kip4_transition: Option, + /// KIP6 activiation block height. + pub kip6_transition: Option, } #[cfg(test)] mod tests { - use serde_json; - use uint::Uint; - use ethereum_types::U256; - use spec::params::Params; - - #[test] - fn params_deserialization() { - let s = r#"{ + use ethereum_types::U256; + use serde_json; + use spec::params::Params; + use uint::Uint; + + #[test] + fn params_deserialization() { + let s = r#"{ "maximumExtraDataSize": "0x20", "networkID" : "0x1", "chainID" : "0x15", @@ -147,22 +159,28 @@ mod tests { "wasmActivationTransition": "0x1010" }"#; - let deserialized: Params = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized.maximum_extra_data_size, Uint(U256::from(0x20))); - assert_eq!(deserialized.network_id, Uint(U256::from(0x1))); - assert_eq!(deserialized.chain_id, Some(Uint(U256::from(0x15)))); - assert_eq!(deserialized.subprotocol_name, Some("exp".to_owned())); - assert_eq!(deserialized.min_gas_limit, Uint(U256::from(0x1388))); - assert_eq!(deserialized.account_start_nonce, Some(Uint(U256::from(0x01)))); - assert_eq!(deserialized.gas_limit_bound_divisor, Uint(U256::from(0x20))); - assert_eq!(deserialized.max_code_size, Some(Uint(U256::from(0x1000)))); - assert_eq!(deserialized.wasm_activation_transition, Some(Uint(U256::from(0x1010)))); - } - - #[test] - #[should_panic(expected = "a non-zero value")] - fn test_zero_value_divisor() { - let s = r#"{ + let deserialized: Params = serde_json::from_str(s).unwrap(); + assert_eq!(deserialized.maximum_extra_data_size, Uint(U256::from(0x20))); + assert_eq!(deserialized.network_id, Uint(U256::from(0x1))); + assert_eq!(deserialized.chain_id, Some(Uint(U256::from(0x15)))); + assert_eq!(deserialized.subprotocol_name, Some("exp".to_owned())); + assert_eq!(deserialized.min_gas_limit, Uint(U256::from(0x1388))); + assert_eq!( + deserialized.account_start_nonce, + Some(Uint(U256::from(0x01))) + ); + assert_eq!(deserialized.gas_limit_bound_divisor, Uint(U256::from(0x20))); + assert_eq!(deserialized.max_code_size, Some(Uint(U256::from(0x1000)))); + assert_eq!( + deserialized.wasm_activation_transition, + Some(Uint(U256::from(0x1010))) + ); + } + + #[test] + #[should_panic(expected = "a non-zero value")] + fn test_zero_value_divisor() { + let s = r#"{ "maximumExtraDataSize": "0x20", "networkID" : "0x1", "chainID" : "0x15", @@ -173,6 +191,6 @@ mod tests { "maxCodeSize": "0x1000" }"#; - let _deserialized: Params = serde_json::from_str(s).unwrap(); - } + let _deserialized: Params = serde_json::from_str(s).unwrap(); + } } diff --git a/json/src/spec/seal.rs b/json/src/spec/seal.rs index e716a05bc26..5bf894fdfc9 100644 --- a/json/src/spec/seal.rs +++ b/json/src/spec/seal.rs @@ -1,56 +1,56 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Spec seal deserialization. +use bytes::Bytes; use hash::*; use uint::Uint; -use bytes::Bytes; /// Ethereum seal. #[derive(Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub struct Ethereum { - /// Seal nonce. - pub nonce: H64, - /// Seal mix hash. - pub mix_hash: H256, + /// Seal nonce. + pub nonce: H64, + /// Seal mix hash. + pub mix_hash: H256, } /// AuthorityRound seal. #[derive(Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] pub struct AuthorityRoundSeal { - /// Seal step. - pub step: Uint, - /// Seal signature. - pub signature: H520, + /// Seal step. + pub step: Uint, + /// Seal signature. + pub signature: H520, } /// Tendermint seal. #[derive(Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] pub struct TendermintSeal { - /// Seal round. - pub round: Uint, - /// Proposal seal signature. - pub proposal: H520, - /// Proposal seal signature. - pub precommits: Vec, + /// Seal round. + pub round: Uint, + /// Proposal seal signature. + pub proposal: H520, + /// Proposal seal signature. + pub precommits: Vec, } /// Seal variants. @@ -58,28 +58,28 @@ pub struct TendermintSeal { #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub enum Seal { - /// Ethereum seal. - Ethereum(Ethereum), - /// AuthorityRound seal. - AuthorityRound(AuthorityRoundSeal), - /// Tendermint seal. - Tendermint(TendermintSeal), - /// Generic seal. - Generic(Bytes), + /// Ethereum seal. + Ethereum(Ethereum), + /// AuthorityRound seal. + AuthorityRound(AuthorityRoundSeal), + /// Tendermint seal. + Tendermint(TendermintSeal), + /// Generic seal. + Generic(Bytes), } #[cfg(test)] mod tests { - use serde_json; - use hash::*; - use bytes::Bytes; - use uint::Uint; - use ethereum_types::{U256, H64 as Eth64, H256 as Eth256, H520 as Eth520}; - use spec::{Ethereum, AuthorityRoundSeal, TendermintSeal, Seal}; - - #[test] - fn seal_deserialization() { - let s = r#"[{ + use bytes::Bytes; + use ethereum_types::{H256 as Eth256, H520 as Eth520, H64 as Eth64, U256}; + use hash::*; + use serde_json; + use spec::{AuthorityRoundSeal, Ethereum, Seal, TendermintSeal}; + use uint::Uint; + + #[test] + fn seal_deserialization() { + let s = r#"[{ "ethereum": { "nonce": "0x0000000000000042", "mixHash": "0x1000000000000000000000000000000000000000000000000000000000000001" @@ -101,31 +101,41 @@ mod tests { } }]"#; - let deserialized: Vec = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized.len(), 4); - - // [0] - assert_eq!(deserialized[0], Seal::Ethereum(Ethereum { - nonce: H64(Eth64::from("0x0000000000000042")), - mix_hash: H256(Eth256::from("0x1000000000000000000000000000000000000000000000000000000000000001")) - })); - - // [1] - assert_eq!(deserialized[1], Seal::Generic(Bytes::new(vec![ - 0xe0, 0x11, 0xbb, 0xe8, 0xdb, 0x4e, 0x34, 0x7b, 0x4e, 0x8c, 0x93, 0x7c, 0x1c, 0x83, 0x70, 0xe4, - 0xb5, 0xed, 0x33, 0xad, 0xb3, 0xdb, 0x69, 0xcb, 0xdb, 0x7a, 0x38, 0xe1, 0xe5, 0x0b, 0x1b, 0x82, 0xfa]))); - - // [2] - assert_eq!(deserialized[2], Seal::AuthorityRound(AuthorityRoundSeal { + let deserialized: Vec = serde_json::from_str(s).unwrap(); + assert_eq!(deserialized.len(), 4); + + // [0] + assert_eq!( + deserialized[0], + Seal::Ethereum(Ethereum { + nonce: H64(Eth64::from("0x0000000000000042")), + mix_hash: H256(Eth256::from( + "0x1000000000000000000000000000000000000000000000000000000000000001" + )) + }) + ); + + // [1] + assert_eq!( + deserialized[1], + Seal::Generic(Bytes::new(vec![ + 0xe0, 0x11, 0xbb, 0xe8, 0xdb, 0x4e, 0x34, 0x7b, 0x4e, 0x8c, 0x93, 0x7c, 0x1c, 0x83, + 0x70, 0xe4, 0xb5, 0xed, 0x33, 0xad, 0xb3, 0xdb, 0x69, 0xcb, 0xdb, 0x7a, 0x38, 0xe1, + 0xe5, 0x0b, 0x1b, 0x82, 0xfa + ])) + ); + + // [2] + assert_eq!(deserialized[2], Seal::AuthorityRound(AuthorityRoundSeal { step: Uint(U256::from(0x0)), signature: H520(Eth520::from("0x2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002")) })); - // [3] - assert_eq!(deserialized[3], Seal::Tendermint(TendermintSeal { + // [3] + assert_eq!(deserialized[3], Seal::Tendermint(TendermintSeal { round: Uint(U256::from(0x3)), proposal: H520(Eth520::from("0x3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003")), precommits: vec![H520(Eth520::from("0x4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004"))] })); - } + } } diff --git a/json/src/spec/spec.rs b/json/src/spec/spec.rs index 68824cad99b..e5e23be7783 100644 --- a/json/src/spec/spec.rs +++ b/json/src/spec/spec.rs @@ -1,40 +1,43 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Spec deserialization. +use serde_json::{self, Error}; +use spec::{Engine, Genesis, Params, State}; use std::io::Read; -use serde_json; -use serde_json::Error; -use spec::{Params, Genesis, Engine, State, HardcodedSync}; /// Fork spec definition #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize)] pub enum ForkSpec { - EIP150, - EIP158, - Frontier, - Homestead, - Byzantium, - Constantinople, - ConstantinopleFix, - EIP158ToByzantiumAt5, - FrontierToHomesteadAt5, - HomesteadToDaoAt5, - HomesteadToEIP150At5, + EIP150, + EIP158, + Frontier, + Homestead, + Byzantium, + Constantinople, + ConstantinopleFix, + Istanbul, + EIP158ToByzantiumAt5, + FrontierToHomesteadAt5, + HomesteadToDaoAt5, + HomesteadToEIP150At5, + ByzantiumToConstantinopleAt5, + ByzantiumToConstantinopleFixAt5, + Berlin, } /// Spec deserialization. @@ -42,162 +45,195 @@ pub enum ForkSpec { #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub struct Spec { - /// Spec name. - pub name: String, - /// Special fork name. - pub data_dir: Option, - /// Engine. - pub engine: Engine, - /// Spec params. - pub params: Params, - /// Genesis header. - pub genesis: Genesis, - /// Genesis state. - pub accounts: State, - /// Boot nodes. - pub nodes: Option>, - /// Hardcoded synchronization for the light client. - pub hardcoded_sync: Option, + /// Spec name. + pub name: String, + /// Special fork name. + pub data_dir: Option, + /// Engine. + pub engine: Engine, + /// Spec params. + pub params: Params, + /// Genesis header. + pub genesis: Genesis, + /// Genesis state. + pub accounts: State, + /// Boot nodes. + pub nodes: Option>, } impl Spec { - /// Loads test from json. - pub fn load(reader: R) -> Result where R: Read { - serde_json::from_reader(reader) - } + /// Loads test from json. + pub fn load(reader: R) -> Result + where + R: Read, + { + serde_json::from_reader(reader) + } } #[cfg(test)] mod tests { - use serde_json; - use spec::spec::Spec; + use serde_json; + use spec::spec::Spec; - #[test] - fn should_error_on_unknown_fields() { - let s = r#"{ - "name": "Morden", - "dataDir": "morden", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "durationLimit": "0x0d", - "homesteadTransition" : "0x", - "daoHardforkTransition": "0xffffffffffffffff", - "daoHardforkBeneficiary": "0x0000000000000000000000000000000000000000", - "daoHardforkAccounts": [] - } - } - }, - "params": { - "accountStartNonce": "0x0100000", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x2", - "forkBlock": "0xffffffffffffffff", - "forkCanonHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "gasLimitBoundDivisor": "0x20", - "unknownField": "0x0" - }, - "genesis": { - "seal": { - "ethereum": { - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "nonce": "0x00006d6f7264656e" + #[test] + fn should_error_on_unknown_fields() { + let s = r#"{ + "name": "Morden", + "dataDir": "morden", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x020000", + "difficultyBoundDivisor": "0x0800", + "durationLimit": "0x0d", + "homesteadTransition" : "0x", + "daoHardforkTransition": "0xffffffffffffffff", + "daoHardforkBeneficiary": "0x0000000000000000000000000000000000000000", + "daoHardforkAccounts": [] + } } }, - "difficulty": "0x20000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x", - "gasLimit": "0x2fefd8" - }, - "nodes": [ - "enode://b1217cbaa440e35ed471157123fe468e19e8b5ad5bedb4b1fdbcbdab6fb2f5ed3e95dd9c24a22a79fdb2352204cea207df27d92bfd21bfd41545e8b16f637499@104.44.138.37:30303" - ], - "accounts": { - "0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, - "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" } - }, - "hardcodedSync": { - "header": "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23", - "totalDifficulty": "0x400000000", - "CHTs": [ - "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", - "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" - ] - } + "params": { + "accountStartNonce": "0x0100000", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x2", + "forkBlock": "0xffffffffffffffff", + "forkCanonHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "gasLimitBoundDivisor": "0x20", + "unknownField": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x00006d6f7264656e" + } + }, + "difficulty": "0x20000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x2fefd8" + }, + "nodes": [ + "enode://b1217cbaa440e35ed471157123fe468e19e8b5ad5bedb4b1fdbcbdab6fb2f5ed3e95dd9c24a22a79fdb2352204cea207df27d92bfd21bfd41545e8b16f637499@104.44.138.37:30303" + ], + "accounts": { + "0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" } + } }"#; - let result: Result = serde_json::from_str(s); - assert!(result.is_err()); - } + let result: Result = serde_json::from_str(s); + assert!(result.is_err()); + } - #[test] - fn spec_deserialization() { - let s = r#"{ - "name": "Morden", - "dataDir": "morden", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "durationLimit": "0x0d", - "homesteadTransition" : "0x", - "daoHardforkTransition": "0xffffffffffffffff", - "daoHardforkBeneficiary": "0x0000000000000000000000000000000000000000", - "daoHardforkAccounts": [] - } - } - }, - "params": { - "accountStartNonce": "0x0100000", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x2", - "forkBlock": "0xffffffffffffffff", - "forkCanonHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "gasLimitBoundDivisor": "0x20" - }, - "genesis": { - "seal": { - "ethereum": { - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "nonce": "0x00006d6f7264656e" + #[test] + fn spec_deserialization() { + let s = r#"{ + "name": "Morden", + "dataDir": "morden", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x020000", + "difficultyBoundDivisor": "0x0800", + "durationLimit": "0x0d", + "homesteadTransition" : "0x", + "daoHardforkTransition": "0xffffffffffffffff", + "daoHardforkBeneficiary": "0x0000000000000000000000000000000000000000", + "daoHardforkAccounts": [] + } } }, - "difficulty": "0x20000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x", - "gasLimit": "0x2fefd8" - }, - "nodes": [ - "enode://b1217cbaa440e35ed471157123fe468e19e8b5ad5bedb4b1fdbcbdab6fb2f5ed3e95dd9c24a22a79fdb2352204cea207df27d92bfd21bfd41545e8b16f637499@104.44.138.37:30303" - ], - "accounts": { - "0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, - "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" } - }, - "hardcodedSync": { - "header": "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23", - "totalDifficulty": "0x400000000", - "CHTs": [ - "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", - "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" - ] - } + "params": { + "accountStartNonce": "0x0100000", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x2", + "forkBlock": "0xffffffffffffffff", + "forkCanonHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "gasLimitBoundDivisor": "0x20" + }, + "genesis": { + "seal": { + "ethereum": { + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x00006d6f7264656e" + } + }, + "difficulty": "0x20000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x2fefd8" + }, + "nodes": [ + "enode://b1217cbaa440e35ed471157123fe468e19e8b5ad5bedb4b1fdbcbdab6fb2f5ed3e95dd9c24a22a79fdb2352204cea207df27d92bfd21bfd41545e8b16f637499@104.44.138.37:30303" + ], + "accounts": { + "0000000000000000000000000000000000000001": { + "balance": "1", + "nonce": "1048576", + "builtin": { + "name": "ecrecover", + "pricing": { + "linear": { + "base": 3000, + "word": 0 + } + } + } + }, + "0000000000000000000000000000000000000002": { + "balance": "1", + "nonce": "1048576", + "builtin": { + "name": "sha256", + "pricing": { + "linear": { + "base": 60, + "word": 12 + } + } + } + }, + "0000000000000000000000000000000000000003": { + "balance": "1", + "nonce": "1048576", + "builtin": { + "name": "ripemd160", + "pricing": { + "linear": { + "base": 600, + "word": 120 + } + } + } + }, + "0000000000000000000000000000000000000004": { + "balance": "1", + "nonce": "1048576", + "builtin": { + "name": "identity", + "pricing": { + "linear": { + "base": 15, + "word": 3 + } + } + } + }, + "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" } + } }"#; - let _deserialized: Spec = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } + let _deserialized: Spec = serde_json::from_str(s).unwrap(); + // TODO: validate all fields + } } diff --git a/json/src/spec/state.rs b/json/src/spec/state.rs index e8a8663672e..0bf295589a3 100644 --- a/json/src/spec/state.rs +++ b/json/src/spec/state.rs @@ -1,25 +1,25 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Blockchain test state deserializer. -use std::collections::BTreeMap; -use hash::Address; use bytes::Bytes; +use hash::Address; use spec::{Account, Builtin}; +use std::collections::BTreeMap; /// Blockchain test state deserializer. #[derive(Debug, PartialEq, Deserialize)] @@ -27,28 +27,28 @@ use spec::{Account, Builtin}; pub struct State(BTreeMap); impl State { - /// Returns all builtins. - pub fn builtins(&self) -> BTreeMap { - self.0 - .iter() - .filter_map(|(add, ref acc)| acc.builtin.clone().map(|b| (add.clone(), b))) - .collect() - } - - /// Returns all constructors. - pub fn constructors(&self) -> BTreeMap { - self.0 - .iter() - .filter_map(|(add, ref acc)| acc.constructor.clone().map(|b| (add.clone(), b))) - .collect() - } + /// Returns all builtins. + pub fn builtins(&self) -> BTreeMap { + self.0 + .iter() + .filter_map(|(add, ref acc)| acc.builtin.clone().map(|b| (add.clone(), b.into()))) + .collect() + } + + /// Returns all constructors. + pub fn constructors(&self) -> BTreeMap { + self.0 + .iter() + .filter_map(|(add, ref acc)| acc.constructor.clone().map(|b| (add.clone(), b))) + .collect() + } } impl IntoIterator for State { - type Item = as IntoIterator>::Item; - type IntoIter = as IntoIterator>::IntoIter; + type Item = as IntoIterator>::Item; + type IntoIter = as IntoIterator>::IntoIter; - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } } diff --git a/json/src/spec/validator_set.rs b/json/src/spec/validator_set.rs index e7e82282c7a..17c75162c18 100644 --- a/json/src/spec/validator_set.rs +++ b/json/src/spec/validator_set.rs @@ -1,51 +1,51 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Validator set deserialization. +use hash::Address; use std::collections::BTreeMap; use uint::Uint; -use hash::Address; /// Different ways of specifying validators. #[derive(Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub enum ValidatorSet { - /// A simple list of authorities. - List(Vec
), - /// Address of a contract that indicates the list of authorities. - SafeContract(Address), - /// Address of a contract that indicates the list of authorities and enables reporting of theor misbehaviour using transactions. - Contract(Address), - /// A map of starting blocks for each validator set. - Multi(BTreeMap), + /// A simple list of authorities. + List(Vec
), + /// Address of a contract that indicates the list of authorities. + SafeContract(Address), + /// Address of a contract that indicates the list of authorities and enables reporting of theor misbehaviour using transactions. + Contract(Address), + /// A map of starting blocks for each validator set. + Multi(BTreeMap), } #[cfg(test)] mod tests { - use serde_json; - use uint::Uint; - use ethereum_types::{H160, U256}; - use hash::Address; - use spec::validator_set::ValidatorSet; + use ethereum_types::{H160, U256}; + use hash::Address; + use serde_json; + use spec::validator_set::ValidatorSet; + use uint::Uint; - #[test] - fn validator_set_deserialization() { - let s = r#"[{ + #[test] + fn validator_set_deserialization() { + let s = r#"[{ "list": ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"] }, { "safeContract": "0xc6d9d2cd449a754c494264e1809c50e34d64562b" @@ -59,20 +59,35 @@ mod tests { } }]"#; - let deserialized: Vec = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized.len(), 4); + let deserialized: Vec = serde_json::from_str(s).unwrap(); + assert_eq!(deserialized.len(), 4); - assert_eq!(deserialized[0], ValidatorSet::List(vec![Address(H160::from("0xc6d9d2cd449a754c494264e1809c50e34d64562b"))])); - assert_eq!(deserialized[1], ValidatorSet::SafeContract(Address(H160::from("0xc6d9d2cd449a754c494264e1809c50e34d64562b")))); - assert_eq!(deserialized[2], ValidatorSet::Contract(Address(H160::from("0xc6d9d2cd449a754c494264e1809c50e34d64562b")))); - match deserialized[3] { - ValidatorSet::Multi(ref map) => { - assert_eq!(map.len(), 3); - assert!(map.contains_key(&Uint(U256::from(0)))); - assert!(map.contains_key(&Uint(U256::from(10)))); - assert!(map.contains_key(&Uint(U256::from(20)))); - }, - _ => assert!(false), - } - } + assert_eq!( + deserialized[0], + ValidatorSet::List(vec![Address(H160::from( + "0xc6d9d2cd449a754c494264e1809c50e34d64562b" + ))]) + ); + assert_eq!( + deserialized[1], + ValidatorSet::SafeContract(Address(H160::from( + "0xc6d9d2cd449a754c494264e1809c50e34d64562b" + ))) + ); + assert_eq!( + deserialized[2], + ValidatorSet::Contract(Address(H160::from( + "0xc6d9d2cd449a754c494264e1809c50e34d64562b" + ))) + ); + match deserialized[3] { + ValidatorSet::Multi(ref map) => { + assert_eq!(map.len(), 3); + assert!(map.contains_key(&Uint(U256::from(0)))); + assert!(map.contains_key(&Uint(U256::from(10)))); + assert!(map.contains_key(&Uint(U256::from(20)))); + } + _ => assert!(false), + } + } } diff --git a/json/src/state/log.rs b/json/src/state/log.rs index 1a0dda5295a..bd753d4c9ef 100644 --- a/json/src/state/log.rs +++ b/json/src/state/log.rs @@ -1,44 +1,44 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! State test log deserialization. -use hash::{Address, H256, Bloom}; use bytes::Bytes; +use hash::{Address, Bloom, H256}; /// State test log deserialization. #[derive(Debug, PartialEq, Deserialize)] pub struct Log { - /// Address. - pub address: Address, - /// Topics. - pub topics: Vec, - /// Data. - pub data: Bytes, - /// Bloom. - pub bloom: Bloom, + /// Address. + pub address: Address, + /// Topics. + pub topics: Vec, + /// Data. + pub data: Bytes, + /// Bloom. + pub bloom: Bloom, } #[cfg(test)] mod tests { - use serde_json; - use state::Log; + use serde_json; + use state::Log; - #[test] - fn log_deserialization() { - let s = r#"{ + #[test] + fn log_deserialization() { + let s = r#"{ "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008800000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000", "data" : "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -46,7 +46,7 @@ mod tests { "0000000000000000000000000000000000000000000000000000000000000000" ] }"#; - let _deserialized: Log = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } + let _deserialized: Log = serde_json::from_str(s).unwrap(); + // TODO: validate all fields + } } diff --git a/json/src/state/mod.rs b/json/src/state/mod.rs index 8c2ac287535..865e3373ea3 100644 --- a/json/src/state/mod.rs +++ b/json/src/state/mod.rs @@ -1,29 +1,26 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! State test deserialization. +pub mod log; pub mod state; -pub mod transaction; pub mod test; -pub mod log; +pub mod transaction; -pub use self::state::State; -pub use self::transaction::Transaction; -pub use self::test::Test; -pub use self::log::Log; -pub use vm::Env as Env; +pub use self::{log::Log, state::State, test::Test, transaction::Transaction}; pub use blockchain::State as AccountState; +pub use vm::Env; diff --git a/json/src/state/state.rs b/json/src/state/state.rs index f25dabec798..fc18e544002 100644 --- a/json/src/state/state.rs +++ b/json/src/state/state.rs @@ -1,56 +1,56 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! State test deserialization. use bytes::Bytes; use hash::H256; -use state::{Env, AccountState, Transaction, Log}; +use state::{AccountState, Env, Log, Transaction}; /// State test deserialization. #[derive(Debug, PartialEq, Deserialize)] #[serde(rename_all = "camelCase")] pub struct State { - /// Environment. - pub env: Env, - /// Output. - #[serde(rename = "out")] - pub output: Bytes, - /// Pre state. - #[serde(rename = "pre")] - pub pre_state: AccountState, - /// Post state. - #[serde(rename = "post")] - pub post_state: AccountState, - /// Post state root. - pub post_state_root: H256, - /// Transaction. - pub transaction: Transaction, - /// Logs. - pub logs: Vec + /// Environment. + pub env: Env, + /// Output. + #[serde(rename = "out")] + pub output: Bytes, + /// Pre state. + #[serde(rename = "pre")] + pub pre_state: AccountState, + /// Post state. + #[serde(rename = "post")] + pub post_state: AccountState, + /// Post state root. + pub post_state_root: H256, + /// Transaction. + pub transaction: Transaction, + /// Logs. + pub logs: Vec, } #[cfg(test)] mod tests { - use serde_json; - use state::State; + use serde_json; + use state::State; - #[test] - fn state_deserialization() { - let s = r#"{ + #[test] + fn state_deserialization() { + let s = r#"{ "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "0x0100", @@ -150,7 +150,7 @@ mod tests { "value" : "0x00" } }"#; - let _deserialized: State = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } + let _deserialized: State = serde_json::from_str(s).unwrap(); + // TODO: validate all fields + } } diff --git a/json/src/state/test.rs b/json/src/state/test.rs index 3521c797785..5c2439d3d20 100644 --- a/json/src/state/test.rs +++ b/json/src/state/test.rs @@ -1,130 +1,132 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! General test deserialization. -use std::io::Read; -use std::collections::BTreeMap; -use uint::Uint; use bytes::Bytes; use hash::{Address, H256}; -use spec::ForkSpec; -use state::{Env, AccountState, Transaction}; use maybe::MaybeEmpty; use serde_json::{self, Error}; +use spec::ForkSpec; +use state::{AccountState, Env, Transaction}; +use std::{collections::BTreeMap, io::Read}; +use uint::Uint; /// State test deserializer. #[derive(Debug, PartialEq, Deserialize)] pub struct Test(BTreeMap); impl IntoIterator for Test { - type Item = as IntoIterator>::Item; - type IntoIter = as IntoIterator>::IntoIter; + type Item = as IntoIterator>::Item; + type IntoIter = as IntoIterator>::IntoIter; - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } } impl Test { - /// Loads test from json. - pub fn load(reader: R) -> Result where R: Read { - serde_json::from_reader(reader) - } + /// Loads test from json. + pub fn load(reader: R) -> Result + where + R: Read, + { + serde_json::from_reader(reader) + } } /// State test deserialization. #[derive(Debug, PartialEq, Deserialize)] pub struct State { - /// Environment. - pub env: Env, - /// Pre state. - #[serde(rename = "pre")] - pub pre_state: AccountState, - /// Post state. - #[serde(rename = "post")] - pub post_states: BTreeMap>, - /// Transaction. - pub transaction: MultiTransaction, + /// Environment. + pub env: Env, + /// Pre state. + #[serde(rename = "pre")] + pub pre_state: AccountState, + /// Post state. + #[serde(rename = "post")] + pub post_states: BTreeMap>, + /// Transaction. + pub transaction: MultiTransaction, } /// State test transaction deserialization. #[derive(Debug, PartialEq, Deserialize)] #[serde(rename_all = "camelCase")] pub struct MultiTransaction { - /// Transaction data set. - pub data: Vec, - /// Gas limit set. - pub gas_limit: Vec, - /// Gas price. - pub gas_price: Uint, - /// Nonce. - pub nonce: Uint, - /// Secret key. - #[serde(rename = "secretKey")] - pub secret: Option, - /// To. - pub to: MaybeEmpty
, - /// Value set. - pub value: Vec, + /// Transaction data set. + pub data: Vec, + /// Gas limit set. + pub gas_limit: Vec, + /// Gas price. + pub gas_price: Uint, + /// Nonce. + pub nonce: Uint, + /// Secret key. + #[serde(rename = "secretKey")] + pub secret: Option, + /// To. + pub to: MaybeEmpty
, + /// Value set. + pub value: Vec, } impl MultiTransaction { - /// Build transaction with given indexes. - pub fn select(&self, indexes: &PostStateIndexes) -> Transaction { - Transaction { - data: self.data[indexes.data as usize].clone(), - gas_limit: self.gas_limit[indexes.gas as usize].clone(), - gas_price: self.gas_price.clone(), - nonce: self.nonce.clone(), - secret: self.secret.clone(), - to: self.to.clone(), - value: self.value[indexes.value as usize].clone(), - } - } + /// Build transaction with given indexes. + pub fn select(&self, indexes: &PostStateIndexes) -> Transaction { + Transaction { + data: self.data[indexes.data as usize].clone(), + gas_limit: self.gas_limit[indexes.gas as usize].clone(), + gas_price: self.gas_price.clone(), + nonce: self.nonce.clone(), + secret: self.secret.clone(), + to: self.to.clone(), + value: self.value[indexes.value as usize].clone(), + } + } } /// State test indexes deserialization. #[derive(Debug, PartialEq, Deserialize)] pub struct PostStateIndexes { - /// Index into transaction data set. - pub data: u64, - /// Index into transaction gas limit set. - pub gas: u64, - /// Index into transaction value set. - pub value: u64, + /// Index into transaction data set. + pub data: u64, + /// Index into transaction gas limit set. + pub gas: u64, + /// Index into transaction value set. + pub value: u64, } /// State test indexed state result deserialization. #[derive(Debug, PartialEq, Deserialize)] pub struct PostStateResult { - /// Post state hash - pub hash: H256, - /// Indexes - pub indexes: PostStateIndexes, + /// Post state hash + pub hash: H256, + /// Indexes + pub indexes: PostStateIndexes, } #[cfg(test)] mod tests { - use serde_json; - use super::{MultiTransaction, State}; + use super::{MultiTransaction, State}; + use serde_json; - #[test] - fn multi_transaction_deserialization() { - let s = r#"{ + #[test] + fn multi_transaction_deserialization() { + let s = r#"{ "data" : [ "" ], "gasLimit" : [ "0x2dc6c0", "0x222222" ], "gasPrice" : "0x01", @@ -133,12 +135,12 @@ mod tests { "to" : "1000000000000000000000000000000000000000", "value" : [ "0x00", "0x01", "0x02" ] }"#; - let _deserialized: MultiTransaction = serde_json::from_str(s).unwrap(); - } + let _deserialized: MultiTransaction = serde_json::from_str(s).unwrap(); + } - #[test] - fn state_deserialization() { - let s = r#"{ + #[test] + fn state_deserialization() { + let s = r#"{ "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "0x0100", @@ -209,7 +211,7 @@ mod tests { "value" : [ "10", "0" ] } }"#; - let _deserialized: State = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } + let _deserialized: State = serde_json::from_str(s).unwrap(); + // TODO: validate all fields + } } diff --git a/json/src/state/transaction.rs b/json/src/state/transaction.rs index 693b976994e..68e639c9c5d 100644 --- a/json/src/state/transaction.rs +++ b/json/src/state/transaction.rs @@ -1,55 +1,55 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! State test transaction deserialization. -use uint::Uint; use bytes::Bytes; use hash::{Address, H256}; use maybe::MaybeEmpty; +use uint::Uint; /// State test transaction deserialization. #[derive(Debug, PartialEq, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Transaction { - /// Transaction data. - pub data: Bytes, - /// Gas limit. - pub gas_limit: Uint, - /// Gas price. - pub gas_price: Uint, - /// Nonce. - pub nonce: Uint, - /// Secret key. - #[serde(rename = "secretKey")] - pub secret: Option, - /// To. - pub to: MaybeEmpty
, - /// Value. - pub value: Uint, + /// Transaction data. + pub data: Bytes, + /// Gas limit. + pub gas_limit: Uint, + /// Gas price. + pub gas_price: Uint, + /// Nonce. + pub nonce: Uint, + /// Secret key. + #[serde(rename = "secretKey")] + pub secret: Option, + /// To. + pub to: MaybeEmpty
, + /// Value. + pub value: Uint, } #[cfg(test)] mod tests { - use serde_json; - use state::Transaction; + use serde_json; + use state::Transaction; - #[test] - fn transaction_deserialization() { - let s = r#"{ + #[test] + fn transaction_deserialization() { + let s = r#"{ "data" : "", "gasLimit" : "0x2dc6c0", "gasPrice" : "0x01", @@ -58,7 +58,7 @@ mod tests { "to" : "1000000000000000000000000000000000000000", "value" : "0x00" }"#; - let _deserialized: Transaction = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } + let _deserialized: Transaction = serde_json::from_str(s).unwrap(); + // TODO: validate all fields + } } diff --git a/json/src/test/mod.rs b/json/src/test/mod.rs index 355d30d69fc..cf77f6a377f 100644 --- a/json/src/test/mod.rs +++ b/json/src/test/mod.rs @@ -1,44 +1,42 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Additional test structures deserialization. -use std::collections::BTreeMap; -use std::io::Read; -use serde_json; -use serde_json::Error; use hash::H256; +use serde_json::{self, Error}; +use std::{collections::BTreeMap, io::Read, path::PathBuf}; use uint::Uint; /// Blockchain test header deserializer. #[derive(Debug, PartialEq, Deserialize)] #[serde(rename_all = "camelCase")] pub struct DifficultyTestCase { - /// Parent timestamp. - pub parent_timestamp: Uint, - /// Parent difficulty. - pub parent_difficulty: Uint, - /// Parent uncle hash. - pub parent_uncles: H256, - /// Current timestamp. - pub current_timestamp: Uint, - /// Current difficulty. - pub current_difficulty: Uint, - /// Current block number. - pub current_block_number: Uint, + /// Parent timestamp. + pub parent_timestamp: Uint, + /// Parent difficulty. + pub parent_difficulty: Uint, + /// Parent uncle hash. + pub parent_uncles: H256, + /// Current timestamp. + pub current_timestamp: Uint, + /// Current difficulty. + pub current_difficulty: Uint, + /// Current block number. + pub current_block_number: Uint, } /// Blockchain test deserializer. @@ -46,73 +44,198 @@ pub struct DifficultyTestCase { pub struct DifficultyTest(BTreeMap); impl IntoIterator for DifficultyTest { - type Item = as IntoIterator>::Item; - type IntoIter = as IntoIterator>::IntoIter; + type Item = as IntoIterator>::Item; + type IntoIter = as IntoIterator>::IntoIter; - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } } impl DifficultyTest { - /// Loads test from json. - pub fn load(reader: R) -> Result where R: Read { - serde_json::from_reader(reader) - } + /// Loads test from json. + pub fn load(reader: R) -> Result + where + R: Read, + { + serde_json::from_reader(reader) + } } /// Test to skip (only if issue ongoing) #[derive(Debug, PartialEq, Deserialize)] pub struct SkipStates { - /// Block tests - pub block: Vec, - /// State tests - pub state: Vec, - + /// Block tests + pub block: Vec, + /// State tests + pub state: Vec, } /// Block test to skip. #[derive(Debug, PartialEq, Deserialize)] pub struct BlockSkipStates { - /// Issue reference. - pub reference: String, - /// Test failing name. - pub failing: String, - /// Items failing for the test. - pub subtests: Vec, + /// Issue reference. + pub reference: String, + /// Test failing name. + pub failing: String, + /// Items failing for the test. + pub subtests: Vec, } /// State test to skip. #[derive(Debug, PartialEq, Deserialize)] pub struct StateSkipStates { - /// Issue reference. - pub reference: String, - /// Test failing name. - pub failing: String, - /// Items failing for the test. - pub subtests: BTreeMap + /// Issue reference. + pub reference: String, + /// Test failing name. + pub failing: String, + /// Items failing for the test. + pub subtests: BTreeMap, } /// State subtest to skip. #[derive(Debug, PartialEq, Deserialize)] pub struct StateSkipSubStates { - /// State test number of this item. Or '*' for all state. - pub subnumbers: Vec, - /// Chain for this items. - pub chain: String, + /// State test number of this item. Or '*' for all state. + pub subnumbers: Vec, + /// Chain for this items. + pub chain: String, } impl SkipStates { - /// Loads skip states from json. - pub fn load(reader: R) -> Result where R: Read { - serde_json::from_reader(reader) - } - - /// Empty skip states. - pub fn empty() -> Self { - SkipStates { - block: Vec::new(), - state: Vec::new(), - } - } + /// Loads skip states from json. + pub fn load(reader: R) -> Result + where + R: Read, + { + serde_json::from_reader(reader) + } + + /// Empty skip states. + pub fn empty() -> Self { + SkipStates { + block: Vec::new(), + state: Vec::new(), + } + } +} + +/// Describes a github.com/ethereum/tests suite +#[derive(Debug, PartialEq, Deserialize)] +pub struct EthereumTestSuite { + /// Blockchain tests + pub chain: Vec, + /// State tests + pub state: Vec, + /// Difficulty tests + pub difficulty: Vec, + /// Executive tests + pub executive: Vec, + /// Transaction tests + pub transaction: Vec, + /// Trie tests + pub trie: Vec, +} + +/// Chain spec used in tests +#[derive(Debug, PartialEq, Deserialize)] +pub enum TestChainSpec { + /// Foundation + Foundation, + /// ByzantiumTest + ByzantiumTest, + /// FrontierTest + FrontierTest, + /// HomesteadTest + HomesteadTest, +} + +/// Kind of trie used in test +#[derive(Debug, PartialEq, Deserialize)] +pub enum TestTrieSpec { + /// Generic + Generic, + /// Secure + Secure, +} + +/// A set of blockchain tests +#[derive(Debug, PartialEq, Deserialize)] +pub struct ChainTests { + /// Path of the json tests + pub path: PathBuf, + /// Tests to skip + pub skip: Vec, +} + +/// Tests to skip in chain tests +#[derive(Debug, PartialEq, Deserialize)] +pub struct ChainTestSkip { + /// Issue reference. + pub reference: String, + /// Test names to skip + pub names: Vec, + /// Test paths to skip + pub paths: Vec, +} + +/// A set of state tests +#[derive(Debug, PartialEq, Deserialize)] +pub struct StateTests { + /// Path of the json tests + pub path: PathBuf, + /// Tests to skip + pub skip: Vec, +} + +/// State test to skip +#[derive(Debug, PartialEq, Deserialize)] +pub struct StateTestSkip { + /// Issue reference. + pub reference: String, + /// Paths to skip + pub paths: Vec, + /// Test names to skip + pub names: BTreeMap, +} + +/// State subtest to skip. +#[derive(Debug, PartialEq, Deserialize)] +pub struct StateSkipSubStates1 { + /// State test number of this item. Or '*' for all state. + pub subnumbers: Vec, + /// Chain for this items. + pub chain: String, +} + +/// A set of difficulty tests +#[derive(Debug, PartialEq, Deserialize)] +pub struct DifficultyTests { + /// Path of the json tests + pub path: Vec, + /// Chain spec to use + pub chainspec: TestChainSpec, +} + +/// A set of executive tests +#[derive(Debug, PartialEq, Deserialize)] +pub struct ExecutiveTests { + /// Path of the json tests + pub path: PathBuf, +} + +/// A set of transaction tests +#[derive(Debug, PartialEq, Deserialize)] +pub struct TransactionTests { + /// Path of the json tests + pub path: PathBuf, +} + +/// A set of trie tests +#[derive(Debug, PartialEq, Deserialize)] +pub struct TrieTests { + /// Path of the json tests + pub path: Vec, + /// Trie spec to use + pub triespec: TestTrieSpec, } diff --git a/json/src/transaction/mod.rs b/json/src/transaction/mod.rs index f845fe3340a..3c9d92e0848 100644 --- a/json/src/transaction/mod.rs +++ b/json/src/transaction/mod.rs @@ -1,25 +1,23 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Transaction test deserialization. +mod test; mod transaction; mod txtest; -mod test; -pub use self::transaction::Transaction; -pub use self::txtest::TransactionTest; -pub use self::test::Test; +pub use self::{test::Test, transaction::Transaction, txtest::TransactionTest}; diff --git a/json/src/transaction/test.rs b/json/src/transaction/test.rs index ee9d4110f1f..2fd4a1c7d8f 100644 --- a/json/src/transaction/test.rs +++ b/json/src/transaction/test.rs @@ -1,25 +1,23 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! TransactionTest test deserializer. -use std::collections::BTreeMap; -use std::io::Read; -use serde_json; -use serde_json::Error; +use serde_json::{self, Error}; +use std::{collections::BTreeMap, io::Read}; use transaction::TransactionTest; /// TransactionTest test deserializer. @@ -27,17 +25,20 @@ use transaction::TransactionTest; pub struct Test(BTreeMap); impl IntoIterator for Test { - type Item = as IntoIterator>::Item; - type IntoIter = as IntoIterator>::IntoIter; + type Item = as IntoIterator>::Item; + type IntoIter = as IntoIterator>::IntoIter; - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } } impl Test { - /// Loads test from json. - pub fn load(reader: R) -> Result where R: Read { - serde_json::from_reader(reader) - } + /// Loads test from json. + pub fn load(reader: R) -> Result + where + R: Read, + { + serde_json::from_reader(reader) + } } diff --git a/json/src/transaction/transaction.rs b/json/src/transaction/transaction.rs index 718d3080bd2..5186918aaea 100644 --- a/json/src/transaction/transaction.rs +++ b/json/src/transaction/transaction.rs @@ -1,58 +1,58 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Transaction test transaction deserialization. -use uint::Uint; use bytes::Bytes; use hash::Address; use maybe::MaybeEmpty; +use uint::Uint; /// Transaction test transaction deserialization. #[derive(Debug, PartialEq, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Transaction { - /// Transaction data. - pub data: Bytes, - /// Gas limit. - pub gas_limit: Uint, - /// Gas price. - pub gas_price: Uint, - /// Nonce. - pub nonce: Uint, - /// To. - pub to: MaybeEmpty
, - /// Value. - pub value: Uint, - /// R. - pub r: Uint, - /// S. - pub s: Uint, - /// V. - pub v: Uint, + /// Transaction data. + pub data: Bytes, + /// Gas limit. + pub gas_limit: Uint, + /// Gas price. + pub gas_price: Uint, + /// Nonce. + pub nonce: Uint, + /// To. + pub to: MaybeEmpty
, + /// Value. + pub value: Uint, + /// R. + pub r: Uint, + /// S. + pub s: Uint, + /// V. + pub v: Uint, } #[cfg(test)] mod tests { - use serde_json; - use transaction::Transaction; + use serde_json; + use transaction::Transaction; - #[test] - fn transaction_deserialization() { - let s = r#"{ + #[test] + fn transaction_deserialization() { + let s = r#"{ "data" : "0x", "gasLimit" : "0xf388", "gasPrice" : "0x09184e72a000", @@ -63,7 +63,7 @@ mod tests { "v" : "0x1b", "value" : "0x00" }"#; - let _deserialized: Transaction = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } + let _deserialized: Transaction = serde_json::from_str(s).unwrap(); + // TODO: validate all fields + } } diff --git a/json/src/transaction/txtest.rs b/json/src/transaction/txtest.rs index e72e5f60bfa..e466374f6c3 100644 --- a/json/src/transaction/txtest.rs +++ b/json/src/transaction/txtest.rs @@ -1,54 +1,53 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Transaction test deserialization. -use std::collections::BTreeMap; use bytes::Bytes; -use hash::Address; -use hash::H256; +use hash::{Address, H256}; use spec::ForkSpec; +use std::collections::BTreeMap; /// Transaction test deserialization. #[derive(Debug, Deserialize)] pub struct TransactionTest { - pub rlp: Bytes, - pub _info: ::serde::de::IgnoredAny, - #[serde(flatten)] - pub post_state: BTreeMap, + pub rlp: Bytes, + pub _info: ::serde::de::IgnoredAny, + #[serde(flatten)] + pub post_state: BTreeMap, } /// TransactionTest post state. #[derive(Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] pub struct PostState { - /// Transaction sender. - pub sender: Option
, - /// Transaction hash. - pub hash: Option, + /// Transaction sender. + pub sender: Option
, + /// Transaction hash. + pub hash: Option, } #[cfg(test)] mod tests { - use serde_json; - use transaction::TransactionTest; + use serde_json; + use transaction::TransactionTest; - #[test] - fn transaction_deserialization() { - let s = r#"{ + #[test] + fn transaction_deserialization() { + let s = r#"{ "Byzantium" : { "hash" : "4782cb5edcaeda1f0aef204b161214f124cefade9e146245183abbb9ca01bca5", "sender" : "2ea991808ba979ba103147edfd72304ebd95c028" @@ -77,7 +76,7 @@ mod tests { "rlp" : "0xf865808698852840a46f82d6d894095e7baea6a6c7c4c2dfeb977efac326af552d87808025a098ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4aa01887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3" }"#; - let _deserialized: TransactionTest = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } + let _deserialized: TransactionTest = serde_json::from_str(s).unwrap(); + // TODO: validate all fields + } } diff --git a/json/src/trie/input.rs b/json/src/trie/input.rs index 4c56c10d7cd..0bdbc82db88 100644 --- a/json/src/trie/input.rs +++ b/json/src/trie/input.rs @@ -1,156 +1,179 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Trie test input deserialization. -use std::fmt; -use std::collections::BTreeMap; -use std::str::FromStr; use bytes::Bytes; -use serde::{Deserialize, Deserializer}; -use serde::de::{Error as ErrorTrait, Visitor, MapAccess, SeqAccess}; +use serde::{ + de::{Error as ErrorTrait, MapAccess, SeqAccess, Visitor}, + Deserialize, Deserializer, +}; +use std::{collections::BTreeMap, fmt, str::FromStr}; /// Trie test input. #[derive(Debug, PartialEq)] pub struct Input { - /// Input params. - pub data: BTreeMap>, + /// Input params. + pub data: BTreeMap>, } impl<'a> Deserialize<'a> for Input { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> - { - deserializer.deserialize_any(InputVisitor) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + deserializer.deserialize_any(InputVisitor) + } } struct InputVisitor; impl<'a> Visitor<'a> for InputVisitor { - type Value = Input; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a map of bytes into bytes") - } - - fn visit_map(self, mut visitor: V) -> Result where V: MapAccess<'a> { - let mut result = BTreeMap::new(); - - loop { - let key_str: Option = visitor.next_key()?; - let key = match key_str { - Some(ref k) if k.starts_with("0x") => Bytes::from_str(k).map_err(V::Error::custom)?, - Some(k) => Bytes::new(k.into_bytes()), - None => { break; } - }; - - let val_str: Option = visitor.next_value()?; - let val = match val_str { - Some(ref v) if v.starts_with("0x") => Some(Bytes::from_str(v).map_err(V::Error::custom)?), - Some(v) => Some(Bytes::new(v.into_bytes())), - None => None, - }; - - result.insert(key, val); - } - - let input = Input { - data: result - }; - - Ok(input) - } - - fn visit_seq(self, mut visitor: V) -> Result where V: SeqAccess<'a> { - let mut result = BTreeMap::new(); - - loop { - let keyval: Option>> = visitor.next_element()?; - let keyval = match keyval { - Some(k) => k, - _ => { break; }, - }; - - if keyval.len() != 2 { - return Err(V::Error::custom("Invalid key value pair.")); - } - - let ref key_str: Option = keyval[0]; - let ref val_str: Option = keyval[1]; - - let key = match *key_str { - Some(ref k) if k.starts_with("0x") => Bytes::from_str(k).map_err(V::Error::custom)?, - Some(ref k) => Bytes::new(k.clone().into_bytes()), - None => { break; } - }; - - let val = match *val_str { - Some(ref v) if v.starts_with("0x") => Some(Bytes::from_str(v).map_err(V::Error::custom)?), - Some(ref v) => Some(Bytes::new(v.clone().into_bytes())), - None => None, - }; - - result.insert(key, val); - } - - let input = Input { - data: result - }; - - Ok(input) - } + type Value = Input; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a map of bytes into bytes") + } + + fn visit_map(self, mut visitor: V) -> Result + where + V: MapAccess<'a>, + { + let mut result = BTreeMap::new(); + + loop { + let key_str: Option = visitor.next_key()?; + let key = match key_str { + Some(ref k) if k.starts_with("0x") => { + Bytes::from_str(k).map_err(V::Error::custom)? + } + Some(k) => Bytes::new(k.into_bytes()), + None => { + break; + } + }; + + let val_str: Option = visitor.next_value()?; + let val = match val_str { + Some(ref v) if v.starts_with("0x") => { + Some(Bytes::from_str(v).map_err(V::Error::custom)?) + } + Some(v) => Some(Bytes::new(v.into_bytes())), + None => None, + }; + + result.insert(key, val); + } + + let input = Input { data: result }; + + Ok(input) + } + + fn visit_seq(self, mut visitor: V) -> Result + where + V: SeqAccess<'a>, + { + let mut result = BTreeMap::new(); + + loop { + let keyval: Option>> = visitor.next_element()?; + let keyval = match keyval { + Some(k) => k, + _ => { + break; + } + }; + + if keyval.len() != 2 { + return Err(V::Error::custom("Invalid key value pair.")); + } + + let ref key_str: Option = keyval[0]; + let ref val_str: Option = keyval[1]; + + let key = match *key_str { + Some(ref k) if k.starts_with("0x") => { + Bytes::from_str(k).map_err(V::Error::custom)? + } + Some(ref k) => Bytes::new(k.clone().into_bytes()), + None => { + break; + } + }; + + let val = match *val_str { + Some(ref v) if v.starts_with("0x") => { + Some(Bytes::from_str(v).map_err(V::Error::custom)?) + } + Some(ref v) => Some(Bytes::new(v.clone().into_bytes())), + None => None, + }; + + result.insert(key, val); + } + + let input = Input { data: result }; + + Ok(input) + } } #[cfg(test)] mod tests { - use std::collections::BTreeMap; - use serde_json; - use bytes::Bytes; - use super::Input; - - #[test] - fn input_deserialization_from_map() { - let s = r#"{ + use super::Input; + use bytes::Bytes; + use serde_json; + use std::collections::BTreeMap; + + #[test] + fn input_deserialization_from_map() { + let s = r#"{ "0x0045" : "0x0123456789", "be" : "e", "0x0a" : null }"#; - let input: Input = serde_json::from_str(s).unwrap(); - let mut map = BTreeMap::new(); - map.insert(Bytes::new(vec![0, 0x45]), Some(Bytes::new(vec![0x01, 0x23, 0x45, 0x67, 0x89]))); - map.insert(Bytes::new(vec![0x62, 0x65]), Some(Bytes::new(vec![0x65]))); - map.insert(Bytes::new(vec![0x0a]), None); - assert_eq!(input.data, map); - } - - #[test] - fn input_deserialization_from_array() { - let s = r#"[ + let input: Input = serde_json::from_str(s).unwrap(); + let mut map = BTreeMap::new(); + map.insert( + Bytes::new(vec![0, 0x45]), + Some(Bytes::new(vec![0x01, 0x23, 0x45, 0x67, 0x89])), + ); + map.insert(Bytes::new(vec![0x62, 0x65]), Some(Bytes::new(vec![0x65]))); + map.insert(Bytes::new(vec![0x0a]), None); + assert_eq!(input.data, map); + } + + #[test] + fn input_deserialization_from_array() { + let s = r#"[ ["0x0045", "0x0123456789"], ["be", "e"], ["0x0a", null] ]"#; - let input: Input = serde_json::from_str(s).unwrap(); - let mut map = BTreeMap::new(); - map.insert(Bytes::new(vec![0, 0x45]), Some(Bytes::new(vec![0x01, 0x23, 0x45, 0x67, 0x89]))); - map.insert(Bytes::new(vec![0x62, 0x65]), Some(Bytes::new(vec![0x65]))); - map.insert(Bytes::new(vec![0x0a]), None); - assert_eq!(input.data, map); - } + let input: Input = serde_json::from_str(s).unwrap(); + let mut map = BTreeMap::new(); + map.insert( + Bytes::new(vec![0, 0x45]), + Some(Bytes::new(vec![0x01, 0x23, 0x45, 0x67, 0x89])), + ); + map.insert(Bytes::new(vec![0x62, 0x65]), Some(Bytes::new(vec![0x65]))); + map.insert(Bytes::new(vec![0x0a]), None); + assert_eq!(input.data, map); + } } diff --git a/json/src/trie/mod.rs b/json/src/trie/mod.rs index 68fec087617..8502505e85c 100644 --- a/json/src/trie/mod.rs +++ b/json/src/trie/mod.rs @@ -1,25 +1,23 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Trie test deserialization. mod input; -mod trie; mod test; +mod trie; -pub use self::input::Input; -pub use self::trie::Trie; -pub use self::test::Test; +pub use self::{input::Input, test::Test, trie::Trie}; diff --git a/json/src/trie/test.rs b/json/src/trie/test.rs index 39876da0a16..eb2bcea9594 100644 --- a/json/src/trie/test.rs +++ b/json/src/trie/test.rs @@ -1,25 +1,23 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! TransactionTest test deserializer. -use std::collections::BTreeMap; -use std::io::Read; -use serde_json; -use serde_json::Error; +use serde_json::{self, Error}; +use std::{collections::BTreeMap, io::Read}; use trie::Trie; /// TransactionTest test deserializer. @@ -27,17 +25,20 @@ use trie::Trie; pub struct Test(BTreeMap); impl IntoIterator for Test { - type Item = as IntoIterator>::Item; - type IntoIter = as IntoIterator>::IntoIter; + type Item = as IntoIterator>::Item; + type IntoIter = as IntoIterator>::IntoIter; - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } } impl Test { - /// Loads test from json. - pub fn load(reader: R) -> Result where R: Read { - serde_json::from_reader(reader) - } + /// Loads test from json. + pub fn load(reader: R) -> Result + where + R: Read, + { + serde_json::from_reader(reader) + } } diff --git a/json/src/trie/trie.rs b/json/src/trie/trie.rs index 756e54f4351..ec8f9466daf 100644 --- a/json/src/trie/trie.rs +++ b/json/src/trie/trie.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Trie test deserialization. @@ -22,9 +22,9 @@ use trie::Input; /// Trie test deserialization. #[derive(Debug, Deserialize, PartialEq)] pub struct Trie { - /// Trie test input. - #[serde(rename = "in")] - pub input: Input, - /// Trie root hash. - pub root: H256, + /// Trie test input. + #[serde(rename = "in")] + pub input: Input, + /// Trie root hash. + pub root: H256, } diff --git a/json/src/uint.rs b/json/src/uint.rs index 3f1d02f03e7..0df6131d63b 100644 --- a/json/src/uint.rs +++ b/json/src/uint.rs @@ -1,145 +1,176 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Lenient uint json deserialization for test json files. -use std::fmt; -use std::str::FromStr; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use serde::de::{Error, Visitor, Unexpected}; use ethereum_types::U256; +use serde::{ + de::{Error, Unexpected, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; +use std::{fmt, str::FromStr}; /// Lenient uint json deserialization for test json files. #[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] pub struct Uint(pub U256); impl Into for Uint { - fn into(self) -> U256 { - self.0 - } + fn into(self) -> U256 { + self.0 + } } impl Into for Uint { - fn into(self) -> u64 { - u64::from(self.0) - } + fn into(self) -> u64 { + u64::from(self.0) + } } impl Into for Uint { - fn into(self) -> usize { - // TODO: clean it after util conversions refactored. - u64::from(self.0) as usize - } + fn into(self) -> usize { + // TODO: clean it after util conversions refactored. + u64::from(self.0) as usize + } } impl Into for Uint { - fn into(self) -> u8 { - u64::from(self.0) as u8 - } + fn into(self) -> u8 { + u64::from(self.0) as u8 + } } impl Serialize for Uint { - fn serialize(&self, serializer: S) -> Result - where S: Serializer { - self.0.to_string().serialize(serializer) - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.0.to_string().serialize(serializer) + } } impl<'a> Deserialize<'a> for Uint { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> { - deserializer.deserialize_any(UintVisitor) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + deserializer.deserialize_any(UintVisitor) + } } struct UintVisitor; impl<'a> Visitor<'a> for UintVisitor { - type Value = Uint; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a hex encoded or decimal uint") - } - - fn visit_u64(self, value: u64) -> Result where E: Error { - Ok(Uint(U256::from(value))) - } - - fn visit_str(self, value: &str) -> Result where E: Error { - let value = match value.len() { - 0 => U256::from(0), - 2 if value.starts_with("0x") => U256::from(0), - _ if value.starts_with("0x") => U256::from_str(&value[2..]).map_err(|e| { - Error::custom(format!("Invalid hex value {}: {}", value, e).as_str()) - })?, - _ => U256::from_dec_str(value).map_err(|e| { - Error::custom(format!("Invalid decimal value {}: {:?}", value, e).as_str()) - })? - }; - - Ok(Uint(value)) - } - - fn visit_string(self, value: String) -> Result where E: Error { - self.visit_str(value.as_ref()) - } + type Value = Uint; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a hex encoded or decimal uint") + } + + fn visit_u64(self, value: u64) -> Result + where + E: Error, + { + Ok(Uint(U256::from(value))) + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + let value = match value.len() { + 0 => U256::from(0), + 2 if value.starts_with("0x") => U256::from(0), + _ if value.starts_with("0x") => U256::from_str(&value[2..]).map_err(|e| { + Error::custom(format!("Invalid hex value {}: {}", value, e).as_str()) + })?, + _ => U256::from_dec_str(value).map_err(|e| { + Error::custom(format!("Invalid decimal value {}: {:?}", value, e).as_str()) + })?, + }; + + Ok(Uint(value)) + } + + fn visit_string(self, value: String) -> Result + where + E: Error, + { + self.visit_str(value.as_ref()) + } } -pub fn validate_non_zero<'de, D>(d: D) -> Result where D: Deserializer<'de> { - let value = Uint::deserialize(d)?; - - if value == Uint(U256::from(0)) { - return Err(Error::invalid_value(Unexpected::Unsigned(value.into()), &"a non-zero value")) - } - - Ok(value) +/// Deserialize and validate that the value is non-zero +pub fn validate_non_zero<'de, D>(d: D) -> Result +where + D: Deserializer<'de>, +{ + let value = Uint::deserialize(d)?; + + if value == Uint(U256::from(0)) { + return Err(Error::invalid_value( + Unexpected::Unsigned(value.into()), + &"a non-zero value", + )); + } + + Ok(value) } -pub fn validate_optional_non_zero<'de, D>(d: D) -> Result, D::Error> where D: Deserializer<'de> { - let value: Option = Option::deserialize(d)?; - - if let Some(value) = value { - if value == Uint(U256::from(0)) { - return Err(Error::invalid_value(Unexpected::Unsigned(value.into()), &"a non-zero value")) - } - } - - Ok(value) +/// Deserialize and validate that the value is non-zero +pub fn validate_optional_non_zero<'de, D>(d: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let value: Option = Option::deserialize(d)?; + + if let Some(value) = value { + if value == Uint(U256::from(0)) { + return Err(Error::invalid_value( + Unexpected::Unsigned(value.into()), + &"a non-zero value", + )); + } + } + + Ok(value) } #[cfg(test)] mod test { - use serde_json; - use ethereum_types::U256; - use uint::Uint; - - #[test] - fn uint_deserialization() { - let s = r#"["0xa", "10", "", "0x", 0]"#; - let deserialized: Vec = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, vec![ - Uint(U256::from(10)), - Uint(U256::from(10)), - Uint(U256::from(0)), - Uint(U256::from(0)), - Uint(U256::from(0)) - ]); - } - - #[test] - fn uint_into() { - assert_eq!(U256::from(10), Uint(U256::from(10)).into()); - } + use ethereum_types::U256; + use serde_json; + use uint::Uint; + + #[test] + fn uint_deserialization() { + let s = r#"["0xa", "10", "", "0x", 0]"#; + let deserialized: Vec = serde_json::from_str(s).unwrap(); + assert_eq!( + deserialized, + vec![ + Uint(U256::from(10)), + Uint(U256::from(10)), + Uint(U256::from(0)), + Uint(U256::from(0)), + Uint(U256::from(0)) + ] + ); + } + + #[test] + fn uint_into() { + assert_eq!(U256::from(10), Uint(U256::from(10)).into()); + } } diff --git a/json/src/vm/call.rs b/json/src/vm/call.rs index aa75862f0dd..7dbb46a1a35 100644 --- a/json/src/vm/call.rs +++ b/json/src/vm/call.rs @@ -1,84 +1,93 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Vm call deserialization. use bytes::Bytes; use hash::Address; -use uint::Uint; use maybe::MaybeEmpty; +use uint::Uint; /// Vm call deserialization. #[derive(Debug, PartialEq, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Call { - /// Call data. - pub data: Bytes, - /// Call destination. - pub destination: MaybeEmpty
, - /// Gas limit. - pub gas_limit: Uint, - /// Call value. - pub value: Uint, + /// Call data. + pub data: Bytes, + /// Call destination. + pub destination: MaybeEmpty
, + /// Gas limit. + pub gas_limit: Uint, + /// Call value. + pub value: Uint, } #[cfg(test)] mod tests { - use serde_json; - use vm::Call; - use ethereum_types::{U256, H160 as Hash160}; - use uint::Uint; - use hash::Address; - use maybe::MaybeEmpty; - use std::str::FromStr; + use ethereum_types::{H160 as Hash160, U256}; + use hash::Address; + use maybe::MaybeEmpty; + use serde_json; + use std::str::FromStr; + use uint::Uint; + use vm::Call; - #[test] - fn call_deserialization_empty_dest() { - let s = r#"{ + #[test] + fn call_deserialization_empty_dest() { + let s = r#"{ "data" : "0x1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff", "destination" : "", "gasLimit" : "0x1748766aa5", "value" : "0x00" }"#; - let call: Call = serde_json::from_str(s).unwrap(); + let call: Call = serde_json::from_str(s).unwrap(); - assert_eq!(&call.data[..], - &[0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66, 0x66, 0x77, 0x77, - 0x88, 0x88, 0x99, 0x99, 0x00, 0x00, 0xaa, 0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, - 0xee, 0xee, 0xff, 0xff]); + assert_eq!( + &call.data[..], + &[ + 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66, 0x66, 0x77, 0x77, + 0x88, 0x88, 0x99, 0x99, 0x00, 0x00, 0xaa, 0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, + 0xee, 0xee, 0xff, 0xff + ] + ); - assert_eq!(call.destination, MaybeEmpty::None); - assert_eq!(call.gas_limit, Uint(U256::from(0x1748766aa5u64))); - assert_eq!(call.value, Uint(U256::from(0))); - } + assert_eq!(call.destination, MaybeEmpty::None); + assert_eq!(call.gas_limit, Uint(U256::from(0x1748766aa5u64))); + assert_eq!(call.value, Uint(U256::from(0))); + } - #[test] - fn call_deserialization_full_dest() { - let s = r#"{ + #[test] + fn call_deserialization_full_dest() { + let s = r#"{ "data" : "0x1234", "destination" : "5a39ed1020c04d4d84539975b893a4e7c53eab6c", "gasLimit" : "0x1748766aa5", "value" : "0x00" }"#; - let call: Call = serde_json::from_str(s).unwrap(); + let call: Call = serde_json::from_str(s).unwrap(); - assert_eq!(&call.data[..], &[0x12, 0x34]); - assert_eq!(call.destination, MaybeEmpty::Some(Address(Hash160::from_str("5a39ed1020c04d4d84539975b893a4e7c53eab6c").unwrap()))); - assert_eq!(call.gas_limit, Uint(U256::from(0x1748766aa5u64))); - assert_eq!(call.value, Uint(U256::from(0))); - } + assert_eq!(&call.data[..], &[0x12, 0x34]); + assert_eq!( + call.destination, + MaybeEmpty::Some(Address( + Hash160::from_str("5a39ed1020c04d4d84539975b893a4e7c53eab6c").unwrap() + )) + ); + assert_eq!(call.gas_limit, Uint(U256::from(0x1748766aa5u64))); + assert_eq!(call.value, Uint(U256::from(0))); + } } diff --git a/json/src/vm/env.rs b/json/src/vm/env.rs index e06812c0a87..09bcda0d610 100644 --- a/json/src/vm/env.rs +++ b/json/src/vm/env.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Vm environment. use hash::Address; @@ -21,38 +21,38 @@ use uint::Uint; /// Vm environment. #[derive(Debug, PartialEq, Deserialize)] pub struct Env { - /// Address. - #[serde(rename = "currentCoinbase")] - pub author: Address, - /// Difficulty - #[serde(rename = "currentDifficulty")] - pub difficulty: Uint, - /// Gas limit. - #[serde(rename = "currentGasLimit")] - pub gas_limit: Uint, - /// Number. - #[serde(rename = "currentNumber")] - pub number: Uint, - /// Timestamp. - #[serde(rename = "currentTimestamp")] - pub timestamp: Uint, + /// Address. + #[serde(rename = "currentCoinbase")] + pub author: Address, + /// Difficulty + #[serde(rename = "currentDifficulty")] + pub difficulty: Uint, + /// Gas limit. + #[serde(rename = "currentGasLimit")] + pub gas_limit: Uint, + /// Number. + #[serde(rename = "currentNumber")] + pub number: Uint, + /// Timestamp. + #[serde(rename = "currentTimestamp")] + pub timestamp: Uint, } #[cfg(test)] mod tests { - use serde_json; - use vm::Env; + use serde_json; + use vm::Env; - #[test] - fn env_deserialization() { - let s = r#"{ + #[test] + fn env_deserialization() { + let s = r#"{ "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "0x0100", "currentGasLimit" : "0x0f4240", "currentNumber" : "0x00", "currentTimestamp" : "0x01" }"#; - let _deserialized: Env = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } + let _deserialized: Env = serde_json::from_str(s).unwrap(); + // TODO: validate all fields + } } diff --git a/json/src/vm/mod.rs b/json/src/vm/mod.rs index d8f99e20021..e22fbf35c53 100644 --- a/json/src/vm/mod.rs +++ b/json/src/vm/mod.rs @@ -1,29 +1,25 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Vm test loader. +pub mod call; pub mod env; +pub mod test; pub mod transaction; pub mod vm; -pub mod call; -pub mod test; -pub use self::env::Env; -pub use self::transaction::Transaction; -pub use self::vm::Vm; -pub use self::call::Call; -pub use self::test::Test; +pub use self::{call::Call, env::Env, test::Test, transaction::Transaction, vm::Vm}; diff --git a/json/src/vm/test.rs b/json/src/vm/test.rs index 9dfe814ae82..7e40d8f2287 100644 --- a/json/src/vm/test.rs +++ b/json/src/vm/test.rs @@ -1,25 +1,23 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Vm test deserializer. -use std::collections::BTreeMap; -use std::io::Read; -use serde_json; -use serde_json::Error; +use serde_json::{self, Error}; +use std::{collections::BTreeMap, io::Read}; use vm::Vm; /// Vm test deserializer. @@ -27,17 +25,20 @@ use vm::Vm; pub struct Test(BTreeMap); impl IntoIterator for Test { - type Item = as IntoIterator>::Item; - type IntoIter = as IntoIterator>::IntoIter; + type Item = as IntoIterator>::Item; + type IntoIter = as IntoIterator>::IntoIter; - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } } impl Test { - /// Loads test from json. - pub fn load(reader: R) -> Result where R: Read { - serde_json::from_reader(reader) - } + /// Loads test from json. + pub fn load(reader: R) -> Result + where + R: Read, + { + serde_json::from_reader(reader) + } } diff --git a/json/src/vm/transaction.rs b/json/src/vm/transaction.rs index 4a9374531d5..87e79965b9f 100644 --- a/json/src/vm/transaction.rs +++ b/json/src/vm/transaction.rs @@ -1,55 +1,55 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Executed transaction. +use bytes::Bytes; use hash::Address; use uint::Uint; -use bytes::Bytes; /// Executed transaction. #[derive(Debug, PartialEq, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Transaction { - /// Contract address. - pub address: Address, - /// Transaction sender. - #[serde(rename = "caller")] - pub sender: Address, - /// Contract code. - pub code: Bytes, - /// Input data. - pub data: Bytes, - /// Gas. - pub gas: Uint, - /// Gas price. - pub gas_price: Uint, - /// Transaction origin. - pub origin: Address, - /// Sent value. - pub value: Uint, + /// Contract address. + pub address: Address, + /// Transaction sender. + #[serde(rename = "caller")] + pub sender: Address, + /// Contract code. + pub code: Bytes, + /// Input data. + pub data: Bytes, + /// Gas. + pub gas: Uint, + /// Gas price. + pub gas_price: Uint, + /// Transaction origin. + pub origin: Address, + /// Sent value. + pub value: Uint, } #[cfg(test)] mod tests { - use serde_json; - use vm::Transaction; + use serde_json; + use vm::Transaction; - #[test] - fn transaction_deserialization() { - let s = r#"{ + #[test] + fn transaction_deserialization() { + let s = r#"{ "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055", @@ -59,6 +59,6 @@ mod tests { "origin" : "cd1722f2947def4cf144679da39c4c32bdc35681", "value" : "0x0de0b6b3a7640000" }"#; - let _deserialized: Transaction = serde_json::from_str(s).unwrap(); - } + let _deserialized: Transaction = serde_json::from_str(s).unwrap(); + } } diff --git a/json/src/vm/vm.rs b/json/src/vm/vm.rs index 1fbb937cb7f..0945449e7ee 100644 --- a/json/src/vm/vm.rs +++ b/json/src/vm/vm.rs @@ -1,69 +1,69 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Vm execution env. +use blockchain::State; use bytes::Bytes; -use uint::Uint; use hash::H256; -use blockchain::State; -use vm::{Transaction, Call, Env}; +use uint::Uint; +use vm::{Call, Env, Transaction}; /// Represents vm execution environment before and after execution of transaction. #[derive(Debug, PartialEq, Deserialize)] pub struct Vm { - /// Contract calls made internaly by executed transaction. - #[serde(rename = "callcreates")] - pub calls: Option>, - /// Env info. - pub env: Env, - /// Executed transaction - #[serde(rename = "exec")] - pub transaction: Transaction, - /// Gas left after transaction execution. - #[serde(rename = "gas")] - pub gas_left: Option, - /// Hash of logs created during execution of transaction. - pub logs: Option, - /// Transaction output. - #[serde(rename = "out")] - pub output: Option, - /// Post execution vm state. - #[serde(rename = "post")] - pub post_state: Option, - /// Pre execution vm state. - #[serde(rename = "pre")] - pub pre_state: State, + /// Contract calls made internaly by executed transaction. + #[serde(rename = "callcreates")] + pub calls: Option>, + /// Env info. + pub env: Env, + /// Executed transaction + #[serde(rename = "exec")] + pub transaction: Transaction, + /// Gas left after transaction execution. + #[serde(rename = "gas")] + pub gas_left: Option, + /// Hash of logs created during execution of transaction. + pub logs: Option, + /// Transaction output. + #[serde(rename = "out")] + pub output: Option, + /// Post execution vm state. + #[serde(rename = "post")] + pub post_state: Option, + /// Pre execution vm state. + #[serde(rename = "pre")] + pub pre_state: State, } impl Vm { - /// Returns true if transaction execution run out of gas. - pub fn out_of_gas(&self) -> bool { - self.calls.is_none() - } + /// Returns true if transaction execution run out of gas. + pub fn out_of_gas(&self) -> bool { + self.calls.is_none() + } } #[cfg(test)] mod tests { - use serde_json; - use vm::Vm; + use serde_json; + use vm::Vm; - #[test] - fn vm_deserialization() { - let s = r#"{ + #[test] + fn vm_deserialization() { + let s = r#"{ "callcreates" : [ ], "env" : { @@ -107,7 +107,7 @@ mod tests { } } }"#; - let _deserialized: Vm = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } + let _deserialized: Vm = serde_json::from_str(s).unwrap(); + // TODO: validate all fields + } } diff --git a/license_header b/license_header index 2fc672e4f1f..dfdfdc1d221 100644 --- a/license_header +++ b/license_header @@ -1,16 +1,16 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . diff --git a/miner/Cargo.toml b/miner/Cargo.toml index f7dfd8f0836..22e2e7dd849 100644 --- a/miner/Cargo.toml +++ b/miner/Cargo.toml @@ -1,7 +1,7 @@ [package] -description = "Parity Miner interface." +description = "OpenEthereum Miner Interface." name = "ethcore-miner" -homepage = "http://parity.io" +homepage = "https://github.com/openethereum/openethereum" license = "GPL-3.0" version = "1.12.0" authors = ["Parity Technologies "] @@ -11,7 +11,7 @@ authors = ["Parity Technologies "] ethash = { path = "../ethash", optional = true } fetch = { path = "../util/fetch", optional = true } hyper = { version = "0.12", optional = true } -url = { version = "1", optional = true } +url = { version = "2", optional = true } # Miner ansi_term = "0.10" @@ -32,7 +32,7 @@ parking_lot = "0.7" price-info = { path = "./price-info", optional = true } rlp = { version = "0.3.0", features = ["ethereum"] } trace-time = "0.1" -transaction-pool = "2.0" +transaction-pool = "2.0.1" [dev-dependencies] env_logger = "0.5" diff --git a/miner/local-store/src/lib.rs b/miner/local-store/src/lib.rs index 56c573b06ff..b05efe2e00d 100644 --- a/miner/local-store/src/lib.rs +++ b/miner/local-store/src/lib.rs @@ -1,39 +1,36 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Manages local node data: pending local transactions, sync security level -use std::sync::Arc; -use std::fmt; -use std::time::Duration; +use std::{fmt, sync::Arc, time::Duration}; -use types::transaction::{ - SignedTransaction, PendingTransaction, UnverifiedTransaction, - Condition as TransactionCondition -}; use io::IoHandler; -use rlp::Rlp; use kvdb::KeyValueDB; +use rlp::Rlp; +use types::transaction::{ + Condition as TransactionCondition, PendingTransaction, SignedTransaction, UnverifiedTransaction, +}; extern crate common_types as types; extern crate ethcore_io as io; +extern crate kvdb; extern crate rlp; -extern crate serde_json; extern crate serde; -extern crate kvdb; +extern crate serde_json; #[macro_use] extern crate serde_derive; @@ -54,95 +51,99 @@ const UPDATE_TIMEOUT: Duration = Duration::from_secs(15 * 60); // once every 15 /// Errors which can occur while using the local data store. #[derive(Debug)] pub enum Error { - /// Io and database errors: these manifest as `String`s. - Io(::std::io::Error), - /// JSON errors. - Json(::serde_json::Error), + /// Io and database errors: these manifest as `String`s. + Io(::std::io::Error), + /// JSON errors. + Json(::serde_json::Error), } impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::Io(ref val) => write!(f, "{}", val), - Error::Json(ref err) => write!(f, "{}", err), - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::Io(ref val) => write!(f, "{}", val), + Error::Json(ref err) => write!(f, "{}", err), + } + } } #[derive(Serialize, Deserialize)] enum Condition { - Number(types::BlockNumber), - Timestamp(u64), + Number(types::BlockNumber), + Timestamp(u64), } impl From for Condition { - fn from(cond: TransactionCondition) -> Self { - match cond { - TransactionCondition::Number(num) => Condition::Number(num), - TransactionCondition::Timestamp(tm) => Condition::Timestamp(tm), - } - } + fn from(cond: TransactionCondition) -> Self { + match cond { + TransactionCondition::Number(num) => Condition::Number(num), + TransactionCondition::Timestamp(tm) => Condition::Timestamp(tm), + } + } } impl Into for Condition { - fn into(self) -> TransactionCondition { - match self { - Condition::Number(num) => TransactionCondition::Number(num), - Condition::Timestamp(tm) => TransactionCondition::Timestamp(tm), - } - } + fn into(self) -> TransactionCondition { + match self { + Condition::Number(num) => TransactionCondition::Number(num), + Condition::Timestamp(tm) => TransactionCondition::Timestamp(tm), + } + } } #[derive(Serialize, Deserialize)] struct TransactionEntry { - rlp_bytes: Vec, - condition: Option, + rlp_bytes: Vec, + condition: Option, } impl TransactionEntry { - fn into_pending(self) -> Option { - let tx: UnverifiedTransaction = match Rlp::new(&self.rlp_bytes).as_val() { - Err(e) => { - warn!(target: "local_store", "Invalid persistent transaction stored: {}", e); - return None - } - Ok(tx) => tx, - }; - - let hash = tx.hash(); - match SignedTransaction::new(tx) { - Ok(tx) => Some(PendingTransaction::new(tx, self.condition.map(Into::into))), - Err(_) => { - warn!(target: "local_store", "Bad signature on persistent transaction: {}", hash); - return None - } - } - } + fn into_pending(self) -> Option { + let tx: UnverifiedTransaction = match Rlp::new(&self.rlp_bytes).as_val() { + Err(e) => { + warn!(target: "local_store", "Invalid persistent transaction stored: {}", e); + return None; + } + Ok(tx) => tx, + }; + + let hash = tx.hash(); + match SignedTransaction::new(tx) { + Ok(tx) => Some(PendingTransaction::new(tx, self.condition.map(Into::into))), + Err(_) => { + warn!(target: "local_store", "Bad signature on persistent transaction: {}", hash); + return None; + } + } + } } impl From for TransactionEntry { - fn from(pending: PendingTransaction) -> Self { - TransactionEntry { - rlp_bytes: ::rlp::encode(&pending.transaction), - condition: pending.condition.map(Into::into), - } - } + fn from(pending: PendingTransaction) -> Self { + TransactionEntry { + rlp_bytes: ::rlp::encode(&pending.transaction), + condition: pending.condition.map(Into::into), + } + } } /// Something which can provide information about the local node. pub trait NodeInfo: Send + Sync { - /// Get all pending transactions of local origin. - fn pending_transactions(&self) -> Vec; + /// Get all pending transactions of local origin. + fn pending_transactions(&self) -> Vec; } /// Create a new local data store, given a database, a column to write to, and a node. /// Attempts to read data out of the store, and move it into the node. -pub fn create(db: Arc, col: Option, node: T) -> LocalDataStore { - LocalDataStore { - db: db, - col: col, - node: node, - } +pub fn create( + db: Arc, + col: Option, + node: T, +) -> LocalDataStore { + LocalDataStore { + db: db, + col: col, + node: node, + } } /// Manages local node data. @@ -150,180 +151,192 @@ pub fn create(db: Arc, col: Option, node: T) -> Lo /// In specific, this will be used to store things like unpropagated local transactions /// and the node security level. pub struct LocalDataStore { - db: Arc, - col: Option, - node: T, + db: Arc, + col: Option, + node: T, } impl LocalDataStore { - /// Attempt to read pending transactions out of the local store. - pub fn pending_transactions(&self) -> Result, Error> { - if let Some(val) = self.db.get(self.col, LOCAL_TRANSACTIONS_KEY).map_err(Error::Io)? { - let local_txs: Vec<_> = ::serde_json::from_slice::>(&val) - .map_err(Error::Json)? - .into_iter() - .filter_map(TransactionEntry::into_pending) - .collect(); - - Ok(local_txs) - } else { - Ok(Vec::new()) - } - } - - /// Update the entries in the database. - pub fn update(&self) -> Result<(), Error> { - trace!(target: "local_store", "Updating local store entries."); - - let local_entries: Vec = self.node.pending_transactions() - .into_iter() - .map(Into::into) - .collect(); - - self.write_txs(&local_entries) - } - - /// Clear data in this column. - pub fn clear(&self) -> Result<(), Error> { - trace!(target: "local_store", "Clearing local store entries."); - - self.write_txs(&[]) - } - - // helper for writing a vector of transaction entries to disk. - fn write_txs(&self, txs: &[TransactionEntry]) -> Result<(), Error> { - let mut batch = self.db.transaction(); - - let local_json = ::serde_json::to_value(txs).map_err(Error::Json)?; - let json_str = format!("{}", local_json); - - batch.put_vec(self.col, LOCAL_TRANSACTIONS_KEY, json_str.into_bytes()); - self.db.write(batch).map_err(Error::Io) - } + /// Attempt to read pending transactions out of the local store. + pub fn pending_transactions(&self) -> Result, Error> { + if let Some(val) = self + .db + .get(self.col, LOCAL_TRANSACTIONS_KEY) + .map_err(Error::Io)? + { + let local_txs: Vec<_> = ::serde_json::from_slice::>(&val) + .map_err(Error::Json)? + .into_iter() + .filter_map(TransactionEntry::into_pending) + .collect(); + + Ok(local_txs) + } else { + Ok(Vec::new()) + } + } + + /// Update the entries in the database. + pub fn update(&self) -> Result<(), Error> { + trace!(target: "local_store", "Updating local store entries."); + + let local_entries: Vec = self + .node + .pending_transactions() + .into_iter() + .map(Into::into) + .collect(); + + self.write_txs(&local_entries) + } + + /// Clear data in this column. + pub fn clear(&self) -> Result<(), Error> { + trace!(target: "local_store", "Clearing local store entries."); + + self.write_txs(&[]) + } + + // helper for writing a vector of transaction entries to disk. + fn write_txs(&self, txs: &[TransactionEntry]) -> Result<(), Error> { + let mut batch = self.db.transaction(); + + let local_json = ::serde_json::to_value(txs).map_err(Error::Json)?; + let json_str = format!("{}", local_json); + + batch.put_vec(self.col, LOCAL_TRANSACTIONS_KEY, json_str.into_bytes()); + self.db.write(batch).map_err(Error::Io) + } } impl IoHandler for LocalDataStore { - fn initialize(&self, io: &::io::IoContext) { - if let Err(e) = io.register_timer(UPDATE_TIMER, UPDATE_TIMEOUT) { - warn!(target: "local_store", "Error registering local store update timer: {}", e); - } - } - - fn timeout(&self, _io: &::io::IoContext, timer: ::io::TimerToken) { - if let UPDATE_TIMER = timer { - if let Err(e) = self.update() { - debug!(target: "local_store", "Error updating local store: {}", e); - } - } - } + fn initialize(&self, io: &::io::IoContext) { + if let Err(e) = io.register_timer(UPDATE_TIMER, UPDATE_TIMEOUT) { + warn!(target: "local_store", "Error registering local store update timer: {}", e); + } + } + + fn timeout(&self, _io: &::io::IoContext, timer: ::io::TimerToken) { + if let UPDATE_TIMER = timer { + if let Err(e) = self.update() { + debug!(target: "local_store", "Error updating local store: {}", e); + } + } + } } impl Drop for LocalDataStore { - fn drop(&mut self) { - debug!(target: "local_store", "Updating node data store on shutdown."); + fn drop(&mut self) { + debug!(target: "local_store", "Updating node data store on shutdown."); - let _ = self.update(); - } + let _ = self.update(); + } } #[cfg(test)] mod tests { - use super::NodeInfo; - - use std::sync::Arc; - use types::transaction::{Transaction, Condition, PendingTransaction}; - use ethkey::{Brain, Generator}; - - // we want to test: round-trip of good transactions. - // failure to roundtrip bad transactions (but that it doesn't panic) - - struct Dummy(Vec); - impl NodeInfo for Dummy { - fn pending_transactions(&self) -> Vec { self.0.clone() } - } - - #[test] - fn twice_empty() { - let db = Arc::new(::kvdb_memorydb::create(0)); - - { - let store = super::create(db.clone(), None, Dummy(vec![])); - assert_eq!(store.pending_transactions().unwrap(), vec![]) - } - - { - let store = super::create(db.clone(), None, Dummy(vec![])); - assert_eq!(store.pending_transactions().unwrap(), vec![]) - } - } - - #[test] - fn with_condition() { - let keypair = Brain::new("abcd".into()).generate().unwrap(); - let transactions: Vec<_> = (0..10u64).map(|nonce| { - let mut tx = Transaction::default(); - tx.nonce = nonce.into(); - - let signed = tx.sign(keypair.secret(), None); - let condition = match nonce { - 5 => Some(Condition::Number(100_000)), - _ => None, - }; - - PendingTransaction::new(signed, condition) - }).collect(); - - let db = Arc::new(::kvdb_memorydb::create(0)); - - { - // nothing written yet, will write pending. - let store = super::create(db.clone(), None, Dummy(transactions.clone())); - assert_eq!(store.pending_transactions().unwrap(), vec![]) - } - { - // pending written, will write nothing. - let store = super::create(db.clone(), None, Dummy(vec![])); - assert_eq!(store.pending_transactions().unwrap(), transactions) - } - { - // pending removed, will write nothing. - let store = super::create(db.clone(), None, Dummy(vec![])); - assert_eq!(store.pending_transactions().unwrap(), vec![]) - } - } - - #[test] - fn skips_bad_transactions() { - let keypair = Brain::new("abcd".into()).generate().unwrap(); - let mut transactions: Vec<_> = (0..10u64).map(|nonce| { - let mut tx = Transaction::default(); - tx.nonce = nonce.into(); - - let signed = tx.sign(keypair.secret(), None); - - PendingTransaction::new(signed, None) - }).collect(); - - transactions.push({ - let mut tx = Transaction::default(); - tx.nonce = 10.into(); - - let signed = tx.fake_sign(Default::default()); - PendingTransaction::new(signed, None) - }); - - let db = Arc::new(::kvdb_memorydb::create(0)); - { - // nothing written, will write bad. - let store = super::create(db.clone(), None, Dummy(transactions.clone())); - assert_eq!(store.pending_transactions().unwrap(), vec![]) - } - { - // try to load transactions. The last transaction, which is invalid, will be skipped. - let store = super::create(db.clone(), None, Dummy(vec![])); - let loaded = store.pending_transactions().unwrap(); - transactions.pop(); - assert_eq!(loaded, transactions); - } - } + use super::NodeInfo; + + use ethkey::{Brain, Generator}; + use std::sync::Arc; + use types::transaction::{Condition, PendingTransaction, Transaction}; + + // we want to test: round-trip of good transactions. + // failure to roundtrip bad transactions (but that it doesn't panic) + + struct Dummy(Vec); + impl NodeInfo for Dummy { + fn pending_transactions(&self) -> Vec { + self.0.clone() + } + } + + #[test] + fn twice_empty() { + let db = Arc::new(::kvdb_memorydb::create(0)); + + { + let store = super::create(db.clone(), None, Dummy(vec![])); + assert_eq!(store.pending_transactions().unwrap(), vec![]) + } + + { + let store = super::create(db.clone(), None, Dummy(vec![])); + assert_eq!(store.pending_transactions().unwrap(), vec![]) + } + } + + #[test] + fn with_condition() { + let keypair = Brain::new("abcd".into()).generate().unwrap(); + let transactions: Vec<_> = (0..10u64) + .map(|nonce| { + let mut tx = Transaction::default(); + tx.nonce = nonce.into(); + + let signed = tx.sign(keypair.secret(), None); + let condition = match nonce { + 5 => Some(Condition::Number(100_000)), + _ => None, + }; + + PendingTransaction::new(signed, condition) + }) + .collect(); + + let db = Arc::new(::kvdb_memorydb::create(0)); + + { + // nothing written yet, will write pending. + let store = super::create(db.clone(), None, Dummy(transactions.clone())); + assert_eq!(store.pending_transactions().unwrap(), vec![]) + } + { + // pending written, will write nothing. + let store = super::create(db.clone(), None, Dummy(vec![])); + assert_eq!(store.pending_transactions().unwrap(), transactions) + } + { + // pending removed, will write nothing. + let store = super::create(db.clone(), None, Dummy(vec![])); + assert_eq!(store.pending_transactions().unwrap(), vec![]) + } + } + + #[test] + fn skips_bad_transactions() { + let keypair = Brain::new("abcd".into()).generate().unwrap(); + let mut transactions: Vec<_> = (0..10u64) + .map(|nonce| { + let mut tx = Transaction::default(); + tx.nonce = nonce.into(); + + let signed = tx.sign(keypair.secret(), None); + + PendingTransaction::new(signed, None) + }) + .collect(); + + transactions.push({ + let mut tx = Transaction::default(); + tx.nonce = 10.into(); + + let signed = tx.fake_sign(Default::default()); + PendingTransaction::new(signed, None) + }); + + let db = Arc::new(::kvdb_memorydb::create(0)); + { + // nothing written, will write bad. + let store = super::create(db.clone(), None, Dummy(transactions.clone())); + assert_eq!(store.pending_transactions().unwrap(), vec![]) + } + { + // try to load transactions. The last transaction, which is invalid, will be skipped. + let store = super::create(db.clone(), None, Dummy(vec![])); + let loaded = store.pending_transactions().unwrap(); + transactions.pop(); + assert_eq!(loaded, transactions); + } + } } diff --git a/miner/price-info/Cargo.toml b/miner/price-info/Cargo.toml index 0b72fd37b37..0426b01b4b9 100644 --- a/miner/price-info/Cargo.toml +++ b/miner/price-info/Cargo.toml @@ -1,6 +1,6 @@ [package] description = "Fetch current ETH price" -homepage = "http://parity.io" +homepage = "https://github.com/openethereum/openethereum" license = "GPL-3.0" name = "price-info" version = "1.12.0" diff --git a/miner/price-info/src/lib.rs b/miner/price-info/src/lib.rs index 4117f84c525..51ad6a0bc45 100644 --- a/miner/price-info/src/lib.rs +++ b/miner/price-info/src/lib.rs @@ -1,26 +1,26 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . #![warn(missing_docs)] //! A simple client to get the current ETH price using an external API. extern crate futures; -extern crate serde_json; extern crate parity_runtime; +extern crate serde_json; #[macro_use] extern crate log; @@ -30,131 +30,151 @@ extern crate fake_fetch; pub extern crate fetch; -use std::cmp; -use std::fmt; -use std::io; -use std::str; +use std::{cmp, fmt, io, str}; use fetch::{Client as FetchClient, Fetch}; -use futures::{Future, Stream}; -use futures::future::{self, Either}; -use serde_json::Value; +use futures::{ + future::{self, Either}, + Future, Stream, +}; use parity_runtime::Executor; +use serde_json::Value; /// Current ETH price information. #[derive(Debug)] pub struct PriceInfo { - /// Current ETH price in USD. - pub ethusd: f32, + /// Current ETH price in USD. + pub ethusd: f32, } /// Price info error. #[derive(Debug)] pub enum Error { - /// The API returned an unexpected status code. - StatusCode(&'static str), - /// The API returned an unexpected status content. - UnexpectedResponse(Option), - /// There was an error when trying to reach the API. - Fetch(fetch::Error), - /// IO error when reading API response. - Io(io::Error), + /// The API returned an unexpected status code. + StatusCode(&'static str), + /// The API returned an unexpected status content. + UnexpectedResponse(Option), + /// There was an error when trying to reach the API. + Fetch(fetch::Error), + /// IO error when reading API response. + Io(io::Error), } impl From for Error { - fn from(err: io::Error) -> Self { Error::Io(err) } + fn from(err: io::Error) -> Self { + Error::Io(err) + } } impl From for Error { - fn from(err: fetch::Error) -> Self { Error::Fetch(err) } + fn from(err: fetch::Error) -> Self { + Error::Fetch(err) + } } /// A client to get the current ETH price using an external API. pub struct Client { - pool: Executor, - api_endpoint: String, - fetch: F, + pool: Executor, + api_endpoint: String, + fetch: F, } impl fmt::Debug for Client { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("price_info::Client") - .field("api_endpoint", &self.api_endpoint) - .finish() - } + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("price_info::Client") + .field("api_endpoint", &self.api_endpoint) + .finish() + } } impl cmp::PartialEq for Client { - fn eq(&self, other: &Client) -> bool { - self.api_endpoint == other.api_endpoint - } + fn eq(&self, other: &Client) -> bool { + self.api_endpoint == other.api_endpoint + } } impl Client { - /// Creates a new instance of the `Client` given a `fetch::Client`. - pub fn new(fetch: F, pool: Executor) -> Client { - let api_endpoint = "https://api.etherscan.io/api?module=stats&action=ethprice".to_owned(); - Client { pool, api_endpoint, fetch } - } - - /// Gets the current ETH price and calls `set_price` with the result. - pub fn get(&self, set_price: G) { - let future = self.fetch.get(&self.api_endpoint, fetch::Abort::default()) - .from_err() - .and_then(|response| { - if !response.is_success() { - let s = Error::StatusCode(response.status().canonical_reason().unwrap_or("unknown")); - return Either::A(future::err(s)); - } - Either::B(response.concat2().from_err()) - }) - .and_then(move |body| { - let body_str = str::from_utf8(&body).ok(); - let value: Option = body_str.and_then(|s| serde_json::from_str(s).ok()); - - let ethusd = value - .as_ref() - .and_then(|value| value.pointer("/result/ethusd")) - .and_then(|obj| obj.as_str()) - .and_then(|s| s.parse().ok()); - - match ethusd { - Some(ethusd) => { - set_price(PriceInfo { ethusd }); - Ok(()) - }, - None => Err(Error::UnexpectedResponse(body_str.map(From::from))), - } - }) - .map_err(|err| { - warn!("Failed to auto-update latest ETH price: {:?}", err); - }); - self.pool.spawn(future) - } + /// Creates a new instance of the `Client` given a `fetch::Client`. + pub fn new(fetch: F, pool: Executor, api_endpoint: String) -> Client { + Client { + pool, + api_endpoint, + fetch, + } + } + + /// Gets the current ETH price and calls `set_price` with the result. + pub fn get(&self, set_price: G) { + let future = self + .fetch + .get(&self.api_endpoint, fetch::Abort::default()) + .from_err() + .and_then(|response| { + if !response.is_success() { + let s = Error::StatusCode( + response.status().canonical_reason().unwrap_or("unknown"), + ); + return Either::A(future::err(s)); + } + Either::B(response.concat2().from_err()) + }) + .and_then(move |body| { + let body_str = str::from_utf8(&body).ok(); + let value: Option = body_str.and_then(|s| serde_json::from_str(s).ok()); + + let ethusd = value + .as_ref() + .and_then(|value| value.pointer("/result/ethusd")) + .and_then(|obj| obj.as_str()) + .and_then(|s| s.parse().ok()); + + match ethusd { + Some(ethusd) => { + set_price(PriceInfo { ethusd }); + Ok(()) + } + None => Err(Error::UnexpectedResponse(body_str.map(From::from))), + } + }) + .map_err(|err| { + warn!("Failed to auto-update latest ETH price: {:?}", err); + }); + self.pool.spawn(future) + } } #[cfg(test)] mod test { - use std::sync::Arc; - use parity_runtime::{Runtime, Executor}; - use Client; - use std::sync::atomic::{AtomicBool, Ordering}; - use fake_fetch::FakeFetch; - - fn price_info_ok(response: &str, executor: Executor) -> Client> { - Client::new(FakeFetch::new(Some(response.to_owned())), executor) - } - - fn price_info_not_found(executor: Executor) -> Client> { - Client::new(FakeFetch::new(None::), executor) - } - - #[test] - fn should_get_price_info() { - let runtime = Runtime::with_thread_count(1); - - // given - let response = r#"{ + use fake_fetch::FakeFetch; + use parity_runtime::{Executor, Runtime}; + use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }; + use Client; + + fn price_info_ok(response: &str, executor: Executor) -> Client> { + Client::new( + FakeFetch::new(Some(response.to_owned())), + executor, + "fake_endpoint".to_owned(), + ) + } + + fn price_info_not_found(executor: Executor) -> Client> { + Client::new( + FakeFetch::new(None::), + executor, + "fake_endpoint".to_owned(), + ) + } + + #[test] + fn should_get_price_info() { + let runtime = Runtime::with_thread_count(1); + + // given + let response = r#"{ "status": "1", "message": "OK", "result": { @@ -165,51 +185,50 @@ mod test { } }"#; - let price_info = price_info_ok(response, runtime.executor()); - - // when - price_info.get(|price| { + let price_info = price_info_ok(response, runtime.executor()); - // then - assert_eq!(price.ethusd, 209.55); - }); - } + // when + price_info.get(|price| { + // then + assert_eq!(price.ethusd, 209.55); + }); + } - #[test] - fn should_not_call_set_price_if_response_is_malformed() { - let runtime = Runtime::with_thread_count(1); + #[test] + fn should_not_call_set_price_if_response_is_malformed() { + let runtime = Runtime::with_thread_count(1); - // given - let response = "{}"; + // given + let response = "{}"; - let price_info = price_info_ok(response, runtime.executor()); - let b = Arc::new(AtomicBool::new(false)); + let price_info = price_info_ok(response, runtime.executor()); + let b = Arc::new(AtomicBool::new(false)); - // when - let bb = b.clone(); - price_info.get(move |_| { - bb.store(true, Ordering::Relaxed); - }); + // when + let bb = b.clone(); + price_info.get(move |_| { + bb.store(true, Ordering::Relaxed); + }); - // then - assert_eq!(b.load(Ordering::Relaxed), false); - } + // then + assert_eq!(b.load(Ordering::Relaxed), false); + } - #[test] - fn should_not_call_set_price_if_response_is_invalid() { - let runtime = Runtime::with_thread_count(1); + #[test] + fn should_not_call_set_price_if_response_is_invalid() { + let runtime = Runtime::with_thread_count(1); - // given - let price_info = price_info_not_found(runtime.executor()); - let b = Arc::new(AtomicBool::new(false)); + // given + let price_info = price_info_not_found(runtime.executor()); + let b = Arc::new(AtomicBool::new(false)); - // when - let bb = b.clone(); - price_info.get(move |_| { - bb.store(true, Ordering::Relaxed); - }); + // when + let bb = b.clone(); + price_info.get(move |_| { + bb.store(true, Ordering::Relaxed); + }); - // then - assert_eq!(b.load(Ordering::Relaxed), false); - } + // then + assert_eq!(b.load(Ordering::Relaxed), false); + } } diff --git a/miner/src/external.rs b/miner/src/external.rs index f68e065abd0..403f6949d87 100644 --- a/miner/src/external.rs +++ b/miner/src/external.rs @@ -1,112 +1,120 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! External Miner hashrate tracker. -use std::collections::HashMap; -use std::sync::Arc; -use std::time::{Instant, Duration}; use ethereum_types::{H256, U256}; use parking_lot::Mutex; +use std::{ + collections::HashMap, + sync::Arc, + time::{Duration, Instant}, +}; /// External miner interface. pub trait ExternalMinerService: Send + Sync { - /// Submit hashrate for given miner. - fn submit_hashrate(&self, hashrate: U256, id: H256); + /// Submit hashrate for given miner. + fn submit_hashrate(&self, hashrate: U256, id: H256); - /// Total hashrate. - fn hashrate(&self) -> U256; + /// Total hashrate. + fn hashrate(&self) -> U256; } /// External Miner. pub struct ExternalMiner { - hashrates: Arc>>, + hashrates: Arc>>, } impl Default for ExternalMiner { - fn default() -> Self { - ExternalMiner { - hashrates: Arc::new(Mutex::new(HashMap::new())), - } - } + fn default() -> Self { + ExternalMiner { + hashrates: Arc::new(Mutex::new(HashMap::new())), + } + } } impl ExternalMiner { - /// Creates new external miner with prefilled hashrates. - pub fn new(hashrates: Arc>>) -> Self { - ExternalMiner { - hashrates: hashrates, - } - } + /// Creates new external miner with prefilled hashrates. + pub fn new(hashrates: Arc>>) -> Self { + ExternalMiner { + hashrates: hashrates, + } + } } const ENTRY_TIMEOUT: Duration = Duration::from_secs(2); impl ExternalMinerService for ExternalMiner { - fn submit_hashrate(&self, hashrate: U256, id: H256) { - self.hashrates.lock().insert(id, (Instant::now() + ENTRY_TIMEOUT, hashrate)); - } - - fn hashrate(&self) -> U256 { - let mut hashrates = self.hashrates.lock(); - let h = hashrates.drain().filter(|&(_, (t, _))| t > Instant::now()).collect(); - *hashrates = h; - hashrates.iter().fold(U256::from(0), |sum, (_, &(_, v))| sum + v) - } + fn submit_hashrate(&self, hashrate: U256, id: H256) { + self.hashrates + .lock() + .insert(id, (Instant::now() + ENTRY_TIMEOUT, hashrate)); + } + + fn hashrate(&self) -> U256 { + let mut hashrates = self.hashrates.lock(); + let h = hashrates + .drain() + .filter(|&(_, (t, _))| t > Instant::now()) + .collect(); + *hashrates = h; + hashrates + .iter() + .fold(U256::from(0), |sum, (_, &(_, v))| sum + v) + } } #[cfg(test)] mod tests { - use super::*; - use std::thread::sleep; - use std::time::Duration; - use ethereum_types::{H256, U256}; - - fn miner() -> ExternalMiner { - ExternalMiner::default() - } - - #[test] - fn it_should_forget_old_hashrates() { - // given - let m = miner(); - assert_eq!(m.hashrate(), U256::from(0)); - m.submit_hashrate(U256::from(10), H256::from(1)); - assert_eq!(m.hashrate(), U256::from(10)); - - // when - sleep(Duration::from_secs(3)); - - // then - assert_eq!(m.hashrate(), U256::from(0)); - } - - #[test] - fn should_sum_up_hashrate() { - // given - let m = miner(); - assert_eq!(m.hashrate(), U256::from(0)); - m.submit_hashrate(U256::from(10), H256::from(1)); - assert_eq!(m.hashrate(), U256::from(10)); - - // when - m.submit_hashrate(U256::from(15), H256::from(1)); - m.submit_hashrate(U256::from(20), H256::from(2)); - - // then - assert_eq!(m.hashrate(), U256::from(35)); - } + use super::*; + use ethereum_types::{H256, U256}; + use std::{thread::sleep, time::Duration}; + + fn miner() -> ExternalMiner { + ExternalMiner::default() + } + + #[test] + fn it_should_forget_old_hashrates() { + // given + let m = miner(); + assert_eq!(m.hashrate(), U256::from(0)); + m.submit_hashrate(U256::from(10), H256::from(1)); + assert_eq!(m.hashrate(), U256::from(10)); + + // when + sleep(Duration::from_secs(3)); + + // then + assert_eq!(m.hashrate(), U256::from(0)); + } + + #[test] + fn should_sum_up_hashrate() { + // given + let m = miner(); + assert_eq!(m.hashrate(), U256::from(0)); + m.submit_hashrate(U256::from(10), H256::from(1)); + assert_eq!(m.hashrate(), U256::from(10)); + + // when + m.submit_hashrate(U256::from(15), H256::from(1)); + m.submit_hashrate(U256::from(20), H256::from(2)); + + // then + assert_eq!(m.hashrate(), U256::from(35)); + } } diff --git a/miner/src/gas_price_calibrator.rs b/miner/src/gas_price_calibrator.rs index 7a0943640cf..297b49cb718 100644 --- a/miner/src/gas_price_calibrator.rs +++ b/miner/src/gas_price_calibrator.rs @@ -1,63 +1,67 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Auto-updates minimal gas price requirement from a price-info source. -use std::time::{Instant, Duration}; +use std::time::{Duration, Instant}; use ansi_term::Colour; use ethereum_types::U256; use parity_runtime::Executor; -use price_info::{Client as PriceInfoClient, PriceInfo}; -use price_info::fetch::Client as FetchClient; +use price_info::{fetch::Client as FetchClient, Client as PriceInfoClient, PriceInfo}; /// Options for the dynamic gas price recalibrator. #[derive(Debug, PartialEq)] pub struct GasPriceCalibratorOptions { - /// Base transaction price to match against. - pub usd_per_tx: f32, - /// How frequently we should recalibrate. - pub recalibration_period: Duration, + /// Base transaction price to match against. + pub usd_per_tx: f32, + /// How frequently we should recalibrate. + pub recalibration_period: Duration, } /// The gas price validator variant for a `GasPricer`. #[derive(Debug, PartialEq)] pub struct GasPriceCalibrator { - options: GasPriceCalibratorOptions, - next_calibration: Instant, - price_info: PriceInfoClient, + options: GasPriceCalibratorOptions, + next_calibration: Instant, + price_info: PriceInfoClient, } impl GasPriceCalibrator { - /// Create a new gas price calibrator. - pub fn new(options: GasPriceCalibratorOptions, fetch: FetchClient, p: Executor) -> GasPriceCalibrator { - GasPriceCalibrator { - options: options, - next_calibration: Instant::now(), - price_info: PriceInfoClient::new(fetch, p), - } - } + /// Create a new gas price calibrator. + pub fn new( + options: GasPriceCalibratorOptions, + fetch: FetchClient, + p: Executor, + api_endpoint: String, + ) -> GasPriceCalibrator { + GasPriceCalibrator { + options: options, + next_calibration: Instant::now(), + price_info: PriceInfoClient::new(fetch, p, api_endpoint), + } + } - pub(crate) fn recalibrate(&mut self, set_price: F) { - trace!(target: "miner", "Recalibrating {:?} versus {:?}", Instant::now(), self.next_calibration); - if Instant::now() >= self.next_calibration { - let usd_per_tx = self.options.usd_per_tx; - trace!(target: "miner", "Getting price info"); + pub(crate) fn recalibrate(&mut self, set_price: F) { + trace!(target: "miner", "Recalibrating {:?} versus {:?}", Instant::now(), self.next_calibration); + if Instant::now() >= self.next_calibration { + let usd_per_tx = self.options.usd_per_tx; + trace!(target: "miner", "Getting price info"); - self.price_info.get(move |price: PriceInfo| { + self.price_info.get(move |price: PriceInfo| { trace!(target: "miner", "Price info arrived: {:?}", price); let usd_per_eth = price.ethusd; let wei_per_usd: f32 = 1.0e18 / usd_per_eth; @@ -67,7 +71,7 @@ impl GasPriceCalibrator { set_price(U256::from(wei_per_gas as u64)); }); - self.next_calibration = Instant::now() + self.options.recalibration_period; - } - } + self.next_calibration = Instant::now() + self.options.recalibration_period; + } + } } diff --git a/miner/src/gas_pricer.rs b/miner/src/gas_pricer.rs index c4e04442f19..6e60c447bf9 100644 --- a/miner/src/gas_pricer.rs +++ b/miner/src/gas_pricer.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Auto-updates minimal gas price requirement. @@ -23,31 +23,31 @@ use gas_price_calibrator::GasPriceCalibrator; /// Struct to look after updating the acceptable gas price of a miner. #[derive(Debug, PartialEq)] pub enum GasPricer { - /// A fixed gas price in terms of Wei - always the argument given. - Fixed(U256), - /// Gas price is calibrated according to a fixed amount of USD. - #[cfg(feature = "price-info")] - Calibrated(GasPriceCalibrator), + /// A fixed gas price in terms of Wei - always the argument given. + Fixed(U256), + /// Gas price is calibrated according to a fixed amount of USD. + #[cfg(feature = "price-info")] + Calibrated(GasPriceCalibrator), } impl GasPricer { - /// Create a new Calibrated `GasPricer`. - #[cfg(feature = "price-info")] - pub fn new_calibrated(calibrator: GasPriceCalibrator) -> GasPricer { - GasPricer::Calibrated(calibrator) - } - - /// Create a new Fixed `GasPricer`. - pub fn new_fixed(gas_price: U256) -> GasPricer { - GasPricer::Fixed(gas_price) - } - - /// Recalibrate current gas price. - pub fn recalibrate(&mut self, set_price: F) { - match *self { - GasPricer::Fixed(ref curr) => set_price(curr.clone()), - #[cfg(feature = "price-info")] - GasPricer::Calibrated(ref mut cal) => cal.recalibrate(set_price), - } - } + /// Create a new Calibrated `GasPricer`. + #[cfg(feature = "price-info")] + pub fn new_calibrated(calibrator: GasPriceCalibrator) -> GasPricer { + GasPricer::Calibrated(calibrator) + } + + /// Create a new Fixed `GasPricer`. + pub fn new_fixed(gas_price: U256) -> GasPricer { + GasPricer::Fixed(gas_price) + } + + /// Recalibrate current gas price. + pub fn recalibrate(&mut self, set_price: F) { + match *self { + GasPricer::Fixed(ref curr) => set_price(curr.clone()), + #[cfg(feature = "price-info")] + GasPricer::Calibrated(ref mut cal) => cal.recalibrate(set_price), + } + } } diff --git a/miner/src/lib.rs b/miner/src/lib.rs index 55091093aca..89bd18c4f3d 100644 --- a/miner/src/lib.rs +++ b/miner/src/lib.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . #![warn(missing_docs)] @@ -47,11 +47,11 @@ extern crate log; extern crate trace_time; #[cfg(test)] -extern crate rustc_hex; +extern crate env_logger; #[cfg(test)] extern crate ethkey; #[cfg(test)] -extern crate env_logger; +extern crate rustc_hex; pub mod external; #[cfg(feature = "price-info")] diff --git a/miner/src/local_accounts.rs b/miner/src/local_accounts.rs index 23bcf814425..b562459c7a0 100644 --- a/miner/src/local_accounts.rs +++ b/miner/src/local_accounts.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Local Accounts checker @@ -22,22 +22,22 @@ use ethereum_types::Address; /// Local accounts checker pub trait LocalAccounts: Send + Sync { - /// Returns true if given address should be considered local account. - fn is_local(&self, &Address) -> bool; + /// Returns true if given address should be considered local account. + fn is_local(&self, &Address) -> bool; } impl LocalAccounts for HashSet
{ - fn is_local(&self, address: &Address) -> bool { - self.contains(address) - } + fn is_local(&self, address: &Address) -> bool { + self.contains(address) + } } -impl LocalAccounts for (A, B) where - A: LocalAccounts, - B: LocalAccounts, +impl LocalAccounts for (A, B) +where + A: LocalAccounts, + B: LocalAccounts, { - fn is_local(&self, address: &Address) -> bool { - self.0.is_local(address) || self.1.is_local(address) - } + fn is_local(&self, address: &Address) -> bool { + self.0.is_local(address) || self.1.is_local(address) + } } - diff --git a/miner/src/pool/client.rs b/miner/src/pool/client.rs index bcd2b968954..b5623460439 100644 --- a/miner/src/pool/client.rs +++ b/miner/src/pool/client.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Transaction Pool state client. //! @@ -22,54 +22,69 @@ use std::fmt; -use ethereum_types::{U256, H256, H160 as Address}; +use ethereum_types::{H160 as Address, H256, U256}; use types::transaction; /// Account Details #[derive(Debug, Clone)] pub struct AccountDetails { - /// Current account nonce - pub nonce: U256, - /// Current account balance - pub balance: U256, - /// Is this account a local account? - pub is_local: bool, + /// Current account nonce + pub nonce: U256, + /// Current account balance + pub balance: U256, + /// Is this account a local account? + pub is_local: bool, } /// Transaction type #[derive(Debug, PartialEq)] pub enum TransactionType { - /// Regular transaction - Regular, - /// Service transaction (allowed by a contract to have gas_price=0) - Service, + /// Regular transaction + Regular, + /// Service transaction (allowed by a contract to have gas_price=0) + Service, } /// Verification client. pub trait Client: fmt::Debug + Sync { - /// Is transaction with given hash already in the blockchain? - fn transaction_already_included(&self, hash: &H256) -> bool; + /// Is transaction with given hash already in the blockchain? + fn transaction_already_included(&self, hash: &H256) -> bool; - /// Structurarily verify given transaction. - fn verify_transaction(&self, tx: transaction::UnverifiedTransaction) - -> Result; + /// Perform basic/cheap transaction verification. + /// + /// This should include all cheap checks that can be done before + /// actually checking the signature, like chain-replay protection. + /// + /// This method is currently used only for verifying local transactions. + fn verify_transaction_basic( + &self, + t: &transaction::UnverifiedTransaction, + ) -> Result<(), transaction::Error>; - /// Estimate minimal gas requirurement for given transaction. - fn required_gas(&self, tx: &transaction::Transaction) -> U256; + /// Structurarily verify given transaction. + fn verify_transaction( + &self, + tx: transaction::UnverifiedTransaction, + ) -> Result; - /// Fetch account details for given sender. - fn account_details(&self, address: &Address) -> AccountDetails; + /// Estimate minimal gas requirurement for given transaction. + fn required_gas(&self, tx: &transaction::Transaction) -> U256; - /// Classify transaction (check if transaction is filtered by some contracts). - fn transaction_type(&self, tx: &transaction::SignedTransaction) -> TransactionType; + /// Fetch account details for given sender. + fn account_details(&self, address: &Address) -> AccountDetails; - /// Performs pre-validation of RLP decoded transaction - fn decode_transaction(&self, transaction: &[u8]) - -> Result; + /// Classify transaction (check if transaction is filtered by some contracts). + fn transaction_type(&self, tx: &transaction::SignedTransaction) -> TransactionType; + + /// Performs pre-validation of RLP decoded transaction + fn decode_transaction( + &self, + transaction: &[u8], + ) -> Result; } /// State nonce client pub trait NonceClient: fmt::Debug + Sync { - /// Fetch only account nonce for given sender. - fn account_nonce(&self, address: &Address) -> U256; + /// Fetch only account nonce for given sender. + fn account_nonce(&self, address: &Address) -> U256; } diff --git a/miner/src/pool/listener.rs b/miner/src/pool/listener.rs index 67034aa5239..35f94d60078 100644 --- a/miner/src/pool/listener.rs +++ b/miner/src/pool/listener.rs @@ -1,71 +1,70 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Notifier for new transaction hashes. -use std::fmt; -use std::sync::Arc; +use std::{fmt, sync::Arc}; use ethereum_types::H256; use txpool::{self, VerifiedTransaction}; use pool::VerifiedTransaction as Transaction; -type Listener = Box; +type Listener = Box; /// Manages notifications to pending transaction listeners. #[derive(Default)] pub struct Notifier { - listeners: Vec, - pending: Vec, + listeners: Vec, + pending: Vec, } impl fmt::Debug for Notifier { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("Notifier") - .field("listeners", &self.listeners.len()) - .field("pending", &self.pending) - .finish() - } + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("Notifier") + .field("listeners", &self.listeners.len()) + .field("pending", &self.pending) + .finish() + } } impl Notifier { - /// Add new listener to receive notifications. - pub fn add(&mut self, f: Listener) { - self.listeners.push(f) - } - - /// Notify listeners about all currently pending transactions. - pub fn notify(&mut self) { - if self.pending.is_empty() { - return; - } - - for l in &self.listeners { - (l)(&self.pending); - } - - self.pending.clear(); - } + /// Add new listener to receive notifications. + pub fn add(&mut self, f: Listener) { + self.listeners.push(f) + } + + /// Notify listeners about all currently pending transactions. + pub fn notify(&mut self) { + if self.pending.is_empty() { + return; + } + + for l in &self.listeners { + (l)(&self.pending); + } + + self.pending.clear(); + } } impl txpool::Listener for Notifier { - fn added(&mut self, tx: &Arc, _old: Option<&Arc>) { - self.pending.push(*tx.hash()); - } + fn added(&mut self, tx: &Arc, _old: Option<&Arc>) { + self.pending.push(*tx.hash()); + } } /// Transaction pool logger. @@ -73,91 +72,102 @@ impl txpool::Listener for Notifier { pub struct Logger; impl txpool::Listener for Logger { - fn added(&mut self, tx: &Arc, old: Option<&Arc>) { - debug!(target: "txqueue", "[{:?}] Added to the pool.", tx.hash()); - debug!( - target: "txqueue", - "[{hash:?}] Sender: {sender}, nonce: {nonce}, gasPrice: {gas_price}, gas: {gas}, value: {value}, dataLen: {data}))", - hash = tx.hash(), - sender = tx.sender(), - nonce = tx.signed().nonce, - gas_price = tx.signed().gas_price, - gas = tx.signed().gas, - value = tx.signed().value, - data = tx.signed().data.len(), - ); - - if let Some(old) = old { - debug!(target: "txqueue", "[{:?}] Dropped. Replaced by [{:?}]", old.hash(), tx.hash()); - } - } - - fn rejected(&mut self, _tx: &Arc, reason: &txpool::Error) { - trace!(target: "txqueue", "Rejected {}.", reason); - } - - fn dropped(&mut self, tx: &Arc, new: Option<&Transaction>) { - match new { - Some(new) => debug!(target: "txqueue", "[{:?}] Pushed out by [{:?}]", tx.hash(), new.hash()), - None => debug!(target: "txqueue", "[{:?}] Dropped.", tx.hash()), - } - } - - fn invalid(&mut self, tx: &Arc) { - debug!(target: "txqueue", "[{:?}] Marked as invalid by executor.", tx.hash()); - } - - fn canceled(&mut self, tx: &Arc) { - debug!(target: "txqueue", "[{:?}] Canceled by the user.", tx.hash()); - } - - fn culled(&mut self, tx: &Arc) { - debug!(target: "txqueue", "[{:?}] Culled or mined.", tx.hash()); - } + fn added(&mut self, tx: &Arc, old: Option<&Arc>) { + debug!(target: "txqueue", "[{:?}] Added to the pool.", tx.hash()); + debug!( + target: "txqueue", + "[{hash:?}] Sender: {sender}, nonce: {nonce}, gasPrice: {gas_price}, gas: {gas}, value: {value}, dataLen: {data}))", + hash = tx.hash(), + sender = tx.sender(), + nonce = tx.signed().nonce, + gas_price = tx.signed().gas_price, + gas = tx.signed().gas, + value = tx.signed().value, + data = tx.signed().data.len(), + ); + + if let Some(old) = old { + debug!(target: "txqueue", "[{:?}] Dropped. Replaced by [{:?}]", old.hash(), tx.hash()); + } + } + + fn rejected( + &mut self, + _tx: &Arc, + reason: &txpool::Error, + ) { + trace!(target: "txqueue", "Rejected {}.", reason); + } + + fn dropped(&mut self, tx: &Arc, new: Option<&Transaction>) { + match new { + Some(new) => { + debug!(target: "txqueue", "[{:?}] Pushed out by [{:?}]", tx.hash(), new.hash()) + } + None => debug!(target: "txqueue", "[{:?}] Dropped.", tx.hash()), + } + } + + fn invalid(&mut self, tx: &Arc) { + debug!(target: "txqueue", "[{:?}] Marked as invalid by executor.", tx.hash()); + } + + fn canceled(&mut self, tx: &Arc) { + debug!(target: "txqueue", "[{:?}] Canceled by the user.", tx.hash()); + } + + fn culled(&mut self, tx: &Arc) { + debug!(target: "txqueue", "[{:?}] Culled or mined.", tx.hash()); + } } #[cfg(test)] mod tests { - use super::*; - use parking_lot::Mutex; - use types::transaction; - use txpool::Listener; - - #[test] - fn should_notify_listeners() { - // given - let received = Arc::new(Mutex::new(vec![])); - let r = received.clone(); - let listener = Box::new(move |hashes: &[H256]| { - *r.lock() = hashes.iter().map(|x| *x).collect(); - }); - - let mut tx_listener = Notifier::default(); - tx_listener.add(listener); - - // when - let tx = new_tx(); - tx_listener.added(&tx, None); - assert_eq!(*received.lock(), vec![]); - - // then - tx_listener.notify(); - assert_eq!( - *received.lock(), - vec!["13aff4201ac1dc49daf6a7cf07b558ed956511acbaabf9502bdacc353953766d".parse().unwrap()] - ); - } - - fn new_tx() -> Arc { - let signed = transaction::Transaction { - action: transaction::Action::Create, - data: vec![1, 2, 3], - nonce: 5.into(), - gas: 21_000.into(), - gas_price: 5.into(), - value: 0.into(), - }.fake_sign(5.into()); - - Arc::new(Transaction::from_pending_block_transaction(signed)) - } + use super::*; + use parking_lot::Mutex; + use txpool::Listener; + use types::transaction; + + #[test] + fn should_notify_listeners() { + // given + let received = Arc::new(Mutex::new(vec![])); + let r = received.clone(); + let listener = Box::new(move |hashes: &[H256]| { + *r.lock() = hashes.iter().map(|x| *x).collect(); + }); + + let mut tx_listener = Notifier::default(); + tx_listener.add(listener); + + // when + let tx = new_tx(); + tx_listener.added(&tx, None); + assert_eq!(*received.lock(), vec![]); + + // then + tx_listener.notify(); + assert_eq!( + *received.lock(), + vec![ + "13aff4201ac1dc49daf6a7cf07b558ed956511acbaabf9502bdacc353953766d" + .parse() + .unwrap() + ] + ); + } + + fn new_tx() -> Arc { + let signed = transaction::Transaction { + action: transaction::Action::Create, + data: vec![1, 2, 3], + nonce: 5.into(), + gas: 21_000.into(), + gas_price: 5.into(), + value: 0.into(), + } + .fake_sign(5.into()); + + Arc::new(Transaction::from_pending_block_transaction(signed)) + } } diff --git a/miner/src/pool/local_transactions.rs b/miner/src/pool/local_transactions.rs index 346877d0301..89f1476248e 100644 --- a/miner/src/pool/local_transactions.rs +++ b/miner/src/pool/local_transactions.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Local Transactions List. @@ -20,7 +20,7 @@ use std::{fmt, sync::Arc}; use ethereum_types::H256; use linked_hash_map::LinkedHashMap; -use pool::{VerifiedTransaction as Transaction, ScoredTransaction}; +use pool::{ScoredTransaction, VerifiedTransaction as Transaction}; use txpool::{self, VerifiedTransaction}; /// Status of local transaction. @@ -28,295 +28,331 @@ use txpool::{self, VerifiedTransaction}; /// or gives a reason why the transaction was removed. #[derive(Debug, PartialEq, Clone)] pub enum Status { - /// The transaction is currently in the transaction queue. - Pending(Arc), - /// Transaction is already mined. - Mined(Arc), - /// Transaction didn't get into any block, but some other tx with the same nonce got. - Culled(Arc), - /// Transaction is dropped because of limit - Dropped(Arc), - /// Replaced because of higher gas price of another transaction. - Replaced { - /// Replaced transaction - old: Arc, - /// Transaction that replaced this one. - new: Arc, - }, - /// Transaction was never accepted to the queue. - /// It means that it was too cheap to replace any transaction already in the pool. - Rejected(Arc, String), - /// Transaction is invalid. - Invalid(Arc), - /// Transaction was canceled. - Canceled(Arc), + /// The transaction is currently in the transaction queue. + Pending(Arc), + /// Transaction is already mined. + Mined(Arc), + /// Transaction didn't get into any block, but some other tx with the same nonce got. + Culled(Arc), + /// Transaction is dropped because of limit + Dropped(Arc), + /// Replaced because of higher gas price of another transaction. + Replaced { + /// Replaced transaction + old: Arc, + /// Transaction that replaced this one. + new: Arc, + }, + /// Transaction was never accepted to the queue. + /// It means that it was too cheap to replace any transaction already in the pool. + Rejected(Arc, String), + /// Transaction is invalid. + Invalid(Arc), + /// Transaction was canceled. + Canceled(Arc), } impl Status { - fn is_pending(&self) -> bool { - match *self { - Status::Pending(_) => true, - _ => false, - } - } + fn is_pending(&self) -> bool { + match *self { + Status::Pending(_) => true, + _ => false, + } + } } /// Keeps track of local transactions that are in the queue or were mined/dropped recently. pub struct LocalTransactionsList { - max_old: usize, - transactions: LinkedHashMap, - pending: usize, - in_chain: Option bool + Send + Sync>>, + max_old: usize, + transactions: LinkedHashMap, + pending: usize, + in_chain: Option bool + Send + Sync>>, } impl fmt::Debug for LocalTransactionsList { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("LocalTransactionsList") - .field("max_old", &self.max_old) - .field("transactions", &self.transactions) - .field("pending", &self.pending) - .field("in_chain", &self.in_chain.is_some()) - .finish() - } + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("LocalTransactionsList") + .field("max_old", &self.max_old) + .field("transactions", &self.transactions) + .field("pending", &self.pending) + .field("in_chain", &self.in_chain.is_some()) + .finish() + } } impl Default for LocalTransactionsList { - fn default() -> Self { - Self::new(10) - } + fn default() -> Self { + Self::new(10) + } } impl LocalTransactionsList { - /// Create a new list of local transactions. - pub fn new(max_old: usize) -> Self { - LocalTransactionsList { - max_old, - transactions: Default::default(), - pending: 0, - in_chain: None, - } - } - - /// Set blockchain checker. - /// - /// The function should return true if transaction is included in chain. - pub fn set_in_chain_checker(&mut self, checker: T) where - T: Into>, - F: Fn(&H256) -> bool + Send + Sync + 'static - { - self.in_chain = checker.into().map(|f| Box::new(f) as _); - } - - /// Returns true if the transaction is already in local transactions. - pub fn contains(&self, hash: &H256) -> bool { - self.transactions.contains_key(hash) - } - - /// Return a map of all currently stored transactions. - pub fn all_transactions(&self) -> &LinkedHashMap { - &self.transactions - } - - /// Returns true if there are pending local transactions. - pub fn has_pending(&self) -> bool { - self.pending > 0 - } - - fn clear_old(&mut self) { - let number_of_old = self.transactions.len() - self.pending; - if self.max_old >= number_of_old { - return; - } - - let to_remove: Vec<_> = self.transactions - .iter() - .filter(|&(_, status)| !status.is_pending()) - .map(|(hash, _)| *hash) - .take(number_of_old - self.max_old) - .collect(); - - for hash in to_remove { - self.transactions.remove(&hash); - } - } - - fn insert(&mut self, hash: H256, status: Status) { - let result = self.transactions.insert(hash, status); - if let Some(old) = result { - if old.is_pending() { - self.pending -= 1; - } - } - } + /// Create a new list of local transactions. + pub fn new(max_old: usize) -> Self { + LocalTransactionsList { + max_old, + transactions: Default::default(), + pending: 0, + in_chain: None, + } + } + + /// Set blockchain checker. + /// + /// The function should return true if transaction is included in chain. + pub fn set_in_chain_checker(&mut self, checker: T) + where + T: Into>, + F: Fn(&H256) -> bool + Send + Sync + 'static, + { + self.in_chain = checker.into().map(|f| Box::new(f) as _); + } + + /// Returns true if the transaction is already in local transactions. + pub fn contains(&self, hash: &H256) -> bool { + self.transactions.contains_key(hash) + } + + /// Return a map of all currently stored transactions. + pub fn all_transactions(&self) -> &LinkedHashMap { + &self.transactions + } + + /// Returns true if there are pending local transactions. + pub fn has_pending(&self) -> bool { + self.pending > 0 + } + + fn clear_old(&mut self) { + let number_of_old = self.transactions.len() - self.pending; + if self.max_old >= number_of_old { + return; + } + + let to_remove: Vec<_> = self + .transactions + .iter() + .filter(|&(_, status)| !status.is_pending()) + .map(|(hash, _)| *hash) + .take(number_of_old - self.max_old) + .collect(); + + for hash in to_remove { + self.transactions.remove(&hash); + } + } + + fn insert(&mut self, hash: H256, status: Status) { + let result = self.transactions.insert(hash, status); + if let Some(old) = result { + if old.is_pending() { + self.pending -= 1; + } + } + } } impl txpool::Listener for LocalTransactionsList { - fn added(&mut self, tx: &Arc, old: Option<&Arc>) { - if !tx.priority().is_local() { - return; - } - - debug!(target: "own_tx", "Imported to the pool (hash {:?})", tx.hash()); - self.clear_old(); - self.insert(*tx.hash(), Status::Pending(tx.clone())); - self.pending += 1; - - if let Some(old) = old { - if self.transactions.contains_key(old.hash()) { - self.insert(*old.hash(), Status::Replaced { - old: old.clone(), - new: tx.clone(), - }); - } - } - } - - fn rejected(&mut self, tx: &Arc, reason: &txpool::Error) { - if !tx.priority().is_local() { - return; - } - - debug!(target: "own_tx", "Transaction rejected (hash {:?}). {}", tx.hash(), reason); - self.insert(*tx.hash(), Status::Rejected(tx.clone(), format!("{}", reason))); - self.clear_old(); - } - - fn dropped(&mut self, tx: &Arc, new: Option<&Transaction>) { - if !tx.priority().is_local() { - return; - } - - match new { - Some(new) => warn!(target: "own_tx", "Transaction pushed out because of limit (hash {:?}, replacement: {:?})", tx.hash(), new.hash()), - None => warn!(target: "own_tx", "Transaction dropped because of limit (hash: {:?})", tx.hash()), - } - self.insert(*tx.hash(), Status::Dropped(tx.clone())); - self.clear_old(); - } - - fn invalid(&mut self, tx: &Arc) { - if !tx.priority().is_local() { - return; - } - - warn!(target: "own_tx", "Transaction marked invalid (hash {:?})", tx.hash()); - self.insert(*tx.hash(), Status::Invalid(tx.clone())); - self.clear_old(); - } - - fn canceled(&mut self, tx: &Arc) { - if !tx.priority().is_local() { - return; - } - - warn!(target: "own_tx", "Transaction canceled (hash {:?})", tx.hash()); - self.insert(*tx.hash(), Status::Canceled(tx.clone())); - self.clear_old(); - } - - fn culled(&mut self, tx: &Arc) { - if !tx.priority().is_local() { - return; - } - - let is_in_chain = self.in_chain.as_ref().map(|checker| checker(tx.hash())).unwrap_or(false); - if is_in_chain { - info!(target: "own_tx", "Transaction mined (hash {:?})", tx.hash()); - self.insert(*tx.hash(), Status::Mined(tx.clone())); - return; - } - - info!(target: "own_tx", "Transaction culled (hash {:?})", tx.hash()); - self.insert(*tx.hash(), Status::Culled(tx.clone())); - } + fn added(&mut self, tx: &Arc, old: Option<&Arc>) { + if !tx.priority().is_local() { + return; + } + + debug!(target: "own_tx", "Imported to the pool (hash {:?})", tx.hash()); + self.clear_old(); + self.insert(*tx.hash(), Status::Pending(tx.clone())); + self.pending += 1; + + if let Some(old) = old { + if self.transactions.contains_key(old.hash()) { + self.insert( + *old.hash(), + Status::Replaced { + old: old.clone(), + new: tx.clone(), + }, + ); + } + } + } + + fn rejected( + &mut self, + tx: &Arc, + reason: &txpool::Error, + ) { + if !tx.priority().is_local() { + return; + } + + debug!(target: "own_tx", "Transaction rejected (hash {:?}). {}", tx.hash(), reason); + self.insert( + *tx.hash(), + Status::Rejected(tx.clone(), format!("{}", reason)), + ); + self.clear_old(); + } + + fn dropped(&mut self, tx: &Arc, new: Option<&Transaction>) { + if !tx.priority().is_local() { + return; + } + + match new { + Some(new) => { + warn!(target: "own_tx", "Transaction pushed out because of limit (hash {:?}, replacement: {:?})", tx.hash(), new.hash()) + } + None => { + warn!(target: "own_tx", "Transaction dropped because of limit (hash: {:?})", tx.hash()) + } + } + self.insert(*tx.hash(), Status::Dropped(tx.clone())); + self.clear_old(); + } + + fn invalid(&mut self, tx: &Arc) { + if !tx.priority().is_local() { + return; + } + + warn!(target: "own_tx", "Transaction marked invalid (hash {:?})", tx.hash()); + self.insert(*tx.hash(), Status::Invalid(tx.clone())); + self.clear_old(); + } + + fn canceled(&mut self, tx: &Arc) { + if !tx.priority().is_local() { + return; + } + + warn!(target: "own_tx", "Transaction canceled (hash {:?})", tx.hash()); + self.insert(*tx.hash(), Status::Canceled(tx.clone())); + self.clear_old(); + } + + fn culled(&mut self, tx: &Arc) { + if !tx.priority().is_local() { + return; + } + + let is_in_chain = self + .in_chain + .as_ref() + .map(|checker| checker(tx.hash())) + .unwrap_or(false); + if is_in_chain { + info!(target: "own_tx", "Transaction mined (hash {:?})", tx.hash()); + self.insert(*tx.hash(), Status::Mined(tx.clone())); + return; + } + + info!(target: "own_tx", "Transaction culled (hash {:?})", tx.hash()); + self.insert(*tx.hash(), Status::Culled(tx.clone())); + } } #[cfg(test)] mod tests { - use super::*; - use ethereum_types::U256; - use ethkey::{Random, Generator}; - use types::transaction; - use txpool::Listener; - - use pool; - - #[test] - fn should_add_transaction_as_pending() { - // given - let mut list = LocalTransactionsList::default(); - let tx1 = new_tx(10); - let tx2 = new_tx(20); - - // when - list.added(&tx1, None); - list.added(&tx2, None); - - // then - assert!(list.contains(tx1.hash())); - assert!(list.contains(tx2.hash())); - let statuses = list.all_transactions().values().cloned().collect::>(); - assert_eq!(statuses, vec![Status::Pending(tx1), Status::Pending(tx2)]); - } - - #[test] - fn should_use_in_chain_checker_if_present() { - // given - let mut list = LocalTransactionsList::default(); - let tx1 = new_tx(10); - let tx2 = new_tx(20); - list.culled(&tx1); - list.culled(&tx2); - let statuses = list.all_transactions().values().cloned().collect::>(); - assert_eq!(statuses, vec![Status::Culled(tx1.clone()), Status::Culled(tx2.clone())]); - - // when - list.set_in_chain_checker(|_: &_| true); - list.culled(&tx1); - - // then - let statuses = list.all_transactions().values().cloned().collect::>(); - assert_eq!(statuses, vec![Status::Culled(tx2), Status::Mined(tx1)]); - } - - #[test] - fn should_clear_old_transactions() { - // given - let mut list = LocalTransactionsList::new(1); - let tx1 = new_tx(10); - let tx2 = new_tx(50); - let tx3 = new_tx(51); - - list.added(&tx1, None); - list.invalid(&tx1); - list.dropped(&tx2, None); - assert!(!list.contains(tx1.hash())); - assert!(list.contains(tx2.hash())); - assert!(!list.contains(tx3.hash())); - - // when - list.added(&tx3, Some(&tx1)); - - // then - assert!(!list.contains(tx1.hash())); - assert!(list.contains(tx2.hash())); - assert!(list.contains(tx3.hash())); - } - - fn new_tx>(nonce: T) -> Arc { - let keypair = Random.generate().unwrap(); - let signed = transaction::Transaction { - action: transaction::Action::Create, - value: U256::from(100), - data: Default::default(), - gas: U256::from(10), - gas_price: U256::from(1245), - nonce: nonce.into(), - }.sign(keypair.secret(), None); - - let mut tx = Transaction::from_pending_block_transaction(signed); - tx.priority = pool::Priority::Local; - - Arc::new(tx) - } + use super::*; + use ethereum_types::U256; + use ethkey::{Generator, Random}; + use txpool::Listener; + use types::transaction; + + use pool; + + #[test] + fn should_add_transaction_as_pending() { + // given + let mut list = LocalTransactionsList::default(); + let tx1 = new_tx(10); + let tx2 = new_tx(20); + + // when + list.added(&tx1, None); + list.added(&tx2, None); + + // then + assert!(list.contains(tx1.hash())); + assert!(list.contains(tx2.hash())); + let statuses = list + .all_transactions() + .values() + .cloned() + .collect::>(); + assert_eq!(statuses, vec![Status::Pending(tx1), Status::Pending(tx2)]); + } + + #[test] + fn should_use_in_chain_checker_if_present() { + // given + let mut list = LocalTransactionsList::default(); + let tx1 = new_tx(10); + let tx2 = new_tx(20); + list.culled(&tx1); + list.culled(&tx2); + let statuses = list + .all_transactions() + .values() + .cloned() + .collect::>(); + assert_eq!( + statuses, + vec![Status::Culled(tx1.clone()), Status::Culled(tx2.clone())] + ); + + // when + list.set_in_chain_checker(|_: &_| true); + list.culled(&tx1); + + // then + let statuses = list + .all_transactions() + .values() + .cloned() + .collect::>(); + assert_eq!(statuses, vec![Status::Culled(tx2), Status::Mined(tx1)]); + } + + #[test] + fn should_clear_old_transactions() { + // given + let mut list = LocalTransactionsList::new(1); + let tx1 = new_tx(10); + let tx2 = new_tx(50); + let tx3 = new_tx(51); + + list.added(&tx1, None); + list.invalid(&tx1); + list.dropped(&tx2, None); + assert!(!list.contains(tx1.hash())); + assert!(list.contains(tx2.hash())); + assert!(!list.contains(tx3.hash())); + + // when + list.added(&tx3, Some(&tx1)); + + // then + assert!(!list.contains(tx1.hash())); + assert!(list.contains(tx2.hash())); + assert!(list.contains(tx3.hash())); + } + + fn new_tx>(nonce: T) -> Arc { + let keypair = Random.generate().unwrap(); + let signed = transaction::Transaction { + action: transaction::Action::Create, + value: U256::from(100), + data: Default::default(), + gas: U256::from(10), + gas_price: U256::from(1245), + nonce: nonce.into(), + } + .sign(keypair.secret(), None); + + let mut tx = Transaction::from_pending_block_transaction(signed); + tx.priority = pool::Priority::Local; + + Arc::new(tx) + } } diff --git a/miner/src/pool/mod.rs b/miner/src/pool/mod.rs index 40a226d9fcd..306189d13ca 100644 --- a/miner/src/pool/mod.rs +++ b/miner/src/pool/mod.rs @@ -1,25 +1,25 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Transaction Pool -use ethereum_types::{U256, H256, Address}; +use ethereum_types::{Address, H256, U256}; use heapsize::HeapSizeOf; -use types::transaction; use txpool; +use types::transaction; mod listener; mod queue; @@ -34,168 +34,169 @@ pub mod verifier; #[cfg(test)] mod tests; -pub use self::queue::{TransactionQueue, Status as QueueStatus}; -pub use self::txpool::{VerifiedTransaction as PoolVerifiedTransaction, Options}; +pub use self::{ + queue::{Status as QueueStatus, TransactionQueue}, + txpool::{Options, VerifiedTransaction as PoolVerifiedTransaction}, +}; /// How to prioritize transactions in the pool /// /// TODO [ToDr] Implement more strategies. #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum PrioritizationStrategy { - /// Simple gas-price based prioritization. - GasPriceOnly, + /// Simple gas-price based prioritization. + GasPriceOnly, } /// Transaction ordering when requesting pending set. #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum PendingOrdering { - /// Get pending transactions ordered by their priority (potentially expensive) - Priority, - /// Get pending transactions without any care of particular ordering (cheaper). - Unordered, + /// Get pending transactions ordered by their priority (potentially expensive) + Priority, + /// Get pending transactions without any care of particular ordering (cheaper). + Unordered, } /// Pending set query settings #[derive(Debug, Clone)] pub struct PendingSettings { - /// Current block number (affects readiness of some transactions). - pub block_number: u64, - /// Current timestamp (affects readiness of some transactions). - pub current_timestamp: u64, - /// Nonce cap (for dust protection; EIP-168) - pub nonce_cap: Option, - /// Maximal number of transactions in pending the set. - pub max_len: usize, - /// Ordering of transactions. - pub ordering: PendingOrdering, + /// Current block number (affects readiness of some transactions). + pub block_number: u64, + /// Current timestamp (affects readiness of some transactions). + pub current_timestamp: u64, + /// Nonce cap (for dust protection; EIP-168) + pub nonce_cap: Option, + /// Maximal number of transactions in pending the set. + pub max_len: usize, + /// Ordering of transactions. + pub ordering: PendingOrdering, } impl PendingSettings { - /// Get all transactions (no cap or len limit) prioritized. - pub fn all_prioritized(block_number: u64, current_timestamp: u64) -> Self { - PendingSettings { - block_number, - current_timestamp, - nonce_cap: None, - max_len: usize::max_value(), - ordering: PendingOrdering::Priority, - } - } + /// Get all transactions (no cap or len limit) prioritized. + pub fn all_prioritized(block_number: u64, current_timestamp: u64) -> Self { + PendingSettings { + block_number, + current_timestamp, + nonce_cap: None, + max_len: usize::max_value(), + ordering: PendingOrdering::Priority, + } + } } /// Transaction priority. -#[derive(Debug, PartialEq, Eq, PartialOrd, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Clone, Copy)] pub enum Priority { - /// Regular transactions received over the network. (no priority boost) - Regular, - /// Transactions from retracted blocks (medium priority) - /// - /// When block becomes non-canonical we re-import the transactions it contains - /// to the queue and boost their priority. - Retracted, - /// Local transactions (high priority) - /// - /// Transactions either from a local account or - /// submitted over local RPC connection via `eth_sendRawTransaction` - Local, + /// Regular transactions received over the network. (no priority boost) + Regular, + /// Transactions from retracted blocks (medium priority) + /// + /// When block becomes non-canonical we re-import the transactions it contains + /// to the queue and boost their priority. + Retracted, + /// Local transactions (high priority) + /// + /// Transactions either from a local account or + /// submitted over local RPC connection via `eth_sendRawTransaction` + Local, } impl Priority { - fn is_local(&self) -> bool { - match *self { - Priority::Local => true, - _ => false, - } - } + fn is_local(&self) -> bool { + match *self { + Priority::Local => true, + _ => false, + } + } } /// Scoring properties for verified transaction. pub trait ScoredTransaction { - /// Gets transaction priority. - fn priority(&self) -> Priority; + /// Gets transaction priority. + fn priority(&self) -> Priority; - /// Gets transaction gas price. - fn gas_price(&self) -> &U256; + /// Gets transaction gas price. + fn gas_price(&self) -> &U256; - /// Gets transaction nonce. - fn nonce(&self) -> U256; + /// Gets transaction nonce. + fn nonce(&self) -> U256; } /// Verified transaction stored in the pool. #[derive(Debug, Clone, PartialEq, Eq)] pub struct VerifiedTransaction { - transaction: transaction::PendingTransaction, - // TODO [ToDr] hash and sender should go directly from the transaction - hash: H256, - sender: Address, - priority: Priority, - insertion_id: usize, + transaction: transaction::PendingTransaction, + // TODO [ToDr] hash and sender should go directly from the transaction + hash: H256, + sender: Address, + priority: Priority, + insertion_id: usize, } impl VerifiedTransaction { - /// Create `VerifiedTransaction` directly from `SignedTransaction`. - /// - /// This method should be used only: - /// 1. for tests - /// 2. In case we are converting pending block transactions that are already in the queue to match the function signature. - pub fn from_pending_block_transaction(tx: transaction::SignedTransaction) -> Self { - let hash = tx.hash(); - let sender = tx.sender(); - VerifiedTransaction { - transaction: tx.into(), - hash, - sender, - priority: Priority::Retracted, - insertion_id: 0, - } - } - - /// Gets transaction insertion id. - pub(crate) fn insertion_id(&self) -> usize { - self.insertion_id - } - - /// Gets wrapped `SignedTransaction` - pub fn signed(&self) -> &transaction::SignedTransaction { - &self.transaction - } - - /// Gets wrapped `PendingTransaction` - pub fn pending(&self) -> &transaction::PendingTransaction { - &self.transaction - } - + /// Create `VerifiedTransaction` directly from `SignedTransaction`. + /// + /// This method should be used only: + /// 1. for tests + /// 2. In case we are converting pending block transactions that are already in the queue to match the function signature. + pub fn from_pending_block_transaction(tx: transaction::SignedTransaction) -> Self { + let hash = tx.hash(); + let sender = tx.sender(); + VerifiedTransaction { + transaction: tx.into(), + hash, + sender, + priority: Priority::Retracted, + insertion_id: 0, + } + } + + /// Gets transaction insertion id. + pub(crate) fn insertion_id(&self) -> usize { + self.insertion_id + } + + /// Gets wrapped `SignedTransaction` + pub fn signed(&self) -> &transaction::SignedTransaction { + &self.transaction + } + + /// Gets wrapped `PendingTransaction` + pub fn pending(&self) -> &transaction::PendingTransaction { + &self.transaction + } } impl txpool::VerifiedTransaction for VerifiedTransaction { - type Hash = H256; - type Sender = Address; + type Hash = H256; + type Sender = Address; - fn hash(&self) -> &H256 { - &self.hash - } + fn hash(&self) -> &H256 { + &self.hash + } - fn mem_usage(&self) -> usize { - self.transaction.heap_size_of_children() - } + fn mem_usage(&self) -> usize { + self.transaction.heap_size_of_children() + } - fn sender(&self) -> &Address { - &self.sender - } + fn sender(&self) -> &Address { + &self.sender + } } impl ScoredTransaction for VerifiedTransaction { - fn priority(&self) -> Priority { - self.priority - } - - /// Gets transaction gas price. - fn gas_price(&self) -> &U256 { - &self.transaction.gas_price - } - - /// Gets transaction nonce. - fn nonce(&self) -> U256 { - self.transaction.nonce - } + fn priority(&self) -> Priority { + self.priority + } + + /// Gets transaction gas price. + fn gas_price(&self) -> &U256 { + &self.transaction.gas_price + } + + /// Gets transaction nonce. + fn nonce(&self) -> U256 { + self.transaction.nonce + } } diff --git a/miner/src/pool/queue.rs b/miner/src/pool/queue.rs index ad7c9e6f125..cf8a1f3802a 100644 --- a/miner/src/pool/queue.rs +++ b/miner/src/pool/queue.rs @@ -1,38 +1,45 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Ethereum Transaction Queue -use std::{cmp, fmt}; -use std::sync::Arc; -use std::sync::atomic::{self, AtomicUsize}; -use std::collections::{BTreeMap, BTreeSet, HashMap}; +use std::{ + cmp, + collections::{BTreeMap, BTreeSet, HashMap}, + fmt, + sync::{ + atomic::{self, AtomicUsize}, + Arc, + }, +}; -use ethereum_types::{H256, U256, Address}; +use ethereum_types::{Address, H256, U256}; use parking_lot::RwLock; use txpool::{self, Verifier}; use types::transaction; use pool::{ - self, replace, scoring, verifier, client, ready, listener, - PrioritizationStrategy, PendingOrdering, PendingSettings, + self, client, listener, local_transactions::LocalTransactionsList, ready, replace, scoring, + verifier, PendingOrdering, PendingSettings, PrioritizationStrategy, }; -use pool::local_transactions::LocalTransactionsList; -type Listener = (LocalTransactionsList, (listener::Notifier, listener::Logger)); +type Listener = ( + LocalTransactionsList, + (listener::Notifier, listener::Logger), +); type Pool = txpool::Pool; /// Max cache time in milliseconds for pending transactions. @@ -54,17 +61,17 @@ const CULL_SENDERS_CHUNK: usize = 1024; /// Transaction queue status. #[derive(Debug, Clone, PartialEq)] pub struct Status { - /// Verifier options. - pub options: verifier::Options, - /// Current status of the transaction pool. - pub status: txpool::LightStatus, - /// Current limits of the transaction pool. - pub limits: txpool::Options, + /// Verifier options. + pub options: verifier::Options, + /// Current status of the transaction pool. + pub status: txpool::LightStatus, + /// Current limits of the transaction pool. + pub limits: txpool::Options, } impl fmt::Display for Status { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - writeln!( + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + writeln!( fmt, "Pool: {current}/{max} ({senders} senders; {mem}/{mem_max} kB) [minGasPrice: {gp} Mwei, maxGas: {max_gas}]", current = self.status.transaction_count, @@ -75,116 +82,116 @@ impl fmt::Display for Status { gp = self.options.minimal_gas_price / 1_000_000, max_gas = cmp::min(self.options.block_gas_limit, self.options.tx_gas_limit), ) - } + } } #[derive(Debug)] struct CachedPending { - block_number: u64, - current_timestamp: u64, - nonce_cap: Option, - has_local_pending: bool, - pending: Option>>, - max_len: usize, + block_number: u64, + current_timestamp: u64, + nonce_cap: Option, + has_local_pending: bool, + pending: Option>>, + max_len: usize, } impl CachedPending { - /// Creates new `CachedPending` without cached set. - pub fn none() -> Self { - CachedPending { - block_number: 0, - current_timestamp: 0, - has_local_pending: false, - pending: None, - nonce_cap: None, - max_len: 0, - } - } - - /// Remove cached pending set. - pub fn clear(&mut self) { - self.pending = None; - } - - /// Returns cached pending set (if any) if it's valid. - pub fn pending( - &self, - block_number: u64, - current_timestamp: u64, - nonce_cap: Option<&U256>, - max_len: usize, - ) -> Option>> { - // First check if we have anything in cache. - let pending = self.pending.as_ref()?; - - if block_number != self.block_number { - return None; - } - - // In case we don't have any local pending transactions - // there is no need to invalidate the cache because of timestamp. - // Timestamp only affects local `PendingTransactions` with `Condition::Timestamp`. - if self.has_local_pending && current_timestamp > self.current_timestamp + TIMESTAMP_CACHE { - return None; - } - - // It's fine to return limited set even if `nonce_cap` is `None`. - // The worst thing that may happen is that some transactions won't get propagated in current round, - // but they are not really valid in current block anyway. We will propagate them in the next round. - // Also there is no way to have both `Some` with different numbers since it depends on the block number - // and a constant parameter in schedule (`nonce_cap_increment`) - if self.nonce_cap.is_none() && nonce_cap.is_some() { - return None; - } - - // It's fine to just take a smaller subset, but not other way around. - if max_len > self.max_len { - return None; - } - - Some(pending.iter().take(max_len).cloned().collect()) - } + /// Creates new `CachedPending` without cached set. + pub fn none() -> Self { + CachedPending { + block_number: 0, + current_timestamp: 0, + has_local_pending: false, + pending: None, + nonce_cap: None, + max_len: 0, + } + } + + /// Remove cached pending set. + pub fn clear(&mut self) { + self.pending = None; + } + + /// Returns cached pending set (if any) if it's valid. + pub fn pending( + &self, + block_number: u64, + current_timestamp: u64, + nonce_cap: Option<&U256>, + max_len: usize, + ) -> Option>> { + // First check if we have anything in cache. + let pending = self.pending.as_ref()?; + + if block_number != self.block_number { + return None; + } + + // In case we don't have any local pending transactions + // there is no need to invalidate the cache because of timestamp. + // Timestamp only affects local `PendingTransactions` with `Condition::Timestamp`. + if self.has_local_pending && current_timestamp > self.current_timestamp + TIMESTAMP_CACHE { + return None; + } + + // It's fine to return limited set even if `nonce_cap` is `None`. + // The worst thing that may happen is that some transactions won't get propagated in current round, + // but they are not really valid in current block anyway. We will propagate them in the next round. + // Also there is no way to have both `Some` with different numbers since it depends on the block number + // and a constant parameter in schedule (`nonce_cap_increment`) + if self.nonce_cap.is_none() && nonce_cap.is_some() { + return None; + } + + // It's fine to just take a smaller subset, but not other way around. + if max_len > self.max_len { + return None; + } + + Some(pending.iter().take(max_len).cloned().collect()) + } } #[derive(Debug)] struct RecentlyRejected { - inner: RwLock>, - limit: usize, + inner: RwLock>, + limit: usize, } impl RecentlyRejected { - fn new(limit: usize) -> Self { - RecentlyRejected { - limit, - inner: RwLock::new(HashMap::with_capacity(MIN_REJECTED_CACHE_SIZE)), - } - } - - fn clear(&self) { - self.inner.write().clear(); - } - - fn get(&self, hash: &H256) -> Option { - self.inner.read().get(hash).cloned() - } - - fn insert(&self, hash: H256, err: &transaction::Error) { - if self.inner.read().contains_key(&hash) { - return; - } - - let mut inner = self.inner.write(); - inner.insert(hash, err.clone()); - - // clean up - if inner.len() > self.limit { - // randomly remove half of the entries - let to_remove: Vec<_> = inner.keys().take(self.limit / 2).cloned().collect(); - for key in to_remove { - inner.remove(&key); - } - } - } + fn new(limit: usize) -> Self { + RecentlyRejected { + limit, + inner: RwLock::new(HashMap::with_capacity(MIN_REJECTED_CACHE_SIZE)), + } + } + + fn clear(&self) { + self.inner.write().clear(); + } + + fn get(&self, hash: &H256) -> Option { + self.inner.read().get(hash).cloned() + } + + fn insert(&self, hash: H256, err: &transaction::Error) { + if self.inner.read().contains_key(&hash) { + return; + } + + let mut inner = self.inner.write(); + inner.insert(hash, err.clone()); + + // clean up + if inner.len() > self.limit { + // randomly remove half of the entries + let to_remove: Vec<_> = inner.keys().take(self.limit / 2).cloned().collect(); + for key in to_remove { + inner.remove(&key); + } + } + } } /// Minimal size of rejection cache, by default it's equal to queue size. @@ -198,80 +205,90 @@ const MIN_REJECTED_CACHE_SIZE: usize = 2048; /// - returning an iterator for transactions that are ready to be included in block (pending) #[derive(Debug)] pub struct TransactionQueue { - insertion_id: Arc, - pool: RwLock, - options: RwLock, - cached_pending: RwLock, - recently_rejected: RecentlyRejected, + insertion_id: Arc, + pool: RwLock, + options: RwLock, + cached_pending: RwLock, + recently_rejected: RecentlyRejected, } impl TransactionQueue { - /// Create new queue with given pool limits and initial verification options. - pub fn new( - limits: txpool::Options, - verification_options: verifier::Options, - strategy: PrioritizationStrategy, - ) -> Self { - let max_count = limits.max_count; - TransactionQueue { - insertion_id: Default::default(), - pool: RwLock::new(txpool::Pool::new(Default::default(), scoring::NonceAndGasPrice(strategy), limits)), - options: RwLock::new(verification_options), - cached_pending: RwLock::new(CachedPending::none()), - recently_rejected: RecentlyRejected::new(cmp::max(MIN_REJECTED_CACHE_SIZE, max_count / 4)), - } - } - - /// Update verification options - /// - /// Some parameters of verification may vary in time (like block gas limit or minimal gas price). - pub fn set_verifier_options(&self, options: verifier::Options) { - *self.options.write() = options; - } - - /// Sets the in-chain transaction checker for pool listener. - pub fn set_in_chain_checker(&self, f: F) where - F: Fn(&H256) -> bool + Send + Sync + 'static - { - self.pool.write().listener_mut().0.set_in_chain_checker(f) - } - - /// Import a set of transactions to the pool. - /// - /// Given blockchain and state access (Client) - /// verifies and imports transactions to the pool. - pub fn import( - &self, - client: C, - transactions: Vec, - ) -> Vec> { - // Run verification - trace_time!("pool::verify_and_import"); - let options = self.options.read().clone(); - - let transaction_to_replace = { - if options.no_early_reject { - None - } else { - let pool = self.pool.read(); - if pool.is_full() { - pool.worst_transaction().map(|worst| (pool.scoring().clone(), worst)) - } else { - None - } - } - }; - - let verifier = verifier::Verifier::new( - client.clone(), - options, - self.insertion_id.clone(), - transaction_to_replace, - ); - - let mut replace = replace::ReplaceByScoreAndReadiness::new(self.pool.read().scoring().clone(), client); - - let results = transactions + /// Create new queue with given pool limits and initial verification options. + pub fn new( + limits: txpool::Options, + verification_options: verifier::Options, + strategy: PrioritizationStrategy, + ) -> Self { + let max_count = limits.max_count; + TransactionQueue { + insertion_id: Default::default(), + pool: RwLock::new(txpool::Pool::new( + Default::default(), + scoring::NonceAndGasPrice(strategy), + limits, + )), + options: RwLock::new(verification_options), + cached_pending: RwLock::new(CachedPending::none()), + recently_rejected: RecentlyRejected::new(cmp::max( + MIN_REJECTED_CACHE_SIZE, + max_count / 4, + )), + } + } + + /// Update verification options + /// + /// Some parameters of verification may vary in time (like block gas limit or minimal gas price). + pub fn set_verifier_options(&self, options: verifier::Options) { + *self.options.write() = options; + } + + /// Sets the in-chain transaction checker for pool listener. + pub fn set_in_chain_checker(&self, f: F) + where + F: Fn(&H256) -> bool + Send + Sync + 'static, + { + self.pool.write().listener_mut().0.set_in_chain_checker(f) + } + + /// Import a set of transactions to the pool. + /// + /// Given blockchain and state access (Client) + /// verifies and imports transactions to the pool. + pub fn import( + &self, + client: C, + transactions: Vec, + ) -> Vec> { + // Run verification + trace_time!("pool::verify_and_import"); + let options = self.options.read().clone(); + + let transaction_to_replace = { + if options.no_early_reject { + None + } else { + let pool = self.pool.read(); + if pool.is_full() { + pool.worst_transaction() + .map(|worst| (pool.scoring().clone(), worst)) + } else { + None + } + } + }; + + let verifier = verifier::Verifier::new( + client.clone(), + options, + self.insertion_id.clone(), + transaction_to_replace, + ); + + let mut replace = + replace::ReplaceByScoreAndReadiness::new(self.pool.read().scoring().clone(), client); + + let results = transactions .into_iter() .map(|transaction| { let hash = transaction.hash(); @@ -301,309 +318,348 @@ impl TransactionQueue { }) .collect::>(); - // Notify about imported transactions. - (self.pool.write().listener_mut().1).0.notify(); - - if results.iter().any(|r| r.is_ok()) { - self.cached_pending.write().clear(); - } - - results - } - - /// Returns all transactions in the queue without explicit ordering. - pub fn all_transactions(&self) -> Vec> { - let ready = |_tx: &pool::VerifiedTransaction| txpool::Readiness::Ready; - self.pool.read().unordered_pending(ready).collect() - } - - /// Returns all transaction hashes in the queue without explicit ordering. - pub fn all_transaction_hashes(&self) -> Vec { - let ready = |_tx: &pool::VerifiedTransaction| txpool::Readiness::Ready; - self.pool.read().unordered_pending(ready).map(|tx| tx.hash).collect() - } - - /// Computes unordered set of pending hashes. - /// - /// Since strict nonce-checking is not required, you may get some false positive future transactions as well. - pub fn pending_hashes( - &self, - nonce: N, - ) -> BTreeSet where - N: Fn(&Address) -> Option, - { - let ready = ready::OptionalState::new(nonce); - self.pool.read().unordered_pending(ready).map(|tx| tx.hash).collect() - } - - /// Returns current pending transactions ordered by priority. - /// - /// NOTE: This may return a cached version of pending transaction set. - /// Re-computing the pending set is possible with `#collect_pending` method, - /// but be aware that it's a pretty expensive operation. - pub fn pending( - &self, - client: C, - settings: PendingSettings, - ) -> Vec> where - C: client::NonceClient, - { - let PendingSettings { block_number, current_timestamp, nonce_cap, max_len, ordering } = settings; - if let Some(pending) = self.cached_pending.read().pending(block_number, current_timestamp, nonce_cap.as_ref(), max_len) { - return pending; - } - - // Double check after acquiring write lock - let mut cached_pending = self.cached_pending.write(); - if let Some(pending) = cached_pending.pending(block_number, current_timestamp, nonce_cap.as_ref(), max_len) { - return pending; - } - - // In case we don't have a cached set, but we don't care about order - // just return the unordered set. - if let PendingOrdering::Unordered = ordering { - let ready = Self::ready(client, block_number, current_timestamp, nonce_cap); - return self.pool.read().unordered_pending(ready).take(max_len).collect(); - } - - let pending: Vec<_> = self.collect_pending(client, block_number, current_timestamp, nonce_cap, |i| { - i.take(max_len).collect() - }); - - *cached_pending = CachedPending { - block_number, - current_timestamp, - nonce_cap, - has_local_pending: self.has_local_pending_transactions(), - pending: Some(pending.clone()), - max_len, - }; - - pending - } - - /// Collect pending transactions. - /// - /// NOTE This is re-computing the pending set and it might be expensive to do so. - /// Prefer using cached pending set using `#pending` method. - pub fn collect_pending( - &self, - client: C, - block_number: u64, - current_timestamp: u64, - nonce_cap: Option, - collect: F, - ) -> T where - C: client::NonceClient, - F: FnOnce(txpool::PendingIterator< - pool::VerifiedTransaction, - (ready::Condition, ready::State), - scoring::NonceAndGasPrice, - Listener, - >) -> T, - { - debug!(target: "txqueue", "Re-computing pending set for block: {}", block_number); - trace_time!("pool::collect_pending"); - let ready = Self::ready(client, block_number, current_timestamp, nonce_cap); - collect(self.pool.read().pending(ready)) - } - - fn ready( - client: C, - block_number: u64, - current_timestamp: u64, - nonce_cap: Option, - ) -> (ready::Condition, ready::State) where - C: client::NonceClient, - { - let pending_readiness = ready::Condition::new(block_number, current_timestamp); - // don't mark any transactions as stale at this point. - let stale_id = None; - let state_readiness = ready::State::new(client, stale_id, nonce_cap); - - (pending_readiness, state_readiness) - } - - /// Culls all stalled transactions from the pool. - pub fn cull( - &self, - client: C, - ) { - trace_time!("pool::cull"); - // We don't care about future transactions, so nonce_cap is not important. - let nonce_cap = None; - // We want to clear stale transactions from the queue as well. - // (Transactions that are occuping the queue for a long time without being included) - let stale_id = { - let current_id = self.insertion_id.load(atomic::Ordering::Relaxed); - // wait at least for half of the queue to be replaced - let gap = self.pool.read().options().max_count / 2; - // but never less than 100 transactions - let gap = cmp::max(100, gap); - - current_id.checked_sub(gap) - }; - - self.recently_rejected.clear(); - - let mut removed = 0; - let senders: Vec<_> = { - let pool = self.pool.read(); - let senders = pool.senders().cloned().collect(); - senders - }; - for chunk in senders.chunks(CULL_SENDERS_CHUNK) { - trace_time!("pool::cull::chunk"); - let state_readiness = ready::State::new(client.clone(), stale_id, nonce_cap); - removed += self.pool.write().cull(Some(chunk), state_readiness); - } - debug!(target: "txqueue", "Removed {} stalled transactions. {}", removed, self.status()); - } - - /// Returns next valid nonce for given sender - /// or `None` if there are no pending transactions from that sender. - pub fn next_nonce( - &self, - client: C, - address: &Address, - ) -> Option { - // Do not take nonce_cap into account when determining next nonce. - let nonce_cap = None; - // Also we ignore stale transactions in the queue. - let stale_id = None; - - let state_readiness = ready::State::new(client, stale_id, nonce_cap); - - self.pool.read().pending_from_sender(state_readiness, address) - .last() - .map(|tx| tx.signed().nonce.saturating_add(U256::from(1))) - } - - /// Retrieve a transaction from the pool. - /// - /// Given transaction hash looks up that transaction in the pool - /// and returns a shared pointer to it or `None` if it's not present. - pub fn find( - &self, - hash: &H256, - ) -> Option> { - self.pool.read().find(hash) - } - - /// Remove a set of transactions from the pool. - /// - /// Given an iterator of transaction hashes - /// removes them from the pool. - /// That method should be used if invalid transactions are detected - /// or you want to cancel a transaction. - pub fn remove<'a, T: IntoIterator>( - &self, - hashes: T, - is_invalid: bool, - ) -> Vec>> { - let results = { - let mut pool = self.pool.write(); - - hashes - .into_iter() - .map(|hash| pool.remove(hash, is_invalid)) - .collect::>() - }; - - if results.iter().any(Option::is_some) { - self.cached_pending.write().clear(); - } - - results - } - - /// Clear the entire pool. - pub fn clear(&self) { - self.pool.write().clear(); - } - - /// Penalize given senders. - pub fn penalize<'a, T: IntoIterator>(&self, senders: T) { - let mut pool = self.pool.write(); - for sender in senders { - pool.update_scores(sender, ()); - } - } - - /// Returns gas price of currently the worst transaction in the pool. - pub fn current_worst_gas_price(&self) -> U256 { - match self.pool.read().worst_transaction() { - Some(tx) => tx.signed().gas_price, - None => self.options.read().minimal_gas_price, - } - } - - /// Returns a status of the queue. - pub fn status(&self) -> Status { - let pool = self.pool.read(); - let status = pool.light_status(); - let limits = pool.options(); - let options = self.options.read().clone(); - - Status { - options, - status, - limits, - } - } - - /// Check if there are any local transactions in the pool. - /// - /// Returns `true` if there are any transactions in the pool - /// that has been marked as local. - /// - /// Local transactions are the ones from accounts managed by this node - /// and transactions submitted via local RPC (`eth_sendRawTransaction`) - pub fn has_local_pending_transactions(&self) -> bool { - self.pool.read().listener().0.has_pending() - } - - /// Returns status of recently seen local transactions. - pub fn local_transactions(&self) -> BTreeMap { - self.pool.read().listener().0.all_transactions().iter().map(|(a, b)| (*a, b.clone())).collect() - } - - /// Add a callback to be notified about all transactions entering the pool. - pub fn add_listener(&self, f: Box) { - let mut pool = self.pool.write(); - (pool.listener_mut().1).0.add(f); - } - - /// Check if pending set is cached. - #[cfg(test)] - pub fn is_pending_cached(&self) -> bool { - self.cached_pending.read().pending.is_some() - } + // Notify about imported transactions. + (self.pool.write().listener_mut().1).0.notify(); + + if results.iter().any(|r| r.is_ok()) { + self.cached_pending.write().clear(); + } + + results + } + + /// Returns all transactions in the queue without explicit ordering. + pub fn all_transactions(&self) -> Vec> { + let ready = |_tx: &pool::VerifiedTransaction| txpool::Readiness::Ready; + self.pool.read().unordered_pending(ready).collect() + } + + /// Returns all transaction hashes in the queue without explicit ordering. + pub fn all_transaction_hashes(&self) -> Vec { + let ready = |_tx: &pool::VerifiedTransaction| txpool::Readiness::Ready; + self.pool + .read() + .unordered_pending(ready) + .map(|tx| tx.hash) + .collect() + } + + /// Computes unordered set of pending hashes. + /// + /// Since strict nonce-checking is not required, you may get some false positive future transactions as well. + pub fn pending_hashes(&self, nonce: N) -> BTreeSet + where + N: Fn(&Address) -> Option, + { + let ready = ready::OptionalState::new(nonce); + self.pool + .read() + .unordered_pending(ready) + .map(|tx| tx.hash) + .collect() + } + + /// Returns current pending transactions ordered by priority. + /// + /// NOTE: This may return a cached version of pending transaction set. + /// Re-computing the pending set is possible with `#collect_pending` method, + /// but be aware that it's a pretty expensive operation. + pub fn pending( + &self, + client: C, + settings: PendingSettings, + ) -> Vec> + where + C: client::NonceClient, + { + let PendingSettings { + block_number, + current_timestamp, + nonce_cap, + max_len, + ordering, + } = settings; + if let Some(pending) = self.cached_pending.read().pending( + block_number, + current_timestamp, + nonce_cap.as_ref(), + max_len, + ) { + return pending; + } + + // Double check after acquiring write lock + let mut cached_pending = self.cached_pending.write(); + if let Some(pending) = + cached_pending.pending(block_number, current_timestamp, nonce_cap.as_ref(), max_len) + { + return pending; + } + + // In case we don't have a cached set, but we don't care about order + // just return the unordered set. + if let PendingOrdering::Unordered = ordering { + let ready = Self::ready(client, block_number, current_timestamp, nonce_cap); + return self + .pool + .read() + .unordered_pending(ready) + .take(max_len) + .collect(); + } + + let pending: Vec<_> = + self.collect_pending(client, block_number, current_timestamp, nonce_cap, |i| { + i.take(max_len).collect() + }); + + *cached_pending = CachedPending { + block_number, + current_timestamp, + nonce_cap, + has_local_pending: self.has_local_pending_transactions(), + pending: Some(pending.clone()), + max_len, + }; + + pending + } + + /// Collect pending transactions. + /// + /// NOTE This is re-computing the pending set and it might be expensive to do so. + /// Prefer using cached pending set using `#pending` method. + pub fn collect_pending( + &self, + client: C, + block_number: u64, + current_timestamp: u64, + nonce_cap: Option, + collect: F, + ) -> T + where + C: client::NonceClient, + F: FnOnce( + txpool::PendingIterator< + pool::VerifiedTransaction, + (ready::Condition, ready::State), + scoring::NonceAndGasPrice, + Listener, + >, + ) -> T, + { + debug!(target: "txqueue", "Re-computing pending set for block: {}", block_number); + trace_time!("pool::collect_pending"); + let ready = Self::ready(client, block_number, current_timestamp, nonce_cap); + collect(self.pool.read().pending(ready)) + } + + fn ready( + client: C, + block_number: u64, + current_timestamp: u64, + nonce_cap: Option, + ) -> (ready::Condition, ready::State) + where + C: client::NonceClient, + { + let pending_readiness = ready::Condition::new(block_number, current_timestamp); + // don't mark any transactions as stale at this point. + let stale_id = None; + let state_readiness = ready::State::new(client, stale_id, nonce_cap); + + (pending_readiness, state_readiness) + } + + /// Culls all stalled transactions from the pool. + pub fn cull(&self, client: C) { + trace_time!("pool::cull"); + // We don't care about future transactions, so nonce_cap is not important. + let nonce_cap = None; + // We want to clear stale transactions from the queue as well. + // (Transactions that are occuping the queue for a long time without being included) + let stale_id = { + let current_id = self.insertion_id.load(atomic::Ordering::Relaxed); + // wait at least for half of the queue to be replaced + let gap = self.pool.read().options().max_count / 2; + // but never less than 100 transactions + let gap = cmp::max(100, gap); + + current_id.checked_sub(gap) + }; + + self.recently_rejected.clear(); + + let mut removed = 0; + let senders: Vec<_> = { + let pool = self.pool.read(); + let senders = pool.senders().cloned().collect(); + senders + }; + for chunk in senders.chunks(CULL_SENDERS_CHUNK) { + trace_time!("pool::cull::chunk"); + let state_readiness = ready::State::new(client.clone(), stale_id, nonce_cap); + removed += self.pool.write().cull(Some(chunk), state_readiness); + } + debug!(target: "txqueue", "Removed {} stalled transactions. {}", removed, self.status()); + } + + /// Returns next valid nonce for given sender + /// or `None` if there are no pending transactions from that sender. + pub fn next_nonce(&self, client: C, address: &Address) -> Option { + // Do not take nonce_cap into account when determining next nonce. + let nonce_cap = None; + // Also we ignore stale transactions in the queue. + let stale_id = None; + + let state_readiness = ready::State::new(client, stale_id, nonce_cap); + + self.pool + .read() + .pending_from_sender(state_readiness, address) + .last() + .map(|tx| tx.signed().nonce.saturating_add(U256::from(1))) + } + + /// Retrieve a transaction from the pool. + /// + /// Given transaction hash looks up that transaction in the pool + /// and returns a shared pointer to it or `None` if it's not present. + pub fn find(&self, hash: &H256) -> Option> { + self.pool.read().find(hash) + } + + /// Remove a set of transactions from the pool. + /// + /// Given an iterator of transaction hashes + /// removes them from the pool. + /// That method should be used if invalid transactions are detected + /// or you want to cancel a transaction. + pub fn remove<'a, T: IntoIterator>( + &self, + hashes: T, + is_invalid: bool, + ) -> Vec>> { + let results = { + let mut pool = self.pool.write(); + + hashes + .into_iter() + .map(|hash| pool.remove(hash, is_invalid)) + .collect::>() + }; + + if results.iter().any(Option::is_some) { + self.cached_pending.write().clear(); + } + + results + } + + /// Clear the entire pool. + pub fn clear(&self) { + self.pool.write().clear(); + } + + /// Penalize given senders. + pub fn penalize<'a, T: IntoIterator>(&self, senders: T) { + let mut pool = self.pool.write(); + for sender in senders { + pool.update_scores(sender, ()); + } + } + + /// Returns gas price of currently the worst transaction in the pool. + pub fn current_worst_gas_price(&self) -> U256 { + match self.pool.read().worst_transaction() { + Some(tx) => tx.signed().gas_price, + None => self.options.read().minimal_gas_price, + } + } + + /// Returns a status of the queue. + pub fn status(&self) -> Status { + let pool = self.pool.read(); + let status = pool.light_status(); + let limits = pool.options(); + let options = self.options.read().clone(); + + Status { + options, + status, + limits, + } + } + + /// Check if there are any local transactions in the pool. + /// + /// Returns `true` if there are any transactions in the pool + /// that has been marked as local. + /// + /// Local transactions are the ones from accounts managed by this node + /// and transactions submitted via local RPC (`eth_sendRawTransaction`) + pub fn has_local_pending_transactions(&self) -> bool { + self.pool.read().listener().0.has_pending() + } + + /// Returns status of recently seen local transactions. + pub fn local_transactions(&self) -> BTreeMap { + self.pool + .read() + .listener() + .0 + .all_transactions() + .iter() + .map(|(a, b)| (*a, b.clone())) + .collect() + } + + /// Add a callback to be notified about all transactions entering the pool. + pub fn add_listener(&self, f: Box) { + let mut pool = self.pool.write(); + (pool.listener_mut().1).0.add(f); + } + + /// Check if pending set is cached. + #[cfg(test)] + pub fn is_pending_cached(&self) -> bool { + self.cached_pending.read().pending.is_some() + } } fn convert_error(err: txpool::Error) -> transaction::Error { - use self::txpool::Error; - - match err { - Error::AlreadyImported(..) => transaction::Error::AlreadyImported, - Error::TooCheapToEnter(..) => transaction::Error::LimitReached, - Error::TooCheapToReplace(..) => transaction::Error::TooCheapToReplace - } + use self::txpool::Error; + + match err { + Error::AlreadyImported(..) => transaction::Error::AlreadyImported, + Error::TooCheapToEnter(..) => transaction::Error::LimitReached, + Error::TooCheapToReplace(..) => transaction::Error::TooCheapToReplace { + prev: None, + new: None, + }, + } } #[cfg(test)] mod tests { - use super::*; - use pool::tests::client::TestClient; - - #[test] - fn should_get_pending_transactions() { - let queue = TransactionQueue::new(txpool::Options::default(), verifier::Options::default(), PrioritizationStrategy::GasPriceOnly); - - let pending: Vec<_> = queue.pending(TestClient::default(), PendingSettings::all_prioritized(0, 0)); - - for tx in pending { - assert!(tx.signed().nonce > 0.into()); - } - } + use super::*; + use pool::tests::client::TestClient; + + #[test] + fn should_get_pending_transactions() { + let queue = TransactionQueue::new( + txpool::Options::default(), + verifier::Options::default(), + PrioritizationStrategy::GasPriceOnly, + ); + + let pending: Vec<_> = queue.pending( + TestClient::default(), + PendingSettings::all_prioritized(0, 0), + ); + + for tx in pending { + assert!(tx.signed().nonce > 0.into()); + } + } } diff --git a/miner/src/pool/ready.rs b/miner/src/pool/ready.rs index 3accba13903..75d54bf8ffe 100644 --- a/miner/src/pool/ready.rs +++ b/miner/src/pool/ready.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Transaction Readiness indicator //! @@ -38,95 +38,90 @@ //! First `Readiness::Future` response also causes all subsequent transactions from the same sender //! to be marked as `Future`. -use std::cmp; -use std::collections::HashMap; +use std::{cmp, collections::HashMap}; -use ethereum_types::{U256, H160 as Address}; +use ethereum_types::{H160 as Address, U256}; use txpool::{self, VerifiedTransaction as PoolVerifiedTransaction}; use types::transaction; -use super::client::NonceClient; -use super::VerifiedTransaction; +use super::{client::NonceClient, VerifiedTransaction}; /// Checks readiness of transactions by comparing the nonce to state nonce. #[derive(Debug)] pub struct State { - nonces: HashMap, - state: C, - max_nonce: Option, - stale_id: Option, + nonces: HashMap, + state: C, + max_nonce: Option, + stale_id: Option, } impl State { - /// Create new State checker, given client interface. - pub fn new( - state: C, - stale_id: Option, - max_nonce: Option, - ) -> Self { - State { - nonces: Default::default(), - state, - max_nonce, - stale_id, - } - } + /// Create new State checker, given client interface. + pub fn new(state: C, stale_id: Option, max_nonce: Option) -> Self { + State { + nonces: Default::default(), + state, + max_nonce, + stale_id, + } + } } impl txpool::Ready for State { - fn is_ready(&mut self, tx: &VerifiedTransaction) -> txpool::Readiness { - // Check max nonce - match self.max_nonce { - Some(nonce) if tx.transaction.nonce > nonce => { - return txpool::Readiness::Future; - }, - _ => {}, - } - - let sender = tx.sender(); - let state = &self.state; - let state_nonce = || state.account_nonce(sender); - let nonce = self.nonces.entry(*sender).or_insert_with(state_nonce); - match tx.transaction.nonce.cmp(nonce) { - // Before marking as future check for stale ids - cmp::Ordering::Greater => match self.stale_id { - Some(id) if tx.insertion_id() < id => txpool::Readiness::Stale, - _ => txpool::Readiness::Future, - }, - cmp::Ordering::Less => txpool::Readiness::Stale, - cmp::Ordering::Equal => { - *nonce = nonce.saturating_add(U256::from(1)); - txpool::Readiness::Ready - }, - } - } + fn is_ready(&mut self, tx: &VerifiedTransaction) -> txpool::Readiness { + // Check max nonce + match self.max_nonce { + Some(nonce) if tx.transaction.nonce > nonce => { + return txpool::Readiness::Future; + } + _ => {} + } + + let sender = tx.sender(); + let state = &self.state; + let state_nonce = || state.account_nonce(sender); + let nonce = self.nonces.entry(*sender).or_insert_with(state_nonce); + match tx.transaction.nonce.cmp(nonce) { + // Before marking as future check for stale ids + cmp::Ordering::Greater => match self.stale_id { + Some(id) if tx.insertion_id() < id => txpool::Readiness::Stale, + _ => txpool::Readiness::Future, + }, + cmp::Ordering::Less => txpool::Readiness::Stale, + cmp::Ordering::Equal => { + *nonce = nonce.saturating_add(U256::from(1)); + txpool::Readiness::Ready + } + } + } } /// Checks readines of Pending transactions by comparing it with current time and block number. #[derive(Debug)] pub struct Condition { - block_number: u64, - now: u64, + block_number: u64, + now: u64, } impl Condition { - /// Create a new condition checker given current block number and UTC timestamp. - pub fn new(block_number: u64, now: u64) -> Self { - Condition { - block_number, - now, - } - } + /// Create a new condition checker given current block number and UTC timestamp. + pub fn new(block_number: u64, now: u64) -> Self { + Condition { block_number, now } + } } impl txpool::Ready for Condition { - fn is_ready(&mut self, tx: &VerifiedTransaction) -> txpool::Readiness { - match tx.transaction.condition { - Some(transaction::Condition::Number(block)) if block > self.block_number => txpool::Readiness::Future, - Some(transaction::Condition::Timestamp(time)) if time > self.now => txpool::Readiness::Future, - _ => txpool::Readiness::Ready, - } - } + fn is_ready(&mut self, tx: &VerifiedTransaction) -> txpool::Readiness { + match tx.transaction.condition { + Some(transaction::Condition::Number(block)) if block > self.block_number => { + txpool::Readiness::Future + } + Some(transaction::Condition::Timestamp(time)) if time > self.now => { + txpool::Readiness::Future + } + _ => txpool::Readiness::Ready, + } + } } /// Readiness checker that only relies on nonce cache (does actually go to state). @@ -135,114 +130,144 @@ impl txpool::Ready for Condition { /// isn't found in provided state nonce store, defaults to the tx nonce and updates /// the nonce store. Useful for using with a state nonce cache when false positives are allowed. pub struct OptionalState { - nonces: HashMap, - state: C, + nonces: HashMap, + state: C, } impl OptionalState { - pub fn new(state: C) -> Self { - OptionalState { - nonces: Default::default(), - state, - } - } + pub fn new(state: C) -> Self { + OptionalState { + nonces: Default::default(), + state, + } + } } impl Option> txpool::Ready for OptionalState { - fn is_ready(&mut self, tx: &VerifiedTransaction) -> txpool::Readiness { - let sender = tx.sender(); - let state = &self.state; - let nonce = self.nonces.entry(*sender).or_insert_with(|| { - state(sender).unwrap_or_else(|| tx.transaction.nonce) - }); - match tx.transaction.nonce.cmp(nonce) { - cmp::Ordering::Greater => txpool::Readiness::Future, - cmp::Ordering::Less => txpool::Readiness::Stale, - cmp::Ordering::Equal => { - *nonce = nonce.saturating_add(U256::from(1)); - txpool::Readiness::Ready - }, - } - } + fn is_ready(&mut self, tx: &VerifiedTransaction) -> txpool::Readiness { + let sender = tx.sender(); + let state = &self.state; + let nonce = self + .nonces + .entry(*sender) + .or_insert_with(|| state(sender).unwrap_or_else(|| tx.transaction.nonce)); + match tx.transaction.nonce.cmp(nonce) { + cmp::Ordering::Greater => txpool::Readiness::Future, + cmp::Ordering::Less => txpool::Readiness::Stale, + cmp::Ordering::Equal => { + *nonce = nonce.saturating_add(U256::from(1)); + txpool::Readiness::Ready + } + } + } } #[cfg(test)] mod tests { - use super::*; - use txpool::Ready; - use pool::tests::client::TestClient; - use pool::tests::tx::{Tx, TxExt}; - - #[test] - fn should_return_correct_state_readiness() { - // given - let (tx1, tx2, tx3) = Tx::default().signed_triple(); - let (tx1, tx2, tx3) = (tx1.verified(), tx2.verified(), tx3.verified()); - - // when - assert_eq!(State::new(TestClient::new(), None, None).is_ready(&tx3), txpool::Readiness::Future); - assert_eq!(State::new(TestClient::new(), None, None).is_ready(&tx2), txpool::Readiness::Future); - - let mut ready = State::new(TestClient::new(), None, None); - - // then - assert_eq!(ready.is_ready(&tx1), txpool::Readiness::Ready); - assert_eq!(ready.is_ready(&tx2), txpool::Readiness::Ready); - assert_eq!(ready.is_ready(&tx3), txpool::Readiness::Ready); - } - - #[test] - fn should_return_future_if_nonce_cap_reached() { - // given - let tx = Tx::default().signed().verified(); - - // when - let res1 = State::new(TestClient::new(), None, Some(10.into())).is_ready(&tx); - let res2 = State::new(TestClient::new(), None, Some(124.into())).is_ready(&tx); - - // then - assert_eq!(res1, txpool::Readiness::Future); - assert_eq!(res2, txpool::Readiness::Ready); - } - - #[test] - fn should_return_stale_if_nonce_does_not_match() { - // given - let tx = Tx::default().signed().verified(); - - // when - let res = State::new(TestClient::new().with_nonce(125), None, None).is_ready(&tx); - - // then - assert_eq!(res, txpool::Readiness::Stale); - } - - #[test] - fn should_return_stale_for_old_transactions() { - // given - let (_, tx) = Tx::default().signed_pair().verified(); - - // when - let res = State::new(TestClient::new(), Some(1), None).is_ready(&tx); - - // then - assert_eq!(res, txpool::Readiness::Stale); - } - - #[test] - fn should_check_readiness_of_condition() { - // given - let tx = Tx::default().signed(); - let v = |tx: transaction::PendingTransaction| TestClient::new().verify(tx); - let tx1 = v(transaction::PendingTransaction::new(tx.clone(), transaction::Condition::Number(5).into())); - let tx2 = v(transaction::PendingTransaction::new(tx.clone(), transaction::Condition::Timestamp(3).into())); - let tx3 = v(transaction::PendingTransaction::new(tx.clone(), None)); - - // when/then - assert_eq!(Condition::new(0, 0).is_ready(&tx1), txpool::Readiness::Future); - assert_eq!(Condition::new(0, 0).is_ready(&tx2), txpool::Readiness::Future); - assert_eq!(Condition::new(0, 0).is_ready(&tx3), txpool::Readiness::Ready); - assert_eq!(Condition::new(5, 0).is_ready(&tx1), txpool::Readiness::Ready); - assert_eq!(Condition::new(0, 3).is_ready(&tx2), txpool::Readiness::Ready); - } + use super::*; + use pool::tests::{ + client::TestClient, + tx::{Tx, TxExt}, + }; + use txpool::Ready; + + #[test] + fn should_return_correct_state_readiness() { + // given + let (tx1, tx2, tx3) = Tx::default().signed_triple(); + let (tx1, tx2, tx3) = (tx1.verified(), tx2.verified(), tx3.verified()); + + // when + assert_eq!( + State::new(TestClient::new(), None, None).is_ready(&tx3), + txpool::Readiness::Future + ); + assert_eq!( + State::new(TestClient::new(), None, None).is_ready(&tx2), + txpool::Readiness::Future + ); + + let mut ready = State::new(TestClient::new(), None, None); + + // then + assert_eq!(ready.is_ready(&tx1), txpool::Readiness::Ready); + assert_eq!(ready.is_ready(&tx2), txpool::Readiness::Ready); + assert_eq!(ready.is_ready(&tx3), txpool::Readiness::Ready); + } + + #[test] + fn should_return_future_if_nonce_cap_reached() { + // given + let tx = Tx::default().signed().verified(); + + // when + let res1 = State::new(TestClient::new(), None, Some(10.into())).is_ready(&tx); + let res2 = State::new(TestClient::new(), None, Some(124.into())).is_ready(&tx); + + // then + assert_eq!(res1, txpool::Readiness::Future); + assert_eq!(res2, txpool::Readiness::Ready); + } + + #[test] + fn should_return_stale_if_nonce_does_not_match() { + // given + let tx = Tx::default().signed().verified(); + + // when + let res = State::new(TestClient::new().with_nonce(125), None, None).is_ready(&tx); + + // then + assert_eq!(res, txpool::Readiness::Stale); + } + + #[test] + fn should_return_stale_for_old_transactions() { + // given + let (_, tx) = Tx::default().signed_pair().verified(); + + // when + let res = State::new(TestClient::new(), Some(1), None).is_ready(&tx); + + // then + assert_eq!(res, txpool::Readiness::Stale); + } + + #[test] + fn should_check_readiness_of_condition() { + // given + let tx = Tx::default().signed(); + let v = |tx: transaction::PendingTransaction| TestClient::new().verify(tx); + let tx1 = v(transaction::PendingTransaction::new( + tx.clone(), + transaction::Condition::Number(5).into(), + )); + let tx2 = v(transaction::PendingTransaction::new( + tx.clone(), + transaction::Condition::Timestamp(3).into(), + )); + let tx3 = v(transaction::PendingTransaction::new(tx.clone(), None)); + + // when/then + assert_eq!( + Condition::new(0, 0).is_ready(&tx1), + txpool::Readiness::Future + ); + assert_eq!( + Condition::new(0, 0).is_ready(&tx2), + txpool::Readiness::Future + ); + assert_eq!( + Condition::new(0, 0).is_ready(&tx3), + txpool::Readiness::Ready + ); + assert_eq!( + Condition::new(5, 0).is_ready(&tx1), + txpool::Readiness::Ready + ); + assert_eq!( + Condition::new(0, 3).is_ready(&tx2), + txpool::Readiness::Ready + ); + } } diff --git a/miner/src/pool/replace.rs b/miner/src/pool/replace.rs index b1112dcae1f..0e2b7cc95ec 100644 --- a/miner/src/pool/replace.rs +++ b/miner/src/pool/replace.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Replacing Transactions //! @@ -25,391 +25,658 @@ use std::cmp; -use ethereum_types::{U256, H160 as Address}; -use txpool::{self, scoring::{Choice, Scoring}, ReplaceTransaction}; -use txpool::VerifiedTransaction; use super::{client, ScoredTransaction}; +use ethereum_types::{H160 as Address, U256}; +use txpool::{ + self, + scoring::{Choice, Scoring}, + ReplaceTransaction, VerifiedTransaction, +}; /// Choose whether to replace based on the sender, the score and finally the /// `Readiness` of the transactions being compared. #[derive(Debug)] pub struct ReplaceByScoreAndReadiness { - scoring: S, - client: C, + scoring: S, + client: C, } impl ReplaceByScoreAndReadiness { - /// Create a new `ReplaceByScoreAndReadiness` - pub fn new(scoring: S, client: C) -> Self { - ReplaceByScoreAndReadiness { scoring, client } - } + /// Create a new `ReplaceByScoreAndReadiness` + pub fn new(scoring: S, client: C) -> Self { + ReplaceByScoreAndReadiness { scoring, client } + } } impl txpool::ShouldReplace for ReplaceByScoreAndReadiness where - T: VerifiedTransaction + ScoredTransaction + PartialEq, - S: Scoring, - C: client::NonceClient, + T: VerifiedTransaction + ScoredTransaction + PartialEq, + S: Scoring, + C: client::NonceClient, { - fn should_replace( - &self, - old: &ReplaceTransaction, - new: &ReplaceTransaction, - ) -> Choice { - let both_local = old.priority().is_local() && new.priority().is_local(); - if old.sender() == new.sender() { - // prefer earliest transaction - match new.nonce().cmp(&old.nonce()) { - cmp::Ordering::Equal => self.scoring.choose(&old, &new), - _ if both_local => Choice::InsertNew, - cmp::Ordering::Less => Choice::ReplaceOld, - cmp::Ordering::Greater => Choice::RejectNew, - } - } else if both_local { - Choice::InsertNew - } else { - let old_score = (old.priority(), old.gas_price()); - let new_score = (new.priority(), new.gas_price()); - if new_score > old_score { - let state = &self.client; - // calculate readiness based on state nonce + pooled txs from same sender - let is_ready = |replace: &ReplaceTransaction| { - let mut nonce = state.account_nonce(replace.sender()); - if let Some(txs) = replace.pooled_by_sender { - for tx in txs.iter() { - if nonce == tx.nonce() && *tx.transaction != ***replace.transaction { - nonce = nonce.saturating_add(U256::from(1)) - } else { - break - } - } - } - nonce == replace.nonce() - }; - - if !is_ready(new) && is_ready(old) { - // prevent a ready transaction being replace by a non-ready transaction - Choice::RejectNew - } else { - Choice::ReplaceOld - } - } else { - Choice::RejectNew - } - } - } + fn should_replace(&self, old: &ReplaceTransaction, new: &ReplaceTransaction) -> Choice { + let both_local = old.priority().is_local() && new.priority().is_local(); + if old.sender() == new.sender() { + // prefer earliest transaction + match new.nonce().cmp(&old.nonce()) { + cmp::Ordering::Equal => self.scoring.choose(&old, &new), + _ if both_local => Choice::InsertNew, + cmp::Ordering::Less => Choice::ReplaceOld, + cmp::Ordering::Greater => Choice::RejectNew, + } + } else if both_local { + Choice::InsertNew + } else { + let old_score = (old.priority(), old.gas_price()); + let new_score = (new.priority(), new.gas_price()); + if new_score > old_score { + // Check if this is a replacement transaction. + // + // With replacement transactions we can safely return `InsertNew` here, because + // we don't need to remove `old` (worst transaction in the pool) since `new` will replace + // some other transaction in the pool so we will never go above limit anyway. + if let Some(txs) = new.pooled_by_sender { + if let Ok(index) = txs.binary_search_by(|old| self.scoring.compare(old, new)) { + return match self.scoring.choose(&txs[index], new) { + Choice::ReplaceOld => Choice::InsertNew, + choice => choice, + }; + } + } + + let state = &self.client; + // calculate readiness based on state nonce + pooled txs from same sender + let is_ready = |replace: &ReplaceTransaction| { + let mut nonce = state.account_nonce(replace.sender()); + if let Some(txs) = replace.pooled_by_sender { + for tx in txs.iter() { + if nonce == tx.nonce() && *tx.transaction != ***replace.transaction { + nonce = nonce.saturating_add(U256::from(1)) + } else { + break; + } + } + } + nonce == replace.nonce() + }; + + if !is_ready(new) && is_ready(old) { + // prevent a ready transaction being replace by a non-ready transaction + Choice::RejectNew + } else { + Choice::ReplaceOld + } + } else { + Choice::RejectNew + } + } + } } #[cfg(test)] mod tests { - use super::*; - - use std::sync::Arc; - use ethkey::{Random, Generator, KeyPair}; - use pool::tests::tx::{Tx, TxExt}; - use pool::tests::client::TestClient; - use pool::scoring::*; - use pool::{PrioritizationStrategy, VerifiedTransaction}; - use txpool::scoring::Choice::*; - use txpool::ShouldReplace; - - fn local_tx_verified(tx: Tx, keypair: &KeyPair) -> VerifiedTransaction { - let mut verified_tx = tx.unsigned().sign(keypair.secret(), None).verified(); - verified_tx.priority = ::pool::Priority::Local; - verified_tx - } - - fn should_replace(replace: &ShouldReplace, old: VerifiedTransaction, new: VerifiedTransaction) -> Choice { - let old_tx = txpool::Transaction { insertion_id: 0, transaction: Arc::new(old) }; - let new_tx = txpool::Transaction { insertion_id: 0, transaction: Arc::new(new) }; - let old = ReplaceTransaction::new(&old_tx, Default::default()); - let new = ReplaceTransaction::new(&new_tx, Default::default()); - replace.should_replace(&old, &new) - } - - #[test] - fn should_always_accept_local_transactions_unless_same_sender_and_nonce() { - let scoring = NonceAndGasPrice(PrioritizationStrategy::GasPriceOnly); - let client = TestClient::new().with_nonce(1); - let replace = ReplaceByScoreAndReadiness::new(scoring, client); - - // same sender txs - let keypair = Random.generate().unwrap(); - - let same_sender_tx1 = local_tx_verified(Tx { - nonce: 1, - gas_price: 1, - ..Default::default() - }, &keypair); - - let same_sender_tx2 = local_tx_verified(Tx { - nonce: 2, - gas_price: 100, - ..Default::default() - }, &keypair); - - let same_sender_tx3 = local_tx_verified(Tx { - nonce: 2, - gas_price: 200, - ..Default::default() - }, &keypair); - - // different sender txs - let sender1 = Random.generate().unwrap(); - let different_sender_tx1 = local_tx_verified(Tx { - nonce: 2, - gas_price: 1, - ..Default::default() - }, &sender1); - - let sender2 = Random.generate().unwrap(); - let different_sender_tx2 = local_tx_verified(Tx { - nonce: 1, - gas_price: 10, - ..Default::default() - }, &sender2); - - assert_eq!(should_replace(&replace, same_sender_tx1.clone(), same_sender_tx2.clone()), InsertNew); - assert_eq!(should_replace(&replace, same_sender_tx2.clone(), same_sender_tx1.clone()), InsertNew); - - assert_eq!(should_replace(&replace, different_sender_tx1.clone(), different_sender_tx2.clone()), InsertNew); - assert_eq!(should_replace(&replace, different_sender_tx2.clone(), different_sender_tx1.clone()), InsertNew); - - // txs with same sender and nonce - assert_eq!(should_replace(&replace, same_sender_tx2.clone(), same_sender_tx3.clone()), ReplaceOld); - assert_eq!(should_replace(&replace, same_sender_tx3.clone(), same_sender_tx2.clone()), RejectNew); - } - - #[test] - fn should_replace_same_sender_by_nonce() { - let scoring = NonceAndGasPrice(PrioritizationStrategy::GasPriceOnly); - let client = TestClient::new().with_nonce(1); - let replace = ReplaceByScoreAndReadiness::new(scoring, client); - - let tx1 = Tx { - nonce: 1, - gas_price: 1, - ..Default::default() - }; - let tx2 = Tx { - nonce: 2, - gas_price: 100, - ..Default::default() - }; - let tx3 = Tx { - nonce: 2, - gas_price: 110, - ..Default::default() - }; - let tx4 = Tx { - nonce: 2, - gas_price: 130, - ..Default::default() - }; - - let keypair = Random.generate().unwrap(); - let txs = vec![tx1, tx2, tx3, tx4].into_iter().map(|tx| { - tx.unsigned().sign(keypair.secret(), None).verified() - }).collect::>(); - - assert_eq!(should_replace(&replace, txs[0].clone(), txs[1].clone()), RejectNew); - assert_eq!(should_replace(&replace, txs[1].clone(), txs[0].clone()), ReplaceOld); - - assert_eq!(should_replace(&replace, txs[1].clone(), txs[2].clone()), RejectNew); - assert_eq!(should_replace(&replace, txs[2].clone(), txs[1].clone()), RejectNew); - - assert_eq!(should_replace(&replace, txs[1].clone(), txs[3].clone()), ReplaceOld); - assert_eq!(should_replace(&replace, txs[3].clone(), txs[1].clone()), RejectNew); - } - - #[test] - fn should_replace_different_sender_by_priority_and_gas_price() { - // given - let scoring = NonceAndGasPrice(PrioritizationStrategy::GasPriceOnly); - let client = TestClient::new().with_nonce(0); - let replace = ReplaceByScoreAndReadiness::new(scoring, client); - - let tx_regular_low_gas = { - let tx = Tx { - nonce: 1, - gas_price: 1, - ..Default::default() - }; - tx.signed().verified() - }; - let tx_regular_high_gas = { - let tx = Tx { - nonce: 2, - gas_price: 10, - ..Default::default() - }; - tx.signed().verified() - }; - let tx_local_low_gas = { - let tx = Tx { - nonce: 2, - gas_price: 1, - ..Default::default() - }; - let mut verified_tx = tx.signed().verified(); - verified_tx.priority = ::pool::Priority::Local; - verified_tx - }; - let tx_local_high_gas = { - let tx = Tx { - nonce: 1, - gas_price: 10, - ..Default::default() - }; - let mut verified_tx = tx.signed().verified(); - verified_tx.priority = ::pool::Priority::Local; - verified_tx - }; - - assert_eq!(should_replace(&replace, tx_regular_low_gas.clone(), tx_regular_high_gas.clone()), ReplaceOld); - assert_eq!(should_replace(&replace, tx_regular_high_gas.clone(), tx_regular_low_gas.clone()), RejectNew); - - assert_eq!(should_replace(&replace, tx_regular_high_gas.clone(), tx_local_low_gas.clone()), ReplaceOld); - assert_eq!(should_replace(&replace, tx_local_low_gas.clone(), tx_regular_high_gas.clone()), RejectNew); - - assert_eq!(should_replace(&replace, tx_local_low_gas.clone(), tx_local_high_gas.clone()), InsertNew); - assert_eq!(should_replace(&replace, tx_local_high_gas.clone(), tx_regular_low_gas.clone()), RejectNew); - } - - #[test] - fn should_not_replace_ready_transaction_with_future_transaction() { - let scoring = NonceAndGasPrice(PrioritizationStrategy::GasPriceOnly); - let client = TestClient::new().with_nonce(1); - let replace = ReplaceByScoreAndReadiness::new(scoring, client); - - let tx_ready_low_score = { - let tx = Tx { - nonce: 1, - gas_price: 1, - ..Default::default() - }; - tx.signed().verified() - }; - let tx_future_high_score = { - let tx = Tx { - nonce: 3, // future nonce - gas_price: 10, - ..Default::default() - }; - tx.signed().verified() - }; - - assert_eq!(should_replace(&replace, tx_ready_low_score, tx_future_high_score), RejectNew); - } - - #[test] - fn should_compute_readiness_with_pooled_transactions_from_the_same_sender_as_the_existing_transaction() { - let scoring = NonceAndGasPrice(PrioritizationStrategy::GasPriceOnly); - let client = TestClient::new().with_nonce(1); - let replace = ReplaceByScoreAndReadiness::new(scoring, client); - - let old_sender = Random.generate().unwrap(); - let tx_old_ready_1 = { - let tx = Tx { - nonce: 1, - gas_price: 1, - ..Default::default() - }; - tx.unsigned().sign(&old_sender.secret(), None).verified() - }; - let tx_old_ready_2 = { - let tx = Tx { - nonce: 2, - gas_price: 1, - ..Default::default() - }; - tx.unsigned().sign(&old_sender.secret(), None).verified() - }; - let tx_old_ready_3 = { - let tx = Tx { - nonce: 3, - gas_price: 1, - ..Default::default() - }; - tx.unsigned().sign(&old_sender.secret(), None).verified() - }; - - let new_tx = { - let tx = Tx { - nonce: 3, // future nonce - gas_price: 10, - ..Default::default() - }; - tx.signed().verified() - }; - - let old_tx = txpool::Transaction { insertion_id: 0, transaction: Arc::new(tx_old_ready_3) }; - let pooled_txs = [ - txpool::Transaction { insertion_id: 0, transaction: Arc::new(tx_old_ready_1) }, - txpool::Transaction { insertion_id: 0, transaction: Arc::new(tx_old_ready_2) }, - ]; - - let new_tx = txpool::Transaction { insertion_id: 0, transaction: Arc::new(new_tx) }; - - let old = ReplaceTransaction::new(&old_tx, Some(&pooled_txs)); - let new = ReplaceTransaction::new(&new_tx, Default::default()); - - assert_eq!(replace.should_replace(&old, &new), RejectNew); - } - - #[test] - fn should_compute_readiness_with_pooled_transactions_from_the_same_sender_as_the_new_transaction() { - let scoring = NonceAndGasPrice(PrioritizationStrategy::GasPriceOnly); - let client = TestClient::new().with_nonce(1); - let replace = ReplaceByScoreAndReadiness::new(scoring, client); - - // current transaction is ready but has a lower gas price than the new one - let old_tx = { - let tx = Tx { - nonce: 1, - gas_price: 1, - ..Default::default() - }; - tx.signed().verified() - }; - - let new_sender = Random.generate().unwrap(); - let tx_new_ready_1 = { - let tx = Tx { - nonce: 1, - gas_price: 1, - ..Default::default() - }; - tx.unsigned().sign(&new_sender.secret(), None).verified() - }; - let tx_new_ready_2 = { - let tx = Tx { - nonce: 2, - gas_price: 1, - ..Default::default() - }; - tx.unsigned().sign(&new_sender.secret(), None).verified() - }; - let tx_new_ready_3 = { - let tx = Tx { - nonce: 3, - gas_price: 10, // hi - ..Default::default() - }; - tx.unsigned().sign(&new_sender.secret(), None).verified() - }; - - let old_tx = txpool::Transaction { insertion_id: 0, transaction: Arc::new(old_tx) }; - - let new_tx = txpool::Transaction { insertion_id: 0, transaction: Arc::new(tx_new_ready_3) }; - let pooled_txs = [ - txpool::Transaction { insertion_id: 0, transaction: Arc::new(tx_new_ready_1) }, - txpool::Transaction { insertion_id: 0, transaction: Arc::new(tx_new_ready_2) }, - ]; - - let old = ReplaceTransaction::new(&old_tx, None); - let new = ReplaceTransaction::new(&new_tx, Some(&pooled_txs)); - - assert_eq!(replace.should_replace(&old, &new), ReplaceOld); - } + use super::*; + + use ethkey::{Generator, KeyPair, Random}; + use pool::{ + scoring::*, + tests::{ + client::TestClient, + tx::{Tx, TxExt}, + }, + PrioritizationStrategy, VerifiedTransaction, + }; + use std::sync::Arc; + use txpool::{scoring::Choice::*, ShouldReplace}; + + fn local_tx_verified(tx: Tx, keypair: &KeyPair) -> VerifiedTransaction { + let mut verified_tx = tx.unsigned().sign(keypair.secret(), None).verified(); + verified_tx.priority = ::pool::Priority::Local; + verified_tx + } + + fn should_replace( + replace: &dyn ShouldReplace, + old: VerifiedTransaction, + new: VerifiedTransaction, + ) -> Choice { + let old_tx = txpool::Transaction { + insertion_id: 0, + transaction: Arc::new(old), + }; + let new_tx = txpool::Transaction { + insertion_id: 0, + transaction: Arc::new(new), + }; + let old = ReplaceTransaction::new(&old_tx, Default::default()); + let new = ReplaceTransaction::new(&new_tx, Default::default()); + replace.should_replace(&old, &new) + } + + #[test] + fn should_always_accept_local_transactions_unless_same_sender_and_nonce() { + let scoring = NonceAndGasPrice(PrioritizationStrategy::GasPriceOnly); + let client = TestClient::new().with_nonce(1); + let replace = ReplaceByScoreAndReadiness::new(scoring, client); + + // same sender txs + let keypair = Random.generate().unwrap(); + + let same_sender_tx1 = local_tx_verified( + Tx { + nonce: 1, + gas_price: 1, + ..Default::default() + }, + &keypair, + ); + + let same_sender_tx2 = local_tx_verified( + Tx { + nonce: 2, + gas_price: 100, + ..Default::default() + }, + &keypair, + ); + + let same_sender_tx3 = local_tx_verified( + Tx { + nonce: 2, + gas_price: 200, + ..Default::default() + }, + &keypair, + ); + + // different sender txs + let sender1 = Random.generate().unwrap(); + let different_sender_tx1 = local_tx_verified( + Tx { + nonce: 2, + gas_price: 1, + ..Default::default() + }, + &sender1, + ); + + let sender2 = Random.generate().unwrap(); + let different_sender_tx2 = local_tx_verified( + Tx { + nonce: 1, + gas_price: 10, + ..Default::default() + }, + &sender2, + ); + + assert_eq!( + should_replace(&replace, same_sender_tx1.clone(), same_sender_tx2.clone()), + InsertNew + ); + assert_eq!( + should_replace(&replace, same_sender_tx2.clone(), same_sender_tx1.clone()), + InsertNew + ); + + assert_eq!( + should_replace( + &replace, + different_sender_tx1.clone(), + different_sender_tx2.clone() + ), + InsertNew + ); + assert_eq!( + should_replace( + &replace, + different_sender_tx2.clone(), + different_sender_tx1.clone() + ), + InsertNew + ); + + // txs with same sender and nonce + assert_eq!( + should_replace(&replace, same_sender_tx2.clone(), same_sender_tx3.clone()), + ReplaceOld + ); + assert_eq!( + should_replace(&replace, same_sender_tx3.clone(), same_sender_tx2.clone()), + RejectNew + ); + } + + #[test] + fn should_replace_same_sender_by_nonce() { + let scoring = NonceAndGasPrice(PrioritizationStrategy::GasPriceOnly); + let client = TestClient::new().with_nonce(1); + let replace = ReplaceByScoreAndReadiness::new(scoring, client); + + let tx1 = Tx { + nonce: 1, + gas_price: 1, + ..Default::default() + }; + let tx2 = Tx { + nonce: 2, + gas_price: 100, + ..Default::default() + }; + let tx3 = Tx { + nonce: 2, + gas_price: 110, + ..Default::default() + }; + let tx4 = Tx { + nonce: 2, + gas_price: 130, + ..Default::default() + }; + + let keypair = Random.generate().unwrap(); + let txs = vec![tx1, tx2, tx3, tx4] + .into_iter() + .map(|tx| tx.unsigned().sign(keypair.secret(), None).verified()) + .collect::>(); + + assert_eq!( + should_replace(&replace, txs[0].clone(), txs[1].clone()), + RejectNew + ); + assert_eq!( + should_replace(&replace, txs[1].clone(), txs[0].clone()), + ReplaceOld + ); + + assert_eq!( + should_replace(&replace, txs[1].clone(), txs[2].clone()), + RejectNew + ); + assert_eq!( + should_replace(&replace, txs[2].clone(), txs[1].clone()), + RejectNew + ); + + assert_eq!( + should_replace(&replace, txs[1].clone(), txs[3].clone()), + ReplaceOld + ); + assert_eq!( + should_replace(&replace, txs[3].clone(), txs[1].clone()), + RejectNew + ); + } + + #[test] + fn should_replace_different_sender_by_priority_and_gas_price() { + // given + let scoring = NonceAndGasPrice(PrioritizationStrategy::GasPriceOnly); + let client = TestClient::new().with_nonce(0); + let replace = ReplaceByScoreAndReadiness::new(scoring, client); + + let tx_regular_low_gas = { + let tx = Tx { + nonce: 1, + gas_price: 1, + ..Default::default() + }; + tx.signed().verified() + }; + let tx_regular_high_gas = { + let tx = Tx { + nonce: 2, + gas_price: 10, + ..Default::default() + }; + tx.signed().verified() + }; + let tx_local_low_gas = { + let tx = Tx { + nonce: 2, + gas_price: 1, + ..Default::default() + }; + let mut verified_tx = tx.signed().verified(); + verified_tx.priority = ::pool::Priority::Local; + verified_tx + }; + let tx_local_high_gas = { + let tx = Tx { + nonce: 1, + gas_price: 10, + ..Default::default() + }; + let mut verified_tx = tx.signed().verified(); + verified_tx.priority = ::pool::Priority::Local; + verified_tx + }; + + assert_eq!( + should_replace( + &replace, + tx_regular_low_gas.clone(), + tx_regular_high_gas.clone() + ), + ReplaceOld + ); + assert_eq!( + should_replace( + &replace, + tx_regular_high_gas.clone(), + tx_regular_low_gas.clone() + ), + RejectNew + ); + + assert_eq!( + should_replace( + &replace, + tx_regular_high_gas.clone(), + tx_local_low_gas.clone() + ), + ReplaceOld + ); + assert_eq!( + should_replace( + &replace, + tx_local_low_gas.clone(), + tx_regular_high_gas.clone() + ), + RejectNew + ); + + assert_eq!( + should_replace( + &replace, + tx_local_low_gas.clone(), + tx_local_high_gas.clone() + ), + InsertNew + ); + assert_eq!( + should_replace( + &replace, + tx_local_high_gas.clone(), + tx_regular_low_gas.clone() + ), + RejectNew + ); + } + + #[test] + fn should_not_replace_ready_transaction_with_future_transaction() { + let scoring = NonceAndGasPrice(PrioritizationStrategy::GasPriceOnly); + let client = TestClient::new().with_nonce(1); + let replace = ReplaceByScoreAndReadiness::new(scoring, client); + + let tx_ready_low_score = { + let tx = Tx { + nonce: 1, + gas_price: 1, + ..Default::default() + }; + tx.signed().verified() + }; + let tx_future_high_score = { + let tx = Tx { + nonce: 3, // future nonce + gas_price: 10, + ..Default::default() + }; + tx.signed().verified() + }; + + assert_eq!( + should_replace(&replace, tx_ready_low_score, tx_future_high_score), + RejectNew + ); + } + + #[test] + fn should_compute_readiness_with_pooled_transactions_from_the_same_sender_as_the_existing_transaction( + ) { + let scoring = NonceAndGasPrice(PrioritizationStrategy::GasPriceOnly); + let client = TestClient::new().with_nonce(1); + let replace = ReplaceByScoreAndReadiness::new(scoring, client); + + let old_sender = Random.generate().unwrap(); + let tx_old_ready_1 = { + let tx = Tx { + nonce: 1, + gas_price: 1, + ..Default::default() + }; + tx.unsigned().sign(&old_sender.secret(), None).verified() + }; + let tx_old_ready_2 = { + let tx = Tx { + nonce: 2, + gas_price: 1, + ..Default::default() + }; + tx.unsigned().sign(&old_sender.secret(), None).verified() + }; + let tx_old_ready_3 = { + let tx = Tx { + nonce: 3, + gas_price: 1, + ..Default::default() + }; + tx.unsigned().sign(&old_sender.secret(), None).verified() + }; + + let new_tx = { + let tx = Tx { + nonce: 3, // future nonce + gas_price: 10, + ..Default::default() + }; + tx.signed().verified() + }; + + let old_tx = txpool::Transaction { + insertion_id: 0, + transaction: Arc::new(tx_old_ready_3), + }; + let pooled_txs = [ + txpool::Transaction { + insertion_id: 0, + transaction: Arc::new(tx_old_ready_1), + }, + txpool::Transaction { + insertion_id: 0, + transaction: Arc::new(tx_old_ready_2), + }, + ]; + + let new_tx = txpool::Transaction { + insertion_id: 0, + transaction: Arc::new(new_tx), + }; + + let old = ReplaceTransaction::new(&old_tx, Some(&pooled_txs)); + let new = ReplaceTransaction::new(&new_tx, Default::default()); + + assert_eq!(replace.should_replace(&old, &new), RejectNew); + } + + #[test] + fn should_compute_readiness_with_pooled_transactions_from_the_same_sender_as_the_new_transaction( + ) { + let scoring = NonceAndGasPrice(PrioritizationStrategy::GasPriceOnly); + let client = TestClient::new().with_nonce(1); + let replace = ReplaceByScoreAndReadiness::new(scoring, client); + + // current transaction is ready but has a lower gas price than the new one + let old_tx = { + let tx = Tx { + nonce: 1, + gas_price: 1, + ..Default::default() + }; + tx.signed().verified() + }; + + let new_sender = Random.generate().unwrap(); + let tx_new_ready_1 = { + let tx = Tx { + nonce: 1, + gas_price: 1, + ..Default::default() + }; + tx.unsigned().sign(&new_sender.secret(), None).verified() + }; + let tx_new_ready_2 = { + let tx = Tx { + nonce: 2, + gas_price: 1, + ..Default::default() + }; + tx.unsigned().sign(&new_sender.secret(), None).verified() + }; + let tx_new_ready_3 = { + let tx = Tx { + nonce: 3, + gas_price: 10, // hi + ..Default::default() + }; + tx.unsigned().sign(&new_sender.secret(), None).verified() + }; + + let old_tx = txpool::Transaction { + insertion_id: 0, + transaction: Arc::new(old_tx), + }; + + let new_tx = txpool::Transaction { + insertion_id: 0, + transaction: Arc::new(tx_new_ready_3), + }; + let pooled_txs = [ + txpool::Transaction { + insertion_id: 0, + transaction: Arc::new(tx_new_ready_1), + }, + txpool::Transaction { + insertion_id: 0, + transaction: Arc::new(tx_new_ready_2), + }, + ]; + + let old = ReplaceTransaction::new(&old_tx, None); + let new = ReplaceTransaction::new(&new_tx, Some(&pooled_txs)); + + assert_eq!(replace.should_replace(&old, &new), ReplaceOld); + } + + #[test] + fn should_accept_local_tx_with_same_sender_and_nonce_with_better_gas_price() { + let scoring = NonceAndGasPrice(PrioritizationStrategy::GasPriceOnly); + let client = TestClient::new().with_nonce(1); + let replace = ReplaceByScoreAndReadiness::new(scoring, client); + + // current transaction is ready + let old_tx = { + let tx = Tx { + nonce: 1, + gas_price: 1, + ..Default::default() + }; + tx.signed().verified() + }; + + let new_sender = Random.generate().unwrap(); + let tx_new_ready_1 = local_tx_verified( + Tx { + nonce: 1, + gas_price: 1, + ..Default::default() + }, + &new_sender, + ); + + let tx_new_ready_2 = local_tx_verified( + Tx { + nonce: 1, + gas_price: 2, // same nonce, higher gas price + ..Default::default() + }, + &new_sender, + ); + + let old_tx = txpool::Transaction { + insertion_id: 0, + transaction: Arc::new(old_tx), + }; + + let new_tx = txpool::Transaction { + insertion_id: 0, + transaction: Arc::new(tx_new_ready_2), + }; + let pooled_txs = [txpool::Transaction { + insertion_id: 0, + transaction: Arc::new(tx_new_ready_1), + }]; + + let old = ReplaceTransaction::new(&old_tx, None); + let new = ReplaceTransaction::new(&new_tx, Some(&pooled_txs)); + + assert_eq!(replace.should_replace(&old, &new), InsertNew); + } + + #[test] + fn should_reject_local_tx_with_same_sender_and_nonce_with_worse_gas_price() { + let scoring = NonceAndGasPrice(PrioritizationStrategy::GasPriceOnly); + let client = TestClient::new().with_nonce(1); + let replace = ReplaceByScoreAndReadiness::new(scoring, client); + + // current transaction is ready + let old_tx = { + let tx = Tx { + nonce: 1, + gas_price: 1, + ..Default::default() + }; + tx.signed().verified() + }; + + let new_sender = Random.generate().unwrap(); + let tx_new_ready_1 = local_tx_verified( + Tx { + nonce: 1, + gas_price: 2, + ..Default::default() + }, + &new_sender, + ); + + let tx_new_ready_2 = local_tx_verified( + Tx { + nonce: 1, + gas_price: 1, // same nonce, lower gas price + ..Default::default() + }, + &new_sender, + ); + + let old_tx = txpool::Transaction { + insertion_id: 0, + transaction: Arc::new(old_tx), + }; + + let new_tx = txpool::Transaction { + insertion_id: 0, + transaction: Arc::new(tx_new_ready_2), + }; + let pooled_txs = [txpool::Transaction { + insertion_id: 0, + transaction: Arc::new(tx_new_ready_1), + }]; + + let old = ReplaceTransaction::new(&old_tx, None); + let new = ReplaceTransaction::new(&new_tx, Some(&pooled_txs)); + + assert_eq!(replace.should_replace(&old, &new), RejectNew); + } } diff --git a/miner/src/pool/scoring.rs b/miner/src/pool/scoring.rs index 0360bec3547..1b5e1641791 100644 --- a/miner/src/pool/scoring.rs +++ b/miner/src/pool/scoring.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Transaction Scoring and Ordering //! @@ -29,9 +29,9 @@ use std::cmp; +use super::{verifier, PrioritizationStrategy, ScoredTransaction, VerifiedTransaction}; use ethereum_types::U256; use txpool::{self, scoring}; -use super::{verifier, PrioritizationStrategy, VerifiedTransaction, ScoredTransaction}; /// Transaction with the same (sender, nonce) can be replaced only if /// `new_gas_price > old_gas_price + old_gas_price >> SHIFT` @@ -40,7 +40,7 @@ const GAS_PRICE_BUMP_SHIFT: usize = 3; // 2 = 25%, 3 = 12.5%, 4 = 6.25% /// Calculate minimal gas price requirement. #[inline] fn bump_gas_price(old_gp: U256) -> U256 { - old_gp.saturating_add(old_gp >> GAS_PRICE_BUMP_SHIFT) + old_gp.saturating_add(old_gp >> GAS_PRICE_BUMP_SHIFT) } /// Simple, gas-price based scoring for transactions. @@ -51,140 +51,156 @@ fn bump_gas_price(old_gp: U256) -> U256 { pub struct NonceAndGasPrice(pub PrioritizationStrategy); impl NonceAndGasPrice { - /// Decide if the transaction should even be considered into the pool (if the pool is full). - /// - /// Used by Verifier to quickly reject transactions that don't have any chance to get into the pool later on, - /// and save time on more expensive checks like sender recovery, etc. - /// - /// NOTE The method is never called for zero-gas-price transactions or local transactions - /// (such transactions are always considered to the pool and potentially rejected later on) - pub fn should_reject_early(&self, old: &VerifiedTransaction, new: &verifier::Transaction) -> bool { - if old.priority().is_local() { - return true - } - - &old.transaction.gas_price > new.gas_price() - } + /// Decide if the transaction should even be considered into the pool (if the pool is full). + /// + /// Used by Verifier to quickly reject transactions that don't have any chance to get into the pool later on, + /// and save time on more expensive checks like sender recovery, etc. + /// + /// NOTE The method is never called for zero-gas-price transactions or local transactions + /// (such transactions are always considered to the pool and potentially rejected later on) + pub fn should_reject_early( + &self, + old: &VerifiedTransaction, + new: &verifier::Transaction, + ) -> bool { + if old.priority().is_local() { + return true; + } + + &old.transaction.gas_price > new.gas_price() + } } -impl

txpool::Scoring

for NonceAndGasPrice where P: ScoredTransaction + txpool::VerifiedTransaction { - type Score = U256; - type Event = (); - - fn compare(&self, old: &P, other: &P) -> cmp::Ordering { - old.nonce().cmp(&other.nonce()) - } - - fn choose(&self, old: &P, new: &P) -> scoring::Choice { - if old.nonce() != new.nonce() { - return scoring::Choice::InsertNew - } - - let old_gp = old.gas_price(); - let new_gp = new.gas_price(); - - let min_required_gp = bump_gas_price(*old_gp); - - match min_required_gp.cmp(&new_gp) { - cmp::Ordering::Greater => scoring::Choice::RejectNew, - _ => scoring::Choice::ReplaceOld, - } - } - - fn update_scores(&self, txs: &[txpool::Transaction

], scores: &mut [U256], change: scoring::Change) { - use self::scoring::Change; - - match change { - Change::Culled(_) => {}, - Change::RemovedAt(_) => {} - Change::InsertedAt(i) | Change::ReplacedAt(i) => { - assert!(i < txs.len()); - assert!(i < scores.len()); - - scores[i] = *txs[i].transaction.gas_price(); - let boost = match txs[i].priority() { - super::Priority::Local => 15, - super::Priority::Retracted => 10, - super::Priority::Regular => 0, - }; - scores[i] = scores[i] << boost; - }, - // We are only sending an event in case of penalization. - // So just lower the priority of all non-local transactions. - Change::Event(_) => { - for (score, tx) in scores.iter_mut().zip(txs) { - // Never penalize local transactions. - if !tx.priority().is_local() { - *score = *score >> 3; - } - } - }, - } - } - - fn should_ignore_sender_limit(&self, new: &P) -> bool { - new.priority().is_local() - } +impl

txpool::Scoring

for NonceAndGasPrice +where + P: ScoredTransaction + txpool::VerifiedTransaction, +{ + type Score = U256; + type Event = (); + + fn compare(&self, old: &P, other: &P) -> cmp::Ordering { + old.nonce().cmp(&other.nonce()) + } + + fn choose(&self, old: &P, new: &P) -> scoring::Choice { + if old.nonce() != new.nonce() { + return scoring::Choice::InsertNew; + } + + let old_gp = old.gas_price(); + let new_gp = new.gas_price(); + + let min_required_gp = bump_gas_price(*old_gp); + + match min_required_gp.cmp(&new_gp) { + cmp::Ordering::Greater => scoring::Choice::RejectNew, + _ => scoring::Choice::ReplaceOld, + } + } + + fn update_scores( + &self, + txs: &[txpool::Transaction

], + scores: &mut [U256], + change: scoring::Change, + ) { + use self::scoring::Change; + + match change { + Change::Culled(_) => {} + Change::RemovedAt(_) => {} + Change::InsertedAt(i) | Change::ReplacedAt(i) => { + assert!(i < txs.len()); + assert!(i < scores.len()); + + scores[i] = *txs[i].transaction.gas_price(); + let boost = match txs[i].priority() { + super::Priority::Local => 15, + super::Priority::Retracted => 10, + super::Priority::Regular => 0, + }; + scores[i] = scores[i] << boost; + } + // We are only sending an event in case of penalization. + // So just lower the priority of all non-local transactions. + Change::Event(_) => { + for (score, tx) in scores.iter_mut().zip(txs) { + // Never penalize local transactions. + if !tx.priority().is_local() { + *score = *score >> 3; + } + } + } + } + } + + fn should_ignore_sender_limit(&self, new: &P) -> bool { + new.priority().is_local() + } } #[cfg(test)] mod tests { - use super::*; - - use std::sync::Arc; - use pool::tests::tx::{Tx, TxExt}; - use txpool::Scoring; - - #[test] - fn should_calculate_score_correctly() { - // given - let scoring = NonceAndGasPrice(PrioritizationStrategy::GasPriceOnly); - let (tx1, tx2, tx3) = Tx::default().signed_triple(); - let transactions = vec![tx1, tx2, tx3].into_iter().enumerate().map(|(i, tx)| { - let mut verified = tx.verified(); - verified.priority = match i { - 0 => ::pool::Priority::Local, - 1 => ::pool::Priority::Retracted, - _ => ::pool::Priority::Regular, - }; - txpool::Transaction { - insertion_id: 0, - transaction: Arc::new(verified), - } - }).collect::>(); - let initial_scores = vec![U256::from(0), 0.into(), 0.into()]; - - // No update required - let mut scores = initial_scores.clone(); - scoring.update_scores(&transactions, &mut *scores, scoring::Change::Culled(0)); - scoring.update_scores(&transactions, &mut *scores, scoring::Change::Culled(1)); - scoring.update_scores(&transactions, &mut *scores, scoring::Change::Culled(2)); - assert_eq!(scores, initial_scores); - let mut scores = initial_scores.clone(); - scoring.update_scores(&transactions, &mut *scores, scoring::Change::RemovedAt(0)); - scoring.update_scores(&transactions, &mut *scores, scoring::Change::RemovedAt(1)); - scoring.update_scores(&transactions, &mut *scores, scoring::Change::RemovedAt(2)); - assert_eq!(scores, initial_scores); - - // Compute score at given index - let mut scores = initial_scores.clone(); - scoring.update_scores(&transactions, &mut *scores, scoring::Change::InsertedAt(0)); - assert_eq!(scores, vec![32768.into(), 0.into(), 0.into()]); - scoring.update_scores(&transactions, &mut *scores, scoring::Change::InsertedAt(1)); - assert_eq!(scores, vec![32768.into(), 1024.into(), 0.into()]); - scoring.update_scores(&transactions, &mut *scores, scoring::Change::InsertedAt(2)); - assert_eq!(scores, vec![32768.into(), 1024.into(), 1.into()]); - - let mut scores = initial_scores.clone(); - scoring.update_scores(&transactions, &mut *scores, scoring::Change::ReplacedAt(0)); - assert_eq!(scores, vec![32768.into(), 0.into(), 0.into()]); - scoring.update_scores(&transactions, &mut *scores, scoring::Change::ReplacedAt(1)); - assert_eq!(scores, vec![32768.into(), 1024.into(), 0.into()]); - scoring.update_scores(&transactions, &mut *scores, scoring::Change::ReplacedAt(2)); - assert_eq!(scores, vec![32768.into(), 1024.into(), 1.into()]); - - // Check penalization - scoring.update_scores(&transactions, &mut *scores, scoring::Change::Event(())); - assert_eq!(scores, vec![32768.into(), 128.into(), 0.into()]); - } + use super::*; + + use pool::tests::tx::{Tx, TxExt}; + use std::sync::Arc; + use txpool::Scoring; + + #[test] + fn should_calculate_score_correctly() { + // given + let scoring = NonceAndGasPrice(PrioritizationStrategy::GasPriceOnly); + let (tx1, tx2, tx3) = Tx::default().signed_triple(); + let transactions = vec![tx1, tx2, tx3] + .into_iter() + .enumerate() + .map(|(i, tx)| { + let mut verified = tx.verified(); + verified.priority = match i { + 0 => ::pool::Priority::Local, + 1 => ::pool::Priority::Retracted, + _ => ::pool::Priority::Regular, + }; + txpool::Transaction { + insertion_id: 0, + transaction: Arc::new(verified), + } + }) + .collect::>(); + let initial_scores = vec![U256::from(0), 0.into(), 0.into()]; + + // No update required + let mut scores = initial_scores.clone(); + scoring.update_scores(&transactions, &mut *scores, scoring::Change::Culled(0)); + scoring.update_scores(&transactions, &mut *scores, scoring::Change::Culled(1)); + scoring.update_scores(&transactions, &mut *scores, scoring::Change::Culled(2)); + assert_eq!(scores, initial_scores); + let mut scores = initial_scores.clone(); + scoring.update_scores(&transactions, &mut *scores, scoring::Change::RemovedAt(0)); + scoring.update_scores(&transactions, &mut *scores, scoring::Change::RemovedAt(1)); + scoring.update_scores(&transactions, &mut *scores, scoring::Change::RemovedAt(2)); + assert_eq!(scores, initial_scores); + + // Compute score at given index + let mut scores = initial_scores.clone(); + scoring.update_scores(&transactions, &mut *scores, scoring::Change::InsertedAt(0)); + assert_eq!(scores, vec![32768.into(), 0.into(), 0.into()]); + scoring.update_scores(&transactions, &mut *scores, scoring::Change::InsertedAt(1)); + assert_eq!(scores, vec![32768.into(), 1024.into(), 0.into()]); + scoring.update_scores(&transactions, &mut *scores, scoring::Change::InsertedAt(2)); + assert_eq!(scores, vec![32768.into(), 1024.into(), 1.into()]); + + let mut scores = initial_scores.clone(); + scoring.update_scores(&transactions, &mut *scores, scoring::Change::ReplacedAt(0)); + assert_eq!(scores, vec![32768.into(), 0.into(), 0.into()]); + scoring.update_scores(&transactions, &mut *scores, scoring::Change::ReplacedAt(1)); + assert_eq!(scores, vec![32768.into(), 1024.into(), 0.into()]); + scoring.update_scores(&transactions, &mut *scores, scoring::Change::ReplacedAt(2)); + assert_eq!(scores, vec![32768.into(), 1024.into(), 1.into()]); + + // Check penalization + scoring.update_scores(&transactions, &mut *scores, scoring::Change::Event(())); + assert_eq!(scores, vec![32768.into(), 128.into(), 0.into()]); + } } diff --git a/miner/src/pool/tests/client.rs b/miner/src/pool/tests/client.rs index 4735fbd64d4..fc0413690f4 100644 --- a/miner/src/pool/tests/client.rs +++ b/miner/src/pool/tests/client.rs @@ -1,148 +1,162 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use std::sync::{atomic, Arc}; -use ethereum_types::{U256, H256, Address}; +use ethereum_types::{Address, H256, U256}; use rlp::Rlp; -use types::transaction::{self, Transaction, SignedTransaction, UnverifiedTransaction}; +use types::transaction::{self, SignedTransaction, Transaction, UnverifiedTransaction}; -use pool; -use pool::client::AccountDetails; +use pool::{self, client::AccountDetails}; const MAX_TRANSACTION_SIZE: usize = 15 * 1024; #[derive(Debug, Clone)] pub struct TestClient { - verification_invoked: Arc, - account_details: AccountDetails, - gas_required: U256, - is_service_transaction: bool, - local_address: Address, - max_transaction_size: usize, + verification_invoked: Arc, + account_details: AccountDetails, + gas_required: U256, + is_service_transaction: bool, + local_address: Address, + max_transaction_size: usize, } impl Default for TestClient { - fn default() -> Self { - TestClient { - verification_invoked: Default::default(), - account_details: AccountDetails { - nonce: 123.into(), - balance: 63_100.into(), - is_local: false, - }, - gas_required: 21_000.into(), - is_service_transaction: false, - local_address: Default::default(), - max_transaction_size: MAX_TRANSACTION_SIZE, - } - } + fn default() -> Self { + TestClient { + verification_invoked: Default::default(), + account_details: AccountDetails { + nonce: 123.into(), + balance: 63_100.into(), + is_local: false, + }, + gas_required: 21_000.into(), + is_service_transaction: false, + local_address: Default::default(), + max_transaction_size: MAX_TRANSACTION_SIZE, + } + } } impl TestClient { - pub fn new() -> Self { - TestClient::default() - } - - pub fn with_balance>(mut self, balance: T) -> Self { - self.account_details.balance = balance.into(); - self - } - - pub fn with_nonce>(mut self, nonce: T) -> Self { - self.account_details.nonce = nonce.into(); - self - } - - pub fn with_gas_required>(mut self, gas_required: T) -> Self { - self.gas_required = gas_required.into(); - self - } - - pub fn with_local(mut self, address: &Address) -> Self { - self.local_address = *address; - self - } - - pub fn with_service_transaction(mut self) -> Self { - self.is_service_transaction = true; - self - } - - pub fn verify>(&self, tx: T) -> pool::VerifiedTransaction { - let tx = tx.into(); - pool::VerifiedTransaction { - hash: tx.hash(), - sender: tx.sender(), - priority: pool::Priority::Regular, - transaction: tx, - insertion_id: 1, - } - } - - pub fn was_verification_triggered(&self) -> bool { - self.verification_invoked.load(atomic::Ordering::SeqCst) - } + pub fn new() -> Self { + TestClient::default() + } + + pub fn with_balance>(mut self, balance: T) -> Self { + self.account_details.balance = balance.into(); + self + } + + pub fn with_nonce>(mut self, nonce: T) -> Self { + self.account_details.nonce = nonce.into(); + self + } + + pub fn with_gas_required>(mut self, gas_required: T) -> Self { + self.gas_required = gas_required.into(); + self + } + + pub fn with_local(mut self, address: &Address) -> Self { + self.local_address = *address; + self + } + + pub fn with_service_transaction(mut self) -> Self { + self.is_service_transaction = true; + self + } + + pub fn verify>( + &self, + tx: T, + ) -> pool::VerifiedTransaction { + let tx = tx.into(); + pool::VerifiedTransaction { + hash: tx.hash(), + sender: tx.sender(), + priority: pool::Priority::Regular, + transaction: tx, + insertion_id: 1, + } + } + + pub fn was_verification_triggered(&self) -> bool { + self.verification_invoked.load(atomic::Ordering::SeqCst) + } } impl pool::client::Client for TestClient { - fn transaction_already_included(&self, _hash: &H256) -> bool { - false - } - - fn verify_transaction(&self, tx: UnverifiedTransaction) - -> Result - { - self.verification_invoked.store(true, atomic::Ordering::SeqCst); - Ok(SignedTransaction::new(tx)?) - } - - fn account_details(&self, address: &Address) -> AccountDetails { - let mut details = self.account_details.clone(); - if address == &self.local_address { - details.is_local = true; - } - - details - } - - fn required_gas(&self, _tx: &Transaction) -> U256 { - self.gas_required - } - - fn transaction_type(&self, _tx: &SignedTransaction) -> pool::client::TransactionType { - if self.is_service_transaction { - pool::client::TransactionType::Service - } else { - pool::client::TransactionType::Regular - } - } - - fn decode_transaction(&self, transaction: &[u8]) -> Result { - let rlp = Rlp::new(&transaction); - if rlp.as_raw().len() > self.max_transaction_size { - return Err(transaction::Error::TooBig) - } - rlp.as_val().map_err(|e| transaction::Error::InvalidRlp(e.to_string())) - } - + fn transaction_already_included(&self, _hash: &H256) -> bool { + false + } + + fn verify_transaction_basic( + &self, + _tx: &UnverifiedTransaction, + ) -> Result<(), transaction::Error> { + Ok(()) + } + + fn verify_transaction( + &self, + tx: UnverifiedTransaction, + ) -> Result { + self.verification_invoked + .store(true, atomic::Ordering::SeqCst); + Ok(SignedTransaction::new(tx)?) + } + + fn account_details(&self, address: &Address) -> AccountDetails { + let mut details = self.account_details.clone(); + if address == &self.local_address { + details.is_local = true; + } + + details + } + + fn required_gas(&self, _tx: &Transaction) -> U256 { + self.gas_required + } + + fn transaction_type(&self, _tx: &SignedTransaction) -> pool::client::TransactionType { + if self.is_service_transaction { + pool::client::TransactionType::Service + } else { + pool::client::TransactionType::Regular + } + } + + fn decode_transaction( + &self, + transaction: &[u8], + ) -> Result { + let rlp = Rlp::new(&transaction); + if rlp.as_raw().len() > self.max_transaction_size { + return Err(transaction::Error::TooBig); + } + rlp.as_val() + .map_err(|e| transaction::Error::InvalidRlp(e.to_string())) + } } impl pool::client::NonceClient for TestClient { - fn account_nonce(&self, _address: &Address) -> U256 { - self.account_details.nonce - } + fn account_nonce(&self, _address: &Address) -> U256 { + self.account_details.nonce + } } diff --git a/miner/src/pool/tests/mod.rs b/miner/src/pool/tests/mod.rs index 0eb223dba00..fb25d66ac98 100644 --- a/miner/src/pool/tests/mod.rs +++ b/miner/src/pool/tests/mod.rs @@ -1,30 +1,32 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use ethereum_types::U256; -use types::transaction::{self, PendingTransaction}; use txpool; +use types::transaction::{self, PendingTransaction}; -use pool::{verifier, TransactionQueue, PrioritizationStrategy, PendingSettings, PendingOrdering}; +use pool::{verifier, PendingOrdering, PendingSettings, PrioritizationStrategy, TransactionQueue}; -pub mod tx; pub mod client; +pub mod tx; -use self::tx::{Tx, TxExt, PairExt}; -use self::client::TestClient; +use self::{ + client::TestClient, + tx::{PairExt, Tx, TxExt}, +}; // max mem for 3 transaction, this is relative // to the global use allocator, the value is currently @@ -33,1044 +35,1181 @@ use self::client::TestClient; const TEST_QUEUE_MAX_MEM: usize = 100; fn new_queue() -> TransactionQueue { - TransactionQueue::new( - txpool::Options { - max_count: 3, - max_per_sender: 3, - max_mem_usage: TEST_QUEUE_MAX_MEM - }, - verifier::Options { - minimal_gas_price: 1.into(), - block_gas_limit: 1_000_000.into(), - tx_gas_limit: 1_000_000.into(), - no_early_reject: false, - }, - PrioritizationStrategy::GasPriceOnly, - ) + TransactionQueue::new( + txpool::Options { + max_count: 3, + max_per_sender: 3, + max_mem_usage: TEST_QUEUE_MAX_MEM, + }, + verifier::Options { + minimal_gas_price: 1.into(), + block_gas_limit: 1_000_000.into(), + tx_gas_limit: 1_000_000.into(), + no_early_reject: false, + }, + PrioritizationStrategy::GasPriceOnly, + ) } #[test] fn should_return_correct_nonces_when_dropped_because_of_limit() { - // given - let txq = TransactionQueue::new( - txpool::Options { - max_count: 3, - max_per_sender: 1, - max_mem_usage: TEST_QUEUE_MAX_MEM - }, - verifier::Options { - minimal_gas_price: 1.into(), - block_gas_limit: 1_000_000.into(), - tx_gas_limit: 1_000_000.into(), - no_early_reject: false, - }, - PrioritizationStrategy::GasPriceOnly, - ); - let (tx1, tx2) = Tx::gas_price(2).signed_pair(); - let sender = tx1.sender(); - let nonce = tx1.nonce; - - // when - let r1 = txq.import(TestClient::new(), vec![tx1].retracted()); - let r2 = txq.import(TestClient::new(), vec![tx2].retracted()); - assert_eq!(r1, vec![Ok(())]); - assert_eq!(r2, vec![Err(transaction::Error::LimitReached)]); - assert_eq!(txq.status().status.transaction_count, 1); - - // then - assert_eq!(txq.next_nonce(TestClient::new(), &sender), Some(nonce + 1)); - - // when - let tx1 = Tx::gas_price(2).signed(); - let tx2 = Tx::gas_price(2).signed(); - let sender = tx2.sender(); - let tx3 = Tx::gas_price(1).signed(); - let tx4 = Tx::gas_price(3).signed(); - let res = txq.import(TestClient::new(), vec![tx1, tx2].retracted()); - let res2 = txq.import(TestClient::new(), vec![tx3, tx4].retracted()); - - // then - assert_eq!(res, vec![Ok(()), Ok(())]); - assert_eq!(res2, vec![ - // The error here indicates reaching the limit - // and minimal effective gas price taken into account. - Err(transaction::Error::InsufficientGasPrice { minimal: 2.into(), got: 1.into() }), - Ok(()) - ]); - assert_eq!(txq.status().status.transaction_count, 3); - // tx2 transaction got dropped because of limit - // tx1 and tx1' are kept, because they have lower insertion_ids so they are preferred. - assert_eq!(txq.next_nonce(TestClient::new(), &sender), None); + // given + let txq = TransactionQueue::new( + txpool::Options { + max_count: 3, + max_per_sender: 1, + max_mem_usage: TEST_QUEUE_MAX_MEM, + }, + verifier::Options { + minimal_gas_price: 1.into(), + block_gas_limit: 1_000_000.into(), + tx_gas_limit: 1_000_000.into(), + no_early_reject: false, + }, + PrioritizationStrategy::GasPriceOnly, + ); + let (tx1, tx2) = Tx::gas_price(2).signed_pair(); + let sender = tx1.sender(); + let nonce = tx1.nonce; + + // when + let r1 = txq.import(TestClient::new(), vec![tx1].retracted()); + let r2 = txq.import(TestClient::new(), vec![tx2].retracted()); + assert_eq!(r1, vec![Ok(())]); + assert_eq!(r2, vec![Err(transaction::Error::LimitReached)]); + assert_eq!(txq.status().status.transaction_count, 1); + + // then + assert_eq!(txq.next_nonce(TestClient::new(), &sender), Some(nonce + 1)); + + // when + let tx1 = Tx::gas_price(2).signed(); + let tx2 = Tx::gas_price(2).signed(); + let sender = tx2.sender(); + let tx3 = Tx::gas_price(1).signed(); + let tx4 = Tx::gas_price(3).signed(); + let res = txq.import(TestClient::new(), vec![tx1, tx2].retracted()); + let res2 = txq.import(TestClient::new(), vec![tx3, tx4].retracted()); + + // then + assert_eq!(res, vec![Ok(()), Ok(())]); + assert_eq!( + res2, + vec![ + // The error here indicates reaching the limit + // and minimal effective gas price taken into account. + Err(transaction::Error::TooCheapToReplace { + prev: Some(2.into()), + new: Some(1.into()) + }), + Ok(()) + ] + ); + assert_eq!(txq.status().status.transaction_count, 3); + // tx2 transaction got dropped because of limit + // tx1 and tx1' are kept, because they have lower insertion_ids so they are preferred. + assert_eq!(txq.next_nonce(TestClient::new(), &sender), None); } #[test] fn should_never_drop_local_transactions_from_different_senders() { - // given - let txq = TransactionQueue::new( - txpool::Options { - max_count: 3, - max_per_sender: 1, - max_mem_usage: TEST_QUEUE_MAX_MEM - }, - verifier::Options { - minimal_gas_price: 1.into(), - block_gas_limit: 1_000_000.into(), - tx_gas_limit: 1_000_000.into(), - no_early_reject: false, - }, - PrioritizationStrategy::GasPriceOnly, - ); - let (tx1, tx2) = Tx::gas_price(2).signed_pair(); - let sender = tx1.sender(); - let nonce = tx1.nonce; - - // when - let r1 = txq.import(TestClient::new(), vec![tx1].local()); - let r2 = txq.import(TestClient::new(), vec![tx2].local()); - assert_eq!(r1, vec![Ok(())]); - assert_eq!(r2, vec![Ok(())]); - assert_eq!(txq.status().status.transaction_count, 2); - - // then - assert_eq!(txq.next_nonce(TestClient::new(), &sender), Some(nonce + 2)); - - // when - let tx1 = Tx::gas_price(2).signed(); - let tx2 = Tx::gas_price(2).signed(); - let tx3 = Tx::gas_price(1).signed(); - let tx4 = Tx::gas_price(3).signed(); - let res = txq.import(TestClient::new(), vec![tx1, tx2].local()); - let res2 = txq.import(TestClient::new(), vec![tx3, tx4].local()); - - // then - assert_eq!(res, vec![Ok(()), Ok(())]); - assert_eq!(res2, vec![Ok(()), Ok(())]); - assert_eq!(txq.status().status.transaction_count, 6); - assert_eq!(txq.next_nonce(TestClient::new(), &sender), Some(nonce + 2)); + // given + let txq = TransactionQueue::new( + txpool::Options { + max_count: 3, + max_per_sender: 1, + max_mem_usage: TEST_QUEUE_MAX_MEM, + }, + verifier::Options { + minimal_gas_price: 1.into(), + block_gas_limit: 1_000_000.into(), + tx_gas_limit: 1_000_000.into(), + no_early_reject: false, + }, + PrioritizationStrategy::GasPriceOnly, + ); + let (tx1, tx2) = Tx::gas_price(2).signed_pair(); + let sender = tx1.sender(); + let nonce = tx1.nonce; + + // when + let r1 = txq.import(TestClient::new(), vec![tx1].local()); + let r2 = txq.import(TestClient::new(), vec![tx2].local()); + assert_eq!(r1, vec![Ok(())]); + assert_eq!(r2, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 2); + + // then + assert_eq!(txq.next_nonce(TestClient::new(), &sender), Some(nonce + 2)); + + // when + let tx1 = Tx::gas_price(2).signed(); + let tx2 = Tx::gas_price(2).signed(); + let tx3 = Tx::gas_price(1).signed(); + let tx4 = Tx::gas_price(3).signed(); + let res = txq.import(TestClient::new(), vec![tx1, tx2].local()); + let res2 = txq.import(TestClient::new(), vec![tx3, tx4].local()); + + // then + assert_eq!(res, vec![Ok(()), Ok(())]); + assert_eq!(res2, vec![Ok(()), Ok(())]); + assert_eq!(txq.status().status.transaction_count, 6); + assert_eq!(txq.next_nonce(TestClient::new(), &sender), Some(nonce + 2)); } #[test] fn should_handle_same_transaction_imported_twice_with_different_state_nonces() { - // given - let txq = new_queue(); - let (tx, tx2) = Tx::default().signed_replacement(); - let hash = tx2.hash(); - let client = TestClient::new().with_nonce(122); - - // First insert one transaction to future - let res = txq.import(client.clone(), vec![tx].local()); - assert_eq!(res, vec![Ok(())]); - // next_nonce === None -> transaction is in future - assert_eq!(txq.next_nonce(client.clone(), &tx2.sender()), None); - - // now import second transaction to current - let res = txq.import(TestClient::new(), vec![tx2.local()]); - - // and then there should be only one transaction in current (the one with higher gas_price) - assert_eq!(res, vec![Ok(())]); - assert_eq!(txq.status().status.transaction_count, 1); - let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); - assert_eq!(top[0].hash, hash); + // given + let txq = new_queue(); + let (tx, tx2) = Tx::default().signed_replacement(); + let hash = tx2.hash(); + let client = TestClient::new().with_nonce(122); + + // First insert one transaction to future + let res = txq.import(client.clone(), vec![tx].local()); + assert_eq!(res, vec![Ok(())]); + // next_nonce === None -> transaction is in future + assert_eq!(txq.next_nonce(client.clone(), &tx2.sender()), None); + + // now import second transaction to current + let res = txq.import(TestClient::new(), vec![tx2.local()]); + + // and then there should be only one transaction in current (the one with higher gas_price) + assert_eq!(res, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 1); + let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); + assert_eq!(top[0].hash, hash); } #[test] fn should_move_all_transactions_from_future() { - // given - let txq = new_queue(); - let txs = Tx::default().signed_pair(); - let (hash, hash2) = txs.hash(); - let (tx, tx2) = txs; - let client = TestClient::new().with_nonce(122); - - // First insert one transaction to future - let res = txq.import(client.clone(), vec![tx.local()]); - assert_eq!(res, vec![Ok(())]); - // next_nonce === None -> transaction is in future - assert_eq!(txq.next_nonce(client.clone(), &tx2.sender()), None); - - // now import second transaction to current - let res = txq.import(client.clone(), vec![tx2.local()]); - - // then - assert_eq!(res, vec![Ok(())]); - assert_eq!(txq.status().status.transaction_count, 2); - let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); - assert_eq!(top[0].hash, hash); - assert_eq!(top[1].hash, hash2); + // given + let txq = new_queue(); + let txs = Tx::default().signed_pair(); + let (hash, hash2) = txs.hash(); + let (tx, tx2) = txs; + let client = TestClient::new().with_nonce(122); + + // First insert one transaction to future + let res = txq.import(client.clone(), vec![tx.local()]); + assert_eq!(res, vec![Ok(())]); + // next_nonce === None -> transaction is in future + assert_eq!(txq.next_nonce(client.clone(), &tx2.sender()), None); + + // now import second transaction to current + let res = txq.import(client.clone(), vec![tx2.local()]); + + // then + assert_eq!(res, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 2); + let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); + assert_eq!(top[0].hash, hash); + assert_eq!(top[1].hash, hash2); } #[test] fn should_drop_transactions_from_senders_without_balance() { - // given - let txq = new_queue(); - let tx = Tx::default().signed(); - let client = TestClient::new().with_balance(1); - - // when - let res = txq.import(client, vec![tx.local()]); - - // then - assert_eq!(res, vec![Err(transaction::Error::InsufficientBalance { - balance: U256::from(1), - cost: U256::from(21_100), - })]); - assert_eq!(txq.status().status.transaction_count, 0); + // given + let txq = new_queue(); + let tx = Tx::default().signed(); + let client = TestClient::new().with_balance(1); + + // when + let res = txq.import(client, vec![tx.local()]); + + // then + assert_eq!( + res, + vec![Err(transaction::Error::InsufficientBalance { + balance: U256::from(1), + cost: U256::from(21_100), + })] + ); + assert_eq!(txq.status().status.transaction_count, 0); } #[test] fn should_not_import_transaction_below_min_gas_price_threshold_if_external() { - // given - let txq = new_queue(); - let tx = Tx::default(); - txq.set_verifier_options(verifier::Options { - minimal_gas_price: 3.into(), - ..Default::default() - }); - - // when - let res = txq.import(TestClient::new(), vec![tx.signed().unverified()]); - - // then - assert_eq!(res, vec![Err(transaction::Error::InsufficientGasPrice { - minimal: U256::from(3), - got: U256::from(1), - })]); - assert_eq!(txq.status().status.transaction_count, 0); + // given + let txq = new_queue(); + let tx = Tx::default(); + txq.set_verifier_options(verifier::Options { + minimal_gas_price: 3.into(), + ..Default::default() + }); + + // when + let res = txq.import(TestClient::new(), vec![tx.signed().unverified()]); + + // then + assert_eq!( + res, + vec![Err(transaction::Error::InsufficientGasPrice { + minimal: U256::from(3), + got: U256::from(1), + })] + ); + assert_eq!(txq.status().status.transaction_count, 0); } #[test] fn should_import_transaction_below_min_gas_price_threshold_if_local() { - // given - let txq = new_queue(); - let tx = Tx::default(); - txq.set_verifier_options(verifier::Options { - minimal_gas_price: 3.into(), - ..Default::default() - }); - - // when - let res = txq.import(TestClient::new(), vec![tx.signed().local()]); - - // then - assert_eq!(res, vec![Ok(())]); - assert_eq!(txq.status().status.transaction_count, 1); + // given + let txq = new_queue(); + let tx = Tx::default(); + txq.set_verifier_options(verifier::Options { + minimal_gas_price: 3.into(), + ..Default::default() + }); + + // when + let res = txq.import(TestClient::new(), vec![tx.signed().local()]); + + // then + assert_eq!(res, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 1); } #[test] fn should_import_txs_from_same_sender() { - // given - let txq = new_queue(); + // given + let txq = new_queue(); - let txs = Tx::default().signed_pair(); - let (hash, hash2) = txs.hash(); + let txs = Tx::default().signed_pair(); + let (hash, hash2) = txs.hash(); - // when - txq.import(TestClient::new(), txs.local().into_vec()); + // when + txq.import(TestClient::new(), txs.local().into_vec()); - // then - let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0 ,0)); - assert_eq!(top[0].hash, hash); - assert_eq!(top[1].hash, hash2); - assert_eq!(top.len(), 2); + // then + let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); + assert_eq!(top[0].hash, hash); + assert_eq!(top[1].hash, hash2); + assert_eq!(top.len(), 2); } #[test] fn should_prioritize_local_transactions_within_same_nonce_height() { - // given - let txq = new_queue(); - let tx = Tx::default().signed(); - // the second one has same nonce but higher `gas_price` - let tx2 = Tx::gas_price(2).signed(); - let (hash, hash2) = (tx.hash(), tx2.hash()); - let client = TestClient::new().with_local(&tx.sender()); - - // when - // first insert the one with higher gas price - let res = txq.import(client.clone(), vec![tx.local(), tx2.unverified()]); - assert_eq!(res, vec![Ok(()), Ok(())]); - - // then - let top = txq.pending(client, PendingSettings::all_prioritized(0, 0)); - assert_eq!(top[0].hash, hash); // local should be first - assert_eq!(top[1].hash, hash2); - assert_eq!(top.len(), 2); + // given + let txq = new_queue(); + let tx = Tx::default().signed(); + // the second one has same nonce but higher `gas_price` + let tx2 = Tx::gas_price(2).signed(); + let (hash, hash2) = (tx.hash(), tx2.hash()); + let client = TestClient::new().with_local(&tx.sender()); + + // when + // first insert the one with higher gas price + let res = txq.import(client.clone(), vec![tx.local(), tx2.unverified()]); + assert_eq!(res, vec![Ok(()), Ok(())]); + + // then + let top = txq.pending(client, PendingSettings::all_prioritized(0, 0)); + assert_eq!(top[0].hash, hash); // local should be first + assert_eq!(top[1].hash, hash2); + assert_eq!(top.len(), 2); } #[test] fn should_prioritize_reimported_transactions_within_same_nonce_height() { - // given - let txq = new_queue(); - let tx = Tx::default().signed(); - // the second one has same nonce but higher `gas_price` - let tx2 = Tx::gas_price(2).signed(); - let (hash, hash2) = (tx.hash(), tx2.hash()); - - // when - // first insert local one with higher gas price - // then the one with lower gas price, but from retracted block - let res = txq.import(TestClient::new(), vec![tx2.unverified(), tx.retracted()]); - assert_eq!(res, vec![Ok(()), Ok(())]); - - // then - let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); - assert_eq!(top[0].hash, hash); // retracted should be first - assert_eq!(top[1].hash, hash2); - assert_eq!(top.len(), 2); + // given + let txq = new_queue(); + let tx = Tx::default().signed(); + // the second one has same nonce but higher `gas_price` + let tx2 = Tx::gas_price(2).signed(); + let (hash, hash2) = (tx.hash(), tx2.hash()); + + // when + // first insert local one with higher gas price + // then the one with lower gas price, but from retracted block + let res = txq.import(TestClient::new(), vec![tx2.unverified(), tx.retracted()]); + assert_eq!(res, vec![Ok(()), Ok(())]); + + // then + let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); + assert_eq!(top[0].hash, hash); // retracted should be first + assert_eq!(top[1].hash, hash2); + assert_eq!(top.len(), 2); } #[test] fn should_not_prioritize_local_transactions_with_different_nonce_height() { - // given - let txq = new_queue(); - let txs = Tx::default().signed_pair(); - let (hash, hash2) = txs.hash(); - let (tx, tx2) = txs; - - // when - let res = txq.import(TestClient::new(), vec![tx.unverified(), tx2.local()]); - assert_eq!(res, vec![Ok(()), Ok(())]); - - // then - let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); - assert_eq!(top[0].hash, hash); - assert_eq!(top[1].hash, hash2); - assert_eq!(top.len(), 2); + // given + let txq = new_queue(); + let txs = Tx::default().signed_pair(); + let (hash, hash2) = txs.hash(); + let (tx, tx2) = txs; + + // when + let res = txq.import(TestClient::new(), vec![tx.unverified(), tx2.local()]); + assert_eq!(res, vec![Ok(()), Ok(())]); + + // then + let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); + assert_eq!(top[0].hash, hash); + assert_eq!(top[1].hash, hash2); + assert_eq!(top.len(), 2); } #[test] fn should_put_transaction_to_futures_if_gap_detected() { - // given - let txq = new_queue(); - let (tx, _, tx2) = Tx::default().signed_triple(); - let hash = tx.hash(); - - // when - let res = txq.import(TestClient::new(), vec![tx, tx2].local()); - - // then - assert_eq!(res, vec![Ok(()), Ok(())]); - let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); - assert_eq!(top.len(), 1); - assert_eq!(top[0].hash, hash); + // given + let txq = new_queue(); + let (tx, _, tx2) = Tx::default().signed_triple(); + let hash = tx.hash(); + + // when + let res = txq.import(TestClient::new(), vec![tx, tx2].local()); + + // then + assert_eq!(res, vec![Ok(()), Ok(())]); + let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); + assert_eq!(top.len(), 1); + assert_eq!(top[0].hash, hash); } #[test] fn should_handle_min_block() { - // given - let txq = new_queue(); - - let (tx, tx2) = Tx::default().signed_pair(); - - // when - let res = txq.import(TestClient::new(), vec![ - verifier::Transaction::Local(PendingTransaction::new(tx, transaction::Condition::Number(1).into())), - tx2.local() - ]); - assert_eq!(res, vec![Ok(()), Ok(())]); - - // then - let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); - assert_eq!(top.len(), 0); - let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(1, 0)); - assert_eq!(top.len(), 2); + // given + let txq = new_queue(); + + let (tx, tx2) = Tx::default().signed_pair(); + + // when + let res = txq.import( + TestClient::new(), + vec![ + verifier::Transaction::Local(PendingTransaction::new( + tx, + transaction::Condition::Number(1).into(), + )), + tx2.local(), + ], + ); + assert_eq!(res, vec![Ok(()), Ok(())]); + + // then + let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); + assert_eq!(top.len(), 0); + let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(1, 0)); + assert_eq!(top.len(), 2); } #[test] fn should_correctly_update_futures_when_removing() { - // given - let txq = new_queue(); - let txs= Tx::default().signed_pair(); + // given + let txq = new_queue(); + let txs = Tx::default().signed_pair(); - let res = txq.import(TestClient::new().with_nonce(121), txs.local().into_vec()); - assert_eq!(res, vec![Ok(()), Ok(())]); - assert_eq!(txq.status().status.transaction_count, 2); + let res = txq.import(TestClient::new().with_nonce(121), txs.local().into_vec()); + assert_eq!(res, vec![Ok(()), Ok(())]); + assert_eq!(txq.status().status.transaction_count, 2); - // when - txq.cull(TestClient::new().with_nonce(125)); - // should remove both transactions since they are stalled + // when + txq.cull(TestClient::new().with_nonce(125)); + // should remove both transactions since they are stalled - // then - assert_eq!(txq.status().status.transaction_count, 0); + // then + assert_eq!(txq.status().status.transaction_count, 0); } #[test] fn should_move_transactions_if_gap_filled() { - // given - let txq = new_queue(); - let (tx, tx1, tx2) = Tx::default().signed_triple(); - - let res = txq.import(TestClient::new(), vec![tx, tx2].local()); - assert_eq!(res, vec![Ok(()), Ok(())]); - assert_eq!(txq.status().status.transaction_count, 2); - assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 1); - - // when - let res = txq.import(TestClient::new(), vec![tx1.local()]); - assert_eq!(res, vec![Ok(())]); - - // then - assert_eq!(txq.status().status.transaction_count, 3); - assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 3); + // given + let txq = new_queue(); + let (tx, tx1, tx2) = Tx::default().signed_triple(); + + let res = txq.import(TestClient::new(), vec![tx, tx2].local()); + assert_eq!(res, vec![Ok(()), Ok(())]); + assert_eq!(txq.status().status.transaction_count, 2); + assert_eq!( + txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)) + .len(), + 1 + ); + + // when + let res = txq.import(TestClient::new(), vec![tx1.local()]); + assert_eq!(res, vec![Ok(())]); + + // then + assert_eq!(txq.status().status.transaction_count, 3); + assert_eq!( + txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)) + .len(), + 3 + ); } #[test] fn should_remove_transaction() { - // given - let txq = new_queue(); - let (tx, _, tx2) = Tx::default().signed_triple(); - - let res = txq.import(TestClient::default(), vec![tx, tx2].local()); - assert_eq!(res, vec![Ok(()), Ok(())]); - assert_eq!(txq.status().status.transaction_count, 2); - assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 1); - - // when - txq.cull(TestClient::new().with_nonce(124)); - assert_eq!(txq.status().status.transaction_count, 1); - assert_eq!(txq.pending(TestClient::new().with_nonce(125), PendingSettings::all_prioritized(0, 0)).len(), 1); - txq.cull(TestClient::new().with_nonce(126)); - - // then - assert_eq!(txq.status().status.transaction_count, 0); + // given + let txq = new_queue(); + let (tx, _, tx2) = Tx::default().signed_triple(); + + let res = txq.import(TestClient::default(), vec![tx, tx2].local()); + assert_eq!(res, vec![Ok(()), Ok(())]); + assert_eq!(txq.status().status.transaction_count, 2); + assert_eq!( + txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)) + .len(), + 1 + ); + + // when + txq.cull(TestClient::new().with_nonce(124)); + assert_eq!(txq.status().status.transaction_count, 1); + assert_eq!( + txq.pending( + TestClient::new().with_nonce(125), + PendingSettings::all_prioritized(0, 0) + ) + .len(), + 1 + ); + txq.cull(TestClient::new().with_nonce(126)); + + // then + assert_eq!(txq.status().status.transaction_count, 0); } #[test] fn should_move_transactions_to_future_if_gap_introduced() { - // given - let txq = new_queue(); - let (tx, tx2) = Tx::default().signed_pair(); - let hash = tx.hash(); - let tx3 = Tx::default().signed(); - - let res = txq.import(TestClient::new(), vec![tx3, tx2].local()); - assert_eq!(res, vec![Ok(()), Ok(())]); - assert_eq!(txq.status().status.transaction_count, 2); - assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 1); - - let res = txq.import(TestClient::new(), vec![tx].local()); - assert_eq!(res, vec![Ok(())]); - assert_eq!(txq.status().status.transaction_count, 3); - assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 3); - - // when - txq.remove(vec![&hash], true); - - // then - assert_eq!(txq.status().status.transaction_count, 2); - assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 1); + // given + let txq = new_queue(); + let (tx, tx2) = Tx::default().signed_pair(); + let hash = tx.hash(); + let tx3 = Tx::default().signed(); + + let res = txq.import(TestClient::new(), vec![tx3, tx2].local()); + assert_eq!(res, vec![Ok(()), Ok(())]); + assert_eq!(txq.status().status.transaction_count, 2); + assert_eq!( + txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)) + .len(), + 1 + ); + + let res = txq.import(TestClient::new(), vec![tx].local()); + assert_eq!(res, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 3); + assert_eq!( + txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)) + .len(), + 3 + ); + + // when + txq.remove(vec![&hash], true); + + // then + assert_eq!(txq.status().status.transaction_count, 2); + assert_eq!( + txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)) + .len(), + 1 + ); } #[test] fn should_clear_queue() { - // given - let txq = new_queue(); - let txs = Tx::default().signed_pair(); + // given + let txq = new_queue(); + let txs = Tx::default().signed_pair(); - // add - txq.import(TestClient::new(), txs.local().into_vec()); - assert_eq!(txq.status().status.transaction_count, 2); + // add + txq.import(TestClient::new(), txs.local().into_vec()); + assert_eq!(txq.status().status.transaction_count, 2); - // when - txq.clear(); + // when + txq.clear(); - // then - assert_eq!(txq.status().status.transaction_count, 0); + // then + assert_eq!(txq.status().status.transaction_count, 0); } #[test] fn should_prefer_current_transactions_when_hitting_the_limit() { - // given - let txq = TransactionQueue::new( - txpool::Options { - max_count: 1, - max_per_sender: 2, - max_mem_usage: TEST_QUEUE_MAX_MEM - }, - verifier::Options { - minimal_gas_price: 1.into(), - block_gas_limit: 1_000_000.into(), - tx_gas_limit: 1_000_000.into(), - no_early_reject: false, - }, - PrioritizationStrategy::GasPriceOnly, - ); - let (tx, tx2) = Tx::default().signed_pair(); - let hash = tx.hash(); - let sender = tx.sender(); - - let res = txq.import(TestClient::new(), vec![tx2.unverified()]); - assert_eq!(res, vec![Ok(())]); - assert_eq!(txq.status().status.transaction_count, 1); - - // when - let res = txq.import(TestClient::new(), vec![tx.unverified()]); - - // then - assert_eq!(res, vec![Ok(())]); - assert_eq!(txq.status().status.transaction_count, 1); - - let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); - assert_eq!(top.len(), 1); - assert_eq!(top[0].hash, hash); - assert_eq!(txq.next_nonce(TestClient::new(), &sender), Some(124.into())); + // given + let txq = TransactionQueue::new( + txpool::Options { + max_count: 1, + max_per_sender: 2, + max_mem_usage: TEST_QUEUE_MAX_MEM, + }, + verifier::Options { + minimal_gas_price: 1.into(), + block_gas_limit: 1_000_000.into(), + tx_gas_limit: 1_000_000.into(), + no_early_reject: false, + }, + PrioritizationStrategy::GasPriceOnly, + ); + let (tx, tx2) = Tx::default().signed_pair(); + let hash = tx.hash(); + let sender = tx.sender(); + + let res = txq.import(TestClient::new(), vec![tx2.unverified()]); + assert_eq!(res, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 1); + + // when + let res = txq.import(TestClient::new(), vec![tx.unverified()]); + + // then + assert_eq!(res, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 1); + + let top = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); + assert_eq!(top.len(), 1); + assert_eq!(top[0].hash, hash); + assert_eq!(txq.next_nonce(TestClient::new(), &sender), Some(124.into())); } #[test] fn should_drop_transactions_with_old_nonces() { - let txq = new_queue(); - let tx = Tx::default().signed(); + let txq = new_queue(); + let tx = Tx::default().signed(); - // when - let res = txq.import(TestClient::new().with_nonce(125), vec![tx.unverified()]); + // when + let res = txq.import(TestClient::new().with_nonce(125), vec![tx.unverified()]); - // then - assert_eq!(res, vec![Err(transaction::Error::Old)]); - assert_eq!(txq.status().status.transaction_count, 0); + // then + assert_eq!(res, vec![Err(transaction::Error::Old)]); + assert_eq!(txq.status().status.transaction_count, 0); } #[test] fn should_not_insert_same_transaction_twice() { - // given - let txq = new_queue(); - let (_tx1, tx2) = Tx::default().signed_pair(); - let res = txq.import(TestClient::new(), vec![tx2.clone().local()]); - assert_eq!(res, vec![Ok(())]); - assert_eq!(txq.status().status.transaction_count, 1); - - // when - let res = txq.import(TestClient::new(), vec![tx2.local()]); - - // then - assert_eq!(res, vec![Err(transaction::Error::AlreadyImported)]); - assert_eq!(txq.status().status.transaction_count, 1); + // given + let txq = new_queue(); + let (_tx1, tx2) = Tx::default().signed_pair(); + let res = txq.import(TestClient::new(), vec![tx2.clone().local()]); + assert_eq!(res, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 1); + + // when + let res = txq.import(TestClient::new(), vec![tx2.local()]); + + // then + assert_eq!(res, vec![Err(transaction::Error::AlreadyImported)]); + assert_eq!(txq.status().status.transaction_count, 1); } #[test] fn should_accept_same_transaction_twice_if_removed() { - // given - let txq = new_queue(); - let txs = Tx::default().signed_pair(); - let (tx1, _) = txs.clone(); - let (hash, _) = txs.hash(); - - let res = txq.import(TestClient::new(), txs.local().into_vec()); - assert_eq!(res, vec![Ok(()), Ok(())]); - assert_eq!(txq.status().status.transaction_count, 2); - assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 2); - - // when - txq.remove(vec![&hash], true); - assert_eq!(txq.status().status.transaction_count, 1); - assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 0); - - let res = txq.import(TestClient::new(), vec![tx1].local()); - assert_eq!(res, vec![Ok(())]); - - // then - assert_eq!(txq.status().status.transaction_count, 2); - assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 2); + // given + let txq = new_queue(); + let txs = Tx::default().signed_pair(); + let (tx1, _) = txs.clone(); + let (hash, _) = txs.hash(); + + let res = txq.import(TestClient::new(), txs.local().into_vec()); + assert_eq!(res, vec![Ok(()), Ok(())]); + assert_eq!(txq.status().status.transaction_count, 2); + assert_eq!( + txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)) + .len(), + 2 + ); + + // when + txq.remove(vec![&hash], true); + assert_eq!(txq.status().status.transaction_count, 1); + assert_eq!( + txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)) + .len(), + 0 + ); + + let res = txq.import(TestClient::new(), vec![tx1].local()); + assert_eq!(res, vec![Ok(())]); + + // then + assert_eq!(txq.status().status.transaction_count, 2); + assert_eq!( + txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)) + .len(), + 2 + ); } #[test] fn should_not_replace_same_transaction_if_the_fee_is_less_than_minimal_bump() { - // given - let txq = new_queue(); - let (tx, tx2) = Tx::gas_price(20).signed_replacement(); - let (tx3, tx4) = Tx::gas_price(1).signed_replacement(); - let client = TestClient::new().with_balance(1_000_000); - - // when - let res = txq.import(client.clone(), vec![tx, tx3].local()); - assert_eq!(res, vec![Ok(()), Ok(())]); - - let res = txq.import(client.clone(), vec![tx2, tx4].local()); - - // then - assert_eq!(res, vec![Err(transaction::Error::TooCheapToReplace), Ok(())]); - assert_eq!(txq.status().status.transaction_count, 2); - assert_eq!(txq.pending(client.clone(), PendingSettings::all_prioritized(0, 0))[0].signed().gas_price, U256::from(20)); - assert_eq!(txq.pending(client.clone(), PendingSettings::all_prioritized(0, 0))[1].signed().gas_price, U256::from(2)); + // given + let txq = new_queue(); + let (tx, tx2) = Tx::gas_price(20).signed_replacement(); + let (tx3, tx4) = Tx::gas_price(1).signed_replacement(); + let client = TestClient::new().with_balance(1_000_000); + + // when + let res = txq.import(client.clone(), vec![tx, tx3].local()); + assert_eq!(res, vec![Ok(()), Ok(())]); + + let res = txq.import(client.clone(), vec![tx2, tx4].local()); + + // then + assert_eq!( + res, + vec![ + Err(transaction::Error::TooCheapToReplace { + prev: None, + new: None + }), + Ok(()) + ] + ); + assert_eq!(txq.status().status.transaction_count, 2); + assert_eq!( + txq.pending(client.clone(), PendingSettings::all_prioritized(0, 0))[0] + .signed() + .gas_price, + U256::from(20) + ); + assert_eq!( + txq.pending(client.clone(), PendingSettings::all_prioritized(0, 0))[1] + .signed() + .gas_price, + U256::from(2) + ); } #[test] fn should_return_none_when_transaction_from_given_address_does_not_exist() { - // given - let txq = new_queue(); + // given + let txq = new_queue(); - // then - assert_eq!(txq.next_nonce(TestClient::new(), &Default::default()), None); + // then + assert_eq!(txq.next_nonce(TestClient::new(), &Default::default()), None); } #[test] fn should_return_correct_nonce_when_transactions_from_given_address_exist() { - // given - let txq = new_queue(); - let tx = Tx::default().signed(); - let from = tx.sender(); - let nonce = tx.nonce; + // given + let txq = new_queue(); + let tx = Tx::default().signed(); + let from = tx.sender(); + let nonce = tx.nonce; - // when - txq.import(TestClient::new(), vec![tx.local()]); + // when + txq.import(TestClient::new(), vec![tx.local()]); - // then - assert_eq!(txq.next_nonce(TestClient::new(), &from), Some(nonce + 1 )); + // then + assert_eq!(txq.next_nonce(TestClient::new(), &from), Some(nonce + 1)); } #[test] fn should_return_valid_last_nonce_after_cull() { - // given - let txq = new_queue(); - let (tx1, _, tx2) = Tx::default().signed_triple(); - let sender = tx1.sender(); - - // when - // Second should go to future - let res = txq.import(TestClient::new(), vec![tx1, tx2].local()); - assert_eq!(res, vec![Ok(()), Ok(())]); - // Now block is imported - let client = TestClient::new().with_nonce(124); - txq.cull(client.clone()); - // tx2 should be not be promoted to current - assert_eq!(txq.pending(client.clone(), PendingSettings::all_prioritized(0, 0)).len(), 0); - - // then - assert_eq!(txq.next_nonce(client.clone(), &sender), None); - assert_eq!(txq.next_nonce(client.with_nonce(125), &sender), Some(126.into())); + // given + let txq = new_queue(); + let (tx1, _, tx2) = Tx::default().signed_triple(); + let sender = tx1.sender(); + + // when + // Second should go to future + let res = txq.import(TestClient::new(), vec![tx1, tx2].local()); + assert_eq!(res, vec![Ok(()), Ok(())]); + // Now block is imported + let client = TestClient::new().with_nonce(124); + txq.cull(client.clone()); + // tx2 should be not be promoted to current + assert_eq!( + txq.pending(client.clone(), PendingSettings::all_prioritized(0, 0)) + .len(), + 0 + ); + + // then + assert_eq!(txq.next_nonce(client.clone(), &sender), None); + assert_eq!( + txq.next_nonce(client.with_nonce(125), &sender), + Some(126.into()) + ); } #[test] fn should_return_true_if_there_is_local_transaction_pending() { - // given - let txq = new_queue(); - let (tx1, tx2) = Tx::default().signed_pair(); - assert_eq!(txq.has_local_pending_transactions(), false); - let client = TestClient::new().with_local(&tx1.sender()); - - // when - let res = txq.import(client.clone(), vec![tx1.unverified(), tx2.local()]); - assert_eq!(res, vec![Ok(()), Ok(())]); - - // then - assert_eq!(txq.has_local_pending_transactions(), true); + // given + let txq = new_queue(); + let (tx1, tx2) = Tx::default().signed_pair(); + assert_eq!(txq.has_local_pending_transactions(), false); + let client = TestClient::new().with_local(&tx1.sender()); + + // when + let res = txq.import(client.clone(), vec![tx1.unverified(), tx2.local()]); + assert_eq!(res, vec![Ok(()), Ok(())]); + + // then + assert_eq!(txq.has_local_pending_transactions(), true); } #[test] fn should_reject_transactions_below_base_gas() { - // given - let txq = new_queue(); - let tx = Tx::default().signed(); - - // when - let res = txq.import(TestClient::new().with_gas_required(100_001), vec![tx].local()); - - // then - assert_eq!(res, vec![Err(transaction::Error::InsufficientGas { - minimal: 100_001.into(), - got: 21_000.into(), - })]); + // given + let txq = new_queue(); + let tx = Tx::default().signed(); + + // when + let res = txq.import( + TestClient::new().with_gas_required(100_001), + vec![tx].local(), + ); + + // then + assert_eq!( + res, + vec![Err(transaction::Error::InsufficientGas { + minimal: 100_001.into(), + got: 21_000.into(), + })] + ); } #[test] fn should_remove_out_of_date_transactions_occupying_queue() { - // given - let txq = TransactionQueue::new( - txpool::Options { - max_count: 105, - max_per_sender: 3, - max_mem_usage: 5_000_000, - }, - verifier::Options { - minimal_gas_price: 10.into(), - ..Default::default() - }, - PrioritizationStrategy::GasPriceOnly, - ); - // that transaction will be occupying the queue - let (_, tx) = Tx::default().signed_pair(); - let res = txq.import(TestClient::new(), vec![tx.local()]); - assert_eq!(res, vec![Ok(())]); - // This should not clear the transaction (yet) - txq.cull(TestClient::new()); - assert_eq!(txq.status().status.transaction_count, 1); - - // Now insert at least 100 transactions to have the other one marked as future. - for _ in 0..34 { - let (tx1, tx2, tx3) = Tx::default().signed_triple(); - txq.import(TestClient::new(), vec![tx1, tx2, tx3].local()); - } - assert_eq!(txq.status().status.transaction_count, 103); - - // when - txq.cull(TestClient::new()); - - // then - assert_eq!(txq.status().status.transaction_count, 102); + // given + let txq = TransactionQueue::new( + txpool::Options { + max_count: 105, + max_per_sender: 3, + max_mem_usage: 5_000_000, + }, + verifier::Options { + minimal_gas_price: 10.into(), + ..Default::default() + }, + PrioritizationStrategy::GasPriceOnly, + ); + // that transaction will be occupying the queue + let (_, tx) = Tx::default().signed_pair(); + let res = txq.import(TestClient::new(), vec![tx.local()]); + assert_eq!(res, vec![Ok(())]); + // This should not clear the transaction (yet) + txq.cull(TestClient::new()); + assert_eq!(txq.status().status.transaction_count, 1); + + // Now insert at least 100 transactions to have the other one marked as future. + for _ in 0..34 { + let (tx1, tx2, tx3) = Tx::default().signed_triple(); + txq.import(TestClient::new(), vec![tx1, tx2, tx3].local()); + } + assert_eq!(txq.status().status.transaction_count, 103); + + // when + txq.cull(TestClient::new()); + + // then + assert_eq!(txq.status().status.transaction_count, 102); } #[test] fn should_accept_local_transactions_below_min_gas_price() { - // given - let txq = TransactionQueue::new( - txpool::Options { - max_count: 3, - max_per_sender: 3, - max_mem_usage: TEST_QUEUE_MAX_MEM - }, - verifier::Options { - minimal_gas_price: 10.into(), - ..Default::default() - }, - PrioritizationStrategy::GasPriceOnly, - ); - let tx = Tx::gas_price(1).signed(); - - // when - let res = txq.import(TestClient::new(), vec![tx.local()]); - assert_eq!(res, vec![Ok(())]); - - // then - assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 1); + // given + let txq = TransactionQueue::new( + txpool::Options { + max_count: 3, + max_per_sender: 3, + max_mem_usage: TEST_QUEUE_MAX_MEM, + }, + verifier::Options { + minimal_gas_price: 10.into(), + ..Default::default() + }, + PrioritizationStrategy::GasPriceOnly, + ); + let tx = Tx::gas_price(1).signed(); + + // when + let res = txq.import(TestClient::new(), vec![tx.local()]); + assert_eq!(res, vec![Ok(())]); + + // then + assert_eq!( + txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)) + .len(), + 1 + ); } #[test] fn should_accept_local_service_transaction() { - // given - let txq = new_queue(); - let tx = Tx::gas_price(0).signed(); - - // when - let res = txq.import( - TestClient::new() - .with_local(&tx.sender()), - vec![tx.local()] - ); - assert_eq!(res, vec![Ok(())]); - - // then - assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)).len(), 1); + // given + let txq = new_queue(); + let tx = Tx::gas_price(0).signed(); + + // when + let res = txq.import(TestClient::new().with_local(&tx.sender()), vec![tx.local()]); + assert_eq!(res, vec![Ok(())]); + + // then + assert_eq!( + txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)) + .len(), + 1 + ); } #[test] fn should_not_accept_external_service_transaction_if_sender_not_certified() { - // given - let txq = new_queue(); - let tx1 = Tx::gas_price(0).signed().unverified(); - let tx2 = Tx::gas_price(0).signed().retracted(); - let tx3 = Tx::gas_price(0).signed().unverified(); - - // when - let res = txq.import(TestClient::new(), vec![tx1, tx2]); - assert_eq!(res, vec![ - Err(transaction::Error::InsufficientGasPrice { - minimal: 1.into(), - got: 0.into(), - }), - Err(transaction::Error::InsufficientGasPrice { - minimal: 1.into(), - got: 0.into(), - }), - ]); - - // then - let res = txq.import(TestClient::new().with_service_transaction(), vec![tx3]); - assert_eq!(res, vec![Ok(())]); + // given + let txq = new_queue(); + let tx1 = Tx::gas_price(0).signed().unverified(); + let tx2 = Tx::gas_price(0).signed().retracted(); + let tx3 = Tx::gas_price(0).signed().unverified(); + + // when + let res = txq.import(TestClient::new(), vec![tx1, tx2]); + assert_eq!( + res, + vec![ + Err(transaction::Error::InsufficientGasPrice { + minimal: 1.into(), + got: 0.into(), + }), + Err(transaction::Error::InsufficientGasPrice { + minimal: 1.into(), + got: 0.into(), + }), + ] + ); + + // then + let res = txq.import(TestClient::new().with_service_transaction(), vec![tx3]); + assert_eq!(res, vec![Ok(())]); } #[test] fn should_not_return_transactions_over_nonce_cap() { - // given - let txq = new_queue(); - let (tx1, tx2, tx3) = Tx::default().signed_triple(); - let res = txq.import( - TestClient::new(), - vec![tx1, tx2, tx3].local() - ); - assert_eq!(res, vec![Ok(()), Ok(()), Ok(())]); - - // when - let all = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); - // This should invalidate the cache! - let limited = txq.pending(TestClient::new(), PendingSettings { - block_number: 0, - current_timestamp: 0, - nonce_cap: Some(123.into()), - max_len: usize::max_value(), - ordering: PendingOrdering::Priority, - }); - - // then - assert_eq!(all.len(), 3); - assert_eq!(limited.len(), 1); + // given + let txq = new_queue(); + let (tx1, tx2, tx3) = Tx::default().signed_triple(); + let res = txq.import(TestClient::new(), vec![tx1, tx2, tx3].local()); + assert_eq!(res, vec![Ok(()), Ok(()), Ok(())]); + + // when + let all = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); + // This should invalidate the cache! + let limited = txq.pending( + TestClient::new(), + PendingSettings { + block_number: 0, + current_timestamp: 0, + nonce_cap: Some(123.into()), + max_len: usize::max_value(), + ordering: PendingOrdering::Priority, + }, + ); + + // then + assert_eq!(all.len(), 3); + assert_eq!(limited.len(), 1); } #[test] fn should_return_cached_pending_even_if_unordered_is_requested() { - // given - let txq = new_queue(); - let tx1 = Tx::default().signed(); - let (tx2_1, tx2_2)= Tx::default().signed_pair(); - let tx2_1_hash = tx2_1.hash(); - let res = txq.import(TestClient::new(), vec![tx1].unverified()); - assert_eq!(res, vec![Ok(())]); - let res = txq.import(TestClient::new(), vec![tx2_1, tx2_2].local()); - assert_eq!(res, vec![Ok(()), Ok(())]); - - // when - let all = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); - assert_eq!(all[0].hash, tx2_1_hash); - assert_eq!(all.len(), 3); - - // This should not invalidate the cache! - let limited = txq.pending(TestClient::new(), PendingSettings { - block_number: 0, - current_timestamp: 0, - nonce_cap: None, - max_len: 3, - ordering: PendingOrdering::Unordered, - }); - - // then - assert_eq!(all, limited); + // given + let txq = new_queue(); + let tx1 = Tx::default().signed(); + let (tx2_1, tx2_2) = Tx::default().signed_pair(); + let tx2_1_hash = tx2_1.hash(); + let res = txq.import(TestClient::new(), vec![tx1].unverified()); + assert_eq!(res, vec![Ok(())]); + let res = txq.import(TestClient::new(), vec![tx2_1, tx2_2].local()); + assert_eq!(res, vec![Ok(()), Ok(())]); + + // when + let all = txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 0)); + assert_eq!(all[0].hash, tx2_1_hash); + assert_eq!(all.len(), 3); + + // This should not invalidate the cache! + let limited = txq.pending( + TestClient::new(), + PendingSettings { + block_number: 0, + current_timestamp: 0, + nonce_cap: None, + max_len: 3, + ordering: PendingOrdering::Unordered, + }, + ); + + // then + assert_eq!(all, limited); } #[test] fn should_return_unordered_and_not_populate_the_cache() { - // given - let txq = new_queue(); - let tx1 = Tx::default().signed(); - let (tx2_1, tx2_2)= Tx::default().signed_pair(); - let res = txq.import(TestClient::new(), vec![tx1].unverified()); - assert_eq!(res, vec![Ok(())]); - let res = txq.import(TestClient::new(), vec![tx2_1, tx2_2].local()); - assert_eq!(res, vec![Ok(()), Ok(())]); - - // when - // This should not invalidate the cache! - let limited = txq.pending(TestClient::new(), PendingSettings { - block_number: 0, - current_timestamp: 0, - nonce_cap: None, - max_len: usize::max_value(), - ordering: PendingOrdering::Unordered, - }); - - // then - assert_eq!(limited.len(), 3); - assert!(!txq.is_pending_cached()); + // given + let txq = new_queue(); + let tx1 = Tx::default().signed(); + let (tx2_1, tx2_2) = Tx::default().signed_pair(); + let res = txq.import(TestClient::new(), vec![tx1].unverified()); + assert_eq!(res, vec![Ok(())]); + let res = txq.import(TestClient::new(), vec![tx2_1, tx2_2].local()); + assert_eq!(res, vec![Ok(()), Ok(())]); + + // when + // This should not invalidate the cache! + let limited = txq.pending( + TestClient::new(), + PendingSettings { + block_number: 0, + current_timestamp: 0, + nonce_cap: None, + max_len: usize::max_value(), + ordering: PendingOrdering::Unordered, + }, + ); + + // then + assert_eq!(limited.len(), 3); + assert!(!txq.is_pending_cached()); } #[test] fn should_clear_cache_after_timeout_for_local() { - // given - let txq = new_queue(); - let (tx, tx2) = Tx::default().signed_pair(); - let res = txq.import(TestClient::new(), vec![ - verifier::Transaction::Local(PendingTransaction::new(tx, transaction::Condition::Timestamp(1000).into())), - tx2.local() - ]); - assert_eq!(res, vec![Ok(()), Ok(())]); - - // This should populate cache and set timestamp to 1 - // when - assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 1)).len(), 0); - assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 1000)).len(), 0); - - // This should invalidate the cache and trigger transaction ready. - // then - assert_eq!(txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 1002)).len(), 2); + // given + let txq = new_queue(); + let (tx, tx2) = Tx::default().signed_pair(); + let res = txq.import( + TestClient::new(), + vec![ + verifier::Transaction::Local(PendingTransaction::new( + tx, + transaction::Condition::Timestamp(1000).into(), + )), + tx2.local(), + ], + ); + assert_eq!(res, vec![Ok(()), Ok(())]); + + // This should populate cache and set timestamp to 1 + // when + assert_eq!( + txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 1)) + .len(), + 0 + ); + assert_eq!( + txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 1000)) + .len(), + 0 + ); + + // This should invalidate the cache and trigger transaction ready. + // then + assert_eq!( + txq.pending(TestClient::new(), PendingSettings::all_prioritized(0, 1002)) + .len(), + 2 + ); } #[test] fn should_reject_big_transaction() { - let txq = new_queue(); - let big_tx = Tx::default().big_one(); - let res = txq.import(TestClient::new(), vec![ - verifier::Transaction::Local(PendingTransaction::new(big_tx, transaction::Condition::Timestamp(1000).into())) - ]); - assert_eq!(res, vec![Err(transaction::Error::TooBig)]); + let txq = new_queue(); + let big_tx = Tx::default().big_one(); + let res = txq.import( + TestClient::new(), + vec![verifier::Transaction::Local(PendingTransaction::new( + big_tx, + transaction::Condition::Timestamp(1000).into(), + ))], + ); + assert_eq!(res, vec![Err(transaction::Error::TooBig)]); } #[test] fn should_include_local_transaction_to_a_full_pool() { - // given - let txq = TransactionQueue::new( - txpool::Options { - max_count: 1, - max_per_sender: 2, - max_mem_usage: TEST_QUEUE_MAX_MEM - }, - verifier::Options { - minimal_gas_price: 1.into(), - block_gas_limit: 1_000_000.into(), - tx_gas_limit: 1_000_000.into(), - no_early_reject: false, - }, - PrioritizationStrategy::GasPriceOnly, - ); - let tx1 = Tx::gas_price(10_000).signed().unverified(); - let tx2 = Tx::gas_price(1).signed().local(); - - let res = txq.import(TestClient::new().with_balance(1_000_000_000), vec![tx1]); - assert_eq!(res, vec![Ok(())]); - assert_eq!(txq.status().status.transaction_count, 1); - - // when - let res = txq.import(TestClient::new(), vec![tx2]); - assert_eq!(res, vec![Ok(())]); - - // then - assert_eq!(txq.status().status.transaction_count, 1); + // given + let txq = TransactionQueue::new( + txpool::Options { + max_count: 1, + max_per_sender: 2, + max_mem_usage: TEST_QUEUE_MAX_MEM, + }, + verifier::Options { + minimal_gas_price: 1.into(), + block_gas_limit: 1_000_000.into(), + tx_gas_limit: 1_000_000.into(), + no_early_reject: false, + }, + PrioritizationStrategy::GasPriceOnly, + ); + let tx1 = Tx::gas_price(10_000).signed().unverified(); + let tx2 = Tx::gas_price(1).signed().local(); + + let res = txq.import(TestClient::new().with_balance(1_000_000_000), vec![tx1]); + assert_eq!(res, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 1); + + // when + let res = txq.import(TestClient::new(), vec![tx2]); + assert_eq!(res, vec![Ok(())]); + + // then + assert_eq!(txq.status().status.transaction_count, 1); } #[test] fn should_avoid_verifying_transaction_already_in_pool() { - // given - let txq = TransactionQueue::new( - txpool::Options { - max_count: 1, - max_per_sender: 2, - max_mem_usage: TEST_QUEUE_MAX_MEM - }, - verifier::Options { - minimal_gas_price: 1.into(), - block_gas_limit: 1_000_000.into(), - tx_gas_limit: 1_000_000.into(), - no_early_reject: false, - }, - PrioritizationStrategy::GasPriceOnly, - ); - let client = TestClient::new().with_balance(1_000_000_000); - let tx1 = Tx::gas_price(2).signed().unverified(); - - let res = txq.import(client.clone(), vec![tx1.clone()]); - assert_eq!(res, vec![Ok(())]); - assert_eq!(txq.status().status.transaction_count, 1); - assert!(client.was_verification_triggered()); - - // when - let client = TestClient::new(); - let res = txq.import(client.clone(), vec![tx1]); - assert_eq!(res, vec![Err(transaction::Error::AlreadyImported)]); - assert!(!client.was_verification_triggered()); - - // then - assert_eq!(txq.status().status.transaction_count, 1); + // given + let txq = TransactionQueue::new( + txpool::Options { + max_count: 1, + max_per_sender: 2, + max_mem_usage: TEST_QUEUE_MAX_MEM, + }, + verifier::Options { + minimal_gas_price: 1.into(), + block_gas_limit: 1_000_000.into(), + tx_gas_limit: 1_000_000.into(), + no_early_reject: false, + }, + PrioritizationStrategy::GasPriceOnly, + ); + let client = TestClient::new().with_balance(1_000_000_000); + let tx1 = Tx::gas_price(2).signed().unverified(); + + let res = txq.import(client.clone(), vec![tx1.clone()]); + assert_eq!(res, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 1); + assert!(client.was_verification_triggered()); + + // when + let client = TestClient::new(); + let res = txq.import(client.clone(), vec![tx1]); + assert_eq!(res, vec![Err(transaction::Error::AlreadyImported)]); + assert!(!client.was_verification_triggered()); + + // then + assert_eq!(txq.status().status.transaction_count, 1); } #[test] fn should_avoid_reverifying_recently_rejected_transactions() { - // given - let txq = TransactionQueue::new( - txpool::Options { - max_count: 1, - max_per_sender: 2, - max_mem_usage: TEST_QUEUE_MAX_MEM - }, - verifier::Options { - minimal_gas_price: 1.into(), - block_gas_limit: 1_000_000.into(), - tx_gas_limit: 1_000_000.into(), - no_early_reject: false, - }, - PrioritizationStrategy::GasPriceOnly, - ); - - let client = TestClient::new(); - let tx1 = Tx::gas_price(10_000).signed().unverified(); - - let res = txq.import(client.clone(), vec![tx1.clone()]); - assert_eq!(res, vec![Err(transaction::Error::InsufficientBalance { - balance: 0xf67c.into(), - cost: 0xc8458e4.into(), - })]); - assert_eq!(txq.status().status.transaction_count, 0); - assert!(client.was_verification_triggered()); - - // when - let client = TestClient::new(); - let res = txq.import(client.clone(), vec![tx1]); - assert_eq!(res, vec![Err(transaction::Error::InsufficientBalance { - balance: 0xf67c.into(), - cost: 0xc8458e4.into(), - })]); - assert!(!client.was_verification_triggered()); - - // then - assert_eq!(txq.status().status.transaction_count, 0); + // given + let txq = TransactionQueue::new( + txpool::Options { + max_count: 1, + max_per_sender: 2, + max_mem_usage: TEST_QUEUE_MAX_MEM, + }, + verifier::Options { + minimal_gas_price: 1.into(), + block_gas_limit: 1_000_000.into(), + tx_gas_limit: 1_000_000.into(), + no_early_reject: false, + }, + PrioritizationStrategy::GasPriceOnly, + ); + + let client = TestClient::new(); + let tx1 = Tx::gas_price(10_000).signed().unverified(); + + let res = txq.import(client.clone(), vec![tx1.clone()]); + assert_eq!( + res, + vec![Err(transaction::Error::InsufficientBalance { + balance: 0xf67c.into(), + cost: 0xc8458e4.into(), + })] + ); + assert_eq!(txq.status().status.transaction_count, 0); + assert!(client.was_verification_triggered()); + + // when + let client = TestClient::new(); + let res = txq.import(client.clone(), vec![tx1]); + assert_eq!( + res, + vec![Err(transaction::Error::InsufficientBalance { + balance: 0xf67c.into(), + cost: 0xc8458e4.into(), + })] + ); + assert!(!client.was_verification_triggered()); + + // then + assert_eq!(txq.status().status.transaction_count, 0); } #[test] fn should_reject_early_in_case_gas_price_is_less_than_min_effective() { - // given - let txq = TransactionQueue::new( - txpool::Options { - max_count: 1, - max_per_sender: 2, - max_mem_usage: TEST_QUEUE_MAX_MEM - }, - verifier::Options { - minimal_gas_price: 1.into(), - block_gas_limit: 1_000_000.into(), - tx_gas_limit: 1_000_000.into(), - no_early_reject: false, - }, - PrioritizationStrategy::GasPriceOnly, - ); - let client = TestClient::new().with_balance(1_000_000_000); - let tx1 = Tx::gas_price(2).signed().unverified(); - - let res = txq.import(client.clone(), vec![tx1]); - assert_eq!(res, vec![Ok(())]); - assert_eq!(txq.status().status.transaction_count, 1); - assert!(client.was_verification_triggered()); - - // when - let client = TestClient::new(); - let tx1 = Tx::default().signed().unverified(); - let res = txq.import(client.clone(), vec![tx1]); - assert_eq!(res, vec![Err(transaction::Error::InsufficientGasPrice { - minimal: 2.into(), - got: 1.into(), - })]); - assert!(!client.was_verification_triggered()); - - // then - assert_eq!(txq.status().status.transaction_count, 1); + // given + let txq = TransactionQueue::new( + txpool::Options { + max_count: 1, + max_per_sender: 2, + max_mem_usage: TEST_QUEUE_MAX_MEM, + }, + verifier::Options { + minimal_gas_price: 1.into(), + block_gas_limit: 1_000_000.into(), + tx_gas_limit: 1_000_000.into(), + no_early_reject: false, + }, + PrioritizationStrategy::GasPriceOnly, + ); + let client = TestClient::new().with_balance(1_000_000_000); + let tx1 = Tx::gas_price(2).signed().unverified(); + + let res = txq.import(client.clone(), vec![tx1]); + assert_eq!(res, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 1); + assert!(client.was_verification_triggered()); + + // when + let client = TestClient::new(); + let tx1 = Tx::default().signed().unverified(); + let res = txq.import(client.clone(), vec![tx1]); + assert_eq!( + res, + vec![Err(transaction::Error::TooCheapToReplace { + prev: Some(2.into()), + new: Some(1.into()), + })] + ); + assert!(!client.was_verification_triggered()); + + // then + assert_eq!(txq.status().status.transaction_count, 1); } #[test] fn should_not_reject_early_in_case_gas_price_is_less_than_min_effective() { - // given - let txq = TransactionQueue::new( - txpool::Options { - max_count: 1, - max_per_sender: 2, - max_mem_usage: TEST_QUEUE_MAX_MEM - }, - verifier::Options { - minimal_gas_price: 1.into(), - block_gas_limit: 1_000_000.into(), - tx_gas_limit: 1_000_000.into(), - no_early_reject: true, - }, - PrioritizationStrategy::GasPriceOnly, - ); - // when - let tx1 = Tx::gas_price(2).signed(); - let client = TestClient::new().with_local(&tx1.sender()); - let res = txq.import(client.clone(), vec![tx1.unverified()]); - - // then - assert_eq!(res, vec![Ok(())]); - assert_eq!(txq.status().status.transaction_count, 1); - assert!(client.was_verification_triggered()); - - // when - let tx1 = Tx::gas_price(1).signed(); - let client = TestClient::new().with_local(&tx1.sender()); - let res = txq.import(client.clone(), vec![tx1.unverified()]); - - // then - assert_eq!(res, vec![Ok(())]); - assert_eq!(txq.status().status.transaction_count, 2); - assert!(client.was_verification_triggered()); + // given + let txq = TransactionQueue::new( + txpool::Options { + max_count: 1, + max_per_sender: 2, + max_mem_usage: TEST_QUEUE_MAX_MEM, + }, + verifier::Options { + minimal_gas_price: 1.into(), + block_gas_limit: 1_000_000.into(), + tx_gas_limit: 1_000_000.into(), + no_early_reject: true, + }, + PrioritizationStrategy::GasPriceOnly, + ); + // when + let tx1 = Tx::gas_price(2).signed(); + let client = TestClient::new().with_local(&tx1.sender()); + let res = txq.import(client.clone(), vec![tx1.unverified()]); + + // then + assert_eq!(res, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 1); + assert!(client.was_verification_triggered()); + + // when + let tx1 = Tx::gas_price(1).signed(); + let client = TestClient::new().with_local(&tx1.sender()); + let res = txq.import(client.clone(), vec![tx1.unverified()]); + + // then + assert_eq!(res, vec![Ok(())]); + assert_eq!(txq.status().status.transaction_count, 2); + assert!(client.was_verification_triggered()); } diff --git a/miner/src/pool/tests/tx.rs b/miner/src/pool/tests/tx.rs index b8f6dca676d..2e46fe28ef5 100644 --- a/miner/src/pool/tests/tx.rs +++ b/miner/src/pool/tests/tx.rs @@ -1,197 +1,221 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use ethereum_types::{U256, H256}; -use ethkey::{Random, Generator}; +use ethereum_types::{H256, U256}; +use ethkey::{Generator, Random}; use rustc_hex::FromHex; -use types::transaction::{self, Transaction, SignedTransaction, UnverifiedTransaction}; +use types::transaction::{self, SignedTransaction, Transaction, UnverifiedTransaction}; use pool::{verifier, VerifiedTransaction}; #[derive(Clone)] pub struct Tx { - pub nonce: u64, - pub gas: u64, - pub gas_price: u64, + pub nonce: u64, + pub gas: u64, + pub gas_price: u64, } impl Default for Tx { - fn default() -> Self { - Tx { - nonce: 123, - gas: 21_000, - gas_price: 1, - } - } + fn default() -> Self { + Tx { + nonce: 123, + gas: 21_000, + gas_price: 1, + } + } } impl Tx { - pub fn gas_price(gas_price: u64) -> Self { - Tx { - gas_price, - ..Default::default() - } - } - - pub fn signed(self) -> SignedTransaction { - let keypair = Random.generate().unwrap(); - self.unsigned().sign(keypair.secret(), None) - } - - pub fn signed_pair(self) -> (SignedTransaction, SignedTransaction) { - let (tx1, tx2, _) = self.signed_triple(); - (tx1, tx2) - } - - pub fn signed_triple(mut self) -> (SignedTransaction, SignedTransaction, SignedTransaction) { - let keypair = Random.generate().unwrap(); - let tx1 = self.clone().unsigned().sign(keypair.secret(), None); - self.nonce += 1; - let tx2 = self.clone().unsigned().sign(keypair.secret(), None); - self.nonce += 1; - let tx3 = self.unsigned().sign(keypair.secret(), None); - - (tx1, tx2, tx3) - } - - pub fn signed_replacement(mut self) -> (SignedTransaction, SignedTransaction) { - let keypair = Random.generate().unwrap(); - let tx1 = self.clone().unsigned().sign(keypair.secret(), None); - self.gas_price += 1; - let tx2 = self.unsigned().sign(keypair.secret(), None); - - (tx1, tx2) - } - - pub fn unsigned(self) -> Transaction { - Transaction { - action: transaction::Action::Create, - value: U256::from(100), - data: "3331600055".from_hex().unwrap(), - gas: self.gas.into(), - gas_price: self.gas_price.into(), - nonce: self.nonce.into() - } - } - - pub fn big_one(self) -> SignedTransaction { - let keypair = Random.generate().unwrap(); - let tx = Transaction { - action: transaction::Action::Create, - value: U256::from(100), - data: include_str!("../res/big_transaction.data").from_hex().unwrap(), - gas: self.gas.into(), - gas_price: self.gas_price.into(), - nonce: self.nonce.into() - }; - tx.sign(keypair.secret(), None) - } + pub fn gas_price(gas_price: u64) -> Self { + Tx { + gas_price, + ..Default::default() + } + } + + pub fn signed(self) -> SignedTransaction { + let keypair = Random.generate().unwrap(); + self.unsigned().sign(keypair.secret(), None) + } + + pub fn signed_pair(self) -> (SignedTransaction, SignedTransaction) { + let (tx1, tx2, _) = self.signed_triple(); + (tx1, tx2) + } + + pub fn signed_triple(mut self) -> (SignedTransaction, SignedTransaction, SignedTransaction) { + let keypair = Random.generate().unwrap(); + let tx1 = self.clone().unsigned().sign(keypair.secret(), None); + self.nonce += 1; + let tx2 = self.clone().unsigned().sign(keypair.secret(), None); + self.nonce += 1; + let tx3 = self.unsigned().sign(keypair.secret(), None); + + (tx1, tx2, tx3) + } + + pub fn signed_replacement(mut self) -> (SignedTransaction, SignedTransaction) { + let keypair = Random.generate().unwrap(); + let tx1 = self.clone().unsigned().sign(keypair.secret(), None); + self.gas_price += 1; + let tx2 = self.unsigned().sign(keypair.secret(), None); + + (tx1, tx2) + } + + pub fn unsigned(self) -> Transaction { + Transaction { + action: transaction::Action::Create, + value: U256::from(100), + data: "3331600055".from_hex().unwrap(), + gas: self.gas.into(), + gas_price: self.gas_price.into(), + nonce: self.nonce.into(), + } + } + + pub fn big_one(self) -> SignedTransaction { + let keypair = Random.generate().unwrap(); + let tx = Transaction { + action: transaction::Action::Create, + value: U256::from(100), + data: include_str!("../res/big_transaction.data") + .from_hex() + .unwrap(), + gas: self.gas.into(), + gas_price: self.gas_price.into(), + nonce: self.nonce.into(), + }; + tx.sign(keypair.secret(), None) + } } pub trait TxExt: Sized { - type Out; - type Verified; - type Hash; + type Out; + type Verified; + type Hash; - fn hash(&self) -> Self::Hash; + fn hash(&self) -> Self::Hash; - fn local(self) -> Self::Out; + fn local(self) -> Self::Out; - fn retracted(self) -> Self::Out; + fn retracted(self) -> Self::Out; - fn unverified(self) -> Self::Out; + fn unverified(self) -> Self::Out; - fn verified(self) -> Self::Verified; + fn verified(self) -> Self::Verified; } -impl TxExt for (A, B) where - A: TxExt, - B: TxExt, +impl TxExt for (A, B) +where + A: TxExt, + B: TxExt, { - type Out = (O, O); - type Verified = (V, V); - type Hash = (H, H); - - fn hash(&self) -> Self::Hash { (self.0.hash(), self.1.hash()) } - fn local(self) -> Self::Out { (self.0.local(), self.1.local()) } - fn retracted(self) -> Self::Out { (self.0.retracted(), self.1.retracted()) } - fn unverified(self) -> Self::Out { (self.0.unverified(), self.1.unverified()) } - fn verified(self) -> Self::Verified { (self.0.verified(), self.1.verified()) } + type Out = (O, O); + type Verified = (V, V); + type Hash = (H, H); + + fn hash(&self) -> Self::Hash { + (self.0.hash(), self.1.hash()) + } + fn local(self) -> Self::Out { + (self.0.local(), self.1.local()) + } + fn retracted(self) -> Self::Out { + (self.0.retracted(), self.1.retracted()) + } + fn unverified(self) -> Self::Out { + (self.0.unverified(), self.1.unverified()) + } + fn verified(self) -> Self::Verified { + (self.0.verified(), self.1.verified()) + } } impl TxExt for SignedTransaction { - type Out = verifier::Transaction; - type Verified = VerifiedTransaction; - type Hash = H256; + type Out = verifier::Transaction; + type Verified = VerifiedTransaction; + type Hash = H256; - fn hash(&self) -> Self::Hash { - UnverifiedTransaction::hash(self) - } + fn hash(&self) -> Self::Hash { + UnverifiedTransaction::hash(self) + } - fn local(self) -> Self::Out { - verifier::Transaction::Local(self.into()) - } + fn local(self) -> Self::Out { + verifier::Transaction::Local(self.into()) + } - fn retracted(self) -> Self::Out { - verifier::Transaction::Retracted(self.into()) - } + fn retracted(self) -> Self::Out { + verifier::Transaction::Retracted(self.into()) + } - fn unverified(self) -> Self::Out { - verifier::Transaction::Unverified(self.into()) - } + fn unverified(self) -> Self::Out { + verifier::Transaction::Unverified(self.into()) + } - fn verified(self) -> Self::Verified { - VerifiedTransaction::from_pending_block_transaction(self) - } + fn verified(self) -> Self::Verified { + VerifiedTransaction::from_pending_block_transaction(self) + } } impl TxExt for Vec { - type Out = Vec; - type Verified = Vec; - type Hash = Vec; - - fn hash(&self) -> Self::Hash { - self.iter().map(|tx| tx.hash()).collect() - } - - fn local(self) -> Self::Out { - self.into_iter().map(Into::into).map(verifier::Transaction::Local).collect() - } - - fn retracted(self) -> Self::Out { - self.into_iter().map(Into::into).map(verifier::Transaction::Retracted).collect() - } - - fn unverified(self) -> Self::Out { - self.into_iter().map(Into::into).map(verifier::Transaction::Unverified).collect() - } - - fn verified(self) -> Self::Verified { - self.into_iter().map(VerifiedTransaction::from_pending_block_transaction).collect() - } + type Out = Vec; + type Verified = Vec; + type Hash = Vec; + + fn hash(&self) -> Self::Hash { + self.iter().map(|tx| tx.hash()).collect() + } + + fn local(self) -> Self::Out { + self.into_iter() + .map(Into::into) + .map(verifier::Transaction::Local) + .collect() + } + + fn retracted(self) -> Self::Out { + self.into_iter() + .map(Into::into) + .map(verifier::Transaction::Retracted) + .collect() + } + + fn unverified(self) -> Self::Out { + self.into_iter() + .map(Into::into) + .map(verifier::Transaction::Unverified) + .collect() + } + + fn verified(self) -> Self::Verified { + self.into_iter() + .map(VerifiedTransaction::from_pending_block_transaction) + .collect() + } } pub trait PairExt { - type Type; + type Type; - fn into_vec(self) -> Vec; + fn into_vec(self) -> Vec; } impl PairExt for (A, A) { - type Type = A; - fn into_vec(self) -> Vec { - vec![self.0, self.1] - } + type Type = A; + fn into_vec(self) -> Vec { + vec![self.0, self.1] + } } diff --git a/miner/src/pool/verifier.rs b/miner/src/pool/verifier.rs index 1fded37630e..cf4e116de89 100644 --- a/miner/src/pool/verifier.rs +++ b/miner/src/pool/verifier.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Transaction Verifier //! @@ -22,110 +22,116 @@ //! May have some overlap with `Readiness` since we don't want to keep around //! stalled transactions. -use std::cmp; -use std::sync::Arc; -use std::sync::atomic::{self, AtomicUsize}; +use std::{ + cmp, + sync::{ + atomic::{self, AtomicUsize}, + Arc, + }, +}; -use ethereum_types::{U256, H256}; +use ethereum_types::{H256, U256}; use rlp::Encodable; use txpool; use types::transaction; -use super::client::{Client, TransactionType}; -use super::VerifiedTransaction; +use super::{ + client::{Client, TransactionType}, + VerifiedTransaction, +}; /// Verification options. #[derive(Debug, Clone, PartialEq)] pub struct Options { - /// Minimal allowed gas price. - pub minimal_gas_price: U256, - /// Current block gas limit. - pub block_gas_limit: U256, - /// Maximal gas limit for a single transaction. - pub tx_gas_limit: U256, - /// Skip checks for early rejection, to make sure that local transactions are always imported. - pub no_early_reject: bool, + /// Minimal allowed gas price. + pub minimal_gas_price: U256, + /// Current block gas limit. + pub block_gas_limit: U256, + /// Maximal gas limit for a single transaction. + pub tx_gas_limit: U256, + /// Skip checks for early rejection, to make sure that local transactions are always imported. + pub no_early_reject: bool, } #[cfg(test)] impl Default for Options { - fn default() -> Self { - Options { - minimal_gas_price: 0.into(), - block_gas_limit: U256::max_value(), - tx_gas_limit: U256::max_value(), - no_early_reject: false, - } - } + fn default() -> Self { + Options { + minimal_gas_price: 0.into(), + block_gas_limit: U256::max_value(), + tx_gas_limit: U256::max_value(), + no_early_reject: false, + } + } } /// Transaction to verify. #[cfg_attr(test, derive(Clone))] pub enum Transaction { - /// Fresh, never verified transaction. - /// - /// We need to do full verification of such transactions - Unverified(transaction::UnverifiedTransaction), - - /// Transaction from retracted block. - /// - /// We could skip some parts of verification of such transactions - Retracted(transaction::UnverifiedTransaction), - - /// Locally signed or retracted transaction. - /// - /// We can skip consistency verifications and just verify readiness. - Local(transaction::PendingTransaction), + /// Fresh, never verified transaction. + /// + /// We need to do full verification of such transactions + Unverified(transaction::UnverifiedTransaction), + + /// Transaction from retracted block. + /// + /// We could skip some parts of verification of such transactions + Retracted(transaction::UnverifiedTransaction), + + /// Locally signed or retracted transaction. + /// + /// We can skip consistency verifications and just verify readiness. + Local(transaction::PendingTransaction), } impl Transaction { - /// Return transaction hash - pub fn hash(&self) -> H256 { - match *self { - Transaction::Unverified(ref tx) => tx.hash(), - Transaction::Retracted(ref tx) => tx.hash(), - Transaction::Local(ref tx) => tx.hash(), - } - } - - /// Return transaction gas price - pub fn gas_price(&self) -> &U256 { - match *self { - Transaction::Unverified(ref tx) => &tx.gas_price, - Transaction::Retracted(ref tx) => &tx.gas_price, - Transaction::Local(ref tx) => &tx.gas_price, - } - } - - fn gas(&self) -> &U256 { - match *self { - Transaction::Unverified(ref tx) => &tx.gas, - Transaction::Retracted(ref tx) => &tx.gas, - Transaction::Local(ref tx) => &tx.gas, - } - } - - fn transaction(&self) -> &transaction::Transaction { - match *self { - Transaction::Unverified(ref tx) => &*tx, - Transaction::Retracted(ref tx) => &*tx, - Transaction::Local(ref tx) => &*tx, - } - } - - fn is_local(&self) -> bool { - match *self { - Transaction::Local(..) => true, - _ => false, - } - } - - fn is_retracted(&self) -> bool { - match *self { - Transaction::Retracted(..) => true, - _ => false, - } - } + /// Return transaction hash + pub fn hash(&self) -> H256 { + match *self { + Transaction::Unverified(ref tx) => tx.hash(), + Transaction::Retracted(ref tx) => tx.hash(), + Transaction::Local(ref tx) => tx.hash(), + } + } + + /// Return transaction gas price + pub fn gas_price(&self) -> &U256 { + match *self { + Transaction::Unverified(ref tx) => &tx.gas_price, + Transaction::Retracted(ref tx) => &tx.gas_price, + Transaction::Local(ref tx) => &tx.gas_price, + } + } + + fn gas(&self) -> &U256 { + match *self { + Transaction::Unverified(ref tx) => &tx.gas, + Transaction::Retracted(ref tx) => &tx.gas, + Transaction::Local(ref tx) => &tx.gas, + } + } + + fn transaction(&self) -> &transaction::Transaction { + match *self { + Transaction::Unverified(ref tx) => &*tx, + Transaction::Retracted(ref tx) => &*tx, + Transaction::Local(ref tx) => &*tx, + } + } + + fn is_local(&self) -> bool { + match *self { + Transaction::Local(..) => true, + _ => false, + } + } + + fn is_retracted(&self) -> bool { + match *self { + Transaction::Retracted(..) => true, + _ => false, + } + } } /// Transaction verifier. @@ -133,205 +139,218 @@ impl Transaction { /// Verification can be run in parallel for all incoming transactions. #[derive(Debug)] pub struct Verifier { - client: C, - options: Options, - id: Arc, - transaction_to_replace: Option<(S, Arc)>, + client: C, + options: Options, + id: Arc, + transaction_to_replace: Option<(S, Arc)>, } impl Verifier { - /// Creates new transaction verfier with specified options. - pub fn new( - client: C, - options: Options, - id: Arc, - transaction_to_replace: Option<(S, Arc)>, - ) -> Self { - Verifier { - client, - options, - id, - transaction_to_replace, - } - } + /// Creates new transaction verfier with specified options. + pub fn new( + client: C, + options: Options, + id: Arc, + transaction_to_replace: Option<(S, Arc)>, + ) -> Self { + Verifier { + client, + options, + id, + transaction_to_replace, + } + } } -impl txpool::Verifier for Verifier { - type Error = transaction::Error; - type VerifiedTransaction = VerifiedTransaction; - - fn verify_transaction(&self, tx: Transaction) -> Result { - // The checks here should be ordered by cost/complexity. - // Cheap checks should be done as early as possible to discard unneeded transactions early. - - let hash = tx.hash(); - - if self.client.transaction_already_included(&hash) { - trace!(target: "txqueue", "[{:?}] Rejected tx already in the blockchain", hash); - bail!(transaction::Error::AlreadyImported) - } - - let gas_limit = cmp::min(self.options.tx_gas_limit, self.options.block_gas_limit); - if tx.gas() > &gas_limit { - debug!( - target: "txqueue", - "[{:?}] Rejected transaction above gas limit: {} > min({}, {})", - hash, - tx.gas(), - self.options.block_gas_limit, - self.options.tx_gas_limit, - ); - bail!(transaction::Error::GasLimitExceeded { - limit: gas_limit, - got: *tx.gas(), - }); - } - - let minimal_gas = self.client.required_gas(tx.transaction()); - if tx.gas() < &minimal_gas { - trace!(target: "txqueue", - "[{:?}] Rejected transaction with insufficient gas: {} < {}", - hash, - tx.gas(), - minimal_gas, - ); - - bail!(transaction::Error::InsufficientGas { - minimal: minimal_gas, - got: *tx.gas(), - }) - } - - let is_own = tx.is_local(); - // Quick exit for non-service and non-local transactions - // - // We're checking if the transaction is below configured minimal gas price - // or the effective minimal gas price in case the pool is full. - if !tx.gas_price().is_zero() && !is_own { - if tx.gas_price() < &self.options.minimal_gas_price { - trace!( - target: "txqueue", - "[{:?}] Rejected tx below minimal gas price threshold: {} < {}", - hash, - tx.gas_price(), - self.options.minimal_gas_price, - ); - bail!(transaction::Error::InsufficientGasPrice { - minimal: self.options.minimal_gas_price, - got: *tx.gas_price(), - }); - } - - if let Some((ref scoring, ref vtx)) = self.transaction_to_replace { - if scoring.should_reject_early(vtx, &tx) { - trace!( - target: "txqueue", - "[{:?}] Rejected tx early, cause it doesn't have any chance to get to the pool: (gas price: {} < {})", - hash, - tx.gas_price(), - vtx.transaction.gas_price, - ); - bail!(transaction::Error::InsufficientGasPrice { - minimal: vtx.transaction.gas_price, - got: *tx.gas_price(), - }); - } - } - } - - // Some more heavy checks below. - // Actually recover sender and verify that transaction - let is_retracted = tx.is_retracted(); - let transaction = match tx { - Transaction::Retracted(tx) | Transaction::Unverified(tx) => match self.client.verify_transaction(tx) { - Ok(signed) => signed.into(), - Err(err) => { - debug!(target: "txqueue", "[{:?}] Rejected tx {:?}", hash, err); - bail!(err) - }, - }, - Transaction::Local(tx) => tx, - }; - - // Verify RLP payload - if let Err(err) = self.client.decode_transaction(&transaction.rlp_bytes()) { - debug!(target: "txqueue", "[{:?}] Rejected transaction's rlp payload", err); - bail!(err) - } - - let sender = transaction.sender(); - let account_details = self.client.account_details(&sender); - - if transaction.gas_price < self.options.minimal_gas_price { - let transaction_type = self.client.transaction_type(&transaction); - if let TransactionType::Service = transaction_type { - debug!(target: "txqueue", "Service tx {:?} below minimal gas price accepted", hash); - } else if is_own || account_details.is_local { - info!(target: "own_tx", "Local tx {:?} below minimal gas price accepted", hash); - } else { - trace!( - target: "txqueue", - "[{:?}] Rejected tx below minimal gas price threshold: {} < {}", - hash, - transaction.gas_price, - self.options.minimal_gas_price, - ); - bail!(transaction::Error::InsufficientGasPrice { - minimal: self.options.minimal_gas_price, - got: transaction.gas_price, - }); - } - } - - let (full_gas_price, overflow_1) = transaction.gas_price.overflowing_mul(transaction.gas); - let (cost, overflow_2) = transaction.value.overflowing_add(full_gas_price); - if overflow_1 || overflow_2 { - trace!( - target: "txqueue", - "[{:?}] Rejected tx, price overflow", - hash - ); - bail!(transaction::Error::InsufficientBalance { - cost: U256::max_value(), - balance: account_details.balance, - }); - } - if account_details.balance < cost { - debug!( - target: "txqueue", - "[{:?}] Rejected tx with not enough balance: {} < {}", - hash, - account_details.balance, - cost, - ); - bail!(transaction::Error::InsufficientBalance { - cost: cost, - balance: account_details.balance, - }); - } - - if transaction.nonce < account_details.nonce { - debug!( - target: "txqueue", - "[{:?}] Rejected tx with old nonce ({} < {})", - hash, - transaction.nonce, - account_details.nonce, - ); - bail!(transaction::Error::Old); - } - - let priority = match (is_own || account_details.is_local, is_retracted) { - (true, _) => super::Priority::Local, - (false, false) => super::Priority::Regular, - (false, true) => super::Priority::Retracted, - }; - Ok(VerifiedTransaction { - transaction, - priority, - hash, - sender, - insertion_id: self.id.fetch_add(1, atomic::Ordering::AcqRel), - }) - } +impl txpool::Verifier + for Verifier +{ + type Error = transaction::Error; + type VerifiedTransaction = VerifiedTransaction; + + fn verify_transaction( + &self, + tx: Transaction, + ) -> Result { + // The checks here should be ordered by cost/complexity. + // Cheap checks should be done as early as possible to discard unneeded transactions early. + + let hash = tx.hash(); + + if self.client.transaction_already_included(&hash) { + trace!(target: "txqueue", "[{:?}] Rejected tx already in the blockchain", hash); + bail!(transaction::Error::AlreadyImported) + } + + let gas_limit = cmp::min(self.options.tx_gas_limit, self.options.block_gas_limit); + if tx.gas() > &gas_limit { + debug!( + target: "txqueue", + "[{:?}] Rejected transaction above gas limit: {} > min({}, {})", + hash, + tx.gas(), + self.options.block_gas_limit, + self.options.tx_gas_limit, + ); + bail!(transaction::Error::GasLimitExceeded { + limit: gas_limit, + got: *tx.gas(), + }); + } + + let minimal_gas = self.client.required_gas(tx.transaction()); + if tx.gas() < &minimal_gas { + trace!(target: "txqueue", + "[{:?}] Rejected transaction with insufficient gas: {} < {}", + hash, + tx.gas(), + minimal_gas, + ); + + bail!(transaction::Error::InsufficientGas { + minimal: minimal_gas, + got: *tx.gas(), + }) + } + + let is_own = tx.is_local(); + // Quick exit for non-service and non-local transactions + // + // We're checking if the transaction is below configured minimal gas price + // or the effective minimal gas price in case the pool is full. + if !tx.gas_price().is_zero() && !is_own { + if tx.gas_price() < &self.options.minimal_gas_price { + trace!( + target: "txqueue", + "[{:?}] Rejected tx below minimal gas price threshold: {} < {}", + hash, + tx.gas_price(), + self.options.minimal_gas_price, + ); + bail!(transaction::Error::InsufficientGasPrice { + minimal: self.options.minimal_gas_price, + got: *tx.gas_price(), + }); + } + + if let Some((ref scoring, ref vtx)) = self.transaction_to_replace { + if scoring.should_reject_early(vtx, &tx) { + trace!( + target: "txqueue", + "[{:?}] Rejected tx early, cause it doesn't have any chance to get to the pool: (gas price: {} < {})", + hash, + tx.gas_price(), + vtx.transaction.gas_price, + ); + return Err(transaction::Error::TooCheapToReplace { + prev: Some(vtx.transaction.gas_price), + new: Some(*tx.gas_price()), + }); + } + } + } + + // Some more heavy checks below. + // Actually recover sender and verify that transaction + let is_retracted = tx.is_retracted(); + let transaction = match tx { + Transaction::Retracted(tx) | Transaction::Unverified(tx) => { + match self.client.verify_transaction(tx) { + Ok(signed) => signed.into(), + Err(err) => { + debug!(target: "txqueue", "[{:?}] Rejected tx {:?}", hash, err); + bail!(err) + } + } + } + Transaction::Local(tx) => match self.client.verify_transaction_basic(&**tx) { + Ok(()) => tx, + Err(err) => { + warn!(target: "txqueue", "[{:?}] Rejected local tx {:?}", hash, err); + return Err(err); + } + }, + }; + + // Verify RLP payload + if let Err(err) = self.client.decode_transaction(&transaction.rlp_bytes()) { + debug!(target: "txqueue", "[{:?}] Rejected transaction's rlp payload", err); + bail!(err) + } + + let sender = transaction.sender(); + let account_details = self.client.account_details(&sender); + + if transaction.gas_price < self.options.minimal_gas_price { + let transaction_type = self.client.transaction_type(&transaction); + if let TransactionType::Service = transaction_type { + debug!(target: "txqueue", "Service tx {:?} below minimal gas price accepted", hash); + } else if is_own || account_details.is_local { + info!(target: "own_tx", "Local tx {:?} below minimal gas price accepted", hash); + } else { + trace!( + target: "txqueue", + "[{:?}] Rejected tx below minimal gas price threshold: {} < {}", + hash, + transaction.gas_price, + self.options.minimal_gas_price, + ); + bail!(transaction::Error::InsufficientGasPrice { + minimal: self.options.minimal_gas_price, + got: transaction.gas_price, + }); + } + } + + let (full_gas_price, overflow_1) = transaction.gas_price.overflowing_mul(transaction.gas); + let (cost, overflow_2) = transaction.value.overflowing_add(full_gas_price); + if overflow_1 || overflow_2 { + trace!( + target: "txqueue", + "[{:?}] Rejected tx, price overflow", + hash + ); + bail!(transaction::Error::InsufficientBalance { + cost: U256::max_value(), + balance: account_details.balance, + }); + } + if account_details.balance < cost { + debug!( + target: "txqueue", + "[{:?}] Rejected tx with not enough balance: {} < {}", + hash, + account_details.balance, + cost, + ); + bail!(transaction::Error::InsufficientBalance { + cost: cost, + balance: account_details.balance, + }); + } + + if transaction.nonce < account_details.nonce { + debug!( + target: "txqueue", + "[{:?}] Rejected tx with old nonce ({} < {})", + hash, + transaction.nonce, + account_details.nonce, + ); + bail!(transaction::Error::Old); + } + + let priority = match (is_own || account_details.is_local, is_retracted) { + (true, _) => super::Priority::Local, + (false, false) => super::Priority::Regular, + (false, true) => super::Priority::Retracted, + }; + Ok(VerifiedTransaction { + transaction, + priority, + hash, + sender, + insertion_id: self.id.fetch_add(1, atomic::Ordering::AcqRel), + }) + } } diff --git a/miner/src/service_transaction_checker.rs b/miner/src/service_transaction_checker.rs index 56e65c8b8f4..e6917568c44 100644 --- a/miner/src/service_transaction_checker.rs +++ b/miner/src/service_transaction_checker.rs @@ -1,94 +1,125 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! A service transactions contract checker. -use std::collections::HashMap; -use std::mem; -use std::sync::Arc; -use call_contract::{RegistryInfo, CallContract}; -use types::ids::BlockId; -use types::transaction::SignedTransaction; +use call_contract::{CallContract, RegistryInfo}; use ethabi::FunctionOutputDecoder; use ethereum_types::Address; use parking_lot::RwLock; +use std::{collections::HashMap, mem, sync::Arc}; +use types::{ids::BlockId, transaction::SignedTransaction}; -use_contract!(service_transaction, "res/contracts/service_transaction.json"); +use_contract!( + service_transaction, + "res/contracts/service_transaction.json" +); const SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME: &'static str = "service_transaction_checker"; /// Service transactions checker. #[derive(Default, Clone)] pub struct ServiceTransactionChecker { - certified_addresses_cache: Arc>> + certified_addresses_cache: Arc>>, } impl ServiceTransactionChecker { + /// Checks if given address in tx is whitelisted to send service transactions. + pub fn check( + &self, + client: &C, + tx: &SignedTransaction, + ) -> Result { + let sender = tx.sender(); + // Skip checking the contract if the transaction does not have zero gas price + if !tx.gas_price.is_zero() { + return Ok(false); + } - /// Checks if given address in tx is whitelisted to send service transactions. - pub fn check(&self, client: &C, tx: &SignedTransaction) -> Result { - let sender = tx.sender(); - // Skip checking the contract if the transaction does not have zero gas price - if !tx.gas_price.is_zero() { - return Ok(false) - } + self.check_address(client, sender) + } - self.check_address(client, sender) - } + /// Checks if given address is whitelisted to send service transactions. + pub fn check_address( + &self, + client: &C, + sender: Address, + ) -> Result { + trace!(target: "txqueue", "Checking service transaction checker contract from {}", sender); + if let Some(allowed) = self + .certified_addresses_cache + .try_read() + .as_ref() + .and_then(|c| c.get(&sender)) + { + return Ok(*allowed); + } + let contract_address = client + .registry_address( + SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME.to_owned(), + BlockId::Latest, + ) + .ok_or_else(|| "contract is not configured")?; + self.call_contract(client, contract_address, sender) + .and_then(|allowed| { + if let Some(mut cache) = self.certified_addresses_cache.try_write() { + cache.insert(sender, allowed); + }; + Ok(allowed) + }) + } - /// Checks if given address is whitelisted to send service transactions. - pub fn check_address(&self, client: &C, sender: Address) -> Result { - trace!(target: "txqueue", "Checking service transaction checker contract from {}", sender); - if let Some(allowed) = self.certified_addresses_cache.try_read().as_ref().and_then(|c| c.get(&sender)) { - return Ok(*allowed); - } - let contract_address = client.registry_address(SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME.to_owned(), BlockId::Latest) - .ok_or_else(|| "contract is not configured")?; - self.call_contract(client, contract_address, sender).and_then(|allowed| { - if let Some(mut cache) = self.certified_addresses_cache.try_write() { - cache.insert(sender, allowed); - }; - Ok(allowed) - }) - } + /// Refresh certified addresses cache + pub fn refresh_cache( + &self, + client: &C, + ) -> Result { + trace!(target: "txqueue", "Refreshing certified addresses cache"); + // replace the cache with an empty list, + // since it's not recent it won't be used anyway. + let cache = mem::replace( + &mut *self.certified_addresses_cache.write(), + HashMap::default(), + ); - /// Refresh certified addresses cache - pub fn refresh_cache(&self, client: &C) -> Result { - trace!(target: "txqueue", "Refreshing certified addresses cache"); - // replace the cache with an empty list, - // since it's not recent it won't be used anyway. - let cache = mem::replace(&mut *self.certified_addresses_cache.write(), HashMap::default()); + if let Some(contract_address) = client.registry_address( + SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME.to_owned(), + BlockId::Latest, + ) { + let addresses: Vec<_> = cache.keys().collect(); + let mut cache: HashMap = HashMap::default(); + for address in addresses { + let allowed = self.call_contract(client, contract_address, *address)?; + cache.insert(*address, allowed); + } + *self.certified_addresses_cache.write() = cache; + Ok(true) + } else { + Ok(false) + } + } - if let Some(contract_address) = client.registry_address(SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME.to_owned(), BlockId::Latest) { - let addresses: Vec<_> = cache.keys().collect(); - let mut cache: HashMap = HashMap::default(); - for address in addresses { - let allowed = self.call_contract(client, contract_address, *address)?; - cache.insert(*address, allowed); - } - mem::replace(&mut *self.certified_addresses_cache.write(), cache); - Ok(true) - } else { - Ok(false) - } - } - - fn call_contract(&self, client: &C, contract_address: Address, sender: Address) -> Result { - let (data, decoder) = service_transaction::functions::certified::call(sender); - let value = client.call_contract(BlockId::Latest, contract_address, data)?; - decoder.decode(&value).map_err(|e| e.to_string()) - } + fn call_contract( + &self, + client: &C, + contract_address: Address, + sender: Address, + ) -> Result { + let (data, decoder) = service_transaction::functions::certified::call(sender); + let value = client.call_contract(BlockId::Latest, contract_address, data)?; + decoder.decode(&value).map_err(|e| e.to_string()) + } } diff --git a/miner/src/work_notify.rs b/miner/src/work_notify.rs index 367990f2255..7371ce0786f 100644 --- a/miner/src/work_notify.rs +++ b/miner/src/work_notify.rs @@ -1,32 +1,34 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Sends HTTP notifications to a list of URLs every time new work is available. extern crate ethash; extern crate fetch; +extern crate hyper; extern crate parity_runtime; extern crate url; -extern crate hyper; -use self::fetch::{Fetch, Request, Client as FetchClient, Method}; -use self::parity_runtime::Executor; -use self::ethash::SeedHashCompute; -use self::url::Url; -use self::hyper::header::{self, HeaderValue}; +use self::{ + ethash::SeedHashCompute, + fetch::{Client as FetchClient, Fetch, Method, Request}, + hyper::header::{self, HeaderValue}, + parity_runtime::Executor, + url::Url, +}; use ethereum_types::{H256, U256}; use parking_lot::Mutex; @@ -34,60 +36,70 @@ use parking_lot::Mutex; use futures::Future; /// Trait for notifying about new mining work -pub trait NotifyWork : Send + Sync { - /// Fired when new mining job available - fn notify(&self, pow_hash: H256, difficulty: U256, number: u64); +pub trait NotifyWork: Send + Sync { + /// Fired when new mining job available + fn notify(&self, pow_hash: H256, difficulty: U256, number: u64); } /// POSTs info about new work to given urls. pub struct WorkPoster { - urls: Vec, - client: FetchClient, - executor: Executor, - seed_compute: Mutex, + urls: Vec, + client: FetchClient, + executor: Executor, + seed_compute: Mutex, } impl WorkPoster { - /// Create new `WorkPoster`. - pub fn new(urls: &[String], fetch: FetchClient, executor: Executor) -> Self { - let urls = urls.into_iter().filter_map(|u| { - match Url::parse(u) { - Ok(url) => Some(url), - Err(e) => { - warn!("Error parsing URL {} : {}", u, e); - None - } - } - }).collect(); - WorkPoster { - client: fetch, - executor: executor, - urls: urls, - seed_compute: Mutex::new(SeedHashCompute::default()), - } - } + /// Create new `WorkPoster`. + pub fn new(urls: &[String], fetch: FetchClient, executor: Executor) -> Self { + let urls = urls + .into_iter() + .filter_map(|u| match Url::parse(u) { + Ok(url) => Some(url), + Err(e) => { + warn!("Error parsing URL {} : {}", u, e); + None + } + }) + .collect(); + WorkPoster { + client: fetch, + executor: executor, + urls: urls, + seed_compute: Mutex::new(SeedHashCompute::default()), + } + } } impl NotifyWork for WorkPoster { - fn notify(&self, pow_hash: H256, difficulty: U256, number: u64) { - // TODO: move this to engine - let target = ethash::difficulty_to_boundary(&difficulty); - let seed_hash = &self.seed_compute.lock().hash_block_number(number); - let seed_hash = H256::from_slice(&seed_hash[..]); - let body = format!( - r#"{{ "result": ["0x{:x}","0x{:x}","0x{:x}","0x{:x}"] }}"#, - pow_hash, seed_hash, target, number - ); + fn notify(&self, pow_hash: H256, difficulty: U256, number: u64) { + // TODO: move this to engine + let target = ethash::difficulty_to_boundary(&difficulty); + let seed_hash = &self.seed_compute.lock().hash_block_number(number); + let seed_hash = H256::from_slice(&seed_hash[..]); + let body = format!( + r#"{{ "result": ["0x{:x}","0x{:x}","0x{:x}","0x{:x}"] }}"#, + pow_hash, seed_hash, target, number + ); - for u in &self.urls { - let u = u.clone(); - self.executor.spawn(self.client.fetch( - Request::new(u.clone(), Method::POST) - .with_header(header::CONTENT_TYPE, HeaderValue::from_static("application/json")) - .with_body(body.clone()), Default::default() - ).map_err(move |e| { - warn!("Error sending HTTP notification to {} : {}, retrying", u, e); - }).map(|_| ())); - } - } + for u in &self.urls { + let u = u.clone(); + self.executor.spawn( + self.client + .fetch( + Request::new(u.clone(), Method::POST) + .with_header( + header::CONTENT_TYPE, + HeaderValue::from_static("application/json"), + ) + .with_body(body.clone()), + Default::default(), + ) + .map_err(move |e| { + warn!("Error sending HTTP notification to {} : {}, retrying", u, e); + }) + .map(|_| ()), + ); + } + } } diff --git a/miner/stratum/Cargo.toml b/miner/stratum/Cargo.toml index a7e13ef681c..a4977555e53 100644 --- a/miner/stratum/Cargo.toml +++ b/miner/stratum/Cargo.toml @@ -8,8 +8,8 @@ authors = ["Parity Technologies "] [dependencies] ethereum-types = "0.4" keccak-hash = "0.1" -jsonrpc-core = "10.0.1" -jsonrpc-tcp-server = "10.0.1" +jsonrpc-core = "15.0.0" +jsonrpc-tcp-server = "15.0.0" log = "0.4" parking_lot = "0.7" diff --git a/miner/stratum/src/lib.rs b/miner/stratum/src/lib.rs index 5ee0296dacf..e1e79a548e5 100644 --- a/miner/stratum/src/lib.rs +++ b/miner/stratum/src/lib.rs @@ -1,51 +1,55 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Stratum protocol implementation for parity ethereum/bitcoin clients -extern crate jsonrpc_tcp_server; -extern crate jsonrpc_core; extern crate ethereum_types; +extern crate jsonrpc_core; +extern crate jsonrpc_tcp_server; extern crate keccak_hash as hash; extern crate parking_lot; -#[macro_use] extern crate log; +#[macro_use] +extern crate log; -#[cfg(test)] extern crate tokio; -#[cfg(test)] extern crate tokio_io; -#[cfg(test)] extern crate env_logger; +#[cfg(test)] +extern crate env_logger; +#[cfg(test)] +extern crate tokio; +#[cfg(test)] +extern crate tokio_io; mod traits; -pub use traits::{ - JobDispatcher, PushWorkHandler, Error, ServiceConfiguration, -}; +pub use traits::{Error, JobDispatcher, PushWorkHandler, ServiceConfiguration}; +use jsonrpc_core::{to_value, Compatibility, IoDelegate, MetaIoHandler, Metadata, Params, Value}; use jsonrpc_tcp_server::{ - Server as JsonRpcServer, ServerBuilder as JsonRpcServerBuilder, - RequestContext, MetaExtractor, Dispatcher, PushMessageError, + Dispatcher, MetaExtractor, PushMessageError, RequestContext, Server as JsonRpcServer, + ServerBuilder as JsonRpcServerBuilder, }; -use jsonrpc_core::{MetaIoHandler, Params, to_value, Value, Metadata, Compatibility, IoDelegate}; use std::sync::Arc; -use std::net::SocketAddr; -use std::collections::{HashSet, HashMap}; -use hash::keccak; use ethereum_types::H256; +use hash::keccak; use parking_lot::RwLock; +use std::{ + collections::{HashMap, HashSet}, + net::SocketAddr, +}; type RpcResult = Result; @@ -53,448 +57,461 @@ const NOTIFY_COUNTER_INITIAL: u32 = 16; /// Container which owns rpc server and stratum implementation pub struct Stratum { - /// RPC server - /// - /// It is an `Option` so it can be easily closed and released during `drop` phase - rpc_server: Option, - /// stratum protocol implementation - /// - /// It is owned by a container and rpc server - implementation: Arc, - /// Message dispatcher (tcp/ip service) - /// - /// Used to push messages to peers - tcp_dispatcher: Dispatcher, + /// RPC server + /// + /// It is an `Option` so it can be easily closed and released during `drop` phase + rpc_server: Option, + /// stratum protocol implementation + /// + /// It is owned by a container and rpc server + implementation: Arc, + /// Message dispatcher (tcp/ip service) + /// + /// Used to push messages to peers + tcp_dispatcher: Dispatcher, } impl Stratum { - pub fn start( - addr: &SocketAddr, - dispatcher: Arc, - secret: Option, - ) -> Result, Error> { - - let implementation = Arc::new(StratumImpl { - subscribers: RwLock::default(), - job_que: RwLock::default(), - dispatcher, - workers: Arc::new(RwLock::default()), - secret, - notify_counter: RwLock::new(NOTIFY_COUNTER_INITIAL), - }); - - let mut delegate = IoDelegate::::new(implementation.clone()); - delegate.add_method_with_meta("mining.subscribe", StratumImpl::subscribe); - delegate.add_method_with_meta("mining.authorize", StratumImpl::authorize); - delegate.add_method_with_meta("mining.submit", StratumImpl::submit); - let mut handler = MetaIoHandler::::with_compatibility(Compatibility::Both); - handler.extend_with(delegate); - - let server_builder = JsonRpcServerBuilder::new(handler); - let tcp_dispatcher = server_builder.dispatcher(); - let server_builder = server_builder.session_meta_extractor(PeerMetaExtractor::new(tcp_dispatcher.clone())); - let server = server_builder.start(addr)?; - - let stratum = Arc::new(Stratum { - rpc_server: Some(server), - implementation, - tcp_dispatcher, - }); - - Ok(stratum) - } + pub fn start( + addr: &SocketAddr, + dispatcher: Arc, + secret: Option, + ) -> Result, Error> { + let implementation = Arc::new(StratumImpl { + subscribers: RwLock::default(), + job_queue: RwLock::default(), + dispatcher, + workers: Arc::new(RwLock::default()), + secret, + notify_counter: RwLock::new(NOTIFY_COUNTER_INITIAL), + }); + + let mut delegate = IoDelegate::::new(implementation.clone()); + delegate.add_method_with_meta("mining.subscribe", StratumImpl::subscribe); + delegate.add_method_with_meta("mining.authorize", StratumImpl::authorize); + delegate.add_method_with_meta("mining.submit", StratumImpl::submit); + let mut handler = MetaIoHandler::::with_compatibility(Compatibility::Both); + handler.extend_with(delegate); + + let server_builder = JsonRpcServerBuilder::new(handler); + let tcp_dispatcher = server_builder.dispatcher(); + let server_builder = + server_builder.session_meta_extractor(PeerMetaExtractor::new(tcp_dispatcher.clone())); + let server = server_builder.start(addr)?; + + let stratum = Arc::new(Stratum { + rpc_server: Some(server), + implementation, + tcp_dispatcher, + }); + + Ok(stratum) + } } impl PushWorkHandler for Stratum { - fn push_work_all(&self, payload: String) -> Result<(), Error> { - self.implementation.push_work_all(payload, &self.tcp_dispatcher) - } - - fn push_work(&self, payloads: Vec) -> Result<(), Error> { - self.implementation.push_work(payloads, &self.tcp_dispatcher) - } + fn push_work_all(&self, payload: String) { + self.implementation + .push_work_all(payload, &self.tcp_dispatcher) + } } impl Drop for Stratum { - fn drop(&mut self) { - // shut down rpc server - self.rpc_server.take().map(|server| server.close()); - } + fn drop(&mut self) { + // shut down rpc server + self.rpc_server.take().map(|server| server.close()); + } } struct StratumImpl { - /// Subscribed clients - subscribers: RwLock>, - /// List of workers supposed to receive job update - job_que: RwLock>, - /// Payload manager - dispatcher: Arc, - /// Authorized workers (socket - worker_id) - workers: Arc>>, - /// Secret if any - secret: Option, - /// Dispatch notify couinter - notify_counter: RwLock, + /// Subscribed clients + subscribers: RwLock>, + /// List of workers supposed to receive job update + job_queue: RwLock>, + /// Payload manager + dispatcher: Arc, + /// Authorized workers (socket - worker_id) + workers: Arc>>, + /// Secret if any + secret: Option, + /// Dispatch notify counter + notify_counter: RwLock, } impl StratumImpl { - /// rpc method `mining.subscribe` - fn subscribe(&self, _params: Params, meta: SocketMetadata) -> RpcResult { - use std::str::FromStr; - - self.subscribers.write().push(meta.addr().clone()); - self.job_que.write().insert(meta.addr().clone()); - trace!(target: "stratum", "Subscription request from {:?}", meta.addr()); - - Ok(match self.dispatcher.initial() { - Some(initial) => match jsonrpc_core::Value::from_str(&initial) { - Ok(val) => Ok(val), - Err(e) => { - warn!(target: "stratum", "Invalid payload: '{}' ({:?})", &initial, e); - to_value(&[0u8; 0]) - }, - }, - None => to_value(&[0u8; 0]), - }.expect("Empty slices are serializable; qed")) - } - - /// rpc method `mining.authorize` - fn authorize(&self, params: Params, meta: SocketMetadata) -> RpcResult { - params.parse::<(String, String)>().map(|(worker_id, secret)|{ - if let Some(valid_secret) = self.secret { - let hash = keccak(secret); - if hash != valid_secret { - return to_value(&false); - } - } - trace!(target: "stratum", "New worker #{} registered", worker_id); - self.workers.write().insert(meta.addr().clone(), worker_id); - to_value(true) - }).map(|v| v.expect("Only true/false is returned and it's always serializable; qed")) - } - - /// rpc method `mining.submit` - fn submit(&self, params: Params, meta: SocketMetadata) -> RpcResult { - Ok(match params { - Params::Array(vals) => { - // first two elements are service messages (worker_id & job_id) - match self.dispatcher.submit(vals.iter().skip(2) - .filter_map(|val| match *val { - Value::String(ref s) => Some(s.to_owned()), - _ => None - }) - .collect::>()) { - Ok(()) => { - self.update_peers(&meta.tcp_dispatcher.expect("tcp_dispatcher is always initialized; qed")); - to_value(true) - }, - Err(submit_err) => { - warn!("Error while submitting share: {:?}", submit_err); - to_value(false) - } - } - }, - _ => { - trace!(target: "stratum", "Invalid submit work format {:?}", params); - to_value(false) - } - }.expect("Only true/false is returned and it's always serializable; qed")) - } - - /// Helper method - fn update_peers(&self, tcp_dispatcher: &Dispatcher) { - if let Some(job) = self.dispatcher.job() { - if let Err(e) = self.push_work_all(job, tcp_dispatcher) { - warn!("Failed to update some of the peers: {:?}", e); - } - } - } - - fn push_work_all(&self, payload: String, tcp_dispatcher: &Dispatcher) -> Result<(), Error> { - let hup_peers = { - let workers = self.workers.read(); - let next_request_id = { - let mut counter = self.notify_counter.write(); - if *counter == ::std::u32::MAX { *counter = NOTIFY_COUNTER_INITIAL; } - else { *counter = *counter + 1 } - *counter - }; - - let mut hup_peers = HashSet::with_capacity(0); // most of the cases won't be needed, hence avoid allocation - let workers_msg = format!("{{ \"id\": {}, \"method\": \"mining.notify\", \"params\": {} }}", next_request_id, payload); - trace!(target: "stratum", "pushing work for {} workers (payload: '{}')", workers.len(), &workers_msg); - for (ref addr, _) in workers.iter() { - trace!(target: "stratum", "pusing work to {}", addr); - match tcp_dispatcher.push_message(addr, workers_msg.clone()) { - Err(PushMessageError::NoSuchPeer) => { - trace!(target: "stratum", "Worker no longer connected: {}", &addr); - hup_peers.insert(*addr.clone()); - }, - Err(e) => { - warn!(target: "stratum", "Unexpected transport error: {:?}", e); - }, - Ok(_) => { }, - } - } - hup_peers - }; - - if !hup_peers.is_empty() { - let mut workers = self.workers.write(); - for hup_peer in hup_peers { workers.remove(&hup_peer); } - } - - Ok(()) - } - - fn push_work(&self, payloads: Vec, tcp_dispatcher: &Dispatcher) -> Result<(), Error> { - if !payloads.len() > 0 { - return Err(Error::NoWork); - } - let workers = self.workers.read(); - let addrs = workers.keys().collect::>(); - if !workers.len() > 0 { - return Err(Error::NoWorkers); - } - let mut que = payloads; - let mut addr_index = 0; - while que.len() > 0 { - let next_worker = addrs[addr_index]; - let mut next_payload = que.drain(0..1); - tcp_dispatcher.push_message( - next_worker, - next_payload.nth(0).expect("drained successfully of 0..1, so 0-th element should exist") - )?; - addr_index = addr_index + 1; - } - Ok(()) - } + /// rpc method `mining.subscribe` + fn subscribe(&self, _params: Params, meta: SocketMetadata) -> RpcResult { + use std::str::FromStr; + + self.subscribers.write().push(meta.addr().clone()); + self.job_queue.write().insert(meta.addr().clone()); + trace!(target: "stratum", "Subscription request from {:?}", meta.addr()); + + Ok(match self.dispatcher.initial() { + Some(initial) => match jsonrpc_core::Value::from_str(&initial) { + Ok(val) => Ok(val), + Err(e) => { + warn!(target: "stratum", "Invalid payload: '{}' ({:?})", &initial, e); + to_value(&[0u8; 0]) + } + }, + None => to_value(&[0u8; 0]), + } + .expect("Empty slices are serializable; qed")) + } + + /// rpc method `mining.authorize` + fn authorize(&self, params: Params, meta: SocketMetadata) -> RpcResult { + params + .parse::<(String, String)>() + .map(|(worker_id, secret)| { + if let Some(valid_secret) = self.secret { + let hash = keccak(secret); + if hash != valid_secret { + return to_value(&false); + } + } + trace!(target: "stratum", "New worker #{} registered", worker_id); + self.workers.write().insert(meta.addr().clone(), worker_id); + to_value(true) + }) + .map(|v| v.expect("Only true/false is returned and it's always serializable; qed")) + } + + /// rpc method `mining.submit` + fn submit(&self, params: Params, meta: SocketMetadata) -> RpcResult { + Ok(match params { + Params::Array(vals) => { + // first two elements are service messages (worker_id & job_id) + match self.dispatcher.submit( + vals.iter() + .skip(2) + .filter_map(|val| match *val { + Value::String(ref s) => Some(s.to_owned()), + _ => None, + }) + .collect::>(), + ) { + Ok(()) => { + self.update_peers( + &meta + .tcp_dispatcher + .expect("tcp_dispatcher is always initialized; qed"), + ); + to_value(true) + } + Err(submit_err) => { + warn!("Error while submitting share: {:?}", submit_err); + to_value(false) + } + } + } + _ => { + trace!(target: "stratum", "Invalid submit work format {:?}", params); + to_value(false) + } + } + .expect("Only true/false is returned and it's always serializable; qed")) + } + + /// Helper method + fn update_peers(&self, tcp_dispatcher: &Dispatcher) { + if let Some(job) = self.dispatcher.job() { + self.push_work_all(job, tcp_dispatcher) + } + } + + fn push_work_all(&self, payload: String, tcp_dispatcher: &Dispatcher) { + let hup_peers = { + let workers = self.workers.read(); + let next_request_id = { + let mut counter = self.notify_counter.write(); + if *counter == ::std::u32::MAX { + *counter = NOTIFY_COUNTER_INITIAL; + } else { + *counter = *counter + 1 + } + *counter + }; + + let mut hup_peers = HashSet::new(); + let workers_msg = format!( + "{{ \"id\": {}, \"method\": \"mining.notify\", \"params\": {} }}", + next_request_id, payload + ); + trace!(target: "stratum", "pushing work for {} workers (payload: '{}')", workers.len(), &workers_msg); + for (addr, _) in workers.iter() { + trace!(target: "stratum", "pusing work to {}", addr); + match tcp_dispatcher.push_message(addr, workers_msg.clone()) { + Err(PushMessageError::NoSuchPeer) => { + trace!(target: "stratum", "Worker no longer connected: {}", addr); + hup_peers.insert(addr.clone()); + } + Err(e) => { + warn!(target: "stratum", "Unexpected transport error: {:?}", e); + } + Ok(_) => {} + } + } + hup_peers + }; + + if !hup_peers.is_empty() { + let mut workers = self.workers.write(); + for hup_peer in hup_peers { + workers.remove(&hup_peer); + } + } + } } #[derive(Clone)] pub struct SocketMetadata { - addr: SocketAddr, - // with the new version of jsonrpc-core, SocketMetadata - // won't have to implement default, so this field will not - // have to be an Option - tcp_dispatcher: Option, + addr: SocketAddr, + // with the new version of jsonrpc-core, SocketMetadata + // won't have to implement default, so this field will not + // have to be an Option + tcp_dispatcher: Option, } impl Default for SocketMetadata { - fn default() -> Self { - SocketMetadata { - addr: "0.0.0.0:0".parse().unwrap(), - tcp_dispatcher: None, - } - } + fn default() -> Self { + SocketMetadata { + addr: "0.0.0.0:0".parse().unwrap(), + tcp_dispatcher: None, + } + } } impl SocketMetadata { - pub fn addr(&self) -> &SocketAddr { - &self.addr - } + pub fn addr(&self) -> &SocketAddr { + &self.addr + } } -impl Metadata for SocketMetadata { } +impl Metadata for SocketMetadata {} pub struct PeerMetaExtractor { - tcp_dispatcher: Dispatcher, + tcp_dispatcher: Dispatcher, } impl PeerMetaExtractor { - fn new(tcp_dispatcher: Dispatcher) -> Self { - PeerMetaExtractor { - tcp_dispatcher, - } - } + fn new(tcp_dispatcher: Dispatcher) -> Self { + PeerMetaExtractor { tcp_dispatcher } + } } impl MetaExtractor for PeerMetaExtractor { - fn extract(&self, context: &RequestContext) -> SocketMetadata { - SocketMetadata { - addr: context.peer_addr, - tcp_dispatcher: Some(self.tcp_dispatcher.clone()), - } - } + fn extract(&self, context: &RequestContext) -> SocketMetadata { + SocketMetadata { + addr: context.peer_addr, + tcp_dispatcher: Some(self.tcp_dispatcher.clone()), + } + } } #[cfg(test)] mod tests { - use super::*; - use std::net::{SocketAddr, Shutdown}; - use std::sync::Arc; - - use tokio::{io, runtime::Runtime, timer::timeout::{self, Timeout}, net::TcpStream}; - use jsonrpc_core::futures::{Future, future}; - - pub struct VoidManager; - - impl JobDispatcher for VoidManager { - fn submit(&self, _payload: Vec) -> Result<(), Error> { - Ok(()) - } - } - - fn dummy_request(addr: &SocketAddr, data: &str) -> Vec { - let mut runtime = Runtime::new().expect("Tokio Runtime should be created with no errors"); - - let mut data_vec = data.as_bytes().to_vec(); - data_vec.extend(b"\n"); - - let stream = TcpStream::connect(addr) - .and_then(move |stream| { - io::write_all(stream, data_vec) - }) - .and_then(|(stream, _)| { - stream.shutdown(Shutdown::Write).unwrap(); - io::read_to_end(stream, Vec::with_capacity(2048)) - }) - .and_then(|(_stream, read_buf)| { - future::ok(read_buf) - }); - let result = runtime.block_on(stream).expect("Runtime should run with no errors"); - - result - } - - #[test] - fn can_be_started() { - let stratum = Stratum::start(&"127.0.0.1:19980".parse().unwrap(), Arc::new(VoidManager), None); - assert!(stratum.is_ok()); - } - - #[test] - fn records_subscriber() { - let _ = ::env_logger::try_init(); - - let addr = "127.0.0.1:19985".parse().unwrap(); - let stratum = Stratum::start(&addr, Arc::new(VoidManager), None).unwrap(); - let request = r#"{"jsonrpc": "2.0", "method": "mining.subscribe", "params": [], "id": 1}"#; - dummy_request(&addr, request); - assert_eq!(1, stratum.implementation.subscribers.read().len()); - } - - struct DummyManager { - initial_payload: String - } - - impl DummyManager { - fn new() -> Arc { - Arc::new(Self::build()) - } - - fn build() -> DummyManager { - DummyManager { initial_payload: r#"[ "dummy payload" ]"#.to_owned() } - } - - fn of_initial(mut self, new_initial: &str) -> DummyManager { - self.initial_payload = new_initial.to_owned(); - self - } - } - - impl JobDispatcher for DummyManager { - fn initial(&self) -> Option { - Some(self.initial_payload.clone()) - } - - fn submit(&self, _payload: Vec) -> Result<(), Error> { - Ok(()) - } - } - - fn terminated_str(origin: &'static str) -> String { - let mut s = String::new(); - s.push_str(origin); - s.push_str("\n"); - s - } - - #[test] - fn receives_initial_payload() { - let addr = "127.0.0.1:19975".parse().unwrap(); - let _stratum = Stratum::start(&addr, DummyManager::new(), None).expect("There should be no error starting stratum"); - let request = r#"{"jsonrpc": "2.0", "method": "mining.subscribe", "params": [], "id": 2}"#; - - let response = String::from_utf8(dummy_request(&addr, request)).unwrap(); - - assert_eq!(terminated_str(r#"{"jsonrpc":"2.0","result":["dummy payload"],"id":2}"#), response); - } - - #[test] - fn can_authorize() { - let addr = "127.0.0.1:19970".parse().unwrap(); - let stratum = Stratum::start( - &addr, - Arc::new(DummyManager::build().of_initial(r#"["dummy autorize payload"]"#)), - None - ).expect("There should be no error starting stratum"); - - let request = r#"{"jsonrpc": "2.0", "method": "mining.authorize", "params": ["miner1", ""], "id": 1}"#; - let response = String::from_utf8(dummy_request(&addr, request)).unwrap(); - - assert_eq!(terminated_str(r#"{"jsonrpc":"2.0","result":true,"id":1}"#), response); - assert_eq!(1, stratum.implementation.workers.read().len()); - } - - #[test] - fn can_push_work() { - let _ = ::env_logger::try_init(); - - let addr = "127.0.0.1:19995".parse().unwrap(); - let stratum = Stratum::start( - &addr, - Arc::new(DummyManager::build().of_initial(r#"["dummy autorize payload"]"#)), - None - ).expect("There should be no error starting stratum"); - - let mut auth_request = + use super::*; + use std::{ + net::{Shutdown, SocketAddr}, + sync::Arc, + }; + + use jsonrpc_core::futures::{future, Future}; + use tokio::{ + io, + net::TcpStream, + runtime::Runtime, + timer::timeout::{self, Timeout}, + }; + + pub struct VoidManager; + + impl JobDispatcher for VoidManager { + fn submit(&self, _payload: Vec) -> Result<(), Error> { + Ok(()) + } + } + + fn dummy_request(addr: &SocketAddr, data: &str) -> Vec { + let mut runtime = Runtime::new().expect("Tokio Runtime should be created with no errors"); + + let mut data_vec = data.as_bytes().to_vec(); + data_vec.extend(b"\n"); + + let stream = TcpStream::connect(addr) + .and_then(move |stream| io::write_all(stream, data_vec)) + .and_then(|(stream, _)| { + stream.shutdown(Shutdown::Write).unwrap(); + io::read_to_end(stream, Vec::with_capacity(2048)) + }) + .and_then(|(_stream, read_buf)| future::ok(read_buf)); + let result = runtime + .block_on(stream) + .expect("Runtime should run with no errors"); + + result + } + + #[test] + fn can_be_started() { + let stratum = Stratum::start( + &"127.0.0.1:19980".parse().unwrap(), + Arc::new(VoidManager), + None, + ); + assert!(stratum.is_ok()); + } + + #[test] + fn records_subscriber() { + let _ = ::env_logger::try_init(); + + let addr = "127.0.0.1:19985".parse().unwrap(); + let stratum = Stratum::start(&addr, Arc::new(VoidManager), None).unwrap(); + let request = r#"{"jsonrpc": "2.0", "method": "mining.subscribe", "params": [], "id": 1}"#; + dummy_request(&addr, request); + assert_eq!(1, stratum.implementation.subscribers.read().len()); + } + + struct DummyManager { + initial_payload: String, + } + + impl DummyManager { + fn new() -> Arc { + Arc::new(Self::build()) + } + + fn build() -> DummyManager { + DummyManager { + initial_payload: r#"[ "dummy payload" ]"#.to_owned(), + } + } + + fn of_initial(mut self, new_initial: &str) -> DummyManager { + self.initial_payload = new_initial.to_owned(); + self + } + } + + impl JobDispatcher for DummyManager { + fn initial(&self) -> Option { + Some(self.initial_payload.clone()) + } + + fn submit(&self, _payload: Vec) -> Result<(), Error> { + Ok(()) + } + } + + fn terminated_str(origin: &'static str) -> String { + let mut s = String::new(); + s.push_str(origin); + s.push_str("\n"); + s + } + + #[test] + fn receives_initial_payload() { + let addr = "127.0.0.1:19975".parse().unwrap(); + let _stratum = Stratum::start(&addr, DummyManager::new(), None) + .expect("There should be no error starting stratum"); + let request = r#"{"jsonrpc": "2.0", "method": "mining.subscribe", "params": [], "id": 2}"#; + + let response = String::from_utf8(dummy_request(&addr, request)).unwrap(); + + assert_eq!( + terminated_str(r#"{"jsonrpc":"2.0","result":["dummy payload"],"id":2}"#), + response + ); + } + + #[test] + fn can_authorize() { + let addr = "127.0.0.1:19970".parse().unwrap(); + let stratum = Stratum::start( + &addr, + Arc::new(DummyManager::build().of_initial(r#"["dummy autorize payload"]"#)), + None, + ) + .expect("There should be no error starting stratum"); + + let request = r#"{"jsonrpc": "2.0", "method": "mining.authorize", "params": ["miner1", ""], "id": 1}"#; + let response = String::from_utf8(dummy_request(&addr, request)).unwrap(); + + assert_eq!( + terminated_str(r#"{"jsonrpc":"2.0","result":true,"id":1}"#), + response + ); + assert_eq!(1, stratum.implementation.workers.read().len()); + } + + #[test] + fn can_push_work() { + let _ = ::env_logger::try_init(); + + let addr = "127.0.0.1:19995".parse().unwrap(); + let stratum = Stratum::start( + &addr, + Arc::new(DummyManager::build().of_initial(r#"["dummy autorize payload"]"#)), + None, + ) + .expect("There should be no error starting stratum"); + + let mut auth_request = r#"{"jsonrpc": "2.0", "method": "mining.authorize", "params": ["miner1", ""], "id": 1}"# .as_bytes() .to_vec(); - auth_request.extend(b"\n"); - - let auth_response = "{\"jsonrpc\":\"2.0\",\"result\":true,\"id\":1}\n"; - - let mut runtime = Runtime::new().expect("Tokio Runtime should be created with no errors"); - let read_buf0 = vec![0u8; auth_response.len()]; - let read_buf1 = Vec::with_capacity(2048); - let stream = TcpStream::connect(&addr) - .and_then(move |stream| { - io::write_all(stream, auth_request) - }) - .and_then(|(stream, _)| { - io::read_exact(stream, read_buf0) - }) - .map_err(|err| panic!("{:?}", err)) - .and_then(move |(stream, read_buf0)| { - assert_eq!(String::from_utf8(read_buf0).unwrap(), auth_response); - trace!(target: "stratum", "Received authorization confirmation"); - Timeout::new(future::ok(stream), ::std::time::Duration::from_millis(100)) - }) - .map_err(|err: timeout::Error<()>| panic!("Timeout: {:?}", err)) - .and_then(move |stream| { - trace!(target: "stratum", "Pusing work to peers"); - stratum.push_work_all(r#"{ "00040008", "100500" }"#.to_owned()) - .expect("Pushing work should produce no errors"); - Timeout::new(future::ok(stream), ::std::time::Duration::from_millis(100)) - }) - .map_err(|err: timeout::Error<()>| panic!("Timeout: {:?}", err)) - .and_then(|stream| { - trace!(target: "stratum", "Ready to read work from server"); - stream.shutdown(Shutdown::Write).unwrap(); - io::read_to_end(stream, read_buf1) - }) - .and_then(|(_, read_buf1)| { - trace!(target: "stratum", "Received work from server"); - future::ok(read_buf1) - }); - let response = String::from_utf8( - runtime.block_on(stream).expect("Runtime should run with no errors") - ).expect("Response should be utf-8"); - - assert_eq!( + auth_request.extend(b"\n"); + + let auth_response = "{\"jsonrpc\":\"2.0\",\"result\":true,\"id\":1}\n"; + + let mut runtime = Runtime::new().expect("Tokio Runtime should be created with no errors"); + let read_buf0 = vec![0u8; auth_response.len()]; + let read_buf1 = Vec::with_capacity(2048); + let stream = TcpStream::connect(&addr) + .and_then(move |stream| io::write_all(stream, auth_request)) + .and_then(|(stream, _)| io::read_exact(stream, read_buf0)) + .map_err(|err| panic!("{:?}", err)) + .and_then(move |(stream, read_buf0)| { + assert_eq!(String::from_utf8(read_buf0).unwrap(), auth_response); + trace!(target: "stratum", "Received authorization confirmation"); + Timeout::new(future::ok(stream), ::std::time::Duration::from_millis(100)) + }) + .map_err(|err: timeout::Error<()>| panic!("Timeout: {:?}", err)) + .and_then(move |stream| { + trace!(target: "stratum", "Pusing work to peers"); + stratum.push_work_all(r#"{ "00040008", "100500" }"#.to_owned()); + Timeout::new(future::ok(stream), ::std::time::Duration::from_millis(100)) + }) + .map_err(|err: timeout::Error<()>| panic!("Timeout: {:?}", err)) + .and_then(|stream| { + trace!(target: "stratum", "Ready to read work from server"); + stream.shutdown(Shutdown::Write).unwrap(); + io::read_to_end(stream, read_buf1) + }) + .and_then(|(_, read_buf1)| { + trace!(target: "stratum", "Received work from server"); + future::ok(read_buf1) + }); + let response = String::from_utf8( + runtime + .block_on(stream) + .expect("Runtime should run with no errors"), + ) + .expect("Response should be utf-8"); + + assert_eq!( "{ \"id\": 17, \"method\": \"mining.notify\", \"params\": { \"00040008\", \"100500\" } }\n", response); - } + } + + #[test] + fn jsonprc_server_is_send_and_sync() { + fn is_send_and_sync() {} + + is_send_and_sync::(); + } } diff --git a/miner/stratum/src/traits.rs b/miner/stratum/src/traits.rs index 36b95a0169e..04b852958d0 100644 --- a/miner/stratum/src/traits.rs +++ b/miner/stratum/src/traits.rs @@ -1,69 +1,71 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std; -use std::error::Error as StdError; use ethereum_types::H256; use jsonrpc_tcp_server::PushMessageError; +use std; #[derive(Debug, Clone)] pub enum Error { - NoWork, - NoWorkers, - Io(String), - Tcp(String), - Dispatch(String), + NoWork, + NoWorkers, + Io(String), + Tcp(String), + Dispatch(String), } impl From for Error { - fn from(err: std::io::Error) -> Self { - Error::Io(err.description().to_owned()) - } + fn from(err: std::io::Error) -> Self { + Error::Io(err.to_string()) + } } impl From for Error { - fn from(err: PushMessageError) -> Self { - Error::Tcp(format!("Push message error: {:?}", err)) - } + fn from(err: PushMessageError) -> Self { + Error::Tcp(format!("Push message error: {:?}", err)) + } } /// Interface that can provide pow/blockchain-specific responses for the clients pub trait JobDispatcher: Send + Sync { - // json for initial client handshake - fn initial(&self) -> Option { None } - // json for difficulty dispatch - fn difficulty(&self) -> Option { None } - // json for job update given worker_id (payload manager should split job!) - fn job(&self) -> Option { None } - // miner job result - fn submit(&self, payload: Vec) -> Result<(), Error>; + // json for initial client handshake + fn initial(&self) -> Option { + None + } + // json for difficulty dispatch + fn difficulty(&self) -> Option { + None + } + // json for job update given worker_id (payload manager should split job!) + fn job(&self) -> Option { + None + } + // miner job result + fn submit(&self, payload: Vec) -> Result<(), Error>; } /// Interface that can handle requests to push job for workers pub trait PushWorkHandler: Send + Sync { - /// push the same work package for all workers (`payload`: json of pow-specific set of work specification) - fn push_work_all(&self, payload: String) -> Result<(), Error>; - - /// push the work packages worker-wise (`payload`: json of pow-specific set of work specification) - fn push_work(&self, payloads: Vec) -> Result<(), Error>; + /// push the same work package for all workers (`payload`: json of pow-specific set of work specification) + fn push_work_all(&self, payload: String); } pub struct ServiceConfiguration { - pub io_path: String, - pub listen_addr: String, - pub port: u16, - pub secret: Option, + pub io_path: String, + pub listen_addr: String, + pub port: u16, + pub secret: Option, } diff --git a/miner/using-queue/src/lib.rs b/miner/using-queue/src/lib.rs index 56e99879db7..78ad9183107 100644 --- a/miner/using-queue/src/lib.rs +++ b/miner/using-queue/src/lib.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Queue-like datastructure including notion of usage. @@ -20,257 +20,282 @@ /// usage to avoid items that were queued but never used from making it into /// the queue. pub struct UsingQueue { - /// Not yet being sealed by a miner, but if one asks for work, we'd prefer they do this. - pending: Option, - /// Currently being sealed by miners. - in_use: Vec, - /// The maximum allowable number of items in_use. - max_size: usize, + /// Not yet being sealed by a miner, but if one asks for work, we'd prefer they do this. + pending: Option, + /// Currently being sealed by miners. + in_use: Vec, + /// The maximum allowable number of items in_use. + max_size: usize, } /// Take an item or just clone it? pub enum GetAction { - /// Remove the item, faster but you can't get it back. - Take, - /// Clone the item, slower but you can get it again. - Clone, + /// Remove the item, faster but you can't get it back. + Take, + /// Clone the item, slower but you can get it again. + Clone, } impl UsingQueue { - /// Create a new struct with a maximum size of `max_size`. - pub fn new(max_size: usize) -> UsingQueue { - UsingQueue { - pending: None, - in_use: vec![], - max_size: max_size, - } - } - - /// Return a reference to the item at the top of the queue (or `None` if the queue is empty); - /// it doesn't constitute noting that the item is used. - pub fn peek_last_ref(&self) -> Option<&T> { - self.pending.as_ref().or(self.in_use.last()) - } - - /// Return a reference to the item at the top of the queue (or `None` if the queue is empty); - /// this constitutes using the item and will remain in the queue for at least another - /// `max_size` invocations of `set_pending() + use_last_ref()`. - pub fn use_last_ref(&mut self) -> Option<&T> { - if let Some(x) = self.pending.take() { - self.in_use.push(x); - if self.in_use.len() > self.max_size { - self.in_use.remove(0); - } - } - self.in_use.last() - } - - /// Place an item on the end of the queue. The previously pending item will be removed - /// if `use_last_ref()` since it was set. - pub fn set_pending(&mut self, b: T) { - self.pending = Some(b); - } - - /// Is there anything in the queue currently? - pub fn is_in_use(&self) -> bool { self.in_use.len() > 0 } - - /// Clears everything; the queue is entirely reset. - pub fn reset(&mut self) { - self.pending = None; - self.in_use.clear(); - } - - /// Returns `Some` item which is the first that `f` returns `true` with a reference to it - /// as a parameter or `None` if no such item exists in the queue. - fn take_used_if

(&mut self, predicate: P) -> Option where P: Fn(&T) -> bool { - self.in_use.iter().position(|r| predicate(r)).map(|i| self.in_use.remove(i)) - } - - /// Returns `Some` item which is the first that `f` returns `true` with a reference to it - /// as a parameter or `None` if no such item exists in the queue. - fn clone_used_if

(&mut self, predicate: P) -> Option where P: Fn(&T) -> bool, T: Clone { - self.in_use.iter().find(|r| predicate(r)).cloned() - } - - /// Fork-function for `take_used_if` and `clone_used_if`. - pub fn get_used_if

(&mut self, action: GetAction, predicate: P) -> Option where P: Fn(&T) -> bool, T: Clone { - match action { - GetAction::Take => self.take_used_if(predicate), - GetAction::Clone => self.clone_used_if(predicate), - } - } - - /// Returns a clone of the pending block if `f` returns `true` with a reference to it as - /// a parameter, otherwise `None`. - /// - /// If pending block is not available will clone the first of the used blocks that match the predicate. - pub fn get_pending_if

(&mut self, predicate: P) -> Option where P: Fn(&T) -> bool, T: Clone { - // a bit clumsy - TODO: think about a nicer way of expressing this. - if let Some(ref x) = self.pending { - if predicate(x) { - Some(x.clone()) - } else { - None - } - } else { - self.in_use.last().into_iter().filter(|x| predicate(x)).next().cloned() - } - } + /// Create a new struct with a maximum size of `max_size`. + pub fn new(max_size: usize) -> UsingQueue { + UsingQueue { + pending: None, + in_use: vec![], + max_size: max_size, + } + } + + /// Return a reference to the item at the top of the queue (or `None` if the queue is empty); + /// it doesn't constitute noting that the item is used. + pub fn peek_last_ref(&self) -> Option<&T> { + self.pending.as_ref().or(self.in_use.last()) + } + + /// Return a reference to the item at the top of the queue (or `None` if the queue is empty); + /// this constitutes using the item and will remain in the queue for at least another + /// `max_size` invocations of `set_pending() + use_last_ref()`. + pub fn use_last_ref(&mut self) -> Option<&T> { + if let Some(x) = self.pending.take() { + self.in_use.push(x); + if self.in_use.len() > self.max_size { + self.in_use.remove(0); + } + } + self.in_use.last() + } + + /// Place an item on the end of the queue. The previously pending item will be removed + /// if `use_last_ref()` since it was set. + pub fn set_pending(&mut self, b: T) { + self.pending = Some(b); + } + + /// Is there anything in the queue currently? + pub fn is_in_use(&self) -> bool { + self.in_use.len() > 0 + } + + /// Clears everything; the queue is entirely reset. + pub fn reset(&mut self) { + self.pending = None; + self.in_use.clear(); + } + + /// Returns `Some` item which is the first that `f` returns `true` with a reference to it + /// as a parameter or `None` if no such item exists in the queue. + fn take_used_if

(&mut self, predicate: P) -> Option + where + P: Fn(&T) -> bool, + { + self.in_use + .iter() + .position(|r| predicate(r)) + .map(|i| self.in_use.remove(i)) + } + + /// Returns `Some` item which is the first that `f` returns `true` with a reference to it + /// as a parameter or `None` if no such item exists in the queue. + fn clone_used_if

(&mut self, predicate: P) -> Option + where + P: Fn(&T) -> bool, + T: Clone, + { + self.in_use.iter().find(|r| predicate(r)).cloned() + } + + /// Fork-function for `take_used_if` and `clone_used_if`. + pub fn get_used_if

(&mut self, action: GetAction, predicate: P) -> Option + where + P: Fn(&T) -> bool, + T: Clone, + { + match action { + GetAction::Take => self.take_used_if(predicate), + GetAction::Clone => self.clone_used_if(predicate), + } + } + + /// Returns a clone of the pending block if `f` returns `true` with a reference to it as + /// a parameter, otherwise `None`. + /// + /// If pending block is not available will clone the first of the used blocks that match the predicate. + pub fn get_pending_if

(&mut self, predicate: P) -> Option + where + P: Fn(&T) -> bool, + T: Clone, + { + // a bit clumsy - TODO: think about a nicer way of expressing this. + if let Some(ref x) = self.pending { + if predicate(x) { + Some(x.clone()) + } else { + None + } + } else { + self.in_use + .last() + .into_iter() + .filter(|x| predicate(x)) + .next() + .cloned() + } + } } #[test] fn should_not_find_when_pushed() { - let mut q = UsingQueue::new(2); - q.set_pending(1); - assert!(q.take_used_if(|i| i == &1).is_none()); + let mut q = UsingQueue::new(2); + q.set_pending(1); + assert!(q.take_used_if(|i| i == &1).is_none()); } #[test] fn should_not_find_when_pushed_with_clone() { - let mut q = UsingQueue::new(2); - q.set_pending(1); - assert!(q.clone_used_if(|i| i == &1).is_none()); + let mut q = UsingQueue::new(2); + q.set_pending(1); + assert!(q.clone_used_if(|i| i == &1).is_none()); } #[test] fn should_find_when_pushed_and_used() { - let mut q = UsingQueue::new(2); - q.set_pending(1); - q.use_last_ref(); - assert!(q.take_used_if(|i| i == &1).unwrap() == 1); + let mut q = UsingQueue::new(2); + q.set_pending(1); + q.use_last_ref(); + assert!(q.take_used_if(|i| i == &1).unwrap() == 1); } #[test] fn should_have_same_semantics_for_get_take_clone() { - let mut q = UsingQueue::new(2); - q.set_pending(1); - assert!(q.get_used_if(GetAction::Clone, |i| i == &1).is_none()); - assert!(q.get_used_if(GetAction::Take, |i| i == &1).is_none()); - q.use_last_ref(); - assert!(q.get_used_if(GetAction::Clone, |i| i == &1).unwrap() == 1); - assert!(q.get_used_if(GetAction::Clone, |i| i == &1).unwrap() == 1); - assert!(q.get_used_if(GetAction::Take, |i| i == &1).unwrap() == 1); - assert!(q.get_used_if(GetAction::Clone, |i| i == &1).is_none()); - assert!(q.get_used_if(GetAction::Take, |i| i == &1).is_none()); + let mut q = UsingQueue::new(2); + q.set_pending(1); + assert!(q.get_used_if(GetAction::Clone, |i| i == &1).is_none()); + assert!(q.get_used_if(GetAction::Take, |i| i == &1).is_none()); + q.use_last_ref(); + assert!(q.get_used_if(GetAction::Clone, |i| i == &1).unwrap() == 1); + assert!(q.get_used_if(GetAction::Clone, |i| i == &1).unwrap() == 1); + assert!(q.get_used_if(GetAction::Take, |i| i == &1).unwrap() == 1); + assert!(q.get_used_if(GetAction::Clone, |i| i == &1).is_none()); + assert!(q.get_used_if(GetAction::Take, |i| i == &1).is_none()); } #[test] fn should_find_when_pushed_and_used_with_clone() { - let mut q = UsingQueue::new(2); - q.set_pending(1); - q.use_last_ref(); - assert!(q.clone_used_if(|i| i == &1).unwrap() == 1); + let mut q = UsingQueue::new(2); + q.set_pending(1); + q.use_last_ref(); + assert!(q.clone_used_if(|i| i == &1).unwrap() == 1); } #[test] fn should_not_find_again_when_pushed_and_taken() { - let mut q = UsingQueue::new(2); - q.set_pending(1); - q.use_last_ref(); - assert!(q.take_used_if(|i| i == &1).unwrap() == 1); - assert!(q.clone_used_if(|i| i == &1).is_none()); + let mut q = UsingQueue::new(2); + q.set_pending(1); + q.use_last_ref(); + assert!(q.take_used_if(|i| i == &1).unwrap() == 1); + assert!(q.clone_used_if(|i| i == &1).is_none()); } #[test] fn should_find_again_when_pushed_and_cloned() { - let mut q = UsingQueue::new(2); - q.set_pending(1); - q.use_last_ref(); - assert!(q.clone_used_if(|i| i == &1).unwrap() == 1); - assert!(q.clone_used_if(|i| i == &1).unwrap() == 1); - assert!(q.take_used_if(|i| i == &1).unwrap() == 1); + let mut q = UsingQueue::new(2); + q.set_pending(1); + q.use_last_ref(); + assert!(q.clone_used_if(|i| i == &1).unwrap() == 1); + assert!(q.clone_used_if(|i| i == &1).unwrap() == 1); + assert!(q.take_used_if(|i| i == &1).unwrap() == 1); } #[test] fn should_find_when_others_used() { - let mut q = UsingQueue::new(2); - q.set_pending(1); - q.use_last_ref(); - q.set_pending(2); - q.use_last_ref(); - assert!(q.take_used_if(|i| i == &1).is_some()); + let mut q = UsingQueue::new(2); + q.set_pending(1); + q.use_last_ref(); + q.set_pending(2); + q.use_last_ref(); + assert!(q.take_used_if(|i| i == &1).is_some()); } #[test] fn should_not_find_when_too_many_used() { - let mut q = UsingQueue::new(1); - q.set_pending(1); - q.use_last_ref(); - q.set_pending(2); - q.use_last_ref(); - assert!(q.take_used_if(|i| i == &1).is_none()); + let mut q = UsingQueue::new(1); + q.set_pending(1); + q.use_last_ref(); + q.set_pending(2); + q.use_last_ref(); + assert!(q.take_used_if(|i| i == &1).is_none()); } #[test] fn should_not_find_when_not_used_and_then_pushed() { - let mut q = UsingQueue::new(3); - q.set_pending(1); - q.set_pending(2); - q.use_last_ref(); - assert!(q.take_used_if(|i| i == &1).is_none()); + let mut q = UsingQueue::new(3); + q.set_pending(1); + q.set_pending(2); + q.use_last_ref(); + assert!(q.take_used_if(|i| i == &1).is_none()); } #[test] fn should_peek_correctly_after_push() { - let mut q = UsingQueue::new(3); - q.set_pending(1); - assert_eq!(q.peek_last_ref(), Some(&1)); - q.set_pending(2); - assert_eq!(q.peek_last_ref(), Some(&2)); + let mut q = UsingQueue::new(3); + q.set_pending(1); + assert_eq!(q.peek_last_ref(), Some(&1)); + q.set_pending(2); + assert_eq!(q.peek_last_ref(), Some(&2)); } #[test] fn should_inspect_correctly() { - let mut q = UsingQueue::new(3); - q.set_pending(1); - assert_eq!(q.use_last_ref(), Some(&1)); - assert_eq!(q.peek_last_ref(), Some(&1)); - q.set_pending(2); - assert_eq!(q.use_last_ref(), Some(&2)); - assert_eq!(q.peek_last_ref(), Some(&2)); + let mut q = UsingQueue::new(3); + q.set_pending(1); + assert_eq!(q.use_last_ref(), Some(&1)); + assert_eq!(q.peek_last_ref(), Some(&1)); + q.set_pending(2); + assert_eq!(q.use_last_ref(), Some(&2)); + assert_eq!(q.peek_last_ref(), Some(&2)); } #[test] fn should_not_find_when_not_used_peeked_and_then_pushed() { - let mut q = UsingQueue::new(3); - q.set_pending(1); - q.peek_last_ref(); - q.set_pending(2); - q.use_last_ref(); - assert!(q.take_used_if(|i| i == &1).is_none()); + let mut q = UsingQueue::new(3); + q.set_pending(1); + q.peek_last_ref(); + q.set_pending(2); + q.use_last_ref(); + assert!(q.take_used_if(|i| i == &1).is_none()); } #[test] fn should_pop_used() { - let mut q = UsingQueue::new(3); - q.set_pending(1); - q.use_last_ref(); - let popped = q.get_pending_if(|i| i == &1); - assert_eq!(popped, Some(1)); + let mut q = UsingQueue::new(3); + q.set_pending(1); + q.use_last_ref(); + let popped = q.get_pending_if(|i| i == &1); + assert_eq!(popped, Some(1)); } #[test] fn should_not_pop_last_pending() { - let mut q = UsingQueue::new(3); - q.set_pending(1); - assert_eq!(q.get_pending_if(|i| i == &1), Some(1)); - assert_eq!(q.get_pending_if(|i| i == &1), Some(1)); + let mut q = UsingQueue::new(3); + q.set_pending(1); + assert_eq!(q.get_pending_if(|i| i == &1), Some(1)); + assert_eq!(q.get_pending_if(|i| i == &1), Some(1)); } #[test] fn should_not_pop_unused_before_used() { - let mut q = UsingQueue::new(3); - q.set_pending(1); - q.set_pending(2); - let popped = q.get_pending_if(|i| i == &1); - assert_eq!(popped, None); + let mut q = UsingQueue::new(3); + q.set_pending(1); + q.set_pending(2); + let popped = q.get_pending_if(|i| i == &1); + assert_eq!(popped, None); } #[test] fn should_not_remove_used_popped() { - let mut q = UsingQueue::new(3); - q.set_pending(1); - q.use_last_ref(); - assert_eq!(q.get_pending_if(|i| i == &1), Some(1)); - assert_eq!(q.get_pending_if(|i| i == &1), Some(1)); + let mut q = UsingQueue::new(3); + q.set_pending(1); + q.use_last_ref(); + assert_eq!(q.get_pending_if(|i| i == &1), Some(1)); + assert_eq!(q.get_pending_if(|i| i == &1), Some(1)); } diff --git a/parity-clib/Cargo.toml b/parity-clib/Cargo.toml deleted file mode 100644 index f57e503dea6..00000000000 --- a/parity-clib/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -description = "C bindings for the Parity Ethereum client" -name = "parity-clib" -version = "1.12.0" -license = "GPL-3.0" -authors = ["Parity Technologies "] - -[lib] -name = "parity" -crate-type = ["cdylib", "staticlib"] - -[dependencies] -futures = "0.1.6" -jni = { version = "0.11", optional = true } -panic_hook = { path = "../util/panic-hook" } -parity-ethereum = { path = "../", default-features = false } -tokio = "0.1.11" -tokio-current-thread = "0.1.3" - -[features] -default = [] -final = ["parity-ethereum/final"] diff --git a/parity-clib/Parity.java b/parity-clib/Parity.java deleted file mode 100644 index 3885cfb1e68..00000000000 --- a/parity-clib/Parity.java +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -package io.parity.ethereum; - -/** - * Interface to the Parity client. - */ -public class Parity { - /** - * Starts the Parity client with the CLI options passed as an array of strings. - * - * Each space-delimited option corresponds to an array entry. - * For example: `["--port", "12345"]` - * - * @param options The CLI options to start Parity with - */ - public Parity(String[] options, String loggerMode, String loggerFile) { - long config = configFromCli(options); - inner = build(config, loggerMode, loggerFile); - } - - /** Performs an asynchronous RPC query by spawning a background thread that is executed until - * either a response is received or the timeout has been expired. - * - * @param query The JSON-encoded RPC query to perform - * @param timeoutMillis The maximum time in milliseconds that the query will run - * @param callback An instance of class which must have a instance method named `callback` that will be - * invoke when the result is ready - */ - public void rpcQuery(String query, long timeoutMillis, Object callback) { - rpcQueryNative(inner, query, timeoutMillis, callback); - } - - /** Subscribes to a specific WebSocket event that will run in a background thread until it is canceled. - * - * @param query The JSON-encoded RPC query to perform - * @param callback An instance of class which must have a instance method named `callback` that will be invoked - * when the result is ready - * - * @return A pointer to the current sessions which can be used to terminate the session later - */ - public long subscribeWebSocket(String query, Object callback) { - return subscribeWebSocketNative(inner, query, callback); - } - - /** Unsubscribes to a specific WebSocket event - * - * @param session Pointer the the session to terminate - */ - public void unsubscribeWebSocket(long session) { - unsubscribeWebSocketNative(session); - } - - // FIXME: `finalize` is deprecated - https://github.com/paritytech/parity-ethereum/issues/10066 - @Override - protected void finalize​() { - destroy(inner); - } - - static { - System.loadLibrary("parity"); - } - - private static native long configFromCli(String[] cliOptions); - private static native long build(long config, String loggerMode, String loggerFile); - private static native void destroy(long inner); - private static native void rpcQueryNative(long inner, String rpc, long timeoutMillis, Object callback); - private static native long subscribeWebSocketNative(long inner, String rpc, Object callback); - private static native void unsubscribeWebSocketNative(long session); - - private long inner; -} diff --git a/parity-clib/examples/cpp/CMakeLists.txt b/parity-clib/examples/cpp/CMakeLists.txt deleted file mode 100644 index 8cc6aef8f5c..00000000000 --- a/parity-clib/examples/cpp/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -cmake_minimum_required(VERSION 3.5) -include(ExternalProject) -include_directories("${CMAKE_SOURCE_DIR}/../..") -set (CMAKE_CXX_STANDARD 11) # Enfore C++11 -add_executable(parity-example main.cpp) - -ExternalProject_Add( - libparity - DOWNLOAD_COMMAND "" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - COMMAND cargo build -p parity-clib # Note: use --release in a real project - BINARY_DIR "${CMAKE_SOURCE_DIR}/../../../target" - INSTALL_COMMAND "" - LOG_BUILD ON) - -add_dependencies(parity-example libparity) -target_link_libraries(parity-example "${CMAKE_SOURCE_DIR}/../../../target/debug/${CMAKE_SHARED_LIBRARY_PREFIX}parity${CMAKE_SHARED_LIBRARY_SUFFIX}") diff --git a/parity-clib/examples/cpp/main.cpp b/parity-clib/examples/cpp/main.cpp deleted file mode 100644 index 43b31d793fa..00000000000 --- a/parity-clib/examples/cpp/main.cpp +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -#include -#include -#include -#include -#include - -void* parity_run(std::vector); -int parity_subscribe_to_websocket(void*); -int parity_rpc_queries(void*); - -const int SUBSCRIPTION_ID_LEN = 18; -const size_t TIMEOUT_ONE_MIN_AS_MILLIS = 60 * 1000; -const unsigned int CALLBACK_RPC = 1; -const unsigned int CALLBACK_WS = 2; - -struct Callback { - unsigned int type; - long unsigned int counter; -}; - -// list of rpc queries -const std::vector rpc_queries { - "{\"method\":\"parity_versionInfo\",\"params\":[],\"id\":1,\"jsonrpc\":\"2.0\"}", - "{\"method\":\"eth_getTransactionReceipt\",\"params\":[\"0x444172bef57ad978655171a8af2cfd89baa02a97fcb773067aef7794d6913fff\"],\"id\":1,\"jsonrpc\":\"2.0\"}", - "{\"method\":\"eth_estimateGas\",\"params\":[{\"from\":\"0x0066Dc48bb833d2B59f730F33952B3c29fE926F5\"}],\"id\":1,\"jsonrpc\":\"2.0\"}", - "{\"method\":\"eth_getBalance\",\"params\":[\"0x0066Dc48bb833d2B59f730F33952B3c29fE926F5\"],\"id\":1,\"jsonrpc\":\"2.0\"}" -}; - -// list of subscriptions -const std::vector ws_subscriptions { - "{\"method\":\"parity_subscribe\",\"params\":[\"eth_getBalance\",[\"0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826\",\"latest\"]],\"id\":1,\"jsonrpc\":\"2.0\"}", - "{\"method\":\"parity_subscribe\",\"params\":[\"parity_netPeers\"],\"id\":1,\"jsonrpc\":\"2.0\"}", - "{\"method\":\"eth_subscribe\",\"params\":[\"newHeads\"],\"id\":1,\"jsonrpc\":\"2.0\"}" -}; - -// callback that gets invoked upon an event -void callback(void* user_data, const char* response, size_t _len) { - Callback* cb = static_cast(user_data); - if (cb->type == CALLBACK_RPC) { - cb->counter -= 1; - } else if (cb->type == CALLBACK_WS) { - std::regex is_subscription ("\\{\"jsonrpc\":\"2.0\",\"result\":\"0[xX][a-fA-F0-9]{16}\",\"id\":1\\}"); - if (std::regex_match(response, is_subscription) == true) { - cb->counter -= 1; - } - } -} - -int main() { - // run full-client - { - std::vector config = {"--no-ipc" , "--jsonrpc-apis=all", "--chain", "kovan"}; - void* parity = parity_run(config); - if (parity_rpc_queries(parity)) { - printf("rpc_queries failed\r\n"); - return 1; - } - - if (parity_subscribe_to_websocket(parity)) { - printf("ws_queries failed\r\n"); - return 1; - } - - if (parity != nullptr) { - parity_destroy(parity); - } - } - - // run light-client - { - std::vector light_config = {"--no-ipc", "--light", "--jsonrpc-apis=all", "--chain", "kovan"}; - void* parity = parity_run(light_config); - - if (parity_rpc_queries(parity)) { - printf("rpc_queries failed\r\n"); - return 1; - } - - if (parity_subscribe_to_websocket(parity)) { - printf("ws_queries failed\r\n"); - return 1; - } - - if (parity != nullptr) { - parity_destroy(parity); - } - } - return 0; -} - -int parity_rpc_queries(void* parity) { - if (!parity) { - return 1; - } - - Callback cb { .type = CALLBACK_RPC, .counter = rpc_queries.size() }; - - for (auto query : rpc_queries) { - if (parity_rpc(parity, query.c_str(), query.length(), TIMEOUT_ONE_MIN_AS_MILLIS, callback, &cb) != 0) { - return 1; - } - } - - while(cb.counter != 0); - return 0; -} - - -int parity_subscribe_to_websocket(void* parity) { - if (!parity) { - return 1; - } - - std::vector sessions; - - Callback cb { .type = CALLBACK_WS, .counter = ws_subscriptions.size() }; - - for (auto sub : ws_subscriptions) { - void *const session = parity_subscribe_ws(parity, sub.c_str(), sub.length(), callback, &cb); - if (!session) { - return 1; - } - sessions.push_back(session); - } - - while(cb.counter != 0); - std::this_thread::sleep_for(std::chrono::seconds(60)); - for (auto session : sessions) { - parity_unsubscribe_ws(session); - } - return 0; -} - -void* parity_run(std::vector args) { - ParityParams cfg = { - .configuration = nullptr, - .on_client_restart_cb = callback, - .on_client_restart_cb_custom = nullptr, - .logger = nullptr - }; - - std::vector str_lens; - - for (auto arg: args) { - str_lens.push_back(std::strlen(arg)); - } - - // make sure no out-of-range access happens here - if (args.empty()) { - if (parity_config_from_cli(nullptr, nullptr, 0, &cfg.configuration) != 0) { - return nullptr; - } - } else { - if (parity_config_from_cli(&args[0], &str_lens[0], args.size(), &cfg.configuration) != 0) { - return nullptr; - } - } - - // enable logging but only the `rpc module` and don't write it to a file - char log_mode [] = "rpc=trace"; - parity_set_logger(log_mode, strlen(log_mode), nullptr, 0, &cfg.logger); - - void *parity = nullptr; - if (parity_start(&cfg, &parity) != 0) { - return nullptr; - } - - return parity; -} diff --git a/parity-clib/examples/java/Main.java b/parity-clib/examples/java/Main.java deleted file mode 100644 index c20b9e34d6f..00000000000 --- a/parity-clib/examples/java/Main.java +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -import java.util.Vector; -import java.util.concurrent.atomic.AtomicInteger; -import io.parity.ethereum.Parity; - -class Main { - public static final int ONE_MINUTE_AS_MILLIS = 60 * 1000; - - public static final String[] rpc_queries = { - "{\"method\":\"parity_versionInfo\",\"params\":[],\"id\":1,\"jsonrpc\":\"2.0\"}", - "{\"method\":\"eth_getTransactionReceipt\",\"params\":[\"0x444172bef57ad978655171a8af2cfd89baa02a97fcb773067aef7794d6913fff\"],\"id\":1,\"jsonrpc\":\"2.0\"}", - "{\"method\":\"eth_estimateGas\",\"params\":[{\"from\":\"0x0066Dc48bb833d2B59f730F33952B3c29fE926F5\"}],\"id\":1,\"jsonrpc\":\"2.0\"}", - "{\"method\":\"eth_getBalance\",\"params\":[\"0x0066Dc48bb833d2B59f730F33952B3c29fE926F5\"],\"id\":1,\"jsonrpc\":\"2.0\"}" - }; - - public static final String[] ws_queries = { - "{\"method\":\"parity_subscribe\",\"params\":[\"eth_getBalance\",[\"0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826\",\"latest\"]],\"id\":1,\"jsonrpc\":\"2.0\"}", - "{\"method\":\"parity_subscribe\",\"params\":[\"parity_netPeers\"],\"id\":1,\"jsonrpc\":\"2.0\"}", - "{\"method\":\"eth_subscribe\",\"params\":[\"newHeads\"],\"id\":1,\"jsonrpc\":\"2.0\"}" - }; - - public static void runParity(String[] config) { - String loggerMode = "rpc=trace"; - String loggerFile = "foo.log"; - Parity parity = new Parity(config, loggerMode, loggerFile); - - Callback rpcCallback = new Callback(1); - Callback webSocketCallback = new Callback(2); - - for (String query : rpc_queries) { - parity.rpcQuery(query, ONE_MINUTE_AS_MILLIS, rpcCallback); - } - - while (rpcCallback.getNumCallbacks() != 4); - - Vector sessions = new Vector(); - - for (String ws : ws_queries) { - long session = parity.subscribeWebSocket(ws, webSocketCallback); - sessions.add(session); - } - - try { - Thread.sleep(ONE_MINUTE_AS_MILLIS); - } catch (Exception e) { - System.out.println(e); - } - - for (long session : sessions) { - parity.unsubscribeWebSocket(session); - } - - // Force GC to destroy parity - parity = null; - System.gc(); - } - - public static void main(String[] args) { - String[] full = {"--no-ipc" , "--jsonrpc-apis=all", "--chain", "kovan"}; - String[] light = {"--no-ipc", "--light", "--jsonrpc-apis=all", "--chain", "kovan"}; - - runParity(full); - - try { - Thread.sleep(ONE_MINUTE_AS_MILLIS); - } catch (Exception e) { - System.out.println(e); - } - - runParity(light); - } -} - -class Callback { - private AtomicInteger counter; - private final int callbackType; - - public Callback(int type) { - counter = new AtomicInteger(); - callbackType = type; - } - - public void callback(Object response) { - counter.getAndIncrement(); - } - - public int getNumCallbacks() { - return counter.intValue(); - } -} diff --git a/parity-clib/examples/java/README.md b/parity-clib/examples/java/README.md deleted file mode 100644 index ec83905bf2b..00000000000 --- a/parity-clib/examples/java/README.md +++ /dev/null @@ -1,9 +0,0 @@ -parity-clib: Java example -=================================== - -An example Java application to demonstrate how to use `jni` bindings to parity-ethereum. Note, that the example is built in debug-mode to reduce the build time. If you want to use it in real project use release-mode instead to facilitate all compiler optimizations. - -## How to compile and run - -1. Make sure you have installed [JDK](https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) -2. Run `run.sh` \ No newline at end of file diff --git a/parity-clib/examples/java/run.sh b/parity-clib/examples/java/run.sh deleted file mode 100755 index 428a7dc751f..00000000000 --- a/parity-clib/examples/java/run.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -FLAGS="-Xlint:deprecation" -PARITY_JAVA="../../Parity.java" -# parity-clib must be built with feature `jni` in debug-mode to work -PARITY_LIB=".:../../../target/debug/" - -# build -cd .. -cargo build --features jni -cd - -javac $FLAGS -d $PWD $PARITY_JAVA -javac $FLAGS *.java -# Setup the path `libparity.so` and run -java -Djava.library.path=$PARITY_LIB Main diff --git a/parity-clib/parity.h b/parity-clib/parity.h deleted file mode 100644 index 7bba08e4337..00000000000 --- a/parity-clib/parity.h +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -#ifndef _PARITY_H_INCLUDED_ -#define _PARITY_H_INCLUDED_ - -#include - -/// Parameters to pass to `parity_start`. -struct ParityParams { - /// Configuration object, as handled by the `parity_config_*` functions. - /// Note that calling `parity_start` will destroy the configuration object (even on failure). - void *configuration; - - /// Callback function to call when the client receives an RPC request to change its chain spec. - /// - /// Will only be called if you enable the `--can-restart` flag. - /// - /// The first parameter of the callback is the value of `on_client_restart_cb_custom`. - /// The second and third parameters of the callback are the string pointer and length. - void (*on_client_restart_cb)(void* custom, const char* new_chain, size_t new_chain_len); - - /// Custom parameter passed to the `on_client_restart_cb` callback as first parameter. - void *on_client_restart_cb_custom; - - /// Logger object which must be created by the `parity_config_logger` function - void *logger; -}; - -#ifdef __cplusplus -extern "C" { -#endif - -/// Builds a new configuration object by parsing a list of CLI arguments. -/// -/// The first two parameters are string pointers and string lengths. They must have a length equal -/// to `len`. The strings don't need to be zero-terminated. -/// -/// On success, the produced object will be written to the `void*` pointed by `out`. -/// -/// Returns 0 on success, and non-zero on error. -/// -/// # Example -/// -/// ```no_run -/// void* cfg; -/// const char *args[] = {"--light", "--can-restart"}; -/// size_t str_lens[] = {7, 13}; -/// if (parity_config_from_cli(args, str_lens, 2, &cfg) != 0) { -/// return 1; -/// } -/// ``` -/// -int parity_config_from_cli(char const* const* args, size_t const* arg_lens, size_t len, void** out); - -/// Builds a new logger object which should be a member of the `ParityParams struct` -/// -/// - log_mode : String representing the log mode according to `Rust LOG` or nullptr to disable logging. -/// See module documentation for `ethcore-logger` for more info. -/// - log_mode_len : Length of the log_mode or zero to disable logging -/// - log_file : String respresenting the file name to write to log to or nullptr to disable logging to a file -/// - log_mode_len : Length of the log_file or zero to disable logging to a file -/// - logger : Pointer to point to the created `Logger` object - -/// **Important**: This function must only be called exactly once otherwise it will panic. If you want to disable a -/// logging mode or logging to a file make sure that you pass the `length` as zero -/// -/// # Example -/// -/// ```no_run -/// void* cfg; -/// const char *args[] = {"--light", "--can-restart"}; -/// size_t str_lens[] = {7, 13}; -/// if (parity_config_from_cli(args, str_lens, 2, &cfg) != 0) { -/// return 1; -/// } -/// char[] logger_mode = "rpc=trace"; -/// parity_set_logger(logger_mode, strlen(logger_mode), nullptr, 0, &cfg.logger); -/// ``` -/// -int parity_set_logger(const char* log_mode, size_t log_mode_len, const char* log_file, size_t log_file_len, void** logger); - -/// Destroys a configuration object created earlier. -/// -/// **Important**: You probably don't need to call this function. Calling `parity_start` destroys -/// the configuration object as well (even on failure). -void parity_config_destroy(void* cfg); - -/// Starts the parity client in background threads. Returns a pointer to a struct that represents -/// the running client. Can also return NULL if the execution completes instantly. -/// -/// **Important**: The configuration object passed inside `cfg` is destroyed when you -/// call `parity_start` (even on failure). -/// -/// On success, the produced object will be written to the `void*` pointed by `out`. -/// -/// Returns 0 on success, and non-zero on error. -int parity_start(const ParityParams* params, void** out); - -/// Destroys the parity client created with `parity_start`. -/// -/// **Warning**: `parity_start` can return NULL if execution finished instantly, in which case you -/// must not call this function. -void parity_destroy(void* parity); - -/// Performs an asynchronous RPC request running in a background thread for at most X milliseconds -/// -/// - parity : Reference to the running parity client -/// - rpc_query : JSON encoded string representing the RPC request. -/// - len : Length of the RPC query -/// - timeout_ms : Maximum time that request is waiting for a response -/// - response : Callback to invoke when the query gets answered. It will respond with a JSON encoded the string -/// with the result both on success and error. -/// - ud : Specific user defined data that can used in the callback -/// -/// - On success : The function returns 0 -/// - On error : The function returns 1 -/// -int parity_rpc(const void *const parity, const char* rpc_query, size_t rpc_len, size_t timeout_ms, - void (*subscribe)(void* ud, const char* response, size_t len), void* ud); - - -/// Subscribes to a specific websocket event that will run until it is canceled -/// -/// - parity : Reference to the running parity client -/// - ws_query : JSON encoded string representing the websocket event to subscribe to -/// - len : Length of the query -/// - response : Callback to invoke when a websocket event occurs -/// - ud : Specific user defined data that can used in the callback -/// -/// - On success : The function returns an object to the current session -/// which can be used cancel the subscription -/// - On error : The function returns a null pointer -/// -void* parity_subscribe_ws(const void *const parity, const char* ws_query, size_t len, - void (*subscribe)(void* ud, const char* response, size_t len), void* ud); - -/// Unsubscribes from a websocket subscription. Caution this function consumes the session object and must only be -/// used exactly once per session. -/// -/// - session : Pointer to the session to unsubscribe from -/// -int parity_unsubscribe_ws(const void *const session); - -/// Sets a callback to call when a panic happens in the Rust code. -/// -/// The callback takes as parameter the custom param (the one passed to this function), plus the -/// panic message. You are expected to log the panic message somehow, in order to communicate it to -/// the user. A panic always indicates a bug in Parity. -/// -/// Note that this method sets the panic hook for the whole program, and not just for Parity. In -/// other words, if you use multiple Rust libraries at once (and not just Parity), then a panic -/// in any Rust code will call this callback as well. -/// -/// ## Thread safety -/// -/// The callback can be called from any thread and multiple times simultaneously. Make sure that -/// your code is thread safe. -/// -int parity_set_panic_hook(void (*cb)(void* param, const char* msg, size_t msg_len), void* param); - -#ifdef __cplusplus -} -#endif - -#endif // include guard diff --git a/parity-clib/src/java.rs b/parity-clib/src/java.rs deleted file mode 100644 index 98969b1d10e..00000000000 --- a/parity-clib/src/java.rs +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::{mem, ptr}; -use std::ffi::c_void; -use std::sync::Arc; - -use {Callback, parity_config_from_cli, parity_destroy, parity_rpc_worker, parity_start, parity_set_logger, - parity_unsubscribe_ws, parity_ws_worker, ParityParams}; - -use jni::{JavaVM, JNIEnv}; -use jni::objects::{JClass, JString, JObject, JValue, GlobalRef}; -use jni::sys::{jlong, jobjectArray, va_list}; -use parity_ethereum::RunningClient; - -type CheckedQuery<'a> = (&'a RunningClient, String, JavaVM, GlobalRef); - -// Creates a Java callback to a static method named `void callback(Object)` -struct JavaCallback<'a> { - jvm: JavaVM, - callback: GlobalRef, - method_name: &'a str, - method_descriptor: &'a str, -} - -impl<'a> JavaCallback<'a> { - fn new(jvm: JavaVM, callback: GlobalRef) -> Self { - Self { - jvm, - callback, - method_name: "callback", - method_descriptor: "(Ljava/lang/Object;)V", - } - } -} - -impl<'a> Callback for JavaCallback<'a> { - fn call(&self, msg: &str) { - let env = self.jvm.attach_current_thread().expect("JavaVM should have an environment; qed"); - let java_str = env.new_string(msg.to_string()).expect("Rust String is valid JString; qed"); - let val = &[JValue::Object(JObject::from(java_str))]; - env.call_method(self.callback.as_obj(), self.method_name, self.method_descriptor, val).expect( - "The callback must be an instance method and be named \"void callback(Object)\"; qed)"); - } -} - -#[no_mangle] -pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_configFromCli(env: JNIEnv, _: JClass, cli: jobjectArray) -> jlong { - let cli_len = env.get_array_length(cli).expect("invalid Java bindings") as usize; - - let mut jni_strings = Vec::with_capacity(cli_len); - let mut opts = Vec::with_capacity(cli_len); - let mut opts_lens = Vec::with_capacity(cli_len); - - for n in 0..cli_len as i32 { - let elem = env.get_object_array_element(cli, n).expect("invalid Java bindings"); - let elem_str: JString = elem.into(); - match env.get_string(elem_str) { - Ok(s) => { - opts.push(s.as_ptr()); - opts_lens.push(s.to_bytes().len()); - jni_strings.push(s); - } - Err(err) => { - let _ = env.throw_new("java/lang/Exception", err.to_string()); - return 0 - } - }; - } - - let mut out = ptr::null_mut(); - match parity_config_from_cli(opts.as_ptr(), opts_lens.as_ptr(), cli_len, &mut out) { - 0 => out as jlong, - _ => { - let _ = env.throw_new("java/lang/Exception", "failed to create config object"); - 0 - }, - } -} - -#[no_mangle] -pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_build( - env: JNIEnv, - _: JClass, - config: va_list, - logger_mode: JString, - logger_file: JString -) -> jlong { - let mut params = ParityParams { - configuration: config, - .. mem::zeroed() - }; - - let logger_mode: String = env.get_string(logger_mode).expect("valid JString; qed").into(); - let logger_file: String = env.get_string(logger_file).expect("valid JString; qed").into(); - - parity_set_logger(logger_mode.as_ptr(), logger_mode.as_bytes().len(), logger_file.as_ptr(), - logger_file.as_bytes().len(), &mut params.logger); - - let mut out = ptr::null_mut(); - match parity_start(¶ms, &mut out) { - 0 => out as jlong, - _ => { - let _ = env.throw_new("java/lang/Exception", "failed to start Parity"); - 0 - } - } -} - -#[no_mangle] -pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_destroy(_env: JNIEnv, _: JClass, parity: va_list) { - parity_destroy(parity); -} - -unsafe fn java_query_checker<'a>(client: va_list, rpc: JString, callback: JObject, env: &JNIEnv<'a>) --> Result, String> { - let query: String = env.get_string(rpc) - .map(Into::into) - .map_err(|e| e.to_string())?; - - let client: &RunningClient = &*(client as *const RunningClient); - let jvm = env.get_java_vm().map_err(|e| e.to_string())?; - let global_ref = env.new_global_ref(callback).map_err(|e| e.to_string())?; - Ok((client, query, jvm, global_ref)) -} - -#[no_mangle] -pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_rpcQueryNative( - env: JNIEnv, - _: JClass, - parity: va_list, - rpc: JString, - timeout_ms: jlong, - callback: JObject, - ) -{ - let _ = java_query_checker(parity, rpc, callback, &env) - .map(|(client, query, jvm, global_ref)| { - let callback = Arc::new(JavaCallback::new(jvm, global_ref)); - parity_rpc_worker(client, &query, callback, timeout_ms as u64); - }) - .map_err(|e| { - let _ = env.throw_new("java/lang/Exception", e); - }); -} - -#[no_mangle] -pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_subscribeWebSocketNative( - env: JNIEnv, - _: JClass, - parity: va_list, - rpc: JString, - callback: JObject, - ) -> va_list { - - java_query_checker(parity, rpc, callback, &env) - .map(move |(client, query, jvm, global_ref)| { - let callback = Arc::new(JavaCallback::new(jvm, global_ref)); - parity_ws_worker(client, &query, callback) as va_list - }) - .unwrap_or_else(|e| { - let _ = env.throw_new("java/lang/Exception", e); - ptr::null_mut() - }) -} - -#[no_mangle] -pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_unsubscribeWebSocketNative( - _: JNIEnv, - _: JClass, - session: va_list) { - parity_unsubscribe_ws(session as *const c_void); -} diff --git a/parity-clib/src/lib.rs b/parity-clib/src/lib.rs deleted file mode 100644 index bbb60ec2d26..00000000000 --- a/parity-clib/src/lib.rs +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Note that all the structs and functions here are documented in `parity.h`, to avoid -//! duplicating documentation. - -extern crate futures; -extern crate panic_hook; -extern crate parity_ethereum; -extern crate tokio; -extern crate tokio_current_thread; - -#[cfg(feature = "jni")] -extern crate jni; - -#[cfg(feature = "jni")] -mod java; - -use std::ffi::CString; -use std::os::raw::{c_char, c_void, c_int}; -use std::{panic, ptr, slice, str, thread}; -use std::sync::Arc; -use std::time::Duration; - -use futures::{Future, Stream}; -use futures::sync::mpsc; -use parity_ethereum::{PubSubSession, RunningClient}; -use tokio_current_thread::CurrentThread; - -type CCallback = Option; -type CheckedQuery<'a> = (&'a RunningClient, &'static str); - -pub mod error { - pub const EMPTY: &str = r#"{"jsonrpc":"2.0","result":"null","id":1}"#; - pub const TIMEOUT: &str = r#"{"jsonrpc":"2.0","result":"timeout","id":1}"#; - pub const SUBSCRIBE: &str = r#"{"jsonrpc":"2.0","result":"subcribe_fail","id":1}"#; -} - -#[repr(C)] -pub struct ParityParams { - pub configuration: *mut c_void, - pub on_client_restart_cb: CCallback, - pub on_client_restart_cb_custom: *mut c_void, - pub logger: *mut c_void -} - -/// Trait representing a callback that passes a string -pub(crate) trait Callback: Send + Sync { - fn call(&self, msg: &str); -} - -// Internal structure for handling callbacks that get passed a string. -struct CallbackStr { - user_data: *mut c_void, - function: CCallback, -} - -unsafe impl Send for CallbackStr {} -unsafe impl Sync for CallbackStr {} -impl Callback for CallbackStr { - fn call(&self, msg: &str) { - if let Some(ref cb) = self.function { - let cstr = CString::new(msg).expect("valid string with no nul bytes in the middle; qed").into_raw(); - cb(self.user_data, cstr, msg.len()) - } - } -} - -#[no_mangle] -pub unsafe extern fn parity_config_from_cli( - args: *const *const c_char, - args_lens: *const usize, - len: usize, - output: *mut *mut c_void -) -> c_int { - panic::catch_unwind(|| { - *output = ptr::null_mut(); - - let args = { - let arg_ptrs = slice::from_raw_parts(args, len); - let arg_lens = slice::from_raw_parts(args_lens, len); - - let mut args = Vec::with_capacity(len + 1); - args.push("parity".to_owned()); - - for (&arg, &len) in arg_ptrs.iter().zip(arg_lens.iter()) { - let string = slice::from_raw_parts(arg as *const u8, len); - match String::from_utf8(string.to_owned()) { - Ok(a) => args.push(a), - Err(_) => return 1, - }; - } - args - }; - - match parity_ethereum::Configuration::parse_cli(&args) { - Ok(mut cfg) => { - // Always disable the auto-updater when used as a library. - cfg.args.arg_auto_update = "none".to_owned(); - - let cfg = Box::into_raw(Box::new(cfg)); - *output = cfg as *mut _; - 0 - }, - Err(_) => { - 1 - }, - } - }).unwrap_or(1) -} - -#[no_mangle] -pub unsafe extern fn parity_config_destroy(cfg: *mut c_void) { - let _ = panic::catch_unwind(|| { - let _cfg = Box::from_raw(cfg as *mut parity_ethereum::Configuration); - }); -} - -#[no_mangle] -pub unsafe extern fn parity_start(cfg: *const ParityParams, output: *mut *mut c_void) -> c_int { - panic::catch_unwind(|| { - *output = ptr::null_mut(); - let cfg: &ParityParams = &*cfg; - let logger = Arc::from_raw(cfg.logger as *mut parity_ethereum::RotatingLogger); - let config = Box::from_raw(cfg.configuration as *mut parity_ethereum::Configuration); - - let on_client_restart_cb = { - let cb = CallbackStr { - user_data: cfg.on_client_restart_cb_custom, - function: cfg.on_client_restart_cb, - }; - move |new_chain: String| { cb.call(&new_chain); } - }; - - let action = match parity_ethereum::start(*config, logger, on_client_restart_cb, || {}) { - Ok(action) => action, - Err(_) => return 1, - }; - - match action { - parity_ethereum::ExecutionAction::Instant(Some(s)) => { println!("{}", s); 0 }, - parity_ethereum::ExecutionAction::Instant(None) => 0, - parity_ethereum::ExecutionAction::Running(client) => { - *output = Box::into_raw(Box::new(client)) as *mut c_void; - 0 - } - } - }).unwrap_or(1) -} - -#[no_mangle] -pub unsafe extern fn parity_destroy(client: *mut c_void) { - let _ = panic::catch_unwind(|| { - let client = Box::from_raw(client as *mut RunningClient); - client.shutdown(); - }); -} - -#[no_mangle] -pub unsafe extern fn parity_rpc( - client: *const c_void, - query: *const c_char, - len: usize, - timeout_ms: usize, - callback: CCallback, - user_data: *mut c_void, -) -> c_int { - panic::catch_unwind(|| { - if let Some((client, query)) = parity_rpc_query_checker(client, query, len) { - let callback = Arc::new(CallbackStr {user_data, function: callback} ); - parity_rpc_worker(client, query, callback, timeout_ms as u64); - 0 - } else { - 1 - } - }).unwrap_or(1) -} - -#[no_mangle] -pub unsafe extern fn parity_subscribe_ws( - client: *const c_void, - query: *const c_char, - len: usize, - callback: CCallback, - user_data: *mut c_void, -) -> *const c_void { - panic::catch_unwind(|| { - if let Some((client, query)) = parity_rpc_query_checker(client, query, len) { - let callback = Arc::new(CallbackStr { user_data, function: callback}); - parity_ws_worker(client, query, callback) - } else { - ptr::null() - } - }) - .unwrap_or(ptr::null()) -} - -#[no_mangle] -pub unsafe extern fn parity_unsubscribe_ws(session: *const c_void) { - let _ = panic::catch_unwind(|| { - let _session = Arc::from_raw(session as *const PubSubSession); - }); -} - -#[no_mangle] -pub extern fn parity_set_panic_hook(callback: CCallback, param: *mut c_void) { - let cb = CallbackStr {user_data: param, function: callback}; - panic_hook::set_with(move |panic_msg| { - cb.call(panic_msg); - }); -} - -#[no_mangle] -pub unsafe extern fn parity_set_logger( - logger_mode: *const u8, - logger_mode_len: usize, - log_file: *const u8, - log_file_len: usize, - logger: *mut *mut c_void) { - - let mut logger_cfg = parity_ethereum::LoggerConfig::default(); - logger_cfg.mode = String::from_utf8(slice::from_raw_parts(logger_mode, logger_mode_len).to_owned()).ok(); - - // Make sure an empty string is not constructed as file name (to prevent panic) - if log_file_len != 0 && !log_file.is_null() { - logger_cfg.file = String::from_utf8(slice::from_raw_parts(log_file, log_file_len).to_owned()).ok(); - } - - *logger = Arc::into_raw(parity_ethereum::setup_log(&logger_cfg).expect("Logger initialized only once; qed")) as *mut _; -} - -// WebSocket event loop -fn parity_ws_worker(client: &RunningClient, query: &str, callback: Arc) -> *const c_void { - let (tx, mut rx) = mpsc::channel(1); - let session = Arc::new(PubSubSession::new(tx)); - let query_future = client.rpc_query(query, Some(session.clone())); - let weak_session = Arc::downgrade(&session); - let _handle = thread::Builder::new() - .name("ws-subscriber".into()) - .spawn(move || { - // Wait for subscription ID - // Note this may block forever and be can't destroyed using the session object - // However, this will likely timeout or be catched the RPC layer - if let Ok(Some(response)) = query_future.wait() { - callback.call(&response); - } else { - callback.call(error::SUBSCRIBE); - return; - } - - while weak_session.upgrade().map_or(0, |session| Arc::strong_count(&session)) > 1 { - for response in rx.by_ref().wait() { - if let Ok(r) = response { - callback.call(&r); - } - } - } - }) - .expect("rpc-subscriber thread shouldn't fail; qed"); - Arc::into_raw(session) as *const c_void -} - -// RPC event loop that runs for at most `timeout_ms` -fn parity_rpc_worker(client: &RunningClient, query: &str, callback: Arc, timeout_ms: u64) { - let cb = callback.clone(); - let query = client.rpc_query(query, None).map(move |response| { - let response = response.unwrap_or_else(|| error::EMPTY.to_string()); - callback.call(&response); - }); - - let _handle = thread::Builder::new() - .name("rpc_query".to_string()) - .spawn(move || { - let mut current_thread = CurrentThread::new(); - current_thread.spawn(query); - let _ = current_thread - .run_timeout(Duration::from_millis(timeout_ms)) - .map_err(|_e| { - cb.call(error::TIMEOUT); - }); - }) - .expect("rpc-query thread shouldn't fail; qed"); -} - -unsafe fn parity_rpc_query_checker<'a>(client: *const c_void, query: *const c_char, len: usize) - -> Option> -{ - let query_str = str::from_utf8(slice::from_raw_parts(query as *const u8, len)).ok()?; - let client: &RunningClient = &*(client as *const RunningClient); - Some((client, query_str)) -} diff --git a/parity/account.rs b/parity/account.rs index 118c06fddab..430dd2a19ff 100644 --- a/parity/account.rs +++ b/parity/account.rs @@ -1,156 +1,138 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::num::NonZeroU32; use params::SpecType; +use std::num::NonZeroU32; #[derive(Debug, PartialEq)] pub enum AccountCmd { - New(NewAccount), - List(ListAccounts), - Import(ImportAccounts), - ImportFromGeth(ImportFromGethAccounts) + New(NewAccount), + List(ListAccounts), + Import(ImportAccounts), } #[derive(Debug, PartialEq)] pub struct ListAccounts { - pub path: String, - pub spec: SpecType, + pub path: String, + pub spec: SpecType, } #[derive(Debug, PartialEq)] pub struct NewAccount { - pub iterations: NonZeroU32, - pub path: String, - pub spec: SpecType, - pub password_file: Option, + pub iterations: NonZeroU32, + pub path: String, + pub spec: SpecType, + pub password_file: Option, } #[derive(Debug, PartialEq)] pub struct ImportAccounts { - pub from: Vec, - pub to: String, - pub spec: SpecType, -} - -/// Parameters for geth accounts' import -#[derive(Debug, PartialEq)] -pub struct ImportFromGethAccounts { - /// import mainnet (false) or testnet (true) accounts - pub testnet: bool, - /// directory to import accounts to - pub to: String, - pub spec: SpecType, + pub from: Vec, + pub to: String, + pub spec: SpecType, } - #[cfg(not(feature = "accounts"))] pub fn execute(_cmd: AccountCmd) -> Result { - Err("Account management is deprecated. Please see #9997 for alternatives:\nhttps://github.com/paritytech/parity-ethereum/issues/9997".into()) + Err("Account management is deprecated. Please see #9997 for alternatives:\nhttps://github.com/openethereum/openethereum/issues/9997".into()) } #[cfg(feature = "accounts")] mod command { - use super::*; - use std::path::PathBuf; - use accounts::{AccountProvider, AccountProviderSettings}; - use ethstore::{EthStore, SecretStore, SecretVaultRef, import_account, import_accounts, read_geth_accounts}; - use ethstore::accounts_dir::RootDiskDirectory; - use helpers::{password_prompt, password_from_file}; - - pub fn execute(cmd: AccountCmd) -> Result { - match cmd { - AccountCmd::New(new_cmd) => new(new_cmd), - AccountCmd::List(list_cmd) => list(list_cmd), - AccountCmd::Import(import_cmd) => import(import_cmd), - AccountCmd::ImportFromGeth(import_geth_cmd) => import_geth(import_geth_cmd) - } - } - - fn keys_dir(path: String, spec: SpecType) -> Result { - let spec = spec.spec(&::std::env::temp_dir())?; - let mut path = PathBuf::from(&path); - path.push(spec.data_dir); - RootDiskDirectory::create(path).map_err(|e| format!("Could not open keys directory: {}", e)) - } - - fn secret_store(dir: Box, iterations: Option) -> Result { - match iterations { - Some(i) => EthStore::open_with_iterations(dir, i), - _ => EthStore::open(dir) - }.map_err(|e| format!("Could not open keys store: {}", e)) - } - - fn new(n: NewAccount) -> Result { - let password = match n.password_file { - Some(file) => password_from_file(file)?, - None => password_prompt()?, - }; - - let dir = Box::new(keys_dir(n.path, n.spec)?); - let secret_store = Box::new(secret_store(dir, Some(n.iterations))?); - let acc_provider = AccountProvider::new(secret_store, AccountProviderSettings::default()); - let new_account = acc_provider.new_account(&password).map_err(|e| format!("Could not create new account: {}", e))?; - Ok(format!("0x{:x}", new_account)) - } - - fn list(list_cmd: ListAccounts) -> Result { - let dir = Box::new(keys_dir(list_cmd.path, list_cmd.spec)?); - let secret_store = Box::new(secret_store(dir, None)?); - let acc_provider = AccountProvider::new(secret_store, AccountProviderSettings::default()); - let accounts = acc_provider.accounts().map_err(|e| format!("{}", e))?; - let result = accounts.into_iter() - .map(|a| format!("0x{:x}", a)) - .collect::>() - .join("\n"); - - Ok(result) - } - - fn import(i: ImportAccounts) -> Result { - let to = keys_dir(i.to, i.spec)?; - let mut imported = 0; - - for path in &i.from { - let path = PathBuf::from(path); - if path.is_dir() { - let from = RootDiskDirectory::at(&path); - imported += import_accounts(&from, &to).map_err(|e| format!("Importing accounts from {:?} failed: {}", path, e))?.len(); - } else if path.is_file() { - import_account(&path, &to).map_err(|e| format!("Importing account from {:?} failed: {}", path, e))?; - imported += 1; - } - } - - Ok(format!("{} account(s) imported", imported)) - } - - fn import_geth(i: ImportFromGethAccounts) -> Result { - use std::io::ErrorKind; - use ethstore::Error; - - let dir = Box::new(keys_dir(i.to, i.spec)?); - let secret_store = Box::new(secret_store(dir, None)?); - let geth_accounts = read_geth_accounts(i.testnet); - match secret_store.import_geth_accounts(SecretVaultRef::Root, geth_accounts, i.testnet) { - Ok(v) => Ok(format!("Successfully imported {} account(s) from geth.", v.len())), - Err(Error::Io(ref io_err)) if io_err.kind() == ErrorKind::NotFound => Err("Failed to find geth keys folder.".into()), - Err(err) => Err(format!("Import geth accounts failed. {}", err)) - } - } + use super::*; + use accounts::{AccountProvider, AccountProviderSettings}; + use ethstore::{accounts_dir::RootDiskDirectory, import_account, import_accounts, EthStore}; + use helpers::{password_from_file, password_prompt}; + use std::path::PathBuf; + + pub fn execute(cmd: AccountCmd) -> Result { + match cmd { + AccountCmd::New(new_cmd) => new(new_cmd), + AccountCmd::List(list_cmd) => list(list_cmd), + AccountCmd::Import(import_cmd) => import(import_cmd), + } + } + + fn keys_dir(path: String, spec: SpecType) -> Result { + let spec = spec.spec(&::std::env::temp_dir())?; + let mut path = PathBuf::from(&path); + path.push(spec.data_dir); + RootDiskDirectory::create(path).map_err(|e| format!("Could not open keys directory: {}", e)) + } + + fn secret_store( + dir: Box, + iterations: Option, + ) -> Result { + match iterations { + Some(i) => EthStore::open_with_iterations(dir, i), + _ => EthStore::open(dir), + } + .map_err(|e| format!("Could not open keys store: {}", e)) + } + + fn new(n: NewAccount) -> Result { + let password = match n.password_file { + Some(file) => password_from_file(file)?, + None => password_prompt()?, + }; + + let dir = Box::new(keys_dir(n.path, n.spec)?); + let secret_store = Box::new(secret_store(dir, Some(n.iterations))?); + let acc_provider = AccountProvider::new(secret_store, AccountProviderSettings::default()); + let new_account = acc_provider + .new_account(&password) + .map_err(|e| format!("Could not create new account: {}", e))?; + Ok(format!("0x{:x}", new_account)) + } + + fn list(list_cmd: ListAccounts) -> Result { + let dir = Box::new(keys_dir(list_cmd.path, list_cmd.spec)?); + let secret_store = Box::new(secret_store(dir, None)?); + let acc_provider = AccountProvider::new(secret_store, AccountProviderSettings::default()); + let accounts = acc_provider.accounts().map_err(|e| format!("{}", e))?; + let result = accounts + .into_iter() + .map(|a| format!("0x{:x}", a)) + .collect::>() + .join("\n"); + + Ok(result) + } + + fn import(i: ImportAccounts) -> Result { + let to = keys_dir(i.to, i.spec)?; + let mut imported = 0; + + for path in &i.from { + let path = PathBuf::from(path); + if path.is_dir() { + let from = RootDiskDirectory::at(&path); + imported += import_accounts(&from, &to) + .map_err(|e| format!("Importing accounts from {:?} failed: {}", path, e))? + .len(); + } else if path.is_file() { + import_account(&path, &to) + .map_err(|e| format!("Importing account from {:?} failed: {}", path, e))?; + imported += 1; + } + } + + Ok(format!("{} account(s) imported", imported)) + } } #[cfg(feature = "accounts")] diff --git a/parity/account_utils.rs b/parity/account_utils.rs index 6c11ae23b34..36a91a06e1a 100644 --- a/parity/account_utils.rs +++ b/parity/account_utils.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. -// This file is part of Parity. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity. If not, see . +// along with OpenEthereum. If not, see . use std::sync::Arc; @@ -20,225 +20,238 @@ use dir::Directories; use ethereum_types::Address; use ethkey::Password; -use params::{SpecType, AccountsConfig}; +use params::{AccountsConfig, SpecType}; #[cfg(not(feature = "accounts"))] mod accounts { - use super::*; - - /// Dummy AccountProvider - pub struct AccountProvider; - - impl ::ethcore::miner::LocalAccounts for AccountProvider { - fn is_local(&self, _address: &Address) -> bool { - false - } - } - - pub fn prepare_account_provider(_spec: &SpecType, _dirs: &Directories, _data_dir: &str, _cfg: AccountsConfig, _passwords: &[Password]) -> Result { - warn!("Note: Your instance of Parity Ethereum is running without account support. Some CLI options are ignored."); - Ok(AccountProvider) - } - - pub fn miner_local_accounts(_: Arc) -> AccountProvider { - AccountProvider - } - - pub fn miner_author(_spec: &SpecType, _dirs: &Directories, _account_provider: &Arc, _engine_signer: Address, _passwords: &[Password]) -> Result, String> { - Ok(None) - } - - pub fn private_tx_signer(_account_provider: Arc, _passwords: &[Password]) -> Result, String> { - Ok(Arc::new(::ethcore_private_tx::DummySigner)) - } - - pub fn accounts_list(_account_provider: Arc) -> Arc Vec

+ Send + Sync> { - Arc::new(|| vec![]) - } + use super::*; + + /// Dummy AccountProvider + pub struct AccountProvider; + + impl ::ethcore::miner::LocalAccounts for AccountProvider { + fn is_local(&self, _address: &Address) -> bool { + false + } + } + + pub fn prepare_account_provider( + _spec: &SpecType, + _dirs: &Directories, + _data_dir: &str, + _cfg: AccountsConfig, + _passwords: &[Password], + ) -> Result { + warn!("Note: Your instance of OpenEthereum is running without account support. Some CLI options are ignored."); + Ok(AccountProvider) + } + + pub fn miner_local_accounts(_: Arc) -> AccountProvider { + AccountProvider + } + + pub fn miner_author( + _spec: &SpecType, + _dirs: &Directories, + _account_provider: &Arc, + _engine_signer: Address, + _passwords: &[Password], + ) -> Result, String> { + Ok(None) + } + + pub fn accounts_list( + _account_provider: Arc, + ) -> Arc Vec
+ Send + Sync> { + Arc::new(|| vec![]) + } } #[cfg(feature = "accounts")] mod accounts { - use super::*; - use upgrade::upgrade_key_location; - - pub use accounts::AccountProvider; - - /// Pops along with error messages when a password is missing or invalid. - const VERIFY_PASSWORD_HINT: &str = "Make sure valid password is present in files passed using `--password` or in the configuration file."; - - /// Initialize account provider - pub fn prepare_account_provider(spec: &SpecType, dirs: &Directories, data_dir: &str, cfg: AccountsConfig, passwords: &[Password]) -> Result { - use ethstore::EthStore; - use ethstore::accounts_dir::RootDiskDirectory; - use accounts::AccountProviderSettings; - - let path = dirs.keys_path(data_dir); - upgrade_key_location(&dirs.legacy_keys_path(cfg.testnet), &path); - let dir = Box::new(RootDiskDirectory::create(&path).map_err(|e| format!("Could not open keys directory: {}", e))?); - let account_settings = AccountProviderSettings { - enable_hardware_wallets: cfg.enable_hardware_wallets, - hardware_wallet_classic_key: spec == &SpecType::Classic, - unlock_keep_secret: cfg.enable_fast_unlock, - blacklisted_accounts: match *spec { - SpecType::Morden | SpecType::Ropsten | SpecType::Kovan | SpecType::Sokol | SpecType::Dev => vec![], - _ => vec![ - "00a329c0648769a73afac7f9381e08fb43dbea72".into() - ], - }, - }; - - let ethstore = EthStore::open_with_iterations(dir, cfg.iterations).map_err(|e| format!("Could not open keys directory: {}", e))?; - if cfg.refresh_time > 0 { - ethstore.set_refresh_time(::std::time::Duration::from_secs(cfg.refresh_time)); - } - let account_provider = AccountProvider::new( - Box::new(ethstore), - account_settings, - ); - - // Add development account if running dev chain: - if let SpecType::Dev = *spec { - insert_dev_account(&account_provider); - } - - for a in cfg.unlocked_accounts { - // Check if the account exists - if !account_provider.has_account(a) { - return Err(format!("Account {} not found for the current chain. {}", a, build_create_account_hint(spec, &dirs.keys))); - } - - // Check if any passwords have been read from the password file(s) - if passwords.is_empty() { - return Err(format!("No password found to unlock account {}. {}", a, VERIFY_PASSWORD_HINT)); - } - - if !passwords.iter().any(|p| account_provider.unlock_account_permanently(a, (*p).clone()).is_ok()) { - return Err(format!("No valid password to unlock account {}. {}", a, VERIFY_PASSWORD_HINT)); - } - } - - Ok(account_provider) - } - - pub struct LocalAccounts(Arc); - impl ::ethcore::miner::LocalAccounts for LocalAccounts { - fn is_local(&self, address: &Address) -> bool { - self.0.has_account(*address) - } - } - - pub fn miner_local_accounts(account_provider: Arc) -> LocalAccounts { - LocalAccounts(account_provider) - } - - pub fn miner_author(spec: &SpecType, dirs: &Directories, account_provider: &Arc, engine_signer: Address, passwords: &[Password]) -> Result, String> { - use ethcore::engines::EngineSigner; - - // Check if engine signer exists - if !account_provider.has_account(engine_signer) { - return Err(format!("Consensus signer account not found for the current chain. {}", build_create_account_hint(spec, &dirs.keys))); - } - - // Check if any passwords have been read from the password file(s) - if passwords.is_empty() { - return Err(format!("No password found for the consensus signer {}. {}", engine_signer, VERIFY_PASSWORD_HINT)); - } - - let mut author = None; - for password in passwords { - let signer = parity_rpc::signer::EngineSigner::new( - account_provider.clone(), - engine_signer, - password.clone(), - ); - if signer.sign(Default::default()).is_ok() { - author = Some(::ethcore::miner::Author::Sealer(Box::new(signer))); - } - } - if author.is_none() { - return Err(format!("No valid password for the consensus signer {}. {}", engine_signer, VERIFY_PASSWORD_HINT)); - } - - Ok(author) - } - - - mod private_tx { - use super::*; - use ethkey::{Signature, Message}; - use ethcore_private_tx::{Error}; - - pub struct AccountSigner { - pub accounts: Arc, - pub passwords: Vec, - } - - impl ::ethcore_private_tx::Signer for AccountSigner { - fn decrypt(&self, account: Address, shared_mac: &[u8], payload: &[u8]) -> Result, Error> { - let password = self.find_account_password(&account); - Ok(self.accounts.decrypt(account, password, shared_mac, payload).map_err(|e| e.to_string())?) - } - - fn sign(&self, account: Address, hash: Message) -> Result { - let password = self.find_account_password(&account); - Ok(self.accounts.sign(account, password, hash).map_err(|e| e.to_string())?) - } - } - - impl AccountSigner { - /// Try to unlock account using stored password, return found password if any - fn find_account_password(&self, account: &Address) -> Option { - for password in &self.passwords { - if let Ok(true) = self.accounts.test_password(account, password) { - return Some(password.clone()); - } - } - None - } - } - } - - pub fn private_tx_signer(accounts: Arc, passwords: &[Password]) -> Result, String> { - Ok(Arc::new(self::private_tx::AccountSigner { - accounts, - passwords: passwords.to_vec(), - })) - } - - pub fn accounts_list(account_provider: Arc) -> Arc Vec
+ Send + Sync> { - Arc::new(move || account_provider.accounts().unwrap_or_default()) - } - - fn insert_dev_account(account_provider: &AccountProvider) { - let secret: ethkey::Secret = "4d5db4107d237df6a3d58ee5f70ae63d73d7658d4026f2eefd2f204c81682cb7".into(); - let dev_account = ethkey::KeyPair::from_secret(secret.clone()).expect("Valid secret produces valid key;qed"); - if !account_provider.has_account(dev_account.address()) { - match account_provider.insert_account(secret, &Password::from(String::new())) { - Err(e) => warn!("Unable to add development account: {}", e), - Ok(address) => { - let _ = account_provider.set_account_name(address.clone(), "Development Account".into()); - let _ = account_provider.set_account_meta(address, ::serde_json::to_string(&(vec![ - ("description", "Never use this account outside of development chain!"), - ("passwordHint","Password is empty string"), - ].into_iter().collect::<::std::collections::HashMap<_,_>>())).expect("Serialization of hashmap does not fail.")); - }, - } - } - } - - // Construct an error `String` with an adaptive hint on how to create an account. - fn build_create_account_hint(spec: &SpecType, keys: &str) -> String { - format!("You can create an account via RPC, UI or `parity account new --chain {} --keys-path {}`.", spec, keys) - } + use super::*; + use upgrade::upgrade_key_location; + + pub use accounts::AccountProvider; + + /// Pops along with error messages when a password is missing or invalid. + const VERIFY_PASSWORD_HINT: &str = "Make sure valid password is present in files passed using `--password` or in the configuration file."; + + /// Initialize account provider + pub fn prepare_account_provider( + spec: &SpecType, + dirs: &Directories, + data_dir: &str, + cfg: AccountsConfig, + passwords: &[Password], + ) -> Result { + use accounts::AccountProviderSettings; + use ethstore::{accounts_dir::RootDiskDirectory, EthStore}; + + let path = dirs.keys_path(data_dir); + upgrade_key_location(&dirs.legacy_keys_path(cfg.testnet), &path); + let dir = Box::new( + RootDiskDirectory::create(&path) + .map_err(|e| format!("Could not open keys directory: {}", e))?, + ); + let account_settings = AccountProviderSettings { + unlock_keep_secret: cfg.enable_fast_unlock, + blacklisted_accounts: match *spec { + SpecType::Morden + | SpecType::Ropsten + | SpecType::Kovan + | SpecType::Goerli + | SpecType::Sokol + | SpecType::Dev => vec![], + _ => vec!["00a329c0648769a73afac7f9381e08fb43dbea72".into()], + }, + }; + + let ethstore = EthStore::open_with_iterations(dir, cfg.iterations) + .map_err(|e| format!("Could not open keys directory: {}", e))?; + if cfg.refresh_time > 0 { + ethstore.set_refresh_time(::std::time::Duration::from_secs(cfg.refresh_time)); + } + let account_provider = AccountProvider::new(Box::new(ethstore), account_settings); + + // Add development account if running dev chain: + if let SpecType::Dev = *spec { + insert_dev_account(&account_provider); + } + + for a in cfg.unlocked_accounts { + // Check if the account exists + if !account_provider.has_account(a) { + return Err(format!( + "Account {} not found for the current chain. {}", + a, + build_create_account_hint(spec, &dirs.keys) + )); + } + + // Check if any passwords have been read from the password file(s) + if passwords.is_empty() { + return Err(format!( + "No password found to unlock account {}. {}", + a, VERIFY_PASSWORD_HINT + )); + } + + if !passwords.iter().any(|p| { + account_provider + .unlock_account_permanently(a, (*p).clone()) + .is_ok() + }) { + return Err(format!( + "No valid password to unlock account {}. {}", + a, VERIFY_PASSWORD_HINT + )); + } + } + + Ok(account_provider) + } + + pub struct LocalAccounts(Arc); + impl ::ethcore::miner::LocalAccounts for LocalAccounts { + fn is_local(&self, address: &Address) -> bool { + self.0.has_account(*address) + } + } + + pub fn miner_local_accounts(account_provider: Arc) -> LocalAccounts { + LocalAccounts(account_provider) + } + + pub fn miner_author( + spec: &SpecType, + dirs: &Directories, + account_provider: &Arc, + engine_signer: Address, + passwords: &[Password], + ) -> Result, String> { + use ethcore::engines::EngineSigner; + + // Check if engine signer exists + if !account_provider.has_account(engine_signer) { + return Err(format!( + "Consensus signer account not found for the current chain. {}", + build_create_account_hint(spec, &dirs.keys) + )); + } + + // Check if any passwords have been read from the password file(s) + if passwords.is_empty() { + return Err(format!( + "No password found for the consensus signer {}. {}", + engine_signer, VERIFY_PASSWORD_HINT + )); + } + + let mut author = None; + for password in passwords { + let signer = parity_rpc::signer::EngineSigner::new( + account_provider.clone(), + engine_signer, + password.clone(), + ); + if signer.sign(Default::default()).is_ok() { + author = Some(::ethcore::miner::Author::Sealer(Box::new(signer))); + } + } + if author.is_none() { + return Err(format!( + "No valid password for the consensus signer {}. {}", + engine_signer, VERIFY_PASSWORD_HINT + )); + } + + Ok(author) + } + + pub fn accounts_list( + account_provider: Arc, + ) -> Arc Vec
+ Send + Sync> { + Arc::new(move || account_provider.accounts().unwrap_or_default()) + } + + fn insert_dev_account(account_provider: &AccountProvider) { + let secret: ethkey::Secret = + "4d5db4107d237df6a3d58ee5f70ae63d73d7658d4026f2eefd2f204c81682cb7".into(); + let dev_account = ethkey::KeyPair::from_secret(secret.clone()) + .expect("Valid secret produces valid key;qed"); + if !account_provider.has_account(dev_account.address()) { + match account_provider.insert_account(secret, &Password::from(String::new())) { + Err(e) => warn!("Unable to add development account: {}", e), + Ok(address) => { + let _ = account_provider + .set_account_name(address.clone(), "Development Account".into()); + let _ = account_provider.set_account_meta( + address, + ::serde_json::to_string( + &(vec![ + ( + "description", + "Never use this account outside of development chain!", + ), + ("passwordHint", "Password is empty string"), + ] + .into_iter() + .collect::<::std::collections::HashMap<_, _>>()), + ) + .expect("Serialization of hashmap does not fail."), + ); + } + } + } + } + + // Construct an error `String` with an adaptive hint on how to create an account. + fn build_create_account_hint(spec: &SpecType, keys: &str) -> String { + format!("You can create an account via RPC, UI or `openethereum account new --chain {} --keys-path {}`.", spec, keys) + } } pub use self::accounts::{ - AccountProvider, - prepare_account_provider, - miner_local_accounts, - miner_author, - private_tx_signer, - accounts_list, + accounts_list, miner_author, miner_local_accounts, prepare_account_provider, AccountProvider, }; - diff --git a/parity/blockchain.rs b/parity/blockchain.rs index 262b98ae2c5..f101aa86906 100644 --- a/parity/blockchain.rs +++ b/parity/blockchain.rs @@ -1,499 +1,257 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::str::{FromStr, from_utf8}; -use std::{io, fs}; -use std::io::{BufReader, BufRead}; -use std::time::{Instant, Duration}; -use std::thread::sleep; -use std::sync::Arc; -use rustc_hex::FromHex; -use hash::{keccak, KECCAK_NULL_RLP}; -use ethereum_types::{U256, H256, Address}; +// along with OpenEthereum. If not, see . + +use std::{fs, io, sync::Arc, time::Instant}; + +use ansi_term::Colour; use bytes::ToPretty; -use rlp::PayloadInfo; -use ethcore::client::{ - Mode, DatabaseCompactionProfile, VMType, Nonce, Balance, BlockChainClient, BlockId, BlockInfo, ImportBlock, BlockChainReset -}; -use ethcore::error::{ImportErrorKind, ErrorKind as EthcoreErrorKind, Error as EthcoreError}; -use ethcore::miner::Miner; -use ethcore::verification::queue::VerifierSettings; -use ethcore::verification::queue::kind::blocks::Unverified; -use ethcore_service::ClientService; use cache::CacheConfig; -use informant::{Informant, FullNodeInformantData, MillisecondDuration}; -use params::{SpecType, Pruning, Switch, tracing_switch_to_bool, fatdb_switch_to_bool}; -use helpers::{to_client_config, execute_upgrades}; +use db; use dir::Directories; +use ethcore::{ + client::{ + Balance, BlockChainClient, BlockChainReset, BlockId, DatabaseCompactionProfile, + ImportExportBlocks, Mode, Nonce, VMType, + }, + miner::Miner, + verification::queue::VerifierSettings, +}; +use ethcore_service::ClientService; +use ethereum_types::{Address, H256, U256}; +use hash::{keccak, KECCAK_NULL_RLP}; +use helpers::{execute_upgrades, to_client_config}; +use informant::{FullNodeInformantData, Informant, MillisecondDuration}; +use params::{fatdb_switch_to_bool, tracing_switch_to_bool, Pruning, SpecType, Switch}; +use types::data_format::DataFormat; use user_defaults::UserDefaults; -use ethcore_private_tx; -use db; -use ansi_term::Colour; - -#[derive(Debug, PartialEq)] -pub enum DataFormat { - Hex, - Binary, -} - -impl Default for DataFormat { - fn default() -> Self { - DataFormat::Binary - } -} - -impl FromStr for DataFormat { - type Err = String; - - fn from_str(s: &str) -> Result { - match s { - "binary" | "bin" => Ok(DataFormat::Binary), - "hex" => Ok(DataFormat::Hex), - x => Err(format!("Invalid format: {}", x)) - } - } -} #[derive(Debug, PartialEq)] pub enum BlockchainCmd { - Kill(KillBlockchain), - Import(ImportBlockchain), - Export(ExportBlockchain), - ExportState(ExportState), - Reset(ResetBlockchain) + Kill(KillBlockchain), + Import(ImportBlockchain), + Export(ExportBlockchain), + ExportState(ExportState), + Reset(ResetBlockchain), } #[derive(Debug, PartialEq)] pub struct ResetBlockchain { - pub dirs: Directories, - pub spec: SpecType, - pub pruning: Pruning, - pub pruning_history: u64, - pub pruning_memory: usize, - pub tracing: Switch, - pub fat_db: Switch, - pub compaction: DatabaseCompactionProfile, - pub cache_config: CacheConfig, - pub num: u32, + pub dirs: Directories, + pub spec: SpecType, + pub pruning: Pruning, + pub pruning_history: u64, + pub pruning_memory: usize, + pub tracing: Switch, + pub fat_db: Switch, + pub compaction: DatabaseCompactionProfile, + pub cache_config: CacheConfig, + pub num: u32, } #[derive(Debug, PartialEq)] pub struct KillBlockchain { - pub spec: SpecType, - pub dirs: Directories, - pub pruning: Pruning, + pub spec: SpecType, + pub dirs: Directories, + pub pruning: Pruning, } #[derive(Debug, PartialEq)] pub struct ImportBlockchain { - pub spec: SpecType, - pub cache_config: CacheConfig, - pub dirs: Directories, - pub file_path: Option, - pub format: Option, - pub pruning: Pruning, - pub pruning_history: u64, - pub pruning_memory: usize, - pub compaction: DatabaseCompactionProfile, - pub tracing: Switch, - pub fat_db: Switch, - pub vm_type: VMType, - pub check_seal: bool, - pub with_color: bool, - pub verifier_settings: VerifierSettings, - pub light: bool, - pub max_round_blocks_to_import: usize, + pub spec: SpecType, + pub cache_config: CacheConfig, + pub dirs: Directories, + pub file_path: Option, + pub format: Option, + pub pruning: Pruning, + pub pruning_history: u64, + pub pruning_memory: usize, + pub compaction: DatabaseCompactionProfile, + pub tracing: Switch, + pub fat_db: Switch, + pub vm_type: VMType, + pub check_seal: bool, + pub with_color: bool, + pub verifier_settings: VerifierSettings, + pub max_round_blocks_to_import: usize, } #[derive(Debug, PartialEq)] pub struct ExportBlockchain { - pub spec: SpecType, - pub cache_config: CacheConfig, - pub dirs: Directories, - pub file_path: Option, - pub format: Option, - pub pruning: Pruning, - pub pruning_history: u64, - pub pruning_memory: usize, - pub compaction: DatabaseCompactionProfile, - pub fat_db: Switch, - pub tracing: Switch, - pub from_block: BlockId, - pub to_block: BlockId, - pub check_seal: bool, - pub max_round_blocks_to_import: usize, + pub spec: SpecType, + pub cache_config: CacheConfig, + pub dirs: Directories, + pub file_path: Option, + pub format: Option, + pub pruning: Pruning, + pub pruning_history: u64, + pub pruning_memory: usize, + pub compaction: DatabaseCompactionProfile, + pub fat_db: Switch, + pub tracing: Switch, + pub from_block: BlockId, + pub to_block: BlockId, + pub check_seal: bool, + pub max_round_blocks_to_import: usize, } #[derive(Debug, PartialEq)] pub struct ExportState { - pub spec: SpecType, - pub cache_config: CacheConfig, - pub dirs: Directories, - pub file_path: Option, - pub format: Option, - pub pruning: Pruning, - pub pruning_history: u64, - pub pruning_memory: usize, - pub compaction: DatabaseCompactionProfile, - pub fat_db: Switch, - pub tracing: Switch, - pub at: BlockId, - pub storage: bool, - pub code: bool, - pub min_balance: Option, - pub max_balance: Option, - pub max_round_blocks_to_import: usize, + pub spec: SpecType, + pub cache_config: CacheConfig, + pub dirs: Directories, + pub file_path: Option, + pub format: Option, + pub pruning: Pruning, + pub pruning_history: u64, + pub pruning_memory: usize, + pub compaction: DatabaseCompactionProfile, + pub fat_db: Switch, + pub tracing: Switch, + pub at: BlockId, + pub storage: bool, + pub code: bool, + pub min_balance: Option, + pub max_balance: Option, + pub max_round_blocks_to_import: usize, } pub fn execute(cmd: BlockchainCmd) -> Result<(), String> { - match cmd { - BlockchainCmd::Kill(kill_cmd) => kill_db(kill_cmd), - BlockchainCmd::Import(import_cmd) => { - if import_cmd.light { - execute_import_light(import_cmd) - } else { - execute_import(import_cmd) - } - } - BlockchainCmd::Export(export_cmd) => execute_export(export_cmd), - BlockchainCmd::ExportState(export_cmd) => execute_export_state(export_cmd), - BlockchainCmd::Reset(reset_cmd) => execute_reset(reset_cmd), - } -} - -fn execute_import_light(cmd: ImportBlockchain) -> Result<(), String> { - use light::client::{Service as LightClientService, Config as LightClientConfig}; - use light::cache::Cache as LightDataCache; - use parking_lot::Mutex; - - let timer = Instant::now(); - - // load spec file - let spec = cmd.spec.spec(&cmd.dirs.cache)?; - - // load genesis hash - let genesis_hash = spec.genesis_header().hash(); - - // database paths - let db_dirs = cmd.dirs.database(genesis_hash, None, spec.data_dir.clone()); - - // user defaults path - let user_defaults_path = db_dirs.user_defaults_path(); - - // load user defaults - let user_defaults = UserDefaults::load(&user_defaults_path)?; - - // select pruning algorithm - let algorithm = cmd.pruning.to_algorithm(&user_defaults); - - // prepare client and snapshot paths. - let client_path = db_dirs.client_path(algorithm); - - // execute upgrades - execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?; - - // create dirs used by parity - cmd.dirs.create_dirs(false, false)?; - - let cache = Arc::new(Mutex::new( - LightDataCache::new(Default::default(), Duration::new(0, 0)) - )); - - let mut config = LightClientConfig { - queue: Default::default(), - chain_column: ethcore_db::COL_LIGHT_CHAIN, - verify_full: true, - check_seal: cmd.check_seal, - no_hardcoded_sync: true, - }; - - config.queue.max_mem_use = cmd.cache_config.queue() as usize * 1024 * 1024; - config.queue.verifier_settings = cmd.verifier_settings; - - // initialize database. - let db = db::open_db(&client_path.to_str().expect("DB path could not be converted to string."), - &cmd.cache_config, - &cmd.compaction).map_err(|e| format!("Failed to open database: {:?}", e))?; - - // TODO: could epoch signals be avilable at the end of the file? - let fetch = ::light::client::fetch::unavailable(); - let service = LightClientService::start(config, &spec, fetch, db, cache) - .map_err(|e| format!("Failed to start client: {}", e))?; - - // free up the spec in memory. - drop(spec); - - let client = service.client(); - - let mut instream: Box = match cmd.file_path { - Some(f) => Box::new(fs::File::open(&f).map_err(|_| format!("Cannot open given file: {}", f))?), - None => Box::new(io::stdin()), - }; - - const READAHEAD_BYTES: usize = 8; - - let mut first_bytes: Vec = vec![0; READAHEAD_BYTES]; - let mut first_read = 0; - - let format = match cmd.format { - Some(format) => format, - None => { - first_read = instream.read(&mut first_bytes).map_err(|_| "Error reading from the file/stream.")?; - match first_bytes[0] { - 0xf9 => DataFormat::Binary, - _ => DataFormat::Hex, - } - } - }; - - let do_import = |bytes: Vec| { - while client.queue_info().is_full() { sleep(Duration::from_secs(1)); } - - let header: ::types::header::Header = ::rlp::Rlp::new(&bytes).val_at(0) - .map_err(|e| format!("Bad block: {}", e))?; - - if client.best_block_header().number() >= header.number() { return Ok(()) } - - if header.number() % 10000 == 0 { - info!("#{}", header.number()); - } - - match client.import_header(header) { - Err(EthcoreError(EthcoreErrorKind::Import(ImportErrorKind::AlreadyInChain), _)) => { - trace!("Skipping block already in chain."); - } - Err(e) => { - return Err(format!("Cannot import block: {:?}", e)); - }, - Ok(_) => {}, - } - Ok(()) - }; - - match format { - DataFormat::Binary => { - loop { - let mut bytes = if first_read > 0 {first_bytes.clone()} else {vec![0; READAHEAD_BYTES]}; - let n = if first_read > 0 { - first_read - } else { - instream.read(&mut bytes).map_err(|_| "Error reading from the file/stream.")? - }; - if n == 0 { break; } - first_read = 0; - let s = PayloadInfo::from(&bytes).map_err(|e| format!("Invalid RLP in the file/stream: {:?}", e))?.total(); - bytes.resize(s, 0); - instream.read_exact(&mut bytes[n..]).map_err(|_| "Error reading from the file/stream.")?; - do_import(bytes)?; - } - } - DataFormat::Hex => { - for line in BufReader::new(instream).lines() { - let s = line.map_err(|_| "Error reading from the file/stream.")?; - let s = if first_read > 0 {from_utf8(&first_bytes).unwrap().to_owned() + &(s[..])} else {s}; - first_read = 0; - let bytes = s.from_hex().map_err(|_| "Invalid hex in file/stream.")?; - do_import(bytes)?; - } - } - } - client.flush_queue(); - - let ms = timer.elapsed().as_milliseconds(); - let report = client.report(); - - info!("Import completed in {} seconds, {} headers, {} hdr/s", - ms / 1000, - report.blocks_imported, - (report.blocks_imported * 1000) as u64 / ms, - ); - - Ok(()) + match cmd { + BlockchainCmd::Kill(kill_cmd) => kill_db(kill_cmd), + BlockchainCmd::Import(import_cmd) => execute_import(import_cmd), + BlockchainCmd::Export(export_cmd) => execute_export(export_cmd), + BlockchainCmd::ExportState(export_cmd) => execute_export_state(export_cmd), + BlockchainCmd::Reset(reset_cmd) => execute_reset(reset_cmd), + } } fn execute_import(cmd: ImportBlockchain) -> Result<(), String> { - let timer = Instant::now(); - - // load spec file - let spec = cmd.spec.spec(&cmd.dirs.cache)?; - - // load genesis hash - let genesis_hash = spec.genesis_header().hash(); - - // database paths - let db_dirs = cmd.dirs.database(genesis_hash, None, spec.data_dir.clone()); - - // user defaults path - let user_defaults_path = db_dirs.user_defaults_path(); - - // load user defaults - let mut user_defaults = UserDefaults::load(&user_defaults_path)?; - - // select pruning algorithm - let algorithm = cmd.pruning.to_algorithm(&user_defaults); - - // check if tracing is on - let tracing = tracing_switch_to_bool(cmd.tracing, &user_defaults)?; - - // check if fatdb is on - let fat_db = fatdb_switch_to_bool(cmd.fat_db, &user_defaults, algorithm)?; - - // prepare client and snapshot paths. - let client_path = db_dirs.client_path(algorithm); - let snapshot_path = db_dirs.snapshot_path(); - - // execute upgrades - execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?; - - // create dirs used by parity - cmd.dirs.create_dirs(false, false)?; - - // prepare client config - let mut client_config = to_client_config( - &cmd.cache_config, - spec.name.to_lowercase(), - Mode::Active, - tracing, - fat_db, - cmd.compaction, - cmd.vm_type, - "".into(), - algorithm, - cmd.pruning_history, - cmd.pruning_memory, - cmd.check_seal, - 12, - ); - - client_config.queue.verifier_settings = cmd.verifier_settings; - - let restoration_db_handler = db::restoration_db_handler(&client_path, &client_config); - let client_db = restoration_db_handler.open(&client_path) - .map_err(|e| format!("Failed to open database {:?}", e))?; - - // build client - let service = ClientService::start( - client_config, - &spec, - client_db, - &snapshot_path, - restoration_db_handler, - &cmd.dirs.ipc_path(), - // TODO [ToDr] don't use test miner here - // (actually don't require miner at all) - Arc::new(Miner::new_for_tests(&spec, None)), - Arc::new(ethcore_private_tx::DummySigner), - Box::new(ethcore_private_tx::NoopEncryptor), - Default::default(), - Default::default(), - ).map_err(|e| format!("Client service error: {:?}", e))?; - - // free up the spec in memory. - drop(spec); - - let client = service.client(); - - let mut instream: Box = match cmd.file_path { - Some(f) => Box::new(fs::File::open(&f).map_err(|_| format!("Cannot open given file: {}", f))?), - None => Box::new(io::stdin()), - }; - - const READAHEAD_BYTES: usize = 8; - - let mut first_bytes: Vec = vec![0; READAHEAD_BYTES]; - let mut first_read = 0; - - let format = match cmd.format { - Some(format) => format, - None => { - first_read = instream.read(&mut first_bytes).map_err(|_| "Error reading from the file/stream.")?; - match first_bytes[0] { - 0xf9 => DataFormat::Binary, - _ => DataFormat::Hex, - } - } - }; - - let informant = Arc::new(Informant::new( - FullNodeInformantData { - client: client.clone(), - sync: None, - net: None, - }, - None, - None, - cmd.with_color, - )); - - service.register_io_handler(informant).map_err(|_| "Unable to register informant handler".to_owned())?; - - let do_import = |bytes| { - let block = Unverified::from_rlp(bytes).map_err(|_| "Invalid block rlp")?; - while client.queue_info().is_full() { sleep(Duration::from_secs(1)); } - match client.import_block(block) { - Err(EthcoreError(EthcoreErrorKind::Import(ImportErrorKind::AlreadyInChain), _)) => { - trace!("Skipping block already in chain."); - } - Err(e) => { - return Err(format!("Cannot import block: {:?}", e)); - }, - Ok(_) => {}, - } - Ok(()) - }; - - match format { - DataFormat::Binary => { - loop { - let mut bytes = if first_read > 0 {first_bytes.clone()} else {vec![0; READAHEAD_BYTES]}; - let n = if first_read > 0 { - first_read - } else { - instream.read(&mut bytes).map_err(|_| "Error reading from the file/stream.")? - }; - if n == 0 { break; } - first_read = 0; - let s = PayloadInfo::from(&bytes).map_err(|e| format!("Invalid RLP in the file/stream: {:?}", e))?.total(); - bytes.resize(s, 0); - instream.read_exact(&mut bytes[n..]).map_err(|_| "Error reading from the file/stream.")?; - do_import(bytes)?; - } - } - DataFormat::Hex => { - for line in BufReader::new(instream).lines() { - let s = line.map_err(|_| "Error reading from the file/stream.")?; - let s = if first_read > 0 {from_utf8(&first_bytes).unwrap().to_owned() + &(s[..])} else {s}; - first_read = 0; - let bytes = s.from_hex().map_err(|_| "Invalid hex in file/stream.")?; - do_import(bytes)?; - } - } - } - client.flush_queue(); - - // save user defaults - user_defaults.pruning = algorithm; - user_defaults.tracing = tracing; - user_defaults.fat_db = fat_db; - user_defaults.save(&user_defaults_path)?; - - let report = client.report(); - - let ms = timer.elapsed().as_milliseconds(); - info!("Import completed in {} seconds, {} blocks, {} blk/s, {} transactions, {} tx/s, {} Mgas, {} Mgas/s", + let timer = Instant::now(); + + // load spec file + let spec = cmd.spec.spec(&cmd.dirs.cache)?; + + // load genesis hash + let genesis_hash = spec.genesis_header().hash(); + + // database paths + let db_dirs = cmd.dirs.database(genesis_hash, None, spec.data_dir.clone()); + + // user defaults path + let user_defaults_path = db_dirs.user_defaults_path(); + + // load user defaults + let mut user_defaults = UserDefaults::load(&user_defaults_path)?; + + // select pruning algorithm + let algorithm = cmd.pruning.to_algorithm(&user_defaults); + + // check if tracing is on + let tracing = tracing_switch_to_bool(cmd.tracing, &user_defaults)?; + + // check if fatdb is on + let fat_db = fatdb_switch_to_bool(cmd.fat_db, &user_defaults, algorithm)?; + + // prepare client and snapshot paths. + let client_path = db_dirs.client_path(algorithm); + let snapshot_path = db_dirs.snapshot_path(); + + // execute upgrades + execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?; + + // create dirs used by parity + cmd.dirs.create_dirs(false, false)?; + + // prepare client config + let mut client_config = to_client_config( + &cmd.cache_config, + spec.name.to_lowercase(), + Mode::Active, + tracing, + fat_db, + cmd.compaction, + cmd.vm_type, + "".into(), + algorithm, + cmd.pruning_history, + cmd.pruning_memory, + cmd.check_seal, + 12, + ); + + client_config.queue.verifier_settings = cmd.verifier_settings; + + let restoration_db_handler = db::restoration_db_handler(&client_path, &client_config); + let client_db = restoration_db_handler + .open(&client_path) + .map_err(|e| format!("Failed to open database {:?}", e))?; + + // build client + let service = ClientService::start( + client_config, + &spec, + client_db, + &snapshot_path, + restoration_db_handler, + &cmd.dirs.ipc_path(), + // TODO [ToDr] don't use test miner here + // (actually don't require miner at all) + Arc::new(Miner::new_for_tests(&spec, None)), + ) + .map_err(|e| format!("Client service error: {:?}", e))?; + + // free up the spec in memory. + drop(spec); + + let client = service.client(); + + let instream: Box = match cmd.file_path { + Some(f) => { + Box::new(fs::File::open(&f).map_err(|_| format!("Cannot open given file: {}", f))?) + } + None => Box::new(io::stdin()), + }; + + let informant = Arc::new(Informant::new( + FullNodeInformantData { + client: client.clone(), + sync: None, + net: None, + }, + None, + None, + cmd.with_color, + )); + + service + .register_io_handler(informant) + .map_err(|_| "Unable to register informant handler".to_owned())?; + + client.import_blocks(instream, cmd.format)?; + + // save user defaults + user_defaults.pruning = algorithm; + user_defaults.tracing = tracing; + user_defaults.fat_db = fat_db; + user_defaults.save(&user_defaults_path)?; + + let report = client.report(); + + let ms = timer.elapsed().as_milliseconds(); + info!("Import completed in {} seconds, {} blocks, {} blk/s, {} transactions, {} tx/s, {} Mgas, {} Mgas/s", ms / 1000, report.blocks_imported, (report.blocks_imported * 1000) as u64 / ms, @@ -502,278 +260,290 @@ fn execute_import(cmd: ImportBlockchain) -> Result<(), String> { report.gas_processed / 1_000_000, (report.gas_processed / (ms * 1000)).low_u64(), ); - Ok(()) + Ok(()) } fn start_client( - dirs: Directories, - spec: SpecType, - pruning: Pruning, - pruning_history: u64, - pruning_memory: usize, - tracing: Switch, - fat_db: Switch, - compaction: DatabaseCompactionProfile, - cache_config: CacheConfig, - require_fat_db: bool, - max_round_blocks_to_import: usize, + dirs: Directories, + spec: SpecType, + pruning: Pruning, + pruning_history: u64, + pruning_memory: usize, + tracing: Switch, + fat_db: Switch, + compaction: DatabaseCompactionProfile, + cache_config: CacheConfig, + require_fat_db: bool, + max_round_blocks_to_import: usize, ) -> Result { - - // load spec file - let spec = spec.spec(&dirs.cache)?; - - // load genesis hash - let genesis_hash = spec.genesis_header().hash(); - - // database paths - let db_dirs = dirs.database(genesis_hash, None, spec.data_dir.clone()); - - // user defaults path - let user_defaults_path = db_dirs.user_defaults_path(); - - // load user defaults - let user_defaults = UserDefaults::load(&user_defaults_path)?; - - // select pruning algorithm - let algorithm = pruning.to_algorithm(&user_defaults); - - // check if tracing is on - let tracing = tracing_switch_to_bool(tracing, &user_defaults)?; - - // check if fatdb is on - let fat_db = fatdb_switch_to_bool(fat_db, &user_defaults, algorithm)?; - if !fat_db && require_fat_db { - return Err("This command requires Parity to be synced with --fat-db on.".to_owned()); - } - - // prepare client and snapshot paths. - let client_path = db_dirs.client_path(algorithm); - let snapshot_path = db_dirs.snapshot_path(); - - // execute upgrades - execute_upgrades(&dirs.base, &db_dirs, algorithm, &compaction)?; - - // create dirs used by parity - dirs.create_dirs(false, false)?; - - // prepare client config - let client_config = to_client_config( - &cache_config, - spec.name.to_lowercase(), - Mode::Active, - tracing, - fat_db, - compaction, - VMType::default(), - "".into(), - algorithm, - pruning_history, - pruning_memory, - true, - max_round_blocks_to_import, - ); - - let restoration_db_handler = db::restoration_db_handler(&client_path, &client_config); - let client_db = restoration_db_handler.open(&client_path) - .map_err(|e| format!("Failed to open database {:?}", e))?; - - let service = ClientService::start( - client_config, - &spec, - client_db, - &snapshot_path, - restoration_db_handler, - &dirs.ipc_path(), - // It's fine to use test version here, - // since we don't care about miner parameters at all - Arc::new(Miner::new_for_tests(&spec, None)), - Arc::new(ethcore_private_tx::DummySigner), - Box::new(ethcore_private_tx::NoopEncryptor), - Default::default(), - Default::default(), - ).map_err(|e| format!("Client service error: {:?}", e))?; - - drop(spec); - Ok(service) + // load spec file + let spec = spec.spec(&dirs.cache)?; + + // load genesis hash + let genesis_hash = spec.genesis_header().hash(); + + // database paths + let db_dirs = dirs.database(genesis_hash, None, spec.data_dir.clone()); + + // user defaults path + let user_defaults_path = db_dirs.user_defaults_path(); + + // load user defaults + let user_defaults = UserDefaults::load(&user_defaults_path)?; + + // select pruning algorithm + let algorithm = pruning.to_algorithm(&user_defaults); + + // check if tracing is on + let tracing = tracing_switch_to_bool(tracing, &user_defaults)?; + + // check if fatdb is on + let fat_db = fatdb_switch_to_bool(fat_db, &user_defaults, algorithm)?; + if !fat_db && require_fat_db { + return Err("This command requires OpenEthereum to be synced with --fat-db on.".to_owned()); + } + + // prepare client and snapshot paths. + let client_path = db_dirs.client_path(algorithm); + let snapshot_path = db_dirs.snapshot_path(); + + // execute upgrades + execute_upgrades(&dirs.base, &db_dirs, algorithm, &compaction)?; + + // create dirs used by OpenEthereum. + dirs.create_dirs(false, false)?; + + // prepare client config + let client_config = to_client_config( + &cache_config, + spec.name.to_lowercase(), + Mode::Active, + tracing, + fat_db, + compaction, + VMType::default(), + "".into(), + algorithm, + pruning_history, + pruning_memory, + true, + max_round_blocks_to_import, + ); + + let restoration_db_handler = db::restoration_db_handler(&client_path, &client_config); + let client_db = restoration_db_handler + .open(&client_path) + .map_err(|e| format!("Failed to open database {:?}", e))?; + + let service = ClientService::start( + client_config, + &spec, + client_db, + &snapshot_path, + restoration_db_handler, + &dirs.ipc_path(), + // It's fine to use test version here, + // since we don't care about miner parameters at all + Arc::new(Miner::new_for_tests(&spec, None)), + ) + .map_err(|e| format!("Client service error: {:?}", e))?; + + drop(spec); + Ok(service) } fn execute_export(cmd: ExportBlockchain) -> Result<(), String> { - let service = start_client( - cmd.dirs, - cmd.spec, - cmd.pruning, - cmd.pruning_history, - cmd.pruning_memory, - cmd.tracing, - cmd.fat_db, - cmd.compaction, - cmd.cache_config, - false, - cmd.max_round_blocks_to_import, - )?; - let format = cmd.format.unwrap_or_default(); - - let client = service.client(); - - let mut out: Box = match cmd.file_path { - Some(f) => Box::new(fs::File::create(&f).map_err(|_| format!("Cannot write to file given: {}", f))?), - None => Box::new(io::stdout()), - }; - - let from = client.block_number(cmd.from_block).ok_or("From block could not be found")?; - let to = client.block_number(cmd.to_block).ok_or("To block could not be found")?; - - for i in from..(to + 1) { - if i % 10000 == 0 { - info!("#{}", i); - } - let b = client.block(BlockId::Number(i)).ok_or("Error exporting incomplete chain")?.into_inner(); - match format { - DataFormat::Binary => { - out.write(&b).map_err(|e| format!("Couldn't write to stream. Cause: {}", e))?; - } - DataFormat::Hex => { - out.write_fmt(format_args!("{}", b.pretty())).map_err(|e| format!("Couldn't write to stream. Cause: {}", e))?; - } - } - } - - info!("Export completed."); - Ok(()) + let service = start_client( + cmd.dirs, + cmd.spec, + cmd.pruning, + cmd.pruning_history, + cmd.pruning_memory, + cmd.tracing, + cmd.fat_db, + cmd.compaction, + cmd.cache_config, + false, + cmd.max_round_blocks_to_import, + )?; + let client = service.client(); + + let out: Box = match cmd.file_path { + Some(f) => Box::new( + fs::File::create(&f).map_err(|_| format!("Cannot write to file given: {}", f))?, + ), + None => Box::new(io::stdout()), + }; + + client.export_blocks(out, cmd.from_block, cmd.to_block, cmd.format)?; + + info!("Export completed."); + Ok(()) } fn execute_export_state(cmd: ExportState) -> Result<(), String> { - let service = start_client( - cmd.dirs, - cmd.spec, - cmd.pruning, - cmd.pruning_history, - cmd.pruning_memory, - cmd.tracing, - cmd.fat_db, - cmd.compaction, - cmd.cache_config, - true, - cmd.max_round_blocks_to_import, - )?; - - let client = service.client(); - - let mut out: Box = match cmd.file_path { - Some(f) => Box::new(fs::File::create(&f).map_err(|_| format!("Cannot write to file given: {}", f))?), - None => Box::new(io::stdout()), - }; - - let mut last: Option
= None; - let at = cmd.at; - let mut i = 0usize; - - out.write_fmt(format_args!("{{ \"state\": {{", )).expect("Couldn't write to stream."); - loop { - let accounts = client.list_accounts(at, last.as_ref(), 1000).ok_or("Specified block not found")?; - if accounts.is_empty() { - break; - } - - for account in accounts.into_iter() { - let balance = client.balance(&account, at.into()).unwrap_or_else(U256::zero); - if cmd.min_balance.map_or(false, |m| balance < m) || cmd.max_balance.map_or(false, |m| balance > m) { - last = Some(account); - continue; //filtered out - } - - if i != 0 { - out.write(b",").expect("Write error"); - } - out.write_fmt(format_args!("\n\"0x{:x}\": {{\"balance\": \"{:x}\", \"nonce\": \"{:x}\"", account, balance, client.nonce(&account, at).unwrap_or_else(U256::zero))).expect("Write error"); - let code = client.code(&account, at.into()).unwrap_or(None).unwrap_or_else(Vec::new); - if !code.is_empty() { - out.write_fmt(format_args!(", \"code_hash\": \"0x{:x}\"", keccak(&code))).expect("Write error"); - if cmd.code { - out.write_fmt(format_args!(", \"code\": \"{}\"", code.to_hex())).expect("Write error"); - } - } - let storage_root = client.storage_root(&account, at).unwrap_or(KECCAK_NULL_RLP); - if storage_root != KECCAK_NULL_RLP { - out.write_fmt(format_args!(", \"storage_root\": \"0x{:x}\"", storage_root)).expect("Write error"); - if cmd.storage { - out.write_fmt(format_args!(", \"storage\": {{")).expect("Write error"); - let mut last_storage: Option = None; - loop { - let keys = client.list_storage(at, &account, last_storage.as_ref(), 1000).ok_or("Specified block not found")?; - if keys.is_empty() { - break; - } - - for key in keys.into_iter() { - if last_storage.is_some() { - out.write(b",").expect("Write error"); - } - out.write_fmt(format_args!("\n\t\"0x{:x}\": \"0x{:x}\"", key, client.storage_at(&account, &key, at.into()).unwrap_or_else(Default::default))).expect("Write error"); - last_storage = Some(key); - } - } - out.write(b"\n}").expect("Write error"); - } - } - out.write(b"}").expect("Write error"); - i += 1; - if i % 10000 == 0 { - info!("Account #{}", i); - } - last = Some(account); - } - } - out.write_fmt(format_args!("\n}}}}")).expect("Write error"); - info!("Export completed."); - Ok(()) + let service = start_client( + cmd.dirs, + cmd.spec, + cmd.pruning, + cmd.pruning_history, + cmd.pruning_memory, + cmd.tracing, + cmd.fat_db, + cmd.compaction, + cmd.cache_config, + true, + cmd.max_round_blocks_to_import, + )?; + + let client = service.client(); + + let mut out: Box = match cmd.file_path { + Some(f) => Box::new( + fs::File::create(&f).map_err(|_| format!("Cannot write to file given: {}", f))?, + ), + None => Box::new(io::stdout()), + }; + + let mut last: Option
= None; + let at = cmd.at; + let mut i = 0usize; + + out.write_fmt(format_args!("{{ \"state\": {{",)) + .expect("Couldn't write to stream."); + loop { + let accounts = client + .list_accounts(at, last.as_ref(), 1000) + .ok_or("Specified block not found")?; + if accounts.is_empty() { + break; + } + + for account in accounts.into_iter() { + let balance = client + .balance(&account, at.into()) + .unwrap_or_else(U256::zero); + if cmd.min_balance.map_or(false, |m| balance < m) + || cmd.max_balance.map_or(false, |m| balance > m) + { + last = Some(account); + continue; //filtered out + } + + if i != 0 { + out.write(b",").expect("Write error"); + } + out.write_fmt(format_args!( + "\n\"0x{:x}\": {{\"balance\": \"{:x}\", \"nonce\": \"{:x}\"", + account, + balance, + client.nonce(&account, at).unwrap_or_else(U256::zero) + )) + .expect("Write error"); + let code = client + .code(&account, at.into()) + .unwrap_or(None) + .unwrap_or_else(Vec::new); + if !code.is_empty() { + out.write_fmt(format_args!(", \"code_hash\": \"0x{:x}\"", keccak(&code))) + .expect("Write error"); + if cmd.code { + out.write_fmt(format_args!(", \"code\": \"{}\"", code.to_hex())) + .expect("Write error"); + } + } + let storage_root = client.storage_root(&account, at).unwrap_or(KECCAK_NULL_RLP); + if storage_root != KECCAK_NULL_RLP { + out.write_fmt(format_args!(", \"storage_root\": \"0x{:x}\"", storage_root)) + .expect("Write error"); + if cmd.storage { + out.write_fmt(format_args!(", \"storage\": {{")) + .expect("Write error"); + let mut last_storage: Option = None; + loop { + let keys = client + .list_storage(at, &account, last_storage.as_ref(), 1000) + .ok_or("Specified block not found")?; + if keys.is_empty() { + break; + } + + for key in keys.into_iter() { + if last_storage.is_some() { + out.write(b",").expect("Write error"); + } + out.write_fmt(format_args!( + "\n\t\"0x{:x}\": \"0x{:x}\"", + key, + client + .storage_at(&account, &key, at.into()) + .unwrap_or_else(Default::default) + )) + .expect("Write error"); + last_storage = Some(key); + } + } + out.write(b"\n}").expect("Write error"); + } + } + out.write(b"}").expect("Write error"); + i += 1; + if i % 10000 == 0 { + info!("Account #{}", i); + } + last = Some(account); + } + } + out.write_fmt(format_args!("\n}}}}")).expect("Write error"); + info!("Export completed."); + Ok(()) } fn execute_reset(cmd: ResetBlockchain) -> Result<(), String> { - let service = start_client( - cmd.dirs, - cmd.spec, - cmd.pruning, - cmd.pruning_history, - cmd.pruning_memory, - cmd.tracing, - cmd.fat_db, - cmd.compaction, - cmd.cache_config, - false, - 0, - )?; - - let client = service.client(); - client.reset(cmd.num)?; - info!("{}", Colour::Green.bold().paint("Successfully reset db!")); - - Ok(()) + let service = start_client( + cmd.dirs, + cmd.spec, + cmd.pruning, + cmd.pruning_history, + cmd.pruning_memory, + cmd.tracing, + cmd.fat_db, + cmd.compaction, + cmd.cache_config, + false, + 0, + )?; + + let client = service.client(); + client.reset(cmd.num)?; + info!("{}", Colour::Green.bold().paint("Successfully reset db!")); + + Ok(()) } pub fn kill_db(cmd: KillBlockchain) -> Result<(), String> { - let spec = cmd.spec.spec(&cmd.dirs.cache)?; - let genesis_hash = spec.genesis_header().hash(); - let db_dirs = cmd.dirs.database(genesis_hash, None, spec.data_dir); - let user_defaults_path = db_dirs.user_defaults_path(); - let mut user_defaults = UserDefaults::load(&user_defaults_path)?; - let algorithm = cmd.pruning.to_algorithm(&user_defaults); - let dir = db_dirs.db_path(algorithm); - fs::remove_dir_all(&dir).map_err(|e| format!("Error removing database: {:?}", e))?; - user_defaults.is_first_launch = true; - user_defaults.save(&user_defaults_path)?; - info!("Database deleted."); - Ok(()) + let spec = cmd.spec.spec(&cmd.dirs.cache)?; + let genesis_hash = spec.genesis_header().hash(); + let db_dirs = cmd.dirs.database(genesis_hash, None, spec.data_dir); + let user_defaults_path = db_dirs.user_defaults_path(); + let mut user_defaults = UserDefaults::load(&user_defaults_path)?; + let algorithm = cmd.pruning.to_algorithm(&user_defaults); + let dir = db_dirs.db_path(algorithm); + fs::remove_dir_all(&dir).map_err(|e| format!("Error removing database: {:?}", e))?; + user_defaults.is_first_launch = true; + user_defaults.save(&user_defaults_path)?; + info!("Database deleted."); + Ok(()) } #[cfg(test)] mod test { - use super::DataFormat; - - #[test] - fn test_data_format_parsing() { - assert_eq!(DataFormat::Binary, "binary".parse().unwrap()); - assert_eq!(DataFormat::Binary, "bin".parse().unwrap()); - assert_eq!(DataFormat::Hex, "hex".parse().unwrap()); - } + use super::DataFormat; + + #[test] + fn test_data_format_parsing() { + assert_eq!(DataFormat::Binary, "binary".parse().unwrap()); + assert_eq!(DataFormat::Binary, "bin".parse().unwrap()); + assert_eq!(DataFormat::Hex, "hex".parse().unwrap()); + } } diff --git a/parity/cache.rs b/parity/cache.rs index d6487221b35..26b34e8992f 100644 --- a/parity/cache.rs +++ b/parity/cache.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use std::cmp::max; @@ -29,110 +29,114 @@ const DEFAULT_STATE_CACHE_SIZE: u32 = 25; /// All values are represented in MB. #[derive(Debug, PartialEq)] pub struct CacheConfig { - /// Size of rocksDB cache. Almost all goes to the state column. - db: u32, - /// Size of blockchain cache. - blockchain: u32, - /// Size of transaction queue cache. - queue: u32, - /// Size of traces cache. - traces: u32, - /// Size of the state cache. - state: u32, + /// Size of rocksDB cache. Almost all goes to the state column. + db: u32, + /// Size of blockchain cache. + blockchain: u32, + /// Size of transaction queue cache. + queue: u32, + /// Size of traces cache. + traces: u32, + /// Size of the state cache. + state: u32, } impl Default for CacheConfig { - fn default() -> Self { - CacheConfig::new( - DEFAULT_DB_CACHE_SIZE, - DEFAULT_BC_CACHE_SIZE, - DEFAULT_BLOCK_QUEUE_SIZE_LIMIT_MB, - DEFAULT_STATE_CACHE_SIZE) - } + fn default() -> Self { + CacheConfig::new( + DEFAULT_DB_CACHE_SIZE, + DEFAULT_BC_CACHE_SIZE, + DEFAULT_BLOCK_QUEUE_SIZE_LIMIT_MB, + DEFAULT_STATE_CACHE_SIZE, + ) + } } impl CacheConfig { - /// Creates new cache config with cumulative size equal `total`. - pub fn new_with_total_cache_size(total: u32) -> Self { - CacheConfig { - db: total * 7 / 10, - blockchain: total / 10, - queue: DEFAULT_BLOCK_QUEUE_SIZE_LIMIT_MB, - traces: DEFAULT_TRACE_CACHE_SIZE, - state: total * 2 / 10, - } - } - - /// Creates new cache config with gitven details. - pub fn new(db: u32, blockchain: u32, queue: u32, state: u32) -> Self { - CacheConfig { - db: db, - blockchain: blockchain, - queue: queue, - traces: DEFAULT_TRACE_CACHE_SIZE, - state: state, - } - } - - /// Size of db cache. - pub fn db_cache_size(&self) -> u32 { - max(MIN_DB_CACHE_MB, self.db) - } - - /// Size of block queue size limit - pub fn queue(&self) -> u32 { - max(self.queue, MIN_BLOCK_QUEUE_SIZE_LIMIT_MB) - } - - /// Size of the blockchain cache. - pub fn blockchain(&self) -> u32 { - max(self.blockchain, MIN_BC_CACHE_MB) - } - - /// Size of the traces cache. - pub fn traces(&self) -> u32 { - self.traces - } - - /// Size of the state cache. - pub fn state(&self) -> u32 { - self.state * 3 / 4 - } - - /// Size of the jump-tables cache. - pub fn jump_tables(&self) -> u32 { - self.state / 4 - } + /// Creates new cache config with cumulative size equal `total`. + pub fn new_with_total_cache_size(total: u32) -> Self { + CacheConfig { + db: total * 7 / 10, + blockchain: total / 10, + queue: DEFAULT_BLOCK_QUEUE_SIZE_LIMIT_MB, + traces: DEFAULT_TRACE_CACHE_SIZE, + state: total * 2 / 10, + } + } + + /// Creates new cache config with gitven details. + pub fn new(db: u32, blockchain: u32, queue: u32, state: u32) -> Self { + CacheConfig { + db: db, + blockchain: blockchain, + queue: queue, + traces: DEFAULT_TRACE_CACHE_SIZE, + state: state, + } + } + + /// Size of db cache. + pub fn db_cache_size(&self) -> u32 { + max(MIN_DB_CACHE_MB, self.db) + } + + /// Size of block queue size limit + pub fn queue(&self) -> u32 { + max(self.queue, MIN_BLOCK_QUEUE_SIZE_LIMIT_MB) + } + + /// Size of the blockchain cache. + pub fn blockchain(&self) -> u32 { + max(self.blockchain, MIN_BC_CACHE_MB) + } + + /// Size of the traces cache. + pub fn traces(&self) -> u32 { + self.traces + } + + /// Size of the state cache. + pub fn state(&self) -> u32 { + self.state * 3 / 4 + } + + /// Size of the jump-tables cache. + pub fn jump_tables(&self) -> u32 { + self.state / 4 + } } #[cfg(test)] mod tests { - use super::CacheConfig; - - #[test] - fn test_cache_config_constructor() { - let config = CacheConfig::new_with_total_cache_size(200); - assert_eq!(config.db, 140); - assert_eq!(config.blockchain(), 20); - assert_eq!(config.queue(), 40); - assert_eq!(config.state(), 30); - assert_eq!(config.jump_tables(), 10); - } - - #[test] - fn test_cache_config_db_cache_sizes() { - let config = CacheConfig::new_with_total_cache_size(400); - assert_eq!(config.db, 280); - assert_eq!(config.db_cache_size(), 280); - } - - #[test] - fn test_cache_config_default() { - assert_eq!(CacheConfig::default(), - CacheConfig::new( - super::DEFAULT_DB_CACHE_SIZE, - super::DEFAULT_BC_CACHE_SIZE, - super::DEFAULT_BLOCK_QUEUE_SIZE_LIMIT_MB, - super::DEFAULT_STATE_CACHE_SIZE)); - } + use super::CacheConfig; + + #[test] + fn test_cache_config_constructor() { + let config = CacheConfig::new_with_total_cache_size(200); + assert_eq!(config.db, 140); + assert_eq!(config.blockchain(), 20); + assert_eq!(config.queue(), 40); + assert_eq!(config.state(), 30); + assert_eq!(config.jump_tables(), 10); + } + + #[test] + fn test_cache_config_db_cache_sizes() { + let config = CacheConfig::new_with_total_cache_size(400); + assert_eq!(config.db, 280); + assert_eq!(config.db_cache_size(), 280); + } + + #[test] + fn test_cache_config_default() { + assert_eq!( + CacheConfig::default(), + CacheConfig::new( + super::DEFAULT_DB_CACHE_SIZE, + super::DEFAULT_BC_CACHE_SIZE, + super::DEFAULT_BLOCK_QUEUE_SIZE_LIMIT_MB, + super::DEFAULT_STATE_CACHE_SIZE + ) + ); + } } diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index 561f5477126..207e7db3b0c 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -1,2154 +1,1641 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . #[macro_use] mod usage; mod presets; -use std::collections::HashSet; use super::helpers; +use std::collections::HashSet; usage! { - { - // CLI subcommands - // Subcommands must start with cmd_ and have '_' in place of '-' - // Sub-subcommands must start with the name of the subcommand - // Arguments must start with arg_ - // Flags must start with flag_ - - CMD cmd_daemon - { - "Use Parity as a daemon", - - ARG arg_daemon_pid_file: (Option) = None, - "", - "Path to the pid file", - } - - CMD cmd_account - { - "Manage accounts", - - CMD cmd_account_new { - "Create a new account (and its associated key) for the given --chain (default: mainnet)", - } - - CMD cmd_account_list { - "List existing accounts of the given --chain (default: mainnet)", - } - - CMD cmd_account_import - { - "Import accounts from JSON UTC keystore files to the specified --chain (default mainnet)", - - ARG arg_account_import_path : (Option>) = None, - "...", - "Path to the accounts", - } - } - - CMD cmd_wallet - { - "Manage wallet", - - CMD cmd_wallet_import - { - "Import wallet into the given --chain (default: mainnet)", - - ARG arg_wallet_import_path: (Option) = None, - "", - "Path to the wallet", - } - } - - CMD cmd_import - { - "Import blockchain data from a file to the given --chain database (default: mainnet)", - - ARG arg_import_format: (Option) = None, - "--format=[FORMAT]", - "Import in a given format. FORMAT must be either 'hex' or 'binary'. (default: auto)", - - ARG arg_import_file: (Option) = None, - "[FILE]", - "Path to the file to import from", - } - - CMD cmd_export - { - "Export blockchain", - - CMD cmd_export_blocks - { - "Export the blockchain blocks from the given --chain database (default: mainnet) into a file. This command requires the chain to be synced with --fat-db on.", - - ARG arg_export_blocks_format: (Option) = None, - "--format=[FORMAT]", - "Export in a given format. FORMAT must be either 'hex' or 'binary'. (default: binary)", - - ARG arg_export_blocks_from: (String) = "1", - "--from=[BLOCK]", - "Export from block BLOCK, which may be an index or hash.", - - ARG arg_export_blocks_to: (String) = "latest", - "--to=[BLOCK]", - "Export to (including) block BLOCK, which may be an index, hash or latest.", - - ARG arg_export_blocks_file: (Option) = None, - "[FILE]", - "Path to the exported file", - } - - CMD cmd_export_state - { - "Export the blockchain state from the given --chain (default: mainnet) into a file. This command requires the chain to be synced with --fat-db on.", - - FLAG flag_export_state_no_storage: (bool) = false, - "--no-storage", - "Don't export account storage.", - - FLAG flag_export_state_no_code: (bool) = false, - "--no-code", - "Don't export account code.", - - ARG arg_export_state_min_balance: (Option) = None, - "--min-balance=[WEI]", - "Don't export accounts with balance less than specified.", - - ARG arg_export_state_max_balance: (Option) = None, - "--max-balance=[WEI]", - "Don't export accounts with balance greater than specified.", - - ARG arg_export_state_at: (String) = "latest", - "--at=[BLOCK]", - "Take a snapshot at the given block, which may be an index, hash, or latest. Note that taking snapshots at non-recent blocks will only work with --pruning archive", - - ARG arg_export_state_format: (Option) = None, - "--format=[FORMAT]", - "Export in a given format. FORMAT must be either 'hex' or 'binary'. (default: binary)", - - ARG arg_export_state_file: (Option) = None, - "[FILE]", - "Path to the exported file", - } - } - - CMD cmd_signer - { - "Manage signer", - - CMD cmd_signer_new_token { - "Generate a new signer-authentication token for the given --chain (default: mainnet)", - } - - CMD cmd_signer_list { - "List the signer-authentication tokens from given --chain (default: mainnet)", - } - - CMD cmd_signer_sign - { - "Sign", - - ARG arg_signer_sign_id: (Option) = None, - "[ID]", - "ID", - } - - CMD cmd_signer_reject - { - "Reject", - - ARG arg_signer_reject_id: (Option) = None, - "", - "ID", - } - } - - CMD cmd_snapshot - { - "Make a snapshot of the database of the given --chain (default: mainnet)", - - ARG arg_snapshot_at: (String) = "latest", - "--at=[BLOCK]", - "Take a snapshot at the given block, which may be an index, hash, or latest. Note that taking snapshots at non-recent blocks will only work with --pruning archive", - - ARG arg_snapshot_file: (Option) = None, - "", - "Path to the file to export to", - } - - CMD cmd_restore - { - "Restore the database of the given --chain (default: mainnet) from a snapshot file", - - ARG arg_restore_file: (Option) = None, - "[FILE]", - "Path to the file to restore from", - } - - CMD cmd_tools - { - "Tools", - - CMD cmd_tools_hash - { - "Hash a file using the Keccak-256 algorithm", - - ARG arg_tools_hash_file: (Option) = None, - "", - "File", - } - } - - CMD cmd_db - { - "Manage the database representing the state of the blockchain on this system", - - CMD cmd_db_kill { - "Clean the database of the given --chain (default: mainnet)", - } - - CMD cmd_db_reset { - "Removes NUM latests blocks from the db", - - ARG arg_db_reset_num: (u32) = 10u32, - "", - "Number of blocks to revert", - } - - } - - CMD cmd_export_hardcoded_sync - { - "Print the hashed light clients headers of the given --chain (default: mainnet) in a JSON format. To be used as hardcoded headers in a genesis file.", - } - - // CMD removed in 2.0 - - CMD cmd_dapp - { - "Manage dapps", - - ARG arg_dapp_path: (Option) = None, - "", - "Path to the dapps", - } - } - { - // Global flags and arguments - ["Operating Options"] - FLAG flag_no_download: (bool) = false, or |c: &Config| c.parity.as_ref()?.no_download.clone(), - "--no-download", - "Normally new releases will be downloaded ready for updating. This disables it. Not recommended.", - - FLAG flag_no_consensus: (bool) = false, or |c: &Config| c.parity.as_ref()?.no_consensus.clone(), - "--no-consensus", - "Force the binary to run even if there are known issues regarding consensus. Not recommended.", - - FLAG flag_light: (bool) = false, or |c: &Config| c.parity.as_ref()?.light, - "--light", - "Experimental: run in light client mode. Light clients synchronize a bare minimum of data and fetch necessary data on-demand from the network. Much lower in storage, potentially higher in bandwidth. Has no effect with subcommands.", - - FLAG flag_no_hardcoded_sync: (bool) = false, or |c: &Config| c.parity.as_ref()?.no_hardcoded_sync, - "--no-hardcoded-sync", - "By default, if there is no existing database the light client will automatically jump to a block hardcoded in the chain's specifications. This disables this feature.", - - FLAG flag_force_direct: (bool) = false, or |_| None, - "--force-direct", - "Run the originally installed version of Parity, ignoring any updates that have since been installed.", - - ARG arg_mode: (String) = "last", or |c: &Config| c.parity.as_ref()?.mode.clone(), - "--mode=[MODE]", - "Set the operating mode. MODE can be one of: last - Uses the last-used mode, active if none; active - Parity continuously syncs the chain; passive - Parity syncs initially, then sleeps and wakes regularly to resync; dark - Parity syncs only when the JSON-RPC is active; offline - Parity doesn't sync.", - - ARG arg_mode_timeout: (u64) = 300u64, or |c: &Config| c.parity.as_ref()?.mode_timeout.clone(), - "--mode-timeout=[SECS]", - "Specify the number of seconds before inactivity timeout occurs when mode is dark or passive", - - ARG arg_mode_alarm: (u64) = 3600u64, or |c: &Config| c.parity.as_ref()?.mode_alarm.clone(), - "--mode-alarm=[SECS]", - "Specify the number of seconds before auto sleep reawake timeout occurs when mode is passive", - - ARG arg_auto_update: (String) = "critical", or |c: &Config| c.parity.as_ref()?.auto_update.clone(), - "--auto-update=[SET]", - "Set a releases set to automatically update and install. SET can be one of: all - All updates in the our release track; critical - Only consensus/security updates; none - No updates will be auto-installed.", - - ARG arg_auto_update_delay: (u16) = 100u16, or |c: &Config| c.parity.as_ref()?.auto_update_delay.clone(), - "--auto-update-delay=[NUM]", - "Specify the maximum number of blocks used for randomly delaying updates.", - - ARG arg_auto_update_check_frequency: (u16) = 20u16, or |c: &Config| c.parity.as_ref()?.auto_update_check_frequency.clone(), - "--auto-update-check-frequency=[NUM]", - "Specify the number of blocks between each auto-update check.", - - ARG arg_release_track: (String) = "current", or |c: &Config| c.parity.as_ref()?.release_track.clone(), - "--release-track=[TRACK]", - "Set which release track we should use for updates. TRACK can be one of: stable - Stable releases; beta - Beta releases; nightly - Nightly releases (unstable); testing - Testing releases (do not use); current - Whatever track this executable was released on.", - - ARG arg_chain: (String) = "foundation", or |c: &Config| c.parity.as_ref()?.chain.clone(), - "--chain=[CHAIN]", - "Specify the blockchain type. CHAIN may be either a JSON chain specification file or ethereum, classic, poacore, tobalaba, expanse, musicoin, ellaism, mix, callisto, morden, ropsten, kovan, rinkeby, goerli, kotti, poasokol, testnet, or dev.", - - ARG arg_keys_path: (String) = "$BASE/keys", or |c: &Config| c.parity.as_ref()?.keys_path.clone(), - "--keys-path=[PATH]", - "Specify the path for JSON key files to be found", - - ARG arg_identity: (String) = "", or |c: &Config| c.parity.as_ref()?.identity.clone(), - "--identity=[NAME]", - "Specify your node's name.", - - ARG arg_base_path: (Option) = None, or |c: &Config| c.parity.as_ref()?.base_path.clone(), - "-d, --base-path=[PATH]", - "Specify the base data storage path.", - - ARG arg_db_path: (Option) = None, or |c: &Config| c.parity.as_ref()?.db_path.clone(), - "--db-path=[PATH]", - "Specify the database directory path", - - ["Convenience Options"] - FLAG flag_unsafe_expose: (bool) = false, or |c: &Config| c.misc.as_ref()?.unsafe_expose, - "--unsafe-expose", - "All servers will listen on external interfaces and will be remotely accessible. It's equivalent with setting the following: --[ws,jsonrpc,ipfs-api,secretstore,stratum,dapps,secretstore-http]-interface=all --*-hosts=all This option is UNSAFE and should be used with great care!", - - ARG arg_config: (String) = "$BASE/config.toml", or |_| None, - "-c, --config=[CONFIG]", - "Specify a configuration. CONFIG may be either a configuration file or a preset: dev, insecure, dev-insecure, mining, or non-standard-ports.", - - ARG arg_ports_shift: (u16) = 0u16, or |c: &Config| c.misc.as_ref()?.ports_shift, - "--ports-shift=[SHIFT]", - "Add SHIFT to all port numbers Parity is listening on. Includes network port and all servers (HTTP JSON-RPC, WebSockets JSON-RPC, IPFS, SecretStore).", - - ["Account Options"] - FLAG flag_no_hardware_wallets: (bool) = false, or |c: &Config| c.account.as_ref()?.disable_hardware.clone(), - "--no-hardware-wallets", - "Disables hardware wallet support.", - - FLAG flag_fast_unlock: (bool) = false, or |c: &Config| c.account.as_ref()?.fast_unlock.clone(), - "--fast-unlock", - "Use drastically faster unlocking mode. This setting causes raw secrets to be stored unprotected in memory, so use with care.", - - ARG arg_keys_iterations: (u32) = 10240u32, or |c: &Config| c.account.as_ref()?.keys_iterations.clone(), - "--keys-iterations=[NUM]", - "Specify the number of iterations to use when deriving key from the password (bigger is more secure)", - - ARG arg_accounts_refresh: (u64) = 5u64, or |c: &Config| c.account.as_ref()?.refresh_time.clone(), - "--accounts-refresh=[TIME]", - "Specify the cache time of accounts read from disk. If you manage thousands of accounts set this to 0 to disable refresh.", - - ARG arg_unlock: (Option) = None, or |c: &Config| c.account.as_ref()?.unlock.as_ref().map(|vec| vec.join(",")), - "--unlock=[ACCOUNTS]", - "Unlock ACCOUNTS for the duration of the execution. ACCOUNTS is a comma-delimited list of addresses.", - - ARG arg_password: (Vec) = Vec::new(), or |c: &Config| c.account.as_ref()?.password.clone(), - "--password=[FILE]...", - "Provide a file containing a password for unlocking an account. Leading and trailing whitespace is trimmed.", - - ["Private Transactions Options"] - FLAG flag_private_enabled: (bool) = false, or |c: &Config| c.private_tx.as_ref()?.enabled, - "--private-tx-enabled", - "Enable private transactions.", - - ARG arg_private_signer: (Option) = None, or |c: &Config| c.private_tx.as_ref()?.signer.clone(), - "--private-signer=[ACCOUNT]", - "Specify the account for signing public transaction created upon verified private transaction.", - - ARG arg_private_validators: (Option) = None, or |c: &Config| c.private_tx.as_ref()?.validators.as_ref().map(|vec| vec.join(",")), - "--private-validators=[ACCOUNTS]", - "Specify the accounts for validating private transactions. ACCOUNTS is a comma-delimited list of addresses.", - - ARG arg_private_account: (Option) = None, or |c: &Config| c.private_tx.as_ref()?.account.clone(), - "--private-account=[ACCOUNT]", - "Specify the account for signing requests to secret store.", - - ARG arg_private_sstore_url: (Option) = None, or |c: &Config| c.private_tx.as_ref()?.sstore_url.clone(), - "--private-sstore-url=[URL]", - "Specify secret store URL used for encrypting private transactions.", - - ARG arg_private_sstore_threshold: (Option) = None, or |c: &Config| c.private_tx.as_ref()?.sstore_threshold.clone(), - "--private-sstore-threshold=[NUM]", - "Specify secret store threshold used for encrypting private transactions.", - - ARG arg_private_passwords: (Option) = None, or |c: &Config| c.private_tx.as_ref()?.passwords.clone(), - "--private-passwords=[FILE]...", - "Provide a file containing passwords for unlocking accounts (signer, private account, validators).", - - ["UI Options"] - ARG arg_ui_path: (String) = "$BASE/signer", or |c: &Config| c.ui.as_ref()?.path.clone(), - "--ui-path=[PATH]", - "Specify directory where Trusted UIs tokens should be stored.", - - ["Networking Options"] - FLAG flag_no_warp: (bool) = false, or |c: &Config| c.network.as_ref()?.warp.clone().map(|w| !w), - "--no-warp", - "Disable syncing from the snapshot over the network.", - - FLAG flag_no_discovery: (bool) = false, or |c: &Config| c.network.as_ref()?.discovery.map(|d| !d).clone(), - "--no-discovery", - "Disable new peer discovery.", - - FLAG flag_reserved_only: (bool) = false, or |c: &Config| c.network.as_ref()?.reserved_only.clone(), - "--reserved-only", - "Connect only to reserved nodes.", - - FLAG flag_no_ancient_blocks: (bool) = false, or |_| None, - "--no-ancient-blocks", - "Disable downloading old blocks after snapshot restoration or warp sync. Not recommended.", - - FLAG flag_no_serve_light: (bool) = false, or |c: &Config| c.network.as_ref()?.no_serve_light.clone(), - "--no-serve-light", - "Disable serving of light peers.", - - ARG arg_warp_barrier: (Option) = None, or |c: &Config| c.network.as_ref()?.warp_barrier.clone(), - "--warp-barrier=[NUM]", - "When warp enabled never attempt regular sync before warping to block NUM.", - - ARG arg_port: (u16) = 30303u16, or |c: &Config| c.network.as_ref()?.port.clone(), - "--port=[PORT]", - "Override the port on which the node should listen.", - - ARG arg_interface: (String) = "all", or |c: &Config| c.network.as_ref()?.interface.clone(), - "--interface=[IP]", - "Network interfaces. Valid values are 'all', 'local' or the ip of the interface you want parity to listen to.", - - ARG arg_min_peers: (Option) = None, or |c: &Config| c.network.as_ref()?.min_peers.clone(), - "--min-peers=[NUM]", - "Try to maintain at least NUM peers.", - - ARG arg_max_peers: (Option) = None, or |c: &Config| c.network.as_ref()?.max_peers.clone(), - "--max-peers=[NUM]", - "Allow up to NUM peers.", - - ARG arg_snapshot_peers: (u16) = 0u16, or |c: &Config| c.network.as_ref()?.snapshot_peers.clone(), - "--snapshot-peers=[NUM]", - "Allow additional NUM peers for a snapshot sync.", - - ARG arg_nat: (String) = "any", or |c: &Config| c.network.as_ref()?.nat.clone(), - "--nat=[METHOD]", - "Specify method to use for determining public address. Must be one of: any, none, upnp, extip:.", - - ARG arg_allow_ips: (String) = "all", or |c: &Config| c.network.as_ref()?.allow_ips.clone(), - "--allow-ips=[FILTER]", - "Filter outbound connections. Must be one of: private - connect to private network IP addresses only; public - connect to public network IP addresses only; all - connect to any IP address.", - - ARG arg_max_pending_peers: (u16) = 64u16, or |c: &Config| c.network.as_ref()?.max_pending_peers.clone(), - "--max-pending-peers=[NUM]", - "Allow up to NUM pending connections.", - - ARG arg_network_id: (Option) = None, or |c: &Config| c.network.as_ref()?.id.clone(), - "--network-id=[INDEX]", - "Override the network identifier from the chain we are on.", - - ARG arg_bootnodes: (Option) = None, or |c: &Config| c.network.as_ref()?.bootnodes.as_ref().map(|vec| vec.join(",")), - "--bootnodes=[NODES]", - "Override the bootnodes from our chain. NODES should be comma-delimited enodes.", - - ARG arg_node_key: (Option) = None, or |c: &Config| c.network.as_ref()?.node_key.clone(), - "--node-key=[KEY]", - "Specify node secret key, either as 64-character hex string or input to SHA3 operation.", - - ARG arg_reserved_peers: (Option) = None, or |c: &Config| c.network.as_ref()?.reserved_peers.clone(), - "--reserved-peers=[FILE]", - "Provide a file containing enodes, one per line. These nodes will always have a reserved slot on top of the normal maximum peers.", - - CHECK |args: &Args| { - if let (Some(max_peers), Some(min_peers)) = (args.arg_max_peers, args.arg_min_peers) { - if min_peers > max_peers { - return Err(ArgsError::PeerConfiguration); - } - } - - Ok(()) - }, - - ["API and Console Options – HTTP JSON-RPC"] - FLAG flag_jsonrpc_allow_missing_blocks: (bool) = false, or |c: &Config| c.rpc.as_ref()?.allow_missing_blocks.clone(), - "--jsonrpc-allow-missing-blocks", - "RPC calls will return 'null' instead of an error if ancient block sync is still in progress and the block information requested could not be found", - - FLAG flag_no_jsonrpc: (bool) = false, or |c: &Config| c.rpc.as_ref()?.disable.clone(), - "--no-jsonrpc", - "Disable the HTTP JSON-RPC API server.", - - FLAG flag_jsonrpc_no_keep_alive: (bool) = false, or |c: &Config| c.rpc.as_ref()?.keep_alive, - "--jsonrpc-no-keep-alive", - "Disable HTTP/1.1 keep alive header. Disabling keep alive will prevent re-using the same TCP connection to fire multiple requests, recommended when using one request per connection.", - - FLAG flag_jsonrpc_experimental: (bool) = false, or |c: &Config| c.rpc.as_ref()?.experimental_rpcs.clone(), - "--jsonrpc-experimental", - "Enable experimental RPCs. Enable to have access to methods from unfinalised EIPs in all namespaces", - - ARG arg_jsonrpc_port: (u16) = 8545u16, or |c: &Config| c.rpc.as_ref()?.port.clone(), - "--jsonrpc-port=[PORT]", - "Specify the port portion of the HTTP JSON-RPC API server.", - - ARG arg_jsonrpc_interface: (String) = "local", or |c: &Config| c.rpc.as_ref()?.interface.clone(), - "--jsonrpc-interface=[IP]", - "Specify the hostname portion of the HTTP JSON-RPC API server, IP should be an interface's IP address, or all (all interfaces) or local.", - - ARG arg_jsonrpc_apis: (String) = "web3,eth,pubsub,net,parity,private,parity_pubsub,traces,rpc,shh,shh_pubsub", or |c: &Config| c.rpc.as_ref()?.apis.as_ref().map(|vec| vec.join(",")), - "--jsonrpc-apis=[APIS]", - "Specify the APIs available through the HTTP JSON-RPC interface using a comma-delimited list of API names. Possible names are: all, safe, debug, web3, net, eth, pubsub, personal, signer, parity, parity_pubsub, parity_accounts, parity_set, traces, rpc, secretstore, shh, shh_pubsub. You can also disable a specific API by putting '-' in the front, example: all,-personal. 'safe' enables the following APIs: web3, net, eth, pubsub, parity, parity_pubsub, traces, rpc, shh, shh_pubsub", - - ARG arg_jsonrpc_hosts: (String) = "none", or |c: &Config| c.rpc.as_ref()?.hosts.as_ref().map(|vec| vec.join(",")), - "--jsonrpc-hosts=[HOSTS]", - "List of allowed Host header values. This option will validate the Host header sent by the browser, it is additional security against some attack vectors. Special options: \"all\", \"none\",.", - - ARG arg_jsonrpc_threads: (usize) = 4usize, or |c: &Config| c.rpc.as_ref()?.processing_threads, - "--jsonrpc-threads=[THREADS]", - "Turn on additional processing threads for JSON-RPC servers (all transports). Setting this to a non-zero value allows parallel execution of cpu-heavy queries.", - - ARG arg_jsonrpc_cors: (String) = "none", or |c: &Config| c.rpc.as_ref()?.cors.as_ref().map(|vec| vec.join(",")), - "--jsonrpc-cors=[URL]", - "Specify CORS header for HTTP JSON-RPC API responses. Special options: \"all\", \"none\".", - - ARG arg_jsonrpc_server_threads: (Option) = None, or |c: &Config| c.rpc.as_ref()?.server_threads, - "--jsonrpc-server-threads=[NUM]", - "Enables multiple threads handling incoming connections for HTTP JSON-RPC server.", - - ARG arg_jsonrpc_max_payload: (Option) = None, or |c: &Config| c.rpc.as_ref()?.max_payload, - "--jsonrpc-max-payload=[MB]", - "Specify maximum size for HTTP JSON-RPC requests in megabytes.", - - ARG arg_poll_lifetime: (u32) = 60u32, or |c: &Config| c.rpc.as_ref()?.poll_lifetime.clone(), - "--poll-lifetime=[S]", - "Set the RPC filter lifetime to S seconds. The filter has to be polled at least every S seconds , otherwise it is removed.", - - ["API and Console Options – WebSockets"] - FLAG flag_no_ws: (bool) = false, or |c: &Config| c.websockets.as_ref()?.disable.clone(), - "--no-ws", - "Disable the WebSockets JSON-RPC server.", - - ARG arg_ws_port: (u16) = 8546u16, or |c: &Config| c.websockets.as_ref()?.port.clone(), - "--ws-port=[PORT]", - "Specify the port portion of the WebSockets JSON-RPC server.", - - ARG arg_ws_interface: (String) = "local", or |c: &Config| c.websockets.as_ref()?.interface.clone(), - "--ws-interface=[IP]", - "Specify the hostname portion of the WebSockets JSON-RPC server, IP should be an interface's IP address, or all (all interfaces) or local.", - - ARG arg_ws_apis: (String) = "web3,eth,pubsub,net,parity,parity_pubsub,private,traces,rpc,shh,shh_pubsub", or |c: &Config| c.websockets.as_ref()?.apis.as_ref().map(|vec| vec.join(",")), - "--ws-apis=[APIS]", - "Specify the JSON-RPC APIs available through the WebSockets interface using a comma-delimited list of API names. Possible names are: all, safe, web3, net, eth, pubsub, personal, signer, parity, parity_pubsub, parity_accounts, parity_set, traces, rpc, secretstore, shh, shh_pubsub. You can also disable a specific API by putting '-' in the front, example: all,-personal. 'safe' enables the following APIs: web3, net, eth, pubsub, parity, parity_pubsub, traces, rpc, shh, shh_pubsub", - - ARG arg_ws_origins: (String) = "parity://*,chrome-extension://*,moz-extension://*", or |c: &Config| c.websockets.as_ref()?.origins.as_ref().map(|vec| vec.join(",")), - "--ws-origins=[URL]", - "Specify Origin header values allowed to connect. Special options: \"all\", \"none\".", - - ARG arg_ws_hosts: (String) = "none", or |c: &Config| c.websockets.as_ref()?.hosts.as_ref().map(|vec| vec.join(",")), - "--ws-hosts=[HOSTS]", - "List of allowed Host header values. This option will validate the Host header sent by the browser, it is additional security against some attack vectors. Special options: \"all\", \"none\".", - - ARG arg_ws_max_connections: (usize) = 100usize, or |c: &Config| c.websockets.as_ref()?.max_connections, - "--ws-max-connections=[CONN]", - "Maximum number of allowed concurrent WebSockets JSON-RPC connections.", - - ["API and Console Options – IPC"] - FLAG flag_no_ipc: (bool) = false, or |c: &Config| c.ipc.as_ref()?.disable.clone(), - "--no-ipc", - "Disable JSON-RPC over IPC service.", - - ARG arg_ipc_path: (String) = if cfg!(windows) { r"\\.\pipe\jsonrpc.ipc" } else { "$BASE/jsonrpc.ipc" }, or |c: &Config| c.ipc.as_ref()?.path.clone(), - "--ipc-path=[PATH]", - "Specify custom path for JSON-RPC over IPC service.", - - ARG arg_ipc_apis: (String) = "web3,eth,pubsub,net,parity,parity_pubsub,parity_accounts,private,traces,rpc,shh,shh_pubsub", or |c: &Config| c.ipc.as_ref()?.apis.as_ref().map(|vec| vec.join(",")), - "--ipc-apis=[APIS]", - "Specify custom API set available via JSON-RPC over IPC using a comma-delimited list of API names. Possible names are: all, safe, web3, net, eth, pubsub, personal, signer, parity, parity_pubsub, parity_accounts, parity_set, traces, rpc, secretstore, shh, shh_pubsub. You can also disable a specific API by putting '-' in the front, example: all,-personal. 'safe' enables the following APIs: web3, net, eth, pubsub, parity, parity_pubsub, traces, rpc, shh, shh_pubsub", - - ["API and Console Options – IPFS"] - FLAG flag_ipfs_api: (bool) = false, or |c: &Config| c.ipfs.as_ref()?.enable.clone(), - "--ipfs-api", - "Enable IPFS-compatible HTTP API.", - - ARG arg_ipfs_api_port: (u16) = 5001u16, or |c: &Config| c.ipfs.as_ref()?.port.clone(), - "--ipfs-api-port=[PORT]", - "Configure on which port the IPFS HTTP API should listen.", - - ARG arg_ipfs_api_interface: (String) = "local", or |c: &Config| c.ipfs.as_ref()?.interface.clone(), - "--ipfs-api-interface=[IP]", - "Specify the hostname portion of the IPFS API server, IP should be an interface's IP address or local.", - - ARG arg_ipfs_api_hosts: (String) = "none", or |c: &Config| c.ipfs.as_ref()?.hosts.as_ref().map(|vec| vec.join(",")), - "--ipfs-api-hosts=[HOSTS]", - "List of allowed Host header values. This option will validate the Host header sent by the browser, it is additional security against some attack vectors. Special options: \"all\", \"none\".", - - ARG arg_ipfs_api_cors: (String) = "none", or |c: &Config| c.ipfs.as_ref()?.cors.as_ref().map(|vec| vec.join(",")), - "--ipfs-api-cors=[URL]", - "Specify CORS header for IPFS API responses. Special options: \"all\", \"none\".", - - ["Light Client Options"] - ARG arg_on_demand_response_time_window: (Option) = None, or |c: &Config| c.light.as_ref()?.on_demand_response_time_window, - "--on-demand-time-window=[S]", - "Specify the maximum time to wait for a successful response", - - ARG arg_on_demand_request_backoff_start: (Option) = None, or |c: &Config| c.light.as_ref()?.on_demand_request_backoff_start, - "--on-demand-start-backoff=[S]", - "Specify light client initial backoff time for a request", - - ARG arg_on_demand_request_backoff_max: (Option) = None, or |c: &Config| c.light.as_ref()?.on_demand_request_backoff_max, - "--on-demand-end-backoff=[S]", - "Specify light client maximum backoff time for a request", - - ARG arg_on_demand_request_backoff_rounds_max: (Option) = None, or |c: &Config| c.light.as_ref()?.on_demand_request_backoff_rounds_max, - "--on-demand-max-backoff-rounds=[TIMES]", - "Specify light client maximum number of backoff iterations for a request", - - ARG arg_on_demand_request_consecutive_failures: (Option) = None, or |c: &Config| c.light.as_ref()?.on_demand_request_consecutive_failures, - "--on-demand-consecutive-failures=[TIMES]", - "Specify light client the number of failures for a request until it gets exponentially backed off", - - ["Secret Store Options"] - FLAG flag_no_secretstore: (bool) = false, or |c: &Config| c.secretstore.as_ref()?.disable.clone(), - "--no-secretstore", - "Disable Secret Store functionality.", - - FLAG flag_no_secretstore_http: (bool) = false, or |c: &Config| c.secretstore.as_ref()?.disable_http.clone(), - "--no-secretstore-http", - "Disable Secret Store HTTP API.", - - FLAG flag_no_secretstore_auto_migrate: (bool) = false, or |c: &Config| c.secretstore.as_ref()?.disable_auto_migrate.clone(), - "--no-secretstore-auto-migrate", - "Do not run servers set change session automatically when servers set changes. This option has no effect when servers set is read from configuration file.", - - ARG arg_secretstore_acl_contract: (Option) = Some("registry".into()), or |c: &Config| c.secretstore.as_ref()?.acl_contract.clone(), - "--secretstore-acl-contract=[SOURCE]", - "Secret Store permissioning contract address source: none, registry (contract address is read from 'secretstore_acl_checker' entry in registry) or address.", - - ARG arg_secretstore_contract: (Option) = None, or |c: &Config| c.secretstore.as_ref()?.service_contract.clone(), - "--secretstore-contract=[SOURCE]", - "Secret Store Service contract address source: none, registry (contract address is read from 'secretstore_service' entry in registry) or address.", - - ARG arg_secretstore_srv_gen_contract: (Option) = None, or |c: &Config| c.secretstore.as_ref()?.service_contract_srv_gen.clone(), - "--secretstore-srv-gen-contract=[SOURCE]", - "Secret Store Service server key generation contract address source: none, registry (contract address is read from 'secretstore_service_srv_gen' entry in registry) or address.", - - ARG arg_secretstore_srv_retr_contract: (Option) = None, or |c: &Config| c.secretstore.as_ref()?.service_contract_srv_retr.clone(), - "--secretstore-srv-retr-contract=[SOURCE]", - "Secret Store Service server key retrieval contract address source: none, registry (contract address is read from 'secretstore_service_srv_retr' entry in registry) or address.", - - ARG arg_secretstore_doc_store_contract: (Option) = None, or |c: &Config| c.secretstore.as_ref()?.service_contract_doc_store.clone(), - "--secretstore-doc-store-contract=[SOURCE]", - "Secret Store Service document key store contract address source: none, registry (contract address is read from 'secretstore_service_doc_store' entry in registry) or address.", - - ARG arg_secretstore_doc_sretr_contract: (Option) = None, or |c: &Config| c.secretstore.as_ref()?.service_contract_doc_sretr.clone(), - "--secretstore-doc-sretr-contract=[SOURCE]", - "Secret Store Service document key shadow retrieval contract address source: none, registry (contract address is read from 'secretstore_service_doc_sretr' entry in registry) or address.", - - ARG arg_secretstore_nodes: (String) = "", or |c: &Config| c.secretstore.as_ref()?.nodes.as_ref().map(|vec| vec.join(",")), - "--secretstore-nodes=[NODES]", - "Comma-separated list of other secret store cluster nodes in form NODE_PUBLIC_KEY_IN_HEX@NODE_IP_ADDR:NODE_PORT.", - - ARG arg_secretstore_server_set_contract: (Option) = Some("registry".into()), or |c: &Config| c.secretstore.as_ref()?.server_set_contract.clone(), - "--secretstore-server-set-contract=[SOURCE]", - "Secret Store server set contract address source: none, registry (contract address is read from 'secretstore_server_set' entry in registry) or address.", - - ARG arg_secretstore_interface: (String) = "local", or |c: &Config| c.secretstore.as_ref()?.interface.clone(), - "--secretstore-interface=[IP]", - "Specify the hostname portion for listening to Secret Store Key Server internal requests, IP should be an interface's IP address, or local.", - - ARG arg_secretstore_port: (u16) = 8083u16, or |c: &Config| c.secretstore.as_ref()?.port.clone(), - "--secretstore-port=[PORT]", - "Specify the port portion for listening to Secret Store Key Server internal requests.", - - ARG arg_secretstore_http_interface: (String) = "local", or |c: &Config| c.secretstore.as_ref()?.http_interface.clone(), - "--secretstore-http-interface=[IP]", - "Specify the hostname portion for listening to Secret Store Key Server HTTP requests, IP should be an interface's IP address, or local.", - - ARG arg_secretstore_http_port: (u16) = 8082u16, or |c: &Config| c.secretstore.as_ref()?.http_port.clone(), - "--secretstore-http-port=[PORT]", - "Specify the port portion for listening to Secret Store Key Server HTTP requests.", - - ARG arg_secretstore_path: (String) = "$BASE/secretstore", or |c: &Config| c.secretstore.as_ref()?.path.clone(), - "--secretstore-path=[PATH]", - "Specify directory where Secret Store should save its data.", - - ARG arg_secretstore_secret: (Option) = None, or |c: &Config| c.secretstore.as_ref()?.self_secret.clone(), - "--secretstore-secret=[SECRET]", - "Hex-encoded secret key of this node.", - - ARG arg_secretstore_admin_public: (Option) = None, or |c: &Config| c.secretstore.as_ref()?.admin_public.clone(), - "--secretstore-admin=[PUBLIC]", - "Hex-encoded public key of secret store administrator.", - - ["Sealing/Mining Options"] - FLAG flag_force_sealing: (bool) = false, or |c: &Config| c.mining.as_ref()?.force_sealing.clone(), - "--force-sealing", - "Force the node to author new blocks as if it were always sealing/mining.", - - FLAG flag_reseal_on_uncle: (bool) = false, or |c: &Config| c.mining.as_ref()?.reseal_on_uncle.clone(), - "--reseal-on-uncle", - "Force the node to author new blocks when a new uncle block is imported.", - - FLAG flag_remove_solved: (bool) = false, or |c: &Config| c.mining.as_ref()?.remove_solved.clone(), - "--remove-solved", - "Move solved blocks from the work package queue instead of cloning them. This gives a slightly faster import speed, but means that extra solutions submitted for the same work package will go unused.", - - FLAG flag_tx_queue_no_unfamiliar_locals: (bool) = false, or |c: &Config| c.mining.as_ref()?.tx_queue_no_unfamiliar_locals.clone(), - "--tx-queue-no-unfamiliar-locals", - "Local transactions sent through JSON-RPC (HTTP, WebSockets, etc) will be treated as 'external' if the sending account is unknown.", - - FLAG flag_tx_queue_no_early_reject: (bool) = false, or |c: &Config| c.mining.as_ref()?.tx_queue_no_early_reject.clone(), - "--tx-queue-no-early-reject", - "Disables transaction queue optimization to early reject transactions below minimal effective gas price. This allows local transactions to always enter the pool, despite it being full, but requires additional ecrecover on every transaction.", - - FLAG flag_refuse_service_transactions: (bool) = false, or |c: &Config| c.mining.as_ref()?.refuse_service_transactions.clone(), - "--refuse-service-transactions", - "Always refuse service transactions.", - - FLAG flag_infinite_pending_block: (bool) = false, or |c: &Config| c.mining.as_ref()?.infinite_pending_block.clone(), - "--infinite-pending-block", - "Pending block will be created with maximal possible gas limit and will execute all transactions in the queue. Note that such block is invalid and should never be attempted to be mined.", - - FLAG flag_no_persistent_txqueue: (bool) = false, or |c: &Config| c.parity.as_ref()?.no_persistent_txqueue, - "--no-persistent-txqueue", - "Don't save pending local transactions to disk to be restored whenever the node restarts.", - - FLAG flag_stratum: (bool) = false, or |c: &Config| Some(c.stratum.is_some()), - "--stratum", - "Run Stratum server for miner push notification.", - - ARG arg_reseal_on_txs: (String) = "own", or |c: &Config| c.mining.as_ref()?.reseal_on_txs.clone(), - "--reseal-on-txs=[SET]", - "Specify which transactions should force the node to reseal a block. SET is one of: none - never reseal on new transactions; own - reseal only on a new local transaction; ext - reseal only on a new external transaction; all - reseal on all new transactions.", - - ARG arg_reseal_min_period: (u64) = 2000u64, or |c: &Config| c.mining.as_ref()?.reseal_min_period.clone(), - "--reseal-min-period=[MS]", - "Specify the minimum time between reseals from incoming transactions. MS is time measured in milliseconds.", - - ARG arg_reseal_max_period: (u64) = 120000u64, or |c: &Config| c.mining.as_ref()?.reseal_max_period.clone(), - "--reseal-max-period=[MS]", - "Specify the maximum time since last block to enable force-sealing. MS is time measured in milliseconds.", - - ARG arg_work_queue_size: (usize) = 20usize, or |c: &Config| c.mining.as_ref()?.work_queue_size.clone(), - "--work-queue-size=[ITEMS]", - "Specify the number of historical work packages which are kept cached lest a solution is found for them later. High values take more memory but result in fewer unusable solutions.", - - ARG arg_relay_set: (String) = "cheap", or |c: &Config| c.mining.as_ref()?.relay_set.clone(), - "--relay-set=[SET]", - "Set of transactions to relay. SET may be: cheap - Relay any transaction in the queue (this may include invalid transactions); strict - Relay only executed transactions (this guarantees we don't relay invalid transactions, but means we relay nothing if not mining); lenient - Same as strict when mining, and cheap when not.", - - ARG arg_usd_per_tx: (String) = "0.0001", or |c: &Config| c.mining.as_ref()?.usd_per_tx.clone(), - "--usd-per-tx=[USD]", - "Amount of USD to be paid for a basic transaction. The minimum gas price is set accordingly.", - - ARG arg_usd_per_eth: (String) = "auto", or |c: &Config| c.mining.as_ref()?.usd_per_eth.clone(), - "--usd-per-eth=[SOURCE]", - "USD value of a single ETH. SOURCE may be either an amount in USD, a web service or 'auto' to use each web service in turn and fallback on the last known good value.", - - ARG arg_price_update_period: (String) = "hourly", or |c: &Config| c.mining.as_ref()?.price_update_period.clone(), - "--price-update-period=[T]", - "T will be allowed to pass between each gas price update. T may be daily, hourly, a number of seconds, or a time string of the form \"2 days\", \"30 minutes\" etc..", - - ARG arg_gas_floor_target: (String) = "8000000", or |c: &Config| c.mining.as_ref()?.gas_floor_target.clone(), - "--gas-floor-target=[GAS]", - "Amount of gas per block to target when sealing a new block.", - - ARG arg_gas_cap: (String) = "10000000", or |c: &Config| c.mining.as_ref()?.gas_cap.clone(), - "--gas-cap=[GAS]", - "A cap on how large we will raise the gas limit per block due to transaction volume.", - - ARG arg_tx_queue_mem_limit: (u32) = 4u32, or |c: &Config| c.mining.as_ref()?.tx_queue_mem_limit.clone(), - "--tx-queue-mem-limit=[MB]", - "Maximum amount of memory that can be used by the transaction queue. Setting this parameter to 0 disables limiting.", - - ARG arg_tx_queue_size: (usize) = 8_192usize, or |c: &Config| c.mining.as_ref()?.tx_queue_size.clone(), - "--tx-queue-size=[LIMIT]", - "Maximum amount of transactions in the queue (waiting to be included in next block).", - - ARG arg_tx_queue_per_sender: (Option) = None, or |c: &Config| c.mining.as_ref()?.tx_queue_per_sender.clone(), - "--tx-queue-per-sender=[LIMIT]", - "Maximum number of transactions per sender in the queue. By default it's 1% of the entire queue, but not less than 16.", - - ARG arg_tx_queue_locals: (Option) = None, or |c: &Config| helpers::join_set(c.mining.as_ref()?.tx_queue_locals.as_ref()), - "--tx-queue-locals=[ACCOUNTS]", - "Specify local accounts for which transactions are prioritized in the queue. ACCOUNTS is a comma-delimited list of addresses.", - - ARG arg_tx_queue_strategy: (String) = "gas_price", or |c: &Config| c.mining.as_ref()?.tx_queue_strategy.clone(), - "--tx-queue-strategy=[S]", - "Prioritization strategy used to order transactions in the queue. S may be: gas_price - Prioritize txs with high gas price", - - ARG arg_stratum_interface: (String) = "local", or |c: &Config| c.stratum.as_ref()?.interface.clone(), - "--stratum-interface=[IP]", - "Interface address for Stratum server.", - - ARG arg_stratum_port: (u16) = 8008u16, or |c: &Config| c.stratum.as_ref()?.port.clone(), - "--stratum-port=[PORT]", - "Port for Stratum server to listen on.", - - ARG arg_min_gas_price: (Option) = None, or |c: &Config| c.mining.as_ref()?.min_gas_price.clone(), - "--min-gas-price=[STRING]", - "Minimum amount of Wei per GAS to be paid for a transaction to be accepted for mining. Overrides --usd-per-tx.", - - ARG arg_gas_price_percentile: (usize) = 50usize, or |c: &Config| c.mining.as_ref()?.gas_price_percentile, - "--gas-price-percentile=[PCT]", - "Set PCT percentile gas price value from last 100 blocks as default gas price when sending transactions.", - - ARG arg_author: (Option) = None, or |c: &Config| c.mining.as_ref()?.author.clone(), - "--author=[ADDRESS]", - "Specify the block author (aka \"coinbase\") address for sending block rewards from sealed blocks. NOTE: MINING WILL NOT WORK WITHOUT THIS OPTION.", // Sealing/Mining Option - - ARG arg_engine_signer: (Option) = None, or |c: &Config| c.mining.as_ref()?.engine_signer.clone(), - "--engine-signer=[ADDRESS]", - "Specify the address which should be used to sign consensus messages and issue blocks. Relevant only to non-PoW chains.", - - ARG arg_tx_gas_limit: (Option) = None, or |c: &Config| c.mining.as_ref()?.tx_gas_limit.clone(), - "--tx-gas-limit=[GAS]", - "Apply a limit of GAS as the maximum amount of gas a single transaction may have for it to be mined.", - - ARG arg_tx_time_limit: (Option) = None, or |c: &Config| c.mining.as_ref()?.tx_time_limit.clone(), - "--tx-time-limit=[MS]", - "Maximal time for processing single transaction. If enabled senders of transactions offending the limit will get other transactions penalized.", - - ARG arg_extra_data: (Option) = None, or |c: &Config| c.mining.as_ref()?.extra_data.clone(), - "--extra-data=[STRING]", - "Specify a custom extra-data for authored blocks, no more than 32 characters.", - - ARG arg_notify_work: (Option) = None, or |c: &Config| c.mining.as_ref()?.notify_work.as_ref().map(|vec| vec.join(",")), - "--notify-work=[URLS]", - "URLs to which work package notifications are pushed. URLS should be a comma-delimited list of HTTP URLs.", - - ARG arg_stratum_secret: (Option) = None, or |c: &Config| c.stratum.as_ref()?.secret.clone(), - "--stratum-secret=[STRING]", - "Secret for authorizing Stratum server for peers.", - - ARG arg_max_round_blocks_to_import: (usize) = 12usize, or |c: &Config| c.mining.as_ref()?.max_round_blocks_to_import.clone(), - "--max-round-blocks-to-import=[S]", - "Maximal number of blocks to import for each import round.", - - ["Internal Options"] - FLAG flag_can_restart: (bool) = false, or |_| None, - "--can-restart", - "Executable will auto-restart if exiting with 69", - - ["Miscellaneous Options"] - FLAG flag_no_color: (bool) = false, or |c: &Config| c.misc.as_ref()?.color.map(|c| !c).clone(), - "--no-color", - "Don't use terminal color codes in output.", - - FLAG flag_version: (bool) = false, or |_| None, - "-v, --version", - "Show information about version.", - - FLAG flag_no_config: (bool) = false, or |_| None, - "--no-config", - "Don't load a configuration file.", - - ARG arg_logging: (Option) = None, or |c: &Config| c.misc.as_ref()?.logging.clone(), - "-l, --logging=[LOGGING]", - "Specify the general logging level (error, warn, info, debug or trace). It can also be set for a specific module, example: '-l sync=debug,rpc=trace'", - - ARG arg_log_file: (Option) = None, or |c: &Config| c.misc.as_ref()?.log_file.clone(), - "--log-file=[FILENAME]", - "Specify a filename into which logging should be appended.", - - ["Footprint Options"] - FLAG flag_scale_verifiers: (bool) = false, or |c: &Config| c.footprint.as_ref()?.scale_verifiers.clone(), - "--scale-verifiers", - "Automatically scale amount of verifier threads based on workload. Not guaranteed to be faster.", - - ARG arg_tracing: (String) = "auto", or |c: &Config| c.footprint.as_ref()?.tracing.clone(), - "--tracing=[BOOL]", - "Indicates if full transaction tracing should be enabled. Works only if client had been fully synced with tracing enabled. BOOL may be one of auto, on, off. auto uses last used value of this option (off if it does not exist).", // footprint option - - ARG arg_pruning: (String) = "auto", or |c: &Config| c.footprint.as_ref()?.pruning.clone(), - "--pruning=[METHOD]", - "Configure pruning of the state/storage trie. METHOD may be one of auto, archive, fast: archive - keep all state trie data. No pruning. fast - maintain journal overlay. Fast but 50MB used. auto - use the method most recently synced or default to fast if none synced.", - - ARG arg_pruning_history: (u64) = 64u64, or |c: &Config| c.footprint.as_ref()?.pruning_history.clone(), - "--pruning-history=[NUM]", - "Set a minimum number of recent states to keep in memory when pruning is active.", - - ARG arg_pruning_memory: (usize) = 32usize, or |c: &Config| c.footprint.as_ref()?.pruning_memory.clone(), - "--pruning-memory=[MB]", - "The ideal amount of memory in megabytes to use to store recent states. As many states as possible will be kept within this limit, and at least --pruning-history states will always be kept.", - - ARG arg_cache_size_db: (u32) = 128u32, or |c: &Config| c.footprint.as_ref()?.cache_size_db.clone(), - "--cache-size-db=[MB]", - "Override database cache size.", - - ARG arg_cache_size_blocks: (u32) = 8u32, or |c: &Config| c.footprint.as_ref()?.cache_size_blocks.clone(), - "--cache-size-blocks=[MB]", - "Specify the preferred size of the blockchain cache in megabytes.", - - ARG arg_cache_size_queue: (u32) = 40u32, or |c: &Config| c.footprint.as_ref()?.cache_size_queue.clone(), - "--cache-size-queue=[MB]", - "Specify the maximum size of memory to use for block queue.", - - ARG arg_cache_size_state: (u32) = 25u32, or |c: &Config| c.footprint.as_ref()?.cache_size_state.clone(), - "--cache-size-state=[MB]", - "Specify the maximum size of memory to use for the state cache.", - - ARG arg_db_compaction: (String) = "auto", or |c: &Config| c.footprint.as_ref()?.db_compaction.clone(), - "--db-compaction=[TYPE]", - "Database compaction type. TYPE may be one of: ssd - suitable for SSDs and fast HDDs; hdd - suitable for slow HDDs; auto - determine automatically.", - - ARG arg_fat_db: (String) = "auto", or |c: &Config| c.footprint.as_ref()?.fat_db.clone(), - "--fat-db=[BOOL]", - "Build appropriate information to allow enumeration of all accounts and storage keys. Doubles the size of the state database. BOOL may be one of on, off or auto.", - - ARG arg_cache_size: (Option) = None, or |c: &Config| c.footprint.as_ref()?.cache_size.clone(), - "--cache-size=[MB]", - "Set total amount of discretionary memory to use for the entire system, overrides other cache and queue options.", - - ARG arg_num_verifiers: (Option) = None, or |c: &Config| c.footprint.as_ref()?.num_verifiers.clone(), - "--num-verifiers=[INT]", - "Amount of verifier threads to use or to begin with, if verifier auto-scaling is enabled.", - - ["Import/export Options"] - FLAG flag_no_seal_check: (bool) = false, or |_| None, - "--no-seal-check", - "Skip block seal check.", - - ["Snapshot Options"] - FLAG flag_no_periodic_snapshot: (bool) = false, or |c: &Config| c.snapshots.as_ref()?.disable_periodic.clone(), - "--no-periodic-snapshot", - "Disable automated snapshots which usually occur once every 5000 blocks.", - - ARG arg_snapshot_threads: (Option) = None, or |c: &Config| c.snapshots.as_ref()?.processing_threads, - "--snapshot-threads=[NUM]", - "Enables multiple threads for snapshots creation.", - - ["Whisper Options"] - FLAG flag_whisper: (bool) = false, or |c: &Config| c.whisper.as_ref()?.enabled, - "--whisper", - "Enable the Whisper network.", - - ARG arg_whisper_pool_size: (usize) = 10usize, or |c: &Config| c.whisper.as_ref()?.pool_size.clone(), - "--whisper-pool-size=[MB]", - "Target size of the whisper message pool in megabytes.", - - ["Legacy Options"] - // Options that are hidden from config, but are still unique for its functionality. - - FLAG flag_geth: (bool) = false, or |_| None, - "--geth", - "Run in Geth-compatibility mode. Sets the IPC path to be the same as Geth's. Overrides the --ipc-path and --ipcpath options. Alters RPCs to reflect Geth bugs. Includes the personal_ RPC by default.", - - FLAG flag_import_geth_keys: (bool) = false, or |_| None, - "--import-geth-keys", - "Attempt to import keys from Geth client.", - - // Options that either do nothing, or are replaced by other options. - // FLAG Removed in 1.6 or before. - - FLAG flag_warp: (bool) = false, or |_| None, - "--warp", - "Does nothing; warp sync is enabled by default. Use --no-warp to disable.", - - FLAG flag_jsonrpc: (bool) = false, or |_| None, - "-j, --jsonrpc", - "Does nothing; HTTP JSON-RPC is on by default now.", - - FLAG flag_rpc: (bool) = false, or |_| None, - "--rpc", - "Does nothing; HTTP JSON-RPC is on by default now.", - - FLAG flag_jsonrpc_off: (bool) = false, or |_| None, - "--jsonrpc-off", - "Equivalent to --no-jsonrpc.", - - FLAG flag_webapp: (bool) = false, or |_| None, - "-w, --webapp", - "Does nothing; dapps server has been removed.", - - FLAG flag_dapps_off: (bool) = false, or |_| None, - "--dapps-off", - "Equivalent to --no-dapps.", - - FLAG flag_ipcdisable: (bool) = false, or |_| None, - "--ipcdisable", - "Equivalent to --no-ipc.", - - FLAG flag_ipc_off: (bool) = false, or |_| None, - "--ipc-off", - "Equivalent to --no-ipc.", + { + // CLI subcommands + // Subcommands must start with cmd_ and have '_' in place of '-' + // Sub-subcommands must start with the name of the subcommand + // Arguments must start with arg_ + // Flags must start with flag_ + + CMD cmd_daemon + { + "Use OpenEthereum as a daemon", + + ARG arg_daemon_pid_file: (Option) = None, + "", + "Path to the pid file", + } + + CMD cmd_account + { + "Manage accounts", + + CMD cmd_account_new { + "Create a new account (and its associated key) for the given --chain (default: mainnet)", + } + + CMD cmd_account_list { + "List existing accounts of the given --chain (default: mainnet)", + } + + CMD cmd_account_import + { + "Import accounts from JSON UTC keystore files to the specified --chain (default mainnet)", + + ARG arg_account_import_path : (Option>) = None, + "...", + "Path to the accounts", + } + } + + CMD cmd_wallet + { + "Manage wallet", + + CMD cmd_wallet_import + { + "Import wallet into the given --chain (default: mainnet)", + + ARG arg_wallet_import_path: (Option) = None, + "", + "Path to the wallet", + } + } + + CMD cmd_import + { + "Import blockchain data from a file to the given --chain database (default: mainnet)", + + ARG arg_import_format: (Option) = None, + "--format=[FORMAT]", + "Import in a given format. FORMAT must be either 'hex' or 'binary'. (default: auto)", + + ARG arg_import_file: (Option) = None, + "[FILE]", + "Path to the file to import from", + } + + CMD cmd_export + { + "Export blockchain", + + CMD cmd_export_blocks + { + "Export the blockchain blocks from the given --chain database (default: mainnet) into a file. This command requires the chain to be synced with --fat-db on.", + + ARG arg_export_blocks_format: (Option) = None, + "--format=[FORMAT]", + "Export in a given format. FORMAT must be either 'hex' or 'binary'. (default: binary)", + + ARG arg_export_blocks_from: (String) = "1", + "--from=[BLOCK]", + "Export from block BLOCK, which may be an index or hash.", + + ARG arg_export_blocks_to: (String) = "latest", + "--to=[BLOCK]", + "Export to (including) block BLOCK, which may be an index, hash or latest.", + + ARG arg_export_blocks_file: (Option) = None, + "[FILE]", + "Path to the exported file", + } + + CMD cmd_export_state + { + "Export the blockchain state from the given --chain (default: mainnet) into a file. This command requires the chain to be synced with --fat-db on.", + + FLAG flag_export_state_no_storage: (bool) = false, + "--no-storage", + "Don't export account storage.", + + FLAG flag_export_state_no_code: (bool) = false, + "--no-code", + "Don't export account code.", + + ARG arg_export_state_min_balance: (Option) = None, + "--min-balance=[WEI]", + "Don't export accounts with balance less than specified.", + + ARG arg_export_state_max_balance: (Option) = None, + "--max-balance=[WEI]", + "Don't export accounts with balance greater than specified.", + + ARG arg_export_state_at: (String) = "latest", + "--at=[BLOCK]", + "Take a snapshot at the given block, which may be an index, hash, or latest. Note that taking snapshots at non-recent blocks will only work with --pruning archive", + + ARG arg_export_state_format: (Option) = None, + "--format=[FORMAT]", + "Export in a given format. FORMAT must be either 'hex' or 'binary'. (default: binary)", + + ARG arg_export_state_file: (Option) = None, + "[FILE]", + "Path to the exported file", + } + } + + CMD cmd_signer + { + "Manage signer", + + CMD cmd_signer_new_token { + "Generate a new signer-authentication token for the given --chain (default: mainnet)", + } + + CMD cmd_signer_list { + "List the signer-authentication tokens from given --chain (default: mainnet)", + } + + CMD cmd_signer_sign + { + "Sign", + + ARG arg_signer_sign_id: (Option) = None, + "[ID]", + "ID", + } + + CMD cmd_signer_reject + { + "Reject", + + ARG arg_signer_reject_id: (Option) = None, + "", + "ID", + } + } + + CMD cmd_snapshot + { + "Make a snapshot of the database of the given --chain (default: mainnet)", + + ARG arg_snapshot_at: (String) = "latest", + "--at=[BLOCK]", + "Take a snapshot at the given block, which may be an index, hash, or latest. Note that taking snapshots at non-recent blocks will only work with --pruning archive", + + ARG arg_snapshot_file: (Option) = None, + "", + "Path to the file to export to", + } + + CMD cmd_restore + { + "Restore the database of the given --chain (default: mainnet) from a snapshot file", + + ARG arg_restore_file: (Option) = None, + "[FILE]", + "Path to the file to restore from", + } + + CMD cmd_tools + { + "Tools", + + CMD cmd_tools_hash + { + "Hash a file using the Keccak-256 algorithm", + + ARG arg_tools_hash_file: (Option) = None, + "", + "File", + } + } + + CMD cmd_db + { + "Manage the database representing the state of the blockchain on this system", + + CMD cmd_db_kill { + "Clean the database of the given --chain (default: mainnet)", + } + + CMD cmd_db_reset { + "Removes NUM latests blocks from the db", + + ARG arg_db_reset_num: (u32) = 10u32, + "", + "Number of blocks to revert", + } + + } + } + { + // Global flags and arguments + ["Operating Options"] + ARG arg_mode: (String) = "last", or |c: &Config| c.parity.as_ref()?.mode.clone(), + "--mode=[MODE]", + "Set the operating mode. MODE can be one of: last - Uses the last-used mode, active if none; active - Parity continuously syncs the chain; passive - Parity syncs initially, then sleeps and wakes regularly to resync; dark - Parity syncs only when the JSON-RPC is active; offline - Parity doesn't sync.", + + ARG arg_mode_timeout: (u64) = 300u64, or |c: &Config| c.parity.as_ref()?.mode_timeout.clone(), + "--mode-timeout=[SECS]", + "Specify the number of seconds before inactivity timeout occurs when mode is dark or passive", + + ARG arg_mode_alarm: (u64) = 3600u64, or |c: &Config| c.parity.as_ref()?.mode_alarm.clone(), + "--mode-alarm=[SECS]", + "Specify the number of seconds before auto sleep reawake timeout occurs when mode is passive", + + ARG arg_chain: (String) = "foundation", or |c: &Config| c.parity.as_ref()?.chain.clone(), + "--chain=[CHAIN]", + "Specify the blockchain type. CHAIN may be either a JSON chain specification file or ethereum, poacore, xdai, volta, ewc, musicoin, ellaism, mix, callisto, morden, ropsten, kovan, rinkeby, goerli, poasokol, testnet, or dev.", + + ARG arg_keys_path: (String) = "$BASE/keys", or |c: &Config| c.parity.as_ref()?.keys_path.clone(), + "--keys-path=[PATH]", + "Specify the path for JSON key files to be found", + + ARG arg_identity: (String) = "", or |c: &Config| c.parity.as_ref()?.identity.clone(), + "--identity=[NAME]", + "Specify your node's name.", + + ARG arg_base_path: (Option) = None, or |c: &Config| c.parity.as_ref()?.base_path.clone(), + "-d, --base-path=[PATH]", + "Specify the base data storage path.", + + ARG arg_db_path: (Option) = None, or |c: &Config| c.parity.as_ref()?.db_path.clone(), + "--db-path=[PATH]", + "Specify the database directory path", + + ["Convenience Options"] + FLAG flag_unsafe_expose: (bool) = false, or |c: &Config| c.misc.as_ref()?.unsafe_expose, + "--unsafe-expose", + "All servers will listen on external interfaces and will be remotely accessible. It's equivalent with setting the following: --[ws,jsonrpc,secretstore,stratum,secretstore-http]-interface=all --*-hosts=all This option is UNSAFE and should be used with great care!", + + ARG arg_config: (String) = "$BASE/config.toml", or |_| None, + "-c, --config=[CONFIG]", + "Specify a configuration. CONFIG may be either a configuration file or a preset: dev, insecure, dev-insecure, mining, or non-standard-ports.", + + ARG arg_ports_shift: (u16) = 0u16, or |c: &Config| c.misc.as_ref()?.ports_shift, + "--ports-shift=[SHIFT]", + "Add SHIFT to all port numbers OpenEthereum is listening on. Includes network port and all servers (HTTP JSON-RPC, WebSockets JSON-RPC, SecretStore).", + + ["Account Options"] + FLAG flag_fast_unlock: (bool) = false, or |c: &Config| c.account.as_ref()?.fast_unlock.clone(), + "--fast-unlock", + "Use drastically faster unlocking mode. This setting causes raw secrets to be stored unprotected in memory, so use with care.", + + ARG arg_keys_iterations: (u32) = 10240u32, or |c: &Config| c.account.as_ref()?.keys_iterations.clone(), + "--keys-iterations=[NUM]", + "Specify the number of iterations to use when deriving key from the password (bigger is more secure)", + + ARG arg_accounts_refresh: (u64) = 5u64, or |c: &Config| c.account.as_ref()?.refresh_time.clone(), + "--accounts-refresh=[TIME]", + "Specify the cache time of accounts read from disk. If you manage thousands of accounts set this to 0 to disable refresh.", + + ARG arg_unlock: (Option) = None, or |c: &Config| c.account.as_ref()?.unlock.as_ref().map(|vec| vec.join(",")), + "--unlock=[ACCOUNTS]", + "Unlock ACCOUNTS for the duration of the execution. ACCOUNTS is a comma-delimited list of addresses.", + + ARG arg_password: (Vec) = Vec::new(), or |c: &Config| c.account.as_ref()?.password.clone(), + "--password=[FILE]...", + "Provide a file containing a password for unlocking an account. Leading and trailing whitespace is trimmed.", + + ["UI Options"] + ARG arg_ui_path: (String) = "$BASE/signer", or |c: &Config| c.ui.as_ref()?.path.clone(), + "--ui-path=[PATH]", + "Specify directory where Trusted UIs tokens should be stored.", + + ["Networking Options"] + FLAG flag_no_warp: (bool) = false, or |c: &Config| c.network.as_ref()?.warp.clone().map(|w| !w), + "--no-warp", + "Disable syncing from the snapshot over the network.", + + FLAG flag_no_discovery: (bool) = false, or |c: &Config| c.network.as_ref()?.discovery.map(|d| !d).clone(), + "--no-discovery", + "Disable new peer discovery.", + + FLAG flag_reserved_only: (bool) = false, or |c: &Config| c.network.as_ref()?.reserved_only.clone(), + "--reserved-only", + "Connect only to reserved nodes.", + + FLAG flag_no_ancient_blocks: (bool) = false, or |_| None, + "--no-ancient-blocks", + "Disable downloading old blocks after snapshot restoration or warp sync. Not recommended.", + + ARG arg_warp_barrier: (Option) = None, or |c: &Config| c.network.as_ref()?.warp_barrier.clone(), + "--warp-barrier=[NUM]", + "When warp enabled never attempt regular sync before warping to block NUM.", + + ARG arg_port: (u16) = 30303u16, or |c: &Config| c.network.as_ref()?.port.clone(), + "--port=[PORT]", + "Override the port on which the node should listen.", + + ARG arg_interface: (String) = "all", or |c: &Config| c.network.as_ref()?.interface.clone(), + "--interface=[IP]", + "Network interfaces. Valid values are 'all', 'local' or the ip of the interface you want OpenEthereum to listen to.", + + ARG arg_min_peers: (Option) = None, or |c: &Config| c.network.as_ref()?.min_peers.clone(), + "--min-peers=[NUM]", + "Try to maintain at least NUM peers.", + + ARG arg_max_peers: (Option) = None, or |c: &Config| c.network.as_ref()?.max_peers.clone(), + "--max-peers=[NUM]", + "Allow up to NUM peers.", + + ARG arg_snapshot_peers: (u16) = 0u16, or |c: &Config| c.network.as_ref()?.snapshot_peers.clone(), + "--snapshot-peers=[NUM]", + "Allow additional NUM peers for a snapshot sync.", + + ARG arg_nat: (String) = "any", or |c: &Config| c.network.as_ref()?.nat.clone(), + "--nat=[METHOD]", + "Specify method to use for determining public address. Must be one of: any, none, upnp, extip:.", + + ARG arg_allow_ips: (String) = "all", or |c: &Config| c.network.as_ref()?.allow_ips.clone(), + "--allow-ips=[FILTER]", + "Filter outbound connections. Must be one of: private - connect to private network IP addresses only; public - connect to public network IP addresses only; all - connect to any IP address.", + + ARG arg_max_pending_peers: (u16) = 64u16, or |c: &Config| c.network.as_ref()?.max_pending_peers.clone(), + "--max-pending-peers=[NUM]", + "Allow up to NUM pending connections.", + + ARG arg_network_id: (Option) = None, or |c: &Config| c.network.as_ref()?.id.clone(), + "--network-id=[INDEX]", + "Override the network identifier from the chain we are on.", + + ARG arg_bootnodes: (Option) = None, or |c: &Config| c.network.as_ref()?.bootnodes.as_ref().map(|vec| vec.join(",")), + "--bootnodes=[NODES]", + "Override the bootnodes from our chain. NODES should be comma-delimited enodes.", + + ARG arg_node_key: (Option) = None, or |c: &Config| c.network.as_ref()?.node_key.clone(), + "--node-key=[KEY]", + "Specify node secret key, either as 64-character hex string or input to SHA3 operation.", + + ARG arg_reserved_peers: (Option) = None, or |c: &Config| c.network.as_ref()?.reserved_peers.clone(), + "--reserved-peers=[FILE]", + "Provide a file containing enodes, one per line. These nodes will always have a reserved slot on top of the normal maximum peers.", + + CHECK |args: &Args| { + if let (Some(max_peers), Some(min_peers)) = (args.arg_max_peers, args.arg_min_peers) { + if min_peers > max_peers { + return Err(ArgsError::PeerConfiguration); + } + } + + Ok(()) + }, + + ["API and Console Options – HTTP JSON-RPC"] + FLAG flag_jsonrpc_allow_missing_blocks: (bool) = false, or |c: &Config| c.rpc.as_ref()?.allow_missing_blocks.clone(), + "--jsonrpc-allow-missing-blocks", + "RPC calls will return 'null' instead of an error if ancient block sync is still in progress and the block information requested could not be found", + + FLAG flag_no_jsonrpc: (bool) = false, or |c: &Config| c.rpc.as_ref()?.disable.clone(), + "--no-jsonrpc", + "Disable the HTTP JSON-RPC API server.", + + FLAG flag_jsonrpc_no_keep_alive: (bool) = false, or |c: &Config| c.rpc.as_ref()?.keep_alive, + "--jsonrpc-no-keep-alive", + "Disable HTTP/1.1 keep alive header. Disabling keep alive will prevent re-using the same TCP connection to fire multiple requests, recommended when using one request per connection.", + + FLAG flag_jsonrpc_experimental: (bool) = false, or |c: &Config| c.rpc.as_ref()?.experimental_rpcs.clone(), + "--jsonrpc-experimental", + "Enable experimental RPCs. Enable to have access to methods from unfinalised EIPs in all namespaces", + + ARG arg_jsonrpc_port: (u16) = 8545u16, or |c: &Config| c.rpc.as_ref()?.port.clone(), + "--jsonrpc-port=[PORT]", + "Specify the port portion of the HTTP JSON-RPC API server.", + + ARG arg_jsonrpc_interface: (String) = "local", or |c: &Config| c.rpc.as_ref()?.interface.clone(), + "--jsonrpc-interface=[IP]", + "Specify the hostname portion of the HTTP JSON-RPC API server, IP should be an interface's IP address, or all (all interfaces) or local.", + + ARG arg_jsonrpc_apis: (String) = "web3,eth,pubsub,net,parity,parity_pubsub,traces", or |c: &Config| c.rpc.as_ref()?.apis.as_ref().map(|vec| vec.join(",")), + "--jsonrpc-apis=[APIS]", + "Specify the APIs available through the HTTP JSON-RPC interface using a comma-delimited list of API names. Possible names are: all, safe, debug, web3, net, eth, pubsub, personal, signer, parity, parity_pubsub, parity_accounts, parity_set, traces, secretstore. You can also disable a specific API by putting '-' in the front, example: all,-personal. 'safe' enables the following APIs: web3, net, eth, pubsub, parity, parity_pubsub, traces", + + ARG arg_jsonrpc_hosts: (String) = "none", or |c: &Config| c.rpc.as_ref()?.hosts.as_ref().map(|vec| vec.join(",")), + "--jsonrpc-hosts=[HOSTS]", + "List of allowed Host header values. This option will validate the Host header sent by the browser, it is additional security against some attack vectors. Special options: \"all\", \"none\",.", + + ARG arg_jsonrpc_threads: (usize) = 4usize, or |c: &Config| c.rpc.as_ref()?.processing_threads, + "--jsonrpc-threads=[THREADS]", + "Turn on additional processing threads for JSON-RPC servers (all transports). Setting this to a non-zero value allows parallel execution of cpu-heavy queries.", + + ARG arg_jsonrpc_cors: (String) = "none", or |c: &Config| c.rpc.as_ref()?.cors.as_ref().map(|vec| vec.join(",")), + "--jsonrpc-cors=[URL]", + "Specify CORS header for HTTP JSON-RPC API responses. Special options: \"all\", \"none\".", + + ARG arg_jsonrpc_server_threads: (Option) = None, or |c: &Config| c.rpc.as_ref()?.server_threads, + "--jsonrpc-server-threads=[NUM]", + "Enables multiple threads handling incoming connections for HTTP JSON-RPC server.", + + ARG arg_jsonrpc_max_payload: (Option) = None, or |c: &Config| c.rpc.as_ref()?.max_payload, + "--jsonrpc-max-payload=[MB]", + "Specify maximum size for HTTP JSON-RPC requests in megabytes.", + + ARG arg_poll_lifetime: (u32) = 60u32, or |c: &Config| c.rpc.as_ref()?.poll_lifetime.clone(), + "--poll-lifetime=[S]", + "Set the RPC filter lifetime to S seconds. The filter has to be polled at least every S seconds , otherwise it is removed.", + + ["API and Console Options – WebSockets"] + FLAG flag_no_ws: (bool) = false, or |c: &Config| c.websockets.as_ref()?.disable.clone(), + "--no-ws", + "Disable the WebSockets JSON-RPC server.", + + ARG arg_ws_port: (u16) = 8546u16, or |c: &Config| c.websockets.as_ref()?.port.clone(), + "--ws-port=[PORT]", + "Specify the port portion of the WebSockets JSON-RPC server.", + + ARG arg_ws_interface: (String) = "local", or |c: &Config| c.websockets.as_ref()?.interface.clone(), + "--ws-interface=[IP]", + "Specify the hostname portion of the WebSockets JSON-RPC server, IP should be an interface's IP address, or all (all interfaces) or local.", + + ARG arg_ws_apis: (String) = "web3,eth,pubsub,net,parity,parity_pubsub,traces", or |c: &Config| c.websockets.as_ref()?.apis.as_ref().map(|vec| vec.join(",")), + "--ws-apis=[APIS]", + "Specify the JSON-RPC APIs available through the WebSockets interface using a comma-delimited list of API names. Possible names are: all, safe, web3, net, eth, pubsub, personal, signer, parity, parity_pubsub, parity_accounts, parity_set, traces, secretstore. You can also disable a specific API by putting '-' in the front, example: all,-personal. 'safe' enables the following APIs: web3, net, eth, pubsub, parity, parity_pubsub, traces", + + ARG arg_ws_origins: (String) = "parity://*,chrome-extension://*,moz-extension://*", or |c: &Config| c.websockets.as_ref()?.origins.as_ref().map(|vec| vec.join(",")), + "--ws-origins=[URL]", + "Specify Origin header values allowed to connect. Special options: \"all\", \"none\".", + + ARG arg_ws_hosts: (String) = "none", or |c: &Config| c.websockets.as_ref()?.hosts.as_ref().map(|vec| vec.join(",")), + "--ws-hosts=[HOSTS]", + "List of allowed Host header values. This option will validate the Host header sent by the browser, it is additional security against some attack vectors. Special options: \"all\", \"none\".", + + ARG arg_ws_max_connections: (usize) = 100usize, or |c: &Config| c.websockets.as_ref()?.max_connections, + "--ws-max-connections=[CONN]", + "Maximum number of allowed concurrent WebSockets JSON-RPC connections.", + + ["Metrics"] + FLAG flag_metrics: (bool) = false, or |c: &Config| c.metrics.as_ref()?.enable.clone(), + "--metrics", + "Enable prometheus metrics (only full client).", + + ARG arg_metrics_port: (u16) = 3000u16, or |c: &Config| c.metrics.as_ref()?.port.clone(), + "--metrics-port=[PORT]", + "Specify the port portion of the metrics server.", + + ARG arg_metrics_interface: (String) = "local", or |c: &Config| c.metrics.as_ref()?.interface.clone(), + "--metrics-interface=[IP]", + "Specify the hostname portion of the metrics server, IP should be an interface's IP address, or all (all interfaces) or local.", + + ["API and Console Options – IPC"] + FLAG flag_no_ipc: (bool) = false, or |c: &Config| c.ipc.as_ref()?.disable.clone(), + "--no-ipc", + "Disable JSON-RPC over IPC service.", + + ARG arg_ipc_path: (String) = if cfg!(windows) { r"\\.\pipe\jsonrpc.ipc" } else { "$BASE/jsonrpc.ipc" }, or |c: &Config| c.ipc.as_ref()?.path.clone(), + "--ipc-path=[PATH]", + "Specify custom path for JSON-RPC over IPC service.", + + ARG arg_ipc_apis: (String) = "web3,eth,pubsub,net,parity,parity_pubsub,parity_accounts,traces", or |c: &Config| c.ipc.as_ref()?.apis.as_ref().map(|vec| vec.join(",")), + "--ipc-apis=[APIS]", + "Specify custom API set available via JSON-RPC over IPC using a comma-delimited list of API names. Possible names are: all, safe, web3, net, eth, pubsub, personal, signer, parity, parity_pubsub, parity_accounts, parity_set, traces, secretstore. You can also disable a specific API by putting '-' in the front, example: all,-personal. 'safe' enables the following APIs: web3, net, eth, pubsub, parity, parity_pubsub, traces", + + ["Secret Store Options"] + FLAG flag_no_secretstore: (bool) = false, or |c: &Config| c.secretstore.as_ref()?.disable.clone(), + "--no-secretstore", + "Disable Secret Store functionality.", + + FLAG flag_no_secretstore_http: (bool) = false, or |c: &Config| c.secretstore.as_ref()?.disable_http.clone(), + "--no-secretstore-http", + "Disable Secret Store HTTP API.", + + FLAG flag_no_secretstore_auto_migrate: (bool) = false, or |c: &Config| c.secretstore.as_ref()?.disable_auto_migrate.clone(), + "--no-secretstore-auto-migrate", + "Do not run servers set change session automatically when servers set changes. This option has no effect when servers set is read from configuration file.", + + ARG arg_secretstore_acl_contract: (Option) = Some("registry".into()), or |c: &Config| c.secretstore.as_ref()?.acl_contract.clone(), + "--secretstore-acl-contract=[SOURCE]", + "Secret Store permissioning contract address source: none, registry (contract address is read from 'secretstore_acl_checker' entry in registry) or address.", + + ARG arg_secretstore_contract: (Option) = None, or |c: &Config| c.secretstore.as_ref()?.service_contract.clone(), + "--secretstore-contract=[SOURCE]", + "Secret Store Service contract address source: none, registry (contract address is read from 'secretstore_service' entry in registry) or address.", + + ARG arg_secretstore_srv_gen_contract: (Option) = None, or |c: &Config| c.secretstore.as_ref()?.service_contract_srv_gen.clone(), + "--secretstore-srv-gen-contract=[SOURCE]", + "Secret Store Service server key generation contract address source: none, registry (contract address is read from 'secretstore_service_srv_gen' entry in registry) or address.", + + ARG arg_secretstore_srv_retr_contract: (Option) = None, or |c: &Config| c.secretstore.as_ref()?.service_contract_srv_retr.clone(), + "--secretstore-srv-retr-contract=[SOURCE]", + "Secret Store Service server key retrieval contract address source: none, registry (contract address is read from 'secretstore_service_srv_retr' entry in registry) or address.", + + ARG arg_secretstore_doc_store_contract: (Option) = None, or |c: &Config| c.secretstore.as_ref()?.service_contract_doc_store.clone(), + "--secretstore-doc-store-contract=[SOURCE]", + "Secret Store Service document key store contract address source: none, registry (contract address is read from 'secretstore_service_doc_store' entry in registry) or address.", + + ARG arg_secretstore_doc_sretr_contract: (Option) = None, or |c: &Config| c.secretstore.as_ref()?.service_contract_doc_sretr.clone(), + "--secretstore-doc-sretr-contract=[SOURCE]", + "Secret Store Service document key shadow retrieval contract address source: none, registry (contract address is read from 'secretstore_service_doc_sretr' entry in registry) or address.", + + ARG arg_secretstore_nodes: (String) = "", or |c: &Config| c.secretstore.as_ref()?.nodes.as_ref().map(|vec| vec.join(",")), + "--secretstore-nodes=[NODES]", + "Comma-separated list of other secret store cluster nodes in form NODE_PUBLIC_KEY_IN_HEX@NODE_IP_ADDR:NODE_PORT.", + + ARG arg_secretstore_server_set_contract: (Option) = Some("registry".into()), or |c: &Config| c.secretstore.as_ref()?.server_set_contract.clone(), + "--secretstore-server-set-contract=[SOURCE]", + "Secret Store server set contract address source: none, registry (contract address is read from 'secretstore_server_set' entry in registry) or address.", + + ARG arg_secretstore_interface: (String) = "local", or |c: &Config| c.secretstore.as_ref()?.interface.clone(), + "--secretstore-interface=[IP]", + "Specify the hostname portion for listening to Secret Store Key Server internal requests, IP should be an interface's IP address, or local.", + + ARG arg_secretstore_port: (u16) = 8083u16, or |c: &Config| c.secretstore.as_ref()?.port.clone(), + "--secretstore-port=[PORT]", + "Specify the port portion for listening to Secret Store Key Server internal requests.", + + ARG arg_secretstore_http_interface: (String) = "local", or |c: &Config| c.secretstore.as_ref()?.http_interface.clone(), + "--secretstore-http-interface=[IP]", + "Specify the hostname portion for listening to Secret Store Key Server HTTP requests, IP should be an interface's IP address, or local.", + + ARG arg_secretstore_http_port: (u16) = 8082u16, or |c: &Config| c.secretstore.as_ref()?.http_port.clone(), + "--secretstore-http-port=[PORT]", + "Specify the port portion for listening to Secret Store Key Server HTTP requests.", + + ARG arg_secretstore_path: (String) = "$BASE/secretstore", or |c: &Config| c.secretstore.as_ref()?.path.clone(), + "--secretstore-path=[PATH]", + "Specify directory where Secret Store should save its data.", + + ARG arg_secretstore_secret: (Option) = None, or |c: &Config| c.secretstore.as_ref()?.self_secret.clone(), + "--secretstore-secret=[SECRET]", + "Hex-encoded secret key of this node.", + + ARG arg_secretstore_admin_public: (Option) = None, or |c: &Config| c.secretstore.as_ref()?.admin_public.clone(), + "--secretstore-admin=[PUBLIC]", + "Hex-encoded public key of secret store administrator.", + + ["Sealing/Mining Options"] + FLAG flag_force_sealing: (bool) = false, or |c: &Config| c.mining.as_ref()?.force_sealing.clone(), + "--force-sealing", + "Force the node to author new blocks as if it were always sealing/mining.", - FLAG flag_testnet: (bool) = false, or |_| None, - "--testnet", - "Testnet mode. Equivalent to --chain testnet. Overrides the --keys-path option.", + FLAG flag_reseal_on_uncle: (bool) = false, or |c: &Config| c.mining.as_ref()?.reseal_on_uncle.clone(), + "--reseal-on-uncle", + "Force the node to author new blocks when a new uncle block is imported.", - FLAG flag_nodiscover: (bool) = false, or |_| None, - "--nodiscover", - "Equivalent to --no-discovery.", + FLAG flag_remove_solved: (bool) = false, or |c: &Config| c.mining.as_ref()?.remove_solved.clone(), + "--remove-solved", + "Move solved blocks from the work package queue instead of cloning them. This gives a slightly faster import speed, but means that extra solutions submitted for the same work package will go unused.", - // FLAG Removed in 1.7. + FLAG flag_tx_queue_no_unfamiliar_locals: (bool) = false, or |c: &Config| c.mining.as_ref()?.tx_queue_no_unfamiliar_locals.clone(), + "--tx-queue-no-unfamiliar-locals", + "Local transactions sent through JSON-RPC (HTTP, WebSockets, etc) will be treated as 'external' if the sending account is unknown.", - FLAG flag_dapps_apis_all: (bool) = false, or |_| None, - "--dapps-apis-all", - "Dapps server is merged with HTTP JSON-RPC server. Use --jsonrpc-apis.", + FLAG flag_tx_queue_no_early_reject: (bool) = false, or |c: &Config| c.mining.as_ref()?.tx_queue_no_early_reject.clone(), + "--tx-queue-no-early-reject", + "Disables transaction queue optimization to early reject transactions below minimal effective gas price. This allows local transactions to always enter the pool, despite it being full, but requires additional ecrecover on every transaction.", - // FLAG Removed in 1.11. + FLAG flag_refuse_service_transactions: (bool) = false, or |c: &Config| c.mining.as_ref()?.refuse_service_transactions.clone(), + "--refuse-service-transactions", + "Always refuse service transactions.", - FLAG flag_public_node: (bool) = false, or |_| None, - "--public-node", - "Does nothing; Public node is removed from Parity.", + FLAG flag_infinite_pending_block: (bool) = false, or |c: &Config| c.mining.as_ref()?.infinite_pending_block.clone(), + "--infinite-pending-block", + "Pending block will be created with maximal possible gas limit and will execute all transactions in the queue. Note that such block is invalid and should never be attempted to be mined.", - FLAG flag_force_ui: (bool) = false, or |_| None, - "--force-ui", - "Does nothing; UI is now a separate project.", + FLAG flag_no_persistent_txqueue: (bool) = false, or |c: &Config| c.parity.as_ref()?.no_persistent_txqueue, + "--no-persistent-txqueue", + "Don't save pending local transactions to disk to be restored whenever the node restarts.", - FLAG flag_no_ui: (bool) = false, or |_| None, - "--no-ui", - "Does nothing; UI is now a separate project.", + FLAG flag_stratum: (bool) = false, or |c: &Config| Some(c.stratum.is_some()), + "--stratum", + "Run Stratum server for miner push notification.", - FLAG flag_ui_no_validation: (bool) = false, or |_| None, - "--ui-no-validation", - "Does nothing; UI is now a separate project.", + ARG arg_reseal_on_txs: (String) = "own", or |c: &Config| c.mining.as_ref()?.reseal_on_txs.clone(), + "--reseal-on-txs=[SET]", + "Specify which transactions should force the node to reseal a block. SET is one of: none - never reseal on new transactions; own - reseal only on a new local transaction; ext - reseal only on a new external transaction; all - reseal on all new transactions.", - // FLAG Removed in 2.0. + ARG arg_reseal_min_period: (u64) = 2000u64, or |c: &Config| c.mining.as_ref()?.reseal_min_period.clone(), + "--reseal-min-period=[MS]", + "Specify the minimum time between reseals from incoming transactions. MS is time measured in milliseconds.", - FLAG flag_fast_and_loose: (bool) = false, or |_| None, - "--fast-and-loose", - "Does nothing; DB WAL is always activated.", + ARG arg_reseal_max_period: (u64) = 120000u64, or |c: &Config| c.mining.as_ref()?.reseal_max_period.clone(), + "--reseal-max-period=[MS]", + "Specify the maximum time since last block to enable force-sealing. MS is time measured in milliseconds.", + + ARG arg_work_queue_size: (usize) = 20usize, or |c: &Config| c.mining.as_ref()?.work_queue_size.clone(), + "--work-queue-size=[ITEMS]", + "Specify the number of historical work packages which are kept cached lest a solution is found for them later. High values take more memory but result in fewer unusable solutions.", + + ARG arg_relay_set: (String) = "cheap", or |c: &Config| c.mining.as_ref()?.relay_set.clone(), + "--relay-set=[SET]", + "Set of transactions to relay. SET may be: cheap - Relay any transaction in the queue (this may include invalid transactions); strict - Relay only executed transactions (this guarantees we don't relay invalid transactions, but means we relay nothing if not mining); lenient - Same as strict when mining, and cheap when not.", + + ARG arg_usd_per_tx: (String) = "0.0001", or |c: &Config| c.mining.as_ref()?.usd_per_tx.clone(), + "--usd-per-tx=[USD]", + "Amount of USD to be paid for a basic transaction. The minimum gas price is set accordingly.", + + ARG arg_usd_per_eth: (String) = "auto", or |c: &Config| c.mining.as_ref()?.usd_per_eth.clone(), + "--usd-per-eth=[SOURCE]", + "USD value of a single ETH. SOURCE may be either an amount in USD, a web service or 'auto' to use each web service in turn and fallback on the last known good value.", + + ARG arg_price_update_period: (String) = "hourly", or |c: &Config| c.mining.as_ref()?.price_update_period.clone(), + "--price-update-period=[T]", + "T will be allowed to pass between each gas price update. T may be daily, hourly, a number of seconds, or a time string of the form \"2 days\", \"30 minutes\" etc..", + + ARG arg_gas_floor_target: (String) = "8000000", or |c: &Config| c.mining.as_ref()?.gas_floor_target.clone(), + "--gas-floor-target=[GAS]", + "Amount of gas per block to target when sealing a new block.", + + ARG arg_gas_cap: (String) = "10000000", or |c: &Config| c.mining.as_ref()?.gas_cap.clone(), + "--gas-cap=[GAS]", + "A cap on how large we will raise the gas limit per block due to transaction volume.", + + ARG arg_tx_queue_mem_limit: (u32) = 4u32, or |c: &Config| c.mining.as_ref()?.tx_queue_mem_limit.clone(), + "--tx-queue-mem-limit=[MB]", + "Maximum amount of memory that can be used by the transaction queue. Setting this parameter to 0 disables limiting.", + + ARG arg_tx_queue_size: (usize) = 8_192usize, or |c: &Config| c.mining.as_ref()?.tx_queue_size.clone(), + "--tx-queue-size=[LIMIT]", + "Maximum amount of transactions in the queue (waiting to be included in next block).", + + ARG arg_tx_queue_per_sender: (Option) = None, or |c: &Config| c.mining.as_ref()?.tx_queue_per_sender.clone(), + "--tx-queue-per-sender=[LIMIT]", + "Maximum number of transactions per sender in the queue. By default it's 1% of the entire queue, but not less than 16.", + + ARG arg_tx_queue_locals: (Option) = None, or |c: &Config| helpers::join_set(c.mining.as_ref()?.tx_queue_locals.as_ref()), + "--tx-queue-locals=[ACCOUNTS]", + "Specify local accounts for which transactions are prioritized in the queue. ACCOUNTS is a comma-delimited list of addresses.", + + ARG arg_tx_queue_strategy: (String) = "gas_price", or |c: &Config| c.mining.as_ref()?.tx_queue_strategy.clone(), + "--tx-queue-strategy=[S]", + "Prioritization strategy used to order transactions in the queue. S may be: gas_price - Prioritize txs with high gas price", + + ARG arg_stratum_interface: (String) = "local", or |c: &Config| c.stratum.as_ref()?.interface.clone(), + "--stratum-interface=[IP]", + "Interface address for Stratum server.", + + ARG arg_stratum_port: (u16) = 8008u16, or |c: &Config| c.stratum.as_ref()?.port.clone(), + "--stratum-port=[PORT]", + "Port for Stratum server to listen on.", - FLAG flag_no_dapps: (bool) = false, or |c: &Config| c.dapps.as_ref()?._legacy_disable.clone(), - "--no-dapps", - "Disable the Dapps server (e.g. status page).", + ARG arg_min_gas_price: (Option) = None, or |c: &Config| c.mining.as_ref()?.min_gas_price.clone(), + "--min-gas-price=[STRING]", + "Minimum amount of Wei per GAS to be paid for a transaction to be accepted for mining. Overrides --usd-per-tx.", - // ARG Removed in 1.6 or before. + ARG arg_gas_price_percentile: (usize) = 50usize, or |c: &Config| c.mining.as_ref()?.gas_price_percentile, + "--gas-price-percentile=[PCT]", + "Set PCT percentile gas price value from last 100 blocks as default gas price when sending transactions.", - ARG arg_etherbase: (Option) = None, or |_| None, - "--etherbase=[ADDRESS]", - "Equivalent to --author ADDRESS.", + ARG arg_author: (Option) = None, or |c: &Config| c.mining.as_ref()?.author.clone(), + "--author=[ADDRESS]", + "Specify the block author (aka \"coinbase\") address for sending block rewards from sealed blocks. NOTE: MINING WILL NOT WORK WITHOUT THIS OPTION.", // Sealing/Mining Option - ARG arg_extradata: (Option) = None, or |_| None, - "--extradata=[STRING]", - "Equivalent to --extra-data STRING.", + ARG arg_engine_signer: (Option) = None, or |c: &Config| c.mining.as_ref()?.engine_signer.clone(), + "--engine-signer=[ADDRESS]", + "Specify the address which should be used to sign consensus messages and issue blocks. Relevant only to non-PoW chains.", - ARG arg_datadir: (Option) = None, or |_| None, - "--datadir=[PATH]", - "Equivalent to --base-path PATH.", + ARG arg_tx_gas_limit: (Option) = None, or |c: &Config| c.mining.as_ref()?.tx_gas_limit.clone(), + "--tx-gas-limit=[GAS]", + "Apply a limit of GAS as the maximum amount of gas a single transaction may have for it to be mined.", - ARG arg_networkid: (Option) = None, or |_| None, - "--networkid=[INDEX]", - "Equivalent to --network-id INDEX.", + ARG arg_tx_time_limit: (Option) = None, or |c: &Config| c.mining.as_ref()?.tx_time_limit.clone(), + "--tx-time-limit=[MS]", + "Maximal time for processing single transaction. If enabled senders of transactions offending the limit will get other transactions penalized.", - ARG arg_peers: (Option) = None, or |_| None, - "--peers=[NUM]", - "Equivalent to --min-peers NUM.", - - ARG arg_nodekey: (Option) = None, or |_| None, - "--nodekey=[KEY]", - "Equivalent to --node-key KEY.", - - ARG arg_rpcaddr: (Option) = None, or |_| None, - "--rpcaddr=[IP]", - "Equivalent to --jsonrpc-interface IP.", - - ARG arg_rpcport: (Option) = None, or |_| None, - "--rpcport=[PORT]", - "Equivalent to --jsonrpc-port PORT.", - - ARG arg_rpcapi: (Option) = None, or |_| None, - "--rpcapi=[APIS]", - "Equivalent to --jsonrpc-apis APIS.", - - ARG arg_rpccorsdomain: (Option) = None, or |_| None, - "--rpccorsdomain=[URL]", - "Equivalent to --jsonrpc-cors URL.", - - ARG arg_ipcapi: (Option) = None, or |_| None, - "--ipcapi=[APIS]", - "Equivalent to --ipc-apis APIS.", - - ARG arg_ipcpath: (Option) = None, or |_| None, - "--ipcpath=[PATH]", - "Equivalent to --ipc-path PATH.", - - ARG arg_gasprice: (Option) = None, or |_| None, - "--gasprice=[WEI]", - "Equivalent to --min-gas-price WEI.", - - ARG arg_cache: (Option) = None, or |_| None, - "--cache=[MB]", - "Equivalent to --cache-size MB.", - - // ARG Removed in 1.7. - - ARG arg_dapps_port: (Option) = None, or |c: &Config| c.dapps.as_ref()?._legacy_port.clone(), - "--dapps-port=[PORT]", - "Does nothing; dapps server has been removed.", - - ARG arg_dapps_interface: (Option) = None, or |c: &Config| c.dapps.as_ref()?._legacy_interface.clone(), - "--dapps-interface=[IP]", - "Does nothing; dapps server has been removed.", - - ARG arg_dapps_hosts: (Option) = None, or |c: &Config| c.dapps.as_ref()?._legacy_hosts.as_ref().map(|vec| vec.join(",")), - "--dapps-hosts=[HOSTS]", - "Does nothing; dapps server has been removed.", - - ARG arg_dapps_cors: (Option) = None, or |c: &Config| c.dapps.as_ref()?._legacy_cors.clone(), - "--dapps-cors=[URL]", - "Does nothing; dapps server has been removed.", - - ARG arg_dapps_user: (Option) = None, or |c: &Config| c.dapps.as_ref()?._legacy_user.clone(), - "--dapps-user=[USERNAME]", - "Dapps server authentication has been removed.", - - ARG arg_dapps_pass: (Option) = None, or |c: &Config| c.dapps.as_ref()?._legacy_pass.clone(), - "--dapps-pass=[PASSWORD]", - "Dapps server authentication has been removed.", - - // ARG removed in 1.11. - - ARG arg_ui_interface: (Option) = None, or |_| None, - "--ui-interface=[IP]", - "Does nothing; UI is now a separate project.", - - ARG arg_ui_hosts: (Option) = None, or |_| None, - "--ui-hosts=[HOSTS]", - "Does nothing; UI is now a separate project.", - - ARG arg_ui_port: (Option) = None, or |_| None, - "--ui-port=[PORT]", - "Does nothing; UI is now a separate project.", - - ARG arg_tx_queue_ban_count: (Option) = None, or |c: &Config| c.mining.as_ref()?.tx_queue_ban_count.clone(), - "--tx-queue-ban-count=[C]", - "Not supported.", - - ARG arg_tx_queue_ban_time: (Option) = None, or |c: &Config| c.mining.as_ref()?.tx_queue_ban_time.clone(), - "--tx-queue-ban-time=[SEC]", - "Not supported.", - - // ARG removed in 2.0. - - ARG arg_dapps_path: (Option) = None, or |c: &Config| c.dapps.as_ref()?._legacy_path.clone(), - "--dapps-path=[PATH]", - "Specify directory where dapps should be installed.", - - ARG arg_ntp_servers: (Option) = None, or |_| None, - "--ntp-servers=[HOSTS]", - "Does nothing; checking if clock is sync with NTP servers is now done on the UI.", - } + ARG arg_extra_data: (Option) = None, or |c: &Config| c.mining.as_ref()?.extra_data.clone(), + "--extra-data=[STRING]", + "Specify a custom extra-data for authored blocks, no more than 32 characters.", + + ARG arg_notify_work: (Option) = None, or |c: &Config| c.mining.as_ref()?.notify_work.as_ref().map(|vec| vec.join(",")), + "--notify-work=[URLS]", + "URLs to which work package notifications are pushed. URLS should be a comma-delimited list of HTTP URLs.", + + ARG arg_stratum_secret: (Option) = None, or |c: &Config| c.stratum.as_ref()?.secret.clone(), + "--stratum-secret=[STRING]", + "Secret for authorizing Stratum server for peers.", + + ARG arg_max_round_blocks_to_import: (usize) = 12usize, or |c: &Config| c.mining.as_ref()?.max_round_blocks_to_import.clone(), + "--max-round-blocks-to-import=[S]", + "Maximal number of blocks to import for each import round.", + + ["Internal Options"] + FLAG flag_can_restart: (bool) = false, or |_| None, + "--can-restart", + "Executable will auto-restart if exiting with 69", + + ["Miscellaneous Options"] + FLAG flag_no_color: (bool) = false, or |c: &Config| c.misc.as_ref()?.color.map(|c| !c).clone(), + "--no-color", + "Don't use terminal color codes in output.", + + FLAG flag_version: (bool) = false, or |_| None, + "-v, --version", + "Show information about version.", + + FLAG flag_no_config: (bool) = false, or |_| None, + "--no-config", + "Don't load a configuration file.", + + ARG arg_logging: (Option) = None, or |c: &Config| c.misc.as_ref()?.logging.clone(), + "-l, --logging=[LOGGING]", + "Specify the general logging level (error, warn, info, debug or trace). It can also be set for a specific module, example: '-l sync=debug,rpc=trace'", + + ARG arg_log_file: (Option) = None, or |c: &Config| c.misc.as_ref()?.log_file.clone(), + "--log-file=[FILENAME]", + "Specify a filename into which logging should be appended.", + + ["Footprint Options"] + FLAG flag_scale_verifiers: (bool) = false, or |c: &Config| c.footprint.as_ref()?.scale_verifiers.clone(), + "--scale-verifiers", + "Automatically scale amount of verifier threads based on workload. Not guaranteed to be faster.", + + ARG arg_tracing: (String) = "auto", or |c: &Config| c.footprint.as_ref()?.tracing.clone(), + "--tracing=[BOOL]", + "Indicates if full transaction tracing should be enabled. Works only if client had been fully synced with tracing enabled. BOOL may be one of auto, on, off. auto uses last used value of this option (off if it does not exist).", // footprint option + + ARG arg_pruning: (String) = "auto", or |c: &Config| c.footprint.as_ref()?.pruning.clone(), + "--pruning=[METHOD]", + "Configure pruning of the state/storage trie. METHOD may be one of auto, archive, fast: archive - keep all state trie data. No pruning. fast - maintain journal overlay. Fast but 50MB used. auto - use the method most recently synced or default to fast if none synced.", + + ARG arg_pruning_history: (u64) = 64u64, or |c: &Config| c.footprint.as_ref()?.pruning_history.clone(), + "--pruning-history=[NUM]", + "Set a minimum number of recent states to keep in memory when pruning is active.", + + ARG arg_pruning_memory: (usize) = 32usize, or |c: &Config| c.footprint.as_ref()?.pruning_memory.clone(), + "--pruning-memory=[MB]", + "The ideal amount of memory in megabytes to use to store recent states. As many states as possible will be kept within this limit, and at least --pruning-history states will always be kept.", + + ARG arg_cache_size_db: (u32) = 128u32, or |c: &Config| c.footprint.as_ref()?.cache_size_db.clone(), + "--cache-size-db=[MB]", + "Override database cache size.", + + ARG arg_cache_size_blocks: (u32) = 8u32, or |c: &Config| c.footprint.as_ref()?.cache_size_blocks.clone(), + "--cache-size-blocks=[MB]", + "Specify the preferred size of the blockchain cache in megabytes.", + + ARG arg_cache_size_queue: (u32) = 40u32, or |c: &Config| c.footprint.as_ref()?.cache_size_queue.clone(), + "--cache-size-queue=[MB]", + "Specify the maximum size of memory to use for block queue.", + + ARG arg_cache_size_state: (u32) = 25u32, or |c: &Config| c.footprint.as_ref()?.cache_size_state.clone(), + "--cache-size-state=[MB]", + "Specify the maximum size of memory to use for the state cache.", + + ARG arg_db_compaction: (String) = "auto", or |c: &Config| c.footprint.as_ref()?.db_compaction.clone(), + "--db-compaction=[TYPE]", + "Database compaction type. TYPE may be one of: ssd - suitable for SSDs and fast HDDs; hdd - suitable for slow HDDs; auto - determine automatically.", + + ARG arg_fat_db: (String) = "auto", or |c: &Config| c.footprint.as_ref()?.fat_db.clone(), + "--fat-db=[BOOL]", + "Build appropriate information to allow enumeration of all accounts and storage keys. Doubles the size of the state database. BOOL may be one of on, off or auto.", + + ARG arg_cache_size: (Option) = None, or |c: &Config| c.footprint.as_ref()?.cache_size.clone(), + "--cache-size=[MB]", + "Set total amount of discretionary memory to use for the entire system, overrides other cache and queue options.", + + ARG arg_num_verifiers: (Option) = None, or |c: &Config| c.footprint.as_ref()?.num_verifiers.clone(), + "--num-verifiers=[INT]", + "Amount of verifier threads to use or to begin with, if verifier auto-scaling is enabled.", + + ["Import/export Options"] + FLAG flag_no_seal_check: (bool) = false, or |_| None, + "--no-seal-check", + "Skip block seal check.", + + ["Snapshot Options"] + FLAG flag_enable_snapshotting: (bool) = false, or |c: &Config| c.snapshots.as_ref()?.enable.clone(), + "--enable-snapshotting", + "Enable automated snapshots which usually occur once every 5000 blocks.", + + ARG arg_snapshot_threads: (Option) = None, or |c: &Config| c.snapshots.as_ref()?.processing_threads, + "--snapshot-threads=[NUM]", + "Enables multiple threads for snapshots creation.", + } } #[derive(Default, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] struct Config { - parity: Option, - account: Option, - ui: Option, - network: Option, - rpc: Option, - websockets: Option, - ipc: Option, - dapps: Option, - secretstore: Option, - private_tx: Option, - ipfs: Option, - mining: Option, - footprint: Option, - snapshots: Option, - misc: Option, - stratum: Option, - whisper: Option, - light: Option, + parity: Option, + account: Option, + ui: Option, + network: Option, + rpc: Option, + websockets: Option, + ipc: Option, + secretstore: Option, + mining: Option, + footprint: Option, + snapshots: Option, + misc: Option, + stratum: Option, + metrics: Option, } #[derive(Default, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] struct Operating { - mode: Option, - mode_timeout: Option, - mode_alarm: Option, - auto_update: Option, - auto_update_delay: Option, - auto_update_check_frequency: Option, - release_track: Option, - no_download: Option, - no_consensus: Option, - chain: Option, - base_path: Option, - db_path: Option, - keys_path: Option, - identity: Option, - light: Option, - no_persistent_txqueue: Option, - no_hardcoded_sync: Option, - - #[serde(rename = "public_node")] - _legacy_public_node: Option, + mode: Option, + mode_timeout: Option, + mode_alarm: Option, + chain: Option, + base_path: Option, + db_path: Option, + keys_path: Option, + identity: Option, + no_persistent_txqueue: Option, } #[derive(Default, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] struct Account { - unlock: Option>, - password: Option>, - keys_iterations: Option, - refresh_time: Option, - disable_hardware: Option, - fast_unlock: Option, -} - -#[derive(Default, Debug, PartialEq, Deserialize)] -#[serde(deny_unknown_fields)] -struct PrivateTransactions { - enabled: Option, - signer: Option, - validators: Option>, - account: Option, - passwords: Option, - sstore_url: Option, - sstore_threshold: Option, + unlock: Option>, + password: Option>, + keys_iterations: Option, + refresh_time: Option, + fast_unlock: Option, } #[derive(Default, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] struct Ui { - path: Option, - - #[serde(rename = "force")] - _legacy_force: Option, - #[serde(rename = "disable")] - _legacy_disable: Option, - #[serde(rename = "port")] - _legacy_port: Option, - #[serde(rename = "interface")] - _legacy_interface: Option, - #[serde(rename = "hosts")] - _legacy_hosts: Option>, + path: Option, } #[derive(Default, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] struct Network { - warp: Option, - warp_barrier: Option, - port: Option, - interface: Option, - min_peers: Option, - max_peers: Option, - snapshot_peers: Option, - max_pending_peers: Option, - nat: Option, - allow_ips: Option, - id: Option, - bootnodes: Option>, - discovery: Option, - node_key: Option, - reserved_peers: Option, - reserved_only: Option, - no_serve_light: Option, + warp: Option, + warp_barrier: Option, + port: Option, + interface: Option, + min_peers: Option, + max_peers: Option, + snapshot_peers: Option, + max_pending_peers: Option, + nat: Option, + allow_ips: Option, + id: Option, + bootnodes: Option>, + discovery: Option, + node_key: Option, + reserved_peers: Option, + reserved_only: Option, } #[derive(Default, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] struct Rpc { - disable: Option, - port: Option, - interface: Option, - cors: Option>, - apis: Option>, - hosts: Option>, - server_threads: Option, - processing_threads: Option, - max_payload: Option, - keep_alive: Option, - experimental_rpcs: Option, - poll_lifetime: Option, - allow_missing_blocks: Option, + disable: Option, + port: Option, + interface: Option, + cors: Option>, + apis: Option>, + hosts: Option>, + server_threads: Option, + processing_threads: Option, + max_payload: Option, + keep_alive: Option, + experimental_rpcs: Option, + poll_lifetime: Option, + allow_missing_blocks: Option, } #[derive(Default, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] struct Ws { - disable: Option, - port: Option, - interface: Option, - apis: Option>, - origins: Option>, - hosts: Option>, - max_connections: Option, + disable: Option, + port: Option, + interface: Option, + apis: Option>, + origins: Option>, + hosts: Option>, + max_connections: Option, } #[derive(Default, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] struct Ipc { - disable: Option, - path: Option, - apis: Option>, + disable: Option, + path: Option, + apis: Option>, } #[derive(Default, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] -struct Dapps { - #[serde(rename = "disable")] - _legacy_disable: Option, - #[serde(rename = "port")] - _legacy_port: Option, - #[serde(rename = "interface")] - _legacy_interface: Option, - #[serde(rename = "hosts")] - _legacy_hosts: Option>, - #[serde(rename = "cors")] - _legacy_cors: Option, - #[serde(rename = "path")] - _legacy_path: Option, - #[serde(rename = "user")] - _legacy_user: Option, - #[serde(rename = "pass")] - _legacy_pass: Option, +struct Metrics { + enable: Option, + port: Option, + interface: Option, } #[derive(Default, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] struct SecretStore { - disable: Option, - disable_http: Option, - disable_auto_migrate: Option, - acl_contract: Option, - service_contract: Option, - service_contract_srv_gen: Option, - service_contract_srv_retr: Option, - service_contract_doc_store: Option, - service_contract_doc_sretr: Option, - self_secret: Option, - admin_public: Option, - nodes: Option>, - server_set_contract: Option, - interface: Option, - port: Option, - http_interface: Option, - http_port: Option, - path: Option, -} - -#[derive(Default, Debug, PartialEq, Deserialize)] -#[serde(deny_unknown_fields)] -struct Ipfs { - enable: Option, - port: Option, - interface: Option, - cors: Option>, - hosts: Option>, + disable: Option, + disable_http: Option, + disable_auto_migrate: Option, + acl_contract: Option, + service_contract: Option, + service_contract_srv_gen: Option, + service_contract_srv_retr: Option, + service_contract_doc_store: Option, + service_contract_doc_sretr: Option, + self_secret: Option, + admin_public: Option, + nodes: Option>, + server_set_contract: Option, + interface: Option, + port: Option, + http_interface: Option, + http_port: Option, + path: Option, } #[derive(Default, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] struct Mining { - author: Option, - engine_signer: Option, - force_sealing: Option, - reseal_on_uncle: Option, - reseal_on_txs: Option, - reseal_min_period: Option, - reseal_max_period: Option, - work_queue_size: Option, - tx_gas_limit: Option, - tx_time_limit: Option, - relay_set: Option, - min_gas_price: Option, - gas_price_percentile: Option, - usd_per_tx: Option, - usd_per_eth: Option, - price_update_period: Option, - gas_floor_target: Option, - gas_cap: Option, - extra_data: Option, - tx_queue_size: Option, - tx_queue_per_sender: Option, - tx_queue_mem_limit: Option, - tx_queue_locals: Option>, - tx_queue_strategy: Option, - tx_queue_ban_count: Option, - tx_queue_ban_time: Option, - tx_queue_no_unfamiliar_locals: Option, - tx_queue_no_early_reject: Option, - remove_solved: Option, - notify_work: Option>, - refuse_service_transactions: Option, - infinite_pending_block: Option, - max_round_blocks_to_import: Option, + author: Option, + engine_signer: Option, + force_sealing: Option, + reseal_on_uncle: Option, + reseal_on_txs: Option, + reseal_min_period: Option, + reseal_max_period: Option, + work_queue_size: Option, + tx_gas_limit: Option, + tx_time_limit: Option, + relay_set: Option, + min_gas_price: Option, + gas_price_percentile: Option, + usd_per_tx: Option, + usd_per_eth: Option, + price_update_period: Option, + gas_floor_target: Option, + gas_cap: Option, + extra_data: Option, + tx_queue_size: Option, + tx_queue_per_sender: Option, + tx_queue_mem_limit: Option, + tx_queue_locals: Option>, + tx_queue_strategy: Option, + tx_queue_ban_count: Option, + tx_queue_ban_time: Option, + tx_queue_no_unfamiliar_locals: Option, + tx_queue_no_early_reject: Option, + remove_solved: Option, + notify_work: Option>, + refuse_service_transactions: Option, + infinite_pending_block: Option, + max_round_blocks_to_import: Option, } #[derive(Default, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] struct Stratum { - interface: Option, - port: Option, - secret: Option, + interface: Option, + port: Option, + secret: Option, } #[derive(Default, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] struct Footprint { - tracing: Option, - pruning: Option, - pruning_history: Option, - pruning_memory: Option, - fast_and_loose: Option, - cache_size: Option, - cache_size_db: Option, - cache_size_blocks: Option, - cache_size_queue: Option, - cache_size_state: Option, - db_compaction: Option, - fat_db: Option, - scale_verifiers: Option, - num_verifiers: Option, + tracing: Option, + pruning: Option, + pruning_history: Option, + pruning_memory: Option, + fast_and_loose: Option, + cache_size: Option, + cache_size_db: Option, + cache_size_blocks: Option, + cache_size_queue: Option, + cache_size_state: Option, + db_compaction: Option, + fat_db: Option, + scale_verifiers: Option, + num_verifiers: Option, } #[derive(Default, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] struct Snapshots { - disable_periodic: Option, - processing_threads: Option, + enable: Option, + processing_threads: Option, } #[derive(Default, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] struct Misc { - logging: Option, - log_file: Option, - color: Option, - ports_shift: Option, - unsafe_expose: Option, -} - -#[derive(Default, Debug, PartialEq, Deserialize)] -#[serde(deny_unknown_fields)] -struct Whisper { - enabled: Option, - pool_size: Option, -} - -#[derive(Default, Debug, PartialEq, Deserialize)] -#[serde(deny_unknown_fields)] -struct Light { - on_demand_response_time_window: Option, - on_demand_request_backoff_start: Option, - on_demand_request_backoff_max: Option, - on_demand_request_backoff_rounds_max: Option, - on_demand_request_consecutive_failures: Option, + logging: Option, + log_file: Option, + color: Option, + ports_shift: Option, + unsafe_expose: Option, } #[cfg(test)] mod tests { - use super::{ - Args, ArgsError, - Config, Operating, Account, Ui, Network, Ws, Rpc, Ipc, Dapps, Ipfs, Mining, Footprint, - Snapshots, Misc, Whisper, SecretStore, Light, - }; - use toml; - use clap::{ErrorKind as ClapErrorKind}; - - #[test] - fn should_accept_any_argument_order() { - let args = Args::parse(&["parity", "--no-warp", "account", "list"]).unwrap(); - assert_eq!(args.flag_no_warp, true); - - let args = Args::parse(&["parity", "account", "list", "--no-warp"]).unwrap(); - assert_eq!(args.flag_no_warp, true); - - let args = Args::parse(&["parity", "--chain=dev", "account", "list"]).unwrap(); - assert_eq!(args.arg_chain, "dev"); - - let args = Args::parse(&["parity", "account", "list", "--chain=dev"]).unwrap(); - assert_eq!(args.arg_chain, "dev"); - } - - #[test] - fn should_not_crash_on_warp() { - let args = Args::parse(&["parity", "--warp"]); - assert!(args.is_ok()); - - let args = Args::parse(&["parity", "account", "list", "--warp"]); - assert!(args.is_ok()); - } - - #[test] - fn should_reject_invalid_values() { - let args = Args::parse(&["parity", "--cache=20"]); - assert!(args.is_ok()); - - let args = Args::parse(&["parity", "--cache=asd"]); - assert!(args.is_err()); - } - - #[test] - fn should_parse_args_and_flags() { - let args = Args::parse(&["parity", "--no-warp"]).unwrap(); - assert_eq!(args.flag_no_warp, true); - - let args = Args::parse(&["parity", "--pruning", "archive"]).unwrap(); - assert_eq!(args.arg_pruning, "archive"); - - let args = Args::parse(&["parity", "export", "state", "--no-storage"]).unwrap(); - assert_eq!(args.flag_export_state_no_storage, true); - - let args = Args::parse(&["parity", "export", "state", "--min-balance","123"]).unwrap(); - assert_eq!(args.arg_export_state_min_balance, Some("123".to_string())); - } - - #[test] - fn should_exit_gracefully_on_unknown_argument() { - let result = Args::parse(&["parity", "--please-exit-gracefully"]); - assert!( - match result { - Err(ArgsError::Clap(ref clap_error)) if clap_error.kind == ClapErrorKind::UnknownArgument => true, - _ => false - } - ); - } - - #[test] - fn should_use_subcommand_arg_default() { - let args = Args::parse(&["parity", "export", "state", "--at", "123"]).unwrap(); - assert_eq!(args.arg_export_state_at, "123"); - assert_eq!(args.arg_snapshot_at, "latest"); - - let args = Args::parse(&["parity", "snapshot", "--at", "123", "file.dump"]).unwrap(); - assert_eq!(args.arg_snapshot_at, "123"); - assert_eq!(args.arg_export_state_at, "latest"); - - let args = Args::parse(&["parity", "export", "state"]).unwrap(); - assert_eq!(args.arg_snapshot_at, "latest"); - assert_eq!(args.arg_export_state_at, "latest"); - - let args = Args::parse(&["parity", "snapshot", "file.dump"]).unwrap(); - assert_eq!(args.arg_snapshot_at, "latest"); - assert_eq!(args.arg_export_state_at, "latest"); - } - - #[test] - fn should_parse_multiple_values() { - let args = Args::parse(&["parity", "account", "import", "~/1", "~/2"]).unwrap(); - assert_eq!(args.arg_account_import_path, Some(vec!["~/1".to_owned(), "~/2".to_owned()])); - - let args = Args::parse(&["parity", "account", "import", "~/1,ext"]).unwrap(); - assert_eq!(args.arg_account_import_path, Some(vec!["~/1,ext".to_owned()])); - - let args = Args::parse(&["parity", "--secretstore-nodes", "abc@127.0.0.1:3333,cde@10.10.10.10:4444"]).unwrap(); - assert_eq!(args.arg_secretstore_nodes, "abc@127.0.0.1:3333,cde@10.10.10.10:4444"); - - let args = Args::parse(&["parity", "--password", "~/.safe/1", "--password", "~/.safe/2", "--ui-port", "8123"]).unwrap(); - assert_eq!(args.arg_password, vec!["~/.safe/1".to_owned(), "~/.safe/2".to_owned()]); - assert_eq!(args.arg_ui_port, Some(8123)); - - let args = Args::parse(&["parity", "--password", "~/.safe/1,~/.safe/2", "--ui-port", "8123"]).unwrap(); - assert_eq!(args.arg_password, vec!["~/.safe/1".to_owned(), "~/.safe/2".to_owned()]); - assert_eq!(args.arg_ui_port, Some(8123)); - } - - #[test] - fn should_parse_global_args_with_subcommand() { - let args = Args::parse(&["parity", "--chain", "dev", "account", "list"]).unwrap(); - assert_eq!(args.arg_chain, "dev".to_owned()); - } - - #[test] - fn should_parse_args_and_include_config() { - // given - let mut config = Config::default(); - let mut operating = Operating::default(); - operating.chain = Some("morden".into()); - config.parity = Some(operating); - - // when - let args = Args::parse_with_config(&["parity"], config).unwrap(); - - // then - assert_eq!(args.arg_chain, "morden".to_owned()); - } - - #[test] - fn should_not_use_config_if_cli_is_provided() { - // given - let mut config = Config::default(); - let mut operating = Operating::default(); - operating.chain = Some("morden".into()); - config.parity = Some(operating); - - // when - let args = Args::parse_with_config(&["parity", "--chain", "xyz"], config).unwrap(); - - // then - assert_eq!(args.arg_chain, "xyz".to_owned()); - } - - #[test] - fn should_use_config_if_cli_is_missing() { - let mut config = Config::default(); - let mut footprint = Footprint::default(); - footprint.pruning_history = Some(128); - config.footprint = Some(footprint); - - // when - let args = Args::parse_with_config(&["parity"], config).unwrap(); - - // then - assert_eq!(args.arg_pruning_history, 128); - } - - #[test] - fn should_parse_full_config() { - // given - let config = toml::from_str(include_str!("./tests/config.full.toml")).unwrap(); - - // when - let args = Args::parse_with_config(&["parity", "--chain", "xyz"], config).unwrap(); - - // then - assert_eq!(args, Args { - // Commands - cmd_dapp: false, - cmd_daemon: false, - cmd_account: false, - cmd_account_new: false, - cmd_account_list: false, - cmd_account_import: false, - cmd_wallet: false, - cmd_wallet_import: false, - cmd_import: false, - cmd_export: false, - cmd_export_blocks: false, - cmd_export_state: false, - cmd_signer: false, - cmd_signer_list: false, - cmd_signer_sign: false, - cmd_signer_reject: false, - cmd_signer_new_token: false, - cmd_snapshot: false, - cmd_restore: false, - cmd_tools: false, - cmd_tools_hash: false, - cmd_db: false, - cmd_db_kill: false, - cmd_db_reset: false, - cmd_export_hardcoded_sync: false, - - // Arguments - arg_daemon_pid_file: None, - arg_import_file: None, - arg_import_format: None, - arg_export_blocks_file: None, - arg_export_blocks_format: None, - arg_export_state_file: None, - arg_export_state_format: None, - arg_snapshot_file: None, - arg_restore_file: None, - arg_tools_hash_file: None, - - arg_signer_sign_id: None, - arg_signer_reject_id: None, - arg_dapp_path: None, - arg_account_import_path: None, - arg_wallet_import_path: None, - arg_db_reset_num: 10, - - // -- Operating Options - arg_mode: "last".into(), - arg_mode_timeout: 300u64, - arg_mode_alarm: 3600u64, - arg_auto_update: "none".into(), - arg_auto_update_delay: 200u16, - arg_auto_update_check_frequency: 50u16, - arg_release_track: "current".into(), - flag_public_node: false, - flag_no_download: false, - flag_no_consensus: false, - arg_chain: "xyz".into(), - arg_base_path: Some("$HOME/.parity".into()), - arg_db_path: Some("$HOME/.parity/chains".into()), - arg_keys_path: "$HOME/.parity/keys".into(), - arg_identity: "".into(), - flag_light: false, - flag_no_hardcoded_sync: false, - flag_no_persistent_txqueue: false, - flag_force_direct: false, - - // -- Convenience Options - arg_config: "$BASE/config.toml".into(), - arg_ports_shift: 0, - flag_unsafe_expose: false, - - // -- Account Options - arg_unlock: Some("0xdeadbeefcafe0000000000000000000000000000".into()), - arg_password: vec!["~/.safe/password.file".into()], - arg_keys_iterations: 10240u32, - arg_accounts_refresh: 5u64, - flag_no_hardware_wallets: false, - flag_fast_unlock: false, - - // -- Private Transactions Options - flag_private_enabled: true, - arg_private_signer: Some("0xdeadbeefcafe0000000000000000000000000000".into()), - arg_private_validators: Some("0xdeadbeefcafe0000000000000000000000000000".into()), - arg_private_passwords: Some("~/.safe/password.file".into()), - arg_private_account: Some("0xdeadbeefcafe0000000000000000000000000000".into()), - arg_private_sstore_url: Some("http://localhost:8082".into()), - arg_private_sstore_threshold: Some(0), - - flag_force_ui: false, - flag_no_ui: false, - arg_ui_port: None, - arg_ui_interface: None, - arg_ui_hosts: None, - arg_ui_path: "$HOME/.parity/signer".into(), - flag_ui_no_validation: false, - - // -- Networking Options - flag_no_warp: false, - arg_port: 30303u16, - arg_interface: "all".into(), - arg_min_peers: Some(25u16), - arg_max_peers: Some(50u16), - arg_max_pending_peers: 64u16, - arg_snapshot_peers: 0u16, - arg_allow_ips: "all".into(), - arg_nat: "any".into(), - arg_network_id: Some(1), - arg_bootnodes: Some("".into()), - flag_no_discovery: false, - arg_node_key: None, - arg_reserved_peers: Some("./path_to_file".into()), - flag_reserved_only: false, - flag_no_ancient_blocks: false, - flag_no_serve_light: false, - - // -- API and Console Options - // RPC - flag_no_jsonrpc: false, - flag_jsonrpc_no_keep_alive: false, - flag_jsonrpc_experimental: false, - arg_jsonrpc_port: 8545u16, - arg_jsonrpc_interface: "local".into(), - arg_jsonrpc_cors: "null".into(), - arg_jsonrpc_apis: "web3,eth,net,parity,traces,rpc,secretstore".into(), - arg_jsonrpc_hosts: "none".into(), - arg_jsonrpc_server_threads: None, - arg_jsonrpc_threads: 4, - arg_jsonrpc_max_payload: None, - arg_poll_lifetime: 60u32, - flag_jsonrpc_allow_missing_blocks: false, - - // WS - flag_no_ws: false, - arg_ws_port: 8546u16, - arg_ws_interface: "local".into(), - arg_ws_apis: "web3,eth,net,parity,traces,rpc,secretstore".into(), - arg_ws_origins: "none".into(), - arg_ws_hosts: "none".into(), - arg_ws_max_connections: 100, - - // IPC - flag_no_ipc: false, - arg_ipc_path: "$HOME/.parity/jsonrpc.ipc".into(), - arg_ipc_apis: "web3,eth,net,parity,parity_accounts,personal,traces,rpc,secretstore".into(), - - // DAPPS - arg_dapps_path: Some("$HOME/.parity/dapps".into()), - flag_no_dapps: false, - - // SECRETSTORE - flag_no_secretstore: false, - flag_no_secretstore_http: false, - flag_no_secretstore_auto_migrate: false, - arg_secretstore_acl_contract: Some("registry".into()), - arg_secretstore_contract: Some("none".into()), - arg_secretstore_srv_gen_contract: Some("none".into()), - arg_secretstore_srv_retr_contract: Some("none".into()), - arg_secretstore_doc_store_contract: Some("none".into()), - arg_secretstore_doc_sretr_contract: Some("none".into()), - arg_secretstore_secret: None, - arg_secretstore_admin_public: None, - arg_secretstore_nodes: "".into(), - arg_secretstore_server_set_contract: Some("registry".into()), - arg_secretstore_interface: "local".into(), - arg_secretstore_port: 8083u16, - arg_secretstore_http_interface: "local".into(), - arg_secretstore_http_port: 8082u16, - arg_secretstore_path: "$HOME/.parity/secretstore".into(), - - // IPFS - flag_ipfs_api: false, - arg_ipfs_api_port: 5001u16, - arg_ipfs_api_interface: "local".into(), - arg_ipfs_api_cors: "null".into(), - arg_ipfs_api_hosts: "none".into(), - - // -- Sealing/Mining Options - arg_author: Some("0xdeadbeefcafe0000000000000000000000000001".into()), - arg_engine_signer: Some("0xdeadbeefcafe0000000000000000000000000001".into()), - flag_force_sealing: true, - arg_reseal_on_txs: "all".into(), - arg_reseal_min_period: 4000u64, - arg_reseal_max_period: 60000u64, - flag_reseal_on_uncle: false, - arg_work_queue_size: 20usize, - arg_tx_gas_limit: Some("10000000".into()), - arg_tx_time_limit: Some(100u64), - arg_relay_set: "cheap".into(), - arg_min_gas_price: Some(0u64), - arg_usd_per_tx: "0.0001".into(), - arg_gas_price_percentile: 50usize, - arg_usd_per_eth: "auto".into(), - arg_price_update_period: "hourly".into(), - arg_gas_floor_target: "8000000".into(), - arg_gas_cap: "10000000".into(), - arg_extra_data: Some("Parity".into()), - flag_tx_queue_no_unfamiliar_locals: false, - flag_tx_queue_no_early_reject: false, - arg_tx_queue_size: 8192usize, - arg_tx_queue_per_sender: None, - arg_tx_queue_mem_limit: 4u32, - arg_tx_queue_locals: Some("0xdeadbeefcafe0000000000000000000000000000".into()), - arg_tx_queue_strategy: "gas_factor".into(), - arg_tx_queue_ban_count: Some(1u16), - arg_tx_queue_ban_time: Some(180u16), - flag_remove_solved: false, - arg_notify_work: Some("http://localhost:3001".into()), - flag_refuse_service_transactions: false, - flag_infinite_pending_block: false, - arg_max_round_blocks_to_import: 12usize, - - flag_stratum: false, - arg_stratum_interface: "local".to_owned(), - arg_stratum_port: 8008u16, - arg_stratum_secret: None, - - // -- Footprint Options - arg_tracing: "auto".into(), - arg_pruning: "auto".into(), - arg_pruning_history: 64u64, - arg_pruning_memory: 500usize, - arg_cache_size_db: 64u32, - arg_cache_size_blocks: 8u32, - arg_cache_size_queue: 50u32, - arg_cache_size_state: 25u32, - arg_cache_size: Some(128), - flag_fast_and_loose: false, - arg_db_compaction: "ssd".into(), - arg_fat_db: "auto".into(), - flag_scale_verifiers: true, - arg_num_verifiers: Some(6), - - // -- Import/Export Options - arg_export_blocks_from: "1".into(), - arg_export_blocks_to: "latest".into(), - flag_no_seal_check: false, - flag_export_state_no_code: false, - flag_export_state_no_storage: false, - arg_export_state_min_balance: None, - arg_export_state_max_balance: None, - - // -- Snapshot Optons - arg_export_state_at: "latest".into(), - arg_snapshot_at: "latest".into(), - flag_no_periodic_snapshot: false, - arg_snapshot_threads: None, - - // -- Light options. - arg_on_demand_response_time_window: Some(2), - arg_on_demand_request_backoff_start: Some(9), - arg_on_demand_request_backoff_max: Some(15), - arg_on_demand_request_backoff_rounds_max: Some(100), - arg_on_demand_request_consecutive_failures: Some(1), - - // -- Whisper options. - flag_whisper: false, - arg_whisper_pool_size: 20, - - // -- Legacy Options - flag_warp: false, - flag_geth: false, - flag_testnet: false, - flag_import_geth_keys: false, - arg_warp_barrier: None, - arg_datadir: None, - arg_networkid: None, - arg_peers: None, - arg_nodekey: None, - flag_nodiscover: false, - flag_jsonrpc: false, - flag_jsonrpc_off: false, - flag_webapp: false, - flag_dapps_off: false, - flag_rpc: false, - arg_rpcaddr: None, - arg_rpcport: None, - arg_rpcapi: None, - arg_rpccorsdomain: None, - flag_ipcdisable: false, - flag_ipc_off: false, - arg_ipcapi: None, - arg_ipcpath: None, - arg_gasprice: None, - arg_etherbase: None, - arg_extradata: None, - arg_cache: None, - // Legacy-Dapps - arg_dapps_port: Some(8080), - arg_dapps_interface: Some("local".into()), - arg_dapps_hosts: Some("none".into()), - arg_dapps_cors: None, - arg_dapps_user: Some("test_user".into()), - arg_dapps_pass: Some("test_pass".into()), - flag_dapps_apis_all: false, - - // -- Internal Options - flag_can_restart: false, - - // -- Miscellaneous Options - arg_ntp_servers: None, - flag_version: false, - arg_logging: Some("own_tx=trace".into()), - arg_log_file: Some("/var/log/parity.log".into()), - flag_no_color: false, - flag_no_config: false, - }); - } - - #[test] - fn should_parse_config_and_return_errors() { - let config1 = Args::parse_config(include_str!("./tests/config.invalid1.toml")); - let config2 = Args::parse_config(include_str!("./tests/config.invalid2.toml")); - let config3 = Args::parse_config(include_str!("./tests/config.invalid3.toml")); - let config4 = Args::parse_config(include_str!("./tests/config.invalid4.toml")); - - match (config1, config2, config3, config4) { - ( - Err(ArgsError::Decode(_)), - Err(ArgsError::Decode(_)), - Err(ArgsError::Decode(_)), - Err(ArgsError::Decode(_)), - ) => {}, - (a, b, c, d) => { - assert!(false, "Got invalid error types: {:?}, {:?}, {:?}, {:?}", a, b, c, d); - } - } - } - - #[test] - fn should_deserialize_toml_file() { - let config: Config = toml::from_str(include_str!("./tests/config.toml")).unwrap(); - - assert_eq!(config, Config { - parity: Some(Operating { - mode: Some("dark".into()), - mode_timeout: Some(15u64), - mode_alarm: Some(10u64), - auto_update: None, - auto_update_delay: None, - auto_update_check_frequency: None, - release_track: None, - no_download: None, - no_consensus: None, - chain: Some("./chain.json".into()), - base_path: None, - db_path: None, - keys_path: None, - identity: None, - light: None, - no_hardcoded_sync: None, - no_persistent_txqueue: None, - _legacy_public_node: None, - }), - account: Some(Account { - unlock: Some(vec!["0x1".into(), "0x2".into(), "0x3".into()]), - password: Some(vec!["passwdfile path".into()]), - keys_iterations: None, - refresh_time: None, - disable_hardware: None, - fast_unlock: None, - }), - ui: Some(Ui { - path: None, - _legacy_force: None, - _legacy_disable: Some(true), - _legacy_port: None, - _legacy_interface: None, - _legacy_hosts: None, - }), - network: Some(Network { - warp: Some(false), - warp_barrier: None, - port: None, - interface: None, - min_peers: Some(10), - max_peers: Some(20), - max_pending_peers: Some(30), - snapshot_peers: Some(40), - allow_ips: Some("public".into()), - nat: Some("any".into()), - id: None, - bootnodes: None, - discovery: Some(true), - node_key: None, - reserved_peers: Some("./path/to/reserved_peers".into()), - reserved_only: Some(true), - no_serve_light: None, - }), - websockets: Some(Ws { - disable: Some(true), - port: None, - interface: None, - apis: None, - origins: Some(vec!["none".into()]), - hosts: None, - max_connections: None, - }), - rpc: Some(Rpc { - disable: Some(true), - port: Some(8180), - interface: None, - cors: None, - apis: None, - hosts: None, - server_threads: None, - processing_threads: None, - max_payload: None, - keep_alive: None, - experimental_rpcs: None, - poll_lifetime: None, - allow_missing_blocks: None - }), - ipc: Some(Ipc { - disable: None, - path: None, - apis: Some(vec!["rpc".into(), "eth".into()]), - }), - dapps: Some(Dapps { - _legacy_disable: None, - _legacy_port: Some(8080), - _legacy_path: None, - _legacy_interface: None, - _legacy_hosts: None, - _legacy_cors: None, - _legacy_user: Some("username".into()), - _legacy_pass: Some("password".into()) - }), - secretstore: Some(SecretStore { - disable: None, - disable_http: None, - disable_auto_migrate: None, - acl_contract: None, - service_contract: None, - service_contract_srv_gen: None, - service_contract_srv_retr: None, - service_contract_doc_store: None, - service_contract_doc_sretr: None, - self_secret: None, - admin_public: None, - nodes: None, - server_set_contract: None, - interface: None, - port: Some(8083), - http_interface: None, - http_port: Some(8082), - path: None, - }), - private_tx: None, - ipfs: Some(Ipfs { - enable: Some(false), - port: Some(5001), - interface: None, - cors: None, - hosts: None, - }), - mining: Some(Mining { - author: Some("0xdeadbeefcafe0000000000000000000000000001".into()), - engine_signer: Some("0xdeadbeefcafe0000000000000000000000000001".into()), - force_sealing: Some(true), - reseal_on_txs: Some("all".into()), - reseal_on_uncle: None, - reseal_min_period: Some(4000), - reseal_max_period: Some(60000), - work_queue_size: None, - relay_set: None, - min_gas_price: None, - gas_price_percentile: None, - usd_per_tx: None, - usd_per_eth: None, - price_update_period: Some("hourly".into()), - gas_floor_target: None, - gas_cap: None, - tx_queue_size: Some(8192), - tx_queue_per_sender: None, - tx_queue_mem_limit: None, - tx_queue_locals: None, - tx_queue_strategy: None, - tx_queue_ban_count: None, - tx_queue_ban_time: None, - tx_queue_no_unfamiliar_locals: None, - tx_queue_no_early_reject: None, - tx_gas_limit: None, - tx_time_limit: None, - extra_data: None, - remove_solved: None, - notify_work: None, - refuse_service_transactions: None, - infinite_pending_block: None, - max_round_blocks_to_import: None, - }), - footprint: Some(Footprint { - tracing: Some("on".into()), - pruning: Some("fast".into()), - pruning_history: Some(64), - pruning_memory: None, - fast_and_loose: None, - cache_size: None, - cache_size_db: Some(256), - cache_size_blocks: Some(16), - cache_size_queue: Some(100), - cache_size_state: Some(25), - db_compaction: Some("ssd".into()), - fat_db: Some("off".into()), - scale_verifiers: Some(false), - num_verifiers: None, - }), - light: Some(Light { - on_demand_response_time_window: Some(2), - on_demand_request_backoff_start: Some(9), - on_demand_request_backoff_max: Some(15), - on_demand_request_backoff_rounds_max: Some(10), - on_demand_request_consecutive_failures: Some(1), - }), - snapshots: Some(Snapshots { - disable_periodic: Some(true), - processing_threads: None, - }), - misc: Some(Misc { - logging: Some("own_tx=trace".into()), - log_file: Some("/var/log/parity.log".into()), - color: Some(true), - ports_shift: Some(0), - unsafe_expose: Some(false), - }), - whisper: Some(Whisper { - enabled: Some(true), - pool_size: Some(50), - }), - stratum: None, - }); - } - - #[test] - fn should_not_accept_min_peers_bigger_than_max_peers() { - match Args::parse(&["parity", "--max-peers=39", "--min-peers=40"]) { - Err(ArgsError::PeerConfiguration) => (), - _ => assert_eq!(false, true), - } - } - - #[test] - fn should_accept_max_peers_equal_or_bigger_than_min_peers() { - Args::parse(&["parity", "--max-peers=40", "--min-peers=40"]).unwrap(); - Args::parse(&["parity", "--max-peers=100", "--min-peers=40"]).unwrap(); - } + use super::{ + Account, Args, ArgsError, Config, Footprint, Ipc, Metrics, Mining, Misc, Network, + Operating, Rpc, SecretStore, Snapshots, Ws, + }; + use clap::ErrorKind as ClapErrorKind; + use toml; + + #[test] + fn should_accept_any_argument_order() { + let args = Args::parse(&["openethereum", "--no-warp", "account", "list"]).unwrap(); + assert_eq!(args.flag_no_warp, true); + + let args = Args::parse(&["openethereum", "account", "list", "--no-warp"]).unwrap(); + assert_eq!(args.flag_no_warp, true); + + let args = Args::parse(&["openethereum", "--chain=dev", "account", "list"]).unwrap(); + assert_eq!(args.arg_chain, "dev"); + + let args = Args::parse(&["openethereum", "account", "list", "--chain=dev"]).unwrap(); + assert_eq!(args.arg_chain, "dev"); + } + + #[test] + fn should_reject_invalid_values() { + let args = Args::parse(&["openethereum", "--jsonrpc-port=8545"]); + assert!(args.is_ok()); + + let args = Args::parse(&["openethereum", "--jsonrpc-port=asd"]); + assert!(args.is_err()); + } + + #[test] + fn should_parse_args_and_flags() { + let args = Args::parse(&["openethereum", "--no-warp"]).unwrap(); + assert_eq!(args.flag_no_warp, true); + + let args = Args::parse(&["openethereum", "--pruning", "archive"]).unwrap(); + assert_eq!(args.arg_pruning, "archive"); + + let args = Args::parse(&["openethereum", "export", "state", "--no-storage"]).unwrap(); + assert_eq!(args.flag_export_state_no_storage, true); + + let args = + Args::parse(&["openethereum", "export", "state", "--min-balance", "123"]).unwrap(); + assert_eq!(args.arg_export_state_min_balance, Some("123".to_string())); + } + + #[test] + fn should_exit_gracefully_on_unknown_argument() { + let result = Args::parse(&["openethereum", "--please-exit-gracefully"]); + assert!(match result { + Err(ArgsError::Clap(ref clap_error)) + if clap_error.kind == ClapErrorKind::UnknownArgument => + true, + _ => false, + }); + } + + #[test] + fn should_use_subcommand_arg_default() { + let args = Args::parse(&["openethereum", "export", "state", "--at", "123"]).unwrap(); + assert_eq!(args.arg_export_state_at, "123"); + assert_eq!(args.arg_snapshot_at, "latest"); + + let args = Args::parse(&["openethereum", "snapshot", "--at", "123", "file.dump"]).unwrap(); + assert_eq!(args.arg_snapshot_at, "123"); + assert_eq!(args.arg_export_state_at, "latest"); + + let args = Args::parse(&["openethereum", "export", "state"]).unwrap(); + assert_eq!(args.arg_snapshot_at, "latest"); + assert_eq!(args.arg_export_state_at, "latest"); + + let args = Args::parse(&["openethereum", "snapshot", "file.dump"]).unwrap(); + assert_eq!(args.arg_snapshot_at, "latest"); + assert_eq!(args.arg_export_state_at, "latest"); + } + + #[test] + fn should_parse_multiple_values() { + let args = Args::parse(&["openethereum", "account", "import", "~/1", "~/2"]).unwrap(); + assert_eq!( + args.arg_account_import_path, + Some(vec!["~/1".to_owned(), "~/2".to_owned()]) + ); + + let args = Args::parse(&["openethereum", "account", "import", "~/1,ext"]).unwrap(); + assert_eq!( + args.arg_account_import_path, + Some(vec!["~/1,ext".to_owned()]) + ); + + let args = Args::parse(&[ + "openethereum", + "--secretstore-nodes", + "abc@127.0.0.1:3333,cde@10.10.10.10:4444", + ]) + .unwrap(); + assert_eq!( + args.arg_secretstore_nodes, + "abc@127.0.0.1:3333,cde@10.10.10.10:4444" + ); + + let args = Args::parse(&[ + "openethereum", + "--password", + "~/.safe/1", + "--password", + "~/.safe/2", + ]) + .unwrap(); + assert_eq!( + args.arg_password, + vec!["~/.safe/1".to_owned(), "~/.safe/2".to_owned()] + ); + + let args = Args::parse(&["openethereum", "--password", "~/.safe/1,~/.safe/2"]).unwrap(); + assert_eq!( + args.arg_password, + vec!["~/.safe/1".to_owned(), "~/.safe/2".to_owned()] + ); + } + + #[test] + fn should_parse_global_args_with_subcommand() { + let args = Args::parse(&["openethereum", "--chain", "dev", "account", "list"]).unwrap(); + assert_eq!(args.arg_chain, "dev".to_owned()); + } + + #[test] + fn should_parse_args_and_include_config() { + // given + let mut config = Config::default(); + let mut operating = Operating::default(); + operating.chain = Some("goerli".into()); + config.parity = Some(operating); + + // when + let args = Args::parse_with_config(&["openethereum"], config).unwrap(); + + // then + assert_eq!(args.arg_chain, "goerli".to_owned()); + } + + #[test] + fn should_not_use_config_if_cli_is_provided() { + // given + let mut config = Config::default(); + let mut operating = Operating::default(); + operating.chain = Some("goerli".into()); + config.parity = Some(operating); + + // when + let args = Args::parse_with_config(&["openethereum", "--chain", "xyz"], config).unwrap(); + + // then + assert_eq!(args.arg_chain, "xyz".to_owned()); + } + + #[test] + fn should_use_config_if_cli_is_missing() { + let mut config = Config::default(); + let mut footprint = Footprint::default(); + footprint.pruning_history = Some(128); + config.footprint = Some(footprint); + + // when + let args = Args::parse_with_config(&["openethereum"], config).unwrap(); + + // then + assert_eq!(args.arg_pruning_history, 128); + } + + #[test] + fn should_parse_full_config() { + // given + let config = toml::from_str(include_str!("./tests/config.full.toml")).unwrap(); + + // when + let args = Args::parse_with_config(&["openethereum", "--chain", "xyz"], config).unwrap(); + + // then + assert_eq!( + args, + Args { + // Commands + cmd_daemon: false, + cmd_account: false, + cmd_account_new: false, + cmd_account_list: false, + cmd_account_import: false, + cmd_wallet: false, + cmd_wallet_import: false, + cmd_import: false, + cmd_export: false, + cmd_export_blocks: false, + cmd_export_state: false, + cmd_signer: false, + cmd_signer_list: false, + cmd_signer_sign: false, + cmd_signer_reject: false, + cmd_signer_new_token: false, + cmd_snapshot: false, + cmd_restore: false, + cmd_tools: false, + cmd_tools_hash: false, + cmd_db: false, + cmd_db_kill: false, + cmd_db_reset: false, + + // Arguments + arg_daemon_pid_file: None, + arg_import_file: None, + arg_import_format: None, + arg_export_blocks_file: None, + arg_export_blocks_format: None, + arg_export_state_file: None, + arg_export_state_format: None, + arg_snapshot_file: None, + arg_restore_file: None, + arg_tools_hash_file: None, + + arg_signer_sign_id: None, + arg_signer_reject_id: None, + arg_account_import_path: None, + arg_wallet_import_path: None, + arg_db_reset_num: 10, + + // -- Operating Options + arg_mode: "last".into(), + arg_mode_timeout: 300u64, + arg_mode_alarm: 3600u64, + arg_chain: "xyz".into(), + arg_base_path: Some("$HOME/.parity".into()), + arg_db_path: Some("$HOME/.parity/chains".into()), + arg_keys_path: "$HOME/.parity/keys".into(), + arg_identity: "".into(), + flag_no_persistent_txqueue: false, + + // -- Convenience Options + arg_config: "$BASE/config.toml".into(), + arg_ports_shift: 0, + flag_unsafe_expose: false, + + // -- Account Options + arg_unlock: Some("0xdeadbeefcafe0000000000000000000000000000".into()), + arg_password: vec!["~/.safe/password.file".into()], + arg_keys_iterations: 10240u32, + arg_accounts_refresh: 5u64, + flag_fast_unlock: false, + + arg_ui_path: "$HOME/.parity/signer".into(), + + // -- Networking Options + flag_no_warp: false, + arg_port: 30303u16, + arg_interface: "all".into(), + arg_min_peers: Some(25u16), + arg_max_peers: Some(50u16), + arg_max_pending_peers: 64u16, + arg_snapshot_peers: 0u16, + arg_allow_ips: "all".into(), + arg_nat: "any".into(), + arg_network_id: Some(1), + arg_bootnodes: Some("".into()), + flag_no_discovery: false, + arg_node_key: None, + arg_reserved_peers: Some("./path_to_file".into()), + flag_reserved_only: false, + flag_no_ancient_blocks: false, + arg_warp_barrier: None, + + // -- API and Console Options + // RPC + flag_no_jsonrpc: false, + flag_jsonrpc_no_keep_alive: false, + flag_jsonrpc_experimental: false, + arg_jsonrpc_port: 8545u16, + arg_jsonrpc_interface: "local".into(), + arg_jsonrpc_cors: "null".into(), + arg_jsonrpc_apis: "web3,eth,net,parity,traces,secretstore".into(), + arg_jsonrpc_hosts: "none".into(), + arg_jsonrpc_server_threads: None, + arg_jsonrpc_threads: 4, + arg_jsonrpc_max_payload: None, + arg_poll_lifetime: 60u32, + flag_jsonrpc_allow_missing_blocks: false, + + // WS + flag_no_ws: false, + arg_ws_port: 8546u16, + arg_ws_interface: "local".into(), + arg_ws_apis: "web3,eth,net,parity,traces,secretstore".into(), + arg_ws_origins: "none".into(), + arg_ws_hosts: "none".into(), + arg_ws_max_connections: 100, + + // IPC + flag_no_ipc: false, + arg_ipc_path: "$HOME/.parity/jsonrpc.ipc".into(), + arg_ipc_apis: "web3,eth,net,parity,parity_accounts,personal,traces,secretstore" + .into(), + + // METRICS + flag_metrics: false, + arg_metrics_port: 3000u16, + arg_metrics_interface: "local".into(), + + // SECRETSTORE + flag_no_secretstore: false, + flag_no_secretstore_http: false, + flag_no_secretstore_auto_migrate: false, + arg_secretstore_acl_contract: Some("registry".into()), + arg_secretstore_contract: Some("none".into()), + arg_secretstore_srv_gen_contract: Some("none".into()), + arg_secretstore_srv_retr_contract: Some("none".into()), + arg_secretstore_doc_store_contract: Some("none".into()), + arg_secretstore_doc_sretr_contract: Some("none".into()), + arg_secretstore_secret: None, + arg_secretstore_admin_public: None, + arg_secretstore_nodes: "".into(), + arg_secretstore_server_set_contract: Some("registry".into()), + arg_secretstore_interface: "local".into(), + arg_secretstore_port: 8083u16, + arg_secretstore_http_interface: "local".into(), + arg_secretstore_http_port: 8082u16, + arg_secretstore_path: "$HOME/.parity/secretstore".into(), + + // -- Sealing/Mining Options + arg_author: Some("0xdeadbeefcafe0000000000000000000000000001".into()), + arg_engine_signer: Some("0xdeadbeefcafe0000000000000000000000000001".into()), + flag_force_sealing: true, + arg_reseal_on_txs: "all".into(), + arg_reseal_min_period: 4000u64, + arg_reseal_max_period: 60000u64, + flag_reseal_on_uncle: false, + arg_work_queue_size: 20usize, + arg_tx_gas_limit: Some("10000000".into()), + arg_tx_time_limit: Some(100u64), + arg_relay_set: "cheap".into(), + arg_min_gas_price: Some(0u64), + arg_usd_per_tx: "0.0001".into(), + arg_gas_price_percentile: 50usize, + arg_usd_per_eth: "auto".into(), + arg_price_update_period: "hourly".into(), + arg_gas_floor_target: "8000000".into(), + arg_gas_cap: "10000000".into(), + arg_extra_data: Some("Parity".into()), + flag_tx_queue_no_unfamiliar_locals: false, + flag_tx_queue_no_early_reject: false, + arg_tx_queue_size: 8192usize, + arg_tx_queue_per_sender: None, + arg_tx_queue_mem_limit: 4u32, + arg_tx_queue_locals: Some("0xdeadbeefcafe0000000000000000000000000000".into()), + arg_tx_queue_strategy: "gas_factor".into(), + flag_remove_solved: false, + arg_notify_work: Some("http://localhost:3001".into()), + flag_refuse_service_transactions: false, + flag_infinite_pending_block: false, + arg_max_round_blocks_to_import: 12usize, + + flag_stratum: false, + arg_stratum_interface: "local".to_owned(), + arg_stratum_port: 8008u16, + arg_stratum_secret: None, + + // -- Footprint Options + arg_tracing: "auto".into(), + arg_pruning: "auto".into(), + arg_pruning_history: 64u64, + arg_pruning_memory: 500usize, + arg_cache_size_db: 64u32, + arg_cache_size_blocks: 8u32, + arg_cache_size_queue: 50u32, + arg_cache_size_state: 25u32, + arg_cache_size: Some(128), + arg_db_compaction: "ssd".into(), + arg_fat_db: "auto".into(), + flag_scale_verifiers: true, + arg_num_verifiers: Some(6), + + // -- Import/Export Options + arg_export_blocks_from: "1".into(), + arg_export_blocks_to: "latest".into(), + flag_no_seal_check: false, + flag_export_state_no_code: false, + flag_export_state_no_storage: false, + arg_export_state_min_balance: None, + arg_export_state_max_balance: None, + + // -- Snapshot Optons + arg_export_state_at: "latest".into(), + arg_snapshot_at: "latest".into(), + flag_enable_snapshotting: false, + arg_snapshot_threads: None, + + // -- Internal Options + flag_can_restart: false, + + // -- Miscellaneous Options + flag_version: false, + arg_logging: Some("own_tx=trace".into()), + arg_log_file: Some("/var/log/openethereum.log".into()), + flag_no_color: false, + flag_no_config: false, + } + ); + } + + #[test] + fn should_parse_config_and_return_errors() { + let config1 = Args::parse_config(include_str!("./tests/config.invalid1.toml")); + let config2 = Args::parse_config(include_str!("./tests/config.invalid2.toml")); + let config3 = Args::parse_config(include_str!("./tests/config.invalid3.toml")); + let config4 = Args::parse_config(include_str!("./tests/config.invalid4.toml")); + + match (config1, config2, config3, config4) { + ( + Err(ArgsError::Decode(_)), + Err(ArgsError::Decode(_)), + Err(ArgsError::Decode(_)), + Err(ArgsError::Decode(_)), + ) => {} + (a, b, c, d) => { + assert!( + false, + "Got invalid error types: {:?}, {:?}, {:?}, {:?}", + a, b, c, d + ); + } + } + } + + #[test] + fn should_deserialize_toml_file() { + let config: Config = toml::from_str(include_str!("./tests/config.toml")).unwrap(); + + assert_eq!( + config, + Config { + parity: Some(Operating { + mode: Some("dark".into()), + mode_timeout: Some(15u64), + mode_alarm: Some(10u64), + chain: Some("./chain.json".into()), + base_path: None, + db_path: None, + keys_path: None, + identity: None, + no_persistent_txqueue: None, + }), + account: Some(Account { + unlock: Some(vec!["0x1".into(), "0x2".into(), "0x3".into()]), + password: Some(vec!["passwdfile path".into()]), + keys_iterations: None, + refresh_time: None, + fast_unlock: None, + }), + ui: None, + network: Some(Network { + warp: Some(false), + warp_barrier: None, + port: None, + interface: None, + min_peers: Some(10), + max_peers: Some(20), + max_pending_peers: Some(30), + snapshot_peers: Some(40), + allow_ips: Some("public".into()), + nat: Some("any".into()), + id: None, + bootnodes: None, + discovery: Some(true), + node_key: None, + reserved_peers: Some("./path/to/reserved_peers".into()), + reserved_only: Some(true), + }), + websockets: Some(Ws { + disable: Some(true), + port: None, + interface: None, + apis: None, + origins: Some(vec!["none".into()]), + hosts: None, + max_connections: None, + }), + rpc: Some(Rpc { + disable: Some(true), + port: Some(8180), + interface: None, + cors: None, + apis: None, + hosts: None, + server_threads: None, + processing_threads: None, + max_payload: None, + keep_alive: None, + experimental_rpcs: None, + poll_lifetime: None, + allow_missing_blocks: None + }), + ipc: Some(Ipc { + disable: None, + path: None, + apis: Some(vec!["rpc".into(), "eth".into()]), + }), + metrics: Some(Metrics { + enable: Some(true), + interface: Some("local".to_string()), + port: Some(4000), + }), + secretstore: Some(SecretStore { + disable: None, + disable_http: None, + disable_auto_migrate: None, + acl_contract: None, + service_contract: None, + service_contract_srv_gen: None, + service_contract_srv_retr: None, + service_contract_doc_store: None, + service_contract_doc_sretr: None, + self_secret: None, + admin_public: None, + nodes: None, + server_set_contract: None, + interface: None, + port: Some(8083), + http_interface: None, + http_port: Some(8082), + path: None, + }), + mining: Some(Mining { + author: Some("0xdeadbeefcafe0000000000000000000000000001".into()), + engine_signer: Some("0xdeadbeefcafe0000000000000000000000000001".into()), + force_sealing: Some(true), + reseal_on_txs: Some("all".into()), + reseal_on_uncle: None, + reseal_min_period: Some(4000), + reseal_max_period: Some(60000), + work_queue_size: None, + relay_set: None, + min_gas_price: None, + gas_price_percentile: None, + usd_per_tx: None, + usd_per_eth: None, + price_update_period: Some("hourly".into()), + gas_floor_target: None, + gas_cap: None, + tx_queue_size: Some(8192), + tx_queue_per_sender: None, + tx_queue_mem_limit: None, + tx_queue_locals: None, + tx_queue_strategy: None, + tx_queue_ban_count: None, + tx_queue_ban_time: None, + tx_queue_no_unfamiliar_locals: None, + tx_queue_no_early_reject: None, + tx_gas_limit: None, + tx_time_limit: None, + extra_data: None, + remove_solved: None, + notify_work: None, + refuse_service_transactions: None, + infinite_pending_block: None, + max_round_blocks_to_import: None, + }), + footprint: Some(Footprint { + tracing: Some("on".into()), + pruning: Some("fast".into()), + pruning_history: Some(64), + pruning_memory: None, + fast_and_loose: None, + cache_size: None, + cache_size_db: Some(256), + cache_size_blocks: Some(16), + cache_size_queue: Some(100), + cache_size_state: Some(25), + db_compaction: Some("ssd".into()), + fat_db: Some("off".into()), + scale_verifiers: Some(false), + num_verifiers: None, + }), + snapshots: Some(Snapshots { + enable: Some(false), + processing_threads: None, + }), + misc: Some(Misc { + logging: Some("own_tx=trace".into()), + log_file: Some("/var/log/openethereum.log".into()), + color: Some(true), + ports_shift: Some(0), + unsafe_expose: Some(false), + }), + stratum: None, + } + ); + } + + #[test] + fn should_not_accept_min_peers_bigger_than_max_peers() { + match Args::parse(&["openethereum", "--max-peers=39", "--min-peers=40"]) { + Err(ArgsError::PeerConfiguration) => (), + _ => assert_eq!(false, true), + } + } + + #[test] + fn should_accept_max_peers_equal_or_bigger_than_min_peers() { + Args::parse(&["openethereum", "--max-peers=40", "--min-peers=40"]).unwrap(); + Args::parse(&["openethereum", "--max-peers=100", "--min-peers=40"]).unwrap(); + } } diff --git a/parity/cli/presets/config.dev-insecure.toml b/parity/cli/presets/config.dev-insecure.toml index e4c2c9f4769..0f594b8b3db 100644 --- a/parity/cli/presets/config.dev-insecure.toml +++ b/parity/cli/presets/config.dev-insecure.toml @@ -1,5 +1,4 @@ [parity] -no_consensus = true chain = "dev" [mining] @@ -10,7 +9,3 @@ min_gas_price = 0 interface = "all" apis = ["all"] hosts = ["all"] - -[ipfs] -enable = false # this is the default -hosts = ["all"] diff --git a/parity/cli/presets/config.insecure.toml b/parity/cli/presets/config.insecure.toml index 18859c9cc1f..7481506a447 100644 --- a/parity/cli/presets/config.insecure.toml +++ b/parity/cli/presets/config.insecure.toml @@ -1,11 +1,4 @@ -[parity] -no_consensus = true - [rpc] interface = "all" apis = ["all"] hosts = ["all"] - -[ipfs] -enable = false # this is the default -hosts = ["all"] diff --git a/parity/cli/presets/config.mining.toml b/parity/cli/presets/config.mining.toml index 6d22a0f0878..2f240ad3822 100644 --- a/parity/cli/presets/config.mining.toml +++ b/parity/cli/presets/config.mining.toml @@ -1,15 +1,11 @@ [network] -# Parity will try to maintain connection to at least 50 peers. +# OpenEthereum will try to maintain connection to at least 50 peers. min_peers = 50 -# Parity will maintain at most 100 peers. +# OpenEthereum will maintain at most 100 peers. max_peers = 100 [ipc] -# You won't be able to use IPC to interact with Parity. -disable = true - -[dapps] -# You won't be able to access any web Dapps. +# You won't be able to use IPC to interact with OpenEthereum. disable = true [mining] @@ -19,7 +15,7 @@ force_sealing = true reseal_on_txs = "all" # New pending block will be created only once per 4000 milliseconds. reseal_min_period = 4000 -# Parity will keep/relay at most 8192 transactions in queue. +# OpenEthereum will keep/relay at most 8192 transactions in queue. tx_queue_size = 8192 tx_queue_per_sender = 128 diff --git a/parity/cli/presets/config.non-standard-ports.toml b/parity/cli/presets/config.non-standard-ports.toml index 5c522d86795..de44a8b6806 100644 --- a/parity/cli/presets/config.non-standard-ports.toml +++ b/parity/cli/presets/config.non-standard-ports.toml @@ -1,5 +1,5 @@ [network] -# Parity will listen for connections on port 30305. +# OpenEthereum will listen for connections on port 30305. port = 30305 [rpc] diff --git a/parity/cli/presets/mod.rs b/parity/cli/presets/mod.rs index 25bccf41b83..14a7bae1291 100644 --- a/parity/cli/presets/mod.rs +++ b/parity/cli/presets/mod.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use std::io::{Error, ErrorKind}; diff --git a/parity/cli/tests/config.full.toml b/parity/cli/tests/config.full.toml index 34dd39058b7..37b01cafc78 100644 --- a/parity/cli/tests/config.full.toml +++ b/parity/cli/tests/config.full.toml @@ -2,13 +2,6 @@ mode = "last" mode_timeout = 300 mode_alarm = 3600 -auto_update = "none" -auto_update_delay = 200 -auto_update_check_frequency = 50 -release_track = "current" -public_node = false -no_download = false -no_consensus = false no_persistent_txqueue = false chain = "homestead" @@ -16,28 +9,13 @@ base_path = "$HOME/.parity" db_path = "$HOME/.parity/chains" keys_path = "$HOME/.parity/keys" identity = "" -light = false -no_hardcoded_sync = false [account] unlock = ["0xdeadbeefcafe0000000000000000000000000000"] password = ["~/.safe/password.file"] keys_iterations = 10240 -[private_tx] -enabled = true -signer = "0xdeadbeefcafe0000000000000000000000000000" -validators = ["0xdeadbeefcafe0000000000000000000000000000"] -passwords = "~/.safe/password.file" -account = "0xdeadbeefcafe0000000000000000000000000000" -sstore_url = "http://localhost:8082" -sstore_threshold = 0 - [ui] -force = false -disable = false -port = 8180 -interface = "127.0.0.1" path = "$HOME/.parity/signer" [network] @@ -52,7 +30,6 @@ warp = true allow_ips = "all" snapshot_peers = 0 max_pending_peers = 64 -no_serve_light = false reserved_only = false reserved_peers = "./path_to_file" @@ -62,7 +39,7 @@ disable = false port = 8545 interface = "local" cors = ["null"] -apis = ["web3", "eth", "net", "parity", "traces", "rpc", "secretstore"] +apis = ["web3", "eth", "net", "parity", "traces", "secretstore"] hosts = ["none"] allow_missing_blocks = false @@ -71,23 +48,13 @@ disable = false port = 8546 interface = "local" origins = ["none"] -apis = ["web3", "eth", "net", "parity", "traces", "rpc", "secretstore"] +apis = ["web3", "eth", "net", "parity", "traces", "secretstore"] hosts = ["none"] [ipc] disable = false path = "$HOME/.parity/jsonrpc.ipc" -apis = ["web3", "eth", "net", "parity", "parity_accounts", "personal", "traces", "rpc", "secretstore"] - -[dapps] -disable = false -port = 8080 -interface = "local" -hosts = ["none"] -path = "$HOME/.parity/dapps" -# authorization: -user = "test_user" -pass = "test_pass" +apis = ["web3", "eth", "net", "parity", "parity_accounts", "personal", "traces", "secretstore"] [secretstore] disable = false @@ -106,13 +73,6 @@ interface = "local" port = 8083 path = "$HOME/.parity/secretstore" -[ipfs] -enable = false -port = 5001 -interface = "local" -cors = ["null"] -hosts = ["none"] - [mining] author = "0xdeadbeefcafe0000000000000000000000000001" engine_signer = "0xdeadbeefcafe0000000000000000000000000001" @@ -157,21 +117,10 @@ fat_db = "auto" scale_verifiers = true num_verifiers = 6 -[light] -on_demand_response_time_window = 2 -on_demand_request_backoff_start = 9 -on_demand_request_backoff_max = 15 -on_demand_request_backoff_rounds_max = 100 -on_demand_request_consecutive_failures = 1 - [snapshots] -disable_periodic = false +enable = false [misc] logging = "own_tx=trace" -log_file = "/var/log/parity.log" +log_file = "/var/log/openethereum.log" color = true - -[whisper] -enabled = false -pool_size = 20 diff --git a/parity/cli/tests/config.toml b/parity/cli/tests/config.toml index a10cb02113c..581a59af53f 100644 --- a/parity/cli/tests/config.toml +++ b/parity/cli/tests/config.toml @@ -8,9 +8,6 @@ chain = "./chain.json" unlock = ["0x1", "0x2", "0x3"] password = ["passwdfile path"] -[ui] -disable = true - [network] warp = false discovery = true @@ -35,19 +32,16 @@ port = 8180 [ipc] apis = ["rpc", "eth"] -[dapps] -port = 8080 -user = "username" -pass = "password" +[metrics] +enable = true +interface = "local" +port = 4000 + [secretstore] http_port = 8082 port = 8083 -[ipfs] -enable = false -port = 5001 - [mining] author = "0xdeadbeefcafe0000000000000000000000000001" engine_signer = "0xdeadbeefcafe0000000000000000000000000001" @@ -70,23 +64,12 @@ db_compaction = "ssd" fat_db = "off" scale_verifiers = false -[light] -on_demand_response_time_window = 2 -on_demand_request_backoff_start = 9 -on_demand_request_backoff_max = 15 -on_demand_request_backoff_rounds_max = 10 -on_demand_request_consecutive_failures = 1 - [snapshots] -disable_periodic = true +enable = false [misc] logging = "own_tx=trace" -log_file = "/var/log/parity.log" +log_file = "/var/log/openethereum.log" color = true ports_shift = 0 unsafe_expose = false - -[whisper] -enabled = true -pool_size = 50 diff --git a/parity/cli/usage.rs b/parity/cli/usage.rs index 8b06f4f1f68..9a8f8cc9b01 100644 --- a/parity/cli/usage.rs +++ b/parity/cli/usage.rs @@ -1,31 +1,38 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . macro_rules! return_if_parse_error { - ($e:expr) => ( - match $e { - Err(clap_error @ ClapError { kind: ClapErrorKind::ValueValidation, .. }) => { - return Err(clap_error); - }, - - // Otherwise, if $e is ClapErrorKind::ArgumentNotFound or Ok(), - // then convert to Option - _ => $e.ok() - } - ) + ($e:expr) => { + match $e { + Err( + clap_error + @ + ClapError { + kind: ClapErrorKind::ValueValidation, + .. + }, + ) => { + return Err(clap_error); + } + + // Otherwise, if $e is ClapErrorKind::ArgumentNotFound or Ok(), + // then convert to Option + _ => $e.ok(), + } + }; } macro_rules! if_option { @@ -47,46 +54,46 @@ macro_rules! if_vec { } macro_rules! if_option_vec { - (Option>, THEN {$then:expr} ELSE {$otherwise:expr}) => ( - $then - ); - (Option<$type:ty>, THEN {$then:expr} ELSE {$otherwise:expr}) => ( - $otherwise - ); + (Option>, THEN {$then:expr} ELSE {$otherwise:expr}) => { + $then + }; + (Option<$type:ty>, THEN {$then:expr} ELSE {$otherwise:expr}) => { + $otherwise + }; } macro_rules! inner_option_type { - (Option<$type:ty>) => ( - $type - ) + (Option<$type:ty>) => { + $type + }; } macro_rules! inner_vec_type { - (Vec<$type:ty>) => ( - $type - ) + (Vec<$type:ty>) => { + $type + }; } macro_rules! inner_option_vec_type { - (Option>) => ( - String - ) + (Option>) => { + String + }; } macro_rules! usage_with_ident { - ($name:expr, $usage:expr, $help:expr) => ( - if $usage.contains("<") { - format!("<{}> {} '{}'",$name, $usage, $help) - } else { - format!("[{}] {} '{}'",$name, $usage, $help) - } - ); + ($name:expr, $usage:expr, $help:expr) => { + if $usage.contains("<") { + format!("<{}> {} '{}'", $name, $usage, $help) + } else { + format!("[{}] {} '{}'", $name, $usage, $help) + } + }; } macro_rules! underscore_to_hyphen { - ($e:expr) => ( - str::replace($e, "_", "-") - ) + ($e:expr) => { + str::replace($e, "_", "-") + }; } macro_rules! usage { @@ -388,7 +395,7 @@ macro_rules! usage { // Subcommands let mut subcommands_wrapper = Wrapper::new(term_width).subsequent_indent(TAB); - help.push_str("parity [options]\n"); + help.push_str("openethereum [options]\n"); $( { let mut subc_subc_exist = false; @@ -406,7 +413,7 @@ macro_rules! usage { help.push_str(&subcommands_wrapper.fill( format!( - "parity [options] {} {} {}\n", + "openethereum [options] {} {} {}\n", underscore_to_hyphen!(&stringify!($subc)[4..]), underscore_to_hyphen!(&stringify!($subc_subc)[stringify!($subc).len()+1..]), subc_subc_usages.join(" ") @@ -427,7 +434,7 @@ macro_rules! usage { help.push_str(&subcommands_wrapper.fill( format!( - "parity [options] {} {}\n", + "openethereum [options] {} {}\n", underscore_to_hyphen!(&stringify!($subc)[4..]), subc_usages.join(" ") ).as_ref()) @@ -603,7 +610,7 @@ macro_rules! usage { } )* - let matches = App::new("Parity") + let matches = App::new("OpenEthereum") .global_setting(AppSettings::VersionlessSubcommands) .global_setting(AppSettings::DisableHelpSubcommand) .max_term_width(MAX_TERM_WIDTH) @@ -626,7 +633,7 @@ macro_rules! usage { .about($subc_help) .args(&subc_usages.get(stringify!($subc)).unwrap().iter().map(|u| Arg::from_usage(u).use_delimiter(false).allow_hyphen_values(true)).collect::>()) $( - .setting(AppSettings::SubcommandRequired) // prevent from running `parity account` + .setting(AppSettings::SubcommandRequired) // prevent from running `openethereum account` .subcommand( SubCommand::with_name(&underscore_to_hyphen!(&stringify!($subc_subc)[stringify!($subc).len()+1..])) .about($subc_subc_help) diff --git a/parity/cli/usage_header.txt b/parity/cli/usage_header.txt index eaf88670013..a7f156d1f6e 100644 --- a/parity/cli/usage_header.txt +++ b/parity/cli/usage_header.txt @@ -1,3 +1,6 @@ -Parity Ethereum Client. - By Wood/Paronyan/Kotewicz/Drwięga/Volf et al. - Copyright 2015-2019 Parity Technologies (UK) Ltd. +OpenEthereum Client. + By Wood/Paronyan/Kotewicz/Drwięga/Volf/Greeff + Habermeier/Czaban/Gotchac/Redman/Nikolsky + Schoedon/Tang/Adolfsson/Silva/Palm/Hirsz et al. + Copyright 2015-2020 Parity Technologies (UK) Ltd. + License GPLv3+: GNU GPL version 3 or later . diff --git a/parity/cli/version.txt b/parity/cli/version.txt index 85ef190f124..41fa0421247 100644 --- a/parity/cli/version.txt +++ b/parity/cli/version.txt @@ -1,9 +1,10 @@ -Parity Ethereum +OpenEthereum Client. version {} -Copyright 2015-2019 Parity Technologies (UK) Ltd. +Copyright 2015-2020 Parity Technologies (UK) Ltd. License GPLv3+: GNU GPL version 3 or later . This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. -By Wood/Paronyan/Kotewicz/Drwięga/Volf - Habermeier/Czaban/Greeff/Gotchac/Redmann +By Wood/Paronyan/Kotewicz/Drwięga/Volf/Greeff + Habermeier/Czaban/Gotchac/Redman/Nikolsky + Schoedon/Tang/Adolfsson/Silva/Palm/Hirsz et al. diff --git a/parity/configuration.rs b/parity/configuration.rs index 198a58c3f99..7416bfa8c8b 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -1,1065 +1,1079 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::time::Duration; -use std::io::Read; -use std::net::SocketAddr; -use std::num::NonZeroU32; -use std::path::PathBuf; -use std::collections::{HashSet, BTreeMap}; -use std::iter::FromIterator; -use std::cmp; +// along with OpenEthereum. If not, see . + +use ansi_term::Colour; +use bytes::Bytes; use cli::{Args, ArgsError}; +use ethcore::{ + client::VMType, + miner::{stratum, MinerOptions}, + snapshot::SnapshotConfiguration, + verification::queue::VerifierSettings, +}; +use ethereum_types::{Address, H256, U256}; +use ethkey::{Public, Secret}; use hash::keccak; -use ethereum_types::{U256, H256, Address}; -use parity_version::{version_data, version}; -use bytes::Bytes; -use ansi_term::Colour; -use sync::{NetworkConfiguration, validate_node_url, self}; -use ethkey::{Secret, Public}; -use ethcore::client::{VMType}; -use ethcore::miner::{stratum, MinerOptions}; -use ethcore::snapshot::SnapshotConfiguration; -use ethcore::verification::queue::VerifierSettings; +use metrics::MetricsConfiguration; use miner::pool; use num_cpus; - -use rpc::{IpcConfiguration, HttpConfiguration, WsConfiguration}; -use parity_rpc::NetworkSettings; +use parity_version::{version, version_data}; +use std::{ + cmp, + collections::{BTreeMap, HashSet}, + io::Read, + iter::FromIterator, + net::{SocketAddr, ToSocketAddrs}, + num::NonZeroU32, + path::PathBuf, + time::Duration, +}; +use sync::{self, validate_node_url, NetworkConfiguration}; + +use account::{AccountCmd, ImportAccounts, ListAccounts, NewAccount}; +use blockchain::{ + BlockchainCmd, ExportBlockchain, ExportState, ImportBlockchain, KillBlockchain, ResetBlockchain, +}; use cache::CacheConfig; -use helpers::{to_duration, to_mode, to_block_id, to_u256, to_pending_set, to_price, geth_ipc_path, parity_ipc_path, to_bootnodes, to_addresses, to_address, to_queue_strategy, to_queue_penalization}; -use dir::helpers::{replace_home, replace_home_and_local}; -use params::{ResealPolicy, AccountsConfig, GasPricerConfig, MinerExtras, SpecType}; +use dir::{ + self, default_data_path, default_local_path, + helpers::{replace_home, replace_home_and_local}, + Directories, +}; use ethcore_logger::Config as LogConfig; -use dir::{self, Directories, default_hypervisor_path, default_local_path, default_data_path}; -use ipfs::Configuration as IpfsConfiguration; -use ethcore_private_tx::{ProviderConfig, EncryptorConfig}; -use secretstore::{NodeSecretKey, Configuration as SecretStoreConfiguration, ContractAddress as SecretStoreContractAddress}; -use updater::{UpdatePolicy, UpdateFilter, ReleaseTrack}; -use run::RunCmd; -use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, KillBlockchain, ExportState, DataFormat, ResetBlockchain}; -use export_hardcoded_sync::ExportHsyncCmd; +use helpers::{ + parity_ipc_path, to_address, to_addresses, to_block_id, to_bootnodes, to_duration, to_mode, + to_pending_set, to_price, to_queue_penalization, to_queue_strategy, to_u256, +}; +use network::IpFilter; +use params::{AccountsConfig, GasPricerConfig, MinerExtras, ResealPolicy, SpecType}; +use parity_rpc::NetworkSettings; use presale::ImportWallet; -use account::{AccountCmd, NewAccount, ListAccounts, ImportAccounts, ImportFromGethAccounts}; +use rpc::{HttpConfiguration, IpcConfiguration, WsConfiguration}; +use run::RunCmd; +use secretstore::{ + Configuration as SecretStoreConfiguration, ContractAddress as SecretStoreContractAddress, + NodeSecretKey, +}; use snapshot::{self, SnapshotCommand}; -use network::{IpFilter}; +use types::data_format::DataFormat; const DEFAULT_MAX_PEERS: u16 = 50; const DEFAULT_MIN_PEERS: u16 = 25; +pub const ETHERSCAN_ETH_PRICE_ENDPOINT: &str = + "https://api.etherscan.io/api?module=stats&action=ethprice"; #[derive(Debug, PartialEq)] pub enum Cmd { - Run(RunCmd), - Version, - Account(AccountCmd), - ImportPresaleWallet(ImportWallet), - Blockchain(BlockchainCmd), - SignerToken(WsConfiguration, LogConfig), - SignerSign { - id: Option, - pwfile: Option, - port: u16, - authfile: PathBuf, - }, - SignerList { - port: u16, - authfile: PathBuf - }, - SignerReject { - id: Option, - port: u16, - authfile: PathBuf - }, - Snapshot(SnapshotCommand), - Hash(Option), - ExportHardcodedSync(ExportHsyncCmd), + Run(RunCmd), + Version, + Account(AccountCmd), + ImportPresaleWallet(ImportWallet), + Blockchain(BlockchainCmd), + SignerToken(WsConfiguration, LogConfig), + SignerSign { + id: Option, + pwfile: Option, + port: u16, + authfile: PathBuf, + }, + SignerList { + port: u16, + authfile: PathBuf, + }, + SignerReject { + id: Option, + port: u16, + authfile: PathBuf, + }, + Snapshot(SnapshotCommand), + Hash(Option), } pub struct Execute { - pub logger: LogConfig, - pub cmd: Cmd, + pub logger: LogConfig, + pub cmd: Cmd, } -/// Configuration for the Parity client. +/// Configuration for the OpenEthereum client. #[derive(Debug, PartialEq)] pub struct Configuration { - /// Arguments to be interpreted. - pub args: Args, + /// Arguments to be interpreted. + pub args: Args, } impl Configuration { - /// Parses a configuration from a list of command line arguments. - /// - /// # Example - /// - /// ``` - /// let _cfg = parity_ethereum::Configuration::parse_cli(&["--light", "--chain", "kovan"]).unwrap(); - /// ``` - pub fn parse_cli>(command: &[S]) -> Result { - let config = Configuration { - args: Args::parse(command)?, - }; - - Ok(config) - } - - pub(crate) fn into_command(self) -> Result { - let dirs = self.directories(); - let pruning = self.args.arg_pruning.parse()?; - let pruning_history = self.args.arg_pruning_history; - let vm_type = self.vm_type()?; - let spec = self.chain()?; - let mode = match self.args.arg_mode.as_ref() { - "last" => None, - mode => Some(to_mode(&mode, self.args.arg_mode_timeout, self.args.arg_mode_alarm)?), - }; - let update_policy = self.update_policy()?; - let logger_config = self.logger_config(); - let ws_conf = self.ws_config()?; - let snapshot_conf = self.snapshot_config()?; - let http_conf = self.http_config()?; - let ipc_conf = self.ipc_config()?; - let net_conf = self.net_config()?; - let network_id = self.network_id(); - let cache_config = self.cache_config(); - let tracing = self.args.arg_tracing.parse()?; - let fat_db = self.args.arg_fat_db.parse()?; - let compaction = self.args.arg_db_compaction.parse()?; - let warp_sync = !self.args.flag_no_warp; - let geth_compatibility = self.args.flag_geth; - let experimental_rpcs = self.args.flag_jsonrpc_experimental; - let ipfs_conf = self.ipfs_config(); - let secretstore_conf = self.secretstore_config()?; - let format = self.format()?; - let keys_iterations = NonZeroU32::new(self.args.arg_keys_iterations) - .ok_or_else(|| "--keys-iterations must be non-zero")?; - - let cmd = if self.args.flag_version { - Cmd::Version - } else if self.args.cmd_signer { - let authfile = ::signer::codes_path(&ws_conf.signer_path); - - if self.args.cmd_signer_new_token { - Cmd::SignerToken(ws_conf, logger_config.clone()) - } else if self.args.cmd_signer_sign { - let pwfile = self.accounts_config()?.password_files.first().map(|pwfile| { - PathBuf::from(pwfile) - }); - Cmd::SignerSign { - id: self.args.arg_signer_sign_id, - pwfile: pwfile, - port: ws_conf.port, - authfile: authfile, - } - } else if self.args.cmd_signer_reject { - Cmd::SignerReject { - id: self.args.arg_signer_reject_id, - port: ws_conf.port, - authfile: authfile, - } - } else if self.args.cmd_signer_list { - Cmd::SignerList { - port: ws_conf.port, - authfile: authfile, - } - } else { - unreachable!(); - } - } else if self.args.cmd_tools && self.args.cmd_tools_hash { - Cmd::Hash(self.args.arg_tools_hash_file) - } else if self.args.cmd_db && self.args.cmd_db_reset { - Cmd::Blockchain(BlockchainCmd::Reset(ResetBlockchain { - dirs, - spec, - pruning, - pruning_history, - pruning_memory: self.args.arg_pruning_memory, - tracing, - fat_db, - compaction, - cache_config, - num: self.args.arg_db_reset_num, - })) - } else if self.args.cmd_db && self.args.cmd_db_kill { - Cmd::Blockchain(BlockchainCmd::Kill(KillBlockchain { - spec: spec, - dirs: dirs, - pruning: pruning, - })) - } else if self.args.cmd_account { - let account_cmd = if self.args.cmd_account_new { - let new_acc = NewAccount { - iterations: keys_iterations, - path: dirs.keys, - spec: spec, - password_file: self.accounts_config()?.password_files.first().map(|x| x.to_owned()), - }; - AccountCmd::New(new_acc) - } else if self.args.cmd_account_list { - let list_acc = ListAccounts { - path: dirs.keys, - spec: spec, - }; - AccountCmd::List(list_acc) - } else if self.args.cmd_account_import { - let import_acc = ImportAccounts { - from: self.args.arg_account_import_path.expect("CLI argument is required; qed").clone(), - to: dirs.keys, - spec: spec, - }; - AccountCmd::Import(import_acc) - } else { - unreachable!(); - }; - Cmd::Account(account_cmd) - } else if self.args.flag_import_geth_keys { - let account_cmd = AccountCmd::ImportFromGeth( - ImportFromGethAccounts { - spec: spec, - to: dirs.keys, - testnet: self.args.flag_testnet - } - ); - Cmd::Account(account_cmd) - } else if self.args.cmd_wallet { - let presale_cmd = ImportWallet { - iterations: keys_iterations, - path: dirs.keys, - spec: spec, - wallet_path: self.args.arg_wallet_import_path.clone().unwrap(), - password_file: self.accounts_config()?.password_files.first().map(|x| x.to_owned()), - }; - Cmd::ImportPresaleWallet(presale_cmd) - } else if self.args.cmd_import { - let import_cmd = ImportBlockchain { - spec: spec, - cache_config: cache_config, - dirs: dirs, - file_path: self.args.arg_import_file.clone(), - format: format, - pruning: pruning, - pruning_history: pruning_history, - pruning_memory: self.args.arg_pruning_memory, - compaction: compaction, - tracing: tracing, - fat_db: fat_db, - vm_type: vm_type, - check_seal: !self.args.flag_no_seal_check, - with_color: logger_config.color, - verifier_settings: self.verifier_settings(), - light: self.args.flag_light, - max_round_blocks_to_import: self.args.arg_max_round_blocks_to_import, - }; - Cmd::Blockchain(BlockchainCmd::Import(import_cmd)) - } else if self.args.cmd_export { - if self.args.cmd_export_blocks { - let export_cmd = ExportBlockchain { - spec: spec, - cache_config: cache_config, - dirs: dirs, - file_path: self.args.arg_export_blocks_file.clone(), - format: format, - pruning: pruning, - pruning_history: pruning_history, - pruning_memory: self.args.arg_pruning_memory, - compaction: compaction, - tracing: tracing, - fat_db: fat_db, - from_block: to_block_id(&self.args.arg_export_blocks_from)?, - to_block: to_block_id(&self.args.arg_export_blocks_to)?, - check_seal: !self.args.flag_no_seal_check, - max_round_blocks_to_import: self.args.arg_max_round_blocks_to_import, - }; - Cmd::Blockchain(BlockchainCmd::Export(export_cmd)) - } else if self.args.cmd_export_state { - let export_cmd = ExportState { - spec: spec, - cache_config: cache_config, - dirs: dirs, - file_path: self.args.arg_export_state_file.clone(), - format: format, - pruning: pruning, - pruning_history: pruning_history, - pruning_memory: self.args.arg_pruning_memory, - compaction: compaction, - tracing: tracing, - fat_db: fat_db, - at: to_block_id(&self.args.arg_export_state_at)?, - storage: !self.args.flag_export_state_no_storage, - code: !self.args.flag_export_state_no_code, - min_balance: self.args.arg_export_state_min_balance.and_then(|s| to_u256(&s).ok()), - max_balance: self.args.arg_export_state_max_balance.and_then(|s| to_u256(&s).ok()), - max_round_blocks_to_import: self.args.arg_max_round_blocks_to_import, - }; - Cmd::Blockchain(BlockchainCmd::ExportState(export_cmd)) - } else { - unreachable!(); - } - } else if self.args.cmd_snapshot { - let snapshot_cmd = SnapshotCommand { - cache_config: cache_config, - dirs: dirs, - spec: spec, - pruning: pruning, - pruning_history: pruning_history, - pruning_memory: self.args.arg_pruning_memory, - tracing: tracing, - fat_db: fat_db, - compaction: compaction, - file_path: self.args.arg_snapshot_file.clone(), - kind: snapshot::Kind::Take, - block_at: to_block_id(&self.args.arg_snapshot_at)?, - max_round_blocks_to_import: self.args.arg_max_round_blocks_to_import, - snapshot_conf: snapshot_conf, - }; - Cmd::Snapshot(snapshot_cmd) - } else if self.args.cmd_restore { - let restore_cmd = SnapshotCommand { - cache_config: cache_config, - dirs: dirs, - spec: spec, - pruning: pruning, - pruning_history: pruning_history, - pruning_memory: self.args.arg_pruning_memory, - tracing: tracing, - fat_db: fat_db, - compaction: compaction, - file_path: self.args.arg_restore_file.clone(), - kind: snapshot::Kind::Restore, - block_at: to_block_id("latest")?, // unimportant. - max_round_blocks_to_import: self.args.arg_max_round_blocks_to_import, - snapshot_conf: snapshot_conf, - }; - Cmd::Snapshot(restore_cmd) - } else if self.args.cmd_export_hardcoded_sync { - let export_hs_cmd = ExportHsyncCmd { - cache_config: cache_config, - dirs: dirs, - spec: spec, - pruning: pruning, - compaction: compaction, - }; - Cmd::ExportHardcodedSync(export_hs_cmd) - } else { - let daemon = if self.args.cmd_daemon { - Some(self.args.arg_daemon_pid_file.clone().expect("CLI argument is required; qed")) - } else { - None - }; - - let verifier_settings = self.verifier_settings(); - let whisper_config = self.whisper_config(); - let (private_provider_conf, private_enc_conf, private_tx_enabled) = self.private_provider_config()?; - - let run_cmd = RunCmd { - cache_config: cache_config, - dirs: dirs, - spec: spec, - pruning: pruning, - pruning_history: pruning_history, - pruning_memory: self.args.arg_pruning_memory, - daemon: daemon, - logger_config: logger_config.clone(), - miner_options: self.miner_options()?, - gas_price_percentile: self.args.arg_gas_price_percentile, - poll_lifetime: self.args.arg_poll_lifetime, - ws_conf: ws_conf, - snapshot_conf: snapshot_conf, - http_conf: http_conf, - ipc_conf: ipc_conf, - net_conf: net_conf, - network_id: network_id, - acc_conf: self.accounts_config()?, - gas_pricer_conf: self.gas_pricer_config()?, - miner_extras: self.miner_extras()?, - stratum: self.stratum_options()?, - update_policy: update_policy, - allow_missing_blocks: self.args.flag_jsonrpc_allow_missing_blocks, - mode: mode, - tracing: tracing, - fat_db: fat_db, - compaction: compaction, - vm_type: vm_type, - warp_sync: warp_sync, - warp_barrier: self.args.arg_warp_barrier, - geth_compatibility: geth_compatibility, - experimental_rpcs, - net_settings: self.network_settings()?, - ipfs_conf: ipfs_conf, - secretstore_conf: secretstore_conf, - private_provider_conf: private_provider_conf, - private_encryptor_conf: private_enc_conf, - private_tx_enabled, - name: self.args.arg_identity, - custom_bootnodes: self.args.arg_bootnodes.is_some(), - check_seal: !self.args.flag_no_seal_check, - download_old_blocks: !self.args.flag_no_ancient_blocks, - verifier_settings: verifier_settings, - serve_light: !self.args.flag_no_serve_light, - light: self.args.flag_light, - no_persistent_txqueue: self.args.flag_no_persistent_txqueue, - whisper: whisper_config, - no_hardcoded_sync: self.args.flag_no_hardcoded_sync, - max_round_blocks_to_import: self.args.arg_max_round_blocks_to_import, - on_demand_response_time_window: self.args.arg_on_demand_response_time_window, - on_demand_request_backoff_start: self.args.arg_on_demand_request_backoff_start, - on_demand_request_backoff_max: self.args.arg_on_demand_request_backoff_max, - on_demand_request_backoff_rounds_max: self.args.arg_on_demand_request_backoff_rounds_max, - on_demand_request_consecutive_failures: self.args.arg_on_demand_request_consecutive_failures, - }; - Cmd::Run(run_cmd) - }; - - Ok(Execute { - logger: logger_config, - cmd: cmd, - }) - } - - fn vm_type(&self) -> Result { - Ok(VMType::Interpreter) - } - - fn miner_extras(&self) -> Result { - let floor = to_u256(&self.args.arg_gas_floor_target)?; - let ceil = to_u256(&self.args.arg_gas_cap)?; - let extras = MinerExtras { - author: self.author()?, - extra_data: self.extra_data()?, - gas_range_target: (floor, ceil), - engine_signer: self.engine_signer()?, - work_notify: self.work_notify(), - local_accounts: HashSet::from_iter(to_addresses(&self.args.arg_tx_queue_locals)?.into_iter()), - }; - - Ok(extras) - } - - fn author(&self) -> Result { - to_address(self.args.arg_etherbase.clone().or(self.args.arg_author.clone())) - } - - fn engine_signer(&self) -> Result { - to_address(self.args.arg_engine_signer.clone()) - } - - fn format(&self) -> Result, String> { - match self.args.arg_import_format.clone() - .or(self.args.arg_export_blocks_format.clone()) - .or(self.args.arg_export_state_format.clone()) { - Some(ref f) => Ok(Some(f.parse()?)), - None => Ok(None), - } - } - - fn cache_config(&self) -> CacheConfig { - match self.args.arg_cache_size.or(self.args.arg_cache) { - Some(size) => CacheConfig::new_with_total_cache_size(size), - None => CacheConfig::new( - self.args.arg_cache_size_db, - self.args.arg_cache_size_blocks, - self.args.arg_cache_size_queue, - self.args.arg_cache_size_state, - ), - } - } - - /// returns logger config - pub fn logger_config(&self) -> LogConfig { - LogConfig { - mode: self.args.arg_logging.clone(), - color: !self.args.flag_no_color && !cfg!(windows), - file: self.args.arg_log_file.as_ref().map(|log_file| replace_home(&self.directories().base, log_file)), - } - } - - fn chain(&self) -> Result { - let name = if self.args.flag_testnet { - "testnet".to_owned() - } else { - self.args.arg_chain.clone() - }; - - Ok(name.parse()?) - } - - fn is_dev_chain(&self) -> Result { - Ok(self.chain()? == SpecType::Dev) - } - - fn max_peers(&self) -> u32 { - self.args.arg_max_peers - .or(cmp::max(self.args.arg_min_peers, Some(DEFAULT_MAX_PEERS))) - .unwrap_or(DEFAULT_MAX_PEERS) as u32 - } - - fn ip_filter(&self) -> Result { - match IpFilter::parse(self.args.arg_allow_ips.as_str()) { - Ok(allow_ip) => Ok(allow_ip), - Err(_) => Err("Invalid IP filter value".to_owned()), - } - } - - fn min_peers(&self) -> u32 { - self.args.arg_min_peers - .or(cmp::min(self.args.arg_max_peers, Some(DEFAULT_MIN_PEERS))) - .unwrap_or(DEFAULT_MIN_PEERS) as u32 - } - - fn max_pending_peers(&self) -> u32 { - self.args.arg_max_pending_peers as u32 - } - - fn snapshot_peers(&self) -> u32 { - self.args.arg_snapshot_peers as u32 - } - - fn work_notify(&self) -> Vec { - self.args.arg_notify_work.as_ref().map_or_else(Vec::new, |s| s.split(',').map(|s| s.to_owned()).collect()) - } - - fn accounts_config(&self) -> Result { - let keys_iterations = NonZeroU32::new(self.args.arg_keys_iterations) - .ok_or_else(|| "--keys-iterations must be non-zero")?; - let cfg = AccountsConfig { - iterations: keys_iterations, - refresh_time: self.args.arg_accounts_refresh, - testnet: self.args.flag_testnet, - password_files: self.args.arg_password.iter().map(|s| replace_home(&self.directories().base, s)).collect(), - unlocked_accounts: to_addresses(&self.args.arg_unlock)?, - enable_hardware_wallets: !self.args.flag_no_hardware_wallets, - enable_fast_unlock: self.args.flag_fast_unlock, - }; - - Ok(cfg) - } - - fn stratum_options(&self) -> Result, String> { - if self.args.flag_stratum { - Ok(Some(stratum::Options { - io_path: self.directories().db, - listen_addr: self.stratum_interface(), - port: self.args.arg_ports_shift + self.args.arg_stratum_port, - secret: self.args.arg_stratum_secret.as_ref().map(|s| s.parse::().unwrap_or_else(|_| keccak(s))), - })) - } else { Ok(None) } - } - - fn miner_options(&self) -> Result { - let is_dev_chain = self.is_dev_chain()?; - if is_dev_chain && self.args.flag_force_sealing && self.args.arg_reseal_min_period == 0 { - return Err("Force sealing can't be used with reseal_min_period = 0".into()); - } - - let reseal = self.args.arg_reseal_on_txs.parse::()?; - - let options = MinerOptions { - force_sealing: self.args.flag_force_sealing, - reseal_on_external_tx: reseal.external, - reseal_on_own_tx: reseal.own, - reseal_on_uncle: self.args.flag_reseal_on_uncle, - reseal_min_period: Duration::from_millis(self.args.arg_reseal_min_period), - reseal_max_period: Duration::from_millis(self.args.arg_reseal_max_period), - - pending_set: to_pending_set(&self.args.arg_relay_set)?, - work_queue_size: self.args.arg_work_queue_size, - enable_resubmission: !self.args.flag_remove_solved, - infinite_pending_block: self.args.flag_infinite_pending_block, - - tx_queue_penalization: to_queue_penalization(self.args.arg_tx_time_limit)?, - tx_queue_strategy: to_queue_strategy(&self.args.arg_tx_queue_strategy)?, - tx_queue_no_unfamiliar_locals: self.args.flag_tx_queue_no_unfamiliar_locals, - refuse_service_transactions: self.args.flag_refuse_service_transactions, - - pool_limits: self.pool_limits()?, - pool_verification_options: self.pool_verification_options()?, - }; - - Ok(options) - } - - fn pool_limits(&self) -> Result { - let max_count = self.args.arg_tx_queue_size; - - Ok(pool::Options { - max_count, - max_per_sender: self.args.arg_tx_queue_per_sender.unwrap_or_else(|| cmp::max(16, max_count / 100)), - max_mem_usage: if self.args.arg_tx_queue_mem_limit > 0 { - self.args.arg_tx_queue_mem_limit as usize * 1024 * 1024 - } else { - usize::max_value() - }, - }) - } - - fn pool_verification_options(&self) -> Result{ - Ok(pool::verifier::Options { - // NOTE min_gas_price and block_gas_limit will be overwritten right after start. - minimal_gas_price: U256::from(20_000_000) * 1_000u32, - block_gas_limit: U256::max_value(), - tx_gas_limit: match self.args.arg_tx_gas_limit { - Some(ref d) => to_u256(d)?, - None => U256::max_value(), - }, - no_early_reject: self.args.flag_tx_queue_no_early_reject, - }) - } - - fn secretstore_config(&self) -> Result { - Ok(SecretStoreConfiguration { - enabled: self.secretstore_enabled(), - http_enabled: self.secretstore_http_enabled(), - auto_migrate_enabled: self.secretstore_auto_migrate_enabled(), - acl_check_contract_address: self.secretstore_acl_check_contract_address()?, - service_contract_address: self.secretstore_service_contract_address()?, - service_contract_srv_gen_address: self.secretstore_service_contract_srv_gen_address()?, - service_contract_srv_retr_address: self.secretstore_service_contract_srv_retr_address()?, - service_contract_doc_store_address: self.secretstore_service_contract_doc_store_address()?, - service_contract_doc_sretr_address: self.secretstore_service_contract_doc_sretr_address()?, - self_secret: self.secretstore_self_secret()?, - nodes: self.secretstore_nodes()?, - key_server_set_contract_address: self.secretstore_key_server_set_contract_address()?, - interface: self.secretstore_interface(), - port: self.args.arg_ports_shift + self.args.arg_secretstore_port, - http_interface: self.secretstore_http_interface(), - http_port: self.args.arg_ports_shift + self.args.arg_secretstore_http_port, - data_path: self.directories().secretstore, - admin_public: self.secretstore_admin_public()?, - }) - } - - fn ipfs_config(&self) -> IpfsConfiguration { - IpfsConfiguration { - enabled: self.args.flag_ipfs_api, - port: self.args.arg_ports_shift + self.args.arg_ipfs_api_port, - interface: self.ipfs_interface(), - cors: self.ipfs_cors(), - hosts: self.ipfs_hosts(), - } - } - - fn gas_pricer_config(&self) -> Result { - fn wei_per_gas(usd_per_tx: f32, usd_per_eth: f32) -> U256 { - let wei_per_usd: f32 = 1.0e18 / usd_per_eth; - let gas_per_tx: f32 = 21000.0; - let wei_per_gas: f32 = wei_per_usd * usd_per_tx / gas_per_tx; - U256::from_dec_str(&format!("{:.0}", wei_per_gas)).unwrap() - } - - if let Some(dec) = self.args.arg_gasprice.as_ref() { - return Ok(GasPricerConfig::Fixed(to_u256(dec)?)); - } else if let Some(dec) = self.args.arg_min_gas_price { - return Ok(GasPricerConfig::Fixed(U256::from(dec))); - } else if self.chain()? != SpecType::Foundation { - return Ok(GasPricerConfig::Fixed(U256::zero())); - } - - let usd_per_tx = to_price(&self.args.arg_usd_per_tx)?; - if "auto" == self.args.arg_usd_per_eth.as_str() { - return Ok(GasPricerConfig::Calibrated { - usd_per_tx: usd_per_tx, - recalibration_period: to_duration(self.args.arg_price_update_period.as_str())?, - }); - } - - let usd_per_eth = to_price(&self.args.arg_usd_per_eth)?; - let wei_per_gas = wei_per_gas(usd_per_tx, usd_per_eth); - - info!( - "Using a fixed conversion rate of Ξ1 = {} ({} wei/gas)", - Colour::White.bold().paint(format!("US${:.2}", usd_per_eth)), - Colour::Yellow.bold().paint(format!("{}", wei_per_gas)) - ); - - Ok(GasPricerConfig::Fixed(wei_per_gas)) - } - - fn extra_data(&self) -> Result { - match self.args.arg_extradata.as_ref().or(self.args.arg_extra_data.as_ref()) { - Some(x) if x.len() <= 32 => Ok(x.as_bytes().to_owned()), - None => Ok(version_data()), - Some(_) => Err("Extra data must be at most 32 characters".into()), - } - } - - fn init_reserved_nodes(&self) -> Result, String> { - use std::fs::File; - - match self.args.arg_reserved_peers { - Some(ref path) => { - let path = replace_home(&self.directories().base, path); - - let mut buffer = String::new(); - let mut node_file = File::open(&path).map_err(|e| format!("Error opening reserved nodes file: {}", e))?; - node_file.read_to_string(&mut buffer).map_err(|_| "Error reading reserved node file")?; - let lines = buffer.lines().map(|s| s.trim().to_owned()).filter(|s| !s.is_empty() && !s.starts_with("#")).collect::>(); - - for line in &lines { - match validate_node_url(line).map(Into::into) { - None => continue, - Some(sync::ErrorKind::AddressResolve(_)) => return Err(format!("Failed to resolve hostname of a boot node: {}", line)), - Some(_) => return Err(format!("Invalid node address format given for a boot node: {}", line)), - } - } - - Ok(lines) - }, - None => Ok(Vec::new()) - } - } - - fn net_addresses(&self) -> Result<(SocketAddr, Option), String> { - let port = self.args.arg_ports_shift + self.args.arg_port; - let listen_address = SocketAddr::new(self.interface(&self.args.arg_interface).parse().unwrap(), port); - let public_address = if self.args.arg_nat.starts_with("extip:") { - let host = &self.args.arg_nat[6..]; - let host = host.parse().map_err(|_| format!("Invalid host given with `--nat extip:{}`", host))?; - Some(SocketAddr::new(host, port)) - } else { - None - }; - Ok((listen_address, public_address)) - } - - fn net_config(&self) -> Result { - let mut ret = NetworkConfiguration::new(); - ret.nat_enabled = self.args.arg_nat == "any" || self.args.arg_nat == "upnp"; - ret.boot_nodes = to_bootnodes(&self.args.arg_bootnodes)?; - let (listen, public) = self.net_addresses()?; - ret.listen_address = Some(format!("{}", listen)); - ret.public_address = public.map(|p| format!("{}", p)); - ret.use_secret = match self.args.arg_node_key.as_ref() - .map(|s| s.parse::().or_else(|_| Secret::from_unsafe_slice(&keccak(s))).map_err(|e| format!("Invalid key: {:?}", e)) - ) { - None => None, - Some(Ok(key)) => Some(key), - Some(Err(err)) => return Err(err), - }; - ret.discovery_enabled = !self.args.flag_no_discovery && !self.args.flag_nodiscover; - ret.max_peers = self.max_peers(); - ret.min_peers = self.min_peers(); - ret.snapshot_peers = self.snapshot_peers(); - ret.ip_filter = self.ip_filter()?; - ret.max_pending_peers = self.max_pending_peers(); - let mut net_path = PathBuf::from(self.directories().base); - net_path.push("network"); - ret.config_path = Some(net_path.to_str().unwrap().to_owned()); - ret.reserved_nodes = self.init_reserved_nodes()?; - ret.allow_non_reserved = !self.args.flag_reserved_only; - ret.client_version = { - let mut client_version = version(); - if !self.args.arg_identity.is_empty() { - // Insert name after the "Parity-Ethereum/" at the beginning of version string. - let idx = client_version.find('/').unwrap_or(client_version.len()); - client_version.insert_str(idx, &format!("/{}", self.args.arg_identity)); - } - client_version - }; - Ok(ret) - } - - fn network_id(&self) -> Option { - self.args.arg_network_id.or(self.args.arg_networkid) - } - - fn rpc_apis(&self) -> String { - let mut apis: Vec<&str> = self.args.arg_rpcapi - .as_ref() - .unwrap_or(&self.args.arg_jsonrpc_apis) - .split(",") - .collect(); - - if self.args.flag_geth { - apis.insert(0, "personal"); - } - - apis.join(",") - } - - fn cors(cors: &str) -> Option> { - match cors { - "none" => return Some(Vec::new()), - "*" | "all" | "any" => return None, - _ => {}, - } - - Some(cors.split(',').map(Into::into).collect()) - } - - fn rpc_cors(&self) -> Option> { - let cors = self.args.arg_rpccorsdomain.clone().unwrap_or_else(|| self.args.arg_jsonrpc_cors.to_owned()); - Self::cors(&cors) - } - - fn ipfs_cors(&self) -> Option> { - Self::cors(self.args.arg_ipfs_api_cors.as_ref()) - } - - fn hosts(&self, hosts: &str, interface: &str) -> Option> { - if self.args.flag_unsafe_expose { - return None; - } - - if interface == "0.0.0.0" && hosts == "none" { - return None; - } - - Self::parse_hosts(hosts) - } - - fn parse_hosts(hosts: &str) -> Option> { - match hosts { - "none" => return Some(Vec::new()), - "*" | "all" | "any" => return None, - _ => {} - } - let hosts = hosts.split(',').map(Into::into).collect(); - Some(hosts) - } - - fn rpc_hosts(&self) -> Option> { - self.hosts(&self.args.arg_jsonrpc_hosts, &self.rpc_interface()) - } - - fn ws_hosts(&self) -> Option> { - self.hosts(&self.args.arg_ws_hosts, &self.ws_interface()) - } - - fn ws_origins(&self) -> Option> { - if self.args.flag_unsafe_expose { - return None; - } - - Self::parse_hosts(&self.args.arg_ws_origins) - } - - fn ipfs_hosts(&self) -> Option> { - self.hosts(&self.args.arg_ipfs_api_hosts, &self.ipfs_interface()) - } - - fn ipc_config(&self) -> Result { - let conf = IpcConfiguration { - enabled: !(self.args.flag_ipcdisable || self.args.flag_ipc_off || self.args.flag_no_ipc), - socket_addr: self.ipc_path(), - apis: { - let mut apis = self.args.arg_ipcapi.clone().unwrap_or(self.args.arg_ipc_apis.clone()); - if self.args.flag_geth { - if !apis.is_empty() { - apis.push_str(","); - } - apis.push_str("personal"); - } - apis.parse()? - }, - }; - - Ok(conf) - } - - fn http_config(&self) -> Result { - let conf = HttpConfiguration { - enabled: self.rpc_enabled(), - interface: self.rpc_interface(), - port: self.args.arg_ports_shift + self.args.arg_rpcport.unwrap_or(self.args.arg_jsonrpc_port), - apis: self.rpc_apis().parse()?, - hosts: self.rpc_hosts(), - cors: self.rpc_cors(), - server_threads: match self.args.arg_jsonrpc_server_threads { - Some(threads) if threads > 0 => threads, - _ => 1, - }, - processing_threads: self.args.arg_jsonrpc_threads, - max_payload: match self.args.arg_jsonrpc_max_payload { - Some(max) if max > 0 => max as usize, - _ => 5usize, - }, - keep_alive: !self.args.flag_jsonrpc_no_keep_alive, - }; - - Ok(conf) - } - - fn ws_config(&self) -> Result { - let support_token_api = + /// Parses a configuration from a list of command line arguments. + /// + /// # Example + /// + /// ``` + /// let _cfg = openethereum::Configuration::parse_cli(&["--light", "--chain", "kovan"]).unwrap(); + /// ``` + pub fn parse_cli>(command: &[S]) -> Result { + let config = Configuration { + args: Args::parse(command)?, + }; + + Ok(config) + } + + pub(crate) fn into_command(self) -> Result { + let dirs = self.directories(); + let pruning = self.args.arg_pruning.parse()?; + let pruning_history = self.args.arg_pruning_history; + let vm_type = self.vm_type()?; + let spec = self.chain()?; + let mode = match self.args.arg_mode.as_ref() { + "last" => None, + mode => Some(to_mode( + &mode, + self.args.arg_mode_timeout, + self.args.arg_mode_alarm, + )?), + }; + let logger_config = self.logger_config(); + let ws_conf = self.ws_config()?; + let snapshot_conf = self.snapshot_config()?; + let http_conf = self.http_config()?; + let ipc_conf = self.ipc_config()?; + let net_conf = self.net_config()?; + let network_id = self.network_id(); + let cache_config = self.cache_config(); + let tracing = self.args.arg_tracing.parse()?; + let fat_db = self.args.arg_fat_db.parse()?; + let compaction = self.args.arg_db_compaction.parse()?; + let warp_sync = !self.args.flag_no_warp; + let experimental_rpcs = self.args.flag_jsonrpc_experimental; + let secretstore_conf = self.secretstore_config()?; + let format = self.format()?; + let metrics_conf = self.metrics_config()?; + let keys_iterations = NonZeroU32::new(self.args.arg_keys_iterations) + .ok_or_else(|| "--keys-iterations must be non-zero")?; + + let cmd = if self.args.flag_version { + Cmd::Version + } else if self.args.cmd_signer { + let authfile = ::signer::codes_path(&ws_conf.signer_path); + + if self.args.cmd_signer_new_token { + Cmd::SignerToken(ws_conf, logger_config.clone()) + } else if self.args.cmd_signer_sign { + let pwfile = self + .accounts_config()? + .password_files + .first() + .map(|pwfile| PathBuf::from(pwfile)); + Cmd::SignerSign { + id: self.args.arg_signer_sign_id, + pwfile: pwfile, + port: ws_conf.port, + authfile: authfile, + } + } else if self.args.cmd_signer_reject { + Cmd::SignerReject { + id: self.args.arg_signer_reject_id, + port: ws_conf.port, + authfile: authfile, + } + } else if self.args.cmd_signer_list { + Cmd::SignerList { + port: ws_conf.port, + authfile: authfile, + } + } else { + unreachable!(); + } + } else if self.args.cmd_tools && self.args.cmd_tools_hash { + Cmd::Hash(self.args.arg_tools_hash_file) + } else if self.args.cmd_db && self.args.cmd_db_reset { + Cmd::Blockchain(BlockchainCmd::Reset(ResetBlockchain { + dirs, + spec, + pruning, + pruning_history, + pruning_memory: self.args.arg_pruning_memory, + tracing, + fat_db, + compaction, + cache_config, + num: self.args.arg_db_reset_num, + })) + } else if self.args.cmd_db && self.args.cmd_db_kill { + Cmd::Blockchain(BlockchainCmd::Kill(KillBlockchain { + spec: spec, + dirs: dirs, + pruning: pruning, + })) + } else if self.args.cmd_account { + let account_cmd = if self.args.cmd_account_new { + let new_acc = NewAccount { + iterations: keys_iterations, + path: dirs.keys, + spec: spec, + password_file: self + .accounts_config()? + .password_files + .first() + .map(|x| x.to_owned()), + }; + AccountCmd::New(new_acc) + } else if self.args.cmd_account_list { + let list_acc = ListAccounts { + path: dirs.keys, + spec: spec, + }; + AccountCmd::List(list_acc) + } else if self.args.cmd_account_import { + let import_acc = ImportAccounts { + from: self + .args + .arg_account_import_path + .expect("CLI argument is required; qed") + .clone(), + to: dirs.keys, + spec: spec, + }; + AccountCmd::Import(import_acc) + } else { + unreachable!(); + }; + Cmd::Account(account_cmd) + } else if self.args.cmd_wallet { + let presale_cmd = ImportWallet { + iterations: keys_iterations, + path: dirs.keys, + spec: spec, + wallet_path: self.args.arg_wallet_import_path.clone().unwrap(), + password_file: self + .accounts_config()? + .password_files + .first() + .map(|x| x.to_owned()), + }; + Cmd::ImportPresaleWallet(presale_cmd) + } else if self.args.cmd_import { + let import_cmd = ImportBlockchain { + spec: spec, + cache_config: cache_config, + dirs: dirs, + file_path: self.args.arg_import_file.clone(), + format: format, + pruning: pruning, + pruning_history: pruning_history, + pruning_memory: self.args.arg_pruning_memory, + compaction: compaction, + tracing: tracing, + fat_db: fat_db, + vm_type: vm_type, + check_seal: !self.args.flag_no_seal_check, + with_color: logger_config.color, + verifier_settings: self.verifier_settings(), + max_round_blocks_to_import: self.args.arg_max_round_blocks_to_import, + }; + Cmd::Blockchain(BlockchainCmd::Import(import_cmd)) + } else if self.args.cmd_export { + if self.args.cmd_export_blocks { + let export_cmd = ExportBlockchain { + spec: spec, + cache_config: cache_config, + dirs: dirs, + file_path: self.args.arg_export_blocks_file.clone(), + format: format, + pruning: pruning, + pruning_history: pruning_history, + pruning_memory: self.args.arg_pruning_memory, + compaction: compaction, + tracing: tracing, + fat_db: fat_db, + from_block: to_block_id(&self.args.arg_export_blocks_from)?, + to_block: to_block_id(&self.args.arg_export_blocks_to)?, + check_seal: !self.args.flag_no_seal_check, + max_round_blocks_to_import: self.args.arg_max_round_blocks_to_import, + }; + Cmd::Blockchain(BlockchainCmd::Export(export_cmd)) + } else if self.args.cmd_export_state { + let export_cmd = ExportState { + spec: spec, + cache_config: cache_config, + dirs: dirs, + file_path: self.args.arg_export_state_file.clone(), + format: format, + pruning: pruning, + pruning_history: pruning_history, + pruning_memory: self.args.arg_pruning_memory, + compaction: compaction, + tracing: tracing, + fat_db: fat_db, + at: to_block_id(&self.args.arg_export_state_at)?, + storage: !self.args.flag_export_state_no_storage, + code: !self.args.flag_export_state_no_code, + min_balance: self + .args + .arg_export_state_min_balance + .and_then(|s| to_u256(&s).ok()), + max_balance: self + .args + .arg_export_state_max_balance + .and_then(|s| to_u256(&s).ok()), + max_round_blocks_to_import: self.args.arg_max_round_blocks_to_import, + }; + Cmd::Blockchain(BlockchainCmd::ExportState(export_cmd)) + } else { + unreachable!(); + } + } else if self.args.cmd_snapshot { + let snapshot_cmd = SnapshotCommand { + cache_config: cache_config, + dirs: dirs, + spec: spec, + pruning: pruning, + pruning_history: pruning_history, + pruning_memory: self.args.arg_pruning_memory, + tracing: tracing, + fat_db: fat_db, + compaction: compaction, + file_path: self.args.arg_snapshot_file.clone(), + kind: snapshot::Kind::Take, + block_at: to_block_id(&self.args.arg_snapshot_at)?, + max_round_blocks_to_import: self.args.arg_max_round_blocks_to_import, + snapshot_conf: snapshot_conf, + }; + Cmd::Snapshot(snapshot_cmd) + } else if self.args.cmd_restore { + let restore_cmd = SnapshotCommand { + cache_config: cache_config, + dirs: dirs, + spec: spec, + pruning: pruning, + pruning_history: pruning_history, + pruning_memory: self.args.arg_pruning_memory, + tracing: tracing, + fat_db: fat_db, + compaction: compaction, + file_path: self.args.arg_restore_file.clone(), + kind: snapshot::Kind::Restore, + block_at: to_block_id("latest")?, // unimportant. + max_round_blocks_to_import: self.args.arg_max_round_blocks_to_import, + snapshot_conf: snapshot_conf, + }; + Cmd::Snapshot(restore_cmd) + } else { + let daemon = if self.args.cmd_daemon { + Some( + self.args + .arg_daemon_pid_file + .clone() + .expect("CLI argument is required; qed"), + ) + } else { + None + }; + + let verifier_settings = self.verifier_settings(); + + let run_cmd = RunCmd { + cache_config: cache_config, + dirs: dirs, + spec: spec, + pruning: pruning, + pruning_history: pruning_history, + pruning_memory: self.args.arg_pruning_memory, + daemon: daemon, + logger_config: logger_config.clone(), + miner_options: self.miner_options()?, + gas_price_percentile: self.args.arg_gas_price_percentile, + poll_lifetime: self.args.arg_poll_lifetime, + ws_conf: ws_conf, + snapshot_conf: snapshot_conf, + http_conf: http_conf, + ipc_conf: ipc_conf, + net_conf: net_conf, + network_id: network_id, + acc_conf: self.accounts_config()?, + gas_pricer_conf: self.gas_pricer_config()?, + miner_extras: self.miner_extras()?, + stratum: self.stratum_options()?, + allow_missing_blocks: self.args.flag_jsonrpc_allow_missing_blocks, + mode: mode, + tracing: tracing, + fat_db: fat_db, + compaction: compaction, + vm_type: vm_type, + warp_sync: warp_sync, + warp_barrier: self.args.arg_warp_barrier, + experimental_rpcs, + net_settings: self.network_settings()?, + secretstore_conf: secretstore_conf, + name: self.args.arg_identity, + custom_bootnodes: self.args.arg_bootnodes.is_some(), + check_seal: !self.args.flag_no_seal_check, + download_old_blocks: !self.args.flag_no_ancient_blocks, + verifier_settings: verifier_settings, + no_persistent_txqueue: self.args.flag_no_persistent_txqueue, + max_round_blocks_to_import: self.args.arg_max_round_blocks_to_import, + metrics_conf, + }; + Cmd::Run(run_cmd) + }; + + Ok(Execute { + logger: logger_config, + cmd: cmd, + }) + } + + fn vm_type(&self) -> Result { + Ok(VMType::Interpreter) + } + + fn miner_extras(&self) -> Result { + let floor = to_u256(&self.args.arg_gas_floor_target)?; + let ceil = to_u256(&self.args.arg_gas_cap)?; + let extras = MinerExtras { + author: self.author()?, + extra_data: self.extra_data()?, + gas_range_target: (floor, ceil), + engine_signer: self.engine_signer()?, + work_notify: self.work_notify(), + local_accounts: HashSet::from_iter( + to_addresses(&self.args.arg_tx_queue_locals)?.into_iter(), + ), + }; + + Ok(extras) + } + + fn author(&self) -> Result { + to_address(self.args.arg_author.clone()) + } + + fn engine_signer(&self) -> Result { + to_address(self.args.arg_engine_signer.clone()) + } + + fn format(&self) -> Result, String> { + match self + .args + .arg_import_format + .clone() + .or(self.args.arg_export_blocks_format.clone()) + .or(self.args.arg_export_state_format.clone()) + { + Some(ref f) => Ok(Some(f.parse()?)), + None => Ok(None), + } + } + + fn cache_config(&self) -> CacheConfig { + match self.args.arg_cache_size { + Some(size) => CacheConfig::new_with_total_cache_size(size), + None => CacheConfig::new( + self.args.arg_cache_size_db, + self.args.arg_cache_size_blocks, + self.args.arg_cache_size_queue, + self.args.arg_cache_size_state, + ), + } + } + + /// returns logger config + pub fn logger_config(&self) -> LogConfig { + LogConfig { + mode: self.args.arg_logging.clone(), + color: !self.args.flag_no_color && !cfg!(windows), + file: self + .args + .arg_log_file + .as_ref() + .map(|log_file| replace_home(&self.directories().base, log_file)), + } + } + + fn chain(&self) -> Result { + Ok(self.args.arg_chain.parse()?) + } + + fn is_dev_chain(&self) -> Result { + Ok(self.chain()? == SpecType::Dev) + } + + fn max_peers(&self) -> u32 { + self.args + .arg_max_peers + .or(cmp::max(self.args.arg_min_peers, Some(DEFAULT_MAX_PEERS))) + .unwrap_or(DEFAULT_MAX_PEERS) as u32 + } + + fn ip_filter(&self) -> Result { + match IpFilter::parse(self.args.arg_allow_ips.as_str()) { + Ok(allow_ip) => Ok(allow_ip), + Err(_) => Err("Invalid IP filter value".to_owned()), + } + } + + fn min_peers(&self) -> u32 { + self.args + .arg_min_peers + .or(cmp::min(self.args.arg_max_peers, Some(DEFAULT_MIN_PEERS))) + .unwrap_or(DEFAULT_MIN_PEERS) as u32 + } + + fn max_pending_peers(&self) -> u32 { + self.args.arg_max_pending_peers as u32 + } + + fn snapshot_peers(&self) -> u32 { + self.args.arg_snapshot_peers as u32 + } + + fn work_notify(&self) -> Vec { + self.args + .arg_notify_work + .as_ref() + .map_or_else(Vec::new, |s| s.split(',').map(|s| s.to_owned()).collect()) + } + + fn accounts_config(&self) -> Result { + let keys_iterations = NonZeroU32::new(self.args.arg_keys_iterations) + .ok_or_else(|| "--keys-iterations must be non-zero")?; + let cfg = AccountsConfig { + iterations: keys_iterations, + refresh_time: self.args.arg_accounts_refresh, + testnet: false, + password_files: self + .args + .arg_password + .iter() + .map(|s| replace_home(&self.directories().base, s)) + .collect(), + unlocked_accounts: to_addresses(&self.args.arg_unlock)?, + enable_fast_unlock: self.args.flag_fast_unlock, + }; + + Ok(cfg) + } + + fn stratum_options(&self) -> Result, String> { + if self.args.flag_stratum { + Ok(Some(stratum::Options { + io_path: self.directories().db, + listen_addr: self.stratum_interface(), + port: self.args.arg_ports_shift + self.args.arg_stratum_port, + secret: self + .args + .arg_stratum_secret + .as_ref() + .map(|s| s.parse::().unwrap_or_else(|_| keccak(s))), + })) + } else { + Ok(None) + } + } + + fn miner_options(&self) -> Result { + let is_dev_chain = self.is_dev_chain()?; + if is_dev_chain && self.args.flag_force_sealing && self.args.arg_reseal_min_period == 0 { + return Err("Force sealing can't be used with reseal_min_period = 0".into()); + } + + let reseal = self.args.arg_reseal_on_txs.parse::()?; + + let options = MinerOptions { + force_sealing: self.args.flag_force_sealing, + reseal_on_external_tx: reseal.external, + reseal_on_own_tx: reseal.own, + reseal_on_uncle: self.args.flag_reseal_on_uncle, + reseal_min_period: Duration::from_millis(self.args.arg_reseal_min_period), + reseal_max_period: Duration::from_millis(self.args.arg_reseal_max_period), + + pending_set: to_pending_set(&self.args.arg_relay_set)?, + work_queue_size: self.args.arg_work_queue_size, + enable_resubmission: !self.args.flag_remove_solved, + infinite_pending_block: self.args.flag_infinite_pending_block, + + tx_queue_penalization: to_queue_penalization(self.args.arg_tx_time_limit)?, + tx_queue_strategy: to_queue_strategy(&self.args.arg_tx_queue_strategy)?, + tx_queue_no_unfamiliar_locals: self.args.flag_tx_queue_no_unfamiliar_locals, + refuse_service_transactions: self.args.flag_refuse_service_transactions, + + pool_limits: self.pool_limits()?, + pool_verification_options: self.pool_verification_options()?, + }; + + Ok(options) + } + + fn pool_limits(&self) -> Result { + let max_count = self.args.arg_tx_queue_size; + + Ok(pool::Options { + max_count, + max_per_sender: self + .args + .arg_tx_queue_per_sender + .unwrap_or_else(|| cmp::max(16, max_count / 100)), + max_mem_usage: if self.args.arg_tx_queue_mem_limit > 0 { + self.args.arg_tx_queue_mem_limit as usize * 1024 * 1024 + } else { + usize::max_value() + }, + }) + } + + fn pool_verification_options(&self) -> Result { + Ok(pool::verifier::Options { + // NOTE min_gas_price and block_gas_limit will be overwritten right after start. + minimal_gas_price: U256::from(20_000_000) * 1_000u32, + block_gas_limit: U256::max_value(), + tx_gas_limit: match self.args.arg_tx_gas_limit { + Some(ref d) => to_u256(d)?, + None => U256::max_value(), + }, + no_early_reject: self.args.flag_tx_queue_no_early_reject, + }) + } + + fn secretstore_config(&self) -> Result { + Ok(SecretStoreConfiguration { + enabled: self.secretstore_enabled(), + http_enabled: self.secretstore_http_enabled(), + auto_migrate_enabled: self.secretstore_auto_migrate_enabled(), + acl_check_contract_address: self.secretstore_acl_check_contract_address()?, + service_contract_address: self.secretstore_service_contract_address()?, + service_contract_srv_gen_address: self + .secretstore_service_contract_srv_gen_address()?, + service_contract_srv_retr_address: self + .secretstore_service_contract_srv_retr_address()?, + service_contract_doc_store_address: self + .secretstore_service_contract_doc_store_address()?, + service_contract_doc_sretr_address: self + .secretstore_service_contract_doc_sretr_address()?, + self_secret: self.secretstore_self_secret()?, + nodes: self.secretstore_nodes()?, + key_server_set_contract_address: self.secretstore_key_server_set_contract_address()?, + interface: self.secretstore_interface(), + port: self.args.arg_ports_shift + self.args.arg_secretstore_port, + http_interface: self.secretstore_http_interface(), + http_port: self.args.arg_ports_shift + self.args.arg_secretstore_http_port, + data_path: self.directories().secretstore, + admin_public: self.secretstore_admin_public()?, + }) + } + + fn gas_pricer_config(&self) -> Result { + fn wei_per_gas(usd_per_tx: f32, usd_per_eth: f32) -> U256 { + let wei_per_usd: f32 = 1.0e18 / usd_per_eth; + let gas_per_tx: f32 = 21000.0; + let wei_per_gas: f32 = wei_per_usd * usd_per_tx / gas_per_tx; + U256::from_dec_str(&format!("{:.0}", wei_per_gas)).unwrap() + } + + if let Some(dec) = self.args.arg_min_gas_price { + return Ok(GasPricerConfig::Fixed(U256::from(dec))); + } else if self.chain()? != SpecType::Foundation { + return Ok(GasPricerConfig::Fixed(U256::zero())); + } + + let usd_per_tx = to_price(&self.args.arg_usd_per_tx)?; + + if "auto" == self.args.arg_usd_per_eth { + Ok(GasPricerConfig::Calibrated { + usd_per_tx: usd_per_tx, + recalibration_period: to_duration(self.args.arg_price_update_period.as_str())?, + api_endpoint: ETHERSCAN_ETH_PRICE_ENDPOINT.to_string(), + }) + } else if let Ok(usd_per_eth_parsed) = to_price(&self.args.arg_usd_per_eth) { + let wei_per_gas = wei_per_gas(usd_per_tx, usd_per_eth_parsed); + + info!( + "Using a fixed conversion rate of Ξ1 = {} ({} wei/gas)", + Colour::White + .bold() + .paint(format!("US${:.2}", usd_per_eth_parsed)), + Colour::Yellow.bold().paint(format!("{}", wei_per_gas)) + ); + + Ok(GasPricerConfig::Fixed(wei_per_gas)) + } else { + Ok(GasPricerConfig::Calibrated { + usd_per_tx: usd_per_tx, + recalibration_period: to_duration(self.args.arg_price_update_period.as_str())?, + api_endpoint: self.args.arg_usd_per_eth.clone(), + }) + } + } + + fn extra_data(&self) -> Result { + match &self.args.arg_extra_data { + Some(x) if x.len() <= 32 => Ok(x.as_bytes().to_owned()), + None => Ok(version_data()), + Some(_) => Err("Extra data must be at most 32 characters".into()), + } + } + + fn init_reserved_nodes(&self) -> Result, String> { + use std::fs::File; + + match self.args.arg_reserved_peers { + Some(ref path) => { + let path = replace_home(&self.directories().base, path); + + let mut buffer = String::new(); + let mut node_file = File::open(&path) + .map_err(|e| format!("Error opening reserved nodes file: {}", e))?; + node_file + .read_to_string(&mut buffer) + .map_err(|_| "Error reading reserved node file")?; + let lines = buffer + .lines() + .map(|s| s.trim().to_owned()) + .filter(|s| !s.is_empty() && !s.starts_with("#")) + .collect::>(); + + for line in &lines { + match validate_node_url(line).map(Into::into) { + None => continue, + Some(sync::ErrorKind::AddressResolve(_)) => { + return Err(format!( + "Failed to resolve hostname of a boot node: {}", + line + )) + } + Some(_) => { + return Err(format!( + "Invalid node address format given for a boot node: {}", + line + )) + } + } + } + + Ok(lines) + } + None => Ok(Vec::new()), + } + } + + fn net_addresses(&self) -> Result<(SocketAddr, Option), String> { + let port = self.args.arg_ports_shift + self.args.arg_port; + let listen_address = SocketAddr::new( + self.interface(&self.args.arg_interface).parse().unwrap(), + port, + ); + let public_address = if self.args.arg_nat.starts_with("extip:") { + let host = self.args.arg_nat[6..] + .split(':') + .next() + .expect("split has at least one part; qed"); + let host = format!("{}:{}", host, port); + match host.to_socket_addrs() { + Ok(mut addr_iter) => { + if let Some(addr) = addr_iter.next() { + Some(addr) + } else { + return Err(format!( + "Invalid host given with `--nat extip:{}`", + &self.args.arg_nat[6..] + )); + } + } + Err(_) => { + return Err(format!( + "Invalid host given with `--nat extip:{}`", + &self.args.arg_nat[6..] + )) + } + } + } else { + None + }; + Ok((listen_address, public_address)) + } + + fn net_config(&self) -> Result { + let mut ret = NetworkConfiguration::new(); + ret.nat_enabled = self.args.arg_nat == "any" || self.args.arg_nat == "upnp"; + ret.boot_nodes = to_bootnodes(&self.args.arg_bootnodes)?; + let (listen, public) = self.net_addresses()?; + ret.listen_address = Some(format!("{}", listen)); + ret.public_address = public.map(|p| format!("{}", p)); + ret.use_secret = match self.args.arg_node_key.as_ref().map(|s| { + s.parse::() + .or_else(|_| Secret::from_unsafe_slice(&keccak(s))) + .map_err(|e| format!("Invalid key: {:?}", e)) + }) { + None => None, + Some(Ok(key)) => Some(key), + Some(Err(err)) => return Err(err), + }; + ret.discovery_enabled = !self.args.flag_no_discovery; + ret.max_peers = self.max_peers(); + ret.min_peers = self.min_peers(); + ret.snapshot_peers = self.snapshot_peers(); + ret.ip_filter = self.ip_filter()?; + ret.max_pending_peers = self.max_pending_peers(); + let mut net_path = PathBuf::from(self.directories().base); + net_path.push("network"); + ret.config_path = Some(net_path.to_str().unwrap().to_owned()); + ret.reserved_nodes = self.init_reserved_nodes()?; + ret.allow_non_reserved = !self.args.flag_reserved_only; + ret.client_version = { + let mut client_version = version(); + if !self.args.arg_identity.is_empty() { + // Insert name after the "Parity-Ethereum/" at the beginning of version string. + let idx = client_version.find('/').unwrap_or(client_version.len()); + client_version.insert_str(idx, &format!("/{}", self.args.arg_identity)); + } + client_version + }; + Ok(ret) + } + + fn network_id(&self) -> Option { + self.args.arg_network_id + } + + fn rpc_apis(&self) -> String { + self.args.arg_jsonrpc_apis.clone() + } + + fn cors(cors: &str) -> Option> { + match cors { + "none" => return Some(Vec::new()), + "*" | "all" | "any" => return None, + _ => {} + } + + Some(cors.split(',').map(Into::into).collect()) + } + + fn rpc_cors(&self) -> Option> { + let cors = self.args.arg_jsonrpc_cors.to_owned(); + Self::cors(&cors) + } + + fn hosts(&self, hosts: &str, interface: &str) -> Option> { + if self.args.flag_unsafe_expose { + return None; + } + + if interface == "0.0.0.0" && hosts == "none" { + return None; + } + + Self::parse_hosts(hosts) + } + + fn parse_hosts(hosts: &str) -> Option> { + match hosts { + "none" => return Some(Vec::new()), + "*" | "all" | "any" => return None, + _ => {} + } + let hosts = hosts.split(',').map(Into::into).collect(); + Some(hosts) + } + + fn rpc_hosts(&self) -> Option> { + self.hosts(&self.args.arg_jsonrpc_hosts, &self.rpc_interface()) + } + + fn ws_hosts(&self) -> Option> { + self.hosts(&self.args.arg_ws_hosts, &self.ws_interface()) + } + + fn ws_origins(&self) -> Option> { + if self.args.flag_unsafe_expose { + return None; + } + + Self::parse_hosts(&self.args.arg_ws_origins) + } + + fn ipc_config(&self) -> Result { + let conf = IpcConfiguration { + enabled: !self.args.flag_no_ipc, + socket_addr: self.ipc_path(), + apis: self.args.arg_ipc_apis.parse()?, + }; + + Ok(conf) + } + + fn http_config(&self) -> Result { + let conf = HttpConfiguration { + enabled: self.rpc_enabled(), + interface: self.rpc_interface(), + port: self.args.arg_ports_shift + self.args.arg_jsonrpc_port, + apis: self.rpc_apis().parse()?, + hosts: self.rpc_hosts(), + cors: self.rpc_cors(), + server_threads: match self.args.arg_jsonrpc_server_threads { + Some(threads) if threads > 0 => threads, + _ => 1, + }, + processing_threads: self.args.arg_jsonrpc_threads, + max_payload: match self.args.arg_jsonrpc_max_payload { + Some(max) if max > 0 => max as usize, + _ => 5usize, + }, + keep_alive: !self.args.flag_jsonrpc_no_keep_alive, + }; + + Ok(conf) + } + + fn ws_config(&self) -> Result { + let support_token_api = // enabled when not unlocking self.args.arg_unlock.is_none(); - let conf = WsConfiguration { - enabled: self.ws_enabled(), - interface: self.ws_interface(), - port: self.args.arg_ports_shift + self.args.arg_ws_port, - apis: self.args.arg_ws_apis.parse()?, - hosts: self.ws_hosts(), - origins: self.ws_origins(), - signer_path: self.directories().signer.into(), - support_token_api, - max_connections: self.args.arg_ws_max_connections, - }; - - Ok(conf) - } - - fn private_provider_config(&self) -> Result<(ProviderConfig, EncryptorConfig, bool), String> { - let provider_conf = ProviderConfig { - validator_accounts: to_addresses(&self.args.arg_private_validators)?, - signer_account: self.args.arg_private_signer.clone().and_then(|account| to_address(Some(account)).ok()), - }; - - let encryptor_conf = EncryptorConfig { - base_url: self.args.arg_private_sstore_url.clone(), - threshold: self.args.arg_private_sstore_threshold.unwrap_or(0), - key_server_account: self.args.arg_private_account.clone().and_then(|account| to_address(Some(account)).ok()), - }; - - Ok((provider_conf, encryptor_conf, self.args.flag_private_enabled)) - } - - fn snapshot_config(&self) -> Result { - let conf = SnapshotConfiguration { - no_periodic: self.args.flag_no_periodic_snapshot, - processing_threads: match self.args.arg_snapshot_threads { - Some(threads) if threads > 0 => threads, - _ => ::std::cmp::max(1, num_cpus::get() / 2), - }, - }; - - Ok(conf) - } - - fn network_settings(&self) -> Result { - let http_conf = self.http_config()?; - let net_addresses = self.net_addresses()?; - Ok(NetworkSettings { - name: self.args.arg_identity.clone(), - chain: format!("{}", self.chain()?), - is_dev_chain: self.is_dev_chain()?, - network_port: net_addresses.0.port(), - rpc_enabled: http_conf.enabled, - rpc_interface: http_conf.interface, - rpc_port: http_conf.port, - }) - } - - fn update_policy(&self) -> Result { - Ok(UpdatePolicy { - enable_downloading: !self.args.flag_no_download, - require_consensus: !self.args.flag_no_consensus, - filter: match self.args.arg_auto_update.as_ref() { - "none" => UpdateFilter::None, - "critical" => UpdateFilter::Critical, - "all" => UpdateFilter::All, - _ => return Err("Invalid value for `--auto-update`. See `--help` for more information.".into()), - }, - track: match self.args.arg_release_track.as_ref() { - "stable" => ReleaseTrack::Stable, - "beta" => ReleaseTrack::Beta, - "nightly" => ReleaseTrack::Nightly, - "testing" => ReleaseTrack::Testing, - "current" => ReleaseTrack::Unknown, - _ => return Err("Invalid value for `--releases-track`. See `--help` for more information.".into()), - }, - path: default_hypervisor_path(), - max_size: 128 * 1024 * 1024, - max_delay: self.args.arg_auto_update_delay as u64, - frequency: self.args.arg_auto_update_check_frequency as u64, - }) - } - - fn directories(&self) -> Directories { - let local_path = default_local_path(); - let base_path = self.args.arg_base_path.as_ref().or_else(|| self.args.arg_datadir.as_ref()).map_or_else(|| default_data_path(), |s| s.clone()); - let data_path = replace_home("", &base_path); - let is_using_base_path = self.args.arg_base_path.is_some(); - // If base_path is set and db_path is not we default to base path subdir instead of LOCAL. - let base_db_path = if is_using_base_path && self.args.arg_db_path.is_none() { - if self.args.flag_light { - "$BASE/chains_light" - } else { - "$BASE/chains" - } - } else if self.args.flag_light { - self.args.arg_db_path.as_ref().map_or(dir::CHAINS_PATH_LIGHT, |s| &s) - } else { - self.args.arg_db_path.as_ref().map_or(dir::CHAINS_PATH, |s| &s) - }; - let cache_path = if is_using_base_path { "$BASE/cache" } else { dir::CACHE_PATH }; - - let db_path = replace_home_and_local(&data_path, &local_path, &base_db_path); - let cache_path = replace_home_and_local(&data_path, &local_path, cache_path); - let keys_path = replace_home(&data_path, &self.args.arg_keys_path); - let secretstore_path = replace_home(&data_path, &self.args.arg_secretstore_path); - let ui_path = replace_home(&data_path, &self.args.arg_ui_path); - - Directories { - keys: keys_path, - base: data_path, - cache: cache_path, - db: db_path, - signer: ui_path, - secretstore: secretstore_path, - } - } - - fn ipc_path(&self) -> String { - if self.args.flag_geth { - geth_ipc_path(self.args.flag_testnet) - } else { - parity_ipc_path( - &self.directories().base, - &self.args.arg_ipcpath.clone().unwrap_or(self.args.arg_ipc_path.clone()), - self.args.arg_ports_shift, - ) - } - } - - fn interface(&self, interface: &str) -> String { - if self.args.flag_unsafe_expose { - return "0.0.0.0".into(); - } - - match interface { - "all" => "0.0.0.0", - "local" => "127.0.0.1", - x => x, - }.into() - } - - fn rpc_interface(&self) -> String { - let rpc_interface = self.args.arg_rpcaddr.clone().unwrap_or(self.args.arg_jsonrpc_interface.clone()); - self.interface(&rpc_interface) - } - - fn ws_interface(&self) -> String { - self.interface(&self.args.arg_ws_interface) - } - - fn ipfs_interface(&self) -> String { - self.interface(&self.args.arg_ipfs_api_interface) - } - - fn secretstore_interface(&self) -> String { - self.interface(&self.args.arg_secretstore_interface) - } - - fn secretstore_http_interface(&self) -> String { - self.interface(&self.args.arg_secretstore_http_interface) - } - - fn secretstore_self_secret(&self) -> Result, String> { - match self.args.arg_secretstore_secret { + let conf = WsConfiguration { + enabled: self.ws_enabled(), + interface: self.ws_interface(), + port: self.args.arg_ports_shift + self.args.arg_ws_port, + apis: self.args.arg_ws_apis.parse()?, + hosts: self.ws_hosts(), + origins: self.ws_origins(), + signer_path: self.directories().signer.into(), + support_token_api, + max_connections: self.args.arg_ws_max_connections, + }; + + Ok(conf) + } + + fn metrics_config(&self) -> Result { + let conf = MetricsConfiguration { + enabled: self.metrics_enabled(), + interface: self.metrics_interface(), + port: self.args.arg_ports_shift + self.args.arg_metrics_port, + }; + Ok(conf) + } + + fn snapshot_config(&self) -> Result { + let conf = SnapshotConfiguration { + enable: self.args.flag_enable_snapshotting, + processing_threads: match self.args.arg_snapshot_threads { + Some(threads) if threads > 0 => threads, + _ => ::std::cmp::max(1, num_cpus::get_physical() / 2), + }, + }; + + Ok(conf) + } + + fn network_settings(&self) -> Result { + let http_conf = self.http_config()?; + let net_addresses = self.net_addresses()?; + Ok(NetworkSettings { + name: self.args.arg_identity.clone(), + chain: format!("{}", self.chain()?), + is_dev_chain: self.is_dev_chain()?, + network_port: net_addresses.0.port(), + rpc_enabled: http_conf.enabled, + rpc_interface: http_conf.interface, + rpc_port: http_conf.port, + }) + } + + fn directories(&self) -> Directories { + let local_path = default_local_path(); + let base_path = self + .args + .arg_base_path + .as_ref() + .map_or_else(|| default_data_path(), |s| s.clone()); + let data_path = replace_home("", &base_path); + let is_using_base_path = self.args.arg_base_path.is_some(); + // If base_path is set and db_path is not we default to base path subdir instead of LOCAL. + let base_db_path = if is_using_base_path && self.args.arg_db_path.is_none() { + "$BASE/chains" + } else { + self.args + .arg_db_path + .as_ref() + .map_or(dir::CHAINS_PATH, |s| &s) + }; + let cache_path = if is_using_base_path { + "$BASE/cache" + } else { + dir::CACHE_PATH + }; + + let db_path = replace_home_and_local(&data_path, &local_path, &base_db_path); + let cache_path = replace_home_and_local(&data_path, &local_path, cache_path); + let keys_path = replace_home(&data_path, &self.args.arg_keys_path); + let secretstore_path = replace_home(&data_path, &self.args.arg_secretstore_path); + let ui_path = replace_home(&data_path, &self.args.arg_ui_path); + + Directories { + keys: keys_path, + base: data_path, + cache: cache_path, + db: db_path, + signer: ui_path, + secretstore: secretstore_path, + } + } + + fn ipc_path(&self) -> String { + parity_ipc_path( + &self.directories().base, + &self.args.arg_ipc_path, + self.args.arg_ports_shift, + ) + } + + fn interface(&self, interface: &str) -> String { + if self.args.flag_unsafe_expose { + return "0.0.0.0".into(); + } + + match interface { + "all" => "0.0.0.0", + "local" => "127.0.0.1", + x => x, + } + .into() + } + + fn rpc_interface(&self) -> String { + self.interface(&self.args.arg_jsonrpc_interface) + } + + fn ws_interface(&self) -> String { + self.interface(&self.args.arg_ws_interface) + } + + fn metrics_interface(&self) -> String { + self.interface(&self.args.arg_metrics_interface) + } + + fn secretstore_interface(&self) -> String { + self.interface(&self.args.arg_secretstore_interface) + } + + fn secretstore_http_interface(&self) -> String { + self.interface(&self.args.arg_secretstore_http_interface) + } + + fn secretstore_self_secret(&self) -> Result, String> { + match self.args.arg_secretstore_secret { Some(ref s) if s.len() == 64 => Ok(Some(NodeSecretKey::Plain(s.parse() .map_err(|e| format!("Invalid secret store secret: {}. Error: {:?}", s, e))?))), #[cfg(feature = "accounts")] @@ -1068,905 +1082,948 @@ impl Configuration { Some(_) => Err(format!("Invalid secret store secret. Must be either existing account address, or hex-encoded private key")), None => Ok(None), } - } - - fn secretstore_admin_public(&self) -> Result, String> { - match self.args.arg_secretstore_admin_public.as_ref() { - Some(admin_public) => Ok(Some(admin_public.parse().map_err(|e| format!("Invalid secret store admin public: {}", e))?)), - None => Ok(None), - } - } - - fn secretstore_nodes(&self) -> Result, String> { - let mut nodes = BTreeMap::new(); - for node in self.args.arg_secretstore_nodes.split(',').filter(|n| n != &"") { - let public_and_addr: Vec<_> = node.split('@').collect(); - if public_and_addr.len() != 2 { - return Err(format!("Invalid secret store node: {}", node)); - } - - let ip_and_port: Vec<_> = public_and_addr[1].split(':').collect(); - if ip_and_port.len() != 2 { - return Err(format!("Invalid secret store node: {}", node)); - } - - let public = public_and_addr[0].parse() - .map_err(|e| format!("Invalid public key in secret store node: {}. Error: {:?}", public_and_addr[0], e))?; - let port = ip_and_port[1].parse() - .map_err(|e| format!("Invalid port in secret store node: {}. Error: {:?}", ip_and_port[1], e))?; - - nodes.insert(public, (ip_and_port[0].into(), port)); - } - - Ok(nodes) - } - - fn stratum_interface(&self) -> String { - self.interface(&self.args.arg_stratum_interface) - } - - fn rpc_enabled(&self) -> bool { - !self.args.flag_jsonrpc_off && !self.args.flag_no_jsonrpc - } - - fn ws_enabled(&self) -> bool { - !self.args.flag_no_ws - } - - fn secretstore_enabled(&self) -> bool { - !self.args.flag_no_secretstore && cfg!(feature = "secretstore") - } - - fn secretstore_http_enabled(&self) -> bool { - !self.args.flag_no_secretstore_http && cfg!(feature = "secretstore") - } - - fn secretstore_auto_migrate_enabled(&self) -> bool { - !self.args.flag_no_secretstore_auto_migrate - } - - fn secretstore_acl_check_contract_address(&self) -> Result, String> { - into_secretstore_service_contract_address(self.args.arg_secretstore_acl_contract.as_ref()) - } - - fn secretstore_service_contract_address(&self) -> Result, String> { - into_secretstore_service_contract_address(self.args.arg_secretstore_contract.as_ref()) - } - - fn secretstore_service_contract_srv_gen_address(&self) -> Result, String> { - into_secretstore_service_contract_address(self.args.arg_secretstore_srv_gen_contract.as_ref()) - } - - fn secretstore_service_contract_srv_retr_address(&self) -> Result, String> { - into_secretstore_service_contract_address(self.args.arg_secretstore_srv_retr_contract.as_ref()) - } - - fn secretstore_service_contract_doc_store_address(&self) -> Result, String> { - into_secretstore_service_contract_address(self.args.arg_secretstore_doc_store_contract.as_ref()) - } - - fn secretstore_service_contract_doc_sretr_address(&self) -> Result, String> { - into_secretstore_service_contract_address(self.args.arg_secretstore_doc_sretr_contract.as_ref()) - } - - fn secretstore_key_server_set_contract_address(&self) -> Result, String> { - into_secretstore_service_contract_address(self.args.arg_secretstore_server_set_contract.as_ref()) - } - - fn verifier_settings(&self) -> VerifierSettings { - let mut settings = VerifierSettings::default(); - settings.scale_verifiers = self.args.flag_scale_verifiers; - if let Some(num_verifiers) = self.args.arg_num_verifiers { - settings.num_verifiers = num_verifiers; - } - - settings - } - - fn whisper_config(&self) -> ::whisper::Config { - ::whisper::Config { - enabled: self.args.flag_whisper, - target_message_pool_size: self.args.arg_whisper_pool_size * 1024 * 1024, - } - } + } + + fn secretstore_admin_public(&self) -> Result, String> { + match self.args.arg_secretstore_admin_public.as_ref() { + Some(admin_public) => { + Ok(Some(admin_public.parse().map_err(|e| { + format!("Invalid secret store admin public: {}", e) + })?)) + } + None => Ok(None), + } + } + + fn secretstore_nodes(&self) -> Result, String> { + let mut nodes = BTreeMap::new(); + for node in self + .args + .arg_secretstore_nodes + .split(',') + .filter(|n| n != &"") + { + let public_and_addr: Vec<_> = node.split('@').collect(); + if public_and_addr.len() != 2 { + return Err(format!("Invalid secret store node: {}", node)); + } + + let ip_and_port: Vec<_> = public_and_addr[1].split(':').collect(); + if ip_and_port.len() != 2 { + return Err(format!("Invalid secret store node: {}", node)); + } + + let public = public_and_addr[0].parse().map_err(|e| { + format!( + "Invalid public key in secret store node: {}. Error: {:?}", + public_and_addr[0], e + ) + })?; + let port = ip_and_port[1].parse().map_err(|e| { + format!( + "Invalid port in secret store node: {}. Error: {:?}", + ip_and_port[1], e + ) + })?; + + nodes.insert(public, (ip_and_port[0].into(), port)); + } + + Ok(nodes) + } + + fn stratum_interface(&self) -> String { + self.interface(&self.args.arg_stratum_interface) + } + + fn rpc_enabled(&self) -> bool { + !self.args.flag_no_jsonrpc + } + + fn ws_enabled(&self) -> bool { + !self.args.flag_no_ws + } + + fn metrics_enabled(&self) -> bool { + self.args.flag_metrics + } + + fn secretstore_enabled(&self) -> bool { + !self.args.flag_no_secretstore && cfg!(feature = "secretstore") + } + + fn secretstore_http_enabled(&self) -> bool { + !self.args.flag_no_secretstore_http && cfg!(feature = "secretstore") + } + + fn secretstore_auto_migrate_enabled(&self) -> bool { + !self.args.flag_no_secretstore_auto_migrate + } + + fn secretstore_acl_check_contract_address( + &self, + ) -> Result, String> { + into_secretstore_service_contract_address(self.args.arg_secretstore_acl_contract.as_ref()) + } + + fn secretstore_service_contract_address( + &self, + ) -> Result, String> { + into_secretstore_service_contract_address(self.args.arg_secretstore_contract.as_ref()) + } + + fn secretstore_service_contract_srv_gen_address( + &self, + ) -> Result, String> { + into_secretstore_service_contract_address( + self.args.arg_secretstore_srv_gen_contract.as_ref(), + ) + } + + fn secretstore_service_contract_srv_retr_address( + &self, + ) -> Result, String> { + into_secretstore_service_contract_address( + self.args.arg_secretstore_srv_retr_contract.as_ref(), + ) + } + + fn secretstore_service_contract_doc_store_address( + &self, + ) -> Result, String> { + into_secretstore_service_contract_address( + self.args.arg_secretstore_doc_store_contract.as_ref(), + ) + } + + fn secretstore_service_contract_doc_sretr_address( + &self, + ) -> Result, String> { + into_secretstore_service_contract_address( + self.args.arg_secretstore_doc_sretr_contract.as_ref(), + ) + } + + fn secretstore_key_server_set_contract_address( + &self, + ) -> Result, String> { + into_secretstore_service_contract_address( + self.args.arg_secretstore_server_set_contract.as_ref(), + ) + } + + fn verifier_settings(&self) -> VerifierSettings { + let mut settings = VerifierSettings::default(); + settings.scale_verifiers = self.args.flag_scale_verifiers; + if let Some(num_verifiers) = self.args.arg_num_verifiers { + settings.num_verifiers = num_verifiers; + } + + settings + } } -fn into_secretstore_service_contract_address(s: Option<&String>) -> Result, String> { - match s.map(String::as_str) { - None | Some("none") => Ok(None), - Some("registry") => Ok(Some(SecretStoreContractAddress::Registry)), - Some(a) => Ok(Some(SecretStoreContractAddress::Address(a.parse().map_err(|e| format!("{}", e))?))), - } +fn into_secretstore_service_contract_address( + s: Option<&String>, +) -> Result, String> { + match s.map(String::as_str) { + None | Some("none") => Ok(None), + Some("registry") => Ok(Some(SecretStoreContractAddress::Registry)), + Some(a) => Ok(Some(SecretStoreContractAddress::Address( + a.parse().map_err(|e| format!("{}", e))?, + ))), + } } #[cfg(test)] mod tests { - use std::io::Write; - use std::fs::File; - use std::str::FromStr; - - use tempdir::TempDir; - use ethcore::client::{VMType, BlockId}; - use ethcore::miner::MinerOptions; - use miner::pool::PrioritizationStrategy; - use parity_rpc::NetworkSettings; - use updater::{UpdatePolicy, UpdateFilter, ReleaseTrack}; - - use account::{AccountCmd, NewAccount, ImportAccounts, ListAccounts}; - use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, DataFormat, ExportState}; - use cli::Args; - use dir::{Directories, default_hypervisor_path}; - use helpers::{default_network_config}; - use params::SpecType; - use presale::ImportWallet; - use rpc::WsConfiguration; - use rpc_apis::ApiSet; - use run::RunCmd; - - use network::{AllowIP, IpFilter}; - - extern crate ipnetwork; - use self::ipnetwork::IpNetwork; - - use super::*; - - lazy_static! { - static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed"); - } - - #[derive(Debug, PartialEq)] - struct TestPasswordReader(&'static str); - - fn parse(args: &[&str]) -> Configuration { - Configuration { - args: Args::parse_without_config(args).unwrap(), - } - } - - #[test] - fn test_command_version() { - let args = vec!["parity", "--version"]; - let conf = parse(&args); - assert_eq!(conf.into_command().unwrap().cmd, Cmd::Version); - } - - #[test] - fn test_command_account_new() { - let args = vec!["parity", "account", "new"]; - let conf = parse(&args); - assert_eq!(conf.into_command().unwrap().cmd, Cmd::Account(AccountCmd::New(NewAccount { - iterations: *ITERATIONS, - path: Directories::default().keys, - password_file: None, - spec: SpecType::default(), - }))); - } - - #[test] - fn test_command_account_list() { - let args = vec!["parity", "account", "list"]; - let conf = parse(&args); - assert_eq!(conf.into_command().unwrap().cmd, Cmd::Account( - AccountCmd::List(ListAccounts { - path: Directories::default().keys, - spec: SpecType::default(), - }) - )); - } - - #[test] - fn test_command_account_import() { - let args = vec!["parity", "account", "import", "my_dir", "another_dir"]; - let conf = parse(&args); - assert_eq!(conf.into_command().unwrap().cmd, Cmd::Account(AccountCmd::Import(ImportAccounts { - from: vec!["my_dir".into(), "another_dir".into()], - to: Directories::default().keys, - spec: SpecType::default(), - }))); - } - - #[test] - fn test_command_wallet_import() { - let args = vec!["parity", "wallet", "import", "my_wallet.json", "--password", "pwd"]; - let conf = parse(&args); - assert_eq!(conf.into_command().unwrap().cmd, Cmd::ImportPresaleWallet(ImportWallet { - iterations: *ITERATIONS, - path: Directories::default().keys, - wallet_path: "my_wallet.json".into(), - password_file: Some("pwd".into()), - spec: SpecType::default(), - })); - } - - #[test] - fn test_command_blockchain_import() { - let args = vec!["parity", "import", "blockchain.json"]; - let conf = parse(&args); - assert_eq!(conf.into_command().unwrap().cmd, Cmd::Blockchain(BlockchainCmd::Import(ImportBlockchain { - spec: Default::default(), - cache_config: Default::default(), - dirs: Default::default(), - file_path: Some("blockchain.json".into()), - format: Default::default(), - pruning: Default::default(), - pruning_history: 64, - pruning_memory: 32, - compaction: Default::default(), - tracing: Default::default(), - fat_db: Default::default(), - vm_type: VMType::Interpreter, - check_seal: true, - with_color: !cfg!(windows), - verifier_settings: Default::default(), - light: false, - max_round_blocks_to_import: 12, - }))); - } - - #[test] - fn test_command_blockchain_export() { - let args = vec!["parity", "export", "blocks", "blockchain.json"]; - let conf = parse(&args); - assert_eq!(conf.into_command().unwrap().cmd, Cmd::Blockchain(BlockchainCmd::Export(ExportBlockchain { - spec: Default::default(), - cache_config: Default::default(), - dirs: Default::default(), - file_path: Some("blockchain.json".into()), - pruning: Default::default(), - pruning_history: 64, - pruning_memory: 32, - format: Default::default(), - compaction: Default::default(), - tracing: Default::default(), - fat_db: Default::default(), - from_block: BlockId::Number(1), - to_block: BlockId::Latest, - check_seal: true, - max_round_blocks_to_import: 12, - }))); - } - - #[test] - fn test_command_state_export() { - let args = vec!["parity", "export", "state", "state.json"]; - let conf = parse(&args); - assert_eq!(conf.into_command().unwrap().cmd, Cmd::Blockchain(BlockchainCmd::ExportState(ExportState { - spec: Default::default(), - cache_config: Default::default(), - dirs: Default::default(), - file_path: Some("state.json".into()), - pruning: Default::default(), - pruning_history: 64, - pruning_memory: 32, - format: Default::default(), - compaction: Default::default(), - tracing: Default::default(), - fat_db: Default::default(), - at: BlockId::Latest, - storage: true, - code: true, - min_balance: None, - max_balance: None, - max_round_blocks_to_import: 12, - }))); - } - - #[test] - fn test_command_blockchain_export_with_custom_format() { - let args = vec!["parity", "export", "blocks", "--format", "hex", "blockchain.json"]; - let conf = parse(&args); - assert_eq!(conf.into_command().unwrap().cmd, Cmd::Blockchain(BlockchainCmd::Export(ExportBlockchain { - spec: Default::default(), - cache_config: Default::default(), - dirs: Default::default(), - file_path: Some("blockchain.json".into()), - pruning: Default::default(), - pruning_history: 64, - pruning_memory: 32, - format: Some(DataFormat::Hex), - compaction: Default::default(), - tracing: Default::default(), - fat_db: Default::default(), - from_block: BlockId::Number(1), - to_block: BlockId::Latest, - check_seal: true, - max_round_blocks_to_import: 12, - }))); - } - - #[test] - fn test_command_signer_new_token() { - let args = vec!["parity", "signer", "new-token"]; - let conf = parse(&args); - let expected = Directories::default().signer; - assert_eq!(conf.into_command().unwrap().cmd, Cmd::SignerToken(WsConfiguration { - enabled: true, - interface: "127.0.0.1".into(), - port: 8546, - apis: ApiSet::UnsafeContext, - origins: Some(vec!["parity://*".into(),"chrome-extension://*".into(), "moz-extension://*".into()]), - hosts: Some(vec![]), - signer_path: expected.into(), - support_token_api: true, - max_connections: 100, - }, LogConfig { - color: !cfg!(windows), - mode: None, - file: None, - } )); - } - - #[test] - fn test_ws_max_connections() { - let args = vec!["parity", "--ws-max-connections", "1"]; - let conf = parse(&args); - - assert_eq!(conf.ws_config().unwrap(), WsConfiguration { - max_connections: 1, - ..Default::default() - }); - } - - #[test] - fn test_run_cmd() { - let args = vec!["parity"]; - let conf = parse(&args); - let mut expected = RunCmd { - allow_missing_blocks: false, - cache_config: Default::default(), - dirs: Default::default(), - spec: Default::default(), - pruning: Default::default(), - pruning_history: 64, - pruning_memory: 32, - daemon: None, - logger_config: Default::default(), - miner_options: Default::default(), - gas_price_percentile: 50, - poll_lifetime: 60, - ws_conf: Default::default(), - http_conf: Default::default(), - ipc_conf: Default::default(), - net_conf: default_network_config(), - network_id: None, - warp_sync: true, - warp_barrier: None, - acc_conf: Default::default(), - gas_pricer_conf: Default::default(), - miner_extras: Default::default(), - update_policy: UpdatePolicy { - enable_downloading: true, - require_consensus: true, - filter: UpdateFilter::Critical, - track: ReleaseTrack::Unknown, - path: default_hypervisor_path(), - max_size: 128 * 1024 * 1024, - max_delay: 100, - frequency: 20, - }, - mode: Default::default(), - tracing: Default::default(), - compaction: Default::default(), - vm_type: Default::default(), - geth_compatibility: false, - experimental_rpcs: false, - net_settings: Default::default(), - ipfs_conf: Default::default(), - secretstore_conf: Default::default(), - private_provider_conf: Default::default(), - private_encryptor_conf: Default::default(), - private_tx_enabled: false, - name: "".into(), - custom_bootnodes: false, - fat_db: Default::default(), - snapshot_conf: Default::default(), - stratum: None, - check_seal: true, - download_old_blocks: true, - verifier_settings: Default::default(), - serve_light: true, - light: false, - no_hardcoded_sync: false, - no_persistent_txqueue: false, - whisper: Default::default(), - max_round_blocks_to_import: 12, - on_demand_response_time_window: None, - on_demand_request_backoff_start: None, - on_demand_request_backoff_max: None, - on_demand_request_backoff_rounds_max: None, - on_demand_request_consecutive_failures: None, - }; - expected.secretstore_conf.enabled = cfg!(feature = "secretstore"); - expected.secretstore_conf.http_enabled = cfg!(feature = "secretstore"); - assert_eq!(conf.into_command().unwrap().cmd, Cmd::Run(expected)); - } - - #[test] - fn should_parse_mining_options() { - // given - let mut mining_options = MinerOptions::default(); - - // when - let conf0 = parse(&["parity"]); - let conf2 = parse(&["parity", "--tx-queue-strategy", "gas_price"]); - - // then - assert_eq!(conf0.miner_options().unwrap(), mining_options); - mining_options.tx_queue_strategy = PrioritizationStrategy::GasPriceOnly; - assert_eq!(conf2.miner_options().unwrap(), mining_options); - } - - #[test] - fn should_fail_on_force_reseal_and_reseal_min_period() { - let conf = parse(&["parity", "--chain", "dev", "--force-sealing", "--reseal-min-period", "0"]); - - assert!(conf.miner_options().is_err()); - } - - #[test] - fn should_parse_updater_options() { - // when - let conf0 = parse(&["parity", "--release-track=testing"]); - let conf1 = parse(&["parity", "--auto-update", "all", "--no-consensus", "--auto-update-delay", "300"]); - let conf2 = parse(&["parity", "--no-download", "--auto-update=all", "--release-track=beta", "--auto-update-delay=300", "--auto-update-check-frequency=100"]); - let conf3 = parse(&["parity", "--auto-update=xxx"]); - - // then - assert_eq!(conf0.update_policy().unwrap(), UpdatePolicy { - enable_downloading: true, - require_consensus: true, - filter: UpdateFilter::Critical, - track: ReleaseTrack::Testing, - path: default_hypervisor_path(), - max_size: 128 * 1024 * 1024, - max_delay: 100, - frequency: 20, - }); - assert_eq!(conf1.update_policy().unwrap(), UpdatePolicy { - enable_downloading: true, - require_consensus: false, - filter: UpdateFilter::All, - track: ReleaseTrack::Unknown, - path: default_hypervisor_path(), - max_size: 128 * 1024 * 1024, - max_delay: 300, - frequency: 20, - }); - assert_eq!(conf2.update_policy().unwrap(), UpdatePolicy { - enable_downloading: false, - require_consensus: true, - filter: UpdateFilter::All, - track: ReleaseTrack::Beta, - path: default_hypervisor_path(), - max_size: 128 * 1024 * 1024, - max_delay: 300, - frequency: 100, - }); - assert!(conf3.update_policy().is_err()); - } - - #[test] - fn should_parse_network_settings() { - // given - - // when - let conf = parse(&["parity", "--testnet", "--identity", "testname"]); - - // then - assert_eq!(conf.network_settings(), Ok(NetworkSettings { - name: "testname".to_owned(), - chain: "kovan".to_owned(), - is_dev_chain: false, - network_port: 30303, - rpc_enabled: true, - rpc_interface: "127.0.0.1".to_owned(), - rpc_port: 8545, - })); - } - - #[test] - fn should_parse_rpc_settings_with_geth_compatiblity() { - // given - fn assert(conf: Configuration) { - let net = conf.network_settings().unwrap(); - assert_eq!(net.rpc_enabled, true); - assert_eq!(net.rpc_interface, "0.0.0.0".to_owned()); - assert_eq!(net.rpc_port, 8000); - assert_eq!(conf.rpc_cors(), None); - assert_eq!(conf.rpc_apis(), "web3,eth".to_owned()); - } - - // when - let conf1 = parse(&["parity", "-j", - "--jsonrpc-port", "8000", - "--jsonrpc-interface", "all", - "--jsonrpc-cors", "*", - "--jsonrpc-apis", "web3,eth" - ]); - let conf2 = parse(&["parity", "--rpc", - "--rpcport", "8000", - "--rpcaddr", "all", - "--rpccorsdomain", "*", - "--rpcapi", "web3,eth" - ]); - - // then - assert(conf1); - assert(conf2); - } - - #[test] - fn should_parse_rpc_hosts() { - // given - - // when - let conf0 = parse(&["parity"]); - let conf1 = parse(&["parity", "--jsonrpc-hosts", "none"]); - let conf2 = parse(&["parity", "--jsonrpc-hosts", "all"]); - let conf3 = parse(&["parity", "--jsonrpc-hosts", "parity.io,something.io"]); - - // then - assert_eq!(conf0.rpc_hosts(), Some(Vec::new())); - assert_eq!(conf1.rpc_hosts(), Some(Vec::new())); - assert_eq!(conf2.rpc_hosts(), None); - assert_eq!(conf3.rpc_hosts(), Some(vec!["parity.io".into(), "something.io".into()])); - } - - #[test] - fn should_parse_ipfs_hosts() { - // given - - // when - let conf0 = parse(&["parity"]); - let conf1 = parse(&["parity", "--ipfs-api-hosts", "none"]); - let conf2 = parse(&["parity", "--ipfs-api-hosts", "all"]); - let conf3 = parse(&["parity", "--ipfs-api-hosts", "parity.io,something.io"]); - - // then - assert_eq!(conf0.ipfs_hosts(), Some(Vec::new())); - assert_eq!(conf1.ipfs_hosts(), Some(Vec::new())); - assert_eq!(conf2.ipfs_hosts(), None); - assert_eq!(conf3.ipfs_hosts(), Some(vec!["parity.io".into(), "something.io".into()])); - } - - #[test] - fn should_parse_ipfs_cors() { - // given - - // when - let conf0 = parse(&["parity"]); - let conf1 = parse(&["parity", "--ipfs-api-cors", "*"]); - let conf2 = parse(&["parity", "--ipfs-api-cors", "http://parity.io,http://something.io"]); - - // then - assert_eq!(conf0.ipfs_cors(), Some(vec![])); - assert_eq!(conf1.ipfs_cors(), None); - assert_eq!(conf2.ipfs_cors(), Some(vec!["http://parity.io".into(),"http://something.io".into()])); - } - - #[test] - fn should_parse_ui_configuration() { - // given - - // when - let conf0 = parse(&["parity", "--ui-path=signer"]); - let conf1 = parse(&["parity", "--ui-path=signer", "--ui-no-validation"]); - let conf2 = parse(&["parity", "--ui-path=signer", "--ui-port", "3123"]); - let conf3 = parse(&["parity", "--ui-path=signer", "--ui-interface", "test"]); - let conf4 = parse(&["parity", "--ui-path=signer", "--force-ui"]); - - // then - assert_eq!(conf0.directories().signer, "signer".to_owned()); - - assert!(conf1.ws_config().unwrap().hosts.is_some()); - assert_eq!(conf1.ws_config().unwrap().origins, Some(vec!["parity://*".into(), "chrome-extension://*".into(), "moz-extension://*".into()])); - assert_eq!(conf1.directories().signer, "signer".to_owned()); - - assert!(conf2.ws_config().unwrap().hosts.is_some()); - assert_eq!(conf2.directories().signer, "signer".to_owned()); - - assert!(conf3.ws_config().unwrap().hosts.is_some()); - assert_eq!(conf3.directories().signer, "signer".to_owned()); - - assert!(conf4.ws_config().unwrap().hosts.is_some()); - assert_eq!(conf4.directories().signer, "signer".to_owned()); - } - - #[test] - fn should_not_bail_on_empty_line_in_reserved_peers() { - let tempdir = TempDir::new("").unwrap(); - let filename = tempdir.path().join("peers"); - File::create(&filename).unwrap().write_all(b" \n\t\n").unwrap(); - let args = vec!["parity", "--reserved-peers", filename.to_str().unwrap()]; - let conf = Configuration::parse_cli(&args).unwrap(); - assert!(conf.init_reserved_nodes().is_ok()); - } - - #[test] - fn should_ignore_comments_in_reserved_peers() { - let tempdir = TempDir::new("").unwrap(); - let filename = tempdir.path().join("peers_comments"); - File::create(&filename).unwrap().write_all(b"# Sample comment\nenode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@172.0.0.1:30303\n").unwrap(); - let args = vec!["parity", "--reserved-peers", filename.to_str().unwrap()]; - let conf = Configuration::parse_cli(&args).unwrap(); - let reserved_nodes = conf.init_reserved_nodes(); - assert!(reserved_nodes.is_ok()); - assert_eq!(reserved_nodes.unwrap().len(), 1); - } - - #[test] - fn test_dev_preset() { - let args = vec!["parity", "--config", "dev"]; - let conf = Configuration::parse_cli(&args).unwrap(); - match conf.into_command().unwrap().cmd { - Cmd::Run(c) => { - assert_eq!(c.net_settings.chain, "dev"); - assert_eq!(c.gas_pricer_conf, GasPricerConfig::Fixed(0.into())); - assert_eq!(c.miner_options.reseal_min_period, Duration::from_millis(0)); - }, - _ => panic!("Should be Cmd::Run"), - } - } - - #[test] - fn test_mining_preset() { - let args = vec!["parity", "--config", "mining"]; - let conf = Configuration::parse_cli(&args).unwrap(); - match conf.into_command().unwrap().cmd { - Cmd::Run(c) => { - assert_eq!(c.net_conf.min_peers, 50); - assert_eq!(c.net_conf.max_peers, 100); - assert_eq!(c.ipc_conf.enabled, false); - assert_eq!(c.miner_options.force_sealing, true); - assert_eq!(c.miner_options.reseal_on_external_tx, true); - assert_eq!(c.miner_options.reseal_on_own_tx, true); - assert_eq!(c.miner_options.reseal_min_period, Duration::from_millis(4000)); - assert_eq!(c.miner_options.pool_limits.max_count, 8192); - assert_eq!(c.cache_config, CacheConfig::new_with_total_cache_size(1024)); - assert_eq!(c.logger_config.mode.unwrap(), "miner=trace,own_tx=trace"); - }, - _ => panic!("Should be Cmd::Run"), - } - } - - #[test] - fn test_non_standard_ports_preset() { - let args = vec!["parity", "--config", "non-standard-ports"]; - let conf = Configuration::parse_cli(&args).unwrap(); - match conf.into_command().unwrap().cmd { - Cmd::Run(c) => { - assert_eq!(c.net_settings.network_port, 30305); - assert_eq!(c.net_settings.rpc_port, 8645); - }, - _ => panic!("Should be Cmd::Run"), - } - } - - #[test] - fn test_insecure_preset() { - let args = vec!["parity", "--config", "insecure"]; - let conf = Configuration::parse_cli(&args).unwrap(); - match conf.into_command().unwrap().cmd { - Cmd::Run(c) => { - assert_eq!(c.update_policy.require_consensus, false); - assert_eq!(c.net_settings.rpc_interface, "0.0.0.0"); - match c.http_conf.apis { - ApiSet::List(set) => assert_eq!(set, ApiSet::All.list_apis()), - _ => panic!("Incorrect rpc apis"), - } - // "web3,eth,net,personal,parity,parity_set,traces,rpc,parity_accounts"); - assert_eq!(c.http_conf.hosts, None); - assert_eq!(c.ipfs_conf.hosts, None); - }, - _ => panic!("Should be Cmd::Run"), - } - } - - #[test] - fn test_dev_insecure_preset() { - let args = vec!["parity", "--config", "dev-insecure"]; - let conf = Configuration::parse_cli(&args).unwrap(); - match conf.into_command().unwrap().cmd { - Cmd::Run(c) => { - assert_eq!(c.net_settings.chain, "dev"); - assert_eq!(c.gas_pricer_conf, GasPricerConfig::Fixed(0.into())); - assert_eq!(c.miner_options.reseal_min_period, Duration::from_millis(0)); - assert_eq!(c.update_policy.require_consensus, false); - assert_eq!(c.net_settings.rpc_interface, "0.0.0.0"); - match c.http_conf.apis { - ApiSet::List(set) => assert_eq!(set, ApiSet::All.list_apis()), - _ => panic!("Incorrect rpc apis"), - } - // "web3,eth,net,personal,parity,parity_set,traces,rpc,parity_accounts"); - assert_eq!(c.http_conf.hosts, None); - assert_eq!(c.ipfs_conf.hosts, None); - }, - _ => panic!("Should be Cmd::Run"), - } - } - - #[test] - fn test_override_preset() { - let args = vec!["parity", "--config", "mining", "--min-peers=99"]; - let conf = Configuration::parse_cli(&args).unwrap(); - match conf.into_command().unwrap().cmd { - Cmd::Run(c) => { - assert_eq!(c.net_conf.min_peers, 99); - }, - _ => panic!("Should be Cmd::Run"), - } - } - - #[test] - fn test_identity_arg() { - let args = vec!["parity", "--identity", "Somebody"]; - let conf = Configuration::parse_cli(&args).unwrap(); - match conf.into_command().unwrap().cmd { - Cmd::Run(c) => { - assert_eq!(c.name, "Somebody"); - assert!(c.net_conf.client_version.starts_with("Parity-Ethereum/Somebody/")); - } - _ => panic!("Should be Cmd::Run"), - } - } - - #[test] - fn should_apply_ports_shift() { - // given - - // when - let conf0 = parse(&["parity", "--ports-shift", "1", "--stratum"]); - let conf1 = parse(&["parity", "--ports-shift", "1", "--jsonrpc-port", "8544"]); - - // then - assert_eq!(conf0.net_addresses().unwrap().0.port(), 30304); - assert_eq!(conf0.network_settings().unwrap().network_port, 30304); - assert_eq!(conf0.network_settings().unwrap().rpc_port, 8546); - assert_eq!(conf0.http_config().unwrap().port, 8546); - assert_eq!(conf0.ws_config().unwrap().port, 8547); - assert_eq!(conf0.secretstore_config().unwrap().port, 8084); - assert_eq!(conf0.secretstore_config().unwrap().http_port, 8083); - assert_eq!(conf0.ipfs_config().port, 5002); - assert_eq!(conf0.stratum_options().unwrap().unwrap().port, 8009); - - assert_eq!(conf1.net_addresses().unwrap().0.port(), 30304); - assert_eq!(conf1.network_settings().unwrap().network_port, 30304); - assert_eq!(conf1.network_settings().unwrap().rpc_port, 8545); - assert_eq!(conf1.http_config().unwrap().port, 8545); - assert_eq!(conf1.ws_config().unwrap().port, 8547); - assert_eq!(conf1.secretstore_config().unwrap().port, 8084); - assert_eq!(conf1.secretstore_config().unwrap().http_port, 8083); - assert_eq!(conf1.ipfs_config().port, 5002); - } - - #[test] - fn should_expose_all_servers() { - // given - - // when - let conf0 = parse(&["parity", "--unsafe-expose"]); - - // then - assert_eq!(&conf0.network_settings().unwrap().rpc_interface, "0.0.0.0"); - assert_eq!(&conf0.http_config().unwrap().interface, "0.0.0.0"); - assert_eq!(conf0.http_config().unwrap().hosts, None); - assert_eq!(&conf0.ws_config().unwrap().interface, "0.0.0.0"); - assert_eq!(conf0.ws_config().unwrap().hosts, None); - assert_eq!(conf0.ws_config().unwrap().origins, None); - assert_eq!(&conf0.secretstore_config().unwrap().interface, "0.0.0.0"); - assert_eq!(&conf0.secretstore_config().unwrap().http_interface, "0.0.0.0"); - assert_eq!(&conf0.ipfs_config().interface, "0.0.0.0"); - assert_eq!(conf0.ipfs_config().hosts, None); - } - - #[test] - fn allow_ips() { - let all = parse(&["parity", "--allow-ips", "all"]); - let private = parse(&["parity", "--allow-ips", "private"]); - let block_custom = parse(&["parity", "--allow-ips", "-10.0.0.0/8"]); - let combo = parse(&["parity", "--allow-ips", "public 10.0.0.0/8 -1.0.0.0/8"]); - let ipv6_custom_public = parse(&["parity", "--allow-ips", "public fc00::/7"]); - let ipv6_custom_private = parse(&["parity", "--allow-ips", "private -fc00::/7"]); - - assert_eq!(all.ip_filter().unwrap(), IpFilter { - predefined: AllowIP::All, - custom_allow: vec![], - custom_block: vec![], - }); - - assert_eq!(private.ip_filter().unwrap(), IpFilter { - predefined: AllowIP::Private, - custom_allow: vec![], - custom_block: vec![], - }); - - assert_eq!(block_custom.ip_filter().unwrap(), IpFilter { - predefined: AllowIP::All, - custom_allow: vec![], - custom_block: vec![IpNetwork::from_str("10.0.0.0/8").unwrap()], - }); - - assert_eq!(combo.ip_filter().unwrap(), IpFilter { - predefined: AllowIP::Public, - custom_allow: vec![IpNetwork::from_str("10.0.0.0/8").unwrap()], - custom_block: vec![IpNetwork::from_str("1.0.0.0/8").unwrap()], - }); - - assert_eq!(ipv6_custom_public.ip_filter().unwrap(), IpFilter { - predefined: AllowIP::Public, - custom_allow: vec![IpNetwork::from_str("fc00::/7").unwrap()], - custom_block: vec![], - }); - - assert_eq!(ipv6_custom_private.ip_filter().unwrap(), IpFilter { - predefined: AllowIP::Private, - custom_allow: vec![], - custom_block: vec![IpNetwork::from_str("fc00::/7").unwrap()], - }); - } - - #[test] - fn should_use_correct_cache_path_if_base_is_set() { - use std::path; - - let std = parse(&["parity"]); - let base = parse(&["parity", "--base-path", "/test"]); - - let base_path = ::dir::default_data_path(); - let local_path = ::dir::default_local_path(); - assert_eq!(std.directories().cache, dir::helpers::replace_home_and_local(&base_path, &local_path, ::dir::CACHE_PATH)); - assert_eq!(path::Path::new(&base.directories().cache), path::Path::new("/test/cache")); - } - - #[test] - fn should_respect_only_max_peers_and_default() { - let args = vec!["parity", "--max-peers=50"]; - let conf = Configuration::parse_cli(&args).unwrap(); - match conf.into_command().unwrap().cmd { - Cmd::Run(c) => { - assert_eq!(c.net_conf.min_peers, 25); - assert_eq!(c.net_conf.max_peers, 50); - }, - _ => panic!("Should be Cmd::Run"), - } - } - - #[test] - fn should_respect_only_max_peers_less_than_default() { - let args = vec!["parity", "--max-peers=5"]; - let conf = Configuration::parse_cli(&args).unwrap(); - match conf.into_command().unwrap().cmd { - Cmd::Run(c) => { - assert_eq!(c.net_conf.min_peers, 5); - assert_eq!(c.net_conf.max_peers, 5); - }, - _ => panic!("Should be Cmd::Run"), - } - } - - #[test] - fn should_respect_only_min_peers_and_default() { - let args = vec!["parity", "--min-peers=5"]; - let conf = Configuration::parse_cli(&args).unwrap(); - match conf.into_command().unwrap().cmd { - Cmd::Run(c) => { - assert_eq!(c.net_conf.min_peers, 5); - assert_eq!(c.net_conf.max_peers, 50); - }, - _ => panic!("Should be Cmd::Run"), - } - } - - #[test] - fn should_respect_only_min_peers_and_greater_than_default() { - let args = vec!["parity", "--min-peers=500"]; - let conf = Configuration::parse_cli(&args).unwrap(); - match conf.into_command().unwrap().cmd { - Cmd::Run(c) => { - assert_eq!(c.net_conf.min_peers, 500); - assert_eq!(c.net_conf.max_peers, 500); - }, - _ => panic!("Should be Cmd::Run"), - } - } + use std::{fs::File, io::Write, str::FromStr}; + + use account::{AccountCmd, ImportAccounts, ListAccounts, NewAccount}; + use blockchain::{BlockchainCmd, ExportBlockchain, ExportState, ImportBlockchain}; + use cli::Args; + use dir::Directories; + use ethcore::{client::VMType, miner::MinerOptions}; + use helpers::default_network_config; + use miner::pool::PrioritizationStrategy; + use params::SpecType; + use parity_rpc::NetworkSettings; + use presale::ImportWallet; + use rpc::WsConfiguration; + use rpc_apis::ApiSet; + use run::RunCmd; + use tempdir::TempDir; + use types::{data_format::DataFormat, ids::BlockId}; + + use network::{AllowIP, IpFilter}; + + extern crate ipnetwork; + use self::ipnetwork::IpNetwork; + + use super::*; + + lazy_static! { + static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed"); + } + + #[derive(Debug, PartialEq)] + struct TestPasswordReader(&'static str); + + fn parse(args: &[&str]) -> Configuration { + Configuration { + args: Args::parse_without_config(args).unwrap(), + } + } + + #[test] + fn test_command_version() { + let args = vec!["openethereum", "--version"]; + let conf = parse(&args); + assert_eq!(conf.into_command().unwrap().cmd, Cmd::Version); + } + + #[test] + fn test_command_account_new() { + let args = vec!["openethereum", "account", "new"]; + let conf = parse(&args); + assert_eq!( + conf.into_command().unwrap().cmd, + Cmd::Account(AccountCmd::New(NewAccount { + iterations: *ITERATIONS, + path: Directories::default().keys, + password_file: None, + spec: SpecType::default(), + })) + ); + } + + #[test] + fn test_command_account_list() { + let args = vec!["openethereum", "account", "list"]; + let conf = parse(&args); + assert_eq!( + conf.into_command().unwrap().cmd, + Cmd::Account(AccountCmd::List(ListAccounts { + path: Directories::default().keys, + spec: SpecType::default(), + })) + ); + } + + #[test] + fn test_command_account_import() { + let args = vec!["openethereum", "account", "import", "my_dir", "another_dir"]; + let conf = parse(&args); + assert_eq!( + conf.into_command().unwrap().cmd, + Cmd::Account(AccountCmd::Import(ImportAccounts { + from: vec!["my_dir".into(), "another_dir".into()], + to: Directories::default().keys, + spec: SpecType::default(), + })) + ); + } + + #[test] + fn test_command_wallet_import() { + let args = vec![ + "openethereum", + "wallet", + "import", + "my_wallet.json", + "--password", + "pwd", + ]; + let conf = parse(&args); + assert_eq!( + conf.into_command().unwrap().cmd, + Cmd::ImportPresaleWallet(ImportWallet { + iterations: *ITERATIONS, + path: Directories::default().keys, + wallet_path: "my_wallet.json".into(), + password_file: Some("pwd".into()), + spec: SpecType::default(), + }) + ); + } + + #[test] + fn test_command_blockchain_import() { + let args = vec!["openethereum", "import", "blockchain.json"]; + let conf = parse(&args); + assert_eq!( + conf.into_command().unwrap().cmd, + Cmd::Blockchain(BlockchainCmd::Import(ImportBlockchain { + spec: Default::default(), + cache_config: Default::default(), + dirs: Default::default(), + file_path: Some("blockchain.json".into()), + format: Default::default(), + pruning: Default::default(), + pruning_history: 64, + pruning_memory: 32, + compaction: Default::default(), + tracing: Default::default(), + fat_db: Default::default(), + vm_type: VMType::Interpreter, + check_seal: true, + with_color: !cfg!(windows), + verifier_settings: Default::default(), + max_round_blocks_to_import: 12, + })) + ); + } + + #[test] + fn test_command_blockchain_export() { + let args = vec!["openethereum", "export", "blocks", "blockchain.json"]; + let conf = parse(&args); + assert_eq!( + conf.into_command().unwrap().cmd, + Cmd::Blockchain(BlockchainCmd::Export(ExportBlockchain { + spec: Default::default(), + cache_config: Default::default(), + dirs: Default::default(), + file_path: Some("blockchain.json".into()), + pruning: Default::default(), + pruning_history: 64, + pruning_memory: 32, + format: Default::default(), + compaction: Default::default(), + tracing: Default::default(), + fat_db: Default::default(), + from_block: BlockId::Number(1), + to_block: BlockId::Latest, + check_seal: true, + max_round_blocks_to_import: 12, + })) + ); + } + + #[test] + fn test_command_state_export() { + let args = vec!["openethereum", "export", "state", "state.json"]; + let conf = parse(&args); + assert_eq!( + conf.into_command().unwrap().cmd, + Cmd::Blockchain(BlockchainCmd::ExportState(ExportState { + spec: Default::default(), + cache_config: Default::default(), + dirs: Default::default(), + file_path: Some("state.json".into()), + pruning: Default::default(), + pruning_history: 64, + pruning_memory: 32, + format: Default::default(), + compaction: Default::default(), + tracing: Default::default(), + fat_db: Default::default(), + at: BlockId::Latest, + storage: true, + code: true, + min_balance: None, + max_balance: None, + max_round_blocks_to_import: 12, + })) + ); + } + + #[test] + fn test_command_blockchain_export_with_custom_format() { + let args = vec![ + "openethereum", + "export", + "blocks", + "--format", + "hex", + "blockchain.json", + ]; + let conf = parse(&args); + assert_eq!( + conf.into_command().unwrap().cmd, + Cmd::Blockchain(BlockchainCmd::Export(ExportBlockchain { + spec: Default::default(), + cache_config: Default::default(), + dirs: Default::default(), + file_path: Some("blockchain.json".into()), + pruning: Default::default(), + pruning_history: 64, + pruning_memory: 32, + format: Some(DataFormat::Hex), + compaction: Default::default(), + tracing: Default::default(), + fat_db: Default::default(), + from_block: BlockId::Number(1), + to_block: BlockId::Latest, + check_seal: true, + max_round_blocks_to_import: 12, + })) + ); + } + + #[test] + fn test_command_signer_new_token() { + let args = vec!["openethereum", "signer", "new-token"]; + let conf = parse(&args); + let expected = Directories::default().signer; + assert_eq!( + conf.into_command().unwrap().cmd, + Cmd::SignerToken( + WsConfiguration { + enabled: true, + interface: "127.0.0.1".into(), + port: 8546, + apis: ApiSet::UnsafeContext, + origins: Some(vec![ + "parity://*".into(), + "chrome-extension://*".into(), + "moz-extension://*".into() + ]), + hosts: Some(vec![]), + signer_path: expected.into(), + support_token_api: true, + max_connections: 100, + }, + LogConfig { + color: !cfg!(windows), + mode: None, + file: None, + } + ) + ); + } + + #[test] + fn test_ws_max_connections() { + let args = vec!["openethereum", "--ws-max-connections", "1"]; + let conf = parse(&args); + + assert_eq!( + conf.ws_config().unwrap(), + WsConfiguration { + max_connections: 1, + ..Default::default() + } + ); + } + + #[test] + fn test_run_cmd() { + let args = vec!["openethereum"]; + let conf = parse(&args); + let mut expected = RunCmd { + allow_missing_blocks: false, + cache_config: Default::default(), + dirs: Default::default(), + spec: Default::default(), + pruning: Default::default(), + pruning_history: 64, + pruning_memory: 32, + daemon: None, + logger_config: Default::default(), + miner_options: Default::default(), + gas_price_percentile: 50, + poll_lifetime: 60, + ws_conf: Default::default(), + http_conf: Default::default(), + ipc_conf: Default::default(), + net_conf: default_network_config(), + network_id: None, + warp_sync: true, + warp_barrier: None, + acc_conf: Default::default(), + gas_pricer_conf: Default::default(), + miner_extras: Default::default(), + mode: Default::default(), + tracing: Default::default(), + compaction: Default::default(), + vm_type: Default::default(), + experimental_rpcs: false, + net_settings: Default::default(), + secretstore_conf: Default::default(), + name: "".into(), + custom_bootnodes: false, + fat_db: Default::default(), + snapshot_conf: Default::default(), + stratum: None, + check_seal: true, + download_old_blocks: true, + verifier_settings: Default::default(), + no_persistent_txqueue: false, + max_round_blocks_to_import: 12, + metrics_conf: MetricsConfiguration::default(), + }; + expected.secretstore_conf.enabled = cfg!(feature = "secretstore"); + expected.secretstore_conf.http_enabled = cfg!(feature = "secretstore"); + assert_eq!(conf.into_command().unwrap().cmd, Cmd::Run(expected)); + } + + #[test] + fn should_parse_mining_options() { + // given + let mut mining_options = MinerOptions::default(); + + // when + let conf0 = parse(&["openethereum"]); + let conf2 = parse(&["openethereum", "--tx-queue-strategy", "gas_price"]); + + // then + assert_eq!(conf0.miner_options().unwrap(), mining_options); + mining_options.tx_queue_strategy = PrioritizationStrategy::GasPriceOnly; + assert_eq!(conf2.miner_options().unwrap(), mining_options); + } + + #[test] + fn should_fail_on_force_reseal_and_reseal_min_period() { + let conf = parse(&[ + "openethereum", + "--chain", + "dev", + "--force-sealing", + "--reseal-min-period", + "0", + ]); + + assert!(conf.miner_options().is_err()); + } + + #[test] + fn should_parse_network_settings() { + // given + + // when + let conf = parse(&[ + "openethereum", + "--chain", + "goerli", + "--identity", + "testname", + ]); + + // then + assert_eq!( + conf.network_settings(), + Ok(NetworkSettings { + name: "testname".to_owned(), + chain: "goerli".to_owned(), + is_dev_chain: false, + network_port: 30303, + rpc_enabled: true, + rpc_interface: "127.0.0.1".to_owned(), + rpc_port: 8545, + }) + ); + } + + #[test] + fn should_parse_rpc_hosts() { + // given + + // when + let conf0 = parse(&["openethereum"]); + let conf1 = parse(&["openethereum", "--jsonrpc-hosts", "none"]); + let conf2 = parse(&["openethereum", "--jsonrpc-hosts", "all"]); + let conf3 = parse(&["openethereum", "--jsonrpc-hosts", "parity.io,something.io"]); + + // then + assert_eq!(conf0.rpc_hosts(), Some(Vec::new())); + assert_eq!(conf1.rpc_hosts(), Some(Vec::new())); + assert_eq!(conf2.rpc_hosts(), None); + assert_eq!( + conf3.rpc_hosts(), + Some(vec!["parity.io".into(), "something.io".into()]) + ); + } + + #[test] + fn should_parse_ui_configuration() { + // given + + // when + let conf0 = parse(&["openethereum", "--ui-path=signer"]); + + // then + assert_eq!(conf0.directories().signer, "signer".to_owned()); + } + + #[test] + fn should_not_bail_on_empty_line_in_reserved_peers() { + let tempdir = TempDir::new("").unwrap(); + let filename = tempdir.path().join("peers"); + File::create(&filename) + .unwrap() + .write_all(b" \n\t\n") + .unwrap(); + let args = vec![ + "openethereum", + "--reserved-peers", + filename.to_str().unwrap(), + ]; + let conf = Configuration::parse_cli(&args).unwrap(); + assert!(conf.init_reserved_nodes().is_ok()); + } + + #[test] + fn should_ignore_comments_in_reserved_peers() { + let tempdir = TempDir::new("").unwrap(); + let filename = tempdir.path().join("peers_comments"); + File::create(&filename).unwrap().write_all(b"# Sample comment\nenode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@172.0.0.1:30303\n").unwrap(); + let args = vec![ + "openethereum", + "--reserved-peers", + filename.to_str().unwrap(), + ]; + let conf = Configuration::parse_cli(&args).unwrap(); + let reserved_nodes = conf.init_reserved_nodes(); + assert!(reserved_nodes.is_ok()); + assert_eq!(reserved_nodes.unwrap().len(), 1); + } + + #[test] + fn test_dev_preset() { + let args = vec!["openethereum", "--config", "dev"]; + let conf = Configuration::parse_cli(&args).unwrap(); + match conf.into_command().unwrap().cmd { + Cmd::Run(c) => { + assert_eq!(c.net_settings.chain, "dev"); + assert_eq!(c.gas_pricer_conf, GasPricerConfig::Fixed(0.into())); + assert_eq!(c.miner_options.reseal_min_period, Duration::from_millis(0)); + } + _ => panic!("Should be Cmd::Run"), + } + } + + #[test] + fn test_mining_preset() { + let args = vec!["openethereum", "--config", "mining"]; + let conf = Configuration::parse_cli(&args).unwrap(); + match conf.into_command().unwrap().cmd { + Cmd::Run(c) => { + assert_eq!(c.net_conf.min_peers, 50); + assert_eq!(c.net_conf.max_peers, 100); + assert_eq!(c.ipc_conf.enabled, false); + assert_eq!(c.miner_options.force_sealing, true); + assert_eq!(c.miner_options.reseal_on_external_tx, true); + assert_eq!(c.miner_options.reseal_on_own_tx, true); + assert_eq!( + c.miner_options.reseal_min_period, + Duration::from_millis(4000) + ); + assert_eq!(c.miner_options.pool_limits.max_count, 8192); + assert_eq!(c.cache_config, CacheConfig::new_with_total_cache_size(1024)); + assert_eq!(c.logger_config.mode.unwrap(), "miner=trace,own_tx=trace"); + } + _ => panic!("Should be Cmd::Run"), + } + } + + #[test] + fn test_non_standard_ports_preset() { + let args = vec!["openethereum", "--config", "non-standard-ports"]; + let conf = Configuration::parse_cli(&args).unwrap(); + match conf.into_command().unwrap().cmd { + Cmd::Run(c) => { + assert_eq!(c.net_settings.network_port, 30305); + assert_eq!(c.net_settings.rpc_port, 8645); + } + _ => panic!("Should be Cmd::Run"), + } + } + + #[test] + fn test_insecure_preset() { + let args = vec!["openethereum", "--config", "insecure"]; + let conf = Configuration::parse_cli(&args).unwrap(); + match conf.into_command().unwrap().cmd { + Cmd::Run(c) => { + assert_eq!(c.net_settings.rpc_interface, "0.0.0.0"); + match c.http_conf.apis { + ApiSet::List(set) => assert_eq!(set, ApiSet::All.list_apis()), + _ => panic!("Incorrect rpc apis"), + } + // "web3,eth,net,personal,parity,parity_set,traces,parity_accounts"); + assert_eq!(c.http_conf.hosts, None); + } + _ => panic!("Should be Cmd::Run"), + } + } + + #[test] + fn test_dev_insecure_preset() { + let args = vec!["openethereum", "--config", "dev-insecure"]; + let conf = Configuration::parse_cli(&args).unwrap(); + match conf.into_command().unwrap().cmd { + Cmd::Run(c) => { + assert_eq!(c.net_settings.chain, "dev"); + assert_eq!(c.gas_pricer_conf, GasPricerConfig::Fixed(0.into())); + assert_eq!(c.miner_options.reseal_min_period, Duration::from_millis(0)); + assert_eq!(c.net_settings.rpc_interface, "0.0.0.0"); + match c.http_conf.apis { + ApiSet::List(set) => assert_eq!(set, ApiSet::All.list_apis()), + _ => panic!("Incorrect rpc apis"), + } + // "web3,eth,net,personal,parity,parity_set,traces,parity_accounts"); + assert_eq!(c.http_conf.hosts, None); + } + _ => panic!("Should be Cmd::Run"), + } + } + + #[test] + fn test_override_preset() { + let args = vec!["openethereum", "--config", "mining", "--min-peers=99"]; + let conf = Configuration::parse_cli(&args).unwrap(); + match conf.into_command().unwrap().cmd { + Cmd::Run(c) => { + assert_eq!(c.net_conf.min_peers, 99); + } + _ => panic!("Should be Cmd::Run"), + } + } + + #[test] + fn test_identity_arg() { + let args = vec!["openethereum", "--identity", "Somebody"]; + let conf = Configuration::parse_cli(&args).unwrap(); + match conf.into_command().unwrap().cmd { + Cmd::Run(c) => { + assert_eq!(c.name, "Somebody"); + assert!(c + .net_conf + .client_version + .starts_with("OpenEthereum/Somebody/")); + } + _ => panic!("Should be Cmd::Run"), + } + } + + #[test] + fn should_apply_ports_shift() { + // given + + // when + let conf0 = parse(&["openethereum", "--ports-shift", "1", "--stratum"]); + let conf1 = parse(&[ + "openethereum", + "--ports-shift", + "1", + "--jsonrpc-port", + "8544", + ]); + + // then + assert_eq!(conf0.net_addresses().unwrap().0.port(), 30304); + assert_eq!(conf0.network_settings().unwrap().network_port, 30304); + assert_eq!(conf0.network_settings().unwrap().rpc_port, 8546); + assert_eq!(conf0.http_config().unwrap().port, 8546); + assert_eq!(conf0.ws_config().unwrap().port, 8547); + assert_eq!(conf0.secretstore_config().unwrap().port, 8084); + assert_eq!(conf0.secretstore_config().unwrap().http_port, 8083); + assert_eq!(conf0.stratum_options().unwrap().unwrap().port, 8009); + + assert_eq!(conf1.net_addresses().unwrap().0.port(), 30304); + assert_eq!(conf1.network_settings().unwrap().network_port, 30304); + assert_eq!(conf1.network_settings().unwrap().rpc_port, 8545); + assert_eq!(conf1.http_config().unwrap().port, 8545); + assert_eq!(conf1.ws_config().unwrap().port, 8547); + assert_eq!(conf1.secretstore_config().unwrap().port, 8084); + assert_eq!(conf1.secretstore_config().unwrap().http_port, 8083); + } + + #[test] + fn should_resolve_external_nat_hosts() { + // Ip works + let conf = parse(&["openethereum", "--nat", "extip:1.1.1.1"]); + assert_eq!( + conf.net_addresses().unwrap().1.unwrap().ip().to_string(), + "1.1.1.1" + ); + assert_eq!(conf.net_addresses().unwrap().1.unwrap().port(), 30303); + + // Ip with port works, port is discarded + let conf = parse(&["openethereum", "--nat", "extip:192.168.1.1:123"]); + assert_eq!( + conf.net_addresses().unwrap().1.unwrap().ip().to_string(), + "192.168.1.1" + ); + assert_eq!(conf.net_addresses().unwrap().1.unwrap().port(), 30303); + + // Hostname works + let conf = parse(&["openethereum", "--nat", "extip:ethereum.org"]); + assert!(conf.net_addresses().unwrap().1.is_some()); + assert_eq!(conf.net_addresses().unwrap().1.unwrap().port(), 30303); + + // Hostname works, garbage at the end is discarded + let conf = parse(&[ + "openethereum", + "--nat", + "extip:ethereum.org:whatever bla bla 123", + ]); + assert!(conf.net_addresses().unwrap().1.is_some()); + assert_eq!(conf.net_addresses().unwrap().1.unwrap().port(), 30303); + + // Garbage is error + let conf = parse(&["openethereum", "--nat", "extip:blabla"]); + assert!(conf.net_addresses().is_err()); + } + + #[test] + fn should_expose_all_servers() { + // given + + // when + let conf0 = parse(&["openethereum", "--unsafe-expose"]); + + // then + assert_eq!(&conf0.network_settings().unwrap().rpc_interface, "0.0.0.0"); + assert_eq!(&conf0.http_config().unwrap().interface, "0.0.0.0"); + assert_eq!(conf0.http_config().unwrap().hosts, None); + assert_eq!(&conf0.ws_config().unwrap().interface, "0.0.0.0"); + assert_eq!(conf0.ws_config().unwrap().hosts, None); + assert_eq!(conf0.ws_config().unwrap().origins, None); + assert_eq!(&conf0.secretstore_config().unwrap().interface, "0.0.0.0"); + assert_eq!( + &conf0.secretstore_config().unwrap().http_interface, + "0.0.0.0" + ); + } + + #[test] + fn allow_ips() { + let all = parse(&["openethereum", "--allow-ips", "all"]); + let private = parse(&["openethereum", "--allow-ips", "private"]); + let block_custom = parse(&["openethereum", "--allow-ips", "-10.0.0.0/8"]); + let combo = parse(&[ + "openethereum", + "--allow-ips", + "public 10.0.0.0/8 -1.0.0.0/8", + ]); + let ipv6_custom_public = parse(&["openethereum", "--allow-ips", "public fc00::/7"]); + let ipv6_custom_private = parse(&["openethereum", "--allow-ips", "private -fc00::/7"]); + + assert_eq!( + all.ip_filter().unwrap(), + IpFilter { + predefined: AllowIP::All, + custom_allow: vec![], + custom_block: vec![], + } + ); + + assert_eq!( + private.ip_filter().unwrap(), + IpFilter { + predefined: AllowIP::Private, + custom_allow: vec![], + custom_block: vec![], + } + ); + + assert_eq!( + block_custom.ip_filter().unwrap(), + IpFilter { + predefined: AllowIP::All, + custom_allow: vec![], + custom_block: vec![IpNetwork::from_str("10.0.0.0/8").unwrap()], + } + ); + + assert_eq!( + combo.ip_filter().unwrap(), + IpFilter { + predefined: AllowIP::Public, + custom_allow: vec![IpNetwork::from_str("10.0.0.0/8").unwrap()], + custom_block: vec![IpNetwork::from_str("1.0.0.0/8").unwrap()], + } + ); + + assert_eq!( + ipv6_custom_public.ip_filter().unwrap(), + IpFilter { + predefined: AllowIP::Public, + custom_allow: vec![IpNetwork::from_str("fc00::/7").unwrap()], + custom_block: vec![], + } + ); + + assert_eq!( + ipv6_custom_private.ip_filter().unwrap(), + IpFilter { + predefined: AllowIP::Private, + custom_allow: vec![], + custom_block: vec![IpNetwork::from_str("fc00::/7").unwrap()], + } + ); + } + + #[test] + fn should_use_correct_cache_path_if_base_is_set() { + use std::path; + + let std = parse(&["openethereum"]); + let base = parse(&["openethereum", "--base-path", "/test"]); + + let base_path = ::dir::default_data_path(); + let local_path = ::dir::default_local_path(); + assert_eq!( + std.directories().cache, + dir::helpers::replace_home_and_local(&base_path, &local_path, ::dir::CACHE_PATH) + ); + assert_eq!( + path::Path::new(&base.directories().cache), + path::Path::new("/test/cache") + ); + } + + #[test] + fn should_respect_only_max_peers_and_default() { + let args = vec!["openethereum", "--max-peers=50"]; + let conf = Configuration::parse_cli(&args).unwrap(); + match conf.into_command().unwrap().cmd { + Cmd::Run(c) => { + assert_eq!(c.net_conf.min_peers, 25); + assert_eq!(c.net_conf.max_peers, 50); + } + _ => panic!("Should be Cmd::Run"), + } + } + + #[test] + fn should_respect_only_max_peers_less_than_default() { + let args = vec!["openethereum", "--max-peers=5"]; + let conf = Configuration::parse_cli(&args).unwrap(); + match conf.into_command().unwrap().cmd { + Cmd::Run(c) => { + assert_eq!(c.net_conf.min_peers, 5); + assert_eq!(c.net_conf.max_peers, 5); + } + _ => panic!("Should be Cmd::Run"), + } + } + + #[test] + fn should_respect_only_min_peers_and_default() { + let args = vec!["openethereum", "--min-peers=5"]; + let conf = Configuration::parse_cli(&args).unwrap(); + match conf.into_command().unwrap().cmd { + Cmd::Run(c) => { + assert_eq!(c.net_conf.min_peers, 5); + assert_eq!(c.net_conf.max_peers, 50); + } + _ => panic!("Should be Cmd::Run"), + } + } + + #[test] + fn should_respect_only_min_peers_and_greater_than_default() { + let args = vec!["openethereum", "--min-peers=500"]; + let conf = Configuration::parse_cli(&args).unwrap(); + match conf.into_command().unwrap().cmd { + Cmd::Run(c) => { + assert_eq!(c.net_conf.min_peers, 500); + assert_eq!(c.net_conf.max_peers, 500); + } + _ => panic!("Should be Cmd::Run"), + } + } } diff --git a/parity/db/mod.rs b/parity/db/mod.rs index 9b46624425b..3df530260bb 100644 --- a/parity/db/mod.rs +++ b/parity/db/mod.rs @@ -1,25 +1,25 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Database-related operations. -#[path="rocksdb/mod.rs"] +#[path = "rocksdb/mod.rs"] mod impls; -pub use self::impls::{open_db, restoration_db_handler, migrate}; +pub use self::impls::{migrate, restoration_db_handler}; #[cfg(feature = "secretstore")] pub use self::impls::open_secretstore_db; diff --git a/parity/db/rocksdb/blooms.rs b/parity/db/rocksdb/blooms.rs index eea913bea49..06c8749d3a0 100644 --- a/parity/db/rocksdb/blooms.rs +++ b/parity/db/rocksdb/blooms.rs @@ -1,88 +1,83 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Blooms migration from rocksdb to blooms-db -use std::path::Path; -use ethereum_types::Bloom; +use super::{kvdb_rocksdb::DatabaseConfig, open_database}; use ethcore::error::Error; +use ethereum_types::Bloom; use rlp; -use super::kvdb_rocksdb::DatabaseConfig; -use super::open_database; +use std::path::Path; const LOG_BLOOMS_ELEMENTS_PER_INDEX: u64 = 16; pub fn migrate_blooms>(path: P, config: &DatabaseConfig) -> Result<(), Error> { - // init - let db = open_database(&path.as_ref().to_string_lossy(), config)?; + // init + let db = open_database(&path.as_ref().to_string_lossy(), config)?; - // possible optimization: - // pre-allocate space on disk for faster migration + // possible optimization: + // pre-allocate space on disk for faster migration - // iterate over header blooms and insert them in blooms-db - // Some(3) -> COL_EXTRA - // 3u8 -> ExtrasIndex::BlocksBlooms - // 0u8 -> level 0 - let blooms_iterator = db.key_value() - .iter_from_prefix(Some(3), &[3u8, 0u8]) - .filter(|(key, _)| key.len() == 6) - .take_while(|(key, _)| { - key[0] == 3u8 && key[1] == 0u8 - }) - .map(|(key, group)| { - let index = - (key[2] as u64) << 24 | - (key[3] as u64) << 16 | - (key[4] as u64) << 8 | - (key[5] as u64); - let number = index * LOG_BLOOMS_ELEMENTS_PER_INDEX; + // iterate over header blooms and insert them in blooms-db + // Some(3) -> COL_EXTRA + // 3u8 -> ExtrasIndex::BlocksBlooms + // 0u8 -> level 0 + let blooms_iterator = db + .key_value() + .iter_from_prefix(Some(3), &[3u8, 0u8]) + .filter(|(key, _)| key.len() == 6) + .take_while(|(key, _)| key[0] == 3u8 && key[1] == 0u8) + .map(|(key, group)| { + let index = (key[2] as u64) << 24 + | (key[3] as u64) << 16 + | (key[4] as u64) << 8 + | (key[5] as u64); + let number = index * LOG_BLOOMS_ELEMENTS_PER_INDEX; - let blooms = rlp::decode_list::(&group); - (number, blooms) - }); + let blooms = rlp::decode_list::(&group); + (number, blooms) + }); - for (number, blooms) in blooms_iterator { - db.blooms().insert_blooms(number, blooms.iter())?; - } + for (number, blooms) in blooms_iterator { + db.blooms().insert_blooms(number, blooms.iter())?; + } - // iterate over trace blooms and insert them in blooms-db - // Some(4) -> COL_TRACE - // 1u8 -> TraceDBIndex::BloomGroups - // 0u8 -> level 0 - let trace_blooms_iterator = db.key_value() - .iter_from_prefix(Some(4), &[1u8, 0u8]) - .filter(|(key, _)| key.len() == 6) - .take_while(|(key, _)| { - key[0] == 1u8 && key[1] == 0u8 - }) - .map(|(key, group)| { - let index = - (key[2] as u64) | - (key[3] as u64) << 8 | - (key[4] as u64) << 16 | - (key[5] as u64) << 24; - let number = index * LOG_BLOOMS_ELEMENTS_PER_INDEX; + // iterate over trace blooms and insert them in blooms-db + // Some(4) -> COL_TRACE + // 1u8 -> TraceDBIndex::BloomGroups + // 0u8 -> level 0 + let trace_blooms_iterator = db + .key_value() + .iter_from_prefix(Some(4), &[1u8, 0u8]) + .filter(|(key, _)| key.len() == 6) + .take_while(|(key, _)| key[0] == 1u8 && key[1] == 0u8) + .map(|(key, group)| { + let index = (key[2] as u64) + | (key[3] as u64) << 8 + | (key[4] as u64) << 16 + | (key[5] as u64) << 24; + let number = index * LOG_BLOOMS_ELEMENTS_PER_INDEX; - let blooms = rlp::decode_list::(&group); - (number, blooms) - }); + let blooms = rlp::decode_list::(&group); + (number, blooms) + }); - for (number, blooms) in trace_blooms_iterator { - db.trace_blooms().insert_blooms(number, blooms.iter())?; - } + for (number, blooms) in trace_blooms_iterator { + db.trace_blooms().insert_blooms(number, blooms.iter())?; + } - Ok(()) + Ok(()) } diff --git a/parity/db/rocksdb/helpers.rs b/parity/db/rocksdb/helpers.rs index 9829cb5a62c..e75f6efded8 100644 --- a/parity/db/rocksdb/helpers.rs +++ b/parity/db/rocksdb/helpers.rs @@ -1,37 +1,40 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::path::Path; -use ethcore_db::NUM_COLUMNS; -use ethcore::client::{ClientConfig, DatabaseCompactionProfile}; use super::kvdb_rocksdb::{CompactionProfile, DatabaseConfig}; +use ethcore::client::{ClientConfig, DatabaseCompactionProfile}; +use ethcore_db::NUM_COLUMNS; +use std::path::Path; -pub fn compaction_profile(profile: &DatabaseCompactionProfile, db_path: &Path) -> CompactionProfile { - match profile { - &DatabaseCompactionProfile::Auto => CompactionProfile::auto(db_path), - &DatabaseCompactionProfile::SSD => CompactionProfile::ssd(), - &DatabaseCompactionProfile::HDD => CompactionProfile::hdd(), - } +pub fn compaction_profile( + profile: &DatabaseCompactionProfile, + db_path: &Path, +) -> CompactionProfile { + match profile { + &DatabaseCompactionProfile::Auto => CompactionProfile::auto(db_path), + &DatabaseCompactionProfile::SSD => CompactionProfile::ssd(), + &DatabaseCompactionProfile::HDD => CompactionProfile::hdd(), + } } pub fn client_db_config(client_path: &Path, client_config: &ClientConfig) -> DatabaseConfig { - let mut client_db_config = DatabaseConfig::with_columns(NUM_COLUMNS); + let mut client_db_config = DatabaseConfig::with_columns(NUM_COLUMNS); - client_db_config.memory_budget = client_config.db_cache_size; - client_db_config.compaction = compaction_profile(&client_config.db_compaction, &client_path); + client_db_config.memory_budget = client_config.db_cache_size; + client_db_config.compaction = compaction_profile(&client_config.db_compaction, &client_path); - client_db_config + client_db_config } diff --git a/parity/db/rocksdb/migration.rs b/parity/db/rocksdb/migration.rs index eec43d23362..441927aeeb7 100644 --- a/parity/db/rocksdb/migration.rs +++ b/parity/db/rocksdb/migration.rs @@ -1,51 +1,55 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::fs; -use std::io::{Read, Write, Error as IoError, ErrorKind}; -use std::path::{Path, PathBuf}; -use std::fmt::{Display, Formatter, Error as FmtError}; -use super::migration_rocksdb::{Manager as MigrationManager, Config as MigrationConfig, ChangeColumns}; -use super::kvdb_rocksdb::{CompactionProfile, DatabaseConfig}; -use ethcore::client::DatabaseCompactionProfile; -use ethcore; +use super::{ + kvdb_rocksdb::{CompactionProfile, DatabaseConfig}, + migration_rocksdb::{ChangeColumns, Config as MigrationConfig, Manager as MigrationManager}, +}; +use ethcore::{self, client::DatabaseCompactionProfile}; +use std::{ + fmt::{Display, Error as FmtError, Formatter}, + fs, + io::{Error as IoError, ErrorKind, Read, Write}, + path::{Path, PathBuf}, +}; -use super::helpers; -use super::blooms::migrate_blooms; +use super::{blooms::migrate_blooms, helpers}; /// The migration from v10 to v11. /// Adds a column for node info. pub const TO_V11: ChangeColumns = ChangeColumns { - pre_columns: Some(6), - post_columns: Some(7), - version: 11, + pre_columns: Some(6), + post_columns: Some(7), + version: 11, }; /// The migration from v11 to v12. /// Adds a column for light chain storage. pub const TO_V12: ChangeColumns = ChangeColumns { - pre_columns: Some(7), - post_columns: Some(8), - version: 12, + pre_columns: Some(7), + post_columns: Some(8), + version: 12, }; /// Database is assumed to be at default version, when no version file is found. const DEFAULT_VERSION: u32 = 5; /// Current version of database models. -const CURRENT_VERSION: u32 = 13; +const CURRENT_VERSION: u32 = 16; +/// Until this version please use upgrade tool. +const USE_MIGRATION_TOOL: u32 = 15; /// A version of database at which blooms-db was introduced const BLOOMS_DB_VERSION: u32 = 13; /// Defines how many items are migrated to the new version of database at once. @@ -56,176 +60,203 @@ const VERSION_FILE_NAME: &'static str = "db_version"; /// Migration related erorrs. #[derive(Debug)] pub enum Error { - /// Returned when current version cannot be read or guessed. - UnknownDatabaseVersion, - /// Existing DB is newer than the known one. - FutureDBVersion, - /// Migration is not possible. - MigrationImpossible, - /// Blooms-db migration error. - BloomsDB(ethcore::error::Error), - /// Migration was completed succesfully, - /// but there was a problem with io. - Io(IoError), + /// Returned when current version cannot be read or guessed. + UnknownDatabaseVersion, + /// Existing DB is newer than the known one. + FutureDBVersion, + /// Migration is not possible. + MigrationImpossible, + /// For old versions use external migration tool + UseMigrationTool, + /// Blooms-db migration error. + BloomsDB(ethcore::error::Error), + /// Migration was completed succesfully, + /// but there was a problem with io. + Io(IoError), } impl Display for Error { - fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { - let out = match *self { + fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { + let out = match *self { Error::UnknownDatabaseVersion => "Current database version cannot be read".into(), Error::FutureDBVersion => "Database was created with newer client version. Upgrade your client or delete DB and resync.".into(), Error::MigrationImpossible => format!("Database migration to version {} is not possible.", CURRENT_VERSION), - Error::BloomsDB(ref err) => format!("blooms-db migration error: {}", err), + Error::BloomsDB(ref err) => format!("blooms-db migration error: {}", err), + Error::UseMigrationTool => "For db versions 15 and lower (v2.5.13=>13, 2.7.2=>14, v3.0.1=>15) please use upgrade db tool to manually upgrade db: https://github.com/openethereum/3.1-db-upgrade-tool".into(), Error::Io(ref err) => format!("Unexpected io error on DB migration: {}.", err), }; - write!(f, "{}", out) - } + write!(f, "{}", out) + } } impl From for Error { - fn from(err: IoError) -> Self { - Error::Io(err) - } + fn from(err: IoError) -> Self { + Error::Io(err) + } } /// Returns the version file path. fn version_file_path(path: &Path) -> PathBuf { - let mut file_path = path.to_owned(); - file_path.push(VERSION_FILE_NAME); - file_path + let mut file_path = path.to_owned(); + file_path.push(VERSION_FILE_NAME); + file_path } /// Reads current database version from the file at given path. /// If the file does not exist returns `DEFAULT_VERSION`. fn current_version(path: &Path) -> Result { - match fs::File::open(version_file_path(path)) { - Err(ref err) if err.kind() == ErrorKind::NotFound => Ok(DEFAULT_VERSION), - Err(_) => Err(Error::UnknownDatabaseVersion), - Ok(mut file) => { - let mut s = String::new(); - file.read_to_string(&mut s).map_err(|_| Error::UnknownDatabaseVersion)?; - u32::from_str_radix(&s, 10).map_err(|_| Error::UnknownDatabaseVersion) - }, - } + match fs::File::open(version_file_path(path)) { + Err(ref err) if err.kind() == ErrorKind::NotFound => Ok(DEFAULT_VERSION), + Err(_) => Err(Error::UnknownDatabaseVersion), + Ok(mut file) => { + let mut s = String::new(); + file.read_to_string(&mut s) + .map_err(|_| Error::UnknownDatabaseVersion)?; + u32::from_str_radix(&s, 10).map_err(|_| Error::UnknownDatabaseVersion) + } + } } /// Writes current database version to the file. /// Creates a new file if the version file does not exist yet. fn update_version(path: &Path) -> Result<(), Error> { - fs::create_dir_all(path)?; - let mut file = fs::File::create(version_file_path(path))?; - file.write_all(format!("{}", CURRENT_VERSION).as_bytes())?; - Ok(()) + fs::create_dir_all(path)?; + let mut file = fs::File::create(version_file_path(path))?; + file.write_all(format!("{}", CURRENT_VERSION).as_bytes())?; + Ok(()) } /// Consolidated database path fn consolidated_database_path(path: &Path) -> PathBuf { - let mut state_path = path.to_owned(); - state_path.push("db"); - state_path + let mut state_path = path.to_owned(); + state_path.push("db"); + state_path } /// Database backup fn backup_database_path(path: &Path) -> PathBuf { - let mut backup_path = path.to_owned(); - backup_path.pop(); - backup_path.push("temp_backup"); - backup_path + let mut backup_path = path.to_owned(); + backup_path.pop(); + backup_path.push("temp_backup"); + backup_path } /// Default migration settings. pub fn default_migration_settings(compaction_profile: &CompactionProfile) -> MigrationConfig { - MigrationConfig { - batch_size: BATCH_SIZE, - compaction_profile: *compaction_profile, - } + MigrationConfig { + batch_size: BATCH_SIZE, + compaction_profile: *compaction_profile, + } } /// Migrations on the consolidated database. -fn consolidated_database_migrations(compaction_profile: &CompactionProfile) -> Result { - let mut manager = MigrationManager::new(default_migration_settings(compaction_profile)); - manager.add_migration(TO_V11).map_err(|_| Error::MigrationImpossible)?; - manager.add_migration(TO_V12).map_err(|_| Error::MigrationImpossible)?; - Ok(manager) +fn consolidated_database_migrations( + compaction_profile: &CompactionProfile, +) -> Result { + let mut manager = MigrationManager::new(default_migration_settings(compaction_profile)); + manager + .add_migration(TO_V11) + .map_err(|_| Error::MigrationImpossible)?; + manager + .add_migration(TO_V12) + .map_err(|_| Error::MigrationImpossible)?; + Ok(manager) } /// Migrates database at given position with given migration rules. -fn migrate_database(version: u32, db_path: &Path, mut migrations: MigrationManager) -> Result<(), Error> { - // check if migration is needed - if !migrations.is_needed(version) { - return Ok(()) - } - - let backup_path = backup_database_path(&db_path); - // remove the backup dir if it exists - let _ = fs::remove_dir_all(&backup_path); - - // migrate old database to the new one - let temp_path = migrations.execute(&db_path, version)?; - - // completely in-place migration leads to the paths being equal. - // in that case, no need to shuffle directories. - if temp_path == db_path { return Ok(()) } - - // create backup - fs::rename(&db_path, &backup_path)?; - - // replace the old database with the new one - if let Err(err) = fs::rename(&temp_path, &db_path) { - // if something went wrong, bring back backup - fs::rename(&backup_path, &db_path)?; - return Err(err.into()); - } - - // remove backup - fs::remove_dir_all(&backup_path).map_err(Into::into) +fn migrate_database( + version: u32, + db_path: &Path, + mut migrations: MigrationManager, +) -> Result<(), Error> { + // check if migration is needed + if !migrations.is_needed(version) { + return Ok(()); + } + + let backup_path = backup_database_path(&db_path); + // remove the backup dir if it exists + let _ = fs::remove_dir_all(&backup_path); + + // migrate old database to the new one + let temp_path = migrations.execute(&db_path, version)?; + + // completely in-place migration leads to the paths being equal. + // in that case, no need to shuffle directories. + if temp_path == db_path { + return Ok(()); + } + + // create backup + fs::rename(&db_path, &backup_path)?; + + // replace the old database with the new one + if let Err(err) = fs::rename(&temp_path, &db_path) { + // if something went wrong, bring back backup + fs::rename(&backup_path, &db_path)?; + return Err(err.into()); + } + + // remove backup + fs::remove_dir_all(&backup_path).map_err(Into::into) } fn exists(path: &Path) -> bool { - fs::metadata(path).is_ok() + fs::metadata(path).is_ok() } /// Migrates the database. pub fn migrate(path: &Path, compaction_profile: &DatabaseCompactionProfile) -> Result<(), Error> { - let compaction_profile = helpers::compaction_profile(&compaction_profile, path); - - // read version file. - let version = current_version(path)?; - - // migrate the databases. - // main db directory may already exists, so let's check if we have blocks dir - if version > CURRENT_VERSION { - return Err(Error::FutureDBVersion); - } - - // We are in the latest version, yay! - if version == CURRENT_VERSION { - return Ok(()) - } - - let db_path = consolidated_database_path(path); - - // Further migrations - if version < CURRENT_VERSION && exists(&db_path) { - println!("Migrating database from version {} to {}", version, CURRENT_VERSION); - migrate_database(version, &db_path, consolidated_database_migrations(&compaction_profile)?)?; - - if version < BLOOMS_DB_VERSION { - println!("Migrating blooms to blooms-db..."); - let db_config = DatabaseConfig { - max_open_files: 64, - memory_budget: None, - compaction: compaction_profile, - columns: ethcore_db::NUM_COLUMNS, - }; - - migrate_blooms(&db_path, &db_config).map_err(Error::BloomsDB)?; - } - - println!("Migration finished"); - } - - // update version file. - update_version(path) + let compaction_profile = helpers::compaction_profile(&compaction_profile, path); + + // read version file. + let version = current_version(path)?; + + // migrate the databases. + // main db directory may already exists, so let's check if we have blocks dir + if version > CURRENT_VERSION { + return Err(Error::FutureDBVersion); + } + + // We are in the latest version, yay! + if version == CURRENT_VERSION { + return Ok(()); + } + + if version != DEFAULT_VERSION && version <= USE_MIGRATION_TOOL { + return Err(Error::UseMigrationTool); + } + + let db_path = consolidated_database_path(path); + + // Further migrations + if version < CURRENT_VERSION && exists(&db_path) { + println!( + "Migrating database from version {} to {}", + version, CURRENT_VERSION + ); + migrate_database( + version, + &db_path, + consolidated_database_migrations(&compaction_profile)?, + )?; + + if version < BLOOMS_DB_VERSION { + println!("Migrating blooms to blooms-db..."); + let db_config = DatabaseConfig { + max_open_files: 64, + memory_budget: None, + compaction: compaction_profile, + columns: ethcore_db::NUM_COLUMNS, + }; + + migrate_blooms(&db_path, &db_config).map_err(Error::BloomsDB)?; + } + + println!("Migration finished"); + } + + // update version file. + update_version(path) } diff --git a/parity/db/rocksdb/mod.rs b/parity/db/rocksdb/mod.rs index c7aa0a5344d..c923cba2faa 100644 --- a/parity/db/rocksdb/mod.rs +++ b/parity/db/rocksdb/mod.rs @@ -1,117 +1,111 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . +extern crate ethcore_blockchain; extern crate kvdb_rocksdb; extern crate migration_rocksdb; -extern crate ethcore_blockchain; -use std::{io, fs}; -use std::sync::Arc; -use std::path::Path; +use self::{ + ethcore_blockchain::{BlockChainDB, BlockChainDBHandler}, + kvdb_rocksdb::{Database, DatabaseConfig}, +}; use blooms_db; -use ethcore_db::NUM_COLUMNS; -use ethcore::client::{ClientConfig, DatabaseCompactionProfile}; +use ethcore::client::ClientConfig; use kvdb::KeyValueDB; -use self::ethcore_blockchain::{BlockChainDBHandler, BlockChainDB}; -use self::kvdb_rocksdb::{Database, DatabaseConfig}; - -use cache::CacheConfig; +use std::{fs, io, path::Path, sync::Arc}; mod blooms; -mod migration; mod helpers; +mod migration; pub use self::migration::migrate; struct AppDB { - key_value: Arc, - blooms: blooms_db::Database, - trace_blooms: blooms_db::Database, + key_value: Arc, + blooms: blooms_db::Database, + trace_blooms: blooms_db::Database, } impl BlockChainDB for AppDB { - fn key_value(&self) -> &Arc { - &self.key_value - } + fn key_value(&self) -> &Arc { + &self.key_value + } - fn blooms(&self) -> &blooms_db::Database { - &self.blooms - } + fn blooms(&self) -> &blooms_db::Database { + &self.blooms + } - fn trace_blooms(&self) -> &blooms_db::Database { - &self.trace_blooms - } + fn trace_blooms(&self) -> &blooms_db::Database { + &self.trace_blooms + } } /// Open a secret store DB using the given secret store data path. The DB path is one level beneath the data path. #[cfg(feature = "secretstore")] -pub fn open_secretstore_db(data_path: &str) -> Result, String> { - use std::path::PathBuf; - - let mut db_path = PathBuf::from(data_path); - db_path.push("db"); - let db_path = db_path.to_str().ok_or_else(|| "Invalid secretstore path".to_string())?; - Ok(Arc::new(Database::open_default(&db_path).map_err(|e| format!("Error opening database: {:?}", e))?)) +pub fn open_secretstore_db(data_path: &str) -> Result, String> { + use std::path::PathBuf; + + let mut db_path = PathBuf::from(data_path); + db_path.push("db"); + let db_path = db_path + .to_str() + .ok_or_else(|| "Invalid secretstore path".to_string())?; + Ok(Arc::new( + Database::open_default(&db_path).map_err(|e| format!("Error opening database: {:?}", e))?, + )) } /// Create a restoration db handler using the config generated by `client_path` and `client_config`. -pub fn restoration_db_handler(client_path: &Path, client_config: &ClientConfig) -> Box { - let client_db_config = helpers::client_db_config(client_path, client_config); - - struct RestorationDBHandler { - config: DatabaseConfig, - } - - impl BlockChainDBHandler for RestorationDBHandler { - fn open(&self, db_path: &Path) -> io::Result> { - open_database(&db_path.to_string_lossy(), &self.config) - } - } - - Box::new(RestorationDBHandler { - config: client_db_config, - }) -} - -/// Open a new main DB. -pub fn open_db(client_path: &str, cache_config: &CacheConfig, compaction: &DatabaseCompactionProfile) -> io::Result> { - let path = Path::new(client_path); - - let db_config = DatabaseConfig { - memory_budget: Some(cache_config.blockchain() as usize * 1024 * 1024), - compaction: helpers::compaction_profile(&compaction, path), - .. DatabaseConfig::with_columns(NUM_COLUMNS) - }; - - open_database(client_path, &db_config) +pub fn restoration_db_handler( + client_path: &Path, + client_config: &ClientConfig, +) -> Box { + let client_db_config = helpers::client_db_config(client_path, client_config); + + struct RestorationDBHandler { + config: DatabaseConfig, + } + + impl BlockChainDBHandler for RestorationDBHandler { + fn open(&self, db_path: &Path) -> io::Result> { + open_database(&db_path.to_string_lossy(), &self.config) + } + } + + Box::new(RestorationDBHandler { + config: client_db_config, + }) } -pub fn open_database(client_path: &str, config: &DatabaseConfig) -> io::Result> { - let path = Path::new(client_path); +pub fn open_database( + client_path: &str, + config: &DatabaseConfig, +) -> io::Result> { + let path = Path::new(client_path); - let blooms_path = path.join("blooms"); - let trace_blooms_path = path.join("trace_blooms"); - fs::create_dir_all(&blooms_path)?; - fs::create_dir_all(&trace_blooms_path)?; + let blooms_path = path.join("blooms"); + let trace_blooms_path = path.join("trace_blooms"); + fs::create_dir_all(&blooms_path)?; + fs::create_dir_all(&trace_blooms_path)?; - let db = AppDB { - key_value: Arc::new(Database::open(&config, client_path)?), - blooms: blooms_db::Database::open(blooms_path)?, - trace_blooms: blooms_db::Database::open(trace_blooms_path)?, - }; + let db = AppDB { + key_value: Arc::new(Database::open(&config, client_path)?), + blooms: blooms_db::Database::open(blooms_path)?, + trace_blooms: blooms_db::Database::open(trace_blooms_path)?, + }; - Ok(Arc::new(db)) + Ok(Arc::new(db)) } diff --git a/parity/deprecated.rs b/parity/deprecated.rs deleted file mode 100644 index 49155225aa0..00000000000 --- a/parity/deprecated.rs +++ /dev/null @@ -1,287 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::fmt; -use cli::Args; - -#[derive(Debug, PartialEq)] -pub enum Deprecated { - DoesNothing(&'static str), - Replaced(&'static str, &'static str), - Removed(&'static str), -} - -impl fmt::Display for Deprecated { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match *self { - Deprecated::DoesNothing(s) => write!(f, "Option '{}' does nothing. It's on by default.", s), - Deprecated::Replaced(old, new) => write!(f, "Option '{}' is deprecated. Please use '{}' instead.", old, new), - Deprecated::Removed(s) => write!(f, "Option '{}' has been removed and is no longer supported.", s) - } - } -} - -pub fn find_deprecated(args: &Args) -> Vec { - let mut result = vec![]; - - // Removed in 1.6 or before. - - if args.flag_warp { - result.push(Deprecated::DoesNothing("--warp")); - } - - if args.flag_jsonrpc { - result.push(Deprecated::DoesNothing("--jsonrpc")); - } - - if args.flag_rpc { - result.push(Deprecated::DoesNothing("--rpc")); - } - - if args.flag_jsonrpc_off { - result.push(Deprecated::Replaced("--jsonrpc-off", "--no-jsonrpc")); - } - - if args.flag_webapp { - result.push(Deprecated::DoesNothing("--webapp")); - } - - if args.flag_dapps_off { - result.push(Deprecated::Replaced("--dapps-off", "--no-dapps")); - } - - if args.flag_ipcdisable { - result.push(Deprecated::Replaced("--ipcdisable", "--no-ipc")); - } - - if args.flag_ipc_off { - result.push(Deprecated::Replaced("--ipc-off", "--no-ipc")); - } - - if args.arg_etherbase.is_some() { - result.push(Deprecated::Replaced("--etherbase", "--author")); - } - - if args.arg_extradata.is_some() { - result.push(Deprecated::Replaced("--extradata", "--extra-data")); - } - - if args.flag_testnet { - result.push(Deprecated::Replaced("--testnet", "--chain testnet")); - } - - if args.flag_nodiscover { - result.push(Deprecated::Replaced("--nodiscover", "--no-discovery")); - } - - if args.arg_datadir.is_some() { - result.push(Deprecated::Replaced("--datadir", "--base-path")); - } - - if args.arg_networkid.is_some() { - result.push(Deprecated::Replaced("--networkid", "--network-id")); - } - - if args.arg_peers.is_some() { - result.push(Deprecated::Replaced("--peers", "--min-peers")); - } - - if args.arg_nodekey.is_some() { - result.push(Deprecated::Replaced("--nodekey", "--node-key")); - } - - if args.arg_rpcaddr.is_some() { - result.push(Deprecated::Replaced("--rpcaddr", "--jsonrpc-interface")); - } - - if args.arg_rpcport.is_some() { - result.push(Deprecated::Replaced("--rpcport", "--jsonrpc-port")); - } - - if args.arg_rpcapi.is_some() { - result.push(Deprecated::Replaced("--rpcapi", "--jsonrpc-api")); - } - - if args.arg_rpccorsdomain.is_some() { - result.push(Deprecated::Replaced("--rpccorsdomain", "--jsonrpc-cors")); - } - - if args.arg_ipcapi.is_some() { - result.push(Deprecated::Replaced("--ipcapi", "--ipc-apis")); - } - - if args.arg_ipcpath.is_some() { - result.push(Deprecated::Replaced("--ipcpath", "--ipc-path")); - } - - if args.arg_gasprice.is_some() { - result.push(Deprecated::Replaced("--gasprice", "--min-gas-price")); - } - - if args.arg_cache.is_some() { - result.push(Deprecated::Replaced("--cache", "--cache-size")); - } - - // Removed in 1.7. - - if args.arg_dapps_port.is_some() { - result.push(Deprecated::Removed("--dapps-port")); - } - - if args.arg_dapps_interface.is_some() { - result.push(Deprecated::Removed("--dapps-interface")); - } - - if args.arg_dapps_hosts.is_some() { - result.push(Deprecated::Removed("--dapps-hosts")); - } - - if args.arg_dapps_cors.is_some() { - result.push(Deprecated::Removed("--dapps-cors")); - } - - if args.arg_dapps_user.is_some() { - result.push(Deprecated::Removed("--dapps-user")); - } - - if args.arg_dapps_pass.is_some() { - result.push(Deprecated::Removed("--dapps-pass")); - } - - if args.flag_dapps_apis_all { - result.push(Deprecated::Replaced("--dapps-apis-all", "--jsonrpc-apis")); - } - - // Removed in 1.11. - - if args.flag_public_node { - result.push(Deprecated::Removed("--public-node")); - } - - if args.flag_force_ui { - result.push(Deprecated::Removed("--force-ui")); - } - - if args.flag_no_ui { - result.push(Deprecated::Removed("--no-ui")); - } - - if args.flag_ui_no_validation { - result.push(Deprecated::Removed("--ui-no-validation")); - } - - if args.arg_ui_interface.is_some() { - result.push(Deprecated::Removed("--ui-interface")); - } - - if args.arg_ui_hosts.is_some() { - result.push(Deprecated::Removed("--ui-hosts")); - } - - if args.arg_ui_port.is_some() { - result.push(Deprecated::Removed("--ui-port")); - } - - if args.arg_tx_queue_ban_count.is_some() { - result.push(Deprecated::Removed("--tx-queue-ban-count")); - } - - if args.arg_tx_queue_ban_time.is_some() { - result.push(Deprecated::Removed("--tx-queue-ban-time")); - } - - // Removed in 2.0. - - if args.flag_fast_and_loose { - result.push(Deprecated::Removed("--fast-and-loose")); - } - - if args.cmd_dapp { - result.push(Deprecated::Removed("parity dapp")); - } - - if args.arg_dapp_path.is_some() { - result.push(Deprecated::Removed("--dapp-path")); - } - - if args.flag_no_dapps { - result.push(Deprecated::Removed("--no-dapps")); - } - - if args.arg_dapps_path.is_some() { - result.push(Deprecated::Removed("--dapps-path")); - } - - if args.arg_ntp_servers.is_some() { - result.push(Deprecated::Removed("--ntp-servers")); - } - - result -} - -#[cfg(test)] -mod tests { - use cli::Args; - use super::{Deprecated, find_deprecated}; - - #[test] - fn test_find_deprecated() { - assert_eq!(find_deprecated(&Args::default()), vec![]); - assert_eq!(find_deprecated(&{ - let mut args = Args::default(); - args.flag_warp = true; - args.flag_jsonrpc = true; - args.flag_rpc = true; - args.flag_jsonrpc_off = true; - args.flag_webapp = true; - args.flag_dapps_off = true; - args.flag_ipcdisable = true; - args.flag_ipc_off = true; - args.arg_etherbase = Some(Default::default()); - args.arg_extradata = Some(Default::default()); - args.arg_dapps_port = Some(Default::default()); - args.arg_dapps_interface = Some(Default::default()); - args.arg_dapps_hosts = Some(Default::default()); - args.arg_dapps_cors = Some(Default::default()); - args.arg_dapps_user = Some(Default::default()); - args.arg_dapps_pass = Some(Default::default()); - args.flag_dapps_apis_all = true; - args.flag_fast_and_loose = true; - args.arg_ntp_servers = Some(Default::default()); - args - }), vec![ - Deprecated::DoesNothing("--warp"), - Deprecated::DoesNothing("--jsonrpc"), - Deprecated::DoesNothing("--rpc"), - Deprecated::Replaced("--jsonrpc-off", "--no-jsonrpc"), - Deprecated::DoesNothing("--webapp"), - Deprecated::Replaced("--dapps-off", "--no-dapps"), - Deprecated::Replaced("--ipcdisable", "--no-ipc"), - Deprecated::Replaced("--ipc-off", "--no-ipc"), - Deprecated::Replaced("--etherbase", "--author"), - Deprecated::Replaced("--extradata", "--extra-data"), - Deprecated::Removed("--dapps-port"), - Deprecated::Removed("--dapps-interface"), - Deprecated::Removed("--dapps-hosts"), - Deprecated::Removed("--dapps-cors"), - Deprecated::Removed("--dapps-user"), - Deprecated::Removed("--dapps-pass"), - Deprecated::Replaced("--dapps-apis-all", "--jsonrpc-apis"), - Deprecated::Removed("--fast-and-loose"), - Deprecated::Removed("--ntp-servers"), - ]); - } -} diff --git a/parity/export_hardcoded_sync.rs b/parity/export_hardcoded_sync.rs deleted file mode 100644 index 0e527b3413b..00000000000 --- a/parity/export_hardcoded_sync.rs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::time::Duration; - -use ethcore::client::DatabaseCompactionProfile; -use ethcore::spec::{SpecParams, OptimizeFor}; -use light::client::fetch::Unavailable as UnavailableDataFetcher; -use light::Cache as LightDataCache; - -use params::{SpecType, Pruning}; -use helpers::execute_upgrades; -use dir::Directories; -use cache::CacheConfig; -use user_defaults::UserDefaults; -use db; - -// Number of minutes before a given gas price corpus should expire. -// Light client only. -const GAS_CORPUS_EXPIRATION_MINUTES: u64 = 60 * 6; - -#[derive(Debug, PartialEq)] -pub struct ExportHsyncCmd { - pub cache_config: CacheConfig, - pub dirs: Directories, - pub spec: SpecType, - pub pruning: Pruning, - pub compaction: DatabaseCompactionProfile, -} - -pub fn execute(cmd: ExportHsyncCmd) -> Result { - use light::client as light_client; - use parking_lot::Mutex; - - // load spec - let spec = cmd.spec.spec(SpecParams::new(cmd.dirs.cache.as_ref(), OptimizeFor::Memory))?; - - // load genesis hash - let genesis_hash = spec.genesis_header().hash(); - - // database paths - let db_dirs = cmd.dirs.database(genesis_hash, cmd.spec.legacy_fork_name(), spec.data_dir.clone()); - - // user defaults path - let user_defaults_path = db_dirs.user_defaults_path(); - - // load user defaults - let user_defaults = UserDefaults::load(&user_defaults_path)?; - - // select pruning algorithm - let algorithm = cmd.pruning.to_algorithm(&user_defaults); - - // execute upgrades - execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?; - - // create dirs used by parity - cmd.dirs.create_dirs(false, false)?; - - // TODO: configurable cache size. - let cache = LightDataCache::new(Default::default(), Duration::from_secs(60 * GAS_CORPUS_EXPIRATION_MINUTES)); - let cache = Arc::new(Mutex::new(cache)); - - // start client and create transaction queue. - let mut config = light_client::Config { - queue: Default::default(), - chain_column: ::ethcore_db::COL_LIGHT_CHAIN, - verify_full: true, - check_seal: true, - no_hardcoded_sync: true, - }; - - config.queue.max_mem_use = cmd.cache_config.queue() as usize * 1024 * 1024; - - // initialize database. - let db = db::open_db(&db_dirs.client_path(algorithm).to_str().expect("DB path could not be converted to string."), - &cmd.cache_config, - &cmd.compaction).map_err(|e| format!("Failed to open database {:?}", e))?; - - let service = light_client::Service::start(config, &spec, UnavailableDataFetcher, db, cache) - .map_err(|e| format!("Error starting light client: {}", e))?; - - let hs = service.client().read_hardcoded_sync() - .map_err(|e| format!("Error reading hardcoded sync: {}", e))?; - if let Some(hs) = hs { - Ok(::serde_json::to_string_pretty(&hs.to_json()).expect("generated JSON is always valid")) - } else { - Err("Error: cannot generate hardcoded sync because the database is empty.".into()) - } -} diff --git a/parity/helpers.rs b/parity/helpers.rs index b68d854d1a9..76d28defd33 100644 --- a/parity/helpers.rs +++ b/parity/helpers.rs @@ -1,324 +1,360 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::io; -use std::io::{Write, BufReader, BufRead}; -use std::time::Duration; -use std::fs::File; -use std::collections::HashSet; -use ethereum_types::{U256, clean_0x, Address}; -use journaldb::Algorithm; -use ethcore::client::{Mode, BlockId, VMType, DatabaseCompactionProfile, ClientConfig, VerifierType}; -use ethcore::miner::{PendingSet, Penalization}; -use miner::pool::PrioritizationStrategy; +// along with OpenEthereum. If not, see . + use cache::CacheConfig; -use dir::DatabaseDirectories; -use dir::helpers::replace_home; -use upgrade::{upgrade, upgrade_data_paths}; -use sync::{validate_node_url, self}; use db::migrate; -use path; +use dir::{helpers::replace_home, DatabaseDirectories}; +use ethcore::{ + client::{BlockId, ClientConfig, DatabaseCompactionProfile, Mode, VMType, VerifierType}, + miner::{Penalization, PendingSet}, +}; +use ethereum_types::{clean_0x, Address, U256}; use ethkey::Password; +use journaldb::Algorithm; +use miner::pool::PrioritizationStrategy; +use std::{ + collections::HashSet, + fs::File, + io, + io::{BufRead, BufReader, Write}, + time::Duration, +}; +use sync::{self, validate_node_url}; +use upgrade::{upgrade, upgrade_data_paths}; pub fn to_duration(s: &str) -> Result { - to_seconds(s).map(Duration::from_secs) + to_seconds(s).map(Duration::from_secs) } fn to_seconds(s: &str) -> Result { - let bad = |_| { - format!("{}: Invalid duration given. See parity --help for more information.", s) - }; - - match s { - "twice-daily" => Ok(12 * 60 * 60), - "half-hourly" => Ok(30 * 60), - "1second" | "1 second" | "second" => Ok(1), - "1minute" | "1 minute" | "minute" => Ok(60), - "hourly" | "1hour" | "1 hour" | "hour" => Ok(60 * 60), - "daily" | "1day" | "1 day" | "day" => Ok(24 * 60 * 60), - x if x.ends_with("seconds") => x[0..x.len() - 7].trim().parse().map_err(bad), - x if x.ends_with("minutes") => x[0..x.len() - 7].trim().parse::().map_err(bad).map(|x| x * 60), - x if x.ends_with("hours") => x[0..x.len() - 5].trim().parse::().map_err(bad).map(|x| x * 60 * 60), - x if x.ends_with("days") => x[0..x.len() - 4].trim().parse::().map_err(bad).map(|x| x * 24 * 60 * 60), - x => x.trim().parse().map_err(bad), - } + let bad = |_| { + format!( + "{}: Invalid duration given. See openethereum --help for more information.", + s + ) + }; + + match s { + "twice-daily" => Ok(12 * 60 * 60), + "half-hourly" => Ok(30 * 60), + "1second" | "1 second" | "second" => Ok(1), + "1minute" | "1 minute" | "minute" => Ok(60), + "hourly" | "1hour" | "1 hour" | "hour" => Ok(60 * 60), + "daily" | "1day" | "1 day" | "day" => Ok(24 * 60 * 60), + x if x.ends_with("seconds") => x[0..x.len() - 7].trim().parse().map_err(bad), + x if x.ends_with("minutes") => x[0..x.len() - 7] + .trim() + .parse::() + .map_err(bad) + .map(|x| x * 60), + x if x.ends_with("hours") => x[0..x.len() - 5] + .trim() + .parse::() + .map_err(bad) + .map(|x| x * 60 * 60), + x if x.ends_with("days") => x[0..x.len() - 4] + .trim() + .parse::() + .map_err(bad) + .map(|x| x * 24 * 60 * 60), + x => x.trim().parse().map_err(bad), + } } pub fn to_mode(s: &str, timeout: u64, alarm: u64) -> Result { - match s { - "active" => Ok(Mode::Active), - "passive" => Ok(Mode::Passive(Duration::from_secs(timeout), Duration::from_secs(alarm))), - "dark" => Ok(Mode::Dark(Duration::from_secs(timeout))), - "offline" => Ok(Mode::Off), - _ => Err(format!("{}: Invalid value for --mode. Must be one of active, passive, dark or offline.", s)), - } + match s { + "active" => Ok(Mode::Active), + "passive" => Ok(Mode::Passive( + Duration::from_secs(timeout), + Duration::from_secs(alarm), + )), + "dark" => Ok(Mode::Dark(Duration::from_secs(timeout))), + "offline" => Ok(Mode::Off), + _ => Err(format!( + "{}: Invalid value for --mode. Must be one of active, passive, dark or offline.", + s + )), + } } pub fn to_block_id(s: &str) -> Result { - if s == "latest" { - Ok(BlockId::Latest) - } else if let Ok(num) = s.parse() { - Ok(BlockId::Number(num)) - } else if let Ok(hash) = s.parse() { - Ok(BlockId::Hash(hash)) - } else { - Err("Invalid block.".into()) - } + if s == "latest" { + Ok(BlockId::Latest) + } else if let Ok(num) = s.parse() { + Ok(BlockId::Number(num)) + } else if let Ok(hash) = s.parse() { + Ok(BlockId::Hash(hash)) + } else { + Err("Invalid block.".into()) + } } pub fn to_u256(s: &str) -> Result { - if let Ok(decimal) = U256::from_dec_str(s) { - Ok(decimal) - } else if let Ok(hex) = clean_0x(s).parse() { - Ok(hex) - } else { - Err(format!("Invalid numeric value: {}", s)) - } + if let Ok(decimal) = U256::from_dec_str(s) { + Ok(decimal) + } else if let Ok(hex) = clean_0x(s).parse() { + Ok(hex) + } else { + Err(format!("Invalid numeric value: {}", s)) + } } pub fn to_pending_set(s: &str) -> Result { - match s { - "cheap" => Ok(PendingSet::AlwaysQueue), - "strict" => Ok(PendingSet::AlwaysSealing), - "lenient" => Ok(PendingSet::SealingOrElseQueue), - other => Err(format!("Invalid pending set value: {:?}", other)), - } + match s { + "cheap" => Ok(PendingSet::AlwaysQueue), + "strict" => Ok(PendingSet::AlwaysSealing), + "lenient" => Ok(PendingSet::SealingOrElseQueue), + other => Err(format!("Invalid pending set value: {:?}", other)), + } } pub fn to_queue_strategy(s: &str) -> Result { - match s { - "gas_price" => Ok(PrioritizationStrategy::GasPriceOnly), - other => Err(format!("Invalid queue strategy: {}", other)), - } + match s { + "gas_price" => Ok(PrioritizationStrategy::GasPriceOnly), + other => Err(format!("Invalid queue strategy: {}", other)), + } } pub fn to_queue_penalization(time: Option) -> Result { - Ok(match time { - Some(threshold_ms) => Penalization::Enabled { - offend_threshold: Duration::from_millis(threshold_ms), - }, - None => Penalization::Disabled, - }) + Ok(match time { + Some(threshold_ms) => Penalization::Enabled { + offend_threshold: Duration::from_millis(threshold_ms), + }, + None => Penalization::Disabled, + }) } pub fn to_address(s: Option) -> Result { - match s { - Some(ref a) => clean_0x(a).parse().map_err(|_| format!("Invalid address: {:?}", a)), - None => Ok(Address::default()) - } + match s { + Some(ref a) => clean_0x(a) + .parse() + .map_err(|_| format!("Invalid address: {:?}", a)), + None => Ok(Address::default()), + } } pub fn to_addresses(s: &Option) -> Result, String> { - match *s { - Some(ref adds) if !adds.is_empty() => adds.split(',') - .map(|a| clean_0x(a).parse().map_err(|_| format!("Invalid address: {:?}", a))) - .collect(), - _ => Ok(Vec::new()), - } + match *s { + Some(ref adds) if !adds.is_empty() => adds + .split(',') + .map(|a| { + clean_0x(a) + .parse() + .map_err(|_| format!("Invalid address: {:?}", a)) + }) + .collect(), + _ => Ok(Vec::new()), + } } /// Tries to parse string as a price. pub fn to_price(s: &str) -> Result { - s.parse::().map_err(|_| format!("Invalid transaciton price 's' given. Must be a decimal number.")) + s.parse::().map_err(|_| { + format!( + "Invalid transaction price {:?} given. Must be a decimal number.", + s + ) + }) } pub fn join_set(set: Option<&HashSet>) -> Option { - match set { - Some(s) => Some(s.iter().map(|s| s.as_str()).collect::>().join(",")), - None => None - } + match set { + Some(s) => Some( + s.iter() + .map(|s| s.as_str()) + .collect::>() + .join(","), + ), + None => None, + } } /// Flush output buffer. pub fn flush_stdout() { - io::stdout().flush().expect("stdout is flushable; qed"); -} - -/// Returns default geth ipc path. -pub fn geth_ipc_path(testnet: bool) -> String { - // Windows path should not be hardcoded here. - // Instead it should be a part of path::ethereum - if cfg!(windows) { - return r"\\.\pipe\geth.ipc".to_owned(); - } - - if testnet { - path::ethereum::with_testnet("geth.ipc").to_str().unwrap().to_owned() - } else { - path::ethereum::with_default("geth.ipc").to_str().unwrap().to_owned() - } + io::stdout().flush().expect("stdout is flushable; qed"); } /// Formats and returns parity ipc path. pub fn parity_ipc_path(base: &str, path: &str, shift: u16) -> String { - let mut path = path.to_owned(); - if shift != 0 { - path = path.replace("jsonrpc.ipc", &format!("jsonrpc-{}.ipc", shift)); - } - replace_home(base, &path) + let mut path = path.to_owned(); + if shift != 0 { + path = path.replace("jsonrpc.ipc", &format!("jsonrpc-{}.ipc", shift)); + } + replace_home(base, &path) } /// Validates and formats bootnodes option. pub fn to_bootnodes(bootnodes: &Option) -> Result, String> { - match *bootnodes { - Some(ref x) if !x.is_empty() => x.split(',').map(|s| { - match validate_node_url(s).map(Into::into) { - None => Ok(s.to_owned()), - Some(sync::ErrorKind::AddressResolve(_)) => Err(format!("Failed to resolve hostname of a boot node: {}", s)), - Some(_) => Err(format!("Invalid node address format given for a boot node: {}", s)), - } - }).collect(), - Some(_) => Ok(vec![]), - None => Ok(vec![]) - } + match *bootnodes { + Some(ref x) if !x.is_empty() => x + .split(',') + .map(|s| match validate_node_url(s).map(Into::into) { + None => Ok(s.to_owned()), + Some(sync::ErrorKind::AddressResolve(_)) => { + Err(format!("Failed to resolve hostname of a boot node: {}", s)) + } + Some(_) => Err(format!( + "Invalid node address format given for a boot node: {}", + s + )), + }) + .collect(), + Some(_) => Ok(vec![]), + None => Ok(vec![]), + } } #[cfg(test)] pub fn default_network_config() -> ::sync::NetworkConfiguration { - use sync::{NetworkConfiguration}; - use super::network::IpFilter; - NetworkConfiguration { - config_path: Some(replace_home(&::dir::default_data_path(), "$BASE/network")), - net_config_path: None, - listen_address: Some("0.0.0.0:30303".into()), - public_address: None, - udp_port: None, - nat_enabled: true, - discovery_enabled: true, - boot_nodes: Vec::new(), - use_secret: None, - max_peers: 50, - min_peers: 25, - snapshot_peers: 0, - max_pending_peers: 64, - ip_filter: IpFilter::default(), - reserved_nodes: Vec::new(), - allow_non_reserved: true, - client_version: ::parity_version::version(), - } + use super::network::IpFilter; + use sync::NetworkConfiguration; + NetworkConfiguration { + config_path: Some(replace_home(&::dir::default_data_path(), "$BASE/network")), + net_config_path: None, + listen_address: Some("0.0.0.0:30303".into()), + public_address: None, + udp_port: None, + nat_enabled: true, + discovery_enabled: true, + boot_nodes: Vec::new(), + use_secret: None, + max_peers: 50, + min_peers: 25, + snapshot_peers: 0, + max_pending_peers: 64, + ip_filter: IpFilter::default(), + reserved_nodes: Vec::new(), + allow_non_reserved: true, + client_version: ::parity_version::version(), + } } pub fn to_client_config( - cache_config: &CacheConfig, - spec_name: String, - mode: Mode, - tracing: bool, - fat_db: bool, - compaction: DatabaseCompactionProfile, - vm_type: VMType, - name: String, - pruning: Algorithm, - pruning_history: u64, - pruning_memory: usize, - check_seal: bool, - max_round_blocks_to_import: usize, + cache_config: &CacheConfig, + spec_name: String, + mode: Mode, + tracing: bool, + fat_db: bool, + compaction: DatabaseCompactionProfile, + vm_type: VMType, + name: String, + pruning: Algorithm, + pruning_history: u64, + pruning_memory: usize, + check_seal: bool, + max_round_blocks_to_import: usize, ) -> ClientConfig { - let mut client_config = ClientConfig::default(); - - let mb = 1024 * 1024; - // in bytes - client_config.blockchain.max_cache_size = cache_config.blockchain() as usize * mb; - // in bytes - client_config.blockchain.pref_cache_size = cache_config.blockchain() as usize * 3 / 4 * mb; - // db cache size, in megabytes - client_config.db_cache_size = Some(cache_config.db_cache_size() as usize); - // db queue cache size, in bytes - client_config.queue.max_mem_use = cache_config.queue() as usize * mb; - // in bytes - client_config.tracing.max_cache_size = cache_config.traces() as usize * mb; - // in bytes - client_config.tracing.pref_cache_size = cache_config.traces() as usize * 3 / 4 * mb; - // in bytes - client_config.state_cache_size = cache_config.state() as usize * mb; - // in bytes - client_config.jump_table_size = cache_config.jump_tables() as usize * mb; - // in bytes - client_config.history_mem = pruning_memory * mb; - - client_config.mode = mode; - client_config.tracing.enabled = tracing; - client_config.fat_db = fat_db; - client_config.pruning = pruning; - client_config.history = pruning_history; - client_config.db_compaction = compaction; - client_config.vm_type = vm_type; - client_config.name = name; - client_config.verifier_type = if check_seal { VerifierType::Canon } else { VerifierType::CanonNoSeal }; - client_config.spec_name = spec_name; - client_config.max_round_blocks_to_import = max_round_blocks_to_import; - client_config + let mut client_config = ClientConfig::default(); + + let mb = 1024 * 1024; + // in bytes + client_config.blockchain.max_cache_size = cache_config.blockchain() as usize * mb; + // in bytes + client_config.blockchain.pref_cache_size = cache_config.blockchain() as usize * 3 / 4 * mb; + // db cache size, in megabytes + client_config.db_cache_size = Some(cache_config.db_cache_size() as usize); + // db queue cache size, in bytes + client_config.queue.max_mem_use = cache_config.queue() as usize * mb; + // in bytes + client_config.tracing.max_cache_size = cache_config.traces() as usize * mb; + // in bytes + client_config.tracing.pref_cache_size = cache_config.traces() as usize * 3 / 4 * mb; + // in bytes + client_config.state_cache_size = cache_config.state() as usize * mb; + // in bytes + client_config.jump_table_size = cache_config.jump_tables() as usize * mb; + // in bytes + client_config.history_mem = pruning_memory * mb; + + client_config.mode = mode; + client_config.tracing.enabled = tracing; + client_config.fat_db = fat_db; + client_config.pruning = pruning; + client_config.history = pruning_history; + client_config.db_compaction = compaction; + client_config.vm_type = vm_type; + client_config.name = name; + client_config.verifier_type = if check_seal { + VerifierType::Canon + } else { + VerifierType::CanonNoSeal + }; + client_config.spec_name = spec_name; + client_config.max_round_blocks_to_import = max_round_blocks_to_import; + client_config } pub fn execute_upgrades( - base_path: &str, - dirs: &DatabaseDirectories, - pruning: Algorithm, - compaction_profile: &DatabaseCompactionProfile + base_path: &str, + dirs: &DatabaseDirectories, + pruning: Algorithm, + compaction_profile: &DatabaseCompactionProfile, ) -> Result<(), String> { - - upgrade_data_paths(base_path, dirs, pruning); - - match upgrade(&dirs.path) { - Ok(upgrades_applied) if upgrades_applied > 0 => { - debug!("Executed {} upgrade scripts - ok", upgrades_applied); - }, - Err(e) => { - return Err(format!("Error upgrading parity data: {:?}", e)); - }, - _ => {}, - } - - let client_path = dirs.db_path(pruning); - migrate(&client_path, compaction_profile).map_err(|e| format!("{}", e)) + upgrade_data_paths(base_path, dirs, pruning); + + match upgrade(&dirs.path) { + Ok(upgrades_applied) if upgrades_applied > 0 => { + debug!("Executed {} upgrade scripts - ok", upgrades_applied); + } + Err(e) => { + return Err(format!("Error upgrading OpenEthereum data: {:?}", e)); + } + _ => {} + } + + let client_path = dirs.db_path(pruning); + migrate(&client_path, compaction_profile).map_err(|e| format!("{}", e)) } /// Prompts user asking for password. pub fn password_prompt() -> Result { - use rpassword::read_password; - const STDIN_ERROR: &'static str = "Unable to ask for password on non-interactive terminal."; + use rpassword::read_password; + const STDIN_ERROR: &'static str = "Unable to ask for password on non-interactive terminal."; - println!("Please note that password is NOT RECOVERABLE."); - print!("Type password: "); - flush_stdout(); + println!("Please note that password is NOT RECOVERABLE."); + print!("Type password: "); + flush_stdout(); - let password = read_password().map_err(|_| STDIN_ERROR.to_owned())?.into(); + let password = read_password().map_err(|_| STDIN_ERROR.to_owned())?.into(); - print!("Repeat password: "); - flush_stdout(); + print!("Repeat password: "); + flush_stdout(); - let password_repeat = read_password().map_err(|_| STDIN_ERROR.to_owned())?.into(); + let password_repeat = read_password().map_err(|_| STDIN_ERROR.to_owned())?.into(); - if password != password_repeat { - return Err("Passwords do not match!".into()); - } + if password != password_repeat { + return Err("Passwords do not match!".into()); + } - Ok(password) + Ok(password) } /// Read a password from password file. pub fn password_from_file(path: String) -> Result { - let passwords = passwords_from_files(&[path])?; - // use only first password from the file - passwords.get(0).map(Password::clone) - .ok_or_else(|| "Password file seems to be empty.".to_owned()) + let passwords = passwords_from_files(&[path])?; + // use only first password from the file + passwords + .get(0) + .map(Password::clone) + .ok_or_else(|| "Password file seems to be empty.".to_owned()) } /// Reads passwords from files. Treats each line as a separate password. pub fn passwords_from_files(files: &[String]) -> Result, String> { - let passwords = files.iter().map(|filename| { + let passwords = files.iter().map(|filename| { let file = File::open(filename).map_err(|_| format!("{} Unable to read password file. Ensure it exists and permissions are correct.", filename))?; let reader = BufReader::new(&file); let lines = reader.lines() @@ -327,174 +363,231 @@ pub fn passwords_from_files(files: &[String]) -> Result, String> { .collect::>(); Ok(lines) }).collect::>, String>>(); - Ok(passwords?.into_iter().flat_map(|x| x).collect()) + Ok(passwords?.into_iter().flat_map(|x| x).collect()) } #[cfg(test)] mod tests { - use std::time::Duration; - use std::fs::File; - use std::io::Write; - use std::collections::HashSet; - use tempdir::TempDir; - use ethereum_types::U256; - use ethcore::client::{Mode, BlockId}; - use ethcore::miner::PendingSet; - use ethkey::Password; - use super::{to_duration, to_mode, to_block_id, to_u256, to_pending_set, to_address, to_addresses, to_price, geth_ipc_path, to_bootnodes, join_set, password_from_file}; - - #[test] - fn test_to_duration() { - assert_eq!(to_duration("twice-daily").unwrap(), Duration::from_secs(12 * 60 * 60)); - assert_eq!(to_duration("half-hourly").unwrap(), Duration::from_secs(30 * 60)); - assert_eq!(to_duration("1second").unwrap(), Duration::from_secs(1)); - assert_eq!(to_duration("2seconds").unwrap(), Duration::from_secs(2)); - assert_eq!(to_duration("15seconds").unwrap(), Duration::from_secs(15)); - assert_eq!(to_duration("1minute").unwrap(), Duration::from_secs(1 * 60)); - assert_eq!(to_duration("2minutes").unwrap(), Duration::from_secs(2 * 60)); - assert_eq!(to_duration("15minutes").unwrap(), Duration::from_secs(15 * 60)); - assert_eq!(to_duration("hourly").unwrap(), Duration::from_secs(60 * 60)); - assert_eq!(to_duration("daily").unwrap(), Duration::from_secs(24 * 60 * 60)); - assert_eq!(to_duration("1hour").unwrap(), Duration::from_secs(1 * 60 * 60)); - assert_eq!(to_duration("2hours").unwrap(), Duration::from_secs(2 * 60 * 60)); - assert_eq!(to_duration("15hours").unwrap(), Duration::from_secs(15 * 60 * 60)); - assert_eq!(to_duration("1day").unwrap(), Duration::from_secs(1 * 24 * 60 * 60)); - assert_eq!(to_duration("2days").unwrap(), Duration::from_secs(2 * 24 *60 * 60)); - assert_eq!(to_duration("15days").unwrap(), Duration::from_secs(15 * 24 * 60 * 60)); - assert_eq!(to_duration("15 days").unwrap(), Duration::from_secs(15 * 24 * 60 * 60)); - assert_eq!(to_duration("2 seconds").unwrap(), Duration::from_secs(2)); - } - - #[test] - fn test_to_mode() { - assert_eq!(to_mode("active", 0, 0).unwrap(), Mode::Active); - assert_eq!(to_mode("passive", 10, 20).unwrap(), Mode::Passive(Duration::from_secs(10), Duration::from_secs(20))); - assert_eq!(to_mode("dark", 20, 30).unwrap(), Mode::Dark(Duration::from_secs(20))); - assert!(to_mode("other", 20, 30).is_err()); - } - - #[test] - fn test_to_block_id() { - assert_eq!(to_block_id("latest").unwrap(), BlockId::Latest); - assert_eq!(to_block_id("0").unwrap(), BlockId::Number(0)); - assert_eq!(to_block_id("2").unwrap(), BlockId::Number(2)); - assert_eq!(to_block_id("15").unwrap(), BlockId::Number(15)); - assert_eq!( - to_block_id("9fc84d84f6a785dc1bd5abacfcf9cbdd3b6afb80c0f799bfb2fd42c44a0c224e").unwrap(), - BlockId::Hash("9fc84d84f6a785dc1bd5abacfcf9cbdd3b6afb80c0f799bfb2fd42c44a0c224e".parse().unwrap()) - ); - } - - #[test] - fn test_to_u256() { - assert_eq!(to_u256("0").unwrap(), U256::from(0)); - assert_eq!(to_u256("11").unwrap(), U256::from(11)); - assert_eq!(to_u256("0x11").unwrap(), U256::from(17)); - assert!(to_u256("u").is_err()) - } - - #[test] - fn test_pending_set() { - assert_eq!(to_pending_set("cheap").unwrap(), PendingSet::AlwaysQueue); - assert_eq!(to_pending_set("strict").unwrap(), PendingSet::AlwaysSealing); - assert_eq!(to_pending_set("lenient").unwrap(), PendingSet::SealingOrElseQueue); - assert!(to_pending_set("othe").is_err()); - } - - #[test] - fn test_to_address() { - assert_eq!( - to_address(Some("0xD9A111feda3f362f55Ef1744347CDC8Dd9964a41".into())).unwrap(), - "D9A111feda3f362f55Ef1744347CDC8Dd9964a41".parse().unwrap() - ); - assert_eq!( - to_address(Some("D9A111feda3f362f55Ef1744347CDC8Dd9964a41".into())).unwrap(), - "D9A111feda3f362f55Ef1744347CDC8Dd9964a41".parse().unwrap() - ); - assert_eq!(to_address(None).unwrap(), Default::default()); - } - - #[test] - fn test_to_addresses() { - let addresses = to_addresses(&Some("0xD9A111feda3f362f55Ef1744347CDC8Dd9964a41,D9A111feda3f362f55Ef1744347CDC8Dd9964a42".into())).unwrap(); - assert_eq!( - addresses, - vec![ - "D9A111feda3f362f55Ef1744347CDC8Dd9964a41".parse().unwrap(), - "D9A111feda3f362f55Ef1744347CDC8Dd9964a42".parse().unwrap(), - ] - ); - } - - #[test] - fn test_password() { - let tempdir = TempDir::new("").unwrap(); - let path = tempdir.path().join("file"); - let mut file = File::create(&path).unwrap(); - file.write_all(b"a bc ").unwrap(); - assert_eq!(password_from_file(path.to_str().unwrap().into()).unwrap().as_bytes(), b"a bc"); - } - - #[test] - fn test_password_multiline() { - let tempdir = TempDir::new("").unwrap(); - let path = tempdir.path().join("file"); - let mut file = File::create(path.as_path()).unwrap(); - file.write_all(br#" password with trailing whitespace + use super::{ + join_set, password_from_file, to_address, to_addresses, to_block_id, to_bootnodes, + to_duration, to_mode, to_pending_set, to_price, to_u256, + }; + use ethcore::{ + client::{BlockId, Mode}, + miner::PendingSet, + }; + use ethereum_types::U256; + use ethkey::Password; + use std::{collections::HashSet, fs::File, io::Write, time::Duration}; + use tempdir::TempDir; + + #[test] + fn test_to_duration() { + assert_eq!( + to_duration("twice-daily").unwrap(), + Duration::from_secs(12 * 60 * 60) + ); + assert_eq!( + to_duration("half-hourly").unwrap(), + Duration::from_secs(30 * 60) + ); + assert_eq!(to_duration("1second").unwrap(), Duration::from_secs(1)); + assert_eq!(to_duration("2seconds").unwrap(), Duration::from_secs(2)); + assert_eq!(to_duration("15seconds").unwrap(), Duration::from_secs(15)); + assert_eq!(to_duration("1minute").unwrap(), Duration::from_secs(1 * 60)); + assert_eq!( + to_duration("2minutes").unwrap(), + Duration::from_secs(2 * 60) + ); + assert_eq!( + to_duration("15minutes").unwrap(), + Duration::from_secs(15 * 60) + ); + assert_eq!(to_duration("hourly").unwrap(), Duration::from_secs(60 * 60)); + assert_eq!( + to_duration("daily").unwrap(), + Duration::from_secs(24 * 60 * 60) + ); + assert_eq!( + to_duration("1hour").unwrap(), + Duration::from_secs(1 * 60 * 60) + ); + assert_eq!( + to_duration("2hours").unwrap(), + Duration::from_secs(2 * 60 * 60) + ); + assert_eq!( + to_duration("15hours").unwrap(), + Duration::from_secs(15 * 60 * 60) + ); + assert_eq!( + to_duration("1day").unwrap(), + Duration::from_secs(1 * 24 * 60 * 60) + ); + assert_eq!( + to_duration("2days").unwrap(), + Duration::from_secs(2 * 24 * 60 * 60) + ); + assert_eq!( + to_duration("15days").unwrap(), + Duration::from_secs(15 * 24 * 60 * 60) + ); + assert_eq!( + to_duration("15 days").unwrap(), + Duration::from_secs(15 * 24 * 60 * 60) + ); + assert_eq!(to_duration("2 seconds").unwrap(), Duration::from_secs(2)); + } + + #[test] + fn test_to_mode() { + assert_eq!(to_mode("active", 0, 0).unwrap(), Mode::Active); + assert_eq!( + to_mode("passive", 10, 20).unwrap(), + Mode::Passive(Duration::from_secs(10), Duration::from_secs(20)) + ); + assert_eq!( + to_mode("dark", 20, 30).unwrap(), + Mode::Dark(Duration::from_secs(20)) + ); + assert!(to_mode("other", 20, 30).is_err()); + } + + #[test] + fn test_to_block_id() { + assert_eq!(to_block_id("latest").unwrap(), BlockId::Latest); + assert_eq!(to_block_id("0").unwrap(), BlockId::Number(0)); + assert_eq!(to_block_id("2").unwrap(), BlockId::Number(2)); + assert_eq!(to_block_id("15").unwrap(), BlockId::Number(15)); + assert_eq!( + to_block_id("9fc84d84f6a785dc1bd5abacfcf9cbdd3b6afb80c0f799bfb2fd42c44a0c224e") + .unwrap(), + BlockId::Hash( + "9fc84d84f6a785dc1bd5abacfcf9cbdd3b6afb80c0f799bfb2fd42c44a0c224e" + .parse() + .unwrap() + ) + ); + } + + #[test] + fn test_to_u256() { + assert_eq!(to_u256("0").unwrap(), U256::from(0)); + assert_eq!(to_u256("11").unwrap(), U256::from(11)); + assert_eq!(to_u256("0x11").unwrap(), U256::from(17)); + assert!(to_u256("u").is_err()) + } + + #[test] + fn test_pending_set() { + assert_eq!(to_pending_set("cheap").unwrap(), PendingSet::AlwaysQueue); + assert_eq!(to_pending_set("strict").unwrap(), PendingSet::AlwaysSealing); + assert_eq!( + to_pending_set("lenient").unwrap(), + PendingSet::SealingOrElseQueue + ); + assert!(to_pending_set("othe").is_err()); + } + + #[test] + fn test_to_address() { + assert_eq!( + to_address(Some("0xD9A111feda3f362f55Ef1744347CDC8Dd9964a41".into())).unwrap(), + "D9A111feda3f362f55Ef1744347CDC8Dd9964a41".parse().unwrap() + ); + assert_eq!( + to_address(Some("D9A111feda3f362f55Ef1744347CDC8Dd9964a41".into())).unwrap(), + "D9A111feda3f362f55Ef1744347CDC8Dd9964a41".parse().unwrap() + ); + assert_eq!(to_address(None).unwrap(), Default::default()); + } + + #[test] + fn test_to_addresses() { + let addresses = to_addresses(&Some( + "0xD9A111feda3f362f55Ef1744347CDC8Dd9964a41,D9A111feda3f362f55Ef1744347CDC8Dd9964a42" + .into(), + )) + .unwrap(); + assert_eq!( + addresses, + vec![ + "D9A111feda3f362f55Ef1744347CDC8Dd9964a41".parse().unwrap(), + "D9A111feda3f362f55Ef1744347CDC8Dd9964a42".parse().unwrap(), + ] + ); + } + + #[test] + fn test_password() { + let tempdir = TempDir::new("").unwrap(); + let path = tempdir.path().join("file"); + let mut file = File::create(&path).unwrap(); + file.write_all(b"a bc ").unwrap(); + assert_eq!( + password_from_file(path.to_str().unwrap().into()) + .unwrap() + .as_bytes(), + b"a bc" + ); + } + + #[test] + fn test_password_multiline() { + let tempdir = TempDir::new("").unwrap(); + let path = tempdir.path().join("file"); + let mut file = File::create(path.as_path()).unwrap(); + file.write_all( + br#" password with trailing whitespace those passwords should be ignored but the first password is trimmed -"#).unwrap(); - assert_eq!(password_from_file(path.to_str().unwrap().into()).unwrap(), Password::from("password with trailing whitespace")); - } - - #[test] - fn test_to_price() { - assert_eq!(to_price("1").unwrap(), 1.0); - assert_eq!(to_price("2.3").unwrap(), 2.3); - assert_eq!(to_price("2.33").unwrap(), 2.33); - } - - #[test] - #[cfg(windows)] - fn test_geth_ipc_path() { - assert_eq!(geth_ipc_path(true), r"\\.\pipe\geth.ipc".to_owned()); - assert_eq!(geth_ipc_path(false), r"\\.\pipe\geth.ipc".to_owned()); - } - - #[test] - #[cfg(not(windows))] - fn test_geth_ipc_path() { - use path; - assert_eq!(geth_ipc_path(true), path::ethereum::with_testnet("geth.ipc").to_str().unwrap().to_owned()); - assert_eq!(geth_ipc_path(false), path::ethereum::with_default("geth.ipc").to_str().unwrap().to_owned()); - } - - #[test] - fn test_to_bootnodes() { - let one_bootnode = "enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303"; - let two_bootnodes = "enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303,enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303"; - - assert_eq!(to_bootnodes(&Some("".into())), Ok(vec![])); - assert_eq!(to_bootnodes(&None), Ok(vec![])); - assert_eq!(to_bootnodes(&Some(one_bootnode.into())), Ok(vec![one_bootnode.into()])); - assert_eq!(to_bootnodes(&Some(two_bootnodes.into())), Ok(vec![one_bootnode.into(), one_bootnode.into()])); - } - - #[test] - fn test_join_set() { - let mut test_set = HashSet::new(); - test_set.insert("0x1111111111111111111111111111111111111111".to_string()); - test_set.insert("0x0000000000000000000000000000000000000000".to_string()); - - - let res = join_set(Some(&test_set)).unwrap(); - - assert!( +"#, + ) + .unwrap(); + assert_eq!( + password_from_file(path.to_str().unwrap().into()).unwrap(), + Password::from("password with trailing whitespace") + ); + } + + #[test] + fn test_to_price() { + assert_eq!(to_price("1").unwrap(), 1.0); + assert_eq!(to_price("2.3").unwrap(), 2.3); + assert_eq!(to_price("2.33").unwrap(), 2.33); + } + + #[test] + fn test_to_bootnodes() { + let one_bootnode = "enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303"; + let two_bootnodes = "enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303,enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303"; + + assert_eq!(to_bootnodes(&Some("".into())), Ok(vec![])); + assert_eq!(to_bootnodes(&None), Ok(vec![])); + assert_eq!( + to_bootnodes(&Some(one_bootnode.into())), + Ok(vec![one_bootnode.into()]) + ); + assert_eq!( + to_bootnodes(&Some(two_bootnodes.into())), + Ok(vec![one_bootnode.into(), one_bootnode.into()]) + ); + } + + #[test] + fn test_join_set() { + let mut test_set = HashSet::new(); + test_set.insert("0x1111111111111111111111111111111111111111".to_string()); + test_set.insert("0x0000000000000000000000000000000000000000".to_string()); + + let res = join_set(Some(&test_set)).unwrap(); + + assert!( res == "0x1111111111111111111111111111111111111111,0x0000000000000000000000000000000000000000" || res == "0x0000000000000000000000000000000000000000,0x1111111111111111111111111111111111111111" ); - } + } } diff --git a/parity/informant.rs b/parity/informant.rs index 78d055686b9..4f09c137a5c 100644 --- a/parity/informant.rs +++ b/parity/informant.rs @@ -1,451 +1,415 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . extern crate ansi_term; -use self::ansi_term::Colour::{White, Yellow, Green, Cyan, Blue}; -use self::ansi_term::{Colour, Style}; +use self::ansi_term::{ + Colour, + Colour::{Blue, Cyan, Green, White, Yellow}, + Style, +}; -use std::sync::{Arc}; -use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering}; -use std::time::{Instant, Duration}; +use std::{ + sync::{ + atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering}, + Arc, + }, + time::{Duration, Instant}, +}; use atty; -use ethcore::client::{ - BlockId, BlockChainClient, ChainInfo, BlockInfo, BlockChainInfo, - BlockQueueInfo, ChainNotify, NewBlocks, ClientReport, Client, ClientIoMessage +use ethcore::{ + client::{ + BlockChainClient, BlockChainInfo, BlockId, BlockInfo, BlockQueueInfo, ChainInfo, + ChainNotify, Client, ClientIoMessage, ClientReport, NewBlocks, + }, + snapshot::{service::Service as SnapshotService, RestorationStatus, SnapshotService as SS}, }; +use io::{IoContext, IoHandler, TimerToken}; +use number_prefix::{binary_prefix, Prefixed, Standalone}; +use parity_rpc::{informant::RpcStats, is_major_importing_or_waiting}; +use parking_lot::{Mutex, RwLock}; +use sync::{ManageNetwork, SyncProvider}; use types::BlockNumber; -use ethcore::snapshot::{RestorationStatus, SnapshotService as SS}; -use ethcore::snapshot::service::Service as SnapshotService; -use sync::{LightSyncProvider, LightSync, SyncProvider, ManageNetwork}; -use io::{TimerToken, IoContext, IoHandler}; -use light::Cache as LightDataCache; -use light::client::{LightChainClient, LightChainNotify}; -use number_prefix::{binary_prefix, Standalone, Prefixed}; -use parity_rpc::is_major_importing_or_waiting; -use parity_rpc::informant::RpcStats; -use ethereum_types::H256; -use parking_lot::{RwLock, Mutex}; /// Format byte counts to standard denominations. pub fn format_bytes(b: usize) -> String { - match binary_prefix(b as f64) { - Standalone(bytes) => format!("{} bytes", bytes), - Prefixed(prefix, n) => format!("{:.0} {}B", n, prefix), - } + match binary_prefix(b as f64) { + Standalone(bytes) => format!("{} bytes", bytes), + Prefixed(prefix, n) => format!("{:.0} {}B", n, prefix), + } } /// Something that can be converted to milliseconds. pub trait MillisecondDuration { - /// Get the value in milliseconds. - fn as_milliseconds(&self) -> u64; + /// Get the value in milliseconds. + fn as_milliseconds(&self) -> u64; } impl MillisecondDuration for Duration { - fn as_milliseconds(&self) -> u64 { - self.as_secs() * 1000 + self.subsec_nanos() as u64 / 1_000_000 - } + fn as_milliseconds(&self) -> u64 { + self.as_secs() * 1000 + self.subsec_nanos() as u64 / 1_000_000 + } } #[derive(Default)] struct CacheSizes { - sizes: ::std::collections::BTreeMap<&'static str, usize>, + sizes: ::std::collections::BTreeMap<&'static str, usize>, } impl CacheSizes { - fn insert(&mut self, key: &'static str, bytes: usize) { - self.sizes.insert(key, bytes); - } - - fn display(&self, style: Style, paint: F) -> String - where F: Fn(Style, String) -> String - { - use std::fmt::Write; - - let mut buf = String::new(); - for (name, &size) in &self.sizes { - - write!(buf, " {:>8} {}", paint(style, format_bytes(size)), name) - .expect("writing to string won't fail unless OOM; qed") - } - - buf - } + fn insert(&mut self, key: &'static str, bytes: usize) { + self.sizes.insert(key, bytes); + } + + fn display(&self, style: Style, paint: F) -> String + where + F: Fn(Style, String) -> String, + { + use std::fmt::Write; + + let mut buf = String::new(); + for (name, &size) in &self.sizes { + write!(buf, " {:>8} {}", paint(style, format_bytes(size)), name) + .expect("writing to string won't fail unless OOM; qed") + } + + buf + } } pub struct SyncInfo { - last_imported_block_number: BlockNumber, - last_imported_old_block_number: Option, - num_peers: usize, - max_peers: u32, - snapshot_sync: bool, + last_imported_block_number: BlockNumber, + last_imported_old_block_number: Option, + num_peers: usize, + max_peers: u32, + snapshot_sync: bool, } pub struct Report { - importing: bool, - chain_info: BlockChainInfo, - client_report: ClientReport, - queue_info: BlockQueueInfo, - cache_sizes: CacheSizes, - sync_info: Option, + importing: bool, + chain_info: BlockChainInfo, + client_report: ClientReport, + queue_info: BlockQueueInfo, + cache_sizes: CacheSizes, + sync_info: Option, } /// Something which can provide data to the informant. pub trait InformantData: Send + Sync { - /// Whether it executes transactions - fn executes_transactions(&self) -> bool; + /// Whether it executes transactions + fn executes_transactions(&self) -> bool; - /// Whether it is currently importing (also included in `Report`) - fn is_major_importing(&self) -> bool; + /// Whether it is currently importing (also included in `Report`) + fn is_major_importing(&self) -> bool; - /// Generate a report of blockchain status, memory usage, and sync info. - fn report(&self) -> Report; + /// Generate a report of blockchain status, memory usage, and sync info. + fn report(&self) -> Report; } /// Informant data for a full node. pub struct FullNodeInformantData { - pub client: Arc, - pub sync: Option>, - pub net: Option>, + pub client: Arc, + pub sync: Option>, + pub net: Option>, } impl InformantData for FullNodeInformantData { - fn executes_transactions(&self) -> bool { true } - - fn is_major_importing(&self) -> bool { - let state = self.sync.as_ref().map(|sync| sync.status().state); - is_major_importing_or_waiting(state, self.client.queue_info(), false) - } - - fn report(&self) -> Report { - let (client_report, queue_info, blockchain_cache_info) = - (self.client.report(), self.client.queue_info(), self.client.blockchain_cache_info()); - - let chain_info = self.client.chain_info(); - - let mut cache_sizes = CacheSizes::default(); - cache_sizes.insert("db", client_report.state_db_mem); - cache_sizes.insert("queue", queue_info.mem_used); - cache_sizes.insert("chain", blockchain_cache_info.total()); - - let importing = self.is_major_importing(); - let sync_info = match (self.sync.as_ref(), self.net.as_ref()) { - (Some(sync), Some(net)) => { - let status = sync.status(); - let num_peers_range = net.num_peers_range(); - debug_assert!(num_peers_range.end() >= num_peers_range.start()); - - cache_sizes.insert("sync", status.mem_used); - - Some(SyncInfo { - last_imported_block_number: status.last_imported_block_number.unwrap_or(chain_info.best_block_number), - last_imported_old_block_number: status.last_imported_old_block_number, - num_peers: status.num_peers, - max_peers: status.current_max_peers(*num_peers_range.start(), *num_peers_range.end()), - snapshot_sync: status.is_snapshot_syncing(), - }) - } - _ => None - }; - - Report { - importing, - chain_info, - client_report, - queue_info, - cache_sizes, - sync_info, - } - } -} - -/// Informant data for a light node -- note that the network is required. -pub struct LightNodeInformantData { - pub client: Arc, - pub sync: Arc, - pub cache: Arc>, -} - -impl InformantData for LightNodeInformantData { - fn executes_transactions(&self) -> bool { false } - - fn is_major_importing(&self) -> bool { - self.sync.is_major_importing() - } - - fn report(&self) -> Report { - let (client_report, queue_info, chain_info) = - (self.client.report(), self.client.queue_info(), self.client.chain_info()); - - let mut cache_sizes = CacheSizes::default(); - cache_sizes.insert("queue", queue_info.mem_used); - cache_sizes.insert("cache", self.cache.lock().mem_used()); - - let peer_numbers = self.sync.peer_numbers(); - let sync_info = Some(SyncInfo { - last_imported_block_number: chain_info.best_block_number, - last_imported_old_block_number: None, - num_peers: peer_numbers.connected, - max_peers: peer_numbers.max as u32, - snapshot_sync: false, - }); - - Report { - importing: self.sync.is_major_importing(), - chain_info, - client_report, - queue_info, - cache_sizes, - sync_info, - } - } + fn executes_transactions(&self) -> bool { + true + } + + fn is_major_importing(&self) -> bool { + let state = self.sync.as_ref().map(|sync| sync.status().state); + is_major_importing_or_waiting(state, self.client.queue_info(), false) + } + + fn report(&self) -> Report { + let (client_report, queue_info, blockchain_cache_info) = ( + self.client.report(), + self.client.queue_info(), + self.client.blockchain_cache_info(), + ); + + let chain_info = self.client.chain_info(); + + let mut cache_sizes = CacheSizes::default(); + cache_sizes.insert("queue", queue_info.mem_used); + cache_sizes.insert("chain", blockchain_cache_info.total()); + + let importing = self.is_major_importing(); + let sync_info = match (self.sync.as_ref(), self.net.as_ref()) { + (Some(sync), Some(net)) => { + let status = sync.status(); + let num_peers_range = net.num_peers_range(); + debug_assert!(num_peers_range.end() >= num_peers_range.start()); + + Some(SyncInfo { + last_imported_block_number: status + .last_imported_block_number + .unwrap_or(chain_info.best_block_number), + last_imported_old_block_number: status.last_imported_old_block_number, + num_peers: status.num_peers, + max_peers: status + .current_max_peers(*num_peers_range.start(), *num_peers_range.end()), + snapshot_sync: status.is_snapshot_syncing(), + }) + } + _ => None, + }; + + Report { + importing, + chain_info, + client_report, + queue_info, + cache_sizes, + sync_info, + } + } } pub struct Informant { - last_tick: RwLock, - with_color: bool, - target: T, - snapshot: Option>, - rpc_stats: Option>, - last_import: Mutex, - skipped: AtomicUsize, - skipped_txs: AtomicUsize, - in_shutdown: AtomicBool, - last_report: Mutex, + last_tick: RwLock, + with_color: bool, + target: T, + snapshot: Option>, + rpc_stats: Option>, + last_import: Mutex, + skipped: AtomicUsize, + skipped_txs: AtomicUsize, + in_shutdown: AtomicBool, + last_report: Mutex, } impl Informant { - /// Make a new instance potentially `with_color` output. - pub fn new( - target: T, - snapshot: Option>, - rpc_stats: Option>, - with_color: bool, - ) -> Self { - Informant { - last_tick: RwLock::new(Instant::now()), - with_color: with_color, - target: target, - snapshot: snapshot, - rpc_stats: rpc_stats, - last_import: Mutex::new(Instant::now()), - skipped: AtomicUsize::new(0), - skipped_txs: AtomicUsize::new(0), - in_shutdown: AtomicBool::new(false), - last_report: Mutex::new(Default::default()), - } - } - - /// Signal that we're shutting down; no more output necessary. - pub fn shutdown(&self) { - self.in_shutdown.store(true, ::std::sync::atomic::Ordering::SeqCst); - } - - pub fn tick(&self) { - let now = Instant::now(); - let elapsed = now.duration_since(*self.last_tick.read()); - - let (client_report, full_report) = { - let mut last_report = self.last_report.lock(); - let full_report = self.target.report(); - let diffed = full_report.client_report.clone() - &*last_report; - (diffed, full_report) - }; - - let Report { - importing, - chain_info, - queue_info, - cache_sizes, - sync_info, - .. - } = full_report; - - let rpc_stats = self.rpc_stats.as_ref(); - let snapshot_sync = sync_info.as_ref().map_or(false, |s| s.snapshot_sync) && self.snapshot.as_ref().map_or(false, |s| - match s.status() { - RestorationStatus::Ongoing { .. } | RestorationStatus::Initializing { .. } => true, - _ => false, - } - ); - if !importing && !snapshot_sync && elapsed < Duration::from_secs(30) { - return; - } - - *self.last_tick.write() = now; - *self.last_report.lock() = full_report.client_report.clone(); - - let paint = |c: Style, t: String| match self.with_color && atty::is(atty::Stream::Stdout) { - true => format!("{}", c.paint(t)), - false => t, - }; - - info!(target: "import", "{} {} {} {}", - match importing { - true => match snapshot_sync { - false => format!("Syncing {} {} {} {}+{} Qed", - paint(White.bold(), format!("{:>8}", format!("#{}", chain_info.best_block_number))), - paint(White.bold(), format!("{}", chain_info.best_block_hash)), - if self.target.executes_transactions() { - format!("{} blk/s {} tx/s {} Mgas/s", - paint(Yellow.bold(), format!("{:7.2}", (client_report.blocks_imported * 1000) as f64 / elapsed.as_milliseconds() as f64)), - paint(Yellow.bold(), format!("{:6.1}", (client_report.transactions_applied * 1000) as f64 / elapsed.as_milliseconds() as f64)), - paint(Yellow.bold(), format!("{:6.1}", (client_report.gas_processed / 1000).low_u64() as f64 / elapsed.as_milliseconds() as f64)) - ) - } else { - format!("{} hdr/s", - paint(Yellow.bold(), format!("{:6.1}", (client_report.blocks_imported * 1000) as f64 / elapsed.as_milliseconds() as f64)) - ) - }, - paint(Green.bold(), format!("{:5}", queue_info.unverified_queue_size)), - paint(Green.bold(), format!("{:5}", queue_info.verified_queue_size)) - ), - true => { - self.snapshot.as_ref().map_or(String::new(), |s| - match s.status() { - RestorationStatus::Ongoing { state_chunks, block_chunks, state_chunks_done, block_chunks_done } => { - format!("Syncing snapshot {}/{}", state_chunks_done + block_chunks_done, state_chunks + block_chunks) - }, - RestorationStatus::Initializing { chunks_done } => { - format!("Snapshot initializing ({} chunks restored)", chunks_done) - }, - _ => String::new(), - } - ) - }, - }, - false => String::new(), - }, - match sync_info.as_ref() { - Some(ref sync_info) => format!("{}{}/{} peers", - match importing { - true => format!("{}", - if self.target.executes_transactions() { - paint(Green.bold(), format!("{:>8} ", format!("#{}", sync_info.last_imported_block_number))) - } else { - String::new() - } - ), - false => match sync_info.last_imported_old_block_number { - Some(number) => format!("{} ", paint(Yellow.bold(), format!("{:>8}", format!("#{}", number)))), - None => String::new(), - } - }, - paint(Cyan.bold(), format!("{:2}", sync_info.num_peers)), - paint(Cyan.bold(), format!("{:2}", sync_info.max_peers)), - ), - _ => String::new(), - }, - cache_sizes.display(Blue.bold(), &paint), - match rpc_stats { - Some(ref rpc_stats) => format!( - "RPC: {} conn, {} req/s, {} µs", - paint(Blue.bold(), format!("{:2}", rpc_stats.sessions())), - paint(Blue.bold(), format!("{:4}", rpc_stats.requests_rate())), - paint(Blue.bold(), format!("{:4}", rpc_stats.approximated_roundtrip())), - ), - _ => String::new(), - }, - ); - } + /// Make a new instance potentially `with_color` output. + pub fn new( + target: T, + snapshot: Option>, + rpc_stats: Option>, + with_color: bool, + ) -> Self { + Informant { + last_tick: RwLock::new(Instant::now()), + with_color: with_color, + target: target, + snapshot: snapshot, + rpc_stats: rpc_stats, + last_import: Mutex::new(Instant::now()), + skipped: AtomicUsize::new(0), + skipped_txs: AtomicUsize::new(0), + in_shutdown: AtomicBool::new(false), + last_report: Mutex::new(Default::default()), + } + } + + /// Signal that we're shutting down; no more output necessary. + pub fn shutdown(&self) { + self.in_shutdown + .store(true, ::std::sync::atomic::Ordering::SeqCst); + } + + pub fn tick(&self) { + let now = Instant::now(); + let elapsed = now.duration_since(*self.last_tick.read()); + + let (client_report, full_report) = { + let last_report = self.last_report.lock(); + let full_report = self.target.report(); + let diffed = full_report.client_report.clone() - &*last_report; + (diffed, full_report) + }; + + let Report { + importing, + chain_info, + queue_info, + cache_sizes, + sync_info, + .. + } = full_report; + + let rpc_stats = self.rpc_stats.as_ref(); + let snapshot_sync = sync_info.as_ref().map_or(false, |s| s.snapshot_sync) + && self + .snapshot + .as_ref() + .map_or(false, |s| match s.restoration_status() { + RestorationStatus::Ongoing { .. } | RestorationStatus::Initializing { .. } => { + true + } + _ => false, + }); + if !importing && !snapshot_sync && elapsed < Duration::from_secs(30) { + return; + } + + *self.last_tick.write() = now; + *self.last_report.lock() = full_report.client_report.clone(); + + let paint = |c: Style, t: String| match self.with_color && atty::is(atty::Stream::Stdout) { + true => format!("{}", c.paint(t)), + false => t, + }; + + info!(target: "import", "{} {} {} {}", + match importing { + true => match snapshot_sync { + false => format!("Syncing {} {} {} {}+{} Qed", + paint(White.bold(), format!("{:>8}", format!("#{}", chain_info.best_block_number))), + paint(White.bold(), format!("{}", chain_info.best_block_hash)), + if self.target.executes_transactions() { + format!("{} blk/s {} tx/s {} Mgas/s", + paint(Yellow.bold(), format!("{:7.2}", (client_report.blocks_imported * 1000) as f64 / elapsed.as_milliseconds() as f64)), + paint(Yellow.bold(), format!("{:6.1}", (client_report.transactions_applied * 1000) as f64 / elapsed.as_milliseconds() as f64)), + paint(Yellow.bold(), format!("{:6.1}", (client_report.gas_processed / 1000).low_u64() as f64 / elapsed.as_milliseconds() as f64)) + ) + } else { + format!("{} hdr/s", + paint(Yellow.bold(), format!("{:6.1}", (client_report.blocks_imported * 1000) as f64 / elapsed.as_milliseconds() as f64)) + ) + }, + paint(Green.bold(), format!("{:5}", queue_info.unverified_queue_size)), + paint(Green.bold(), format!("{:5}", queue_info.verified_queue_size)) + ), + true => { + self.snapshot.as_ref().map_or(String::new(), |s| + match s.restoration_status() { + RestorationStatus::Ongoing { state_chunks, block_chunks, state_chunks_done, block_chunks_done, .. } => { + format!("Syncing snapshot {}/{}", state_chunks_done + block_chunks_done, state_chunks + block_chunks) + }, + RestorationStatus::Initializing { chunks_done } => { + format!("Snapshot initializing ({} chunks restored)", chunks_done) + }, + _ => String::new(), + } + ) + }, + }, + false => String::new(), + }, + match sync_info.as_ref() { + Some(ref sync_info) => format!("{}{}/{} peers", + match importing { + true => format!("{}", + if self.target.executes_transactions() { + paint(Green.bold(), format!("{:>8} ", format!("#{}", sync_info.last_imported_block_number))) + } else { + String::new() + } + ), + false => match sync_info.last_imported_old_block_number { + Some(number) => format!("{} ", paint(Yellow.bold(), format!("{:>8}", format!("#{}", number)))), + None => String::new(), + } + }, + paint(Cyan.bold(), format!("{:2}", sync_info.num_peers)), + paint(Cyan.bold(), format!("{:2}", sync_info.max_peers)), + ), + _ => String::new(), + }, + cache_sizes.display(Blue.bold(), &paint), + match rpc_stats { + Some(ref rpc_stats) => format!( + "RPC: {} conn, {} req/s, {} µs", + paint(Blue.bold(), format!("{:2}", rpc_stats.sessions())), + paint(Blue.bold(), format!("{:4}", rpc_stats.requests_rate())), + paint(Blue.bold(), format!("{:4}", rpc_stats.approximated_roundtrip())), + ), + _ => String::new(), + }, + ); + } } impl ChainNotify for Informant { - fn new_blocks(&self, new_blocks: NewBlocks) { - if new_blocks.has_more_blocks_to_import { return } - let mut last_import = self.last_import.lock(); - let client = &self.target.client; - - let importing = self.target.is_major_importing(); - let ripe = Instant::now() > *last_import + Duration::from_secs(1) && !importing; - let txs_imported = new_blocks.imported.iter() - .take(new_blocks.imported.len().saturating_sub(if ripe { 1 } else { 0 })) - .filter_map(|h| client.block(BlockId::Hash(*h))) - .map(|b| b.transactions_count()) - .sum(); - - if ripe { - if let Some(block) = new_blocks.imported.last().and_then(|h| client.block(BlockId::Hash(*h))) { - let header_view = block.header_view(); - let size = block.rlp().as_raw().len(); - let (skipped, skipped_txs) = (self.skipped.load(AtomicOrdering::Relaxed) + new_blocks.imported.len() - 1, self.skipped_txs.load(AtomicOrdering::Relaxed) + txs_imported); - info!(target: "import", "Imported {} {} ({} txs, {} Mgas, {} ms, {} KiB){}", - Colour::White.bold().paint(format!("#{}", header_view.number())), - Colour::White.bold().paint(format!("{}", header_view.hash())), - Colour::Yellow.bold().paint(format!("{}", block.transactions_count())), - Colour::Yellow.bold().paint(format!("{:.2}", header_view.gas_used().low_u64() as f32 / 1000000f32)), - Colour::Purple.bold().paint(format!("{}", new_blocks.duration.as_milliseconds())), - Colour::Blue.bold().paint(format!("{:.2}", size as f32 / 1024f32)), - if skipped > 0 { - format!(" + another {} block(s) containing {} tx(s)", - Colour::Red.bold().paint(format!("{}", skipped)), - Colour::Red.bold().paint(format!("{}", skipped_txs)) - ) - } else { - String::new() - } - ); - self.skipped.store(0, AtomicOrdering::Relaxed); - self.skipped_txs.store(0, AtomicOrdering::Relaxed); - *last_import = Instant::now(); - } - } else { - self.skipped.fetch_add(new_blocks.imported.len(), AtomicOrdering::Relaxed); - self.skipped_txs.fetch_add(txs_imported, AtomicOrdering::Relaxed); - } - } -} - -impl LightChainNotify for Informant { - fn new_headers(&self, good: &[H256]) { - let mut last_import = self.last_import.lock(); - let client = &self.target.client; - - let importing = self.target.is_major_importing(); - let ripe = Instant::now() > *last_import + Duration::from_secs(1) && !importing; - - if ripe { - if let Some(header) = good.last().and_then(|h| client.block_header(BlockId::Hash(*h))) { - info!(target: "import", "Imported {} {} ({} Mgas){}", - Colour::White.bold().paint(format!("#{}", header.number())), - Colour::White.bold().paint(format!("{}", header.hash())), - Colour::Yellow.bold().paint(format!("{:.2}", header.gas_used().low_u64() as f32 / 1000000f32)), - if good.len() > 1 { - format!(" + another {} header(s)", - Colour::Red.bold().paint(format!("{}", good.len() - 1))) - } else { - String::new() - } - ); - *last_import = Instant::now(); - } - } - } + fn new_blocks(&self, new_blocks: NewBlocks) { + if new_blocks.has_more_blocks_to_import { + return; + } + let mut last_import = self.last_import.lock(); + let client = &self.target.client; + + let importing = self.target.is_major_importing(); + let ripe = Instant::now() > *last_import + Duration::from_secs(1) && !importing; + let txs_imported = new_blocks + .imported + .iter() + .take( + new_blocks + .imported + .len() + .saturating_sub(if ripe { 1 } else { 0 }), + ) + .filter_map(|h| client.block(BlockId::Hash(*h))) + .map(|b| b.transactions_count()) + .sum(); + + if ripe { + if let Some(block) = new_blocks + .imported + .last() + .and_then(|h| client.block(BlockId::Hash(*h))) + { + let header_view = block.header_view(); + let size = block.rlp().as_raw().len(); + let (skipped, skipped_txs) = ( + self.skipped.load(AtomicOrdering::Relaxed) + new_blocks.imported.len() - 1, + self.skipped_txs.load(AtomicOrdering::Relaxed) + txs_imported, + ); + info!(target: "import", "Imported {} {} ({} txs, {} Mgas, {} ms, {} KiB){}", + Colour::White.bold().paint(format!("#{}", header_view.number())), + Colour::White.bold().paint(format!("{}", header_view.hash())), + Colour::Yellow.bold().paint(format!("{}", block.transactions_count())), + Colour::Yellow.bold().paint(format!("{:.2}", header_view.gas_used().low_u64() as f32 / 1000000f32)), + Colour::Purple.bold().paint(format!("{}", new_blocks.duration.as_milliseconds())), + Colour::Blue.bold().paint(format!("{:.2}", size as f32 / 1024f32)), + if skipped > 0 { + format!(" + another {} block(s) containing {} tx(s)", + Colour::Red.bold().paint(format!("{}", skipped)), + Colour::Red.bold().paint(format!("{}", skipped_txs)) + ) + } else { + String::new() + } + ); + self.skipped.store(0, AtomicOrdering::Relaxed); + self.skipped_txs.store(0, AtomicOrdering::Relaxed); + *last_import = Instant::now(); + } + } else { + self.skipped + .fetch_add(new_blocks.imported.len(), AtomicOrdering::Relaxed); + self.skipped_txs + .fetch_add(txs_imported, AtomicOrdering::Relaxed); + } + } } const INFO_TIMER: TimerToken = 0; impl IoHandler for Informant { - fn initialize(&self, io: &IoContext) { - io.register_timer(INFO_TIMER, Duration::from_secs(5)).expect("Error registering timer"); - } - - fn timeout(&self, _io: &IoContext, timer: TimerToken) { - if timer == INFO_TIMER && !self.in_shutdown.load(AtomicOrdering::SeqCst) { - self.tick(); - } - } + fn initialize(&self, io: &IoContext) { + io.register_timer(INFO_TIMER, Duration::from_secs(5)) + .expect("Error registering timer"); + } + + fn timeout(&self, _io: &IoContext, timer: TimerToken) { + if timer == INFO_TIMER && !self.in_shutdown.load(AtomicOrdering::SeqCst) { + self.tick(); + } + } } diff --git a/parity/ipfs.rs b/parity/ipfs.rs deleted file mode 100644 index 0923a1e7d40..00000000000 --- a/parity/ipfs.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use parity_ipfs_api::{self, AccessControlAllowOrigin, Host, Listening}; -use parity_ipfs_api::error::ServerError; -use ethcore::client::BlockChainClient; - -#[derive(Debug, PartialEq, Clone)] -pub struct Configuration { - pub enabled: bool, - pub port: u16, - pub interface: String, - pub cors: Option>, - pub hosts: Option>, -} - -impl Default for Configuration { - fn default() -> Self { - Configuration { - enabled: false, - port: 5001, - interface: "127.0.0.1".into(), - cors: Some(vec![]), - hosts: Some(vec![]), - } - } -} - -pub fn start_server(conf: Configuration, client: Arc) -> Result, ServerError> { - if !conf.enabled { - return Ok(None); - } - - let cors = conf.cors.map(|cors| cors.into_iter().map(AccessControlAllowOrigin::from).collect()); - let hosts = conf.hosts.map(|hosts| hosts.into_iter().map(Host::from).collect()); - - parity_ipfs_api::start_server( - conf.port, - conf.interface, - cors.into(), - hosts.into(), - client - ).map(Some) -} diff --git a/parity/lib.rs b/parity/lib.rs index 9141bdcadee..3f811826d60 100644 --- a/parity/lib.rs +++ b/parity/lib.rs @@ -1,30 +1,29 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Ethcore client application. - #![warn(missing_docs)] extern crate ansi_term; extern crate docopt; #[macro_use] extern crate clap; +extern crate atty; extern crate dir; extern crate futures; -extern crate atty; extern crate jsonrpc_core; extern crate num_cpus; extern crate number_prefix; @@ -47,31 +46,29 @@ extern crate ethcore; extern crate ethcore_call_contract as call_contract; extern crate ethcore_db; extern crate ethcore_io as io; -extern crate ethcore_light as light; extern crate ethcore_logger; extern crate ethcore_miner as miner; extern crate ethcore_network as network; -extern crate ethcore_private_tx; extern crate ethcore_service; extern crate ethcore_sync as sync; extern crate ethereum_types; extern crate ethkey; extern crate ethstore; +extern crate fetch; +extern crate hyper; extern crate journaldb; extern crate keccak_hash as hash; extern crate kvdb; extern crate node_filter; extern crate parity_bytes as bytes; -extern crate parity_hash_fetch as hash_fetch; -extern crate parity_ipfs_api; extern crate parity_local_store as local_store; extern crate parity_path as path; extern crate parity_rpc; extern crate parity_runtime; -extern crate parity_updater as updater; extern crate parity_version; -extern crate parity_whisper; +extern crate prometheus; extern crate registrar; +extern crate stats; #[macro_use] extern crate log as rlog; @@ -99,12 +96,10 @@ mod blockchain; mod cache; mod cli; mod configuration; -mod export_hardcoded_sync; -mod ipfs; -mod deprecated; +mod db; mod helpers; mod informant; -mod light_helpers; +mod metrics; mod modules; mod params; mod presale; @@ -116,136 +111,128 @@ mod signer; mod snapshot; mod upgrade; mod user_defaults; -mod whisper; -mod db; -use std::fs::File; -use std::io::BufReader; -use std::sync::Arc; +use std::{fs::File, io::BufReader, sync::Arc}; use cli::Args; use configuration::{Cmd, Execute}; -use deprecated::find_deprecated; use hash::keccak_buffer; #[cfg(feature = "memory_profiling")] use std::alloc::System; -pub use self::configuration::Configuration; -pub use self::run::RunningClient; +pub use self::{configuration::Configuration, run::RunningClient}; +pub use ethcore_logger::{setup_log, Config as LoggerConfig, RotatingLogger}; pub use parity_rpc::PubSubSession; -pub use ethcore_logger::{Config as LoggerConfig, setup_log, RotatingLogger}; #[cfg(feature = "memory_profiling")] #[global_allocator] static A: System = System; fn print_hash_of(maybe_file: Option) -> Result { - if let Some(file) = maybe_file { - let mut f = BufReader::new(File::open(&file).map_err(|_| "Unable to open file".to_owned())?); - let hash = keccak_buffer(&mut f).map_err(|_| "Unable to read from file".to_owned())?; - Ok(format!("{:x}", hash)) - } else { - Err("Streaming from standard input not yet supported. Specify a file.".to_owned()) - } + if let Some(file) = maybe_file { + let mut f = + BufReader::new(File::open(&file).map_err(|_| "Unable to open file".to_owned())?); + let hash = keccak_buffer(&mut f).map_err(|_| "Unable to read from file".to_owned())?; + Ok(format!("{:x}", hash)) + } else { + Err("Streaming from standard input not yet supported. Specify a file.".to_owned()) + } } #[cfg(feature = "deadlock_detection")] fn run_deadlock_detection_thread() { - use std::thread; - use std::time::Duration; - use parking_lot::deadlock; - use ansi_term::Style; - - info!("Starting deadlock detection thread."); - // Create a background thread which checks for deadlocks every 10s - thread::spawn(move || { - loop { - thread::sleep(Duration::from_secs(10)); - let deadlocks = deadlock::check_deadlock(); - if deadlocks.is_empty() { - continue; - } - - warn!("{} {} detected", deadlocks.len(), Style::new().bold().paint("deadlock(s)")); - for (i, threads) in deadlocks.iter().enumerate() { - warn!("{} #{}", Style::new().bold().paint("Deadlock"), i); - for t in threads { - warn!("Thread Id {:#?}", t.thread_id()); - warn!("{:#?}", t.backtrace()); - } - } - } - }); + use ansi_term::Style; + use parking_lot::deadlock; + use std::{thread, time::Duration}; + + info!("Starting deadlock detection thread."); + // Create a background thread which checks for deadlocks every 10s + thread::spawn(move || loop { + thread::sleep(Duration::from_secs(10)); + let deadlocks = deadlock::check_deadlock(); + if deadlocks.is_empty() { + continue; + } + + warn!( + "{} {} detected", + deadlocks.len(), + Style::new().bold().paint("deadlock(s)") + ); + for (i, threads) in deadlocks.iter().enumerate() { + warn!("{} #{}", Style::new().bold().paint("Deadlock"), i); + for t in threads { + warn!("Thread Id {:#?}", t.thread_id()); + warn!("{:#?}", t.backtrace()); + } + } + }); } -/// Action that Parity performed when running `start`. +/// Action that OpenEthereum performed when running `start`. pub enum ExecutionAction { - /// The execution didn't require starting a node, and thus has finished. - /// Contains the string to print on stdout, if any. - Instant(Option), - - /// The client has started running and must be shut down manually by calling `shutdown`. - /// - /// If you don't call `shutdown()`, execution will continue in the background. - Running(RunningClient), + /// The execution didn't require starting a node, and thus has finished. + /// Contains the string to print on stdout, if any. + Instant(Option), + + /// The client has started running and must be shut down manually by calling `shutdown`. + /// + /// If you don't call `shutdown()`, execution will continue in the background. + Running(RunningClient), } -fn execute( - command: Execute, - logger: Arc, - on_client_rq: Cr, on_updater_rq: Rr) -> Result - where Cr: Fn(String) + 'static + Send, - Rr: Fn() + 'static + Send -{ - #[cfg(feature = "deadlock_detection")] - run_deadlock_detection_thread(); - - match command.cmd { - Cmd::Run(run_cmd) => { - let outcome = run::execute(run_cmd, logger, on_client_rq, on_updater_rq)?; - Ok(ExecutionAction::Running(outcome)) - }, - Cmd::Version => Ok(ExecutionAction::Instant(Some(Args::print_version()))), - Cmd::Hash(maybe_file) => print_hash_of(maybe_file).map(|s| ExecutionAction::Instant(Some(s))), - Cmd::Account(account_cmd) => account::execute(account_cmd).map(|s| ExecutionAction::Instant(Some(s))), - Cmd::ImportPresaleWallet(presale_cmd) => presale::execute(presale_cmd).map(|s| ExecutionAction::Instant(Some(s))), - Cmd::Blockchain(blockchain_cmd) => blockchain::execute(blockchain_cmd).map(|_| ExecutionAction::Instant(None)), - Cmd::SignerToken(ws_conf, logger_config) => signer::execute(ws_conf, logger_config).map(|s| ExecutionAction::Instant(Some(s))), - Cmd::SignerSign { id, pwfile, port, authfile } => cli_signer::signer_sign(id, pwfile, port, authfile).map(|s| ExecutionAction::Instant(Some(s))), - Cmd::SignerList { port, authfile } => cli_signer::signer_list(port, authfile).map(|s| ExecutionAction::Instant(Some(s))), - Cmd::SignerReject { id, port, authfile } => cli_signer::signer_reject(id, port, authfile).map(|s| ExecutionAction::Instant(Some(s))), - Cmd::Snapshot(snapshot_cmd) => snapshot::execute(snapshot_cmd).map(|s| ExecutionAction::Instant(Some(s))), - Cmd::ExportHardcodedSync(export_hs_cmd) => export_hardcoded_sync::execute(export_hs_cmd).map(|s| ExecutionAction::Instant(Some(s))), - } +fn execute(command: Execute, logger: Arc) -> Result { + #[cfg(feature = "deadlock_detection")] + run_deadlock_detection_thread(); + + match command.cmd { + Cmd::Run(run_cmd) => { + let outcome = run::execute(run_cmd, logger)?; + Ok(ExecutionAction::Running(outcome)) + } + Cmd::Version => Ok(ExecutionAction::Instant(Some(Args::print_version()))), + Cmd::Hash(maybe_file) => { + print_hash_of(maybe_file).map(|s| ExecutionAction::Instant(Some(s))) + } + Cmd::Account(account_cmd) => { + account::execute(account_cmd).map(|s| ExecutionAction::Instant(Some(s))) + } + Cmd::ImportPresaleWallet(presale_cmd) => { + presale::execute(presale_cmd).map(|s| ExecutionAction::Instant(Some(s))) + } + Cmd::Blockchain(blockchain_cmd) => { + blockchain::execute(blockchain_cmd).map(|_| ExecutionAction::Instant(None)) + } + Cmd::SignerToken(ws_conf, logger_config) => { + signer::execute(ws_conf, logger_config).map(|s| ExecutionAction::Instant(Some(s))) + } + Cmd::SignerSign { + id, + pwfile, + port, + authfile, + } => cli_signer::signer_sign(id, pwfile, port, authfile) + .map(|s| ExecutionAction::Instant(Some(s))), + Cmd::SignerList { port, authfile } => { + cli_signer::signer_list(port, authfile).map(|s| ExecutionAction::Instant(Some(s))) + } + Cmd::SignerReject { id, port, authfile } => { + cli_signer::signer_reject(id, port, authfile).map(|s| ExecutionAction::Instant(Some(s))) + } + Cmd::Snapshot(snapshot_cmd) => { + snapshot::execute(snapshot_cmd).map(|s| ExecutionAction::Instant(Some(s))) + } + } } -/// Starts the parity client. -/// -/// `on_client_rq` is the action to perform when the client receives an RPC request to be restarted -/// with a different chain. -/// -/// `on_updater_rq` is the action to perform when the updater has a new binary to execute. +/// Starts the OpenEthereum client. /// -/// The first parameter is the command line arguments that you would pass when running the parity +/// The first parameter is the command line arguments that you would pass when running the openethereum /// binary. /// /// On error, returns what to print on stderr. -// FIXME: totally independent logging capability, see https://github.com/paritytech/parity-ethereum/issues/10252 -pub fn start( - conf: Configuration, - logger: Arc, - on_client_rq: Cr, - on_updater_rq: Rr -) -> Result - where - Cr: Fn(String) + 'static + Send, - Rr: Fn() + 'static + Send -{ - let deprecated = find_deprecated(&conf.args); - for d in deprecated { - println!("{}", d); - } - - execute(conf.into_command()?, logger, on_client_rq, on_updater_rq) +// FIXME: totally independent logging capability, see https://github.com/openethereum/openethereum/issues/10252 +pub fn start(conf: Configuration, logger: Arc) -> Result { + execute(conf.into_command()?, logger) } diff --git a/parity/light_helpers/epoch_fetch.rs b/parity/light_helpers/epoch_fetch.rs deleted file mode 100644 index 9c7fd6a8ee3..00000000000 --- a/parity/light_helpers/epoch_fetch.rs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::{Arc, Weak}; - -use ethcore::engines::{EthEngine, StateDependentProof}; -use ethcore::machine::EthereumMachine; -use sync::{LightSync, LightNetworkDispatcher}; -use types::encoded; -use types::header::Header; -use types::receipt::Receipt; - -use futures::{future, Future}; -use futures::future::Either; - -use light::client::fetch::ChainDataFetcher; -use light::on_demand::{request, OnDemand, OnDemandRequester}; - -use parking_lot::RwLock; -use ethereum_types::H256; - -const ALL_VALID_BACKREFS: &str = "no back-references, therefore all back-references valid; qed"; - -type BoxFuture = Box>; - -/// Allows on-demand fetch of data useful for the light client. -pub struct EpochFetch { - /// A handle to the sync service. - pub sync: Arc>>, - /// The on-demand request service. - pub on_demand: Arc, -} - -impl EpochFetch { - fn request(&self, req: T) -> BoxFuture - where T: Send + request::RequestAdapter + 'static, T::Out: Send + 'static - { - Box::new(match self.sync.read().upgrade() { - Some(sync) => { - let on_demand = &self.on_demand; - let maybe_future = sync.with_context(move |ctx| { - on_demand.request(ctx, req).expect(ALL_VALID_BACKREFS) - }); - - match maybe_future { - Some(x) => Either::A(x.map_err(|_| "Request canceled")), - None => Either::B(future::err("Unable to access network.")), - } - } - None => Either::B(future::err("Unable to access network")), - }) - } -} - -impl ChainDataFetcher for EpochFetch { - type Error = &'static str; - - type Body = BoxFuture; - type Receipts = BoxFuture, &'static str>; - type Transition = BoxFuture, &'static str>; - - fn block_body(&self, header: &Header) -> Self::Body { - self.request(request::Body(header.encoded().into())) - } - - /// Fetch block receipts. - fn block_receipts(&self, header: &Header) -> Self::Receipts { - self.request(request::BlockReceipts(header.encoded().into())) - } - - /// Fetch epoch transition proof at given header. - fn epoch_transition(&self, hash: H256, engine: Arc, checker: Arc>) - -> Self::Transition - { - self.request(request::Signal { - hash: hash, - engine: engine, - proof_check: checker, - }) - } -} diff --git a/parity/light_helpers/mod.rs b/parity/light_helpers/mod.rs deleted file mode 100644 index 9a9bbf2cd87..00000000000 --- a/parity/light_helpers/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Utilities and helpers for the light client. - -mod epoch_fetch; -mod queue_cull; - -pub use self::epoch_fetch::EpochFetch; -pub use self::queue_cull::QueueCull; diff --git a/parity/light_helpers/queue_cull.rs b/parity/light_helpers/queue_cull.rs deleted file mode 100644 index 693d8f93cff..00000000000 --- a/parity/light_helpers/queue_cull.rs +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Service for culling the light client's transaction queue. - -use std::sync::Arc; -use std::time::Duration; - -use ethcore::client::ClientIoMessage; -use sync::{LightSync, LightNetworkDispatcher}; -use io::{IoContext, IoHandler, TimerToken}; - -use light::client::LightChainClient; -use light::on_demand::{request, OnDemand, OnDemandRequester}; -use light::TransactionQueue; - -use futures::{future, Future}; - -use parity_runtime::Executor; - -use parking_lot::RwLock; - -// Attepmt to cull once every 10 minutes. -const TOKEN: TimerToken = 1; -const TIMEOUT: Duration = Duration::from_secs(60 * 10); - -// But make each attempt last only 9 minutes -const PURGE_TIMEOUT: Duration = Duration::from_secs(60 * 9); - -/// Periodically culls the transaction queue of mined transactions. -pub struct QueueCull { - /// A handle to the client, for getting the latest block header. - pub client: Arc, - /// A handle to the sync service. - pub sync: Arc, - /// The on-demand request service. - pub on_demand: Arc, - /// The transaction queue. - pub txq: Arc>, - /// Event loop executor. - pub executor: Executor, -} - -impl IoHandler for QueueCull { - fn initialize(&self, io: &IoContext) { - io.register_timer(TOKEN, TIMEOUT).expect("Error registering timer"); - } - - fn timeout(&self, _io: &IoContext, timer: TimerToken) { - if timer != TOKEN { return } - - let senders = self.txq.read().queued_senders(); - if senders.is_empty() { return } - - let (sync, on_demand, txq) = (self.sync.clone(), self.on_demand.clone(), self.txq.clone()); - let best_header = self.client.best_block_header(); - let start_nonce = self.client.engine().account_start_nonce(best_header.number()); - - info!(target: "cull", "Attempting to cull queued transactions from {} senders.", senders.len()); - self.executor.spawn_with_timeout(move || { - let maybe_fetching = sync.with_context(move |ctx| { - // fetch the nonce of each sender in the queue. - let nonce_reqs = senders.iter() - .map(|&address| request::Account { header: best_header.clone().into(), address: address }) - .collect::>(); - - // when they come in, update each sender to the new nonce. - on_demand.request(ctx, nonce_reqs) - .expect("No back-references; therefore all back-references are valid; qed") - .map(move |accs| { - let txq = txq.write(); - let _ = accs.into_iter() - .map(|maybe_acc| maybe_acc.map_or(start_nonce, |acc| acc.nonce)) - .zip(senders) - .fold(txq, |mut txq, (nonce, addr)| { - txq.cull(addr, nonce); - txq - }); - }) - .map_err(|_| debug!(target: "cull", "OnDemand prematurely closed channel.")) - }); - - match maybe_fetching { - Some(fut) => future::Either::A(fut), - None => { - debug!(target: "cull", "Unable to acquire network context; qed"); - future::Either::B(future::ok(())) - }, - } - }, PURGE_TIMEOUT, || {}) - } -} diff --git a/parity/logger/Cargo.toml b/parity/logger/Cargo.toml index 17c8c4396de..217bcf48a61 100644 --- a/parity/logger/Cargo.toml +++ b/parity/logger/Cargo.toml @@ -1,5 +1,5 @@ [package] -description = "Log implementation for Parity" +description = "Parity Ethereum Logger Implementation" name = "ethcore-logger" version = "1.12.0" license = "GPL-3.0" diff --git a/parity/logger/src/lib.rs b/parity/logger/src/lib.rs index a2e3de176aa..44412a05a54 100644 --- a/parity/logger/src/lib.rs +++ b/parity/logger/src/lib.rs @@ -1,20 +1,20 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -//! Logger for parity executables +//! Logger for OpenEthereum executables extern crate ansi_term; extern crate arrayvec; @@ -30,136 +30,161 @@ extern crate lazy_static; mod rotating; -use std::{env, thread, fs}; -use std::sync::{Weak, Arc}; -use std::io::Write; -use env_logger::{Builder as LogBuilder, Formatter}; -use regex::Regex; use ansi_term::Colour; +use env_logger::{Builder as LogBuilder, Formatter}; use parking_lot::Mutex; +use regex::Regex; +use std::{ + env, fs, + io::Write, + sync::{Arc, Weak}, + thread, +}; -pub use rotating::{RotatingLogger, init_log}; +pub use rotating::{init_log, RotatingLogger}; #[derive(Debug, PartialEq, Clone)] pub struct Config { - pub mode: Option, - pub color: bool, - pub file: Option, + pub mode: Option, + pub color: bool, + pub file: Option, } impl Default for Config { - fn default() -> Self { - Config { - mode: None, - color: !cfg!(windows), - file: None, - } - } + fn default() -> Self { + Config { + mode: None, + color: !cfg!(windows), + file: None, + } + } } lazy_static! { - static ref ROTATING_LOGGER : Mutex> = Mutex::new(Default::default()); + static ref ROTATING_LOGGER: Mutex> = Mutex::new(Default::default()); } /// Sets up the logger pub fn setup_log(config: &Config) -> Result, String> { - use rlog::*; - - let mut levels = String::new(); - let mut builder = LogBuilder::new(); - // Disable info logging by default for some modules: - builder.filter(Some("ws"), LevelFilter::Warn); - builder.filter(Some("hyper"), LevelFilter::Warn); - builder.filter(Some("rustls"), LevelFilter::Error); - // Enable info for others. - builder.filter(None, LevelFilter::Info); - - if let Ok(lvl) = env::var("RUST_LOG") { - levels.push_str(&lvl); - levels.push_str(","); - builder.parse(&lvl); - } - - if let Some(ref s) = config.mode { - levels.push_str(s); - builder.parse(s); - } - - let isatty = atty::is(atty::Stream::Stderr); - let enable_color = config.color && isatty; - let logs = Arc::new(RotatingLogger::new(levels)); - let logger = logs.clone(); - let mut open_options = fs::OpenOptions::new(); - - let maybe_file = match config.file.as_ref() { - Some(f) => Some(open_options - .append(true).create(true).open(f) - .map_err(|e| format!("Cannot write to log file given: {}, {}", f, e))?), - None => None, - }; - - let format = move |buf: &mut Formatter, record: &Record| { - let timestamp = time::strftime("%Y-%m-%d %H:%M:%S %Z", &time::now()).unwrap(); - - let with_color = if max_level() <= LevelFilter::Info { - format!("{} {}", Colour::Black.bold().paint(timestamp), record.args()) - } else { - let name = thread::current().name().map_or_else(Default::default, |x| format!("{}", Colour::Blue.bold().paint(x))); - format!("{} {} {} {} {}", Colour::Black.bold().paint(timestamp), name, record.level(), record.target(), record.args()) - }; - - let removed_color = kill_color(with_color.as_ref()); - - let ret = match enable_color { - true => with_color, - false => removed_color.clone(), - }; - - if let Some(mut file) = maybe_file.as_ref() { - // ignore errors - there's nothing we can do - let _ = file.write_all(removed_color.as_bytes()); - let _ = file.write_all(b"\n"); - } - logger.append(removed_color); - if !isatty && record.level() <= Level::Info && atty::is(atty::Stream::Stdout) { - // duplicate INFO/WARN output to console - println!("{}", ret); - } - - writeln!(buf, "{}", ret) + use rlog::*; + + let mut levels = String::new(); + let mut builder = LogBuilder::new(); + // Disable info logging by default for some modules: + builder.filter(Some("ws"), LevelFilter::Warn); + builder.filter(Some("hyper"), LevelFilter::Warn); + builder.filter(Some("rustls"), LevelFilter::Error); + // Enable info for others. + builder.filter(None, LevelFilter::Info); + + if let Ok(lvl) = env::var("RUST_LOG") { + levels.push_str(&lvl); + levels.push_str(","); + builder.parse(&lvl); + } + + if let Some(ref s) = config.mode { + levels.push_str(s); + builder.parse(s); + } + + let isatty = atty::is(atty::Stream::Stderr); + let enable_color = config.color && isatty; + let logs = Arc::new(RotatingLogger::new(levels)); + let logger = logs.clone(); + let mut open_options = fs::OpenOptions::new(); + + let maybe_file = match config.file.as_ref() { + Some(f) => Some( + open_options + .append(true) + .create(true) + .open(f) + .map_err(|e| format!("Cannot write to log file given: {}, {}", f, e))?, + ), + None => None, + }; + + let format = move |buf: &mut Formatter, record: &Record| { + let timestamp = time::strftime("%Y-%m-%d %H:%M:%S %Z", &time::now()).unwrap(); + + let with_color = if max_level() <= LevelFilter::Info { + format!( + "{} {}", + Colour::Black.bold().paint(timestamp), + record.args() + ) + } else { + let name = thread::current().name().map_or_else(Default::default, |x| { + format!("{}", Colour::Blue.bold().paint(x)) + }); + format!( + "{} {} {} {} {}", + Colour::Black.bold().paint(timestamp), + name, + record.level(), + record.target(), + record.args() + ) + }; + + let removed_color = kill_color(with_color.as_ref()); + + let ret = match enable_color { + true => with_color, + false => removed_color.clone(), + }; + + if let Some(mut file) = maybe_file.as_ref() { + // ignore errors - there's nothing we can do + let _ = file.write_all(removed_color.as_bytes()); + let _ = file.write_all(b"\n"); + } + logger.append(removed_color); + if !isatty && record.level() <= Level::Info && atty::is(atty::Stream::Stdout) { + // duplicate INFO/WARN output to console + println!("{}", ret); + } + + writeln!(buf, "{}", ret) }; - builder.format(format); - builder.try_init() - .and_then(|_| { - *ROTATING_LOGGER.lock() = Arc::downgrade(&logs); - Ok(logs) - }) - // couldn't create new logger - try to fall back on previous logger. - .or_else(|err| match ROTATING_LOGGER.lock().upgrade() { - Some(l) => Ok(l), - // no previous logger. fatal. - None => Err(format!("{:?}", err)), - }) + builder.format(format); + builder + .try_init() + .and_then(|_| { + *ROTATING_LOGGER.lock() = Arc::downgrade(&logs); + Ok(logs) + }) + // couldn't create new logger - try to fall back on previous logger. + .or_else(|err| match ROTATING_LOGGER.lock().upgrade() { + Some(l) => Ok(l), + // no previous logger. fatal. + None => Err(format!("{:?}", err)), + }) } fn kill_color(s: &str) -> String { - lazy_static! { - static ref RE: Regex = Regex::new("\x1b\\[[^m]+m").unwrap(); - } - RE.replace_all(s, "").to_string() + lazy_static! { + static ref RE: Regex = Regex::new("\x1b\\[[^m]+m").unwrap(); + } + RE.replace_all(s, "").to_string() } #[test] fn should_remove_colour() { - let before = "test"; - let after = kill_color(&Colour::Red.bold().paint(before)); - assert_eq!(after, "test"); + let before = "test"; + let after = kill_color(&Colour::Red.bold().paint(before)); + assert_eq!(after, "test"); } #[test] fn should_remove_multiple_colour() { - let t = format!("{} {}", Colour::Red.bold().paint("test"), Colour::White.normal().paint("again")); - let after = kill_color(&t); - assert_eq!(after, "test again"); + let t = format!( + "{} {}", + Colour::Red.bold().paint("test"), + Colour::White.normal().paint("again") + ); + let after = kill_color(&t); + assert_eq!(after, "test again"); } diff --git a/parity/logger/src/rotating.rs b/parity/logger/src/rotating.rs index 2745e95bf62..7a00c85c3ec 100644 --- a/parity/logger/src/rotating.rs +++ b/parity/logger/src/rotating.rs @@ -1,123 +1,121 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Common log helper functions -use std::env; -use rlog::LevelFilter; -use env_logger::Builder as LogBuilder; use arrayvec::ArrayVec; +use env_logger::Builder as LogBuilder; +use rlog::LevelFilter; +use std::env; use parking_lot::{RwLock, RwLockReadGuard}; lazy_static! { - static ref LOG_DUMMY: () = { - let mut builder = LogBuilder::new(); - builder.filter(None, LevelFilter::Info); - - if let Ok(log) = env::var("RUST_LOG") { - builder.parse(&log); - } - - if !builder.try_init().is_ok() { - println!("logger initialization failed!"); - } - }; + static ref LOG_DUMMY: () = { + let mut builder = LogBuilder::new(); + builder.filter(None, LevelFilter::Info); + + if let Ok(log) = env::var("RUST_LOG") { + builder.parse(&log); + } + + if !builder.try_init().is_ok() { + println!("logger initialization failed!"); + } + }; } /// Intialize log with default settings pub fn init_log() { - *LOG_DUMMY + *LOG_DUMMY } -const LOG_SIZE : usize = 128; +const LOG_SIZE: usize = 128; /// Logger implementation that keeps up to `LOG_SIZE` log elements. pub struct RotatingLogger { - /// Defined logger levels - levels: String, - /// Logs array. Latest log is always at index 0 - logs: RwLock>, + /// Defined logger levels + levels: String, + /// Logs array. Latest log is always at index 0 + logs: RwLock>, } impl RotatingLogger { - - /// Creates new `RotatingLogger` with given levels. - /// It does not enforce levels - it's just read only. - pub fn new(levels: String) -> Self { - RotatingLogger { - levels: levels, - logs: RwLock::new(ArrayVec::<[_; LOG_SIZE]>::new()), - } - } - - /// Append new log entry - pub fn append(&self, log: String) { - let mut logs = self.logs.write(); - if logs.is_full() { - logs.pop(); - } - logs.insert(0, log); - } - - /// Return levels - pub fn levels(&self) -> &str { - &self.levels - } - - /// Return logs - pub fn logs(&self) -> RwLockReadGuard> { - self.logs.read() - } - + /// Creates new `RotatingLogger` with given levels. + /// It does not enforce levels - it's just read only. + pub fn new(levels: String) -> Self { + RotatingLogger { + levels: levels, + logs: RwLock::new(ArrayVec::<[_; LOG_SIZE]>::new()), + } + } + + /// Append new log entry + pub fn append(&self, log: String) { + let mut logs = self.logs.write(); + if logs.is_full() { + logs.pop(); + } + logs.insert(0, log); + } + + /// Return levels + pub fn levels(&self) -> &str { + &self.levels + } + + /// Return logs + pub fn logs(&self) -> RwLockReadGuard> { + self.logs.read() + } } #[cfg(test)] mod test { - use super::RotatingLogger; - - fn logger() -> RotatingLogger { - RotatingLogger::new("test".to_owned()) - } - - #[test] - fn should_return_log_levels() { - // given - let logger = logger(); - - // when - let levels = logger.levels(); - - // then - assert_eq!(levels, "test"); - } - - #[test] - fn should_return_latest_logs() { - // given - let logger = logger(); - - // when - logger.append("a".to_owned()); - logger.append("b".to_owned()); - - // then - let logs = logger.logs(); - assert_eq!(logs[0], "b".to_owned()); - assert_eq!(logs[1], "a".to_owned()); - assert_eq!(logs.len(), 2); - } + use super::RotatingLogger; + + fn logger() -> RotatingLogger { + RotatingLogger::new("test".to_owned()) + } + + #[test] + fn should_return_log_levels() { + // given + let logger = logger(); + + // when + let levels = logger.levels(); + + // then + assert_eq!(levels, "test"); + } + + #[test] + fn should_return_latest_logs() { + // given + let logger = logger(); + + // when + logger.append("a".to_owned()); + logger.append("b".to_owned()); + + // then + let logs = logger.logs(); + assert_eq!(logs[0], "b".to_owned()); + assert_eq!(logs[1], "a".to_owned()); + assert_eq!(logs.len(), 2); + } } diff --git a/parity/main.rs b/parity/main.rs index 066061cbafd..b6189d27a00 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Ethcore client application. @@ -23,432 +23,158 @@ extern crate dir; extern crate fdlimit; #[macro_use] extern crate log; +extern crate ansi_term; +extern crate openethereum; extern crate panic_hook; -extern crate parity_ethereum; -extern crate parking_lot; extern crate parity_daemonize; -extern crate ansi_term; +extern crate parking_lot; -#[cfg(windows)] extern crate winapi; extern crate ethcore_logger; +#[cfg(windows)] +extern crate winapi; -use std::ffi::OsString; -use std::fs::{remove_file, metadata, File, create_dir_all}; -use std::io::{Read, Write}; -use std::path::PathBuf; -use std::sync::Arc; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::{process, env}; +use std::{ + io::Write, + process, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, +}; use ansi_term::Colour; use ctrlc::CtrlC; -use dir::default_hypervisor_path; -use fdlimit::raise_fd_limit; use ethcore_logger::setup_log; -use parity_ethereum::{start, ExecutionAction}; +use fdlimit::raise_fd_limit; +use openethereum::{start, ExecutionAction}; use parity_daemonize::AsHandle; use parking_lot::{Condvar, Mutex}; -const PLEASE_RESTART_EXIT_CODE: i32 = 69; -const PARITY_EXECUTABLE_NAME: &str = "parity"; - -#[derive(Debug)] -enum Error { - BinaryNotFound, - ExitCode(i32), - Restart, - Unknown -} - -fn update_path(name: &str) -> PathBuf { - let mut dest = default_hypervisor_path(); - dest.push(name); - dest -} - -fn latest_exe_path() -> Result { - File::open(update_path("latest")).and_then(|mut f| { - let mut exe_path = String::new(); - trace!(target: "updater", "latest binary path: {:?}", f); - f.read_to_string(&mut exe_path).map(|_| update_path(&exe_path)) - }) - .or(Err(Error::BinaryNotFound)) -} - -fn latest_binary_is_newer(current_binary: &Option, latest_binary: &Option) -> bool { - match ( - current_binary - .as_ref() - .and_then(|p| metadata(p.as_path()).ok()) - .and_then(|m| m.modified().ok()), - latest_binary - .as_ref() - .and_then(|p| metadata(p.as_path()).ok()) - .and_then(|m| m.modified().ok()) - ) { - (Some(latest_exe_time), Some(this_exe_time)) if latest_exe_time > this_exe_time => true, - _ => false, - } -} - -fn set_spec_name_override(spec_name: &str) { - if let Err(e) = create_dir_all(default_hypervisor_path()) - .and_then(|_| File::create(update_path("spec_name_override")) - .and_then(|mut f| f.write_all(spec_name.as_bytes()))) - { - warn!("Couldn't override chain spec: {} at {:?}", e, update_path("spec_name_override")); - } -} - -fn take_spec_name_override() -> Option { - let p = update_path("spec_name_override"); - let r = File::open(p.clone()) - .ok() - .and_then(|mut f| { - let mut spec_name = String::new(); - f.read_to_string(&mut spec_name).ok().map(|_| spec_name) - }); - let _ = remove_file(p); - r -} - -#[cfg(windows)] -fn global_cleanup() { - // We need to cleanup all sockets before spawning another Parity process. This makes sure everything is cleaned up. - // The loop is required because of internal reference counter for winsock dll. We don't know how many crates we use do - // initialize it. There's at least 2 now. - for _ in 0.. 10 { - unsafe { ::winapi::um::winsock2::WSACleanup(); } - } -} - -#[cfg(not(windows))] -fn global_init() {} - -#[cfg(windows)] -fn global_init() { - // When restarting in the same process this reinits windows sockets. - unsafe { - const WS_VERSION: u16 = 0x202; - let mut wsdata: ::winapi::um::winsock2::WSADATA = ::std::mem::zeroed(); - ::winapi::um::winsock2::WSAStartup(WS_VERSION, &mut wsdata); - } -} - -#[cfg(not(windows))] -fn global_cleanup() {} - -// Starts parity binary installed via `parity-updater` and returns the code it exits with. -fn run_parity() -> Result<(), Error> { - global_init(); - - let prefix = vec![OsString::from("--can-restart"), OsString::from("--force-direct")]; - - let res: Result<(), Error> = latest_exe_path() - .and_then(|exe| process::Command::new(exe) - .args(&(env::args_os().skip(1).chain(prefix.into_iter()).collect::>())) - .status() - .ok() - .map_or(Err(Error::Unknown), |es| { - match es.code() { - // Process success - Some(0) => Ok(()), - // Please restart - Some(PLEASE_RESTART_EXIT_CODE) => Err(Error::Restart), - // Process error code `c` - Some(c) => Err(Error::ExitCode(c)), - // Unknown error, couldn't determine error code - _ => Err(Error::Unknown), - } - }) - ); - - global_cleanup(); - res -} - #[derive(Debug)] /// Status used to exit or restart the program. struct ExitStatus { - /// Whether the program panicked. - panicking: bool, - /// Whether the program should exit. - should_exit: bool, - /// Whether the program should restart. - should_restart: bool, - /// If a restart happens, whether a new chain spec should be used. - spec_name_override: Option, + /// Whether the program panicked. + panicking: bool, + /// Whether the program should exit. + should_exit: bool, } -// Run `locally installed version` of parity (i.e, not installed via `parity-updater`) -// Returns the exit error code. -fn main_direct(force_can_restart: bool) -> i32 { - global_init(); - - let mut conf = { - let args = std::env::args().collect::>(); - parity_ethereum::Configuration::parse_cli(&args).unwrap_or_else(|e| e.exit()) - }; - - let logger = setup_log(&conf.logger_config()).unwrap_or_else(|e| { - eprintln!("{}", e); - process::exit(2) - }); - - if let Some(spec_override) = take_spec_name_override() { - conf.args.flag_testnet = false; - conf.args.arg_chain = spec_override; - } - - // FIXME: `pid_file` shouldn't need to cloned here - // see: `https://github.com/paritytech/parity-daemonize/pull/13` for more info - let handle = if let Some(pid) = conf.args.arg_daemon_pid_file.clone() { - info!("{}", Colour::Blue.paint("starting in daemon mode").to_string()); - let _ = std::io::stdout().flush(); - - match parity_daemonize::daemonize(pid) { - Ok(h) => Some(h), - Err(e) => { - error!( - "{}", - Colour::Red.paint(format!("{}", e)) - ); - return 1; - } - } - } else { - None - }; - - let can_restart = force_can_restart || conf.args.flag_can_restart; - - // increase max number of open files - raise_fd_limit(); - - let exit = Arc::new((Mutex::new(ExitStatus { - panicking: false, - should_exit: false, - should_restart: false, - spec_name_override: None - }), Condvar::new())); - - // Double panic can happen. So when we lock `ExitStatus` after the main thread is notified, it cannot be locked - // again. - let exiting = Arc::new(AtomicBool::new(false)); - - let exec = if can_restart { - start( - conf, - logger, - { - let e = exit.clone(); - let exiting = exiting.clone(); - move |new_chain: String| { - if !exiting.swap(true, Ordering::SeqCst) { - *e.0.lock() = ExitStatus { - panicking: false, - should_exit: true, - should_restart: true, - spec_name_override: Some(new_chain), - }; - e.1.notify_all(); - } - } - }, - { - let e = exit.clone(); - let exiting = exiting.clone(); - move || { - if !exiting.swap(true, Ordering::SeqCst) { - *e.0.lock() = ExitStatus { - panicking: false, - should_exit: true, - should_restart: true, - spec_name_override: None, - }; - e.1.notify_all(); - } - } - } - ) - } else { - trace!(target: "mode", "Not hypervised: not setting exit handlers."); - start(conf, logger, move |_| {}, move || {}) - }; - - let res = match exec { - Ok(result) => match result { - ExecutionAction::Instant(Some(s)) => { println!("{}", s); 0 }, - ExecutionAction::Instant(None) => 0, - ExecutionAction::Running(client) => { - panic_hook::set_with({ - let e = exit.clone(); - let exiting = exiting.clone(); - move |panic_msg| { - eprintln!("{}", panic_msg); - if !exiting.swap(true, Ordering::SeqCst) { - *e.0.lock() = ExitStatus { - panicking: true, - should_exit: true, - should_restart: false, - spec_name_override: None, - }; - e.1.notify_all(); - } - } - }); - - CtrlC::set_handler({ - let e = exit.clone(); - let exiting = exiting.clone(); - move || { - if !exiting.swap(true, Ordering::SeqCst) { - *e.0.lock() = ExitStatus { - panicking: false, - should_exit: true, - should_restart: false, - spec_name_override: None, - }; - e.1.notify_all(); - } - } - }); - - // so the client has started successfully - // if this is a daemon, detach from the parent process - if let Some(mut handle) = handle { - handle.detach() - } - - // Wait for signal - let mut lock = exit.0.lock(); - if !lock.should_exit { - let _ = exit.1.wait(&mut lock); - } - - client.shutdown(); - - if lock.should_restart { - if let Some(ref spec_name) = lock.spec_name_override { - set_spec_name_override(&spec_name.clone()); - } - PLEASE_RESTART_EXIT_CODE - } else { - if lock.panicking { - 1 - } else { - 0 - } - } - }, - }, - Err(err) => { - // error occured during start up - // if this is a daemon, detach from the parent process - if let Some(mut handle) = handle { - handle.detach_with_msg(format!("{}", Colour::Red.paint(&err))) - } - eprintln!("{}", err); - 1 - }, - }; - - global_cleanup(); - res -} - -fn println_trace_main(s: String) { - if env::var("RUST_LOG").ok().and_then(|s| s.find("main=trace")).is_some() { - println!("{}", s); - } -} - -macro_rules! trace_main { - ($arg:expr) => (println_trace_main($arg.into())); - ($($arg:tt)*) => (println_trace_main(format!("{}", format_args!($($arg)*)))); -} - -fn main() { - panic_hook::set_abort(); - - // the user has specified to run its originally installed binary (not via `parity-updater`) - let force_direct = std::env::args().any(|arg| arg == "--force-direct"); - - // absolute path to the current `binary` - let exe_path = std::env::current_exe().ok(); - - // the binary is named `target/xx/yy` - let development = exe_path - .as_ref() - .and_then(|p| { - p.parent() - .and_then(|p| p.parent()) - .and_then(|p| p.file_name()) - .map(|n| n == "target") - }) - .unwrap_or(false); - - // the binary is named `parity` - let same_name = exe_path - .as_ref() - .map_or(false, |p| { - p.file_stem().map_or(false, |n| n == PARITY_EXECUTABLE_NAME) - }); - - trace_main!("Starting up {} (force-direct: {}, development: {}, same-name: {})", - std::env::current_exe().ok().map_or_else(|| "".into(), |x| format!("{}", x.display())), - force_direct, - development, - same_name); - - if !force_direct && !development && same_name { - // Try to run the latest installed version of `parity`, - // Upon failure it falls back to the locally installed version of `parity` - // Everything run inside a loop, so we'll be able to restart from the child into a new version seamlessly. - loop { - // `Path` to the latest downloaded binary - let latest_exe = latest_exe_path().ok(); - - // `Latest´ binary exist - let have_update = latest_exe.as_ref().map_or(false, |p| p.exists()); - - // Canonicalized path to the current binary is not the same as to latest binary - let canonicalized_path_not_same = exe_path - .as_ref() - .map_or(false, |exe| latest_exe.as_ref() - .map_or(false, |lexe| exe.canonicalize().ok() != lexe.canonicalize().ok())); - - // Downloaded `binary` is newer - let update_is_newer = latest_binary_is_newer(&latest_exe, &exe_path); - trace_main!("Starting... (have-update: {}, non-updated-current: {}, update-is-newer: {})", have_update, canonicalized_path_not_same, update_is_newer); - - let exit_code = if have_update && canonicalized_path_not_same && update_is_newer { - trace_main!("Attempting to run latest update ({})...", - latest_exe.as_ref().expect("guarded by have_update; latest_exe must exist for have_update; qed").display()); - match run_parity() { - Ok(_) => 0, - // Restart parity - Err(Error::Restart) => PLEASE_RESTART_EXIT_CODE, - // Fall back to local version - Err(e) => { - error!(target: "updater", "Updated binary could not be executed error: {:?}. Falling back to local version", e); - main_direct(true) - } - } - } else { - trace_main!("No latest update. Attempting to direct..."); - main_direct(true) - }; - trace_main!("Latest binary exited with exit code: {}", exit_code); - if exit_code != PLEASE_RESTART_EXIT_CODE { - trace_main!("Quitting..."); - process::exit(exit_code); - } - trace!(target: "updater", "Re-running updater loop"); - } - } else { - trace_main!("Running direct"); - // Otherwise, we're presumably running the version we want. Just run and fall-through. - process::exit(main_direct(false)); - } +fn main() -> Result<(), i32> { + let conf = { + let args = std::env::args().collect::>(); + openethereum::Configuration::parse_cli(&args).unwrap_or_else(|e| e.exit()) + }; + + let logger = setup_log(&conf.logger_config()).unwrap_or_else(|e| { + eprintln!("{}", e); + process::exit(2) + }); + + // FIXME: `pid_file` shouldn't need to cloned here + // see: `https://github.com/paritytech/parity-daemonize/pull/13` for more info + let handle = if let Some(pid) = conf.args.arg_daemon_pid_file.clone() { + info!( + "{}", + Colour::Blue.paint("starting in daemon mode").to_string() + ); + let _ = std::io::stdout().flush(); + + match parity_daemonize::daemonize(pid) { + Ok(h) => Some(h), + Err(e) => { + error!("{}", Colour::Red.paint(format!("{}", e))); + return Err(1); + } + } + } else { + None + }; + + // increase max number of open files + raise_fd_limit(); + + let exit = Arc::new(( + Mutex::new(ExitStatus { + panicking: false, + should_exit: false, + }), + Condvar::new(), + )); + + // Double panic can happen. So when we lock `ExitStatus` after the main thread is notified, it cannot be locked + // again. + let exiting = Arc::new(AtomicBool::new(false)); + + trace!(target: "mode", "Not hypervised: not setting exit handlers."); + let exec = start(conf, logger); + + match exec { + Ok(result) => match result { + ExecutionAction::Instant(output) => { + if let Some(s) = output { + println!("{}", s); + } + } + ExecutionAction::Running(client) => { + panic_hook::set_with({ + let e = exit.clone(); + let exiting = exiting.clone(); + move |panic_msg| { + warn!("Panic occured, see stderr for details"); + eprintln!("{}", panic_msg); + if !exiting.swap(true, Ordering::SeqCst) { + *e.0.lock() = ExitStatus { + panicking: true, + should_exit: true, + }; + e.1.notify_all(); + } + } + }); + + CtrlC::set_handler({ + let e = exit.clone(); + let exiting = exiting.clone(); + move || { + if !exiting.swap(true, Ordering::SeqCst) { + *e.0.lock() = ExitStatus { + panicking: false, + should_exit: true, + }; + e.1.notify_all(); + } + } + }); + + // so the client has started successfully + // if this is a daemon, detach from the parent process + if let Some(mut handle) = handle { + handle.detach() + } + + // Wait for signal + let mut lock = exit.0.lock(); + if !lock.should_exit { + let _ = exit.1.wait(&mut lock); + } + + client.shutdown(); + + if lock.panicking { + return Err(1); + } + } + }, + Err(err) => { + // error occured during start up + // if this is a daemon, detach from the parent process + if let Some(mut handle) = handle { + handle.detach_with_msg(format!("{}", Colour::Red.paint(&err))) + } + eprintln!("{}", err); + return Err(1); + } + }; + + Ok(()) } diff --git a/parity/metrics.rs b/parity/metrics.rs new file mode 100644 index 00000000000..da083bd8d77 --- /dev/null +++ b/parity/metrics.rs @@ -0,0 +1,108 @@ +use std::{sync::Arc, time::Instant}; + +use crate::{futures::Future, rpc, rpc_apis}; + +use parking_lot::Mutex; + +use hyper::{service::service_fn_ok, Body, Method, Request, Response, Server, StatusCode}; + +use stats::{ + prometheus::{self, Encoder}, + prometheus_gauge, PrometheusMetrics, +}; + +#[derive(Debug, Clone, PartialEq)] +pub struct MetricsConfiguration { + /// Are metrics enabled (default is false)? + pub enabled: bool, + /// The IP of the network interface used (default is 127.0.0.1). + pub interface: String, + /// The network port (default is 3000). + pub port: u16, +} + +impl Default for MetricsConfiguration { + fn default() -> Self { + MetricsConfiguration { + enabled: false, + interface: "127.0.0.1".into(), + port: 3000, + } + } +} + +struct State { + rpc_apis: Arc, +} + +fn handle_request(req: Request, state: Arc>) -> Response { + let (parts, _body) = req.into_parts(); + match (parts.method, parts.uri.path()) { + (Method::GET, "/metrics") => { + let start = Instant::now(); + + let mut reg = prometheus::Registry::new(); + let state = state.lock(); + state.rpc_apis.client.prometheus_metrics(&mut reg); + state.rpc_apis.sync.prometheus_metrics(&mut reg); + let elapsed = start.elapsed(); + prometheus_gauge( + &mut reg, + "metrics_time", + "Time to perform rpc metrics", + elapsed.as_millis() as i64, + ); + + let mut buffer = vec![]; + let encoder = prometheus::TextEncoder::new(); + let metric_families = reg.gather(); + + encoder + .encode(&metric_families, &mut buffer) + .expect("all source of metrics are static; qed"); + let text = String::from_utf8(buffer).expect("metrics encoding is ASCII; qed"); + + Response::new(Body::from(text)) + } + (_, _) => { + let mut res = Response::new(Body::from("not found")); + *res.status_mut() = StatusCode::NOT_FOUND; + res + } + } +} + +/// Start the prometheus metrics server accessible via GET :/metrics +pub fn start_prometheus_metrics( + conf: &MetricsConfiguration, + deps: &rpc::Dependencies, +) -> Result<(), String> { + if !conf.enabled { + return Ok(()); + } + + let addr = format!("{}:{}", conf.interface, conf.port); + let addr = addr + .parse() + .map_err(|err| format!("Failed to parse address '{}': {}", addr, err))?; + + let state = State { + rpc_apis: deps.apis.clone(), + }; + let state = Arc::new(Mutex::new(state)); + + let server = Server::bind(&addr) + .serve(move || { + // This is the `Service` that will handle the connection. + // `service_fn_ok` is a helper to convert a function that + // returns a Response into a `Service`. + let state = state.clone(); + service_fn_ok(move |req: Request| handle_request(req, state.clone())) + }) + .map_err(|e| eprintln!("server error: {}", e)); + println!("Listening on http://{}", addr); + + deps.executor.spawn(server); + + Ok(()) +} diff --git a/parity/modules.rs b/parity/modules.rs index 9f5d25a11e2..5cf6971d67f 100644 --- a/parity/modules.rs +++ b/parity/modules.rs @@ -1,63 +1,61 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::sync::{Arc, mpsc}; +use std::sync::{mpsc, Arc}; -use ethcore::client::BlockChainClient; -use sync::{self, AttachedProtocol, SyncConfig, NetworkConfiguration, Params, ConnectionFilter}; -use ethcore::snapshot::SnapshotService; -use light::Provider; +use ethcore::{client::BlockChainClient, snapshot::SnapshotService}; +use std::collections::BTreeSet; +use sync::{self, ConnectionFilter, NetworkConfiguration, Params, SyncConfig}; +use types::BlockNumber; -pub use sync::{EthSync, SyncProvider, ManageNetwork, PrivateTxHandler}; pub use ethcore::client::ChainNotify; use ethcore_logger::Config as LogConfig; +pub use sync::{EthSync, ManageNetwork, SyncProvider}; pub type SyncModules = ( - Arc, - Arc, - Arc, - mpsc::Sender, + Arc, + Arc, + Arc, + mpsc::Sender, ); pub fn sync( - config: SyncConfig, - network_config: NetworkConfiguration, - chain: Arc, - snapshot_service: Arc, - private_tx_handler: Option>, - provider: Arc, - _log_settings: &LogConfig, - attached_protos: Vec, - connection_filter: Option>, + config: SyncConfig, + network_config: NetworkConfiguration, + chain: Arc, + forks: BTreeSet, + snapshot_service: Arc, + _log_settings: &LogConfig, + connection_filter: Option>, ) -> Result { - let eth_sync = EthSync::new(Params { - config, - chain, - provider, - snapshot_service, - private_tx_handler, - network_config, - attached_protos, - }, - connection_filter)?; - - Ok(( - eth_sync.clone() as Arc, - eth_sync.clone() as Arc, - eth_sync.clone() as Arc, - eth_sync.priority_tasks() - )) + let eth_sync = EthSync::new( + Params { + config, + chain, + forks, + snapshot_service, + network_config, + }, + connection_filter, + )?; + + Ok(( + eth_sync.clone() as Arc, + eth_sync.clone() as Arc, + eth_sync.clone() as Arc, + eth_sync.priority_tasks(), + )) } diff --git a/parity/params.rs b/parity/params.rs index d01f784616c..3d3163b19eb 100644 --- a/parity/params.rs +++ b/parity/params.rs @@ -1,488 +1,536 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::collections::HashSet; -use std::time::Duration; -use std::{str, fs, fmt}; -use std::num::NonZeroU32; +use std::{collections::HashSet, fmt, fs, num::NonZeroU32, str, time::Duration}; -use ethcore::client::Mode; -use ethcore::ethereum; -use ethcore::spec::{Spec, SpecParams}; -use ethereum_types::{U256, Address}; -use parity_runtime::Executor; -use hash_fetch::fetch::Client as FetchClient; +use ethcore::{ + client::Mode, + ethereum, + spec::{Spec, SpecParams}, +}; +use ethereum_types::{Address, U256}; +use fetch::Client as FetchClient; use journaldb::Algorithm; -use miner::gas_pricer::GasPricer; -use miner::gas_price_calibrator::{GasPriceCalibratorOptions, GasPriceCalibrator}; +use miner::{ + gas_price_calibrator::{GasPriceCalibrator, GasPriceCalibratorOptions}, + gas_pricer::GasPricer, +}; +use parity_runtime::Executor; use parity_version::version_data; use user_defaults::UserDefaults; +use crate::configuration; + #[derive(Debug, PartialEq)] pub enum SpecType { - Foundation, - Classic, - Poanet, - Tobalaba, - Expanse, - Musicoin, - Ellaism, - Mix, - Callisto, - Morden, - Ropsten, - Kovan, - Rinkeby, - Goerli, - Kotti, - Sokol, - Dev, - Custom(String), + Foundation, + Poanet, + Xdai, + Volta, + Ewc, + Musicoin, + Ellaism, + Mix, + Callisto, + Morden, + Ropsten, + Kovan, + Rinkeby, + Goerli, + Sokol, + Dev, + Custom(String), } impl Default for SpecType { - fn default() -> Self { - SpecType::Foundation - } + fn default() -> Self { + SpecType::Foundation + } } impl str::FromStr for SpecType { - type Err = String; - - fn from_str(s: &str) -> Result { - let spec = match s { - "ethereum" | "frontier" | "homestead" | "byzantium" | "foundation" | "mainnet" => SpecType::Foundation, - "classic" | "frontier-dogmatic" | "homestead-dogmatic" => SpecType::Classic, - "poanet" | "poacore" => SpecType::Poanet, - "tobalaba" => SpecType::Tobalaba, - "expanse" => SpecType::Expanse, - "musicoin" => SpecType::Musicoin, - "ellaism" => SpecType::Ellaism, - "mix" => SpecType::Mix, - "callisto" => SpecType::Callisto, - "morden" | "classic-testnet" => SpecType::Morden, - "ropsten" => SpecType::Ropsten, - "kovan" | "testnet" => SpecType::Kovan, - "rinkeby" => SpecType::Rinkeby, - "goerli" | "görli" => SpecType::Goerli, - "kotti" => SpecType::Kotti, - "sokol" | "poasokol" => SpecType::Sokol, - "dev" => SpecType::Dev, - other => SpecType::Custom(other.into()), - }; - Ok(spec) - } + type Err = String; + + fn from_str(s: &str) -> Result { + let spec = match s { + "eth" | "ethereum" | "foundation" | "mainnet" => SpecType::Foundation, + "poanet" | "poacore" => SpecType::Poanet, + "xdai" => SpecType::Xdai, + "volta" => SpecType::Volta, + "ewc" | "energyweb" => SpecType::Ewc, + "musicoin" => SpecType::Musicoin, + "ellaism" => SpecType::Ellaism, + "mix" => SpecType::Mix, + "callisto" => SpecType::Callisto, + "morden" => SpecType::Morden, + "ropsten" => SpecType::Ropsten, + "kovan" => SpecType::Kovan, + "rinkeby" => SpecType::Rinkeby, + "goerli" | "görli" | "testnet" => SpecType::Goerli, + "sokol" | "poasokol" => SpecType::Sokol, + "dev" => SpecType::Dev, + other => SpecType::Custom(other.into()), + }; + Ok(spec) + } } impl fmt::Display for SpecType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(match *self { - SpecType::Foundation => "foundation", - SpecType::Classic => "classic", - SpecType::Poanet => "poanet", - SpecType::Tobalaba => "tobalaba", - SpecType::Expanse => "expanse", - SpecType::Musicoin => "musicoin", - SpecType::Ellaism => "ellaism", - SpecType::Mix => "mix", - SpecType::Callisto => "callisto", - SpecType::Morden => "morden", - SpecType::Ropsten => "ropsten", - SpecType::Kovan => "kovan", - SpecType::Rinkeby => "rinkeby", - SpecType::Goerli => "goerli", - SpecType::Kotti => "kotti", - SpecType::Sokol => "sokol", - SpecType::Dev => "dev", - SpecType::Custom(ref custom) => custom, - }) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(match *self { + SpecType::Foundation => "foundation", + SpecType::Poanet => "poanet", + SpecType::Xdai => "xdai", + SpecType::Volta => "volta", + SpecType::Ewc => "energyweb", + SpecType::Musicoin => "musicoin", + SpecType::Ellaism => "ellaism", + SpecType::Mix => "mix", + SpecType::Callisto => "callisto", + SpecType::Morden => "morden", + SpecType::Ropsten => "ropsten", + SpecType::Kovan => "kovan", + SpecType::Rinkeby => "rinkeby", + SpecType::Goerli => "goerli", + SpecType::Sokol => "sokol", + SpecType::Dev => "dev", + SpecType::Custom(ref custom) => custom, + }) + } } impl SpecType { - pub fn spec<'a, T: Into>>(&self, params: T) -> Result { - let params = params.into(); - match *self { - SpecType::Foundation => Ok(ethereum::new_foundation(params)), - SpecType::Classic => Ok(ethereum::new_classic(params)), - SpecType::Poanet => Ok(ethereum::new_poanet(params)), - SpecType::Tobalaba => Ok(ethereum::new_tobalaba(params)), - SpecType::Expanse => Ok(ethereum::new_expanse(params)), - SpecType::Musicoin => Ok(ethereum::new_musicoin(params)), - SpecType::Ellaism => Ok(ethereum::new_ellaism(params)), - SpecType::Mix => Ok(ethereum::new_mix(params)), - SpecType::Callisto => Ok(ethereum::new_callisto(params)), - SpecType::Morden => Ok(ethereum::new_morden(params)), - SpecType::Ropsten => Ok(ethereum::new_ropsten(params)), - SpecType::Kovan => Ok(ethereum::new_kovan(params)), - SpecType::Rinkeby => Ok(ethereum::new_rinkeby(params)), - SpecType::Goerli => Ok(ethereum::new_goerli(params)), - SpecType::Kotti => Ok(ethereum::new_kotti(params)), - SpecType::Sokol => Ok(ethereum::new_sokol(params)), - SpecType::Dev => Ok(Spec::new_instant()), - SpecType::Custom(ref filename) => { - let file = fs::File::open(filename).map_err(|e| format!("Could not load specification file at {}: {}", filename, e))?; - Spec::load(params, file) - } - } - } - - pub fn legacy_fork_name(&self) -> Option { - match *self { - SpecType::Classic => Some("classic".to_owned()), - SpecType::Expanse => Some("expanse".to_owned()), - SpecType::Musicoin => Some("musicoin".to_owned()), - _ => None, - } - } + pub fn spec<'a, T: Into>>(&self, params: T) -> Result { + let params = params.into(); + match *self { + SpecType::Foundation => Ok(ethereum::new_foundation(params)), + SpecType::Poanet => Ok(ethereum::new_poanet(params)), + SpecType::Xdai => Ok(ethereum::new_xdai(params)), + SpecType::Volta => Ok(ethereum::new_volta(params)), + SpecType::Ewc => Ok(ethereum::new_ewc(params)), + SpecType::Musicoin => Ok(ethereum::new_musicoin(params)), + SpecType::Ellaism => Ok(ethereum::new_ellaism(params)), + SpecType::Mix => Ok(ethereum::new_mix(params)), + SpecType::Callisto => Ok(ethereum::new_callisto(params)), + SpecType::Morden => Ok(ethereum::new_morden(params)), + SpecType::Ropsten => Ok(ethereum::new_ropsten(params)), + SpecType::Kovan => Ok(ethereum::new_kovan(params)), + SpecType::Rinkeby => Ok(ethereum::new_rinkeby(params)), + SpecType::Goerli => Ok(ethereum::new_goerli(params)), + SpecType::Sokol => Ok(ethereum::new_sokol(params)), + SpecType::Dev => Ok(Spec::new_instant()), + SpecType::Custom(ref filename) => { + let file = fs::File::open(filename).map_err(|e| { + format!("Could not load specification file at {}: {}", filename, e) + })?; + Spec::load(params, file) + } + } + } + + pub fn legacy_fork_name(&self) -> Option { + match *self { + SpecType::Musicoin => Some("musicoin".to_owned()), + _ => None, + } + } } #[derive(Debug, PartialEq)] pub enum Pruning { - Specific(Algorithm), - Auto, + Specific(Algorithm), + Auto, } impl Default for Pruning { - fn default() -> Self { - Pruning::Auto - } + fn default() -> Self { + Pruning::Auto + } } impl str::FromStr for Pruning { - type Err = String; - - fn from_str(s: &str) -> Result { - match s { - "auto" => Ok(Pruning::Auto), - other => other.parse().map(Pruning::Specific), - } - } + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "auto" => Ok(Pruning::Auto), + other => other.parse().map(Pruning::Specific), + } + } } impl Pruning { - pub fn to_algorithm(&self, user_defaults: &UserDefaults) -> Algorithm { - match *self { - Pruning::Specific(algo) => algo, - Pruning::Auto => user_defaults.pruning, - } - } + pub fn to_algorithm(&self, user_defaults: &UserDefaults) -> Algorithm { + match *self { + Pruning::Specific(algo) => algo, + Pruning::Auto => user_defaults.pruning, + } + } } #[derive(Debug, PartialEq)] pub struct ResealPolicy { - pub own: bool, - pub external: bool, + pub own: bool, + pub external: bool, } impl Default for ResealPolicy { - fn default() -> Self { - ResealPolicy { - own: true, - external: true, - } - } + fn default() -> Self { + ResealPolicy { + own: true, + external: true, + } + } } impl str::FromStr for ResealPolicy { - type Err = String; - - fn from_str(s: &str) -> Result { - let (own, external) = match s { - "none" => (false, false), - "own" => (true, false), - "ext" => (false, true), - "all" => (true, true), - x => return Err(format!("Invalid reseal value: {}", x)), - }; - - let reseal = ResealPolicy { - own: own, - external: external, - }; - - Ok(reseal) - } + type Err = String; + + fn from_str(s: &str) -> Result { + let (own, external) = match s { + "none" => (false, false), + "own" => (true, false), + "ext" => (false, true), + "all" => (true, true), + x => return Err(format!("Invalid reseal value: {}", x)), + }; + + let reseal = ResealPolicy { + own: own, + external: external, + }; + + Ok(reseal) + } } #[derive(Debug, PartialEq)] pub struct AccountsConfig { - pub iterations: NonZeroU32, - pub refresh_time: u64, - pub testnet: bool, - pub password_files: Vec, - pub unlocked_accounts: Vec
, - pub enable_hardware_wallets: bool, - pub enable_fast_unlock: bool, + pub iterations: NonZeroU32, + pub refresh_time: u64, + pub testnet: bool, + pub password_files: Vec, + pub unlocked_accounts: Vec
, + pub enable_fast_unlock: bool, } impl Default for AccountsConfig { - fn default() -> Self { - AccountsConfig { - iterations: NonZeroU32::new(10240).expect("10240 > 0; qed"), - refresh_time: 5, - testnet: false, - password_files: Vec::new(), - unlocked_accounts: Vec::new(), - enable_hardware_wallets: true, - enable_fast_unlock: false, - } - } + fn default() -> Self { + AccountsConfig { + iterations: NonZeroU32::new(10240).expect("10240 > 0; qed"), + refresh_time: 5, + testnet: false, + password_files: Vec::new(), + unlocked_accounts: Vec::new(), + enable_fast_unlock: false, + } + } } #[derive(Debug, PartialEq)] pub enum GasPricerConfig { - Fixed(U256), - Calibrated { - usd_per_tx: f32, - recalibration_period: Duration, - } + Fixed(U256), + Calibrated { + usd_per_tx: f32, + recalibration_period: Duration, + api_endpoint: String, + }, } impl Default for GasPricerConfig { - fn default() -> Self { - GasPricerConfig::Calibrated { - usd_per_tx: 0.0001f32, - recalibration_period: Duration::from_secs(3600), - } - } + fn default() -> Self { + GasPricerConfig::Calibrated { + usd_per_tx: 0.0001f32, + recalibration_period: Duration::from_secs(3600), + api_endpoint: configuration::ETHERSCAN_ETH_PRICE_ENDPOINT.to_string(), + } + } } impl GasPricerConfig { - pub fn to_gas_pricer(&self, fetch: FetchClient, p: Executor) -> GasPricer { - match *self { - GasPricerConfig::Fixed(u) => GasPricer::Fixed(u), - GasPricerConfig::Calibrated { usd_per_tx, recalibration_period, .. } => { - GasPricer::new_calibrated( - GasPriceCalibrator::new( - GasPriceCalibratorOptions { - usd_per_tx: usd_per_tx, - recalibration_period: recalibration_period, - }, - fetch, - p, - ) - ) - } - } - } + pub fn to_gas_pricer(&self, fetch: FetchClient, p: Executor) -> GasPricer { + match *self { + GasPricerConfig::Fixed(u) => GasPricer::Fixed(u), + GasPricerConfig::Calibrated { + usd_per_tx, + recalibration_period, + ref api_endpoint, + } => GasPricer::new_calibrated(GasPriceCalibrator::new( + GasPriceCalibratorOptions { + usd_per_tx: usd_per_tx, + recalibration_period: recalibration_period, + }, + fetch, + p, + api_endpoint.clone(), + )), + } + } } #[derive(Debug, PartialEq)] pub struct MinerExtras { - pub author: Address, - pub engine_signer: Address, - pub extra_data: Vec, - pub gas_range_target: (U256, U256), - pub work_notify: Vec, - pub local_accounts: HashSet
, + pub author: Address, + pub engine_signer: Address, + pub extra_data: Vec, + pub gas_range_target: (U256, U256), + pub work_notify: Vec, + pub local_accounts: HashSet
, } impl Default for MinerExtras { - fn default() -> Self { - MinerExtras { - author: Default::default(), - engine_signer: Default::default(), - extra_data: version_data(), - gas_range_target: (8_000_000.into(), 10_000_000.into()), - work_notify: Default::default(), - local_accounts: Default::default(), - } - } + fn default() -> Self { + MinerExtras { + author: Default::default(), + engine_signer: Default::default(), + extra_data: version_data(), + gas_range_target: (8_000_000.into(), 10_000_000.into()), + work_notify: Default::default(), + local_accounts: Default::default(), + } + } } /// 3-value enum. #[derive(Debug, Clone, Copy, PartialEq)] pub enum Switch { - /// True. - On, - /// False. - Off, - /// Auto. - Auto, + /// True. + On, + /// False. + Off, + /// Auto. + Auto, } impl Default for Switch { - fn default() -> Self { - Switch::Auto - } + fn default() -> Self { + Switch::Auto + } } impl str::FromStr for Switch { - type Err = String; - - fn from_str(s: &str) -> Result { - match s { - "on" => Ok(Switch::On), - "off" => Ok(Switch::Off), - "auto" => Ok(Switch::Auto), - other => Err(format!("Invalid switch value: {}", other)) - } - } + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "on" => Ok(Switch::On), + "off" => Ok(Switch::Off), + "auto" => Ok(Switch::Auto), + other => Err(format!("Invalid switch value: {}", other)), + } + } } -pub fn tracing_switch_to_bool(switch: Switch, user_defaults: &UserDefaults) -> Result { - match (user_defaults.is_first_launch, switch, user_defaults.tracing) { - (false, Switch::On, false) => Err("TraceDB resync required".into()), - (_, Switch::On, _) => Ok(true), - (_, Switch::Off, _) => Ok(false), - (_, Switch::Auto, def) => Ok(def), - } +pub fn tracing_switch_to_bool( + switch: Switch, + user_defaults: &UserDefaults, +) -> Result { + match (user_defaults.is_first_launch, switch, user_defaults.tracing) { + (false, Switch::On, false) => Err("TraceDB resync required".into()), + (_, Switch::On, _) => Ok(true), + (_, Switch::Off, _) => Ok(false), + (_, Switch::Auto, def) => Ok(def), + } } -pub fn fatdb_switch_to_bool(switch: Switch, user_defaults: &UserDefaults, _algorithm: Algorithm) -> Result { - let result = match (user_defaults.is_first_launch, switch, user_defaults.fat_db) { - (false, Switch::On, false) => Err("FatDB resync required".into()), - (_, Switch::On, _) => Ok(true), - (_, Switch::Off, _) => Ok(false), - (_, Switch::Auto, def) => Ok(def), - }; - result +pub fn fatdb_switch_to_bool( + switch: Switch, + user_defaults: &UserDefaults, + _algorithm: Algorithm, +) -> Result { + let result = match (user_defaults.is_first_launch, switch, user_defaults.fat_db) { + (false, Switch::On, false) => Err("FatDB resync required".into()), + (_, Switch::On, _) => Ok(true), + (_, Switch::Off, _) => Ok(false), + (_, Switch::Auto, def) => Ok(def), + }; + result } -pub fn mode_switch_to_bool(switch: Option, user_defaults: &UserDefaults) -> Result { - Ok(switch.unwrap_or(user_defaults.mode().clone())) +pub fn mode_switch_to_bool( + switch: Option, + user_defaults: &UserDefaults, +) -> Result { + Ok(switch.unwrap_or(user_defaults.mode().clone())) } #[cfg(test)] mod tests { - use journaldb::Algorithm; - use user_defaults::UserDefaults; - use super::{SpecType, Pruning, ResealPolicy, Switch, tracing_switch_to_bool}; - - #[test] - fn test_spec_type_parsing() { - assert_eq!(SpecType::Foundation, "foundation".parse().unwrap()); - assert_eq!(SpecType::Foundation, "frontier".parse().unwrap()); - assert_eq!(SpecType::Foundation, "homestead".parse().unwrap()); - assert_eq!(SpecType::Foundation, "byzantium".parse().unwrap()); - assert_eq!(SpecType::Foundation, "mainnet".parse().unwrap()); - assert_eq!(SpecType::Foundation, "ethereum".parse().unwrap()); - assert_eq!(SpecType::Classic, "classic".parse().unwrap()); - assert_eq!(SpecType::Classic, "frontier-dogmatic".parse().unwrap()); - assert_eq!(SpecType::Classic, "homestead-dogmatic".parse().unwrap()); - assert_eq!(SpecType::Poanet, "poanet".parse().unwrap()); - assert_eq!(SpecType::Poanet, "poacore".parse().unwrap()); - assert_eq!(SpecType::Tobalaba, "tobalaba".parse().unwrap()); - assert_eq!(SpecType::Expanse, "expanse".parse().unwrap()); - assert_eq!(SpecType::Musicoin, "musicoin".parse().unwrap()); - assert_eq!(SpecType::Ellaism, "ellaism".parse().unwrap()); - assert_eq!(SpecType::Mix, "mix".parse().unwrap()); - assert_eq!(SpecType::Callisto, "callisto".parse().unwrap()); - assert_eq!(SpecType::Morden, "morden".parse().unwrap()); - assert_eq!(SpecType::Morden, "classic-testnet".parse().unwrap()); - assert_eq!(SpecType::Ropsten, "ropsten".parse().unwrap()); - assert_eq!(SpecType::Kovan, "kovan".parse().unwrap()); - assert_eq!(SpecType::Kovan, "testnet".parse().unwrap()); - assert_eq!(SpecType::Rinkeby, "rinkeby".parse().unwrap()); - assert_eq!(SpecType::Goerli, "goerli".parse().unwrap()); - assert_eq!(SpecType::Goerli, "görli".parse().unwrap()); - assert_eq!(SpecType::Kotti, "kotti".parse().unwrap()); - assert_eq!(SpecType::Sokol, "sokol".parse().unwrap()); - assert_eq!(SpecType::Sokol, "poasokol".parse().unwrap()); - } - - #[test] - fn test_spec_type_default() { - assert_eq!(SpecType::Foundation, SpecType::default()); - } - - #[test] - fn test_spec_type_display() { - assert_eq!(format!("{}", SpecType::Foundation), "foundation"); - assert_eq!(format!("{}", SpecType::Classic), "classic"); - assert_eq!(format!("{}", SpecType::Poanet), "poanet"); - assert_eq!(format!("{}", SpecType::Tobalaba), "tobalaba"); - assert_eq!(format!("{}", SpecType::Expanse), "expanse"); - assert_eq!(format!("{}", SpecType::Musicoin), "musicoin"); - assert_eq!(format!("{}", SpecType::Ellaism), "ellaism"); - assert_eq!(format!("{}", SpecType::Mix), "mix"); - assert_eq!(format!("{}", SpecType::Callisto), "callisto"); - assert_eq!(format!("{}", SpecType::Morden), "morden"); - assert_eq!(format!("{}", SpecType::Ropsten), "ropsten"); - assert_eq!(format!("{}", SpecType::Kovan), "kovan"); - assert_eq!(format!("{}", SpecType::Rinkeby), "rinkeby"); - assert_eq!(format!("{}", SpecType::Goerli), "goerli"); - assert_eq!(format!("{}", SpecType::Kotti), "kotti"); - assert_eq!(format!("{}", SpecType::Sokol), "sokol"); - assert_eq!(format!("{}", SpecType::Dev), "dev"); - assert_eq!(format!("{}", SpecType::Custom("foo/bar".into())), "foo/bar"); - } - - #[test] - fn test_pruning_parsing() { - assert_eq!(Pruning::Auto, "auto".parse().unwrap()); - assert_eq!(Pruning::Specific(Algorithm::Archive), "archive".parse().unwrap()); - assert_eq!(Pruning::Specific(Algorithm::EarlyMerge), "light".parse().unwrap()); - assert_eq!(Pruning::Specific(Algorithm::OverlayRecent), "fast".parse().unwrap()); - assert_eq!(Pruning::Specific(Algorithm::RefCounted), "basic".parse().unwrap()); - } - - #[test] - fn test_pruning_default() { - assert_eq!(Pruning::Auto, Pruning::default()); - } - - #[test] - fn test_reseal_policy_parsing() { - let none = ResealPolicy { own: false, external: false }; - let own = ResealPolicy { own: true, external: false }; - let ext = ResealPolicy { own: false, external: true }; - let all = ResealPolicy { own: true, external: true }; - assert_eq!(none, "none".parse().unwrap()); - assert_eq!(own, "own".parse().unwrap()); - assert_eq!(ext, "ext".parse().unwrap()); - assert_eq!(all, "all".parse().unwrap()); - } - - #[test] - fn test_reseal_policy_default() { - let all = ResealPolicy { own: true, external: true }; - assert_eq!(all, ResealPolicy::default()); - } - - #[test] - fn test_switch_parsing() { - assert_eq!(Switch::On, "on".parse().unwrap()); - assert_eq!(Switch::Off, "off".parse().unwrap()); - assert_eq!(Switch::Auto, "auto".parse().unwrap()); - } - - #[test] - fn test_switch_default() { - assert_eq!(Switch::default(), Switch::Auto); - } - - fn user_defaults_with_tracing(first_launch: bool, tracing: bool) -> UserDefaults { - let mut ud = UserDefaults::default(); - ud.is_first_launch = first_launch; - ud.tracing = tracing; - ud - } - - #[test] - fn test_switch_to_bool() { - assert!(!tracing_switch_to_bool(Switch::Off, &user_defaults_with_tracing(true, true)).unwrap()); - assert!(!tracing_switch_to_bool(Switch::Off, &user_defaults_with_tracing(true, false)).unwrap()); - assert!(!tracing_switch_to_bool(Switch::Off, &user_defaults_with_tracing(false, true)).unwrap()); - assert!(!tracing_switch_to_bool(Switch::Off, &user_defaults_with_tracing(false, false)).unwrap()); - - assert!(tracing_switch_to_bool(Switch::On, &user_defaults_with_tracing(true, true)).unwrap()); - assert!(tracing_switch_to_bool(Switch::On, &user_defaults_with_tracing(true, false)).unwrap()); - assert!(tracing_switch_to_bool(Switch::On, &user_defaults_with_tracing(false, true)).unwrap()); - assert!(tracing_switch_to_bool(Switch::On, &user_defaults_with_tracing(false, false)).is_err()); - } + use super::{tracing_switch_to_bool, Pruning, ResealPolicy, SpecType, Switch}; + use journaldb::Algorithm; + use user_defaults::UserDefaults; + + #[test] + fn test_spec_type_parsing() { + assert_eq!(SpecType::Foundation, "eth".parse().unwrap()); + assert_eq!(SpecType::Foundation, "ethereum".parse().unwrap()); + assert_eq!(SpecType::Foundation, "foundation".parse().unwrap()); + assert_eq!(SpecType::Foundation, "mainnet".parse().unwrap()); + assert_eq!(SpecType::Poanet, "poanet".parse().unwrap()); + assert_eq!(SpecType::Poanet, "poacore".parse().unwrap()); + assert_eq!(SpecType::Xdai, "xdai".parse().unwrap()); + assert_eq!(SpecType::Volta, "volta".parse().unwrap()); + assert_eq!(SpecType::Ewc, "ewc".parse().unwrap()); + assert_eq!(SpecType::Ewc, "energyweb".parse().unwrap()); + assert_eq!(SpecType::Musicoin, "musicoin".parse().unwrap()); + assert_eq!(SpecType::Ellaism, "ellaism".parse().unwrap()); + assert_eq!(SpecType::Mix, "mix".parse().unwrap()); + assert_eq!(SpecType::Callisto, "callisto".parse().unwrap()); + assert_eq!(SpecType::Morden, "morden".parse().unwrap()); + assert_eq!(SpecType::Ropsten, "ropsten".parse().unwrap()); + assert_eq!(SpecType::Kovan, "kovan".parse().unwrap()); + assert_eq!(SpecType::Rinkeby, "rinkeby".parse().unwrap()); + assert_eq!(SpecType::Goerli, "goerli".parse().unwrap()); + assert_eq!(SpecType::Goerli, "görli".parse().unwrap()); + assert_eq!(SpecType::Goerli, "testnet".parse().unwrap()); + assert_eq!(SpecType::Sokol, "sokol".parse().unwrap()); + assert_eq!(SpecType::Sokol, "poasokol".parse().unwrap()); + } + + #[test] + fn test_spec_type_default() { + assert_eq!(SpecType::Foundation, SpecType::default()); + } + + #[test] + fn test_spec_type_display() { + assert_eq!(format!("{}", SpecType::Foundation), "foundation"); + assert_eq!(format!("{}", SpecType::Poanet), "poanet"); + assert_eq!(format!("{}", SpecType::Xdai), "xdai"); + assert_eq!(format!("{}", SpecType::Volta), "volta"); + assert_eq!(format!("{}", SpecType::Ewc), "energyweb"); + assert_eq!(format!("{}", SpecType::Musicoin), "musicoin"); + assert_eq!(format!("{}", SpecType::Ellaism), "ellaism"); + assert_eq!(format!("{}", SpecType::Mix), "mix"); + assert_eq!(format!("{}", SpecType::Callisto), "callisto"); + assert_eq!(format!("{}", SpecType::Morden), "morden"); + assert_eq!(format!("{}", SpecType::Ropsten), "ropsten"); + assert_eq!(format!("{}", SpecType::Kovan), "kovan"); + assert_eq!(format!("{}", SpecType::Rinkeby), "rinkeby"); + assert_eq!(format!("{}", SpecType::Goerli), "goerli"); + assert_eq!(format!("{}", SpecType::Sokol), "sokol"); + assert_eq!(format!("{}", SpecType::Dev), "dev"); + assert_eq!(format!("{}", SpecType::Custom("foo/bar".into())), "foo/bar"); + } + + #[test] + fn test_pruning_parsing() { + assert_eq!(Pruning::Auto, "auto".parse().unwrap()); + assert_eq!( + Pruning::Specific(Algorithm::Archive), + "archive".parse().unwrap() + ); + assert_eq!( + Pruning::Specific(Algorithm::EarlyMerge), + "light".parse().unwrap() + ); + assert_eq!( + Pruning::Specific(Algorithm::OverlayRecent), + "fast".parse().unwrap() + ); + assert_eq!( + Pruning::Specific(Algorithm::RefCounted), + "basic".parse().unwrap() + ); + } + + #[test] + fn test_pruning_default() { + assert_eq!(Pruning::Auto, Pruning::default()); + } + + #[test] + fn test_reseal_policy_parsing() { + let none = ResealPolicy { + own: false, + external: false, + }; + let own = ResealPolicy { + own: true, + external: false, + }; + let ext = ResealPolicy { + own: false, + external: true, + }; + let all = ResealPolicy { + own: true, + external: true, + }; + assert_eq!(none, "none".parse().unwrap()); + assert_eq!(own, "own".parse().unwrap()); + assert_eq!(ext, "ext".parse().unwrap()); + assert_eq!(all, "all".parse().unwrap()); + } + + #[test] + fn test_reseal_policy_default() { + let all = ResealPolicy { + own: true, + external: true, + }; + assert_eq!(all, ResealPolicy::default()); + } + + #[test] + fn test_switch_parsing() { + assert_eq!(Switch::On, "on".parse().unwrap()); + assert_eq!(Switch::Off, "off".parse().unwrap()); + assert_eq!(Switch::Auto, "auto".parse().unwrap()); + } + + #[test] + fn test_switch_default() { + assert_eq!(Switch::default(), Switch::Auto); + } + + fn user_defaults_with_tracing(first_launch: bool, tracing: bool) -> UserDefaults { + let mut ud = UserDefaults::default(); + ud.is_first_launch = first_launch; + ud.tracing = tracing; + ud + } + + #[test] + fn test_switch_to_bool() { + assert!( + !tracing_switch_to_bool(Switch::Off, &user_defaults_with_tracing(true, true)).unwrap() + ); + assert!( + !tracing_switch_to_bool(Switch::Off, &user_defaults_with_tracing(true, false)).unwrap() + ); + assert!( + !tracing_switch_to_bool(Switch::Off, &user_defaults_with_tracing(false, true)).unwrap() + ); + assert!( + !tracing_switch_to_bool(Switch::Off, &user_defaults_with_tracing(false, false)) + .unwrap() + ); + + assert!( + tracing_switch_to_bool(Switch::On, &user_defaults_with_tracing(true, true)).unwrap() + ); + assert!( + tracing_switch_to_bool(Switch::On, &user_defaults_with_tracing(true, false)).unwrap() + ); + assert!( + tracing_switch_to_bool(Switch::On, &user_defaults_with_tracing(false, true)).unwrap() + ); + assert!( + tracing_switch_to_bool(Switch::On, &user_defaults_with_tracing(false, false)).is_err() + ); + } } diff --git a/parity/presale.rs b/parity/presale.rs index 162d149b544..b160ab77448 100644 --- a/parity/presale.rs +++ b/parity/presale.rs @@ -1,58 +1,59 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - +// along with OpenEthereum. If not, see . use ethkey::Password; use ethstore::PresaleWallet; -use helpers::{password_prompt, password_from_file}; +use helpers::{password_from_file, password_prompt}; use params::SpecType; use std::num::NonZeroU32; #[derive(Debug, PartialEq)] pub struct ImportWallet { - pub iterations: NonZeroU32, - pub path: String, - pub spec: SpecType, - pub wallet_path: String, - pub password_file: Option, + pub iterations: NonZeroU32, + pub path: String, + pub spec: SpecType, + pub wallet_path: String, + pub password_file: Option, } pub fn execute(cmd: ImportWallet) -> Result { - let password = match cmd.password_file.clone() { - Some(file) => password_from_file(file)?, - None => password_prompt()?, - }; - - let wallet = PresaleWallet::open(cmd.wallet_path.clone()).map_err(|_| "Unable to open presale wallet.")?; - let kp = wallet.decrypt(&password).map_err(|_| "Invalid password.")?; - let address = kp.address(); - import_account(&cmd, kp, password); - Ok(format!("{:?}", address)) + let password = match cmd.password_file.clone() { + Some(file) => password_from_file(file)?, + None => password_prompt()?, + }; + + let wallet = PresaleWallet::open(cmd.wallet_path.clone()) + .map_err(|_| "Unable to open presale wallet.")?; + let kp = wallet.decrypt(&password).map_err(|_| "Invalid password.")?; + let address = kp.address(); + import_account(&cmd, kp, password); + Ok(format!("{:?}", address)) } #[cfg(feature = "accounts")] pub fn import_account(cmd: &ImportWallet, kp: ethkey::KeyPair, password: Password) { - use accounts::{AccountProvider, AccountProviderSettings}; - use ethstore::EthStore; - use ethstore::accounts_dir::RootDiskDirectory; - - let dir = Box::new(RootDiskDirectory::create(cmd.path.clone()).unwrap()); - let secret_store = Box::new(EthStore::open_with_iterations(dir, cmd.iterations).unwrap()); - let acc_provider = AccountProvider::new(secret_store, AccountProviderSettings::default()); - acc_provider.insert_account(kp.secret().clone(), &password).unwrap(); + use accounts::{AccountProvider, AccountProviderSettings}; + use ethstore::{accounts_dir::RootDiskDirectory, EthStore}; + + let dir = Box::new(RootDiskDirectory::create(cmd.path.clone()).unwrap()); + let secret_store = Box::new(EthStore::open_with_iterations(dir, cmd.iterations).unwrap()); + let acc_provider = AccountProvider::new(secret_store, AccountProviderSettings::default()); + acc_provider + .insert_account(kp.secret().clone(), &password) + .unwrap(); } #[cfg(not(feature = "accounts"))] diff --git a/parity/rpc.rs b/parity/rpc.rs index b07ca3f3e48..1022cfc9cb5 100644 --- a/parity/rpc.rs +++ b/parity/rpc.rs @@ -1,193 +1,213 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::io; -use std::sync::Arc; -use std::path::PathBuf; -use std::collections::HashSet; +use std::{collections::HashSet, io, path::PathBuf, sync::Arc}; -use dir::default_data_path; -use dir::helpers::replace_home; +use dir::{default_data_path, helpers::replace_home}; use helpers::parity_ipc_path; use jsonrpc_core::MetaIoHandler; +use parity_rpc::{ + self as rpc, + informant::{Middleware, RpcStats}, + DomainsValidation, Metadata, +}; use parity_runtime::Executor; -use parity_rpc::informant::{RpcStats, Middleware}; -use parity_rpc::{self as rpc, Metadata, DomainsValidation}; use rpc_apis::{self, ApiSet}; -pub use parity_rpc::{IpcServer, HttpServer, RequestMiddleware}; -pub use parity_rpc::ws::Server as WsServer; +pub use parity_rpc::{HttpServer, IpcServer, RequestMiddleware}; +//pub use parity_rpc::ws::Server as WsServer; +pub use parity_rpc::ws::{ws, Server as WsServer}; pub const DAPPS_DOMAIN: &'static str = "web3.site"; #[derive(Debug, Clone, PartialEq)] pub struct HttpConfiguration { - pub enabled: bool, - pub interface: String, - pub port: u16, - pub apis: ApiSet, - pub cors: Option>, - pub hosts: Option>, - pub server_threads: usize, - pub processing_threads: usize, - pub max_payload: usize, - pub keep_alive: bool, + pub enabled: bool, + pub interface: String, + pub port: u16, + pub apis: ApiSet, + pub cors: Option>, + pub hosts: Option>, + pub server_threads: usize, + pub processing_threads: usize, + pub max_payload: usize, + pub keep_alive: bool, } impl Default for HttpConfiguration { - fn default() -> Self { - HttpConfiguration { - enabled: true, - interface: "127.0.0.1".into(), - port: 8545, - apis: ApiSet::UnsafeContext, - cors: Some(vec![]), - hosts: Some(vec![]), - server_threads: 1, - processing_threads: 4, - max_payload: 5, - keep_alive: true, - } - } + fn default() -> Self { + HttpConfiguration { + enabled: true, + interface: "127.0.0.1".into(), + port: 8545, + apis: ApiSet::UnsafeContext, + cors: Some(vec![]), + hosts: Some(vec![]), + server_threads: 1, + processing_threads: 4, + max_payload: 5, + keep_alive: true, + } + } } #[derive(Debug, PartialEq)] pub struct IpcConfiguration { - pub enabled: bool, - pub socket_addr: String, - pub apis: ApiSet, + pub enabled: bool, + pub socket_addr: String, + pub apis: ApiSet, } impl Default for IpcConfiguration { - fn default() -> Self { - IpcConfiguration { - enabled: true, - socket_addr: if cfg!(windows) { - r"\\.\pipe\jsonrpc.ipc".into() - } else { - let data_dir = ::dir::default_data_path(); - parity_ipc_path(&data_dir, "$BASE/jsonrpc.ipc", 0) - }, - apis: ApiSet::IpcContext, - } - } + fn default() -> Self { + IpcConfiguration { + enabled: true, + socket_addr: if cfg!(windows) { + r"\\.\pipe\jsonrpc.ipc".into() + } else { + let data_dir = ::dir::default_data_path(); + parity_ipc_path(&data_dir, "$BASE/jsonrpc.ipc", 0) + }, + apis: ApiSet::IpcContext, + } + } } #[derive(Debug, Clone, PartialEq)] pub struct WsConfiguration { - pub enabled: bool, - pub interface: String, - pub port: u16, - pub apis: ApiSet, - pub max_connections: usize, - pub origins: Option>, - pub hosts: Option>, - pub signer_path: PathBuf, - pub support_token_api: bool, + pub enabled: bool, + pub interface: String, + pub port: u16, + pub apis: ApiSet, + pub max_connections: usize, + pub origins: Option>, + pub hosts: Option>, + pub signer_path: PathBuf, + pub support_token_api: bool, } impl Default for WsConfiguration { - fn default() -> Self { - let data_dir = default_data_path(); - WsConfiguration { - enabled: true, - interface: "127.0.0.1".into(), - port: 8546, - apis: ApiSet::UnsafeContext, - max_connections: 100, - origins: Some(vec!["parity://*".into(),"chrome-extension://*".into(), "moz-extension://*".into()]), - hosts: Some(Vec::new()), - signer_path: replace_home(&data_dir, "$BASE/signer").into(), - support_token_api: true, - } - } + fn default() -> Self { + let data_dir = default_data_path(); + WsConfiguration { + enabled: true, + interface: "127.0.0.1".into(), + port: 8546, + apis: ApiSet::UnsafeContext, + max_connections: 100, + origins: Some(vec![ + "parity://*".into(), + "chrome-extension://*".into(), + "moz-extension://*".into(), + ]), + hosts: Some(Vec::new()), + signer_path: replace_home(&data_dir, "$BASE/signer").into(), + support_token_api: true, + } + } } impl WsConfiguration { - pub fn address(&self) -> Option { - address(self.enabled, &self.interface, self.port, &self.hosts) - } + pub fn address(&self) -> Option { + address(self.enabled, &self.interface, self.port, &self.hosts) + } } -fn address(enabled: bool, bind_iface: &str, bind_port: u16, hosts: &Option>) -> Option { - if !enabled { - return None; - } - - match *hosts { - Some(ref hosts) if !hosts.is_empty() => Some(hosts[0].clone().into()), - _ => Some(format!("{}:{}", bind_iface, bind_port).into()), - } +fn address( + enabled: bool, + bind_iface: &str, + bind_port: u16, + hosts: &Option>, +) -> Option { + if !enabled { + return None; + } + + match *hosts { + Some(ref hosts) if !hosts.is_empty() => Some(hosts[0].clone().into()), + _ => Some(format!("{}:{}", bind_iface, bind_port).into()), + } } pub struct Dependencies { - pub apis: Arc, - pub executor: Executor, - pub stats: Arc, + pub apis: Arc, + pub executor: Executor, + pub stats: Arc, } pub fn new_ws( - conf: WsConfiguration, - deps: &Dependencies, + conf: WsConfiguration, + deps: &Dependencies, ) -> Result, String> { - if !conf.enabled { - return Ok(None); - } - - let domain = DAPPS_DOMAIN; - let url = format!("{}:{}", conf.interface, conf.port); - let addr = url.parse().map_err(|_| format!("Invalid WebSockets listen host/port given: {}", url))?; - - let full_handler = setup_apis(rpc_apis::ApiSet::All, deps); - let handler = { - let mut handler = MetaIoHandler::with_middleware(( - rpc::WsDispatcher::new(full_handler), - Middleware::new(deps.stats.clone(), deps.apis.activity_notifier()) - )); - let apis = conf.apis.list_apis(); - deps.apis.extend_with_set(&mut handler, &apis); - - handler - }; - - let allowed_origins = into_domains(with_domain(conf.origins, domain, &None)); - let allowed_hosts = into_domains(with_domain(conf.hosts, domain, &Some(url.clone().into()))); - - let signer_path; - let path = match conf.support_token_api { - true => { - signer_path = ::signer::codes_path(&conf.signer_path); - Some(signer_path.as_path()) - }, - false => None - }; - let start_result = rpc::start_ws( - &addr, - handler, - allowed_origins, - allowed_hosts, - conf.max_connections, - rpc::WsExtractor::new(path.clone()), - rpc::WsExtractor::new(path.clone()), - rpc::WsStats::new(deps.stats.clone()), - ); - - match start_result { + if !conf.enabled { + return Ok(None); + } + + let domain = DAPPS_DOMAIN; + let url = format!("{}:{}", conf.interface, conf.port); + let addr = url + .parse() + .map_err(|_| format!("Invalid WebSockets listen host/port given: {}", url))?; + + let full_handler = setup_apis(rpc_apis::ApiSet::All, deps); + let handler = { + let mut handler = MetaIoHandler::with_middleware(( + rpc::WsDispatcher::new(full_handler), + Middleware::new(deps.stats.clone(), deps.apis.activity_notifier()), + )); + let apis = conf.apis.list_apis(); + deps.apis.extend_with_set(&mut handler, &apis); + + handler + }; + + let allowed_origins = into_domains(with_domain(conf.origins, domain, &None)); + let allowed_hosts = into_domains(with_domain(conf.hosts, domain, &Some(url.clone().into()))); + + let signer_path; + let path = match conf.support_token_api { + true => { + signer_path = ::signer::codes_path(&conf.signer_path); + Some(signer_path.as_path()) + } + false => None, + }; + let start_result = rpc::start_ws( + &addr, + handler, + allowed_origins, + allowed_hosts, + conf.max_connections, + rpc::WsExtractor::new(path.clone()), + rpc::WsExtractor::new(path.clone()), + rpc::WsStats::new(deps.stats.clone()), + ); + + // match start_result { + // Ok(server) => Ok(Some(server)), + // Err(rpc::ws::Error::Io(rpc::ws::ErrorKind::Io(ref err), _)) if err.kind() == io::ErrorKind::AddrInUse => Err( + // format!("WebSockets address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --ws-port and --ws-interface options.", url) + // ), + // Err(e) => Err(format!("WebSockets error: {:?}", e)), + // } + match start_result { Ok(server) => Ok(Some(server)), - Err(rpc::ws::Error(rpc::ws::ErrorKind::Io(ref err), _)) if err.kind() == io::ErrorKind::AddrInUse => Err( + Err(rpc::ws::Error::WsError(ws::Error { + kind: ws::ErrorKind::Io(ref err), .. + })) if err.kind() == io::ErrorKind::AddrInUse => Err( format!("WebSockets address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --ws-port and --ws-interface options.", url) ), Err(e) => Err(format!("WebSockets error: {:?}", e)), @@ -195,35 +215,37 @@ pub fn new_ws( } pub fn new_http( - id: &str, - options: &str, - conf: HttpConfiguration, - deps: &Dependencies, + id: &str, + options: &str, + conf: HttpConfiguration, + deps: &Dependencies, ) -> Result, String> { - if !conf.enabled { - return Ok(None); - } - - let domain = DAPPS_DOMAIN; - let url = format!("{}:{}", conf.interface, conf.port); - let addr = url.parse().map_err(|_| format!("Invalid {} listen host/port given: {}", id, url))?; - let handler = setup_apis(conf.apis, deps); - - let cors_domains = into_domains(conf.cors); - let allowed_hosts = into_domains(with_domain(conf.hosts, domain, &Some(url.clone().into()))); - - let start_result = rpc::start_http( - &addr, - cors_domains, - allowed_hosts, - handler, - rpc::RpcExtractor, - conf.server_threads, - conf.max_payload, - conf.keep_alive, - ); - - match start_result { + if !conf.enabled { + return Ok(None); + } + + let domain = DAPPS_DOMAIN; + let url = format!("{}:{}", conf.interface, conf.port); + let addr = url + .parse() + .map_err(|_| format!("Invalid {} listen host/port given: {}", id, url))?; + let handler = setup_apis(conf.apis, deps); + + let cors_domains = into_domains(conf.cors); + let allowed_hosts = into_domains(with_domain(conf.hosts, domain, &Some(url.clone().into()))); + + let start_result = rpc::start_http( + &addr, + cors_domains, + allowed_hosts, + handler, + rpc::RpcExtractor, + conf.server_threads, + conf.max_payload, + conf.keep_alive, + ); + + match start_result { Ok(server) => Ok(Some(server)), Err(ref err) if err.kind() == io::ErrorKind::AddrInUse => Err( format!("{} address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --{}-port and --{}-interface options.", id, url, options, options) @@ -233,80 +255,105 @@ pub fn new_http( } pub fn new_ipc( - conf: IpcConfiguration, - dependencies: &Dependencies + conf: IpcConfiguration, + dependencies: &Dependencies, ) -> Result, String> { - if !conf.enabled { - return Ok(None); - } - - let handler = setup_apis(conf.apis, dependencies); - let path = PathBuf::from(&conf.socket_addr); - // Make sure socket file can be created on unix-like OS. - // Windows pipe paths are not on the FS. - if !cfg!(windows) { - if let Some(dir) = path.parent() { - ::std::fs::create_dir_all(&dir) - .map_err(|err| format!("Unable to create IPC directory at {}: {}", dir.display(), err))?; - } - } - - match rpc::start_ipc(&conf.socket_addr, handler, rpc::RpcExtractor) { - Ok(server) => Ok(Some(server)), - Err(io_error) => Err(format!("IPC error: {}", io_error)), - } + if !conf.enabled { + return Ok(None); + } + + let handler = setup_apis(conf.apis, dependencies); + let path = PathBuf::from(&conf.socket_addr); + // Make sure socket file can be created on unix-like OS. + // Windows pipe paths are not on the FS. + if !cfg!(windows) { + if let Some(dir) = path.parent() { + ::std::fs::create_dir_all(&dir).map_err(|err| { + format!( + "Unable to create IPC directory at {}: {}", + dir.display(), + err + ) + })?; + } + } + + match rpc::start_ipc(&conf.socket_addr, handler, rpc::RpcExtractor) { + Ok(server) => Ok(Some(server)), + Err(io_error) => Err(format!("IPC error: {}", io_error)), + } } fn into_domains>(items: Option>) -> DomainsValidation { - items.map(|vals| vals.into_iter().map(T::from).collect()).into() + items + .map(|vals| vals.into_iter().map(T::from).collect()) + .into() } -fn with_domain(items: Option>, domain: &str, dapps_address: &Option) -> Option> { - fn extract_port(s: &str) -> Option { - s.split(':').nth(1).and_then(|s| s.parse().ok()) - } - - items.map(move |items| { - let mut items = items.into_iter().collect::>(); - { - let mut add_hosts = |address: &Option| { - if let Some(host) = address.clone() { - items.insert(host.to_string()); - items.insert(host.replace("127.0.0.1", "localhost")); - items.insert(format!("http://*.{}", domain)); //proxypac - if let Some(port) = extract_port(&*host) { - items.insert(format!("http://*.{}:{}", domain, port)); - } - } - }; - - add_hosts(dapps_address); - } - items.into_iter().collect() - }) +fn with_domain( + items: Option>, + domain: &str, + dapps_address: &Option, +) -> Option> { + fn extract_port(s: &str) -> Option { + s.split(':').nth(1).and_then(|s| s.parse().ok()) + } + + items.map(move |items| { + let mut items = items.into_iter().collect::>(); + { + let mut add_hosts = |address: &Option| { + if let Some(host) = address.clone() { + items.insert(host.to_string()); + items.insert(host.replace("127.0.0.1", "localhost")); + items.insert(format!("http://*.{}", domain)); //proxypac + if let Some(port) = extract_port(&*host) { + items.insert(format!("http://*.{}:{}", domain, port)); + } + } + }; + + add_hosts(dapps_address); + } + items.into_iter().collect() + }) } -pub fn setup_apis(apis: ApiSet, deps: &Dependencies) -> MetaIoHandler> - where D: rpc_apis::Dependencies +pub fn setup_apis( + apis: ApiSet, + deps: &Dependencies, +) -> MetaIoHandler> +where + D: rpc_apis::Dependencies, { - let mut handler = MetaIoHandler::with_middleware( - Middleware::new(deps.stats.clone(), deps.apis.activity_notifier()) - ); - let apis = apis.list_apis(); - deps.apis.extend_with_set(&mut handler, &apis); - - handler + let mut handler = MetaIoHandler::with_middleware(Middleware::new( + deps.stats.clone(), + deps.apis.activity_notifier(), + )); + let apis = apis.list_apis(); + deps.apis.extend_with_set(&mut handler, &apis); + + handler } #[cfg(test)] mod tests { - use super::address; - - #[test] - fn should_return_proper_address() { - assert_eq!(address(false, "localhost", 8180, &None), None); - assert_eq!(address(true, "localhost", 8180, &None), Some("localhost:8180".into())); - assert_eq!(address(true, "localhost", 8180, &Some(vec!["host:443".into()])), Some("host:443".into())); - assert_eq!(address(true, "localhost", 8180, &Some(vec!["host".into()])), Some("host".into())); - } + use super::address; + + #[test] + fn should_return_proper_address() { + assert_eq!(address(false, "localhost", 8180, &None), None); + assert_eq!( + address(true, "localhost", 8180, &None), + Some("localhost:8180".into()) + ); + assert_eq!( + address(true, "localhost", 8180, &Some(vec!["host:443".into()])), + Some("host:443".into()) + ); + assert_eq!( + address(true, "localhost", 8180, &Some(vec!["host".into()])), + Some("host".into()) + ); + } } diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index 288f85ab422..0de19f7685b 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -1,908 +1,596 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::cmp::PartialEq; -use std::collections::{BTreeMap, HashSet}; -use std::str::FromStr; -use std::sync::{Arc, Weak}; +use std::{cmp::PartialEq, collections::HashSet, str::FromStr, sync::Arc}; pub use parity_rpc::signer::SignerService; use account_utils::{self, AccountProvider}; -use ethcore::client::Client; -use ethcore::miner::Miner; -use ethcore::snapshot::SnapshotService; +use ethcore::{client::Client, miner::Miner, snapshot::SnapshotService}; use ethcore_logger::RotatingLogger; -use ethcore_private_tx::Provider as PrivateTransactionManager; -use ethcore_service::PrivateTxService; -use hash_fetch::fetch::Client as FetchClient; +use fetch::Client as FetchClient; use jsonrpc_core::{self as core, MetaIoHandler}; -use light::client::LightChainClient; -use light::{Cache as LightDataCache, TransactionQueue as LightTransactionQueue}; use miner::external::ExternalMiner; -use parity_rpc::dispatch::{FullDispatcher, LightDispatcher}; -use parity_rpc::informant::{ActivityNotifier, ClientNotifier}; -use parity_rpc::{Host, Metadata, NetworkSettings}; +use parity_rpc::{ + dispatch::FullDispatcher, + informant::{ActivityNotifier, ClientNotifier}, + Host, Metadata, NetworkSettings, +}; use parity_runtime::Executor; -use parking_lot::{Mutex, RwLock}; -use sync::{LightSync, ManageNetwork, SyncProvider}; -use updater::Updater; +use parking_lot::Mutex; +use sync::{ManageNetwork, SyncProvider}; #[derive(Debug, PartialEq, Clone, Eq, Hash)] pub enum Api { - /// Web3 (Safe) - Web3, - /// Net (Safe) - Net, - /// Eth (Safe) - Eth, - /// Eth Pub-Sub (Safe) - EthPubSub, - /// Geth-compatible "personal" API (DEPRECATED; only used in `--geth` mode.) - Personal, - /// Signer - Confirm transactions in Signer (UNSAFE: Passwords, List of transactions) - Signer, - /// Parity - Custom extensions (Safe) - Parity, - /// Traces (Safe) - Traces, - /// Rpc (Safe) - Rpc, - /// Private transaction manager (Safe) - Private, - /// Whisper (Safe) - // TODO: _if_ someone guesses someone else's key or filter IDs they can remove - // BUT these are all ephemeral so it seems fine. - Whisper, - /// Whisper Pub-Sub (Safe but same concerns as above). - WhisperPubSub, - /// Parity PubSub - Generic Publish-Subscriber (Safety depends on other APIs exposed). - ParityPubSub, - /// Parity Accounts extensions (UNSAFE: Passwords, Side Effects (new account)) - ParityAccounts, - /// Parity - Set methods (UNSAFE: Side Effects affecting node operation) - ParitySet, - /// SecretStore (UNSAFE: arbitrary hash signing) - SecretStore, - /// Geth-compatible (best-effort) debug API (Potentially UNSAFE) - /// NOTE We don't aim to support all methods, only the ones that are useful. - Debug, + /// Web3 (Safe) + Web3, + /// Net (Safe) + Net, + /// Eth (Safe) + Eth, + /// Eth Pub-Sub (Safe) + EthPubSub, + /// Geth-compatible "personal" API (DEPRECATED; only used in `--geth` mode.) + Personal, + /// Signer - Confirm transactions in Signer (UNSAFE: Passwords, List of transactions) + Signer, + /// Parity - Custom extensions (Safe) + Parity, + /// Traces (Safe) + Traces, + /// Parity PubSub - Generic Publish-Subscriber (Safety depends on other APIs exposed). + ParityPubSub, + /// Parity Accounts extensions (UNSAFE: Passwords, Side Effects (new account)) + ParityAccounts, + /// Parity - Set methods (UNSAFE: Side Effects affecting node operation) + ParitySet, + /// SecretStore (UNSAFE: arbitrary hash signing) + SecretStore, + /// Geth-compatible (best-effort) debug API (Potentially UNSAFE) + /// NOTE We don't aim to support all methods, only the ones that are useful. + Debug, } impl FromStr for Api { - type Err = String; - - fn from_str(s: &str) -> Result { - use self::Api::*; - - match s { - "debug" => Ok(Debug), - "eth" => Ok(Eth), - "net" => Ok(Net), - "parity" => Ok(Parity), - "parity_accounts" => Ok(ParityAccounts), - "parity_pubsub" => Ok(ParityPubSub), - "parity_set" => Ok(ParitySet), - "personal" => Ok(Personal), - "private" => Ok(Private), - "pubsub" => Ok(EthPubSub), - "rpc" => Ok(Rpc), - "secretstore" => Ok(SecretStore), - "shh" => Ok(Whisper), - "shh_pubsub" => Ok(WhisperPubSub), - "signer" => Ok(Signer), - "traces" => Ok(Traces), - "web3" => Ok(Web3), - api => Err(format!("Unknown api: {}", api)), - } - } + type Err = String; + + fn from_str(s: &str) -> Result { + use self::Api::*; + + match s { + "debug" => Ok(Debug), + "eth" => Ok(Eth), + "net" => Ok(Net), + "parity" => Ok(Parity), + "parity_accounts" => Ok(ParityAccounts), + "parity_pubsub" => Ok(ParityPubSub), + "parity_set" => Ok(ParitySet), + "personal" => Ok(Personal), + "pubsub" => Ok(EthPubSub), + "secretstore" => Ok(SecretStore), + "signer" => Ok(Signer), + "traces" => Ok(Traces), + "web3" => Ok(Web3), + api => Err(format!("Unknown api: {}", api)), + } + } } #[derive(Debug, Clone)] pub enum ApiSet { - // Unsafe context (like jsonrpc over http) - UnsafeContext, - // All possible APIs (safe context like token-protected WS interface) - All, - // Local "unsafe" context and accounts access - IpcContext, - // APIs for Parity Generic Pub-Sub - PubSub, - // Fixed list of APis - List(HashSet), + // Unsafe context (like jsonrpc over http) + UnsafeContext, + // All possible APIs (safe context like token-protected WS interface) + All, + // Local "unsafe" context and accounts access + IpcContext, + // APIs for Parity Generic Pub-Sub + PubSub, + // Fixed list of APis + List(HashSet), } impl Default for ApiSet { - fn default() -> Self { - ApiSet::UnsafeContext - } + fn default() -> Self { + ApiSet::UnsafeContext + } } impl PartialEq for ApiSet { - fn eq(&self, other: &Self) -> bool { - self.list_apis() == other.list_apis() - } + fn eq(&self, other: &Self) -> bool { + self.list_apis() == other.list_apis() + } } impl FromStr for ApiSet { - type Err = String; - - fn from_str(s: &str) -> Result { - let mut apis = HashSet::new(); - - for api in s.split(',') { - match api { - "all" => { - apis.extend(ApiSet::All.list_apis()); - } - "safe" => { - // Safe APIs are those that are safe even in UnsafeContext. - apis.extend(ApiSet::UnsafeContext.list_apis()); - } - // Remove the API - api if api.starts_with("-") => { - let api = api[1..].parse()?; - apis.remove(&api); - } - api => { - let api = api.parse()?; - apis.insert(api); - } - } - } - - Ok(ApiSet::List(apis)) - } -} - -fn to_modules(apis: &HashSet) -> BTreeMap { - let mut modules = BTreeMap::new(); - for api in apis { - let (name, version) = match *api { - Api::Debug => ("debug", "1.0"), - Api::Eth => ("eth", "1.0"), - Api::EthPubSub => ("pubsub", "1.0"), - Api::Net => ("net", "1.0"), - Api::Parity => ("parity", "1.0"), - Api::ParityAccounts => ("parity_accounts", "1.0"), - Api::ParityPubSub => ("parity_pubsub", "1.0"), - Api::ParitySet => ("parity_set", "1.0"), - Api::Personal => ("personal", "1.0"), - Api::Private => ("private", "1.0"), - Api::Rpc => ("rpc", "1.0"), - Api::SecretStore => ("secretstore", "1.0"), - Api::Signer => ("signer", "1.0"), - Api::Traces => ("traces", "1.0"), - Api::Web3 => ("web3", "1.0"), - Api::Whisper => ("shh", "1.0"), - Api::WhisperPubSub => ("shh_pubsub", "1.0"), - }; - modules.insert(name.into(), version.into()); - } - modules + type Err = String; + + fn from_str(s: &str) -> Result { + let mut apis = HashSet::new(); + + for api in s.split(',') { + match api { + "all" => { + apis.extend(ApiSet::All.list_apis()); + } + "safe" => { + // Safe APIs are those that are safe even in UnsafeContext. + apis.extend(ApiSet::UnsafeContext.list_apis()); + } + // Remove the API + api if api.starts_with("-") => { + let api = api[1..].parse()?; + apis.remove(&api); + } + api => { + let api = api.parse()?; + apis.insert(api); + } + } + } + + Ok(ApiSet::List(apis)) + } } macro_rules! add_signing_methods { - ($namespace:ident, $handler:expr, $deps:expr, $dispatch:expr) => {{ - let deps = &$deps; - let (dispatcher, accounts) = $dispatch; - if deps.signer_service.is_enabled() { - $handler.extend_with($namespace::to_delegate(SigningQueueClient::new( - &deps.signer_service, - dispatcher.clone(), - deps.executor.clone(), - accounts, - ))) - } else { - $handler.extend_with($namespace::to_delegate(SigningUnsafeClient::new( - accounts, - dispatcher.clone(), - ))) - } - }}; + ($namespace:ident, $handler:expr, $deps:expr, $dispatch:expr) => {{ + let deps = &$deps; + let (dispatcher, accounts) = $dispatch; + if deps.signer_service.is_enabled() { + $handler.extend_with($namespace::to_delegate(SigningQueueClient::new( + &deps.signer_service, + dispatcher.clone(), + deps.executor.clone(), + accounts, + ))) + } else { + $handler.extend_with($namespace::to_delegate(SigningUnsafeClient::new( + accounts, + dispatcher.clone(), + ))) + } + }}; } /// RPC dependencies can be used to initialize RPC endpoints from APIs. pub trait Dependencies { - type Notifier: ActivityNotifier; + type Notifier: ActivityNotifier; - /// Create the activity notifier. - fn activity_notifier(&self) -> Self::Notifier; + /// Create the activity notifier. + fn activity_notifier(&self) -> Self::Notifier; - /// Extend the given I/O handler with endpoints for each API. - fn extend_with_set(&self, handler: &mut MetaIoHandler, apis: &HashSet) - where - S: core::Middleware; + /// Extend the given I/O handler with endpoints for each API. + fn extend_with_set(&self, handler: &mut MetaIoHandler, apis: &HashSet) + where + S: core::Middleware; } /// RPC dependencies for a full node. pub struct FullDependencies { - pub signer_service: Arc, - pub client: Arc, - pub snapshot: Arc, - pub sync: Arc, - pub net: Arc, - pub accounts: Arc, - pub private_tx_service: Option>, - pub miner: Arc, - pub external_miner: Arc, - pub logger: Arc, - pub settings: Arc, - pub net_service: Arc, - pub updater: Arc, - pub geth_compatibility: bool, - pub experimental_rpcs: bool, - pub ws_address: Option, - pub fetch: FetchClient, - pub executor: Executor, - pub whisper_rpc: Option<::whisper::RpcFactory>, - pub gas_price_percentile: usize, - pub poll_lifetime: u32, - pub allow_missing_blocks: bool, + pub signer_service: Arc, + pub client: Arc, + pub snapshot: Arc, + pub sync: Arc, + pub net: Arc, + pub accounts: Arc, + pub miner: Arc, + pub external_miner: Arc, + pub logger: Arc, + pub settings: Arc, + pub net_service: Arc, + pub experimental_rpcs: bool, + pub ws_address: Option, + pub fetch: FetchClient, + pub executor: Executor, + pub gas_price_percentile: usize, + pub poll_lifetime: u32, + pub allow_missing_blocks: bool, + pub no_ancient_blocks: bool, } impl FullDependencies { - fn extend_api( - &self, - handler: &mut MetaIoHandler, - apis: &HashSet, - for_generic_pubsub: bool, - ) where - S: core::Middleware, - { - use parity_rpc::v1::*; - - let nonces = Arc::new(Mutex::new(dispatch::Reservations::new( - self.executor.clone(), - ))); - let dispatcher = FullDispatcher::new( - self.client.clone(), - self.miner.clone(), - nonces.clone(), - self.gas_price_percentile, - ); - let account_signer = Arc::new(dispatch::Signer::new(self.accounts.clone())) as _; - let accounts = account_utils::accounts_list(self.accounts.clone()); - - for api in apis { - match *api { - Api::Debug => { - handler.extend_with(DebugClient::new(self.client.clone()).to_delegate()); - } - Api::Web3 => { - handler.extend_with(Web3Client::default().to_delegate()); - } - Api::Net => { - handler.extend_with(NetClient::new(&self.sync).to_delegate()); - } - Api::Eth => { - let client = EthClient::new( - &self.client, - &self.snapshot, - &self.sync, - &accounts, - &self.miner, - &self.external_miner, - EthClientOptions { - pending_nonce_from_queue: self.geth_compatibility, - allow_pending_receipt_query: !self.geth_compatibility, - send_block_number_in_get_work: !self.geth_compatibility, - gas_price_percentile: self.gas_price_percentile, - allow_missing_blocks: self.allow_missing_blocks, - allow_experimental_rpcs: self.experimental_rpcs, - } - ); - handler.extend_with(client.to_delegate()); - - if !for_generic_pubsub { - let filter_client = EthFilterClient::new( - self.client.clone(), - self.miner.clone(), - self.poll_lifetime, - ); - handler.extend_with(filter_client.to_delegate()); - - add_signing_methods!(EthSigning, handler, self, (&dispatcher, &account_signer)); - } - } - Api::EthPubSub => { - if !for_generic_pubsub { - let client = - EthPubSubClient::new(self.client.clone(), self.executor.clone()); - let h = client.handler(); - self.miner - .add_transactions_listener(Box::new(move |hashes| { - if let Some(h) = h.upgrade() { - h.notify_new_transactions(hashes); - } - })); - - if let Some(h) = client.handler().upgrade() { - self.client.add_notify(h); - } - handler.extend_with(client.to_delegate()); - } - } - Api::Personal => { - #[cfg(feature = "accounts")] - handler.extend_with( - PersonalClient::new( - &self.accounts, - dispatcher.clone(), - self.geth_compatibility, - self.experimental_rpcs, - ).to_delegate(), - ); - } - Api::Signer => { - handler.extend_with( - SignerClient::new( - account_signer.clone(), - dispatcher.clone(), - &self.signer_service, - self.executor.clone(), - ).to_delegate(), - ); - } - Api::Parity => { - let signer = match self.signer_service.is_enabled() { - true => Some(self.signer_service.clone()), - false => None, - }; - handler.extend_with( - ParityClient::new( - self.client.clone(), - self.miner.clone(), - self.sync.clone(), - self.updater.clone(), - self.net_service.clone(), - self.logger.clone(), - self.settings.clone(), - signer, - self.ws_address.clone(), - self.snapshot.clone().into(), - ).to_delegate(), - ); - #[cfg(feature = "accounts")] - handler.extend_with(ParityAccountsInfo::to_delegate(ParityAccountsClient::new(&self.accounts))); - - if !for_generic_pubsub { - add_signing_methods!(ParitySigning, handler, self, (&dispatcher, &account_signer)); - } - } - Api::ParityPubSub => { - if !for_generic_pubsub { - let mut rpc = MetaIoHandler::default(); - let apis = ApiSet::List(apis.clone()) - .retain(ApiSet::PubSub) - .list_apis(); - self.extend_api(&mut rpc, &apis, true); - handler.extend_with( - PubSubClient::new(rpc, self.executor.clone()).to_delegate(), - ); - } - } - Api::ParityAccounts => { - #[cfg(feature = "accounts")] - handler.extend_with(ParityAccounts::to_delegate(ParityAccountsClient::new(&self.accounts))); - } - Api::ParitySet => { - handler.extend_with( - ParitySetClient::new( - &self.client, - &self.miner, - &self.updater, - &self.net_service, - self.fetch.clone(), - ).to_delegate(), - ); - #[cfg(feature = "accounts")] - handler.extend_with( - ParitySetAccountsClient::new( - &self.accounts, - &self.miner, - ).to_delegate(), - ); - } - Api::Traces => handler.extend_with(TracesClient::new(&self.client).to_delegate()), - Api::Rpc => { - let modules = to_modules(&apis); - handler.extend_with(RpcClient::new(modules).to_delegate()); - } - Api::SecretStore => { - #[cfg(feature = "accounts")] - handler.extend_with(SecretStoreClient::new(&self.accounts).to_delegate()); - } - Api::Whisper => { - if let Some(ref whisper_rpc) = self.whisper_rpc { - let whisper = whisper_rpc.make_handler(self.net.clone()); - handler.extend_with(::parity_whisper::rpc::Whisper::to_delegate(whisper)); - } - } - Api::WhisperPubSub => { - if !for_generic_pubsub { - if let Some(ref whisper_rpc) = self.whisper_rpc { - let whisper = whisper_rpc.make_handler(self.net.clone()); - handler.extend_with(::parity_whisper::rpc::WhisperPubSub::to_delegate( - whisper, - )); - } - } - } - Api::Private => { - handler.extend_with( - PrivateClient::new(self.private_tx_service.as_ref().map(|p| p.provider())) - .to_delegate(), - ); - } - } - } - } + fn extend_api( + &self, + handler: &mut MetaIoHandler, + apis: &HashSet, + for_generic_pubsub: bool, + ) where + S: core::Middleware, + { + use parity_rpc::v1::*; + + let nonces = Arc::new(Mutex::new(dispatch::Reservations::new( + self.executor.clone(), + ))); + let dispatcher = FullDispatcher::new( + self.client.clone(), + self.miner.clone(), + nonces.clone(), + self.gas_price_percentile, + ); + let account_signer = Arc::new(dispatch::Signer::new(self.accounts.clone())) as _; + let accounts = account_utils::accounts_list(self.accounts.clone()); + + for api in apis { + match *api { + Api::Debug => { + handler.extend_with(DebugClient::new(self.client.clone()).to_delegate()); + } + Api::Web3 => { + handler.extend_with(Web3Client::default().to_delegate()); + } + Api::Net => { + handler.extend_with(NetClient::new(&self.sync).to_delegate()); + } + Api::Eth => { + let client = EthClient::new( + &self.client, + &self.snapshot, + &self.sync, + &accounts, + &self.miner, + &self.external_miner, + EthClientOptions { + gas_price_percentile: self.gas_price_percentile, + allow_missing_blocks: self.allow_missing_blocks, + allow_experimental_rpcs: self.experimental_rpcs, + no_ancient_blocks: self.no_ancient_blocks, + }, + ); + handler.extend_with(client.to_delegate()); + + if !for_generic_pubsub { + let filter_client = EthFilterClient::new( + self.client.clone(), + self.miner.clone(), + self.poll_lifetime, + ); + handler.extend_with(filter_client.to_delegate()); + + add_signing_methods!( + EthSigning, + handler, + self, + (&dispatcher, &account_signer) + ); + } + } + Api::EthPubSub => { + if !for_generic_pubsub { + let client = + EthPubSubClient::new(self.client.clone(), self.executor.clone()); + let h = client.handler(); + self.miner + .add_transactions_listener(Box::new(move |hashes| { + if let Some(h) = h.upgrade() { + h.notify_new_transactions(hashes); + } + })); + + if let Some(h) = client.handler().upgrade() { + self.client.add_notify(h); + } + handler.extend_with(client.to_delegate()); + } + } + Api::Personal => { + #[cfg(feature = "accounts")] + handler.extend_with( + PersonalClient::new( + &self.accounts, + dispatcher.clone(), + self.experimental_rpcs, + ) + .to_delegate(), + ); + } + Api::Signer => { + handler.extend_with( + SignerClient::new( + account_signer.clone(), + dispatcher.clone(), + &self.signer_service, + self.executor.clone(), + ) + .to_delegate(), + ); + } + Api::Parity => { + let signer = match self.signer_service.is_enabled() { + true => Some(self.signer_service.clone()), + false => None, + }; + handler.extend_with( + ParityClient::new( + self.client.clone(), + self.miner.clone(), + self.sync.clone(), + self.net_service.clone(), + self.logger.clone(), + self.settings.clone(), + signer, + self.ws_address.clone(), + self.snapshot.clone().into(), + ) + .to_delegate(), + ); + #[cfg(feature = "accounts")] + handler.extend_with(ParityAccountsInfo::to_delegate( + ParityAccountsClient::new(&self.accounts), + )); + + if !for_generic_pubsub { + add_signing_methods!( + ParitySigning, + handler, + self, + (&dispatcher, &account_signer) + ); + } + } + Api::ParityPubSub => { + if !for_generic_pubsub { + let mut rpc = MetaIoHandler::default(); + let apis = ApiSet::List(apis.clone()) + .retain(ApiSet::PubSub) + .list_apis(); + self.extend_api(&mut rpc, &apis, true); + handler.extend_with( + PubSubClient::new(rpc, self.executor.clone()).to_delegate(), + ); + } + } + Api::ParityAccounts => { + #[cfg(feature = "accounts")] + handler.extend_with(ParityAccounts::to_delegate(ParityAccountsClient::new( + &self.accounts, + ))); + } + Api::ParitySet => { + handler.extend_with( + ParitySetClient::new( + &self.client, + &self.miner, + &self.net_service, + self.fetch.clone(), + ) + .to_delegate(), + ); + #[cfg(feature = "accounts")] + handler.extend_with( + ParitySetAccountsClient::new(&self.accounts, &self.miner).to_delegate(), + ); + } + Api::Traces => handler.extend_with(TracesClient::new(&self.client).to_delegate()), + Api::SecretStore => { + #[cfg(feature = "accounts")] + handler.extend_with(SecretStoreClient::new(&self.accounts).to_delegate()); + } + } + } + } } impl Dependencies for FullDependencies { - type Notifier = ClientNotifier; - - fn activity_notifier(&self) -> ClientNotifier { - ClientNotifier { - client: self.client.clone(), - } - } - - fn extend_with_set(&self, handler: &mut MetaIoHandler, apis: &HashSet) - where - S: core::Middleware, - { - self.extend_api(handler, apis, false) - } -} - -/// Light client notifier. Doesn't do anything yet, but might in the future. -pub struct LightClientNotifier; - -impl ActivityNotifier for LightClientNotifier { - fn active(&self) {} -} - -/// RPC dependencies for a light client. -pub struct LightDependencies { - pub signer_service: Arc, - pub client: Arc, - pub sync: Arc, - pub net: Arc, - pub accounts: Arc, - pub logger: Arc, - pub settings: Arc, - pub on_demand: Arc<::light::on_demand::OnDemand>, - pub cache: Arc>, - pub transaction_queue: Arc>, - pub ws_address: Option, - pub fetch: FetchClient, - pub geth_compatibility: bool, - pub experimental_rpcs: bool, - pub executor: Executor, - pub whisper_rpc: Option<::whisper::RpcFactory>, - pub private_tx_service: Option>, - pub gas_price_percentile: usize, - pub poll_lifetime: u32, -} - -impl LightDependencies { - fn extend_api>( - &self, - handler: &mut MetaIoHandler, - apis: &HashSet, - for_generic_pubsub: bool, - ) { - use parity_rpc::v1::*; - - let dispatcher = LightDispatcher::new( - self.sync.clone(), - self.client.clone(), - self.on_demand.clone(), - self.cache.clone(), - self.transaction_queue.clone(), - Arc::new(Mutex::new(dispatch::Reservations::new( - self.executor.clone(), - ))), - self.gas_price_percentile, - ); - let account_signer = Arc::new(dispatch::Signer::new(self.accounts.clone())) as _; - let accounts = account_utils::accounts_list(self.accounts.clone()); - - for api in apis { - match *api { - Api::Debug => { - warn!(target: "rpc", "Debug API is not available in light client mode.") - } - Api::Web3 => { - handler.extend_with(Web3Client::default().to_delegate()); - } - Api::Net => { - handler.extend_with(light::NetClient::new(self.sync.clone()).to_delegate()); - } - Api::Eth => { - let client = light::EthClient::new( - self.sync.clone(), - self.client.clone(), - self.on_demand.clone(), - self.transaction_queue.clone(), - accounts.clone(), - self.cache.clone(), - self.gas_price_percentile, - self.poll_lifetime, - ); - handler.extend_with(Eth::to_delegate(client.clone())); - - if !for_generic_pubsub { - handler.extend_with(EthFilter::to_delegate(client)); - add_signing_methods!(EthSigning, handler, self, (&dispatcher, &account_signer)); - } - } - Api::EthPubSub => { - let client = EthPubSubClient::light( - self.client.clone(), - self.on_demand.clone(), - self.sync.clone(), - self.cache.clone(), - self.executor.clone(), - self.gas_price_percentile, - ); - self.client.add_listener(client.handler() as Weak<_>); - let h = client.handler(); - self.transaction_queue - .write() - .add_listener(Box::new(move |transactions| { - if let Some(h) = h.upgrade() { - h.notify_new_transactions(transactions); - } - })); - handler.extend_with(EthPubSub::to_delegate(client)); - } - Api::Personal => { - #[cfg(feature = "accounts")] - handler.extend_with( - PersonalClient::new( - &self.accounts, - dispatcher.clone(), - self.geth_compatibility, - self.experimental_rpcs, - ).to_delegate(), - ); - } - Api::Signer => { - handler.extend_with( - SignerClient::new( - account_signer.clone(), - dispatcher.clone(), - &self.signer_service, - self.executor.clone(), - ).to_delegate(), - ); - } - Api::Parity => { - let signer = match self.signer_service.is_enabled() { - true => Some(self.signer_service.clone()), - false => None, - }; - handler.extend_with( - light::ParityClient::new( - Arc::new(dispatcher.clone()), - self.logger.clone(), - self.settings.clone(), - signer, - self.ws_address.clone(), - self.gas_price_percentile, - ).to_delegate(), - ); - #[cfg(feature = "accounts")] - handler.extend_with( - ParityAccountsInfo::to_delegate(ParityAccountsClient::new(&self.accounts)) - ); - - if !for_generic_pubsub { - add_signing_methods!(ParitySigning, handler, self, (&dispatcher, &account_signer)); - } - } - Api::ParityPubSub => { - if !for_generic_pubsub { - let mut rpc = MetaIoHandler::default(); - let apis = ApiSet::List(apis.clone()) - .retain(ApiSet::PubSub) - .list_apis(); - self.extend_api(&mut rpc, &apis, true); - handler.extend_with( - PubSubClient::new(rpc, self.executor.clone()).to_delegate(), - ); - } - } - Api::ParityAccounts => { - #[cfg(feature = "accounts")] - handler.extend_with(ParityAccounts::to_delegate(ParityAccountsClient::new(&self.accounts))); - } - Api::ParitySet => handler.extend_with( - light::ParitySetClient::new(self.client.clone(), self.sync.clone(), self.fetch.clone()) - .to_delegate(), - ), - Api::Traces => handler.extend_with(light::TracesClient.to_delegate()), - Api::Rpc => { - let modules = to_modules(&apis); - handler.extend_with(RpcClient::new(modules).to_delegate()); - } - Api::SecretStore => { - #[cfg(feature = "accounts")] - handler.extend_with(SecretStoreClient::new(&self.accounts).to_delegate()); - } - Api::Whisper => { - if let Some(ref whisper_rpc) = self.whisper_rpc { - let whisper = whisper_rpc.make_handler(self.net.clone()); - handler.extend_with(::parity_whisper::rpc::Whisper::to_delegate(whisper)); - } - } - Api::WhisperPubSub => { - if let Some(ref whisper_rpc) = self.whisper_rpc { - let whisper = whisper_rpc.make_handler(self.net.clone()); - handler.extend_with(::parity_whisper::rpc::WhisperPubSub::to_delegate( - whisper, - )); - } - } - Api::Private => { - if let Some(ref tx_manager) = self.private_tx_service { - let private_tx_service = Some(tx_manager.clone()); - handler.extend_with(PrivateClient::new(private_tx_service).to_delegate()); - } - } - } - } - } -} - -impl Dependencies for LightDependencies { - type Notifier = LightClientNotifier; - - fn activity_notifier(&self) -> Self::Notifier { - LightClientNotifier - } - - fn extend_with_set(&self, handler: &mut MetaIoHandler, apis: &HashSet) - where - S: core::Middleware, - { - self.extend_api(handler, apis, false) - } + type Notifier = ClientNotifier; + + fn activity_notifier(&self) -> ClientNotifier { + ClientNotifier { + client: self.client.clone(), + } + } + + fn extend_with_set(&self, handler: &mut MetaIoHandler, apis: &HashSet) + where + S: core::Middleware, + { + self.extend_api(handler, apis, false) + } } impl ApiSet { - /// Retains only APIs in given set. - pub fn retain(self, set: Self) -> Self { - ApiSet::List(&self.list_apis() & &set.list_apis()) - } - - pub fn list_apis(&self) -> HashSet { - let mut public_list: HashSet = [ - Api::Web3, - Api::Net, - Api::Eth, - Api::EthPubSub, - Api::Parity, - Api::Rpc, - Api::Whisper, - Api::WhisperPubSub, - Api::Private, - ] - .into_iter() - .cloned() - .collect(); - - match *self { - ApiSet::List(ref apis) => apis.clone(), - ApiSet::UnsafeContext => { - public_list.insert(Api::Traces); - public_list.insert(Api::ParityPubSub); - public_list - } - ApiSet::IpcContext => { - public_list.insert(Api::Traces); - public_list.insert(Api::ParityPubSub); - public_list.insert(Api::ParityAccounts); - public_list - } - ApiSet::All => { - public_list.insert(Api::Debug); - public_list.insert(Api::Traces); - public_list.insert(Api::ParityPubSub); - public_list.insert(Api::ParityAccounts); - public_list.insert(Api::ParitySet); - public_list.insert(Api::Signer); - public_list.insert(Api::Personal); - public_list.insert(Api::SecretStore); - public_list - } - ApiSet::PubSub => [ - Api::Eth, - Api::Parity, - Api::ParityAccounts, - Api::ParitySet, - Api::Traces, - ] - .into_iter() - .cloned() - .collect(), - } - } + /// Retains only APIs in given set. + pub fn retain(self, set: Self) -> Self { + ApiSet::List(&self.list_apis() & &set.list_apis()) + } + + pub fn list_apis(&self) -> HashSet { + let mut public_list: HashSet = + [Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity] + .iter() + .cloned() + .collect(); + + match *self { + ApiSet::List(ref apis) => apis.clone(), + ApiSet::UnsafeContext => { + public_list.insert(Api::Traces); + public_list.insert(Api::ParityPubSub); + public_list + } + ApiSet::IpcContext => { + public_list.insert(Api::Traces); + public_list.insert(Api::ParityPubSub); + public_list.insert(Api::ParityAccounts); + public_list + } + ApiSet::All => { + public_list.insert(Api::Debug); + public_list.insert(Api::Traces); + public_list.insert(Api::ParityPubSub); + public_list.insert(Api::ParityAccounts); + public_list.insert(Api::ParitySet); + public_list.insert(Api::Signer); + public_list.insert(Api::Personal); + public_list.insert(Api::SecretStore); + public_list + } + ApiSet::PubSub => [ + Api::Eth, + Api::Parity, + Api::ParityAccounts, + Api::ParitySet, + Api::Traces, + ] + .iter() + .cloned() + .collect(), + } + } } #[cfg(test)] mod test { - use super::{Api, ApiSet}; - - #[test] - fn test_api_parsing() { - assert_eq!(Api::Debug, "debug".parse().unwrap()); - assert_eq!(Api::Web3, "web3".parse().unwrap()); - assert_eq!(Api::Net, "net".parse().unwrap()); - assert_eq!(Api::Eth, "eth".parse().unwrap()); - assert_eq!(Api::EthPubSub, "pubsub".parse().unwrap()); - assert_eq!(Api::Personal, "personal".parse().unwrap()); - assert_eq!(Api::Signer, "signer".parse().unwrap()); - assert_eq!(Api::Parity, "parity".parse().unwrap()); - assert_eq!(Api::ParityAccounts, "parity_accounts".parse().unwrap()); - assert_eq!(Api::ParitySet, "parity_set".parse().unwrap()); - assert_eq!(Api::Traces, "traces".parse().unwrap()); - assert_eq!(Api::Rpc, "rpc".parse().unwrap()); - assert_eq!(Api::SecretStore, "secretstore".parse().unwrap()); - assert_eq!(Api::Private, "private".parse().unwrap()); - assert_eq!(Api::Whisper, "shh".parse().unwrap()); - assert_eq!(Api::WhisperPubSub, "shh_pubsub".parse().unwrap()); - assert!("rp".parse::().is_err()); - } - - #[test] - fn test_api_set_default() { - assert_eq!(ApiSet::UnsafeContext, ApiSet::default()); - } - - #[test] - fn test_api_set_parsing() { - assert_eq!( - ApiSet::List(vec![Api::Web3, Api::Eth].into_iter().collect()), - "web3,eth".parse().unwrap() - ); - } - - #[test] - fn test_api_set_unsafe_context() { - let expected = vec![ - // make sure this list contains only SAFE methods - Api::Web3, - Api::Net, - Api::Eth, - Api::EthPubSub, - Api::Parity, - Api::ParityPubSub, - Api::Traces, - Api::Rpc, - Api::Whisper, - Api::WhisperPubSub, - Api::Private, - ].into_iter() - .collect(); - assert_eq!(ApiSet::UnsafeContext.list_apis(), expected); - } - - #[test] - fn test_api_set_ipc_context() { - let expected = vec![ - // safe - Api::Web3, - Api::Net, - Api::Eth, - Api::EthPubSub, - Api::Parity, - Api::ParityPubSub, - Api::Traces, - Api::Rpc, - Api::Whisper, - Api::WhisperPubSub, - Api::Private, - // semi-safe - Api::ParityAccounts, - ].into_iter() - .collect(); - assert_eq!(ApiSet::IpcContext.list_apis(), expected); - } - - #[test] - fn test_all_apis() { - assert_eq!( - "all".parse::().unwrap(), - ApiSet::List( - vec![ - Api::Web3, - Api::Net, - Api::Eth, - Api::EthPubSub, - Api::Parity, - Api::ParityPubSub, - Api::Traces, - Api::Rpc, - Api::SecretStore, - Api::Whisper, - Api::WhisperPubSub, - Api::ParityAccounts, - Api::ParitySet, - Api::Signer, - Api::Personal, - Api::Private, - Api::Debug, - ].into_iter() - .collect() - ) - ); - } - - #[test] - fn test_all_without_personal_apis() { - assert_eq!( - "personal,all,-personal".parse::().unwrap(), - ApiSet::List( - vec![ - Api::Web3, - Api::Net, - Api::Eth, - Api::EthPubSub, - Api::Parity, - Api::ParityPubSub, - Api::Traces, - Api::Rpc, - Api::SecretStore, - Api::Whisper, - Api::WhisperPubSub, - Api::ParityAccounts, - Api::ParitySet, - Api::Signer, - Api::Private, - Api::Debug, - ].into_iter() - .collect() - ) - ); - } - - #[test] - fn test_safe_parsing() { - assert_eq!( - "safe".parse::().unwrap(), - ApiSet::List( - vec![ - Api::Web3, - Api::Net, - Api::Eth, - Api::EthPubSub, - Api::Parity, - Api::ParityPubSub, - Api::Traces, - Api::Rpc, - Api::Whisper, - Api::WhisperPubSub, - Api::Private, - ].into_iter() - .collect() - ) - ); - } + use super::{Api, ApiSet}; + + #[test] + fn test_api_parsing() { + assert_eq!(Api::Debug, "debug".parse().unwrap()); + assert_eq!(Api::Web3, "web3".parse().unwrap()); + assert_eq!(Api::Net, "net".parse().unwrap()); + assert_eq!(Api::Eth, "eth".parse().unwrap()); + assert_eq!(Api::EthPubSub, "pubsub".parse().unwrap()); + assert_eq!(Api::Personal, "personal".parse().unwrap()); + assert_eq!(Api::Signer, "signer".parse().unwrap()); + assert_eq!(Api::Parity, "parity".parse().unwrap()); + assert_eq!(Api::ParityAccounts, "parity_accounts".parse().unwrap()); + assert_eq!(Api::ParitySet, "parity_set".parse().unwrap()); + assert_eq!(Api::Traces, "traces".parse().unwrap()); + assert_eq!(Api::SecretStore, "secretstore".parse().unwrap()); + assert!("rp".parse::().is_err()); + } + + #[test] + fn test_api_set_default() { + assert_eq!(ApiSet::UnsafeContext, ApiSet::default()); + } + + #[test] + fn test_api_set_parsing() { + assert_eq!( + ApiSet::List(vec![Api::Web3, Api::Eth].into_iter().collect()), + "web3,eth".parse().unwrap() + ); + } + + #[test] + fn test_api_set_unsafe_context() { + let expected = vec![ + // make sure this list contains only SAFE methods + Api::Web3, + Api::Net, + Api::Eth, + Api::EthPubSub, + Api::Parity, + Api::ParityPubSub, + Api::Traces, + ] + .into_iter() + .collect(); + assert_eq!(ApiSet::UnsafeContext.list_apis(), expected); + } + + #[test] + fn test_api_set_ipc_context() { + let expected = vec![ + // safe + Api::Web3, + Api::Net, + Api::Eth, + Api::EthPubSub, + Api::Parity, + Api::ParityPubSub, + Api::Traces, + // semi-safe + Api::ParityAccounts, + ] + .into_iter() + .collect(); + assert_eq!(ApiSet::IpcContext.list_apis(), expected); + } + + #[test] + fn test_all_apis() { + assert_eq!( + "all".parse::().unwrap(), + ApiSet::List( + vec![ + Api::Web3, + Api::Net, + Api::Eth, + Api::EthPubSub, + Api::Parity, + Api::ParityPubSub, + Api::Traces, + Api::SecretStore, + Api::ParityAccounts, + Api::ParitySet, + Api::Signer, + Api::Personal, + Api::Debug, + ] + .into_iter() + .collect() + ) + ); + } + + #[test] + fn test_all_without_personal_apis() { + assert_eq!( + "personal,all,-personal".parse::().unwrap(), + ApiSet::List( + vec![ + Api::Web3, + Api::Net, + Api::Eth, + Api::EthPubSub, + Api::Parity, + Api::ParityPubSub, + Api::Traces, + Api::SecretStore, + Api::ParityAccounts, + Api::ParitySet, + Api::Signer, + Api::Debug, + ] + .into_iter() + .collect() + ) + ); + } + + #[test] + fn test_safe_parsing() { + assert_eq!( + "safe".parse::().unwrap(), + ApiSet::List( + vec![ + Api::Web3, + Api::Net, + Api::Eth, + Api::EthPubSub, + Api::Parity, + Api::ParityPubSub, + Api::Traces, + ] + .into_iter() + .collect() + ) + ); + } } diff --git a/parity/run.rs b/parity/run.rs index c4a3c75120e..59548bf7003 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -1,69 +1,64 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::any::Any; -use std::sync::{Arc, Weak, atomic}; -use std::time::{Duration, Instant}; -use std::thread; +use std::{ + any::Any, + sync::{atomic, Arc, Weak}, + thread, + time::{Duration, Instant}, +}; +use account_utils; use ansi_term::Colour; -use bytes::Bytes; -use call_contract::CallContract; -use ethcore::client::{BlockId, Client, Mode, DatabaseCompactionProfile, VMType, BlockChainClient, BlockInfo}; -use ethcore::miner::{self, stratum, Miner, MinerService, MinerOptions}; -use ethcore::snapshot::{self, SnapshotConfiguration}; -use ethcore::spec::{SpecParams, OptimizeFor}; -use ethcore::verification::queue::VerifierSettings; +use cache::CacheConfig; +use db; +use dir::{DatabaseDirectories, Directories}; +use ethcore::{ + client::{BlockChainClient, BlockInfo, Client, DatabaseCompactionProfile, Mode, VMType}, + miner::{self, stratum, Miner, MinerOptions, MinerService}, + snapshot::{self, SnapshotConfiguration}, + verification::queue::VerifierSettings, +}; use ethcore_logger::{Config as LogConfig, RotatingLogger}; use ethcore_service::ClientService; -use ethereum_types::Address; -use futures::IntoFuture; -use hash_fetch::{self, fetch}; -use informant::{Informant, LightNodeInformantData, FullNodeInformantData}; +use ethereum_types::H256; +use helpers::{execute_upgrades, passwords_from_files, to_client_config}; +use informant::{FullNodeInformantData, Informant}; use journaldb::Algorithm; -use light::Cache as LightDataCache; -use miner::external::ExternalMiner; -use miner::work_notify::WorkPoster; +use jsonrpc_core; +use metrics::{start_prometheus_metrics, MetricsConfiguration}; +use miner::{external::ExternalMiner, work_notify::WorkPoster}; +use modules; use node_filter::NodeFilter; -use parity_runtime::Runtime; -use sync::{self, SyncConfig, PrivateTxHandler}; +use params::{ + fatdb_switch_to_bool, mode_switch_to_bool, tracing_switch_to_bool, AccountsConfig, + GasPricerConfig, MinerExtras, Pruning, SpecType, Switch, +}; use parity_rpc::{ - Origin, Metadata, NetworkSettings, informant, is_major_importing, PubSubSession, FutureResult, FutureResponse, FutureOutput + informant, is_major_importing, FutureOutput, FutureResponse, FutureResult, Metadata, + NetworkSettings, Origin, PubSubSession, }; -use updater::{UpdatePolicy, Updater}; +use parity_runtime::Runtime; use parity_version::version; -use ethcore_private_tx::{ProviderConfig, EncryptorConfig, SecretStoreEncryptor}; -use params::{ - SpecType, Pruning, AccountsConfig, GasPricerConfig, MinerExtras, Switch, - tracing_switch_to_bool, fatdb_switch_to_bool, mode_switch_to_bool -}; -use account_utils; -use helpers::{to_client_config, execute_upgrades, passwords_from_files}; -use dir::{Directories, DatabaseDirectories}; -use cache::CacheConfig; -use user_defaults::UserDefaults; -use ipfs; -use jsonrpc_core; -use modules; -use registrar::{RegistrarClient, Asynchronous}; use rpc; use rpc_apis; use secretstore; use signer; -use db; +use sync::{self, SyncConfig}; +use user_defaults::UserDefaults; // how often to take periodic snapshots. const SNAPSHOT_PERIOD: u64 = 5000; @@ -71,776 +66,544 @@ const SNAPSHOT_PERIOD: u64 = 5000; // how many blocks to wait before starting a periodic snapshot. const SNAPSHOT_HISTORY: u64 = 100; -// Number of minutes before a given gas price corpus should expire. -// Light client only. -const GAS_CORPUS_EXPIRATION_MINUTES: u64 = 60 * 6; - // Full client number of DNS threads const FETCH_FULL_NUM_DNS_THREADS: usize = 4; -// Light client number of DNS threads -const FETCH_LIGHT_NUM_DNS_THREADS: usize = 1; - #[derive(Debug, PartialEq)] pub struct RunCmd { - pub cache_config: CacheConfig, - pub dirs: Directories, - pub spec: SpecType, - pub pruning: Pruning, - pub pruning_history: u64, - pub pruning_memory: usize, - /// Some if execution should be daemonized. Contains pid_file path. - pub daemon: Option, - pub logger_config: LogConfig, - pub miner_options: MinerOptions, - pub gas_price_percentile: usize, - pub poll_lifetime: u32, - pub ws_conf: rpc::WsConfiguration, - pub http_conf: rpc::HttpConfiguration, - pub ipc_conf: rpc::IpcConfiguration, - pub net_conf: sync::NetworkConfiguration, - pub network_id: Option, - pub warp_sync: bool, - pub warp_barrier: Option, - pub acc_conf: AccountsConfig, - pub gas_pricer_conf: GasPricerConfig, - pub miner_extras: MinerExtras, - pub update_policy: UpdatePolicy, - pub mode: Option, - pub tracing: Switch, - pub fat_db: Switch, - pub compaction: DatabaseCompactionProfile, - pub vm_type: VMType, - pub geth_compatibility: bool, - pub experimental_rpcs: bool, - pub net_settings: NetworkSettings, - pub ipfs_conf: ipfs::Configuration, - pub secretstore_conf: secretstore::Configuration, - pub private_provider_conf: ProviderConfig, - pub private_encryptor_conf: EncryptorConfig, - pub private_tx_enabled: bool, - pub name: String, - pub custom_bootnodes: bool, - pub stratum: Option, - pub snapshot_conf: SnapshotConfiguration, - pub check_seal: bool, - pub allow_missing_blocks: bool, - pub download_old_blocks: bool, - pub verifier_settings: VerifierSettings, - pub serve_light: bool, - pub light: bool, - pub no_persistent_txqueue: bool, - pub whisper: ::whisper::Config, - pub no_hardcoded_sync: bool, - pub max_round_blocks_to_import: usize, - pub on_demand_response_time_window: Option, - pub on_demand_request_backoff_start: Option, - pub on_demand_request_backoff_max: Option, - pub on_demand_request_backoff_rounds_max: Option, - pub on_demand_request_consecutive_failures: Option, + pub cache_config: CacheConfig, + pub dirs: Directories, + pub spec: SpecType, + pub pruning: Pruning, + pub pruning_history: u64, + pub pruning_memory: usize, + /// Some if execution should be daemonized. Contains pid_file path. + pub daemon: Option, + pub logger_config: LogConfig, + pub miner_options: MinerOptions, + pub gas_price_percentile: usize, + pub poll_lifetime: u32, + pub ws_conf: rpc::WsConfiguration, + pub http_conf: rpc::HttpConfiguration, + pub ipc_conf: rpc::IpcConfiguration, + pub net_conf: sync::NetworkConfiguration, + pub network_id: Option, + pub warp_sync: bool, + pub warp_barrier: Option, + pub acc_conf: AccountsConfig, + pub gas_pricer_conf: GasPricerConfig, + pub miner_extras: MinerExtras, + pub mode: Option, + pub tracing: Switch, + pub fat_db: Switch, + pub compaction: DatabaseCompactionProfile, + pub vm_type: VMType, + pub experimental_rpcs: bool, + pub net_settings: NetworkSettings, + pub secretstore_conf: secretstore::Configuration, + pub name: String, + pub custom_bootnodes: bool, + pub stratum: Option, + pub snapshot_conf: SnapshotConfiguration, + pub check_seal: bool, + pub allow_missing_blocks: bool, + pub download_old_blocks: bool, + pub verifier_settings: VerifierSettings, + pub no_persistent_txqueue: bool, + pub max_round_blocks_to_import: usize, + pub metrics_conf: MetricsConfiguration, } // node info fetcher for the local store. struct FullNodeInfo { - miner: Option>, // TODO: only TXQ needed, just use that after decoupling. + miner: Option>, // TODO: only TXQ needed, just use that after decoupling. } impl ::local_store::NodeInfo for FullNodeInfo { - fn pending_transactions(&self) -> Vec<::types::transaction::PendingTransaction> { - let miner = match self.miner.as_ref() { - Some(m) => m, - None => return Vec::new(), - }; - - miner.local_transactions() - .values() - .filter_map(|status| match *status { - ::miner::pool::local_transactions::Status::Pending(ref tx) => Some(tx.pending().clone()), - _ => None, - }) - .collect() - } + fn pending_transactions(&self) -> Vec<::types::transaction::PendingTransaction> { + let miner = match self.miner.as_ref() { + Some(m) => m, + None => return Vec::new(), + }; + + miner + .local_transactions() + .values() + .filter_map(|status| match *status { + ::miner::pool::local_transactions::Status::Pending(ref tx) => { + Some(tx.pending().clone()) + } + _ => None, + }) + .collect() + } } -type LightClient = ::light::client::Client<::light_helpers::EpochFetch>; - -// helper for light execution. -fn execute_light_impl(cmd: RunCmd, logger: Arc, on_client_rq: Cr) -> Result - where Cr: Fn(String) + 'static + Send -{ - use light::client as light_client; - use sync::{LightSyncParams, LightSync, ManageNetwork}; - use parking_lot::{Mutex, RwLock}; - - // load spec - let spec = cmd.spec.spec(SpecParams::new(cmd.dirs.cache.as_ref(), OptimizeFor::Memory))?; - - // load genesis hash - let genesis_hash = spec.genesis_header().hash(); - - // database paths - let db_dirs = cmd.dirs.database(genesis_hash, cmd.spec.legacy_fork_name(), spec.data_dir.clone()); - - // user defaults path - let user_defaults_path = db_dirs.user_defaults_path(); - - // load user defaults - let user_defaults = UserDefaults::load(&user_defaults_path)?; - - // select pruning algorithm - let algorithm = cmd.pruning.to_algorithm(&user_defaults); - - // execute upgrades - execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?; - - // create dirs used by parity - cmd.dirs.create_dirs(cmd.acc_conf.unlocked_accounts.len() == 0, cmd.secretstore_conf.enabled)?; - - //print out running parity environment - print_running_environment(&spec.data_dir, &cmd.dirs, &db_dirs); - - info!("Running in experimental {} mode.", Colour::Blue.bold().paint("Light Client")); - - // TODO: configurable cache size. - let cache = LightDataCache::new(Default::default(), Duration::from_secs(60 * GAS_CORPUS_EXPIRATION_MINUTES)); - let cache = Arc::new(Mutex::new(cache)); - - // start client and create transaction queue. - let mut config = light_client::Config { - queue: Default::default(), - chain_column: ::ethcore_db::COL_LIGHT_CHAIN, - verify_full: true, - check_seal: cmd.check_seal, - no_hardcoded_sync: cmd.no_hardcoded_sync, - }; - - config.queue.max_mem_use = cmd.cache_config.queue() as usize * 1024 * 1024; - config.queue.verifier_settings = cmd.verifier_settings; - - // start on_demand service. - - let response_time_window = cmd.on_demand_response_time_window.map_or( - ::light::on_demand::DEFAULT_RESPONSE_TIME_TO_LIVE, - |s| Duration::from_secs(s) - ); - - let request_backoff_start = cmd.on_demand_request_backoff_start.map_or( - ::light::on_demand::DEFAULT_REQUEST_MIN_BACKOFF_DURATION, - |s| Duration::from_secs(s) - ); - - let request_backoff_max = cmd.on_demand_request_backoff_max.map_or( - ::light::on_demand::DEFAULT_REQUEST_MAX_BACKOFF_DURATION, - |s| Duration::from_secs(s) - ); - - let on_demand = Arc::new({ - ::light::on_demand::OnDemand::new( - cache.clone(), - response_time_window, - request_backoff_start, - request_backoff_max, - cmd.on_demand_request_backoff_rounds_max.unwrap_or(::light::on_demand::DEFAULT_MAX_REQUEST_BACKOFF_ROUNDS), - cmd.on_demand_request_consecutive_failures.unwrap_or(::light::on_demand::DEFAULT_NUM_CONSECUTIVE_FAILED_REQUESTS) - ) - }); - - let sync_handle = Arc::new(RwLock::new(Weak::new())); - let fetch = ::light_helpers::EpochFetch { - on_demand: on_demand.clone(), - sync: sync_handle.clone(), - }; - - // initialize database. - let db = db::open_db(&db_dirs.client_path(algorithm).to_str().expect("DB path could not be converted to string."), - &cmd.cache_config, - &cmd.compaction).map_err(|e| format!("Failed to open database {:?}", e))?; - - let service = light_client::Service::start(config, &spec, fetch, db, cache.clone()) - .map_err(|e| format!("Error starting light client: {}", e))?; - let client = service.client().clone(); - let txq = Arc::new(RwLock::new(::light::transaction_queue::TransactionQueue::default())); - let provider = ::light::provider::LightProvider::new(client.clone(), txq.clone()); - - // start network. - // set up bootnodes - let mut net_conf = cmd.net_conf; - if !cmd.custom_bootnodes { - net_conf.boot_nodes = spec.nodes.clone(); - } - - let mut attached_protos = Vec::new(); - let whisper_factory = if cmd.whisper.enabled { - let whisper_factory = ::whisper::setup(cmd.whisper.target_message_pool_size, &mut attached_protos) - .map_err(|e| format!("Failed to initialize whisper: {}", e))?; - whisper_factory - } else { - None - }; - - // set network path. - net_conf.net_config_path = Some(db_dirs.network_path().to_string_lossy().into_owned()); - let sync_params = LightSyncParams { - network_config: net_conf.into_basic().map_err(|e| format!("Failed to produce network config: {}", e))?, - client: Arc::new(provider), - network_id: cmd.network_id.unwrap_or(spec.network_id()), - subprotocol_name: sync::LIGHT_PROTOCOL, - handlers: vec![on_demand.clone()], - attached_protos: attached_protos, - }; - let light_sync = LightSync::new(sync_params).map_err(|e| format!("Error starting network: {}", e))?; - let light_sync = Arc::new(light_sync); - *sync_handle.write() = Arc::downgrade(&light_sync); - - // spin up event loop - let runtime = Runtime::with_default_thread_count(); - - // queue cull service. - let queue_cull = Arc::new(::light_helpers::QueueCull { - client: client.clone(), - sync: light_sync.clone(), - on_demand: on_demand.clone(), - txq: txq.clone(), - executor: runtime.executor(), - }); - - service.register_handler(queue_cull).map_err(|e| format!("Error attaching service: {:?}", e))?; - - // start the network. - light_sync.start_network(); - - // fetch service - let fetch = fetch::Client::new(FETCH_LIGHT_NUM_DNS_THREADS).map_err(|e| format!("Error starting fetch client: {:?}", e))?; - let passwords = passwords_from_files(&cmd.acc_conf.password_files)?; - - // prepare account provider - let account_provider = Arc::new(account_utils::prepare_account_provider(&cmd.spec, &cmd.dirs, &spec.data_dir, cmd.acc_conf, &passwords)?); - let rpc_stats = Arc::new(informant::RpcStats::default()); - - // the dapps server - let signer_service = Arc::new(signer::new_service(&cmd.ws_conf, &cmd.logger_config)); - - // start RPCs - let deps_for_rpc_apis = Arc::new(rpc_apis::LightDependencies { - signer_service: signer_service, - client: client.clone(), - sync: light_sync.clone(), - net: light_sync.clone(), - accounts: account_provider, - logger: logger, - settings: Arc::new(cmd.net_settings), - on_demand: on_demand, - cache: cache.clone(), - transaction_queue: txq, - ws_address: cmd.ws_conf.address(), - fetch: fetch, - geth_compatibility: cmd.geth_compatibility, - experimental_rpcs: cmd.experimental_rpcs, - executor: runtime.executor(), - whisper_rpc: whisper_factory, - private_tx_service: None, //TODO: add this to client. - gas_price_percentile: cmd.gas_price_percentile, - poll_lifetime: cmd.poll_lifetime - }); - - let dependencies = rpc::Dependencies { - apis: deps_for_rpc_apis.clone(), - executor: runtime.executor(), - stats: rpc_stats.clone(), - }; - - // start rpc servers - let rpc_direct = rpc::setup_apis(rpc_apis::ApiSet::All, &dependencies); - let ws_server = rpc::new_ws(cmd.ws_conf, &dependencies)?; - let http_server = rpc::new_http("HTTP JSON-RPC", "jsonrpc", cmd.http_conf.clone(), &dependencies)?; - let ipc_server = rpc::new_ipc(cmd.ipc_conf, &dependencies)?; - - // the informant - let informant = Arc::new(Informant::new( - LightNodeInformantData { - client: client.clone(), - sync: light_sync.clone(), - cache: cache, - }, - None, - Some(rpc_stats), - cmd.logger_config.color, - )); - service.add_notify(informant.clone()); - service.register_handler(informant.clone()).map_err(|_| "Unable to register informant handler".to_owned())?; - - client.set_exit_handler(on_client_rq); - - Ok(RunningClient { - inner: RunningClientInner::Light { - rpc: rpc_direct, - informant, - client, - keep_alive: Box::new((service, ws_server, http_server, ipc_server, runtime)), - } - }) +/// Executes the given run command. +/// +/// On error, returns what to print on stderr. +pub fn execute(cmd: RunCmd, logger: Arc) -> Result { + // load spec + let spec = cmd.spec.spec(&cmd.dirs.cache)?; + + // load genesis hash + let genesis_hash = spec.genesis_header().hash(); + + // database paths + let db_dirs = cmd.dirs.database( + genesis_hash, + cmd.spec.legacy_fork_name(), + spec.data_dir.clone(), + ); + + // user defaults path + let user_defaults_path = db_dirs.user_defaults_path(); + + // load user defaults + let mut user_defaults = UserDefaults::load(&user_defaults_path)?; + + // select pruning algorithm + let algorithm = cmd.pruning.to_algorithm(&user_defaults); + + // check if tracing is on + let tracing = tracing_switch_to_bool(cmd.tracing, &user_defaults)?; + + // check if fatdb is on + let fat_db = fatdb_switch_to_bool(cmd.fat_db, &user_defaults, algorithm)?; + + // get the mode + let mode = mode_switch_to_bool(cmd.mode, &user_defaults)?; + trace!(target: "mode", "mode is {:?}", mode); + let network_enabled = match mode { + Mode::Dark(_) | Mode::Off => false, + _ => true, + }; + + // prepare client and snapshot paths. + let client_path = db_dirs.client_path(algorithm); + let snapshot_path = db_dirs.snapshot_path(); + + // execute upgrades + execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?; + + // create dirs used by parity + cmd.dirs.create_dirs( + cmd.acc_conf.unlocked_accounts.len() == 0, + cmd.secretstore_conf.enabled, + )?; + + //print out running parity environment + print_running_environment(&spec.data_dir, &cmd.dirs, &db_dirs); + + // display info about used pruning algorithm + info!( + "State DB configuration: {}{}{}", + Colour::White.bold().paint(algorithm.as_str()), + match fat_db { + true => Colour::White.bold().paint(" +Fat").to_string(), + false => "".to_owned(), + }, + match tracing { + true => Colour::White.bold().paint(" +Trace").to_string(), + false => "".to_owned(), + } + ); + info!( + "Operating mode: {}", + Colour::White.bold().paint(format!("{}", mode)) + ); + + // display warning about using experimental journaldb algorithm + if !algorithm.is_stable() { + warn!( + "Your chosen strategy is {}! You can re-run with --pruning to change.", + Colour::Red.bold().paint("unstable") + ); + } + + // create sync config + let mut sync_config = SyncConfig::default(); + sync_config.network_id = match cmd.network_id { + Some(id) => id, + None => spec.network_id(), + }; + if spec.subprotocol_name().len() != 3 { + warn!("Your chain specification's subprotocol length is not 3. Ignoring."); + } else { + sync_config + .subprotocol_name + .clone_from_slice(spec.subprotocol_name().as_bytes()); + } + + sync_config.fork_block = spec.fork_block(); + let mut warp_sync = spec.engine.supports_warp() && cmd.warp_sync; + if warp_sync { + // Logging is not initialized yet, so we print directly to stderr + if fat_db { + warn!("Warning: Warp Sync is disabled because Fat DB is turned on."); + warp_sync = false; + } else if tracing { + warn!("Warning: Warp Sync is disabled because tracing is turned on."); + warp_sync = false; + } else if algorithm != Algorithm::OverlayRecent { + warn!("Warning: Warp Sync is disabled because of non-default pruning mode."); + warp_sync = false; + } + } + sync_config.warp_sync = match (warp_sync, cmd.warp_barrier) { + (true, Some(block)) => sync::WarpSync::OnlyAndAfter(block), + (true, _) => sync::WarpSync::Enabled, + _ => sync::WarpSync::Disabled, + }; + sync_config.download_old_blocks = cmd.download_old_blocks; + + let passwords = passwords_from_files(&cmd.acc_conf.password_files)?; + + // prepare account provider + let account_provider = Arc::new(account_utils::prepare_account_provider( + &cmd.spec, + &cmd.dirs, + &spec.data_dir, + cmd.acc_conf, + &passwords, + )?); + + // spin up event loop + let runtime = Runtime::with_default_thread_count(); + + // fetch service + let fetch = fetch::Client::new(FETCH_FULL_NUM_DNS_THREADS) + .map_err(|e| format!("Error starting fetch client: {:?}", e))?; + + let txpool_size = cmd.miner_options.pool_limits.max_count; + // create miner + let miner = Arc::new(Miner::new( + cmd.miner_options, + cmd.gas_pricer_conf + .to_gas_pricer(fetch.clone(), runtime.executor()), + &spec, + ( + cmd.miner_extras.local_accounts, + account_utils::miner_local_accounts(account_provider.clone()), + ), + )); + miner.set_author(miner::Author::External(cmd.miner_extras.author)); + miner.set_gas_range_target(cmd.miner_extras.gas_range_target); + miner.set_extra_data(cmd.miner_extras.extra_data); + + if !cmd.miner_extras.work_notify.is_empty() { + miner.add_work_listener(Box::new(WorkPoster::new( + &cmd.miner_extras.work_notify, + fetch.clone(), + runtime.executor(), + ))); + } + + let engine_signer = cmd.miner_extras.engine_signer; + if engine_signer != Default::default() { + if let Some(author) = account_utils::miner_author( + &cmd.spec, + &cmd.dirs, + &account_provider, + engine_signer, + &passwords, + )? { + miner.set_author(author); + } + } + + // create client config + let mut client_config = to_client_config( + &cmd.cache_config, + spec.name.to_lowercase(), + mode.clone(), + tracing, + fat_db, + cmd.compaction, + cmd.vm_type, + cmd.name, + algorithm, + cmd.pruning_history, + cmd.pruning_memory, + cmd.check_seal, + cmd.max_round_blocks_to_import, + ); + + client_config.queue.verifier_settings = cmd.verifier_settings; + client_config.queue.verifier_settings.bad_hashes = verification_bad_blocks(&cmd.spec); + client_config.transaction_verification_queue_size = ::std::cmp::max(2048, txpool_size / 4); + client_config.snapshot = cmd.snapshot_conf.clone(); + + // set up bootnodes + let mut net_conf = cmd.net_conf; + if !cmd.custom_bootnodes { + net_conf.boot_nodes = spec.nodes.clone(); + } + + // set network path. + net_conf.net_config_path = Some(db_dirs.network_path().to_string_lossy().into_owned()); + + let restoration_db_handler = db::restoration_db_handler(&client_path, &client_config); + let client_db = restoration_db_handler + .open(&client_path) + .map_err(|e| format!("Failed to open database {:?}", e))?; + + // create client service. + let service = ClientService::start( + client_config, + &spec, + client_db, + &snapshot_path, + restoration_db_handler, + &cmd.dirs.ipc_path(), + miner.clone(), + ) + .map_err(|e| format!("Client service error: {:?}", e))?; + + let connection_filter_address = spec.params().node_permission_contract; + // drop the spec to free up genesis state. + let forks = spec.hard_forks.clone(); + drop(spec); + + // take handle to client + let client = service.client(); + // Update miners block gas limit + miner.update_transaction_queue_limits(*client.best_block_header().gas_limit()); + + let connection_filter = connection_filter_address.map(|a| { + Arc::new(NodeFilter::new( + Arc::downgrade(&client) as Weak, + a, + )) + }); + let snapshot_service = service.snapshot_service(); + + // initialize the local node information store. + let store = { + let db = service.db(); + let node_info = FullNodeInfo { + miner: match cmd.no_persistent_txqueue { + true => None, + false => Some(miner.clone()), + }, + }; + + let store = ::local_store::create( + db.key_value().clone(), + ::ethcore_db::COL_NODE_INFO, + node_info, + ); + + if cmd.no_persistent_txqueue { + info!("Running without a persistent transaction queue."); + + if let Err(e) = store.clear() { + warn!("Error clearing persistent transaction queue: {}", e); + } + } + + // re-queue pending transactions. + match store.pending_transactions() { + Ok(pending) => { + for pending_tx in pending { + if let Err(e) = miner.import_own_transaction(&*client, pending_tx) { + warn!("Error importing saved transaction: {}", e) + } + } + } + Err(e) => warn!("Error loading cached pending transactions from disk: {}", e), + } + + Arc::new(store) + }; + + // register it as an IO service to update periodically. + service + .register_io_handler(store) + .map_err(|_| "Unable to register local store handler".to_owned())?; + + // create external miner + let external_miner = Arc::new(ExternalMiner::default()); + + // start stratum + if let Some(ref stratum_config) = cmd.stratum { + stratum::Stratum::register(stratum_config, miner.clone(), Arc::downgrade(&client)) + .map_err(|e| format!("Stratum start error: {:?}", e))?; + } + + // create sync object + let (sync_provider, manage_network, chain_notify, priority_tasks) = modules::sync( + sync_config, + net_conf.clone().into(), + client.clone(), + forks, + snapshot_service.clone(), + &cmd.logger_config, + connection_filter + .clone() + .map(|f| f as Arc), + ) + .map_err(|e| format!("Sync error: {}", e))?; + + service.add_notify(chain_notify.clone()); + + // Propagate transactions as soon as they are imported. + let tx = ::parking_lot::Mutex::new(priority_tasks); + let is_ready = Arc::new(atomic::AtomicBool::new(true)); + miner.add_transactions_listener(Box::new(move |_hashes| { + // we want to have only one PendingTransactions task in the queue. + if is_ready.compare_and_swap(true, false, atomic::Ordering::SeqCst) { + let task = + ::sync::PriorityTask::PropagateTransactions(Instant::now(), is_ready.clone()); + // we ignore error cause it means that we are closing + let _ = tx.lock().send(task); + } + })); + + // start network + if network_enabled { + chain_notify.start(); + } + + // set up dependencies for rpc servers + let rpc_stats = Arc::new(informant::RpcStats::default()); + let secret_store = account_provider.clone(); + let signer_service = Arc::new(signer::new_service(&cmd.ws_conf, &cmd.logger_config)); + + let deps_for_rpc_apis = Arc::new(rpc_apis::FullDependencies { + signer_service: signer_service, + snapshot: snapshot_service.clone(), + client: client.clone(), + sync: sync_provider.clone(), + net: manage_network.clone(), + accounts: secret_store, + miner: miner.clone(), + external_miner: external_miner.clone(), + logger: logger.clone(), + settings: Arc::new(cmd.net_settings.clone()), + net_service: manage_network.clone(), + experimental_rpcs: cmd.experimental_rpcs, + ws_address: cmd.ws_conf.address(), + fetch: fetch.clone(), + executor: runtime.executor(), + gas_price_percentile: cmd.gas_price_percentile, + poll_lifetime: cmd.poll_lifetime, + allow_missing_blocks: cmd.allow_missing_blocks, + no_ancient_blocks: !cmd.download_old_blocks, + }); + + let dependencies = rpc::Dependencies { + apis: deps_for_rpc_apis.clone(), + executor: runtime.executor(), + stats: rpc_stats.clone(), + }; + + // start rpc servers + let rpc_direct = rpc::setup_apis(rpc_apis::ApiSet::All, &dependencies); + let ws_server = rpc::new_ws(cmd.ws_conf.clone(), &dependencies)?; + let ipc_server = rpc::new_ipc(cmd.ipc_conf, &dependencies)?; + + // start the prometheus metrics server + start_prometheus_metrics(&cmd.metrics_conf, &dependencies)?; + + let http_server = rpc::new_http( + "HTTP JSON-RPC", + "jsonrpc", + cmd.http_conf.clone(), + &dependencies, + )?; + + // secret store key server + let secretstore_deps = secretstore::Dependencies { + client: client.clone(), + sync: sync_provider.clone(), + miner: miner.clone(), + account_provider, + accounts_passwords: &passwords, + }; + let secretstore_key_server = secretstore::start( + cmd.secretstore_conf.clone(), + secretstore_deps, + runtime.executor(), + )?; + + // the informant + let informant = Arc::new(Informant::new( + FullNodeInformantData { + client: service.client(), + sync: Some(sync_provider.clone()), + net: Some(manage_network.clone()), + }, + Some(snapshot_service.clone()), + Some(rpc_stats.clone()), + cmd.logger_config.color, + )); + service.add_notify(informant.clone()); + service + .register_io_handler(informant.clone()) + .map_err(|_| "Unable to register informant handler".to_owned())?; + + // save user defaults + user_defaults.is_first_launch = false; + user_defaults.pruning = algorithm; + user_defaults.tracing = tracing; + user_defaults.fat_db = fat_db; + user_defaults.set_mode(mode); + user_defaults.save(&user_defaults_path)?; + + // tell client how to save the default mode if it gets changed. + client.on_user_defaults_change(move |mode: Option| { + if let Some(mode) = mode { + user_defaults.set_mode(mode); + } + let _ = user_defaults.save(&user_defaults_path); // discard failures - there's nothing we can do + }); + + // the watcher must be kept alive. + let watcher = match cmd.snapshot_conf.enable { + false => None, + true => { + let sync = sync_provider.clone(); + let client = client.clone(); + let watcher = Arc::new(snapshot::Watcher::new( + service.client(), + move || is_major_importing(Some(sync.status().state), client.queue_info()), + service.io().channel(), + SNAPSHOT_PERIOD, + SNAPSHOT_HISTORY, + )); + + service.add_notify(watcher.clone()); + Some(watcher) + } + }; + + Ok(RunningClient { + inner: RunningClientInner::Full { + rpc: rpc_direct, + informant, + client, + client_service: Arc::new(service), + keep_alive: Box::new(( + watcher, + ws_server, + http_server, + ipc_server, + secretstore_key_server, + runtime, + )), + }, + }) } -fn execute_impl(cmd: RunCmd, logger: Arc, on_client_rq: Cr, - on_updater_rq: Rr) -> Result - where Cr: Fn(String) + 'static + Send, - Rr: Fn() + 'static + Send -{ - // load spec - let spec = cmd.spec.spec(&cmd.dirs.cache)?; - - // load genesis hash - let genesis_hash = spec.genesis_header().hash(); - - // database paths - let db_dirs = cmd.dirs.database(genesis_hash, cmd.spec.legacy_fork_name(), spec.data_dir.clone()); - - // user defaults path - let user_defaults_path = db_dirs.user_defaults_path(); - - // load user defaults - let mut user_defaults = UserDefaults::load(&user_defaults_path)?; - - // select pruning algorithm - let algorithm = cmd.pruning.to_algorithm(&user_defaults); - - // check if tracing is on - let tracing = tracing_switch_to_bool(cmd.tracing, &user_defaults)?; - - // check if fatdb is on - let fat_db = fatdb_switch_to_bool(cmd.fat_db, &user_defaults, algorithm)?; - - // get the mode - let mode = mode_switch_to_bool(cmd.mode, &user_defaults)?; - trace!(target: "mode", "mode is {:?}", mode); - let network_enabled = match mode { Mode::Dark(_) | Mode::Off => false, _ => true, }; - - // get the update policy - let update_policy = cmd.update_policy; - - // prepare client and snapshot paths. - let client_path = db_dirs.client_path(algorithm); - let snapshot_path = db_dirs.snapshot_path(); - - // execute upgrades - execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?; - - // create dirs used by parity - cmd.dirs.create_dirs(cmd.acc_conf.unlocked_accounts.len() == 0, cmd.secretstore_conf.enabled)?; - - //print out running parity environment - print_running_environment(&spec.data_dir, &cmd.dirs, &db_dirs); - - // display info about used pruning algorithm - info!("State DB configuration: {}{}{}", - Colour::White.bold().paint(algorithm.as_str()), - match fat_db { - true => Colour::White.bold().paint(" +Fat").to_string(), - false => "".to_owned(), - }, - match tracing { - true => Colour::White.bold().paint(" +Trace").to_string(), - false => "".to_owned(), - } - ); - info!("Operating mode: {}", Colour::White.bold().paint(format!("{}", mode))); - - // display warning about using experimental journaldb algorithm - if !algorithm.is_stable() { - warn!("Your chosen strategy is {}! You can re-run with --pruning to change.", Colour::Red.bold().paint("unstable")); - } - - // create sync config - let mut sync_config = SyncConfig::default(); - sync_config.network_id = match cmd.network_id { - Some(id) => id, - None => spec.network_id(), - }; - if spec.subprotocol_name().len() != 3 { - warn!("Your chain specification's subprotocol length is not 3. Ignoring."); - } else { - sync_config.subprotocol_name.clone_from_slice(spec.subprotocol_name().as_bytes()); - } - - sync_config.fork_block = spec.fork_block(); - let mut warp_sync = spec.engine.supports_warp() && cmd.warp_sync; - if warp_sync { - // Logging is not initialized yet, so we print directly to stderr - if fat_db { - warn!("Warning: Warp Sync is disabled because Fat DB is turned on."); - warp_sync = false; - } else if tracing { - warn!("Warning: Warp Sync is disabled because tracing is turned on."); - warp_sync = false; - } else if algorithm != Algorithm::OverlayRecent { - warn!("Warning: Warp Sync is disabled because of non-default pruning mode."); - warp_sync = false; - } - } - sync_config.warp_sync = match (warp_sync, cmd.warp_barrier) { - (true, Some(block)) => sync::WarpSync::OnlyAndAfter(block), - (true, _) => sync::WarpSync::Enabled, - _ => sync::WarpSync::Disabled, - }; - sync_config.download_old_blocks = cmd.download_old_blocks; - sync_config.serve_light = cmd.serve_light; - - let passwords = passwords_from_files(&cmd.acc_conf.password_files)?; - - // prepare account provider - let account_provider = Arc::new(account_utils::prepare_account_provider(&cmd.spec, &cmd.dirs, &spec.data_dir, cmd.acc_conf, &passwords)?); - - // spin up event loop - let runtime = Runtime::with_default_thread_count(); - - // fetch service - let fetch = fetch::Client::new(FETCH_FULL_NUM_DNS_THREADS).map_err(|e| format!("Error starting fetch client: {:?}", e))?; - - let txpool_size = cmd.miner_options.pool_limits.max_count; - // create miner - let miner = Arc::new(Miner::new( - cmd.miner_options, - cmd.gas_pricer_conf.to_gas_pricer(fetch.clone(), runtime.executor()), - &spec, - ( - cmd.miner_extras.local_accounts, - account_utils::miner_local_accounts(account_provider.clone()), - ) - )); - miner.set_author(miner::Author::External(cmd.miner_extras.author)); - miner.set_gas_range_target(cmd.miner_extras.gas_range_target); - miner.set_extra_data(cmd.miner_extras.extra_data); - - if !cmd.miner_extras.work_notify.is_empty() { - miner.add_work_listener(Box::new( - WorkPoster::new(&cmd.miner_extras.work_notify, fetch.clone(), runtime.executor()) - )); - } - - let engine_signer = cmd.miner_extras.engine_signer; - if engine_signer != Default::default() { - if let Some(author) = account_utils::miner_author(&cmd.spec, &cmd.dirs, &account_provider, engine_signer, &passwords)? { - miner.set_author(author); - } - } - - // display warning if using --no-hardcoded-sync - if cmd.no_hardcoded_sync { - warn!("The --no-hardcoded-sync flag has no effect if you don't use --light"); - } - - // create client config - let mut client_config = to_client_config( - &cmd.cache_config, - spec.name.to_lowercase(), - mode.clone(), - tracing, - fat_db, - cmd.compaction, - cmd.vm_type, - cmd.name, - algorithm, - cmd.pruning_history, - cmd.pruning_memory, - cmd.check_seal, - cmd.max_round_blocks_to_import, - ); - - client_config.queue.verifier_settings = cmd.verifier_settings; - client_config.transaction_verification_queue_size = ::std::cmp::max(2048, txpool_size / 4); - client_config.snapshot = cmd.snapshot_conf.clone(); - - // set up bootnodes - let mut net_conf = cmd.net_conf; - if !cmd.custom_bootnodes { - net_conf.boot_nodes = spec.nodes.clone(); - } - - // set network path. - net_conf.net_config_path = Some(db_dirs.network_path().to_string_lossy().into_owned()); - - let restoration_db_handler = db::restoration_db_handler(&client_path, &client_config); - let client_db = restoration_db_handler.open(&client_path) - .map_err(|e| format!("Failed to open database {:?}", e))?; - - let private_tx_signer = account_utils::private_tx_signer(account_provider.clone(), &passwords)?; - - // create client service. - let service = ClientService::start( - client_config, - &spec, - client_db, - &snapshot_path, - restoration_db_handler, - &cmd.dirs.ipc_path(), - miner.clone(), - private_tx_signer.clone(), - Box::new(SecretStoreEncryptor::new(cmd.private_encryptor_conf.clone(), fetch.clone(), private_tx_signer).map_err(|e| e.to_string())?), - cmd.private_provider_conf, - cmd.private_encryptor_conf, - ).map_err(|e| format!("Client service error: {:?}", e))?; - - let connection_filter_address = spec.params().node_permission_contract; - // drop the spec to free up genesis state. - drop(spec); - - // take handle to client - let client = service.client(); - // Update miners block gas limit - miner.update_transaction_queue_limits(*client.best_block_header().gas_limit()); - - // take handle to private transactions service - let private_tx_service = service.private_tx_service(); - let private_tx_provider = private_tx_service.provider(); - let connection_filter = connection_filter_address.map(|a| Arc::new(NodeFilter::new(Arc::downgrade(&client) as Weak, a))); - let snapshot_service = service.snapshot_service(); - - // initialize the local node information store. - let store = { - let db = service.db(); - let node_info = FullNodeInfo { - miner: match cmd.no_persistent_txqueue { - true => None, - false => Some(miner.clone()), - } - }; - - let store = ::local_store::create(db.key_value().clone(), ::ethcore_db::COL_NODE_INFO, node_info); - - if cmd.no_persistent_txqueue { - info!("Running without a persistent transaction queue."); - - if let Err(e) = store.clear() { - warn!("Error clearing persistent transaction queue: {}", e); - } - } - - // re-queue pending transactions. - match store.pending_transactions() { - Ok(pending) => { - for pending_tx in pending { - if let Err(e) = miner.import_own_transaction(&*client, pending_tx) { - warn!("Error importing saved transaction: {}", e) - } - } - } - Err(e) => warn!("Error loading cached pending transactions from disk: {}", e), - } - - Arc::new(store) - }; - - // register it as an IO service to update periodically. - service.register_io_handler(store).map_err(|_| "Unable to register local store handler".to_owned())?; - - // create external miner - let external_miner = Arc::new(ExternalMiner::default()); - - // start stratum - if let Some(ref stratum_config) = cmd.stratum { - stratum::Stratum::register(stratum_config, miner.clone(), Arc::downgrade(&client)) - .map_err(|e| format!("Stratum start error: {:?}", e))?; - } - - let mut attached_protos = Vec::new(); - - let whisper_factory = if cmd.whisper.enabled { - let whisper_factory = ::whisper::setup(cmd.whisper.target_message_pool_size, &mut attached_protos) - .map_err(|e| format!("Failed to initialize whisper: {}", e))?; - - whisper_factory - } else { - None - }; - - let private_tx_sync: Option> = match cmd.private_tx_enabled { - true => Some(private_tx_service.clone() as Arc), - false => None, - }; - - // create sync object - let (sync_provider, manage_network, chain_notify, priority_tasks) = modules::sync( - sync_config, - net_conf.clone().into(), - client.clone(), - snapshot_service.clone(), - private_tx_sync, - client.clone(), - &cmd.logger_config, - attached_protos, - connection_filter.clone().map(|f| f as Arc<::sync::ConnectionFilter + 'static>), - ).map_err(|e| format!("Sync error: {}", e))?; - - service.add_notify(chain_notify.clone()); - - // Propagate transactions as soon as they are imported. - let tx = ::parking_lot::Mutex::new(priority_tasks); - let is_ready = Arc::new(atomic::AtomicBool::new(true)); - miner.add_transactions_listener(Box::new(move |_hashes| { - // we want to have only one PendingTransactions task in the queue. - if is_ready.compare_and_swap(true, false, atomic::Ordering::SeqCst) { - let task = ::sync::PriorityTask::PropagateTransactions(Instant::now(), is_ready.clone()); - // we ignore error cause it means that we are closing - let _ = tx.lock().send(task); - } - })); - - // provider not added to a notification center is effectively disabled - // TODO [debris] refactor it later on - if cmd.private_tx_enabled { - service.add_notify(private_tx_provider.clone()); - // TODO [ToDr] PrivateTX should use separate notifications - // re-using ChainNotify for this is a bit abusive. - private_tx_provider.add_notify(chain_notify.clone()); - } - - // start network - if network_enabled { - chain_notify.start(); - } - - let contract_client = { - struct FullRegistrar { client: Arc } - impl RegistrarClient for FullRegistrar { - type Call = Asynchronous; - fn registrar_address(&self) -> Result { - self.client.registrar_address() - .ok_or_else(|| "Registrar not defined.".into()) - } - fn call_contract(&self, address: Address, data: Bytes) -> Self::Call { - Box::new(self.client.call_contract(BlockId::Latest, address, data).into_future()) - } - } - - Arc::new(FullRegistrar { client: client.clone() }) - }; - - // the updater service - let updater_fetch = fetch.clone(); - let updater = Updater::new( - &Arc::downgrade(&(service.client() as Arc)), - &Arc::downgrade(&sync_provider), - update_policy, - hash_fetch::Client::with_fetch(contract_client.clone(), updater_fetch, runtime.executor()) - ); - service.add_notify(updater.clone()); - - // set up dependencies for rpc servers - let rpc_stats = Arc::new(informant::RpcStats::default()); - let secret_store = account_provider.clone(); - let signer_service = Arc::new(signer::new_service(&cmd.ws_conf, &cmd.logger_config)); - - let deps_for_rpc_apis = Arc::new(rpc_apis::FullDependencies { - signer_service: signer_service, - snapshot: snapshot_service.clone(), - client: client.clone(), - sync: sync_provider.clone(), - net: manage_network.clone(), - accounts: secret_store, - miner: miner.clone(), - external_miner: external_miner.clone(), - logger: logger.clone(), - settings: Arc::new(cmd.net_settings.clone()), - net_service: manage_network.clone(), - updater: updater.clone(), - geth_compatibility: cmd.geth_compatibility, - experimental_rpcs: cmd.experimental_rpcs, - ws_address: cmd.ws_conf.address(), - fetch: fetch.clone(), - executor: runtime.executor(), - whisper_rpc: whisper_factory, - private_tx_service: Some(private_tx_service.clone()), - gas_price_percentile: cmd.gas_price_percentile, - poll_lifetime: cmd.poll_lifetime, - allow_missing_blocks: cmd.allow_missing_blocks, - }); - - let dependencies = rpc::Dependencies { - apis: deps_for_rpc_apis.clone(), - executor: runtime.executor(), - stats: rpc_stats.clone(), - }; - - // start rpc servers - let rpc_direct = rpc::setup_apis(rpc_apis::ApiSet::All, &dependencies); - let ws_server = rpc::new_ws(cmd.ws_conf.clone(), &dependencies)?; - let ipc_server = rpc::new_ipc(cmd.ipc_conf, &dependencies)?; - let http_server = rpc::new_http("HTTP JSON-RPC", "jsonrpc", cmd.http_conf.clone(), &dependencies)?; - - // secret store key server - let secretstore_deps = secretstore::Dependencies { - client: client.clone(), - sync: sync_provider.clone(), - miner: miner.clone(), - account_provider, - accounts_passwords: &passwords, - }; - let secretstore_key_server = secretstore::start(cmd.secretstore_conf.clone(), secretstore_deps, runtime.executor())?; - - // the ipfs server - let ipfs_server = ipfs::start_server(cmd.ipfs_conf.clone(), client.clone())?; - - // the informant - let informant = Arc::new(Informant::new( - FullNodeInformantData { - client: service.client(), - sync: Some(sync_provider.clone()), - net: Some(manage_network.clone()), - }, - Some(snapshot_service.clone()), - Some(rpc_stats.clone()), - cmd.logger_config.color, - )); - service.add_notify(informant.clone()); - service.register_io_handler(informant.clone()).map_err(|_| "Unable to register informant handler".to_owned())?; - - // save user defaults - user_defaults.is_first_launch = false; - user_defaults.pruning = algorithm; - user_defaults.tracing = tracing; - user_defaults.fat_db = fat_db; - user_defaults.set_mode(mode); - user_defaults.save(&user_defaults_path)?; - - // tell client how to save the default mode if it gets changed. - client.on_user_defaults_change(move |mode: Option| { - if let Some(mode) = mode { - user_defaults.set_mode(mode); - } - let _ = user_defaults.save(&user_defaults_path); // discard failures - there's nothing we can do - }); - - // the watcher must be kept alive. - let watcher = match cmd.snapshot_conf.no_periodic { - true => None, - false => { - let sync = sync_provider.clone(); - let client = client.clone(); - let watcher = Arc::new(snapshot::Watcher::new( - service.client(), - move || is_major_importing(Some(sync.status().state), client.queue_info()), - service.io().channel(), - SNAPSHOT_PERIOD, - SNAPSHOT_HISTORY, - )); - - service.add_notify(watcher.clone()); - Some(watcher) - }, - }; - - client.set_exit_handler(on_client_rq); - updater.set_exit_handler(on_updater_rq); - - Ok(RunningClient { - inner: RunningClientInner::Full { - rpc: rpc_direct, - informant, - client, - client_service: Arc::new(service), - keep_alive: Box::new((watcher, updater, ws_server, http_server, ipc_server, secretstore_key_server, ipfs_server, runtime)), - } - }) +/// Set bad blocks in VerificationQeueu. By omiting header we can omit particular fork of chain. +fn verification_bad_blocks(spec: &SpecType) -> Vec { + match *spec { + SpecType::Ropsten => { + vec!["1eac3d16c642411f13c287e29144c6f58fda859407c8f24c38deb168e1040714".into()] + } + _ => vec![], + } } /// Parity client currently executing in background threads. @@ -848,125 +611,121 @@ fn execute_impl(cmd: RunCmd, logger: Arc, on_client_rq: /// Should be destroyed by calling `shutdown()`, otherwise execution will continue in the /// background. pub struct RunningClient { - inner: RunningClientInner, + inner: RunningClientInner, } enum RunningClientInner { - Light { - rpc: jsonrpc_core::MetaIoHandler>, - informant: Arc>, - client: Arc, - keep_alive: Box, - }, - Full { - rpc: jsonrpc_core::MetaIoHandler>, - informant: Arc>, - client: Arc, - client_service: Arc, - keep_alive: Box, - }, + Full { + rpc: + jsonrpc_core::MetaIoHandler>, + informant: Arc>, + client: Arc, + client_service: Arc, + keep_alive: Box, + }, } impl RunningClient { - /// Performs an asynchronous RPC query. - // FIXME: [tomaka] This API should be better, with for example a Future - pub fn rpc_query(&self, request: &str, session: Option>) - -> FutureResult - { - let metadata = Metadata { - origin: Origin::CApi, - session, - }; - - match self.inner { - RunningClientInner::Light { ref rpc, .. } => rpc.handle_request(request, metadata), - RunningClientInner::Full { ref rpc, .. } => rpc.handle_request(request, metadata), - } - } - - /// Shuts down the client. - pub fn shutdown(self) { - match self.inner { - RunningClientInner::Light { rpc, informant, client, keep_alive } => { - // Create a weak reference to the client so that we can wait on shutdown - // until it is dropped - let weak_client = Arc::downgrade(&client); - drop(rpc); - drop(keep_alive); - informant.shutdown(); - drop(informant); - drop(client); - wait_for_drop(weak_client); - }, - RunningClientInner::Full { rpc, informant, client, client_service, keep_alive } => { - info!("Finishing work, please wait..."); - // Create a weak reference to the client so that we can wait on shutdown - // until it is dropped - let weak_client = Arc::downgrade(&client); - // Shutdown and drop the ServiceClient - client_service.shutdown(); - drop(client_service); - // drop this stuff as soon as exit detected. - drop(rpc); - drop(keep_alive); - // to make sure timer does not spawn requests while shutdown is in progress - informant.shutdown(); - // just Arc is dropping here, to allow other reference release in its default time - drop(informant); - drop(client); - wait_for_drop(weak_client); - } - } - } -} - -/// Executes the given run command. -/// -/// `on_client_rq` is the action to perform when the client receives an RPC request to be restarted -/// with a different chain. -/// -/// `on_updater_rq` is the action to perform when the updater has a new binary to execute. -/// -/// On error, returns what to print on stderr. -pub fn execute(cmd: RunCmd, logger: Arc, - on_client_rq: Cr, on_updater_rq: Rr) -> Result - where Cr: Fn(String) + 'static + Send, - Rr: Fn() + 'static + Send -{ - if cmd.light { - execute_light_impl(cmd, logger, on_client_rq) - } else { - execute_impl(cmd, logger, on_client_rq, on_updater_rq) - } + /// Performs an asynchronous RPC query. + // FIXME: [tomaka] This API should be better, with for example a Future + pub fn rpc_query( + &self, + request: &str, + session: Option>, + ) -> FutureResult { + let metadata = Metadata { + origin: Origin::CApi, + session, + }; + + match self.inner { + RunningClientInner::Full { ref rpc, .. } => rpc.handle_request(request, metadata), + } + } + + /// Shuts down the client. + pub fn shutdown(self) { + match self.inner { + RunningClientInner::Full { + rpc, + informant, + client, + client_service, + keep_alive, + } => { + info!("Finishing work, please wait..."); + // Create a weak reference to the client so that we can wait on shutdown + // until it is dropped + let weak_client = Arc::downgrade(&client); + // Shutdown and drop the ClientService + client_service.shutdown(); + trace!(target: "shutdown", "ClientService shut down"); + drop(client_service); + trace!(target: "shutdown", "ClientService dropped"); + // drop this stuff as soon as exit detected. + drop(rpc); + trace!(target: "shutdown", "RPC dropped"); + drop(keep_alive); + trace!(target: "shutdown", "KeepAlive dropped"); + // to make sure timer does not spawn requests while shutdown is in progress + informant.shutdown(); + trace!(target: "shutdown", "Informant shut down"); + // just Arc is dropping here, to allow other reference release in its default time + drop(informant); + trace!(target: "shutdown", "Informant dropped"); + drop(client); + trace!(target: "shutdown", "Client dropped"); + // This may help when debugging ref cycles. Requires nightly-only `#![feature(weak_counts)]` + // trace!(target: "shutdown", "Waiting for refs to Client to shutdown, strong_count={:?}, weak_count={:?}", weak_client.strong_count(), weak_client.weak_count()); + trace!(target: "shutdown", "Waiting for refs to Client to shutdown"); + wait_for_drop(weak_client); + } + } + } } fn print_running_environment(data_dir: &str, dirs: &Directories, db_dirs: &DatabaseDirectories) { - info!("Starting {}", Colour::White.bold().paint(version())); - info!("Keys path {}", Colour::White.bold().paint(dirs.keys_path(data_dir).to_string_lossy().into_owned())); - info!("DB path {}", Colour::White.bold().paint(db_dirs.db_root_path().to_string_lossy().into_owned())); + info!("Starting {}", Colour::White.bold().paint(version())); + info!( + "Keys path {}", + Colour::White + .bold() + .paint(dirs.keys_path(data_dir).to_string_lossy().into_owned()) + ); + info!( + "DB path {}", + Colour::White + .bold() + .paint(db_dirs.db_root_path().to_string_lossy().into_owned()) + ); } fn wait_for_drop(w: Weak) { - let sleep_duration = Duration::from_secs(1); - let warn_timeout = Duration::from_secs(60); - let max_timeout = Duration::from_secs(300); + const SLEEP_DURATION: Duration = Duration::from_secs(1); + const WARN_TIMEOUT: Duration = Duration::from_secs(60); + const MAX_TIMEOUT: Duration = Duration::from_secs(300); - let instant = Instant::now(); - let mut warned = false; + let instant = Instant::now(); + let mut warned = false; - while instant.elapsed() < max_timeout { - if w.upgrade().is_none() { - return; - } + while instant.elapsed() < MAX_TIMEOUT { + if w.upgrade().is_none() { + return; + } - if !warned && instant.elapsed() > warn_timeout { - warned = true; - warn!("Shutdown is taking longer than expected."); - } + if !warned && instant.elapsed() > WARN_TIMEOUT { + warned = true; + warn!("Shutdown is taking longer than expected."); + } - thread::sleep(sleep_duration); - } + thread::sleep(SLEEP_DURATION); - warn!("Shutdown timeout reached, exiting uncleanly."); -} + // When debugging shutdown issues on a nightly build it can help to enable this with the + // `#![feature(weak_counts)]` added to lib.rs (TODO: enable when + // https://github.com/rust-lang/rust/issues/57977 is stable) + // trace!(target: "shutdown", "Waiting for client to drop, strong_count={:?}, weak_count={:?}", w.strong_count(), w.weak_count()); + trace!(target: "shutdown", "Waiting for client to drop"); + } + warn!("Shutdown timeout reached, exiting uncleanly."); +} diff --git a/parity/secretstore.rs b/parity/secretstore.rs index d9075edec80..a55bfd90b05 100644 --- a/parity/secretstore.rs +++ b/parity/secretstore.rs @@ -1,249 +1,333 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::collections::BTreeMap; -use std::sync::Arc; use account_utils::AccountProvider; -use dir::default_data_path; -use dir::helpers::replace_home; -use ethcore::client::Client; -use ethcore::miner::Miner; -use ethkey::{Secret, Public, Password}; -use sync::SyncProvider; +use dir::{default_data_path, helpers::replace_home}; +use ethcore::{client::Client, miner::Miner}; use ethereum_types::Address; +use ethkey::{Password, Public, Secret}; use parity_runtime::Executor; +use std::{collections::BTreeMap, sync::Arc}; +use sync::SyncProvider; /// This node secret key. #[derive(Debug, PartialEq, Clone)] pub enum NodeSecretKey { - /// Stored as plain text in configuration file. - Plain(Secret), - /// Stored as account in key store. - #[cfg(feature = "accounts")] - KeyStore(Address), + /// Stored as plain text in configuration file. + Plain(Secret), + /// Stored as account in key store. + #[cfg(feature = "accounts")] + KeyStore(Address), } /// Secret store service contract address. #[derive(Debug, PartialEq, Clone)] pub enum ContractAddress { - /// Contract address is read from registry. - Registry, - /// Contract address is specified. - Address(Address), + /// Contract address is read from registry. + Registry, + /// Contract address is specified. + Address(Address), } #[derive(Debug, PartialEq, Clone)] /// Secret store configuration pub struct Configuration { - /// Is secret store functionality enabled? - pub enabled: bool, - /// Is HTTP API enabled? - pub http_enabled: bool, - /// Is auto migrate enabled. - pub auto_migrate_enabled: bool, - /// ACL check contract address. - pub acl_check_contract_address: Option, - /// Service contract address. - pub service_contract_address: Option, - /// Server key generation service contract address. - pub service_contract_srv_gen_address: Option, - /// Server key retrieval service contract address. - pub service_contract_srv_retr_address: Option, - /// Document key store service contract address. - pub service_contract_doc_store_address: Option, - /// Document key shadow retrieval service contract address. - pub service_contract_doc_sretr_address: Option, - /// This node secret. - pub self_secret: Option, - /// Other nodes IDs + addresses. - pub nodes: BTreeMap, - /// Key Server Set contract address. If None, 'nodes' map is used. - pub key_server_set_contract_address: Option, - /// Interface to listen to - pub interface: String, - /// Port to listen to - pub port: u16, - /// Interface to listen to - pub http_interface: String, - /// Port to listen to - pub http_port: u16, - /// Data directory path for secret store - pub data_path: String, - /// Administrator public key. - pub admin_public: Option, + /// Is secret store functionality enabled? + pub enabled: bool, + /// Is HTTP API enabled? + pub http_enabled: bool, + /// Is auto migrate enabled. + pub auto_migrate_enabled: bool, + /// ACL check contract address. + pub acl_check_contract_address: Option, + /// Service contract address. + pub service_contract_address: Option, + /// Server key generation service contract address. + pub service_contract_srv_gen_address: Option, + /// Server key retrieval service contract address. + pub service_contract_srv_retr_address: Option, + /// Document key store service contract address. + pub service_contract_doc_store_address: Option, + /// Document key shadow retrieval service contract address. + pub service_contract_doc_sretr_address: Option, + /// This node secret. + pub self_secret: Option, + /// Other nodes IDs + addresses. + pub nodes: BTreeMap, + /// Key Server Set contract address. If None, 'nodes' map is used. + pub key_server_set_contract_address: Option, + /// Interface to listen to + pub interface: String, + /// Port to listen to + pub port: u16, + /// Interface to listen to + pub http_interface: String, + /// Port to listen to + pub http_port: u16, + /// Data directory path for secret store + pub data_path: String, + /// Administrator public key. + pub admin_public: Option, } /// Secret store dependencies pub struct Dependencies<'a> { - /// Blockchain client. - pub client: Arc, - /// Sync provider. - pub sync: Arc, - /// Miner service. - pub miner: Arc, - /// Account provider. - pub account_provider: Arc, - /// Passed accounts passwords. - pub accounts_passwords: &'a [Password], + /// Blockchain client. + pub client: Arc, + /// Sync provider. + pub sync: Arc, + /// Miner service. + pub miner: Arc, + /// Account provider. + pub account_provider: Arc, + /// Passed accounts passwords. + pub accounts_passwords: &'a [Password], } #[cfg(not(feature = "secretstore"))] mod server { - use super::{Configuration, Dependencies, Executor}; + use super::{Configuration, Dependencies, Executor}; - /// Noop key server implementation - pub struct KeyServer; + /// Noop key server implementation + pub struct KeyServer; - impl KeyServer { - /// Create new noop key server - pub fn new(_conf: Configuration, _deps: Dependencies, _executor: Executor) -> Result { - Ok(KeyServer) - } - } + impl KeyServer { + /// Create new noop key server + pub fn new( + _conf: Configuration, + _deps: Dependencies, + _executor: Executor, + ) -> Result { + Ok(KeyServer) + } + } } #[cfg(feature = "secretstore")] mod server { - use std::sync::Arc; - use ethcore_secretstore; - use ethkey::KeyPair; - use ansi_term::Colour::{Red, White}; - use db; - use super::{Configuration, Dependencies, NodeSecretKey, ContractAddress, Executor}; - - fn into_service_contract_address(address: ContractAddress) -> ethcore_secretstore::ContractAddress { - match address { - ContractAddress::Registry => ethcore_secretstore::ContractAddress::Registry, - ContractAddress::Address(address) => ethcore_secretstore::ContractAddress::Address(address), - } - } - - /// Key server - pub struct KeyServer { - _key_server: Box, - } - - impl KeyServer { - /// Create new key server - pub fn new(mut conf: Configuration, deps: Dependencies, executor: Executor) -> Result { - let self_secret: Arc = match conf.self_secret.take() { - Some(NodeSecretKey::Plain(secret)) => Arc::new(ethcore_secretstore::PlainNodeKeyPair::new( - KeyPair::from_secret(secret).map_err(|e| format!("invalid secret: {}", e))?)), - #[cfg(feature = "accounts")] - Some(NodeSecretKey::KeyStore(account)) => { - // Check if account exists - if !deps.account_provider.has_account(account.clone()) { - return Err(format!("Account {} passed as secret store node key is not found", account)); - } - - // Check if any passwords have been read from the password file(s) - if deps.accounts_passwords.is_empty() { - return Err(format!("No password found for the secret store node account {}", account)); - } - - // Attempt to sign in the engine signer. - let password = deps.accounts_passwords.iter() - .find(|p| deps.account_provider.sign(account.clone(), Some((*p).clone()), Default::default()).is_ok()) - .ok_or_else(|| format!("No valid password for the secret store node account {}", account))?; - Arc::new(ethcore_secretstore::KeyStoreNodeKeyPair::new(deps.account_provider, account, password.clone()) - .map_err(|e| format!("{}", e))?) - }, - None => return Err("self secret is required when using secretstore".into()), - }; - - info!("Starting SecretStore node: {}", White.bold().paint(format!("{:?}", self_secret.public()))); - if conf.acl_check_contract_address.is_none() { - warn!("Running SecretStore with disabled ACL check: {}", Red.bold().paint("everyone has access to stored keys")); - } - - let key_server_name = format!("{}:{}", conf.interface, conf.port); - let mut cconf = ethcore_secretstore::ServiceConfiguration { - listener_address: if conf.http_enabled { Some(ethcore_secretstore::NodeAddress { - address: conf.http_interface.clone(), - port: conf.http_port, - }) } else { None }, - service_contract_address: conf.service_contract_address.map(into_service_contract_address), - service_contract_srv_gen_address: conf.service_contract_srv_gen_address.map(into_service_contract_address), - service_contract_srv_retr_address: conf.service_contract_srv_retr_address.map(into_service_contract_address), - service_contract_doc_store_address: conf.service_contract_doc_store_address.map(into_service_contract_address), - service_contract_doc_sretr_address: conf.service_contract_doc_sretr_address.map(into_service_contract_address), - acl_check_contract_address: conf.acl_check_contract_address.map(into_service_contract_address), - cluster_config: ethcore_secretstore::ClusterConfiguration { - listener_address: ethcore_secretstore::NodeAddress { - address: conf.interface.clone(), - port: conf.port, - }, - nodes: conf.nodes.into_iter().map(|(p, (ip, port))| (p, ethcore_secretstore::NodeAddress { - address: ip, - port: port, - })).collect(), - key_server_set_contract_address: conf.key_server_set_contract_address.map(into_service_contract_address), - allow_connecting_to_higher_nodes: true, - admin_public: conf.admin_public, - auto_migrate_enabled: conf.auto_migrate_enabled, - }, - }; - - cconf.cluster_config.nodes.insert(self_secret.public().clone(), cconf.cluster_config.listener_address.clone()); - - let db = db::open_secretstore_db(&conf.data_path)?; - let key_server = ethcore_secretstore::start(deps.client, deps.sync, deps.miner, self_secret, cconf, db, executor) - .map_err(|e| format!("Error starting KeyServer {}: {}", key_server_name, e))?; - - Ok(KeyServer { - _key_server: key_server, - }) - } - } + use super::{Configuration, ContractAddress, Dependencies, Executor, NodeSecretKey}; + use ansi_term::Colour::{Red, White}; + use db; + use ethcore_secretstore; + use ethkey::KeyPair; + use std::sync::Arc; + + fn into_service_contract_address( + address: ContractAddress, + ) -> ethcore_secretstore::ContractAddress { + match address { + ContractAddress::Registry => ethcore_secretstore::ContractAddress::Registry, + ContractAddress::Address(address) => { + ethcore_secretstore::ContractAddress::Address(address) + } + } + } + + /// Key server + pub struct KeyServer { + _key_server: Box, + } + + impl KeyServer { + /// Create new key server + pub fn new( + mut conf: Configuration, + deps: Dependencies, + executor: Executor, + ) -> Result { + let self_secret: Arc = + match conf.self_secret.take() { + Some(NodeSecretKey::Plain(secret)) => { + Arc::new(ethcore_secretstore::PlainNodeKeyPair::new( + KeyPair::from_secret(secret) + .map_err(|e| format!("invalid secret: {}", e))?, + )) + } + #[cfg(feature = "accounts")] + Some(NodeSecretKey::KeyStore(account)) => { + // Check if account exists + if !deps.account_provider.has_account(account.clone()) { + return Err(format!( + "Account {} passed as secret store node key is not found", + account + )); + } + + // Check if any passwords have been read from the password file(s) + if deps.accounts_passwords.is_empty() { + return Err(format!( + "No password found for the secret store node account {}", + account + )); + } + + // Attempt to sign in the engine signer. + let password = deps + .accounts_passwords + .iter() + .find(|p| { + deps.account_provider + .sign(account.clone(), Some((*p).clone()), Default::default()) + .is_ok() + }) + .ok_or_else(|| { + format!( + "No valid password for the secret store node account {}", + account + ) + })?; + Arc::new( + ethcore_secretstore::KeyStoreNodeKeyPair::new( + deps.account_provider, + account, + password.clone(), + ) + .map_err(|e| format!("{}", e))?, + ) + } + None => return Err("self secret is required when using secretstore".into()), + }; + + info!( + "Starting SecretStore node: {}", + White.bold().paint(format!("{:?}", self_secret.public())) + ); + if conf.acl_check_contract_address.is_none() { + warn!( + "Running SecretStore with disabled ACL check: {}", + Red.bold().paint("everyone has access to stored keys") + ); + } + + let key_server_name = format!("{}:{}", conf.interface, conf.port); + let mut cconf = ethcore_secretstore::ServiceConfiguration { + listener_address: if conf.http_enabled { + Some(ethcore_secretstore::NodeAddress { + address: conf.http_interface.clone(), + port: conf.http_port, + }) + } else { + None + }, + service_contract_address: conf + .service_contract_address + .map(into_service_contract_address), + service_contract_srv_gen_address: conf + .service_contract_srv_gen_address + .map(into_service_contract_address), + service_contract_srv_retr_address: conf + .service_contract_srv_retr_address + .map(into_service_contract_address), + service_contract_doc_store_address: conf + .service_contract_doc_store_address + .map(into_service_contract_address), + service_contract_doc_sretr_address: conf + .service_contract_doc_sretr_address + .map(into_service_contract_address), + acl_check_contract_address: conf + .acl_check_contract_address + .map(into_service_contract_address), + cluster_config: ethcore_secretstore::ClusterConfiguration { + listener_address: ethcore_secretstore::NodeAddress { + address: conf.interface.clone(), + port: conf.port, + }, + nodes: conf + .nodes + .into_iter() + .map(|(p, (ip, port))| { + ( + p, + ethcore_secretstore::NodeAddress { + address: ip, + port: port, + }, + ) + }) + .collect(), + key_server_set_contract_address: conf + .key_server_set_contract_address + .map(into_service_contract_address), + allow_connecting_to_higher_nodes: true, + admin_public: conf.admin_public, + auto_migrate_enabled: conf.auto_migrate_enabled, + }, + }; + + cconf.cluster_config.nodes.insert( + self_secret.public().clone(), + cconf.cluster_config.listener_address.clone(), + ); + + let db = db::open_secretstore_db(&conf.data_path)?; + let key_server = ethcore_secretstore::start( + deps.client, + deps.sync, + deps.miner, + self_secret, + cconf, + db, + executor, + ) + .map_err(|e| format!("Error starting KeyServer {}: {}", key_server_name, e))?; + + Ok(KeyServer { + _key_server: key_server, + }) + } + } } pub use self::server::KeyServer; impl Default for Configuration { - fn default() -> Self { - let data_dir = default_data_path(); - Configuration { - enabled: true, - http_enabled: true, - auto_migrate_enabled: true, - acl_check_contract_address: Some(ContractAddress::Registry), - service_contract_address: None, - service_contract_srv_gen_address: None, - service_contract_srv_retr_address: None, - service_contract_doc_store_address: None, - service_contract_doc_sretr_address: None, - self_secret: None, - admin_public: None, - nodes: BTreeMap::new(), - key_server_set_contract_address: Some(ContractAddress::Registry), - interface: "127.0.0.1".to_owned(), - port: 8083, - http_interface: "127.0.0.1".to_owned(), - http_port: 8082, - data_path: replace_home(&data_dir, "$BASE/secretstore"), - } - } + fn default() -> Self { + let data_dir = default_data_path(); + Configuration { + enabled: true, + http_enabled: true, + auto_migrate_enabled: true, + acl_check_contract_address: Some(ContractAddress::Registry), + service_contract_address: None, + service_contract_srv_gen_address: None, + service_contract_srv_retr_address: None, + service_contract_doc_store_address: None, + service_contract_doc_sretr_address: None, + self_secret: None, + admin_public: None, + nodes: BTreeMap::new(), + key_server_set_contract_address: Some(ContractAddress::Registry), + interface: "127.0.0.1".to_owned(), + port: 8083, + http_interface: "127.0.0.1".to_owned(), + http_port: 8082, + data_path: replace_home(&data_dir, "$BASE/secretstore"), + } + } } /// Start secret store-related functionality -pub fn start(conf: Configuration, deps: Dependencies, executor: Executor) -> Result, String> { - if !conf.enabled { - return Ok(None); - } +pub fn start( + conf: Configuration, + deps: Dependencies, + executor: Executor, +) -> Result, String> { + if !conf.enabled { + return Ok(None); + } - KeyServer::new(conf, deps, executor) - .map(|s| Some(s)) + KeyServer::new(conf, deps, executor).map(|s| Some(s)) } diff --git a/parity/signer.rs b/parity/signer.rs index 98f2b8cc6b6..7dd00f728fa 100644 --- a/parity/signer.rs +++ b/parity/signer.rs @@ -1,85 +1,100 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::io; -use std::path::{Path, PathBuf}; +use std::{ + io, + path::{Path, PathBuf}, +}; use ansi_term::Colour::White; use ethcore_logger::Config as LogConfig; -use rpc; -use rpc_apis; use parity_rpc; use path::restrict_permissions_owner; +use rpc; +use rpc_apis; pub const CODES_FILENAME: &'static str = "authcodes"; pub struct NewToken { - pub token: String, - pub message: String, + pub token: String, + pub message: String, } -pub fn new_service(ws_conf: &rpc::WsConfiguration, logger_config: &LogConfig) -> rpc_apis::SignerService { - let logger_config_color = logger_config.color; - let signer_path = ws_conf.signer_path.clone(); - let signer_enabled = ws_conf.support_token_api; +pub fn new_service( + ws_conf: &rpc::WsConfiguration, + logger_config: &LogConfig, +) -> rpc_apis::SignerService { + let logger_config_color = logger_config.color; + let signer_path = ws_conf.signer_path.clone(); + let signer_enabled = ws_conf.support_token_api; - rpc_apis::SignerService::new(move || { - generate_new_token(&signer_path, logger_config_color).map_err(|e| format!("{:?}", e)) - }, signer_enabled) + rpc_apis::SignerService::new( + move || { + generate_new_token(&signer_path, logger_config_color).map_err(|e| format!("{:?}", e)) + }, + signer_enabled, + ) } pub fn codes_path(path: &Path) -> PathBuf { - let mut p = path.to_owned(); - p.push(CODES_FILENAME); - let _ = restrict_permissions_owner(&p, true, false); - p + let mut p = path.to_owned(); + p.push(CODES_FILENAME); + let _ = restrict_permissions_owner(&p, true, false); + p } pub fn execute(ws_conf: rpc::WsConfiguration, logger_config: LogConfig) -> Result { - Ok(generate_token_and_url(&ws_conf, &logger_config)?.message) + Ok(generate_token_and_url(&ws_conf, &logger_config)?.message) } -pub fn generate_token_and_url(ws_conf: &rpc::WsConfiguration, logger_config: &LogConfig) -> Result { - let code = generate_new_token(&ws_conf.signer_path, logger_config.color).map_err(|err| format!("Error generating token: {:?}", err))?; - let colored = |s: String| match logger_config.color { - true => format!("{}", White.bold().paint(s)), - false => s, - }; +pub fn generate_token_and_url( + ws_conf: &rpc::WsConfiguration, + logger_config: &LogConfig, +) -> Result { + let code = generate_new_token(&ws_conf.signer_path, logger_config.color) + .map_err(|err| format!("Error generating token: {:?}", err))?; + let colored = |s: String| match logger_config.color { + true => format!("{}", White.bold().paint(s)), + false => s, + }; - Ok(NewToken { - token: code.clone(), - message: format!( - r#" + Ok(NewToken { + token: code.clone(), + message: format!( + r#" Generated token: {} "#, - colored(code) - ), - }) + colored(code) + ), + }) } fn generate_new_token(path: &Path, logger_config_color: bool) -> io::Result { - let path = codes_path(path); - let mut codes = parity_rpc::AuthCodes::from_file(&path)?; - codes.clear_garbage(); - let code = codes.generate_new()?; - codes.to_file(&path)?; - trace!("New key code created: {}", match logger_config_color { - true => format!("{}", White.bold().paint(&code[..])), - false => format!("{}", &code[..]) - }); - Ok(code) + let path = codes_path(path); + let mut codes = parity_rpc::AuthCodes::from_file(&path)?; + codes.clear_garbage(); + let code = codes.generate_new()?; + codes.to_file(&path)?; + trace!( + "New key code created: {}", + match logger_config_color { + true => format!("{}", White.bold().paint(&code[..])), + false => format!("{}", &code[..]), + } + ); + Ok(code) } diff --git a/parity/snapshot.rs b/parity/snapshot.rs index 70957762f5a..1cd4bbf9915 100644 --- a/parity/snapshot.rs +++ b/parity/snapshot.rs @@ -1,294 +1,346 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Snapshot and restoration commands. -use std::time::Duration; -use std::path::{Path, PathBuf}; -use std::sync::Arc; - -use hash::keccak; -use ethcore::snapshot::{Progress, RestorationStatus, SnapshotConfiguration, SnapshotService as SS}; -use ethcore::snapshot::io::{SnapshotReader, PackedReader, PackedWriter}; -use ethcore::snapshot::service::Service as SnapshotService; -use ethcore::client::{Mode, DatabaseCompactionProfile, VMType}; -use ethcore::miner::Miner; +use std::{ + path::{Path, PathBuf}, + sync::Arc, + time::Duration, +}; + +use ethcore::{ + client::{DatabaseCompactionProfile, Mode, VMType}, + miner::Miner, + snapshot::{ + io::{PackedReader, PackedWriter, SnapshotReader}, + service::Service as SnapshotService, + Progress, RestorationStatus, SnapshotConfiguration, SnapshotService as SS, + }, +}; use ethcore_service::ClientService; +use hash::keccak; use types::ids::BlockId; use cache::CacheConfig; -use params::{SpecType, Pruning, Switch, tracing_switch_to_bool, fatdb_switch_to_bool}; -use helpers::{to_client_config, execute_upgrades}; +use db; use dir::Directories; +use helpers::{execute_upgrades, to_client_config}; +use params::{fatdb_switch_to_bool, tracing_switch_to_bool, Pruning, SpecType, Switch}; use user_defaults::UserDefaults; -use ethcore_private_tx; -use db; /// Kinds of snapshot commands. #[derive(Debug, PartialEq, Clone, Copy)] pub enum Kind { - /// Take a snapshot. - Take, - /// Restore a snapshot. - Restore + /// Take a snapshot. + Take, + /// Restore a snapshot. + Restore, } /// Command for snapshot creation or restoration. #[derive(Debug, PartialEq)] pub struct SnapshotCommand { - pub cache_config: CacheConfig, - pub dirs: Directories, - pub spec: SpecType, - pub pruning: Pruning, - pub pruning_history: u64, - pub pruning_memory: usize, - pub tracing: Switch, - pub fat_db: Switch, - pub compaction: DatabaseCompactionProfile, - pub file_path: Option, - pub kind: Kind, - pub block_at: BlockId, - pub max_round_blocks_to_import: usize, - pub snapshot_conf: SnapshotConfiguration, + pub cache_config: CacheConfig, + pub dirs: Directories, + pub spec: SpecType, + pub pruning: Pruning, + pub pruning_history: u64, + pub pruning_memory: usize, + pub tracing: Switch, + pub fat_db: Switch, + pub compaction: DatabaseCompactionProfile, + pub file_path: Option, + pub kind: Kind, + pub block_at: BlockId, + pub max_round_blocks_to_import: usize, + pub snapshot_conf: SnapshotConfiguration, } // helper for reading chunks from arbitrary reader and feeding them into the // service. -fn restore_using(snapshot: Arc, reader: &R, recover: bool) -> Result<(), String> { - let manifest = reader.manifest(); - - info!("Restoring to block #{} (0x{:?})", manifest.block_number, manifest.block_hash); - - snapshot.init_restore(manifest.clone(), recover).map_err(|e| { - format!("Failed to begin restoration: {}", e) - })?; - - let (num_state, num_blocks) = (manifest.state_hashes.len(), manifest.block_hashes.len()); - - let informant_handle = snapshot.clone(); - ::std::thread::spawn(move || { - while let RestorationStatus::Ongoing { state_chunks_done, block_chunks_done, .. } = informant_handle.status() { - info!("Processed {}/{} state chunks and {}/{} block chunks.", - state_chunks_done, num_state, block_chunks_done, num_blocks); - ::std::thread::sleep(Duration::from_secs(5)); - } - }); - - info!("Restoring state"); - for &state_hash in &manifest.state_hashes { - if snapshot.status() == RestorationStatus::Failed { - return Err("Restoration failed".into()); - } - - let chunk = reader.chunk(state_hash) - .map_err(|e| format!("Encountered error while reading chunk {:?}: {}", state_hash, e))?; - - let hash = keccak(&chunk); - if hash != state_hash { - return Err(format!("Mismatched chunk hash. Expected {:?}, got {:?}", state_hash, hash)); - } - - snapshot.feed_state_chunk(state_hash, &chunk); - } - - info!("Restoring blocks"); - for &block_hash in &manifest.block_hashes { - if snapshot.status() == RestorationStatus::Failed { - return Err("Restoration failed".into()); - } - - let chunk = reader.chunk(block_hash) - .map_err(|e| format!("Encountered error while reading chunk {:?}: {}", block_hash, e))?; - - let hash = keccak(&chunk); - if hash != block_hash { - return Err(format!("Mismatched chunk hash. Expected {:?}, got {:?}", block_hash, hash)); - } - snapshot.feed_block_chunk(block_hash, &chunk); - } - - match snapshot.status() { - RestorationStatus::Ongoing { .. } => Err("Snapshot file is incomplete and missing chunks.".into()), - RestorationStatus::Initializing { .. } => Err("Snapshot restoration is still initializing.".into()), - RestorationStatus::Failed => Err("Snapshot restoration failed.".into()), - RestorationStatus::Inactive => { - info!("Restoration complete."); - Ok(()) - } - } +fn restore_using( + snapshot: Arc, + reader: &R, + recover: bool, +) -> Result<(), String> { + let manifest = reader.manifest(); + + info!( + "Restoring to block #{} (0x{:?})", + manifest.block_number, manifest.block_hash + ); + + snapshot + .init_restore(manifest.clone(), recover) + .map_err(|e| format!("Failed to begin restoration: {}", e))?; + + let (num_state, num_blocks) = (manifest.state_hashes.len(), manifest.block_hashes.len()); + + let informant_handle = snapshot.clone(); + ::std::thread::spawn(move || { + while let RestorationStatus::Ongoing { + state_chunks_done, + block_chunks_done, + .. + } = informant_handle.restoration_status() + { + info!( + "Processed {}/{} state chunks and {}/{} block chunks.", + state_chunks_done, num_state, block_chunks_done, num_blocks + ); + ::std::thread::sleep(Duration::from_secs(5)); + } + }); + + info!("Restoring state"); + for &state_hash in &manifest.state_hashes { + if snapshot.restoration_status() == RestorationStatus::Failed { + return Err("Restoration failed".into()); + } + + let chunk = reader.chunk(state_hash).map_err(|e| { + format!( + "Encountered error while reading chunk {:?}: {}", + state_hash, e + ) + })?; + + let hash = keccak(&chunk); + if hash != state_hash { + return Err(format!( + "Mismatched chunk hash. Expected {:?}, got {:?}", + state_hash, hash + )); + } + + snapshot.feed_state_chunk(state_hash, &chunk); + } + + info!("Restoring blocks"); + for &block_hash in &manifest.block_hashes { + if snapshot.restoration_status() == RestorationStatus::Failed { + return Err("Restoration failed".into()); + } + + let chunk = reader.chunk(block_hash).map_err(|e| { + format!( + "Encountered error while reading chunk {:?}: {}", + block_hash, e + ) + })?; + + let hash = keccak(&chunk); + if hash != block_hash { + return Err(format!( + "Mismatched chunk hash. Expected {:?}, got {:?}", + block_hash, hash + )); + } + snapshot.feed_block_chunk(block_hash, &chunk); + } + + match snapshot.restoration_status() { + RestorationStatus::Ongoing { .. } => { + Err("Snapshot file is incomplete and missing chunks.".into()) + } + RestorationStatus::Initializing { .. } => { + Err("Snapshot restoration is still initializing.".into()) + } + RestorationStatus::Failed => Err("Snapshot restoration failed.".into()), + RestorationStatus::Inactive => { + info!("Restoration complete."); + Ok(()) + } + } } impl SnapshotCommand { - // shared portion of snapshot commands: start the client service - fn start_service(self) -> Result { - // load spec file - let spec = self.spec.spec(&self.dirs.cache)?; - - // load genesis hash - let genesis_hash = spec.genesis_header().hash(); - - // database paths - let db_dirs = self.dirs.database(genesis_hash, None, spec.data_dir.clone()); - - // user defaults path - let user_defaults_path = db_dirs.user_defaults_path(); - - // load user defaults - let user_defaults = UserDefaults::load(&user_defaults_path)?; - - // select pruning algorithm - let algorithm = self.pruning.to_algorithm(&user_defaults); - - // check if tracing is on - let tracing = tracing_switch_to_bool(self.tracing, &user_defaults)?; - - // check if fatdb is on - let fat_db = fatdb_switch_to_bool(self.fat_db, &user_defaults, algorithm)?; - - // prepare client and snapshot paths. - let client_path = db_dirs.client_path(algorithm); - let snapshot_path = db_dirs.snapshot_path(); - - // execute upgrades - execute_upgrades(&self.dirs.base, &db_dirs, algorithm, &self.compaction)?; - - // prepare client config - let mut client_config = to_client_config( - &self.cache_config, - spec.name.to_lowercase(), - Mode::Active, - tracing, - fat_db, - self.compaction, - VMType::default(), - "".into(), - algorithm, - self.pruning_history, - self.pruning_memory, - true, - self.max_round_blocks_to_import, - ); - - client_config.snapshot = self.snapshot_conf; - - let restoration_db_handler = db::restoration_db_handler(&client_path, &client_config); - let client_db = restoration_db_handler.open(&client_path) - .map_err(|e| format!("Failed to open database {:?}", e))?; - - let service = ClientService::start( - client_config, - &spec, - client_db, - &snapshot_path, - restoration_db_handler, - &self.dirs.ipc_path(), - // TODO [ToDr] don't use test miner here - // (actually don't require miner at all) - Arc::new(Miner::new_for_tests(&spec, None)), - Arc::new(ethcore_private_tx::DummySigner), - Box::new(ethcore_private_tx::NoopEncryptor), - Default::default(), - Default::default(), - ).map_err(|e| format!("Client service error: {:?}", e))?; - - Ok(service) - } - /// restore from a snapshot - pub fn restore(self) -> Result<(), String> { - let file = self.file_path.clone(); - let service = self.start_service()?; - - warn!("Snapshot restoration is experimental and the format may be subject to change."); - warn!("On encountering an unexpected error, please ensure that you have a recent snapshot."); - - let snapshot = service.snapshot_service(); - - if let Some(file) = file { - info!("Attempting to restore from snapshot at '{}'", file); - - let reader = PackedReader::new(Path::new(&file)) - .map_err(|e| format!("Couldn't open snapshot file: {}", e)) - .and_then(|x| x.ok_or("Snapshot file has invalid format.".into())); - - let reader = reader?; - restore_using(snapshot, &reader, true)?; - } else { - info!("Attempting to restore from local snapshot."); - - // attempting restoration with recovery will lead to deadlock - // as we currently hold a read lock on the service's reader. - match *snapshot.reader() { - Some(ref reader) => restore_using(snapshot.clone(), reader, false)?, - None => return Err("No local snapshot found.".into()), - } - } - - Ok(()) - } - - /// Take a snapshot from the head of the chain. - pub fn take_snapshot(self) -> Result<(), String> { - let file_path = self.file_path.clone().ok_or("No file path provided.".to_owned())?; - let file_path: PathBuf = file_path.into(); - let block_at = self.block_at; - let service = self.start_service()?; - - warn!("Snapshots are currently experimental. File formats may be subject to change."); - - let writer = PackedWriter::new(&file_path) - .map_err(|e| format!("Failed to open snapshot writer: {}", e))?; - - let progress = Arc::new(Progress::default()); - let p = progress.clone(); - let informant_handle = ::std::thread::spawn(move || { - ::std::thread::sleep(Duration::from_secs(5)); - - let mut last_size = 0; - while !p.done() { - let cur_size = p.size(); - if cur_size != last_size { - last_size = cur_size; - let bytes = ::informant::format_bytes(p.size()); - info!("Snapshot: {} accounts {} blocks {}", p.accounts(), p.blocks(), bytes); - } - - ::std::thread::sleep(Duration::from_secs(5)); - } - }); - - if let Err(e) = service.client().take_snapshot(writer, block_at, &*progress) { - let _ = ::std::fs::remove_file(&file_path); - return Err(format!("Encountered fatal error while creating snapshot: {}", e)); - } - - info!("snapshot creation complete"); - - assert!(progress.done()); - informant_handle.join().map_err(|_| "failed to join logger thread")?; - - Ok(()) - } + // shared portion of snapshot commands: start the client service + fn start_service(self) -> Result { + // load spec file + let spec = self.spec.spec(&self.dirs.cache)?; + + // load genesis hash + let genesis_hash = spec.genesis_header().hash(); + + // database paths + let db_dirs = self + .dirs + .database(genesis_hash, None, spec.data_dir.clone()); + + // user defaults path + let user_defaults_path = db_dirs.user_defaults_path(); + + // load user defaults + let user_defaults = UserDefaults::load(&user_defaults_path)?; + + // select pruning algorithm + let algorithm = self.pruning.to_algorithm(&user_defaults); + + // check if tracing is on + let tracing = tracing_switch_to_bool(self.tracing, &user_defaults)?; + + // check if fatdb is on + let fat_db = fatdb_switch_to_bool(self.fat_db, &user_defaults, algorithm)?; + + // prepare client and snapshot paths. + let client_path = db_dirs.client_path(algorithm); + let snapshot_path = db_dirs.snapshot_path(); + + // execute upgrades + execute_upgrades(&self.dirs.base, &db_dirs, algorithm, &self.compaction)?; + + // prepare client config + let mut client_config = to_client_config( + &self.cache_config, + spec.name.to_lowercase(), + Mode::Active, + tracing, + fat_db, + self.compaction, + VMType::default(), + "".into(), + algorithm, + self.pruning_history, + self.pruning_memory, + true, + self.max_round_blocks_to_import, + ); + + client_config.snapshot = self.snapshot_conf; + + let restoration_db_handler = db::restoration_db_handler(&client_path, &client_config); + let client_db = restoration_db_handler + .open(&client_path) + .map_err(|e| format!("Failed to open database {:?}", e))?; + + let service = ClientService::start( + client_config, + &spec, + client_db, + &snapshot_path, + restoration_db_handler, + &self.dirs.ipc_path(), + // TODO [ToDr] don't use test miner here + // (actually don't require miner at all) + Arc::new(Miner::new_for_tests(&spec, None)), + ) + .map_err(|e| format!("Client service error: {:?}", e))?; + + Ok(service) + } + /// restore from a snapshot + pub fn restore(self) -> Result<(), String> { + let file = self.file_path.clone(); + let service = self.start_service()?; + + warn!("Snapshot restoration is experimental and the format may be subject to change."); + warn!( + "On encountering an unexpected error, please ensure that you have a recent snapshot." + ); + + let snapshot = service.snapshot_service(); + + if let Some(file) = file { + info!("Attempting to restore from snapshot at '{}'", file); + + let reader = PackedReader::new(Path::new(&file)) + .map_err(|e| format!("Couldn't open snapshot file: {}", e)) + .and_then(|x| x.ok_or("Snapshot file has invalid format.".into())); + + let reader = reader?; + restore_using(snapshot, &reader, true)?; + } else { + info!("Attempting to restore from local snapshot."); + + // attempting restoration with recovery will lead to deadlock + // as we currently hold a read lock on the service's reader. + match *snapshot.reader() { + Some(ref reader) => restore_using(snapshot.clone(), reader, false)?, + None => return Err("No local snapshot found.".into()), + } + } + + Ok(()) + } + + /// Take a snapshot from the head of the chain. + pub fn take_snapshot(self) -> Result<(), String> { + let file_path = self + .file_path + .clone() + .ok_or("No file path provided.".to_owned())?; + let file_path: PathBuf = file_path.into(); + let block_at = self.block_at; + let service = self.start_service()?; + + warn!("Snapshots are currently experimental. File formats may be subject to change."); + + let writer = PackedWriter::new(&file_path) + .map_err(|e| format!("Failed to open snapshot writer: {}", e))?; + + let progress = Arc::new(Progress::default()); + let p = progress.clone(); + let informant_handle = ::std::thread::spawn(move || { + ::std::thread::sleep(Duration::from_secs(5)); + + let mut last_size = 0; + while !p.done() { + let cur_size = p.size(); + if cur_size != last_size { + last_size = cur_size; + let bytes = ::informant::format_bytes(cur_size as usize); + info!( + "Snapshot: {} accounts {} blocks {}", + p.accounts(), + p.blocks(), + bytes + ); + } + + ::std::thread::sleep(Duration::from_secs(5)); + } + }); + + if let Err(e) = service.client().take_snapshot(writer, block_at, &*progress) { + let _ = ::std::fs::remove_file(&file_path); + return Err(format!( + "Encountered fatal error while creating snapshot: {}", + e + )); + } + + info!("snapshot creation complete"); + + assert!(progress.done()); + informant_handle + .join() + .map_err(|_| "failed to join logger thread")?; + + Ok(()) + } } /// Execute this snapshot command. pub fn execute(cmd: SnapshotCommand) -> Result { - match cmd.kind { - Kind::Take => cmd.take_snapshot()?, - Kind::Restore => cmd.restore()?, - } + match cmd.kind { + Kind::Take => cmd.take_snapshot()?, + Kind::Restore => cmd.restore()?, + } - Ok(String::new()) + Ok(String::new()) } diff --git a/parity/stratum.rs b/parity/stratum.rs index a74f60d852a..14e54aa908f 100644 --- a/parity/stratum.rs +++ b/parity/stratum.rs @@ -1,20 +1,20 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -//! Parity sync service +//! OpenEthereum sync service use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; diff --git a/parity/upgrade.rs b/parity/upgrade.rs index ecd9beff16d..c1a38d2c264 100644 --- a/parity/upgrade.rs +++ b/parity/upgrade.rs @@ -1,217 +1,246 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Parity upgrade logic -use semver::{Version, SemVerError}; -use std::collections::*; -use std::fs::{self, File, create_dir_all}; -use std::io; -use std::io::{Read, Write}; -use std::path::{PathBuf, Path}; -use dir::{DatabaseDirectories, default_data_path, home_dir}; -use dir::helpers::replace_home; +use dir::{default_data_path, helpers::replace_home, home_dir, DatabaseDirectories}; use journaldb::Algorithm; +use semver::{SemVerError, Version}; +use std::{ + collections::*, + fs::{self, create_dir_all, File}, + io, + io::{Read, Write}, + path::{Path, PathBuf}, +}; #[derive(Debug)] pub enum Error { - CannotCreateConfigPath(io::Error), - CannotWriteVersionFile(io::Error), - CannotUpdateVersionFile(io::Error), - SemVer(SemVerError), + CannotCreateConfigPath(io::Error), + CannotWriteVersionFile(io::Error), + CannotUpdateVersionFile(io::Error), + SemVer(SemVerError), } impl From for Error { - fn from(err: SemVerError) -> Self { - Error::SemVer(err) - } + fn from(err: SemVerError) -> Self { + Error::SemVer(err) + } } const CURRENT_VERSION: &'static str = env!("CARGO_PKG_VERSION"); #[derive(Hash, PartialEq, Eq)] struct UpgradeKey { - pub old_version: Version, - pub new_version: Version, + pub old_version: Version, + pub new_version: Version, } type UpgradeList = HashMap Result<(), Error>>; impl UpgradeKey { - // given the following config exist - // ver.lock 1.1 (`previous_version`) - // - // current_version 1.4 (`current_version`) - // - // - //upgrades (set of `UpgradeKey`) - // 1.0 -> 1.1 (u1) - // 1.1 -> 1.2 (u2) - // 1.2 -> 1.3 (u3) - // 1.3 -> 1.4 (u4) - // 1.4 -> 1.5 (u5) - // - // then the following upgrades should be applied: - // u2, u3, u4 - fn is_applicable(&self, previous_version: &Version, current_version: &Version) -> bool { - self.old_version >= *previous_version && self.new_version <= *current_version - } + // given the following config exist + // ver.lock 1.1 (`previous_version`) + // + // current_version 1.4 (`current_version`) + // + // + //upgrades (set of `UpgradeKey`) + // 1.0 -> 1.1 (u1) + // 1.1 -> 1.2 (u2) + // 1.2 -> 1.3 (u3) + // 1.3 -> 1.4 (u4) + // 1.4 -> 1.5 (u5) + // + // then the following upgrades should be applied: + // u2, u3, u4 + fn is_applicable(&self, previous_version: &Version, current_version: &Version) -> bool { + self.old_version >= *previous_version && self.new_version <= *current_version + } } // dummy upgrade (remove when the first one is in) fn dummy_upgrade() -> Result<(), Error> { - Ok(()) + Ok(()) } -fn push_upgrades(upgrades: &mut UpgradeList) -{ - // dummy upgrade (remove when the first one is in) - upgrades.insert( - UpgradeKey { old_version: Version::new(0, 9, 0), new_version: Version::new(1, 0, 0)}, - dummy_upgrade); +fn push_upgrades(upgrades: &mut UpgradeList) { + // dummy upgrade (remove when the first one is in) + upgrades.insert( + UpgradeKey { + old_version: Version::new(0, 9, 0), + new_version: Version::new(1, 0, 0), + }, + dummy_upgrade, + ); } fn upgrade_from_version(previous_version: &Version) -> Result { - let mut upgrades = HashMap::new(); - push_upgrades(&mut upgrades); - - let current_version = Version::parse(CURRENT_VERSION)?; - - let mut count = 0; - for upgrade_key in upgrades.keys() { - if upgrade_key.is_applicable(previous_version, ¤t_version) { - let upgrade_script = upgrades[upgrade_key]; - upgrade_script()?; - count += 1; - } - } - Ok(count) + let mut upgrades = HashMap::new(); + push_upgrades(&mut upgrades); + + let current_version = Version::parse(CURRENT_VERSION)?; + + let mut count = 0; + for upgrade_key in upgrades.keys() { + if upgrade_key.is_applicable(previous_version, ¤t_version) { + let upgrade_script = upgrades[upgrade_key]; + upgrade_script()?; + count += 1; + } + } + Ok(count) } fn with_locked_version(db_path: &str, script: F) -> Result - where F: Fn(&Version) -> Result +where + F: Fn(&Version) -> Result, { - let mut path = PathBuf::from(db_path); - create_dir_all(&path).map_err(Error::CannotCreateConfigPath)?; - path.push("ver.lock"); - - let version = - File::open(&path).ok().and_then(|ref mut file| - { - let mut version_string = String::new(); - file.read_to_string(&mut version_string) - .ok() - .and_then(|_| Version::parse(&version_string).ok()) - }) - .unwrap_or(Version::new(0, 9, 0)); - - let mut lock = File::create(&path).map_err(Error::CannotWriteVersionFile)?; - let result = script(&version); - - let written_version = Version::parse(CURRENT_VERSION)?; - lock.write_all(written_version.to_string().as_bytes()).map_err(Error::CannotUpdateVersionFile)?; - result + let mut path = PathBuf::from(db_path); + create_dir_all(&path).map_err(Error::CannotCreateConfigPath)?; + path.push("ver.lock"); + + let version = File::open(&path) + .ok() + .and_then(|ref mut file| { + let mut version_string = String::new(); + file.read_to_string(&mut version_string) + .ok() + .and_then(|_| Version::parse(&version_string).ok()) + }) + .unwrap_or(Version::new(0, 9, 0)); + + let mut lock = File::create(&path).map_err(Error::CannotWriteVersionFile)?; + let result = script(&version); + + let written_version = Version::parse(CURRENT_VERSION)?; + lock.write_all(written_version.to_string().as_bytes()) + .map_err(Error::CannotUpdateVersionFile)?; + result } pub fn upgrade(db_path: &str) -> Result { - with_locked_version(db_path, |ver| { - upgrade_from_version(ver) - }) + with_locked_version(db_path, |ver| upgrade_from_version(ver)) } fn file_exists(path: &Path) -> bool { - match fs::metadata(&path) { - Err(ref e) if e.kind() == io::ErrorKind::NotFound => false, - _ => true, - } + match fs::metadata(&path) { + Err(ref e) if e.kind() == io::ErrorKind::NotFound => false, + _ => true, + } } #[cfg(any(test, feature = "accounts"))] pub fn upgrade_key_location(from: &PathBuf, to: &PathBuf) { - match fs::create_dir_all(&to).and_then(|()| fs::read_dir(from)) { - Ok(entries) => { - let files: Vec<_> = entries.filter_map(|f| f.ok().and_then(|f| if f.file_type().ok().map_or(false, |f| f.is_file()) { f.file_name().to_str().map(|s| s.to_owned()) } else { None })).collect(); - let mut num: usize = 0; - for name in files { - let mut from = from.clone(); - from.push(&name); - let mut to = to.clone(); - to.push(&name); - if !file_exists(&to) { - if let Err(e) = fs::rename(&from, &to) { - debug!("Error upgrading key {:?}: {:?}", from, e); - } else { - num += 1; - } - } else { - debug!("Skipped upgrading key {:?}", from); - } - } - if num > 0 { - info!("Moved {} keys from {} to {}", num, from.to_string_lossy(), to.to_string_lossy()); - } - }, - Err(e) => { - debug!("Error moving keys from {:?} to {:?}: {:?}", from, to, e); - } - } + match fs::create_dir_all(&to).and_then(|()| fs::read_dir(from)) { + Ok(entries) => { + let files: Vec<_> = entries + .filter_map(|f| { + f.ok().and_then(|f| { + if f.file_type().ok().map_or(false, |f| f.is_file()) { + f.file_name().to_str().map(|s| s.to_owned()) + } else { + None + } + }) + }) + .collect(); + let mut num: usize = 0; + for name in files { + let mut from = from.clone(); + from.push(&name); + let mut to = to.clone(); + to.push(&name); + if !file_exists(&to) { + if let Err(e) = fs::rename(&from, &to) { + debug!("Error upgrading key {:?}: {:?}", from, e); + } else { + num += 1; + } + } else { + debug!("Skipped upgrading key {:?}", from); + } + } + if num > 0 { + info!( + "Moved {} keys from {} to {}", + num, + from.to_string_lossy(), + to.to_string_lossy() + ); + } + } + Err(e) => { + debug!("Error moving keys from {:?} to {:?}: {:?}", from, to, e); + } + } } fn upgrade_dir_location(source: &PathBuf, dest: &PathBuf) { - if file_exists(&source) { - if !file_exists(&dest) { - let mut parent = dest.clone(); - parent.pop(); - if let Err(e) = fs::create_dir_all(&parent).and_then(|()| fs::rename(&source, &dest)) { - debug!("Skipped path {:?} -> {:?} :{:?}", source, dest, e); - } else { - info!("Moved {} to {}", source.to_string_lossy(), dest.to_string_lossy()); - } - } else { - debug!("Skipped upgrading directory {:?}, Destination already exists at {:?}", source, dest); - } - } + if file_exists(&source) { + if !file_exists(&dest) { + let mut parent = dest.clone(); + parent.pop(); + if let Err(e) = fs::create_dir_all(&parent).and_then(|()| fs::rename(&source, &dest)) { + debug!("Skipped path {:?} -> {:?} :{:?}", source, dest, e); + } else { + info!( + "Moved {} to {}", + source.to_string_lossy(), + dest.to_string_lossy() + ); + } + } else { + debug!( + "Skipped upgrading directory {:?}, Destination already exists at {:?}", + source, dest + ); + } + } } fn upgrade_user_defaults(dirs: &DatabaseDirectories) { - let source = dirs.legacy_user_defaults_path(); - let dest = dirs.user_defaults_path(); - if file_exists(&source) { - if !file_exists(&dest) { - if let Err(e) = fs::rename(&source, &dest) { - debug!("Skipped upgrading user defaults {:?}:{:?}", dest, e); - } - } else { - debug!("Skipped upgrading user defaults {:?}, File exists at {:?}", source, dest); - } - } + let source = dirs.legacy_user_defaults_path(); + let dest = dirs.user_defaults_path(); + if file_exists(&source) { + if !file_exists(&dest) { + if let Err(e) = fs::rename(&source, &dest) { + debug!("Skipped upgrading user defaults {:?}:{:?}", dest, e); + } + } else { + debug!( + "Skipped upgrading user defaults {:?}, File exists at {:?}", + source, dest + ); + } + } } pub fn upgrade_data_paths(base_path: &str, dirs: &DatabaseDirectories, pruning: Algorithm) { - if home_dir().is_none() { - return; - } - - let legacy_root_path = replace_home("", "$HOME/.parity"); - let default_path = default_data_path(); - if legacy_root_path != base_path && base_path == default_path { - upgrade_dir_location(&PathBuf::from(legacy_root_path), &PathBuf::from(&base_path)); - } - upgrade_dir_location(&dirs.legacy_version_path(pruning), &dirs.db_path(pruning)); - upgrade_dir_location(&dirs.legacy_snapshot_path(), &dirs.snapshot_path()); - upgrade_dir_location(&dirs.legacy_network_path(), &dirs.network_path()); - upgrade_user_defaults(&dirs); + if home_dir().is_none() { + return; + } + + let legacy_root_path = replace_home("", "$HOME/.parity"); + let default_path = default_data_path(); + if legacy_root_path != base_path && base_path == default_path { + upgrade_dir_location(&PathBuf::from(legacy_root_path), &PathBuf::from(&base_path)); + } + upgrade_dir_location(&dirs.legacy_version_path(pruning), &dirs.db_path(pruning)); + upgrade_dir_location(&dirs.legacy_snapshot_path(), &dirs.snapshot_path()); + upgrade_dir_location(&dirs.legacy_network_path(), &dirs.network_path()); + upgrade_user_defaults(&dirs); } diff --git a/parity/user_defaults.rs b/parity/user_defaults.rs index bdd5d9efb33..95fa66a78ee 100644 --- a/parity/user_defaults.rs +++ b/parity/user_defaults.rs @@ -1,174 +1,188 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::fs::File; -use std::io::Write; -use std::path::Path; -use std::time::Duration; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use serde_json::de::from_reader; -use serde_json::ser::to_string; +use ethcore::client::Mode as ClientMode; use journaldb::Algorithm; -use ethcore::client::{Mode as ClientMode}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde_json::{de::from_reader, ser::to_string}; +use std::{fs::File, io::Write, path::Path, time::Duration}; #[derive(Clone)] pub struct Seconds(Duration); impl Seconds { - pub fn value(&self) -> u64 { - self.0.as_secs() - } + pub fn value(&self) -> u64 { + self.0.as_secs() + } } impl From for Seconds { - fn from(s: u64) -> Seconds { - Seconds(Duration::from_secs(s)) - } + fn from(s: u64) -> Seconds { + Seconds(Duration::from_secs(s)) + } } impl From for Seconds { - fn from(d: Duration) -> Seconds { - Seconds(d) - } + fn from(d: Duration) -> Seconds { + Seconds(d) + } } impl Into for Seconds { - fn into(self) -> Duration { - self.0 - } + fn into(self) -> Duration { + self.0 + } } impl Serialize for Seconds { - fn serialize(&self, serializer: S) -> Result { - serializer.serialize_u64(self.value()) - } + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_u64(self.value()) + } } impl<'de> Deserialize<'de> for Seconds { - fn deserialize>(deserializer: D) -> Result { - let secs = u64::deserialize(deserializer)?; - Ok(Seconds::from(secs)) - } + fn deserialize>(deserializer: D) -> Result { + let secs = u64::deserialize(deserializer)?; + Ok(Seconds::from(secs)) + } } #[derive(Clone, Serialize, Deserialize)] #[serde(rename_all = "lowercase", tag = "mode")] pub enum Mode { - Active, - Passive { - #[serde(rename = "mode.timeout")] - timeout: Seconds, - #[serde(rename = "mode.alarm")] - alarm: Seconds, - }, - Dark { - #[serde(rename = "mode.timeout")] - timeout: Seconds, - }, - Offline, + Active, + Passive { + #[serde(rename = "mode.timeout")] + timeout: Seconds, + #[serde(rename = "mode.alarm")] + alarm: Seconds, + }, + Dark { + #[serde(rename = "mode.timeout")] + timeout: Seconds, + }, + Offline, } impl Into for Mode { - fn into(self) -> ClientMode { - match self { - Mode::Active => ClientMode::Active, - Mode::Passive { timeout, alarm } => ClientMode::Passive(timeout.into(), alarm.into()), - Mode::Dark { timeout } => ClientMode::Dark(timeout.into()), - Mode::Offline => ClientMode::Off, - } - } + fn into(self) -> ClientMode { + match self { + Mode::Active => ClientMode::Active, + Mode::Passive { timeout, alarm } => ClientMode::Passive(timeout.into(), alarm.into()), + Mode::Dark { timeout } => ClientMode::Dark(timeout.into()), + Mode::Offline => ClientMode::Off, + } + } } impl From for Mode { - fn from(mode: ClientMode) -> Mode { - match mode { - ClientMode::Active => Mode::Active, - ClientMode::Passive(timeout, alarm) => Mode::Passive { timeout: timeout.into(), alarm: alarm.into() }, - ClientMode::Dark(timeout) => Mode::Dark { timeout: timeout.into() }, - ClientMode::Off => Mode::Offline, - } - } + fn from(mode: ClientMode) -> Mode { + match mode { + ClientMode::Active => Mode::Active, + ClientMode::Passive(timeout, alarm) => Mode::Passive { + timeout: timeout.into(), + alarm: alarm.into(), + }, + ClientMode::Dark(timeout) => Mode::Dark { + timeout: timeout.into(), + }, + ClientMode::Off => Mode::Offline, + } + } } #[derive(Serialize, Deserialize)] pub struct UserDefaults { - pub is_first_launch: bool, - #[serde(with = "algorithm_serde")] - pub pruning: Algorithm, - pub tracing: bool, - pub fat_db: bool, - #[serde(flatten)] - mode: Mode, + pub is_first_launch: bool, + #[serde(with = "algorithm_serde")] + pub pruning: Algorithm, + pub tracing: bool, + pub fat_db: bool, + #[serde(flatten)] + mode: Mode, } impl UserDefaults { - pub fn mode(&self) -> ClientMode { - self.mode.clone().into() - } + pub fn mode(&self) -> ClientMode { + self.mode.clone().into() + } - pub fn set_mode(&mut self, mode: ClientMode) { - self.mode = mode.into(); - } + pub fn set_mode(&mut self, mode: ClientMode) { + self.mode = mode.into(); + } } mod algorithm_serde { - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - use serde::de::Error; - use journaldb::Algorithm; - - pub fn serialize(algorithm: &Algorithm, serializer: S) -> Result - where S: Serializer { - algorithm.as_str().serialize(serializer) - } - - pub fn deserialize<'de, D>(deserializer: D) -> Result - where D: Deserializer<'de> { - let pruning = String::deserialize(deserializer)?; - pruning.parse().map_err(|_| Error::custom("invalid pruning method")) - } + use journaldb::Algorithm; + use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; + + pub fn serialize(algorithm: &Algorithm, serializer: S) -> Result + where + S: Serializer, + { + algorithm.as_str().serialize(serializer) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let pruning = String::deserialize(deserializer)?; + pruning + .parse() + .map_err(|_| Error::custom("invalid pruning method")) + } } impl Default for UserDefaults { - fn default() -> Self { - UserDefaults { - is_first_launch: true, - pruning: Algorithm::OverlayRecent, - tracing: false, - fat_db: false, - mode: Mode::Active, - } - } + fn default() -> Self { + UserDefaults { + is_first_launch: true, + pruning: Algorithm::OverlayRecent, + tracing: false, + fat_db: false, + mode: Mode::Active, + } + } } impl UserDefaults { - pub fn load

(path: P) -> Result where P: AsRef { - match File::open(path) { - Ok(file) => match from_reader(file) { - Ok(defaults) => Ok(defaults), - Err(e) => { - warn!("Error loading user defaults file: {:?}", e); - Ok(UserDefaults::default()) - }, - }, - _ => Ok(UserDefaults::default()), - } - } - - pub fn save

(&self, path: P) -> Result<(), String> where P: AsRef { - let mut file: File = File::create(path).map_err(|_| "Cannot create user defaults file".to_owned())?; - file.write_all(to_string(&self).unwrap().as_bytes()).map_err(|_| "Failed to save user defaults".to_owned()) - } + pub fn load

(path: P) -> Result + where + P: AsRef, + { + match File::open(path) { + Ok(file) => match from_reader(file) { + Ok(defaults) => Ok(defaults), + Err(e) => { + warn!("Error loading user defaults file: {:?}", e); + Ok(UserDefaults::default()) + } + }, + _ => Ok(UserDefaults::default()), + } + } + + pub fn save

(&self, path: P) -> Result<(), String> + where + P: AsRef, + { + let mut file: File = + File::create(path).map_err(|_| "Cannot create user defaults file".to_owned())?; + file.write_all(to_string(&self).unwrap().as_bytes()) + .map_err(|_| "Failed to save user defaults".to_owned()) + } } diff --git a/parity/whisper.rs b/parity/whisper.rs deleted file mode 100644 index e9a744b5469..00000000000 --- a/parity/whisper.rs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::io; - -use sync::{AttachedProtocol, ManageNetwork}; -use parity_rpc::Metadata; -use parity_whisper::message::Message; -use parity_whisper::net::{self as whisper_net, Network as WhisperNetwork}; -use parity_whisper::rpc::{WhisperClient, PoolHandle, FilterManager}; - -/// Whisper config. -#[derive(Debug, PartialEq, Eq)] -pub struct Config { - pub enabled: bool, - pub target_message_pool_size: usize, -} - -impl Default for Config { - fn default() -> Self { - Config { - enabled: false, - target_message_pool_size: 10 * 1024 * 1024, - } - } -} - -/// Standard pool handle. -pub struct NetPoolHandle { - /// Pool handle. - handle: Arc>>, - /// Network manager. - net: Arc, -} - -impl PoolHandle for NetPoolHandle { - fn relay(&self, message: Message) -> bool { - let mut res = false; - let mut message = Some(message); - self.net.with_proto_context(whisper_net::PROTOCOL_ID, &mut |ctx| { - if let Some(message) = message.take() { - res = self.handle.post_message(message, ctx); - } - }); - res - } - - fn pool_status(&self) -> whisper_net::PoolStatus { - self.handle.pool_status() - } -} - -/// Factory for standard whisper RPC. -pub struct RpcFactory { - net: Arc>>, - manager: Arc, -} - -impl RpcFactory { - pub fn make_handler(&self, net: Arc) -> WhisperClient { - let handle = NetPoolHandle { handle: self.net.clone(), net: net }; - WhisperClient::new(handle, self.manager.clone()) - } -} - -/// Sets up whisper protocol and RPC handler. -/// -/// Will target the given pool size. -#[cfg(not(feature = "ipc"))] -pub fn setup(target_pool_size: usize, protos: &mut Vec) - -> io::Result> -{ - let manager = Arc::new(FilterManager::new()?); - let net = Arc::new(WhisperNetwork::new(target_pool_size, manager.clone())); - - protos.push(AttachedProtocol { - handler: net.clone() as Arc<_>, - versions: whisper_net::SUPPORTED_VERSIONS, - protocol_id: whisper_net::PROTOCOL_ID, - }); - - // parity-only extensions to whisper. - protos.push(AttachedProtocol { - handler: Arc::new(whisper_net::ParityExtensions), - versions: whisper_net::SUPPORTED_VERSIONS, - protocol_id: whisper_net::PARITY_PROTOCOL_ID, - }); - - let factory = RpcFactory { net: net, manager: manager }; - - Ok(Some(factory)) -} - -// TODO: make it possible to attach generic protocols in IPC. -#[cfg(feature = "ipc")] -pub fn setup(_target_pool_size: usize, _protos: &mut Vec) - -> io::Result> -{ - Ok(None) -} diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 33559a55954..ae4215f01a0 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -1,5 +1,5 @@ [package] -description = "Parity JSON-RPC servers." +description = "Parity Ethereum JSON-RPC Servers (WS, HTTP, IPC)" name = "parity-rpc" version = "1.12.0" license = "GPL-3.0" @@ -9,15 +9,12 @@ authors = ["Parity Technologies "] [dependencies] ansi_term = "0.10" -cid = "0.3" futures = "0.1.6" log = "0.4" -multihash = "0.8" order-stat = "0.1" parking_lot = "0.7" rand = "0.4" rustc-hex = "1.0" -semver = "0.9" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" @@ -27,25 +24,22 @@ tokio-timer = "0.1" transient-hashmap = "0.4" itertools = "0.5" -jsonrpc-core = "10.0.1" -jsonrpc-derive = "10.0.2" -jsonrpc-http-server = "10.0.1" -jsonrpc-ws-server = "10.0.1" -jsonrpc-ipc-server = "10.0.1" -jsonrpc-pubsub = "10.0.1" +jsonrpc-core = "15.0.0" +jsonrpc-derive = "15.0.0" +jsonrpc-http-server = "15.0.0" +jsonrpc-ws-server = "15.0.0" +jsonrpc-ipc-server = "15.0.0" +jsonrpc-pubsub = "15.0.0" common-types = { path = "../ethcore/types" } ethash = { path = "../ethash" } -ethcore = { path = "../ethcore", features = ["test-helpers"] } +ethcore = { path = "../ethcore" } ethcore-accounts = { path = "../accounts", optional = true } -ethcore-light = { path = "../ethcore/light" } ethcore-logger = { path = "../parity/logger" } ethcore-miner = { path = "../miner" } ethcore-network = { path = "../util/network" } -ethcore-private-tx = { path = "../ethcore/private-tx" } ethcore-sync = { path = "../ethcore/sync" } ethereum-types = "0.4" -fastmap = { path = "../util/fastmap" } parity-bytes = "0.1" parity-crypto = "0.3.0" @@ -56,7 +50,6 @@ ethstore = { path = "../accounts/ethstore" } fetch = { path = "../util/fetch" } keccak-hash = "0.1.2" parity-runtime = { path = "../util/runtime" } -parity-updater = { path = "../updater" } parity-version = { path = "../util/version" } rlp = { version = "0.3.0", features = ["ethereum"] } stats = { path = "../util/stats" } @@ -70,7 +63,7 @@ ethcore-network = { path = "../util/network" } fake-fetch = { path = "../util/fake-fetch" } macros = { path = "../util/macros" } pretty_assertions = "0.1" -transaction-pool = "2.0" +transaction-pool = "2.0.1" [features] accounts = ["ethcore-accounts"] diff --git a/rpc/src/authcodes.rs b/rpc/src/authcodes.rs index b348dfd729d..12ec3dbb125 100644 --- a/rpc/src/authcodes.rs +++ b/rpc/src/authcodes.rs @@ -1,39 +1,42 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::io::{self, Read, Write}; -use std::path::Path; -use std::{fs, time, mem}; +use std::{ + fs, + io::{self, Read, Write}, + mem, + path::Path, + time, +}; -use itertools::Itertools; -use rand::Rng; -use rand::os::OsRng; -use hash::keccak; use ethereum_types::H256; +use hash::keccak; +use itertools::Itertools; +use rand::{os::OsRng, Rng}; /// Providing current time in seconds pub trait TimeProvider { - /// Returns timestamp (in seconds since epoch) - fn now(&self) -> u64; + /// Returns timestamp (in seconds since epoch) + fn now(&self) -> u64; } -impl u64> TimeProvider for F { - fn now(&self) -> u64 { - self() - } +impl u64> TimeProvider for F { + fn now(&self) -> u64 { + self() + } } /// Default implementation of `TimeProvider` using system time. @@ -41,9 +44,12 @@ impl u64> TimeProvider for F { pub struct DefaultTimeProvider; impl TimeProvider for DefaultTimeProvider { - fn now(&self) -> u64 { - time::UNIX_EPOCH.elapsed().expect("Valid time has to be set in your system.").as_secs() - } + fn now(&self) -> u64 { + time::UNIX_EPOCH + .elapsed() + .expect("Valid time has to be set in your system.") + .as_secs() + } } /// No of seconds the hash is valid @@ -56,285 +62,303 @@ const SEPARATOR: &str = ";"; const UNUSED_TOKEN_TIMEOUT: u64 = 3600 * 24; // a day struct Code { - code: String, - /// Duration since unix_epoch - created_at: time::Duration, - /// Duration since unix_epoch - last_used_at: Option, + code: String, + /// Duration since unix_epoch + created_at: time::Duration, + /// Duration since unix_epoch + last_used_at: Option, } fn decode_time(val: &str) -> Option { - let time = val.parse::().ok(); - time.map(time::Duration::from_secs) + let time = val.parse::().ok(); + time.map(time::Duration::from_secs) } fn encode_time(time: time::Duration) -> String { - format!("{}", time.as_secs()) + format!("{}", time.as_secs()) } /// Manages authorization codes for `SignerUIs` pub struct AuthCodes { - codes: Vec, - now: T, + codes: Vec, + now: T, } impl AuthCodes { - - /// Reads `AuthCodes` from file and creates new instance using `DefaultTimeProvider`. - pub fn from_file(file: &Path) -> io::Result { - let content = { - if let Ok(mut file) = fs::File::open(file) { - let mut s = String::new(); - let _ = file.read_to_string(&mut s)?; - s - } else { - "".into() - } - }; - let time_provider = DefaultTimeProvider::default(); - - let codes = content.lines() - .filter_map(|line| { - let mut parts = line.split(SEPARATOR); - let token = parts.next(); - let created = parts.next(); - let used = parts.next(); - - match token { - None => None, - Some(token) if token.len() < TOKEN_LENGTH => None, - Some(token) => { - Some(Code { - code: token.into(), - last_used_at: used.and_then(decode_time), - created_at: created.and_then(decode_time) - .unwrap_or_else(|| time::Duration::from_secs(time_provider.now())), - }) - } - } - }) - .collect(); - Ok(AuthCodes { - codes, - now: time_provider, - }) - } - + /// Reads `AuthCodes` from file and creates new instance using `DefaultTimeProvider`. + pub fn from_file(file: &Path) -> io::Result { + let content = { + if let Ok(mut file) = fs::File::open(file) { + let mut s = String::new(); + let _ = file.read_to_string(&mut s)?; + s + } else { + "".into() + } + }; + let time_provider = DefaultTimeProvider::default(); + + let codes = content + .lines() + .filter_map(|line| { + let mut parts = line.split(SEPARATOR); + let token = parts.next(); + let created = parts.next(); + let used = parts.next(); + + match token { + None => None, + Some(token) if token.len() < TOKEN_LENGTH => None, + Some(token) => Some(Code { + code: token.into(), + last_used_at: used.and_then(decode_time), + created_at: created + .and_then(decode_time) + .unwrap_or_else(|| time::Duration::from_secs(time_provider.now())), + }), + } + }) + .collect(); + Ok(AuthCodes { + codes, + now: time_provider, + }) + } } impl AuthCodes { - - /// Writes all `AuthCodes` to a disk. - pub fn to_file(&self, file: &Path) -> io::Result<()> { - let mut file = fs::File::create(file)?; - let content = self.codes.iter().map(|code| { - let mut data = vec![code.code.clone(), encode_time(code.created_at)]; - if let Some(used_at) = code.last_used_at { - data.push(encode_time(used_at)); - } - data.join(SEPARATOR) - }).join("\n"); - file.write_all(content.as_bytes()) - } - - /// Creates a new `AuthCodes` store with given `TimeProvider`. - pub fn new(codes: Vec, now: T) -> Self { - AuthCodes { - codes: codes.into_iter().map(|code| Code { - code, - created_at: time::Duration::from_secs(now.now()), - last_used_at: None, - }).collect(), - now, - } - } - - /// Checks if given hash is correct authcode of `SignerUI` - /// Updates this hash last used field in case it's valid. - pub fn is_valid(&mut self, hash: &H256, time: u64) -> bool { - let now = self.now.now(); - // check time - if time >= now + TIME_THRESHOLD || time <= now - TIME_THRESHOLD { - warn!(target: "signer", "Received old authentication request. ({} vs {})", now, time); - return false; - } - - let as_token = |code| keccak(format!("{}:{}", code, time)); - - // look for code - for code in &mut self.codes { - if &as_token(&code.code) == hash { - code.last_used_at = Some(time::Duration::from_secs(now)); - return true; - } - } - - false - } - - /// Generates and returns a new code that can be used by `SignerUIs` - pub fn generate_new(&mut self) -> io::Result { - let mut rng = OsRng::new()?; - let code = rng.gen_ascii_chars().take(TOKEN_LENGTH).collect::(); - let readable_code = code.as_bytes() - .chunks(4) - .filter_map(|f| String::from_utf8(f.to_vec()).ok()) - .collect::>() - .join("-"); - trace!(target: "signer", "New authentication token generated."); - self.codes.push(Code { - code, - created_at: time::Duration::from_secs(self.now.now()), - last_used_at: None, - }); - Ok(readable_code) - } - - /// Returns true if there are no tokens in this store - pub fn is_empty(&self) -> bool { - self.codes.is_empty() - } - - /// Removes old tokens that have not been used since creation. - pub fn clear_garbage(&mut self) { - let now = self.now.now(); - let threshold = time::Duration::from_secs(now.saturating_sub(UNUSED_TOKEN_TIMEOUT)); - - let codes = mem::replace(&mut self.codes, Vec::new()); - for code in codes { - // Skip codes that are old and were never used. - if code.last_used_at.is_none() && code.created_at <= threshold { - continue; - } - self.codes.push(code); - } - } + /// Writes all `AuthCodes` to a disk. + pub fn to_file(&self, file: &Path) -> io::Result<()> { + let mut file = fs::File::create(file)?; + let content = self + .codes + .iter() + .map(|code| { + let mut data = vec![code.code.clone(), encode_time(code.created_at)]; + if let Some(used_at) = code.last_used_at { + data.push(encode_time(used_at)); + } + data.join(SEPARATOR) + }) + .join("\n"); + file.write_all(content.as_bytes()) + } + + /// Creates a new `AuthCodes` store with given `TimeProvider`. + pub fn new(codes: Vec, now: T) -> Self { + AuthCodes { + codes: codes + .into_iter() + .map(|code| Code { + code, + created_at: time::Duration::from_secs(now.now()), + last_used_at: None, + }) + .collect(), + now, + } + } + + /// Checks if given hash is correct authcode of `SignerUI` + /// Updates this hash last used field in case it's valid. + pub fn is_valid(&mut self, hash: &H256, time: u64) -> bool { + let now = self.now.now(); + // check time + if time >= now + TIME_THRESHOLD || time <= now - TIME_THRESHOLD { + warn!(target: "signer", "Received old authentication request. ({} vs {})", now, time); + return false; + } + + let as_token = |code| keccak(format!("{}:{}", code, time)); + + // look for code + for code in &mut self.codes { + if &as_token(&code.code) == hash { + code.last_used_at = Some(time::Duration::from_secs(now)); + return true; + } + } + + false + } + + /// Generates and returns a new code that can be used by `SignerUIs` + pub fn generate_new(&mut self) -> io::Result { + let mut rng = OsRng::new()?; + let code = rng.gen_ascii_chars().take(TOKEN_LENGTH).collect::(); + let readable_code = code + .as_bytes() + .chunks(4) + .filter_map(|f| String::from_utf8(f.to_vec()).ok()) + .collect::>() + .join("-"); + trace!(target: "signer", "New authentication token generated."); + self.codes.push(Code { + code, + created_at: time::Duration::from_secs(self.now.now()), + last_used_at: None, + }); + Ok(readable_code) + } + + /// Returns true if there are no tokens in this store + pub fn is_empty(&self) -> bool { + self.codes.is_empty() + } + + /// Removes old tokens that have not been used since creation. + pub fn clear_garbage(&mut self) { + let now = self.now.now(); + let threshold = time::Duration::from_secs(now.saturating_sub(UNUSED_TOKEN_TIMEOUT)); + + let codes = mem::replace(&mut self.codes, Vec::new()); + for code in codes { + // Skip codes that are old and were never used. + if code.last_used_at.is_none() && code.created_at <= threshold { + continue; + } + self.codes.push(code); + } + } } #[cfg(test)] mod tests { - use std::io::{Read, Write}; - use std::{time, fs}; - use std::cell::Cell; - use tempdir::TempDir; - use hash::keccak; - - use ethereum_types::H256; - use super::*; - - fn generate_hash(val: &str, time: u64) -> H256 { - keccak(format!("{}:{}", val, time)) - } - - #[test] - fn should_return_false_even_if_code_is_initial_and_store_is_empty() { - // given - let code = "initial"; - let time = 99; - let mut codes = AuthCodes::new(vec![], || 100); - - // when - let res1 = codes.is_valid(&generate_hash(code, time), time); - let res2 = codes.is_valid(&generate_hash(code, time), time); - - // then - assert_eq!(res1, false); - assert_eq!(res2, false); - } - - #[test] - fn should_return_true_if_hash_is_valid() { - // given - let code = "23521352asdfasdfadf"; - let time = 99; - let mut codes = AuthCodes::new(vec![code.into()], || 100); - - // when - let res = codes.is_valid(&generate_hash(code, time), time); - - // then - assert_eq!(res, true); - } - - #[test] - fn should_return_false_if_code_is_unknown() { - // given - let code = "23521352asdfasdfadf"; - let time = 99; - let mut codes = AuthCodes::new(vec!["1".into()], || 100); - - // when - let res = codes.is_valid(&generate_hash(code, time), time); - - // then - assert_eq!(res, false); - } - - #[test] - fn should_return_false_if_hash_is_valid_but_time_is_invalid() { - // given - let code = "23521352asdfasdfadf"; - let time = 107; - let time2 = 93; - let mut codes = AuthCodes::new(vec![code.into()], || 100); - - // when - let res1 = codes.is_valid(&generate_hash(code, time), time); - let res2 = codes.is_valid(&generate_hash(code, time2), time2); - - // then - assert_eq!(res1, false); - assert_eq!(res2, false); - } - - #[test] - fn should_read_old_format_from_file() { - // given - let tempdir = TempDir::new("").unwrap(); - let file_path = tempdir.path().join("file"); - let code = "23521352asdfasdfadf"; - { - let mut file = fs::File::create(&file_path).unwrap(); - file.write_all(b"a\n23521352asdfasdfadf\nb\n").unwrap(); - } - - // when - let mut authcodes = AuthCodes::from_file(&file_path).unwrap(); - let time = time::UNIX_EPOCH.elapsed().unwrap().as_secs(); - - // then - assert!(authcodes.is_valid(&generate_hash(code, time), time), "Code should be read from file"); - } - - #[test] - fn should_remove_old_unused_tokens() { - // given - let tempdir = TempDir::new("").unwrap(); - let file_path = tempdir.path().join("file"); - let code1 = "11111111asdfasdf111"; - let code2 = "22222222asdfasdf222"; - let code3 = "33333333asdfasdf333"; - - let time = Cell::new(100); - let mut codes = AuthCodes::new(vec![code1.into(), code2.into(), code3.into()], || time.get()); - // `code2` should not be removed (we never remove tokens that were used) - codes.is_valid(&generate_hash(code2, time.get()), time.get()); - - // when - time.set(100 + 10_000_000); - // mark `code1` as used now - codes.is_valid(&generate_hash(code1, time.get()), time.get()); - - let new_code = codes.generate_new().unwrap().replace('-', ""); - codes.clear_garbage(); - codes.to_file(&file_path).unwrap(); - - // then - let mut content = String::new(); - let mut file = fs::File::open(&file_path).unwrap(); - file.read_to_string(&mut content).unwrap(); - - assert_eq!(content, format!("{};100;10000100\n{};100;100\n{};10000100", code1, code2, new_code)); - } - + use hash::keccak; + use std::{ + cell::Cell, + fs, + io::{Read, Write}, + time, + }; + use tempdir::TempDir; + + use super::*; + use ethereum_types::H256; + + fn generate_hash(val: &str, time: u64) -> H256 { + keccak(format!("{}:{}", val, time)) + } + + #[test] + fn should_return_false_even_if_code_is_initial_and_store_is_empty() { + // given + let code = "initial"; + let time = 99; + let mut codes = AuthCodes::new(vec![], || 100); + + // when + let res1 = codes.is_valid(&generate_hash(code, time), time); + let res2 = codes.is_valid(&generate_hash(code, time), time); + + // then + assert_eq!(res1, false); + assert_eq!(res2, false); + } + + #[test] + fn should_return_true_if_hash_is_valid() { + // given + let code = "23521352asdfasdfadf"; + let time = 99; + let mut codes = AuthCodes::new(vec![code.into()], || 100); + + // when + let res = codes.is_valid(&generate_hash(code, time), time); + + // then + assert_eq!(res, true); + } + + #[test] + fn should_return_false_if_code_is_unknown() { + // given + let code = "23521352asdfasdfadf"; + let time = 99; + let mut codes = AuthCodes::new(vec!["1".into()], || 100); + + // when + let res = codes.is_valid(&generate_hash(code, time), time); + + // then + assert_eq!(res, false); + } + + #[test] + fn should_return_false_if_hash_is_valid_but_time_is_invalid() { + // given + let code = "23521352asdfasdfadf"; + let time = 107; + let time2 = 93; + let mut codes = AuthCodes::new(vec![code.into()], || 100); + + // when + let res1 = codes.is_valid(&generate_hash(code, time), time); + let res2 = codes.is_valid(&generate_hash(code, time2), time2); + + // then + assert_eq!(res1, false); + assert_eq!(res2, false); + } + + #[test] + fn should_read_old_format_from_file() { + // given + let tempdir = TempDir::new("").unwrap(); + let file_path = tempdir.path().join("file"); + let code = "23521352asdfasdfadf"; + { + let mut file = fs::File::create(&file_path).unwrap(); + file.write_all(b"a\n23521352asdfasdfadf\nb\n").unwrap(); + } + + // when + let mut authcodes = AuthCodes::from_file(&file_path).unwrap(); + let time = time::UNIX_EPOCH.elapsed().unwrap().as_secs(); + + // then + assert!( + authcodes.is_valid(&generate_hash(code, time), time), + "Code should be read from file" + ); + } + + #[test] + fn should_remove_old_unused_tokens() { + // given + let tempdir = TempDir::new("").unwrap(); + let file_path = tempdir.path().join("file"); + let code1 = "11111111asdfasdf111"; + let code2 = "22222222asdfasdf222"; + let code3 = "33333333asdfasdf333"; + + let time = Cell::new(100); + let mut codes = AuthCodes::new(vec![code1.into(), code2.into(), code3.into()], || { + time.get() + }); + // `code2` should not be removed (we never remove tokens that were used) + codes.is_valid(&generate_hash(code2, time.get()), time.get()); + + // when + time.set(100 + 10_000_000); + // mark `code1` as used now + codes.is_valid(&generate_hash(code1, time.get()), time.get()); + + let new_code = codes.generate_new().unwrap().replace('-', ""); + codes.clear_garbage(); + codes.to_file(&file_path).unwrap(); + + // then + let mut content = String::new(); + let mut file = fs::File::open(&file_path).unwrap(); + file.read_to_string(&mut content).unwrap(); + + assert_eq!( + content, + format!( + "{};100;10000100\n{};100;100\n{};10000100", + code1, code2, new_code + ) + ); + } } diff --git a/rpc/src/http_common.rs b/rpc/src/http_common.rs index 99bd392f356..bd20caedfd5 100644 --- a/rpc/src/http_common.rs +++ b/rpc/src/http_common.rs @@ -1,54 +1,55 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Transport-specific metadata extractors. -use jsonrpc_core; use http; use hyper; +use jsonrpc_core; /// HTTP RPC server impl-independent metadata extractor pub trait HttpMetaExtractor: Send + Sync + 'static { - /// Type of Metadata - type Metadata: jsonrpc_core::Metadata; - /// Extracts metadata from given params. - fn read_metadata(&self, origin: Option, user_agent: Option) -> Self::Metadata; + /// Type of Metadata + type Metadata: jsonrpc_core::Metadata; + /// Extracts metadata from given params. + fn read_metadata(&self, origin: Option, user_agent: Option) -> Self::Metadata; } pub struct MetaExtractor { - extractor: T, + extractor: T, } impl MetaExtractor { - pub fn new(extractor: T) -> Self { - MetaExtractor { extractor } - } + pub fn new(extractor: T) -> Self { + MetaExtractor { extractor } + } } -impl http::MetaExtractor for MetaExtractor where - T: HttpMetaExtractor, - M: jsonrpc_core::Metadata, +impl http::MetaExtractor for MetaExtractor +where + T: HttpMetaExtractor, + M: jsonrpc_core::Metadata, { - fn read_metadata(&self, req: &hyper::Request) -> M { - let as_string = |header: Option<&hyper::header::HeaderValue>| { - header.and_then(|val| val.to_str().ok().map(ToOwned::to_owned)) - }; - - let origin = as_string(req.headers().get("origin")); - let user_agent = as_string(req.headers().get("user-agent")); - self.extractor.read_metadata(origin, user_agent) - } + fn read_metadata(&self, req: &hyper::Request) -> M { + let as_string = |header: Option<&hyper::header::HeaderValue>| { + header.and_then(|val| val.to_str().ok().map(ToOwned::to_owned)) + }; + + let origin = as_string(req.headers().get("origin")); + let user_agent = as_string(req.headers().get("user-agent")); + self.extractor.read_metadata(origin, user_agent) + } } diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index a537cb29424..6a83c80434b 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -1,20 +1,20 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -//! Parity RPC. +//! OpenEthereum JSON-RPC Servers (WS, HTTP, IPC). #![warn(missing_docs, unused_extern_crates)] #![cfg_attr(feature = "cargo-clippy", warn(clippy::all, clippy::pedantic))] @@ -41,14 +41,11 @@ extern crate futures; extern crate ansi_term; -extern crate cid; extern crate itertools; -extern crate multihash; extern crate order_stat; extern crate parking_lot; extern crate rand; extern crate rustc_hex; -extern crate semver; extern crate serde; extern crate serde_json; extern crate tokio_timer; @@ -61,26 +58,22 @@ extern crate jsonrpc_ipc_server as ipc; extern crate jsonrpc_pubsub; extern crate common_types as types; +extern crate eip_712; extern crate ethash; extern crate ethcore; -extern crate fastmap; -extern crate parity_bytes as bytes; -extern crate parity_crypto as crypto; -extern crate ethcore_light as light; extern crate ethcore_logger; extern crate ethcore_miner as miner; extern crate ethcore_network as network; -extern crate ethcore_private_tx; extern crate ethcore_sync as sync; extern crate ethereum_types; extern crate ethkey; extern crate ethstore; extern crate fetch; extern crate keccak_hash as hash; +extern crate parity_bytes as bytes; +extern crate parity_crypto as crypto; extern crate parity_runtime; -extern crate parity_updater as updater; extern crate parity_version as version; -extern crate eip_712; extern crate rlp; extern crate stats; extern crate tempdir; @@ -124,20 +117,24 @@ pub mod v1; pub mod tests; -pub use jsonrpc_core::{FutureOutput, FutureResult, FutureResponse, FutureRpcResult}; -pub use jsonrpc_pubsub::Session as PubSubSession; -pub use ipc::{Server as IpcServer, MetaExtractor as IpcMetaExtractor, RequestContext as IpcRequestContext}; pub use http::{ - hyper, - RequestMiddleware, RequestMiddlewareAction, - AccessControlAllowOrigin, Host, DomainsValidation, cors::AccessControlAllowHeaders + cors::AccessControlAllowHeaders, hyper, AccessControlAllowOrigin, DomainsValidation, Host, + RequestMiddleware, RequestMiddlewareAction, +}; +pub use ipc::{ + MetaExtractor as IpcMetaExtractor, RequestContext as IpcRequestContext, Server as IpcServer, }; +pub use jsonrpc_core::{FutureOutput, FutureResponse, FutureResult, FutureRpcResult}; +pub use jsonrpc_pubsub::Session as PubSubSession; -pub use v1::{NetworkSettings, Metadata, Origin, informant, dispatch, signer}; -pub use v1::block_import::{is_major_importing, is_major_importing_or_waiting}; -pub use v1::extractors::{RpcExtractor, WsExtractor, WsStats, WsDispatcher}; pub use authcodes::{AuthCodes, TimeProvider}; pub use http_common::HttpMetaExtractor; +pub use v1::{ + block_import::{is_major_importing, is_major_importing_or_waiting}, + dispatch, + extractors::{RpcExtractor, WsDispatcher, WsExtractor, WsStats}, + informant, signer, Metadata, NetworkSettings, Origin, +}; use std::net::SocketAddr; @@ -146,101 +143,100 @@ pub type HttpServer = http::Server; /// Start http server asynchronously and returns result with `Server` handle on success or an error. pub fn start_http( - addr: &SocketAddr, - cors_domains: http::DomainsValidation, - allowed_hosts: http::DomainsValidation, - handler: H, - extractor: T, - threads: usize, - max_payload: usize, - keep_alive: bool, -) -> ::std::io::Result where - M: jsonrpc_core::Metadata, - S: jsonrpc_core::Middleware, - H: Into>, - T: HttpMetaExtractor, + addr: &SocketAddr, + cors_domains: http::DomainsValidation, + allowed_hosts: http::DomainsValidation, + handler: H, + extractor: T, + threads: usize, + max_payload: usize, + keep_alive: bool, +) -> ::std::io::Result +where + M: jsonrpc_core::Metadata, + S: jsonrpc_core::Middleware, + H: Into>, + T: HttpMetaExtractor, { - let extractor = http_common::MetaExtractor::new(extractor); - Ok(http::ServerBuilder::with_meta_extractor(handler, extractor) - .keep_alive(keep_alive) - .threads(threads) - .cors(cors_domains) - .allowed_hosts(allowed_hosts) - .health_api(("/api/health", "parity_nodeStatus")) - .cors_allow_headers(AccessControlAllowHeaders::Any) - .max_request_body_size(max_payload * 1024 * 1024) - .start_http(addr)?) + let extractor = http_common::MetaExtractor::new(extractor); + Ok(http::ServerBuilder::with_meta_extractor(handler, extractor) + .keep_alive(keep_alive) + .threads(threads) + .cors(cors_domains) + .allowed_hosts(allowed_hosts) + .health_api(("/api/health", "parity_nodeStatus")) + .cors_allow_headers(AccessControlAllowHeaders::Any) + .max_request_body_size(max_payload * 1024 * 1024) + .start_http(addr)?) } /// Same as `start_http`, but takes an additional `middleware` parameter that is introduced as a /// hyper middleware. pub fn start_http_with_middleware( - addr: &SocketAddr, - cors_domains: http::DomainsValidation, - allowed_hosts: http::DomainsValidation, - handler: H, - extractor: T, - middleware: R, - threads: usize, - max_payload: usize, - keep_alive: bool, -) -> ::std::io::Result where - M: jsonrpc_core::Metadata, - S: jsonrpc_core::Middleware, - H: Into>, - T: HttpMetaExtractor, - R: RequestMiddleware, + addr: &SocketAddr, + cors_domains: http::DomainsValidation, + allowed_hosts: http::DomainsValidation, + handler: H, + extractor: T, + middleware: R, + threads: usize, + max_payload: usize, + keep_alive: bool, +) -> ::std::io::Result +where + M: jsonrpc_core::Metadata, + S: jsonrpc_core::Middleware, + H: Into>, + T: HttpMetaExtractor, + R: RequestMiddleware, { - let extractor = http_common::MetaExtractor::new(extractor); - Ok(http::ServerBuilder::with_meta_extractor(handler, extractor) - .keep_alive(keep_alive) - .threads(threads) - .cors(cors_domains) - .allowed_hosts(allowed_hosts) - .cors_allow_headers(AccessControlAllowHeaders::Any) - .max_request_body_size(max_payload * 1024 * 1024) - .request_middleware(middleware) - .start_http(addr)?) + let extractor = http_common::MetaExtractor::new(extractor); + Ok(http::ServerBuilder::with_meta_extractor(handler, extractor) + .keep_alive(keep_alive) + .threads(threads) + .cors(cors_domains) + .allowed_hosts(allowed_hosts) + .cors_allow_headers(AccessControlAllowHeaders::Any) + .max_request_body_size(max_payload * 1024 * 1024) + .request_middleware(middleware) + .start_http(addr)?) } /// Start ipc server asynchronously and returns result with `Server` handle on success or an error. -pub fn start_ipc( - addr: &str, - handler: H, - extractor: T, -) -> ::std::io::Result where - M: jsonrpc_core::Metadata, - S: jsonrpc_core::Middleware, - H: Into>, - T: IpcMetaExtractor, +pub fn start_ipc(addr: &str, handler: H, extractor: T) -> ::std::io::Result +where + M: jsonrpc_core::Metadata, + S: jsonrpc_core::Middleware, + H: Into>, + T: IpcMetaExtractor, { - ipc::ServerBuilder::with_meta_extractor(handler, extractor) - .start(addr) + ipc::ServerBuilder::with_meta_extractor(handler, extractor).start(addr) } /// Start WS server and return `Server` handle. pub fn start_ws( - addr: &SocketAddr, - handler: H, - allowed_origins: ws::DomainsValidation, - allowed_hosts: ws::DomainsValidation, - max_connections: usize, - extractor: T, - middleware: V, - stats: U, -) -> Result where - M: jsonrpc_core::Metadata, - S: jsonrpc_core::Middleware, - H: Into>, - T: ws::MetaExtractor, - U: ws::SessionStats, - V: ws::RequestMiddleware, + addr: &SocketAddr, + handler: H, + allowed_origins: ws::DomainsValidation, + allowed_hosts: ws::DomainsValidation, + max_connections: usize, + extractor: T, + middleware: V, + stats: U, +) -> Result +where + M: jsonrpc_core::Metadata, + S: jsonrpc_core::Middleware, + H: Into>, + T: ws::MetaExtractor, + U: ws::SessionStats, + V: ws::RequestMiddleware, { - ws::ServerBuilder::with_meta_extractor(handler, extractor) - .request_middleware(middleware) - .allowed_origins(allowed_origins) - .allowed_hosts(allowed_hosts) - .max_connections(max_connections) - .session_stats(stats) - .start(addr) + ws::ServerBuilder::with_meta_extractor(handler, extractor) + .request_middleware(middleware) + .allowed_origins(allowed_origins) + .allowed_hosts(allowed_hosts) + .max_connections(max_connections) + .session_stats(stats) + .start(addr) } diff --git a/rpc/src/tests/helpers.rs b/rpc/src/tests/helpers.rs index 301d77e91ce..923b5a92c8c 100644 --- a/rpc/src/tests/helpers.rs +++ b/rpc/src/tests/helpers.rs @@ -1,21 +1,23 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::ops::{Deref, DerefMut}; -use std::path::PathBuf; +use std::{ + ops::{Deref, DerefMut}, + path::PathBuf, +}; use tempdir::TempDir; use parity_runtime::{Runtime, TaskExecutor}; @@ -24,64 +26,65 @@ use authcodes::AuthCodes; /// Server with event loop pub struct Server { - /// Server - pub server: T, - /// RPC Event Loop - pub event_loop: Runtime, + /// Server + pub server: T, + /// RPC Event Loop + pub event_loop: Runtime, } impl Server { - pub fn new(f: F) -> Server where - F: FnOnce(TaskExecutor) -> T, - { - let event_loop = Runtime::with_thread_count(1); - let remote = event_loop.raw_executor(); - - Server { - server: f(remote), - event_loop, - } - } + pub fn new(f: F) -> Server + where + F: FnOnce(TaskExecutor) -> T, + { + let event_loop = Runtime::with_thread_count(1); + let remote = event_loop.raw_executor(); + + Server { + server: f(remote), + event_loop, + } + } } impl Deref for Server { - type Target = T; + type Target = T; - fn deref(&self) -> &Self::Target { - &self.server - } + fn deref(&self) -> &Self::Target { + &self.server + } } /// Struct representing authcodes pub struct GuardedAuthCodes { - authcodes: AuthCodes, - _tempdir: TempDir, - /// The path to the mock authcodes - pub path: PathBuf, + authcodes: AuthCodes, + _tempdir: TempDir, + /// The path to the mock authcodes + pub path: PathBuf, } impl Default for GuardedAuthCodes { - fn default() -> Self { - let tempdir = TempDir::new("").unwrap(); - let path = tempdir.path().join("file"); - - GuardedAuthCodes { - authcodes: AuthCodes::from_file(&path).unwrap(), - _tempdir: tempdir, - path, - } - } + fn default() -> Self { + let tempdir = TempDir::new("").unwrap(); + let path = tempdir.path().join("file"); + + GuardedAuthCodes { + authcodes: AuthCodes::from_file(&path).unwrap(), + _tempdir: tempdir, + path, + } + } } impl Deref for GuardedAuthCodes { - type Target = AuthCodes; - fn deref(&self) -> &Self::Target { - &self.authcodes - } + type Target = AuthCodes; + fn deref(&self) -> &Self::Target { + &self.authcodes + } } impl DerefMut for GuardedAuthCodes { - fn deref_mut(&mut self) -> &mut AuthCodes { - &mut self.authcodes - } + fn deref_mut(&mut self) -> &mut AuthCodes { + &mut self.authcodes + } } diff --git a/rpc/src/tests/http_client.rs b/rpc/src/tests/http_client.rs index 0588c791e77..dc5d3a5c7d4 100644 --- a/rpc/src/tests/http_client.rs +++ b/rpc/src/tests/http_client.rs @@ -1,132 +1,159 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::thread; -use std::time::Duration; -use std::io::{self, Read, Write}; -use std::str::{self, Lines}; -use std::net::{TcpStream, SocketAddr}; +use std::{ + io::{self, Read, Write}, + net::{SocketAddr, TcpStream}, + str::{self, Lines}, + thread, + time::Duration, +}; pub struct Response { - pub status: String, - pub headers: Vec, - pub headers_raw: String, - pub body: String, + pub status: String, + pub headers: Vec, + pub headers_raw: String, + pub body: String, } impl Response { - pub fn assert_header(&self, header: &str, value: &str) { - let header = format!("{}: {}", header, value); - assert!(self.headers.iter().any(|h| h == &header), "Couldn't find header {} in {:?}", header, &self.headers) - } - - pub fn assert_status(&self, status: &str) { - assert_eq!(self.status, status.to_owned(), "Got unexpected code. Body: {:?}", self.body); - } - - pub fn assert_security_headers_present(&self, port: Option) { - assert_security_headers_present(&self.headers, port) - } + pub fn assert_header(&self, header: &str, value: &str) { + let header = format!("{}: {}", header, value); + assert!( + self.headers.iter().any(|h| h == &header), + "Couldn't find header {} in {:?}", + header, + &self.headers + ) + } + + pub fn assert_status(&self, status: &str) { + assert_eq!( + self.status, + status.to_owned(), + "Got unexpected code. Body: {:?}", + self.body + ); + } + + pub fn assert_security_headers_present(&self, port: Option) { + assert_security_headers_present(&self.headers, port) + } } pub fn read_block(lines: &mut Lines, all: bool) -> String { - let mut block = String::new(); - loop { - let line = lines.next(); - match line { - None => break, - Some("") if !all => break, - Some(v) => { - block.push_str(v); - block.push_str("\n"); - }, - } - } - block + let mut block = String::new(); + loop { + let line = lines.next(); + match line { + None => break, + Some("") if !all => break, + Some(v) => { + block.push_str(v); + block.push_str("\n"); + } + } + } + block } fn connect(address: &SocketAddr) -> TcpStream { - let mut retries = 0; - let mut last_error = None; - while retries < 10 { - retries += 1; - - let res = TcpStream::connect(address); - match res { - Ok(stream) => { - return stream; - }, - Err(e) => { - last_error = Some(e); - thread::sleep(Duration::from_millis(retries * 10)); - } - } - } - panic!("Unable to connect to the server. Last error: {:?}", last_error); + let mut retries = 0; + let mut last_error = None; + while retries < 10 { + retries += 1; + + let res = TcpStream::connect(address); + match res { + Ok(stream) => { + return stream; + } + Err(e) => { + last_error = Some(e); + thread::sleep(Duration::from_millis(retries * 10)); + } + } + } + panic!( + "Unable to connect to the server. Last error: {:?}", + last_error + ); } pub fn request(address: &SocketAddr, request: &str) -> Response { - let mut req = connect(address); - req.set_read_timeout(Some(Duration::from_secs(2))).unwrap(); - req.write_all(request.as_bytes()).unwrap(); - - let mut response = Vec::new(); - loop { - let mut chunk = [0; 32 *1024]; - match req.read(&mut chunk) { - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => break, - Err(err) => panic!("Unable to read response: {:?}", err), - Ok(0) => break, - Ok(read) => response.extend_from_slice(&chunk[..read]), - } - } - - let response = String::from_utf8_lossy(&response).into_owned(); - let mut lines = response.lines(); - let status = lines.next().expect("Expected a response").to_owned(); - let headers_raw = read_block(&mut lines, false); - let headers = headers_raw.split('\n').map(ToOwned::to_owned).collect(); - let body = read_block(&mut lines, true); - - Response { - status, - headers, - headers_raw, - body, - } + let mut req = connect(address); + req.set_read_timeout(Some(Duration::from_secs(2))).unwrap(); + req.write_all(request.as_bytes()).unwrap(); + + let mut response = Vec::new(); + loop { + let mut chunk = [0; 32 * 1024]; + match req.read(&mut chunk) { + Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => break, + Err(err) => panic!("Unable to read response: {:?}", err), + Ok(0) => break, + Ok(read) => response.extend_from_slice(&chunk[..read]), + } + } + + let response = String::from_utf8_lossy(&response).into_owned(); + let mut lines = response.lines(); + let status = lines.next().expect("Expected a response").to_owned(); + let headers_raw = read_block(&mut lines, false); + let headers = headers_raw.split('\n').map(ToOwned::to_owned).collect(); + let body = read_block(&mut lines, true); + + Response { + status, + headers, + headers_raw, + body, + } } /// Check if all required security headers are present pub fn assert_security_headers_present(headers: &[String], port: Option) { - if port.is_none() { - assert!( - headers.iter().any(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN") - "X-Frame-Options: SAMEORIGIN missing: {:?}", headers - ); - } - assert!( - headers.iter().any(|header| header.as_str() == "X-XSS-Protection: 1; mode=block") - "X-XSS-Protection missing: {:?}", headers - ); - assert!( - headers.iter().any(|header| header.as_str() == "X-Content-Type-Options: nosniff") - "X-Content-Type-Options missing: {:?}", headers - ); - assert!( - headers.iter().any(|header| header.starts_with("Content-Security-Policy: ")) - "Content-Security-Policy missing: {:?}", headers - ) + if port.is_none() { + assert!( + headers + .iter() + .any(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN"), + "X-Frame-Options: SAMEORIGIN missing: {:?}", + headers + ); + } + assert!( + headers + .iter() + .any(|header| header.as_str() == "X-XSS-Protection: 1; mode=block"), + "X-XSS-Protection missing: {:?}", + headers + ); + assert!( + headers + .iter() + .any(|header| header.as_str() == "X-Content-Type-Options: nosniff"), + "X-Content-Type-Options missing: {:?}", + headers + ); + assert!( + headers + .iter() + .any(|header| header.starts_with("Content-Security-Policy: ")), + "Content-Security-Policy missing: {:?}", + headers + ) } diff --git a/rpc/src/tests/mod.rs b/rpc/src/tests/mod.rs index a73d69096da..91762fe0250 100644 --- a/rpc/src/tests/mod.rs +++ b/rpc/src/tests/mod.rs @@ -1,22 +1,23 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! RPC integration tests. mod helpers; mod http_client; -#[cfg(test)] mod rpc; +#[cfg(test)] +mod rpc; pub mod ws; diff --git a/rpc/src/tests/rpc.rs b/rpc/src/tests/rpc.rs index 99498c3e5d4..1eb41550a5b 100644 --- a/rpc/src/tests/rpc.rs +++ b/rpc/src/tests/rpc.rs @@ -1,81 +1,83 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use jsonrpc_core::MetaIoHandler; use http::{self, hyper}; +use jsonrpc_core::MetaIoHandler; -use {HttpServer}; -use tests::helpers::Server; -use tests::http_client; +use tests::{helpers::Server, http_client}; use v1::{extractors, Metadata}; +use HttpServer; fn serve(handler: Option>) -> Server { - let address = "127.0.0.1:0".parse().unwrap(); - let handler = handler.unwrap_or_default(); - - Server::new(|_remote| ::start_http_with_middleware( - &address, - http::DomainsValidation::Disabled, - http::DomainsValidation::Disabled, - handler, - extractors::RpcExtractor, - |request: hyper::Request| { - http::RequestMiddlewareAction::Proceed { - should_continue_on_invalid_cors: false, - request, - } - }, - 1, - 5, - false, - ).unwrap()) + let address = "127.0.0.1:0".parse().unwrap(); + let handler = handler.unwrap_or_default(); + + Server::new(|_remote| { + ::start_http_with_middleware( + &address, + http::DomainsValidation::Disabled, + http::DomainsValidation::Disabled, + handler, + extractors::RpcExtractor, + |request: hyper::Request| http::RequestMiddlewareAction::Proceed { + should_continue_on_invalid_cors: false, + request, + }, + 1, + 5, + false, + ) + .unwrap() + }) } /// Test a single request to running server fn request(server: Server, request: &str) -> http_client::Response { - http_client::request(server.server.address(), request) + http_client::request(server.server.address(), request) } #[cfg(test)] mod tests { - use jsonrpc_core::{MetaIoHandler, Value}; - use v1::Metadata; - use super::{request, Server}; - - fn serve() -> (Server<::HttpServer>, ::std::net::SocketAddr) { - let mut io = MetaIoHandler::default(); - io.add_method_with_meta("hello", |_, meta: Metadata| { - Ok(Value::String(format!("{}", meta.origin))) - }); - let server = super::serve(Some(io)); - let address = server.server.address().to_owned(); - - (server, address) - } - - #[test] - fn should_extract_rpc_origin() { - // given - let (server, address) = serve(); - - // when - let req = r#"{"method":"hello","params":[],"jsonrpc":"2.0","id":1}"#; - let expected = "{\"jsonrpc\":\"2.0\",\"result\":\"unknown origin / unknown agent via RPC\",\"id\":1}\n"; - let res = request(server, - &format!("\ + use super::{request, Server}; + use jsonrpc_core::{MetaIoHandler, Value}; + use v1::Metadata; + + fn serve() -> (Server<::HttpServer>, ::std::net::SocketAddr) { + let mut io = MetaIoHandler::default(); + io.add_method_with_meta("hello", |_, meta: Metadata| { + Ok(Value::String(format!("{}", meta.origin))) + }); + let server = super::serve(Some(io)); + let address = server.server.address().to_owned(); + + (server, address) + } + + #[test] + fn should_extract_rpc_origin() { + // given + let (server, address) = serve(); + + // when + let req = r#"{"method":"hello","params":[],"jsonrpc":"2.0","id":1}"#; + let expected = "{\"jsonrpc\":\"2.0\",\"result\":\"unknown origin / unknown agent via RPC\",\"id\":1}\n"; + let res = request( + server, + &format!( + "\ POST / HTTP/1.1\r\n\ Host: {}\r\n\ Content-Type: application/json\r\n\ @@ -83,24 +85,31 @@ mod tests { Connection: close\r\n\ \r\n\ {} - ", address, req.len(), req) - ); - - // then - res.assert_status("HTTP/1.1 200 OK"); - assert_eq!(res.body, expected); - } - - #[test] - fn should_extract_rpc_origin_with_service() { - // given - let (server, address) = serve(); - - // when - let req = r#"{"method":"hello","params":[],"jsonrpc":"2.0","id":1}"#; - let expected = "{\"jsonrpc\":\"2.0\",\"result\":\"unknown origin / curl/7.16.3 via RPC\",\"id\":1}\n"; - let res = request(server, - &format!("\ + ", + address, + req.len(), + req + ), + ); + + // then + res.assert_status("HTTP/1.1 200 OK"); + assert_eq!(res.body, expected); + } + + #[test] + fn should_extract_rpc_origin_with_service() { + // given + let (server, address) = serve(); + + // when + let req = r#"{"method":"hello","params":[],"jsonrpc":"2.0","id":1}"#; + let expected = + "{\"jsonrpc\":\"2.0\",\"result\":\"unknown origin / curl/7.16.3 via RPC\",\"id\":1}\n"; + let res = request( + server, + &format!( + "\ POST / HTTP/1.1\r\n\ Host: {}\r\n\ Content-Type: application/json\r\n\ @@ -109,38 +118,49 @@ mod tests { User-Agent: curl/7.16.3\r\n\ \r\n\ {} - ", address, req.len(), req) - ); - - // then - res.assert_status("HTTP/1.1 200 OK"); - assert_eq!(res.body, expected); - } - - #[test] - fn should_respond_valid_to_any_requested_header() { - // given - let (server, address) = serve(); - let headers = "Something, Anything, Xyz, 123, _?"; - - // when - let res = request(server, - &format!("\ + ", + address, + req.len(), + req + ), + ); + + // then + res.assert_status("HTTP/1.1 200 OK"); + assert_eq!(res.body, expected); + } + + #[test] + fn should_respond_valid_to_any_requested_header() { + // given + let (server, address) = serve(); + let headers = "Something, Anything, Xyz, 123, _?"; + + // when + let res = request( + server, + &format!( + "\ OPTIONS / HTTP/1.1\r\n\ Host: {}\r\n\ - Origin: http://parity.io\r\n\ + Origin: http://openethereum.github.io\r\n\ Content-Length: 0\r\n\ Content-Type: application/json\r\n\ Connection: close\r\n\ Access-Control-Request-Headers: {}\r\n\ \r\n\ - ", address, headers) - ); - - // then - assert_eq!(res.status, "HTTP/1.1 200 OK".to_owned()); - let expected = format!("access-control-allow-headers: {}", headers); - assert!(res.headers.contains(&expected), "Headers missing in {:?}", res.headers); - } - + ", + address, headers + ), + ); + + // then + assert_eq!(res.status, "HTTP/1.1 200 OK".to_owned()); + let expected = format!("access-control-allow-headers: {}", headers); + assert!( + res.headers.contains(&expected), + "Headers missing in {:?}", + res.headers + ); + } } diff --git a/rpc/src/tests/ws.rs b/rpc/src/tests/ws.rs index 3b607888205..ef6991afcac 100644 --- a/rpc/src/tests/ws.rs +++ b/rpc/src/tests/ws.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! WebSockets server tests. @@ -21,71 +21,82 @@ use std::sync::Arc; use jsonrpc_core::MetaIoHandler; use ws; +use tests::{ + helpers::{GuardedAuthCodes, Server}, + http_client, +}; use v1::{extractors, informant}; -use tests::helpers::{GuardedAuthCodes, Server}; -use tests::http_client; /// Setup a mock signer for tests pub fn serve() -> (Server, usize, GuardedAuthCodes) { - let address = "127.0.0.1:0".parse().unwrap(); - let io = MetaIoHandler::default(); - let authcodes = GuardedAuthCodes::default(); - let stats = Arc::new(informant::RpcStats::default()); - - let res = Server::new(|_| ::start_ws( - &address, - io, - ws::DomainsValidation::Disabled, - ws::DomainsValidation::Disabled, - 5, - extractors::WsExtractor::new(Some(&authcodes.path)), - extractors::WsExtractor::new(Some(&authcodes.path)), - extractors::WsStats::new(stats), - ).unwrap()); - let port = res.addr().port() as usize; - - (res, port, authcodes) + let address = "127.0.0.1:0".parse().unwrap(); + let io = MetaIoHandler::default(); + let authcodes = GuardedAuthCodes::default(); + let stats = Arc::new(informant::RpcStats::default()); + + let res = Server::new(|_| { + ::start_ws( + &address, + io, + ws::DomainsValidation::Disabled, + ws::DomainsValidation::Disabled, + 5, + extractors::WsExtractor::new(Some(&authcodes.path)), + extractors::WsExtractor::new(Some(&authcodes.path)), + extractors::WsStats::new(stats), + ) + .unwrap() + }); + let port = res.addr().port() as usize; + + (res, port, authcodes) } /// Test a single request to running server pub fn request(server: Server, request: &str) -> http_client::Response { - http_client::request(server.server.addr(), request) + http_client::request(server.server.addr(), request) } #[cfg(test)] mod testing { - use std::time; - use hash::keccak; - use super::{serve, request, http_client}; - - #[test] - fn should_not_redirect_to_parity_host() { - // given - let (server, port, _) = serve(); - - // when - let response = request(server, - &format!("\ + use super::{http_client, request, serve}; + use hash::keccak; + use std::time; + + #[test] + fn should_not_redirect_to_parity_host() { + // given + let (server, port, _) = serve(); + + // when + let response = request( + server, + &format!( + "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:{}\r\n\ Connection: close\r\n\ \r\n\ {{}} - ", port) - ); - - // then - assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); - } - - #[test] - fn should_block_if_authorization_is_incorrect() { - // given - let (server, port, _) = serve(); - - // when - let response = request(server, - &format!("\ + ", + port + ), + ); + + // then + assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); + } + + #[test] + fn should_block_if_authorization_is_incorrect() { + // given + let (server, port, _) = serve(); + + // when + let response = request( + server, + &format!( + "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:{}\r\n\ Connection: Upgrade\r\n\ @@ -94,26 +105,30 @@ mod testing { Sec-WebSocket-Version: 13\r\n\ \r\n\ {{}} - ", port) - ); - - // then - assert_eq!(response.status, "HTTP/1.1 403 Forbidden".to_owned()); - http_client::assert_security_headers_present(&response.headers, None); - } - - #[cfg(not(target_os = "windows"))] - #[test] - fn should_allow_if_authorization_is_correct() { - // given - let (server, port, mut authcodes) = serve(); - let code = authcodes.generate_new().unwrap().replace("-", ""); - authcodes.to_file(&authcodes.path).unwrap(); - let timestamp = time::UNIX_EPOCH.elapsed().unwrap().as_secs(); - - // when - let response = request(server, - &format!("\ + ", + port + ), + ); + + // then + assert_eq!(response.status, "HTTP/1.1 403 Forbidden".to_owned()); + http_client::assert_security_headers_present(&response.headers, None); + } + + #[cfg(not(target_os = "windows"))] + #[test] + fn should_allow_if_authorization_is_correct() { + // given + let (server, port, mut authcodes) = serve(); + let code = authcodes.generate_new().unwrap().replace("-", ""); + authcodes.to_file(&authcodes.path).unwrap(); + let timestamp = time::UNIX_EPOCH.elapsed().unwrap().as_secs(); + + // when + let response = request( + server, + &format!( + "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:{}\r\n\ Connection: Close\r\n\ @@ -123,27 +138,32 @@ mod testing { \r\n\ {{}} ", - port, - keccak(format!("{}:{}", code, timestamp)), - timestamp, - ) - ); - - // then - assert_eq!(response.status, "HTTP/1.1 101 Switching Protocols".to_owned()); - } - - #[test] - fn should_not_allow_initial_connection_even_once() { - // given - let (server, port, authcodes) = serve(); - let code = "initial"; - let timestamp = time::UNIX_EPOCH.elapsed().unwrap().as_secs(); - assert!(authcodes.is_empty()); - - // when - let response1 = http_client::request(server.addr(), - &format!("\ + port, + keccak(format!("{}:{}", code, timestamp)), + timestamp, + ), + ); + + // then + assert_eq!( + response.status, + "HTTP/1.1 101 Switching Protocols".to_owned() + ); + } + + #[test] + fn should_not_allow_initial_connection_even_once() { + // given + let (server, port, authcodes) = serve(); + let code = "initial"; + let timestamp = time::UNIX_EPOCH.elapsed().unwrap().as_secs(); + assert!(authcodes.is_empty()); + + // when + let response1 = http_client::request( + server.addr(), + &format!( + "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:{}\r\n\ Connection: Close\r\n\ @@ -153,14 +173,14 @@ mod testing { \r\n\ {{}} ", - port, - keccak(format!("{}:{}", code, timestamp)), - timestamp, - ) - ); - - // then - assert_eq!(response1.status, "HTTP/1.1 403 Forbidden".to_owned()); - http_client::assert_security_headers_present(&response1.headers, None); - } + port, + keccak(format!("{}:{}", code, timestamp)), + timestamp, + ), + ); + + // then + assert_eq!(response1.status, "HTTP/1.1 403 Forbidden".to_owned()); + http_client::assert_security_headers_present(&response1.headers, None); + } } diff --git a/rpc/src/v1/extractors.rs b/rpc/src/v1/extractors.rs index d3384c2c1d1..ef053fd6bb0 100644 --- a/rpc/src/v1/extractors.rs +++ b/rpc/src/v1/extractors.rs @@ -1,264 +1,282 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -//! Parity-specific metadata extractors. +//! OpenEthereum-specific metadata extractors. -use std::path::{Path, PathBuf}; -use std::sync::Arc; +use std::{ + path::{Path, PathBuf}, + sync::Arc, +}; use authcodes; +use ethereum_types::H256; use http_common::HttpMetaExtractor; use ipc; use jsonrpc_core as core; use jsonrpc_core::futures::future::Either; use jsonrpc_pubsub::Session; use ws; -use ethereum_types::H256; -use v1::{Metadata, Origin}; -use v1::informant::RpcStats; +use v1::{informant::RpcStats, Metadata, Origin}; /// Common HTTP & IPC metadata extractor. pub struct RpcExtractor; impl HttpMetaExtractor for RpcExtractor { - type Metadata = Metadata; - - fn read_metadata(&self, origin: Option, user_agent: Option) -> Metadata { - Metadata { - origin: Origin::Rpc( - format!("{} / {}", - origin.unwrap_or_else(|| "unknown origin".to_string()), - user_agent.unwrap_or_else(|| "unknown agent".to_string())) - ), - session: None, - } - } + type Metadata = Metadata; + + fn read_metadata(&self, origin: Option, user_agent: Option) -> Metadata { + Metadata { + origin: Origin::Rpc(format!( + "{} / {}", + origin.unwrap_or_else(|| "unknown origin".to_string()), + user_agent.unwrap_or_else(|| "unknown agent".to_string()) + )), + session: None, + } + } } impl ipc::MetaExtractor for RpcExtractor { - fn extract(&self, req: &ipc::RequestContext) -> Metadata { - Metadata { - origin: Origin::Ipc(req.session_id.into()), - session: Some(Arc::new(Session::new(req.sender.clone()))), - } - } + fn extract(&self, req: &ipc::RequestContext) -> Metadata { + Metadata { + origin: Origin::Ipc(req.session_id.into()), + session: Some(Arc::new(Session::new(req.sender.clone()))), + } + } } /// WebSockets server metadata extractor and request middleware. pub struct WsExtractor { - authcodes_path: Option, + authcodes_path: Option, } impl WsExtractor { - /// Creates new `WsExtractor` with given authcodes path. - pub fn new(path: Option<&Path>) -> Self { - WsExtractor { - authcodes_path: path.map(ToOwned::to_owned), - } - } + /// Creates new `WsExtractor` with given authcodes path. + pub fn new(path: Option<&Path>) -> Self { + WsExtractor { + authcodes_path: path.map(ToOwned::to_owned), + } + } } impl ws::MetaExtractor for WsExtractor { - fn extract(&self, req: &ws::RequestContext) -> Metadata { - let id = req.session_id as u64; - - let origin = match self.authcodes_path { - Some(ref path) => { - let authorization = req.protocols.get(0).and_then(|p| auth_token_hash(&path, p, true)); - match authorization { - Some(id) => Origin::Signer { session: id }, - None => Origin::Ws { session: id.into() }, - } - }, - None => Origin::Ws { session: id.into() }, - }; - let session = Some(Arc::new(Session::new(req.sender()))); - Metadata { - origin, - session, - } - } + fn extract(&self, req: &ws::RequestContext) -> Metadata { + let id = req.session_id as u64; + + let origin = match self.authcodes_path { + Some(ref path) => { + let authorization = req + .protocols + .get(0) + .and_then(|p| auth_token_hash(&path, p, true)); + match authorization { + Some(id) => Origin::Signer { session: id }, + None => Origin::Ws { session: id.into() }, + } + } + None => Origin::Ws { session: id.into() }, + }; + let session = Some(Arc::new(Session::new(req.sender()))); + Metadata { origin, session } + } } impl ws::RequestMiddleware for WsExtractor { - fn process(&self, req: &ws::ws::Request) -> ws::MiddlewareAction { - use self::ws::ws::Response; - - // Reply with 200 OK to HEAD requests. - if req.method() == "HEAD" { - let mut response = Response::new(200, "OK", vec![]); - add_security_headers(&mut response); - return Some(response).into(); - } - - // Display WS info. - if req.header("sec-websocket-key").is_none() { - let mut response = Response::new(200, "OK", b"WebSocket interface is active. Open WS connection to access RPC.".to_vec()); - add_security_headers(&mut response); - return Some(response).into(); - } - - // If protocol is provided it needs to be valid. - let protocols = req.protocols().ok().unwrap_or_else(Vec::new); - if let Some(ref path) = self.authcodes_path { - if protocols.len() == 1 { - let authorization = auth_token_hash(&path, protocols[0], false); - if authorization.is_none() { - warn!( - "Blocked connection from {} using invalid token.", - req.header("origin").and_then(|e| ::std::str::from_utf8(e).ok()).unwrap_or("Unknown Origin") - ); - let mut response = Response::new(403, "Forbidden", vec![]); - add_security_headers(&mut response); - return Some(response).into(); - } - } - } - - // Otherwise just proceed. - ws::MiddlewareAction::Proceed - } + fn process(&self, req: &ws::ws::Request) -> ws::MiddlewareAction { + use self::ws::ws::Response; + + // Reply with 200 OK to HEAD requests. + if req.method() == "HEAD" { + let mut response = Response::new(200, "OK", vec![]); + add_security_headers(&mut response); + return Some(response).into(); + } + + // Display WS info. + if req.header("sec-websocket-key").is_none() { + let mut response = Response::new( + 200, + "OK", + b"WebSocket interface is active. Open WS connection to access RPC.".to_vec(), + ); + add_security_headers(&mut response); + return Some(response).into(); + } + + // If protocol is provided it needs to be valid. + let protocols = req.protocols().ok().unwrap_or_else(Vec::new); + if let Some(ref path) = self.authcodes_path { + if protocols.len() == 1 { + let authorization = auth_token_hash(&path, protocols[0], false); + if authorization.is_none() { + warn!( + "Blocked connection from {} using invalid token.", + req.header("origin") + .and_then(|e| ::std::str::from_utf8(e).ok()) + .unwrap_or("Unknown Origin") + ); + let mut response = Response::new(403, "Forbidden", vec![]); + add_security_headers(&mut response); + return Some(response).into(); + } + } + } + + // Otherwise just proceed. + ws::MiddlewareAction::Proceed + } } fn add_security_headers(res: &mut ws::ws::Response) { - let headers = res.headers_mut(); - headers.push(("X-Frame-Options".into(), b"SAMEORIGIN".to_vec())); - headers.push(("X-XSS-Protection".into(), b"1; mode=block".to_vec())); - headers.push(("X-Content-Type-Options".into(), b"nosniff".to_vec())); - headers.push(("Content-Security-Policy".into(), - b"default-src 'self';form-action 'none';block-all-mixed-content;sandbox allow-scripts;".to_vec() - )); + let headers = res.headers_mut(); + headers.push(("X-Frame-Options".into(), b"SAMEORIGIN".to_vec())); + headers.push(("X-XSS-Protection".into(), b"1; mode=block".to_vec())); + headers.push(("X-Content-Type-Options".into(), b"nosniff".to_vec())); + headers.push(( + "Content-Security-Policy".into(), + b"default-src 'self';form-action 'none';block-all-mixed-content;sandbox allow-scripts;" + .to_vec(), + )); } fn auth_token_hash(codes_path: &Path, protocol: &str, save_file: bool) -> Option { - let mut split = protocol.split('_'); - let auth = split.next().and_then(|v| v.parse().ok()); - let time = split.next().and_then(|v| u64::from_str_radix(v, 10).ok()); - - if let (Some(auth), Some(time)) = (auth, time) { - // Check if the code is valid - return authcodes::AuthCodes::from_file(codes_path) - .ok() - .and_then(|mut codes| { - // remove old tokens - codes.clear_garbage(); - - let res = codes.is_valid(&auth, time); - - if save_file { - // make sure to save back authcodes - it might have been modified - if codes.to_file(codes_path).is_err() { - warn!(target: "signer", "Couldn't save authorization codes to file."); - } - } - - if res { - Some(auth) - } else { - None - } - }) - } - - None + let mut split = protocol.split('_'); + let auth = split.next().and_then(|v| v.parse().ok()); + let time = split.next().and_then(|v| u64::from_str_radix(v, 10).ok()); + + if let (Some(auth), Some(time)) = (auth, time) { + // Check if the code is valid + return authcodes::AuthCodes::from_file(codes_path) + .ok() + .and_then(|mut codes| { + // remove old tokens + codes.clear_garbage(); + + let res = codes.is_valid(&auth, time); + + if save_file { + // make sure to save back authcodes - it might have been modified + if codes.to_file(codes_path).is_err() { + warn!(target: "signer", "Couldn't save authorization codes to file."); + } + } + + if res { + Some(auth) + } else { + None + } + }); + } + + None } /// WebSockets RPC usage statistics. pub struct WsStats { - stats: Arc, + stats: Arc, } impl WsStats { - /// Creates new WS usage tracker. - pub fn new(stats: Arc) -> Self { - WsStats { - stats, - } - } + /// Creates new WS usage tracker. + pub fn new(stats: Arc) -> Self { + WsStats { stats } + } } impl ws::SessionStats for WsStats { - fn open_session(&self, _id: ws::SessionId) { - self.stats.open_session() - } + fn open_session(&self, _id: ws::SessionId) { + self.stats.open_session() + } - fn close_session(&self, _id: ws::SessionId) { - self.stats.close_session() - } + fn close_session(&self, _id: ws::SessionId) { + self.stats.close_session() + } } /// WebSockets middleware dispatching requests to different handles dependning on metadata. pub struct WsDispatcher> { - full_handler: core::MetaIoHandler, + full_handler: core::MetaIoHandler, } impl> WsDispatcher { - /// Create new `WsDispatcher` with given full handler. - pub fn new(full_handler: core::MetaIoHandler) -> Self { - WsDispatcher { - full_handler, - } - } + /// Create new `WsDispatcher` with given full handler. + pub fn new(full_handler: core::MetaIoHandler) -> Self { + WsDispatcher { full_handler } + } } impl> core::Middleware for WsDispatcher { - type Future = Either< - core::FutureRpcResult, - core::FutureResponse, - >; - type CallFuture = core::middleware::NoopCallFuture; - - fn on_request(&self, request: core::Request, meta: Metadata, process: F) - -> Either - where - F: FnOnce(core::Request, Metadata) -> X, - X: core::futures::Future, Error=()> + Send + 'static, - { - let use_full = match &meta.origin { - Origin::Signer { .. } => true, - _ => false, - }; - - if use_full { - Either::A(Either::A(self.full_handler.handle_rpc_request(request, meta))) - } else { - Either::B(process(request, meta)) - } - } + type Future = Either, core::FutureResponse>; + type CallFuture = core::middleware::NoopCallFuture; + + fn on_request( + &self, + request: core::Request, + meta: Metadata, + process: F, + ) -> Either + where + F: FnOnce(core::Request, Metadata) -> X, + X: core::futures::Future, Error = ()> + Send + 'static, + { + let use_full = match &meta.origin { + Origin::Signer { .. } => true, + _ => false, + }; + + if use_full { + Either::A(Either::A( + self.full_handler.handle_rpc_request(request, meta), + )) + } else { + Either::B(process(request, meta)) + } + } } #[cfg(test)] mod tests { - use super::RpcExtractor; - use {HttpMetaExtractor, Origin}; - - #[test] - fn should_extract_rpc_origin() { - // given - let extractor = RpcExtractor; - - // when - let meta1 = extractor.read_metadata(None, None); - let meta2 = extractor.read_metadata(None, Some("http://parity.io".to_owned())); - let meta3 = extractor.read_metadata(None, Some("http://parity.io".to_owned())); - - // then - assert_eq!(meta1.origin, Origin::Rpc("unknown origin / unknown agent".into())); - assert_eq!(meta2.origin, Origin::Rpc("unknown origin / http://parity.io".into())); - assert_eq!(meta3.origin, Origin::Rpc("unknown origin / http://parity.io".into())); - } + use super::RpcExtractor; + use HttpMetaExtractor; + use Origin; + + #[test] + fn should_extract_rpc_origin() { + // given + let extractor = RpcExtractor; + + // when + let meta1 = extractor.read_metadata(None, None); + let meta2 = extractor.read_metadata(None, Some("http://openethereum.github.io".to_owned())); + let meta3 = extractor.read_metadata(None, Some("http://openethereum.github.io".to_owned())); + + // then + assert_eq!( + meta1.origin, + Origin::Rpc("unknown origin / unknown agent".into()) + ); + assert_eq!( + meta2.origin, + Origin::Rpc("unknown origin / http://openethereum.github.io".into()) + ); + assert_eq!( + meta3.origin, + Origin::Rpc("unknown origin / http://openethereum.github.io".into()) + ); + } } diff --git a/rpc/src/v1/helpers/block_import.rs b/rpc/src/v1/helpers/block_import.rs index 3fd5d9fff7d..b979334284e 100644 --- a/rpc/src/v1/helpers/block_import.rs +++ b/rpc/src/v1/helpers/block_import.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Block import analysis functions. @@ -21,47 +21,54 @@ use sync::SyncState; /// Check if client is during major sync or during block import and allows defining whether 'waiting for peers' should /// be considered a syncing state. -pub fn is_major_importing_or_waiting(sync_state: Option, queue_info: BlockQueueInfo, waiting_is_syncing_state: bool) -> bool { - let is_syncing_state = sync_state.map_or(false, |s| match s { - SyncState::Idle | SyncState::NewBlocks => false, - SyncState::WaitingPeers if !waiting_is_syncing_state => false, - _ => true, - }); - let is_verifying = queue_info.unverified_queue_size + queue_info.verified_queue_size > 3; - is_verifying || is_syncing_state +pub fn is_major_importing_or_waiting( + sync_state: Option, + queue_info: BlockQueueInfo, + waiting_is_syncing_state: bool, +) -> bool { + let is_syncing_state = sync_state.map_or(false, |s| match s { + SyncState::Idle | SyncState::NewBlocks => false, + SyncState::WaitingPeers if !waiting_is_syncing_state => false, + _ => true, + }); + let is_verifying = queue_info.unverified_queue_size + queue_info.verified_queue_size > 3; + is_verifying || is_syncing_state } /// Check if client is during major sync or during block import. pub fn is_major_importing(sync_state: Option, queue_info: BlockQueueInfo) -> bool { - is_major_importing_or_waiting(sync_state, queue_info, true) + is_major_importing_or_waiting(sync_state, queue_info, true) } #[cfg(test)] mod tests { - use ethcore::client::BlockQueueInfo; - use sync::SyncState; - use super::is_major_importing; + use super::is_major_importing; + use ethcore::client::BlockQueueInfo; + use sync::SyncState; - fn queue_info(unverified: usize, verified: usize) -> BlockQueueInfo { - BlockQueueInfo { - unverified_queue_size: unverified, - verified_queue_size: verified, - verifying_queue_size: 0, - max_queue_size: 1000, - max_mem_use: 1000, - mem_used: 500 - } - } + fn queue_info(unverified: usize, verified: usize) -> BlockQueueInfo { + BlockQueueInfo { + unverified_queue_size: unverified, + verified_queue_size: verified, + verifying_queue_size: 0, + max_queue_size: 1000, + max_mem_use: 1000, + mem_used: 500, + } + } - #[test] - fn is_still_verifying() { - assert!(!is_major_importing(None, queue_info(2, 1))); - assert!(is_major_importing(None, queue_info(2, 2))); - } + #[test] + fn is_still_verifying() { + assert!(!is_major_importing(None, queue_info(2, 1))); + assert!(is_major_importing(None, queue_info(2, 2))); + } - #[test] - fn is_synced_state() { - assert!(is_major_importing(Some(SyncState::Blocks), queue_info(0, 0))); - assert!(!is_major_importing(Some(SyncState::Idle), queue_info(0, 0))); - } + #[test] + fn is_synced_state() { + assert!(is_major_importing( + Some(SyncState::Blocks), + queue_info(0, 0) + )); + assert!(!is_major_importing(Some(SyncState::Idle), queue_info(0, 0))); + } } diff --git a/rpc/src/v1/helpers/deprecated.rs b/rpc/src/v1/helpers/deprecated.rs index 49e9d8b0742..ef248aca82f 100644 --- a/rpc/src/v1/helpers/deprecated.rs +++ b/rpc/src/v1/helpers/deprecated.rs @@ -1,33 +1,34 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. -// This file is part of Parity. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity. If not, see . +// along with OpenEthereum. If not, see . //! Deprecation notice for RPC methods. //! //! Displays a warning but avoids spamming the log. use std::{ - collections::HashMap, - time::{Duration, Instant}, + collections::HashMap, + time::{Duration, Instant}, }; use parking_lot::RwLock; /// Deprecation messages pub mod msgs { - pub const ACCOUNTS: Option<&str> = Some("Account management is being phased out see #9997 for alternatives."); + pub const ACCOUNTS: Option<&str> = + Some("Account management is being phased out see #9997 for alternatives."); } type MethodName = &'static str; @@ -36,76 +37,84 @@ const PRINT_INTERVAL: Duration = Duration::from_secs(60); /// Displays a deprecation notice without spamming the log. pub struct DeprecationNotice Instant> { - now: T, - next_warning_at: RwLock>, - printer: Box) + Send + Sync>, + now: T, + next_warning_at: RwLock>, + printer: Box) + Send + Sync>, } impl Default for DeprecationNotice { - fn default() -> Self { - Self::new(Instant::now, |method, more| { - let more = more.map(|x| format!(": {}", x)).unwrap_or_else(|| ".".into()); - warn!(target: "rpc", "{} is deprecated and will be removed in future versions{}", method, more); - }) - } + fn default() -> Self { + Self::new(Instant::now, |method, more| { + let more = more + .map(|x| format!(": {}", x)) + .unwrap_or_else(|| ".".into()); + warn!(target: "rpc", "{} is deprecated and will be removed in future versions{}", method, more); + }) + } } impl Instant> DeprecationNotice { - /// Create new deprecation notice printer with custom display and interval. - pub fn new(now: N, printer: T) -> Self where - T: Fn(MethodName, Option<&str>) + Send + Sync + 'static, - { - DeprecationNotice { - now, - next_warning_at: Default::default(), - printer: Box::new(printer), - } - } - - /// Print deprecation notice for given method and with some additional details (explanations). - pub fn print<'a, T: Into>>(&self, method: MethodName, details: T) { - let now = (self.now)(); - match self.next_warning_at.read().get(method) { - Some(next) if *next > now => return, - _ => {}, - } - - self.next_warning_at.write().insert(method.to_owned(), now + PRINT_INTERVAL); - (self.printer)(method, details.into()); - } + /// Create new deprecation notice printer with custom display and interval. + pub fn new(now: N, printer: T) -> Self + where + T: Fn(MethodName, Option<&str>) + Send + Sync + 'static, + { + DeprecationNotice { + now, + next_warning_at: Default::default(), + printer: Box::new(printer), + } + } + + /// Print deprecation notice for given method and with some additional details (explanations). + pub fn print<'a, T: Into>>(&self, method: MethodName, details: T) { + let now = (self.now)(); + match self.next_warning_at.read().get(method) { + Some(next) if *next > now => return, + _ => {} + } + + self.next_warning_at + .write() + .insert(method.to_owned(), now + PRINT_INTERVAL); + (self.printer)(method, details.into()); + } } #[cfg(test)] mod tests { - use super::*; - - use std::sync::Arc; - - #[test] - fn should_throttle_printing() { - let saved = Arc::new(RwLock::new(None)); - let s = saved.clone(); - let printer = move |method: MethodName, more: Option<&str>| { - *s.write() = Some((method, more.map(|s| s.to_owned()))); - }; - - let now = Arc::new(RwLock::new(Instant::now())); - let n = now.clone(); - let get_now = || n.read().clone(); - let notice = DeprecationNotice::new(get_now, printer); - - let details = Some("See issue #123456"); - notice.print("eth_test", details.clone()); - // printer shouldn't be called - notice.print("eth_test", None); - assert_eq!(saved.read().clone().unwrap(), ("eth_test", details.as_ref().map(|x| x.to_string()))); - // but calling a different method is fine - notice.print("eth_test2", None); - assert_eq!(saved.read().clone().unwrap(), ("eth_test2", None)); - - // wait and call again - *now.write() = Instant::now() + PRINT_INTERVAL; - notice.print("eth_test", None); - assert_eq!(saved.read().clone().unwrap(), ("eth_test", None)); - } + use super::*; + + use std::sync::Arc; + + #[test] + fn should_throttle_printing() { + let saved = Arc::new(RwLock::new(None)); + let s = saved.clone(); + let printer = move |method: MethodName, more: Option<&str>| { + *s.write() = Some((method, more.map(|s| s.to_owned()))); + }; + + let now = Arc::new(RwLock::new(Instant::now())); + let n = now.clone(); + let get_now = || n.read().clone(); + let notice = DeprecationNotice::new(get_now, printer); + + let details = Some("See issue #123456"); + notice.print("eth_test", details.clone()); + // printer shouldn't be called + notice.print("eth_test", None); + assert_eq!( + saved.read().clone().unwrap(), + ("eth_test", details.as_ref().map(|x| x.to_string())) + ); + // but calling a different method is fine + notice.print("eth_test2", None); + assert_eq!(saved.read().clone().unwrap(), ("eth_test2", None)); + + // wait and call again + *now.write() = Instant::now() + PRINT_INTERVAL; + notice.print("eth_test", None); + assert_eq!(saved.read().clone().unwrap(), ("eth_test", None)); + } } diff --git a/rpc/src/v1/helpers/dispatch/full.rs b/rpc/src/v1/helpers/dispatch/full.rs index d958416cbf2..fda6d4701f8 100644 --- a/rpc/src/v1/helpers/dispatch/full.rs +++ b/rpc/src/v1/helpers/dispatch/full.rs @@ -1,151 +1,180 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use std::sync::Arc; -use ethcore::client::BlockChainClient; -use ethcore::miner::{self, MinerService}; -use ethereum_types::{H256, U256, Address}; -use types::transaction::{SignedTransaction, PendingTransaction}; +use ethcore::{ + client::BlockChainClient, + miner::{self, MinerService}, +}; +use ethereum_types::{Address, H256, U256}; use parking_lot::Mutex; - -use jsonrpc_core::{BoxFuture, Result}; -use jsonrpc_core::futures::{future, Future, IntoFuture}; -use v1::helpers::{errors, nonce, TransactionRequest, FilledTransactionRequest}; -use v1::types::{RichRawTransaction as RpcRichRawTransaction}; - -use super::prospective_signer::ProspectiveSigner; -use super::{Dispatcher, Accounts, SignWith, PostSign, default_gas_price}; +use types::transaction::{PendingTransaction, SignedTransaction}; + +use jsonrpc_core::{ + futures::{future, Future, IntoFuture}, + BoxFuture, Result, +}; +use v1::{ + helpers::{errors, nonce, FilledTransactionRequest, TransactionRequest}, + types::RichRawTransaction as RpcRichRawTransaction, +}; + +use super::{ + default_gas_price, prospective_signer::ProspectiveSigner, Accounts, Dispatcher, PostSign, + SignWith, +}; /// A dispatcher which uses references to a client and miner in order to sign /// requests locally. #[derive(Debug)] pub struct FullDispatcher { - client: Arc, - miner: Arc, - nonces: Arc>, - gas_price_percentile: usize, + client: Arc, + miner: Arc, + nonces: Arc>, + gas_price_percentile: usize, } impl FullDispatcher { - /// Create a `FullDispatcher` from Arc references to a client and miner. - pub fn new( - client: Arc, - miner: Arc, - nonces: Arc>, - gas_price_percentile: usize, - ) -> Self { - FullDispatcher { - client, - miner, - nonces, - gas_price_percentile, - } - } + /// Create a `FullDispatcher` from Arc references to a client and miner. + pub fn new( + client: Arc, + miner: Arc, + nonces: Arc>, + gas_price_percentile: usize, + ) -> Self { + FullDispatcher { + client, + miner, + nonces, + gas_price_percentile, + } + } } impl Clone for FullDispatcher { - fn clone(&self) -> Self { - FullDispatcher { - client: self.client.clone(), - miner: self.miner.clone(), - nonces: self.nonces.clone(), - gas_price_percentile: self.gas_price_percentile, - } - } + fn clone(&self) -> Self { + FullDispatcher { + client: self.client.clone(), + miner: self.miner.clone(), + nonces: self.nonces.clone(), + gas_price_percentile: self.gas_price_percentile, + } + } } impl FullDispatcher { - fn state_nonce(&self, from: &Address) -> U256 { - self.miner.next_nonce(&*self.client, from) - } - - /// Post transaction to the network. - /// - /// If transaction is trusted we are more likely to assume it is coming from a local account. - pub fn dispatch_transaction(client: &C, miner: &M, signed_transaction: PendingTransaction, trusted: bool) -> Result { - let hash = signed_transaction.transaction.hash(); - - // use `import_claimed_local_transaction` so we can decide (based on config flags) if we want to treat - // it as local or not. Nodes with public RPC interfaces will want these transactions to be treated like - // external transactions. - miner.import_claimed_local_transaction(client, signed_transaction, trusted) - .map_err(errors::transaction) - .map(|_| hash) - } + fn state_nonce(&self, from: &Address) -> U256 { + self.miner.next_nonce(&*self.client, from) + } + + /// Post transaction to the network. + /// + /// If transaction is trusted we are more likely to assume it is coming from a local account. + pub fn dispatch_transaction( + client: &C, + miner: &M, + signed_transaction: PendingTransaction, + trusted: bool, + ) -> Result { + let hash = signed_transaction.transaction.hash(); + + // use `import_claimed_local_transaction` so we can decide (based on config flags) if we want to treat + // it as local or not. Nodes with public RPC interfaces will want these transactions to be treated like + // external transactions. + miner + .import_claimed_local_transaction(client, signed_transaction, trusted) + .map_err(errors::transaction) + .map(|_| hash) + } } -impl Dispatcher for FullDispatcher { - fn fill_optional_fields(&self, request: TransactionRequest, default_sender: Address, force_nonce: bool) - -> BoxFuture - { - let request = request; - let from = request.from.unwrap_or(default_sender); - let nonce = if force_nonce { - request.nonce.or_else(|| Some(self.state_nonce(&from))) - } else { - request.nonce - }; - - Box::new(future::ok(FilledTransactionRequest { - from, - used_default_from: request.from.is_none(), - to: request.to, - nonce, - gas_price: request.gas_price.unwrap_or_else(|| { - default_gas_price(&*self.client, &*self.miner, self.gas_price_percentile) - }), - gas: request.gas.unwrap_or_else(|| self.miner.sensible_gas_limit()), - value: request.value.unwrap_or_else(|| 0.into()), - data: request.data.unwrap_or_else(Vec::new), - condition: request.condition, - })) - } - - fn sign

( - &self, - filled: FilledTransactionRequest, - signer: &Arc, - password: SignWith, - post_sign: P, - ) -> BoxFuture - where - P: PostSign + 'static, - ::Future: Send, - { - let chain_id = self.client.signing_chain_id(); - - if let Some(nonce) = filled.nonce { - let future = signer.sign_transaction(filled, chain_id, nonce, password) - .into_future() - .and_then(move |signed| post_sign.execute(signed)); - Box::new(future) - } else { - let state = self.state_nonce(&filled.from); - let reserved = self.nonces.lock().reserve(filled.from, state); - - Box::new(ProspectiveSigner::new(signer.clone(), filled, chain_id, reserved, password, post_sign)) - } - } - - fn enrich(&self, signed_transaction: SignedTransaction) -> RpcRichRawTransaction { - RpcRichRawTransaction::from_signed(signed_transaction) - } - - fn dispatch_transaction(&self, signed_transaction: PendingTransaction) -> Result { - Self::dispatch_transaction(&*self.client, &*self.miner, signed_transaction, true) - } +impl Dispatcher + for FullDispatcher +{ + fn fill_optional_fields( + &self, + request: TransactionRequest, + default_sender: Address, + force_nonce: bool, + ) -> BoxFuture { + let request = request; + let from = request.from.unwrap_or(default_sender); + let nonce = if force_nonce { + request.nonce.or_else(|| Some(self.state_nonce(&from))) + } else { + request.nonce + }; + + Box::new(future::ok(FilledTransactionRequest { + from, + used_default_from: request.from.is_none(), + to: request.to, + nonce, + gas_price: request.gas_price.unwrap_or_else(|| { + default_gas_price(&*self.client, &*self.miner, self.gas_price_percentile) + }), + gas: request + .gas + .unwrap_or_else(|| self.miner.sensible_gas_limit()), + value: request.value.unwrap_or_else(|| 0.into()), + data: request.data.unwrap_or_else(Vec::new), + condition: request.condition, + })) + } + + fn sign

( + &self, + filled: FilledTransactionRequest, + signer: &Arc, + password: SignWith, + post_sign: P, + ) -> BoxFuture + where + P: PostSign + 'static, + ::Future: Send, + { + let chain_id = self.client.signing_chain_id(); + + if let Some(nonce) = filled.nonce { + let future = signer + .sign_transaction(filled, chain_id, nonce, password) + .into_future() + .and_then(move |signed| post_sign.execute(signed)); + Box::new(future) + } else { + let state = self.state_nonce(&filled.from); + let reserved = self.nonces.lock().reserve(filled.from, state); + + Box::new(ProspectiveSigner::new( + signer.clone(), + filled, + chain_id, + reserved, + password, + post_sign, + )) + } + } + + fn enrich(&self, signed_transaction: SignedTransaction) -> RpcRichRawTransaction { + RpcRichRawTransaction::from_signed(signed_transaction) + } + + fn dispatch_transaction(&self, signed_transaction: PendingTransaction) -> Result { + Self::dispatch_transaction(&*self.client, &*self.miner, signed_transaction, true) + } } diff --git a/rpc/src/v1/helpers/dispatch/light.rs b/rpc/src/v1/helpers/dispatch/light.rs deleted file mode 100644 index 88f9fafcf16..00000000000 --- a/rpc/src/v1/helpers/dispatch/light.rs +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; - -use ethereum_types::{H256, Address, U256}; -use light::TransactionQueue as LightTransactionQueue; -use light::cache::Cache as LightDataCache; -use light::client::LightChainClient; -use light::on_demand::{request, OnDemandRequester}; -use parking_lot::{Mutex, RwLock}; -use stats::Corpus; -use sync::{LightSyncProvider, LightNetworkDispatcher, ManageNetwork}; -use types::basic_account::BasicAccount; -use types::ids::BlockId; -use types::transaction::{SignedTransaction, PendingTransaction, Error as TransactionError}; - -use jsonrpc_core::{BoxFuture, Result}; -use jsonrpc_core::futures::{future, Future, IntoFuture}; -use jsonrpc_core::futures::future::Either; -use v1::helpers::{errors, nonce, TransactionRequest, FilledTransactionRequest}; -use v1::types::{RichRawTransaction as RpcRichRawTransaction,}; - -use super::{Dispatcher, Accounts, SignWith, PostSign}; - -/// Dispatcher for light clients -- fetches default gas price, next nonce, etc. from network. -pub struct LightDispatcher -where - S: LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, - OD: OnDemandRequester + 'static -{ - /// Sync service. - pub sync: Arc, - /// Header chain client. - pub client: Arc, - /// On-demand request service. - pub on_demand: Arc, - /// Data cache. - pub cache: Arc>, - /// Transaction queue. - pub transaction_queue: Arc>, - /// Nonce reservations - pub nonces: Arc>, - /// Gas Price percentile value used as default gas price. - pub gas_price_percentile: usize, -} - -impl LightDispatcher -where - S: LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, - OD: OnDemandRequester + 'static -{ - /// Create a new `LightDispatcher` from its requisite parts. - /// - /// For correct operation, the OnDemand service is assumed to be registered as a network handler, - pub fn new( - sync: Arc, - client: Arc, - on_demand: Arc, - cache: Arc>, - transaction_queue: Arc>, - nonces: Arc>, - gas_price_percentile: usize, - ) -> Self { - LightDispatcher { - sync, - client, - on_demand, - cache, - transaction_queue, - nonces, - gas_price_percentile, - } - } - - /// Get a recent gas price corpus. - // TODO: this could be `impl Trait`. - pub fn gas_price_corpus(&self) -> BoxFuture> { - fetch_gas_price_corpus( - self.sync.clone(), - self.client.clone(), - self.on_demand.clone(), - self.cache.clone(), - ) - } - - /// Get an account's state - fn account(&self, addr: Address) -> BoxFuture> { - let best_header = self.client.best_block_header(); - let account_future = self.sync.with_context(|ctx| self.on_demand.request(ctx, request::Account { - header: best_header.into(), - address: addr, - }).expect("no back-references; therefore all back-references valid; qed")); - - match account_future { - Some(response) => Box::new(response.map_err(|_| errors::no_light_peers())), - None => Box::new(future::err(errors::network_disabled())), - } - } - - /// Get an account's next nonce. - pub fn next_nonce(&self, addr: Address) -> BoxFuture { - let account_start_nonce = self.client.engine().account_start_nonce(self.client.best_block_header().number()); - Box::new(self.account(addr) - .and_then(move |maybe_account| { - future::ok(maybe_account.map_or(account_start_nonce, |account| account.nonce)) - }) - ) - } -} - -impl Clone for LightDispatcher -where - S: LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, - OD: OnDemandRequester + 'static -{ - fn clone(&self) -> Self { - Self { - sync: self.sync.clone(), - client: self.client.clone(), - on_demand: self.on_demand.clone(), - cache: self.cache.clone(), - transaction_queue: self.transaction_queue.clone(), - nonces: self.nonces.clone(), - gas_price_percentile: self.gas_price_percentile - } - } -} - -impl Dispatcher for LightDispatcher -where - S: LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, - OD: OnDemandRequester + 'static -{ - // Ignore the `force_nonce` flag in order to always query the network when fetching the nonce and - // the account state. If the nonce is specified in the transaction use that nonce instead but do the - // network request anyway to the account state (balance) - fn fill_optional_fields(&self, request: TransactionRequest, default_sender: Address, _force_nonce: bool) - -> BoxFuture - { - const DEFAULT_GAS_PRICE: U256 = U256([0, 0, 0, 21_000_000]); - - let gas_limit = self.client.best_block_header().gas_limit(); - let request_gas_price = request.gas_price; - let from = request.from.unwrap_or(default_sender); - - let with_gas_price = move |gas_price| { - let request = request; - FilledTransactionRequest { - from, - used_default_from: request.from.is_none(), - to: request.to, - nonce: request.nonce, - gas_price, - gas: request.gas.unwrap_or_else(|| gas_limit / 3), - value: request.value.unwrap_or_default(), - data: request.data.unwrap_or_else(Vec::new), - condition: request.condition, - } - }; - - // fast path for known gas price. - let gas_price_percentile = self.gas_price_percentile; - let gas_price = match request_gas_price { - Some(gas_price) => Either::A(future::ok(with_gas_price(gas_price))), - None => Either::B(fetch_gas_price_corpus( - self.sync.clone(), - self.client.clone(), - self.on_demand.clone(), - self.cache.clone() - ).and_then(move |corp| match corp.percentile(gas_price_percentile) { - Some(percentile) => Ok(*percentile), - None => Ok(DEFAULT_GAS_PRICE), // fall back to default on error. - }).map(with_gas_price)) - }; - - let future_account = self.account(from); - - Box::new(gas_price.and_then(move |mut filled| { - future_account - .and_then(move |maybe_account| { - let cost = filled.value.saturating_add(filled.gas.saturating_mul(filled.gas_price)); - match maybe_account { - Some(ref account) if cost > account.balance => { - Err(errors::transaction(TransactionError::InsufficientBalance { - balance: account.balance, - cost, - })) - } - Some(account) => { - if filled.nonce.is_none() { - filled.nonce = Some(account.nonce); - } - Ok(filled) - } - None => Err(errors::account("Account not found", "")), - } - }) - })) - } - - fn sign

( - &self, - filled: FilledTransactionRequest, - signer: &Arc, - password: SignWith, - post_sign: P - ) -> BoxFuture - where - P: PostSign + 'static, - ::Future: Send, - { - let chain_id = self.client.signing_chain_id(); - let nonce = filled.nonce.expect("nonce is always provided; qed"); - let future = signer.sign_transaction(filled, chain_id, nonce, password) - .into_future() - .and_then(move |signed| post_sign.execute(signed)); - Box::new(future) - } - - fn enrich(&self, signed_transaction: SignedTransaction) -> RpcRichRawTransaction { - RpcRichRawTransaction::from_signed(signed_transaction) - } - - fn dispatch_transaction(&self, signed_transaction: PendingTransaction) -> Result { - let hash = signed_transaction.transaction.hash(); - - self.transaction_queue.write().import(signed_transaction) - .map_err(errors::transaction) - .map(|_| hash) - } -} - -/// Get a recent gas price corpus. -// TODO: this could be `impl Trait`. -pub fn fetch_gas_price_corpus( - sync: Arc, - client: Arc, - on_demand: Arc, - cache: Arc>, -) -> BoxFuture> -where - S: LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, - OD: OnDemandRequester + 'static -{ - const GAS_PRICE_SAMPLE_SIZE: usize = 100; - - if let Some(cached) = { cache.lock().gas_price_corpus() } { - return Box::new(future::ok(cached)) - } - - let cache = cache.clone(); - let eventual_corpus = sync.with_context(|ctx| { - // get some recent headers with gas used, - // and request each of the blocks from the network. - let block_requests = client.ancestry_iter(BlockId::Latest) - .filter(|hdr| hdr.gas_used() != U256::default()) - .take(GAS_PRICE_SAMPLE_SIZE) - .map(|hdr| request::Body(hdr.into())) - .collect::>(); - - // when the blocks come in, collect gas prices into a vector - on_demand.request(ctx, block_requests) - .expect("no back-references; therefore all back-references are valid; qed") - .map(|bodies| { - bodies.into_iter().fold(Vec::new(), |mut v, block| { - for t in block.transaction_views().iter() { - v.push(t.gas_price()) - } - v - }) - }) - .map(move |prices| { - // produce a corpus from the vector and cache it. - // It's later used to get a percentile for default gas price. - let corpus: ::stats::Corpus<_> = prices.into(); - cache.lock().set_gas_price_corpus(corpus.clone()); - corpus - }) - }); - - match eventual_corpus { - Some(corp) => Box::new(corp.map_err(|_| errors::no_light_peers())), - None => Box::new(future::err(errors::network_disabled())), - } -} diff --git a/rpc/src/v1/helpers/dispatch/mod.rs b/rpc/src/v1/helpers/dispatch/mod.rs index 3f247f0c6c0..d211e35d409 100644 --- a/rpc/src/v1/helpers/dispatch/mod.rs +++ b/rpc/src/v1/helpers/dispatch/mod.rs @@ -1,22 +1,21 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Utilities and helpers for transaction dispatch. -pub(crate) mod light; mod full; mod prospective_signer; @@ -24,75 +23,86 @@ mod prospective_signer; mod signing; #[cfg(not(any(test, feature = "accounts")))] mod signing { - use super::*; - use v1::helpers::errors; - - /// Dummy signer implementation - #[derive(Debug, Clone)] - pub struct Signer; - - impl Signer { - /// Create new instance of dummy signer (accept any AccountProvider) - pub fn new(_ap: T) -> Self { - Signer - } - } - - impl super::Accounts for Signer { - fn sign_transaction(&self, _filled: FilledTransactionRequest, _chain_id: Option, _nonce: U256, _password: SignWith) -> Result> { - Err(errors::account("Signing unsupported", "See #9997")) - } - - fn sign_message(&self, _address: Address, _password: SignWith, _hash: SignMessage) -> Result> { - Err(errors::account("Signing unsupported", "See #9997")) - } - - fn decrypt(&self, _address: Address, _password: SignWith, _data: Bytes) -> Result> { - Err(errors::account("Signing unsupported", "See #9997")) - } - - fn supports_prospective_signing(&self, _address: &Address, _password: &SignWith) -> bool { - false - } - - fn default_account(&self) -> Address { - Default::default() - } - - fn is_unlocked(&self, _address: &Address) -> bool { - false - } - } + use super::*; + use v1::helpers::errors; + + /// Dummy signer implementation + #[derive(Debug, Clone)] + pub struct Signer; + + impl Signer { + /// Create new instance of dummy signer (accept any AccountProvider) + pub fn new(_ap: T) -> Self { + Signer + } + } + + impl super::Accounts for Signer { + fn sign_transaction( + &self, + _filled: FilledTransactionRequest, + _chain_id: Option, + _nonce: U256, + _password: SignWith, + ) -> Result> { + Err(errors::account("Signing unsupported", "See #9997")) + } + + fn sign_message( + &self, + _address: Address, + _password: SignWith, + _hash: SignMessage, + ) -> Result> { + Err(errors::account("Signing unsupported", "See #9997")) + } + + fn decrypt( + &self, + _address: Address, + _password: SignWith, + _data: Bytes, + ) -> Result> { + Err(errors::account("Signing unsupported", "See #9997")) + } + + fn supports_prospective_signing(&self, _address: &Address, _password: &SignWith) -> bool { + false + } + + fn default_account(&self) -> Address { + Default::default() + } + + fn is_unlocked(&self, _address: &Address) -> bool { + false + } + } } -pub use self::light::LightDispatcher; -pub use self::full::FullDispatcher; -pub use self::signing::Signer; +pub use self::{full::FullDispatcher, signing::Signer}; pub use v1::helpers::nonce::Reservations; -use std::fmt::Debug; -use std::ops::Deref; -use std::sync::Arc; +use std::{fmt::Debug, ops::Deref, sync::Arc}; use bytes::Bytes; -use ethcore::client::BlockChainClient; -use ethcore::miner::MinerService; -use ethereum_types::{H520, H256, U256, Address}; +use ethcore::{client::BlockChainClient, miner::MinerService}; +use ethereum_types::{Address, H256, H520, U256}; use ethkey::{Password, Signature}; use hash::keccak; -use types::transaction::{SignedTransaction, PendingTransaction}; - -use jsonrpc_core::{BoxFuture, Result, Error}; -use jsonrpc_core::futures::{future, Future, IntoFuture}; -use v1::helpers::{TransactionRequest, FilledTransactionRequest, ConfirmationPayload}; -use v1::types::{ - Bytes as RpcBytes, - RichRawTransaction as RpcRichRawTransaction, - ConfirmationPayload as RpcConfirmationPayload, - ConfirmationResponse, - EthSignRequest as RpcEthSignRequest, - EIP191SignRequest as RpcSignRequest, - DecryptRequest as RpcDecryptRequest, +use types::transaction::{PendingTransaction, SignedTransaction}; + +use jsonrpc_core::{ + futures::{future, Future, IntoFuture}, + BoxFuture, Error, Result, +}; +use v1::{ + helpers::{ConfirmationPayload, FilledTransactionRequest, TransactionRequest}, + types::{ + Bytes as RpcBytes, ConfirmationPayload as RpcConfirmationPayload, ConfirmationResponse, + DecryptRequest as RpcDecryptRequest, EIP191SignRequest as RpcSignRequest, + EthSignRequest as RpcEthSignRequest, RichRawTransaction as RpcRichRawTransaction, + }, }; /// Has the capability to dispatch, sign, and decrypt. @@ -100,37 +110,42 @@ use v1::types::{ /// Requires a clone implementation, with the implication that it be cheap; /// usually just bumping a reference count or two. pub trait Dispatcher: Send + Sync + Clone { - // TODO: when ATC exist, use zero-cost - // type Out: IntoFuture - - /// Fill optional fields of a transaction request, fetching gas price but not nonce. - fn fill_optional_fields(&self, request: TransactionRequest, default_sender: Address, force_nonce: bool) - -> BoxFuture; - - /// Sign the given transaction request without dispatching, fetching appropriate nonce. - fn sign

( - &self, - filled: FilledTransactionRequest, - signer: &Arc, - password: SignWith, - post_sign: P, - ) -> BoxFuture where - P: PostSign + 'static, - ::Future: Send; - - /// Converts a `SignedTransaction` into `RichRawTransaction` - fn enrich(&self, SignedTransaction) -> RpcRichRawTransaction; - - /// "Dispatch" a local transaction. - fn dispatch_transaction(&self, signed_transaction: PendingTransaction) -> Result; + // TODO: when ATC exist, use zero-cost + // type Out: IntoFuture + + /// Fill optional fields of a transaction request, fetching gas price but not nonce. + fn fill_optional_fields( + &self, + request: TransactionRequest, + default_sender: Address, + force_nonce: bool, + ) -> BoxFuture; + + /// Sign the given transaction request without dispatching, fetching appropriate nonce. + fn sign

( + &self, + filled: FilledTransactionRequest, + signer: &Arc, + password: SignWith, + post_sign: P, + ) -> BoxFuture + where + P: PostSign + 'static, + ::Future: Send; + + /// Converts a `SignedTransaction` into `RichRawTransaction` + fn enrich(&self, SignedTransaction) -> RpcRichRawTransaction; + + /// "Dispatch" a local transaction. + fn dispatch_transaction(&self, signed_transaction: PendingTransaction) -> Result; } /// Payload to sign pub enum SignMessage { - /// Eth-sign kind data (requires prefixing) - Data(Bytes), - /// Prefixed data hash - Hash(H256), + /// Eth-sign kind data (requires prefixing) + Data(Bytes), + /// Prefixed data hash + Hash(H256), } /// Abstract transaction signer. @@ -138,52 +153,69 @@ pub enum SignMessage { /// NOTE This signer is semi-correct, it's a temporary measure to avoid moving too much code. /// If accounts are ultimately removed all password-dealing endpoints will be wiped out. pub trait Accounts: Send + Sync { - /// Sign given filled transaction request for the specified chain_id. - fn sign_transaction(&self, filled: FilledTransactionRequest, chain_id: Option, nonce: U256, password: SignWith) -> Result>; - - /// Sign given message. - fn sign_message(&self, address: Address, password: SignWith, hash: SignMessage) -> Result>; - - /// Decrypt given message. - fn decrypt(&self, address: Address, password: SignWith, data: Bytes) -> Result>; - - /// Returns `true` if the accounts can sign multiple times. - fn supports_prospective_signing(&self, address: &Address, password: &SignWith) -> bool; - - /// Returns default account. - fn default_account(&self) -> Address; - - /// Returns true if account is unlocked (i.e. can sign without a password) - fn is_unlocked(&self, address: &Address) -> bool; + /// Sign given filled transaction request for the specified chain_id. + fn sign_transaction( + &self, + filled: FilledTransactionRequest, + chain_id: Option, + nonce: U256, + password: SignWith, + ) -> Result>; + + /// Sign given message. + fn sign_message( + &self, + address: Address, + password: SignWith, + hash: SignMessage, + ) -> Result>; + + /// Decrypt given message. + fn decrypt( + &self, + address: Address, + password: SignWith, + data: Bytes, + ) -> Result>; + + /// Returns `true` if the accounts can sign multiple times. + fn supports_prospective_signing(&self, address: &Address, password: &SignWith) -> bool; + + /// Returns default account. + fn default_account(&self) -> Address; + + /// Returns true if account is unlocked (i.e. can sign without a password) + fn is_unlocked(&self, address: &Address) -> bool; } /// action to execute after signing /// e.g importing a transaction into the chain pub trait PostSign: Send { - /// item that this PostSign returns - type Item: Send; - /// incase you need to perform async PostSign actions - type Out: IntoFuture + Send; - /// perform an action with the signed transaction - fn execute(self, signer: WithToken) -> Self::Out; + /// item that this PostSign returns + type Item: Send; + /// incase you need to perform async PostSign actions + type Out: IntoFuture + Send; + /// perform an action with the signed transaction + fn execute(self, signer: WithToken) -> Self::Out; } impl PostSign for () { - type Item = WithToken; - type Out = Result; - fn execute(self, signed: WithToken) -> Self::Out { - Ok(signed) - } + type Item = WithToken; + type Out = Result; + fn execute(self, signed: WithToken) -> Self::Out { + Ok(signed) + } } impl PostSign for F - where F: FnOnce(WithToken) -> Result +where + F: FnOnce(WithToken) -> Result, { - type Item = T; - type Out = Result; - fn execute(self, signed: WithToken) -> Self::Out { - (self)(signed) - } + type Item = T; + type Out = Result; + fn execute(self, signed: WithToken) -> Self::Out { + (self)(signed) + } } /// Single-use account token. @@ -192,189 +224,207 @@ pub type AccountToken = Password; /// Values used to unlock accounts for signing. #[derive(Clone, PartialEq)] pub enum SignWith { - /// Nothing -- implies the account is already unlocked. - Nothing, - /// Unlock with password. - Password(Password), - /// Unlock with single-use token. - Token(AccountToken), + /// Nothing -- implies the account is already unlocked. + Nothing, + /// Unlock with password. + Password(Password), + /// Unlock with single-use token. + Token(AccountToken), } impl SignWith { - #[cfg(any(test, feature = "accounts"))] - fn is_password(&self) -> bool { - if let SignWith::Password(_) = *self { - true - } else { - false - } - } + #[cfg(any(test, feature = "accounts"))] + fn is_password(&self) -> bool { + if let SignWith::Password(_) = *self { + true + } else { + false + } + } } /// A value, potentially accompanied by a signing token. pub enum WithToken { - /// No token. - No(T), - /// With token. - Yes(T, AccountToken), + /// No token. + No(T), + /// With token. + Yes(T, AccountToken), } impl Deref for WithToken { - type Target = T; - - fn deref(&self) -> &Self::Target { - match *self { - WithToken::No(ref v) => v, - WithToken::Yes(ref v, _) => v, - } - } + type Target = T; + + fn deref(&self) -> &Self::Target { + match *self { + WithToken::No(ref v) => v, + WithToken::Yes(ref v, _) => v, + } + } } impl WithToken { - /// Map the value with the given closure, preserving the token. - pub fn map(self, f: F) -> WithToken where - S: Debug, - F: FnOnce(T) -> S, - { - match self { - WithToken::No(v) => WithToken::No(f(v)), - WithToken::Yes(v, token) => WithToken::Yes(f(v), token), - } - } - - /// Convert into inner value, ignoring possible token. - pub fn into_value(self) -> T { - match self { - WithToken::No(v) => v, - WithToken::Yes(v, _) => v, - } - } - - /// Convert the `WithToken` into a tuple. - pub fn into_tuple(self) -> (T, Option) { - match self { - WithToken::No(v) => (v, None), - WithToken::Yes(v, token) => (v, Some(token)) - } - } + /// Map the value with the given closure, preserving the token. + pub fn map(self, f: F) -> WithToken + where + S: Debug, + F: FnOnce(T) -> S, + { + match self { + WithToken::No(v) => WithToken::No(f(v)), + WithToken::Yes(v, token) => WithToken::Yes(f(v), token), + } + } + + /// Convert into inner value, ignoring possible token. + pub fn into_value(self) -> T { + match self { + WithToken::No(v) => v, + WithToken::Yes(v, _) => v, + } + } + + /// Convert the `WithToken` into a tuple. + pub fn into_tuple(self) -> (T, Option) { + match self { + WithToken::No(v) => (v, None), + WithToken::Yes(v, token) => (v, Some(token)), + } + } } impl From<(T, AccountToken)> for WithToken { - fn from(tuple: (T, AccountToken)) -> Self { - WithToken::Yes(tuple.0, tuple.1) - } + fn from(tuple: (T, AccountToken)) -> Self { + WithToken::Yes(tuple.0, tuple.1) + } } impl From<(T, Option)> for WithToken { - fn from(tuple: (T, Option)) -> Self { - match tuple.1 { - Some(token) => WithToken::Yes(tuple.0, token), - None => WithToken::No(tuple.0), - } - } + fn from(tuple: (T, Option)) -> Self { + match tuple.1 { + Some(token) => WithToken::Yes(tuple.0, token), + None => WithToken::No(tuple.0), + } + } } /// Execute a confirmation payload. pub fn execute( - dispatcher: D, - signer: &Arc, - payload: ConfirmationPayload, - pass: SignWith + dispatcher: D, + signer: &Arc, + payload: ConfirmationPayload, + pass: SignWith, ) -> BoxFuture> { - match payload { - ConfirmationPayload::SendTransaction(request) => { - let condition = request.condition.clone().map(Into::into); - let cloned_dispatcher = dispatcher.clone(); - let post_sign = move |with_token_signed: WithToken| { - let (signed, token) = with_token_signed.into_tuple(); - let signed_transaction = PendingTransaction::new(signed, condition); - cloned_dispatcher.dispatch_transaction(signed_transaction) - .map(|hash| (hash, token)) - }; - - Box::new( - dispatcher.sign(request, &signer, pass, post_sign).map(|(hash, token)| { - WithToken::from((ConfirmationResponse::SendTransaction(hash), token)) - }) - ) - }, - ConfirmationPayload::SignTransaction(request) => { - Box::new(dispatcher.sign(request, &signer, pass, ()) - .map(move |result| result - .map(move |tx| dispatcher.enrich(tx)) - .map(ConfirmationResponse::SignTransaction) - )) - }, - ConfirmationPayload::EthSignMessage(address, data) => { - let res = signer.sign_message(address, pass, SignMessage::Data(data)) - .map(|result| result - .map(|s| H520(s.into_electrum())) - .map(ConfirmationResponse::Signature) - ); - - Box::new(future::done(res)) - }, - ConfirmationPayload::SignMessage(address, data) => { - let res = signer.sign_message(address, pass, SignMessage::Hash(data)) - .map(|result| result - .map(|rsv| H520(rsv.into_electrum())) - .map(ConfirmationResponse::Signature) - ); - - Box::new(future::done(res)) - }, - ConfirmationPayload::Decrypt(address, data) => { - let res = signer.decrypt(address, pass, data) - .map(|result| result - .map(RpcBytes) - .map(ConfirmationResponse::Decrypt) - ); - Box::new(future::done(res)) - }, - } + match payload { + ConfirmationPayload::SendTransaction(request) => { + let condition = request.condition.clone().map(Into::into); + let cloned_dispatcher = dispatcher.clone(); + let post_sign = move |with_token_signed: WithToken| { + let (signed, token) = with_token_signed.into_tuple(); + let signed_transaction = PendingTransaction::new(signed, condition); + cloned_dispatcher + .dispatch_transaction(signed_transaction) + .map(|hash| (hash, token)) + }; + + Box::new( + dispatcher + .sign(request, &signer, pass, post_sign) + .map(|(hash, token)| { + WithToken::from((ConfirmationResponse::SendTransaction(hash), token)) + }), + ) + } + ConfirmationPayload::SignTransaction(request) => Box::new( + dispatcher + .sign(request, &signer, pass, ()) + .map(move |result| { + result + .map(move |tx| dispatcher.enrich(tx)) + .map(ConfirmationResponse::SignTransaction) + }), + ), + ConfirmationPayload::EthSignMessage(address, data) => { + let res = signer + .sign_message(address, pass, SignMessage::Data(data)) + .map(|result| { + result + .map(|s| H520(s.into_electrum())) + .map(ConfirmationResponse::Signature) + }); + + Box::new(future::done(res)) + } + ConfirmationPayload::SignMessage(address, data) => { + let res = signer + .sign_message(address, pass, SignMessage::Hash(data)) + .map(|result| { + result + .map(|rsv| H520(rsv.into_electrum())) + .map(ConfirmationResponse::Signature) + }); + + Box::new(future::done(res)) + } + ConfirmationPayload::Decrypt(address, data) => { + let res = signer + .decrypt(address, pass, data) + .map(|result| result.map(RpcBytes).map(ConfirmationResponse::Decrypt)); + Box::new(future::done(res)) + } + } } /// Returns a eth_sign-compatible hash of data to sign. /// The data is prepended with special message to prevent /// malicious DApps from using the function to sign forged transactions. pub fn eth_data_hash(mut data: Bytes) -> H256 { - let mut message_data = - format!("\x19Ethereum Signed Message:\n{}", data.len()) - .into_bytes(); - message_data.append(&mut data); - keccak(message_data) + let mut message_data = format!("\x19Ethereum Signed Message:\n{}", data.len()).into_bytes(); + message_data.append(&mut data); + keccak(message_data) } /// Extract the default gas price from a client and miner. -pub fn default_gas_price(client: &C, miner: &M, percentile: usize) -> U256 where - C: BlockChainClient, - M: MinerService, +pub fn default_gas_price(client: &C, miner: &M, percentile: usize) -> U256 +where + C: BlockChainClient, + M: MinerService, { - client.gas_price_corpus(100).percentile(percentile).cloned().unwrap_or_else(|| miner.sensible_gas_price()) + client + .gas_price_corpus(100) + .percentile(percentile) + .cloned() + .unwrap_or_else(|| miner.sensible_gas_price()) } /// Convert RPC confirmation payload to signer confirmation payload. /// May need to resolve in the future to fetch things like gas price. -pub fn from_rpc(payload: RpcConfirmationPayload, default_account: Address, dispatcher: &D) -> BoxFuture - where D: Dispatcher +pub fn from_rpc( + payload: RpcConfirmationPayload, + default_account: Address, + dispatcher: &D, +) -> BoxFuture +where + D: Dispatcher, { - match payload { - RpcConfirmationPayload::SendTransaction(request) => { - Box::new(dispatcher.fill_optional_fields(request.into(), default_account, false) - .map(ConfirmationPayload::SendTransaction)) - }, - RpcConfirmationPayload::SignTransaction(request) => { - Box::new(dispatcher.fill_optional_fields(request.into(), default_account, false) - .map(ConfirmationPayload::SignTransaction)) - }, - RpcConfirmationPayload::Decrypt(RpcDecryptRequest { address, msg }) => { - Box::new(future::ok(ConfirmationPayload::Decrypt(address, msg.into()))) - }, - RpcConfirmationPayload::EthSignMessage(RpcEthSignRequest { address, data }) => { - Box::new(future::ok(ConfirmationPayload::EthSignMessage(address, data.into()))) - }, - RpcConfirmationPayload::EIP191SignMessage(RpcSignRequest { address, data }) => { - Box::new(future::ok(ConfirmationPayload::SignMessage(address, data))) - }, - } + match payload { + RpcConfirmationPayload::SendTransaction(request) => Box::new( + dispatcher + .fill_optional_fields(request.into(), default_account, false) + .map(ConfirmationPayload::SendTransaction), + ), + RpcConfirmationPayload::SignTransaction(request) => Box::new( + dispatcher + .fill_optional_fields(request.into(), default_account, false) + .map(ConfirmationPayload::SignTransaction), + ), + RpcConfirmationPayload::Decrypt(RpcDecryptRequest { address, msg }) => Box::new( + future::ok(ConfirmationPayload::Decrypt(address, msg.into())), + ), + RpcConfirmationPayload::EthSignMessage(RpcEthSignRequest { address, data }) => Box::new( + future::ok(ConfirmationPayload::EthSignMessage(address, data.into())), + ), + RpcConfirmationPayload::EIP191SignMessage(RpcSignRequest { address, data }) => { + Box::new(future::ok(ConfirmationPayload::SignMessage(address, data))) + } + } } diff --git a/rpc/src/v1/helpers/dispatch/prospective_signer.rs b/rpc/src/v1/helpers/dispatch/prospective_signer.rs index 034d19dc659..8768ad6413b 100644 --- a/rpc/src/v1/helpers/dispatch/prospective_signer.rs +++ b/rpc/src/v1/helpers/dispatch/prospective_signer.rs @@ -1,152 +1,158 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use std::sync::Arc; use ethereum_types::U256; -use jsonrpc_core::{Result, Error}; -use jsonrpc_core::futures::{Future, Poll, Async, IntoFuture}; +use jsonrpc_core::{ + futures::{Async, Future, IntoFuture, Poll}, + Error, Result, +}; use types::transaction::SignedTransaction; +use super::{Accounts, PostSign, SignWith, WithToken}; use v1::helpers::{errors, nonce, FilledTransactionRequest}; -use super::{Accounts, SignWith, WithToken, PostSign}; #[derive(Debug, Clone, Copy)] enum ProspectiveSignerState { - TryProspectiveSign, - WaitForPostSign, - WaitForNonce, + TryProspectiveSign, + WaitForPostSign, + WaitForNonce, } pub struct ProspectiveSigner { - signer: Arc, - filled: FilledTransactionRequest, - chain_id: Option, - reserved: nonce::Reserved, - password: SignWith, - state: ProspectiveSignerState, - prospective: Option>, - ready: Option, - post_sign: Option

, - post_sign_future: Option<::Future> + signer: Arc, + filled: FilledTransactionRequest, + chain_id: Option, + reserved: nonce::Reserved, + password: SignWith, + state: ProspectiveSignerState, + prospective: Option>, + ready: Option, + post_sign: Option

, + post_sign_future: Option<::Future>, } impl ProspectiveSigner

{ - pub fn new( - signer: Arc, - filled: FilledTransactionRequest, - chain_id: Option, - reserved: nonce::Reserved, - password: SignWith, - post_sign: P - ) -> Self { - let supports_prospective = signer.supports_prospective_signing(&filled.from, &password); + pub fn new( + signer: Arc, + filled: FilledTransactionRequest, + chain_id: Option, + reserved: nonce::Reserved, + password: SignWith, + post_sign: P, + ) -> Self { + let supports_prospective = signer.supports_prospective_signing(&filled.from, &password); - ProspectiveSigner { - signer, - filled, - chain_id, - reserved, - password, - state: if supports_prospective { - ProspectiveSignerState::TryProspectiveSign - } else { - ProspectiveSignerState::WaitForNonce - }, - prospective: None, - ready: None, - post_sign: Some(post_sign), - post_sign_future: None - } - } + ProspectiveSigner { + signer, + filled, + chain_id, + reserved, + password, + state: if supports_prospective { + ProspectiveSignerState::TryProspectiveSign + } else { + ProspectiveSignerState::WaitForNonce + }, + prospective: None, + ready: None, + post_sign: Some(post_sign), + post_sign_future: None, + } + } - fn sign(&self, nonce: &U256) -> Result> { - self.signer.sign_transaction( - self.filled.clone(), - self.chain_id, - *nonce, - self.password.clone() - ) - } + fn sign(&self, nonce: &U256) -> Result> { + self.signer.sign_transaction( + self.filled.clone(), + self.chain_id, + *nonce, + self.password.clone(), + ) + } - fn poll_reserved(&mut self) -> Poll { - self.reserved.poll().map_err(|_| errors::internal("Nonce reservation failure", "")) - } + fn poll_reserved(&mut self) -> Poll { + self.reserved + .poll() + .map_err(|_| errors::internal("Nonce reservation failure", "")) + } } impl Future for ProspectiveSigner

{ - type Item = P::Item; - type Error = Error; + type Item = P::Item; + type Error = Error; - fn poll(&mut self) -> Poll { - use self::ProspectiveSignerState::*; + fn poll(&mut self) -> Poll { + use self::ProspectiveSignerState::*; - loop { - match self.state { - TryProspectiveSign => { - // Try to poll reserved, it might be ready. - match self.poll_reserved()? { - Async::NotReady => { - self.state = WaitForNonce; - self.prospective = Some(self.sign(self.reserved.prospective_value())?); - }, - Async::Ready(nonce) => { - self.state = WaitForPostSign; - self.post_sign_future = Some( - self.post_sign.take() - .expect("post_sign is set on creation; qed") - .execute(self.sign(nonce.value())?) - .into_future() - ); - self.ready = Some(nonce); - }, - } - }, - WaitForNonce => { - let nonce = try_ready!(self.poll_reserved()); - let prospective = match (self.prospective.take(), nonce.matches_prospective()) { - (Some(prospective), true) => prospective, - _ => self.sign(nonce.value())?, - }; - self.ready = Some(nonce); - self.state = WaitForPostSign; - self.post_sign_future = Some(self.post_sign.take() - .expect("post_sign is set on creation; qed") - .execute(prospective) - .into_future()); - }, - WaitForPostSign => { - if let Some(mut fut) = self.post_sign_future.as_mut() { - match fut.poll()? { - Async::Ready(item) => { - let nonce = self.ready - .take() - .expect("nonce is set before state transitions to WaitForPostSign; qed"); - nonce.mark_used(); - return Ok(Async::Ready(item)) - }, - Async::NotReady => { - return Ok(Async::NotReady) - } - } - } else { - panic!("Poll after ready."); - } - } - } - } - } + loop { + match self.state { + TryProspectiveSign => { + // Try to poll reserved, it might be ready. + match self.poll_reserved()? { + Async::NotReady => { + self.state = WaitForNonce; + self.prospective = Some(self.sign(self.reserved.prospective_value())?); + } + Async::Ready(nonce) => { + self.state = WaitForPostSign; + self.post_sign_future = Some( + self.post_sign + .take() + .expect("post_sign is set on creation; qed") + .execute(self.sign(nonce.value())?) + .into_future(), + ); + self.ready = Some(nonce); + } + } + } + WaitForNonce => { + let nonce = try_ready!(self.poll_reserved()); + let prospective = match (self.prospective.take(), nonce.matches_prospective()) { + (Some(prospective), true) => prospective, + _ => self.sign(nonce.value())?, + }; + self.ready = Some(nonce); + self.state = WaitForPostSign; + self.post_sign_future = Some( + self.post_sign + .take() + .expect("post_sign is set on creation; qed") + .execute(prospective) + .into_future(), + ); + } + WaitForPostSign => { + if let Some(fut) = self.post_sign_future.as_mut() { + match fut.poll()? { + Async::Ready(item) => { + let nonce = self.ready.take().expect( + "nonce is set before state transitions to WaitForPostSign; qed", + ); + nonce.mark_used(); + return Ok(Async::Ready(item)); + } + Async::NotReady => return Ok(Async::NotReady), + } + } else { + panic!("Poll after ready."); + } + } + } + } + } } diff --git a/rpc/src/v1/helpers/dispatch/signing.rs b/rpc/src/v1/helpers/dispatch/signing.rs index 8243dcbdf81..55b03b2faa5 100644 --- a/rpc/src/v1/helpers/dispatch/signing.rs +++ b/rpc/src/v1/helpers/dispatch/signing.rs @@ -1,156 +1,146 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use std::sync::Arc; use accounts::AccountProvider; use bytes::Bytes; use crypto::DEFAULT_MAC; -use ethereum_types::{H256, U256, Address}; -use ethkey::{Signature}; -use types::transaction::{Transaction, Action, SignedTransaction}; +use ethereum_types::{Address, H256, U256}; +use ethkey::Signature; +use types::transaction::{Action, SignedTransaction, Transaction}; use jsonrpc_core::Result; use v1::helpers::{errors, FilledTransactionRequest}; -use super::{eth_data_hash, WithToken, SignWith, SignMessage}; +use super::{eth_data_hash, SignMessage, SignWith, WithToken}; /// Account-aware signer pub struct Signer { - accounts: Arc, + accounts: Arc, } impl Signer { - /// Create new instance of signer - pub fn new(accounts: Arc) -> Self { - Signer { accounts } - } + /// Create new instance of signer + pub fn new(accounts: Arc) -> Self { + Signer { accounts } + } } impl super::Accounts for Signer { - fn sign_transaction(&self, filled: FilledTransactionRequest, chain_id: Option, nonce: U256, password: SignWith) -> Result> { - let t = Transaction { - nonce: nonce, - action: filled.to.map_or(Action::Create, Action::Call), - gas: filled.gas, - gas_price: filled.gas_price, - value: filled.value, - data: filled.data, - }; - - if self.accounts.is_hardware_address(&filled.from) { - return hardware_signature(&*self.accounts, filled.from, t, chain_id).map(WithToken::No) - } - - let hash = t.hash(chain_id); - let signature = signature(&*self.accounts, filled.from, hash, password)?; - - Ok(signature.map(|sig| { - SignedTransaction::new(t.with_signature(sig, chain_id)) + fn sign_transaction( + &self, + filled: FilledTransactionRequest, + chain_id: Option, + nonce: U256, + password: SignWith, + ) -> Result> { + let t = Transaction { + nonce: nonce, + action: filled.to.map_or(Action::Create, Action::Call), + gas: filled.gas, + gas_price: filled.gas_price, + value: filled.value, + data: filled.data, + }; + + let hash = t.hash(chain_id); + let signature = signature(&*self.accounts, filled.from, hash, password)?; + + Ok(signature.map(|sig| { + SignedTransaction::new(t.with_signature(sig, chain_id)) .expect("Transaction was signed by AccountsProvider; it never produces invalid signatures; qed") - })) - } - - fn sign_message(&self, address: Address, password: SignWith, hash: SignMessage) -> Result> { - if self.accounts.is_hardware_address(&address) { - return if let SignMessage::Data(data) = hash { - let signature = self.accounts.sign_message_with_hardware(&address, &data) - // TODO: is this correct? I guess the `token` is the wallet in this context - .map(WithToken::No) - .map_err(|e| errors::account("Error signing message with hardware_wallet", e)); - - signature - } else { - Err(errors::account("Error signing message with hardware_wallet", "Message signing is unsupported")) - } - } - - match hash { - SignMessage::Data(data) => { - let hash = eth_data_hash(data); - signature(&self.accounts, address, hash, password) - }, - SignMessage::Hash(hash) => { - signature(&self.accounts, address, hash, password) - } - } - } - - fn decrypt(&self, address: Address, password: SignWith, data: Bytes) -> Result> { - if self.accounts.is_hardware_address(&address) { - return Err(errors::unsupported("Decrypting via hardware wallets is not supported.", None)); - } - - match password.clone() { - SignWith::Nothing => self.accounts.decrypt(address, None, &DEFAULT_MAC, &data).map(WithToken::No), - SignWith::Password(pass) => self.accounts.decrypt(address, Some(pass), &DEFAULT_MAC, &data).map(WithToken::No), - SignWith::Token(token) => self.accounts.decrypt_with_token(address, token, &DEFAULT_MAC, &data).map(Into::into), - }.map_err(|e| match password { - SignWith::Nothing => errors::signing(e), - _ => errors::password(e), - }) - } - - fn supports_prospective_signing(&self, address: &Address, password: &SignWith) -> bool { - // If the account is permanently unlocked we can try to sign - // using prospective nonce. This should speed up sending - // multiple subsequent transactions in multi-threaded RPC environment. - let is_unlocked_permanently = self.accounts.is_unlocked_permanently(address); - let has_password = password.is_password(); - - is_unlocked_permanently || has_password - } - - fn default_account(&self) -> Address { - self.accounts.default_account().ok().unwrap_or_default() - } - - fn is_unlocked(&self, address: &Address) -> bool { - self.accounts.is_unlocked(address) - } + })) + } + + fn sign_message( + &self, + address: Address, + password: SignWith, + hash: SignMessage, + ) -> Result> { + match hash { + SignMessage::Data(data) => { + let hash = eth_data_hash(data); + signature(&self.accounts, address, hash, password) + } + SignMessage::Hash(hash) => signature(&self.accounts, address, hash, password), + } + } + + fn decrypt( + &self, + address: Address, + password: SignWith, + data: Bytes, + ) -> Result> { + match password.clone() { + SignWith::Nothing => self + .accounts + .decrypt(address, None, &DEFAULT_MAC, &data) + .map(WithToken::No), + SignWith::Password(pass) => self + .accounts + .decrypt(address, Some(pass), &DEFAULT_MAC, &data) + .map(WithToken::No), + SignWith::Token(token) => self + .accounts + .decrypt_with_token(address, token, &DEFAULT_MAC, &data) + .map(Into::into), + } + .map_err(|e| match password { + SignWith::Nothing => errors::signing(e), + _ => errors::password(e), + }) + } + + fn supports_prospective_signing(&self, address: &Address, password: &SignWith) -> bool { + // If the account is permanently unlocked we can try to sign + // using prospective nonce. This should speed up sending + // multiple subsequent transactions in multi-threaded RPC environment. + let is_unlocked_permanently = self.accounts.is_unlocked_permanently(address); + let has_password = password.is_password(); + + is_unlocked_permanently || has_password + } + + fn default_account(&self) -> Address { + self.accounts.default_account().ok().unwrap_or_default() + } + + fn is_unlocked(&self, address: &Address) -> bool { + self.accounts.is_unlocked(address) + } } -fn signature(accounts: &AccountProvider, address: Address, hash: H256, password: SignWith) -> Result> { - match password.clone() { - SignWith::Nothing => accounts.sign(address, None, hash).map(WithToken::No), - SignWith::Password(pass) => accounts.sign(address, Some(pass), hash).map(WithToken::No), - SignWith::Token(token) => accounts.sign_with_token(address, token, hash).map(Into::into), - }.map_err(|e| match password { - SignWith::Nothing => errors::signing(e), - _ => errors::password(e), - }) -} - -// obtain a hardware signature from the given account. -fn hardware_signature(accounts: &AccountProvider, address: Address, t: Transaction, chain_id: Option) - -> Result -{ - debug_assert!(accounts.is_hardware_address(&address)); - - let mut stream = rlp::RlpStream::new(); - t.rlp_append_unsigned_transaction(&mut stream, chain_id); - let signature = accounts.sign_transaction_with_hardware(&address, &t, chain_id, &stream.as_raw()) - .map_err(|e| { - debug!(target: "miner", "Error signing transaction with hardware wallet: {}", e); - errors::account("Error signing transaction with hardware wallet", e) - })?; - - SignedTransaction::new(t.with_signature(signature, chain_id)) - .map_err(|e| { - debug!(target: "miner", "Hardware wallet has produced invalid signature: {}", e); - errors::account("Invalid signature generated", e) - }) +fn signature( + accounts: &AccountProvider, + address: Address, + hash: H256, + password: SignWith, +) -> Result> { + match password.clone() { + SignWith::Nothing => accounts.sign(address, None, hash).map(WithToken::No), + SignWith::Password(pass) => accounts.sign(address, Some(pass), hash).map(WithToken::No), + SignWith::Token(token) => accounts + .sign_with_token(address, token, hash) + .map(Into::into), + } + .map_err(|e| match password { + SignWith::Nothing => errors::signing(e), + _ => errors::password(e), + }) } diff --git a/rpc/src/v1/helpers/eip191.rs b/rpc/src/v1/helpers/eip191.rs index 938ab81dc4e..51bd1580d66 100644 --- a/rpc/src/v1/helpers/eip191.rs +++ b/rpc/src/v1/helpers/eip191.rs @@ -1,61 +1,60 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! EIP-191 compliant decoding + hashing -use v1::types::{EIP191Version, Bytes, PresignedTransaction}; use eip_712::{hash_structured_data, EIP712}; -use serde_json::{Value, from_value}; -use v1::helpers::errors; -use jsonrpc_core::Error; -use v1::helpers::dispatch::eth_data_hash; +use ethereum_types::H256; use hash::keccak; +use jsonrpc_core::Error; +use serde_json::{from_value, Value}; use std::fmt::Display; -use ethereum_types::H256; +use v1::{ + helpers::{dispatch::eth_data_hash, errors}, + types::{Bytes, EIP191Version, PresignedTransaction}, +}; /// deserializes and hashes the message depending on the version specifier pub fn hash_message(version: EIP191Version, message: Value) -> Result { - let data = match version { - EIP191Version::StructuredData => { - let typed_data = from_value::(message) - .map_err(map_serde_err("StructuredData"))?; - - hash_structured_data(typed_data) - .map_err(|err| errors::invalid_call_data(err.kind()))? - } - - EIP191Version::PresignedTransaction => { - let data = from_value::(message) - .map_err(map_serde_err("WithValidator"))?; - let prefix = b"\x19\x00"; - let data = [&prefix[..], &data.validator.0[..], &data.data.0[..]].concat(); - keccak(data) - } - - EIP191Version::PersonalMessage => { - let bytes = from_value::(message) - .map_err(map_serde_err("Bytes"))?; - eth_data_hash(bytes.0) - } - }; - - Ok(data) + let data = match version { + EIP191Version::StructuredData => { + let typed_data = + from_value::(message).map_err(map_serde_err("StructuredData"))?; + + hash_structured_data(typed_data).map_err(|err| errors::invalid_call_data(err.kind()))? + } + + EIP191Version::PresignedTransaction => { + let data = from_value::(message) + .map_err(map_serde_err("WithValidator"))?; + let prefix = b"\x19\x00"; + let data = [&prefix[..], &data.validator.0[..], &data.data.0[..]].concat(); + keccak(data) + } + + EIP191Version::PersonalMessage => { + let bytes = from_value::(message).map_err(map_serde_err("Bytes"))?; + eth_data_hash(bytes.0) + } + }; + + Ok(data) } fn map_serde_err(struct_name: &'static str) -> impl Fn(T) -> Error { - move |error: T| { - errors::invalid_call_data(format!("Error deserializing '{}': {}", struct_name, error)) - } + move |error: T| { + errors::invalid_call_data(format!("Error deserializing '{}': {}", struct_name, error)) + } } diff --git a/rpc/src/v1/helpers/engine_signer.rs b/rpc/src/v1/helpers/engine_signer.rs index f993d15f230..bcd9ca9bad9 100644 --- a/rpc/src/v1/helpers/engine_signer.rs +++ b/rpc/src/v1/helpers/engine_signer.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. -// This file is part of Parity. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity. If not, see . +// along with OpenEthereum. If not, see . use std::sync::Arc; @@ -21,31 +21,34 @@ use ethkey::{self, Address, Password}; /// An implementation of EngineSigner using internal account management. pub struct EngineSigner { - accounts: Arc, - address: Address, - password: Password, + accounts: Arc, + address: Address, + password: Password, } impl EngineSigner { - /// Creates new `EngineSigner` given account manager and account details. - pub fn new(accounts: Arc, address: Address, password: Password) -> Self { - EngineSigner { accounts, address, password } - } + /// Creates new `EngineSigner` given account manager and account details. + pub fn new(accounts: Arc, address: Address, password: Password) -> Self { + EngineSigner { + accounts, + address, + password, + } + } } impl ethcore::engines::EngineSigner for EngineSigner { - fn sign(&self, message: ethkey::Message) -> Result { - match self.accounts.sign(self.address, Some(self.password.clone()), message) { - Ok(ok) => Ok(ok), - Err(e) => { - warn!("Unable to sign consensus message: {:?}", e); - Err(ethkey::Error::InvalidSecret) - }, - } - } - - fn address(&self) -> Address { - self.address - } + fn sign(&self, message: ethkey::Message) -> Result { + match self + .accounts + .sign(self.address, Some(self.password.clone()), message) + { + Ok(ok) => Ok(ok), + Err(_e) => Err(ethkey::Error::InvalidSecret), + } + } + + fn address(&self) -> Address { + self.address + } } - diff --git a/rpc/src/v1/helpers/errors.rs b/rpc/src/v1/helpers/errors.rs index c04374d6562..bb3d71c70f7 100644 --- a/rpc/src/v1/helpers/errors.rs +++ b/rpc/src/v1/helpers/errors.rs @@ -1,135 +1,117 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! RPC Error codes and error objects use std::fmt; -use ethcore::error::{Error as EthcoreError, ErrorKind, CallError}; -use ethcore::client::BlockId; -use jsonrpc_core::{futures, Result as RpcResult, Error, ErrorCode, Value}; +use ethcore::{ + client::{BlockChainClient, BlockId}, + error::{CallError, Error as EthcoreError, ErrorKind}, +}; +use jsonrpc_core::{Error, ErrorCode, Result as RpcResult, Value}; use rlp::DecoderError; -use types::transaction::Error as TransactionError; -use ethcore_private_tx::Error as PrivateTransactionError; +use types::{blockchain_info::BlockChainInfo, transaction::Error as TransactionError}; +use v1::{impls::EthClientOptions, types::BlockNumber}; use vm::Error as VMError; -use light::on_demand::error::{Error as OnDemandError, ErrorKind as OnDemandErrorKind}; -use ethcore::client::BlockChainClient; -use types::blockchain_info::BlockChainInfo; -use v1::types::BlockNumber; mod codes { - // NOTE [ToDr] Codes from [-32099, -32000] - pub const UNSUPPORTED_REQUEST: i64 = -32000; - pub const NO_WORK: i64 = -32001; - pub const NO_AUTHOR: i64 = -32002; - pub const NO_NEW_WORK: i64 = -32003; - pub const NO_WORK_REQUIRED: i64 = -32004; - pub const CANNOT_SUBMIT_WORK: i64 = -32005; - pub const UNKNOWN_ERROR: i64 = -32009; - pub const TRANSACTION_ERROR: i64 = -32010; - pub const EXECUTION_ERROR: i64 = -32015; - pub const EXCEPTION_ERROR: i64 = -32016; - pub const DATABASE_ERROR: i64 = -32017; - #[cfg(any(test, feature = "accounts"))] - pub const ACCOUNT_LOCKED: i64 = -32020; - #[cfg(any(test, feature = "accounts"))] - pub const PASSWORD_INVALID: i64 = -32021; - pub const ACCOUNT_ERROR: i64 = -32023; - pub const PRIVATE_ERROR: i64 = -32024; - pub const REQUEST_REJECTED: i64 = -32040; - pub const REQUEST_REJECTED_LIMIT: i64 = -32041; - pub const REQUEST_NOT_FOUND: i64 = -32042; - pub const ENCRYPTION_ERROR: i64 = -32055; - pub const ENCODING_ERROR: i64 = -32058; - pub const FETCH_ERROR: i64 = -32060; - pub const NO_LIGHT_PEERS: i64 = -32065; - pub const NO_PEERS: i64 = -32066; - pub const DEPRECATED: i64 = -32070; - pub const EXPERIMENTAL_RPC: i64 = -32071; - pub const CANNOT_RESTART: i64 = -32080; + // NOTE [ToDr] Codes from [-32099, -32000] + pub const UNSUPPORTED_REQUEST: i64 = -32000; + pub const NO_WORK: i64 = -32001; + pub const NO_AUTHOR: i64 = -32002; + pub const NO_NEW_WORK: i64 = -32003; + pub const NO_WORK_REQUIRED: i64 = -32004; + pub const CANNOT_SUBMIT_WORK: i64 = -32005; + pub const UNKNOWN_ERROR: i64 = -32009; + pub const TRANSACTION_ERROR: i64 = -32010; + pub const EXECUTION_ERROR: i64 = -32015; + pub const EXCEPTION_ERROR: i64 = -32016; + pub const DATABASE_ERROR: i64 = -32017; + #[cfg(any(test, feature = "accounts"))] + pub const ACCOUNT_LOCKED: i64 = -32020; + #[cfg(any(test, feature = "accounts"))] + pub const PASSWORD_INVALID: i64 = -32021; + pub const ACCOUNT_ERROR: i64 = -32023; + pub const REQUEST_REJECTED: i64 = -32040; + pub const REQUEST_REJECTED_LIMIT: i64 = -32041; + pub const REQUEST_NOT_FOUND: i64 = -32042; + pub const ENCRYPTION_ERROR: i64 = -32055; + #[cfg(any(test, feature = "accounts"))] + pub const ENCODING_ERROR: i64 = -32058; + pub const FETCH_ERROR: i64 = -32060; + pub const NO_PEERS: i64 = -32066; + pub const DEPRECATED: i64 = -32070; + pub const EXPERIMENTAL_RPC: i64 = -32071; + pub const CANNOT_RESTART: i64 = -32080; } pub fn unimplemented(details: Option) -> Error { - Error { - code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), - message: "This request is not implemented yet. Please create an issue on Github repo.".into(), - data: details.map(Value::String), - } -} - -pub fn light_unimplemented(details: Option) -> Error { - Error { - code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), - message: "This request is unsupported for light clients.".into(), - data: details.map(Value::String), - } + Error { + code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), + message: "This request is not implemented yet. Please create an issue on Github repo." + .into(), + data: details.map(Value::String), + } } pub fn unsupported>(msg: T, details: Option) -> Error { - Error { - code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), - message: msg.into(), - data: details.map(Into::into).map(Value::String), - } + Error { + code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), + message: msg.into(), + data: details.map(Into::into).map(Value::String), + } } pub fn request_not_found() -> Error { - Error { - code: ErrorCode::ServerError(codes::REQUEST_NOT_FOUND), - message: "Request not found.".into(), - data: None, - } + Error { + code: ErrorCode::ServerError(codes::REQUEST_NOT_FOUND), + message: "Request not found.".into(), + data: None, + } } pub fn request_rejected() -> Error { - Error { - code: ErrorCode::ServerError(codes::REQUEST_REJECTED), - message: "Request has been rejected.".into(), - data: None, - } + Error { + code: ErrorCode::ServerError(codes::REQUEST_REJECTED), + message: "Request has been rejected.".into(), + data: None, + } } pub fn request_rejected_limit() -> Error { - Error { - code: ErrorCode::ServerError(codes::REQUEST_REJECTED_LIMIT), - message: "Request has been rejected because of queue limit.".into(), - data: None, - } -} - -pub fn request_rejected_param_limit(limit: u64, items_desc: &str) -> Error { - Error { - code: ErrorCode::ServerError(codes::REQUEST_REJECTED_LIMIT), - message: format!("Requested data size exceeds limit of {} {}.", limit, items_desc), - data: None, - } + Error { + code: ErrorCode::ServerError(codes::REQUEST_REJECTED_LIMIT), + message: "Request has been rejected because of queue limit.".into(), + data: None, + } } pub fn account(error: &str, details: T) -> Error { - Error { - code: ErrorCode::ServerError(codes::ACCOUNT_ERROR), - message: error.into(), - data: Some(Value::String(format!("{:?}", details))), - } + Error { + code: ErrorCode::ServerError(codes::ACCOUNT_ERROR), + message: error.into(), + data: Some(Value::String(format!("{:?}", details))), + } } pub fn cannot_restart() -> Error { - Error { + Error { code: ErrorCode::ServerError(codes::CANNOT_RESTART), - message: "Parity could not be restarted. This feature is disabled in development mode and if the binary name isn't parity.".into(), + message: "OpenEthereum could not be restarted. This feature is disabled in development mode and if the binary name isn't openethereum.".into(), data: None, } } @@ -138,31 +120,31 @@ pub fn cannot_restart() -> Error { /// Should not be used when function can just fail /// because of invalid parameters or incomplete node state. pub fn internal(error: &str, data: T) -> Error { - Error { - code: ErrorCode::InternalError, - message: format!("Internal error occurred: {}", error), - data: Some(Value::String(format!("{:?}", data))), - } + Error { + code: ErrorCode::InternalError, + message: format!("Internal error occurred: {}", error), + data: Some(Value::String(format!("{:?}", data))), + } } pub fn invalid_params(param: &str, details: T) -> Error { - Error { - code: ErrorCode::InvalidParams, - message: format!("Couldn't parse parameters: {}", param), - data: Some(Value::String(format!("{:?}", details))), - } + Error { + code: ErrorCode::InvalidParams, + message: format!("Couldn't parse parameters: {}", param), + data: Some(Value::String(format!("{:?}", details))), + } } pub fn execution(data: T) -> Error { - Error { - code: ErrorCode::ServerError(codes::EXECUTION_ERROR), - message: "Transaction execution error.".into(), - data: Some(Value::String(format!("{:?}", data))), - } + Error { + code: ErrorCode::ServerError(codes::EXECUTION_ERROR), + message: "Transaction execution error.".into(), + data: Some(Value::String(format!("{:?}", data))), + } } pub fn state_pruned() -> Error { - Error { + Error { code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), message: "This request is not supported because your node is running with state pruning. Run with --pruning=archive.".into(), data: None, @@ -170,195 +152,203 @@ pub fn state_pruned() -> Error { } pub fn state_corrupt() -> Error { - internal("State corrupt", "") + internal("State corrupt", "") } pub fn exceptional(data: T) -> Error { - Error { - code: ErrorCode::ServerError(codes::EXCEPTION_ERROR), - message: "The execution failed due to an exception.".into(), - data: Some(Value::String(data.to_string())), - } + Error { + code: ErrorCode::ServerError(codes::EXCEPTION_ERROR), + message: "The execution failed due to an exception.".into(), + data: Some(Value::String(data.to_string())), + } } pub fn no_work() -> Error { - Error { - code: ErrorCode::ServerError(codes::NO_WORK), - message: "Still syncing.".into(), - data: None, - } + Error { + code: ErrorCode::ServerError(codes::NO_WORK), + message: "Still syncing.".into(), + data: None, + } } pub fn no_new_work() -> Error { - Error { - code: ErrorCode::ServerError(codes::NO_NEW_WORK), - message: "Work has not changed.".into(), - data: None, - } + Error { + code: ErrorCode::ServerError(codes::NO_NEW_WORK), + message: "Work has not changed.".into(), + data: None, + } } pub fn no_author() -> Error { - Error { - code: ErrorCode::ServerError(codes::NO_AUTHOR), - message: "Author not configured. Run Parity with --author to configure.".into(), - data: None, - } + Error { + code: ErrorCode::ServerError(codes::NO_AUTHOR), + message: "Author not configured. Run Parity with --author to configure.".into(), + data: None, + } } pub fn no_work_required() -> Error { - Error { - code: ErrorCode::ServerError(codes::NO_WORK_REQUIRED), - message: "External work is only required for Proof of Work engines.".into(), - data: None, - } + Error { + code: ErrorCode::ServerError(codes::NO_WORK_REQUIRED), + message: "External work is only required for Proof of Work engines.".into(), + data: None, + } } pub fn cannot_submit_work(err: EthcoreError) -> Error { - Error { - code: ErrorCode::ServerError(codes::CANNOT_SUBMIT_WORK), - message: "Cannot submit work.".into(), - data: Some(Value::String(err.to_string())), - } -} - -pub fn unavailable_block() -> Error { - Error { - code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), - message: "Ancient block sync is still in progress".into(), - data: None, - } + Error { + code: ErrorCode::ServerError(codes::CANNOT_SUBMIT_WORK), + message: "Cannot submit work.".into(), + data: Some(Value::String(err.to_string())), + } +} + +pub fn unavailable_block(no_ancient_block: bool, by_hash: bool) -> Error { + if no_ancient_block { + Error { + code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), + message: "Looks like you disabled ancient block download, unfortunately the information you're \ + trying to fetch doesn't exist in the db and is probably in the ancient blocks.".into(), + data: None, + } + } else if by_hash { + Error { + code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), + message: "Block information is incomplete while ancient block sync is still in progress, before \ + it's finished we can't determine the existence of requested item.".into(), + data: None, + } + } else { + Error { + code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), + message: "Requested block number is in a range that is not available yet, because the ancient block sync is still in progress.".into(), + data: None, + } + } } pub fn check_block_number_existence<'a, T, C>( - client: &'a C, - num: BlockNumber, - allow_missing_blocks: bool, -) -> - impl Fn(Option) -> RpcResult> + 'a - where C: BlockChainClient, + client: &'a C, + num: BlockNumber, + options: EthClientOptions, +) -> impl Fn(Option) -> RpcResult> + 'a +where + C: BlockChainClient, { - move |response| { - if response.is_none() { - if let BlockNumber::Num(block_number) = num { - // tried to fetch block number and got nothing even though the block number is - // less than the latest block number - if block_number < client.chain_info().best_block_number && !allow_missing_blocks { - return Err(unavailable_block()); - } - } - } - Ok(response) - } + move |response| { + if response.is_none() { + if let BlockNumber::Num(block_number) = num { + // tried to fetch block number and got nothing even though the block number is + // less than the latest block number + if block_number < client.chain_info().best_block_number + && !options.allow_missing_blocks + { + return Err(unavailable_block(options.no_ancient_blocks, false)); + } + } + } + Ok(response) + } } pub fn check_block_gap<'a, T, C>( - client: &'a C, - allow_missing_blocks: bool, + client: &'a C, + options: EthClientOptions, ) -> impl Fn(Option) -> RpcResult> + 'a - where C: BlockChainClient, +where + C: BlockChainClient, { - move |response| { - if response.is_none() && !allow_missing_blocks { - let BlockChainInfo { ancient_block_hash, .. } = client.chain_info(); - // block information was requested, but unfortunately we couldn't find it and there - // are gaps in the database ethcore/src/blockchain/blockchain.rs - if ancient_block_hash.is_some() { - return Err(Error { - code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), - message: "Block information is incomplete while ancient block sync is still in progress, before \ - it's finished we can't determine the existence of requested item.".into(), - data: None, - }) - } - } - Ok(response) - } + move |response| { + if response.is_none() && !options.allow_missing_blocks { + let BlockChainInfo { + ancient_block_hash, .. + } = client.chain_info(); + // block information was requested, but unfortunately we couldn't find it and there + // are gaps in the database ethcore/src/blockchain/blockchain.rs + if ancient_block_hash.is_some() { + return Err(unavailable_block(options.no_ancient_blocks, true)); + } + } + Ok(response) + } } pub fn not_enough_data() -> Error { - Error { - code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), - message: "The node does not have enough data to compute the given statistic.".into(), - data: None, - } + Error { + code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), + message: "The node does not have enough data to compute the given statistic.".into(), + data: None, + } } pub fn token(e: String) -> Error { - Error { - code: ErrorCode::ServerError(codes::UNKNOWN_ERROR), - message: "There was an error when saving your authorization tokens.".into(), - data: Some(Value::String(e)), - } + Error { + code: ErrorCode::ServerError(codes::UNKNOWN_ERROR), + message: "There was an error when saving your authorization tokens.".into(), + data: Some(Value::String(e)), + } } pub fn signer_disabled() -> Error { - Error { - code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), - message: "Trusted Signer is disabled. This API is not available.".into(), - data: None, - } + Error { + code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), + message: "Trusted Signer is disabled. This API is not available.".into(), + data: None, + } } pub fn ws_disabled() -> Error { - Error { - code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), - message: "WebSockets Server is disabled. This API is not available.".into(), - data: None, - } + Error { + code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), + message: "WebSockets Server is disabled. This API is not available.".into(), + data: None, + } } pub fn network_disabled() -> Error { - Error { - code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), - message: "Network is disabled or not yet up.".into(), - data: None, - } + Error { + code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), + message: "Network is disabled or not yet up.".into(), + data: None, + } } pub fn encryption(error: T) -> Error { - Error { - code: ErrorCode::ServerError(codes::ENCRYPTION_ERROR), - message: "Encryption error.".into(), - data: Some(Value::String(format!("{:?}", error))), - } -} - -pub fn encoding(error: T) -> Error { - Error { - code: ErrorCode::ServerError(codes::ENCODING_ERROR), - message: "Encoding error.".into(), - data: Some(Value::String(format!("{:?}", error))), - } + Error { + code: ErrorCode::ServerError(codes::ENCRYPTION_ERROR), + message: "Encryption error.".into(), + data: Some(Value::String(format!("{:?}", error))), + } } pub fn database(error: T) -> Error { - Error { - code: ErrorCode::ServerError(codes::DATABASE_ERROR), - message: "Database error.".into(), - data: Some(Value::String(format!("{:?}", error))), - } + Error { + code: ErrorCode::ServerError(codes::DATABASE_ERROR), + message: "Database error.".into(), + data: Some(Value::String(format!("{:?}", error))), + } } pub fn fetch(error: T) -> Error { - Error { - code: ErrorCode::ServerError(codes::FETCH_ERROR), - message: "Error while fetching content.".into(), - data: Some(Value::String(format!("{:?}", error))), - } + Error { + code: ErrorCode::ServerError(codes::FETCH_ERROR), + message: "Error while fetching content.".into(), + data: Some(Value::String(format!("{:?}", error))), + } } #[cfg(any(test, feature = "accounts"))] pub fn invalid_call_data(error: T) -> Error { - Error { - code: ErrorCode::ServerError(codes::ENCODING_ERROR), - message: format!("{}", error), - data: None - } + Error { + code: ErrorCode::ServerError(codes::ENCODING_ERROR), + message: format!("{}", error), + data: None, + } } #[cfg(any(test, feature = "accounts"))] pub fn signing(error: ::accounts::SignError) -> Error { - Error { + Error { code: ErrorCode::ServerError(codes::ACCOUNT_LOCKED), message: "Your account is locked. Unlock the account via CLI, personal_unlockAccount or use Trusted Signer.".into(), data: Some(Value::String(format!("{:?}", error))), @@ -367,37 +357,24 @@ pub fn signing(error: ::accounts::SignError) -> Error { #[cfg(any(test, feature = "accounts"))] pub fn password(error: ::accounts::SignError) -> Error { - Error { - code: ErrorCode::ServerError(codes::PASSWORD_INVALID), - message: "Account password is invalid or account does not exist.".into(), - data: Some(Value::String(format!("{:?}", error))), - } -} - -pub fn private_message(error: PrivateTransactionError) -> Error { - Error { - code: ErrorCode::ServerError(codes::PRIVATE_ERROR), - message: "Private transactions call failed.".into(), - data: Some(Value::String(format!("{:?}", error))), - } -} - -pub fn private_message_block_id_not_supported() -> Error { - Error { - code: ErrorCode::ServerError(codes::PRIVATE_ERROR), - message: "Pending block id not supported.".into(), - data: None, - } + Error { + code: ErrorCode::ServerError(codes::PASSWORD_INVALID), + message: "Account password is invalid or account does not exist.".into(), + data: Some(Value::String(format!("{:?}", error))), + } } pub fn transaction_message(error: &TransactionError) -> String { - use self::TransactionError::*; + use self::TransactionError::*; - match *error { + match *error { AlreadyImported => "Transaction with the same hash was already imported.".into(), Old => "Transaction nonce is too low. Try incrementing the nonce.".into(), - TooCheapToReplace => { - "Transaction gas price is too low. There is another transaction with same nonce in the queue. Try increasing the gas price or incrementing the nonce.".into() + TooCheapToReplace { prev, new } => { + format!("Transaction gas price {} is too low. There is another transaction with same nonce in the queue{}. Try increasing the gas price or incrementing the nonce.", + new.map(|gas| format!("{}wei", gas)).unwrap_or("supplied".into()), + prev.map(|gas| format!(" with gas price: {}wei", gas)).unwrap_or("".into()) + ) } LimitReached => { "There are too many transactions in the queue. Your transaction was dropped due to limit. Try increasing the fee.".into() @@ -427,101 +404,96 @@ pub fn transaction_message(error: &TransactionError) -> String { } pub fn transaction>(error: T) -> Error { - let error = error.into(); - if let ErrorKind::Transaction(ref e) = *error.kind() { - Error { - code: ErrorCode::ServerError(codes::TRANSACTION_ERROR), - message: transaction_message(e), - data: None, - } - } else { - Error { - code: ErrorCode::ServerError(codes::UNKNOWN_ERROR), - message: "Unknown error when sending transaction.".into(), - data: Some(Value::String(format!("{:?}", error))), - } - } + let error = error.into(); + if let ErrorKind::Transaction(ref e) = *error.kind() { + Error { + code: ErrorCode::ServerError(codes::TRANSACTION_ERROR), + message: transaction_message(e), + data: None, + } + } else { + Error { + code: ErrorCode::ServerError(codes::UNKNOWN_ERROR), + message: "Unknown error when sending transaction.".into(), + data: Some(Value::String(format!("{:?}", error))), + } + } } pub fn decode>(error: T) -> Error { - let error = error.into(); - match *error.kind() { - ErrorKind::Decoder(ref dec_err) => rlp(dec_err.clone()), - _ => Error { - code: ErrorCode::InternalError, - message: "decoding error".into(), - data: None, - } - } + let error = error.into(); + match *error.kind() { + ErrorKind::Decoder(ref dec_err) => rlp(dec_err.clone()), + _ => Error { + code: ErrorCode::InternalError, + message: "decoding error".into(), + data: None, + }, + } } pub fn rlp(error: DecoderError) -> Error { - Error { - code: ErrorCode::InvalidParams, - message: "Invalid RLP.".into(), - data: Some(Value::String(format!("{:?}", error))), - } + Error { + code: ErrorCode::InvalidParams, + message: "Invalid RLP.".into(), + data: Some(Value::String(format!("{:?}", error))), + } } pub fn call(error: CallError) -> Error { - match error { - CallError::StatePruned => state_pruned(), - CallError::StateCorrupt => state_corrupt(), - CallError::Exceptional(e) => exceptional(e), - CallError::Execution(e) => execution(e), - CallError::TransactionNotFound => internal("{}, this should not be the case with eth_call, most likely a bug.", CallError::TransactionNotFound), - } + match error { + CallError::StatePruned => state_pruned(), + CallError::StateCorrupt => state_corrupt(), + CallError::Exceptional(e) => exceptional(e), + CallError::Execution(e) => execution(e), + CallError::TransactionNotFound => internal( + "{}, this should not be the case with eth_call, most likely a bug.", + CallError::TransactionNotFound, + ), + } } pub fn vm(error: &VMError, output: &[u8]) -> Error { - use rustc_hex::ToHex; + use rustc_hex::ToHex; - let data = match error { - &VMError::Reverted => format!("{} 0x{}", VMError::Reverted, output.to_hex()), - error => format!("{}", error), - }; + let data = match error { + &VMError::Reverted => format!("{} 0x{}", VMError::Reverted, output.to_hex()), + error => format!("{}", error), + }; - Error { - code: ErrorCode::ServerError(codes::EXECUTION_ERROR), - message: "VM execution error.".into(), - data: Some(Value::String(data)), - } + Error { + code: ErrorCode::ServerError(codes::EXECUTION_ERROR), + message: "VM execution error.".into(), + data: Some(Value::String(data)), + } } pub fn unknown_block() -> Error { - Error { - code: ErrorCode::InvalidParams, - message: "Unknown block number".into(), - data: None, - } -} - -pub fn no_light_peers() -> Error { - Error { - code: ErrorCode::ServerError(codes::NO_LIGHT_PEERS), - message: "No light peers who can serve data".into(), - data: None, - } + Error { + code: ErrorCode::InvalidParams, + message: "Unknown block number".into(), + data: None, + } } pub fn deprecated, T: Into>>(message: T) -> Error { - Error { - code: ErrorCode::ServerError(codes::DEPRECATED), - message: "Method deprecated".into(), - data: message.into().map(Into::into).map(Value::String), - } + Error { + code: ErrorCode::ServerError(codes::DEPRECATED), + message: "Method deprecated".into(), + data: message.into().map(Into::into).map(Value::String), + } } pub fn filter_not_found() -> Error { - Error { - code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), - message: "Filter not found".into(), - data: None, - } + Error { + code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), + message: "Filter not found".into(), + data: None, + } } pub fn filter_block_not_found(id: BlockId) -> Error { - Error { + Error { code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), // Specified in EIP-234. message: "One of the blocks specified in filter (fromBlock, toBlock or blockHash) cannot be found".into(), data: Some(Value::String(match id { @@ -533,65 +505,27 @@ pub fn filter_block_not_found(id: BlockId) -> Error { } } -pub fn on_demand_error(err: OnDemandError) -> Error { - match err { - OnDemandError(OnDemandErrorKind::ChannelCanceled(e), _) => on_demand_cancel(e), - OnDemandError(OnDemandErrorKind::RequestLimit, _) => timeout_new_peer(&err), - OnDemandError(OnDemandErrorKind::BadResponse(_), _) => max_attempts_reached(&err), - _ => on_demand_others(&err), - } -} - -// on-demand sender cancelled. -pub fn on_demand_cancel(_cancel: futures::sync::oneshot::Canceled) -> Error { - internal("on-demand sender cancelled", "") -} - -pub fn max_attempts_reached(err: &OnDemandError) -> Error { - Error { - code: ErrorCode::ServerError(codes::REQUEST_NOT_FOUND), - message: err.to_string(), - data: None, - } -} - -pub fn timeout_new_peer(err: &OnDemandError) -> Error { - Error { - code: ErrorCode::ServerError(codes::NO_LIGHT_PEERS), - message: err.to_string(), - data: None, - } -} - -pub fn on_demand_others(err: &OnDemandError) -> Error { - Error { - code: ErrorCode::ServerError(codes::UNKNOWN_ERROR), - message: err.to_string(), - data: None, - } -} - pub fn status_error(has_peers: bool) -> Error { - if has_peers { - no_work() - } else { - Error { - code: ErrorCode::ServerError(codes::NO_PEERS), - message: "Node is not connected to any peers.".into(), - data: None, - } - } + if has_peers { + no_work() + } else { + Error { + code: ErrorCode::ServerError(codes::NO_PEERS), + message: "Node is not connected to any peers.".into(), + data: None, + } + } } /// Returns a descriptive error in case experimental RPCs are not enabled. pub fn require_experimental(allow_experimental_rpcs: bool, eip: &str) -> Result<(), Error> { - if allow_experimental_rpcs { - Ok(()) - } else { - Err(Error { + if allow_experimental_rpcs { + Ok(()) + } else { + Err(Error { code: ErrorCode::ServerError(codes::EXPERIMENTAL_RPC), message: format!("This method is not part of the official RPC API yet (EIP-{}). Run with `--jsonrpc-experimental` to enable it.", eip), data: Some(Value::String(format!("See EIP: https://eips.ethereum.org/EIPS/eip-{}", eip))), }) - } + } } diff --git a/rpc/src/v1/helpers/external_signer/mod.rs b/rpc/src/v1/helpers/external_signer/mod.rs index 0797929cbdf..9af45967802 100644 --- a/rpc/src/v1/helpers/external_signer/mod.rs +++ b/rpc/src/v1/helpers/external_signer/mod.rs @@ -1,74 +1,77 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! An list of requests to be confirmed or signed by an external approver/signer. -use std::sync::Arc; -use std::ops::Deref; +use std::{ops::Deref, sync::Arc}; mod oneshot; mod signing_queue; -pub use self::signing_queue::{SigningQueue, ConfirmationsQueue, ConfirmationReceiver, ConfirmationResult}; #[cfg(test)] pub use self::signing_queue::QueueEvent; +pub use self::signing_queue::{ + ConfirmationReceiver, ConfirmationResult, ConfirmationsQueue, SigningQueue, +}; /// Manages communication with Signer crate pub struct SignerService { - is_enabled: bool, - queue: Arc, - generate_new_token: Box Result + Send + Sync + 'static>, + is_enabled: bool, + queue: Arc, + generate_new_token: Box Result + Send + Sync + 'static>, } impl SignerService { - /// Creates new Signer Service given function to generate new tokens. - pub fn new(new_token: F, is_enabled: bool) -> Self - where F: Fn() -> Result + Send + Sync + 'static { - SignerService { - queue: Arc::new(ConfirmationsQueue::default()), - generate_new_token: Box::new(new_token), - is_enabled, - } - } + /// Creates new Signer Service given function to generate new tokens. + pub fn new(new_token: F, is_enabled: bool) -> Self + where + F: Fn() -> Result + Send + Sync + 'static, + { + SignerService { + queue: Arc::new(ConfirmationsQueue::default()), + generate_new_token: Box::new(new_token), + is_enabled, + } + } - /// Generates new signer authorization token. - pub fn generate_token(&self) -> Result { - (self.generate_new_token)() - } + /// Generates new signer authorization token. + pub fn generate_token(&self) -> Result { + (self.generate_new_token)() + } - /// Returns a reference to `ConfirmationsQueue` - pub fn queue(&self) -> Arc { - self.queue.clone() - } + /// Returns a reference to `ConfirmationsQueue` + pub fn queue(&self) -> Arc { + self.queue.clone() + } - /// Returns true if Signer is enabled. - pub fn is_enabled(&self) -> bool { - self.is_enabled - } + /// Returns true if Signer is enabled. + pub fn is_enabled(&self) -> bool { + self.is_enabled + } - #[cfg(test)] - /// Creates new Signer Service for tests. - pub fn new_test(is_enabled: bool) -> Self { - SignerService::new(|| Ok("new_token".into()), is_enabled) - } + #[cfg(test)] + /// Creates new Signer Service for tests. + pub fn new_test(is_enabled: bool) -> Self { + SignerService::new(|| Ok("new_token".into()), is_enabled) + } } impl Deref for SignerService { - type Target = ConfirmationsQueue; - fn deref(&self) -> &Self::Target { - &self.queue - } + type Target = ConfirmationsQueue; + fn deref(&self) -> &Self::Target { + &self.queue + } } diff --git a/rpc/src/v1/helpers/external_signer/oneshot.rs b/rpc/src/v1/helpers/external_signer/oneshot.rs index eac3dca7f81..948951aad3f 100644 --- a/rpc/src/v1/helpers/external_signer/oneshot.rs +++ b/rpc/src/v1/helpers/external_signer/oneshot.rs @@ -1,67 +1,64 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use jsonrpc_core::Error; -use jsonrpc_core::futures::{self, Future}; -use jsonrpc_core::futures::sync::oneshot; +use jsonrpc_core::{ + futures::{self, sync::oneshot, Future}, + Error, +}; use v1::helpers::errors; pub type Res = Result; pub struct Sender { - sender: oneshot::Sender>, + sender: oneshot::Sender>, } impl Sender { - pub fn send(self, data: Res) { - let res = self.sender.send(data); - if res.is_err() { - debug!(target: "rpc", "Responding to a no longer active request."); - } - } + pub fn send(self, data: Res) { + let res = self.sender.send(data); + if res.is_err() { + debug!(target: "rpc", "Responding to a no longer active request."); + } + } } pub struct Receiver { - receiver: oneshot::Receiver>, + receiver: oneshot::Receiver>, } impl Future for Receiver { - type Item = T; - type Error = Error; + type Item = T; + type Error = Error; - fn poll(&mut self) -> futures::Poll { - let res = self.receiver.poll(); - match res { - Ok(futures::Async::NotReady) => Ok(futures::Async::NotReady), - Ok(futures::Async::Ready(Ok(res))) => Ok(futures::Async::Ready(res)), - Ok(futures::Async::Ready(Err(err))) => Err(err), - Err(e) => { - debug!(target: "rpc", "Responding to a canceled request: {:?}", e); - Err(errors::internal("Request was canceled by client.", e)) - }, - } - } + fn poll(&mut self) -> futures::Poll { + let res = self.receiver.poll(); + match res { + Ok(futures::Async::NotReady) => Ok(futures::Async::NotReady), + Ok(futures::Async::Ready(Ok(res))) => Ok(futures::Async::Ready(res)), + Ok(futures::Async::Ready(Err(err))) => Err(err), + Err(e) => { + debug!(target: "rpc", "Responding to a canceled request: {:?}", e); + Err(errors::internal("Request was canceled by client.", e)) + } + } + } } pub fn oneshot() -> (Sender, Receiver) { - let (tx, rx) = futures::oneshot(); + let (tx, rx) = futures::oneshot(); - (Sender { - sender: tx, - }, Receiver { - receiver: rx, - }) + (Sender { sender: tx }, Receiver { receiver: rx }) } diff --git a/rpc/src/v1/helpers/external_signer/signing_queue.rs b/rpc/src/v1/helpers/external_signer/signing_queue.rs index 00a459a869a..745304836ba 100644 --- a/rpc/src/v1/helpers/external_signer/signing_queue.rs +++ b/rpc/src/v1/helpers/external_signer/signing_queue.rs @@ -1,27 +1,31 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use std::collections::BTreeMap; +use super::oneshot; use ethereum_types::U256; use parking_lot::{Mutex, RwLock}; -use super::oneshot; -use v1::helpers::errors; -use v1::helpers::requests::{ConfirmationRequest, ConfirmationPayload}; -use v1::types::{ConfirmationResponse, Origin}; +use v1::{ + helpers::{ + errors, + requests::{ConfirmationPayload, ConfirmationRequest}, + }, + types::{ConfirmationResponse, Origin}, +}; use jsonrpc_core::Error; @@ -31,20 +35,20 @@ pub type ConfirmationResult = Result; /// Possible events happening in the queue that can be listened to. #[derive(Debug, PartialEq, Clone)] pub enum QueueEvent { - /// Receiver should stop work upon receiving `Finish` message. - Finish, - /// Informs about new request. - NewRequest(U256), - /// Request rejected. - RequestRejected(U256), - /// Request resolved. - RequestConfirmed(U256), + /// Receiver should stop work upon receiving `Finish` message. + Finish, + /// Informs about new request. + NewRequest(U256), + /// Request rejected. + RequestRejected(U256), + /// Request resolved. + RequestConfirmed(U256), } /// Defines possible errors when inserting to queue #[derive(Debug, PartialEq)] pub enum QueueAddError { - LimitReached, + LimitReached, } // TODO [todr] to consider: timeout instead of limit? @@ -52,241 +56,275 @@ pub const QUEUE_LIMIT: usize = 50; /// A queue of transactions awaiting to be confirmed and signed. pub trait SigningQueue: Send + Sync { - /// Add new request to the queue. - /// Returns a `Result` wrapping `ConfirmationReceiver` together with it's unique id in the queue. - /// `ConfirmationReceiver` is a `Future` awaiting for resolution of the given request. - fn add_request(&self, request: ConfirmationPayload, origin: Origin) -> Result<(U256, ConfirmationReceiver), QueueAddError>; - - /// Notifies possible token holders that request was rejected. - fn request_rejected(&self, sender: ConfirmationSender) -> Option; - - /// Notifies possible token holders that request was confirmed and given hash was assigned. - fn request_confirmed(&self, sender: ConfirmationSender, result: ConfirmationResult) -> Option; - - /// Put a request taken from `SigningQueue::take` back to the queue. - fn request_untouched(&self, sender: ConfirmationSender); - - /// Returns and removes a request if it is contained in the queue. - fn take(&self, id: &U256) -> Option; - - /// Return copy of all the requests in the queue. - fn requests(&self) -> Vec; - - /// Returns number of requests awaiting confirmation. - fn len(&self) -> usize; - - /// Returns true if there are no requests awaiting confirmation. - fn is_empty(&self) -> bool; + /// Add new request to the queue. + /// Returns a `Result` wrapping `ConfirmationReceiver` together with it's unique id in the queue. + /// `ConfirmationReceiver` is a `Future` awaiting for resolution of the given request. + fn add_request( + &self, + request: ConfirmationPayload, + origin: Origin, + ) -> Result<(U256, ConfirmationReceiver), QueueAddError>; + + /// Notifies possible token holders that request was rejected. + fn request_rejected(&self, sender: ConfirmationSender) -> Option; + + /// Notifies possible token holders that request was confirmed and given hash was assigned. + fn request_confirmed( + &self, + sender: ConfirmationSender, + result: ConfirmationResult, + ) -> Option; + + /// Put a request taken from `SigningQueue::take` back to the queue. + fn request_untouched(&self, sender: ConfirmationSender); + + /// Returns and removes a request if it is contained in the queue. + fn take(&self, id: &U256) -> Option; + + /// Return copy of all the requests in the queue. + fn requests(&self) -> Vec; + + /// Returns number of requests awaiting confirmation. + fn len(&self) -> usize; + + /// Returns true if there are no requests awaiting confirmation. + fn is_empty(&self) -> bool; } /// Confirmation request information with result notifier. pub struct ConfirmationSender { - /// Confirmation request information. - pub request: ConfirmationRequest, + /// Confirmation request information. + pub request: ConfirmationRequest, - sender: oneshot::Sender, + sender: oneshot::Sender, } /// Receiving end of the Confirmation channel; can be used as a `Future` to await for `ConfirmationRequest` /// being processed and turned into `ConfirmationOutcome` -pub type ConfirmationReceiver = oneshot::Receiver; +pub type ConfirmationReceiver = oneshot::Receiver; /// Queue for all unconfirmed requests. #[derive(Default)] pub struct ConfirmationsQueue { - id: Mutex, - queue: RwLock>, - on_event: RwLock () + Send + Sync>>>, + id: Mutex, + queue: RwLock>, + on_event: RwLock () + Send + Sync>>>, } impl ConfirmationsQueue { - /// Adds a queue listener. For each event, `listener` callback will be invoked. - pub fn on_event () + Send + Sync + 'static>(&self, listener: F) { - self.on_event.write().push(Box::new(listener)); - } - - /// Notifies consumer that the communcation is over. - /// No more events will be sent after this function is invoked. - pub fn finish(&self) { - self.notify_message(QueueEvent::Finish); - self.on_event.write().clear(); - } - - /// Notifies `ConfirmationReceiver` holder about the result given a request. - fn notify_result(&self, sender: ConfirmationSender, result: Option) -> Option { - // notify receiver about the event - self.notify_message(result.clone().map_or_else( - || QueueEvent::RequestRejected(sender.request.id), - |_| QueueEvent::RequestConfirmed(sender.request.id) - )); - - // notify confirmation receiver about resolution - let result = result.ok_or_else(errors::request_rejected); - sender.sender.send(result); - - Some(sender.request) - } - - /// Notifies receiver about the event happening in this queue. - fn notify_message(&self, message: QueueEvent) { - for listener in &*self.on_event.read() { - listener(message.clone()) - } - } + /// Adds a queue listener. For each event, `listener` callback will be invoked. + pub fn on_event () + Send + Sync + 'static>(&self, listener: F) { + self.on_event.write().push(Box::new(listener)); + } + + /// Notifies consumer that the communcation is over. + /// No more events will be sent after this function is invoked. + pub fn finish(&self) { + self.notify_message(QueueEvent::Finish); + self.on_event.write().clear(); + } + + /// Notifies `ConfirmationReceiver` holder about the result given a request. + fn notify_result( + &self, + sender: ConfirmationSender, + result: Option, + ) -> Option { + // notify receiver about the event + self.notify_message(result.clone().map_or_else( + || QueueEvent::RequestRejected(sender.request.id), + |_| QueueEvent::RequestConfirmed(sender.request.id), + )); + + // notify confirmation receiver about resolution + let result = result.ok_or_else(errors::request_rejected); + sender.sender.send(result); + + Some(sender.request) + } + + /// Notifies receiver about the event happening in this queue. + fn notify_message(&self, message: QueueEvent) { + for listener in &*self.on_event.read() { + listener(message.clone()) + } + } } impl Drop for ConfirmationsQueue { - fn drop(&mut self) { - self.finish(); - } + fn drop(&mut self) { + self.finish(); + } } impl SigningQueue for ConfirmationsQueue { - fn add_request(&self, request: ConfirmationPayload, origin: Origin) -> Result<(U256, ConfirmationReceiver), QueueAddError> { - if self.len() > QUEUE_LIMIT { - return Err(QueueAddError::LimitReached); - } - - // Increment id - let id = { - let mut last_id = self.id.lock(); - *last_id += U256::from(1); - *last_id - }; - // Add request to queue - let res = { - debug!(target: "own_tx", "Signer: New entry ({:?}) in confirmation queue.", id); - trace!(target: "own_tx", "Signer: ({:?}) : {:?}", id, request); - - let mut queue = self.queue.write(); - let (sender, receiver) = oneshot::oneshot::(); - - queue.insert(id, ConfirmationSender { - sender, - request: ConfirmationRequest { - id, - payload: request, - origin, - }, - }); - (id, receiver) - }; - // Notify listeners - self.notify_message(QueueEvent::NewRequest(id)); - Ok(res) - } - - fn take(&self, id: &U256) -> Option { - self.queue.write().remove(id) - } - - fn request_rejected(&self, sender: ConfirmationSender) -> Option { - debug!(target: "own_tx", "Signer: Request rejected ({:?}).", sender.request.id); - self.notify_result(sender, None) - } - - fn request_confirmed(&self, sender: ConfirmationSender, result: ConfirmationResult) -> Option { - debug!(target: "own_tx", "Signer: Request confirmed ({:?}).", sender.request.id); - self.notify_result(sender, Some(result)) - } - - fn request_untouched(&self, sender: ConfirmationSender) { - self.queue.write().insert(sender.request.id, sender); - } - - fn requests(&self) -> Vec { - let queue = self.queue.read(); - queue.values().map(|sender| sender.request.clone()).collect() - } - - fn len(&self) -> usize { - let queue = self.queue.read(); - queue.len() - } - - fn is_empty(&self) -> bool { - let queue = self.queue.read(); - queue.is_empty() - } + fn add_request( + &self, + request: ConfirmationPayload, + origin: Origin, + ) -> Result<(U256, ConfirmationReceiver), QueueAddError> { + if self.len() > QUEUE_LIMIT { + return Err(QueueAddError::LimitReached); + } + + // Increment id + let id = { + let mut last_id = self.id.lock(); + *last_id += U256::from(1); + *last_id + }; + // Add request to queue + let res = { + debug!(target: "own_tx", "Signer: New entry ({:?}) in confirmation queue.", id); + trace!(target: "own_tx", "Signer: ({:?}) : {:?}", id, request); + + let mut queue = self.queue.write(); + let (sender, receiver) = oneshot::oneshot::(); + + queue.insert( + id, + ConfirmationSender { + sender, + request: ConfirmationRequest { + id, + payload: request, + origin, + }, + }, + ); + (id, receiver) + }; + // Notify listeners + self.notify_message(QueueEvent::NewRequest(id)); + Ok(res) + } + + fn take(&self, id: &U256) -> Option { + self.queue.write().remove(id) + } + + fn request_rejected(&self, sender: ConfirmationSender) -> Option { + debug!(target: "own_tx", "Signer: Request rejected ({:?}).", sender.request.id); + self.notify_result(sender, None) + } + + fn request_confirmed( + &self, + sender: ConfirmationSender, + result: ConfirmationResult, + ) -> Option { + debug!(target: "own_tx", "Signer: Request confirmed ({:?}).", sender.request.id); + self.notify_result(sender, Some(result)) + } + + fn request_untouched(&self, sender: ConfirmationSender) { + self.queue.write().insert(sender.request.id, sender); + } + + fn requests(&self) -> Vec { + let queue = self.queue.read(); + queue + .values() + .map(|sender| sender.request.clone()) + .collect() + } + + fn len(&self) -> usize { + let queue = self.queue.read(); + queue.len() + } + + fn is_empty(&self) -> bool { + let queue = self.queue.read(); + queue.is_empty() + } } #[cfg(test)] mod test { - use std::sync::Arc; - use ethereum_types::{U256, Address}; - use parking_lot::Mutex; - use jsonrpc_core::futures::Future; - use v1::helpers::external_signer::{SigningQueue, ConfirmationsQueue, QueueEvent}; - use v1::helpers::{FilledTransactionRequest, ConfirmationPayload}; - use v1::types::ConfirmationResponse; - - fn request() -> ConfirmationPayload { - ConfirmationPayload::SendTransaction(FilledTransactionRequest { - from: Address::from(1), - used_default_from: false, - to: Some(Address::from(2)), - gas_price: 0.into(), - gas: 10_000.into(), - value: 10_000_000.into(), - data: vec![], - nonce: None, - condition: None, - }) - } - - #[test] - fn should_wait_for_hash() { - // given - let queue = Arc::new(ConfirmationsQueue::default()); - let request = request(); - - // when - let (id, future) = queue.add_request(request, Default::default()).unwrap(); - let sender = queue.take(&id).unwrap(); - queue.request_confirmed(sender, Ok(ConfirmationResponse::SendTransaction(1.into()))); - - // then - let confirmation = future.wait().unwrap(); - assert_eq!(confirmation, Ok(ConfirmationResponse::SendTransaction(1.into()))); - } - - #[test] - fn should_receive_notification() { - // given - let received = Arc::new(Mutex::new(vec![])); - let queue = Arc::new(ConfirmationsQueue::default()); - let request = request(); - - // when - let r = received.clone(); - queue.on_event(move |notification| { - r.lock().push(notification); - }); - let _future = queue.add_request(request, Default::default()).unwrap(); - queue.finish(); - - // then - let r = received.lock(); - assert_eq!(r[0], QueueEvent::NewRequest(U256::from(1))); - assert_eq!(r[1], QueueEvent::Finish); - assert_eq!(r.len(), 2); - } - - #[test] - fn should_add_transactions() { - // given - let queue = ConfirmationsQueue::default(); - let request = request(); - - // when - let _future = queue.add_request(request.clone(), Default::default()).unwrap(); - let all = queue.requests(); - - // then - assert_eq!(all.len(), 1); - let el = all.get(0).unwrap(); - assert_eq!(el.id, U256::from(1)); - assert_eq!(el.payload, request); - } + use ethereum_types::{Address, U256}; + use jsonrpc_core::futures::Future; + use parking_lot::Mutex; + use std::sync::Arc; + use v1::{ + helpers::{ + external_signer::{ConfirmationsQueue, QueueEvent, SigningQueue}, + ConfirmationPayload, FilledTransactionRequest, + }, + types::ConfirmationResponse, + }; + + fn request() -> ConfirmationPayload { + ConfirmationPayload::SendTransaction(FilledTransactionRequest { + from: Address::from(1), + used_default_from: false, + to: Some(Address::from(2)), + gas_price: 0.into(), + gas: 10_000.into(), + value: 10_000_000.into(), + data: vec![], + nonce: None, + condition: None, + }) + } + + #[test] + fn should_wait_for_hash() { + // given + let queue = Arc::new(ConfirmationsQueue::default()); + let request = request(); + + // when + let (id, future) = queue.add_request(request, Default::default()).unwrap(); + let sender = queue.take(&id).unwrap(); + queue.request_confirmed(sender, Ok(ConfirmationResponse::SendTransaction(1.into()))); + + // then + let confirmation = future.wait().unwrap(); + assert_eq!( + confirmation, + Ok(ConfirmationResponse::SendTransaction(1.into())) + ); + } + + #[test] + fn should_receive_notification() { + // given + let received = Arc::new(Mutex::new(vec![])); + let queue = Arc::new(ConfirmationsQueue::default()); + let request = request(); + + // when + let r = received.clone(); + queue.on_event(move |notification| { + r.lock().push(notification); + }); + let _future = queue.add_request(request, Default::default()).unwrap(); + queue.finish(); + + // then + let r = received.lock(); + assert_eq!(r[0], QueueEvent::NewRequest(U256::from(1))); + assert_eq!(r[1], QueueEvent::Finish); + assert_eq!(r.len(), 2); + } + + #[test] + fn should_add_transactions() { + // given + let queue = ConfirmationsQueue::default(); + let request = request(); + + // when + let _future = queue + .add_request(request.clone(), Default::default()) + .unwrap(); + let all = queue.requests(); + + // then + assert_eq!(all.len(), 1); + let el = all.get(0).unwrap(); + assert_eq!(el.id, U256::from(1)); + assert_eq!(el.payload, request); + } } - diff --git a/rpc/src/v1/helpers/fake_sign.rs b/rpc/src/v1/helpers/fake_sign.rs index d93408b89a7..de9ae57089f 100644 --- a/rpc/src/v1/helpers/fake_sign.rs +++ b/rpc/src/v1/helpers/fake_sign.rs @@ -1,37 +1,38 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use types::transaction::{Transaction, SignedTransaction, Action}; use std::cmp::min; +use types::transaction::{Action, SignedTransaction, Transaction}; use ethereum_types::U256; use jsonrpc_core::Error; use v1::helpers::CallRequest; pub fn sign_call(request: CallRequest) -> Result { - let max_gas = U256::from(500_000_000); - let gas = min(request.gas.unwrap_or(max_gas), max_gas); - let from = request.from.unwrap_or_default(); + let max_gas = U256::from(500_000_000); + let gas = min(request.gas.unwrap_or(max_gas), max_gas); + let from = request.from.unwrap_or_default(); - Ok(Transaction { - nonce: request.nonce.unwrap_or_default(), - action: request.to.map_or(Action::Create, Action::Call), - gas, - gas_price: request.gas_price.unwrap_or_default(), - value: request.value.unwrap_or_default(), - data: request.data.unwrap_or_default(), - }.fake_sign(from)) + Ok(Transaction { + nonce: request.nonce.unwrap_or_default(), + action: request.to.map_or(Action::Create, Action::Call), + gas, + gas_price: request.gas_price.unwrap_or_default(), + value: request.value.unwrap_or_default(), + data: request.data.unwrap_or_default(), + } + .fake_sign(from)) } diff --git a/rpc/src/v1/helpers/ipfs.rs b/rpc/src/v1/helpers/ipfs.rs deleted file mode 100644 index 93110dbf34d..00000000000 --- a/rpc/src/v1/helpers/ipfs.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! IPFS utility functions - -use multihash; -use cid::{Cid, Codec, Version}; -use crypto::digest; -use jsonrpc_core::Error; -use v1::types::Bytes; -use super::errors; - -/// Compute CIDv0 from protobuf encoded bytes. -pub fn cid(content: Bytes) -> Result { - let hash = digest::sha256(&content.0); - let mh = multihash::encode(multihash::Hash::SHA2256, &*hash).map_err(errors::encoding)?; - let cid = Cid::new(Codec::DagProtobuf, Version::V0, &mh); - Ok(cid.to_string()) -} diff --git a/rpc/src/v1/helpers/light_fetch.rs b/rpc/src/v1/helpers/light_fetch.rs deleted file mode 100644 index e18695fcb76..00000000000 --- a/rpc/src/v1/helpers/light_fetch.rs +++ /dev/null @@ -1,791 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Helpers for fetching blockchain data either from the light client or the network. - -use std::cmp; -use std::clone::Clone; -use std::sync::Arc; - -use types::basic_account::BasicAccount; -use types::encoded; -use types::filter::Filter as EthcoreFilter; -use types::ids::BlockId; -use types::receipt::Receipt; -use ethcore::executed::ExecutionError; - -use jsonrpc_core::{Result, Error}; -use jsonrpc_core::futures::{future, Future}; -use jsonrpc_core::futures::future::Either; - -use light::cache::Cache; -use light::client::LightChainClient; -use light::{cht, MAX_HEADERS_PER_REQUEST}; -use light::on_demand::{ - request, OnDemandRequester, HeaderRef, Request as OnDemandRequest, - Response as OnDemandResponse, ExecutionResult, -}; -use light::on_demand::error::Error as OnDemandError; -use light::request::Field; -use light::TransactionQueue; - -use sync::{LightNetworkDispatcher, ManageNetwork, LightSyncProvider}; - -use ethereum_types::{Address, U256}; -use hash::H256; -use parking_lot::{Mutex, RwLock}; -use fastmap::H256FastMap; -use std::collections::BTreeMap; -use types::transaction::{Action, Transaction as EthTransaction, PendingTransaction, SignedTransaction, LocalizedTransaction}; - -use v1::helpers::{CallRequest as CallRequestHelper, errors, dispatch}; -use v1::types::{BlockNumber, CallRequest, Log, Transaction}; - -const NO_INVALID_BACK_REFS_PROOF: &str = "Fails only on invalid back-references; back-references here known to be valid; qed"; -const WRONG_RESPONSE_AMOUNT_TYPE_PROOF: &str = "responses correspond directly with requests in amount and type; qed"; -const DEFAULT_GAS_PRICE: u64 = 21_000; - -pub fn light_all_transactions(dispatch: &Arc>) -> impl Iterator -where - S: LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, - OD: OnDemandRequester + 'static -{ - let txq = dispatch.transaction_queue.read(); - let chain_info = dispatch.client.chain_info(); - - let current = txq.ready_transactions(chain_info.best_block_number, chain_info.best_block_timestamp); - let future = txq.future_transactions(chain_info.best_block_number, chain_info.best_block_timestamp); - current.into_iter().chain(future.into_iter()) -} - -/// Helper for fetching blockchain data either from the light client or the network -/// as necessary. -pub struct LightFetch -where - S: LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, - OD: OnDemandRequester + 'static -{ - /// The light client. - pub client: Arc, - /// The on-demand request service. - pub on_demand: Arc, - /// Handle to the network. - pub sync: Arc, - /// The light data cache. - pub cache: Arc>, - /// Gas Price percentile - pub gas_price_percentile: usize, -} - -impl Clone for LightFetch -where - S: LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, - OD: OnDemandRequester + 'static -{ - fn clone(&self) -> Self { - Self { - client: self.client.clone(), - on_demand: self.on_demand.clone(), - sync: self.sync.clone(), - cache: self.cache.clone(), - gas_price_percentile: self.gas_price_percentile, - } - } -} - - -/// Extract a transaction at given index. -pub fn extract_transaction_at_index(block: encoded::Block, index: usize) -> Option { - block.transactions().into_iter().nth(index) - // Verify if transaction signature is correct. - .and_then(|tx| SignedTransaction::new(tx).ok()) - .map(|signed_tx| { - let (signed, sender, _) = signed_tx.deconstruct(); - let block_hash = block.hash(); - let block_number = block.number(); - let transaction_index = index; - let cached_sender = Some(sender); - - LocalizedTransaction { - signed, - block_number, - block_hash, - transaction_index, - cached_sender, - } - }) - .map(Transaction::from_localized) -} - -// extract the header indicated by the given `HeaderRef` from the given responses. -// fails only if they do not correspond. -fn extract_header(res: &[OnDemandResponse], header: HeaderRef) -> Option { - match header { - HeaderRef::Stored(hdr) => Some(hdr), - HeaderRef::Unresolved(idx, _) => match res.get(idx) { - Some(&OnDemandResponse::HeaderByHash(ref hdr)) => Some(hdr.clone()), - _ => None, - }, - } -} - -impl LightFetch -where - S: LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, - OD: OnDemandRequester + 'static -{ - // push the necessary requests onto the request chain to get the header by the given ID. - // yield a header reference which other requests can use. - fn make_header_requests(&self, id: BlockId, reqs: &mut Vec) -> Result { - if let Some(h) = self.client.block_header(id) { - return Ok(h.into()); - } - - match id { - BlockId::Number(n) => { - let cht_root = cht::block_to_cht_number(n).and_then(|cn| self.client.cht_root(cn as usize)); - match cht_root { - None => Err(errors::unknown_block()), - Some(root) => { - let req = request::HeaderProof::new(n, root) - .expect("only fails for 0; client always stores genesis; client already queried; qed"); - - let idx = reqs.len(); - let hash_ref = Field::back_ref(idx, 0); - reqs.push(req.into()); - reqs.push(request::HeaderByHash(hash_ref).into()); - - Ok(HeaderRef::Unresolved(idx + 1, hash_ref)) - } - } - } - BlockId::Hash(h) => { - let idx = reqs.len(); - reqs.push(request::HeaderByHash(h.into()).into()); - Ok(HeaderRef::Unresolved(idx, h.into())) - } - _ => Err(errors::unknown_block()) // latest, earliest, and pending will have all already returned. - } - } - - /// Get a block header from the on demand service or client, or error. - pub fn header(&self, id: BlockId) -> impl Future + Send { - let mut reqs = Vec::new(); - let header_ref = match self.make_header_requests(id, &mut reqs) { - Ok(r) => r, - Err(e) => return Either::A(future::err(e)), - }; - - Either::B(self.send_requests(reqs, |res| - extract_header(&res, header_ref) - .expect("these responses correspond to requests that header_ref belongs to \ - therefore it will not fail; qed") - )) - } - - /// Helper for getting contract code at a given block. - pub fn code(&self, address: Address, id: BlockId) -> impl Future, Error = Error> + Send { - let mut reqs = Vec::new(); - let header_ref = match self.make_header_requests(id, &mut reqs) { - Ok(r) => r, - Err(e) => return Either::A(future::err(e)), - }; - - reqs.push(request::Account { header: header_ref.clone(), address }.into()); - let account_idx = reqs.len() - 1; - reqs.push(request::Code { header: header_ref, code_hash: Field::back_ref(account_idx, 0) }.into()); - - Either::B(self.send_requests(reqs, |mut res| match res.pop() { - Some(OnDemandResponse::Code(code)) => code, - _ => panic!(WRONG_RESPONSE_AMOUNT_TYPE_PROOF), - })) - } - - /// Helper for getting account info at a given block. - /// `None` indicates the account doesn't exist at the given block. - pub fn account( - &self, - address: Address, - id: BlockId, - tx_queue: Arc> - ) -> impl Future, Error = Error> + Send { - - let mut reqs = Vec::new(); - let header_ref = match self.make_header_requests(id, &mut reqs) { - Ok(r) => r, - Err(e) => return Either::A(future::err(e)), - }; - - reqs.push(request::Account { header: header_ref, address }.into()); - - Either::B(self.send_requests(reqs, move |mut res| match res.pop() { - Some(OnDemandResponse::Account(maybe_account)) => { - if let Some(ref acc) = maybe_account { - let mut txq = tx_queue.write(); - txq.cull(address, acc.nonce); - } - maybe_account - } - _ => panic!(WRONG_RESPONSE_AMOUNT_TYPE_PROOF), - })) - } - - /// Helper for getting proved execution. - pub fn proved_read_only_execution( - &self, - req: CallRequest, - num: Option, - txq: Arc> - ) -> impl Future + Send { - - // (21000 G_transaction + 32000 G_create + some marginal to allow a few operations) - const START_GAS: u64 = 60_000; - - let (sync, on_demand, client) = (self.sync.clone(), self.on_demand.clone(), self.client.clone()); - let req: CallRequestHelper = req.into(); - - // Note: Here we treat `Pending` as `Latest`. - // Since light clients don't produce pending blocks - // (they don't have state) we can safely fallback to `Latest`. - let id = match num.unwrap_or_default() { - BlockNumber::Num(n) => BlockId::Number(n), - BlockNumber::Earliest => BlockId::Earliest, - BlockNumber::Latest => BlockId::Latest, - BlockNumber::Pending => { - warn!("`Pending` is deprecated and may be removed in future versions. Falling back to `Latest`"); - BlockId::Latest - } - }; - - let from = req.from.unwrap_or_default(); - let nonce_fut = match req.nonce { - Some(nonce) => Either::A(future::ok(Some(nonce))), - None => Either::B(self.account(from, id, txq).map(|acc| acc.map(|a| a.nonce))), - }; - - let gas_price_fut = match req.gas_price { - Some(price) => Either::A(future::ok(price)), - None => Either::B(self.gas_price()), - }; - - // if nonce resolves, this should too since it'll be in the LRU-cache. - let header_fut = self.header(id); - - // fetch missing transaction fields from the network. - Box::new(nonce_fut.join(gas_price_fut).and_then(move |(nonce, gas_price)| { - future::done( - Ok((req.gas.is_some(), EthTransaction { - nonce: nonce.unwrap_or_default(), - action: req.to.map_or(Action::Create, Action::Call), - gas: req.gas.unwrap_or_else(|| START_GAS.into()), - gas_price, - value: req.value.unwrap_or_default(), - data: req.data.unwrap_or_default(), - })) - ) - }).join(header_fut).and_then(move |((gas_known, tx), hdr)| { - // then request proved execution. - // TODO: get last-hashes from network. - let hash = hdr.hash(); - let env_info = match client.env_info(BlockId::Hash(hash)) { - Some(env_info) => env_info, - _ => return Either::A(future::err(errors::unknown_block())), - }; - - Either::B(execute_read_only_tx(gas_known, ExecuteParams { - from, - tx, - hdr, - env_info, - engine: client.engine().clone(), - on_demand, - sync, - })) - })) - } - - /// Helper to fetch the corpus gas price from 1) the cache 2) the network then it tries to estimate the percentile - /// using `gas_price_percentile` if the estimated percentile is zero the `DEFAULT_GAS_PRICE` is returned - pub fn gas_price(&self) -> impl Future + Send { - let gas_price_percentile = self.gas_price_percentile; - - dispatch::light::fetch_gas_price_corpus( - self.sync.clone(), - self.client.clone(), - self.on_demand.clone(), - self.cache.clone(), - ) - .map(move |corp| { - corp.percentile(gas_price_percentile) - .map_or_else(|| DEFAULT_GAS_PRICE.into(), |percentile| *percentile) - }) - } - - /// Get a block itself. Fails on unknown block ID. - pub fn block(&self, id: BlockId) -> impl Future + Send { - let mut reqs = Vec::new(); - let header_ref = match self.make_header_requests(id, &mut reqs) { - Ok(r) => r, - Err(e) => return Either::A(future::err(e)), - }; - - reqs.push(request::Body(header_ref).into()); - - Either::B(self.send_requests(reqs, |mut res| match res.pop() { - Some(OnDemandResponse::Body(b)) => b, - _ => panic!(WRONG_RESPONSE_AMOUNT_TYPE_PROOF), - })) - } - - /// Get the block receipts. Fails on unknown block ID. - pub fn receipts(&self, id: BlockId) -> impl Future, Error = Error> + Send { - let mut reqs = Vec::new(); - let header_ref = match self.make_header_requests(id, &mut reqs) { - Ok(r) => r, - Err(e) => return Either::A(future::err(e)), - }; - - reqs.push(request::BlockReceipts(header_ref).into()); - - Either::B(self.send_requests(reqs, |mut res| match res.pop() { - Some(OnDemandResponse::Receipts(b)) => b, - _ => panic!(WRONG_RESPONSE_AMOUNT_TYPE_PROOF), - })) - } - - pub fn logs_no_tx_hash(&self, filter: EthcoreFilter) -> impl Future, Error = Error> + Send { - use jsonrpc_core::futures::stream::{self, Stream}; - - const MAX_BLOCK_RANGE: u64 = 1000; - - let fetcher = self.clone(); - self.headers_range_by_block_id(filter.from_block, filter.to_block, MAX_BLOCK_RANGE) - .and_then(move |mut headers| { - if headers.is_empty() { - return Either::A(future::ok(Vec::new())); - } - - let on_demand = &fetcher.on_demand; - - let maybe_future = fetcher.sync.with_context(move |ctx| { - // find all headers which match the filter, and fetch the receipts for each one. - // match them with their numbers for easy sorting later. - let bit_combos = filter.bloom_possibilities(); - let receipts_futures: Vec<_> = headers.drain(..) - .filter(|ref hdr| { - let hdr_bloom = hdr.log_bloom(); - bit_combos.iter().any(|bloom| hdr_bloom.contains_bloom(bloom)) - }) - .map(|hdr| (hdr.number(), hdr.hash(), request::BlockReceipts(hdr.into()))) - .map(|(num, hash, req)| on_demand.request(ctx, req).expect(NO_INVALID_BACK_REFS_PROOF).map(move |x| (num, hash, x))) - .collect(); - - // as the receipts come in, find logs within them which match the filter. - // insert them into a BTreeMap to maintain order by number and block index. - stream::futures_unordered(receipts_futures) - .fold(BTreeMap::new(), move |mut matches, (num, hash, receipts)| { - let mut block_index: usize = 0; - for (transaction_index, receipt) in receipts.into_iter().enumerate() { - for (transaction_log_index, log) in receipt.logs.into_iter().enumerate() { - if filter.matches(&log) { - matches.insert((num, block_index), Log { - address: log.address, - topics: log.topics.into_iter().map(Into::into).collect(), - data: log.data.into(), - block_hash: Some(hash), - block_number: Some(num.into()), - // No way to easily retrieve transaction hash, so let's just skip it. - transaction_hash: None, - transaction_index: Some(transaction_index.into()), - log_index: Some(block_index.into()), - transaction_log_index: Some(transaction_log_index.into()), - log_type: "mined".into(), - removed: false, - }); - } - block_index += 1; - } - } - future::ok::<_, OnDemandError>(matches) - }) - .map_err(errors::on_demand_error) - .map(|matches| matches.into_iter().map(|(_, v)| v).collect()) - }); - - match maybe_future { - Some(fut) => Either::B(Either::A(fut)), - None => Either::B(Either::B(future::err(errors::network_disabled()))), - } - }) - } - - /// Get transaction logs - pub fn logs(&self, filter: EthcoreFilter) -> impl Future, Error = Error> + Send { - use jsonrpc_core::futures::stream::{self, Stream}; - let fetcher_block = self.clone(); - self.logs_no_tx_hash(filter) - // retrieve transaction hash. - .and_then(move |mut result| { - let mut blocks = BTreeMap::new(); - for log in result.iter() { - let block_hash = log.block_hash.as_ref().expect("Previously initialized with value; qed"); - blocks.entry(*block_hash).or_insert_with(|| { - fetcher_block.block(BlockId::Hash(*block_hash)) - }); - } - // future get blocks (unordered it) - stream::futures_unordered(blocks.into_iter().map(|(_, v)| v)).collect().map(move |blocks| { - let transactions_per_block: BTreeMap<_, _> = blocks.iter() - .map(|block| (block.hash(), block.transactions())).collect(); - for log in result.iter_mut() { - let log_index = log.transaction_index.expect("Previously initialized with value; qed"); - let block_hash = log.block_hash.expect("Previously initialized with value; qed"); - let tx_hash = transactions_per_block.get(&block_hash) - // transaction index is from an enumerate call in log common so not need to check value - .and_then(|txs| txs.get(log_index.as_usize())) - .map(types::transaction::UnverifiedTransaction::hash); - log.transaction_hash = tx_hash; - } - result - }) - }) - } - - // Get a transaction by hash. also returns the index in the block. - // Only returns transactions in the canonical chain. - pub fn transaction_by_hash(&self, tx_hash: H256) - -> impl Future, Error = Error> + Send - { - let params = (self.sync.clone(), self.on_demand.clone()); - let fetcher: Self = self.clone(); - - Box::new(future::loop_fn(params, move |(sync, on_demand)| { - let maybe_future = sync.with_context(|ctx| { - let req = request::TransactionIndex(tx_hash.into()); - on_demand.request(ctx, req) - }); - - let eventual_index = match maybe_future { - Some(e) => e.expect(NO_INVALID_BACK_REFS_PROOF).map_err(errors::on_demand_error), - None => return Either::A(future::err(errors::network_disabled())), - }; - - let fetcher = fetcher.clone(); - let extract_transaction = eventual_index.and_then(move |index| { - // check that the block is known by number. - // that ensures that it is within the chain that we are aware of. - fetcher.block(BlockId::Number(index.num)).then(move |blk| match blk { - Ok(blk) => { - // if the block is known by number, make sure the - // index from earlier isn't garbage. - - if blk.hash() != index.hash { - // index is on a different chain from us. - return Ok(future::Loop::Continue((sync, on_demand))) - } - - let index = index.index as usize; - let transaction = extract_transaction_at_index(blk, index); - - if transaction.as_ref().map_or(true, |tx| tx.hash != tx_hash) { - // index is actively wrong: indicated block has - // fewer transactions than necessary or the transaction - // at that index had a different hash. - // TODO: punish peer/move into OnDemand somehow? - Ok(future::Loop::Continue((sync, on_demand))) - } else { - let transaction = transaction.map(move |tx| (tx, index)); - Ok(future::Loop::Break(transaction)) - } - } - Err(ref e) if e == &errors::unknown_block() => { - // block by number not in the canonical chain. - Ok(future::Loop::Break(None)) - } - Err(e) => Err(e), - }) - }); - - Either::B(extract_transaction) - })) - } - - fn send_requests(&self, reqs: Vec, parse_response: F) -> impl Future + Send where - F: FnOnce(Vec) -> T + Send + 'static, - T: Send + 'static, - { - let maybe_future = self.sync.with_context(move |ctx| { - Box::new(self.on_demand.request_raw(ctx, reqs) - .expect(NO_INVALID_BACK_REFS_PROOF) - .map_err(errors::on_demand_cancel) - .and_then(|responses| { - match responses { - Ok(responses) => Ok(parse_response(responses)), - Err(e) => Err(errors::on_demand_error(e)), - } - }) - ) - }); - - match maybe_future { - Some(recv) => recv, - None => Box::new(future::err(errors::network_disabled())) as Box + Send> - } - } - - fn headers_range_by_block_id( - &self, - from_block: BlockId, - to_block: BlockId, - max: u64 - ) -> impl Future, Error = Error> { - let fetch_hashes = [from_block, to_block].iter() - .filter_map(|block_id| match block_id { - BlockId::Hash(hash) => Some(*hash), - _ => None, - }) - .collect::>(); - - let best_number = self.client.chain_info().best_block_number; - - let fetcher = self.clone(); - self.headers_by_hash(&fetch_hashes[..]).and_then(move |mut header_map| { - let (from_block_num, to_block_num) = { - let block_number = |id| match id { - BlockId::Earliest => 0, - BlockId::Latest => best_number, - BlockId::Hash(ref h) => - header_map.get(h).map(types::encoded::Header::number) - .expect("from_block and to_block headers are fetched by hash; this closure is only called on from_block and to_block; qed"), - BlockId::Number(x) => x, - }; - (block_number(from_block), block_number(to_block)) - }; - - if to_block_num < from_block_num { - // early exit for "to" block before "from" block. - return Either::A(future::err(errors::filter_block_not_found(to_block))); - } else if to_block_num - from_block_num >= max { - return Either::A(future::err(errors::request_rejected_param_limit(max, "blocks"))); - } - - let to_header_hint = match to_block { - BlockId::Hash(ref h) => header_map.remove(h), - _ => None, - }; - let headers_fut = fetcher.headers_range(from_block_num, to_block_num, to_header_hint); - Either::B(headers_fut.map(move |headers| { - // Validate from_block if it's a hash - let last_hash = headers.last().map(types::encoded::Header::hash); - match (last_hash, from_block) { - (Some(h1), BlockId::Hash(h2)) if h1 != h2 => Vec::new(), - _ => headers, - } - })) - }) - } - - fn headers_by_hash(&self, hashes: &[H256]) -> impl Future, Error = Error> { - let mut refs = H256FastMap::with_capacity_and_hasher(hashes.len(), Default::default()); - let mut reqs = Vec::with_capacity(hashes.len()); - - for hash in hashes { - refs.entry(*hash).or_insert_with(|| { - self.make_header_requests(BlockId::Hash(*hash), &mut reqs) - .expect("make_header_requests never fails for BlockId::Hash; qed") - }); - } - - self.send_requests(reqs, move |res| { - refs.into_iter().map(|(hash, header_ref)| { - let hdr = extract_header(&res, header_ref) - .expect("these responses correspond to requests that header_ref belongs to; \ - qed"); - (hash, hdr) - }) - .collect() - }) - } - - fn headers_range( - &self, - from_number: u64, - to_number: u64, - to_header_hint: Option - ) -> impl Future, Error = Error> { - let range_length = (to_number - from_number + 1) as usize; - let mut headers: Vec = Vec::with_capacity(range_length); - - let iter_start = match to_header_hint { - Some(hdr) => { - let block_id = BlockId::Hash(hdr.parent_hash()); - headers.push(hdr); - block_id - } - None => BlockId::Number(to_number), - }; - headers.extend(self.client.ancestry_iter(iter_start) - .take_while(|hdr| hdr.number() >= from_number)); - - let fetcher = self.clone(); - future::loop_fn(headers, move |mut headers| { - let remaining = range_length - headers.len(); - if remaining == 0 { - return Either::A(future::ok(future::Loop::Break(headers))); - } - - let mut reqs: Vec = Vec::with_capacity(2); - - let start_hash = if let Some(hdr) = headers.last() { - hdr.parent_hash().into() - } else { - let cht_root = cht::block_to_cht_number(to_number) - .and_then(|cht_num| fetcher.client.cht_root(cht_num as usize)); - - let cht_root = match cht_root { - Some(cht_root) => cht_root, - None => return Either::A(future::err(errors::unknown_block())), - }; - - let header_proof = request::HeaderProof::new(to_number, cht_root) - .expect("HeaderProof::new is Some(_) if cht::block_to_cht_number() is Some(_); \ - this would return above if block_to_cht_number returned None; qed"); - - let idx = reqs.len(); - let hash_ref = Field::back_ref(idx, 0); - reqs.push(header_proof.into()); - - hash_ref - }; - - let max = cmp::min(remaining as u64, MAX_HEADERS_PER_REQUEST); - reqs.push(request::HeaderWithAncestors { - block_hash: start_hash, - ancestor_count: max - 1, - }.into()); - - Either::B(fetcher.send_requests(reqs, |mut res| { - match res.last_mut() { - Some(&mut OnDemandResponse::HeaderWithAncestors(ref mut res_headers)) => - headers.extend(res_headers.drain(..)), - _ => panic!("reqs has at least one entry; each request maps to a response; qed"), - }; - future::Loop::Continue(headers) - })) - }) - } -} - -struct ExecuteParams -where - S: LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, - OD: OnDemandRequester + 'static -{ - from: Address, - tx: EthTransaction, - hdr: encoded::Header, - env_info: ::vm::EnvInfo, - engine: Arc<::ethcore::engines::EthEngine>, - on_demand: Arc, - sync: Arc, -} - -impl Clone for ExecuteParams -where - S: LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, - OD: OnDemandRequester + 'static -{ - fn clone(&self) -> Self { - Self { - from: self.from, - tx: self.tx.clone(), - hdr: self.hdr.clone(), - env_info: self.env_info.clone(), - engine: self.engine.clone(), - on_demand: self.on_demand.clone(), - sync: self.sync.clone() - } - } -} - -// Has a peer execute the transaction with given params. If `gas_known` is false, this will set the `gas value` to the -// `required gas value` unless it exceeds the block gas limit -fn execute_read_only_tx(gas_known: bool, params: ExecuteParams) -> impl Future + Send -where - S: LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, - OD: OnDemandRequester + 'static -{ - if !gas_known { - Box::new(future::loop_fn(params, |mut params| { - execute_read_only_tx(true, params.clone()).and_then(move |res| { - match res { - Ok(executed) => { - // `OutOfGas` exception, try double the gas - if let Some(::vm::Error::OutOfGas) = executed.exception { - // block gas limit already tried, regard as an error and don't retry - if params.tx.gas >= params.hdr.gas_limit() { - trace!(target: "light_fetch", "OutOutGas exception received, gas increase: failed"); - } else { - params.tx.gas = cmp::min(params.tx.gas * 2_u32, params.hdr.gas_limit()); - trace!(target: "light_fetch", "OutOutGas exception received, gas increased to {}", - params.tx.gas); - return Ok(future::Loop::Continue(params)) - } - } - Ok(future::Loop::Break(Ok(executed))) - } - Err(ExecutionError::NotEnoughBaseGas { required, got }) => { - trace!(target: "light_fetch", "Not enough start gas provided required: {}, got: {}", - required, got); - if required <= params.hdr.gas_limit() { - params.tx.gas = required; - Ok(future::Loop::Continue(params)) - } else { - warn!(target: "light_fetch", - "Required gas is bigger than block header's gas dropping the request"); - Ok(future::Loop::Break(Err(ExecutionError::NotEnoughBaseGas { required, got }))) - } - } - // Non-recoverable execution error - failed => Ok(future::Loop::Break(failed)), - } - }) - })) as Box + Send> - } else { - trace!(target: "light_fetch", "Placing execution request for {} gas in on_demand", - params.tx.gas); - - let request = request::TransactionProof { - tx: params.tx.fake_sign(params.from), - header: params.hdr.into(), - env_info: params.env_info, - engine: params.engine, - }; - - let on_demand = params.on_demand; - let proved_future = params.sync.with_context(move |ctx| { - on_demand - .request(ctx, request) - .expect("no back-references; therefore all back-refs valid; qed") - .map_err(errors::on_demand_error) - }); - - match proved_future { - Some(fut) => Box::new(fut) as Box + Send>, - None => Box::new(future::err(errors::network_disabled())) as Box + Send>, - } - } -} diff --git a/rpc/src/v1/helpers/mod.rs b/rpc/src/v1/helpers/mod.rs index 8a25f93056a..686ec773871 100644 --- a/rpc/src/v1/helpers/mod.rs +++ b/rpc/src/v1/helpers/mod.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . #[macro_use] pub mod errors; @@ -26,8 +26,6 @@ pub mod eip191; pub mod engine_signer; pub mod external_signer; pub mod fake_sign; -pub mod ipfs; -pub mod light_fetch; pub mod nonce; #[cfg(any(test, feature = "accounts"))] pub mod secretstore; @@ -36,23 +34,26 @@ mod network_settings; mod poll_filter; mod poll_manager; mod requests; +mod signature; mod subscribers; mod subscription_manager; mod work; -mod signature; -pub use self::dispatch::{Dispatcher, FullDispatcher, LightDispatcher}; -pub use self::signature::verify_signature; -pub use self::network_settings::NetworkSettings; -pub use self::poll_manager::PollManager; -pub use self::poll_filter::{PollFilter, SyncPollFilter, limit_logs}; -pub use self::requests::{ - TransactionRequest, FilledTransactionRequest, ConfirmationRequest, ConfirmationPayload, CallRequest, +pub use self::{ + dispatch::{Dispatcher, FullDispatcher}, + network_settings::NetworkSettings, + poll_filter::{limit_logs, PollFilter, SyncPollFilter}, + poll_manager::PollManager, + requests::{ + CallRequest, ConfirmationPayload, ConfirmationRequest, FilledTransactionRequest, + TransactionRequest, + }, + signature::verify_signature, + subscribers::Subscribers, + subscription_manager::GenericPollManager, + work::submit_work_detail, }; -pub use self::subscribers::Subscribers; -pub use self::subscription_manager::GenericPollManager; -pub use self::work::submit_work_detail; pub fn to_url(address: &Option<::Host>) -> Option { - address.as_ref().map(|host| (**host).to_owned()) + address.as_ref().map(|host| (**host).to_owned()) } diff --git a/rpc/src/v1/helpers/network_settings.rs b/rpc/src/v1/helpers/network_settings.rs index ed515e471a9..c9aab20298b 100644 --- a/rpc/src/v1/helpers/network_settings.rs +++ b/rpc/src/v1/helpers/network_settings.rs @@ -1,50 +1,50 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Structure to hold network settings configured from CLI /// Networking & RPC settings #[derive(Debug, PartialEq, Clone)] pub struct NetworkSettings { - /// Node name - pub name: String, - /// Name of the chain we are connected to - pub chain: String, - /// Is development chain - pub is_dev_chain: bool, - /// Networking port - pub network_port: u16, - /// Is JSON-RPC server enabled? - pub rpc_enabled: bool, - /// Interface that JSON-RPC listens on - pub rpc_interface: String, - /// Port for JSON-RPC server - pub rpc_port: u16, + /// Node name + pub name: String, + /// Name of the chain we are connected to + pub chain: String, + /// Is development chain + pub is_dev_chain: bool, + /// Networking port + pub network_port: u16, + /// Is JSON-RPC server enabled? + pub rpc_enabled: bool, + /// Interface that JSON-RPC listens on + pub rpc_interface: String, + /// Port for JSON-RPC server + pub rpc_port: u16, } impl Default for NetworkSettings { - fn default() -> Self { - NetworkSettings { - name: "".into(), - chain: "foundation".into(), - is_dev_chain: false, - network_port: 30303, - rpc_enabled: true, - rpc_interface: "127.0.0.1".into(), - rpc_port: 8545 - } - } + fn default() -> Self { + NetworkSettings { + name: "".into(), + chain: "foundation".into(), + is_dev_chain: false, + network_port: 30303, + rpc_enabled: true, + rpc_interface: "127.0.0.1".into(), + rpc_port: 8545, + } + } } diff --git a/rpc/src/v1/helpers/nonce.rs b/rpc/src/v1/helpers/nonce.rs index 25ec89f01b0..47d93702946 100644 --- a/rpc/src/v1/helpers/nonce.rs +++ b/rpc/src/v1/helpers/nonce.rs @@ -1,155 +1,157 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::{cmp, mem}; -use std::collections::HashMap; -use std::sync::{atomic, Arc}; -use std::sync::atomic::{AtomicBool, AtomicUsize}; - -use ethereum_types::{U256, Address}; -use futures::{Future, future, Poll, Async}; -use futures::future::Either; -use futures::sync::oneshot; +// along with OpenEthereum. If not, see . + +use std::{ + cmp, + collections::HashMap, + mem, + sync::{ + atomic, + atomic::{AtomicBool, AtomicUsize}, + Arc, + }, +}; + +use ethereum_types::{Address, U256}; +use futures::{future, future::Either, sync::oneshot, Async, Future, Poll}; use parity_runtime::Executor; /// Manages currently reserved and prospective nonces /// for multiple senders. #[derive(Debug)] pub struct Reservations { - nonces: HashMap, - executor: Executor, + nonces: HashMap, + executor: Executor, } impl Reservations { - /// A maximal number of reserved nonces in the hashmap - /// before we start clearing the unused ones. - const CLEAN_AT: usize = 512; - - /// Create new nonces manager with given executor. - pub fn new(executor: Executor) -> Self { - Reservations { - nonces: Default::default(), - executor, - } - } - - /// Reserve a nonce for particular address. - /// - /// The reserved nonce cannot be smaller than the minimal nonce. - pub fn reserve(&mut self, sender: Address, minimal: U256) -> Reserved { - if self.nonces.len() + 1 > Self::CLEAN_AT { - self.nonces.retain(|_, v| !v.is_empty()); - } - - let executor = &self.executor; - self.nonces.entry(sender) - .or_insert_with(move || SenderReservations::new(executor.clone())) - .reserve_nonce(minimal) - } + /// A maximal number of reserved nonces in the hashmap + /// before we start clearing the unused ones. + const CLEAN_AT: usize = 512; + + /// Create new nonces manager with given executor. + pub fn new(executor: Executor) -> Self { + Reservations { + nonces: Default::default(), + executor, + } + } + + /// Reserve a nonce for particular address. + /// + /// The reserved nonce cannot be smaller than the minimal nonce. + pub fn reserve(&mut self, sender: Address, minimal: U256) -> Reserved { + if self.nonces.len() + 1 > Self::CLEAN_AT { + self.nonces.retain(|_, v| !v.is_empty()); + } + + let executor = &self.executor; + self.nonces + .entry(sender) + .or_insert_with(move || SenderReservations::new(executor.clone())) + .reserve_nonce(minimal) + } } /// Manages currently reserved and prospective nonces. #[derive(Debug)] pub struct SenderReservations { - previous: Option>, - previous_ready: Arc, - executor: Executor, - prospective_value: U256, - dropped: Arc, + previous: Option>, + previous_ready: Arc, + executor: Executor, + prospective_value: U256, + dropped: Arc, } impl SenderReservations { - /// Create new nonces manager with given executor. - pub fn new(executor: Executor) -> Self { - SenderReservations { - previous: None, - previous_ready: Arc::new(AtomicBool::new(true)), - executor, - prospective_value: Default::default(), - dropped: Default::default(), - } - } - - /// Reserves a prospective nonce. - /// The caller should provide a minimal nonce that needs to be reserved (taken from state/txqueue). - /// If there were any previous reserved nonces the returned future will be resolved when those are finished - /// (confirmed that the nonce were indeed used). - /// The caller can use `prospective_nonce` and perform some heavy computation anticipating - /// that the `prospective_nonce` will be equal to the one he will get. - pub fn reserve_nonce(&mut self, minimal: U256) -> Reserved { - // Update prospective value - let dropped = self.dropped.swap(0, atomic::Ordering::SeqCst); - let prospective_value = cmp::max(minimal, self.prospective_value - dropped); - self.prospective_value = prospective_value + 1; - - let (next, rx) = oneshot::channel(); - let next = Some(next); - let next_sent = Arc::new(AtomicBool::default()); - let executor = self.executor.clone(); - let dropped = self.dropped.clone(); - self.previous_ready = next_sent.clone(); - match mem::replace(&mut self.previous, Some(rx)) { - Some(previous) => Reserved { - previous: Either::A(previous), - next, - next_sent, - minimal, - prospective_value, - executor, - dropped, - }, - None => Reserved { - previous: Either::B(future::ok(minimal)), - next, - next_sent, - minimal, - prospective_value, - executor, - dropped, - }, - } - } - - /// Returns true if there are no reserved nonces. - pub fn is_empty(&self) -> bool { - self.previous_ready.load(atomic::Ordering::SeqCst) - } + /// Create new nonces manager with given executor. + pub fn new(executor: Executor) -> Self { + SenderReservations { + previous: None, + previous_ready: Arc::new(AtomicBool::new(true)), + executor, + prospective_value: Default::default(), + dropped: Default::default(), + } + } + + /// Reserves a prospective nonce. + /// The caller should provide a minimal nonce that needs to be reserved (taken from state/txqueue). + /// If there were any previous reserved nonces the returned future will be resolved when those are finished + /// (confirmed that the nonce were indeed used). + /// The caller can use `prospective_nonce` and perform some heavy computation anticipating + /// that the `prospective_nonce` will be equal to the one he will get. + pub fn reserve_nonce(&mut self, minimal: U256) -> Reserved { + // Update prospective value + let dropped = self.dropped.swap(0, atomic::Ordering::SeqCst); + let prospective_value = cmp::max(minimal, self.prospective_value - dropped); + self.prospective_value = prospective_value + 1; + + let (next, rx) = oneshot::channel(); + let next = Some(next); + let next_sent = Arc::new(AtomicBool::default()); + let executor = self.executor.clone(); + let dropped = self.dropped.clone(); + self.previous_ready = next_sent.clone(); + match mem::replace(&mut self.previous, Some(rx)) { + Some(previous) => Reserved { + previous: Either::A(previous), + next, + next_sent, + minimal, + prospective_value, + executor, + dropped, + }, + None => Reserved { + previous: Either::B(future::ok(minimal)), + next, + next_sent, + minimal, + prospective_value, + executor, + dropped, + }, + } + } + + /// Returns true if there are no reserved nonces. + pub fn is_empty(&self) -> bool { + self.previous_ready.load(atomic::Ordering::SeqCst) + } } /// Represents a future nonce. #[derive(Debug)] pub struct Reserved { - previous: Either< - oneshot::Receiver, - future::FutureResult, - >, - next: Option>, - next_sent: Arc, - minimal: U256, - prospective_value: U256, - executor: Executor, - dropped: Arc, + previous: Either, future::FutureResult>, + next: Option>, + next_sent: Arc, + minimal: U256, + prospective_value: U256, + executor: Executor, + dropped: Arc, } impl Reserved { - /// Returns a prospective value of the nonce. - /// NOTE: This might be different than the one we resolve to. - /// Make sure to check if both nonces match or use the latter one. - pub fn prospective_value(&self) -> &U256 { - &self.prospective_value - } + /// Returns a prospective value of the nonce. + /// NOTE: This might be different than the one we resolve to. + /// Make sure to check if both nonces match or use the latter one. + pub fn prospective_value(&self) -> &U256 { + &self.prospective_value + } } impl Future for Reserved { @@ -157,42 +159,42 @@ impl Future for Reserved { type Error = (); fn poll(&mut self) -> Poll { - let mut value = try_ready!(self.previous.poll().map_err(|e| { - warn!("Unexpected nonce cancellation: {}", e); - })); - - if value < self.minimal { - value = self.minimal - } - let matches_prospective = value == self.prospective_value; - - Ok(Async::Ready(Ready { - value, - matches_prospective, - next: self.next.take(), - next_sent: self.next_sent.clone(), - dropped: self.dropped.clone(), - })) - } + let mut value = try_ready!(self.previous.poll().map_err(|e| { + warn!("Unexpected nonce cancellation: {}", e); + })); + + if value < self.minimal { + value = self.minimal + } + let matches_prospective = value == self.prospective_value; + + Ok(Async::Ready(Ready { + value, + matches_prospective, + next: self.next.take(), + next_sent: self.next_sent.clone(), + dropped: self.dropped.clone(), + })) + } } impl Drop for Reserved { - fn drop(&mut self) { - if let Some(next) = self.next.take() { - let next_sent = self.next_sent.clone(); - self.dropped.fetch_add(1, atomic::Ordering::SeqCst); - // If Reserved is dropped just pipe previous and next together. - let previous = mem::replace(&mut self.previous, Either::B(future::ok(U256::default()))); - self.executor.spawn( - previous - .map(move |nonce| { - next_sent.store(true, atomic::Ordering::SeqCst); - next.send(nonce).expect(Ready::RECV_PROOF) - }) - .map_err(|err| error!("Error dropping `Reserved`: {:?}", err)) - ); - } - } + fn drop(&mut self) { + if let Some(next) = self.next.take() { + let next_sent = self.next_sent.clone(); + self.dropped.fetch_add(1, atomic::Ordering::SeqCst); + // If Reserved is dropped just pipe previous and next together. + let previous = mem::replace(&mut self.previous, Either::B(future::ok(U256::default()))); + self.executor.spawn( + previous + .map(move |nonce| { + next_sent.store(true, atomic::Ordering::SeqCst); + next.send(nonce).expect(Ready::RECV_PROOF) + }) + .map_err(|err| error!("Error dropping `Reserved`: {:?}", err)), + ); + } + } } /// Represents a valid reserved nonce. @@ -202,43 +204,46 @@ impl Drop for Reserved { /// using `mark_used` method. #[derive(Debug)] pub struct Ready { - value: U256, - matches_prospective: bool, - next: Option>, - next_sent: Arc, - dropped: Arc, + value: U256, + matches_prospective: bool, + next: Option>, + next_sent: Arc, + dropped: Arc, } impl Ready { - const RECV_PROOF: &'static str = "Receiver never dropped."; - - /// Returns a value of the nonce. - pub fn value(&self) -> &U256 { - &self.value - } - - /// Returns true if current value matches the prospective nonce. - pub fn matches_prospective(&self) -> bool { - self.matches_prospective - } - - /// Marks this nonce as used. - /// Make sure to call that method after this nonce has been consumed. - pub fn mark_used(mut self) { - let next = self.next.take().expect("Nonce can be marked as used only once; qed"); - self.next_sent.store(true, atomic::Ordering::SeqCst); - next.send(self.value + 1).expect(Self::RECV_PROOF); - } + const RECV_PROOF: &'static str = "Receiver never dropped."; + + /// Returns a value of the nonce. + pub fn value(&self) -> &U256 { + &self.value + } + + /// Returns true if current value matches the prospective nonce. + pub fn matches_prospective(&self) -> bool { + self.matches_prospective + } + + /// Marks this nonce as used. + /// Make sure to call that method after this nonce has been consumed. + pub fn mark_used(mut self) { + let next = self + .next + .take() + .expect("Nonce can be marked as used only once; qed"); + self.next_sent.store(true, atomic::Ordering::SeqCst); + next.send(self.value + 1).expect(Self::RECV_PROOF); + } } impl Drop for Ready { - fn drop(&mut self) { - if let Some(next) = self.next.take() { - self.dropped.fetch_add(1, atomic::Ordering::SeqCst); - self.next_sent.store(true, atomic::Ordering::SeqCst); - next.send(self.value).expect(Self::RECV_PROOF); - } - } + fn drop(&mut self) { + if let Some(next) = self.next.take() { + self.dropped.fetch_add(1, atomic::Ordering::SeqCst); + self.next_sent.store(true, atomic::Ordering::SeqCst); + next.send(self.value).expect(Self::RECV_PROOF); + } + } } #[cfg(test)] @@ -246,63 +251,63 @@ mod tests { use super::*; use parity_runtime::Runtime; - #[test] - fn should_reserve_a_set_of_nonces_and_resolve_them() { - let runtime = Runtime::with_thread_count(1); - let mut nonces = SenderReservations::new(runtime.executor()); - - assert!(nonces.is_empty()); - let n1 = nonces.reserve_nonce(5.into()); - let n2 = nonces.reserve_nonce(5.into()); - let n3 = nonces.reserve_nonce(5.into()); - let n4 = nonces.reserve_nonce(5.into()); - assert!(!nonces.is_empty()); - - // Check first nonce - let r = n1.wait().unwrap(); - assert_eq!(r.value(), &U256::from(5)); - assert!(r.matches_prospective()); - r.mark_used(); - - // Drop second nonce - drop(n2); - - // Drop third without marking as used - let r = n3.wait().unwrap(); - drop(r); - - // Last nonce should be resolved to 6 - let r = n4.wait().unwrap(); - assert_eq!(r.value(), &U256::from(6)); - assert!(!r.matches_prospective()); - r.mark_used(); - - // Next nonce should be immediately available. - let n5 = nonces.reserve_nonce(5.into()); - let r = n5.wait().unwrap(); - assert_eq!(r.value(), &U256::from(7)); - assert!(r.matches_prospective()); - r.mark_used(); - - // Should use start number if it's greater - let n6 = nonces.reserve_nonce(10.into()); - let r = n6.wait().unwrap(); - assert_eq!(r.value(), &U256::from(10)); - assert!(r.matches_prospective()); - r.mark_used(); - - assert!(nonces.is_empty()); - } - - #[test] - fn should_return_prospective_nonce() { - let runtime = Runtime::with_thread_count(1); - let mut nonces = SenderReservations::new(runtime.executor()); - - let n1 = nonces.reserve_nonce(5.into()); - let n2 = nonces.reserve_nonce(5.into()); - - assert_eq!(n1.prospective_value(), &U256::from(5)); - assert_eq!(n2.prospective_value(), &U256::from(6)); - } + #[test] + fn should_reserve_a_set_of_nonces_and_resolve_them() { + let runtime = Runtime::with_thread_count(1); + let mut nonces = SenderReservations::new(runtime.executor()); + + assert!(nonces.is_empty()); + let n1 = nonces.reserve_nonce(5.into()); + let n2 = nonces.reserve_nonce(5.into()); + let n3 = nonces.reserve_nonce(5.into()); + let n4 = nonces.reserve_nonce(5.into()); + assert!(!nonces.is_empty()); + + // Check first nonce + let r = n1.wait().unwrap(); + assert_eq!(r.value(), &U256::from(5)); + assert!(r.matches_prospective()); + r.mark_used(); + + // Drop second nonce + drop(n2); + + // Drop third without marking as used + let r = n3.wait().unwrap(); + drop(r); + + // Last nonce should be resolved to 6 + let r = n4.wait().unwrap(); + assert_eq!(r.value(), &U256::from(6)); + assert!(!r.matches_prospective()); + r.mark_used(); + + // Next nonce should be immediately available. + let n5 = nonces.reserve_nonce(5.into()); + let r = n5.wait().unwrap(); + assert_eq!(r.value(), &U256::from(7)); + assert!(r.matches_prospective()); + r.mark_used(); + + // Should use start number if it's greater + let n6 = nonces.reserve_nonce(10.into()); + let r = n6.wait().unwrap(); + assert_eq!(r.value(), &U256::from(10)); + assert!(r.matches_prospective()); + r.mark_used(); + + assert!(nonces.is_empty()); + } + + #[test] + fn should_return_prospective_nonce() { + let runtime = Runtime::with_thread_count(1); + let mut nonces = SenderReservations::new(runtime.executor()); + + let n1 = nonces.reserve_nonce(5.into()); + let n2 = nonces.reserve_nonce(5.into()); + + assert_eq!(n1.prospective_value(), &U256::from(5)); + assert_eq!(n2.prospective_value(), &U256::from(6)); + } } diff --git a/rpc/src/v1/helpers/poll_filter.rs b/rpc/src/v1/helpers/poll_filter.rs index 2d7eb956683..2c373399ebd 100644 --- a/rpc/src/v1/helpers/poll_filter.rs +++ b/rpc/src/v1/helpers/poll_filter.rs @@ -1,29 +1,29 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Helper type with all filter state data. -use std::{ - collections::{BTreeSet, HashSet, VecDeque}, - sync::Arc, -}; use ethereum_types::H256; use parking_lot::Mutex; -use v1::types::Log; +use std::{ + collections::{BTreeSet, HashSet, VecDeque}, + sync::Arc, +}; use types::filter::Filter; +use v1::types::Log; pub type BlockNumber = u64; @@ -32,49 +32,50 @@ pub type BlockNumber = u64; pub struct SyncPollFilter(Arc>); impl SyncPollFilter { - /// New `SyncPollFilter` - pub fn new(f: PollFilter) -> Self { - SyncPollFilter(Arc::new(Mutex::new(f))) - } + /// New `SyncPollFilter` + pub fn new(f: PollFilter) -> Self { + SyncPollFilter(Arc::new(Mutex::new(f))) + } - /// Modify underlying filter - pub fn modify(&self, f: F) -> R where - F: FnOnce(&mut PollFilter) -> R, - { - f(&mut self.0.lock()) - } + /// Modify underlying filter + pub fn modify(&self, f: F) -> R + where + F: FnOnce(&mut PollFilter) -> R, + { + f(&mut self.0.lock()) + } } /// Filter state. #[derive(Clone)] pub enum PollFilter { - /// Number of last block which client was notified about. - Block { - last_block_number: BlockNumber, - #[doc(hidden)] - recent_reported_hashes: VecDeque<(BlockNumber, H256)>, - }, - /// Hashes of all pending transactions the client knows about. - PendingTransaction(BTreeSet), - /// Number of From block number, last seen block hash, pending logs and log filter itself. - Logs { - block_number: BlockNumber, - last_block_hash: Option, - previous_logs: HashSet, - filter: Filter, - include_pending: bool, - } + /// Number of last block which client was notified about. + Block { + last_block_number: BlockNumber, + #[doc(hidden)] + recent_reported_hashes: VecDeque<(BlockNumber, H256)>, + }, + /// Hashes of all pending transactions the client knows about. + PendingTransaction(BTreeSet), + /// Number of From block number, last seen block hash, pending logs and log filter itself. + Logs { + block_number: BlockNumber, + last_block_hash: Option, + previous_logs: HashSet, + filter: Filter, + include_pending: bool, + }, } impl PollFilter { - pub (in v1) const MAX_BLOCK_HISTORY_SIZE: usize = 32; + pub(in v1) const MAX_BLOCK_HISTORY_SIZE: usize = 32; } /// Returns only last `n` logs pub fn limit_logs(mut logs: Vec, limit: Option) -> Vec { - let len = logs.len(); - match limit { - Some(limit) if len >= limit => logs.split_off(len - limit), - _ => logs, - } + let len = logs.len(); + match limit { + Some(limit) if len >= limit => logs.split_off(len - limit), + _ => logs, + } } diff --git a/rpc/src/v1/helpers/poll_manager.rs b/rpc/src/v1/helpers/poll_manager.rs index a0f1684395d..04484403536 100644 --- a/rpc/src/v1/helpers/poll_manager.rs +++ b/rpc/src/v1/helpers/poll_manager.rs @@ -1,123 +1,125 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Indexes all rpc poll requests. -use transient_hashmap::{TransientHashMap, Timer, StandardTimer}; +use transient_hashmap::{StandardTimer, Timer, TransientHashMap}; pub type PollId = usize; /// Indexes all poll requests. /// /// Lazily garbage collects unused polls info. -pub struct PollManager where T: Timer { - polls: TransientHashMap, - next_available_id: PollId, +pub struct PollManager +where + T: Timer, +{ + polls: TransientHashMap, + next_available_id: PollId, } impl PollManager { - /// Creates new instance of indexer - pub fn new(lifetime: u32) -> Self { - PollManager::new_with_timer(Default::default(), lifetime) - } + /// Creates new instance of indexer + pub fn new(lifetime: u32) -> Self { + PollManager::new_with_timer(Default::default(), lifetime) + } } -impl PollManager where T: Timer { - - pub fn new_with_timer(timer: T, lifetime: u32) -> Self { - PollManager { - polls: TransientHashMap::new_with_timer(lifetime, timer), - next_available_id: 0, - } - } - - /// Returns id which can be used for new poll. - /// - /// Stores information when last poll happend. - pub fn create_poll(&mut self, filter: F) -> PollId { - self.polls.prune(); - - let id = self.next_available_id; - self.polls.insert(id, filter); - - self.next_available_id += 1; - id - } - - // Implementation is always using `poll_mut` - /// Get a reference to stored poll filter - pub fn poll(&mut self, id: &PollId) -> Option<&F> { - self.polls.prune(); - self.polls.get(id) - } - - /// Get a mutable reference to stored poll filter - pub fn poll_mut(&mut self, id: &PollId) -> Option<&mut F> { - self.polls.prune(); - self.polls.get_mut(id) - } - - /// Removes poll info. - pub fn remove_poll(&mut self, id: &PollId) -> bool { - self.polls.remove(id).is_some() - } +impl PollManager +where + T: Timer, +{ + pub fn new_with_timer(timer: T, lifetime: u32) -> Self { + PollManager { + polls: TransientHashMap::new_with_timer(lifetime, timer), + next_available_id: 0, + } + } + + /// Returns id which can be used for new poll. + /// + /// Stores information when last poll happend. + pub fn create_poll(&mut self, filter: F) -> PollId { + self.polls.prune(); + + let id = self.next_available_id; + self.polls.insert(id, filter); + + self.next_available_id += 1; + id + } + + // Implementation is always using `poll_mut` + /// Get a reference to stored poll filter + pub fn poll(&mut self, id: &PollId) -> Option<&F> { + self.polls.prune(); + self.polls.get(id) + } + + /// Get a mutable reference to stored poll filter + pub fn poll_mut(&mut self, id: &PollId) -> Option<&mut F> { + self.polls.prune(); + self.polls.get_mut(id) + } + + /// Removes poll info. + pub fn remove_poll(&mut self, id: &PollId) -> bool { + self.polls.remove(id).is_some() + } } #[cfg(test)] mod tests { - use std::cell::Cell; - use transient_hashmap::Timer; - use v1::helpers::PollManager; - - struct TestTimer<'a> { - time: &'a Cell, - } - - impl<'a> Timer for TestTimer<'a> { - fn get_time(&self) -> i64 { - self.time.get() - } - } - - #[test] - fn test_poll_indexer() { - let time = Cell::new(0); - let timer = TestTimer { - time: &time, - }; - - let mut indexer = PollManager::new_with_timer(timer,60); - assert_eq!(indexer.create_poll(20), 0); - assert_eq!(indexer.create_poll(20), 1); - - time.set(10); - *indexer.poll_mut(&0).unwrap() = 21; - assert_eq!(*indexer.poll(&0).unwrap(), 21); - assert_eq!(*indexer.poll(&1).unwrap(), 20); - - time.set(30); - *indexer.poll_mut(&1).unwrap() = 23; - assert_eq!(*indexer.poll(&1).unwrap(), 23); - - time.set(75); - assert!(indexer.poll(&0).is_none()); - assert_eq!(*indexer.poll(&1).unwrap(), 23); - - indexer.remove_poll(&1); - assert!(indexer.poll(&1).is_none()); - } - + use std::cell::Cell; + use transient_hashmap::Timer; + use v1::helpers::PollManager; + + struct TestTimer<'a> { + time: &'a Cell, + } + + impl<'a> Timer for TestTimer<'a> { + fn get_time(&self) -> i64 { + self.time.get() + } + } + + #[test] + fn test_poll_indexer() { + let time = Cell::new(0); + let timer = TestTimer { time: &time }; + + let mut indexer = PollManager::new_with_timer(timer, 60); + assert_eq!(indexer.create_poll(20), 0); + assert_eq!(indexer.create_poll(20), 1); + + time.set(10); + *indexer.poll_mut(&0).unwrap() = 21; + assert_eq!(*indexer.poll(&0).unwrap(), 21); + assert_eq!(*indexer.poll(&1).unwrap(), 20); + + time.set(30); + *indexer.poll_mut(&1).unwrap() = 23; + assert_eq!(*indexer.poll(&1).unwrap(), 23); + + time.set(75); + assert!(indexer.poll(&0).is_none()); + assert_eq!(*indexer.poll(&1).unwrap(), 23); + + indexer.remove_poll(&1); + assert!(indexer.poll(&1).is_none()); + } } diff --git a/rpc/src/v1/helpers/requests.rs b/rpc/src/v1/helpers/requests.rs index e71d104449c..eeb1654d848 100644 --- a/rpc/src/v1/helpers/requests.rs +++ b/rpc/src/v1/helpers/requests.rs @@ -1,136 +1,136 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use ethereum_types::{U256, H256, Address}; use bytes::Bytes; +use ethereum_types::{Address, H256, U256}; use v1::types::{Origin, TransactionCondition}; /// Transaction request coming from RPC #[derive(Debug, Clone, Default, Eq, PartialEq, Hash)] pub struct TransactionRequest { - /// Sender - pub from: Option

, - /// Recipient - pub to: Option
, - /// Gas Price - pub gas_price: Option, - /// Gas - pub gas: Option, - /// Value of transaction in wei - pub value: Option, - /// Additional data sent with transaction - pub data: Option, - /// Transaction's nonce - pub nonce: Option, - /// Delay until this condition is met. - pub condition: Option, + /// Sender + pub from: Option
, + /// Recipient + pub to: Option
, + /// Gas Price + pub gas_price: Option, + /// Gas + pub gas: Option, + /// Value of transaction in wei + pub value: Option, + /// Additional data sent with transaction + pub data: Option, + /// Transaction's nonce + pub nonce: Option, + /// Delay until this condition is met. + pub condition: Option, } /// Transaction request coming from RPC with default values filled in. #[derive(Debug, Clone, Default, Eq, PartialEq, Hash)] pub struct FilledTransactionRequest { - /// Sender - pub from: Address, - /// Indicates if the sender was filled by default value. - pub used_default_from: bool, - /// Recipient - pub to: Option
, - /// Gas Price - pub gas_price: U256, - /// Gas - pub gas: U256, - /// Value of transaction in wei - pub value: U256, - /// Additional data sent with transaction - pub data: Bytes, - /// Transaction's nonce - pub nonce: Option, - /// Delay until this condition is met. - pub condition: Option, + /// Sender + pub from: Address, + /// Indicates if the sender was filled by default value. + pub used_default_from: bool, + /// Recipient + pub to: Option
, + /// Gas Price + pub gas_price: U256, + /// Gas + pub gas: U256, + /// Value of transaction in wei + pub value: U256, + /// Additional data sent with transaction + pub data: Bytes, + /// Transaction's nonce + pub nonce: Option, + /// Delay until this condition is met. + pub condition: Option, } impl From for TransactionRequest { - fn from(r: FilledTransactionRequest) -> Self { - TransactionRequest { - from: Some(r.from), - to: r.to, - gas_price: Some(r.gas_price), - gas: Some(r.gas), - value: Some(r.value), - data: Some(r.data), - nonce: r.nonce, - condition: r.condition, - } - } + fn from(r: FilledTransactionRequest) -> Self { + TransactionRequest { + from: Some(r.from), + to: r.to, + gas_price: Some(r.gas_price), + gas: Some(r.gas), + value: Some(r.value), + data: Some(r.data), + nonce: r.nonce, + condition: r.condition, + } + } } /// Call request #[derive(Debug, Default, PartialEq)] pub struct CallRequest { - /// From - pub from: Option
, - /// To - pub to: Option
, - /// Gas Price - pub gas_price: Option, - /// Gas - pub gas: Option, - /// Value - pub value: Option, - /// Data - pub data: Option>, - /// Nonce - pub nonce: Option, + /// From + pub from: Option
, + /// To + pub to: Option
, + /// Gas Price + pub gas_price: Option, + /// Gas + pub gas: Option, + /// Value + pub value: Option, + /// Data + pub data: Option>, + /// Nonce + pub nonce: Option, } /// Confirmation object #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct ConfirmationRequest { - /// Id of this confirmation - pub id: U256, - /// Payload to confirm - pub payload: ConfirmationPayload, - /// Request origin - pub origin: Origin, + /// Id of this confirmation + pub id: U256, + /// Payload to confirm + pub payload: ConfirmationPayload, + /// Request origin + pub origin: Origin, } /// Payload to confirm in Trusted Signer #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub enum ConfirmationPayload { - /// Transaction - SendTransaction(FilledTransactionRequest), - /// Sign Transaction - SignTransaction(FilledTransactionRequest), - /// Sign a message with an Ethereum specific security prefix. - EthSignMessage(Address, Bytes), - /// Sign a message - SignMessage(Address, H256), - /// Decrypt request - Decrypt(Address, Bytes), + /// Transaction + SendTransaction(FilledTransactionRequest), + /// Sign Transaction + SignTransaction(FilledTransactionRequest), + /// Sign a message with an Ethereum specific security prefix. + EthSignMessage(Address, Bytes), + /// Sign a message + SignMessage(Address, H256), + /// Decrypt request + Decrypt(Address, Bytes), } impl ConfirmationPayload { - pub fn sender(&self) -> Address { - match *self { - ConfirmationPayload::SendTransaction(ref request) => request.from, - ConfirmationPayload::SignTransaction(ref request) => request.from, - ConfirmationPayload::EthSignMessage(ref address, _) => *address, - ConfirmationPayload::SignMessage(ref address, _) => *address, - ConfirmationPayload::Decrypt(ref address, _) => *address, - } - } + pub fn sender(&self) -> Address { + match *self { + ConfirmationPayload::SendTransaction(ref request) => request.from, + ConfirmationPayload::SignTransaction(ref request) => request.from, + ConfirmationPayload::EthSignMessage(ref address, _) => *address, + ConfirmationPayload::SignMessage(ref address, _) => *address, + ConfirmationPayload::Decrypt(ref address, _) => *address, + } + } } diff --git a/rpc/src/v1/helpers/secretstore.rs b/rpc/src/v1/helpers/secretstore.rs index 6e1cbca45de..ed24d83cadc 100644 --- a/rpc/src/v1/helpers/secretstore.rs +++ b/rpc/src/v1/helpers/secretstore.rs @@ -1,187 +1,217 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::collections::BTreeSet; -use rand::{Rng, OsRng}; -use ethereum_types::{H256, H512}; -use ethkey::{self, Public, Secret, Random, Generator, math}; -use crypto; use bytes::Bytes; +use crypto; +use ethereum_types::{H256, H512}; +use ethkey::{self, math, Generator, Public, Random, Secret}; use jsonrpc_core::Error; -use v1::helpers::errors; -use v1::types::EncryptedDocumentKey; +use rand::{OsRng, Rng}; +use std::collections::BTreeSet; use tiny_keccak::Keccak; +use v1::{helpers::errors, types::EncryptedDocumentKey}; /// Initialization vector length. const INIT_VEC_LEN: usize = 16; /// Generate document key to store in secret store. -pub fn generate_document_key(account_public: Public, server_key_public: Public) -> Result { - // generate random plain document key - let document_key = Random.generate().map_err(errors::encryption)?; - - // encrypt document key using server key - let (common_point, encrypted_point) = encrypt_secret(document_key.public(), &server_key_public)?; - - // ..and now encrypt document key with account public - let encrypted_key = ethkey::crypto::ecies::encrypt(&account_public, &crypto::DEFAULT_MAC, document_key.public()) - .map_err(errors::encryption)?; - - Ok(EncryptedDocumentKey { - common_point: common_point.into(), - encrypted_point: encrypted_point.into(), - encrypted_key: encrypted_key.into(), - }) +pub fn generate_document_key( + account_public: Public, + server_key_public: Public, +) -> Result { + // generate random plain document key + let document_key = Random.generate().map_err(errors::encryption)?; + + // encrypt document key using server key + let (common_point, encrypted_point) = + encrypt_secret(document_key.public(), &server_key_public)?; + + // ..and now encrypt document key with account public + let encrypted_key = ethkey::crypto::ecies::encrypt( + &account_public, + &crypto::DEFAULT_MAC, + document_key.public(), + ) + .map_err(errors::encryption)?; + + Ok(EncryptedDocumentKey { + common_point: common_point.into(), + encrypted_point: encrypted_point.into(), + encrypted_key: encrypted_key.into(), + }) } /// Encrypt document with distributely generated key. pub fn encrypt_document(key: Bytes, document: Bytes) -> Result { - // make document key - let key = into_document_key(key)?; + // make document key + let key = into_document_key(key)?; - // use symmetric encryption to encrypt document - let iv = initialization_vector(); - let mut encrypted_document = vec![0; document.len() + iv.len()]; - { - let (mut encryption_buffer, iv_buffer) = encrypted_document.split_at_mut(document.len()); + // use symmetric encryption to encrypt document + let iv = initialization_vector(); + let mut encrypted_document = vec![0; document.len() + iv.len()]; + { + let (mut encryption_buffer, iv_buffer) = encrypted_document.split_at_mut(document.len()); - crypto::aes::encrypt_128_ctr(&key, &iv, &document, &mut encryption_buffer).map_err(errors::encryption)?; - iv_buffer.copy_from_slice(&iv); - } + crypto::aes::encrypt_128_ctr(&key, &iv, &document, &mut encryption_buffer) + .map_err(errors::encryption)?; + iv_buffer.copy_from_slice(&iv); + } - Ok(encrypted_document) + Ok(encrypted_document) } /// Decrypt document with distributely generated key. pub fn decrypt_document(key: Bytes, mut encrypted_document: Bytes) -> Result { - // initialization vector takes INIT_VEC_LEN bytes - let encrypted_document_len = encrypted_document.len(); - if encrypted_document_len < INIT_VEC_LEN { - return Err(errors::invalid_params("encrypted_document", "invalid encrypted data")); - } - - // make document key - let key = into_document_key(key)?; - - // use symmetric decryption to decrypt document - let iv = encrypted_document.split_off(encrypted_document_len - INIT_VEC_LEN); - let mut document = vec![0; encrypted_document_len - INIT_VEC_LEN]; - crypto::aes::decrypt_128_ctr(&key, &iv, &encrypted_document, &mut document).map_err(errors::encryption)?; - - Ok(document) + // initialization vector takes INIT_VEC_LEN bytes + let encrypted_document_len = encrypted_document.len(); + if encrypted_document_len < INIT_VEC_LEN { + return Err(errors::invalid_params( + "encrypted_document", + "invalid encrypted data", + )); + } + + // make document key + let key = into_document_key(key)?; + + // use symmetric decryption to decrypt document + let iv = encrypted_document.split_off(encrypted_document_len - INIT_VEC_LEN); + let mut document = vec![0; encrypted_document_len - INIT_VEC_LEN]; + crypto::aes::decrypt_128_ctr(&key, &iv, &encrypted_document, &mut document) + .map_err(errors::encryption)?; + + Ok(document) } /// Decrypt document given secret shadow. -pub fn decrypt_document_with_shadow(decrypted_secret: Public, common_point: Public, shadows: Vec, encrypted_document: Bytes) -> Result { - let key = decrypt_with_shadow_coefficients(decrypted_secret, common_point, shadows)?; - decrypt_document(key.to_vec(), encrypted_document) +pub fn decrypt_document_with_shadow( + decrypted_secret: Public, + common_point: Public, + shadows: Vec, + encrypted_document: Bytes, +) -> Result { + let key = decrypt_with_shadow_coefficients(decrypted_secret, common_point, shadows)?; + decrypt_document(key.to_vec(), encrypted_document) } /// Calculate Keccak(ordered servers set) pub fn ordered_servers_keccak(servers_set: BTreeSet) -> H256 { - let mut servers_set_keccak = Keccak::new_keccak256(); - for server in servers_set { - servers_set_keccak.update(&server.0); - } + let mut servers_set_keccak = Keccak::new_keccak256(); + for server in servers_set { + servers_set_keccak.update(&server.0); + } - let mut servers_set_keccak_value = [0u8; 32]; - servers_set_keccak.finalize(&mut servers_set_keccak_value); + let mut servers_set_keccak_value = [0u8; 32]; + servers_set_keccak.finalize(&mut servers_set_keccak_value); - servers_set_keccak_value.into() + servers_set_keccak_value.into() } fn into_document_key(key: Bytes) -> Result { - // key is a previously distributely generated Public - if key.len() != 64 { - return Err(errors::invalid_params("key", "invalid public key length")); - } + // key is a previously distributely generated Public + if key.len() != 64 { + return Err(errors::invalid_params("key", "invalid public key length")); + } - // use x coordinate of distributely generated point as encryption key - Ok(key[..INIT_VEC_LEN].into()) + // use x coordinate of distributely generated point as encryption key + Ok(key[..INIT_VEC_LEN].into()) } fn initialization_vector() -> [u8; INIT_VEC_LEN] { - let mut result = [0u8; INIT_VEC_LEN]; - let mut rng = OsRng::new().unwrap(); - rng.fill_bytes(&mut result); - result + let mut result = [0u8; INIT_VEC_LEN]; + let mut rng = OsRng::new().unwrap(); + rng.fill_bytes(&mut result); + result } -fn decrypt_with_shadow_coefficients(mut decrypted_shadow: Public, mut common_shadow_point: Public, shadow_coefficients: Vec) -> Result { - let mut shadow_coefficients_sum = shadow_coefficients[0].clone(); - for shadow_coefficient in shadow_coefficients.iter().skip(1) { - shadow_coefficients_sum.add(shadow_coefficient) - .map_err(errors::encryption)?; - } - - math::public_mul_secret(&mut common_shadow_point, &shadow_coefficients_sum) - .map_err(errors::encryption)?; - math::public_add(&mut decrypted_shadow, &common_shadow_point) - .map_err(errors::encryption)?; - Ok(decrypted_shadow) +fn decrypt_with_shadow_coefficients( + mut decrypted_shadow: Public, + mut common_shadow_point: Public, + shadow_coefficients: Vec, +) -> Result { + let mut shadow_coefficients_sum = shadow_coefficients[0].clone(); + for shadow_coefficient in shadow_coefficients.iter().skip(1) { + shadow_coefficients_sum + .add(shadow_coefficient) + .map_err(errors::encryption)?; + } + + math::public_mul_secret(&mut common_shadow_point, &shadow_coefficients_sum) + .map_err(errors::encryption)?; + math::public_add(&mut decrypted_shadow, &common_shadow_point).map_err(errors::encryption)?; + Ok(decrypted_shadow) } fn encrypt_secret(secret: &Public, joint_public: &Public) -> Result<(Public, Public), Error> { - // TODO: it is copypaste of `encrypt_secret` from secret_store/src/key_server_cluster/math.rs - // use shared version from SS math library, when it'll be available + // TODO: it is copypaste of `encrypt_secret` from secret_store/src/key_server_cluster/math.rs + // use shared version from SS math library, when it'll be available - let key_pair = Random.generate() - .map_err(errors::encryption)?; + let key_pair = Random.generate().map_err(errors::encryption)?; - // k * T - let mut common_point = math::generation_point(); - math::public_mul_secret(&mut common_point, key_pair.secret()) - .map_err(errors::encryption)?; + // k * T + let mut common_point = math::generation_point(); + math::public_mul_secret(&mut common_point, key_pair.secret()).map_err(errors::encryption)?; - // M + k * y - let mut encrypted_point = joint_public.clone(); - math::public_mul_secret(&mut encrypted_point, key_pair.secret()) - .map_err(errors::encryption)?; - math::public_add(&mut encrypted_point, secret) - .map_err(errors::encryption)?; + // M + k * y + let mut encrypted_point = joint_public.clone(); + math::public_mul_secret(&mut encrypted_point, key_pair.secret()).map_err(errors::encryption)?; + math::public_add(&mut encrypted_point, secret).map_err(errors::encryption)?; - Ok((common_point, encrypted_point)) + Ok((common_point, encrypted_point)) } #[cfg(test)] mod tests { - use bytes::Bytes; - use rustc_hex::FromHex; - use super::{encrypt_document, decrypt_document, decrypt_document_with_shadow}; - - #[test] - fn encrypt_and_decrypt_document() { - let document_key: Bytes = "cac6c205eb06c8308d65156ff6c862c62b000b8ead121a4455a8ddeff7248128d895692136f240d5d1614dc7cc4147b1bd584bd617e30560bb872064d09ea325".from_hex().unwrap(); - let document: Bytes = b"Hello, world!!!"[..].into(); - - let encrypted_document = encrypt_document(document_key.clone(), document.clone()).unwrap(); - assert!(document != encrypted_document); - - let decrypted_document = decrypt_document(document_key.clone(), encrypted_document).unwrap(); - assert_eq!(decrypted_document, document); - } - - #[test] - fn encrypt_and_shadow_decrypt_document() { - let document: Bytes = "deadbeef".from_hex().unwrap(); - let encrypted_document = "2ddec1f96229efa2916988d8b2a82a47ef36f71c".from_hex().unwrap(); - let decrypted_secret = "843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91".parse().unwrap(); - let common_point = "07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3".parse().unwrap(); - let shadows = vec!["46f542416216f66a7d7881f5a283d2a1ab7a87b381cbc5f29d0b093c7c89ee31".parse().unwrap()]; - let decrypted_document = decrypt_document_with_shadow(decrypted_secret, common_point, shadows, encrypted_document).unwrap(); - assert_eq!(decrypted_document, document); - } + use super::{decrypt_document, decrypt_document_with_shadow, encrypt_document}; + use bytes::Bytes; + use rustc_hex::FromHex; + + #[test] + fn encrypt_and_decrypt_document() { + let document_key: Bytes = "cac6c205eb06c8308d65156ff6c862c62b000b8ead121a4455a8ddeff7248128d895692136f240d5d1614dc7cc4147b1bd584bd617e30560bb872064d09ea325".from_hex().unwrap(); + let document: Bytes = b"Hello, world!!!"[..].into(); + + let encrypted_document = encrypt_document(document_key.clone(), document.clone()).unwrap(); + assert!(document != encrypted_document); + + let decrypted_document = + decrypt_document(document_key.clone(), encrypted_document).unwrap(); + assert_eq!(decrypted_document, document); + } + + #[test] + fn encrypt_and_shadow_decrypt_document() { + let document: Bytes = "deadbeef".from_hex().unwrap(); + let encrypted_document = "2ddec1f96229efa2916988d8b2a82a47ef36f71c" + .from_hex() + .unwrap(); + let decrypted_secret = "843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91".parse().unwrap(); + let common_point = "07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3".parse().unwrap(); + let shadows = vec![ + "46f542416216f66a7d7881f5a283d2a1ab7a87b381cbc5f29d0b093c7c89ee31" + .parse() + .unwrap(), + ]; + let decrypted_document = decrypt_document_with_shadow( + decrypted_secret, + common_point, + shadows, + encrypted_document, + ) + .unwrap(); + assert_eq!(decrypted_document, document); + } } diff --git a/rpc/src/v1/helpers/signature.rs b/rpc/src/v1/helpers/signature.rs index b191a3737e8..ce71b9f2dc9 100644 --- a/rpc/src/v1/helpers/signature.rs +++ b/rpc/src/v1/helpers/signature.rs @@ -1,182 +1,215 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use ethkey::{recover, public_to_address, Signature}; use ethereum_types::{H256, U64}; -use jsonrpc_core::Result; -use v1::types::{Bytes, RecoveredAccount}; -use v1::helpers::errors; -use v1::helpers::dispatch::eth_data_hash; +use ethkey::{public_to_address, recover, Signature}; use hash::keccak; +use jsonrpc_core::Result; +use v1::{ + helpers::{dispatch::eth_data_hash, errors}, + types::{Bytes, RecoveredAccount}, +}; /// helper method for parity_verifySignature pub fn verify_signature( - is_prefixed: bool, - message: Bytes, - r: H256, - s: H256, - v: U64, - chain_id: Option + is_prefixed: bool, + message: Bytes, + r: H256, + s: H256, + v: U64, + chain_id: Option, ) -> Result { - let hash = if is_prefixed { - eth_data_hash(message.0) - } else { - keccak(message.0) - }; - let v = v.as_u64(); - let is_valid_for_current_chain = match (chain_id, v) { - (None, v) if v == 0 || v == 1 => true, - (Some(chain_id), v) if v >= 35 => (v - 35) / 2 == chain_id, - _ => false, - }; - - let v = if v >= 35 { (v - 1) % 2 } else { v }; - - let signature = Signature::from_rsv(&r, &s, v as u8); - let public_key = recover(&signature, &hash).map_err(errors::encryption)?; - let address = public_to_address(&public_key); - Ok(RecoveredAccount { address, public_key, is_valid_for_current_chain }) + let hash = if is_prefixed { + eth_data_hash(message.0) + } else { + keccak(message.0) + }; + let v = v.as_u64(); + let is_valid_for_current_chain = match (chain_id, v) { + (None, v) if v == 0 || v == 1 => true, + (Some(chain_id), v) if v >= 35 => (v - 35) / 2 == chain_id, + _ => false, + }; + + let v = if v >= 35 { (v - 1) % 2 } else { v }; + + let signature = Signature::from_rsv(&r, &s, v as u8); + let public_key = recover(&signature, &hash).map_err(errors::encryption)?; + let address = public_to_address(&public_key); + Ok(RecoveredAccount { + address, + public_key, + is_valid_for_current_chain, + }) } #[cfg(test)] mod tests { - use super::*; - use ethkey::Generator; - use ethereum_types::{H160, U64}; - - pub fn add_chain_replay_protection(v: u64, chain_id: Option) -> u64 { - v + if let Some(n) = chain_id { 35 + n * 2 } else { 0 } - } - - struct TestCase { - should_prefix: bool, - signing_chain_id: Option, - rpc_chain_id: Option, - is_valid_for_current_chain: bool, - } - - /// mocked signer - fn sign(should_prefix: bool, data: Vec, signing_chain_id: Option) -> (H160, [u8; 32], [u8; 32], U64) { - let hash = if should_prefix { eth_data_hash(data) } else { keccak(data) }; - let account = ethkey::Random.generate().unwrap(); - let address = account.address(); - let sig = ethkey::sign(account.secret(), &hash).unwrap(); - let (r, s, v) = (sig.r(), sig.s(), sig.v()); - let v = add_chain_replay_protection(v as u64, signing_chain_id); - let (r_buf, s_buf) = { - let (mut r_buf, mut s_buf) = ([0u8; 32], [0u8; 32]); - r_buf.copy_from_slice(r); - s_buf.copy_from_slice(s); - (r_buf, s_buf) - }; - (address.into(), r_buf, s_buf, v.into()) - } - - fn run_test(test_case: TestCase) { - let TestCase { should_prefix, signing_chain_id, rpc_chain_id, is_valid_for_current_chain } = test_case; - let data = vec![5u8]; - - let (address, r, s, v) = sign(should_prefix, data.clone(), signing_chain_id); - let account = verify_signature(should_prefix, data.into(), r.into(), s.into(), v, rpc_chain_id).unwrap(); - - assert_eq!(account.address, address.into()); - assert_eq!(account.is_valid_for_current_chain, is_valid_for_current_chain) - } - - #[test] - fn test_verify_signature_prefixed_mainnet() { - run_test(TestCase { - should_prefix: true, - signing_chain_id: Some(1), - rpc_chain_id: Some(1), - is_valid_for_current_chain: true, - }) - } - - #[test] - fn test_verify_signature_not_prefixed_mainnet() { - run_test(TestCase { - should_prefix: false, - signing_chain_id: Some(1), - rpc_chain_id: Some(1), - is_valid_for_current_chain: true, - }) - } - - #[test] - fn test_verify_signature_incompatible_chain_id() { - run_test(TestCase { - should_prefix: false, - signing_chain_id: Some(65), - rpc_chain_id: Some(1), - is_valid_for_current_chain: false, - }); - run_test(TestCase { - should_prefix: true, - signing_chain_id: Some(65), - rpc_chain_id: Some(1), - is_valid_for_current_chain: false, - }); - } - - #[test] - fn test_verify_signature_no_signing_chain_id() { - run_test(TestCase { - should_prefix: false, - signing_chain_id: None, - rpc_chain_id: Some(1), - is_valid_for_current_chain: false, - }); - run_test(TestCase { - should_prefix: true, - signing_chain_id: None, - rpc_chain_id: Some(1), - is_valid_for_current_chain: false, - }); - } - - #[test] - fn test_verify_signature_no_rpc_chain_id() { - run_test(TestCase { - should_prefix: false, - signing_chain_id: Some(1), - rpc_chain_id: None, - is_valid_for_current_chain: false, - }); - run_test(TestCase { - should_prefix: true, - signing_chain_id: Some(1), - rpc_chain_id: None, - is_valid_for_current_chain: false, - }); - } - - #[test] - fn test_verify_signature_no_chain_replay_protection() { - run_test(TestCase { - should_prefix: false, - signing_chain_id: None, - rpc_chain_id: None, - is_valid_for_current_chain: true, - }); - run_test(TestCase { - should_prefix: true, - signing_chain_id: None, - rpc_chain_id: None, - is_valid_for_current_chain: true, - }); - } + use super::*; + use ethereum_types::{H160, U64}; + use ethkey::Generator; + + pub fn add_chain_replay_protection(v: u64, chain_id: Option) -> u64 { + v + if let Some(n) = chain_id { + 35 + n * 2 + } else { + 0 + } + } + + struct TestCase { + should_prefix: bool, + signing_chain_id: Option, + rpc_chain_id: Option, + is_valid_for_current_chain: bool, + } + + /// mocked signer + fn sign( + should_prefix: bool, + data: Vec, + signing_chain_id: Option, + ) -> (H160, [u8; 32], [u8; 32], U64) { + let hash = if should_prefix { + eth_data_hash(data) + } else { + keccak(data) + }; + let account = ethkey::Random.generate().unwrap(); + let address = account.address(); + let sig = ethkey::sign(account.secret(), &hash).unwrap(); + let (r, s, v) = (sig.r(), sig.s(), sig.v()); + let v = add_chain_replay_protection(v as u64, signing_chain_id); + let (r_buf, s_buf) = { + let (mut r_buf, mut s_buf) = ([0u8; 32], [0u8; 32]); + r_buf.copy_from_slice(r); + s_buf.copy_from_slice(s); + (r_buf, s_buf) + }; + (address.into(), r_buf, s_buf, v.into()) + } + + fn run_test(test_case: TestCase) { + let TestCase { + should_prefix, + signing_chain_id, + rpc_chain_id, + is_valid_for_current_chain, + } = test_case; + let data = vec![5u8]; + + let (address, r, s, v) = sign(should_prefix, data.clone(), signing_chain_id); + let account = verify_signature( + should_prefix, + data.into(), + r.into(), + s.into(), + v, + rpc_chain_id, + ) + .unwrap(); + + assert_eq!(account.address, address.into()); + assert_eq!( + account.is_valid_for_current_chain, + is_valid_for_current_chain + ) + } + + #[test] + fn test_verify_signature_prefixed_mainnet() { + run_test(TestCase { + should_prefix: true, + signing_chain_id: Some(1), + rpc_chain_id: Some(1), + is_valid_for_current_chain: true, + }) + } + + #[test] + fn test_verify_signature_not_prefixed_mainnet() { + run_test(TestCase { + should_prefix: false, + signing_chain_id: Some(1), + rpc_chain_id: Some(1), + is_valid_for_current_chain: true, + }) + } + + #[test] + fn test_verify_signature_incompatible_chain_id() { + run_test(TestCase { + should_prefix: false, + signing_chain_id: Some(65), + rpc_chain_id: Some(1), + is_valid_for_current_chain: false, + }); + run_test(TestCase { + should_prefix: true, + signing_chain_id: Some(65), + rpc_chain_id: Some(1), + is_valid_for_current_chain: false, + }); + } + + #[test] + fn test_verify_signature_no_signing_chain_id() { + run_test(TestCase { + should_prefix: false, + signing_chain_id: None, + rpc_chain_id: Some(1), + is_valid_for_current_chain: false, + }); + run_test(TestCase { + should_prefix: true, + signing_chain_id: None, + rpc_chain_id: Some(1), + is_valid_for_current_chain: false, + }); + } + + #[test] + fn test_verify_signature_no_rpc_chain_id() { + run_test(TestCase { + should_prefix: false, + signing_chain_id: Some(1), + rpc_chain_id: None, + is_valid_for_current_chain: false, + }); + run_test(TestCase { + should_prefix: true, + signing_chain_id: Some(1), + rpc_chain_id: None, + is_valid_for_current_chain: false, + }); + } + + #[test] + fn test_verify_signature_no_chain_replay_protection() { + run_test(TestCase { + should_prefix: false, + signing_chain_id: None, + rpc_chain_id: None, + is_valid_for_current_chain: true, + }); + run_test(TestCase { + should_prefix: true, + signing_chain_id: None, + rpc_chain_id: None, + is_valid_for_current_chain: true, + }); + } } diff --git a/rpc/src/v1/helpers/subscribers.rs b/rpc/src/v1/helpers/subscribers.rs index 9483d8e3216..b428d7fbc09 100644 --- a/rpc/src/v1/helpers/subscribers.rs +++ b/rpc/src/v1/helpers/subscribers.rs @@ -1,126 +1,128 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! A map of subscribers. -use std::{ops, str}; -use std::collections::HashMap; -use jsonrpc_pubsub::{typed::{Subscriber, Sink}, SubscriptionId}; use ethereum_types::H64; +use jsonrpc_pubsub::{ + typed::{Sink, Subscriber}, + SubscriptionId, +}; use rand::{Rng, StdRng}; +use std::{collections::HashMap, ops, str}; #[derive(Debug, Clone, Hash, Eq, PartialEq)] pub struct Id(H64); impl str::FromStr for Id { - type Err = String; - - fn from_str(s: &str) -> Result { - if s.starts_with("0x") { - Ok(Id(s[2..].parse().map_err(|e| format!("{}", e))?)) - } else { - Err("The id must start with 0x".into()) - } - } + type Err = String; + + fn from_str(s: &str) -> Result { + if s.starts_with("0x") { + Ok(Id(s[2..].parse().map_err(|e| format!("{}", e))?)) + } else { + Err("The id must start with 0x".into()) + } + } } impl Id { - // TODO: replace `format!` see [#10412](https://github.com/paritytech/parity-ethereum/issues/10412) - pub fn as_string(&self) -> String { - format!("{:?}", self.0) - } + // TODO: replace `format!` see [#10412](https://github.com/openethereum/openethereum/issues/10412) + pub fn as_string(&self) -> String { + format!("{:?}", self.0) + } } #[derive(Clone)] pub struct Subscribers { - rand: StdRng, - subscriptions: HashMap, + rand: StdRng, + subscriptions: HashMap, } impl Default for Subscribers { - fn default() -> Self { - Subscribers { - rand: StdRng::new().expect("Valid random source is required."), - subscriptions: HashMap::new(), - } - } + fn default() -> Self { + Subscribers { + rand: StdRng::new().expect("Valid random source is required."), + subscriptions: HashMap::new(), + } + } } impl Subscribers { - /// Create a new Subscribers with given random source. - #[cfg(test)] - pub fn new_test() -> Self { - Subscribers { - rand: ::rand::SeedableRng::from_seed([0usize].as_ref()), - subscriptions: HashMap::new(), - } - } - - fn next_id(&mut self) -> Id { - let mut data = H64::default(); - self.rand.fill_bytes(&mut data.0); - Id(data) - } - - /// Insert new subscription and return assigned id. - pub fn insert(&mut self, val: T) -> SubscriptionId { - let id = self.next_id(); - debug!(target: "pubsub", "Adding subscription id={:?}", id); - let s = id.as_string(); - self.subscriptions.insert(id, val); - SubscriptionId::String(s) - } - - /// Removes subscription with given id and returns it (if any). - pub fn remove(&mut self, id: &SubscriptionId) -> Option { - trace!(target: "pubsub", "Removing subscription id={:?}", id); - match *id { - SubscriptionId::String(ref id) => match id.parse() { - Ok(id) => self.subscriptions.remove(&id), - Err(_) => None, - }, - _ => None, - } - } + /// Create a new Subscribers with given random source. + #[cfg(test)] + pub fn new_test() -> Self { + Subscribers { + rand: ::rand::SeedableRng::from_seed([0usize].as_ref()), + subscriptions: HashMap::new(), + } + } + + fn next_id(&mut self) -> Id { + let mut data = H64::default(); + self.rand.fill_bytes(&mut data.0); + Id(data) + } + + /// Insert new subscription and return assigned id. + pub fn insert(&mut self, val: T) -> SubscriptionId { + let id = self.next_id(); + debug!(target: "pubsub", "Adding subscription id={:?}", id); + let s = id.as_string(); + self.subscriptions.insert(id, val); + SubscriptionId::String(s) + } + + /// Removes subscription with given id and returns it (if any). + pub fn remove(&mut self, id: &SubscriptionId) -> Option { + trace!(target: "pubsub", "Removing subscription id={:?}", id); + match *id { + SubscriptionId::String(ref id) => match id.parse() { + Ok(id) => self.subscriptions.remove(&id), + Err(_) => None, + }, + _ => None, + } + } } impl Subscribers> { - /// Assigns id and adds a subscriber to the list. - pub fn push(&mut self, sub: Subscriber) { - let id = self.next_id(); - if let Ok(sink) = sub.assign_id(SubscriptionId::String(id.as_string())) { - debug!(target: "pubsub", "Adding subscription id={:?}", id); - self.subscriptions.insert(id, sink); - } - } + /// Assigns id and adds a subscriber to the list. + pub fn push(&mut self, sub: Subscriber) { + let id = self.next_id(); + if let Ok(sink) = sub.assign_id(SubscriptionId::String(id.as_string())) { + debug!(target: "pubsub", "Adding subscription id={:?}", id); + self.subscriptions.insert(id, sink); + } + } } impl Subscribers<(Sink, V)> { - /// Assigns id and adds a subscriber to the list. - pub fn push(&mut self, sub: Subscriber, val: V) { - let id = self.next_id(); - if let Ok(sink) = sub.assign_id(SubscriptionId::String(id.as_string())) { - debug!(target: "pubsub", "Adding subscription id={:?}", id); - self.subscriptions.insert(id, (sink, val)); - } - } + /// Assigns id and adds a subscriber to the list. + pub fn push(&mut self, sub: Subscriber, val: V) { + let id = self.next_id(); + if let Ok(sink) = sub.assign_id(SubscriptionId::String(id.as_string())) { + debug!(target: "pubsub", "Adding subscription id={:?}", id); + self.subscriptions.insert(id, (sink, val)); + } + } } impl ops::Deref for Subscribers { - type Target = HashMap; + type Target = HashMap; - fn deref(&self) -> &Self::Target { - &self.subscriptions - } + fn deref(&self) -> &Self::Target { + &self.subscriptions + } } diff --git a/rpc/src/v1/helpers/subscription_manager.rs b/rpc/src/v1/helpers/subscription_manager.rs index d83beb397f0..2bf525b9b84 100644 --- a/rpc/src/v1/helpers/subscription_manager.rs +++ b/rpc/src/v1/helpers/subscription_manager.rs @@ -1,186 +1,208 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Generic poll manager for Pub-Sub. -use std::sync::Arc; -use std::sync::atomic::{self, AtomicBool}; use parking_lot::Mutex; - -use jsonrpc_core::futures::future::{self, Either}; -use jsonrpc_core::futures::sync::mpsc; -use jsonrpc_core::futures::{Sink, Future}; -use jsonrpc_core::{self as core, MetaIoHandler}; +use std::sync::{ + atomic::{self, AtomicBool}, + Arc, +}; + +use jsonrpc_core::{ + self as core, + futures::{ + future::{self, Either}, + sync::mpsc, + Future, Sink, + }, + MetaIoHandler, +}; use jsonrpc_pubsub::SubscriptionId; -use v1::helpers::Subscribers; -use v1::metadata::Metadata; +use v1::{helpers::Subscribers, metadata::Metadata}; #[derive(Debug)] struct Subscription { - metadata: Metadata, - method: String, - params: core::Params, - sink: mpsc::Sender>, - /// a flag if subscription is still active and last returned value - last_result: Arc<(AtomicBool, Mutex>)>, + metadata: Metadata, + method: String, + params: core::Params, + sink: mpsc::Sender>, + /// a flag if subscription is still active and last returned value + last_result: Arc<(AtomicBool, Mutex>)>, } /// A struct managing all subscriptions. /// TODO [ToDr] Depending on the method decide on poll interval. /// For most of the methods it will be enough to poll on new block instead of time-interval. pub struct GenericPollManager> { - subscribers: Subscribers, - rpc: MetaIoHandler, + subscribers: Subscribers, + rpc: MetaIoHandler, } impl> GenericPollManager { - /// Creates new poll manager - pub fn new(rpc: MetaIoHandler) -> Self { - GenericPollManager { - subscribers: Default::default(), - rpc, - } - } - - /// Creates new poll manager with deterministic ids. - #[cfg(test)] - pub fn new_test(rpc: MetaIoHandler) -> Self { - let mut manager = Self::new(rpc); - manager.subscribers = Subscribers::new_test(); - manager - } - - /// Subscribes to update from polling given method. - pub fn subscribe(&mut self, metadata: Metadata, method: String, params: core::Params) - -> (SubscriptionId, mpsc::Receiver>) - { - let (sink, stream) = mpsc::channel(1); - let subscription = Subscription { - metadata, - method, - params, - sink, - last_result: Default::default(), - }; - let id = self.subscribers.insert(subscription); - (id, stream) - } - - pub fn unsubscribe(&mut self, id: &SubscriptionId) -> bool { - debug!(target: "pubsub", "Removing subscription: {:?}", id); - self.subscribers.remove(id).map(|subscription| { - subscription.last_result.0.store(true, atomic::Ordering::SeqCst); - }).is_some() - } - - pub fn tick(&self) -> Box + Send> { - let mut futures = Vec::new(); - // poll all subscriptions - for (id, subscription) in self.subscribers.iter() { - let call = core::MethodCall { - jsonrpc: Some(core::Version::V2), - id: core::Id::Str(id.as_string()), - method: subscription.method.clone(), - params: subscription.params.clone(), - }; - trace!(target: "pubsub", "Polling method: {:?}", call); - let result = self.rpc.handle_call(call.into(), subscription.metadata.clone()); - - let last_result = subscription.last_result.clone(); - let sender = subscription.sink.clone(); - - let result = result.and_then(move |response| { - // quick check if the subscription is still valid - if last_result.0.load(atomic::Ordering::SeqCst) { - return Either::B(future::ok(())) - } - - let mut last_result = last_result.1.lock(); - if *last_result != response && response.is_some() { - let output = response.expect("Existence proved by the condition."); - debug!(target: "pubsub", "Got new response, sending: {:?}", output); - *last_result = Some(output.clone()); - - let send = match output { - core::Output::Success(core::Success { result, .. }) => Ok(result), - core::Output::Failure(core::Failure { error, .. }) => Err(error), - }; - Either::A(sender.send(send).map(|_| ()).map_err(|_| ())) - } else { - trace!(target: "pubsub", "Response was not changed: {:?}", response); - Either::B(future::ok(())) - } - }); - - futures.push(result) - } - - // return a future represeting all the polls - Box::new(future::join_all(futures).map(|_| ())) - } + /// Creates new poll manager + pub fn new(rpc: MetaIoHandler) -> Self { + GenericPollManager { + subscribers: Default::default(), + rpc, + } + } + + /// Creates new poll manager with deterministic ids. + #[cfg(test)] + pub fn new_test(rpc: MetaIoHandler) -> Self { + let mut manager = Self::new(rpc); + manager.subscribers = Subscribers::new_test(); + manager + } + + /// Subscribes to update from polling given method. + pub fn subscribe( + &mut self, + metadata: Metadata, + method: String, + params: core::Params, + ) -> ( + SubscriptionId, + mpsc::Receiver>, + ) { + let (sink, stream) = mpsc::channel(1); + let subscription = Subscription { + metadata, + method, + params, + sink, + last_result: Default::default(), + }; + let id = self.subscribers.insert(subscription); + (id, stream) + } + + pub fn unsubscribe(&mut self, id: &SubscriptionId) -> bool { + debug!(target: "pubsub", "Removing subscription: {:?}", id); + self.subscribers + .remove(id) + .map(|subscription| { + subscription + .last_result + .0 + .store(true, atomic::Ordering::SeqCst); + }) + .is_some() + } + + pub fn tick(&self) -> Box + Send> { + let mut futures = Vec::new(); + // poll all subscriptions + for (id, subscription) in self.subscribers.iter() { + let call = core::MethodCall { + jsonrpc: Some(core::Version::V2), + id: core::Id::Str(id.as_string()), + method: subscription.method.clone(), + params: subscription.params.clone(), + }; + trace!(target: "pubsub", "Polling method: {:?}", call); + let result = self + .rpc + .handle_call(call.into(), subscription.metadata.clone()); + + let last_result = subscription.last_result.clone(); + let sender = subscription.sink.clone(); + + let result = result.and_then(move |response| { + // quick check if the subscription is still valid + if last_result.0.load(atomic::Ordering::SeqCst) { + return Either::B(future::ok(())); + } + + let mut last_result = last_result.1.lock(); + if *last_result != response && response.is_some() { + let output = response.expect("Existence proved by the condition."); + debug!(target: "pubsub", "Got new response, sending: {:?}", output); + *last_result = Some(output.clone()); + + let send = match output { + core::Output::Success(core::Success { result, .. }) => Ok(result), + core::Output::Failure(core::Failure { error, .. }) => Err(error), + }; + Either::A(sender.send(send).map(|_| ()).map_err(|_| ())) + } else { + trace!(target: "pubsub", "Response was not changed: {:?}", response); + Either::B(future::ok(())) + } + }); + + futures.push(result) + } + + // return a future represeting all the polls + Box::new(future::join_all(futures).map(|_| ())) + } } #[cfg(test)] mod tests { - use std::sync::atomic::{self, AtomicBool}; - - use jsonrpc_core::{MetaIoHandler, NoopMiddleware, Value, Params}; - use jsonrpc_core::futures::{Future, Stream}; - use jsonrpc_pubsub::SubscriptionId; - use http::tokio::runtime::Runtime; - - use super::GenericPollManager; - - fn poll_manager() -> GenericPollManager { - let mut io = MetaIoHandler::default(); - let called = AtomicBool::new(false); - io.add_method("hello", move |_| { - if !called.load(atomic::Ordering::SeqCst) { - called.store(true, atomic::Ordering::SeqCst); - Ok(Value::String("hello".into())) - } else { - Ok(Value::String("world".into())) - } - }); - GenericPollManager::new_test(io) - } - - #[test] - fn should_poll_subscribed_method() { - // given - let mut el = Runtime::new().unwrap(); - let mut poll_manager = poll_manager(); - let (id, rx) = poll_manager.subscribe(Default::default(), "hello".into(), Params::None); - assert_eq!(id, SubscriptionId::String("0x416d77337e24399d".into())); - - // then - poll_manager.tick().wait().unwrap(); - let (res, rx) = el.block_on(rx.into_future()).unwrap(); - assert_eq!(res, Some(Ok(Value::String("hello".into())))); - - // retrieve second item - poll_manager.tick().wait().unwrap(); - let (res, rx) = el.block_on(rx.into_future()).unwrap(); - assert_eq!(res, Some(Ok(Value::String("world".into())))); - - // and no more notifications - poll_manager.tick().wait().unwrap(); - // we need to unsubscribe otherwise the future will never finish. - poll_manager.unsubscribe(&id); - assert_eq!(el.block_on(rx.into_future()).unwrap().0, None); - } + use std::sync::atomic::{self, AtomicBool}; + + use http::tokio::runtime::Runtime; + use jsonrpc_core::{ + futures::{Future, Stream}, + MetaIoHandler, NoopMiddleware, Params, Value, + }; + use jsonrpc_pubsub::SubscriptionId; + + use super::GenericPollManager; + + fn poll_manager() -> GenericPollManager { + let mut io = MetaIoHandler::default(); + let called = AtomicBool::new(false); + io.add_method("hello", move |_| { + if !called.load(atomic::Ordering::SeqCst) { + called.store(true, atomic::Ordering::SeqCst); + Ok(Value::String("hello".into())) + } else { + Ok(Value::String("world".into())) + } + }); + GenericPollManager::new_test(io) + } + + #[test] + fn should_poll_subscribed_method() { + // given + let mut el = Runtime::new().unwrap(); + let mut poll_manager = poll_manager(); + let (id, rx) = poll_manager.subscribe(Default::default(), "hello".into(), Params::None); + assert_eq!(id, SubscriptionId::String("0x416d77337e24399d".into())); + + // then + poll_manager.tick().wait().unwrap(); + let (res, rx) = el.block_on(rx.into_future()).unwrap(); + assert_eq!(res, Some(Ok(Value::String("hello".into())))); + + // retrieve second item + poll_manager.tick().wait().unwrap(); + let (res, rx) = el.block_on(rx.into_future()).unwrap(); + assert_eq!(res, Some(Ok(Value::String("world".into())))); + + // and no more notifications + poll_manager.tick().wait().unwrap(); + // we need to unsubscribe otherwise the future will never finish. + poll_manager.unsubscribe(&id); + assert_eq!(el.block_on(rx.into_future()).unwrap().0, None); + } } diff --git a/rpc/src/v1/helpers/work.rs b/rpc/src/v1/helpers/work.rs index b52cb70c5f0..9f9bb5df386 100644 --- a/rpc/src/v1/helpers/work.rs +++ b/rpc/src/v1/helpers/work.rs @@ -1,38 +1,45 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Helpers for submit a POW work. use std::sync::Arc; -use rlp; use ethcore::miner::{BlockChainClient, MinerService}; -use ethereum_types::{H64, H256}; +use ethereum_types::{H256, H64}; use jsonrpc_core::Error; +use rlp; use v1::helpers::errors; // Submit a POW work and return the block's hash -pub fn submit_work_detail(client: &Arc, miner: &Arc, nonce: H64, pow_hash: H256, mix_hash: H256) -> Result { - // TODO [ToDr] Should disallow submissions in case of PoA? - trace!(target: "miner", "submit_work_detail: Decoded: nonce={}, pow_hash={}, mix_hash={}", nonce, pow_hash, mix_hash); - let seal = vec![rlp::encode(&mix_hash), rlp::encode(&nonce)]; - miner.submit_seal(pow_hash, seal) - .and_then(|block| client.import_sealed_block(block)) - .map_err(|e| { - warn!(target: "miner", "Cannot submit work - {:?}.", e); - errors::cannot_submit_work(e) - }) +pub fn submit_work_detail( + client: &Arc, + miner: &Arc, + nonce: H64, + pow_hash: H256, + mix_hash: H256, +) -> Result { + // TODO [ToDr] Should disallow submissions in case of PoA? + trace!(target: "miner", "submit_work_detail: Decoded: nonce={}, pow_hash={}, mix_hash={}", nonce, pow_hash, mix_hash); + let seal = vec![rlp::encode(&mix_hash), rlp::encode(&nonce)]; + miner + .submit_seal(pow_hash, seal) + .and_then(|block| client.import_sealed_block(block)) + .map_err(|e| { + warn!(target: "miner", "Cannot submit work - {:?}.", e); + errors::cannot_submit_work(e) + }) } diff --git a/rpc/src/v1/impls/debug.rs b/rpc/src/v1/impls/debug.rs index e46dd628d1e..d72adce5243 100644 --- a/rpc/src/v1/impls/debug.rs +++ b/rpc/src/v1/impls/debug.rs @@ -1,97 +1,114 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Debug APIs RPC implementation use std::sync::Arc; use ethcore::client::BlockChainClient; -use types::header::Header; -use types::transaction::LocalizedTransaction; +use types::{header::Header, transaction::LocalizedTransaction}; use jsonrpc_core::Result; -use v1::traits::Debug; -use v1::types::{Block, Bytes, RichBlock, BlockTransactions, Transaction}; +use v1::{ + traits::Debug, + types::{Block, BlockTransactions, Bytes, RichBlock, Transaction}, +}; /// Debug rpc implementation. pub struct DebugClient { - client: Arc, + client: Arc, } impl DebugClient { - /// Creates new debug client. - pub fn new(client: Arc) -> Self { - Self { - client, - } - } + /// Creates new debug client. + pub fn new(client: Arc) -> Self { + Self { client } + } } impl Debug for DebugClient { - fn bad_blocks(&self) -> Result> { - fn cast>(t: &T) -> O { - (*t).into() - } + fn bad_blocks(&self) -> Result> { + fn cast>(t: &T) -> O { + (*t).into() + } - Ok(self.client.bad_blocks().into_iter().map(|(block, reason)| { - let number = block.header.number(); - let hash = block.header.hash(); - RichBlock { - inner: Block { - hash: Some(hash), - size: Some(block.bytes.len().into()), - parent_hash: cast(block.header.parent_hash()), - uncles_hash: cast(block.header.uncles_hash()), - author: cast(block.header.author()), - miner: cast(block.header.author()), - state_root: cast(block.header.state_root()), - receipts_root: cast(block.header.receipts_root()), - number: Some(number.into()), - gas_used: cast(block.header.gas_used()), - gas_limit: cast(block.header.gas_limit()), - logs_bloom: Some(cast(block.header.log_bloom())), - timestamp: block.header.timestamp().into(), - difficulty: cast(block.header.difficulty()), - total_difficulty: None, - seal_fields: block.header.seal().iter().cloned().map(Into::into).collect(), - uncles: block.uncles.iter().map(Header::hash).collect(), - transactions: BlockTransactions::Full(block.transactions - .into_iter() - .enumerate() - .map(|(transaction_index, signed)| Transaction::from_localized(LocalizedTransaction { - block_number: number, - block_hash: hash, - transaction_index, - signed, - cached_sender: None, - })).collect() - ), - transactions_root: cast(block.header.transactions_root()), - extra_data: block.header.extra_data().clone().into(), - }, - extra_info: vec![ - ("reason".to_owned(), reason), - ("rlp".to_owned(), serialize(&Bytes(block.bytes))), - ("hash".to_owned(), format!("{:#x}", hash)), - ].into_iter().collect(), - } - }).collect()) - } + Ok(self + .client + .bad_blocks() + .into_iter() + .map(|(block, reason)| { + let number = block.header.number(); + let hash = block.header.hash(); + RichBlock { + inner: Block { + hash: Some(hash), + size: Some(block.bytes.len().into()), + parent_hash: cast(block.header.parent_hash()), + uncles_hash: cast(block.header.uncles_hash()), + author: cast(block.header.author()), + miner: cast(block.header.author()), + state_root: cast(block.header.state_root()), + receipts_root: cast(block.header.receipts_root()), + number: Some(number.into()), + gas_used: cast(block.header.gas_used()), + gas_limit: cast(block.header.gas_limit()), + logs_bloom: Some(cast(block.header.log_bloom())), + timestamp: block.header.timestamp().into(), + difficulty: cast(block.header.difficulty()), + total_difficulty: None, + seal_fields: block + .header + .seal() + .iter() + .cloned() + .map(Into::into) + .collect(), + uncles: block.uncles.iter().map(Header::hash).collect(), + transactions: BlockTransactions::Full( + block + .transactions + .into_iter() + .enumerate() + .map(|(transaction_index, signed)| { + Transaction::from_localized(LocalizedTransaction { + block_number: number, + block_hash: hash, + transaction_index, + signed, + cached_sender: None, + }) + }) + .collect(), + ), + transactions_root: cast(block.header.transactions_root()), + extra_data: block.header.extra_data().clone().into(), + }, + extra_info: vec![ + ("reason".to_owned(), reason), + ("rlp".to_owned(), serialize(&Bytes(block.bytes))), + ("hash".to_owned(), format!("{:#x}", hash)), + ] + .into_iter() + .collect(), + } + }) + .collect()) + } } fn serialize(t: &T) -> String { - ::serde_json::to_string(t).expect("RPC types serialization is non-fallible.") + ::serde_json::to_string(t).expect("RPC types serialization is non-fallible.") } diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 32f9c1c57f1..882f382ecec 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -1,994 +1,1203 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Eth rpc implementation. -use std::thread; -use std::time::{Instant, Duration, SystemTime, UNIX_EPOCH}; -use std::sync::Arc; +use std::{ + sync::Arc, + thread, + time::{Duration, Instant, SystemTime, UNIX_EPOCH}, +}; -use rlp::Rlp; -use ethereum_types::{Address, H64, H160, H256, U64, U256}; +use ethereum_types::{Address, H160, H256, H64, U256, U64}; use parking_lot::Mutex; +use rlp::Rlp; use ethash::{self, SeedHashCompute}; -use ethcore::client::{BlockChainClient, BlockId, TransactionId, UncleId, StateOrBlock, StateClient, StateInfo, Call, EngineInfo, ProvingBlockChainClient}; -use ethcore::miner::{self, MinerService}; -use ethcore::snapshot::SnapshotService; +use ethcore::{ + client::{ + BlockChainClient, BlockId, Call, EngineInfo, ProvingBlockChainClient, StateClient, + StateInfo, StateOrBlock, TransactionId, UncleId, + }, + miner::{self, MinerService}, + snapshot::SnapshotService, +}; use hash::keccak; use miner::external::ExternalMinerService; use sync::SyncProvider; -use types::transaction::{SignedTransaction, LocalizedTransaction}; -use types::BlockNumber as EthBlockNumber; -use types::encoded; -use types::filter::Filter as EthcoreFilter; - -use jsonrpc_core::{BoxFuture, Result}; -use jsonrpc_core::futures::future; - -use v1::helpers::{self, errors, limit_logs, fake_sign}; -use v1::helpers::deprecated::{self, DeprecationNotice}; -use v1::helpers::dispatch::{FullDispatcher, default_gas_price}; -use v1::helpers::block_import::is_major_importing; -use v1::traits::Eth; -use v1::types::{ - RichBlock, Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, - Transaction, CallRequest, Index, Filter, Log, Receipt, Work, EthAccount, StorageProof, - block_number_to_id +use types::{ + encoded, + filter::Filter as EthcoreFilter, + header::Header, + transaction::{LocalizedTransaction, SignedTransaction}, + BlockNumber as EthBlockNumber, +}; + +use jsonrpc_core::{futures::future, BoxFuture, Result}; + +use v1::{ + helpers::{ + self, + block_import::is_major_importing, + deprecated::{self, DeprecationNotice}, + dispatch::{default_gas_price, FullDispatcher}, + errors, fake_sign, limit_logs, + }, + metadata::Metadata, + traits::Eth, + types::{ + block_number_to_id, Block, BlockNumber, BlockTransactions, Bytes, CallRequest, EthAccount, + Filter, Index, Log, Receipt, RichBlock, StorageProof, SyncInfo, SyncStatus, Transaction, + Work, + }, }; -use v1::metadata::Metadata; const EXTRA_INFO_PROOF: &str = "Object exists in blockchain (fetched earlier), extra_info is always available if object exists; qed"; /// Eth RPC options +#[derive(Copy, Clone)] pub struct EthClientOptions { - /// Return nonce from transaction queue when pending block not available. - pub pending_nonce_from_queue: bool, - /// Returns receipt from pending blocks - pub allow_pending_receipt_query: bool, - /// Send additional block number when asking for work - pub send_block_number_in_get_work: bool, - /// Gas Price Percentile used as default gas price. - pub gas_price_percentile: usize, - /// Return 'null' instead of an error if ancient block sync is still in - /// progress and the block information requested could not be found. - pub allow_missing_blocks: bool, - /// Enable Experimental RPC-Calls - pub allow_experimental_rpcs: bool, + /// Gas Price Percentile used as default gas price. + pub gas_price_percentile: usize, + /// Return 'null' instead of an error if ancient block sync is still in + /// progress and the block information requested could not be found. + pub allow_missing_blocks: bool, + /// Enable Experimental RPC-Calls + pub allow_experimental_rpcs: bool, + /// flag for ancient block sync + pub no_ancient_blocks: bool, } impl EthClientOptions { - /// Creates new default `EthClientOptions` and allows alterations - /// by provided function. - pub fn with(fun: F) -> Self { - let mut options = Self::default(); - fun(&mut options); - options - } + /// Creates new default `EthClientOptions` and allows alterations + /// by provided function. + pub fn with(fun: F) -> Self { + let mut options = Self::default(); + fun(&mut options); + options + } } impl Default for EthClientOptions { - fn default() -> Self { - EthClientOptions { - pending_nonce_from_queue: false, - allow_pending_receipt_query: true, - send_block_number_in_get_work: true, - gas_price_percentile: 50, - allow_missing_blocks: false, - allow_experimental_rpcs: false, - } - } + fn default() -> Self { + EthClientOptions { + gas_price_percentile: 50, + allow_missing_blocks: false, + allow_experimental_rpcs: false, + no_ancient_blocks: false, + } + } } /// Eth rpc implementation. -pub struct EthClient where - C: miner::BlockChainClient + BlockChainClient, - SN: SnapshotService, - S: SyncProvider, - M: MinerService, - EM: ExternalMinerService { - - client: Arc, - snapshot: Arc, - sync: Arc, - accounts: Arc Vec
+ Send + Sync>, - miner: Arc, - external_miner: Arc, - seed_compute: Mutex, - options: EthClientOptions, - deprecation_notice: DeprecationNotice, +pub struct EthClient +where + C: miner::BlockChainClient + BlockChainClient, + SN: SnapshotService, + S: SyncProvider, + M: MinerService, + EM: ExternalMinerService, +{ + client: Arc, + snapshot: Arc, + sync: Arc, + accounts: Arc Vec
+ Send + Sync>, + miner: Arc, + external_miner: Arc, + seed_compute: Mutex, + options: EthClientOptions, + deprecation_notice: DeprecationNotice, } #[derive(Debug)] enum BlockNumberOrId { - Number(BlockNumber), - Id(BlockId), + Number(BlockNumber), + Id(BlockId), } impl From for BlockNumberOrId { - fn from(value: BlockId) -> BlockNumberOrId { - BlockNumberOrId::Id(value) - } + fn from(value: BlockId) -> BlockNumberOrId { + BlockNumberOrId::Id(value) + } } impl From for BlockNumberOrId { - fn from(value: BlockNumber) -> BlockNumberOrId { - BlockNumberOrId::Number(value) - } + fn from(value: BlockNumber) -> BlockNumberOrId { + BlockNumberOrId::Number(value) + } } enum PendingOrBlock { - Block(BlockId), - Pending, + Block(BlockId), + Pending, } struct PendingUncleId { - id: PendingOrBlock, - position: usize, + id: PendingOrBlock, + position: usize, } enum PendingTransactionId { - Hash(H256), - Location(PendingOrBlock, usize) + Hash(H256), + Location(PendingOrBlock, usize), } -pub fn base_logs (client: &C, miner: &M, filter: Filter) -> BoxFuture> where - C: miner::BlockChainClient + BlockChainClient + StateClient + Call, - M: MinerService { - let include_pending = filter.to_block == Some(BlockNumber::Pending); - let filter: EthcoreFilter = match filter.try_into() { - Ok(value) => value, - Err(err) => return Box::new(future::err(err)), - }; - let mut logs = match client.logs(filter.clone()) { - Ok(logs) => logs - .into_iter() - .map(From::from) - .collect::>(), - Err(id) => return Box::new(future::err(errors::filter_block_not_found(id))), - }; - - if include_pending { - let best_block = client.chain_info().best_block_number; - let pending = pending_logs(&*miner, best_block, &filter); - logs.extend(pending); - } - - let logs = limit_logs(logs, filter.limit); - - Box::new(future::ok(logs)) -} - -impl EthClient where - C: miner::BlockChainClient + BlockChainClient + StateClient + Call + EngineInfo, - SN: SnapshotService, - S: SyncProvider, - M: MinerService, - EM: ExternalMinerService { - - /// Creates new EthClient. - pub fn new( - client: &Arc, - snapshot: &Arc, - sync: &Arc, - accounts: &Arc Vec
+ Send + Sync>, - miner: &Arc, - em: &Arc, - options: EthClientOptions - ) -> Self { - EthClient { - client: client.clone(), - snapshot: snapshot.clone(), - sync: sync.clone(), - miner: miner.clone(), - accounts: accounts.clone(), - external_miner: em.clone(), - seed_compute: Mutex::new(SeedHashCompute::default()), - options, - deprecation_notice: Default::default(), - } - } - - fn rich_block(&self, id: BlockNumberOrId, include_txs: bool) -> Result> { - let client = &self.client; - - let client_query = |id| (client.block(id), client.block_total_difficulty(id), client.block_extra_info(id), false); - - let (block, difficulty, extra, is_pending) = match id { - BlockNumberOrId::Number(BlockNumber::Pending) => { - let info = self.client.chain_info(); - match self.miner.pending_block(info.best_block_number) { - Some(pending_block) => { - warn!("`Pending` is deprecated and may be removed in future versions."); - - let difficulty = { - let latest_difficulty = self.client.block_total_difficulty(BlockId::Latest).expect("blocks in chain have details; qed"); - let pending_difficulty = self.miner.pending_block_header(info.best_block_number).map(|header| *header.difficulty()); - - if let Some(difficulty) = pending_difficulty { - difficulty + latest_difficulty - } else { - latest_difficulty - } - }; - - let extra = self.client.engine().extra_info(&pending_block.header); - - (Some(encoded::Block::new(pending_block.rlp_bytes())), Some(difficulty), Some(extra), true) - }, - None => { - warn!("`Pending` is deprecated and may be removed in future versions. Falling back to `Latest`"); - client_query(BlockId::Latest) - } - } - }, - - BlockNumberOrId::Number(num) => { - let id = match num { - BlockNumber::Latest => BlockId::Latest, - BlockNumber::Earliest => BlockId::Earliest, - BlockNumber::Num(n) => BlockId::Number(n), - BlockNumber::Pending => unreachable!() // Already covered - }; - - client_query(id) - }, - - BlockNumberOrId::Id(id) => client_query(id), - }; - - match (block, difficulty) { - (Some(block), Some(total_difficulty)) => { - let view = block.header_view(); - Ok(Some(RichBlock { - inner: Block { - hash: match is_pending { - true => None, - false => Some(view.hash()), - }, - size: Some(block.rlp().as_raw().len().into()), - parent_hash: view.parent_hash(), - uncles_hash: view.uncles_hash(), - author: view.author(), - miner: view.author(), - state_root: view.state_root(), - transactions_root: view.transactions_root(), - receipts_root: view.receipts_root(), - number: match is_pending { - true => None, - false => Some(view.number().into()), - }, - gas_used: view.gas_used(), - gas_limit: view.gas_limit(), - logs_bloom: match is_pending { - true => None, - false => Some(view.log_bloom()), - }, - timestamp: view.timestamp().into(), - difficulty: view.difficulty(), - total_difficulty: Some(total_difficulty), - seal_fields: view.seal().into_iter().map(Into::into).collect(), - uncles: block.uncle_hashes(), - transactions: match include_txs { - true => BlockTransactions::Full(block.view().localized_transactions().into_iter().map(Transaction::from_localized).collect()), - false => BlockTransactions::Hashes(block.transaction_hashes()), - }, - extra_data: Bytes::new(view.extra_data()), - }, - extra_info: extra.expect(EXTRA_INFO_PROOF), - })) - }, - _ => Ok(None) - } - } - - fn transaction(&self, id: PendingTransactionId) -> Result> { - let client_transaction = |id| match self.client.transaction(id) { - Some(t) => Ok(Some(Transaction::from_localized(t))), - None => Ok(None), - }; - - match id { - PendingTransactionId::Hash(hash) => client_transaction(TransactionId::Hash(hash)), - - PendingTransactionId::Location(PendingOrBlock::Block(block), index) => { - client_transaction(TransactionId::Location(block, index)) - }, - - PendingTransactionId::Location(PendingOrBlock::Pending, index) => { - let info = self.client.chain_info(); - let pending_block = match self.miner.pending_block(info.best_block_number) { - Some(block) => block, - None => return Ok(None), - }; - - // Implementation stolen from `extract_transaction_at_index` - let transaction = pending_block.transactions.get(index) - // Verify if transaction signature is correct. - .and_then(|tx| SignedTransaction::new(tx.clone()).ok()) - .map(|signed_tx| { - let (signed, sender, _) = signed_tx.deconstruct(); - let block_hash = pending_block.header.hash(); - let block_number = pending_block.header.number(); - let transaction_index = index; - let cached_sender = Some(sender); - - LocalizedTransaction { - signed, - block_number, - block_hash, - transaction_index, - cached_sender, - } - }) - .map(Transaction::from_localized); - - Ok(transaction) - } - } - } - - fn uncle(&self, id: PendingUncleId) -> Result> { - let client = &self.client; - - let (uncle, parent_difficulty, extra) = match id { - PendingUncleId { id: PendingOrBlock::Pending, position } => { - let info = self.client.chain_info(); - - let pending_block = match self.miner.pending_block(info.best_block_number) { - Some(block) => block, - None => return Ok(None), - }; - - let uncle = match pending_block.uncles.get(position) { - Some(uncle) => uncle.clone(), - None => return Ok(None), - }; - - let difficulty = { - let latest_difficulty = self.client.block_total_difficulty(BlockId::Latest).expect("blocks in chain have details; qed"); - let pending_difficulty = self.miner.pending_block_header(info.best_block_number).map(|header| *header.difficulty()); - - if let Some(difficulty) = pending_difficulty { - difficulty + latest_difficulty - } else { - latest_difficulty - } - }; - - let extra = self.client.engine().extra_info(&pending_block.header); - - (uncle, difficulty, extra) - }, - - PendingUncleId { id: PendingOrBlock::Block(block_id), position } => { - let uncle_id = UncleId { block: block_id, position }; - - let uncle = match client.uncle(uncle_id) { - Some(hdr) => match hdr.decode() { - Ok(h) => h, - Err(e) => return Err(errors::decode(e)) - }, - None => { return Ok(None); } - }; - - let parent_difficulty = match client.block_total_difficulty(BlockId::Hash(*uncle.parent_hash())) { - Some(difficulty) => difficulty, - None => { return Ok(None); } - }; - - let extra = client.uncle_extra_info(uncle_id).expect(EXTRA_INFO_PROOF); - - (uncle, parent_difficulty, extra) - } - }; - - let size = client.block(BlockId::Hash(uncle.hash())) - .map(|block| block.into_inner().len()) - .map(U256::from); - - let block = RichBlock { - inner: Block { - hash: Some(uncle.hash()), - size, - parent_hash: *uncle.parent_hash(), - uncles_hash: *uncle.uncles_hash(), - author: *uncle.author(), - miner: *uncle.author(), - state_root: *uncle.state_root(), - transactions_root: *uncle.transactions_root(), - number: Some(uncle.number().into()), - gas_used: *uncle.gas_used(), - gas_limit: *uncle.gas_limit(), - logs_bloom: Some(*uncle.log_bloom()), - timestamp: uncle.timestamp().into(), - difficulty: *uncle.difficulty(), - total_difficulty: Some(uncle.difficulty() + parent_difficulty), - receipts_root: *uncle.receipts_root(), - extra_data: uncle.extra_data().clone().into(), - seal_fields: uncle.seal().iter().cloned().map(Into::into).collect(), - uncles: vec![], - transactions: BlockTransactions::Hashes(vec![]), - }, - extra_info: extra, - }; - Ok(Some(block)) - } - - fn get_state(&self, number: BlockNumber) -> StateOrBlock { - match number { - BlockNumber::Num(num) => BlockId::Number(num).into(), - BlockNumber::Earliest => BlockId::Earliest.into(), - BlockNumber::Latest => BlockId::Latest.into(), - - BlockNumber::Pending => { - let info = self.client.chain_info(); - - self.miner +impl EthClient +where + C: miner::BlockChainClient + + BlockChainClient + + StateClient + + Call + + EngineInfo, + SN: SnapshotService, + S: SyncProvider, + M: MinerService, + EM: ExternalMinerService, +{ + /// Creates new EthClient. + pub fn new( + client: &Arc, + snapshot: &Arc, + sync: &Arc, + accounts: &Arc Vec
+ Send + Sync>, + miner: &Arc, + em: &Arc, + options: EthClientOptions, + ) -> Self { + EthClient { + client: client.clone(), + snapshot: snapshot.clone(), + sync: sync.clone(), + miner: miner.clone(), + accounts: accounts.clone(), + external_miner: em.clone(), + seed_compute: Mutex::new(SeedHashCompute::default()), + options, + deprecation_notice: Default::default(), + } + } + + fn rich_block(&self, id: BlockNumberOrId, include_txs: bool) -> Result> { + let client = &self.client; + + let client_query = |id| { + ( + client.block(id), + client.block_total_difficulty(id), + client.block_extra_info(id), + false, + ) + }; + + let (block, difficulty, extra, is_pending) = match id { + BlockNumberOrId::Number(BlockNumber::Pending) => { + let info = self.client.chain_info(); + match self.miner.pending_block(info.best_block_number) { + Some(pending_block) => { + warn!("`Pending` is deprecated and may be removed in future versions."); + + let difficulty = { + let latest_difficulty = self + .client + .block_total_difficulty(BlockId::Latest) + .expect("blocks in chain have details; qed"); + let pending_difficulty = self + .miner + .pending_block_header(info.best_block_number) + .map(|header| *header.difficulty()); + + if let Some(difficulty) = pending_difficulty { + difficulty + latest_difficulty + } else { + latest_difficulty + } + }; + + let extra = self.client.engine().extra_info(&pending_block.header); + + ( + Some(encoded::Block::new(pending_block.rlp_bytes())), + Some(difficulty), + Some(extra), + true, + ) + } + None => { + warn!("`Pending` is deprecated and may be removed in future versions. Falling back to `Latest`"); + client_query(BlockId::Latest) + } + } + } + + BlockNumberOrId::Number(num) => { + let id = match num { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), + BlockNumber::Latest => BlockId::Latest, + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Num(n) => BlockId::Number(n), + BlockNumber::Pending => unreachable!(), // Already covered + }; + + client_query(id) + } + + BlockNumberOrId::Id(id) => client_query(id), + }; + + match (block, difficulty) { + (Some(block), Some(total_difficulty)) => { + let view = block.header_view(); + Ok(Some(RichBlock { + inner: Block { + hash: match is_pending { + true => None, + false => Some(view.hash()), + }, + size: Some(block.rlp().as_raw().len().into()), + parent_hash: view.parent_hash(), + uncles_hash: view.uncles_hash(), + author: view.author(), + miner: view.author(), + state_root: view.state_root(), + transactions_root: view.transactions_root(), + receipts_root: view.receipts_root(), + number: match is_pending { + true => None, + false => Some(view.number().into()), + }, + gas_used: view.gas_used(), + gas_limit: view.gas_limit(), + logs_bloom: match is_pending { + true => None, + false => Some(view.log_bloom()), + }, + timestamp: view.timestamp().into(), + difficulty: view.difficulty(), + total_difficulty: Some(total_difficulty), + seal_fields: view.seal().into_iter().map(Into::into).collect(), + uncles: block.uncle_hashes(), + transactions: match include_txs { + true => BlockTransactions::Full( + block + .view() + .localized_transactions() + .into_iter() + .map(Transaction::from_localized) + .collect(), + ), + false => BlockTransactions::Hashes(block.transaction_hashes()), + }, + extra_data: Bytes::new(view.extra_data()), + }, + extra_info: extra.expect(EXTRA_INFO_PROOF), + })) + } + _ => Ok(None), + } + } + + fn transaction(&self, id: PendingTransactionId) -> Result> { + let client_transaction = |id| match self.client.transaction(id) { + Some(t) => Ok(Some(Transaction::from_localized(t))), + None => Ok(None), + }; + + match id { + PendingTransactionId::Hash(hash) => client_transaction(TransactionId::Hash(hash)), + + PendingTransactionId::Location(PendingOrBlock::Block(block), index) => { + client_transaction(TransactionId::Location(block, index)) + } + + PendingTransactionId::Location(PendingOrBlock::Pending, index) => { + let info = self.client.chain_info(); + let pending_block = match self.miner.pending_block(info.best_block_number) { + Some(block) => block, + None => return Ok(None), + }; + + // Implementation stolen from `extract_transaction_at_index` + let transaction = pending_block + .transactions + .get(index) + // Verify if transaction signature is correct. + .and_then(|tx| SignedTransaction::new(tx.clone()).ok()) + .map(|signed_tx| { + let (signed, sender, _) = signed_tx.deconstruct(); + let block_hash = pending_block.header.hash(); + let block_number = pending_block.header.number(); + let transaction_index = index; + let cached_sender = Some(sender); + + LocalizedTransaction { + signed, + block_number, + block_hash, + transaction_index, + cached_sender, + } + }) + .map(Transaction::from_localized); + + Ok(transaction) + } + } + } + + fn uncle(&self, id: PendingUncleId) -> Result> { + let client = &self.client; + + let (uncle, parent_difficulty, extra) = match id { + PendingUncleId { + id: PendingOrBlock::Pending, + position, + } => { + let info = self.client.chain_info(); + + let pending_block = match self.miner.pending_block(info.best_block_number) { + Some(block) => block, + None => return Ok(None), + }; + + let uncle = match pending_block.uncles.get(position) { + Some(uncle) => uncle.clone(), + None => return Ok(None), + }; + + let difficulty = { + let latest_difficulty = self + .client + .block_total_difficulty(BlockId::Latest) + .expect("blocks in chain have details; qed"); + let pending_difficulty = self + .miner + .pending_block_header(info.best_block_number) + .map(|header| *header.difficulty()); + + if let Some(difficulty) = pending_difficulty { + difficulty + latest_difficulty + } else { + latest_difficulty + } + }; + + let extra = self.client.engine().extra_info(&pending_block.header); + + (uncle, difficulty, extra) + } + + PendingUncleId { + id: PendingOrBlock::Block(block_id), + position, + } => { + let uncle_id = UncleId { + block: block_id, + position, + }; + + let uncle = match client.uncle(uncle_id) { + Some(hdr) => match hdr.decode() { + Ok(h) => h, + Err(e) => return Err(errors::decode(e)), + }, + None => { + return Ok(None); + } + }; + + let parent_difficulty = + match client.block_total_difficulty(BlockId::Hash(*uncle.parent_hash())) { + Some(difficulty) => difficulty, + None => { + return Ok(None); + } + }; + + let extra = client.uncle_extra_info(uncle_id).expect(EXTRA_INFO_PROOF); + + (uncle, parent_difficulty, extra) + } + }; + + let size = client + .block(BlockId::Hash(uncle.hash())) + .map(|block| block.into_inner().len()) + .map(U256::from); + + let block = RichBlock { + inner: Block { + hash: Some(uncle.hash()), + size, + parent_hash: *uncle.parent_hash(), + uncles_hash: *uncle.uncles_hash(), + author: *uncle.author(), + miner: *uncle.author(), + state_root: *uncle.state_root(), + transactions_root: *uncle.transactions_root(), + number: Some(uncle.number().into()), + gas_used: *uncle.gas_used(), + gas_limit: *uncle.gas_limit(), + logs_bloom: Some(*uncle.log_bloom()), + timestamp: uncle.timestamp().into(), + difficulty: *uncle.difficulty(), + total_difficulty: Some(uncle.difficulty() + parent_difficulty), + receipts_root: *uncle.receipts_root(), + extra_data: uncle.extra_data().clone().into(), + seal_fields: uncle.seal().iter().cloned().map(Into::into).collect(), + uncles: vec![], + transactions: BlockTransactions::Hashes(vec![]), + }, + extra_info: extra, + }; + Ok(Some(block)) + } + + /// Get state for the given block number. Returns either the State or a block from which state + /// can be retrieved. + /// Note: When passing `BlockNumber::Pending` we fall back to the state of the current best block + /// if no state found for the best pending block. + fn get_state(&self, number: BlockNumber) -> StateOrBlock { + match number { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash).into(), + BlockNumber::Num(num) => BlockId::Number(num).into(), + BlockNumber::Earliest => BlockId::Earliest.into(), + BlockNumber::Latest => BlockId::Latest.into(), + BlockNumber::Pending => { + let info = self.client.chain_info(); + + self.miner .pending_state(info.best_block_number) - .map(|s| Box::new(s) as Box) - .unwrap_or(Box::new(self.client.latest_state()) as Box) + .map(|s| Box::new(s) as Box) + .unwrap_or_else(|| { + warn!("Asked for best pending state, but none found. Falling back to latest state"); + let (state, _) = self.client.latest_state_and_header(); + Box::new(state) as Box + }) .into() - } - } - } + } + } + } + + /// Get the state and header of best pending block. On failure, fall back to the best imported + /// blocks state&header. + fn pending_state_and_header_with_fallback(&self) -> (T, Header) { + let best_block_number = self.client.chain_info().best_block_number; + let (maybe_state, maybe_header) = self.miner.pending_state(best_block_number).map_or_else( + || (None, None), + |s| (Some(s), self.miner.pending_block_header(best_block_number)), + ); + + match (maybe_state, maybe_header) { + (Some(state), Some(header)) => (state, header), + _ => { + warn!("Falling back to \"Latest\""); + self.client.latest_state_and_header() + } + } + } } -pub fn pending_logs(miner: &M, best_block: EthBlockNumber, filter: &EthcoreFilter) -> Vec where M: MinerService { - let receipts = miner.pending_receipts(best_block).unwrap_or_default(); - - receipts.into_iter() - .flat_map(|r| { - let hash = r.transaction_hash; - r.logs.into_iter().map(move |l| (hash, l)) - }) - .filter(|pair| filter.matches(&pair.1)) - .map(|pair| { - let mut log = Log::from(pair.1); - log.transaction_hash = Some(pair.0); - log - }) - .collect() +pub fn pending_logs(miner: &M, best_block: EthBlockNumber, filter: &EthcoreFilter) -> Vec +where + M: MinerService, +{ + let receipts = miner.pending_receipts(best_block).unwrap_or_default(); + + receipts + .into_iter() + .flat_map(|r| { + let hash = r.transaction_hash; + r.logs.into_iter().map(move |l| (hash, l)) + }) + .filter(|pair| filter.matches(&pair.1)) + .map(|pair| { + let mut log = Log::from(pair.1); + log.transaction_hash = Some(pair.0); + log + }) + .collect() } -fn check_known(client: &C, number: BlockNumber) -> Result<()> where C: BlockChainClient { - use types::block_status::BlockStatus; - - let id = match number { - BlockNumber::Pending => return Ok(()), - - BlockNumber::Num(n) => BlockId::Number(n), - BlockNumber::Latest => BlockId::Latest, - BlockNumber::Earliest => BlockId::Earliest, - }; - - match client.block_status(id) { - BlockStatus::InChain => Ok(()), - _ => Err(errors::unknown_block()), - } +fn check_known(client: &C, number: BlockNumber) -> Result<()> +where + C: BlockChainClient, +{ + use types::block_status::BlockStatus; + + let id = match number { + BlockNumber::Pending => return Ok(()), + BlockNumber::Num(n) => BlockId::Number(n), + BlockNumber::Latest => BlockId::Latest, + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Hash { + hash, + require_canonical, + } => { + // block check takes precedence over canon check. + match client.block_status(BlockId::Hash(hash.clone())) { + BlockStatus::InChain => {} + _ => return Err(errors::unknown_block()), + }; + + // TODO Check block hash canonical + //if require_canonical && !client.chain().is_canon(&hash) { + // return Err(errors::invalid_input()); + //} + + return Ok(()); + } + }; + + match client.block_status(id) { + BlockStatus::InChain => Ok(()), + _ => Err(errors::unknown_block()), + } } -const MAX_QUEUE_SIZE_TO_MINE_ON: usize = 4; // because uncles go back 6. - -impl Eth for EthClient where - C: miner::BlockChainClient + StateClient + ProvingBlockChainClient + Call + EngineInfo + 'static, - SN: SnapshotService + 'static, - S: SyncProvider + 'static, - M: MinerService + 'static, - EM: ExternalMinerService + 'static, +const MAX_QUEUE_SIZE_TO_MINE_ON: usize = 4; // because uncles go back 6. + +impl Eth for EthClient +where + C: miner::BlockChainClient + + StateClient + + ProvingBlockChainClient + + Call + + EngineInfo + + 'static, + SN: SnapshotService + 'static, + S: SyncProvider + 'static, + M: MinerService + 'static, + EM: ExternalMinerService + 'static, { - type Metadata = Metadata; - - fn protocol_version(&self) -> Result { - let version = self.sync.status().protocol_version.to_owned(); - Ok(format!("{}", version)) - } - - fn syncing(&self) -> Result { - use ethcore::snapshot::RestorationStatus; - - let status = self.sync.status(); - let client = &self.client; - let snapshot_status = self.snapshot.status(); - - let (warping, warp_chunks_amount, warp_chunks_processed) = match snapshot_status { - RestorationStatus::Ongoing { state_chunks, block_chunks, state_chunks_done, block_chunks_done } => - (true, Some(block_chunks + state_chunks), Some(block_chunks_done + state_chunks_done)), - _ => (false, None, None), - }; - - if warping || is_major_importing(Some(status.state), client.queue_info()) { - let chain_info = client.chain_info(); - let current_block = U256::from(chain_info.best_block_number); - let highest_block = U256::from(status.highest_block_number.unwrap_or(status.start_block_number)); - - let info = SyncInfo { - starting_block: status.start_block_number.into(), - current_block, - highest_block, - warp_chunks_amount: warp_chunks_amount.map(|x| U256::from(x as u64)).map(Into::into), - warp_chunks_processed: warp_chunks_processed.map(|x| U256::from(x as u64)).map(Into::into), - }; - Ok(SyncStatus::Info(info)) - } else { - Ok(SyncStatus::None) - } - } - - fn author(&self) -> Result { - let miner = self.miner.authoring_params().author; - if miner == 0.into() { - (self.accounts)() - .first() - .cloned() - .ok_or_else(|| errors::account("No accounts were found", "")) - } else { - Ok(miner) - } - } - - fn is_mining(&self) -> Result { - Ok(self.miner.is_currently_sealing()) - } - - fn chain_id(&self) -> Result> { - Ok(self.client.signing_chain_id().map(U64::from)) - } - - fn hashrate(&self) -> Result { - Ok(self.external_miner.hashrate()) - } - - fn gas_price(&self) -> BoxFuture { - Box::new(future::ok(default_gas_price(&*self.client, &*self.miner, self.options.gas_price_percentile))) - } - - fn accounts(&self) -> Result> { - self.deprecation_notice.print("eth_accounts", deprecated::msgs::ACCOUNTS); - - let accounts = (self.accounts)(); - Ok(accounts) - } - - fn block_number(&self) -> Result { - Ok(U256::from(self.client.chain_info().best_block_number)) - } - - fn balance(&self, address: H160, num: Option) -> BoxFuture { - let num = num.unwrap_or_default(); - - try_bf!(check_known(&*self.client, num.clone())); - let res = match self.client.balance(&address, self.get_state(num)) { - Some(balance) => Ok(balance), - None => Err(errors::state_pruned()), - }; - - Box::new(future::done(res)) - } - - fn proof(&self, address: H160, values: Vec, num: Option) -> BoxFuture { - try_bf!(errors::require_experimental(self.options.allow_experimental_rpcs, "1186")); - - let key1 = keccak(address); - - let num = num.unwrap_or_default(); - let id = match num { - BlockNumber::Num(n) => BlockId::Number(n), - BlockNumber::Earliest => BlockId::Earliest, - BlockNumber::Latest => BlockId::Latest, - BlockNumber::Pending => { - self.deprecation_notice.print("`Pending`", Some("falling back to `Latest`")); - BlockId::Latest - } - }; - - try_bf!(check_known(&*self.client, num.clone())); - let res = match self.client.prove_account(key1, id) { - Some((proof, account)) => Ok(EthAccount { - address, - balance: account.balance, - nonce: account.nonce, - code_hash: account.code_hash, - storage_hash: account.storage_root, - account_proof: proof.into_iter().map(Bytes::new).collect(), - storage_proof: values.into_iter().filter_map(|storage_index| { - let key2: H256 = storage_index; - self.client.prove_storage(key1, keccak(key2), id) - .map(|(storage_proof, storage_value)| StorageProof { - key: key2.into(), - value: storage_value.into(), - proof: storage_proof.into_iter().map(Bytes::new).collect() - }) - }) - .collect::>() - }), - None => Err(errors::state_pruned()), - }; - - Box::new(future::done(res)) - } - - fn storage_at(&self, address: H160, position: U256, num: Option) -> BoxFuture { - let num = num.unwrap_or_default(); - - try_bf!(check_known(&*self.client, num.clone())); - let res = match self.client.storage_at(&address, &H256::from(position), self.get_state(num)) { - Some(s) => Ok(s), - None => Err(errors::state_pruned()), - }; - - Box::new(future::done(res)) - } - - fn transaction_count(&self, address: H160, num: Option) -> BoxFuture { - let res = match num.unwrap_or_default() { - BlockNumber::Pending if self.options.pending_nonce_from_queue => { - Ok(self.miner.next_nonce(&*self.client, &address)) - } - BlockNumber::Pending => { - let info = self.client.chain_info(); - let nonce = self.miner - .pending_state(info.best_block_number) - .and_then(|s| s.nonce(&address).ok()) - .or_else(|| { - warn!("Fallback to `BlockId::Latest`"); - self.client.nonce(&address, BlockId::Latest) - }); - - match nonce { - Some(nonce) => Ok(nonce), - None => Err(errors::database("latest nonce missing")) - } - }, - number => { - try_bf!(check_known(&*self.client, number.clone())); - match self.client.nonce(&address, block_number_to_id(number)) { - Some(nonce) => Ok(nonce), - None => Err(errors::state_pruned()), - } - } - }; - - Box::new(future::done(res)) - } - - fn block_transaction_count_by_hash(&self, hash: H256) -> BoxFuture> { - let trx_count = self.client.block(BlockId::Hash(hash)) - .map(|block| block.transactions_count().into()); - let result = Ok(trx_count) - .and_then(errors::check_block_gap(&*self.client, self.options.allow_missing_blocks)); - Box::new(future::done(result)) - } - - fn block_transaction_count_by_number(&self, num: BlockNumber) -> BoxFuture> { - Box::new(future::done(match num { - BlockNumber::Pending => - Ok(Some(self.miner.pending_transaction_hashes(&*self.client).len().into())), - _ => { - let trx_count = self.client.block(block_number_to_id(num.clone())) - .map(|block| block.transactions_count().into()); - Ok(trx_count) - .and_then(errors::check_block_number_existence( - &*self.client, - num, - self.options.allow_missing_blocks - )) - } - })) - } - - fn block_uncles_count_by_hash(&self, hash: H256) -> BoxFuture> { - let uncle_count = self.client.block(BlockId::Hash(hash)) - .map(|block| block.uncles_count().into()); - let result = Ok(uncle_count) - .and_then(errors::check_block_gap(&*self.client, self.options.allow_missing_blocks)); - Box::new(future::done(result)) - } - - fn block_uncles_count_by_number(&self, num: BlockNumber) -> BoxFuture> { - Box::new(future::done(match num { - BlockNumber::Pending => Ok(Some(0.into())), - _ => { - let uncles_count = self.client.block(block_number_to_id(num.clone())) - .map(|block| block.uncles_count().into()); - Ok(uncles_count) - .and_then(errors::check_block_number_existence( - &*self.client, - num, - self.options.allow_missing_blocks - )) - } - })) - } - - fn code_at(&self, address: H160, num: Option) -> BoxFuture { - let address: Address = H160::into(address); - - let num = num.unwrap_or_default(); - try_bf!(check_known(&*self.client, num.clone())); - - let res = match self.client.code(&address, self.get_state(num)) { - Some(code) => Ok(code.map_or_else(Bytes::default, Bytes::new)), - None => Err(errors::state_pruned()), - }; - - Box::new(future::done(res)) - } - - fn block_by_hash(&self, hash: H256, include_txs: bool) -> BoxFuture> { - let result = self.rich_block(BlockId::Hash(hash).into(), include_txs) - .and_then(errors::check_block_gap(&*self.client, self.options.allow_missing_blocks)); - Box::new(future::done(result)) - } - - fn block_by_number(&self, num: BlockNumber, include_txs: bool) -> BoxFuture> { - let result = self.rich_block(num.clone().into(), include_txs).and_then( - errors::check_block_number_existence(&*self.client, num, self.options.allow_missing_blocks)); - Box::new(future::done(result)) - } - - fn transaction_by_hash(&self, hash: H256) -> BoxFuture> { - let tx = try_bf!(self.transaction(PendingTransactionId::Hash(hash))).or_else(|| { - self.miner.transaction(&hash) - .map(|t| Transaction::from_pending(t.pending().clone())) - }); - let result = Ok(tx).and_then( - errors::check_block_gap(&*self.client, self.options.allow_missing_blocks)); - Box::new(future::done(result)) - } - - fn transaction_by_block_hash_and_index(&self, hash: H256, index: Index) -> BoxFuture> { - let id = PendingTransactionId::Location(PendingOrBlock::Block(BlockId::Hash(hash)), index.value()); - let result = self.transaction(id).and_then( - errors::check_block_gap(&*self.client, self.options.allow_missing_blocks)); - Box::new(future::done(result)) - } - - fn transaction_by_block_number_and_index(&self, num: BlockNumber, index: Index) -> BoxFuture> { - let block_id = match num { - BlockNumber::Latest => PendingOrBlock::Block(BlockId::Latest), - BlockNumber::Earliest => PendingOrBlock::Block(BlockId::Earliest), - BlockNumber::Num(num) => PendingOrBlock::Block(BlockId::Number(num)), - BlockNumber::Pending => PendingOrBlock::Pending, - }; - - let transaction_id = PendingTransactionId::Location(block_id, index.value()); - let result = self.transaction(transaction_id).and_then( - errors::check_block_number_existence(&*self.client, num, self.options.allow_missing_blocks)); - Box::new(future::done(result)) - } - - fn transaction_receipt(&self, hash: H256) -> BoxFuture> { - if self.options.allow_pending_receipt_query { - let best_block = self.client.chain_info().best_block_number; - if let Some(receipt) = self.miner.pending_receipt(best_block, &hash) { - return Box::new(future::ok(Some(receipt.into()))); - } - } - - let receipt = self.client.transaction_receipt(TransactionId::Hash(hash)); - let result = Ok(receipt.map(Into::into)) - .and_then(errors::check_block_gap(&*self.client, self.options.allow_missing_blocks)); - Box::new(future::done(result)) - } - - fn uncle_by_block_hash_and_index(&self, hash: H256, index: Index) -> BoxFuture> { - let result = self.uncle(PendingUncleId { - id: PendingOrBlock::Block(BlockId::Hash(hash)), - position: index.value() - }).and_then(errors::check_block_gap(&*self.client, self.options.allow_missing_blocks)); - Box::new(future::done(result)) - } - - fn uncle_by_block_number_and_index(&self, num: BlockNumber, index: Index) -> BoxFuture> { - let id = match num { - BlockNumber::Latest => PendingUncleId { id: PendingOrBlock::Block(BlockId::Latest), position: index.value() }, - BlockNumber::Earliest => PendingUncleId { id: PendingOrBlock::Block(BlockId::Earliest), position: index.value() }, - BlockNumber::Num(num) => PendingUncleId { id: PendingOrBlock::Block(BlockId::Number(num)), position: index.value() }, - - BlockNumber::Pending => PendingUncleId { id: PendingOrBlock::Pending, position: index.value() }, - }; - - let result = self.uncle(id) - .and_then(errors::check_block_number_existence( - &*self.client, - num, - self.options.allow_missing_blocks - )); - - Box::new(future::done(result)) - } - - fn compilers(&self) -> Result> { - Err(errors::deprecated("Compilation functionality is deprecated.".to_string())) - } - - fn logs(&self, filter: Filter) -> BoxFuture> { - base_logs(&*self.client, &*self.miner, filter) - } - - fn work(&self, no_new_work_timeout: Option) -> Result { - let no_new_work_timeout = no_new_work_timeout.unwrap_or_default(); - - // check if we're still syncing and return empty strings in that case - { - let sync_status = self.sync.status(); - let queue_info = self.client.queue_info(); - let total_queue_size = queue_info.total_queue_size(); - - if sync_status.is_snapshot_syncing() || total_queue_size > MAX_QUEUE_SIZE_TO_MINE_ON { - trace!(target: "miner", "Syncing. Cannot give any work."); - return Err(errors::no_work()); - } - - // Otherwise spin until our submitted block has been included. - let timeout = Instant::now() + Duration::from_millis(1000); - while Instant::now() < timeout && self.client.queue_info().total_queue_size() > 0 { - thread::sleep(Duration::from_millis(1)); - } - } - - if self.miner.authoring_params().author.is_zero() { - warn!(target: "miner", "Cannot give work package - no author is configured. Use --author to configure!"); - return Err(errors::no_author()) - } - - let work = self.miner.work_package(&*self.client).ok_or_else(|| { - warn!(target: "miner", "Cannot give work package - engine seals internally."); - errors::no_work_required() - })?; - - let (pow_hash, number, timestamp, difficulty) = work; - let target = ethash::difficulty_to_boundary(&difficulty); - let seed_hash = self.seed_compute.lock().hash_block_number(number); - - let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); - if no_new_work_timeout > 0 && timestamp + no_new_work_timeout < now { - Err(errors::no_new_work()) - } else if self.options.send_block_number_in_get_work { - Ok(Work { - pow_hash, - seed_hash: seed_hash.into(), - target, - number: Some(number), - }) - } else { - Ok(Work { - pow_hash, - seed_hash: seed_hash.into(), - target, - number: None - }) - } - } - - fn submit_work(&self, nonce: H64, pow_hash: H256, mix_hash: H256) -> Result { - match helpers::submit_work_detail(&self.client, &self.miner, nonce, pow_hash, mix_hash) { - Ok(_) => Ok(true), - Err(_) => Ok(false), - } - } - - fn submit_hashrate(&self, rate: U256, id: H256) -> Result { - self.external_miner.submit_hashrate(rate, id); - Ok(true) - } - - fn send_raw_transaction(&self, raw: Bytes) -> Result { - Rlp::new(&raw.into_vec()).as_val() - .map_err(errors::rlp) - .and_then(|tx| SignedTransaction::new(tx).map_err(errors::transaction)) - .and_then(|signed_transaction| { - FullDispatcher::dispatch_transaction( - &*self.client, - &*self.miner, - signed_transaction.into(), - false - ) - }) - .map(Into::into) - } - - fn submit_transaction(&self, raw: Bytes) -> Result { - self.send_raw_transaction(raw) - } - - fn call(&self, request: CallRequest, num: Option) -> BoxFuture { - let request = CallRequest::into(request); - let signed = try_bf!(fake_sign::sign_call(request)); - - let num = num.unwrap_or_default(); - - let (mut state, header) = if num == BlockNumber::Pending { - let info = self.client.chain_info(); - let state = try_bf!(self.miner.pending_state(info.best_block_number).ok_or_else(errors::state_pruned)); - let header = try_bf!(self.miner.pending_block_header(info.best_block_number).ok_or_else(errors::state_pruned)); - - (state, header) - } else { - let id = match num { - BlockNumber::Num(num) => BlockId::Number(num), - BlockNumber::Earliest => BlockId::Earliest, - BlockNumber::Latest => BlockId::Latest, - BlockNumber::Pending => unreachable!(), // Already covered - }; - - let state = try_bf!(self.client.state_at(id).ok_or_else(errors::state_pruned)); - let header = try_bf!(self.client.block_header(id).ok_or_else(errors::state_pruned).and_then(|h| h.decode().map_err(errors::decode))); - - (state, header) - }; - - let result = self.client.call(&signed, Default::default(), &mut state, &header); - - Box::new(future::done(result - .map_err(errors::call) - .and_then(|executed| { - match executed.exception { - Some(ref exception) => Err(errors::vm(exception, &executed.output)), - None => Ok(executed) - } - }) - .map(|b| b.output.into()) - )) - } - - fn estimate_gas(&self, request: CallRequest, num: Option) -> BoxFuture { - let request = CallRequest::into(request); - let signed = try_bf!(fake_sign::sign_call(request)); - let num = num.unwrap_or_default(); - - let (state, header) = if num == BlockNumber::Pending { - let info = self.client.chain_info(); - let state = try_bf!(self.miner.pending_state(info.best_block_number) - .ok_or_else(errors::state_pruned)); - let header = try_bf!(self.miner.pending_block_header(info.best_block_number) - .ok_or_else(errors::state_pruned)); - - (state, header) - } else { - let id = match num { - BlockNumber::Num(num) => BlockId::Number(num), - BlockNumber::Earliest => BlockId::Earliest, - BlockNumber::Latest => BlockId::Latest, - BlockNumber::Pending => unreachable!(), // Already covered - }; - - let state = try_bf!(self.client.state_at(id) - .ok_or_else(errors::state_pruned)); - let header = try_bf!(self.client.block_header(id) - .ok_or_else(errors::state_pruned) - .and_then(|h| h.decode().map_err(errors::decode))); - (state, header) - }; - - Box::new(future::done(self.client.estimate_gas(&signed, &state, &header) - .map_err(errors::call) - )) - } - - fn compile_lll(&self, _: String) -> Result { - Err(errors::deprecated("Compilation of LLL via RPC is deprecated".to_string())) - } - - fn compile_serpent(&self, _: String) -> Result { - Err(errors::deprecated("Compilation of Serpent via RPC is deprecated".to_string())) - } - - fn compile_solidity(&self, _: String) -> Result { - Err(errors::deprecated("Compilation of Solidity via RPC is deprecated".to_string())) - } + type Metadata = Metadata; + + fn protocol_version(&self) -> Result { + let version = self.sync.status().protocol_version.to_owned(); + Ok(format!("{}", version)) + } + + fn syncing(&self) -> Result { + use ethcore::snapshot::RestorationStatus; + + let status = self.sync.status(); + let client = &self.client; + let snapshot_status = self.snapshot.restoration_status(); + + let (warping, warp_chunks_amount, warp_chunks_processed) = match snapshot_status { + RestorationStatus::Ongoing { + state_chunks, + block_chunks, + state_chunks_done, + block_chunks_done, + .. + } => ( + true, + Some(block_chunks + state_chunks), + Some(block_chunks_done + state_chunks_done), + ), + _ => (false, None, None), + }; + + if warping || is_major_importing(Some(status.state), client.queue_info()) { + let chain_info = client.chain_info(); + let current_block = U256::from(chain_info.best_block_number); + let highest_block = U256::from( + status + .highest_block_number + .unwrap_or(status.start_block_number), + ); + + let info = SyncInfo { + starting_block: status.start_block_number.into(), + current_block, + highest_block, + warp_chunks_amount: warp_chunks_amount + .map(|x| U256::from(x as u64)) + .map(Into::into), + warp_chunks_processed: warp_chunks_processed + .map(|x| U256::from(x as u64)) + .map(Into::into), + }; + Ok(SyncStatus::Info(info)) + } else { + Ok(SyncStatus::None) + } + } + + fn author(&self) -> Result { + let miner = self.miner.authoring_params().author; + if miner == 0.into() { + (self.accounts)() + .first() + .cloned() + .ok_or_else(|| errors::account("No accounts were found", "")) + } else { + Ok(miner) + } + } + + fn is_mining(&self) -> Result { + Ok(self.miner.is_currently_sealing()) + } + + fn chain_id(&self) -> Result> { + Ok(self.client.signing_chain_id().map(U64::from)) + } + + fn hashrate(&self) -> Result { + Ok(self.external_miner.hashrate()) + } + + fn gas_price(&self) -> BoxFuture { + Box::new(future::ok(default_gas_price( + &*self.client, + &*self.miner, + self.options.gas_price_percentile, + ))) + } + + fn accounts(&self) -> Result> { + self.deprecation_notice + .print("eth_accounts", deprecated::msgs::ACCOUNTS); + + let accounts = (self.accounts)(); + Ok(accounts) + } + + fn block_number(&self) -> Result { + Ok(U256::from(self.client.chain_info().best_block_number)) + } + + fn balance(&self, address: H160, num: Option) -> BoxFuture { + let num = num.unwrap_or_default(); + + try_bf!(check_known(&*self.client, num.clone())); + let res = match self.client.balance(&address, self.get_state(num)) { + Some(balance) => Ok(balance), + None => Err(errors::state_pruned()), + }; + + Box::new(future::done(res)) + } + + fn proof( + &self, + address: H160, + values: Vec, + num: Option, + ) -> BoxFuture { + try_bf!(errors::require_experimental( + self.options.allow_experimental_rpcs, + "1186" + )); + + let key1 = keccak(address); + + let num = num.unwrap_or_default(); + let id = match num { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), + BlockNumber::Num(n) => BlockId::Number(n), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + BlockNumber::Pending => { + self.deprecation_notice + .print("`Pending`", Some("falling back to `Latest`")); + BlockId::Latest + } + }; + + try_bf!(check_known(&*self.client, num.clone())); + let res = match self.client.prove_account(key1, id) { + Some((proof, account)) => Ok(EthAccount { + address, + balance: account.balance, + nonce: account.nonce, + code_hash: account.code_hash, + storage_hash: account.storage_root, + account_proof: proof.into_iter().map(Bytes::new).collect(), + storage_proof: values + .into_iter() + .filter_map(|storage_index| { + let key2: H256 = storage_index; + self.client.prove_storage(key1, keccak(key2), id).map( + |(storage_proof, storage_value)| StorageProof { + key: key2.into(), + value: storage_value.into(), + proof: storage_proof.into_iter().map(Bytes::new).collect(), + }, + ) + }) + .collect::>(), + }), + None => Err(errors::state_pruned()), + }; + + Box::new(future::done(res)) + } + + fn storage_at( + &self, + address: H160, + position: U256, + num: Option, + ) -> BoxFuture { + let num = num.unwrap_or_default(); + + try_bf!(check_known(&*self.client, num.clone())); + let res = match self + .client + .storage_at(&address, &H256::from(position), self.get_state(num)) + { + Some(s) => Ok(s), + None => Err(errors::state_pruned()), + }; + + Box::new(future::done(res)) + } + + fn transaction_count(&self, address: H160, num: Option) -> BoxFuture { + let res = match num.unwrap_or_default() { + BlockNumber::Pending => { + let info = self.client.chain_info(); + let nonce = self + .miner + .pending_state(info.best_block_number) + .and_then(|s| s.nonce(&address).ok()) + .or_else(|| { + warn!("Fallback to `BlockId::Latest`"); + self.client.nonce(&address, BlockId::Latest) + }); + + match nonce { + Some(nonce) => Ok(nonce), + None => Err(errors::database("latest nonce missing")), + } + } + number => { + try_bf!(check_known(&*self.client, number.clone())); + match self.client.nonce(&address, block_number_to_id(number)) { + Some(nonce) => Ok(nonce), + None => Err(errors::state_pruned()), + } + } + }; + + Box::new(future::done(res)) + } + + fn block_transaction_count_by_hash(&self, hash: H256) -> BoxFuture> { + let trx_count = self + .client + .block(BlockId::Hash(hash)) + .map(|block| block.transactions_count().into()); + let result = Ok(trx_count).and_then(errors::check_block_gap(&*self.client, self.options)); + Box::new(future::done(result)) + } + + fn block_transaction_count_by_number(&self, num: BlockNumber) -> BoxFuture> { + Box::new(future::done(match num { + BlockNumber::Pending => Ok(Some( + self.miner + .pending_transaction_hashes(&*self.client) + .len() + .into(), + )), + _ => { + let trx_count = self + .client + .block(block_number_to_id(num.clone())) + .map(|block| block.transactions_count().into()); + Ok(trx_count).and_then(errors::check_block_number_existence( + &*self.client, + num, + self.options, + )) + } + })) + } + + fn block_uncles_count_by_hash(&self, hash: H256) -> BoxFuture> { + let uncle_count = self + .client + .block(BlockId::Hash(hash)) + .map(|block| block.uncles_count().into()); + let result = Ok(uncle_count).and_then(errors::check_block_gap(&*self.client, self.options)); + Box::new(future::done(result)) + } + + fn block_uncles_count_by_number(&self, num: BlockNumber) -> BoxFuture> { + Box::new(future::done(match num { + BlockNumber::Pending => Ok(Some(0.into())), + _ => { + let uncles_count = self + .client + .block(block_number_to_id(num.clone())) + .map(|block| block.uncles_count().into()); + Ok(uncles_count).and_then(errors::check_block_number_existence( + &*self.client, + num, + self.options, + )) + } + })) + } + + fn code_at(&self, address: H160, num: Option) -> BoxFuture { + let address: Address = H160::into(address); + + let num = num.unwrap_or_default(); + try_bf!(check_known(&*self.client, num.clone())); + + let res = match self.client.code(&address, self.get_state(num)) { + Some(code) => Ok(code.map_or_else(Bytes::default, Bytes::new)), + None => Err(errors::state_pruned()), + }; + + Box::new(future::done(res)) + } + + fn block_by_hash(&self, hash: H256, include_txs: bool) -> BoxFuture> { + let result = self + .rich_block(BlockId::Hash(hash).into(), include_txs) + .and_then(errors::check_block_gap(&*self.client, self.options)); + Box::new(future::done(result)) + } + + fn block_by_number(&self, num: BlockNumber, include_txs: bool) -> BoxFuture> { + let result = self.rich_block(num.clone().into(), include_txs).and_then( + errors::check_block_number_existence(&*self.client, num, self.options), + ); + Box::new(future::done(result)) + } + + fn transaction_by_hash(&self, hash: H256) -> BoxFuture> { + let tx = try_bf!(self.transaction(PendingTransactionId::Hash(hash))).or_else(|| { + self.miner + .transaction(&hash) + .map(|t| Transaction::from_pending(t.pending().clone())) + }); + let result = Ok(tx).and_then(errors::check_block_gap(&*self.client, self.options)); + Box::new(future::done(result)) + } + + fn transaction_by_block_hash_and_index( + &self, + hash: H256, + index: Index, + ) -> BoxFuture> { + let id = PendingTransactionId::Location( + PendingOrBlock::Block(BlockId::Hash(hash)), + index.value(), + ); + let result = self + .transaction(id) + .and_then(errors::check_block_gap(&*self.client, self.options)); + Box::new(future::done(result)) + } + + fn transaction_by_block_number_and_index( + &self, + num: BlockNumber, + index: Index, + ) -> BoxFuture> { + let block_id = match num { + BlockNumber::Hash { hash, .. } => PendingOrBlock::Block(BlockId::Hash(hash)), + BlockNumber::Latest => PendingOrBlock::Block(BlockId::Latest), + BlockNumber::Earliest => PendingOrBlock::Block(BlockId::Earliest), + BlockNumber::Num(num) => PendingOrBlock::Block(BlockId::Number(num)), + BlockNumber::Pending => PendingOrBlock::Pending, + }; + + let transaction_id = PendingTransactionId::Location(block_id, index.value()); + let result = + self.transaction(transaction_id) + .and_then(errors::check_block_number_existence( + &*self.client, + num, + self.options, + )); + Box::new(future::done(result)) + } + + fn transaction_receipt(&self, hash: H256) -> BoxFuture> { + let best_block = self.client.chain_info().best_block_number; + if let Some(receipt) = self.miner.pending_receipt(best_block, &hash) { + return Box::new(future::ok(Some(receipt.into()))); + } + + let receipt = self.client.transaction_receipt(TransactionId::Hash(hash)); + let result = Ok(receipt.map(Into::into)) + .and_then(errors::check_block_gap(&*self.client, self.options)); + Box::new(future::done(result)) + } + + fn uncle_by_block_hash_and_index( + &self, + hash: H256, + index: Index, + ) -> BoxFuture> { + let result = self + .uncle(PendingUncleId { + id: PendingOrBlock::Block(BlockId::Hash(hash)), + position: index.value(), + }) + .and_then(errors::check_block_gap(&*self.client, self.options)); + Box::new(future::done(result)) + } + + fn uncle_by_block_number_and_index( + &self, + num: BlockNumber, + index: Index, + ) -> BoxFuture> { + let id = match num { + BlockNumber::Hash { hash, .. } => PendingUncleId { + id: PendingOrBlock::Block(BlockId::Hash(hash)), + position: index.value(), + }, + BlockNumber::Latest => PendingUncleId { + id: PendingOrBlock::Block(BlockId::Latest), + position: index.value(), + }, + BlockNumber::Earliest => PendingUncleId { + id: PendingOrBlock::Block(BlockId::Earliest), + position: index.value(), + }, + BlockNumber::Num(num) => PendingUncleId { + id: PendingOrBlock::Block(BlockId::Number(num)), + position: index.value(), + }, + + BlockNumber::Pending => PendingUncleId { + id: PendingOrBlock::Pending, + position: index.value(), + }, + }; + + let result = self + .uncle(id) + .and_then(errors::check_block_number_existence( + &*self.client, + num, + self.options, + )); + + Box::new(future::done(result)) + } + + fn compilers(&self) -> Result> { + Err(errors::deprecated( + "Compilation functionality is deprecated.".to_string(), + )) + } + + fn logs(&self, filter: Filter) -> BoxFuture> { + let include_pending = filter.to_block == Some(BlockNumber::Pending); + let filter: EthcoreFilter = match filter.try_into() { + Ok(value) => value, + Err(err) => return Box::new(future::err(err)), + }; + let mut logs = match self.client.logs(filter.clone()) { + Ok(logs) => logs.into_iter().map(From::from).collect::>(), + Err(id) => return Box::new(future::err(errors::filter_block_not_found(id))), + }; + + if include_pending { + let best_block = self.client.chain_info().best_block_number; + let pending = pending_logs(&*self.miner, best_block, &filter); + logs.extend(pending); + } + + let logs = limit_logs(logs, filter.limit); + + Box::new(future::ok(logs)) + } + + fn work(&self, no_new_work_timeout: Option) -> Result { + let no_new_work_timeout = no_new_work_timeout.unwrap_or_default(); + + // check if we're still syncing and return empty strings in that case + { + let sync_status = self.sync.status(); + let queue_info = self.client.queue_info(); + let total_queue_size = queue_info.total_queue_size(); + + if sync_status.is_snapshot_syncing() || total_queue_size > MAX_QUEUE_SIZE_TO_MINE_ON { + trace!(target: "miner", "Syncing. Cannot give any work."); + return Err(errors::no_work()); + } + + // Otherwise spin until our submitted block has been included. + let timeout = Instant::now() + Duration::from_millis(1000); + while Instant::now() < timeout && self.client.queue_info().total_queue_size() > 0 { + thread::sleep(Duration::from_millis(1)); + } + } + + if self.miner.authoring_params().author.is_zero() { + warn!(target: "miner", "Cannot give work package - no author is configured. Use --author to configure!"); + return Err(errors::no_author()); + } + + let work = self.miner.work_package(&*self.client).ok_or_else(|| { + warn!(target: "miner", "Cannot give work package - engine seals internally."); + errors::no_work_required() + })?; + + let (pow_hash, number, timestamp, difficulty) = work; + let target = ethash::difficulty_to_boundary(&difficulty); + let seed_hash = self.seed_compute.lock().hash_block_number(number); + + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_secs(); + if no_new_work_timeout > 0 && timestamp + no_new_work_timeout < now { + Err(errors::no_new_work()) + } else { + Ok(Work { + pow_hash, + seed_hash: seed_hash.into(), + target, + number: Some(number), + }) + } + } + + fn submit_work(&self, nonce: H64, pow_hash: H256, mix_hash: H256) -> Result { + match helpers::submit_work_detail(&self.client, &self.miner, nonce, pow_hash, mix_hash) { + Ok(_) => Ok(true), + Err(_) => Ok(false), + } + } + + fn submit_hashrate(&self, rate: U256, id: H256) -> Result { + self.external_miner.submit_hashrate(rate, id); + Ok(true) + } + + fn send_raw_transaction(&self, raw: Bytes) -> Result { + Rlp::new(&raw.into_vec()) + .as_val() + .map_err(errors::rlp) + .and_then(|tx| SignedTransaction::new(tx).map_err(errors::transaction)) + .and_then(|signed_transaction| { + FullDispatcher::dispatch_transaction( + &*self.client, + &*self.miner, + signed_transaction.into(), + false, + ) + }) + .map(Into::into) + } + + fn submit_transaction(&self, raw: Bytes) -> Result { + self.send_raw_transaction(raw) + } + + fn call(&self, request: CallRequest, num: Option) -> BoxFuture { + let request = CallRequest::into(request); + let signed = try_bf!(fake_sign::sign_call(request)); + + let num = num.unwrap_or_default(); + + let (mut state, header) = if num == BlockNumber::Pending { + self.pending_state_and_header_with_fallback() + } else { + let id = match num { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), + BlockNumber::Num(num) => BlockId::Number(num), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + BlockNumber::Pending => unreachable!(), // Already covered + }; + + let state = try_bf!(self.client.state_at(id).ok_or_else(errors::state_pruned)); + let header = try_bf!(self + .client + .block_header(id) + .ok_or_else(errors::state_pruned) + .and_then(|h| h.decode().map_err(errors::decode))); + + (state, header) + }; + + let result = self + .client + .call(&signed, Default::default(), &mut state, &header); + + Box::new(future::done( + result + .map_err(errors::call) + .and_then(|executed| match executed.exception { + Some(ref exception) => Err(errors::vm(exception, &executed.output)), + None => Ok(executed), + }) + .map(|b| b.output.into()), + )) + } + + fn estimate_gas(&self, request: CallRequest, num: Option) -> BoxFuture { + let request = CallRequest::into(request); + let signed = try_bf!(fake_sign::sign_call(request)); + let num = num.unwrap_or_default(); + + let (state, header) = if num == BlockNumber::Pending { + self.pending_state_and_header_with_fallback() + } else { + let id = match num { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), + BlockNumber::Num(num) => BlockId::Number(num), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + BlockNumber::Pending => unreachable!(), // Already covered + }; + + let state = try_bf!(self.client.state_at(id).ok_or_else(errors::state_pruned)); + let header = try_bf!(self + .client + .block_header(id) + .ok_or_else(errors::state_pruned) + .and_then(|h| h.decode().map_err(errors::decode))); + (state, header) + }; + + Box::new(future::done( + self.client + .estimate_gas(&signed, &state, &header) + .map_err(errors::call), + )) + } + + fn compile_lll(&self, _: String) -> Result { + Err(errors::deprecated( + "Compilation of LLL via RPC is deprecated".to_string(), + )) + } + + fn compile_serpent(&self, _: String) -> Result { + Err(errors::deprecated( + "Compilation of Serpent via RPC is deprecated".to_string(), + )) + } + + fn compile_solidity(&self, _: String) -> Result { + Err(errors::deprecated( + "Compilation of Solidity via RPC is deprecated".to_string(), + )) + } } diff --git a/rpc/src/v1/impls/eth_filter.rs b/rpc/src/v1/impls/eth_filter.rs index c51c85fb629..417048a406a 100644 --- a/rpc/src/v1/impls/eth_filter.rs +++ b/rpc/src/v1/impls/eth_filter.rs @@ -1,312 +1,372 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Eth Filter RPC implementation -use std::sync::Arc; -use std::collections::{BTreeSet, VecDeque}; +use std::{ + collections::{BTreeSet, VecDeque}, + sync::Arc, +}; -use ethcore::client::{BlockChainClient, BlockId}; -use ethcore::miner::{self, MinerService}; +use ethcore::{ + client::{BlockChainClient, BlockId}, + miner::{self, MinerService}, +}; use ethereum_types::{H256, U256}; use parking_lot::Mutex; use types::filter::Filter as EthcoreFilter; -use jsonrpc_core::{BoxFuture, Result}; -use jsonrpc_core::futures::{future, Future}; -use jsonrpc_core::futures::future::Either; -use v1::traits::EthFilter; -use v1::types::{BlockNumber, Index, Filter, FilterChanges, Log}; -use v1::helpers::{errors, SyncPollFilter, PollFilter, PollManager, limit_logs}; -use v1::impls::eth::pending_logs; +use jsonrpc_core::{ + futures::{future, future::Either, Future}, + BoxFuture, Result, +}; +use v1::{ + helpers::{errors, limit_logs, PollFilter, PollManager, SyncPollFilter}, + impls::eth::pending_logs, + traits::EthFilter, + types::{BlockNumber, Filter, FilterChanges, Index, Log}, +}; /// Something which provides data that can be filtered over. pub trait Filterable { - /// Current best block number. - fn best_block_number(&self) -> u64; + /// Current best block number. + fn best_block_number(&self) -> u64; - /// Get a block hash by block id. - fn block_hash(&self, id: BlockId) -> Option; + /// Get a block hash by block id. + fn block_hash(&self, id: BlockId) -> Option; - /// pending transaction hashes at the given block (unordered). - fn pending_transaction_hashes(&self) -> BTreeSet; + /// pending transaction hashes at the given block (unordered). + fn pending_transaction_hashes(&self) -> BTreeSet; - /// Get logs that match the given filter. - fn logs(&self, filter: EthcoreFilter) -> BoxFuture>; + /// Get logs that match the given filter. + fn logs(&self, filter: EthcoreFilter) -> BoxFuture>; - /// Get logs from the pending block. - fn pending_logs(&self, block_number: u64, filter: &EthcoreFilter) -> Vec; + /// Get logs from the pending block. + fn pending_logs(&self, block_number: u64, filter: &EthcoreFilter) -> Vec; - /// Get a reference to the poll manager. - fn polls(&self) -> &Mutex>; + /// Get a reference to the poll manager. + fn polls(&self) -> &Mutex>; - /// Get removed logs within route from the given block to the nearest canon block, not including the canon block. Also returns how many logs have been traversed. - fn removed_logs(&self, block_hash: H256, filter: &EthcoreFilter) -> (Vec, u64); + /// Get removed logs within route from the given block to the nearest canon block, not including the canon block. Also returns how many logs have been traversed. + fn removed_logs(&self, block_hash: H256, filter: &EthcoreFilter) -> (Vec, u64); } /// Eth filter rpc implementation for a full node. pub struct EthFilterClient { - client: Arc, - miner: Arc, - polls: Mutex>, + client: Arc, + miner: Arc, + polls: Mutex>, } impl EthFilterClient { - /// Creates new Eth filter client. - pub fn new(client: Arc, miner: Arc, poll_lifetime: u32) -> Self { - EthFilterClient { - client, - miner, - polls: Mutex::new(PollManager::new(poll_lifetime)), - } - } + /// Creates new Eth filter client. + pub fn new(client: Arc, miner: Arc, poll_lifetime: u32) -> Self { + EthFilterClient { + client, + miner, + polls: Mutex::new(PollManager::new(poll_lifetime)), + } + } } -impl Filterable for EthFilterClient where - C: miner::BlockChainClient + BlockChainClient, - M: MinerService, +impl Filterable for EthFilterClient +where + C: miner::BlockChainClient + BlockChainClient, + M: MinerService, { - fn best_block_number(&self) -> u64 { - self.client.chain_info().best_block_number - } - - fn block_hash(&self, id: BlockId) -> Option { - self.client.block_hash(id) - } - - fn pending_transaction_hashes(&self) -> BTreeSet { - self.miner.pending_transaction_hashes(&*self.client) - } - - fn logs(&self, filter: EthcoreFilter) -> BoxFuture> { - Box::new(future::ok(self.client.logs(filter).unwrap_or_default().into_iter().map(Into::into).collect())) - } - - fn pending_logs(&self, block_number: u64, filter: &EthcoreFilter) -> Vec { - pending_logs(&*self.miner, block_number, filter) - } - - fn polls(&self) -> &Mutex> { &self.polls } - - fn removed_logs(&self, block_hash: H256, filter: &EthcoreFilter) -> (Vec, u64) { - let inner = || -> Option> { - let mut route = Vec::new(); - - let mut current_block_hash = block_hash; - let mut current_block_header = self.client.block_header(BlockId::Hash(current_block_hash))?; - - while current_block_hash != self.client.block_hash(BlockId::Number(current_block_header.number()))? { - route.push(current_block_hash); - - current_block_hash = current_block_header.parent_hash(); - current_block_header = self.client.block_header(BlockId::Hash(current_block_hash))?; - } - - Some(route) - }; - - let route = inner().unwrap_or_default(); - let route_len = route.len() as u64; - (route.into_iter().flat_map(|block_hash| { - let mut filter = filter.clone(); - filter.from_block = BlockId::Hash(block_hash); - filter.to_block = filter.from_block; - - self.client.logs(filter).unwrap_or_default().into_iter().map(|log| { - let mut log: Log = log.into(); - log.log_type = "removed".into(); - log.removed = true; - - log - }) - }).collect(), route_len) - } + fn best_block_number(&self) -> u64 { + self.client.chain_info().best_block_number + } + + fn block_hash(&self, id: BlockId) -> Option { + self.client.block_hash(id) + } + + fn pending_transaction_hashes(&self) -> BTreeSet { + self.miner.pending_transaction_hashes(&*self.client) + } + + fn logs(&self, filter: EthcoreFilter) -> BoxFuture> { + Box::new(future::ok( + self.client + .logs(filter) + .unwrap_or_default() + .into_iter() + .map(Into::into) + .collect(), + )) + } + + fn pending_logs(&self, block_number: u64, filter: &EthcoreFilter) -> Vec { + pending_logs(&*self.miner, block_number, filter) + } + + fn polls(&self) -> &Mutex> { + &self.polls + } + + fn removed_logs(&self, block_hash: H256, filter: &EthcoreFilter) -> (Vec, u64) { + let inner = || -> Option> { + let mut route = Vec::new(); + + let mut current_block_hash = block_hash; + let mut current_block_header = self + .client + .block_header(BlockId::Hash(current_block_hash))?; + + while current_block_hash + != self + .client + .block_hash(BlockId::Number(current_block_header.number()))? + { + route.push(current_block_hash); + + current_block_hash = current_block_header.parent_hash(); + current_block_header = self + .client + .block_header(BlockId::Hash(current_block_hash))?; + } + + Some(route) + }; + + let route = inner().unwrap_or_default(); + let route_len = route.len() as u64; + ( + route + .into_iter() + .flat_map(|block_hash| { + let mut filter = filter.clone(); + filter.from_block = BlockId::Hash(block_hash); + filter.to_block = filter.from_block; + + self.client + .logs(filter) + .unwrap_or_default() + .into_iter() + .map(|log| { + let mut log: Log = log.into(); + log.log_type = "removed".into(); + log.removed = true; + + log + }) + }) + .collect(), + route_len, + ) + } } impl EthFilter for T { - fn new_filter(&self, filter: Filter) -> Result { - let mut polls = self.polls().lock(); - let block_number = self.best_block_number(); - let include_pending = filter.to_block == Some(BlockNumber::Pending); - let filter = filter.try_into()?; - let id = polls.create_poll(SyncPollFilter::new(PollFilter::Logs { - block_number, filter, include_pending, - last_block_hash: None, - previous_logs: Default::default() - })); - Ok(id.into()) - } - - fn new_block_filter(&self) -> Result { - let mut polls = self.polls().lock(); - // +1, since we don't want to include the current block - let id = polls.create_poll(SyncPollFilter::new(PollFilter::Block { - last_block_number: self.best_block_number(), - recent_reported_hashes: VecDeque::with_capacity(PollFilter::MAX_BLOCK_HISTORY_SIZE), - })); - Ok(id.into()) - } - - fn new_pending_transaction_filter(&self) -> Result { - let mut polls = self.polls().lock(); - let pending_transactions = self.pending_transaction_hashes(); - let id = polls.create_poll(SyncPollFilter::new(PollFilter::PendingTransaction(pending_transactions))); - Ok(id.into()) - } - - fn filter_changes(&self, index: Index) -> BoxFuture { - let filter = match self.polls().lock().poll_mut(&index.value()) { - Some(filter) => filter.clone(), - None => return Box::new(future::err(errors::filter_not_found())), - }; - - Box::new(filter.modify(|filter| match *filter { - PollFilter::Block { - ref mut last_block_number, - ref mut recent_reported_hashes, - } => { - // Check validity of recently reported blocks -- in case of re-org, rewind block to last valid - while let Some((num, hash)) = recent_reported_hashes.front().cloned() { - if self.block_hash(BlockId::Number(num)) == Some(hash) { break; } - *last_block_number = num - 1; - recent_reported_hashes.pop_front(); - } - let current_number = self.best_block_number(); - let mut hashes = Vec::new(); - for n in (*last_block_number + 1)..=current_number { - let block_number = BlockId::Number(n); - if let Some(hash) = self.block_hash(block_number) { - *last_block_number = n; - hashes.push(hash); - // Only keep the most recent history - if recent_reported_hashes.len() >= PollFilter::MAX_BLOCK_HISTORY_SIZE { - recent_reported_hashes.pop_back(); - } - recent_reported_hashes.push_front((n, hash)); - } - } - - Either::A(future::ok(FilterChanges::Hashes(hashes))) - }, - PollFilter::PendingTransaction(ref mut previous_hashes) => { - // get hashes of pending transactions - let current_hashes = self.pending_transaction_hashes(); - - let new_hashes = { - // find all new hashes - current_hashes.difference(previous_hashes) - .cloned() - .map(Into::into) - .collect() - }; - - // save all hashes of pending transactions - *previous_hashes = current_hashes; - - // return new hashes - Either::A(future::ok(FilterChanges::Hashes(new_hashes))) - }, - PollFilter::Logs { - ref mut block_number, - ref mut last_block_hash, - ref mut previous_logs, - ref filter, - include_pending, - } => { - // retrive the current block number - let current_number = self.best_block_number(); - - let mut filter = filter.clone(); - - // retrieve reorg logs - let (mut reorg, reorg_len) = last_block_hash.map_or_else(|| (Vec::new(), 0), |h| self.removed_logs(h, &filter)); - *block_number -= reorg_len as u64; - - filter.from_block = BlockId::Number(*block_number); - filter.to_block = BlockId::Latest; - - // retrieve pending logs - let pending = if include_pending { - let pending_logs = self.pending_logs(current_number, &filter); - - // remove logs about which client was already notified about - let new_pending_logs: Vec<_> = pending_logs.iter() - .filter(|p| !previous_logs.contains(p)) - .cloned() - .collect(); - - // save all logs retrieved by client - *previous_logs = pending_logs.into_iter().collect(); - - new_pending_logs - } else { - Vec::new() - }; - - // save the number of the next block as a first block from which - // we want to get logs - *block_number = current_number + 1; - - // save the current block hash, which we used to get back to the - // canon chain in case of reorg. - *last_block_hash = self.block_hash(BlockId::Number(current_number)); - - // retrieve logs in range from_block..min(BlockId::Latest..to_block) - let limit = filter.limit; - Either::B(self.logs(filter) - .map(move |logs| { reorg.extend(logs); reorg }) // append reorg logs in the front - .map(move |mut logs| { logs.extend(pending); logs }) // append fetched pending logs - .map(move |logs| limit_logs(logs, limit)) // limit the logs - .map(FilterChanges::Logs)) - } - })) - } - - fn filter_logs(&self, index: Index) -> BoxFuture> { - let (filter, include_pending) = { - let mut polls = self.polls().lock(); - - match polls.poll(&index.value()).and_then(|f| f.modify(|filter| match *filter { - PollFilter::Logs { ref filter, include_pending, .. } => - Some((filter.clone(), include_pending)), - _ => None, - })) { - Some((filter, include_pending)) => (filter, include_pending), - None => return Box::new(future::err(errors::filter_not_found())), - } - }; - - // fetch pending logs. - let pending = if include_pending { - let best_block = self.best_block_number(); - self.pending_logs(best_block, &filter) - } else { - Vec::new() - }; - - // retrieve logs asynchronously, appending pending logs. - let limit = filter.limit; - let logs = self.logs(filter); - Box::new(logs - .map(move |mut logs| { logs.extend(pending); logs }) - .map(move |logs| limit_logs(logs, limit)) - ) - } - - fn uninstall_filter(&self, index: Index) -> Result { - Ok(self.polls().lock().remove_poll(&index.value())) - } + fn new_filter(&self, filter: Filter) -> Result { + let mut polls = self.polls().lock(); + let block_number = self.best_block_number(); + let include_pending = filter.to_block == Some(BlockNumber::Pending); + let filter = filter.try_into()?; + let id = polls.create_poll(SyncPollFilter::new(PollFilter::Logs { + block_number, + filter, + include_pending, + last_block_hash: None, + previous_logs: Default::default(), + })); + Ok(id.into()) + } + + fn new_block_filter(&self) -> Result { + let mut polls = self.polls().lock(); + // +1, since we don't want to include the current block + let id = polls.create_poll(SyncPollFilter::new(PollFilter::Block { + last_block_number: self.best_block_number(), + recent_reported_hashes: VecDeque::with_capacity(PollFilter::MAX_BLOCK_HISTORY_SIZE), + })); + Ok(id.into()) + } + + fn new_pending_transaction_filter(&self) -> Result { + let mut polls = self.polls().lock(); + let pending_transactions = self.pending_transaction_hashes(); + let id = polls.create_poll(SyncPollFilter::new(PollFilter::PendingTransaction( + pending_transactions, + ))); + Ok(id.into()) + } + + fn filter_changes(&self, index: Index) -> BoxFuture { + let filter = match self.polls().lock().poll_mut(&index.value()) { + Some(filter) => filter.clone(), + None => return Box::new(future::err(errors::filter_not_found())), + }; + + Box::new(filter.modify(|filter| match *filter { + PollFilter::Block { + ref mut last_block_number, + ref mut recent_reported_hashes, + } => { + // Check validity of recently reported blocks -- in case of re-org, rewind block to last valid + while let Some((num, hash)) = recent_reported_hashes.front().cloned() { + if self.block_hash(BlockId::Number(num)) == Some(hash) { + break; + } + *last_block_number = num - 1; + recent_reported_hashes.pop_front(); + } + let current_number = self.best_block_number(); + let mut hashes = Vec::new(); + for n in (*last_block_number + 1)..=current_number { + let block_number = BlockId::Number(n); + if let Some(hash) = self.block_hash(block_number) { + *last_block_number = n; + hashes.push(hash); + // Only keep the most recent history + if recent_reported_hashes.len() >= PollFilter::MAX_BLOCK_HISTORY_SIZE { + recent_reported_hashes.pop_back(); + } + recent_reported_hashes.push_front((n, hash)); + } + } + + Either::A(future::ok(FilterChanges::Hashes(hashes))) + } + PollFilter::PendingTransaction(ref mut previous_hashes) => { + // get hashes of pending transactions + let current_hashes = self.pending_transaction_hashes(); + + let new_hashes = { + // find all new hashes + current_hashes + .difference(previous_hashes) + .cloned() + .map(Into::into) + .collect() + }; + + // save all hashes of pending transactions + *previous_hashes = current_hashes; + + // return new hashes + Either::A(future::ok(FilterChanges::Hashes(new_hashes))) + } + PollFilter::Logs { + ref mut block_number, + ref mut last_block_hash, + ref mut previous_logs, + ref filter, + include_pending, + } => { + // retrive the current block number + let current_number = self.best_block_number(); + + let mut filter = filter.clone(); + + // retrieve reorg logs + let (mut reorg, reorg_len) = last_block_hash + .map_or_else(|| (Vec::new(), 0), |h| self.removed_logs(h, &filter)); + *block_number -= reorg_len as u64; + + filter.from_block = BlockId::Number(*block_number); + filter.to_block = BlockId::Latest; + + // retrieve pending logs + let pending = if include_pending { + let pending_logs = self.pending_logs(current_number, &filter); + + // remove logs about which client was already notified about + let new_pending_logs: Vec<_> = pending_logs + .iter() + .filter(|p| !previous_logs.contains(p)) + .cloned() + .collect(); + + // save all logs retrieved by client + *previous_logs = pending_logs.into_iter().collect(); + + new_pending_logs + } else { + Vec::new() + }; + + // save the number of the next block as a first block from which + // we want to get logs + *block_number = current_number + 1; + + // save the current block hash, which we used to get back to the + // canon chain in case of reorg. + *last_block_hash = self.block_hash(BlockId::Number(current_number)); + + // retrieve logs in range from_block..min(BlockId::Latest..to_block) + let limit = filter.limit; + Either::B( + self.logs(filter) + .map(move |logs| { + reorg.extend(logs); + reorg + }) // append reorg logs in the front + .map(move |mut logs| { + logs.extend(pending); + logs + }) // append fetched pending logs + .map(move |logs| limit_logs(logs, limit)) // limit the logs + .map(FilterChanges::Logs), + ) + } + })) + } + + fn filter_logs(&self, index: Index) -> BoxFuture> { + let (filter, include_pending) = { + let mut polls = self.polls().lock(); + + match polls.poll(&index.value()).and_then(|f| { + f.modify(|filter| match *filter { + PollFilter::Logs { + ref filter, + include_pending, + .. + } => Some((filter.clone(), include_pending)), + _ => None, + }) + }) { + Some((filter, include_pending)) => (filter, include_pending), + None => return Box::new(future::err(errors::filter_not_found())), + } + }; + + // fetch pending logs. + let pending = if include_pending { + let best_block = self.best_block_number(); + self.pending_logs(best_block, &filter) + } else { + Vec::new() + }; + + // retrieve logs asynchronously, appending pending logs. + let limit = filter.limit; + let logs = self.logs(filter); + Box::new( + logs.map(move |mut logs| { + logs.extend(pending); + logs + }) + .map(move |logs| limit_logs(logs, limit)), + ) + } + + fn uninstall_filter(&self, index: Index) -> Result { + Ok(self.polls().lock().remove_poll(&index.value())) + } } diff --git a/rpc/src/v1/impls/eth_pubsub.rs b/rpc/src/v1/impls/eth_pubsub.rs index 45072815780..e5d0deefb83 100644 --- a/rpc/src/v1/impls/eth_pubsub.rs +++ b/rpc/src/v1/impls/eth_pubsub.rs @@ -1,314 +1,276 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Eth PUB-SUB rpc implementation. -use std::sync::{Arc, Weak}; -use std::collections::BTreeMap; - -use jsonrpc_core::{BoxFuture, Result, Error}; -use jsonrpc_core::futures::{self, Future, IntoFuture}; -use jsonrpc_pubsub::{SubscriptionId, typed::{Sink, Subscriber}}; - -use v1::helpers::{errors, limit_logs, Subscribers}; -use v1::helpers::light_fetch::LightFetch; -use v1::metadata::Metadata; -use v1::traits::EthPubSub; -use v1::types::{pubsub, RichHeader, Log}; - -use ethcore::client::{BlockChainClient, ChainNotify, NewBlocks, ChainRouteType, BlockId}; +use std::{ + collections::BTreeMap, + sync::{Arc, Weak}, +}; + +use jsonrpc_core::{ + futures::{self, Future, IntoFuture}, + Error, Result, +}; +use jsonrpc_pubsub::{ + typed::{Sink, Subscriber}, + SubscriptionId, +}; + +use v1::{ + helpers::{errors, limit_logs, Subscribers}, + metadata::Metadata, + traits::EthPubSub, + types::{pubsub, Log, RichHeader}, +}; + +use ethcore::client::{BlockChainClient, BlockId, ChainNotify, ChainRouteType, NewBlocks}; use ethereum_types::H256; -use light::cache::Cache; -use light::client::{LightChainClient, LightChainNotify}; -use light::on_demand::OnDemandRequester; use parity_runtime::Executor; -use parking_lot::{RwLock, Mutex}; - -use sync::{LightSyncProvider, LightNetworkDispatcher, ManageNetwork}; +use parking_lot::RwLock; -use types::encoded; -use types::filter::Filter as EthFilter; +use types::{encoded, filter::Filter as EthFilter}; type Client = Sink; /// Eth PubSub implementation. pub struct EthPubSubClient { - handler: Arc>, - heads_subscribers: Arc>>, - logs_subscribers: Arc>>, - transactions_subscribers: Arc>>, + handler: Arc>, + heads_subscribers: Arc>>, + logs_subscribers: Arc>>, + transactions_subscribers: Arc>>, } impl EthPubSubClient { - /// Creates new `EthPubSubClient`. - pub fn new(client: Arc, executor: Executor) -> Self { - let heads_subscribers = Arc::new(RwLock::new(Subscribers::default())); - let logs_subscribers = Arc::new(RwLock::new(Subscribers::default())); - let transactions_subscribers = Arc::new(RwLock::new(Subscribers::default())); - - EthPubSubClient { - handler: Arc::new(ChainNotificationHandler { - client, - executor, - heads_subscribers: heads_subscribers.clone(), - logs_subscribers: logs_subscribers.clone(), - transactions_subscribers: transactions_subscribers.clone(), - }), - heads_subscribers, - logs_subscribers, - transactions_subscribers, - } - } - - /// Creates new `EthPubSubCient` with deterministic subscription ids. - #[cfg(test)] - pub fn new_test(client: Arc, executor: Executor) -> Self { - let client = Self::new(client, executor); - *client.heads_subscribers.write() = Subscribers::new_test(); - *client.logs_subscribers.write() = Subscribers::new_test(); - *client.transactions_subscribers.write() = Subscribers::new_test(); - client - } - - /// Returns a chain notification handler. - pub fn handler(&self) -> Weak> { - Arc::downgrade(&self.handler) - } -} - -impl EthPubSubClient> -where - S: LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, - OD: OnDemandRequester + 'static -{ - /// Creates a new `EthPubSubClient` for `LightClient`. - pub fn light( - client: Arc, - on_demand: Arc, - sync: Arc, - cache: Arc>, - executor: Executor, - gas_price_percentile: usize, - ) -> Self { - let fetch = LightFetch { - client, - on_demand, - sync, - cache, - gas_price_percentile, - }; - EthPubSubClient::new(Arc::new(fetch), executor) - } + /// Creates new `EthPubSubClient`. + pub fn new(client: Arc, executor: Executor) -> Self { + let heads_subscribers = Arc::new(RwLock::new(Subscribers::default())); + let logs_subscribers = Arc::new(RwLock::new(Subscribers::default())); + let transactions_subscribers = Arc::new(RwLock::new(Subscribers::default())); + + EthPubSubClient { + handler: Arc::new(ChainNotificationHandler { + client, + executor, + heads_subscribers: heads_subscribers.clone(), + logs_subscribers: logs_subscribers.clone(), + transactions_subscribers: transactions_subscribers.clone(), + }), + heads_subscribers, + logs_subscribers, + transactions_subscribers, + } + } + + /// Creates new `EthPubSubCient` with deterministic subscription ids. + #[cfg(test)] + pub fn new_test(client: Arc, executor: Executor) -> Self { + let client = Self::new(client, executor); + *client.heads_subscribers.write() = Subscribers::new_test(); + *client.logs_subscribers.write() = Subscribers::new_test(); + *client.transactions_subscribers.write() = Subscribers::new_test(); + client + } + + /// Returns a chain notification handler. + pub fn handler(&self) -> Weak> { + Arc::downgrade(&self.handler) + } } /// PubSub Notification handler. pub struct ChainNotificationHandler { - client: Arc, - executor: Executor, - heads_subscribers: Arc>>, - logs_subscribers: Arc>>, - transactions_subscribers: Arc>>, + client: Arc, + executor: Executor, + heads_subscribers: Arc>>, + logs_subscribers: Arc>>, + transactions_subscribers: Arc>>, } impl ChainNotificationHandler { - fn notify(executor: &Executor, subscriber: &Client, result: pubsub::Result) { - executor.spawn(subscriber - .notify(Ok(result)) - .map(|_| ()) - .map_err(|e| warn!(target: "rpc", "Unable to send notification: {}", e)) - ); - } - - fn notify_heads(&self, headers: &[(encoded::Header, BTreeMap)]) { - for subscriber in self.heads_subscribers.read().values() { - for &(ref header, ref extra_info) in headers { - Self::notify(&self.executor, subscriber, pubsub::Result::Header(Box::new(RichHeader { - inner: header.into(), - extra_info: extra_info.clone(), - }))); - } - } - } - - fn notify_logs(&self, enacted: &[(H256, Ex)], logs: F) where - F: Fn(EthFilter, &Ex) -> T, - Ex: Send, - T: IntoFuture, Error = Error>, - T::Future: Send + 'static, - { - for &(ref subscriber, ref filter) in self.logs_subscribers.read().values() { - let logs = futures::future::join_all(enacted - .iter() - .map(|&(hash, ref ex)| { - let mut filter = filter.clone(); - filter.from_block = BlockId::Hash(hash); - filter.to_block = filter.from_block; - logs(filter, ex).into_future() - }) - .collect::>() - ); - let limit = filter.limit; - let executor = self.executor.clone(); - let subscriber = subscriber.clone(); - self.executor.spawn(logs - .map(move |logs| { - let logs = logs.into_iter().flat_map(|log| log).collect(); - - for log in limit_logs(logs, limit) { - Self::notify(&executor, &subscriber, pubsub::Result::Log(Box::new(log))) - } - }) - .map_err(|e| warn!("Unable to fetch latest logs: {:?}", e)) - ); - } - } - - /// Notify all subscribers about new transaction hashes. - pub fn notify_new_transactions(&self, hashes: &[H256]) { - for subscriber in self.transactions_subscribers.read().values() { - for hash in hashes { - Self::notify(&self.executor, subscriber, pubsub::Result::TransactionHash(*hash)); - } - } - } -} - -/// A light client wrapper struct. -pub trait LightClient: Send + Sync { - /// Get a recent block header. - fn block_header(&self, id: BlockId) -> Option; - - /// Fetch logs. - fn logs(&self, filter: EthFilter) -> BoxFuture>; -} - -impl LightClient for LightFetch -where - S: LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, - OD: OnDemandRequester + 'static -{ - fn block_header(&self, id: BlockId) -> Option { - self.client.block_header(id) - } - - fn logs(&self, filter: EthFilter) -> BoxFuture> { - Box::new(LightFetch::logs(self, filter)) as BoxFuture<_> - } -} - -impl LightChainNotify for ChainNotificationHandler { - fn new_headers(&self, enacted: &[H256]) { - let headers = enacted - .iter() - .filter_map(|hash| self.client.block_header(BlockId::Hash(*hash))) - .map(|header| (header, Default::default())) - .collect::>(); - - self.notify_heads(&headers); - self.notify_logs(&enacted.iter().map(|h| (*h, ())).collect::>(), |filter, _| self.client.logs(filter)) - } + fn notify(executor: &Executor, subscriber: &Client, result: pubsub::Result) { + executor.spawn( + subscriber + .notify(Ok(result)) + .map(|_| ()) + .map_err(|e| warn!(target: "rpc", "Unable to send notification: {}", e)), + ); + } + + fn notify_heads(&self, headers: &[(encoded::Header, BTreeMap)]) { + for subscriber in self.heads_subscribers.read().values() { + for &(ref header, ref extra_info) in headers { + Self::notify( + &self.executor, + subscriber, + pubsub::Result::Header(Box::new(RichHeader { + inner: header.into(), + extra_info: extra_info.clone(), + })), + ); + } + } + } + + fn notify_logs(&self, enacted: &[(H256, Ex)], logs: F) + where + F: Fn(EthFilter, &Ex) -> T, + Ex: Send, + T: IntoFuture, Error = Error>, + T::Future: Send + 'static, + { + for &(ref subscriber, ref filter) in self.logs_subscribers.read().values() { + let logs = futures::future::join_all( + enacted + .iter() + .map(|&(hash, ref ex)| { + let mut filter = filter.clone(); + filter.from_block = BlockId::Hash(hash); + filter.to_block = filter.from_block; + logs(filter, ex).into_future() + }) + .collect::>(), + ); + let limit = filter.limit; + let executor = self.executor.clone(); + let subscriber = subscriber.clone(); + self.executor.spawn( + logs.map(move |logs| { + let logs = logs.into_iter().flat_map(|log| log).collect(); + + for log in limit_logs(logs, limit) { + Self::notify(&executor, &subscriber, pubsub::Result::Log(Box::new(log))) + } + }) + .map_err(|e| warn!("Unable to fetch latest logs: {:?}", e)), + ); + } + } + + /// Notify all subscribers about new transaction hashes. + pub fn notify_new_transactions(&self, hashes: &[H256]) { + for subscriber in self.transactions_subscribers.read().values() { + for hash in hashes { + Self::notify( + &self.executor, + subscriber, + pubsub::Result::TransactionHash(*hash), + ); + } + } + } } impl ChainNotify for ChainNotificationHandler { - fn new_blocks(&self, new_blocks: NewBlocks) { - if self.heads_subscribers.read().is_empty() && self.logs_subscribers.read().is_empty() { return } - const EXTRA_INFO_PROOF: &str = "Object exists in in blockchain (fetched earlier), extra_info is always available if object exists; qed"; - let headers = new_blocks.route.route() - .iter() - .filter_map(|&(hash, ref typ)| { - match typ { - ChainRouteType::Retracted => None, - ChainRouteType::Enacted => self.client.block_header(BlockId::Hash(hash)) - } - }) - .map(|header| { - let hash = header.hash(); - (header, self.client.block_extra_info(BlockId::Hash(hash)).expect(EXTRA_INFO_PROOF)) - }) - .collect::>(); - - // Headers - self.notify_heads(&headers); - - // We notify logs enacting and retracting as the order in route. - self.notify_logs(new_blocks.route.route(), |filter, ex| { - match ex { - ChainRouteType::Enacted => - Ok(self.client.logs(filter).unwrap_or_default().into_iter().map(Into::into).collect()), - ChainRouteType::Retracted => - Ok(self.client.logs(filter).unwrap_or_default().into_iter().map(Into::into).map(|mut log: Log| { - log.log_type = "removed".into(); - log.removed = true; - log - }).collect()), - } - }); - } + fn new_blocks(&self, new_blocks: NewBlocks) { + if self.heads_subscribers.read().is_empty() && self.logs_subscribers.read().is_empty() { + return; + } + const EXTRA_INFO_PROOF: &str = "Object exists in in blockchain (fetched earlier), extra_info is always available if object exists; qed"; + let headers = new_blocks + .route + .route() + .iter() + .filter_map(|&(hash, ref typ)| match typ { + ChainRouteType::Retracted => None, + ChainRouteType::Enacted => self.client.block_header(BlockId::Hash(hash)), + }) + .map(|header| { + let hash = header.hash(); + ( + header, + self.client + .block_extra_info(BlockId::Hash(hash)) + .expect(EXTRA_INFO_PROOF), + ) + }) + .collect::>(); + + // Headers + self.notify_heads(&headers); + + // We notify logs enacting and retracting as the order in route. + self.notify_logs(new_blocks.route.route(), |filter, ex| match ex { + ChainRouteType::Enacted => Ok(self + .client + .logs(filter) + .unwrap_or_default() + .into_iter() + .map(Into::into) + .collect()), + ChainRouteType::Retracted => Ok(self + .client + .logs(filter) + .unwrap_or_default() + .into_iter() + .map(Into::into) + .map(|mut log: Log| { + log.log_type = "removed".into(); + log.removed = true; + log + }) + .collect()), + }); + } } impl EthPubSub for EthPubSubClient { - type Metadata = Metadata; - - fn subscribe( - &self, - _meta: Metadata, - subscriber: Subscriber, - kind: pubsub::Kind, - params: Option, - ) { - let error = match (kind, params) { - (pubsub::Kind::NewHeads, None) => { - self.heads_subscribers.write().push(subscriber); - return; - }, - (pubsub::Kind::NewHeads, _) => { - errors::invalid_params("newHeads", "Expected no parameters.") - }, - (pubsub::Kind::Logs, Some(pubsub::Params::Logs(filter))) => { - match filter.try_into() { - Ok(filter) => { - self.logs_subscribers.write().push(subscriber, filter); - return; - }, - Err(err) => err, - } - }, - (pubsub::Kind::Logs, _) => { - errors::invalid_params("logs", "Expected a filter object.") - }, - (pubsub::Kind::NewPendingTransactions, None) => { - self.transactions_subscribers.write().push(subscriber); - return; - }, - (pubsub::Kind::NewPendingTransactions, _) => { - errors::invalid_params("newPendingTransactions", "Expected no parameters.") - }, - _ => { - errors::unimplemented(None) - }, - }; - - let _ = subscriber.reject(error); - } - - fn unsubscribe(&self, _: Option, id: SubscriptionId) -> Result { - let res = self.heads_subscribers.write().remove(&id).is_some(); - let res2 = self.logs_subscribers.write().remove(&id).is_some(); - let res3 = self.transactions_subscribers.write().remove(&id).is_some(); - - Ok(res || res2 || res3) - } + type Metadata = Metadata; + + fn subscribe( + &self, + _meta: Metadata, + subscriber: Subscriber, + kind: pubsub::Kind, + params: Option, + ) { + let error = match (kind, params) { + (pubsub::Kind::NewHeads, None) => { + self.heads_subscribers.write().push(subscriber); + return; + } + (pubsub::Kind::NewHeads, _) => { + errors::invalid_params("newHeads", "Expected no parameters.") + } + (pubsub::Kind::Logs, Some(pubsub::Params::Logs(filter))) => match filter.try_into() { + Ok(filter) => { + self.logs_subscribers.write().push(subscriber, filter); + return; + } + Err(err) => err, + }, + (pubsub::Kind::Logs, _) => errors::invalid_params("logs", "Expected a filter object."), + (pubsub::Kind::NewPendingTransactions, None) => { + self.transactions_subscribers.write().push(subscriber); + return; + } + (pubsub::Kind::NewPendingTransactions, _) => { + errors::invalid_params("newPendingTransactions", "Expected no parameters.") + } + _ => errors::unimplemented(None), + }; + + let _ = subscriber.reject(error); + } + + fn unsubscribe(&self, _: Option, id: SubscriptionId) -> Result { + let res = self.heads_subscribers.write().remove(&id).is_some(); + let res2 = self.logs_subscribers.write().remove(&id).is_some(); + let res3 = self.transactions_subscribers.write().remove(&id).is_some(); + + Ok(res || res2 || res3) + } } diff --git a/rpc/src/v1/impls/light/eth.rs b/rpc/src/v1/impls/light/eth.rs deleted file mode 100644 index 73e2b99c61d..00000000000 --- a/rpc/src/v1/impls/light/eth.rs +++ /dev/null @@ -1,600 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Eth RPC interface for the light client. - -use std::collections::BTreeSet; -use std::sync::Arc; - -use jsonrpc_core::{Result, BoxFuture}; -use jsonrpc_core::futures::{future, Future}; -use jsonrpc_core::futures::future::Either; - -use light::cache::Cache as LightDataCache; -use light::client::LightChainClient; -use light::{cht, TransactionQueue}; -use light::on_demand::{request, OnDemandRequester}; - -use ethereum_types::{Address, H64, H160, H256, U64, U256}; -use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY_LIST_RLP}; -use parking_lot::{RwLock, Mutex}; -use rlp::Rlp; -use types::transaction::SignedTransaction; -use types::encoded; -use types::filter::Filter as EthcoreFilter; -use types::ids::BlockId; - -use v1::impls::eth_filter::Filterable; -use v1::helpers::{errors, limit_logs, SyncPollFilter, PollManager}; -use v1::helpers::deprecated::{self, DeprecationNotice}; -use v1::helpers::light_fetch::{self, LightFetch}; -use v1::traits::Eth; -use v1::types::{ - RichBlock, Block, BlockTransactions, BlockNumber, LightBlockNumber, Bytes, SyncStatus as RpcSyncStatus, - SyncInfo as RpcSyncInfo, Transaction, CallRequest, Index, Filter, Log, Receipt, Work, EthAccount -}; -use v1::metadata::Metadata; - -use sync::{LightSyncInfo, LightSyncProvider, LightNetworkDispatcher, ManageNetwork}; - -const NO_INVALID_BACK_REFS: &str = "Fails only on invalid back-references; back-references here known to be valid; qed"; - -/// Light client `ETH` (and filter) RPC. -pub struct EthClient { - sync: Arc, - client: Arc, - on_demand: Arc, - transaction_queue: Arc>, - accounts: Arc Vec
+ Send + Sync>, - cache: Arc>, - polls: Mutex>, - poll_lifetime: u32, - gas_price_percentile: usize, - deprecation_notice: DeprecationNotice, -} - -impl Clone for EthClient -where - S: LightSyncProvider + LightNetworkDispatcher + 'static, - OD: OnDemandRequester + 'static -{ - fn clone(&self) -> Self { - // each instance should have its own poll manager. - EthClient { - sync: self.sync.clone(), - client: self.client.clone(), - on_demand: self.on_demand.clone(), - transaction_queue: self.transaction_queue.clone(), - accounts: self.accounts.clone(), - cache: self.cache.clone(), - polls: Mutex::new(PollManager::new(self.poll_lifetime)), - poll_lifetime: self.poll_lifetime, - gas_price_percentile: self.gas_price_percentile, - deprecation_notice: Default::default(), - } - } -} - -impl EthClient -where - C: LightChainClient + 'static, - S: LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, - OD: OnDemandRequester + 'static -{ - /// Create a new `EthClient` with a handle to the light sync instance, client, - /// and on-demand request service, which is assumed to be attached as a handler. - pub fn new( - sync: Arc, - client: Arc, - on_demand: Arc, - transaction_queue: Arc>, - accounts: Arc Vec
+ Send + Sync>, - cache: Arc>, - gas_price_percentile: usize, - poll_lifetime: u32 - ) -> Self { - EthClient { - sync, - client, - on_demand, - transaction_queue, - accounts, - cache, - polls: Mutex::new(PollManager::new(poll_lifetime)), - poll_lifetime, - gas_price_percentile, - deprecation_notice: Default::default(), - } - } - - /// Create a light data fetcher instance. - fn fetcher(&self) -> LightFetch - { - LightFetch { - client: self.client.clone(), - on_demand: self.on_demand.clone(), - sync: self.sync.clone(), - cache: self.cache.clone(), - gas_price_percentile: self.gas_price_percentile, - } - } - - // get a "rich" block structure. Fails on unknown block. - fn rich_block(&self, id: BlockId, include_txs: bool) -> BoxFuture { - let (on_demand, sync) = (self.on_demand.clone(), self.sync.clone()); - let (client, engine) = (self.client.clone(), self.client.engine().clone()); - - // helper for filling out a rich block once we've got a block and a score. - let fill_rich = move |block: encoded::Block, score: Option| { - let header = block.decode_header(); - let extra_info = engine.extra_info(&header); - RichBlock { - inner: Block { - hash: Some(header.hash()), - size: Some(block.rlp().as_raw().len().into()), - parent_hash: *header.parent_hash(), - uncles_hash: *header.uncles_hash(), - author: *header.author(), - miner: *header.author(), - state_root: *header.state_root(), - transactions_root: *header.transactions_root(), - receipts_root: *header.receipts_root(), - number: Some(header.number().into()), - gas_used: *header.gas_used(), - gas_limit: *header.gas_limit(), - logs_bloom: Some(*header.log_bloom()), - timestamp: header.timestamp().into(), - difficulty: *header.difficulty(), - total_difficulty: score.map(Into::into), - seal_fields: header.seal().iter().cloned().map(Into::into).collect(), - uncles: block.uncle_hashes().into_iter().map(Into::into).collect(), - transactions: match include_txs { - true => BlockTransactions::Full(block.view().localized_transactions().into_iter().map(Transaction::from_localized).collect()), - _ => BlockTransactions::Hashes(block.transaction_hashes().into_iter().map(Into::into).collect()), - }, - extra_data: Bytes::new(header.extra_data().clone()), - }, - extra_info, - } - }; - - // get the block itself. - Box::new(self.fetcher().block(id).and_then(move |block| { - // then fetch the total difficulty (this is much easier after getting the block). - match client.score(id) { - Some(score) => Either::A(future::ok(fill_rich(block, Some(score)))), - None => { - // make a CHT request to fetch the chain score. - let req = cht::block_to_cht_number(block.number()) - .and_then(|num| client.cht_root(num as usize)) - .and_then(|root| request::HeaderProof::new(block.number(), root)); - - let req = match req { - Some(req) => req, - None => { - // somehow the genesis block slipped past other checks. - // return it now. - let score = client.block_header(BlockId::Number(0)) - .expect("genesis always stored; qed") - .difficulty(); - - return Either::A(future::ok(fill_rich(block, Some(score)))) - } - }; - - // three possible outcomes: - // - network is down. - // - we get a score, but our hash is non-canonical. - // - we get a score, and our hash is canonical. - let maybe_fut = sync.with_context(move |ctx| on_demand.request(ctx, req).expect(NO_INVALID_BACK_REFS)); - match maybe_fut { - Some(fut) => Either::B(fut - .map(move |(hash, score)| { - let score = if hash == block.hash() { - Some(score) - } else { - None - }; - - fill_rich(block, score) - }).map_err(errors::on_demand_error)), - None => Either::A(future::err(errors::network_disabled())), - } - } - } - })) - } -} - -impl Eth for EthClient -where - C: LightChainClient + 'static, - S: LightSyncInfo + LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, - OD: OnDemandRequester + 'static -{ - type Metadata = Metadata; - - fn protocol_version(&self) -> Result { - Ok(format!("{}", ::light::net::MAX_PROTOCOL_VERSION)) - } - - fn syncing(&self) -> Result { - if self.sync.is_major_importing() { - let chain_info = self.client.chain_info(); - let current_block = U256::from(chain_info.best_block_number); - let highest_block = self.sync.highest_block().map(U256::from) - .unwrap_or_else(|| current_block); - - Ok(RpcSyncStatus::Info(RpcSyncInfo { - starting_block: U256::from(self.sync.start_block()), - current_block, - highest_block, - warp_chunks_amount: None, - warp_chunks_processed: None, - })) - } else { - Ok(RpcSyncStatus::None) - } - } - - fn author(&self) -> Result { - (self.accounts)() - .first() - .cloned() - .map(From::from) - .ok_or_else(|| errors::account("No accounts were found", "")) - } - - fn is_mining(&self) -> Result { - Ok(false) - } - - fn chain_id(&self) -> Result> { - Ok(self.client.signing_chain_id().map(U64::from)) - } - - fn hashrate(&self) -> Result { - Ok(Default::default()) - } - - fn gas_price(&self) -> BoxFuture { - Box::new(self.fetcher().gas_price()) - } - - fn accounts(&self) -> Result> { - self.deprecation_notice.print("eth_accounts", deprecated::msgs::ACCOUNTS); - - Ok((self.accounts)() - .into_iter() - .map(Into::into) - .collect()) - } - - fn block_number(&self) -> Result { - Ok(self.client.chain_info().best_block_number.into()) - } - - fn balance(&self, address: H160, num: Option) -> BoxFuture { - Box::new(self.fetcher().account(address, num.unwrap_or_default().to_block_id(), self.transaction_queue.clone()) - .map(|acc| acc.map_or(0.into(), |a| a.balance))) - } - - fn storage_at(&self, _address: H160, _key: U256, _num: Option) -> BoxFuture { - Box::new(future::err(errors::unimplemented(None))) - } - - fn block_by_hash(&self, hash: H256, include_txs: bool) -> BoxFuture> { - Box::new(self.rich_block(BlockId::Hash(hash), include_txs).map(Some)) - } - - fn block_by_number(&self, num: BlockNumber, include_txs: bool) -> BoxFuture> { - Box::new(self.rich_block(num.to_block_id(), include_txs).map(Some)) - } - - fn transaction_count(&self, address: H160, num: Option) -> BoxFuture { - Box::new(self.fetcher().account(address, num.unwrap_or_default().to_block_id(), self.transaction_queue.clone()) - .map(|acc| acc.map_or(0.into(), |a| a.nonce))) - } - - fn block_transaction_count_by_hash(&self, hash: H256) -> BoxFuture> { - let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone()); - - Box::new(self.fetcher().header(BlockId::Hash(hash)).and_then(move |hdr| { - if hdr.transactions_root() == KECCAK_NULL_RLP { - Either::A(future::ok(Some(U256::from(0)))) - } else { - sync.with_context(|ctx| on_demand.request(ctx, request::Body(hdr.into()))) - .map(|x| x.expect(NO_INVALID_BACK_REFS)) - .map(|x| x.map(|b| Some(U256::from(b.transactions_count())))) - .map(|x| Either::B(x.map_err(errors::on_demand_error))) - .unwrap_or_else(|| Either::A(future::err(errors::network_disabled()))) - } - })) - } - - fn block_transaction_count_by_number(&self, num: BlockNumber) -> BoxFuture> { - let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone()); - - Box::new(self.fetcher().header(num.to_block_id()).and_then(move |hdr| { - if hdr.transactions_root() == KECCAK_NULL_RLP { - Either::A(future::ok(Some(U256::from(0)))) - } else { - sync.with_context(|ctx| on_demand.request(ctx, request::Body(hdr.into()))) - .map(|x| x.expect(NO_INVALID_BACK_REFS)) - .map(|x| x.map(|b| Some(U256::from(b.transactions_count())))) - .map(|x| Either::B(x.map_err(errors::on_demand_error))) - .unwrap_or_else(|| Either::A(future::err(errors::network_disabled()))) - } - })) - } - - fn block_uncles_count_by_hash(&self, hash: H256) -> BoxFuture> { - let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone()); - - Box::new(self.fetcher().header(BlockId::Hash(hash)).and_then(move |hdr| { - if hdr.uncles_hash() == KECCAK_EMPTY_LIST_RLP { - Either::A(future::ok(Some(U256::from(0)))) - } else { - sync.with_context(|ctx| on_demand.request(ctx, request::Body(hdr.into()))) - .map(|x| x.expect(NO_INVALID_BACK_REFS)) - .map(|x| x.map(|b| Some(U256::from(b.uncles_count())))) - .map(|x| Either::B(x.map_err(errors::on_demand_error))) - .unwrap_or_else(|| Either::A(future::err(errors::network_disabled()))) - } - })) - } - - fn block_uncles_count_by_number(&self, num: BlockNumber) -> BoxFuture> { - let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone()); - - Box::new(self.fetcher().header(num.to_block_id()).and_then(move |hdr| { - if hdr.uncles_hash() == KECCAK_EMPTY_LIST_RLP { - Either::B(future::ok(Some(U256::from(0)))) - } else { - sync.with_context(|ctx| on_demand.request(ctx, request::Body(hdr.into()))) - .map(|x| x.expect(NO_INVALID_BACK_REFS)) - .map(|x| x.map(|b| Some(U256::from(b.uncles_count())))) - .map(|x| Either::A(x.map_err(errors::on_demand_error))) - .unwrap_or_else(|| Either::B(future::err(errors::network_disabled()))) - } - })) - } - - fn code_at(&self, address: H160, num: Option) -> BoxFuture { - Box::new(self.fetcher().code(address, num.unwrap_or_default().to_block_id()).map(Into::into)) - } - - fn send_raw_transaction(&self, raw: Bytes) -> Result { - let best_header = self.client.best_block_header().decode().map_err(errors::decode)?; - - Rlp::new(&raw.into_vec()).as_val() - .map_err(errors::rlp) - .and_then(|tx| { - self.client.engine().verify_transaction_basic(&tx, &best_header) - .map_err(errors::transaction)?; - - let signed = SignedTransaction::new(tx).map_err(errors::transaction)?; - let hash = signed.hash(); - - self.transaction_queue.write().import(signed.into()) - .map(|_| hash) - .map_err(errors::transaction) - }) - .map(Into::into) - } - - fn submit_transaction(&self, raw: Bytes) -> Result { - self.send_raw_transaction(raw) - } - - fn call(&self, req: CallRequest, num: Option) -> BoxFuture { - Box::new(self.fetcher().proved_read_only_execution(req, num, self.transaction_queue.clone()).and_then(|res| { - match res { - Ok(exec) => Ok(exec.output.into()), - Err(e) => Err(errors::execution(e)), - } - })) - } - - fn estimate_gas(&self, req: CallRequest, num: Option) -> BoxFuture { - // TODO: binary chop for more accurate estimates. - Box::new(self.fetcher().proved_read_only_execution(req, num, self.transaction_queue.clone()).and_then(|res| { - match res { - Ok(exec) => Ok(exec.refunded + exec.gas_used), - Err(e) => Err(errors::execution(e)), - } - })) - } - - fn transaction_by_hash(&self, hash: H256) -> BoxFuture> { - { - let tx_queue = self.transaction_queue.read(); - if let Some(tx) = tx_queue.get(&hash) { - return Box::new(future::ok(Some(Transaction::from_pending( - tx.clone(), - )))); - } - } - - Box::new(self.fetcher().transaction_by_hash(hash).map(|x| x.map(|(tx, _)| tx))) - } - - fn transaction_by_block_hash_and_index(&self, hash: H256, idx: Index) -> BoxFuture> { - Box::new(self.fetcher().block(BlockId::Hash(hash)).map(move |block| { - light_fetch::extract_transaction_at_index(block, idx.value()) - })) - } - - fn transaction_by_block_number_and_index(&self, num: BlockNumber, idx: Index) -> BoxFuture> { - Box::new(self.fetcher().block(num.to_block_id()).map(move |block| { - light_fetch::extract_transaction_at_index(block, idx.value()) - })) - } - - fn transaction_receipt(&self, hash: H256) -> BoxFuture> { - let fetcher = self.fetcher(); - Box::new(fetcher.transaction_by_hash(hash).and_then(move |tx| { - // the block hash included in the transaction object here has - // already been checked for canonicality and whether it contains - // the transaction. - match tx { - Some((tx, index)) => match tx.block_hash { - Some(block_hash) => { - let extract_receipt = fetcher.receipts(BlockId::Hash(block_hash)) - .and_then(move |mut receipts| future::ok(receipts.swap_remove(index))) - .map(Receipt::from) - .map(move |mut receipt| { - receipt.transaction_hash = Some(hash); - receipt.transaction_index = Some(index.into()); - receipt.block_hash = Some(block_hash); - receipt.block_number = tx.block_number; - receipt - }) - .map(Some); - - Either::B(extract_receipt) - } - None => Either::A(future::err(errors::unknown_block())), - }, - None => Either::A(future::ok(None)), - } - })) - } - - fn uncle_by_block_hash_and_index(&self, hash: H256, idx: Index) -> BoxFuture> { - let client = self.client.clone(); - Box::new(self.fetcher().block(BlockId::Hash(hash)).map(move |block| { - extract_uncle_at_index(block, idx, client) - })) - } - - fn uncle_by_block_number_and_index(&self, num: BlockNumber, idx: Index) -> BoxFuture> { - let client = self.client.clone(); - Box::new(self.fetcher().block(num.to_block_id()).map(move |block| { - extract_uncle_at_index(block, idx, client) - })) - } - - fn proof(&self, _address: H160, _values:Vec, _num: Option) -> BoxFuture { - Box::new(future::err(errors::unimplemented(None))) - } - - fn compilers(&self) -> Result> { - Err(errors::deprecated("Compilation functionality is deprecated.".to_string())) - } - - fn compile_lll(&self, _: String) -> Result { - Err(errors::deprecated("Compilation of LLL via RPC is deprecated".to_string())) - } - - fn compile_serpent(&self, _: String) -> Result { - Err(errors::deprecated("Compilation of Serpent via RPC is deprecated".to_string())) - } - - fn compile_solidity(&self, _: String) -> Result { - Err(errors::deprecated("Compilation of Solidity via RPC is deprecated".to_string())) - } - - fn logs(&self, filter: Filter) -> BoxFuture> { - let limit = filter.limit; - - Box::new( - Filterable::logs(self, match filter.try_into() { - Ok(value) => value, - Err(err) => return Box::new(future::err(err)), - }).map(move |logs| limit_logs(logs, limit))) - } - - fn work(&self, _timeout: Option) -> Result { - Err(errors::light_unimplemented(None)) - } - - fn submit_work(&self, _nonce: H64, _pow_hash: H256, _mix_hash: H256) -> Result { - Err(errors::light_unimplemented(None)) - } - - fn submit_hashrate(&self, _rate: U256, _id: H256) -> Result { - Err(errors::light_unimplemented(None)) - } -} - -// This trait implementation triggers a blanked impl of `EthFilter`. -impl Filterable for EthClient -where - C: LightChainClient + 'static, - S: LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, - OD: OnDemandRequester + 'static -{ - fn best_block_number(&self) -> u64 { self.client.chain_info().best_block_number } - - fn block_hash(&self, id: BlockId) -> Option { - self.client.block_hash(id) - } - - fn pending_transaction_hashes(&self) -> BTreeSet { - BTreeSet::new() - } - - fn logs(&self, filter: EthcoreFilter) -> BoxFuture> { - Box::new(self.fetcher().logs(filter)) as BoxFuture<_> - } - - fn pending_logs(&self, _block_number: u64, _filter: &EthcoreFilter) -> Vec { - Vec::new() // light clients don't mine. - } - - fn polls(&self) -> &Mutex> { - &self.polls - } - - fn removed_logs(&self, _block_hash: ::ethereum_types::H256, _filter: &EthcoreFilter) -> (Vec, u64) { - (Default::default(), 0) - } -} - -fn extract_uncle_at_index(block: encoded::Block, index: Index, client: Arc) -> Option { - let uncle = match block.uncles().into_iter().nth(index.value()) { - Some(u) => u, - None => return None, - }; - - let extra_info = client.engine().extra_info(&uncle); - Some(RichBlock { - inner: Block { - hash: Some(uncle.hash()), - size: None, - parent_hash: *uncle.parent_hash(), - uncles_hash: *uncle.uncles_hash(), - author: *uncle.author(), - miner: *uncle.author(), - state_root: *uncle.state_root(), - transactions_root: *uncle.transactions_root(), - number: Some(uncle.number().into()), - gas_used: *uncle.gas_used(), - gas_limit: *uncle.gas_limit(), - logs_bloom: Some(*uncle.log_bloom()), - timestamp: uncle.timestamp().into(), - difficulty: *uncle.difficulty(), - total_difficulty: None, - receipts_root: *uncle.receipts_root(), - extra_data: uncle.extra_data().clone().into(), - seal_fields: uncle.seal().iter().cloned().map(Into::into).collect(), - uncles: vec![], - transactions: BlockTransactions::Hashes(vec![]), - }, - extra_info, - }) -} diff --git a/rpc/src/v1/impls/light/mod.rs b/rpc/src/v1/impls/light/mod.rs deleted file mode 100644 index c159514582b..00000000000 --- a/rpc/src/v1/impls/light/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! RPC implementations for the light client. -//! -//! This doesn't re-implement all of the RPC APIs, just those which aren't -//! significantly generic to be reused. - -pub mod eth; -pub mod parity; -pub mod parity_set; -pub mod trace; -pub mod net; - -pub use self::eth::EthClient; -pub use self::parity::ParityClient; -pub use self::parity_set::ParitySetClient; -pub use self::net::NetClient; -pub use self::trace::TracesClient; diff --git a/rpc/src/v1/impls/light/net.rs b/rpc/src/v1/impls/light/net.rs deleted file mode 100644 index a9ab012e5ad..00000000000 --- a/rpc/src/v1/impls/light/net.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Net rpc implementation. -use std::sync::Arc; -use jsonrpc_core::Result; -use sync::LightSyncProvider; -use v1::traits::Net; - -/// Net rpc implementation. -pub struct NetClient { - sync: Arc -} - -impl NetClient where S: LightSyncProvider { - /// Creates new NetClient. - pub fn new(sync: Arc) -> Self { - NetClient { - sync, - } - } -} - -impl Net for NetClient where S: LightSyncProvider { - fn version(&self) -> Result { - Ok(format!("{}", self.sync.network_id()).to_owned()) - } - - fn peer_count(&self) -> Result { - Ok(format!("0x{:x}", self.sync.peer_numbers().connected as u64).to_owned()) - } - - fn is_listening(&self) -> Result { - Ok(true) - } -} diff --git a/rpc/src/v1/impls/light/parity.rs b/rpc/src/v1/impls/light/parity.rs deleted file mode 100644 index 0486366de62..00000000000 --- a/rpc/src/v1/impls/light/parity.rs +++ /dev/null @@ -1,410 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Parity-specific rpc implementation. -use std::sync::Arc; -use std::collections::BTreeMap; - -use version::version_data; - -use crypto::DEFAULT_MAC; -use ethkey::{crypto::ecies, Brain, Generator}; -use ethstore::random_phrase; -use sync::{LightSyncInfo, LightSyncProvider, LightNetworkDispatcher, ManageNetwork}; -use updater::VersionInfo as UpdaterVersionInfo; -use ethereum_types::{H64, H160, H256, H512, U64, U256}; -use ethcore_logger::RotatingLogger; - -use jsonrpc_core::{Result, BoxFuture}; -use jsonrpc_core::futures::{future, Future}; -use light::on_demand::OnDemandRequester; -use v1::helpers::{self, errors, ipfs, NetworkSettings, verify_signature}; -use v1::helpers::external_signer::{SignerService, SigningQueue}; -use v1::helpers::dispatch::LightDispatcher; -use v1::helpers::light_fetch::{LightFetch, light_all_transactions}; -use v1::metadata::Metadata; -use v1::traits::Parity; -use v1::types::{ - Bytes, CallRequest, - Peers, Transaction, RpcSettings, Histogram, - TransactionStats, LocalTransactionStatus, - LightBlockNumber, ChainStatus, Receipt, - BlockNumber, ConsensusCapability, VersionInfo, - OperationsInfo, Header, RichHeader, RecoveredAccount, - Log, Filter, -}; -use Host; - -/// Parity implementation for light client. -pub struct ParityClient -where - S: LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, - OD: OnDemandRequester + 'static -{ - light_dispatch: Arc>, - logger: Arc, - settings: Arc, - signer: Option>, - ws_address: Option, - gas_price_percentile: usize, -} - -impl ParityClient -where - S: LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, - OD: OnDemandRequester + 'static -{ - /// Creates new `ParityClient`. - pub fn new( - light_dispatch: Arc>, - logger: Arc, - settings: Arc, - signer: Option>, - ws_address: Option, - gas_price_percentile: usize, - ) -> Self { - ParityClient { - light_dispatch, - logger, - settings, - signer, - ws_address, - gas_price_percentile, - } - } - - /// Create a light blockchain data fetcher. - fn fetcher(&self) -> LightFetch - { - LightFetch { - client: self.light_dispatch.client.clone(), - on_demand: self.light_dispatch.on_demand.clone(), - sync: self.light_dispatch.sync.clone(), - cache: self.light_dispatch.cache.clone(), - gas_price_percentile: self.gas_price_percentile, - } - } -} - -impl Parity for ParityClient -where - S: LightSyncInfo + LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, - OD: OnDemandRequester + 'static -{ - type Metadata = Metadata; - - fn transactions_limit(&self) -> Result { - Ok(usize::max_value()) - } - - fn min_gas_price(&self) -> Result { - Ok(U256::default()) - } - - fn extra_data(&self) -> Result { - Ok(Bytes::default()) - } - - fn gas_floor_target(&self) -> Result { - Ok(U256::default()) - } - - fn gas_ceil_target(&self) -> Result { - Ok(U256::default()) - } - - fn dev_logs(&self) -> Result> { - let logs = self.logger.logs(); - Ok(logs.as_slice().to_owned()) - } - - fn dev_logs_levels(&self) -> Result { - Ok(self.logger.levels().to_owned()) - } - - fn net_chain(&self) -> Result { - Ok(self.settings.chain.clone()) - } - - fn net_peers(&self) -> Result { - let peers = self.light_dispatch.sync.peers().into_iter().map(Into::into).collect(); - let peer_numbers = self.light_dispatch.sync.peer_numbers(); - - Ok(Peers { - active: peer_numbers.active, - connected: peer_numbers.connected, - max: peer_numbers.max as u32, - peers, - }) - } - - fn net_port(&self) -> Result { - Ok(self.settings.network_port) - } - - fn node_name(&self) -> Result { - Ok(self.settings.name.clone()) - } - - fn registry_address(&self) -> Result> { - let reg = self.light_dispatch.client.engine().params().registrar; - if reg == Default::default() { - Ok(None) - } else { - Ok(Some(reg)) - } - } - - fn rpc_settings(&self) -> Result { - Ok(RpcSettings { - enabled: self.settings.rpc_enabled, - interface: self.settings.rpc_interface.clone(), - port: self.settings.rpc_port as u64, - }) - } - - fn default_extra_data(&self) -> Result { - Ok(Bytes::new(version_data())) - } - - fn gas_price_histogram(&self) -> BoxFuture { - Box::new(self.light_dispatch.gas_price_corpus() - .and_then(|corpus| corpus.histogram(10).ok_or_else(errors::not_enough_data)) - .map(Into::into)) - } - - fn unsigned_transactions_count(&self) -> Result { - match self.signer { - None => Err(errors::signer_disabled()), - Some(ref signer) => Ok(signer.len()), - } - } - - fn generate_secret_phrase(&self) -> Result { - Ok(random_phrase(12)) - } - - fn phrase_to_address(&self, phrase: String) -> Result { - Ok(Brain::new(phrase).generate().expect("Brain::generate always returns Ok; qed").address()) - } - - fn list_accounts(&self, _: u64, _: Option, _: Option) -> Result>> { - Err(errors::light_unimplemented(None)) - } - - fn list_storage_keys(&self, _: H160, _: u64, _: Option, _: Option) -> Result>> { - Err(errors::light_unimplemented(None)) - } - - fn encrypt_message(&self, key: H512, phrase: Bytes) -> Result { - ecies::encrypt(&key, &DEFAULT_MAC, &phrase.0) - .map_err(errors::encryption) - .map(Into::into) - } - - fn pending_transactions(&self, limit: Option) -> Result> { - let txq = self.light_dispatch.transaction_queue.read(); - let chain_info = self.light_dispatch.client.chain_info(); - Ok( - txq.ready_transactions(chain_info.best_block_number, chain_info.best_block_timestamp) - .into_iter() - .take(limit.unwrap_or_else(usize::max_value)) - .map(Transaction::from_pending) - .collect::>() - ) - } - - fn all_transactions(&self) -> Result> { - Ok( - light_all_transactions(&self.light_dispatch) - .map(Transaction::from_pending) - .collect() - ) - } - - fn all_transaction_hashes(&self) -> Result> { - Ok( - light_all_transactions(&self.light_dispatch) - .map(|tx| tx.transaction.hash()) - .collect() - ) - } - - fn future_transactions(&self) -> Result> { - let txq = self.light_dispatch.transaction_queue.read(); - let chain_info = self.light_dispatch.client.chain_info(); - Ok( - txq.future_transactions(chain_info.best_block_number, chain_info.best_block_timestamp) - .into_iter() - .map(Transaction::from_pending) - .collect::>() - ) - } - - fn pending_transactions_stats(&self) -> Result> { - let stats = self.light_dispatch.sync.transactions_stats(); - Ok(stats.into_iter() - .map(|(hash, stats)| (hash, stats.into())) - .collect() - ) - } - - fn local_transactions(&self) -> Result> { - let mut map = BTreeMap::new(); - let chain_info = self.light_dispatch.client.chain_info(); - let (best_num, best_tm) = (chain_info.best_block_number, chain_info.best_block_timestamp); - let txq = self.light_dispatch.transaction_queue.read(); - - for pending in txq.ready_transactions(best_num, best_tm) { - map.insert(pending.hash(), LocalTransactionStatus::Pending); - } - - for future in txq.future_transactions(best_num, best_tm) { - map.insert(future.hash(), LocalTransactionStatus::Future); - } - - // TODO: other types? - - Ok(map) - } - - fn ws_url(&self) -> Result { - helpers::to_url(&self.ws_address) - .ok_or_else(errors::ws_disabled) - } - - fn next_nonce(&self, address: H160) -> BoxFuture { - Box::new(self.light_dispatch.next_nonce(address)) - } - - fn mode(&self) -> Result { - Err(errors::light_unimplemented(None)) - } - - fn chain(&self) -> Result { - Ok(self.settings.chain.clone()) - } - - fn enode(&self) -> Result { - self.light_dispatch.sync.enode().ok_or_else(errors::network_disabled) - } - - fn consensus_capability(&self) -> Result { - Err(errors::light_unimplemented(None)) - } - - fn version_info(&self) -> Result { - Ok(UpdaterVersionInfo::this().into()) - } - - fn releases_info(&self) -> Result> { - Err(errors::light_unimplemented(None)) - } - - fn chain_status(&self) -> Result { - let chain_info = self.light_dispatch.client.chain_info(); - - let gap = chain_info.ancient_block_number.map(|x| U256::from(x + 1)) - .and_then(|first| chain_info.first_block_number.map(|last| (first, U256::from(last)))); - - Ok(ChainStatus { - block_gap: gap, - }) - } - - fn node_kind(&self) -> Result<::v1::types::NodeKind> { - use ::v1::types::{NodeKind, Availability, Capability}; - - Ok(NodeKind { - availability: Availability::Personal, - capability: Capability::Light, - }) - } - - fn block_header(&self, number: Option) -> BoxFuture { - use types::encoded; - - let engine = self.light_dispatch.client.engine().clone(); - let from_encoded = move |encoded: encoded::Header| { - let header = encoded.decode().map_err(errors::decode)?; - let extra_info = engine.extra_info(&header); - Ok(RichHeader { - inner: Header { - hash: Some(header.hash()), - size: Some(encoded.rlp().as_raw().len().into()), - parent_hash: *header.parent_hash(), - uncles_hash: *header.uncles_hash(), - author: *header.author(), - miner: *header.author(), - state_root: *header.state_root(), - transactions_root: *header.transactions_root(), - receipts_root: *header.receipts_root(), - number: Some(header.number().into()), - gas_used: *header.gas_used(), - gas_limit: *header.gas_limit(), - logs_bloom: *header.log_bloom(), - timestamp: header.timestamp().into(), - difficulty: *header.difficulty(), - seal_fields: header.seal().iter().cloned().map(Into::into).collect(), - extra_data: Bytes::new(header.extra_data().clone()), - }, - extra_info, - }) - }; - let id = number.unwrap_or_default().to_block_id(); - Box::new(self.fetcher().header(id).and_then(from_encoded)) - } - - fn block_receipts(&self, number: Option) -> BoxFuture> { - let id = number.unwrap_or_default().to_block_id(); - Box::new(self.fetcher().receipts(id).and_then(|receipts| Ok(receipts.into_iter().map(Into::into).collect()))) - } - - fn ipfs_cid(&self, content: Bytes) -> Result { - ipfs::cid(content) - } - - fn call(&self, _requests: Vec, _block: Option) -> Result> { - Err(errors::light_unimplemented(None)) - } - - fn submit_work_detail(&self, _nonce: H64, _pow_hash: H256, _mix_hash: H256) -> Result { - Err(errors::light_unimplemented(None)) - } - - fn status(&self) -> Result<()> { - let has_peers = self.settings.is_dev_chain || self.light_dispatch.sync.peer_numbers().connected > 0; - let is_importing = (*self.light_dispatch.sync).is_major_importing(); - - if has_peers && !is_importing { - Ok(()) - } else { - Err(errors::status_error(has_peers)) - } - } - - fn logs_no_tx_hash(&self, filter: Filter) -> BoxFuture> { - let filter = match filter.try_into() { - Ok(value) => value, - Err(err) => return Box::new(future::err(err)), - }; - Box::new(self.fetcher().logs_no_tx_hash(filter)) as BoxFuture<_> - } - - fn verify_signature(&self, is_prefixed: bool, message: Bytes, r: H256, s: H256, v: U64) -> Result { - verify_signature(is_prefixed, message, r, s, v, self.light_dispatch.client.signing_chain_id()) - } -} diff --git a/rpc/src/v1/impls/light/parity_set.rs b/rpc/src/v1/impls/light/parity_set.rs deleted file mode 100644 index 68fc212b2fb..00000000000 --- a/rpc/src/v1/impls/light/parity_set.rs +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Parity-specific rpc interface for operations altering the settings. -//! Implementation for light client. - -use std::io; -use std::sync::Arc; - -use ethereum_types::{H160, H256, U256}; -use fetch::{self, Fetch}; -use hash::keccak_buffer; -use light::client::LightChainClient; -use sync::ManageNetwork; - -use jsonrpc_core::{Result, BoxFuture}; -use jsonrpc_core::futures::Future; -use v1::helpers::errors; -use v1::traits::ParitySet; -use v1::types::{Bytes, ReleaseInfo, Transaction}; - -/// Parity-specific rpc interface for operations altering the settings. -pub struct ParitySetClient { - client: Arc, - net: Arc, - fetch: F, -} - -impl ParitySetClient { - /// Creates new `ParitySetClient` with given `Fetch`. - pub fn new(client: Arc, net: Arc, fetch: F) -> Self { - ParitySetClient { - client, - net, - fetch, - } - } -} - -impl ParitySet for ParitySetClient { - fn set_min_gas_price(&self, _gas_price: U256) -> Result { - Err(errors::light_unimplemented(None)) - } - - fn set_gas_floor_target(&self, _target: U256) -> Result { - Err(errors::light_unimplemented(None)) - } - - fn set_gas_ceil_target(&self, _target: U256) -> Result { - Err(errors::light_unimplemented(None)) - } - - fn set_extra_data(&self, _extra_data: Bytes) -> Result { - Err(errors::light_unimplemented(None)) - } - - fn set_author(&self, _author: H160) -> Result { - Err(errors::light_unimplemented(None)) - } - - fn set_engine_signer_secret(&self, _secret: H256) -> Result { - Err(errors::light_unimplemented(None)) - } - - fn set_transactions_limit(&self, _limit: usize) -> Result { - Err(errors::light_unimplemented(None)) - } - - fn set_tx_gas_limit(&self, _limit: U256) -> Result { - Err(errors::light_unimplemented(None)) - } - - fn add_reserved_peer(&self, peer: String) -> Result { - match self.net.add_reserved_peer(peer) { - Ok(()) => Ok(true), - Err(e) => Err(errors::invalid_params("Peer address", e)), - } - } - - fn remove_reserved_peer(&self, peer: String) -> Result { - match self.net.remove_reserved_peer(peer) { - Ok(()) => Ok(true), - Err(e) => Err(errors::invalid_params("Peer address", e)), - } - } - - fn drop_non_reserved_peers(&self) -> Result { - self.net.deny_unreserved_peers(); - Ok(true) - } - - fn accept_non_reserved_peers(&self) -> Result { - self.net.accept_unreserved_peers(); - Ok(true) - } - - fn start_network(&self) -> Result { - self.net.start_network(); - Ok(true) - } - - fn stop_network(&self) -> Result { - self.net.stop_network(); - Ok(true) - } - - fn set_mode(&self, _mode: String) -> Result { - Err(errors::light_unimplemented(None)) - } - - fn set_spec_name(&self, spec_name: String) -> Result { - self.client.set_spec_name(spec_name).map(|_| true).map_err(|()| errors::cannot_restart()) - } - - fn hash_content(&self, url: String) -> BoxFuture { - let future = self.fetch.get(&url, Default::default()).then(move |result| { - result - .map_err(errors::fetch) - .and_then(move |response| { - let mut reader = io::BufReader::new(fetch::BodyReader::new(response)); - keccak_buffer(&mut reader).map_err(errors::fetch) - }) - .map(Into::into) - }); - Box::new(future) - } - - fn upgrade_ready(&self) -> Result> { - Err(errors::light_unimplemented(None)) - } - - fn execute_upgrade(&self) -> Result { - Err(errors::light_unimplemented(None)) - } - - fn remove_transaction(&self, _hash: H256) -> Result> { - Err(errors::light_unimplemented(None)) - } -} diff --git a/rpc/src/v1/impls/light/trace.rs b/rpc/src/v1/impls/light/trace.rs deleted file mode 100644 index a560f980e78..00000000000 --- a/rpc/src/v1/impls/light/trace.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Traces api implementation. - -use ethereum_types::H256; -use jsonrpc_core::Result; -use v1::Metadata; -use v1::traits::Traces; -use v1::helpers::errors; -use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, - TraceResultsWithTransactionHash, TraceOptions}; - -/// Traces api implementation. -// TODO: all calling APIs should be possible w. proved remote TX execution. -pub struct TracesClient; - -impl Traces for TracesClient { - type Metadata = Metadata; - - fn filter(&self, _filter: TraceFilter) -> Result>> { - Err(errors::light_unimplemented(None)) - } - - fn block_traces(&self, _block_number: BlockNumber) -> Result>> { - Err(errors::light_unimplemented(None)) - } - - fn transaction_traces(&self, _transaction_hash: H256) -> Result>> { - Err(errors::light_unimplemented(None)) - } - - fn trace(&self, _transaction_hash: H256, _address: Vec) -> Result> { - Err(errors::light_unimplemented(None)) - } - - fn call(&self, _request: CallRequest, _flags: TraceOptions, _block: Option) -> Result { - Err(errors::light_unimplemented(None)) - } - - fn call_many(&self, _request: Vec<(CallRequest, TraceOptions)>, _block: Option) -> Result> { - Err(errors::light_unimplemented(None)) - } - - fn raw_transaction(&self, _raw_transaction: Bytes, _flags: TraceOptions, _block: Option) -> Result { - Err(errors::light_unimplemented(None)) - } - - fn replay_transaction(&self, _transaction_hash: H256, _flags: TraceOptions) -> Result { - Err(errors::light_unimplemented(None)) - } - - fn replay_block_transactions(&self, _block_number: BlockNumber, _flags: TraceOptions) -> Result> { - Err(errors::light_unimplemented(None)) - } -} diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index ba1cc100e8a..c8a1c64bc7f 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Ethereum rpc interface implementation. @@ -27,9 +27,7 @@ mod parity_accounts; mod parity_set; #[cfg(any(test, feature = "accounts"))] mod personal; -mod private; mod pubsub; -mod rpc; #[cfg(any(test, feature = "accounts"))] mod secretstore; mod signer; @@ -38,28 +36,26 @@ mod signing_unsafe; mod traces; mod web3; -pub mod light; - -pub use self::debug::DebugClient; -pub use self::eth::{EthClient, EthClientOptions}; -pub use self::eth_filter::EthFilterClient; -pub use self::eth_pubsub::EthPubSubClient; -pub use self::net::NetClient; -pub use self::parity::ParityClient; #[cfg(any(test, feature = "accounts"))] pub use self::parity_accounts::ParityAccountsClient; -pub use self::parity_set::ParitySetClient; #[cfg(any(test, feature = "accounts"))] pub use self::parity_set::accounts::ParitySetAccountsClient; #[cfg(any(test, feature = "accounts"))] pub use self::personal::PersonalClient; -pub use self::private::PrivateClient; -pub use self::pubsub::PubSubClient; -pub use self::rpc::RpcClient; #[cfg(any(test, feature = "accounts"))] pub use self::secretstore::SecretStoreClient; -pub use self::signer::SignerClient; -pub use self::signing::SigningQueueClient; -pub use self::signing_unsafe::SigningUnsafeClient; -pub use self::traces::TracesClient; -pub use self::web3::Web3Client; +pub use self::{ + debug::DebugClient, + eth::{EthClient, EthClientOptions}, + eth_filter::EthFilterClient, + eth_pubsub::EthPubSubClient, + net::NetClient, + parity::ParityClient, + parity_set::ParitySetClient, + pubsub::PubSubClient, + signer::SignerClient, + signing::SigningQueueClient, + signing_unsafe::SigningUnsafeClient, + traces::TracesClient, + web3::Web3Client, +}; diff --git a/rpc/src/v1/impls/net.rs b/rpc/src/v1/impls/net.rs index 188d67cd5aa..779c47773bc 100644 --- a/rpc/src/v1/impls/net.rs +++ b/rpc/src/v1/impls/net.rs @@ -1,59 +1,64 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Net rpc implementation. -use std::sync::Arc; use jsonrpc_core::Result; +use std::sync::Arc; use sync::SyncProvider; use v1::traits::Net; /// Net rpc implementation. pub struct NetClient { - sync: Arc, - /// Cached `network_id`. - /// - /// We cache it to avoid redundant aquire of sync read lock. - /// https://github.com/paritytech/parity-ethereum/issues/8746 - network_id: u64, + sync: Arc, + /// Cached `network_id`. + /// + /// We cache it to avoid redundant aquire of sync read lock. + /// https://github.com/openethereum/openethereum/issues/8746 + network_id: u64, } -impl NetClient where S: SyncProvider { - /// Creates new NetClient. - pub fn new(sync: &Arc) -> Self { - NetClient { - sync: sync.clone(), - network_id: sync.status().network_id, - } - } +impl NetClient +where + S: SyncProvider, +{ + /// Creates new NetClient. + pub fn new(sync: &Arc) -> Self { + NetClient { + sync: sync.clone(), + network_id: sync.status().network_id, + } + } } -impl Net for NetClient where S: SyncProvider + 'static { - fn version(&self) -> Result { - Ok(format!("{}", self.network_id)) - } - - fn peer_count(&self) -> Result { - Ok(format!("{:#x}", self.sync.status().num_peers as u64)) - } +impl Net for NetClient +where + S: SyncProvider + 'static, +{ + fn version(&self) -> Result { + Ok(format!("{}", self.network_id)) + } - fn is_listening(&self) -> Result { - // right now (11 march 2016), we are always listening for incoming connections - // - // (this may not be true now -- 26 september 2016) - Ok(true) - } + fn peer_count(&self) -> Result { + Ok(format!("{:#x}", self.sync.status().num_peers as u64)) + } + fn is_listening(&self) -> Result { + // right now (11 march 2016), we are always listening for incoming connections + // + // (this may not be true now -- 26 september 2016) + Ok(true) + } } diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index 27a70379586..3c1c9c7629d 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -1,467 +1,500 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Parity-specific rpc implementation. -use std::sync::Arc; -use std::str::FromStr; -use std::collections::BTreeMap; +use std::{collections::BTreeMap, str::FromStr, sync::Arc}; use crypto::DEFAULT_MAC; -use ethereum_types::{Address, H64, H160, H256, H512, U64, U256}; -use ethcore::client::{BlockChainClient, StateClient, Call}; -use ethcore::miner::{self, MinerService}; -use ethcore::snapshot::{SnapshotService, RestorationStatus}; -use ethcore::state::StateInfo; +use ethcore::{ + client::{BlockChainClient, Call, StateClient}, + miner::{self, MinerService}, + snapshot::{RestorationStatus, SnapshotService}, + state::StateInfo, +}; use ethcore_logger::RotatingLogger; +use ethereum_types::{Address, H160, H256, H512, H64, U256, U64}; use ethkey::{crypto::ecies, Brain, Generator}; use ethstore::random_phrase; -use jsonrpc_core::futures::future; -use jsonrpc_core::{BoxFuture, Result}; -use sync::{SyncProvider, ManageNetwork}; +use jsonrpc_core::{futures::future, BoxFuture, Result}; +use stats::PrometheusMetrics; +use sync::{ManageNetwork, SyncProvider}; use types::ids::BlockId; -use updater::{Service as UpdateService}; use version::version_data; -use v1::helpers::block_import::is_major_importing; -use v1::helpers::{self, errors, fake_sign, ipfs, NetworkSettings, verify_signature}; -use v1::helpers::external_signer::{SigningQueue, SignerService}; -use v1::metadata::Metadata; -use v1::traits::Parity; -use v1::types::{ - Bytes, CallRequest, - Peers, Transaction, RpcSettings, Histogram, - TransactionStats, LocalTransactionStatus, - BlockNumber, ConsensusCapability, VersionInfo, - OperationsInfo, ChainStatus, Log, Filter, - RichHeader, Receipt, RecoveredAccount, - block_number_to_id +use v1::{ + helpers::{ + self, + block_import::is_major_importing, + errors, + external_signer::{SignerService, SigningQueue}, + fake_sign, verify_signature, NetworkSettings, + }, + metadata::Metadata, + traits::Parity, + types::{ + block_number_to_id, BlockNumber, Bytes, CallRequest, ChainStatus, Histogram, + LocalTransactionStatus, Peers, Receipt, RecoveredAccount, RichHeader, RpcSettings, + Transaction, TransactionStats, + }, }; use Host; /// Parity implementation. -pub struct ParityClient { - client: Arc, - miner: Arc, - updater: Arc, - sync: Arc, - net: Arc, - logger: Arc, - settings: Arc, - signer: Option>, - ws_address: Option, - snapshot: Option>, +pub struct ParityClient +where + C: PrometheusMetrics, +{ + client: Arc, + miner: Arc, + sync: Arc, + net: Arc, + logger: Arc, + settings: Arc, + signer: Option>, + ws_address: Option, + snapshot: Option>, } -impl ParityClient where - C: BlockChainClient, +impl ParityClient +where + C: BlockChainClient + PrometheusMetrics, { - /// Creates new `ParityClient`. - pub fn new( - client: Arc, - miner: Arc, - sync: Arc, - updater: Arc, - net: Arc, - logger: Arc, - settings: Arc, - signer: Option>, - ws_address: Option, - snapshot: Option>, - ) -> Self { - ParityClient { - client, - miner, - sync, - updater, - net, - logger, - settings, - signer, - ws_address, - snapshot, - } - } + /// Creates new `ParityClient`. + pub fn new( + client: Arc, + miner: Arc, + sync: Arc, + net: Arc, + logger: Arc, + settings: Arc, + signer: Option>, + ws_address: Option, + snapshot: Option>, + ) -> Self { + ParityClient { + client, + miner, + sync, + net, + logger, + settings, + signer, + ws_address, + snapshot, + } + } } -impl Parity for ParityClient where - S: StateInfo + 'static, - C: miner::BlockChainClient + BlockChainClient + StateClient + Call + 'static, - M: MinerService + 'static, - U: UpdateService + 'static, +impl Parity for ParityClient +where + S: StateInfo + 'static, + C: miner::BlockChainClient + + BlockChainClient + + PrometheusMetrics + + StateClient + + Call + + 'static, + M: MinerService + 'static, { - type Metadata = Metadata; - - fn transactions_limit(&self) -> Result { - Ok(self.miner.queue_status().limits.max_count) - } - - fn min_gas_price(&self) -> Result { - Ok(self.miner.queue_status().options.minimal_gas_price) - } - - fn extra_data(&self) -> Result { - Ok(Bytes::new(self.miner.authoring_params().extra_data)) - } - - fn gas_floor_target(&self) -> Result { - Ok(self.miner.authoring_params().gas_range_target.0) - } - - fn gas_ceil_target(&self) -> Result { - Ok(self.miner.authoring_params().gas_range_target.1) - } - - fn dev_logs(&self) -> Result> { - warn!("This method is deprecated and will be removed in future. See PR #10102"); - let logs = self.logger.logs(); - Ok(logs.as_slice().to_owned()) - } - - fn dev_logs_levels(&self) -> Result { - Ok(self.logger.levels().to_owned()) - } - - fn net_chain(&self) -> Result { - Ok(self.settings.chain.clone()) - } - - fn chain(&self) -> Result { - Ok(self.client.spec_name()) - } - - fn net_peers(&self) -> Result { - let sync_status = self.sync.status(); - let num_peers_range = self.net.num_peers_range(); - debug_assert!(num_peers_range.end() >= num_peers_range.start()); - let peers = self.sync.peers().into_iter().map(Into::into).collect(); - - Ok(Peers { - active: sync_status.num_active_peers, - connected: sync_status.num_peers, - max: sync_status.current_max_peers(*num_peers_range.start(), *num_peers_range.end()), - peers, - }) - } - - fn net_port(&self) -> Result { - Ok(self.settings.network_port) - } - - fn node_name(&self) -> Result { - Ok(self.settings.name.clone()) - } - - fn registry_address(&self) -> Result> { - Ok( - self.client - .additional_params() - .get("registrar") - .and_then(|s| Address::from_str(s).ok()) - ) - } - - fn rpc_settings(&self) -> Result { - Ok(RpcSettings { - enabled: self.settings.rpc_enabled, - interface: self.settings.rpc_interface.clone(), - port: self.settings.rpc_port as u64, - }) - } - - fn default_extra_data(&self) -> Result { - Ok(Bytes::new(version_data())) - } - - fn gas_price_histogram(&self) -> BoxFuture { - Box::new(future::done(self.client - .gas_price_corpus(100) - .histogram(10) - .ok_or_else(errors::not_enough_data) - .map(Into::into) - )) - } - - fn unsigned_transactions_count(&self) -> Result { - match self.signer { - None => Err(errors::signer_disabled()), - Some(ref signer) => Ok(signer.len()), - } - } - - fn generate_secret_phrase(&self) -> Result { - Ok(random_phrase(12)) - } - - fn phrase_to_address(&self, phrase: String) -> Result { - Ok(Brain::new(phrase).generate().expect("Brain::generate always returns Ok; qed").address()) - } - - fn list_accounts(&self, count: u64, after: Option, block_number: Option) -> Result>> { - let number = match block_number.unwrap_or_default() { - BlockNumber::Pending => { - warn!("BlockNumber::Pending is unsupported"); - return Ok(None); - }, - - num => block_number_to_id(num) - }; - - Ok(self.client - .list_accounts(number, after.map(Into::into).as_ref(), count) - .map(|a| a.into_iter().map(Into::into).collect())) - } - - fn list_storage_keys(&self, address: H160, count: u64, after: Option, block_number: Option) -> Result>> { - let number = match block_number.unwrap_or_default() { - BlockNumber::Pending => { - warn!("BlockNumber::Pending is unsupported"); - return Ok(None); - }, - - num => block_number_to_id(num) - }; - - Ok(self.client - .list_storage(number, &address, after.map(Into::into).as_ref(), count) - .map(|a| a.into_iter().map(Into::into).collect())) - } - - fn encrypt_message(&self, key: H512, phrase: Bytes) -> Result { - ecies::encrypt(&key, &DEFAULT_MAC, &phrase.0) - .map_err(errors::encryption) - .map(Into::into) - } - - fn pending_transactions(&self, limit: Option) -> Result> { - let ready_transactions = self.miner.ready_transactions( - &*self.client, - limit.unwrap_or_else(usize::max_value), - miner::PendingOrdering::Priority, - ); - - Ok(ready_transactions - .into_iter() - .map(|t| Transaction::from_pending(t.pending().clone())) - .collect() - ) - } - - fn all_transactions(&self) -> Result> { - let all_transactions = self.miner.queued_transactions(); - - Ok(all_transactions - .into_iter() - .map(|t| Transaction::from_pending(t.pending().clone())) - .collect() - ) - } - - fn all_transaction_hashes(&self) -> Result> { - Ok(self.miner.queued_transaction_hashes()) - } - - fn future_transactions(&self) -> Result> { - Err(errors::deprecated("Use `parity_allTransaction` instead.")) - } - - fn pending_transactions_stats(&self) -> Result> { - let stats = self.sync.transactions_stats(); - Ok(stats.into_iter() - .map(|(hash, stats)| (hash, stats.into())) - .collect() - ) - } - - fn local_transactions(&self) -> Result> { - let transactions = self.miner.local_transactions(); - Ok(transactions - .into_iter() - .map(|(hash, status)| (hash, LocalTransactionStatus::from(status))) - .collect() - ) - } - - fn ws_url(&self) -> Result { - helpers::to_url(&self.ws_address) - .ok_or_else(errors::ws_disabled) - } - - fn next_nonce(&self, address: H160) -> BoxFuture { - Box::new(future::ok(self.miner.next_nonce(&*self.client, &address))) - } - - fn mode(&self) -> Result { - Ok(self.client.mode().to_string()) - } - - fn enode(&self) -> Result { - self.sync.enode().ok_or_else(errors::network_disabled) - } - - fn consensus_capability(&self) -> Result { - Ok(self.updater.capability().into()) - } - - fn version_info(&self) -> Result { - Ok(self.updater.version_info().into()) - } - - fn releases_info(&self) -> Result> { - Ok(self.updater.info().map(Into::into)) - } - - fn chain_status(&self) -> Result { - let chain_info = self.client.chain_info(); - - let gap = chain_info.ancient_block_number.map(|x| U256::from(x + 1)) - .and_then(|first| chain_info.first_block_number.map(|last| (first, U256::from(last)))); - - Ok(ChainStatus { - block_gap: gap, - }) - } - - fn node_kind(&self) -> Result<::v1::types::NodeKind> { - use ::v1::types::{NodeKind, Availability, Capability}; - - Ok(NodeKind { - availability: Availability::Personal, - capability: Capability::Full, - }) - } - - fn block_header(&self, number: Option) -> BoxFuture { - const EXTRA_INFO_PROOF: &str = "Object exists in blockchain (fetched earlier), extra_info is always available if object exists; qed"; - let number = number.unwrap_or_default(); - - let (header, extra) = if number == BlockNumber::Pending { - let info = self.client.chain_info(); - let header = - try_bf!(self.miner.pending_block_header(info.best_block_number).ok_or_else(errors::unknown_block)); - - (header.encoded(), None) - } else { - let id = match number { - BlockNumber::Num(num) => BlockId::Number(num), - BlockNumber::Earliest => BlockId::Earliest, - BlockNumber::Latest => BlockId::Latest, - BlockNumber::Pending => unreachable!(), // Already covered - }; - - let header = try_bf!(self.client.block_header(id).ok_or_else(errors::unknown_block)); - let info = self.client.block_extra_info(id).expect(EXTRA_INFO_PROOF); - - (header, Some(info)) - }; - - Box::new(future::ok(RichHeader { - inner: header.into(), - extra_info: extra.unwrap_or_default(), - })) - } - - fn block_receipts(&self, number: Option) -> BoxFuture> { - let number = number.unwrap_or_default(); - - let id = match number { - BlockNumber::Pending => { - let info = self.client.chain_info(); - let receipts = try_bf!(self.miner.pending_receipts(info.best_block_number).ok_or_else(errors::unknown_block)); - return Box::new(future::ok(receipts - .into_iter() - .map(Into::into) - .collect() - )) - }, - BlockNumber::Num(num) => BlockId::Number(num), - BlockNumber::Earliest => BlockId::Earliest, - BlockNumber::Latest => BlockId::Latest, - }; - let receipts = try_bf!(self.client.localized_block_receipts(id).ok_or_else(errors::unknown_block)); - Box::new(future::ok(receipts.into_iter().map(Into::into).collect())) - } - - fn ipfs_cid(&self, content: Bytes) -> Result { - ipfs::cid(content) - } - - fn call(&self, requests: Vec, num: Option) -> Result> { - let requests = requests - .into_iter() - .map(|request| Ok(( - fake_sign::sign_call(request.into())?, - Default::default() - ))) - .collect::>>()?; - - let num = num.unwrap_or_default(); - - let (mut state, header) = if num == BlockNumber::Pending { - let info = self.client.chain_info(); - let state = self.miner.pending_state(info.best_block_number).ok_or_else(errors::state_pruned)?; - let header = self.miner.pending_block_header(info.best_block_number).ok_or_else(errors::state_pruned)?; - - (state, header) - } else { - let id = match num { - BlockNumber::Num(num) => BlockId::Number(num), - BlockNumber::Earliest => BlockId::Earliest, - BlockNumber::Latest => BlockId::Latest, - BlockNumber::Pending => unreachable!(), // Already covered - }; - - let state = self.client.state_at(id).ok_or_else(errors::state_pruned)?; - let header = self.client.block_header(id).ok_or_else(errors::state_pruned)?.decode().map_err(errors::decode)?; - - (state, header) - }; - - self.client.call_many(&requests, &mut state, &header) - .map(|res| res.into_iter().map(|res| res.output.into()).collect()) - .map_err(errors::call) - } - - fn submit_work_detail(&self, nonce: H64, pow_hash: H256, mix_hash: H256) -> Result { - helpers::submit_work_detail(&self.client, &self.miner, nonce, pow_hash, mix_hash) - } - - fn status(&self) -> Result<()> { - let has_peers = self.settings.is_dev_chain || self.sync.status().num_peers > 0; - let is_warping = match self.snapshot.as_ref().map(|s| s.status()) { - Some(RestorationStatus::Ongoing { .. }) => true, - _ => false, - }; - let is_not_syncing = - !is_warping && - !is_major_importing(Some(self.sync.status().state), self.client.queue_info()); - - if has_peers && is_not_syncing { - Ok(()) - } else { - Err(errors::status_error(has_peers)) - } - } - - fn logs_no_tx_hash(&self, filter: Filter) -> BoxFuture> { - use v1::impls::eth::base_logs; - // only specific impl for lightclient - base_logs(&*self.client, &*self.miner, filter) - } - - fn verify_signature(&self, is_prefixed: bool, message: Bytes, r: H256, s: H256, v: U64) -> Result { - verify_signature(is_prefixed, message, r, s, v, self.client.signing_chain_id()) - } + type Metadata = Metadata; + + fn transactions_limit(&self) -> Result { + Ok(self.miner.queue_status().limits.max_count) + } + + fn min_gas_price(&self) -> Result { + Ok(self.miner.queue_status().options.minimal_gas_price) + } + + fn extra_data(&self) -> Result { + Ok(Bytes::new(self.miner.authoring_params().extra_data)) + } + + fn gas_floor_target(&self) -> Result { + Ok(self.miner.authoring_params().gas_range_target.0) + } + + fn gas_ceil_target(&self) -> Result { + Ok(self.miner.authoring_params().gas_range_target.1) + } + + fn dev_logs(&self) -> Result> { + warn!("This method is deprecated and will be removed in future. See PR #10102"); + let logs = self.logger.logs(); + Ok(logs.as_slice().to_owned()) + } + + fn dev_logs_levels(&self) -> Result { + Ok(self.logger.levels().to_owned()) + } + + fn net_chain(&self) -> Result { + Ok(self.settings.chain.clone()) + } + + fn chain(&self) -> Result { + Ok(self.client.spec_name()) + } + + fn net_peers(&self) -> Result { + let sync_status = self.sync.status(); + let num_peers_range = self.net.num_peers_range(); + debug_assert!(num_peers_range.end() >= num_peers_range.start()); + let peers = self.sync.peers().into_iter().map(Into::into).collect(); + + Ok(Peers { + active: sync_status.num_active_peers, + connected: sync_status.num_peers, + max: sync_status.current_max_peers(*num_peers_range.start(), *num_peers_range.end()), + peers, + }) + } + + fn net_port(&self) -> Result { + Ok(self.settings.network_port) + } + + fn node_name(&self) -> Result { + Ok(self.settings.name.clone()) + } + + fn registry_address(&self) -> Result> { + Ok(self + .client + .additional_params() + .get("registrar") + .and_then(|s| Address::from_str(s).ok())) + } + + fn rpc_settings(&self) -> Result { + Ok(RpcSettings { + enabled: self.settings.rpc_enabled, + interface: self.settings.rpc_interface.clone(), + port: self.settings.rpc_port as u64, + }) + } + + fn default_extra_data(&self) -> Result { + Ok(Bytes::new(version_data())) + } + + fn gas_price_histogram(&self) -> BoxFuture { + Box::new(future::done( + self.client + .gas_price_corpus(100) + .histogram(10) + .ok_or_else(errors::not_enough_data) + .map(Into::into), + )) + } + + fn unsigned_transactions_count(&self) -> Result { + match self.signer { + None => Err(errors::signer_disabled()), + Some(ref signer) => Ok(signer.len()), + } + } + + fn generate_secret_phrase(&self) -> Result { + Ok(random_phrase(12)) + } + + fn phrase_to_address(&self, phrase: String) -> Result { + Ok(Brain::new(phrase) + .generate() + .expect("Brain::generate always returns Ok; qed") + .address()) + } + + fn list_accounts( + &self, + count: u64, + after: Option, + block_number: Option, + ) -> Result>> { + let number = match block_number.unwrap_or_default() { + BlockNumber::Pending => { + warn!("BlockNumber::Pending is unsupported"); + return Ok(None); + } + + num => block_number_to_id(num), + }; + + Ok(self + .client + .list_accounts(number, after.map(Into::into).as_ref(), count) + .map(|a| a.into_iter().map(Into::into).collect())) + } + + fn list_storage_keys( + &self, + address: H160, + count: u64, + after: Option, + block_number: Option, + ) -> Result>> { + let number = match block_number.unwrap_or_default() { + BlockNumber::Pending => { + warn!("BlockNumber::Pending is unsupported"); + return Ok(None); + } + + num => block_number_to_id(num), + }; + + Ok(self + .client + .list_storage(number, &address, after.map(Into::into).as_ref(), count) + .map(|a| a.into_iter().map(Into::into).collect())) + } + + fn encrypt_message(&self, key: H512, phrase: Bytes) -> Result { + ecies::encrypt(&key, &DEFAULT_MAC, &phrase.0) + .map_err(errors::encryption) + .map(Into::into) + } + + fn pending_transactions(&self, limit: Option) -> Result> { + let ready_transactions = self.miner.ready_transactions( + &*self.client, + limit.unwrap_or_else(usize::max_value), + miner::PendingOrdering::Priority, + ); + + Ok(ready_transactions + .into_iter() + .map(|t| Transaction::from_pending(t.pending().clone())) + .collect()) + } + + fn all_transactions(&self) -> Result> { + let all_transactions = self.miner.queued_transactions(); + + Ok(all_transactions + .into_iter() + .map(|t| Transaction::from_pending(t.pending().clone())) + .collect()) + } + + fn all_transaction_hashes(&self) -> Result> { + Ok(self.miner.queued_transaction_hashes()) + } + + fn future_transactions(&self) -> Result> { + Err(errors::deprecated("Use `parity_allTransaction` instead.")) + } + + fn pending_transactions_stats(&self) -> Result> { + let stats = self.sync.transactions_stats(); + Ok(stats + .into_iter() + .map(|(hash, stats)| (hash, stats.into())) + .collect()) + } + + fn local_transactions(&self) -> Result> { + let transactions = self.miner.local_transactions(); + Ok(transactions + .into_iter() + .map(|(hash, status)| (hash, LocalTransactionStatus::from(status))) + .collect()) + } + + fn ws_url(&self) -> Result { + helpers::to_url(&self.ws_address).ok_or_else(errors::ws_disabled) + } + + fn next_nonce(&self, address: H160) -> BoxFuture { + Box::new(future::ok(self.miner.next_nonce(&*self.client, &address))) + } + + fn mode(&self) -> Result { + Ok(self.client.mode().to_string()) + } + + fn enode(&self) -> Result { + self.sync.enode().ok_or_else(errors::network_disabled) + } + + fn chain_status(&self) -> Result { + let chain_info = self.client.chain_info(); + + let gap = chain_info + .ancient_block_number + .map(|x| U256::from(x + 1)) + .and_then(|first| { + chain_info + .first_block_number + .map(|last| (first, U256::from(last))) + }); + + Ok(ChainStatus { block_gap: gap }) + } + + fn node_kind(&self) -> Result<::v1::types::NodeKind> { + use v1::types::{Availability, Capability, NodeKind}; + + Ok(NodeKind { + availability: Availability::Personal, + capability: Capability::Full, + }) + } + + fn block_header(&self, number: Option) -> BoxFuture { + const EXTRA_INFO_PROOF: &str = "Object exists in blockchain (fetched earlier), extra_info is always available if object exists; qed"; + let number = number.unwrap_or_default(); + + let (header, extra) = if number == BlockNumber::Pending { + let info = self.client.chain_info(); + let header = try_bf!(self + .miner + .pending_block_header(info.best_block_number) + .ok_or_else(errors::unknown_block)); + + (header.encoded(), None) + } else { + let id = match number { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), + BlockNumber::Num(num) => BlockId::Number(num), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + BlockNumber::Pending => unreachable!(), // Already covered + }; + + let header = try_bf!(self + .client + .block_header(id) + .ok_or_else(errors::unknown_block)); + let info = self.client.block_extra_info(id).expect(EXTRA_INFO_PROOF); + + (header, Some(info)) + }; + + Box::new(future::ok(RichHeader { + inner: header.into(), + extra_info: extra.unwrap_or_default(), + })) + } + + fn block_receipts(&self, number: Option) -> BoxFuture> { + let number = number.unwrap_or_default(); + + let id = match number { + BlockNumber::Pending => { + let info = self.client.chain_info(); + let receipts = try_bf!(self + .miner + .pending_receipts(info.best_block_number) + .ok_or_else(errors::unknown_block)); + return Box::new(future::ok(receipts.into_iter().map(Into::into).collect())); + } + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), + BlockNumber::Num(num) => BlockId::Number(num), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + }; + let receipts = try_bf!(self + .client + .localized_block_receipts(id) + .ok_or_else(errors::unknown_block)); + Box::new(future::ok(receipts.into_iter().map(Into::into).collect())) + } + + fn call(&self, requests: Vec, num: Option) -> Result> { + let requests = requests + .into_iter() + .map(|request| Ok((fake_sign::sign_call(request.into())?, Default::default()))) + .collect::>>()?; + + let num = num.unwrap_or_default(); + + let (mut state, header) = if num == BlockNumber::Pending { + let info = self.client.chain_info(); + let state = self + .miner + .pending_state(info.best_block_number) + .ok_or_else(errors::state_pruned)?; + let header = self + .miner + .pending_block_header(info.best_block_number) + .ok_or_else(errors::state_pruned)?; + + (state, header) + } else { + let id = match num { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), + BlockNumber::Num(num) => BlockId::Number(num), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + BlockNumber::Pending => unreachable!(), // Already covered + }; + + let state = self.client.state_at(id).ok_or_else(errors::state_pruned)?; + let header = self + .client + .block_header(id) + .ok_or_else(errors::state_pruned)? + .decode() + .map_err(errors::decode)?; + + (state, header) + }; + + self.client + .call_many(&requests, &mut state, &header) + .map(|res| res.into_iter().map(|res| res.output.into()).collect()) + .map_err(errors::call) + } + + fn submit_work_detail(&self, nonce: H64, pow_hash: H256, mix_hash: H256) -> Result { + helpers::submit_work_detail(&self.client, &self.miner, nonce, pow_hash, mix_hash) + } + + fn status(&self) -> Result<()> { + let has_peers = self.settings.is_dev_chain || self.sync.status().num_peers > 0; + let is_warping = match self.snapshot.as_ref().map(|s| s.restoration_status()) { + Some(RestorationStatus::Ongoing { .. }) => true, + _ => false, + }; + let is_not_syncing = !is_warping + && !is_major_importing(Some(self.sync.status().state), self.client.queue_info()); + + if has_peers && is_not_syncing { + Ok(()) + } else { + Err(errors::status_error(has_peers)) + } + } + + fn verify_signature( + &self, + is_prefixed: bool, + message: Bytes, + r: H256, + s: H256, + v: U64, + ) -> Result { + verify_signature( + is_prefixed, + message, + r, + s, + v, + self.client.signing_chain_id(), + ) + } } diff --git a/rpc/src/v1/impls/parity_accounts.rs b/rpc/src/v1/impls/parity_accounts.rs index e52f8b7ac0d..053a8dd7102 100644 --- a/rpc/src/v1/impls/parity_accounts.rs +++ b/rpc/src/v1/impls/parity_accounts.rs @@ -1,367 +1,365 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Account management (personal) rpc implementation -use std::sync::Arc; -use std::collections::{ - btree_map::{BTreeMap, Entry}, - HashSet, +use std::{ + collections::{ + btree_map::{BTreeMap, Entry}, + HashSet, + }, + sync::Arc, }; +use accounts::AccountProvider; use ethereum_types::{Address, H160, H256, H520}; -use ethkey::{Brain, Generator, Secret}; +use ethkey::{Brain, Generator, Password, Secret}; use ethstore::KeyFile; -use accounts::AccountProvider; use jsonrpc_core::Result; -use v1::helpers::deprecated::{self, DeprecationNotice}; -use v1::helpers::errors; -use v1::traits::{ParityAccounts, ParityAccountsInfo}; -use v1::types::{Derive, DeriveHierarchical, DeriveHash,ExtAccountInfo, AccountInfo, HwAccountInfo}; -use ethkey::Password; +use v1::{ + helpers::{ + deprecated::{self, DeprecationNotice}, + errors, + }, + traits::{ParityAccounts, ParityAccountsInfo}, + types::{AccountInfo, Derive, DeriveHash, DeriveHierarchical, ExtAccountInfo}, +}; /// Account management (personal) rpc implementation. pub struct ParityAccountsClient { - accounts: Arc, - deprecation_notice: DeprecationNotice, + accounts: Arc, + deprecation_notice: DeprecationNotice, } impl ParityAccountsClient { - /// Creates new PersonalClient - pub fn new(store: &Arc) -> Self { - ParityAccountsClient { - accounts: store.clone(), - deprecation_notice: Default::default(), - } - } + /// Creates new PersonalClient + pub fn new(store: &Arc) -> Self { + ParityAccountsClient { + accounts: store.clone(), + deprecation_notice: Default::default(), + } + } } impl ParityAccountsClient { - fn deprecation_notice(&self, method: &'static str) { - self.deprecation_notice.print(method, deprecated::msgs::ACCOUNTS); - } + fn deprecation_notice(&self, method: &'static str) { + self.deprecation_notice + .print(method, deprecated::msgs::ACCOUNTS); + } } impl ParityAccountsInfo for ParityAccountsClient { - fn accounts_info(&self) -> Result> { - self.deprecation_notice("parity_accountsInfo"); - - let dapp_accounts = self.accounts.accounts() - .map_err(|e| errors::account("Could not fetch accounts.", e))? - .into_iter().collect::>(); - - let info = self.accounts.accounts_info().map_err(|e| errors::account("Could not fetch account info.", e))?; - let other = self.accounts.addresses_info(); - - Ok(info - .into_iter() - .chain(other.into_iter()) - .filter(|&(ref a, _)| dapp_accounts.contains(a)) - .map(|(a, v)| (H160::from(a), AccountInfo { name: v.name })) - .collect() - ) - } - - fn hardware_accounts_info(&self) -> Result> { - self.deprecation_notice("parity_hardwareAccountsInfo"); - - let info = self.accounts.hardware_accounts_info().map_err(|e| errors::account("Could not fetch account info.", e))?; - Ok(info - .into_iter() - .map(|(a, v)| (H160::from(a), HwAccountInfo { name: v.name, manufacturer: v.meta })) - .collect() - ) - } - - fn locked_hardware_accounts_info(&self) -> Result> { - self.deprecation_notice("parity_lockedHardwareAccountsInfo"); - - self.accounts.locked_hardware_accounts().map_err(|e| errors::account("Error communicating with hardware wallet.", e)) - } - - fn default_account(&self) -> Result { - self.deprecation_notice("parity_defaultAccount"); - - Ok(self.accounts.default_account() - .map(Into::into) - .ok() - .unwrap_or_default()) - } + fn accounts_info(&self) -> Result> { + self.deprecation_notice("parity_accountsInfo"); + + let dapp_accounts = self + .accounts + .accounts() + .map_err(|e| errors::account("Could not fetch accounts.", e))? + .into_iter() + .collect::>(); + + let info = self + .accounts + .accounts_info() + .map_err(|e| errors::account("Could not fetch account info.", e))?; + let other = self.accounts.addresses_info(); + + Ok(info + .into_iter() + .chain(other.into_iter()) + .filter(|&(ref a, _)| dapp_accounts.contains(a)) + .map(|(a, v)| (H160::from(a), AccountInfo { name: v.name })) + .collect()) + } + + fn default_account(&self) -> Result { + self.deprecation_notice("parity_defaultAccount"); + + Ok(self + .accounts + .default_account() + .map(Into::into) + .ok() + .unwrap_or_default()) + } } impl ParityAccounts for ParityAccountsClient { - fn all_accounts_info(&self) -> Result> { - let info = self.accounts.accounts_info().map_err(|e| errors::account("Could not fetch account info.", e))?; - let other = self.accounts.addresses_info(); - - let account_iter = info - .into_iter() - .chain(other.into_iter()) - .map(|(address, v)| (address.into(), ExtAccountInfo { - name: v.name, - meta: v.meta, - uuid: v.uuid.map(|uuid| uuid.to_string()) - })); - - let mut accounts: BTreeMap = BTreeMap::new(); - - for (address, account) in account_iter { - match accounts.entry(address) { - // Insert only if occupied entry isn't already an account with UUID - Entry::Occupied(ref mut occupied) if occupied.get().uuid.is_none() => { - occupied.insert(account); - }, - Entry::Vacant(vacant) => { - vacant.insert(account); - }, - _ => {} - }; - } - - Ok(accounts) - } - - fn new_account_from_phrase(&self, phrase: String, pass: Password) -> Result { - self.deprecation_notice("parity_newAccountFromPhrase"); - let brain = Brain::new(phrase).generate().unwrap(); - self.accounts.insert_account(brain.secret().clone(), &pass) - .map(Into::into) - .map_err(|e| errors::account("Could not create account.", e)) - } - - fn new_account_from_wallet(&self, json: String, pass: Password) -> Result { - self.deprecation_notice("parity_newAccountFromWallet"); - self.accounts.import_presale(json.as_bytes(), &pass) - .or_else(|_| self.accounts.import_wallet(json.as_bytes(), &pass, true)) - .map(Into::into) - .map_err(|e| errors::account("Could not create account.", e)) - } - - fn new_account_from_secret(&self, secret: H256, pass: Password) -> Result { - self.deprecation_notice("parity_newAccountFromSecret"); - let secret = Secret::from_unsafe_slice(&secret.0) - .map_err(|e| errors::account("Could not create account.", e))?; - self.accounts.insert_account(secret, &pass) - .map(Into::into) - .map_err(|e| errors::account("Could not create account.", e)) - } - - fn test_password(&self, account: H160, password: Password) -> Result { - self.deprecation_notice("parity_testPassword"); - let account: Address = account.into(); - - self.accounts - .test_password(&account, &password) - .map_err(|e| errors::account("Could not fetch account info.", e)) - } - - fn change_password(&self, account: H160, password: Password, new_password: Password) -> Result { - self.deprecation_notice("parity_changePassword"); - let account: Address = account.into(); - self.accounts - .change_password(&account, password, new_password) - .map(|_| true) - .map_err(|e| errors::account("Could not fetch account info.", e)) - } - - fn kill_account(&self, account: H160, password: Password) -> Result { - self.deprecation_notice("parity_killAccount"); - let account: Address = account.into(); - self.accounts - .kill_account(&account, &password) - .map(|_| true) - .map_err(|e| errors::account("Could not delete account.", e)) - } - - fn remove_address(&self, addr: H160) -> Result { - self.deprecation_notice("parity_removeAddresss"); - let addr: Address = addr.into(); - - self.accounts.remove_address(addr); - Ok(true) - } - - fn set_account_name(&self, addr: H160, name: String) -> Result { - self.deprecation_notice("parity_setAccountName"); - let addr: Address = addr.into(); - - self.accounts.set_account_name(addr.clone(), name.clone()) - .unwrap_or_else(|_| self.accounts.set_address_name(addr, name)); - Ok(true) - } - - fn set_account_meta(&self, addr: H160, meta: String) -> Result { - self.deprecation_notice("parity_setAccountMeta"); - let addr: Address = addr.into(); - - self.accounts.set_account_meta(addr.clone(), meta.clone()) - .unwrap_or_else(|_| self.accounts.set_address_meta(addr, meta)); - Ok(true) - } - - fn import_geth_accounts(&self, addresses: Vec) -> Result> { - self.deprecation_notice("parity_importGethAccounts"); - self.accounts - .import_geth_accounts(into_vec(addresses), false) - .map(into_vec) - .map_err(|e| errors::account("Couldn't import Geth accounts", e)) - } - - fn geth_accounts(&self) -> Result> { - self.deprecation_notice("parity_listGethAccounts"); - Ok(into_vec(self.accounts.list_geth_accounts(false))) - } - - fn create_vault(&self, name: String, password: Password) -> Result { - self.deprecation_notice("parity_newVault"); - - self.accounts - .create_vault(&name, &password) - .map_err(|e| errors::account("Could not create vault.", e)) - .map(|_| true) - } - - fn open_vault(&self, name: String, password: Password) -> Result { - self.deprecation_notice("parity_openVault"); - - self.accounts - .open_vault(&name, &password) - .map_err(|e| errors::account("Could not open vault.", e)) - .map(|_| true) - } - - fn close_vault(&self, name: String) -> Result { - self.deprecation_notice("parity_closeVault"); - - self.accounts - .close_vault(&name) - .map_err(|e| errors::account("Could not close vault.", e)) - .map(|_| true) - } - - fn list_vaults(&self) -> Result> { - self.deprecation_notice("parity_listVaults"); - - self.accounts - .list_vaults() - .map_err(|e| errors::account("Could not list vaults.", e)) - } - - fn list_opened_vaults(&self) -> Result> { - self.deprecation_notice("parity_listOpenedVaults"); - - self.accounts - .list_opened_vaults() - .map_err(|e| errors::account("Could not list vaults.", e)) - } - - fn change_vault_password(&self, name: String, new_password: Password) -> Result { - self.deprecation_notice("parity_changeVaultPassword"); - - self.accounts - .change_vault_password(&name, &new_password) - .map_err(|e| errors::account("Could not change vault password.", e)) - .map(|_| true) - } - - fn change_vault(&self, address: H160, new_vault: String) -> Result { - self.deprecation_notice("parity_changeVault"); - self.accounts - .change_vault(address.into(), &new_vault) - .map_err(|e| errors::account("Could not change vault.", e)) - .map(|_| true) - } - - fn get_vault_meta(&self, name: String) -> Result { - self.deprecation_notice("parity_getVaultMeta"); - - self.accounts - .get_vault_meta(&name) - .map_err(|e| errors::account("Could not get vault metadata.", e)) - } - - fn set_vault_meta(&self, name: String, meta: String) -> Result { - self.deprecation_notice("parity_setVaultMeta"); - - self.accounts - .set_vault_meta(&name, &meta) - .map_err(|e| errors::account("Could not update vault metadata.", e)) - .map(|_| true) - } - - fn derive_key_index(&self, addr: H160, password: Password, derivation: DeriveHierarchical, save_as_account: bool) -> Result { - self.deprecation_notice("parity_deriveAddressIndex"); - let addr: Address = addr.into(); - self.accounts - .derive_account( - &addr, - Some(password), - Derive::from(derivation).to_derivation() - .map_err(|c| errors::account("Could not parse derivation request: {:?}", c))?, - save_as_account) - .map(Into::into) - .map_err(|e| errors::account("Could not derive account.", e)) - } - - fn derive_key_hash(&self, addr: H160, password: Password, derivation: DeriveHash, save_as_account: bool) -> Result { - self.deprecation_notice("parity_deriveAddressHash"); - let addr: Address = addr.into(); - self.accounts - .derive_account( - &addr, - Some(password), - Derive::from(derivation).to_derivation() - .map_err(|c| errors::account("Could not parse derivation request: {:?}", c))?, - save_as_account) - .map(Into::into) - .map_err(|e| errors::account("Could not derive account.", e)) - } - - fn export_account(&self, addr: H160, password: Password) -> Result { - self.deprecation_notice("parity_exportAccount"); - let addr = addr.into(); - self.accounts - .export_account( - &addr, - password, - ) - .map(Into::into) - .map_err(|e| errors::account("Could not export account.", e)) - } - - fn sign_message(&self, addr: H160, password: Password, message: H256) -> Result { - self.deprecation_notice("parity_signMessage"); - self.accounts - .sign( - addr.into(), - Some(password), - message.into() - ) - .map(Into::into) - .map_err(|e| errors::account("Could not sign message.", e)) - } - - fn hardware_pin_matrix_ack(&self, path: String, pin: String) -> Result { - self.deprecation_notice("parity_hardwarePinMatrixAck"); - - self.accounts.hardware_pin_matrix_ack(&path, &pin).map_err(|e| errors::account("Error communicating with hardware wallet.", e)) - } -} - -fn into_vec(a: Vec) -> Vec where - A: Into -{ - a.into_iter().map(Into::into).collect() + fn all_accounts_info(&self) -> Result> { + let info = self + .accounts + .accounts_info() + .map_err(|e| errors::account("Could not fetch account info.", e))?; + let other = self.accounts.addresses_info(); + + let account_iter = info + .into_iter() + .chain(other.into_iter()) + .map(|(address, v)| { + ( + address.into(), + ExtAccountInfo { + name: v.name, + meta: v.meta, + uuid: v.uuid.map(|uuid| uuid.to_string()), + }, + ) + }); + + let mut accounts: BTreeMap = BTreeMap::new(); + + for (address, account) in account_iter { + match accounts.entry(address) { + // Insert only if occupied entry isn't already an account with UUID + Entry::Occupied(ref mut occupied) if occupied.get().uuid.is_none() => { + occupied.insert(account); + } + Entry::Vacant(vacant) => { + vacant.insert(account); + } + _ => {} + }; + } + + Ok(accounts) + } + + fn new_account_from_phrase(&self, phrase: String, pass: Password) -> Result { + self.deprecation_notice("parity_newAccountFromPhrase"); + let brain = Brain::new(phrase).generate().unwrap(); + self.accounts + .insert_account(brain.secret().clone(), &pass) + .map(Into::into) + .map_err(|e| errors::account("Could not create account.", e)) + } + + fn new_account_from_wallet(&self, json: String, pass: Password) -> Result { + self.deprecation_notice("parity_newAccountFromWallet"); + self.accounts + .import_presale(json.as_bytes(), &pass) + .or_else(|_| self.accounts.import_wallet(json.as_bytes(), &pass, true)) + .map(Into::into) + .map_err(|e| errors::account("Could not create account.", e)) + } + + fn new_account_from_secret(&self, secret: H256, pass: Password) -> Result { + self.deprecation_notice("parity_newAccountFromSecret"); + let secret = Secret::from_unsafe_slice(&secret.0) + .map_err(|e| errors::account("Could not create account.", e))?; + self.accounts + .insert_account(secret, &pass) + .map(Into::into) + .map_err(|e| errors::account("Could not create account.", e)) + } + + fn test_password(&self, account: H160, password: Password) -> Result { + self.deprecation_notice("parity_testPassword"); + let account: Address = account.into(); + + self.accounts + .test_password(&account, &password) + .map_err(|e| errors::account("Could not fetch account info.", e)) + } + + fn change_password( + &self, + account: H160, + password: Password, + new_password: Password, + ) -> Result { + self.deprecation_notice("parity_changePassword"); + let account: Address = account.into(); + self.accounts + .change_password(&account, password, new_password) + .map(|_| true) + .map_err(|e| errors::account("Could not fetch account info.", e)) + } + + fn kill_account(&self, account: H160, password: Password) -> Result { + self.deprecation_notice("parity_killAccount"); + let account: Address = account.into(); + self.accounts + .kill_account(&account, &password) + .map(|_| true) + .map_err(|e| errors::account("Could not delete account.", e)) + } + + fn remove_address(&self, addr: H160) -> Result { + self.deprecation_notice("parity_removeAddresss"); + let addr: Address = addr.into(); + + self.accounts.remove_address(addr); + Ok(true) + } + + fn set_account_name(&self, addr: H160, name: String) -> Result { + self.deprecation_notice("parity_setAccountName"); + let addr: Address = addr.into(); + + self.accounts + .set_account_name(addr.clone(), name.clone()) + .unwrap_or_else(|_| self.accounts.set_address_name(addr, name)); + Ok(true) + } + + fn set_account_meta(&self, addr: H160, meta: String) -> Result { + self.deprecation_notice("parity_setAccountMeta"); + let addr: Address = addr.into(); + + self.accounts + .set_account_meta(addr.clone(), meta.clone()) + .unwrap_or_else(|_| self.accounts.set_address_meta(addr, meta)); + Ok(true) + } + + fn create_vault(&self, name: String, password: Password) -> Result { + self.deprecation_notice("parity_newVault"); + + self.accounts + .create_vault(&name, &password) + .map_err(|e| errors::account("Could not create vault.", e)) + .map(|_| true) + } + + fn open_vault(&self, name: String, password: Password) -> Result { + self.deprecation_notice("parity_openVault"); + + self.accounts + .open_vault(&name, &password) + .map_err(|e| errors::account("Could not open vault.", e)) + .map(|_| true) + } + + fn close_vault(&self, name: String) -> Result { + self.deprecation_notice("parity_closeVault"); + + self.accounts + .close_vault(&name) + .map_err(|e| errors::account("Could not close vault.", e)) + .map(|_| true) + } + + fn list_vaults(&self) -> Result> { + self.deprecation_notice("parity_listVaults"); + + self.accounts + .list_vaults() + .map_err(|e| errors::account("Could not list vaults.", e)) + } + + fn list_opened_vaults(&self) -> Result> { + self.deprecation_notice("parity_listOpenedVaults"); + + self.accounts + .list_opened_vaults() + .map_err(|e| errors::account("Could not list vaults.", e)) + } + + fn change_vault_password(&self, name: String, new_password: Password) -> Result { + self.deprecation_notice("parity_changeVaultPassword"); + + self.accounts + .change_vault_password(&name, &new_password) + .map_err(|e| errors::account("Could not change vault password.", e)) + .map(|_| true) + } + + fn change_vault(&self, address: H160, new_vault: String) -> Result { + self.deprecation_notice("parity_changeVault"); + self.accounts + .change_vault(address.into(), &new_vault) + .map_err(|e| errors::account("Could not change vault.", e)) + .map(|_| true) + } + + fn get_vault_meta(&self, name: String) -> Result { + self.deprecation_notice("parity_getVaultMeta"); + + self.accounts + .get_vault_meta(&name) + .map_err(|e| errors::account("Could not get vault metadata.", e)) + } + + fn set_vault_meta(&self, name: String, meta: String) -> Result { + self.deprecation_notice("parity_setVaultMeta"); + + self.accounts + .set_vault_meta(&name, &meta) + .map_err(|e| errors::account("Could not update vault metadata.", e)) + .map(|_| true) + } + + fn derive_key_index( + &self, + addr: H160, + password: Password, + derivation: DeriveHierarchical, + save_as_account: bool, + ) -> Result { + self.deprecation_notice("parity_deriveAddressIndex"); + let addr: Address = addr.into(); + self.accounts + .derive_account( + &addr, + Some(password), + Derive::from(derivation) + .to_derivation() + .map_err(|c| errors::account("Could not parse derivation request: {:?}", c))?, + save_as_account, + ) + .map(Into::into) + .map_err(|e| errors::account("Could not derive account.", e)) + } + + fn derive_key_hash( + &self, + addr: H160, + password: Password, + derivation: DeriveHash, + save_as_account: bool, + ) -> Result { + self.deprecation_notice("parity_deriveAddressHash"); + let addr: Address = addr.into(); + self.accounts + .derive_account( + &addr, + Some(password), + Derive::from(derivation) + .to_derivation() + .map_err(|c| errors::account("Could not parse derivation request: {:?}", c))?, + save_as_account, + ) + .map(Into::into) + .map_err(|e| errors::account("Could not derive account.", e)) + } + + fn export_account(&self, addr: H160, password: Password) -> Result { + self.deprecation_notice("parity_exportAccount"); + let addr = addr.into(); + self.accounts + .export_account(&addr, password) + .map(Into::into) + .map_err(|e| errors::account("Could not export account.", e)) + } + + fn sign_message(&self, addr: H160, password: Password, message: H256) -> Result { + self.deprecation_notice("parity_signMessage"); + self.accounts + .sign(addr.into(), Some(password), message.into()) + .map(Into::into) + .map_err(|e| errors::account("Could not sign message.", e)) + } } diff --git a/rpc/src/v1/impls/parity_set.rs b/rpc/src/v1/impls/parity_set.rs index b7cef6c6b8c..aad0fbf8c6e 100644 --- a/rpc/src/v1/impls/parity_set.rs +++ b/rpc/src/v1/impls/parity_set.rs @@ -1,243 +1,238 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . /// Parity-specific rpc interface for operations altering the settings. use std::io; -use std::sync::Arc; -use std::time::Duration; +use std::{sync::Arc, time::Duration}; -use ethcore::client::{BlockChainClient, Mode}; -use ethcore::miner::{self, MinerService}; +use ethcore::{ + client::{BlockChainClient, Mode}, + miner::{self, MinerService}, +}; use ethereum_types::{H160, H256, U256}; use ethkey; use fetch::{self, Fetch}; use hash::keccak_buffer; use sync::ManageNetwork; -use updater::{Service as UpdateService}; -use jsonrpc_core::{BoxFuture, Result}; -use jsonrpc_core::futures::Future; -use v1::helpers::errors; -use v1::traits::ParitySet; -use v1::types::{Bytes, ReleaseInfo, Transaction}; +use jsonrpc_core::{futures::Future, BoxFuture, Result}; +use v1::{ + helpers::errors, + traits::ParitySet, + types::{Bytes, Transaction}, +}; #[cfg(any(test, feature = "accounts"))] pub mod accounts { - use super::*; - use accounts::AccountProvider; - use v1::traits::ParitySetAccounts; - use v1::helpers::deprecated::DeprecationNotice; - use v1::helpers::engine_signer::EngineSigner; - - /// Parity-specific account-touching RPC interfaces. - pub struct ParitySetAccountsClient { - miner: Arc, - accounts: Arc, - deprecation_notice: DeprecationNotice, - } - - impl ParitySetAccountsClient { - /// Creates new ParitySetAccountsClient - pub fn new( - accounts: &Arc, - miner: &Arc, - ) -> Self { - ParitySetAccountsClient { - accounts: accounts.clone(), - miner: miner.clone(), - deprecation_notice: Default::default(), - } - } - } - - impl ParitySetAccounts for ParitySetAccountsClient { - fn set_engine_signer(&self, address: H160, password: String) -> Result { - self.deprecation_notice.print( - "parity_setEngineSigner", - "use `parity_setEngineSignerSecret` instead. See #9997 for context." - ); - - let signer = Box::new(EngineSigner::new( - self.accounts.clone(), - address.clone().into(), - password.into(), - )); - self.miner.set_author(miner::Author::Sealer(signer)); - Ok(true) - } - } + use super::*; + use accounts::AccountProvider; + use v1::{ + helpers::{deprecated::DeprecationNotice, engine_signer::EngineSigner}, + traits::ParitySetAccounts, + }; + + /// Parity-specific account-touching RPC interfaces. + pub struct ParitySetAccountsClient { + miner: Arc, + accounts: Arc, + deprecation_notice: DeprecationNotice, + } + + impl ParitySetAccountsClient { + /// Creates new ParitySetAccountsClient + pub fn new(accounts: &Arc, miner: &Arc) -> Self { + ParitySetAccountsClient { + accounts: accounts.clone(), + miner: miner.clone(), + deprecation_notice: Default::default(), + } + } + } + + impl ParitySetAccounts for ParitySetAccountsClient { + fn set_engine_signer(&self, address: H160, password: String) -> Result { + self.deprecation_notice.print( + "parity_setEngineSigner", + "use `parity_setEngineSignerSecret` instead. See #9997 for context.", + ); + + let signer = Box::new(EngineSigner::new( + self.accounts.clone(), + address.clone().into(), + password.into(), + )); + self.miner.set_author(miner::Author::Sealer(signer)); + Ok(true) + } + } } /// Parity-specific rpc interface for operations altering the settings. -pub struct ParitySetClient { - client: Arc, - miner: Arc, - updater: Arc, - net: Arc, - fetch: F, +pub struct ParitySetClient { + client: Arc, + miner: Arc, + net: Arc, + fetch: F, } -impl ParitySetClient - where C: BlockChainClient + 'static, +impl ParitySetClient +where + C: BlockChainClient + 'static, { - /// Creates new `ParitySetClient` with given `Fetch`. - pub fn new( - client: &Arc, - miner: &Arc, - updater: &Arc, - net: &Arc, - fetch: F, - ) -> Self { - ParitySetClient { - client: client.clone(), - miner: miner.clone(), - updater: updater.clone(), - net: net.clone(), - fetch, - } - } + /// Creates new `ParitySetClient` with given `Fetch`. + pub fn new(client: &Arc, miner: &Arc, net: &Arc, fetch: F) -> Self { + ParitySetClient { + client: client.clone(), + miner: miner.clone(), + net: net.clone(), + fetch, + } + } } -impl ParitySet for ParitySetClient where - C: BlockChainClient + 'static, - M: MinerService + 'static, - U: UpdateService + 'static, - F: Fetch + 'static, +impl ParitySet for ParitySetClient +where + C: BlockChainClient + 'static, + M: MinerService + 'static, + F: Fetch + 'static, { - - fn set_min_gas_price(&self, gas_price: U256) -> Result { - match self.miner.set_minimal_gas_price(gas_price) { - Ok(success) => Ok(success), - Err(e) => Err(errors::unsupported(e, None)), - } - } - - fn set_transactions_limit(&self, _limit: usize) -> Result { - warn!("setTransactionsLimit is deprecated. Ignoring request."); - Ok(false) - } - - fn set_tx_gas_limit(&self, _limit: U256) -> Result { - warn!("setTxGasLimit is deprecated. Ignoring request."); - Ok(false) - } - - fn set_gas_floor_target(&self, target: U256) -> Result { - let mut range = self.miner.authoring_params().gas_range_target; - range.0 = target; - self.miner.set_gas_range_target(range); - Ok(true) - } - - fn set_gas_ceil_target(&self, target: U256) -> Result { - let mut range = self.miner.authoring_params().gas_range_target; - range.1 = target; - self.miner.set_gas_range_target(range); - Ok(true) - } - - fn set_extra_data(&self, extra_data: Bytes) -> Result { - self.miner.set_extra_data(extra_data.into_vec()); - Ok(true) - } - - fn set_author(&self, address: H160) -> Result { - self.miner.set_author(miner::Author::External(address)); - Ok(true) - } - - fn set_engine_signer_secret(&self, secret: H256) -> Result { - let keypair = ethkey::KeyPair::from_secret(secret.into()).map_err(|e| errors::account("Invalid secret", e))?; - self.miner.set_author(miner::Author::Sealer(ethcore::engines::signer::from_keypair(keypair))); - Ok(true) - } - - fn add_reserved_peer(&self, peer: String) -> Result { - match self.net.add_reserved_peer(peer) { - Ok(()) => Ok(true), - Err(e) => Err(errors::invalid_params("Peer address", e)), - } - } - - fn remove_reserved_peer(&self, peer: String) -> Result { - match self.net.remove_reserved_peer(peer) { - Ok(()) => Ok(true), - Err(e) => Err(errors::invalid_params("Peer address", e)), - } - } - - fn drop_non_reserved_peers(&self) -> Result { - self.net.deny_unreserved_peers(); - Ok(true) - } - - fn accept_non_reserved_peers(&self) -> Result { - self.net.accept_unreserved_peers(); - Ok(true) - } - - fn start_network(&self) -> Result { - self.net.start_network(); - Ok(true) - } - - fn stop_network(&self) -> Result { - self.net.stop_network(); - Ok(true) - } - - fn set_mode(&self, mode: String) -> Result { - self.client.set_mode(match mode.as_str() { - "offline" => Mode::Off, - "dark" => Mode::Dark(Duration::from_secs(300)), - "passive" => Mode::Passive(Duration::from_secs(300), Duration::from_secs(3600)), - "active" => Mode::Active, - e => { return Err(errors::invalid_params("mode", e.to_owned())); }, - }); - Ok(true) - } - - fn set_spec_name(&self, spec_name: String) -> Result { - self.client.set_spec_name(spec_name).map(|_| true).map_err(|()| errors::cannot_restart()) - } - - fn hash_content(&self, url: String) -> BoxFuture { - let future = self.fetch.get(&url, Default::default()).then(move |result| { - result - .map_err(errors::fetch) - .and_then(move |response| { - let mut reader = io::BufReader::new(fetch::BodyReader::new(response)); - keccak_buffer(&mut reader).map_err(errors::fetch) - }) - .map(Into::into) - }); - Box::new(future) - } - - fn upgrade_ready(&self) -> Result> { - Ok(self.updater.upgrade_ready().map(Into::into)) - } - - fn execute_upgrade(&self) -> Result { - Ok(self.updater.execute_upgrade()) - } - - fn remove_transaction(&self, hash: H256) -> Result> { - Ok(self.miner.remove_transaction(&hash) - .map(|t| Transaction::from_pending(t.pending().clone())) - ) - } + fn set_min_gas_price(&self, gas_price: U256) -> Result { + match self.miner.set_minimal_gas_price(gas_price) { + Ok(success) => Ok(success), + Err(e) => Err(errors::unsupported(e, None)), + } + } + + fn set_transactions_limit(&self, _limit: usize) -> Result { + warn!("setTransactionsLimit is deprecated. Ignoring request."); + Ok(false) + } + + fn set_tx_gas_limit(&self, _limit: U256) -> Result { + warn!("setTxGasLimit is deprecated. Ignoring request."); + Ok(false) + } + + fn set_gas_floor_target(&self, target: U256) -> Result { + let mut range = self.miner.authoring_params().gas_range_target; + range.0 = target; + self.miner.set_gas_range_target(range); + Ok(true) + } + + fn set_gas_ceil_target(&self, target: U256) -> Result { + let mut range = self.miner.authoring_params().gas_range_target; + range.1 = target; + self.miner.set_gas_range_target(range); + Ok(true) + } + + fn set_extra_data(&self, extra_data: Bytes) -> Result { + self.miner.set_extra_data(extra_data.into_vec()); + Ok(true) + } + + fn set_author(&self, address: H160) -> Result { + self.miner.set_author(miner::Author::External(address)); + Ok(true) + } + + fn set_engine_signer_secret(&self, secret: H256) -> Result { + let keypair = ethkey::KeyPair::from_secret(secret.into()) + .map_err(|e| errors::account("Invalid secret", e))?; + self.miner.set_author(miner::Author::Sealer( + ethcore::engines::signer::from_keypair(keypair), + )); + Ok(true) + } + + fn add_reserved_peer(&self, peer: String) -> Result { + match self.net.add_reserved_peer(peer) { + Ok(()) => Ok(true), + Err(e) => Err(errors::invalid_params("Peer address", e)), + } + } + + fn remove_reserved_peer(&self, peer: String) -> Result { + match self.net.remove_reserved_peer(peer) { + Ok(()) => Ok(true), + Err(e) => Err(errors::invalid_params("Peer address", e)), + } + } + + fn drop_non_reserved_peers(&self) -> Result { + self.net.deny_unreserved_peers(); + Ok(true) + } + + fn accept_non_reserved_peers(&self) -> Result { + self.net.accept_unreserved_peers(); + Ok(true) + } + + fn start_network(&self) -> Result { + self.net.start_network(); + Ok(true) + } + + fn stop_network(&self) -> Result { + self.net.stop_network(); + Ok(true) + } + + fn set_mode(&self, mode: String) -> Result { + self.client.set_mode(match mode.as_str() { + "offline" => Mode::Off, + "dark" => Mode::Dark(Duration::from_secs(300)), + "passive" => Mode::Passive(Duration::from_secs(300), Duration::from_secs(3600)), + "active" => Mode::Active, + e => { + return Err(errors::invalid_params("mode", e.to_owned())); + } + }); + Ok(true) + } + + fn set_spec_name(&self, spec_name: String) -> Result { + self.client + .set_spec_name(spec_name) + .map(|_| true) + .map_err(|()| errors::cannot_restart()) + } + + fn hash_content(&self, url: String) -> BoxFuture { + let future = self + .fetch + .get(&url, Default::default()) + .then(move |result| { + result + .map_err(errors::fetch) + .and_then(move |response| { + let mut reader = io::BufReader::new(fetch::BodyReader::new(response)); + keccak_buffer(&mut reader).map_err(errors::fetch) + }) + .map(Into::into) + }); + Box::new(future) + } + + fn remove_transaction(&self, hash: H256) -> Result> { + Ok(self + .miner + .remove_transaction(&hash) + .map(|t| Transaction::from_pending(t.pending().clone()))) + } } diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs index b6af1f81e5a..ce03eebdf36 100644 --- a/rpc/src/v1/impls/personal.rs +++ b/rpc/src/v1/impls/personal.rs @@ -1,270 +1,348 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Account management (personal) rpc implementation use std::sync::Arc; -use std::time::Duration; use accounts::AccountProvider; use bytes::Bytes; -use eip_712::{EIP712, hash_structured_data}; -use ethereum_types::{H160, H256, H520, U128, Address}; +use eip_712::{hash_structured_data, EIP712}; +use ethereum_types::{Address, H160, H256, H520, U128}; use ethkey::{public_to_address, recover, Signature}; use types::transaction::{PendingTransaction, SignedTransaction}; -use jsonrpc_core::futures::{future, Future}; -use jsonrpc_core::types::Value; -use jsonrpc_core::{BoxFuture, Result}; -use v1::helpers::deprecated::{self, DeprecationNotice}; -use v1::helpers::dispatch::{self, eth_data_hash, Dispatcher, SignWith, PostSign, WithToken}; -use v1::helpers::{errors, eip191}; -use v1::metadata::Metadata; -use v1::traits::Personal; -use v1::types::{ - Bytes as RpcBytes, - ConfirmationPayload as RpcConfirmationPayload, - ConfirmationResponse as RpcConfirmationResponse, - TransactionRequest, - RichRawTransaction as RpcRichRawTransaction, - EIP191Version, +use jsonrpc_core::{ + futures::{future, Future}, + types::Value, + BoxFuture, Result, +}; +use v1::{ + helpers::{ + deprecated::{self, DeprecationNotice}, + dispatch::{self, eth_data_hash, Dispatcher, PostSign, SignWith, WithToken}, + eip191, errors, + }, + metadata::Metadata, + traits::Personal, + types::{ + Bytes as RpcBytes, ConfirmationPayload as RpcConfirmationPayload, + ConfirmationResponse as RpcConfirmationResponse, EIP191Version, + RichRawTransaction as RpcRichRawTransaction, TransactionRequest, + }, }; /// Account management (personal) rpc implementation. pub struct PersonalClient { - accounts: Arc, - dispatcher: D, - allow_perm_unlock: bool, - allow_experimental_rpcs: bool, - deprecation_notice: DeprecationNotice, + accounts: Arc, + dispatcher: D, + allow_experimental_rpcs: bool, + deprecation_notice: DeprecationNotice, } impl PersonalClient { - /// Creates new PersonalClient - pub fn new( - accounts: &Arc, - dispatcher: D, - allow_perm_unlock: bool, - allow_experimental_rpcs: bool, - ) -> Self { - PersonalClient { - accounts: accounts.clone(), - dispatcher, - allow_perm_unlock, - allow_experimental_rpcs, - deprecation_notice: DeprecationNotice::default(), - } - } + /// Creates new PersonalClient + pub fn new( + accounts: &Arc, + dispatcher: D, + allow_experimental_rpcs: bool, + ) -> Self { + PersonalClient { + accounts: accounts.clone(), + dispatcher, + allow_experimental_rpcs, + deprecation_notice: DeprecationNotice::default(), + } + } } impl PersonalClient { - fn do_sign_transaction

( - &self, - _meta: Metadata, - request: TransactionRequest, - password: String, - post_sign: P - ) -> BoxFuture - where P: PostSign + 'static, - ::Future: Send - { - let dispatcher = self.dispatcher.clone(); - let accounts = self.accounts.clone(); - - let default = match request.from.as_ref() { - Some(account) => Ok(account.clone().into()), - None => accounts - .default_account() - .map_err(|e| errors::account("Cannot find default account.", e)), - }; - - let default = match default { - Ok(default) => default, - Err(e) => return Box::new(future::err(e)), - }; - - let accounts = Arc::new(dispatch::Signer::new(accounts)) as _; - Box::new(dispatcher.fill_optional_fields(request.into(), default, false) - .and_then(move |filled| { - dispatcher.sign(filled, &accounts, SignWith::Password(password.into()), post_sign) - }) - ) - } + fn do_sign_transaction

( + &self, + _meta: Metadata, + request: TransactionRequest, + password: String, + post_sign: P, + ) -> BoxFuture + where + P: PostSign + 'static, + ::Future: Send, + { + let dispatcher = self.dispatcher.clone(); + let accounts = self.accounts.clone(); + + let default = match request.from.as_ref() { + Some(account) => Ok(account.clone().into()), + None => accounts + .default_account() + .map_err(|e| errors::account("Cannot find default account.", e)), + }; + + let default = match default { + Ok(default) => default, + Err(e) => return Box::new(future::err(e)), + }; + + let accounts = Arc::new(dispatch::Signer::new(accounts)) as _; + Box::new( + dispatcher + .fill_optional_fields(request.into(), default, false) + .and_then(move |filled| { + dispatcher.sign( + filled, + &accounts, + SignWith::Password(password.into()), + post_sign, + ) + }), + ) + } } impl Personal for PersonalClient { - type Metadata = Metadata; - - fn accounts(&self) -> Result> { - self.deprecation_notice.print("personal_accounts", deprecated::msgs::ACCOUNTS); - let accounts = self.accounts.accounts().map_err(|e| errors::account("Could not fetch accounts.", e))?; - Ok(accounts.into_iter().map(Into::into).collect::>()) - } - - fn new_account(&self, pass: String) -> Result { - self.deprecation_notice.print("personal_newAccount", deprecated::msgs::ACCOUNTS); - self.accounts.new_account(&pass.into()) - .map(Into::into) - .map_err(|e| errors::account("Could not create account.", e)) - } - - fn unlock_account(&self, account: H160, account_pass: String, duration: Option) -> Result { - self.deprecation_notice.print("personal_unlockAccount", deprecated::msgs::ACCOUNTS); - let account: Address = account.into(); - let store = self.accounts.clone(); - let duration = match duration { - None => None, - Some(duration) => { - let duration: U128 = duration.into(); - let v = duration.low_u64() as u32; - if duration != v.into() { - return Err(errors::invalid_params("Duration", "Invalid Number")); - } else { - Some(v) - } - }, - }; - - let r = match (self.allow_perm_unlock, duration) { - (false, None) => store.unlock_account_temporarily(account, account_pass.into()), - (false, _) => return Err(errors::unsupported( - "Time-unlocking is not supported when permanent unlock is disabled.", - Some("Use personal_sendTransaction or enable permanent unlocking, instead."), - )), - (true, Some(0)) => store.unlock_account_permanently(account, account_pass.into()), - (true, Some(d)) => store.unlock_account_timed(account, account_pass.into(), Duration::from_secs(d.into())), - (true, None) => store.unlock_account_timed(account, account_pass.into(), Duration::from_secs(300)), - }; - match r { - Ok(_) => Ok(true), - Err(err) => Err(errors::account("Unable to unlock the account.", err)), - } - } - - fn sign(&self, data: RpcBytes, account: H160, password: String) -> BoxFuture { - self.deprecation_notice.print("personal_sign", deprecated::msgs::ACCOUNTS); - let dispatcher = self.dispatcher.clone(); - let accounts = Arc::new(dispatch::Signer::new(self.accounts.clone())) as _; - - let payload = RpcConfirmationPayload::EthSignMessage((account.clone(), data).into()); - - Box::new(dispatch::from_rpc(payload, account.into(), &dispatcher) - .and_then(move |payload| { - dispatch::execute(dispatcher, &accounts, payload, dispatch::SignWith::Password(password.into())) - }) - .map(|v| v.into_value()) - .then(|res| match res { - Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature), - Err(e) => Err(e), - e => Err(errors::internal("Unexpected result", e)), - })) - } - - fn sign_191(&self, version: EIP191Version, data: Value, account: H160, password: String) -> BoxFuture { - self.deprecation_notice.print("personal_sign191", deprecated::msgs::ACCOUNTS); - try_bf!(errors::require_experimental(self.allow_experimental_rpcs, "191")); - - let data = try_bf!(eip191::hash_message(version, data)); - let dispatcher = self.dispatcher.clone(); - let accounts = Arc::new(dispatch::Signer::new(self.accounts.clone())) as _; - - let payload = RpcConfirmationPayload::EIP191SignMessage((account.clone(), data.into()).into()); - - Box::new(dispatch::from_rpc(payload, account.into(), &dispatcher) - .and_then(move |payload| { - dispatch::execute(dispatcher, &accounts, payload, dispatch::SignWith::Password(password.into())) - }) - .map(|v| v.into_value()) - .then(|res| match res { - Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature), - Err(e) => Err(e), - e => Err(errors::internal("Unexpected result", e)), - }) - ) - } - - fn sign_typed_data(&self, typed_data: EIP712, account: H160, password: String) -> BoxFuture { - self.deprecation_notice.print("personal_signTypedData", deprecated::msgs::ACCOUNTS); - try_bf!(errors::require_experimental(self.allow_experimental_rpcs, "712")); - - let data = match hash_structured_data(typed_data) { - Ok(d) => d, - Err(err) => return Box::new(future::err(errors::invalid_call_data(err.kind()))), - }; - let dispatcher = self.dispatcher.clone(); - let accounts = Arc::new(dispatch::Signer::new(self.accounts.clone())) as _; - - let payload = RpcConfirmationPayload::EIP191SignMessage((account.clone(), data.into()).into()); - - Box::new(dispatch::from_rpc(payload, account.into(), &dispatcher) - .and_then(move |payload| { - dispatch::execute(dispatcher, &accounts, payload, dispatch::SignWith::Password(password.into())) - }) - .map(|v| v.into_value()) - .then(|res| match res { - Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature), - Err(e) => Err(e), - e => Err(errors::internal("Unexpected result", e)), - }) - ) - } - - fn ec_recover(&self, data: RpcBytes, signature: H520) -> BoxFuture { - let signature: H520 = signature.into(); - let signature = Signature::from_electrum(&signature); - let data: Bytes = data.into(); - - let hash = eth_data_hash(data); - let account = recover(&signature.into(), &hash) - .map_err(errors::encryption) - .map(|public| { - public_to_address(&public).into() - }); - - Box::new(future::done(account)) - } - - fn sign_transaction(&self, meta: Metadata, request: TransactionRequest, password: String) -> BoxFuture { - self.deprecation_notice.print("personal_signTransaction", deprecated::msgs::ACCOUNTS); - - let condition = request.condition.clone().map(Into::into); - let dispatcher = self.dispatcher.clone(); - Box::new(self.do_sign_transaction(meta, request, password, ()) - .map(move |tx| PendingTransaction::new(tx.into_value(), condition)) - .map(move |pending_tx| dispatcher.enrich(pending_tx.transaction))) - } - - fn send_transaction(&self, meta: Metadata, request: TransactionRequest, password: String) -> BoxFuture { - self.deprecation_notice.print("personal_sendTransaction", deprecated::msgs::ACCOUNTS); - let condition = request.condition.clone().map(Into::into); - let dispatcher = self.dispatcher.clone(); - Box::new( - self.do_sign_transaction(meta, request, password, move |signed: WithToken| { - dispatcher.dispatch_transaction( - PendingTransaction::new( - signed.into_value(), - condition - ) - ) - }) - ) - } - - fn sign_and_send_transaction(&self, meta: Metadata, request: TransactionRequest, password: String) -> BoxFuture { - self.deprecation_notice.print("personal_signAndSendTransaction", Some("use personal_sendTransaction instead.")); - warn!("Using deprecated personal_signAndSendTransaction, use personal_sendTransaction instead."); - self.send_transaction(meta, request, password) - } + type Metadata = Metadata; + + fn accounts(&self) -> Result> { + self.deprecation_notice + .print("personal_accounts", deprecated::msgs::ACCOUNTS); + let accounts = self + .accounts + .accounts() + .map_err(|e| errors::account("Could not fetch accounts.", e))?; + Ok(accounts.into_iter().map(Into::into).collect::>()) + } + + fn new_account(&self, pass: String) -> Result { + self.deprecation_notice + .print("personal_newAccount", deprecated::msgs::ACCOUNTS); + self.accounts + .new_account(&pass.into()) + .map(Into::into) + .map_err(|e| errors::account("Could not create account.", e)) + } + + fn unlock_account( + &self, + account: H160, + account_pass: String, + duration: Option, + ) -> Result { + self.deprecation_notice + .print("personal_unlockAccount", deprecated::msgs::ACCOUNTS); + let account: Address = account.into(); + let store = self.accounts.clone(); + let duration = match duration { + None => None, + Some(duration) => { + let duration: U128 = duration.into(); + let v = duration.low_u64() as u32; + if duration != v.into() { + return Err(errors::invalid_params("Duration", "Invalid Number")); + } else { + Some(v) + } + } + }; + + let r = match duration { + None => store.unlock_account_temporarily(account, account_pass.into()), + _ => { + return Err(errors::unsupported( + "Time-unlocking is not supported when permanent unlock is disabled.", + Some("Use personal_sendTransaction instead."), + )) + } + }; + match r { + Ok(_) => Ok(true), + Err(err) => Err(errors::account("Unable to unlock the account.", err)), + } + } + + fn sign(&self, data: RpcBytes, account: H160, password: String) -> BoxFuture { + self.deprecation_notice + .print("personal_sign", deprecated::msgs::ACCOUNTS); + let dispatcher = self.dispatcher.clone(); + let accounts = Arc::new(dispatch::Signer::new(self.accounts.clone())) as _; + + let payload = RpcConfirmationPayload::EthSignMessage((account.clone(), data).into()); + + Box::new( + dispatch::from_rpc(payload, account.into(), &dispatcher) + .and_then(move |payload| { + dispatch::execute( + dispatcher, + &accounts, + payload, + dispatch::SignWith::Password(password.into()), + ) + }) + .map(|v| v.into_value()) + .then(|res| match res { + Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature), + Err(e) => Err(e), + e => Err(errors::internal("Unexpected result", e)), + }), + ) + } + + fn sign_191( + &self, + version: EIP191Version, + data: Value, + account: H160, + password: String, + ) -> BoxFuture { + self.deprecation_notice + .print("personal_sign191", deprecated::msgs::ACCOUNTS); + try_bf!(errors::require_experimental( + self.allow_experimental_rpcs, + "191" + )); + + let data = try_bf!(eip191::hash_message(version, data)); + let dispatcher = self.dispatcher.clone(); + let accounts = Arc::new(dispatch::Signer::new(self.accounts.clone())) as _; + + let payload = + RpcConfirmationPayload::EIP191SignMessage((account.clone(), data.into()).into()); + + Box::new( + dispatch::from_rpc(payload, account.into(), &dispatcher) + .and_then(move |payload| { + dispatch::execute( + dispatcher, + &accounts, + payload, + dispatch::SignWith::Password(password.into()), + ) + }) + .map(|v| v.into_value()) + .then(|res| match res { + Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature), + Err(e) => Err(e), + e => Err(errors::internal("Unexpected result", e)), + }), + ) + } + + fn sign_typed_data( + &self, + typed_data: EIP712, + account: H160, + password: String, + ) -> BoxFuture { + self.deprecation_notice + .print("personal_signTypedData", deprecated::msgs::ACCOUNTS); + try_bf!(errors::require_experimental( + self.allow_experimental_rpcs, + "712" + )); + + let data = match hash_structured_data(typed_data) { + Ok(d) => d, + Err(err) => return Box::new(future::err(errors::invalid_call_data(err.kind()))), + }; + let dispatcher = self.dispatcher.clone(); + let accounts = Arc::new(dispatch::Signer::new(self.accounts.clone())) as _; + + let payload = + RpcConfirmationPayload::EIP191SignMessage((account.clone(), data.into()).into()); + + Box::new( + dispatch::from_rpc(payload, account.into(), &dispatcher) + .and_then(move |payload| { + dispatch::execute( + dispatcher, + &accounts, + payload, + dispatch::SignWith::Password(password.into()), + ) + }) + .map(|v| v.into_value()) + .then(|res| match res { + Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature), + Err(e) => Err(e), + e => Err(errors::internal("Unexpected result", e)), + }), + ) + } + + fn ec_recover(&self, data: RpcBytes, signature: H520) -> BoxFuture { + let signature: H520 = signature.into(); + let signature = Signature::from_electrum(&signature); + let data: Bytes = data.into(); + + let hash = eth_data_hash(data); + let account = recover(&signature.into(), &hash) + .map_err(errors::encryption) + .map(|public| public_to_address(&public).into()); + + Box::new(future::done(account)) + } + + fn sign_transaction( + &self, + meta: Metadata, + request: TransactionRequest, + password: String, + ) -> BoxFuture { + self.deprecation_notice + .print("personal_signTransaction", deprecated::msgs::ACCOUNTS); + + let condition = request.condition.clone().map(Into::into); + let dispatcher = self.dispatcher.clone(); + Box::new( + self.do_sign_transaction(meta, request, password, ()) + .map(move |tx| PendingTransaction::new(tx.into_value(), condition)) + .map(move |pending_tx| dispatcher.enrich(pending_tx.transaction)), + ) + } + + fn send_transaction( + &self, + meta: Metadata, + request: TransactionRequest, + password: String, + ) -> BoxFuture { + self.deprecation_notice + .print("personal_sendTransaction", deprecated::msgs::ACCOUNTS); + let condition = request.condition.clone().map(Into::into); + let dispatcher = self.dispatcher.clone(); + Box::new(self.do_sign_transaction( + meta, + request, + password, + move |signed: WithToken| { + dispatcher + .dispatch_transaction(PendingTransaction::new(signed.into_value(), condition)) + }, + )) + } + + fn sign_and_send_transaction( + &self, + meta: Metadata, + request: TransactionRequest, + password: String, + ) -> BoxFuture { + self.deprecation_notice.print( + "personal_signAndSendTransaction", + Some("use personal_sendTransaction instead."), + ); + warn!("Using deprecated personal_signAndSendTransaction, use personal_sendTransaction instead."); + self.send_transaction(meta, request, password) + } } diff --git a/rpc/src/v1/impls/private.rs b/rpc/src/v1/impls/private.rs deleted file mode 100644 index c3be3f91506..00000000000 --- a/rpc/src/v1/impls/private.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Private transaction signing RPC implementation. - -use std::sync::Arc; - -use rlp::Rlp; - -use ethcore_private_tx::Provider as PrivateTransactionManager; -use ethereum_types::{Address, H160, H256, U256}; -use types::transaction::SignedTransaction; - -use jsonrpc_core::{Error}; -use v1::types::{Bytes, PrivateTransactionReceipt, TransactionRequest, - BlockNumber, PrivateTransactionReceiptAndTransaction, CallRequest, block_number_to_id}; -use v1::traits::Private; -use v1::metadata::Metadata; -use v1::helpers::{errors, fake_sign}; - -/// Private transaction manager API endpoint implementation. -pub struct PrivateClient { - private: Option>, -} - -impl PrivateClient { - /// Creates a new instance. - pub fn new(private: Option>) -> Self { - PrivateClient { - private, - } - } - - fn unwrap_manager(&self) -> Result<&PrivateTransactionManager, Error> { - match self.private { - Some(ref arc) => Ok(&**arc), - None => Err(errors::light_unimplemented(None)), - } - } -} - -impl Private for PrivateClient { - type Metadata = Metadata; - - fn send_transaction(&self, request: Bytes) -> Result { - let signed_transaction = Rlp::new(&request.into_vec()).as_val() - .map_err(errors::rlp) - .and_then(|tx| SignedTransaction::new(tx).map_err(errors::transaction))?; - let client = self.unwrap_manager()?; - let receipt = client.create_private_transaction(signed_transaction).map_err(errors::private_message)?; - Ok(receipt.into()) - } - - fn compose_deployment_transaction(&self, block_number: BlockNumber, request: Bytes, validators: Vec, gas_price: U256) -> Result { - let signed_transaction = Rlp::new(&request.into_vec()).as_val() - .map_err(errors::rlp) - .and_then(|tx| SignedTransaction::new(tx).map_err(errors::transaction))?; - let client = self.unwrap_manager()?; - - let addresses: Vec

= validators.into_iter().map(Into::into).collect(); - let id = match block_number { - BlockNumber::Pending => return Err(errors::private_message_block_id_not_supported()), - num => block_number_to_id(num) - }; - - let (transaction, contract_address) = client - .public_creation_transaction(id, &signed_transaction, addresses.as_slice(), gas_price) - .map_err(errors::private_message)?; - let tx_hash = transaction.hash(None); - let request = TransactionRequest { - from: Some(signed_transaction.sender()), - to: None, - nonce: Some(transaction.nonce), - gas_price: Some(transaction.gas_price), - gas: Some(transaction.gas), - value: Some(transaction.value), - data: Some(transaction.data.into()), - condition: None, - }; - - Ok(PrivateTransactionReceiptAndTransaction { - transaction: request, - receipt: PrivateTransactionReceipt { - transaction_hash: tx_hash, - contract_address, - status_code: 0, - } - }) - } - - fn private_call(&self, block_number: BlockNumber, request: CallRequest) -> Result { - let id = match block_number { - BlockNumber::Pending => return Err(errors::private_message_block_id_not_supported()), - num => block_number_to_id(num) - }; - - let request = CallRequest::into(request); - let signed = fake_sign::sign_call(request)?; - let client = self.unwrap_manager()?; - let executed_result = client.private_call(id, &signed).map_err(errors::private_message)?; - Ok(executed_result.output.into()) - } - - fn private_contract_key(&self, contract_address: H160) -> Result { - let client = self.unwrap_manager()?; - let key = client.contract_key_id(&contract_address).map_err(errors::private_message)?; - Ok(key) - } -} diff --git a/rpc/src/v1/impls/pubsub.rs b/rpc/src/v1/impls/pubsub.rs index 1575aacdd67..7d39d1c14d2 100644 --- a/rpc/src/v1/impls/pubsub.rs +++ b/rpc/src/v1/impls/pubsub.rs @@ -1,106 +1,119 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -//! Parity-specific PUB-SUB rpc implementation. +//! OpenEthereum-specific PUB-SUB rpc implementation. -use std::sync::Arc; -use std::time::Duration; use parking_lot::RwLock; - -use jsonrpc_core::{self as core, Result, MetaIoHandler}; -use jsonrpc_core::futures::{future, Future, Stream, Sink}; -use jsonrpc_pubsub::typed::Subscriber; -use jsonrpc_pubsub::SubscriptionId; +use std::{sync::Arc, time::Duration}; + +use jsonrpc_core::{ + self as core, + futures::{future, Future, Sink, Stream}, + MetaIoHandler, Result, +}; +use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId}; use tokio_timer; use parity_runtime::Executor; -use v1::helpers::GenericPollManager; -use v1::metadata::Metadata; -use v1::traits::PubSub; +use v1::{helpers::GenericPollManager, metadata::Metadata, traits::PubSub}; /// Parity PubSub implementation. pub struct PubSubClient> { - poll_manager: Arc>>, - executor: Executor, + poll_manager: Arc>>, + executor: Executor, } impl> PubSubClient { - /// Creates new `PubSubClient`. - pub fn new(rpc: MetaIoHandler, executor: Executor) -> Self { - let poll_manager = Arc::new(RwLock::new(GenericPollManager::new(rpc))); - let pm2 = Arc::downgrade(&poll_manager); - - let timer = tokio_timer::wheel() - .tick_duration(Duration::from_millis(500)) - .build(); - - // Start ticking - let interval = timer.interval(Duration::from_millis(1000)); - executor.spawn(interval - .map_err(|e| warn!("Polling timer error: {:?}", e)) - .for_each(move |_| { - if let Some(pm2) = pm2.upgrade() { - pm2.read().tick() - } else { - Box::new(future::err(())) - } - }) - ); - - PubSubClient { - poll_manager, - executor, - } - } + /// Creates new `PubSubClient`. + pub fn new(rpc: MetaIoHandler, executor: Executor) -> Self { + let poll_manager = Arc::new(RwLock::new(GenericPollManager::new(rpc))); + let pm2 = Arc::downgrade(&poll_manager); + + let timer = tokio_timer::wheel() + .tick_duration(Duration::from_millis(500)) + .build(); + + // Start ticking + let interval = timer.interval(Duration::from_millis(1000)); + executor.spawn( + interval + .map_err(|e| warn!("Polling timer error: {:?}", e)) + .for_each(move |_| { + if let Some(pm2) = pm2.upgrade() { + pm2.read().tick() + } else { + Box::new(future::err(())) + } + }), + ); + + PubSubClient { + poll_manager, + executor, + } + } } impl PubSubClient { - /// Creates new `PubSubClient` with deterministic ids. - #[cfg(test)] - pub fn new_test(rpc: MetaIoHandler, executor: Executor) -> Self { - let client = Self::new(MetaIoHandler::with_middleware(Default::default()), executor); - *client.poll_manager.write() = GenericPollManager::new_test(rpc); - client - } + /// Creates new `PubSubClient` with deterministic ids. + #[cfg(test)] + pub fn new_test( + rpc: MetaIoHandler, + executor: Executor, + ) -> Self { + let client = Self::new(MetaIoHandler::with_middleware(Default::default()), executor); + *client.poll_manager.write() = GenericPollManager::new_test(rpc); + client + } } impl> PubSub for PubSubClient { - type Metadata = Metadata; - - fn parity_subscribe(&self, mut meta: Metadata, subscriber: Subscriber, method: String, params: Option) { - let params = params.unwrap_or_else(|| core::Params::Array(vec![])); - // Make sure to get rid of PubSub session otherwise it will never be dropped. - meta.session = None; - - let mut poll_manager = self.poll_manager.write(); - let (id, receiver) = poll_manager.subscribe(meta, method, params); - match subscriber.assign_id(id.clone()) { - Ok(sink) => { - self.executor.spawn(receiver.forward(sink.sink_map_err(|e| { - warn!("Cannot send notification: {:?}", e); - })).map(|_| ())); - }, - Err(_) => { - poll_manager.unsubscribe(&id); - }, - } - } - - fn parity_unsubscribe(&self, _: Option, id: SubscriptionId) -> Result { - let res = self.poll_manager.write().unsubscribe(&id); - Ok(res) - } + type Metadata = Metadata; + + fn parity_subscribe( + &self, + mut meta: Metadata, + subscriber: Subscriber, + method: String, + params: Option, + ) { + let params = params.unwrap_or_else(|| core::Params::Array(vec![])); + // Make sure to get rid of PubSub session otherwise it will never be dropped. + meta.session = None; + + let mut poll_manager = self.poll_manager.write(); + let (id, receiver) = poll_manager.subscribe(meta, method, params); + match subscriber.assign_id(id.clone()) { + Ok(sink) => { + self.executor.spawn( + receiver + .forward(sink.sink_map_err(|e| { + warn!("Cannot send notification: {:?}", e); + })) + .map(|_| ()), + ); + } + Err(_) => { + poll_manager.unsubscribe(&id); + } + } + } + + fn parity_unsubscribe(&self, _: Option, id: SubscriptionId) -> Result { + let res = self.poll_manager.write().unsubscribe(&id); + Ok(res) + } } diff --git a/rpc/src/v1/impls/rpc.rs b/rpc/src/v1/impls/rpc.rs deleted file mode 100644 index 0c2afd57caf..00000000000 --- a/rpc/src/v1/impls/rpc.rs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! RPC generic methods implementation. -use std::collections::BTreeMap; -use jsonrpc_core::Result; -use v1::traits::Rpc; - -/// RPC generic methods implementation. -pub struct RpcClient { - modules: BTreeMap, - valid_apis: Vec, -} - -impl RpcClient { - /// Creates new `RpcClient`. - pub fn new(modules: BTreeMap) -> Self { - // geth 1.3.6 fails upon receiving unknown api - let valid_apis = vec!["web3", "eth", "net", "personal", "rpc"]; - - RpcClient { - modules, - valid_apis: valid_apis.into_iter().map(ToOwned::to_owned).collect(), - } - } -} - -impl Rpc for RpcClient { - fn rpc_modules(&self) -> Result> { - let modules = self.modules.iter() - .fold(BTreeMap::new(), |mut map, (k, v)| { - map.insert(k.to_owned(), v.to_owned()); - map - }); - - Ok(modules) - } - - fn modules(&self) -> Result> { - let modules = self.modules.iter() - .filter(|&(k, _v)| { - self.valid_apis.contains(k) - }) - .fold(BTreeMap::new(), |mut map, (k, v)| { - map.insert(k.to_owned(), v.to_owned()); - map - }); - - Ok(modules) - } -} diff --git a/rpc/src/v1/impls/secretstore.rs b/rpc/src/v1/impls/secretstore.rs index b6526b85d5f..8844fd62be7 100644 --- a/rpc/src/v1/impls/secretstore.rs +++ b/rpc/src/v1/impls/secretstore.rs @@ -1,98 +1,123 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! SecretStore-specific rpc implementation. -use std::collections::BTreeSet; -use std::sync::Arc; +use std::{collections::BTreeSet, sync::Arc}; use accounts::AccountProvider; use crypto::DEFAULT_MAC; use ethereum_types::{H160, H256, H512}; use ethkey::Secret; -use jsonrpc_core::Result; -use v1::helpers::errors; -use v1::helpers::secretstore::{generate_document_key, encrypt_document, - decrypt_document, decrypt_document_with_shadow, ordered_servers_keccak}; -use v1::traits::SecretStore; -use v1::types::{Bytes, EncryptedDocumentKey}; use ethkey::Password; +use jsonrpc_core::Result; +use v1::{ + helpers::{ + errors, + secretstore::{ + decrypt_document, decrypt_document_with_shadow, encrypt_document, + generate_document_key, ordered_servers_keccak, + }, + }, + traits::SecretStore, + types::{Bytes, EncryptedDocumentKey}, +}; /// Parity implementation. pub struct SecretStoreClient { - accounts: Arc, + accounts: Arc, } impl SecretStoreClient { - /// Creates new SecretStoreClient - pub fn new(store: &Arc) -> Self { - SecretStoreClient { - accounts: store.clone(), - } - } - - /// Decrypt public key using account' private key - fn decrypt_key(&self, address: H160, password: Password, key: Bytes) -> Result> { - self.accounts.decrypt(address.into(), Some(password), &DEFAULT_MAC, &key.0) - .map_err(|e| errors::account("Could not decrypt key.", e)) - } - - /// Decrypt secret key using account' private key - fn decrypt_secret(&self, address: H160, password: Password, key: Bytes) -> Result { - self.decrypt_key(address, password, key) - .and_then(|s| Secret::from_unsafe_slice(&s).map_err(|e| errors::account("invalid secret", e))) - } + /// Creates new SecretStoreClient + pub fn new(store: &Arc) -> Self { + SecretStoreClient { + accounts: store.clone(), + } + } + + /// Decrypt public key using account' private key + fn decrypt_key(&self, address: H160, password: Password, key: Bytes) -> Result> { + self.accounts + .decrypt(address.into(), Some(password), &DEFAULT_MAC, &key.0) + .map_err(|e| errors::account("Could not decrypt key.", e)) + } + + /// Decrypt secret key using account' private key + fn decrypt_secret(&self, address: H160, password: Password, key: Bytes) -> Result { + self.decrypt_key(address, password, key).and_then(|s| { + Secret::from_unsafe_slice(&s).map_err(|e| errors::account("invalid secret", e)) + }) + } } impl SecretStore for SecretStoreClient { - fn generate_document_key(&self, address: H160, password: Password, server_key_public: H512) -> Result { - let account_public = self.accounts.account_public(address.into(), &password) - .map_err(|e| errors::account("Could not read account public.", e))?; - generate_document_key(account_public, server_key_public.into()) - } - - fn encrypt(&self, address: H160, password: Password, key: Bytes, data: Bytes) -> Result { - encrypt_document(self.decrypt_key(address, password, key)?, data.0) - .map(Into::into) - } - - fn decrypt(&self, address: H160, password: Password, key: Bytes, data: Bytes) -> Result { - decrypt_document(self.decrypt_key(address, password, key)?, data.0) - .map(Into::into) - } - - fn shadow_decrypt(&self, address: H160, password: Password, decrypted_secret: H512, common_point: H512, decrypt_shadows: Vec, data: Bytes) -> Result { - let mut shadows = Vec::with_capacity(decrypt_shadows.len()); - for decrypt_shadow in decrypt_shadows { - shadows.push(self.decrypt_secret(address.clone(), password.clone(), decrypt_shadow)?); - } - - decrypt_document_with_shadow(decrypted_secret.into(), common_point.into(), shadows, data.0) - .map(Into::into) - } - - fn servers_set_hash(&self, servers_set: BTreeSet) -> Result { - Ok(ordered_servers_keccak(servers_set)) - } - - fn sign_raw_hash(&self, address: H160, password: Password, raw_hash: H256) -> Result { - self.accounts - .sign(address.into(), Some(password), raw_hash.into()) - .map(|s| Bytes::new((*s).to_vec())) - .map_err(|e| errors::account("Could not sign raw hash.", e)) - } + fn generate_document_key( + &self, + address: H160, + password: Password, + server_key_public: H512, + ) -> Result { + let account_public = self + .accounts + .account_public(address.into(), &password) + .map_err(|e| errors::account("Could not read account public.", e))?; + generate_document_key(account_public, server_key_public.into()) + } + + fn encrypt(&self, address: H160, password: Password, key: Bytes, data: Bytes) -> Result { + encrypt_document(self.decrypt_key(address, password, key)?, data.0).map(Into::into) + } + + fn decrypt(&self, address: H160, password: Password, key: Bytes, data: Bytes) -> Result { + decrypt_document(self.decrypt_key(address, password, key)?, data.0).map(Into::into) + } + + fn shadow_decrypt( + &self, + address: H160, + password: Password, + decrypted_secret: H512, + common_point: H512, + decrypt_shadows: Vec, + data: Bytes, + ) -> Result { + let mut shadows = Vec::with_capacity(decrypt_shadows.len()); + for decrypt_shadow in decrypt_shadows { + shadows.push(self.decrypt_secret(address.clone(), password.clone(), decrypt_shadow)?); + } + + decrypt_document_with_shadow( + decrypted_secret.into(), + common_point.into(), + shadows, + data.0, + ) + .map(Into::into) + } + + fn servers_set_hash(&self, servers_set: BTreeSet) -> Result { + Ok(ordered_servers_keccak(servers_set)) + } + + fn sign_raw_hash(&self, address: H160, password: Password, raw_hash: H256) -> Result { + self.accounts + .sign(address.into(), Some(password), raw_hash.into()) + .map(|s| Bytes::new((*s).to_vec())) + .map_err(|e| errors::account("Could not sign raw hash.", e)) + } } diff --git a/rpc/src/v1/impls/signer.rs b/rpc/src/v1/impls/signer.rs index 4edac1144a4..b7dca6998a5 100644 --- a/rpc/src/v1/impls/signer.rs +++ b/rpc/src/v1/impls/signer.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Transactions Confirmations rpc implementation @@ -23,250 +23,346 @@ use ethkey; use parity_runtime::Executor; use parking_lot::Mutex; use rlp::Rlp; -use types::transaction::{SignedTransaction, PendingTransaction}; - -use jsonrpc_core::{Result, BoxFuture, Error}; -use jsonrpc_core::futures::{future, Future, IntoFuture}; -use jsonrpc_core::futures::future::Either; -use jsonrpc_pubsub::{SubscriptionId, typed::{Sink, Subscriber}}; -use v1::helpers::deprecated::{self, DeprecationNotice}; -use v1::helpers::dispatch::{self, Dispatcher, WithToken, eth_data_hash}; -use v1::helpers::{errors, ConfirmationPayload, FilledTransactionRequest, Subscribers}; -use v1::helpers::external_signer::{SigningQueue, SignerService}; -use v1::metadata::Metadata; -use v1::traits::Signer; -use v1::types::{TransactionModification, ConfirmationRequest, ConfirmationResponse, ConfirmationResponseWithToken, Bytes}; +use types::transaction::{PendingTransaction, SignedTransaction}; + +use jsonrpc_core::{ + futures::{future, future::Either, Future, IntoFuture}, + BoxFuture, Error, Result, +}; +use jsonrpc_pubsub::{ + typed::{Sink, Subscriber}, + SubscriptionId, +}; +use v1::{ + helpers::{ + deprecated::{self, DeprecationNotice}, + dispatch::{self, eth_data_hash, Dispatcher, WithToken}, + errors, + external_signer::{SignerService, SigningQueue}, + ConfirmationPayload, FilledTransactionRequest, Subscribers, + }, + metadata::Metadata, + traits::Signer, + types::{ + Bytes, ConfirmationRequest, ConfirmationResponse, ConfirmationResponseWithToken, + TransactionModification, + }, +}; /// Transactions confirmation (personal) rpc implementation. pub struct SignerClient { - signer: Arc, - accounts: Arc, - dispatcher: D, - subscribers: Arc>>>>, - deprecation_notice: DeprecationNotice, + signer: Arc, + accounts: Arc, + dispatcher: D, + subscribers: Arc>>>>, + deprecation_notice: DeprecationNotice, } impl SignerClient { - /// Create new instance of signer client. - pub fn new( - accounts: Arc, - dispatcher: D, - signer: &Arc, - executor: Executor, - ) -> Self { - let subscribers = Arc::new(Mutex::new(Subscribers::default())); - let subs = Arc::downgrade(&subscribers); - let s = Arc::downgrade(signer); - signer.queue().on_event(move |_event| { - if let (Some(s), Some(subs)) = (s.upgrade(), subs.upgrade()) { - let requests = s.requests().into_iter().map(Into::into).collect::>(); - for subscription in subs.lock().values() { - let subscription: &Sink<_> = subscription; - executor.spawn(subscription - .notify(Ok(requests.clone())) - .map(|_| ()) - .map_err(|e| warn!(target: "rpc", "Unable to send notification: {}", e)) - ); - } - } - }); - - SignerClient { - signer: signer.clone(), - accounts: accounts.clone(), - dispatcher, - subscribers, - deprecation_notice: Default::default(), - } - } - - fn confirm_internal(&self, id: U256, modification: TransactionModification, f: F) -> BoxFuture> where - F: FnOnce(D, &Arc, ConfirmationPayload) -> T, - T: IntoFuture, Error=Error>, - T::Future: Send + 'static - { - let dispatcher = self.dispatcher.clone(); - let signer = self.signer.clone(); - - Box::new(signer.take(&id).map(|sender| { - let mut payload = sender.request.payload.clone(); - // Modify payload - if let ConfirmationPayload::SendTransaction(ref mut request) = payload { - if let Some(sender) = modification.sender { - request.from = sender; - // Altering sender should always reset the nonce. - request.nonce = None; - } - if let Some(gas_price) = modification.gas_price { - request.gas_price = gas_price; - } - if let Some(gas) = modification.gas { - request.gas = gas; - } - if let Some(ref condition) = modification.condition { - request.condition = condition.clone().map(Into::into); - } - } - let fut = f(dispatcher, &self.accounts, payload); - Either::A(fut.into_future().then(move |result| { - // Execute - if let Ok(ref response) = result { - signer.request_confirmed(sender, Ok((*response).clone())); - } else { - signer.request_untouched(sender); - } - - result - })) - }) - .unwrap_or_else(|| Either::B(future::err(errors::invalid_params("Unknown RequestID", id))))) - } - - fn verify_transaction(bytes: Bytes, request: FilledTransactionRequest, process: F) -> Result where - F: FnOnce(PendingTransaction) -> Result, - { - let signed_transaction = Rlp::new(&bytes.0).as_val().map_err(errors::rlp)?; - let signed_transaction = SignedTransaction::new(signed_transaction).map_err(|e| errors::invalid_params("Invalid signature.", e))?; - let sender = signed_transaction.sender(); - - // Verification - let sender_matches = sender == request.from; - let data_matches = signed_transaction.data == request.data; - let value_matches = signed_transaction.value == request.value; - let nonce_matches = match request.nonce { - Some(nonce) => signed_transaction.nonce == nonce, - None => true, - }; - - // Dispatch if everything is ok - if sender_matches && data_matches && value_matches && nonce_matches { - let pending_transaction = PendingTransaction::new(signed_transaction, request.condition.map(Into::into)); - process(pending_transaction) - } else { - let mut error = Vec::new(); - if !sender_matches { error.push("from") } - if !data_matches { error.push("data") } - if !value_matches { error.push("value") } - if !nonce_matches { error.push("nonce") } - - Err(errors::invalid_params("Sent transaction does not match the request.", error)) - } - } + /// Create new instance of signer client. + pub fn new( + accounts: Arc, + dispatcher: D, + signer: &Arc, + executor: Executor, + ) -> Self { + let subscribers = Arc::new(Mutex::new(Subscribers::default())); + let subs = Arc::downgrade(&subscribers); + let s = Arc::downgrade(signer); + signer.queue().on_event(move |_event| { + if let (Some(s), Some(subs)) = (s.upgrade(), subs.upgrade()) { + let requests = s + .requests() + .into_iter() + .map(Into::into) + .collect::>(); + for subscription in subs.lock().values() { + let subscription: &Sink<_> = subscription; + executor.spawn( + subscription + .notify(Ok(requests.clone())) + .map(|_| ()) + .map_err( + |e| warn!(target: "rpc", "Unable to send notification: {}", e), + ), + ); + } + } + }); + + SignerClient { + signer: signer.clone(), + accounts: accounts.clone(), + dispatcher, + subscribers, + deprecation_notice: Default::default(), + } + } + + fn confirm_internal( + &self, + id: U256, + modification: TransactionModification, + f: F, + ) -> BoxFuture> + where + F: FnOnce(D, &Arc, ConfirmationPayload) -> T, + T: IntoFuture, Error = Error>, + T::Future: Send + 'static, + { + let dispatcher = self.dispatcher.clone(); + let signer = self.signer.clone(); + + Box::new( + signer + .take(&id) + .map(|sender| { + let mut payload = sender.request.payload.clone(); + // Modify payload + if let ConfirmationPayload::SendTransaction(ref mut request) = payload { + if let Some(sender) = modification.sender { + request.from = sender; + // Altering sender should always reset the nonce. + request.nonce = None; + } + if let Some(gas_price) = modification.gas_price { + request.gas_price = gas_price; + } + if let Some(gas) = modification.gas { + request.gas = gas; + } + if let Some(ref condition) = modification.condition { + request.condition = condition.clone().map(Into::into); + } + } + let fut = f(dispatcher, &self.accounts, payload); + Either::A(fut.into_future().then(move |result| { + // Execute + if let Ok(ref response) = result { + signer.request_confirmed(sender, Ok((*response).clone())); + } else { + signer.request_untouched(sender); + } + + result + })) + }) + .unwrap_or_else(|| { + Either::B(future::err(errors::invalid_params("Unknown RequestID", id))) + }), + ) + } + + fn verify_transaction( + bytes: Bytes, + request: FilledTransactionRequest, + process: F, + ) -> Result + where + F: FnOnce(PendingTransaction) -> Result, + { + let signed_transaction = Rlp::new(&bytes.0).as_val().map_err(errors::rlp)?; + let signed_transaction = SignedTransaction::new(signed_transaction) + .map_err(|e| errors::invalid_params("Invalid signature.", e))?; + let sender = signed_transaction.sender(); + + // Verification + let sender_matches = sender == request.from; + let data_matches = signed_transaction.data == request.data; + let value_matches = signed_transaction.value == request.value; + let nonce_matches = match request.nonce { + Some(nonce) => signed_transaction.nonce == nonce, + None => true, + }; + + // Dispatch if everything is ok + if sender_matches && data_matches && value_matches && nonce_matches { + let pending_transaction = + PendingTransaction::new(signed_transaction, request.condition.map(Into::into)); + process(pending_transaction) + } else { + let mut error = Vec::new(); + if !sender_matches { + error.push("from") + } + if !data_matches { + error.push("data") + } + if !value_matches { + error.push("value") + } + if !nonce_matches { + error.push("nonce") + } + + Err(errors::invalid_params( + "Sent transaction does not match the request.", + error, + )) + } + } } impl Signer for SignerClient { - type Metadata = Metadata; - - fn requests_to_confirm(&self) -> Result> { - self.deprecation_notice.print("signer_requestsToConfirm", deprecated::msgs::ACCOUNTS); - - Ok(self.signer.requests() - .into_iter() - .map(Into::into) - .collect() - ) - } - - // TODO [ToDr] TransactionModification is redundant for some calls - // might be better to replace it in future - fn confirm_request(&self, id: U256, modification: TransactionModification, pass: String) - -> BoxFuture - { - self.deprecation_notice.print("signer_confirmRequest", deprecated::msgs::ACCOUNTS); - - Box::new(self.confirm_internal(id, modification, move |dis, accounts, payload| { - dispatch::execute(dis, accounts, payload, dispatch::SignWith::Password(pass.into())) - }).map(dispatch::WithToken::into_value)) - } - - fn confirm_request_with_token(&self, id: U256, modification: TransactionModification, token: String) - -> BoxFuture - { - self.deprecation_notice.print("signer_confirmRequestWithToken", deprecated::msgs::ACCOUNTS); - - Box::new(self.confirm_internal(id, modification, move |dis, accounts, payload| { - dispatch::execute(dis, accounts, payload, dispatch::SignWith::Token(token.into())) - }).and_then(|v| match v { - WithToken::No(_) => Err(errors::internal("Unexpected response without token.", "")), - WithToken::Yes(response, token) => Ok(ConfirmationResponseWithToken { - result: response, - token, - }), - })) - } - - fn confirm_request_raw(&self, id: U256, bytes: Bytes) -> Result { - self.deprecation_notice.print("signer_confirmRequestRaw", deprecated::msgs::ACCOUNTS); - - self.signer.take(&id).map(|sender| { - let payload = sender.request.payload.clone(); - let result = match payload { - ConfirmationPayload::SendTransaction(request) => { - Self::verify_transaction(bytes, request, |pending_transaction| { - self.dispatcher.dispatch_transaction(pending_transaction) - .map(Into::into) - .map(ConfirmationResponse::SendTransaction) - }) - }, - ConfirmationPayload::SignTransaction(request) => { - Self::verify_transaction(bytes, request, |pending_transaction| { - let rich = self.dispatcher.enrich(pending_transaction.transaction); - Ok(ConfirmationResponse::SignTransaction(rich)) - }) - }, - ConfirmationPayload::EthSignMessage(address, data) => { - let expected_hash = eth_data_hash(data); - let signature = ethkey::Signature::from_electrum(&bytes.0); - match ethkey::verify_address(&address, &signature, &expected_hash) { - Ok(true) => Ok(ConfirmationResponse::Signature(bytes.0.as_slice().into())), - Ok(false) => Err(errors::invalid_params("Sender address does not match the signature.", ())), - Err(err) => Err(errors::invalid_params("Invalid signature received.", err)), - } - }, - ConfirmationPayload::SignMessage(address, hash) => { - let signature = ethkey::Signature::from_electrum(&bytes.0); - match ethkey::verify_address(&address, &signature, &hash) { - Ok(true) => Ok(ConfirmationResponse::Signature(bytes.0.as_slice().into())), - Ok(false) => Err(errors::invalid_params("Sender address does not match the signature.", ())), - Err(err) => Err(errors::invalid_params("Invalid signature received.", err)), - } - }, - ConfirmationPayload::Decrypt(_address, _data) => { - // TODO [ToDr]: Decrypt can we verify if the answer is correct? - Ok(ConfirmationResponse::Decrypt(bytes)) - }, - }; - if let Ok(ref response) = result { - self.signer.request_confirmed(sender, Ok(response.clone())); - } else { - self.signer.request_untouched(sender); - } - result - }).unwrap_or_else(|| Err(errors::invalid_params("Unknown RequestID", id))) - } - - fn reject_request(&self, id: U256) -> Result { - self.deprecation_notice.print("signer_rejectRequest", deprecated::msgs::ACCOUNTS); - - let res = self.signer.take(&id).map(|sender| self.signer.request_rejected(sender)); - Ok(res.is_some()) - } - - fn generate_token(&self) -> Result { - self.deprecation_notice.print("signer_generateAuthorizationToken", deprecated::msgs::ACCOUNTS); - - self.signer.generate_token() - .map_err(errors::token) - } - - fn subscribe_pending(&self, _meta: Self::Metadata, sub: Subscriber>) { - self.deprecation_notice.print("signer_subscribePending", deprecated::msgs::ACCOUNTS); - - self.subscribers.lock().push(sub) - } - - fn unsubscribe_pending(&self, _: Option, id: SubscriptionId) -> Result { - let res = self.subscribers.lock().remove(&id).is_some(); - Ok(res) - } + type Metadata = Metadata; + + fn requests_to_confirm(&self) -> Result> { + self.deprecation_notice + .print("signer_requestsToConfirm", deprecated::msgs::ACCOUNTS); + + Ok(self.signer.requests().into_iter().map(Into::into).collect()) + } + + // TODO [ToDr] TransactionModification is redundant for some calls + // might be better to replace it in future + fn confirm_request( + &self, + id: U256, + modification: TransactionModification, + pass: String, + ) -> BoxFuture { + self.deprecation_notice + .print("signer_confirmRequest", deprecated::msgs::ACCOUNTS); + + Box::new( + self.confirm_internal(id, modification, move |dis, accounts, payload| { + dispatch::execute( + dis, + accounts, + payload, + dispatch::SignWith::Password(pass.into()), + ) + }) + .map(dispatch::WithToken::into_value), + ) + } + + fn confirm_request_with_token( + &self, + id: U256, + modification: TransactionModification, + token: String, + ) -> BoxFuture { + self.deprecation_notice + .print("signer_confirmRequestWithToken", deprecated::msgs::ACCOUNTS); + + Box::new( + self.confirm_internal(id, modification, move |dis, accounts, payload| { + dispatch::execute( + dis, + accounts, + payload, + dispatch::SignWith::Token(token.into()), + ) + }) + .and_then(|v| match v { + WithToken::No(_) => Err(errors::internal("Unexpected response without token.", "")), + WithToken::Yes(response, token) => Ok(ConfirmationResponseWithToken { + result: response, + token, + }), + }), + ) + } + + fn confirm_request_raw(&self, id: U256, bytes: Bytes) -> Result { + self.deprecation_notice + .print("signer_confirmRequestRaw", deprecated::msgs::ACCOUNTS); + + self.signer + .take(&id) + .map(|sender| { + let payload = sender.request.payload.clone(); + let result = match payload { + ConfirmationPayload::SendTransaction(request) => { + Self::verify_transaction(bytes, request, |pending_transaction| { + self.dispatcher + .dispatch_transaction(pending_transaction) + .map(Into::into) + .map(ConfirmationResponse::SendTransaction) + }) + } + ConfirmationPayload::SignTransaction(request) => { + Self::verify_transaction(bytes, request, |pending_transaction| { + let rich = self.dispatcher.enrich(pending_transaction.transaction); + Ok(ConfirmationResponse::SignTransaction(rich)) + }) + } + ConfirmationPayload::EthSignMessage(address, data) => { + let expected_hash = eth_data_hash(data); + let signature = ethkey::Signature::from_electrum(&bytes.0); + match ethkey::verify_address(&address, &signature, &expected_hash) { + Ok(true) => { + Ok(ConfirmationResponse::Signature(bytes.0.as_slice().into())) + } + Ok(false) => Err(errors::invalid_params( + "Sender address does not match the signature.", + (), + )), + Err(err) => { + Err(errors::invalid_params("Invalid signature received.", err)) + } + } + } + ConfirmationPayload::SignMessage(address, hash) => { + let signature = ethkey::Signature::from_electrum(&bytes.0); + match ethkey::verify_address(&address, &signature, &hash) { + Ok(true) => { + Ok(ConfirmationResponse::Signature(bytes.0.as_slice().into())) + } + Ok(false) => Err(errors::invalid_params( + "Sender address does not match the signature.", + (), + )), + Err(err) => { + Err(errors::invalid_params("Invalid signature received.", err)) + } + } + } + ConfirmationPayload::Decrypt(_address, _data) => { + // TODO [ToDr]: Decrypt can we verify if the answer is correct? + Ok(ConfirmationResponse::Decrypt(bytes)) + } + }; + if let Ok(ref response) = result { + self.signer.request_confirmed(sender, Ok(response.clone())); + } else { + self.signer.request_untouched(sender); + } + result + }) + .unwrap_or_else(|| Err(errors::invalid_params("Unknown RequestID", id))) + } + + fn reject_request(&self, id: U256) -> Result { + self.deprecation_notice + .print("signer_rejectRequest", deprecated::msgs::ACCOUNTS); + + let res = self + .signer + .take(&id) + .map(|sender| self.signer.request_rejected(sender)); + Ok(res.is_some()) + } + + fn generate_token(&self) -> Result { + self.deprecation_notice.print( + "signer_generateAuthorizationToken", + deprecated::msgs::ACCOUNTS, + ); + + self.signer.generate_token().map_err(errors::token) + } + + fn subscribe_pending(&self, _meta: Self::Metadata, sub: Subscriber>) { + self.deprecation_notice + .print("signer_subscribePending", deprecated::msgs::ACCOUNTS); + + self.subscribers.lock().push(sub) + } + + fn unsubscribe_pending(&self, _: Option, id: SubscriptionId) -> Result { + let res = self.subscribers.lock().remove(&id).is_some(); + Ok(res) + } } diff --git a/rpc/src/v1/impls/signing.rs b/rpc/src/v1/impls/signing.rs index 38ca6d59c9c..3bb68910f19 100644 --- a/rpc/src/v1/impls/signing.rs +++ b/rpc/src/v1/impls/signing.rs @@ -1,49 +1,49 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Signing RPC implementation. +use parking_lot::Mutex; use std::sync::Arc; use transient_hashmap::TransientHashMap; -use parking_lot::Mutex; use ethereum_types::{H160, H256, H520, U256}; -use jsonrpc_core::{BoxFuture, Result, Error}; -use jsonrpc_core::futures::{future, Future, Poll, Async}; -use jsonrpc_core::futures::future::Either; - -use v1::helpers::deprecated::{self, DeprecationNotice}; -use v1::helpers::dispatch::{self, Dispatcher}; -use v1::helpers::errors; -use v1::helpers::external_signer::{ - SignerService, SigningQueue, - ConfirmationReceiver as RpcConfirmationReceiver, - ConfirmationResult as RpcConfirmationResult, +use jsonrpc_core::{ + futures::{future, future::Either, Async, Future, Poll}, + BoxFuture, Error, Result, }; -use v1::metadata::Metadata; -use v1::traits::{EthSigning, ParitySigning}; -use v1::types::{ - Bytes as RpcBytes, - Either as RpcEither, - RichRawTransaction as RpcRichRawTransaction, - TransactionRequest as RpcTransactionRequest, - ConfirmationPayload as RpcConfirmationPayload, - ConfirmationResponse as RpcConfirmationResponse, - Origin, + +use v1::{ + helpers::{ + deprecated::{self, DeprecationNotice}, + dispatch::{self, Dispatcher}, + errors, + external_signer::{ + ConfirmationReceiver as RpcConfirmationReceiver, + ConfirmationResult as RpcConfirmationResult, SignerService, SigningQueue, + }, + }, + metadata::Metadata, + traits::{EthSigning, ParitySigning}, + types::{ + Bytes as RpcBytes, ConfirmationPayload as RpcConfirmationPayload, + ConfirmationResponse as RpcConfirmationResponse, Either as RpcEither, Origin, + RichRawTransaction as RpcRichRawTransaction, TransactionRequest as RpcTransactionRequest, + }, }; use parity_runtime::Executor; @@ -53,200 +53,255 @@ const MAX_PENDING_DURATION_SEC: u32 = 60; #[must_use = "futures do nothing unless polled"] enum DispatchResult { - Future(U256, RpcConfirmationReceiver), - Value(RpcConfirmationResponse), + Future(U256, RpcConfirmationReceiver), + Value(RpcConfirmationResponse), } impl Future for DispatchResult { - type Item = RpcConfirmationResponse; - type Error = Error; - - fn poll(&mut self) -> Poll { - match *self { - DispatchResult::Value(ref response) => Ok(Async::Ready(response.clone())), - DispatchResult::Future(_uid, ref mut future) => try_ready!(future.poll()).map(Async::Ready), - } - } + type Item = RpcConfirmationResponse; + type Error = Error; + + fn poll(&mut self) -> Poll { + match *self { + DispatchResult::Value(ref response) => Ok(Async::Ready(response.clone())), + DispatchResult::Future(_uid, ref mut future) => { + try_ready!(future.poll()).map(Async::Ready) + } + } + } } -fn schedule(executor: Executor, - confirmations: Arc>>>, - id: U256, - future: RpcConfirmationReceiver) { - { - let mut confirmations = confirmations.lock(); - confirmations.insert(id, None); - } - - let future = future.then(move |result| { - let mut confirmations = confirmations.lock(); - confirmations.prune(); - let result = result.and_then(|response| response); - confirmations.insert(id, Some(result)); - Ok(()) - }); - executor.spawn(future); +fn schedule( + executor: Executor, + confirmations: Arc>>>, + id: U256, + future: RpcConfirmationReceiver, +) { + { + let mut confirmations = confirmations.lock(); + confirmations.insert(id, None); + } + + let future = future.then(move |result| { + let mut confirmations = confirmations.lock(); + confirmations.prune(); + let result = result.and_then(|response| response); + confirmations.insert(id, Some(result)); + Ok(()) + }); + executor.spawn(future); } /// Implementation of functions that require signing when no trusted signer is used. pub struct SigningQueueClient { - signer: Arc, - accounts: Arc, - dispatcher: D, - executor: Executor, - // None here means that the request hasn't yet been confirmed - confirmations: Arc>>>, - deprecation_notice: DeprecationNotice, + signer: Arc, + accounts: Arc, + dispatcher: D, + executor: Executor, + // None here means that the request hasn't yet been confirmed + confirmations: Arc>>>, + deprecation_notice: DeprecationNotice, } impl SigningQueueClient { - /// Creates a new signing queue client given shared signing queue. - pub fn new(signer: &Arc, dispatcher: D, executor: Executor, accounts: &Arc) -> Self { - SigningQueueClient { - signer: signer.clone(), - accounts: accounts.clone(), - dispatcher, - executor, - confirmations: Arc::new(Mutex::new(TransientHashMap::new(MAX_PENDING_DURATION_SEC))), - deprecation_notice: Default::default(), - } - } - - fn dispatch(&self, payload: RpcConfirmationPayload, origin: Origin) -> BoxFuture { - let default_account = self.accounts.default_account(); - let accounts = self.accounts.clone(); - let dispatcher = self.dispatcher.clone(); - let signer = self.signer.clone(); - Box::new(dispatch::from_rpc(payload, default_account, &dispatcher) - .and_then(move |payload| { - let sender = payload.sender(); - if accounts.is_unlocked(&sender) { - Either::A(dispatch::execute(dispatcher, &accounts, payload, dispatch::SignWith::Nothing) - .map(dispatch::WithToken::into_value) - .map(DispatchResult::Value)) - } else { - Either::B(future::done( - signer.add_request(payload, origin) - .map(|(id, future)| DispatchResult::Future(id, future)) - .map_err(|_| errors::request_rejected_limit()) - )) - } - })) - } + /// Creates a new signing queue client given shared signing queue. + pub fn new( + signer: &Arc, + dispatcher: D, + executor: Executor, + accounts: &Arc, + ) -> Self { + SigningQueueClient { + signer: signer.clone(), + accounts: accounts.clone(), + dispatcher, + executor, + confirmations: Arc::new(Mutex::new(TransientHashMap::new(MAX_PENDING_DURATION_SEC))), + deprecation_notice: Default::default(), + } + } + + fn dispatch( + &self, + payload: RpcConfirmationPayload, + origin: Origin, + ) -> BoxFuture { + let default_account = self.accounts.default_account(); + let accounts = self.accounts.clone(); + let dispatcher = self.dispatcher.clone(); + let signer = self.signer.clone(); + Box::new( + dispatch::from_rpc(payload, default_account, &dispatcher).and_then(move |payload| { + let sender = payload.sender(); + if accounts.is_unlocked(&sender) { + Either::A( + dispatch::execute( + dispatcher, + &accounts, + payload, + dispatch::SignWith::Nothing, + ) + .map(dispatch::WithToken::into_value) + .map(DispatchResult::Value), + ) + } else { + Either::B(future::done( + signer + .add_request(payload, origin) + .map(|(id, future)| DispatchResult::Future(id, future)) + .map_err(|_| errors::request_rejected_limit()), + )) + } + }), + ) + } } impl ParitySigning for SigningQueueClient { - type Metadata = Metadata; - - fn compose_transaction(&self, _meta: Metadata, transaction: RpcTransactionRequest) -> BoxFuture { - let default_account = self.accounts.default_account(); - Box::new(self.dispatcher.fill_optional_fields(transaction.into(), default_account, true).map(Into::into)) - } - - fn post_sign(&self, meta: Metadata, address: H160, data: RpcBytes) -> BoxFuture> { - self.deprecation_notice.print("parity_postSign", deprecated::msgs::ACCOUNTS); - let executor = self.executor.clone(); - let confirmations = self.confirmations.clone(); - - Box::new(self.dispatch( - RpcConfirmationPayload::EthSignMessage((address, data).into()), - meta.origin - ).map(move |result| match result { - DispatchResult::Value(v) => RpcEither::Or(v), - DispatchResult::Future(id, future) => { - schedule(executor, confirmations, id, future); - RpcEither::Either(id) - }, - })) - } - - fn post_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture> { - self.deprecation_notice.print("parity_postTransaction", deprecated::msgs::ACCOUNTS); - let executor = self.executor.clone(); - let confirmations = self.confirmations.clone(); - - Box::new(self.dispatch(RpcConfirmationPayload::SendTransaction(request), meta.origin) - .map(|result| match result { - DispatchResult::Value(v) => RpcEither::Or(v), - DispatchResult::Future(id, future) => { - schedule(executor, confirmations, id, future); - RpcEither::Either(id) - }, - })) - } - - fn check_request(&self, id: U256) -> Result> { - self.deprecation_notice.print("parity_checkRequest", deprecated::msgs::ACCOUNTS); - match self.confirmations.lock().get(&id) { - None => Err(errors::request_not_found()), // Request info has been dropped, or even never been there - Some(&None) => Ok(None), // No confirmation yet, request is known, confirmation is pending - Some(&Some(ref confirmation)) => confirmation.clone().map(Some), // Confirmation is there - } - } - - fn decrypt_message(&self, meta: Metadata, address: H160, data: RpcBytes) -> BoxFuture { - self.deprecation_notice.print("parity_decryptMessage", deprecated::msgs::ACCOUNTS); - let res = self.dispatch( - RpcConfirmationPayload::Decrypt((address, data).into()), - meta.origin, - ); - - // when dispatch is complete - wait for result and then - Box::new(res.flatten().and_then(move |response| { - match response { - RpcConfirmationResponse::Decrypt(data) => Ok(data), - e => Err(errors::internal("Unexpected result.", e)), - } - })) - } + type Metadata = Metadata; + + fn compose_transaction( + &self, + _meta: Metadata, + transaction: RpcTransactionRequest, + ) -> BoxFuture { + let default_account = self.accounts.default_account(); + Box::new( + self.dispatcher + .fill_optional_fields(transaction.into(), default_account, true) + .map(Into::into), + ) + } + + fn post_sign( + &self, + meta: Metadata, + address: H160, + data: RpcBytes, + ) -> BoxFuture> { + self.deprecation_notice + .print("parity_postSign", deprecated::msgs::ACCOUNTS); + let executor = self.executor.clone(); + let confirmations = self.confirmations.clone(); + + Box::new( + self.dispatch( + RpcConfirmationPayload::EthSignMessage((address, data).into()), + meta.origin, + ) + .map(move |result| match result { + DispatchResult::Value(v) => RpcEither::Or(v), + DispatchResult::Future(id, future) => { + schedule(executor, confirmations, id, future); + RpcEither::Either(id) + } + }), + ) + } + + fn post_transaction( + &self, + meta: Metadata, + request: RpcTransactionRequest, + ) -> BoxFuture> { + self.deprecation_notice + .print("parity_postTransaction", deprecated::msgs::ACCOUNTS); + let executor = self.executor.clone(); + let confirmations = self.confirmations.clone(); + + Box::new( + self.dispatch( + RpcConfirmationPayload::SendTransaction(request), + meta.origin, + ) + .map(|result| match result { + DispatchResult::Value(v) => RpcEither::Or(v), + DispatchResult::Future(id, future) => { + schedule(executor, confirmations, id, future); + RpcEither::Either(id) + } + }), + ) + } + + fn check_request(&self, id: U256) -> Result> { + self.deprecation_notice + .print("parity_checkRequest", deprecated::msgs::ACCOUNTS); + match self.confirmations.lock().get(&id) { + None => Err(errors::request_not_found()), // Request info has been dropped, or even never been there + Some(&None) => Ok(None), // No confirmation yet, request is known, confirmation is pending + Some(&Some(ref confirmation)) => confirmation.clone().map(Some), // Confirmation is there + } + } + + fn decrypt_message( + &self, + meta: Metadata, + address: H160, + data: RpcBytes, + ) -> BoxFuture { + self.deprecation_notice + .print("parity_decryptMessage", deprecated::msgs::ACCOUNTS); + let res = self.dispatch( + RpcConfirmationPayload::Decrypt((address, data).into()), + meta.origin, + ); + + // when dispatch is complete - wait for result and then + Box::new(res.flatten().and_then(move |response| match response { + RpcConfirmationResponse::Decrypt(data) => Ok(data), + e => Err(errors::internal("Unexpected result.", e)), + })) + } } impl EthSigning for SigningQueueClient { - type Metadata = Metadata; - - fn sign(&self, meta: Metadata, address: H160, data: RpcBytes) -> BoxFuture { - self.deprecation_notice.print("eth_sign", deprecated::msgs::ACCOUNTS); - let res = self.dispatch( - RpcConfirmationPayload::EthSignMessage((address, data).into()), - meta.origin, - ); - - Box::new(res.flatten().and_then(move |response| { - match response { - RpcConfirmationResponse::Signature(sig) => Ok(sig), - e => Err(errors::internal("Unexpected result.", e)), - } - })) - } - - fn send_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture { - self.deprecation_notice.print("eth_sendTransaction", deprecated::msgs::ACCOUNTS); - let res = self.dispatch( - RpcConfirmationPayload::SendTransaction(request), - meta.origin, - ); - - Box::new(res.flatten().and_then(move |response| { - match response { - RpcConfirmationResponse::SendTransaction(hash) => Ok(hash), - e => Err(errors::internal("Unexpected result.", e)), - } - })) - } - - fn sign_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture { - self.deprecation_notice.print("eth_signTransaction", deprecated::msgs::ACCOUNTS); - - let res = self.dispatch( - RpcConfirmationPayload::SignTransaction(request), - meta.origin, - ); - - Box::new(res.flatten().and_then(move |response| { - match response { - RpcConfirmationResponse::SignTransaction(tx) => Ok(tx), - e => Err(errors::internal("Unexpected result.", e)), - } - })) - } + type Metadata = Metadata; + + fn sign(&self, meta: Metadata, address: H160, data: RpcBytes) -> BoxFuture { + self.deprecation_notice + .print("eth_sign", deprecated::msgs::ACCOUNTS); + let res = self.dispatch( + RpcConfirmationPayload::EthSignMessage((address, data).into()), + meta.origin, + ); + + Box::new(res.flatten().and_then(move |response| match response { + RpcConfirmationResponse::Signature(sig) => Ok(sig), + e => Err(errors::internal("Unexpected result.", e)), + })) + } + + fn send_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture { + self.deprecation_notice + .print("eth_sendTransaction", deprecated::msgs::ACCOUNTS); + let res = self.dispatch( + RpcConfirmationPayload::SendTransaction(request), + meta.origin, + ); + + Box::new(res.flatten().and_then(move |response| match response { + RpcConfirmationResponse::SendTransaction(hash) => Ok(hash), + e => Err(errors::internal("Unexpected result.", e)), + })) + } + + fn sign_transaction( + &self, + meta: Metadata, + request: RpcTransactionRequest, + ) -> BoxFuture { + self.deprecation_notice + .print("eth_signTransaction", deprecated::msgs::ACCOUNTS); + + let res = self.dispatch( + RpcConfirmationPayload::SignTransaction(request), + meta.origin, + ); + + Box::new(res.flatten().and_then(move |response| match response { + RpcConfirmationResponse::SignTransaction(tx) => Ok(tx), + e => Err(errors::internal("Unexpected result.", e)), + })) + } } diff --git a/rpc/src/v1/impls/signing_unsafe.rs b/rpc/src/v1/impls/signing_unsafe.rs index f08a9ffbe6e..d515ef45183 100644 --- a/rpc/src/v1/impls/signing_unsafe.rs +++ b/rpc/src/v1/impls/signing_unsafe.rs @@ -1,136 +1,189 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Unsafe Signing RPC implementation. use std::sync::Arc; use ethereum_types::{Address, H160, H256, H520, U256}; -use jsonrpc_core::{BoxFuture, Result}; -use jsonrpc_core::futures::{future, Future}; -use v1::helpers::{errors}; -use v1::helpers::deprecated::{self, DeprecationNotice}; -use v1::helpers::dispatch::{self, Dispatcher}; -use v1::metadata::Metadata; -use v1::traits::{EthSigning, ParitySigning}; -use v1::types::{ - Bytes as RpcBytes, - Either as RpcEither, - RichRawTransaction as RpcRichRawTransaction, - TransactionRequest as RpcTransactionRequest, - ConfirmationPayload as RpcConfirmationPayload, - ConfirmationResponse as RpcConfirmationResponse, +use jsonrpc_core::{ + futures::{future, Future}, + BoxFuture, Result, +}; +use v1::{ + helpers::{ + deprecated::{self, DeprecationNotice}, + dispatch::{self, Dispatcher}, + errors, + }, + metadata::Metadata, + traits::{EthSigning, ParitySigning}, + types::{ + Bytes as RpcBytes, ConfirmationPayload as RpcConfirmationPayload, + ConfirmationResponse as RpcConfirmationResponse, Either as RpcEither, + RichRawTransaction as RpcRichRawTransaction, TransactionRequest as RpcTransactionRequest, + }, }; /// Implementation of functions that require signing when no trusted signer is used. pub struct SigningUnsafeClient { - accounts: Arc, - dispatcher: D, - deprecation_notice: DeprecationNotice, + accounts: Arc, + dispatcher: D, + deprecation_notice: DeprecationNotice, } impl SigningUnsafeClient { - /// Creates new SigningUnsafeClient. - pub fn new(accounts: &Arc, dispatcher: D) -> Self { - SigningUnsafeClient { - accounts: accounts.clone(), - dispatcher, - deprecation_notice: Default::default(), - } - } - - fn handle(&self, payload: RpcConfirmationPayload, account: Address) -> BoxFuture { - let accounts = self.accounts.clone(); - - let dis = self.dispatcher.clone(); - Box::new(dispatch::from_rpc(payload, account, &dis) - .and_then(move |payload| { - dispatch::execute(dis, &accounts, payload, dispatch::SignWith::Nothing) - }) - .map(dispatch::WithToken::into_value)) - } + /// Creates new SigningUnsafeClient. + pub fn new(accounts: &Arc, dispatcher: D) -> Self { + SigningUnsafeClient { + accounts: accounts.clone(), + dispatcher, + deprecation_notice: Default::default(), + } + } + + fn handle( + &self, + payload: RpcConfirmationPayload, + account: Address, + ) -> BoxFuture { + let accounts = self.accounts.clone(); + + let dis = self.dispatcher.clone(); + Box::new( + dispatch::from_rpc(payload, account, &dis) + .and_then(move |payload| { + dispatch::execute(dis, &accounts, payload, dispatch::SignWith::Nothing) + }) + .map(dispatch::WithToken::into_value), + ) + } } -impl EthSigning for SigningUnsafeClient -{ - type Metadata = Metadata; - - fn sign(&self, _: Metadata, address: H160, data: RpcBytes) -> BoxFuture { - self.deprecation_notice.print("eth_sign", deprecated::msgs::ACCOUNTS); - Box::new(self.handle(RpcConfirmationPayload::EthSignMessage((address, data).into()), address) - .then(|res| match res { - Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature), - Err(e) => Err(e), - e => Err(errors::internal("Unexpected result", e)), - })) - } - - fn send_transaction(&self, _meta: Metadata, request: RpcTransactionRequest) -> BoxFuture { - self.deprecation_notice.print("eth_sendTransaction", deprecated::msgs::ACCOUNTS); - Box::new(self.handle(RpcConfirmationPayload::SendTransaction(request), self.accounts.default_account()) - .then(|res| match res { - Ok(RpcConfirmationResponse::SendTransaction(hash)) => Ok(hash), - Err(e) => Err(e), - e => Err(errors::internal("Unexpected result", e)), - })) - } - - fn sign_transaction(&self, _meta: Metadata, request: RpcTransactionRequest) -> BoxFuture { - self.deprecation_notice.print("eth_signTransaction", deprecated::msgs::ACCOUNTS); - - Box::new(self.handle(RpcConfirmationPayload::SignTransaction(request), self.accounts.default_account()) - .then(|res| match res { - Ok(RpcConfirmationResponse::SignTransaction(tx)) => Ok(tx), - Err(e) => Err(e), - e => Err(errors::internal("Unexpected result", e)), - })) - } +impl EthSigning for SigningUnsafeClient { + type Metadata = Metadata; + + fn sign(&self, _: Metadata, address: H160, data: RpcBytes) -> BoxFuture { + self.deprecation_notice + .print("eth_sign", deprecated::msgs::ACCOUNTS); + Box::new( + self.handle( + RpcConfirmationPayload::EthSignMessage((address, data).into()), + address, + ) + .then(|res| match res { + Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature), + Err(e) => Err(e), + e => Err(errors::internal("Unexpected result", e)), + }), + ) + } + + fn send_transaction(&self, _meta: Metadata, request: RpcTransactionRequest) -> BoxFuture { + self.deprecation_notice + .print("eth_sendTransaction", deprecated::msgs::ACCOUNTS); + Box::new( + self.handle( + RpcConfirmationPayload::SendTransaction(request), + self.accounts.default_account(), + ) + .then(|res| match res { + Ok(RpcConfirmationResponse::SendTransaction(hash)) => Ok(hash), + Err(e) => Err(e), + e => Err(errors::internal("Unexpected result", e)), + }), + ) + } + + fn sign_transaction( + &self, + _meta: Metadata, + request: RpcTransactionRequest, + ) -> BoxFuture { + self.deprecation_notice + .print("eth_signTransaction", deprecated::msgs::ACCOUNTS); + + Box::new( + self.handle( + RpcConfirmationPayload::SignTransaction(request), + self.accounts.default_account(), + ) + .then(|res| match res { + Ok(RpcConfirmationResponse::SignTransaction(tx)) => Ok(tx), + Err(e) => Err(e), + e => Err(errors::internal("Unexpected result", e)), + }), + ) + } } impl ParitySigning for SigningUnsafeClient { - type Metadata = Metadata; - - fn compose_transaction(&self, _meta: Metadata, transaction: RpcTransactionRequest) -> BoxFuture { - let accounts = self.accounts.clone(); - let default_account = accounts.default_account(); - Box::new(self.dispatcher.fill_optional_fields(transaction.into(), default_account, true).map(Into::into)) - } - - fn decrypt_message(&self, _: Metadata, address: H160, data: RpcBytes) -> BoxFuture { - self.deprecation_notice.print("parity_decryptMessage", deprecated::msgs::ACCOUNTS); - Box::new(self.handle(RpcConfirmationPayload::Decrypt((address, data).into()), address) - .then(|res| match res { - Ok(RpcConfirmationResponse::Decrypt(data)) => Ok(data), - Err(e) => Err(e), - e => Err(errors::internal("Unexpected result", e)), - })) - } - - fn post_sign(&self, _: Metadata, _: H160, _: RpcBytes) -> BoxFuture> { - // We don't support this in non-signer mode. - Box::new(future::err(errors::signer_disabled())) - } - - fn post_transaction(&self, _: Metadata, _: RpcTransactionRequest) -> BoxFuture> { - // We don't support this in non-signer mode. - Box::new(future::err(errors::signer_disabled())) - } - - fn check_request(&self, _: U256) -> Result> { - // We don't support this in non-signer mode. - Err(errors::signer_disabled()) - } + type Metadata = Metadata; + + fn compose_transaction( + &self, + _meta: Metadata, + transaction: RpcTransactionRequest, + ) -> BoxFuture { + let accounts = self.accounts.clone(); + let default_account = accounts.default_account(); + Box::new( + self.dispatcher + .fill_optional_fields(transaction.into(), default_account, true) + .map(Into::into), + ) + } + + fn decrypt_message(&self, _: Metadata, address: H160, data: RpcBytes) -> BoxFuture { + self.deprecation_notice + .print("parity_decryptMessage", deprecated::msgs::ACCOUNTS); + Box::new( + self.handle( + RpcConfirmationPayload::Decrypt((address, data).into()), + address, + ) + .then(|res| match res { + Ok(RpcConfirmationResponse::Decrypt(data)) => Ok(data), + Err(e) => Err(e), + e => Err(errors::internal("Unexpected result", e)), + }), + ) + } + + fn post_sign( + &self, + _: Metadata, + _: H160, + _: RpcBytes, + ) -> BoxFuture> { + // We don't support this in non-signer mode. + Box::new(future::err(errors::signer_disabled())) + } + + fn post_transaction( + &self, + _: Metadata, + _: RpcTransactionRequest, + ) -> BoxFuture> { + // We don't support this in non-signer mode. + Box::new(future::err(errors::signer_disabled())) + } + + fn check_request(&self, _: U256) -> Result> { + // We don't support this in non-signer mode. + Err(errors::signer_disabled()) + } } diff --git a/rpc/src/v1/impls/traces.rs b/rpc/src/v1/impls/traces.rs index a6301eda539..810db8f8683 100644 --- a/rpc/src/v1/impls/traces.rs +++ b/rpc/src/v1/impls/traces.rs @@ -1,181 +1,273 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Traces api implementation. use std::sync::Arc; -use ethcore::client::{BlockChainClient, CallAnalytics, TransactionId, TraceId, StateClient, StateInfo, Call, BlockId}; +use ethcore::client::{ + BlockChainClient, BlockId, Call, CallAnalytics, StateClient, StateInfo, TraceId, TransactionId, +}; use ethereum_types::H256; use rlp::Rlp; use types::transaction::SignedTransaction; use jsonrpc_core::Result; -use v1::Metadata; -use v1::traits::Traces; -use v1::helpers::{errors, fake_sign}; -use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, - TraceResultsWithTransactionHash, TraceOptions, block_number_to_id}; +use v1::{ + helpers::{errors, fake_sign}, + traits::Traces, + types::{ + block_number_to_id, BlockNumber, Bytes, CallRequest, Index, LocalizedTrace, TraceFilter, + TraceOptions, TraceResults, TraceResultsWithTransactionHash, + }, + Metadata, +}; fn to_call_analytics(flags: TraceOptions) -> CallAnalytics { - CallAnalytics { - transaction_tracing: flags.contains(&("trace".to_owned())), - vm_tracing: flags.contains(&("vmTrace".to_owned())), - state_diffing: flags.contains(&("stateDiff".to_owned())), - } + CallAnalytics { + transaction_tracing: flags.contains(&("trace".to_owned())), + vm_tracing: flags.contains(&("vmTrace".to_owned())), + state_diffing: flags.contains(&("stateDiff".to_owned())), + } } /// Traces api implementation. pub struct TracesClient { - client: Arc, + client: Arc, } impl TracesClient { - /// Creates new Traces client. - pub fn new(client: &Arc) -> Self { - TracesClient { - client: client.clone(), - } - } + /// Creates new Traces client. + pub fn new(client: &Arc) -> Self { + TracesClient { + client: client.clone(), + } + } } -impl Traces for TracesClient where - S: StateInfo + 'static, - C: BlockChainClient + StateClient + Call + 'static +impl Traces for TracesClient +where + S: StateInfo + 'static, + C: BlockChainClient + StateClient + Call + 'static, { - type Metadata = Metadata; - - fn filter(&self, filter: TraceFilter) -> Result>> { - Ok(self.client.filter_traces(filter.into()) - .map(|traces| traces.into_iter().map(LocalizedTrace::from).collect())) - } - - fn block_traces(&self, block_number: BlockNumber) -> Result>> { - let id = match block_number { - BlockNumber::Pending => return Ok(None), - num => block_number_to_id(num) - }; - - Ok(self.client.block_traces(id) - .map(|traces| traces.into_iter().map(LocalizedTrace::from).collect())) - } - - fn transaction_traces(&self, transaction_hash: H256) -> Result>> { - Ok(self.client.transaction_traces(TransactionId::Hash(transaction_hash)) - .map(|traces| traces.into_iter().map(LocalizedTrace::from).collect())) - } - - fn trace(&self, transaction_hash: H256, address: Vec) -> Result> { - let id = TraceId { - transaction: TransactionId::Hash(transaction_hash), - address: address.into_iter().map(|i| i.value()).collect() - }; - - Ok(self.client.trace(id) - .map(LocalizedTrace::from)) - } - - fn call(&self, request: CallRequest, flags: TraceOptions, block: Option) -> Result { - let block = block.unwrap_or_default(); - - let request = CallRequest::into(request); - let signed = fake_sign::sign_call(request)?; - - let id = match block { - BlockNumber::Num(num) => BlockId::Number(num), - BlockNumber::Earliest => BlockId::Earliest, - BlockNumber::Latest => BlockId::Latest, - - BlockNumber::Pending => return Err(errors::invalid_params("`BlockNumber::Pending` is not supported", ())), - }; - - let mut state = self.client.state_at(id).ok_or_else(errors::state_pruned)?; - let header = self.client.block_header(id).ok_or_else(errors::state_pruned)?; - - self.client.call(&signed, to_call_analytics(flags), &mut state, &header.decode().map_err(errors::decode)?) - .map(TraceResults::from) - .map_err(errors::call) - } - - fn call_many(&self, requests: Vec<(CallRequest, TraceOptions)>, block: Option) -> Result> { - let block = block.unwrap_or_default(); - - let requests = requests.into_iter() - .map(|(request, flags)| { - let request = CallRequest::into(request); - let signed = fake_sign::sign_call(request)?; - Ok((signed, to_call_analytics(flags))) - }) - .collect::>>()?; - - let id = match block { - BlockNumber::Num(num) => BlockId::Number(num), - BlockNumber::Earliest => BlockId::Earliest, - BlockNumber::Latest => BlockId::Latest, - - BlockNumber::Pending => return Err(errors::invalid_params("`BlockNumber::Pending` is not supported", ())), - }; - - let mut state = self.client.state_at(id).ok_or_else(errors::state_pruned)?; - let header = self.client.block_header(id).ok_or_else(errors::state_pruned)?; - - self.client.call_many(&requests, &mut state, &header.decode().map_err(errors::decode)?) - .map(|results| results.into_iter().map(TraceResults::from).collect()) - .map_err(errors::call) - } - - fn raw_transaction(&self, raw_transaction: Bytes, flags: TraceOptions, block: Option) -> Result { - let block = block.unwrap_or_default(); - - let tx = Rlp::new(&raw_transaction.into_vec()).as_val().map_err(|e| errors::invalid_params("Transaction is not valid RLP", e))?; - let signed = SignedTransaction::new(tx).map_err(errors::transaction)?; - - let id = match block { - BlockNumber::Num(num) => BlockId::Number(num), - BlockNumber::Earliest => BlockId::Earliest, - BlockNumber::Latest => BlockId::Latest, - - BlockNumber::Pending => return Err(errors::invalid_params("`BlockNumber::Pending` is not supported", ())), - }; - - let mut state = self.client.state_at(id).ok_or_else(errors::state_pruned)?; - let header = self.client.block_header(id).ok_or_else(errors::state_pruned)?; - - self.client.call(&signed, to_call_analytics(flags), &mut state, &header.decode().map_err(errors::decode)?) - .map(TraceResults::from) - .map_err(errors::call) - } - - fn replay_transaction(&self, transaction_hash: H256, flags: TraceOptions) -> Result { - self.client.replay(TransactionId::Hash(transaction_hash), to_call_analytics(flags)) - .map(TraceResults::from) - .map_err(errors::call) - } - - fn replay_block_transactions(&self, block_number: BlockNumber, flags: TraceOptions) -> Result> { - let id = match block_number { - BlockNumber::Num(num) => BlockId::Number(num), - BlockNumber::Earliest => BlockId::Earliest, - BlockNumber::Latest => BlockId::Latest, - - BlockNumber::Pending => return Err(errors::invalid_params("`BlockNumber::Pending` is not supported", ())), - }; - - self.client.replay_block_transactions(id, to_call_analytics(flags)) - .map(|results| results.map(TraceResultsWithTransactionHash::from).collect()) - .map_err(errors::call) - } + type Metadata = Metadata; + + fn filter(&self, filter: TraceFilter) -> Result>> { + Ok(self + .client + .filter_traces(filter.into()) + .map(|traces| traces.into_iter().map(LocalizedTrace::from).collect())) + } + + fn block_traces(&self, block_number: BlockNumber) -> Result>> { + let id = match block_number { + BlockNumber::Pending => return Ok(None), + num => block_number_to_id(num), + }; + + Ok(self + .client + .block_traces(id) + .map(|traces| traces.into_iter().map(LocalizedTrace::from).collect())) + } + + fn transaction_traces(&self, transaction_hash: H256) -> Result>> { + Ok(self + .client + .transaction_traces(TransactionId::Hash(transaction_hash)) + .map(|traces| traces.into_iter().map(LocalizedTrace::from).collect())) + } + + fn trace(&self, transaction_hash: H256, address: Vec) -> Result> { + let id = TraceId { + transaction: TransactionId::Hash(transaction_hash), + address: address.into_iter().map(|i| i.value()).collect(), + }; + + Ok(self.client.trace(id).map(LocalizedTrace::from)) + } + + fn call( + &self, + request: CallRequest, + flags: TraceOptions, + block: Option, + ) -> Result { + let block = block.unwrap_or_default(); + + let request = CallRequest::into(request); + let signed = fake_sign::sign_call(request)?; + + let id = match block { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), + BlockNumber::Num(num) => BlockId::Number(num), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + + BlockNumber::Pending => { + return Err(errors::invalid_params( + "`BlockNumber::Pending` is not supported", + (), + )) + } + }; + + let mut state = self.client.state_at(id).ok_or_else(errors::state_pruned)?; + let header = self + .client + .block_header(id) + .ok_or_else(errors::state_pruned)?; + + self.client + .call( + &signed, + to_call_analytics(flags), + &mut state, + &header.decode().map_err(errors::decode)?, + ) + .map(TraceResults::from) + .map_err(errors::call) + } + + fn call_many( + &self, + requests: Vec<(CallRequest, TraceOptions)>, + block: Option, + ) -> Result> { + let block = block.unwrap_or_default(); + + let requests = requests + .into_iter() + .map(|(request, flags)| { + let request = CallRequest::into(request); + let signed = fake_sign::sign_call(request)?; + Ok((signed, to_call_analytics(flags))) + }) + .collect::>>()?; + + let id = match block { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), + BlockNumber::Num(num) => BlockId::Number(num), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + + BlockNumber::Pending => { + return Err(errors::invalid_params( + "`BlockNumber::Pending` is not supported", + (), + )) + } + }; + + let mut state = self.client.state_at(id).ok_or_else(errors::state_pruned)?; + let header = self + .client + .block_header(id) + .ok_or_else(errors::state_pruned)?; + + self.client + .call_many( + &requests, + &mut state, + &header.decode().map_err(errors::decode)?, + ) + .map(|results| results.into_iter().map(TraceResults::from).collect()) + .map_err(errors::call) + } + + fn raw_transaction( + &self, + raw_transaction: Bytes, + flags: TraceOptions, + block: Option, + ) -> Result { + let block = block.unwrap_or_default(); + + let tx = Rlp::new(&raw_transaction.into_vec()) + .as_val() + .map_err(|e| errors::invalid_params("Transaction is not valid RLP", e))?; + let signed = SignedTransaction::new(tx).map_err(errors::transaction)?; + + let id = match block { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), + BlockNumber::Num(num) => BlockId::Number(num), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + + BlockNumber::Pending => { + return Err(errors::invalid_params( + "`BlockNumber::Pending` is not supported", + (), + )) + } + }; + + let mut state = self.client.state_at(id).ok_or_else(errors::state_pruned)?; + let header = self + .client + .block_header(id) + .ok_or_else(errors::state_pruned)?; + + self.client + .call( + &signed, + to_call_analytics(flags), + &mut state, + &header.decode().map_err(errors::decode)?, + ) + .map(TraceResults::from) + .map_err(errors::call) + } + + fn replay_transaction( + &self, + transaction_hash: H256, + flags: TraceOptions, + ) -> Result { + self.client + .replay( + TransactionId::Hash(transaction_hash), + to_call_analytics(flags), + ) + .map(TraceResults::from) + .map_err(errors::call) + } + + fn replay_block_transactions( + &self, + block_number: BlockNumber, + flags: TraceOptions, + ) -> Result> { + let id = match block_number { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), + BlockNumber::Num(num) => BlockId::Number(num), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + + BlockNumber::Pending => { + return Err(errors::invalid_params( + "`BlockNumber::Pending` is not supported", + (), + )) + } + }; + + self.client + .replay_block_transactions(id, to_call_analytics(flags)) + .map(|results| results.map(TraceResultsWithTransactionHash::from).collect()) + .map_err(errors::call) + } } diff --git a/rpc/src/v1/impls/web3.rs b/rpc/src/v1/impls/web3.rs index 5ffda51b66c..54d5059bce8 100644 --- a/rpc/src/v1/impls/web3.rs +++ b/rpc/src/v1/impls/web3.rs @@ -1,37 +1,36 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Web3 rpc implementation. use ethereum_types::H256; use hash::keccak; use jsonrpc_core::Result; +use v1::{traits::Web3, types::Bytes}; use version::version; -use v1::traits::Web3; -use v1::types::Bytes; /// Web3 rpc implementation. #[derive(Default)] pub struct Web3Client; impl Web3 for Web3Client { - fn client_version(&self) -> Result { - Ok(version().to_owned().replacen("/", "//", 1)) - } + fn client_version(&self) -> Result { + Ok(version().to_owned().replacen("/", "//", 1)) + } - fn sha3(&self, data: Bytes) -> Result { - Ok(keccak(&data.0)) - } + fn sha3(&self, data: Bytes) -> Result { + Ok(keccak(&data.0)) + } } diff --git a/rpc/src/v1/informant.rs b/rpc/src/v1/informant.rs index 945378390b3..0b146969de2 100644 --- a/rpc/src/v1/informant.rs +++ b/rpc/src/v1/informant.rs @@ -1,30 +1,34 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! RPC Requests Statistics -use std::fmt; -use std::sync::Arc; -use std::sync::atomic::{self, AtomicUsize}; -use std::time; -use parity_runtime; use jsonrpc_core as core; use jsonrpc_core::futures::future::Either; use order_stat; +use parity_runtime; use parking_lot::RwLock; +use std::{ + fmt, + sync::{ + atomic::{self, AtomicUsize}, + Arc, + }, + time, +}; pub use self::parity_runtime::Executor; @@ -32,284 +36,291 @@ const RATE_SECONDS: usize = 10; const STATS_SAMPLES: usize = 60; struct RateCalculator { - era: time::Instant, - samples: [u16; RATE_SECONDS], + era: time::Instant, + samples: [u16; RATE_SECONDS], } impl fmt::Debug for RateCalculator { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "{} req/s", self.rate()) - } + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{} req/s", self.rate()) + } } impl Default for RateCalculator { - fn default() -> Self { - RateCalculator { - era: time::Instant::now(), - samples: [0; RATE_SECONDS], - } - } + fn default() -> Self { + RateCalculator { + era: time::Instant::now(), + samples: [0; RATE_SECONDS], + } + } } impl RateCalculator { - fn elapsed(&self) -> u64 { - self.era.elapsed().as_secs() - } - - pub fn tick(&mut self) -> u16 { - if self.elapsed() >= RATE_SECONDS as u64 { - self.era = time::Instant::now(); - self.samples[0] = 0; - } - - let pos = self.elapsed() as usize % RATE_SECONDS; - let next = (pos + 1) % RATE_SECONDS; - self.samples[next] = 0; - self.samples[pos] = self.samples[pos].saturating_add(1); - self.samples[pos] - } - - fn current_rate(&self) -> usize { - let now = match self.elapsed() { - i if i >= RATE_SECONDS as u64 => RATE_SECONDS, - i => i as usize + 1, - }; - let sum: usize = self.samples[0..now].iter().map(|x| *x as usize).sum(); - sum / now - } - - pub fn rate(&self) -> usize { - if self.elapsed() > RATE_SECONDS as u64 { - 0 - } else { - self.current_rate() - } - } + fn elapsed(&self) -> u64 { + self.era.elapsed().as_secs() + } + + pub fn tick(&mut self) -> u16 { + if self.elapsed() >= RATE_SECONDS as u64 { + self.era = time::Instant::now(); + self.samples[0] = 0; + } + + let pos = self.elapsed() as usize % RATE_SECONDS; + let next = (pos + 1) % RATE_SECONDS; + self.samples[next] = 0; + self.samples[pos] = self.samples[pos].saturating_add(1); + self.samples[pos] + } + + fn current_rate(&self) -> usize { + let now = match self.elapsed() { + i if i >= RATE_SECONDS as u64 => RATE_SECONDS, + i => i as usize + 1, + }; + let sum: usize = self.samples[0..now].iter().map(|x| *x as usize).sum(); + sum / now + } + + pub fn rate(&self) -> usize { + if self.elapsed() > RATE_SECONDS as u64 { + 0 + } else { + self.current_rate() + } + } } struct StatsCalculator { - filled: bool, - idx: usize, - samples: [T; STATS_SAMPLES], + filled: bool, + idx: usize, + samples: [T; STATS_SAMPLES], } impl Default for StatsCalculator { - fn default() -> Self { - StatsCalculator { - filled: false, - idx: 0, - samples: [T::default(); STATS_SAMPLES], - } - } + fn default() -> Self { + StatsCalculator { + filled: false, + idx: 0, + samples: [T::default(); STATS_SAMPLES], + } + } } impl fmt::Debug for StatsCalculator { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "median: {} ms", self.approximated_median()) - } + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "median: {} ms", self.approximated_median()) + } } impl StatsCalculator { - pub fn add(&mut self, sample: T) { - self.idx += 1; - if self.idx >= STATS_SAMPLES { - self.filled = true; - self.idx = 0; - } - - self.samples[self.idx] = sample; - } - - /// Returns aproximate of media - pub fn approximated_median(&self) -> T { - let mut copy = [T::default(); STATS_SAMPLES]; - copy.copy_from_slice(&self.samples); - let bound = if self.filled { STATS_SAMPLES } else { self.idx + 1 }; - - let (_, &mut median) = order_stat::median_of_medians(&mut copy[0..bound]); - median - } + pub fn add(&mut self, sample: T) { + self.idx += 1; + if self.idx >= STATS_SAMPLES { + self.filled = true; + self.idx = 0; + } + + self.samples[self.idx] = sample; + } + + /// Returns aproximate of media + pub fn approximated_median(&self) -> T { + let mut copy = [T::default(); STATS_SAMPLES]; + copy.copy_from_slice(&self.samples); + let bound = if self.filled { + STATS_SAMPLES + } else { + self.idx + 1 + }; + + let (_, &mut median) = order_stat::median_of_medians(&mut copy[0..bound]); + median + } } /// RPC Statistics #[derive(Default, Debug)] pub struct RpcStats { - requests: RwLock, - roundtrips: RwLock>, - active_sessions: AtomicUsize, + requests: RwLock, + roundtrips: RwLock>, + active_sessions: AtomicUsize, } impl RpcStats { - /// Count session opened - pub fn open_session(&self) { - self.active_sessions.fetch_add(1, atomic::Ordering::SeqCst); - } - - /// Count session closed. - /// Silently overflows if closing unopened session. - pub fn close_session(&self) { - self.active_sessions.fetch_sub(1, atomic::Ordering::SeqCst); - } - - /// Count request. Returns number of requests in current second. - pub fn count_request(&self) -> u16 { - self.requests.write().tick() - } - - /// Add roundtrip time (microseconds) - pub fn add_roundtrip(&self, microseconds: u128) { - self.roundtrips.write().add(microseconds) - } - - /// Returns number of open sessions - pub fn sessions(&self) -> usize { - self.active_sessions.load(atomic::Ordering::Relaxed) - } - - /// Returns requests rate - pub fn requests_rate(&self) -> usize { - self.requests.read().rate() - } - - /// Returns approximated roundtrip in microseconds - pub fn approximated_roundtrip(&self) -> u128 { - self.roundtrips.read().approximated_median() - } + /// Count session opened + pub fn open_session(&self) { + self.active_sessions.fetch_add(1, atomic::Ordering::SeqCst); + } + + /// Count session closed. + /// Silently overflows if closing unopened session. + pub fn close_session(&self) { + self.active_sessions.fetch_sub(1, atomic::Ordering::SeqCst); + } + + /// Count request. Returns number of requests in current second. + pub fn count_request(&self) -> u16 { + self.requests.write().tick() + } + + /// Add roundtrip time (microseconds) + pub fn add_roundtrip(&self, microseconds: u128) { + self.roundtrips.write().add(microseconds) + } + + /// Returns number of open sessions + pub fn sessions(&self) -> usize { + self.active_sessions.load(atomic::Ordering::Relaxed) + } + + /// Returns requests rate + pub fn requests_rate(&self) -> usize { + self.requests.read().rate() + } + + /// Returns approximated roundtrip in microseconds + pub fn approximated_roundtrip(&self) -> u128 { + self.roundtrips.read().approximated_median() + } } /// Notifies about RPC activity. pub trait ActivityNotifier: Send + Sync + 'static { - /// Activity on RPC interface - fn active(&self); + /// Activity on RPC interface + fn active(&self); } /// Stats-counting RPC middleware pub struct Middleware { - stats: Arc, - notifier: T, + stats: Arc, + notifier: T, } impl Middleware { - /// Create new Middleware with stats counter and activity notifier. - pub fn new(stats: Arc, notifier: T) -> Self { - Middleware { - stats, - notifier, - } - } + /// Create new Middleware with stats counter and activity notifier. + pub fn new(stats: Arc, notifier: T) -> Self { + Middleware { stats, notifier } + } } impl core::Middleware for Middleware { - type Future = core::FutureResponse; - type CallFuture = core::middleware::NoopCallFuture; - - fn on_request(&self, request: core::Request, meta: M, process: F) -> Either where - F: FnOnce(core::Request, M) -> X, - X: core::futures::Future, Error=()> + Send + 'static, - { - let start = time::Instant::now(); - - self.notifier.active(); - self.stats.count_request(); - - let id = match request { - core::Request::Single(core::Call::MethodCall(ref call)) => Some(call.id.clone()), - _ => None, - }; - let stats = self.stats.clone(); - - let future = process(request, meta).map(move |res| { - let time = start.elapsed().as_micros(); - if time > 10_000 { - debug!(target: "rpc", "[{:?}] Took {}ms", id, time / 1_000); - } - stats.add_roundtrip(time); - res - }); - - Either::A(Box::new(future)) - } + type Future = core::FutureResponse; + type CallFuture = core::middleware::NoopCallFuture; + + fn on_request( + &self, + request: core::Request, + meta: M, + process: F, + ) -> Either + where + F: FnOnce(core::Request, M) -> X, + X: core::futures::Future, Error = ()> + Send + 'static, + { + let start = time::Instant::now(); + + self.notifier.active(); + self.stats.count_request(); + + let id = match request { + core::Request::Single(core::Call::MethodCall(ref call)) => Some(call.id.clone()), + _ => None, + }; + let stats = self.stats.clone(); + + let future = process(request, meta).map(move |res| { + let time = start.elapsed().as_micros(); + if time > 10_000 { + debug!(target: "rpc", "[{:?}] Took {}ms", id, time / 1_000); + } + stats.add_roundtrip(time); + res + }); + + Either::A(Box::new(future)) + } } /// Client Notifier pub struct ClientNotifier { - /// Client - pub client: Arc<::ethcore::client::Client>, + /// Client + pub client: Arc<::ethcore::client::Client>, } impl ActivityNotifier for ClientNotifier { - fn active(&self) { - self.client.keep_alive() - } + fn active(&self) { + self.client.keep_alive() + } } #[cfg(test)] mod tests { - use super::{RateCalculator, StatsCalculator, RpcStats}; - - #[test] - fn should_calculate_rate() { - // given - let mut avg = RateCalculator::default(); - - // when - avg.tick(); - avg.tick(); - avg.tick(); - let rate = avg.rate(); - - // then - assert_eq!(rate, 3usize); - } - - #[test] - fn should_approximate_median() { - // given - let mut stats = StatsCalculator::default(); - stats.add(5); - stats.add(100); - stats.add(3); - stats.add(15); - stats.add(20); - stats.add(6); - - // when - let median = stats.approximated_median(); - - // then - assert_eq!(median, 5); - } - - #[test] - fn should_count_rpc_stats() { - // given - let stats = RpcStats::default(); - assert_eq!(stats.sessions(), 0); - assert_eq!(stats.requests_rate(), 0); - assert_eq!(stats.approximated_roundtrip(), 0); - - // when - stats.open_session(); - stats.close_session(); - stats.open_session(); - stats.count_request(); - stats.count_request(); - stats.add_roundtrip(125); - - // then - assert_eq!(stats.sessions(), 1); - assert_eq!(stats.requests_rate(), 2); - assert_eq!(stats.approximated_roundtrip(), 125); - } - - #[test] - fn should_be_sync_and_send() { - let stats = RpcStats::default(); - is_sync(stats); - } - - fn is_sync(x: F) { - drop(x) - } + use super::{RateCalculator, RpcStats, StatsCalculator}; + + #[test] + fn should_calculate_rate() { + // given + let mut avg = RateCalculator::default(); + + // when + avg.tick(); + avg.tick(); + avg.tick(); + let rate = avg.rate(); + + // then + assert_eq!(rate, 3usize); + } + + #[test] + fn should_approximate_median() { + // given + let mut stats = StatsCalculator::default(); + stats.add(5); + stats.add(100); + stats.add(3); + stats.add(15); + stats.add(20); + stats.add(6); + + // when + let median = stats.approximated_median(); + + // then + assert_eq!(median, 5); + } + + #[test] + fn should_count_rpc_stats() { + // given + let stats = RpcStats::default(); + assert_eq!(stats.sessions(), 0); + assert_eq!(stats.requests_rate(), 0); + assert_eq!(stats.approximated_roundtrip(), 0); + + // when + stats.open_session(); + stats.close_session(); + stats.open_session(); + stats.count_request(); + stats.count_request(); + stats.add_roundtrip(125); + + // then + assert_eq!(stats.sessions(), 1); + assert_eq!(stats.requests_rate(), 2); + assert_eq!(stats.approximated_roundtrip(), 125); + } + + #[test] + fn should_be_sync_and_send() { + let stats = RpcStats::default(); + is_sync(stats); + } + + fn is_sync(x: F) { + drop(x) + } } diff --git a/rpc/src/v1/metadata.rs b/rpc/src/v1/metadata.rs index 3224bd2c039..4e89a6c119b 100644 --- a/rpc/src/v1/metadata.rs +++ b/rpc/src/v1/metadata.rs @@ -1,39 +1,39 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -//! Parity RPC requests Metadata. +//! OpenEthereum RPC requests Metadata. use std::sync::Arc; use jsonrpc_core; -use jsonrpc_pubsub::{Session, PubSubMetadata}; +use jsonrpc_pubsub::{PubSubMetadata, Session}; use v1::types::Origin; /// RPC methods metadata. #[derive(Clone, Default, Debug)] pub struct Metadata { - /// Request origin - pub origin: Origin, - /// Request PubSub Session - pub session: Option>, + /// Request origin + pub origin: Origin, + /// Request PubSub Session + pub session: Option>, } impl jsonrpc_core::Metadata for Metadata {} impl PubSubMetadata for Metadata { - fn session(&self) -> Option> { - self.session.clone() - } + fn session(&self) -> Option> { + self.session.clone() + } } diff --git a/rpc/src/v1/mod.rs b/rpc/src/v1/mod.rs index 8b8afacdb1c..1645cecd8b3 100644 --- a/rpc/src/v1/mod.rs +++ b/rpc/src/v1/mod.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Ethcore rpc v1. //! @@ -21,37 +21,45 @@ // short for "try_boxfuture" // unwrap a result, returning a BoxFuture<_, Err> on failure. macro_rules! try_bf { - ($res: expr) => { - match $res { - Ok(val) => val, - Err(e) => return Box::new(::jsonrpc_core::futures::future::err(e.into())), - } - } + ($res: expr) => { + match $res { + Ok(val) => val, + Err(e) => return Box::new(::jsonrpc_core::futures::future::err(e.into())), + } + }; } #[macro_use] mod helpers; mod impls; -mod types; #[cfg(test)] mod tests; +mod types; pub mod extractors; pub mod informant; pub mod metadata; pub mod traits; -pub use self::traits::{Debug, Eth, EthFilter, EthPubSub, EthSigning, Net, Parity, ParityAccountsInfo, ParityAccounts, ParitySet, ParitySetAccounts, ParitySigning, Personal, PubSub, Private, Rpc, SecretStore, Signer, Traces, Web3}; -pub use self::impls::*; -pub use self::helpers::{NetworkSettings, block_import, dispatch}; -pub use self::metadata::Metadata; -pub use self::types::Origin; -pub use self::extractors::{RpcExtractor, WsExtractor, WsStats, WsDispatcher}; +pub use self::{ + extractors::{RpcExtractor, WsDispatcher, WsExtractor, WsStats}, + helpers::{block_import, dispatch, NetworkSettings}, + impls::*, + metadata::Metadata, + traits::{ + Debug, Eth, EthFilter, EthPubSub, EthSigning, Net, Parity, ParityAccounts, + ParityAccountsInfo, ParitySet, ParitySetAccounts, ParitySigning, Personal, PubSub, + SecretStore, Signer, Traces, Web3, + }, + types::Origin, +}; /// Signer utilities pub mod signer { - #[cfg(any(test, feature = "accounts"))] - pub use super::helpers::engine_signer::EngineSigner; - pub use super::helpers::external_signer::{SignerService, ConfirmationsQueue}; - pub use super::types::{ConfirmationRequest, TransactionModification, TransactionCondition}; + #[cfg(any(test, feature = "accounts"))] + pub use super::helpers::engine_signer::EngineSigner; + pub use super::{ + helpers::external_signer::{ConfirmationsQueue, SignerService}, + types::{ConfirmationRequest, TransactionCondition, TransactionModification}, + }; } diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 68710ae5591..a3999f431b4 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -1,34 +1,33 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! rpc integration tests. -use std::env; -use std::sync::Arc; +use std::{env, sync::Arc}; use accounts::AccountProvider; -use ethcore::client::{BlockChainClient, Client, ClientConfig, ChainInfo, ImportBlock}; -use ethcore::ethereum; -use ethcore::miner::Miner; -use ethcore::spec::{Genesis, Spec}; -use ethcore::test_helpers; -use ethcore::verification::VerifierType; -use ethcore::verification::queue::kind::blocks::Unverified; +use ethcore::{ + client::{BlockChainClient, ChainInfo, Client, ClientConfig, ImportBlock}, + ethereum, + miner::Miner, + spec::{Genesis, Spec}, + test_helpers, + verification::{queue::kind::blocks::Unverified, VerifierType}, +}; use ethereum_types::{Address, H256, U256}; -use ethjson::blockchain::BlockChain; -use ethjson::spec::ForkSpec; +use ethjson::{blockchain::BlockChain, spec::ForkSpec}; use io::IoChannel; use miner::external::ExternalMiner; use parity_runtime::Runtime; @@ -36,235 +35,263 @@ use parking_lot::Mutex; use types::ids::BlockId; use jsonrpc_core::IoHandler; -use v1::helpers::dispatch::{self, FullDispatcher}; -use v1::helpers::nonce; -use v1::impls::{EthClient, EthClientOptions, SigningUnsafeClient}; -use v1::metadata::Metadata; -use v1::tests::helpers::{TestSnapshotService, TestSyncProvider, Config}; -use v1::traits::{Eth, EthSigning}; +use v1::{ + helpers::{ + dispatch::{self, FullDispatcher}, + nonce, + }, + impls::{EthClient, EthClientOptions, SigningUnsafeClient}, + metadata::Metadata, + tests::helpers::{Config, TestSnapshotService, TestSyncProvider}, + traits::{Eth, EthSigning}, +}; fn account_provider() -> Arc { - Arc::new(AccountProvider::transient_provider()) + Arc::new(AccountProvider::transient_provider()) } fn sync_provider() -> Arc { - Arc::new(TestSyncProvider::new(Config { - network_id: 3, - num_peers: 120, - })) + Arc::new(TestSyncProvider::new(Config { + network_id: 3, + num_peers: 120, + })) } fn miner_service(spec: &Spec) -> Arc { - Arc::new(Miner::new_for_tests(spec, None)) + Arc::new(Miner::new_for_tests(spec, None)) } fn snapshot_service() -> Arc { - Arc::new(TestSnapshotService::new()) + Arc::new(TestSnapshotService::new()) } fn make_spec(chain: &BlockChain) -> Spec { - let genesis = Genesis::from(chain.genesis()); - let mut spec = ethereum::new_frontier_test(); - let state = chain.pre_state.clone().into(); - spec.set_genesis_state(state).expect("unable to set genesis state"); - spec.overwrite_genesis_params(genesis); - assert!(spec.is_state_root_valid()); - spec + let genesis = Genesis::from(chain.genesis()); + let mut spec = ethereum::new_frontier_test(); + let state = chain.pre_state.clone().into(); + spec.set_genesis_state(state) + .expect("unable to set genesis state"); + spec.overwrite_genesis_params(genesis); + assert!(spec.is_state_root_valid()); + spec } struct EthTester { - _miner: Arc, - _runtime: Runtime, - _snapshot: Arc, - accounts: Arc, - client: Arc, - handler: IoHandler, + _miner: Arc, + _runtime: Runtime, + _snapshot: Arc, + accounts: Arc, + client: Arc, + handler: IoHandler, } impl EthTester { - fn from_chain(chain: &BlockChain) -> Self { - - let tester = if ::ethjson::blockchain::Engine::NoProof == chain.engine { - let mut config = ClientConfig::default(); - config.verifier_type = VerifierType::CanonNoSeal; - config.check_seal = false; - Self::from_spec_conf(make_spec(chain), config) - } else { - Self::from_spec(make_spec(chain)) - }; - - for b in chain.blocks_rlp() { - if let Ok(block) = Unverified::from_rlp(b) { - let _ = tester.client.import_block(block); - tester.client.flush_queue(); - tester.client.import_verified_blocks(); - } - } - - tester.client.flush_queue(); - - assert!(tester.client.chain_info().best_block_hash == chain.best_block.clone().into()); - tester - } - - fn from_spec(spec: Spec) -> Self { - let config = ClientConfig::default(); - Self::from_spec_conf(spec, config) - } - - fn from_spec_conf(spec: Spec, config: ClientConfig) -> Self { - let runtime = Runtime::with_thread_count(1); - let account_provider = account_provider(); - let ap = account_provider.clone(); - let accounts = Arc::new(move || ap.accounts().unwrap_or_default()) as _; - let miner_service = miner_service(&spec); - let snapshot_service = snapshot_service(); - - let client = Client::new( - config, - &spec, - test_helpers::new_db(), - miner_service.clone(), - IoChannel::disconnected(), - ).unwrap(); - let sync_provider = sync_provider(); - let external_miner = Arc::new(ExternalMiner::default()); - - let eth_client = EthClient::new( - &client, - &snapshot_service, - &sync_provider, - &accounts, - &miner_service, - &external_miner, - EthClientOptions { - pending_nonce_from_queue: false, - allow_pending_receipt_query: true, - send_block_number_in_get_work: true, - gas_price_percentile: 50, - allow_experimental_rpcs: true, - allow_missing_blocks: false - }, - ); - - let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor()))); - - let dispatcher = FullDispatcher::new(client.clone(), miner_service.clone(), reservations, 50); - let signer = Arc::new(dispatch::Signer::new(account_provider.clone())) as _; - let eth_sign = SigningUnsafeClient::new( - &signer, - dispatcher, - ); - - let mut handler = IoHandler::default(); - handler.extend_with(eth_client.to_delegate()); - handler.extend_with(eth_sign.to_delegate()); - - EthTester { - _miner: miner_service, - _runtime: runtime, - _snapshot: snapshot_service, - accounts: account_provider, - client: client, - handler: handler, - } - } + fn from_chain(chain: &BlockChain) -> Self { + let tester = if ::ethjson::blockchain::Engine::NoProof == chain.engine { + let mut config = ClientConfig::default(); + config.verifier_type = VerifierType::CanonNoSeal; + config.check_seal = false; + Self::from_spec_conf(make_spec(chain), config) + } else { + Self::from_spec(make_spec(chain)) + }; + + for b in chain.blocks_rlp() { + if let Ok(block) = Unverified::from_rlp(b) { + let _ = tester.client.import_block(block); + tester.client.flush_queue(); + tester.client.import_verified_blocks(); + } + } + + tester.client.flush_queue(); + + assert!(tester.client.chain_info().best_block_hash == chain.best_block.clone().into()); + tester + } + + fn from_spec(spec: Spec) -> Self { + let config = ClientConfig::default(); + Self::from_spec_conf(spec, config) + } + + fn from_spec_conf(spec: Spec, config: ClientConfig) -> Self { + let runtime = Runtime::with_thread_count(1); + let account_provider = account_provider(); + let ap = account_provider.clone(); + let accounts = Arc::new(move || ap.accounts().unwrap_or_default()) as _; + let miner_service = miner_service(&spec); + let snapshot_service = snapshot_service(); + + let client = Client::new( + config, + &spec, + test_helpers::new_db(), + miner_service.clone(), + IoChannel::disconnected(), + ) + .unwrap(); + let sync_provider = sync_provider(); + let external_miner = Arc::new(ExternalMiner::default()); + + let eth_client = EthClient::new( + &client, + &snapshot_service, + &sync_provider, + &accounts, + &miner_service, + &external_miner, + EthClientOptions { + gas_price_percentile: 50, + allow_experimental_rpcs: true, + allow_missing_blocks: false, + no_ancient_blocks: false, + }, + ); + + let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor()))); + + let dispatcher = + FullDispatcher::new(client.clone(), miner_service.clone(), reservations, 50); + let signer = Arc::new(dispatch::Signer::new(account_provider.clone())) as _; + let eth_sign = SigningUnsafeClient::new(&signer, dispatcher); + + let mut handler = IoHandler::default(); + handler.extend_with(eth_client.to_delegate()); + handler.extend_with(eth_sign.to_delegate()); + + EthTester { + _miner: miner_service, + _runtime: runtime, + _snapshot: snapshot_service, + accounts: account_provider, + client: client, + handler: handler, + } + } } #[test] fn harness_works() { - let chain: BlockChain = extract_chain!("BlockchainTests/bcWalletTest/wallet2outOf3txs"); - let _ = EthTester::from_chain(&chain); + let chain: BlockChain = + extract_chain!("BlockchainTests/ValidBlocks/bcWalletTest/wallet2outOf3txs"); + let _ = EthTester::from_chain(&chain); } #[test] fn eth_get_balance() { - let chain = extract_chain!("BlockchainTests/bcWalletTest/wallet2outOf3txs"); - let tester = EthTester::from_chain(&chain); - // final account state - let req_latest = r#"{ + let chain = extract_chain!("BlockchainTests/ValidBlocks/bcWalletTest/wallet2outOf3txs"); + let tester = EthTester::from_chain(&chain); + // final account state + let req_latest = r#"{ "jsonrpc": "2.0", "method": "eth_getBalance", "params": ["0xaaaf5374fce5edbc8e2a8697c15331677e6ebaaa", "latest"], "id": 1 }"#; - let res_latest = r#"{"jsonrpc":"2.0","result":"0x9","id":1}"#.to_owned(); - assert_eq!(tester.handler.handle_request_sync(req_latest).unwrap(), res_latest); - - // non-existant account - let req_new_acc = r#"{ + let res_latest = r#"{"jsonrpc":"2.0","result":"0x9","id":1}"#.to_owned(); + assert_eq!( + tester.handler.handle_request_sync(req_latest).unwrap(), + res_latest + ); + + // non-existant account + let req_new_acc = r#"{ "jsonrpc": "2.0", "method": "eth_getBalance", "params": ["0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"], "id": 3 }"#; - let res_new_acc = r#"{"jsonrpc":"2.0","result":"0x0","id":3}"#.to_owned(); - assert_eq!(tester.handler.handle_request_sync(req_new_acc).unwrap(), res_new_acc); + let res_new_acc = r#"{"jsonrpc":"2.0","result":"0x0","id":3}"#.to_owned(); + assert_eq!( + tester.handler.handle_request_sync(req_new_acc).unwrap(), + res_new_acc + ); } #[test] fn eth_get_proof() { - let chain = extract_chain!("BlockchainTests/bcWalletTest/wallet2outOf3txs"); - let tester = EthTester::from_chain(&chain); - // final account state - let req_latest = r#"{ + let chain = extract_chain!("BlockchainTests/ValidBlocks/bcWalletTest/wallet2outOf3txs"); + let tester = EthTester::from_chain(&chain); + // final account state + let req_latest = r#"{ "jsonrpc": "2.0", "method": "eth_getProof", "params": ["0xaaaf5374fce5edbc8e2a8697c15331677e6ebaaa", [], "latest"], "id": 1 }"#; - let res_latest = r#","address":"0xaaaf5374fce5edbc8e2a8697c15331677e6ebaaa","balance":"0x9","codeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","nonce":"0x0","storageHash":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","storageProof":[]},"id":1}"#.to_owned(); - assert!(tester.handler.handle_request_sync(req_latest).unwrap().to_string().ends_with(res_latest.as_str())); + let res_latest = r#","address":"0xaaaf5374fce5edbc8e2a8697c15331677e6ebaaa","balance":"0x9","codeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","nonce":"0x0","storageHash":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","storageProof":[]},"id":1}"#.to_owned(); + assert!(tester + .handler + .handle_request_sync(req_latest) + .unwrap() + .to_string() + .ends_with(res_latest.as_str())); - // non-existant account - let req_new_acc = r#"{ + // non-existant account + let req_new_acc = r#"{ "jsonrpc": "2.0", "method": "eth_getProof", "params": ["0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",[],"latest"], "id": 3 }"#; - let res_new_acc = r#","address":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","balance":"0x0","codeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","nonce":"0x0","storageHash":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","storageProof":[]},"id":3}"#.to_owned(); - assert!(tester.handler.handle_request_sync(req_new_acc).unwrap().to_string().ends_with(res_new_acc.as_str())); + let res_new_acc = r#","address":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","balance":"0x0","codeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","nonce":"0x0","storageHash":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","storageProof":[]},"id":3}"#.to_owned(); + assert!(tester + .handler + .handle_request_sync(req_new_acc) + .unwrap() + .to_string() + .ends_with(res_new_acc.as_str())); } #[test] fn eth_block_number() { - let chain = extract_chain!("BlockchainTests/bcGasPricerTest/RPC_API_Test"); - let tester = EthTester::from_chain(&chain); - let req_number = r#"{ + let chain = extract_chain!("BlockchainTests/ValidBlocks/bcGasPricerTest/RPC_API_Test"); + let tester = EthTester::from_chain(&chain); + let req_number = r#"{ "jsonrpc": "2.0", "method": "eth_blockNumber", "params": [], "id": 1 }"#; - let res_number = r#"{"jsonrpc":"2.0","result":"0x20","id":1}"#.to_owned(); - assert_eq!(tester.handler.handle_request_sync(req_number).unwrap(), res_number); + let res_number = r#"{"jsonrpc":"2.0","result":"0x20","id":1}"#.to_owned(); + assert_eq!( + tester.handler.handle_request_sync(req_number).unwrap(), + res_number + ); } #[test] fn eth_get_block() { - let chain = extract_chain!("BlockchainTests/bcGasPricerTest/RPC_API_Test"); - let tester = EthTester::from_chain(&chain); - let req_block = r#"{"method":"eth_getBlockByNumber","params":["0x0",false],"id":1,"jsonrpc":"2.0"}"#; - - let res_block = r#"{"jsonrpc":"2.0","result":{"author":"0x8888f1f195afa192cfee860698584c030f4c9db1","difficulty":"0x20000","extraData":"0x42","gasLimit":"0x1df5d44","gasUsed":"0x0","hash":"0xcded1bc807465a72e2d54697076ab858f28b15d4beaae8faa47339c8eee386a3","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x8888f1f195afa192cfee860698584c030f4c9db1","mixHash":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","nonce":"0x0102030405060708","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":["0xa056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","0x880102030405060708"],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x200","stateRoot":"0x7dba07d6b448a186e9612e5f737d1c909dce473e53199901a302c00646d523c1","timestamp":"0x54c98c81","totalDifficulty":"0x20000","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]},"id":1}"#; - assert_eq!(tester.handler.handle_request_sync(req_block).unwrap(), res_block); + let chain = extract_chain!("BlockchainTests/ValidBlocks/bcGasPricerTest/RPC_API_Test"); + let tester = EthTester::from_chain(&chain); + let req_block = + r#"{"method":"eth_getBlockByNumber","params":["0x0",false],"id":1,"jsonrpc":"2.0"}"#; + + let res_block = r#"{"jsonrpc":"2.0","result":{"author":"0x8888f1f195afa192cfee860698584c030f4c9db1","difficulty":"0x20000","extraData":"0x42","gasLimit":"0x1df5d44","gasUsed":"0x0","hash":"0xcded1bc807465a72e2d54697076ab858f28b15d4beaae8faa47339c8eee386a3","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x8888f1f195afa192cfee860698584c030f4c9db1","mixHash":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","nonce":"0x0102030405060708","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":["0xa056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","0x880102030405060708"],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x200","stateRoot":"0x7dba07d6b448a186e9612e5f737d1c909dce473e53199901a302c00646d523c1","timestamp":"0x54c98c81","totalDifficulty":"0x20000","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]},"id":1}"#; + assert_eq!( + tester.handler.handle_request_sync(req_block).unwrap(), + res_block + ); } #[test] fn eth_get_block_by_hash() { - let chain = extract_chain!("BlockchainTests/bcGasPricerTest/RPC_API_Test"); - let tester = EthTester::from_chain(&chain); + let chain = extract_chain!("BlockchainTests/ValidBlocks/bcGasPricerTest/RPC_API_Test"); + let tester = EthTester::from_chain(&chain); - // We're looking for block number 4 from "RPC_API_Test_Frontier" - let req_block = r#"{"method":"eth_getBlockByHash","params":["0xaddb9e39795e9e041c936b88a2577802569f34afded0948707b074caa3163a87",false],"id":1,"jsonrpc":"2.0"}"#; + // We're looking for block number 4 from "RPC_API_Test_Frontier" + let req_block = r#"{"method":"eth_getBlockByHash","params":["0x088987877b431b8156c79c4b1c9543a8747531e5acaebca8f5d516cb3b35b0f2",false],"id":1,"jsonrpc":"2.0"}"#; - let res_block = r#"{"jsonrpc":"2.0","result":{"author":"0x8888f1f195afa192cfee860698584c030f4c9db1","difficulty":"0x20080","extraData":"0x","gasLimit":"0x1dd7ea0","gasUsed":"0x5458","hash":"0xaddb9e39795e9e041c936b88a2577802569f34afded0948707b074caa3163a87","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x8888f1f195afa192cfee860698584c030f4c9db1","mixHash":"0x713b0b31f6e72d8cb7367eaf59447ea531f209fc80e6379edd9f8d3bb73931c4","nonce":"0x4534b406bc23b86d","number":"0x4","parentHash":"0x17567aa5995b703736e32972289d68af50543acc4d56d37e8ad1fea7252cac4a","receiptsRoot":"0x7ed8026cf72ed0e98e6fd53ab406e51ffd34397d9da0052494ff41376fda7b5f","sealFields":["0xa0713b0b31f6e72d8cb7367eaf59447ea531f209fc80e6379edd9f8d3bb73931c4","0x884534b406bc23b86d"],"sha3Uncles":"0xe588a44b3e320e72e70b32b531f3ac0d432e756120135ae8fe5fa10895196b40","size":"0x661","stateRoot":"0x68805721294e365020aca15ed56c360d9dc2cf03cbeff84c9b84b8aed023bfb5","timestamp":"0x5bbdf772","totalDifficulty":"0xa00c0","transactions":["0xb094b9dc356dbb8b256402c6d5709288066ad6a372c90c9c516f14277545fd58"],"transactionsRoot":"0x97a593d8d7e15b57f5c6bb25bc6c325463ef99f874bc08a78656c3ab5cb23262","uncles":["0x86b48f5186c4b0882d3dca7977aa37840008832ef092f8ef797019dc74bfa8c7","0x2da9d062c11d536f0f1cc2a4e0111597c79926958d0fc26ae1a2d07d1a3bf47d"]},"id":1}"#; - assert_eq!(tester.handler.handle_request_sync(req_block).unwrap(), res_block); + let res_block = r#"{"jsonrpc":"2.0","result":{"author":"0x8888f1f195afa192cfee860698584c030f4c9db1","difficulty":"0x200c0","extraData":"0x","gasLimit":"0x1dd7ea0","gasUsed":"0x5458","hash":"0x088987877b431b8156c79c4b1c9543a8747531e5acaebca8f5d516cb3b35b0f2","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x8888f1f195afa192cfee860698584c030f4c9db1","mixHash":"0xeb709bc3b92c8f28ffa3d4ad7558689316cfbf599ac9541d9f3e79d31b0b9af6","nonce":"0x6ab72973b649825d","number":"0x4","parentHash":"0x095303ee87197430f9bf30ecd09735a11ab78db59abf67a36d4ada73f96800b9","receiptsRoot":"0x7ed8026cf72ed0e98e6fd53ab406e51ffd34397d9da0052494ff41376fda7b5f","sealFields":["0xa0eb709bc3b92c8f28ffa3d4ad7558689316cfbf599ac9541d9f3e79d31b0b9af6","0x886ab72973b649825d"],"sha3Uncles":"0xb9cf7d3adde9f960e6c2510c1a7d35e94223db71277463ef8dc94f8afbe44403","size":"0x661","stateRoot":"0x68805721294e365020aca15ed56c360d9dc2cf03cbeff84c9b84b8aed023bfb5","timestamp":"0x5db6f5a1","totalDifficulty":"0xa0180","transactions":["0xb094b9dc356dbb8b256402c6d5709288066ad6a372c90c9c516f14277545fd58"],"transactionsRoot":"0x97a593d8d7e15b57f5c6bb25bc6c325463ef99f874bc08a78656c3ab5cb23262","uncles":["0xffa20f3c2eafd8a4def16b2742e576280282c1476a8307ac6ff5a8a1eac99cdf","0x67faba5ff44f91127c12823a820ab5456252afa3397d08b2781fe308fb1c8359"]},"id":1}"#; + assert_eq!( + tester.handler.handle_request_sync(req_block).unwrap(), + res_block + ); } // a frontier-like test with an expanded gas limit and balance on known account. @@ -366,27 +393,43 @@ const POSITIVE_NONCE_SPEC: &'static [u8] = br#"{ #[test] fn eth_transaction_count() { - let secret = "8a283037bb19c4fed7b1c569e40c7dcff366165eb869110a1b11532963eb9cb2".parse().unwrap(); - let tester = EthTester::from_spec(Spec::load(&env::temp_dir(), TRANSACTION_COUNT_SPEC).expect("invalid chain spec")); - let address = tester.accounts.insert_account(secret, &"".into()).unwrap(); - tester.accounts.unlock_account_permanently(address, "".into()).unwrap(); - - let req_before = r#"{ + let secret = "8a283037bb19c4fed7b1c569e40c7dcff366165eb869110a1b11532963eb9cb2" + .parse() + .unwrap(); + let tester = EthTester::from_spec( + Spec::load(&env::temp_dir(), TRANSACTION_COUNT_SPEC).expect("invalid chain spec"), + ); + let address = tester.accounts.insert_account(secret, &"".into()).unwrap(); + tester + .accounts + .unlock_account_permanently(address, "".into()) + .unwrap(); + + let req_before = r#"{ "jsonrpc": "2.0", "method": "eth_getTransactionCount", - "params": [""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", "latest"], + "params": [""# + .to_owned() + + format!("0x{:x}", address).as_ref() + + r#"", "latest"], "id": 15 }"#; - let res_before = r#"{"jsonrpc":"2.0","result":"0x0","id":15}"#; + let res_before = r#"{"jsonrpc":"2.0","result":"0x0","id":15}"#; - assert_eq!(tester.handler.handle_request_sync(&req_before).unwrap(), res_before); + assert_eq!( + tester.handler.handle_request_sync(&req_before).unwrap(), + res_before + ); - let req_send_trans = r#"{ + let req_send_trans = r#"{ "jsonrpc": "2.0", "method": "eth_sendTransaction", "params": [{ - "from": ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", + "from": ""# + .to_owned() + + format!("0x{:x}", address).as_ref() + + r#"", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "gas": "0x30000", "gasPrice": "0x1", @@ -395,120 +438,173 @@ fn eth_transaction_count() { "id": 16 }"#; - // dispatch the transaction. - tester.handler.handle_request_sync(&req_send_trans).unwrap(); + // dispatch the transaction. + tester.handler.handle_request_sync(&req_send_trans).unwrap(); - // we have submitted the transaction -- but this shouldn't be reflected in a "latest" query. - let req_after_latest = r#"{ + // we have submitted the transaction -- but this shouldn't be reflected in a "latest" query. + let req_after_latest = r#"{ "jsonrpc": "2.0", "method": "eth_getTransactionCount", - "params": [""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", "latest"], + "params": [""# + .to_owned() + + format!("0x{:x}", address).as_ref() + + r#"", "latest"], "id": 17 }"#; - let res_after_latest = r#"{"jsonrpc":"2.0","result":"0x0","id":17}"#; + let res_after_latest = r#"{"jsonrpc":"2.0","result":"0x0","id":17}"#; - assert_eq!(&tester.handler.handle_request_sync(&req_after_latest).unwrap(), res_after_latest); + assert_eq!( + &tester + .handler + .handle_request_sync(&req_after_latest) + .unwrap(), + res_after_latest + ); - // the pending transactions should have been updated. - let req_after_pending = r#"{ + // the pending transactions should have been updated. + let req_after_pending = r#"{ "jsonrpc": "2.0", "method": "eth_getTransactionCount", - "params": [""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", "pending"], + "params": [""# + .to_owned() + + format!("0x{:x}", address).as_ref() + + r#"", "pending"], "id": 18 }"#; - let res_after_pending = r#"{"jsonrpc":"2.0","result":"0x1","id":18}"#; + let res_after_pending = r#"{"jsonrpc":"2.0","result":"0x1","id":18}"#; - assert_eq!(&tester.handler.handle_request_sync(&req_after_pending).unwrap(), res_after_pending); + assert_eq!( + &tester + .handler + .handle_request_sync(&req_after_pending) + .unwrap(), + res_after_pending + ); } fn verify_transaction_counts(name: String, chain: BlockChain) { - struct PanicHandler(String); - impl Drop for PanicHandler { - fn drop(&mut self) { - if ::std::thread::panicking() { - println!("Test failed: {}", self.0); - } - } - } - - let _panic = PanicHandler(name); - - fn by_hash(hash: H256, count: usize, id: &mut usize) -> (String, String) { - let req = r#"{ + struct PanicHandler(String); + impl Drop for PanicHandler { + fn drop(&mut self) { + if ::std::thread::panicking() { + println!("Test failed: {}", self.0); + } + } + } + + let _panic = PanicHandler(name); + + fn by_hash(hash: H256, count: usize, id: &mut usize) -> (String, String) { + let req = r#"{ "jsonrpc": "2.0", "method": "eth_getBlockTransactionCountByHash", "params": [ - ""#.to_owned() + format!("0x{:x}", hash).as_ref() + r#"" + ""# + .to_owned() + + format!("0x{:x}", hash).as_ref() + + r#"" ], - "id": "# + format!("{}", *id).as_ref() + r#" + "id": "# + format!("{}", *id).as_ref() + + r#" }"#; - let res = r#"{"jsonrpc":"2.0","result":""#.to_owned() - + format!("0x{:x}", count).as_ref() - + r#"","id":"# - + format!("{}", *id).as_ref() + r#"}"#; - *id += 1; - (req, res) - } - - fn by_number(num: u64, count: usize, id: &mut usize) -> (String, String) { - let req = r#"{ + let res = r#"{"jsonrpc":"2.0","result":""#.to_owned() + + format!("0x{:x}", count).as_ref() + + r#"","id":"# + + format!("{}", *id).as_ref() + + r#"}"#; + *id += 1; + (req, res) + } + + fn by_number(num: u64, count: usize, id: &mut usize) -> (String, String) { + let req = r#"{ "jsonrpc": "2.0", "method": "eth_getBlockTransactionCountByNumber", "params": [ - "#.to_owned() + &::serde_json::to_string(&U256::from(num)).unwrap() + r#" + "# + .to_owned() + + &::serde_json::to_string(&U256::from(num)).unwrap() + + r#" ], - "id": "# + format!("{}", *id).as_ref() + r#" + "id": "# + format!("{}", *id).as_ref() + + r#" }"#; - let res = r#"{"jsonrpc":"2.0","result":""#.to_owned() - + format!("0x{:x}", count).as_ref() - + r#"","id":"# - + format!("{}", *id).as_ref() + r#"}"#; - *id += 1; - (req, res) - } - - let tester = EthTester::from_chain(&chain); - - let mut id = 1; - for b in chain.blocks_rlp().into_iter().filter_map(|b| Unverified::from_rlp(b).ok()) { - let count = b.transactions.len(); - - let hash = b.header.hash(); - let number = b.header.number(); - - let (req, res) = by_hash(hash, count, &mut id); - assert_eq!(tester.handler.handle_request_sync(&req), Some(res)); - - // uncles can share block numbers, so skip them. - if tester.client.block_hash(BlockId::Number(number)) == Some(hash) { - let (req, res) = by_number(number, count, &mut id); - assert_eq!(tester.handler.handle_request_sync(&req), Some(res)); - } - } + let res = r#"{"jsonrpc":"2.0","result":""#.to_owned() + + format!("0x{:x}", count).as_ref() + + r#"","id":"# + + format!("{}", *id).as_ref() + + r#"}"#; + *id += 1; + (req, res) + } + + let tester = EthTester::from_chain(&chain); + + let mut id = 1; + for b in chain + .blocks_rlp() + .into_iter() + .filter_map(|b| Unverified::from_rlp(b).ok()) + { + let count = b.transactions.len(); + + let hash = b.header.hash(); + let number = b.header.number(); + + let (req, res) = by_hash(hash, count, &mut id); + assert_eq!(tester.handler.handle_request_sync(&req), Some(res)); + + // uncles can share block numbers, so skip them. + if tester.client.block_hash(BlockId::Number(number)) == Some(hash) { + let (req, res) = by_number(number, count, &mut id); + assert_eq!(tester.handler.handle_request_sync(&req), Some(res)); + } + } } #[test] fn starting_nonce_test() { - let tester = EthTester::from_spec(Spec::load(&env::temp_dir(), POSITIVE_NONCE_SPEC).expect("invalid chain spec")); - let address = Address::from(10); - - let sample = tester.handler.handle_request_sync(&(r#" + let tester = EthTester::from_spec( + Spec::load(&env::temp_dir(), POSITIVE_NONCE_SPEC).expect("invalid chain spec"), + ); + let address = Address::from(10); + + let sample = tester + .handler + .handle_request_sync( + &(r#" { "jsonrpc": "2.0", "method": "eth_getTransactionCount", - "params": [""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", "latest"], + "params": [""# + .to_owned() + + format!("0x{:x}", address).as_ref() + + r#"", "latest"], "id": 15 } - "#) - ).unwrap(); + "#), + ) + .unwrap(); - assert_eq!(r#"{"jsonrpc":"2.0","result":"0x100","id":15}"#, &sample); + assert_eq!(r#"{"jsonrpc":"2.0","result":"0x100","id":15}"#, &sample); } -register_test!(eth_transaction_count_1, verify_transaction_counts, "BlockchainTests/bcWalletTest/wallet2outOf3txs"); -register_test!(eth_transaction_count_2, verify_transaction_counts, "BlockchainTests/bcTotalDifficultyTest/sideChainWithMoreTransactions"); -register_test!(eth_transaction_count_3, verify_transaction_counts, "BlockchainTests/bcGasPricerTest/RPC_API_Test"); +register_test!( + eth_transaction_count_1, + verify_transaction_counts, + "BlockchainTests/ValidBlocks/bcWalletTest/wallet2outOf3txs" +); +register_test!( + eth_transaction_count_2, + verify_transaction_counts, + "BlockchainTests/ValidBlocks/bcTotalDifficultyTest/sideChainWithMoreTransactions" +); +register_test!( + eth_transaction_count_3, + verify_transaction_counts, + "BlockchainTests/ValidBlocks/bcGasPricerTest/RPC_API_Test" +); diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index b16651bd9c5..7e29f6dd70c 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -1,299 +1,366 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Test implementation of miner service. -use std::sync::Arc; -use std::collections::{BTreeMap, BTreeSet, HashMap}; +use std::{ + collections::{BTreeMap, BTreeSet, HashMap}, + sync::Arc, +}; use bytes::Bytes; -use ethcore::block::SealedBlock; -use ethcore::client::{Nonce, PrepareOpenBlock, StateClient, EngineInfo}; -use ethcore::engines::{EthEngine, signer::EngineSigner}; -use ethcore::error::Error; -use ethcore::miner::{self, MinerService, AuthoringParams}; -use ethereum_types::{H256, U256, Address}; -use miner::pool::local_transactions::Status as LocalTransactionStatus; -use miner::pool::{verifier, VerifiedTransaction, QueueStatus}; -use parking_lot::{RwLock, Mutex}; -use types::transaction::{self, UnverifiedTransaction, SignedTransaction, PendingTransaction}; +use ethcore::{ + block::SealedBlock, + client::{ + test_client::TestState, traits::ForceUpdateSealing, EngineInfo, Nonce, PrepareOpenBlock, + StateClient, + }, + engines::{signer::EngineSigner, EthEngine}, + error::Error, + miner::{self, AuthoringParams, MinerService}, +}; +use ethereum_types::{Address, H256, U256}; +use miner::pool::{ + local_transactions::Status as LocalTransactionStatus, verifier, QueueStatus, + VerifiedTransaction, +}; +use parking_lot::{Mutex, RwLock}; use txpool; -use types::BlockNumber; -use types::block::Block; -use types::header::Header; -use types::ids::BlockId; -use types::receipt::RichReceipt; +use types::{ + block::Block, + header::Header, + ids::BlockId, + receipt::RichReceipt, + transaction::{self, PendingTransaction, SignedTransaction, UnverifiedTransaction}, + BlockNumber, +}; /// Test miner service. pub struct TestMinerService { - /// Imported transactions. - pub imported_transactions: Mutex>, - /// Pre-existed pending transactions - pub pending_transactions: Mutex>, - /// Pre-existed local transactions - pub local_transactions: Mutex>, - /// Pre-existed pending receipts - pub pending_receipts: Mutex>, - /// Next nonces. - pub next_nonces: RwLock>, - /// Minimum gas price - pub min_gas_price: RwLock>, - /// Signer (if any) - pub signer: RwLock>>, - - authoring_params: RwLock, + /// Imported transactions. + pub imported_transactions: Mutex>, + /// Pre-existed pending transactions + pub pending_transactions: Mutex>, + /// Pre-existed local transactions + pub local_transactions: Mutex>, + /// Pre-existed pending receipts + pub pending_receipts: Mutex>, + /// Next nonces. + pub next_nonces: RwLock>, + /// Minimum gas price + pub min_gas_price: RwLock>, + /// Signer (if any) + pub signer: RwLock>>, + + authoring_params: RwLock, } impl Default for TestMinerService { - fn default() -> TestMinerService { - TestMinerService { - imported_transactions: Default::default(), - pending_transactions: Default::default(), - local_transactions: Default::default(), - pending_receipts: Default::default(), - next_nonces: Default::default(), - min_gas_price: RwLock::new(Some(0.into())), - authoring_params: RwLock::new(AuthoringParams { - author: Address::zero(), - gas_range_target: (12345.into(), 54321.into()), - extra_data: vec![1, 2, 3, 4], - }), - signer: RwLock::new(None), - } - } + fn default() -> TestMinerService { + TestMinerService { + imported_transactions: Default::default(), + pending_transactions: Default::default(), + local_transactions: Default::default(), + pending_receipts: Default::default(), + next_nonces: Default::default(), + min_gas_price: RwLock::new(Some(0.into())), + authoring_params: RwLock::new(AuthoringParams { + author: Address::zero(), + gas_range_target: (12345.into(), 54321.into()), + extra_data: vec![1, 2, 3, 4], + }), + signer: RwLock::new(None), + } + } } impl TestMinerService { - /// Increments nonce for given address. - pub fn increment_nonce(&self, address: &Address) { - let mut next_nonces = self.next_nonces.write(); - let nonce = next_nonces.entry(*address).or_insert_with(|| 0.into()); - *nonce = *nonce + 1; - } + /// Increments nonce for given address. + pub fn increment_nonce(&self, address: &Address) { + let mut next_nonces = self.next_nonces.write(); + let nonce = next_nonces.entry(*address).or_insert_with(|| 0.into()); + *nonce = *nonce + 1; + } } impl StateClient for TestMinerService { - // State will not be used by test client anyway, since all methods that accept state are mocked - type State = (); + // State will not be used by test client anyway, since all methods that accept state are mocked + type State = TestState; - fn latest_state(&self) -> Self::State { - () - } + fn latest_state_and_header(&self) -> (Self::State, Header) { + (TestState, Header::default()) + } - fn state_at(&self, _id: BlockId) -> Option { - Some(()) - } + fn state_at(&self, _id: BlockId) -> Option { + Some(TestState) + } } impl EngineInfo for TestMinerService { - fn engine(&self) -> &EthEngine { - unimplemented!() - } + fn engine(&self) -> &dyn EthEngine { + unimplemented!() + } } impl MinerService for TestMinerService { - type State = (); - - fn pending_state(&self, _latest_block_number: BlockNumber) -> Option { - None - } - - fn pending_block_header(&self, _latest_block_number: BlockNumber) -> Option
{ - None - } - - fn pending_block(&self, _latest_block_number: BlockNumber) -> Option { - None - } - - fn authoring_params(&self) -> AuthoringParams { - self.authoring_params.read().clone() - } - - fn set_author(&self, author: miner::Author) { - self.authoring_params.write().author = author.address(); - if let miner::Author::Sealer(signer) = author { - *self.signer.write() = Some(signer); - } - } - - fn set_extra_data(&self, extra_data: Bytes) { - self.authoring_params.write().extra_data = extra_data; - } - - fn set_gas_range_target(&self, target: (U256, U256)) { - self.authoring_params.write().gas_range_target = target; - } - - /// Imports transactions to transaction queue. - fn import_external_transactions(&self, chain: &C, transactions: Vec) - -> Vec> - { - // lets assume that all txs are valid - let transactions: Vec<_> = transactions.into_iter().map(|tx| SignedTransaction::new(tx).unwrap()).collect(); - self.imported_transactions.lock().extend_from_slice(&transactions); - - for sender in transactions.iter().map(|tx| tx.sender()) { - let nonce = self.next_nonce(chain, &sender); - self.next_nonces.write().insert(sender, nonce); - } - - transactions - .iter() - .map(|_| Ok(())) - .collect() - } - - /// Imports transactions to transaction queue. - fn import_own_transaction(&self, _chain: &C, _pending: PendingTransaction) - -> Result<(), transaction::Error> { - // this function is no longer called directly from RPC - unimplemented!(); - } - - /// Imports transactions to queue - treats as local based on trusted flag, config, and tx source - fn import_claimed_local_transaction(&self, chain: &C, pending: PendingTransaction, _trusted: bool) - -> Result<(), transaction::Error> { - - // keep the pending nonces up to date - let sender = pending.transaction.sender(); - let nonce = self.next_nonce(chain, &sender); - self.next_nonces.write().insert(sender, nonce); - - // lets assume that all txs are valid - self.imported_transactions.lock().push(pending.transaction); - - Ok(()) - } - - /// Called when blocks are imported to chain, updates transactions queue. - fn chain_new_blocks(&self, _chain: &C, _imported: &[H256], _invalid: &[H256], _enacted: &[H256], _retracted: &[H256], _is_internal: bool) { - unimplemented!(); - } - - /// New chain head event. Restart mining operation. - fn update_sealing(&self, _chain: &C) { - unimplemented!(); - } - - fn work_package(&self, chain: &C) -> Option<(H256, BlockNumber, u64, U256)> { - let params = self.authoring_params(); - let open_block = chain.prepare_open_block(params.author, params.gas_range_target, params.extra_data).unwrap(); - let closed = open_block.close().unwrap(); - let header = &closed.header; - - Some((header.hash(), header.number(), header.timestamp(), *header.difficulty())) - } - - fn transaction(&self, hash: &H256) -> Option> { - self.pending_transactions.lock().get(hash).cloned().map(|tx| { - Arc::new(VerifiedTransaction::from_pending_block_transaction(tx)) - }) - } - - fn remove_transaction(&self, hash: &H256) -> Option> { - self.pending_transactions.lock().remove(hash).map(|tx| { - Arc::new(VerifiedTransaction::from_pending_block_transaction(tx)) - }) - } - - fn pending_transactions(&self, _best_block: BlockNumber) -> Option> { - Some(self.pending_transactions.lock().values().cloned().collect()) - } - - fn local_transactions(&self) -> BTreeMap { - self.local_transactions.lock().iter().map(|(hash, stats)| (*hash, stats.clone())).collect() - } - - fn ready_transactions(&self, _chain: &C, _max_len: usize, _ordering: miner::PendingOrdering) -> Vec> { - self.queued_transactions() - } - - fn pending_transaction_hashes(&self, _chain: &C) -> BTreeSet { - self.queued_transactions().into_iter().map(|tx| tx.signed().hash()).collect() - } - - fn queued_transactions(&self) -> Vec> { - self.pending_transactions.lock().values().cloned().map(|tx| { - Arc::new(VerifiedTransaction::from_pending_block_transaction(tx)) - }).collect() - } - - fn queued_transaction_hashes(&self) -> Vec { - self.pending_transactions.lock().keys().cloned().map(|hash| hash).collect() - } - - fn pending_receipts(&self, _best_block: BlockNumber) -> Option> { - Some(self.pending_receipts.lock().clone()) - } - - fn next_nonce(&self, _chain: &C, address: &Address) -> U256 { - self.next_nonces.read().get(address).cloned().unwrap_or_default() - } - - fn is_currently_sealing(&self) -> bool { - false - } - - fn queue_status(&self) -> QueueStatus { - QueueStatus { - options: verifier::Options { - minimal_gas_price: 0x1312d00.into(), - block_gas_limit: 5_000_000.into(), - tx_gas_limit: 5_000_000.into(), - no_early_reject: false, - }, - status: txpool::LightStatus { - mem_usage: 1_000, - transaction_count: 52, - senders: 1, - }, - limits: txpool::Options { - max_count: 1_024, - max_per_sender: 16, - max_mem_usage: 5_000, - }, - } - } - - /// Submit `seal` as a valid solution for the header of `pow_hash`. - /// Will check the seal, but not actually insert the block into the chain. - fn submit_seal(&self, _pow_hash: H256, _seal: Vec) -> Result { - unimplemented!(); - } - - fn sensible_gas_price(&self) -> U256 { - 20_000_000_000u64.into() - } - - fn sensible_gas_limit(&self) -> U256 { - 0x5208.into() - } - - fn set_minimal_gas_price(&self, gas_price: U256) -> Result { - let mut new_price = self.min_gas_price.write(); - match *new_price { - Some(ref mut v) => { - *v = gas_price; - Ok(true) - }, - None => { - let error_msg = "Can't update fixed gas price while automatic gas calibration is enabled."; - Err(error_msg) - }, - } - } + type State = TestState; + + fn pending_state(&self, _latest_block_number: BlockNumber) -> Option { + None + } + + fn pending_block_header(&self, _latest_block_number: BlockNumber) -> Option
{ + None + } + + fn pending_block(&self, _latest_block_number: BlockNumber) -> Option { + None + } + + fn authoring_params(&self) -> AuthoringParams { + self.authoring_params.read().clone() + } + + fn set_author(&self, author: miner::Author) { + self.authoring_params.write().author = author.address(); + if let miner::Author::Sealer(signer) = author { + *self.signer.write() = Some(signer); + } + } + + fn set_extra_data(&self, extra_data: Bytes) { + self.authoring_params.write().extra_data = extra_data; + } + + fn set_gas_range_target(&self, target: (U256, U256)) { + self.authoring_params.write().gas_range_target = target; + } + + /// Imports transactions to transaction queue. + fn import_external_transactions( + &self, + chain: &C, + transactions: Vec, + ) -> Vec> { + // lets assume that all txs are valid + let transactions: Vec<_> = transactions + .into_iter() + .map(|tx| SignedTransaction::new(tx).unwrap()) + .collect(); + self.imported_transactions + .lock() + .extend_from_slice(&transactions); + + for sender in transactions.iter().map(|tx| tx.sender()) { + let nonce = self.next_nonce(chain, &sender); + self.next_nonces.write().insert(sender, nonce); + } + + transactions.iter().map(|_| Ok(())).collect() + } + + /// Imports transactions to transaction queue. + fn import_own_transaction( + &self, + _chain: &C, + _pending: PendingTransaction, + ) -> Result<(), transaction::Error> { + // this function is no longer called directly from RPC + unimplemented!(); + } + + /// Imports transactions to queue - treats as local based on trusted flag, config, and tx source + fn import_claimed_local_transaction( + &self, + chain: &C, + pending: PendingTransaction, + _trusted: bool, + ) -> Result<(), transaction::Error> { + // keep the pending nonces up to date + let sender = pending.transaction.sender(); + let nonce = self.next_nonce(chain, &sender); + self.next_nonces.write().insert(sender, nonce); + + // lets assume that all txs are valid + self.imported_transactions.lock().push(pending.transaction); + + Ok(()) + } + + /// Called when blocks are imported to chain, updates transactions queue. + fn chain_new_blocks( + &self, + _chain: &C, + _imported: &[H256], + _invalid: &[H256], + _enacted: &[H256], + _retracted: &[H256], + _is_internal: bool, + ) { + unimplemented!(); + } + + /// New chain head event. Restart mining operation. + fn update_sealing(&self, _chain: &C, _force: ForceUpdateSealing) { + unimplemented!(); + } + + fn work_package( + &self, + chain: &C, + ) -> Option<(H256, BlockNumber, u64, U256)> { + let params = self.authoring_params(); + let open_block = chain + .prepare_open_block(params.author, params.gas_range_target, params.extra_data) + .unwrap(); + let closed = open_block.close().unwrap(); + let header = &closed.header; + + Some(( + header.hash(), + header.number(), + header.timestamp(), + *header.difficulty(), + )) + } + + fn transaction(&self, hash: &H256) -> Option> { + self.pending_transactions + .lock() + .get(hash) + .cloned() + .map(|tx| Arc::new(VerifiedTransaction::from_pending_block_transaction(tx))) + } + + fn remove_transaction(&self, hash: &H256) -> Option> { + self.pending_transactions + .lock() + .remove(hash) + .map(|tx| Arc::new(VerifiedTransaction::from_pending_block_transaction(tx))) + } + + fn pending_transactions(&self, _best_block: BlockNumber) -> Option> { + Some(self.pending_transactions.lock().values().cloned().collect()) + } + + fn local_transactions(&self) -> BTreeMap { + self.local_transactions + .lock() + .iter() + .map(|(hash, stats)| (*hash, stats.clone())) + .collect() + } + + fn ready_transactions( + &self, + _chain: &C, + _max_len: usize, + _ordering: miner::PendingOrdering, + ) -> Vec> { + self.queued_transactions() + } + + fn pending_transaction_hashes(&self, _chain: &C) -> BTreeSet { + self.queued_transactions() + .into_iter() + .map(|tx| tx.signed().hash()) + .collect() + } + + fn queued_transactions(&self) -> Vec> { + self.pending_transactions + .lock() + .values() + .cloned() + .map(|tx| Arc::new(VerifiedTransaction::from_pending_block_transaction(tx))) + .collect() + } + + fn queued_transaction_hashes(&self) -> Vec { + self.pending_transactions + .lock() + .keys() + .cloned() + .map(|hash| hash) + .collect() + } + + fn pending_receipts(&self, _best_block: BlockNumber) -> Option> { + Some(self.pending_receipts.lock().clone()) + } + + fn next_nonce(&self, _chain: &C, address: &Address) -> U256 { + self.next_nonces + .read() + .get(address) + .cloned() + .unwrap_or_default() + } + + fn is_currently_sealing(&self) -> bool { + false + } + + fn queue_status(&self) -> QueueStatus { + QueueStatus { + options: verifier::Options { + minimal_gas_price: 0x1312d00.into(), + block_gas_limit: 5_000_000.into(), + tx_gas_limit: 5_000_000.into(), + no_early_reject: false, + }, + status: txpool::LightStatus { + mem_usage: 1_000, + transaction_count: 52, + senders: 1, + }, + limits: txpool::Options { + max_count: 1_024, + max_per_sender: 16, + max_mem_usage: 5_000, + }, + } + } + + /// Submit `seal` as a valid solution for the header of `pow_hash`. + /// Will check the seal, but not actually insert the block into the chain. + fn submit_seal(&self, _pow_hash: H256, _seal: Vec) -> Result { + unimplemented!(); + } + + fn sensible_gas_price(&self) -> U256 { + 20_000_000_000u64.into() + } + + fn sensible_gas_limit(&self) -> U256 { + 0x5208.into() + } + + fn set_minimal_gas_price(&self, gas_price: U256) -> Result { + let mut new_price = self.min_gas_price.write(); + match *new_price { + Some(ref mut v) => { + *v = gas_price; + Ok(true) + } + None => { + let error_msg = + "Can't update fixed gas price while automatic gas calibration is enabled."; + Err(error_msg) + } + } + } } diff --git a/rpc/src/v1/tests/helpers/mod.rs b/rpc/src/v1/tests/helpers/mod.rs index 0cecd271c05..69220e34c1f 100644 --- a/rpc/src/v1/tests/helpers/mod.rs +++ b/rpc/src/v1/tests/helpers/mod.rs @@ -1,27 +1,27 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Test rpc services. mod miner_service; mod snapshot_service; mod sync_provider; -mod update_service; -pub use self::miner_service::TestMinerService; -pub use self::snapshot_service::TestSnapshotService; -pub use self::sync_provider::{Config, TestSyncProvider}; -pub use self::update_service::TestUpdater; +pub use self::{ + miner_service::TestMinerService, + snapshot_service::TestSnapshotService, + sync_provider::{Config, TestSyncProvider}, +}; diff --git a/rpc/src/v1/tests/helpers/snapshot_service.rs b/rpc/src/v1/tests/helpers/snapshot_service.rs index 5450886bb42..cbc4d79d897 100644 --- a/rpc/src/v1/tests/helpers/snapshot_service.rs +++ b/rpc/src/v1/tests/helpers/snapshot_service.rs @@ -1,20 +1,20 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use ethcore::snapshot::{ManifestData, RestorationStatus, SnapshotService}; +use ethcore::snapshot::{CreationStatus, ManifestData, RestorationStatus, SnapshotService}; use bytes::Bytes; use ethereum_types::H256; @@ -22,33 +22,47 @@ use parking_lot::Mutex; /// Mocked snapshot service (used for sync info extensions). pub struct TestSnapshotService { - status: Mutex, + status: Mutex, } impl TestSnapshotService { - /// Create a test snapshot service. Only the `status` function matters -- it'll - /// return `Inactive` by default. - pub fn new() -> Self { - TestSnapshotService { - status: Mutex::new(RestorationStatus::Inactive), - } - } - - /// Set the restoration status. - pub fn set_status(&self, status: RestorationStatus) { - *self.status.lock() = status; - } + /// Create a test snapshot service. Only the `status` function matters -- it'll + /// return `Inactive` by default. + pub fn new() -> Self { + TestSnapshotService { + status: Mutex::new(RestorationStatus::Inactive), + } + } + + /// Set the restoration status. + pub fn set_status(&self, status: RestorationStatus) { + *self.status.lock() = status; + } } impl SnapshotService for TestSnapshotService { - fn manifest(&self) -> Option { None } - fn supported_versions(&self) -> Option<(u64, u64)> { None } - fn completed_chunks(&self) -> Option> { Some(vec![]) } - fn chunk(&self, _hash: H256) -> Option { None } - fn status(&self) -> RestorationStatus { self.status.lock().clone() } - fn begin_restore(&self, _manifest: ManifestData) { } - fn abort_restore(&self) { } - fn restore_state_chunk(&self, _hash: H256, _chunk: Bytes) { } - fn restore_block_chunk(&self, _hash: H256, _chunk: Bytes) { } - fn shutdown(&self) { } + fn manifest(&self) -> Option { + None + } + fn supported_versions(&self) -> Option<(u64, u64)> { + None + } + fn completed_chunks(&self) -> Option> { + Some(vec![]) + } + fn chunk(&self, _hash: H256) -> Option { + None + } + fn restoration_status(&self) -> RestorationStatus { + self.status.lock().clone() + } + fn creation_status(&self) -> CreationStatus { + CreationStatus::Inactive + } + fn begin_restore(&self, _manifest: ManifestData) {} + fn abort_restore(&self) {} + fn abort_snapshot(&self) {} + fn restore_state_chunk(&self, _hash: H256, _chunk: Bytes) {} + fn restore_block_chunk(&self, _hash: H256, _chunk: Bytes) {} + fn shutdown(&self) {} } diff --git a/rpc/src/v1/tests/helpers/sync_provider.rs b/rpc/src/v1/tests/helpers/sync_provider.rs index 37c2f935531..6b58bd46f19 100644 --- a/rpc/src/v1/tests/helpers/sync_provider.rs +++ b/rpc/src/v1/tests/helpers/sync_provider.rs @@ -1,126 +1,129 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Test implementation of SyncProvider. -use std::collections::BTreeMap; use ethereum_types::H256; -use parking_lot::RwLock; -use sync::{SyncProvider, EthProtocolInfo, SyncStatus, SyncState, PeerInfo, TransactionStats}; use network::client_version::ClientVersion; +use parking_lot::RwLock; +use stats::{prometheus, PrometheusMetrics}; +use std::collections::BTreeMap; +use sync::{EthProtocolInfo, PeerInfo, SyncProvider, SyncState, SyncStatus, TransactionStats}; /// TestSyncProvider config. pub struct Config { - /// Protocol version. - pub network_id: u64, - /// Number of peers. - pub num_peers: usize, + /// Protocol version. + pub network_id: u64, + /// Number of peers. + pub num_peers: usize, } /// Test sync provider. pub struct TestSyncProvider { - /// Sync status. - pub status: RwLock, + /// Sync status. + pub status: RwLock, } impl TestSyncProvider { - /// Creates new sync provider. - pub fn new(config: Config) -> Self { - TestSyncProvider { - status: RwLock::new(SyncStatus { - state: SyncState::Idle, - network_id: config.network_id, - protocol_version: 63, - start_block_number: 0, - last_imported_block_number: None, - highest_block_number: None, - blocks_total: 0, - blocks_received: 0, - num_peers: config.num_peers, - num_active_peers: 0, - mem_used: 0, - num_snapshot_chunks: 0, - snapshot_chunks_done: 0, - last_imported_old_block_number: None, - }), - } - } + /// Creates new sync provider. + pub fn new(config: Config) -> Self { + TestSyncProvider { + status: RwLock::new(SyncStatus { + state: SyncState::Idle, + network_id: config.network_id, + protocol_version: 64, + start_block_number: 0, + last_imported_block_number: None, + highest_block_number: None, + blocks_total: 0, + blocks_received: 0, + num_peers: config.num_peers, + num_active_peers: 0, + num_snapshot_chunks: 0, + snapshot_chunks_done: 0, + last_imported_old_block_number: None, + item_sizes: BTreeMap::new(), + }), + } + } + + /// Simulate importing blocks. + pub fn increase_imported_block_number(&self, count: u64) { + let mut status = self.status.write(); + let current_number = status.last_imported_block_number.unwrap_or(0); + status.last_imported_block_number = Some(current_number + count); + } +} - /// Simulate importing blocks. - pub fn increase_imported_block_number(&self, count: u64) { - let mut status = self.status.write(); - let current_number = status.last_imported_block_number.unwrap_or(0); - status.last_imported_block_number = Some(current_number + count); - } +impl PrometheusMetrics for TestSyncProvider { + fn prometheus_metrics(&self, _: &mut prometheus::Registry) {} } impl SyncProvider for TestSyncProvider { - fn status(&self) -> SyncStatus { - self.status.read().clone() - } + fn status(&self) -> SyncStatus { + self.status.read().clone() + } - fn peers(&self) -> Vec { - vec![ - PeerInfo { - id: Some("node1".to_owned()), - client_version: ClientVersion::from("Parity-Ethereum/1/v2.4.0/linux/rustc"), - capabilities: vec!["eth/62".to_owned(), "eth/63".to_owned()], - remote_address: "127.0.0.1:7777".to_owned(), - local_address: "127.0.0.1:8888".to_owned(), - eth_info: Some(EthProtocolInfo { - version: 62, - difficulty: Some(40.into()), - head: 50.into(), - }), - pip_info: None, - }, - PeerInfo { - id: None, - client_version: ClientVersion::from("Parity-Ethereum/2/v2.4.0/linux/rustc"), - capabilities: vec!["eth/63".to_owned(), "eth/64".to_owned()], - remote_address: "Handshake".to_owned(), - local_address: "127.0.0.1:3333".to_owned(), - eth_info: Some(EthProtocolInfo { - version: 64, - difficulty: None, - head: 60.into() - }), - pip_info: None, - } - ] - } + fn peers(&self) -> Vec { + vec![ + PeerInfo { + id: Some("node1".to_owned()), + client_version: ClientVersion::from("Parity-Ethereum/1/v2.4.0/linux/rustc"), + capabilities: vec!["eth/63".to_owned(), "eth/64".to_owned()], + remote_address: "127.0.0.1:7777".to_owned(), + local_address: "127.0.0.1:8888".to_owned(), + eth_info: Some(EthProtocolInfo { + version: 63, + difficulty: Some(40.into()), + head: 50.into(), + }), + }, + PeerInfo { + id: None, + client_version: ClientVersion::from("Open-Ethereum/2/v2.4.0/linux/rustc"), + capabilities: vec!["eth/64".to_owned(), "eth/65".to_owned()], + remote_address: "Handshake".to_owned(), + local_address: "127.0.0.1:3333".to_owned(), + eth_info: Some(EthProtocolInfo { + version: 65, + difficulty: None, + head: 60.into(), + }), + }, + ] + } - fn enode(&self) -> Option { - None - } + fn enode(&self) -> Option { + None + } - fn transactions_stats(&self) -> BTreeMap { - map![ - 1.into() => TransactionStats { - first_seen: 10, - propagated_to: map![ - 128.into() => 16 - ], - }, - 5.into() => TransactionStats { - first_seen: 16, - propagated_to: map![ - 16.into() => 1 - ], - } - ] - } + fn transactions_stats(&self) -> BTreeMap { + map![ + 1.into() => TransactionStats { + first_seen: 10, + propagated_to: map![ + 128.into() => 16 + ], + }, + 5.into() => TransactionStats { + first_seen: 16, + propagated_to: map![ + 16.into() => 1 + ], + } + ] + } } diff --git a/rpc/src/v1/tests/helpers/update_service.rs b/rpc/src/v1/tests/helpers/update_service.rs deleted file mode 100644 index ccf3315c47f..00000000000 --- a/rpc/src/v1/tests/helpers/update_service.rs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Test implementation of fetch client. - -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use semver::Version; -use updater::{Service as UpdateService, CapState, ReleaseInfo, VersionInfo, OperationsInfo, ReleaseTrack}; - -/// Test implementation of fetcher. Will always return the same file. -#[derive(Default)] -pub struct TestUpdater { - updated: AtomicBool, - current_block: AtomicUsize, -} - -impl TestUpdater { - /// Update the (faked) current block. - pub fn set_current_block(&self, n: usize) { - self.current_block.store(n, Ordering::Relaxed); - } - - /// Update the (faked) current block. - pub fn set_updated(&self, v: bool) { - self.updated.store(v, Ordering::Relaxed); - } -} - -impl UpdateService for TestUpdater { - fn capability(&self) -> CapState { - if self.updated.load(Ordering::Relaxed) { - CapState::Capable - } else { - if self.current_block.load(Ordering::Relaxed) < 15100 { - CapState::CapableUntil(15100) - } else { - CapState::IncapableSince(15100) - } - } - } - - fn upgrade_ready(&self) -> Option { - if self.updated.load(Ordering::Relaxed) { - None - } else { - self.info().map(|i| i.track) - } - } - - fn execute_upgrade(&self) -> bool { - if self.updated.load(Ordering::Relaxed) { - false - } else { - self.updated.store(true, Ordering::Relaxed); - true - } - } - - fn version_info(&self) -> VersionInfo { - VersionInfo { - track: ReleaseTrack::Beta, - version: Version{major: 1, minor: 5, patch: 0, build: vec![], pre: vec![]}, - hash: 150.into(), - } - } - - fn info(&self) -> Option { - Some(OperationsInfo { - fork: 15100, - this_fork: Some(15000), - track: ReleaseInfo { - version: VersionInfo { - track: ReleaseTrack::Beta, - version: Version{major: 1, minor: 5, patch: 1, build: vec![], pre: vec![]}, - hash: 151.into(), - }, - is_critical: true, - fork: 15100, - binary: Some(1510.into()), - }, - minor: None, - }) - } -} diff --git a/rpc/src/v1/tests/mocked/debug.rs b/rpc/src/v1/tests/mocked/debug.rs index ffbe8d4d1c9..ea2ef979ee0 100644 --- a/rpc/src/v1/tests/mocked/debug.rs +++ b/rpc/src/v1/tests/mocked/debug.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use std::sync::Arc; @@ -22,16 +22,16 @@ use jsonrpc_core::IoHandler; use v1::{Debug, DebugClient}; fn io() -> IoHandler { - let client = Arc::new(TestBlockChainClient::new()); + let client = Arc::new(TestBlockChainClient::new()); - let mut io = IoHandler::new(); - io.extend_with(DebugClient::new(client).to_delegate()); - io + let mut io = IoHandler::new(); + io.extend_with(DebugClient::new(client).to_delegate()); + io } #[test] fn rpc_debug_get_bad_blocks() { - let request = r#"{"jsonrpc": "2.0", "method": "debug_getBadBlocks", "params": [], "id": 1}"#; - let response = "{\"jsonrpc\":\"2.0\",\"result\":[{\"author\":\"0x0000000000000000000000000000000000000000\",\"difficulty\":\"0x0\",\"extraData\":\"0x\",\"gasLimit\":\"0x0\",\"gasUsed\":\"0x0\",\"hash\":\"0x27bfb37e507ce90da141307204b1c6ba24194380613590ac50ca4b1d7198ff65\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0x0000000000000000000000000000000000000000\",\"number\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"reason\":\"Invalid block\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"rlp\":\"\\\"0x010203\\\"\",\"sealFields\":[],\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"size\":\"0x3\",\"stateRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"timestamp\":\"0x0\",\"totalDifficulty\":null,\"transactions\":[],\"transactionsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"uncles\":[]}],\"id\":1}"; - assert_eq!(io().handle_request_sync(request), Some(response.to_owned())); + let request = r#"{"jsonrpc": "2.0", "method": "debug_getBadBlocks", "params": [], "id": 1}"#; + let response = "{\"jsonrpc\":\"2.0\",\"result\":[{\"author\":\"0x0000000000000000000000000000000000000000\",\"difficulty\":\"0x0\",\"extraData\":\"0x\",\"gasLimit\":\"0x0\",\"gasUsed\":\"0x0\",\"hash\":\"0x27bfb37e507ce90da141307204b1c6ba24194380613590ac50ca4b1d7198ff65\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0x0000000000000000000000000000000000000000\",\"number\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"reason\":\"Invalid block\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"rlp\":\"\\\"0x010203\\\"\",\"sealFields\":[],\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"size\":\"0x3\",\"stateRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"timestamp\":\"0x0\",\"totalDifficulty\":null,\"transactions\":[],\"transactionsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"uncles\":[]}],\"id\":1}"; + assert_eq!(io().handle_request_sync(request), Some(response.to_owned())); } diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 75e05ce4674..c50a61f9ded 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -1,344 +1,446 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::str::FromStr; -use std::collections::HashMap; -use std::sync::Arc; -use std::time::{Instant, Duration, SystemTime, UNIX_EPOCH}; +use std::{ + collections::HashMap, + str::FromStr, + sync::Arc, + time::{Duration, Instant, SystemTime, UNIX_EPOCH}, +}; use accounts::AccountProvider; -use ethcore::client::{BlockChainClient, BlockId, EachBlockWith, Executed, TestBlockChainClient, TransactionId}; -use ethcore::miner::{self, MinerService}; -use ethereum_types::{H160, H256, U256, Address}; +use ethcore::{ + client::{BlockChainClient, EachBlockWith, Executed, TestBlockChainClient}, + miner::{self, MinerService}, +}; +use ethereum_types::{Address, Bloom, H160, H256, U256}; use miner::external::ExternalMiner; use parity_runtime::Runtime; use parking_lot::Mutex; use rlp; use rustc_hex::{FromHex, ToHex}; use sync::SyncState; -use types::transaction::{Transaction, Action}; -use types::log_entry::{LocalizedLogEntry, LogEntry}; -use types::receipt::{LocalizedReceipt, TransactionOutcome}; +use types::{ + ids::{BlockId, TransactionId}, + log_entry::{LocalizedLogEntry, LogEntry}, + receipt::{LocalizedReceipt, RichReceipt, TransactionOutcome}, + transaction::{Action, Transaction}, +}; use jsonrpc_core::IoHandler; -use v1::{Eth, EthClient, EthClientOptions, EthFilter, EthFilterClient}; -use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService, TestSnapshotService}; -use v1::metadata::Metadata; +use v1::{ + metadata::Metadata, + tests::helpers::{Config, TestMinerService, TestSnapshotService, TestSyncProvider}, + Eth, EthClient, EthClientOptions, EthFilter, EthFilterClient, +}; fn blockchain_client() -> Arc { - let client = TestBlockChainClient::new(); - Arc::new(client) + let client = TestBlockChainClient::new(); + Arc::new(client) } fn accounts_provider() -> Arc { - Arc::new(AccountProvider::transient_provider()) + Arc::new(AccountProvider::transient_provider()) } fn sync_provider() -> Arc { - Arc::new(TestSyncProvider::new(Config { - network_id: 3, - num_peers: 120, - })) + Arc::new(TestSyncProvider::new(Config { + network_id: 3, + num_peers: 120, + })) } fn miner_service() -> Arc { - Arc::new(TestMinerService::default()) + Arc::new(TestMinerService::default()) } fn snapshot_service() -> Arc { - Arc::new(TestSnapshotService::new()) + Arc::new(TestSnapshotService::new()) } struct EthTester { - pub runtime: Runtime, - pub client: Arc, - pub sync: Arc, - pub accounts_provider: Arc, - pub miner: Arc, - pub snapshot: Arc, - hashrates: Arc>>, - pub io: IoHandler, + pub runtime: Runtime, + pub client: Arc, + pub sync: Arc, + pub accounts_provider: Arc, + pub miner: Arc, + pub snapshot: Arc, + hashrates: Arc>>, + pub io: IoHandler, } impl Default for EthTester { - fn default() -> Self { - Self::new_with_options(Default::default()) - } + fn default() -> Self { + Self::new_with_options(Default::default()) + } } impl EthTester { - pub fn new_with_options(options: EthClientOptions) -> Self { - let runtime = Runtime::with_thread_count(1); - let client = blockchain_client(); - let sync = sync_provider(); - let ap = accounts_provider(); - let ap2 = ap.clone(); - let opt_ap = Arc::new(move || ap2.accounts().unwrap_or_default()) as _; - let miner = miner_service(); - let snapshot = snapshot_service(); - let hashrates = Arc::new(Mutex::new(HashMap::new())); - let external_miner = Arc::new(ExternalMiner::new(hashrates.clone())); - let eth = EthClient::new(&client, &snapshot, &sync, &opt_ap, &miner, &external_miner, options).to_delegate(); - let filter = EthFilterClient::new(client.clone(), miner.clone(), 60).to_delegate(); - - let mut io: IoHandler = IoHandler::default(); - io.extend_with(eth); - io.extend_with(filter); - - EthTester { - runtime, - client: client, - sync: sync, - accounts_provider: ap, - miner: miner, - snapshot: snapshot, - io: io, - hashrates: hashrates, - } - } - - pub fn add_blocks(&self, count: usize, with: EachBlockWith) { - self.client.add_blocks(count, with); - self.sync.increase_imported_block_number(count as u64); - } + pub fn new_with_options(options: EthClientOptions) -> Self { + let runtime = Runtime::with_thread_count(1); + let client = blockchain_client(); + let sync = sync_provider(); + let ap = accounts_provider(); + let ap2 = ap.clone(); + let opt_ap = Arc::new(move || ap2.accounts().unwrap_or_default()) as _; + let miner = miner_service(); + let snapshot = snapshot_service(); + let hashrates = Arc::new(Mutex::new(HashMap::new())); + let external_miner = Arc::new(ExternalMiner::new(hashrates.clone())); + let eth = EthClient::new( + &client, + &snapshot, + &sync, + &opt_ap, + &miner, + &external_miner, + options, + ) + .to_delegate(); + let filter = EthFilterClient::new(client.clone(), miner.clone(), 60).to_delegate(); + + let mut io: IoHandler = IoHandler::default(); + io.extend_with(eth); + io.extend_with(filter); + + EthTester { + runtime, + client, + sync, + accounts_provider: ap, + miner, + snapshot, + io, + hashrates, + } + } + + pub fn add_blocks(&self, count: usize, with: EachBlockWith) { + self.client.add_blocks(count, with); + self.sync.increase_imported_block_number(count as u64); + } } #[test] fn rpc_eth_protocol_version() { - let request = r#"{"jsonrpc": "2.0", "method": "eth_protocolVersion", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"63","id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "eth_protocolVersion", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"64","id":1}"#; - assert_eq!(EthTester::default().io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + EthTester::default().io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_syncing() { - use ethcore::snapshot::RestorationStatus; - - let request = r#"{"jsonrpc": "2.0", "method": "eth_syncing", "params": [], "id": 1}"#; - - let tester = EthTester::default(); - - let false_res = r#"{"jsonrpc":"2.0","result":false,"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(false_res.to_owned())); - - { - let mut status = tester.sync.status.write(); - status.state = SyncState::Blocks; - status.highest_block_number = Some(2500); - } - - // "sync" to 1000 blocks. - // causes TestBlockChainClient to return 1000 for its best block number. - tester.add_blocks(1000, EachBlockWith::Nothing); - - let true_res = r#"{"jsonrpc":"2.0","result":{"currentBlock":"0x3e8","highestBlock":"0x9c4","startingBlock":"0x0","warpChunksAmount":null,"warpChunksProcessed":null},"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(true_res.to_owned())); - - *tester.client.ancient_block.write() = None; - *tester.client.first_block.write() = None; - - let snap_res = r#"{"jsonrpc":"2.0","result":{"currentBlock":"0x3e8","highestBlock":"0x9c4","startingBlock":"0x0","warpChunksAmount":"0x32","warpChunksProcessed":"0x18"},"id":1}"#; - tester.snapshot.set_status(RestorationStatus::Ongoing { - state_chunks: 40, - block_chunks: 10, - state_chunks_done: 18, - block_chunks_done: 6, - }); - - assert_eq!(tester.io.handle_request_sync(request), Some(snap_res.to_owned())); - - tester.snapshot.set_status(RestorationStatus::Inactive); - - // finish "syncing" - tester.add_blocks(1500, EachBlockWith::Nothing); - - { - let mut status = tester.sync.status.write(); - status.state = SyncState::Idle; - } - - assert_eq!(tester.io.handle_request_sync(request), Some(false_res.to_owned())); + use ethcore::snapshot::RestorationStatus; + + let request = r#"{"jsonrpc": "2.0", "method": "eth_syncing", "params": [], "id": 1}"#; + + let tester = EthTester::default(); + + let false_res = r#"{"jsonrpc":"2.0","result":false,"id":1}"#; + assert_eq!( + tester.io.handle_request_sync(request), + Some(false_res.to_owned()) + ); + + { + let mut status = tester.sync.status.write(); + status.state = SyncState::Blocks; + status.highest_block_number = Some(2500); + } + + // "sync" to 1000 blocks. + // causes TestBlockChainClient to return 1000 for its best block number. + tester.add_blocks(1000, EachBlockWith::Nothing); + + let true_res = r#"{"jsonrpc":"2.0","result":{"currentBlock":"0x3e8","highestBlock":"0x9c4","startingBlock":"0x0","warpChunksAmount":null,"warpChunksProcessed":null},"id":1}"#; + assert_eq!( + tester.io.handle_request_sync(request), + Some(true_res.to_owned()) + ); + + *tester.client.ancient_block.write() = None; + *tester.client.first_block.write() = None; + + let snap_res = r#"{"jsonrpc":"2.0","result":{"currentBlock":"0x3e8","highestBlock":"0x9c4","startingBlock":"0x0","warpChunksAmount":"0x32","warpChunksProcessed":"0x18"},"id":1}"#; + tester.snapshot.set_status(RestorationStatus::Ongoing { + block_number: 0, + state_chunks: 40, + block_chunks: 10, + state_chunks_done: 18, + block_chunks_done: 6, + }); + + assert_eq!( + tester.io.handle_request_sync(request), + Some(snap_res.to_owned()) + ); + + tester.snapshot.set_status(RestorationStatus::Inactive); + + // finish "syncing" + tester.add_blocks(1500, EachBlockWith::Nothing); + + { + let mut status = tester.sync.status.write(); + status.state = SyncState::Idle; + } + + assert_eq!( + tester.io.handle_request_sync(request), + Some(false_res.to_owned()) + ); } #[test] fn rpc_eth_chain_id() { - let tester = EthTester::default(); - let request = r#"{"jsonrpc": "2.0", "method": "eth_chainId", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; - - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + let tester = EthTester::default(); + let request = r#"{"jsonrpc": "2.0", "method": "eth_chainId", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_hashrate() { - let tester = EthTester::default(); - tester.hashrates.lock().insert(H256::from(0), (Instant::now() + Duration::from_secs(2), U256::from(0xfffa))); - tester.hashrates.lock().insert(H256::from(0), (Instant::now() + Duration::from_secs(2), U256::from(0xfffb))); - tester.hashrates.lock().insert(H256::from(1), (Instant::now() + Duration::from_secs(2), U256::from(0x1))); - - let request = r#"{"jsonrpc": "2.0", "method": "eth_hashrate", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"0xfffc","id":1}"#; - - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + let tester = EthTester::default(); + tester.hashrates.lock().insert( + H256::from(0), + (Instant::now() + Duration::from_secs(2), U256::from(0xfffa)), + ); + tester.hashrates.lock().insert( + H256::from(0), + (Instant::now() + Duration::from_secs(2), U256::from(0xfffb)), + ); + tester.hashrates.lock().insert( + H256::from(1), + (Instant::now() + Duration::from_secs(2), U256::from(0x1)), + ); + + let request = r#"{"jsonrpc": "2.0", "method": "eth_hashrate", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0xfffc","id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_logs() { - let tester = EthTester::default(); - tester.client.set_logs(vec![LocalizedLogEntry { - block_number: 1, - block_hash: H256::default(), - entry: LogEntry { - address: Address::default(), - topics: vec![], - data: vec![1,2,3], - }, - transaction_index: 0, - transaction_log_index: 0, - transaction_hash: H256::default(), - log_index: 0, - }, LocalizedLogEntry { - block_number: 1, - block_hash: H256::default(), - entry: LogEntry { - address: Address::default(), - topics: vec![], - data: vec![1,2,3], - }, - transaction_index: 0, - transaction_log_index: 1, - transaction_hash: H256::default(), - log_index: 1, - }]); - - let request1 = r#"{"jsonrpc": "2.0", "method": "eth_getLogs", "params": [{}], "id": 1}"#; - let request2 = r#"{"jsonrpc": "2.0", "method": "eth_getLogs", "params": [{"limit":1}], "id": 1}"#; - let request3 = r#"{"jsonrpc": "2.0", "method": "eth_getLogs", "params": [{"limit":0}], "id": 1}"#; - - let response1 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x0","removed":false,"topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"},{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x1","removed":false,"topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x1","type":"mined"}],"id":1}"#; - let response2 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x1","removed":false,"topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x1","type":"mined"}],"id":1}"#; - let response3 = r#"{"jsonrpc":"2.0","result":[],"id":1}"#; - - assert_eq!(tester.io.handle_request_sync(request1), Some(response1.to_owned())); - assert_eq!(tester.io.handle_request_sync(request2), Some(response2.to_owned())); - assert_eq!(tester.io.handle_request_sync(request3), Some(response3.to_owned())); + let tester = EthTester::default(); + tester.client.set_logs(vec![ + LocalizedLogEntry { + block_number: 1, + block_hash: H256::default(), + entry: LogEntry { + address: Address::default(), + topics: vec![], + data: vec![1, 2, 3], + }, + transaction_index: 0, + transaction_log_index: 0, + transaction_hash: H256::default(), + log_index: 0, + }, + LocalizedLogEntry { + block_number: 1, + block_hash: H256::default(), + entry: LogEntry { + address: Address::default(), + topics: vec![], + data: vec![1, 2, 3], + }, + transaction_index: 0, + transaction_log_index: 1, + transaction_hash: H256::default(), + log_index: 1, + }, + ]); + + let request1 = r#"{"jsonrpc": "2.0", "method": "eth_getLogs", "params": [{}], "id": 1}"#; + let request2 = + r#"{"jsonrpc": "2.0", "method": "eth_getLogs", "params": [{"limit":1}], "id": 1}"#; + let request3 = + r#"{"jsonrpc": "2.0", "method": "eth_getLogs", "params": [{"limit":0}], "id": 1}"#; + + let response1 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x0","removed":false,"topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"},{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x1","removed":false,"topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x1","type":"mined"}],"id":1}"#; + let response2 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x1","removed":false,"topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x1","type":"mined"}],"id":1}"#; + let response3 = r#"{"jsonrpc":"2.0","result":[],"id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(request1), + Some(response1.to_owned()) + ); + assert_eq!( + tester.io.handle_request_sync(request2), + Some(response2.to_owned()) + ); + assert_eq!( + tester.io.handle_request_sync(request3), + Some(response3.to_owned()) + ); } #[test] fn rpc_eth_logs_error() { - let tester = EthTester::default(); - tester.client.set_error_on_logs(Some(BlockId::Hash(H256::from([5u8].as_ref())))); - let request = r#"{"jsonrpc": "2.0", "method": "eth_getLogs", "params": [{"limit":1,"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"One of the blocks specified in filter (fromBlock, toBlock or blockHash) cannot be found","data":"0x0500000000000000000000000000000000000000000000000000000000000000"},"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + let tester = EthTester::default(); + tester + .client + .set_error_on_logs(Some(BlockId::Hash(H256::from([5u8].as_ref())))); + let request = r#"{"jsonrpc": "2.0", "method": "eth_getLogs", "params": [{"limit":1,"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"One of the blocks specified in filter (fromBlock, toBlock or blockHash) cannot be found","data":"0x0500000000000000000000000000000000000000000000000000000000000000"},"id":1}"#; + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_logs_filter() { - let tester = EthTester::default(); - // Set some logs - tester.client.set_logs(vec![LocalizedLogEntry { - block_number: 1, - block_hash: H256::default(), - entry: LogEntry { - address: Address::default(), - topics: vec![], - data: vec![1,2,3], - }, - transaction_index: 0, - transaction_log_index: 0, - transaction_hash: H256::default(), - log_index: 0, - }, LocalizedLogEntry { - block_number: 1, - block_hash: H256::default(), - entry: LogEntry { - address: Address::default(), - topics: vec![], - data: vec![1,2,3], - }, - transaction_index: 0, - transaction_log_index: 1, - transaction_hash: H256::default(), - log_index: 1, - }]); - - // Register filters first - let request_default = r#"{"jsonrpc": "2.0", "method": "eth_newFilter", "params": [{}], "id": 1}"#; - let request_limit = r#"{"jsonrpc": "2.0", "method": "eth_newFilter", "params": [{"limit":1}], "id": 1}"#; - let response1 = r#"{"jsonrpc":"2.0","result":"0x0","id":1}"#; - let response2 = r#"{"jsonrpc":"2.0","result":"0x1","id":1}"#; - - assert_eq!(tester.io.handle_request_sync(request_default), Some(response1.to_owned())); - assert_eq!(tester.io.handle_request_sync(request_limit), Some(response2.to_owned())); - - let request_changes1 = r#"{"jsonrpc": "2.0", "method": "eth_getFilterChanges", "params": ["0x0"], "id": 1}"#; - let request_changes2 = r#"{"jsonrpc": "2.0", "method": "eth_getFilterChanges", "params": ["0x1"], "id": 1}"#; - let response1 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x0","removed":false,"topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"},{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x1","removed":false,"topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x1","type":"mined"}],"id":1}"#; - let response2 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x1","removed":false,"topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x1","type":"mined"}],"id":1}"#; - - assert_eq!(tester.io.handle_request_sync(request_changes1), Some(response1.to_owned())); - assert_eq!(tester.io.handle_request_sync(request_changes2), Some(response2.to_owned())); + let tester = EthTester::default(); + // Set some logs + tester.client.set_logs(vec![ + LocalizedLogEntry { + block_number: 1, + block_hash: H256::default(), + entry: LogEntry { + address: Address::default(), + topics: vec![], + data: vec![1, 2, 3], + }, + transaction_index: 0, + transaction_log_index: 0, + transaction_hash: H256::default(), + log_index: 0, + }, + LocalizedLogEntry { + block_number: 1, + block_hash: H256::default(), + entry: LogEntry { + address: Address::default(), + topics: vec![], + data: vec![1, 2, 3], + }, + transaction_index: 0, + transaction_log_index: 1, + transaction_hash: H256::default(), + log_index: 1, + }, + ]); + + // Register filters first + let request_default = + r#"{"jsonrpc": "2.0", "method": "eth_newFilter", "params": [{}], "id": 1}"#; + let request_limit = + r#"{"jsonrpc": "2.0", "method": "eth_newFilter", "params": [{"limit":1}], "id": 1}"#; + let response1 = r#"{"jsonrpc":"2.0","result":"0x0","id":1}"#; + let response2 = r#"{"jsonrpc":"2.0","result":"0x1","id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(request_default), + Some(response1.to_owned()) + ); + assert_eq!( + tester.io.handle_request_sync(request_limit), + Some(response2.to_owned()) + ); + + let request_changes1 = + r#"{"jsonrpc": "2.0", "method": "eth_getFilterChanges", "params": ["0x0"], "id": 1}"#; + let request_changes2 = + r#"{"jsonrpc": "2.0", "method": "eth_getFilterChanges", "params": ["0x1"], "id": 1}"#; + let response1 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x0","removed":false,"topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"},{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x1","removed":false,"topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x1","type":"mined"}],"id":1}"#; + let response2 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x1","removed":false,"topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x1","type":"mined"}],"id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(request_changes1), + Some(response1.to_owned()) + ); + assert_eq!( + tester.io.handle_request_sync(request_changes2), + Some(response2.to_owned()) + ); } #[test] fn rpc_blocks_filter() { - let tester = EthTester::default(); - let request_filter = r#"{"jsonrpc": "2.0", "method": "eth_newBlockFilter", "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"0x0","id":1}"#; - - assert_eq!(tester.io.handle_request_sync(request_filter), Some(response.to_owned())); - - let request_changes = r#"{"jsonrpc": "2.0", "method": "eth_getFilterChanges", "params": ["0x0"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":[],"id":1}"#; - - assert_eq!(tester.io.handle_request_sync(request_changes), Some(response.to_owned())); - - tester.client.add_blocks(2, EachBlockWith::Nothing); - - let hash1 = tester.client.block_hash(BlockId::Number(1)).unwrap(); - let hash2 = tester.client.block_hash(BlockId::Number(2)).unwrap(); - let response = format!( - r#"{{"jsonrpc":"2.0","result":["0x{:x}","0x{:x}"],"id":1}}"#, - hash1, - hash2); - - assert_eq!(tester.io.handle_request_sync(request_changes), Some(response.to_owned())); - - // in the case of a re-org we get same block number if hash is different - BlockId::Number(2) - tester.client.blocks.write().remove(&hash2).unwrap(); - tester.client.numbers.write().remove(&2).unwrap(); - *tester.client.last_hash.write() = hash1; - tester.client.add_blocks(2, EachBlockWith::Uncle); - - let request_changes = r#"{"jsonrpc": "2.0", "method": "eth_getFilterChanges", "params": ["0x0"], "id": 2}"#; - let response = format!( - r#"{{"jsonrpc":"2.0","result":["0x{:x}","0x{:x}"],"id":2}}"#, - tester.client.block_hash(BlockId::Number(2)).unwrap(), - tester.client.block_hash(BlockId::Number(3)).unwrap()); - - assert_eq!(tester.io.handle_request_sync(request_changes), Some(response.to_owned())); + let tester = EthTester::default(); + let request_filter = r#"{"jsonrpc": "2.0", "method": "eth_newBlockFilter", "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x0","id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(request_filter), + Some(response.to_owned()) + ); + + let request_changes = + r#"{"jsonrpc": "2.0", "method": "eth_getFilterChanges", "params": ["0x0"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":[],"id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(request_changes), + Some(response.to_owned()) + ); + + tester.client.add_blocks(2, EachBlockWith::Nothing); + + let hash1 = tester.client.block_hash(BlockId::Number(1)).unwrap(); + let hash2 = tester.client.block_hash(BlockId::Number(2)).unwrap(); + let response = format!( + r#"{{"jsonrpc":"2.0","result":["0x{:x}","0x{:x}"],"id":1}}"#, + hash1, hash2 + ); + + assert_eq!( + tester.io.handle_request_sync(request_changes), + Some(response.to_owned()) + ); + + // in the case of a re-org we get same block number if hash is different - BlockId::Number(2) + tester.client.blocks.write().remove(&hash2).unwrap(); + tester.client.numbers.write().remove(&2).unwrap(); + *tester.client.last_hash.write() = hash1; + tester.client.add_blocks(2, EachBlockWith::Uncle); + + let request_changes = + r#"{"jsonrpc": "2.0", "method": "eth_getFilterChanges", "params": ["0x0"], "id": 2}"#; + let response = format!( + r#"{{"jsonrpc":"2.0","result":["0x{:x}","0x{:x}"],"id":2}}"#, + tester.client.block_hash(BlockId::Number(2)).unwrap(), + tester.client.block_hash(BlockId::Number(3)).unwrap() + ); + + assert_eq!( + tester.io.handle_request_sync(request_changes), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_submit_hashrate() { - let tester = EthTester::default(); + let tester = EthTester::default(); - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", "method": "eth_submitHashrate", "params": [ @@ -346,295 +448,349 @@ fn rpc_eth_submit_hashrate() { "0x59daa26581d0acd1fce254fb7e85952f4c09d0915afd33d3886cd914bc7d283c"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); - assert_eq!(tester.hashrates.lock().get(&H256::from("0x59daa26581d0acd1fce254fb7e85952f4c09d0915afd33d3886cd914bc7d283c")).cloned().unwrap().1, - U256::from(0x500_000)); + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); + assert_eq!( + tester + .hashrates + .lock() + .get(&H256::from( + "0x59daa26581d0acd1fce254fb7e85952f4c09d0915afd33d3886cd914bc7d283c" + )) + .cloned() + .unwrap() + .1, + U256::from(0x500_000) + ); } #[test] fn rpc_eth_author() { - let make_res = |addr| r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{:x}", addr) + r#"","id":1}"#; - let tester = EthTester::default(); + let make_res = |addr| { + r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{:x}", addr) + r#"","id":1}"# + }; + let tester = EthTester::default(); - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", "method": "eth_coinbase", "params": [], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32023,"message":"No accounts were found","data":"\"\""},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32023,"message":"No accounts were found","data":"\"\""},"id":1}"#; - // No accounts - returns an error indicating that no accounts were found - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_string())); + // No accounts - returns an error indicating that no accounts were found + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_string()) + ); - // Account set - return first account - let addr = tester.accounts_provider.new_account(&"123".into()).unwrap(); - assert_eq!(tester.io.handle_request_sync(request), Some(make_res(addr))); + // Account set - return first account + let addr = tester.accounts_provider.new_account(&"123".into()).unwrap(); + assert_eq!(tester.io.handle_request_sync(request), Some(make_res(addr))); - for i in 0..20 { - let addr = tester.accounts_provider.new_account(&format!("{}", i).into()).unwrap(); - tester.miner.set_author(miner::Author::External(addr)); + for i in 0..20 { + let addr = tester + .accounts_provider + .new_account(&format!("{}", i).into()) + .unwrap(); + tester.miner.set_author(miner::Author::External(addr)); - assert_eq!(tester.io.handle_request_sync(request), Some(make_res(addr))); - } + assert_eq!(tester.io.handle_request_sync(request), Some(make_res(addr))); + } } #[test] fn rpc_eth_mining() { - let tester = EthTester::default(); - tester.miner.set_author(miner::Author::External(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap())); - - let request = r#"{"jsonrpc": "2.0", "method": "eth_mining", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":false,"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + let tester = EthTester::default(); + tester.miner.set_author(miner::Author::External( + Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(), + )); + + let request = r#"{"jsonrpc": "2.0", "method": "eth_mining", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":false,"id":1}"#; + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_gas_price() { - let request = r#"{"jsonrpc": "2.0", "method": "eth_gasPrice", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"0x4a817c800","id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "eth_gasPrice", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x4a817c800","id":1}"#; - assert_eq!(EthTester::default().io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + EthTester::default().io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_accounts() { - let tester = EthTester::default(); - let address = tester.accounts_provider.new_account(&"".into()).unwrap(); - tester.accounts_provider.set_address_name(1.into(), "1".into()); - tester.accounts_provider.set_address_name(10.into(), "10".into()); - - // with current policy it should return the account - let request = r#"{"jsonrpc": "2.0", "method": "eth_accounts", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":[""#.to_owned() + &format!("0x{:x}", address) + r#""],"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + let tester = EthTester::default(); + let address = tester.accounts_provider.new_account(&"".into()).unwrap(); + tester + .accounts_provider + .set_address_name(1.into(), "1".into()); + tester + .accounts_provider + .set_address_name(10.into(), "10".into()); + + // with current policy it should return the account + let request = r#"{"jsonrpc": "2.0", "method": "eth_accounts", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":[""#.to_owned() + + &format!("0x{:x}", address) + + r#""],"id":1}"#; + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_block_number() { - let tester = EthTester::default(); - tester.client.add_blocks(10, EachBlockWith::Nothing); + let tester = EthTester::default(); + tester.client.add_blocks(10, EachBlockWith::Nothing); - let request = r#"{"jsonrpc": "2.0", "method": "eth_blockNumber", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"0xa","id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "eth_blockNumber", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0xa","id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_balance() { - let tester = EthTester::default(); - tester.client.set_balance(Address::from(1), U256::from(5)); + let tester = EthTester::default(); + tester.client.set_balance(Address::from(1), U256::from(5)); - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", "method": "eth_getBalance", "params": ["0x0000000000000000000000000000000000000001", "latest"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0x5","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x5","id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_balance_pending() { - let tester = EthTester::default(); - tester.client.set_balance(Address::from(1), U256::from(5)); + let tester = EthTester::default(); + tester.client.set_balance(Address::from(1), U256::from(5)); - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", "method": "eth_getBalance", "params": ["0x0000000000000000000000000000000000000001", "pending"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0x5","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x5","id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_storage_at() { - let tester = EthTester::default(); - tester.client.set_storage(Address::from(1), H256::from(4), H256::from(7)); + let tester = EthTester::default(); + tester + .client + .set_storage(Address::from(1), H256::from(4), H256::from(7)); - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", "method": "eth_getStorageAt", "params": ["0x0000000000000000000000000000000000000001", "0x4", "latest"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000007","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000007","id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_transaction_count() { - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", "method": "eth_getTransactionCount", "params": ["0x0000000000000000000000000000000000000001", "latest"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0x0","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x0","id":1}"#; - assert_eq!(EthTester::default().io.handle_request_sync(request), Some(response.to_owned())); -} - -#[test] -fn rpc_eth_transaction_count_next_nonce() { - let tester = EthTester::new_with_options(EthClientOptions::with(|options| { - options.pending_nonce_from_queue = true; - })); - tester.miner.increment_nonce(&1.into()); - - let request1 = r#"{ - "jsonrpc": "2.0", - "method": "eth_getTransactionCount", - "params": ["0x0000000000000000000000000000000000000001", "pending"], - "id": 1 - }"#; - let response1 = r#"{"jsonrpc":"2.0","result":"0x1","id":1}"#; - assert_eq!(tester.io.handle_request_sync(request1), Some(response1.to_owned())); - - let request2 = r#"{ - "jsonrpc": "2.0", - "method": "eth_getTransactionCount", - "params": ["0x0000000000000000000000000000000000000002", "pending"], - "id": 1 - }"#; - let response2 = r#"{"jsonrpc":"2.0","result":"0x0","id":1}"#; - assert_eq!(tester.io.handle_request_sync(request2), Some(response2.to_owned())); + assert_eq!( + EthTester::default().io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_block_transaction_count_by_hash() { - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", "method": "eth_getBlockTransactionCountByHash", "params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; - assert_eq!(EthTester::default().io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + EthTester::default().io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_transaction_count_by_number() { - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", "method": "eth_getBlockTransactionCountByNumber", "params": ["latest"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0x0","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x0","id":1}"#; - assert_eq!(EthTester::default().io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + EthTester::default().io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_transaction_count_by_number_pending() { - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", "method": "eth_getBlockTransactionCountByNumber", "params": ["pending"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0x0","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x0","id":1}"#; - assert_eq!(EthTester::default().io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + EthTester::default().io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_pending_transaction_by_hash() { - use ethereum_types::H256; - use rlp; - use types::transaction::SignedTransaction; - - let tester = EthTester::default(); - { - let bytes = FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap(); - let tx = rlp::decode(&bytes).expect("decoding failure"); - let tx = SignedTransaction::new(tx).unwrap(); - tester.miner.pending_transactions.lock().insert(H256::zero(), tx); - } - - let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"chainId":null,"condition":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","standardV":"0x0","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"v":"0x1b","value":"0xa"},"id":1}"#; - let request = r#"{ + use ethereum_types::H256; + use rlp; + use types::transaction::SignedTransaction; + + let tester = EthTester::default(); + { + let bytes = FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap(); + let tx = rlp::decode(&bytes).expect("decoding failure"); + let tx = SignedTransaction::new(tx).unwrap(); + tester + .miner + .pending_transactions + .lock() + .insert(H256::zero(), tx); + } + + let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"chainId":null,"condition":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","standardV":"0x0","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"v":"0x1b","value":"0xa"},"id":1}"#; + let request = r#"{ "jsonrpc": "2.0", "method": "eth_getTransactionByHash", "params": ["0x0000000000000000000000000000000000000000000000000000000000000000"], "id": 1 }"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_uncle_count_by_block_hash() { - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", "method": "eth_getUncleCountByBlockHash", "params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; - assert_eq!(EthTester::default().io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + EthTester::default().io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_uncle_count_by_block_number() { - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", "method": "eth_getUncleCountByBlockNumber", "params": ["latest"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0x0","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x0","id":1}"#; - assert_eq!(EthTester::default().io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + EthTester::default().io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_code() { - let tester = EthTester::default(); - tester.client.set_code(Address::from(1), vec![0xff, 0x21]); + let tester = EthTester::default(); + tester.client.set_code(Address::from(1), vec![0xff, 0x21]); - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", "method": "eth_getCode", "params": ["0x0000000000000000000000000000000000000001", "latest"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0xff21","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0xff21","id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_call_latest() { - let tester = EthTester::default(); - tester.client.set_execution_result(Ok(Executed { - exception: None, - gas: U256::zero(), - gas_used: U256::from(0xff30), - refunded: U256::from(0x5), - cumulative_gas_used: U256::zero(), - logs: vec![], - contracts_created: vec![], - output: vec![0x12, 0x34, 0xff], - trace: vec![], - vm_trace: None, - state_diff: None, - })); - - let request = r#"{ + let tester = EthTester::default(); + tester.client.set_execution_result(Ok(Executed { + exception: None, + gas: U256::zero(), + gas_used: U256::from(0xff30), + refunded: U256::from(0x5), + cumulative_gas_used: U256::zero(), + logs: vec![], + contracts_created: vec![], + output: vec![0x12, 0x34, 0xff], + trace: vec![], + vm_trace: None, + state_diff: None, + })); + + let request = r#"{ "jsonrpc": "2.0", "method": "eth_call", "params": [{ @@ -648,29 +804,72 @@ fn rpc_eth_call_latest() { "latest"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0x1234ff","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x1234ff","id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); +} + +#[test] +fn rpc_eth_call_pending() { + let tester = EthTester::default(); + tester.client.set_execution_result(Ok(Executed { + exception: None, + gas: U256::zero(), + gas_used: U256::from(0xff30), + refunded: U256::from(0x5), + cumulative_gas_used: U256::zero(), + logs: vec![], + contracts_created: vec![], + output: vec![0x12, 0x34, 0xff], + trace: vec![], + vm_trace: None, + state_diff: None, + })); + + let request = r#"{ + "jsonrpc": "2.0", + "method": "eth_call", + "params": [{ + "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "value": "0x9184e72a", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + }, + "pending"], + "id": 1 + }"#; + // Falls back to "Latest" and gives the same result. + let response = r#"{"jsonrpc":"2.0","result":"0x1234ff","id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_call() { - let tester = EthTester::default(); - tester.client.set_execution_result(Ok(Executed { - exception: None, - gas: U256::zero(), - gas_used: U256::from(0xff30), - refunded: U256::from(0x5), - cumulative_gas_used: U256::zero(), - logs: vec![], - contracts_created: vec![], - output: vec![0x12, 0x34, 0xff], - trace: vec![], - vm_trace: None, - state_diff: None, - })); - - let request = r#"{ + let tester = EthTester::default(); + tester.client.set_execution_result(Ok(Executed { + exception: None, + gas: U256::zero(), + gas_used: U256::from(0xff30), + refunded: U256::from(0x5), + cumulative_gas_used: U256::zero(), + logs: vec![], + contracts_created: vec![], + output: vec![0x12, 0x34, 0xff], + trace: vec![], + vm_trace: None, + state_diff: None, + })); + + let request = r#"{ "jsonrpc": "2.0", "method": "eth_call", "params": [{ @@ -684,29 +883,32 @@ fn rpc_eth_call() { "0x0"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0x1234ff","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x1234ff","id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_call_default_block() { - let tester = EthTester::default(); - tester.client.set_execution_result(Ok(Executed { - exception: None, - gas: U256::zero(), - gas_used: U256::from(0xff30), - refunded: U256::from(0x5), - cumulative_gas_used: U256::zero(), - logs: vec![], - contracts_created: vec![], - output: vec![0x12, 0x34, 0xff], - trace: vec![], - vm_trace: None, - state_diff: None, - })); - - let request = r#"{ + let tester = EthTester::default(); + tester.client.set_execution_result(Ok(Executed { + exception: None, + gas: U256::zero(), + gas_used: U256::from(0xff30), + refunded: U256::from(0x5), + cumulative_gas_used: U256::zero(), + logs: vec![], + contracts_created: vec![], + output: vec![0x12, 0x34, 0xff], + trace: vec![], + vm_trace: None, + state_diff: None, + })); + + let request = r#"{ "jsonrpc": "2.0", "method": "eth_call", "params": [{ @@ -719,29 +921,32 @@ fn rpc_eth_call_default_block() { }], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0x1234ff","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x1234ff","id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_estimate_gas() { - let tester = EthTester::default(); - tester.client.set_execution_result(Ok(Executed { - exception: None, - gas: U256::zero(), - gas_used: U256::from(0xff30), - refunded: U256::from(0x5), - cumulative_gas_used: U256::zero(), - logs: vec![], - contracts_created: vec![], - output: vec![0x12, 0x34, 0xff], - trace: vec![], - vm_trace: None, - state_diff: None, - })); - - let request = r#"{ + let tester = EthTester::default(); + tester.client.set_execution_result(Ok(Executed { + exception: None, + gas: U256::zero(), + gas_used: U256::from(0xff30), + refunded: U256::from(0x5), + cumulative_gas_used: U256::zero(), + logs: vec![], + contracts_created: vec![], + output: vec![0x12, 0x34, 0xff], + trace: vec![], + vm_trace: None, + state_diff: None, + })); + + let request = r#"{ "jsonrpc": "2.0", "method": "eth_estimateGas", "params": [{ @@ -755,29 +960,72 @@ fn rpc_eth_estimate_gas() { "latest"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0x5208","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x5208","id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); +} + +#[test] +fn rpc_eth_estimate_gas_pending() { + let tester = EthTester::default(); + tester.client.set_execution_result(Ok(Executed { + exception: None, + gas: U256::zero(), + gas_used: U256::from(0xff30), + refunded: U256::from(0x5), + cumulative_gas_used: U256::zero(), + logs: vec![], + contracts_created: vec![], + output: vec![0x12, 0x34, 0xff], + trace: vec![], + vm_trace: None, + state_diff: None, + })); + + let request = r#"{ + "jsonrpc": "2.0", + "method": "eth_estimateGas", + "params": [{ + "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "value": "0x9184e72a", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + }, + "pending"], + "id": 1 + }"#; + // Falls back to "Latest" so the result is the same + let response = r#"{"jsonrpc":"2.0","result":"0x5208","id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_estimate_gas_default_block() { - let tester = EthTester::default(); - tester.client.set_execution_result(Ok(Executed { - exception: None, - gas: U256::zero(), - gas_used: U256::from(0xff30), - refunded: U256::from(0x5), - cumulative_gas_used: U256::zero(), - logs: vec![], - contracts_created: vec![], - output: vec![0x12, 0x34, 0xff], - trace: vec![], - vm_trace: None, - state_diff: None, - })); - - let request = r#"{ + let tester = EthTester::default(); + tester.client.set_execution_result(Ok(Executed { + exception: None, + gas: U256::zero(), + gas_used: U256::from(0xff30), + refunded: U256::from(0x5), + cumulative_gas_used: U256::zero(), + logs: vec![], + contracts_created: vec![], + output: vec![0x12, 0x34, 0xff], + trace: vec![], + vm_trace: None, + state_diff: None, + })); + + let request = r#"{ "jsonrpc": "2.0", "method": "eth_estimateGas", "params": [{ @@ -790,16 +1038,19 @@ fn rpc_eth_estimate_gas_default_block() { }], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0x5208","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x5208","id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_send_raw_transaction_error() { - let tester = EthTester::default(); + let tester = EthTester::default(); - let req = r#"{ + let req = r#"{ "jsonrpc": "2.0", "method": "eth_sendRawTransaction", "params": [ @@ -807,104 +1058,174 @@ fn rpc_eth_send_raw_transaction_error() { ], "id": 1 }"#; - let res = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid RLP.","data":"RlpExpectedToBeList"},"id":1}"#.into(); + let res = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid RLP.","data":"RlpExpectedToBeList"},"id":1}"#.into(); - assert_eq!(tester.io.handle_request_sync(&req), Some(res)); + assert_eq!(tester.io.handle_request_sync(&req), Some(res)); } #[test] fn rpc_eth_send_raw_transaction() { - let tester = EthTester::default(); - let address = tester.accounts_provider.new_account(&"abcd".into()).unwrap(); - tester.accounts_provider.unlock_account_permanently(address, "abcd".into()).unwrap(); - - let t = Transaction { - nonce: U256::zero(), - gas_price: U256::from(0x9184e72a000u64), - gas: U256::from(0x76c0), - action: Action::Call(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), - value: U256::from(0x9184e72au64), - data: vec![] - }; - let signature = tester.accounts_provider.sign(address, None, t.hash(None)).unwrap(); - let t = t.with_signature(signature, None); - - let rlp = rlp::encode(&t).to_hex(); - - let req = r#"{ + let tester = EthTester::default(); + let address = tester + .accounts_provider + .new_account(&"abcd".into()) + .unwrap(); + tester + .accounts_provider + .unlock_account_permanently(address, "abcd".into()) + .unwrap(); + + let t = Transaction { + nonce: U256::zero(), + gas_price: U256::from(0x9184e72a000u64), + gas: U256::from(0x76c0), + action: Action::Call( + Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(), + ), + value: U256::from(0x9184e72au64), + data: vec![], + }; + let signature = tester + .accounts_provider + .sign(address, None, t.hash(None)) + .unwrap(); + let t = t.with_signature(signature, None); + + let rlp = rlp::encode(&t).to_hex(); + + let req = r#"{ "jsonrpc": "2.0", "method": "eth_sendRawTransaction", "params": [ - "0x"#.to_owned() + &rlp + r#"" + "0x"# + .to_owned() + + &rlp + + r#"" ], "id": 1 }"#; - let res = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{:x}", t.hash()) + r#"","id":1}"#; + let res = + r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{:x}", t.hash()) + r#"","id":1}"#; - assert_eq!(tester.io.handle_request_sync(&req), Some(res)); + assert_eq!(tester.io.handle_request_sync(&req), Some(res)); } #[test] fn rpc_eth_transaction_receipt() { - let receipt = LocalizedReceipt { - from: H160::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap(), - to: Some(H160::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), - transaction_hash: H256::zero(), - transaction_index: 0, - block_hash: H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap(), - block_number: 0x4510c, - cumulative_gas_used: U256::from(0x20), - gas_used: U256::from(0x10), - contract_address: None, - logs: vec![LocalizedLogEntry { - entry: LogEntry { - address: Address::from_str("33990122638b9132ca29c723bdf037f1a891a70c").unwrap(), - topics: vec![ - H256::from_str("a6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc").unwrap(), - H256::from_str("4861736852656700000000000000000000000000000000000000000000000000").unwrap() - ], - data: vec![], - }, - block_hash: H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap(), - block_number: 0x4510c, - transaction_hash: H256::new(), - transaction_index: 0, - transaction_log_index: 0, - log_index: 1, - }], - log_bloom: 0.into(), - outcome: TransactionOutcome::StateRoot(0.into()), - }; - - let hash = H256::from_str("b903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238").unwrap(); - let tester = EthTester::default(); - tester.client.set_transaction_receipt(TransactionId::Hash(hash), receipt); - - let request = r#"{ + let receipt = LocalizedReceipt { + from: H160::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap(), + to: Some(H160::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), + transaction_hash: H256::zero(), + transaction_index: 0, + block_hash: H256::from_str( + "ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5", + ) + .unwrap(), + block_number: 0x4510c, + cumulative_gas_used: U256::from(0x20), + gas_used: U256::from(0x10), + contract_address: None, + logs: vec![LocalizedLogEntry { + entry: LogEntry { + address: Address::from_str("33990122638b9132ca29c723bdf037f1a891a70c").unwrap(), + topics: vec![ + H256::from_str( + "a6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc", + ) + .unwrap(), + H256::from_str( + "4861736852656700000000000000000000000000000000000000000000000000", + ) + .unwrap(), + ], + data: vec![], + }, + block_hash: H256::from_str( + "ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5", + ) + .unwrap(), + block_number: 0x4510c, + transaction_hash: H256::new(), + transaction_index: 0, + transaction_log_index: 0, + log_index: 1, + }], + log_bloom: 0.into(), + outcome: TransactionOutcome::StateRoot(0.into()), + }; + + let hash = + H256::from_str("b903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238").unwrap(); + let tester = EthTester::default(); + tester + .client + .set_transaction_receipt(TransactionId::Hash(hash), receipt); + + let request = r#"{ "jsonrpc": "2.0", "method": "eth_getTransactionReceipt", "params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":{"blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","contractAddress":null,"cumulativeGasUsed":"0x20","from":"0xb60e8dd61c5d32be8058bb8eb970870f07233155","gasUsed":"0x10","logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","data":"0x","logIndex":"0x1","removed":false,"topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"}],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","root":"0x0000000000000000000000000000000000000000000000000000000000000000","status":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0"},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","contractAddress":null,"cumulativeGasUsed":"0x20","from":"0xb60e8dd61c5d32be8058bb8eb970870f07233155","gasUsed":"0x10","logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","data":"0x","logIndex":"0x1","removed":false,"topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"}],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","root":"0x0000000000000000000000000000000000000000000000000000000000000000","to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0"},"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_eth_transaction_receipt_null() { - let tester = EthTester::default(); + let tester = EthTester::default(); - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", "method": "eth_getTransactionReceipt", "params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); +} + +#[test] +fn rpc_eth_pending_receipt() { + let pending = RichReceipt { + from: H160::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap(), + to: Some(H160::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), + transaction_hash: H256::from_str( + "b903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238", + ) + .unwrap(), + transaction_index: 0, + cumulative_gas_used: U256::from(0x20), + gas_used: U256::from(0x10), + contract_address: None, + logs: Vec::new(), + log_bloom: Bloom::zero(), + outcome: TransactionOutcome::Unknown, + }; + let tester = EthTester::default(); + + tester.miner.pending_receipts.lock().push(pending); + + let request = r#"{ + "jsonrpc": "2.0", + "method": "eth_getTransactionReceipt", + "params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"], + "id": 1 + }"#; + let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"contractAddress":null,"cumulativeGasUsed":"0x20","from":"0xb60e8dd61c5d32be8058bb8eb970870f07233155","gasUsed":"0x10","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionHash":"0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238","transactionIndex":"0x0"},"id":1}"#; + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } // These tests are incorrect: their output is undefined as long as eth_getCompilers is []. @@ -914,104 +1235,134 @@ fn rpc_eth_transaction_receipt_null() { #[ignore] #[test] fn rpc_eth_compilers() { - let request = r#"{"jsonrpc": "2.0", "method": "eth_getCompilers", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32070,"message":"Method deprecated","data":"Compilation functionality is deprecated."},"id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "eth_getCompilers", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32070,"message":"Method deprecated","data":"Compilation functionality is deprecated."},"id":1}"#; - assert_eq!(EthTester::default().io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + EthTester::default().io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[ignore] #[test] fn rpc_eth_compile_lll() { - let request = r#"{"jsonrpc": "2.0", "method": "eth_compileLLL", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32070,"message":"Method deprecated","data":"Compilation of LLL via RPC is deprecated"},"id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "eth_compileLLL", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32070,"message":"Method deprecated","data":"Compilation of LLL via RPC is deprecated"},"id":1}"#; - assert_eq!(EthTester::default().io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + EthTester::default().io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[ignore] #[test] fn rpc_eth_compile_solidity() { - let request = r#"{"jsonrpc": "2.0", "method": "eth_compileSolidity", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32070,"message":"Method deprecated","data":"Compilation of Solidity via RPC is deprecated"},"id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "eth_compileSolidity", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32070,"message":"Method deprecated","data":"Compilation of Solidity via RPC is deprecated"},"id":1}"#; - assert_eq!(EthTester::default().io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + EthTester::default().io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[ignore] #[test] fn rpc_eth_compile_serpent() { - let request = r#"{"jsonrpc": "2.0", "method": "eth_compileSerpent", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32070,"message":"Method deprecated","data":"Compilation of Serpent via RPC is deprecated"},"id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "eth_compileSerpent", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32070,"message":"Method deprecated","data":"Compilation of Serpent via RPC is deprecated"},"id":1}"#; - assert_eq!(EthTester::default().io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + EthTester::default().io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_get_work_returns_no_work_if_cant_mine() { - let eth_tester = EthTester::default(); - eth_tester.client.set_queue_size(10); + let eth_tester = EthTester::default(); + eth_tester.client.set_queue_size(10); - let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32001,"message":"Still syncing."},"id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32001,"message":"Still syncing."},"id":1}"#; - assert_eq!(eth_tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + eth_tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_get_work_returns_correct_work_package() { - let eth_tester = EthTester::default(); - eth_tester.miner.set_author(miner::Author::External(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap())); - - let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":["0x76c7bd86693aee93d1a80a408a09a0585b1a1292afcb56192f171d925ea18e2d","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000800000000000000000000000000000000000000000000000000000000000","0x1"],"id":1}"#; - - assert_eq!(eth_tester.io.handle_request_sync(request), Some(response.to_owned())); -} - -#[test] -fn rpc_get_work_should_not_return_block_number() { - let eth_tester = EthTester::new_with_options(EthClientOptions::with(|options| { - options.send_block_number_in_get_work = false; - })); - eth_tester.miner.set_author(miner::Author::External(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap())); - - let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":["0x76c7bd86693aee93d1a80a408a09a0585b1a1292afcb56192f171d925ea18e2d","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000800000000000000000000000000000000000000000000000000000000000"],"id":1}"#; - - assert_eq!(eth_tester.io.handle_request_sync(request), Some(response.to_owned())); + let eth_tester = EthTester::default(); + eth_tester.miner.set_author(miner::Author::External( + Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(), + )); + + let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":["0x76c7bd86693aee93d1a80a408a09a0585b1a1292afcb56192f171d925ea18e2d","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000800000000000000000000000000000000000000000000000000000000000","0x1"],"id":1}"#; + + assert_eq!( + eth_tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_get_work_should_timeout() { - let eth_tester = EthTester::default(); - eth_tester.miner.set_author(miner::Author::External(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap())); - let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() - 1000; // Set latest block to 1000 seconds ago - eth_tester.client.set_latest_block_timestamp(timestamp); - let hash = eth_tester.miner.work_package(&*eth_tester.client).unwrap().0; - - // Request without providing timeout. This should work since we're disabling timeout. - let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [], "id": 1}"#; - let work_response = format!( - r#"{{"jsonrpc":"2.0","result":["0x{:x}","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000800000000000000000000000000000000000000000000000000000000000","0x1"],"id":1}}"#, - hash, - ); - assert_eq!(eth_tester.io.handle_request_sync(request), Some(work_response.to_owned())); - - // Request with timeout of 0 seconds. This should work since we're disabling timeout. - let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [0], "id": 1}"#; - let work_response = format!( - r#"{{"jsonrpc":"2.0","result":["0x{:x}","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000800000000000000000000000000000000000000000000000000000000000","0x1"],"id":1}}"#, - hash, - ); - assert_eq!(eth_tester.io.handle_request_sync(request), Some(work_response.to_owned())); - - // Request with timeout of 10K seconds. This should work. - let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [10000], "id": 1}"#; - assert_eq!(eth_tester.io.handle_request_sync(request), Some(work_response.to_owned())); - - // Request with timeout of 10 seconds. This should fail. - let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [10], "id": 1}"#; - let err_response = r#"{"jsonrpc":"2.0","error":{"code":-32003,"message":"Work has not changed."},"id":1}"#; - assert_eq!(eth_tester.io.handle_request_sync(request), Some(err_response.to_owned())); + let eth_tester = EthTester::default(); + eth_tester.miner.set_author(miner::Author::External( + Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(), + )); + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() + - 1000; // Set latest block to 1000 seconds ago + eth_tester.client.set_latest_block_timestamp(timestamp); + let hash = eth_tester + .miner + .work_package(&*eth_tester.client) + .unwrap() + .0; + + // Request without providing timeout. This should work since we're disabling timeout. + let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [], "id": 1}"#; + let work_response = format!( + r#"{{"jsonrpc":"2.0","result":["0x{:x}","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000800000000000000000000000000000000000000000000000000000000000","0x1"],"id":1}}"#, + hash, + ); + assert_eq!( + eth_tester.io.handle_request_sync(request), + Some(work_response.to_owned()) + ); + + // Request with timeout of 0 seconds. This should work since we're disabling timeout. + let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [0], "id": 1}"#; + let work_response = format!( + r#"{{"jsonrpc":"2.0","result":["0x{:x}","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000800000000000000000000000000000000000000000000000000000000000","0x1"],"id":1}}"#, + hash, + ); + assert_eq!( + eth_tester.io.handle_request_sync(request), + Some(work_response.to_owned()) + ); + + // Request with timeout of 10K seconds. This should work. + let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [10000], "id": 1}"#; + assert_eq!( + eth_tester.io.handle_request_sync(request), + Some(work_response.to_owned()) + ); + + // Request with timeout of 10 seconds. This should fail. + let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [10], "id": 1}"#; + let err_response = + r#"{"jsonrpc":"2.0","error":{"code":-32003,"message":"Work has not changed."},"id":1}"#; + assert_eq!( + eth_tester.io.handle_request_sync(request), + Some(err_response.to_owned()) + ); } diff --git a/rpc/src/v1/tests/mocked/eth_pubsub.rs b/rpc/src/v1/tests/mocked/eth_pubsub.rs index 6fd7394a4b2..0e5c6bbba68 100644 --- a/rpc/src/v1/tests/mocked/eth_pubsub.rs +++ b/rpc/src/v1/tests/mocked/eth_pubsub.rs @@ -1,222 +1,288 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use std::sync::Arc; -use jsonrpc_core::MetaIoHandler; -use jsonrpc_core::futures::{self, Stream, Future}; +use jsonrpc_core::{ + futures::{self, Future, Stream}, + MetaIoHandler, +}; use jsonrpc_pubsub::Session; use std::time::Duration; use v1::{EthPubSub, EthPubSubClient, Metadata}; -use ethcore::client::{TestBlockChainClient, EachBlockWith, ChainNotify, NewBlocks, ChainRoute, ChainRouteType}; +use ethcore::client::{ + ChainNotify, ChainRoute, ChainRouteType, EachBlockWith, NewBlocks, TestBlockChainClient, +}; use parity_runtime::Runtime; const DURATION_ZERO: Duration = Duration::from_millis(0); #[test] fn should_subscribe_to_new_heads() { - // given - let el = Runtime::with_thread_count(1); - let mut client = TestBlockChainClient::new(); - // Insert some blocks - client.add_blocks(3, EachBlockWith::Nothing); - let h3 = client.block_hash_delta_minus(1); - let h2 = client.block_hash_delta_minus(2); - let h1 = client.block_hash_delta_minus(3); - - let pubsub = EthPubSubClient::new_test(Arc::new(client), el.executor()); - let handler = pubsub.handler().upgrade().unwrap(); - let pubsub = pubsub.to_delegate(); - - let mut io = MetaIoHandler::default(); - io.extend_with(pubsub); - - let mut metadata = Metadata::default(); - let (sender, receiver) = futures::sync::mpsc::channel(8); - metadata.session = Some(Arc::new(Session::new(sender))); - - // Subscribe - let request = r#"{"jsonrpc": "2.0", "method": "eth_subscribe", "params": ["newHeads"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"0x416d77337e24399d","id":1}"#; - assert_eq!(io.handle_request_sync(request, metadata.clone()), Some(response.to_owned())); - - // Check notifications - handler.new_blocks(NewBlocks::new(vec![], vec![], ChainRoute::new(vec![(h1, ChainRouteType::Enacted)]), vec![], vec![], DURATION_ZERO, true)); - let (res, receiver) = receiver.into_future().wait().unwrap(); - let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x","gasLimit":"0xf4240","gasUsed":"0x0","hash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","number":"0x1","parentHash":"0x0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":[],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x1c9","stateRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","timestamp":"0x0","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"},"subscription":"0x416d77337e24399d"}}"#; - assert_eq!(res, Some(response.into())); - - // Notify about two blocks - handler.new_blocks(NewBlocks::new(vec![], vec![], ChainRoute::new(vec![(h2, ChainRouteType::Enacted), (h3, ChainRouteType::Enacted)]), vec![], vec![], DURATION_ZERO, true)); - - // Receive both - let (res, receiver) = receiver.into_future().wait().unwrap(); - let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x2","extraData":"0x","gasLimit":"0xf4240","gasUsed":"0x0","hash":"0x44e5ecf454ea99af9d8a8f2ca0daba96964c90de05db7a78f59b84ae9e749706","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","number":"0x2","parentHash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":[],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x1c9","stateRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","timestamp":"0x0","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"},"subscription":"0x416d77337e24399d"}}"#; - assert_eq!(res, Some(response.into())); - let (res, receiver) = receiver.into_future().wait().unwrap(); - let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x3","extraData":"0x","gasLimit":"0xf4240","gasUsed":"0x0","hash":"0xdf04a98bb0c6fa8441bd429822f65a46d0cb553f6bcef602b973e65c81497f8e","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","number":"0x3","parentHash":"0x44e5ecf454ea99af9d8a8f2ca0daba96964c90de05db7a78f59b84ae9e749706","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":[],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x1c9","stateRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","timestamp":"0x0","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"},"subscription":"0x416d77337e24399d"}}"#; - assert_eq!(res, Some(response.into())); - - // And unsubscribe - let request = r#"{"jsonrpc": "2.0", "method": "eth_unsubscribe", "params": ["0x416d77337e24399d"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - assert_eq!(io.handle_request_sync(request, metadata), Some(response.to_owned())); - - let (res, _receiver) = receiver.into_future().wait().unwrap(); - assert_eq!(res, None); + // given + let el = Runtime::with_thread_count(1); + let mut client = TestBlockChainClient::new(); + // Insert some blocks + client.add_blocks(3, EachBlockWith::Nothing); + let h3 = client.block_hash_delta_minus(1); + let h2 = client.block_hash_delta_minus(2); + let h1 = client.block_hash_delta_minus(3); + + let pubsub = EthPubSubClient::new_test(Arc::new(client), el.executor()); + let handler = pubsub.handler().upgrade().unwrap(); + let pubsub = pubsub.to_delegate(); + + let mut io = MetaIoHandler::default(); + io.extend_with(pubsub); + + let mut metadata = Metadata::default(); + let (sender, receiver) = futures::sync::mpsc::channel(8); + metadata.session = Some(Arc::new(Session::new(sender))); + + // Subscribe + let request = + r#"{"jsonrpc": "2.0", "method": "eth_subscribe", "params": ["newHeads"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x416d77337e24399d","id":1}"#; + assert_eq!( + io.handle_request_sync(request, metadata.clone()), + Some(response.to_owned()) + ); + + // Check notifications + handler.new_blocks(NewBlocks::new( + vec![], + vec![], + ChainRoute::new(vec![(h1, ChainRouteType::Enacted)]), + vec![], + vec![], + DURATION_ZERO, + true, + )); + let (res, receiver) = receiver.into_future().wait().unwrap(); + let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x","gasLimit":"0xf4240","gasUsed":"0x0","hash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","number":"0x1","parentHash":"0x0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":[],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x1c9","stateRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","timestamp":"0x0","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"},"subscription":"0x416d77337e24399d"}}"#; + assert_eq!(res, Some(response.into())); + + // Notify about two blocks + handler.new_blocks(NewBlocks::new( + vec![], + vec![], + ChainRoute::new(vec![ + (h2, ChainRouteType::Enacted), + (h3, ChainRouteType::Enacted), + ]), + vec![], + vec![], + DURATION_ZERO, + true, + )); + + // Receive both + let (res, receiver) = receiver.into_future().wait().unwrap(); + let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x2","extraData":"0x","gasLimit":"0xf4240","gasUsed":"0x0","hash":"0x44e5ecf454ea99af9d8a8f2ca0daba96964c90de05db7a78f59b84ae9e749706","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","number":"0x2","parentHash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":[],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x1c9","stateRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","timestamp":"0x0","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"},"subscription":"0x416d77337e24399d"}}"#; + assert_eq!(res, Some(response.into())); + let (res, receiver) = receiver.into_future().wait().unwrap(); + let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x3","extraData":"0x","gasLimit":"0xf4240","gasUsed":"0x0","hash":"0xdf04a98bb0c6fa8441bd429822f65a46d0cb553f6bcef602b973e65c81497f8e","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","number":"0x3","parentHash":"0x44e5ecf454ea99af9d8a8f2ca0daba96964c90de05db7a78f59b84ae9e749706","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":[],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x1c9","stateRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","timestamp":"0x0","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"},"subscription":"0x416d77337e24399d"}}"#; + assert_eq!(res, Some(response.into())); + + // And unsubscribe + let request = r#"{"jsonrpc": "2.0", "method": "eth_unsubscribe", "params": ["0x416d77337e24399d"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + assert_eq!( + io.handle_request_sync(request, metadata), + Some(response.to_owned()) + ); + + let (res, _receiver) = receiver.into_future().wait().unwrap(); + assert_eq!(res, None); } #[test] fn should_subscribe_to_logs() { - use ethcore::client::BlockInfo; - use types::log_entry::{LocalizedLogEntry, LogEntry}; - use types::ids::BlockId; - - // given - let el = Runtime::with_thread_count(1); - let mut client = TestBlockChainClient::new(); - // Insert some blocks - client.add_blocks(1, EachBlockWith::Transaction); - let h1 = client.block_hash_delta_minus(1); - let block = client.block(BlockId::Hash(h1)).unwrap(); - let tx_hash = block.transactions()[0].hash(); - client.set_logs(vec![ - LocalizedLogEntry { - entry: LogEntry { - address: 5.into(), - topics: vec![1.into(), 2.into(), 0.into(), 0.into()], - data: vec![], - }, - block_hash: h1, - block_number: block.header().number(), - transaction_hash: tx_hash, - transaction_index: 0, - log_index: 0, - transaction_log_index: 0, - } - ]); - - let pubsub = EthPubSubClient::new_test(Arc::new(client), el.executor()); - let handler = pubsub.handler().upgrade().unwrap(); - let pubsub = pubsub.to_delegate(); - - let mut io = MetaIoHandler::default(); - io.extend_with(pubsub); - - let mut metadata = Metadata::default(); - let (sender, receiver) = futures::sync::mpsc::channel(8); - metadata.session = Some(Arc::new(Session::new(sender))); - - // Subscribe - let request = r#"{"jsonrpc": "2.0", "method": "eth_subscribe", "params": ["logs", {}], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"0x416d77337e24399d","id":1}"#; - assert_eq!(io.handle_request_sync(request, metadata.clone()), Some(response.to_owned())); - - // Check notifications (enacted) - handler.new_blocks(NewBlocks::new(vec![], vec![], ChainRoute::new(vec![(h1, ChainRouteType::Enacted)]), vec![], vec![], DURATION_ZERO, false)); - let (res, receiver) = receiver.into_future().wait().unwrap(); - let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"address":"0x0000000000000000000000000000000000000005","blockHash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","blockNumber":"0x1","data":"0x","logIndex":"0x0","removed":false,"topics":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000"],"transactionHash":""#.to_owned() + use ethcore::client::BlockInfo; + use types::{ + ids::BlockId, + log_entry::{LocalizedLogEntry, LogEntry}, + }; + + // given + let el = Runtime::with_thread_count(1); + let mut client = TestBlockChainClient::new(); + // Insert some blocks + client.add_blocks(1, EachBlockWith::Transaction); + let h1 = client.block_hash_delta_minus(1); + let block = client.block(BlockId::Hash(h1)).unwrap(); + let tx_hash = block.transactions()[0].hash(); + client.set_logs(vec![LocalizedLogEntry { + entry: LogEntry { + address: 5.into(), + topics: vec![1.into(), 2.into(), 0.into(), 0.into()], + data: vec![], + }, + block_hash: h1, + block_number: block.header().number(), + transaction_hash: tx_hash, + transaction_index: 0, + log_index: 0, + transaction_log_index: 0, + }]); + + let pubsub = EthPubSubClient::new_test(Arc::new(client), el.executor()); + let handler = pubsub.handler().upgrade().unwrap(); + let pubsub = pubsub.to_delegate(); + + let mut io = MetaIoHandler::default(); + io.extend_with(pubsub); + + let mut metadata = Metadata::default(); + let (sender, receiver) = futures::sync::mpsc::channel(8); + metadata.session = Some(Arc::new(Session::new(sender))); + + // Subscribe + let request = + r#"{"jsonrpc": "2.0", "method": "eth_subscribe", "params": ["logs", {}], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x416d77337e24399d","id":1}"#; + assert_eq!( + io.handle_request_sync(request, metadata.clone()), + Some(response.to_owned()) + ); + + // Check notifications (enacted) + handler.new_blocks(NewBlocks::new( + vec![], + vec![], + ChainRoute::new(vec![(h1, ChainRouteType::Enacted)]), + vec![], + vec![], + DURATION_ZERO, + false, + )); + let (res, receiver) = receiver.into_future().wait().unwrap(); + let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"address":"0x0000000000000000000000000000000000000005","blockHash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","blockNumber":"0x1","data":"0x","logIndex":"0x0","removed":false,"topics":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000"],"transactionHash":""#.to_owned() + &format!("0x{:x}", tx_hash) + r#"","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"},"subscription":"0x416d77337e24399d"}}"#; - assert_eq!(res, Some(response.into())); - - // Check notifications (retracted) - handler.new_blocks(NewBlocks::new(vec![], vec![], ChainRoute::new(vec![(h1, ChainRouteType::Retracted)]), vec![], vec![], DURATION_ZERO, false)); - let (res, receiver) = receiver.into_future().wait().unwrap(); - let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"address":"0x0000000000000000000000000000000000000005","blockHash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","blockNumber":"0x1","data":"0x","logIndex":"0x0","removed":true,"topics":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000"],"transactionHash":""#.to_owned() + assert_eq!(res, Some(response.into())); + + // Check notifications (retracted) + handler.new_blocks(NewBlocks::new( + vec![], + vec![], + ChainRoute::new(vec![(h1, ChainRouteType::Retracted)]), + vec![], + vec![], + DURATION_ZERO, + false, + )); + let (res, receiver) = receiver.into_future().wait().unwrap(); + let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"address":"0x0000000000000000000000000000000000000005","blockHash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","blockNumber":"0x1","data":"0x","logIndex":"0x0","removed":true,"topics":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000"],"transactionHash":""#.to_owned() + &format!("0x{:x}", tx_hash) + r#"","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"removed"},"subscription":"0x416d77337e24399d"}}"#; - assert_eq!(res, Some(response.into())); - - // And unsubscribe - let request = r#"{"jsonrpc": "2.0", "method": "eth_unsubscribe", "params": ["0x416d77337e24399d"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - assert_eq!(io.handle_request_sync(request, metadata), Some(response.to_owned())); - - let (res, _receiver) = receiver.into_future().wait().unwrap(); - assert_eq!(res, None); + assert_eq!(res, Some(response.into())); + + // And unsubscribe + let request = r#"{"jsonrpc": "2.0", "method": "eth_unsubscribe", "params": ["0x416d77337e24399d"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + assert_eq!( + io.handle_request_sync(request, metadata), + Some(response.to_owned()) + ); + + let (res, _receiver) = receiver.into_future().wait().unwrap(); + assert_eq!(res, None); } #[test] fn should_subscribe_to_pending_transactions() { - // given - let el = Runtime::with_thread_count(1); - let client = TestBlockChainClient::new(); - - let pubsub = EthPubSubClient::new_test(Arc::new(client), el.executor()); - let handler = pubsub.handler().upgrade().unwrap(); - let pubsub = pubsub.to_delegate(); - - let mut io = MetaIoHandler::default(); - io.extend_with(pubsub); - - let mut metadata = Metadata::default(); - let (sender, receiver) = futures::sync::mpsc::channel(8); - metadata.session = Some(Arc::new(Session::new(sender))); - - // Fail if params are provided - let request = r#"{"jsonrpc": "2.0", "method": "eth_subscribe", "params": ["newPendingTransactions", {}], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Couldn't parse parameters: newPendingTransactions","data":"\"Expected no parameters.\""},"id":1}"#; - assert_eq!(io.handle_request_sync(request, metadata.clone()), Some(response.to_owned())); - - // Subscribe - let request = r#"{"jsonrpc": "2.0", "method": "eth_subscribe", "params": ["newPendingTransactions"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"0x416d77337e24399d","id":1}"#; - assert_eq!(io.handle_request_sync(request, metadata.clone()), Some(response.to_owned())); - - // Send new transactions - handler.notify_new_transactions(&[5.into(), 7.into()]); - - let (res, receiver) = receiver.into_future().wait().unwrap(); - let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":"0x0000000000000000000000000000000000000000000000000000000000000005","subscription":"0x416d77337e24399d"}}"#; - assert_eq!(res, Some(response.into())); - - let (res, receiver) = receiver.into_future().wait().unwrap(); - let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":"0x0000000000000000000000000000000000000000000000000000000000000007","subscription":"0x416d77337e24399d"}}"#; - assert_eq!(res, Some(response.into())); - - // And unsubscribe - let request = r#"{"jsonrpc": "2.0", "method": "eth_unsubscribe", "params": ["0x416d77337e24399d"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - assert_eq!(io.handle_request_sync(request, metadata), Some(response.to_owned())); - - let (res, _receiver) = receiver.into_future().wait().unwrap(); - assert_eq!(res, None); + // given + let el = Runtime::with_thread_count(1); + let client = TestBlockChainClient::new(); + + let pubsub = EthPubSubClient::new_test(Arc::new(client), el.executor()); + let handler = pubsub.handler().upgrade().unwrap(); + let pubsub = pubsub.to_delegate(); + + let mut io = MetaIoHandler::default(); + io.extend_with(pubsub); + + let mut metadata = Metadata::default(); + let (sender, receiver) = futures::sync::mpsc::channel(8); + metadata.session = Some(Arc::new(Session::new(sender))); + + // Fail if params are provided + let request = r#"{"jsonrpc": "2.0", "method": "eth_subscribe", "params": ["newPendingTransactions", {}], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Couldn't parse parameters: newPendingTransactions","data":"\"Expected no parameters.\""},"id":1}"#; + assert_eq!( + io.handle_request_sync(request, metadata.clone()), + Some(response.to_owned()) + ); + + // Subscribe + let request = r#"{"jsonrpc": "2.0", "method": "eth_subscribe", "params": ["newPendingTransactions"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x416d77337e24399d","id":1}"#; + assert_eq!( + io.handle_request_sync(request, metadata.clone()), + Some(response.to_owned()) + ); + + // Send new transactions + handler.notify_new_transactions(&[5.into(), 7.into()]); + + let (res, receiver) = receiver.into_future().wait().unwrap(); + let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":"0x0000000000000000000000000000000000000000000000000000000000000005","subscription":"0x416d77337e24399d"}}"#; + assert_eq!(res, Some(response.into())); + + let (res, receiver) = receiver.into_future().wait().unwrap(); + let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":"0x0000000000000000000000000000000000000000000000000000000000000007","subscription":"0x416d77337e24399d"}}"#; + assert_eq!(res, Some(response.into())); + + // And unsubscribe + let request = r#"{"jsonrpc": "2.0", "method": "eth_unsubscribe", "params": ["0x416d77337e24399d"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + assert_eq!( + io.handle_request_sync(request, metadata), + Some(response.to_owned()) + ); + + let (res, _receiver) = receiver.into_future().wait().unwrap(); + assert_eq!(res, None); } #[test] fn should_return_unimplemented() { - // given - let el = Runtime::with_thread_count(1); - let client = TestBlockChainClient::new(); - let pubsub = EthPubSubClient::new_test(Arc::new(client), el.executor()); - let pubsub = pubsub.to_delegate(); - - let mut io = MetaIoHandler::default(); - io.extend_with(pubsub); - - let mut metadata = Metadata::default(); - let (sender, _receiver) = futures::sync::mpsc::channel(8); - metadata.session = Some(Arc::new(Session::new(sender))); - - // Subscribe - let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not implemented yet. Please create an issue on Github repo."},"id":1}"#; - let request = r#"{"jsonrpc": "2.0", "method": "eth_subscribe", "params": ["syncing"], "id": 1}"#; - assert_eq!(io.handle_request_sync(request, metadata.clone()), Some(response.to_owned())); + // given + let el = Runtime::with_thread_count(1); + let client = TestBlockChainClient::new(); + let pubsub = EthPubSubClient::new_test(Arc::new(client), el.executor()); + let pubsub = pubsub.to_delegate(); + + let mut io = MetaIoHandler::default(); + io.extend_with(pubsub); + + let mut metadata = Metadata::default(); + let (sender, _receiver) = futures::sync::mpsc::channel(8); + metadata.session = Some(Arc::new(Session::new(sender))); + + // Subscribe + let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not implemented yet. Please create an issue on Github repo."},"id":1}"#; + let request = + r#"{"jsonrpc": "2.0", "method": "eth_subscribe", "params": ["syncing"], "id": 1}"#; + assert_eq!( + io.handle_request_sync(request, metadata.clone()), + Some(response.to_owned()) + ); } diff --git a/rpc/src/v1/tests/mocked/manage_network.rs b/rpc/src/v1/tests/mocked/manage_network.rs index d327a8743c6..eab1de00f30 100644 --- a/rpc/src/v1/tests/mocked/manage_network.rs +++ b/rpc/src/v1/tests/mocked/manage_network.rs @@ -1,22 +1,22 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . +use self::ethcore_network::{NetworkContext, ProtocolId}; use std::ops::RangeInclusive; use sync::ManageNetwork; -use self::ethcore_network::{ProtocolId, NetworkContext}; extern crate ethcore_network; @@ -24,12 +24,18 @@ pub struct TestManageNetwork; // TODO: rob, gavin (originally introduced this functions) - proper tests and test state impl ManageNetwork for TestManageNetwork { - fn accept_unreserved_peers(&self) { } - fn deny_unreserved_peers(&self) { } - fn remove_reserved_peer(&self, _peer: String) -> Result<(), String> { Ok(()) } - fn add_reserved_peer(&self, _peer: String) -> Result<(), String> { Ok(()) } - fn start_network(&self) {} - fn stop_network(&self) {} - fn num_peers_range(&self) -> RangeInclusive { 25..=50 } - fn with_proto_context(&self, _: ProtocolId, _: &mut FnMut(&NetworkContext)) { } + fn accept_unreserved_peers(&self) {} + fn deny_unreserved_peers(&self) {} + fn remove_reserved_peer(&self, _peer: String) -> Result<(), String> { + Ok(()) + } + fn add_reserved_peer(&self, _peer: String) -> Result<(), String> { + Ok(()) + } + fn start_network(&self) {} + fn stop_network(&self) {} + fn num_peers_range(&self) -> RangeInclusive { + 25..=50 + } + fn with_proto_context(&self, _: ProtocolId, _: &mut dyn FnMut(&dyn NetworkContext)) {} } diff --git a/rpc/src/v1/tests/mocked/mod.rs b/rpc/src/v1/tests/mocked/mod.rs index 35d109b1715..878e0b0500c 100644 --- a/rpc/src/v1/tests/mocked/mod.rs +++ b/rpc/src/v1/tests/mocked/mod.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! RPC mocked tests. Most of these test that the RPC server is serializing and forwarding //! method calls properly. @@ -29,7 +29,6 @@ mod parity_set; #[cfg(any(test, feature = "accounts"))] mod personal; mod pubsub; -mod rpc; #[cfg(any(test, feature = "accounts"))] mod secretstore; mod signer; diff --git a/rpc/src/v1/tests/mocked/net.rs b/rpc/src/v1/tests/mocked/net.rs index ff6d152d825..87450bd83e4 100644 --- a/rpc/src/v1/tests/mocked/net.rs +++ b/rpc/src/v1/tests/mocked/net.rs @@ -1,66 +1,68 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::sync::Arc; use jsonrpc_core::IoHandler; -use v1::{Net, NetClient}; -use v1::tests::helpers::{Config, TestSyncProvider}; +use std::sync::Arc; +use v1::{ + tests::helpers::{Config, TestSyncProvider}, + Net, NetClient, +}; fn sync_provider() -> Arc { - Arc::new(TestSyncProvider::new(Config { - network_id: 3, - num_peers: 120, - })) + Arc::new(TestSyncProvider::new(Config { + network_id: 3, + num_peers: 120, + })) } #[test] fn rpc_net_version() { - let sync = sync_provider(); - let net = NetClient::new(&sync).to_delegate(); - let mut io = IoHandler::new(); - io.extend_with(net); + let sync = sync_provider(); + let net = NetClient::new(&sync).to_delegate(); + let mut io = IoHandler::new(); + io.extend_with(net); - let request = r#"{"jsonrpc": "2.0", "method": "net_version", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"3","id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "net_version", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"3","id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_net_peer_count() { - let sync = sync_provider(); - let net = NetClient::new(&sync).to_delegate(); - let mut io = IoHandler::new(); - io.extend_with(net); + let sync = sync_provider(); + let net = NetClient::new(&sync).to_delegate(); + let mut io = IoHandler::new(); + io.extend_with(net); - let request = r#"{"jsonrpc": "2.0", "method": "net_peerCount", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"0x78","id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "net_peerCount", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x78","id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_net_listening() { - let sync = sync_provider(); - let net = NetClient::new(&sync).to_delegate(); - let mut io = IoHandler::new(); - io.extend_with(net); + let sync = sync_provider(); + let net = NetClient::new(&sync).to_delegate(); + let mut io = IoHandler::new(); + io.extend_with(net); - let request = r#"{"jsonrpc": "2.0", "method": "net_listening", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "net_listening", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } diff --git a/rpc/src/v1/tests/mocked/parity.rs b/rpc/src/v1/tests/mocked/parity.rs index 6608589f557..f3ec851ad6a 100644 --- a/rpc/src/v1/tests/mocked/parity.rs +++ b/rpc/src/v1/tests/mocked/parity.rs @@ -1,467 +1,446 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::sync::Arc; -use ethcore::client::{TestBlockChainClient, Executed, TransactionId}; +use ethcore::client::{Executed, TestBlockChainClient, TransactionId}; use ethcore_logger::RotatingLogger; -use ethereum_types::{Address, U256, H256}; +use ethereum_types::{Address, H256, U256}; use ethstore::ethkey::{Generator, Random}; use miner::pool::local_transactions::Status as LocalTransactionStatus; +use std::sync::Arc; use sync::ManageNetwork; use types::receipt::{LocalizedReceipt, TransactionOutcome}; -use jsonrpc_core::IoHandler; -use v1::{Parity, ParityClient}; -use v1::metadata::Metadata; -use v1::helpers::NetworkSettings; -use v1::helpers::external_signer::SignerService; -use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService, TestUpdater}; use super::manage_network::TestManageNetwork; +use jsonrpc_core::IoHandler; +use v1::{ + helpers::{external_signer::SignerService, NetworkSettings}, + metadata::Metadata, + tests::helpers::{Config, TestMinerService, TestSyncProvider}, + Parity, ParityClient, +}; use Host; -pub type TestParityClient = ParityClient; +pub type TestParityClient = ParityClient; pub struct Dependencies { - pub miner: Arc, - pub client: Arc, - pub sync: Arc, - pub updater: Arc, - pub logger: Arc, - pub settings: Arc, - pub network: Arc, - pub ws_address: Option, + pub miner: Arc, + pub client: Arc, + pub sync: Arc, + pub logger: Arc, + pub settings: Arc, + pub network: Arc, + pub ws_address: Option, } impl Dependencies { - pub fn new() -> Self { - Dependencies { - miner: Arc::new(TestMinerService::default()), - client: Arc::new(TestBlockChainClient::default()), - sync: Arc::new(TestSyncProvider::new(Config { - network_id: 3, - num_peers: 120, - })), - updater: Arc::new(TestUpdater::default()), - logger: Arc::new(RotatingLogger::new("rpc=trace".to_owned())), - settings: Arc::new(NetworkSettings { - name: "mynode".to_owned(), - chain: "testchain".to_owned(), - is_dev_chain: false, - network_port: 30303, - rpc_enabled: true, - rpc_interface: "all".to_owned(), - rpc_port: 8545, - }), - network: Arc::new(TestManageNetwork), - ws_address: Some("127.0.0.1:18546".into()), - } - } - - pub fn client(&self, signer: Option>) -> TestParityClient { - ParityClient::new( - self.client.clone(), - self.miner.clone(), - self.sync.clone(), - self.updater.clone(), - self.network.clone(), - self.logger.clone(), - self.settings.clone(), - signer, - self.ws_address.clone(), - None, - ) - } - - fn default_client(&self) -> IoHandler { - let mut io = IoHandler::default(); - io.extend_with(self.client(None).to_delegate()); - io - } - - fn with_signer(&self, signer: SignerService) -> IoHandler { - let mut io = IoHandler::default(); - io.extend_with(self.client(Some(Arc::new(signer))).to_delegate()); - io - } -} - -#[test] -fn rpc_parity_consensus_capability() { - let deps = Dependencies::new(); - let io = deps.default_client(); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_consensusCapability", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"capableUntil":15100},"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); - - deps.updater.set_current_block(15101); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_consensusCapability", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"incapableSince":15100},"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); - - deps.updater.set_updated(true); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_consensusCapability", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"capable","id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); -} - -#[test] -fn rpc_parity_version_info() { - let deps = Dependencies::new(); - let io = deps.default_client(); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_versionInfo", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"hash":"0x0000000000000000000000000000000000000096","track":"beta","version":{"major":1,"minor":5,"patch":0}},"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); -} - -#[test] -fn rpc_parity_releases_info() { - let deps = Dependencies::new(); - let io = deps.default_client(); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_releasesInfo", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"fork":15100,"minor":null,"this_fork":15000,"track":{"binary":"0x00000000000000000000000000000000000000000000000000000000000005e6","fork":15100,"is_critical":true,"version":{"hash":"0x0000000000000000000000000000000000000097","track":"beta","version":{"major":1,"minor":5,"patch":1}}}},"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + pub fn new() -> Self { + Dependencies { + miner: Arc::new(TestMinerService::default()), + client: Arc::new(TestBlockChainClient::default()), + sync: Arc::new(TestSyncProvider::new(Config { + network_id: 3, + num_peers: 120, + })), + logger: Arc::new(RotatingLogger::new("rpc=trace".to_owned())), + settings: Arc::new(NetworkSettings { + name: "mynode".to_owned(), + chain: "testchain".to_owned(), + is_dev_chain: false, + network_port: 30303, + rpc_enabled: true, + rpc_interface: "all".to_owned(), + rpc_port: 8545, + }), + network: Arc::new(TestManageNetwork), + ws_address: Some("127.0.0.1:18546".into()), + } + } + + pub fn client(&self, signer: Option>) -> TestParityClient { + ParityClient::new( + self.client.clone(), + self.miner.clone(), + self.sync.clone(), + self.network.clone(), + self.logger.clone(), + self.settings.clone(), + signer, + self.ws_address.clone(), + None, + ) + } + + fn default_client(&self) -> IoHandler { + let mut io = IoHandler::default(); + io.extend_with(self.client(None).to_delegate()); + io + } + + fn with_signer(&self, signer: SignerService) -> IoHandler { + let mut io = IoHandler::default(); + io.extend_with(self.client(Some(Arc::new(signer))).to_delegate()); + io + } } #[test] fn rpc_parity_extra_data() { - let deps = Dependencies::new(); - let io = deps.default_client(); + let deps = Dependencies::new(); + let io = deps.default_client(); - let request = r#"{"jsonrpc": "2.0", "method": "parity_extraData", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"0x01020304","id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_extraData", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x01020304","id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_default_extra_data() { - use version::version_data; - use bytes::ToPretty; + use bytes::ToPretty; + use version::version_data; - let deps = Dependencies::new(); - let io = deps.default_client(); + let deps = Dependencies::new(); + let io = deps.default_client(); - let request = r#"{"jsonrpc": "2.0", "method": "parity_defaultExtraData", "params": [], "id": 1}"#; - let response = format!(r#"{{"jsonrpc":"2.0","result":"0x{}","id":1}}"#, version_data().to_hex()); + let request = + r#"{"jsonrpc": "2.0", "method": "parity_defaultExtraData", "params": [], "id": 1}"#; + let response = format!( + r#"{{"jsonrpc":"2.0","result":"0x{}","id":1}}"#, + version_data().to_hex() + ); - assert_eq!(io.handle_request_sync(request), Some(response)); + assert_eq!(io.handle_request_sync(request), Some(response)); } #[test] fn rpc_parity_gas_floor_target() { - let deps = Dependencies::new(); - let io = deps.default_client(); + let deps = Dependencies::new(); + let io = deps.default_client(); - let request = r#"{"jsonrpc": "2.0", "method": "parity_gasFloorTarget", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"0x3039","id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_gasFloorTarget", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x3039","id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_min_gas_price() { - let deps = Dependencies::new(); - let io = deps.default_client(); + let deps = Dependencies::new(); + let io = deps.default_client(); - let request = r#"{"jsonrpc": "2.0", "method": "parity_minGasPrice", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"0x1312d00","id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_minGasPrice", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x1312d00","id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_dev_logs() { - let deps = Dependencies::new(); - deps.logger.append("a".to_owned()); - deps.logger.append("b".to_owned()); + let deps = Dependencies::new(); + deps.logger.append("a".to_owned()); + deps.logger.append("b".to_owned()); - let io = deps.default_client(); + let io = deps.default_client(); - let request = r#"{"jsonrpc": "2.0", "method": "parity_devLogs", "params":[], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":["b","a"],"id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_devLogs", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":["b","a"],"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_dev_logs_levels() { - let deps = Dependencies::new(); - let io = deps.default_client(); + let deps = Dependencies::new(); + let io = deps.default_client(); - let request = r#"{"jsonrpc": "2.0", "method": "parity_devLogsLevels", "params":[], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"rpc=trace","id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_devLogsLevels", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"rpc=trace","id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_transactions_limit() { - let deps = Dependencies::new(); - let io = deps.default_client(); + let deps = Dependencies::new(); + let io = deps.default_client(); - let request = r#"{"jsonrpc": "2.0", "method": "parity_transactionsLimit", "params":[], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":1024,"id":1}"#; + let request = + r#"{"jsonrpc": "2.0", "method": "parity_transactionsLimit", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":1024,"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_net_chain() { - let deps = Dependencies::new(); - let io = deps.default_client(); + let deps = Dependencies::new(); + let io = deps.default_client(); - let request = r#"{"jsonrpc": "2.0", "method": "parity_netChain", "params":[], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"testchain","id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_netChain", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"testchain","id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_chain() { - let deps = Dependencies::new(); - let io = deps.default_client(); + let deps = Dependencies::new(); + let io = deps.default_client(); - let request = r#"{"jsonrpc": "2.0", "method": "parity_chain", "params":[], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"foundation","id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_chain", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"foundation","id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_net_peers() { - let deps = Dependencies::new(); - let io = deps.default_client(); + let deps = Dependencies::new(); + let io = deps.default_client(); - let request = r#"{"jsonrpc": "2.0", "method": "parity_netPeers", "params":[], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"active":0,"connected":120,"max":50,"peers":[{"caps":["eth/62","eth/63"],"id":"node1","name":{"ParityClient":{"can_handle_large_requests":true,"compiler":"rustc","identity":"1","name":"Parity-Ethereum","os":"linux","semver":"2.4.0"}},"network":{"localAddress":"127.0.0.1:8888","remoteAddress":"127.0.0.1:7777"},"protocols":{"eth":{"difficulty":"0x28","head":"0000000000000000000000000000000000000000000000000000000000000032","version":62},"pip":null}},{"caps":["eth/63","eth/64"],"id":null,"name":{"ParityClient":{"can_handle_large_requests":true,"compiler":"rustc","identity":"2","name":"Parity-Ethereum","os":"linux","semver":"2.4.0"}},"network":{"localAddress":"127.0.0.1:3333","remoteAddress":"Handshake"},"protocols":{"eth":{"difficulty":null,"head":"000000000000000000000000000000000000000000000000000000000000003c","version":64},"pip":null}}]},"id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_netPeers", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"active":0,"connected":120,"max":50,"peers":[{"caps":["eth/63","eth/64"],"id":"node1","name":{"ParityClient":{"can_handle_large_requests":true,"compiler":"rustc","identity":"1","name":"Parity-Ethereum","os":"linux","semver":"2.4.0"}},"network":{"localAddress":"127.0.0.1:8888","remoteAddress":"127.0.0.1:7777"},"protocols":{"eth":{"difficulty":"0x28","head":"0000000000000000000000000000000000000000000000000000000000000032","version":63}}},{"caps":["eth/64","eth/65"],"id":null,"name":{"Other":"Open-Ethereum/2/v2.4.0/linux/rustc"},"network":{"localAddress":"127.0.0.1:3333","remoteAddress":"Handshake"},"protocols":{"eth":{"difficulty":null,"head":"000000000000000000000000000000000000000000000000000000000000003c","version":65}}}]},"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_net_port() { - let deps = Dependencies::new(); - let io = deps.default_client(); + let deps = Dependencies::new(); + let io = deps.default_client(); - let request = r#"{"jsonrpc": "2.0", "method": "parity_netPort", "params":[], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":30303,"id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_netPort", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":30303,"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_rpc_settings() { - let deps = Dependencies::new(); - let io = deps.default_client(); + let deps = Dependencies::new(); + let io = deps.default_client(); - let request = r#"{"jsonrpc": "2.0", "method": "parity_rpcSettings", "params":[], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"enabled":true,"interface":"all","port":8545},"id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_rpcSettings", "params":[], "id": 1}"#; + let response = + r#"{"jsonrpc":"2.0","result":{"enabled":true,"interface":"all","port":8545},"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_node_name() { - let deps = Dependencies::new(); - let io = deps.default_client(); + let deps = Dependencies::new(); + let io = deps.default_client(); - let request = r#"{"jsonrpc": "2.0", "method": "parity_nodeName", "params":[], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"mynode","id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_nodeName", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"mynode","id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_unsigned_transactions_count() { - let deps = Dependencies::new(); - let io = deps.with_signer(SignerService::new_test(true)); + let deps = Dependencies::new(); + let io = deps.with_signer(SignerService::new_test(true)); - let request = r#"{"jsonrpc": "2.0", "method": "parity_unsignedTransactionsCount", "params":[], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":0,"id":1}"#; + let request = + r#"{"jsonrpc": "2.0", "method": "parity_unsignedTransactionsCount", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":0,"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_unsigned_transactions_count_when_signer_disabled() { - let deps = Dependencies::new(); - let io = deps.default_client(); + let deps = Dependencies::new(); + let io = deps.default_client(); - let request = r#"{"jsonrpc": "2.0", "method": "parity_unsignedTransactionsCount", "params":[], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Trusted Signer is disabled. This API is not available."},"id":1}"#; + let request = + r#"{"jsonrpc": "2.0", "method": "parity_unsignedTransactionsCount", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Trusted Signer is disabled. This API is not available."},"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_pending_transactions() { - let deps = Dependencies::new(); - let io = deps.default_client(); + let deps = Dependencies::new(); + let io = deps.default_client(); - let request = r#"{"jsonrpc": "2.0", "method": "parity_pendingTransactions", "params":[], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":[],"id":1}"#; + let request = + r#"{"jsonrpc": "2.0", "method": "parity_pendingTransactions", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":[],"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_encrypt() { - let deps = Dependencies::new(); - let io = deps.default_client(); - let key = format!("{:x}", Random.generate().unwrap().public()); + let deps = Dependencies::new(); + let io = deps.default_client(); + let key = format!("{:x}", Random.generate().unwrap().public()); - let request = r#"{"jsonrpc": "2.0", "method": "parity_encryptMessage", "params":["0x"#.to_owned() + &key + r#"", "0x01"], "id": 1}"#; - assert!(io.handle_request_sync(&request).unwrap().contains("result"), "Should return success."); + let request = r#"{"jsonrpc": "2.0", "method": "parity_encryptMessage", "params":["0x"# + .to_owned() + + &key + + r#"", "0x01"], "id": 1}"#; + assert!( + io.handle_request_sync(&request).unwrap().contains("result"), + "Should return success." + ); } #[test] fn rpc_parity_ws_address() { - // given - let mut deps = Dependencies::new(); - let io1 = deps.default_client(); - deps.ws_address = None; - let io2 = deps.default_client(); - - // when - let request = r#"{"jsonrpc": "2.0", "method": "parity_wsUrl", "params": [], "id": 1}"#; - let response1 = r#"{"jsonrpc":"2.0","result":"127.0.0.1:18546","id":1}"#; - let response2 = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"WebSockets Server is disabled. This API is not available."},"id":1}"#; - - // then - assert_eq!(io1.handle_request_sync(request), Some(response1.to_owned())); - assert_eq!(io2.handle_request_sync(request), Some(response2.to_owned())); + // given + let mut deps = Dependencies::new(); + let io1 = deps.default_client(); + deps.ws_address = None; + let io2 = deps.default_client(); + + // when + let request = r#"{"jsonrpc": "2.0", "method": "parity_wsUrl", "params": [], "id": 1}"#; + let response1 = r#"{"jsonrpc":"2.0","result":"127.0.0.1:18546","id":1}"#; + let response2 = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"WebSockets Server is disabled. This API is not available."},"id":1}"#; + + // then + assert_eq!(io1.handle_request_sync(request), Some(response1.to_owned())); + assert_eq!(io2.handle_request_sync(request), Some(response2.to_owned())); } #[test] fn rpc_parity_next_nonce() { - let deps = Dependencies::new(); - let address = Address::default(); - let io1 = deps.default_client(); - let deps = Dependencies::new(); - deps.miner.increment_nonce(&address); - deps.miner.increment_nonce(&address); - deps.miner.increment_nonce(&address); - let io2 = deps.default_client(); - - let request = r#"{ + let deps = Dependencies::new(); + let address = Address::default(); + let io1 = deps.default_client(); + let deps = Dependencies::new(); + deps.miner.increment_nonce(&address); + deps.miner.increment_nonce(&address); + deps.miner.increment_nonce(&address); + let io2 = deps.default_client(); + + let request = r#"{ "jsonrpc": "2.0", "method": "parity_nextNonce", - "params": [""#.to_owned() + &format!("0x{:x}", address) + r#""], + "params": [""# + .to_owned() + + &format!("0x{:x}", address) + + r#""], "id": 1 }"#; - let response1 = r#"{"jsonrpc":"2.0","result":"0x0","id":1}"#; - let response2 = r#"{"jsonrpc":"2.0","result":"0x3","id":1}"#; + let response1 = r#"{"jsonrpc":"2.0","result":"0x0","id":1}"#; + let response2 = r#"{"jsonrpc":"2.0","result":"0x3","id":1}"#; - assert_eq!(io1.handle_request_sync(&request), Some(response1.to_owned())); - assert_eq!(io2.handle_request_sync(&request), Some(response2.to_owned())); + assert_eq!( + io1.handle_request_sync(&request), + Some(response1.to_owned()) + ); + assert_eq!( + io2.handle_request_sync(&request), + Some(response2.to_owned()) + ); } #[test] fn rpc_parity_transactions_stats() { - let deps = Dependencies::new(); - let io = deps.default_client(); + let deps = Dependencies::new(); + let io = deps.default_client(); - let request = r#"{"jsonrpc": "2.0", "method": "parity_pendingTransactionsStats", "params":[], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"0x0000000000000000000000000000000000000000000000000000000000000001":{"firstSeen":10,"propagatedTo":{"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080":16}},"0x0000000000000000000000000000000000000000000000000000000000000005":{"firstSeen":16,"propagatedTo":{"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010":1}}},"id":1}"#; + let request = + r#"{"jsonrpc": "2.0", "method": "parity_pendingTransactionsStats", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"0x0000000000000000000000000000000000000000000000000000000000000001":{"firstSeen":10,"propagatedTo":{"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080":16}},"0x0000000000000000000000000000000000000000000000000000000000000005":{"firstSeen":16,"propagatedTo":{"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010":1}}},"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_local_transactions() { - let deps = Dependencies::new(); - let io = deps.default_client(); - let tx = ::types::transaction::Transaction { - value: 5.into(), - gas: 3.into(), - gas_price: 2.into(), - action: ::types::transaction::Action::Create, - data: vec![1, 2, 3], - nonce: 0.into(), - }.fake_sign(3.into()); - let tx = Arc::new(::miner::pool::VerifiedTransaction::from_pending_block_transaction(tx)); - deps.miner.local_transactions.lock().insert(10.into(), LocalTransactionStatus::Pending(tx.clone())); - deps.miner.local_transactions.lock().insert(15.into(), LocalTransactionStatus::Pending(tx.clone())); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_localTransactions", "params":[], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"0x000000000000000000000000000000000000000000000000000000000000000a":{"status":"pending"},"0x000000000000000000000000000000000000000000000000000000000000000f":{"status":"pending"}},"id":1}"#; - - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + let deps = Dependencies::new(); + let io = deps.default_client(); + let tx = ::types::transaction::Transaction { + value: 5.into(), + gas: 3.into(), + gas_price: 2.into(), + action: ::types::transaction::Action::Create, + data: vec![1, 2, 3], + nonce: 0.into(), + } + .fake_sign(3.into()); + let tx = Arc::new(::miner::pool::VerifiedTransaction::from_pending_block_transaction(tx)); + deps.miner + .local_transactions + .lock() + .insert(10.into(), LocalTransactionStatus::Pending(tx.clone())); + deps.miner + .local_transactions + .lock() + .insert(15.into(), LocalTransactionStatus::Pending(tx.clone())); + + let request = + r#"{"jsonrpc": "2.0", "method": "parity_localTransactions", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"0x000000000000000000000000000000000000000000000000000000000000000a":{"status":"pending"},"0x000000000000000000000000000000000000000000000000000000000000000f":{"status":"pending"}},"id":1}"#; + + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_chain_status() { - let deps = Dependencies::new(); - let io = deps.default_client(); + let deps = Dependencies::new(); + let io = deps.default_client(); - *deps.client.ancient_block.write() = Some((H256::default(), 5)); - *deps.client.first_block.write() = Some((H256::from(U256::from(1234)), 3333)); + *deps.client.ancient_block.write() = Some((H256::default(), 5)); + *deps.client.first_block.write() = Some((H256::from(U256::from(1234)), 3333)); - let request = r#"{"jsonrpc": "2.0", "method": "parity_chainStatus", "params":[], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"blockGap":["0x6","0xd05"]},"id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_chainStatus", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"blockGap":["0x6","0xd05"]},"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_node_kind() { - let deps = Dependencies::new(); - let io = deps.default_client(); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_nodeKind", "params":[], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"availability":"personal","capability":"full"},"id":1}"#; - - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); -} - -#[test] -fn rpc_parity_cid() { - let deps = Dependencies::new(); - let io = deps.default_client(); + let deps = Dependencies::new(); + let io = deps.default_client(); - let request = r#"{"jsonrpc": "2.0", "method": "parity_cidV0", "params":["0x414243"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"QmSF59MAENc8ZhM4aM1thuAE8w5gDmyfzkAvNoyPea7aDz","id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_nodeKind", "params":[], "id": 1}"#; + let response = + r#"{"jsonrpc":"2.0","result":{"availability":"personal","capability":"full"},"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_call() { - let deps = Dependencies::new(); - deps.client.set_execution_result(Ok(Executed { - exception: None, - gas: U256::zero(), - gas_used: U256::from(0xff30), - refunded: U256::from(0x5), - cumulative_gas_used: U256::zero(), - logs: vec![], - contracts_created: vec![], - output: vec![0x12, 0x34, 0xff], - trace: vec![], - vm_trace: None, - state_diff: None, - })); - let io = deps.default_client(); - - let request = r#"{ + let deps = Dependencies::new(); + deps.client.set_execution_result(Ok(Executed { + exception: None, + gas: U256::zero(), + gas_used: U256::from(0xff30), + refunded: U256::from(0x5), + cumulative_gas_used: U256::zero(), + logs: vec![], + contracts_created: vec![], + output: vec![0x12, 0x34, 0xff], + trace: vec![], + vm_trace: None, + state_diff: None, + })); + let io = deps.default_client(); + + let request = r#"{ "jsonrpc": "2.0", "method": "parity_call", "params": [[{ @@ -475,98 +454,100 @@ fn rpc_parity_call() { "latest"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":["0x1234ff"],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":["0x1234ff"],"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_block_receipts() { - let deps = Dependencies::new(); - deps.client.receipts.write() - .insert(TransactionId::Hash(1.into()), LocalizedReceipt { - transaction_hash: 1.into(), - transaction_index: 0, - block_hash: 3.into(), - block_number: 0, - cumulative_gas_used: 21_000.into(), - gas_used: 21_000.into(), - contract_address: None, - logs: vec![], - log_bloom: 1.into(), - outcome: TransactionOutcome::Unknown, - to: None, - from: 9.into(), - }); - let io = deps.default_client(); - - let request = r#"{ + let deps = Dependencies::new(); + deps.client.receipts.write().insert( + TransactionId::Hash(1.into()), + LocalizedReceipt { + transaction_hash: 1.into(), + transaction_index: 0, + block_hash: 3.into(), + block_number: 0, + cumulative_gas_used: 21_000.into(), + gas_used: 21_000.into(), + contract_address: None, + logs: vec![], + log_bloom: 1.into(), + outcome: TransactionOutcome::Unknown, + to: None, + from: 9.into(), + }, + ); + let io = deps.default_client(); + + let request = r#"{ "jsonrpc": "2.0", "method": "parity_getBlockReceipts", "params": [], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":[{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000003","blockNumber":"0x0","contractAddress":null,"cumulativeGasUsed":"0x5208","from":"0x0000000000000000000000000000000000000009","gasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","root":null,"status":null,"to":null,"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000001","transactionIndex":"0x0"}],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":[{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000003","blockNumber":"0x0","contractAddress":null,"cumulativeGasUsed":"0x5208","from":"0x0000000000000000000000000000000000000009","gasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","to":null,"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000001","transactionIndex":"0x0"}],"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_status_ok() { - let deps = Dependencies::new(); - let io = deps.default_client(); + let deps = Dependencies::new(); + let io = deps.default_client(); - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", "method": "parity_nodeStatus", "params": [], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_status_error_peers() { - let deps = Dependencies::new(); - deps.sync.status.write().num_peers = 0; - let io = deps.default_client(); + let deps = Dependencies::new(); + deps.sync.status.write().num_peers = 0; + let io = deps.default_client(); - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", "method": "parity_nodeStatus", "params": [], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32066,"message":"Node is not connected to any peers."},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32066,"message":"Node is not connected to any peers."},"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_status_error_sync() { - let deps = Dependencies::new(); - deps.sync.status.write().state = ::sync::SyncState::Blocks; - let io = deps.default_client(); + let deps = Dependencies::new(); + deps.sync.status.write().state = ::sync::SyncState::Blocks; + let io = deps.default_client(); - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", "method": "parity_nodeStatus", "params": [], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32001,"message":"Still syncing."},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32001,"message":"Still syncing."},"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_verify_signature() { - let deps = Dependencies::new(); - let io = deps.default_client(); + let deps = Dependencies::new(); + let io = deps.default_client(); - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", "method": "parity_verifySignature", "params": [ @@ -579,7 +560,7 @@ fn rpc_parity_verify_signature() { "id": 0 }"#; - let response = r#"{"jsonrpc":"2.0","result":{"address":"0x9a2a08a1170f51208c2f3cede0d29ada94481eed","isValidForCurrentChain":true,"publicKey":"0xbeec94ea24444906fe247c47841a45220f07e5718d06157fe4502fac326dab617e973e221e45746721330c2db3f63202268686378cc28b9800c1daaf0bbafeb1"},"id":0}"#; + let response = r#"{"jsonrpc":"2.0","result":{"address":"0x9a2a08a1170f51208c2f3cede0d29ada94481eed","isValidForCurrentChain":true,"publicKey":"0xbeec94ea24444906fe247c47841a45220f07e5718d06157fe4502fac326dab617e973e221e45746721330c2db3f63202268686378cc28b9800c1daaf0bbafeb1"},"id":0}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } diff --git a/rpc/src/v1/tests/mocked/parity_accounts.rs b/rpc/src/v1/tests/mocked/parity_accounts.rs index 5b2e0762b18..78cd70d390e 100644 --- a/rpc/src/v1/tests/mocked/parity_accounts.rs +++ b/rpc/src/v1/tests/mocked/parity_accounts.rs @@ -1,481 +1,678 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use std::sync::Arc; use accounts::{AccountProvider, AccountProviderSettings}; use ethereum_types::Address; -use ethstore::EthStore; -use ethstore::accounts_dir::RootDiskDirectory; +use ethstore::{accounts_dir::RootDiskDirectory, EthStore}; use tempdir::TempDir; use jsonrpc_core::IoHandler; -use v1::{ParityAccounts, ParityAccountsInfo, ParityAccountsClient}; +use v1::{ParityAccounts, ParityAccountsClient, ParityAccountsInfo}; struct ParityAccountsTester { - accounts: Arc, - io: IoHandler, + accounts: Arc, + io: IoHandler, } fn accounts_provider() -> Arc { - Arc::new(AccountProvider::transient_provider()) + Arc::new(AccountProvider::transient_provider()) } fn accounts_provider_with_vaults_support(temp_path: &str) -> Arc { - let root_keys_dir = RootDiskDirectory::create(temp_path).unwrap(); - let secret_store = EthStore::open(Box::new(root_keys_dir)).unwrap(); - Arc::new(AccountProvider::new(Box::new(secret_store), AccountProviderSettings::default())) + let root_keys_dir = RootDiskDirectory::create(temp_path).unwrap(); + let secret_store = EthStore::open(Box::new(root_keys_dir)).unwrap(); + Arc::new(AccountProvider::new( + Box::new(secret_store), + AccountProviderSettings::default(), + )) } fn setup_with_accounts_provider(accounts_provider: Arc) -> ParityAccountsTester { - let opt_ap = accounts_provider.clone(); - let parity_accounts = ParityAccountsClient::new(&opt_ap); - let parity_accounts2 = ParityAccountsClient::new(&opt_ap); - let mut io = IoHandler::default(); - io.extend_with(ParityAccounts::to_delegate(parity_accounts)); - io.extend_with(ParityAccountsInfo::to_delegate(parity_accounts2)); - - let tester = ParityAccountsTester { - accounts: accounts_provider, - io: io, - }; - - tester + let opt_ap = accounts_provider.clone(); + let parity_accounts = ParityAccountsClient::new(&opt_ap); + let parity_accounts2 = ParityAccountsClient::new(&opt_ap); + let mut io = IoHandler::default(); + io.extend_with(ParityAccounts::to_delegate(parity_accounts)); + io.extend_with(ParityAccountsInfo::to_delegate(parity_accounts2)); + + let tester = ParityAccountsTester { + accounts: accounts_provider, + io: io, + }; + + tester } fn setup() -> ParityAccountsTester { - setup_with_accounts_provider(accounts_provider()) + setup_with_accounts_provider(accounts_provider()) } fn setup_with_vaults_support(temp_path: &str) -> ParityAccountsTester { - setup_with_accounts_provider(accounts_provider_with_vaults_support(temp_path)) + setup_with_accounts_provider(accounts_provider_with_vaults_support(temp_path)) } #[test] fn rpc_parity_accounts_info() { - let tester = setup(); - let io = tester.io; - - tester.accounts.new_account(&"".into()).unwrap(); - let accounts = tester.accounts.accounts().unwrap(); - assert_eq!(accounts.len(), 1); - let address = accounts[0]; - - tester.accounts.set_address_name(1.into(), "XX".into()); - tester.accounts.set_account_name(address.clone(), "Test".into()).unwrap(); - tester.accounts.set_account_meta(address.clone(), "{foo: 69}".into()).unwrap(); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_accountsInfo", "params": [], "id": 1}"#; - let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":{{\"0x{:x}\":{{\"name\":\"Test\"}}}},\"id\":1}}", address); - assert_eq!(io.handle_request_sync(request), Some(response)); + let tester = setup(); + let io = tester.io; + + tester.accounts.new_account(&"".into()).unwrap(); + let accounts = tester.accounts.accounts().unwrap(); + assert_eq!(accounts.len(), 1); + let address = accounts[0]; + + tester.accounts.set_address_name(1.into(), "XX".into()); + tester + .accounts + .set_account_name(address.clone(), "Test".into()) + .unwrap(); + tester + .accounts + .set_account_meta(address.clone(), "{foo: 69}".into()) + .unwrap(); + + let request = r#"{"jsonrpc": "2.0", "method": "parity_accountsInfo", "params": [], "id": 1}"#; + let response = format!( + "{{\"jsonrpc\":\"2.0\",\"result\":{{\"0x{:x}\":{{\"name\":\"Test\"}}}},\"id\":1}}", + address + ); + assert_eq!(io.handle_request_sync(request), Some(response)); } #[test] fn rpc_parity_default_account() { - let tester = setup(); - let io = tester.io; - - // Check empty - let address = Address::default(); - let request = r#"{"jsonrpc": "2.0", "method": "parity_defaultAccount", "params": [], "id": 1}"#; - let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":\"0x{:x}\",\"id\":1}}", address); - assert_eq!(io.handle_request_sync(request), Some(response)); - - // With account - tester.accounts.new_account(&"".into()).unwrap(); - let accounts = tester.accounts.accounts().unwrap(); - assert_eq!(accounts.len(), 1); - let address = accounts[0]; - - let request = r#"{"jsonrpc": "2.0", "method": "parity_defaultAccount", "params": [], "id": 1}"#; - let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":\"0x{:x}\",\"id\":1}}", address); - assert_eq!(io.handle_request_sync(request), Some(response)); + let tester = setup(); + let io = tester.io; + + // Check empty + let address = Address::default(); + let request = r#"{"jsonrpc": "2.0", "method": "parity_defaultAccount", "params": [], "id": 1}"#; + let response = format!( + "{{\"jsonrpc\":\"2.0\",\"result\":\"0x{:x}\",\"id\":1}}", + address + ); + assert_eq!(io.handle_request_sync(request), Some(response)); + + // With account + tester.accounts.new_account(&"".into()).unwrap(); + let accounts = tester.accounts.accounts().unwrap(); + assert_eq!(accounts.len(), 1); + let address = accounts[0]; + + let request = r#"{"jsonrpc": "2.0", "method": "parity_defaultAccount", "params": [], "id": 1}"#; + let response = format!( + "{{\"jsonrpc\":\"2.0\",\"result\":\"0x{:x}\",\"id\":1}}", + address + ); + assert_eq!(io.handle_request_sync(request), Some(response)); } #[test] fn should_be_able_to_get_account_info() { - let tester = setup(); - tester.accounts.new_account(&"".into()).unwrap(); - let accounts = tester.accounts.accounts().unwrap(); - assert_eq!(accounts.len(), 1); - let address = accounts[0]; - - let uuid = tester.accounts.accounts_info().unwrap().get(&address).unwrap().uuid.as_ref().unwrap().clone(); - tester.accounts.set_account_name(address.clone(), "Test".to_owned()).unwrap(); - tester.accounts.set_account_meta(address.clone(), "{foo: 69}".to_owned()).unwrap(); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params": [], "id": 1}"#; - let res = tester.io.handle_request_sync(request); - let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":{{\"0x{:x}\":{{\"meta\":\"{{foo: 69}}\",\"name\":\"Test\",\"uuid\":\"{}\"}}}},\"id\":1}}", address, uuid); - assert_eq!(res, Some(response)); + let tester = setup(); + tester.accounts.new_account(&"".into()).unwrap(); + let accounts = tester.accounts.accounts().unwrap(); + assert_eq!(accounts.len(), 1); + let address = accounts[0]; + + let uuid = tester + .accounts + .accounts_info() + .unwrap() + .get(&address) + .unwrap() + .uuid + .as_ref() + .unwrap() + .clone(); + tester + .accounts + .set_account_name(address.clone(), "Test".to_owned()) + .unwrap(); + tester + .accounts + .set_account_meta(address.clone(), "{foo: 69}".to_owned()) + .unwrap(); + + let request = + r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params": [], "id": 1}"#; + let res = tester.io.handle_request_sync(request); + let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":{{\"0x{:x}\":{{\"meta\":\"{{foo: 69}}\",\"name\":\"Test\",\"uuid\":\"{}\"}}}},\"id\":1}}", address, uuid); + assert_eq!(res, Some(response)); } #[test] fn should_be_able_to_set_name() { - let tester = setup(); - tester.accounts.new_account(&"".into()).unwrap(); - let accounts = tester.accounts.accounts().unwrap(); - assert_eq!(accounts.len(), 1); - let address = accounts[0]; - - let request = format!(r#"{{"jsonrpc": "2.0", "method": "parity_setAccountName", "params": ["0x{:x}", "Test"], "id": 1}}"#, address); - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - let res = tester.io.handle_request_sync(&request); - assert_eq!(res, Some(response.into())); - - let uuid = tester.accounts.accounts_info().unwrap().get(&address).unwrap().uuid.as_ref().unwrap().clone(); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params": [], "id": 1}"#; - let res = tester.io.handle_request_sync(request); - let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":{{\"0x{:x}\":{{\"meta\":\"{{}}\",\"name\":\"Test\",\"uuid\":\"{}\"}}}},\"id\":1}}", address, uuid); - assert_eq!(res, Some(response)); + let tester = setup(); + tester.accounts.new_account(&"".into()).unwrap(); + let accounts = tester.accounts.accounts().unwrap(); + assert_eq!(accounts.len(), 1); + let address = accounts[0]; + + let request = format!( + r#"{{"jsonrpc": "2.0", "method": "parity_setAccountName", "params": ["0x{:x}", "Test"], "id": 1}}"#, + address + ); + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + let res = tester.io.handle_request_sync(&request); + assert_eq!(res, Some(response.into())); + + let uuid = tester + .accounts + .accounts_info() + .unwrap() + .get(&address) + .unwrap() + .uuid + .as_ref() + .unwrap() + .clone(); + + let request = + r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params": [], "id": 1}"#; + let res = tester.io.handle_request_sync(request); + let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":{{\"0x{:x}\":{{\"meta\":\"{{}}\",\"name\":\"Test\",\"uuid\":\"{}\"}}}},\"id\":1}}", address, uuid); + assert_eq!(res, Some(response)); } #[test] fn should_be_able_to_set_meta() { - let tester = setup(); - tester.accounts.new_account(&"".into()).unwrap(); - let accounts = tester.accounts.accounts().unwrap(); - assert_eq!(accounts.len(), 1); - let address = accounts[0]; - - let request = format!(r#"{{"jsonrpc": "2.0", "method": "parity_setAccountMeta", "params": ["0x{:x}", "{{foo: 69}}"], "id": 1}}"#, address); - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - let res = tester.io.handle_request_sync(&request); - assert_eq!(res, Some(response.into())); - - let uuid = tester.accounts.accounts_info().unwrap().get(&address).unwrap().uuid.as_ref().unwrap().clone(); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params": [], "id": 1}"#; - let res = tester.io.handle_request_sync(request); - let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":{{\"0x{:x}\":{{\"meta\":\"{{foo: 69}}\",\"name\":\"\",\"uuid\":\"{}\"}}}},\"id\":1}}", address, uuid); - assert_eq!(res, Some(response)); + let tester = setup(); + tester.accounts.new_account(&"".into()).unwrap(); + let accounts = tester.accounts.accounts().unwrap(); + assert_eq!(accounts.len(), 1); + let address = accounts[0]; + + let request = format!( + r#"{{"jsonrpc": "2.0", "method": "parity_setAccountMeta", "params": ["0x{:x}", "{{foo: 69}}"], "id": 1}}"#, + address + ); + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + let res = tester.io.handle_request_sync(&request); + assert_eq!(res, Some(response.into())); + + let uuid = tester + .accounts + .accounts_info() + .unwrap() + .get(&address) + .unwrap() + .uuid + .as_ref() + .unwrap() + .clone(); + + let request = + r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params": [], "id": 1}"#; + let res = tester.io.handle_request_sync(request); + let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":{{\"0x{:x}\":{{\"meta\":\"{{foo: 69}}\",\"name\":\"\",\"uuid\":\"{}\"}}}},\"id\":1}}", address, uuid); + assert_eq!(res, Some(response)); } #[test] fn should_be_able_to_kill_account() { - let tester = setup(); - tester.accounts.new_account(&"password".into()).unwrap(); - let accounts = tester.accounts.accounts().unwrap(); - assert_eq!(accounts.len(), 1); - let address = accounts[0]; - - let request = format!(r#"{{"jsonrpc": "2.0", "method": "parity_killAccount", "params": ["0xf00baba2f00baba2f00baba2f00baba2f00baba2"], "id": 1}}"#); - let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid params: invalid length 1, expected a tuple of size 2."},"id":1}"#; - let res = tester.io.handle_request_sync(&request); - assert_eq!(res, Some(response.into())); - - let request = format!(r#"{{"jsonrpc": "2.0", "method": "parity_killAccount", "params": ["0x{:x}", "password"], "id": 1}}"#, address); - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - let res = tester.io.handle_request_sync(&request); - assert_eq!(res, Some(response.into())); - - let accounts = tester.accounts.accounts().unwrap(); - assert_eq!(accounts.len(), 0); + let tester = setup(); + tester.accounts.new_account(&"password".into()).unwrap(); + let accounts = tester.accounts.accounts().unwrap(); + assert_eq!(accounts.len(), 1); + let address = accounts[0]; + + let request = format!( + r#"{{"jsonrpc": "2.0", "method": "parity_killAccount", "params": ["0xf00baba2f00baba2f00baba2f00baba2f00baba2"], "id": 1}}"# + ); + let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid params: invalid length 1, expected a tuple of size 2."},"id":1}"#; + let res = tester.io.handle_request_sync(&request); + assert_eq!(res, Some(response.into())); + + let request = format!( + r#"{{"jsonrpc": "2.0", "method": "parity_killAccount", "params": ["0x{:x}", "password"], "id": 1}}"#, + address + ); + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + let res = tester.io.handle_request_sync(&request); + assert_eq!(res, Some(response.into())); + + let accounts = tester.accounts.accounts().unwrap(); + assert_eq!(accounts.len(), 0); } #[test] fn should_be_able_to_remove_address() { - let tester = setup(); - - // add an address - let request = r#"{"jsonrpc": "2.0", "method": "parity_setAccountName", "params": ["0x000baba1000baba2000baba3000baba4000baba5", "Test"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - let res = tester.io.handle_request_sync(&request); - assert_eq!(res, Some(response.into())); - - // verify it exists - let request = r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params": [], "id": 2}"#; - let res = tester.io.handle_request_sync(request); - let response = r#"{"jsonrpc":"2.0","result":{"0x000baba1000baba2000baba3000baba4000baba5":{"meta":"{}","name":"Test"}},"id":2}"#; - assert_eq!(res, Some(response.into())); - - // remove the address - let request = r#"{"jsonrpc": "2.0", "method": "parity_removeAddress", "params": ["0x000baba1000baba2000baba3000baba4000baba5"], "id": 3}"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":3}"#; - let res = tester.io.handle_request_sync(&request); - assert_eq!(res, Some(response.into())); - - // verify empty - let request = r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params": [], "id": 4}"#; - let res = tester.io.handle_request_sync(request); - let response = r#"{"jsonrpc":"2.0","result":{},"id":4}"#; - assert_eq!(res, Some(response.into())); + let tester = setup(); + + // add an address + let request = r#"{"jsonrpc": "2.0", "method": "parity_setAccountName", "params": ["0x000baba1000baba2000baba3000baba4000baba5", "Test"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + let res = tester.io.handle_request_sync(&request); + assert_eq!(res, Some(response.into())); + + // verify it exists + let request = + r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params": [], "id": 2}"#; + let res = tester.io.handle_request_sync(request); + let response = r#"{"jsonrpc":"2.0","result":{"0x000baba1000baba2000baba3000baba4000baba5":{"meta":"{}","name":"Test"}},"id":2}"#; + assert_eq!(res, Some(response.into())); + + // remove the address + let request = r#"{"jsonrpc": "2.0", "method": "parity_removeAddress", "params": ["0x000baba1000baba2000baba3000baba4000baba5"], "id": 3}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":3}"#; + let res = tester.io.handle_request_sync(&request); + assert_eq!(res, Some(response.into())); + + // verify empty + let request = + r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params": [], "id": 4}"#; + let res = tester.io.handle_request_sync(request); + let response = r#"{"jsonrpc":"2.0","result":{},"id":4}"#; + assert_eq!(res, Some(response.into())); } #[test] fn rpc_parity_new_vault() { - let tempdir = TempDir::new("").unwrap(); - let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap()); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_newVault", "params":["vault1", "password1"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); - assert!(tester.accounts.close_vault("vault1").is_ok()); - assert!(tester.accounts.open_vault("vault1", &"password1".into()).is_ok()); + let tempdir = TempDir::new("").unwrap(); + let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap()); + + let request = r#"{"jsonrpc": "2.0", "method": "parity_newVault", "params":["vault1", "password1"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); + assert!(tester.accounts.close_vault("vault1").is_ok()); + assert!(tester + .accounts + .open_vault("vault1", &"password1".into()) + .is_ok()); } #[test] fn rpc_parity_open_vault() { - let tempdir = TempDir::new("").unwrap(); - let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap()); - - assert!(tester.accounts.create_vault("vault1", &"password1".into()).is_ok()); - assert!(tester.accounts.close_vault("vault1").is_ok()); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_openVault", "params":["vault1", "password1"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + let tempdir = TempDir::new("").unwrap(); + let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap()); + + assert!(tester + .accounts + .create_vault("vault1", &"password1".into()) + .is_ok()); + assert!(tester.accounts.close_vault("vault1").is_ok()); + + let request = r#"{"jsonrpc": "2.0", "method": "parity_openVault", "params":["vault1", "password1"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_parity_close_vault() { - let tempdir = TempDir::new("").unwrap(); - let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap()); - - assert!(tester.accounts.create_vault("vault1", &"password1".into()).is_ok()); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_closeVault", "params":["vault1"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + let tempdir = TempDir::new("").unwrap(); + let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap()); + + assert!(tester + .accounts + .create_vault("vault1", &"password1".into()) + .is_ok()); + + let request = + r#"{"jsonrpc": "2.0", "method": "parity_closeVault", "params":["vault1"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_parity_change_vault_password() { - let tempdir = TempDir::new("").unwrap(); - let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap()); + let tempdir = TempDir::new("").unwrap(); + let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap()); - assert!(tester.accounts.create_vault("vault1", &"password1".into()).is_ok()); + assert!(tester + .accounts + .create_vault("vault1", &"password1".into()) + .is_ok()); - let request = r#"{"jsonrpc": "2.0", "method": "parity_changeVaultPassword", "params":["vault1", "password2"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_changeVaultPassword", "params":["vault1", "password2"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_parity_change_vault() { - let tempdir = TempDir::new("").unwrap(); - let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap()); - - let (address, _) = tester.accounts.new_account_and_public(&"root_password".into()).unwrap(); - assert!(tester.accounts.create_vault("vault1", &"password1".into()).is_ok()); - - let request = format!(r#"{{"jsonrpc": "2.0", "method": "parity_changeVault", "params":["0x{:x}", "vault1"], "id": 1}}"#, address); - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - - assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); + let tempdir = TempDir::new("").unwrap(); + let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap()); + + let (address, _) = tester + .accounts + .new_account_and_public(&"root_password".into()) + .unwrap(); + assert!(tester + .accounts + .create_vault("vault1", &"password1".into()) + .is_ok()); + + let request = format!( + r#"{{"jsonrpc": "2.0", "method": "parity_changeVault", "params":["0x{:x}", "vault1"], "id": 1}}"#, + address + ); + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.to_owned()) + ); } #[test] fn rpc_parity_vault_adds_vault_field_to_acount_meta() { - let tempdir = TempDir::new("").unwrap(); - let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap()); - - let (address1, _) = tester.accounts.new_account_and_public(&"root_password1".into()).unwrap(); - let uuid1 = tester.accounts.account_meta(address1.clone()).unwrap().uuid.unwrap(); - assert!(tester.accounts.create_vault("vault1", &"password1".into()).is_ok()); - assert!(tester.accounts.change_vault(address1, "vault1").is_ok()); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params":[], "id": 1}"#; - let response = format!(r#"{{"jsonrpc":"2.0","result":{{"0x{:x}":{{"meta":"{{\"vault\":\"vault1\"}}","name":"","uuid":"{}"}}}},"id":1}}"#, address1, uuid1); - - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); - - // and then - assert!(tester.accounts.change_vault(address1, "").is_ok()); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params":[], "id": 1}"#; - let response = format!(r#"{{"jsonrpc":"2.0","result":{{"0x{:x}":{{"meta":"{{}}","name":"","uuid":"{}"}}}},"id":1}}"#, address1, uuid1); - - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + let tempdir = TempDir::new("").unwrap(); + let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap()); + + let (address1, _) = tester + .accounts + .new_account_and_public(&"root_password1".into()) + .unwrap(); + let uuid1 = tester + .accounts + .account_meta(address1.clone()) + .unwrap() + .uuid + .unwrap(); + assert!(tester + .accounts + .create_vault("vault1", &"password1".into()) + .is_ok()); + assert!(tester.accounts.change_vault(address1, "vault1").is_ok()); + + let request = r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params":[], "id": 1}"#; + let response = format!( + r#"{{"jsonrpc":"2.0","result":{{"0x{:x}":{{"meta":"{{\"vault\":\"vault1\"}}","name":"","uuid":"{}"}}}},"id":1}}"#, + address1, uuid1 + ); + + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); + + // and then + assert!(tester.accounts.change_vault(address1, "").is_ok()); + + let request = r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params":[], "id": 1}"#; + let response = format!( + r#"{{"jsonrpc":"2.0","result":{{"0x{:x}":{{"meta":"{{}}","name":"","uuid":"{}"}}}},"id":1}}"#, + address1, uuid1 + ); + + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_parity_list_vaults() { - let tempdir = TempDir::new("").unwrap(); - let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap()); - - assert!(tester.accounts.create_vault("vault1", &"password1".into()).is_ok()); - assert!(tester.accounts.create_vault("vault2", &"password2".into()).is_ok()); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_listVaults", "params":[], "id": 1}"#; - let response1 = r#"{"jsonrpc":"2.0","result":["vault1","vault2"],"id":1}"#; - let response2 = r#"{"jsonrpc":"2.0","result":["vault2","vault1"],"id":1}"#; - - let actual_response = tester.io.handle_request_sync(request); - assert!(actual_response == Some(response1.to_owned()) - || actual_response == Some(response2.to_owned())); + let tempdir = TempDir::new("").unwrap(); + let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap()); + + assert!(tester + .accounts + .create_vault("vault1", &"password1".into()) + .is_ok()); + assert!(tester + .accounts + .create_vault("vault2", &"password2".into()) + .is_ok()); + + let request = r#"{"jsonrpc": "2.0", "method": "parity_listVaults", "params":[], "id": 1}"#; + let response1 = r#"{"jsonrpc":"2.0","result":["vault1","vault2"],"id":1}"#; + let response2 = r#"{"jsonrpc":"2.0","result":["vault2","vault1"],"id":1}"#; + + let actual_response = tester.io.handle_request_sync(request); + assert!( + actual_response == Some(response1.to_owned()) + || actual_response == Some(response2.to_owned()) + ); } #[test] fn rpc_parity_list_opened_vaults() { - let tempdir = TempDir::new("").unwrap(); - let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap()); - - assert!(tester.accounts.create_vault("vault1", &"password1".into()).is_ok()); - assert!(tester.accounts.create_vault("vault2", &"password2".into()).is_ok()); - assert!(tester.accounts.create_vault("vault3", &"password3".into()).is_ok()); - assert!(tester.accounts.close_vault("vault2").is_ok()); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_listOpenedVaults", "params":[], "id": 1}"#; - let response1 = r#"{"jsonrpc":"2.0","result":["vault1","vault3"],"id":1}"#; - let response2 = r#"{"jsonrpc":"2.0","result":["vault3","vault1"],"id":1}"#; - - let actual_response = tester.io.handle_request_sync(request); - assert!(actual_response == Some(response1.to_owned()) - || actual_response == Some(response2.to_owned())); + let tempdir = TempDir::new("").unwrap(); + let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap()); + + assert!(tester + .accounts + .create_vault("vault1", &"password1".into()) + .is_ok()); + assert!(tester + .accounts + .create_vault("vault2", &"password2".into()) + .is_ok()); + assert!(tester + .accounts + .create_vault("vault3", &"password3".into()) + .is_ok()); + assert!(tester.accounts.close_vault("vault2").is_ok()); + + let request = + r#"{"jsonrpc": "2.0", "method": "parity_listOpenedVaults", "params":[], "id": 1}"#; + let response1 = r#"{"jsonrpc":"2.0","result":["vault1","vault3"],"id":1}"#; + let response2 = r#"{"jsonrpc":"2.0","result":["vault3","vault1"],"id":1}"#; + + let actual_response = tester.io.handle_request_sync(request); + assert!( + actual_response == Some(response1.to_owned()) + || actual_response == Some(response2.to_owned()) + ); } #[test] fn rpc_parity_get_set_vault_meta() { - let tempdir = TempDir::new("").unwrap(); - let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap()); - - assert!(tester.accounts.create_vault("vault1", &"password1".into()).is_ok()); - - // when no meta set - let request = r#"{"jsonrpc": "2.0", "method": "parity_getVaultMeta", "params":["vault1"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"{}","id":1}"#; - - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); - - // when meta set - assert!(tester.accounts.set_vault_meta("vault1", "vault1_meta").is_ok()); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_getVaultMeta", "params":["vault1"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"vault1_meta","id":1}"#; - - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); - - // change meta - let request = r#"{"jsonrpc": "2.0", "method": "parity_setVaultMeta", "params":["vault1", "updated_vault1_meta"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); - - // query changed meta - let request = r#"{"jsonrpc": "2.0", "method": "parity_getVaultMeta", "params":["vault1"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"updated_vault1_meta","id":1}"#; - - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + let tempdir = TempDir::new("").unwrap(); + let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap()); + + assert!(tester + .accounts + .create_vault("vault1", &"password1".into()) + .is_ok()); + + // when no meta set + let request = + r#"{"jsonrpc": "2.0", "method": "parity_getVaultMeta", "params":["vault1"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"{}","id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); + + // when meta set + assert!(tester + .accounts + .set_vault_meta("vault1", "vault1_meta") + .is_ok()); + + let request = + r#"{"jsonrpc": "2.0", "method": "parity_getVaultMeta", "params":["vault1"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"vault1_meta","id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); + + // change meta + let request = r#"{"jsonrpc": "2.0", "method": "parity_setVaultMeta", "params":["vault1", "updated_vault1_meta"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); + + // query changed meta + let request = + r#"{"jsonrpc": "2.0", "method": "parity_getVaultMeta", "params":["vault1"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"updated_vault1_meta","id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } // name: parity_deriveAddressHash // example: {"jsonrpc": "2.0", "method": "parity_deriveAddressHash", "params": ["0xc171033d5cbff7175f29dfd3a63dda3d6f8f385e", "password1", { "type": "soft", "hash": "0x0c0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0c0c" }, true ], "id": 3} #[test] fn derive_key_hash() { - let tester = setup(); - let hash = tester.accounts - .insert_account( - "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a".parse().unwrap(), - &"password1".into()) - .expect("account should be inserted ok"); - - assert_eq!(hash, "c171033d5cbff7175f29dfd3a63dda3d6f8f385e".parse().unwrap()); - - // derive by hash - let request = r#"{"jsonrpc": "2.0", "method": "parity_deriveAddressHash", "params": ["0xc171033d5cbff7175f29dfd3a63dda3d6f8f385e", "password1", { "type": "soft", "hash": "0x0c0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0c0c" }, true ], "id": 3}"#; - let response = r#"{"jsonrpc":"2.0","result":"0xf28c28fcddf4a9b8f474237278d3647f9c0d1b3c","id":3}"#; - let res = tester.io.handle_request_sync(&request); - assert_eq!(res, Some(response.into())); + let tester = setup(); + let hash = tester + .accounts + .insert_account( + "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a" + .parse() + .unwrap(), + &"password1".into(), + ) + .expect("account should be inserted ok"); + + assert_eq!( + hash, + "c171033d5cbff7175f29dfd3a63dda3d6f8f385e".parse().unwrap() + ); + + // derive by hash + let request = r#"{"jsonrpc": "2.0", "method": "parity_deriveAddressHash", "params": ["0xc171033d5cbff7175f29dfd3a63dda3d6f8f385e", "password1", { "type": "soft", "hash": "0x0c0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0c0c" }, true ], "id": 3}"#; + let response = + r#"{"jsonrpc":"2.0","result":"0xf28c28fcddf4a9b8f474237278d3647f9c0d1b3c","id":3}"#; + let res = tester.io.handle_request_sync(&request); + assert_eq!(res, Some(response.into())); } // name: parity_deriveAddressIndex // example: {"jsonrpc": "2.0", "method": "parity_deriveAddressIndex", "params": ["0xc171033d5cbff7175f29dfd3a63dda3d6f8f385e", "password1", [{ "type": "soft", "index": 0 }, { "type": "soft", "index": 1 }], false ], "id": 3} #[test] fn derive_key_index() { - let tester = setup(); - let hash = tester.accounts - .insert_account( - "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a".parse().unwrap(), - &"password1".into()) - .expect("account should be inserted ok"); - - assert_eq!(hash, "c171033d5cbff7175f29dfd3a63dda3d6f8f385e".parse().unwrap()); - - // derive by hash - let request = r#"{"jsonrpc": "2.0", "method": "parity_deriveAddressIndex", "params": ["0xc171033d5cbff7175f29dfd3a63dda3d6f8f385e", "password1", [{ "type": "soft", "index": 0 }, { "type": "soft", "index": 1 }], false ], "id": 3}"#; - let response = r#"{"jsonrpc":"2.0","result":"0xcc548e0bb2efe792a920ae0fbf583b13919f274f","id":3}"#; - let res = tester.io.handle_request_sync(&request); - assert_eq!(res, Some(response.into())); + let tester = setup(); + let hash = tester + .accounts + .insert_account( + "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a" + .parse() + .unwrap(), + &"password1".into(), + ) + .expect("account should be inserted ok"); + + assert_eq!( + hash, + "c171033d5cbff7175f29dfd3a63dda3d6f8f385e".parse().unwrap() + ); + + // derive by hash + let request = r#"{"jsonrpc": "2.0", "method": "parity_deriveAddressIndex", "params": ["0xc171033d5cbff7175f29dfd3a63dda3d6f8f385e", "password1", [{ "type": "soft", "index": 0 }, { "type": "soft", "index": 1 }], false ], "id": 3}"#; + let response = + r#"{"jsonrpc":"2.0","result":"0xcc548e0bb2efe792a920ae0fbf583b13919f274f","id":3}"#; + let res = tester.io.handle_request_sync(&request); + assert_eq!(res, Some(response.into())); } #[test] fn should_export_account() { - // given - let tester = setup(); - let wallet = r#"{"id":"6a186c80-7797-cff2-bc2e-7c1d6a6cc76e","version":3,"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"a1c6ff99070f8032ca1c4e8add006373"},"ciphertext":"df27e3db64aa18d984b6439443f73660643c2d119a6f0fa2fa9a6456fc802d75","kdf":"pbkdf2","kdfparams":{"c":10240,"dklen":32,"prf":"hmac-sha256","salt":"ddc325335cda5567a1719313e73b4842511f3e4a837c9658eeb78e51ebe8c815"},"mac":"3dc888ae79cbb226ff9c455669f6cf2d79be72120f2298f6cb0d444fddc0aa3d"},"address":"0042e5d2a662eeaca8a7e828c174f98f35d8925b","name":"parity-export-test","meta":"{\"passwordHint\":\"parity-export-test\",\"timestamp\":1490017814987}"}"#; - tester.accounts.import_wallet(wallet.as_bytes(), &"parity-export-test".into(), false).unwrap(); - let accounts = tester.accounts.accounts().unwrap(); - assert_eq!(accounts.len(), 1); - - // invalid password - let request = r#"{"jsonrpc":"2.0","method":"parity_exportAccount","params":["0x0042e5d2a662eeaca8a7e828c174f98f35d8925b","123"],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32023,"message":"Could not export account.","data":"InvalidPassword"},"id":1}"#; - let res = tester.io.handle_request_sync(&request); - assert_eq!(res, Some(response.into())); - - // correct password - let request = r#"{"jsonrpc":"2.0","method":"parity_exportAccount","params":["0x0042e5d2a662eeaca8a7e828c174f98f35d8925b","parity-export-test"],"id":1}"#; - - let response = r#"{"jsonrpc":"2.0","result":{"address":"0042e5d2a662eeaca8a7e828c174f98f35d8925b","crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"a1c6ff99070f8032ca1c4e8add006373"},"ciphertext":"df27e3db64aa18d984b6439443f73660643c2d119a6f0fa2fa9a6456fc802d75","kdf":"pbkdf2","kdfparams":{"c":10240,"dklen":32,"prf":"hmac-sha256","salt":"ddc325335cda5567a1719313e73b4842511f3e4a837c9658eeb78e51ebe8c815"},"mac":"3dc888ae79cbb226ff9c455669f6cf2d79be72120f2298f6cb0d444fddc0aa3d"},"id":"6a186c80-7797-cff2-bc2e-7c1d6a6cc76e","meta":"{\"passwordHint\":\"parity-export-test\",\"timestamp\":1490017814987}","name":"parity-export-test","version":3},"id":1}"#; - let result = tester.io.handle_request_sync(&request); - - println!("Result: {:?}", result); - println!("Response: {:?}", response); - assert_eq!(result, Some(response.into())); + // given + let tester = setup(); + let wallet = r#"{"id":"6a186c80-7797-cff2-bc2e-7c1d6a6cc76e","version":3,"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"a1c6ff99070f8032ca1c4e8add006373"},"ciphertext":"df27e3db64aa18d984b6439443f73660643c2d119a6f0fa2fa9a6456fc802d75","kdf":"pbkdf2","kdfparams":{"c":10240,"dklen":32,"prf":"hmac-sha256","salt":"ddc325335cda5567a1719313e73b4842511f3e4a837c9658eeb78e51ebe8c815"},"mac":"3dc888ae79cbb226ff9c455669f6cf2d79be72120f2298f6cb0d444fddc0aa3d"},"address":"0042e5d2a662eeaca8a7e828c174f98f35d8925b","name":"parity-export-test","meta":"{\"passwordHint\":\"parity-export-test\",\"timestamp\":1490017814987}"}"#; + tester + .accounts + .import_wallet(wallet.as_bytes(), &"parity-export-test".into(), false) + .unwrap(); + let accounts = tester.accounts.accounts().unwrap(); + assert_eq!(accounts.len(), 1); + + // invalid password + let request = r#"{"jsonrpc":"2.0","method":"parity_exportAccount","params":["0x0042e5d2a662eeaca8a7e828c174f98f35d8925b","123"],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32023,"message":"Could not export account.","data":"InvalidPassword"},"id":1}"#; + let res = tester.io.handle_request_sync(&request); + assert_eq!(res, Some(response.into())); + + // correct password + let request = r#"{"jsonrpc":"2.0","method":"parity_exportAccount","params":["0x0042e5d2a662eeaca8a7e828c174f98f35d8925b","parity-export-test"],"id":1}"#; + + let response = r#"{"jsonrpc":"2.0","result":{"address":"0042e5d2a662eeaca8a7e828c174f98f35d8925b","crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"a1c6ff99070f8032ca1c4e8add006373"},"ciphertext":"df27e3db64aa18d984b6439443f73660643c2d119a6f0fa2fa9a6456fc802d75","kdf":"pbkdf2","kdfparams":{"c":10240,"dklen":32,"prf":"hmac-sha256","salt":"ddc325335cda5567a1719313e73b4842511f3e4a837c9658eeb78e51ebe8c815"},"mac":"3dc888ae79cbb226ff9c455669f6cf2d79be72120f2298f6cb0d444fddc0aa3d"},"id":"6a186c80-7797-cff2-bc2e-7c1d6a6cc76e","meta":"{\"passwordHint\":\"parity-export-test\",\"timestamp\":1490017814987}","name":"parity-export-test","version":3},"id":1}"#; + let result = tester.io.handle_request_sync(&request); + + println!("Result: {:?}", result); + println!("Response: {:?}", response); + assert_eq!(result, Some(response.into())); } #[test] fn should_import_wallet() { - let tester = setup(); + let tester = setup(); - let id = "6a186c80-7797-cff2-bc2e-7c1d6a6cc76e"; - let request = r#"{"jsonrpc":"2.0","method":"parity_newAccountFromWallet","params":["{\"id\":\"\",\"version\":3,\"crypto\":{\"cipher\":\"aes-128-ctr\",\"cipherparams\":{\"iv\":\"478736fb55872c1baf01b27b1998c90b\"},\"ciphertext\":\"fe5a63cc0055d7b0b3b57886f930ad9b63f48950d1348145d95996c41e05f4e0\",\"kdf\":\"pbkdf2\",\"kdfparams\":{\"c\":10240,\"dklen\":32,\"prf\":\"hmac-sha256\",\"salt\":\"658436d6738a19731149a98744e5cf02c8d5aa1f8e80c1a43cc9351c70a984e4\"},\"mac\":\"c7384b26ecf25539d942030230062af9b69de5766cbcc4690bffce1536644631\"},\"address\":\"00bac56a8a27232baa044c03f43bf3648c961735\",\"name\":\"hello world\",\"meta\":\"{}\"}", "himom"],"id":1}"#; - let request = request.replace("", id); - let response = r#"{"jsonrpc":"2.0","result":"0x00bac56a8a27232baa044c03f43bf3648c961735","id":1}"#; + let id = "6a186c80-7797-cff2-bc2e-7c1d6a6cc76e"; + let request = r#"{"jsonrpc":"2.0","method":"parity_newAccountFromWallet","params":["{\"id\":\"\",\"version\":3,\"crypto\":{\"cipher\":\"aes-128-ctr\",\"cipherparams\":{\"iv\":\"478736fb55872c1baf01b27b1998c90b\"},\"ciphertext\":\"fe5a63cc0055d7b0b3b57886f930ad9b63f48950d1348145d95996c41e05f4e0\",\"kdf\":\"pbkdf2\",\"kdfparams\":{\"c\":10240,\"dklen\":32,\"prf\":\"hmac-sha256\",\"salt\":\"658436d6738a19731149a98744e5cf02c8d5aa1f8e80c1a43cc9351c70a984e4\"},\"mac\":\"c7384b26ecf25539d942030230062af9b69de5766cbcc4690bffce1536644631\"},\"address\":\"00bac56a8a27232baa044c03f43bf3648c961735\",\"name\":\"hello world\",\"meta\":\"{}\"}", "himom"],"id":1}"#; + let request = request.replace("", id); + let response = + r#"{"jsonrpc":"2.0","result":"0x00bac56a8a27232baa044c03f43bf3648c961735","id":1}"#; - let res = tester.io.handle_request_sync(&request).unwrap(); + let res = tester.io.handle_request_sync(&request).unwrap(); - assert_eq!(res, response); + assert_eq!(res, response); - let account_meta = tester.accounts.account_meta("0x00bac56a8a27232baa044c03f43bf3648c961735".into()).unwrap(); - let account_uuid: String = account_meta.uuid.unwrap().into(); + let account_meta = tester + .accounts + .account_meta("0x00bac56a8a27232baa044c03f43bf3648c961735".into()) + .unwrap(); + let account_uuid: String = account_meta.uuid.unwrap().into(); - // the RPC should import the account with a new id - assert!(account_uuid != id); + // the RPC should import the account with a new id + assert!(account_uuid != id); } #[test] fn should_sign_message() { - let tester = setup(); - let hash = tester.accounts - .insert_account( - "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a".parse().unwrap(), - &"password1".into()) - .expect("account should be inserted ok"); - - assert_eq!(hash, "c171033d5cbff7175f29dfd3a63dda3d6f8f385e".parse().unwrap()); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_signMessage", "params": ["0xc171033d5cbff7175f29dfd3a63dda3d6f8f385e", "password1", "0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a"], "id": 3}"#; - let response = r#"{"jsonrpc":"2.0","result":"0x1d9e33a8cf8bfc089a172bca01da462f9e359c6cb1b0f29398bc884e4d18df4f78588aee4fb5cc067ca62d2abab995e0bba29527be6ac98105b0320020a2efaf00","id":3}"#; - let res = tester.io.handle_request_sync(&request); - assert_eq!(res, Some(response.into())); + let tester = setup(); + let hash = tester + .accounts + .insert_account( + "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a" + .parse() + .unwrap(), + &"password1".into(), + ) + .expect("account should be inserted ok"); + + assert_eq!( + hash, + "c171033d5cbff7175f29dfd3a63dda3d6f8f385e".parse().unwrap() + ); + + let request = r#"{"jsonrpc": "2.0", "method": "parity_signMessage", "params": ["0xc171033d5cbff7175f29dfd3a63dda3d6f8f385e", "password1", "0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a"], "id": 3}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x1d9e33a8cf8bfc089a172bca01da462f9e359c6cb1b0f29398bc884e4d18df4f78588aee4fb5cc067ca62d2abab995e0bba29527be6ac98105b0320020a2efaf00","id":3}"#; + let res = tester.io.handle_request_sync(&request); + assert_eq!(res, Some(response.into())); } diff --git a/rpc/src/v1/tests/mocked/parity_set.rs b/rpc/src/v1/tests/mocked/parity_set.rs index 25c13fb1cb6..18a3f930ce0 100644 --- a/rpc/src/v1/tests/mocked/parity_set.rs +++ b/rpc/src/v1/tests/mocked/parity_set.rs @@ -1,271 +1,243 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::sync::Arc; -use std::str::FromStr; +use ethereum_types::{Address, U256}; use rustc_hex::FromHex; -use ethereum_types::{U256, Address}; +use std::{str::FromStr, sync::Arc}; -use ethcore::miner::MinerService; -use ethcore::client::TestBlockChainClient; +use ethcore::{client::TestBlockChainClient, miner::MinerService}; use sync::ManageNetwork; -use jsonrpc_core::IoHandler; -use v1::{ParitySet, ParitySetClient}; -use v1::tests::helpers::{TestMinerService, TestUpdater}; use super::manage_network::TestManageNetwork; +use jsonrpc_core::IoHandler; +use v1::{tests::helpers::TestMinerService, ParitySet, ParitySetClient}; use fake_fetch::FakeFetch; fn miner_service() -> Arc { - Arc::new(TestMinerService::default()) + Arc::new(TestMinerService::default()) } fn client_service() -> Arc { - Arc::new(TestBlockChainClient::default()) + Arc::new(TestBlockChainClient::default()) } fn network_service() -> Arc { - Arc::new(TestManageNetwork) -} - -fn updater_service() -> Arc { - Arc::new(TestUpdater::default()) + Arc::new(TestManageNetwork) } -pub type TestParitySetClient = ParitySetClient>; +pub type TestParitySetClient = + ParitySetClient>; fn parity_set_client( - client: &Arc, - miner: &Arc, - updater: &Arc, - net: &Arc, + client: &Arc, + miner: &Arc, + net: &Arc, ) -> TestParitySetClient { - ParitySetClient::new( - client, - miner, - updater, - &(net.clone() as Arc), - FakeFetch::new(Some(1)), - ) -} - -#[test] -fn rpc_parity_execute_upgrade() { - let miner = miner_service(); - let client = client_service(); - let network = network_service(); - let updater = updater_service(); - let mut io = IoHandler::new(); - io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_executeUpgrade", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_executeUpgrade", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":false,"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); -} - -#[test] -fn rpc_parity_upgrade_ready() { - let miner = miner_service(); - let client = client_service(); - let network = network_service(); - let updater = updater_service(); - let mut io = IoHandler::new(); - io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_upgradeReady", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"binary":"0x00000000000000000000000000000000000000000000000000000000000005e6","fork":15100,"is_critical":true,"version":{"hash":"0x0000000000000000000000000000000000000097","track":"beta","version":{"major":1,"minor":5,"patch":1}}},"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); - - updater.set_updated(true); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_upgradeReady", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + ParitySetClient::new( + client, + miner, + &(net.clone() as Arc), + FakeFetch::new(Some(1)), + ) } #[test] fn rpc_parity_set_min_gas_price() { - let miner = miner_service(); - let client = client_service(); - let network = network_service(); - let updater = updater_service(); + let miner = miner_service(); + let client = client_service(); + let network = network_service(); - let mut io = IoHandler::new(); - io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); + let mut io = IoHandler::new(); + io.extend_with(parity_set_client(&client, &miner, &network).to_delegate()); - let request = r#"{"jsonrpc": "2.0", "method": "parity_setMinGasPrice", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_setMinGasPrice", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_set_min_gas_price_with_automated_calibration_enabled() { - let miner = miner_service(); - *miner.min_gas_price.write() = None; + let miner = miner_service(); + *miner.min_gas_price.write() = None; - let client = client_service(); - let network = network_service(); - let updater = updater_service(); + let client = client_service(); + let network = network_service(); - let mut io = IoHandler::new(); - io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); + let mut io = IoHandler::new(); + io.extend_with(parity_set_client(&client, &miner, &network).to_delegate()); - let request = r#"{"jsonrpc": "2.0", "method": "parity_setMinGasPrice", "params":["0xdeadbeef"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Can't update fixed gas price while automatic gas calibration is enabled."},"id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_setMinGasPrice", "params":["0xdeadbeef"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Can't update fixed gas price while automatic gas calibration is enabled."},"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_set_gas_floor_target() { - let miner = miner_service(); - let client = client_service(); - let network = network_service(); - let updater = updater_service(); + let miner = miner_service(); + let client = client_service(); + let network = network_service(); - let mut io = IoHandler::new(); - io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); + let mut io = IoHandler::new(); + io.extend_with(parity_set_client(&client, &miner, &network).to_delegate()); - let request = r#"{"jsonrpc": "2.0", "method": "parity_setGasFloorTarget", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_setGasFloorTarget", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); - assert_eq!(miner.authoring_params().gas_range_target.0, U256::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap()); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + miner.authoring_params().gas_range_target.0, + U256::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap() + ); } #[test] fn rpc_parity_set_extra_data() { - let miner = miner_service(); - let client = client_service(); - let network = network_service(); - let updater = updater_service(); + let miner = miner_service(); + let client = client_service(); + let network = network_service(); - let mut io = IoHandler::new(); - io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); + let mut io = IoHandler::new(); + io.extend_with(parity_set_client(&client, &miner, &network).to_delegate()); - let request = r#"{"jsonrpc": "2.0", "method": "parity_setExtraData", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_setExtraData", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); - assert_eq!(miner.authoring_params().extra_data, "cd1722f3947def4cf144679da39c4c32bdc35681".from_hex().unwrap()); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + miner.authoring_params().extra_data, + "cd1722f3947def4cf144679da39c4c32bdc35681" + .from_hex() + .unwrap() + ); } #[test] fn rpc_parity_set_author() { - let miner = miner_service(); - let client = client_service(); - let network = network_service(); - let updater = updater_service(); - let mut io = IoHandler::new(); - io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_setAuthor", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); - assert_eq!(miner.authoring_params().author, Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap()); + let miner = miner_service(); + let client = client_service(); + let network = network_service(); + + let mut io = IoHandler::new(); + io.extend_with(parity_set_client(&client, &miner, &network).to_delegate()); + + let request = r#"{"jsonrpc": "2.0", "method": "parity_setAuthor", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + miner.authoring_params().author, + Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap() + ); } #[test] fn rpc_parity_set_transactions_limit() { - let miner = miner_service(); - let client = client_service(); - let network = network_service(); - let updater = updater_service(); - let mut io = IoHandler::new(); - io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); + let miner = miner_service(); + let client = client_service(); + let network = network_service(); + + let mut io = IoHandler::new(); + io.extend_with(parity_set_client(&client, &miner, &network).to_delegate()); - let request = r#"{"jsonrpc": "2.0", "method": "parity_setTransactionsLimit", "params":[10240240], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":false,"id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_setTransactionsLimit", "params":[10240240], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":false,"id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_set_hash_content() { - let miner = miner_service(); - let client = client_service(); - let network = network_service(); - let updater = updater_service(); - let mut io = IoHandler::new(); - io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); + let miner = miner_service(); + let client = client_service(); + let network = network_service(); - let request = r#"{"jsonrpc": "2.0", "method": "parity_hashContent", "params":["https://parity.io/assets/images/ethcore-black-horizontal.png"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"0x2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e","id":1}"#; + let mut io = IoHandler::new(); + io.extend_with(parity_set_client(&client, &miner, &network).to_delegate()); - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + let request = r#"{"jsonrpc": "2.0", "method": "parity_hashContent", "params":["https://parity.io/assets/images/ethcore-black-horizontal.png"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e","id":1}"#; + + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_parity_remove_transaction() { - use types::transaction::{Transaction, Action}; - - let miner = miner_service(); - let client = client_service(); - let network = network_service(); - let updater = updater_service(); - let mut io = IoHandler::new(); - io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); - - let tx = Transaction { - nonce: 1.into(), - gas_price: 0x9184e72a000u64.into(), - gas: 0x76c0.into(), - action: Action::Call(5.into()), - value: 0x9184e72au64.into(), - data: vec![] - }; - let signed = tx.fake_sign(2.into()); - let hash = signed.hash(); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_removeTransaction", "params":[""#.to_owned() + &format!("0x{:x}", hash) + r#""], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"chainId":null,"condition":null,"creates":null,"from":"0x0000000000000000000000000000000000000002","gas":"0x76c0","gasPrice":"0x9184e72a000","hash":"0xa2e0da8a8064e0b9f93e95a53c2db6d01280efb8ac72a708d25487e67dd0f8fc","input":"0x","nonce":"0x1","publicKey":null,"r":"0x1","raw":"0xe9018609184e72a0008276c0940000000000000000000000000000000000000005849184e72a80800101","s":"0x1","standardV":"0x4","to":"0x0000000000000000000000000000000000000005","transactionIndex":null,"v":"0x0","value":"0x9184e72a"},"id":1}"#; - - miner.pending_transactions.lock().insert(hash, signed); - assert_eq!(io.handle_request_sync(&request), Some(response.to_owned())); + use types::transaction::{Action, Transaction}; + + let miner = miner_service(); + let client = client_service(); + let network = network_service(); + + let mut io = IoHandler::new(); + io.extend_with(parity_set_client(&client, &miner, &network).to_delegate()); + + let tx = Transaction { + nonce: 1.into(), + gas_price: 0x9184e72a000u64.into(), + gas: 0x76c0.into(), + action: Action::Call(5.into()), + value: 0x9184e72au64.into(), + data: vec![], + }; + let signed = tx.fake_sign(2.into()); + let hash = signed.hash(); + + let request = r#"{"jsonrpc": "2.0", "method": "parity_removeTransaction", "params":[""# + .to_owned() + + &format!("0x{:x}", hash) + + r#""], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"chainId":null,"condition":null,"creates":null,"from":"0x0000000000000000000000000000000000000002","gas":"0x76c0","gasPrice":"0x9184e72a000","hash":"0xa2e0da8a8064e0b9f93e95a53c2db6d01280efb8ac72a708d25487e67dd0f8fc","input":"0x","nonce":"0x1","publicKey":null,"r":"0x1","raw":"0xe9018609184e72a0008276c0940000000000000000000000000000000000000005849184e72a80800101","s":"0x1","standardV":"0x4","to":"0x0000000000000000000000000000000000000005","transactionIndex":null,"v":"0x0","value":"0x9184e72a"},"id":1}"#; + + miner.pending_transactions.lock().insert(hash, signed); + assert_eq!(io.handle_request_sync(&request), Some(response.to_owned())); } #[test] fn rpc_parity_set_engine_signer() { - use accounts::AccountProvider; - use bytes::ToPretty; - use v1::impls::ParitySetAccountsClient; - use v1::traits::ParitySetAccounts; - - let account_provider = Arc::new(AccountProvider::transient_provider()); - account_provider.insert_account(::hash::keccak("cow").into(), &"password".into()).unwrap(); - - let miner = miner_service(); - let mut io = IoHandler::new(); - io.extend_with( - ParitySetAccountsClient::new(&account_provider, &miner).to_delegate() - ); - - let request = r#"{"jsonrpc": "2.0", "method": "parity_setEngineSigner", "params":["0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826", "password"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); - assert_eq!(miner.authoring_params().author, Address::from_str("cd2a3d9f938e13cd947ec05abc7fe734df8dd826").unwrap()); - let signature = miner.signer.read().as_ref().unwrap().sign(::hash::keccak("x")).unwrap().to_vec(); - assert_eq!(&format!("{}", signature.pretty()), "6f46069ded2154af6e806706e4f7f6fd310ac45f3c6dccb85f11c0059ee20a09245df0a0008bb84a10882b1298284bc93058e7bc5938ea728e77620061687a6401"); + use accounts::AccountProvider; + use bytes::ToPretty; + use v1::{impls::ParitySetAccountsClient, traits::ParitySetAccounts}; + + let account_provider = Arc::new(AccountProvider::transient_provider()); + account_provider + .insert_account(::hash::keccak("cow").into(), &"password".into()) + .unwrap(); + + let miner = miner_service(); + let mut io = IoHandler::new(); + io.extend_with(ParitySetAccountsClient::new(&account_provider, &miner).to_delegate()); + + let request = r#"{"jsonrpc": "2.0", "method": "parity_setEngineSigner", "params":["0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826", "password"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + miner.authoring_params().author, + Address::from_str("cd2a3d9f938e13cd947ec05abc7fe734df8dd826").unwrap() + ); + let signature = miner + .signer + .read() + .as_ref() + .unwrap() + .sign(::hash::keccak("x")) + .unwrap() + .to_vec(); + assert_eq!(&format!("{}", signature.pretty()), "6f46069ded2154af6e806706e4f7f6fd310ac45f3c6dccb85f11c0059ee20a09245df0a0008bb84a10882b1298284bc93058e7bc5938ea728e77620061687a6401"); } - diff --git a/rpc/src/v1/tests/mocked/personal.rs b/rpc/src/v1/tests/mocked/personal.rs index a2d6b87ce26..f940351e0bf 100644 --- a/rpc/src/v1/tests/mocked/personal.rs +++ b/rpc/src/v1/tests/mocked/personal.rs @@ -1,129 +1,144 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::sync::Arc; -use std::str::FromStr; +use std::{str::FromStr, sync::Arc}; -use bytes::ToPretty; use accounts::AccountProvider; -use ethereum_types::{Address, H520, U256}; +use bytes::ToPretty; use ethcore::client::TestBlockChainClient; +use ethereum_types::{Address, H520, U256}; +use hash::keccak; use jsonrpc_core::IoHandler; +use parity_runtime::Runtime; use parking_lot::Mutex; use types::transaction::{Action, Transaction}; -use parity_runtime::Runtime; -use hash::keccak; -use v1::{PersonalClient, Personal, Metadata}; -use v1::helpers::{nonce, eip191}; -use v1::helpers::dispatch::{eth_data_hash, FullDispatcher}; -use v1::tests::helpers::TestMinerService; -use v1::types::{EIP191Version, PresignedTransaction}; +use ethkey::Secret; use rustc_hex::ToHex; use serde_json::to_value; -use ethkey::Secret; +use v1::{ + helpers::{ + dispatch::{eth_data_hash, FullDispatcher}, + eip191, nonce, + }, + tests::helpers::TestMinerService, + types::{EIP191Version, PresignedTransaction}, + Metadata, Personal, PersonalClient, +}; struct PersonalTester { - _runtime: Runtime, - accounts: Arc, - io: IoHandler, - miner: Arc, + _runtime: Runtime, + accounts: Arc, + io: IoHandler, + miner: Arc, } fn blockchain_client() -> Arc { - let client = TestBlockChainClient::new(); - Arc::new(client) + let client = TestBlockChainClient::new(); + Arc::new(client) } fn accounts_provider() -> Arc { - Arc::new(AccountProvider::transient_provider()) + Arc::new(AccountProvider::transient_provider()) } fn miner_service() -> Arc { - Arc::new(TestMinerService::default()) + Arc::new(TestMinerService::default()) } fn setup() -> PersonalTester { - setup_with(Config { - allow_experimental_rpcs: true - }) + setup_with(Config { + allow_experimental_rpcs: true, + }) } struct Config { - pub allow_experimental_rpcs: bool, + pub allow_experimental_rpcs: bool, } fn setup_with(c: Config) -> PersonalTester { - let runtime = Runtime::with_thread_count(1); - let accounts = accounts_provider(); - let client = blockchain_client(); - let miner = miner_service(); - let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor()))); - - let dispatcher = FullDispatcher::new(client, miner.clone(), reservations, 50); - let personal = PersonalClient::new(&accounts, dispatcher, false, c.allow_experimental_rpcs); - - let mut io = IoHandler::default(); - io.extend_with(personal.to_delegate()); - - let tester = PersonalTester { - _runtime: runtime, - accounts: accounts, - io: io, - miner: miner, - }; - - tester + let runtime = Runtime::with_thread_count(1); + let accounts = accounts_provider(); + let client = blockchain_client(); + let miner = miner_service(); + let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor()))); + + let dispatcher = FullDispatcher::new(client, miner.clone(), reservations, 50); + let personal = PersonalClient::new(&accounts, dispatcher, c.allow_experimental_rpcs); + + let mut io = IoHandler::default(); + io.extend_with(personal.to_delegate()); + + let tester = PersonalTester { + _runtime: runtime, + accounts: accounts, + io: io, + miner: miner, + }; + + tester } #[test] fn accounts() { - let tester = setup(); - let address = tester.accounts.new_account(&"".into()).unwrap(); - let request = r#"{"jsonrpc": "2.0", "method": "personal_listAccounts", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":[""#.to_owned() + &format!("0x{:x}", address) + r#""],"id":1}"#; - - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + let tester = setup(); + let address = tester.accounts.new_account(&"".into()).unwrap(); + let request = r#"{"jsonrpc": "2.0", "method": "personal_listAccounts", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":[""#.to_owned() + + &format!("0x{:x}", address) + + r#""],"id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn new_account() { - let tester = setup(); - let request = r#"{"jsonrpc": "2.0", "method": "personal_newAccount", "params": ["pass"], "id": 1}"#; + let tester = setup(); + let request = + r#"{"jsonrpc": "2.0", "method": "personal_newAccount", "params": ["pass"], "id": 1}"#; - let res = tester.io.handle_request_sync(request); + let res = tester.io.handle_request_sync(request); - let accounts = tester.accounts.accounts().unwrap(); - assert_eq!(accounts.len(), 1); - let address = accounts[0]; - let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"","id":1}"#; + let accounts = tester.accounts.accounts().unwrap(); + assert_eq!(accounts.len(), 1); + let address = accounts[0]; + let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + + format!("0x{:x}", address).as_ref() + + r#"","id":1}"#; - assert_eq!(res, Some(response)); + assert_eq!(res, Some(response)); } -fn invalid_password_test(method: &str) -{ - let tester = setup(); - let address = tester.accounts.new_account(&"password123".into()).unwrap(); +fn invalid_password_test(method: &str) { + let tester = setup(); + let address = tester.accounts.new_account(&"password123".into()).unwrap(); - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", - "method": ""#.to_owned() + method + r#"", + "method": ""# + .to_owned() + + method + + r#"", "params": [{ - "from": ""# + format!("0x{:x}", address).as_ref() + r#"", + "from": ""# + + format!("0x{:x}", address).as_ref() + + r#"", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "gas": "0x76c0", "gasPrice": "0x9184e72a000", @@ -132,87 +147,114 @@ fn invalid_password_test(method: &str) "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32021,"message":"Account password is invalid or account does not exist.","data":"SStore(InvalidPassword)"},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32021,"message":"Account password is invalid or account does not exist.","data":"SStore(InvalidPassword)"},"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request.as_ref()), Some(response.into())); + assert_eq!( + tester.io.handle_request_sync(request.as_ref()), + Some(response.into()) + ); } #[test] fn sign() { - let tester = setup(); - let address = tester.accounts.new_account(&"password123".into()).unwrap(); - let data = vec![5u8]; + let tester = setup(); + let address = tester.accounts.new_account(&"password123".into()).unwrap(); + let data = vec![5u8]; - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", "method": "personal_sign", "params": [ - ""#.to_owned() + format!("0x{}", data.to_hex()).as_ref() + r#"", - ""# + format!("0x{:x}", address).as_ref() + r#"", + ""# + .to_owned() + + format!("0x{}", data.to_hex()).as_ref() + + r#"", + ""# + format!("0x{:x}", address).as_ref() + + r#"", "password123" ], "id": 1 }"#; - let hash = eth_data_hash(data); - let signature = H520(tester.accounts.sign(address, Some("password123".into()), hash).unwrap().into_electrum()); - let signature = format!("{:?}", signature); - - let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &signature + r#"","id":1}"#; - - assert_eq!(tester.io.handle_request_sync(request.as_ref()), Some(response)); + let hash = eth_data_hash(data); + let signature = H520( + tester + .accounts + .sign(address, Some("password123".into()), hash) + .unwrap() + .into_electrum(), + ); + let signature = format!("{:?}", signature); + + let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &signature + r#"","id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(request.as_ref()), + Some(response) + ); } #[test] fn sign_with_invalid_password() { - let tester = setup(); - let address = tester.accounts.new_account(&"password123".into()).unwrap(); + let tester = setup(); + let address = tester.accounts.new_account(&"password123".into()).unwrap(); - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", "method": "personal_sign", "params": [ "0x0000000000000000000000000000000000000000000000000000000000000005", - ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", + ""# + .to_owned() + + format!("0x{:x}", address).as_ref() + + r#"", "" ], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32021,"message":"Account password is invalid or account does not exist.","data":"SStore(InvalidPassword)"},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32021,"message":"Account password is invalid or account does not exist.","data":"SStore(InvalidPassword)"},"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request.as_ref()), Some(response.into())); + assert_eq!( + tester.io.handle_request_sync(request.as_ref()), + Some(response.into()) + ); } #[test] fn sign_transaction_with_invalid_password() { - invalid_password_test("personal_signTransaction"); + invalid_password_test("personal_signTransaction"); } #[test] fn sign_and_send_transaction_with_invalid_password() { - invalid_password_test("personal_sendTransaction"); + invalid_password_test("personal_sendTransaction"); } #[test] fn send_transaction() { - sign_and_send_test("personal_sendTransaction"); + sign_and_send_test("personal_sendTransaction"); } #[test] fn sign_and_send_transaction() { - sign_and_send_test("personal_signAndSendTransaction"); + sign_and_send_test("personal_signAndSendTransaction"); } fn sign_and_send_test(method: &str) { - let tester = setup(); - let address = tester.accounts.new_account(&"password123".into()).unwrap(); + let tester = setup(); + let address = tester.accounts.new_account(&"password123".into()).unwrap(); - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", - "method": ""#.to_owned() + method + r#"", + "method": ""# + .to_owned() + + method + + r#"", "params": [{ - "from": ""# + format!("0x{:x}", address).as_ref() + r#"", + "from": ""# + + format!("0x{:x}", address).as_ref() + + r#"", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "gas": "0x76c0", "gasPrice": "0x9184e72a000", @@ -221,163 +263,209 @@ fn sign_and_send_test(method: &str) { "id": 1 }"#; - let t = Transaction { - nonce: U256::zero(), - gas_price: U256::from(0x9184e72a000u64), - gas: U256::from(0x76c0), - action: Action::Call(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), - value: U256::from(0x9184e72au64), - data: vec![] - }; - tester.accounts.unlock_account_temporarily(address, "password123".into()).unwrap(); - let signature = tester.accounts.sign(address, None, t.hash(None)).unwrap(); - let t = t.with_signature(signature, None); - - let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:x}", t.hash()).as_ref() + r#"","id":1}"#; - - assert_eq!(tester.io.handle_request_sync(request.as_ref()), Some(response)); - - tester.miner.increment_nonce(&address); - - let t = Transaction { - nonce: U256::one(), - gas_price: U256::from(0x9184e72a000u64), - gas: U256::from(0x76c0), - action: Action::Call(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), - value: U256::from(0x9184e72au64), - data: vec![] - }; - tester.accounts.unlock_account_temporarily(address, "password123".into()).unwrap(); - let signature = tester.accounts.sign(address, None, t.hash(None)).unwrap(); - let t = t.with_signature(signature, None); - - let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:x}", t.hash()).as_ref() + r#"","id":1}"#; - - assert_eq!(tester.io.handle_request_sync(request.as_ref()), Some(response)); + let t = Transaction { + nonce: U256::zero(), + gas_price: U256::from(0x9184e72a000u64), + gas: U256::from(0x76c0), + action: Action::Call( + Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(), + ), + value: U256::from(0x9184e72au64), + data: vec![], + }; + tester + .accounts + .unlock_account_temporarily(address, "password123".into()) + .unwrap(); + let signature = tester.accounts.sign(address, None, t.hash(None)).unwrap(); + let t = t.with_signature(signature, None); + + let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + + format!("0x{:x}", t.hash()).as_ref() + + r#"","id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(request.as_ref()), + Some(response) + ); + + tester.miner.increment_nonce(&address); + + let t = Transaction { + nonce: U256::one(), + gas_price: U256::from(0x9184e72a000u64), + gas: U256::from(0x76c0), + action: Action::Call( + Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(), + ), + value: U256::from(0x9184e72au64), + data: vec![], + }; + tester + .accounts + .unlock_account_temporarily(address, "password123".into()) + .unwrap(); + let signature = tester.accounts.sign(address, None, t.hash(None)).unwrap(); + let t = t.with_signature(signature, None); + + let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + + format!("0x{:x}", t.hash()).as_ref() + + r#"","id":1}"#; + + assert_eq!( + tester.io.handle_request_sync(request.as_ref()), + Some(response) + ); } #[test] fn ec_recover() { - let tester = setup(); - let address = tester.accounts.new_account(&"password123".into()).unwrap(); - let data = vec![5u8]; - - let hash = eth_data_hash(data.clone()); - let signature = H520(tester.accounts.sign(address, Some("password123".into()), hash).unwrap().into_electrum()); - let signature = format!("{:?}", signature); - - let request = r#"{ + let tester = setup(); + let address = tester.accounts.new_account(&"password123".into()).unwrap(); + let data = vec![5u8]; + + let hash = eth_data_hash(data.clone()); + let signature = H520( + tester + .accounts + .sign(address, Some("password123".into()), hash) + .unwrap() + .into_electrum(), + ); + let signature = format!("{:?}", signature); + + let request = r#"{ "jsonrpc": "2.0", "method": "personal_ecRecover", "params": [ - ""#.to_owned() + format!("0x{}", data.to_hex()).as_ref() + r#"", - ""# + &signature + r#"" + ""# + .to_owned() + + format!("0x{}", data.to_hex()).as_ref() + + r#"", + ""# + &signature + + r#"" ], "id": 1 }"#; - let address = format!("0x{:x}", address); - let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &address + r#"","id":1}"#; + let address = format!("0x{:x}", address); + let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &address + r#"","id":1}"#; - assert_eq!(tester.io.handle_request_sync(request.as_ref()), Some(response.into())); + assert_eq!( + tester.io.handle_request_sync(request.as_ref()), + Some(response.into()) + ); } #[test] fn ec_recover_invalid_signature() { - let tester = setup(); - let data = vec![5u8]; + let tester = setup(); + let data = vec![5u8]; - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", "method": "personal_ecRecover", "params": [ - ""#.to_owned() + format!("0x{}", data.to_hex()).as_ref() + r#"", + ""# + .to_owned() + + format!("0x{}", data.to_hex()).as_ref() + + r#"", "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32055,"message":"Encryption error.","data":"InvalidSignature"},"id":1}"#; - - assert_eq!(tester.io.handle_request_sync(request.as_ref()), Some(response.into())); -} - -#[test] -fn should_not_unlock_account_temporarily_if_allow_perm_is_disabled() { - let tester = setup(); - let address = tester.accounts.new_account(&"password123".into()).unwrap(); - - let request = r#"{ - "jsonrpc": "2.0", - "method": "personal_unlockAccount", - "params": [ - ""#.to_owned() + &format!("0x{:x}", address) + r#"", - "password123", - "0x100" - ], - "id": 1 - }"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Time-unlocking is not supported when permanent unlock is disabled.","data":"Use personal_sendTransaction or enable permanent unlocking, instead."},"id":1}"#; - assert_eq!(tester.io.handle_request_sync(&request), Some(response.into())); + let response = r#"{"jsonrpc":"2.0","error":{"code":-32055,"message":"Encryption error.","data":"InvalidSignature"},"id":1}"#; - assert!(tester.accounts.sign(address, None, Default::default()).is_err(), "Should not unlock account."); + assert_eq!( + tester.io.handle_request_sync(request.as_ref()), + Some(response.into()) + ); } #[test] fn should_unlock_account_permanently() { - let tester = setup(); - let address = tester.accounts.new_account(&"password123".into()).unwrap(); + let tester = setup(); + let address = tester.accounts.new_account(&"password123".into()).unwrap(); - let request = r#"{ + let request = r#"{ "jsonrpc": "2.0", "method": "personal_unlockAccount", "params": [ - ""#.to_owned() + &format!("0x{:x}", address) + r#"", + ""# + .to_owned() + + &format!("0x{:x}", address) + + r#"", "password123", null ], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - assert_eq!(tester.io.handle_request_sync(&request), Some(response.into())); - assert!(tester.accounts.sign(address, None, Default::default()).is_ok(), "Should unlock account."); + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.into()) + ); + assert!( + tester + .accounts + .sign(address, None, Default::default()) + .is_ok(), + "Should unlock account." + ); } #[test] fn sign_eip191_with_validator() { - let tester = setup(); - let address = tester.accounts.new_account(&"password123".into()).unwrap(); - let request = r#"{ + let tester = setup(); + let address = tester.accounts.new_account(&"password123".into()).unwrap(); + let request = r#"{ "jsonrpc": "2.0", "method": "personal_sign191", "params": [ "0x00", { - "validator": ""#.to_owned() + &format!("0x{:x}", address) + r#"", - "data": ""# + &format!("0x{:x}", keccak("hello world")) + r#"" + "validator": ""# + .to_owned() + + &format!("0x{:x}", address) + + r#"", + "data": ""# + + &format!("0x{:x}", keccak("hello world")) + + r#"" }, - ""# + &format!("0x{:x}", address) + r#"", + ""# + &format!("0x{:x}", address) + + r#"", "password123" ], "id": 1 }"#; - let with_validator = to_value(PresignedTransaction { - validator: address.into(), - data: keccak("hello world").to_vec().into() - }).unwrap(); - let result = eip191::hash_message(EIP191Version::PresignedTransaction, with_validator).unwrap(); - let result = tester.accounts.sign(address, Some("password123".into()), result).unwrap().into_electrum(); - let expected = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{}", result.to_hex()) + r#"","id":1}"#; - let response = tester.io.handle_request_sync(&request).unwrap(); - assert_eq!(response, expected) + let with_validator = to_value(PresignedTransaction { + validator: address.into(), + data: keccak("hello world").to_vec().into(), + }) + .unwrap(); + let result = eip191::hash_message(EIP191Version::PresignedTransaction, with_validator).unwrap(); + let result = tester + .accounts + .sign(address, Some("password123".into()), result) + .unwrap() + .into_electrum(); + let expected = r#"{"jsonrpc":"2.0","result":""#.to_owned() + + &format!("0x{}", result.to_hex()) + + r#"","id":1}"#; + let response = tester.io.handle_request_sync(&request).unwrap(); + assert_eq!(response, expected) } #[test] fn sign_eip191_structured_data() { - let tester = setup(); - let secret: Secret = keccak("cow").into(); - let address = tester.accounts.insert_account(secret, &"lol".into()).unwrap(); - let request = r#"{ + let tester = setup(); + let secret: Secret = keccak("cow").into(); + let address = tester + .accounts + .insert_account(secret, &"lol".into()) + .unwrap(); + let request = r#"{ "jsonrpc": "2.0", "method": "personal_sign191", "params": [ @@ -419,22 +507,28 @@ fn sign_eip191_structured_data() { ] } }, - ""#.to_owned() + &format!("0x{:x}", address) + r#"", + ""# + .to_owned() + + &format!("0x{:x}", address) + + r#"", "lol" ], "id": 1 }"#; - let expected = r#"{"jsonrpc":"2.0","result":"0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c","id":1}"#; - let response = tester.io.handle_request_sync(&request).unwrap(); - assert_eq!(response, expected) + let expected = r#"{"jsonrpc":"2.0","result":"0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c","id":1}"#; + let response = tester.io.handle_request_sync(&request).unwrap(); + assert_eq!(response, expected) } #[test] fn sign_structured_data() { - let tester = setup(); - let secret: Secret = keccak("cow").into(); - let address = tester.accounts.insert_account(secret, &"lol".into()).unwrap(); - let request = r#"{ + let tester = setup(); + let secret: Secret = keccak("cow").into(); + let address = tester + .accounts + .insert_account(secret, &"lol".into()) + .unwrap(); + let request = r#"{ "jsonrpc": "2.0", "method": "personal_signTypedData", "params": [ @@ -475,25 +569,28 @@ fn sign_structured_data() { ] } }, - ""#.to_owned() + &format!("0x{:x}", address) + r#"", + ""# + .to_owned() + + &format!("0x{:x}", address) + + r#"", "lol" ], "id": 1 }"#; - let expected = r#"{"jsonrpc":"2.0","result":"0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c","id":1}"#; - let response = tester.io.handle_request_sync(&request).unwrap(); - assert_eq!(response, expected) + let expected = r#"{"jsonrpc":"2.0","result":"0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c","id":1}"#; + let response = tester.io.handle_request_sync(&request).unwrap(); + assert_eq!(response, expected) } #[test] fn should_disable_experimental_apis() { - // given - let tester = setup_with(Config { - allow_experimental_rpcs: false, - }); + // given + let tester = setup_with(Config { + allow_experimental_rpcs: false, + }); - // when - let request = r#"{ + // when + let request = r#"{ "jsonrpc": "2.0", "method": "personal_sign191", "params": [ @@ -504,8 +601,8 @@ fn should_disable_experimental_apis() { ], "id": 1 }"#; - let r1 = tester.io.handle_request_sync(&request).unwrap(); - let request = r#"{ + let r1 = tester.io.handle_request_sync(&request).unwrap(); + let request = r#"{ "jsonrpc": "2.0", "method": "personal_signTypedData", "params": [ @@ -525,12 +622,12 @@ fn should_disable_experimental_apis() { ], "id": 1 }"#; - let r2 = tester.io.handle_request_sync(&request).unwrap(); + let r2 = tester.io.handle_request_sync(&request).unwrap(); - // then - let expected = r#"{"jsonrpc":"2.0","error":{"code":-32071,"message":"This method is not part of the official RPC API yet (EIP-191). Run with `--jsonrpc-experimental` to enable it.","data":"See EIP: https://eips.ethereum.org/EIPS/eip-191"},"id":1}"#; - assert_eq!(r1, expected); + // then + let expected = r#"{"jsonrpc":"2.0","error":{"code":-32071,"message":"This method is not part of the official RPC API yet (EIP-191). Run with `--jsonrpc-experimental` to enable it.","data":"See EIP: https://eips.ethereum.org/EIPS/eip-191"},"id":1}"#; + assert_eq!(r1, expected); - let expected = r#"{"jsonrpc":"2.0","error":{"code":-32071,"message":"This method is not part of the official RPC API yet (EIP-712). Run with `--jsonrpc-experimental` to enable it.","data":"See EIP: https://eips.ethereum.org/EIPS/eip-712"},"id":1}"#; - assert_eq!(r2, expected); + let expected = r#"{"jsonrpc":"2.0","error":{"code":-32071,"message":"This method is not part of the official RPC API yet (EIP-712). Run with `--jsonrpc-experimental` to enable it.","data":"See EIP: https://eips.ethereum.org/EIPS/eip-712"},"id":1}"#; + assert_eq!(r2, expected); } diff --git a/rpc/src/v1/tests/mocked/pubsub.rs b/rpc/src/v1/tests/mocked/pubsub.rs index c0f664d5fc1..cf144a21b80 100644 --- a/rpc/src/v1/tests/mocked/pubsub.rs +++ b/rpc/src/v1/tests/mocked/pubsub.rs @@ -1,77 +1,85 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use std::sync::{atomic, Arc}; -use jsonrpc_core::{self as core, MetaIoHandler}; -use jsonrpc_core::futures::{self, Stream, Future}; +use jsonrpc_core::{ + self as core, + futures::{self, Future, Stream}, + MetaIoHandler, +}; use jsonrpc_pubsub::Session; use parity_runtime::Runtime; -use v1::{PubSub, PubSubClient, Metadata}; +use v1::{Metadata, PubSub, PubSubClient}; fn rpc() -> MetaIoHandler { - let mut io = MetaIoHandler::default(); - let called = atomic::AtomicBool::new(false); - io.add_method("hello", move |_| { - if !called.load(atomic::Ordering::SeqCst) { - called.store(true, atomic::Ordering::SeqCst); - Ok(core::Value::String("hello".into())) - } else { - Ok(core::Value::String("world".into())) - } - }); - io + let mut io = MetaIoHandler::default(); + let called = atomic::AtomicBool::new(false); + io.add_method("hello", move |_| { + if !called.load(atomic::Ordering::SeqCst) { + called.store(true, atomic::Ordering::SeqCst); + Ok(core::Value::String("hello".into())) + } else { + Ok(core::Value::String("world".into())) + } + }); + io } #[test] fn should_subscribe_to_a_method() { - // given - let el = Runtime::with_thread_count(1); - let rpc = rpc(); - let pubsub = PubSubClient::new_test(rpc, el.executor()).to_delegate(); + // given + let el = Runtime::with_thread_count(1); + let rpc = rpc(); + let pubsub = PubSubClient::new_test(rpc, el.executor()).to_delegate(); - let mut io = MetaIoHandler::default(); - io.extend_with(pubsub); + let mut io = MetaIoHandler::default(); + io.extend_with(pubsub); - let mut metadata = Metadata::default(); - let (sender, receiver) = futures::sync::mpsc::channel(8); - metadata.session = Some(Arc::new(Session::new(sender))); + let mut metadata = Metadata::default(); + let (sender, receiver) = futures::sync::mpsc::channel(8); + metadata.session = Some(Arc::new(Session::new(sender))); - // Subscribe - let request = r#"{"jsonrpc": "2.0", "method": "parity_subscribe", "params": ["hello", []], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"0x416d77337e24399d","id":1}"#; - assert_eq!(io.handle_request_sync(request, metadata.clone()), Some(response.to_owned())); + // Subscribe + let request = + r#"{"jsonrpc": "2.0", "method": "parity_subscribe", "params": ["hello", []], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x416d77337e24399d","id":1}"#; + assert_eq!( + io.handle_request_sync(request, metadata.clone()), + Some(response.to_owned()) + ); - // Check notifications - let (res, receiver) = receiver.into_future().wait().unwrap(); - let response = - r#"{"jsonrpc":"2.0","method":"parity_subscription","params":{"result":"hello","subscription":"0x416d77337e24399d"}}"#; - assert_eq!(res, Some(response.into())); + // Check notifications + let (res, receiver) = receiver.into_future().wait().unwrap(); + let response = r#"{"jsonrpc":"2.0","method":"parity_subscription","params":{"result":"hello","subscription":"0x416d77337e24399d"}}"#; + assert_eq!(res, Some(response.into())); - let (res, receiver) = receiver.into_future().wait().unwrap(); - let response = - r#"{"jsonrpc":"2.0","method":"parity_subscription","params":{"result":"world","subscription":"0x416d77337e24399d"}}"#; - assert_eq!(res, Some(response.into())); + let (res, receiver) = receiver.into_future().wait().unwrap(); + let response = r#"{"jsonrpc":"2.0","method":"parity_subscription","params":{"result":"world","subscription":"0x416d77337e24399d"}}"#; + assert_eq!(res, Some(response.into())); - // And unsubscribe - let request = r#"{"jsonrpc": "2.0", "method": "parity_unsubscribe", "params": ["0x416d77337e24399d"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - assert_eq!(io.handle_request_sync(request, metadata), Some(response.to_owned())); + // And unsubscribe + let request = r#"{"jsonrpc": "2.0", "method": "parity_unsubscribe", "params": ["0x416d77337e24399d"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + assert_eq!( + io.handle_request_sync(request, metadata), + Some(response.to_owned()) + ); - let (res, _receiver) = receiver.into_future().wait().unwrap(); - assert_eq!(res, None); + let (res, _receiver) = receiver.into_future().wait().unwrap(); + assert_eq!(res, None); } diff --git a/rpc/src/v1/tests/mocked/rpc.rs b/rpc/src/v1/tests/mocked/rpc.rs deleted file mode 100644 index d4634ac90e4..00000000000 --- a/rpc/src/v1/tests/mocked/rpc.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::BTreeMap; -use jsonrpc_core::IoHandler; -use v1::{Rpc, RpcClient}; - -fn rpc_client() -> RpcClient { - let mut modules = BTreeMap::new(); - modules.insert("rpc".to_owned(), "1.0".to_owned()); - modules.insert("web3".to_owned(), "1.0".to_owned()); - modules.insert("ethcore".to_owned(), "1.0".to_owned()); - RpcClient::new(modules) -} - -#[test] -fn modules() { - let rpc = rpc_client().to_delegate(); - let mut io = IoHandler::new(); - io.extend_with(rpc); - - let request = r#"{"jsonrpc": "2.0", "method": "modules", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"rpc":"1.0","web3":"1.0"},"id":1}"#; - - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); -} - -#[test] -fn rpc_modules() { - let rpc = rpc_client().to_delegate(); - let mut io = IoHandler::new(); - io.extend_with(rpc); - - let request = r#"{"jsonrpc": "2.0", "method": "rpc_modules", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"ethcore":"1.0","rpc":"1.0","web3":"1.0"},"id":1}"#; - - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); -} diff --git a/rpc/src/v1/tests/mocked/secretstore.rs b/rpc/src/v1/tests/mocked/secretstore.rs index 96e20d0028b..2b702823c6c 100644 --- a/rpc/src/v1/tests/mocked/secretstore.rs +++ b/rpc/src/v1/tests/mocked/secretstore.rs @@ -1,176 +1,205 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use std::sync::Arc; -use crypto::DEFAULT_MAC; use accounts::AccountProvider; +use crypto::DEFAULT_MAC; use ethereum_types::H256; -use ethkey::{KeyPair, Signature, verify_public}; +use ethkey::{verify_public, KeyPair, Signature}; -use serde_json; use jsonrpc_core::{IoHandler, Success}; -use v1::metadata::Metadata; -use v1::SecretStoreClient; -use v1::traits::secretstore::SecretStore; -use v1::helpers::secretstore::ordered_servers_keccak; -use v1::types::EncryptedDocumentKey; +use serde_json; +use v1::{ + helpers::secretstore::ordered_servers_keccak, metadata::Metadata, + traits::secretstore::SecretStore, types::EncryptedDocumentKey, SecretStoreClient, +}; struct Dependencies { - pub accounts: Arc, + pub accounts: Arc, } impl Dependencies { - pub fn new() -> Self { - Dependencies { - accounts: Arc::new(AccountProvider::transient_provider()), - } - } - - pub fn client(&self) -> SecretStoreClient { - SecretStoreClient::new(&self.accounts) - } - - fn default_client(&self) -> IoHandler { - let mut io = IoHandler::default(); - io.extend_with(self.client().to_delegate()); - io - } + pub fn new() -> Self { + Dependencies { + accounts: Arc::new(AccountProvider::transient_provider()), + } + } + + pub fn client(&self) -> SecretStoreClient { + SecretStoreClient::new(&self.accounts) + } + + fn default_client(&self) -> IoHandler { + let mut io = IoHandler::default(); + io.extend_with(self.client().to_delegate()); + io + } } #[test] fn rpc_secretstore_encrypt_and_decrypt() { - let deps = Dependencies::new(); - let io = deps.default_client(); - - // insert new account - let secret = "c1f1cfe279a5c350d13795bce162941967340c8a228e6ba175489afc564a5bef".parse().unwrap(); - deps.accounts.insert_account(secret, &"password".into()).unwrap(); - - // execute encryption request - let encryption_request = r#"{"jsonrpc": "2.0", "method": "secretstore_encrypt", "params":[ + let deps = Dependencies::new(); + let io = deps.default_client(); + + // insert new account + let secret = "c1f1cfe279a5c350d13795bce162941967340c8a228e6ba175489afc564a5bef" + .parse() + .unwrap(); + deps.accounts + .insert_account(secret, &"password".into()) + .unwrap(); + + // execute encryption request + let encryption_request = r#"{"jsonrpc": "2.0", "method": "secretstore_encrypt", "params":[ "0x5c2f3b4ec0c2234f8358697edc8b82a62e3ac995", "password", "0x0440262acc06f1e13cb11b34e792cdf698673a16bb812163cb52689ac34c94ae47047b58f58d8b596d21ac7b03a55896132d07a7dc028b2dad88f6c5a90623fa5b30ff4b1ba385a98c970432d13417cf6d7facd62f86faaef15ca993735890da0cb3e417e2740fc72de7501eef083a12dd5a9ebe513b592b1740848576a936a1eb88fc553fc624b1cae41a0a4e074e34e2aaae686709f08d70e505c5acba12ef96017e89be675a2adb07c72c4e95814fbf", "0xdeadbeef" ], "id": 1}"#; - let encryption_response = io.handle_request_sync(encryption_request).unwrap(); - let encryption_response: Success = serde_json::from_str(&encryption_response).unwrap(); + let encryption_response = io.handle_request_sync(encryption_request).unwrap(); + let encryption_response: Success = serde_json::from_str(&encryption_response).unwrap(); - // execute decryption request - let decryption_request_left = r#"{"jsonrpc": "2.0", "method": "secretstore_decrypt", "params":[ + // execute decryption request + let decryption_request_left = r#"{"jsonrpc": "2.0", "method": "secretstore_decrypt", "params":[ "0x5c2f3b4ec0c2234f8358697edc8b82a62e3ac995", "password", "0x0440262acc06f1e13cb11b34e792cdf698673a16bb812163cb52689ac34c94ae47047b58f58d8b596d21ac7b03a55896132d07a7dc028b2dad88f6c5a90623fa5b30ff4b1ba385a98c970432d13417cf6d7facd62f86faaef15ca993735890da0cb3e417e2740fc72de7501eef083a12dd5a9ebe513b592b1740848576a936a1eb88fc553fc624b1cae41a0a4e074e34e2aaae686709f08d70e505c5acba12ef96017e89be675a2adb07c72c4e95814fbf",""#; - let decryption_request_mid = encryption_response.result.as_str().unwrap(); - let decryption_request_right = r#"" + let decryption_request_mid = encryption_response.result.as_str().unwrap(); + let decryption_request_right = r#"" ], "id": 2}"#; - let decryption_request = decryption_request_left.to_owned() + decryption_request_mid + decryption_request_right; - let decryption_response = io.handle_request_sync(&decryption_request).unwrap(); - assert_eq!(decryption_response, r#"{"jsonrpc":"2.0","result":"0xdeadbeef","id":2}"#); + let decryption_request = + decryption_request_left.to_owned() + decryption_request_mid + decryption_request_right; + let decryption_response = io.handle_request_sync(&decryption_request).unwrap(); + assert_eq!( + decryption_response, + r#"{"jsonrpc":"2.0","result":"0xdeadbeef","id":2}"# + ); } #[test] fn rpc_secretstore_shadow_decrypt() { - let deps = Dependencies::new(); - let io = deps.default_client(); - - // insert new account - let secret = "82758356bf46b42710d3946a8efa612b7bf5e125e4d49f28facf1139db4a46f4".parse().unwrap(); - deps.accounts.insert_account(secret, &"password".into()).unwrap(); - - // execute decryption request - let decryption_request = r#"{"jsonrpc": "2.0", "method": "secretstore_shadowDecrypt", "params":[ + let deps = Dependencies::new(); + let io = deps.default_client(); + + // insert new account + let secret = "82758356bf46b42710d3946a8efa612b7bf5e125e4d49f28facf1139db4a46f4" + .parse() + .unwrap(); + deps.accounts + .insert_account(secret, &"password".into()) + .unwrap(); + + // execute decryption request + let decryption_request = r#"{"jsonrpc": "2.0", "method": "secretstore_shadowDecrypt", "params":[ "0x00dfE63B22312ab4329aD0d28CaD8Af987A01932", "password", "0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91", "0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3", ["0x049ce50bbadb6352574f2c59742f78df83333975cbd5cbb151c6e8628749a33dc1fa93bb6dffae5994e3eb98ae859ed55ee82937538e6adb054d780d1e89ff140f121529eeadb1161562af9d3342db0008919ca280a064305e5a4e518e93279de7a9396fe5136a9658e337e8e276221248c381c5384cd1ad28e5921f46ff058d5fbcf8a388fc881d0dd29421c218d51761"], "0x2ddec1f96229efa2916988d8b2a82a47ef36f71c" ], "id": 1}"#; - let decryption_response = io.handle_request_sync(&decryption_request).unwrap(); - assert_eq!(decryption_response, r#"{"jsonrpc":"2.0","result":"0xdeadbeef","id":1}"#); + let decryption_response = io.handle_request_sync(&decryption_request).unwrap(); + assert_eq!( + decryption_response, + r#"{"jsonrpc":"2.0","result":"0xdeadbeef","id":1}"# + ); } #[test] fn rpc_secretstore_servers_set_hash() { - let deps = Dependencies::new(); - let io = deps.default_client(); + let deps = Dependencies::new(); + let io = deps.default_client(); - // execute hashing request - let hashing_request = r#"{"jsonrpc": "2.0", "method": "secretstore_serversSetHash", "params":[ + // execute hashing request + let hashing_request = r#"{"jsonrpc": "2.0", "method": "secretstore_serversSetHash", "params":[ ["0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91", "0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3"] ], "id": 1}"#; - let hashing_response = io.handle_request_sync(&hashing_request).unwrap(); - let hashing_response = hashing_response.replace(r#"{"jsonrpc":"2.0","result":"0x"#, ""); - let hashing_response = hashing_response.replace(r#"","id":1}"#, ""); - let hash: H256 = hashing_response.parse().unwrap(); + let hashing_response = io.handle_request_sync(&hashing_request).unwrap(); + let hashing_response = hashing_response.replace(r#"{"jsonrpc":"2.0","result":"0x"#, ""); + let hashing_response = hashing_response.replace(r#"","id":1}"#, ""); + let hash: H256 = hashing_response.parse().unwrap(); - let servers_set_keccak = ordered_servers_keccak(vec![ + let servers_set_keccak = ordered_servers_keccak(vec![ "843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91".parse().unwrap(), "07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3".parse().unwrap() ].into_iter().collect()); - assert_eq!(hash, servers_set_keccak); + assert_eq!(hash, servers_set_keccak); } #[test] fn rpc_secretstore_sign_raw_hash() { - let deps = Dependencies::new(); - let io = deps.default_client(); - - // insert new account - let secret = "82758356bf46b42710d3946a8efa612b7bf5e125e4d49f28facf1139db4a46f4".parse().unwrap(); - let key_pair = KeyPair::from_secret(secret).unwrap(); - deps.accounts.insert_account(key_pair.secret().clone(), &"password".into()).unwrap(); - - // execute signing request - let signing_request = r#"{"jsonrpc": "2.0", "method": "secretstore_signRawHash", "params":[ + let deps = Dependencies::new(); + let io = deps.default_client(); + + // insert new account + let secret = "82758356bf46b42710d3946a8efa612b7bf5e125e4d49f28facf1139db4a46f4" + .parse() + .unwrap(); + let key_pair = KeyPair::from_secret(secret).unwrap(); + deps.accounts + .insert_account(key_pair.secret().clone(), &"password".into()) + .unwrap(); + + // execute signing request + let signing_request = r#"{"jsonrpc": "2.0", "method": "secretstore_signRawHash", "params":[ "0x00dfE63B22312ab4329aD0d28CaD8Af987A01932", "password", "0x0000000000000000000000000000000000000000000000000000000000000001" ], "id": 1}"#; - let signing_response = io.handle_request_sync(&signing_request).unwrap(); - let signing_response = signing_response.replace(r#"{"jsonrpc":"2.0","result":"0x"#, ""); - let signing_response = signing_response.replace(r#"","id":1}"#, ""); - let signature: Signature = signing_response.parse().unwrap(); - - let hash = "0000000000000000000000000000000000000000000000000000000000000001".parse().unwrap(); - assert!(verify_public(key_pair.public(), &signature, &hash).unwrap()); + let signing_response = io.handle_request_sync(&signing_request).unwrap(); + let signing_response = signing_response.replace(r#"{"jsonrpc":"2.0","result":"0x"#, ""); + let signing_response = signing_response.replace(r#"","id":1}"#, ""); + let signature: Signature = signing_response.parse().unwrap(); + + let hash = "0000000000000000000000000000000000000000000000000000000000000001" + .parse() + .unwrap(); + assert!(verify_public(key_pair.public(), &signature, &hash).unwrap()); } #[test] fn rpc_secretstore_generate_document_key() { - let deps = Dependencies::new(); - let io = deps.default_client(); - - // insert new account - let secret = "82758356bf46b42710d3946a8efa612b7bf5e125e4d49f28facf1139db4a46f4".parse().unwrap(); - let key_pair = KeyPair::from_secret(secret).unwrap(); - deps.accounts.insert_account(key_pair.secret().clone(), &"password".into()).unwrap(); - - // execute generation request - let generation_request = r#"{"jsonrpc": "2.0", "method": "secretstore_generateDocumentKey", "params":[ + let deps = Dependencies::new(); + let io = deps.default_client(); + + // insert new account + let secret = "82758356bf46b42710d3946a8efa612b7bf5e125e4d49f28facf1139db4a46f4" + .parse() + .unwrap(); + let key_pair = KeyPair::from_secret(secret).unwrap(); + deps.accounts + .insert_account(key_pair.secret().clone(), &"password".into()) + .unwrap(); + + // execute generation request + let generation_request = r#"{"jsonrpc": "2.0", "method": "secretstore_generateDocumentKey", "params":[ "0x00dfE63B22312ab4329aD0d28CaD8Af987A01932", "password", "0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91" ], "id": 1}"#; - let generation_response = io.handle_request_sync(&generation_request).unwrap(); - let generation_response = generation_response.replace(r#"{"jsonrpc":"2.0","result":"#, ""); - let generation_response = generation_response.replace(r#","id":1}"#, ""); - let generation_response: EncryptedDocumentKey = serde_json::from_str(&generation_response).unwrap(); - - // the only thing we can check is that 'encrypted_key' can be decrypted by passed account - assert!(deps.accounts.decrypt( - "00dfE63B22312ab4329aD0d28CaD8Af987A01932".parse().unwrap(), - Some("password".into()), - &DEFAULT_MAC, - &generation_response.encrypted_key.0).is_ok()); + let generation_response = io.handle_request_sync(&generation_request).unwrap(); + let generation_response = generation_response.replace(r#"{"jsonrpc":"2.0","result":"#, ""); + let generation_response = generation_response.replace(r#","id":1}"#, ""); + let generation_response: EncryptedDocumentKey = + serde_json::from_str(&generation_response).unwrap(); + + // the only thing we can check is that 'encrypted_key' can be decrypted by passed account + assert!(deps + .accounts + .decrypt( + "00dfE63B22312ab4329aD0d28CaD8Af987A01932".parse().unwrap(), + Some("password".into()), + &DEFAULT_MAC, + &generation_response.encrypted_key.0 + ) + .is_ok()); } diff --git a/rpc/src/v1/tests/mocked/signer.rs b/rpc/src/v1/tests/mocked/signer.rs index e22c5b8d249..3d9f6f9bf45 100644 --- a/rpc/src/v1/tests/mocked/signer.rs +++ b/rpc/src/v1/tests/mocked/signer.rs @@ -1,559 +1,732 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::sync::Arc; -use std::str::FromStr; -use ethereum_types::{H520, U256, Address}; use bytes::ToPretty; +use ethereum_types::{Address, H520, U256}; +use std::{str::FromStr, sync::Arc}; use accounts::AccountProvider; use ethcore::client::TestBlockChainClient; use parity_runtime::Runtime; use parking_lot::Mutex; use rlp::encode; -use types::transaction::{Transaction, Action, SignedTransaction}; +use types::transaction::{Action, SignedTransaction, Transaction}; -use serde_json; use jsonrpc_core::IoHandler; -use v1::{SignerClient, Signer, Origin}; -use v1::metadata::Metadata; -use v1::tests::helpers::TestMinerService; -use v1::types::Bytes as RpcBytes; -use v1::helpers::{nonce, FilledTransactionRequest, ConfirmationPayload}; -use v1::helpers::external_signer::{SigningQueue, SignerService}; -use v1::helpers::dispatch::{self, FullDispatcher, eth_data_hash}; +use serde_json; +use v1::{ + helpers::{ + dispatch::{self, eth_data_hash, FullDispatcher}, + external_signer::{SignerService, SigningQueue}, + nonce, ConfirmationPayload, FilledTransactionRequest, + }, + metadata::Metadata, + tests::helpers::TestMinerService, + types::Bytes as RpcBytes, + Origin, Signer, SignerClient, +}; struct SignerTester { - _runtime: Runtime, - signer: Arc, - accounts: Arc, - io: IoHandler, - miner: Arc, + _runtime: Runtime, + signer: Arc, + accounts: Arc, + io: IoHandler, + miner: Arc, } fn blockchain_client() -> Arc { - let client = TestBlockChainClient::new(); - Arc::new(client) + let client = TestBlockChainClient::new(); + Arc::new(client) } fn accounts_provider() -> Arc { - Arc::new(AccountProvider::transient_provider()) + Arc::new(AccountProvider::transient_provider()) } fn miner_service() -> Arc { - Arc::new(TestMinerService::default()) + Arc::new(TestMinerService::default()) } fn signer_tester() -> SignerTester { - let runtime = Runtime::with_thread_count(1); - let signer = Arc::new(SignerService::new_test(false)); - let accounts = accounts_provider(); - let account_signer = Arc::new(dispatch::Signer::new(accounts.clone())); - let client = blockchain_client(); - let miner = miner_service(); - let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor()))); - - let dispatcher = FullDispatcher::new(client, miner.clone(), reservations, 50); - let mut io = IoHandler::default(); - io.extend_with(SignerClient::new(account_signer, dispatcher, &signer, runtime.executor()).to_delegate()); - - SignerTester { - _runtime: runtime, - signer: signer, - accounts: accounts, - io: io, - miner: miner, - } + let runtime = Runtime::with_thread_count(1); + let signer = Arc::new(SignerService::new_test(false)); + let accounts = accounts_provider(); + let account_signer = Arc::new(dispatch::Signer::new(accounts.clone())); + let client = blockchain_client(); + let miner = miner_service(); + let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor()))); + + let dispatcher = FullDispatcher::new(client, miner.clone(), reservations, 50); + let mut io = IoHandler::default(); + io.extend_with( + SignerClient::new(account_signer, dispatcher, &signer, runtime.executor()).to_delegate(), + ); + + SignerTester { + _runtime: runtime, + signer: signer, + accounts: accounts, + io: io, + miner: miner, + } } #[test] fn should_return_list_of_items_to_confirm() { - // given - let tester = signer_tester(); - let _send_future = tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { - from: Address::from(1), - used_default_from: false, - to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), - gas_price: U256::from(10_000), - gas: U256::from(10_000_000), - value: U256::from(1), - data: vec![], - nonce: None, - condition: None, - }), Origin::Unknown).unwrap(); - let _sign_future = tester.signer.add_request(ConfirmationPayload::EthSignMessage(1.into(), vec![5].into()), Origin::Unknown).unwrap(); - - // when - let request = r#"{"jsonrpc":"2.0","method":"signer_requestsToConfirm","params":[],"id":1}"#; - let response = concat!( - r#"{"jsonrpc":"2.0","result":["#, - r#"{"id":"0x1","origin":"unknown","payload":{"sendTransaction":{"condition":null,"data":"0x","from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x1"}}},"#, - r#"{"id":"0x2","origin":"unknown","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","data":"0x05"}}}"#, - r#"],"id":1}"# - ); - - // then - assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); + // given + let tester = signer_tester(); + let _send_future = tester + .signer + .add_request( + ConfirmationPayload::SendTransaction(FilledTransactionRequest { + from: Address::from(1), + used_default_from: false, + to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), + gas_price: U256::from(10_000), + gas: U256::from(10_000_000), + value: U256::from(1), + data: vec![], + nonce: None, + condition: None, + }), + Origin::Unknown, + ) + .unwrap(); + let _sign_future = tester + .signer + .add_request( + ConfirmationPayload::EthSignMessage(1.into(), vec![5].into()), + Origin::Unknown, + ) + .unwrap(); + + // when + let request = r#"{"jsonrpc":"2.0","method":"signer_requestsToConfirm","params":[],"id":1}"#; + let response = concat!( + r#"{"jsonrpc":"2.0","result":["#, + r#"{"id":"0x1","origin":"unknown","payload":{"sendTransaction":{"condition":null,"data":"0x","from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x1"}}},"#, + r#"{"id":"0x2","origin":"unknown","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","data":"0x05"}}}"#, + r#"],"id":1}"# + ); + + // then + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.to_owned()) + ); } #[test] fn should_reject_transaction_from_queue_without_dispatching() { - // given - let tester = signer_tester(); - let _confirmation_future = tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { - from: Address::from(1), - used_default_from: false, - to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), - gas_price: U256::from(10_000), - gas: U256::from(10_000_000), - value: U256::from(1), - data: vec![], - nonce: None, - condition: None, - }), Origin::Unknown).unwrap(); - assert_eq!(tester.signer.requests().len(), 1); - - // when - let request = r#"{"jsonrpc":"2.0","method":"signer_rejectRequest","params":["0x1"],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; - - // then - assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); - assert_eq!(tester.signer.requests().len(), 0); - assert_eq!(tester.miner.imported_transactions.lock().len(), 0); + // given + let tester = signer_tester(); + let _confirmation_future = tester + .signer + .add_request( + ConfirmationPayload::SendTransaction(FilledTransactionRequest { + from: Address::from(1), + used_default_from: false, + to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), + gas_price: U256::from(10_000), + gas: U256::from(10_000_000), + value: U256::from(1), + data: vec![], + nonce: None, + condition: None, + }), + Origin::Unknown, + ) + .unwrap(); + assert_eq!(tester.signer.requests().len(), 1); + + // when + let request = r#"{"jsonrpc":"2.0","method":"signer_rejectRequest","params":["0x1"],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + + // then + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.to_owned()) + ); + assert_eq!(tester.signer.requests().len(), 0); + assert_eq!(tester.miner.imported_transactions.lock().len(), 0); } #[test] fn should_not_remove_transaction_if_password_is_invalid() { - // given - let tester = signer_tester(); - let _confirmation_future = tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { - from: Address::from(1), - used_default_from: false, - to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), - gas_price: U256::from(10_000), - gas: U256::from(10_000_000), - value: U256::from(1), - data: vec![], - nonce: None, - condition: None, - }), Origin::Unknown).unwrap(); - assert_eq!(tester.signer.requests().len(), 1); - - // when - let request = r#"{"jsonrpc":"2.0","method":"signer_confirmRequest","params":["0x1",{},"xxx"],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32021,"message":"Account password is invalid or account does not exist.","data":"SStore(InvalidAccount)"},"id":1}"#; - - // then - assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); - assert_eq!(tester.signer.requests().len(), 1); + // given + let tester = signer_tester(); + let _confirmation_future = tester + .signer + .add_request( + ConfirmationPayload::SendTransaction(FilledTransactionRequest { + from: Address::from(1), + used_default_from: false, + to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), + gas_price: U256::from(10_000), + gas: U256::from(10_000_000), + value: U256::from(1), + data: vec![], + nonce: None, + condition: None, + }), + Origin::Unknown, + ) + .unwrap(); + assert_eq!(tester.signer.requests().len(), 1); + + // when + let request = + r#"{"jsonrpc":"2.0","method":"signer_confirmRequest","params":["0x1",{},"xxx"],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32021,"message":"Account password is invalid or account does not exist.","data":"SStore(InvalidAccount)"},"id":1}"#; + + // then + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.to_owned()) + ); + assert_eq!(tester.signer.requests().len(), 1); } #[test] fn should_not_remove_sign_if_password_is_invalid() { - // given - let tester = signer_tester(); - let _confirmation_future = tester.signer.add_request(ConfirmationPayload::EthSignMessage(0.into(), vec![5].into()), Origin::Unknown).unwrap(); - assert_eq!(tester.signer.requests().len(), 1); - - // when - let request = r#"{"jsonrpc":"2.0","method":"signer_confirmRequest","params":["0x1",{},"xxx"],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32021,"message":"Account password is invalid or account does not exist.","data":"SStore(InvalidAccount)"},"id":1}"#; - - // then - assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); - assert_eq!(tester.signer.requests().len(), 1); + // given + let tester = signer_tester(); + let _confirmation_future = tester + .signer + .add_request( + ConfirmationPayload::EthSignMessage(0.into(), vec![5].into()), + Origin::Unknown, + ) + .unwrap(); + assert_eq!(tester.signer.requests().len(), 1); + + // when + let request = + r#"{"jsonrpc":"2.0","method":"signer_confirmRequest","params":["0x1",{},"xxx"],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32021,"message":"Account password is invalid or account does not exist.","data":"SStore(InvalidAccount)"},"id":1}"#; + + // then + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.to_owned()) + ); + assert_eq!(tester.signer.requests().len(), 1); } #[test] fn should_confirm_transaction_and_dispatch() { - //// given - let tester = signer_tester(); - let address = tester.accounts.new_account(&"test".into()).unwrap(); - let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(); - let _confirmation_future = tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { - from: address, - used_default_from: false, - to: Some(recipient), - gas_price: U256::from(10_000), - gas: U256::from(10_000_000), - value: U256::from(1), - data: vec![], - nonce: None, - condition: None, - }), Origin::Unknown).unwrap(); - - let t = Transaction { - nonce: U256::zero(), - gas_price: U256::from(0x1000), - gas: U256::from(0x50505), - action: Action::Call(recipient), - value: U256::from(0x1), - data: vec![] - }; - tester.accounts.unlock_account_temporarily(address, "test".into()).unwrap(); - let signature = tester.accounts.sign(address, None, t.hash(None)).unwrap(); - let t = t.with_signature(signature, None); - - assert_eq!(tester.signer.requests().len(), 1); - - // when - let request = r#"{ + //// given + let tester = signer_tester(); + let address = tester.accounts.new_account(&"test".into()).unwrap(); + let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(); + let _confirmation_future = tester + .signer + .add_request( + ConfirmationPayload::SendTransaction(FilledTransactionRequest { + from: address, + used_default_from: false, + to: Some(recipient), + gas_price: U256::from(10_000), + gas: U256::from(10_000_000), + value: U256::from(1), + data: vec![], + nonce: None, + condition: None, + }), + Origin::Unknown, + ) + .unwrap(); + + let t = Transaction { + nonce: U256::zero(), + gas_price: U256::from(0x1000), + gas: U256::from(0x50505), + action: Action::Call(recipient), + value: U256::from(0x1), + data: vec![], + }; + tester + .accounts + .unlock_account_temporarily(address, "test".into()) + .unwrap(); + let signature = tester.accounts.sign(address, None, t.hash(None)).unwrap(); + let t = t.with_signature(signature, None); + + assert_eq!(tester.signer.requests().len(), 1); + + // when + let request = r#"{ "jsonrpc":"2.0", "method":"signer_confirmRequest", "params":["0x1", {"gasPrice":"0x1000","gas":"0x50505"}, "test"], "id":1 }"#; - let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:x}", t.hash()).as_ref() + r#"","id":1}"#; - - // then - assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); - assert_eq!(tester.signer.requests().len(), 0); - assert_eq!(tester.miner.imported_transactions.lock().len(), 1); + let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + + format!("0x{:x}", t.hash()).as_ref() + + r#"","id":1}"#; + + // then + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.to_owned()) + ); + assert_eq!(tester.signer.requests().len(), 0); + assert_eq!(tester.miner.imported_transactions.lock().len(), 1); } #[test] fn should_alter_the_sender_and_nonce() { - //// given - let tester = signer_tester(); - let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(); - let _confirmation_future = tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { - from: 0.into(), - used_default_from: false, - to: Some(recipient), - gas_price: U256::from(10_000), - gas: U256::from(10_000_000), - value: U256::from(1), - data: vec![], - nonce: Some(10.into()), - condition: None, - }), Origin::Unknown).unwrap(); - - let t = Transaction { - nonce: U256::zero(), - gas_price: U256::from(0x1000), - gas: U256::from(0x50505), - action: Action::Call(recipient), - value: U256::from(0x1), - data: vec![] - }; - - let address = tester.accounts.new_account(&"test".into()).unwrap(); - let signature = tester.accounts.sign(address, Some("test".into()), t.hash(None)).unwrap(); - let t = t.with_signature(signature, None); - - assert_eq!(tester.signer.requests().len(), 1); - - // when - let request = r#"{ + //// given + let tester = signer_tester(); + let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(); + let _confirmation_future = tester + .signer + .add_request( + ConfirmationPayload::SendTransaction(FilledTransactionRequest { + from: 0.into(), + used_default_from: false, + to: Some(recipient), + gas_price: U256::from(10_000), + gas: U256::from(10_000_000), + value: U256::from(1), + data: vec![], + nonce: Some(10.into()), + condition: None, + }), + Origin::Unknown, + ) + .unwrap(); + + let t = Transaction { + nonce: U256::zero(), + gas_price: U256::from(0x1000), + gas: U256::from(0x50505), + action: Action::Call(recipient), + value: U256::from(0x1), + data: vec![], + }; + + let address = tester.accounts.new_account(&"test".into()).unwrap(); + let signature = tester + .accounts + .sign(address, Some("test".into()), t.hash(None)) + .unwrap(); + let t = t.with_signature(signature, None); + + assert_eq!(tester.signer.requests().len(), 1); + + // when + let request = r#"{ "jsonrpc":"2.0", "method":"signer_confirmRequest", - "params":["0x1", {"sender":""#.to_owned() - + &format!("0x{:x}", address) - + r#"","gasPrice":"0x1000","gas":"0x50505"}, "test"], + "params":["0x1", {"sender":""# + .to_owned() + + &format!("0x{:x}", address) + + r#"","gasPrice":"0x1000","gas":"0x50505"}, "test"], "id":1 }"#; - let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{:x}", t.hash()) + r#"","id":1}"#; - - // then - assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); - assert_eq!(tester.signer.requests().len(), 0); - assert_eq!(tester.miner.imported_transactions.lock().len(), 1); + let response = + r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{:x}", t.hash()) + r#"","id":1}"#; + + // then + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.to_owned()) + ); + assert_eq!(tester.signer.requests().len(), 0); + assert_eq!(tester.miner.imported_transactions.lock().len(), 1); } #[test] fn should_confirm_transaction_with_token() { - // given - let tester = signer_tester(); - let address = tester.accounts.new_account(&"test".into()).unwrap(); - let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(); - let _confirmation_future = tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { - from: address, - used_default_from: false, - to: Some(recipient), - gas_price: U256::from(10_000), - gas: U256::from(10_000_000), - value: U256::from(1), - data: vec![], - nonce: None, - condition: None, - }), Origin::Unknown).unwrap(); - - let t = Transaction { - nonce: U256::zero(), - gas_price: U256::from(0x1000), - gas: U256::from(10_000_000), - action: Action::Call(recipient), - value: U256::from(0x1), - data: vec![] - }; - let (signature, token) = tester.accounts.sign_with_token(address, "test".into(), t.hash(None)).unwrap(); - let t = t.with_signature(signature, None); - - assert_eq!(tester.signer.requests().len(), 1); - - // when - let request = r#"{ + // given + let tester = signer_tester(); + let address = tester.accounts.new_account(&"test".into()).unwrap(); + let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(); + let _confirmation_future = tester + .signer + .add_request( + ConfirmationPayload::SendTransaction(FilledTransactionRequest { + from: address, + used_default_from: false, + to: Some(recipient), + gas_price: U256::from(10_000), + gas: U256::from(10_000_000), + value: U256::from(1), + data: vec![], + nonce: None, + condition: None, + }), + Origin::Unknown, + ) + .unwrap(); + + let t = Transaction { + nonce: U256::zero(), + gas_price: U256::from(0x1000), + gas: U256::from(10_000_000), + action: Action::Call(recipient), + value: U256::from(0x1), + data: vec![], + }; + let (signature, token) = tester + .accounts + .sign_with_token(address, "test".into(), t.hash(None)) + .unwrap(); + let t = t.with_signature(signature, None); + + assert_eq!(tester.signer.requests().len(), 1); + + // when + let request = r#"{ "jsonrpc":"2.0", "method":"signer_confirmRequestWithToken", - "params":["0x1", {"gasPrice":"0x1000"}, ""#.to_owned() + token.as_str() + r#""], + "params":["0x1", {"gasPrice":"0x1000"}, ""# + .to_owned() + + token.as_str() + + r#""], "id":1 }"#; - let response = r#"{"jsonrpc":"2.0","result":{"result":""#.to_owned() + - format!("0x{:x}", t.hash()).as_ref() + - r#"","token":""#; - - // then - let result = tester.io.handle_request_sync(&request).unwrap(); - assert!(result.starts_with(&response), "Should return correct result. Expected: {:?}, Got: {:?}", response, result); - assert_eq!(tester.signer.requests().len(), 0); - assert_eq!(tester.miner.imported_transactions.lock().len(), 1); + let response = r#"{"jsonrpc":"2.0","result":{"result":""#.to_owned() + + format!("0x{:x}", t.hash()).as_ref() + + r#"","token":""#; + + // then + let result = tester.io.handle_request_sync(&request).unwrap(); + assert!( + result.starts_with(&response), + "Should return correct result. Expected: {:?}, Got: {:?}", + response, + result + ); + assert_eq!(tester.signer.requests().len(), 0); + assert_eq!(tester.miner.imported_transactions.lock().len(), 1); } #[test] fn should_confirm_transaction_with_rlp() { - // given - let tester = signer_tester(); - let address = tester.accounts.new_account(&"test".into()).unwrap(); - let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(); - let _confirmation_future = tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { - from: address, - used_default_from: false, - to: Some(recipient), - gas_price: U256::from(10_000), - gas: U256::from(10_000_000), - value: U256::from(1), - data: vec![], - nonce: None, - condition: None, - }), Origin::Unknown).unwrap(); - - let t = Transaction { - nonce: U256::zero(), - gas_price: U256::from(0x1000), - gas: U256::from(10_000_000), - action: Action::Call(recipient), - value: U256::from(0x1), - data: vec![] - }; - let signature = tester.accounts.sign(address, Some("test".into()), t.hash(None)).unwrap(); - let t = t.with_signature(signature, None); - let rlp = encode(&t); - - assert_eq!(tester.signer.requests().len(), 1); - - // when - let request = r#"{ + // given + let tester = signer_tester(); + let address = tester.accounts.new_account(&"test".into()).unwrap(); + let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(); + let _confirmation_future = tester + .signer + .add_request( + ConfirmationPayload::SendTransaction(FilledTransactionRequest { + from: address, + used_default_from: false, + to: Some(recipient), + gas_price: U256::from(10_000), + gas: U256::from(10_000_000), + value: U256::from(1), + data: vec![], + nonce: None, + condition: None, + }), + Origin::Unknown, + ) + .unwrap(); + + let t = Transaction { + nonce: U256::zero(), + gas_price: U256::from(0x1000), + gas: U256::from(10_000_000), + action: Action::Call(recipient), + value: U256::from(0x1), + data: vec![], + }; + let signature = tester + .accounts + .sign(address, Some("test".into()), t.hash(None)) + .unwrap(); + let t = t.with_signature(signature, None); + let rlp = encode(&t); + + assert_eq!(tester.signer.requests().len(), 1); + + // when + let request = r#"{ "jsonrpc":"2.0", "method":"signer_confirmRequestRaw", - "params":["0x1", "0x"#.to_owned() + &rlp.to_hex() + r#""], + "params":["0x1", "0x"# + .to_owned() + + &rlp.to_hex() + + r#""], "id":1 }"#; - let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:x}", t.hash()).as_ref() + r#"","id":1}"#; - - // then - assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); - assert_eq!(tester.signer.requests().len(), 0); - assert_eq!(tester.miner.imported_transactions.lock().len(), 1); + let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + + format!("0x{:x}", t.hash()).as_ref() + + r#"","id":1}"#; + + // then + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.to_owned()) + ); + assert_eq!(tester.signer.requests().len(), 0); + assert_eq!(tester.miner.imported_transactions.lock().len(), 1); } #[test] fn should_return_error_when_sender_does_not_match() { - // given - let tester = signer_tester(); - let address = tester.accounts.new_account(&"test".into()).unwrap(); - let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(); - let _confirmation_future = tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { - from: Address::default(), - used_default_from: false, - to: Some(recipient), - gas_price: U256::from(10_000), - gas: U256::from(10_000_000), - value: U256::from(1), - data: vec![], - nonce: None, - condition: None, - }), Origin::Unknown).unwrap(); - - let t = Transaction { - nonce: U256::zero(), - gas_price: U256::from(0x1000), - gas: U256::from(10_000_000), - action: Action::Call(recipient), - value: U256::from(0x1), - data: vec![] - }; - tester.accounts.unlock_account_temporarily(address, "test".into()).unwrap(); - let signature = tester.accounts.sign(address, None, t.hash(None)).unwrap(); - let t = t.with_signature(signature, None); - let rlp = encode(&t); - - assert_eq!(tester.signer.requests().len(), 1); - - // when - let request = r#"{ + // given + let tester = signer_tester(); + let address = tester.accounts.new_account(&"test".into()).unwrap(); + let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(); + let _confirmation_future = tester + .signer + .add_request( + ConfirmationPayload::SendTransaction(FilledTransactionRequest { + from: Address::default(), + used_default_from: false, + to: Some(recipient), + gas_price: U256::from(10_000), + gas: U256::from(10_000_000), + value: U256::from(1), + data: vec![], + nonce: None, + condition: None, + }), + Origin::Unknown, + ) + .unwrap(); + + let t = Transaction { + nonce: U256::zero(), + gas_price: U256::from(0x1000), + gas: U256::from(10_000_000), + action: Action::Call(recipient), + value: U256::from(0x1), + data: vec![], + }; + tester + .accounts + .unlock_account_temporarily(address, "test".into()) + .unwrap(); + let signature = tester.accounts.sign(address, None, t.hash(None)).unwrap(); + let t = t.with_signature(signature, None); + let rlp = encode(&t); + + assert_eq!(tester.signer.requests().len(), 1); + + // when + let request = r#"{ "jsonrpc":"2.0", "method":"signer_confirmRequestRaw", - "params":["0x1", "0x"#.to_owned() + &rlp.to_hex() + r#""], + "params":["0x1", "0x"# + .to_owned() + + &rlp.to_hex() + + r#""], "id":1 }"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Couldn't parse parameters: Sent transaction does not match the request.","data":"[\"from\"]"},"id":1}"#; - - // then - assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); - assert_eq!(tester.signer.requests().len(), 1); + let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Couldn't parse parameters: Sent transaction does not match the request.","data":"[\"from\"]"},"id":1}"#; + + // then + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.to_owned()) + ); + assert_eq!(tester.signer.requests().len(), 1); } #[test] fn should_confirm_sign_transaction_with_rlp() { - // given - let tester = signer_tester(); - let address = tester.accounts.new_account(&"test".into()).unwrap(); - let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(); - let _confirmation_future = tester.signer.add_request(ConfirmationPayload::SignTransaction(FilledTransactionRequest { - from: address, - used_default_from: false, - to: Some(recipient), - gas_price: U256::from(10_000), - gas: U256::from(10_000_000), - value: U256::from(1), - data: vec![], - nonce: None, - condition: None, - }), Origin::Unknown).unwrap(); - assert_eq!(tester.signer.requests().len(), 1); - - let t = Transaction { - nonce: U256::zero(), - gas_price: U256::from(0x1000), - gas: U256::from(10_000_000), - action: Action::Call(recipient), - value: U256::from(0x1), - data: vec![] - }; - let signature = tester.accounts.sign(address, Some("test".into()), t.hash(None)).unwrap(); - let t = SignedTransaction::new(t.with_signature(signature.clone(), None)).unwrap(); - let rlp = encode(&t); - - // when - let request = r#"{ + // given + let tester = signer_tester(); + let address = tester.accounts.new_account(&"test".into()).unwrap(); + let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(); + let _confirmation_future = tester + .signer + .add_request( + ConfirmationPayload::SignTransaction(FilledTransactionRequest { + from: address, + used_default_from: false, + to: Some(recipient), + gas_price: U256::from(10_000), + gas: U256::from(10_000_000), + value: U256::from(1), + data: vec![], + nonce: None, + condition: None, + }), + Origin::Unknown, + ) + .unwrap(); + assert_eq!(tester.signer.requests().len(), 1); + + let t = Transaction { + nonce: U256::zero(), + gas_price: U256::from(0x1000), + gas: U256::from(10_000_000), + action: Action::Call(recipient), + value: U256::from(0x1), + data: vec![], + }; + let signature = tester + .accounts + .sign(address, Some("test".into()), t.hash(None)) + .unwrap(); + let t = SignedTransaction::new(t.with_signature(signature.clone(), None)).unwrap(); + let rlp = encode(&t); + + // when + let request = r#"{ "jsonrpc":"2.0", "method":"signer_confirmRequestRaw", - "params":["0x1", "0x"#.to_owned() + &rlp.to_hex() + r#""], + "params":["0x1", "0x"# + .to_owned() + + &rlp.to_hex() + + r#""], "id":1 }"#; - let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() + - r#""raw":"0x"# + &rlp.to_hex() + r#"","# + - r#""tx":{"# + - r#""blockHash":null,"blockNumber":null,"# + - &format!("\"chainId\":{},", t.chain_id().map_or("null".to_owned(), |n| format!("{}", n))) + - r#""condition":null,"creates":null,"# + - &format!("\"from\":\"0x{:x}\",", &address) + - r#""gas":"0x989680","gasPrice":"0x1000","# + - &format!("\"hash\":\"0x{:x}\",", t.hash()) + - r#""input":"0x","# + - r#""nonce":"0x0","# + - &format!("\"publicKey\":\"0x{:x}\",", t.public_key().unwrap()) + - &format!("\"r\":\"0x{:x}\",", U256::from(signature.r())) + - &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + - &format!("\"s\":\"0x{:x}\",", U256::from(signature.s())) + - &format!("\"standardV\":\"0x{:x}\",", U256::from(t.standard_v())) + - r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"# + - &format!("\"v\":\"0x{:x}\",", U256::from(t.original_v())) + - r#""value":"0x1""# + - r#"}},"id":1}"#; - - // then - assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); - assert_eq!(tester.signer.requests().len(), 0); - assert_eq!(tester.miner.imported_transactions.lock().len(), 0); + let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() + + r#""raw":"0x"# + + &rlp.to_hex() + + r#"","# + + r#""tx":{"# + + r#""blockHash":null,"blockNumber":null,"# + + &format!( + "\"chainId\":{},", + t.chain_id().map_or("null".to_owned(), |n| format!("{}", n)) + ) + + r#""condition":null,"creates":null,"# + + &format!("\"from\":\"0x{:x}\",", &address) + + r#""gas":"0x989680","gasPrice":"0x1000","# + + &format!("\"hash\":\"0x{:x}\",", t.hash()) + + r#""input":"0x","# + + r#""nonce":"0x0","# + + &format!("\"publicKey\":\"0x{:x}\",", t.public_key().unwrap()) + + &format!("\"r\":\"0x{:x}\",", U256::from(signature.r())) + + &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + + &format!("\"s\":\"0x{:x}\",", U256::from(signature.s())) + + &format!("\"standardV\":\"0x{:x}\",", U256::from(t.standard_v())) + + r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"# + + &format!("\"v\":\"0x{:x}\",", U256::from(t.original_v())) + + r#""value":"0x1""# + + r#"}},"id":1}"#; + + // then + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.to_owned()) + ); + assert_eq!(tester.signer.requests().len(), 0); + assert_eq!(tester.miner.imported_transactions.lock().len(), 0); } #[test] fn should_confirm_data_sign_with_signature() { - // given - let tester = signer_tester(); - let address = tester.accounts.new_account(&"test".into()).unwrap(); - let _confirmation_future = tester.signer.add_request(ConfirmationPayload::EthSignMessage( - address, - vec![1, 2, 3, 4].into(), - ), Origin::Unknown).unwrap(); - assert_eq!(tester.signer.requests().len(), 1); - - let data_hash = eth_data_hash(vec![1, 2, 3, 4].into()); - let signature = H520(tester.accounts.sign(address, Some("test".into()), data_hash).unwrap().into_electrum()); - let signature = format!("{:?}", signature); - - // when - let request = r#"{ + // given + let tester = signer_tester(); + let address = tester.accounts.new_account(&"test".into()).unwrap(); + let _confirmation_future = tester + .signer + .add_request( + ConfirmationPayload::EthSignMessage(address, vec![1, 2, 3, 4].into()), + Origin::Unknown, + ) + .unwrap(); + assert_eq!(tester.signer.requests().len(), 1); + + let data_hash = eth_data_hash(vec![1, 2, 3, 4].into()); + let signature = H520( + tester + .accounts + .sign(address, Some("test".into()), data_hash) + .unwrap() + .into_electrum(), + ); + let signature = format!("{:?}", signature); + + // when + let request = r#"{ "jsonrpc":"2.0", "method":"signer_confirmRequestRaw", - "params":["0x1", ""#.to_owned() + &signature + r#""], + "params":["0x1", ""# + .to_owned() + + &signature + + r#""], "id":1 }"#; - let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &signature + r#"","id":1}"#; - - // then - assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); - assert_eq!(tester.signer.requests().len(), 0); - assert_eq!(tester.miner.imported_transactions.lock().len(), 0); + let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &signature + r#"","id":1}"#; + + // then + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.to_owned()) + ); + assert_eq!(tester.signer.requests().len(), 0); + assert_eq!(tester.miner.imported_transactions.lock().len(), 0); } #[test] fn should_confirm_decrypt_with_phrase() { - // given - let tester = signer_tester(); - let address = tester.accounts.new_account(&"test".into()).unwrap(); - let _confirmation_future = tester.signer.add_request(ConfirmationPayload::Decrypt( - address, - vec![1, 2, 3, 4].into(), - ), Origin::Unknown).unwrap(); - assert_eq!(tester.signer.requests().len(), 1); - - let decrypted = serde_json::to_string(&RpcBytes::new(b"phrase".to_vec())).unwrap(); - - // when - let request = r#"{ + // given + let tester = signer_tester(); + let address = tester.accounts.new_account(&"test".into()).unwrap(); + let _confirmation_future = tester + .signer + .add_request( + ConfirmationPayload::Decrypt(address, vec![1, 2, 3, 4].into()), + Origin::Unknown, + ) + .unwrap(); + assert_eq!(tester.signer.requests().len(), 1); + + let decrypted = serde_json::to_string(&RpcBytes::new(b"phrase".to_vec())).unwrap(); + + // when + let request = r#"{ "jsonrpc":"2.0", "method":"signer_confirmRequestRaw", - "params":["0x1", "#.to_owned() + &decrypted + r#"], + "params":["0x1", "# + .to_owned() + + &decrypted + + r#"], "id":1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"#.to_owned() + &decrypted + r#","id":1}"#; - - // then - assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); - assert_eq!(tester.signer.requests().len(), 0); - assert_eq!(tester.miner.imported_transactions.lock().len(), 0); + let response = r#"{"jsonrpc":"2.0","result":"#.to_owned() + &decrypted + r#","id":1}"#; + + // then + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.to_owned()) + ); + assert_eq!(tester.signer.requests().len(), 0); + assert_eq!(tester.miner.imported_transactions.lock().len(), 0); } #[test] fn should_generate_new_token() { - // given - let tester = signer_tester(); + // given + let tester = signer_tester(); - // when - let request = r#"{ + // when + let request = r#"{ "jsonrpc":"2.0", "method":"signer_generateAuthorizationToken", "params":[], "id":1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"new_token","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"new_token","id":1}"#; - // then - assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); + // then + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.to_owned()) + ); } diff --git a/rpc/src/v1/tests/mocked/signing.rs b/rpc/src/v1/tests/mocked/signing.rs index 39385d19bd8..9d8734a3cab 100644 --- a/rpc/src/v1/tests/mocked/signing.rs +++ b/rpc/src/v1/tests/mocked/signing.rs @@ -1,276 +1,331 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::thread; -use std::str::FromStr; -use std::sync::Arc; -use std::time::Duration; use rlp; - -use jsonrpc_core::{IoHandler, Success}; -use jsonrpc_core::futures::Future; -use v1::impls::SigningQueueClient; -use v1::metadata::Metadata; -use v1::traits::{EthSigning, ParitySigning, Parity}; -use v1::helpers::{nonce, dispatch, FullDispatcher}; -use v1::helpers::external_signer::{SignerService, SigningQueue}; -use v1::types::{ConfirmationResponse, RichRawTransaction}; -use v1::tests::helpers::TestMinerService; -use v1::tests::mocked::parity; +use std::{str::FromStr, sync::Arc, thread, time::Duration}; + +use jsonrpc_core::{futures::Future, IoHandler, Success}; +use v1::{ + helpers::{ + dispatch, + external_signer::{SignerService, SigningQueue}, + nonce, FullDispatcher, + }, + impls::SigningQueueClient, + metadata::Metadata, + tests::{helpers::TestMinerService, mocked::parity}, + traits::{EthSigning, Parity, ParitySigning}, + types::{ConfirmationResponse, RichRawTransaction}, +}; use accounts::AccountProvider; use bytes::ToPretty; -use ethereum_types::{U256, Address}; use ethcore::client::TestBlockChainClient; +use ethereum_types::{Address, U256}; use ethkey::Secret; use ethstore::ethkey::{Generator, Random}; +use parity_runtime::{Executor, Runtime}; use parking_lot::Mutex; use serde_json; -use types::transaction::{Transaction, Action, SignedTransaction}; -use parity_runtime::{Runtime, Executor}; +use types::transaction::{Action, SignedTransaction, Transaction}; struct SigningTester { - pub runtime: Runtime, - pub signer: Arc, - pub client: Arc, - pub miner: Arc, - pub accounts: Arc, - pub io: IoHandler, + pub runtime: Runtime, + pub signer: Arc, + pub client: Arc, + pub miner: Arc, + pub accounts: Arc, + pub io: IoHandler, } impl Default for SigningTester { - fn default() -> Self { - let runtime = Runtime::with_thread_count(1); - let signer = Arc::new(SignerService::new_test(false)); - let client = Arc::new(TestBlockChainClient::default()); - let miner = Arc::new(TestMinerService::default()); - let accounts = Arc::new(AccountProvider::transient_provider()); - let account_signer = Arc::new(dispatch::Signer::new(accounts.clone())) as _; - let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor()))); - let mut io = IoHandler::default(); - - let dispatcher = FullDispatcher::new(client.clone(), miner.clone(), reservations, 50); - - let executor = Executor::new_thread_per_future(); - - let rpc = SigningQueueClient::new(&signer, dispatcher.clone(), executor.clone(), &account_signer); - io.extend_with(EthSigning::to_delegate(rpc)); - let rpc = SigningQueueClient::new(&signer, dispatcher, executor, &account_signer); - io.extend_with(ParitySigning::to_delegate(rpc)); - - SigningTester { - runtime, - signer: signer, - client: client, - miner: miner, - accounts: accounts, - io: io, - } - } + fn default() -> Self { + let runtime = Runtime::with_thread_count(1); + let signer = Arc::new(SignerService::new_test(false)); + let client = Arc::new(TestBlockChainClient::default()); + let miner = Arc::new(TestMinerService::default()); + let accounts = Arc::new(AccountProvider::transient_provider()); + let account_signer = Arc::new(dispatch::Signer::new(accounts.clone())) as _; + let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor()))); + let mut io = IoHandler::default(); + + let dispatcher = FullDispatcher::new(client.clone(), miner.clone(), reservations, 50); + + let executor = Executor::new_thread_per_future(); + + let rpc = SigningQueueClient::new( + &signer, + dispatcher.clone(), + executor.clone(), + &account_signer, + ); + io.extend_with(EthSigning::to_delegate(rpc)); + let rpc = SigningQueueClient::new(&signer, dispatcher, executor, &account_signer); + io.extend_with(ParitySigning::to_delegate(rpc)); + + SigningTester { + runtime, + signer: signer, + client: client, + miner: miner, + accounts: accounts, + io: io, + } + } } fn eth_signing() -> SigningTester { - SigningTester::default() + SigningTester::default() } #[test] fn rpc_eth_sign() { - use rustc_hex::FromHex; - - let tester = eth_signing(); - - let account = tester.accounts.insert_account(Secret::from([69u8; 32]), &"abcd".into()).unwrap(); - tester.accounts.unlock_account_permanently(account, "abcd".into()).unwrap(); - let _message = "0cc175b9c0f1b6a831c399e26977266192eb5ffee6ae2fec3ad71c777531578f".from_hex().unwrap(); - - let req = r#"{ + use rustc_hex::FromHex; + + let tester = eth_signing(); + + let account = tester + .accounts + .insert_account(Secret::from([69u8; 32]), &"abcd".into()) + .unwrap(); + tester + .accounts + .unlock_account_permanently(account, "abcd".into()) + .unwrap(); + let _message = "0cc175b9c0f1b6a831c399e26977266192eb5ffee6ae2fec3ad71c777531578f" + .from_hex() + .unwrap(); + + let req = r#"{ "jsonrpc": "2.0", "method": "eth_sign", "params": [ - ""#.to_owned() + &format!("0x{:x}", account) + r#"", + ""# + .to_owned() + + &format!("0x{:x}", account) + + r#"", "0x0cc175b9c0f1b6a831c399e26977266192eb5ffee6ae2fec3ad71c777531578f" ], "id": 1 }"#; - let res = r#"{"jsonrpc":"2.0","result":"0xa2870db1d0c26ef93c7b72d2a0830fa6b841e0593f7186bc6c7cc317af8cf3a42fda03bd589a49949aa05db83300cdb553116274518dbe9d90c65d0213f4af491b","id":1}"#; + let res = r#"{"jsonrpc":"2.0","result":"0xa2870db1d0c26ef93c7b72d2a0830fa6b841e0593f7186bc6c7cc317af8cf3a42fda03bd589a49949aa05db83300cdb553116274518dbe9d90c65d0213f4af491b","id":1}"#; - assert_eq!(tester.io.handle_request_sync(&req), Some(res.into())); + assert_eq!(tester.io.handle_request_sync(&req), Some(res.into())); } #[test] fn should_add_sign_to_queue() { - // given - let tester = eth_signing(); - let address = Address::random(); - assert_eq!(tester.signer.requests().len(), 0); + // given + let tester = eth_signing(); + let address = Address::random(); + assert_eq!(tester.signer.requests().len(), 0); - // when - let request = r#"{ + // when + let request = r#"{ "jsonrpc": "2.0", "method": "eth_sign", "params": [ - ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", + ""# + .to_owned() + + format!("0x{:x}", address).as_ref() + + r#"", "0x0000000000000000000000000000000000000000000000000000000000000005" ], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","id":1}"#; - - // then - let promise = tester.io.handle_request(&request); - - // the future must be polled at least once before request is queued. - let signer = tester.signer.clone(); - ::std::thread::spawn(move || loop { - if signer.requests().len() == 1 { - // respond - let sender = signer.take(&1.into()).unwrap(); - signer.request_confirmed(sender, Ok(ConfirmationResponse::Signature(0.into()))); - break - } - ::std::thread::sleep(Duration::from_millis(100)) - }); - - let res = promise.wait().unwrap(); - assert_eq!(res, Some(response.to_owned())); + let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","id":1}"#; + + // then + let promise = tester.io.handle_request(&request); + + // the future must be polled at least once before request is queued. + let signer = tester.signer.clone(); + ::std::thread::spawn(move || loop { + if signer.requests().len() == 1 { + // respond + let sender = signer.take(&1.into()).unwrap(); + signer.request_confirmed(sender, Ok(ConfirmationResponse::Signature(0.into()))); + break; + } + ::std::thread::sleep(Duration::from_millis(100)) + }); + + let res = promise.wait().unwrap(); + assert_eq!(res, Some(response.to_owned())); } #[test] fn should_post_sign_to_queue() { - // given - let tester = eth_signing(); - let address = Address::random(); - assert_eq!(tester.signer.requests().len(), 0); + // given + let tester = eth_signing(); + let address = Address::random(); + assert_eq!(tester.signer.requests().len(), 0); - // when - let request = r#"{ + // when + let request = r#"{ "jsonrpc": "2.0", "method": "parity_postSign", "params": [ - ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", + ""# + .to_owned() + + format!("0x{:x}", address).as_ref() + + r#"", "0x0000000000000000000000000000000000000000000000000000000000000005" ], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0x1","id":1}"#; - - // then - assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); - assert_eq!(tester.signer.requests().len(), 1); + let response = r#"{"jsonrpc":"2.0","result":"0x1","id":1}"#; + + // then + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.to_owned()) + ); + assert_eq!(tester.signer.requests().len(), 1); } #[test] fn should_check_status_of_request() { - // given - let tester = eth_signing(); - let address = Address::random(); - let request = r#"{ + // given + let tester = eth_signing(); + let address = Address::random(); + let request = r#"{ "jsonrpc": "2.0", "method": "parity_postSign", "params": [ - ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", + ""# + .to_owned() + + format!("0x{:x}", address).as_ref() + + r#"", "0x0000000000000000000000000000000000000000000000000000000000000005" ], "id": 1 }"#; - tester.io.handle_request_sync(&request).expect("Sent"); + tester.io.handle_request_sync(&request).expect("Sent"); - // when - let request = r#"{ + // when + let request = r#"{ "jsonrpc": "2.0", "method": "parity_checkRequest", "params": ["0x1"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; - // then - assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); + // then + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.to_owned()) + ); } #[test] fn should_check_status_of_request_when_its_resolved() { - // given - let tester = eth_signing(); - let address = Address::random(); - let request = r#"{ + // given + let tester = eth_signing(); + let address = Address::random(); + let request = r#"{ "jsonrpc": "2.0", "method": "parity_postSign", "params": [ - ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", + ""# + .to_owned() + + format!("0x{:x}", address).as_ref() + + r#"", "0x0000000000000000000000000000000000000000000000000000000000000005" ], "id": 1 }"#; - tester.io.handle_request_sync(&request).expect("Sent"); - let sender = tester.signer.take(&1.into()).unwrap(); - tester.signer.request_confirmed(sender, Ok(ConfirmationResponse::Signature(1.into()))); + tester.io.handle_request_sync(&request).expect("Sent"); + let sender = tester.signer.take(&1.into()).unwrap(); + tester + .signer + .request_confirmed(sender, Ok(ConfirmationResponse::Signature(1.into()))); - // This is not ideal, but we need to give futures some time to be executed, and they need to run in a separate thread - thread::sleep(Duration::from_millis(20)); + // This is not ideal, but we need to give futures some time to be executed, and they need to run in a separate thread + thread::sleep(Duration::from_millis(20)); - // when - let request = r#"{ + // when + let request = r#"{ "jsonrpc": "2.0", "method": "parity_checkRequest", "params": ["0x1"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","id":1}"#; - // then - assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); + // then + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.to_owned()) + ); } #[test] fn should_sign_if_account_is_unlocked() { - // given - let tester = eth_signing(); - let data = vec![5u8]; - let acc = tester.accounts.insert_account(Secret::from([69u8; 32]), &"test".into()).unwrap(); - tester.accounts.unlock_account_permanently(acc, "test".into()).unwrap(); - - // when - let request = r#"{ + // given + let tester = eth_signing(); + let data = vec![5u8]; + let acc = tester + .accounts + .insert_account(Secret::from([69u8; 32]), &"test".into()) + .unwrap(); + tester + .accounts + .unlock_account_permanently(acc, "test".into()) + .unwrap(); + + // when + let request = r#"{ "jsonrpc": "2.0", "method": "eth_sign", "params": [ - ""#.to_owned() + format!("0x{:x}", acc).as_ref() + r#"", - ""# + format!("0x{}", data.to_hex()).as_ref() + r#"" + ""# + .to_owned() + + format!("0x{:x}", acc).as_ref() + + r#"", + ""# + format!("0x{}", data.to_hex()).as_ref() + + r#"" ], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0xdb53b32e56cf3e9735377b7664d6de5a03e125b1bf8ec55715d253668b4238503b4ac931fe6af90add73e72a585e952665376b2b9afc5b6b239b7df74c734e121b","id":1}"#; - assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); - assert_eq!(tester.signer.requests().len(), 0); + let response = r#"{"jsonrpc":"2.0","result":"0xdb53b32e56cf3e9735377b7664d6de5a03e125b1bf8ec55715d253668b4238503b4ac931fe6af90add73e72a585e952665376b2b9afc5b6b239b7df74c734e121b","id":1}"#; + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.to_owned()) + ); + assert_eq!(tester.signer.requests().len(), 0); } #[test] fn should_add_transaction_to_queue() { - // given - let tester = eth_signing(); - let address = Address::random(); - assert_eq!(tester.signer.requests().len(), 0); + // given + let tester = eth_signing(); + let address = Address::random(); + assert_eq!(tester.signer.requests().len(), 0); - // when - let request = r#"{ + // when + let request = r#"{ "jsonrpc": "2.0", "method": "eth_sendTransaction", "params": [{ - "from": ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", + "from": ""# + .to_owned() + + format!("0x{:x}", address).as_ref() + + r#"", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "gas": "0x76c0", "gasPrice": "0x9184e72a000", @@ -278,41 +333,44 @@ fn should_add_transaction_to_queue() { }], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000","id":1}"#; - - // then - let promise = tester.io.handle_request(&request); - - // the future must be polled at least once before request is queued. - let signer = tester.signer.clone(); - ::std::thread::spawn(move || loop { - if signer.requests().len() == 1 { - // respond - let sender = signer.take(&1.into()).unwrap(); - signer.request_confirmed(sender, Ok(ConfirmationResponse::SendTransaction(0.into()))); - break - } - ::std::thread::sleep(Duration::from_millis(100)) - }); - - let res = promise.wait().unwrap(); - assert_eq!(res, Some(response.to_owned())); + let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000","id":1}"#; + + // then + let promise = tester.io.handle_request(&request); + + // the future must be polled at least once before request is queued. + let signer = tester.signer.clone(); + ::std::thread::spawn(move || loop { + if signer.requests().len() == 1 { + // respond + let sender = signer.take(&1.into()).unwrap(); + signer.request_confirmed(sender, Ok(ConfirmationResponse::SendTransaction(0.into()))); + break; + } + ::std::thread::sleep(Duration::from_millis(100)) + }); + + let res = promise.wait().unwrap(); + assert_eq!(res, Some(response.to_owned())); } #[test] fn should_add_sign_transaction_to_the_queue() { - // given - let tester = eth_signing(); - let address = tester.accounts.new_account(&"test".into()).unwrap(); + // given + let tester = eth_signing(); + let address = tester.accounts.new_account(&"test".into()).unwrap(); - assert_eq!(tester.signer.requests().len(), 0); + assert_eq!(tester.signer.requests().len(), 0); - // when - let request = r#"{ + // when + let request = r#"{ "jsonrpc": "2.0", "method": "eth_signTransaction", "params": [{ - "from": ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", + "from": ""# + .to_owned() + + format!("0x{:x}", address).as_ref() + + r#"", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "gas": "0x76c0", "gasPrice": "0x9184e72a000", @@ -321,87 +379,108 @@ fn should_add_sign_transaction_to_the_queue() { "id": 1 }"#; - let t = Transaction { - nonce: U256::one(), - gas_price: U256::from(0x9184e72a000u64), - gas: U256::from(0x76c0), - action: Action::Call(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), - value: U256::from(0x9184e72au64), - data: vec![] - }; - let signature = tester.accounts.sign(address, Some("test".into()), t.hash(None)).unwrap(); - let t = t.with_signature(signature, None); - let t = SignedTransaction::new(t).unwrap(); - let signature = t.signature(); - let rlp = rlp::encode(&t); - - let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() + - r#""raw":"0x"# + &rlp.to_hex() + r#"","# + - r#""tx":{"# + - r#""blockHash":null,"blockNumber":null,"# + - &format!("\"chainId\":{},", t.chain_id().map_or("null".to_owned(), |n| format!("{}", n))) + - r#""condition":null,"creates":null,"# + - &format!("\"from\":\"0x{:x}\",", &address) + - r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + - &format!("\"hash\":\"0x{:x}\",", t.hash()) + - r#""input":"0x","# + - r#""nonce":"0x1","# + - &format!("\"publicKey\":\"0x{:x}\",", t.public_key().unwrap()) + - &format!("\"r\":\"0x{:x}\",", U256::from(signature.r())) + - &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + - &format!("\"s\":\"0x{:x}\",", U256::from(signature.s())) + - &format!("\"standardV\":\"0x{:x}\",", U256::from(t.standard_v())) + - r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"# + - &format!("\"v\":\"0x{:x}\",", U256::from(t.original_v())) + - r#""value":"0x9184e72a""# + - r#"}},"id":1}"#; - - // then - tester.miner.increment_nonce(&address); - let promise = tester.io.handle_request(&request); - - // the future must be polled at least once before request is queued. - let signer = tester.signer.clone(); - ::std::thread::spawn(move || loop { - if signer.requests().len() == 1 { - // respond - let sender = signer.take(&1.into()).unwrap(); - signer.request_confirmed(sender, Ok(ConfirmationResponse::SignTransaction( - RichRawTransaction::from_signed(t.into()) - ))); - break - } - ::std::thread::sleep(Duration::from_millis(100)) - }); - - let res = promise.wait().unwrap(); - assert_eq!(res, Some(response.to_owned())); + let t = Transaction { + nonce: U256::one(), + gas_price: U256::from(0x9184e72a000u64), + gas: U256::from(0x76c0), + action: Action::Call( + Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(), + ), + value: U256::from(0x9184e72au64), + data: vec![], + }; + let signature = tester + .accounts + .sign(address, Some("test".into()), t.hash(None)) + .unwrap(); + let t = t.with_signature(signature, None); + let t = SignedTransaction::new(t).unwrap(); + let signature = t.signature(); + let rlp = rlp::encode(&t); + + let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() + + r#""raw":"0x"# + + &rlp.to_hex() + + r#"","# + + r#""tx":{"# + + r#""blockHash":null,"blockNumber":null,"# + + &format!( + "\"chainId\":{},", + t.chain_id().map_or("null".to_owned(), |n| format!("{}", n)) + ) + + r#""condition":null,"creates":null,"# + + &format!("\"from\":\"0x{:x}\",", &address) + + r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + + &format!("\"hash\":\"0x{:x}\",", t.hash()) + + r#""input":"0x","# + + r#""nonce":"0x1","# + + &format!("\"publicKey\":\"0x{:x}\",", t.public_key().unwrap()) + + &format!("\"r\":\"0x{:x}\",", U256::from(signature.r())) + + &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + + &format!("\"s\":\"0x{:x}\",", U256::from(signature.s())) + + &format!("\"standardV\":\"0x{:x}\",", U256::from(t.standard_v())) + + r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"# + + &format!("\"v\":\"0x{:x}\",", U256::from(t.original_v())) + + r#""value":"0x9184e72a""# + + r#"}},"id":1}"#; + + // then + tester.miner.increment_nonce(&address); + let promise = tester.io.handle_request(&request); + + // the future must be polled at least once before request is queued. + let signer = tester.signer.clone(); + ::std::thread::spawn(move || loop { + if signer.requests().len() == 1 { + // respond + let sender = signer.take(&1.into()).unwrap(); + signer.request_confirmed( + sender, + Ok(ConfirmationResponse::SignTransaction( + RichRawTransaction::from_signed(t.into()), + )), + ); + break; + } + ::std::thread::sleep(Duration::from_millis(100)) + }); + + let res = promise.wait().unwrap(); + assert_eq!(res, Some(response.to_owned())); } #[test] fn should_dispatch_transaction_if_account_is_unlock() { - // given - let tester = eth_signing(); - let acc = tester.accounts.new_account(&"test".into()).unwrap(); - tester.accounts.unlock_account_permanently(acc, "test".into()).unwrap(); - - let t = Transaction { - nonce: U256::zero(), - gas_price: U256::from(0x9184e72a000u64), - gas: U256::from(0x76c0), - action: Action::Call(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), - value: U256::from(0x9184e72au64), - data: vec![] - }; - let signature = tester.accounts.sign(acc, None, t.hash(None)).unwrap(); - let t = t.with_signature(signature, None); - - // when - let request = r#"{ + // given + let tester = eth_signing(); + let acc = tester.accounts.new_account(&"test".into()).unwrap(); + tester + .accounts + .unlock_account_permanently(acc, "test".into()) + .unwrap(); + + let t = Transaction { + nonce: U256::zero(), + gas_price: U256::from(0x9184e72a000u64), + gas: U256::from(0x76c0), + action: Action::Call( + Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(), + ), + value: U256::from(0x9184e72au64), + data: vec![], + }; + let signature = tester.accounts.sign(acc, None, t.hash(None)).unwrap(); + let t = t.with_signature(signature, None); + + // when + let request = r#"{ "jsonrpc": "2.0", "method": "eth_sendTransaction", "params": [{ - "from": ""#.to_owned() + format!("0x{:x}", acc).as_ref() + r#"", + "from": ""# + .to_owned() + + format!("0x{:x}", acc).as_ref() + + r#"", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "gas": "0x76c0", "gasPrice": "0x9184e72a000", @@ -409,102 +488,129 @@ fn should_dispatch_transaction_if_account_is_unlock() { }], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:x}", t.hash()).as_ref() + r#"","id":1}"#; - - // then - assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); + let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + + format!("0x{:x}", t.hash()).as_ref() + + r#"","id":1}"#; + + // then + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.to_owned()) + ); } #[test] fn should_decrypt_message_if_account_is_unlocked() { - // given - let mut tester = eth_signing(); - let parity = parity::Dependencies::new(); - tester.io.extend_with(parity.client(None).to_delegate()); - let (address, public) = tester.accounts.new_account_and_public(&"test".into()).unwrap(); - tester.accounts.unlock_account_permanently(address, "test".into()).unwrap(); - - // First encrypt message - let request = format!("{}0x{:x}{}", - r#"{"jsonrpc": "2.0", "method": "parity_encryptMessage", "params":[""#, - public, - r#"", "0x01020304"], "id": 1}"# - ); - let encrypted: Success = serde_json::from_str(&tester.io.handle_request_sync(&request).unwrap()).unwrap(); - - // then call decrypt - let request = format!("{}{:x}{}{}{}", - r#"{"jsonrpc": "2.0", "method": "parity_decryptMessage", "params":["0x"#, - address, - r#"","#, - encrypted.result, - r#"], "id": 1}"# - ); - println!("Request: {:?}", request); - let response = r#"{"jsonrpc":"2.0","result":"0x01020304","id":1}"#; - - // then - assert_eq!(tester.io.handle_request_sync(&request), Some(response.into())); + // given + let mut tester = eth_signing(); + let parity = parity::Dependencies::new(); + tester.io.extend_with(parity.client(None).to_delegate()); + let (address, public) = tester + .accounts + .new_account_and_public(&"test".into()) + .unwrap(); + tester + .accounts + .unlock_account_permanently(address, "test".into()) + .unwrap(); + + // First encrypt message + let request = format!( + "{}0x{:x}{}", + r#"{"jsonrpc": "2.0", "method": "parity_encryptMessage", "params":[""#, + public, + r#"", "0x01020304"], "id": 1}"# + ); + let encrypted: Success = + serde_json::from_str(&tester.io.handle_request_sync(&request).unwrap()).unwrap(); + + // then call decrypt + let request = format!( + "{}{:x}{}{}{}", + r#"{"jsonrpc": "2.0", "method": "parity_decryptMessage", "params":["0x"#, + address, + r#"","#, + encrypted.result, + r#"], "id": 1}"# + ); + println!("Request: {:?}", request); + let response = r#"{"jsonrpc":"2.0","result":"0x01020304","id":1}"#; + + // then + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.into()) + ); } #[test] fn should_add_decryption_to_the_queue() { - // given - let tester = eth_signing(); - let acc = Random.generate().unwrap(); - assert_eq!(tester.signer.requests().len(), 0); + // given + let tester = eth_signing(); + let acc = Random.generate().unwrap(); + assert_eq!(tester.signer.requests().len(), 0); - // when - let request = r#"{ + // when + let request = r#"{ "jsonrpc": "2.0", "method": "parity_decryptMessage", - "params": ["0x"#.to_owned() + &format!("{:x}", acc.address()) + r#"", + "params": ["0x"# + .to_owned() + + &format!("{:x}", acc.address()) + + r#"", "0x012345"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0x0102","id":1}"#; - - // then - let promise = tester.io.handle_request(&request); - - // the future must be polled at least once before request is queued. - let signer = tester.signer.clone(); - ::std::thread::spawn(move || loop { - if signer.requests().len() == 1 { - // respond - let sender = signer.take(&1.into()).unwrap(); - signer.request_confirmed(sender, Ok(ConfirmationResponse::Decrypt(vec![0x1, 0x2].into()))); - break - } - ::std::thread::sleep(Duration::from_millis(10)) - }); - - // check response: will deadlock if unsuccessful. - let res = promise.wait().unwrap(); - assert_eq!(res, Some(response.to_owned())); + let response = r#"{"jsonrpc":"2.0","result":"0x0102","id":1}"#; + + // then + let promise = tester.io.handle_request(&request); + + // the future must be polled at least once before request is queued. + let signer = tester.signer.clone(); + ::std::thread::spawn(move || loop { + if signer.requests().len() == 1 { + // respond + let sender = signer.take(&1.into()).unwrap(); + signer.request_confirmed( + sender, + Ok(ConfirmationResponse::Decrypt(vec![0x1, 0x2].into())), + ); + break; + } + ::std::thread::sleep(Duration::from_millis(10)) + }); + + // check response: will deadlock if unsuccessful. + let res = promise.wait().unwrap(); + assert_eq!(res, Some(response.to_owned())); } #[test] fn should_compose_transaction() { - // given - let tester = eth_signing(); - let acc = Random.generate().unwrap(); - assert_eq!(tester.signer.requests().len(), 0); - let from = format!("{:x}", acc.address()); - - // when - let request = r#"{ + // given + let tester = eth_signing(); + let acc = Random.generate().unwrap(); + assert_eq!(tester.signer.requests().len(), 0); + let from = format!("{:x}", acc.address()); + + // when + let request = r#"{ "jsonrpc": "2.0", "method": "parity_composeTransaction", - "params": [{"from":"0x"#.to_owned() + &from + r#"","value":"0x5"}], + "params": [{"from":"0x"# + .to_owned() + + &from + + r#"","value":"0x5"}], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":{"condition":null,"data":"0x","from":"0x"#.to_owned() - + &from - + r#"","gas":"0x5208","gasPrice":"0x4a817c800","nonce":"0x0","to":null,"value":"0x5"},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"condition":null,"data":"0x","from":"0x"# + .to_owned() + + &from + + r#"","gas":"0x5208","gasPrice":"0x4a817c800","nonce":"0x0","to":null,"value":"0x5"},"id":1}"#; - // then - let res = tester.io.handle_request(&request).wait().unwrap(); - assert_eq!(res, Some(response.to_owned())); + // then + let res = tester.io.handle_request(&request).wait().unwrap(); + assert_eq!(res, Some(response.to_owned())); } diff --git a/rpc/src/v1/tests/mocked/signing_unsafe.rs b/rpc/src/v1/tests/mocked/signing_unsafe.rs index a91a85ea1fb..bb67b42b883 100644 --- a/rpc/src/v1/tests/mocked/signing_unsafe.rs +++ b/rpc/src/v1/tests/mocked/signing_unsafe.rs @@ -1,100 +1,114 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. -// This file is part of Parity. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity. If not, see . +// along with OpenEthereum. If not, see . -use std::str::FromStr; -use std::sync::Arc; +use std::{str::FromStr, sync::Arc}; use accounts::AccountProvider; use ethcore::client::TestBlockChainClient; -use ethereum_types::{U256, Address}; +use ethereum_types::{Address, U256}; use parity_runtime::Runtime; use parking_lot::Mutex; use rlp; use rustc_hex::ToHex; -use types::transaction::{Transaction, Action}; +use types::transaction::{Action, Transaction}; use jsonrpc_core::IoHandler; -use v1::{EthClientOptions, EthSigning, SigningUnsafeClient}; -use v1::helpers::nonce; -use v1::helpers::dispatch::{self, FullDispatcher}; -use v1::tests::helpers::{TestMinerService}; -use v1::metadata::Metadata; +use v1::{ + helpers::{ + dispatch::{self, FullDispatcher}, + nonce, + }, + metadata::Metadata, + tests::helpers::TestMinerService, + EthClientOptions, EthSigning, SigningUnsafeClient, +}; fn blockchain_client() -> Arc { - let client = TestBlockChainClient::new(); - Arc::new(client) + let client = TestBlockChainClient::new(); + Arc::new(client) } fn accounts_provider() -> Arc { - Arc::new(AccountProvider::transient_provider()) + Arc::new(AccountProvider::transient_provider()) } fn miner_service() -> Arc { - Arc::new(TestMinerService::default()) + Arc::new(TestMinerService::default()) } struct EthTester { - pub runtime: Runtime, - pub client: Arc, - pub accounts_provider: Arc, - pub miner: Arc, - pub io: IoHandler, + pub runtime: Runtime, + pub client: Arc, + pub accounts_provider: Arc, + pub miner: Arc, + pub io: IoHandler, } impl Default for EthTester { - fn default() -> Self { - Self::new_with_options(Default::default()) - } + fn default() -> Self { + Self::new_with_options(Default::default()) + } } impl EthTester { - pub fn new_with_options(options: EthClientOptions) -> Self { - let runtime = Runtime::with_thread_count(1); - let client = blockchain_client(); - let accounts_provider = accounts_provider(); - let ap = Arc::new(dispatch::Signer::new(accounts_provider.clone())) as _; - let miner = miner_service(); - let gas_price_percentile = options.gas_price_percentile; - let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor()))); - - let dispatcher = FullDispatcher::new(client.clone(), miner.clone(), reservations, gas_price_percentile); - let sign = SigningUnsafeClient::new(&ap, dispatcher).to_delegate(); - let mut io: IoHandler = IoHandler::default(); - io.extend_with(sign); - - EthTester { - runtime, - client, - miner, - io, - accounts_provider, - } - } + pub fn new_with_options(options: EthClientOptions) -> Self { + let runtime = Runtime::with_thread_count(1); + let client = blockchain_client(); + let accounts_provider = accounts_provider(); + let ap = Arc::new(dispatch::Signer::new(accounts_provider.clone())) as _; + let miner = miner_service(); + let gas_price_percentile = options.gas_price_percentile; + let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor()))); + + let dispatcher = FullDispatcher::new( + client.clone(), + miner.clone(), + reservations, + gas_price_percentile, + ); + let sign = SigningUnsafeClient::new(&ap, dispatcher).to_delegate(); + let mut io: IoHandler = IoHandler::default(); + io.extend_with(sign); + + EthTester { + runtime, + client, + miner, + io, + accounts_provider, + } + } } #[test] fn rpc_eth_send_transaction() { - let tester = EthTester::default(); - let address = tester.accounts_provider.new_account(&"".into()).unwrap(); - tester.accounts_provider.unlock_account_permanently(address, "".into()).unwrap(); - let request = r#"{ + let tester = EthTester::default(); + let address = tester.accounts_provider.new_account(&"".into()).unwrap(); + tester + .accounts_provider + .unlock_account_permanently(address, "".into()) + .unwrap(); + let request = r#"{ "jsonrpc": "2.0", "method": "eth_sendTransaction", "params": [{ - "from": ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", + "from": ""# + .to_owned() + + format!("0x{:x}", address).as_ref() + + r#"", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "gas": "0x76c0", "gasPrice": "0x9184e72a000", @@ -103,49 +117,69 @@ fn rpc_eth_send_transaction() { "id": 1 }"#; - let t = Transaction { - nonce: U256::zero(), - gas_price: U256::from(0x9184e72a000u64), - gas: U256::from(0x76c0), - action: Action::Call(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), - value: U256::from(0x9184e72au64), - data: vec![] - }; - let signature = tester.accounts_provider.sign(address, None, t.hash(None)).unwrap(); - let t = t.with_signature(signature, None); - - let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:x}", t.hash()).as_ref() + r#"","id":1}"#; - - assert_eq!(tester.io.handle_request_sync(&request), Some(response)); - - tester.miner.increment_nonce(&address); - - let t = Transaction { - nonce: U256::one(), - gas_price: U256::from(0x9184e72a000u64), - gas: U256::from(0x76c0), - action: Action::Call(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), - value: U256::from(0x9184e72au64), - data: vec![] - }; - let signature = tester.accounts_provider.sign(address, None, t.hash(None)).unwrap(); - let t = t.with_signature(signature, None); - - let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:x}", t.hash()).as_ref() + r#"","id":1}"#; - - assert_eq!(tester.io.handle_request_sync(&request), Some(response)); + let t = Transaction { + nonce: U256::zero(), + gas_price: U256::from(0x9184e72a000u64), + gas: U256::from(0x76c0), + action: Action::Call( + Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(), + ), + value: U256::from(0x9184e72au64), + data: vec![], + }; + let signature = tester + .accounts_provider + .sign(address, None, t.hash(None)) + .unwrap(); + let t = t.with_signature(signature, None); + + let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + + format!("0x{:x}", t.hash()).as_ref() + + r#"","id":1}"#; + + assert_eq!(tester.io.handle_request_sync(&request), Some(response)); + + tester.miner.increment_nonce(&address); + + let t = Transaction { + nonce: U256::one(), + gas_price: U256::from(0x9184e72a000u64), + gas: U256::from(0x76c0), + action: Action::Call( + Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(), + ), + value: U256::from(0x9184e72au64), + data: vec![], + }; + let signature = tester + .accounts_provider + .sign(address, None, t.hash(None)) + .unwrap(); + let t = t.with_signature(signature, None); + + let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + + format!("0x{:x}", t.hash()).as_ref() + + r#"","id":1}"#; + + assert_eq!(tester.io.handle_request_sync(&request), Some(response)); } #[test] fn rpc_eth_sign_transaction() { - let tester = EthTester::default(); - let address = tester.accounts_provider.new_account(&"".into()).unwrap(); - tester.accounts_provider.unlock_account_permanently(address, "".into()).unwrap(); - let request = r#"{ + let tester = EthTester::default(); + let address = tester.accounts_provider.new_account(&"".into()).unwrap(); + tester + .accounts_provider + .unlock_account_permanently(address, "".into()) + .unwrap(); + let request = r#"{ "jsonrpc": "2.0", "method": "eth_signTransaction", "params": [{ - "from": ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", + "from": ""# + .to_owned() + + format!("0x{:x}", address).as_ref() + + r#"", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "gas": "0x76c0", "gasPrice": "0x9184e72a000", @@ -154,54 +188,67 @@ fn rpc_eth_sign_transaction() { "id": 1 }"#; - let t = Transaction { - nonce: U256::one(), - gas_price: U256::from(0x9184e72a000u64), - gas: U256::from(0x76c0), - action: Action::Call(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), - value: U256::from(0x9184e72au64), - data: vec![] - }; - let signature = tester.accounts_provider.sign(address, None, t.hash(None)).unwrap(); - let t = t.with_signature(signature, None); - let signature = t.signature(); - let rlp = rlp::encode(&t); - - let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() + - r#""raw":"0x"# + &rlp.to_hex() + r#"","# + - r#""tx":{"# + - r#""blockHash":null,"blockNumber":null,"# + - &format!("\"chainId\":{},", t.chain_id().map_or("null".to_owned(), |n| format!("{}", n))) + - r#""condition":null,"creates":null,"# + - &format!("\"from\":\"0x{:x}\",", &address) + - r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + - &format!("\"hash\":\"0x{:x}\",", t.hash()) + - r#""input":"0x","# + - r#""nonce":"0x1","# + - &format!("\"publicKey\":\"0x{:x}\",", t.recover_public().unwrap()) + - &format!("\"r\":\"0x{:x}\",", U256::from(signature.r())) + - &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + - &format!("\"s\":\"0x{:x}\",", U256::from(signature.s())) + - &format!("\"standardV\":\"0x{:x}\",", U256::from(t.standard_v())) + - r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"# + - &format!("\"v\":\"0x{:x}\",", U256::from(t.original_v())) + - r#""value":"0x9184e72a""# + - r#"}},"id":1}"#; - - tester.miner.increment_nonce(&address); - - assert_eq!(tester.io.handle_request_sync(&request), Some(response)); + let t = Transaction { + nonce: U256::one(), + gas_price: U256::from(0x9184e72a000u64), + gas: U256::from(0x76c0), + action: Action::Call( + Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(), + ), + value: U256::from(0x9184e72au64), + data: vec![], + }; + let signature = tester + .accounts_provider + .sign(address, None, t.hash(None)) + .unwrap(); + let t = t.with_signature(signature, None); + let signature = t.signature(); + let rlp = rlp::encode(&t); + + let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() + + r#""raw":"0x"# + + &rlp.to_hex() + + r#"","# + + r#""tx":{"# + + r#""blockHash":null,"blockNumber":null,"# + + &format!( + "\"chainId\":{},", + t.chain_id().map_or("null".to_owned(), |n| format!("{}", n)) + ) + + r#""condition":null,"creates":null,"# + + &format!("\"from\":\"0x{:x}\",", &address) + + r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + + &format!("\"hash\":\"0x{:x}\",", t.hash()) + + r#""input":"0x","# + + r#""nonce":"0x1","# + + &format!("\"publicKey\":\"0x{:x}\",", t.recover_public().unwrap()) + + &format!("\"r\":\"0x{:x}\",", U256::from(signature.r())) + + &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + + &format!("\"s\":\"0x{:x}\",", U256::from(signature.s())) + + &format!("\"standardV\":\"0x{:x}\",", U256::from(t.standard_v())) + + r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"# + + &format!("\"v\":\"0x{:x}\",", U256::from(t.original_v())) + + r#""value":"0x9184e72a""# + + r#"}},"id":1}"#; + + tester.miner.increment_nonce(&address); + + assert_eq!(tester.io.handle_request_sync(&request), Some(response)); } #[test] fn rpc_eth_send_transaction_with_bad_to() { - let tester = EthTester::default(); - let address = tester.accounts_provider.new_account(&"".into()).unwrap(); - let request = r#"{ + let tester = EthTester::default(); + let address = tester.accounts_provider.new_account(&"".into()).unwrap(); + let request = r#"{ "jsonrpc": "2.0", "method": "eth_sendTransaction", "params": [{ - "from": ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", + "from": ""# + .to_owned() + + format!("0x{:x}", address).as_ref() + + r#"", "to": "", "gas": "0x76c0", "gasPrice": "0x9184e72a000", @@ -210,20 +257,26 @@ fn rpc_eth_send_transaction_with_bad_to() { "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid params: prefix is missing."},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid params: prefix is missing."},"id":1}"#; - assert_eq!(tester.io.handle_request_sync(&request), Some(response.into())); + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.into()) + ); } #[test] fn rpc_eth_send_transaction_error() { - let tester = EthTester::default(); - let address = tester.accounts_provider.new_account(&"".into()).unwrap(); - let request = r#"{ + let tester = EthTester::default(); + let address = tester.accounts_provider.new_account(&"".into()).unwrap(); + let request = r#"{ "jsonrpc": "2.0", "method": "eth_sendTransaction", "params": [{ - "from": ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", + "from": ""# + .to_owned() + + format!("0x{:x}", address).as_ref() + + r#"", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "gas": "0x76c0", "gasPrice": "0x9184e72a000", @@ -232,6 +285,9 @@ fn rpc_eth_send_transaction_error() { "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32020,"message":"Your account is locked. Unlock the account via CLI, personal_unlockAccount or use Trusted Signer.","data":"NotUnlocked"},"id":1}"#; - assert_eq!(tester.io.handle_request_sync(&request), Some(response.into())); + let response = r#"{"jsonrpc":"2.0","error":{"code":-32020,"message":"Your account is locked. Unlock the account via CLI, personal_unlockAccount or use Trusted Signer.","data":"NotUnlocked"},"id":1}"#; + assert_eq!( + tester.io.handle_request_sync(&request), + Some(response.into()) + ); } diff --git a/rpc/src/v1/tests/mocked/traces.rs b/rpc/src/v1/tests/mocked/traces.rs index 89cf198aa1d..34b0a289671 100644 --- a/rpc/src/v1/tests/mocked/traces.rs +++ b/rpc/src/v1/tests/mocked/traces.rs @@ -1,244 +1,295 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use std::sync::Arc; -use ethcore::executed::{Executed, CallError}; -use ethcore::trace::trace::{Action, Res, Call}; -use ethcore::trace::LocalizedTrace; -use ethcore::client::TestBlockChainClient; +use ethcore::{ + client::TestBlockChainClient, + executed::{CallError, Executed}, + trace::{ + trace::{Action, Call, Res}, + LocalizedTrace, + }, +}; use vm::CallType; use jsonrpc_core::IoHandler; -use v1::tests::helpers::{TestMinerService}; -use v1::{Metadata, Traces, TracesClient}; +use v1::{tests::helpers::TestMinerService, Metadata, Traces, TracesClient}; struct Tester { - client: Arc, - _miner: Arc, - io: IoHandler, + client: Arc, + _miner: Arc, + io: IoHandler, } fn io() -> Tester { - let client = Arc::new(TestBlockChainClient::new()); - *client.traces.write() = Some(vec![LocalizedTrace { - action: Action::Call(Call { - from: 0xf.into(), - to: 0x10.into(), - value: 0x1.into(), - gas: 0x100.into(), - input: vec![1, 2, 3], - call_type: CallType::Call, - }), - result: Res::None, - subtraces: 0, - trace_address: vec![0], - transaction_number: Some(0), - transaction_hash: Some(5.into()), - block_number: 10, - block_hash: 10.into(), - }]); - *client.execution_result.write() = Some(Ok(Executed { - exception: None, - gas: 20_000.into(), - gas_used: 10_000.into(), - refunded: 0.into(), - cumulative_gas_used: 10_000.into(), - logs: vec![], - contracts_created: vec![], - output: vec![1, 2, 3], - trace: vec![], - vm_trace: None, - state_diff: None, - })); - let miner = Arc::new(TestMinerService::default()); - let traces = TracesClient::new(&client); - let mut io = IoHandler::default(); - io.extend_with(traces.to_delegate()); - - Tester { - client: client, - _miner: miner, - io: io, - } + let client = Arc::new(TestBlockChainClient::new()); + *client.traces.write() = Some(vec![LocalizedTrace { + action: Action::Call(Call { + from: 0xf.into(), + to: 0x10.into(), + value: 0x1.into(), + gas: 0x100.into(), + input: vec![1, 2, 3], + call_type: CallType::Call, + }), + result: Res::None, + subtraces: 0, + trace_address: vec![0], + transaction_number: Some(0), + transaction_hash: Some(5.into()), + block_number: 10, + block_hash: 10.into(), + }]); + *client.execution_result.write() = Some(Ok(Executed { + exception: None, + gas: 20_000.into(), + gas_used: 10_000.into(), + refunded: 0.into(), + cumulative_gas_used: 10_000.into(), + logs: vec![], + contracts_created: vec![], + output: vec![1, 2, 3], + trace: vec![], + vm_trace: None, + state_diff: None, + })); + let miner = Arc::new(TestMinerService::default()); + let traces = TracesClient::new(&client); + let mut io = IoHandler::default(); + io.extend_with(traces.to_delegate()); + + Tester { + client: client, + _miner: miner, + io: io, + } } #[test] fn rpc_trace_filter() { - let tester = io(); + let tester = io(); - let request = r#"{"jsonrpc":"2.0","method":"trace_filter","params": [{}],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","result":[{"action":{"callType":"call","from":"0x000000000000000000000000000000000000000f","gas":"0x100","input":"0x010203","to":"0x0000000000000000000000000000000000000010","value":"0x1"},"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000a","blockNumber":10,"result":null,"subtraces":0,"traceAddress":[0],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000005","transactionPosition":0,"type":"call"}],"id":1}"#; + let request = r#"{"jsonrpc":"2.0","method":"trace_filter","params": [{}],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":[{"action":{"callType":"call","from":"0x000000000000000000000000000000000000000f","gas":"0x100","input":"0x010203","to":"0x0000000000000000000000000000000000000010","value":"0x1"},"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000a","blockNumber":10,"result":null,"subtraces":0,"traceAddress":[0],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000005","transactionPosition":0,"type":"call"}],"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_trace_filter_missing_trace() { - let tester = io(); - *tester.client.traces.write() = None; + let tester = io(); + *tester.client.traces.write() = None; - let request = r#"{"jsonrpc":"2.0","method":"trace_filter","params": [{}],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; + let request = r#"{"jsonrpc":"2.0","method":"trace_filter","params": [{}],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_trace_block() { - let tester = io(); + let tester = io(); - let request = r#"{"jsonrpc":"2.0","method":"trace_block","params": ["0x10"],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","result":[{"action":{"callType":"call","from":"0x000000000000000000000000000000000000000f","gas":"0x100","input":"0x010203","to":"0x0000000000000000000000000000000000000010","value":"0x1"},"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000a","blockNumber":10,"result":null,"subtraces":0,"traceAddress":[0],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000005","transactionPosition":0,"type":"call"}],"id":1}"#; + let request = r#"{"jsonrpc":"2.0","method":"trace_block","params": ["0x10"],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":[{"action":{"callType":"call","from":"0x000000000000000000000000000000000000000f","gas":"0x100","input":"0x010203","to":"0x0000000000000000000000000000000000000010","value":"0x1"},"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000a","blockNumber":10,"result":null,"subtraces":0,"traceAddress":[0],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000005","transactionPosition":0,"type":"call"}],"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_trace_block_missing_traces() { - let tester = io(); - *tester.client.traces.write() = None; + let tester = io(); + *tester.client.traces.write() = None; - let request = r#"{"jsonrpc":"2.0","method":"trace_block","params": ["0x10"],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; + let request = r#"{"jsonrpc":"2.0","method":"trace_block","params": ["0x10"],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_trace_transaction() { - let tester = io(); + let tester = io(); - let request = r#"{"jsonrpc":"2.0","method":"trace_transaction","params":["0x0000000000000000000000000000000000000000000000000000000000000005"],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","result":[{"action":{"callType":"call","from":"0x000000000000000000000000000000000000000f","gas":"0x100","input":"0x010203","to":"0x0000000000000000000000000000000000000010","value":"0x1"},"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000a","blockNumber":10,"result":null,"subtraces":0,"traceAddress":[0],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000005","transactionPosition":0,"type":"call"}],"id":1}"#; + let request = r#"{"jsonrpc":"2.0","method":"trace_transaction","params":["0x0000000000000000000000000000000000000000000000000000000000000005"],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":[{"action":{"callType":"call","from":"0x000000000000000000000000000000000000000f","gas":"0x100","input":"0x010203","to":"0x0000000000000000000000000000000000000010","value":"0x1"},"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000a","blockNumber":10,"result":null,"subtraces":0,"traceAddress":[0],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000005","transactionPosition":0,"type":"call"}],"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_trace_transaction_missing_trace() { - let tester = io(); - *tester.client.traces.write() = None; + let tester = io(); + *tester.client.traces.write() = None; - let request = r#"{"jsonrpc":"2.0","method":"trace_transaction","params":["0x0000000000000000000000000000000000000000000000000000000000000005"],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; + let request = r#"{"jsonrpc":"2.0","method":"trace_transaction","params":["0x0000000000000000000000000000000000000000000000000000000000000005"],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_trace_get() { - let tester = io(); + let tester = io(); - let request = r#"{"jsonrpc":"2.0","method":"trace_get","params":["0x0000000000000000000000000000000000000000000000000000000000000005", ["0","0","0"]],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"action":{"callType":"call","from":"0x000000000000000000000000000000000000000f","gas":"0x100","input":"0x010203","to":"0x0000000000000000000000000000000000000010","value":"0x1"},"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000a","blockNumber":10,"result":null,"subtraces":0,"traceAddress":[0],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000005","transactionPosition":0,"type":"call"},"id":1}"#; + let request = r#"{"jsonrpc":"2.0","method":"trace_get","params":["0x0000000000000000000000000000000000000000000000000000000000000005", ["0","0","0"]],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"action":{"callType":"call","from":"0x000000000000000000000000000000000000000f","gas":"0x100","input":"0x010203","to":"0x0000000000000000000000000000000000000010","value":"0x1"},"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000a","blockNumber":10,"result":null,"subtraces":0,"traceAddress":[0],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000005","transactionPosition":0,"type":"call"},"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_trace_get_missing_trace() { - let tester = io(); - *tester.client.traces.write() = None; - - let request = r#"{"jsonrpc":"2.0","method":"trace_get","params":["0x0000000000000000000000000000000000000000000000000000000000000005", ["0","0","0"]],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + let tester = io(); + *tester.client.traces.write() = None; + + let request = r#"{"jsonrpc":"2.0","method":"trace_get","params":["0x0000000000000000000000000000000000000000000000000000000000000005", ["0","0","0"]],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_trace_call() { - let tester = io(); + let tester = io(); - let request = r#"{"jsonrpc":"2.0","method":"trace_call","params":[{}, ["stateDiff", "vmTrace", "trace"]],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"output":"0x010203","stateDiff":null,"trace":[],"vmTrace":null},"id":1}"#; + let request = r#"{"jsonrpc":"2.0","method":"trace_call","params":[{}, ["stateDiff", "vmTrace", "trace"]],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"output":"0x010203","stateDiff":null,"trace":[],"vmTrace":null},"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_trace_multi_call() { - let tester = io(); + let tester = io(); - let request = r#"{"jsonrpc":"2.0","method":"trace_callMany","params":[[[{}, ["stateDiff", "vmTrace", "trace"]]]],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","result":[{"output":"0x010203","stateDiff":null,"trace":[],"vmTrace":null}],"id":1}"#; + let request = r#"{"jsonrpc":"2.0","method":"trace_callMany","params":[[[{}, ["stateDiff", "vmTrace", "trace"]]]],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":[{"output":"0x010203","stateDiff":null,"trace":[],"vmTrace":null}],"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_trace_call_state_pruned() { - let tester = io(); - *tester.client.execution_result.write() = Some(Err(CallError::StatePruned)); + let tester = io(); + *tester.client.execution_result.write() = Some(Err(CallError::StatePruned)); - let request = r#"{"jsonrpc":"2.0","method":"trace_call","params":[{}, ["stateDiff", "vmTrace", "trace"]],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not supported because your node is running with state pruning. Run with --pruning=archive."},"id":1}"#; + let request = r#"{"jsonrpc":"2.0","method":"trace_call","params":[{}, ["stateDiff", "vmTrace", "trace"]],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not supported because your node is running with state pruning. Run with --pruning=archive."},"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_trace_raw_transaction() { - let tester = io(); + let tester = io(); - let request = r#"{"jsonrpc":"2.0","method":"trace_rawTransaction","params":["0xf869018609184e72a0008276c094d46e8dd67c5d32be8058bb8eb970870f07244567849184e72a801ba0617f39c1a107b63302449c476d96a6cb17a5842fc98ff0c5bcf4d5c4d8166b95a009fdb6097c6196b9bbafc3a59f02f38d91baeef23d0c60a8e4f23c7714cea3a9", ["stateDiff", "vmTrace", "trace"]],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"output":"0x010203","stateDiff":null,"trace":[],"vmTrace":null},"id":1}"#; + let request = r#"{"jsonrpc":"2.0","method":"trace_rawTransaction","params":["0xf869018609184e72a0008276c094d46e8dd67c5d32be8058bb8eb970870f07244567849184e72a801ba0617f39c1a107b63302449c476d96a6cb17a5842fc98ff0c5bcf4d5c4d8166b95a009fdb6097c6196b9bbafc3a59f02f38d91baeef23d0c60a8e4f23c7714cea3a9", ["stateDiff", "vmTrace", "trace"]],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"output":"0x010203","stateDiff":null,"trace":[],"vmTrace":null},"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_trace_raw_transaction_state_pruned() { - let tester = io(); - *tester.client.execution_result.write() = Some(Err(CallError::StatePruned)); + let tester = io(); + *tester.client.execution_result.write() = Some(Err(CallError::StatePruned)); - let request = r#"{"jsonrpc":"2.0","method":"trace_rawTransaction","params":["0xf869018609184e72a0008276c094d46e8dd67c5d32be8058bb8eb970870f07244567849184e72a801ba0617f39c1a107b63302449c476d96a6cb17a5842fc98ff0c5bcf4d5c4d8166b95a009fdb6097c6196b9bbafc3a59f02f38d91baeef23d0c60a8e4f23c7714cea3a9", ["stateDiff", "vmTrace", "trace"]],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not supported because your node is running with state pruning. Run with --pruning=archive."},"id":1}"#; + let request = r#"{"jsonrpc":"2.0","method":"trace_rawTransaction","params":["0xf869018609184e72a0008276c094d46e8dd67c5d32be8058bb8eb970870f07244567849184e72a801ba0617f39c1a107b63302449c476d96a6cb17a5842fc98ff0c5bcf4d5c4d8166b95a009fdb6097c6196b9bbafc3a59f02f38d91baeef23d0c60a8e4f23c7714cea3a9", ["stateDiff", "vmTrace", "trace"]],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not supported because your node is running with state pruning. Run with --pruning=archive."},"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_trace_replay_transaction() { - let tester = io(); + let tester = io(); - let request = r#"{"jsonrpc":"2.0","method":"trace_replayTransaction","params":["0x0000000000000000000000000000000000000000000000000000000000000005", ["trace", "stateDiff", "vmTrace"]],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"output":"0x010203","stateDiff":null,"trace":[],"vmTrace":null},"id":1}"#; + let request = r#"{"jsonrpc":"2.0","method":"trace_replayTransaction","params":["0x0000000000000000000000000000000000000000000000000000000000000005", ["trace", "stateDiff", "vmTrace"]],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"output":"0x010203","stateDiff":null,"trace":[],"vmTrace":null},"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_trace_replay_transaction_state_pruned() { - let tester = io(); - *tester.client.execution_result.write() = Some(Err(CallError::StatePruned)); + let tester = io(); + *tester.client.execution_result.write() = Some(Err(CallError::StatePruned)); - let request = r#"{"jsonrpc":"2.0","method":"trace_replayTransaction","params":["0x0000000000000000000000000000000000000000000000000000000000000005", ["trace", "stateDiff", "vmTrace"]],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not supported because your node is running with state pruning. Run with --pruning=archive."},"id":1}"#; + let request = r#"{"jsonrpc":"2.0","method":"trace_replayTransaction","params":["0x0000000000000000000000000000000000000000000000000000000000000005", ["trace", "stateDiff", "vmTrace"]],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not supported because your node is running with state pruning. Run with --pruning=archive."},"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } #[test] fn rpc_trace_replay_block_transactions() { - let tester = io(); + let tester = io(); - let request = r#"{"jsonrpc":"2.0","method":"trace_replayBlockTransactions","params":["0x10", ["trace", "stateDiff", "vmTrace"]],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","result":[{"output":"0x010203","stateDiff":null,"trace":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000005","vmTrace":null}],"id":1}"#; + let request = r#"{"jsonrpc":"2.0","method":"trace_replayBlockTransactions","params":["0x10", ["trace", "stateDiff", "vmTrace"]],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":[{"output":"0x010203","stateDiff":null,"trace":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000005","vmTrace":null}],"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!( + tester.io.handle_request_sync(request), + Some(response.to_owned()) + ); } diff --git a/rpc/src/v1/tests/mocked/web3.rs b/rpc/src/v1/tests/mocked/web3.rs index 5590d5d2832..61158db3e47 100644 --- a/rpc/src/v1/tests/mocked/web3.rs +++ b/rpc/src/v1/tests/mocked/web3.rs @@ -1,57 +1,59 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use jsonrpc_core::IoHandler; -use version::version; use v1::{Web3, Web3Client}; +use version::version; #[test] fn rpc_web3_version() { - let web3 = Web3Client::default().to_delegate(); - let mut io = IoHandler::new(); - io.extend_with(web3); + let web3 = Web3Client::default().to_delegate(); + let mut io = IoHandler::new(); + io.extend_with(web3); - let v = version().to_owned().replacen("/", "//", 1); + let v = version().to_owned().replacen("/", "//", 1); - let request = r#"{"jsonrpc": "2.0", "method": "web3_clientVersion", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"VER","id":1}"#.to_owned().replace("VER", v.as_ref()); + let request = r#"{"jsonrpc": "2.0", "method": "web3_clientVersion", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"VER","id":1}"# + .to_owned() + .replace("VER", v.as_ref()); - assert_eq!(io.handle_request_sync(request), Some(response)); + assert_eq!(io.handle_request_sync(request), Some(response)); } #[test] fn rpc_web3_sha3() { - let web3 = Web3Client::default().to_delegate(); - let mut io = IoHandler::new(); - io.extend_with(web3); + let web3 = Web3Client::default().to_delegate(); + let mut io = IoHandler::new(); + io.extend_with(web3); - let request = r#"{"jsonrpc": "2.0", "method": "web3_sha3", "params": ["0x00"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a","id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "web3_sha3", "params": ["0x00"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a","id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } #[test] fn rpc_web3_sha3_wiki() { - let web3 = Web3Client::default().to_delegate(); - let mut io = IoHandler::new(); - io.extend_with(web3); + let web3 = Web3Client::default().to_delegate(); + let mut io = IoHandler::new(); + io.extend_with(web3); - let request = r#"{"jsonrpc": "2.0", "method": "web3_sha3", "params": ["0x68656c6c6f20776f726c64"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad","id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "web3_sha3", "params": ["0x68656c6c6f20776f726c64"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad","id":1}"#; - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } diff --git a/rpc/src/v1/tests/mod.rs b/rpc/src/v1/tests/mod.rs index 83f9dca905f..e695bc4cc57 100644 --- a/rpc/src/v1/tests/mod.rs +++ b/rpc/src/v1/tests/mod.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! RPC unit test moduleS @@ -31,7 +31,7 @@ pub mod helpers; macro_rules! extract_chain { (iter $file:expr) => {{ const RAW_DATA: &'static [u8] = - include_bytes!(concat!("../../../../ethcore/res/ethereum/tests/", $file, ".json")); + include_bytes!(concat!("../../../../ethcore/res/ethereum/tests/LegacyTests/Constantinople/", $file, ".json")); ::ethjson::blockchain::Test::load(RAW_DATA).unwrap().into_iter() }}; @@ -51,7 +51,7 @@ macro_rules! register_test { }; } -#[cfg(test)] -mod mocked; #[cfg(test)] mod eth; +#[cfg(test)] +mod mocked; diff --git a/rpc/src/v1/traits/debug.rs b/rpc/src/v1/traits/debug.rs index 5d332d434ad..98687c3d29d 100644 --- a/rpc/src/v1/traits/debug.rs +++ b/rpc/src/v1/traits/debug.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Debug RPC interface. @@ -22,9 +22,9 @@ use jsonrpc_derive::rpc; use v1::types::RichBlock; /// Debug RPC interface. -#[rpc] +#[rpc(server)] pub trait Debug { - /// Returns recently seen bad blocks. - #[rpc(name = "debug_getBadBlocks")] - fn bad_blocks(&self) -> Result>; + /// Returns recently seen bad blocks. + #[rpc(name = "debug_getBadBlocks")] + fn bad_blocks(&self) -> Result>; } diff --git a/rpc/src/v1/traits/eth.rs b/rpc/src/v1/traits/eth.rs index 69a37ae5264..86b60c0bd70 100644 --- a/rpc/src/v1/traits/eth.rs +++ b/rpc/src/v1/traits/eth.rs @@ -1,217 +1,231 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Eth rpc interface. -use jsonrpc_core::{Result, BoxFuture}; +use ethereum_types::{H160, H256, H64, U256, U64}; +use jsonrpc_core::{BoxFuture, Result}; use jsonrpc_derive::rpc; -use ethereum_types::{H64, H160, H256, U64, U256}; -use v1::types::{RichBlock, BlockNumber, Bytes, CallRequest, Filter, FilterChanges, Index, EthAccount}; -use v1::types::{Log, Receipt, SyncStatus, Transaction, Work}; +use v1::types::{ + BlockNumber, Bytes, CallRequest, EthAccount, Filter, FilterChanges, Index, Log, Receipt, + RichBlock, SyncStatus, Transaction, Work, +}; /// Eth rpc interface. -#[rpc] +#[rpc(server)] pub trait Eth { - /// RPC Metadata - type Metadata; + /// RPC Metadata + type Metadata; - /// Returns protocol version encoded as a string (quotes are necessary). - #[rpc(name = "eth_protocolVersion")] - fn protocol_version(&self) -> Result; + /// Returns protocol version encoded as a string (quotes are necessary). + #[rpc(name = "eth_protocolVersion")] + fn protocol_version(&self) -> Result; - /// Returns an object with data about the sync status or false. (wtf?) - #[rpc(name = "eth_syncing")] - fn syncing(&self) -> Result; - - /// Returns the number of hashes per second that the node is mining with. - #[rpc(name = "eth_hashrate")] - fn hashrate(&self) -> Result; - - /// Returns block author. - #[rpc(name = "eth_coinbase")] - fn author(&self) -> Result; - - /// Returns true if client is actively mining new blocks. - #[rpc(name = "eth_mining")] - fn is_mining(&self) -> Result; - - /// Returns the chain ID used for transaction signing at the - /// current best block. None is returned if not - /// available. - #[rpc(name = "eth_chainId")] - fn chain_id(&self) -> Result>; - - /// Returns current gas_price. - #[rpc(name = "eth_gasPrice")] - fn gas_price(&self) -> BoxFuture; - - /// Returns accounts list. - #[rpc(name = "eth_accounts")] - fn accounts(&self) -> Result>; - - /// Returns highest block number. - #[rpc(name = "eth_blockNumber")] - fn block_number(&self) -> Result; - - /// Returns balance of the given account. - #[rpc(name = "eth_getBalance")] - fn balance(&self, H160, Option) -> BoxFuture; - - /// Returns the account- and storage-values of the specified account including the Merkle-proof - #[rpc(name = "eth_getProof")] - fn proof(&self, H160, Vec, Option) -> BoxFuture; - - /// Returns content of the storage at given address. - #[rpc(name = "eth_getStorageAt")] - fn storage_at(&self, H160, U256, Option) -> BoxFuture; - - /// Returns block with given hash. - #[rpc(name = "eth_getBlockByHash")] - fn block_by_hash(&self, H256, bool) -> BoxFuture>; - - /// Returns block with given number. - #[rpc(name = "eth_getBlockByNumber")] - fn block_by_number(&self, BlockNumber, bool) -> BoxFuture>; - - /// Returns the number of transactions sent from given address at given time (block number). - #[rpc(name = "eth_getTransactionCount")] - fn transaction_count(&self, H160, Option) -> BoxFuture; - - /// Returns the number of transactions in a block with given hash. - #[rpc(name = "eth_getBlockTransactionCountByHash")] - fn block_transaction_count_by_hash(&self, H256) -> BoxFuture>; - - /// Returns the number of transactions in a block with given block number. - #[rpc(name = "eth_getBlockTransactionCountByNumber")] - fn block_transaction_count_by_number(&self, BlockNumber) -> BoxFuture>; - - /// Returns the number of uncles in a block with given hash. - #[rpc(name = "eth_getUncleCountByBlockHash")] - fn block_uncles_count_by_hash(&self, H256) -> BoxFuture>; - - /// Returns the number of uncles in a block with given block number. - #[rpc(name = "eth_getUncleCountByBlockNumber")] - fn block_uncles_count_by_number(&self, BlockNumber) -> BoxFuture>; - - /// Returns the code at given address at given time (block number). - #[rpc(name = "eth_getCode")] - fn code_at(&self, H160, Option) -> BoxFuture; - - /// Sends signed transaction, returning its hash. - #[rpc(name = "eth_sendRawTransaction")] - fn send_raw_transaction(&self, Bytes) -> Result; - - /// @alias of `eth_sendRawTransaction`. - #[rpc(name = "eth_submitTransaction")] - fn submit_transaction(&self, Bytes) -> Result; - - /// Call contract, returning the output data. - #[rpc(name = "eth_call")] - fn call(&self, CallRequest, Option) -> BoxFuture; - - /// Estimate gas needed for execution of given contract. - #[rpc(name = "eth_estimateGas")] - fn estimate_gas(&self, CallRequest, Option) -> BoxFuture; - - /// Get transaction by its hash. - #[rpc(name = "eth_getTransactionByHash")] - fn transaction_by_hash(&self, H256) -> BoxFuture>; - - /// Returns transaction at given block hash and index. - #[rpc(name = "eth_getTransactionByBlockHashAndIndex")] - fn transaction_by_block_hash_and_index(&self, H256, Index) -> BoxFuture>; - - /// Returns transaction by given block number and index. - #[rpc(name = "eth_getTransactionByBlockNumberAndIndex")] - fn transaction_by_block_number_and_index(&self, BlockNumber, Index) -> BoxFuture>; - - /// Returns transaction receipt by transaction hash. - #[rpc(name = "eth_getTransactionReceipt")] - fn transaction_receipt(&self, H256) -> BoxFuture>; - - /// Returns an uncles at given block and index. - #[rpc(name = "eth_getUncleByBlockHashAndIndex")] - fn uncle_by_block_hash_and_index(&self, H256, Index) -> BoxFuture>; - - /// Returns an uncles at given block and index. - #[rpc(name = "eth_getUncleByBlockNumberAndIndex")] - fn uncle_by_block_number_and_index(&self, BlockNumber, Index) -> BoxFuture>; - - /// Returns available compilers. - /// @deprecated - #[rpc(name = "eth_getCompilers")] - fn compilers(&self) -> Result>; - - /// Compiles lll code. - /// @deprecated - #[rpc(name = "eth_compileLLL")] - fn compile_lll(&self, String) -> Result; - - /// Compiles solidity. - /// @deprecated - #[rpc(name = "eth_compileSolidity")] - fn compile_solidity(&self, String) -> Result; - - /// Compiles serpent. - /// @deprecated - #[rpc(name = "eth_compileSerpent")] - fn compile_serpent(&self, String) -> Result; - - /// Returns logs matching given filter object. - #[rpc(name = "eth_getLogs")] - fn logs(&self, Filter) -> BoxFuture>; - - /// Returns the hash of the current block, the seedHash, and the boundary condition to be met. - #[rpc(name = "eth_getWork")] - fn work(&self, Option) -> Result; - - /// Used for submitting a proof-of-work solution. - #[rpc(name = "eth_submitWork")] - fn submit_work(&self, H64, H256, H256) -> Result; - - /// Used for submitting mining hashrate. - #[rpc(name = "eth_submitHashrate")] - fn submit_hashrate(&self, U256, H256) -> Result; + /// Returns an object with data about the sync status or false. (wtf?) + #[rpc(name = "eth_syncing")] + fn syncing(&self) -> Result; + + /// Returns the number of hashes per second that the node is mining with. + #[rpc(name = "eth_hashrate")] + fn hashrate(&self) -> Result; + + /// Returns block author. + #[rpc(name = "eth_coinbase")] + fn author(&self) -> Result; + + /// Returns true if client is actively mining new blocks. + #[rpc(name = "eth_mining")] + fn is_mining(&self) -> Result; + + /// Returns the chain ID used for transaction signing at the + /// current best block. None is returned if not + /// available. + #[rpc(name = "eth_chainId")] + fn chain_id(&self) -> Result>; + + /// Returns current gas_price. + #[rpc(name = "eth_gasPrice")] + fn gas_price(&self) -> BoxFuture; + + /// Returns accounts list. + #[rpc(name = "eth_accounts")] + fn accounts(&self) -> Result>; + + /// Returns highest block number. + #[rpc(name = "eth_blockNumber")] + fn block_number(&self) -> Result; + + /// Returns balance of the given account. + #[rpc(name = "eth_getBalance")] + fn balance(&self, _: H160, _: Option) -> BoxFuture; + + /// Returns the account- and storage-values of the specified account including the Merkle-proof + #[rpc(name = "eth_getProof")] + fn proof(&self, _: H160, _: Vec, _: Option) -> BoxFuture; + + /// Returns content of the storage at given address. + #[rpc(name = "eth_getStorageAt")] + fn storage_at(&self, _: H160, _: U256, _: Option) -> BoxFuture; + + /// Returns block with given hash. + #[rpc(name = "eth_getBlockByHash")] + fn block_by_hash(&self, _: H256, _: bool) -> BoxFuture>; + + /// Returns block with given number. + #[rpc(name = "eth_getBlockByNumber")] + fn block_by_number(&self, _: BlockNumber, _: bool) -> BoxFuture>; + + /// Returns the number of transactions sent from given address at given time (block number). + #[rpc(name = "eth_getTransactionCount")] + fn transaction_count(&self, _: H160, _: Option) -> BoxFuture; + + /// Returns the number of transactions in a block with given hash. + #[rpc(name = "eth_getBlockTransactionCountByHash")] + fn block_transaction_count_by_hash(&self, _: H256) -> BoxFuture>; + + /// Returns the number of transactions in a block with given block number. + #[rpc(name = "eth_getBlockTransactionCountByNumber")] + fn block_transaction_count_by_number(&self, _: BlockNumber) -> BoxFuture>; + + /// Returns the number of uncles in a block with given hash. + #[rpc(name = "eth_getUncleCountByBlockHash")] + fn block_uncles_count_by_hash(&self, _: H256) -> BoxFuture>; + + /// Returns the number of uncles in a block with given block number. + #[rpc(name = "eth_getUncleCountByBlockNumber")] + fn block_uncles_count_by_number(&self, _: BlockNumber) -> BoxFuture>; + + /// Returns the code at given address at given time (block number). + #[rpc(name = "eth_getCode")] + fn code_at(&self, _: H160, _: Option) -> BoxFuture; + + /// Sends signed transaction, returning its hash. + #[rpc(name = "eth_sendRawTransaction")] + fn send_raw_transaction(&self, _: Bytes) -> Result; + + /// @alias of `eth_sendRawTransaction`. + #[rpc(name = "eth_submitTransaction")] + fn submit_transaction(&self, _: Bytes) -> Result; + + /// Call contract, returning the output data. + #[rpc(name = "eth_call")] + fn call(&self, _: CallRequest, _: Option) -> BoxFuture; + + /// Estimate gas needed for execution of given contract. + #[rpc(name = "eth_estimateGas")] + fn estimate_gas(&self, _: CallRequest, _: Option) -> BoxFuture; + + /// Get transaction by its hash. + #[rpc(name = "eth_getTransactionByHash")] + fn transaction_by_hash(&self, _: H256) -> BoxFuture>; + + /// Returns transaction at given block hash and index. + #[rpc(name = "eth_getTransactionByBlockHashAndIndex")] + fn transaction_by_block_hash_and_index( + &self, + _: H256, + _: Index, + ) -> BoxFuture>; + + /// Returns transaction by given block number and index. + #[rpc(name = "eth_getTransactionByBlockNumberAndIndex")] + fn transaction_by_block_number_and_index( + &self, + _: BlockNumber, + _: Index, + ) -> BoxFuture>; + + /// Returns transaction receipt by transaction hash. + #[rpc(name = "eth_getTransactionReceipt")] + fn transaction_receipt(&self, _: H256) -> BoxFuture>; + + /// Returns an uncles at given block and index. + #[rpc(name = "eth_getUncleByBlockHashAndIndex")] + fn uncle_by_block_hash_and_index(&self, _: H256, _: Index) -> BoxFuture>; + + /// Returns an uncles at given block and index. + #[rpc(name = "eth_getUncleByBlockNumberAndIndex")] + fn uncle_by_block_number_and_index( + &self, + _: BlockNumber, + _: Index, + ) -> BoxFuture>; + + /// Returns available compilers. + /// @deprecated + #[rpc(name = "eth_getCompilers")] + fn compilers(&self) -> Result>; + + /// Compiles lll code. + /// @deprecated + #[rpc(name = "eth_compileLLL")] + fn compile_lll(&self, _: String) -> Result; + + /// Compiles solidity. + /// @deprecated + #[rpc(name = "eth_compileSolidity")] + fn compile_solidity(&self, _: String) -> Result; + + /// Compiles serpent. + /// @deprecated + #[rpc(name = "eth_compileSerpent")] + fn compile_serpent(&self, _: String) -> Result; + + /// Returns logs matching given filter object. + #[rpc(name = "eth_getLogs")] + fn logs(&self, _: Filter) -> BoxFuture>; + + /// Returns the hash of the current block, the seedHash, and the boundary condition to be met. + #[rpc(name = "eth_getWork")] + fn work(&self, _: Option) -> Result; + + /// Used for submitting a proof-of-work solution. + #[rpc(name = "eth_submitWork")] + fn submit_work(&self, _: H64, _: H256, _: H256) -> Result; + + /// Used for submitting mining hashrate. + #[rpc(name = "eth_submitHashrate")] + fn submit_hashrate(&self, _: U256, _: H256) -> Result; } /// Eth filters rpc api (polling). // TODO: do filters api properly -#[rpc] +#[rpc(server)] pub trait EthFilter { - /// Returns id of new filter. - #[rpc(name = "eth_newFilter")] - fn new_filter(&self, Filter) -> Result; + /// Returns id of new filter. + #[rpc(name = "eth_newFilter")] + fn new_filter(&self, _: Filter) -> Result; - /// Returns id of new block filter. - #[rpc(name = "eth_newBlockFilter")] - fn new_block_filter(&self) -> Result; + /// Returns id of new block filter. + #[rpc(name = "eth_newBlockFilter")] + fn new_block_filter(&self) -> Result; - /// Returns id of new block filter. - #[rpc(name = "eth_newPendingTransactionFilter")] - fn new_pending_transaction_filter(&self) -> Result; + /// Returns id of new block filter. + #[rpc(name = "eth_newPendingTransactionFilter")] + fn new_pending_transaction_filter(&self) -> Result; - /// Returns filter changes since last poll. - #[rpc(name = "eth_getFilterChanges")] - fn filter_changes(&self, Index) -> BoxFuture; + /// Returns filter changes since last poll. + #[rpc(name = "eth_getFilterChanges")] + fn filter_changes(&self, _: Index) -> BoxFuture; - /// Returns all logs matching given filter (in a range 'from' - 'to'). - #[rpc(name = "eth_getFilterLogs")] - fn filter_logs(&self, Index) -> BoxFuture>; + /// Returns all logs matching given filter (in a range 'from' - 'to'). + #[rpc(name = "eth_getFilterLogs")] + fn filter_logs(&self, _: Index) -> BoxFuture>; - /// Uninstalls filter. - #[rpc(name = "eth_uninstallFilter")] - fn uninstall_filter(&self, Index) -> Result; + /// Uninstalls filter. + #[rpc(name = "eth_uninstallFilter")] + fn uninstall_filter(&self, _: Index) -> Result; } diff --git a/rpc/src/v1/traits/eth_pubsub.rs b/rpc/src/v1/traits/eth_pubsub.rs index 06287813921..06b9fa27942 100644 --- a/rpc/src/v1/traits/eth_pubsub.rs +++ b/rpc/src/v1/traits/eth_pubsub.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Eth PUB-SUB rpc interface. @@ -23,16 +23,26 @@ use jsonrpc_pubsub::{typed, SubscriptionId}; use v1::types::pubsub; /// Eth PUB-SUB rpc interface. -#[rpc] +#[rpc(server)] pub trait EthPubSub { - /// RPC Metadata - type Metadata; - - /// Subscribe to Eth subscription. - #[pubsub(subscription = "eth_subscription", subscribe, name = "eth_subscribe")] - fn subscribe(&self, Self::Metadata, typed::Subscriber, pubsub::Kind, Option); - - /// Unsubscribe from existing Eth subscription. - #[pubsub(subscription = "eth_subscription", unsubscribe, name = "eth_unsubscribe")] - fn unsubscribe(&self, Option, SubscriptionId) -> Result; + /// RPC Metadata + type Metadata; + + /// Subscribe to Eth subscription. + #[pubsub(subscription = "eth_subscription", subscribe, name = "eth_subscribe")] + fn subscribe( + &self, + _: Self::Metadata, + _: typed::Subscriber, + _: pubsub::Kind, + _: Option, + ); + + /// Unsubscribe from existing Eth subscription. + #[pubsub( + subscription = "eth_subscription", + unsubscribe, + name = "eth_unsubscribe" + )] + fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; } diff --git a/rpc/src/v1/traits/eth_signing.rs b/rpc/src/v1/traits/eth_signing.rs index 72e13ddabe6..1622fcb9562 100644 --- a/rpc/src/v1/traits/eth_signing.rs +++ b/rpc/src/v1/traits/eth_signing.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Eth rpc interface. @@ -20,27 +20,31 @@ use jsonrpc_core::BoxFuture; use jsonrpc_derive::rpc; use ethereum_types::{H160, H256, H520}; -use v1::types::{Bytes, TransactionRequest, RichRawTransaction}; +use v1::types::{Bytes, RichRawTransaction, TransactionRequest}; /// Signing methods implementation relying on unlocked accounts. -#[rpc] +#[rpc(server)] pub trait EthSigning { - /// RPC Metadata - type Metadata; - - /// Signs the hash of data with given address signature. - #[rpc(meta, name = "eth_sign")] - fn sign(&self, Self::Metadata, H160, Bytes) -> BoxFuture; - - /// Sends transaction; will block waiting for signer to return the - /// transaction hash. - /// If Signer is disable it will require the account to be unlocked. - #[rpc(meta, name = "eth_sendTransaction")] - fn send_transaction(&self, Self::Metadata, TransactionRequest) -> BoxFuture; - - /// Signs transactions without dispatching it to the network. - /// Returns signed transaction RLP representation and the transaction itself. - /// It can be later submitted using `eth_sendRawTransaction/eth_submitTransaction`. - #[rpc(meta, name = "eth_signTransaction")] - fn sign_transaction(&self, Self::Metadata, TransactionRequest) -> BoxFuture; + /// RPC Metadata + type Metadata; + + /// Signs the hash of data with given address signature. + #[rpc(meta, name = "eth_sign")] + fn sign(&self, _: Self::Metadata, _: H160, _: Bytes) -> BoxFuture; + + /// Sends transaction; will block waiting for signer to return the + /// transaction hash. + /// If Signer is disable it will require the account to be unlocked. + #[rpc(meta, name = "eth_sendTransaction")] + fn send_transaction(&self, _: Self::Metadata, _: TransactionRequest) -> BoxFuture; + + /// Signs transactions without dispatching it to the network. + /// Returns signed transaction RLP representation and the transaction itself. + /// It can be later submitted using `eth_sendRawTransaction/eth_submitTransaction`. + #[rpc(meta, name = "eth_signTransaction")] + fn sign_transaction( + &self, + _: Self::Metadata, + _: TransactionRequest, + ) -> BoxFuture; } diff --git a/rpc/src/v1/traits/mod.rs b/rpc/src/v1/traits/mod.rs index e25ca76ac44..046d54cb126 100644 --- a/rpc/src/v1/traits/mod.rs +++ b/rpc/src/v1/traits/mod.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Ethereum rpc interfaces. @@ -26,28 +26,26 @@ pub mod parity_accounts; pub mod parity_set; pub mod parity_signing; pub mod personal; -pub mod private; pub mod pubsub; -pub mod rpc; pub mod secretstore; pub mod signer; pub mod traces; pub mod web3; -pub use self::debug::Debug; -pub use self::eth::{Eth, EthFilter}; -pub use self::eth_pubsub::EthPubSub; -pub use self::eth_signing::EthSigning; -pub use self::net::Net; -pub use self::parity::Parity; -pub use self::parity_accounts::{ParityAccounts, ParityAccountsInfo}; -pub use self::parity_set::{ParitySet, ParitySetAccounts}; -pub use self::parity_signing::ParitySigning; -pub use self::personal::Personal; -pub use self::private::Private; -pub use self::pubsub::PubSub; -pub use self::rpc::Rpc; -pub use self::secretstore::SecretStore; -pub use self::signer::Signer; -pub use self::traces::Traces; -pub use self::web3::Web3; +pub use self::{ + debug::Debug, + eth::{Eth, EthFilter}, + eth_pubsub::EthPubSub, + eth_signing::EthSigning, + net::Net, + parity::Parity, + parity_accounts::{ParityAccounts, ParityAccountsInfo}, + parity_set::{ParitySet, ParitySetAccounts}, + parity_signing::ParitySigning, + personal::Personal, + pubsub::PubSub, + secretstore::SecretStore, + signer::Signer, + traces::Traces, + web3::Web3, +}; diff --git a/rpc/src/v1/traits/net.rs b/rpc/src/v1/traits/net.rs index a16729294d7..7c04e542335 100644 --- a/rpc/src/v1/traits/net.rs +++ b/rpc/src/v1/traits/net.rs @@ -1,36 +1,36 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Net rpc interface. use jsonrpc_core::Result; use jsonrpc_derive::rpc; /// Net rpc interface. -#[rpc] +#[rpc(server)] pub trait Net { - /// Returns protocol version. - #[rpc(name = "net_version")] - fn version(&self) -> Result; + /// Returns protocol version. + #[rpc(name = "net_version")] + fn version(&self) -> Result; - /// Returns number of peers connected to node. - #[rpc(name = "net_peerCount")] - fn peer_count(&self) -> Result; + /// Returns number of peers connected to node. + #[rpc(name = "net_peerCount")] + fn peer_count(&self) -> Result; - /// Returns true if client is actively listening for network connections. - /// Otherwise false. - #[rpc(name = "net_listening")] - fn is_listening(&self) -> Result; + /// Returns true if client is actively listening for network connections. + /// Otherwise false. + #[rpc(name = "net_listening")] + fn is_listening(&self) -> Result; } diff --git a/rpc/src/v1/traits/parity.rs b/rpc/src/v1/traits/parity.rs index e3821355eee..d8d37468148 100644 --- a/rpc/src/v1/traits/parity.rs +++ b/rpc/src/v1/traits/parity.rs @@ -1,235 +1,228 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Parity-specific rpc interface. use std::collections::BTreeMap; -use ethereum_types::{H64, H160, H256, H512, U64, U256}; +use ethereum_types::{H160, H256, H512, H64, U256, U64}; use jsonrpc_core::{BoxFuture, Result}; use jsonrpc_derive::rpc; use v1::types::{ - Bytes, CallRequest, - Peers, Transaction, RpcSettings, Histogram, RecoveredAccount, - TransactionStats, LocalTransactionStatus, - BlockNumber, ConsensusCapability, VersionInfo, - OperationsInfo, ChainStatus, Log, Filter, - RichHeader, Receipt, + BlockNumber, Bytes, CallRequest, ChainStatus, Histogram, LocalTransactionStatus, Peers, + Receipt, RecoveredAccount, RichHeader, RpcSettings, Transaction, TransactionStats, }; /// Parity-specific rpc interface. -#[rpc] +#[rpc(server)] pub trait Parity { - /// RPC Metadata - type Metadata; - - /// Returns current transactions limit. - #[rpc(name = "parity_transactionsLimit")] - fn transactions_limit(&self) -> Result; - - /// Returns mining extra data. - #[rpc(name = "parity_extraData")] - fn extra_data(&self) -> Result; - - /// Returns mining gas floor target. - #[rpc(name = "parity_gasFloorTarget")] - fn gas_floor_target(&self) -> Result; - - /// Returns mining gas floor cap. - #[rpc(name = "parity_gasCeilTarget")] - fn gas_ceil_target(&self) -> Result; - - /// Returns minimal gas price for transaction to be included in queue. - #[rpc(name = "parity_minGasPrice")] - fn min_gas_price(&self) -> Result; - - /// Returns latest logs - #[rpc(name = "parity_devLogs")] - fn dev_logs(&self) -> Result>; - - /// Returns logs levels - #[rpc(name = "parity_devLogsLevels")] - fn dev_logs_levels(&self) -> Result; - - /// Returns chain name - DEPRECATED. Use `parity_chainName` instead. - #[rpc(name = "parity_netChain")] - fn net_chain(&self) -> Result; - - /// Returns peers details - #[rpc(name = "parity_netPeers")] - fn net_peers(&self) -> Result; - - /// Returns network port - #[rpc(name = "parity_netPort")] - fn net_port(&self) -> Result; - - /// Returns rpc settings - #[rpc(name = "parity_rpcSettings")] - fn rpc_settings(&self) -> Result; - - /// Returns node name - #[rpc(name = "parity_nodeName")] - fn node_name(&self) -> Result; - - /// Returns default extra data - #[rpc(name = "parity_defaultExtraData")] - fn default_extra_data(&self) -> Result; - - /// Returns distribution of gas price in latest blocks. - #[rpc(name = "parity_gasPriceHistogram")] - fn gas_price_histogram(&self) -> BoxFuture; - - /// Returns number of unsigned transactions waiting in the signer queue (if signer enabled) - /// Returns error when signer is disabled - #[rpc(name = "parity_unsignedTransactionsCount")] - fn unsigned_transactions_count(&self) -> Result; - - /// Returns a cryptographically random phrase sufficient for securely seeding a secret key. - #[rpc(name = "parity_generateSecretPhrase")] - fn generate_secret_phrase(&self) -> Result; - - /// Returns whatever address would be derived from the given phrase if it were to seed a brainwallet. - #[rpc(name = "parity_phraseToAddress")] - fn phrase_to_address(&self, String) -> Result; - - /// Returns the value of the registrar for this network. - #[rpc(name = "parity_registryAddress")] - fn registry_address(&self) -> Result>; - - /// Returns all addresses if Fat DB is enabled (`--fat-db`), or null if not. - #[rpc(name = "parity_listAccounts")] - fn list_accounts(&self, u64, Option, Option) -> Result>>; - - /// Returns all storage keys of the given address (first parameter) if Fat DB is enabled (`--fat-db`), - /// or null if not. - #[rpc(name = "parity_listStorageKeys")] - fn list_storage_keys(&self, H160, u64, Option, Option) -> Result>>; - - /// Encrypt some data with a public key under ECIES. - /// First parameter is the 512-byte destination public key, second is the message. - #[rpc(name = "parity_encryptMessage")] - fn encrypt_message(&self, H512, Bytes) -> Result; - - /// Returns all pending transactions from transaction queue. - #[rpc(name = "parity_pendingTransactions")] - fn pending_transactions(&self, Option) -> Result>; - - /// Returns all transactions from transaction queue. - /// - /// Some of them might not be ready to be included in a block yet. - #[rpc(name = "parity_allTransactions")] - fn all_transactions(&self) -> Result>; - - /// Same as parity_allTransactions, but return only transactions hashes. - #[rpc(name = "parity_allTransactionHashes")] - fn all_transaction_hashes(&self) -> Result>; - - /// Returns all future transactions from transaction queue (deprecated) - #[rpc(name = "parity_futureTransactions")] - fn future_transactions(&self) -> Result>; - - /// Returns propagation statistics on transactions pending in the queue. - #[rpc(name = "parity_pendingTransactionsStats")] - fn pending_transactions_stats(&self) -> Result>; - - /// Returns a list of current and past local transactions with status details. - #[rpc(name = "parity_localTransactions")] - fn local_transactions(&self) -> Result>; - - /// Returns current WS Server interface and port or an error if ws server is disabled. - #[rpc(name = "parity_wsUrl")] - fn ws_url(&self) -> Result; - - /// Returns next nonce for particular sender. Should include all transactions in the queue. - #[rpc(name = "parity_nextNonce")] - fn next_nonce(&self, H160) -> BoxFuture; - - /// Get the mode. Returns one of: "active", "passive", "dark", "offline". - #[rpc(name = "parity_mode")] - fn mode(&self) -> Result; - - /// Get the chain name. Returns one of the pre-configured chain names or a filename. - #[rpc(name = "parity_chain")] - fn chain(&self) -> Result; - - /// Get the enode of this node. - #[rpc(name = "parity_enode")] - fn enode(&self) -> Result; - - /// Returns information on current consensus capability. - #[rpc(name = "parity_consensusCapability")] - fn consensus_capability(&self) -> Result; - - /// Get our version information in a nice object. - #[rpc(name = "parity_versionInfo")] - fn version_info(&self) -> Result; - - /// Get information concerning the latest releases if available. - #[rpc(name = "parity_releasesInfo")] - fn releases_info(&self) -> Result>; - - /// Get the current chain status. - #[rpc(name = "parity_chainStatus")] - fn chain_status(&self) -> Result; - - /// Get node kind info. - #[rpc(name = "parity_nodeKind")] - fn node_kind(&self) -> Result<::v1::types::NodeKind>; - - /// Get block header. - /// Same as `eth_getBlockByNumber` but without uncles and transactions. - #[rpc(name = "parity_getBlockHeaderByNumber")] - fn block_header(&self, Option) -> BoxFuture; - - /// Get block receipts. - /// Allows you to fetch receipts from the entire block at once. - /// If no parameter is provided defaults to `latest`. - #[rpc(name = "parity_getBlockReceipts")] - fn block_receipts(&self, Option) -> BoxFuture>; - - /// Get IPFS CIDv0 given protobuf encoded bytes. - #[rpc(name = "parity_cidV0")] - fn ipfs_cid(&self, Bytes) -> Result; - - /// Call contract, returning the output data. - #[rpc(name = "parity_call")] - fn call(&self, Vec, Option) -> Result>; - - /// Used for submitting a proof-of-work solution (similar to `eth_submitWork`, - /// but returns block hash on success, and returns an explicit error message on failure). - #[rpc(name = "parity_submitWorkDetail")] - fn submit_work_detail(&self, H64, H256, H256) -> Result; - - /// Returns the status of the node. Used as the health endpoint. - /// - /// The RPC returns successful response if: - /// - The node have a peer (unless running a dev chain) - /// - The node is not syncing. - /// - /// Otherwise the RPC returns error. - #[rpc(name = "parity_nodeStatus")] - fn status(&self) -> Result<()>; - - /// Extracts Address and public key from signature using the r, s and v params. Equivalent to Solidity erecover - /// as well as checks the signature for chain replay protection - #[rpc(name = "parity_verifySignature")] - fn verify_signature(&self, bool, Bytes, H256, H256, U64) -> Result; - - /// Returns logs matching given filter object. - /// Is allowed to skip filling transaction hash for faster query. - #[rpc(name = "parity_getLogsNoTransactionHash")] - fn logs_no_tx_hash(&self, Filter) -> BoxFuture>; + /// RPC Metadata + type Metadata; + + /// Returns current transactions limit. + #[rpc(name = "parity_transactionsLimit")] + fn transactions_limit(&self) -> Result; + + /// Returns mining extra data. + #[rpc(name = "parity_extraData")] + fn extra_data(&self) -> Result; + + /// Returns mining gas floor target. + #[rpc(name = "parity_gasFloorTarget")] + fn gas_floor_target(&self) -> Result; + + /// Returns mining gas floor cap. + #[rpc(name = "parity_gasCeilTarget")] + fn gas_ceil_target(&self) -> Result; + + /// Returns minimal gas price for transaction to be included in queue. + #[rpc(name = "parity_minGasPrice")] + fn min_gas_price(&self) -> Result; + + /// Returns latest logs + #[rpc(name = "parity_devLogs")] + fn dev_logs(&self) -> Result>; + + /// Returns logs levels + #[rpc(name = "parity_devLogsLevels")] + fn dev_logs_levels(&self) -> Result; + + /// Returns chain name - DEPRECATED. Use `parity_chainName` instead. + #[rpc(name = "parity_netChain")] + fn net_chain(&self) -> Result; + + /// Returns peers details + #[rpc(name = "parity_netPeers")] + fn net_peers(&self) -> Result; + + /// Returns network port + #[rpc(name = "parity_netPort")] + fn net_port(&self) -> Result; + + /// Returns rpc settings + #[rpc(name = "parity_rpcSettings")] + fn rpc_settings(&self) -> Result; + + /// Returns node name + #[rpc(name = "parity_nodeName")] + fn node_name(&self) -> Result; + + /// Returns default extra data + #[rpc(name = "parity_defaultExtraData")] + fn default_extra_data(&self) -> Result; + + /// Returns distribution of gas price in latest blocks. + #[rpc(name = "parity_gasPriceHistogram")] + fn gas_price_histogram(&self) -> BoxFuture; + + /// Returns number of unsigned transactions waiting in the signer queue (if signer enabled) + /// Returns error when signer is disabled + #[rpc(name = "parity_unsignedTransactionsCount")] + fn unsigned_transactions_count(&self) -> Result; + + /// Returns a cryptographically random phrase sufficient for securely seeding a secret key. + #[rpc(name = "parity_generateSecretPhrase")] + fn generate_secret_phrase(&self) -> Result; + + /// Returns whatever address would be derived from the given phrase if it were to seed a brainwallet. + #[rpc(name = "parity_phraseToAddress")] + fn phrase_to_address(&self, _: String) -> Result; + + /// Returns the value of the registrar for this network. + #[rpc(name = "parity_registryAddress")] + fn registry_address(&self) -> Result>; + + /// Returns all addresses if Fat DB is enabled (`--fat-db`), or null if not. + #[rpc(name = "parity_listAccounts")] + fn list_accounts( + &self, + _: u64, + _: Option, + _: Option, + ) -> Result>>; + + /// Returns all storage keys of the given address (first parameter) if Fat DB is enabled (`--fat-db`), + /// or null if not. + #[rpc(name = "parity_listStorageKeys")] + fn list_storage_keys( + &self, + _: H160, + _: u64, + _: Option, + _: Option, + ) -> Result>>; + + /// Encrypt some data with a public key under ECIES. + /// First parameter is the 512-byte destination public key, second is the message. + #[rpc(name = "parity_encryptMessage")] + fn encrypt_message(&self, _: H512, _: Bytes) -> Result; + + /// Returns all pending transactions from transaction queue. + #[rpc(name = "parity_pendingTransactions")] + fn pending_transactions(&self, _: Option) -> Result>; + + /// Returns all transactions from transaction queue. + /// + /// Some of them might not be ready to be included in a block yet. + #[rpc(name = "parity_allTransactions")] + fn all_transactions(&self) -> Result>; + + /// Same as parity_allTransactions, but return only transactions hashes. + #[rpc(name = "parity_allTransactionHashes")] + fn all_transaction_hashes(&self) -> Result>; + + /// Returns all future transactions from transaction queue (deprecated) + #[rpc(name = "parity_futureTransactions")] + fn future_transactions(&self) -> Result>; + + /// Returns propagation statistics on transactions pending in the queue. + #[rpc(name = "parity_pendingTransactionsStats")] + fn pending_transactions_stats(&self) -> Result>; + + /// Returns a list of current and past local transactions with status details. + #[rpc(name = "parity_localTransactions")] + fn local_transactions(&self) -> Result>; + + /// Returns current WS Server interface and port or an error if ws server is disabled. + #[rpc(name = "parity_wsUrl")] + fn ws_url(&self) -> Result; + + /// Returns next nonce for particular sender. Should include all transactions in the queue. + #[rpc(name = "parity_nextNonce")] + fn next_nonce(&self, _: H160) -> BoxFuture; + + /// Get the mode. Returns one of: "active", "passive", "dark", "offline". + #[rpc(name = "parity_mode")] + fn mode(&self) -> Result; + + /// Get the chain name. Returns one of the pre-configured chain names or a filename. + #[rpc(name = "parity_chain")] + fn chain(&self) -> Result; + + /// Get the enode of this node. + #[rpc(name = "parity_enode")] + fn enode(&self) -> Result; + + /// Get the current chain status. + #[rpc(name = "parity_chainStatus")] + fn chain_status(&self) -> Result; + + /// Get node kind info. + #[rpc(name = "parity_nodeKind")] + fn node_kind(&self) -> Result<::v1::types::NodeKind>; + + /// Get block header. + /// Same as `eth_getBlockByNumber` but without uncles and transactions. + #[rpc(name = "parity_getBlockHeaderByNumber")] + fn block_header(&self, _: Option) -> BoxFuture; + + /// Get block receipts. + /// Allows you to fetch receipts from the entire block at once. + /// If no parameter is provided defaults to `latest`. + #[rpc(name = "parity_getBlockReceipts")] + fn block_receipts(&self, _: Option) -> BoxFuture>; + + /// Call contract, returning the output data. + #[rpc(name = "parity_call")] + fn call(&self, _: Vec, _: Option) -> Result>; + + /// Used for submitting a proof-of-work solution (similar to `eth_submitWork`, + /// but returns block hash on success, and returns an explicit error message on failure). + #[rpc(name = "parity_submitWorkDetail")] + fn submit_work_detail(&self, _: H64, _: H256, _: H256) -> Result; + + /// Returns the status of the node. Used as the health endpoint. + /// + /// The RPC returns successful response if: + /// - The node have a peer (unless running a dev chain) + /// - The node is not syncing. + /// + /// Otherwise the RPC returns error. + #[rpc(name = "parity_nodeStatus")] + fn status(&self) -> Result<()>; + + /// Extracts Address and public key from signature using the r, s and v params. Equivalent to Solidity erecover + /// as well as checks the signature for chain replay protection + #[rpc(name = "parity_verifySignature")] + fn verify_signature( + &self, + _: bool, + _: Bytes, + _: H256, + _: H256, + _: U64, + ) -> Result; } diff --git a/rpc/src/v1/traits/parity_accounts.rs b/rpc/src/v1/traits/parity_accounts.rs index eaffac78850..d856f53e1c5 100644 --- a/rpc/src/v1/traits/parity_accounts.rs +++ b/rpc/src/v1/traits/parity_accounts.rs @@ -1,164 +1,149 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Parity Accounts-related rpc interface. use std::collections::BTreeMap; -use jsonrpc_core::Result; -use jsonrpc_derive::rpc; use ethereum_types::{H160, H256, H520}; use ethkey::Password; use ethstore::KeyFile; -use v1::types::{DeriveHash, DeriveHierarchical, ExtAccountInfo}; -use v1::types::{AccountInfo, HwAccountInfo}; +use jsonrpc_core::Result; +use jsonrpc_derive::rpc; +use v1::types::{AccountInfo, DeriveHash, DeriveHierarchical, ExtAccountInfo}; /// Parity-specific read-only accounts rpc interface. -#[rpc] +#[rpc(server)] pub trait ParityAccountsInfo { - /// Returns accounts information. - #[rpc(name = "parity_accountsInfo")] - fn accounts_info(&self) -> Result>; - - /// Returns hardware accounts information. - #[rpc(name = "parity_hardwareAccountsInfo")] - fn hardware_accounts_info(&self) -> Result>; - - /// Get a list of paths to locked hardware wallets - #[rpc(name = "parity_lockedHardwareAccountsInfo")] - fn locked_hardware_accounts_info(&self) -> Result>; + /// Returns accounts information. + #[rpc(name = "parity_accountsInfo")] + fn accounts_info(&self) -> Result>; - /// Returns default account for dapp. - #[rpc(name = "parity_defaultAccount")] - fn default_account(&self) -> Result; + /// Returns default account for dapp. + #[rpc(name = "parity_defaultAccount")] + fn default_account(&self) -> Result; } /// Personal Parity rpc interface. -#[rpc] +#[rpc(server)] pub trait ParityAccounts { - /// Returns accounts information. - #[rpc(name = "parity_allAccountsInfo")] - fn all_accounts_info(&self) -> Result>; - - /// Creates new account from the given phrase using standard brainwallet mechanism. - /// Second parameter is password for the new account. - #[rpc(name = "parity_newAccountFromPhrase")] - fn new_account_from_phrase(&self, String, Password) -> Result; - - /// Creates new account from the given JSON wallet. - /// Second parameter is password for the wallet and the new account. - #[rpc(name = "parity_newAccountFromWallet")] - fn new_account_from_wallet(&self, String, Password) -> Result; - - /// Creates new account from the given raw secret. - /// Second parameter is password for the new account. - #[rpc(name = "parity_newAccountFromSecret")] - fn new_account_from_secret(&self, H256, Password) -> Result; - - /// Returns true if given `password` would unlock given `account`. - /// Arguments: `account`, `password`. - #[rpc(name = "parity_testPassword")] - fn test_password(&self, H160, Password) -> Result; - - /// Changes an account's password. - /// Arguments: `account`, `password`, `new_password`. - #[rpc(name = "parity_changePassword")] - fn change_password(&self, H160, Password, Password) -> Result; - - /// Permanently deletes an account. - /// Arguments: `account`, `password`. - #[rpc(name = "parity_killAccount")] - fn kill_account(&self, H160, Password) -> Result; - - /// Permanently deletes an address from the addressbook - /// Arguments: `address` - #[rpc(name = "parity_removeAddress")] - fn remove_address(&self, H160) -> Result; - - /// Set an account's name. - #[rpc(name = "parity_setAccountName")] - fn set_account_name(&self, H160, String) -> Result; - - /// Set an account's metadata string. - #[rpc(name = "parity_setAccountMeta")] - fn set_account_meta(&self, H160, String) -> Result; - - /// Imports a number of Geth accounts, with the list provided as the argument. - #[rpc(name = "parity_importGethAccounts")] - fn import_geth_accounts(&self, Vec) -> Result>; - - /// Returns the accounts available for importing from Geth. - #[rpc(name = "parity_listGethAccounts")] - fn geth_accounts(&self) -> Result>; - - /// Create new vault. - #[rpc(name = "parity_newVault")] - fn create_vault(&self, String, Password) -> Result; - - /// Open existing vault. - #[rpc(name = "parity_openVault")] - fn open_vault(&self, String, Password) -> Result; - - /// Close previously opened vault. - #[rpc(name = "parity_closeVault")] - fn close_vault(&self, String) -> Result; - - /// List all vaults. - #[rpc(name = "parity_listVaults")] - fn list_vaults(&self) -> Result>; - - /// List all currently opened vaults. - #[rpc(name = "parity_listOpenedVaults")] - fn list_opened_vaults(&self) -> Result>; - - /// Change vault password. - #[rpc(name = "parity_changeVaultPassword")] - fn change_vault_password(&self, String, Password) -> Result; - - /// Change vault of the given address. - #[rpc(name = "parity_changeVault")] - fn change_vault(&self, H160, String) -> Result; - - /// Get vault metadata string. - #[rpc(name = "parity_getVaultMeta")] - fn get_vault_meta(&self, String) -> Result; - - /// Set vault metadata string. - #[rpc(name = "parity_setVaultMeta")] - fn set_vault_meta(&self, String, String) -> Result; - - /// Derive new address from given account address using specific hash. - /// Resulting address can be either saved as a new account (with the same password). - #[rpc(name = "parity_deriveAddressHash")] - fn derive_key_hash(&self, H160, Password, DeriveHash, bool) -> Result; - - /// Derive new address from given account address using - /// hierarchical derivation (sequence of 32-bit integer indices). - /// Resulting address can be either saved as a new account (with the same password). - #[rpc(name = "parity_deriveAddressIndex")] - fn derive_key_index(&self, H160, Password, DeriveHierarchical, bool) -> Result; - - /// Exports an account with given address if provided password matches. - #[rpc(name = "parity_exportAccount")] - fn export_account(&self, H160, Password) -> Result; - - /// Sign raw hash with the key corresponding to address and password. - #[rpc(name = "parity_signMessage")] - fn sign_message(&self, H160, Password, H256) -> Result; - - /// Send a PinMatrixAck to a hardware wallet, unlocking it - #[rpc(name = "parity_hardwarePinMatrixAck")] - fn hardware_pin_matrix_ack(&self, String, String) -> Result; + /// Returns accounts information. + #[rpc(name = "parity_allAccountsInfo")] + fn all_accounts_info(&self) -> Result>; + + /// Creates new account from the given phrase using standard brainwallet mechanism. + /// Second parameter is password for the new account. + #[rpc(name = "parity_newAccountFromPhrase")] + fn new_account_from_phrase(&self, _: String, _: Password) -> Result; + + /// Creates new account from the given JSON wallet. + /// Second parameter is password for the wallet and the new account. + #[rpc(name = "parity_newAccountFromWallet")] + fn new_account_from_wallet(&self, _: String, _: Password) -> Result; + + /// Creates new account from the given raw secret. + /// Second parameter is password for the new account. + #[rpc(name = "parity_newAccountFromSecret")] + fn new_account_from_secret(&self, _: H256, _: Password) -> Result; + + /// Returns true if given `password` would unlock given `account`. + /// Arguments: `account`, `password`. + #[rpc(name = "parity_testPassword")] + fn test_password(&self, _: H160, _: Password) -> Result; + + /// Changes an account's password. + /// Arguments: `account`, `password`, `new_password`. + #[rpc(name = "parity_changePassword")] + fn change_password(&self, _: H160, _: Password, _: Password) -> Result; + + /// Permanently deletes an account. + /// Arguments: `account`, `password`. + #[rpc(name = "parity_killAccount")] + fn kill_account(&self, _: H160, _: Password) -> Result; + + /// Permanently deletes an address from the addressbook + /// Arguments: `address` + #[rpc(name = "parity_removeAddress")] + fn remove_address(&self, _: H160) -> Result; + + /// Set an account's name. + #[rpc(name = "parity_setAccountName")] + fn set_account_name(&self, _: H160, _: String) -> Result; + + /// Set an account's metadata string. + #[rpc(name = "parity_setAccountMeta")] + fn set_account_meta(&self, _: H160, _: String) -> Result; + + /// Create new vault. + #[rpc(name = "parity_newVault")] + fn create_vault(&self, _: String, _: Password) -> Result; + + /// Open existing vault. + #[rpc(name = "parity_openVault")] + fn open_vault(&self, _: String, _: Password) -> Result; + + /// Close previously opened vault. + #[rpc(name = "parity_closeVault")] + fn close_vault(&self, _: String) -> Result; + + /// List all vaults. + #[rpc(name = "parity_listVaults")] + fn list_vaults(&self) -> Result>; + + /// List all currently opened vaults. + #[rpc(name = "parity_listOpenedVaults")] + fn list_opened_vaults(&self) -> Result>; + + /// Change vault password. + #[rpc(name = "parity_changeVaultPassword")] + fn change_vault_password(&self, _: String, _: Password) -> Result; + + /// Change vault of the given address. + #[rpc(name = "parity_changeVault")] + fn change_vault(&self, _: H160, _: String) -> Result; + + /// Get vault metadata string. + #[rpc(name = "parity_getVaultMeta")] + fn get_vault_meta(&self, _: String) -> Result; + + /// Set vault metadata string. + #[rpc(name = "parity_setVaultMeta")] + fn set_vault_meta(&self, _: String, _: String) -> Result; + + /// Derive new address from given account address using specific hash. + /// Resulting address can be either saved as a new account (with the same password). + #[rpc(name = "parity_deriveAddressHash")] + fn derive_key_hash(&self, _: H160, _: Password, _: DeriveHash, _: bool) -> Result; + + /// Derive new address from given account address using + /// hierarchical derivation (sequence of 32-bit integer indices). + /// Resulting address can be either saved as a new account (with the same password). + #[rpc(name = "parity_deriveAddressIndex")] + fn derive_key_index( + &self, + _: H160, + _: Password, + _: DeriveHierarchical, + _: bool, + ) -> Result; + + /// Exports an account with given address if provided password matches. + #[rpc(name = "parity_exportAccount")] + fn export_account(&self, _: H160, _: Password) -> Result; + + /// Sign raw hash with the key corresponding to address and password. + #[rpc(name = "parity_signMessage")] + fn sign_message(&self, _: H160, _: Password, _: H256) -> Result; } diff --git a/rpc/src/v1/traits/parity_set.rs b/rpc/src/v1/traits/parity_set.rs index c7c23387900..2d64e613d07 100644 --- a/rpc/src/v1/traits/parity_set.rs +++ b/rpc/src/v1/traits/parity_set.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Parity-specific rpc interface for operations altering the settings. @@ -20,105 +20,97 @@ use ethereum_types::{H160, H256, U256}; use jsonrpc_core::{BoxFuture, Result}; use jsonrpc_derive::rpc; -use v1::types::{Bytes, ReleaseInfo, Transaction}; +use v1::types::{Bytes, Transaction}; /// Parity-specific rpc interface for operations altering the account-related settings. -#[rpc] +#[rpc(server)] pub trait ParitySetAccounts { - /// Sets account for signing consensus messages. - #[rpc(name = "parity_setEngineSigner")] - fn set_engine_signer(&self, H160, String) -> Result; + /// Sets account for signing consensus messages. + #[rpc(name = "parity_setEngineSigner")] + fn set_engine_signer(&self, _: H160, _: String) -> Result; } /// Parity-specific rpc interface for operations altering the settings. -#[rpc] +#[rpc(server)] pub trait ParitySet { - /// Sets new minimal gas price for mined blocks. - #[rpc(name = "parity_setMinGasPrice")] - fn set_min_gas_price(&self, U256) -> Result; - - /// Sets new gas floor target for mined blocks. - #[rpc(name = "parity_setGasFloorTarget")] - fn set_gas_floor_target(&self, U256) -> Result; - - /// Sets new gas ceiling target for mined blocks. - #[rpc(name = "parity_setGasCeilTarget")] - fn set_gas_ceil_target(&self, U256) -> Result; - - /// Sets new extra data for mined blocks. - #[rpc(name = "parity_setExtraData")] - fn set_extra_data(&self, Bytes) -> Result; - - /// Sets new author for mined block. - #[rpc(name = "parity_setAuthor")] - fn set_author(&self, H160) -> Result; - - /// Sets the secret of engine signer account. - #[rpc(name = "parity_setEngineSignerSecret")] - fn set_engine_signer_secret(&self, H256) -> Result; - - /// Sets the limits for transaction queue. - #[rpc(name = "parity_setTransactionsLimit")] - fn set_transactions_limit(&self, usize) -> Result; - - /// Sets the maximum amount of gas a single transaction may consume. - #[rpc(name = "parity_setMaxTransactionGas")] - fn set_tx_gas_limit(&self, U256) -> Result; - - /// Add a reserved peer. - #[rpc(name = "parity_addReservedPeer")] - fn add_reserved_peer(&self, String) -> Result; - - /// Remove a reserved peer. - #[rpc(name = "parity_removeReservedPeer")] - fn remove_reserved_peer(&self, String) -> Result; - - /// Drop all non-reserved peers. - #[rpc(name = "parity_dropNonReservedPeers")] - fn drop_non_reserved_peers(&self) -> Result; - - /// Accept non-reserved peers (default behavior) - #[rpc(name = "parity_acceptNonReservedPeers")] - fn accept_non_reserved_peers(&self) -> Result; - - /// Start the network. - /// - /// @deprecated - Use `set_mode("active")` instead. - #[rpc(name = "parity_startNetwork")] - fn start_network(&self) -> Result; - - /// Stop the network. - /// - /// @deprecated - Use `set_mode("offline")` instead. - #[rpc(name = "parity_stopNetwork")] - fn stop_network(&self) -> Result; - - /// Set the mode. Argument must be one of: "active", "passive", "dark", "offline". - #[rpc(name = "parity_setMode")] - fn set_mode(&self, String) -> Result; - - /// Set the network spec. Argument must be one of pre-configured chains or a filename. - #[rpc(name = "parity_setChain")] - fn set_spec_name(&self, String) -> Result; - - /// Hash a file content under given URL. - #[rpc(name = "parity_hashContent")] - fn hash_content(&self, String) -> BoxFuture; - - /// Is there a release ready for install? - #[rpc(name = "parity_upgradeReady")] - fn upgrade_ready(&self) -> Result>; - - /// Execute a release which is ready according to upgrade_ready(). - #[rpc(name = "parity_executeUpgrade")] - fn execute_upgrade(&self) -> Result; - - /// Removes transaction from transaction queue. - /// Makes sense only for transactions that were not propagated to other peers yet - /// like scheduled transactions or transactions in future. - /// It might also work for some local transactions with to low gas price - /// or excessive gas limit that are not accepted by other peers whp. - /// Returns `true` when transaction was removed, `false` if it was not found. - #[rpc(name = "parity_removeTransaction")] - fn remove_transaction(&self, H256) -> Result>; + /// Sets new minimal gas price for mined blocks. + #[rpc(name = "parity_setMinGasPrice")] + fn set_min_gas_price(&self, _: U256) -> Result; + + /// Sets new gas floor target for mined blocks. + #[rpc(name = "parity_setGasFloorTarget")] + fn set_gas_floor_target(&self, _: U256) -> Result; + + /// Sets new gas ceiling target for mined blocks. + #[rpc(name = "parity_setGasCeilTarget")] + fn set_gas_ceil_target(&self, _: U256) -> Result; + + /// Sets new extra data for mined blocks. + #[rpc(name = "parity_setExtraData")] + fn set_extra_data(&self, _: Bytes) -> Result; + + /// Sets new author for mined block. + #[rpc(name = "parity_setAuthor")] + fn set_author(&self, _: H160) -> Result; + + /// Sets the secret of engine signer account. + #[rpc(name = "parity_setEngineSignerSecret")] + fn set_engine_signer_secret(&self, _: H256) -> Result; + + /// Sets the limits for transaction queue. + #[rpc(name = "parity_setTransactionsLimit")] + fn set_transactions_limit(&self, _: usize) -> Result; + + /// Sets the maximum amount of gas a single transaction may consume. + #[rpc(name = "parity_setMaxTransactionGas")] + fn set_tx_gas_limit(&self, _: U256) -> Result; + + /// Add a reserved peer. + #[rpc(name = "parity_addReservedPeer")] + fn add_reserved_peer(&self, _: String) -> Result; + + /// Remove a reserved peer. + #[rpc(name = "parity_removeReservedPeer")] + fn remove_reserved_peer(&self, _: String) -> Result; + + /// Drop all non-reserved peers. + #[rpc(name = "parity_dropNonReservedPeers")] + fn drop_non_reserved_peers(&self) -> Result; + + /// Accept non-reserved peers (default behavior) + #[rpc(name = "parity_acceptNonReservedPeers")] + fn accept_non_reserved_peers(&self) -> Result; + + /// Start the network. + /// + /// @deprecated - Use `set_mode("active")` instead. + #[rpc(name = "parity_startNetwork")] + fn start_network(&self) -> Result; + + /// Stop the network. + /// + /// @deprecated - Use `set_mode("offline")` instead. + #[rpc(name = "parity_stopNetwork")] + fn stop_network(&self) -> Result; + + /// Set the mode. Argument must be one of: "active", "passive", "dark", "offline". + #[rpc(name = "parity_setMode")] + fn set_mode(&self, _: String) -> Result; + + /// Set the network spec. Argument must be one of pre-configured chains or a filename. + #[rpc(name = "parity_setChain")] + fn set_spec_name(&self, _: String) -> Result; + + /// Hash a file content under given URL. + #[rpc(name = "parity_hashContent")] + fn hash_content(&self, _: String) -> BoxFuture; + + /// Removes transaction from transaction queue. + /// Makes sense only for transactions that were not propagated to other peers yet + /// like scheduled transactions or transactions in future. + /// It might also work for some local transactions with to low gas price + /// or excessive gas limit that are not accepted by other peers whp. + /// Returns `true` when transaction was removed, `false` if it was not found. + #[rpc(name = "parity_removeTransaction")] + fn remove_transaction(&self, _: H256) -> Result>; } diff --git a/rpc/src/v1/traits/parity_signing.rs b/rpc/src/v1/traits/parity_signing.rs index acd9e8cd686..058c1536d94 100644 --- a/rpc/src/v1/traits/parity_signing.rs +++ b/rpc/src/v1/traits/parity_signing.rs @@ -1,54 +1,67 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! ParitySigning rpc interface. use jsonrpc_core::{BoxFuture, Result}; use jsonrpc_derive::rpc; use ethereum_types::{H160, U256}; -use v1::types::{Bytes, ConfirmationResponse, TransactionRequest, Either}; +use v1::types::{Bytes, ConfirmationResponse, Either, TransactionRequest}; /// Signing methods implementation. -#[rpc] +#[rpc(server)] pub trait ParitySigning { - /// RPC Metadata - type Metadata; - - /// Given partial transaction request produces transaction with all fields filled in. - /// Such transaction can be then signed externally. - #[rpc(meta, name = "parity_composeTransaction")] - fn compose_transaction(&self, Self::Metadata, TransactionRequest) -> BoxFuture; - - /// Posts sign request asynchronously. - /// Will return a confirmation ID for later use with check_transaction. - #[rpc(meta, name = "parity_postSign")] - fn post_sign(&self, Self::Metadata, H160, Bytes) -> BoxFuture>; - - /// Posts transaction asynchronously. - /// Will return a transaction ID for later use with check_transaction. - #[rpc(meta, name = "parity_postTransaction")] - fn post_transaction(&self, Self::Metadata, TransactionRequest) -> BoxFuture>; - - /// Checks the progress of a previously posted request (transaction/sign). - /// Should be given a valid send_transaction ID. - #[rpc(name = "parity_checkRequest")] - fn check_request(&self, U256) -> Result>; - - /// Decrypt some ECIES-encrypted message. - /// First parameter is the address with which it is encrypted, second is the ciphertext. - #[rpc(meta, name = "parity_decryptMessage")] - fn decrypt_message(&self, Self::Metadata, H160, Bytes) -> BoxFuture; + /// RPC Metadata + type Metadata; + + /// Given partial transaction request produces transaction with all fields filled in. + /// Such transaction can be then signed externally. + #[rpc(meta, name = "parity_composeTransaction")] + fn compose_transaction( + &self, + _: Self::Metadata, + _: TransactionRequest, + ) -> BoxFuture; + + /// Posts sign request asynchronously. + /// Will return a confirmation ID for later use with check_transaction. + #[rpc(meta, name = "parity_postSign")] + fn post_sign( + &self, + _: Self::Metadata, + _: H160, + _: Bytes, + ) -> BoxFuture>; + + /// Posts transaction asynchronously. + /// Will return a transaction ID for later use with check_transaction. + #[rpc(meta, name = "parity_postTransaction")] + fn post_transaction( + &self, + _: Self::Metadata, + _: TransactionRequest, + ) -> BoxFuture>; + + /// Checks the progress of a previously posted request (transaction/sign). + /// Should be given a valid send_transaction ID. + #[rpc(name = "parity_checkRequest")] + fn check_request(&self, _: U256) -> Result>; + + /// Decrypt some ECIES-encrypted message. + /// First parameter is the address with which it is encrypted, second is the ciphertext. + #[rpc(meta, name = "parity_decryptMessage")] + fn decrypt_message(&self, _: Self::Metadata, _: H160, _: Bytes) -> BoxFuture; } diff --git a/rpc/src/v1/traits/personal.rs b/rpc/src/v1/traits/personal.rs index e3632731fa5..b3b61ec557d 100644 --- a/rpc/src/v1/traits/personal.rs +++ b/rpc/src/v1/traits/personal.rs @@ -1,74 +1,90 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Personal rpc interface. use eip_712::EIP712; use ethereum_types::{H160, H256, H520, U128}; -use jsonrpc_core::types::Value; -use jsonrpc_core::{BoxFuture, Result}; +use jsonrpc_core::{types::Value, BoxFuture, Result}; use jsonrpc_derive::rpc; -use v1::types::{Bytes, TransactionRequest, RichRawTransaction as RpcRichRawTransaction, EIP191Version}; +use v1::types::{ + Bytes, EIP191Version, RichRawTransaction as RpcRichRawTransaction, TransactionRequest, +}; /// Personal rpc interface. Safe (read-only) functions. -#[rpc] +#[rpc(server)] pub trait Personal { - /// RPC Metadata - type Metadata; + /// RPC Metadata + type Metadata; - /// Lists all stored accounts - #[rpc(name = "personal_listAccounts")] - fn accounts(&self) -> Result>; + /// Lists all stored accounts + #[rpc(name = "personal_listAccounts")] + fn accounts(&self) -> Result>; - /// Creates new account (it becomes new current unlocked account) - /// Param is the password for the account. - #[rpc(name = "personal_newAccount")] - fn new_account(&self, String) -> Result; + /// Creates new account (it becomes new current unlocked account) + /// Param is the password for the account. + #[rpc(name = "personal_newAccount")] + fn new_account(&self, _: String) -> Result; - /// Unlocks specified account for use (can only be one unlocked account at one moment) - #[rpc(name = "personal_unlockAccount")] - fn unlock_account(&self, H160, String, Option) -> Result; + /// Unlocks specified account for use (can only be one unlocked account at one moment) + #[rpc(name = "personal_unlockAccount")] + fn unlock_account(&self, _: H160, _: String, _: Option) -> Result; - /// Signs the hash of data with given account signature using the given password to unlock the account during - /// the request. - #[rpc(name = "personal_sign")] - fn sign(&self, Bytes, H160, String) -> BoxFuture; + /// Signs the hash of data with given account signature using the given password to unlock the account during + /// the request. + #[rpc(name = "personal_sign")] + fn sign(&self, _: Bytes, _: H160, _: String) -> BoxFuture; - /// Produces an EIP-712 compliant signature with given account using the given password to unlock the - /// account during the request. - #[rpc(name = "personal_signTypedData")] - fn sign_typed_data(&self, EIP712, H160, String) -> BoxFuture; + /// Produces an EIP-712 compliant signature with given account using the given password to unlock the + /// account during the request. + #[rpc(name = "personal_signTypedData")] + fn sign_typed_data(&self, _: EIP712, _: H160, _: String) -> BoxFuture; - /// Signs an arbitrary message based on the version specified - #[rpc(name = "personal_sign191")] - fn sign_191(&self, EIP191Version, Value, H160, String) -> BoxFuture; + /// Signs an arbitrary message based on the version specified + #[rpc(name = "personal_sign191")] + fn sign_191(&self, _: EIP191Version, _: Value, _: H160, _: String) -> BoxFuture; - /// Returns the account associated with the private key that was used to calculate the signature in - /// `personal_sign`. - #[rpc(name = "personal_ecRecover")] - fn ec_recover(&self, Bytes, H520) -> BoxFuture; + /// Returns the account associated with the private key that was used to calculate the signature in + /// `personal_sign`. + #[rpc(name = "personal_ecRecover")] + fn ec_recover(&self, _: Bytes, _: H520) -> BoxFuture; - /// Signs transaction. The account is not unlocked in such case. - #[rpc(meta, name = "personal_signTransaction")] - fn sign_transaction(&self, Self::Metadata, TransactionRequest, String) -> BoxFuture; + /// Signs transaction. The account is not unlocked in such case. + #[rpc(meta, name = "personal_signTransaction")] + fn sign_transaction( + &self, + _: Self::Metadata, + _: TransactionRequest, + _: String, + ) -> BoxFuture; - /// Sends transaction and signs it in single call. The account is not unlocked in such case. - #[rpc(meta, name = "personal_sendTransaction")] - fn send_transaction(&self, Self::Metadata, TransactionRequest, String) -> BoxFuture; + /// Sends transaction and signs it in single call. The account is not unlocked in such case. + #[rpc(meta, name = "personal_sendTransaction")] + fn send_transaction( + &self, + _: Self::Metadata, + _: TransactionRequest, + _: String, + ) -> BoxFuture; - /// @deprecated alias for `personal_sendTransaction`. - #[rpc(meta, name = "personal_signAndSendTransaction")] - fn sign_and_send_transaction(&self, Self::Metadata, TransactionRequest, String) -> BoxFuture; + /// @deprecated alias for `personal_sendTransaction`. + #[rpc(meta, name = "personal_signAndSendTransaction")] + fn sign_and_send_transaction( + &self, + _: Self::Metadata, + _: TransactionRequest, + _: String, + ) -> BoxFuture; } diff --git a/rpc/src/v1/traits/private.rs b/rpc/src/v1/traits/private.rs deleted file mode 100644 index 732e3914bda..00000000000 --- a/rpc/src/v1/traits/private.rs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! SecretStore-specific rpc interface. - -use ethereum_types::{H160, H256, U256}; -use jsonrpc_core::Error; -use jsonrpc_derive::rpc; - -use v1::types::{Bytes, PrivateTransactionReceipt, BlockNumber, - PrivateTransactionReceiptAndTransaction, CallRequest}; - -/// Private transaction management RPC interface. -#[rpc] -pub trait Private { - /// RPC Metadata - type Metadata; - - /// Sends private transaction; Transaction will be added to the validation queue and sent out when ready. - #[rpc(name = "private_sendTransaction")] - fn send_transaction(&self, Bytes) -> Result; - - /// Creates a transaction for contract's deployment from origin (signed transaction) - #[rpc(name = "private_composeDeploymentTransaction")] - fn compose_deployment_transaction(&self, BlockNumber, Bytes, Vec, U256) -> Result; - - /// Make a call to the private contract - #[rpc(name = "private_call")] - fn private_call(&self, BlockNumber, CallRequest) -> Result; - - /// Retrieve the id of the key associated with the contract - #[rpc(name = "private_contractKey")] - fn private_contract_key(&self, H160) -> Result; -} diff --git a/rpc/src/v1/traits/pubsub.rs b/rpc/src/v1/traits/pubsub.rs index c91b335613f..429b11565d6 100644 --- a/rpc/src/v1/traits/pubsub.rs +++ b/rpc/src/v1/traits/pubsub.rs @@ -1,36 +1,50 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Parity-specific PUB-SUB rpc interface. -use jsonrpc_core::{Result, Value, Params}; -use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId}; +use jsonrpc_core::{Params, Result, Value}; use jsonrpc_derive::rpc; +use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId}; /// Parity-specific PUB-SUB rpc interface. -#[rpc] +#[rpc(server)] pub trait PubSub { - /// Pub/Sub Metadata - type Metadata; - - /// Subscribe to changes of any RPC method in Parity. - #[pubsub(subscription = "parity_subscription", subscribe, name = "parity_subscribe")] - fn parity_subscribe(&self, Self::Metadata, Subscriber, String, Option); - - /// Unsubscribe from existing Parity subscription. - #[pubsub(subscription = "parity_subscription", unsubscribe, name = "parity_unsubscribe")] - fn parity_unsubscribe(&self, Option, SubscriptionId) -> Result; + /// Pub/Sub Metadata + type Metadata; + + /// Subscribe to changes of any RPC method in Parity. + #[pubsub( + subscription = "parity_subscription", + subscribe, + name = "parity_subscribe" + )] + fn parity_subscribe( + &self, + _: Self::Metadata, + _: Subscriber, + _: String, + _: Option, + ); + + /// Unsubscribe from existing Parity subscription. + #[pubsub( + subscription = "parity_subscription", + unsubscribe, + name = "parity_unsubscribe" + )] + fn parity_unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; } diff --git a/rpc/src/v1/traits/rpc.rs b/rpc/src/v1/traits/rpc.rs deleted file mode 100644 index 9600630fecd..00000000000 --- a/rpc/src/v1/traits/rpc.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! RPC interface. - -use std::collections::BTreeMap; - -use jsonrpc_core::Result; -use jsonrpc_derive::rpc; - -/// RPC Interface. -#[rpc] -pub trait Rpc { - /// Returns supported modules for Geth 1.3.6 - /// @ignore - #[rpc(name = "modules")] - fn modules(&self) -> Result>; - - /// Returns supported modules for Geth 1.4.0 - /// @ignore - #[rpc(name = "rpc_modules")] - fn rpc_modules(&self) -> Result>; -} diff --git a/rpc/src/v1/traits/secretstore.rs b/rpc/src/v1/traits/secretstore.rs index 6883753b4fa..b65efa0a1a7 100644 --- a/rpc/src/v1/traits/secretstore.rs +++ b/rpc/src/v1/traits/secretstore.rs @@ -1,61 +1,69 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! SecretStore-specific rpc interface. use std::collections::BTreeSet; -use jsonrpc_core::Result; -use jsonrpc_derive::rpc; use ethereum_types::{H160, H256, H512}; use ethkey::Password; +use jsonrpc_core::Result; +use jsonrpc_derive::rpc; use v1::types::{Bytes, EncryptedDocumentKey}; /// Parity-specific rpc interface. -#[rpc] +#[rpc(server)] pub trait SecretStore { - /// Generate document key to store in secret store. - /// Arguments: `account`, `password`, `server_key_public`. - #[rpc(name = "secretstore_generateDocumentKey")] - fn generate_document_key(&self, H160, Password, H512) -> Result; - - /// Encrypt data with key, received from secret store. - /// Arguments: `account`, `password`, `key`, `data`. - #[rpc(name = "secretstore_encrypt")] - fn encrypt(&self, H160, Password, Bytes, Bytes) -> Result; - - /// Decrypt data with key, received from secret store. - /// Arguments: `account`, `password`, `key`, `data`. - #[rpc(name = "secretstore_decrypt")] - fn decrypt(&self, H160, Password, Bytes, Bytes) -> Result; - - /// Decrypt data with shadow key, received from secret store. - /// Arguments: `account`, `password`, `decrypted_secret`, `common_point`, `decrypt_shadows`, `data`. - #[rpc(name = "secretstore_shadowDecrypt")] - fn shadow_decrypt(&self, H160, Password, H512, H512, Vec, Bytes) -> Result; - - /// Calculates the hash (keccak256) of servers set for using in ServersSetChange session. - /// Returned hash must be signed later by using `secretstore_signRawHash` method. - /// Arguments: `servers_set`. - #[rpc(name = "secretstore_serversSetHash")] - fn servers_set_hash(&self, BTreeSet) -> Result; - - /// Generate recoverable ECDSA signature of raw hash. - /// Passed hash is treated as an input to the `sign` function (no prefixes added, no hash function is applied). - /// Arguments: `account`, `password`, `raw_hash`. - #[rpc(name = "secretstore_signRawHash")] - fn sign_raw_hash(&self, H160, Password, H256) -> Result; + /// Generate document key to store in secret store. + /// Arguments: `account`, `password`, `server_key_public`. + #[rpc(name = "secretstore_generateDocumentKey")] + fn generate_document_key(&self, _: H160, _: Password, _: H512) -> Result; + + /// Encrypt data with key, received from secret store. + /// Arguments: `account`, `password`, `key`, `data`. + #[rpc(name = "secretstore_encrypt")] + fn encrypt(&self, _: H160, _: Password, _: Bytes, _: Bytes) -> Result; + + /// Decrypt data with key, received from secret store. + /// Arguments: `account`, `password`, `key`, `data`. + #[rpc(name = "secretstore_decrypt")] + fn decrypt(&self, _: H160, _: Password, _: Bytes, _: Bytes) -> Result; + + /// Decrypt data with shadow key, received from secret store. + /// Arguments: `account`, `password`, `decrypted_secret`, `common_point`, `decrypt_shadows`, `data`. + #[rpc(name = "secretstore_shadowDecrypt")] + fn shadow_decrypt( + &self, + _: H160, + _: Password, + _: H512, + _: H512, + _: Vec, + _: Bytes, + ) -> Result; + + /// Calculates the hash (keccak256) of servers set for using in ServersSetChange session. + /// Returned hash must be signed later by using `secretstore_signRawHash` method. + /// Arguments: `servers_set`. + #[rpc(name = "secretstore_serversSetHash")] + fn servers_set_hash(&self, _: BTreeSet) -> Result; + + /// Generate recoverable ECDSA signature of raw hash. + /// Passed hash is treated as an input to the `sign` function (no prefixes added, no hash function is applied). + /// Arguments: `account`, `password`, `raw_hash`. + #[rpc(name = "secretstore_signRawHash")] + fn sign_raw_hash(&self, _: H160, _: Password, _: H256) -> Result; } diff --git a/rpc/src/v1/traits/signer.rs b/rpc/src/v1/traits/signer.rs index b5653eba635..d679c341303 100644 --- a/rpc/src/v1/traits/signer.rs +++ b/rpc/src/v1/traits/signer.rs @@ -1,63 +1,84 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Parity Signer-related rpc interface. use ethereum_types::U256; use jsonrpc_core::{BoxFuture, Result}; -use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId}; use jsonrpc_derive::rpc; +use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId}; -use v1::types::{Bytes, TransactionModification, ConfirmationRequest, ConfirmationResponse, ConfirmationResponseWithToken}; +use v1::types::{ + Bytes, ConfirmationRequest, ConfirmationResponse, ConfirmationResponseWithToken, + TransactionModification, +}; /// Signer extension for confirmations rpc interface. -#[rpc] +#[rpc(server)] pub trait Signer { - /// RPC Metadata - type Metadata; + /// RPC Metadata + type Metadata; - /// Returns a list of items to confirm. - #[rpc(name = "signer_requestsToConfirm")] - fn requests_to_confirm(&self) -> Result>; + /// Returns a list of items to confirm. + #[rpc(name = "signer_requestsToConfirm")] + fn requests_to_confirm(&self) -> Result>; - /// Confirm specific request. - #[rpc(name = "signer_confirmRequest")] - fn confirm_request(&self, U256, TransactionModification, String) -> BoxFuture; + /// Confirm specific request. + #[rpc(name = "signer_confirmRequest")] + fn confirm_request( + &self, + _: U256, + _: TransactionModification, + _: String, + ) -> BoxFuture; - /// Confirm specific request with token. - #[rpc(name = "signer_confirmRequestWithToken")] - fn confirm_request_with_token(&self, U256, TransactionModification, String) -> BoxFuture; + /// Confirm specific request with token. + #[rpc(name = "signer_confirmRequestWithToken")] + fn confirm_request_with_token( + &self, + _: U256, + _: TransactionModification, + _: String, + ) -> BoxFuture; - /// Confirm specific request with already signed data. - #[rpc(name = "signer_confirmRequestRaw")] - fn confirm_request_raw(&self, U256, Bytes) -> Result; + /// Confirm specific request with already signed data. + #[rpc(name = "signer_confirmRequestRaw")] + fn confirm_request_raw(&self, _: U256, _: Bytes) -> Result; - /// Reject the confirmation request. - #[rpc(name = "signer_rejectRequest")] - fn reject_request(&self, U256) -> Result; + /// Reject the confirmation request. + #[rpc(name = "signer_rejectRequest")] + fn reject_request(&self, _: U256) -> Result; - /// Generates new authorization token. - #[rpc(name = "signer_generateAuthorizationToken")] - fn generate_token(&self) -> Result; + /// Generates new authorization token. + #[rpc(name = "signer_generateAuthorizationToken")] + fn generate_token(&self) -> Result; - /// Subscribe to new pending requests on signer interface. - #[pubsub(subscription = "signer_pending", subscribe, name = "signer_subscribePending")] - fn subscribe_pending(&self, Self::Metadata, Subscriber>); + /// Subscribe to new pending requests on signer interface. + #[pubsub( + subscription = "signer_pending", + subscribe, + name = "signer_subscribePending" + )] + fn subscribe_pending(&self, _: Self::Metadata, _: Subscriber>); - /// Unsubscribe from pending requests subscription. - #[pubsub(subscription = "signer_pending", unsubscribe, name = "signer_unsubscribePending")] - fn unsubscribe_pending(&self, Option, SubscriptionId) -> Result; + /// Unsubscribe from pending requests subscription. + #[pubsub( + subscription = "signer_pending", + unsubscribe, + name = "signer_unsubscribePending" + )] + fn unsubscribe_pending(&self, _: Option, _: SubscriptionId) -> Result; } diff --git a/rpc/src/v1/traits/traces.rs b/rpc/src/v1/traits/traces.rs index 5308ed4c6c3..2dbbe1ed9af 100644 --- a/rpc/src/v1/traits/traces.rs +++ b/rpc/src/v1/traits/traces.rs @@ -1,66 +1,82 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Traces specific rpc interface. use ethereum_types::H256; use jsonrpc_core::Result; use jsonrpc_derive::rpc; -use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, - TraceResultsWithTransactionHash, TraceOptions}; +use v1::types::{ + BlockNumber, Bytes, CallRequest, Index, LocalizedTrace, TraceFilter, TraceOptions, + TraceResults, TraceResultsWithTransactionHash, +}; /// Traces specific rpc interface. -#[rpc] +#[rpc(server)] pub trait Traces { - /// RPC Metadata - type Metadata; + /// RPC Metadata + type Metadata; - /// Returns traces matching given filter. - #[rpc(name = "trace_filter")] - fn filter(&self, TraceFilter) -> Result>>; + /// Returns traces matching given filter. + #[rpc(name = "trace_filter")] + fn filter(&self, _: TraceFilter) -> Result>>; - /// Returns transaction trace at given index. - #[rpc(name = "trace_get")] - fn trace(&self, H256, Vec) -> Result>; + /// Returns transaction trace at given index. + #[rpc(name = "trace_get")] + fn trace(&self, _: H256, _: Vec) -> Result>; - /// Returns all traces of given transaction. - #[rpc(name = "trace_transaction")] - fn transaction_traces(&self, H256) -> Result>>; + /// Returns all traces of given transaction. + #[rpc(name = "trace_transaction")] + fn transaction_traces(&self, _: H256) -> Result>>; - /// Returns all traces produced at given block. - #[rpc(name = "trace_block")] - fn block_traces(&self, BlockNumber) -> Result>>; + /// Returns all traces produced at given block. + #[rpc(name = "trace_block")] + fn block_traces(&self, _: BlockNumber) -> Result>>; - /// Executes the given call and returns a number of possible traces for it. - #[rpc(name = "trace_call")] - fn call(&self, CallRequest, TraceOptions, Option) -> Result; + /// Executes the given call and returns a number of possible traces for it. + #[rpc(name = "trace_call")] + fn call(&self, _: CallRequest, _: TraceOptions, _: Option) + -> Result; - /// Executes all given calls and returns a number of possible traces for each of it. - #[rpc(name = "trace_callMany")] - fn call_many(&self, Vec<(CallRequest, TraceOptions)>, Option) -> Result>; + /// Executes all given calls and returns a number of possible traces for each of it. + #[rpc(name = "trace_callMany")] + fn call_many( + &self, + _: Vec<(CallRequest, TraceOptions)>, + _: Option, + ) -> Result>; - /// Executes the given raw transaction and returns a number of possible traces for it. - #[rpc(name = "trace_rawTransaction")] - fn raw_transaction(&self, Bytes, TraceOptions, Option) -> Result; + /// Executes the given raw transaction and returns a number of possible traces for it. + #[rpc(name = "trace_rawTransaction")] + fn raw_transaction( + &self, + _: Bytes, + _: TraceOptions, + _: Option, + ) -> Result; - /// Executes the transaction with the given hash and returns a number of possible traces for it. - #[rpc(name = "trace_replayTransaction")] - fn replay_transaction(&self, H256, TraceOptions) -> Result; + /// Executes the transaction with the given hash and returns a number of possible traces for it. + #[rpc(name = "trace_replayTransaction")] + fn replay_transaction(&self, _: H256, _: TraceOptions) -> Result; - /// Executes all the transactions at the given block and returns a number of possible traces for each transaction. - #[rpc(name = "trace_replayBlockTransactions")] - fn replay_block_transactions(&self, BlockNumber, TraceOptions) -> Result>; + /// Executes all the transactions at the given block and returns a number of possible traces for each transaction. + #[rpc(name = "trace_replayBlockTransactions")] + fn replay_block_transactions( + &self, + _: BlockNumber, + _: TraceOptions, + ) -> Result>; } diff --git a/rpc/src/v1/traits/web3.rs b/rpc/src/v1/traits/web3.rs index dd464ee1c22..a2323a26b46 100644 --- a/rpc/src/v1/traits/web3.rs +++ b/rpc/src/v1/traits/web3.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Web3 rpc interface. use ethereum_types::H256; @@ -22,13 +22,13 @@ use jsonrpc_derive::rpc; use v1::types::Bytes; /// Web3 rpc interface. -#[rpc] +#[rpc(server)] pub trait Web3 { - /// Returns current client version. - #[rpc(name = "web3_clientVersion")] - fn client_version(&self) -> Result; + /// Returns current client version. + #[rpc(name = "web3_clientVersion")] + fn client_version(&self) -> Result; - /// Returns sha3 of the given data - #[rpc(name = "web3_sha3")] - fn sha3(&self, Bytes) -> Result; + /// Returns sha3 of the given data + #[rpc(name = "web3_sha3")] + fn sha3(&self, _: Bytes) -> Result; } diff --git a/rpc/src/v1/types/account_info.rs b/rpc/src/v1/types/account_info.rs index 6d7585f87fc..dd1eb612c61 100644 --- a/rpc/src/v1/types/account_info.rs +++ b/rpc/src/v1/types/account_info.rs @@ -1,86 +1,77 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Return types for RPC calls -use ethereum_types::{Public, Address, H160, H256, U256}; +use ethereum_types::{Address, Public, H160, H256, U256}; use v1::types::Bytes; /// Account information. #[derive(Debug, Default, Clone, PartialEq, Serialize)] pub struct AccountInfo { - /// Account name - pub name: String, + /// Account name + pub name: String, } /// Datastructure with proof for one single storage-entry #[derive(Debug, Default, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct StorageProof { - pub key: U256, - pub value: U256, - pub proof: Vec + pub key: U256, + pub value: U256, + pub proof: Vec, } /// Account information. #[derive(Debug, Default, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct EthAccount { - pub address: H160, - pub balance: U256, - pub nonce: U256, - pub code_hash: H256, - pub storage_hash: H256, - pub account_proof: Vec, - pub storage_proof: Vec, + pub address: H160, + pub balance: U256, + pub nonce: U256, + pub code_hash: H256, + pub storage_hash: H256, + pub account_proof: Vec, + pub storage_proof: Vec, } /// Extended account information (used by `parity_allAccountInfo`). #[derive(Debug, Default, Clone, PartialEq, Serialize)] pub struct ExtAccountInfo { - /// Account name - pub name: String, - /// Account meta JSON - pub meta: String, - /// Account UUID (`None` for address book entries) - #[serde(skip_serializing_if = "Option::is_none")] - pub uuid: Option, -} - -/// Hardware wallet information. -#[derive(Debug, Default, Clone, PartialEq, Serialize)] -pub struct HwAccountInfo { - /// Device name. - pub name: String, - /// Device manufacturer. - pub manufacturer: String, + /// Account name + pub name: String, + /// Account meta JSON + pub meta: String, + /// Account UUID (`None` for address book entries) + #[serde(skip_serializing_if = "Option::is_none")] + pub uuid: Option, } /// account derived from a signature /// as well as information that tells if it is valid for /// the current chain #[derive(Debug, Clone, Serialize)] -#[serde(rename_all="camelCase")] +#[serde(rename_all = "camelCase")] pub struct RecoveredAccount { - /// address of the recovered account - pub address: Address, - /// public key of the recovered account - pub public_key: Public, - /// If the signature contains chain replay protection, - /// And the chain_id encoded within the signature - /// matches the current chain this would be true, otherwise false. - pub is_valid_for_current_chain: bool + /// address of the recovered account + pub address: Address, + /// public key of the recovered account + pub public_key: Public, + /// If the signature contains chain replay protection, + /// And the chain_id encoded within the signature + /// matches the current chain this would be true, otherwise false. + pub is_valid_for_current_chain: bool, } diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index 61f4402af09..49c603fdeb2 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -1,144 +1,144 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::ops::Deref; -use std::collections::BTreeMap; +use std::{collections::BTreeMap, ops::Deref}; -use ethereum_types::{H160, H256, U256, Bloom as H2048}; -use serde::ser::Error; -use serde::{Serialize, Serializer}; +use ethereum_types::{Bloom as H2048, H160, H256, U256}; +use serde::{ser::Error, Serialize, Serializer}; use types::encoded::Header as EthHeader; use v1::types::{Bytes, Transaction}; /// Block Transactions #[derive(Debug)] pub enum BlockTransactions { - /// Only hashes - Hashes(Vec), - /// Full transactions - Full(Vec) + /// Only hashes + Hashes(Vec), + /// Full transactions + Full(Vec), } impl Serialize for BlockTransactions { - fn serialize(&self, serializer: S) -> Result - where S: Serializer { - match *self { - BlockTransactions::Hashes(ref hashes) => hashes.serialize(serializer), - BlockTransactions::Full(ref ts) => ts.serialize(serializer) - } - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + BlockTransactions::Hashes(ref hashes) => hashes.serialize(serializer), + BlockTransactions::Full(ref ts) => ts.serialize(serializer), + } + } } /// Block representation #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct Block { - /// Hash of the block - pub hash: Option, - /// Hash of the parent - pub parent_hash: H256, - /// Hash of the uncles - #[serde(rename = "sha3Uncles")] - pub uncles_hash: H256, - /// Authors address - pub author: H160, - /// Alias of `author` - pub miner: H160, - /// State root hash - pub state_root: H256, - /// Transactions root hash - pub transactions_root: H256, - /// Transactions receipts root hash - pub receipts_root: H256, - /// Block number - pub number: Option, - /// Gas Used - pub gas_used: U256, - /// Gas Limit - pub gas_limit: U256, - /// Extra data - pub extra_data: Bytes, - /// Logs bloom - pub logs_bloom: Option, - /// Timestamp - pub timestamp: U256, - /// Difficulty - pub difficulty: U256, - /// Total difficulty - pub total_difficulty: Option, - /// Seal fields - pub seal_fields: Vec, - /// Uncles' hashes - pub uncles: Vec, - /// Transactions - pub transactions: BlockTransactions, - /// Size in bytes - pub size: Option, + /// Hash of the block + pub hash: Option, + /// Hash of the parent + pub parent_hash: H256, + /// Hash of the uncles + #[serde(rename = "sha3Uncles")] + pub uncles_hash: H256, + /// Authors address + pub author: H160, + /// Alias of `author` + pub miner: H160, + /// State root hash + pub state_root: H256, + /// Transactions root hash + pub transactions_root: H256, + /// Transactions receipts root hash + pub receipts_root: H256, + /// Block number + pub number: Option, + /// Gas Used + pub gas_used: U256, + /// Gas Limit + pub gas_limit: U256, + /// Extra data + pub extra_data: Bytes, + /// Logs bloom + pub logs_bloom: Option, + /// Timestamp + pub timestamp: U256, + /// Difficulty + pub difficulty: U256, + /// Total difficulty + pub total_difficulty: Option, + /// Seal fields + pub seal_fields: Vec, + /// Uncles' hashes + pub uncles: Vec, + /// Transactions + pub transactions: BlockTransactions, + /// Size in bytes + pub size: Option, } /// Block header representation. #[derive(Debug, Clone, Serialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Header { - /// Hash of the block - pub hash: Option, - /// Hash of the parent - pub parent_hash: H256, - /// Hash of the uncles - #[serde(rename = "sha3Uncles")] - pub uncles_hash: H256, - /// Authors address - pub author: H160, - /// Alias of `author` - pub miner: H160, - /// State root hash - pub state_root: H256, - /// Transactions root hash - pub transactions_root: H256, - /// Transactions receipts root hash - pub receipts_root: H256, - /// Block number - pub number: Option, - /// Gas Used - pub gas_used: U256, - /// Gas Limit - pub gas_limit: U256, - /// Extra data - pub extra_data: Bytes, - /// Logs bloom - pub logs_bloom: H2048, - /// Timestamp - pub timestamp: U256, - /// Difficulty - pub difficulty: U256, - /// Seal fields - pub seal_fields: Vec, - /// Size in bytes - pub size: Option, + /// Hash of the block + pub hash: Option, + /// Hash of the parent + pub parent_hash: H256, + /// Hash of the uncles + #[serde(rename = "sha3Uncles")] + pub uncles_hash: H256, + /// Authors address + pub author: H160, + /// Alias of `author` + pub miner: H160, + /// State root hash + pub state_root: H256, + /// Transactions root hash + pub transactions_root: H256, + /// Transactions receipts root hash + pub receipts_root: H256, + /// Block number + pub number: Option, + /// Gas Used + pub gas_used: U256, + /// Gas Limit + pub gas_limit: U256, + /// Extra data + pub extra_data: Bytes, + /// Logs bloom + pub logs_bloom: H2048, + /// Timestamp + pub timestamp: U256, + /// Difficulty + pub difficulty: U256, + /// Seal fields + pub seal_fields: Vec, + /// Size in bytes + pub size: Option, } impl From for Header { - fn from(h: EthHeader) -> Self { - (&h).into() - } + fn from(h: EthHeader) -> Self { + (&h).into() + } } impl<'a> From<&'a EthHeader> for Header { - fn from(h: &'a EthHeader) -> Self { - Header { + fn from(h: &'a EthHeader) -> Self { + Header { hash: Some(h.hash()), size: Some(h.rlp().as_raw().len().into()), parent_hash: h.parent_hash(), @@ -159,7 +159,7 @@ impl<'a> From<&'a EthHeader> for Header { .expect("Client/Miner returns only valid headers. We only serialize headers from Client/Miner; qed") .into_iter().map(Into::into).collect(), } - } + } } /// Block representation with additional info. @@ -171,164 +171,193 @@ pub type RichHeader = Rich
; /// Value representation with additional info #[derive(Debug, Clone, PartialEq, Eq)] pub struct Rich { - /// Standard value. - pub inner: T, - /// Engine-specific fields with additional description. - /// Should be included directly to serialized block object. - // TODO [ToDr] #[serde(skip_serializing)] - pub extra_info: BTreeMap, + /// Standard value. + pub inner: T, + /// Engine-specific fields with additional description. + /// Should be included directly to serialized block object. + // TODO [ToDr] #[serde(skip_serializing)] + pub extra_info: BTreeMap, } impl Deref for Rich { - type Target = T; - fn deref(&self) -> &Self::Target { - &self.inner - } + type Target = T; + fn deref(&self) -> &Self::Target { + &self.inner + } } impl Serialize for Rich { - fn serialize(&self, serializer: S) -> Result where S: Serializer { - use serde_json::{to_value, Value}; + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use serde_json::{to_value, Value}; - let serialized = (to_value(&self.inner), to_value(&self.extra_info)); - if let (Ok(Value::Object(mut value)), Ok(Value::Object(extras))) = serialized { - // join two objects - value.extend(extras); - // and serialize - value.serialize(serializer) - } else { - Err(S::Error::custom("Unserializable structures: expected objects")) - } - } + let serialized = (to_value(&self.inner), to_value(&self.extra_info)); + if let (Ok(Value::Object(mut value)), Ok(Value::Object(extras))) = serialized { + // join two objects + value.extend(extras); + // and serialize + value.serialize(serializer) + } else { + Err(S::Error::custom( + "Unserializable structures: expected objects", + )) + } + } } #[cfg(test)] mod tests { - use std::collections::BTreeMap; - use ethereum_types::{H64, H160, H256, U256, Bloom as H2048}; - use serde_json; - use v1::types::{Transaction, Bytes}; - use super::{Block, RichBlock, BlockTransactions, Header, RichHeader}; + use super::{Block, BlockTransactions, Header, RichBlock, RichHeader}; + use ethereum_types::{Bloom as H2048, H160, H256, H64, U256}; + use serde_json; + use std::collections::BTreeMap; + use v1::types::{Bytes, Transaction}; - #[test] - fn test_serialize_block_transactions() { - let t = BlockTransactions::Full(vec![Transaction::default()]); - let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"chainId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0","condition":null}]"#); + #[test] + fn test_serialize_block_transactions() { + let t = BlockTransactions::Full(vec![Transaction::default()]); + let serialized = serde_json::to_string(&t).unwrap(); + assert_eq!( + serialized, + r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"chainId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0","condition":null}]"# + ); - let t = BlockTransactions::Hashes(vec![H256::default().into()]); - let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"["0x0000000000000000000000000000000000000000000000000000000000000000"]"#); - } + let t = BlockTransactions::Hashes(vec![H256::default().into()]); + let serialized = serde_json::to_string(&t).unwrap(); + assert_eq!( + serialized, + r#"["0x0000000000000000000000000000000000000000000000000000000000000000"]"# + ); + } - #[test] - fn test_serialize_block() { - let block = Block { - hash: Some(H256::default()), - parent_hash: H256::default(), - uncles_hash: H256::default(), - author: H160::default(), - miner: H160::default(), - state_root: H256::default(), - transactions_root: H256::default(), - receipts_root: H256::default(), - number: Some(U256::default()), - gas_used: U256::default(), - gas_limit: U256::default(), - extra_data: Bytes::default(), - logs_bloom: Some(H2048::default()), - timestamp: U256::default(), - difficulty: U256::default(), - total_difficulty: Some(U256::default()), - seal_fields: vec![Bytes::default(), Bytes::default()], - uncles: vec![], - transactions: BlockTransactions::Hashes(vec![].into()), - size: Some(69.into()), - }; - let serialized_block = serde_json::to_string(&block).unwrap(); - let rich_block = RichBlock { - inner: block, - extra_info: map![ - "mixHash".into() => format!("{:?}", H256::default()), - "nonce".into() => format!("{:?}", H64::default()) - ], - }; - let serialized_rich_block = serde_json::to_string(&rich_block).unwrap(); + #[test] + fn test_serialize_block() { + let block = Block { + hash: Some(H256::default()), + parent_hash: H256::default(), + uncles_hash: H256::default(), + author: H160::default(), + miner: H160::default(), + state_root: H256::default(), + transactions_root: H256::default(), + receipts_root: H256::default(), + number: Some(U256::default()), + gas_used: U256::default(), + gas_limit: U256::default(), + extra_data: Bytes::default(), + logs_bloom: Some(H2048::default()), + timestamp: U256::default(), + difficulty: U256::default(), + total_difficulty: Some(U256::default()), + seal_fields: vec![Bytes::default(), Bytes::default()], + uncles: vec![], + transactions: BlockTransactions::Hashes(vec![].into()), + size: Some(69.into()), + }; + let serialized_block = serde_json::to_string(&block).unwrap(); + let rich_block = RichBlock { + inner: block, + extra_info: map![ + "mixHash".into() => format!("{:?}", H256::default()), + "nonce".into() => format!("{:?}", H64::default()) + ], + }; + let serialized_rich_block = serde_json::to_string(&rich_block).unwrap(); - assert_eq!(serialized_block, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","author":"0x0000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","number":"0x0","gasUsed":"0x0","gasLimit":"0x0","extraData":"0x","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","difficulty":"0x0","totalDifficulty":"0x0","sealFields":["0x","0x"],"uncles":[],"transactions":[],"size":"0x45"}"#); - assert_eq!(serialized_rich_block, r#"{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x0","extraData":"0x","gasLimit":"0x0","gasUsed":"0x0","hash":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","sealFields":["0x","0x"],"sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","size":"0x45","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","totalDifficulty":"0x0","transactions":[],"transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","uncles":[]}"#); - } + assert_eq!( + serialized_block, + r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","author":"0x0000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","number":"0x0","gasUsed":"0x0","gasLimit":"0x0","extraData":"0x","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","difficulty":"0x0","totalDifficulty":"0x0","sealFields":["0x","0x"],"uncles":[],"transactions":[],"size":"0x45"}"# + ); + assert_eq!( + serialized_rich_block, + r#"{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x0","extraData":"0x","gasLimit":"0x0","gasUsed":"0x0","hash":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","sealFields":["0x","0x"],"sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","size":"0x45","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","totalDifficulty":"0x0","transactions":[],"transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","uncles":[]}"# + ); + } - #[test] - fn none_size_null() { - let block = Block { - hash: Some(H256::default()), - parent_hash: H256::default(), - uncles_hash: H256::default(), - author: H160::default(), - miner: H160::default(), - state_root: H256::default(), - transactions_root: H256::default(), - receipts_root: H256::default(), - number: Some(U256::default()), - gas_used: U256::default(), - gas_limit: U256::default(), - extra_data: Bytes::default(), - logs_bloom: Some(H2048::default()), - timestamp: U256::default(), - difficulty: U256::default(), - total_difficulty: Some(U256::default()), - seal_fields: vec![Bytes::default(), Bytes::default()], - uncles: vec![], - transactions: BlockTransactions::Hashes(vec![].into()), - size: None, - }; - let serialized_block = serde_json::to_string(&block).unwrap(); - let rich_block = RichBlock { - inner: block, - extra_info: map![ - "mixHash".into() => format!("{:?}", H256::default()), - "nonce".into() => format!("{:?}", H64::default()) - ], - }; - let serialized_rich_block = serde_json::to_string(&rich_block).unwrap(); + #[test] + fn none_size_null() { + let block = Block { + hash: Some(H256::default()), + parent_hash: H256::default(), + uncles_hash: H256::default(), + author: H160::default(), + miner: H160::default(), + state_root: H256::default(), + transactions_root: H256::default(), + receipts_root: H256::default(), + number: Some(U256::default()), + gas_used: U256::default(), + gas_limit: U256::default(), + extra_data: Bytes::default(), + logs_bloom: Some(H2048::default()), + timestamp: U256::default(), + difficulty: U256::default(), + total_difficulty: Some(U256::default()), + seal_fields: vec![Bytes::default(), Bytes::default()], + uncles: vec![], + transactions: BlockTransactions::Hashes(vec![].into()), + size: None, + }; + let serialized_block = serde_json::to_string(&block).unwrap(); + let rich_block = RichBlock { + inner: block, + extra_info: map![ + "mixHash".into() => format!("{:?}", H256::default()), + "nonce".into() => format!("{:?}", H64::default()) + ], + }; + let serialized_rich_block = serde_json::to_string(&rich_block).unwrap(); - assert_eq!(serialized_block, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","author":"0x0000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","number":"0x0","gasUsed":"0x0","gasLimit":"0x0","extraData":"0x","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","difficulty":"0x0","totalDifficulty":"0x0","sealFields":["0x","0x"],"uncles":[],"transactions":[],"size":null}"#); - assert_eq!(serialized_rich_block, r#"{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x0","extraData":"0x","gasLimit":"0x0","gasUsed":"0x0","hash":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","sealFields":["0x","0x"],"sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","size":null,"stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","totalDifficulty":"0x0","transactions":[],"transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","uncles":[]}"#); - } + assert_eq!( + serialized_block, + r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","author":"0x0000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","number":"0x0","gasUsed":"0x0","gasLimit":"0x0","extraData":"0x","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","difficulty":"0x0","totalDifficulty":"0x0","sealFields":["0x","0x"],"uncles":[],"transactions":[],"size":null}"# + ); + assert_eq!( + serialized_rich_block, + r#"{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x0","extraData":"0x","gasLimit":"0x0","gasUsed":"0x0","hash":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","sealFields":["0x","0x"],"sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","size":null,"stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","totalDifficulty":"0x0","transactions":[],"transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","uncles":[]}"# + ); + } - #[test] - fn test_serialize_header() { - let header = Header { - hash: Some(H256::default()), - parent_hash: H256::default(), - uncles_hash: H256::default(), - author: H160::default(), - miner: H160::default(), - state_root: H256::default(), - transactions_root: H256::default(), - receipts_root: H256::default(), - number: Some(U256::default()), - gas_used: U256::default(), - gas_limit: U256::default(), - extra_data: Bytes::default(), - logs_bloom: H2048::default(), - timestamp: U256::default(), - difficulty: U256::default(), - seal_fields: vec![Bytes::default(), Bytes::default()], - size: Some(69.into()), - }; - let serialized_header = serde_json::to_string(&header).unwrap(); - let rich_header = RichHeader { - inner: header, - extra_info: map![ - "mixHash".into() => format!("{:?}", H256::default()), - "nonce".into() => format!("{:?}", H64::default()) - ], - }; - let serialized_rich_header = serde_json::to_string(&rich_header).unwrap(); + #[test] + fn test_serialize_header() { + let header = Header { + hash: Some(H256::default()), + parent_hash: H256::default(), + uncles_hash: H256::default(), + author: H160::default(), + miner: H160::default(), + state_root: H256::default(), + transactions_root: H256::default(), + receipts_root: H256::default(), + number: Some(U256::default()), + gas_used: U256::default(), + gas_limit: U256::default(), + extra_data: Bytes::default(), + logs_bloom: H2048::default(), + timestamp: U256::default(), + difficulty: U256::default(), + seal_fields: vec![Bytes::default(), Bytes::default()], + size: Some(69.into()), + }; + let serialized_header = serde_json::to_string(&header).unwrap(); + let rich_header = RichHeader { + inner: header, + extra_info: map![ + "mixHash".into() => format!("{:?}", H256::default()), + "nonce".into() => format!("{:?}", H64::default()) + ], + }; + let serialized_rich_header = serde_json::to_string(&rich_header).unwrap(); - assert_eq!(serialized_header, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","author":"0x0000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","number":"0x0","gasUsed":"0x0","gasLimit":"0x0","extraData":"0x","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","difficulty":"0x0","sealFields":["0x","0x"],"size":"0x45"}"#); - assert_eq!(serialized_rich_header, r#"{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x0","extraData":"0x","gasLimit":"0x0","gasUsed":"0x0","hash":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","sealFields":["0x","0x"],"sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","size":"0x45","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000"}"#); - } + assert_eq!( + serialized_header, + r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","author":"0x0000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","number":"0x0","gasUsed":"0x0","gasLimit":"0x0","extraData":"0x","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","difficulty":"0x0","sealFields":["0x","0x"],"size":"0x45"}"# + ); + assert_eq!( + serialized_rich_header, + r#"{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x0","extraData":"0x","gasLimit":"0x0","gasUsed":"0x0","hash":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","sealFields":["0x","0x"],"sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","size":"0x45","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000"}"# + ); + } } diff --git a/rpc/src/v1/types/block_number.rs b/rpc/src/v1/types/block_number.rs index 7e19f2d3d9a..6ac3afb5761 100644 --- a/rpc/src/v1/types/block_number.rs +++ b/rpc/src/v1/types/block_number.rs @@ -1,162 +1,260 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use std::fmt; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use serde::de::{Error, Visitor}; use ethcore::client::BlockId; +use hash::H256; +use serde::{ + de::{Error, MapAccess, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; +use std::fmt; /// Represents rpc api block number param. #[derive(Debug, PartialEq, Clone, Hash, Eq)] pub enum BlockNumber { - /// Number - Num(u64), - /// Latest block - Latest, - /// Earliest block (genesis) - Earliest, - /// Pending block (being mined) - Pending, + /// Hash + Hash { + /// block hash + hash: H256, + /// only return blocks part of the canon chain + require_canonical: bool, + }, + /// Number + Num(u64), + /// Latest block + Latest, + /// Earliest block (genesis) + Earliest, + /// Pending block (being mined) + Pending, } impl Default for BlockNumber { - fn default() -> Self { - BlockNumber::Latest - } + fn default() -> Self { + BlockNumber::Latest + } } impl<'a> Deserialize<'a> for BlockNumber { - fn deserialize(deserializer: D) -> Result where D: Deserializer<'a> { - deserializer.deserialize_any(BlockNumberVisitor) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + deserializer.deserialize_any(BlockNumberVisitor) + } } impl BlockNumber { - /// Convert block number to min block target. - pub fn to_min_block_num(&self) -> Option { - match *self { - BlockNumber::Num(ref x) => Some(*x), - _ => None, - } - } -} - -/// BlockNumber to BlockId conversion -/// -/// NOTE use only for light clients. -pub trait LightBlockNumber { - /// Convert block number to block id. - fn to_block_id(self) -> BlockId; -} - -impl LightBlockNumber for BlockNumber { - fn to_block_id(self) -> BlockId { - // NOTE Here we treat `Pending` as `Latest`. - // Since light clients don't produce pending blocks - // (they don't have state) we can safely fallback to `Latest`. - match self { - BlockNumber::Num(n) => BlockId::Number(n), - BlockNumber::Earliest => BlockId::Earliest, - BlockNumber::Latest => BlockId::Latest, - BlockNumber::Pending => { - warn!("`Pending` is deprecated and may be removed in future versions. Falling back to `Latest`"); - BlockId::Latest - } - } - } + /// Convert block number to min block target. + pub fn to_min_block_num(&self) -> Option { + match *self { + BlockNumber::Num(ref x) => Some(*x), + _ => None, + } + } } impl Serialize for BlockNumber { - fn serialize(&self, serializer: S) -> Result where S: Serializer { - match *self { - BlockNumber::Num(ref x) => serializer.serialize_str(&format!("0x{:x}", x)), - BlockNumber::Latest => serializer.serialize_str("latest"), - BlockNumber::Earliest => serializer.serialize_str("earliest"), - BlockNumber::Pending => serializer.serialize_str("pending"), - } - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + BlockNumber::Hash { + hash, + require_canonical, + } => serializer.serialize_str(&format!( + "{{ 'hash': '{}', 'requireCanonical': '{}' }}", + hash, require_canonical + )), + BlockNumber::Num(ref x) => serializer.serialize_str(&format!("0x{:x}", x)), + BlockNumber::Latest => serializer.serialize_str("latest"), + BlockNumber::Earliest => serializer.serialize_str("earliest"), + BlockNumber::Pending => serializer.serialize_str("pending"), + } + } } struct BlockNumberVisitor; impl<'a> Visitor<'a> for BlockNumberVisitor { - type Value = BlockNumber; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a block number or 'latest', 'earliest' or 'pending'") - } - - fn visit_str(self, value: &str) -> Result where E: Error { - match value { - "latest" => Ok(BlockNumber::Latest), - "earliest" => Ok(BlockNumber::Earliest), - "pending" => Ok(BlockNumber::Pending), - _ if value.starts_with("0x") => u64::from_str_radix(&value[2..], 16).map(BlockNumber::Num).map_err(|e| { - Error::custom(format!("Invalid block number: {}", e)) - }), - _ => Err(Error::custom("Invalid block number: missing 0x prefix".to_string())), - } - } - - fn visit_string(self, value: String) -> Result where E: Error { - self.visit_str(value.as_ref()) - } + type Value = BlockNumber; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!( + formatter, + "a block number or 'latest', 'earliest' or 'pending'" + ) + } + + fn visit_map(self, mut visitor: V) -> Result + where + V: MapAccess<'a>, + { + let (mut require_canonical, mut block_number, mut block_hash) = + (false, None::, None::); + + loop { + let key_str: Option = visitor.next_key()?; + + match key_str { + Some(key) => match key.as_str() { + "blockNumber" => { + let value: String = visitor.next_value()?; + if value.starts_with("0x") { + let number = u64::from_str_radix(&value[2..], 16).map_err(|e| { + Error::custom(format!("Invalid block number: {}", e)) + })?; + + block_number = Some(number); + break; + } else { + return Err(Error::custom( + "Invalid block number: missing 0x prefix".to_string(), + )); + } + } + "blockHash" => { + block_hash = Some(visitor.next_value()?); + } + "requireCanonical" => { + require_canonical = visitor.next_value()?; + } + key => return Err(Error::custom(format!("Unknown key: {}", key))), + }, + None => break, + }; + } + + if let Some(number) = block_number { + return Ok(BlockNumber::Num(number)); + } + + if let Some(hash) = block_hash { + return Ok(BlockNumber::Hash { + hash, + require_canonical, + }); + } + + return Err(Error::custom("Invalid input")); + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + match value { + "latest" => Ok(BlockNumber::Latest), + "earliest" => Ok(BlockNumber::Earliest), + "pending" => Ok(BlockNumber::Pending), + _ if value.starts_with("0x") => u64::from_str_radix(&value[2..], 16) + .map(BlockNumber::Num) + .map_err(|e| Error::custom(format!("Invalid block number: {}", e))), + _ => Err(Error::custom( + "Invalid block number: missing 0x prefix".to_string(), + )), + } + } + + fn visit_string(self, value: String) -> Result + where + E: Error, + { + self.visit_str(value.as_ref()) + } } /// Converts `BlockNumber` to `BlockId`, panics on `BlockNumber::Pending` pub fn block_number_to_id(number: BlockNumber) -> BlockId { - match number { - BlockNumber::Num(num) => BlockId::Number(num), - BlockNumber::Earliest => BlockId::Earliest, - BlockNumber::Latest => BlockId::Latest, - - BlockNumber::Pending => panic!("`BlockNumber::Pending` should be handled manually") - } + match number { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), + BlockNumber::Num(num) => BlockId::Number(num), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + BlockNumber::Pending => panic!("`BlockNumber::Pending` should be handled manually"), + } } #[cfg(test)] mod tests { - use ethcore::client::BlockId; - use super::*; - use serde_json; - - #[test] - fn block_number_deserialization() { - let s = r#"["0xa", "latest", "earliest", "pending"]"#; - let deserialized: Vec = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, vec![BlockNumber::Num(10), BlockNumber::Latest, BlockNumber::Earliest, BlockNumber::Pending]) - } - - #[test] - fn should_not_deserialize_decimal() { - let s = r#""10""#; - assert!(serde_json::from_str::(s).is_err()); - } - - #[test] - fn normal_block_number_to_id() { - assert_eq!(block_number_to_id(BlockNumber::Num(100)), BlockId::Number(100)); - assert_eq!(block_number_to_id(BlockNumber::Earliest), BlockId::Earliest); - assert_eq!(block_number_to_id(BlockNumber::Latest), BlockId::Latest); - } - - #[test] - #[should_panic] - fn pending_block_number_to_id() { - // Since this function is not allowed to be called in such way, panic should happen - block_number_to_id(BlockNumber::Pending); - } + use super::*; + use ethcore::client::BlockId; + use serde_json; + use std::str::FromStr; + + #[test] + fn block_number_deserialization() { + let s = r#"[ + "0xa", + "latest", + "earliest", + "pending", + {"blockNumber": "0xa"}, + {"blockHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"}, + {"blockHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "requireCanonical": true} + ]"#; + let deserialized: Vec = serde_json::from_str(s).unwrap(); + + assert_eq!( + deserialized, + vec![ + BlockNumber::Num(10), + BlockNumber::Latest, + BlockNumber::Earliest, + BlockNumber::Pending, + BlockNumber::Num(10), + BlockNumber::Hash { + hash: H256::from_str( + "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + ) + .unwrap(), + require_canonical: false + }, + BlockNumber::Hash { + hash: H256::from_str( + "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + ) + .unwrap(), + require_canonical: true + } + ] + ) + } + + #[test] + fn should_not_deserialize() { + let s = r#"[{}, "10"]"#; + assert!(serde_json::from_str::>(s).is_err()); + } + + #[test] + fn normal_block_number_to_id() { + assert_eq!( + block_number_to_id(BlockNumber::Num(100)), + BlockId::Number(100) + ); + assert_eq!(block_number_to_id(BlockNumber::Earliest), BlockId::Earliest); + assert_eq!(block_number_to_id(BlockNumber::Latest), BlockId::Latest); + } + + #[test] + #[should_panic] + fn pending_block_number_to_id() { + // Since this function is not allowed to be called in such way, panic should happen + block_number_to_id(BlockNumber::Pending); + } } diff --git a/rpc/src/v1/types/bytes.rs b/rpc/src/v1/types/bytes.rs index 837f3b5f9a6..e5792dd2857 100644 --- a/rpc/src/v1/types/bytes.rs +++ b/rpc/src/v1/types/bytes.rs @@ -1,121 +1,137 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Serializable wrapper around vector of bytes +use rustc_hex::{FromHex, ToHex}; +use serde::{ + de::{Error, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; use std::fmt; -use rustc_hex::{ToHex, FromHex}; -use serde::{Serialize, Serializer, Deserialize, Deserializer}; -use serde::de::{Error, Visitor}; /// Wrapper structure around vector of bytes. #[derive(Debug, PartialEq, Eq, Default, Hash, Clone)] pub struct Bytes(pub Vec); impl Bytes { - /// Simple constructor. - pub fn new(bytes: Vec) -> Bytes { - Bytes(bytes) - } - /// Convert back to vector - pub fn into_vec(self) -> Vec { - self.0 - } + /// Simple constructor. + pub fn new(bytes: Vec) -> Bytes { + Bytes(bytes) + } + /// Convert back to vector + pub fn into_vec(self) -> Vec { + self.0 + } } impl From> for Bytes { - fn from(bytes: Vec) -> Bytes { - Bytes(bytes) - } + fn from(bytes: Vec) -> Bytes { + Bytes(bytes) + } } impl Into> for Bytes { - fn into(self) -> Vec { - self.0 - } + fn into(self) -> Vec { + self.0 + } } impl Serialize for Bytes { - fn serialize(&self, serializer: S) -> Result - where S: Serializer { - let mut serialized = "0x".to_owned(); - serialized.push_str(self.0.to_hex().as_ref()); - serializer.serialize_str(serialized.as_ref()) - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut serialized = "0x".to_owned(); + serialized.push_str(self.0.to_hex().as_ref()); + serializer.serialize_str(serialized.as_ref()) + } } impl<'a> Deserialize<'a> for Bytes { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> { - deserializer.deserialize_any(BytesVisitor) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + deserializer.deserialize_any(BytesVisitor) + } } struct BytesVisitor; impl<'a> Visitor<'a> for BytesVisitor { - type Value = Bytes; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a 0x-prefixed, hex-encoded vector of bytes") - } - - fn visit_str(self, value: &str) -> Result where E: Error { - if value.len() >= 2 && value.starts_with("0x") && value.len() & 1 == 0 { - Ok(Bytes::new(FromHex::from_hex(&value[2..]).map_err(|e| Error::custom(format!("Invalid hex: {}", e)))?)) - } else { - Err(Error::custom("Invalid bytes format. Expected a 0x-prefixed hex string with even length")) - } - } - - fn visit_string(self, value: String) -> Result where E: Error { - self.visit_str(value.as_ref()) - } + type Value = Bytes; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a 0x-prefixed, hex-encoded vector of bytes") + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + if value.len() >= 2 && value.starts_with("0x") && value.len() & 1 == 0 { + Ok(Bytes::new(FromHex::from_hex(&value[2..]).map_err(|e| { + Error::custom(format!("Invalid hex: {}", e)) + })?)) + } else { + Err(Error::custom( + "Invalid bytes format. Expected a 0x-prefixed hex string with even length", + )) + } + } + + fn visit_string(self, value: String) -> Result + where + E: Error, + { + self.visit_str(value.as_ref()) + } } #[cfg(test)] mod tests { - use super::*; - use serde_json; - use rustc_hex::FromHex; - - #[test] - fn test_bytes_serialize() { - let bytes = Bytes("0123456789abcdef".from_hex().unwrap()); - let serialized = serde_json::to_string(&bytes).unwrap(); - assert_eq!(serialized, r#""0x0123456789abcdef""#); - } - - #[test] - fn test_bytes_deserialize() { - let bytes0: Result = serde_json::from_str(r#""∀∂""#); - let bytes1: Result = serde_json::from_str(r#""""#); - let bytes2: Result = serde_json::from_str(r#""0x123""#); - let bytes3: Result = serde_json::from_str(r#""0xgg""#); - - let bytes4: Bytes = serde_json::from_str(r#""0x""#).unwrap(); - let bytes5: Bytes = serde_json::from_str(r#""0x12""#).unwrap(); - let bytes6: Bytes = serde_json::from_str(r#""0x0123""#).unwrap(); - - assert!(bytes0.is_err()); - assert!(bytes1.is_err()); - assert!(bytes2.is_err()); - assert!(bytes3.is_err()); - assert_eq!(bytes4, Bytes(vec![])); - assert_eq!(bytes5, Bytes(vec![0x12])); - assert_eq!(bytes6, Bytes(vec![0x1, 0x23])); - } + use super::*; + use rustc_hex::FromHex; + use serde_json; + + #[test] + fn test_bytes_serialize() { + let bytes = Bytes("0123456789abcdef".from_hex().unwrap()); + let serialized = serde_json::to_string(&bytes).unwrap(); + assert_eq!(serialized, r#""0x0123456789abcdef""#); + } + + #[test] + fn test_bytes_deserialize() { + let bytes0: Result = serde_json::from_str(r#""∀∂""#); + let bytes1: Result = serde_json::from_str(r#""""#); + let bytes2: Result = serde_json::from_str(r#""0x123""#); + let bytes3: Result = serde_json::from_str(r#""0xgg""#); + + let bytes4: Bytes = serde_json::from_str(r#""0x""#).unwrap(); + let bytes5: Bytes = serde_json::from_str(r#""0x12""#).unwrap(); + let bytes6: Bytes = serde_json::from_str(r#""0x0123""#).unwrap(); + + assert!(bytes0.is_err()); + assert!(bytes1.is_err()); + assert!(bytes2.is_err()); + assert!(bytes3.is_err()); + assert_eq!(bytes4, Bytes(vec![])); + assert_eq!(bytes5, Bytes(vec![0x12])); + assert_eq!(bytes6, Bytes(vec![0x1, 0x23])); + } } diff --git a/rpc/src/v1/types/call_request.rs b/rpc/src/v1/types/call_request.rs index d75e4b1a2d7..ac465d3ca36 100644 --- a/rpc/src/v1/types/call_request.rs +++ b/rpc/src/v1/types/call_request.rs @@ -1,69 +1,68 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use ethereum_types::{H160, U256}; -use v1::helpers::CallRequest as Request; -use v1::types::Bytes; +use v1::{helpers::CallRequest as Request, types::Bytes}; /// Call request #[derive(Debug, Default, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub struct CallRequest { - /// From - pub from: Option, - /// To - pub to: Option, - /// Gas Price - pub gas_price: Option, - /// Gas - pub gas: Option, - /// Value - pub value: Option, - /// Data - pub data: Option, - /// Nonce - pub nonce: Option, + /// From + pub from: Option, + /// To + pub to: Option, + /// Gas Price + pub gas_price: Option, + /// Gas + pub gas: Option, + /// Value + pub value: Option, + /// Data + pub data: Option, + /// Nonce + pub nonce: Option, } impl Into for CallRequest { - fn into(self) -> Request { - Request { - from: self.from.map(Into::into), - to: self.to.map(Into::into), - gas_price: self.gas_price.map(Into::into), - gas: self.gas.map(Into::into), - value: self.value.map(Into::into), - data: self.data.map(Into::into), - nonce: self.nonce.map(Into::into), - } - } + fn into(self) -> Request { + Request { + from: self.from.map(Into::into), + to: self.to.map(Into::into), + gas_price: self.gas_price.map(Into::into), + gas: self.gas.map(Into::into), + value: self.value.map(Into::into), + data: self.data.map(Into::into), + nonce: self.nonce.map(Into::into), + } + } } #[cfg(test)] mod tests { - use std::str::FromStr; - use rustc_hex::FromHex; - use serde_json; - use ethereum_types::{U256, H160}; - use super::CallRequest; + use super::CallRequest; + use ethereum_types::{H160, U256}; + use rustc_hex::FromHex; + use serde_json; + use std::str::FromStr; - #[test] - fn call_request_deserialize() { - let s = r#"{ + #[test] + fn call_request_deserialize() { + let s = r#"{ "from":"0x0000000000000000000000000000000000000001", "to":"0x0000000000000000000000000000000000000002", "gasPrice":"0x1", @@ -72,22 +71,25 @@ mod tests { "data":"0x123456", "nonce":"0x4" }"#; - let deserialized: CallRequest = serde_json::from_str(s).unwrap(); + let deserialized: CallRequest = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, CallRequest { - from: Some(H160::from(1)), - to: Some(H160::from(2)), - gas_price: Some(U256::from(1)), - gas: Some(U256::from(2)), - value: Some(U256::from(3)), - data: Some(vec![0x12, 0x34, 0x56].into()), - nonce: Some(U256::from(4)), - }); - } + assert_eq!( + deserialized, + CallRequest { + from: Some(H160::from(1)), + to: Some(H160::from(2)), + gas_price: Some(U256::from(1)), + gas: Some(U256::from(2)), + value: Some(U256::from(3)), + data: Some(vec![0x12, 0x34, 0x56].into()), + nonce: Some(U256::from(4)), + } + ); + } - #[test] - fn call_request_deserialize2() { - let s = r#"{ + #[test] + fn call_request_deserialize2() { + let s = r#"{ "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "gas": "0x76c0", @@ -95,9 +97,9 @@ mod tests { "value": "0x9184e72a", "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" }"#; - let deserialized: CallRequest = serde_json::from_str(s).unwrap(); + let deserialized: CallRequest = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, CallRequest { + assert_eq!(deserialized, CallRequest { from: Some(H160::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap()), to: Some(H160::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), gas_price: Some(U256::from_str("9184e72a000").unwrap()), @@ -106,21 +108,24 @@ mod tests { data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap().into()), nonce: None }); - } + } - #[test] - fn call_request_deserialize_empty() { - let s = r#"{"from":"0x0000000000000000000000000000000000000001"}"#; - let deserialized: CallRequest = serde_json::from_str(s).unwrap(); + #[test] + fn call_request_deserialize_empty() { + let s = r#"{"from":"0x0000000000000000000000000000000000000001"}"#; + let deserialized: CallRequest = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, CallRequest { - from: Some(H160::from(1)), - to: None, - gas_price: None, - gas: None, - value: None, - data: None, - nonce: None, - }); - } + assert_eq!( + deserialized, + CallRequest { + from: Some(H160::from(1)), + to: None, + gas_price: None, + gas: None, + value: None, + data: None, + nonce: None, + } + ); + } } diff --git a/rpc/src/v1/types/confirmations.rs b/rpc/src/v1/types/confirmations.rs index dedd0ba2539..b64f1a5a3cc 100644 --- a/rpc/src/v1/types/confirmations.rs +++ b/rpc/src/v1/types/confirmations.rs @@ -1,193 +1,202 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Types used in Confirmations queue (Trusted Signer) -use std::fmt; -use serde::{Serialize, Serializer}; use ansi_term::Colour; use bytes::ToPretty; +use serde::{Serialize, Serializer}; +use std::fmt; use ethereum_types::{H160, H256, H520, U256}; -use v1::types::{TransactionRequest, RichRawTransaction, Bytes, TransactionCondition, Origin}; -use v1::helpers; use ethkey::Password; +use v1::{ + helpers, + types::{Bytes, Origin, RichRawTransaction, TransactionCondition, TransactionRequest}, +}; /// Confirmation waiting in a queue #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct ConfirmationRequest { - /// Id of this confirmation - pub id: U256, - /// Payload - pub payload: ConfirmationPayload, - /// Request origin - pub origin: Origin, + /// Id of this confirmation + pub id: U256, + /// Payload + pub payload: ConfirmationPayload, + /// Request origin + pub origin: Origin, } impl From for ConfirmationRequest { - fn from(c: helpers::ConfirmationRequest) -> Self { - ConfirmationRequest { - id: c.id, - payload: c.payload.into(), - origin: c.origin, - } - } + fn from(c: helpers::ConfirmationRequest) -> Self { + ConfirmationRequest { + id: c.id, + payload: c.payload.into(), + origin: c.origin, + } + } } impl fmt::Display for ConfirmationRequest { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "#{}: {} coming from {}", self.id, self.payload, self.origin) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "#{}: {} coming from {}", + self.id, self.payload, self.origin + ) + } } impl fmt::Display for ConfirmationPayload { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ConfirmationPayload::SendTransaction(ref transaction) => write!(f, "{}", transaction), - ConfirmationPayload::SignTransaction(ref transaction) => write!(f, "(Sign only) {}", transaction), - ConfirmationPayload::EthSignMessage(ref sign) => write!(f, "{}", sign), - ConfirmationPayload::EIP191SignMessage(ref sign) => write!(f, "{}", sign), - ConfirmationPayload::Decrypt(ref decrypt) => write!(f, "{}", decrypt), - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ConfirmationPayload::SendTransaction(ref transaction) => write!(f, "{}", transaction), + ConfirmationPayload::SignTransaction(ref transaction) => { + write!(f, "(Sign only) {}", transaction) + } + ConfirmationPayload::EthSignMessage(ref sign) => write!(f, "{}", sign), + ConfirmationPayload::EIP191SignMessage(ref sign) => write!(f, "{}", sign), + ConfirmationPayload::Decrypt(ref decrypt) => write!(f, "{}", decrypt), + } + } } /// Ethereum-prefixed Sign request #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct EthSignRequest { - /// Address - pub address: H160, - /// Hash to sign - pub data: Bytes, + /// Address + pub address: H160, + /// Hash to sign + pub data: Bytes, } /// EIP191 Sign request #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct EIP191SignRequest { - /// Address - pub address: H160, - /// Hash to sign - pub data: H256, + /// Address + pub address: H160, + /// Hash to sign + pub data: H256, } impl From<(H160, H256)> for EIP191SignRequest { - fn from(tuple: (H160, H256)) -> Self { - EIP191SignRequest { - address: tuple.0, - data: tuple.1, - } - } + fn from(tuple: (H160, H256)) -> Self { + EIP191SignRequest { + address: tuple.0, + data: tuple.1, + } + } } impl fmt::Display for EIP191SignRequest { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "sign 0x{} with {}", - self.data.0.pretty(), - Colour::White.bold().paint(format!("0x{:?}", self.address)), - ) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "sign 0x{} with {}", + self.data.0.pretty(), + Colour::White.bold().paint(format!("0x{:?}", self.address)), + ) + } } impl From<(H160, Bytes)> for EthSignRequest { - fn from(tuple: (H160, Bytes)) -> Self { - EthSignRequest { - address: tuple.0, - data: tuple.1, - } - } + fn from(tuple: (H160, Bytes)) -> Self { + EthSignRequest { + address: tuple.0, + data: tuple.1, + } + } } impl fmt::Display for EthSignRequest { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "sign 0x{} with {}", - self.data.0.pretty(), - Colour::White.bold().paint(format!("0x{:?}", self.address)), - ) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "sign 0x{} with {}", + self.data.0.pretty(), + Colour::White.bold().paint(format!("0x{:?}", self.address)), + ) + } } /// Decrypt request #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct DecryptRequest { - /// Address - pub address: H160, - /// Message to decrypt - pub msg: Bytes, + /// Address + pub address: H160, + /// Message to decrypt + pub msg: Bytes, } impl From<(H160, Bytes)> for DecryptRequest { - fn from(tuple: (H160, Bytes)) -> Self { - DecryptRequest { - address: tuple.0, - msg: tuple.1, - } - } + fn from(tuple: (H160, Bytes)) -> Self { + DecryptRequest { + address: tuple.0, + msg: tuple.1, + } + } } impl fmt::Display for DecryptRequest { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "decrypt data with {}", - Colour::White.bold().paint(format!("0x{:?}", self.address)), - ) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "decrypt data with {}", + Colour::White.bold().paint(format!("0x{:?}", self.address)), + ) + } } /// Confirmation response for particular payload #[derive(Debug, Clone, PartialEq)] pub enum ConfirmationResponse { - /// Transaction Hash - SendTransaction(H256), - /// Transaction RLP - SignTransaction(RichRawTransaction), - /// Signature (encoded as VRS) - Signature(H520), - /// Decrypted data - Decrypt(Bytes), + /// Transaction Hash + SendTransaction(H256), + /// Transaction RLP + SignTransaction(RichRawTransaction), + /// Signature (encoded as VRS) + Signature(H520), + /// Decrypted data + Decrypt(Bytes), } impl Serialize for ConfirmationResponse { - fn serialize(&self, serializer: S) -> Result - where S: Serializer - { - match *self { - ConfirmationResponse::SendTransaction(ref hash) => hash.serialize(serializer), - ConfirmationResponse::SignTransaction(ref rlp) => rlp.serialize(serializer), - ConfirmationResponse::Signature(ref signature) => signature.serialize(serializer), - ConfirmationResponse::Decrypt(ref data) => data.serialize(serializer), - } - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + ConfirmationResponse::SendTransaction(ref hash) => hash.serialize(serializer), + ConfirmationResponse::SignTransaction(ref rlp) => rlp.serialize(serializer), + ConfirmationResponse::Signature(ref signature) => signature.serialize(serializer), + ConfirmationResponse::Decrypt(ref data) => data.serialize(serializer), + } + } } /// Confirmation response with additional token for further requests #[derive(Clone, PartialEq, Serialize)] pub struct ConfirmationResponseWithToken { - /// Actual response - pub result: ConfirmationResponse, - /// New token - pub token: Password, + /// Actual response + pub result: ConfirmationResponse, + /// New token + pub token: Password, } /// Confirmation payload, i.e. the thing to be confirmed @@ -195,38 +204,45 @@ pub struct ConfirmationResponseWithToken { #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub enum ConfirmationPayload { - /// Send Transaction - SendTransaction(TransactionRequest), - /// Sign Transaction - SignTransaction(TransactionRequest), - /// Signature - #[serde(rename = "sign")] - EthSignMessage(EthSignRequest), - /// signature without prefix - EIP191SignMessage(EIP191SignRequest), - /// Decryption - Decrypt(DecryptRequest), + /// Send Transaction + SendTransaction(TransactionRequest), + /// Sign Transaction + SignTransaction(TransactionRequest), + /// Signature + #[serde(rename = "sign")] + EthSignMessage(EthSignRequest), + /// signature without prefix + EIP191SignMessage(EIP191SignRequest), + /// Decryption + Decrypt(DecryptRequest), } impl From for ConfirmationPayload { - fn from(c: helpers::ConfirmationPayload) -> Self { - match c { - helpers::ConfirmationPayload::SendTransaction(t) => ConfirmationPayload::SendTransaction(t.into()), - helpers::ConfirmationPayload::SignTransaction(t) => ConfirmationPayload::SignTransaction(t.into()), - helpers::ConfirmationPayload::EthSignMessage(address, data) => ConfirmationPayload::EthSignMessage(EthSignRequest { - address, - data: data.into(), - }), - helpers::ConfirmationPayload::SignMessage(address, data) => ConfirmationPayload::EIP191SignMessage(EIP191SignRequest { - address, - data, - }), - helpers::ConfirmationPayload::Decrypt(address, msg) => ConfirmationPayload::Decrypt(DecryptRequest { - address, - msg: msg.into(), - }), - } - } + fn from(c: helpers::ConfirmationPayload) -> Self { + match c { + helpers::ConfirmationPayload::SendTransaction(t) => { + ConfirmationPayload::SendTransaction(t.into()) + } + helpers::ConfirmationPayload::SignTransaction(t) => { + ConfirmationPayload::SignTransaction(t.into()) + } + helpers::ConfirmationPayload::EthSignMessage(address, data) => { + ConfirmationPayload::EthSignMessage(EthSignRequest { + address, + data: data.into(), + }) + } + helpers::ConfirmationPayload::SignMessage(address, data) => { + ConfirmationPayload::EIP191SignMessage(EIP191SignRequest { address, data }) + } + helpers::ConfirmationPayload::Decrypt(address, msg) => { + ConfirmationPayload::Decrypt(DecryptRequest { + address, + msg: msg.into(), + }) + } + } + } } /// Possible modifications to the confirmed transaction sent by `Trusted Signer` @@ -234,202 +250,214 @@ impl From for ConfirmationPayload { #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub struct TransactionModification { - /// Modified transaction sender - pub sender: Option, - /// Modified gas price - pub gas_price: Option, - /// Modified gas - pub gas: Option, - /// Modified transaction condition. - pub condition: Option>, + /// Modified transaction sender + pub sender: Option, + /// Modified gas price + pub gas_price: Option, + /// Modified gas + pub gas: Option, + /// Modified transaction condition. + pub condition: Option>, } /// Represents two possible return values. #[derive(Debug, Clone)] -pub enum Either where - A: fmt::Debug + Clone, - B: fmt::Debug + Clone, +pub enum Either +where + A: fmt::Debug + Clone, + B: fmt::Debug + Clone, { - /// Primary value - Either(A), - /// Secondary value - Or(B), + /// Primary value + Either(A), + /// Secondary value + Or(B), } -impl From for Either where - A: fmt::Debug + Clone, - B: fmt::Debug + Clone, +impl From for Either +where + A: fmt::Debug + Clone, + B: fmt::Debug + Clone, { - fn from(a: A) -> Self { - Either::Either(a) - } + fn from(a: A) -> Self { + Either::Either(a) + } } -impl Serialize for Either where - A: Serialize + fmt::Debug + Clone, - B: Serialize + fmt::Debug + Clone, +impl Serialize for Either +where + A: Serialize + fmt::Debug + Clone, + B: Serialize + fmt::Debug + Clone, { - fn serialize(&self, serializer: S) -> Result - where S: Serializer - { - match *self { - Either::Either(ref a) => a.serialize(serializer), - Either::Or(ref b) => b.serialize(serializer), - } - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + Either::Either(ref a) => a.serialize(serializer), + Either::Or(ref b) => b.serialize(serializer), + } + } } #[cfg(test)] mod tests { - use std::str::FromStr; + use super::*; use ethereum_types::{H256, U256}; - use serde_json; - use v1::types::TransactionCondition; - use v1::helpers; - use super::*; - - #[test] - fn should_serialize_sign_confirmation() { - // given - let request = helpers::ConfirmationRequest { - id: 15.into(), - payload: helpers::ConfirmationPayload::EthSignMessage(1.into(), vec![5].into()), - origin: Origin::Rpc("test service".into()), - }; - - // when - let res = serde_json::to_string(&ConfirmationRequest::from(request)); - let expected = r#"{"id":"0xf","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","data":"0x05"}},"origin":{"rpc":"test service"}}"#; - - // then - assert_eq!(res.unwrap(), expected.to_owned()); - } - - #[test] - fn should_serialize_transaction_confirmation() { - // given - let request = helpers::ConfirmationRequest { - id: 15.into(), - payload: helpers::ConfirmationPayload::SendTransaction(helpers::FilledTransactionRequest { - from: 0.into(), - used_default_from: false, - to: None, - gas: 15_000.into(), - gas_price: 10_000.into(), - value: 100_000.into(), - data: vec![1, 2, 3], - nonce: Some(1.into()), - condition: None, - }), - origin: Origin::Signer { - session: 5.into(), - } - }; - - // when - let res = serde_json::to_string(&ConfirmationRequest::from(request)); - let expected = r#"{"id":"0xf","payload":{"sendTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1","condition":null}},"origin":{"signer":{"session":"0x0000000000000000000000000000000000000000000000000000000000000005"}}}"#; - - // then - assert_eq!(res.unwrap(), expected.to_owned()); - } - - #[test] - fn should_serialize_sign_transaction_confirmation() { - // given - let request = helpers::ConfirmationRequest { - id: 15.into(), - payload: helpers::ConfirmationPayload::SignTransaction(helpers::FilledTransactionRequest { - from: 0.into(), - used_default_from: false, - to: None, - gas: 15_000.into(), - gas_price: 10_000.into(), - value: 100_000.into(), - data: vec![1, 2, 3], - nonce: Some(1.into()), - condition: None, - }), - origin: Origin::Unknown, - }; - - // when - let res = serde_json::to_string(&ConfirmationRequest::from(request)); - let expected = r#"{"id":"0xf","payload":{"signTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1","condition":null}},"origin":"unknown"}"#; - - // then - assert_eq!(res.unwrap(), expected.to_owned()); - } - - #[test] - fn should_serialize_decrypt_confirmation() { - // given - let request = helpers::ConfirmationRequest { - id: 15.into(), - payload: helpers::ConfirmationPayload::Decrypt( - 10.into(), vec![1, 2, 3].into(), - ), - origin: Default::default(), - }; - - // when - let res = serde_json::to_string(&ConfirmationRequest::from(request)); - let expected = r#"{"id":"0xf","payload":{"decrypt":{"address":"0x000000000000000000000000000000000000000a","msg":"0x010203"}},"origin":"unknown"}"#; - - // then - assert_eq!(res.unwrap(), expected.to_owned()); - } - - #[test] - fn should_deserialize_modification() { - // given - let s1 = r#"{ + use serde_json; + use std::str::FromStr; + use v1::{helpers, types::TransactionCondition}; + + #[test] + fn should_serialize_sign_confirmation() { + // given + let request = helpers::ConfirmationRequest { + id: 15.into(), + payload: helpers::ConfirmationPayload::EthSignMessage(1.into(), vec![5].into()), + origin: Origin::Rpc("test service".into()), + }; + + // when + let res = serde_json::to_string(&ConfirmationRequest::from(request)); + let expected = r#"{"id":"0xf","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","data":"0x05"}},"origin":{"rpc":"test service"}}"#; + + // then + assert_eq!(res.unwrap(), expected.to_owned()); + } + + #[test] + fn should_serialize_transaction_confirmation() { + // given + let request = helpers::ConfirmationRequest { + id: 15.into(), + payload: helpers::ConfirmationPayload::SendTransaction( + helpers::FilledTransactionRequest { + from: 0.into(), + used_default_from: false, + to: None, + gas: 15_000.into(), + gas_price: 10_000.into(), + value: 100_000.into(), + data: vec![1, 2, 3], + nonce: Some(1.into()), + condition: None, + }, + ), + origin: Origin::Signer { session: 5.into() }, + }; + + // when + let res = serde_json::to_string(&ConfirmationRequest::from(request)); + let expected = r#"{"id":"0xf","payload":{"sendTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1","condition":null}},"origin":{"signer":{"session":"0x0000000000000000000000000000000000000000000000000000000000000005"}}}"#; + + // then + assert_eq!(res.unwrap(), expected.to_owned()); + } + + #[test] + fn should_serialize_sign_transaction_confirmation() { + // given + let request = helpers::ConfirmationRequest { + id: 15.into(), + payload: helpers::ConfirmationPayload::SignTransaction( + helpers::FilledTransactionRequest { + from: 0.into(), + used_default_from: false, + to: None, + gas: 15_000.into(), + gas_price: 10_000.into(), + value: 100_000.into(), + data: vec![1, 2, 3], + nonce: Some(1.into()), + condition: None, + }, + ), + origin: Origin::Unknown, + }; + + // when + let res = serde_json::to_string(&ConfirmationRequest::from(request)); + let expected = r#"{"id":"0xf","payload":{"signTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1","condition":null}},"origin":"unknown"}"#; + + // then + assert_eq!(res.unwrap(), expected.to_owned()); + } + + #[test] + fn should_serialize_decrypt_confirmation() { + // given + let request = helpers::ConfirmationRequest { + id: 15.into(), + payload: helpers::ConfirmationPayload::Decrypt(10.into(), vec![1, 2, 3].into()), + origin: Default::default(), + }; + + // when + let res = serde_json::to_string(&ConfirmationRequest::from(request)); + let expected = r#"{"id":"0xf","payload":{"decrypt":{"address":"0x000000000000000000000000000000000000000a","msg":"0x010203"}},"origin":"unknown"}"#; + + // then + assert_eq!(res.unwrap(), expected.to_owned()); + } + + #[test] + fn should_deserialize_modification() { + // given + let s1 = r#"{ "sender": "0x000000000000000000000000000000000000000a", "gasPrice":"0xba43b7400", "condition": { "block": 66 } }"#; - let s2 = r#"{"gas": "0x1233"}"#; - let s3 = r#"{}"#; - - // when - let res1: TransactionModification = serde_json::from_str(s1).unwrap(); - let res2: TransactionModification = serde_json::from_str(s2).unwrap(); - let res3: TransactionModification = serde_json::from_str(s3).unwrap(); - - // then - assert_eq!(res1, TransactionModification { - sender: Some(10.into()), - gas_price: Some(U256::from_str("0ba43b7400").unwrap()), - gas: None, - condition: Some(Some(TransactionCondition::Number(0x42))), - }); - assert_eq!(res2, TransactionModification { - sender: None, - gas_price: None, - gas: Some(U256::from_str("1233").unwrap()), - condition: None, - }); - assert_eq!(res3, TransactionModification { - sender: None, - gas_price: None, - gas: None, - condition: None, - }); - } - - #[test] - fn should_serialize_confirmation_response_with_token() { - // given - let response = ConfirmationResponseWithToken { - result: ConfirmationResponse::SendTransaction(H256::default()), - token: "test-token".into(), - }; - - // when - let res = serde_json::to_string(&response); - let expected = r#"{"result":"0x0000000000000000000000000000000000000000000000000000000000000000","token":"test-token"}"#; - - // then - assert_eq!(res.unwrap(), expected.to_owned()); - } + let s2 = r#"{"gas": "0x1233"}"#; + let s3 = r#"{}"#; + + // when + let res1: TransactionModification = serde_json::from_str(s1).unwrap(); + let res2: TransactionModification = serde_json::from_str(s2).unwrap(); + let res3: TransactionModification = serde_json::from_str(s3).unwrap(); + + // then + assert_eq!( + res1, + TransactionModification { + sender: Some(10.into()), + gas_price: Some(U256::from_str("0ba43b7400").unwrap()), + gas: None, + condition: Some(Some(TransactionCondition::Number(0x42))), + } + ); + assert_eq!( + res2, + TransactionModification { + sender: None, + gas_price: None, + gas: Some(U256::from_str("1233").unwrap()), + condition: None, + } + ); + assert_eq!( + res3, + TransactionModification { + sender: None, + gas_price: None, + gas: None, + condition: None, + } + ); + } + + #[test] + fn should_serialize_confirmation_response_with_token() { + // given + let response = ConfirmationResponseWithToken { + result: ConfirmationResponse::SendTransaction(H256::default()), + token: "test-token".into(), + }; + + // when + let res = serde_json::to_string(&response); + let expected = r#"{"result":"0x0000000000000000000000000000000000000000000000000000000000000000","token":"test-token"}"#; + + // then + assert_eq!(res.unwrap(), expected.to_owned()); + } } diff --git a/rpc/src/v1/types/consensus_status.rs b/rpc/src/v1/types/consensus_status.rs deleted file mode 100644 index da2aa26a1d8..00000000000 --- a/rpc/src/v1/types/consensus_status.rs +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use ethereum_types::{H160, H256}; -use semver; -use updater::{self, CapState}; - -/// Capability info -#[derive(Debug, PartialEq, Serialize)] -#[serde(rename_all = "camelCase")] -pub enum ConsensusCapability { - /// Unknown. - Unknown, - /// Capable of consensus indefinitely. - Capable, - /// Capable of consensus up until a definite block. - CapableUntil(u64), - /// Incapable of consensus since a particular block. - IncapableSince(u64), -} - -impl Into for CapState { - fn into(self) -> ConsensusCapability { - match self { - CapState::Unknown => ConsensusCapability::Unknown, - CapState::Capable => ConsensusCapability::Capable, - CapState::CapableUntil(n) => ConsensusCapability::CapableUntil(n), - CapState::IncapableSince(n) => ConsensusCapability::IncapableSince(n), - } - } -} - -/// A release's track. -#[derive(Debug, PartialEq, Serialize)] -#[serde(rename_all = "camelCase")] -pub enum ReleaseTrack { - /// Stable track. - Stable, - /// Beta track. - Beta, - /// Nightly track. - Nightly, - /// Testing track. - Testing, - /// No known track. - #[serde(rename = "null")] - Unknown, -} - -impl Into for updater::ReleaseTrack { - fn into(self) -> ReleaseTrack { - match self { - updater::ReleaseTrack::Stable => ReleaseTrack::Stable, - updater::ReleaseTrack::Beta => ReleaseTrack::Beta, - updater::ReleaseTrack::Nightly => ReleaseTrack::Nightly, - updater::ReleaseTrack::Testing => ReleaseTrack::Testing, - updater::ReleaseTrack::Unknown => ReleaseTrack::Unknown, - } - } -} - -/// Semantic version. -#[derive(Debug, PartialEq, Serialize)] -pub struct Version { - /// Major part. - major: u64, - /// Minor part. - minor: u64, - /// Patch part. - patch: u64, -} - -impl Into for semver::Version { - fn into(self) -> Version { - Version { - major: self.major, - minor: self.minor, - patch: self.patch, - } - } -} - -/// Version information of a particular release. -#[derive(Debug, PartialEq, Serialize)] -pub struct VersionInfo { - /// The track on which it was released. - pub track: ReleaseTrack, - /// The version. - pub version: Version, - /// The (SHA1?) 160-bit hash of this build's code base. - pub hash: H160, -} - -impl Into for updater::VersionInfo { - fn into(self) -> VersionInfo { - VersionInfo { - track: self.track.into(), - version: self.version.into(), - hash: self.hash, - } - } -} - -/// Information regarding a particular release of Parity -#[derive(Debug, PartialEq, Serialize)] -pub struct ReleaseInfo { - /// Information on the version. - pub version: VersionInfo, - /// Does this release contain critical security updates? - pub is_critical: bool, - /// The latest fork that this release can handle. - pub fork: u64, - /// Our platform's binary, if known. - pub binary: Option, -} - -impl Into for updater::ReleaseInfo { - fn into(self) -> ReleaseInfo { - ReleaseInfo { - version: self.version.into(), - is_critical: self.is_critical, - fork: self.fork, - binary: self.binary.map(Into::into), - } - } -} - -/// Information on our operations environment. -#[derive(Debug, PartialEq, Serialize)] -pub struct OperationsInfo { - /// Our blockchain's latest fork. - pub fork: u64, - /// Last fork our client supports, if known. - pub this_fork: Option, - /// Information on our track's latest release. - pub track: ReleaseInfo, - /// Information on our minor version's latest release. - pub minor: Option, -} - -impl Into for updater::OperationsInfo { - fn into(self) -> OperationsInfo { - OperationsInfo { - fork: self.fork, - this_fork: self.this_fork, - track: self.track.into(), - minor: self.minor.map(Into::into), - } - } -} diff --git a/rpc/src/v1/types/derivation.rs b/rpc/src/v1/types/derivation.rs index 1f2764d9ff2..c1f6340402e 100644 --- a/rpc/src/v1/types/derivation.rs +++ b/rpc/src/v1/types/derivation.rs @@ -1,48 +1,51 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . +use serde::{ + de::{Error, Visitor}, + Deserialize, Deserializer, +}; use std::fmt; -use serde::{Deserialize, Deserializer}; -use serde::de::{Error, Visitor}; use ethereum_types::H256; -use ethstore; /// Type of derivation pub enum DerivationType { - /// Soft - allow proof of parent - Soft, - /// Hard - does not allow proof of parent - Hard, + /// Soft - allow proof of parent + Soft, + /// Hard - does not allow proof of parent + Hard, } /// Derivation request by hash +#[allow(dead_code)] #[derive(Deserialize)] pub struct DeriveHash { - hash: H256, - #[serde(rename = "type")] - d_type: DerivationType, + hash: H256, + #[serde(rename = "type")] + d_type: DerivationType, } /// Node propertoes in hierarchical derivation request +#[allow(dead_code)] #[derive(Deserialize)] pub struct DeriveHierarchicalItem { - index: u64, - #[serde(rename = "type")] - d_type: DerivationType, + index: u64, + #[serde(rename = "type")] + d_type: DerivationType, } /// Hierarchical (index sequence) request @@ -50,83 +53,96 @@ pub type DeriveHierarchical = Vec; /// Generic derivate request pub enum Derive { - /// Hierarchical (index sequence) request - Hierarchical(DeriveHierarchical), - /// Hash request - Hash(DeriveHash), + /// Hierarchical (index sequence) request + Hierarchical(DeriveHierarchical), + /// Hash request + Hash(DeriveHash), } impl From for Derive { - fn from(d: DeriveHierarchical) -> Self { - Derive::Hierarchical(d) - } + fn from(d: DeriveHierarchical) -> Self { + Derive::Hierarchical(d) + } } impl From for Derive { - fn from(d: DeriveHash) -> Self { - Derive::Hash(d) - } + fn from(d: DeriveHash) -> Self { + Derive::Hash(d) + } } /// Error converting request data #[cfg(any(test, feature = "accounts"))] #[derive(Debug)] pub enum ConvertError { - IndexOverlfow(u64), + IndexOverlfow(u64), } impl Derive { - /// Convert to account provider struct dealing with possible overflows - #[cfg(any(test, feature = "accounts"))] - pub fn to_derivation(self) -> Result { - Ok(match self { - Derive::Hierarchical(drv) => { - ethstore::Derivation::Hierarchical({ - let mut members = Vec::::new(); - for h in drv { - if h.index > ::std::u32::MAX as u64 { return Err(ConvertError::IndexOverlfow(h.index)); } - members.push(match h.d_type { - DerivationType::Soft => ethstore::IndexDerivation { soft: true, index: h.index as u32 }, - DerivationType::Hard => ethstore::IndexDerivation { soft: false, index: h.index as u32 }, - }); - } - members - }) - }, - Derive::Hash(drv) => { - match drv.d_type { - DerivationType::Soft => ethstore::Derivation::SoftHash(drv.hash.into()), - DerivationType::Hard => ethstore::Derivation::HardHash(drv.hash.into()), - } - }, - }) - } + /// Convert to account provider struct dealing with possible overflows + #[cfg(any(test, feature = "accounts"))] + pub fn to_derivation(self) -> Result { + Ok(match self { + Derive::Hierarchical(drv) => ethstore::Derivation::Hierarchical({ + let mut members = Vec::::new(); + for h in drv { + if h.index > ::std::u32::MAX as u64 { + return Err(ConvertError::IndexOverlfow(h.index)); + } + members.push(match h.d_type { + DerivationType::Soft => ethstore::IndexDerivation { + soft: true, + index: h.index as u32, + }, + DerivationType::Hard => ethstore::IndexDerivation { + soft: false, + index: h.index as u32, + }, + }); + } + members + }), + Derive::Hash(drv) => match drv.d_type { + DerivationType::Soft => ethstore::Derivation::SoftHash(drv.hash.into()), + DerivationType::Hard => ethstore::Derivation::HardHash(drv.hash.into()), + }, + }) + } } impl<'a> Deserialize<'a> for DerivationType { - fn deserialize(deserializer: D) -> Result where D: Deserializer<'a> { - deserializer.deserialize_any(DerivationTypeVisitor) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + deserializer.deserialize_any(DerivationTypeVisitor) + } } struct DerivationTypeVisitor; impl<'a> Visitor<'a> for DerivationTypeVisitor { - type Value = DerivationType; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "'hard' or 'soft'") - } - - fn visit_str(self, value: &str) -> Result where E: Error { - match value { - "soft" => Ok(DerivationType::Soft), - "hard" => Ok(DerivationType::Hard), - v => Err(Error::custom(format!("invalid derivation type: {:?}", v))), - } - } - - fn visit_string(self, value: String) -> Result where E: Error { - self.visit_str(value.as_ref()) - } + type Value = DerivationType; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "'hard' or 'soft'") + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + match value { + "soft" => Ok(DerivationType::Soft), + "hard" => Ok(DerivationType::Hard), + v => Err(Error::custom(format!("invalid derivation type: {:?}", v))), + } + } + + fn visit_string(self, value: String) -> Result + where + E: Error, + { + self.visit_str(value.as_ref()) + } } diff --git a/rpc/src/v1/types/eip191.rs b/rpc/src/v1/types/eip191.rs index fe3aab4c51a..6733cc778d3 100644 --- a/rpc/src/v1/types/eip191.rs +++ b/rpc/src/v1/types/eip191.rs @@ -1,59 +1,63 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! EIP-191 specific types use ethereum_types::H160; -use serde::{Deserialize, Deserializer}; -use serde::de; +use serde::{de, Deserialize, Deserializer}; use v1::types::Bytes; /// EIP-191 version specifier #[derive(Debug)] pub enum EIP191Version { - /// byte specifier for structured data (0x01) - StructuredData, - /// byte specifier for personal message (0x45) - PersonalMessage, - /// byte specifier for presignedtransaction (0x00) - PresignedTransaction + /// byte specifier for structured data (0x01) + StructuredData, + /// byte specifier for personal message (0x45) + PersonalMessage, + /// byte specifier for presignedtransaction (0x00) + PresignedTransaction, } /// EIP-191 version 0x0 struct #[derive(Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct PresignedTransaction { - // address of intended validator - pub validator: H160, - // application specific data - pub data: Bytes + // address of intended validator + pub validator: H160, + // application specific data + pub data: Bytes, } impl<'de> Deserialize<'de> for EIP191Version { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - let byte_version = match s.as_str() { - "0x00" => EIP191Version::PresignedTransaction, - "0x01" => EIP191Version::StructuredData, - "0x45" => EIP191Version::PersonalMessage, - other => return Err(de::Error::custom(format!("Invalid byte version '{}'", other))), - }; - Ok(byte_version) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + let byte_version = match s.as_str() { + "0x00" => EIP191Version::PresignedTransaction, + "0x01" => EIP191Version::StructuredData, + "0x45" => EIP191Version::PersonalMessage, + other => { + return Err(de::Error::custom(format!( + "Invalid byte version '{}'", + other + ))) + } + }; + Ok(byte_version) + } } diff --git a/rpc/src/v1/types/eth_types.rs b/rpc/src/v1/types/eth_types.rs index 606e7592489..f048cf814a0 100644 --- a/rpc/src/v1/types/eth_types.rs +++ b/rpc/src/v1/types/eth_types.rs @@ -5,80 +5,111 @@ type Res = Result; #[test] fn should_serialize_u256() { - let serialized1 = serde_json::to_string(&U256::from(0)).unwrap(); - let serialized2 = serde_json::to_string(&U256::from(1)).unwrap(); - let serialized3 = serde_json::to_string(&U256::from(16)).unwrap(); - let serialized4 = serde_json::to_string(&U256::from(256)).unwrap(); + let serialized1 = serde_json::to_string(&U256::from(0)).unwrap(); + let serialized2 = serde_json::to_string(&U256::from(1)).unwrap(); + let serialized3 = serde_json::to_string(&U256::from(16)).unwrap(); + let serialized4 = serde_json::to_string(&U256::from(256)).unwrap(); - assert_eq!(serialized1, r#""0x0""#); - assert_eq!(serialized2, r#""0x1""#); - assert_eq!(serialized3, r#""0x10""#); - assert_eq!(serialized4, r#""0x100""#); + assert_eq!(serialized1, r#""0x0""#); + assert_eq!(serialized2, r#""0x1""#); + assert_eq!(serialized3, r#""0x10""#); + assert_eq!(serialized4, r#""0x100""#); } #[test] fn should_serialize_h256() { - let serialized1 = serde_json::to_string(&H256::from(0)).unwrap(); - let serialized2 = serde_json::to_string(&H256::from(1)).unwrap(); - let serialized3 = serde_json::to_string(&H256::from(16)).unwrap(); - let serialized4 = serde_json::to_string(&H256::from(256)).unwrap(); + let serialized1 = serde_json::to_string(&H256::from(0)).unwrap(); + let serialized2 = serde_json::to_string(&H256::from(1)).unwrap(); + let serialized3 = serde_json::to_string(&H256::from(16)).unwrap(); + let serialized4 = serde_json::to_string(&H256::from(256)).unwrap(); - assert_eq!(serialized1, r#""0x0000000000000000000000000000000000000000000000000000000000000000""#); - assert_eq!(serialized2, r#""0x0000000000000000000000000000000000000000000000000000000000000001""#); - assert_eq!(serialized3, r#""0x0000000000000000000000000000000000000000000000000000000000000010""#); - assert_eq!(serialized4, r#""0x0000000000000000000000000000000000000000000000000000000000000100""#); + assert_eq!( + serialized1, + r#""0x0000000000000000000000000000000000000000000000000000000000000000""# + ); + assert_eq!( + serialized2, + r#""0x0000000000000000000000000000000000000000000000000000000000000001""# + ); + assert_eq!( + serialized3, + r#""0x0000000000000000000000000000000000000000000000000000000000000010""# + ); + assert_eq!( + serialized4, + r#""0x0000000000000000000000000000000000000000000000000000000000000100""# + ); } #[test] fn should_fail_to_deserialize_decimals() { - let deserialized0: Res = serde_json::from_str(r#""∀∂""#); - let deserialized1: Res = serde_json::from_str(r#""""#); - let deserialized2: Res = serde_json::from_str(r#""0""#); - let deserialized3: Res = serde_json::from_str(r#""10""#); - let deserialized4: Res = serde_json::from_str(r#""1000000""#); - let deserialized5: Res = serde_json::from_str(r#""1000000000000000000""#); - let deserialized6: Res = serde_json::from_str(r#""0x""#); + let deserialized0: Res = serde_json::from_str(r#""∀∂""#); + let deserialized1: Res = serde_json::from_str(r#""""#); + let deserialized2: Res = serde_json::from_str(r#""0""#); + let deserialized3: Res = serde_json::from_str(r#""10""#); + let deserialized4: Res = serde_json::from_str(r#""1000000""#); + let deserialized5: Res = serde_json::from_str(r#""1000000000000000000""#); + let deserialized6: Res = serde_json::from_str(r#""0x""#); - assert!(deserialized0.is_err()); - assert!(deserialized1.is_err()); - assert!(deserialized2.is_err()); - assert!(deserialized3.is_err()); - assert!(deserialized4.is_err()); - assert!(deserialized5.is_err()); - assert!(deserialized6.is_err(), "Quantities should represent zero as 0x0"); + assert!(deserialized0.is_err()); + assert!(deserialized1.is_err()); + assert!(deserialized2.is_err()); + assert!(deserialized3.is_err()); + assert!(deserialized4.is_err()); + assert!(deserialized5.is_err()); + assert!( + deserialized6.is_err(), + "Quantities should represent zero as 0x0" + ); } #[test] fn should_fail_to_deserialize_bad_hex_strings() { - let deserialized1: Result = serde_json::from_str(r#""0""#); - let deserialized2: Result = serde_json::from_str(r#""0x""#); - let deserialized3: Result = serde_json::from_str(r#""0x∀∂0000000000000000000000000000000000000000000000000000000000""#); + let deserialized1: Result = serde_json::from_str(r#""0""#); + let deserialized2: Result = serde_json::from_str(r#""0x""#); + let deserialized3: Result = + serde_json::from_str(r#""0x∀∂0000000000000000000000000000000000000000000000000000000000""#); - assert!(deserialized1.is_err(), "hex string should start with 0x"); - assert!(deserialized2.is_err(), "0x-prefixed hex string of length 64"); - assert!(deserialized3.is_err(), "hex string should only contain hex chars"); + assert!(deserialized1.is_err(), "hex string should start with 0x"); + assert!( + deserialized2.is_err(), + "0x-prefixed hex string of length 64" + ); + assert!( + deserialized3.is_err(), + "hex string should only contain hex chars" + ); } #[test] fn should_deserialize_u256() { - let deserialized1: U256 = serde_json::from_str(r#""0x0""#).unwrap(); - let deserialized2: U256 = serde_json::from_str(r#""0x1""#).unwrap(); - let deserialized3: U256 = serde_json::from_str(r#""0x01""#).unwrap(); - let deserialized4: U256 = serde_json::from_str(r#""0x100""#).unwrap(); + let deserialized1: U256 = serde_json::from_str(r#""0x0""#).unwrap(); + let deserialized2: U256 = serde_json::from_str(r#""0x1""#).unwrap(); + let deserialized3: U256 = serde_json::from_str(r#""0x01""#).unwrap(); + let deserialized4: U256 = serde_json::from_str(r#""0x100""#).unwrap(); - assert_eq!(deserialized1, 0.into()); - assert_eq!(deserialized2, 1.into()); - assert_eq!(deserialized3, 1.into()); - assert_eq!(deserialized4, 256.into()); + assert_eq!(deserialized1, 0.into()); + assert_eq!(deserialized2, 1.into()); + assert_eq!(deserialized3, 1.into()); + assert_eq!(deserialized4, 256.into()); } #[test] fn should_deserialize_h256() { - let deserialized1: H256 = serde_json::from_str(r#""0x0000000000000000000000000000000000000000000000000000000000000000""#).unwrap(); - let deserialized2: H256 = serde_json::from_str(r#""0x0000000000000000000000000000000000000000000000000000000000000001""#).unwrap(); - let deserialized3: H256 = serde_json::from_str(r#""0x0000000000000000000000000000000000000000000000000000000000000100""#).unwrap(); + let deserialized1: H256 = serde_json::from_str( + r#""0x0000000000000000000000000000000000000000000000000000000000000000""#, + ) + .unwrap(); + let deserialized2: H256 = serde_json::from_str( + r#""0x0000000000000000000000000000000000000000000000000000000000000001""#, + ) + .unwrap(); + let deserialized3: H256 = serde_json::from_str( + r#""0x0000000000000000000000000000000000000000000000000000000000000100""#, + ) + .unwrap(); - assert_eq!(deserialized1, 0.into()); - assert_eq!(deserialized2, 1.into()); - assert_eq!(deserialized3, 256.into()); + assert_eq!(deserialized1, 0.into()); + assert_eq!(deserialized2, 1.into()); + assert_eq!(deserialized3, 256.into()); } diff --git a/rpc/src/v1/types/filter.rs b/rpc/src/v1/types/filter.rs index ec9f541797c..07c841c32e6 100644 --- a/rpc/src/v1/types/filter.rs +++ b/rpc/src/v1/types/filter.rs @@ -1,54 +1,66 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use ethereum_types::{H160, H256}; -use jsonrpc_core::{Error as RpcError}; -use serde::de::{Error, DeserializeOwned}; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use serde_json::{Value, from_value}; -use types::filter::Filter as EthFilter; -use types::ids::BlockId; +use jsonrpc_core::Error as RpcError; +use serde::{ + de::{DeserializeOwned, Error}, + Deserialize, Deserializer, Serialize, Serializer, +}; +use serde_json::{from_value, Value}; +use types::{filter::Filter as EthFilter, ids::BlockId}; -use v1::types::{BlockNumber, Log}; -use v1::helpers::errors::invalid_params; +use v1::{ + helpers::errors::invalid_params, + types::{BlockNumber, Log}, +}; /// Variadic value #[derive(Debug, PartialEq, Eq, Clone, Hash)] -pub enum VariadicValue where T: DeserializeOwned { - /// Single - Single(T), - /// List - Multiple(Vec), - /// None - Null, +pub enum VariadicValue +where + T: DeserializeOwned, +{ + /// Single + Single(T), + /// List + Multiple(Vec), + /// None + Null, } -impl<'a, T> Deserialize<'a> for VariadicValue where T: DeserializeOwned { - fn deserialize(deserializer: D) -> Result, D::Error> - where D: Deserializer<'a> { - let v: Value = Deserialize::deserialize(deserializer)?; +impl<'a, T> Deserialize<'a> for VariadicValue +where + T: DeserializeOwned, +{ + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'a>, + { + let v: Value = Deserialize::deserialize(deserializer)?; - if v.is_null() { - return Ok(VariadicValue::Null); - } + if v.is_null() { + return Ok(VariadicValue::Null); + } - from_value(v.clone()).map(VariadicValue::Single) - .or_else(|_| from_value(v).map(VariadicValue::Multiple)) - .map_err(|err| D::Error::custom(format!("Invalid variadic value type: {}", err))) - } + from_value(v.clone()) + .map(VariadicValue::Single) + .or_else(|_| from_value(v).map(VariadicValue::Multiple)) + .map_err(|err| D::Error::custom(format!("Invalid variadic value type: {}", err))) + } } /// Filter Address @@ -61,151 +73,195 @@ pub type Topic = VariadicValue; #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub struct Filter { - /// From Block - pub from_block: Option, - /// To Block - pub to_block: Option, - /// Block hash - pub block_hash: Option, - /// Address - pub address: Option, - /// Topics - pub topics: Option>, - /// Limit - pub limit: Option, + /// From Block + pub from_block: Option, + /// To Block + pub to_block: Option, + /// Block hash + pub block_hash: Option, + /// Address + pub address: Option, + /// Topics + pub topics: Option>, + /// Limit + pub limit: Option, } impl Filter { - pub fn try_into(self) -> Result { - if self.block_hash.is_some() && (self.from_block.is_some() || self.to_block.is_some()) { - return Err(invalid_params("blockHash", "blockHash is mutually exclusive with fromBlock/toBlock")); - } - - let num_to_id = |num| match num { - BlockNumber::Num(n) => BlockId::Number(n), - BlockNumber::Earliest => BlockId::Earliest, - BlockNumber::Latest | BlockNumber::Pending => BlockId::Latest, - }; - - let (from_block, to_block) = match self.block_hash { - Some(hash) => (BlockId::Hash(hash), BlockId::Hash(hash)), - None => - (self.from_block.map_or_else(|| BlockId::Latest, &num_to_id), - self.to_block.map_or_else(|| BlockId::Latest, &num_to_id)), - }; - - Ok(EthFilter { - from_block, to_block, - address: self.address.and_then(|address| match address { - VariadicValue::Null => None, - VariadicValue::Single(a) => Some(vec![a]), - VariadicValue::Multiple(a) => Some(a) - }), - topics: { - let mut iter = self.topics.map_or_else(Vec::new, |topics| topics.into_iter().take(4).map(|topic| match topic { - VariadicValue::Null => None, - VariadicValue::Single(t) => Some(vec![t]), - VariadicValue::Multiple(t) => Some(t) - }).collect()).into_iter(); - - vec![ - iter.next().unwrap_or(None), - iter.next().unwrap_or(None), - iter.next().unwrap_or(None), - iter.next().unwrap_or(None) - ] - }, - limit: self.limit, - }) - } + pub fn try_into(self) -> Result { + if self.block_hash.is_some() && (self.from_block.is_some() || self.to_block.is_some()) { + return Err(invalid_params( + "blockHash", + "blockHash is mutually exclusive with fromBlock/toBlock", + )); + } + + let num_to_id = |num| match num { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), + BlockNumber::Num(n) => BlockId::Number(n), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest | BlockNumber::Pending => BlockId::Latest, + }; + + let (from_block, to_block) = match self.block_hash { + Some(hash) => (BlockId::Hash(hash), BlockId::Hash(hash)), + None => ( + self.from_block.map_or_else(|| BlockId::Latest, &num_to_id), + self.to_block.map_or_else(|| BlockId::Latest, &num_to_id), + ), + }; + + Ok(EthFilter { + from_block, + to_block, + address: self.address.and_then(|address| match address { + VariadicValue::Null => None, + VariadicValue::Single(a) => Some(vec![a]), + VariadicValue::Multiple(a) => Some(a), + }), + topics: { + let mut iter = self + .topics + .map_or_else(Vec::new, |topics| { + topics + .into_iter() + .take(4) + .map(|topic| match topic { + VariadicValue::Null => None, + VariadicValue::Single(t) => Some(vec![t]), + VariadicValue::Multiple(t) => Some(t), + }) + .collect() + }) + .into_iter(); + + vec![ + iter.next().unwrap_or(None), + iter.next().unwrap_or(None), + iter.next().unwrap_or(None), + iter.next().unwrap_or(None), + ] + }, + limit: self.limit, + }) + } } /// Results of the filter_changes RPC. #[derive(Debug, PartialEq)] pub enum FilterChanges { - /// New logs. - Logs(Vec), - /// New hashes (block or transactions) - Hashes(Vec), - /// Empty result, - Empty, + /// New logs. + Logs(Vec), + /// New hashes (block or transactions) + Hashes(Vec), + /// Empty result, + Empty, } impl Serialize for FilterChanges { - fn serialize(&self, s: S) -> Result where S: Serializer { - match *self { - FilterChanges::Logs(ref logs) => logs.serialize(s), - FilterChanges::Hashes(ref hashes) => hashes.serialize(s), - FilterChanges::Empty => (&[] as &[Value]).serialize(s), - } - } + fn serialize(&self, s: S) -> Result + where + S: Serializer, + { + match *self { + FilterChanges::Logs(ref logs) => logs.serialize(s), + FilterChanges::Hashes(ref hashes) => hashes.serialize(s), + FilterChanges::Empty => (&[] as &[Value]).serialize(s), + } + } } #[cfg(test)] mod tests { - use serde_json; - use std::str::FromStr; - use ethereum_types::H256; - use super::{VariadicValue, Topic, Filter}; - use v1::types::BlockNumber; - use types::filter::Filter as EthFilter; - use types::ids::BlockId; - - #[test] - fn topic_deserialization() { - let s = r#"["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b", null, ["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b", "0x0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc"]]"#; - let deserialized: Vec = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, vec![ - VariadicValue::Single(H256::from_str("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap().into()), - VariadicValue::Null, - VariadicValue::Multiple(vec![ - H256::from_str("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap().into(), - H256::from_str("0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc").unwrap().into(), - ]) - ]); - } - - #[test] - fn filter_deserialization() { - let s = r#"{"fromBlock":"earliest","toBlock":"latest"}"#; - let deserialized: Filter = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, Filter { - from_block: Some(BlockNumber::Earliest), - to_block: Some(BlockNumber::Latest), - block_hash: None, - address: None, - topics: None, - limit: None, - }); - } - - #[test] - fn filter_conversion() { - let filter = Filter { - from_block: Some(BlockNumber::Earliest), - to_block: Some(BlockNumber::Latest), - block_hash: None, - address: Some(VariadicValue::Multiple(vec![])), - topics: Some(vec![ - VariadicValue::Null, - VariadicValue::Single("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b".into()), - VariadicValue::Null, - ]), - limit: None, - }; - - let eth_filter: EthFilter = filter.try_into().unwrap(); - assert_eq!(eth_filter, EthFilter { - from_block: BlockId::Earliest, - to_block: BlockId::Latest, - address: Some(vec![]), - topics: vec![ - None, - Some(vec!["000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b".into()]), - None, - None, - ], - limit: None, - }); - } + use super::{Filter, Topic, VariadicValue}; + use ethereum_types::H256; + use serde_json; + use std::str::FromStr; + use types::{filter::Filter as EthFilter, ids::BlockId}; + use v1::types::BlockNumber; + + #[test] + fn topic_deserialization() { + let s = r#"["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b", null, ["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b", "0x0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc"]]"#; + let deserialized: Vec = serde_json::from_str(s).unwrap(); + assert_eq!( + deserialized, + vec![ + VariadicValue::Single( + H256::from_str( + "000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b" + ) + .unwrap() + .into() + ), + VariadicValue::Null, + VariadicValue::Multiple(vec![ + H256::from_str( + "000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b" + ) + .unwrap() + .into(), + H256::from_str( + "0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc" + ) + .unwrap() + .into(), + ]) + ] + ); + } + + #[test] + fn filter_deserialization() { + let s = r#"{"fromBlock":"earliest","toBlock":"latest"}"#; + let deserialized: Filter = serde_json::from_str(s).unwrap(); + assert_eq!( + deserialized, + Filter { + from_block: Some(BlockNumber::Earliest), + to_block: Some(BlockNumber::Latest), + block_hash: None, + address: None, + topics: None, + limit: None, + } + ); + } + + #[test] + fn filter_conversion() { + let filter = Filter { + from_block: Some(BlockNumber::Earliest), + to_block: Some(BlockNumber::Latest), + block_hash: None, + address: Some(VariadicValue::Multiple(vec![])), + topics: Some(vec![ + VariadicValue::Null, + VariadicValue::Single( + "000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b".into(), + ), + VariadicValue::Null, + ]), + limit: None, + }; + + let eth_filter: EthFilter = filter.try_into().unwrap(); + assert_eq!( + eth_filter, + EthFilter { + from_block: BlockId::Earliest, + to_block: BlockId::Latest, + address: Some(vec![]), + topics: vec![ + None, + Some(vec![ + "000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b".into() + ]), + None, + None, + ], + limit: None, + } + ); + } } diff --git a/rpc/src/v1/types/histogram.rs b/rpc/src/v1/types/histogram.rs index d7f14c514e6..292bb7fd94f 100644 --- a/rpc/src/v1/types/histogram.rs +++ b/rpc/src/v1/types/histogram.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Gas prices histogram. @@ -23,17 +23,17 @@ use ethereum_types::U256; #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub struct Histogram { - /// Gas prices for bucket edges. - pub bucket_bounds: Vec, - /// Transacion counts for each bucket. - pub counts: Vec, + /// Gas prices for bucket edges. + pub bucket_bounds: Vec, + /// Transacion counts for each bucket. + pub counts: Vec, } impl From<::stats::Histogram<::ethereum_types::U256>> for Histogram { - fn from(h: ::stats::Histogram<::ethereum_types::U256>) -> Self { - Histogram { - bucket_bounds: h.bucket_bounds.into_iter().map(Into::into).collect(), - counts: h.counts - } - } + fn from(h: ::stats::Histogram<::ethereum_types::U256>) -> Self { + Histogram { + bucket_bounds: h.bucket_bounds.into_iter().map(Into::into).collect(), + counts: h.counts, + } + } } diff --git a/rpc/src/v1/types/index.rs b/rpc/src/v1/types/index.rs index 3f4b4e31705..9c25b7b61ae 100644 --- a/rpc/src/v1/types/index.rs +++ b/rpc/src/v1/types/index.rs @@ -1,75 +1,86 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . +use serde::{ + de::{Error, Visitor}, + Deserialize, Deserializer, +}; use std::fmt; -use serde::{Deserialize, Deserializer}; -use serde::de::{Error, Visitor}; /// Represents usize. #[derive(Debug, PartialEq)] pub struct Index(usize); impl Index { - /// Convert to usize - pub fn value(&self) -> usize { - self.0 - } + /// Convert to usize + pub fn value(&self) -> usize { + self.0 + } } impl<'a> Deserialize<'a> for Index { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> { - deserializer.deserialize_any(IndexVisitor) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + deserializer.deserialize_any(IndexVisitor) + } } struct IndexVisitor; impl<'a> Visitor<'a> for IndexVisitor { - type Value = Index; + type Value = Index; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a hex-encoded or decimal index") - } + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a hex-encoded or decimal index") + } - fn visit_str(self, value: &str) -> Result where E: Error { - match value { - _ if value.starts_with("0x") => usize::from_str_radix(&value[2..], 16).map(Index).map_err(|e| { - Error::custom(format!("Invalid index: {}", e)) - }), - _ => value.parse::().map(Index).map_err(|e| { - Error::custom(format!("Invalid index: {}", e)) - }), - } - } + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + match value { + _ if value.starts_with("0x") => usize::from_str_radix(&value[2..], 16) + .map(Index) + .map_err(|e| Error::custom(format!("Invalid index: {}", e))), + _ => value + .parse::() + .map(Index) + .map_err(|e| Error::custom(format!("Invalid index: {}", e))), + } + } - fn visit_string(self, value: String) -> Result where E: Error { - self.visit_str(value.as_ref()) - } + fn visit_string(self, value: String) -> Result + where + E: Error, + { + self.visit_str(value.as_ref()) + } } #[cfg(test)] mod tests { - use super::*; - use serde_json; + use super::*; + use serde_json; - #[test] - fn block_number_deserialization() { - let s = r#"["0xa", "10"]"#; - let deserialized: Vec = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, vec![Index(10), Index(10)]); - } + #[test] + fn block_number_deserialization() { + let s = r#"["0xa", "10"]"#; + let deserialized: Vec = serde_json::from_str(s).unwrap(); + assert_eq!(deserialized, vec![Index(10), Index(10)]); + } } diff --git a/rpc/src/v1/types/log.rs b/rpc/src/v1/types/log.rs index 57b2cdd5dcd..72278ca5d29 100644 --- a/rpc/src/v1/types/log.rs +++ b/rpc/src/v1/types/log.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use ethereum_types::{H160, H256, U256}; use types::log_entry::{LocalizedLogEntry, LogEntry}; @@ -22,97 +22,102 @@ use v1::types::Bytes; #[derive(Debug, Serialize, PartialEq, Eq, Hash, Clone)] #[serde(rename_all = "camelCase")] pub struct Log { - /// H160 - pub address: H160, - /// Topics - pub topics: Vec, - /// Data - pub data: Bytes, - /// Block Hash - pub block_hash: Option, - /// Block Number - pub block_number: Option, - /// Transaction Hash - pub transaction_hash: Option, - /// Transaction Index - pub transaction_index: Option, - /// Log Index in Block - pub log_index: Option, - /// Log Index in Transaction - pub transaction_log_index: Option, - /// Log Type - #[serde(rename = "type")] - pub log_type: String, - /// Whether Log Type is Removed (Geth Compatibility Field) - #[serde(default)] - pub removed: bool, + /// H160 + pub address: H160, + /// Topics + pub topics: Vec, + /// Data + pub data: Bytes, + /// Block Hash + pub block_hash: Option, + /// Block Number + pub block_number: Option, + /// Transaction Hash + pub transaction_hash: Option, + /// Transaction Index + pub transaction_index: Option, + /// Log Index in Block + pub log_index: Option, + /// Log Index in Transaction + pub transaction_log_index: Option, + /// Log Type + #[serde(rename = "type")] + pub log_type: String, + /// Whether Log Type is Removed (Geth Compatibility Field) + #[serde(default)] + pub removed: bool, } impl From for Log { - fn from(e: LocalizedLogEntry) -> Log { - Log { - address: e.entry.address, - topics: e.entry.topics.into_iter().map(Into::into).collect(), - data: e.entry.data.into(), - block_hash: Some(e.block_hash), - block_number: Some(e.block_number.into()), - transaction_hash: Some(e.transaction_hash), - transaction_index: Some(e.transaction_index.into()), - log_index: Some(e.log_index.into()), - transaction_log_index: Some(e.transaction_log_index.into()), - log_type: "mined".to_owned(), - removed: false, - } - } + fn from(e: LocalizedLogEntry) -> Log { + Log { + address: e.entry.address, + topics: e.entry.topics.into_iter().map(Into::into).collect(), + data: e.entry.data.into(), + block_hash: Some(e.block_hash), + block_number: Some(e.block_number.into()), + transaction_hash: Some(e.transaction_hash), + transaction_index: Some(e.transaction_index.into()), + log_index: Some(e.log_index.into()), + transaction_log_index: Some(e.transaction_log_index.into()), + log_type: "mined".to_owned(), + removed: false, + } + } } impl From for Log { - fn from(e: LogEntry) -> Log { - Log { - address: e.address, - topics: e.topics.into_iter().map(Into::into).collect(), - data: e.data.into(), - block_hash: None, - block_number: None, - transaction_hash: None, - transaction_index: None, - log_index: None, - transaction_log_index: None, - log_type: "pending".to_owned(), - removed: false, - } - } + fn from(e: LogEntry) -> Log { + Log { + address: e.address, + topics: e.topics.into_iter().map(Into::into).collect(), + data: e.data.into(), + block_hash: None, + block_number: None, + transaction_hash: None, + transaction_index: None, + log_index: None, + transaction_log_index: None, + log_type: "pending".to_owned(), + removed: false, + } + } } #[cfg(test)] mod tests { - use serde_json; - use std::str::FromStr; - use v1::types::Log; - use ethereum_types::{H160, H256, U256}; + use ethereum_types::{H160, H256, U256}; + use serde_json; + use std::str::FromStr; + use v1::types::Log; - #[test] - fn log_serialization() { - let s = r#"{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","logIndex":"0x1","transactionLogIndex":"0x1","type":"mined","removed":false}"#; + #[test] + fn log_serialization() { + let s = r#"{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","logIndex":"0x1","transactionLogIndex":"0x1","type":"mined","removed":false}"#; - let log = Log { - address: H160::from_str("33990122638b9132ca29c723bdf037f1a891a70c").unwrap(), - topics: vec![ - H256::from_str("a6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc").unwrap(), - H256::from_str("4861736852656700000000000000000000000000000000000000000000000000").unwrap(), - ], - data: vec![].into(), - block_hash: Some(H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap()), - block_number: Some(U256::from(0x4510c)), - transaction_hash: Some(H256::default()), - transaction_index: Some(U256::default()), - transaction_log_index: Some(1.into()), - log_index: Some(U256::from(1)), - log_type: "mined".to_owned(), - removed: false, - }; + let log = Log { + address: H160::from_str("33990122638b9132ca29c723bdf037f1a891a70c").unwrap(), + topics: vec![ + H256::from_str("a6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc") + .unwrap(), + H256::from_str("4861736852656700000000000000000000000000000000000000000000000000") + .unwrap(), + ], + data: vec![].into(), + block_hash: Some( + H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5") + .unwrap(), + ), + block_number: Some(U256::from(0x4510c)), + transaction_hash: Some(H256::default()), + transaction_index: Some(U256::default()), + transaction_log_index: Some(1.into()), + log_index: Some(U256::from(1)), + log_type: "mined".to_owned(), + removed: false, + }; - let serialized = serde_json::to_string(&log).unwrap(); - assert_eq!(serialized, s); - } + let serialized = serde_json::to_string(&log).unwrap(); + assert_eq!(serialized, s); + } } diff --git a/rpc/src/v1/types/mod.rs b/rpc/src/v1/types/mod.rs index a41f49fab18..2da3457d6ec 100644 --- a/rpc/src/v1/types/mod.rs +++ b/rpc/src/v1/types/mod.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! RPC types @@ -25,8 +25,8 @@ mod block_number; mod bytes; mod call_request; mod confirmations; -mod consensus_status; mod derivation; +mod eip191; mod filter; mod histogram; mod index; @@ -40,46 +40,45 @@ mod sync; mod trace; mod trace_filter; mod transaction; -mod transaction_request; mod transaction_condition; +mod transaction_request; mod work; -mod private_receipt; -mod eip191; pub mod pubsub; -pub use self::eip191::{EIP191Version, PresignedTransaction}; -pub use self::account_info::{AccountInfo, ExtAccountInfo, HwAccountInfo, EthAccount, StorageProof, RecoveredAccount}; -pub use self::bytes::Bytes; -pub use self::block::{RichBlock, Block, BlockTransactions, Header, RichHeader, Rich}; -pub use self::block_number::{BlockNumber, LightBlockNumber, block_number_to_id}; -pub use self::call_request::CallRequest; -pub use self::confirmations::{ - ConfirmationPayload, ConfirmationRequest, ConfirmationResponse, ConfirmationResponseWithToken, - TransactionModification, EIP191SignRequest, EthSignRequest, DecryptRequest, Either -}; -pub use self::consensus_status::*; -pub use self::derivation::{DeriveHash, DeriveHierarchical, Derive}; -pub use self::filter::{Filter, FilterChanges}; -pub use self::histogram::Histogram; -pub use self::index::Index; -pub use self::log::Log; -pub use self::node_kind::{NodeKind, Availability, Capability}; -pub use self::provenance::Origin; -pub use self::receipt::Receipt; -pub use self::rpc_settings::RpcSettings; -pub use self::secretstore::EncryptedDocumentKey; -pub use self::sync::{ - SyncStatus, SyncInfo, Peers, PeerInfo, PeerNetworkInfo, PeerProtocolsInfo, - TransactionStats, ChainStatus, EthProtocolInfo, PipProtocolInfo, +pub use self::{ + account_info::{AccountInfo, EthAccount, ExtAccountInfo, RecoveredAccount, StorageProof}, + block::{Block, BlockTransactions, Header, Rich, RichBlock, RichHeader}, + block_number::{block_number_to_id, BlockNumber}, + bytes::Bytes, + call_request::CallRequest, + confirmations::{ + ConfirmationPayload, ConfirmationRequest, ConfirmationResponse, + ConfirmationResponseWithToken, DecryptRequest, EIP191SignRequest, Either, EthSignRequest, + TransactionModification, + }, + derivation::{Derive, DeriveHash, DeriveHierarchical}, + eip191::{EIP191Version, PresignedTransaction}, + filter::{Filter, FilterChanges}, + histogram::Histogram, + index::Index, + log::Log, + node_kind::{Availability, Capability, NodeKind}, + provenance::Origin, + receipt::Receipt, + rpc_settings::RpcSettings, + secretstore::EncryptedDocumentKey, + sync::{ + ChainStatus, EthProtocolInfo, PeerInfo, PeerNetworkInfo, PeerProtocolsInfo, Peers, + SyncInfo, SyncStatus, TransactionStats, + }, + trace::{LocalizedTrace, TraceResults, TraceResultsWithTransactionHash}, + trace_filter::TraceFilter, + transaction::{LocalTransactionStatus, RichRawTransaction, Transaction}, + transaction_condition::TransactionCondition, + transaction_request::TransactionRequest, + work::Work, }; -pub use self::trace::{LocalizedTrace, TraceResults, TraceResultsWithTransactionHash}; -pub use self::trace_filter::TraceFilter; -pub use self::transaction::{Transaction, RichRawTransaction, LocalTransactionStatus}; -pub use self::transaction_request::TransactionRequest; -pub use self::transaction_condition::TransactionCondition; -pub use self::work::Work; -pub use self::private_receipt::{PrivateTransactionReceipt, PrivateTransactionReceiptAndTransaction}; // TODO [ToDr] Refactor to a proper type Vec of enums? /// Expected tracing type. diff --git a/rpc/src/v1/types/node_kind.rs b/rpc/src/v1/types/node_kind.rs index f02f21939d0..d937cdcd5ab 100644 --- a/rpc/src/v1/types/node_kind.rs +++ b/rpc/src/v1/types/node_kind.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Description of the node. @@ -20,71 +20,80 @@ /// applications about how to utilize the RPC. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct NodeKind { - /// The capability of the node. - pub capability: Capability, - /// Who the node is available to. - pub availability: Availability, + /// The capability of the node. + pub capability: Capability, + /// Who the node is available to. + pub availability: Availability, } /// Who the node is available to. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum Availability { - /// A personal node, not intended to be available to everyone. - Personal, - /// A public, open node. - Public, + /// A personal node, not intended to be available to everyone. + Personal, + /// A public, open node. + Public, } /// The capability of the node. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum Capability { - /// A full node stores the full state and fully enacts incoming blocks. - Full, - /// A light node does a minimal header sync and fetches data as needed - /// from the network. - Light, + /// A full node stores the full state and fully enacts incoming blocks. + Full, } #[cfg(test)] mod tests { - use super::{NodeKind, Availability, Capability}; - use serde_json; - - #[test] - fn availability() { - let personal = r#""personal""#; - let public = r#""public""#; - - assert_eq!(serde_json::to_string(&Availability::Personal).unwrap(), personal); - assert_eq!(serde_json::to_string(&Availability::Public).unwrap(), public); - - assert_eq!(serde_json::from_str::(personal).unwrap(), Availability::Personal); - assert_eq!(serde_json::from_str::(public).unwrap(), Availability::Public); - } - - #[test] - fn capability() { - let light = r#""light""#; - let full = r#""full""#; - - assert_eq!(serde_json::to_string(&Capability::Light).unwrap(), light); - assert_eq!(serde_json::to_string(&Capability::Full).unwrap(), full); - - assert_eq!(serde_json::from_str::(light).unwrap(), Capability::Light); - assert_eq!(serde_json::from_str::(full).unwrap(), Capability::Full); - } - - #[test] - fn node_kind() { - let kind = NodeKind { - capability: Capability::Full, - availability: Availability::Public, - }; - let s = r#"{"capability":"full","availability":"public"}"#; - - assert_eq!(serde_json::to_string(&kind).unwrap(), s); - assert_eq!(serde_json::from_str::(s).unwrap(), kind); - } + use super::{Availability, Capability, NodeKind}; + use serde_json; + + #[test] + fn availability() { + let personal = r#""personal""#; + let public = r#""public""#; + + assert_eq!( + serde_json::to_string(&Availability::Personal).unwrap(), + personal + ); + assert_eq!( + serde_json::to_string(&Availability::Public).unwrap(), + public + ); + + assert_eq!( + serde_json::from_str::(personal).unwrap(), + Availability::Personal + ); + assert_eq!( + serde_json::from_str::(public).unwrap(), + Availability::Public + ); + } + + #[test] + fn capability() { + let full = r#""full""#; + + assert_eq!(serde_json::to_string(&Capability::Full).unwrap(), full); + + assert_eq!( + serde_json::from_str::(full).unwrap(), + Capability::Full + ); + } + + #[test] + fn node_kind() { + let kind = NodeKind { + capability: Capability::Full, + availability: Availability::Public, + }; + let s = r#"{"capability":"full","availability":"public"}"#; + + assert_eq!(serde_json::to_string(&kind).unwrap(), s); + assert_eq!(serde_json::from_str::(s).unwrap(), kind); + } } diff --git a/rpc/src/v1/types/private_receipt.rs b/rpc/src/v1/types/private_receipt.rs deleted file mode 100644 index 68e9e716f99..00000000000 --- a/rpc/src/v1/types/private_receipt.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use ethcore_private_tx::{Receipt as EthPrivateReceipt}; -use ethereum_types::{H160, H256}; -use v1::types::TransactionRequest; - -/// Receipt -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct PrivateTransactionReceipt { - /// Transaction Hash - pub transaction_hash: H256, - /// Private contract address - pub contract_address: H160, - /// Status code - #[serde(rename = "status")] - pub status_code: u8, -} - -impl From for PrivateTransactionReceipt { - fn from(r: EthPrivateReceipt) -> Self { - PrivateTransactionReceipt { - transaction_hash: r.hash, - contract_address: r.contract_address, - status_code: r.status_code, - } - } -} - -/// Receipt and Transaction -#[derive(Debug, Serialize)] -pub struct PrivateTransactionReceiptAndTransaction { - /// Receipt - pub receipt: PrivateTransactionReceipt, - /// Transaction - pub transaction: TransactionRequest, -} diff --git a/rpc/src/v1/types/provenance.rs b/rpc/src/v1/types/provenance.rs index dcdd2408fe8..5311cc4006d 100644 --- a/rpc/src/v1/types/provenance.rs +++ b/rpc/src/v1/types/provenance.rs @@ -1,98 +1,103 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Request Provenance -use std::fmt; use ethereum_types::H256; +use std::fmt; /// RPC request origin #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(deny_unknown_fields)] #[serde(rename_all = "kebab-case")] pub enum Origin { - /// RPC server (includes request origin) - Rpc(String), - /// IPC server (includes session hash) - Ipc(H256), - /// WS server - Ws { - /// Session id - session: H256, - }, - /// Signer (authorized WS server) - Signer { - /// Session id - session: H256 - }, - /// From the C API - CApi, - /// Unknown - Unknown, + /// RPC server (includes request origin) + Rpc(String), + /// IPC server (includes session hash) + Ipc(H256), + /// WS server + Ws { + /// Session id + session: H256, + }, + /// Signer (authorized WS server) + Signer { + /// Session id + session: H256, + }, + /// From the C API + CApi, + /// Unknown + Unknown, } impl Default for Origin { - fn default() -> Self { - Origin::Unknown - } + fn default() -> Self { + Origin::Unknown + } } impl fmt::Display for Origin { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Origin::Rpc(ref origin) => write!(f, "{} via RPC", origin), - Origin::Ipc(ref session) => write!(f, "IPC (session: {})", session), - Origin::Ws { ref session } => write!(f, "WebSocket (session: {})", session), - Origin::Signer { ref session } => write!(f, "Secure Session (session: {})", session), - Origin::CApi => write!(f, "C API"), - Origin::Unknown => write!(f, "unknown origin"), - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Origin::Rpc(ref origin) => write!(f, "{} via RPC", origin), + Origin::Ipc(ref session) => write!(f, "IPC (session: {})", session), + Origin::Ws { ref session } => write!(f, "WebSocket (session: {})", session), + Origin::Signer { ref session } => write!(f, "Secure Session (session: {})", session), + Origin::CApi => write!(f, "C API"), + Origin::Unknown => write!(f, "unknown origin"), + } + } } #[cfg(test)] mod tests { - use serde_json; - use super::Origin; + use super::Origin; + use serde_json; - #[test] - fn should_serialize_origin() { - // given - let o1 = Origin::Rpc("test service".into()); - let o3 = Origin::Ipc(5.into()); - let o4 = Origin::Signer { - session: 10.into(), - }; - let o5 = Origin::Unknown; - let o6 = Origin::Ws { - session: 5.into(), - }; + #[test] + fn should_serialize_origin() { + // given + let o1 = Origin::Rpc("test service".into()); + let o3 = Origin::Ipc(5.into()); + let o4 = Origin::Signer { session: 10.into() }; + let o5 = Origin::Unknown; + let o6 = Origin::Ws { session: 5.into() }; - // when - let res1 = serde_json::to_string(&o1).unwrap(); - let res3 = serde_json::to_string(&o3).unwrap(); - let res4 = serde_json::to_string(&o4).unwrap(); - let res5 = serde_json::to_string(&o5).unwrap(); - let res6 = serde_json::to_string(&o6).unwrap(); + // when + let res1 = serde_json::to_string(&o1).unwrap(); + let res3 = serde_json::to_string(&o3).unwrap(); + let res4 = serde_json::to_string(&o4).unwrap(); + let res5 = serde_json::to_string(&o5).unwrap(); + let res6 = serde_json::to_string(&o6).unwrap(); - // then - assert_eq!(res1, r#"{"rpc":"test service"}"#); - assert_eq!(res3, r#"{"ipc":"0x0000000000000000000000000000000000000000000000000000000000000005"}"#); - assert_eq!(res4, r#"{"signer":{"session":"0x000000000000000000000000000000000000000000000000000000000000000a"}}"#); - assert_eq!(res5, r#""unknown""#); - assert_eq!(res6, r#"{"ws":{"session":"0x0000000000000000000000000000000000000000000000000000000000000005"}}"#); - } + // then + assert_eq!(res1, r#"{"rpc":"test service"}"#); + assert_eq!( + res3, + r#"{"ipc":"0x0000000000000000000000000000000000000000000000000000000000000005"}"# + ); + assert_eq!( + res4, + r#"{"signer":{"session":"0x000000000000000000000000000000000000000000000000000000000000000a"}}"# + ); + assert_eq!(res5, r#""unknown""#); + assert_eq!( + res6, + r#"{"ws":{"session":"0x0000000000000000000000000000000000000000000000000000000000000005"}}"# + ); + } } diff --git a/rpc/src/v1/types/pubsub.rs b/rpc/src/v1/types/pubsub.rs index 1586b115c38..219d44a3ee9 100644 --- a/rpc/src/v1/types/pubsub.rs +++ b/rpc/src/v1/types/pubsub.rs @@ -1,48 +1,48 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Pub-Sub types. use ethereum_types::H256; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use serde::de::Error; -use serde_json::{Value, from_value}; -use v1::types::{RichHeader, Filter, Log}; +use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; +use serde_json::{from_value, Value}; +use v1::types::{Filter, Log, RichHeader}; /// Subscription result. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Result { - /// New block header. - Header(Box), - /// Log - Log(Box), - /// Transaction hash - TransactionHash(H256), + /// New block header. + Header(Box), + /// Log + Log(Box), + /// Transaction hash + TransactionHash(H256), } impl Serialize for Result { - fn serialize(&self, serializer: S) -> ::std::result::Result - where S: Serializer - { - match *self { - Result::Header(ref header) => header.serialize(serializer), - Result::Log(ref log) => log.serialize(serializer), - Result::TransactionHash(ref hash) => hash.serialize(serializer), - } - } + fn serialize(&self, serializer: S) -> ::std::result::Result + where + S: Serializer, + { + match *self { + Result::Header(ref header) => header.serialize(serializer), + Result::Log(ref log) => log.serialize(serializer), + Result::TransactionHash(ref hash) => hash.serialize(serializer), + } + } } /// Subscription kind. @@ -50,123 +50,149 @@ impl Serialize for Result { #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub enum Kind { - /// New block headers subscription. - NewHeads, - /// Logs subscription. - Logs, - /// New Pending Transactions subscription. - NewPendingTransactions, - /// Node syncing status subscription. - Syncing, + /// New block headers subscription. + NewHeads, + /// Logs subscription. + Logs, + /// New Pending Transactions subscription. + NewPendingTransactions, + /// Node syncing status subscription. + Syncing, } /// Subscription kind. #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub enum Params { - /// No parameters passed. - None, - /// Log parameters. - Logs(Filter), + /// No parameters passed. + None, + /// Log parameters. + Logs(Filter), } impl Default for Params { - fn default() -> Self { - Params::None - } + fn default() -> Self { + Params::None + } } impl<'a> Deserialize<'a> for Params { - fn deserialize(deserializer: D) -> ::std::result::Result - where D: Deserializer<'a> { - let v: Value = Deserialize::deserialize(deserializer)?; - - if v.is_null() { - return Ok(Params::None); - } - - from_value(v.clone()).map(Params::Logs) - .map_err(|e| D::Error::custom(format!("Invalid Pub-Sub parameters: {}", e))) - } + fn deserialize(deserializer: D) -> ::std::result::Result + where + D: Deserializer<'a>, + { + let v: Value = Deserialize::deserialize(deserializer)?; + + if v.is_null() { + return Ok(Params::None); + } + + from_value(v.clone()) + .map(Params::Logs) + .map_err(|e| D::Error::custom(format!("Invalid Pub-Sub parameters: {}", e))) + } } #[cfg(test)] mod tests { - use serde_json; - use super::{Result, Kind, Params}; - use v1::types::{RichHeader, Header, Filter}; - use v1::types::filter::VariadicValue; - - #[test] - fn should_deserialize_kind() { - assert_eq!(serde_json::from_str::(r#""newHeads""#).unwrap(), Kind::NewHeads); - assert_eq!(serde_json::from_str::(r#""logs""#).unwrap(), Kind::Logs); - assert_eq!(serde_json::from_str::(r#""newPendingTransactions""#).unwrap(), Kind::NewPendingTransactions); - assert_eq!(serde_json::from_str::(r#""syncing""#).unwrap(), Kind::Syncing); - } - - #[test] - fn should_deserialize_logs() { - let none = serde_json::from_str::(r#"null"#).unwrap(); - assert_eq!(none, Params::None); - - let logs1 = serde_json::from_str::(r#"{}"#).unwrap(); - let logs2 = serde_json::from_str::(r#"{"limit":10}"#).unwrap(); - let logs3 = serde_json::from_str::( - r#"{"topics":["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b"]}"# - ).unwrap(); - assert_eq!(logs1, Params::Logs(Filter { - from_block: None, - to_block: None, - block_hash: None, - address: None, - topics: None, - limit: None, - })); - assert_eq!(logs2, Params::Logs(Filter { - from_block: None, - to_block: None, - block_hash: None, - address: None, - topics: None, - limit: Some(10), - })); - assert_eq!(logs3, Params::Logs(Filter { - from_block: None, - to_block: None, - block_hash: None, - address: None, - topics: Some(vec![ - VariadicValue::Single("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b".parse().unwrap() - )]), - limit: None, - })); - } - - #[test] - fn should_serialize_header() { - let header = Result::Header(Box::new(RichHeader { - extra_info: Default::default(), - inner: Header { - hash: Some(Default::default()), - parent_hash: Default::default(), - uncles_hash: Default::default(), - author: Default::default(), - miner: Default::default(), - state_root: Default::default(), - transactions_root: Default::default(), - receipts_root: Default::default(), - number: Some(Default::default()), - gas_used: Default::default(), - gas_limit: Default::default(), - extra_data: Default::default(), - logs_bloom: Default::default(), - timestamp: Default::default(), - difficulty: Default::default(), - seal_fields: vec![Default::default(), Default::default()], - size: Some(69.into()), - }, - })); - let expected = r#"{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x0","extraData":"0x","gasLimit":"0x0","gasUsed":"0x0","hash":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","sealFields":["0x","0x"],"sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","size":"0x45","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000"}"#; - assert_eq!(serde_json::to_string(&header).unwrap(), expected); - } + use super::{Kind, Params, Result}; + use serde_json; + use v1::types::{filter::VariadicValue, Filter, Header, RichHeader}; + + #[test] + fn should_deserialize_kind() { + assert_eq!( + serde_json::from_str::(r#""newHeads""#).unwrap(), + Kind::NewHeads + ); + assert_eq!( + serde_json::from_str::(r#""logs""#).unwrap(), + Kind::Logs + ); + assert_eq!( + serde_json::from_str::(r#""newPendingTransactions""#).unwrap(), + Kind::NewPendingTransactions + ); + assert_eq!( + serde_json::from_str::(r#""syncing""#).unwrap(), + Kind::Syncing + ); + } + + #[test] + fn should_deserialize_logs() { + let none = serde_json::from_str::(r#"null"#).unwrap(); + assert_eq!(none, Params::None); + + let logs1 = serde_json::from_str::(r#"{}"#).unwrap(); + let logs2 = serde_json::from_str::(r#"{"limit":10}"#).unwrap(); + let logs3 = serde_json::from_str::( + r#"{"topics":["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b"]}"#, + ) + .unwrap(); + assert_eq!( + logs1, + Params::Logs(Filter { + from_block: None, + to_block: None, + block_hash: None, + address: None, + topics: None, + limit: None, + }) + ); + assert_eq!( + logs2, + Params::Logs(Filter { + from_block: None, + to_block: None, + block_hash: None, + address: None, + topics: None, + limit: Some(10), + }) + ); + assert_eq!( + logs3, + Params::Logs(Filter { + from_block: None, + to_block: None, + block_hash: None, + address: None, + topics: Some(vec![VariadicValue::Single( + "000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b" + .parse() + .unwrap() + )]), + limit: None, + }) + ); + } + + #[test] + fn should_serialize_header() { + let header = Result::Header(Box::new(RichHeader { + extra_info: Default::default(), + inner: Header { + hash: Some(Default::default()), + parent_hash: Default::default(), + uncles_hash: Default::default(), + author: Default::default(), + miner: Default::default(), + state_root: Default::default(), + transactions_root: Default::default(), + receipts_root: Default::default(), + number: Some(Default::default()), + gas_used: Default::default(), + gas_limit: Default::default(), + extra_data: Default::default(), + logs_bloom: Default::default(), + timestamp: Default::default(), + difficulty: Default::default(), + seal_fields: vec![Default::default(), Default::default()], + size: Some(69.into()), + }, + })); + let expected = r#"{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x0","extraData":"0x","gasLimit":"0x0","gasUsed":"0x0","hash":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","sealFields":["0x","0x"],"sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","size":"0x45","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000"}"#; + assert_eq!(serde_json::to_string(&header).unwrap(), expected); + } } diff --git a/rpc/src/v1/types/receipt.rs b/rpc/src/v1/types/receipt.rs index f492ca98c52..a524d19156e 100644 --- a/rpc/src/v1/types/receipt.rs +++ b/rpc/src/v1/types/receipt.rs @@ -1,174 +1,188 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . -use ethereum_types::{H160, H256, U64, U256, Bloom as H2048}; +use ethereum_types::{Bloom as H2048, H160, H256, U256, U64}; +use types::receipt::{LocalizedReceipt, Receipt as EthReceipt, RichReceipt, TransactionOutcome}; use v1::types::Log; -use types::receipt::{Receipt as EthReceipt, RichReceipt, LocalizedReceipt, TransactionOutcome}; /// Receipt #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct Receipt { - /// Transaction Hash - pub transaction_hash: Option, - /// Transaction index - pub transaction_index: Option, - /// Block hash - pub block_hash: Option, - /// Sender - pub from: Option, - /// Recipient - pub to: Option, - /// Block number - pub block_number: Option, - /// Cumulative gas used - pub cumulative_gas_used: U256, - /// Gas used - pub gas_used: Option, - /// Contract address - pub contract_address: Option, - /// Logs - pub logs: Vec, - /// State Root - #[serde(rename = "root")] - pub state_root: Option, - /// Logs bloom - pub logs_bloom: H2048, - /// Status code - #[serde(rename = "status")] - pub status_code: Option, + /// Transaction Hash + pub transaction_hash: Option, + /// Transaction index + pub transaction_index: Option, + /// Block hash + pub block_hash: Option, + /// Sender + pub from: Option, + /// Recipient + pub to: Option, + /// Block number + pub block_number: Option, + /// Cumulative gas used + pub cumulative_gas_used: U256, + /// Gas used + pub gas_used: Option, + /// Contract address + pub contract_address: Option, + /// Logs + pub logs: Vec, + /// State Root + // NOTE(niklasad1): EIP98 makes this optional field, if it's missing then skip serializing it + #[serde(skip_serializing_if = "Option::is_none", rename = "root")] + pub state_root: Option, + /// Logs bloom + pub logs_bloom: H2048, + /// Status code + // NOTE(niklasad1): Unknown after EIP98 rules, if it's missing then skip serializing it + #[serde(skip_serializing_if = "Option::is_none", rename = "status")] + pub status_code: Option, } impl Receipt { - fn outcome_to_state_root(outcome: TransactionOutcome) -> Option { - match outcome { - TransactionOutcome::Unknown | TransactionOutcome::StatusCode(_) => None, - TransactionOutcome::StateRoot(root) => Some(root), - } - } + fn outcome_to_state_root(outcome: TransactionOutcome) -> Option { + match outcome { + TransactionOutcome::Unknown | TransactionOutcome::StatusCode(_) => None, + TransactionOutcome::StateRoot(root) => Some(root), + } + } - fn outcome_to_status_code(outcome: &TransactionOutcome) -> Option { - match *outcome { - TransactionOutcome::Unknown | TransactionOutcome::StateRoot(_) => None, - TransactionOutcome::StatusCode(ref code) => Some((*code as u64).into()), - } - } + fn outcome_to_status_code(outcome: &TransactionOutcome) -> Option { + match *outcome { + TransactionOutcome::Unknown | TransactionOutcome::StateRoot(_) => None, + TransactionOutcome::StatusCode(ref code) => Some((*code as u64).into()), + } + } } impl From for Receipt { - fn from(r: LocalizedReceipt) -> Self { - Receipt { - to: r.to.map(Into::into), - from: Some(r.from), - transaction_hash: Some(r.transaction_hash), - transaction_index: Some(r.transaction_index.into()), - block_hash: Some(r.block_hash), - block_number: Some(r.block_number.into()), - cumulative_gas_used: r.cumulative_gas_used, - gas_used: Some(r.gas_used), - contract_address: r.contract_address.map(Into::into), - logs: r.logs.into_iter().map(Into::into).collect(), - status_code: Self::outcome_to_status_code(&r.outcome), - state_root: Self::outcome_to_state_root(r.outcome), - logs_bloom: r.log_bloom, - } - } + fn from(r: LocalizedReceipt) -> Self { + Receipt { + to: r.to.map(Into::into), + from: Some(r.from), + transaction_hash: Some(r.transaction_hash), + transaction_index: Some(r.transaction_index.into()), + block_hash: Some(r.block_hash), + block_number: Some(r.block_number.into()), + cumulative_gas_used: r.cumulative_gas_used, + gas_used: Some(r.gas_used), + contract_address: r.contract_address.map(Into::into), + logs: r.logs.into_iter().map(Into::into).collect(), + status_code: Self::outcome_to_status_code(&r.outcome), + state_root: Self::outcome_to_state_root(r.outcome), + logs_bloom: r.log_bloom, + } + } } impl From for Receipt { - fn from(r: RichReceipt) -> Self { - Receipt { - from: None, - to: None, - transaction_hash: Some(r.transaction_hash), - transaction_index: Some(r.transaction_index.into()), - block_hash: None, - block_number: None, - cumulative_gas_used: r.cumulative_gas_used, - gas_used: Some(r.gas_used), - contract_address: r.contract_address.map(Into::into), - logs: r.logs.into_iter().map(Into::into).collect(), - status_code: Self::outcome_to_status_code(&r.outcome), - state_root: Self::outcome_to_state_root(r.outcome), - logs_bloom: r.log_bloom, - } - } + fn from(r: RichReceipt) -> Self { + Receipt { + from: Some(r.from), + to: r.to.map(Into::into), + transaction_hash: Some(r.transaction_hash), + transaction_index: Some(r.transaction_index.into()), + block_hash: None, + block_number: None, + cumulative_gas_used: r.cumulative_gas_used, + gas_used: Some(r.gas_used), + contract_address: r.contract_address.map(Into::into), + logs: r.logs.into_iter().map(Into::into).collect(), + status_code: Self::outcome_to_status_code(&r.outcome), + state_root: Self::outcome_to_state_root(r.outcome), + logs_bloom: r.log_bloom, + } + } } impl From for Receipt { - fn from(r: EthReceipt) -> Self { - Receipt { - from: None, - to: None, - transaction_hash: None, - transaction_index: None, - block_hash: None, - block_number: None, - cumulative_gas_used: r.gas_used, - gas_used: None, - contract_address: None, - logs: r.logs.into_iter().map(Into::into).collect(), - status_code: Self::outcome_to_status_code(&r.outcome), - state_root: Self::outcome_to_state_root(r.outcome), - logs_bloom: r.log_bloom, - } - } + fn from(r: EthReceipt) -> Self { + Receipt { + from: None, + to: None, + transaction_hash: None, + transaction_index: None, + block_hash: None, + block_number: None, + cumulative_gas_used: r.gas_used, + gas_used: None, + contract_address: None, + logs: r.logs.into_iter().map(Into::into).collect(), + status_code: Self::outcome_to_status_code(&r.outcome), + state_root: Self::outcome_to_state_root(r.outcome), + logs_bloom: r.log_bloom, + } + } } #[cfg(test)] mod tests { - use serde_json; - use v1::types::{Log, Receipt}; + use serde_json; + use v1::types::{Log, Receipt}; - #[test] - fn receipt_serialization() { - let s = r#"{"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","from":null,"to":null,"blockNumber":"0x4510c","cumulativeGasUsed":"0x20","gasUsed":"0x10","contractAddress":null,"logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","logIndex":"0x1","transactionLogIndex":null,"type":"mined","removed":false}],"root":"0x000000000000000000000000000000000000000000000000000000000000000a","logsBloom":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f","status":"0x1"}"#; + #[test] + fn receipt_serialization() { + let s = r#"{"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","from":null,"to":null,"blockNumber":"0x4510c","cumulativeGasUsed":"0x20","gasUsed":"0x10","contractAddress":null,"logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","logIndex":"0x1","transactionLogIndex":null,"type":"mined","removed":false}],"root":"0x000000000000000000000000000000000000000000000000000000000000000a","logsBloom":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f","status":"0x1"}"#; - let receipt = Receipt { - from: None, - to: None, - transaction_hash: Some(0.into()), - transaction_index: Some(0.into()), - block_hash: Some("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5".parse().unwrap()), - block_number: Some(0x4510c.into()), - cumulative_gas_used: 0x20.into(), - gas_used: Some(0x10.into()), - contract_address: None, - logs: vec![Log { - address: "33990122638b9132ca29c723bdf037f1a891a70c".parse().unwrap(), - topics: vec![ - "a6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc".parse().unwrap(), - "4861736852656700000000000000000000000000000000000000000000000000".parse().unwrap(), - ], - data: vec![].into(), - block_hash: Some("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5".parse().unwrap()), - block_number: Some(0x4510c.into()), - transaction_hash: Some(0.into()), - transaction_index: Some(0.into()), - transaction_log_index: None, - log_index: Some(1.into()), - log_type: "mined".into(), - removed: false, - }], - logs_bloom: 15.into(), - state_root: Some(10.into()), - status_code: Some(1u64.into()), - }; + let receipt = Receipt { + from: None, + to: None, + transaction_hash: Some(0.into()), + transaction_index: Some(0.into()), + block_hash: Some( + "ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5" + .parse() + .unwrap(), + ), + block_number: Some(0x4510c.into()), + cumulative_gas_used: 0x20.into(), + gas_used: Some(0x10.into()), + contract_address: None, + logs: vec![Log { + address: "33990122638b9132ca29c723bdf037f1a891a70c".parse().unwrap(), + topics: vec![ + "a6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc" + .parse() + .unwrap(), + "4861736852656700000000000000000000000000000000000000000000000000" + .parse() + .unwrap(), + ], + data: vec![].into(), + block_hash: Some( + "ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5" + .parse() + .unwrap(), + ), + block_number: Some(0x4510c.into()), + transaction_hash: Some(0.into()), + transaction_index: Some(0.into()), + transaction_log_index: None, + log_index: Some(1.into()), + log_type: "mined".into(), + removed: false, + }], + logs_bloom: 15.into(), + state_root: Some(10.into()), + status_code: Some(1u64.into()), + }; - let serialized = serde_json::to_string(&receipt).unwrap(); - assert_eq!(serialized, s); - } + let serialized = serde_json::to_string(&receipt).unwrap(); + assert_eq!(serialized, s); + } } diff --git a/rpc/src/v1/types/rpc_settings.rs b/rpc/src/v1/types/rpc_settings.rs index 63dfba7a8e4..49472201b06 100644 --- a/rpc/src/v1/types/rpc_settings.rs +++ b/rpc/src/v1/types/rpc_settings.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! RPC Settings data. @@ -20,10 +20,10 @@ #[derive(Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct RpcSettings { - /// Whether RPC is enabled. - pub enabled: bool, - /// The interface being listened on. - pub interface: String, - /// The port being listened on. - pub port: u64, + /// Whether RPC is enabled. + pub enabled: bool, + /// The interface being listened on. + pub interface: String, + /// The port being listened on. + pub port: u64, } diff --git a/rpc/src/v1/types/secretstore.rs b/rpc/src/v1/types/secretstore.rs index ef76ec5b464..b73d4c1d3ad 100644 --- a/rpc/src/v1/types/secretstore.rs +++ b/rpc/src/v1/types/secretstore.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use ethereum_types::H512; use v1::types::Bytes; @@ -21,33 +21,36 @@ use v1::types::Bytes; #[derive(Default, Debug, Serialize, PartialEq)] #[cfg_attr(test, derive(Deserialize))] pub struct EncryptedDocumentKey { - /// Common encryption point. Pass this to Secret Store 'Document key storing session' - pub common_point: H512, - /// Encrypted point. Pass this to Secret Store 'Document key storing session'. - pub encrypted_point: H512, - /// Document key itself, encrypted with passed account public. Pass this to 'secretstore_encrypt'. - pub encrypted_key: Bytes, + /// Common encryption point. Pass this to Secret Store 'Document key storing session' + pub common_point: H512, + /// Encrypted point. Pass this to Secret Store 'Document key storing session'. + pub encrypted_point: H512, + /// Document key itself, encrypted with passed account public. Pass this to 'secretstore_encrypt'. + pub encrypted_key: Bytes, } #[cfg(test)] mod tests { - use serde_json; - use super::EncryptedDocumentKey; - - #[test] - fn test_serialize_encrypted_document_key() { - let initial = EncryptedDocumentKey { - common_point: 1.into(), - encrypted_point: 2.into(), - encrypted_key: vec![3].into(), - }; - - let serialized = serde_json::to_string(&initial).unwrap(); - assert_eq!(serialized, r#"{"common_point":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","encrypted_point":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002","encrypted_key":"0x03"}"#); - - let deserialized: EncryptedDocumentKey = serde_json::from_str(&serialized).unwrap(); - assert_eq!(deserialized.common_point, 1.into()); - assert_eq!(deserialized.encrypted_point, 2.into()); - assert_eq!(deserialized.encrypted_key, vec![3].into()); - } + use super::EncryptedDocumentKey; + use serde_json; + + #[test] + fn test_serialize_encrypted_document_key() { + let initial = EncryptedDocumentKey { + common_point: 1.into(), + encrypted_point: 2.into(), + encrypted_key: vec![3].into(), + }; + + let serialized = serde_json::to_string(&initial).unwrap(); + assert_eq!( + serialized, + r#"{"common_point":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","encrypted_point":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002","encrypted_key":"0x03"}"# + ); + + let deserialized: EncryptedDocumentKey = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized.common_point, 1.into()); + assert_eq!(deserialized.encrypted_point, 2.into()); + assert_eq!(deserialized.encrypted_key, vec![3].into()); + } } diff --git a/rpc/src/v1/types/sync.rs b/rpc/src/v1/types/sync.rs index 901611fea2b..0563b557a97 100644 --- a/rpc/src/v1/types/sync.rs +++ b/rpc/src/v1/types/sync.rs @@ -1,251 +1,242 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use network::client_version::ClientVersion; use std::collections::BTreeMap; -use ethereum_types::{U256, H512}; -use sync::{self, PeerInfo as SyncPeerInfo, TransactionStats as SyncTransactionStats}; +use ethereum_types::{H512, U256}; use serde::{Serialize, Serializer}; +use sync::{self, PeerInfo as SyncPeerInfo, TransactionStats as SyncTransactionStats}; /// Sync info #[derive(Default, Debug, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct SyncInfo { - /// Starting block - pub starting_block: U256, - /// Current block - pub current_block: U256, - /// Highest block seen so far - pub highest_block: U256, - /// Warp sync snapshot chunks total. - pub warp_chunks_amount: Option, - /// Warp sync snpashot chunks processed. - pub warp_chunks_processed: Option, + /// Starting block + pub starting_block: U256, + /// Current block + pub current_block: U256, + /// Highest block seen so far + pub highest_block: U256, + /// Warp sync snapshot chunks total. + pub warp_chunks_amount: Option, + /// Warp sync snpashot chunks processed. + pub warp_chunks_processed: Option, } /// Peers info #[derive(Default, Debug, Serialize)] pub struct Peers { - /// Number of active peers - pub active: usize, - /// Number of connected peers - pub connected: usize, - /// Max number of peers - pub max: u32, - /// Detailed information on peers - pub peers: Vec, + /// Number of active peers + pub active: usize, + /// Number of connected peers + pub connected: usize, + /// Max number of peers + pub max: u32, + /// Detailed information on peers + pub peers: Vec, } /// Peer connection information #[derive(Default, Debug, Serialize)] pub struct PeerInfo { - /// Public node id - pub id: Option, - /// Node client ID - pub name: ClientVersion, - /// Capabilities - pub caps: Vec, - /// Network information - pub network: PeerNetworkInfo, - /// Protocols information - pub protocols: PeerProtocolsInfo, + /// Public node id + pub id: Option, + /// Node client ID + pub name: ClientVersion, + /// Capabilities + pub caps: Vec, + /// Network information + pub network: PeerNetworkInfo, + /// Protocols information + pub protocols: PeerProtocolsInfo, } /// Peer network information #[derive(Default, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct PeerNetworkInfo { - /// Remote endpoint address - pub remote_address: String, - /// Local endpoint address - pub local_address: String, + /// Remote endpoint address + pub remote_address: String, + /// Local endpoint address + pub local_address: String, } /// Peer protocols information #[derive(Default, Debug, Serialize)] pub struct PeerProtocolsInfo { - /// Ethereum protocol information - pub eth: Option, - /// PIP protocol information. - pub pip: Option, + /// Ethereum protocol information + pub eth: Option, } /// Peer Ethereum protocol information #[derive(Default, Debug, Serialize)] pub struct EthProtocolInfo { - /// Negotiated ethereum protocol version - pub version: u32, - /// Peer total difficulty if known - pub difficulty: Option, - /// SHA3 of peer best block hash - pub head: String, + /// Negotiated ethereum protocol version + pub version: u32, + /// Peer total difficulty if known + pub difficulty: Option, + /// SHA3 of peer best block hash + pub head: String, } impl From for EthProtocolInfo { - fn from(info: sync::EthProtocolInfo) -> Self { - EthProtocolInfo { - version: info.version, - difficulty: info.difficulty.map(Into::into), - head: format!("{:x}", info.head), - } - } -} - -/// Peer PIP protocol information -#[derive(Default, Debug, Serialize)] -pub struct PipProtocolInfo { - /// Negotiated PIP protocol version - pub version: u32, - /// Peer total difficulty - pub difficulty: U256, - /// SHA3 of peer best block hash - pub head: String, -} - -impl From for PipProtocolInfo { - fn from(info: sync::PipProtocolInfo) -> Self { - PipProtocolInfo { - version: info.version, - difficulty: info.difficulty, - head: format!("{:x}", info.head), - } - } + fn from(info: sync::EthProtocolInfo) -> Self { + EthProtocolInfo { + version: info.version, + difficulty: info.difficulty.map(Into::into), + head: format!("{:x}", info.head), + } + } } /// Sync status #[derive(Debug, PartialEq)] pub enum SyncStatus { - /// Info when syncing - Info(SyncInfo), - /// Not syncing - None + /// Info when syncing + Info(SyncInfo), + /// Not syncing + None, } impl Serialize for SyncStatus { - fn serialize(&self, serializer: S) -> Result - where S: Serializer { - match *self { - SyncStatus::Info(ref info) => info.serialize(serializer), - SyncStatus::None => false.serialize(serializer) - } - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + SyncStatus::Info(ref info) => info.serialize(serializer), + SyncStatus::None => false.serialize(serializer), + } + } } /// Propagation statistics for pending transaction. #[derive(Default, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct TransactionStats { - /// Block no this transaction was first seen. - pub first_seen: u64, - /// Peers this transaction was propagated to with count. - pub propagated_to: BTreeMap, + /// Block no this transaction was first seen. + pub first_seen: u64, + /// Peers this transaction was propagated to with count. + pub propagated_to: BTreeMap, } impl From for PeerInfo { - fn from(p: SyncPeerInfo) -> Self { - PeerInfo { - id: p.id, - name: p.client_version, - caps: p.capabilities, - network: PeerNetworkInfo { - remote_address: p.remote_address, - local_address: p.local_address, - }, - protocols: PeerProtocolsInfo { - eth: p.eth_info.map(Into::into), - pip: p.pip_info.map(Into::into), - }, - } - } + fn from(p: SyncPeerInfo) -> Self { + PeerInfo { + id: p.id, + name: p.client_version, + caps: p.capabilities, + network: PeerNetworkInfo { + remote_address: p.remote_address, + local_address: p.local_address, + }, + protocols: PeerProtocolsInfo { + eth: p.eth_info.map(Into::into), + }, + } + } } impl From for TransactionStats { - fn from(s: SyncTransactionStats) -> Self { - TransactionStats { - first_seen: s.first_seen, - propagated_to: s.propagated_to - .into_iter() - .map(|(id, count)| (id, count)) - .collect(), - } - } + fn from(s: SyncTransactionStats) -> Self { + TransactionStats { + first_seen: s.first_seen, + propagated_to: s + .propagated_to + .into_iter() + .map(|(id, count)| (id, count)) + .collect(), + } + } } /// Chain status. #[derive(Default, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct ChainStatus { - /// Describes the gap in the blockchain, if there is one: (first, last) - pub block_gap: Option<(U256, U256)>, + /// Describes the gap in the blockchain, if there is one: (first, last) + pub block_gap: Option<(U256, U256)>, } #[cfg(test)] mod tests { - use serde_json; - use std::collections::BTreeMap; - use super::{SyncInfo, SyncStatus, Peers, TransactionStats, ChainStatus}; - - #[test] - fn test_serialize_sync_info() { - let t = SyncInfo::default(); - let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"startingBlock":"0x0","currentBlock":"0x0","highestBlock":"0x0","warpChunksAmount":null,"warpChunksProcessed":null}"#); - } - - #[test] - fn test_serialize_peers() { - let t = Peers::default(); - let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"active":0,"connected":0,"max":0,"peers":[]}"#); - } - - #[test] - fn test_serialize_sync_status() { - let t = SyncStatus::None; - let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, "false"); - - let t = SyncStatus::Info(SyncInfo::default()); - let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"startingBlock":"0x0","currentBlock":"0x0","highestBlock":"0x0","warpChunksAmount":null,"warpChunksProcessed":null}"#); - } - - #[test] - fn test_serialize_block_gap() { - let mut t = ChainStatus::default(); - let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"blockGap":null}"#); - - t.block_gap = Some((1.into(), 5.into())); - - let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"blockGap":["0x1","0x5"]}"#); - } - - #[test] - fn test_serialize_transaction_stats() { - let stats = TransactionStats { - first_seen: 100, - propagated_to: map![ - 10.into() => 50 - ], - }; - - let serialized = serde_json::to_string(&stats).unwrap(); - assert_eq!(serialized, r#"{"firstSeen":100,"propagatedTo":{"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a":50}}"#) - } + use super::{ChainStatus, Peers, SyncInfo, SyncStatus, TransactionStats}; + use serde_json; + use std::collections::BTreeMap; + + #[test] + fn test_serialize_sync_info() { + let t = SyncInfo::default(); + let serialized = serde_json::to_string(&t).unwrap(); + assert_eq!( + serialized, + r#"{"startingBlock":"0x0","currentBlock":"0x0","highestBlock":"0x0","warpChunksAmount":null,"warpChunksProcessed":null}"# + ); + } + + #[test] + fn test_serialize_peers() { + let t = Peers::default(); + let serialized = serde_json::to_string(&t).unwrap(); + assert_eq!( + serialized, + r#"{"active":0,"connected":0,"max":0,"peers":[]}"# + ); + } + + #[test] + fn test_serialize_sync_status() { + let t = SyncStatus::None; + let serialized = serde_json::to_string(&t).unwrap(); + assert_eq!(serialized, "false"); + + let t = SyncStatus::Info(SyncInfo::default()); + let serialized = serde_json::to_string(&t).unwrap(); + assert_eq!( + serialized, + r#"{"startingBlock":"0x0","currentBlock":"0x0","highestBlock":"0x0","warpChunksAmount":null,"warpChunksProcessed":null}"# + ); + } + + #[test] + fn test_serialize_block_gap() { + let mut t = ChainStatus::default(); + let serialized = serde_json::to_string(&t).unwrap(); + assert_eq!(serialized, r#"{"blockGap":null}"#); + + t.block_gap = Some((1.into(), 5.into())); + + let serialized = serde_json::to_string(&t).unwrap(); + assert_eq!(serialized, r#"{"blockGap":["0x1","0x5"]}"#); + } + + #[test] + fn test_serialize_transaction_stats() { + let stats = TransactionStats { + first_seen: 100, + propagated_to: map![ + 10.into() => 50 + ], + }; + + let serialized = serde_json::to_string(&stats).unwrap(); + assert_eq!( + serialized, + r#"{"firstSeen":100,"propagatedTo":{"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a":50}}"# + ) + } } diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs index 58a5ee59622..e4595879b59 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -1,29 +1,29 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use std::collections::BTreeMap; -use ethcore::client::Executed; -use ethcore::trace as et; -use ethcore::trace::{FlatTrace, LocalizedTrace as EthLocalizedTrace, trace, TraceError}; +use ethcore::{ + client::Executed, + trace as et, + trace::{trace, FlatTrace, LocalizedTrace as EthLocalizedTrace, TraceError}, +}; use ethereum_types::{H160, H256, U256}; -use serde::ser::SerializeStruct; -use serde::{Serialize, Serializer}; -use types::account_diff; -use types::state_diff; +use serde::{ser::SerializeStruct, Serialize, Serializer}; +use types::{account_diff, state_diff}; use vm; use v1::types::Bytes; @@ -31,168 +31,188 @@ use v1::types::Bytes; #[derive(Debug, Serialize)] /// A diff of some chunk of memory. pub struct MemoryDiff { - /// Offset into memory the change begins. - pub off: usize, - /// The changed data. - pub data: Bytes, + /// Offset into memory the change begins. + pub off: usize, + /// The changed data. + pub data: Bytes, } impl From for MemoryDiff { - fn from(c: et::MemoryDiff) -> Self { - MemoryDiff { - off: c.offset, - data: c.data.into(), - } - } + fn from(c: et::MemoryDiff) -> Self { + MemoryDiff { + off: c.offset, + data: c.data.into(), + } + } } #[derive(Debug, Serialize)] /// A diff of some storage value. pub struct StorageDiff { - /// Which key in storage is changed. - pub key: U256, - /// What the value has been changed to. - pub val: U256, + /// Which key in storage is changed. + pub key: U256, + /// What the value has been changed to. + pub val: U256, } impl From for StorageDiff { - fn from(c: et::StorageDiff) -> Self { - StorageDiff { - key: c.location, - val: c.value, - } - } + fn from(c: et::StorageDiff) -> Self { + StorageDiff { + key: c.location, + val: c.value, + } + } } #[derive(Debug, Serialize)] /// A record of an executed VM operation. pub struct VMExecutedOperation { - /// The total gas used. - pub used: u64, - /// The stack item placed, if any. - pub push: Vec, - /// If altered, the memory delta. - pub mem: Option, - /// The altered storage value, if any. - pub store: Option, + /// The total gas used. + pub used: u64, + /// The stack item placed, if any. + pub push: Vec, + /// If altered, the memory delta. + pub mem: Option, + /// The altered storage value, if any. + pub store: Option, } impl From for VMExecutedOperation { - fn from(c: et::VMExecutedOperation) -> Self { - VMExecutedOperation { - used: c.gas_used.low_u64(), - push: c.stack_push.into_iter().map(Into::into).collect(), - mem: c.mem_diff.map(Into::into), - store: c.store_diff.map(Into::into), - } - } + fn from(c: et::VMExecutedOperation) -> Self { + VMExecutedOperation { + used: c.gas_used.low_u64(), + push: c.stack_push.into_iter().map(Into::into).collect(), + mem: c.mem_diff.map(Into::into), + store: c.store_diff.map(Into::into), + } + } } #[derive(Debug, Serialize)] /// A record of the execution of a single VM operation. pub struct VMOperation { - /// The program counter. - pub pc: usize, - /// The gas cost for this instruction. - pub cost: u64, - /// Information concerning the execution of the operation. - pub ex: Option, - /// Subordinate trace of the CALL/CREATE if applicable. - #[serde(bound="VMTrace: Serialize")] - pub sub: Option, + /// The program counter. + pub pc: usize, + /// The gas cost for this instruction. + pub cost: u64, + /// Information concerning the execution of the operation. + pub ex: Option, + /// Subordinate trace of the CALL/CREATE if applicable. + #[serde(bound = "VMTrace: Serialize")] + pub sub: Option, } impl From<(et::VMOperation, Option)> for VMOperation { - fn from(c: (et::VMOperation, Option)) -> Self { - VMOperation { - pc: c.0.pc, - cost: c.0.gas_cost.low_u64(), - ex: c.0.executed.map(Into::into), - sub: c.1.map(Into::into), - } - } + fn from(c: (et::VMOperation, Option)) -> Self { + VMOperation { + pc: c.0.pc, + cost: c.0.gas_cost.low_u64(), + ex: c.0.executed.map(Into::into), + sub: c.1.map(Into::into), + } + } } #[derive(Debug, Serialize)] /// A record of a full VM trace for a CALL/CREATE. pub struct VMTrace { - /// The code to be executed. - pub code: Bytes, - /// The operations executed. - pub ops: Vec, + /// The code to be executed. + pub code: Bytes, + /// The operations executed. + pub ops: Vec, } impl From for VMTrace { - fn from(c: et::VMTrace) -> Self { - let mut subs = c.subs.into_iter(); - let mut next_sub = subs.next(); - VMTrace { - code: c.code.into(), - ops: c.operations - .into_iter() - .enumerate() - .map(|(i, op)| (op, { - let have_sub = next_sub.is_some() && next_sub.as_ref().unwrap().parent_step == i; - if have_sub { - let r = next_sub.clone(); - next_sub = subs.next(); - r - } else { None } - }).into()) - .collect(), - } - } + fn from(c: et::VMTrace) -> Self { + let mut subs = c.subs.into_iter(); + let mut next_sub = subs.next(); + VMTrace { + code: c.code.into(), + ops: c + .operations + .into_iter() + .enumerate() + .map(|(i, op)| { + (op, { + let have_sub = + next_sub.is_some() && next_sub.as_ref().unwrap().parent_step == i; + if have_sub { + let r = next_sub.clone(); + next_sub = subs.next(); + r + } else { + None + } + }) + .into() + }) + .collect(), + } + } } #[derive(Debug, Serialize)] /// Aux type for Diff::Changed. -pub struct ChangedType where T: Serialize { - from: T, - to: T, +pub struct ChangedType +where + T: Serialize, +{ + from: T, + to: T, } #[derive(Debug, Serialize)] /// Serde-friendly `Diff` shadow. -pub enum Diff where T: Serialize { - #[serde(rename = "=")] - Same, - #[serde(rename = "+")] - Born(T), - #[serde(rename = "-")] - Died(T), - #[serde(rename = "*")] - Changed(ChangedType), -} - -impl From> for Diff where T: Eq, U: Serialize + From { - fn from(c: account_diff::Diff) -> Self { - match c { - account_diff::Diff::Same => Diff::Same, - account_diff::Diff::Born(t) => Diff::Born(t.into()), - account_diff::Diff::Died(t) => Diff::Died(t.into()), - account_diff::Diff::Changed(t, u) => Diff::Changed(ChangedType{from: t.into(), to: u.into()}), - } - } +pub enum Diff +where + T: Serialize, +{ + #[serde(rename = "=")] + Same, + #[serde(rename = "+")] + Born(T), + #[serde(rename = "-")] + Died(T), + #[serde(rename = "*")] + Changed(ChangedType), +} + +impl From> for Diff +where + T: Eq, + U: Serialize + From, +{ + fn from(c: account_diff::Diff) -> Self { + match c { + account_diff::Diff::Same => Diff::Same, + account_diff::Diff::Born(t) => Diff::Born(t.into()), + account_diff::Diff::Died(t) => Diff::Died(t.into()), + account_diff::Diff::Changed(t, u) => Diff::Changed(ChangedType { + from: t.into(), + to: u.into(), + }), + } + } } #[derive(Debug, Serialize)] /// Serde-friendly `AccountDiff` shadow. pub struct AccountDiff { - pub balance: Diff, - pub nonce: Diff, - pub code: Diff, - pub storage: BTreeMap>, + pub balance: Diff, + pub nonce: Diff, + pub code: Diff, + pub storage: BTreeMap>, } impl From for AccountDiff { - fn from(c: account_diff::AccountDiff) -> Self { - AccountDiff { - balance: c.balance.into(), - nonce: c.nonce.into(), - code: c.code.into(), - storage: c.storage.into_iter().map(|(k, v)| (k, v.into())).collect(), - } - } + fn from(c: account_diff::AccountDiff) -> Self { + AccountDiff { + balance: c.balance.into(), + nonce: c.nonce.into(), + code: c.code.into(), + storage: c.storage.into_iter().map(|(k, v)| (k, v.into())).collect(), + } + } } #[derive(Debug)] @@ -200,674 +220,709 @@ impl From for AccountDiff { pub struct StateDiff(BTreeMap); impl Serialize for StateDiff { - fn serialize(&self, serializer: S) -> Result - where S: Serializer { - Serialize::serialize(&self.0, serializer) - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + Serialize::serialize(&self.0, serializer) + } } impl From for StateDiff { - fn from(c: state_diff::StateDiff) -> Self { - StateDiff(c.raw.into_iter().map(|(k, v)| (k, v.into())).collect()) - } + fn from(c: state_diff::StateDiff) -> Self { + StateDiff(c.raw.into_iter().map(|(k, v)| (k, v.into())).collect()) + } } /// Create response #[derive(Debug, Serialize)] pub struct Create { - /// Sender - from: H160, - /// Value - value: U256, - /// Gas - gas: U256, - /// Initialization code - init: Bytes, + /// Sender + from: H160, + /// Value + value: U256, + /// Gas + gas: U256, + /// Initialization code + init: Bytes, } impl From for Create { - fn from(c: trace::Create) -> Self { - Create { - from: c.from, - value: c.value, - gas: c.gas, - init: Bytes::new(c.init), - } - } + fn from(c: trace::Create) -> Self { + Create { + from: c.from, + value: c.value, + gas: c.gas, + init: Bytes::new(c.init), + } + } } /// Call type. #[derive(Debug, Serialize)] #[serde(rename_all = "lowercase")] pub enum CallType { - /// None - None, - /// Call - Call, - /// Call code - CallCode, - /// Delegate call - DelegateCall, - /// Static call - StaticCall, + /// None + None, + /// Call + Call, + /// Call code + CallCode, + /// Delegate call + DelegateCall, + /// Static call + StaticCall, } impl From for CallType { - fn from(c: vm::CallType) -> Self { - match c { - vm::CallType::None => CallType::None, - vm::CallType::Call => CallType::Call, - vm::CallType::CallCode => CallType::CallCode, - vm::CallType::DelegateCall => CallType::DelegateCall, - vm::CallType::StaticCall => CallType::StaticCall, - } - } + fn from(c: vm::CallType) -> Self { + match c { + vm::CallType::None => CallType::None, + vm::CallType::Call => CallType::Call, + vm::CallType::CallCode => CallType::CallCode, + vm::CallType::DelegateCall => CallType::DelegateCall, + vm::CallType::StaticCall => CallType::StaticCall, + } + } } /// Call response #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct Call { - /// Sender - from: H160, - /// Recipient - to: H160, - /// Transfered Value - value: U256, - /// Gas - gas: U256, - /// Input data - input: Bytes, - /// The type of the call. - call_type: CallType, + /// Sender + from: H160, + /// Recipient + to: H160, + /// Transfered Value + value: U256, + /// Gas + gas: U256, + /// Input data + input: Bytes, + /// The type of the call. + call_type: CallType, } impl From for Call { - fn from(c: trace::Call) -> Self { - Call { - from: c.from, - to: c.to, - value: c.value, - gas: c.gas, - input: c.input.into(), - call_type: c.call_type.into(), - } - } + fn from(c: trace::Call) -> Self { + Call { + from: c.from, + to: c.to, + value: c.value, + gas: c.gas, + input: c.input.into(), + call_type: c.call_type.into(), + } + } } /// Reward type. #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub enum RewardType { - /// Block - Block, - /// Uncle - Uncle, - /// EmptyStep (AuthorityRound) - EmptyStep, - /// External (attributed as part of an external protocol) - External, + /// Block + Block, + /// Uncle + Uncle, + /// EmptyStep (AuthorityRound) + EmptyStep, + /// External (attributed as part of an external protocol) + External, } impl From for RewardType { - fn from(c: trace::RewardType) -> Self { - match c { - trace::RewardType::Block => RewardType::Block, - trace::RewardType::Uncle => RewardType::Uncle, - trace::RewardType::EmptyStep => RewardType::EmptyStep, - trace::RewardType::External => RewardType::External, - } - } + fn from(c: trace::RewardType) -> Self { + match c { + trace::RewardType::Block => RewardType::Block, + trace::RewardType::Uncle => RewardType::Uncle, + trace::RewardType::EmptyStep => RewardType::EmptyStep, + trace::RewardType::External => RewardType::External, + } + } } /// Reward action #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct Reward { - /// Author's address. - pub author: H160, - /// Reward amount. - pub value: U256, - /// Reward type. - pub reward_type: RewardType, + /// Author's address. + pub author: H160, + /// Reward amount. + pub value: U256, + /// Reward type. + pub reward_type: RewardType, } impl From for Reward { - fn from(r: trace::Reward) -> Self { - Reward { - author: r.author, - value: r.value, - reward_type: r.reward_type.into(), - } - } + fn from(r: trace::Reward) -> Self { + Reward { + author: r.author, + value: r.value, + reward_type: r.reward_type.into(), + } + } } /// Suicide #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct Suicide { - /// Address. - pub address: H160, - /// Refund address. - pub refund_address: H160, - /// Balance. - pub balance: U256, + /// Address. + pub address: H160, + /// Refund address. + pub refund_address: H160, + /// Balance. + pub balance: U256, } impl From for Suicide { - fn from(s: trace::Suicide) -> Self { - Suicide { - address: s.address, - refund_address: s.refund_address, - balance: s.balance, - } - } + fn from(s: trace::Suicide) -> Self { + Suicide { + address: s.address, + refund_address: s.refund_address, + balance: s.balance, + } + } } /// Action #[derive(Debug)] pub enum Action { - /// Call - Call(Call), - /// Create - Create(Create), - /// Suicide - Suicide(Suicide), - /// Reward - Reward(Reward), + /// Call + Call(Call), + /// Create + Create(Create), + /// Suicide + Suicide(Suicide), + /// Reward + Reward(Reward), } impl From for Action { - fn from(c: trace::Action) -> Self { - match c { - trace::Action::Call(call) => Action::Call(call.into()), - trace::Action::Create(create) => Action::Create(create.into()), - trace::Action::Suicide(suicide) => Action::Suicide(suicide.into()), - trace::Action::Reward(reward) => Action::Reward(reward.into()), - } - } + fn from(c: trace::Action) -> Self { + match c { + trace::Action::Call(call) => Action::Call(call.into()), + trace::Action::Create(create) => Action::Create(create.into()), + trace::Action::Suicide(suicide) => Action::Suicide(suicide.into()), + trace::Action::Reward(reward) => Action::Reward(reward.into()), + } + } } /// Call Result #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct CallResult { - /// Gas used - gas_used: U256, - /// Output bytes - output: Bytes, + /// Gas used + gas_used: U256, + /// Output bytes + output: Bytes, } impl From for CallResult { - fn from(c: trace::CallResult) -> Self { - CallResult { - gas_used: c.gas_used, - output: c.output.into(), - } - } + fn from(c: trace::CallResult) -> Self { + CallResult { + gas_used: c.gas_used, + output: c.output.into(), + } + } } /// Craete Result #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct CreateResult { - /// Gas used - gas_used: U256, - /// Code - code: Bytes, - /// Assigned address - address: H160, + /// Gas used + gas_used: U256, + /// Code + code: Bytes, + /// Assigned address + address: H160, } impl From for CreateResult { - fn from(c: trace::CreateResult) -> Self { - CreateResult { - gas_used: c.gas_used, - code: c.code.into(), - address: c.address, - } - } + fn from(c: trace::CreateResult) -> Self { + CreateResult { + gas_used: c.gas_used, + code: c.code.into(), + address: c.address, + } + } } /// Response #[derive(Debug)] pub enum Res { - /// Call - Call(CallResult), - /// Create - Create(CreateResult), - /// Call failure - FailedCall(TraceError), - /// Creation failure - FailedCreate(TraceError), - /// None - None, + /// Call + Call(CallResult), + /// Create + Create(CreateResult), + /// Call failure + FailedCall(TraceError), + /// Creation failure + FailedCreate(TraceError), + /// None + None, } impl From for Res { - fn from(t: trace::Res) -> Self { - match t { - trace::Res::Call(call) => Res::Call(CallResult::from(call)), - trace::Res::Create(create) => Res::Create(CreateResult::from(create)), - trace::Res::FailedCall(error) => Res::FailedCall(error), - trace::Res::FailedCreate(error) => Res::FailedCreate(error), - trace::Res::None => Res::None, - } - } + fn from(t: trace::Res) -> Self { + match t { + trace::Res::Call(call) => Res::Call(CallResult::from(call)), + trace::Res::Create(create) => Res::Create(CreateResult::from(create)), + trace::Res::FailedCall(error) => Res::FailedCall(error), + trace::Res::FailedCreate(error) => Res::FailedCreate(error), + trace::Res::None => Res::None, + } + } } /// Trace #[derive(Debug)] pub struct LocalizedTrace { - /// Action - action: Action, - /// Result - result: Res, - /// Trace address - trace_address: Vec, - /// Subtraces - subtraces: usize, - /// Transaction position - transaction_position: Option, - /// Transaction hash - transaction_hash: Option, - /// Block Number - block_number: u64, - /// Block Hash - block_hash: H256, + /// Action + action: Action, + /// Result + result: Res, + /// Trace address + trace_address: Vec, + /// Subtraces + subtraces: usize, + /// Transaction position + transaction_position: Option, + /// Transaction hash + transaction_hash: Option, + /// Block Number + block_number: u64, + /// Block Hash + block_hash: H256, } impl Serialize for LocalizedTrace { - fn serialize(&self, serializer: S) -> Result - where S: Serializer - { - let mut struc = serializer.serialize_struct("LocalizedTrace", 9)?; - match self.action { - Action::Call(ref call) => { - struc.serialize_field("type", "call")?; - struc.serialize_field("action", call)?; - }, - Action::Create(ref create) => { - struc.serialize_field("type", "create")?; - struc.serialize_field("action", create)?; - }, - Action::Suicide(ref suicide) => { - struc.serialize_field("type", "suicide")?; - struc.serialize_field("action", suicide)?; - }, - Action::Reward(ref reward) => { - struc.serialize_field("type", "reward")?; - struc.serialize_field("action", reward)?; - }, - } - - match self.result { - Res::Call(ref call) => struc.serialize_field("result", call)?, - Res::Create(ref create) => struc.serialize_field("result", create)?, - Res::FailedCall(ref error) => struc.serialize_field("error", &error.to_string())?, - Res::FailedCreate(ref error) => struc.serialize_field("error", &error.to_string())?, - Res::None => struc.serialize_field("result", &None as &Option)?, - } - - struc.serialize_field("traceAddress", &self.trace_address)?; - struc.serialize_field("subtraces", &self.subtraces)?; - struc.serialize_field("transactionPosition", &self.transaction_position)?; - struc.serialize_field("transactionHash", &self.transaction_hash)?; - struc.serialize_field("blockNumber", &self.block_number)?; - struc.serialize_field("blockHash", &self.block_hash)?; - - struc.end() - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut struc = serializer.serialize_struct("LocalizedTrace", 9)?; + match self.action { + Action::Call(ref call) => { + struc.serialize_field("type", "call")?; + struc.serialize_field("action", call)?; + } + Action::Create(ref create) => { + struc.serialize_field("type", "create")?; + struc.serialize_field("action", create)?; + } + Action::Suicide(ref suicide) => { + struc.serialize_field("type", "suicide")?; + struc.serialize_field("action", suicide)?; + } + Action::Reward(ref reward) => { + struc.serialize_field("type", "reward")?; + struc.serialize_field("action", reward)?; + } + } + + match self.result { + Res::Call(ref call) => struc.serialize_field("result", call)?, + Res::Create(ref create) => struc.serialize_field("result", create)?, + Res::FailedCall(ref error) => struc.serialize_field("error", &error.to_string())?, + Res::FailedCreate(ref error) => struc.serialize_field("error", &error.to_string())?, + Res::None => struc.serialize_field("result", &None as &Option)?, + } + + struc.serialize_field("traceAddress", &self.trace_address)?; + struc.serialize_field("subtraces", &self.subtraces)?; + struc.serialize_field("transactionPosition", &self.transaction_position)?; + struc.serialize_field("transactionHash", &self.transaction_hash)?; + struc.serialize_field("blockNumber", &self.block_number)?; + struc.serialize_field("blockHash", &self.block_hash)?; + + struc.end() + } } impl From for LocalizedTrace { - fn from(t: EthLocalizedTrace) -> Self { - LocalizedTrace { - action: t.action.into(), - result: t.result.into(), - trace_address: t.trace_address.into_iter().map(Into::into).collect(), - subtraces: t.subtraces, - transaction_position: t.transaction_number.map(Into::into), - transaction_hash: t.transaction_hash.map(Into::into), - block_number: t.block_number, - block_hash: t.block_hash, - } - } + fn from(t: EthLocalizedTrace) -> Self { + LocalizedTrace { + action: t.action.into(), + result: t.result.into(), + trace_address: t.trace_address.into_iter().map(Into::into).collect(), + subtraces: t.subtraces, + transaction_position: t.transaction_number.map(Into::into), + transaction_hash: t.transaction_hash.map(Into::into), + block_number: t.block_number, + block_hash: t.block_hash, + } + } } /// Trace #[derive(Debug)] pub struct Trace { - /// Trace address - trace_address: Vec, - /// Subtraces - subtraces: usize, - /// Action - action: Action, - /// Result - result: Res, + /// Trace address + trace_address: Vec, + /// Subtraces + subtraces: usize, + /// Action + action: Action, + /// Result + result: Res, } impl Serialize for Trace { - fn serialize(&self, serializer: S) -> Result - where S: Serializer - { - let mut struc = serializer.serialize_struct("Trace", 4)?; - match self.action { - Action::Call(ref call) => { - struc.serialize_field("type", "call")?; - struc.serialize_field("action", call)?; - }, - Action::Create(ref create) => { - struc.serialize_field("type", "create")?; - struc.serialize_field("action", create)?; - }, - Action::Suicide(ref suicide) => { - struc.serialize_field("type", "suicide")?; - struc.serialize_field("action", suicide)?; - }, - Action::Reward(ref reward) => { - struc.serialize_field("type", "reward")?; - struc.serialize_field("action", reward)?; - }, - } - - match self.result { - Res::Call(ref call) => struc.serialize_field("result", call)?, - Res::Create(ref create) => struc.serialize_field("result", create)?, - Res::FailedCall(ref error) => struc.serialize_field("error", &error.to_string())?, - Res::FailedCreate(ref error) => struc.serialize_field("error", &error.to_string())?, - Res::None => struc.serialize_field("result", &None as &Option)?, - } - - struc.serialize_field("traceAddress", &self.trace_address)?; - struc.serialize_field("subtraces", &self.subtraces)?; - - struc.end() - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut struc = serializer.serialize_struct("Trace", 4)?; + match self.action { + Action::Call(ref call) => { + struc.serialize_field("type", "call")?; + struc.serialize_field("action", call)?; + } + Action::Create(ref create) => { + struc.serialize_field("type", "create")?; + struc.serialize_field("action", create)?; + } + Action::Suicide(ref suicide) => { + struc.serialize_field("type", "suicide")?; + struc.serialize_field("action", suicide)?; + } + Action::Reward(ref reward) => { + struc.serialize_field("type", "reward")?; + struc.serialize_field("action", reward)?; + } + } + + match self.result { + Res::Call(ref call) => struc.serialize_field("result", call)?, + Res::Create(ref create) => struc.serialize_field("result", create)?, + Res::FailedCall(ref error) => struc.serialize_field("error", &error.to_string())?, + Res::FailedCreate(ref error) => struc.serialize_field("error", &error.to_string())?, + Res::None => struc.serialize_field("result", &None as &Option)?, + } + + struc.serialize_field("traceAddress", &self.trace_address)?; + struc.serialize_field("subtraces", &self.subtraces)?; + + struc.end() + } } impl From for Trace { - fn from(t: FlatTrace) -> Self { - Trace { - trace_address: t.trace_address.into_iter().map(Into::into).collect(), - subtraces: t.subtraces, - action: t.action.into(), - result: t.result.into(), - } - } + fn from(t: FlatTrace) -> Self { + Trace { + trace_address: t.trace_address.into_iter().map(Into::into).collect(), + subtraces: t.subtraces, + action: t.action.into(), + result: t.result.into(), + } + } } #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] /// A diff of some chunk of memory. pub struct TraceResults { - /// The output of the call/create - pub output: Bytes, - /// The transaction trace. - pub trace: Vec, - /// The transaction trace. - pub vm_trace: Option, - /// The transaction trace. - pub state_diff: Option, + /// The output of the call/create + pub output: Bytes, + /// The transaction trace. + pub trace: Vec, + /// The transaction trace. + pub vm_trace: Option, + /// The transaction trace. + pub state_diff: Option, } impl From for TraceResults { - fn from(t: Executed) -> Self { - TraceResults { - output: t.output.into(), - trace: t.trace.into_iter().map(Into::into).collect(), - vm_trace: t.vm_trace.map(Into::into), - state_diff: t.state_diff.map(Into::into), - } - } + fn from(t: Executed) -> Self { + TraceResults { + output: t.output.into(), + trace: t.trace.into_iter().map(Into::into).collect(), + vm_trace: t.vm_trace.map(Into::into), + state_diff: t.state_diff.map(Into::into), + } + } } #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] /// A diff of some chunk of memory. pub struct TraceResultsWithTransactionHash { - /// The output of the call/create - pub output: Bytes, - /// The transaction trace. - pub trace: Vec, - /// The transaction trace. - pub vm_trace: Option, - /// The transaction trace. - pub state_diff: Option, - /// The transaction Hash. - pub transaction_hash: H256, + /// The output of the call/create + pub output: Bytes, + /// The transaction trace. + pub trace: Vec, + /// The transaction trace. + pub vm_trace: Option, + /// The transaction trace. + pub state_diff: Option, + /// The transaction Hash. + pub transaction_hash: H256, } impl From<(H256, Executed)> for TraceResultsWithTransactionHash { - fn from(t: (H256, Executed)) -> Self { - TraceResultsWithTransactionHash { - output: t.1.output.into(), - trace: t.1.trace.into_iter().map(Into::into).collect(), - vm_trace: t.1.vm_trace.map(Into::into), - state_diff: t.1.state_diff.map(Into::into), - transaction_hash: t.0, - } - } + fn from(t: (H256, Executed)) -> Self { + TraceResultsWithTransactionHash { + output: t.1.output.into(), + trace: t.1.trace.into_iter().map(Into::into).collect(), + vm_trace: t.1.vm_trace.map(Into::into), + state_diff: t.1.state_diff.map(Into::into), + transaction_hash: t.0, + } + } } #[cfg(test)] mod tests { - use serde_json; - use std::collections::BTreeMap; - use v1::types::Bytes; - use ethcore::trace::TraceError; - use super::*; - - #[test] - fn should_serialize_trace_results() { - let r = TraceResults { - output: vec![0x60].into(), - trace: vec![], - vm_trace: None, - state_diff: None, - }; - let serialized = serde_json::to_string(&r).unwrap(); - assert_eq!(serialized, r#"{"output":"0x60","trace":[],"vmTrace":null,"stateDiff":null}"#); - } - - #[test] - fn test_trace_call_serialize() { - let t = LocalizedTrace { - action: Action::Call(Call { - from: 4.into(), - to: 5.into(), - value: 6.into(), - gas: 7.into(), - input: Bytes::new(vec![0x12, 0x34]), - call_type: CallType::Call, - }), - result: Res::Call(CallResult { - gas_used: 8.into(), - output: vec![0x56, 0x78].into(), - }), - trace_address: vec![10], - subtraces: 1, - transaction_position: Some(11), - transaction_hash: Some(12.into()), - block_number: 13, - block_hash: 14.into(), - }; - let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"type":"call","action":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x6","gas":"0x7","input":"0x1234","callType":"call"},"result":{"gasUsed":"0x8","output":"0x5678"},"traceAddress":[10],"subtraces":1,"transactionPosition":11,"transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":13,"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); - } - - #[test] - fn test_trace_failed_call_serialize() { - let t = LocalizedTrace { - action: Action::Call(Call { - from: 4.into(), - to: 5.into(), - value: 6.into(), - gas: 7.into(), - input: Bytes::new(vec![0x12, 0x34]), - call_type: CallType::Call, - }), - result: Res::FailedCall(TraceError::OutOfGas), - trace_address: vec![10], - subtraces: 1, - transaction_position: Some(11), - transaction_hash: Some(12.into()), - block_number: 13, - block_hash: 14.into(), - }; - let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"type":"call","action":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x6","gas":"0x7","input":"0x1234","callType":"call"},"error":"Out of gas","traceAddress":[10],"subtraces":1,"transactionPosition":11,"transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":13,"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); - } - - #[test] - fn test_trace_create_serialize() { - let t = LocalizedTrace { - action: Action::Create(Create { - from: 4.into(), - value: 6.into(), - gas: 7.into(), - init: Bytes::new(vec![0x12, 0x34]), - }), - result: Res::Create(CreateResult { - gas_used: 8.into(), - code: vec![0x56, 0x78].into(), - address: 0xff.into(), - }), - trace_address: vec![10], - subtraces: 1, - transaction_position: Some(11), - transaction_hash: Some(12.into()), - block_number: 13, - block_hash: 14.into(), - }; - let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"type":"create","action":{"from":"0x0000000000000000000000000000000000000004","value":"0x6","gas":"0x7","init":"0x1234"},"result":{"gasUsed":"0x8","code":"0x5678","address":"0x00000000000000000000000000000000000000ff"},"traceAddress":[10],"subtraces":1,"transactionPosition":11,"transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":13,"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); - } - - #[test] - fn test_trace_failed_create_serialize() { - let t = LocalizedTrace { - action: Action::Create(Create { - from: 4.into(), - value: 6.into(), - gas: 7.into(), - init: Bytes::new(vec![0x12, 0x34]), - }), - result: Res::FailedCreate(TraceError::OutOfGas), - trace_address: vec![10], - subtraces: 1, - transaction_position: Some(11), - transaction_hash: Some(12.into()), - block_number: 13, - block_hash: 14.into(), - }; - let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"type":"create","action":{"from":"0x0000000000000000000000000000000000000004","value":"0x6","gas":"0x7","init":"0x1234"},"error":"Out of gas","traceAddress":[10],"subtraces":1,"transactionPosition":11,"transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":13,"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); - } - - #[test] - fn test_trace_suicide_serialize() { - let t = LocalizedTrace { - action: Action::Suicide(Suicide { - address: 4.into(), - refund_address: 6.into(), - balance: 7.into(), - }), - result: Res::None, - trace_address: vec![10], - subtraces: 1, - transaction_position: Some(11), - transaction_hash: Some(12.into()), - block_number: 13, - block_hash: 14.into(), - }; - let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"type":"suicide","action":{"address":"0x0000000000000000000000000000000000000004","refundAddress":"0x0000000000000000000000000000000000000006","balance":"0x7"},"result":null,"traceAddress":[10],"subtraces":1,"transactionPosition":11,"transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":13,"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); - } - - #[test] - fn test_trace_reward_serialize() { - let t = LocalizedTrace { - action: Action::Reward(Reward { - author: 4.into(), - value: 6.into(), - reward_type: RewardType::Block, - }), - result: Res::None, - trace_address: vec![10], - subtraces: 1, - transaction_position: None, - transaction_hash: None, - block_number: 13, - block_hash: 14.into(), - }; - let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"type":"reward","action":{"author":"0x0000000000000000000000000000000000000004","value":"0x6","rewardType":"block"},"result":null,"traceAddress":[10],"subtraces":1,"transactionPosition":null,"transactionHash":null,"blockNumber":13,"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); - } - - #[test] - fn test_vmtrace_serialize() { - let t = VMTrace { - code: vec![0, 1, 2, 3].into(), - ops: vec![ - VMOperation { - pc: 0, - cost: 10, - ex: None, - sub: None, - }, - VMOperation { - pc: 1, - cost: 11, - ex: Some(VMExecutedOperation { - used: 10, - push: vec![69.into()], - mem: None, - store: None, - }), - sub: Some(VMTrace { - code: vec![0].into(), - ops: vec![ - VMOperation { - pc: 0, - cost: 0, - ex: Some(VMExecutedOperation { - used: 10, - push: vec![42.into()].into(), - mem: Some(MemoryDiff {off: 42, data: vec![1, 2, 3].into()}), - store: Some(StorageDiff {key: 69.into(), val: 42.into()}), - }), - sub: None, - } - ] - }), - } - ] - }; - let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"code":"0x00010203","ops":[{"pc":0,"cost":10,"ex":null,"sub":null},{"pc":1,"cost":11,"ex":{"used":10,"push":["0x45"],"mem":null,"store":null},"sub":{"code":"0x00","ops":[{"pc":0,"cost":0,"ex":{"used":10,"push":["0x2a"],"mem":{"off":42,"data":"0x010203"},"store":{"key":"0x45","val":"0x2a"}},"sub":null}]}}]}"#); - } - - #[test] - fn test_statediff_serialize() { - let t = StateDiff(map![ - 42.into() => AccountDiff { - balance: Diff::Same, - nonce: Diff::Born(1.into()), - code: Diff::Same, - storage: map![ - 42.into() => Diff::Same - ] - }, - 69.into() => AccountDiff { - balance: Diff::Same, - nonce: Diff::Changed(ChangedType { from: 1.into(), to: 0.into() }), - code: Diff::Died(vec![96].into()), - storage: map![], - } - ]); - let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"0x000000000000000000000000000000000000002a":{"balance":"=","nonce":{"+":"0x1"},"code":"=","storage":{"0x000000000000000000000000000000000000000000000000000000000000002a":"="}},"0x0000000000000000000000000000000000000045":{"balance":"=","nonce":{"*":{"from":"0x1","to":"0x0"}},"code":{"-":"0x60"},"storage":{}}}"#); - } + use super::*; + use ethcore::trace::TraceError; + use serde_json; + use std::collections::BTreeMap; + use v1::types::Bytes; + + #[test] + fn should_serialize_trace_results() { + let r = TraceResults { + output: vec![0x60].into(), + trace: vec![], + vm_trace: None, + state_diff: None, + }; + let serialized = serde_json::to_string(&r).unwrap(); + assert_eq!( + serialized, + r#"{"output":"0x60","trace":[],"vmTrace":null,"stateDiff":null}"# + ); + } + + #[test] + fn test_trace_call_serialize() { + let t = LocalizedTrace { + action: Action::Call(Call { + from: 4.into(), + to: 5.into(), + value: 6.into(), + gas: 7.into(), + input: Bytes::new(vec![0x12, 0x34]), + call_type: CallType::Call, + }), + result: Res::Call(CallResult { + gas_used: 8.into(), + output: vec![0x56, 0x78].into(), + }), + trace_address: vec![10], + subtraces: 1, + transaction_position: Some(11), + transaction_hash: Some(12.into()), + block_number: 13, + block_hash: 14.into(), + }; + let serialized = serde_json::to_string(&t).unwrap(); + assert_eq!( + serialized, + r#"{"type":"call","action":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x6","gas":"0x7","input":"0x1234","callType":"call"},"result":{"gasUsed":"0x8","output":"0x5678"},"traceAddress":[10],"subtraces":1,"transactionPosition":11,"transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":13,"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"# + ); + } + + #[test] + fn test_trace_failed_call_serialize() { + let t = LocalizedTrace { + action: Action::Call(Call { + from: 4.into(), + to: 5.into(), + value: 6.into(), + gas: 7.into(), + input: Bytes::new(vec![0x12, 0x34]), + call_type: CallType::Call, + }), + result: Res::FailedCall(TraceError::OutOfGas), + trace_address: vec![10], + subtraces: 1, + transaction_position: Some(11), + transaction_hash: Some(12.into()), + block_number: 13, + block_hash: 14.into(), + }; + let serialized = serde_json::to_string(&t).unwrap(); + assert_eq!( + serialized, + r#"{"type":"call","action":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x6","gas":"0x7","input":"0x1234","callType":"call"},"error":"Out of gas","traceAddress":[10],"subtraces":1,"transactionPosition":11,"transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":13,"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"# + ); + } + + #[test] + fn test_trace_create_serialize() { + let t = LocalizedTrace { + action: Action::Create(Create { + from: 4.into(), + value: 6.into(), + gas: 7.into(), + init: Bytes::new(vec![0x12, 0x34]), + }), + result: Res::Create(CreateResult { + gas_used: 8.into(), + code: vec![0x56, 0x78].into(), + address: 0xff.into(), + }), + trace_address: vec![10], + subtraces: 1, + transaction_position: Some(11), + transaction_hash: Some(12.into()), + block_number: 13, + block_hash: 14.into(), + }; + let serialized = serde_json::to_string(&t).unwrap(); + assert_eq!( + serialized, + r#"{"type":"create","action":{"from":"0x0000000000000000000000000000000000000004","value":"0x6","gas":"0x7","init":"0x1234"},"result":{"gasUsed":"0x8","code":"0x5678","address":"0x00000000000000000000000000000000000000ff"},"traceAddress":[10],"subtraces":1,"transactionPosition":11,"transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":13,"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"# + ); + } + + #[test] + fn test_trace_failed_create_serialize() { + let t = LocalizedTrace { + action: Action::Create(Create { + from: 4.into(), + value: 6.into(), + gas: 7.into(), + init: Bytes::new(vec![0x12, 0x34]), + }), + result: Res::FailedCreate(TraceError::OutOfGas), + trace_address: vec![10], + subtraces: 1, + transaction_position: Some(11), + transaction_hash: Some(12.into()), + block_number: 13, + block_hash: 14.into(), + }; + let serialized = serde_json::to_string(&t).unwrap(); + assert_eq!( + serialized, + r#"{"type":"create","action":{"from":"0x0000000000000000000000000000000000000004","value":"0x6","gas":"0x7","init":"0x1234"},"error":"Out of gas","traceAddress":[10],"subtraces":1,"transactionPosition":11,"transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":13,"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"# + ); + } + + #[test] + fn test_trace_suicide_serialize() { + let t = LocalizedTrace { + action: Action::Suicide(Suicide { + address: 4.into(), + refund_address: 6.into(), + balance: 7.into(), + }), + result: Res::None, + trace_address: vec![10], + subtraces: 1, + transaction_position: Some(11), + transaction_hash: Some(12.into()), + block_number: 13, + block_hash: 14.into(), + }; + let serialized = serde_json::to_string(&t).unwrap(); + assert_eq!( + serialized, + r#"{"type":"suicide","action":{"address":"0x0000000000000000000000000000000000000004","refundAddress":"0x0000000000000000000000000000000000000006","balance":"0x7"},"result":null,"traceAddress":[10],"subtraces":1,"transactionPosition":11,"transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":13,"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"# + ); + } + + #[test] + fn test_trace_reward_serialize() { + let t = LocalizedTrace { + action: Action::Reward(Reward { + author: 4.into(), + value: 6.into(), + reward_type: RewardType::Block, + }), + result: Res::None, + trace_address: vec![10], + subtraces: 1, + transaction_position: None, + transaction_hash: None, + block_number: 13, + block_hash: 14.into(), + }; + let serialized = serde_json::to_string(&t).unwrap(); + assert_eq!( + serialized, + r#"{"type":"reward","action":{"author":"0x0000000000000000000000000000000000000004","value":"0x6","rewardType":"block"},"result":null,"traceAddress":[10],"subtraces":1,"transactionPosition":null,"transactionHash":null,"blockNumber":13,"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"# + ); + } + + #[test] + fn test_vmtrace_serialize() { + let t = VMTrace { + code: vec![0, 1, 2, 3].into(), + ops: vec![ + VMOperation { + pc: 0, + cost: 10, + ex: None, + sub: None, + }, + VMOperation { + pc: 1, + cost: 11, + ex: Some(VMExecutedOperation { + used: 10, + push: vec![69.into()], + mem: None, + store: None, + }), + sub: Some(VMTrace { + code: vec![0].into(), + ops: vec![VMOperation { + pc: 0, + cost: 0, + ex: Some(VMExecutedOperation { + used: 10, + push: vec![42.into()].into(), + mem: Some(MemoryDiff { + off: 42, + data: vec![1, 2, 3].into(), + }), + store: Some(StorageDiff { + key: 69.into(), + val: 42.into(), + }), + }), + sub: None, + }], + }), + }, + ], + }; + let serialized = serde_json::to_string(&t).unwrap(); + assert_eq!( + serialized, + r#"{"code":"0x00010203","ops":[{"pc":0,"cost":10,"ex":null,"sub":null},{"pc":1,"cost":11,"ex":{"used":10,"push":["0x45"],"mem":null,"store":null},"sub":{"code":"0x00","ops":[{"pc":0,"cost":0,"ex":{"used":10,"push":["0x2a"],"mem":{"off":42,"data":"0x010203"},"store":{"key":"0x45","val":"0x2a"}},"sub":null}]}}]}"# + ); + } + + #[test] + fn test_statediff_serialize() { + let t = StateDiff(map![ + 42.into() => AccountDiff { + balance: Diff::Same, + nonce: Diff::Born(1.into()), + code: Diff::Same, + storage: map![ + 42.into() => Diff::Same + ] + }, + 69.into() => AccountDiff { + balance: Diff::Same, + nonce: Diff::Changed(ChangedType { from: 1.into(), to: 0.into() }), + code: Diff::Died(vec![96].into()), + storage: map![], + } + ]); + let serialized = serde_json::to_string(&t).unwrap(); + assert_eq!( + serialized, + r#"{"0x000000000000000000000000000000000000002a":{"balance":"=","nonce":{"+":"0x1"},"code":"=","storage":{"0x000000000000000000000000000000000000000000000000000000000000002a":"="}},"0x0000000000000000000000000000000000000045":{"balance":"=","nonce":{"*":{"from":"0x1","to":"0x0"}},"code":{"-":"0x60"},"storage":{}}}"# + ); + } } diff --git a/rpc/src/v1/types/trace_filter.rs b/rpc/src/v1/types/trace_filter.rs index a455c3d1cc2..50b3ea47f12 100644 --- a/rpc/src/v1/types/trace_filter.rs +++ b/rpc/src/v1/types/trace_filter.rs @@ -1,23 +1,22 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! Trace filter deserialization. -use ethcore::client::BlockId; -use ethcore::client; +use ethcore::{client, client::BlockId}; use ethereum_types::H160; use v1::types::BlockNumber; @@ -26,66 +25,74 @@ use v1::types::BlockNumber; #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub struct TraceFilter { - /// From block - pub from_block: Option, - /// To block - pub to_block: Option, - /// From address - pub from_address: Option>, - /// To address - pub to_address: Option>, - /// Output offset - pub after: Option, - /// Output amount - pub count: Option, + /// From block + pub from_block: Option, + /// To block + pub to_block: Option, + /// From address + pub from_address: Option>, + /// To address + pub to_address: Option>, + /// Output offset + pub after: Option, + /// Output amount + pub count: Option, } impl Into for TraceFilter { - fn into(self) -> client::TraceFilter { - let num_to_id = |num| match num { - BlockNumber::Num(n) => BlockId::Number(n), - BlockNumber::Earliest => BlockId::Earliest, - BlockNumber::Latest => BlockId::Latest, - BlockNumber::Pending => { - warn!("Pending traces are not supported and might be removed in future versions. Falling back to Latest"); - BlockId::Latest - } - }; - let start = self.from_block.map_or(BlockId::Latest, &num_to_id); - let end = self.to_block.map_or(BlockId::Latest, &num_to_id); - client::TraceFilter { - range: start..end, - from_address: self.from_address.map_or_else(Vec::new, |x| x.into_iter().map(Into::into).collect()), - to_address: self.to_address.map_or_else(Vec::new, |x| x.into_iter().map(Into::into).collect()), - after: self.after, - count: self.count, - } - } + fn into(self) -> client::TraceFilter { + let num_to_id = |num| match num { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), + BlockNumber::Num(n) => BlockId::Number(n), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + BlockNumber::Pending => { + warn!("Pending traces are not supported and might be removed in future versions. Falling back to Latest"); + BlockId::Latest + } + }; + let start = self.from_block.map_or(BlockId::Latest, &num_to_id); + let end = self.to_block.map_or(BlockId::Latest, &num_to_id); + client::TraceFilter { + range: start..end, + from_address: self + .from_address + .map_or_else(Vec::new, |x| x.into_iter().map(Into::into).collect()), + to_address: self + .to_address + .map_or_else(Vec::new, |x| x.into_iter().map(Into::into).collect()), + after: self.after, + count: self.count, + } + } } #[cfg(test)] mod tests { - use serde_json; - use ethereum_types::Address; - use v1::types::{BlockNumber, TraceFilter}; + use ethereum_types::Address; + use serde_json; + use v1::types::{BlockNumber, TraceFilter}; - #[test] - fn test_empty_trace_filter_deserialize() { - let s = r#"{}"#; - let deserialized: TraceFilter = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, TraceFilter { - from_block: None, - to_block: None, - from_address: None, - to_address: None, - after: None, - count: None, - }); - } + #[test] + fn test_empty_trace_filter_deserialize() { + let s = r#"{}"#; + let deserialized: TraceFilter = serde_json::from_str(s).unwrap(); + assert_eq!( + deserialized, + TraceFilter { + from_block: None, + to_block: None, + from_address: None, + to_address: None, + after: None, + count: None, + } + ); + } - #[test] - fn test_trace_filter_deserialize() { - let s = r#"{ + #[test] + fn test_trace_filter_deserialize() { + let s = r#"{ "fromBlock": "latest", "toBlock": "latest", "fromAddress": ["0x0000000000000000000000000000000000000003"], @@ -93,14 +100,17 @@ mod tests { "after": 50, "count": 100 }"#; - let deserialized: TraceFilter = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, TraceFilter { - from_block: Some(BlockNumber::Latest), - to_block: Some(BlockNumber::Latest), - from_address: Some(vec![Address::from(3).into()]), - to_address: Some(vec![Address::from(5).into()]), - after: 50.into(), - count: 100.into(), - }); - } + let deserialized: TraceFilter = serde_json::from_str(s).unwrap(); + assert_eq!( + deserialized, + TraceFilter { + from_block: Some(BlockNumber::Latest), + to_block: Some(BlockNumber::Latest), + from_address: Some(vec![Address::from(3).into()]), + to_address: Some(vec![Address::from(5).into()]), + after: 50.into(), + count: 100.into(), + } + ); + } } diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 931e0386696..a0d78ff44d6 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -1,331 +1,335 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use std::sync::Arc; -use serde::{Serialize, Serializer}; -use serde::ser::SerializeStruct; use ethcore::{contract_address, CreateContractAddress}; -use ethereum_types::{H160, H256, H512, U64, U256}; +use ethereum_types::{H160, H256, H512, U256, U64}; use miner; -use types::transaction::{LocalizedTransaction, Action, PendingTransaction, SignedTransaction}; +use serde::{ser::SerializeStruct, Serialize, Serializer}; +use types::transaction::{Action, LocalizedTransaction, PendingTransaction, SignedTransaction}; use v1::types::{Bytes, TransactionCondition}; /// Transaction #[derive(Debug, Default, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Transaction { - /// Hash - pub hash: H256, - /// Nonce - pub nonce: U256, - /// Block hash - pub block_hash: Option, - /// Block number - pub block_number: Option, - /// Transaction Index - pub transaction_index: Option, - /// Sender - pub from: H160, - /// Recipient - pub to: Option, - /// Transfered value - pub value: U256, - /// Gas Price - pub gas_price: U256, - /// Gas - pub gas: U256, - /// Data - pub input: Bytes, - /// Creates contract - pub creates: Option, - /// Raw transaction data - pub raw: Bytes, - /// Public key of the signer. - pub public_key: Option, - /// The network id of the transaction, if any. - pub chain_id: Option, - /// The standardised V field of the signature (0 or 1). - pub standard_v: U256, - /// The standardised V field of the signature. - pub v: U256, - /// The R field of the signature. - pub r: U256, - /// The S field of the signature. - pub s: U256, - /// Transaction activates at specified block. - pub condition: Option, + /// Hash + pub hash: H256, + /// Nonce + pub nonce: U256, + /// Block hash + pub block_hash: Option, + /// Block number + pub block_number: Option, + /// Transaction Index + pub transaction_index: Option, + /// Sender + pub from: H160, + /// Recipient + pub to: Option, + /// Transfered value + pub value: U256, + /// Gas Price + pub gas_price: U256, + /// Gas + pub gas: U256, + /// Data + pub input: Bytes, + /// Creates contract + pub creates: Option, + /// Raw transaction data + pub raw: Bytes, + /// Public key of the signer. + pub public_key: Option, + /// The network id of the transaction, if any. + pub chain_id: Option, + /// The standardised V field of the signature (0 or 1). + pub standard_v: U256, + /// The standardised V field of the signature. + pub v: U256, + /// The R field of the signature. + pub r: U256, + /// The S field of the signature. + pub s: U256, + /// Transaction activates at specified block. + pub condition: Option, } /// Local Transaction Status #[derive(Debug)] pub enum LocalTransactionStatus { - /// Transaction is pending - Pending, - /// Transaction is in future part of the queue - Future, - /// Transaction was mined. - Mined(Transaction), - /// Transaction was removed from the queue, but not mined. - Culled(Transaction), - /// Transaction was dropped because of limit. - Dropped(Transaction), - /// Transaction was replaced by transaction with higher gas price. - Replaced(Transaction, U256, H256), - /// Transaction never got into the queue. - Rejected(Transaction, String), - /// Transaction is invalid. - Invalid(Transaction), - /// Transaction was canceled. - Canceled(Transaction), + /// Transaction is pending + Pending, + /// Transaction is in future part of the queue + Future, + /// Transaction was mined. + Mined(Transaction), + /// Transaction was removed from the queue, but not mined. + Culled(Transaction), + /// Transaction was dropped because of limit. + Dropped(Transaction), + /// Transaction was replaced by transaction with higher gas price. + Replaced(Transaction, U256, H256), + /// Transaction never got into the queue. + Rejected(Transaction, String), + /// Transaction is invalid. + Invalid(Transaction), + /// Transaction was canceled. + Canceled(Transaction), } impl Serialize for LocalTransactionStatus { - fn serialize(&self, serializer: S) -> Result - where S: Serializer - { - use self::LocalTransactionStatus::*; + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use self::LocalTransactionStatus::*; - let elems = match *self { - Pending | Future => 1, - Mined(..) | Culled(..) | Dropped(..) | Invalid(..) | Canceled(..) => 2, - Rejected(..) => 3, - Replaced(..) => 4, - }; + let elems = match *self { + Pending | Future => 1, + Mined(..) | Culled(..) | Dropped(..) | Invalid(..) | Canceled(..) => 2, + Rejected(..) => 3, + Replaced(..) => 4, + }; - let status = "status"; - let transaction = "transaction"; + let status = "status"; + let transaction = "transaction"; - let mut struc = serializer.serialize_struct("LocalTransactionStatus", elems)?; - match *self { - Pending => struc.serialize_field(status, "pending")?, - Future => struc.serialize_field(status, "future")?, - Mined(ref tx) => { - struc.serialize_field(status, "mined")?; - struc.serialize_field(transaction, tx)?; - }, - Culled(ref tx) => { - struc.serialize_field(status, "culled")?; - struc.serialize_field(transaction, tx)?; - }, - Dropped(ref tx) => { - struc.serialize_field(status, "dropped")?; - struc.serialize_field(transaction, tx)?; - }, - Canceled(ref tx) => { - struc.serialize_field(status, "canceled")?; - struc.serialize_field(transaction, tx)?; - }, - Invalid(ref tx) => { - struc.serialize_field(status, "invalid")?; - struc.serialize_field(transaction, tx)?; - }, - Rejected(ref tx, ref reason) => { - struc.serialize_field(status, "rejected")?; - struc.serialize_field(transaction, tx)?; - struc.serialize_field("error", reason)?; - }, - Replaced(ref tx, ref gas_price, ref hash) => { - struc.serialize_field(status, "replaced")?; - struc.serialize_field(transaction, tx)?; - struc.serialize_field("hash", hash)?; - struc.serialize_field("gasPrice", gas_price)?; - }, - } + let mut struc = serializer.serialize_struct("LocalTransactionStatus", elems)?; + match *self { + Pending => struc.serialize_field(status, "pending")?, + Future => struc.serialize_field(status, "future")?, + Mined(ref tx) => { + struc.serialize_field(status, "mined")?; + struc.serialize_field(transaction, tx)?; + } + Culled(ref tx) => { + struc.serialize_field(status, "culled")?; + struc.serialize_field(transaction, tx)?; + } + Dropped(ref tx) => { + struc.serialize_field(status, "dropped")?; + struc.serialize_field(transaction, tx)?; + } + Canceled(ref tx) => { + struc.serialize_field(status, "canceled")?; + struc.serialize_field(transaction, tx)?; + } + Invalid(ref tx) => { + struc.serialize_field(status, "invalid")?; + struc.serialize_field(transaction, tx)?; + } + Rejected(ref tx, ref reason) => { + struc.serialize_field(status, "rejected")?; + struc.serialize_field(transaction, tx)?; + struc.serialize_field("error", reason)?; + } + Replaced(ref tx, ref gas_price, ref hash) => { + struc.serialize_field(status, "replaced")?; + struc.serialize_field(transaction, tx)?; + struc.serialize_field("hash", hash)?; + struc.serialize_field("gasPrice", gas_price)?; + } + } - struc.end() - } + struc.end() + } } /// Geth-compatible output for eth_signTransaction method #[derive(Debug, Default, Clone, PartialEq, Serialize)] pub struct RichRawTransaction { - /// Raw transaction RLP - pub raw: Bytes, - /// Transaction details - #[serde(rename = "tx")] - pub transaction: Transaction + /// Raw transaction RLP + pub raw: Bytes, + /// Transaction details + #[serde(rename = "tx")] + pub transaction: Transaction, } impl RichRawTransaction { - /// Creates new `RichRawTransaction` from `SignedTransaction`. - pub fn from_signed(tx: SignedTransaction) -> Self { - let tx = Transaction::from_signed(tx); - RichRawTransaction { - raw: tx.raw.clone(), - transaction: tx, - } - } + /// Creates new `RichRawTransaction` from `SignedTransaction`. + pub fn from_signed(tx: SignedTransaction) -> Self { + let tx = Transaction::from_signed(tx); + RichRawTransaction { + raw: tx.raw.clone(), + transaction: tx, + } + } } impl Transaction { - /// Convert `LocalizedTransaction` into RPC Transaction. - pub fn from_localized(mut t: LocalizedTransaction) -> Transaction { - let signature = t.signature(); - let scheme = CreateContractAddress::FromSenderAndNonce; - Transaction { - hash: t.hash(), - nonce: t.nonce, - block_hash: Some(t.block_hash), - block_number: Some(t.block_number.into()), - transaction_index: Some(t.transaction_index.into()), - from: t.sender(), - to: match t.action { - Action::Create => None, - Action::Call(ref address) => Some(*address) - }, - value: t.value, - gas_price: t.gas_price, - gas: t.gas, - input: Bytes::new(t.data.clone()), - creates: match t.action { - Action::Create => Some(contract_address(scheme, &t.sender(), &t.nonce, &t.data).0), - Action::Call(_) => None, - }, - raw: ::rlp::encode(&t.signed).into(), - public_key: t.recover_public().ok().map(Into::into), - chain_id: t.chain_id().map(U64::from), - standard_v: t.standard_v().into(), - v: t.original_v().into(), - r: signature.r().into(), - s: signature.s().into(), - condition: None, - } - } + /// Convert `LocalizedTransaction` into RPC Transaction. + pub fn from_localized(mut t: LocalizedTransaction) -> Transaction { + let signature = t.signature(); + let scheme = CreateContractAddress::FromSenderAndNonce; + Transaction { + hash: t.hash(), + nonce: t.nonce, + block_hash: Some(t.block_hash), + block_number: Some(t.block_number.into()), + transaction_index: Some(t.transaction_index.into()), + from: t.sender(), + to: match t.action { + Action::Create => None, + Action::Call(ref address) => Some(*address), + }, + value: t.value, + gas_price: t.gas_price, + gas: t.gas, + input: Bytes::new(t.data.clone()), + creates: match t.action { + Action::Create => Some(contract_address(scheme, &t.sender(), &t.nonce, &t.data).0), + Action::Call(_) => None, + }, + raw: ::rlp::encode(&t.signed).into(), + public_key: t.recover_public().ok().map(Into::into), + chain_id: t.chain_id().map(U64::from), + standard_v: t.standard_v().into(), + v: t.original_v().into(), + r: signature.r().into(), + s: signature.s().into(), + condition: None, + } + } - /// Convert `SignedTransaction` into RPC Transaction. - pub fn from_signed(t: SignedTransaction) -> Transaction { - let signature = t.signature(); - let scheme = CreateContractAddress::FromSenderAndNonce; - Transaction { - hash: t.hash(), - nonce: t.nonce, - block_hash: None, - block_number: None, - transaction_index: None, - from: t.sender(), - to: match t.action { - Action::Create => None, - Action::Call(ref address) => Some(*address) - }, - value: t.value, - gas_price: t.gas_price, - gas: t.gas, - input: Bytes::new(t.data.clone()), - creates: match t.action { - Action::Create => Some(contract_address(scheme, &t.sender(), &t.nonce, &t.data).0), - Action::Call(_) => None, - }, - raw: ::rlp::encode(&t).into(), - public_key: t.public_key().map(Into::into), - chain_id: t.chain_id().map(U64::from), - standard_v: t.standard_v().into(), - v: t.original_v().into(), - r: signature.r().into(), - s: signature.s().into(), - condition: None, - } - } + /// Convert `SignedTransaction` into RPC Transaction. + pub fn from_signed(t: SignedTransaction) -> Transaction { + let signature = t.signature(); + let scheme = CreateContractAddress::FromSenderAndNonce; + Transaction { + hash: t.hash(), + nonce: t.nonce, + block_hash: None, + block_number: None, + transaction_index: None, + from: t.sender(), + to: match t.action { + Action::Create => None, + Action::Call(ref address) => Some(*address), + }, + value: t.value, + gas_price: t.gas_price, + gas: t.gas, + input: Bytes::new(t.data.clone()), + creates: match t.action { + Action::Create => Some(contract_address(scheme, &t.sender(), &t.nonce, &t.data).0), + Action::Call(_) => None, + }, + raw: ::rlp::encode(&t).into(), + public_key: t.public_key().map(Into::into), + chain_id: t.chain_id().map(U64::from), + standard_v: t.standard_v().into(), + v: t.original_v().into(), + r: signature.r().into(), + s: signature.s().into(), + condition: None, + } + } - /// Convert `PendingTransaction` into RPC Transaction. - pub fn from_pending(t: PendingTransaction) -> Transaction { - let mut r = Transaction::from_signed(t.transaction); - r.condition = r.condition.map(Into::into); - r - } + /// Convert `PendingTransaction` into RPC Transaction. + pub fn from_pending(t: PendingTransaction) -> Transaction { + let mut r = Transaction::from_signed(t.transaction); + r.condition = r.condition.map(Into::into); + r + } } impl LocalTransactionStatus { - /// Convert `LocalTransactionStatus` into RPC `LocalTransactionStatus`. - pub fn from(s: miner::pool::local_transactions::Status) -> Self { - let convert = |tx: Arc| { - Transaction::from_signed(tx.signed().clone()) - }; - use miner::pool::local_transactions::Status::*; - match s { - Pending(_) => LocalTransactionStatus::Pending, - Mined(tx) => LocalTransactionStatus::Mined(convert(tx)), - Culled(tx) => LocalTransactionStatus::Culled(convert(tx)), - Dropped(tx) => LocalTransactionStatus::Dropped(convert(tx)), - Rejected(tx, reason) => LocalTransactionStatus::Rejected(convert(tx), reason), - Invalid(tx) => LocalTransactionStatus::Invalid(convert(tx)), - Canceled(tx) => LocalTransactionStatus::Canceled(convert(tx)), - Replaced { old, new } => LocalTransactionStatus::Replaced( - convert(old), - new.signed().gas_price, - new.signed().hash(), - ), - } - } + /// Convert `LocalTransactionStatus` into RPC `LocalTransactionStatus`. + pub fn from(s: miner::pool::local_transactions::Status) -> Self { + let convert = |tx: Arc| { + Transaction::from_signed(tx.signed().clone()) + }; + use miner::pool::local_transactions::Status::*; + match s { + Pending(_) => LocalTransactionStatus::Pending, + Mined(tx) => LocalTransactionStatus::Mined(convert(tx)), + Culled(tx) => LocalTransactionStatus::Culled(convert(tx)), + Dropped(tx) => LocalTransactionStatus::Dropped(convert(tx)), + Rejected(tx, reason) => LocalTransactionStatus::Rejected(convert(tx), reason), + Invalid(tx) => LocalTransactionStatus::Invalid(convert(tx)), + Canceled(tx) => LocalTransactionStatus::Canceled(convert(tx)), + Replaced { old, new } => LocalTransactionStatus::Replaced( + convert(old), + new.signed().gas_price, + new.signed().hash(), + ), + } + } } #[cfg(test)] mod tests { - use super::{Transaction, LocalTransactionStatus}; - use serde_json; + use super::{LocalTransactionStatus, Transaction}; + use serde_json; - #[test] - fn test_transaction_serialize() { - let t = Transaction::default(); - let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"chainId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0","condition":null}"#); - } + #[test] + fn test_transaction_serialize() { + let t = Transaction::default(); + let serialized = serde_json::to_string(&t).unwrap(); + assert_eq!( + serialized, + r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"chainId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0","condition":null}"# + ); + } - #[test] - fn test_local_transaction_status_serialize() { - let tx_ser = serde_json::to_string(&Transaction::default()).unwrap(); - let status1 = LocalTransactionStatus::Pending; - let status2 = LocalTransactionStatus::Future; - let status3 = LocalTransactionStatus::Mined(Transaction::default()); - let status4 = LocalTransactionStatus::Dropped(Transaction::default()); - let status5 = LocalTransactionStatus::Invalid(Transaction::default()); - let status6 = LocalTransactionStatus::Rejected(Transaction::default(), "Just because".into()); - let status7 = LocalTransactionStatus::Replaced(Transaction::default(), 5.into(), 10.into()); + #[test] + fn test_local_transaction_status_serialize() { + let tx_ser = serde_json::to_string(&Transaction::default()).unwrap(); + let status1 = LocalTransactionStatus::Pending; + let status2 = LocalTransactionStatus::Future; + let status3 = LocalTransactionStatus::Mined(Transaction::default()); + let status4 = LocalTransactionStatus::Dropped(Transaction::default()); + let status5 = LocalTransactionStatus::Invalid(Transaction::default()); + let status6 = + LocalTransactionStatus::Rejected(Transaction::default(), "Just because".into()); + let status7 = LocalTransactionStatus::Replaced(Transaction::default(), 5.into(), 10.into()); - assert_eq!( - serde_json::to_string(&status1).unwrap(), - r#"{"status":"pending"}"# - ); - assert_eq!( - serde_json::to_string(&status2).unwrap(), - r#"{"status":"future"}"# - ); - assert_eq!( - serde_json::to_string(&status3).unwrap(), - r#"{"status":"mined","transaction":"#.to_owned() + &format!("{}", tx_ser) + r#"}"# - ); - assert_eq!( - serde_json::to_string(&status4).unwrap(), - r#"{"status":"dropped","transaction":"#.to_owned() + &format!("{}", tx_ser) + r#"}"# - ); - assert_eq!( - serde_json::to_string(&status5).unwrap(), - r#"{"status":"invalid","transaction":"#.to_owned() + &format!("{}", tx_ser) + r#"}"# - ); - assert_eq!( - serde_json::to_string(&status6).unwrap(), - r#"{"status":"rejected","transaction":"#.to_owned() + - &format!("{}", tx_ser) + - r#","error":"Just because"}"# - ); - assert_eq!( - serde_json::to_string(&status7).unwrap(), - r#"{"status":"replaced","transaction":"#.to_owned() + - &format!("{}", tx_ser) + - r#","hash":"0x000000000000000000000000000000000000000000000000000000000000000a","gasPrice":"0x5"}"# - ); - } + assert_eq!( + serde_json::to_string(&status1).unwrap(), + r#"{"status":"pending"}"# + ); + assert_eq!( + serde_json::to_string(&status2).unwrap(), + r#"{"status":"future"}"# + ); + assert_eq!( + serde_json::to_string(&status3).unwrap(), + r#"{"status":"mined","transaction":"#.to_owned() + &format!("{}", tx_ser) + r#"}"# + ); + assert_eq!( + serde_json::to_string(&status4).unwrap(), + r#"{"status":"dropped","transaction":"#.to_owned() + &format!("{}", tx_ser) + r#"}"# + ); + assert_eq!( + serde_json::to_string(&status5).unwrap(), + r#"{"status":"invalid","transaction":"#.to_owned() + &format!("{}", tx_ser) + r#"}"# + ); + assert_eq!( + serde_json::to_string(&status6).unwrap(), + r#"{"status":"rejected","transaction":"#.to_owned() + + &format!("{}", tx_ser) + + r#","error":"Just because"}"# + ); + assert_eq!( + serde_json::to_string(&status7).unwrap(), + r#"{"status":"replaced","transaction":"#.to_owned() + + &format!("{}", tx_ser) + + r#","hash":"0x000000000000000000000000000000000000000000000000000000000000000a","gasPrice":"0x5"}"# + ); + } } diff --git a/rpc/src/v1/types/transaction_condition.rs b/rpc/src/v1/types/transaction_condition.rs index 589848fe549..55b1d613cea 100644 --- a/rpc/src/v1/types/transaction_condition.rs +++ b/rpc/src/v1/types/transaction_condition.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use types::transaction; @@ -20,47 +20,59 @@ use types::transaction; #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub enum TransactionCondition { - /// Valid at this minimum block number. - #[serde(rename = "block")] - Number(u64), - /// Valid at given unix time. - #[serde(rename = "time")] - Timestamp(u64), + /// Valid at this minimum block number. + #[serde(rename = "block")] + Number(u64), + /// Valid at given unix time. + #[serde(rename = "time")] + Timestamp(u64), } impl Into for TransactionCondition { - fn into(self) -> transaction::Condition { - match self { - TransactionCondition::Number(n) => transaction::Condition::Number(n), - TransactionCondition::Timestamp(n) => transaction::Condition::Timestamp(n), - } - } + fn into(self) -> transaction::Condition { + match self { + TransactionCondition::Number(n) => transaction::Condition::Number(n), + TransactionCondition::Timestamp(n) => transaction::Condition::Timestamp(n), + } + } } impl From for TransactionCondition { - fn from(condition: transaction::Condition) -> Self { - match condition { - transaction::Condition::Number(n) => TransactionCondition::Number(n), - transaction::Condition::Timestamp(n) => TransactionCondition::Timestamp(n), - } - } + fn from(condition: transaction::Condition) -> Self { + match condition { + transaction::Condition::Number(n) => TransactionCondition::Number(n), + transaction::Condition::Timestamp(n) => TransactionCondition::Timestamp(n), + } + } } #[cfg(test)] mod tests { - use super::*; - use serde_json; + use super::*; + use serde_json; - #[test] - fn condition_deserialization() { - let s = r#"[{ "block": 51 }, { "time": 10 }]"#; - let deserialized: Vec = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, vec![TransactionCondition::Number(51), TransactionCondition::Timestamp(10)]) - } + #[test] + fn condition_deserialization() { + let s = r#"[{ "block": 51 }, { "time": 10 }]"#; + let deserialized: Vec = serde_json::from_str(s).unwrap(); + assert_eq!( + deserialized, + vec![ + TransactionCondition::Number(51), + TransactionCondition::Timestamp(10) + ] + ) + } - #[test] - fn condition_into() { - assert_eq!(transaction::Condition::Number(100), TransactionCondition::Number(100).into()); - assert_eq!(transaction::Condition::Timestamp(100), TransactionCondition::Timestamp(100).into()); - } + #[test] + fn condition_into() { + assert_eq!( + transaction::Condition::Number(100), + TransactionCondition::Number(100).into() + ); + assert_eq!( + transaction::Condition::Timestamp(100), + TransactionCondition::Timestamp(100).into() + ); + } } diff --git a/rpc/src/v1/types/transaction_request.rs b/rpc/src/v1/types/transaction_request.rs index 944ee111484..8b896dc3c90 100644 --- a/rpc/src/v1/types/transaction_request.rs +++ b/rpc/src/v1/types/transaction_request.rs @@ -1,25 +1,27 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . //! `TransactionRequest` type -use ethereum_types::{H160, U256}; -use v1::types::{Bytes, TransactionCondition}; -use v1::helpers; use ansi_term::Colour; +use ethereum_types::{H160, U256}; +use v1::{ + helpers, + types::{Bytes, TransactionCondition}, +}; use std::fmt; @@ -28,123 +30,127 @@ use std::fmt; #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub struct TransactionRequest { - /// Sender - pub from: Option, - /// Recipient - pub to: Option, - /// Gas Price - pub gas_price: Option, - /// Gas - pub gas: Option, - /// Value of transaction in wei - pub value: Option, - /// Additional data sent with transaction - pub data: Option, - /// Transaction's nonce - pub nonce: Option, - /// Delay until this block condition. - pub condition: Option, + /// Sender + pub from: Option, + /// Recipient + pub to: Option, + /// Gas Price + pub gas_price: Option, + /// Gas + pub gas: Option, + /// Value of transaction in wei + pub value: Option, + /// Additional data sent with transaction + pub data: Option, + /// Transaction's nonce + pub nonce: Option, + /// Delay until this block condition. + pub condition: Option, } pub fn format_ether(i: U256) -> String { - let mut string = format!("{}", i); - let idx = string.len() as isize - 18; - if idx <= 0 { - let mut prefix = String::from("0."); - for _ in 0..idx.abs() { - prefix.push('0'); - } - string = prefix + &string; - } else { - string.insert(idx as usize, '.'); - } - String::from(string.trim_end_matches('0').trim_end_matches('.')) + let mut string = format!("{}", i); + let idx = string.len() as isize - 18; + if idx <= 0 { + let mut prefix = String::from("0."); + for _ in 0..idx.abs() { + prefix.push('0'); + } + string = prefix + &string; + } else { + string.insert(idx as usize, '.'); + } + String::from(string.trim_end_matches('0').trim_end_matches('.')) } impl fmt::Display for TransactionRequest { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let eth = self.value.unwrap_or_default(); - match self.to { - Some(ref to) => write!( - f, - "{} ETH from {} to 0x{:?}", - Colour::White.bold().paint(format_ether(eth)), - Colour::White.bold().paint( - self.from.as_ref() - .map(|f| format!("0x{:?}", f)) - .unwrap_or_else(|| "?".to_string())), - to - ), - None => write!( - f, - "{} ETH from {} for contract creation", - Colour::White.bold().paint(format_ether(eth)), - Colour::White.bold().paint( - self.from.as_ref() - .map(|f| format!("0x{:?}", f)) - .unwrap_or_else(|| "?".to_string())), - ), - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let eth = self.value.unwrap_or_default(); + match self.to { + Some(ref to) => write!( + f, + "{} ETH from {} to 0x{:?}", + Colour::White.bold().paint(format_ether(eth)), + Colour::White.bold().paint( + self.from + .as_ref() + .map(|f| format!("0x{:?}", f)) + .unwrap_or_else(|| "?".to_string()) + ), + to + ), + None => write!( + f, + "{} ETH from {} for contract creation", + Colour::White.bold().paint(format_ether(eth)), + Colour::White.bold().paint( + self.from + .as_ref() + .map(|f| format!("0x{:?}", f)) + .unwrap_or_else(|| "?".to_string()) + ), + ), + } + } } impl From for TransactionRequest { - fn from(r: helpers::TransactionRequest) -> Self { - TransactionRequest { - from: r.from.map(Into::into), - to: r.to.map(Into::into), - gas_price: r.gas_price.map(Into::into), - gas: r.gas.map(Into::into), - value: r.value.map(Into::into), - data: r.data.map(Into::into), - nonce: r.nonce.map(Into::into), - condition: r.condition.map(Into::into), - } - } + fn from(r: helpers::TransactionRequest) -> Self { + TransactionRequest { + from: r.from.map(Into::into), + to: r.to.map(Into::into), + gas_price: r.gas_price.map(Into::into), + gas: r.gas.map(Into::into), + value: r.value.map(Into::into), + data: r.data.map(Into::into), + nonce: r.nonce.map(Into::into), + condition: r.condition.map(Into::into), + } + } } impl From for TransactionRequest { - fn from(r: helpers::FilledTransactionRequest) -> Self { - TransactionRequest { - from: Some(r.from), - to: r.to, - gas_price: Some(r.gas_price), - gas: Some(r.gas), - value: Some(r.value), - data: Some(r.data.into()), - nonce: r.nonce, - condition: r.condition, - } - } + fn from(r: helpers::FilledTransactionRequest) -> Self { + TransactionRequest { + from: Some(r.from), + to: r.to, + gas_price: Some(r.gas_price), + gas: Some(r.gas), + value: Some(r.value), + data: Some(r.data.into()), + nonce: r.nonce, + condition: r.condition, + } + } } impl Into for TransactionRequest { - fn into(self) -> helpers::TransactionRequest { - helpers::TransactionRequest { - from: self.from.map(Into::into), - to: self.to.map(Into::into), - gas_price: self.gas_price.map(Into::into), - gas: self.gas.map(Into::into), - value: self.value.map(Into::into), - data: self.data.map(Into::into), - nonce: self.nonce.map(Into::into), - condition: self.condition.map(Into::into), - } - } + fn into(self) -> helpers::TransactionRequest { + helpers::TransactionRequest { + from: self.from.map(Into::into), + to: self.to.map(Into::into), + gas_price: self.gas_price.map(Into::into), + gas: self.gas.map(Into::into), + value: self.value.map(Into::into), + data: self.data.map(Into::into), + nonce: self.nonce.map(Into::into), + condition: self.condition.map(Into::into), + } + } } #[cfg(test)] mod tests { - use std::str::FromStr; - use rustc_hex::FromHex; - use serde_json; - use v1::types::TransactionCondition; - use ethereum_types::{H160, U256}; - use super::*; + use super::*; + use ethereum_types::{H160, U256}; + use rustc_hex::FromHex; + use serde_json; + use std::str::FromStr; + use v1::types::TransactionCondition; - #[test] - fn transaction_request_deserialize() { - let s = r#"{ + #[test] + fn transaction_request_deserialize() { + let s = r#"{ "from":"0x0000000000000000000000000000000000000001", "to":"0x0000000000000000000000000000000000000002", "gasPrice":"0x1", @@ -154,23 +160,26 @@ mod tests { "nonce":"0x4", "condition": { "block": 19 } }"#; - let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); + let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, TransactionRequest { - from: Some(H160::from(1)), - to: Some(H160::from(2)), - gas_price: Some(U256::from(1)), - gas: Some(U256::from(2)), - value: Some(U256::from(3)), - data: Some(vec![0x12, 0x34, 0x56].into()), - nonce: Some(U256::from(4)), - condition: Some(TransactionCondition::Number(0x13)), - }); - } + assert_eq!( + deserialized, + TransactionRequest { + from: Some(H160::from(1)), + to: Some(H160::from(2)), + gas_price: Some(U256::from(1)), + gas: Some(U256::from(2)), + value: Some(U256::from(3)), + data: Some(vec![0x12, 0x34, 0x56].into()), + nonce: Some(U256::from(4)), + condition: Some(TransactionCondition::Number(0x13)), + } + ); + } - #[test] - fn transaction_request_deserialize2() { - let s = r#"{ + #[test] + fn transaction_request_deserialize2() { + let s = r#"{ "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "gas": "0x76c0", @@ -178,9 +187,9 @@ mod tests { "value": "0x9184e72a", "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" }"#; - let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); + let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, TransactionRequest { + assert_eq!(deserialized, TransactionRequest { from: Some(H160::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap()), to: Some(H160::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), gas_price: Some(U256::from_str("9184e72a000").unwrap()), @@ -190,28 +199,31 @@ mod tests { nonce: None, condition: None, }); - } + } - #[test] - fn transaction_request_deserialize_empty() { - let s = r#"{"from":"0x0000000000000000000000000000000000000001"}"#; - let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); + #[test] + fn transaction_request_deserialize_empty() { + let s = r#"{"from":"0x0000000000000000000000000000000000000001"}"#; + let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, TransactionRequest { - from: Some(H160::from(1).into()), - to: None, - gas_price: None, - gas: None, - value: None, - data: None, - nonce: None, - condition: None, - }); - } + assert_eq!( + deserialized, + TransactionRequest { + from: Some(H160::from(1).into()), + to: None, + gas_price: None, + gas: None, + value: None, + data: None, + nonce: None, + condition: None, + } + ); + } - #[test] - fn transaction_request_deserialize_test() { - let s = r#"{ + #[test] + fn transaction_request_deserialize_test() { + let s = r#"{ "from":"0xb5f7502a2807cb23615c7456055e1d65b2508625", "to":"0x895d32f2db7d01ebb50053f9e48aacf26584fe40", "data":"0x8595bab1", @@ -219,23 +231,26 @@ mod tests { "gasPrice":"0x0ba43b7400" }"#; - let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); + let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, TransactionRequest { - from: Some(H160::from_str("b5f7502a2807cb23615c7456055e1d65b2508625").unwrap()), - to: Some(H160::from_str("895d32f2db7d01ebb50053f9e48aacf26584fe40").unwrap()), - gas_price: Some(U256::from_str("0ba43b7400").unwrap()), - gas: Some(U256::from_str("2fd618").unwrap()), - value: None, - data: Some(vec![0x85, 0x95, 0xba, 0xb1].into()), - nonce: None, - condition: None, - }); - } + assert_eq!( + deserialized, + TransactionRequest { + from: Some(H160::from_str("b5f7502a2807cb23615c7456055e1d65b2508625").unwrap()), + to: Some(H160::from_str("895d32f2db7d01ebb50053f9e48aacf26584fe40").unwrap()), + gas_price: Some(U256::from_str("0ba43b7400").unwrap()), + gas: Some(U256::from_str("2fd618").unwrap()), + value: None, + data: Some(vec![0x85, 0x95, 0xba, 0xb1].into()), + nonce: None, + condition: None, + } + ); + } - #[test] - fn transaction_request_deserialize_error() { - let s = r#"{ + #[test] + fn transaction_request_deserialize_error() { + let s = r#"{ "from":"0xb5f7502a2807cb23615c7456055e1d65b2508625", "to":"", "data":"0x8595bab1", @@ -243,19 +258,19 @@ mod tests { "gasPrice":"0x0ba43b7400" }"#; - let deserialized = serde_json::from_str::(s); + let deserialized = serde_json::from_str::(s); - assert!(deserialized.is_err(), "Should be error because to is empty"); - } + assert!(deserialized.is_err(), "Should be error because to is empty"); + } - #[test] - fn test_format_ether() { - assert_eq!(&format_ether(U256::from(1000000000000000000u64)), "1"); - assert_eq!(&format_ether(U256::from(500000000000000000u64)), "0.5"); - assert_eq!(&format_ether(U256::from(50000000000000000u64)), "0.05"); - assert_eq!(&format_ether(U256::from(5000000000000000u64)), "0.005"); - assert_eq!(&format_ether(U256::from(2000000000000000000u64)), "2"); - assert_eq!(&format_ether(U256::from(2500000000000000000u64)), "2.5"); - assert_eq!(&format_ether(U256::from(10000000000000000000u64)), "10"); - } + #[test] + fn test_format_ether() { + assert_eq!(&format_ether(U256::from(1000000000000000000u64)), "1"); + assert_eq!(&format_ether(U256::from(500000000000000000u64)), "0.5"); + assert_eq!(&format_ether(U256::from(50000000000000000u64)), "0.05"); + assert_eq!(&format_ether(U256::from(5000000000000000u64)), "0.005"); + assert_eq!(&format_ether(U256::from(2000000000000000000u64)), "2"); + assert_eq!(&format_ether(U256::from(2500000000000000000u64)), "2.5"); + assert_eq!(&format_ether(U256::from(10000000000000000000u64)), "10"); + } } diff --git a/rpc/src/v1/types/work.rs b/rpc/src/v1/types/work.rs index ed6c7c8e917..7425fa3b9c1 100644 --- a/rpc/src/v1/types/work.rs +++ b/rpc/src/v1/types/work.rs @@ -1,18 +1,18 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of OpenEthereum. -// Parity Ethereum is free software: you can redistribute it and/or modify +// OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Parity Ethereum is distributed in the hope that it will be useful, +// OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . +// along with OpenEthereum. If not, see . use ethereum_types::{H256, U256}; @@ -22,21 +22,30 @@ use serde::{Serialize, Serializer}; /// whether to send the block number. #[derive(Debug, PartialEq, Eq)] pub struct Work { - /// The proof-of-work hash. - pub pow_hash: H256, - /// The seed hash. - pub seed_hash: H256, - /// The target. - pub target: H256, - /// The block number: this isn't always stored. - pub number: Option, + /// The proof-of-work hash. + pub pow_hash: H256, + /// The seed hash. + pub seed_hash: H256, + /// The target. + pub target: H256, + /// The block number: this isn't always stored. + pub number: Option, } impl Serialize for Work { - fn serialize(&self, s: S) -> Result where S: Serializer { - match self.number.as_ref() { - Some(num) => (&self.pow_hash, &self.seed_hash, &self.target, U256::from(*num)).serialize(s), - None => (&self.pow_hash, &self.seed_hash, &self.target).serialize(s), - } - } + fn serialize(&self, s: S) -> Result + where + S: Serializer, + { + match self.number.as_ref() { + Some(num) => ( + &self.pow_hash, + &self.seed_hash, + &self.target, + U256::from(*num), + ) + .serialize(s), + None => (&self.pow_hash, &self.seed_hash, &self.target).serialize(s), + } + } } diff --git a/rustfmt.toml b/rustfmt.toml deleted file mode 100644 index 16016b1b2af..00000000000 --- a/rustfmt.toml +++ /dev/null @@ -1,16 +0,0 @@ -verbose=false -max_width=100 -comment_width=100 -tab_spaces=4 -fn_call_width=100 -struct_lit_width=32 -fn_call_style="Visual" -single_line_if_else_max_width=100 -trailing_comma="Vertical" -chain_indent="Visual" -chain_one_line_max=100 -reorder_imports=true -format_strings=false -hard_tabs=true -wrap_match_arms=false -error_on_line_overflow=false \ No newline at end of file diff --git a/scripts/actions/build-linux.sh b/scripts/actions/build-linux.sh new file mode 100755 index 00000000000..dcfc2e9ae73 --- /dev/null +++ b/scripts/actions/build-linux.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -e # fail on any error +set -u # treat unset variables as error +#strip ON +export RUSTFLAGS=" -Clink-arg=-s -Ctarget-feature=+aes,+sse2,+ssse3" + +echo "_____ Build OpenEthereum and tools _____" + +time cargo build --verbose --color=always --release --features final +time cargo build --verbose --color=always --release -p evmbin +time cargo build --verbose --color=always --release -p ethstore-cli +time cargo build --verbose --color=always --release -p ethkey-cli + +echo "_____ Post-processing binaries _____" +rm -rf artifacts/* +mkdir -p artifacts/ + +cp -v target/release/openethereum artifacts/openethereum +cp -v target/release/openethereum-evm artifacts/openethereum-evm +cp -v target/release/ethstore artifacts/ethstore +cp -v target/release/ethkey artifacts/ethkey diff --git a/scripts/actions/build-windows.sh b/scripts/actions/build-windows.sh new file mode 100755 index 00000000000..948e7f85e58 --- /dev/null +++ b/scripts/actions/build-windows.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -e # fail on any error +set -u # treat unset variables as error +# NOTE: Enables the aes-ni instructions for RustCrypto dependency. +# If you change this please remember to also update .cargo/config +export RUSTFLAGS=" -Ctarget-feature=+aes,+sse2,+ssse3 -Ctarget-feature=+crt-static -Clink-arg=-s" + +echo "_____ Build Parity and tools _____" +time cargo build --verbose --release --features final +time cargo build --verbose --release -p evmbin +time cargo build --verbose --release -p ethstore-cli +time cargo build --verbose --release -p ethkey-cli + +echo "_____ Post-processing binaries _____" +rm -rf artifacts +mkdir -p artifacts + +cp --verbose target/release/openethereum.exe artifacts/openethereum.exe +cp --verbose target/release/openethereum-evm.exe artifacts/openethereum-evm.exe +cp --verbose target/release/ethstore.exe artifacts/ethstore.exe +cp --verbose target/release/ethkey.exe artifacts/ethkey.exe diff --git a/scripts/actions/clean-target.sh b/scripts/actions/clean-target.sh new file mode 100755 index 00000000000..a1cbb4f4f34 --- /dev/null +++ b/scripts/actions/clean-target.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -e # fail on any error +set -u # treat unset variables as error + +find ./target/release -maxdepth 1 -type f -delete; +rm -fr ./target/release/{deps,.fingerprint}/*{openethereum,ethcore,ethkey,ethstore,openethereum-evm}*; +rm -f ./target/.rustc_info.json; diff --git a/scripts/actions/install-sccache.ps1 b/scripts/actions/install-sccache.ps1 new file mode 100755 index 00000000000..b170f473dac --- /dev/null +++ b/scripts/actions/install-sccache.ps1 @@ -0,0 +1,19 @@ +#!/usr/bin/env pwsh +$os=$args[0] +$version="0.2.12" +echo "Current OS:" $os +switch ($os){ + "macOS" {$platform = "x86_64-apple-darwin"} + "Linux" {$platform = "x86_64-unknown-linux-musl"} + "Windows" {$platform ="x86_64-pc-windows-msvc"} +} +echo "Target arch: " $platform +$basename = "sccache-$version-$platform" +$url = "https://github.com/mozilla/sccache/releases/download/"+"$version/$basename.tar.gz" +echo "Download sccache from "+$url +curl -LO $url +tar -xzvf "$basename.tar.gz" +ls $basename/ +. $basename/sccache --start-server +echo "::add-path::$(pwd)/$basename" +echo "::set-env name=RUSTC_WRAPPER::sccache" diff --git a/scripts/gitlab/validate-chainspecs.sh b/scripts/actions/validate-chainspecs.sh similarity index 78% rename from scripts/gitlab/validate-chainspecs.sh rename to scripts/actions/validate-chainspecs.sh index 9b7ef39e727..cf5c126f7b3 100755 --- a/scripts/gitlab/validate-chainspecs.sh +++ b/scripts/actions/validate-chainspecs.sh @@ -6,7 +6,7 @@ echo "________Running validate_chainspecs.sh________" ERR=0 echo "________Validate chainspecs________" -time cargo build --release -p chainspec +time cargo build --release -p chainspec --verbose --color=always for spec in ethcore/res/*.json; do if ! ./target/release/chainspec "$spec"; then ERR=1; fi @@ -15,5 +15,6 @@ done for spec in ethcore/res/ethereum/*.json; do if ! ./target/release/chainspec "$spec"; then ERR=1; fi done - +#show sccache statistics +sccache --stop-server exit $ERR diff --git a/scripts/doc.sh b/scripts/doc.sh index 44e544c993e..e005217cf44 100755 --- a/scripts/doc.sh +++ b/scripts/doc.sh @@ -1,5 +1,5 @@ #!/usr/bin/env sh -# generate documentation only for partiy and ethcore libraries +# generate documentation only for openethereum and ethcore libraries -cargo doc --no-deps --verbose --all --exclude parity-ipfs-api && +cargo doc --no-deps --verbose --all && echo '' > target/doc/index.html diff --git a/scripts/docker/README.md b/scripts/docker/README.md index 86b2fcb6aad..390abc7f85a 100644 --- a/scripts/docker/README.md +++ b/scripts/docker/README.md @@ -1,40 +1,40 @@ ## Usage -```docker build -f docker/ubuntu/Dockerfile --tag ethcore/parity:branch_or_tag_name .``` +```docker build -f docker/ubuntu/Dockerfile --tag ethcore/openethereum:branch_or_tag_name .``` ## Usage - CentOS -Builds a lightweight non-root Parity docker image: +Builds a lightweight non-root OpenEthereum docker image: ``` -git clone https://github.com/paritytech/parity-ethereum.git -cd parity-ethereum -./docker/centos/build.sh +git clone https://github.com/openethereum/openethereum.git +cd openethereum +./scripts/docker/centos/build.sh ``` Fully customised build: ``` -PARITY_IMAGE_REPO=my-personal/parity \ -PARITY_BUILDER_IMAGE_TAG=build-latest \ -PARITY_RUNNER_IMAGE_TAG=centos-parity-experimental \ -./docker/centos/build.sh +OPENETHEREUM_IMAGE_REPO=my-personal/openethereum \ +OPENETHEREUM_BUILDER_IMAGE_TAG=build-latest \ +OPENETHEREUM_RUNNER_IMAGE_TAG=centos-openethereum-experimental \ +./scripts/docker/centos/build.sh ``` Default values: ``` # The image name -PARITY_IMAGE_REPO - parity/parity +OPENETHEREUM_IMAGE_REPO - openethereum/openethereum # The tag to be used for builder image, git commit sha will be appended -PARITY_BUILDER_IMAGE_TAG - build +OPENETHEREUM_BUILDER_IMAGE_TAG - build # The tag to be used for runner image -PARITY_RUNNER_IMAGE_TAG - latest +OPENETHEREUM_RUNNER_IMAGE_TAG - latest ``` All default ports you might use will be exposed: ``` -# secret -# ipfs store ui rpc ws listener discovery -# ↓ ↓ ↓ ↓ ↓ ↓ ↓ -EXPOSE 5001 8082 8083 8180 8545 8546 30303/tcp 30303/udp +# secret +# store ui rpc ws listener discovery +# ↓ ↓ ↓ ↓ ↓ ↓ ↓ +EXPOSE 8082 8083 8180 8545 8546 30303/tcp 30303/udp ``` diff --git a/scripts/docker/alpine/Dockerfile b/scripts/docker/alpine/Dockerfile index 47b37372ebf..800a092caf7 100644 --- a/scripts/docker/alpine/Dockerfile +++ b/scripts/docker/alpine/Dockerfile @@ -10,34 +10,37 @@ RUN apk add --no-cache \ eudev-dev \ linux-headers \ perl \ - rust - -WORKDIR /parity -COPY . /parity -RUN cargo build --release --target x86_64-alpine-linux-musl --verbose -RUN strip target/x86_64-alpine-linux-musl/release/parity + rust \ + git +WORKDIR /openethereum +COPY . /openethereum +RUN cargo build --release --features final --target x86_64-alpine-linux-musl --verbose +RUN strip target/x86_64-alpine-linux-musl/release/openethereum FROM alpine:edge # show backtraces ENV RUST_BACKTRACE 1 +# curl and jq are installed to help create health and readiness checks on Kubernetes RUN apk add --no-cache \ libstdc++ \ eudev-libs \ - libgcc + libgcc \ + curl \ + jq -RUN addgroup -g 1000 parity \ - && adduser -u 1000 -G parity -s /bin/sh -D parity +RUN addgroup -g 1000 openethereum \ + && adduser -u 1000 -G openethereum -s /bin/sh -D openethereum -USER parity +USER openethereum EXPOSE 8080 8545 8180 -WORKDIR /home/parity +WORKDIR /home/openethereum -RUN mkdir -p /home/parity/.local/share/io.parity.ethereum/ -COPY --chown=parity:parity --from=builder /parity/target/x86_64-alpine-linux-musl/release/parity ./ +RUN mkdir -p /home/openethereum/.local/share/io.parity.ethereum/ +COPY --chown=openethereum:openethereum --from=builder /openethereum/target/x86_64-alpine-linux-musl/release/openethereum ./ -ENTRYPOINT ["./parity"] +ENTRYPOINT ["/home/openethereum/openethereum"] diff --git a/scripts/docker/centos/Dockerfile b/scripts/docker/centos/Dockerfile index 22a98c003ae..824bdfe7035 100644 --- a/scripts/docker/centos/Dockerfile +++ b/scripts/docker/centos/Dockerfile @@ -1,28 +1,23 @@ FROM centos:latest -RUN mkdir -p /opt/parity/data && \ - chmod g+rwX /opt/parity/data && \ - mkdir -p /opt/parity/release +RUN mkdir -p /opt/openethereum/data && \ + chmod g+rwX /opt/openethereum/data && \ + mkdir -p /opt/openethereum/release -COPY parity/parity /opt/parity/release +COPY openethereum/openethereum /opt/openethereum/release -WORKDIR /opt/parity/data +WORKDIR /opt/openethereum/data # exposing default ports # -# secret -# ipfs store ui rpc ws listener discovery -# ↓ ↓ ↓ ↓ ↓ ↓ ↓ -EXPOSE 5001 8082 8083 8180 8545 8546 30303/tcp 30303/udp +# secret +# store ui rpc ws listener discovery +# ↓ ↓ ↓ ↓ ↓ ↓ +EXPOSE 8082 8083 8180 8545 8546 30303/tcp 30303/udp # switch to non-root user USER 1001 #if no base path provided, assume it's current workdir CMD ["--base-path","."] -ENTRYPOINT ["/opt/parity/release/parity"] - - - - - +ENTRYPOINT ["/opt/openethereum/release/openethereum"] diff --git a/scripts/docker/centos/Dockerfile.build b/scripts/docker/centos/Dockerfile.build index 454af403a03..1d275fc1d80 100644 --- a/scripts/docker/centos/Dockerfile.build +++ b/scripts/docker/centos/Dockerfile.build @@ -2,7 +2,7 @@ FROM centos:latest WORKDIR /build -ADD . /build/parity-ethereum +ADD . /build/openethereum RUN yum -y update && \ yum install -y systemd-devel git make gcc-c++ gcc file binutils && \ @@ -17,9 +17,9 @@ RUN yum -y update && \ gcc -v && \ g++ -v && \ cmake --version && \ - cd parity-ethereum && \ + cd openethereum && \ cargo build --verbose --release --features final && \ - strip /build/parity-ethereum/target/release/parity && \ - file /build/parity-ethereum/target/release/parity + strip /build/openethereum/target/release/openethereum && \ + file /build/openethereum/target/release/openethereum diff --git a/scripts/docker/centos/build.sh b/scripts/docker/centos/build.sh index 7215e745f0e..4ce972b56d9 100755 --- a/scripts/docker/centos/build.sh +++ b/scripts/docker/centos/build.sh @@ -1,29 +1,29 @@ #!/usr/bin/env sh # The image name -PARITY_IMAGE_REPO=${PARITY_IMAGE_REPO:-parity/parity} +OPENETHEREUM_IMAGE_REPO=${OPENETHEREUM_IMAGE_REPO:-openethereum/openethereum} # The tag to be used for builder image -PARITY_BUILDER_IMAGE_TAG=${PARITY_BUILDER_IMAGE_TAG:-build} +OPENETHEREUM_BUILDER_IMAGE_TAG=${OPENETHEREUM_BUILDER_IMAGE_TAG:-build} # The tag to be used for runner image -PARITY_RUNNER_IMAGE_TAG=${PARITY_RUNNER_IMAGE_TAG:-latest} +OPENETHEREUM_RUNNER_IMAGE_TAG=${OPENETHEREUM_RUNNER_IMAGE_TAG:-latest} -echo Building $PARITY_IMAGE_REPO:$PARITY_BUILDER_IMAGE_TAG-$(git log -1 --format="%H") -docker build --no-cache -t $PARITY_IMAGE_REPO:$PARITY_BUILDER_IMAGE_TAG-$(git log -1 --format="%H") . -f docker/centos/Dockerfile.build +echo Building $OPENETHEREUM_IMAGE_REPO:$OPENETHEREUM_BUILDER_IMAGE_TAG-$(git log -1 --format="%H") +docker build --no-cache -t $OPENETHEREUM_IMAGE_REPO:$OPENETHEREUM_BUILDER_IMAGE_TAG-$(git log -1 --format="%H") . -f scripts/docker/centos/Dockerfile.build -echo Creating $PARITY_BUILDER_IMAGE_TAG-$(git log -1 --format="%H"), extracting binary -docker create --name extract $PARITY_IMAGE_REPO:$PARITY_BUILDER_IMAGE_TAG-$(git log -1 --format="%H") -mkdir docker/centos/parity -docker cp extract:/build/parity-ethereum/target/release/parity docker/centos/parity +echo Creating $OPENETHEREUM_BUILDER_IMAGE_TAG-$(git log -1 --format="%H"), extracting binary +docker create --name extract $OPENETHEREUM_IMAGE_REPO:$OPENETHEREUM_BUILDER_IMAGE_TAG-$(git log -1 --format="%H") +mkdir scripts/docker/centos/openethereum +docker cp extract:/build/openethereum/target/release/openethereum scripts/docker/centos/openethereum -echo Building $PARITY_IMAGE_REPO:$PARITY_RUNNER_IMAGE_TAG -docker build --no-cache -t $PARITY_IMAGE_REPO:$PARITY_RUNNER_IMAGE_TAG docker/centos/ -f docker/centos/Dockerfile +echo Building $OPENETHEREUM_IMAGE_REPO:$OPENETHEREUM_RUNNER_IMAGE_TAG +docker build --no-cache -t $OPENETHEREUM_IMAGE_REPO:$OPENETHEREUM_RUNNER_IMAGE_TAG scripts/docker/centos/ -f scripts/docker/centos/Dockerfile echo Cleaning up ... -rm -rf docker/centos/parity +rm -rf scripts/docker/centos/openethereum docker rm -f extract -docker rmi -f $PARITY_IMAGE_REPO:$PARITY_BUILDER_IMAGE_TAG-$(git log -1 --format="%H") +docker rmi -f $OPENETHEREUM_IMAGE_REPO:$OPENETHEREUM_BUILDER_IMAGE_TAG-$(git log -1 --format="%H") -echo Echoing Parity version: -docker run $PARITY_IMAGE_REPO:$PARITY_RUNNER_IMAGE_TAG --version +echo Echoing OpenEthereum version: +docker run $OPENETHEREUM_IMAGE_REPO:$OPENETHEREUM_RUNNER_IMAGE_TAG --version echo Done. diff --git a/scripts/docker/hub/Dockerfile b/scripts/docker/hub/Dockerfile index a18cacf9cc9..64cc9003b31 100644 --- a/scripts/docker/hub/Dockerfile +++ b/scripts/docker/hub/Dockerfile @@ -1,30 +1,48 @@ FROM ubuntu:xenial -LABEL MAINTAINER="Parity Technologies " -# install tools and dependencies -RUN apt update && apt install -y --no-install-recommends openssl libudev-dev file curl jq +# metadata +ARG VCS_REF +ARG BUILD_DATE + +LABEL openethereum.image.authors="devops-team@parity.io" \ + openethereum.image.vendor="OpenEthereum project" \ + openethereum.image.title="openethereum/openethereum" \ + openethereum.image.description="Fast and feature-rich multi-network Ethereum client." \ + openethereum.image.source="https://github.com/openethereum/openethereum/blob/${VCS_REF}/\ + scripts/docker/hub/Dockerfile" \ + openethereum.image.documentation="https://wiki.parity.io/Parity-Ethereum" \ + openethereum.image.revision="${VCS_REF}" \ + openethereum.image.created="${BUILD_DATE}" # show backtraces ENV RUST_BACKTRACE 1 -# cleanup Docker image -RUN apt autoremove -y \ - && apt clean -y \ - && rm -rf /tmp/* /var/tmp/* /var/lib/apt/lists/* - -RUN groupadd -g 1000 parity \ - && useradd -m -u 1000 -g parity -s /bin/sh parity - -WORKDIR /home/parity - -# add parity-ethereum to docker image -COPY artifacts/x86_64-unknown-linux-gnu/parity /bin/parity - -COPY scripts/docker/hub/check_sync.sh /check_sync.sh - -# switch to user parity here -USER parity +# install tools and dependencies +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + file curl jq ca-certificates; \ + # apt cleanup + apt-get autoremove -y; \ + apt-get clean; \ + update-ca-certificates; \ + rm -rf /tmp/* /var/tmp/* /var/lib/apt/lists/*; \ + # add user + groupadd -g 1000 openethereum; \ + useradd -m -u 1000 -g openethereum -s /bin/sh openethereum + +WORKDIR /home/openethereum + +# add openethereum binary to docker image +COPY artifacts/x86_64-unknown-linux-gnu/openethereum /bin/openethereum +COPY tools/check_sync.sh /check_sync.sh + +# switch to user openethereum here +USER openethereum + +# check if executable works in this container +RUN openethereum --version EXPOSE 5001 8080 8082 8083 8545 8546 8180 30303/tcp 30303/udp -ENTRYPOINT ["/bin/parity"] +ENTRYPOINT ["/bin/openethereum"] diff --git a/scripts/docker/hub/check_sync.sh b/scripts/docker/hub/check_sync.sh index 4640a05386a..bf492dd1133 100755 --- a/scripts/docker/hub/check_sync.sh +++ b/scripts/docker/hub/check_sync.sh @@ -1,13 +1,13 @@ #!/bin/bash -# checks if parity has a fully synced blockchain +# checks if OpenEthereum has a fully synced blockchain ETH_SYNCING=$(curl -X POST --data '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' http://localhost:8545 -H 'Content-Type: application/json') RESULT=$(echo "$ETH_SYNCING" | jq -r .result) if [ "$RESULT" == "false" ]; then - echo "Parity is ready to start accepting traffic" + echo "OpenEthereum is ready to start accepting traffic" exit 0 else - echo "Parity is still syncing the blockchain" + echo "OpenEthereum is still syncing the blockchain" exit 1 fi diff --git a/scripts/docker/hub/publish-docker.sh b/scripts/docker/hub/publish-docker.sh new file mode 100755 index 00000000000..09111548b17 --- /dev/null +++ b/scripts/docker/hub/publish-docker.sh @@ -0,0 +1,56 @@ +#!/bin/sh + +set -e # fail on any error + +VERSION=$(cat ./tools/VERSION) +TRACK=$(cat ./tools/TRACK) +echo "OpenEthereum version = ${VERSION}" +echo "OpenEthereum track = ${TRACK}" + +test "$Docker_Hub_User_OpenEthereum" -a "$Docker_Hub_Pass_OpenEthereum" \ + || ( echo "no docker credentials provided"; exit 1 ) +docker login -u "$Docker_Hub_User_OpenEthereum" -p "$Docker_Hub_Pass_OpenEthereum" +echo "__________Docker info__________" +docker info + +# we stopped pushing nightlies to dockerhub, will push to own registry prb. +case "${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}" in + "$SCHEDULE_TAG") + echo "Docker TAG - 'openethereum/openethereum:${SCHEDULE_TAG}'"; + docker build --no-cache \ + --build-arg VCS_REF="${CI_COMMIT_SHA}" \ + --build-arg BUILD_DATE="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \ + --tag "openethereum/openethereum:${SCHEDULE_TAG}" \ + --file tools/Dockerfile .; + docker push "openethereum/openethereum:${SCHEDULE_TAG}";; + "stable") + echo "Docker TAGs - 'openethereum/openethereum:${VERSION}-${CI_COMMIT_REF_NAME}', 'openethereum/openethereum:stable'"; + docker build --no-cache \ + --build-arg VCS_REF="${CI_COMMIT_SHA}" \ + --build-arg BUILD_DATE="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \ + --tag "openethereum/openethereum:${VERSION}-${CI_COMMIT_REF_NAME}" \ + --tag "openethereum/openethereum:latest" \ + --tag "openethereum/openethereum:stable" \ + --file tools/Dockerfile .; + docker push "openethereum/openethereum:${VERSION}-${CI_COMMIT_REF_NAME}"; + docker push "openethereum/openethereum:stable"; + docker push "openethereum/openethereum:latest";; + v[0-9]*.[0-9]*) + echo "Docker TAG - 'openethereum/openethereum:${VERSION}-${TRACK}'" + docker build --no-cache \ + --build-arg VCS_REF="${CI_COMMIT_SHA}" \ + --build-arg BUILD_DATE="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \ + --tag "openethereum/openethereum:${VERSION}-${TRACK}" \ + --file tools/Dockerfile .; + docker push "openethereum/openethereum:${VERSION}-${TRACK}";; + *) + echo "Docker TAG - 'openethereum/openethereum:${VERSION}-${CI_COMMIT_REF_NAME}'" + docker build --no-cache \ + --build-arg VCS_REF="${CI_COMMIT_SHA}" \ + --build-arg BUILD_DATE="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \ + --tag "openethereum/openethereum:${VERSION}-${CI_COMMIT_REF_NAME}" \ + --file tools/Dockerfile .; + docker push "openethereum/openethereum:${VERSION}-${CI_COMMIT_REF_NAME}";; +esac + +docker logout diff --git a/scripts/docker/ubuntu-aarch64/Dockerfile b/scripts/docker/ubuntu-aarch64/Dockerfile index 53eb325acfe..942e6ba68c2 100644 --- a/scripts/docker/ubuntu-aarch64/Dockerfile +++ b/scripts/docker/ubuntu-aarch64/Dockerfile @@ -19,11 +19,10 @@ RUN echo '# source urls for arm64 \n\ RUN apt-get -y update && \ apt-get upgrade -y && \ apt-get install -y --no-install-recommends \ - curl make cmake file ca-certificates \ - g++ gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \ - libc6-dev-arm64-cross binutils-aarch64-linux-gnu \ - libudev-dev libudev-dev:arm64 \ - && \ + curl make cmake file ca-certificates \ + g++ gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \ + libc6-dev-arm64-cross binutils-aarch64-linux-gnu \ + && \ apt-get clean # install rustup @@ -43,19 +42,19 @@ ENV RUST_BACKTRACE 1 # show tools RUN rustc -vV && cargo -V -# build parity -ADD . /build/parity -RUN cd parity && \ +# build OpenEthereum +ADD . /build/openethereum +RUN cd openethereum && \ mkdir -p .cargo && \ echo '[target.aarch64-unknown-linux-gnu]\n\ linker = "aarch64-linux-gnu-gcc"\n'\ >>.cargo/config && \ cat .cargo/config && \ cargo build --target aarch64-unknown-linux-gnu --release --verbose && \ - ls /build/parity/target/aarch64-unknown-linux-gnu/release/parity && \ - /usr/bin/aarch64-linux-gnu-strip /build/parity/target/aarch64-unknown-linux-gnu/release/parity + ls /build/openethereum/target/aarch64-unknown-linux-gnu/release/openethereum && \ + /usr/bin/aarch64-linux-gnu-strip /build/openethereum/target/aarch64-unknown-linux-gnu/release/openethereum -RUN file /build/parity/target/aarch64-unknown-linux-gnu/release/parity +RUN file /build/openethereum/target/aarch64-unknown-linux-gnu/release/openethereum EXPOSE 8080 8545 8180 -ENTRYPOINT ["/build/parity/target/aarch64-unknown-linux-gnu/release/parity"] +ENTRYPOINT ["/build/openethereum/target/aarch64-unknown-linux-gnu/release/openethereum"] diff --git a/scripts/docker/ubuntu-arm/Dockerfile b/scripts/docker/ubuntu-arm/Dockerfile index bbdc280d51f..360f7abd086 100644 --- a/scripts/docker/ubuntu-arm/Dockerfile +++ b/scripts/docker/ubuntu-arm/Dockerfile @@ -6,9 +6,9 @@ RUN apt-get -y update && \ apt-get install -y --force-yes --no-install-recommends \ curl git make g++ gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf \ libc6-dev-armhf-cross wget file ca-certificates \ - binutils-arm-linux-gnueabihf cmake3 libudev-dev \ + binutils-arm-linux-gnueabihf cmake3 \ && \ - apt-get clean + apt-get clean # install rustup RUN curl https://sh.rustup.rs -sSf | sh -s -- -y @@ -27,19 +27,19 @@ ENV RUST_BACKTRACE 1 # show tools RUN rustc -vV && cargo -V -# build parity -ADD . /build/parity -RUN cd parity && \ +# build OpenEthereum +ADD . /build/openethereum +RUN cd openethereum && \ mkdir -p .cargo && \ echo '[target.armv7-unknown-linux-gnueabihf]\n\ linker = "arm-linux-gnueabihf-gcc"\n'\ >>.cargo/config && \ cat .cargo/config && \ cargo build --target armv7-unknown-linux-gnueabihf --release --verbose && \ - ls /build/parity/target/armv7-unknown-linux-gnueabihf/release/parity && \ - /usr/bin/arm-linux-gnueabihf-strip /build/parity/target/armv7-unknown-linux-gnueabihf/release/parity + ls /build/openethereum/target/armv7-unknown-linux-gnueabihf/release/openethereum && \ + /usr/bin/arm-linux-gnueabihf-strip /build/openethereum/target/armv7-unknown-linux-gnueabihf/release/openethereum -RUN file /build/parity/target/armv7-unknown-linux-gnueabihf/release/parity +RUN file /build/openethereum/target/armv7-unknown-linux-gnueabihf/release/openethereum EXPOSE 8080 8545 8180 -ENTRYPOINT ["/build/parity/target/armv7-unknown-linux-gnueabihf/release/parity"] +ENTRYPOINT ["/build/openethereum/target/armv7-unknown-linux-gnueabihf/release/openethereum"] diff --git a/scripts/evm_jsontests_bench.sh b/scripts/evm_jsontests_bench.sh index acec902196d..e13ff9ef4a5 100755 --- a/scripts/evm_jsontests_bench.sh +++ b/scripts/evm_jsontests_bench.sh @@ -2,15 +2,15 @@ cargo build --release -p evmbin -./target/release/parity-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmArithmeticTest -./target/release/parity-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmBitwiseLogicOperation -./target/release/parity-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmBlockInfoTest -./target/release/parity-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmEnvironmentalInfo -./target/release/parity-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmIOandFlowOperations -./target/release/parity-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmLogTest -./target/release/parity-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmPerformance -./target/release/parity-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmPushDupSwapTest -./target/release/parity-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmRandomTest -./target/release/parity-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmSha3Test -./target/release/parity-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmSystemOperations -./target/release/parity-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmTests +./target/release/openethereum-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmArithmeticTest +./target/release/openethereum-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmBitwiseLogicOperation +./target/release/openethereum-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmBlockInfoTest +./target/release/openethereum-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmEnvironmentalInfo +./target/release/openethereum-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmIOandFlowOperations +./target/release/openethereum-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmLogTest +./target/release/openethereum-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmPerformance +./target/release/openethereum-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmPushDupSwapTest +./target/release/openethereum-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmRandomTest +./target/release/openethereum-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmSha3Test +./target/release/openethereum-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmSystemOperations +./target/release/openethereum-evm stats-jsontests-vm ./ethcore/res/ethereum/tests/VMTests/vmTests diff --git a/scripts/evm_uint_bench.sh b/scripts/evm_uint_bench.sh index b0ca1c4f7bc..30f313fbc01 100755 --- a/scripts/evm_uint_bench.sh +++ b/scripts/evm_uint_bench.sh @@ -8,9 +8,9 @@ if [ -x "$(command -v ethvm)" ]; then ethvm --code $CODE1 echo "^^^^ ethvm" fi -./target/release/parity-evm stats --code $CODE1 --gas 4402000 +./target/release/openethereum-evm stats --code $CODE1 --gas 4402000 echo "^^^^ usize" -./target/release/parity-evm stats --code $CODE1 +./target/release/openethereum-evm stats --code $CODE1 echo "^^^^ U256" # RNG TEST @@ -19,7 +19,7 @@ if [ -x "$(command -v ethvm)" ]; then ethvm --code $CODE2 echo "^^^^ ethvm" fi -./target/release/parity-evm stats --code $CODE2 --gas 143020115 +./target/release/openethereum-evm stats --code $CODE2 --gas 143020115 echo "^^^^ usize" -./target/release/parity-evm stats --code $CODE2 +./target/release/openethereum-evm stats --code $CODE2 echo "^^^^ U256" diff --git a/scripts/gitlab/build-linux.sh b/scripts/gitlab/build-linux.sh deleted file mode 100755 index f0697080cb8..00000000000 --- a/scripts/gitlab/build-linux.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash - -set -e # fail on any error -set -u # treat unset variables as error - -echo "__________Show ENVIROMENT__________" -echo "CI_SERVER_NAME: " $CI_SERVER_NAME -echo "CARGO_HOME: " $CARGO_HOME -echo "CARGO_TARGET: " $CARGO_TARGET -echo "CC: " $CC -echo "CXX: " $CXX -#strip ON -export RUSTFLAGS=" -C link-arg=-s" -# Linker for crosscomile -echo "_____ Linker _____" -cat .cargo/config - -echo "_____ Building target: "$CARGO_TARGET" _____" -if [ "${CARGO_TARGET}" = "armv7-linux-androideabi" ] -then - time cargo build --target $CARGO_TARGET --release -p parity-clib --features final -else - time cargo build --target $CARGO_TARGET --release --features final - time cargo build --target $CARGO_TARGET --release -p evmbin - time cargo build --target $CARGO_TARGET --release -p ethstore-cli - time cargo build --target $CARGO_TARGET --release -p ethkey-cli - time cargo build --target $CARGO_TARGET --release -p whisper-cli -fi - -echo "_____ Post-processing binaries _____" -rm -rf artifacts/* -mkdir -p artifacts/$CARGO_TARGET -cd artifacts/$CARGO_TARGET - -if [ "${CARGO_TARGET}" = "armv7-linux-androideabi" ] -then - cp -v ../../target/$CARGO_TARGET/release/libparity.so ./libparity.so -else - cp -v ../../target/$CARGO_TARGET/release/parity ./parity - cp -v ../../target/$CARGO_TARGET/release/parity-evm ./parity-evm - cp -v ../../target/$CARGO_TARGET/release/ethstore ./ethstore - cp -v ../../target/$CARGO_TARGET/release/ethkey ./ethkey - cp -v ../../target/$CARGO_TARGET/release/whisper ./whisper -fi - -echo "_____ Calculating checksums _____" -for binary in $(ls) -do - rhash --sha256 $binary -o $binary.sha256 #do we still need this hash (SHA2)? - if [[ $CARGO_TARGET == *"x86_64"* ]]; - then - ./parity tools hash $binary > $binary.sha3 - else - echo "> ${binary} cannot be hashed with cross-compiled binary (keccak256)" - fi -done diff --git a/scripts/gitlab/build-windows.sh b/scripts/gitlab/build-windows.sh deleted file mode 100755 index 7ddf4453e50..00000000000 --- a/scripts/gitlab/build-windows.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -set -e # fail on any error -set -u # treat unset variables as error - -set INCLUDE="C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include;C:\vs2015\VC\include;C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt" -set LIB="C:\vs2015\VC\lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10240.0\ucrt\x64" -sccache -s - -echo "__________Show ENVIROMENT__________" -echo "CI_SERVER_NAME: " $CI_SERVER_NAME -echo "CARGO_HOME: " $CARGO_HOME -echo "CARGO_TARGET: " $CARGO_TARGET -echo "RUSTC_WRAPPER: " $RUSTC_WRAPPER -echo "SCCACHE_DIR: " $SCCACHE_DIR - -echo "_____ Building target: "$CARGO_TARGET" _____" -time cargo build --target $CARGO_TARGET --release --features final -time cargo build --target $CARGO_TARGET --release -p evmbin -time cargo build --target $CARGO_TARGET --release -p ethstore-cli -time cargo build --target $CARGO_TARGET --release -p ethkey-cli -time cargo build --target $CARGO_TARGET --release -p whisper-cli - -echo "__________Sign binaries__________" -scripts/gitlab/sign-win.cmd $keyfile $certpass target/$CARGO_TARGET/release/parity.exe -scripts/gitlab/sign-win.cmd $keyfile $certpass target/$CARGO_TARGET/release/parity-evm.exe -scripts/gitlab/sign-win.cmd $keyfile $certpass target/$CARGO_TARGET/release/ethstore.exe -scripts/gitlab/sign-win.cmd $keyfile $certpass target/$CARGO_TARGET/release/ethkey.exe -scripts/gitlab/sign-win.cmd $keyfile $certpass target/$CARGO_TARGET/release/whisper.exe - -echo "_____ Post-processing binaries _____" -rm -rf artifacts -mkdir -p artifacts -cd artifacts -mkdir -p $CARGO_TARGET -cd $CARGO_TARGET -cp --verbose ../../target/$CARGO_TARGET/release/parity.exe ./parity.exe -cp --verbose ../../target/$CARGO_TARGET/release/parity-evm.exe ./parity-evm.exe -cp --verbose ../../target/$CARGO_TARGET/release/ethstore.exe ./ethstore.exe -cp --verbose ../../target/$CARGO_TARGET/release/ethkey.exe ./ethkey.exe -cp --verbose ../../target/$CARGO_TARGET/release/whisper.exe ./whisper.exe - -echo "_____ Calculating checksums _____" -for binary in $(ls) -do - rhash --sha256 $binary -o $binary.sha256 - ./parity.exe tools hash $binary > $binary.sha3 -done -cp parity.exe.sha256 parity.sha256 -cp parity.exe.sha3 parity.sha3 - -sccache -s diff --git a/scripts/gitlab/publish-docker.sh b/scripts/gitlab/publish-docker.sh deleted file mode 100755 index e8697fac2e0..00000000000 --- a/scripts/gitlab/publish-docker.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -##ARGUMENTS: 1. Docker target -set -e # fail on any error -set -u # treat unset variables as error - -if [ "$CI_COMMIT_REF_NAME" == "master" ]; - then export DOCKER_BUILD_TAG="${SCHEDULE_TAG:-latest}"; - else export DOCKER_BUILD_TAG=$CI_COMMIT_REF_NAME; -fi -docker login -u $Docker_Hub_User_Parity -p $Docker_Hub_Pass_Parity - -echo "__________Docker TAG__________" -echo $DOCKER_BUILD_TAG - -echo "__________Docker target__________" -export DOCKER_TARGET=$1 -echo $DOCKER_TARGET - -echo "__________Docker build and push__________" -docker build --build-arg TARGET=$DOCKER_TARGET --no-cache=true --tag parity/$DOCKER_TARGET:$DOCKER_BUILD_TAG -f scripts/docker/hub/Dockerfile . -docker push parity/$DOCKER_TARGET:$DOCKER_BUILD_TAG -docker logout diff --git a/scripts/gitlab/publish-docs.sh b/scripts/gitlab/publish-docs.sh deleted file mode 100755 index 262ea80807f..00000000000 --- a/scripts/gitlab/publish-docs.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash - -set -e # fail on any error -set -u # treat unset variables as error - -clone_repos() { - echo "__________Clone repos__________" - git clone https://github.com/parity-js/jsonrpc.git jsonrpc - git clone https://github.com/paritytech/wiki.git wiki - git clone https://github.com/paritytech/parity-config-generator -} - -build_docs() { - echo "__________Build docs__________" - npm install - npm run build:markdown -} - -build_config() { - echo "_______Build config docs______" - yarn install - AUTOGENSCRIPT=1 yarn generate-docs -} - -update_wiki_docs() { - echo "__________Update WIKI docs__________" - for file in $(ls jsonrpc/docs); do - module_name=${file:0:-3} - mv jsonrpc/docs/$file wiki/JSONRPC-$module_name-module.md - done - mv parity-config-generator/docs/config.md wiki/Configuring-Parity-Ethereum.md -} - -setup_git() { - echo "__________Set github__________" - git config --global user.email "devops@parity.com" - git config --global user.name "Devops Parity" -} - -set_remote_wiki() { - git config remote.origin.url "https://${GITHUB_TOKEN}@github.com/paritytech/wiki.git" -} - -commit_files() { - echo "__________Commit files__________" - git checkout -b rpcdoc-update-${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}} - git add . - git commit -m "Update docs to ${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}" - git tag -a -f "${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}" -m "Update RPC and config docs to ${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}" -} - -upload_files() { - echo "__________Upload files__________" - git push -q origin HEAD - git push -q -f --tags -} - -RPC_TRAITS_DIR="rpc/src/v1/traits" - -setup_git -clone_repos -mkdir -p "jsonrpc/.parity/$RPC_TRAITS_DIR" -cp $RPC_TRAITS_DIR/*.rs "jsonrpc/.parity/$RPC_TRAITS_DIR" -cd jsonrpc -build_docs -cd .. -cd parity-config-generator -build_config -cd .. -update_wiki_docs -cd wiki -set_remote_wiki -commit_files -upload_files diff --git a/scripts/gitlab/publish-onchain.sh b/scripts/gitlab/publish-onchain.sh deleted file mode 100755 index 588cbdfb579..00000000000 --- a/scripts/gitlab/publish-onchain.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -set -e # fail on any error -set -u # treat unset variables as error - -echo "__________Register Release__________" -DATA="secret=$RELEASES_SECRET" - -echo "Pushing release to Mainnet" -./scripts/gitlab/safe-curl.sh $DATA "http://update.parity.io:1337/push-release/${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}/$CI_COMMIT_SHA" - -echo "Pushing release to Kovan" -./scripts/gitlab/safe-curl.sh $DATA "http://update.parity.io:1338/push-release/${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}/$CI_COMMIT_SHA" - -cd artifacts -ls -l | sort -k9 -filetest=( * ) -echo ${filetest[*]} -for DIR in "${filetest[@]}"; -do - cd $DIR - if [[ $DIR =~ "windows" ]]; - then - WIN=".exe"; - else - WIN=""; - fi - sha3=$(cat parity.sha3 | awk '{print $1}') - case $DIR in - x86_64* ) - DATA="commit=$CI_COMMIT_SHA&sha3=$sha3&filename=parity$WIN&secret=$RELEASES_SECRET" - ../../scripts/gitlab/safe-curl.sh $DATA "http://update.parity.io:1337/push-build/${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}/$DIR" - # Kovan - ../../scripts/gitlab/safe-curl.sh $DATA "http://update.parity.io:1338/push-build/${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}/$DIR" - ;; - esac - cd .. -done diff --git a/scripts/gitlab/publish-snap.sh b/scripts/gitlab/publish-snap.sh deleted file mode 100755 index 5e0231af00e..00000000000 --- a/scripts/gitlab/publish-snap.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash - -set -e # fail on any error -set -u # treat unset variables as error - -# some necromancy: -# gsub(/"/, "", $2) deletes "qoutes" -# gsub(/ /, "", $2) deletes whitespaces -TRACK=`awk -F '=' '/^track/ {gsub(/"/, "", $2); gsub(/ /, "", $2); print $2}' ./util/version/Cargo.toml` -echo Track is: $TRACK -# prepare variables -VERSION=v"$(sed -r -n '1,/^version/s/^version = "([^"]+)".*$/\1/p' Cargo.toml)" -SNAP_PACKAGE="parity_"$VERSION"_"$BUILD_ARCH".snap" -CARGO_TARGET="$(ls artifacts)" -# Choose snap release channel based on parity ethereum version track -case ${TRACK} in - nightly) export GRADE="devel" CHANNEL="edge";; - beta) export GRADE="stable" CHANNEL="beta";; - stable) export GRADE="stable" CHANNEL="stable";; - *) echo "No release" && exit 0;; -esac - -# Release untagged versions from branches to the candidate snap channel -case ${CI_COMMIT_REF_NAME} in - beta|stable) export GRADE="stable" CHANNEL="candidate";; -esac -echo "__________Create snap package__________" -echo "Release channel :" $GRADE " Branch/tag: " $CI_COMMIT_REF_NAME -echo $VERSION:$GRADE:$BUILD_ARCH:$CARGO_TARGET - -sed -e 's/$VERSION/'"$VERSION"'/g' \ - -e 's/$GRADE/'"$GRADE"'/g' \ - -e 's/$BUILD_ARCH/'"$BUILD_ARCH"'/g' \ - -e 's/$CARGO_TARGET/'"$CARGO_TARGET"'/g' \ - scripts/snap/snapcraft.template.yaml > snapcraft.yaml - -apt update -apt install -y --no-install-recommends rhash -cat snapcraft.yaml -snapcraft --target-arch=$BUILD_ARCH -ls *.snap - -echo "__________Calculating checksums__________" -rhash --sha256 $SNAP_PACKAGE -o $SNAP_PACKAGE".sha256" -cat $SNAP_PACKAGE".sha256" - -echo "__________Releasing snap package__________" -echo "Release channel :" $CHANNEL " Branch/tag: " $CI_COMMIT_REF_NAME - -echo $SNAPCRAFT_LOGIN_PARITY_BASE64 | base64 --decode > snapcraft.login -snapcraft login --with snapcraft.login -snapcraft push --release $CHANNEL $SNAP_PACKAGE -snapcraft status parity -snapcraft logout diff --git a/scripts/gitlab/rust-changes.sh b/scripts/gitlab/rust-changes.sh deleted file mode 100755 index 236a20d59ee..00000000000 --- a/scripts/gitlab/rust-changes.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -echo "________Running rust_changes.sh________" -set -e # fail on any error -set -u # treat unset variables as error - -echo "__________Checking if Rust files were changed__________" -git log --graph --oneline --decorate=short -n 10 - -case ${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}} in - (beta|stable) - export GIT_COMPARE=origin/${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}~ - ;; - (master|nightly) - export GIT_COMPARE=master~ - ;; - (*) - export GIT_COMPARE=master - ;; -esac - -export RUST_FILES_MODIFIED="$(git --no-pager diff --name-only $GIT_COMPARE...$CI_COMMIT_SHA | grep -v -e ^\\. -e ^LICENSE -e ^README.md -e ^CHANGELOG.md -e ^test.sh -e ^scripts/ -e ^docs/ -e ^docker/ -e ^snap/ | wc -l | tr -d ' ')" -echo "RUST_FILES_MODIFIED: $RUST_FILES_MODIFIED" - -if [ "${RUST_FILES_MODIFIED}" = "0" ] -then - echo "__________Skipping Rust tests since no Rust files modified__________"; - exit 0 -fi - -rustup show diff --git a/scripts/gitlab/safe-curl.sh b/scripts/gitlab/safe-curl.sh deleted file mode 100755 index f5bb2ee4ef5..00000000000 --- a/scripts/gitlab/safe-curl.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash - -set -eu - -DATA=$1 -ADDRESS=$2 - -CODE=$(curl -o out.txt -w '%{http_code}' --data $DATA $ADDRESS) -cat out.txt && rm out.txt -echo "\n" - -if [[ $CODE -eq 200 ]]; then - echo 'Pushed to updater service.'; -elif [[ $CODE -eq 202 ]]; then - echo 'Updater service ignored request.'; -else - echo 'Unable to push info to updater service.'; - exit 2 -fi diff --git a/scripts/gitlab/sign-win.cmd b/scripts/gitlab/sign-win.cmd deleted file mode 100755 index 2b014a0236c..00000000000 --- a/scripts/gitlab/sign-win.cmd +++ /dev/null @@ -1 +0,0 @@ -@signtool sign /f %1 /p %2 /tr http://timestamp.comodoca.com /du https://parity.io %3 diff --git a/scripts/gitlab/test-cpp.sh b/scripts/gitlab/test-cpp.sh deleted file mode 100755 index 1cbd58a30cf..00000000000 --- a/scripts/gitlab/test-cpp.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -echo "________Running test-cpp.sh________" -set -e # fail on any error -set -u # treat unset variables as error -#use nproc `linux only -THREADS=$(nproc) - -echo "________Running the C++ example________" -DIR=parity-clib/examples/cpp/build -mkdir -p $DIR -cd $DIR -cmake .. -make VERBOSE=1 -j $THREADS -# Note: we don't try to run the example because it tries to sync Kovan, and we don't want -# that to happen on CI -cd - -rm -rf $DIR diff --git a/scripts/gitlab/test-linux.sh b/scripts/gitlab/test-linux.sh deleted file mode 100755 index 6a98d2f7bd5..00000000000 --- a/scripts/gitlab/test-linux.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -echo "________Running test-linux.sh________" -set -e # fail on any error -set -u # treat unset variables as error - -FEATURES="json-tests,ci-skip-tests" -OPTIONS="--release" -#use nproc `linux only -THREADS=$(nproc) - -echo "________Running Parity Full Test Suite________" -time cargo test $OPTIONS --features "$FEATURES" --locked --all --target $CARGO_TARGET -- --test-threads $THREADS diff --git a/scripts/hook.sh b/scripts/hook.sh index ed7e173c505..739b11e7fbc 100755 --- a/scripts/hook.sh +++ b/scripts/hook.sh @@ -7,6 +7,6 @@ echo "set -e" >> $FILE # Run release build echo "cargo build --features dev" >> $FILE # Build tests -echo "cargo test --no-run --features dev --all --exclude parity-ipfs-api" >> $FILE +echo "cargo test --no-run --features dev --all" >> $FILE echo "" >> $FILE chmod +x $FILE diff --git a/scripts/parity.service b/scripts/parity.service index 24e14282bc4..1201d7d7085 100644 --- a/scripts/parity.service +++ b/scripts/parity.service @@ -1,19 +1,19 @@ [Unit] -Description=Parity Ethereum Daemon +Description=OpenEthereum Daemon After=network.target [Service] # run as root, set base_path in config.toml -ExecStart=/usr/bin/parity --config /etc/parity/config.toml +ExecStart=/usr/bin/openethereum --config /etc/openethereum/config.toml # To run as user, comment out above and uncomment below, fill in user and group # picks up users default config.toml in $HOME/.local/share/io.parity.ethereum/ # User=username # Group=groupname -# ExecStart=/usr/bin/parity +# ExecStart=/usr/bin/openethereum Restart=on-failure # Specifies which signal to use when killing a service. Defaults to SIGTERM. -# SIGHUP gives parity time to exit cleanly before SIGKILL (default 90s) +# SIGHUP gives openethereum time to exit cleanly before SIGKILL (default 90s) KillSignal=SIGHUP [Install] diff --git a/scripts/prometheus/config/grafana/dashboards/oe.json b/scripts/prometheus/config/grafana/dashboards/oe.json new file mode 100644 index 00000000000..17beec651ba --- /dev/null +++ b/scripts/prometheus/config/grafana/dashboards/oe.json @@ -0,0 +1,1576 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "cacheTimeout": null, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": { + "align": null + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 0, + "y": 0 + }, + "id": 20, + "interval": "", + "links": [], + "maxDataPoints": 0, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + } + }, + "pluginVersion": "7.0.3", + "targets": [ + { + "expr": "oe_sync_blocks_highest", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Highest block", + "transformations": [ + { + "id": "reduce", + "options": {} + } + ], + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 4, + "y": 0 + }, + "id": 29, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + } + }, + "pluginVersion": "7.0.3", + "targets": [ + { + "expr": "oe_prunning_earliest_state", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Earliest non pruned state", + "transformations": [ + { + "id": "reduce", + "options": {} + } + ], + "type": "stat" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 2, + "w": 3, + "x": 6, + "y": 0 + }, + "id": 24, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false, + "ymax": null, + "ymin": null + }, + "tableColumn": "oe_sync_status{instance=\"localhost:8545\", job=\"openethereum\"}", + "targets": [ + { + "expr": "oe_sync_status", + "refId": "A" + } + ], + "thresholds": "", + "timeFrom": null, + "timeShift": null, + "title": "Sync status", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "WaitingPeers", + "value": "0" + }, + { + "op": "=", + "text": "Waiting", + "value": "6" + }, + { + "op": "=", + "text": "SnapshotManifest", + "value": "1" + }, + { + "op": "=", + "text": "SnapshotData", + "value": "2" + }, + { + "op": "=", + "text": "SnapshotWaiting", + "value": "3" + }, + { + "op": "=", + "text": "Blocks", + "value": "4" + }, + { + "op": "=", + "text": "Idle", + "value": "5" + }, + { + "op": "=", + "text": "NewBlocks", + "value": "7" + } + ], + "valueName": "current" + }, + { + "aliasColors": {}, + "bars": true, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 3, + "w": 3, + "x": 9, + "y": 0 + }, + "hiddenSeries": false, + "id": 28, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": false, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "oe_metrics_time", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "RPC Metrics Reponse time", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": false, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": "1000", + "min": "0", + "show": false + }, + { + "decimals": null, + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 4, + "w": 4, + "x": 12, + "y": 0 + }, + "hiddenSeries": false, + "id": 8, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(oe_import_blocks[1m])", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Block/s", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": false, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 4, + "w": 4, + "x": 16, + "y": 0 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(oe_import_gas[1m])/1000000", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "MGas/s", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": false, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 4, + "w": 4, + "x": 20, + "y": 0 + }, + "hiddenSeries": false, + "id": 10, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(oe_import_txs[1m])", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Tx/s", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": false, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 3 + }, + "hiddenSeries": false, + "id": 16, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pluginVersion": "6.5.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "oe_net_peers", + "refId": "A" + }, + { + "expr": "oe_net_active_peers", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Peers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": false, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 4, + "w": 6, + "x": 12, + "y": 4 + }, + "hiddenSeries": false, + "id": 12, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(oe_io_bytes_read[1m])", + "interval": "", + "legendFormat": "", + "refId": "A" + }, + { + "expr": "rate(oe_io_bytes_written[1m])", + "interval": "", + "legendFormat": "", + "refId": "B" + }, + { + "expr": "rate(oe_io_cache_read_bytes[1m])/60", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "DB IO", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": false, + "values": [] + }, + "yaxes": [ + { + "decimals": null, + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 4, + "w": 6, + "x": 18, + "y": 4 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "oe_queue_size_unverified", + "refId": "A" + }, + { + "expr": "oe_queue_size_verified", + "refId": "B" + }, + { + "expr": "oe_queue_size_verifying", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Queue", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": false, + "values": [] + }, + "yaxes": [ + { + "decimals": 0, + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "decimals": 0, + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 4, + "w": 3, + "x": 0, + "y": 8 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + {} + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "oe_snapshot_download_chunks - oe_snapshot_download_chunks_done", + "legendFormat": "Pending blocks", + "refId": "A" + }, + { + "expr": "rate(oe_snapshot_download_chunks_done[5m])*3600", + "legendFormat": "Rate per hour", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Pending Snapshot download", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": false, + "values": [] + }, + "yaxes": [ + { + "decimals": 0, + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "decimals": 0, + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 4, + "w": 3, + "x": 3, + "y": 8 + }, + "hiddenSeries": false, + "id": 27, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "oe_snapshot_restore_block", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Snapshot restore", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": false, + "values": [] + }, + "yaxes": [ + { + "decimals": 0, + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "decimals": 0, + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 4, + "w": 3, + "x": 6, + "y": 8 + }, + "hiddenSeries": false, + "id": 31, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "oe_chain_block - oe_chain_warpsync_gap_last", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Snapshot GAP", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": false, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 4, + "w": 3, + "x": 9, + "y": 8 + }, + "hiddenSeries": false, + "id": 26, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "oe_snapshot_create_block", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Snapshot create", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": false, + "values": [] + }, + "yaxes": [ + { + "decimals": 0, + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "decimals": 0, + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 4, + "w": 6, + "x": 12, + "y": 8 + }, + "hiddenSeries": false, + "id": 22, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "oe_blockchaincache_block_details", + "refId": "A" + }, + { + "expr": "oe_blockchaincache_block_recipts", + "refId": "B" + }, + { + "expr": "oe_blockchaincache_blocks", + "refId": "C" + }, + { + "expr": "oe_blockchaincache_txaddrs", + "refId": "D" + }, + { + "expr": "", + "refId": "E" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Blockchain cache", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": false, + "values": [] + }, + "yaxes": [ + { + "decimals": 0, + "format": "decbytes", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "decimals": 0, + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 4, + "w": 6, + "x": 18, + "y": 8 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "oe_queue_mem_used", + "refId": "A" + }, + { + "expr": "oe_sync_mem_used", + "refId": "B" + }, + { + "expr": "oe_statedb_mem_used", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": false, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": false, + "schemaVersion": 25, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "2020-06-10T16:05:07.848Z", + "to": "2020-06-10T16:08:24.212Z" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "OpenEthereum", + "uid": "fH2SyJiMz", + "version": 2 + } \ No newline at end of file diff --git a/scripts/prometheus/config/grafana/grafana.ini b/scripts/prometheus/config/grafana/grafana.ini new file mode 100644 index 00000000000..dba20ae99a3 --- /dev/null +++ b/scripts/prometheus/config/grafana/grafana.ini @@ -0,0 +1,705 @@ +##################### Grafana Configuration Defaults ##################### +# +# Do not modify this file in grafana installs +# + +# possible values : production, development +app_mode = production + +# instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty +instance_name = ${HOSTNAME} + +#################################### Paths ############################### +[paths] +# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used) +data = data + +# Temporary files in `data` directory older than given duration will be removed +temp_data_lifetime = 24h + +# Directory where grafana can store logs +logs = data/log + +# Directory where grafana will automatically scan and look for plugins +plugins = data/plugins + +# folder that contains provisioning config files that grafana will apply on startup and while running. +provisioning = conf/provisioning + +#################################### Server ############################## +[server] +# Protocol (http, https, h2, socket) +protocol = http + +# The ip address to bind to, empty will bind to all interfaces +http_addr = + +# The http port to use +http_port = 3000 + +# The public facing domain name used to access grafana from a browser +domain = localhost + +# Redirect to correct domain if host header does not match domain +# Prevents DNS rebinding attacks +enforce_domain = false + +# The full public facing url +root_url = %(protocol)s://%(domain)s:%(http_port)s/ + +# Serve Grafana from subpath specified in `root_url` setting. By default it is set to `false` for compatibility reasons. +serve_from_sub_path = false + +# Log web requests +router_logging = false + +# the path relative working path +static_root_path = public + +# enable gzip +enable_gzip = false + +# https certs & key file +cert_file = +cert_key = + +# Unix socket path +socket = /tmp/grafana.sock + +#################################### Database ############################ +[database] +# You can configure the database connection by specifying type, host, name, user and password +# as separate properties or as on string using the url property. + +# Either "mysql", "postgres" or "sqlite3", it's your choice +type = sqlite3 +host = 127.0.0.1:3306 +name = grafana +user = root +# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;""" +password = +# Use either URL or the previous fields to configure the database +# Example: mysql://user:secret@host:port/database +url = + +# Max idle conn setting default is 2 +max_idle_conn = 2 + +# Max conn setting default is 0 (mean not set) +max_open_conn = + +# Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours) +conn_max_lifetime = 14400 + +# Set to true to log the sql calls and execution times. +log_queries = + +# For "postgres", use either "disable", "require" or "verify-full" +# For "mysql", use either "true", "false", or "skip-verify". +ssl_mode = disable + +ca_cert_path = +client_key_path = +client_cert_path = +server_cert_name = + +# For "sqlite3" only, path relative to data_path setting +path = grafana.db + +# For "sqlite3" only. cache mode setting used for connecting to the database +cache_mode = private + +#################################### Cache server ############################# +[remote_cache] +# Either "redis", "memcached" or "database" default is "database" +type = database + +# cache connectionstring options +# database: will use Grafana primary database. +# redis: config like redis server e.g. `addr=127.0.0.1:6379,pool_size=100,db=0,ssl=false`. Only addr is required. ssl may be 'true', 'false', or 'insecure'. +# memcache: 127.0.0.1:11211 +connstr = + +#################################### Data proxy ########################### +[dataproxy] + +# This enables data proxy logging, default is false +logging = false + +# How long the data proxy should wait before timing out default is 30 (seconds) +timeout = 30 + +# If enabled and user is not anonymous, data proxy will add X-Grafana-User header with username into the request, default is false. +send_user_header = false + +#################################### Analytics ########################### +[analytics] +# Server reporting, sends usage counters to stats.grafana.org every 24 hours. +# No ip addresses are being tracked, only simple counters to track +# running instances, dashboard and error counts. It is very helpful to us. +# Change this option to false to disable reporting. +reporting_enabled = true + +# Set to false to disable all checks to https://grafana.com +# for new versions (grafana itself and plugins), check is used +# in some UI views to notify that grafana or plugin update exists +# This option does not cause any auto updates, nor send any information +# only a GET request to https://grafana.com to get latest versions +check_for_updates = true + +# Google Analytics universal tracking code, only enabled if you specify an id here +google_analytics_ua_id = + +# Google Tag Manager ID, only enabled if you specify an id here +google_tag_manager_id = + +#################################### Security ############################ +[security] +# disable creation of admin user on first start of grafana +disable_initial_admin_creation = false + +# default admin user, created on startup +admin_user = admin + +# default admin password, can be changed before first start of grafana, or in profile settings +admin_password = admin + +# used for signing +secret_key = SW2YcwTIb9zpOOhoPsMm + +# disable gravatar profile images +disable_gravatar = false + +# data source proxy whitelist (ip_or_domain:port separated by spaces) +data_source_proxy_whitelist = + +# disable protection against brute force login attempts +disable_brute_force_login_protection = false + +# set to true if you host Grafana behind HTTPS. default is false. +cookie_secure = false + +# set cookie SameSite attribute. defaults to `lax`. can be set to "lax", "strict" and "none" +cookie_samesite = lax + +# set to true if you want to allow browsers to render Grafana in a ,